[
  {
    "path": ".github/ISSUE_TEMPLATE/🐛-bug-report.md",
    "content": "---\nname: \"\\U0001F41B Bug report\"\nabout: Report bugs\ntitle: \"\\U0001F41B Bug report | [Bug Title]\"\nlabels: bug\nassignees: ''\n\n---\n\n**Description:**\nA clear and concise description of what the bug is.\n\n**Version:**\n\n**Steps To Reproduce:**\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/📓-documentation-request.md",
    "content": "---\nname: \"\\U0001F4D3 Documentation request\"\nabout: Request updates to missing/outdated documentation or other documentation issues.\ntitle: \"\\U0001F4D3 Documentation request | [Request Title]\"\nlabels: documentation\nassignees: ''\n\n---\n\n**Document & Version:**\n\n**Section:**\n\n**Request/Report:**\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/🙏-feature-request.md",
    "content": "---\nname: \"\\U0001F64F Feature request\"\nabout: Request a feature for this project\ntitle: \"\\U0001F64F Feature request | [Request Title]\"\nlabels: enhancement\nassignees: ''\n\n---\n\n**Request Description:**\nA clear and concise description of what the problem is. Ex. Request to add new plotfile variable for [...]\n"
  },
  {
    "path": ".github/workflows/linux-pull-request.yml",
    "content": "name: CI - Linux Pull Request\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n      - closed\npermissions:\n  id-token: write # This is required for requesting the JWT\n  contents: read  # This is required for actions/checkout\njobs:\n  call_workflow:\n    uses: febiosoftware/febio-workflows/.github/workflows/linux-reusable.yml@develop\n    with:\n      runTests: true\n      package-name: febio4\n      package-sdk: true\n      build-plugins: true\n      publish: false\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/linux-push.yml",
    "content": "name: CI - Linux - On Push\non:\n  push:\n    branches:\n      - 'develop'\n  workflow_dispatch:\n    inputs:\n      runTests:\n        description: \"Run tests?\"\n        type: boolean\n        required: false\n      build-plugins:\n        description: \"Build all plugins?\"\n        type: boolean\n        required: false\npermissions:\n  id-token: write # This is required for requesting the JWT\n  contents: read  # This is required for actions/checkout\njobs:\n  call_workflow:\n    uses: febiosoftware/febio-workflows/.github/workflows/linux-reusable.yml@develop\n    with:\n      runTests: ${{ (github.event_name == 'workflow_dispatch' && inputs.runTests) || (github.event_name == 'push' && contains(fromJSON('[\"develop\"]'), github.ref_name)) }}\n      build-plugins: ${{ (github.event_name == 'workflow_dispatch' && inputs.build-plugins) || github.event_name == 'push' }}\n      package-name: febio4\n      package-sdk: true\n      publish: true\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/macos-pull-request.yml",
    "content": "name: CI - MacOS Pull Request\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n      - closed\npermissions:\n  id-token: write # This is required for requesting the JWT\n  contents: read  # This is required for actions/checkout\njobs:\n  call_workflow:\n    uses: febiosoftware/febio-workflows/.github/workflows/macos-reusable.yml@develop\n    with:\n      runTests: true\n      package-name: febio4\n      package-sdk: true\n      build-plugins: true\n      publish: false\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/macos-push.yml",
    "content": "name: CI - MacOS Push\non:\n  push:\n    branches:\n      - 'develop'\n  workflow_dispatch:\n    inputs:\n      runTests:\n        description: \"Run tests?\"\n        required: false\n        type: boolean\n        default: false\n      build-plugins:\n        description: \"Build all plugins?\"\n        type: boolean\n        required: false\npermissions:\n  id-token: write # This is required for requesting the JWT\n  contents: read  # This is required for actions/checkout\njobs:\n  call_workflow:\n    uses: febiosoftware/febio-workflows/.github/workflows/macos-reusable.yml@develop\n    with:\n      runTests: ${{ (github.event_name == 'workflow_dispatch' && inputs.runTests) || (github.event_name == 'push' && contains(fromJSON('[\"develop\"]'), github.ref_name)) }}\n      build-plugins: ${{ (github.event_name == 'workflow_dispatch' && inputs.build-plugins) || github.event_name == 'push' }}\n      package-name: febio4\n      package-sdk: true\n      publish: true\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/release-tag.yml",
    "content": "name: CI - FEBio Release\n\non:\n  release:\n    types: [published]\n\njobs:\n  annotate-tag:\n    name: \"Annotate Release Tag\"\n    runs-on: \"ubuntu-latest\"\n    steps:\n      - name: \"Annotated Tag\"\n        id: annotated-tag\n        uses: febiosoftware/febio-workflows/.github/actions/annotated-tag@develop\n        with:\n          github-pat-token: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/windows-pull-request.yml",
    "content": "name: CI - Windows Pull Request\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n      - closed\npermissions:\n  id-token: write # This is required for requesting the JWT\n  contents: read  # This is required for actions/checkout\njobs:\n  call_workflow:\n    uses: febiosoftware/febio-workflows/.github/workflows/windows-reusable.yml@develop\n    with:\n      runTests: true\n      package-name: febio4\n      package-sdk: true\n      build-plugins: true\n      publish: false\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/windows-push.yml",
    "content": "name: CI - Windows Push\n\non:\n  push:\n    branches:\n      - 'develop'\n  workflow_dispatch:\n    inputs:\n      runTests:\n        description: \"Run tests?\"\n        required: false\n        type: boolean\n        default: false\n      build-plugins:\n        description: \"Build all plugins?\"\n        type: boolean\n        required: false\npermissions:\n  id-token: write # This is required for requesting the JWT\n  contents: read  # This is required for actions/checkout\njobs:\n  call_workflow:\n    uses: febiosoftware/febio-workflows/.github/workflows/windows-reusable.yml@develop\n    with:\n      runTests: ${{ (github.event_name == 'workflow_dispatch' && inputs.runTests) || (github.event_name == 'push' && contains(fromJSON('[\"develop\"]'), github.ref_name)) }}\n      build-plugins: ${{ (github.event_name == 'workflow_dispatch' && inputs.build-plugins) || github.event_name == 'push' }}\n      package-name: febio4\n      package-sdk: true\n      publish: true\n    secrets: inherit\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\nbuild/\n\n\n# Visual Studio cache/options directory\n.vs/\nVS2019/\nVS2022/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n*.sln\n*.vcxproj\n*.user\n*.lib\n*.dll\n*.filters\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n# miscellaneous\nDocumentation/Doxygen/doc\nDocumentation/Doxygen/doc_only\n\n.DS_Store\n\n# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n## User settings\nxcuserdata/\n\n\n## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)\n*.xcscmblueprint\n*.xccheckout\n*.xcuserstate\n*.xcscheme\n\n## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)\nDerivedData/\n*.moved-aside\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\n\n## Gcc Patch\n/*.gcno\n\n## cmake build directories\ncbuild/\ncmbuild*/\nvs22/\nCMakeFiles/\n\n##cmake files\n!CMakeLists.txt\nCMakeCache.txt\n!*.cmake\n\n## Visual Studio Code\n.vscode/\n\nDocumentation/FEBio_Theory_Manual.pdf\nDocumentation/FEBio_User_Manual.html.LyXconv/\n\n/FEBioMech/FECGSolidSolver - Copy.cpp\n\nfebio*-sdk\n.envrc\n\nTestSuite\n*.gz\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"ci/common\"]\n\tpath = ci/common\n\turl = https://github.com/febiosoftware/ci-common\n"
  },
  {
    "path": "BUILD.md",
    "content": "# FEBio Build Guide\n\n### Table of contents\n- [Prerequisites](#prereq) \n- [Running CMake](#runCMake)\n- [Building FEBio](#build)\n- [Limitations of CMake](#limits)\n- [Troubleshooting](#trouble)\n\n## Prerequisites <a name=\"prereq\"></a>\n\n### CMake \nFEBio's build process utilizes CMake, an open-source, cross-platform tool designed to streamline the configuration of the build environment. The CMake script in this repository will help you to locate necessary third party libraries on your machine, set up include and library paths, and allow you to choose which of FEBio’s features you would like to include in your build.\n\nPlease download the latest release of CMake from https://cmake.org/, and install it on your machine before proceeding. Many Linux distributions come with CMake pre-installed, or have CMake available through their package managers. \n\n### Intel Math Kernel Library\n\nFEBio requires the Intel Math Kernel Library (MKL) in order to utilize the Pardiso linear solver and some of the iterative linear solvers. This library can be downloaded as part of the Intel oneAPI Base Toolkit from Intel's website: https://software.intel.com/content/www/us/en/develop/tools/oneapi/base-toolkit.html. In the absence of MKL, FEBio will default to using the Skyline linear solver. However, the Pardiso solver is significantly faster and more memory-efficient than the Skyline solver, and it is strongly recommended that the Pardiso solver be used.\n\n### Additional Third Party Packages\n\nFEBio makes use of the following third party packages to add additional functionality. If you do not need the functionality provided by a given package, you can still compile FEBio without it. The libraries below are organized according to the type of functionality they add. \n\n* The Lourakis levmar routine is required by FEBio in order for it to perform its parameter optimization functions. The source for this library can be downloaded from http://users.ics.forth.gr/~lourakis/levmar/.\n\n* MMG is used in the adaptive mesh refinement framework (the FEAMR FEBio library). FEBio only uses the mmg3d component of MMG. To use this library, you will need to download the source and compile it yourself. The source for this library can be downloaded from [MMG's GitHub account](https://github.com/MmgTools/mmg).\n\n* HYPRE is used by FEBio for the BoomerAMG iterative linear solver, which is an algebraic multi-grid solver. To use this library, you will need to download the source and compile it yourself. The source for this library can be downloaded from [HYPRE's GitHub account](https://github.com/hypre-space/hypre).\n\n* zlib is an open-source, lossless data-compression library that is used by FEBio to optionally compress plot files to save disc space. This library is generally pre-installed on macOS, and most Linux distributions. For Windows, you will need to download the latest source from zlib's website, and compile the library yourself.\n\n* FFTW: Fastest Fourier Transform in the West. Alternate library to perform FFTs since certain solvers use conflicting versions of MKL. The library can be found at [FFTW's wegpage](https://fftw.org/)\n\n## Running CMake <a name=\"runCMake\"></a>\n\nCMake is used to configure the build environment for FEBio on your machine. It can be used to generate a Microsoft Visual Studio Solution on Windows, an XCode Project on macOS, and a set of makefiles to be used with the GNU Make tool on Linux. \n\nCMake generates a large number of configuration files that can cause the build directory to become cluttered. It is therefore strongly recommended that you do an [out-of-source build](https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#what-is-an-out-of-source-build) by pointing CMake to an empty directory. The _cbuild_ directory in the FEBio repository is provided for this purpose. If for some reason you find yourself needing to clear out your CMake configuration for FEBio and start this process from scratch, all you will need to do is delete everything in that build directory.\n\n### CMake GUI\n\n<img src=\"Documentation/BuildGuide/CMakeGUI.png\" alt=\"CMake GUI\" width=\"75%\">\n<!-- ![cmake gui](Documentation/BuildGuide/CMakeGUI.png) -->\n\nOn Windows, and macOS CMake is run using the CMake graphical user interface (GUI). The CMake GUI is also available on Linux, but is generally installed separately, and so the command line interface (CLI) version, _ccmake_ is generally used (see below).\n\nTo start the configuration process, enter the path to the root directory of the FEBio repository that you've downloaded onto your machine into the box labeled _Where is the source code:_. To insure that you are doing an out-of-source build, enter the path to the _cbuild_ directory of your FEBio repository in the box labeled _Where to build the binaries:_. You may also locate these directories using a file browser by clicking on the _Browse_ buttons to the right of these fields. \n\n### ccmake\n\n<img src=\"Documentation/BuildGuide/ccmake.png\" alt=\"ccmake\" width=\"75%\">\n<!-- ![cmake gui](Documentation/BuildGuide/ccmake.png) -->\n\nIf you are running Linux and have not installed the CMake GUI, there are two ways to run cmake. You can run the command `cmake` which will call CMake and run through the configuration and generation processes automatically. This method, however does not allow for interactivity, and so it is highly recommended that you instead run the command `ccmake`. This will run an interactive version of CMake with an in-terminal GUI as shown above. Using ccmake should allow you to follow along with the rest of this tutorial. To start an out-of-source build with ccmake, open a terminal in the _cbuild_ directory of your local copy fo the FEBio Studio repository and run the following command:\n\n```\nccmake ..\n```\n\n### First Configuration\n\nThe configuration step in the CMake build process runs the script defined in `CMakeLists.txt` located in the root directory of the FEBio repository. This script does several things: \n\n* Attempts to locate MKL, and any other third party packages that FEBio Studio uses.\n* Automatically enables or disables FEBio features based on which libraries it was able to find.\n* Automatically sets up include and library paths for your build system based on the libraries that it found, and the features that have been enabled.\n\nTo run the configuration process click the _Configure_ button in the lower left part of the GUI, or type `c` if you are using ccmake. If you are running the CMake GUI, you will be asked to choose a generator for the project. On Windows, choose the version of MSVC that you have installed and click _Finish_. On macOS, leave the default value and click _Finish_. CMake will now run the configuration process, the output of which can be seen in the text field at the bottom of the GUI. If all goes well, new fields will be added in red to the GUI, and it should look something like the image below:\n\n<img src=\"Documentation/BuildGuide/CMakeGUIFull.png\" alt=\"ccmake\" width=\"75%\">\n<!-- ![cmake gui](Documentation/BuildGuide/CMakeGUIFull.png) -->\n\nAfter running the configuration process, the CMake GUI will populate with several build options that can be toggled on or off, each corresponding to one of the third party packages listed above. Building FEBio with a given build option enabled requires the corresponding third party packages to be installed on your machine and to be located by CMake. \n\nThe CMake script will do its best to automatically locate these packages, but if it is unable to do so, you will have to point CMake to the packages manually for each package that you'd like to use. \n\n### Manually Locating MKL\n\nIf CMake is unable to locate MKL automatically, the `USE_MKL` option will be automatically turned off. A simple mechanism is provided for you to help the script to locate your MKL installtion. A variable called `MKLROOT` will have appeared in the CMake GUI. Enter the path to the _mkl_ directory of your Intel oneAPI installation as the value for the variable, then run the configuration step again. If you've correctly entered the path, it should find the necessary components of MKL. At this point you will need to manually turn the `USE_MKL` option back on.\n\n### Manually Locating Other Packages\n\nIf CMake is unable to find any of the other third party packages on your system, it will automatically disable the corresponding build option. It will also make visible the fields for the include and library paths for the missing packages. In order to build with any of these options, you will need to manually edit the include and library paths for the required packages. The include path for a given package should point to the directory containing that package's header files, and the library path should point to the library file. Once you have updated the paths for the required packages, you then need to manually toggle the option back on.\n\n### Project Generation\n\nOnce the desired optional packages have been located, and their corresponding build options have been enabled, it is time to generate the platform-specific build files. It's always a good idea to run Configure one more time before you generate the build files. This will make sure that the CMakeLists script catches any errors that you may have introduced by manually changing paths, or toggling build options. Once you've run Configure again, click the _Generate_ button (or type `g` if you're using ccmake). On Windows this will generate a Visual Studio Project, on macOS this will generate an XCode Project, and on Linux this will generate a Makefile. If you're running Windows or macOS, you can click the _Open Project_ button and it will automatically open the created project. \n\n## Building FEBio <a name=\"build\"></a>\n\n### Windows\n\nOnce you have the Visual Studio project open, you can choose whether you'd like to build a debug or a release version of the software, and then start the build process by either clicking on the Play button, or by pressing _F5_. After a successful build, the software should launch automatically. \n\n### macOS\n\nOnce you have the XCode project open, you'll want to change the build target from `ALL_BUILD` to `FEBio`. Do this by clicking on the button that says `ALL_BUILD` in the upper left corner of XCode, next to the Play and Stop buttons. XCode will fully build FEBio with either target selected, but this will ensure that XCode will automatically launch FEBio after it's been built. Then you can start the build by either click on the Play button, or by pressing ⌘R. After a successful build, the software should launch automatically. \n\n### Linux\n\nOnce the Makefiles have been generated, open a terminal in the _cbuild_ directory, and run `make`. If your machine has multiple cores, you can increase the speed of the build by passing a `-j` flag to _make_, followed by the number of threads you want _make_ to use (e.g. `make -j4`). Please note that this will only increase the speed of the build, and will in no way affect the final binary. After a successful build, the compiled binary can be found in the _bin_ subdirectory.\n\n## Limitations of CMake <a name=\"limits\"></a>\n\nCMake is a useful tool for automating cross-platform builds, but it is not without its limitations:\n\n* CMake is unable to create project files or makefiles that can automatically detect the presence of new source files. If you update your local repository after a new FEBio release, or if you modify FEBio's source and add new source files, you will have to rerun CMake in order to insure that any new source files are included in the build.\n\n* On Linux, the type of build (e.g. debug, release, etc) is determined by CMake during the generation process, since the related options are baked into the resulting makefiles. To change the type of build you are building, you must rerun CMake and select the desired build type. \n\n## Troubleshooting <a name=\"trouble\"></a>\n\n* If you get errors that look something like this<br><br>Could not find HYPRE library. Check HYPRE_LIB.<br><br>This means that  CMake is unable to locate the library associated with a currently active build option. To fix this issue, ensure that the CMake variable for that package's library is pointing to the correct location on your machine, and that the library file exists in that location. If CMake is still unable to find the required library, you may either have to rebuild or reinstall the third-party package, or disable the build option that uses it. \n\n* If you run into other issues while building FEBio, please visit [our forums](https://forums.febio.org/) for more help.\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\nproject(FEBio)\nset(FEBIO_LIBS FEBioFluid FEBioLib FEBioMech FEBioMix FEBioOpt FECore NumCore FEAMR FEBioRVE FEImgLib FEBioPlot FEBioXML)\nset(STATIC_FEBIO_LIBS FEBioTest febcode)\n\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)\nset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)\nset(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)\n\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\nset_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER Autogen)\n\n# Set a default build type if none was specified\nset(default_build_type \"Release\")\n \nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  message(STATUS \"Setting build type to '${default_build_type}' as none was specified.\")\n  set(CMAKE_BUILD_TYPE \"${default_build_type}\" CACHE\n      STRING \"Choose the type of build.\" FORCE)\n  # Set the possible values of build type for cmake-gui\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Release\" \"MinSizeRel\" \"RelWithDebInfo\")\nendif()\n\n##### Set appropriate flag for operating system ####\nif(WIN32)\n\tadd_definitions(-DWIN32 -DFECORE_DLL)\nelseif(APPLE)\n    add_definitions(-D__APPLE__)\n    add_definitions(-DHAS_ACCEL)\nelse()\n\tadd_definitions(-DLINUX)\n    \n    option(USE_STATIC_STDLIBS \"Helps to create more portable binaries\" ON)\nendif()\n\n##### Search for library and include directories #####\n\ninclude(FindDependencies.cmake)\n\n##### Check compiler for std::filesystem support #####\ninclude(CheckCXXSourceCompiles)\n\ncheck_cxx_source_compiles(\"\n  #include <filesystem>\n  int main() {\n    std::filesystem::path p{\\\"test\\\"};\n    return 0;\n  }\n\" HAS_STD_FILESYSTEM)\n\n#### Check if paths are valid and find libs ####\n\nfunction(findLib libName libDir libOut)\n    # Optional arguments ARGV3-ARVG6 can be used as alernative names for the library\n    if(WIN32)\n        find_library(TEMP NAMES ${libName}.lib ${ARGV3}.lib ${ARGV4}.lib ${ARGV5}.lib ${ARGV6}.lib\n            PATHS ${${libDir}} NO_DEFAULT_PATH)\n    else()\n        find_library(TEMP NAMES lib${libName}.a lib${ARGV3}.a lib${ARGV4}.a lib${ARGV5}.a lib${ARGV6}.a\n            PATHS ${${libDir}} NO_DEFAULT_PATH)\n    endif()\n    \n    if(TEMP)\n        set(${libOut} ${TEMP} PARENT_SCOPE)\n        unset(TEMP CACHE)\n    else()\n        if(WIN32)\n            message(SEND_ERROR \"Could not find ${libName}.lib. Check ${libName}.\")\n        elseif(APPLE)\n            message(SEND_ERROR \"Could not find lib${libName}.so, lib${libName}.a, or lib${libName}.dylib Check ${libDir}.\")\n        else()\n            message(SEND_ERROR \"Could not find lib${libName}.so, or lib${libName}.a. Check ${libDir}\")\n        endif()\n        unset(TEMP CACHE)\n    endif()\nendfunction()\n\nif(USE_MKL)\n    set(MKL_LIB_NAMES mkl_intel_lp64 mkl_core mkl_intel_thread)\n    foreach(name IN LISTS MKL_LIB_NAMES)\n        findLib(${name} MKL_LIB_DIR MKL_LIB)\n        list(APPEND MKL_LIBS ${MKL_LIB})\n        unset(MKL_LIB)\n    endforeach(name)\n    \n    if(NOT EXISTS ${MKL_OMP_LIB})\n        message(SEND_ERROR \"Could not find MKL OMP library. Check MKL_OMP_LIB.\")\n    endif()\nendif()\n\nif(USE_FFTW)\n\tif(NOT EXISTS ${FFTW_LIB})\n\t\tmessage(SEND_ERROR \"Could not find FFTW library libfftw3-3. Check FFTW_LIB.\")\n        message(SEND_ERROR \"The library is \" ${FFTW_LIB})\n\tendif()\n\tif(NOT EXISTS ${FFTW_INC}/fftw3.h)\n\t\tmessage(SEND_ERROR \"Could not find fftw3.h. Check FFTW_INC.\")\n\tendif()\nendif()\n\nif(USE_HYPRE)\n\tif(NOT EXISTS ${HYPRE_LIB})\n\t\tmessage(SEND_ERROR \"Could not find HYPRE library. Check HYPRE_LIB.\")\n\tendif()\n\tif(NOT EXISTS ${HYPRE_INC}/HYPRE_IJ_mv.h)\n\t\tmessage(SEND_ERROR \"Could not find HYPRE_IJ_mv.h. Check HYPRE_INC.\")\n\tendif()\nendif()\n\nif(USE_SUPERLU_MT)\n\tif(NOT EXISTS ${SUPERLU_MT_LIB})\n\t\tmessage(SEND_ERROR \"Could not find SUPERLU_MT library. Check SUPERLU_MT_LIB.\")\n\tendif()\n\tif(NOT EXISTS ${SUPERLU_MT_INC}/slu_mt_ddefs.h)\n\t\tmessage(SEND_ERROR \"Could not find slu_mt_ddefs.h. Check SUPERLU_MT_INC.\")\n\tendif()\nendif()\n\nif(USE_MMG)\n\tif(NOT EXISTS ${MMG_LIB})\n\t\tmessage(SEND_ERROR \"Could not find MMG library. Check MMG_LIB.\")\n\tendif()\n    if(NOT EXISTS ${MMGS_LIB})\n\t\tmessage(SEND_ERROR \"Could not find MMGS library. Check MMGS_LIB.\")\n\tendif()\n\tif(NOT EXISTS ${MMG_INC}/mmg/libmmg.h)\n\t\tmessage(SEND_ERROR \"Could not find libmmg.h. Check MMG_INC.\")\n\tendif()\nendif()\n\nif(USE_LEVMAR)\n\tif(NOT EXISTS ${LEVMAR_INC}/levmar.h)\n\t\tmessage(SEND_ERROR \"Could not find levmar.h. Check LEVMAR_INC.\")\n\tendif()\n\tif(NOT EXISTS ${LEVMAR_LIB})\n\t\tmessage(SEND_ERROR \"Could not find Levmar library. Check LEVMAR_LIB.\")\n\tendif()\nendif()\n\nif(USE_NLOPT)\n\tif(NOT EXISTS ${NLOPT_INC}/nlopt.h)\n\t\tmessage(SEND_ERROR \"Could not find nlopt.h. Check NLOPT_INC.\")\n\tendif()\n\tif(NOT EXISTS ${NLOPT_LIB})\n\t\tmessage(SEND_ERROR \"Could not find NLOPT library. Check NLOPT_LIB.\")\n\tendif()\nendif()\n\nif(USE_ZLIB)\n\tif(NOT EXISTS ${ZLIB_LIBRARY_RELEASE})\n\t\tmessage(SEND_ERROR \"Could not find ZLIB. Check ZLIB_LIBRARY_RELEASE.\")\n\tendif()\n\tif(NOT EXISTS ${ZLIB_INCLUDE_DIR}/zlib.h)\n\t\tmessage(SEND_ERROR \"Could not find zlib.h. Check ZLIB_INCLUDE_DIR.\")\n\tendif()\nendif()\n\n##### Set up compiler flags #####\nif(WIN32)\n    add_compile_options(/MP /wd4996 /wd4251 /wd4275)\nelseif(APPLE)\n    set(CMAKE_BUILD_RPATH @executable_path/../lib/;@executable_path/../Frameworks)\n    set(OMP_INC \"\" CACHE PATH \"Path to the OMP include directory\")\n    mark_as_advanced(OMP_INC)\nelse()\n\tadd_compile_options(-fopenmp -w)\n    \n    set(CMAKE_BUILD_RPATH_USE_LINK_PATH FALSE)\n    set(CMAKE_BUILD_RPATH $ORIGIN/../lib/)\nendif()\n\nif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Intel\")\n        add_compile_options(-no-intel-extensions)\nendif()\n\n# Add openMP flags for macOS if found.\nif(APPLE)\n    if(${OpenMP_C_FOUND})\n        add_compile_options(-Xpreprocessor -fopenmp)\n    endif()\nendif()\n\n##### Find Source Files #####\n\nmacro(findHdrSrc name)\n\tfile(GLOB HDR_${name} \"${name}/*.h*\")\n\tfile(GLOB SRC_${name} \"${name}/*.cpp\")\nendmacro()\n\nforeach(name IN LISTS FEBIO_LIBS)\n\tfindHdrSrc(${name})\nendforeach(name)\n\nforeach(name IN LISTS STATIC_FEBIO_LIBS)\n\tfindHdrSrc(${name})\nendforeach(name)\n\n\nmacro(addLib name TYPE)\n    string(TOLOWER ${name} lname)\n\tadd_library(${lname} ${TYPE} ${HDR_${name}} ${SRC_${name}})\n\tset_property(TARGET ${lname} PROPERTY AUTOGEN_BUILD_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/AutoGen/${name}_autogen)\n    \n    if(NOT WIN32)\n        set_property(TARGET ${lname} PROPERTY POSITION_INDEPENDENT_CODE ON)\n    endif()\n    \n    if(APPLE)\n        target_link_libraries(${lname} PRIVATE \"-undefined dynamic_lookup\")\n    endif()\nendmacro()\n\nforeach(name IN LISTS FEBIO_LIBS)\n\taddLib(${name} SHARED)\nendforeach(name)\n\nforeach(name IN LISTS STATIC_FEBIO_LIBS)\n\taddLib(${name} STATIC)\nendforeach(name)\n\n##### Set up executable compilation #####\nfile(GLOB HEADERS \"FEBio/*.h\")\nfile(GLOB SOURCES \"FEBio/*.cpp\")\nadd_executable (febio4 ${HEADERS} ${SOURCES})\n\nif(WIN32)\n    target_compile_options(febio4 PRIVATE /openmp)\n    target_compile_options(febiofluid PRIVATE /openmp)\n    target_compile_options(febiolib PRIVATE /openmp)\n    target_compile_options(febiomech PRIVATE /openmp)\n    target_compile_options(febiomix PRIVATE /openmp)\n    target_compile_options(febioopt PRIVATE /openmp)\n    target_compile_options(fecore PRIVATE /openmp)\n    target_compile_options(feamr PRIVATE /openmp)\n    target_compile_options(febiorve PRIVATE /openmp)\n    target_compile_options(feimglib PRIVATE /openmp)\n    target_compile_options(febioplot PRIVATE /openmp)\n    target_compile_options(febiotest PRIVATE /openmp)\n    target_compile_options(febioxml PRIVATE /openmp)\nendif()\n\n##### Set dev commit information #####\n\n# Cross platform execute_process\nmacro(crossExecProcess OUT)\n\tif(WIN32)\n\t\texecute_process(COMMAND cmd.exe /c ${CMD}\n\t\t\tWORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n\t\t\tRESULT_VARIABLE SUCCESS\n\t\t\tOUTPUT_VARIABLE ${OUT}\n\t\t\tOUTPUT_STRIP_TRAILING_WHITESPACE)\n\telse()\n\t\texecute_process(COMMAND ${CMD}\n\t\t\tWORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n\t\t\tRESULT_VARIABLE SUCCESS\n\t\t\tOUTPUT_VARIABLE ${OUT}\n\t\t\tOUTPUT_STRIP_TRAILING_WHITESPACE)\n\tendif()\nendmacro()\n\n# Check for git and give warning if not available\noption(SET_DEVCOMMIT \"Set the DEVCOMMIT macro with git commit hash\" ON)\nif(${SET_DEVCOMMIT} AND EXISTS ${CMAKE_SOURCE_DIR}/.git)\n    if(WIN32)\n        set(CMD where git)\n    else()\n        set(CMD which git)\n    endif()\n\n        crossExecProcess(NULL)\n\n    if(SUCCESS EQUAL 0)\n        # Get branch name and check if we're on the develop branch\n        set(CMD git rev-parse --abbrev-ref HEAD)\n        crossExecProcess(BRANCH_NAME)\n\n        string(COMPARE EQUAL ${BRANCH_NAME} \"master\" ISMASTER)\n        \n        # Get the commit info and set the DEVCOMMIT macro\n        if(SUCCESS EQUAL 0 AND NOT ISMASTER)\n            \n            set(CMD git rev-parse --short=9 HEAD)\n            crossExecProcess(DEVCOMMIT)\n            \n            if(${SUCCESS} EQUAL 0)\n                target_compile_definitions(febiolib PUBLIC \"DEVCOMMIT=\\\"${DEVCOMMIT}\\\"\")\n            endif()\n        endif()\n    else()\n        message(WARNING \"Could not find git in system PATH. Development version info will not be addded.\")\n    endif()\nendif()\n\n##### Temp fix for Gerard to fix HYPRE's MPI include problem #####\nset(EXTRA_INC \"\" CACHE STRING \"Enter extra include paths separated by spaces\")\nset(EXTRA_LIB \"\" CACHE STRING \"Enter extra library paths separated by spaces\")\nmark_as_advanced(EXTRA_INC)\n\n##### Setup includes, defnitions, and linking options #####\ninclude_directories(${PROJECT_SOURCE_DIR})\n\nif(HAS_STD_FILESYSTEM)\n    target_compile_definitions(febiolib PRIVATE HAS_STD_FILESYSTEM)\nendif()\n\n# Link LEVMAR\nif(USE_LEVMAR)\n\ttarget_include_directories(febioopt PRIVATE ${LEVMAR_INC})\n    target_compile_definitions(febioopt PRIVATE HAVE_LEVMAR)\n    target_link_libraries(febioopt PRIVATE ${LEVMAR_LIB})\nendif()\n\n# Link NLOPT\nif(USE_NLOPT)\n\ttarget_include_directories(febioopt PRIVATE ${NLOPT_INC})\n    target_compile_definitions(febioopt PRIVATE HAVE_NLOPT)\n    target_link_libraries(febioopt PRIVATE ${NLOPT_LIB})\nendif()\n\n# Link FFTW\nif(USE_FFTW)\n    target_include_directories(feimglib PRIVATE ${FFTW_INC})\n    target_compile_definitions(feimglib PRIVATE HAVE_FFTW)\n    target_link_libraries(feimglib PRIVATE ${FFTW_LIB})\nendif()\n\n# Link HYPRE\nif(USE_HYPRE)\n    target_include_directories(numcore PRIVATE ${HYPRE_INC})\n    target_compile_definitions(numcore PRIVATE HYPRE)\n    target_link_libraries(numcore PRIVATE ${HYPRE_LIB})\nendif()\n\n# Link SuperLU_MT\nif(USE_SUPERLU_MT)\n\ttarget_include_directories(numcore PRIVATE ${SUPERLU_MT_INC})\n    target_compile_definitions(numcore PRIVATE HAVE_SUPERLU_MT)\n    if(APPLE)\n        target_compile_definitions(numcore PRIVATE _PTHREAD)\n    endif()\n    target_link_libraries(numcore PRIVATE ${SUPERLU_MT_LIB})\nendif()\n\n# Link MKL\nif(USE_MKL)\n    target_include_directories(numcore PRIVATE ${MKL_INC} ${OpenMP_C_LIBRARIES})\n    target_compile_definitions(numcore PRIVATE MKL_ISS PARDISO)\n    \n    target_include_directories(febioopt PRIVATE ${MKL_INC})\n\ttarget_compile_definitions(febioopt PRIVATE MKL_ISS PARDISO)\n\n    if(WIN32 OR APPLE)\n        target_link_libraries(numcore PRIVATE ${MKL_OMP_LIB} ${MKL_LIBS})\n        target_link_libraries(febioopt PRIVATE ${MKL_OMP_LIB} ${MKL_LIBS})\n    else()\n        target_link_libraries(numcore PRIVATE -Wl,--start-group ${MKL_OMP_LIB} ${MKL_LIBS} -Wl,--end-group)\n        target_link_libraries(febioopt PRIVATE -Wl,--start-group ${MKL_OMP_LIB} ${MKL_LIBS} -Wl,--end-group)\n    endif()\n    \n    if(FORCE_SYSTEM_OMP)\n        target_link_libraries(numcore PRIVATE ${OpenMP_C_LIBRARIES})\n        target_link_libraries(febioopt PRIVATE ${OpenMP_C_LIBRARIES})\n    endif()\nelse()\n    # If not using MKL, we still need OpenMP from the system.\n    if(${OpenMP_C_FOUND})\n        target_link_libraries(numcore PRIVATE ${OpenMP_C_LIBRARIES})\n        target_link_libraries(febioopt PRIVATE ${OpenMP_C_LIBRARIES})\n    endif()\nendif()\n\n# Manually pull in include dirs for OpenMP on macOS\nif(APPLE)\n    target_include_directories(febiomech PRIVATE ${OpenMP_C_INCLUDE_DIRS} ${OMP_INC})\nendif()\n\n# Link Pardiso-project\nif(USE_PDL)\n    target_compile_definitions(numcore PRIVATE PARDISODL)\n    target_link_libraries(numcore PRIVATE ${PDL_LIB})\n    set(ENV{PARDISOLICMESSAGE} \"1\")\n\n    # We still need OpenMP from the system.\n    if(${OpenMP_C_FOUND})\n        target_link_libraries(numcore PRIVATE ${OpenMP_C_LIBRARIES})\n        target_link_libraries(febioopt PRIVATE ${OpenMP_C_LIBRARIES})\n    endif()\nendif()\n\n# Link Accelerate framework\nif (APPLE)\n  target_link_libraries(numcore PRIVATE \"-framework Accelerate\")\nendif()\n\n# Link MMG\nif(USE_MMG)\n\ttarget_include_directories(feamr PRIVATE ${MMG_INC})\n    target_compile_definitions(feamr PRIVATE HAS_MMG)\n    target_link_libraries(feamr PRIVATE ${MMG_LIB} ${MMGS_LIB})\nendif()\n\n# Link ZLIB\nif(USE_ZLIB)\n    target_include_directories(febioplot PRIVATE ${ZLIB_INCLUDE_DIR})\n    target_compile_definitions(febioplot PRIVATE HAVE_ZLIB)\n\ttarget_link_libraries(febioplot PRIVATE ${ZLIB_LIBRARY_RELEASE})\nendif()\n\n# Extra Includes\ntarget_include_directories(febioopt PRIVATE ${EXTRA_INC})\ntarget_include_directories(numcore PRIVATE ${EXTRA_INC})\ntarget_include_directories(feamr PRIVATE ${EXTRA_INC})\ntarget_include_directories(febiolib PRIVATE ${EXTRA_INC})\n\n# Extra Libs\ntarget_link_libraries(numcore PRIVATE ${EXTRA_LIB})\n\n\nif(WIN32)\n\ttarget_link_libraries(febio4 psapi.lib ws2_32.lib)\nelse()\n    target_link_libraries(febio4 -ldl)\nendif()\n\n# Link Libraries into FEBioLib\ntarget_link_libraries(numcore PRIVATE fecore)\ntarget_link_libraries(febioxml PRIVATE fecore)\ntarget_link_libraries(febiotest PRIVATE fecore)\ntarget_link_libraries(febioplot PRIVATE fecore)\ntarget_link_libraries(feimglib PRIVATE fecore)\ntarget_link_libraries(feamr PRIVATE fecore)\ntarget_link_libraries(febiorve PRIVATE fecore febiomech febioxml febioplot)\ntarget_link_libraries(febioopt PRIVATE fecore febioxml)\ntarget_link_libraries(febiomix PRIVATE febiomech fecore)\ntarget_link_libraries(febiomech PRIVATE fecore feamr)\ntarget_link_libraries(febiofluid PRIVATE febiomix febiomech fecore)\n\ntarget_link_libraries(febiolib PRIVATE febioplot febiomech \n\t\tfebiomix febioxml numcore febioopt febiotest febiofluid feamr febiorve fecore feimglib)\n\n# pull febcode into FECore\ntarget_link_libraries(fecore PRIVATE febcode)\n\nif (WIN32)\n    target_link_libraries(febiolib PRIVATE psapi.lib)\nendif()\n\n# Link FEBio libraries\nif(WIN32 OR APPLE)\n\ttarget_link_libraries(febio4 fecore febiolib)\nelse()\n    if(USE_STATIC_STDLIBS)\n        target_link_libraries(numcore PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febioxml PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febiotest PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febiorve PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febioplot PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febioopt PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febiomix PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febiomech PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febiofluid PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(feimglib PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(feamr PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(febiolib PRIVATE -static-libstdc++ -static-libgcc)\n        target_link_libraries(fecore PRIVATE -static-libstdc++ -static-libgcc)\n        \n        target_link_libraries(febio4 -static-libstdc++ -static-libgcc)\n    endif()\n\n\ttarget_link_libraries(febio4 -Wl,--start-group fecore febiolib -Wl,--end-group)\n    \n    # Extra compiler flags for intel compiler\n    if(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Intel\")\n        target_link_libraries(febio4 -static-intel)\n    endif()\nendif()\n\n#### Set up installation #####\ninstall(TARGETS febio4 numcore febioxml febiotest febiorve febioplot febioopt\n    febiomix febiomech febiofluid feamr feimglib fecore febiolib\n    RUNTIME DESTINATION bin\n    LIBRARY DESTINATION lib\n    ARCHIVE DESTINATION lib\n    )\n\ninstall(DIRECTORY \n    FEBio NumCore FECore FEAMR FEImgLib FEBioFluid FEBioLib FEBioMech FEBioMix\n    FEBioOpt FEBioRVE FEBioPlot FEBioTest FEBioXML\n    DESTINATION include\n    FILES_MATCHING PATTERN \"*.h*\"\n    )\n\ninstall(FILES FEBioConfig.cmake \n    DESTINATION lib/cmake/FEBio\n)"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n[info@febio.org].\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations."
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribution Guidelines\n\n## Reporting issues\n\n- **Search for existing issues.** Please check to see if someone else has reported the same issue.\n- **Share as much information as possible.** Include operating system and version, browser and version. Also, include steps to reproduce the bug.\n\n## Project Setup\nRefer to the [README](README.md).\n\n## Code Style\n\n### Variable Naming\nNot all current code follows the conventions below but these will be followed for future developments. \n- Maximize the use  of semantic and descriptive variables names (e.g. `faceIndices` not `fcInd` or `fi`). Avoid abbreviations except in cases of industry wide usage. In some cases non-descriptive and short variable names are exceptable for instance vertices (points), faces, edges, colors and logic arrays may be denoted `V`, `F`, `E`, `C`, `L`. Furthermore, if a mathematrical symbol or letter is commonly used for some entity it may be acceptable to use short names e.g. coordinates may be referred to as `X`, `Y` and `Z` and image coordinates of indices may be referred to as `I`, `J` and `K`. \n\n## Testing\nProving example files as well as a description of the expected outcomes. This can be in the form of a log file or data file that contain partial solution data. \n\n## Pull requests\n- Try not to pollute your pull request with unintended changes – keep them simple and small. If possible, squash your commits.\n- Try to share how your code has been tested before submitting a pull request.\n- If your PR resolves an issue, include **closes #ISSUE_NUMBER** in your commit message (or a [synonym](https://help.github.com/articles/closing-issues-via-commit-messages)).\n- Review\n    - If your PR is ready for review, another contributor will be assigned to review your PR\n    - The reviewer will accept or comment on the PR. \n    - If needed address the comments left by the reviewer. Once you're ready to continue the review, ping the reviewer in a comment.\n    - Once accepted your code will be merged to `master`\n"
  },
  {
    "path": "CppProperties.json",
    "content": "{\n  \"configurations\": [\n    {\n      \"inheritEnvironments\": [\n        \"msvc_x64\"\n      ],\n      \"name\": \"x64-Release\",\n      \"includePath\": [\n        \"${env.INCLUDE}\",\n        \"${workspaceRoot}\\\\**\"\n      ],\n      \"defines\": [\n        \"WIN32\",\n        \"NDEBUG\",\n        \"UNICODE\",\n        \"_UNICODE\"\n      ],\n      \"intelliSenseMode\": \"windows-msvc-x64\"\n    }\n  ]\n}"
  },
  {
    "path": "Documentation/Copyright-FEBio.txt",
    "content": "1. Introduction to FEBio licenses\n=================================\nPrior to FEBio 2.9, FEBio (including the source code, SDK, and binaries) were distributed under a custom license that limited the use of FEBio and its derived products to academic users only. Commercial users were required to obtain a separate commercial license. \n\nAs of FEBio version 2.9 the FEBio source code and SDK are licensed under the MIT license, listed below. The binaries and installers available from febio.org are still distributed under the custom license, but the restrictions for commercial users were removed.\n\n\n2. FEBio AUTHORS\n================\nThe following people are considered AUTHORS of the FEBio software. \n\n- Steve Maas\n- Gerard A. Ateshian\n- Jeffrey A. Weiss\n\nThe following people have also contributed to the FEBio software.\n\n- David R. Rawlins\n- Benjamin J. Ellis\n- Alex A. Veress\n- Sam Evans\n- Shawn Reese\n- Aaron Swedberg \n- Jocelyn Todd\n- Lowell Edgar\n- Jay J. Shim\n- Marsh Paulson\n- Klevis Aliaj\n- Brandon Zimmerman\n- Jay C. Hou\n\n\n3. Copyright HOLDERS\n===================\nThe following lists the Copyright HOLDERS: \n\n- University of Utah\n- The Trustees of Columbia University in the City of New York\n- The AUTHORS as defined above\n\n\n4. FEBIO license\n=================\n\nThe following text shall appear in all source code files that are part of the FEBio software. \n\n-->\nThis file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n-->\n"
  },
  {
    "path": "Documentation/Doxygen/Doxyfile",
    "content": "# Doxyfile 1.12.0\n\n# This file describes the settings to be used by the documentation system\n# Doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use Doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use Doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = FEBio\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = 4.8\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          = \"Finite Elements for Biomechanics\"\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           = ../febio_logo.png\n\n# With the PROJECT_ICON tag one can specify an icon that is included in the tabs\n# when the HTML document is shown. Doxygen will copy the logo to the output\n# directory.\n\nPROJECT_ICON           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where Doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = doc\n\n# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding Doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by Doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# Doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = NO\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which Doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where Doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by Doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and Doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# Doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as Doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make Doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by Doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by Doxygen, so you can\n# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 6.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 0\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0 and GITHUB use the lower case version of title\n# with any whitespace replaced by '-' and punctuation characters removed.\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled Doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let Doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also makes the inheritance and\n# collaboration diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse\n# them like normal C++ but will assume all classes use public instead of private\n# inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# Doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then Doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# Doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run Doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use\n# during processing. When set to 0 Doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and macOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = NO\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = NO\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = NO\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= NO\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = NO\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# Doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by Doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by Doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents Doxygen's defaults, run Doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run Doxygen from a directory containing a file called\n# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH\n# environment variable) so that external tools such as latex and gs can be\n# found.\n# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the\n# path already specified by the PATH variable, and are added in the order\n# specified.\n# Note: This option is particularly useful for macOS version 14 (Sonoma) and\n# higher, when running Doxygen from Doxywizard, because in this case any user-\n# defined changes to the PATH are ignored. A typical example on macOS is to set\n# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin\n# together with the standard path, the full search path used by doxygen when\n# launching external tools will then become\n# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\n\nEXTERNAL_TOOL_PATH     =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by Doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete\n# function parameter documentation. If set to NO, Doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, Doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about\n# undocumented enumeration values. If set to NO, Doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the Doxygen process Doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that Doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of Doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = ../../\n\n# This tag can be used to specify the character encoding of the source files\n# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that Doxygen parses The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING) if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1).\n# See also: INPUT_ENCODING for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by Doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,\n# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,\n# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,\n# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to\n# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.idl \\\n                         *.ddl \\\n                         *.odl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.pyw \\\n                         *.f90 \\\n                         *.f95 \\\n                         *.f03 \\\n                         *.f08 \\\n                         *.f \\\n                         *.for \\\n                         *.tcl \\\n                         *.vhd \\\n                         *.vhdl \\\n                         *.ucf \\\n                         *.qsf\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which Doxygen is\n# run.\n\nEXCLUDE                = \n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       = */build/* \\\n                         */cmbuild/* \\\n                         */cmbuild22/* \\\n                         */VS2017/* \\\n                         */VS2019/* \\\n                         */XCode/*\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             = images \\\n                         ../../\n\n# The INPUT_FILTER tag can be used to specify a program that Doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that Doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by Doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by Doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the Doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# multi-line macros, enums or list initialized variables directly into the\n# documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of Doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by Doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = NO\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the\n# clang parser (see:\n# http://clang.llvm.org/) for more accurate parsing at the cost of reduced\n# performance. This can be particularly helpful with template rich C++ code for\n# which Doxygen's built-in parser lacks the necessary type information.\n# Note: The availability of this option depends on whether or not Doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n# The default value is: NO.\n\nCLANG_ASSISTED_PARSING = NO\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS\n# tag is set to YES then Doxygen will add the directory of each input to the\n# include path.\n# The default value is: YES.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_ADD_INC_PATHS    = YES\n\n# If clang assisted parsing is enabled you can provide the compiler with command\n# line options that you would normally use when invoking the compiler. Note that\n# the include paths will already be set by Doxygen for the files and directories\n# specified with INPUT and INCLUDE_PATH.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_OPTIONS          =\n\n# If clang assisted parsing is enabled you can provide the clang parser with the\n# path to the directory containing a file called compile_commands.json. This\n# file is the compilation database (see:\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the\n# options used when the source files were built. This is equivalent to\n# specifying the -p option to a clang tool, such as clang-check. These options\n# will then be passed to the parser. Any options specified with CLANG_OPTIONS\n# will be added as well.\n# Note: The availability of this option depends on whether or not Doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n\nCLANG_DATABASE_PATH    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank Doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that Doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that Doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of Doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank Doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that Doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank Doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that Doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by Doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generates light mode output, DARK always\n# generates dark mode output, AUTO_LIGHT automatically sets the mode according\n# to the user preference, uses light mode if no preference is set (the default),\n# AUTO_DARK automatically sets the mode according to the user preference, uses\n# dark mode if no preference is set and TOGGLE allows a user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_DARK\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be\n# dynamically folded and expanded in the generated HTML source code.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_CODE_FOLDING      = YES\n\n# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in\n# the top right corner of code and text fragments that allows the user to copy\n# its content to the clipboard. Note this only works if supported by the browser\n# and the web page is served via a secure context (see:\n# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:\n# protocol.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COPY_CLIPBOARD    = YES\n\n# Doxygen stores a couple of settings persistently in the browser (via e.g.\n# cookies). By default these settings apply to all HTML pages generated by\n# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store\n# the settings under a project specific key, such that the user preferences will\n# be stored separately.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_PROJECT_COOKIE    =\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, Doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# Doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by Doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = YES\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# Doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified\n# enumeration values besides the enumeration mnemonics.\n# The default value is: NO.\n\nSHOW_ENUM_VALUES       = NO\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# Doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = YES\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for\n# the HTML output. The underlying search engine uses JavaScript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the JavaScript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, Doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank Doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that Doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of Doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank Doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that Doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by Doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to Doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that Doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to Doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the RTF_OUTPUT output directory.\n# Note that the files will be copied as-is; there are no commands or markers\n# available.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTRA_FILES        =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to Sqlite3 output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3\n# database with symbols found by Doxygen stored in tables.\n# The default value is: NO.\n\nGENERATE_SQLITE3       = NO\n\n# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be\n# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put\n# in front of it.\n# The default directory is: sqlite3.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_OUTPUT         = sqlite3\n\n# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db\n# database file will be recreated with each Doxygen run. If set to NO, Doxygen\n# will warn if a database file is already found and not modify it.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_RECREATE_DB    = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which Doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces\n# will be listed in the class and namespace index. If set to NO, only the\n# inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the topic index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed\n# to run in parallel. When set to 0 Doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# Doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=Helvetica,fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=Helvetica,labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links. Explicit enabling an inheritance\n# graph or choosing a different representation for an inheritance graph of a\n# specific class, can be accomplished by means of the command \\inheritancegraph.\n# Disabling an inheritance graph can be accomplished by means of the command\n# \\hideinheritancegraph.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes. Explicit enabling a collaboration graph,\n# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the\n# command \\collaborationgraph. Disabling a collaboration graph can be\n# accomplished by means of the command \\hidecollaborationgraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for\n# groups, showing the direct groups dependencies. Explicit enabling a group\n# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means\n# of the command \\groupgraph. Disabling a directory graph can be accomplished by\n# means of the command \\hidegroupgraph. See also the chapter Grouping in the\n# manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, Doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will be wrapped across multiple lines. Some heuristics are\n# applied to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then Doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,\n# can be accomplished by means of the command \\includegraph. Disabling an\n# include graph can be accomplished by means of the command \\hideincludegraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then Doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set\n# to NO, can be accomplished by means of the command \\includedbygraph. Disabling\n# an included by graph can be accomplished by means of the command\n# \\hideincludedbygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories. Explicit enabling a directory graph, when\n# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command\n# \\directorygraph. Disabling a directory graph can be accomplished by means of\n# the command \\hidedirectorygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in Doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for PlantUML.\n\nPLANTUML_CFG_FILE      =\n\n# When using PlantUML, the specified paths are searched for files specified by\n# the !include statement in a PlantUML block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, Doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within Doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, Doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "Documentation/Doxygen/Doxyfile_doc_only",
    "content": "# Doxyfile 1.12.0\n\n# This file describes the settings to be used by the documentation system\n# Doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use Doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use Doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = FEBio\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = 4.0\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          = \"Finite Elements for Biomechanics\"\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           = C:/Users/Steve/source/repos/FEBio/Documentation/febio_logo.png\n\n# With the PROJECT_ICON tag one can specify an icon that is included in the tabs\n# when the HTML document is shown. Doxygen will copy the logo to the output\n# directory.\n\nPROJECT_ICON           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where Doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = doc_only\n\n# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding Doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by Doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# Doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which Doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where Doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by Doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and Doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# Doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as Doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make Doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by Doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by Doxygen, so you can\n# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 6.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 0\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0 and GITHUB use the lower case version of title\n# with any whitespace replaced by '-' and punctuation characters removed.\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled Doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let Doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also makes the inheritance and\n# collaboration diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse\n# them like normal C++ but will assume all classes use public instead of private\n# inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# Doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then Doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# Doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run Doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use\n# during processing. When set to 0 Doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and macOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# Doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by Doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by Doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents Doxygen's defaults, run Doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run Doxygen from a directory containing a file called\n# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH\n# environment variable) so that external tools such as latex and gs can be\n# found.\n# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the\n# path already specified by the PATH variable, and are added in the order\n# specified.\n# Note: This option is particularly useful for macOS version 14 (Sonoma) and\n# higher, when running Doxygen from Doxywizard, because in this case any user-\n# defined changes to the PATH are ignored. A typical example on macOS is to set\n# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin\n# together with the standard path, the full search path used by doxygen when\n# launching external tools will then become\n# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\n\nEXTERNAL_TOOL_PATH     =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by Doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete\n# function parameter documentation. If set to NO, Doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, Doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about\n# undocumented enumeration values. If set to NO, Doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the Doxygen process Doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that Doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of Doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = .\n\n# This tag can be used to specify the character encoding of the source files\n# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that Doxygen parses The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING) if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1).\n# See also: INPUT_ENCODING for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by Doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,\n# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,\n# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,\n# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to\n# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.idl \\\n                         *.ddl \\\n                         *.odl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.pyw \\\n                         *.f90 \\\n                         *.f95 \\\n                         *.f03 \\\n                         *.f08 \\\n                         *.f \\\n                         *.for \\\n                         *.tcl \\\n                         *.vhd \\\n                         *.vhdl \\\n                         *.ucf \\\n                         *.qsf\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = NO\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which Doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             = images\n\n# The INPUT_FILTER tag can be used to specify a program that Doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that Doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by Doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by Doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the Doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# multi-line macros, enums or list initialized variables directly into the\n# documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of Doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by Doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the\n# clang parser (see:\n# http://clang.llvm.org/) for more accurate parsing at the cost of reduced\n# performance. This can be particularly helpful with template rich C++ code for\n# which Doxygen's built-in parser lacks the necessary type information.\n# Note: The availability of this option depends on whether or not Doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n# The default value is: NO.\n\nCLANG_ASSISTED_PARSING = NO\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS\n# tag is set to YES then Doxygen will add the directory of each input to the\n# include path.\n# The default value is: YES.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_ADD_INC_PATHS    = YES\n\n# If clang assisted parsing is enabled you can provide the compiler with command\n# line options that you would normally use when invoking the compiler. Note that\n# the include paths will already be set by Doxygen for the files and directories\n# specified with INPUT and INCLUDE_PATH.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_OPTIONS          =\n\n# If clang assisted parsing is enabled you can provide the clang parser with the\n# path to the directory containing a file called compile_commands.json. This\n# file is the compilation database (see:\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the\n# options used when the source files were built. This is equivalent to\n# specifying the -p option to a clang tool, such as clang-check. These options\n# will then be passed to the parser. Any options specified with CLANG_OPTIONS\n# will be added as well.\n# Note: The availability of this option depends on whether or not Doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n\nCLANG_DATABASE_PATH    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank Doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that Doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that Doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of Doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank Doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that Doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank Doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that Doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by Doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generates light mode output, DARK always\n# generates dark mode output, AUTO_LIGHT automatically sets the mode according\n# to the user preference, uses light mode if no preference is set (the default),\n# AUTO_DARK automatically sets the mode according to the user preference, uses\n# dark mode if no preference is set and TOGGLE allows a user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_LIGHT\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be\n# dynamically folded and expanded in the generated HTML source code.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_CODE_FOLDING      = YES\n\n# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in\n# the top right corner of code and text fragments that allows the user to copy\n# its content to the clipboard. Note this only works if supported by the browser\n# and the web page is served via a secure context (see:\n# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:\n# protocol.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COPY_CLIPBOARD    = YES\n\n# Doxygen stores a couple of settings persistently in the browser (via e.g.\n# cookies). By default these settings apply to all HTML pages generated by\n# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store\n# the settings under a project specific key, such that the user preferences will\n# be stored separately.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_PROJECT_COOKIE    =\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, Doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# Doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by Doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = YES\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# Doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified\n# enumeration values besides the enumeration mnemonics.\n# The default value is: NO.\n\nSHOW_ENUM_VALUES       = NO\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# Doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = YES\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for\n# the HTML output. The underlying search engine uses JavaScript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the JavaScript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, Doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank Doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that Doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of Doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank Doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that Doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by Doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to Doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that Doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to Doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the RTF_OUTPUT output directory.\n# Note that the files will be copied as-is; there are no commands or markers\n# available.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTRA_FILES        =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to Sqlite3 output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3\n# database with symbols found by Doxygen stored in tables.\n# The default value is: NO.\n\nGENERATE_SQLITE3       = NO\n\n# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be\n# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put\n# in front of it.\n# The default directory is: sqlite3.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_OUTPUT         = sqlite3\n\n# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db\n# database file will be recreated with each Doxygen run. If set to NO, Doxygen\n# will warn if a database file is already found and not modify it.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_RECREATE_DB    = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which Doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces\n# will be listed in the class and namespace index. If set to NO, only the\n# inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the topic index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed\n# to run in parallel. When set to 0 Doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# Doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=Helvetica,fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=Helvetica,labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links. Explicit enabling an inheritance\n# graph or choosing a different representation for an inheritance graph of a\n# specific class, can be accomplished by means of the command \\inheritancegraph.\n# Disabling an inheritance graph can be accomplished by means of the command\n# \\hideinheritancegraph.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes. Explicit enabling a collaboration graph,\n# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the\n# command \\collaborationgraph. Disabling a collaboration graph can be\n# accomplished by means of the command \\hidecollaborationgraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for\n# groups, showing the direct groups dependencies. Explicit enabling a group\n# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means\n# of the command \\groupgraph. Disabling a directory graph can be accomplished by\n# means of the command \\hidegroupgraph. See also the chapter Grouping in the\n# manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, Doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will be wrapped across multiple lines. Some heuristics are\n# applied to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then Doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,\n# can be accomplished by means of the command \\includegraph. Disabling an\n# include graph can be accomplished by means of the command \\hideincludegraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then Doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set\n# to NO, can be accomplished by means of the command \\includedbygraph. Disabling\n# an included by graph can be accomplished by means of the command\n# \\hideincludedbygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories. Explicit enabling a directory graph, when\n# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command\n# \\directorygraph. Disabling a directory graph can be accomplished by means of\n# the command \\hidedirectorygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in Doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for PlantUML.\n\nPLANTUML_CFG_FILE      =\n\n# When using PlantUML, the specified paths are searched for files specified by\n# the !include statement in a PlantUML block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, Doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within Doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, Doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "Documentation/Doxygen/building_a_plugin.dox",
    "content": "/**\r\n\\page building_a_plugin Creating a Plugin\r\n\r\nIn order to create a plugin you need to have the FEBio SDK (Software Development Kit). This SDK contains the header files and the pre-built libraries that you need to create a plugin. The SDK is installed (optionally) as part of the FEBio Studio installation and is usually installed in the same location as FEBio in a subfolder named sdk. Next, you need a C++ compiler since the plugin has to be written in C++. We also recommend having an IDE (Integrated Development Environment) as this makes writing code much easier (e.g. Visual Studio on Windows, or XCode on Mac). Select one of the following links to learn more about setting up a plugin project for a specific operating system.\r\n\r\n\\li \\subpage create_plugin_vs\r\n\\li \\subpage create_plugin_osx\r\n\\li \\subpage create_plugin_linux\r\n\\li \\subpage create_plugin_fbs3\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/callback.dox",
    "content": "/**\r\n\\page callback FEBio callbacks\r\n\r\nA callback is a function that is called by the FEBio framework at specific places in the code. \r\n\r\nFEBio uses this callback mechanism to accomplish several things: A callback is used to update the title of the shell window to display the progress, to query for user interruptions (ctrl+c), and to write the output to the log and plot file.\r\n\r\nTo register a callback function, call the FEModel::AddCallback function. \r\n\r\n\\code\r\nvoid FEModel::AddCallback(FEBCORE_CB_FNC pcb, unsigned int nwhen, void *pd, CBInsertPolicy insert = CBInsertPolicy::CB_ADD_END);\r\n\\endcode\r\n\r\nThis function takes three required parameters and one optional. The first is a pointer to the callback function. This function must be of FECORE_CB_FNC type which is described below.\r\nThe second parameter is an unsigned integer that defines when FEBio will call the callback. The third parameter is a void pointer that the user can use to pass data to the callback. The last parameter, which is optional, tells whether the callback should be added to the front or back of the callback queue. \r\n\r\nThe callback function must have the FEBIO_CB_FNC type which is defined as follows.\r\n\r\n\\code\r\nvoid (*FEBIO_CB_FNC)(FEModel*, unsigned int, void*);\r\n\\endcode\r\n\r\nWhen FEBio calls the function, the first parameter will be a pointer to the FE model. This pointer can be used to query information from the model. Users can also make changes to the FEModel if needed.\r\n\r\nThe second parameter is the reason why FEBio called this function. Since\r\ncallbacks can be called for multiple events, this parameter will contain the explicit reason. \r\nIt can take on the following values.\r\n\\li <b>CB_INIT</b>: Call after FEBio is done with initialization (and before the solve phase starts)\r\n\\li <b>CB_STEP_ACTIVE</b>: Call after a step was activated.\r\n\\li <b>CB_MAJOR_ITERS</b>: Call at end of major iterations, after it has converged.\r\n\\li <b>CB_MINOR_ITERS</b>: Call at the end of each minor iteration. \r\n\\li <b>CB_SOLVED</b>: Call after the entire model is solved.\r\n\\li <b>CB_UPDATE_TIME</b>: Call when the time was updated and before the time step is solved.\r\n\\li <b>CB_AUGMENT</b>: Call before each augmentation\r\n\\li <b>CB_STEP_SOLVED</b>: Call when the step was solved\r\n\\li <b>CB_MATRIX_REFORM</b>: Call when the stiffness matrix is reformed\r\n\\li <b>CB_REMESH</b>: Call after remesh\r\n\\li <b>CB_PRE_MATRIX_SOLVE:</b> Call right before matrix solve\r\n\\li <b>CB_RESET</b>: Call after FEModel::Reset\r\n\\li <b>CB_ALWAYS</b>: Call whenever FEBio calls the callbacks.\r\n\r\nNote that these flags can be combined using bitwise OR. For instance, CB_MAJOR_ITERS | CB_MINOR_ITERS will cause the callback to be called both during the \r\nmajor and minor iterstions.\r\n\r\nThe third parameter is the same parameter that was passed when the callback was registered. This allows users to pass custom data to the callback.\r\n\r\nFor example, the following code snippet shows how to define a callback that is always called.\r\n\r\n\\code\r\nvoid my_cb(FEModel*, unsigned int, void*);\r\n\r\nFEModel fem;\r\nfem.AddCallback(my_cb, CB_ALWAYS, 0);\r\n\\endcode\r\n\r\nInside the callback function, the reason parameter can be used to determine why the callback was called. \r\n\r\n\\code\r\nvoid my_cb(FEModel* fem, unsigned int nreason, void* pd)\r\n{\r\n\tif (nreason & CB_INIT)\r\n\t{\r\n\t\t// initialization has finished\r\n\t}\r\n\telse if (nreason & CB_MAJOR_ITERS)\r\n\t{\r\n\t\t// FEBio solved a converged state\r\n\t}\r\n\telse if (nreason & CB_MINOR_ITERS)\r\n\t{\r\n\t\t// a minor iteration has finished\r\n\t}\r\n}\r\n\\endcode\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/cb_plugin.dox",
    "content": "/**\r\n\\page cb_plugin Callback Plugins\r\nA Callback plugin is a special type of plugin that extends FEBio's callback mechanism to the plugin framework. Callbacks are useful because they allow developers to interact with FEBio's solution pipeline; Something that otherwise would require changing the source code directly. FEBio uses callbacks for various tasks, including updating the shell window title, checking for user interrupts (ctrl+c), and for writing the log and plot files. Developers can now use the callback plugin to define additional callbacks via the plugin mechanism.\r\n\r\nIn essence, the same approach can be used as is explained in \\ref callback. In this case, it is only necessary to obtain a reference to the FEModel class in order to register the callback. One way to accomplish this is in the constructor of another plugin class, since most classes receive a pointer to the FEModel class as a parameter to their constructor. For instance, the optimization module implements a task class. During initialization of this class, it also registers a callback that will be called repeatedly to collect data from the model.\r\n\r\nHowever, the approach outlined in this section allows users to register callbacks via the FEBio input file. See below for details.\r\n\r\n\\section cb_base The FECallback Class\r\nThe base class for defining callback plugins, is the FECallback class (defined in FECore\\FECallback.h). The constructor of the base class takes a pointer to an FEModel\r\nand a parameter that defines when the callback should be called. The possible values are:\r\n\r\n\\li <b>CB_INIT</b>: Call after FEBio is done with initialization (and before the solve phase starts)\r\n\\li <b>CB_STEP_ACTIVE</b>: Call after a step was activated.\r\n\\li <b>CB_MAJOR_ITERS</b>: Call at end of major iterations, after it has converged.\r\n\\li <b>CB_MINOR_ITERS</b>: Call at the end of each minor iteration. \r\n\\li <b>CB_SOLVED</b>: Call after the entire model is solved.\r\n\\li <b>CB_UPDATE_TIME</b>: Call when the time was updated and before the time step is solved.\r\n\\li <b>CB_AUGMENT</b>: Call before each augmentation\r\n\\li <b>CB_STEP_SOLVED</b>: Call when the step was solved\r\n\\li <b>CB_MATRIX_REFORM</b>: Call when the stiffness matrix is reformed\r\n\\li <b>CB_REMESH</b>: Call after remesh\r\n\\li <b>CB_PRE_MATRIX_SOLVE:</b> Call right before matrix solve\r\n\\li <b>CB_RESET</b>: Call after FEModel::Reset\r\n\\li <b>CB_MODEL_UPDATE</b>: Called at the end of FEModel::Update\r\n\\li <b>CB_TIMESTEP_SOLVED</b>: Called at FEAnalysis::SolveTimeStep after the solver returns.\r\n\\li <b>CB_SERIALIZE_SAVE</b>: Called at the end of FEModel::Serialize when saving\r\n\\li <b>CB_SERIALIZE_LOAD</b>: Called at the end of FEModel::Serialize when loading\r\n\\li <b>CB_USER1</b>: can be used by users\r\n\\li <b>CB_ALWAYS</b>: Call whenever FEBio calls the callbacks.\r\n\r\nThese parameters can be combined using a binary OR operator (e.g. CB_INIT | CB_MAJOR_ITERS). If you wish that the callback is called for all events you can use the value CB_ALWAYS. \r\n\r\nThe base class defines an abstract member, FECallback::Execute, which needs to be overridden by the derived class. This is the function that will be called by FEBio.\r\n\r\n\\section cb_create Creating an Callback Plugin\r\nThe first step in creating a callback plugin is deriving a new class from FECallback and defining the Execute member.\r\n\r\n\\code\r\nclass MyCallback : public FECallback\r\n{\r\npublic:\r\n\tMyCallback(FEModel* pfem) : FECallback(pfem, CB_ALWAYS){}\r\n\t\r\n\tvoid Execute(FEModel& fem, unsigned int nreason);\r\n};\r\n\\endcode\r\n\r\nThe constructor of the derived class calls the base class constructor with the pointer to the FEModel class and the events for which the callbacks wants to be called. \r\n\r\nThe Execute member is then called when the events occur for which the callback was registered. Inside this function, a developer can implement any additional functionality. \r\n\r\nIf the callback was requested for multiple events the nreason parameter can be used to identify the event.\r\n\r\n\\code\r\nvoid MyCallback::Execute(FEModel& fem, unsigned int nreason)\r\n{\r\n\tif (nreason==CB_INIT)\r\n\t{\r\n\t\t// Model was initialized\r\n\t}\r\n\telse if (nreason == CB_SOLVED)\r\n\t{\r\n\t\t// Model is solved\r\n\t}\r\n}\r\n\\endcode\r\n\r\n\\section cb_using Using a callback plugin\r\nIn order to use a callback plugin, it must be registered with the framework. This can be done in the usual way. See \\ref plugin_basics for details.\r\n\r\n\\code\r\nREGISTER_FECORE_CLASS(MyCallbackPlugin, \"my_callback\");\r\n\\endcode\r\n\r\nOnce the plugin is registered, it can be referenced in the FEBio input file. A special Code section can be added where the callbacks can be defined. \r\n\r\n\\code\r\n<Code>\r\n\t<callback name=\"my_callback\"/>\r\n</Code>\r\n\\endcode\r\n\r\nThe name attribute of the callback item is set to the name with which the callback plugin was registered with the framework.\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/create_plugin_fbs3.dox",
    "content": "/**\n\\page create_plugin_fbs3 Creating a plugin project with FEBio Studio 3\n\nAs of version 3.0, FEBio Studio has a build-in tool for generating plugin templates. This tool is often a more convenient starting point than trying to set up the plugin in your build environment manually. For most plugin types, the tool will generate skeleton code that is ready to build. \n\n\\section prep Prerequisites\nBefore you can use FEBio Studio to generate a plugin template, you must have the following installed. \n\n\\li FEBio Studio, version 3.0 or newer.\n\\li A build environment, e.g. Visual Studio on Windows. \n\\li CMake, which is used to generate the project files for the build environment.\n\nWith FEBioStudio3 installed, open the <b>Options</b> dialog box (menu Tools\\Options) and select the <b>FEBio</b> item from the list. Set the locations for the FEBio SDK Include path, Library path, and set the default path where you would like FEBio Studio to place the plugin files. Make sure that this folder is writeable. (E.g. don't use the FEBioStudio installation folder.) You only have to set these fields once, the first time you create a plugin.\n\n\\section wizard The FEBio Plugin Wizard\nCreating an FEBio plugin project is as simple as following the steps in the FEBio Plugin wizard. To start the wizard, select the menu <b>FEBio\\Create FEBio Plugin...</b>. \n\n\\subsection wizard_page1 Page1 - Create FEBio Plugin\nThe first page of the wizard allows you to choose a name for the plugin. Since this name will be used to generate the plugin files, make sure to use only characters that are valid in file names. In addition, you can also choose the location where FEBioStudio will generate the plugin files. Lastly, choose the FEBio module that you will expand. For instance, if you intend to add a new constitutive model for an elastic material, select the <i>solid</i> module. \n\n\\image html plugin_wizard_page1.png\n\nAfter filling out the information, click the <b>Next</b> button to proceed to the next page.\n\n\\subsection wizard_page2 Page2 - Configure Plugin\nOn the second page, you can select the plugin type (e.g. Elastic Material, if you intend to implement a new constitutive model) and enter the name of the class. Make sure to use a valid C++ class name. Optionally, you can enter the type string of the plugin class. This is the string value that will be used when registering the plugin and the string that users will use as the value for the type attribute in the FEBio input file. By default, the name of the plugin class will be used. \n\n\\image html plugin_wizard_page2.png\n\nOnce this page is completed, click <b>Next</b> to continue.\n\n\\subsection wizard_page3 Page 3 - Plugin Properties\nThis page will list properties you can set to further configure the particular plugin you want to create. The properties that are listed will depend on the type of plugin you selected. For example, if you selected the \"Elastic Material\" plugin, the properties will allow you to choose the base class for the material class. \n\n\\image html plugin_wizard_page3.png\n\nOnce the properties are set, click <b>Finish</b> to exit the wizard. \n\n\\subsection wizard_finish Finishing the Wizard\nAt this point, the wizard will generate all the necessary files for building the plugin. A dialog box will inform you whether FEBio Studio was able to generate the plugin files or not. If the wizard was successful, the following files should have been generated.\n\n\\li <b>CMakeLists.txt</b> This is the CMake file that will generate the project files for your build environment. \n\\li <b>main.cpp</b> This is the file that contains the required functions that FEBio needs to integrate your plugin. \n\\li <b>MyPluginName.h</b> The header file containing the class declaration. (The actual name will use the class name you entered in the wizard.)\n\\li <b>MyPluginName.cpp</b> The code file containing the class definition. (The actual name will use the class name you entered in the wizard.)\n\n\\section build_plugin Building the plugin\nThe wizard will generate a template code that is ready to build. Of course, the plugin may not actually do anything yet, but at this point, you can verify that the plugin builds correctly with your build environment and can be loaded in FEBio Studio.\n\nThe first step will be to build the project files for your build environment. This essentially means that you have to run CMake on the CMakeLists.txt that the wizard generated. If you are familiar with CMake, you can do this outside FEBioStudio, however, you can also do this from FEBio Studio. In fact, you can build the plugin directly from FEBio Studio. \n\nIf you show the Project panel, you'll notice that your plugin files are listed there under a new item that is named after your plugin. Double-clicking a file will open it up in an editor in FEBio Studio.\n\n\\image html plugin_project.png\n\nTo build the plugin, right-click the top-level item with the plugin's name and select the <i>Build</i> option. When doing this, FEBio Studio will now first call CMake to generate the project files, and then call CMake again to build the plugin with your native build environment. You can monitor the progress on the Output panel. (Make sure \"Build\" is selected in the <i>show output from</i> dropdown list.)\n\n\\section plugin_load Loading the Plugin\n\nIf the build was successful, you can now load the plugin into FEBio Studio. To load the plugin, right-click the item with the plugin's name in the Project panel and select the <i>Load</i> menu option. A dialog box will inform you whether the plugin was loaded successfully or not. \n\n\\section wrap_up Limitations\nAlthough FEBioStudio has now the ability to generate plugin files, edit the files, and build the plugin, it is still highly recommended to familiarize yourself with your build environment since some tasks can only be done there.\n\nOne important thing to keep in mind is that FEBio Studio will only build the <b>Release</b> configuration of the plugin, since only the Release build can be loaded into FEBio Studio. For the purpose of testing your plugin code, it is more convenient to build the <b>Debug</b> configuration. However, this can only be done from your build environment. To build your Debug configuration, you can still use the wizard to generate the plugin files and build the Release configuration, as this will generate all the project files for your build environment. However, after that, you'll have to open the IDE (or manually change the project files) to build and test the Debug configuration of your plugin.\n\nYou can check out some of the other links in the \\ref building_a_plugin section to learn more about how to use different build environments to build FEBio plugins.\n*/\n"
  },
  {
    "path": "Documentation/Doxygen/create_plugin_linux.dox",
    "content": "/**\r\n\\page create_plugin_linux Creating a plugin project with Linux\r\n\r\nA sample plugin, NeoHookeanPI, is included on the FEBio download page.  Review the Makefile for specifics of compiling a plugin on Linux.  The important points are:\r\n- compile with the -fPIC flag.\r\n- link with -shared -Wl,-soname,libname.so where libname is the name of the library.\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/create_plugin_osx.dox",
    "content": "/**\r\n\\page create_plugin_osx Creating a plugin project with XCode\r\n\r\nA sample plugin, NeoHookeanPI, is included on the FEBio download page.  Review the Makefile for specifics of compiling a plugin on Linux.  The important points are:\r\n\r\n- compile with the -fPIC flag.\r\n- link with -dynamiclib\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/create_plugin_vs.dox",
    "content": "/**\r\n\\page create_plugin_vs Creating a plugin project with Visual Studio\r\n\r\n\\section vs_sec1_1 Creating the Visual Studio Project\r\nAs of FEBio 4, Visual Studio 2022 is the recommended IDE for developing plugins on Windows, however VS2019 should work as well. \r\n\r\nTo create a plugin for FEBio in Visual Studio, you need to create a new dll project. To do this, open Visual Studio and select the Dynamic-Link Library (DLL) template from the list. Then, click Next.\r\n\r\n\\image html create_plugin_with_vs_step_1.png\r\n\r\nIn the next page of the wizard, give the new project a name and a location and click Create. A new dll project will be created and VS will load the new project. \r\n\r\nNote that VS already added some files to the project:\r\n\\li <b>framework.h</b> This includes Windows related header files. \r\n\\li <b>pch.h</b> This is the header file that generates the precompiled header file. \r\n\\li <b>pch.cpp</b> This file will be compiled into the precompiled header file \r\n\\li <b>dllmain.cpp</b> Starting point for the dll \r\n\r\nIn principle, none of these files are necessary for building an FEBio plugin. If you prefer to start from an empty project, feel free to delete all these files. (If you remove them, make sure to also delete them from the file system.) Deleting the pch.h and pch.cpp does require to turn off the precompiled header file feature in VS, as described below. \r\n\r\n\\section vs_sec_2 Configuring the project\r\n\r\nNext, we'll need to tell Visual Studio where to find the FEBio header (.h) and library (.lib) files. To do this, right-click on the project name in the Solution Explorer and select Properties from the popup menu.\r\nIn the Property Pages window, nagivate to <b>Configuration Properties \\ C/C++ \\ General</b>. Locate the <b>Additional Include Directories</b> property and add the path to the include files (e.g. \"C:\\Program Files\\FEBioStudio2\\sdk\\include\"). To do this, select the \"Include Directories\" property and click the button on the right of the text edit field. Select \"Edit..,\" from the popup menu. In the dialog box that pops up, the FEBio include folder can be added. Close OK when done. \r\n\r\nNext, navigate to <b>Configuration Properties \\ Linker \\ General</b>, and add the path to the FEBio library files to the <b>Additional Library Directories</b> (e.g. \"C:\\Program Files\\FEBioStudio2\\sdk\\lib\").\r\n\r\nThen, under the <b>Configuration Properties \\ Linker \\ Input</b>, add the febio libraries that you will need to the <b>Additional dependencies</b> property. Do not remove anything that is already there. Instead, click on the property's value, click on the dropdown button on the right side, and select the edit option from the dropdown menu. In the next dialog box that appears, add the febio libraries that you will need to link to. Which libraries you'll need to add, depends on the type of plugin. For example, if your developing a material plugin that is used in the solid module, you'll need to link to <i>fecore.lib</i> and <i>febiomech.lib</i>. Another thing to do is look at the header files that you are including and notice the libraries where they are located. For instance, if you are including <c>\"FEBioMech\\FEElasticMaterial.h\"</c>, then you will need to link to <i>febiomech.lib</i>. \r\n\r\nIt is also necessary to define the <b>WIN32</b> and <b>FECORE_DLL</b> preprocessor macros. This can be set in the Properties dialog box by navigating to <b>Configuration Properties \\ C/C++ \\ Preprocessor</b>. Then add <b>WIN32</b> and <b>FECORE_DLL</b> to the <b>Preprocessor definitions</b>. Note that, as with most settings in VS, entries are separated by semicolons. \r\n\r\nNote that the previous steps need to be done for both the Debug and Release configurations separately. Also make sure to link to the FEBio debug libraries when building the Debug configuration of your plugin, and similarly link to the FEBio release libraries when building the Release configuration. Using the wrong libraries may lead to build or runtime errors. Keep in mind that using the debug libraries may result in poor performance. This is to be expected. The debug libraries should only be used for initial testing and debugging. The release libraries should be used for the actual simulations. \r\n\r\n\\image html create_plugin_with_vs_step_4.png\r\n\r\nIf you deleted the pch.h and pch.cpp files that were automatically added to the project by VS, you also need to turn off the precompiled header file feature. This can be done as follows. Open the Properties dialog, and then navigate to <b>Configuration Properties \\ C/C++ \\ Precompiled Header</b>. Then, set the <b>Precompiled header</b> property to <b>Not using precompiled headers</b>. Make sure to change this for both the Debug and Release configurations. \r\n\r\nNow you are ready to write your plugin. \r\n\r\n\\section vs_sec_3 Building the project\r\n\r\nAfter you've written the plugin code, you'll need to build it. This will produce the final dynamic link library (dll) that you need to import in FEBio. \r\n\r\nBefore building the plugin, first check the configuration that is currently selected. Choose either Debug or Release depending on whether you are developing a version for testing (debug) or actual simulation (release). Remember that it is important that you selected the correct FEBio link libraries depending on the configuration. \r\n\r\nOnce you are ready to build the plugin, use the menu <b>Build \\ Build Solution</b> to build the plugin. If all went well, Visual Studio should produce a dll file. You can now use this plugin in FEBio, as described in the section \\ref using_plugins. (However, also see next section.)\r\n\r\nThe most likely errors you may see are \"unresolved external symbols\". This means that the linker was not able to locate some of the functions that you are calling in your plugin (either directly, or indirectly via another febio library). The usual solution is to find out the library these functions are located in and add it to the <b>Additional Dependencies</b> linker input option, as described above. \r\n\r\n\\section vs_sec_4 Testing the plugin\r\n\r\nFor initial testing and debugging, it might be more convenient to run the plugin code from within Visual Studio. To do this, open the Properties dialog box again for the project. Select the <b>Configuration Properties \\ Debugging</b> settings. Then, change the following settings. \r\n\r\n\\li <b>Command</b> : Specify the path to the FEBio executable here. (e.g. C:\\\\Program Files\\\\FEBioStudio2\\\\sdk\\\\bin\\\\Debug\\\\febio4.exe). \r\n\\li <b>Command Arguments</b> : Set this to <b>-import \"$(TargetPath)\"</b> (if you do this, you do not need to specify the plugin in the FEBio configuration file.)\r\n\\li <b>Working Directory</b> : Set this to the location of your test febio input files. \r\n\r\nPress OK and close the dialog box. \r\n\r\nNow, use the menu <b>Debug \\ Start Debugging</b> to start FEBio. FEBio should start, load the plugin, and display a message that it was able to load the plugin or not. Then, the FEBio prompt appears and you can run a test file by entering the command (assume the input file is called <i>test.feb</i>) <c>run test.feb</c> [ENTER]. \r\nAlternatively, you can also add the file name to the <b>Command Arguments</b> property above. (e.g. <b>-import \"$(TargetPath)\" -i test.feb</b>). In that case, FEBio will start, load the plugin and immediately run the specified input file. It will also exit immediately once the model completes. Thus, for small problems, it is possible that the whole process completes in a fraction of a second, and it may appear as nothing happened. You can always double-check the output files to check if FEBio ran correctly. \r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/debug_tools.dox",
    "content": "/**\r\n\\page debug_tools Debugging Tools\r\n\r\nAlthough the compiler and IDE debugging tools are the best tools for debugging an implementation, the FECore library also provides some useful tools that can help debug your code. \r\n\r\nIn order the use the FECore debugging tools, you must include the following header file. \r\n\r\n\\code\r\n#include <FECore/fecore_debug.h>\r\n\\endcode\r\n\r\nThe FECore debugging tools are most easily accessed via a set of predefined macros. The following macros are available.\r\n\r\n1. fecore_watch: Tells the FECore debugger to track the value of a variable. \r\n2. fecore_print: Prints the value of a watched variable to the screen. \r\n3. fecore_break: Brings up a command prompt from which users can inspect watched variables.\r\n\r\nThe FECore debugger manages so-called watched variables. A watched variable can be declared using the \\c fecore_watch macro. \r\n\r\n\\code\r\nint a;\r\nvec3d r;\r\nfecore_watch(a);\r\nfecore_watch(r);\r\n\\endcode\r\n\r\nMost variables can be made into watched variables, including most of FEBio's tensor classes (e.g. vec3d, mat3ds). \r\n\r\nThe value of a watched variable can be printed to the screen via the \\c fecore_print macro. \r\n\r\n\\code\r\n\r\n\\\\ declare watched variable\r\nmat3ds s;\r\nfecore_watch(s);\r\n\r\n\\\\ calculate s\r\n\r\n\\\\ print the value to the screen\r\nfecore_print(s);\r\n\r\n\\endcode\r\n\r\nAn alternative, and often more convenient, way for inspecting watched variables, is by setting a break point via the fecore_break macro. \r\n\r\n\\code\r\n\r\n\\\\ declare watched variable\r\nmat3ds s;\r\nfecore_watch(s);\r\n\r\n\\\\ calculate s\r\n\r\n\\\\ interrupt FEBio and bring up the febio prompt. \r\nfecore_break();\r\n\r\n\\endcode\r\n\r\nWhen the code reaches the breakpoint, FEBio will bring up a command prompt. At this prompt users can enter several commands:\r\n\r\n\\li <b>cont</b>: continue running FEBio.\r\n\\li <b>print</b>: print the value of a watched variable.\r\n\\li <b>list</b>: list all the watched variable names.\r\n\\li <b>remove</b>: remove this break point\r\n\\li <b>help</b>: print the list of available commands.\r\n\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/febio.dox",
    "content": "/** \r\n\\page febio Overview of FEBio\r\n\r\nFEBio is a finite element solver that is specifically designed for biomechanical and biophysics applications. It offers biologically relevant constitutive models and modeling scenarios.\r\n\r\nThe source code is organized into different libraries. \r\nThe following figure shows the relationship between the different libraries (sometimes referred to as modules).\r\n\r\n\\image html febio_overview.png \"Overview of FEBio's organization of modules.\"\r\n\r\nEach library collects a set of classes and functions that implement part of the FEBio feature set. The following list shows the different\r\nlibraries and the functionality they implement. A more detailed description of each library is provided below.\r\n\r\n\\li <b>FECore</b>: a library containing mostly base classes that define the FEBio framework. FECore also manages the kernel. \r\n\\li <b>NumCore</b>: a library that contains mostly third-party linear solvers\r\n\\li <b>FEBioMech</b>: a library implementing solution algorithms for solving 3D structural mechanics problems.\r\n\\li <b>FEBioMix</b>: this library extends FEBioMech and adds support for biphasic and materials with solutes (e.g. triphasic and multiphasic), as well as chemical reactions.\r\n\\li <b>FEBioFluid</b>: a library that implements a fluid mechanics solver for solving transient fluid dynamics problems, as well as a fluid-solid-interaction (FSI) solver.\r\n\\li <b>FEAMR</b>: a library that implements adaptive remeshing algorithms. \r\n\\li <b>FEImgLib</b>: a library that implements a simple 3D image structure and some basic image operations. \r\n\\li <b>FEBioOpt</b>: a library implementing parameter optimization methods.\r\n\\li <b>FEBioRVE</b>: this library implements material homogenization approaches\r\n\\li <b>FEBioTest</b>: this library implements various tests and diagnostics. \r\n\\li <b>FEBioXML</b>: An xml-parser for reading the FEBio input files\r\n\\li <b>FEBioPlot</b>: library for creating the FEBio plot files. \r\n\\li <b>FEBioLib</b>: the main FEBio library that collects all features and provides the hooks for other codes to interact with FEBio directly. \r\n\r\n\\section febio_lbis FEBio Libraries\r\n\\subsection FECore FECore\r\nThe FECore library contains all the base classes for developing finite element software. It has classes for representing meshes, constitutive models,\r\nboundary conditions, solvers, and more. Many of the classes in this library are virtual base classes. New features are implemented by deriving classes from\r\nthese base classes and overriding some of the virtual functions. This library also implements the FECore kernel class, which is used for registering\r\nclasses with the FEBio framework. Registering classes with FEBio is part of the automation mechanism that allows other modules to instantiate classes.\r\nFor instance, this mechanism makes it possible for users to implement a new feature that will automatically be recognized in the FEBio input file, without the need\r\nto write any I/O code.\r\n\r\n\\subsection NumCore NumCore\r\nThe NumCore library implements algorithms for storing and solving linear systems of equations. Most of the linear solver classes are wrappers to third-party\r\nlinear solver packages, such as Pardiso. Most of these classes are designed for dealing with sparse matrices, although a dense matrix class and a linear solver\r\nfor dense systems is implemented as well. \r\n\r\n\\subsection FEBioMech FEBioMech\r\nThis library implements algorithms for solving 3D quasi-static and dynamic structural mechanics problems. Both implicit and explicit solvers are availalbe. \r\nIt also contains a large number of nonlinear, constitutive models. Many types of boundary conditions and loads can be found here. \r\nContact algorithms for various types of tied and sliding interfaces can be found in this library as well. \r\n\r\n\\subsection FEBioMix FEBioMix\r\nThe FEBioMix library extends the FEBioMech library by solving a type of coupled fluid-mechanics problem where the domain is assumed to be a solid-fluid\r\nmixture. The fluid is assumed to be composed of a solvent that may contain one or more solutes. Optionally, the solute concentrations can be affected\r\nby chemical reactions. This library also implements contact algorithms for handling fluid flow across contacting interfaces. Both quasi-static as well\r\nas transient conditions can be modeled. \r\n\r\n\\subsection FEBioFluid FEBioFluid\r\nThe FEBioFluid library implements a fluid mechanics solver for solving quasi-static and transient fluid dynamics problems. It also implements a fluid-solid\r\n(FSI) solver that couples the solid mechanics and the fluid solver. \r\n\r\n\\subsection FEAMR FEAMR\r\nThe FEAMR (pronounced 'femur') implements adaptive meshing algorithms that allows users to make changes to the mesh as part of the solution process.\r\nApplications are element erosion, where elements are removed from the analysis based on certain criteria, and local mesh refinement, where accuracy heuristics\r\ncan be used to increase (or decrease) mesh resolution in parts of the mesh. \r\n\r\n\\subsection FEImgLib FEImgLib\r\nThis library implements a simple 3D image structure and basic image operations. This library exists mostly to support some other plugins that use images,\r\nsuch as the FEBioWarp plugin. \r\n\r\n\\subsection FEBioOpt FEBioOpt\r\nThis library implements several algorithms for performing model parameter optimizations. \r\n\r\n\\subsection FEBioRVE FEBioRVE\r\nThe FEBioRVE library implements an algorithm for performing first-order \"FE squared\" material homogenization. \r\n\r\n\\subsection FEBioTest FEBioTest\r\nThis library provides various tests and diagnostics that can be used to test different aspects of model solving with FEBio. For instance, it offers several\r\ntangent diagnostics to test whether the material tangent of a constitutive formulation is consistent with its stress evaluations. The library also has tools\r\nfor testing restart and other features. \r\n\r\n\\subsection FEBioXML FEBioXML\r\nThe FEBioXML library implements an XML-based parser that reads the FEBio input file. This library makes extensive use of the automation mechanism that\r\nis implemented in FEBio. This mechanism  facilitates the interaction between modules such that explicit knowledge of the contents of each module is\r\nnot required. A great advantage of this is that users rarely have to modify this library directly. \r\n\r\n\\subsection FEBioPlot FEBioPlot\r\nThis library implements the XPLT format that is used for storing the FEBio output results. The XPLT format is an extendible data format that can\r\neasily by customized to the needs of the model. As in the FEBioXML library, the automation mechanism is used to define the contents of the xplt file\r\nand developers will rarely need to modify this library directly. \r\n\r\n\\subsection FEBioLib FEBioLib\r\nThe FEBio library is a library that handles initialization of all the modules. It also defines the FEBioModel class, derived from FEModel,\r\nwhich handles most of the file management. This library also manages all the plugins.\r\n\r\n\\subsection FEBio FEBio\r\nThe FEBio module brings everything together by providing a command line front-end to the FEBio libraries. This project effectively builds the executable\r\nthat is run by users. It also implements a command manager, used on the FEBio prompt, manages the configuration file and offers several diagnostic tools\r\nfor debugging implementations.\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/index.dox",
    "content": "/**\r\n\\mainpage FEBio Developer's Manual\r\nThe FEBio Developer's Manual describes how to extend the default feature set of FEBio. Although users can download the source code from https://github.com/febiosoftware/FEBio and edit the source code directly, the preferred way to extend FEBio is to write a plugin. \r\n\r\nThe FEBio plugin mechanism allows users to add new features without changing the source code or building the entire source code tree. Plugins can be used to create new constitutive models, boundary conditions, loads, plot variables, as well as couple FEBio to third-party libraries. \r\n\r\nThe following links can be used to learn more about specific topics. \r\n\r\n\\li \\subpage febio\r\n\\li \\subpage plugins\r\n\\li \\subpage technical\r\n\r\nThe FEBio software is developed at the Musculoskeletal Research Laboratories at the University of Utah and at the Musculoskeletal Biomechanics Laboratory at Columbia University.\r\n\r\nAll rights reserved. Copyright (c) 2006 - 2023\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/material.dox",
    "content": "/**\r\n\\page material Material Plugins\r\n\r\nIn this section we will look at the details of setting up a plugin that implements a new constitutive formulation. The new material will be integrated seamlessly in FEBio's framework so that the user can take immediate advantage of additional functionality such as reading material parameters from the xml formatted FEBio input file, serialization to the restart archive, parameter optimization and more.\r\n\r\nThe basic procedure for creating a new material for FEBio requires the following steps.\r\n\r\n1.\tDefining a new material class by deriving it from an suitable base class.\r\n2.\tRegistering the material with FEBio's framework.\r\n3.\tDefining the material parameters.\r\n4.\tImplementing the Init() and Validate() functions (optional)\r\n5.\tDefining a MaterialPoint structure (optional)\r\n6.\tImplementing the stress and tangent functions.\r\n\r\nNext, the steps for the basic procedure will be discussed in more detail.\r\n\r\n\\section mat_sec1 Basic Procedure\r\n\r\n\\subsection step1 Defining the material class\r\n\r\nFEBio is written in C++ and therefore the new material implementation must be coded in C++ as well. Each material requires a separate class and hence the first step is to define a new class. The class has to be derived from one of the available material base classes. In this section it is assumed that the new material is derived from <b>FEElasticMaterial</b>. Materials that are derived from this base class will be materials that are used to describe isotropic, compressible solid materials. An example of such a material is the neo-Hookean material, which will be used as a case study in this section. The implementation of more advanced models will be discussed later, but they too have to follow most of the same steps.\r\n\r\nAt this point it is useful to discuss a common practice in adding new classes in C++. Usually the definition of the class is split over two separate files. One file, the so-called header file, declares the new class. The implementation of the function members are placed in a separate file, usually a .cpp file. FEBio follows this practice and it is recommended that the implementation of new plugin classes follows this practice as well. Thus, we'll have to add a MyNeoHookean.h header file that contains the class declaration. Then, we'll add a MyNeoHookean.cpp file that will contain the implementation of the class' methods. \r\n\r\nThe definition of the neo-Hookean material class looks as follows.\r\n\r\n\\code\r\n// MyNeoHookean.h\r\n#include <FEBioMech/FEElasticMaterial.h>\r\n#include <FECore/FEModelParam.h>\r\n\r\nclass MyNeoHookean : public FEElasticMaterial\r\n{\r\npublic:\r\n\t// constructor\r\n\tMyNeoHookean(FEModel*);\r\n\r\n\t// Cauchy-stress calculation\r\n\tmat3ds Stress(FEMaterialPoint& pt) override;\r\n\r\n\t// Spatial elasticity tensor calculation\r\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\r\n\r\n\t// class initialization (optional)\r\n\tbool Init() override;\r\n\r\n\t// class validation (optional)\r\n\tbool Validate() override;\r\n\r\nprivate:\r\n\t// material parameters\r\n\tFEParamDouble m_E;\r\n\tFEParamDouble m_v;\r\n\r\n\t// required macro for integrating this class with FECore\r\n\tDECLARE_FECORE_CLASS();\r\n};\r\n\\endcode\r\n\r\nThe class derives, as expected, publicly from FEElasticMaterial. It then defines a constructor, which has to take the FEModel* parameter. Next, several functions are overridden in order to implement the specifics of the neo-Hookean material. Then, a list of material parameters is defined. Finally, at the end of the class definition, the required macro <b>DECLARE_FECORE_CLASS</b> is placed, which is needed as it is part of the process of integrating the plugin class with the framework. \r\n\r\nThe MyNeoHookean.cpp file contain the implementation of the class member functions, and a few other required code (e.g. parameter definitions). The top of the file will need the necessary include statements. \r\n\r\n\\code\r\n// MyNeoHookean.cpp\r\n#include \"MyNeoHookean.h\"\r\n#include <FECore/FEModelParam.h>\r\n\r\n/* rest of code here */\r\n\\endcode\r\n\r\n\\subsection step3 Defining the material parameters\r\n\r\nDefining the material parameters of the class requires two steps. First, variables need to be defined that will store the values for these parameters. In our example, the MyNeoHookean class defines two parameters: m_E stores the Young's modulus and m_v stores the Poisson's ratio. The second step is to register the material parameters with FEBio's framework. A set of macros exist that will facilitate this process. The first one is placed in the class definition.\r\n\r\n\\code\r\nDECLARE_FECORE_CLASS();\r\n\\endcode\r\n\r\nThis macro informs FEBio that this material class will be defining a set of material parameters. The actual\r\ndefinition of the material parameter list is placed in the compilation unit (i.e. the .cpp file). In our\r\nexample, this list is found at the top of the MyNeoHookean.cpp file.\r\n\r\n\\code\r\nBEGIN_FECORE_CLASS(MyNeoHookean, FEElasticMaterial);\r\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\");\r\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1, 0.5), \"v\");\r\nEND_FECORE_CLASS();\r\n\\endcode\r\n\r\nThe parameter list definition begins with the BEGIN_FECORE_CLASS macro, which takes two parameters: the name of the class, and the name of the base class. Next, for each parameter, the ADD_PARAMETER macro can be used to define it. This macro takes three parameters: the variable that will store the parameter's value, a range identifer, and a string name for the variable. \r\n\r\nThe type of the parameter can be either a C++ built-in type (e.g. double, int, bool, std::vector) or any of the following special types. These types are preferred as they allow users to assign mathematical expressions or maps to the material parameters: \r\n\\li <b>FEParamDouble</b> Define a mappable parameter of type double \r\n\\li <b>FEParamVec3</b> Define a mappable parameter of type vec3d\r\n\\li <b>FEParamMat3d</b> Define a mappable parameter of type mat3d\r\n\\li <b>FEParamMat3ds</b> Define a mappable parameter of type mat3ds\r\n\r\nThe range identifier can be one of the following values.\r\n\r\n\\li FE_RANGE_DONT_CARE(): parameter can take on any value\r\n\\li FE_RANGE_GREATER(val): parameter must be greater than val.\r\n\\li FE_RANGE_GREATER_OR_EQUAL(val): parameter must be greater than or equal to val\r\n\\li FE_RANGE_LESS (val): parameter must be less than val\r\n\\li FE_RANGE_LESS_OR_EQUAL(val): parameter must be less than or equal to val\r\n\\li FE_RANGE_OPEN(min,max): parameter must be inside the open interval (min,max)\r\n\\li FE_RANGE_CLOSED(min,max): parameter must be inside the closed interval [min,max]\r\n\\li FE_RANGE_RIGHT_OPEN(min,max): parameter must be inside the right-open interval [min, max)\r\n\\li FE_RANGE_LEFT_OPEN(min,max): parameter must be inside the left-open interval (min, max]\r\n\\li FE_RANGE_NOT_EQUAL(val): parameter cannot equal val.\r\n\r\nDefining the valid range of a variable helps FEBio to ensure that the value of the variable remains valid throughout the analysis. FEBio will check the range every time a parameter is changed (e.g. if the parameter defines a loadcurve then the value will be checked at the start of each time step). \r\n\r\nCurrently, automatic range checking is only available for <c>double</c> and <c>int</c> parameters. It is ignored for all other parameters, including all mappable parameter types.\r\n\r\nFinally, the parameter list must end with the END_FECORE_CLASS macro. Note that there a couple of more advanced options to define material parameters, such as vector parameters and load-curve controlled parameters. These will be discussed in the advanced section below.\r\n\r\n\\subsection constructor Implementing the constructor\r\nThe constructor requires some special attention. It is always needed since it needs to pass the FEModel* parameter that it receives to the base class. \r\n\r\n\\code\r\nMyNeoHookean::MyNeoHookean(FEModel* fem) : FEElasticMaterial(fem) \r\n{\r\n\tm_E = 0.0;\r\n\tm_v = 0.0;\r\n}\r\n\\endcode\r\n\r\nIn addition, all member variables should be initialized in the constructor. Note that in this example the material parameters are of type FEParamDouble. The easiest way to initialize a parameter of this type is by assigning a value of type double. \r\n\r\n\\subsection step4 Implementing the Init function\r\n\r\nWhen implementing a new material class the user has the option to override the base class implementation of the Init function. This function is called during model initialization and can be used to allocate any data the class may require. In our example, this function is not necessary but is provided simply as an example. It is important to always call the base class as well.\r\n\r\n\\code\r\nbool MyNeoHookean::Init()\r\n{\r\n\t// call the base-class first!\r\n\tif (FEElasticMaterial::Init() == false) return false;\r\n\t\r\n\t// TODO: Do additional initialization here\r\n\t\r\n\treturn true;\r\n}\r\n\\endcode\r\n\r\n\\subsection step4b Implementing the Validate function\r\nThe Validate function is called whenever the values of the material parameters have changed. By default, FEBio will use the ranges defined in the material parameter definitions to validate materials so for most material implementation this function doest not have to be defined. However, if the material requires additional validation then the user should override the <c>Validate</c> member. Again, make sure to always call the base class version. \r\n\r\n\\code\r\nbool MyNeoHookean::Validate()\r\n{\r\n\t// call base class first!\r\n\tif (FEElasticMaterial::Validate() == false) return false;\r\n\t\r\n\t// TODO: Do additional parameter validation here\r\n\t\r\n\treturn true;\r\n}\r\n\\endcode\r\n\r\nThe Validate function should also be implemented when class members depend on material parameters. Since material parameters can change during an analysis (e.g. via a load curve), then all dependent parameters must be updated as well. \r\n\r\n\\subsection step5 Implementing the stress and tangent functions\r\n\r\nNext follows the most important aspect of the implementation: the declaration of the stress and tangent functions. These functions will describe the physical reaction of this material to an applied deformation. Note that FEBio works in the spatial frame. This implies that the stress function needs to return the Cauchy stress and the tangent function needs to return the spatial elasticity tensor.\r\n\r\nThe stress function is defined as follows in the material class definition (i.e. the header file). \r\n\r\n\\code\r\nmat3ds Stress(FEMaterialPoint& pt) override;\r\n\\endcode\r\n\r\nThis function takes one parameter of type FEMaterialPoint. This parameter stores all the important information about the point at which to calculate the stress value. For example, this variable stores the reference and current location of the point, the local deformation gradient, history variables (if defined) and much more. It also defines a bunch of useful functions that can facilitate the implementation of the stress function, such as a function that calculates the left and right Cauchy-Green tensors. There is a lot to say about this class, but in order not to digress, a detailed explanation of this class is postponed and only a few important aspects of it are mentioned here. \r\n\r\nThe actual definition of the stress function is, as usual, placed in the compilation unit. In our example, this is the MyNeoHookean.cpp file.\r\n\r\n\\code\r\nmat3ds MYNeoHookean::Stress(FEMaterialPoint& mp)\r\n{\r\n FEElasticMaterialPoint& pt =  mp.ExtractData<FEElasticMaterialPoint>();\r\n\r\n mat3d &F = pt.m_F;\r\n double detF = pt.m_J;\r\n double detFi = 1.0/detF;\r\n double lndetF = log(detF);\r\n\r\n  // calculate left Cauchy-Green tensor\r\n  mat3ds b = pt.LeftCauchyGreen();\r\n\r\n  // evaluate material parameters at current material point\r\n  double E = m_E(mp);\r\n  double v = m_v(mp);\r\n \r\n  // lame parameters\r\n  double lam = v*E/((1+v)*(1-2*v));\r\n  double mu  = 0.5*E/(1+v);\r\n \r\n  // Identity\r\n  mat3dd I(1);\r\n \r\n  // calculate stress\r\n  mat3ds s = (b - I)*(mu*detFi) + I*(lam*lndetF*detFi);\r\n \r\n  return s;\r\n }\r\n\\endcode\r\n\r\nAlthough the detailed implementation of this constitutive model will not be explained, a few important points are noted. \r\n\r\nThe FEMaterialPoint contains all the data that is defined at this particular material point. (The material points can be any point in the material, but typically are the element integration points when evaluating the FE integrals.) The first line, then extracts the component of the material point data that we need here: \r\n\r\n\\code\r\nFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\\endcode\r\n\r\nMaterials can define their own material point data classes and extract them in a similar way from the FEMaterialPoint parameter. However, in this case, the FEElasticMaterialPoint, which is defined by the FEElasticMaterial base class provides all the data we need. \r\n \r\nNext, the local deformation gradient is accessed from the material point data, as well as the local Jacobian (which is the determinant of the deformation gradient ). These are data members that can be accessed directly. \r\n\r\n\\code\r\n mat3d &F = pt.m_F;\r\n double detF = pt.m_J;\r\n\\endcode\r\n\r\nThe FEElasticMaterialPoint class has some useful member functions to calculate commonly needed tensors (e.g. left and right Cauchy-Green tensors.) Here, the left Cauchy-Green tensor is retrieved using the LeftCauchyGreen member function. A more detailed description of the available data and function members can be found in the advanced section. \r\n\r\n\\code\r\n  mat3ds b = pt.LeftCauchyGreen();\r\n\\endcode\r\n\r\nNext, the material parameters are evaluated at the current material point. \r\n\r\n\\code\r\n  double E = m_E(mp);\r\n  double v = m_v(mp);\r\n\\endcode\r\n\r\nAfter all the necessary tensors are evaluated, the stress can be calculated. FEBio defines a large number of classes that facilitate the use of tensors. For example, the mat3ds class implements a second-order 3D symmetric tensor of doubles (the d stands for double). The mat3dd class implements a second-order 3D diagonal tensor.  We will see some examples of fourth-order tensor classes in the tangent function. These tensors classes make it easy to implement otherwise complicate tensorial expressions. \r\n\r\n\\code\r\n mat3ds s = (b - I)*(mu*detFi) + I*(lam*lndetF*detFi);\r\n\\endcode\r\n\r\nFinally, the calculated stress value at the current material point. Note that the variable returned is of type mat3ds, that is, a symmetric second-order tensor. \r\n\r\nThe tangent function is declared in the class definition as well.\r\n\r\n\\code\r\ntens4ds Tangent(FEMaterialPoint& pt) override;\r\n\\endcode\r\n\r\nThis function too takes a single FEMaterialPoint variable as input. Note that in this case the return value is of type tens4ds which is a class that implements a fourth-order tensor with major and minor symmetries. The definition of the function can be found in the MyNeoHookean.cpp file.\r\n\r\n\\code\r\ntens4ds MyNeoHookean::Tangent(FEMaterialPoint& mp)\r\n{\r\n   FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\r\n\t// deformation gradient\r\n\tmat3d &F = pt.m_F;\r\n\tdouble detF = pt.m_J;\r\n\r\n\t// evaluate material parameters at current material point\r\n\tdouble E = m_E(mp);\r\n\tdouble v = m_v(mp);\r\n\r\n\t// lame parameters\r\n\tdouble lam = v*E/((1+v)*(1-2*v));\r\n\tdouble mu  = 0.5*E/(1+v);\r\n\r\n\tdouble lam1 = lam / detF;\r\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\r\n\r\n\tmat3dd I(1);\r\n\ttens4ds IxI = dyad1s(I);\r\n\ttens4ds I4 = dyad4s(I);\r\n\r\n\treturn IxI*lam1 + I4*(2*mu1);\r\n}\r\n\\endcode\r\n\r\nFirst, the data of the material point data that pertains to elastic materials is extracted. The next few lines extract some data from the FEElasticMaterialPoint variable and calculate some other parameters, similarly to the evaluation of the stress. \r\n\r\nNext, several tensor are evaluated that are need for the material tangent. Note the use of the fourth-order tensor class tens4ds. This code snippet also illustrates the use of the dyadic products to create fourth-order tensors from second-order tensors. A more detailed explanation of the use of the tensor classes can be found in chapter 3 of the FEBio Theory Manual.\r\n\r\n\\code\r\n\tmat3dd I(1);\r\n\ttens4ds IxI = dyad1s(I);\r\n\ttens4ds I4 = dyad4s(I);\r\n\\endcode\r\n\r\n\\subsection step2 Registering the new material\r\n\r\nIn order for FEBio to recognize the new material, the material needs to be registered with the framework. In a plugin, this is done in the PluginInitialize function, which must be provided by the user. (see \\ref register for more details implementing the required functions.)\r\n\r\n\\code\r\nFECORE_EXPORT void PluginInitialize(FECoreKernel& fecore)\r\n{\r\n\t\\\\ Set the kernel to match FEBio's kernel\r\n\tFECoreKernel::SetInstance(&fecore);\r\n\t\r\n\t\\\\ Register the classes in this plugin\r\n\tREGISTER_FECORE_CLASS(MyNeoHookean, \"neo-Hookean\");\r\n}\r\n\\endcode\r\n\r\nWith the new feature class registered, FEBio will be able to allocate it when referenced in the febio input file, and call the appropriate member functions when needed.\r\n\r\n\\section mat_sec2 The FEMaterialPoint class\r\n\r\nAs was shown above, the stress and tangent are evaluated at so-called material points. These are points at which the state of the deformation is tracked. Most often, these points correspond to the integration points of the elements. \r\n\r\nThe FEMaterialPoint class contains all the data that is stored at the material points. Material classes can append additional data to this class as is described below in one of the advanced topics. Here, we'll take a look at the FEMaterialPoint class itself. \r\n\r\nThe FEMaterialPoint class contains the following member variables that may be useful in the evaluation of the stress and tangent. \r\n\r\n\\li vec3d m_r0: the position of the material point in the reference frame\r\n\\li vec3d m_rt: the position of the material point in the spatial frame\r\n\r\nNote that the jacobians, are the jacobians of the transformation from the isoparametric space to the reference or current spatial frames. (These are not the derivates of the deformation gradient!)\r\n\r\nThe FEMaterialPoint class also contains a linked list of FEMaterialPointData classes, which hold the additional data needed by specific material implementations. One particular FEMaterialPointData class that we've encountered above is the FEElasticMaterial class, which holds additional data relevant for hyperelastic materials. \r\n\r\nThe FEElasticMaterialPoint class defines the state of an elastic material point, or more precisely, the state of a material derived from the FEElasticMaterial class. It defines the following additional state variables.\r\n\r\n\\li mat3d m_F: the deformation gradient\r\n\\li double m_J: the Jacobian, that is the determinant of the deformation gradient\r\n\\li mat3ds m_s: the Cauchy stress tensor\r\n\r\nIf your material derives from FEElasticMaterial (or FEUncoupledMaterial) and no additional state data is required, you do not need to derive a new material point class. For all elastic materials this is the default material point data class to store the point data. \r\n\r\nA note of caution on the stress member variable. Usually the stress is evaluated before the tangent, so you could use the FEElasticMaterialPoint::m_s member in the evaluation of the tangent if needed. However, it is often safer to re-evaluate the stress in the tangent function than relying on the value of m_s. In fact, it is recommended that this member variable should not be accessed when evaluating the Stress and Tangent functions. \r\n\r\nThis FEElasticMaterialPoint class also defines some useful functions that can facilitate the implementation of stress and tangent functions. \r\n\r\n\\li Strain: Calculate the Euler-Lagrange strain tensor.\r\n\\li RightCauchyGreen: Calculate the right Cauchy-Green strain tensor.\r\n\\li RightCauchyGreen: Calculate the left Cauchy-Green strain tensor.\r\n\\li DevRightCauchyGreen: Calculate the deviatoric right Cauchy-Green tensor.\r\n\\li DevLeftCauchyGreen: Calculate the deviatoric left Cauchy-Green tensor.\r\n\\li pull_back: Calculate the pull-back of a 2nd order tensor.\r\n\\li push_back: Calculates the push-back of a tensor. Two versions are defined. One for 2nd order symmetric tensors, and one for 4th order symmetric tensors.\r\n\r\nFor materials that require additional information to be stored at the material points (e.g. history-dependent materials) a new material point data class needs to be defined, by deriving from the FEMaterialPointData class. \r\n\r\n\\section mat_sec3 Using the new material class\r\n\r\nIf the steps to register the material and its material parameters have been followed as outlined in the previous sections, the material class will be seamlessly integrated in FEBio's framework. One of the important consequences of this is that the xml-input file reader will automatically recognize the new material and its parameters. For example, imagine the user created a new class MyFancyMaterial and named it \"fancy material\" by registering the material as follows. \r\n\r\n\\code\r\nREGISTER_FECORE_CLASS(MyFancyMaterial, \"fancy material\");\r\n\\endcode\r\n\r\nThis macro associates the name \"fancy material\" with the MyFancyMaterial class. This name will now be used as the type identifier in the xml-input file.\r\n\r\n\\code\r\n<material id=\"1\" type=\"fancy material\">\r\n\t...\r\n</material>\r\n\\endcode\r\n\r\nFEBio will recognize the type identifier as the name of the MyFancyMaterial class and will create an instance of this class. For all the elements in the mesh that have the material ID of this class (in this case \"1\"), the stress and tangent functions of the new class will be automatically called.\r\n\r\nMaterial parameters are identified in a similar way. For each material parameter, the ADD_PARAMETER macro associates a name with the parameter. For example, imagine that for our new class MyFancyMaterial the following parameter is defined,\r\n\r\n\\code\r\nADD_PARAMETER(m_a, \"a\");\r\n\\endcode\r\n\r\nThe user can now enter a value for this parameter in the FEBio input file as follows,\r\n\r\n\\code\r\n<material id=\"1\" type=\"fancy material\">\r\n\t<a>0.123</a>\r\n</material>\r\n\\endcode\r\n\r\nFEBio will now automatically read the value (here 0.123) and store it in the m_a variable which will be defined as a public member variable of the MyFancyMaterial class.\r\n\r\n\\section mat_sec4 Testing the Material Implementation\r\n\r\nImplementing a new material formulation can be tricky sometimes. Particularly the implementation of the correct tangent stiffness is often quite challenging. For this reason, FEBio offers a few tools that can help in diagnosing a new material implementation.\r\n\r\nFirst, it is highly recommended to use the available tensor classes to implement the stress and tangent stiffness of the material. These classes allow the user to stay as true as possible to the mathematical formulation, facilitating the readability of the code. Obvious mistakes, such as sign errors, will therefore be relatively easy to spot by direct comparison of the code with the mathematical equations. \r\n\r\n\\subsection tangent_diagnostic The Tangent Diagnostic\r\n\r\nFEBio offers a tangent diagnostic tool that allows the user a more direct inspection of the tangent implementation. The tool basically compares the actual implementation of the tangent with an approximation that is obtained by calculating the finite difference of the residual. To run the diagnostic, a separate FEBio input file needs to be defined. An example for the neo-Hookean material follows.\r\n\r\n\\code\r\n<?xml version=\"1.0\"?>\r\n<febio_diagnostic type=\"tangent test\">\r\n\t\t<Control>\r\n\t\t\t<time_steps>1</time_steps>\r\n  \t\t<step_size>1</step_size>\r\n  \t\t<plot_level>PLOT_DEFAULT</plot_level>\r\n\t\t</Control>\r\n\t\t<Scenario type=\"uni-axial\">\r\n\t\t\t<strain>0.15</strain>\r\n \t\t</Scenario>\r\n \t\t<Material>\r\n \t\t\t<material id=\"1\" name=\"Solid\" type=\"neo-Hookean\">\r\n \t\t\t\t<E>1</E>\r\n \t\t\t\t<v>0.45</v>\r\n \t\t\t</material>\r\n \t\t</Material>\r\n </febio_diagnostic>\r\n\\endcode\r\n\r\nThe diagnostics input file also takes an xml-formatted input file and is structured similarly as the FEBio input file. The first line is the xml declaration as required by the xml standard. The next line defines the root element of the xml format. In this case, it is defined as febio_diagnostics to indicate that this file is a diagnostics file. The name attribute identifies the type of diagnostic this file describes, and in this case this is a \"tangent test\". Next follows the definition of the three sections of the file.\r\n\r\nThe first section, the Control section, defines some general control settings such as the number of timesteps, time step size and so on. \r\n\r\nThe second section, the Scenario section, defines the type of model and boundary conditions to apply. This section replaces the geometry section in the usual FEBio input file. The geometry is now defined implicitly through the scenario. The uni-axial scenario runs a simple uni-axial tension or compression problem on a unit cube. The maximum strain level can be defined through the strain parameter. \r\n\r\nThe third section defines the material that will be assigned to the model. In the uni-axial scenario, only one material needs to be defined with the corresponding material parameters. Note that when the material class is properly registered with the framework as explained above, no additional steps need to be taken to use the tangent diagnostics feature aside from creating the diagnostics input file for the new material.\r\n\r\nTo run the tangent diagnostic, simply type the following at the command prompt.\r\n\r\n\\code\r\n>febio -d filename\r\n\\endcode\r\n\r\nNote that the command option -d needs to be used instead of the usual -i to inform FEBio that you are running a diagnostics problem and not a regular model. Replace \"filename\" with the name of the actual input file.\r\n\r\nThis diagnostic test outputs a log file that contains the tangent stiffness as calculated from the implementation and a finite difference approximation to this tangent. It also contains the difference between these two matrices and the matrix element where the difference is largest. Although a small difference between the two matrices can be expected due to the finite difference approximation, the difference should be small, e.g. less than 0.01%. If this is not the case, there is probably a mistake in either the Stress function or in the Tangent function or both. To identify the culprit, the result of the simulation, which is reported as usual in the plot file, can be compared to a known solution (or a solution obtained in a different fashion). If the solutions correspond, then the problem most likely lies with the tangent implementation. If the solutions do not agree, then the implementation of the Cauchy stress is probably also erroneous. \r\n\r\n\\subsection secant_tangent Secant Tangent\r\nFor particularly complex constitutive models the derivation of the tangent can be a daunting task. For this reason, there is an option that evaluates the tangent using an secant approximation. \r\n\r\nIn order to use the secant tangent approximation, override the UseSecantTangent() and have it return true: \r\n\r\n\\code\r\nbool UseSecantTangent() override { return true; }\r\n\\endcode\r\n\r\nWhen this function returns true, FEBio will not call the Tangent member (which does need to be defined, but can be left empty), but instead use an approximation. \r\n\r\nKeep in mind that the secant tangent is just an approximation and may not result in an accurate representation of the actual material tangent. The consequence of this could be poor convergence or even failure to converge. In general it is best to calculate and implement the consistent material tangent, but the secant approximation might be a good place to start. \r\n\r\n\\section mat_sec5 Advanced Topics\r\n\r\n\\subsection subsec61 Array parameters\r\n\r\nIt is possible to define an array of parameters using a single material parameter declaration. This can be done by first defining a member variable as an array in the class definition. For example, imagine that the new material class has the following variable declared.\r\n\r\n\\code\r\ndouble\tm_a[3];\r\n\\endcode\r\n\r\nTo define the variable m_a as the storage for a material parameter, specify the size of the array as the second argument. For example,\r\n\r\n\\code\r\nADD_PARAMETER(m_a, 3, \"a\");\r\n\\endcode\r\n\r\nIn the input file, the parameter's values can then be defined using a comma-separated list. For example,\r\n\r\n\\code\r\n<a>0.1, 0.23, -0.73</a>\r\n\\endcode\r\n\r\nThere is no limitation on the size of array parameters. Currently, the only types that are supported for array parameters are int and double. \r\n\r\n\\subsection subsec62 Uncoupled Materials\r\n\r\nIncompressible materials are an important class of materials since they are dealt with in a very particular manner. FEBio assumes that such materials use a decoupled hyperelastic strain energy function.\r\n\r\n\\f[\r\n\tW(\\mathbf{C})=\\tilde{W}(\\tilde{\\mathbf{C}})+U(J)\r\n\\f]\r\n\r\nHere, \\f$\\mathbf{C}\\f$ is the right Cauchy-Green tensor, \\f$\\tilde{\\mathbf{C}}\\f$ is the deviatoric right Cauchy-Green tensor and J is the Jacobian. Since the incompressibility constraint can sometimes be hard to enforce for these materials with the usual displacement formulation of FE, a different formulation is used. FEBio uses a three-field formulation that requires a separate integration rule for the dilatational stiffness contribution. We refer to the FEBio theory manual for a more detailed description of the theory of incompressible hyperelasticity. As a consequence of the different formulation, incompressible materials require a few changes to the basic procedure.\r\n\r\nFirst, incompressible materials using a decoupled strain energy function, need to be derived from the base class FEUncoupledMaterial. An example of such a class is the FEMooneyRivlin material. This class is defined as follows.\r\n\r\n\\code\r\nclass FEMooneyRivlin : public FEUncoupledMaterial\r\n{\r\n\t//\t...\r\n};\r\n\\endcode\r\n\r\nThe second important difference relates to the calculation of the stress. For a material with an uncoupled strain energy function, the stress is given by,\r\n\r\n\\f[\r\n\t\\mathbf{\\sigma} = p\\mathbf{I}+\\frac{2}{J}\\text{dev}(\\mathbf{\\tilde{F}}\\frac{\\partial W}{\\partial C}\\mathbf{\\tilde{F}}^{T})\r\n\\f]\r\n\r\nThe pressure p is calculated by FEBio. The only thing that the material class needs to implement is the second term. This must be done in the DevStress member function which is inherited from FEUncoupledMaterial. For example, for the Mooney-Rivlin material, the stress is calculated as follows.\r\n\r\n\\code\r\nmat3ds FEMooneyRivlin::DevStress(FEMaterialPoint& mp)\r\n{\r\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\t\t...\r\n\t\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\r\n\r\n\t\treturn T.dev()*(2.0/J);\r\n}\r\n\\endcode\r\n\r\nSimilarly, the elasticity tensor requires a slightly different form. It can be shown that it can be decomposed as follows.\r\n\r\n\\subsection subsec63 User-defined material points\r\n\r\nIn the previous sections we touched briefly on the concept of material points. In this section we will elaborate on this important topic and discuss how it can be used to add additional material point data, e.g. for use in history-dependent materials. \r\n\r\nA material point is a point in the material at which the state is stored. Usually these correspond to the integration points of the elements. The state of the material at this point is defined by the position, the deformation gradient, the stress and so forth. \r\n\r\nThe FEMaterialPoint class (defined in FEMaterialPoint.h) contains a linked list of classes derived from FEMaterialPointData. The linked list is populated by the materials as described below, and materials can extract particular data classes using the ExtractData member of the FEMaterialPoint class. For example, we already saw above how to extract the FEElasticMaterialPoint data. \r\n\r\n\\code\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\\endcode\r\n\r\nIf your material requires storing additional data, then you will need to create your own material point data class. This is the case, for instance, if your material is history dependent. A visco-elastic material is a good example and we'll use it as an example. \r\n\r\nTo define additional material data, one would need to first derive a new material point class from the FEMaterialPoint data base class. \r\n\r\n\\code\r\nclass FEViscoElasticMaterialPoint : public FEMaterialPointData\r\n{\r\npublic:\r\n\tenum { MAX_TERMS = 6 };\r\n\r\npublic:\r\n\t//! constructor\r\n\tFEViscoElasticMaterialPoint(FEMaterialPointData* mp = nullptr);\r\n\r\n\t//! copy material point data\r\n\tFEMaterialPointData* Copy();\r\n\r\n\t//! Initialize material point data\r\n\tvoid Init();\r\n\r\n\t//! Update material point data\r\n\tvoid Update(const FETimeInfo& timeInfo);\r\n\r\n\t//! Serialize data to archive\r\n\tvoid Serialize(DumpStream& ar);\r\n\r\npublic:\r\n\tmat3ds\tm_Se;\t//!< elastic PK2 stress\r\n\tmat3ds\tm_Sep;\t//!< elastic 2nd PK stress at previous time\r\n\r\n\tmat3ds\tm_H[MAX_TERMS];\t\t//!< internal variables\r\n\tmat3ds\tm_Hp[MAX_TERMS];\t//!< internal variables at previous timestep\r\n\r\n    double  m_alpha[MAX_TERMS];     //!< exponent of right-stretch tensor in series spring\r\n    double  m_alphap[MAX_TERMS];    //!< alpha at previous time step\r\n    \r\n\tdouble\tm_sed;\t//!< elastic strain energy density\r\n\tdouble\tm_sedp;\t//!< elastic strain energy density at previous time\r\n};\r\n\\endcode\r\n\r\nThe class inherits from FEMaterialPointData, defines several member variables that record the material point's state, and overrides several functions. The following functions should be defined: \r\n\r\n\\li Constructor: initialize all the class' member variables. \r\n\\li Copy: return a copy of this material point class\r\n\\li Init: initialize all the class' member variables. This function is called during a model reset, and the class should reset its default state. \r\n\\li Update: This is called at the start of a new time step, and the class can use this to update its internal state. \r\n\\li Serialize: Data should be stored and retrieved when the dumpstream is being written, read respectively. This is to ensure that the material works properly with FEBio's running and cold restart capabilities. \r\n\r\nAfter defining the custom material point, it needs to be allocated and appended to the data linked list of each material point that belongs to this material. Much of this is handled by the framework, but the only thing that needs to be done is override and implement the CreateMaterialPointData class in the new material class. \r\n\r\n\\code\r\n\tFEMaterialPointData* CreateMaterialPointData() override;\r\n\\endcode\r\n\r\nFor the viscoelastic material example above, this function is defined in FEViscoElasticMaterial. \r\n\r\n\\code\r\nFEMaterialPointData* FEViscoElasticMaterial::CreateMaterialPointData()\r\n{\r\n\treturn new FEViscoElasticMaterialPoint(m_Base->CreateMaterialPointData());\r\n}\r\n\\endcode\r\n\r\nThe viscoelastic material defines a property, stored in the m_Base member, which can also store material point data. Therefore, the CreateMaterialPointData of this member needs to be called. Material properties are discussed in the next section. \r\n\r\nFinally, to use the custom material point data, just call the ExtractData member function of the FEMaterialPoint class. \r\n\r\n\\code\r\nmat3ds FEViscoElasticMaterial::Stress(FEMaterialPoint& mp)\r\n{\r\n\tFEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\r\n}\r\n\\endcode\r\n\r\n\\subsection subsec64 Material Properties\r\n\r\nIn FEBio, materials can define material properties. A material property is different in a few ways from material parameters. Most importantly, a material property is defined by a pointer to a base class. The explicit class that is allocated and assigned will not be known until runtime. For example, the biphasic material requires a \"solid\" material property and a \"permeability\" material property. But which particular solid material or permeability is needed will be decided by the user in the FEBio input file. \r\n\r\nConsider for example the following class:\r\n\r\n\\code\r\nclass MyViscoMaterial : public FEElasticMaterial\r\n{\r\npublic:\r\n\tMyViscoMaterial(FEModel* pfem);\r\n\t\r\n\tmat3ds Stress(FEMaterialPoint& mp);\r\n\t\r\n\ttens4ds Tangent(FEMaterialPoint& mp);\r\n\t\r\nprotected:\r\n\t\\\\ Define the solid material property\r\n\tFEElasticMaterial*\tm_Solid;\r\n};\r\n\\endcode\r\n\r\nAside from the usual stuff (constructor, stress, tangent functions), it defines a variable, named m_Solid, of type FEElasticMaterial* that represents the solid phase of the visco-elastic material. \r\n\r\nYou can now turn this variable into a property of the material by defining it in the class descriptor using the ADD_PROPERTY macro. \r\n\r\n\\code\r\nBEGIN_FECORE_CLASS(MyViscoMaterial, FEElasticMaterial)\r\n\tADD_PROPERTY(m_Solid, \"solid\");\r\nEND_FECORE_CLASS();\r\n\\endcode\r\n\r\nThis adds a property  with the name \"solid\" to the property list of the material. The name is used in the input file for defining the solid property. \r\nA specific solid material can be created using the <c>type</c> attribute. A definition for this new material will look like this. (Assuming the new material was registered under the name \"my_visco\").\r\n\r\n\\code\r\n<material id=\"1\" type=\"my_visco\">\r\n\t<solid type=\"neo-Hookean\">\r\n\t\t<E>1.0</E>\r\n\t\t<v>0.3</v>\r\n\t</solid>\r\n</material>\r\n\\endcode\r\n\r\nThe viscoelastic material is an example of a <i>nested material</i> class. This is a solid material that uses other solid materials as member variables. In this case, the properties will typically variables of type FEElasticMaterial or FEUncoupledMaterial. \r\n\r\nIn general, properties can be created from most FECore base classes. For the purpose of materials, the FEMaterialProperty base class might be a good place to start, aside from nested materials. One important reason to derive material properties from this class is that is facilitates accessing the local material orientation. \r\n\r\nFor example, the permeability property of the biphasic material is defined as follows. \r\n\r\n\\code\r\nclass FEBIOMIX_API FEHydraulicPermeability : public FEMaterialProperty\r\n{\r\npublic:\r\n\tFEHydraulicPermeability(FEModel* pfem) : FEMaterialProperty(pfem) {}\r\n    \r\n\t//! hydraulic permeability\r\n\tvirtual mat3ds Permeability(FEMaterialPoint& pt) = 0;\r\n    \r\n\t//! tangent of hydraulic permeability with respect to strain\r\n\tvirtual tens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) = 0;\r\n    \r\n\t//! tangent of hydraulic permeability with respect to concentration\r\n\tmat3ds Tangent_Permeability_Concentration(FEMaterialPoint& mp, const int isol);\r\n\r\n\tFECORE_BASE_CLASS(FEHydraulicPermeability);\r\n};\r\n\\endcode\r\n\r\nNote that this function is a virtual base class and the expectation is that particular permeability models are implemented in derived classes. This will be the typical case for material properties: Create an abstract base class from FEMaterialProperty, and then implement specific functionality by deriving from the abstract base class. \r\n\r\nIn order to use material properties, they need to be registered with the framework in the same way as the material classes (and other plugin classes) themselves. (i.e. using the REGISTER_FECORE_CLASS in the PluginInitialize function.)\r\n\r\nTo use a particular material property in an FEBio model, use the type attribute followed by the name that the particular property was registed with. \r\n\r\n\\code\r\n<material id=\"1\" type=\"biphasic\">\r\n\t<solid type=\"neo-Hookean\">\r\n\t\t<!-- solid material parameters -->\r\n\t</solid>\r\n\t<permeability type=\"const\">\r\n\t\t<!-- permeability parameters -->\r\n\t</permeability>\r\n</material>\r\n\\endcode\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/modules.dox",
    "content": "/**\n\\page modules FEBio Modules\n\nFEBio organizes all its features in \\b modules. A module in FEBio is similar to a namespace in c++ and helps in resolving ambiguities in cases where features have the same type string.\n\nModules collect features that logically belong together, such as features needed for solving a particular type of physics problem. \n\nAs of FEBio4, all features, including those defined in plugins, require the specification of the module to which they are added. This is typically done by setting the active module before registering the features. \n\n\\section active_mod Setting the Active Module\n\nSetting the active module is done by calling \\c FECoreKernel::SetActiveModule.\n\n\\code\nFECoreKernel& fecore = FECoreKernel::GetInstance();\nfecore.SetActiveModule(\"solid\");\n\\endcode\n\nFeatures registered after this call will be added to the active module. \n\nAs of FEBio4, the following modules are defined. \n\n\\li \\b solid The solid mechanics module. \n\\li \\b biphasic Extends the solid module and adds biphasic simulation capabilities.\n\\li \\b solute Extends the biphasic module and adds the ability to model solutes in the solvent of a biphasic material. \n\\li \\b multiphasic Extends the solute module by adding multiple solutes to a biphasic material, as well as chemical reactions between solutes. \n\\li \\b fluid Fluid mechanics module.\n\\li \\b fluid-FSI Combines the solid and fluid modules to create fluid-solid interaction modeling capabilities. \n\nAdditional modules may be available via plugins. For instance the FEBioHeat plugin adds the \\b heat module for solving heat transfer analyses. \n\nIf a feature should be made available for all modules, simply set the active module to zero. \n\n\\code\nfecore.SetActiveModule(nullptr);\n\\endcode\n\nFeatures registered after this call will be available to all modules. \n\n\\section creating_mods Creating New Modules\n\nNew modules can be created in a few different ways. The easiest way is to call \\c FECoreKernel::CreateModule(const char* modName). \n\n\\code\nfecore.CreateModule(\"new module\");\n\\endcode\n\nThe new module will automatically be set as the active module. \n\nAn optional argument can be specified that provides additional information about the module. The optional argument is a JSON structured text that allows you to provide a title, a description, author and version string. \n\n\\code\n\tfecore.CreateModule(\"new module\", \n\t\t\"{\"\n\t\t\"   \\\"title\\\"  : \\\"A New Module\\\",\"\n\t\t\"   \\\"info\\\"   : \\\"A module for solving relativistic quantum-chromodynamics simulations.\\\"\"\n\t\t\"   \\\"author\\\" : \\\"Inco Gnito\\\"\"\n\t\t\"   \\\"version\\\": \\\"3.14\\\"\"\n\t\t\"}\");\n\\endcode\n\nNote that none of the fields in the JSON text are required. \n\nInternally, FEBio stores a list of classes derived from FEModule that manages all the information regarding the module. Therefore, modules can also be created by deriving a new class from the FEModule base class. \n\n\\code\nclass MyNewModule : public FEModule\n{\npublic:\n\tMyNewModule() : FEModule(\"new module\") {}\n};\n\\endcode\n\nThe module can then be registered by instantiating the new class. \n\n\\code\n\tfecore.CreateModule(new MyNewModule()); \n\\endcode\n\n\\section nested_mod Nested Modules\n\nModules can be nested. This means that a module can inherit the features from another module, in addition, to defining its own features. \n\nTo add a module dependency to the currently active module, call FECoreKernel::AddModuleDependency. \n\n\\code\n\tfecore.AddModuleDependency(\"solid\");\n\\endcode\n\nIn this case, all features defined in the \\c solid module will then be available in the currently active module. \n\n\\section use_mods Using Modules\n\nThe FEBio input file defines an FEBio model. Before the model can be constructed, the active module must be set and it must be defined as the first tag in the FEBio input file. \n\nFor example, \n\n\\code\n\t<Module type=\"biphasic\"/>\n\\endcode\n\nThis makes the \"biphasic\" module active, and only features that belong to this module (and its dependencies) can be referenced in the input file. \n\n*/\n"
  },
  {
    "path": "Documentation/Doxygen/pipeline.dox",
    "content": "/**\r\n\\page pipeline The FEBio Pipeline\r\n\r\nThis document describes the standard pipeline that FEBio executes when solving a model defined via the FEBio input file. To be more precise, after startup and initialization, FEBio executes a task. The default task will solve the model defined in the input file. Therefore, this document is split into two sections. First, we'll look at the execution of the startup procedure, which is independent of the selected task. Then a description of FEBio's standard solver task follows.\r\n\r\n\\section febio_startup FEBio startup\r\n\r\n\\subsection febio_main The main Function\r\nFEBio's main function resides in the file FEBio/FEBio.cpp. It first initializes the FEBio library by calling febio::InitLibrary(), which will load all the modules. Then it parses the command line arguments. If an input file was defined on the command line (with the -i option), it will call the Run function, otherwise the FEBio interactive prompt is shown where users can enter the input file. An important difference between specifying the input file on the command line and entering it on the FEBio prompt, is that FEBio will exit after solving a model when the input file is entered on the command line. If the FEBio prompt is used, FEBio will return to the FEBio prompt after solving a model. In either case, the Run function is called to solve the model.\r\n\r\n\\subsection febio_run the Run Function\r\nTo solve a model (or any task) FEBio calls the Run function, which can be found in FEBio/FEBio.cpp. The step it executes are:\r\n\r\n1. Create an instance of an FEBioModel class. \r\n2. Call the Input member of the FEBioModel class to read the input file.\r\n3. Create a task to execute. \r\n4. Run the task by calling the Run member of the task class. \r\n\r\nThe task that is executed is specified on the command line. If no task is specified, the default task, which is implemented in the FEBioStdSolver class, will be created. This task will simply call the Solve method of the FEModel class, which solves the model. \r\n\r\nRegardless of the task, usually at some point the FE problem must be solved. This is done by calling the FEModel::Solve member. Note that the FEBio library defines a derived class, called FEBioModel, which is responsible for all the input and output file management. The FEModel::Solve function is discussed in the next section.\r\n\r\n\\section solver Solving the FE model\r\n\\subsection femodel_solve The FEModel::Solve method\r\nThe FEModel::Solve method is called whenever the finite element model, defined in the FEModel class, needs to be solved. \r\n\r\nIn FEBio, a finite element model is defined at the highest level as a sequence of analyses steps. Each analysis, implemented as an instance of the FEAnalysis class, defines what exactly will be solved for, and what boundary conditions and loads will be applied during that step. The FEModel::Solve function simply loops over all the analyis steps defined in the model and executes them one by one. Execution of an analysis proceeds in three steps:\r\n\r\n1. Step activation: allow the step to initialize itself prior to execution.\r\n2. Solve the step: call the FEAnalysis::Solve method to solve the analysis\r\n3. Step deactivation: allow the step to clean up after itself. \r\n\r\n\\subsection feanalysis_solve The FEAnalysis::Solve method\r\nThe FEAnalys::Solve method executes the time stepping loop. The time steps are incremented in either fixed time increments or variable increments, depending on whether the auto-time stepper is enabled or not. \r\n\r\nFor each time step, the analysis class will call the FESolver::SolveStep method. The FESolver is an abstract base class, and classes that implement different types of FE solvers are derived from this class. What particular solver was assigned to the analysis will depend on the type of analysis that is defined in the input file. This is essentially determined by the Module tag of the input file. \r\n*/"
  },
  {
    "path": "Documentation/Doxygen/plugin_basics.dox",
    "content": "/**\r\n\r\n\\page plugin_basics Plugin Basics\r\n\r\nPlugins are a powerful mechanism for extending FEBio's basic feature set. A plugin is a dynamically linked library that can be loaded by FEBio at runtime. Plugins are typically build and maintained separately from the FEBio source code. \r\n\r\nPlugins can be used to develop new constitutive models, boundary conditions, loads, and other model components. They can be used to extend the xplot database (i.e. plotfile), and log files, and can be used to couple FEBio to other codes. \r\n\r\nSelect one of the links below to learn more about a particular aspect\r\nof plugin development.\r\n\r\n\\li \\subpage building_a_plugin\r\n\\li \\subpage required_functions\r\n\\li \\subpage register\r\n\r\nOnce you're done developing a plugin, check out the following article that details how to use plugins in FEBio. \r\n\r\n\\li \\subpage using_plugins\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_bc.dox",
    "content": "/**\n\\page plugin_bc Boundary Condition Plugin\n\nBoundary condition plugins can be developed for applying special boundary conditions that might be difficult to achieve using the regular boundary condition options. \n\nThere are two possible starting points for BC plugins: FEPrescribedNodeset can be used as a base class if the BC can be applied directly to a set of nodes. Alternatively, FEPrescribedSurface if the BC is prescribed more naturally on a surface (e.g. if the prescribed value relates to the surface normal). In either case, since FEBio requires the nodal prescribed values, the plugin class will need to be able to return the desired prescribed value for a particular nodal degree of freedom. \n\n\\section sec_bc_node Prescribed Nodesets\nFor directly prescribing the values of nodal degrees of freedom, create a new class from FEPrescribedNodeSet and override the GetNodelValues function. \n\n\\code\nclass MyBC : public FEPrescribedNodeSet\n{\npublic:\n\tMyBC(FEModel* fem) : FEPrescribedNodeSet(fem){}\n\n\tvoid GetNodalValues(int lid, std::vector<double>& val) override;\n};\n\\endcode\n\nThe \\c GetNodalValues takes two parameters:\n\\li <b>int</b> : The local index into the node set associated with this bc. \n\\li <b>std::vector<double>&</b>: return value with the values of the prescribed nodal degrees of freedom. \n\n\\section sec_bc_surf Prescribed Surface\nInstead of a nodeset, boundary conditions can also be applied to surfaces. This allows plugin classes to use other information, e.g. surface normals, to define the nodal values. However, just like the node set approach above, the boundary condition derived from FEPrescribedSurface should also implement the GetNodalValues function. \n\n\\code\nclass MySurfBC : public FEPrescribedSurface\n{\npublic:\n\tMySurfBC(FEModel* fem) : FEPrescribedSurface(fem){}\n\n\tvoid GetNodalValues(int lid, std::vector<double>& val) override;\n};\n\\endcode\n\nIn this case, the lid parameter is the local node index of the nodes of the surface. \n\n\n*/\n\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_cast.dox",
    "content": "/**\r\n\\page plugin_cast Using dynamic_cast in plugins\r\n\r\nCare must be taken when using dynamic_cast inside plugins to cast a FECore base class to a derived class if the base class was allocated in FEBio (and not in the plugin).\r\nThis is the case for instance when creating plot fields for domains, where FEBio only gives you the FEDomain base class. Access to certain types of data is not possible\r\nvia the base class and a cast is necessary to convert to the correct domain class. \r\n\r\nThe problem is that a dynamic_cast does not always work across the dll boundary and usually will result in an incorrect case (usually NULL, but don't rely on that). \r\nBasically a dynamic cast across the dll boundary is undefined. \r\n\r\nInstead of using a dynamic_cast, use other mechanisms of inferring the correct derived type and then use a static_cast. \r\n\r\nUnfortunately, often a dynamic_cast cannot be avoided. For instance, dynamic_casts are used by FEBio to extract specific material point data. If the plugin \r\ndefines a new material point class, accessing this material point data via the ExtractData member may not work. \r\n\r\nThe only alternative in this case is to ensure that dynamic_casts work correctly across the dll boundary. That can only be assured if both FEBio and the plugin\r\nuse the same c-runtime library. Under Windows this can be accomplished if the plugin is built with the same version of Microsoft Visual Studio as FEBio itself and \r\nif the plugin uses the Multi-threaded DLL runtime library (Properties->Configuration Properties->C/C++->Code Generation->Runtime Library). On Linux and Mac this\r\nhas proven to be more challenging, but it is possible.\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_datagenerator.dox",
    "content": "/**\n\\page plugin_datagenerator Data Generator Plugins\n\nSeveral model components can make use of maps to make their parameters depend on the position (e.g. materials, boundary conditions, loads). The maps are defined over different mesh partitions (e.g. nodesets, surfaces, domains) and are used to assign different values to different nodes or elements of the mesh. The map's values can be defined explicitly (e.g. as a tabulated list in the input file), or alternatively can be generated programatically using data generators. \n\nThe advantage of using data generators to generate the map's value is twofold: 1) It can reduce the size of input files considerably since the values are generated programatically, and 2) allows dynamic changes to map as the model evolves in time.\n\nThe following sections describe how to create data generators for specific mesh partitions. \n\n\\section node_gen Node data generators\n\nNode data generators generate data for node sets and thus can be used by model components that are typically defined on node sets, such as prescribed displacements and nodal loads. \n\nNode data generators inherit from the FENodeDataGenerator base class.\n\n\\code\nclass MyDataGenerator : public FENodeDataGenerator\n{\npublic:\n\tMyDataGenerator(FEModel* fem) : FENodeDataGenerator(fem){}\n\n\t// implement this function\n\tFENodeDataMap* Generate();\n};\n\\endcode\n\nThe main function to implement is the \\c Generate member. This member creates and returns the node data map.\n\n\\code\nFENodeDataMap* MyDataGenerator::Generate()\n{\n\tFENodeDataMap* map = new FENodeDataMap(FEDataType::FE_DOUBLE);\n\tFENodeSet* nodeSet = GetNodeSet();\n\tmap->Create(nodeSet);\n\tmap->fillValue(0.0);\n\treturn map;\n}\n\\endcode\n\n\\section surf_gen Surface data generators\n\nSurface data generators generate data for surfaces and can be used by model components that are typically defined on surfaces, such as surface loads. \n\nSurface data generators inherit from the FEFaceDataGenerator base class. \n\n\\section elem_gen Element data generators\n\nElement data generators generate data for domains and can be used by model components that are typically defined on domains, such as materials or body loads. \n\nElement data generators inherit from the FEElemDataGenerator base class. \n\n\\code\nclass MyElemDataGenerator : public FEElemDataGenerator\n{\npublic:\n\tMyElemDataGenerator(FEModel* fem) : FEElemDataGenerator(fem){}\n\n\tFEDomainMap* Generate() override;\n}\n\\endcode\n\nThe \\c Generate class member creates and returns the FEDomainMap. \n\n\\code\nFEDomainMap* MyElemDataGenerator::Generate()\n{\n\tFEElementSet* elset = GetElementSet();\n\tif (elset == nullptr) return nullptr;\n\n\tFEDomainMap* map = new FEDomainMap(FEDataType::FE_DOUBLE, Storage_Fmt::FMT_MATPOINTS);\n\tmap->Create(elset);\n\tmap->fillValue(0.0);\n\treturn map;\t\n}\n\\endcode\n\n*/\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_lc.dox",
    "content": "/**\n\\page plugin_lc Load controller Plugin\n\nIn FEBio, load controllers are used to manipulate the value of parameters in time. An example of a load controller is the load curve, which defines a function of time by interpolating between (time,value) pairs. Several other examples of load controllers are implemented in FEBio that provide different ways to modify parameter values as function of time. Users can create custom load controllers via a load controller plugin. \n\nTo create a load controller plugin, follow the following steps.\n\n1. Derive a new class from FELoadController. \n2. Implement the constructor and override the GetValue(double) function.\n3. Register the plugin class.\n\n\\section sec1 Create load controller class\n\nA load controller class should be derived from the <b>FELoadController</b> base class. \nAs an example, we'll build a load controller that toggles between two values after a particular duration. \n\n\\code\nclass MyLoadController : public FELoadController\n{\npublic:\n\t// constructor\n\tMyLoadController(FEModel* fem);\n\n\t// initialization (optional)\n\tbool Init() override;\n\nprotected:\n\t// required override\n\tdouble GetValue(double currentTime) override;\n\nprivate:\n\tdouble\tm_val0;\n\tdouble\tm_val1;\n\tdouble\tm_duration;\n\n\tDECLARE_FECORE_CLASS();\n}\n\\endcode\n\nIf the load controller defines parameters that should be specified in the input file, the class needs to have the DECLARE_FECORE_CLASS macro. \n\n\\section sec2 Implement class functions\n\nThe contructor passes the FEModel pointer to the base class and initializes the parameters. \n\n\\code\nMyLoadController::MyLoadController(FEModel* fem) : FELoadController(fem)\n{\n\tm_val0 = 0.0;\n\tm_val1 = 1.0;\n\tm_duration = 1.0;\n}\n\\endcode\n\nThe Init function is not required, but can be helpful for initialization and validation of the parameters. Make sure to call the base class as well. \n\n\\code\nbool MyLoadController::Init()\n{\n\t// add initialization here\n\n\t// call base class\n\treturn FELoadController::Init();\n}\n\\endcode\n\nThe GetValue(double) function evaluates the load controller and should return the value of the load controller at the particular time. \n\n\\code\ndouble MyLoadController::GetValue(double currentTime)\n{\n\treturn (currentTime > m_duration ? m_val1 : m_val0);\n}\n\\endcode\n\n\\section sec3 Register the plugin\n\nFirst, define the class parameters that will be exposed to the framework. To allows the class parameters to be recognized, for instance, by the FEBio input file reader. \n\n\\code\nBEGIN_FECORE_CLASS(MyLoadController, FELoadController)\n\tADD_PARAMETER(m_val0, \"val0\");\n\tADD_PARAMETER(m_val1, \"val1\");\n\tADD_PARAMETER(m_duration, \"duration\");\nEND_FECORE_CLASS();\n\\endcode\n\nNext, register the class in the PluginInitialize() function of the plugin.\n\n\\code\nREGISTER_FECORE_CLASS(MyLoadController, \"toggle\");\n\\endcode\n\n\\section sec4 Using the load controller\n\nLoad controllers are defined in the LoadData section of the FEBio input file. The \\c type attribute defines which load controller will be allocated. \n\n\\code\n<LoadData>\n\t<load_controller id=\"1\" type=\"toggle\">\n\t\t<val0>0.0</val0>\n\t\t<val1>1.0</val1>\n\t\t<duration>3.14</duration>\n\t</load_controller>\n</LoadData>\n\\endcode\n\nThe load controller can be assigned to any valid model parameter in the usual way with the \"lc\" attribute. \n\n*/\n\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_log.dox",
    "content": "/**\r\n\\page plugin_log Accessing the FEBio Log file\r\nBy default FEBio writes all output to a log file. More precisely, it uses the Logfile class which writes to both the standard output as well as to a file. FEBio also\r\nprovides special macros that can be used to write to the log file, whithout the need to know the specifics of how the logging mechanism works. \r\n\r\nIn order to access the FEBio log file, you must call the FECoreKernel::SetInstance during the PluginInitialize function. \r\n\r\n\\code\r\nFECORE_EXPORT void PluginInitialize(FECoreKernel& febio)\r\n{\r\n\tFECoreKernel::SetInstance(&febio);\r\n}\r\n\\endcode\r\n\r\nThis ensures that the plugin and FEBio use the same kernel and consequently the same resources (e.g. the logfile). \r\n\r\nWriting to the logfile can be done by including the file <FECore\\log.h> and using the log macros.\r\n\r\n\\code\r\n#include <FECore/log.h>\r\n\\endcode\r\n\r\nIt is important to understand that a log file is associated with each model. \r\n\r\nFor classes derived from FECoreBase, you can use the following macros in any member function. \r\n\r\nThe <c>feLog</c> macro works just like the c printf function and can be used to write formatted text to the screen and the log file. \r\n\r\n\\code\r\nfeLog(\"A negative Jacobian was detected at integration point %d of element %d\", 1, 123);\r\n\\endcode\r\n\r\nFor producing a warning or an error message, you can use the following two macros. The message will be shown in a box with a title. \r\n\r\n\\code\r\nfeLogInfo(\"This will print a message to the screen.\");\r\nfeLogWarning(\"This will print a warning to the screen.\");\r\nfeLogError(\"This will print an error message to the screen.\");\r\n\\endcode\r\n\r\nFor debugging, you can use the feLogDebug function. This will only print to the output when the user runs a model in debug mode (e.g. -g was added as a command line option.)\r\n\r\n\\code\r\nfeLogDebug(\"A debug message is written only when the user runs in debug mode.\")\r\n\\endcode\r\n\r\nFor classes that do not inherit from FECoreBase, or in non-member functions, you must use the extended macros, which take a pointer to the FEModel as its first argument. \r\n\r\n\\code\r\nFEModel* fem;\r\nFELogEx(fem, \"So far, things are looking good.\");\r\nFELogDebugEx(fem, \"Hmm, should this happen?\");\r\nFELogWarningEx(fem, \"Oh oh, that shouldn't happen.\");\r\nFELogErrorEx(fem, \"Exit the building! Now!!\");\r\n\\endcode\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_logdata.dox",
    "content": "/**\n\\page plugin_logdata Log Data Plugin\n\nLog data is written to the FEBio log file, or optionally to a separate text file. Plugins can extend the type of log data that can be exported from FEBio. \n\n\\section nodal_log Nodal Log Data\n\nTo export data for individual nodes, derive a class from FELogNodeData and implement the \\c value virtual member function. \n\n\\code\nclass MyNodeLogData : public FELogNodeData\n{\npublic:\n\tMyNodeLogData(FEModel* fem) : FELogNodeData(fem) {}\n\n\tdouble value(const FENode& node) override\n\t{\n\t\t// calculate something for this node\n\t\tdouble val;\n\t\t\n\t\treturn val;\n\t}\n}\n\\endcode\n\n\\section elem_log Element Log Data\n\nTo export data for individual elements, derive a class from FELogElemData and implement the \\c value virtual member function. \n\n\\code\nclass MyElemLogData : public FELogElemData\n{\npublic:\n\tMyElemLogData(FEModel* fem) : FELogElemData(fem) {}\n\n\tdouble value(FEElement& el) override\n\t{\n\t\t// calculate something for this element\n\t\tdouble val;\n\t\t\n\t\treturn val;\n\t}\n}\n\\endcode\n\nNote that only a single value for each element can be returned. Since element data is often stored at integration points, FEBio usually returns the element average. The following example illustrates how to calculate an element average value. \n\n\\code\ndouble MyElemLogData::value(FEElement& el)\n{\n\tdouble avg = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(i);\n\n\t\t// extract some material point value\n\t\tdouble vi;\n\n\t\t// add it to the sum\n\t\tavg += vi;\n\t}\n\n\t// return average\n\treturn avg / (double) nint;\n}\n\\endcode\n\n\\section face_log Facet Log Data\n\nTo export data for individual surface facets, derive a class from FELogFaceData and implement the \\c value virtual member function. \n\n\\code\nclass MyFaceLogData : public FELogFaceData\n{\npublic:\n\tMyFaceLogData(FEModel* fem) : FELogFaceData(fem) {}\n\n\tdouble value(FESurfaceElement& el) override\n\t{\n\t\t// calculate something for this element\n\t\tdouble val;\n\t\t\n\t\treturn val;\n\t}\n}\n\\endcode\n\n*/\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_nlc.dox",
    "content": "/**\r\n\\page plugin_nlc Nonlinear Constraint Plugins\r\n\r\nNonlinear constraints allow you to impose certain constraints on a model. The nonlinear constraints in FEBio implement the augmented Lagrangian algorithm for enforcing the constraints. This implies that the Lagrange multipliers are not calculated directly, but instead updated in an iterative approach. The process of updating the Lagrange multipliers is referred to as an augmentation in FEBio. During an augmentation each nonlinear constraint class needs to update the approximate Lagrange multipliers and determine whether they have converged. Most of this will need to be done in the Augment method, which is explained below. \r\n\r\n\r\n\\section nlc_base The FENLConstraint class\r\nThe base class of all nonlinear constraints in FEBio is the FENLConstraint class, and thus this class must also be the base class of all nonlinear constraint plugins. \r\n\r\nThe FENLConstraint base class defines the following abstract members. \r\n\r\n\\li <b>Residual</b>: calculate the contribution to the model's residual.\r\n\\li <b>StiffnessMatrix</b>: Calculate the contribution to the model's stiffness matrix.\r\n\\li <b>Augment</b>: Update Lagrange multipliers and determine convergence\r\n\\li <b>BuildMatrixProfile</b>: Must be implemented if the nonlinear constraint generates nonzero entries in the stiffness matrix that would otherwise not exist.\r\n\r\nThese functions are described in more detail below.\r\n\r\n\\section nlc_residual The Residual function\r\nThe Residual function implements the contribution to the model's residual, which in FEBio is defined as the difference between the internal forces (e.g. stresses), and external forces. \r\n\r\nThe function definition has two parameters.\r\n\\li <b>FEGlobalVector</b>: the vector that stores the residual. \r\n\\li <b>FETimeInfo</b>: information of the current time point.\r\n\r\nOnce the contributions to the residual are calculated, they can be assembled in the residual vector via FEGlobalVector::Assemble. \r\n\r\n\\section nlc_stiffness The StiffnessMatrix function\r\nThe StiffnessMatrix function implements the contribution to the model's global stiffness matrix. This function takes two parameters.\r\n\r\n\\li <b>FESolver</b>: The solver that is calling this funcion. This is used mostly for assembling. \r\n\\li <b>FETimeInfo</b>: information of the current time point.\r\n\r\nContributions to the global stiffness matrix are added by calling one of the Assemble members of the FESolver class. \r\n\r\n\\section nlc_augment The Augment function\r\nAs mentioned above, FEBio implements the augmented Lagrangian method for enforcing nonlinear constraints. This means that during the Newton iterations the (approximate) Lagrange multipliers are held constant. After the Newton iteration converges, each nonlinear constraint gets a chance to update the Lagrange multipliers and determine whether they converged. This is done in the Augment function. This function takes two parameters:\r\n\r\n\\li <b>int naug</b>: The augmentation number\r\n\\li <b>FETimeInto& tp</b>: information on the current time point\r\n\r\nThe return value, a boolean, should indicate whether the Lagrange multipliers have converged or not. \r\n\r\n\\section nlc_matrix The BuildMatrixProfile function\r\nSince the global matrix is usually sparse in a finite-element analysis, FEBio only allocates the nonzero entries of the matrix. It does this by inspecting the connectivity of the mesh. However, nonlinear constraints can sometimes couple additional degrees of freedom that are not directly connected in the mesh. For instance, consider a constraint that forces two nodes to remain equidistance (this is implemented in FEBio in the FEDistanceConstraint). If the two nodes do not belong to the same element, the constraint will generate nonzero entries for the stiffness matrix that would otherwise not exist. In this situation, the nonlinear constraint must implement the BuildMatrixProfile method and add the nonzero matrix elements to the profile. \r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_plotdata.dox",
    "content": "/**\r\n\\page plugin_plot Plot Data Plugin\r\n\r\nWith a PlotData plugin a new plot field can be defined for the xplt plot file. This allows users to customize the output generated by FEBio in an easy manner.\r\n\r\nData can be stored in the plot file using several data formats and storage formats. The data format refers to the type of data that is being stored. \r\nThe following values are allowed:\r\n\r\n\\li <b>PLT_FLOAT</b>: single floating point (s.f.p.) number\r\n\\li <b>PLT_VEC3F</b>: three s.f.p. numbers representing a 3D vector\r\n\\li <b>PLT_MAT3FS</b>: six s.f.p. numbers representing a 3D second order symmetric matrix (order is xx, yy, zz, xy, yz, xz).\r\n\\li <b>PLT_MAT3FD</b>: three s.f.p. numbers representing a 3D diagonal matrix (order is xx, yy, zz)\r\n\\li <b>PLT_MAT3F</b>: nine s.f.p. numbers representing a 3D matrix (order is xx, xy, xz, yx, yy, yz, zx, zy, zz)\r\n\r\nThe storage format defines who many data items are stored.\r\n\r\n\\li <b>FMT_NODE</b>: one value for each node\r\n\\li <b>FMT_ITEM</b>: one value for each item (i.e. element or facet)\r\n\\li <b>FMT_MULT</b>: one value for each node of each item.\r\n\\li <b>FMT_REGION</b>: one value for the entire region.\r\n\\li <b>FMT_MATPOINTS</b>: one value per integration point.\r\n\r\nNote that for nodal data the storage format makes no difference and all formats results in a single value per node. \r\n\r\nThe <b>FMT_MATPOINTS</b> storage mode only applies to element data. \r\n\r\nThere are several classes of plot data currently defined: \r\n\r\n\\li Nodal plot data: Store a value for each node of the mesh\r\n\\li Element plot data: store data on elements of domains\r\n\\li Surface facet plot data: Store data on facets of surfaces\r\n\r\n\\section node_data Nodal Plot Data\r\nTo create a nodal plot data field, derive a new class from <b>FEPlotNodeData</b> and implement the \\c Save member function. Note that the constructor of the FEPlotNodeData class\r\nrequires the data format and storage format. For nodal data the storage format is irrelevant since for nodal data one value per node is expected.\r\n\r\n\\code\r\n#include <FECore/FEPlotData.h>\r\n\r\nclass MyNodeData : public FEPlotNodeData\r\n{\r\npublic:\r\n\tMyNodeData(FEModel* fem) : FEPlotNodeData(fem, PLT_FLOAT, FMT_NODE){}\r\n\tbool Save(FEMesh& m, FEDataStream& a);\r\n};\r\n\\endcode\r\n\r\nThe \\c Save function takes the \\c FEMesh as a parameter and FEDataStream reference.\r\n\r\n\\code\r\nbool MyNodeData::Save(FEMesh& m, FEDataStream& a)\r\n{\r\n\tint N = m.Nodes();\r\n\tfor (int i=0; i<N; ++i)\r\n\t{\r\n\t\tdouble f;\r\n\t\t\\\\ calculate something for f\r\n\t\ta << f;\r\n\t}\r\n\treturn true;\r\n}\r\n\\endcode\r\n\r\n\\section elem_data Element Plot Data\r\nTo create a plot field for element data, create a new class from FEPlotDomainData.\r\n\r\n\\code\r\n#include <FECore/FEPlotData.h>\r\n\r\nclass MyElementData : public FEPlotDomainData\r\n{\r\npublic:\r\n\tMyElementData(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM){}\r\n\tbool Save(FEDomain& dom, FEDataStream& a);\r\n};\r\n\\endcode\r\n\r\nThe \\c Save member function takes the FEDomain class and a FEDataStream as parameters. The domain references the current domain for which data is being stored. A cast may be necessary to convert the FEDomain base class to the correct domain class. \r\n\r\n\\code\r\nclass MyElementData::Save(FEDomain& dom, FEDataStream& a)\r\n{\r\n\tint NE = dom.Elements();\r\n\tfor (int i=0; i<NE; ++i)\r\n\t{\r\n\t\tFEElement& el = dom.ElementRef(i);\r\n\t\t\r\n\t\tdouble f;\r\n\t\t\\\\ calculate something for f\r\n\t\ta << f;\r\n\t}\r\n\t\r\n\treturn true;\r\n}\r\n\\endcode \r\n\r\nThe function should return true if it wrote data to the FEDataStream. If not, it should return false. For example, if the requested data cannot be calculated on the domain (e.g. stress on a rigid domain), the function should return false and not write any data to the data stream.  \r\n\r\nThe FEPlotDomainData supports all storage formats as defined above. Make sure to write the correct amount of data and int the correct order to the data stream. \r\n\r\nFEBio mostly uses the FMT_ITEM storage format for element data. In this case, typically the element average quantity is evaluated. \r\n\r\n\\code\r\nclass MyElementData::Save(FEDomain& dom, FEDataStream& a)\r\n{\r\n\tint NE = dom.Elements();\r\n\tfor (int i=0; i<NE; ++i)\r\n\t{\r\n\t\tFEElement& el = dom.ElementRef(i);\r\n\r\n\t\tdouble s = 0.0;\r\n\t\tint ni = el.GaussPoints();\r\n\t\tfor (int j=0; j<ni; ++j)\r\n\t\t{\r\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\r\n\r\n\t\t\t// calculate something for this integration point and add it to s\r\n\t\t\tdouble sj;\r\n\r\n\t\t\ts += sj;\r\n\t\t}\r\n\t\ts /= (double) ni;\r\n\r\n\t\ta << s;\r\n\t}\r\n\t\r\n\treturn true;\r\n}\r\n\\endcode \r\n\r\nTo further facilitate this case, several template functions are defined in FECore\\writeplot.h that perform these operations. The algorithm that extracts the particular data from the material points can be passed as an argument to these functions. \r\n\r\nFor instance, consider the following function.\r\n\r\n\\code\r\ntemplate <class Tin, class Tout> void writeAverageElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<Tin(const FEMaterialPoint&)> fnc)\r\n\\endcode\r\n\r\nFor example, this can be used to write the stress to the plot file follows.\r\n\r\n\\code\r\nbool FEPlotElementStress::Save(FEDomain& dom, FEDataStream& a)\r\n{\r\n\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\r\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData< FEElasticMaterialPoint>();\r\n\t\treturn ep.m_s;\r\n\t\t});\r\n\r\n\treturn true;\r\n}\r\n\\endcode\r\n\r\n\r\n\\section face_data Facet Plot Data\r\nTo create a surface facet plot field, create a new class from FEPlotSurfaceData.\r\n\r\n\\code\r\n#include <FECore/FEPlotData.h>\r\n\r\nclass MySurfaceData : public FEPlotSurfaceData\r\n{\r\npublic:\r\n\tMySurfaceData(FEModel* fem) : FEPlotSurfaceData(fem, PLT_FLOAT, FMT_ITEM){}\r\n\tbool Save(FESurface& s, FEDataStream& a);\r\n};\r\n\\endcode\r\n\r\nThe \\c Save member function takes the FESurface class and a FEDataStream as parameters. The FESurface references the current surface for which data is being stored. \r\n\r\n\\code\r\nclass MySurfaceData::Save(FESurface& s, FEDataStream& a)\r\n{\r\n\tint NE = s.Elements();\r\n\tfor (int i=0; i<NE; ++i)\r\n\t{\r\n\t\tFESurfaceElement& el = s.Element(i);\r\n\t\t\r\n\t\tdouble f;\r\n\t\t// calculate something for f\r\n\t\ta << f;\r\n\t}\r\n\t\r\n\treturn true;\r\n}\r\n\\endcode \r\n\r\nThe function should return true if the data was successfully written to the data stream. It should return false if the data cannot be written to the surface. \r\n\r\nThe FEPlotSurfaceData class supports the FMT_ITEM, FMT_NODE, and FMT_MULT formats. \r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_solver.dox",
    "content": "/**\r\n\r\n\\page plugin_solver Solver Plugins\r\n\r\nThis section describes how to implement a new FE solver for FEBio as a plugin. Developing a new solver requires at a minimum the implementation\r\nof a new solver class, a new material class, and a new domain class. \r\n\r\n\\section solver_sec1 Choosing the base class\r\nThe FECore library offers several base classes that can be used to develop a custom FE solver. The most general one is the FESolver\r\nbase class, but implementing a solver from this base class is quite challenging. For this reason, two additional classes are offered \r\nthat already implement most of the solution logic. The base classes are:\r\n\r\n\\li <c>FELinearSolver</c>: base class for models that result in a linear set of equations\r\n\\li <c>FENewtonSolver</c>: base class for models that result in a nonlinear set of equations\r\n\r\n\\section solver_sec2 The FELinearSolver base class\r\n\r\nThis base class can be used to develop solvers that result in a linear system of equations. \r\n\r\n\\code\r\nclass MyLinearSolver : public FELinearSolver\r\n{\r\npublic:\r\n\tMyLinearSolver(FEModel* fem);\r\n\t\r\n\tbool InitEquations();\r\n\t\r\n\tvoid ForceVector(FEGlobalVector& R);\r\n\t\r\n\tbool StiffnessMatrix(FELinearSystem& K);\r\n};\r\n\\endcode\r\n\r\nFour functions must be implemented. \r\n\r\n\\subsection The Constructor\r\nThe construct of the solver class needs to define the solution variable and degrees of freedom that this solver uses. \r\n\r\n\\code\r\nMyLinearSolver::MyLinearSolver(FEModel* fem) : FELinearSolver(fem)\r\n{\r\n\t// define the solution variable that this solver will use\r\n\tDOFS& dofs = fem->GetDOFS();\r\n\tint varT = dofs.AddVariable(\"temperature\");\r\n\tdofs.SetDOFName(varT, 0, \"T\");\r\n}\r\n\\endcode\r\n\r\nThe DOFS structure manages all the variables and degrees of freedom that are defined for the FEModel. A variable can manage\r\nmultiple degrees of freedom (e.g. a displacement variable can have an x, y, and z degree of freedom). Each degree of freedom can be\r\ngiven a symbol using the DOFS::SetDOFName method. This symbol will be used in the FEBio input file to reference that degree of freedom \r\n(e.g. in a fixed or prescribed bc).\r\n\r\n\\subsection InitEquations\r\nThe InitEquations function is called to assign equation numbers to the degrees of freedom. \r\n\r\n\\code\r\nbool MyLinearSolver::InitEquations()\r\n{\r\n\tint dof_T = GetFEModel().GetDOFIndex(\"T\");\r\n\tvector<int> dof;\r\n\tdof.push_back(dof_T);\r\n\tSetDOF(dof_T);\r\n\t\r\n\treturn FELinearSolver::InitEquations();\r\n}\r\n\\endcode\r\n\r\nThe base class <c>FELinearSolver</c> does most of the work, but the solver needs to set which degrees of freedom will be solved for with\r\nthe FELinearSolver::SetDOF function. \r\n\r\n\\subsection ForceVector\r\n\r\nThis function is called by the base class whenever the right-hand side must be evaluated. The solver must implement all the terms that \r\ncontribute to the right-hand side vector of the model. \r\n\r\n\\code\r\nvoid FEHeatSolver::ForceVector(FEGlobalVector& R)\r\n{\r\n\t// implement terms that contribute to right-hand side vector\r\n}\r\n\\endcode\r\n\r\nThe parameter that is passed by this function is an object of type FEGlobalVector that represents the right-hand side vector. It also offers\r\nmethods for assembling element force vectors into the global vector. \r\n\r\n\\subsection StiffnessMatrix\r\n\r\nThis function is called by the base class whenever the stiffness matrix must be evaluated. The solver must implement all the terms that \r\ncontribute to the stiffness matrix of the model. \r\n\r\n\\code\r\nbool FEHeatSolver::StiffnessMatrix(FELinearSystem& K)\r\n{\r\n\t// implement terms that contribute to the stiffness matrix\r\n}\r\n\\endcode\r\n\r\nThe parameter that is passed by this function is an object of type FELinearSystem that represents the linear system of equations. It also offers\r\nmethods for assembling element quantities.\r\n\r\n\\section solver_sec3 Custom Material class\r\n\r\nThe material parameters that the solver class uses will be implemented in a new material class. \r\n\r\n\\code\r\nclass MyMaterial : public FEMaterial\r\n{\r\npublic:\r\n\tMyMaterial(FEModel* fem);\r\n\t\r\n\tDECLARE_PARAMETER_LIST();\r\n};\r\n\\endcode\r\n\r\nSee the section on Material plugins for more information on defining custom materials. \r\n\r\n\\section solver_sec4 Custom Domain class\r\n\r\nA domain is a partiation of the mesh that will be used by the solver to solve the model on. Usually, the domain class is responsible for implementing \r\nthe terms that contribute to the stiffness and force vector. It should also store a pointer to the material that it uses.\r\n\r\n\\code\r\nclass MyDomain : public FESolidDomain\r\n{\r\npublic:\r\n\tMyDomain(FEModel* fem);\r\n\t\r\n\tvoid SetMaterial(FEMaterial* mat);\r\n\t\r\n\tbool Initialize();\r\n\t\r\n\tvoid Update(const FETimeInfo& tp);\r\n\t\r\nprivate:\r\n\tMyMaterial*\tm_mat;\r\n};\r\n\\endcode\r\n\r\n\r\n\\subsection Domain Initialization\r\n\r\nAlthough the solver sets the solution variable and degrees of freedom, the domain decides which degrees of freedom are active within that domain. \r\n\r\n\\code\r\nbool MyDomain::Initialize()\r\n{\r\n\tif (FESolidDomain::Initialize() == false) return false;\r\n\t\r\n\tvector<int> dof;\r\n\tdof.push_back(GetFEModel().GetDOFIndex(\"T\"));\r\n\tSetDOF(dof);\r\n\r\n\treturn true;\r\n}\r\n\\endcode\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_upgrade3.dox",
    "content": "/**\n\\page plugin_upgrade3 Upgrading plugins to FEBio 3.0\n\nThis document describes important details for upgrading a plugin that was designed for FEBio2 to FEBio3. \n\nParameter declaration\n---------------------\n\\li DECLARE_PARAMETER_LIST must be replaced with DECLARE_FECORE_CLASS\n\\li BEGIN_PARAMETER_LIST must be replaced with BEGIN_FECORE_CLASS\n\\li END_PARAMETER_LIST must be replaced with END_FECORE_CLASS\n\\li Type specification in ADD_PARAMETER is no longer necessary\n\\li ADD_PARAMETER2 no longer exsists. ADD_PARAMETER must be used for all parameter declarations. \n\nProperty declaration\n--------------------\n\\li Properties are now defined in the BEGIN_FECORE_CLASS/END_FECORE_CLASS using the ADD_PROPERTY macro. \n\\li Properties can be declared using pointer syntax and no longer require the FEPropertyT template declaration.\n\nClass registration\n------------------\n\\li Factory approach for registering plugin classes is deprecated. Use REGISTER_FECORE_CLASS instead in the PluginInitialize function. \n\nPlot classes\n------------\n\\li Base class for plot variables are now FEPlotDomainData (instead of FEDomainData)\n\\li Base class requires passing FEModel pointer\n\nMiscellaneous\n-------------\n\\li SetLocalCoordinateSystem is no longer available since material axes are defined via the new framework for specifying heterogeneous properties.\n\\li FECoreBase::GetFEModel() returns FEModel*. \n\\li Stiffness matrix assembly should be done via FELinearSystem class. \n\\li Use FEElementMatrix for creating element stiffness matrix. \n\\li FEModel needs to be acquired with GetFEModel() (base class variable m_fem is private)\n\\li log mechanism was changed. \n*/\n"
  },
  {
    "path": "Documentation/Doxygen/plugin_upgrade4.dox",
    "content": "/**\n\\page plugin_upgrade4 Upgrading plugins to FEBio 4.0\n\nThis document describes important details for upgrading a plugin that was designed for FEBio3 to FEBio4.\n\nMaterial Point Data\n---------------------\nIn FEBio3, custom material point classes were derived from FEMaterialPoint. However, this resulted in every derived class inheriting the FEMaterialPoint member variables (e.g. m_r0), which is inefficient and can lead to confusion. For this reason, the material point is separated from the material point data needed by material classes. Now, the FEMaterialPoint class stores a list to classes derived from FEMaterialPointData. Custom material point data classes should be derived from \\c FEMaterialPointData instead of \\c FEMaterialPoint. \n\nIf your material plugin defined a custom material point, you need to change the base class from <b>FEMaterialPoint to FEMaterialPointData</b>.\n*/\n"
  },
  {
    "path": "Documentation/Doxygen/plugins.dox",
    "content": "/**\r\n\\page plugins FEBio Plugins\r\n\r\nIntroduction\r\n------------\r\nFEBio has a plugin mechanism that allows users to add new capabilities to FEBio without changing or recompiling the source code. This makes it easy to implement new features, such as constitutive models, boundary conditions, loads, plot variables, and even couple FEBio to other codes. \r\n\r\nPlugin documentation\r\n--------------------\r\nIf you are new to FEBio plugins, it is recommended to checkout the following link first, which introduces the basics of developing and using plugins in FEBio.\r\n\r\n\\li \\subpage plugin_basics\r\n\r\nIf you are looking for information about a particular type of plugin, choose one of the following links.\r\n\\li \\subpage material\r\n\\li \\subpage plugin_bc\r\n\\li \\subpage plugin_plot\r\n\\li \\subpage plugin_logdata\r\n\\li \\subpage cb_plugin\r\n\\li \\subpage task\r\n\\li \\subpage plugin_solver\r\n\\li \\subpage plugin_nlc\r\n\\li \\subpage plugin_lc\r\n\\li \\subpage plugin_datagenerator\r\n\r\nAdvanced topics\r\n---------------\r\nThe following articles discuss some advanced topics related to the FEBio plugin framework. Please select one of the following links to learn more about a particular topic.\r\n\r\n\\li \\subpage plugin_log\r\n\\li \\subpage plugin_cast\r\n\\li \\subpage plugin_upgrade3\r\n\\li \\subpage plugin_upgrade4\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/register.dox",
    "content": "/**\r\n\\page register Registering Plugin Classes\r\n\r\nBefore a plugin class can be recognized by FEBio it must be registered with the framework. Registration is done in the <b>PluginInitialize</b> function, which is one of the required functions. \r\n\r\nFirst, make sure to include any header files for the plugin classes at the top of the file. \r\n\r\n\\section init_plugin Using PluginInitialize\r\n\r\nThe PluginInitialize is one of the required functions and will be called by FEBio after it loads the plugin file. In this function a plugin can allocate and initialize any resources it may need. In this function the plugin classes can be registered using the REGISTER_FECORE_CLASS macro. However, before this macro is used it is important to:\r\n\r\n1. call FECoreKernel::SetInstance with the parameter that is passed to PluginInitialize. \r\n2. set the active physics module. \r\n\r\nThe following code excerpt presents an example of PluginInitialize. \r\n\r\n\\code\r\nFECORE_EXPORT void PluginInitialize(FECoreKernel& fecore)\r\n{\r\n\t// Set the kernel to match FEBio's kernel\r\n\tFECoreKernel::SetInstance(&fecore);\r\n\r\n\t// set the active module to which to add this new feature\r\n\tfecore.SetActiveModule(\"solid\");\r\n\t\r\n\t// Register the classes in this plugin\r\n\tREGISTER_FECORE_CLASS(MyMaterial, \"My material\");\r\n}\r\n\\endcode\r\n\r\nThe first call the FECoreKernel::SetInstance is important to make sure that the plugin and FEBio are using the same kernel. \r\n\r\nThen, the active module is set by calling \\c fecore.SetActiveModule. This function takes the name of the module as an argument. Please see \\ref modules for more information on modules in FEBio. \r\n\r\nThe macro <b>REGISTER_FECORE_CLASS</b> takes two parameters. The first parameter is the name of the c++ plugin class (here, MyMaterial). The second parameter is a name that will be used to represent this class in the FEBio input file (i.e. the value for the <i>type</i> attribute). \r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/required_functions.dox",
    "content": "/**\r\n\r\n\\page required_functions Writing a plugin\r\n\r\nAfter you created a new plugin project, it is time to start writing the plugin code. There are two parts to this: writing the required functions that are common to all plugins, and write the code that is specific to type of plugin you wish to create. This page details the required functions that FEBio needs to interact with your plugin. \r\n\r\nAdd a new cpp file to your project. (A good name for this file would be the project's name.) Then, add the following include file to the top of your source file. (If you are using precompiled header files, make sure the precompiled header file (e.g. pch.h) is the first included file.)\r\n\r\n\\code\r\n#include <FECore\\FECoreKernel.h>\r\n\\endcode\r\n\r\nThis will include all the features of the FEBio kernel that you need to register your plugin with the FEBio framework. \r\n\r\nIn addition, you will add any include files for plugin classes that are defined in the plugin. \r\n\r\nThe following functions need to be defined in the plugin and are needed for plugin registration, initialization, and cleanup. \r\n\r\n1. <b>GetSDKVersion</b>: Returns the version of the SDK library that is used by this plugin.\r\n2. <b>GetPluginVersion</b>: Return the version number of plugin. \r\n3. <b>PluginInitialize</b>: Place for initializing plugin data\r\n4. <b>PluginCleanup</b>: Place for releasing any resources allocated by the plugin.\r\n\r\nAlthough these functions are referred to as the required functions, only the GetSDKVersion and PluginInitialize are really mandatory. The other ones only need to be defined if they are actually used. \r\n\r\n\\section GetSDKVersion\r\nThis function returns the version number of the SDK that was used to build the plugin. When loading the plugin, FEBio will check this number to see if the plugin is compatible with the FEBio version that is trying to load the plugin. This function needs to return the FE_SDK_VERSION macro which is defined in the file FECore\\version.h\r\n\r\n\\code\r\nFECORE_EXPORT unsigned int GetSDKVersion()\r\n{\r\n\treturn FE_SDK_VERSION;\r\n}\r\n\\endcode\r\n\r\nFEBio will not load the plugin if this function is omitted or if the returned version number is incompatible with the version of FEBio that is trying to load the plugin.\r\n\r\n\\section GetPluginVersion\r\nYou can define a verion number for your plugin by implementing the optional function GetPluginVersion. \r\n\r\n\\code\r\nFECORE_EXPORT void GetPluginVersion(int& major, int& minor, int& patch)\r\n{\r\n\tmajor = 1;\r\n\tminor = 0;\r\n\tpatch = 3;\r\n}\r\n\\endcode\r\n\r\nFEBio prints these numbers to the screen when the plugin is loaded. \r\n\r\n\\section PluginInitialize\r\nThis function will be the first function called by FEBio and can be used to allocate and initialize plugin data. It is also recommended to call the SetInstance function as in the example below.\r\n\r\n\\code\r\nFECORE_EXPORT void PluginInitialize(FECoreKernel& febio)\r\n{\r\n\tFECoreKernel::SetInstance(&febio);\r\n\r\n\t// TODO: register your plugin classes here\r\n}\r\n\\endcode\r\n\r\nThis function takes one parameter, a reference to the kernel object. This object is used for accessing the resources managed by this class. \r\nThis is also a good place to allocate any resources that the plugin may need. The call to SetInstance ensures\r\nthat the plugin uses the same kernel object as the FEBio executable. \r\n\r\nThe rest of this function typically registers the plugin classes with the framework (see \\ref register). \r\n\r\n\\section PluginCleanup\r\nThis function will be called when FEBio terminates and can be used to deallocate resources.\r\n\r\n\\code\r\nFECORE_EXPORT void PluginCleanup()\r\n{\r\n\t// Clean up plugin resources \r\n}\r\n\\endcode\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/restart.dox",
    "content": "/**\r\n\\page restart Restart Capabilities\r\n\r\nFEBio has several mechanism for restarting a problem. The \"running restart\" capability allows the model to recover from an otherwise fatal error during runtime and restart\r\nfrom the last converged state. The \"cold restart\" allows the user to restart an analysis using a restart file. Again, the analysis will restart from the last converged state (for which a dump file was created). \r\nThe \"reset\" feature allows a user to reset all data and restart the analysis from scratch. The following sections describe these features in more detail.\r\n\r\n\\section restart_sec1 Running Restart\r\n\r\nThe running restart feature allows the analysis to recover from a potentially fatal error. In FEBio, the most common error that triggers a running restart is \r\nan inverted element. Since in this case the solution has become non-physical, FEBio cannot continue and attempts to do a running restart. The analysis will\r\nrestart from the last converged time step and the time step size will be reduced in the hope that the analysis can proceed. \r\n\r\nThe way this works in FEBio is as follows. At the beginning of a time step, the current state of the model is backed up using FEModel::PushState. This places \r\nthe current state on a stack. This function in turn calls FEModel::ShallowCopy which will call the ShallowCopy function of all the model components.\r\n\r\nAlthough most of this is taken care of by the framework, in some cases a new class should implement the ShallowCopy member function. This is the case if the class\r\nstores data that describes the state of the model, and that cannot be calculated by re-evaluating the model state at the previous time step. History variables\r\nfor material classes is such as example. The Lagrange multipliers for non-linear constraints and contact data is another example.\r\n\r\n\\section restart_sec2 Cold Restart\r\n\r\nThe cold restart feature allows an analysis to restart from a restart file. The file can simply define the state of the model at the last converged timestep\r\nor modify certain analysis parameters prior to restarting from the last converged time step. This feature is useful for restarting a model after a program or\r\nsystem crash.\r\n\r\nIn FEBio this feature is implemented using the Serialize() member function. Data serialization is explained in more depth in another \\ref serialize \"technical page\".\r\n\r\n\\section restart_sec3 Model Reset\r\n\r\nThe model reset allows the user to reset the model to the initial configuration prior to running the model. \r\n\r\nThis feature is used in FEBio by the optimization module, which resets the model at the beginning of each optimization iteration. \r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/serialize.dox",
    "content": "/**\r\n\\page serialize Data Serialization\r\n\r\nData serialization refers to the process of reading and writing class data to or from an external file. This feature is used for the restart capabilities, that\r\nallows an analysis to restart from a previous state.\r\n\r\nMost aspects of serialization is taken care of by the framework, however in some cases a class needs to implement additional support by implementing the \\c Serialize member. \r\nThis function takes a single parameter, namely the dump stream. \r\n\r\n\\code\r\nvoid MyClass::Serialize(DumpStream& ar)\r\n{\r\n\tif (ar.IsSaving())\r\n\t{\r\n\t\t// write data using the << operator\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// read data using the >> operator\r\n\t}\r\n}\r\n\\endcode\r\n\r\nData is written to the dump stream using the << operators (just like when using std::cout). Data is read from the dump file using the >> operators (just like using std::cin). Note that it is important that data is read in the same order as it was written. \r\n\r\nAlternatively, you can use the ampersand operator (&), which will write on saving and read on loading. \r\n\r\nFor instance, consider a class, MyClass, that defines two parameters, m_a and m_b. You can serialize using either the explicit read and write operators. \r\n\r\n\\code\r\nvoid MyClass::Serialize(DumpStream& ar)\r\n{\r\n\tif (ar.IsSaving())\r\n\t{\r\n\t\tar << m_a << m_b;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tar >> m_a >> m_b;\r\n\t}\r\n}\r\n\\endcode\r\n\r\nAlternatively, the following code is equivalent. \r\n\r\n\\code\r\nvoid MyClass::Serialize(DumpStream& ar)\r\n{\r\n\tar & m_a & m_b;\r\n}\r\n\\endcode\r\n\r\n\\section ser_sec1 Shallow vs Deep Serialization\r\n\r\nThe serialization is used during running restarts and cold restarts. A running restart happens when a time step fails and the model's state needs to be restored to the last converged time step so that a new time step size can be tried. A cold restart happens when a model is restarted from a dump file (or restart input file). \r\n\r\nDuring a running restart, not all of the model data needs to be serialized. (For instance, initial nodal coordinates, constant material parameter, and other values that do not change during the solution process.) In this case, only a \"shallow\" copy of the model needs to be stored to the dumpstream. This is in contrast to the cold restart, when a \"deep\" copy needs to be read, since all model parameters need to be restored from the dumpstream. \r\n\r\nYou can check whether a shallow or deep copy needs to be serialized using the \\c DumpStream::IsShallow function. For instance, the following code illustrates how to serialize data for cold restarts. \r\n\r\n\\code\r\nvoid MyClass::Serialize(DumpStream& ar)\r\n{\r\n\tif (ar.IsShallow() == false)\r\n\t{\r\n\t\tar & m_a & m_b;\r\n\t}\r\n}\r\n\\endcode\r\n\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/task.dox",
    "content": "/**\r\n\\page task Task Plugins\r\n\r\nIn this section we will look at the details of creating a task plugin. A task executes the main loop in FEBio. For example, the default task simply calls the FEModel::Solve to solve the forward model. Another example is the optimization solver, which is implemented as a task and solves the FEModel repeatedly as part of a parameter optimization problem. \r\n\r\nCustom task plugins give the user the very powerful capability to control exactly what FEBio does. Task plugins are often useful for coupling FEBio to other codes. Again, the optimization task is a great example since it couples FEBio to an optimization library (e.g. levmar). \r\n\r\nIn order to create a new task plugin, you must implement the following.\r\n\r\n1. Create a new task class, derived from the FECoreTask base class.\r\n2. Implement the virtual Init(const char*) member function do perform any initialization.\r\n3. Implement the virtual Run() member function.\r\n4. Register the task with the framework. \r\n\r\nIn the next section, we will discuss these steps in more detail.\r\n\r\n\\section task_sec1 Creating a Task Plugin\r\n\r\n\\subsection task_sec11 Create the Task Class\r\n\r\nThe first step is to create a new task class, derived from the FECoreTask base class, defined in FECore\\FECoreTask.h. This class must implement a constructor and\r\nthe Run member function.\r\n\r\n\\code\r\nclass MyTask : public FECoreTask\r\n{\r\npublic:\r\n\tMyTask(FEModel* pfem);\r\n\t\r\n\tbool Init(const char* szfile) override;\r\n\t\r\n\tbool Run() override;\r\n}\r\n\\endcode\r\n\r\nThe constructor MyTask takes a pointer to the current active FEModel.\r\n\r\nThe \\c Init member function performs any initialization that the task may require. This function should at least call FEModel::Init to initialize the FEModel data.\r\n\r\nThe \\c Run member function will then be called by FEBio and hands over control to the task.\r\n\r\n\\subsection task_sec12 Implement the Init function\r\n\r\nThis function is called by FEBio after the FEBio input file was read (if one was provided on the command line). In this function, the task can initialize any data it may need, as well as make any changes to the FEModel. It receives one input argument, namely a string. The string can define, for instance, the name of a control file or provide additional control parameters. \r\n\r\nThis function should at least call the FEModel::Init function to initialize the model data. If any changes to the FEModel are made, this function should be called after these changes. Once FEModel::Init is called, a task should not add to or remove any model components to the FEModel.\r\n\r\n\\code\r\nbool MyTask::Init(const char* szfile)\r\n{\r\n\t// get the FE model\r\n\tFEModel* fem = GetFEModel();\r\n\r\n\t// make any changes necessary to the model\r\n\r\n\t// then, initialize the model\r\n\treturn fem->Init();\r\n}\r\n\\endcode\r\n\r\n\\subsection task_sec13 Implement the Run function\r\nAfter FEBio is done initializing the model data, it will call the selected task's Run function and passes the main execution loop to the task.\r\n\r\nInside the Run() function, the task can execute the code that implements the task. After completing, the Run() function must return a boolean indicating whether the task was executed\r\nsuccessfully (true) or not (false). \r\n\r\n\\subsection task_sec14 Registering the task\r\n\r\nAs with any plugins, the task class must be registered with the framework. This is done in the PluginInitialize() function, which is one of the required functions that each plugin needs to define. In this function, the REGISTER_FECORE_CLASS macro can be used to register the task. \r\n\r\n\\code\r\nREGISTER_FECORE_CLASS(MyTask, \"mytask\");\r\n\\endcode\r\n\r\n\\section task_sec2 Running a Task plugin\r\nIn order to use a task plugin you must first register the plugin with the framework in the usual manner (i.e. implementing the required plugin functions and \r\nadding the plugin filename to the FEBio configuration file). Then, you must specify the name of the task on the command line when running FEBio.\r\n\r\n\\code{.unparsed}\r\n>febio -i file.feb -task=\"mytask\" data.txt\r\n\\endcode\r\n\r\nNotice that the model input file is still required and defined with the -i command. FEBio will read the input file before the \\c Init member function is called.\r\nThe -task command line option specifies which task FEBio needs to execute. The task's name is specified after the equal sign (=). The task command can be followed by an optional string. This string will be passed to the task plugin as a parameter to the Init() member function.\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/technical.dox",
    "content": "/**\r\n\\page technical Technical Pages\r\n\r\nThis is a collection of articles describing some of the technical aspects of FEBio. Understanding some of this material may be useful for developing with FEBio.\r\n\r\n\\li \\subpage pipeline\r\n\\li \\subpage restart\r\n\\li \\subpage serialize\r\n\\li \\subpage debug_tools\r\n\\li \\subpage callback\r\n\\li \\subpage modules\r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/Doxygen/using_plugins.dox",
    "content": "/**\r\n\r\n\\page using_plugins Using a plugin in FEBio\r\n\r\nIn order to use a plugin FEBio needs to be informed on where the plugin lives on the file system. This is accomplished by adding the path to the plugin file to the FEBio configuration file. This file, usually called <i>febio.xml</i>, is usually located in the same folder as the FEBio executable. For each plugin, add the following line to this configuration file.\r\n\r\n\\code\r\n<import>c:\\path\\to\\plugin\\plugin.dll</import>\r\n\\endcode\r\n\r\nThat is, enclose the full path and filename of the plugin between <c>import</c> tags. \r\n\r\nNote that you can add as many import tags as needed. There is no limit to the number of plugins that FEBio can load. \r\n\r\nIf you wish to load many plugins, and these plugins are all located in the same folder structure, it might help to define the parent folder with a <c>set</c> tag. Then, plugins paths can be defined referencing this path. \r\n\r\n\\code\r\n<set name=\"PluginFolder\">c:\\path\\to\\plugin</set>\r\n<import>$(PluginFolder)\\plugin.dll</import>\r\n\\endcode\r\n\r\nWhen FEBio starts, it will read the configuration file and try to load all plugins listed therein. A message will be printed on the screen at the start of an FEBio analysis indicating if FEBio was successful in loading the plugin.\r\n\r\nAlternatively, you can add the path to a plugin to the FEBio command line with the <c>-import</c> command line option. \r\n\r\n\\code\r\n>febio4 -i test.feb -import \"C:\\path\\to\\plugin\\plugin.dll\"\r\n\\endcode\r\n\r\nMake sure there is a space between <c>-import</c> and the plugin's path. Currently, you can only load one plugin via the command line option. If you need to load multiple plugins, you need to specify them in the configuration file. \r\n\r\n*/\r\n"
  },
  {
    "path": "Documentation/FEBio3.bib",
    "content": "%% This BibTeX bibliography file was created using BibDesk.\n%% http://bibdesk.sourceforge.net/\n\n%% Created for Gerard Ateshian at 2025-12-17 07:51:29 -0500 \n\n\n%% Saved with string encoding Unicode (UTF-8) \n\n\n@comment{jabref-meta: databaseType:bibtex;}\n\n\n\n@article{Deshpande01,\n\tauthor = {Deshpande, VS and Fleck, NA},\n\tdate-added = {2025-12-17 07:51:22 -0500},\n\tdate-modified = {2025-12-17 07:51:28 -0500},\n\tjournal = {Acta materialia},\n\tnumber = {10},\n\tpages = {1859--1866},\n\tpublisher = {Elsevier},\n\ttitle = {Multi-axial yield behaviour of polymer foams},\n\tvolume = {49},\n\tyear = {2001}}\n\n@article{Drucker52,\n\tauthor = {Drucker, Daniel Charles and Prager, William},\n\tdate-added = {2025-12-16 14:07:53 -0500},\n\tdate-modified = {2025-12-16 14:08:09 -0500},\n\tjournal = {Quarterly of applied mathematics},\n\tnumber = {2},\n\tpages = {157--165},\n\ttitle = {Soil mechanics and plastic analysis or limit design},\n\tvolume = {10},\n\tyear = {1952}}\n\n@article{Xiao02,\n\tauthor = {Xiao, H and Chen, LS},\n\tdate-added = {2025-06-19 13:52:52 -0400},\n\tdate-modified = {2025-06-19 13:53:00 -0400},\n\tjournal = {Acta Mechanica},\n\tnumber = {1},\n\tpages = {51--60},\n\tpublisher = {Springer},\n\ttitle = {Hencky's elasticity model and linear stress-strain relations in isotropic finite hyperelasticity},\n\tvolume = {157},\n\tyear = {2002}}\n\n@article{Coleman61,\n\tauthor = {Coleman, Bernard D and Noll, Walter},\n\tdate-added = {2024-12-12 16:34:44 -0500},\n\tdate-modified = {2024-12-12 16:35:00 -0500},\n\tjournal = {Reviews of modern physics},\n\tnumber = {2},\n\tpages = {239},\n\tpublisher = {APS},\n\ttitle = {Foundations of linear viscoelasticity},\n\tvolume = {33},\n\tyear = {1961}}\n\n@book{Bland16,\n\tauthor = {Bland, David Russell},\n\tdate-added = {2024-12-12 16:33:12 -0500},\n\tdate-modified = {2024-12-12 16:33:20 -0500},\n\tpublisher = {Courier Dover Publications},\n\ttitle = {The theory of linear viscoelasticity},\n\tyear = {2016}}\n\n@article{Brinkman49,\n\tauthor = {Brinkman, Hendrik C},\n\tdate-added = {2024-12-12 16:28:33 -0500},\n\tdate-modified = {2024-12-12 16:28:42 -0500},\n\tjournal = {Flow, Turbulence and Combustion},\n\tnumber = {1},\n\tpages = {27--34},\n\tpublisher = {Springer},\n\ttitle = {A calculation of the viscous force exerted by a flowing fluid on a dense swarm of particles},\n\tvolume = {1},\n\tyear = {1949}}\n\n@article{Bowen69,\n\tauthor = {Bowen, Ray M},\n\tdate-added = {2024-12-12 16:24:02 -0500},\n\tdate-modified = {2024-12-12 16:24:50 -0500},\n\tjournal = {Archive for Rational Mechanics and Analysis},\n\tpages = {97--127},\n\tpublisher = {Springer-Verlag},\n\ttitle = {The thermochemistry of a reacting mixture of elastic materials with diffusion},\n\tvolume = {34},\n\tyear = {1969}}\n\n@article{Mullender94,\n\tabstract = {Although the capacity of bone to adapt to functional mechanical requirements has been known for more than a century, it is still unclear how the bone adaptation processes are regulated. We hypothesize that osteocytes are sensitive to mechanical loading and control the regulation of bone mass in their environment. Recently, simulation models of such a process were developed, using the finite element method. It was discovered that these models produce discontinuous structures, not unlike trabecular bone. However, it was also found that severe discontinuities violate the continuum assumption underlying the finite element method and that the solutions were element mesh dependent. We have developed a simulation model (which is physiologically and mechanically more consistent) which maintains the self-organizational characteristics but does not produce these discontinuities. This was accomplished by separating the sensor density and range of action from the mesh. The results clearly show that predicted trabecular morphology, i.e. sizes and branching of struts, depend on the actual relationship between local load, sensor density and range of influence. We believe that the model is suitable to study the relationship between trabecular morphology and load and can also explain adaptation of morphology, in the sense of 'Wolff's law'.},\n\tauthor = {Mullender, M G and Huiskes, R and Weinans, H},\n\tdate-added = {2024-08-29 05:59:22 -0400},\n\tdate-modified = {2024-08-29 05:59:22 -0400},\n\tdoi = {10.1016/0021-9290(94)90049-3},\n\tjournal = {J Biomech},\n\tjournal-full = {Journal of biomechanics},\n\tmesh = {Adaptation, Physiological; Algorithms; Bone Remodeling; Bone and Bones; Cell Communication; Cell Count; Computer Simulation; Homeostasis; Humans; Models, Biological; Osteocytes; Stress, Mechanical},\n\tmonth = {Nov},\n\tnumber = {11},\n\tpages = {1389-94},\n\tpmid = {7798290},\n\tpst = {ppublish},\n\ttitle = {A physiological approach to the simulation of bone remodeling as a self-organizational control process},\n\tvolume = {27},\n\tyear = {1994},\n\tbdsk-url-1 = {https://doi.org/10.1016/0021-9290(94)90049-3}}\n\n@article{Ateshian23b,\n\tabstract = {This study examines the theoretical foundations for the damage mechanics of biological tissues in relation to viscoelasticity. Its primary goal is to provide a mechanistic understanding of well-known experimental observations in biomechanics, which show that the ultimate tensile strength of viscoelastic biological tissues typically increases with increasing strain rate. The basic premise of this framework is that tissue damage occurs when strong bonds, such as covalent bonds in the solid matrix of a biological tissue, break in response to loading. This type of failure is described as elastic damage, under the idealizing assumption that strong bonds behave elastically. Viscoelasticity arises from three types of dissipative mechanisms: (1) Friction between molecules of the same species, which is represented by the tissue viscosity. (2) Friction between fluid and solid constituents of a porous medium, which is represented by the tissue hydraulic permeability. (3) Dissipative reactions arising from weak bonds breaking in response to loading, and reforming in a stress-free state, such as hydrogen bonds and other weak electrostatic bonds. When a viscoelastic tissue is subjected to loading, some of that load may be temporarily supported by those frictional and weak bond forces, reducing the amount of load supported by elastic strong bonds and thus, the extent of elastic damage sustained by those bonds. This protective effect depends on the characteristic time response of viscoelastic mechanisms in relation to the loading history. This study formalizes these concepts by presenting general equations that can model the damage mechanics of viscoelastic tissues.},\n\tauthor = {Ateshian, Gerard A and Kroupa, Kimberly R and Petersen, Courtney A and Zimmerman, Brandon K and Maas, Steve A and Weiss, Jeffrey A},\n\tdate-added = {2024-05-30 11:26:36 -0400},\n\tdate-modified = {2024-05-30 11:27:14 -0400},\n\tdoi = {10.1115/1.4056063},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Viscosity; Elasticity; Tensile Strength; Biomechanical Phenomena; Porosity; Models, Biological; Stress, Mechanical},\n\tmonth = {Apr},\n\tnumber = {4},\n\tpmc = {PMC9791672},\n\tpmid = {36301266},\n\tpst = {ppublish},\n\ttitle = {Damage Mechanics of Biological Tissues in Relation to Viscoelasticity},\n\tvolume = {145},\n\tyear = {2023},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4056063}}\n\n@article{Quemada78,\n\tauthor = {Quemada, DJRA},\n\tdate-added = {2024-05-14 07:51:25 -0400},\n\tdate-modified = {2024-05-14 07:51:42 -0400},\n\tjournal = {Rheologica Acta},\n\tnumber = {6},\n\tpages = {632--642},\n\tpublisher = {Springer},\n\ttitle = {Rheology of concentrated disperse systems II. A model for non-newtonian shear viscosity in steady flows},\n\tvolume = {17},\n\tyear = {1978}}\n\n@article{KAMENSKY2018522,\n\tabstract = {This work formulates frictionless contact between solid bodies in terms of a repulsive potential energy term and illustrates how numerical integration of the resulting forces is computationally similar to the ``pinball algorithm'' proposed and studied by Belytschko and collaborators in the 1990s. We thereby arrive at a numerical approach that has both the theoretical advantages of a potential-based formulation and the algorithmic simplicity, computational efficiency, and geometrical versatility of pinball contact. The singular nature of the contact potential requires a specialized nonlinear solver and an adaptive time stepping scheme to ensure reliable convergence of implicit dynamic calculations. We illustrate the effectiveness of this numerical method by simulating several benchmark problems and the structural mechanics of the right atrioventricular (tricuspid) heart valve. Atrioventricular valve closure involves contact between every combination of shell surfaces, edges of shells, and cables, but our formulation handles all contact scenarios in a unified manner. We take advantage of this versatility to demonstrate the effects of chordal rupture on tricuspid valve coaptation behavior.},\n\tauthor = {David Kamensky and Fei Xu and Chung-Hao Lee and Jinhui Yan and Yuri Bazilevs and Ming-Chen Hsu},\n\tdoi = {https://doi.org/10.1016/j.cma.2017.11.007},\n\tissn = {0045-7825},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tkeywords = {Heart valves, Contact problems, Pinball algorithm, Isogeometric analysis},\n\tpages = {522-546},\n\ttitle = {A contact formulation based on a volumetric potential: Application to isogeometric simulations of atrioventricular valves},\n\turl = {https://www.sciencedirect.com/science/article/pii/S0045782517307120},\n\tvolume = {330},\n\tyear = {2018},\n\tbdsk-url-1 = {https://www.sciencedirect.com/science/article/pii/S0045782517307120},\n\tbdsk-url-2 = {https://doi.org/10.1016/j.cma.2017.11.007}}\n\n@incollection{Ateshian16,\n\tauthor = {Gerard A. Ateshian},\n\tbooktitle = {Studies in Mechanobiology, Tissue Engineering and Biomaterials},\n\tdate-added = {2023-05-16 21:01:13 -0400},\n\tdate-modified = {2023-05-16 21:01:17 -0400},\n\tdoi = {10.1007/978-3-319-41475-1_1},\n\tpages = {1--51},\n\tpublisher = {Springer International Publishing},\n\ttitle = {Mixture Theory for Modeling Biological Tissues: Illustrations from Articular Cartilage},\n\tyear = {2016},\n\tbdsk-url-1 = {https://doi.org/10.1007/978-3-319-41475-1_1}}\n\n@book{McNaught97,\n\taddress = {Oxford England Malden, MA, USA},\n\tauthor = {McNaught, Alan},\n\tdate-added = {2023-05-16 18:23:14 -0400},\n\tdate-modified = {2023-05-16 18:23:16 -0400},\n\tisbn = {0865426848},\n\tpublisher = {Blackwell Science},\n\ttitle = {Compendium of chemical terminology : IUPAC recommendations},\n\tyear = {1997}}\n\n@article{Ateshian13,\n\tauthor = {Gerard A. Ateshian and Steve Maas and Jeffrey A. Weiss},\n\tdate-added = {2023-05-16 18:17:50 -0400},\n\tdate-modified = {2023-05-16 18:17:53 -0400},\n\tdoi = {10.1115/1.4024823},\n\tjournal = {J. Biomech. Eng.},\n\tnumber = {11},\n\tpublisher = {{ASME} International},\n\ttitle = {Multiphasic Finite Element Framework for Modeling Hydrated Mixtures With Multiple Neutral and Charged Solutes},\n\tvolume = {135},\n\tyear = {2013},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4024823}}\n\n@article{Ateshian14,\n\tauthor = {Gerard A. Ateshian and Robert J. Nims and Steve Maas and Jeffrey A. Weiss},\n\tdate-added = {2023-05-16 18:17:50 -0400},\n\tdate-modified = {2023-05-16 18:17:56 -0400},\n\tdoi = {10.1007/s10237-014-0560-1},\n\tjournal = {Biomech. Model. Mechanobiol.},\n\tnumber = {5},\n\tpages = {1105--1120},\n\tpublisher = {Springer Science and Business Media {LLC}},\n\ttitle = {Computational modeling of chemical reactions and interstitial growth and remodeling involving charged solutes and solid-bound molecules},\n\tvolume = {13},\n\tyear = {2014},\n\tbdsk-url-1 = {https://doi.org/10.1007/s10237-014-0560-1}}\n\n@article{Shim23,\n\tabstract = {The objective of this study was to implement a novel fluid-solutes solver into the open-source finite element software FEBio, that extended available modeling capabilities for biological fluids and fluid-solute mixtures. Using a reactive mixture framework, this solver accommodates diffusion, convection, chemical reactions, electrical charge effects, and external body forces, without requiring stabilization methods that were deemed necessary in previous computational implementations of the convection-diffusion-reaction equation at high Peclet numbers. Verification and validation problems demonstrated the ability of this solver to produce solutions for Peclet numbers as high as 1e11, spanning the range of physiological conditions for convection-dominated solute transport. This outcome was facilitated by the use of a formulation that accommodates realistic values for solvent compressibility, and by expressing the solute mass balance such that it properly captured convective transport by the solvent and produced a natural boundary condition of zero diffusive solute flux at outflow boundaries. Since this numerical scheme was not necessarily foolproof, guidelines were included to achieve better outcomes that minimize or eliminate the potential occurrence of numerical artifacts. The fluid-solutes solver presented in this study represents an important and novel advancement in the modeling capabilities for biomechanics and biophysics as it allows modeling of mechanobiological processes via the incorporation of chemical reactions involving neutral or charged solutes within dynamic fluid flow. The incorporation of charged solutes in a reactive framework represents a significant novelty of this solver. This framework also applies to a broader range of non-biological applications.},\n\tauthor = {Shim, Jay J and Maas, Steve A and Weiss, Jeffrey A and Ateshian, Gerard A},\n\tdate-added = {2023-05-31 13:48:25 -0400},\n\tdate-modified = {2023-05-31 13:48:50 -0400},\n\tdoi = {10.1115/1.4062594},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmonth = {May},\n\tpages = {1-26},\n\tpmid = {37219843},\n\tpst = {aheadofprint},\n\ttitle = {Finite Element Implementation Of Computational Fluid Dynamics With Reactive Neutral And Charged Solute Transport In Febio},\n\tyear = {2023},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4062594}}\n\n@article{Menzel12,\n\tabstract = {Unlike common engineering materials, living matter can autonomously respond to environmental changes. Living structures can grow stronger, weaker, larger, or smaller within months, weeks, or days as a result of a continuous microstructural turnover and renewal. Hard tissues can adapt by increasing their density and grow strong. Soft tissues can adapt by increasing their volume and grow large. For more than three decades, the mechanics community has actively contributed to understand the phenomena of growth and remodeling from a mechanistic point of view. However, to date, there is no single, unified characterization of growth, which is equally accepted by all scientists in the field. Here we shed light on the continuum modeling of growth and remodeling of living matter, and give a comprehensive overview of historical developments and trends. We provide a state-of-the-art review of current research highlights, and discuss challenges and potential future directions. Using the example of volumetric growth, we illustrate how we can establish and utilize growth theories to characterize the functional adaptation of soft living matter. We anticipate this review to be the starting point for critical discussions and future research in growth and remodeling, with a potential impact on life science and medicine.},\n\tauthor = {Menzel, Andreas and Kuhl, Ellen},\n\tdate-added = {2022-12-19 12:01:52 -0500},\n\tdate-modified = {2022-12-19 12:01:52 -0500},\n\tdoi = {10.1016/j.mechrescom.2012.02.007},\n\tjournal = {Mech Res Commun},\n\tjournal-full = {Mechanics research communications},\n\tmonth = {Jun},\n\tpages = {1-14},\n\tpmc = {PMC3424004},\n\tpmid = {22919118},\n\tpst = {ppublish},\n\ttitle = {Frontiers in growth and remodeling},\n\tvolume = {42},\n\tyear = {2012},\n\tbdsk-url-1 = {https://doi.org/10.1016/j.mechrescom.2012.02.007}}\n\n@article{Rodriguez94,\n\tabstract = {Growth and remodeling in tissues may be modulated by mechanical factors such as stress. For example, in cardiac hypertrophy, alterations in wall stress arising from changes in mechanical loading lead to cardiac growth and remodeling. A general continuum formulation for finite volumetric growth in soft elastic tissues is therefore proposed. The shape change of an unloaded tissue during growth is described by a mapping analogous to the deformation gradient tensor. This mapping is decomposed into a transformation of the local zero-stress reference state and an accompanying elastic deformation that ensures the compatibility of the total growth deformation. Residual stress arises from this elastic deformation. Hence, a complete kinematic formulation for growth in general requires a knowledge of the constitutive law for stress in the tissue. Since growth may in turn be affected by stress in the tissue, a general form for the stress-dependent growth law is proposed as a relation between the symmetric growth-rate tensor and the stress tensor. With a thick-walled hollow cylinder of incompressible, isotropic hyperelastic material as an example, the mechanics of left ventricular hypertrophy are investigated. The results show that transmurally uniform pure circumferential growth, which may be similar to eccentric ventricular hypertrophy, changes the state of residual stress in the heart wall. A model of axially loaded bone is used to test a simple stress-dependent growth law in which growth rate depends on the difference between the stress due to loading and a predetermined growth equilibrium stress.},\n\tauthor = {Rodriguez, E K and Hoger, A and McCulloch, A D},\n\tdate-added = {2022-12-19 11:31:36 -0500},\n\tdate-modified = {2022-12-19 11:31:36 -0500},\n\tdoi = {10.1016/0021-9290(94)90021-3},\n\tjournal = {J Biomech},\n\tjournal-full = {Journal of biomechanics},\n\tmesh = {Bone Development; Bone and Bones; Elastic Tissue; Elasticity; Heart; Humans; Hypertrophy; Hypertrophy, Left Ventricular; Mathematics; Models, Biological; Stress, Mechanical},\n\tmonth = {Apr},\n\tnumber = {4},\n\tpages = {455-67},\n\tpmid = {8188726},\n\tpst = {ppublish},\n\ttitle = {Stress-dependent finite growth in soft elastic tissues},\n\tvolume = {27},\n\tyear = {1994},\n\tbdsk-url-1 = {https://doi.org/10.1016/0021-9290(94)90021-3}}\n\n@article{Shi22,\n\tauthor = {Shi, Lei and Hu, Lingfeng and Lee, Nicole and Fang, Shuyang and Myers, Kristin},\n\tdate-added = {2022-11-01 16:48:10 -0400},\n\tdate-modified = {2022-11-01 16:48:15 -0400},\n\tjournal = {Acta Biomaterialia},\n\tpages = {277--294},\n\tpublisher = {Elsevier},\n\ttitle = {Three-dimensional anisotropic hyperelastic constitutive model describing the mechanical response of human and mouse cervix},\n\tvolume = {150},\n\tyear = {2022}}\n\n@article{Ateshian23,\n\tabstract = {Reactive viscoelasticity is a theoretical framework based on the theory of reactive constrained mixtures that encompasses nonlinear viscoelastic responses. It models a viscoelastic solid as a mixture of strong and weak bonds that maintain the cohesiveness of the molecular constituents of the solid matter. Strong bonds impart the elastic response while weak bonds break and reform into a stress-free state in response to loading. The process of bonds breaking and reforming is modeled as a reaction where loaded bonds are the reactants and bonds reformed into a stress-free state are the products of a reaction. The reaction is triggered by the evolving state of loading. The state of stress in strong bonds is a function of the total strain in the material, whereas the state of stress in weak bonds is based on the state of strain relative to the time that these bonds were reformed. This study introduces two important practical contributions to the reactive nonlinear viscoelasticity framework: (1) normally, the evaluation of the stress tensor involves taking a summation over a continually increasing number of weak bond generations, which is poorly suited for a computational scheme. Therefore, this study presents an effective numerical scheme for evaluating the strain energy density, the Cauchy stress, and spatial elasticity tensors of reactive viscoelastic materials. (2) We provide the conditions for satisfying frame indifference for anisotropic nonlinear viscoelasticity, including for tension-bearing fiber models. Code verifications and model validations against experimental data provide evidence in support of this updated formulation.},\n\tauthor = {Ateshian, Gerard A and Petersen, Courtney A and Maas, Steve A and Weiss, Jeffrey A},\n\tdate-added = {2022-10-28 15:55:05 -0400},\n\tdate-modified = {2022-10-28 15:55:05 -0400},\n\tdoi = {10.1115/1.4054983},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Anisotropy; Elasticity; Models, Biological; Nonlinear Dynamics; Stress, Mechanical; Viscosity},\n\tmonth = {Jan},\n\tnumber = {1},\n\tpmid = {35838330},\n\tpst = {ppublish},\n\ttitle = {A Numerical Scheme for Anisotropic Reactive Nonlinear Viscoelasticity},\n\tvolume = {145},\n\tyear = {2023},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4054983}}\n\n@article{Ateshian22,\n\tabstract = {This study examines the theoretical foundations for the damage mechanics of biological tissues in relation to viscoelasticity. Its primary goal is to provide a mechanistic understanding of well-known experimental observations in biomechanics, which show that the ultimate tensile strength of viscoelastic biological tissues typically increases with increasing strain rate. The basic premise of this framework is that tissue damage occurs when strong bonds, such as covalent bonds in the solid matrix of a biological tissue, break in response to loading. This type of failure is described as elastic damage, under the idealizing assumption that strong bonds behave elastically. Viscoelasticity arises from three types of dissipative mechanisms: (1) Friction between molecules of the same species, which is represented by the tissue viscosity. (2) Friction between fluid and solid constituents of a porous medium, which is represented by the tissue hydraulic permeability. (3) Dissipative reactions arising from weak bonds breaking in response to loading, and reforming in a stress-free state, such as hydrogen bonds and other weak electrostatic bonds. When a viscoelastic tissue is subjected to loading, some of that load may be temporarily supported by those frictional and weak bond forces, reducing the amount of load supported by elastic strong bonds and thus, the extent of elastic damage sustained by those bonds. This protective effect depends on the characteristic time response of viscoelastic mechanisms in relation to the loading history. This study formalizes these concepts by presenting general equations that can model the damage mechanics of viscoelastic tissues.},\n\tauthor = {Ateshian, Gerard A and Kroupa, Kimberly and Petersen, Courtney A and Zimmerman, Brandon and Maas, Steve A and Weiss, Jeffrey A},\n\tdate-added = {2022-10-28 15:54:28 -0400},\n\tdate-modified = {2022-10-28 15:54:28 -0400},\n\tdoi = {10.1115/1.4056063},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmonth = {Oct},\n\tpmid = {36301266},\n\tpst = {aheadofprint},\n\ttitle = {Damage Mechanics of Biological Tissues in Relation to Viscoelasticity},\n\tyear = {2022},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4056063}}\n\n@article{Green46,\n\tauthor = {Green, MS and Tobolsky, AV},\n\tdate-added = {2022-10-28 15:53:02 -0400},\n\tdate-modified = {2022-10-28 15:53:02 -0400},\n\tjournal = {J Chem Phys},\n\tnumber = {2},\n\tpages = {80--92},\n\tpublisher = {AIP Publishing},\n\ttitle = {A new approach to the theory of relaxing polymeric media},\n\tvolume = {14},\n\tyear = {1946}}\n\n@book{Tobolsky60,\n\tauthor = {Tobolsky, Arthur Victor},\n\tdate-added = {2022-10-28 15:53:02 -0400},\n\tdate-modified = {2022-10-28 15:53:02 -0400},\n\tpublisher = {New York and London.; John Wiley \\&; Sons. Inc.},\n\ttitle = {Properties and structure of polymers},\n\tyear = {1960}}\n\n@article{Rice76,\n\tauthor = {Rice, James R and Cleary, Michael P},\n\tdate-added = {2022-10-28 15:52:34 -0400},\n\tdate-modified = {2022-10-28 15:52:34 -0400},\n\tjournal = {Reviews of Geophysics},\n\tnumber = {2},\n\tpages = {227--241},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Some basic stress diffusion solutions for fluid-saturated elastic porous media with compressible constituents},\n\tvolume = {14},\n\tyear = {1976}}\n\n@article{Cryer63,\n\tauthor = {Cryer, CWA},\n\tdate-added = {2022-10-28 15:52:24 -0400},\n\tdate-modified = {2022-10-28 15:52:24 -0400},\n\tjournal = {The Quarterly Journal of Mechanics and Applied Mathematics},\n\tnumber = {4},\n\tpages = {401--412},\n\tpublisher = {Oxford University Press},\n\ttitle = {A comparison of the three-dimensional consolidation theories of Biot and Terzaghi},\n\tvolume = {16},\n\tyear = {1963}}\n\n@article{Coleman63,\n\tauthor = {Coleman, Bernard D and Noll, Walter},\n\tdate-added = {2022-10-28 15:52:06 -0400},\n\tdate-modified = {2022-10-28 15:52:06 -0400},\n\tjournal = {Arch Ration Mech An},\n\tnumber = {1},\n\tpages = {167--178},\n\tpublisher = {Springer},\n\ttitle = {The thermodynamics of elastic materials with heat conduction and viscosity},\n\tvolume = {13},\n\tyear = {1963}}\n\n@article{Holzapfel96,\n\tauthor = {Holzapfel, Gerhard A and Simo, Juan C},\n\tdate-added = {2022-10-28 15:36:17 -0400},\n\tdate-modified = {2022-10-28 15:36:17 -0400},\n\tjournal = {Int J Solids Struct},\n\tnumber = {20},\n\tpages = {3019--3034},\n\tpublisher = {Elsevier},\n\ttitle = {A new viscoelastic constitutive model for continuous media at finite thermomechanical changes},\n\tvolume = {33},\n\tyear = {1996}}\n\n@article{Lubliner85,\n\tauthor = {Lubliner, J},\n\tdate-added = {2022-10-28 15:36:10 -0400},\n\tdate-modified = {2022-10-28 15:36:10 -0400},\n\tjournal = {Mech Res Commun},\n\tnumber = {2},\n\tpages = {93--99},\n\tpublisher = {Elsevier},\n\ttitle = {A model of rubber viscoelasticity},\n\tvolume = {12},\n\tyear = {1985}}\n\n@article{Coleman67,\n\tauthor = {Coleman, Bernard D and Gurtin, Morton E},\n\tdate-added = {2022-10-28 15:33:51 -0400},\n\tdate-modified = {2022-10-28 15:33:51 -0400},\n\tjournal = {J Chem Phys},\n\tnumber = {2},\n\tpages = {597--613},\n\tpublisher = {AIP Publishing},\n\ttitle = {Thermodynamics with internal state variables},\n\tvolume = {47},\n\tyear = {1967}}\n\n@article{Shim22a,\n\tabstract = {Mixture theory is a general framework that has been used to model mixtures of solid, fluid, and solute constituents, leading to significant advances in modeling the mechanics of biological tissues and cells. Though versatile and applicable to a wide range of problems in biomechanics and biophysics, standard multiphasic mixture frameworks incorporate neither dynamics of viscous fluids nor fluid compressibility, both of which facilitate the finite element implementation of computational fluid dynamics solvers. This study formulates governing equations for reactive multiphasic mixtures where the interstitial fluid has a solvent which is viscous and compressible. This hybrid reactive multiphasic framework uses state variables that include the deformation gradient of the porous solid matrix, the volumetric strain and rate of deformation of the solvent, the solute concentrations, and the relative velocities between the various constituents. Unlike standard formulations which employ a Lagrange multiplier to model fluid pressure, this framework requires the formulation of a function of state for the pressure, which depends on solvent volumetric strain and solute concentrations. Under isothermal conditions the formulation shows that the solvent volumetric strain remains continuous across interfaces between hybrid multiphasic domains. Apart from the Lagrange multiplier-state function distinction for the fluid pressure, and the ability to accommodate viscous fluid dynamics, this hybrid multiphasic framework remains fully consistent with standard multiphasic formulations previously employed in biomechanics. With these additional features, the hybrid multiphasic mixture theory makes it possible to address a wider range of problems that are important in biomechanics and mechanobiology.},\n\tauthor = {Shim, Jay J and Ateshian, Gerard A},\n\tdate-added = {2022-08-05 09:25:40 -0400},\n\tdate-modified = {2022-08-05 09:25:40 -0400},\n\tdoi = {10.1115/1.4051926},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Biomechanical Phenomena; Finite Element Analysis; Porosity; Solutions; Solvents; Viscosity},\n\tmonth = {01},\n\tnumber = {1},\n\tpmc = {PMC8547015},\n\tpmid = {34318318},\n\tpst = {ppublish},\n\ttitle = {A Hybrid Reactive Multiphasic Mixture With a Compressible Fluid Solvent},\n\tvolume = {144},\n\tyear = {2022},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4051926}}\n\n@article{Zimmerman22,\n\tabstract = {The frictional response of porous and permeable hydrated biological tissues such as articular cartilage is significantly dependent on interstitial fluid pressurization. To model this response, it is common to represent such tissues as biphasic materials, consisting of a binary mixture of a porous solid matrix and an interstitial fluid. However, no computational algorithms currently exist in either commercial or open-source software that can model frictional contact between such materials. Therefore, this study formulates and implements a finite element algorithm for large deformation biphasic frictional contact in the open-source finite element software FEBio. This algorithm relies on a local form of a biphasic friction model that has been previously validated against experiments, and implements the model into our recently-developed surface-to-surface (STS) contact algorithm. Contact constraints, including those specific to pressurized porous media, are enforced with the penalty method regularized with an active-passive augmented Lagrangian scheme. Numerical difficulties specific to challenging finite deformation biphasic contact problems are overcome with novel smoothing schemes for fluid pressures and Lagrange multipliers. Implementation accuracy is verified against semi-analytical solutions for biphasic frictional contact, with extensive validation performed using canonical cartilage friction experiments from prior literature. Essential details of the formulation are provided in this paper, and the source code of this biphasic frictional contact algorithm is made available to the general public.},\n\tauthor = {Zimmerman, Brandon K and Maas, Steve A and Weiss, Jeffrey A and Ateshian, Gerard A},\n\tdate-added = {2022-08-05 09:25:40 -0400},\n\tdate-modified = {2022-08-05 09:25:40 -0400},\n\tdoi = {10.1115/1.4052114},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Algorithms; Biomechanical Phenomena; Cartilage, Articular; Finite Element Analysis; Friction; Models, Biological; Porosity; Stress, Mechanical},\n\tmonth = {02},\n\tnumber = {2},\n\tpmc = {PMC8547016},\n\tpmid = {34382640},\n\tpst = {ppublish},\n\ttitle = {A Finite Element Algorithm for Large Deformation Biphasic Frictional Contact Between Porous-Permeable Hydrated Soft Tissues},\n\tvolume = {144},\n\tyear = {2022},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4052114}}\n\n@article{Ateshian22a,\n\tabstract = {Mixture theory models continua consisting of multiple constituents with independent motions. In constrained mixtures, all constituents share the same velocity but they may have different reference configurations. The theory of constrained reactive mixtures was formulated to analyze growth and remodeling in living biological tissues. It can also reproduce and extend classical frameworks of damage mechanics and viscoelasticity under isothermal conditions, when modeling bonds that can break and reform. This study focuses on establishing the thermodynamic foundations of constrained reactive mixtures under more general conditions, for arbitrary reactive processes where temperature varies in time and space. By incorporating general expressions for reaction kinetics, it is shown that the residual dissipation statement of the Clausius-Duhem inequality must include a reactive power density, while the axiom of energy balance must include a reactive heat supply density. Both of these functions are proportional to the molar production rate of a reaction, and they depend on the chemical potentials of the mixture constituents. We present novel formulas for the classical thermodynamic concepts of energy of formation and heat of reaction, making it possible to evaluate the heat supply generated by reactive processes from the knowledge of the specific free energy of mixture constituents as well as the reaction rate. We illustrate these novel concepts with mixtures of ideal gases, and isothermal reactive damage mechanics and viscoelasticity, as well as reactive thermoelasticity. This framework facilitates the analysis of reactive tissue biomechanics and physiological and biomedical engineering processes where temperature variations cannot be neglected.},\n\tauthor = {Ateshian, Gerard A and Zimmerman, Brandon K},\n\tdate-added = {2022-08-05 09:25:40 -0400},\n\tdate-modified = {2022-08-05 09:25:40 -0400},\n\tdoi = {10.1115/1.4053084},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tkeywords = {constrained reactive mixtures; energy of formation; heat of reaction; ideal gas mixtures; thermodynamics; thermoelasticity},\n\tmesh = {Biomechanical Phenomena; Entropy; Kinetics; Thermodynamics; Viscosity},\n\tmonth = {04},\n\tnumber = {4},\n\tpmc = {PMC8719048},\n\tpmid = {34802058},\n\tpst = {ppublish},\n\ttitle = {Continuum Thermodynamics of Constrained Reactive Mixtures},\n\tvolume = {144},\n\tyear = {2022},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4053084}}\n\n@article{Shim22,\n\tabstract = {The primary aim of this study is to establish the theoretical foundations for a solid-fluid biphasic mixture domain that can accommodate inertial effects and a viscous interstitial fluid, which can interface with a dynamic viscous fluid domain. Most mixture formulations consist of constituents that are either all intrinsically incompressible or compressible, thereby introducing inherent limitations. In particular, mixtures with intrinsically incompressible constituents can only model wave propagation in the porous solid matrix, whereas those with compressible constituents require internal variables, and related evolution equations, to distinguish the compressibility of the solid and fluid under hydrostatic pressure. In this study, we propose a hybrid framework for a biphasic mixture where the skeleton of the porous solid is intrinsically incompressible but the interstitial fluid is compressible. We define a state variable as a measure of the fluid volumetric strain. Within an isothermal framework, the Clausius-Duhem inequality shows that a function of state arises for the fluid pressure as a function of this strain measure. We derive jump conditions across hybrid biphasic interfaces, which are suitable for modeling hydrated biological tissues. We then illustrate this framework using confined compression and dilatational wave propagation analyses. The governing equations for this hybrid biphasic framework reduce to those of the classical biphasic theory whenever the bulk modulus of the fluid is set to infinity and inertia terms and viscous fluid effects are neglected. The availability of this novel framework facilitates the implementation of finite element solvers for fluid-structure interactions at interfaces between viscous fluids and porous-deformable biphasic domains, which can include fluid exchanges across those interfaces.},\n\tauthor = {Shim, Jay J and Ateshian, Gerard A},\n\tdate-added = {2022-08-05 09:25:13 -0400},\n\tdate-modified = {2022-08-05 09:25:13 -0400},\n\tdoi = {10.1007/s00419-020-01851-8},\n\tjournal = {Arch Appl Mech},\n\tjournal-full = {Archive of applied mechanics = Ingenieur-Archiv},\n\tmonth = {Feb},\n\tnumber = {2},\n\tpages = {491-511},\n\tpmc = {PMC8939891},\n\tpmid = {35330673},\n\tpst = {ppublish},\n\ttitle = {A Hybrid Biphasic Mixture Formulation for Modeling Dynamics in Porous Deformable Biological Tissues},\n\tvolume = {92},\n\tyear = {2022},\n\tbdsk-url-1 = {https://doi.org/10.1007/s00419-020-01851-8}}\n\n@article{Hunter98,\n\tabstract = {A model of passive and active cardiac muscle mechanics is presented, suitable for use in continuum mechanics models of the whole heart. The model is based on an extensive review of experimental data from a variety of preparations (intact trabeculae, skinned fibres and myofibrils) and species (mainly rat and ferret) at temperatures from 20 to 27 degrees C. Experimental tests include isometric tension development, isotonic loading, quick-release/restretch, length step and sinusoidal perturbations. We show that all of these experiments can be interpreted with a four state variable model which includes (i) the passive elasticity of myocardial tissue, (ii) the rapid binding of Ca2+ to troponin C and its slower tension-dependent release, (iii) the kinetics of tropomyosin movement and availability of crossbridge binding sites and the length dependence of this process and (iv) the kinetics of crossbridge tension development under perturbations of myofilament length.},\n\tauthor = {Hunter, P J and McCulloch, A D and ter Keurs, H E},\n\tdate-added = {2022-04-16 18:28:38 -0400},\n\tdate-modified = {2022-04-16 18:28:38 -0400},\n\tdoi = {10.1016/s0079-6107(98)00013-3},\n\tjournal = {Prog Biophys Mol Biol},\n\tjournal-full = {Progress in biophysics and molecular biology},\n\tmesh = {Actin Cytoskeleton; Animals; Biomechanical Phenomena; Calcium; Elasticity; Ferrets; Heart; In Vitro Techniques; Kinetics; Mathematics; Models, Cardiovascular; Myocardial Contraction; Myocardium; Rats; Troponin C},\n\tnumber = {2-3},\n\tpages = {289-331},\n\tpmid = {9785944},\n\tpst = {ppublish},\n\ttitle = {Modelling the mechanical properties of cardiac muscle},\n\tvolume = {69},\n\tyear = {1998},\n\tbdsk-url-1 = {https://doi.org/10.1016/s0079-6107(98)00013-3}}\n\n@article{Estrada20,\n\tabstract = {A wide range of emerging therapies, from surgical restraint to biomaterial injection to tissue engineering, aim to improve heart function and limit adverse remodeling following myocardial infarction (MI). We previously showed that longitudinal surgical reinforcement of large anterior infarcts in dogs could significantly enhance systolic function without restricting diastolic function, but the underlying mechanisms for this improvement are poorly understood. The goal of this study was to construct a finite element model that could match our previously published data on changes in regional strains and left ventricular function following longitudinal surgical reinforcement, then use the model to explore potential mechanisms for the improvement in systolic function we observed. The model presented here, implemented in febio, matches all the key features of our experiments, including diastolic remodeling strains in the ischemic region, small shifts in the end-diastolic pressure-volume relationship (EDPVR), and large changes in the end-systolic pressure-volume relationship (ESPVR) in response to ischemia and to patch application. Detailed examination of model strains and stresses suggests that longitudinal reinforcement reduces peak diastolic fiber stretch and systolic fiber stress in the remote myocardium and shifts those peaks away from the endocardial surface by reshaping the left ventricle (LV). These findings could help to guide the development of novel therapies to improve post-MI function by providing specific design objectives.},\n\tauthor = {Estrada, Ana Cristina and Yoshida, Kyoko and Clarke, Samantha A and Holmes, Jeffrey W},\n\tdate-added = {2022-04-16 16:45:14 -0400},\n\tdate-modified = {2022-04-16 16:45:14 -0400},\n\tdoi = {10.1115/1.4044030},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Animals; Diastole; Dogs; Myocardial Contraction; Myocardial Infarction; Ventricular Dysfunction, Left; Ventricular Function, Left},\n\tmonth = {02},\n\tnumber = {2},\n\tpmc = {PMC7104755},\n\tpmid = {31201738},\n\tpst = {ppublish},\n\ttitle = {Longitudinal Reinforcement of Acute Myocardial Infarcts Improves Function by Transmurally Redistributing Stretch and Stress},\n\tvolume = {142},\n\tyear = {2020},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4044030}}\n\n@article{Aguilar08,\n\tauthor = {Aguilar, G and Gaspar, F and Lisbona, F and Rodrigo, C24558811158},\n\tdate-added = {2022-02-16 16:49:15 -0500},\n\tdate-modified = {2022-02-16 16:49:28 -0500},\n\tjournal = {International journal for numerical methods in engineering},\n\tnumber = {11},\n\tpages = {1282--1300},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Numerical stabilization of Biot's consolidation model by a perturbation on the flow equation},\n\tvolume = {75},\n\tyear = {2008}}\n\n@article{Malkin06,\n\tauthor = {Malkin, A Ya},\n\tdate-added = {2022-01-07 17:07:34 -0500},\n\tdate-modified = {2022-01-07 17:07:44 -0500},\n\tjournal = {Applied Mechanics and Engineering},\n\tnumber = {2},\n\tpages = {235},\n\ttitle = {Continuous relaxation spectrum-its advantages and methods of calculation},\n\tvolume = {11},\n\tyear = {2006}}\n\n@book{Fung81,\n\taddress = {New York},\n\tauthor = {Fung, Y. C},\n\tcall-number = {QP88},\n\tdate-added = {2022-01-07 17:03:53 -0500},\n\tdate-modified = {2022-01-07 17:03:53 -0500},\n\tdewey-call-number = {599.01/852},\n\tgenre = {Tissues},\n\tisbn = {0387904727},\n\tlibrary-id = {80016532},\n\tpublisher = {Springer-Verlag},\n\ttitle = {Biomechanics: mechanical properties of living tissues},\n\tyear = {1981}}\n\n@book{Fung72,\n\taddress = {Englewood Cliffs, N.J.},\n\tauthor = {Fung, Y. C and Perrone, Nicholas and Anliker, M},\n\tcall-number = {QP88},\n\tdate-added = {2022-01-07 16:34:55 -0500},\n\tdate-modified = {2022-01-07 16:34:55 -0500},\n\tdewey-call-number = {612/.7},\n\tgenre = {Biomechanics},\n\tisbn = {013077149X},\n\tlibrary-id = {78160253},\n\tpublisher = {Prentice-Hall},\n\ttitle = {Biomechanics, its foundations and objectives},\n\tyear = {1972}}\n\n@misc{Foley96,\n\tauthor = {Foley, James D and van Dam, Andries and Feiner, Steven K and Hughes, John F},\n\tdate-added = {2021-08-20 13:23:10 -0400},\n\tdate-modified = {2021-08-20 13:23:10 -0400},\n\tpublisher = {Addison-Wesley, Reading, Massachusetts},\n\ttitle = {Computer Graphics: Principles and Pract Edition},\n\tyear = {1996}}\n\n@article{Zavarise09,\n\tauthor = {Zavarise, Giorgio and De Lorenzis, Laura},\n\tdate-added = {2021-08-20 13:14:58 -0400},\n\tdate-modified = {2021-08-20 13:14:58 -0400},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tnumber = {41},\n\tpages = {3428--3451},\n\tpublisher = {Elsevier},\n\ttitle = {The node-to-segment algorithm for 2D frictionless contact: classical formulation and special cases},\n\tvolume = {198},\n\tyear = {2009}}\n\n@article{Tur09,\n\tauthor = {Tur, M and Fuenmayor, FJ and Wriggers, P},\n\tdate-added = {2021-08-20 13:14:35 -0400},\n\tdate-modified = {2021-08-20 13:14:35 -0400},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tnumber = {37},\n\tpages = {2860--2873},\n\tpublisher = {Elsevier},\n\ttitle = {A mortar-based frictional contact formulation for large deformations using Lagrange multipliers},\n\tvolume = {198},\n\tyear = {2009}}\n\n@article{Simo92c,\n\tauthor = {Simo, J Ci and Laursen, TA},\n\tdate-added = {2021-08-20 11:45:00 -0400},\n\tdate-modified = {2021-08-20 11:45:04 -0400},\n\tjournal = {Computers \\&amp; Structures},\n\tnumber = {1},\n\tpages = {97--116},\n\tpublisher = {Elsevier},\n\ttitle = {An augmented Lagrangian treatment of contact problems involving friction},\n\tvolume = {42},\n\tyear = {1992}}\n\n@article{Giannakopoulos89,\n\tauthor = {Giannakopoulos, AE},\n\tdate-added = {2021-08-20 11:44:02 -0400},\n\tdate-modified = {2021-08-20 11:44:02 -0400},\n\tjournal = {Computers \\&amp; structures},\n\tnumber = {1},\n\tpages = {157--167},\n\tpublisher = {Elsevier},\n\ttitle = {The return mapping method for the integration of friction constitutive relations},\n\tvolume = {32},\n\tyear = {1989}}\n\n@book{Wriggers06,\n\taddress = {Berlin},\n\tauthor = {Wriggers, P},\n\tcall-number = {TA353},\n\tdate-added = {2021-08-20 11:43:40 -0400},\n\tdate-modified = {2021-08-20 11:43:40 -0400},\n\tdewey-call-number = {620.1/05},\n\tedition = {2nd ed},\n\tgenre = {Contact mechanics},\n\tisbn = {9783540326083 (acid-free paper)},\n\tlibrary-id = {2006922005},\n\tpublisher = {Springer},\n\ttitle = {Computational contact mechanics},\n\turl = {http://www.loc.gov/catdir/enhancements/fy0663/2006922005-d.html},\n\tyear = {2006},\n\tbdsk-url-1 = {http://www.loc.gov/catdir/enhancements/fy0663/2006922005-d.html}}\n\n@article{Saracibar97,\n\tauthor = {de Saracibar, C Agelet},\n\tdate-added = {2021-08-20 11:43:11 -0400},\n\tdate-modified = {2021-08-20 11:43:11 -0400},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tnumber = {3-4},\n\tpages = {303--334},\n\tpublisher = {Elsevier},\n\ttitle = {A new frictional time integration algorithm for large slip multi-body frictional contact problems},\n\tvolume = {142},\n\tyear = {1997}}\n\n@article{Sauer15,\n\tauthor = {Sauer, Roger A and De Lorenzis, Laura},\n\tdate-added = {2021-08-20 11:43:00 -0400},\n\tdate-modified = {2021-08-20 11:43:00 -0400},\n\tjournal = {International Journal for Numerical Methods in Engineering},\n\tnumber = {4},\n\tpages = {251--280},\n\tpublisher = {Wiley Online Library},\n\ttitle = {An unbiased computational contact formulation for 3D friction},\n\tvolume = {101},\n\tyear = {2015}}\n\n@article{Wriggers90,\n\tauthor = {Wriggers, Peter and Van, T Vu and Stein, Erwin},\n\tdate-added = {2021-08-20 11:42:46 -0400},\n\tdate-modified = {2021-08-20 11:42:46 -0400},\n\tjournal = {Computers \\&amp; Structures},\n\tnumber = {3},\n\tpages = {319--331},\n\tpublisher = {Elsevier},\n\ttitle = {Finite element formulation of large deformation impact-contact problems with friction},\n\tvolume = {37},\n\tyear = {1990}}\n\n@article{Poulios15,\n\tauthor = {Poulios, Konstantinos and Renard, Yves},\n\tdate-added = {2021-08-20 11:42:11 -0400},\n\tdate-modified = {2021-08-20 11:42:11 -0400},\n\tjournal = {Computers \\&amp; Structures},\n\tpages = {75--90},\n\tpublisher = {Elsevier},\n\ttitle = {An unconstrained integral approximation of large sliding frictional contact between deformable solids},\n\tvolume = {153},\n\tyear = {2015}}\n\n@book{Bertsekas82,\n\taddress = {New York},\n\tauthor = {Bertsekas, Dimitri P},\n\tcall-number = {QA402.5},\n\tdate-added = {2021-08-20 11:18:29 -0400},\n\tdate-modified = {2021-08-20 11:18:29 -0400},\n\tdewey-call-number = {519.4},\n\tgenre = {Mathematical optimization},\n\tisbn = {0120934809},\n\tlibrary-id = {81017612},\n\tpublisher = {Academic Press},\n\ttitle = {Constrained optimization and Lagrange multiplier methods},\n\tyear = {1982}}\n\n@incollection{Curnier95,\n\tauthor = {Curnier, Alain and He, Qi-Chang and Klarbring, Anders},\n\tbooktitle = {Contact mechanics},\n\tdate-added = {2021-08-20 11:15:58 -0400},\n\tdate-modified = {2021-08-20 11:15:58 -0400},\n\tpages = {145--158},\n\tpublisher = {Springer},\n\ttitle = {Continuum mechanics modelling of large deformation contact with friction},\n\tyear = {1995}}\n\n@book{Wriggers07,\n\taddress = {Wien},\n\tauthor = {Wriggers, P and Laursen, Tod A},\n\tcall-number = {TA353},\n\tdate-added = {2021-08-20 11:15:46 -0400},\n\tdate-modified = {2021-08-20 11:15:46 -0400},\n\tdewey-call-number = {620.105},\n\tgenre = {Contact mechanics},\n\tisbn = {9783211772973},\n\tlibrary-id = {2008273250},\n\tpublisher = {Springer},\n\tseries = {CISM courses and lectures},\n\ttitle = {Computational contact mechanics},\n\tvolume = {no. 498},\n\tyear = {2007},\n\tbdsk-url-1 = {http://www.loc.gov/catdir/enhancements/fy0904/2008273250-d.html}}\n\n@article{Zimmerman18,\n\tabstract = {This study formulates a finite element algorithm for frictional contact of solid materials, accommodating finite deformation and sliding. The algorithm uses a penalty method regularized with an augmented Lagrangian scheme to enforce contact constraints in a nonmortar surface-to-surface approach. Use of a novel kinematical approach to contact detection and enforcement of frictional constraints allows solution of complex problems previously requiring mortar methods or contact smoothing algorithms. Patch tests are satisfied to a high degree of accuracy with a single-pass penalty method, ensuring formulation errors do not affect the solution. The accuracy of the implementation is verified with Hertzian contact, and illustrations demonstrating the ability to handle large deformations and sliding are presented and validated against prior literature. A biomechanically relevant example addressing finger friction during grasping demonstrates the utility of the proposed algorithm. The algorithm is implemented in the open source software febio, and the source code is made available to the general public.},\n\tauthor = {Zimmerman, Brandon K and Ateshian, Gerard A},\n\tdate-added = {2021-08-20 11:02:59 -0400},\n\tdate-modified = {2021-08-20 11:02:59 -0400},\n\tdoi = {10.1115/1.4040497},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Algorithms; Biomechanical Phenomena; Finite Element Analysis; Friction; Surface Properties},\n\tmonth = {08},\n\tnumber = {8},\n\tpmc = {PMC6056201},\n\tpmid = {30003262},\n\tpst = {ppublish},\n\ttitle = {A Surface-to-Surface Finite Element Algorithm for Large Deformation Frictional Contact in febio},\n\tvolume = {140},\n\tyear = {2018},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4040497}}\n\n@article{Ateshian09b,\n\tabstract = {Over the last two decades, considerable progress has been reported in the field of cartilage mechanics that impacts our understanding of the role of interstitial fluid pressurization on cartilage lubrication. Theoretical and experimental studies have demonstrated that the interstitial fluid of cartilage pressurizes considerably under loading, potentially supporting most of the applied load under various transient or steady-state conditions. The fraction of the total load supported by fluid pressurization has been called the fluid load support. Experimental studies have demonstrated that the friction coefficient of cartilage correlates negatively with this variable, achieving remarkably low values when the fluid load support is greatest. A theoretical framework that embodies this relationship has been validated against experiments, predicting and explaining various outcomes, and demonstrating that a low friction coefficient can be maintained for prolonged loading durations under normal physiological function. This paper reviews salient aspects of this topic, as well as its implications for improving our understanding of boundary lubrication by molecular species in synovial fluid and the cartilage superficial zone. Effects of cartilage degeneration on its frictional response are also reviewed.},\n\tauthor = {Ateshian, Gerard A},\n\tdate-added = {2021-08-20 11:02:05 -0400},\n\tdate-modified = {2021-08-20 11:02:05 -0400},\n\tdoi = {10.1016/j.jbiomech.2009.04.040},\n\tjournal = {J Biomech},\n\tjournal-full = {Journal of biomechanics},\n\tmesh = {Animals; Cartilage, Articular; Chondroitin ABC Lyase; Compressive Strength; Extracellular Fluid; Friction; Humans; Stress, Mechanical; Synovial Fluid},\n\tmonth = {Jun},\n\tnumber = {9},\n\tpages = {1163-76},\n\tpmc = {PMC2758165},\n\tpmid = {19464689},\n\tpst = {ppublish},\n\ttitle = {The role of interstitial fluid pressurization in articular cartilage lubrication},\n\tvolume = {42},\n\tyear = {2009},\n\tbdsk-url-1 = {https://doi.org/10.1016/j.jbiomech.2009.04.040}}\n\n@article{Kiousis08,\n\tauthor = {Kiousis, DE and Gasser, TC and Holzapfel, GA},\n\tdate-added = {2021-08-16 11:00:46 -0400},\n\tdate-modified = {2021-08-16 11:00:57 -0400},\n\tjournal = {International journal for numerical methods in engineering},\n\tnumber = {7},\n\tpages = {826--855},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Smooth contact strategies with emphasis on the modeling of balloon angioplasty with stenting},\n\tvolume = {75},\n\tyear = {2008}}\n\n@article{Kiousis09,\n\tauthor = {Kiousis, Dimitrios E and Wulff, Alexander R and Holzapfel, Gerhard A},\n\tdate-added = {2021-08-16 11:00:32 -0400},\n\tdate-modified = {2021-08-16 11:00:57 -0400},\n\tjournal = {Annals of Biomedical Engineering},\n\tnumber = {2},\n\tpages = {315--330},\n\tpublisher = {Springer},\n\ttitle = {Experimental studies and numerical analysis of the inflation and interaction of vascular balloon catheter-stent systems},\n\tvolume = {37},\n\tyear = {2009}}\n\n@article{Skelton97,\n\tauthor = {Skelton, RP and Maier, HJ and Christ, H-J},\n\tdate-added = {2021-06-28 07:15:40 -0400},\n\tdate-modified = {2021-06-28 07:15:40 -0400},\n\tjournal = {Materials Science and Engineering: A},\n\tnumber = {2},\n\tpages = {377--390},\n\tpublisher = {Elsevier},\n\ttitle = {The Bauschinger effect, Masing model and the Ramberg--Osgood relation for cyclic deformation in metals},\n\tvolume = {238},\n\tyear = {1997}}\n\n@article{Nims17,\n\tauthor = {Nims, Robert J and Ateshian, Gerard A},\n\tdate-added = {2021-06-27 13:43:05 -0400},\n\tdate-modified = {2021-06-27 13:43:05 -0400},\n\tjournal = {Journal of Elasticity},\n\tnumber = {1-2},\n\tpages = {69--105},\n\tpublisher = {Springer},\n\ttitle = {Reactive constrained mixtures for modeling the solid matrix of biological tissues},\n\tvolume = {129},\n\tyear = {2017}}\n\n@article{Bonora97,\n\tauthor = {Bonora, Nicola},\n\tdate-added = {2021-06-27 10:34:21 -0400},\n\tdate-modified = {2021-06-27 10:34:21 -0400},\n\tjournal = {Engineering fracture mechanics},\n\tnumber = {1-2},\n\tpages = {11--28},\n\tpublisher = {Elsevier},\n\ttitle = {A nonlinear CDM model for ductile failure},\n\tvolume = {58},\n\tyear = {1997}}\n\n@book{Lemaitre05,\n\tauthor = {Lemaitre, Jean and Desmorat, Rodrigue},\n\tdate-added = {2021-06-27 10:33:56 -0400},\n\tdate-modified = {2021-06-27 10:33:56 -0400},\n\tpublisher = {Springer Science \\& Business Media},\n\ttitle = {Engineering damage mechanics: ductile, creep, fatigue and brittle failures},\n\tyear = {2005}}\n\n@article{Zimmerman21,\n\tabstract = {This study presents a framework for plasticity and elastoplastic damage mechanics by treating materials as reactive solids whose internal composition evolves in response to applied loading. Using the framework of constrained reactive mixtures, plastic deformation is accounted for by allowing loaded bonds within the material to break and reform in a stressed state. Bonds which break and reform represent a new generation with a new reference configuration, which is time-invariant and provided by constitutive assumption. The constitutive relation for the reference configuration of each generation may depend on the selection of a suitable yield measure. The choice of this measure and the resulting plastic flow conditions are constrained by the Clausius-Duhem inequality. We show that this framework remains consistent with classical plasticity approaches and principles. Verification of this reactive plasticity framework, which is implemented in the open source FEBio finite element software (febio.org), is performed against standard 2D and 3D benchmark problems. Damage is incorporated into this reactive framework by allowing loaded bonds to break permanently according to a suitable damage measure, where broken bonds can no longer store free energy. Validation is also demonstrated against experimental data for problems involving plasticity and plastic damage. This study demonstrates that it is possible to formulate simple elastoplasticity and elastoplastic damage models within a consistent framework which uses measures of material mass composition as theoretically observable state variables. This theoretical frame can be expanded in scope to account for more complex behaviors.},\n\tauthor = {Brandon K. Zimmerman and David Jiang and Jeffrey A. Weiss and Lucas H. Timmins and Gerard A. Ateshian},\n\tdate-added = {2021-06-27 10:31:23 -0400},\n\tdate-modified = {2021-06-27 10:31:23 -0400},\n\tdoi = {https://doi.org/10.1016/j.jmps.2021.104534},\n\tissn = {0022-5096},\n\tjournal = {Journal of the Mechanics and Physics of Solids},\n\tkeywords = {Reactive constrained mixtures, Continuum thermodynamics, Plasticity, Damage mechanics},\n\tpages = {104534},\n\ttitle = {On the use of constrained reactive mixtures of solids to model finite deformation isothermal elastoplasticity and elastoplastic damage mechanics},\n\turl = {https://www.sciencedirect.com/science/article/pii/S0022509621001940},\n\tyear = {2021},\n\tbdsk-url-1 = {https://www.sciencedirect.com/science/article/pii/S0022509621001940},\n\tbdsk-url-2 = {https://doi.org/10.1016/j.jmps.2021.104534}}\n\n@article{Papanastasiou87,\n\tauthor = {Papanastasiou, Tasos C},\n\tdate-added = {2021-06-13 20:55:49 -0400},\n\tdate-modified = {2021-06-13 20:56:02 -0400},\n\tjournal = {Journal of Rheology},\n\tnumber = {5},\n\tpages = {385--404},\n\tpublisher = {The Society of Rheology},\n\ttitle = {Flows of materials with yield},\n\tvolume = {31},\n\tyear = {1987}}\n\n@article{Drucker49,\n\tauthor = {Drucker, Daniel Charles},\n\tdate-added = {2021-05-16 18:26:09 -0400},\n\tdate-modified = {2021-05-16 18:26:18 -0400},\n\tjournal = {Journal of Applied Mechanics},\n\ttitle = {Relation of experiments to mathematical theories of plasticity},\n\tyear = {1949}}\n\n@article{Hou89,\n\tabstract = {The objective of this study is to establish and verify the set of boundary conditions at the interface between a biphasic mixture (articular cartilage) and a Newtonian or non-Newtonian fluid (synovial fluid) such that a set of well-posed mathematical problems may be formulated to investigate joint lubrication problems. A \"pseudo-no-slip\" kinematic boundary condition is proposed based upon the principle that the conditions at the interface between mixtures or mixtures and fluids must reduce to those boundary conditions in single phase continuum mechanics. From this proposed kinematic boundary condition, and balances of mass, momentum and energy, the boundary conditions at the interface between a biphasic mixture and a Newtonian or non-Newtonian fluid are mathematically derived. Based upon these general results, the appropriate boundary conditions needed in modeling the cartilage-synovial fluid-cartilage lubrication problem are deduced. For two simple cases where a Newtonian viscous fluid is forced to flow (with imposed Couette or Poiseuille flow conditions) over a porous-permeable biphasic material of relatively low permeability, the well known empirical Taylor slip condition may be derived using matched asymptotic analysis of the boundary layer at the interface.},\n\tauthor = {Hou, J S and Holmes, M H and Lai, W M and Mow, V C},\n\tdate-added = {2021-03-26 16:13:09 -0400},\n\tdate-modified = {2021-03-26 16:13:09 -0400},\n\tdoi = {10.1115/1.3168343},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Cartilage, Articular; Joints; Lubrication; Models, Biological; Rheology; Synovial Fluid; Viscosity},\n\tmonth = {Feb},\n\tnumber = {1},\n\tpages = {78-87},\n\tpmid = {2747237},\n\tpst = {ppublish},\n\ttitle = {Boundary conditions at the cartilage-synovial fluid interface for joint lubrication and theoretical verifications},\n\tvolume = {111},\n\tyear = {1989},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.3168343}}\n\n@article{Shim19,\n\tabstract = {Many physiological systems involve strong interactions between fluids and solids, posing a signicant challenge when modeling biomechanics. The objective of this study was to implement a fluid-structure interaction (FSI) solver in the free, open-source finite element code FEBio (febio.org), that combined the existing solid mechanics and rigid body dynamics solver with a recently-developed computational fluid dynamics (CFD) solver. A novel Galerkin-based finite element FSI formulation was introduced based on mixture theory, where the FSI domain was described as a mixture of fluid and solid constituents that have distinct motions. The mesh was defined on the solid domain, specialized to have zero mass, negligible stiffness and zero frictional interactions with the fluid, whereas the fluid was modeled as isothermal and compressible. The mixture framework provided the foundation for evaluating material time derivatives in a material frame for the solid and in a spatial frame for the fluid. Similar to our recently reported CFD solver, our FSI formulation did not require stabilization methods to achieve good convergence, producing a compact set of equations and code implementation. The code was successfully verified against benchmark problems and an analytical solution for squeeze-film lubrication. It was validated against experimental measurements of the flow rate in a peristaltic pump, and illustrated using non-Newtonian blood flow through a bifurcated carotid artery with a thick arterial wall. The successful formulation and implementation of this FSI solver enhances the multiphysics modeling capabilities in FEBio relevant to the biomechanics and biophysics communities.},\n\tauthor = {Shim, Jay J and Maas, Steve A and Weiss, Jeffrey A and Ateshian, Gerard A},\n\tdate-added = {2021-03-26 16:02:30 -0400},\n\tdate-modified = {2021-03-26 16:02:30 -0400},\n\tdoi = {10.1115/1.4043031},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmonth = {Mar},\n\tpmc = {PMC6528685},\n\tpmid = {30835271},\n\tpst = {aheadofprint},\n\ttitle = {A Formulation for Fluid Structure-Interactions in FEBio Using Mixture Theory},\n\tyear = {2019},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4043031}}\n\n@article{Gatti20,\n\tabstract = {Pulse wave imaging (PWI) is an ultrasound-based method that allows spatiotemporal mapping of the arterial pulse wave propagation, from which the local pulse wave velocity (PWV) can be derived. Recent reports indicate that PWI can help the assessment of atherosclerotic plaque composition and mechanical properties. However, the effect of the atherosclerotic plaque's geometry and mechanics on the arterial wall distension and local PWV remains unclear. In this study we investigated the accuracy of a finite element (FE) fluid-structure interaction (FSI) approach to predict the velocity of a pulse wave propagating through a stenotic artery with an asymmetrical plaque, as quantified with PWI method. Experiments were designed to compare FE-FSI modeling of the pulse wave propagation through a stenotic artery against PWI obtained with manufactured phantom arteries made of PVA material. FSI-generated spatiotemporal maps were used to estimate PWV at the plaque region and compare it to the experimental results. Velocity of the pulse wave propagation and magnitude of the wall distension were correctly predicted with the FE analysis. In addition, findings indicate that a plaque with a high degree of stenosis (>70%) attenuates the propagation of the pulse pressure wave. Results of this study support the validity of the FE-FSI methods to investigate the effect of arterial wall structural and mechanical properties on the pulse wave propagation. This modeling method can help to guide the optimization of PWI to characterize plaque properties and substantiate clinical findings.},\n\tauthor = {Gatti, Vittorio and Nauleau, Pierre and Karageorgos, Grigorios Mario and Shim, Jay J and Ateshian, Gerard A and Konofagou, Elisa E},\n\tdate-added = {2021-03-26 16:02:27 -0400},\n\tdate-modified = {2021-03-26 16:02:27 -0400},\n\tdoi = {10.1115/1.4048708},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmonth = {Oct},\n\tpmc = {PMC7872000},\n\tpmid = {33030208},\n\tpst = {aheadofprint},\n\ttitle = {Modelling Pulse Wave Propagation Through a Stenotic Artery with Fluid Structure Interaction: A Validation Study Using Ultrasound Pulse Wave Imaging},\n\tyear = {2020},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4048708}}\n\n@article{Shim21,\n\tabstract = {In biomechanics, solid-fluid mixtures have commonly been used to model the response of hydrated biological tissues. In cartilage mechanics, this type of mixture, where the fluid and solid constituents are both assumed to be intrinsically incompressible, is often called a biphasic material. Various physiological processes involve the interaction of a viscous fluid with a porous-hydrated tissue, as encountered in synovial joint lubrication, cardiovascular mechanics, and respiratory mechanics. The objective of this study was to implement a finite element solver in the open-source software FEBio that models dynamic interactions between a viscous fluid and a biphasic domain, accommodating finite deformations of both domains as well as fluid exchanges between them. For compatibility with our recent implementation of solvers for computational fluid dynamics (CFD) and fluid-structure interactions (FSI), where the fluid is slightly compressible, this study employs a novel hybrid biphasic formulation where the porous skeleton is intrinsically incompressible but the fluid is also slightly compressible. The resulting biphasic-FSI (BFSI) implementation is verified against published analytical and numerical benchmark problems, as well as novel analytical solutions derived for the purposes of this study. An illustration of this BFSI solver is presented for two-dimensional air flow through a simulated face mask under five cycles of breathing, showing that masks significantly reduce air dispersion compared to the no-mask control analysis. The successful formulation and implementation of this BFSI solver offers enhanced multiphysics modeling capabilities that are accessible via an open-source software platform.},\n\tauthor = {Shim, Jay J and Maas, Steve A and Weiss, Jeffrey A and Ateshian, Gerard A},\n\tdate-added = {2021-03-26 16:02:25 -0400},\n\tdate-modified = {2021-03-26 16:02:25 -0400},\n\tdoi = {10.1115/1.4050646},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmonth = {Mar},\n\tpmid = {33764435},\n\tpst = {aheadofprint},\n\ttitle = {Finite Element Implementation of Biphasic-Fluid Structure Interactions in FEBio},\n\tyear = {2021},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4050646}}\n\n@article{Gultekin19,\n\tauthor = {G{\\\"u}ltekin, Osman and Dal, H{\\\"u}sn{\\\"u} and Holzapfel, Gerhard A},\n\tdate-added = {2020-12-23 09:12:27 -0500},\n\tdate-modified = {2020-12-23 09:12:37 -0500},\n\tjournal = {Computational mechanics},\n\tnumber = {3},\n\tpages = {443--453},\n\tpublisher = {Springer},\n\ttitle = {On the quasi-incompressible finite element analysis of anisotropic hyperelastic materials},\n\tvolume = {63},\n\tyear = {2019}}\n\n@article{Sansour08,\n\tauthor = {Sansour, Carlo},\n\tdate-added = {2020-12-23 09:11:29 -0500},\n\tdate-modified = {2020-12-23 09:12:32 -0500},\n\tjournal = {European Journal of Mechanics-A/Solids},\n\tnumber = {1},\n\tpages = {28--39},\n\tpublisher = {Elsevier},\n\ttitle = {On the physical assumptions underlying the volumetric-isochoric split and the case of anisotropy},\n\tvolume = {27},\n\tyear = {2008}}\n\n@article{Helfenstein10,\n\tauthor = {Helfenstein, J and Jabareen, M and Mazza, Edoardo and Govindjee, S},\n\tdate-added = {2020-12-23 08:58:40 -0500},\n\tdate-modified = {2020-12-23 08:58:46 -0500},\n\tjournal = {International Journal of Solids and Structures},\n\tnumber = {16},\n\tpages = {2056--2061},\n\tpublisher = {Elsevier},\n\ttitle = {On non-physical response in models for fiber-reinforced hyperelastic materials},\n\tvolume = {47},\n\tyear = {2010}}\n\n@article{Simo87a,\n\tauthor = {Simo, Juan C and Ju, JW},\n\tdate-added = {2020-08-23 18:43:17 -0400},\n\tdate-modified = {2020-08-23 18:44:13 -0400},\n\tjournal = {International journal of solids and structures},\n\tnumber = {7},\n\tpages = {821--840},\n\tpublisher = {Elsevier},\n\ttitle = {Strain-and stress-based continuum damage models---I. Formulation},\n\tvolume = {23},\n\tyear = {1987}}\n\n@article{Chaboche81,\n\tauthor = {Chaboche, Jean-Louis},\n\tdate-added = {2020-08-23 18:42:58 -0400},\n\tdate-modified = {2020-08-23 18:43:23 -0400},\n\tjournal = {Nuclear Engineering and Design},\n\tnumber = {2},\n\tpages = {233--247},\n\tpublisher = {Elsevier},\n\ttitle = {Continuous damage mechanics---a tool to describe phenomena before crack initiation},\n\tvolume = {64},\n\tyear = {1981}}\n\n@article{Lemaitre85,\n\tauthor = {Lemaitre, Jean},\n\tdate-added = {2020-08-23 18:42:37 -0400},\n\tdate-modified = {2020-08-23 18:43:33 -0400},\n\tjournal = {J. Eng. Mater. Technol.},\n\ttitle = {A continuous damage mechanics model for ductile fracture},\n\tyear = {1985}}\n\n@article{Lemaitre84,\n\tauthor = {Lemaitre, Jean},\n\tdate-added = {2020-08-23 18:42:20 -0400},\n\tdate-modified = {2020-08-23 18:43:31 -0400},\n\tjournal = {Nuclear engineering and design},\n\tnumber = {2},\n\tpages = {233--245},\n\tpublisher = {Elsevier},\n\ttitle = {How to use damage mechanics},\n\tvolume = {80},\n\tyear = {1984}}\n\n@book{Rabotnov80,\n\tauthor = {Rabotnov, Yu N},\n\tdate-added = {2020-08-23 18:41:17 -0400},\n\tdate-modified = {2020-08-23 18:45:19 -0400},\n\tpublisher = {MIT Publishers, Moscow},\n\ttitle = {Elements of hereditary solid mechanics},\n\tyear = {1980}}\n\n@article{Kachanov58,\n\tauthor = {Kachanov, Lazar M},\n\tdate-added = {2020-08-23 18:40:55 -0400},\n\tdate-modified = {2020-08-23 18:44:51 -0400},\n\tjournal = {International Journal of Fracture},\n\tpublisher = {IZVESTIA ACADEMII NAUK SSSR OTDELENIE TEKHNICHESKICH NAUK},\n\ttitle = {Rupture time under creep conditions},\n\tyear = {1958}}\n\n@article{Nims16,\n\tabstract = {This study presents a damage mechanics framework that employs observable state variables to describe damage in isotropic or anisotropic fibrous tissues. In this mixture theory framework, damage is tracked by the mass fraction of bonds that have broken. Anisotropic damage is subsumed in the assumption that multiple bond species may coexist in a material, each having its own damage behaviour. This approach recovers the classical damage mechanics formulation for isotropic materials, but does not appeal to a tensorial damage measure for anisotropic materials. In contrast with the classical approach, the use of observable state variables for damage allows direct comparison of model predictions to experimental damage measures, such as biochemical assays or Raman spectroscopy. Investigations of damage in discrete fibre distributions demonstrate that the resilience to damage increases with the number of fibre bundles; idealizing fibrous tissues using continuous fibre distribution models precludes the modelling of damage. This damage framework was used to test and validate the hypothesis that growth of cartilage constructs can lead to damage of the synthesized collagen matrix due to excessive swelling caused by synthesized glycosaminoglycans. Therefore, alternative strategies must be implemented in tissue engineering studies to prevent collagen damage during the growth process. },\n\tauthor = {Nims, Robert J and Durney, Krista M and Cigan, Alexander D and Duss{\\'e}aux, Antoine and Hung, Clark T and Ateshian, Gerard A},\n\tdate-added = {2020-08-23 18:21:23 -0400},\n\tdate-modified = {2020-08-23 18:21:32 -0400},\n\tdoi = {10.1098/rsfs.2015.0063},\n\tjournal = {Interface Focus},\n\tjournal-full = {Interface focus},\n\tkeywords = {cartilage tissue engineering; damage mechanics; fibrous tissues},\n\tmonth = {Feb},\n\tnumber = {1},\n\tpages = {20150063},\n\tpmc = {PMC4686240},\n\tpmid = {26855751},\n\tpst = {ppublish},\n\ttitle = {Continuum theory of fibrous tissue damage mechanics using bond kinetics: application to cartilage tissue engineering},\n\tvolume = {6},\n\tyear = {2016},\n\tbdsk-url-1 = {https://doi.org/10.1098/rsfs.2015.0063}}\n\n@article{Hou18,\n\tauthor = {Hou, Jay C and Maas, Steve A and Weiss, Jeffrey A and Ateshian, Gerard A},\n\tdate-added = {2020-07-30 18:51:23 -0400},\n\tdate-modified = {2020-07-30 18:51:34 -0400},\n\tjournal = {Journal of Biomechanical Engineering},\n\tnumber = {12},\n\tpublisher = {American Society of Mechanical Engineers Digital Collection},\n\ttitle = {Finite Element Formulation of Multiphasic Shell Elements for Cell Mechanics Analyses in FEBio},\n\tvolume = {140},\n\tyear = {2018}}\n\n@article{Simo93,\n\tauthor = {Simo, JC and Armero, F and Taylor, RL},\n\tdate-added = {2020-07-30 17:19:55 -0400},\n\tdate-modified = {2020-07-30 17:20:02 -0400},\n\tjournal = {Computer methods in applied mechanics and engineering},\n\tnumber = {3-4},\n\tpages = {359--386},\n\tpublisher = {Elsevier},\n\ttitle = {Improved versions of assumed enhanced strain tri-linear elements for 3D finite deformation problems},\n\tvolume = {110},\n\tyear = {1993}}\n\n@article{Simo90,\n\tauthor = {Simo, Juan C and Rifai, MS10587420724},\n\tdate-added = {2020-07-30 17:18:41 -0400},\n\tdate-modified = {2020-07-30 17:18:55 -0400},\n\tjournal = {International journal for numerical methods in engineering},\n\tnumber = {8},\n\tpages = {1595--1638},\n\tpublisher = {Wiley Online Library},\n\ttitle = {A class of mixed assumed strain methods and the method of incompatible modes},\n\tvolume = {29},\n\tyear = {1990}}\n\n@article{Vu-Quoc03,\n\tauthor = {Vu-Quoc, L and Tan, XG},\n\tdate-added = {2020-07-30 17:16:13 -0400},\n\tdate-modified = {2020-07-30 17:16:31 -0400},\n\tjournal = {Computer methods in applied mechanics and engineering},\n\tnumber = {9-10},\n\tpages = {975--1016},\n\tpublisher = {Elsevier},\n\ttitle = {Optimal solid shells for non-linear analyses of multilayer composites. I. Statics},\n\tvolume = {192},\n\tyear = {2003}}\n\n@article{Klinkel99,\n\tauthor = {Klinkel, S and Gruttmann, F and Wagner, W},\n\tdate-added = {2020-07-30 17:15:51 -0400},\n\tdate-modified = {2020-07-30 17:16:25 -0400},\n\tjournal = {Computers \\& Structures},\n\tnumber = {1},\n\tpages = {43--62},\n\tpublisher = {Elsevier},\n\ttitle = {A continuum based three-dimensional shell element for laminated structures},\n\tvolume = {71},\n\tyear = {1999}}\n\n@article{Bischoff97,\n\tauthor = {Bischoff, M and Ramm, E},\n\tdate-added = {2020-07-30 17:15:20 -0400},\n\tdate-modified = {2020-07-30 17:15:29 -0400},\n\tjournal = {International Journal for Numerical Methods in Engineering},\n\tnumber = {23},\n\tpages = {4427--4449},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Shear deformable shell elements for large strains and rotations},\n\tvolume = {40},\n\tyear = {1997}}\n\n@article{Betsch95,\n\tauthor = {Betsch, P and Stein, E},\n\tdate-added = {2020-07-30 17:14:57 -0400},\n\tdate-modified = {2020-07-30 17:15:04 -0400},\n\tjournal = {Communications in Numerical Methods in Engineering},\n\tnumber = {11},\n\tpages = {899--909},\n\tpublisher = {Wiley Online Library},\n\ttitle = {An assumed strain approach avoiding artificial thickness straining for a non-linear 4-node shell element},\n\tvolume = {11},\n\tyear = {1995}}\n\n@article{Bathe86,\n\tauthor = {Bathe, Klaus-J{\\\"u}rgen and Dvorkin, Eduardo N},\n\tdate-added = {2020-07-30 17:14:19 -0400},\n\tdate-modified = {2020-07-30 17:14:24 -0400},\n\tjournal = {International journal for numerical methods in engineering},\n\tnumber = {3},\n\tpages = {697--722},\n\tpublisher = {Wiley Online Library},\n\ttitle = {A formulation of general shell elements---the use of mixed interpolation of tensorial components},\n\tvolume = {22},\n\tyear = {1986}}\n\n@article{MacNeal78,\n\tauthor = {MacNeal, Richard H},\n\tdate-added = {2020-07-30 17:13:45 -0400},\n\tdate-modified = {2020-07-30 17:13:55 -0400},\n\tjournal = {Computers \\& Structures},\n\tnumber = {2},\n\tpages = {175--183},\n\tpublisher = {Elsevier},\n\ttitle = {A simple quadrilateral shell element},\n\tvolume = {8},\n\tyear = {1978}}\n\n@article{Bischoff18,\n\tauthor = {Bischoff, Manfred and Ramm, E and Irslinger, J},\n\tdate-added = {2020-07-30 17:12:54 -0400},\n\tdate-modified = {2020-07-30 17:13:05 -0400},\n\tjournal = {Encyclopedia of Computational Mechanics Second Edition},\n\tpages = {1--86},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Models and finite elements for thin-walled structures},\n\tyear = {2018}}\n\n@article{Gasser06,\n\tabstract = {Constitutive relations are fundamental to the solution of problems in continuum mechanics, and are required in the study of, for example, mechanically dominated clinical interventions involving soft biological tissues. Structural continuum constitutive models of arterial layers integrate information about the tissue morphology and therefore allow investigation of the interrelation between structure and function in response to mechanical loading. Collagen fibres are key ingredients in the structure of arteries. In the media (the middle layer of the artery wall) they are arranged in two helically distributed families with a small pitch and very little dispersion in their orientation (i.e. they are aligned quite close to the circumferential direction). By contrast, in the adventitial and intimal layers, the orientation of the collagen fibres is dispersed, as shown by polarized light microscopy of stained arterial tissue. As a result, continuum models that do not account for the dispersion are not able to capture accurately the stress-strain response of these layers. The purpose of this paper, therefore, is to develop a structural continuum framework that is able to represent the dispersion of the collagen fibre orientation. This then allows the development of a new hyperelastic free-energy function that is particularly suited for representing the anisotropic elastic properties of adventitial and intimal layers of arterial walls, and is a generalization of the fibre-reinforced structural model introduced by Holzapfel & Gasser (Holzapfel & Gasser 2001 Comput. Meth. Appl. Mech. Eng. 190, 4379-4403) and Holzapfel et al. (Holzapfel et al. 2000 J. Elast. 61, 1-48). The model incorporates an additional scalar structure parameter that characterizes the dispersed collagen orientation. An efficient finite element implementation of the model is then presented and numerical examples show that the dispersion of the orientation of collagen fibres in the adventitia of human iliac arteries has a significant effect on their mechanical response.},\n\tauthor = {Gasser, T Christian and Ogden, Ray W and Holzapfel, Gerhard A},\n\tdate-added = {2020-02-27 09:16:38 -0500},\n\tdate-modified = {2020-02-27 09:16:38 -0500},\n\tdoi = {10.1098/rsif.2005.0073},\n\tjournal = {J R Soc Interface},\n\tjournal-full = {Journal of the Royal Society, Interface},\n\tmesh = {Animals; Anisotropy; Arteries; Biomechanical Phenomena; Elasticity; Fibrillar Collagens; Humans; Models, Cardiovascular; Protein Conformation; Stress, Mechanical},\n\tmonth = {Feb},\n\tnumber = {6},\n\tpages = {15-35},\n\tpmc = {PMC1618483},\n\tpmid = {16849214},\n\tpst = {ppublish},\n\ttitle = {Hyperelastic modelling of arterial layers with distributed collagen fibre orientations},\n\tvolume = {3},\n\tyear = {2006},\n\tbdsk-url-1 = {https://doi.org/10.1098/rsif.2005.0073}}\n\n@article{Criscione00,\n\tauthor = {Criscione, John C and Humphrey, Jay D and Douglas, Andrew S and Hunter, William C},\n\tdate-added = {2019-09-07 09:58:47 -0400},\n\tdate-modified = {2019-09-07 09:59:18 -0400},\n\tjournal = {J. Mech. Phys. Solids},\n\tnumber = {12},\n\tpages = {2445--2465},\n\tpublisher = {Elsevier},\n\ttitle = {An invariant basis for natural strain which yields orthogonal stress response terms in isotropic hyperelasticity},\n\tvolume = {48},\n\tyear = {2000}}\n\n@article{Ateshian18,\n\tabstract = {The mechanics of biological fluids is an important topic in biomechanics, often requiring the use of computational tools to analyze problems with realistic geometries and material properties. This study describes the formulation and implementation of a finite element framework for computational fluid dynamics (CFD) in FEBio, a free software designed to meet the computational needs of the biomechanics and biophysics communities. This formulation models nearly incompressible flow with a compressible isothermal formulation that uses a physically realistic value for the fluid bulk modulus. It employs fluid velocity and dilatation as essential variables: The virtual work integral enforces the balance of linear momentum and the kinematic constraint between fluid velocity and dilatation, while fluid density varies with dilatation as prescribed by the axiom of mass balance. Using this approach, equal-order interpolations may be used for both essential variables over each element, contrary to traditional mixed formulations that must explicitly satisfy the inf-sup condition. The formulation accommodates Newtonian and non-Newtonian viscous responses as well as inviscid fluids. The efficiency of numerical solutions is enhanced using Broyden's quasi-Newton method. The results of finite element simulations were verified using well-documented benchmark problems as well as comparisons with other free and commercial codes. These analyses demonstrated that the novel formulation introduced in FEBio could successfully reproduce the results of other codes. The analogy between this CFD formulation and standard finite element formulations for solid mechanics makes it suitable for future extension to fluid-structure interactions (FSIs).},\n\tauthor = {Ateshian, Gerard A and Shim, Jay J and Maas, Steve A and Weiss, Jeffrey A},\n\tdate-added = {2018-08-05 11:55:36 -0400},\n\tdate-modified = {2018-08-05 11:55:36 -0400},\n\tdoi = {10.1115/1.4038716},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmonth = {Feb},\n\tnumber = {2},\n\tpmc = {PMC5816258},\n\tpmid = {29238817},\n\tpst = {ppublish},\n\ttitle = {Finite Element Framework for Computational Fluid Dynamics in FEBio},\n\tvolume = {140},\n\tyear = {2018},\n\tbdsk-url-1 = {https://doi.org/10.1115/1.4038716}}\n\n@article{Bazilevs08,\n\tauthor = {Bazilevs, Yuri and Calo, Victor M and Hughes, Thomas JR and Zhang, Yongjie},\n\tdate-added = {2018-03-11 22:10:43 +0000},\n\tdate-modified = {2018-03-11 22:10:50 +0000},\n\tjournal = {Computational mechanics},\n\tnumber = {1},\n\tpages = {3--37},\n\tpublisher = {Springer},\n\ttitle = {Isogeometric fluid-structure interaction: theory, algorithms, and computations},\n\tvolume = {43},\n\tyear = {2008}}\n\n@article{Simo91a,\n\tauthor = {Simo, Juan Carlos and Wong, Kachung Kevin},\n\tdate-added = {2017-05-17 11:32:46 +0000},\n\tdate-modified = {2017-05-17 11:32:55 +0000},\n\tjournal = {International journal for numerical methods in engineering},\n\tnumber = {1},\n\tpages = {19--52},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Unconditionally stable algorithms for rigid body dynamics that exactly preserve energy and momentum},\n\tvolume = {31},\n\tyear = {1991}}\n\n@article{Puso02,\n\tauthor = {Puso, Michael Anthony},\n\tdate-added = {2017-05-17 11:29:50 +0000},\n\tdate-modified = {2017-05-17 11:30:01 +0000},\n\tjournal = {International Journal for numerical methods in engineering},\n\tnumber = {6},\n\tpages = {1393--1414},\n\tpublisher = {Wiley Online Library},\n\ttitle = {An energy and momentum conserving method for rigid--flexible body dynamics},\n\tvolume = {53},\n\tyear = {2002}}\n\n@article{Gonzalez00,\n\tauthor = {Gonzalez, Oscar},\n\tdate-added = {2017-05-17 11:29:12 +0000},\n\tdate-modified = {2017-05-17 11:29:18 +0000},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tnumber = {13},\n\tpages = {1763--1783},\n\tpublisher = {Elsevier},\n\ttitle = {Exact energy and momentum conserving algorithms for general models in nonlinear elasticity},\n\tvolume = {190},\n\tyear = {2000}}\n\n@article{Simo92b,\n\tauthor = {Simo, Juan C and Tarnow, N and Wong, KK},\n\tdate-added = {2017-05-17 11:28:15 +0000},\n\tdate-modified = {2017-05-17 11:28:30 +0000},\n\tjournal = {Computer methods in applied mechanics and engineering},\n\tnumber = {1},\n\tpages = {63--116},\n\tpublisher = {Elsevier},\n\ttitle = {Exact energy-momentum conserving algorithms and symplectic schemes for nonlinear dynamics},\n\tvolume = {100},\n\tyear = {1992}}\n\n@article{Simo92a,\n\tauthor = {Simo, JC and Tarnow, Nils},\n\tdate-added = {2017-05-17 11:28:00 +0000},\n\tdate-modified = {2017-05-17 11:28:27 +0000},\n\tjournal = {Zeitschrift f{\\\"u}r angewandte Mathematik und Physik (ZAMP)},\n\tnumber = {5},\n\tpages = {757--792},\n\tpublisher = {Springer},\n\ttitle = {The discrete energy-momentum method. Conserving algorithms for nonlinear elastodynamics},\n\tvolume = {43},\n\tyear = {1992}}\n\n@article{Hilber77,\n\tauthor = {Hilber, Hans M and Hughes, Thomas JR and Taylor, Robert L},\n\tdate-added = {2017-05-17 11:26:42 +0000},\n\tdate-modified = {2017-05-17 11:26:49 +0000},\n\tjournal = {Earthquake Engineering \\&amp; Structural Dynamics},\n\tnumber = {3},\n\tpages = {283--292},\n\tpublisher = {Wiley Online Library},\n\ttitle = {Improved numerical dissipation for time integration algorithms in structural dynamics},\n\tvolume = {5},\n\tyear = {1977}}\n\n@article{Vignon-Clementel06,\n\tauthor = {Vignon-Clementel, Irene E and Figueroa, C Alberto and Jansen, Kenneth E and Taylor, Charles A},\n\tdate-added = {2017-05-17 00:38:10 +0000},\n\tdate-modified = {2017-05-17 00:38:10 +0000},\n\tjournal = {Comput. Methods Appl. Mech. Engrg.},\n\tjournal-full = {Computer methods in applied mechanics and engineering},\n\tnumber = {29},\n\tpages = {3776--3796},\n\tpublisher = {Elsevier},\n\ttitle = {Outflow boundary conditions for three-dimensional finite element modeling of blood flow and pressure in arteries},\n\tvolume = {195},\n\tyear = {2006}}\n\n@article{Esmaily-Moghadam11,\n\tauthor = {Esmaily Moghadam, Mahdi and Bazilevs, Yuri and Hsia, Tain-Yen and Vignon-Clementel, Irene E and Marsden, Alison L},\n\tdate-added = {2017-05-17 00:37:42 +0000},\n\tdate-modified = {2017-05-17 00:37:42 +0000},\n\tjournal = {Comput. Mech.},\n\tjournal-full = {Computational Mechanics},\n\tnumber = {3},\n\tpages = {277--291},\n\tpublisher = {Springer},\n\ttitle = {A comparison of outlet boundary treatments for prevention of backflow divergence with relevance to blood flow simulations},\n\tvolume = {48},\n\tyear = {2011}}\n\n@article{Bazilevs09,\n\tauthor = {Bazilevs, Y and Gohean, JR and Hughes, TJR and Moser, RD and Zhang, Y},\n\tdate-added = {2017-05-17 00:37:37 +0000},\n\tdate-modified = {2017-05-17 00:37:37 +0000},\n\tjournal = {Comput. Methods Appl. Mech. Engrg.},\n\tjournal-full = {Computer Methods in Applied Mechanics and Engineering},\n\tnumber = {45},\n\tpages = {3534--3550},\n\tpublisher = {Elsevier},\n\ttitle = {Patient-specific isogeometric fluid--structure interaction analysis of thoracic aortic blood flow due to implantation of the {Jarvik} 2000 left ventricular assist device},\n\tvolume = {198},\n\tyear = {2009}}\n\n@book{Reddy01,\n\taddress = {Boca Raton, FL},\n\tauthor = {Reddy, J. N. and Gartling, David K.},\n\tdate-added = {2017-05-17 00:36:56 +0000},\n\tdate-modified = {2017-05-17 00:36:56 +0000},\n\tedition = {2nd},\n\tisbn = {084932355X (alk. paper)},\n\tpages = {469 p.},\n\tpublisher = {CRC Press},\n\ttitle = {The finite element method in heat transfer and fluid dynamics},\n\ttype = {Book},\n\turl = {Publisher description http://www.loc.gov/catdir/enhancements/fy0646/00048638-d.html},\n\tyear = {2001},\n\tbdsk-url-1 = {Publisher%20description%20http://www.loc.gov/catdir/enhancements/fy0646/00048638-d.html}}\n\n@article{Cho91,\n\tabstract = {Effects of the non-Newtonian viscosity of blood on a flow in a coronary arterial casting of man were studied numerically using a finite element method. Various constitutive models were examined to model the non-Newtonian viscosity of blood and their model constants were summarized. A method to incorporate the non-Newtonian viscosity of blood was introduced so that the viscosity could be calculated locally. The pressure drop, wall shear stress and velocity profiles for the case of blood viscosity were compared for the case of Newtonian viscosity (0.0345 poise). The effect of the non-Newtonian viscosity of blood on the overall pressure drop across the arterial casting was found to be significant at a flow of the Reynolds number of 100 or less. Also in the region of flow separation or recirculation, the non-Newtonian viscosity of blood yields larger wall shear stress than the Newtonian case. The origin of the non-Newtonian viscosity of blood was discussed in relation to the viscoelasticity and yield stress of blood.},\n\tauthor = {Cho, Y I and Kensey, K R},\n\tdate-added = {2017-05-17 00:24:02 +0000},\n\tdate-modified = {2017-05-17 00:24:02 +0000},\n\tjournal = {Biorheology},\n\tjournal-full = {Biorheology},\n\tmesh = {Biomechanical Phenomena; Blood Pressure; Blood Viscosity; Coronary Disease; Coronary Vessels; Humans; Models, Biological; Regional Blood Flow},\n\tnumber = {3-4},\n\tpages = {241-62},\n\tpmid = {1932716},\n\tpst = {ppublish},\n\ttitle = {Effects of the non-{Newtonian} viscosity of blood on flows in a diseased arterial vessel. {Part} 1: {Steady} flows},\n\tvolume = {28},\n\tyear = {1991}}\n\n@book{Panton06,\n\tauthor = {Panton, Ronald L},\n\tdate-added = {2017-05-17 00:23:49 +0000},\n\tdate-modified = {2017-05-17 00:23:49 +0000},\n\tpublisher = {John Wiley \\&amp; Sons},\n\ttitle = {Incompressible flow},\n\tyear = {2006}}\n\n@book{Reddy08,\n\taddress = {New York},\n\tauthor = {Reddy, J. N.},\n\tdate-added = {2017-05-17 00:23:33 +0000},\n\tdate-modified = {2017-05-17 00:23:33 +0000},\n\tisbn = {9780521870443 (hardback) 0521870445 (hardback)},\n\tpages = {xiv, 354 p.},\n\tpublisher = {Cambridge University Press},\n\ttitle = {An introduction to continuum mechanics : with applications},\n\ttype = {Book},\n\turl = {Table of contents only http://www.loc.gov/catdir/toc/ecip0720/2007025254.html Contributor biographical information http://www.loc.gov/catdir/enhancements/fy0729/2007025254-b.html Publisher description http://www.loc.gov/catdir/enhancements/fy0729/2007025254-d.html},\n\tyear = {2008},\n\tbdsk-url-1 = {Table%20of%20contents%20only%20http://www.loc.gov/catdir/toc/ecip0720/2007025254.html%20Contributor%20biographical%20information%20http://www.loc.gov/catdir/enhancements/fy0729/2007025254-b.html%20Publisher%20description%20http://www.loc.gov/catdir/enhancements/fy0729/2007025254-d.html}}\n\n@article{Jansen00,\n\tauthor = {Jansen, Kenneth E and Whiting, Christian H and Hulbert, Gregory M},\n\tdate-added = {2017-05-17 00:20:46 +0000},\n\tdate-modified = {2017-05-17 00:20:46 +0000},\n\tjournal = {Comput. Methods Appl. Mech. Engrg.},\n\tjournal-full = {Computer Methods in Applied Mechanics and Engineering},\n\tnumber = {3},\n\tpages = {305--319},\n\tpublisher = {Elsevier},\n\ttitle = {A generalized-$\\alpha$ method for integrating the filtered {Navier}--{Stokes} equations with a stabilized finite element method},\n\tvolume = {190},\n\tyear = {2000}}\n\n@article{Ateshian12,\n\tabstract = {A finite element formulation of neutral solute transport across a contact interface between deformable porous media is implemented and validated against analytical solutions. By reducing the integral statements of external virtual work on the two contacting surfaces into a single contact integral, the algorithm automatically enforces continuity of solute molar flux across the contact interface, whereas continuity of the effective solute concentration (a measure of the solute mechano-chemical potential) is achieved using a penalty method. This novel formulation facilitates the analysis of problems in biomechanics where the transport of metabolites across contact interfaces of deformable tissues may be of interest. This contact algorithm is the first to address solute transport across deformable interfaces, and is made available in the public domain, open-source finite element code FEBio (http://www.febio.org).},\n\tauthor = {Ateshian, Gerard A and Maas, Steve and Weiss, Jeffrey A},\n\tdate-added = {2017-01-22 16:40:27 +0000},\n\tdate-modified = {2017-01-22 16:40:27 +0000},\n\tdoi = {10.1016/j.jbiomech.2012.01.003},\n\tjournal = {J Biomech},\n\tjournal-full = {Journal of biomechanics},\n\tmesh = {Algorithms; Animals; Biological Transport; Finite Element Analysis; Humans; Models, Biological; Porosity},\n\tmonth = {Apr},\n\tnumber = {6},\n\tpages = {1023-7},\n\tpmc = {PMC3351088},\n\tpmid = {22281406},\n\tpst = {ppublish},\n\ttitle = {Solute transport across a contact interface in deformable porous media},\n\tvolume = {45},\n\tyear = {2012},\n\tbdsk-url-1 = {http://dx.doi.org/10.1016/j.jbiomech.2012.01.003}}\n\n@book{Bathe82,\n\taddress = {Englewood Cliffs, N.J.},\n\tauthor = {Bathe, Klaus-J{\\\"u}rgen},\n\tcall-number = {TA347.F5},\n\tdate-added = {2016-12-27 19:16:12 +0000},\n\tdate-modified = {2016-12-27 19:16:12 +0000},\n\tdewey-call-number = {620/.00422},\n\tgenre = {Finite element method},\n\tisbn = {0133173054},\n\tlibrary-id = {81012067},\n\tpublisher = {Prentice-Hall},\n\ttitle = {Finite element procedures in engineering analysis},\n\tyear = {1982}}\n\n@article{Azeloglu08,\n\tabstract = {The arterial wall contains a significant amount of charged proteoglycans, which are inhomogeneously distributed, with the greatest concentrations in the intimal and medial layers. The hypothesis of this study is that the transmural distribution of proteoglycans plays a significant role in regulating residual stresses in the arterial wall. This hypothesis was first tested theoretically, using the framework of mixture theory for charged hydrated tissues, and then verified experimentally by measuring the opening angle of rat aorta in NaCl solutions of various ionic strengths. A three-dimensional finite element model of aortic ring, using realistic values of the solid matrix shear modulus and proteoglycan fixed-charge density, yielded opening angles and changes with osmolarity comparable to values reported in the literature. Experimentally, the mean opening angle in isotonic saline (300 mosM) was 15 +/- 17 degrees and changed to 4 +/- 19 degrees and 73 +/- 18 degrees under hypertonic (2,000 mosM) and hypotonic (0 mosM) conditions, respectively (n = 16). In addition, the opening angle in isotonic (300 mosM) sucrose, an uncharged molecule, was 60 +/- 16 degrees (n = 11), suggesting that the charge effect, not cellular swelling, was the major underlying mechanism for these observations. The extent of changes in opening angle under osmotic challenges suggests that transmural heterogeneity of fixed-charge density plays a crucial role in governing the zero-stress configuration of the aorta. A significant implication of this finding is that arterial wall remodeling in response to altered wall stresses may occur via altered deposition of proteoglycans across the wall thickness, providing a novel mechanism for regulating mechanical homeostasis in vascular tissue.},\n\tauthor = {Azeloglu, Evren U and Albro, Michael B and Thimmappa, Vikrum A and Ateshian, Gerard A and Costa, Kevin D},\n\tdate-added = {2016-12-27 19:05:22 +0000},\n\tdate-modified = {2016-12-27 19:05:22 +0000},\n\tdoi = {10.1152/ajpheart.01027.2007},\n\tjournal = {Am J Physiol Heart Circ Physiol},\n\tjournal-full = {American journal of physiology. Heart and circulatory physiology},\n\tmesh = {Algorithms; Animals; Aorta, Thoracic; Finite Element Analysis; Glycosaminoglycans; In Vitro Techniques; Male; Osmolar Concentration; Poisson Distribution; Proteoglycans; Rats; Rats, Sprague-Dawley; Stress, Physiological},\n\tmonth = {Mar},\n\tnumber = {3},\n\tpages = {H1197-205},\n\tpmid = {18156194},\n\tpst = {ppublish},\n\ttitle = {Heterogeneous transmural proteoglycan distribution provides a mechanism for regulating residual stresses in the aorta},\n\tvolume = {294},\n\tyear = {2008},\n\tbdsk-url-1 = {http://dx.doi.org/10.1152/ajpheart.01027.2007}}\n\n@book{Holzapfel00,\n\taddress = {Chichester},\n\tauthor = {Holzapfel, Gerhard A},\n\tcall-number = {QA808.2},\n\tdate-added = {2016-12-27 18:45:16 +0000},\n\tdate-modified = {2016-12-27 18:45:16 +0000},\n\tdewey-call-number = {531},\n\tgenre = {Continuum mechanics},\n\tisbn = {047182304X (acid-free paper)},\n\tlibrary-id = {00027315},\n\tpublisher = {Wiley},\n\ttitle = {Nonlinear solid mechanics: a continuum approach for engineering},\n\turl = {http://www.loc.gov/catdir/description/wiley035/00027315.html},\n\tyear = {2000},\n\tbdsk-url-1 = {http://www.loc.gov/catdir/description/wiley035/00027315.html}}\n\n@book{Lai10,\n\taddress = {Amsterdam},\n\tauthor = {Lai, W. Michael and Rubin, David and Krempl, Erhard},\n\tcall-number = {QA808.2},\n\tdate-added = {2016-12-27 18:43:23 +0000},\n\tdate-modified = {2016-12-27 18:43:23 +0000},\n\tdewey-call-number = {531},\n\tedition = {4th ed},\n\tgenre = {Continuum mechanics},\n\tisbn = {9780750685603 (hardcover)},\n\tlibrary-id = {2009003607},\n\tpublisher = {Butterworth-Heinemann/Elsevier},\n\ttitle = {Introduction to continuum mechanics},\n\tyear = {2010}}\n\n@article{Weinans92,\n\tabstract = {The process of adaptive bone remodeling can be described mathematically and simulated in a computer model, integrated with the finite element method. In the model discussed here, cortical and trabecular bone are described as continuous materials with variable density. The remodeling rule applied to simulate the remodeling process in each element individually is, in fact, an objective function for an optimization process, relative to the external load. Its purpose is to obtain a constant, preset value for the strain energy per unit bone mass, by adapting the density. If an element in the structure cannot achieve that, it either turns to its maximal density (cortical bone) or resorbs completely. It is found that the solution obtained in generally a discontinuous patchwork. For a two-dimensional proximal femur model this patchwork shows a good resemblance with the density distribution of a real proximal femur. It is shown that the discontinuous end configuration is dictated by the nature of the differential equations describing the remodeling process. This process can be considered as a nonlinear dynamical system with many degrees of freedom, which behaves divergent relative to the objective, leading to many possible solutions. The precise solution is dependent on the parameters in the remodeling rule, the load and the initial conditions. The feedback mechanism in the process is self-enhancing, denser bone attracts more strain energy, whereby the bone becomes even more dense. It is suggested that this positive feedback of the attractor state (the strain energy field) creates order in the end configuration. In addition, the process ensures that the discontinuous end configuration is a structure with a relatively low mass, perhaps a minimal-mass structure, although this is no explicit objective in the optimization process. It is hypothesized that trabecular bone is a chaotically ordered structure which can be considered as a fractal with characteristics of optimal mechanical resistance and minimal mass, of which the actual morphology depends on the local (internal) loading characteristics, the sensor-cell density and the degree of mineralization.},\n\tauthor = {Weinans, H and Huiskes, R and Grootenboer, H J},\n\tdate-added = {2016-12-22 04:40:28 +0000},\n\tdate-modified = {2016-12-22 04:40:28 +0000},\n\tjournal = {J Biomech},\n\tjournal-full = {Journal of biomechanics},\n\tmesh = {Bone Density; Bone Remodeling; Bone Resorption; Bone and Bones; Computer Simulation; Elasticity; Femur; Femur Head; Humans; Models, Biological; Periosteum; Stress, Mechanical},\n\tmonth = {Dec},\n\tnumber = {12},\n\tpages = {1425-41},\n\tpmid = {1491020},\n\tpst = {ppublish},\n\ttitle = {The behavior of adaptive bone-remodeling simulation models},\n\tvolume = {25},\n\tyear = {1992}}\n\n@article{Albro09,\n\tabstract = {This study reports experimental measurements of solute diffusivity and partition coefficient for various solute concentrations and gel porosities, and proposes novel constitutive relations to describe these observed values. The longer-term aim is to explore the theoretical ramifications of accommodating variations in diffusivity and partition coefficient with solute concentration and tissue porosity, and investigate whether they might suggest novel mechanisms not previously recognized in the field of solute transport in deformable porous media. The study implements a model transport system of agarose hydrogels to investigate the effect of solute concentration and hydrogel porosity on the transport of dextran polysaccharides. The proposed phenomenological constitutive relations are shown to provide better fits of experimental results than prior models proposed in the literature based on the microstructure of the gel. While these constitutive models were developed for the transport of dextran in agarose hydrogels, it is expected that they may also be applied to the transport of similar molecular weight solutes in other porous media. This quantification can assist in the application of biophysical models that describe biological transport in deformable tissues, as well as the cell cytoplasm.},\n\tauthor = {Albro, Michael B and Rajan, Vikram and Li, Roland and Hung, Clark T and Ateshian, Gerard A},\n\tdate-added = {2016-12-22 04:38:26 +0000},\n\tdate-modified = {2025-12-16 14:06:51 -0500},\n\tdoi = {10.1007/s12195-009-0076-4},\n\tjournal = {Cell Mol Bioeng},\n\tjournal-full = {Cellular and molecular bioengineering},\n\tkeywords = {Collagen},\n\tmonth = {Sep},\n\tnumber = {3},\n\tpages = {295-305},\n\tpmc = {PMC2996616},\n\tpmid = {21152414},\n\tpst = {ppublish},\n\ttitle = {Characterization of the Concentration-Dependence of Solute Diffusivity and Partitioning in a Model Dextran-Agarose Transport System},\n\tvolume = {2},\n\tyear = {2009},\n\tbdsk-url-1 = {http://dx.doi.org/10.1007/s12195-009-0076-4}}\n\n@article{Simo87,\n\tauthor = {Simo, JC},\n\tdate-added = {2016-12-22 04:33:15 +0000},\n\tdate-modified = {2016-12-22 04:33:27 +0000},\n\tjournal = {Computer methods in applied mechanics and engineering},\n\tnumber = {2},\n\tpages = {153--173},\n\tpublisher = {Elsevier},\n\ttitle = {On a fully three-dimensional finite-strain viscoelastic damage model: formulation and computational aspects},\n\tvolume = {60},\n\tyear = {1987}}\n\n@article{Ateshian15,\n\tabstract = {This study presents a framework for viscoelasticity where the free energy density depends on the stored energy of intact strong and weak bonds, where weak bonds break and reform in response to loading. The stress is evaluated by differentiating the free energy density with respect to the deformation gradient, similar to the conventional approach for hyperelasticity. The breaking and reformation of weak bonds is treated as a reaction governed by the axiom of mass balance, where the constitutive relation for the mass supply governs the bond kinetics. The evolving mass contents of these weak bonds serve as observable state variables. Weak bonds reform in an energy-free and stress-free state, therefore their reference configuration is given by the current configuration at the time of their reformation. A principal advantage of this formulation is the availability of a strain energy density function that depends only on observable state variables, also allowing for a separation of the contributions of strong and weak bonds. The Clausius-Duhem inequality is satisfied by requiring that the net free energy from all breaking bonds must be decreasing at all times. In the limit of infinitesimal strains, linear stress-strain responses and first-order kinetics for breaking and reforming of weak bonds, the reactive framework reduces exactly to classical linear viscoelasticity. For large strains, the reactive and classical quasilinear viscoelasticity theories produce different equations, though responses to standard loading configurations behave similarly. This formulation complements existing tools for modeling the nonlinear viscoelastic response of biological soft tissues under large deformations.},\n\tauthor = {Ateshian, Gerard A},\n\tdate-added = {2016-12-22 04:31:42 +0000},\n\tdate-modified = {2016-12-22 04:31:42 +0000},\n\tdoi = {10.1016/j.jbiomech.2015.02.019},\n\tjournal = {J Biomech},\n\tjournal-full = {Journal of biomechanics},\n\tkeywords = {Mixture theory; Reaction kinetics; Soft tissue mechanics; Viscoelasticity},\n\tmesh = {Elasticity; Kinetics; Models, Theoretical; Viscosity},\n\tmonth = {Apr},\n\tnumber = {6},\n\tpages = {941-7},\n\tpmc = {PMC4422403},\n\tpmid = {25757663},\n\tpst = {ppublish},\n\ttitle = {Viscoelasticity using reactive constrained solid mixtures},\n\tvolume = {48},\n\tyear = {2015},\n\tbdsk-url-1 = {http://dx.doi.org/10.1016/j.jbiomech.2015.02.019}}\n\n@article{Lai91,\n\tabstract = {Swelling of articular cartilage depends on its fixed charge density and distribution, the stiffness of its collagen-proteoglycan matrix, and the ion concentrations in the interstitium. A theory for a tertiary mixture has been developed, including the two fluid-solid phases (biphasic), and an ion phase, representing cation and anion of a single salt, to describe the deformation and stress fields for cartilage under chemical and/or mechanical loads. This triphasic theory combines the physico-chemical theory for ionic and polyionic (proteoglycan) solutions with the biphasic theory for cartilage. The present model assumes the fixed charge groups to remain unchanged, and that the counter-ions are the cations of a single-salt of the bathing solution. The momentum equation for the neutral salt and for the intersitial water are expressed in terms of their chemical potentials whose gradients are the driving forces for their movements. These chemical potentials depend on fluid pressure p, salt concentration c, solid matrix dilatation e and fixed charge density cF. For a uni-uni valent salt such as NaCl, they are given by mu i = mu io + (RT/Mi)ln[gamma 2 +/- c(c + cF)] and mu w = mu wo + [p-RT phi (2c + cF) + Bwe]/pwT, where R, T, Mi, gamma +/-, phi, pwT and Bw are universal gas constant, absolute temperature, molecular weight, mean activity coefficient of salt, osmotic coefficient, true density of water, and a coupling material coefficient, respectively. For infinitesimal strains and material isotropy, the stress-strain relationship for the total mixture stress is sigma = - pI-TcI + lambda s(trE)I + 2 musE, where E is the strain tensor and (lambda s, mu s) are the Lam{\\'e} constants of the elastic solid matrix. The chemical-expansion stress (-Tc) derives from the charge-to-charge repulsive forces within the solid matrix. This theory can be applied to both equilibrium and non-equilibrium problems. For equilibrium free swelling problems, the theory yields the well known Donnan equilibrium ion distribution and osmotic pressure equations, along with an analytical expression for the \"pre-stress\" in the solid matrix. For the confined-compression swelling problem, it predicts that the applied compressive stress is shared by three load support mechanisms: 1) the Donnan osmotic pressure; 2) the chemical-expansion stress; and 3) the solid matrix elastic stress. Numerical calculations have been made, based on a set of equilibrium free-swelling and confined-compression data, to assess the relative contribution of each mechanism to load support. Our results show that all three mechanisms are important in determining the overall compressive stiffness of cartilage.},\n\tauthor = {Lai, W M and Hou, J S and Mow, V C},\n\tdate-added = {2016-12-22 04:22:59 +0000},\n\tdate-modified = {2016-12-22 04:22:59 +0000},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Biomechanical Phenomena; Cartilage, Articular; Elasticity; Models, Biological; Osmosis; Solutions; Stress, Mechanical},\n\tmonth = {Aug},\n\tnumber = {3},\n\tpages = {245-58},\n\tpmid = {1921350},\n\tpst = {ppublish},\n\ttitle = {A triphasic theory for the swelling and deformation behaviors of articular cartilage},\n\tvolume = {113},\n\tyear = {1991}}\n\n@article{Overbeek56,\n\tauthor = {Overbeek, J T},\n\tdate-added = {2016-12-22 04:22:34 +0000},\n\tdate-modified = {2020-08-23 18:44:28 -0400},\n\tjournal = {Prog Biophys Biophys Chem},\n\tjournal-full = {Progress in biophysics and biophysical chemistry},\n\tkeywords = {CHEMISTRY, PHYSICAL},\n\tmesh = {Chemical Phenomena; Chemistry, Physical; Physical Examination},\n\tpages = {57-84},\n\tpmid = {13420188},\n\tpst = {ppublish},\n\ttitle = {The Donnan equilibrium},\n\tvolume = {6},\n\tyear = {1956}}\n\n@article{Carter77,\n\tabstract = {Compression tests of human and bovine trabecular bone specimens with and without marrow in situ were conducted at strain rates of from 0.001 to 10.0 per second. A porous platen above the specimens allowed the escape of marrow during testing. The presence of marrow increased the strength, modulus, and energy absorption of specimens only at the highest strain rate of 10.0 per second. This enhancement of material properties at the highest strain rate was due primarily to the restricted viscous flow of marrow through the platen rather than the flow through the pores of the trabecular bone. In specimens without marrow, the strength was proportional to the square of the apparent density and the modulus was proportional to the cube of the apparent density. Both strength and modulus were approximately proportional to the strain rate raised to the 0.06 power. These power relationships, which were shown to hold for all bone in the skeleton, allow meaningful predictions of bone tissue strength and stiffness based on in vivo density measurements.},\n\tauthor = {Carter, D R and Hayes, W C},\n\tdate-added = {2016-12-22 04:21:10 +0000},\n\tdate-modified = {2016-12-22 04:21:10 +0000},\n\tjournal = {J Bone Joint Surg Am},\n\tjournal-full = {The Journal of bone and joint surgery. American volume},\n\tmesh = {Animals; Bone and Bones; Cattle; Humans; Stress, Mechanical; Tensile Strength},\n\tmonth = {Oct},\n\tnumber = {7},\n\tpages = {954-62},\n\tpmid = {561786},\n\tpst = {ppublish},\n\ttitle = {The compressive behavior of bone as a two-phase porous structure},\n\tvolume = {59},\n\tyear = {1977}}\n\n@article{Carter76,\n\tabstract = {The compressive strength of bone is proportional to the square of the apparent density and to the strain rate raised to the 0.06 power. This relationship is applicable to trabecular and compact bone, and provides clinical guidelines for predicting bone strength on the basis of x-ray and densitometric examination.},\n\tauthor = {Carter, D R and Hayes, W C},\n\tdate-added = {2016-12-22 04:20:57 +0000},\n\tdate-modified = {2016-12-22 04:20:57 +0000},\n\tjournal = {Science},\n\tjournal-full = {Science (New York, N.Y.)},\n\tmesh = {Animals; Bone Marrow; Bone and Bones; Cattle; Humans; Stress, Mechanical},\n\tmonth = {Dec},\n\tnumber = {4270},\n\tpages = {1174-6},\n\tpmid = {996549},\n\tpst = {ppublish},\n\ttitle = {Bone compressive strength: the influence of density and strain rate},\n\tvolume = {194},\n\tyear = {1976}}\n\n@article{Ateshian07c,\n\tabstract = {Porous-permeable tissues have often been modeled using porous media theories such as the biphasic theory. This study examines the equivalence of the short-time biphasic and incompressible elastic responses for arbitrary deformations and constitutive relations from first principles. This equivalence is illustrated in problems of unconfined compression of a disk, and of articular contact under finite deformation, using two different constitutive relations for the solid matrix of cartilage, one of which accounts for the large disparity observed between the tensile and compressive moduli in this tissue. Demonstrating this equivalence under general conditions provides a rationale for using available finite element codes for incompressible elastic materials as a practical substitute for biphasic analyses, so long as only the short-time biphasic response is sought. In practice, an incompressible elastic analysis is representative of a biphasic analysis over the short-term response deltat<Delta(2) / //parallelC(4)//K//, where Delta is a characteristic dimension, C(4) is the elasticity tensor, and K is the hydraulic permeability tensor of the solid matrix. Certain notes of caution are provided with regard to implementation issues, particularly when finite element formulations of incompressible elasticity employ an uncoupled strain energy function consisting of additive deviatoric and volumetric components.},\n\tauthor = {Ateshian, Gerard A and Ellis, Benjamin J and Weiss, Jeffrey A},\n\tdate-added = {2016-12-22 04:15:25 +0000},\n\tdate-modified = {2016-12-22 04:16:19 +0000},\n\tdoi = {10.1115/1.2720918},\n\tjournal = {J Biomech Eng},\n\tjournal-full = {Journal of biomechanical engineering},\n\tmesh = {Animals; Biomechanical Phenomena; Cartilage, Articular; Compressive Strength; Elasticity; Finite Element Analysis; Humans; Mathematics; Models, Biological; Models, Theoretical; Stress, Mechanical; Tensile Strength; Time Factors},\n\tmonth = {Jun},\n\tnumber = {3},\n\tpages = {405-12},\n\tpmc = {PMC3312381},\n\tpmid = {17536908},\n\tpst = {ppublish},\n\ttitle = {Equivalence between short-time biphasic and incompressible elastic material responses},\n\tvolume = {129},\n\tyear = {2007},\n\tbdsk-url-1 = {http://dx.doi.org/10.1115/1.2720918}}\n\n@article{Albro08,\n\tauthor = {Albro, M. B. and Chahine, N. O. and Li, R. and Yeager, K. and Hung, C. T. and Ateshian, G. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {S0021-9290(08)00448-X [pii] 10.1016/j.jbiomech.2008.08.023},\n\tissn = {0021-9290 (Print)},\n\tjournal = {J Biomech},\n\tnumber = {15},\n\tpages = {3152-7},\n\ttitle = {Dynamic loading of deformable porous media can induce active solute transport},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=18922531},\n\tvolume = {41},\n\tyear = {2008},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=18922531},\n\tbdsk-url-2 = {http://dx.doi.org/10.1016/j.jbiomech.2008.08.023}}\n\n@article{Albro10,\n\tauthor = {Albro, M. B. and Li, R. and Banerjee, R. E. and Hung, C. T. and Ateshian, G. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {S0021-9290(10)00268-X [pii] 10.1016/j.jbiomech.2010.04.041},\n\tissn = {1873-2380 (Electronic) 0021-9290 (Linking)},\n\tjournal = {J Biomech},\n\tnumber = {12},\n\tpages = {2267-73},\n\ttitle = {Validation of theoretical framework explaining active solute uptake in dynamically loaded porous media},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=20553797},\n\tvolume = {43},\n\tyear = {2010},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=20553797},\n\tbdsk-url-2 = {http://dx.doi.org/10.1016/j.jbiomech.2010.04.041}}\n\n@article{Arruda93,\n\tauthor = {Arruda, E.M. and Boyce, M.C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J. Mech. Phys. Solids},\n\tnumber = {2},\n\tpages = {389-412},\n\ttitle = {A Three-Dimensional Constitutive Model for the Large Stretch Behavior of Rubber Elastic Materials},\n\ttype = {Journal Article},\n\tvolume = {41},\n\tyear = {1993}}\n\n@article{Ateshian10b,\n\tauthor = {Ateshian, GA and Maas, SA and Weiss, J.A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J. Biomech. Engn.},\n\tnumber = {6},\n\tpages = {1006-1019},\n\ttitle = {Finite element algorithm for frictionless contact of porous permeable media under finite deformation and sliding},\n\ttype = {Journal Article},\n\tvolume = {132},\n\tyear = {2010}}\n\n@article{Ateshian07a,\n\tauthor = {Ateshian, G. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {10.1115/1.2486179},\n\tissn = {0148-0731 (Print) 0148-0731 (Linking)},\n\tjournal = {J Biomech Eng},\n\tnumber = {2},\n\tpages = {240-9},\n\ttitle = {Anisotropy of fibrous tissues in relation to the distribution of tensed and buckled fibers},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17408329},\n\tvolume = {129},\n\tyear = {2007},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17408329},\n\tbdsk-url-2 = {http://dx.doi.org/10.1115/1.2486179}}\n\n@article{Ateshian07b,\n\tauthor = {Ateshian, G. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {10.1007/s10237-006-0070-x},\n\tissn = {1617-7959 (Print)},\n\tjournal = {Biomech Model Mechanobiol},\n\tnumber = {6},\n\tpages = {423-45},\n\ttitle = {On the theory of reactive mixtures for modeling biological growth},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17206407},\n\tvolume = {6},\n\tyear = {2007},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17206407},\n\tbdsk-url-2 = {http://dx.doi.org/10.1007/s10237-006-0070-x}}\n\n@article{Ateshian09,\n\tauthor = {Ateshian, G. A. and Costa, K. D.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {S0021-9290(09)00033-5 [pii] 10.1016/j.jbiomech.2009.01.015},\n\tissn = {1873-2380 (Electronic) 1873-2380 (Linking)},\n\tjournal = {J Biomech},\n\tnumber = {6},\n\tpages = {781-5},\n\ttitle = {A frame-invariant formulation of Fung elasticity},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=19281991},\n\tvolume = {42},\n\tyear = {2009},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=19281991},\n\tbdsk-url-2 = {http://dx.doi.org/10.1016/j.jbiomech.2009.01.015}}\n\n@article{Ateshian06,\n\tauthor = {Ateshian, G. A. and Likhitpanichkul, M. and Hung, C. T.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0021-9290 (Print)},\n\tjournal = {J Biomech},\n\tnumber = {3},\n\tpages = {464-75},\n\ttitle = {A mixture theory analysis for passive transport in osmotic loading of cells},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=16389086},\n\tvolume = {39},\n\tyear = {2006},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=16389086}}\n\n@article{Ateshian09a,\n\tauthor = {Ateshian, G. A. and Rajan, V. and Chahine, N. O. and Canal, C. E. and Hung, C. T.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {10.1115/1.3118773},\n\tissn = {0148-0731 (Print) 0148-0731 (Linking)},\n\tjournal = {J Biomech Eng},\n\tnumber = {6},\n\tpages = {061003},\n\ttitle = {Modeling the matrix of articular cartilage using a continuous fiber angular distribution predicts many observed phenomena},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=19449957},\n\tvolume = {131},\n\tyear = {2009},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=19449957},\n\tbdsk-url-2 = {http://dx.doi.org/10.1115/1.3118773}}\n\n@article{Ateshian10a,\n\tauthor = {Ateshian, G. A. and Ricken, T.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {10.1007/s10237-010-0205-y},\n\tissn = {1617-7940 (Electronic) 1617-7940 (Linking)},\n\tjournal = {Biomech Model Mechanobiol},\n\tnumber = {6},\n\tpages = {689-702},\n\ttitle = {Multigenerational interstitial growth of biological tissues},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/pubmed/20238138},\n\tvolume = {9},\n\tyear = {2010},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/pubmed/20238138},\n\tbdsk-url-2 = {http://dx.doi.org/10.1007/s10237-010-0205-y}}\n\n@article{Ateshian97,\n\tauthor = {Ateshian, G. A. and Warden, W. H. and Kim, J. J. and Grelsamer, R. P. and Mow, V. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0021-9290 (Print) 0021-9290 (Linking)},\n\tjournal = {J Biomech},\n\tnumber = {11-12},\n\tpages = {1157-64},\n\ttitle = {Finite deformation biphasic material properties of bovine articular cartilage from confined compression experiments},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=9456384},\n\tvolume = {30},\n\tyear = {1997},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=9456384}}\n\n@article{Ateshian10,\n\tauthor = {Ateshian, G. A. and Weiss, J. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {10.1115/1.4002588},\n\tissn = {1528-8951 (Electronic) 0148-0731 (Linking)},\n\tjournal = {Journal of biomechanical engineering},\n\tnumber = {11},\n\tpages = {111004},\n\ttitle = {Anisotropic hydraulic permeability under finite deformation},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/pubmed/21034145},\n\tvolume = {132},\n\tyear = {2010},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/pubmed/21034145},\n\tbdsk-url-2 = {http://dx.doi.org/10.1115/1.4002588}}\n\n@article{Betsch96,\n\tauthor = {Betsch, P. and Gruttmann, F. and E., Stein},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Comput. Methods Appl. Mech. Engrg},\n\tpages = {57-79},\n\ttitle = {A 4-node finite shell element for the implementation of general hyperelastic 3D-elasticity at finite strains},\n\ttype = {Journal Article},\n\tvolume = {130},\n\tyear = {1996}}\n\n@phdthesis{Blemker04,\n\tauthor = {Blemker, SS},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\ttitle = {3D Modeling of Complex Muscle Architecture and Geometry},\n\ttype = {Thesis},\n\tuniversity = {Stanford University},\n\tyear = {2004}}\n\n@book{Bonet97,\n\tauthor = {Bonet, Javier and Wood, Richard D.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tisbn = {0-521-57272-X},\n\tpublisher = {Cambridge University Press},\n\ttitle = {Nonlinear continuum mechanics for finite element analysis},\n\ttype = {Book},\n\tyear = {1997}}\n\n@article{Bowen76,\n\tauthor = {Bowen, R.M.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-27 18:51:06 +0000},\n\tjournal = {Continuum physics},\n\tnumber = {Pt I},\n\ttitle = {Theory of mixtures},\n\tvolume = {3},\n\tyear = {1976}}\n\n@article{Bowen80,\n\tauthor = {Bowen, Ray M.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Int J Eng Sci},\n\tnumber = {9},\n\tpages = {1129-1148},\n\ttitle = {Incompressible porous media models by use of the theory of mixtures},\n\ttype = {Journal Article},\n\tvolume = {18},\n\tyear = {1980}}\n\n@article{Bowen82,\n\tauthor = {Bowen, Ray M.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Int J Eng Sci},\n\tnumber = {6},\n\tpages = {697-735},\n\ttitle = {Compressible porous media models by use of the theory of mixtures},\n\ttype = {Journal Article},\n\tvolume = {20},\n\tyear = {1982}}\n\n@article{Criscione01,\n\tauthor = {Criscione, JC and Douglas, SA and Hunter, WC},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J. Mech. Phys. Solids},\n\tpages = {871-897},\n\ttitle = {Physically based strain invariant set for materials exhibiting transversely isotropic behavior},\n\ttype = {Journal Article},\n\tvolume = {49},\n\tyear = {2001}}\n\n@article{Curnier94,\n\tauthor = {Curnier, A. and Qi-Chang, He and Zysset, P.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Elasticity},\n\tnumber = {1},\n\tpages = {1-38},\n\ttitle = {Conewise linear elastic materials},\n\ttype = {Journal Article},\n\tvolume = {37},\n\tyear = {1994}}\n\n@article{Eringen65,\n\tauthor = {Eringen, A.C. and Ingram, J.D.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Int J Eng Sci},\n\tpages = {197 - 212},\n\ttitle = {Continuum theory of chemically reacting media -- 1},\n\ttype = {Journal Article},\n\tvolume = {3},\n\tyear = {1965}}\n\n@book{Fung93,\n\taddress = {New York},\n\tauthor = {Fung, Y. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tedition = {2nd},\n\tisbn = {0387979476 (New York acid-free paper) 3540979476 (Berlin acid-free paper)},\n\tpages = {xviii, 568 p.},\n\tpublisher = {Springer-Verlag},\n\ttitle = {Biomechanics : mechanical properties of living tissues},\n\ttype = {Book},\n\turl = {http://www.loc.gov/catdir/enhancements/fy0814/92033749-t.html http://www.loc.gov/catdir/enhancements/fy0814/92033749-d.html},\n\tyear = {1993},\n\tbdsk-url-1 = {http://www.loc.gov/catdir/enhancements/fy0814/92033749-t.html%20http://www.loc.gov/catdir/enhancements/fy0814/92033749-d.html}}\n\n@article{Fung79,\n\tauthor = {Fung, Y. C. and Fronek, K. and Patitucci, P.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0002-9513 (Print) 0002-9513 (Linking)},\n\tjournal = {Am J Physiol},\n\tnumber = {5},\n\tpages = {H620-31},\n\ttitle = {Pseudoelasticity of arteries and the choice of its mathematical expression},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=495769},\n\tvolume = {237},\n\tyear = {1979},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=495769}}\n\n@article{Gee09,\n\tauthor = {Gee, M.W. and Dohrmann, C.R. and Key, S.W. and Wall, W.A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Int. J. Numer. Meth. Engng},\n\tnumber = {78},\n\tpages = {429-443},\n\ttitle = {A uniform nodal strain tetrahedron with isochoric stabilization},\n\ttype = {Journal Article},\n\tyear = {2009}}\n\n@article{Girard09,\n\tauthor = {Girard, M.J.A. and Downs, J.C. and Burgoyne, C. F.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Biomech Eng},\n\tnumber = {5},\n\tpages = {051011},\n\ttitle = {Peripapillary and posterior scleral mechanics - Part I: Development of an anisotropic hyperelastic constitutive model},\n\ttype = {Journal Article},\n\tvolume = {131},\n\tyear = {2009}}\n\n@article{Gouget12,\n\tauthor = {Gouget, C.L.M. and Girard, M.J.A. and Ethier, C.R.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Biomechanics And Modeling in Mechanobiolgy},\n\tnumber = {3-4},\n\tpages = {475-482},\n\ttitle = {A constrained von Mises distribution to describe fiber organization in thin soft tissues},\n\ttype = {Journal Article},\n\tvolume = {11},\n\tyear = {2012}}\n\n@article{Guccione93,\n\tauthor = {Guccione, J.M. and McCulloch, A.D.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J. Biomechanical Engineering},\n\tnumber = {no. 1},\n\tpages = {72-83},\n\ttitle = {Mechanics of active contraction in cardiac muscle: part I - constitutive relations for fiber stress that describe deactivation},\n\ttype = {Journal Article},\n\tvolume = {vol. 115},\n\tyear = {1993}}\n\n@article{Guccione91,\n\tauthor = {Guccione, J. M. and McCulloch, A. D. and Waldman, L. K.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Biomech Eng},\n\tnumber = {1},\n\tpages = {42-55.},\n\ttitle = {Passive material properties of intact ventricular myocardium determined from a cylindrical model},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/htbin-post/Entrez/query?db=m&form=6&dopt=r&uid=2020175},\n\tvolume = {113},\n\tyear = {1991},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/htbin-post/Entrez/query?db=m&form=6&dopt=r&uid=2020175}}\n\n@article{Holmes90,\n\tauthor = {Holmes, M. H. and Mow, V. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {0021-9290(90)90007-P [pii]},\n\tissn = {0021-9290 (Print) 0021-9290 (Linking)},\n\tjournal = {J Biomech},\n\tnumber = {11},\n\tpages = {1145-56},\n\ttitle = {The nonlinear characteristics of soft gels and hydrated connective tissues in ultrafiltration},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=2277049},\n\tvolume = {23},\n\tyear = {1990},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=2277049},\n\tbdsk-url-2 = {http://dx.doi.org/0021-9290(90)90007-P%20%5Bpii%5D}}\n\n@article{Horowitz88,\n\tauthor = {Horowitz, A. and Sheinman, I. and Lanir, Y. and Perl, M. and Sideman, S.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Journal of Biomechanical Engineering, Transactions of the ASME},\n\tnumber = {1},\n\tpages = {57-61},\n\ttitle = {Nonlinear Incompressilbe Finite Element for Simulating Loading of Cardiac Tissue- part I: two Dimensional Formulation for Thin Myocardial Strips},\n\ttype = {Journal Article},\n\tvolume = {110},\n\tyear = {1988}}\n\n@article{Hughes80,\n\tauthor = {Hughes, J.R. and Liu, Wing Kam},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tpages = {331-362},\n\ttitle = {Nonlinear Finite Element Analysis of Shells: Part I. Three-dimensional Shells},\n\ttype = {Journal Article},\n\tvolume = {26},\n\tyear = {1980}}\n\n@article{Humphrey90,\n\tauthor = {Humphrey, J. D. and Strumpf, R. K. and Yin, F. C. P.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Journal of Biomechanical Engineering, Transactions of the ASME},\n\tnumber = {3},\n\tpages = {333-339},\n\ttitle = {Determination of a constitutive relation for passive myocardium. I. A new functional form},\n\ttype = {Journal Article},\n\tvolume = {112},\n\tyear = {1990}}\n\n@article{Humphrey87,\n\tauthor = {Humphrey, J. D. and Yin, F. C. P.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Journal of Biomechanical Engineering, Transactions of the ASME},\n\tnumber = {4},\n\tpages = {298-304},\n\ttitle = {On constitutive Relations and Finite Deformations of Passive Cardiac Tissue: I. A Pseudostrain-Energy Function},\n\ttype = {Journal Article},\n\tvolume = {109},\n\tyear = {1987}}\n\n@article{Huyghe97,\n\tauthor = {Huyghe, J. M. and Janssen, J. D.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Int J Eng Sci},\n\tnumber = {8},\n\tpages = {793-802},\n\ttitle = {Quadriphasic mechanics of swelling incompressible porous media},\n\ttype = {Journal Article},\n\tvolume = {35},\n\tyear = {1997}}\n\n@article{Iatridis98,\n\tauthor = {Iatridis, J. C. and Setton, L. A. and Foster, R. J. and Rawlins, B. A. and Weidenbaum, M. and Mow, V. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {S0021-9290(98)00046-3 [pii]},\n\tissn = {0021-9290 (Print) 0021-9290 (Linking)},\n\tjournal = {J Biomech},\n\tnumber = {6},\n\tpages = {535-44},\n\ttitle = {Degeneration affects the anisotropic and nonlinear behaviors of human anulus fibrosus in compression},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=9755038},\n\tvolume = {31},\n\tyear = {1998},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=9755038},\n\tbdsk-url-2 = {http://dx.doi.org/S0021-9290(98)00046-3%20%5Bpii%5D}}\n\n@book{Katzir-Katchalsky65,\n\taddress = {Cambridge,},\n\tauthor = {Katzir-Katchalsky, Aharon and Curran, Peter F.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpages = {x, 248 p.},\n\tpublisher = {Harvard University Press},\n\ttitle = {Nonequilibrium thermodynamics in biophysics},\n\ttype = {Book},\n\tyear = {1965}}\n\n@article{Lanir83,\n\tauthor = {Lanir, Y.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {0021-9290(83)90041-6 [pii]},\n\tissn = {0021-9290 (Print) 0021-9290 (Linking)},\n\tjournal = {J Biomech},\n\tnumber = {1},\n\tpages = {1-12},\n\ttitle = {Constitutive equations for fibrous connective tissues},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=6833305},\n\tvolume = {16},\n\tyear = {1983},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=6833305},\n\tbdsk-url-2 = {http://dx.doi.org/0021-9290(83)90041-6%20%5Bpii%5D}}\n\n@article{Laurent63,\n\tauthor = {Laurent, Torvard C. and Killander, Johan},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Chromatogr},\n\tpages = {317-330},\n\ttitle = {A Theory of Gel Filtration and its Experimental Verification},\n\ttype = {Journal Article},\n\tvolume = {14},\n\tyear = {1963}}\n\n@book{Laursen02,\n\tauthor = {Laursen, Tod A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpublisher = {Springer},\n\ttitle = {Computational Contact and Impact Mechanics},\n\ttype = {Book},\n\tyear = {2002}}\n\n@article{Laursen95,\n\tauthor = {Laursen, T. A. and Maker, B. N.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {International Journal for Numerical Methods in Engineering},\n\tnumber = {21},\n\tpages = {3571-3590},\n\ttitle = {Augmented Lagrangian quasi-newton solver for constrained nonlinear finite element applications},\n\ttype = {Journal Article},\n\tvolume = {38},\n\tyear = {1995}}\n\n@article{Laursen93,\n\tauthor = {Laursen, T. A. and Simo, J. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {International Journal for Numerical Methods in Engineering},\n\tnumber = {20},\n\tpages = {3451-3485},\n\ttitle = {Continuum-based finite element formulation for the implicit solution of multibody, large deformation frictional contact problems},\n\ttype = {Journal Article},\n\tvolume = {36},\n\tyear = {1993}}\n\n@article{Maker95,\n\tauthor = {Maker, B. N.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {University of California, Lawrence Livermore Lab Rept},\n\tpages = {1-8},\n\ttitle = {Rigid bodies for metal forming analysis with NIKE3D},\n\ttype = {Journal Article},\n\tvolume = {UCRL-JC-119862},\n\tyear = {1995}}\n\n@article{Maker95a,\n\tauthor = {Maker, B. N.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Lawrence Livermore Lab Tech Rept},\n\ttitle = {NIKE3D: A nonlinear, implicit, three-dimensional finite element code for solid and structural mechanics},\n\ttype = {Journal Article},\n\tvolume = {UCRL-MA-105268},\n\tyear = {1995}}\n\n@book{Marsden94,\n\tauthor = {Marsden, J. E. and Hughes, T. J.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpublisher = {Dover Publications},\n\ttitle = {Mathematical Foundations of Elasticity},\n\ttype = {Book},\n\tyear = {1994}}\n\n@article{Matthies79,\n\tauthor = {Matthies, H. and Strang, G.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Intl J Num Meth Eng},\n\tpages = {1613-26},\n\ttitle = {The solution of nonlinear finite element equations},\n\ttype = {Journal Article},\n\tvolume = {14},\n\tyear = {1979}}\n\n@article{Mauck03,\n\tauthor = {Mauck, R. L. and Hung, C. T. and Ateshian, G. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0148-0731 (Print)},\n\tjournal = {J Biomech Eng},\n\tnumber = {5},\n\tpages = {602-14},\n\ttitle = {Modeling of neutral solute transport in a dynamically loaded porous permeable gel: implications for articular cartilage biosynthesis and tissue engineering},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=14618919},\n\tvolume = {125},\n\tyear = {2003},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=14618919}}\n\n@article{Mow80,\n\tauthor = {Mow, V.C. and Kuei, S.C. and Lai, W.M. and Armstrong, C.G.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J. Biomech. Eng.},\n\tpages = {73-84},\n\ttitle = {Biphasic creep and stress relaxation of articular cartilage in compression: Theory and experiments},\n\ttype = {Journal Article},\n\tvolume = {102},\n\tyear = {1980}}\n\n@misc{b,\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\ttype = {Online Multimedia}}\n\n@misc{a,\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\ttype = {Online Multimedia}}\n\n@article{Myers13,\n\tauthor = {Myers, K. and Ateshian, G. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tdoi = {10.1016/j.jmbbm.2013.03.003},\n\tissn = {1878-0180 (Electronic) 1878-0180 (Linking)},\n\tjournal = {J Mech Behav Biomed Mater},\n\ttitle = {Interstitial growth and remodeling of biological tissues: Tissue composition as state variables},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/pubmed/23562499},\n\tyear = {2013},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/pubmed/23562499},\n\tbdsk-url-2 = {http://dx.doi.org/10.1016/j.jmbbm.2013.03.003}}\n\n@article{Ogston61,\n\tauthor = {Ogston, A. G. and Phelps, C. F.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0264-6021 (Print)},\n\tjournal = {Biochem J},\n\tpages = {827-33},\n\ttitle = {The partition of solutes between buffer solutions and solutions containing hyaluronic acid},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=13730460},\n\tvolume = {78},\n\tyear = {1961},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=13730460}}\n\n@article{Puso98,\n\tauthor = {Puso, M. A. and Weiss, J. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Biomech Eng},\n\tnumber = {1},\n\tpages = {62-70},\n\ttitle = {Finite element implementation of anisotropic quasi-linear viscoelasticity using a discrete spectrum approximation},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=9675682},\n\tvolume = {120},\n\tyear = {1998},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=9675682}}\n\n@article{Quapp98,\n\tauthor = {Quapp, K. M. and Weiss, J. A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Biomech Eng},\n\tnumber = {6},\n\tpages = {757-63},\n\ttitle = {Material characterization of human medial collateral ligament},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=10412460},\n\tvolume = {120},\n\tyear = {1998},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=10412460}}\n\n@article{Simo92,\n\tauthor = {Simo, J.C. and Armero, F.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {International Journal for Numerical Methods in Engineering},\n\tpages = {1413-1419},\n\ttitle = {Geometrically Non-linear Enhanced Strain Mixed Methods and the Method of Incompatible Modes},\n\ttype = {Journal Article},\n\tvolume = {33},\n\tyear = {1992}}\n\n@article{Simo91,\n\tauthor = {Simo, J.C. and Taylor, R.L.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tpages = {273-310},\n\ttitle = {Quasi-incompressible finite elasticity in principal stretches: Continuum basis and numerical algorithms},\n\ttype = {Journal Article},\n\tvolume = {85},\n\tyear = {1991}}\n\n@book{Spencer84,\n\taddress = {New York},\n\tauthor = {Spencer, Anthony James Merril},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpublisher = {Springer-Verlag},\n\ttitle = {Continuum Theory of the Mechanics of Fibre-Reinforced Composites},\n\ttype = {Book},\n\tyear = {1984}}\n\n@article{Sun99,\n\tauthor = {Sun, D. N. and Gu, W. Y. and Guo, X. E. and Lai, W. M. and Mow, V. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Int J Numer Meth Eng},\n\tnumber = {10},\n\tpages = {1375-402},\n\ttitle = {A mixed finite element formulation of triphasic mechano-electrochemical theory for charged, hydrated biological soft tissues},\n\ttype = {Journal Article},\n\turl = {http://dx.doi.org/10.1002/(SICI)1097-0207(19990810)45:10<1375::AID-NME635>3.0.CO;2-7},\n\tvolume = {45},\n\tyear = {1999},\n\tbdsk-url-1 = {http://dx.doi.org/10.1002/(SICI)1097-0207(19990810)45:10%3C1375::AID-NME635%3E3.0.CO;2-7}}\n\n@book{Tinoco-Jr.95,\n\tauthor = {Tinoco Jr., I. and Sauer, K. and Wang, J. C.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpublisher = {Prentice Hall},\n\ttitle = {Physical chemistry : principles and applications in biological sciences},\n\ttype = {Book},\n\tyear = {1995}}\n\n@book{Truesdell60,\n\taddress = {Heidelberg},\n\tauthor = {Truesdell, C. and Toupin, R.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpublisher = {Springer},\n\tseries = {Handbuch der physik},\n\ttitle = {The classical field theories},\n\ttype = {Book},\n\tvolume = {III/1},\n\tyear = {1960}}\n\n@article{Un06,\n\tauthor = {Un, K. and Spilker, R. L.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0148-0731 (Print)},\n\tjournal = {J Biomech Eng},\n\tnumber = {6},\n\tpages = {934-42},\n\ttitle = {A penetration-based finite element method for hyperelastic 3D biphasic tissues in contact. Part II: finite element simulations},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17154696},\n\tvolume = {128},\n\tyear = {2006},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17154696}}\n\n@article{Un06a,\n\tauthor = {Un, K. and Spilker, R. L.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0148-0731 (Print)},\n\tjournal = {J Biomech Eng},\n\tnumber = {1},\n\tpages = {124-30},\n\ttitle = {A penetration-based finite element method for hyperelastic 3D biphasic tissues in contact: Part 1--Derivation of contact boundary conditions},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=16532625},\n\tvolume = {128},\n\tyear = {2006},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=16532625}}\n\n@article{Veress06,\n\tauthor = {Veress, A. I. and Segars, W. P. and Weiss, J. A. and Tsui, B. M. and Gullberg, G. T.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tissn = {0278-0062 (Print)},\n\tjournal = {IEEE Trans Med Imaging},\n\tnumber = {12},\n\tpages = {1604-16},\n\ttitle = {Normal and pathological NCAT image and phantom data based on physiologically realistic left ventricle finite-element models},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17167995},\n\tvolume = {25},\n\tyear = {2006},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=17167995}}\n\n@article{Veronda70,\n\tauthor = {Veronda, D.R. and Westmann, R.A.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J. Biomechanics},\n\tpages = {111-124},\n\ttitle = {Mechanical Characterization of Skin - Finite Deformations},\n\ttype = {Journal Article},\n\tvolume = {Vol. 3},\n\tyear = {1970}}\n\n@article{Wayne91,\n\tauthor = {Wayne, J. S. and Woo, S. L. and Kwan, M. K.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {J Biomech Eng},\n\tnumber = {4},\n\tpages = {397-403},\n\ttitle = {Application of the u-p finite element method to the study of articular cartilage},\n\ttype = {Journal Article},\n\turl = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=1762436},\n\tvolume = {113},\n\tyear = {1991},\n\tbdsk-url-1 = {http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=1762436}}\n\n@article{Weiss03,\n\tauthor = {Weiss, J.A. and Gardiner, J.C. and Maker, B.N.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Computer Methods in Biomechanics and Biomedical Engineering},\n\ttitle = {An iterative update algorithm to apply initial stretch to finite element models of ligaments},\n\ttype = {Journal Article},\n\tvolume = {In Review},\n\tyear = {2003}}\n\n@article{Weiss96,\n\tauthor = {Weiss, J.A. and Maker, B.N. and Govindjee, S.},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tjournal = {Computer Methods in Applications of Mechanics and Engineering},\n\tpages = {107-128},\n\ttitle = {Finite element implementation of incompressible, transversely isotropic hyperelasticity},\n\ttype = {Journal Article},\n\tvolume = {135},\n\tyear = {1996}}\n\n@inproceedings{Weiss,\n\tauthor = {Weiss, J.A. and Maker, B.N. and Schauer, D.A.},\n\tbooktitle = {ASME Summer Bioengineering Conference},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpages = {105-106},\n\ttitle = {Treatment of initial stress in hyperelastic finite element models of soft tissues},\n\ttype = {Conference Proceedings},\n\tvolume = {BED-29}}\n\n@inproceedings{Weissa,\n\tauthor = {Weiss, J.A. and Schauer, D.A. and Gardiner, J.C.},\n\tbooktitle = {ASME Winter Annual Meeting},\n\tdate-added = {2016-12-22 02:50:22 +0000},\n\tdate-modified = {2016-12-22 02:50:24 +0000},\n\tpages = {347-348},\n\ttitle = {Modeling contact in biological joints using penalty and augmented Lagrangian methods},\n\ttype = {Conference Proceedings},\n\tvolume = {BED-33}}\n\n@article{Weiss2002,\n\tauthor = {Weiss, J.A. and Gardiner, J.C. and Bonifasi-Lista C.},\n\tjournal = {Journal of Biomechanics},\n\tnumber = {7},\n\tpages = {943-950},\n\ttitle = {Ligament material behavior is nonlinear, viscoelastic and rate-independent under shear loading},\n\tvolume = {35},\n\tyear = {2002}}\n\n@article{Swedberg2014,\n\tauthor = {Swedberg, A.M. and Reese, S.P. and Maas, S.A. and Ellis, B. J. and Weiss, J.A.},\n\tjournal = {Journal of Biomechanics},\n\tnumber = {12},\n\tpages = {3201-3209},\n\ttitle = {Continuum description of the Poisson's ratio of ligament and tendon under finite deformation.},\n\tvolume = {47},\n\tyear = {2014}}\n\n@article{Ateshian2012,\n\tauthor = {Ateshian, G.A and Morrison B. and Holmes, J.W. and Hung, C. T.},\n\tjournal = {Mechanics Research Communications},\n\tpages = {118-125},\n\ttitle = {Mechanics of cell growth},\n\tvolume = {42},\n\tyear = {2012}}\n\n@article{Hou16,\n\tauthor = {Hou, C and Ateshian, G.A.},\n\tdate-modified = {2020-07-30 18:51:32 -0400},\n\tjournal = {Computer Methods in Biomechanics and Biomedical Engineering},\n\tnumber = {8},\n\tpages = {883-893},\n\ttitle = {A Gauss-Kronrod-Trapezoidal integration scheme for modeling biological tissues with continuous fiber distributions.},\n\tvolume = {19},\n\tyear = {2016}}\n\n@article{Ateshian2011,\n\tauthor = {Ateshian, G.A. and Albro, M. B. and Maas, S.A. and Weiss, J.A.},\n\tjournal = {Journal of Biomechanical Engineering},\n\tnumber = {8},\n\tpages = {1005-1017},\n\ttitle = {Finite element implementation of mechanochemical phenomena in neutral deformable porous media under finite deformation.},\n\tvolume = {133},\n\tyear = {2011}}\n\n@article{Maas2016,\n\tauthor = {Maas, S.A. and Erdemir, A. and Halloran, J.P. and Weiss, J.A.},\n\tjournal = {Journal of Biomechanical Behavior of Biomedical Materials},\n\tpages = {499-510},\n\ttitle = {A general framework for applications of prestrain to computational models of biological materials.},\n\tvolume = {61},\n\tyear = {2016}}\n\n@article{BALZANI2012139,\n\tauthor = {Daniel Balzani and Sarah Brinkhues and Gerhard A. Holzapfel},\n\tdoi = {https://doi.org/10.1016/j.cma.2011.11.015},\n\tissn = {0045-7825},\n\tjournal = {Computer Methods in Applied Mechanics and Engineering},\n\tpages = {139-151},\n\ttitle = {Constitutive framework for the modeling of damage in collagenous soft tissues with application to arterial walls},\n\turl = {https://www.sciencedirect.com/science/article/pii/S0045782511003616},\n\tvolume = {213-216},\n\tyear = {2012},\n\tbdsk-url-1 = {https://www.sciencedirect.com/science/article/pii/S0045782511003616},\n\tbdsk-url-2 = {https://doi.org/10.1016/j.cma.2011.11.015}}\n\n@article{Birzle2019,\n\tauthor = {Birzle, Anna M. and Martin, Christian and Uhlig, Stefan and Wall, Wolfgang A.},\n\tdate-modified = {2022-07-20 20:08:15 -0400},\n\tjournal = {Journal of the Mechanical Behavior of Biomedical Materials},\n\tpages = {126--143},\n\ttitle = {A coupled approach for identification of nonlinear and compressible material models for soft tissue based on different experimental setups - exemplified and detailed for Lung Parenchyma},\n\tvolume = {94},\n\tyear = {2019},\n\tbdsk-url-1 = {https://doi.org/10.1016/j.jmbbm.2019.02.019}}\n\n@article{ALLAN2022105342,\n\tabstract = {Many painful and physically debilitating conditions involve sub-failure mechanical damage to seemingly intact connective tissues such as tendons and ligaments. We found that the amount of denatured collagen in rat tail tendon (RTT) fascicles increased over experiments of cyclic loading to a constant load level (creep cyclic fatigue) with fluorescently tagged collagen hybridizing peptides (CHPs) that bind to denatured collagen. To better understand tendon sub-failure damage progression, computational modeling of tendon materials via finite element analysis in FEBio has been conducted. The objective of this project was to develop, implement, and test the ability of a new continuum damage mechanics (CDM) model in FEBio to represent the sub-failure damage behavior seen in our RTT fascicle creep cyclic fatigue experimental data. There appeared to be two distinct mechanisms responsible for the creep cyclic fatigue softening behavior of RTT fascicles over the number of cycles to failure: the preconditioning effect and overall collagen damage. In our finite element (FE) models, the RTT fascicle undamaged elastic constitutive material was composed of a matrix and fibers described by the Coupled Veronda-Westmann and exponential-linear materials. This undamaged elastic material was convolved with a modified CDM model adapted from Balzani et al., in 2012. The novelty of the Balzani damage model is the inclusion of two interrelated mechanisms described as continuous and discontinuous damage. The continuous damage formulation calculates damage accumulation during the loading and reloading of each new cycle, while the discontinuous damage approach accumulates damage from the maximum strain over the loading history to the current time. We modified the Balzani damage model formulations to represent exponential and sigmoidal increases in damage marked by the preconditioning effect and collagen damage in RTT fascicles as functions of continuous and discontinuous damage. The original Balzani damage model was first verified, then the modified CDM model was implemented into FEBio and used to reproduce the sample specific experimental creep cyclic fatigue stress-strain data as well as predict incremental cyclic fatigue. The resulting model will be useful for future experimental and computational studies of damage mechanics to understand tendon pathologies.},\n\tauthor = {Alexandra N. Allan and Jared L. Zitnay and Steve A. Maas and Jeffrey A. Weiss},\n\tdoi = {https://doi.org/10.1016/j.jmbbm.2022.105342},\n\tissn = {1751-6161},\n\tjournal = {Journal of the Mechanical Behavior of Biomedical Materials},\n\tkeywords = {Sub-failure, Damage, Tendon, FEBio, Collagen, Continuum damage mechanics},\n\tpages = {105342},\n\ttitle = {Development of a continuum damage model to predict accumulation of sub-failure damage in tendons},\n\turl = {https://www.sciencedirect.com/science/article/pii/S1751616122002533},\n\tvolume = {135},\n\tyear = {2022},\n\tbdsk-url-1 = {https://www.sciencedirect.com/science/article/pii/S1751616122002533},\n\tbdsk-url-2 = {https://doi.org/10.1016/j.jmbbm.2022.105342}}\n\n@article{rauff_nonparametric_2022,\n\tabstract = {Many biological tissues contain an underlying fibrous microstructure that is optimized to suit a physiological function. The fiber architecture dictates physical characteristics such as stiffness, diffusivity, and electrical conduction. Abnormal deviations of fiber architecture are often associated with disease. Thus, it is useful to characterize fiber network organization from image data in order to better understand pathological mechanisms. We devised a method to quantify distributions of fiber orientations based on the Fourier transform and the Qball algorithm from diffusion MRI. The Fourier transform was used to decompose images into directional components, while the Qball algorithm efficiently converted the directional data from the frequency domain to the orientation domain. The representation in the orientation domain does not require any particular functional representation, and thus the method is nonparametric. The algorithm was verified to demonstrate its reliability and used on datasets from microscopy to show its applicability. This method increases the ability to extract information of microstructural fiber organization from experimental data that will enhance our understanding of structure-function relationships and enable accurate representation of material anisotropy in biological tissues.},\n\tauthor = {Rauff, Adam and Timmins, Lucas H. and Whitaker, Ross T. and Weiss, Jeffrey A.},\n\tdoi = {10.1109/TMI.2021.3115716},\n\tfile = {IEEE Xplore Abstract Record:/home/mherron/Zotero/storage/E338ZCUL/9548066.html:text/html;PubMed Central Full Text PDF:/home/mherron/Zotero/storage/JC837KWX/Rauff et al. - 2022 - A Nonparametric Approach for Estimating Three-Dimensional Fiber Orientation Distribution Functions (.pdf:application/pdf},\n\tissn = {1558-254X},\n\tjournal = {IEEE Transactions on Medical Imaging},\n\tkeywords = {Fibers, Fourier transform, Fourier transforms, Image resolution, Magnetic resonance imaging, nonparametric distributions, Optical fiber dispersion, Optical fiber networks, Optical fiber polarization, orientation distribution function (ODF), Three-dimensional displays},\n\tmonth = feb,\n\tnote = {Conference Name: IEEE Transactions on Medical Imaging},\n\tnumber = {2},\n\tpages = {446--455},\n\ttitle = {A {Nonparametric} {Approach} for {Estimating} {Three}-{Dimensional} {Fiber} {Orientation} {Distribution} {Functions} ({ODFs}) in {Fibrous} {Materials}},\n\turl = {https://ieeexplore.ieee.org/document/9548066},\n\turldate = {2025-01-10},\n\tvolume = {41},\n\tyear = {2022},\n\tbdsk-url-1 = {https://ieeexplore.ieee.org/document/9548066},\n\tbdsk-url-2 = {https://doi.org/10.1109/TMI.2021.3115716}}\n\n@article{rauff_algorithmic_2024,\n\tabstract = {Biological tissues and biomaterials routinely feature a fibrous microstructure that contributes to physical and mechanical properties while influencing cellular guidance, organization and extracellular matrix (ECM) production. Specialized three-dimensional (3D) imaging techniques can visualize fibrillar structure and orientation, and previously we developed a nonparametric approach to extract orientation distribution functions (ODFs) directly from 3D image data [1]. In this work, we expanded our previous approach to provide a complete algorithmic and software framework to characterize inhomogeneous ODFs in image data and use ODFs to model the physics of materials with the finite element method. We characterized inhomogeneity using image subdomains and specialized interpolation methods, and we developed methods to incorporate ODFs directly into constitutive models. To facilitate its adoption by the biomechanics and biophysics communities, we developed a unified software framework in FEBio Studio (www.febio.org). This included new interpolation methods to spatially map the ODFs onto finite element meshes and an approach to downsample ODFs for efficient numerical calculations. The software provides the option to fit ODFs to parametric distributions, and scalar metrics provide means to assess goodness of fit. We evaluated the utility and accuracy of the algorithms and implementation using representative 3D image datasets. Our results demonstrated that utilizing the true measured ODFs provide a more accurate and spatially resolved representation of fiber ODFs and the resulting predicted mechanical response when compared with parametric approaches to approximating the true ODFs. This research provides a powerful, interactive software framework to extract and represent the inhomogeneous anisotropic characteristics of fibrous tissues directly from image data, and to incorporate them into biomechanics and biophysics simulations using the finite element method.\nStatement of Significance\nBiological tissues and biomaterials routinely feature a fibrous microstructure that contributes to physical and mechanical properties while influencing cellular guidance, organization and extracellular matrix (ECM) production. In this study, we developed a complete algorithmic and software framework to characterize inhomogeneous orientation distribution functions (ODFs) directly from biomedical image data and apply the ODFs to model the physics of biological materials. We characterized inhomogeneity using image subdomains and specialized interpolation methods, and we developed methods to incorporate ODFs directly into constitutive models. We developed a unified software framework in FEBio Studio (www.febio.org) to accommodate its adoption by the biomechanics and biophysics communities. The result is a powerful, interactive software framework to extract and represent inhomogeneous, anisotropic characteristics directly from image data, and incorporate them into biomechanics and biophysics simulations.},\n\tauthor = {Rauff, Adam and Herron, Michael R. and Maas, Steve A. and Weiss, Jeffrey A.},\n\tdoi = {10.1016/j.actbio.2024.11.043},\n\tfile = {ScienceDirect Snapshot:/home/mherron/Zotero/storage/Y72ZR633/S1742706124007074.html:text/html},\n\tissn = {1742-7061},\n\tjournal = {Acta Biomaterialia},\n\tkeywords = {Anisotropy, FEBio, Finite element analysis, Image analysis, Multiscale modeling, Orientation distribution function},\n\tmonth = nov,\n\ttitle = {An algorithmic and software framework to incorporate orientation distribution functions in finite element simulations for biomechanics and biophysics},\n\turl = {https://www.sciencedirect.com/science/article/pii/S1742706124007074},\n\turldate = {2025-01-10},\n\tyear = {2024},\n\tbdsk-url-1 = {https://www.sciencedirect.com/science/article/pii/S1742706124007074},\n\tbdsk-url-2 = {https://doi.org/10.1016/j.actbio.2024.11.043}}\n"
  },
  {
    "path": "Documentation/FEBio_EULA_3.rtf",
    "content": "{\\rtf1\\ansi\\deff3\\adeflang1025\n{\\fonttbl{\\f0\\froman\\fprq2\\fcharset0 Times New Roman;}{\\f1\\froman\\fprq2\\fcharset2 Symbol;}{\\f2\\fswiss\\fprq2\\fcharset0 Arial;}{\\f3\\froman\\fprq2\\fcharset0 Liberation Serif{\\*\\falt Times New Roman};}{\\f4\\fswiss\\fprq2\\fcharset0 Liberation Sans{\\*\\falt Arial};}{\\f5\\froman\\fprq2\\fcharset0 Times New Roman;}{\\f6\\froman\\fprq2\\fcharset0 Tahoma;}{\\f7\\froman\\fprq2\\fcharset0 Segoe UI;}{\\f8\\froman\\fprq2\\fcharset0 Cambria;}{\\f9\\froman\\fprq2\\fcharset0 Arial;}{\\f10\\fnil\\fprq2\\fcharset0 Noto Sans CJK SC;}{\\f11\\fnil\\fprq2\\fcharset0 Segoe UI;}{\\f12\\fnil\\fprq2\\fcharset0 Times New Roman;}{\\f13\\fnil\\fprq2\\fcharset0 Lohit Devanagari;}{\\f14\\fnil\\fprq0\\fcharset128 Lohit Devanagari;}{\\f15\\fnil\\fprq2\\fcharset0 Tahoma;}{\\f16\\fnil\\fprq2\\fcharset0 Cambria;}{\\f17\\fnil\\fprq2\\fcharset0 Arial;}}\n{\\colortbl;\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green255\\blue255;\\red0\\green255\\blue0;\\red255\\green0\\blue255;\\red255\\green0\\blue0;\\red255\\green255\\blue0;\\red255\\green255\\blue255;\\red0\\green0\\blue128;\\red0\\green128\\blue128;\\red0\\green128\\blue0;\\red128\\green0\\blue128;\\red128\\green0\\blue0;\\red128\\green128\\blue0;\\red128\\green128\\blue128;\\red192\\green192\\blue192;\\red51\\green51\\blue51;}\n{\\stylesheet{\\s0\\snext0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033 Normal;}\n{\\*\\cs15\\snext15 Default Paragraph Font;}\n{\\*\\cs16\\sbasedon15\\snext16\\dbch\\af12 apple-converted-space;}\n{\\*\\cs17\\sbasedon15\\snext17\\dbch\\af12\\fs16 annotation reference;}\n{\\*\\cs18\\sbasedon15\\snext18\\dbch\\af12 Comment Text Char;}\n{\\*\\cs19\\sbasedon18\\snext19\\b\\dbch\\af12\\ab Comment Subject Char;}\n{\\*\\cs20\\sbasedon15\\snext20\\dbch\\af11\\afs18\\loch\\f7\\fs18 Balloon Text Char;}\n{\\*\\cs21\\sbasedon15\\snext21\\b\\kerning1\\dbch\\af12\\dbch\\af16\\afs32\\ab\\loch\\f8\\fs32 Title Char;}\n{\\*\\cs22\\snext22\\dbch\\af12 ListLabel 1;}\n{\\*\\cs23\\snext23\\dbch\\af12 ListLabel 2;}\n{\\*\\cs24\\snext24\\dbch\\af12 ListLabel 3;}\n{\\*\\cs25\\snext25\\dbch\\af12 ListLabel 4;}\n{\\*\\cs26\\snext26\\dbch\\af12 ListLabel 5;}\n{\\*\\cs27\\snext27\\dbch\\af12 ListLabel 6;}\n{\\*\\cs28\\snext28\\dbch\\af12 ListLabel 7;}\n{\\*\\cs29\\snext29\\dbch\\af12 ListLabel 8;}\n{\\*\\cs30\\snext30\\dbch\\af12 ListLabel 9;}\n{\\*\\cs31\\snext31\\dbch\\af12 ListLabel 10;}\n{\\*\\cs32\\snext32\\dbch\\af12 ListLabel 11;}\n{\\*\\cs33\\snext33\\dbch\\af12 ListLabel 12;}\n{\\*\\cs34\\snext34\\dbch\\af12 ListLabel 13;}\n{\\*\\cs35\\snext35\\dbch\\af12 ListLabel 14;}\n{\\*\\cs36\\snext36\\dbch\\af12 ListLabel 15;}\n{\\*\\cs37\\snext37\\dbch\\af12 ListLabel 16;}\n{\\*\\cs38\\snext38\\dbch\\af12 ListLabel 17;}\n{\\*\\cs39\\snext39\\dbch\\af12 ListLabel 18;}\n{\\s40\\sbasedon0\\snext41\\ql\\widctlpar\\faauto\\sb240\\sa120\\keepn\\ltrpar\\dbch\\af10\\langfe1033\\dbch\\af13\\afs28\\alang1025\\loch\\f4\\fs28\\lang1033 Heading;}\n{\\s41\\sbasedon0\\snext41\\sl276\\slmult1\\ql\\widctlpar\\faauto\\sb0\\sa140\\ltrpar\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\loch\\f5\\fs24\\lang1033 Text Body;}\n{\\s42\\sbasedon41\\snext42\\sl276\\slmult1\\ql\\widctlpar\\faauto\\sb0\\sa140\\ltrpar\\dbch\\af11\\langfe1033\\dbch\\af14\\afs24\\alang1025\\loch\\f5\\fs24\\lang1033 List;}\n{\\s43\\sbasedon0\\snext43\\ql\\widctlpar\\faauto\\sb120\\sa120\\noline\\ltrpar\\i\\dbch\\af11\\langfe1033\\dbch\\af14\\afs24\\alang1025\\ai\\loch\\f5\\fs24\\lang1033 Caption;}\n{\\s44\\sbasedon0\\snext44\\ql\\widctlpar\\faauto\\noline\\ltrpar\\dbch\\af11\\langfe1033\\dbch\\af14\\afs24\\alang1025\\loch\\f5\\fs24\\lang1033 Index;}\n{\\s45\\snext45\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs20\\alang1025\\cf0\\kerning1\\loch\\f5\\fs20\\lang1033 DocumentMap;}\n{\\s46\\sbasedon0\\snext46\\ql\\widctlpar\\faauto\\ltrpar\\dbch\\af11\\langfe1033\\dbch\\af12\\afs20\\alang1025\\loch\\f5\\fs20\\lang1033 annotation text;}\n{\\s47\\sbasedon46\\snext47\\ql\\widctlpar\\faauto\\ltrpar\\b\\dbch\\af11\\langfe1033\\dbch\\af12\\afs20\\alang1025\\ab\\loch\\f5\\fs20\\lang1033 annotation subject;}\n{\\s48\\sbasedon0\\snext48\\ql\\widctlpar\\faauto\\ltrpar\\dbch\\af11\\langfe1033\\dbch\\af15\\afs16\\alang1025\\loch\\f6\\fs16\\lang1033 Balloon Text;}\n{\\s49\\snext49\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\kerning1\\loch\\f5\\fs24\\lang1033 Default;}\n{\\s50\\sbasedon49\\snext50\\ql\\widctlpar\\faauto\\ltrpar\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\loch\\f5\\fs24\\lang1033 Title;}\n{\\s51\\snext51\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033 Revision;}\n}{\\*\\revtbl {Unknown;}{steve maas;}{Unknown Author;}}\n{\\*\\generator LibreOffice/6.0.7.3$Linux_X86_64 LibreOffice_project/00m0$Build-3}{\\info{\\*\\company University of Utah}{\\title 1}{\\author Technology Commercialization Office}{\\creatim\\yr2019\\mo7\\dy8\\hr15\\min55}{\\revtim\\yr2020\\mo8\\dy18\\hr13\\min18}{\\printim\\yr0\\mo0\\dy0\\hr0\\min0}}{\\*\\userprops{\\propname Operator}\\proptype30{\\staticval steve maas}}\\deftab720\\deftab720\\deftab720\\deftab720\n\\viewscale170\\revisions\n{\\*\\pgdsctbl\n{\\pgdsc0\\pgdscuse451\\pgwsxn12240\\pghsxn15840\\marglsxn1800\\margrsxn1800\\margtsxn1440\\margbsxn1440\\pgdscnxt0 Default Style;}}\n\\formshade{\\*\\pgdscno0}\\paperh15840\\paperw12240\\margl1800\\margr1800\\margt1440\\margb1440\\sectd\\sbknone\\sectunlocked1\\pgndec\\pgwsxn12240\\pghsxn15840\\marglsxn1800\\margrsxn1800\\margtsxn1440\\margbsxn1440\\ftnbj\\ftnstart1\\ftnrstcont\\ftnnar\\aenddoc\\aftnrstcont\\aftnstart1\\aftnnrlc\\htmautsp\n{\\*\\ftnsep\\chftnsep}\\pgndec\\pard\\plain \\s50\\ql\\widctlpar\\faauto\\ltrpar\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\loch\\f5\\fs24\\lang1033\\qc\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\cf1\\ul\\ulc0\\b\\langfe1033\\dbch\\af17\\afs18\\alang1025\\ab\\rtlch \\ltrch\\loch\\fs18\\lang1033\\loch\\f9\\hich\\af9\nFEBIO SOFTWARE LICENSE}\n\\par \\pard\\plain \\s50\\ql\\widctlpar\\faauto\\ltrpar\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\loch\\f5\\fs24\\lang1033\\qc\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\cf1\\ul\\ulc0\\b\\langfe1033\\dbch\\af17\\afs18\\alang1025\\ab\\rtlch \\ltrch\\loch\\fs18\\lang1033\\loch\\f9\\hich\\af9\nVersion }{\\deleted\\revauthdel1\\revdttmdel1735780940 \\cf1\\ul\\ulc0\\b\\langfe1033\\dbch\\af17\\afs18\\alang1025\\ab\\rtlch \\ltrch\\loch\\fs18\\lang1033\\loch\\f9\\hich\\af9\n2}{\\revised\\revauth1\\revdttm1735780940 \\cf1\\ul\\ulc0\\b\\langfe1033\\dbch\\af17\\afs18\\alang1025\\ab\\rtlch \\ltrch\\loch\\fs18\\lang1033\\loch\\f9\\hich\\af9\n3}{\\cf1\\ul\\ulc0\\b\\langfe1033\\dbch\\af17\\afs18\\alang1025\\ab\\rtlch \\ltrch\\loch\\fs18\\lang1033\\loch\\f9\\hich\\af9\n.0 }\n\\par \\pard\\plain \\s49\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\kerning1\\loch\\f5\\fs24\\lang1033\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\cf1\\langfe1033\\dbch\\af12\\afs24\\alang1025\\rtlch \\ltrch\\loch\\fs24\\lang1033\n\n\\par \\pard\\plain \\s49\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\kerning1\\loch\\f5\\fs24\\lang1033\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\cf1\\langfe1033\\dbch\\af12\\afs24\\alang1025\\rtlch \\ltrch\\loch\\fs24\\lang1033\n\n\\par \\pard\\plain \\s49\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\cf1\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nTHIS AGREEMENT, between you the licensee, hereinafter referred to as }{\\deleted\\revauthdel2\\revdttmdel1200132945 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\u8220\\'93}{\\revised\\revauth2\\revdttm1200132945 \\cf1\\dbch\\af11\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\"}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nRecipient,}{\\deleted\\revauthdel2\\revdttmdel1200132946 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\u8221\\'94}{\\revised\\revauth2\\revdttm1200132946 \\cf1\\dbch\\af11\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\"}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n and }{\\cf1\\ul\\ulc0\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nthe University of Utah}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n, having an address at the }{\\cf1\\ul\\ulc0\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUniversity of Utah, Technology Commercialization Office, 615 Arapeen Drive, Suite 310, Salt Lake City, Utah 84108}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n, hereinafter referred to as }{\\deleted\\revauthdel2\\revdttmdel1200132945 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\u8220\\'93}{\\revised\\revauth2\\revdttm1200132945 \\cf1\\dbch\\af11\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\"}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUniversity,}{\\deleted\\revauthdel2\\revdttmdel1200132946 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\u8221\\'94}{\\revised\\revauth2\\revdttm1200132946 \\cf1\\dbch\\af11\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\"}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n shall govern the conditions of disclosure by University to Recipient of certain software (SOFTWARE) named: }{\\cf1\\b\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nFEBio}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n, developed by Steve Maas and Jeff Weiss}{\\deleted\\revauthdel1\\revdttmdel1735780934 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n,}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n of the University of Utah}{\\revised\\revauth1\\revdttm1735780934 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n, and }{\\revised\\revauth1\\revdttm1735780935 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nGerard Ateshian, of Columbia University}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n. SOFTWARE, as used herein, includes all such software actually provided to Recipient, including, as the case may be, }{\\deleted\\revauthdel1\\revdttmdel1735780936 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nsource code or }{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nany binaries or executables thereof, plus any software derived directly therefrom}{\\revised\\revauth1\\revdttm1735780936 \\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n, but excludes the source code and the Software Development Kit (SDK)}{\\cf1\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n. }\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n1.\\~ LICENSE.\\~ University grants to you a non-exclusive, non-transferable right to use the SOFTWARE in a single installation on a single machine in a single geographic location}{\\deleted\\revauthdel1\\revdttmdel1735780936 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n for Non-Commercial Use}{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n.}{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\~ Recipient agrees not to use such SOFTWARE for any Commercial Purpose. }\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the software for any Commercial Purpose means use of Software by you for direct or indirect financial, commercial or strategic gain or advantage.  Examples of a Commercial Purpose include, but are not limited to:}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nIntegrating the SOFTWARE with other software for sale as a bundled product.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the SOFTWARE for a fee-based service or subscription.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the SOFTWARE in the creation of commercial products.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the SOFTWARE for research or educational purposes, for or on behalf of a commercial or for-profit entity or project, whether or not such use is a commercial use.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the software by an individual employed at an academic or non-profit research institution to perform contract work funded by a commercial entity.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the software by an individual in the course of consulting or contracting activities for which you are compensated.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nExamples of Non-Commercial Use include, but are not limited to:}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the SOFTWARE by an individual employed by an academic or non-profit research institution for non-commercial research.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nUse of the SOFTWARE by an individual employed by an academic or non-profit research institution for teaching or learning.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nPersonal use of the SOFTWARE for non-commercial research, learning or entertainment, not leading to the creation of a commercial product.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\revised\\revauth1\\revdttm1735780937 \\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nIt is further agreed that the furnishing of SOFTWARE to Recipient shall not constitute any grant of license to Recipient under any legal rights now or hereinafter held by University.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n2.\\~ TERMINATION.\\~ The use of the SOFTWARE by Recipient is conditioned upon Recipient\\u8217\\'92s compliance with the terms of this Agreement.\\~ When this Agreement terminates, Recipient is required to remove all copies of the SOFTWARE and discontinue all use.\\~ Recipient agrees that Recipient will only copy the SOFTWARE into any machine readable or printed form as necessary to use it in accordance with this Agreement or for backup purposes in support of Recipients use of the SOFTWARE. This Agreement is effective until terminated. Recipient may terminate it at any point by destroying the SOFTWARE together with all copies of the SOFTWARE.\\~ Also, University has the option to terminate if Recipient fails to comply with any term or condition of this Agreement or upon 30 days written notice.\\~ Recipient agrees upon such termination to destroy the SOFTWARE together with all copies of the SOFTWARE.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n3.\\~ COPYRIGHT.\\~ The SOFTWARE is protected by United States copyright law and international treaty provisions.\\~ Recipient acknowledges that no title to the intellectual property in the SOFTWARE is transferred to Recipient.\\~ Recipient further acknowledge that title and full ownership rights to the SOFTWARE will remain the exclusive property of University or its suppliers, and Recipient will not acquire any rights to the SOFTWARE except as expressly set forth in this Agreement.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n4.\\~ LIMITATIONS.\\~ }{\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nRecipient may modify the SOFTWARE or create derivative works based upon the SOFTWARE for their own internal or personal use. }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\nRecipient may not rent, lease, distribute, transfer or sublicense the SOFTWARE or any derivative works thereof to any third parties, without first negotiating in good faith with University a separate license agreement.\\~ Recipient may not export the SOFTWARE or any derivative works thereof into any country prohibited by the United States Export Administration Act and the regulations there under.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n5.\\~ LIMITED WARRANTIES.\\~ University warrants that the media on which the SOFTWARE is furnished will be free from defects in materials and workmanship under normal use.}{\\cs16\\dbch\\af12\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\~}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n6.\\~ DISCLAIMER OF WARRANTY: EXCEPT AS SET FORTH HEREIN, SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND.\\~ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, UNIVERISTY FURTHER DISCLAIMS ALL EXPRESSED AND IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATIONS ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.\\~ THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS AGREEMENT.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n7.\\~ GOVERNING LAW.\\~ This Agreement will be governed by the internal laws of the State of Utah without regard to conflict of laws.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n8.\\~ ENTIRE AGREEMENT.\\~ This is the entire agreement between Recipient and University, which supersedes any prior agreement or understanding, whether written, or oral, relating to the subject matter of this license.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\deleted\\revauthdel1\\revdttmdel1735780940 \\cs16\\dbch\\af12\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n\\~}{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n9.\\~ NO LIABILITY FOR CONSEQUENTIAL DAMAGES: IN NO EVENT SHALL UNIVERSITY BE LIABLE TO RECIPIENT FOR ANY CONSEQUENTIAL, SPECIAL, INCIDENTAL OR INDIRECT DAMAGES OF ANY KIND ARISING OUT OF THE DELIVERY, PERFORMANCE OR USE OF THE SOFTWARE, EVEN IF UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar{\\rtlch \\ltrch\\loch\n\\line \\line }{\\cf17\\chcbpat8\\langfe1033\\dbch\\af17\\afs18\\alang1025\\rtlch \\ltrch\\loch\\fs16\\lang1033\\loch\\f9\\hich\\af9\n10.\\~ BASIS OF BARGAIN.\\~ The limited warranty, exclusive remedies, and limited liability set forth above are fundamental elements of the basis of the agreement between University and Recipient.\\~ University would not be able to provide the SOFTWARE on an economic basis without such limitations.}\n\\par \\pard\\plain \\s0\\ql\\widctlpar\\faauto\\ltrpar\\hyphpar0\\dbch\\af11\\langfe1033\\dbch\\af12\\afs24\\alang1025\\cf0\\kerning1\\loch\\f5\\fs24\\lang1033\\qj\\li0\\ri0\\lin0\\rin0\\fi0\\ltrpar\\rtlch \\ltrch\\loch\n\n\\par }"
  },
  {
    "path": "Documentation/FEBio_EULA_3.txt",
    "content": "FEBIO SOFTWARE LICENSE\nVersion 3.0 \n\n\nTHIS AGREEMENT, between you the licensee, hereinafter referred to as \"Recipient,\" and the University of Utah, having an address at the University of Utah, Technology Commercialization Office, 615 Arapeen Drive, Suite 310, Salt Lake City, Utah 84108, hereinafter referred to as \"University,\" shall govern the conditions of disclosure by University to Recipient of certain software (SOFTWARE) named: FEBio, developed by Steve Maas and Jeff Weiss of the University of Utah, and Gerard Ateshian, of Columbia University. SOFTWARE, as used herein, includes all such software actually provided to Recipient, including, as the case may be, any binaries or executables thereof, plus any software derived directly therefrom, but excludes the source code and the Software Development Kit (SDK). \n\n1.  LICENSE.  University grants to you a non-exclusive, non-transferable right to use the SOFTWARE in a single installation on a single machine in a single geographic location. It is further agreed that the furnishing of SOFTWARE to Recipient shall not constitute any grant of license to Recipient under any legal rights now or hereinafter held by University.\n\n2.  TERMINATION.  The use of the SOFTWARE by Recipient is conditioned upon Recipient’s compliance with the terms of this Agreement.  When this Agreement terminates, Recipient is required to remove all copies of the SOFTWARE and discontinue all use.  Recipient agrees that Recipient will only copy the SOFTWARE into any machine readable or printed form as necessary to use it in accordance with this Agreement or for backup purposes in support of Recipients use of the SOFTWARE. This Agreement is effective until terminated. Recipient may terminate it at any point by destroying the SOFTWARE together with all copies of the SOFTWARE.  Also, University has the option to terminate if Recipient fails to comply with any term or condition of this Agreement or upon 30 days written notice.  Recipient agrees upon such termination to destroy the SOFTWARE together with all copies of the SOFTWARE.\n\n3.  COPYRIGHT.  The SOFTWARE is protected by United States copyright law and international treaty provisions.  Recipient acknowledges that no title to the intellectual property in the SOFTWARE is transferred to Recipient.  Recipient further acknowledge that title and full ownership rights to the SOFTWARE will remain the exclusive property of University or its suppliers, and Recipient will not acquire any rights to the SOFTWARE except as expressly set forth in this Agreement.\n\n4.  LIMITATIONS.  Recipient may modify the SOFTWARE or create derivative works based upon the SOFTWARE for their own internal or personal use. Recipient may not rent, lease, distribute, transfer or sublicense the SOFTWARE or any derivative works thereof to any third parties, without first negotiating in good faith with University a separate license agreement.  Recipient may not export the SOFTWARE or any derivative works thereof into any country prohibited by the United States Export Administration Act and the regulations there under.\n\n5.  LIMITED WARRANTIES.  University warrants that the media on which the SOFTWARE is furnished will be free from defects in materials and workmanship under normal use. \n\n6.  DISCLAIMER OF WARRANTY: EXCEPT AS SET FORTH HEREIN, SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND.  TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, UNIVERISTY FURTHER DISCLAIMS ALL EXPRESSED AND IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATIONS ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS AGREEMENT.\n\n7.  GOVERNING LAW.  This Agreement will be governed by the internal laws of the State of Utah without regard to conflict of laws.\n\n8.  ENTIRE AGREEMENT.  This is the entire agreement between Recipient and University, which supersedes any prior agreement or understanding, whether written, or oral, relating to the subject matter of this license.\n\n9.  NO LIABILITY FOR CONSEQUENTIAL DAMAGES: IN NO EVENT SHALL UNIVERSITY BE LIABLE TO RECIPIENT FOR ANY CONSEQUENTIAL, SPECIAL, INCIDENTAL OR INDIRECT DAMAGES OF ANY KIND ARISING OUT OF THE DELIVERY, PERFORMANCE OR USE OF THE SOFTWARE, EVEN IF UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n10.  BASIS OF BARGAIN.  The limited warranty, exclusive remedies, and limited liability set forth above are fundamental elements of the basis of the agreement between University and Recipient.  University would not be able to provide the SOFTWARE on an economic basis without such limitations.\n"
  },
  {
    "path": "Documentation/FEBio_EULA_4.txt",
    "content": "FEBIO SOFTWARE LICENSE\nVersion 4.0\n\n\nTHIS AGREEMENT, between you the licensee, hereinafter referred to as \"Recipient,\" and the University of Utah, having an address at the University of Utah, Technology Commercialization Office, 615 Arapeen Drive, Suite 310, Salt Lake City, Utah 84108, hereinafter referred to as \"University,\" shall govern the conditions of disclosure by University to Recipient of certain software (SOFTWARE) named: FEBio, developed by Steve Maas and Jeff Weiss of the University of Utah, and Gerard Ateshian, of Columbia University. SOFTWARE, as used herein, includes all such software actually provided to Recipient, including, as the case may be, any binaries or executables thereof, plus any software derived directly therefrom, but excludes the source code and the Software Development Kit (SDK).\n\n1.  LICENSE.  University grants to you a non-exclusive, non-transferable right to use the SOFTWARE in a single installation on a single machine in a single geographic location. It is further agreed that the furnishing of SOFTWARE to Recipient shall not constitute any grant of license to Recipient under any legal rights now or hereinafter held by University.\n\n2.  TERMINATION.  The use of the SOFTWARE by Recipient is conditioned upon Recipient's compliance with the terms of this Agreement. When this Agreement terminates, Recipient is required to remove all copies of the SOFTWARE and discontinue all use. Recipient agrees that Recipient will only copy the SOFTWARE into any machine readable or printed form as necessary to use it in accordance with this Agreement or for backup purposes in support of Recipients use of the SOFTWARE. This Agreement is effective until terminated. Recipient may terminate it at any point by destroying the SOFTWARE together with all copies of the SOFTWARE. Also, University has the option to terminate if Recipient fails to comply with any term or condition of this Agreement or upon 30 days written notice. Recipient agrees upon such termination to destroy the SOFTWARE together with all copies of the SOFTWARE.\n\n3.  COPYRIGHT.  The SOFTWARE is protected by United States copyright law and international treaty provisions. Recipient acknowledges that no title to the intellectual property in the SOFTWARE is transferred to Recipient. Recipient further acknowledge that title and full ownership rights to the SOFTWARE will remain the exclusive property of University or its suppliers, and Recipient will not acquire any rights to the SOFTWARE except as expressly set forth in this Agreement.\n\n4.  LIMITATIONS.  Recipient may modify the SOFTWARE or create derivative works based upon the SOFTWARE for their own internal or personal use. Recipient may not rent, lease, distribute, transfer or sublicense the SOFTWARE or any derivative works thereof to any third parties, without first negotiating in good faith with University a separate license agreement. Recipient may not export the SOFTWARE or any derivative works thereof into any country prohibited by the United States Export Administration Act and the regulations there under.\n\n5.  LIMITED WARRANTIES.  University warrants that the media on which the SOFTWARE is furnished will be free from defects in materials and workmanship under normal use.\n\n6.  DISCLAIMER OF WARRANTY: EXCEPT AS SET FORTH HEREIN, SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, UNIVERISTY FURTHER DISCLAIMS ALL EXPRESSED AND IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATIONS ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS AGREEMENT.\n\n7.  GOVERNING LAW.  This Agreement will be governed by the internal laws of the State of Utah without regard to conflict of laws.\n\n8.  ENTIRE AGREEMENT.  This is the entire agreement between Recipient and University, which supersedes any prior agreement or understanding, whether written, or oral, relating to the subject matter of this license.\n\n9.  NO LIABILITY FOR CONSEQUENTIAL DAMAGES: IN NO EVENT SHALL UNIVERSITY BE LIABLE TO RECIPIENT FOR ANY CONSEQUENTIAL, SPECIAL, INCIDENTAL OR INDIRECT DAMAGES OF ANY KIND ARISING OUT OF THE DELIVERY, PERFORMANCE OR USE OF THE SOFTWARE, EVEN IF UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n10.  BASIS OF BARGAIN.  The limited warranty, exclusive remedies, and limited liability set forth above are fundamental elements of the basis of the agreement between University and Recipient. University would not be able to provide the SOFTWARE on an economic basis without such limitations.\n\n11. PRIVACY. The SOFTWARE adheres to the privacy policy set out by the University of Utah (https://www.utah.edu/privacy/gdpr.php). The SOFTWARE does not collect, distribute, or share any personal data. Upon launch, the SOFTWARE does connect to a remote server, managed by University of Utah personnel, and passes a Universally Unique ID (UUID), which was generated upon first use after installation of the SOFTWARE and stored on the Recipient's local computer, to the remote server. This UUID is used as part of a process that accumulates general usage statistics, but is otherwise not distributed or shared with any third party."
  },
  {
    "path": "Documentation/FEBio_Theory_Manual.lyx",
    "content": "#LyX 2.4 created this file. For more info see https://www.lyx.org/\n\\lyxformat 620\n\\begin_document\n\\begin_header\n\\save_transient_properties true\n\\origin unavailable\n\\textclass extbook\n\\begin_preamble\n\\usepackage{hyperref}\n\\end_preamble\n\\use_default_options true\n\\begin_modules\ntheorems-ams\neqs-within-sections\nfigs-within-sections\n\\end_modules\n\\maintain_unincluded_children no\n\\language english\n\\language_package default\n\\inputencoding auto-legacy\n\\fontencoding auto\n\\font_roman \"default\" \"default\"\n\\font_sans \"helvet\" \"default\"\n\\font_typewriter \"default\" \"default\"\n\\font_math \"auto\" \"auto\"\n\\font_default_family sfdefault\n\\use_non_tex_fonts false\n\\font_sc false\n\\font_roman_osf false\n\\font_sans_osf false\n\\font_typewriter_osf false\n\\font_sf_scale 100 100\n\\font_tt_scale 100 100\n\\use_microtype false\n\\use_dash_ligatures true\n\\graphics default\n\\default_output_format default\n\\output_sync 0\n\\bibtex_command default\n\\index_command default\n\\paperfontsize 11\n\\spacing single\n\\use_hyperref false\n\\papersize default\n\\use_geometry true\n\\use_package amsmath 2\n\\use_package amssymb 2\n\\use_package cancel 2\n\\use_package esint 2\n\\use_package mathdots 2\n\\use_package mathtools 2\n\\use_package mhchem 2\n\\use_package stackrel 2\n\\use_package stmaryrd 2\n\\use_package undertilde 0\n\\cite_engine natbib\n\\cite_engine_type numerical\n\\biblio_style plainnat\n\\biblio_options sort&compress\n\\use_bibtopic false\n\\use_indices false\n\\paperorientation portrait\n\\suppress_date false\n\\justification true\n\\use_refstyle 1\n\\use_formatted_ref 0\n\\use_minted 0\n\\use_lineno 0\n\\index Index\n\\shortcut idx\n\\color #008000\n\\end_index\n\\leftmargin 1in\n\\topmargin 1in\n\\rightmargin 1in\n\\bottommargin 1in\n\\secnumdepth 3\n\\tocdepth 3\n\\paragraph_separation indent\n\\paragraph_indentation default\n\\is_math_indent 0\n\\math_numbering_side default\n\\quotes_style english\n\\dynamic_quotes 0\n\\papercolumns 1\n\\papersides 2\n\\paperpagestyle default\n\\tablestyle default\n\\tracking_changes false\n\\output_changes false\n\\change_bars false\n\\postpone_fragile_content false\n\\html_math_output 0\n\\html_css_as_file 0\n\\html_be_strict false\n\\docbook_table_output 0\n\\docbook_mathml_prefix 1\n\\end_header\n\n\\begin_body\n\n\\begin_layout Standard\n\\begin_inset FormulaMacro\n\\newcommand{\\Dev}{\\operatorname{Dev}}\n{\\text{Dev}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\dev}{\\operatorname{dev}}\n{\\text{dev}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\grad}{\\operatorname{grad}}\n{\\text{grad}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Grad}{\\operatorname{Grad}}\n{\\text{Grad}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\divg}{\\operatorname{div}}\n{\\text{div}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Ei}{\\operatorname{Ei}}\n{\\text{Ei}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\tr}{\\operatorname{tr}}\n{\\text{tr}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Divg}{\\operatorname{Div}}\n{\\text{Div}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\cay}{\\operatorname{cay}}\n{\\text{cay}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\rot}{\\operatorname{rot}}\n{\\text{rot}}\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Title\n\\begin_inset Graphics\n\tfilename Figures/FigFEBioTitle.png\n\twidth 4.88in\n\n\\end_inset\n\n\n\\series bold\n\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\series default\nTheory Manual Version 4.12 \n\\end_layout\n\n\\begin_layout Date\nLast Updated:\n February 25,\n 2026\n\\end_layout\n\n\\begin_layout Paragraph*\nContributors\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSteve Maas (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:steve.maas@utah.edu}{steve.maas@utah.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nMichael Herron (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:michael.herron@utah.edu}{michael.herron@utah.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nDr.\n Jeffrey Weiss (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:jeff.weiss@utah.edu}{jeff.weiss@utah.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nDr.\n Gerard Ateshian (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:ateshian@columbia.edu}{ateshian@columbia.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout Paragraph*\nContact address\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMusculoskeletal Research Laboratories,\n University of Utah\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n72 S.\n Central Campus Drive,\n Room 2646\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nSalt Lake City,\n Utah\n\\end_layout\n\n\\begin_layout Paragraph*\nWebsite\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWeiss Lab:\n \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{https://weisslabutah.org}\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nFEBio:\n \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{http://febio.org}\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Paragraph*\nForum\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{https://forums.febio.org/index.php}\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Paragraph*\nAcknowledgments\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDevelopment of the FEBio project is supported in part by a grant from the U.S.\n National Institutes of Health (R01GM083925).\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Graphics\n\tfilename Figures/NIHlogo.png\n\tlyxscale 25\n\twidth 2cm\n\tspecial height=0.75in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset toc\nLatexCommand tableofcontents\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nIntroduction\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:introduction\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Section\nOverview of FEBio\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:overview\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio is an implicit,\n nonlinear finite element solver that is specifically designed for applications in biomechanics.\n It offers analyses,\n constitutive models and boundary conditions that are relevant for this particular field.\n This section describes briefly the available features of FEBio.\n A more detailed overview of features can be found in the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio User's Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFEBio supports two analysis types,\n namely \n\\emph on\nquasi-static\n\\emph default\n and \n\\emph on\nquasi-static poroelastic\n\\emph default\n.\n In a \n\\emph on\nquasi-static\n\\emph default\n analysis the (quasi-) static response of the system is sought;\n inertial terms are ignored.\n In a \n\\emph on\nquasi-static poroelastic\n\\emph default\n analysis a coupled solid-fluid problem is solved.\n The latter analysis type is useful for modeling tissues that have high water content and the explicit modeling of fluid movement relative to the solid phase is important.\n\\end_layout\n\n\\begin_layout Standard\nSeveral nonlinear constitutive models are available to allow the user to model the often complicated biological tissue behavior.\n Several isotropic constitutive models are supported such as Neo-Hookean,\n Mooney-Rivlin,\n Veronda-Westmann,\n Arruda-Boyce and Ogden.\n These models have a nonlinear stress-strain response.\n In addition to the isotropic models,\n there are several anisotropic models available.\n These materials show anisotropic behavior in at least one preferred direction and are useful for modeling biological tissues such as tendons,\n muscles and other tissues that contain fibers.\n FEBio also contains a \n\\emph on\nrigid body \n\\emph default\nmaterial model,\n which can be used to model rigid structures whose deformation is negligible compared to the deformable geometry.\n\\end_layout\n\n\\begin_layout Standard\nBiological tissues can interact in very complicated ways.\n Therefore FEBio supports a wide range of boundary conditions to model these interactions.\n These include prescribed displacements,\n nodal forces,\n and pressure forces.\n Deformable models can also be connected to rigid bodies so that the user can model prescribed rotations and torques.\n Rigid bodies can be connected with rigid joints.\n Even more complicated interactions can be modeled using FEBio's contact interfaces.\n The user can choose between different types of contact interfaces,\n such as sliding interfaces,\n tied interfaces and rigid wall interfaces.\n A sliding interface is defined between two surfaces that are allowed to separate and slide across each other but are not allowed to penetrate.\n The rigid wall interface is also similar to the sliding interface,\n except that one of the contacting surfaces is a movable rigid wall.\n As of version 1.2,\n there is an implementation of a sliding interface that allows for fluid flow crossing the contact interface.\n The tied interface is similar to the sliding interface,\n but in this case,\n the surfaces are not allowed to slide or separate.\n In addition,\n the user may specify a body force which can be used to model the effects of gravity or base acceleration.\n\\end_layout\n\n\\begin_layout Section\nAbout this document\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:about-this-document\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis document is a part of a set of three manuals that accompany FEBio:\n the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio User's Manual}}\n\\end_layout\n\n\\end_inset\n\n,\n describing how to use FEBio,\n the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Developer's \n\\end_layout\n\n\\begin_layout Plain Layout\n\nManual}}\n\\end_layout\n\n\\end_inset\n\n for users who wish to modify or add features to the code,\n and this manual,\n which describes the theory behind most of the FEBio algorithms.\n\\end_layout\n\n\\begin_layout Standard\nThe purpose of this manual is to provide theoretical background on many of the algorithms that are implemented in FEBio.\n In this way the user can develop a better understanding of how the program works and how it can be used to create well defined biomechanical simulations.\n The authors have tried to be as detailed as possible to make the text coherent and comprehensible,\n but due to the complexity of some of the topics,\n some descriptions only skim the surface.\n Many of the theoretical ideas discussed in this manual can and have filled entire bookshelves.\n The explanations contained herein should be sufficient to give the reader a basic understanding of the theoretical developments.\n References to textbooks and the primary literature are provided for further reading.\n\\end_layout\n\n\\begin_layout Standard\nChapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:continuum-mechanics\"\nnolink \"false\"\n\n\\end_inset\n\n starts with a brief overview of some of the important concepts in continuum mechanics.\n Readers who are already familiar with this field can skip this chapter,\n although the material may be useful to get familiar with the notation and terminology used in this manual.\n\\end_layout\n\n\\begin_layout Standard\nChapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Nonlinear-FE-Method\"\nnolink \"false\"\n\n\\end_inset\n\n describes the nonlinear finite element method.\n It also explains the Newton-Raphson method,\n which is the basis for most implementations of the nonlinear finite element method.\n A more specialized version of this algorithm,\n the BFGS method,\n is described as well since it is used in FEBio.\n\\end_layout\n\n\\begin_layout Standard\nIn Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Element-Library\"\nnolink \"false\"\n\n\\end_inset\n\n the different element types that are available in FEBio are described in detail.\n FEBio currently supports 3D solid elements,\n such as the linear hexahedral,\n pentahedral and tetrahedral elements,\n as well as quadrilateral and triangular shell elements.\n\\end_layout\n\n\\begin_layout Standard\nChapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Constitutive-Models\"\nnolink \"false\"\n\n\\end_inset\n\n contains a detailed description of the material models in FEBio.\n Most of these models are based on hyperelasticity,\n which is introduced in chapter 2.\n Several transversely isotropic materials are described as well.\n This also discusses the biphasic material and its implementation in FEBio.\n\\end_layout\n\n\\begin_layout Standard\nChapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Contact-and-Coupling\"\nnolink \"false\"\n\n\\end_inset\n\n describes the basics of the theory of contact and coupling.\n In FEBio the user can connect the different parts of the geometry in a variety of ways.\n There are rigid interfaces where a deformable model is attached to a rigid model,\n rigid joints where two or more rigid bodies connect,\n and sliding interfaces where two surfaces are allowed to separate and slide across each other but are not allowed to penetrate.\n The various contact and coupling algorithms are discussed as well together with their implementation in FEBio.\n\\end_layout\n\n\\begin_layout Chapter\nContinuum Mechanics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:continuum-mechanics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis chapter contains an overview of some of the important concepts from continuum mechanics and establishes some of the notation and terminology that will be used in the rest of this document.\n The section begins by introducing the important concepts of deformation,\n stress and strain.\n Next the concept of hyperelasticity is discussed.\n Finally the concept of virtual work is discussed.\n This concept will be used later to derive the nonlinear finite element equations.\n For a more thorough introduction to the mathematics needed for continuum mechanics,\n the user can consult \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nVectors and Tensors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:vectors-and-tensors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIt is assumed that the reader is familiar with the concepts of vectors and tensors.\n This section summarizes the notation and some useful relations that will be used throughout the manual.\n\\end_layout\n\n\\begin_layout Standard\nVectors are denoted by small,\n bold letters,\n e.g.\n \n\\series bold\nv\n\\series default\n.\n Their components will be denoted by \n\\begin_inset space \\quad{}\n\\end_inset\n\n\n\\begin_inset Formula $v_{i}$\n\\end_inset\n\n,\n where,\n unless otherwise stated,\n Latin under scripts such as \n\\begin_inset Formula $i$\n\\end_inset\n\nor \n\\begin_inset Formula $I$\n\\end_inset\n\nwill range from 1 to 3.\n In matrix form a vector will be represented as a column vector and its transpose as a row vector:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{array}{cc}\n\\mathbf{v}=\\left(\\begin{array}{c}\nv_{1}\\\\\nv_{2}\\\\\nv_{3}\n\\end{array}\\right),\\quad & \\mathbf{v}^{T}=\\left(\\begin{array}{ccc}\nv_{1}, & v_{2}, & v_{3}\\end{array}\\right)\\end{array}.\\label{eq1}\n\\end{equation}\n\n\\end_inset\n\nThe following products are defined between vectors.\n Assume \n\\series bold\nu\n\\series default\n,\n \n\\series bold\nv\n\\series default\n are vectors.\n Also note that the Einstein summation convention is used throughout this manual \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lai10\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ndot \n\\emph default\nor \n\\emph on\nscalar product\n\\emph default\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{u}\\cdot\\mathbf{v}=u_{i}v_{i}\\,.\\label{eq2}\n\\end{equation}\n\n\\end_inset\n\nThe \n\\emph on\ncross product\n\\emph default\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{u}\\times\\mathbf{v}=\\left[\\begin{array}{c}\nu_{2}v_{3}-u_{3}v_{2}\\\\\nu_{3}v_{1}-u_{1}v_{3}\\\\\nu_{1}v_{2}-u_{2}v_{1}\n\\end{array}\\right]\\,.\\label{eq3}\n\\end{equation}\n\n\\end_inset\n\nThe \n\\emph on\nvector\n\\emph default\n \n\\emph on\nouter product\n\\emph default\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{u}\\otimes\\mathbf{v}\\right)_{ij}=u_{i}v_{j}\\,.\\label{eq4}\n\\end{equation}\n\n\\end_inset\n\nNote that vectors are also known as first order tensors.\n Scalars are known as zero order tensors.\n The outer product,\n defined by equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq4\"\nnolink \"false\"\n\n\\end_inset\n\n,\n is a second order tensor.\n\\end_layout\n\n\\begin_layout Standard\nSecond order tensors are denoted by bold,\n capital letters,\n e.g.\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n\n\\series default\n.\n Some exceptions will be made to remain consistent with the literature.\n For instance,\n the Cauchy stress tensor is denoted by \n\\begin_inset Formula $\\mathbf{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n.\n However,\n the nature of the objects will always be clear from the context.\n The following operations on tensors are defined.\n Assume \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n \n\\series default\nand \n\\series bold\n\n\\begin_inset Formula $\\mathbf{B}$\n\\end_inset\n\n \n\\series default\nare second-order tensors.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ndouble contraction\n\\emph default\n or \n\\emph on\ntensor inner product\n\\emph default\n is defined as:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{A}:\\mathbf{B}=A_{ij}B_{ij}\\,.\\label{eq5}\n\\end{equation}\n\n\\end_inset\n\nThe \n\\emph on\ntrace\n\\emph default\n is defined as:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tr\\mathbf{A}=\\mathbf{I}:\\mathbf{A}=A_{ii}\\,.\\label{eq6}\n\\end{equation}\n\n\\end_inset\n\nHere \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n is the second order identity tensor with components \n\\begin_inset Formula $\\delta_{ij}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn general the components of tensors will change under a change of coordinate system.\n Nevertheless,\n certain intrinsic quantities associated with them will remain invariant under such a transformation.\n The scalar product between two vectors is such an example.\n The double contraction between two second-order tensors is another example.\n The following set of invariants for second-order tensors is commonly used:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}I_{1} & =\\tr\\mathbf{A},\\\\\nI_{2} & =\\frac{1}{2}\\left(\\left(\\tr\\mathbf{A}\\right)^{2}-\\tr\\mathbf{A}^{2}\\right)\\\\\nI_{3} & =\\det\\mathbf{A}.\n\\end{aligned}\n\\,.\\label{eq7}\n\\end{equation}\n\n\\end_inset\n\nA tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n \n\\series default\nis called symmetric if it is equal to its transpose:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=\\mathbf{S}^{T}\\,.\\label{eq8}\n\\end{equation}\n\n\\end_inset\n\nA tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{W}$\n\\end_inset\n\n \n\\series default\nis called anti-symmetric if it is equal to the negative of its transpose:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{W}=-\\mathbf{W}^{T}\\,.\\label{eq9}\n\\end{equation}\n\n\\end_inset\n\nAny second order tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n \n\\series default\ncan be written as the sum of a symmetric tensor \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n\n\\series bold\n \n\\series default\nand an anti-symmetric tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{W}$\n\\end_inset\n\n\n\\series default\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{A}=\\mathbf{S}+\\mathbf{W},\\label{eq10}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=\\frac{1}{2}\\left(\\mathbf{A}+\\mathbf{A}^{T}\\right),\\mbox{\\thinspace and\\thinspace}\\mathbf{W}=\\frac{1}{2}\\left(\\mathbf{A}-\\mathbf{A}^{T}\\right).\\label{eq11}\n\\end{equation}\n\n\\end_inset\n\nAlso note that for any tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{B}$\n\\end_inset\n\n \n\\series default\nthe following holds:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{B}:\\mathbf{A}=\\mathbf{B}:\\mathbf{S}\\,,\\quad\\mathbf{B}:\\mathbf{W}=\\mathbf{0}\\,.\\label{eq12}\n\\end{equation}\n\n\\end_inset\n\nWith any anti-symmetric tensor a dual vector \n\\series bold\n\n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n \n\\series default\ncan be associated such that,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\hat{w}}\\cdot\\mathbf{u}=\\mathbf{w}\\times\\mathbf{u}\\,,\\label{eq13}\n\\end{equation}\n\n\\end_inset\n\nwhere the second order tensor \n\\begin_inset Formula $\\mathbf{\\hat{w}}$\n\\end_inset\n\n is defined as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\hat{w}}=\\left[\\begin{array}{ccc}\n0 & -w_{3} & w_{2}\\\\\nw_{3} & 0 & -w_{1}\\\\\n-w_{2} & w_{1} & 0\n\\end{array}\\right]\\,.\\label{eq14}\n\\end{equation}\n\n\\end_inset\n\nA second order \n\\series bold\n\n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n \n\\series default\ntensor is called \n\\emph on\northogonal \n\\emph default\nif \n\\begin_inset Formula $\\mathbf{Q}^{-1}=\\mathbf{Q}^{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn the implementation of the FE method it is often convenient to write symmetric second-order tensors using \n\\emph on\nVoigt notation\n\\emph default\n.\n In this notation the components of a 2\n\\begin_inset Formula $^{\\mathrm{nd}}$\n\\end_inset\n\n order symmetric tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n \n\\series default\nare arranged as a column vector:\n \n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{A}\\right]=\\left[\\begin{array}{c}\nA_{11}\\\\\nA_{22}\\\\\nA_{33}\\\\\nA_{12}\\\\\nA_{23}\\\\\nA_{13}\n\\end{array}\\right]\\,.\\label{eq15}\n\\end{equation}\n\n\\end_inset\n\nHigher order tensors will be denoted by bold,\n capital,\n script symbols,\n e.g.\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{A}}$\n\\end_inset\n\n.\n An example of a third-order tensor is the \n\\emph on\npermutation tensor \n\\emph default\n\n\\begin_inset Formula $\\mathcal{E}_{ijk}$\n\\end_inset\n\n,\n whose components are 1 for an even permutation of \n\\begin_inset Formula $\\left(1,2,3\\right)$\n\\end_inset\n\n,\n -1 for an odd permutation of \n\\begin_inset Formula $\\left(1,2,3\\right)$\n\\end_inset\n\n and zero otherwise.\n The permutation symbol is useful for expressing the cross-product of two vectors in index notation:\n \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{u}\\times\\mathbf{v}\\right)_{i}=\\mathcal{E}_{ijk}u_{j}v_{k}\\,.\\label{eq16}\n\\end{equation}\n\n\\end_inset\n\nAn example of a fourth-order tensor is the elasticity tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n which,\n in linear elasticity theory,\n relates the small strain tensor \n\\begin_inset Formula $\\mathbf{\\boldsymbol{\\varepsilon}}$\n\\end_inset\n\n and the Cauchy stress tensor \n\\begin_inset Formula $\\boldsymbol{\\sigma}=\\boldsymbol{\\mathcal{C}}:\\boldsymbol{\\varepsilon}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nHigher order tensors can be constructed from second order tensors in a similar way as second order tensors can be constructed from vectors.\n If \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{B}$\n\\end_inset\n\n are second order tensors,\n then the following fourth order tensors can be defind by requiring that the following must hold for any second order tensor \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\otimes\\mathbf{B}\\right):\\mathbf{X}=\\left(\\mathbf{B}:\\mathbf{X}\\right)\\mathbf{A}\\,,\\label{eq17}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\oslash\\mathbf{B}\\right):\\mathbf{X}=\\mathbf{A}\\cdot\\mathbf{X}\\cdot\\mathbf{B}^{T}\\,,\\label{eq18}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\obslash\\mathbf{B}\\right):\\mathbf{X}=\\mathbf{A}\\cdot\\mathbf{X}^{T}\\cdot\\mathbf{B}^{T}\\,,\\label{eq19}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\odot\\mathbf{B}\\right):\\mathbf{X}=\\frac{1}{2}\\left(\\mathbf{A}\\cdot\\mathbf{X}\\cdot\\mathbf{B}^{T}+\\mathbf{A}\\cdot\\mathbf{X}^{T}\\cdot\\mathbf{B}^{T}\\right)\\,.\\label{eq20}\n\\end{equation}\n\n\\end_inset\n\nThe Cartesian component forms of the operators \n\\begin_inset Formula $\\otimes$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\oslash$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\obslash$\n\\end_inset\n\n and \n\\begin_inset Formula $\\odot$\n\\end_inset\n\n are defined as follows:\n \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\otimes\\mathbf{B}\\right)_{ijkl}=\\mathbf{A}_{ij}\\mathbf{B}_{kl}\\,,\\label{eq21}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\oslash\\mathbf{B}\\right)_{ijkl}=\\mathbf{A}_{ik}\\mathbf{B}_{jl}\\,,\\label{eq22}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\obslash\\mathbf{B}\\right)_{ijkl}=A_{il}B_{jk}\\,,\\label{eq23}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{A}\\odot\\mathbf{B}\\right)_{ijkl}=\\frac{1}{2}\\left(A_{ik}B_{jl}+A_{il}B_{jk}\\right)\\,.\\label{eq24}\n\\end{equation}\n\n\\end_inset\n\nThe fourth order identity tensors are defined as:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{A} & =\\underline{\\mathcal{I}}:\\mathbf{A}\\,,\\\\\n\\mathbf{A}^{T} & =\\overline{\\mathcal{I}}:\\mathbf{A}\\,,\n\\end{aligned}\n\\label{eq25}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\underline{\\mathcal{I}}=\\mathbf{I}\\oslash\\mathbf{I}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\overline{\\boldsymbol{\\mathcal{I}}}=\\mathbf{I}\\obslash\\mathbf{I}$\n\\end_inset\n\n.\n The components are given by:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathcal{\\underline{I}}_{ijkl} & =\\delta_{ik}\\delta_{jl}\\,,\\\\\n\\overline{\\mathcal{I}}_{ijkl} & =\\delta_{il}\\delta_{jk}\\,.\n\\end{aligned}\n\\label{eq26}\n\\end{equation}\n\n\\end_inset\n\nWe may also define \n\\begin_inset Formula $\\boldsymbol{\\mathcal{I}}=\\frac{1}{2}\\left(\\underline{\\boldsymbol{\\mathcal{I}}}+\\overline{\\boldsymbol{\\mathcal{I}}}\\right)$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $\\boldsymbol{\\mathcal{I}}:\\mathbf{A}=\\frac{1}{2}\\left(\\mathbf{A}+\\mathbf{A}^{T}\\right)$\n\\end_inset\n\n returns the symmetric part of \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n.\n In the special case when \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n is symmetric it follows that \n\\begin_inset Formula $\\underline{\\mathcal{I}}:\\mathbf{A}=\\boldsymbol{\\mathcal{I}}:\\mathbf{A}=\\mathbf{A}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nThe Directional Derivative\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:The-Directional-Derivative\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn later sections the nonlinear finite element method will be formulated.\n Anticipating an iterative solution method to solve the nonlinear equations,\n it will be necessary to linearize the quantities involved.\n This linearization process will utilize a construction called the \n\\emph on\ndirectional derivative \n\\emph default\n\n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe directional derivative of a function \n\\begin_inset Formula $f\\left(\\mathbf{x}\\right)$\n\\end_inset\n\n is defined as follows:\n \n\\begin_inset Formula \n\\begin{equation}\nDf\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]=\\left.\\frac{d}{d\\varepsilon}\\right|_{\\varepsilon=0}f\\left(\\mathbf{x}+\\varepsilon\\mathbf{u}\\right)\\,.\\label{eq27}\n\\end{equation}\n\n\\end_inset\n\nThe quantity \n\\series bold\n\n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n \n\\series default\nmay be a scalar,\n a vector or even a vector of unknown functions.\n For instance,\n consider a scalar function \n\\begin_inset Formula $f\\left(\\mathbf{x}\\right)$\n\\end_inset\n\n,\n where \n\\series bold\n\n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n \n\\series default\nis the position vector in \n\\begin_inset Formula $\\mathbb{R}^{3}$\n\\end_inset\n\n.\n In this case the directional derivative is given by:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}Df\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right] & =\\left.\\frac{d}{d\\varepsilon}\\right|_{\\varepsilon=0}f\\left(\\mathbf{x}+\\varepsilon\\mathbf{u}\\right)\\\\\n & =\\frac{\\partial f}{\\partial x_{i}}u_{i}\\\\\n & =\\nabla f\\cdot\\mathbf{u}\\,.\n\\end{aligned}\n\\label{eq28}\n\\end{equation}\n\n\\end_inset\n\nHere,\n the symbol \n\\begin_inset Formula $\\nabla$\n\\end_inset\n\n(\n\\begin_inset Quotes eld\n\\end_inset\n\nnabla\n\\begin_inset Quotes erd\n\\end_inset\n\n) depicts the gradient operator.\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of a function implies that it is approximated by a linear function.\n Using the directional derivative,\n a function \n\\begin_inset Formula $f$\n\\end_inset\n\n can be linearized as follows:\n \n\\begin_inset Formula \n\\begin{equation}\nf\\left(\\mathbf{x}+\\mathbf{u}\\right)\\cong f\\left(\\mathbf{x}\\right)+Df\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right].\\label{eq29}\n\\end{equation}\n\n\\end_inset\n\nThe directional derivative obeys the usual properties for derivatives.\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\nsum rule\n\\emph default\n:\n If \n\\begin_inset Formula $f=f_{1}+f_{2}$\n\\end_inset\n\n,\n then \n\\begin_inset Formula \n\\begin{equation}\nDf\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]=Df_{1}\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]+Df_{2}\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]\\,.\\label{eq30}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\nproduct rule\n\\emph default\n:\n If \n\\begin_inset Formula $f=f_{1}\\cdot f_{2}$\n\\end_inset\n\n,\n then \n\\begin_inset Formula \n\\begin{equation}\nDf\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]=f_{1}\\left(\\mathbf{x}\\right)\\cdot Df_{2}\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]+f_{2}\\cdot Df_{1}\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]\\,.\\label{eq31}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\nchain rule\n\\emph default\n:\n If \n\\begin_inset Formula $f=g\\left(h\\left(\\mathbf{x}\\right)\\right)$\n\\end_inset\n\n,\n then \n\\begin_inset Formula \n\\begin{equation}\nDf\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]=Dg\\left(h\\left(\\mathbf{x}\\right)\\right)\\left[Dh\\left(\\mathbf{x}\\right)\\left[\\mathbf{u}\\right]\\right]\\,.\\label{eq32}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nCauchy Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Cauchy-Stress\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the plane with unit normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n bisect a loaded material body,\n such that \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n represents the outward normal (pointing away from the material) to one of the two halves.\n The traction vector \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n at any point \n\\begin_inset Formula $P$\n\\end_inset\n\n on this cross-section represents the limit of the reaction force \n\\begin_inset Formula $\\Delta\\mathbf{f}_{n}$\n\\end_inset\n\n acting over an elemental area \n\\begin_inset Formula $\\Delta A_{n}$\n\\end_inset\n\n in the neighborhood of \n\\begin_inset Formula $P$\n\\end_inset\n\n,\n as \n\\begin_inset Formula $\\Delta A_{n}\\to0$\n\\end_inset\n\n.\n For a non-polar medium,\n it is assumed that the reaction moment \n\\begin_inset Formula $\\Delta\\mathbf{m}_{n}$\n\\end_inset\n\n acting over \n\\begin_inset Formula $\\Delta A_{n}$\n\\end_inset\n\n at \n\\begin_inset Formula $P$\n\\end_inset\n\n vanishes as \n\\begin_inset Formula $\\Delta A_{n}\\to0$\n\\end_inset\n\n.\n Since there can be an infinite number of planes passing through the point \n\\begin_inset Formula $P$\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n may assume an infinity of values for each different \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n Therefore,\n we define a unique tensorial measure \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n at \n\\begin_inset Formula $P$\n\\end_inset\n\n,\n which returns the traction \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n for any unit normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}.\\label{eq:traction-stress-relation}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is the \n\\emph on\nCauchy stress tensor\n\\emph default\n at any point \n\\begin_inset Formula $P$\n\\end_inset\n\n inside the material body.\n The Cauchy stress tensor,\n a spatial tensor,\n is the actual physical stress,\n since the corresponding traction vector is the force in the deformed configuration,\n per unit deformed area.\n\\end_layout\n\n\\begin_layout Section\nAxioms of Conservation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Axioms-of-Conservation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe governing equations of continuum mechanics are given by the axioms of conservation of mass,\n linear and angular momentum,\n and energy.\n These axioms produce differential equations that need to be solved for the material response.\n However,\n on their own,\n these equations are insufficient to solve for all the unknowns in an analysis,\n therefore we must also provide equations of state that describe the behavior of specific classes of materials,\n such as elastic solids,\n viscoelastic solids,\n viscous fluids,\n etc.\n These equations of state are formulated as constitutive models,\n which may not be chosen arbitrarily:\n they must be constrained by the axiom of entropy inequality.\n In this section,\n we summarize all the axioms of conservation for a pure substance,\n as well as the axiom of entropy inequality.\n\\end_layout\n\n\\begin_layout Subsection\nAxiom of Mass Balance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Axiom-Mass-Balance\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe axiom of mass balance simply states that mass is conserved,\n or equivalently,\n its time rate of change is zero.\n Normally,\n we formulate this axiom in a volumetric domain (such as a fixed control volume with permeable boundary,\n a material region with impermeable boundary,\n or a material region with permeable boundary),\n then convert it to its differential form.\n In a spatial (Eulerian) frame,\n the differential statement of the axiom of mass balance takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\rho}{\\partial t}+\\divg\\left(\\rho\\mathbf{v}\\right)=0\\,,\\label{eq:axiom-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the mass density of the matter and \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n is its velocity vector.\n Since \n\\begin_inset Formula $\\divg\\left(\\rho\\mathbf{v}\\right)=\\grad\\rho\\cdot\\mathbf{v}+\\rho\\divg\\mathbf{v}$\n\\end_inset\n\n,\n we may also write this relation in the form\n\\begin_inset Formula \n\\begin{equation}\n\\frac{D\\rho}{Dt}+\\rho\\divg\\mathbf{v}=0\\,,\\label{eq:mass-balance-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\frac{D\\rho}{Dt}=\\frac{\\partial\\rho}{\\partial t}+\\grad\\rho\\cdot\\mathbf{v}\\label{eq:material-derivative-density}\n\\end{equation}\n\n\\end_inset\n\nis the \n\\emph on\nmaterial time derivative\n\\emph default\n of the mass density \n\\emph on\nin the spatial frame\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Subsection\nAxioms of Momentum Balance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Axiom-Linear-Momentum\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe axioms of linear and angular momentum balance are also known as Newton's second law of motion.\n The axiom of linear momentum balance states that the time rate of change of linear momentum is equal to the sum of all surface and body forces.\n The differential statement of the axiom of mass balance in a spatial frame is given by\n\\begin_inset Formula \n\\begin{equation}\n\\rho\\frac{D\\mathbf{v}}{Dt}=\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}\\,,\\label{eq:axiom-linear-momentum}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is the Cauchy stress tensor in the material,\n and \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is the specific body force vector (body force per mass),\n which is typically prescribed.\n Note that \n\\begin_inset Formula $D\\mathbf{v}/Dt$\n\\end_inset\n\n is the acceleration of the matter at every point in the continuum,\n as evaluated in the spatial frame.\n\\end_layout\n\n\\begin_layout Standard\nThe axiom of angular momentum balance states that the time rate of change of angular momentum is equal to the sum of all surface and body moments.\n The angular momentum represents the moment of the linear momentum about a fixed point (such as the origin) in an inertial frame.\n In a \n\\emph on\nnon-polar medium\n\\emph default\n,\n we assume that moments are only produces by surface and body forces.\n In that case,\n the axiom of angular momentum balance is satisfied by the linear momentum balance \n\\emph on\nand\n\\emph default\n by letting the Cauchy stress be symmetric,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{T}=\\boldsymbol{\\sigma}\\,.\\label{eq:axiom-angular-momentum}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nAxiom of Energy Balance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Axiom-Energy-Balance\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe axiom of energy balance is also known as the first law of thermodynamics.\n The time rate of change of internal and kinetic energy is balanced by the rate of work done by body and surface forces,\n the rate of heat flow into the medium,\n and rate of heat supply from external sources.\n Its differential statement takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\rho\\frac{D\\varepsilon}{Dt}=\\boldsymbol{\\sigma}:\\mathbf{L}-\\divg\\mathbf{q}+\\rho r\\,,\\label{eq:axiom-energy-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the specific internal energy (internal energy per mass),\n \n\\begin_inset Formula $\\mathbf{L}=\\grad\\mathbf{v}$\n\\end_inset\n\n is the velocity gradient,\n \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n is the heat flux in the medium (units of power per area),\n and \n\\begin_inset Formula $r$\n\\end_inset\n\n is the heat supply per unit mass from external sources that are not modeled explicitly (such as microwave heating in a framework that does not model microwave radiation explicitly,\n or Joule heating in electrical conductors,\n in a framework that does not model frictional interactions between electrons and atomic nuclei explicitly).\n\\end_layout\n\n\\begin_layout Standard\nFor non-polar media,\n since the Cauchy stress is symmetric according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:axiom-angular-momentum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\boldsymbol{\\sigma}:\\mathbf{L}=\\boldsymbol{\\sigma}:\\mathbf{D}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{D}=\\frac{1}{2}\\left(\\mathbf{L}+\\mathbf{L}^{T}\\right)$\n\\end_inset\n\n is the symmetric part of the velocity gradient,\n also known as the \n\\emph on\nrate of deformation tensor\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Subsection\nAxiom of Entropy Inequality\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Axiom-Entropy-Inequality\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe axioms of conservation presented above have introduced several functions of state that describe the behavior of specific materials,\n such as the Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n,\n the specific internal energy \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n,\n and the heat flux \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n.\n These axioms of conservation have a total of ten scalar unknowns (six compoments of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n,\n three components of \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n),\n whereas the equations have five scalar components (three components of the vectorial linear momentum balance,\n and one component for each of the scalar mass and energy balance equations).\n Therefore,\n there are too few equations to solve for all the unknowns.\n Consequently,\n constitutive models must be provided for these functions of state,\n which depend on a user-specified set of state variables (such as strain for elastic solids,\n rate of deformation for viscous fluids,\n temperature for heat transfer,\n etc.).\n\\end_layout\n\n\\begin_layout Standard\nConstitutive relations must be formulated in a manner that does not violate the axiom of entropy inequality,\n also known as the second law of thermodynamics.\n The second law of thermodynamics stipulates that the time rate of change of entropy in a medium is greater than the rate of entropy supply into the medium by various sources of heat.\n In a spatial frame,\n the differential statement of the axiom of entropy inequality is given by\n\\begin_inset Formula \n\\begin{equation}\n\\rho\\frac{D\\eta}{Dt}+\\divg\\frac{\\mathbf{q}}{\\theta}-\\rho\\frac{r}{\\theta}\\geqslant0\\,,\\label{eq:axiom-entropy-inequality}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n is the specific entropy (entropy per unit mass) of the matter.\n Entropy is a measure of disorder in a medium.\n It represents another function of state.\n\\end_layout\n\n\\begin_layout Standard\nSince Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:axiom-entropy-inequality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n does not include all of the functions of state,\n we may combine it with the energy balance to recover \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n in the entropy inequality.\n This may be achived by multiplying the entropy inequality in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:axiom-entropy-inequality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by the absolute temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n (recalling that \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is always positive) and adding it to the energy balance in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:axiom-energy-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to produce\n\\begin_inset Formula \n\\[\n-\\rho\\left(\\frac{D\\varepsilon}{Dt}-\\theta\\frac{D\\eta}{Dt}\\right)+\\boldsymbol{\\sigma}:\\mathbf{D}-\\frac{1}{\\theta}\\mathbf{q}\\cdot\\grad\\theta\\geqslant0\\,.\n\\]\n\n\\end_inset\n\nThe term in parentheses may be rearranged as\n\\begin_inset Formula \n\\[\n\\frac{D\\varepsilon}{Dt}-\\theta\\frac{D\\eta}{Dt}=\\frac{D}{Dt}\\left(\\varepsilon-\\theta\\eta\\right)+\\eta\\frac{D\\theta}{Dt}\\,.\n\\]\n\n\\end_inset\n\nWe now introduce the specific Helmholtz free energy,\n \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n,\n which relates the specific internal energy and entropy according to \n\\begin_inset Formula \n\\begin{equation}\n\\psi=\\varepsilon-\\theta\\eta\\,.\\label{eq:specific-free-energy}\n\\end{equation}\n\n\\end_inset\n\nWe may say that the free energy is the part of the internal energy which is available to produce useful work.\n Substituting this expression above produces the form of the Clausius-Duhem inequality that involves all the independent functions of state appearing in these axioms,\n (\n\\begin_inset Formula $\\psi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n),\n \n\\begin_inset Formula \n\\begin{equation}\n-\\rho\\left(\\dot{\\psi}+\\eta\\dot{\\theta}\\right)+\\boldsymbol{\\sigma}:\\mathbf{D}-\\frac{1}{\\theta}\\mathbf{q}\\cdot\\grad\\theta\\geqslant0\\,,\\label{eq:Clausius-Duhem-inequality}\n\\end{equation}\n\n\\end_inset\n\nwhere we have substituted \n\\begin_inset Formula $D\\left(\\cdot\\right)/Dt$\n\\end_inset\n\n with the \n\\begin_inset Formula $\\left(\\dot{}\\right)$\n\\end_inset\n\n operator,\n for notational simplicity.\n\\end_layout\n\n\\begin_layout Standard\nIt is important to understant that all functions of state are non-observable properties of matter:\n No instruments exist to measure the complete state of stress inside a material,\n nor the heat flux,\n the internal energy,\n or the entropy.\n However,\n we can formulate constitutive models that relate these functions of state to observable state variables,\n then calculate their value for a specific state.\n Observable state variables are all based on measurements of space (length,\n area and volume) and time.\n For example,\n we can measure (observe) displacement,\n velocity and acceleration of matter at various spatial locations,\n from which we can calculate their spatial gradients and treat these as observable state variables.\n From measurements of length in calibrated materials,\n we can derive measurements of force (using a load cell) and temperature (using a thermometer).\n\\end_layout\n\n\\begin_layout Standard\nTo better understand the choice of state variables relevant to mechanics,\n we proceed to describe the kinematics of a continuum in the next section.\n\\end_layout\n\n\\begin_layout Section\nKinematics of the Continuum\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Kinematics-of-Continuum\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nMotion and Path Lines\n\\end_layout\n\n\\begin_layout Standard\nAt any instant of time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n the \n\\emph on\nmotion\n\\emph default\n of a material particle \n\\begin_inset Formula $P$\n\\end_inset\n\n in the continuum is given by the function\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigKinematicsContinuum.png\n\tlyxscale 25\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nKinematics of the continuum.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig15\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)\\label{eq:motion-chi}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n is the reference position,\n at some reference time \n\\begin_inset Formula $t_{0}$\n\\end_inset\n\n,\n of the material particle \n\\begin_inset Formula $P$\n\\end_inset\n\n currently located at the spatial location \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n.\n This motion describes the \n\\emph on\npath line\n\\emph default\n of the particle that starts at \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n at \n\\begin_inset Formula $t=t_{0}$\n\\end_inset\n\n and passes through \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n at time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n At a subsequent time \n\\begin_inset Formula $t^{\\prime}$\n\\end_inset\n\n,\n the material particle passing through \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n would have originated at a different location \n\\begin_inset Formula $\\mathbf{X}^{\\prime}$\n\\end_inset\n\n at \n\\begin_inset Formula $t_{0}$\n\\end_inset\n\n,\n so that \n\\begin_inset Formula $\\mathbf{x}=\\boldsymbol{\\chi}\\left(\\mathbf{X}^{\\prime},t^{\\prime}\\right)=\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n.\n The material particle \n\\begin_inset Formula $P$\n\\end_inset\n\n is uniquely identified by its position \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n in the reference configuration.\n Therefore,\n any function \n\\begin_inset Formula $f$\n\\end_inset\n\n associated with each material point \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n,\n such as density,\n temperature,\n velocity,\n etc.,\n may be expressed as \n\\begin_inset Formula $f=\\hat{f}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n in the \n\\emph on\nmaterial\n\\emph default\n (or \n\\emph on\nLagrangian\n\\emph default\n) frame.\n Alternatively,\n the same function may be expressed in terms of the spatial position \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n through which the material particle originating at \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n passes at time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n In that case,\n \n\\begin_inset Formula $f=\\tilde{f}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n expresses this function in the \n\\emph on\nspatial\n\\emph default\n (or \n\\emph on\nEulerian\n\\emph default\n) frame.\n Importantly,\n since the function \n\\begin_inset Formula $f$\n\\end_inset\n\n is associated with the material,\n its time rate of change as the material moves is called the \n\\emph on\nmaterial time derivative\n\\emph default\n of \n\\begin_inset Formula $f$\n\\end_inset\n\n.\n The material time derivative of \n\\begin_inset Formula $f$\n\\end_inset\n\n in the material frame is given by\n\\begin_inset Formula \n\\begin{equation}\n\\dot{f}=\\frac{\\partial\\hat{f}\\left(\\mathbf{X},t\\right)}{\\partial t}\\,.\\label{eq:material-deriv-material}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n the velocity \n\\begin_inset Formula $\\mathbf{v}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n of the particle \n\\begin_inset Formula $P$\n\\end_inset\n\n is defined in the material frame as the material time derivative \n\\begin_inset Formula $\\dot{\\boldsymbol{\\chi}}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n of the motion,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}\\left(\\mathbf{X},t\\right)=\\frac{\\partial\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)}{\\partial t}\\,.\\label{eq75}\n\\end{equation}\n\n\\end_inset\n\nThe material time derivative of \n\\begin_inset Formula $f$\n\\end_inset\n\n in the spatial frame may be evaluated by using the chain rule on \n\\begin_inset Formula $f=\\tilde{f}\\left(\\mathbf{x},t\\right)\\equiv f\\left(\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right),t\\right)$\n\\end_inset\n\n,\n thus\n\\begin_inset Formula \n\\[\n\\dot{f}=\\frac{\\partial\\tilde{f}\\left(\\boldsymbol{\\chi},t\\right)}{\\partial t}+\\frac{\\partial\\tilde{f}\\left(\\boldsymbol{\\chi},t\\right)}{\\partial\\boldsymbol{\\chi}}\\cdot\\frac{\\partial\\boldsymbol{\\chi}}{\\partial t}\\,.\n\\]\n\n\\end_inset\n\nUsing Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq75\"\nnolink \"false\"\n\n\\end_inset\n\n and recognizing that \n\\begin_inset Formula $\\partial\\tilde{f}/\\partial\\boldsymbol{\\chi}\\equiv\\partial\\tilde{f}/\\partial\\mathbf{x}=\\grad\\tilde{f}$\n\\end_inset\n\n,\n this expression may be rewritten as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{f}=\\frac{\\partial\\tilde{f}}{\\partial t}+\\grad\\tilde{f}\\cdot\\mathbf{v}\\equiv\\frac{D\\tilde{f}\\left(\\mathbf{x},t\\right)}{Dt}\\,.\\label{eq77}\n\\end{equation}\n\n\\end_inset\n\nWe used the operator \n\\begin_inset Formula $D\\left(\\cdot\\right)/Dt$\n\\end_inset\n\n to emphasize that this expression represents the material time derivative of \n\\begin_inset Formula $f$\n\\end_inset\n\n in the spatial frame.\n\\end_layout\n\n\\begin_layout Standard\nFor a given particle \n\\begin_inset Formula $P$\n\\end_inset\n\n,\n the actual value of the function \n\\begin_inset Formula $f$\n\\end_inset\n\n must be the same in the material and spatial frames,\n thus \n\\begin_inset Formula $f=\\hat{f}\\left(\\mathbf{X},t\\right)=\\tilde{f}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n.\n Similarly,\n its material time derivative must have the same value in either frame,\n \n\\begin_inset Formula $\\dot{f}=\\frac{\\partial\\hat{f}}{\\partial t}=\\frac{D\\tilde{f}}{Dt}$\n\\end_inset\n\n.\n For notational convenience,\n we drop the caret and tilde symbols and assume from the context that \n\\begin_inset Formula $f\\left(\\mathbf{X},t\\right)\\equiv\\hat{f}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $f\\left(\\mathbf{x},t\\right)\\equiv\\tilde{f}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDeformation Gradient\n\\end_layout\n\n\\begin_layout Standard\nFor a continuum,\n the increment in motion between the neigboring points \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{X}+d\\mathbf{X}$\n\\end_inset\n\n at a given time \n\\begin_inset Formula $t$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\nd\\mathbf{x}=\\boldsymbol{\\chi}\\left(\\mathbf{X}+d\\mathbf{X},t\\right)-\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)=\\left(\\Grad\\boldsymbol{\\chi}\\right)\\cdot d\\mathbf{X}\\,.\\label{eq:motion-gradient}\n\\end{equation}\n\n\\end_inset\n\nSince the differentiation of \n\\begin_inset Formula $\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n is performed with respect to \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n,\n we denote this gradient as \n\\begin_inset Formula $\\Grad\\equiv\\partial\\left(\\cdot\\right)/\\partial\\mathbf{X}$\n\\end_inset\n\n.\n In particular,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}\\equiv\\frac{\\partial\\boldsymbol{\\chi}}{\\partial\\mathbf{X}}=\\Grad\\boldsymbol{\\chi}\\label{eq:deformation-gradient}\n\\end{equation}\n\n\\end_inset\n\nis known as the \n\\emph on\ndeformation gradient\n\\emph default\n.\n It follows from this relationship that a material element \n\\begin_inset Formula $d\\mathbf{X}$\n\\end_inset\n\n of solid constituent deforms into the material element \n\\begin_inset Formula $d\\mathbf{x}$\n\\end_inset\n\n at the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n such that \n\\begin_inset Formula $d\\mathbf{x}=\\mathbf{F}\\cdot d\\mathbf{X}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe length of the element,\n \n\\begin_inset Formula $ds$\n\\end_inset\n\n,\n is given by \n\\begin_inset Formula \n\\begin{equation}\nds^{2}=d\\mathbf{x}\\cdot d\\mathbf{x}=\\left(\\mathbf{F}\\cdot d\\mathbf{X}\\right)\\cdot\\left(\\mathbf{F}\\cdot d\\mathbf{X}\\right)=d\\mathbf{X}\\cdot\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}\\right)\\cdot d\\mathbf{X}\\equiv d\\mathbf{X}\\cdot\\mathbf{C}\\cdot d\\mathbf{X}\\label{eq82}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{C}=\\mathbf{F}^{T}\\cdot\\mathbf{F}\\label{eq83}\n\\end{equation}\n\n\\end_inset\n\n is the \n\\shape italic\nright Cauchy-Green deformation tensor\n\\shape default\n.\n Note that \n\\begin_inset Formula $\\mathbf{C}^{T}=\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}\\right)^{T}=\\mathbf{F}^{T}\\cdot\\mathbf{F}=\\mathbf{C}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n is symmetric.\n \n\\end_layout\n\n\\begin_layout Standard\nLet the material element \n\\begin_inset Formula $d\\mathbf{X}$\n\\end_inset\n\n be of length \n\\begin_inset Formula $dS$\n\\end_inset\n\n and directed along the unit normal \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $d\\mathbf{X}=dS\\mathbf{n}_{r}$\n\\end_inset\n\n.\n Upon deformation,\n we have \n\\begin_inset Formula $d\\mathbf{x}=\\mathbf{F}\\cdot d\\mathbf{X}=dS\\mathbf{F}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n and the length of the element in this deformed configuration is given by \n\\begin_inset Formula $ds$\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\nds^{2}=d\\mathbf{x}\\cdot d\\mathbf{x}=\\left(dS\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\cdot\\left(dS\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)=dS^{2}\\mathbf{n}_{r}\\cdot\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}\\right)\\cdot\\mathbf{n}_{r}=dS^{2}\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}\\label{eq84}\n\\end{equation}\n\n\\end_inset\n\n The length of the element in the current configuration relative to its length in the reference configuration is given by \n\\begin_inset Formula \n\\begin{equation}\n\\frac{ds}{dS}=\\sqrt{\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}}\\equiv\\lambda_{n}\\label{eq85}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n is the relative change in length of the material element \n\\begin_inset Formula $d\\mathbf{X}$\n\\end_inset\n\n which is oriented along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n is known as the \n\\shape italic\nstretch ratio\n\\shape default\n along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $\\lambda_{n}^{2}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the normal component of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n.\n If we let \n\\begin_inset Formula $d\\mathbf{x}=ds\\,\\mathbf{n}$\n\\end_inset\n\n,\n it follows from \n\\begin_inset Formula $d\\mathbf{x}=\\mathbf{F}\\cdot d\\mathbf{X}$\n\\end_inset\n\n that \n\\begin_inset Formula \n\\begin{equation}\n\\lambda_{n}\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{n}_{r}\\label{eq85a}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNow consider two material elements \n\\begin_inset Formula $d\\mathbf{X}_{m}=dS_{m}\\mathbf{m}_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{X}_{n}=dS_{n}\\mathbf{n}_{r}$\n\\end_inset\n\n.\n They each transform according to \n\\begin_inset Formula $d\\mathbf{x}_{m}=\\mathbf{F}\\cdot d\\mathbf{X}_{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{x}_{n}=\\mathbf{F}\\cdot d\\mathbf{X}_{n}$\n\\end_inset\n\n and their dot product produces\n\\begin_inset Formula \n\\begin{equation}\nd\\mathbf{x}_{m}\\cdot d\\mathbf{x}_{n}=\\left(\\mathbf{F}\\cdot d\\mathbf{X}_{m}\\right)\\cdot\\left(\\mathbf{F}\\cdot d\\mathbf{X}_{n}\\right)=d\\mathbf{X}_{n}\\cdot\\mathbf{C}\\cdot d\\mathbf{X}_{m}\\label{eq85b}\n\\end{equation}\n\n\\end_inset\n\nor equivalently,\n\\begin_inset Formula \n\\begin{equation}\n\\lambda_{m}\\lambda_{n}\\mathbf{m}\\cdot\\mathbf{n}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{m}_{r}\\,,\\quad\\lambda_{m}=\\frac{ds_{m}}{dS_{m}},\\,\\lambda_{n}=\\frac{ds_{n}}{dS_{n}}\\label{eq85c}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $d\\mathbf{x}_{m}=ds_{m}\\mathbf{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{x}_{n}=ds_{n}\\mathbf{n}$\n\\end_inset\n\n.\n Note that we may write \n\\begin_inset Formula $\\mathbf{m}\\cdot\\mathbf{n}=\\cos\\theta=\\sin\\gamma$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the angle between \n\\begin_inset Formula $d\\mathbf{x}_{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{x}_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma=\\frac{\\pi}{2}-\\theta$\n\\end_inset\n\n is its complementary angle (Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig17\"\nnolink \"false\"\n\n\\end_inset\n\n),\n so that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{m}_{r}=\\lambda_{m}\\lambda_{n}\\sin\\gamma\\,.\\label{eq85d}\n\\end{equation}\n\n\\end_inset\n\nNow consider the case where \n\\begin_inset Formula $\\mathbf{m}_{r}\\cdot\\mathbf{n}_{r}=0$\n\\end_inset\n\n,\n i.e.,\n the material elements \n\\begin_inset Formula $d\\mathbf{X}_{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{X}_{n}$\n\\end_inset\n\n are orthogonal,\n so that \n\\begin_inset Formula $\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{m}_{r}=C_{mn}$\n\\end_inset\n\n where \n\\begin_inset Formula $C_{mn}$\n\\end_inset\n\n is a shear component of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n.\n It follows from this result that \n\\begin_inset Formula $C_{mn}$\n\\end_inset\n\n represents a combined measure of the change in angle between \n\\begin_inset Formula $\\mathbf{m}_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n,\n as well as the stretching of elements \n\\begin_inset Formula $d\\mathbf{X}_{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{X}_{n}$\n\\end_inset\n\n.\n The angle \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n,\n which represents the change in angle between initially orthogonal line elements along \n\\begin_inset Formula $\\mathbf{m}_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n,\n is called the \n\\emph on\nengineering shear strain\n\\emph default\n.\n In particular,\n in the range of small strains and rotations,\n \n\\begin_inset Formula $\\lambda_{m}\\approx1$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\lambda_{n}\\approx1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\sin\\gamma\\approx\\gamma$\n\\end_inset\n\n,\n so that \n\\begin_inset Formula $C_{mn}\\approx\\gamma$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigShearStrain.png\n\tlyxscale 25\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nChange of angle between filaments.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig17\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAccording to the \n\\emph on\npolar decomposition theorem\n\\emph default\n,\n the deformation gradient may be decomposed uniquely into\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}=\\mathbf{R}\\cdot\\mathbf{U}=\\mathbf{V}\\cdot\\mathbf{R}\\label{eq:polar-decomposition-RU}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n is a proper orthogonal transformation (a rotation,\n with \n\\begin_inset Formula $\\det\\mathbf{R}=+1$\n\\end_inset\n\n),\n \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n is a symmetric tensor called the \n\\emph on\nright\n\\emph default\n (or \n\\emph on\nmaterial\n\\emph default\n) \n\\emph on\nstretch tensor\n\\emph default\n,\n and \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is a symmetric tensor called the \n\\emph on\nleft stretch tensor\n\\emph default\n.\n It follows from this decomposition that \n\\begin_inset Formula $d\\mathbf{x}=\\mathbf{F}\\cdot d\\mathbf{X}=\\mathbf{R}\\cdot\\left(\\mathbf{U}\\cdot d\\mathbf{X}\\right)$\n\\end_inset\n\n may be viewed as a stretching of \n\\begin_inset Formula $d\\mathbf{X}$\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n followed by a rotation by \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n,\n whereas \n\\begin_inset Formula $d\\mathbf{x}=\\mathbf{V}\\cdot\\left(\\mathbf{R}\\cdot d\\mathbf{X}\\right)$\n\\end_inset\n\n is the rotation of \n\\begin_inset Formula $d\\mathbf{X}$\n\\end_inset\n\n,\n followed by stretching by \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{F}^{T}\\cdot\\mathbf{F}=\\mathbf{U}^{2}$\n\\end_inset\n\n,\n it follows that the eigenvalues of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n are the square of the eigenvalues of \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n.\n Thus,\n these eigenvalues provide a measure of the stretch,\n not a measure of the rotation.\n \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n also share the same eigenvectors.\n Thus,\n if \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n is given by its spectral representation,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{C}=\\lambda_{1}^{2}\\mathbf{u}_{1}\\otimes\\mathbf{u}_{1}+\\lambda_{2}^{2}\\mathbf{u}_{2}\\otimes\\mathbf{u}_{2}+\\lambda_{3}^{2}\\mathbf{u}_{3}\\otimes\\mathbf{u}_{3}\\label{eq:85-2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{u}_{i}$\n\\end_inset\n\n's are its eigenvectors,\n then \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n may be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{U}=\\lambda_{1}\\mathbf{u}_{1}\\otimes\\mathbf{u}_{1}+\\lambda_{2}\\mathbf{u}_{2}\\otimes\\mathbf{u}_{2}+\\lambda_{3}\\mathbf{u}_{3}\\otimes\\mathbf{u}_{3}\\label{eq:85-3}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{R}=\\mathbf{F}\\cdot\\mathbf{U}^{-1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{U}^{-1}=\\lambda_{1}^{-1}\\mathbf{u}_{1}\\otimes\\mathbf{u}_{1}+\\lambda_{2}^{-1}\\mathbf{u}_{2}\\otimes\\mathbf{u}_{2}+\\lambda_{3}^{-1}\\mathbf{u}_{3}\\otimes\\mathbf{u}_{3}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe motion can also be conveniently represented in terms of a \n\\shape italic\ndisplacement\n\\shape default\n function \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n in a material frame of reference such that \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\mathbf{X}+\\mathbf{u}\\left(\\mathbf{X},t\\right)\\label{eq78}\n\\end{equation}\n\n\\end_inset\n\n Then the deformation gradient for this motion is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}=\\frac{\\partial\\mathbf{x}}{\\partial\\mathbf{X}}=\\Grad\\mathbf{x}=\\mathbf{I}+\\Grad\\mathbf{u}\\label{eq81}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{array}{cl}\n\\mathbf{C} & =\\mathbf{F}^{T}\\cdot\\mathbf{F}=\\left(\\mathbf{I}+\\Grad\\mathbf{u}\\right)^{T}\\cdot\\left(\\mathbf{I}+\\Grad\\mathbf{u}\\right)\\\\\n & =\\mathbf{I}+\\Grad\\mathbf{u}+\\Grad^{T}\\mathbf{u}+\\left(\\Grad^{T}\\mathbf{u}\\right)\\cdot\\left(\\Grad\\mathbf{u}\\right)\n\\end{array}\\label{eq83-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n we can also define the \n\\shape italic\nLagrangian strain tensor\n\\shape default\n \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n (also known as the \n\\emph on\nGreen-Lagrange\n\\emph default\n strain) from the above relation such that \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{I}+2\\mathbf{E}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{E}=\\frac{1}{2}\\left(\\mathbf{C}-\\mathbf{I}\\right)=\\frac{1}{2}\\left[\\Grad\\mathbf{u}+\\Grad^{T}\\mathbf{u}+\\left(\\Grad^{T}\\mathbf{u}\\right)\\cdot\\left(\\Grad\\mathbf{u}\\right)\\right]\\label{eq:E-C-relation}\n\\end{equation}\n\n\\end_inset\n\nIn the case of rigid body motion (translation and/or rotation,\n \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{I}$\n\\end_inset\n\n),\n the Lagrangian strain tensor is equal to the null tensor.\n Just as in the case of the stress tensor,\n the strain tensors \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n also have strain invariants,\n principal normal strains and principal directions of normal strain,\n maximum shear strains and directions of maximum shear strain.\n If \n\\begin_inset Formula $\\lambda^{2}$\n\\end_inset\n\n is an eigenvalue of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n is its associated eigenvector,\n then \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n is also an eigenvector of \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n since \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{E}\\cdot\\mathbf{v}=\\frac{1}{2}\\left(\\mathbf{C}-\\mathbf{I}\\right)\\cdot\\mathbf{v}=\\frac{1}{2}\\left(\\mathbf{C}\\cdot\\mathbf{v}-\\mathbf{v}\\right)=\\frac{1}{2}\\left(\\lambda^{2}-1\\right)\\mathbf{v}\\label{eq84-1}\n\\end{equation}\n\n\\end_inset\n\nTherefore the corresponding eigenvalue of \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is \n\\begin_inset Formula $\\left(\\lambda^{2}-1\\right)/2$\n\\end_inset\n\n,\n which also established the relation between the normal components of \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n.\n The shear components of \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n may be related to those of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\[\nE_{mn}=\\mathbf{m}\\cdot\\mathbf{E}\\cdot\\mathbf{n}=\\frac{1}{2}\\mathbf{m}\\cdot\\mathbf{C}\\cdot\\mathbf{n}=\\frac{1}{2}C_{mn}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n are orthogonal unit vectors.\n Therefore,\n the interpretation of \n\\begin_inset Formula $E_{mn}$\n\\end_inset\n\n is similar to that of \n\\begin_inset Formula $C_{mn}$\n\\end_inset\n\n,\n with a factor of \n\\begin_inset Formula $1/2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn the limit of small displacement gradients (small strains and rotations,\n \n\\begin_inset Formula $\\left\\Vert \\Grad\\mathbf{u}\\right\\Vert \\ll1$\n\\end_inset\n\n),\n the expression for the Lagrangian strain tensor in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:E-C-relation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to the \n\\emph on\ninfinitesimal strain tensor\n\\emph default\n,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\varepsilon}=\\frac{1}{2}\\left(\\grad\\mathbf{u}+\\grad^{T}\\mathbf{u}\\right)\\,.\\label{eq46}\n\\end{equation}\n\n\\end_inset\n\nNote that this strain tensor is also the linearization of the Lagrangian strain tensor,\n \n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{E}\\left[\\mathbf{u}\\right]=\\mathbf{F}^{T}\\cdot\\boldsymbol{\\varepsilon}\\cdot\\mathbf{F}\\,.\\label{eq47}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nRate of Deformation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Rate-of-Deformation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material time derivative of the deformation gradient may be denoted by \n\\begin_inset Formula $\\dot{\\mathbf{F}}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\mathbf{F}=\\Grad\\boldsymbol{\\chi}$\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\dot{\\mathbf{F}}=\\Grad\\dot{\\boldsymbol{\\chi}}=\\Grad\\mathbf{v}$\n\\end_inset\n\n.\n Using the chain rule,\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{F}}=\\frac{\\partial\\mathbf{v}}{\\partial\\mathbf{X}}=\\frac{\\partial\\mathbf{v}}{\\partial\\mathbf{x}}\\cdot\\frac{\\partial\\mathbf{x}}{\\partial\\mathbf{X}}=\\grad\\mathbf{v}\\cdot\\mathbf{F}\\label{eq85e}\n\\end{equation}\n\n\\end_inset\n\nor simply\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{F}}=\\mathbf{L}\\cdot\\mathbf{F}\\label{eq85f}\n\\end{equation}\n\n\\end_inset\n\nDifferentiating \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{F}^{T}\\cdot\\mathbf{F}$\n\\end_inset\n\n with respect to time,\n and making use of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq85e\"\nnolink \"false\"\n\n\\end_inset\n\n produces\n\\begin_inset Formula \n\\begin{equation}\n\\mbox{\\dot{\\mathbf{C}}}=\\dot{\\mathbf{F}}^{T}\\cdot\\mathbf{F}+\\mathbf{F}^{T}\\cdot\\dot{\\mathbf{F}}=\\mathbf{F}^{T}\\cdot\\left(\\mathbf{L}^{T}+\\mathbf{L}\\right)\\cdot\\mathbf{F}\\label{eq85g}\n\\end{equation}\n\n\\end_inset\n\n or equivalently,\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{C}}=2\\mathbf{F}^{T}\\cdot\\mathbf{D}\\cdot\\mathbf{F}\\label{eq85h}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n is the rate of deformation tensor.\n This result may now be used in the time differentiation of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq85d\"\nnolink \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}_{r}\\cdot\\dot{\\mathbf{C}}\\cdot\\mathbf{m}_{r}=\\left(\\dot{\\lambda}_{m}\\lambda_{n}+\\lambda_{m}\\dot{\\lambda}_{n}\\right)\\sin\\gamma+\\lambda_{m}\\lambda_{n}\\dot{\\gamma}\\cos\\gamma\\label{eq85i}\n\\end{equation}\n\n\\end_inset\n\nwhere the left-hand-side reduces to \n\\begin_inset Formula $\\mathbf{n}_{r}\\cdot\\dot{\\mathbf{C}}\\cdot\\mathbf{m}_{r}=2\\left(\\mathbf{F}\\cdot\\mathbf{m}_{r}\\right)\\cdot\\mathbf{D}\\cdot\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)=2\\lambda_{m}\\lambda_{n}\\mathbf{m}\\cdot\\mathbf{D}\\cdot\\mathbf{n}$\n\\end_inset\n\n.\n Substituting this result above produces\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}\\cdot\\mathbf{D}\\cdot\\mathbf{n}=\\frac{1}{2}\\left(\\frac{\\dot{\\lambda}_{m}}{\\lambda_{m}}+\\frac{\\dot{\\lambda}_{n}}{\\lambda_{n}}\\right)\\sin\\gamma+\\frac{\\dot{\\gamma}}{2}\\cos\\gamma\\label{eq85j}\n\\end{equation}\n\n\\end_inset\n\nWe now consider two cases:\n When \n\\begin_inset Formula $\\mathbf{m}=\\mathbf{n}$\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\gamma=\\pi/2$\n\\end_inset\n\n,\n so that \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq85j\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to\n\\begin_inset Formula \n\\begin{equation}\nD_{nn}\\equiv\\mathbf{n}\\cdot\\mathbf{D}\\cdot\\mathbf{n}=\\frac{\\dot{\\lambda}_{n}}{\\lambda_{n}}\\,.\\label{eq:D-normal}\n\\end{equation}\n\n\\end_inset\n\nIn other words,\n the normal component of the rate of deformation tensor along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the rate of stretch per stretch of a differential line element along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\begin_inset Formula $\\mathbf{m}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\gamma=0$\n\\end_inset\n\n,\n so that \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq85j\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to\n\\begin_inset Formula \n\\begin{equation}\nD_{mn}\\equiv\\mathbf{m}\\cdot\\mathbf{D}\\cdot\\mathbf{n}=\\frac{\\dot{\\gamma}}{2}\\,.\\label{eq:D-shear}\n\\end{equation}\n\n\\end_inset\n\nThus,\n the shear component of \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n in the plane containing \n\\begin_inset Formula $\\mathbf{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is equal to half the rate at which the angle between those line elements changes with the deformation.\n The quantity \n\\begin_inset Formula $\\dot{\\gamma}$\n\\end_inset\n\n is called the \n\\emph on\nengineering shear rate\n\\emph default\n,\n or simply the \n\\emph on\nshear rate\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n we note from eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:E-C-relation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq85h\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n that the material time derivative of the Lagrangian strain is related to the rate of deformation via\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{E}}=\\mathbf{F}^{T}\\cdot\\mathbf{D}\\cdot\\mathbf{F}\\,.\\label{eq:Edot-D-relation}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nVolume Deformation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Volume-Deformation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet us show that the deformation of a volume of material \n\\begin_inset Formula $dV_{r}$\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\begin{equation}\ndV=J\\,dV_{r},\\quad J=\\det\\mathbf{F}=\\left(\\det\\mathbf{C}\\right)^{1/2}\\label{eq87}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigReferentialVolume.png\n\tlyxscale 25\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nElemental volume.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:Elemental-volume\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote that \n\\begin_inset Formula $\\det\\mathbf{C}=\\det\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}\\right)=\\det\\mathbf{F}^{T}\\det\\mathbf{F}=\\left(\\det\\mathbf{F}\\right)^{2}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\det\\mathbf{F}=\\left(\\det\\mathbf{C}\\right)^{1/2}$\n\\end_inset\n\n,\n which proves the second equation above.\n To prove the first equation,\n let us define three non-collinear material elements in the undeformed reference state,\n \n\\begin_inset Formula $d\\mathbf{X}_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $d\\mathbf{X}_{2}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $d\\mathbf{X}_{3}$\n\\end_inset\n\n.\n The elemental volume defined by these three elements has the magnitude \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\ndV_{r}=\\left(d\\mathbf{X}_{1}\\times d\\mathbf{X}_{2}\\right)\\cdot d\\mathbf{X}_{3}\\tag{a}\n\\]\n\n\\end_inset\n\n in the reference state,\n and the magnitude \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\ndV=\\left(d\\mathbf{x}_{1}\\times d\\mathbf{x}_{2}\\right)\\cdot d\\mathbf{x}_{3}=\\left(\\mathbf{F}\\cdot d\\mathbf{X}_{1}\\times\\mathbf{F}\\cdot d\\mathbf{X}_{2}\\right)\\cdot\\mathbf{F}\\cdot d\\mathbf{X}_{3}\\tag{b}\n\\]\n\n\\end_inset\n\nin the deformed state.\n Let the elements \n\\begin_inset Formula $d\\mathbf{X}_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $d\\mathbf{X}_{2}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $d\\mathbf{X}_{3}$\n\\end_inset\n\n be aligned with the orthogonal coordinate axes such that \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nd\\mathbf{X}_{1}=dS_{1}\\mathbf{e}_{1}\\quad d\\mathbf{X}_{2}=dS_{2}\\mathbf{e}_{2}\\quad d\\mathbf{X}_{3}=dS_{3}\\mathbf{e}_{3}\\tag{c}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{e}_{i}\\cdot\\mathbf{e}_{j}=\\delta_{ij}$\n\\end_inset\n\n,\n then we find from (a) that \n\\begin_inset Formula $dV_{r}=dS_{1}dS_{2}dS_{3}$\n\\end_inset\n\n.\n From (b) we get that \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\ndV=\\left(\\mathbf{F}\\cdot\\mathbf{e}_{1}\\times\\mathbf{F}\\cdot\\mathbf{e}_{2}\\right)\\cdot\\mathbf{F}\\cdot\\mathbf{e}_{3}\\,dS_{1}dS_{2}dS_{3}=\\det\\mathbf{F}\\,dV_{r}\\tag{d}\n\\]\n\n\\end_inset\n\naccording to the definition of the determinant given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsubsec:determinant\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn a compressible material,\n the density will change with changes in volume.\n If an elemental volume of mass \n\\begin_inset Formula $dm$\n\\end_inset\n\n occupies a volume \n\\begin_inset Formula $dV_{r}$\n\\end_inset\n\n in some \n\\emph on\nreference configuration\n\\emph default\n,\n then the density in the current configuration is given by \n\\begin_inset Formula \n\\begin{equation}\n\\rho=\\frac{dm}{dV}=\\frac{dm}{dV_{r}}\\frac{dV_{r}}{dV}=\\frac{\\rho_{r}}{J}\\label{eq88}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n represents the density of the material in the reference configuration,\n which is a constant.\n The result of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq88\"\nnolink \"false\"\n\n\\end_inset\n\n is the solution to the axiom of mass balance.\n To prove that point,\n we use the kinematic identity\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial J}{\\partial\\mathbf{F}}=J\\mathbf{F}^{-T}\\label{eq89-1}\n\\end{equation}\n\n\\end_inset\n\nto express the material time derivative of \n\\begin_inset Formula $J$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}=\\frac{\\partial J}{\\partial\\mathbf{F}}:\\dot{\\mathbf{F}}\\label{eq90-1}\n\\end{equation}\n\n\\end_inset\n\nRecalling that \n\\begin_inset Formula $\\dot{\\mathbf{F}}=\\mathbf{L}\\cdot\\mathbf{F}$\n\\end_inset\n\n,\n it follows that\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}=J\\mathbf{F}^{-T}:\\mathbf{L}\\cdot\\mathbf{F}=J\\mathbf{I}:\\mathbf{L}=J\\tr\\mathbf{L}=J\\divg\\mathbf{v}\\label{eq91-1}\n\\end{equation}\n\n\\end_inset\n\n or\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\mathbf{v}=\\frac{\\dot{J}}{J}\\label{eq92-1}\n\\end{equation}\n\n\\end_inset\n\nNow,\n the axiom of mass balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten as\n\\begin_inset Formula \n\\begin{equation}\n\\frac{D\\rho}{Dt}+\\rho\\divg\\mathbf{v}=\\dot{\\rho}+\\frac{\\rho}{J}\\dot{J}=0\\label{eq93}\n\\end{equation}\n\n\\end_inset\n\nMultiplying this equation across by \n\\begin_inset Formula $J$\n\\end_inset\n\n,\n we find that the resulting expression is equivalent to\n\\begin_inset Formula \n\\begin{equation}\n\\frac{D\\left(\\rho J\\right)}{Dt}=0\\label{eq93b}\n\\end{equation}\n\n\\end_inset\n\nIntegrating this expression produces \n\\begin_inset Formula $\\rho J=\\rho_{r}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n when \n\\begin_inset Formula $J=1$\n\\end_inset\n\n.\n This result reproduces \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq88\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n since we know that \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{R}\\cdot\\mathbf{U}$\n\\end_inset\n\n according to \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:polar-decomposition-RU\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that\n\\begin_inset Formula \n\\[\nJ=\\det\\mathbf{F}=\\left(\\det\\mathbf{R}\\right)\\left(\\det\\mathbf{U}\\right)=\\det\\mathbf{U}=\\lambda_{1}\\lambda_{2}\\lambda_{3}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{i}$\n\\end_inset\n\n are the principal stretches of \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nArea Deformation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Area-Deformation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn general,\n we can express the position of a material point \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n as a function of parametric coordinates \n\\begin_inset Formula $\\eta^{i}$\n\\end_inset\n\n (i=1,2,3),\n thus \n\\begin_inset Formula $\\mathbf{X}=\\mathbf{X}\\left(\\eta^{i}\\right)$\n\\end_inset\n\n.\n Then,\n a line element in the reference configuration is given by\n\\begin_inset Formula \n\\begin{equation}\nd\\mathbf{X}=\\frac{\\partial\\mathbf{X}}{\\partial\\eta^{i}}d\\eta^{i}\\equiv\\mathbf{G}_{i}d\\eta^{i}\\,,\\label{eq:covariant-basis-material}\n\\end{equation}\n\n\\end_inset\n\nwhere we use the implicit (Einstein) summation convention of indicial notation.\n Here,\n the vectors \n\\begin_inset Formula $\\mathbf{G}_{i}$\n\\end_inset\n\n are called the covariant basis vectors of \n\\begin_inset Formula $\\mathbf{X}\\left(\\eta^{i}\\right)$\n\\end_inset\n\n,\n in the material frame.\n We may also express the motion in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:motion-chi\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as a function of the same parametric coordinates,\n such that the corresponding line element in the spatial frame is\n\\begin_inset Formula \n\\begin{equation}\nd\\mathbf{x}=\\frac{\\partial\\boldsymbol{\\chi}}{\\partial\\eta^{i}}d\\eta^{i}\\equiv\\mathbf{g}_{i}d\\eta^{i}\\,,\\label{eq:covariant-basis-spatial}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{g}_{i}$\n\\end_inset\n\n's represent the covariant basis vector at \n\\begin_inset Formula $\\mathbf{x}=\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n in the spatial frame.\n Since \n\\begin_inset Formula $d\\mathbf{x}$\n\\end_inset\n\n is related to \n\\begin_inset Formula $d\\mathbf{X}$\n\\end_inset\n\n by the deformation gradient as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:motion-gradient\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n or \n\\begin_inset Formula $d\\mathbf{x}=\\mathbf{F}\\cdot d\\mathbf{X}$\n\\end_inset\n\n,\n it can be shown that the deformation gradient is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}=\\mathbf{g}_{i}\\otimes\\mathbf{G}^{i}\\,,\\label{eq:defgrad-cov-cont}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{G}^{i}$\n\\end_inset\n\n's represent the contravariant basis vector of \n\\begin_inset Formula $\\mathbf{X}\\left(\\eta^{i}\\right)$\n\\end_inset\n\n,\n in the material frame.\n Covariant and contravariant basis vectors satisfy \n\\begin_inset Formula $\\mathbf{G}_{i}\\cdot\\mathbf{G}^{j}=\\mathbf{G}^{i}\\cdot\\mathbf{G}_{j}=\\delta_{j}^{i}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\delta_{j}^{i}$\n\\end_inset\n\n is the Kronecker delta.\n The same is true in the spatial frame.\n The expression for \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:defgrad-cov-cont\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n emphasizes that \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is a \n\\emph on\ntwo-point tensor\n\\emph default\n:\n It has one leg in the material frame (\n\\begin_inset Formula $\\mathbf{G}^{i}$\n\\end_inset\n\n) and the other in the spatial frame (\n\\begin_inset Formula $\\mathbf{g}_{i}$\n\\end_inset\n\n).\n It follows from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:defgrad-cov-cont\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n that covariant basis vectors in the material and spatial frames are related by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}\\cdot\\mathbf{G}_{j}=\\mathbf{g}_{j}\\,.\\label{eq:cov-basis-xformation}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNow consider that an elemental volume \n\\begin_inset Formula $dV_{r}$\n\\end_inset\n\n in the reference configuration can be evaluated from\n\\begin_inset Formula \n\\[\ndV_{r}=\\left(\\mathbf{G}_{1}d\\eta^{1}\\times\\mathbf{G}_{2}d\\eta^{2}\\right)\\cdot\\mathbf{G}_{3}d\\eta^{3}\\equiv d\\mathbf{A}\\cdot\\mathbf{G}_{3}d\\eta^{3}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $d\\mathbf{A}$\n\\end_inset\n\n is the vectorial representation of the elemental directed area \n\\begin_inset Formula $\\mathbf{G}_{1}d\\eta^{1}\\times\\mathbf{G}_{2}d\\eta^{2}$\n\\end_inset\n\n.\n Similarly,\n the elemental volume \n\\begin_inset Formula $dV$\n\\end_inset\n\n in the current configuration is given by\n\\begin_inset Formula \n\\[\ndV=\\left(\\mathbf{g}_{1}d\\eta^{1}\\times\\mathbf{g}_{2}d\\eta^{2}\\right)\\cdot\\mathbf{g}_{3}d\\eta^{3}\\equiv d\\mathbf{a}\\cdot\\mathbf{g}_{3}d\\eta^{3}\\,.\n\\]\n\n\\end_inset\n\nUsing eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq87\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cov-basis-xformation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we may rewrite this expression as\n\\begin_inset Formula \n\\[\ndV=d\\mathbf{a}\\cdot\\mathbf{F}\\cdot\\mathbf{G}_{3}d\\eta^{3}=JdV_{r}=Jd\\mathbf{A}\\cdot\\mathbf{G}_{3}d\\eta^{3}\\,.\n\\]\n\n\\end_inset\n\nFrom this general expression,\n we can find the relation between \n\\begin_inset Formula $d\\mathbf{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $d\\mathbf{A}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nd\\mathbf{a}=J\\mathbf{F}^{-T}\\cdot d\\mathbf{A}\\,,\\label{eq:Nanson-formula}\n\\end{equation}\n\n\\end_inset\n\nwhich is known as Nanson's formula \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"false\"\n\n\\end_inset\n\n.\n It relates the change in surface area,\n and the reorientation of the surface normal,\n between the referential area \n\\begin_inset Formula $d\\mathbf{A}$\n\\end_inset\n\n and its corresponding area \n\\begin_inset Formula $d\\mathbf{a}$\n\\end_inset\n\n in the current configuration.\n\\end_layout\n\n\\begin_layout Standard\nUsing the general expression for the deformation gradient in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:defgrad-cov-cont\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that the right Cauchy-Green tensor is given by\n\\begin_inset Formula \n\\[\n\\mathbf{C}=\\mathbf{F}^{T}\\cdot\\mathbf{F}=g_{ij}\\mathbf{G}^{i}\\otimes\\mathbf{G}^{j}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $g_{ij}=\\mathbf{g}_{i}\\cdot\\mathbf{g}_{j}$\n\\end_inset\n\n is called the covariant metric tensor in the spatial frame.\n\\end_layout\n\n\\begin_layout Section\nHyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Hyperelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nConstitutive Restrictions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constitutive-Restrictions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNow,\n we need to make constitutive assumptions on the dependent variables which appears in Clausius-Duhem inequality,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Clausius-Duhem-inequality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n namely,\n \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n.\n These functions could depend on a number of \n\\emph on\nstate variables\n\\emph default\n,\n such as strain,\n rate of deformation,\n temperature,\n temperature gradient,\n etc.\n For example,\n the pressure in a gas is generally assumed to depend on temperature and density,\n so it would follow that \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n for a gas should exhibit the same dependencies.\n \n\\end_layout\n\n\\begin_layout Standard\nFor solid materials we have found that that the mass density is dependent on the strain (eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq88\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\rho=\\rho_{r}\\left(\\det\\mathbf{C}\\right)^{-1/2}=\\rho_{r}/J$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is a constant);\n so long as a dependence on the strain is provided,\n an explicit dependency on mass density is not required.\n\\end_layout\n\n\\begin_layout Standard\nFor the purpose of modeling elastic solids under isothermal conditions,\n we would like to restrict our constitutive models to be independent of the temperature or its gradient.\n For an elastic solid,\n the rate of load application does not have an effect;\n therefore,\n there should be no dependency on the rate of deformation \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n.\n Furthermore,\n for an elastic solid,\n upon removal of the loading,\n deformations should disappear completely.\n This implies that there should be no dependence on the history (or path) of loading,\n only on the current state of loading.\n\\end_layout\n\n\\begin_layout Standard\nGiven these restrictions,\n we assume that the non-observable functions of state \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n depend only on the observable solid matrix strain,\n such as \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n.\n Based on the principle of equipresence,\n all functions of state depend only on \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n.\n In particular,\n since \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is the only function of state which is differentiated with respect to time in the Clausius-Duhem inequality,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Clausius-Duhem-inequality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we express it explicitly as\n\\begin_inset Formula \n\\begin{equation}\n\\psi=\\psi\\left(\\mathbf{E}\\right)\\,.\\label{eq95-1}\n\\end{equation}\n\n\\end_inset\n\nGiven this dependence,\n we can use the chain rule of differentiation to get \n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\psi}=\\frac{d\\psi}{d\\mathbf{E}}:\\dot{\\mathbf{E}}=\\frac{d\\psi}{d\\mathbf{E}}:\\mathbf{F}^{T}\\cdot\\mathbf{D}\\cdot\\mathbf{F}\\label{eq96-1}\n\\end{equation}\n\n\\end_inset\n\nwhere we have made use of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Edot-D-relation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Making use of the identity \n\\begin_inset Formula \n\\begin{equation}\n\\frac{d\\psi}{d\\mathbf{E}}:\\mathbf{F}^{T}\\cdot\\mathbf{D}\\cdot\\mathbf{F}=\\mathbf{F}\\cdot\\frac{d\\psi}{d\\mathbf{E}}\\cdot\\mathbf{F}^{T}:\\mathbf{D}\\label{eq100-1}\n\\end{equation}\n\n\\end_inset\n\n we find that the entropy inequality reduces to \n\\begin_inset Formula \n\\begin{equation}\n-\\rho\\eta\\dot{\\theta}-\\frac{1}{\\theta}\\mathbf{q}\\cdot\\grad\\theta+\\left(\\boldsymbol{\\sigma}-\\rho\\mathbf{F}\\cdot\\frac{d\\psi}{d\\mathbf{E}}\\cdot\\mathbf{F}^{T}\\right):\\mathbf{D}\\geqslant0\\label{eq101-1}\n\\end{equation}\n\n\\end_inset\n\nThis inequality must hold for arbitrary processes,\n i.e.,\n for arbitrary changes in \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\dot{\\theta}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\grad\\theta$\n\\end_inset\n\n (all of which are independent,\n observable variables of state),\n under our self-imposed constraint that \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n only depend on the state variable \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n.\n For example,\n since the temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n in the leftmost term of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq101-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may either increase or decrease at various times in any process,\n the sign of \n\\begin_inset Formula $\\dot{\\theta}$\n\\end_inset\n\n may be variably positive or negative.\n However,\n the sign of the product \n\\begin_inset Formula $-\\rho\\eta\\dot{\\theta}$\n\\end_inset\n\n may not be set strictly positive since \n\\begin_inset Formula $\\rho\\eta$\n\\end_inset\n\n does not depend on \n\\begin_inset Formula $\\dot{\\theta}$\n\\end_inset\n\n.\n The same argument applies to the remaining terms in the entropy inequality,\n each of which may vary independently of other terms.\n Therefore,\n the only way that the entropy inequality may be satisfied for arbitrary processes,\n given our choice of state variables,\n is to have each term be independently equal to zero.\n Thus,\n the entropy inequality requires that \n\\begin_inset Formula \n\\begin{equation}\n\\eta=0\\label{eq102-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\rho\\mathbf{F}\\cdot\\frac{d\\psi}{d\\mathbf{E}}\\cdot\\mathbf{F}^{T}\\label{eq103-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{q}=\\mathbf{0}\\label{eq105-1}\n\\end{equation}\n\n\\end_inset\n\nEquation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq102-1\"\nnolink \"false\"\n\n\\end_inset\n\n shows that the entropy must be zero for our choice of state variable,\n whereas Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq103-1\"\nnolink \"false\"\n\n\\end_inset\n\n is the fundamental constraint we seek for formulating constitutive relations in elasticity problems.\n The constraint of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq105-1\"\nnolink \"false\"\n\n\\end_inset\n\n implies that isothermal processes must be adiabatic,\n as there cannot be heat flow in such problems.\n\\end_layout\n\n\\begin_layout Standard\nLet the Helmholtz free energy per unit \n\\shape italic\nreference\n\\shape default\n volume (\n\\shape italic\nenergy density\n\\shape default\n) be denoted by \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{E}\\right)\\equiv\\rho_{r}\\psi\\left(\\mathbf{E}\\right)\\label{eq106-1}\n\\end{equation}\n\n\\end_inset\n\n Then,\n using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq88\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and recalling that \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is constant,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq103-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=J^{-1}\\mathbf{F}\\cdot\\frac{d\\Psi_{r}}{d\\mathbf{E}}\\cdot\\mathbf{F}^{T}\\,.\\label{eq:hyperelastic-stress-E}\n\\end{equation}\n\n\\end_inset\n\nThis formula represents the constraint imposed by the second law of thermodynamics on the formulation of a constitutive relation for an elastic solid.\n For historical reasons,\n this formula is described as the \n\\emph on\nhyperelasticity\n\\emph default\n constraint.\n The method described for obtaining this formula was first proposed by Coleman and Noll in 1963 \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Coleman63\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Prior to that formulation,\n other constitutive relations had been proposed for elastic solids (such as hypoelasticity),\n though it is now believed that the only thermodynamically valid formulation is the one given here.\n \n\\end_layout\n\n\\begin_layout Standard\nIn hyperelasticity theory,\n since the free energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n only depends on the strain,\n we may also call it the \n\\emph on\nstrain energy density\n\\emph default\n.\n A common alternative symbol for the strain energy density is \n\\begin_inset Formula $W\\left(\\mathbf{E}\\right)$\n\\end_inset\n\n.\n Since it is common to include multiple state variables in a general formulation of \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n for a material,\n the derivative \n\\begin_inset Formula $d\\Psi_{r}/d\\mathbf{E}$\n\\end_inset\n\n appearing in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq103-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is often written as a partial derivative,\n \n\\begin_inset Formula $\\partial\\Psi_{r}/\\partial\\mathbf{E}$\n\\end_inset\n\n.\n It is also common to use \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n instead of \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n as a suitable alternative measure of strain under finite deformation and rotation.\n In that case,\n based on eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:E-C-relation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the expression of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq103-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=2J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{C}}\\cdot\\mathbf{F}^{T}\\,.\\label{eq:hyperelastic-stress-C}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOther Stress Tensors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Other-Stress-Tensors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reaction force \n\\begin_inset Formula $\\mathbf{f}$\n\\end_inset\n\n exerted on a surface \n\\begin_inset Formula $S$\n\\end_inset\n\n whose outward normal is \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\[\n\\mathbf{f}=\\int_{S}\\mathbf{t}\\,da=\\int_{S}\\boldsymbol{\\sigma}\\cdot\\mathbf{n}\\,da\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $da$\n\\end_inset\n\n is an elemental area of \n\\begin_inset Formula $S$\n\\end_inset\n\n in the current configuration.\n Here,\n we used the relation between the traction vector \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n and the Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n,\n given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:traction-stress-relation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Note that the directed area \n\\begin_inset Formula $d\\mathbf{a}=\\mathbf{n}\\,da$\n\\end_inset\n\n in this integral may be related to \n\\begin_inset Formula $d\\mathbf{A}$\n\\end_inset\n\n in the reference configuration,\n using Nanson's formula in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Nanson-formula\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to produce\n\\begin_inset Formula \n\\[\n\\mathbf{f}=\\int_{S_{r}}\\boldsymbol{\\sigma}\\cdot J\\mathbf{F}^{-T}\\cdot d\\mathbf{A}=\\int_{S_{r}}J\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}\\cdot\\mathbf{n}_{r}\\,dA\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the unit outward normal on \n\\begin_inset Formula $S_{r}$\n\\end_inset\n\n in the reference configuration.\n Equating these two forms of \n\\begin_inset Formula $\\mathbf{f}$\n\\end_inset\n\n and using the divergence theorem produces\n\\begin_inset Formula \n\\[\n\\mathbf{f}=\\int_{V}\\divg\\boldsymbol{\\sigma}\\,dV=\\int_{V_{r}}\\Divg\\left(J\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}\\right)dV_{r}\\,,\n\\]\n\n\\end_inset\n\nwhere the divergence operator in the material frame is denoted by \n\\begin_inset Formula $\\Divg\\left(\\cdot\\right)$\n\\end_inset\n\n.\n The expression in the argument of this divergence operator is known as the \n\\emph on\nfirst Piola-Kirchhoff stress tensor,\n\n\\emph default\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}=J\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}\\,.\\label{eq50}\n\\end{equation}\n\n\\end_inset\n\nNote that \n\\series bold\n\n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n\n\\series default\n,\n like \n\\series bold\n\n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n\n\\series default\n,\n is not symmetric.\n Also,\n like \n\\series bold\n\n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n\n\\series default\n,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n \n\\series default\nis known as a \n\\emph on\ntwo-point\n\\emph default\n tensor,\n meaning it is neither a material nor a spatial tensor.\n This is demonstrated more easily by expressing \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n in terms of its contravariant components \n\\begin_inset Formula $\\sigma^{ij}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sigma^{ij}\\mathbf{g}_{i}\\otimes\\mathbf{g}_{j}\\label{eq:Cauchy-cov-cont}\n\\end{equation}\n\n\\end_inset\n\nand noting that \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}^{-1}=\\mathbf{G}_{i}\\otimes\\mathbf{g}^{i}\\label{eq:Finv-cov-cont}\n\\end{equation}\n\n\\end_inset\n\nso that the component form of \n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq50\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to\n\\begin_inset Formula \n\\[\n\\mathbf{P}=J\\sigma^{ij}\\left(\\mathbf{g}_{i}\\otimes\\mathbf{g}_{j}\\right)\\cdot\\left(\\mathbf{g}^{k}\\otimes\\mathbf{G}_{k}\\right)=J\\sigma^{ij}\\mathbf{g}_{i}\\otimes\\mathbf{G}_{j}\\,,\n\\]\n\n\\end_inset\n\nconfirming that it is a two-point tensor.\n\\end_layout\n\n\\begin_layout Standard\nTwo other stress measures are often used in continuum mechanics.\n Examining eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hyperelastic-stress-E\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hyperelastic-stress-C\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we may define the \n\\emph on\nsecond Piola-Kirchhoff (PK2) stress tensor\n\\emph default\n as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=J\\mathbf{F}^{-1}\\cdot\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}\\,,\\label{eq51}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n for a hyperelastic material,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{E}}=2\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{C}}\\,.\\label{eq:PK2-hyperelasticity}\n\\end{equation}\n\n\\end_inset\n\nSubstituting the component form of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Cauchy-cov-cont\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq51\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and using \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Finv-cov-cont\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=J\\sigma^{ij}\\mathbf{G}_{i}\\otimes\\mathbf{G}_{j}\\,,\\label{eq:PK2-cov-cont}\n\\end{equation}\n\n\\end_inset\n\nshowing that \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n is a material tensor.\n This component form suggests that may also define the \n\\emph on\nKirchhoff stress tensor\n\\emph default\n as the spatial version of \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\tau}=J\\boldsymbol{\\sigma}=J\\sigma^{ij}\\mathbf{g}_{i}\\otimes\\mathbf{g}_{j}\\,.\\label{eq49}\n\\end{equation}\n\n\\end_inset\n\nSince \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is symmetric,\n it follows that \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is symmetric.\n\\end_layout\n\n\\begin_layout Standard\nThe inverse relations for these various stresses are are \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathbf{\\sigma}}=\\frac{1}{J}\\boldsymbol{\\tau}\\,,\\quad\\boldsymbol{\\sigma}=\\frac{1}{J}\\mathbf{P}\\cdot\\mathbf{F}^{T}\\,,\\quad\\boldsymbol{\\sigma}=\\frac{1}{J}\\mathbf{F}\\cdot\\mathbf{S}\\cdot\\mathbf{F}^{T}\\,.\\label{eq52}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDirectional Derivative of the Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Stress-Directional-Derivative\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe directional derivative of the 2\n\\begin_inset Formula $^{\\mathrm{nd}}$\n\\end_inset\n\n PK stress tensor needs to be calculated for the linearization of the finite element equations.\n For a hyperelastic material,\n as expressed in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:PK2-hyperelasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n a linear relationship between the directional derivative of \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n and the linearized strain \n\\begin_inset Formula $D\\mathbf{E}\\left[\\mathbf{u}\\right]$\n\\end_inset\n\n can be obtained:\n \n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{S}\\left[\\mathbf{u}\\right]=\\boldsymbol{\\mathbb{C}}:D\\mathbf{E}\\left[\\mathbf{u}\\right]\\,.\\label{eq54}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\boldsymbol{\\mathbb{C}}$\n\\end_inset\n\n is a fourth-order tensor known as the \n\\emph on\nmaterial elasticity tensor\n\\emph default\n.\n Its Cartesian components and tensorial definition are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbb{C}_{IJKL} & =\\frac{\\partial S_{IJ}}{\\partial E_{KL}}=\\frac{4\\partial^{2}\\Psi_{r}}{\\partial C_{IJ}\\partial C_{KL}}\\\\\n\\boldsymbol{\\mathbb{C}} & =\\frac{\\partial\\mathbf{S}}{\\partial\\mathbf{E}}=\\frac{4\\partial^{2}\\Psi_{r}}{\\partial\\mathbf{C}\\partial\\mathbf{C}}\n\\end{aligned}\n\\,.\\label{eq55}\n\\end{equation}\n\n\\end_inset\n\nThe spatial equivalent – the \n\\emph on\nspatial elasticity tensor \n\\emph default\n\n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n – can be obtained from \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathcal{C}_{ijkl} & =\\frac{1}{J}F_{iI}F_{jJ}F_{kK}F_{lL}\\mathbb{C}_{IJKL}\\\\\n\\boldsymbol{\\mathcal{C}} & =\\frac{1}{J}\\left(\\mathbf{F}\\oslash\\mathbf{F}\\right):\\boldsymbol{\\mathbb{C}}:\\left(\\mathbf{F}^{T}\\oslash\\mathbf{F}^{T}\\right)\n\\end{aligned}\n.\\label{eq56}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nIsotropic Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:isotropic-hyperelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe hyperelastic constitutive equations discussed so far are unrestricted in their application.\n Isotropic material symmetry is defined by requiring the constitutive behavior to be independent of any material axes.\n Consequently,\n \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n must only be a function of the invariants of \n\\series bold\n\n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series default\n (or \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n),\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\Psi_{r}\\left(I_{1},I_{2},I_{3}\\right)\\,,\\label{eq:isotropic-SED}\n\\end{equation}\n\n\\end_inset\n\nwhere the invariants of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series bold\n \n\\series default\nare defined here as,\n \n\\begin_inset Formula \n\\begin{equation}\nI_{1}=\\tr\\mathbf{C}=\\mathbf{I}:\\mathbf{C},\\,I_{2}=\\frac{1}{2}\\left[\\left(\\tr\\mathbf{C}\\right)^{2}-\\tr\\mathbf{C}^{2}\\right],\\,I_{3}=\\det\\mathbf{C}=J^{2}\\,.\\label{eq:C-invariants}\n\\end{equation}\n\n\\end_inset\n\nAs a result of the isotropic restriction,\n the second Piola-Kirchhoff stress tensor can be written as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=2\\frac{\\partial\\Psi}{\\partial\\mathbf{C}}=2\\frac{\\partial\\Psi}{\\partial I_{1}}\\frac{\\partial I_{1}}{\\partial\\mathbf{C}}+2\\frac{\\partial\\Psi}{\\partial I_{2}}\\frac{\\partial I_{2}}{\\partial\\mathbf{C}}+2\\frac{\\partial\\Psi}{\\partial I_{3}}\\frac{\\partial I_{3}}{\\partial\\mathbf{C}}\\,.\\label{eq:PK2-isotropic}\n\\end{equation}\n\n\\end_inset\n\nThe second order tensors formed by the derivatives of the invariants with respect to \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n can be evaluated as \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial I_{1}}{\\partial\\mathbf{C}}=\\mathbf{I}\\,,\\,\\frac{\\partial I_{2}}{\\partial\\mathbf{C}}=I_{1}\\mathbf{I}-\\mathbf{C}\\,,\\,\\frac{\\partial I_{3}}{\\partial\\mathbf{C}}=I_{3}\\mathbf{C}^{-1}\\,.\\label{eq:C-invariant-deriv}\n\\end{equation}\n\n\\end_inset\n\nIntroducing expressions \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:C-invariant-deriv\"\nnolink \"false\"\n\n\\end_inset\n\n into equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:PK2-isotropic\"\nnolink \"false\"\n\n\\end_inset\n\n enables the second Piola-Kirchhoff stress to be evaluated as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=2\\left(\\Psi_{1}+I_{1}\\Psi_{2}+I_{2}\\Psi_{3})\\right)\\mathbf{I}-2\\left(\\Psi_{2}+I_{1}\\Psi_{3}\\right)\\mathbf{C}+\\Psi_{3}\\mathbf{C}^{2}\\,,\\label{eq:PK2-isotropic-final}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Psi_{1}=\\partial\\Psi/\\partial I_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Psi_{2}=\\partial\\Psi/\\partial I_{2}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Psi_{3}=\\partial\\Psi/\\partial I_{3}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stresses can now be obtained from the second Piola-Kirchhoff stresses by using \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq52\"\nnolink \"false\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=J^{-1}\\left(2\\left(\\Psi_{1}+I_{1}\\Psi_{2}+I_{2}\\Psi_{3})\\right)\\mathbf{b}-2\\left(\\Psi_{2}+I_{1}\\Psi_{3}\\right)\\mathbf{b}^{2}+\\Psi_{3}\\mathbf{b}^{3}\\right)\\,,\\label{eq:Cauchy-isotropic}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}\\,.\\label{eq:left-Cauchy-Green}\n\\end{equation}\n\n\\end_inset\n\nis the \n\\emph on\nleft Cauchy-Green tensor\n\\emph default\n.\n Note that in this equation \n\\begin_inset Formula $\\Psi_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Psi_{2}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Psi_{3}$\n\\end_inset\n\n still involve derivatives with respect to the invariants of \n\\series bold\n\n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series default\n.\n However,\n since the invariants of \n\\series bold\n\n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n\n\\series default\n are identical to those of \n\\series bold\n\n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series default\n,\n the quantities\n\\begin_inset Formula $\\Psi_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Psi_{2}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Psi_{3}$\n\\end_inset\n\n may also be considered to be the derivatives with respect to the invariants of \n\\series bold\n\n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n\n\\series default\n.\n\\end_layout\n\n\\begin_layout Subsection\nIsotropic Elasticity in Principal Directions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Isotropic-Elasticity-Principal\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor isotropic materials,\n the principal directions of the strain and stress tensors are the same.\n Let the eigenvalues of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n be denoted by \n\\begin_inset Formula $\\lambda_{i}^{2}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2,3)$\n\\end_inset\n\n,\n then the strain energy density may be given as a function of these eigenvalues,\n \n\\begin_inset Formula $\\Psi\\left(\\lambda_{1}^{2},\\lambda_{2}^{2},\\lambda_{3}^{2}\\right)$\n\\end_inset\n\n.\n To derive the expression for the stress,\n recognize that \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\lambda_{i}^{2}}{\\partial\\mathbf{C}}=\\mathbf{N}_{i}\\otimes\\mathbf{N}_{i}\\equiv\\mathbf{A}_{i}\\,,\\label{eq68}\n\\end{equation}\n\n\\end_inset\n\nwhere the \n\\begin_inset Formula $\\mathbf{N}_{i}$\n\\end_inset\n\n are the eigenvectors of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n.\n It follows that the second Piola-Kirchhoff stress may be represented as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=\\sum\\limits_{i=1}^{3}S_{i}\\mathbf{A}_{i}\\,,\\label{eq69}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\nS_{i}=2\\frac{\\partial\\Psi}{\\partial\\lambda_{i}^{2}}\\,.\\label{eq70}\n\\end{equation}\n\n\\end_inset\n\nTo evaluate the material elasticity tensor,\n recognize that \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\mathbf{A}_{i}}{\\partial\\mathbf{C}}=\\frac{1}{\\lambda_{i}^{2}-\\lambda_{j}^{2}}\\left(\\mathbf{A}_{i}\\odot\\mathbf{A}_{j}+\\mathbf{A}_{j}\\odot\\mathbf{A}_{i}\\right)+\\frac{1}{\\lambda_{i}^{2}-\\lambda_{k}^{2}}\\left(\\mathbf{A}_{i}\\odot\\mathbf{A}_{k}+\\mathbf{A}_{k}\\odot\\mathbf{A}_{i}\\right)\\,,\\label{eq71}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $i,j,k$\n\\end_inset\n\n form a permutation over \n\\begin_inset Formula $1,2,3$\n\\end_inset\n\n.\n Then it can be shown that the material elasticity tensor is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathbb{C}} & =\\sum\\limits_{i=1}^{3}4\\frac{\\partial^{2}\\Psi}{\\partial\\lambda_{i}^{2}\\partial\\lambda_{i}^{2}}\\mathbf{A}_{i}\\otimes\\mathbf{A}_{i}\\\\\n & +\\sum\\limits_{i=1}^{3}\\sum\\limits_{j=i+1}^{3}4\\frac{\\partial^{2}\\Psi}{\\partial\\lambda_{i}^{2}\\partial\\lambda_{j}^{2}}\\left(\\mathbf{A}_{i}\\otimes\\mathbf{A}_{j}+\\mathbf{A}_{j}\\otimes\\mathbf{A}_{i}\\right)\\\\\n & +\\sum\\limits_{i=1}^{3}\\sum\\limits_{j=i+1}^{3}2\\frac{S_{i}-S_{j}}{\\lambda_{i}^{2}-\\lambda_{j}^{2}}\\left(\\mathbf{A}_{i}\\odot\\mathbf{A}_{j}+\\mathbf{A}_{j}\\odot\\mathbf{A}_{i}\\right)\n\\end{aligned}\n\\,.\\label{eq72}\n\\end{equation}\n\n\\end_inset\n\nWhen eigenvalues coincide,\n L'Hospital's rule may be used to evaluate the coefficient in the last term,\n \n\\begin_inset Formula \n\\begin{equation}\n\\lim\\limits_{\\lambda_{j}^{2}\\to\\lambda_{i}^{2}}2\\frac{S_{i}-S_{j}}{\\lambda_{i}^{2}-\\lambda_{j}^{2}}=4\\left(\\frac{\\partial^{2}\\Psi}{\\partial\\lambda_{j}^{2}\\partial\\lambda_{j}^{2}}-\\frac{\\partial^{2}\\Psi}{\\partial\\lambda_{i}^{2}\\partial\\lambda_{j}^{2}}\\right)\\,.\\label{eq73}\n\\end{equation}\n\n\\end_inset\n\nThe double summations in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq72\"\nnolink \"false\"\n\n\\end_inset\n\n are arranged such that the summands represent fourth-order tensors with major and minor symmetries.\n\\end_layout\n\n\\begin_layout Standard\nIn the spatial frame,\n the Cauchy stress is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sum\\limits_{i=1}^{3}\\sigma_{i}\\mathbf{a}_{i}\\,,\\label{eq:IEPD-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}_{i}=\\mathbf{n}_{i}\\otimes\\mathbf{n}_{i}\\,,\\label{eq:IEPD-tensor-basis}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{n}_{i}=\\left(\\mathbf{F}\\cdot\\mathbf{N}_{i}\\right)/\\lambda_{i}$\n\\end_inset\n\n are the eigenvectors of \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n.\n The principal normal stresses are \n\\begin_inset Formula \n\\begin{equation}\n\\sigma_{i}=\\frac{\\lambda_{i}}{J}\\frac{\\partial\\Psi}{\\partial\\lambda_{i}}\\,.\\label{eq:IEPD-principal-stress}\n\\end{equation}\n\n\\end_inset\n\nThe spatial elasticity tensor is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}} & =\\sum\\limits_{i=1}^{3}\\left(J^{-1}\\lambda_{i}^{2}\\frac{\\partial^{2}\\Psi}{\\partial\\lambda_{i}^{2}}-\\sigma_{i}\\right)\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\\\\n & +\\sum\\limits_{i=1}^{3}\\sum\\limits_{j=i+1}^{3}J^{-1}\\lambda_{i}\\lambda_{j}\\frac{\\partial^{2}\\Psi}{\\partial\\lambda_{i}\\partial\\lambda_{j}}\\left(\\mathbf{a}_{i}\\otimes\\mathbf{a}_{j}+\\mathbf{a}_{j}\\otimes\\mathbf{a}_{i}\\right)\\\\\n & +\\sum\\limits_{i=1}^{3}\\sum\\limits_{j=i+1}^{3}2\\frac{\\lambda_{j}^{2}\\sigma_{i}-\\lambda_{i}^{2}\\sigma_{j}}{\\lambda_{i}^{2}-\\lambda_{j}^{2}}\\left(\\mathbf{a}_{i}\\odot\\mathbf{a}_{j}+\\mathbf{a}_{j}\\odot\\mathbf{a}_{i}\\right)\n\\end{aligned}\n\\,.\\label{eq:IEPD-elasticity}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTransversely Isotropic Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Transversely-Isotropic-Hyperelas\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTransverse isotropy can be introduced by adding a vector field representing the material preferred direction explicitly into the strain energy \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss96\"\nliteral \"true\"\n\n\\end_inset\n\n.\n We require that the strain energy depends on a unit vector field \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n,\n which describes the local fiber direction in the undeformed configuration.\n When the material undergoes deformation,\n the vector \n\\begin_inset Formula $\\mathbf{A}\\left(\\mathbf{X}\\right)$\n\\end_inset\n\n may be described by a unit vector field \n\\begin_inset Formula $\\mathbf{a}\\left(\\boldsymbol{\\varphi}\\left(\\mathbf{X}\\right)\\right)$\n\\end_inset\n\n.\n In general,\n the fibers will also undergo length change.\n The fiber stretch,\n \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n,\n can be determined in terms of the deformation gradient and the fiber direction in the undeformed configuration,\n \n\\begin_inset Formula \n\\begin{equation}\n\\lambda\\mathbf{a}=\\mathbf{F}\\cdot\\mathbf{A}\\,.\\label{eq95}\n\\end{equation}\n\n\\end_inset\n\nAlso,\n since \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is a unit vector,\n \n\\begin_inset Formula \n\\begin{equation}\n\\lambda^{2}=\\mathbf{A}\\cdot\\mathbf{C}\\cdot\\mathbf{A}\\,.\\label{eq96}\n\\end{equation}\n\n\\end_inset\n\nThe strain energy function for a transversely isotropic material,\n \n\\begin_inset Formula $\\Psi\\left(\\mathbf{C},\\mathbf{A},\\mathbf{X}\\right)$\n\\end_inset\n\n is an isotropic function of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{A}\\otimes\\mathbf{A}$\n\\end_inset\n\n.\n It can be shown \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Spencer84\"\nliteral \"true\"\n\n\\end_inset\n\n that the following set of invariants are sufficient to describe the material fully:\n \n\\begin_inset Formula \n\\begin{equation}\nI_{1}=\\tr\\mathbf{C}\\,,\\quad I_{2}=\\frac{1}{2}\\left[\\left(\\tr\\mathbf{C}\\right)^{2}-\\tr\\mathbf{C}^{2}\\right]\\,,\\quad I_{3}=\\det C=J^{2}\\,,\\label{eq97}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nI_{4}=\\mathbf{A}\\cdot\\mathbf{C}\\cdot\\mathbf{A}\\,,\\quad I_{5}=\\mathbf{A}\\cdot\\mathbf{C}^{2}\\cdot\\mathbf{A}\\,.\\label{eq98}\n\\end{equation}\n\n\\end_inset\n\nThe strain energy function can be written in terms of these invariants such that \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(\\mathbf{C},\\mathbf{A},\\mathbf{X}\\right)=\\Psi\\left(I_{1}\\left(\\mathbf{C}\\right),I_{2}\\left(\\mathbf{C}\\right),I_{3}\\left(\\mathbf{C}\\right),I_{4}\\left(\\mathbf{C},\\mathbf{A}\\right),I_{5}\\left(\\mathbf{C},\\mathbf{A}\\right)\\right)\\,.\\label{eq99}\n\\end{equation}\n\n\\end_inset\n\nThe second Piola-Kirchhoff can now be obtained in the standard manner:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=2\\frac{\\partial\\Psi}{\\partial\\mathbf{C}}=2\\sum\\limits_{i=1}^{5}\\frac{\\partial\\Psi}{\\partial I_{i}}\\frac{\\partial I_{i}}{\\partial\\mathbf{C}}\\,.\\label{eq100}\n\\end{equation}\n\n\\end_inset\n\nIn the transversely isotropic constitutive models described in Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Constitutive-Models\"\nnolink \"false\"\n\n\\end_inset\n\n it is further assumed that the strain energy function can be split into the following terms:\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(\\mathbf{C},\\mathbf{A}\\right)=\\Psi_{1}\\left(I_{1},I_{2},I_{3}\\right)+\\Psi_{2}\\left(I_{4}\\right)+\\Psi_{3}\\left(I_{1},I_{2},I_{3}I_{4}\\right)\\,.\\label{eq101}\n\\end{equation}\n\n\\end_inset\n\nThe strain energy function \n\\begin_inset Formula $\\Psi_{1}$\n\\end_inset\n\n represents the material response of the isotropic ground substance matrix,\n \n\\begin_inset Formula $\\Psi_{2}$\n\\end_inset\n\n represents the contribution from the fiber family (e.g.\n collagen),\n and \n\\begin_inset Formula $\\Psi_{3}$\n\\end_inset\n\n is the contribution from interactions between the fibers and matrix.\n The form \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq101\"\nnolink \"false\"\n\n\\end_inset\n\n generalizes many constitutive equations that have been successfully used in the past to describe biological soft tissues e.g.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Horowitz88,Humphrey90,Humphrey87\"\nliteral \"true\"\n\n\\end_inset\n\n.\n While this relation represents a large simplification when compared to the general case,\n it also embodies almost all of the material behavior that one would expect from transversely isotropic,\n large deformation matrix-fiber composites.\n\\end_layout\n\n\\begin_layout Subsection\nIncompressibility\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Incompressibility\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA material is incompressible when its mass density remains constant,\n \n\\begin_inset Formula $\\rho=\\rho_{r}$\n\\end_inset\n\n,\n or equivalently according to eq.\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"eq87\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n its relative volume satisfies\n\\begin_inset Formula \n\\begin{equation}\nJ=1\\label{eq:incompressibility-constraint}\n\\end{equation}\n\n\\end_inset\n\neverywhere throughout the material.\n No real material is truly incompressible,\n since true incompressibility implies that the speed of sound in that medium is infinite,\n which is not physically possible.\n Thus,\n one should view this assumption as an idealization of the behavior of real materials.\n We may replace eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:incompressibility-constraint\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n with the equivalent form \n\\begin_inset Formula $\\dot{J}=0$\n\\end_inset\n\n,\n and make use of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq91-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the form\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\mathbf{v}=\\mathbf{I}:\\mathbf{D}=0\\,,\\label{eq:incompressibility-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere we recognized that the divergence of the velocity is equal to the trace of the velocity gradient \n\\begin_inset Formula $\\mathbf{L}$\n\\end_inset\n\n,\n hence,\n the trace of its symmetric part \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSince \n\\begin_inset Formula $J=\\left(\\det\\mathbf{C}\\right)^{1/2}$\n\\end_inset\n\n,\n the incompressibility constraint of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:incompressibility-constraint\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n implies that the symmetric strain tensor \n\\begin_inset Formula $\\mathbf{E}=\\frac{1}{2}\\left(\\mathbf{C}-\\mathbf{I}\\right)$\n\\end_inset\n\n used as a state variable in the derivation of the hyperelasticity relation (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Constitutive-Restrictions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) does not truly have six independent components.\n Therefore,\n we cannot claim that \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n can vary arbitrarily when enforcing the entropy inequality.\n Instead,\n we introduce the incompressibility constraint of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:incompressibility-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n into the Clausius-Duhem inequality,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Clausius-Duhem-inequality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n using the method of Lagrange multipliers,\n\\begin_inset Formula \n\\[\n-\\rho\\left(\\dot{\\psi}+\\eta\\dot{\\theta}\\right)+\\left(\\boldsymbol{\\sigma}+\\lambda\\mathbf{I}\\right):\\mathbf{D}-\\frac{1}{\\theta}\\mathbf{q}\\cdot\\grad\\theta\\geqslant0\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is the Lagrange multiplier.\n Now,\n we may proceed with the constitutive assumption that \n\\begin_inset Formula $\\psi=\\psi\\left(\\mathbf{E}\\right)$\n\\end_inset\n\n,\n as done in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Constitutive-Restrictions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n until we conclude that the stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is constrained to have the form\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=p\\mathbf{I}+2J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{C}}\\cdot\\mathbf{F}^{T}\\,,\\quad p\\equiv-\\lambda\\,.\\label{eq:Cauchy-hyperelastic-incomp}\n\\end{equation}\n\n\\end_inset\n\nThis expression shows that the incompressibility constraint requires us to have a parameter \n\\begin_inset Formula $p$\n\\end_inset\n\n (a pressure) that accounts for that part of the strain energy density that would normally be stored in the material as a result of volumetric strain.\n Analytically,\n we would solve for this additional scalar parameter \n\\begin_inset Formula $p$\n\\end_inset\n\n using the constraint of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:incompressibility-constraint\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Finally,\n substituting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Cauchy-hyperelastic-incomp\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq51\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the PK2 stress in an incompressible material takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=Jp\\mathbf{C}^{-1}+2\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{C}}\\,.\\label{eq:PK2-hyperelastic-incomp}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nNearly-Incompressible Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Nearly-Incompressible-Hyperelast\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen dealing with incompressible and nearly incompressible materials,\n it proves useful to separate the volumetric and the deviatoric (distortional) components of the deformation gradient \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n.\n Such a separation must ensure that the deviatoric part of the deformation gradient,\n namely \n\\begin_inset Formula $\\mathbf{\\tilde{F}}$\n\\end_inset\n\n,\n does not produce any change in volume.\n Noting that the determinant of the deformation gradient gives the volume ratio,\n the determinant of \n\\begin_inset Formula $\\mathbf{\\tilde{F}}$\n\\end_inset\n\n must therefore satisfy,\n \n\\begin_inset Formula \n\\begin{equation}\n\\det\\mathbf{\\tilde{F}}=1\\,.\\label{eq38}\n\\end{equation}\n\n\\end_inset\n\nThis condition can be achieved by choosing \n\\begin_inset Formula $\\mathbf{\\tilde{F}}$\n\\end_inset\n\n as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\tilde{F}}=J^{-1/3}\\mathbf{F}\\,.\\label{eq39}\n\\end{equation}\n\n\\end_inset\n\nThe right Cauchy-Green tensor can also be split into volumetric and deviatoric components.\n With the use of,\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq39\"\nnolink \"false\"\n\n\\end_inset\n\n the deviatoric right Cauchy-Green tensor is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\tilde{C}}=\\tilde{\\mathbf{F}}^{T}\\cdot\\tilde{\\mathbf{F}}=J^{-2/3}\\mathbf{C}\\,.\\label{eq43-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe process of defining constitutive equations in the case of nearly incompressible hyperelasticity is simplified by uncoupling the strain energy density \n\\begin_inset Formula $\\Psi_{r}\\left(\\mathbf{C}\\right)$\n\\end_inset\n\n into a volumetric energy component \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n and a distortional component \n\\begin_inset Formula $\\tilde{\\Psi}\\left(\\mathbf{C}\\right)$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(\\mathbf{C}\\right)=\\tilde{\\Psi}\\left(\\mathbf{C}\\right)+U\\left(J\\right)\\,.\\label{eq:UC-SED}\n\\end{equation}\n\n\\end_inset\n\nThe second Piola-Kirchhoff tensor for a material defined by \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-SED\"\nnolink \"false\"\n\n\\end_inset\n\n is obtained in the standard manner with the help of equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:PK2-isotropic\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{S} & =2\\frac{\\partial\\Psi}{\\partial\\mathbf{C}}\\\\\n & =2\\frac{\\partial\\tilde{\\Psi}}{\\partial\\mathbf{C}}+2\\frac{dU}{dJ}\\frac{\\partial J}{\\partial C}\\\\\n & =2\\frac{\\partial\\tilde{\\Psi}}{\\partial\\mathbf{C}}+pJ\\mathbf{C}^{-1}\n\\end{aligned}\n\\,,\\label{eq:UC-PK2-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere the pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is defined as \n\\begin_inset Formula \n\\begin{equation}\np=\\frac{dU}{dJ}\\,.\\label{eq:UC-p}\n\\end{equation}\n\n\\end_inset\n\nAn example for \n\\begin_inset Formula $U$\n\\end_inset\n\n that will be used later in the definition of the constitutive models is \n\\begin_inset Formula \n\\begin{equation}\nU\\left(J\\right)=\\frac{1}{2}\\kappa\\left(\\ln J\\right)^{2}\\,.\\label{eq:UC-U-exa}\n\\end{equation}\n\n\\end_inset\n\nThe parameter \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n will be used later as a penalty factor that will enforce the (nearly-) incompressible constraint.\n However,\n \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n can represent a true material coefficient,\n namely the bulk modulus,\n for a compressible material that happens to have a hyperelastic strain energy function in the form of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-SED\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In the case where the dilatational energy is given by \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-U-exa\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the pressure is \n\\begin_inset Formula \n\\begin{equation}\np=\\kappa\\frac{\\ln J}{J}\\,.\\label{eq:UC-p-exa}\n\\end{equation}\n\n\\end_inset\n\nEquation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-PK2-redux\"\nnolink \"false\"\n\n\\end_inset\n\n can be further developed by applying the chain rule to the first term:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=pJ\\mathbf{C}^{-1}+J^{-2/3}\\dev\\tilde{\\mathbf{S}}\\,,\\label{eq:UC-PK2-redux2}\n\\end{equation}\n\n\\end_inset\n\nwhere the \n\\emph on\nfictitious second Piola-Kirchoff \n\\emph default\ntensor \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holzapfel00\"\nliteral \"true\"\n\n\\end_inset\n\n is defined by,\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mathbf{S}}=2\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{\\mathbf{C}}}\\,,\\label{eq:UC-PK2-tilde}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\Dev$\n\\end_inset\n\n is the deviator operator in the reference frame:\n \n\\begin_inset Formula \n\\begin{equation}\n\\Dev\\left(\\cdot\\right)=\\left(\\cdot\\right)-\\frac{1}{3}\\left(\\left(\\cdot\\right):\\mathbf{C}\\right)\\mathbf{C}^{-1}.\\label{eq:UC-Dev}\n\\end{equation}\n\n\\end_inset\n\nThe Cauchy stress can then be obtained from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq52\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula $_{\\mathrm{3}}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=p\\mathbf{I}+\\dev\\tilde{\\boldsymbol{\\sigma}}\\,,\\label{eq:UC-Cauchy-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=\\frac{2}{J}\\mathbf{\\tilde{F}}\\cdot\\frac{\\partial\\tilde{\\Psi}}{\\partial\\mathbf{\\tilde{C}}}\\cdot\\mathbf{\\tilde{F}}^{T}\\,,\\label{eq:UC-Cauchy-stress-tilde}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\dev\\left(\\cdot\\right)=\\left(\\cdot\\right)-\\frac{1}{3}\\left(\\left(\\cdot\\right):\\mathbf{I}\\right)\\mathbf{I}\\label{eq:UC-dev}\n\\end{equation}\n\n\\end_inset\n\nis the deviator operator in the spatial frame.\n\\end_layout\n\n\\begin_layout Standard\nThe following expression will be useful in the following development.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{d\\tilde{C}_{IJ}}{dC_{KL}} & =J^{-2/3}\\left(\\frac{1}{2}\\left(\\delta_{IK}\\delta_{JL}+\\delta_{IL}\\delta_{JK}\\right)-\\frac{1}{3}\\tilde{C}_{IJ}\\tilde{C}_{KL}^{-1}\\right)\\\\\n\\frac{\\partial\\tilde{\\mathbf{C}}}{\\partial\\mathbf{C}} & =J^{-2/3}\\left(\\mathbf{I}\\odot\\mathbf{I}-\\frac{1}{3}\\tilde{\\mathbf{C}}\\otimes\\tilde{\\mathbf{C}}^{-1}\\right)\n\\end{aligned}\n\\,.\\label{eq89}\n\\end{equation}\n\n\\end_inset\n\nNotice that the contraction with a symmetric tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n\n\\series default\n results in,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{d\\tilde{C}_{IJ}}{dC_{KL}}A_{IJ} & =J^{-2/3}\\Dev A_{KL}\\\\\n\\mathbf{A}:\\frac{\\partial\\tilde{\\mathbf{C}}}{\\partial\\mathbf{C}} & =J^{-2/3}\\Dev\\mathbf{A}\n\\end{aligned}\n\\,.\\label{eq90}\n\\end{equation}\n\n\\end_inset\n\nThe elasticity tensor,\n defined in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq55\"\nnolink \"false\"\n\n\\end_inset\n\n,\n takes on the following form.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbb{C}_{IJKL} & =\\left(J^{2}\\frac{dp}{dJ}+Jp\\right)C_{IJ}^{-1}C_{KL}^{-1}-2pJ\\mathcal{I}_{IJKL}\\\\\n & -\\frac{2}{3}J^{-2/3}\\left(\\Dev\\tilde{S}_{IJ}C_{KL}^{-1}+C_{IJ}^{-1}\\Dev\\tilde{S}_{KL}\\right)\\\\\n & +\\frac{2}{3}\\tilde{S}_{RS}\\tilde{C}_{RS}\\left(\\mathcal{I}_{IJKL}-\\frac{1}{3}C_{KL}^{-1}C_{IJ}^{-1}\\right)+J^{-4/3}\\hat{\\mathbb{C}}_{IJKL}\\\\\n\\boldsymbol{\\mathbb{C}} & =\\left(J^{2}\\frac{dp}{dJ}+Jp\\right)\\mathbf{C}^{-1}\\otimes\\mathbf{C}^{-1}-2pJ\\boldsymbol{\\mathcal{I}}\\\\\n & -\\frac{2}{3}J^{-2/3}\\left(\\Dev\\tilde{\\mathbf{S}}\\otimes\\mathbf{C}^{-1}+\\mathbf{C}^{-1}\\otimes\\Dev\\tilde{\\mathbf{S}}\\right)\\\\\n & +\\frac{2}{3}\\left(\\tilde{\\mathbf{S}}:\\tilde{\\mathbf{C}}\\right)\\left(\\boldsymbol{\\mathcal{I}}-\\frac{1}{3}\\mathbf{C}^{-1}\\otimes\\mathbf{C}^{-1}\\right)+J^{-4/3}\\hat{\\boldsymbol{\\mathbb{C}}}\n\\end{aligned}\n\\,,\\label{eq91}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\hat{\\mathcal{\\mathbb{C}}}_{IJKL} & =\\tilde{\\mathcal{\\mathbb{C}}}_{IJKL}-\\frac{1}{3}\\left(\\tilde{\\mathcal{\\mathbb{C}}}_{IJRS}\\tilde{C}_{RS}\\tilde{C}_{KL}^{-1}+\\tilde{\\mathcal{\\mathbb{C}}}_{RSKL}\\tilde{C}_{RS}\\tilde{C}_{IJ}^{-1}\\right)+\\frac{1}{9}\\tilde{C}_{IJ}^{-1}\\tilde{C}_{RS}\\tilde{\\mathbb{C}}_{RSMN}\\tilde{C}_{MN}\\tilde{C}_{KL}^{-1}\\\\\n\\hat{\\mathcal{\\boldsymbol{\\mathbb{C}}}} & =\\tilde{\\mathcal{\\boldsymbol{\\mathbb{C}}}}-\\frac{1}{3}\\left(\\tilde{\\mathcal{\\boldsymbol{\\mathbb{C}}}}:\\tilde{\\mathbf{C}}\\otimes\\tilde{\\mathbf{C}}^{-1}+\\tilde{\\mathbf{C}}^{-1}\\otimes\\tilde{\\mathbf{C}}:\\tilde{\\boldsymbol{\\mathcal{\\mathbb{C}}}}\\right)+\\frac{1}{9}\\tilde{\\mathbf{C}}^{-1}\\otimes\\tilde{\\mathbf{C}}:\\tilde{\\boldsymbol{\\mathbb{C}}}:\\tilde{\\mathbf{C}}\\otimes\\tilde{\\mathbf{C}}^{-1}\n\\end{aligned}\n\\,.\\label{eq92}\n\\end{equation}\n\n\\end_inset\n\nThe spatial elasticity tensor follows from\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}} & =\\left(J\\frac{dp}{dJ}+p\\right)\\mathbf{I}\\otimes\\mathbf{I}-2p\\mathbf{I}\\odot\\mathbf{I}\\\\\n & -\\frac{2}{3}\\left(\\dev\\tilde{\\boldsymbol{\\sigma}}\\otimes\\mathbf{I}+\\mathbf{I}\\otimes\\dev\\tilde{\\boldsymbol{\\sigma}}\\right)\\\\\n & +\\frac{2}{3}\\left(\\tilde{\\boldsymbol{\\sigma}}:\\mathbf{I}\\right)\\left(\\mathbf{I}\\odot\\mathbf{I}-\\frac{1}{3}\\mathbf{I}\\otimes\\mathbf{I}\\right)+\\hat{\\mathcal{\\boldsymbol{C}}}\n\\end{aligned}\n\\,,\\label{eq:UC-spatial-elasticity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\hat{\\mathcal{C}}_{ijkl} & =\\frac{1}{J}\\tilde{F}_{iI}\\tilde{F}_{jJ}\\tilde{F}_{kK}\\tilde{F}_{lL}\\hat{\\mathcal{\\mathbb{C}}}_{IJKL}\\\\\n\\hat{\\boldsymbol{\\mathcal{C}}} & =\\frac{1}{J}\\left(\\tilde{\\mathbf{F}}\\oslash\\tilde{\\mathbf{F}}\\right):\\hat{\\boldsymbol{\\mathbb{C}}}:\\left(\\tilde{\\mathbf{F}}^{T}\\oslash\\tilde{\\mathbf{F}}^{T}\\right)\\\\\n & =\\tilde{\\boldsymbol{\\mathcal{C}}}-\\frac{1}{3}\\left(\\mathbf{I}\\otimes\\mathbf{I}:\\tilde{\\boldsymbol{\\mathcal{C}}}+\\tilde{\\boldsymbol{\\mathcal{C}}}:\\mathbf{I}\\otimes\\mathbf{I}\\right)+\\frac{1}{9}\\left(\\mathbf{I}:\\tilde{\\boldsymbol{\\mathcal{C}}}:\\mathbf{I}\\right)\\mathbf{I}\\otimes\\mathbf{I}\n\\end{aligned}\n\\,.\\label{eq:UC-spatial-elasticity-hat}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTension-Bearing Fiber Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tension-Bearing-Fiber-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn biomechanics we often find it convenient to model fibrous or fibrillar materials using one-dimensional fibers that can only sustain tension.\n Typically,\n such fibers are represented with a strain energy density function\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{C}\\right)=H\\left(I_{n}-1\\right)\\Psi_{n}\\left(I_{n}\\right)\\,,\\quad I_{n}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}\\label{eq:fiber-model}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the unit vector along the fiber in its reference configuration,\n and \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n is the square of the stretch ratio along the fiber.\n The Heaviside unit step function \n\\begin_inset Formula $H\\left(I_{n}-1\\right)$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fiber-model\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n ensures that the fiber contributes strain energy only when it is under tension (\n\\begin_inset Formula $I_{n}>1$\n\\end_inset\n\n);\n thus,\n the constitutive model \n\\begin_inset Formula $\\Psi_{n}\\left(I_{n}\\right)$\n\\end_inset\n\n for the tensile response of the fiber must reduce to zero when \n\\begin_inset Formula $I_{n}=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nUsing the hyperelasticity relations presented above,\n the Cauchy stress in this fiber material can be evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=H\\left(I_{n}-1\\right)2J^{-1}\\frac{d\\Psi_{n}}{dI_{n}}\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\otimes\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\,,\\label{eq:fiber-stress}\n\\end{equation}\n\n\\end_inset\n\nand the spatial elasticity tensor is\n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{C}=4J^{-1}\\frac{d^{2}\\Psi_{n}}{dI_{n}^{2}}\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\otimes\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\otimes\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\otimes\\left(\\mathbf{F}\\cdot\\mathbf{n}_{r}\\right)\\,.\\label{eq:fiber-elasticity}\n\\end{equation}\n\n\\end_inset\n\nIf we denote the unit vector along the fiber in the current configuration as \n\\begin_inset Formula $\\mathbf{n}\\equiv I_{n}^{-1/2}\\mathbf{F}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n,\n the above expressions may be rewritten as \n\\begin_inset Formula $\\boldsymbol{\\sigma}=H\\left(I_{n}-1\\right)2J^{-1}I_{n}\\Psi_{n}^{\\prime}\\left(I_{n}\\right)\\mathbf{n}\\otimes\\mathbf{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathcal{C}=4J^{-1}I_{n}^{2}\\Psi_{n}^{\\prime\\prime}\\left(I_{n}\\right)\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}$\n\\end_inset\n\n.\n As explained in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07a\"\nliteral \"false\"\n\n\\end_inset\n\n,\n these fiber models must be combined with a ground matrix in order to produce a stable material response.\n In FEBio this can be done by using a constrained mixture of solid constituents (for example,\n see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Simple-Solid-Mixtures\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nIn the classical fiber mechanics literature it was suggested that uncoupled fiber formulations could also be implemented,\n whereby \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{C}\\right)=H\\left(\\tilde{I}_{n}-1\\right)\\tilde{\\Psi}_{n}\\left(\\tilde{I}_{n}\\right)+U\\left(J\\right)\\,,\\quad\\tilde{I}_{n}=\\mathbf{n}_{r}\\cdot\\tilde{\\mathbf{C}}\\cdot\\mathbf{n}_{r}\\,,\\label{eq:fiber-model-UC}\n\\end{equation}\n\n\\end_inset\n\nand the Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-Cauchy-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=H\\left(\\tilde{I}_{n}-1\\right)2J^{-1}\\frac{d\\tilde{\\Psi}_{n}}{d\\tilde{I}_{n}}\\left(\\tilde{\\mathbf{F}}\\cdot\\mathbf{n}_{r}\\right)\\otimes\\left(\\tilde{\\mathbf{F}}\\cdot\\mathbf{n}_{r}\\right)\\,.\\label{eq:fiber-stress-UC}\n\\end{equation}\n\n\\end_inset\n\nUncoupled fiber formulations of this kind are available in FEBio.\n More recently however,\n several studies have demonstrated that using \n\\begin_inset Formula $H\\left(\\tilde{I}_{n}-1\\right)$\n\\end_inset\n\n to detect whether a fiber is in tension or compression is non-physical,\n since \n\\begin_inset Formula $\\tilde{I}_{n}\\ne I_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n is the sole true measure of the tensile stretch in the fiber \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Sansour08,Helfenstein10,Gultekin19\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Therefore,\n uncoupled fiber formulations have fallen out of favor,\n even though FEBio still allows users to employ these for historical reasons.\n It is now recommended to use the standard (unconstrained) fiber models,\n also available in FEBio,\n with the formulation given in Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fiber-model\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fiber-elasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n even when the ground matrix uses an uncoupled formulation.\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nBiphasic Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBiphasic materials may be used to model deformable porous media.\n A biphasic material represents a mixture of a porous permeable solid and an interstitial fluid.\n Each constituent is intrinsically incompressible,\n but the mixture may change volume as interstitial fluid is exchanged with the pore space of the solid.\n Biphasic materials require the explicit modeling of fluid that permeates the solid.\n The biphasic material model is useful to simulate materials that show flow-dependent viscoelastic behavior resulting from the frictional interactions of the fluid and solid.\n Several biological materials such as cartilage can be described more accurately this way.\n\\end_layout\n\n\\begin_layout Subsection\nGoverning Equations\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-governing-equations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a mixture consisting of a solid constituent and a fluid constituent.\n Both constituents are considered to be intrinsically incompressible,\n but the mixture can change volume when fluid enters or leaves the porous solid matrix \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bowen80,Mow80\"\nliteral \"true\"\n\n\\end_inset\n\n.\n According to the kinematics of the continuum \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Truesdell60\"\nliteral \"true\"\n\n\\end_inset\n\n,\n each constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n of a mixture (\n\\begin_inset Formula $\\alpha=s$\n\\end_inset\n\n for the solid and \n\\begin_inset Formula $\\alpha=w$\n\\end_inset\n\n for the fluid) has a separate motion \n\\begin_inset Formula $\\boldsymbol{\\chi}^{\\alpha}\\left(\\mathbf{X}^{\\alpha},t\\right)$\n\\end_inset\n\n which places particles of each mixture constituent,\n originally located at \n\\begin_inset Formula $\\mathbf{X}^{\\alpha}$\n\\end_inset\n\n,\n in the current configuration \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n according to \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\boldsymbol{\\chi}^{\\alpha}\\left(\\mathbf{X}^{\\alpha},t\\right)\\,.\\label{eq102}\n\\end{equation}\n\n\\end_inset\n\nFor the purpose of finite element analyses,\n the motion of the solid matrix,\n \n\\begin_inset Formula $\\alpha=s$\n\\end_inset\n\n,\n is of particular interest.\n\\end_layout\n\n\\begin_layout Standard\nThe governing equations that enter into the statement of virtual work are the conservation of linear momentum and the conservation of mass,\n for the mixture as a whole.\n Under quasi-static conditions,\n the conservation of momentum reduces to \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}=\\mathbf{0}\\,,\\label{eq103}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is the Cauchy stress for the mixture,\n \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the mixture density and \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is the external mixture body force per mass.\n Since the mixture is porous,\n this stress may also be written as \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}\\,,\\label{eq104}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure and \n\\begin_inset Formula $\\sigma^{e}$\n\\end_inset\n\n is the effective or extra stress,\n resulting from the deformation of the solid matrix.\n Conservation of mass for the mixture requires that \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=0\\,,\\label{eq105}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{v}^{s}=\\partial\\boldsymbol{\\chi}^{s}/\\partial t$\n\\end_inset\n\n is the solid matrix velocity and \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n is the flux of the fluid relative to the solid matrix.\n Let the solid matrix displacement be denoted by \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n then \n\\begin_inset Formula $\\mathbf{v}^{s}=\\mathbf{\\dot{u}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nTo relate the relative fluid flux \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n to the fluid pressure and solid deformation,\n it is necessary to employ the equation of conservation of linear momentum for the fluid,\n \n\\begin_inset Formula \n\\begin{equation}\n-\\varphi^{w}\\grad p+\\rho^{w}\\mathbf{b}^{w}+\\mathbf{\\hat{p}}_{d}^{w}=\\mathbf{0}\\,,\\label{eq106}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi^{w}$\n\\end_inset\n\n is the solid matrix porosity,\n \n\\begin_inset Formula $\\rho^{w}=\\varphi^{w}\\rho_{T}^{w}$\n\\end_inset\n\n is the apparent fluid density and \n\\begin_inset Formula $\\rho_{T}^{w}$\n\\end_inset\n\n is the true fluid density,\n \n\\begin_inset Formula $\\mathbf{b}^{w}$\n\\end_inset\n\n is the external body force per mass acting on the fluid,\n and \n\\begin_inset Formula $\\mathbf{\\hat{p}}_{d}^{w}$\n\\end_inset\n\n is the momentum exchange between the solid and fluid constituents,\n typically representing the frictional interaction between these constituents.\n This equation neglects the viscous stress of the fluid in comparison to \n\\begin_inset Formula $\\mathbf{\\hat{p}}_{d}^{w}$\n\\end_inset\n\n.\n The most common constitutive relation is \n\\begin_inset Formula $\\mathbf{\\hat{p}}_{d}^{w}=-\\varphi^{w}\\mathbf{k}^{-1}\\cdot\\mathbf{w}$\n\\end_inset\n\n,\n where the second order,\n symmetric tensor \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n is the hydraulic permeability of the mixture.\n When combined with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq106\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it produces \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}=-\\mathbf{k}\\cdot\\left(\\grad p-\\rho_{T}^{w}\\mathbf{b}^{w}\\right)\\,,\\label{eq107}\n\\end{equation}\n\n\\end_inset\n\nwhich is equivalent to Darcy's law.\n In general,\n \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n may be a function of the deformation.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nBiphasic-Solute Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Solute-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA biphasic-solute material is an extension of the biphasic material model that also includes transport and mechano-chemical effects of a neutral solute.\n Transport of a solute in a porous medium includes diffusion,\n resulting from gradients in the solute concentration,\n and convection of the solute by the solvent,\n as a result of fluid pressure gradients.\n Mechano-chemical effects describe phenomena such as osmotic pressurization and swelling.\n\\end_layout\n\n\\begin_layout Subsection\nGoverning Equations\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-governing-equations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe governing equations adopted in this finite element implementation of neutral solute transport in deformable porous media are based on the framework of mixture theory \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Truesdell60,Bowen76\"\nliteral \"true\"\n\n\\end_inset\n\n.\n A single solute is considered in this presentation for notational simplicity,\n though the extension of equations to multiple solutes is straightforward.\n Various forms of the governing equations have been presented in the prior literature \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mauck03,Ateshian06\"\nliteral \"true\"\n\n\\end_inset\n\n,\n though a presentation that incorporates all the desired features of this implementation has not been reported previously and is thus detailed here.\n\\end_layout\n\n\\begin_layout Standard\nThe fundamental modeling assumptions adopted in this treatment are quasi-static conditions for momentum balance (negligible effects of inertia),\n intrinsic incompressibility of all constituents (invariant true densities),\n isothermal conditions,\n negligible volume fraction of solute relative to the solid and solvent,\n and negligible effects of solute and solvent viscosities (friction within constituents) relative to frictional interactions between constituents.\n These assumptions are often made in studies of biological tissues and cells.\n External body forces and chemical reactions are not considered.\n\\end_layout\n\n\\begin_layout Standard\nThe three constituents of the mixture are the porous-permeable solid matrix (\n\\begin_inset Formula $\\alpha=s)$\n\\end_inset\n\n,\n the solvent (\n\\begin_inset Formula $\\alpha=w)$\n\\end_inset\n\n,\n and the solute (\n\\begin_inset Formula $\\alpha=u)$\n\\end_inset\n\n.\n The motion of the solid matrix is described by the displacement vector \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n the pressure of the interstitial fluid (solvent\n\\begin_inset Formula $+$\n\\end_inset\n\nsolute) is \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n and the solute concentration (on a solution-volume basis) is \n\\begin_inset Formula $c$\n\\end_inset\n\n.\n The total (or mixture) stress may be described by the Cauchy stress tensor \n\\begin_inset Formula $\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n is the identity tensor and \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the stress arising from the strain in the porous solid matrix.\n Because it is porous,\n the solid matrix is compressible since the volume of pores changes as interstitial fluid enters or leaves the matrix.\n Under the conditions outlined above,\n the balance of linear momentum for the mixture reduces to \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\boldsymbol{\\sigma}=-\\grad p+\\divg\\boldsymbol{\\sigma}^{e}=\\mathbf{0}\\,.\\label{eq108}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n the equations of balance of linear momentum for the solvent and solute are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\rho^{w}\\grad\\tilde{\\mu}^{w}+\\mathbf{f}^{ws}\\cdot\\left(\\mathbf{v}^{s}-\\mathbf{v}^{w}\\right)+\\mathbf{f}^{wu}\\cdot\\left(\\mathbf{v}^{u}-\\mathbf{v}^{w}\\right) & =\\mathbf{0}\\,,\\\\\n-\\rho^{u}\\grad\\tilde{\\mu}^{u}+\\mathbf{f}^{us}\\cdot\\left(\\mathbf{v}^{s}-\\mathbf{v}^{u}\\right)+\\mathbf{f}^{uw}\\cdot\\left(\\mathbf{v}^{w}-\\mathbf{v}^{u}\\right) & =\\mathbf{0}\\,,\n\\end{aligned}\n\\label{eq109}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho^{\\alpha}$\n\\end_inset\n\n is the apparent density (mass of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n per volume of the mixture),\n \n\\begin_inset Formula $\\tilde{\\mu}^{\\alpha}$\n\\end_inset\n\n is the mechano-chemical potential and \n\\begin_inset Formula $\\mathbf{v}^{\\alpha}$\n\\end_inset\n\n is the velocity of constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n \n\\begin_inset Formula $\\mathbf{f}^{\\alpha\\beta}$\n\\end_inset\n\n is the diffusive drag tensor between constituents \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n and \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n representing momentum exchange via frictional interactions,\n which satisfies \n\\begin_inset Formula $\\mathbf{f}^{\\beta\\alpha}=\\mathbf{f}^{\\alpha\\beta}$\n\\end_inset\n\n.\n An important feature of these relations is the incorporation of momentum exchange term between the solute and solid matrix,\n \n\\begin_inset Formula $\\mathbf{f}^{us}\\cdot\\left(\\mathbf{v}^{s}-\\mathbf{v}^{u}\\right)$\n\\end_inset\n\n,\n which is often neglected in other treatments but plays an important role for describing solid-solute interactions \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mauck03,Albro08,Albro10\"\nliteral \"true\"\n\n\\end_inset\n\n.\n These momentum equations show that the driving force for the transport of solvent or solute is the gradient in its mechano-chemical potential,\n which is resisted by frictional interactions with other constituents.\n\\end_layout\n\n\\begin_layout Standard\nThe mechano-chemical potential is the sum of the mechanical and chemical potentials.\n The chemical potential \n\\begin_inset Formula $\\mu^{\\alpha}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n represents the rate at which the mixture free energy changes with increasing mass of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n The mechanical potential represents the rate at which the mixture free energy density changes with increasing volumetric strain of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n In a mixture of intrinsically incompressible constituents,\n where the volumetric strain is idealized to be zero,\n this potential is given by \n\\begin_inset Formula $\\left(p-p_{0}\\right)/\\rho_{T}^{\\alpha}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\rho_{T}^{\\alpha}$\n\\end_inset\n\n is the true density of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (mass of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n per volume of \n\\begin_inset Formula $\\alpha)$\n\\end_inset\n\n,\n which is invariant for incompressible constituents,\n and \n\\begin_inset Formula $p_{0}$\n\\end_inset\n\n is some arbitrarily set reference pressure (e.g.,\n ambient pressure).\n\\end_layout\n\n\\begin_layout Standard\nFrom classical physical chemistry,\n the general form of a constitutive relation for the chemical potential is \n\\begin_inset Formula $\\mu^{\\alpha}=\\mu_{0}^{\\alpha}\\left(\\theta\\right)+\\left(R\\theta/M^{\\alpha}\\right)\\ln a^{\\alpha}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Tinoco-Jr.95\"\nliteral \"true\"\n\n\\end_inset\n\n,\n where \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant,\n \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the absolute temperature,\n \n\\begin_inset Formula $M^{\\alpha}$\n\\end_inset\n\n is the molecular weight (invariant) and \n\\begin_inset Formula $a^{\\alpha}$\n\\end_inset\n\n is the activity of constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (a non-dimensional quantity);\n \n\\begin_inset Formula $\\mu_{0}^{\\alpha}\\left(\\theta\\right)$\n\\end_inset\n\n is the chemical potential at some arbitrary reference state,\n at a given temperature.\n For solutes,\n physical chemistry treatments let \n\\begin_inset Formula $a^{u}=\\gamma c/c_{0}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $c_{0}$\n\\end_inset\n\n is the solute concentration in some standard reference state (an invariant,\n typically \n\\begin_inset Formula $c_{0}=1\\,\\mbox{M})$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is the non-dimensional activity coefficient,\n which generally depends on the current state (e.g.,\n concentration) but reduces to unity under the assumption of ideal physico-chemical behavior \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Tinoco-Jr.95\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Since this representation is strictly valid for free solutions only,\n whereas solutes may be partially excluded from some of the interstitial space of a porous solid matrix,\n Mauck et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mauck03\"\nliteral \"true\"\n\n\\end_inset\n\n extended this representation of the solute activity to let \n\\begin_inset Formula $a^{u}=\\gamma c/\\kappa c_{0}$\n\\end_inset\n\n,\n where the solubility \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n represents the fraction of the pore space which is accessible to the solute (\n\\begin_inset Formula $0<\\kappa\\leqslant1)$\n\\end_inset\n\n.\n In this extended form,\n it becomes clear that even under ideal behavior (\n\\begin_inset Formula $\\gamma=1)$\n\\end_inset\n\n,\n the solute activity may be affected by the solubility.\n Indeed,\n for neutral solutes,\n the solubility also represents the partition coefficient of the solute between the tissue and external bath \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laurent63,Ogston61\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen accounting for the fact that the solute volume fraction is negligible compared to the solvent volume fraction \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Tinoco-Jr.95,Ateshian07b\"\nliteral \"true\"\n\n\\end_inset\n\n,\n the general expressions for \n\\begin_inset Formula $\\tilde{\\mu}^{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\mu}^{u}$\n\\end_inset\n\n take the form \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{\\mu}^{w} & =\\mu_{0}^{w}\\left(\\theta\\right)+\\frac{1}{\\rho_{T}^{w}}\\left(p-p_{0}-R\\theta\\,\\Phi\\,c\\right)\\,,\\\\\n\\tilde{\\mu}^{u} & =\\mu_{0}^{u}\\left(\\theta\\right)+\\frac{R\\theta}{M}\\ln\\frac{\\gamma c}{\\kappa c_{0}}\\,,\n\\end{aligned}\n\\label{eq110}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is the osmotic coefficient (a non-dimensional function of the state),\n which deviates from unity under non-ideal physico-chemical behavior.\n Therefore,\n a complete description of the physico-chemical state of solvent and solute requires constitutive relations for \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n and the effective solubility \n\\begin_inset Formula $\\tilde{\\kappa}=\\kappa/\\gamma$\n\\end_inset\n\n,\n which should generally depend on the solid matrix strain and the solute concentration.\n\\end_layout\n\n\\begin_layout Standard\nIt is also necessary to satisfy the balance of mass for each of the constituents.\n In the absence of chemical reactions,\n the statement of balance of mass for constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n reduces to \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\rho^{\\alpha}}{\\partial t}+\\divg\\left(\\rho^{\\alpha}\\mathbf{v}^{\\alpha}\\right)=0\\,.\\label{eq111}\n\\end{equation}\n\n\\end_inset\n\nThe apparent density may be related to the true density via \n\\begin_inset Formula $\\rho^{\\alpha}=\\varphi^{\\alpha}\\rho_{T}^{\\alpha}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varphi^{\\alpha}$\n\\end_inset\n\n is the volume fraction of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the mixture.\n Due to mixture saturation (no voids),\n the volume fractions add up to unity.\n Since the volume fraction of solute is considered negligible (\n\\begin_inset Formula $\\varphi^{u}\\ll\\varphi^{s},\\varphi^{w})$\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\sum\\nolimits_{\\alpha}\\varphi^{\\alpha}\\approx\\varphi^{s}+\\varphi^{w}=1$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\rho_{T}^{\\alpha}$\n\\end_inset\n\n of an incompressible constituent is invariant in space and time,\n these relations may be combined to produce the mixture balance of mass relation,\n \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=0,\\label{eq112}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{w}=\\varphi^{w}\\left(\\mathbf{v}^{w}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n is the volumetric flux of solvent relative to the solid.\n The balance of mass for the solute may also be written as \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\left(\\varphi^{w}c\\right)}{\\partial t}+\\divg\\left(\\mathbf{j}+\\varphi^{w}c\\,\\mathbf{v}^{s}\\right)=0\\,,\\label{eq113}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{j}=\\varphi^{w}c\\left(\\mathbf{v}^{u}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n is the molar flux of solute relative to the solid.\n This mass balance relation is obtained by recognizing that the solute apparent density (mass per mixture volume) is related to its concentration (moles per solution volume) via \n\\begin_inset Formula $\\rho^{u}=\\left(1-\\varphi^{s}\\right)Mc\\approx\\varphi^{w}Mc$\n\\end_inset\n\n.\n Finally,\n it can be shown via standard arguments that the mass balance for the solid matrix reduces to \n\\begin_inset Formula \n\\begin{equation}\n\\varphi^{s}=\\frac{\\varphi_{r}^{s}}{J}\\,,\\label{eq114}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the solid volume fraction in the reference state,\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I}+\\grad\\mathbf{u}$\n\\end_inset\n\n is the deformation gradient of the solid matrix.\n\\end_layout\n\n\\begin_layout Standard\nInverting the momentum balance equations in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq109\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it is now possible to relate the solvent and solute fluxes to the driving forces according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w} & =-\\tilde{\\mathbf{k}}\\cdot\\left(\\rho_{T}^{w}\\grad\\tilde{\\mu}^{w}+Mc\\frac{\\mathbf{d}}{d_{0}}\\grad\\tilde{\\mu}^{u}\\right)\\,,\\\\\n\\mathbf{j} & =\\mathbf{d}\\cdot\\left(-\\frac{M}{R\\theta}\\varphi^{w}c\\grad\\tilde{\\mu}^{u}+\\frac{c}{d_{0}}\\mathbf{w}\\right)\\,,\n\\end{aligned}\n\\label{eq115}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n is the solute diffusivity tensor in the mixture (solid\n\\begin_inset Formula $+$\n\\end_inset\n\nsolution),\n \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n is its (isotropic) diffusivity in free solution;\n \n\\begin_inset Formula $\\mathbf{\\tilde{k}}$\n\\end_inset\n\n is the hydraulic permeability tensor of the solution (solvent\n\\begin_inset Formula $+$\n\\end_inset\n\nsolute) through the porous solid matrix,\n which depends explicitly on concentration according to \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mathbf{k}}=\\left[\\mathbf{k}^{-1}+\\frac{R\\theta c}{\\varphi^{w}d_{0}}\\left(\\mathbf{I}-\\frac{\\mathbf{d}}{d_{0}}\\right)\\right]^{-1},\\label{eq116}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n represents the hydraulic permeability tensor of the solvent through the solid matrix.\n The permeability and diffusivity tensors are related to the diffusive drag tensors appearing in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq109\"\nnolink \"false\"\n\n\\end_inset\n\n according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k} & =\\left(\\varphi^{w}\\right)^{2}\\left(\\mathbf{f}^{ws}\\right)^{-1}\\,,\\\\\n\\mathbf{d}_{0} & =R\\theta\\varphi^{w}c\\left(\\mathbf{f}^{uw}\\right)^{-1}\\equiv d_{0}\\mathbf{I}\\,,\\\\\n\\mathbf{d} & =R\\theta\\varphi^{w}c\\left(\\mathbf{f}^{us}+\\mathbf{f}^{uw}\\right)^{-1}\\,,\n\\end{aligned}\n\\label{eq117}\n\\end{equation}\n\n\\end_inset\n\nthough these explicit relationships are not needed here since \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n and \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n may be directly specified in a particular analysis.\n Since the axiom of entropy inequality requires that the tensors \n\\begin_inset Formula $\\mathbf{f}^{\\alpha\\beta}$\n\\end_inset\n\n be positive semi-definite (see appendix of \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n),\n it follows that \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n must be greater than or equal to the largest eigenvalue of \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n.\n Constitutive relations are needed for these transport properties,\n which relate them to the solid matrix strain and solute concentration.\n Note that the relations in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq117\"\nnolink \"false\"\n\n\\end_inset\n\n represent generalizations of Darcy's law for fluid permeation through porous media,\n and Fick's law for solute diffusion in porous media or free solution.\n\\end_layout\n\n\\begin_layout Subsection\nContinuous Variables\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-continuous-variables\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn principle,\n the objective of the finite element analysis is to solve for the three unknowns,\n \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $p$\n\\end_inset\n\n and \n\\begin_inset Formula $c$\n\\end_inset\n\n,\n using the partial differential equations that enforce mixture momentum balance in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq108\"\nnolink \"false\"\n\n\\end_inset\n\n,\n mixture mass balance in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq112\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and solute mass balance in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq113\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The remaining solvent and solute momentum balances in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq115\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and solid mass balance in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq114\"\nnolink \"false\"\n\n\\end_inset\n\n,\n have been reduced to relations that may be substituted into the three partial differential equations as needed.\n Solving these equations requires the application of suitable boundary conditions that are consistent with mass,\n momentum and energy balances across boundary surfaces or interfaces.\n When defining boundaries or interfaces on the solid matrix (the conventional approach in solid mechanics),\n whose outward unit normal is \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n mass and momentum balance relations demonstrate that the mixture traction \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n and normal flux components \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $j_{n}=\\mathbf{j}\\cdot\\mathbf{n}$\n\\end_inset\n\n must be continuous across the interface \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07b,Eringen65\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Therefore,\n \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $j_{n}$\n\\end_inset\n\n may be prescribed as boundary conditions.\n\\end_layout\n\n\\begin_layout Standard\nCombining momentum and energy balances across an interface also demonstrates that \n\\begin_inset Formula $\\tilde{\\mu}^{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\mu}^{u}$\n\\end_inset\n\n must be continuous \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07b,Katzir-Katchalsky65\"\nliteral \"true\"\n\n\\end_inset\n\n,\n implying that these mechano-chemical potentials may be prescribed as boundary conditions.\n However,\n because of the arbitrariness of the reference states \n\\begin_inset Formula $\\mu_{0}^{w}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu_{0}^{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $p_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $c_{0}$\n\\end_inset\n\n,\n and the ill-conditioning of the logarithm function in the limit of small solute concentration,\n the mechano-chemical potentials do not represent practical choices for primary variables in a finite element implementation.\n An examination of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq110\"\nnolink \"false\"\n\n\\end_inset\n\n also shows that continuity of these potentials across an interface does not imply continuity of the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n or solute concentration \n\\begin_inset Formula $c$\n\\end_inset\n\n.\n Therefore,\n pressure and concentration are also unsuitable as nodal variables in a finite element analysis and they must be replaced by alternative choices.\n Based on the similar reasoning presented by Sun et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Sun99\"\nliteral \"true\"\n\n\\end_inset\n\n,\n an examination of the expressions in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq110\"\nnolink \"false\"\n\n\\end_inset\n\n shows that continuity may be enforced by using \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{p} & =p-R\\theta\\,\\Phi\\,c\\,,\\\\\n\\tilde{c} & =\\frac{c}{\\tilde{\\kappa}}\\,,\n\\end{aligned}\n\\label{eq118}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is the effective fluid pressure and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n is the effective solute concentration in the mixture.\n Note that \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n represents that part of the fluid pressure which does not result from osmotic effects (since the term \n\\begin_inset Formula $R\\theta\\,\\Phi\\,c$\n\\end_inset\n\n may be viewed as the osmotic pressure contribution to \n\\begin_inset Formula $p)$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n is a straightforward measure of the solute activity,\n since \n\\begin_inset Formula $a^{u}=\\tilde{c}/c_{0}$\n\\end_inset\n\n.\n Therefore these alternative variables have clear physical meanings.\n\\end_layout\n\n\\begin_layout Standard\nSince the unknowns are now given by \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n,\n the governing partial differential equations may be rewritten in the form \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & \\grad\\left(\\tilde{p}+R\\theta\\,\\Phi\\,\\tilde{\\kappa}\\tilde{c}\\right)+\\divg\\boldsymbol{\\sigma}^{e}=\\mathbf{0}\\,,\\\\\n & \\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=0\\,,\\\\\n & \\frac{\\partial\\left(\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial t}+\\divg\\left(\\mathbf{j}+\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\,\\mathbf{v}^{s}\\right)=0\\,,\n\\end{aligned}\n\\label{eq119}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w} & =-\\tilde{\\mathbf{k}}\\cdot\\left(\\grad\\tilde{p}+R\\theta\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\grad\\tilde{c}\\right)\\,,\\\\\n\\mathbf{j} & =\\tilde{\\kappa}\\mathbf{d}\\cdot\\left(-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}\\right)\\,,\\\\\n\\tilde{\\mathbf{k}} & =\\left[\\mathbf{k}^{-1}+\\frac{R\\theta}{\\varphi^{w}}\\frac{\\tilde{\\kappa}\\tilde{c}}{d_{0}}\\left(\\mathbf{I}-\\frac{\\mathbf{d}}{d_{0}}\\right)\\right]^{-1}\\,.\n\\end{aligned}\n\\label{eq120}\n\\end{equation}\n\n\\end_inset\n\nConstitutive equations are needed to relate \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n to the solid matrix strain and effective solute concentration.\n\\end_layout\n\n\\begin_layout Section\nTriphasic and Multiphasic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Triphasic-Multiphasic-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMultiphasic materials represent an extension of the biphasic-solute material,\n where the mixture may contain a multitude of solutes.\n These solutes may be either electrically charged (ionized) or neutral.\n Similarly,\n the solid matrix may either carry electrical charge (a fixed charge density) or be neutral.\n A triphasic material is a special case of a multiphasic material,\n having two solutes that carry opposite charges.\n Triphasic and multiphasic materials may be used to model porous deformable biological tissues whose solid matrix may be charged and whose interstitial fluid may contain any number of charged or neutral solutes.\n When mixture constituents are electrically charged,\n the response of the tissue to various loading conditions may encompass a range of mechano-electrochemical phenomena,\n including permeation,\n diffusion,\n osmosis,\n streaming potentials and streaming currents.\n To better understand multiphasic materials,\n the reader is encouraged to review the descriptions of biphasic (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Material\"\nnolink \"false\"\n\n\\end_inset\n\n) and biphasic-solute materials (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Solute-Material\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nGoverning Equations\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-governing-equations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn multiphasic materials the solvent is assumed to be neutral,\n whereas the solid and solutes may carry charge.\n The mixture is isothermal and all constituents are considered to be intrinsically incompressible.\n Since the viscosity of the fluid constituents (solvent and solutes) is considered negligible relative to the frictional interactions among constituents,\n the stress tensor \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n for the mixture includes only a contribution from the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n and the stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n in the solid,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}\\,.\\label{eq121}\n\\end{equation}\n\n\\end_inset\n\nThe mechano-chemical potential of the solvent is given by \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mu}^{w}=\\mu_{0}^{w}\\left(\\theta\\right)+\\frac{1}{\\rho_{T}^{w}}\\left(p-p_{0}-R\\theta\\Phi\\sum\\limits_{\\alpha}c^{\\alpha}\\right)\\,,\\label{eq122}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu_{0}^{w}\\left(\\theta\\right)$\n\\end_inset\n\n is the solvent chemical potential in the solvent standard state,\n \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the absolute temperature,\n \n\\begin_inset Formula $\\rho_{T}^{w}$\n\\end_inset\n\n is the true density of the solvent (which is invariant since the solvent is assumed intrinsically incompressible),\n \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure,\n \n\\begin_inset Formula $p_{0}$\n\\end_inset\n\n is the corresponding pressure in the standard state,\n \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is the non-dimensional osmotic coefficient,\n and \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n is the solution volume-based concentration of solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n The summation is taken over all solutes in the mixture.\n The mechano-electrochemical potential of each solute is similarly given by \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mu}^{\\alpha}=\\mu_{0}^{\\alpha}\\left(\\theta\\right)+\\frac{R\\theta}{M^{\\alpha}}\\left(\\frac{z^{\\alpha}F_{c}}{R\\theta}\\left(\\psi-\\psi_{0}\\right)+\\ln\\frac{\\gamma^{\\alpha}c^{\\alpha}}{\\kappa^{\\alpha}c_{0}^{\\alpha}}\\right),\\label{eq123}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $M^{\\alpha}$\n\\end_inset\n\n is the molar mass of the solute,\n \n\\begin_inset Formula $\\gamma^{\\alpha}$\n\\end_inset\n\n is its activity coefficient,\n \n\\begin_inset Formula $\\kappa^{\\alpha}$\n\\end_inset\n\n is its solubility,\n \n\\begin_inset Formula $z^{\\alpha}$\n\\end_inset\n\n is its charge number,\n and \n\\begin_inset Formula $c_{0}^{\\alpha}$\n\\end_inset\n\n is its concentration in the solute standard state;\n \n\\begin_inset Formula $F_{c}$\n\\end_inset\n\n is Faraday's constant,\n \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is the electrical potential of the mixture,\n and \n\\begin_inset Formula $\\psi_{0}$\n\\end_inset\n\n is the corresponding potential in the standard state.\n\\end_layout\n\n\\begin_layout Standard\nIn these relations,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\alpha}$\n\\end_inset\n\n are functions of state that describe the deviation of the mixture from ideal physico-chemical behavior;\n \n\\begin_inset Formula $\\kappa^{\\alpha}$\n\\end_inset\n\n represents the fraction of the pore volume which may be occupied by solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n The standard state represents an arbitrary set of reference conditions for the physico-chemical state of each constituent.\n Therefore,\n the values of \n\\begin_inset Formula $\\mu_{0}^{w}\\left(\\theta\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $p_{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\psi_{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu_{0}^{\\alpha}\\left(\\theta\\right)$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $c_{0}^{\\alpha}$\n\\end_inset\n\n,\n remain invariant over the entire domain of definition of an analysis.\n Since \n\\begin_inset Formula $\\kappa^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\alpha}$\n\\end_inset\n\n appear together as a ratio,\n they may be combined into a single material function,\n \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}=\\kappa^{\\alpha}/\\gamma^{\\alpha}$\n\\end_inset\n\n,\n called the effective solubility.\n\\end_layout\n\n\\begin_layout Standard\nIn multiphasic mixtures,\n it is also assumed that electroneutrality is satisfied at every point in the continuum.\n Therefore,\n the net electrical charge summed over all constituents must reduce to zero,\n and no net charge accumulation may occur at any time.\n Denoting the fixed charge density of the solid by \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n (moles of equivalent charge per solution volume),\n and recognizing that the solvent is always considered neutral,\n the electroneutrality condition may be written as \n\\begin_inset Formula \n\\begin{equation}\nc^{F}+\\sum\\limits_{\\alpha}z^{\\alpha}c^{\\alpha}=0\\,.\\label{eq124}\n\\end{equation}\n\n\\end_inset\n\nThis condition represents a constraint on a mixture of charged constituents.\n If none of the constituents are charged (\n\\begin_inset Formula $c^{F}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $z^{\\alpha}=0$\n\\end_inset\n\n for all \n\\begin_inset Formula $\\alpha)$\n\\end_inset\n\n,\n the constraint disappears.\n\\end_layout\n\n\\begin_layout Standard\nEach constituent of the mixture must satisfy the axiom of mass balance.\n In the absence of chemical reactions involving constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n,\n its mass balance equation is \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\rho^{\\alpha}}{\\partial t}+\\divg\\left(\\rho^{\\alpha}\\mathbf{v}^{\\alpha}\\right)=0\\,,\\label{eq125}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho^{\\alpha}$\n\\end_inset\n\n is the apparent density and \n\\begin_inset Formula $\\mathbf{v}^{\\alpha}$\n\\end_inset\n\n is the velocity of that constituent.\n For solutes,\n the apparent density is related to the concentration according to \n\\begin_inset Formula $\\rho^{\\alpha}=\\left(1-\\varphi^{s}\\right)M^{\\alpha}c^{\\alpha}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varphi^{s}$\n\\end_inset\n\n is the volume fraction of the solid.\n When the solute volume fractions are negligible,\n it follows that \n\\begin_inset Formula $1-\\varphi^{s}\\approx\\varphi^{w}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varphi^{w}$\n\\end_inset\n\n is the solvent volume fraction.\n The molar flux of the solute relative to the solid is given by \n\\begin_inset Formula $\\mathbf{j}^{\\alpha}=\\varphi^{w}c^{\\alpha}\\left(\\mathbf{v}^{\\alpha}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{v}^{\\alpha}$\n\\end_inset\n\n is the solute velocity.\n Using these relations,\n the mass balance relation for the solute may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\frac{1}{J}\\frac{D^{s}}{Dt}\\left(J\\varphi^{w}c^{\\alpha}\\right)+\\divg\\mathbf{j}^{\\alpha}=0\\,,\\label{eq126}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $D^{s}\\left(\\cdot\\right)/Dt$\n\\end_inset\n\n represents the material time derivative in the spatial frame,\n following the solid;\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient of the solid.\n This form of the mass balance for the solute is convenient for a finite element formulation where the mesh is defined on the solid matrix.\n\\end_layout\n\n\\begin_layout Standard\nThe volume flux of solvent relative to the solid is given by \n\\begin_inset Formula $\\mathbf{w}=\\varphi^{w}\\left(\\mathbf{v}^{w}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{v}^{w}$\n\\end_inset\n\n is the solvent velocity.\n When solute volume fractions are negligible,\n the mass balance equation for the mixture reduces to \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=0\\,.\\label{eq126b}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n the mass balance for the solid may be reduced to the form \n\\begin_inset Formula $D^{s}\\left(J\\varphi^{s}\\right)/Dt=0$\n\\end_inset\n\n,\n which may be integrated to produce the algebraic relation \n\\begin_inset Formula $\\varphi^{s}=\\varphi_{r}^{s}/J$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the solid volume fraction in the stress-free reference state of the solid.\n\\end_layout\n\n\\begin_layout Standard\nDifferentiating the electroneutrality condition in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq124\"\nnolink \"false\"\n\n\\end_inset\n\n using the material time derivative following the solid,\n and substituting the mass balance relations into the resulting expressions,\n produces a constraint on the solute fluxes:\n \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\sum\\limits_{\\alpha\\ne s,w}z^{\\alpha}\\mathbf{j}^{\\alpha}=0\\,.\\label{eq127}\n\\end{equation}\n\n\\end_inset\n\nRecognizing that \n\\begin_inset Formula $\\mathbf{I}_{e}=F_{c}\\sum\\nolimits_{\\alpha\\ne s,w}z^{\\alpha}\\mathbf{j}^{\\alpha}$\n\\end_inset\n\n is the current density in the mixture,\n with \n\\begin_inset Formula $F_{c}$\n\\end_inset\n\n representing Faraday's constant,\n the relation of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq127\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to one of the Maxwell's equation,\n \n\\begin_inset Formula $\\divg\\mathbf{I}_{e}=0$\n\\end_inset\n\n,\n in the special case when there can be no charge accumulation (electroneutrality).\n\\end_layout\n\n\\begin_layout Standard\nAs described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:BS-continuous-variables\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n and solute concentrations \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n are not continuous across boundaries of a mixture,\n whereas \n\\begin_inset Formula $\\tilde{\\mu}^{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\mu}^{\\alpha}$\n\\end_inset\n\n's for the solutes do satisfy continuity.\n Therefore,\n in a finite element implementation,\n the following continuous variables are used as nodal degrees of freedom:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{p}=p-R\\theta\\Phi\\sum\\limits_{\\alpha\\ne s,w}c^{\\alpha}\\,,\\label{eq128}\n\\end{equation}\n\n\\end_inset\n\nwhich represents the effective fluid pressure,\n and \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{c}^{\\alpha}=c^{\\alpha}/\\tilde{\\kappa}^{\\alpha}\\,,\\label{eq129}\n\\end{equation}\n\n\\end_inset\n\nwhich represents the effective solute concentration.\n In the last expression,\n \n\\begin_inset Formula $\\tilde{\\kappa}^{\\alpha}$\n\\end_inset\n\n is the partition coefficient of the solute,\n which is related to the effective solubility and electric potential according to \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\kappa}^{\\alpha}=\\hat{\\kappa}^{\\alpha}\\exp\\left(-\\frac{z^{\\alpha}F_{c}\\psi}{R\\theta}\\right)\\,.\\label{eq130}\n\\end{equation}\n\n\\end_inset\n\nPhysically,\n since \n\\begin_inset Formula $R\\theta\\,\\Phi\\sum\\nolimits_{\\alpha\\ne s,w}c^{\\alpha}$\n\\end_inset\n\n is the osmotic (chemical) contribution to the fluid pressure,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n may be interpreted as that part of the total (mechano-chemical) fluid pressure which does not result from osmotic effects;\n thus,\n it is the mechanical contribution to \n\\begin_inset Formula $p$\n\\end_inset\n\n.\n Similarly,\n the effective solute concentration \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n represents the true contribution of the molar solute content to its electrochemical potential.\n\\end_layout\n\n\\begin_layout Standard\nWhen using these variables instead of mechano-electrochemical potentials,\n the momentum equations for the solvent and solutes may be inverted to produce the following flux relations:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}=-\\tilde{\\mathbf{k}}\\cdot\\left(\\mbox{grad}\\tilde{p}+R\\theta\\sum\\limits_{\\beta\\ne s,w}\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\mathbf{d}^{\\beta}\\cdot\\mbox{grad}\\tilde{c}^{\\beta}\\right)\\,,\\label{eq131}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{j}^{\\alpha}=\\tilde{\\kappa}^{\\alpha}\\mathbf{d}^{\\alpha}\\cdot\\left(-\\varphi^{w}\\mbox{grad}\\tilde{c}^{\\alpha}+\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}\\right)\\,,\\label{eq132}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mathbf{k}}=\\left[\\mathbf{k}^{-1}+\\frac{R\\theta}{\\varphi^{w}}\\sum\\limits_{\\alpha\\ne s,w}\\frac{c^{\\alpha}}{d_{0}^{\\alpha}}\\left(\\mathbf{I}-\\frac{\\mathbf{d}^{\\alpha}}{d_{0}^{\\alpha}}\\right)\\right]^{-1}\\label{eq133}\n\\end{equation}\n\n\\end_inset\n\nis the effective hydraulic permeability of the solution (solvent\n\\begin_inset Formula $+$\n\\end_inset\n\nsolutes) in the mixture.\n The momentum equation for the mixture is \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\boldsymbol{\\sigma}=\\mathbf{0}\\,.\\label{eq134}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nConstrained Reactive Mixture of Solids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Mixture-of-Solids\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA solid material may consist of a heterogeneous mixture of various solid constituents that are constrained to move together.\n In this section we describe fundamental considerations for such constrained mixtures.\n\\end_layout\n\n\\begin_layout Subsection\nMixture Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mixture-Kinematics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a constrained mixture of multiple solid constituents \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n The motion of each constituent is given by \n\\begin_inset Formula $\\boldsymbol{\\chi}^{\\sigma}\\left(\\mathbf{X}^{\\sigma},t\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n denotes a material point in the reference configuration of that constituent.\n At the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n various constituents \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n which occupy an elemental region with a spatial position \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n may have originated from distinct referential positions \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n.\n We often label a convenient constituent as the \n\\shape italic\nmaster constituent\n\\shape default\n \n\\begin_inset Formula $s$\n\\end_inset\n\n (e.g.,\n the oldest constituent in a reactive mixture with evolving composition) and call the reference configuration \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n the \n\\shape italic\nmaster reference configuration\n\\shape default\n.\n All the referential mass densities and mass density supplies (see below) are evaluated relative to the master reference configuration \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n.\n The kinematics of each constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n may be related to the kinematics of the master constituent \n\\begin_inset Formula $s$\n\\end_inset\n\n through\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\boldsymbol{\\chi}^{s}\\left(\\mathbf{X}^{s},t\\right)=\\boldsymbol{\\chi}^{\\sigma}\\left(\\mathbf{X}^{\\sigma},t\\right)\\,.\\label{eq:general-mixture-kinematics}\n\\end{equation}\n\n\\end_inset\n\nTaking the material time derivative of this relation in the material frame and recognizing that this relation must hold for all \n\\begin_inset Formula $t$\n\\end_inset\n\n in the case of a constrained mixture establishes that all constituents share the same velocity \n\\begin_inset Formula $\\mathbf{v}^{\\sigma}=\\mathbf{v}^{s}$\n\\end_inset\n\n.\n However,\n as detailed previously \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10,Nims17\"\nliteral \"false\"\n\n\\end_inset\n\n,\n constituents may have distinct deformation gradients \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}=\\partial\\boldsymbol{\\chi}^{\\sigma}/\\partial\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n.\n When a reaction converts a reactant \n\\begin_inset Formula $\\sigma=a$\n\\end_inset\n\n into a product \n\\begin_inset Formula $\\sigma=b$\n\\end_inset\n\n,\n these constituents may have distinct reference configurations.\n The deformation gradient of the master constituent \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n,\n which also serves as the total deformation gradient,\n may be related to the \n\\shape italic\nrelative deformation gradient\n\\shape default\n \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n by applying the chain rule to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mixture-kinematics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n producing\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}^{s}\\left(\\mathbf{X}^{s},t\\right)=\\frac{\\partial\\boldsymbol{\\chi}^{s}\\left(\\mathbf{X}^{s},t\\right)}{\\partial\\mathbf{X}^{\\sigma}}\\cdot\\frac{\\partial\\mathbf{X}^{\\sigma}\\left(\\mathbf{X}^{s}\\right)}{\\partial\\mathbf{X}^{s}}=\\mathbf{F}^{\\sigma}\\left(\\mathbf{X}^{s},t\\right)\\cdot\\mathbf{F}^{\\sigma s}\\left(\\mathbf{X}^{s}\\right)\\,.\\label{eq:general-total-def-grad}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}\\left(\\mathbf{X}^{s}\\right)$\n\\end_inset\n\n is the deformation gradient of \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n relative to \n\\begin_inset Formula $s$\n\\end_inset\n\n,\n which must be \n\\emph on\npostulated by constitutive assumption\n\\emph default\n.\n The relationship between \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n is time-invariant;\n consequently,\n \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n is a time-invariant spatial mapping.\n It follows that only one deformation gradient represents an independent state variable in a constrained mixture framework,\n whereas all others are related to it via eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n;\n any of the \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n's may be selected,\n based on convenience.\n In eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n the spatio-temporal arguments have been written explicitly for clarity.\n These dependencies are implied in the forthcoming sections and henceforth those arguments may be selectively suppressed.\n Taking the determinant of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n produces a relation between the volume ratios \n\\begin_inset Formula $J^{\\sigma}=\\det\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $J^{s}=\\det\\mathbf{F}^{s}$\n\\end_inset\n\n ,\n\\begin_inset Formula \n\\begin{equation}\nJ^{s}=J^{\\sigma}J^{\\sigma s}\\,,\\label{eq:general-jacobian-relation}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J^{\\sigma s}=\\det\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nMixture Composition\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mixture-Composition\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n has an \n\\shape italic\napparent mass density\n\\shape default\n \n\\begin_inset Formula $\\rho^{\\sigma}$\n\\end_inset\n\n which may evolve due to deformation,\n or due to reactive processes which alter the mixture composition.\n Following \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bowen69,Ateshian07b\"\nliteral \"false\"\n\n\\end_inset\n\n,\n we define the \n\\shape italic\nreferential apparent mass density\n\\shape default\n \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n of each constituent as\n\\begin_inset Formula \n\\begin{equation}\n\\rho_{r}^{\\sigma}=J^{s}\\rho^{\\sigma}\\,.\\label{eq:general-referential-mass-density}\n\\end{equation}\n\n\\end_inset\n\nEquation\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-referential-mass-density\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n expresses the mass of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n per referential volume of the master constituent \n\\begin_inset Formula $s$\n\\end_inset\n\n;\n thus \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n may only evolve if the mass content changes via reactions,\n making it a suitable state variable for tracking composition in a reactive framework.\n The axiom of mass balance for each constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n may be written as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\rho}_{r}^{\\sigma}=\\hat{\\rho}_{r}^{\\sigma}\\,,\\label{eq:mass-balance-alpha-1}\n\\end{equation}\n\n\\end_inset\n\nwhere the dot operator represents the material time derivative and \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}$\n\\end_inset\n\n is the \n\\shape italic\nreferential mass supply density\n\\shape default\n for constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n,\n representing the rate at which mass (per referential volume) is added to \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n due to reactions with all other mixture constituents \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10,Nims17\"\nliteral \"false\"\n\n\\end_inset\n\n.\n A constitutive relation must be provided for \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}$\n\\end_inset\n\n for various types of reactions.\n The mixture referential mass density \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\rho_{r}=\\sum_{\\sigma}\\rho_{r}^{\\sigma}\\,.\\label{eq:general-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nThis summation is carried out over all constituents.\n When a constrained mixture of solid constituents represents a closed system,\n \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n remains constant over time.\n Taking the material time derivative of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-alpha-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n shows that the referential mass density supplies must satisfy\n\\begin_inset Formula \n\\begin{equation}\n\\sum_{\\sigma}\\hat{\\rho}_{r}^{\\sigma}=0\\,.\\label{eq:mass-balance-constraint}\n\\end{equation}\n\n\\end_inset\n\nHowever,\n if a constrained mixture of solid constituents represents an open system (i.e.,\n if fluid constituents are present in the mixture,\n either explicitly or implicitly),\n then \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is no longer necessarily constant,\n since there may be mass exchange between fluid and solid constituents.\n\\end_layout\n\n\\begin_layout Subsection\nMixture Free Energy and Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mixture-Free-Energy\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nreferential free energy density\n\\shape default\n of the mixture is obtained as\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\rho_{r}\\psi=\\sum_{\\sigma}\\rho_{r}^{\\sigma}\\psi^{\\sigma}\\,,\\label{eq:general-free-energy}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\psi^{\\sigma}$\n\\end_inset\n\n is the \n\\shape italic\nspecific free energy\n\\shape default\n of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n and \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is the specific free energy of the mixture.\n An important function of state which arises later in our treatment is the \n\\shape italic\nchemical potential\n\\shape default\n of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n,\n given by\n\\begin_inset Formula \n\\begin{equation}\n\\mu^{\\sigma}=\\frac{\\partial\\Psi_{r}}{\\partial\\rho_{r}^{\\sigma}}\\,.\\label{eq:general-chemical-potential}\n\\end{equation}\n\n\\end_inset\n\nThe mixture Cauchy stress is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{1}{J^{s}}\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{F}^{s}}\\cdot\\left(\\mathbf{F}^{s}\\right)^{T}=\\frac{2}{J^{s}}\\mathbf{F}^{s}\\cdot\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{C}^{s}}\\cdot\\left(\\mathbf{F}^{s}\\right)^{T}\\,,\\label{eq:general-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{C}^{s}=\\mathbf{F}^{s}\\cdot\\left(\\mathbf{F}^{s}\\right)^{T}$\n\\end_inset\n\n is the right Cauchy-Green tensor.\n The spatial elasticity tensor may be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\frac{4}{J^{s}}\\left(\\mathbf{F}^{s}\\oslash\\mathbf{F}^{s}\\right):\\frac{\\partial^{2}\\Psi_{r}}{\\partial\\mathbf{C}^{s}\\partial\\mathbf{C}^{s}}:\\left(\\left(\\mathbf{F}^{s}\\right)^{T}\\oslash\\left(\\mathbf{F}^{s}\\right)^{T}\\right)\\,.\\label{eq:general-mixture-elasticity}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nClosed System of Solid Constituents\n\\end_layout\n\n\\begin_layout Standard\nIn reactive frameworks where \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n evolves according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-alpha-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and where \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n remains constant due to the fact that the solid mixture represents a closed system,\n it may be convenient to define the mass fraction\n\\begin_inset Formula \n\\begin{equation}\nw^{\\sigma}=\\frac{\\rho_{r}^{\\sigma}}{\\rho_{r}}\\,,\\label{eq:mass-fraction}\n\\end{equation}\n\n\\end_inset\n\nin which case we may rewrite eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-free-energy\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as \n\\begin_inset Formula $\\Psi_{r}=\\sum_{\\sigma}w^{\\sigma}\\Psi_{r}^{\\sigma}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\Psi_{r}^{\\sigma}\\equiv\\rho_{r}\\psi^{\\sigma}$\n\\end_inset\n\n is the referential strain energy density of solid \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n under the assumption that it is the sole mixture constituent,\n normalized by the referential volume of the master constituent \n\\begin_inset Formula $s$\n\\end_inset\n\n.\n Based on eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n the mass fractions satisfy \n\\begin_inset Formula $\\sum_{\\sigma}w^{\\sigma}=1$\n\\end_inset\n\n.\n In this case,\n when \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}\\ne\\mathbf{X}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\psi^{\\sigma}$\n\\end_inset\n\n is most conveniently expressed as a function of \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n,\n we may use eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to evaluate \n\\begin_inset Formula $\\partial\\mathbf{F}^{s}/\\partial\\mathbf{F}^{\\sigma}=\\mathbf{I}\\oslash\\left(\\mathbf{F}^{\\sigma s}\\right)^{-T}$\n\\end_inset\n\n and calculate the mixture stress using the alternative form\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{1}{J^{s}}\\sum_{\\sigma}w^{\\sigma}\\frac{\\partial\\Psi_{r}^{\\sigma}}{\\partial\\mathbf{F}^{\\sigma}}\\cdot\\left(\\mathbf{F}^{\\sigma}\\right)^{T}\\,.\\label{eq:general-mixture-stress-redux}\n\\end{equation}\n\n\\end_inset\n\nThis expression shows that the mixture stress may evolve not only due to temporal changes in the state of strain but also due to reactive changes in the mass fractions \n\\begin_inset Formula $w^{\\sigma}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio the referential strain energy density for any solid mixture constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is evaluated from the same library of constitutive models used in single-constituent solids.\n In this library the calculation of the referential strain energy density is based on the assumption that the reference configuration corresponds to the configuration when the deformation gradient passed to those functions is equal to the identity tensor.\n Thus,\n passing \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n to those functions returns the correct \n\\begin_inset Formula $\\Psi_{r}^{\\sigma}$\n\\end_inset\n\n.\n However,\n when passing \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n as an argument to those functions,\n the referential volume is based on the configuration \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n.\n Let the referential free energy density returned by FEBio for an argument \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n be denoted by \n\\begin_inset Formula $\\Psi_{0}^{\\sigma}$\n\\end_inset\n\n.\n We may similarly denote the corresponding Cauchy stress and spatial elasticity tensors by \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}^{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}_{0}^{\\sigma}$\n\\end_inset\n\n.\n Here,\n the subscript \n\\begin_inset Formula $0$\n\\end_inset\n\n has two meanings:\n First it emphasizes that the corresponding function is evaluated using \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n for the mixture constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n;\n second it emphasizes that the calculation returns the corresponding measure under the assumption that the mixture consists entirely of that constituent,\n so that its multiplication by the scale factor \n\\begin_inset Formula $w^{\\sigma}$\n\\end_inset\n\n returns the actual contribution of that measure to the entire mixture.\n Based on eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-jacobian-relation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n we find that \n\\begin_inset Formula $\\Psi_{r}^{\\sigma}=J^{\\sigma s}\\Psi_{0}^{\\sigma}$\n\\end_inset\n\n so that the mixture free energy density may be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\sum_{\\sigma}w^{\\sigma}J^{\\sigma s}\\Psi_{0}^{\\sigma}\\left(\\mathbf{F}^{\\sigma}\\right)\\,,\\label{eq:febio-mixture-sed}\n\\end{equation}\n\n\\end_inset\n\nwhereas the mixture Cauchy stress and spatial elasticity are given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sum_{\\sigma}w^{\\sigma}\\boldsymbol{\\sigma}_{0}^{\\sigma}\\,,\\quad\\boldsymbol{\\mathcal{C}}=\\sum_{\\sigma}w^{\\sigma}\\boldsymbol{\\mathcal{C}}_{0}^{\\sigma}\\,,\\label{eq:febio-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}_{0}^{\\sigma}=\\frac{1}{J^{\\sigma}}\\frac{\\partial\\Psi_{0}^{\\sigma}}{\\partial\\mathbf{F}^{\\sigma}}\\cdot\\left(\\mathbf{F}^{\\sigma}\\right)^{T}=\\frac{1}{J^{s}}\\frac{\\partial\\Psi_{r}^{\\sigma}}{\\partial\\mathbf{F}^{\\sigma}}\\cdot\\left(\\mathbf{F}^{\\sigma}\\right)^{T}\\label{eq:febio-stress-function}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}_{0}^{\\sigma}=\\frac{4}{J^{\\sigma}}\\left(\\mathbf{F}^{\\sigma}\\oslash\\mathbf{F}^{\\sigma}\\right):\\frac{\\partial^{2}\\Psi_{0}^{\\sigma}}{\\partial\\mathbf{C}^{\\sigma}\\partial\\mathbf{C}^{\\sigma}}:\\left(\\left(\\mathbf{F}^{\\sigma}\\right)^{T}\\oslash\\left(\\mathbf{F}^{\\sigma}\\right)^{T}\\right)\\,.\\label{eq:febio-elasticity-function}\n\\end{equation}\n\n\\end_inset\n\nThese relations show that the Cauchy stress and spatial elasticity tensor of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be evaluated using existing FEBio functions without needing to adjust for the choice of reference configuration \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n or \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n,\n as evidenced by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-stress-function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the case of the stress.\n However the referential strain energy density needs to be properly scaled by \n\\begin_inset Formula $J^{\\sigma s}$\n\\end_inset\n\n as shown in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-mixture-sed\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe calculation of the 2nd Piola-Kirchhoff stress \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n for each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is more elaborate.\n When using the master reference configuration \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n,\n this stress is given by\n\\begin_inset Formula \n\\[\n\\mathbf{S}^{\\sigma}\\left(\\mathbf{F}^{s}\\right)=2\\frac{\\partial\\Psi_{r}^{\\sigma}\\left(\\mathbf{F}^{s}\\right)}{\\partial\\mathbf{C}^{s}}\\,.\n\\]\n\n\\end_inset\n\nWhen using the reference configuration \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n,\n the stress is evaluated from a similar relation\n\\begin_inset Formula \n\\[\n\\mathbf{S}_{0}^{\\sigma}\\left(\\mathbf{F}^{\\sigma}\\right)=2\\frac{\\partial\\Psi_{0}^{\\sigma}\\left(\\mathbf{F}^{\\sigma}\\right)}{\\partial\\mathbf{C}^{\\sigma}}\\,.\n\\]\n\n\\end_inset\n\nIt can be shown that these stresses are related according to\n\\begin_inset Formula \n\\[\n\\mathbf{S}^{\\sigma}\\left(\\mathbf{F}^{s}\\right)=J^{\\sigma s}\\left(\\mathbf{F}^{\\sigma s}\\right)^{-1}\\cdot\\mathbf{S}_{0}^{\\sigma}\\left(\\mathbf{F}^{\\sigma}\\right)\\cdot\\left(\\mathbf{F}^{\\sigma s}\\right)^{-T}\\,.\n\\]\n\n\\end_inset\n\nFEBio does not use this calculation for solid mixtures,\n as all internal calculations employ the Cauchy stress.\n\\end_layout\n\n\\begin_layout Subsubsection\nOpen System of Solid Constituents\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n as defined in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n does not remain constant due to the implicit or explicit presence of fluid constituents,\n we may choose to define the mass fraction \n\\begin_inset Formula $\\omega^{\\sigma}$\n\\end_inset\n\n of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n based on the true density \n\\begin_inset Formula $\\rho_{0}^{\\sigma}$\n\\end_inset\n\n of the solid constituent,\n\\begin_inset Formula \n\\begin{equation}\n\\omega^{\\sigma}=\\frac{\\rho_{r}^{\\sigma}}{\\rho_{0}^{\\sigma}}\\,,\\label{eq:mass-fraction-1}\n\\end{equation}\n\n\\end_inset\n\nIn this case,\n \n\\begin_inset Formula $\\omega^{\\sigma}=1$\n\\end_inset\n\n when the solid mixture consists entirely of constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n Unlike \n\\begin_inset Formula $w^{\\sigma}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the summation of \n\\begin_inset Formula $\\omega^{\\sigma}$\n\\end_inset\n\n over all \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is meaningless here,\n since the denominator in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-fraction-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is not common to all \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n Nevertheless,\n the mixture free energy density in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-free-energy\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten conveniently as \n\\begin_inset Formula $\\Psi_{r}=\\sum_{\\sigma}\\omega^{\\sigma}\\Psi_{0}^{\\sigma}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\Psi_{0}^{\\sigma}\\equiv\\rho_{0}^{\\sigma}\\psi^{\\sigma}$\n\\end_inset\n\n represents the free energy density of pure solid \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n Here again,\n \n\\begin_inset Formula $\\psi^{\\sigma}$\n\\end_inset\n\n is most conveniently expressed as a function of \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n,\n so that eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be use to evaluate \n\\begin_inset Formula $\\partial\\mathbf{F}^{s}/\\partial\\mathbf{F}^{\\sigma}=\\mathbf{I}\\oslash\\left(\\mathbf{F}^{\\sigma s}\\right)^{-T}$\n\\end_inset\n\n and calculate the mixture stress using the alternative form\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{1}{J^{s}}\\sum_{\\sigma}\\omega^{\\sigma}\\frac{\\partial\\Psi_{0}^{\\sigma}}{\\partial\\mathbf{F}^{\\sigma}}\\cdot\\left(\\mathbf{F}^{\\sigma}\\right)^{T}\\,.\\label{eq:general-mixture-stress-redux-1}\n\\end{equation}\n\n\\end_inset\n\nThis expression shows that the mixture stress may evolve not only due to temporal changes in the state of strain but also due to reactive changes in the mass fractions \n\\begin_inset Formula $\\omega^{\\sigma}$\n\\end_inset\n\n.\n In this type of open-system formulation it becomes the user's responsibility to ensure that all other \n\\begin_inset Formula $\\omega^{\\sigma}$\n\\end_inset\n\n values reduce to zero when one of the \n\\begin_inset Formula $\\omega^{\\sigma}$\n\\end_inset\n\n reaches unity.\n\\end_layout\n\n\\begin_layout Subsection\nFrame Indifference\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Frame-Indifference\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo maintain frame indifference for constrained mixtures we must satisfy \n\\begin_inset Formula $\\boldsymbol{\\sigma}\\left(\\mathbf{Q}\\cdot\\mathbf{F}^{s}\\right)=\\mathbf{Q}\\cdot\\boldsymbol{\\sigma}\\left(\\mathbf{F}^{s}\\right)\\cdot\\mathbf{Q}^{T}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n is an orthogonal transformation that maintains the symmetry group of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian22a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Here,\n we abbreviated the list of state variables for \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n to just \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n,\n for notational simplicity.\n This frame indifference is automatically satisfied for \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n based on the hyperelasticity relation of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and the invariance of \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n to \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n.\n By the same argument,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}^{\\sigma}$\n\\end_inset\n\n as given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-stress-function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n satisfies \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}^{\\sigma}\\left(\\mathbf{Q}\\cdot\\mathbf{F}^{\\sigma}\\right)=\\mathbf{Q}\\cdot\\boldsymbol{\\sigma}_{0}^{\\sigma}\\left(\\mathbf{F}^{\\sigma}\\right)\\cdot\\mathbf{Q}^{T}$\n\\end_inset\n\n for transformations that maintain the symmetry group of \n\\begin_inset Formula $\\psi^{\\sigma}$\n\\end_inset\n\n.\n Since the symmetry transformations \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n must also belong to the symmetry groups of all \n\\begin_inset Formula $\\psi^{\\sigma}$\n\\end_inset\n\n,\n we need to ensure that \n\\begin_inset Formula $\\mathbf{F}^{s*}=\\mathbf{Q}\\cdot\\mathbf{F}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{F}^{\\sigma*}=\\mathbf{Q}\\cdot\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n also satisfy the kinematic constraint of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for those transformations \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n,\n namely \n\\begin_inset Formula $\\mathbf{F}^{s*}=\\mathbf{F}^{\\sigma*}\\cdot\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n.\n This can be achieved if and only if \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n is invariant under a change of frame.\n It follows from this argument that the right Cauchy-Green tensors are invariant,\n \n\\begin_inset Formula $\\mathbf{C}^{s*}=\\left(\\mathbf{F}^{s*}\\right)^{T}\\cdot\\mathbf{F}^{s*}=\\left(\\mathbf{F}^{s}\\right)^{T}\\cdot\\mathbf{F}^{s}=\\mathbf{C}^{s}$\n\\end_inset\n\n and similarly \n\\begin_inset Formula $\\mathbf{C}^{\\sigma*}=\\mathbf{C}^{\\sigma}$\n\\end_inset\n\n,\n while also satisfying \n\\begin_inset Formula $\\mathbf{C}^{s*}=\\mathbf{C}^{s}=\\left(\\mathbf{F}^{\\sigma s}\\right)^{T}\\cdot\\mathbf{C}^{\\sigma}\\cdot\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n.\n Right stretch tensors \n\\begin_inset Formula $\\mathbf{U}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{U}^{\\sigma}$\n\\end_inset\n\n are also invariant.\n\\end_layout\n\n\\begin_layout Subsection\nSimple Solid Mixtures\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Simple-Solid-Mixtures\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the simplest type of non-reactive constrained mixtures of solids all constituents \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n share the same reference configuration \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}=\\mathbf{X}^{s}$\n\\end_inset\n\n,\n in which case \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}=\\mathbf{F}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $J^{\\sigma s}=1$\n\\end_inset\n\n.\n For this type of mixture where \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n does not evolve,\n it is convenient to set \n\\begin_inset Formula $w^{\\sigma}=1$\n\\end_inset\n\n for all \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n and scale the material properties of \n\\begin_inset Formula $\\Psi_{r}^{\\sigma}$\n\\end_inset\n\n to properly reflect the contribution of each constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n to the mixture.\n Thus,\n the relation of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sum_{\\sigma}\\boldsymbol{\\sigma}_{0}^{\\sigma}\\,.\\label{eq:simple-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nThis type of non-reactive mixture of solids is represented in FEBio using the material \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nsolid mixture\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen each constituent of a solid mixture is modeled using an uncoupled formulation to enforce nearly isochoric responses,\n the strain energy density of this type of mixture is given by\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=U\\left(J\\right)+\\sum\\limits_{\\sigma}\\tilde{\\Psi}_{r}^{\\sigma}\\left(\\mathbf{\\tilde{F}}^{s},\\rho_{r}^{\\sigma}\\right)\\,,\\label{eq140}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n is the volumetric energy component,\n \n\\begin_inset Formula $\\tilde{\\Psi}_{r}=\\sum\\nolimits_{\\sigma}\\tilde{\\Psi}_{r}^{\\sigma}$\n\\end_inset\n\n is the distortional energy component,\n and \n\\begin_inset Formula $\\mathbf{\\tilde{F}}^{s}$\n\\end_inset\n\n is the distortional part of the deformation gradient,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Nearly-Incompressible-Hyperelast\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This type of non-reactive mixture of uncoupled solids is represented in FEBio using the material \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nuncoupled solid mixture\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nMultigenerational Interstitial Growth\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Multigen-Interstitial-Growth\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMultigenerational interstitial growth mechanics may be modeled using a reactive mixture of constrained solids as described in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In this framework it is assumed that a porous solid matrix may gain mass via interstitial growth,\n such that the porosity of the solid decreases with increasing solid mass content.\n The history of growth is discretized temporally into generations \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n such that the mass added in the time interval \n\\begin_inset Formula $t^{\\sigma}\\le t<t^{\\sigma+1}$\n\\end_inset\n\n has a reference configuration \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}$\n\\end_inset\n\n.\n This model assumes that each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n gets deposited into the existing mixture in a stress-free state.\n This can be achieved by adopting the constitutive assumption that \n\\begin_inset Formula $\\mathbf{X}^{\\sigma}=\\boldsymbol{\\chi}^{s}\\left(\\mathbf{X}^{s},t^{\\sigma}\\right)$\n\\end_inset\n\n.\n In other words,\n the reference configuration of generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is the current configuration of the solid mixture at time \n\\begin_inset Formula $t^{\\sigma}$\n\\end_inset\n\n.\n An alternative form of this constitutive model,\n which satisfies frame-indifference,\n is that \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}\\left(\\mathbf{X}^{s},t^{\\sigma}\\right)=\\mathbf{R}^{s}\\left(\\mathbf{X}^{s},t^{\\sigma}\\right)$\n\\end_inset\n\n at the time the new generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is being deposited,\n where \n\\begin_inset Formula $\\mathbf{R}^{s}$\n\\end_inset\n\n is the rotation tensor in the polar decomposition of \n\\begin_inset Formula $\\mathbf{F}^{s}=\\mathbf{R}^{s}\\cdot\\mathbf{U}^{s}$\n\\end_inset\n\n.\n Equivalently,\n \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}\\left(\\mathbf{X}^{s}\\right)=\\mathbf{U}^{s}\\left(\\mathbf{X}^{s},t^{\\sigma}\\right)$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n is a right-stretch tensor,\n it satisfies frame indifference.\n\\end_layout\n\n\\begin_layout Standard\nThe underlying assumption of multigenerational growth is that \n\\begin_inset Formula $\\mathbf{U}^{s}\\left(\\mathbf{X}^{s},t^{\\sigma}\\right)$\n\\end_inset\n\n evolves for each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n either due to load-induced deformations or deformations produced by swelling processes,\n such as cell growth or Donnan swelling.\n\\end_layout\n\n\\begin_layout Standard\nThis type of multigenerational growth material is implemented in FEBio as \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nmultigeneration\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n for mixtures of elastic solids,\n and as \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nmultiphasic-multigeneration\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n for reactive multiphasic mixtures whose solid constituent is a multigenerational growth material.\n In this type of material the user needs to prescribe the generation birth times \n\\begin_inset Formula $t^{\\sigma}$\n\\end_inset\n\n and the properties of the material of each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n The code automatically prescribes \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n based on the constitutive model given above,\n for each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n For example,\n one may use a material model whose response depends on the evolving composition \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n of that generation.\n The composition \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n may evolve either due to chemical reactions modeled in a multiphasic framework,\n or by associating a user-defined load curve with \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n.\n Thus,\n the material properties (and the stress response) of generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n need not remain constant over the generation time interval \n\\begin_inset Formula $t^{\\sigma}\\le t<t^{\\sigma+1}$\n\\end_inset\n\n,\n even though \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n remains constant during that generation.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen the growth process is negative (\n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}<0$\n\\end_inset\n\n),\n it implies that the solid constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is losing mass (solid resorption);\n this loss of mass terminates when \n\\begin_inset Formula $\\rho_{r}^{\\sigma}=0$\n\\end_inset\n\n.\n A material model that depends on \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n may exhibit evolving material properties during this resorption process until that generation produces zero stress when \n\\begin_inset Formula $\\rho_{r}^{\\sigma}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nAs a result of these evolving growth processes,\n this multigeneration mixture may exhibit residual stresses.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Pre-Stretch\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Deposition-Stretch\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAn alternative approach to multigenerational growth is to prescribe \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n as a user-defined function.\n For isotropic stretching we may define\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}^{\\sigma s}=\\lambda^{\\sigma s}\\mathbf{I}\\,,\\label{eq:prescribed-defgrad-isotropic}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda^{\\sigma s}>0$\n\\end_inset\n\n is the stretch ratio.\n A value greater than unity causes swelling whereas a value less than unity causes contraction;\n \n\\begin_inset Formula $\\lambda^{\\sigma s}=1$\n\\end_inset\n\n produces \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}=\\mathbf{I}$\n\\end_inset\n\n,\n which recovers the simple solid mixture described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Simple-Solid-Mixtures\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In FEBio a load curve may be associated with \n\\begin_inset Formula $\\lambda^{\\sigma s}$\n\\end_inset\n\n to ramp up the prescribed deposition stretch.\n\\end_layout\n\n\\begin_layout Standard\nFor orthotropic stretching we define mutually orthogonal symmetry planes with unit normals \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2,3$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{a}_{i}\\cdot\\mathbf{a}_{j}=\\delta_{ij}$\n\\end_inset\n\n).\n Then,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}^{\\sigma s}=\\sum_{i=1}^{3}\\lambda_{i}^{\\sigma s}\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\,,\\label{eq:prescribed-defgrad-orthotropic}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{i}^{\\sigma s}$\n\\end_inset\n\n is the stretch ratio for the prescribed stretch along \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n;\n in general,\n \n\\begin_inset Formula $\\lambda_{1}^{\\sigma s}\\ne\\lambda_{2}^{\\sigma s}\\ne\\lambda_{3}^{\\sigma s}$\n\\end_inset\n\n,\n though this model can be specialized to transversely isotropic stretch by setting two of these stretch ratios equal to each other.\n\\end_layout\n\n\\begin_layout Standard\nAt lower symmetries the constitutive model for \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n must necessarily combine stretch and rotation.\n For monoclinic materials,\n deformations may be prescribed along three unit vectors \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n that satisfy \n\\begin_inset Formula $\\mathbf{a}_{1}\\cdot\\mathbf{a}_{2}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{a}_{1}\\cdot\\mathbf{a}_{3}=0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{a}_{2}\\cdot\\mathbf{a}_{3}\\ne0$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $\\mathbf{a}_{1}$\n\\end_inset\n\n defines the single plane of symmetry.\n In this case,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{F}^{\\sigma s} & =\\lambda_{1}^{\\sigma s}\\mathbf{a}_{1}\\otimes\\mathbf{a}_{1}+\\frac{\\lambda_{2}^{\\sigma s}}{1-\\alpha_{23}^{2}}\\mathbf{a}_{2}\\otimes\\left(\\mathbf{a}_{2}-\\alpha_{23}\\mathbf{a}_{3}\\right)\\\\\n & +\\frac{\\lambda_{3}^{\\sigma s}}{1-\\alpha_{23}^{2}}\\mathbf{a}_{3}\\otimes\\left(\\mathbf{a}_{3}-\\alpha_{23}\\mathbf{a}_{2}\\right)\n\\end{aligned}\n\\,,\\label{eq:prescribed-defgrad-monoclinic}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{i}^{\\sigma s}$\n\\end_inset\n\n is the stretch along \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{23}=\\mathbf{a}_{2}\\cdot\\mathbf{a}_{3}$\n\\end_inset\n\n.\n Under general conditions (i.e.,\n when \n\\begin_inset Formula $\\lambda_{1}^{\\sigma s}\\ne\\lambda_{2}^{\\sigma s}\\ne\\lambda_{3}^{\\sigma s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{23}\\ne0$\n\\end_inset\n\n),\n the polar decomposition theorem shows that this deformation gradient is not a pure stretch,\n as it also involves a rotation.\n\\end_layout\n\n\\begin_layout Standard\nThe deformation gradient for expansion of a triclinic material may be similarly constructed by finding \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n such that \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}\\cdot\\mathbf{a}_{i}=\\lambda_{i}^{\\sigma s}\\mathbf{a}_{i}$\n\\end_inset\n\n (no sum) for non-orthogonal and non-collinear unit vectors \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{F}^{\\sigma s} & =\\frac{\\lambda_{1}^{\\sigma s}}{d}\\mathbf{a}_{1}\\otimes\\left(\\left(1-\\alpha_{23}^{2}\\right)\\mathbf{a}_{1}-\\left(\\alpha_{12}-\\alpha_{13}\\alpha_{23}\\right)\\mathbf{a}_{2}-\\left(\\alpha_{13}-\\alpha_{12}\\alpha_{23}\\right)\\mathbf{a}_{3}\\right)\\\\\n & +\\frac{\\lambda_{2}^{\\sigma s}}{d}\\mathbf{a}_{2}\\otimes\\left(-\\left(\\alpha_{12}-\\alpha_{13}\\alpha_{23}\\right)\\mathbf{a}_{1}+\\left(1-\\alpha_{13}^{2}\\right)\\mathbf{a}_{2}-\\left(\\alpha_{23}-\\alpha_{12}\\alpha_{13}\\right)\\mathbf{a}_{3}\\right)\\\\\n & +\\frac{\\lambda_{3}^{\\sigma s}}{d}\\mathbf{a}_{3}\\otimes\\left(-\\left(\\alpha_{13}-\\alpha_{12}\\alpha_{23}\\right)\\mathbf{a}_{1}-\\left(\\alpha_{23}-\\alpha_{12}\\alpha_{13}\\right)\\mathbf{a}_{2}+\\left(1-\\alpha_{12}^{2}\\right)\\mathbf{a}_{3}\\right)\n\\end{aligned}\n\\label{eq:prescribed-defgrad-triclinic}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\nd=1-\\alpha_{12}^{2}-\\alpha_{13}^{2}-\\alpha_{23}^{2}+2\\alpha_{23}\\alpha_{13}\\alpha_{12}\\label{eq:triclinic-defgrad-denominator}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\alpha_{ij}=\\mathbf{a}_{i}\\cdot\\mathbf{a}_{j}$\n\\end_inset\n\n is the cosine of the angle between \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{a}_{j}$\n\\end_inset\n\n.\n All of these constitutive models for \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n satisy frame indifference since they depend on material vectors \n\\begin_inset Formula $\\mathbf{a}_{i}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThis type of elastic solid with prescribed pre-stretch is modeled in FEBio using the materials \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nprestretch elastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n and \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nprestretch uncoupled elastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n In the uncoupled version each solid constituent in the mixture has a deformation gradient \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}$\n\\end_inset\n\n which produces nearly isochoric responses (\n\\begin_inset Formula $J^{\\sigma}=1$\n\\end_inset\n\n),\n whereas no constraint is placed on the volumetric strain of the pre-stretch \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nEquilibrium Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Equilibrium-swelling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the interstitial fluid of a porous medium contains one or more solutes,\n an osmotic pressure may be produced in the fluid if the osmolarity of the interstitial fluid is non-uniform,\n or if it is different from that of the external bathing solution surrounding the porous medium.\n In general,\n since the osmolarity of the interstitial fluid may vary over time in transient problems,\n the analysis of such swelling effects may be addressed using,\n for example,\n the biphasic-solute material model described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Solute-Material\"\nnolink \"false\"\n\n\\end_inset\n\n.\n However,\n if we are only interested in the steady-state response for such types of materials,\n when solvent and solute fluxes have subsided,\n the analysis may be simplified considerably.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress tensor for a mixture of a porous solid and interstitial fluid is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}\\,,\\label{eq141}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n is he fluid pressure and \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the stress in the solid matrix resulting from solid strain.\n When steady-state conditions are achieved,\n the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n results exclusively from osmotic effects and ambient conditions (i.e.,\n it does not depend on the loading history).\n Thus,\n in analogy to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq118\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $p=\\tilde{p}+R\\theta\\Phi c$\n\\end_inset\n\n where \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is the mechanical pressure resulting from ambient conditions and \n\\begin_inset Formula $R\\theta\\Phi c$\n\\end_inset\n\n is the osmotic pressure resulting from the osmolarity \n\\begin_inset Formula $c$\n\\end_inset\n\n of the solution.\n\\end_layout\n\n\\begin_layout Standard\nThe osmotic pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n may produce swelling of the solid matrix,\n which is opposed by the solid matrix stress.\n This becomes more apparent when considering,\n for example,\n the case of a traction-free body.\n The traction is given by \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the unit outward normal to the boundary.\n When \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{0}$\n\\end_inset\n\n,\n the relation of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq141\"\nnolink \"false\"\n\n\\end_inset\n\n produces \n\\begin_inset Formula $p=\\mathbf{n}\\cdot\\boldsymbol{\\sigma}^{e}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n clearly showing that the osmotic pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is balanced by the swelling solid matrix.\n\\end_layout\n\n\\begin_layout Standard\nThe interstitial osmolarity (number of moles of solute per volume of interstitial fluid) may be related to the solute and solid content according to \n\\begin_inset Formula \n\\begin{equation}\nc=\\frac{c_{r}}{J-\\varphi_{r}^{s}}\\,,\\label{eq142}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n is the number of moles of solute per volume of the mixture in the reference configuration,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the volume fraction of the solid in the reference configuration,\n and \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the volume ratio of the porous solid matrix.\n Neither \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n nor \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n depend on the solid matrix deformation,\n thus eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq142\"\nnolink \"false\"\n\n\\end_inset\n\n provides the explicit dependence of \n\\begin_inset Formula $c$\n\\end_inset\n\n on \n\\begin_inset Formula $J$\n\\end_inset\n\n.\n This relation shows that the osmolarity of the interstitial fluid is dependent on the relative change in volume of the solid matrix with deformation.\n Effectively,\n under equilibrium swelling conditions,\n the term \n\\begin_inset Formula $-p\\mathbf{I}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq141\"\nnolink \"false\"\n\n\\end_inset\n\n represents an elastic stress and may be treated in this manner when analyzing equilibrium swelling conditions.\n\\end_layout\n\n\\begin_layout Standard\nSince \n\\begin_inset Formula $p$\n\\end_inset\n\n also depends on the osmotic coefficient,\n if we assume that \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n depends on the solid strain at most via a dependence on \n\\begin_inset Formula $J$\n\\end_inset\n\n,\n we may thus state generically that \n\\begin_inset Formula $p=p\\left(J\\right)$\n\\end_inset\n\n under equilibrium swelling.\n It follows that the elasticity tensor for \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=-\\left(p+J\\frac{dp}{dJ}\\right)\\mathbf{I}\\otimes\\mathbf{I}+2p\\mathbf{I}\\,\\overline{\\underline{\\otimes}}\\,\\mathbf{I}+\\boldsymbol{\\mathcal{C}}^{e}\\,,\\label{eq143}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{e}$\n\\end_inset\n\n is the elasticity tensor of \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPerfect Osmometer\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Perfect-osmometer\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a porous medium with an interstitial fluid that consists of a solvent and one or more solutes,\n whose boundary is permeable to the solvent but not to the solutes (e.g.,\n a biological cell).\n Since solutes are trapped within such a medium,\n \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n is a constant in this type of problem.\n Since the boundary is permeable to the solvent,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n must be continuous across the boundary.\n Assuming ideal physicochemical conditions,\n \n\\begin_inset Formula $\\Phi=1$\n\\end_inset\n\n,\n and zero ambient pressure,\n this continuity requirement implies that \n\\begin_inset Formula $p=R\\theta\\left(c-c^{\\ast}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $c^{\\ast}$\n\\end_inset\n\nis the osmolarity of the external environment.\n Using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq142\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula \n\\begin{equation}\np=R\\theta\\left(\\frac{c_{r}}{J-\\varphi_{r}^{s}}-c^{\\ast}\\right)\\,.\\label{eq144}\n\\end{equation}\n\n\\end_inset\n\nThe reference configuration (the stress-free configuration of the solid) is achieved when \n\\begin_inset Formula $J=1$\n\\end_inset\n\n and \n\\begin_inset Formula $p=0$\n\\end_inset\n\n,\n from which it follows that \n\\begin_inset Formula $c_{r}=\\left(1-\\varphi_{r}^{s}\\right)c_{0}^{\\ast}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $c_{0}^{\\ast}$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $c^{\\ast}$\n\\end_inset\n\n in the reference state.\n Therefore eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq144\"\nnolink \"false\"\n\n\\end_inset\n\n may also be written as \n\\begin_inset Formula \n\\begin{equation}\np=R\\theta c^{\\ast}\\left(\\frac{1-\\varphi_{r}^{s}}{J-\\varphi_{r}^{s}}\\frac{c_{0}^{\\ast}}{c^{\\ast}}-1\\right)\\,,\\label{eq145}\n\\end{equation}\n\n\\end_inset\n\nand this expression may be substituted into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq143\"\nnolink \"false\"\n\n\\end_inset\n\n to evaluate the corresponding elasticity tensor.\n\\end_layout\n\n\\begin_layout Standard\nA perfect osmometer is a porous material whose interstitial fluid behaves ideally and whose solid matrix exhibits negligible resistance to swelling (\n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}\\approx\\mathbf{0})$\n\\end_inset\n\n.\n In that case \n\\begin_inset Formula $p=0$\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq145\"\nnolink \"false\"\n\n\\end_inset\n\n may be rearranged to yield \n\\begin_inset Formula \n\\begin{equation}\nJ=\\left(1-\\varphi_{r}^{s}\\right)\\frac{c_{0}^{\\ast}}{c^{\\ast}}+\\varphi_{r}^{s}.\\label{eq146}\n\\end{equation}\n\n\\end_inset\n\nThis equation is known as the Boyle-van't Hoff relation for a perfect osmometer.\n It predicts that variations in the relative volume of such as medium with changes in external osmolarity \n\\begin_inset Formula $c^{\\ast}$\n\\end_inset\n\n is an affine function of \n\\begin_inset Formula $c_{0}^{\\ast}/c^{\\ast}$\n\\end_inset\n\n,\n with the intercept at the origin representing the solid volume fraction and the slope representing the fluid volume fraction,\n in the reference configuration.\n\\end_layout\n\n\\begin_layout Standard\nFEBio implements the relation of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq145\"\nnolink \"false\"\n\n\\end_inset\n\n for the purpose of modeling equilibrium swelling even when solid matrix stresses are not negligible.\n The name \n\\begin_inset Quotes eld\n\\end_inset\n\nperfect osmometer\n\\begin_inset Quotes erd\n\\end_inset\n\n is adopted for this model because it reproduces the Boyle-van't Hoff response in the special case when \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}=\\mathbf{0}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nCell Growth\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cell-Growth\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe growth of cells requires the active uptake of soluble mass to provide the building blocks for various intracellular structures,\n such as the cytoskeleton or chromosomes,\n and growth contributes to the osmolarity of the intracellular space.\n The resulting mechano-chemical gradient drives solvent into the cell as well,\n contributing to its volumetric growth.\n\\end_layout\n\n\\begin_layout Standard\nCell growth may be modeled using the \n\\begin_inset Quotes eld\n\\end_inset\n\nperfect osmometer\n\\begin_inset Quotes erd\n\\end_inset\n\n framework by simply increasing the mass of the intracellular solid matrix and membrane-impermeant solute.\n This is achieved by using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq144\"\nnolink \"false\"\n\n\\end_inset\n\n to model the osmotic pressure and allowing the parameters \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n (normally constant) to increase over time as a result of growth.\n Since cell growth is often accompanied by cell division,\n and since daughter cells typically achieve the same solid and solute content as their parent,\n it may be convenient to assume that \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n increase proportionally,\n though this is not an obligatory relationship.\n To ensure that the initial configuration is a stress-free reference configuration,\n let \n\\begin_inset Formula $c_{r}=\\left(1-\\varphi_{r}^{s}\\right)c^{\\ast}$\n\\end_inset\n\n in the initial state prior to growth.\n\\end_layout\n\n\\begin_layout Subsection\nDonnan Equilibrium Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Donnan-Equilibrium-Swelling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a porous medium whose solid matrix holds a fixed electrical charge and whose interstitial fluid consists of a solvent and two monovalent counter-ions (such as Na\n\\begin_inset Formula $^{\\mathrm{+}}$\n\\end_inset\n\n and Cl\n\\begin_inset Formula $^{\\mathrm{-}})$\n\\end_inset\n\n.\n The boundaries of the medium are permeable to the solvent and ions.\n The fixed charge density is denoted by \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n;\n it is a measure of the number of fixed charges per volume of the interstitial fluid in the current configuration.\n This charge density may be either negative or positive,\n thereby producing an imbalance in the concentration of anions and cations in the interstitial fluid.\n To determine the osmolarity of the interstitial fluid,\n it is necessary to equate the mechano-chemical potential of the solvent and the mechano-electrochemical potential of the ions between the porous medium and its surrounding bath.\n When assuming ideal physicochemical behavior,\n the interstitial osmolarity (resulting from the interstitial ions) is given by \n\\begin_inset Formula \n\\begin{equation}\nc=\\sqrt{\\left(c^{F}\\right)^{2}+\\left(2c^{\\ast}\\right)^{2}}\\,,\\label{eq147}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $c^{\\ast}$\n\\end_inset\n\n is the salt concentration in the bath.\n Alternatively,\n we note that the osmolarity of the bath is \n\\begin_inset Formula $\\bar{c}^{\\ast}=2c^{\\ast}$\n\\end_inset\n\n.\n Though this expression may be equated with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq142\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the resulting value of \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n is not constant in this case,\n since ions may transport into or out of the pore space;\n therefore that relation is not useful here.\n\\end_layout\n\n\\begin_layout Standard\nHowever,\n since the number of charges fixed to the solid matrix is invariant,\n we may manipulate eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq142\"\nnolink \"false\"\n\n\\end_inset\n\n to produce a relation between the fixed charge density in the current configuration,\n \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n,\n and the corresponding value in the reference configuration,\n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\nc^{F}=\\frac{1-\\varphi_{r}^{s}}{J-\\varphi_{r}^{s}}c_{r}^{F}\\,.\\label{eq148}\n\\end{equation}\n\n\\end_inset\n\nNow the osmotic pressure resulting from the difference in osmolarity between the porous medium and its surrounding bath is given by \n\\begin_inset Formula \n\\begin{equation}\np=R\\theta\\left(\\sqrt{\\left(\\frac{1-\\varphi_{r}^{s}}{J-\\varphi_{r}^{s}}c_{r}^{F}\\right)^{2}+\\left(\\bar{c}^{\\ast}\\right)^{2}}-\\bar{c}^{\\ast}\\right)\\,.\\label{eq149}\n\\end{equation}\n\n\\end_inset\n\nThis expression may be substituted into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq143\"\nnolink \"false\"\n\n\\end_inset\n\n to evaluate the corresponding elasticity tensor.\n\\end_layout\n\n\\begin_layout Standard\nWhen the osmotic pressure results from an imbalance in osmolarity produced by a fixed charge density,\n it is called a Donnan osmotic pressure.\n The analysis associated with this relation is called Donnan equilibrium.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nChemical Reactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Chemical-Reactions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nChemical reactions may be incorportated into a multiphasic mixture by adding a mass supply term to the equation of mass balance,\n \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\rho^{\\alpha}}{\\partial t}+\\divg\\left(\\rho^{\\alpha}\\mathbf{v}^{\\alpha}\\right)=\\hat{\\rho}^{\\alpha}\\,,\\label{eq150}\n\\end{equation}\n\n\\end_inset\n\nWhere \n\\begin_inset Formula $\\hat{\\rho}^{\\alpha}$\n\\end_inset\n\n is the volume density of mass supply to \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n resulting from chemical reactions with all other mixture constitutents.\n Since mass must be conserved over all constituents,\n mass supply terms are constrained by \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\hat{\\rho}^{\\alpha}=0\\,.\\label{eq151}\n\\end{equation}\n\n\\end_inset\n\nIn a mixture containing a solid constituent (denoted by \n\\begin_inset Formula $\\alpha=s$\n\\end_inset\n\n ),\n it is conveniemt to define the mixture domain (and thus the finite element mesh) on the solid and evaluate mass fluxes of constituents relative to the solid,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}^{\\alpha}=\\rho^{\\alpha}\\left(\\mathbf{v}^{\\alpha}-\\mathbf{v}^{s}\\right)\\,.\\label{eq152}\n\\end{equation}\n\n\\end_inset\n\nSubstituting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq152\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq150\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the differential form of the mass balance may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\frac{D^{s}\\rho_{r}^{\\alpha}}{Dt}+J\\divg\\mathbf{m}^{\\alpha}=\\hat{\\rho}_{r}^{\\alpha}\\,,\\label{eq153}\n\\end{equation}\n\n\\end_inset\n\nWhere \n\\begin_inset Formula $D^{s}\\left(\\cdot\\right)/Dt$\n\\end_inset\n\n represents the material time derivative in the spatial frame,\n following the solid,\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient of the solid matrix;\n \n\\begin_inset Formula $\\rho_{r}^{\\alpha}$\n\\end_inset\n\n is the apparent density and \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\alpha}$\n\\end_inset\n\n is the volume density of mass supply to \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n normalized to the mixture volume in the reference configuration,\n \n\\begin_inset Formula \n\\begin{equation}\n\\rho_{r}^{\\alpha}=J\\rho^{\\alpha},\\quad\\hat{\\rho}_{r}^{\\alpha}=J\\hat{\\rho}^{\\alpha}.\\label{eq154}\n\\end{equation}\n\n\\end_inset\n\nSince \n\\begin_inset Formula $\\rho_{r}^{\\alpha}$\n\\end_inset\n\n is the mass of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the current configuration per volume of the mixture in the reference configuration (an invariant quantity),\n this parameter represents a direct measure of the mass content of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the mixture,\n which may thus be used as a state variable in a framework that accounts for chemical reactions.\n A distinction is now made between solid and solute species in the mixture,\n since they are often treated differential in an analysis.\n\\end_layout\n\n\\begin_layout Subsection\nSolid Matrix and Solid-Bound Molecular Constituents\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solid-Matrix-SBM\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor constituents constrained to move with the solid (denoted generically by \n\\begin_inset Formula $\\alpha=\\sigma$\n\\end_inset\n\n and satisfying \n\\begin_inset Formula $\\mathbf{v}^{s}=\\mathbf{v}^{\\sigma}$\n\\end_inset\n\n ,\n \n\\begin_inset Formula $\\forall\\sigma)$\n\\end_inset\n\n,\n the statement of mass balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq153\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to the special form \n\\begin_inset Formula \n\\begin{equation}\nD^{s}\\rho_{r}^{\\sigma}/Dt=\\hat{\\rho}_{r}^{\\sigma}\\,.\\label{eq155}\n\\end{equation}\n\n\\end_inset\n\nThis representation makes it easy to see that alterations in \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n can occur only as a result of chemical reactions (such as synthesis,\n degradation,\n or binding).\n In contrast,\n as seen in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq153\"\nnolink \"false\"\n\n\\end_inset\n\n,\n alterations in \n\\begin_inset Formula $\\rho_{r}^{\\alpha}$\n\\end_inset\n\n for solutes or solvent (\n\\begin_inset Formula $\\alpha\\ne\\sigma)$\n\\end_inset\n\n may also occur as a result of mass transport into or out of the pore space of the solid matrix.\n Therefore,\n \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n is the natural choice of state variable for describing the content of solid constituents in a reactive mixture.\n\\end_layout\n\n\\begin_layout Standard\nWhen multiple solid species are present,\n the net solid mass content may be given by \n\\begin_inset Formula $\\rho_{r}^{s}=\\sum\\limits_{\\sigma}\\rho_{r}^{\\sigma}$\n\\end_inset\n\n whereas the net mass supply of solid is \n\\begin_inset Formula $\\hat{\\rho}_{r}^{s}=\\sum\\limits_{\\sigma}\\hat{\\rho}_{r}^{\\sigma}$\n\\end_inset\n\n such that \n\\begin_inset Formula $D^{s}\\rho_{r}^{s}/Dt=\\hat{\\rho}_{r}^{s}$\n\\end_inset\n\n.\n The referential solid volume fraction,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n,\n may be evaluated from \n\\begin_inset Formula \n\\begin{equation}\n\\varphi_{r}^{s}=\\varphi_{0}^{s}+\\sum\\limits_{\\sigma}\\rho_{r}^{\\sigma}/\\rho_{T}^{\\sigma},\\label{eq:referential-solid-volume-fraction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{T}^{\\sigma}$\n\\end_inset\n\n is the true density of solid constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n (mass of \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n per volume of \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n) and \n\\begin_inset Formula $\\varphi_{0}^{s}$\n\\end_inset\n\n is the referential solid volume fraction of solid constituents not explicitly modeled by solid-bound molecules (a user-defined parameter).\n According to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq154\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that the solid volume fraction in the current configuration is given by \n\\begin_inset Formula $\\varphi^{s}=\\varphi_{r}^{s}/J$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $0\\leqslant\\varphi^{s}\\leqslant1$\n\\end_inset\n\n under all circumstances,\n while \n\\begin_inset Formula $0\\leqslant\\varphi_{r}^{s}\\leqslant J$\n\\end_inset\n\n,\n implying that \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n may exceed unity when solid growth occurs.\n In this study,\n it is assumed that all mixture constituents are intrinsically incompressible,\n implying that their true density is invariant.\n\\end_layout\n\n\\begin_layout Standard\nAt the start of an analysis,\n the formula of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:referential-solid-volume-fraction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\varphi_{r}^{s}\\left(0\\right)=\\varphi_{0}^{s}+\\sum\\limits_{\\sigma}\\rho_{r}^{\\sigma}\\left(0\\right)/\\rho_{T}^{\\sigma}\\label{eq:initial-referential-solid-fraction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}^{\\sigma}\\left(0\\right)$\n\\end_inset\n\n represents the initial apparent mass densities of solid constituents.\n This initial value of \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n must satisfy \n\\begin_inset Formula $0\\le\\varphi_{r}^{s}\\left(0\\right)\\le1$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $J=1$\n\\end_inset\n\n at the start of an analysis.\n\\end_layout\n\n\\begin_layout Standard\nThe various constituents of the porous solid matrix of a multiphasic mixture may be electrically charged.\n The charge density in the current configuration,\n normalized by the mixture volume in the current configuration,\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\check{c}^{F}=\\frac{z^{F}dn^{F}+\\sum_{\\sigma}z^{\\sigma}dn^{\\sigma}}{dV}\\,,\\label{eq:FCD-mixture-volume}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $z^{F}$\n\\end_inset\n\n is the charge number (equivalent charge per mole) and \n\\begin_inset Formula $dn^{F}$\n\\end_inset\n\n is the elemental number of moles associated with fixed charges present in the initial volume fraction \n\\begin_inset Formula $\\varphi_{0}^{s}$\n\\end_inset\n\n of the solid matrix.\n Similarly,\n \n\\begin_inset Formula $z^{\\sigma}$\n\\end_inset\n\n is the charge number of evolving solid constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n By convention however,\n the fixed charge density \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n of a multiphasic material is normalized by the fluid volume of the mixture,\n thus it can be shown that\n\\begin_inset Formula \n\\begin{equation}\nc^{F}=\\frac{\\check{c}^{F}}{1-\\varphi^{s}}=\\frac{1}{J-\\varphi_{r}^{s}}\\left(\\frac{z^{F}dn^{F}}{dV_{r}}+\\sum_{\\sigma}\\frac{z^{\\sigma}\\rho_{r}^{\\sigma}}{M^{\\sigma}}\\right)\\,.\\label{eq:FCD-fluid-volume}\n\\end{equation}\n\n\\end_inset\n\nIn particular,\n in the reference configuration this formula produces\n\\begin_inset Formula \n\\begin{equation}\nc^{F}\\left(0\\right)=\\frac{1}{1-\\varphi_{r}^{s}\\left(0\\right)}\\left(\\frac{z^{F}dn^{F}\\left(0\\right)}{dV_{r}}+\\sum_{\\sigma}\\frac{z^{\\sigma}\\rho_{r}^{\\sigma}\\left(0\\right)}{M^{\\sigma}}\\right)\\,.\\label{eq:FCD-initial-time}\n\\end{equation}\n\n\\end_inset\n\nWe define the first term on the right-hand-side of this equation as the initial value of the user-specified fixed charge density \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n (which is not associated with solid species \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n),\n\\begin_inset Formula \n\\begin{equation}\nc_{0}^{F}\\equiv\\frac{1}{1-\\varphi_{r}^{s}\\left(0\\right)}\\frac{z^{F}dn^{F}\\left(0\\right)}{dV_{r}}\\,.\\label{eq:user-specified-cF0}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n in the current configuration,\n we may rewrite\n\\begin_inset Formula \n\\begin{equation}\nc^{F}=\\frac{1-\\varphi_{r}^{s}\\left(0\\right)}{J-\\varphi_{r}^{s}}c_{0}^{F}+\\frac{1}{J-\\varphi_{r}^{s}}\\sum_{\\sigma}\\frac{z^{\\sigma}\\rho_{r}^{\\sigma}}{M^{\\sigma}}\\,.\\label{eq158}\n\\end{equation}\n\n\\end_inset\n\nwhere the user-specified \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n may optionally be associated with a load curve representing the scale factor \n\\begin_inset Formula $dn^{F}/dn^{F}\\left(0\\right)$\n\\end_inset\n\n,\n in case the user would like to allow \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n to evolve.\n By analogy,\n we may now define the referential fixed-charge density \n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n of the mixture as\n\\begin_inset Formula \n\\begin{equation}\nc_{r}^{F}\\equiv\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}\\left(0\\right)}c^{F}=c_{0}^{F}+\\frac{1}{1-\\varphi_{r}^{s}\\left(0\\right)}\\sum_{\\sigma}\\frac{z^{\\sigma}\\rho_{r}^{\\sigma}}{M^{\\sigma}}\\label{eq157}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n may evolve with time,\n but it represents the number of fixed equivalent charges in the current configuration,\n per fluid volume in the reference configuration.\n Hence we may also write\n\\begin_inset Formula \n\\[\nc^{F}=\\frac{1-\\varphi_{r}^{s}\\left(0\\right)}{J-\\varphi_{r}^{s}}c_{r}^{F}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe molar concentration of a solid-bound molecular constituent,\n which may be needed in a reactive process involving solutes,\n is given by\n\\begin_inset Formula \n\\begin{equation}\nc^{\\sigma}=\\frac{1}{J-\\varphi_{r}^{s}}\\frac{\\rho_{r}^{\\sigma}}{M^{\\sigma}}\\,.\\label{eq:sbm-molar-concentration}\n\\end{equation}\n\n\\end_inset\n\nAn alternative to using solid-bound molecules is to define solutes \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n whose diffusivity \n\\begin_inset Formula $\\mathbf{d}^{\\sigma}$\n\\end_inset\n\n in the mixture is set to \n\\begin_inset Formula $\\mathbf{0}$\n\\end_inset\n\n (however,\n the free diffusivity \n\\begin_inset Formula $d_{0}^{\\sigma}$\n\\end_inset\n\n should not be set to zero,\n to prevent a division by zero;\n its exact value is not important,\n thus let \n\\begin_inset Formula $d_{0}^{\\sigma}=1$\n\\end_inset\n\n).\n Then,\n FEBio treats these solutes as equivalent to solid-bound molecules:\n (1) Their concentration does not contribute to the osmolarity of the interstitial fluid;\n (2) their concentration contributes to the fixed charge density \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n in the current configuration;\n (3) these solutes do not contribute to the effective hydraulic permeability \n\\begin_inset Formula $\\tilde{\\mathbf{k}}$\n\\end_inset\n\n of the porous multiphasic mixture;\n (4) these solutes can be involved in chemical reactions;\n (5) while their initial effective concentration may be prescribed,\n it must not contribute to the prescribed initial effective fluid pressure,\n and the user should not prescribe any boundary conditions on the effective concentration of these solutes.\n Using these 'solid-bound' solutes increases the number of degrees of freedom in an FEBio analysis;\n however,\n unlike solid-bound molecules,\n the solution for the effective concentration of these solutes remains as accurate as all other degrees of freedom in an analysis.\n\\end_layout\n\n\\begin_layout Standard\nTo evaluate the contribution of these solutes to the referential solid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n and to chemical reactions,\n we use eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:sbm-molar-concentration\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n above and substitute it into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:referential-solid-volume-fraction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to produce\n\\begin_inset Formula \n\\[\n\\varphi_{r}^{s}=\\varphi_{0}^{s}+\\left(J-\\varphi_{r}^{s}\\right)\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}\n\\]\n\n\\end_inset\n\nwhich can be solved for \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n using the concentrations \n\\begin_inset Formula $c^{\\sigma}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\varphi_{r}^{s}=\\frac{\\varphi_{0}^{s}+J\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}}{1+\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}}\\,.\\label{eq:phirs-solutes-as-SBMs}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSolutes\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solutes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSolutes are denoted generically by \n\\begin_inset Formula $\\alpha=\\iota$\n\\end_inset\n\n.\n In chemistry solute content is often represented in units of molar concentration (moles per fluid volume).\n It follows that solute molar concentration \n\\begin_inset Formula $c^{\\iota}$\n\\end_inset\n\n and molar supply \n\\begin_inset Formula $\\hat{c}^{\\iota}$\n\\end_inset\n\n are related to \n\\begin_inset Formula $\\rho^{\\iota}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\hat{\\rho}^{\\iota}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\begin{equation}\nc^{\\iota}=\\frac{\\rho^{\\iota}}{\\left(1-\\varphi^{s}\\right)M^{\\iota}},\\quad\\hat{c}^{\\iota}=\\frac{\\hat{\\rho}^{\\iota}}{\\left(1-\\varphi^{s}\\right)M^{\\iota}}\\,.\\label{eq159}\n\\end{equation}\n\n\\end_inset\n\nThe molar flux of constituent \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n relative to the solid is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{j}^{\\iota}=\\left(1-\\varphi^{s}\\right)c^{\\iota}\\left(\\mathbf{v}^{\\iota}-\\mathbf{v}^{s}\\right)\\,,\\label{eq160}\n\\end{equation}\n\n\\end_inset\n\nwhere it may be noted that \n\\begin_inset Formula $\\mathbf{m}^{\\iota}=M^{\\iota}\\mathbf{j}^{\\iota}$\n\\end_inset\n\n.\n Combining these relations with Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq153\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq154\"\nnolink \"false\"\n\n\\end_inset\n\n produces the desired form of the mass balance for the solutes,\n \n\\begin_inset Formula \n\\begin{equation}\n\\frac{1}{J}\\frac{D^{s}\\left[J\\left(1-\\varphi^{s}\\right)c^{\\iota}\\right]}{Dt}+\\mbox{div}\\mathbf{j}^{\\iota}=\\left(1-\\varphi^{s}\\right)\\hat{c}^{\\iota}\\,.\\label{eq161}\n\\end{equation}\n\n\\end_inset\n\nThis form is suitable for implementation in a finite element analysis where the mesh is defined on the solid matrix.\n\\end_layout\n\n\\begin_layout Subsection\nMixture with Negligible Solute Volume Fraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Negligible-Solute-Fraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe volume fraction of each constituent is given by \n\\begin_inset Formula $\\varphi^{\\alpha}=\\rho^{\\alpha}/\\rho_{T}^{\\alpha}$\n\\end_inset\n\n.\n In a saturated mixture these volume fractions satisfy \n\\begin_inset Formula $\\sum\\limits_{\\alpha}\\varphi^{\\alpha}=1$\n\\end_inset\n\n.\n Substituting \n\\begin_inset Formula $\\rho^{\\alpha}=\\varphi^{\\alpha}\\rho_{T}^{\\alpha}$\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq150\"\nnolink \"false\"\n\n\\end_inset\n\n,\n dividing across by \n\\begin_inset Formula $\\rho_{T}^{\\alpha}$\n\\end_inset\n\n (invariant for intrinsically incompressible constituents),\n and taking the sum of the resulting expression over all constituents produces \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\sum\\limits_{\\alpha}\\varphi^{\\alpha}\\mathbf{v}^{\\alpha}\\right)=\\sum\\limits_{\\alpha}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\\,.\\label{eq162}\n\\end{equation}\n\n\\end_inset\n\nThis mass balance relation for the mixture expresses the fact that the mixture volume will change as a result of chemical reactions where the true density of products is different from that of reactants.\n Indeed,\n assuming that \n\\begin_inset Formula $\\rho_{T}^{\\alpha}$\n\\end_inset\n\n is the same for all \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n would nullify the right-hand-side of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq162\"\nnolink \"false\"\n\n\\end_inset\n\n based on eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq151\"\nnolink \"false\"\n\n\\end_inset\n\n.\n We now adopt the assumption that solutes occupy a negligible volume fraction of the mixture (\n\\begin_inset Formula $\\varphi^{\\iota}\\ll1)$\n\\end_inset\n\n,\n from which it follows that \n\\begin_inset Formula $\\varphi^{s}+\\varphi^{w}\\approx1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\sum\\limits_{\\alpha}\\varphi^{\\alpha}\\mathbf{v}^{\\alpha}\\approx\\mathbf{v}^{s}+\\mathbf{w}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{w}=\\varphi^{w}\\left(\\mathbf{v}^{w}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n is the volumetrix flux of solvent relative to the solid.\n Thus,\n the mixture mass balance may be reduced to \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\sum\\limits_{\\alpha}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\\,.\\label{eq163}\n\\end{equation}\n\n\\end_inset\n\nIn the special case of the solvent (\n\\begin_inset Formula $\\alpha=w$\n\\end_inset\n\n),\n FEBio uses a solvent supply,\n \n\\begin_inset Formula $\\hat{\\varphi}^{w}=\\hat{\\rho}^{w}/\\hat{\\rho}_{T}^{w}$\n\\end_inset\n\n,\n which may be incorporated in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq163\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\hat{\\varphi}^{w}+\\sum\\limits_{\\alpha\\ne w}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\\,.\\label{eq163b}\n\\end{equation}\n\n\\end_inset\n\nFrom the mass balance equation for the solvent we have\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\hat{\\varphi}^{w} & =\\frac{D^{s}\\varphi^{w}}{Dt}+\\grad\\varphi^{w}\\cdot\\left(\\mathbf{v}^{w}-\\mathbf{v}^{s}\\right)+\\varphi^{w}\\divg\\mathbf{v}^{w}\\end{aligned}\n\\]\n\n\\end_inset\n\nso that the mixture mass balance takes the form\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right) & =\\frac{D^{s}\\varphi^{w}}{Dt}+\\grad\\varphi^{w}\\cdot\\left(\\mathbf{v}^{w}-\\mathbf{v}^{s}\\right)+\\varphi^{w}\\divg\\mathbf{v}^{w}+\\sum\\limits_{\\alpha\\ne w}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\\,\\end{aligned}\n\\]\n\n\\end_inset\n\nUsing \n\\begin_inset Formula $\\varphi^{w}+\\varphi^{s}=1$\n\\end_inset\n\n this relation further simplifies to\n\\begin_inset Formula \n\\[\n\\varphi^{s}\\divg\\mathbf{v}^{s}+\\frac{D^{s}\\varphi^{s}}{Dt}=\\sum\\limits_{\\alpha\\ne w}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\\,.\n\\]\n\n\\end_inset\n\nNow we use the kinematic relation \n\\begin_inset Formula $\\divg\\mathbf{v}^{s}=\\frac{1}{J}\\frac{D^{s}J}{Dt}$\n\\end_inset\n\n to reduce this equation further to\n\\begin_inset Formula \n\\[\n\\frac{D^{s}\\left(J\\varphi^{s}\\right)}{Dt}=J\\sum\\limits_{\\alpha\\ne w}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\\,.\n\\]\n\n\\end_inset\n\nBut recall that\n\\begin_inset Formula \n\\[\nJ\\varphi^{s}=\\varphi_{r}^{s}\n\\]\n\n\\end_inset\n\nthus the mixture mass balance now simplifies to\n\\begin_inset Formula \n\\[\n\\frac{1}{J}\\frac{D^{s}\\varphi_{r}^{s}}{Dt}=\\sum\\limits_{\\alpha\\ne w}\\frac{\\hat{\\rho}^{\\alpha}}{\\rho_{T}^{\\alpha}}\n\\]\n\n\\end_inset\n\nSince\n\\begin_inset Formula \n\\[\n\\hat{\\rho}^{\\alpha}=\\left(1-\\varphi^{s}\\right)M^{\\alpha}\\hat{c}^{\\alpha}\n\\]\n\n\\end_inset\n\nit follows that\n\\begin_inset Formula \n\\[\n\\frac{D^{s}\\varphi_{r}^{s}}{Dt}=J\\left(1-\\varphi^{s}\\right)\\sum\\limits_{\\alpha\\ne w}\\frac{M^{\\alpha}\\hat{c}^{\\alpha}}{\\rho_{T}^{\\alpha}}\n\\]\n\n\\end_inset\n\nAnother way of presenting these results is to restate the mixture mass balance as\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\hat{\\varphi}_{0}^{w}+\\frac{1}{J}\\frac{D^{s}\\varphi_{r}^{s}}{Dt}\\label{eq:mixture-mass-reactive}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\varphi}_{0}^{w}$\n\\end_inset\n\n is the solvent supply from user-specified sources (not from the chemical reactions).\n\\end_layout\n\n\\begin_layout Subsection\nChemical Kinetics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Chemical-Kinetics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nProductions rates are described by constitutive relations which are functions of the state variables.\n In a biological mixture under isothermal conditions,\n the minimum set of state variables needed to describe reactive mixtures that include a solid matrix are:\n the (uniform) temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n,\n the solid matrix deformation gradient \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n (or related strain measures),\n and the molar content \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n of the various constituents.\n This set differs from the classical treatment of chemical kinetics in fluid mixtures by the inclusion of \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n and the subset of constituents bound to the solid matrix.\n To maintain a consistent notation in this section,\n solid-bound molecular species are described by their molar concentrations and molar supplies which may be related to their referential mass density and referential mass supply according to \n\\begin_inset Formula \n\\begin{equation}\nc^{\\sigma}=\\frac{\\rho_{r}^{\\sigma}}{\\left(J-\\varphi_{r}^{s}\\right)M^{\\sigma}},\\quad\\hat{c}^{\\sigma}=\\frac{\\hat{\\rho}_{r}^{\\sigma}}{\\left(J-\\varphi_{r}^{s}\\right)M^{\\sigma}}\\,.\\label{eq164}\n\\end{equation}\n\n\\end_inset\n\nConsider a general chemical reaction,\n \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\nu_{R}^{\\alpha}\\mathcal{E}^{\\alpha}\\to\\sum\\limits_{\\alpha}\\nu_{P}^{\\alpha}\\mathcal{E}^{\\alpha}\\,,\\label{eq165}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathcal{E}^{\\alpha}$\n\\end_inset\n\n is the chemical species representing constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n;\n \n\\begin_inset Formula $\\nu_{R}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\nu_{P}^{\\alpha}$\n\\end_inset\n\n represent stoichiometric coefficients of the reactants and products,\n respectively.\n Since the molar supply of reactants and products is constrained by stoichiometry,\n it follows that all molar supplies \n\\begin_inset Formula $\\hat{c}^{\\alpha}$\n\\end_inset\n\n in a specific chemical reaction may be related to a production rate \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n according to \n\\begin_inset Formula \n\\begin{equation}\n\\hat{c}^{\\alpha}=\\nu^{\\alpha}\\hat{\\zeta}\\,,\\label{eq166}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\nu^{\\alpha}$\n\\end_inset\n\n represents the net stoichiometric coefficient for \n\\begin_inset Formula $\\mathcal{E}^{\\alpha}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\nu^{\\alpha}=\\nu_{P}^{\\alpha}-\\nu_{R}^{\\alpha}\\,.\\label{eq167}\n\\end{equation}\n\n\\end_inset\n\nThus,\n formulating constitutive relations for \n\\begin_inset Formula $\\hat{c}^{\\alpha}$\n\\end_inset\n\n is equivalent to providing a single relation for \n\\begin_inset Formula $\\hat{\\zeta}\\left(\\theta,\\mathbf{F},c^{\\alpha}\\right)$\n\\end_inset\n\n.\n When the chemical reaction is reversible,\n \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\nu_{R}^{\\alpha}\\mathcal{E}^{\\alpha}\\rightleftharpoons\\sum\\limits_{\\alpha}\\nu_{P}^{\\alpha}\\mathcal{E}^{\\alpha}\\,,\\label{eq168}\n\\end{equation}\n\n\\end_inset\n\nthe relations of Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq166\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq167\"\nnolink \"false\"\n\n\\end_inset\n\n still apply but the form of \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n would be different.\n\\end_layout\n\n\\begin_layout Standard\nUsing the relations of Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq159\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq164\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq166\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows in general that \n\\begin_inset Formula $\\hat{\\rho}^{\\alpha}=\\left(1-\\varphi^{s}\\right)M^{\\alpha}\\nu^{\\alpha}\\hat{\\zeta}$\n\\end_inset\n\n,\n so that the constraint of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq151\"\nnolink \"false\"\n\n\\end_inset\n\n is equivalent to enforcing stoichiometry,\n namely,\n \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\nu^{\\alpha}M^{\\alpha}=0\\,.\\label{eq169}\n\\end{equation}\n\n\\end_inset\n\nThus,\n properly balancing a chemical reaction satisfies this constraint.\n\\end_layout\n\n\\begin_layout Standard\nThe mixture mass balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq163b\"\nnolink \"false\"\n\n\\end_inset\n\n may now be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}^{w}\\right)=\\hat{\\varphi}_{0}^{w}+\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\overline{\\mathcal{V}}\\,,\\label{eq170}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\overline{\\mathcal{V}}=\\sum\\limits_{\\alpha\\ne w}\\nu^{\\alpha}\\mathcal{V}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathcal{V}^{\\alpha}=M^{\\alpha}/\\rho_{T}^{\\alpha}$\n\\end_inset\n\n is the molar volume of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n (Currently in FEBio,\n \n\\begin_inset Formula $\\hat{\\varphi}_{0}^{w}$\n\\end_inset\n\n is specified independently of \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n,\n because users may choose to neglect the contribution from \n\\begin_inset Formula $\\overline{\\mathcal{V}}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq170\"\nnolink \"false\"\n\n\\end_inset\n\n;\n therefore,\n if one desires to model chemical reactions,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq165\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq168\"\nnolink \"false\"\n\n\\end_inset\n\n,\n that involve the solvent,\n it is necessary to explicitly provide a solvent supply function compatible with the above relations,\n namely \n\\begin_inset Formula $\\hat{\\varphi}_{0}^{w}=\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\nu^{w}\\mathcal{V}^{w}$\n\\end_inset\n\n.) Similarly,\n the solute mass balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq161\"\nnolink \"false\"\n\n\\end_inset\n\n becomes \n\\begin_inset Formula \n\\begin{equation}\n\\frac{1}{J}\\frac{D^{s}\\left[J\\left(1-\\varphi^{s}\\right)c^{\\iota}\\right]}{Dt}+\\divg\\mathbf{j}^{\\iota}=\\left(1-\\varphi^{s}\\right)\\nu^{\\iota}\\hat{\\zeta}\\,.\\label{eq:solute-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nThese mass balance equations reduce to those of non-reactive mixtures when \n\\begin_inset Formula $\\hat{\\zeta}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nFluid Mechanics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Fluid-Mechanics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nMass and Momentum Balance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Mass-Momentum\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a spatial (Eulerian) frame,\n the momentum balance equation for a continuum is\n\\begin_inset Formula \n\\begin{equation}\n\\rho\\mathbf{a}=\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}\\,,\\label{eq:momentum-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the density,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is the Cauchy stress,\n \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is the body force per mass,\n and \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is the acceleration,\n given by the material time derivative of the velocity \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n in the spatial frame,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}=\\dot{\\mathbf{v}}=\\frac{\\partial\\mathbf{v}}{\\partial t}+\\mathbf{L}\\cdot\\mathbf{v}\\,,\\label{eq:acceleration}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{L}=\\grad\\mathbf{v}$\n\\end_inset\n\n is the spatial velocity gradient.\n The mass balance equation is\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\rho}+\\rho\\divg\\mathbf{v}=0\\,,\\label{eq:mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere the material time derivative of the density in the spatial frame is\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\rho}=\\frac{\\partial\\rho}{\\partial t}+\\grad\\rho\\cdot\\mathbf{v}\\,.\\label{eq:density-material-derivative}\n\\end{equation}\n\n\\end_inset\n\nLet \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n denote the deformation gradient (the gradient of the motion with respect to the material coordinate).\n The material time derivative of \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is related to \n\\begin_inset Formula $\\mathbf{L}$\n\\end_inset\n\n via\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{F}}=\\mathbf{L}\\cdot\\mathbf{F}\\,.\\label{eq:F-dot}\n\\end{equation}\n\n\\end_inset\n\nLet \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n denote the Jacobian of the motion (the volume ratio,\n or ratio of current to referential volume,\n \n\\begin_inset Formula $J>0$\n\\end_inset\n\n);\n then,\n the dilatation (relative change in volume between current and reference configurations) is given by \n\\begin_inset Formula $e=J-1$\n\\end_inset\n\n.\n Using the chain rule,\n \n\\begin_inset Formula $J$\n\\end_inset\n\n's material time derivative is \n\\begin_inset Formula $\\dot{J}=J\\mathbf{F}^{-T}:\\dot{\\mathbf{F}}$\n\\end_inset\n\n which,\n when combined with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:F-dot\"\nnolink \"false\"\n\n\\end_inset\n\n,\n produces a kinematic constraint between \n\\begin_inset Formula $\\dot{J}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\divg\\mathbf{v}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}=J\\,\\divg\\mathbf{v}\\,.\\label{eq:divv-kinematic-relation}\n\\end{equation}\n\n\\end_inset\n\nSubstituting this relation into the mass balance,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n,\n produces \n\\begin_inset Formula $\\dot{\\overline{\\rho J}}=0$\n\\end_inset\n\n,\n which may be integrated directly to yield\n\\begin_inset Formula \n\\begin{equation}\n\\rho=\\rho_{r}/J\\,,\\label{eq:mass-balance-integrated}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is the density in the reference configuration (when \n\\begin_inset Formula $J=1$\n\\end_inset\n\n).\n Since \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is obtained by integrating the above material time derivative of \n\\begin_inset Formula $\\rho J$\n\\end_inset\n\n,\n it is an intrinsic material property that must be invariant in time and space.\n \n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\tau}\\,,\\label{eq:stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n is the identity tensor,\n \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the viscous stress,\n \n\\begin_inset Formula $p$\n\\end_inset\n\n is the pressure arising from the elastic response,\n\\begin_inset Formula \n\\begin{equation}\np=-\\frac{d\\Psi_{r}\\left(J\\right)}{dJ}\\,,\\label{eq:elastic-pressure}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n is the free energy density of the fluid (free energy per volume of the continuum in the reference configuration).\n The axiom of entropy inequality dictates that \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n cannot be a function of the rate of deformation \n\\begin_inset Formula $\\mathbf{D}=\\left(\\mathbf{L}+\\mathbf{L}^{T}\\right)/2$\n\\end_inset\n\n.\n In contrast,\n the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is generally a function of \n\\begin_inset Formula $J$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nBoundary conditions may be derived by satisfying mass and momentum balance across a moving interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n Let \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n divide the material domain \n\\begin_inset Formula $V$\n\\end_inset\n\n into subdomains \n\\begin_inset Formula $V_{+}$\n\\end_inset\n\n and \n\\begin_inset Formula $V_{-}$\n\\end_inset\n\n and let the outward normal to \n\\begin_inset Formula $V_{+}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n be denoted by \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n The jump condition across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n derived from the axiom of mass balance requires that\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\rho\\mathbf{u}_{\\Gamma}\\right]\\right]\\cdot\\mathbf{n}=0\\,,\\label{eq:mass-jump}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{u}_{\\Gamma}\\equiv\\mathbf{v}-\\mathbf{v}_{\\Gamma}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}_{\\Gamma}$\n\\end_inset\n\n is the velocity of the interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n Thus,\n \n\\begin_inset Formula $\\mathbf{u}_{\\Gamma}$\n\\end_inset\n\n represents the velocity of the fluid relative to \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n The double bracket notation denotes \n\\begin_inset Formula $\\left[\\left[f\\right]\\right]=f_{+}-f_{-}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $f_{+}$\n\\end_inset\n\n and \n\\begin_inset Formula $f_{-}$\n\\end_inset\n\n represent the value of \n\\begin_inset Formula $f$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n in \n\\begin_inset Formula $V_{+}$\n\\end_inset\n\n and \n\\begin_inset Formula $V_{-}$\n\\end_inset\n\n,\n respectively.\n This jump condition implies that the mass flux normal to \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n must be continuous.\n In particular,\n if \n\\begin_inset Formula $V_{+}$\n\\end_inset\n\n is a fluid domain and \n\\begin_inset Formula $V_{-}$\n\\end_inset\n\n is a solid domain,\n and \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n denotes the solid boundary (e.g.,\n a wall),\n we use \n\\begin_inset Formula $\\rho_{+}=\\rho$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{v}_{+}=\\mathbf{v}$\n\\end_inset\n\n for the fluid,\n and \n\\begin_inset Formula $\\mathbf{v}_{-}=\\mathbf{v}_{\\Gamma}$\n\\end_inset\n\n for the solid,\n such that eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-jump\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to \n\\begin_inset Formula $\\rho\\left(\\mathbf{v}-\\mathbf{v}_{\\Gamma}\\right)\\cdot\\mathbf{n}=0$\n\\end_inset\n\n.\n The jump condition derived from the axiom of linear momentum balance similarly requires that\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\boldsymbol{\\sigma}-\\rho\\mathbf{u}_{\\Gamma}\\otimes\\mathbf{u}_{\\Gamma}\\right]\\right]\\cdot\\mathbf{n}=\\mathbf{0}\\,.\\label{eq:momentum-jump}\n\\end{equation}\n\n\\end_inset\n\nThis condition implies that the jump in the traction \n\\begin_inset Formula $\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n must be balanced by the jump in momentum flux normal to \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n In addition to jump conditions dictated by axioms of conservation,\n viscous fluids require the satisfaction of the no-slip condition,\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\left[\\left[\\mathbf{u}_{\\Gamma}\\right]\\right]=\\mathbf{0}\\,,\\label{eq:no-slip-condition}\n\\end{equation}\n\n\\end_inset\n\nwhich implies that the velocity component tangential to \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n is continuous across that interface.\n\\end_layout\n\n\\begin_layout Standard\nIn our finite element treatment we use \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n as nodal variables,\n implying that our formulation automatically enforces continuity of these variables across element boundaries,\n thus \n\\begin_inset Formula $\\left[\\left[\\mathbf{v}\\right]\\right]=\\left[\\left[\\mathbf{u}_{\\Gamma}\\right]\\right]=\\mathbf{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left[\\left[J\\right]\\right]=0$\n\\end_inset\n\n.\n Based on Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-integrated\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:elastic-pressure\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that the density and elastic pressure are continuous across element boundaries in this formulation,\n \n\\begin_inset Formula $\\left[\\left[\\rho\\right]\\right]=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left[\\left[p\\right]\\right]=0$\n\\end_inset\n\n.\n Thus,\n the mass jump in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-jump\"\nnolink \"false\"\n\n\\end_inset\n\n is automatically satisfied,\n and the momentum jump in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:momentum-jump\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to \n\\begin_inset Formula $\\left[\\left[\\boldsymbol{\\sigma}\\right]\\right]\\cdot\\mathbf{n}=\\mathbf{0}$\n\\end_inset\n\n,\n requiring continuity of the traction,\n or more specifically according to \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stress\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the continuity of the viscous traction \n\\begin_inset Formula $\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $p$\n\\end_inset\n\n is automatically continuous.\n\\end_layout\n\n\\begin_layout Subsection\nWall Shear Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Wall-Shear-Stress\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a no-slip impermeable wall (a wall on which the fluid velocity is equal to the wall velocity,\n \n\\begin_inset Formula $\\mathbf{v}=\\mathbf{v}_{\\Gamma}$\n\\end_inset\n\n).\n Let \n\\begin_inset Formula $\\left\\{ \\mathbf{n},\\mathbf{s},\\mathbf{t}\\right\\} $\n\\end_inset\n\n be an orthonormal basis where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the outward normal to the fluid on the wall,\n \n\\begin_inset Formula $\\mathbf{s}$\n\\end_inset\n\n is a unit tangent to the wall along the local direction of the flow (in the immediate wall vicinity),\n and \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n is a unit tangent to the wall in the direction orthogonal to \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{s}$\n\\end_inset\n\n.\n The components of the velocity gradient \n\\begin_inset Formula $\\mathbf{L}=\\grad\\mathbf{v}$\n\\end_inset\n\n on the wall,\n when expressed in matrix form in the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{n},\\mathbf{s},\\mathbf{t}\\right\\} $\n\\end_inset\n\n,\n are zero in the directions of the wall tangent (since there is no variation in any of the velocity components along those directions).\n Moreover,\n by construction,\n the tangential velocity component \n\\begin_inset Formula $v_{t}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n is zero,\n not only on the wall but also in its vicinity (thus,\n \n\\begin_inset Formula $\\partial v_{t}/\\partial x_{n}=0$\n\\end_inset\n\n).\n Combining these factors produces\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{L}\\right]=\\left[\\begin{array}{ccc}\n\\frac{\\partial v_{n}}{\\partial x_{n}} & \\frac{\\partial v_{n}}{\\partial x_{s}} & \\frac{\\partial v_{n}}{\\partial x_{t}}\\\\\n\\frac{\\partial v_{s}}{\\partial x_{n}} & \\frac{\\partial v_{s}}{\\partial x_{s}} & \\frac{\\partial v_{s}}{\\partial x_{t}}\\\\\n\\frac{\\partial v_{t}}{\\partial x_{n}} & \\frac{\\partial v_{t}}{\\partial x_{s}} & \\frac{\\partial v_{t}}{\\partial x_{t}}\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\n\\frac{\\partial v_{n}}{\\partial x_{n}} & 0 & 0\\\\\n\\frac{\\partial v_{s}}{\\partial x_{n}} & 0 & 0\\\\\n0 & 0 & 0\n\\end{array}\\right]\\label{eq:wss-L}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $dx_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $dx_{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $dx_{t}$\n\\end_inset\n\n represent line elements along each of the coordinate directions.\n The resulting rate of deformation tensor is\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{D}\\right]=\\frac{1}{2}\\left(\\left[\\mathbf{L}\\right]+\\left[\\mathbf{L}\\right]^{T}\\right)=\\left[\\begin{array}{ccc}\n\\frac{\\partial v_{n}}{\\partial x_{n}} & \\frac{1}{2}\\frac{\\partial v_{s}}{\\partial x_{n}} & 0\\\\\n\\frac{1}{2}\\frac{\\partial v_{s}}{\\partial x_{n}} & 0 & 0\\\\\n0 & 0 & 0\n\\end{array}\\right]\\label{eq:wss=D}\n\\end{equation}\n\n\\end_inset\n\nTherefore the fluid stress evaluated from Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n will typically have the form\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\boldsymbol{\\sigma}\\right]=-p\\left[\\mathbf{I}\\right]+\\left(\\kappa-\\frac{2}{3}\\mu\\right)\\left(\\tr\\mathbf{D}\\right)\\left[\\mathbf{I}\\right]+2\\mu\\left[\\mathbf{D}\\right]=\\left[\\begin{array}{ccc}\n-p+\\left(\\kappa+\\frac{4}{3}\\eta\\right)\\frac{\\partial v_{n}}{\\partial x_{n}} & \\mu\\frac{\\partial v_{s}}{\\partial x_{n}} & 0\\\\\n\\mu\\frac{\\partial v_{s}}{\\partial x_{n}} & -p & 0\\\\\n0 & 0 & -p\n\\end{array}\\right]\\label{eq:wss-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n is the bulk viscosity and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the shear viscosity (which may be a function of the rate of deformation in the case of a non-Newtonian fluid),\n see Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Viscous-Fluids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n From this result we conclude that the wall shear stress is\n\\begin_inset Formula \n\\begin{equation}\n\\sigma_{ns}=\\sigma_{sn}=\\mu\\frac{\\partial v_{s}}{\\partial x_{n}}\\label{eq:wss-shear-stress}\n\\end{equation}\n\n\\end_inset\n\nIn practice it is inconvenient to evaluate this wall shear stress in a local coordinate system attached to the wall and directed along the flow in the wall vicinity.\n As it turns out,\n the principal normal stresses evaluated from \n\\begin_inset Formula $\\left[\\boldsymbol{\\sigma}\\right]$\n\\end_inset\n\n in Eq.\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wss-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\sigma_{1} & =-p & \\sigma_{2} & =-p+\\frac{1}{2}\\left(\\sigma_{nn}-\\sqrt{\\sigma_{nn}^{2}+4\\sigma_{sn}^{2}}\\right) & \\sigma_{3} & =-p+\\frac{1}{2}\\left(\\sigma_{nn}+\\sqrt{\\sigma_{nn}^{2}+4\\sigma_{sn}^{2}}\\right)\\end{aligned}\n\\label{eq:wss-principal-normal}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\sigma_{nn} & =-p+\\left(\\kappa+\\frac{4}{3}\\mu\\right)\\frac{\\partial v_{n}}{\\partial x_{n}} & \\sigma_{sn} & =\\mu\\frac{\\partial v_{s}}{\\partial x_{n}}\\end{aligned}\n\\label{eq:wss-normal-shear-tractions}\n\\end{equation}\n\n\\end_inset\n\nThus,\n the maximum shear stress is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\sigma_{s,\\text{max}} & =\\max\\left(\\frac{\\left|\\sigma_{1}-\\sigma_{2}\\right|}{2},\\frac{\\left|\\sigma_{2}-\\sigma_{3}\\right|}{2},\\frac{\\left|\\sigma_{3}-\\sigma_{1}\\right|}{2}\\right)\\\\\n & =\\max\\left(\\frac{1}{4}\\left|\\sigma_{nn}-\\sqrt{\\sigma_{nn}^{2}+4\\sigma_{sn}^{2}}\\right|,\\frac{1}{2}\\left|\\sqrt{\\sigma_{nn}^{2}+4\\sigma_{sn}^{2}}\\right|,\\frac{1}{4}\\left|\\sigma_{nn}+\\sqrt{\\sigma_{nn}^{2}+4\\sigma_{sn}^{2}}\\right|\\right)\n\\end{aligned}\n\\label{eq:wss-max-shear-stress-general}\n\\end{equation}\n\n\\end_inset\n\nWhen the flow is incompressible it follows that \n\\begin_inset Formula $\\tr\\mathbf{D}=\\frac{\\partial v_{n}}{\\partial x_{n}}=0$\n\\end_inset\n\n,\n in which case \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{nn}=-p$\n\\end_inset\n\n according to Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wss-normal-shear-tractions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\n\\sigma_{s,\\text{max}}=\\max\\left(\\frac{\\left|\\sigma_{ns}\\right|}{2},\\left|\\sigma_{ns}\\right|,\\frac{\\left|\\sigma_{ns}\\right|}{2}\\right)=\\left|\\sigma_{sn}\\right|\\label{eq:wss-max-shear-incompressible}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n when the flow is incompressible the wall shear stress is equal to the maximum fluid shear stress.\n This result makes it convenient to use the maximum fluid shear stress on the wall as the value of the wall shear stress.\n Conversely,\n if the flow is compressible (which is generally the case in FEBio) but as long as\n\\begin_inset Formula \n\\begin{equation}\n\\left|\\sigma_{nn}\\right|\\ll2\\left|\\sigma_{sn}\\right|\\label{eq:wss-negligible-normal}\n\\end{equation}\n\n\\end_inset\n\nthe conclusion remains the same.\n Therefore,\n in most applications one can use the maximum fluid shear stress as a method for estimating the wall shear stress.\n This result remains valid for problems where the wall is not stationary,\n such as fluid-structure interaction problems.\n In practice one can always examine the principal normal stresses in the fluid,\n along the wall,\n from which one can deduce if Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wss-negligible-normal\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is satisfied satisfactorily.\n\\end_layout\n\n\\begin_layout Standard\nHowever,\n if the wall is porous or if slippage is allowed,\n then the wall velocity components are not necessarily zero or negligible.\n Thus,\n the maximum shear stress is not always equal to the wall shear stress.\n\\end_layout\n\n\\begin_layout Subsection\nEnergy Balance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Energy-Balance\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe energy balance for a continuum may be written in integral form over a control volume \n\\begin_inset Formula $V$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{d}{dt}\\int_{V}\\rho\\left(\\varepsilon+\\frac{1}{2}\\mathbf{v}\\cdot\\mathbf{v}\\right)\\,dV & =-\\int_{S}\\rho\\left(\\varepsilon+\\frac{1}{2}\\mathbf{v}\\cdot\\mathbf{v}\\right)\\left(\\mathbf{v}\\cdot\\mathbf{n}\\right)\\,dS+\\int_{S}\\mathbf{t}\\cdot\\mathbf{v}\\,dS+\\int_{V}\\rho\\mathbf{b}\\cdot\\mathbf{v}\\,dV\\\\\n & -\\int_{S}\\mathbf{q}\\cdot\\mathbf{n}\\,dS+\\int_{V}\\rho r\\,dV\n\\end{aligned}\n\\,,\\label{eq:energy-balance-integral}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $S$\n\\end_inset\n\n is the control surface bounding \n\\begin_inset Formula $V$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the specific internal energy,\n \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n is the heat flux across \n\\begin_inset Formula $S$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $r$\n\\end_inset\n\n is the heat supply per mass to the material in \n\\begin_inset Formula $V$\n\\end_inset\n\n resulting from other sources.\n Bringing the time derivative inside the integral on the left-hand-side,\n and using the divergence theorem,\n this integral statement of the energy balance may be written as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & \\int_{V}\\left[\\rho\\left(\\dot{\\varepsilon}+\\mathbf{v}\\cdot\\mathbf{a}\\right)+\\rho\\left(\\varepsilon+\\frac{1}{2}\\mathbf{v}\\cdot\\mathbf{v}\\right)\\left(\\divg\\mathbf{v}-\\frac{\\dot{J}}{J}\\right)\\right]\\,dV\\\\\n & =\\int_{V}\\left[\\boldsymbol{\\sigma}:\\mathbf{D}-\\divg\\mathbf{q}+\\rho r+\\mathbf{v}\\cdot\\left(\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}\\right)\\right]\\,dV\n\\end{aligned}\n\\,.\\label{eq:energy-integral-redux}\n\\end{equation}\n\n\\end_inset\n\nThis statement must be valid for arbitrary control volumes and arbitrary processes,\n from which we conventionally derive the differential form of the axioms of mass,\n momentum and energy balance.\n\\end_layout\n\n\\begin_layout Standard\nFor the specialized conditions of a viscous fluid at constant temperature assumed in our treatment,\n the only state variables for the functions of state \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n are \n\\begin_inset Formula $J$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n (i.e.,\n the temperature is not a state variable since it is assumed constant).\n Under these conditions the entropy inequality shows that the specific entropy \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n and the heat flux \n\\begin_inset Formula $\\mathbf{q}$\n\\end_inset\n\n must be zero,\n and the Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n must have the form of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stress\"\nnolink \"false\"\n\n\\end_inset\n\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:elastic-pressure\"\nnolink \"false\"\n\n\\end_inset\n\n as a function of \n\\begin_inset Formula $J$\n\\end_inset\n\n only,\n leaving the residual dissipation statement \n\\begin_inset Formula $\\boldsymbol{\\tau}:\\mathbf{D}\\ge0$\n\\end_inset\n\n as a constraint that must be satisfied by constitutive relations for \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n.\n (For a Newtonian fluid,\n this constraint is satisfied when the viscosities \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n and \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n are positive.) From these thermodynamic restrictions we conclude that \n\\begin_inset Formula $\\varepsilon=\\psi$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is the specific (Helmholtz) free energy,\n with \n\\begin_inset Formula $\\Psi_{r}=\\rho_{r}\\psi$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor the conditions adopted here (isothermal viscous fluid),\n the axiom of energy balance reduces to \n\\begin_inset Formula $\\rho\\dot{\\psi}=\\boldsymbol{\\sigma}:\\mathbf{D}+\\rho r$\n\\end_inset\n\n;\n since \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is only a function of \n\\begin_inset Formula $J$\n\\end_inset\n\n,\n this expression may be further simplified using Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:divv-kinematic-relation\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:elastic-pressure\"\nnolink \"false\"\n\n\\end_inset\n\n to produce \n\\begin_inset Formula $\\boldsymbol{\\tau}:\\mathbf{D}+\\rho r=0$\n\\end_inset\n\n.\n In other words,\n isothermal conditions may be maintained only if heat dissipated by the viscous stress is emitted in the form of a heat supply density \n\\begin_inset Formula $\\rho r=-\\boldsymbol{\\tau}:\\mathbf{D}$\n\\end_inset\n\n (heat leaving the system).\n Now,\n the integral form of the energy balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:energy-integral-redux\"\nnolink \"false\"\n\n\\end_inset\n\n simplifies to\n\\begin_inset Formula \n\\begin{equation}\n\\int_{V}\\left[\\mathbf{v}\\cdot\\left(\\divg\\boldsymbol{\\sigma}+\\rho\\left(\\mathbf{b}-\\mathbf{a}\\right)\\right)+\\rho\\left(\\psi+\\frac{1}{2}\\mathbf{v}\\cdot\\mathbf{v}\\right)\\left(\\frac{\\dot{J}}{J}-\\divg\\mathbf{v}\\right)\\right]\\,dV=0\\,.\\label{eq:energy-isothermal-viscous}\n\\end{equation}\n\n\\end_inset\n\nA comparison of this statement with the statement of virtual work,\n presented below in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:virtual work\"\nnolink \"false\"\n\n\\end_inset\n\n,\n establishes a clear correspondence between the virtual velocity \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n,\n and between the virtual energy density \n\\begin_inset Formula $\\delta J$\n\\end_inset\n\n and \n\\begin_inset Formula $\\rho\\left(\\psi+\\frac{1}{2}\\mathbf{v}\\cdot\\mathbf{v}\\right)$\n\\end_inset\n\n,\n with the latter representing the sum of the internal (free) and kinetic energy densities.\n\\end_layout\n\n\\begin_layout Section\nFluid-Structure Interactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Fluid-Structure-Interactions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe model the fluid domain in an FSI analysis as a mixture of an isothermal compressible viscous fluid and a hyperelastic compressible solid,\n where the solid has zero apparent density,\n and negligible (but non-zero) elasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim19\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In a mixture framework,\n all mixture constituents coexist at every point in the continuum.\n For our FSI implementation,\n the finite element mesh is defined on the solid material,\n and the negligible (but non-zero) elasticity of the solid is intended to regularize the mesh deformation.\n The fluid flows through this mesh,\n unimpeded by the solid.\n In particular,\n in this FSI model there is no frictional interaction between the fluid and solid materials inside the mixture domain (i.e.,\n no Darcy-Brinkman type of friction),\n but the no-slip boundary condition may be prescribed on boundaries of the mixture domain,\n where applicable.\n\\end_layout\n\n\\begin_layout Subsection\nFSI Governing Equations\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:FSI-Governing-Equations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe momentum balance for the fluid is\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{f}\\mathbf{a}^{f}=\\divg\\boldsymbol{\\sigma}^{f}+\\rho^{f}\\mathbf{b}\\,,\\label{eq:fsi-fluid-momentum}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is the external body force acting on the FSI domain,\n \n\\begin_inset Formula $\\mathbf{a}^{f}$\n\\end_inset\n\n is the fluid acceleration,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}^{f}=\\frac{\\partial\\mathbf{v}^{f}}{\\partial t}+\\mathbf{L}^{f}\\cdot\\mathbf{v}^{f}\\,,\\label{eq:fsi-fluid-acceleration}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{L}^{f}=\\grad\\mathbf{v}^{f}$\n\\end_inset\n\n is the fluid velocity gradient.\n In principle,\n the same momentum equation may be used for the solid (substituting \n\\begin_inset Formula $f$\n\\end_inset\n\n with \n\\begin_inset Formula $s$\n\\end_inset\n\n);\n however,\n since we opted to let \n\\begin_inset Formula $\\rho^{s}=0$\n\\end_inset\n\n (no solid mass),\n the momentum balance for the solid simply reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\boldsymbol{\\sigma}^{s}=\\mathbf{0}\\,.\\label{eq:fsi-solid-momentum}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe model the fluid as isothermal and compressible,\n consistent with our CFD implementation.\n Thus,\n the fluid stress may be separated into the elastic pressure \n\\begin_inset Formula $p\\left(J^{f}\\right)$\n\\end_inset\n\n,\n which only depends on the fluid volume ratio \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n,\n and the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}\\left(J^{f},\\mathbf{L}^{f}\\right)$\n\\end_inset\n\n,\n as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{f}=-p\\mathbf{I}+\\boldsymbol{\\tau}\\,.\\label{eq:fsi-fluid-stress-split}\n\\end{equation}\n\n\\end_inset\n\nRecall that there is no dependence on temperature in an isothermal formulation.\n As done in our previous study,\n we integrate the mass balance for the fluid to produce\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{f}=\\frac{\\rho_{r}^{f}}{J^{f}}\\,,\\label{eq:fsi-integrated-fluid-mass}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}^{f}$\n\\end_inset\n\n is the fluid density in the reference state (e.g.,\n under ambient pressure) and \n\\begin_inset Formula $J^{f}=\\det\\mathbf{F}^{f}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{F}^{f}$\n\\end_inset\n\n is the fluid deformation gradient.\n We also use the kinematic constraint\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial J^{f}}{\\partial t}+\\grad J^{f}\\cdot\\mathbf{v}^{f}=J^{f}\\divg\\mathbf{v}^{f}\\,,\\label{eq:fsi-fluid-kinematic constraint}\n\\end{equation}\n\n\\end_inset\n\nto relate the fluid volume ratio \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n to its velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n in the spatial frame.\n\\end_layout\n\n\\begin_layout Standard\nThe mesh through which the fluid flows is defined on the solid component of the mixture.\n Therefore,\n we define the relative velocity between the fluid and solid as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}=\\mathbf{v}^{f}-\\mathbf{v}^{s}\\,.\\label{eq:fsi-fluid-relative-velocity}\n\\end{equation}\n\n\\end_inset\n\nSince the solid has zero volume fraction,\n this expression is the same as the flux of fluid relative to the solid.\n We choose to define the nodal degrees of freedom in the mixture domain to be the relative fluid velocity \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n the fluid dilatation \n\\begin_inset Formula $e^{f}=J^{f}-1$\n\\end_inset\n\n,\n and the solid displacement \n\\begin_inset Formula $\\mathbf{u}^{s}$\n\\end_inset\n\n,\n which is related to the solid velocity via \n\\begin_inset Formula $\\mathbf{v}^{s}=\\dot{\\mathbf{u}}^{s}$\n\\end_inset\n\n,\n with the dot operator denoting the material time derivative following the solid.\n (For notational convenience,\n we will continue using \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n in the equations below,\n instead of \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n).\n Now,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{f}=\\mathbf{w}+\\mathbf{v}^{s}\\,,\\label{eq:fsi-fluid-velocity}\n\\end{equation}\n\n\\end_inset\n\nfrom which it follows that the fluid velocity gradient is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{L}^{f}=\\mathbf{L}^{w}+\\mathbf{L}^{s}\\,,\\label{eq:fsi-fluid-velocity-gradient}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{L}^{w}=\\grad\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{L}^{s}=\\grad\\mathbf{v}^{s}$\n\\end_inset\n\n.\n The fluid acceleration may now be rewritten in terms of the FEA degrees of freedom as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}^{f}=\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}+\\left(\\mathbf{L}^{w}+\\mathbf{L}^{s}\\right)\\cdot\\mathbf{w}\\,,\\label{eq:fsi-fluid-acceleration-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\dot{\\mathbf{v}}^{s} & =\\frac{\\partial\\mathbf{v}^{s}}{\\partial t}+\\mathbf{L}^{s}\\cdot\\mathbf{v}^{s}\\\\\n\\dot{\\mathbf{w}} & =\\frac{\\partial\\mathbf{w}}{\\partial t}+\\mathbf{L}^{w}\\cdot\\mathbf{v}^{s}\n\\end{aligned}\n\\label{eq:fsi-vs-w-material-derivs}\n\\end{equation}\n\n\\end_inset\n\nare the material time derivatives of the solid and relative fluid velocities,\n in the frame following the solid.\n We conveniently use this material time derivative (instead of the material time derivative following the fluid) since we define the mixture domain mesh on the solid.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n the kinematic constraint relating \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n may be rewritten as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}=J^{f}\\divg\\left(\\mathbf{w}+\\mathbf{v}^{s}\\right)\\,,\\label{eq:fsi-fluid-kinematic-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}^{f}=\\frac{\\partial J^{f}}{\\partial t}+\\grad J^{f}\\cdot\\mathbf{v}^{s}\\,.\\label{eq:fsi-dilatation-material-deriv}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n as done routinely in our studies of biphasic and multiphasic materials,\n we find it convenient to substitute the kinematic identity\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\mathbf{v}^{s}=\\frac{\\dot{J}^{s}}{J^{s}}\\,,\\label{eq:fsi-solid-kinematic-constraint}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J^{s}=\\det\\mathbf{F}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{F}^{s}=\\mathbf{I}+\\Grad\\mathbf{u}^{s}$\n\\end_inset\n\n is the solid deformation gradient.\n \n\\end_layout\n\n\\begin_layout Standard\nIn summary,\n the governing equations for the FSI mixture domain are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & \\divg\\boldsymbol{\\sigma}^{s}=\\mathbf{0}\\\\\n & \\rho^{f}\\mathbf{a}^{f}=\\divg\\boldsymbol{\\sigma}^{f}+\\rho^{f}\\mathbf{b}\\\\\n & \\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)=\\divg\\mathbf{w}+\\frac{\\dot{J}^{s}}{J^{s}}\n\\end{aligned}\n\\label{eq:fsi-governing-equations}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{a}^{f}$\n\\end_inset\n\n is given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fsi-fluid-acceleration-redux\"\nnolink \"false\"\n\n\\end_inset\n\n above.\n\\end_layout\n\n\\begin_layout Section\nHybrid Biphasic Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Hybrid-Biphasic-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio,\n the standard biphasic material consists of a mixture of intrinsically incompressible solid and fluid constituents,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Material\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In this standard material,\n the solid and fluid dynamics are neglected,\n as well as the fluid viscosity.\n When a user wishes to consider these dynamics effects,\n as well as fluid viscosity,\n they may use a hybrid biphasic material,\n as described in this section.\n The complete theoretical framework for such materials can be found in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22\"\nliteral \"false\"\n\n\\end_inset\n\n.\n A hybrid biphasic domain is a mixture of an isothermal compressible viscous fluid and a hyperelastic compressible porous solid whose solid skeleton is intrinsically incompressible.\n Unlike the fluid-FSI material,\n here the solid has non-negligible mass density and elasticity,\n and frictional interactions may occur between the fluid and solid constituents,\n modeled using a hydraulic permeability (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Hydraulic-Permeability\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n In FEBio,\n the hybrid biphasic material is called \n\\begin_inset Quotes eld\n\\end_inset\n\nbiphasic-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n since it allows dynamic fluid-structure interactions between this material and a fluid-FSI material,\n or a solid material.\n Here,\n we may abbreviate \n\\begin_inset Quotes eld\n\\end_inset\n\nbiphasic-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n as BFSI.\n For BFSI domains,\n as with standard biphasic domains,\n the finite element mesh is defined on the porous solid material.\n The viscous fluid flows through this mesh,\n experiencing frictional drag caused by the porous solid.\n When two BFSI domains are interfaced,\n or when a BFSI domain is interfaced with a fluid-FSI domain,\n the pseudo-no slip condition \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hou89\"\nliteral \"false\"\n\n\\end_inset\n\n is enforced automatically on those interfaces.\n When a BFSI domain interfaces with a solid domain,\n the no-slip boundary condition has to be prescribed explicitly on those interfaces,\n where applicable.\n\\end_layout\n\n\\begin_layout Subsection\nBFSI Governing Equations\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BFSI-Governing-Equations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe model the BFSI domain as an unconstrained mixture of an isothermal,\n compressible,\n and viscous fluid and an isothermal,\n deformable porous solid whose skeleton material is intrinsically incompressible.\n As usual in mixture theory,\n each constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha=s,f$\n\\end_inset\n\n for solid and fluid,\n respectively) has its own apparent density \n\\begin_inset Formula $\\rho^{\\alpha}=dm^{\\alpha}/dV$\n\\end_inset\n\n,\n which represents the ratio of the elemental mass \n\\begin_inset Formula $dm^{\\alpha}$\n\\end_inset\n\n of constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the elemental mixture volume \n\\begin_inset Formula $dV$\n\\end_inset\n\n.\n This apparent density is related to the true density \n\\begin_inset Formula $\\rho_{T}^{\\alpha}=dm^{\\alpha}/dV^{\\alpha}$\n\\end_inset\n\n (mass of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n per volume of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n) via \n\\begin_inset Formula $\\rho^{\\alpha}=\\varphi^{\\alpha}\\rho_{T}^{\\alpha}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varphi^{\\alpha}=dV^{\\alpha}/dV$\n\\end_inset\n\n is the volume fraction of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the mixture,\n satisfying \n\\begin_inset Formula $\\varphi^{s}+\\varphi^{f}=1$\n\\end_inset\n\n.\n Since the solid skeleton is intrinsically incompressible,\n \n\\begin_inset Formula $\\rho_{T}^{s}$\n\\end_inset\n\n is constant.\n The boundaries of a biphasic domain are defined on the porous solid matrix.\n The deformation gradient of the solid is denoted by \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n and its determinant,\n \n\\begin_inset Formula $J^{s}=\\det\\mathbf{F}^{s}=dV/dV_{r}$\n\\end_inset\n\n,\n represents the ratio of the mixture elemental volumes in the current (\n\\begin_inset Formula $dV$\n\\end_inset\n\n) and reference (\n\\begin_inset Formula $dV_{r}$\n\\end_inset\n\n) configurations.\n Thus,\n since the solid skeleton is intrinsically incompressible,\n \n\\begin_inset Formula $J^{s}$\n\\end_inset\n\n purely represents the compressibility of the pore volume as fluid enters or leaves the pore space,\n or as the compressible fluid within the pores changes in volume.\n The axiom of mass balance for the solid may be integrated in closed form to produce \n\\begin_inset Formula $\\rho^{s}=\\rho_{r}^{s}/J^{s}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\rho_{r}^{s}=dm^{s}/dV_{r}$\n\\end_inset\n\n is the solid apparent density in the mixture reference configuration.\n Therefore,\n we may define the referential solid volume fraction as \n\\begin_inset Formula $\\varphi_{r}^{s}=\\rho_{r}^{s}/\\rho_{T}^{s}$\n\\end_inset\n\n.\n In the finite element analysis,\n \n\\begin_inset Formula $\\varphi_{r}^{s}=dV^{s}/dV_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\rho_{T}^{s}$\n\\end_inset\n\n are both specified by the user,\n then \n\\begin_inset Formula $\\rho_{r}^{s}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\rho^{s}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\varphi^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi^{f}$\n\\end_inset\n\n are evaluated from the above relations,\n given a solution for \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n (and thus,\n \n\\begin_inset Formula $J^{s}$\n\\end_inset\n\n).\n The solution for \n\\begin_inset Formula $\\mathbf{F}^{s}=\\mathbf{I}+\\Grad\\mathbf{u}$\n\\end_inset\n\n is obtained by solving for the nodal solid displacement vector \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\Grad\\left(\\cdot\\right)$\n\\end_inset\n\n represents the gradient operator in the material frame.\n\\end_layout\n\n\\begin_layout Standard\nFor the compressible fluid phase of a hybrid biphasic material,\n the true density \n\\begin_inset Formula $\\rho_{T}^{f}$\n\\end_inset\n\n varies with the intrinsic fluid volumetric strain,\n or dilatation,\n \n\\begin_inset Formula $e^{f}=J^{f}-1$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n is the volume ratio of the fluid in its current and reference configurations,\n \n\\begin_inset Formula $dm^{f}$\n\\end_inset\n\n is the element mass of fluid and \n\\begin_inset Formula $dV^{f}$\n\\end_inset\n\n is the elemental volume of that fluid in the pores of the mixture,\n in the current configuration.\n It follows from this definition that\n\\begin_inset Formula \n\\begin{equation}\nJ^{f}=\\frac{dV^{f}}{dV_{r}^{f}}=\\frac{dV^{f}}{dm^{f}}\\frac{dm^{f}}{dV_{r}^{f}}=\\frac{\\rho_{Tr}^{f}}{\\rho_{T}^{f}}\\thinspace,\\label{eq:bfsi-Jf}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $dV_{r}^{f}$\n\\end_inset\n\n is the elemental volume of this same fluid (having the same elemental mass \n\\begin_inset Formula $dm^{f}$\n\\end_inset\n\n) in the fluid's reference configuration.\n Thus,\n \n\\begin_inset Formula $\\rho_{Tr}^{f}=dm^{f}/dV_{r}^{f}$\n\\end_inset\n\n is a constant representing the true fluid density in its reference configuration,\n which is specified by the user.\n Accordingly,\n \n\\begin_inset Formula $J^{f}=1$\n\\end_inset\n\n in the limit when the fluid is idealized to be intrinsically incompressible.\n This definition of \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n is consistent with that for a pure fluid,\n as shown in our earlier formulation of computational fluid dynamics \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n As shown above,\n \n\\begin_inset Formula $\\rho^{f}$\n\\end_inset\n\n may be evaluated as \n\\begin_inset Formula $\\varphi^{f}\\rho_{T}^{f}$\n\\end_inset\n\n,\n which now expands into an expression involving solid and fluid volume ratios,\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{f}=\\left(1-\\frac{\\varphi_{r}^{s}}{J^{s}}\\right)\\frac{\\rho_{Tr}^{f}}{J^{f}}\\,.\\label{eq:bfsi-rhof-Jf-Js}\n\\end{equation}\n\n\\end_inset\n\nThe limiting case when the fluid within the solid matrix pores has been completely squeezed out corresponds to \n\\begin_inset Formula $\\rho^{f}=0$\n\\end_inset\n\n (or \n\\begin_inset Formula $dm^{f}=0$\n\\end_inset\n\n).\n Based on the above relation,\n this is equivalent to having the entire mixture volume \n\\begin_inset Formula $dV$\n\\end_inset\n\n reduce to the solid volume \n\\begin_inset Formula $dV^{s}$\n\\end_inset\n\n in the current configuration,\n or equivalently \n\\begin_inset Formula $J^{s}=\\varphi_{r}^{s}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In this limiting case,\n the mixture becomes an intrinsically incompressible solid and the finite element formulation presented in this study no longer applies.\n\\end_layout\n\n\\begin_layout Standard\nWe assume that \n\\begin_inset Formula $\\mathbf{F}^{s}=\\mathbf{I}$\n\\end_inset\n\n (or equivalently,\n \n\\begin_inset Formula $\\mathbf{u}=\\mathbf{0}$\n\\end_inset\n\n) and \n\\begin_inset Formula $e^{f}=0$\n\\end_inset\n\n at the start of a finite element analysis.\n The fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n is included as a nodal degree of freedom,\n implying that it is continuous across finite element boundaries.\n This assumption is verified below,\n when we review the jump conditions on axioms of mass,\n momentum and energy balance across interfaces.\n\\end_layout\n\n\\begin_layout Standard\nSince the axiom of mass balance for the solid has already been solved in closed form,\n we only need to be concerned with the axiom of mass balance for the fluid,\n or alternatively,\n that of the mixture,\n which takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\frac{D^{f}J^{f}}{Dt}=\\frac{J^{f}}{\\varphi^{f}}\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)\\thinspace,\\label{eq:bfsi-mix-mass-balance-f}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{v}^{s}$\n\\end_inset\n\n is the solid velocity (the material time derivative of the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n),\n and\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}=\\varphi^{f}\\left(\\mathbf{v}^{f}-\\mathbf{v}^{s}\\right)\\thinspace\\label{eq:bfsi-fluid-flux}\n\\end{equation}\n\n\\end_inset\n\nis the volumetric flux of the fluid relative to the solid ,\n with \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n representing the fluid velocity.\n Here,\n \n\\begin_inset Formula $D^{f}\\left(\\cdot\\right)/Dt$\n\\end_inset\n\n is the material time derivative operator in the spatial frame,\n following the fluid motion.\n Since the finite element mesh is defined on the porous solid matrix of a biphasic mixture,\n and since the fluid flows relative to the solid,\n material time derivatives need to follow the solid motion in the finite element implementation.\n A similar scheme was used in our implementations of solute transport within deformable porous domains (Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n to account for the motion of solutes relative to the porous solid matrix,\n as well as in our FSI formulation (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Fluid-Structure-Interactions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian12,Ateshian13,Shim19\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Thus,\n we substitute the following identity,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{D^{f}J^{f}}{Dt} & =\\frac{\\partial J^{f}}{\\partial t}+\\grad J^{f}\\cdot\\mathbf{v}^{f}\\\\\n & =\\frac{D^{s}J^{f}}{Dt}+\\grad J^{f}\\cdot\\left(\\mathbf{v}^{f}-\\mathbf{v}^{s}\\right)\n\\end{aligned}\n\\,,\\label{eq:bfsi-DfJf-DsJf}\n\\end{equation}\n\n\\end_inset\n\ninto the axiom of mixture mass balance in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-mix-mass-balance-f\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to produce the final form\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}^{f}+\\frac{1}{\\varphi^{f}}\\grad J^{f}\\cdot\\mathbf{w}=\\frac{J^{f}}{\\varphi^{f}}\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)\\,.\\label{eq:bfsi-fluid-kin-constr}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n the dot operator in \n\\begin_inset Formula $\\dot{J}^{f}$\n\\end_inset\n\n represents the material time derivative following the solid motion.\n In the solid material frame,\n this material time derivative reduces to the partial time derivative.\n Accordingly,\n we may now write \n\\begin_inset Formula $\\mathbf{v}^{s}=\\dot{\\mathbf{u}}$\n\\end_inset\n\n.\n In the BFSI implementation,\n the fluid volumetric flux \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n relative to the solid is added to the list of nodal DOFs,\n giving us the complete set \n\\begin_inset Formula $\\left(\\mathbf{u},\\mathbf{w},e^{f}\\right)$\n\\end_inset\n\n.\n This choice implies that \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n is continuous across finite element boundaries,\n as justified further below when we review jump conditions across interfaces.\n\\end_layout\n\n\\begin_layout Standard\nBased on the constitutive assumptions of our hybrid biphasic formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the momentum balance equations for the fluid and solid constituents reduce to\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{f}\\mathbf{a}^{f}=-\\varphi^{f}\\grad p+\\divg\\boldsymbol{\\tau}^{f}+\\rho^{f}\\mathbf{b}^{f}-\\varphi^{f}\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\,,\\label{eq:bfsi-fluid-momentum}\n\\end{equation}\n\n\\end_inset\n\n and\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{s}\\mathbf{a}^{s}=-\\varphi^{s}\\grad p+\\divg\\boldsymbol{\\sigma}^{e}+\\rho^{s}\\mathbf{b}^{s}+\\varphi^{f}\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\,,\\label{eq:bfsi-solid-momentum}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{a}^{\\alpha}=D^{\\alpha}\\mathbf{v}^{\\alpha}/Dt$\n\\end_inset\n\n is the acceleration,\n \n\\begin_inset Formula $\\mathbf{b}^{\\alpha}$\n\\end_inset\n\n is the body force per mass acting on constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n,\n \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure,\n \n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}$\n\\end_inset\n\n is the apparent fluid viscous stress,\n and \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the apparent solid elastic stress.\n These stress tensors are called apparent because their associated traction vectors represent a force acting on constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n per elemental \n\\emph on\nmixture\n\\emph default\n area.\n Here,\n \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n is the hydraulic permeability tensor which regulates frictional drag between the fluid and solid constituents;\n setting \n\\begin_inset Formula $\\mathbf{k}^{-1}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\mathbf{0}$\n\\end_inset\n\n implies that this frictional drag is non-existent.\n Since the fluid is compressible,\n its pressure must be given by a function of state.\n In the isothermal framework used here,\n this function only depends on \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n and the form adopted in FEBio is\n\\begin_inset Formula \n\\begin{equation}\np=-Ke^{f}\\,,\\label{eq:bfsi-pressure-state}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K$\n\\end_inset\n\n is the fluid bulk modulus (a user-specified material property).\n In the limit when inertia and body forces are neglected (\n\\begin_inset Formula $\\mathbf{a}^{f}=\\mathbf{b}^{f}=\\mathbf{0}$\n\\end_inset\n\n),\n the fluid momentum balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-momentum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n produces the classical Darcy-Brinkman relation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Brinkman49\"\nliteral \"false\"\n\n\\end_inset\n\n.\n If the fluid viscous stress is also neglected (\n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}=\\mathbf{0}$\n\\end_inset\n\n),\n we recover Darcy's law,\n \n\\begin_inset Formula $\\mathbf{w}=-\\mathbf{k}\\cdot\\grad p$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSince the finite element implementation requires all material time derivatives to follow the motion of the solid,\n we recognize that \n\\begin_inset Formula $\\mathbf{a}^{s}=\\ddot{\\mathbf{u}}$\n\\end_inset\n\n and we evaluate the fluid acceleration as \n\\begin_inset Formula $\\mathbf{a}^{f}=\\dot{\\mathbf{v}}^{f}+\\grad\\mathbf{v}^{f}\\cdot\\left(\\mathbf{v}^{f}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n.\n However,\n since \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n is not a nodal degree of freedom,\n we use eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-flux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to substitute\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{f}=\\frac{1}{\\varphi^{f}}\\mathbf{w}+\\mathbf{v}^{s}\\label{eq:bfsi-fluid-velocity}\n\\end{equation}\n\n\\end_inset\n\ninto this expression.\n It follows that the fluid velocity gradient \n\\begin_inset Formula $\\mathbf{L}^{f}=\\grad\\mathbf{v}^{f}$\n\\end_inset\n\n may be evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{L}^{f}=\\mathbf{L}^{s}+\\frac{1}{\\varphi^{f}}\\left(\\mathbf{L}^{w}-\\frac{1}{\\varphi^{f}}\\mathbf{w}\\otimes\\grad\\varphi^{f}\\right)\\,,\\label{eq:bfsi-Lf}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{L}^{w}=\\grad\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{L}^{s}=\\grad\\mathbf{v}^{s}$\n\\end_inset\n\n.\n Now,\n the fluid acceleration takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}^{f}=\\frac{1}{\\varphi^{f}}\\left(\\dot{\\mathbf{w}}+\\phi^{f}\\ddot{\\mathbf{u}}-\\frac{\\varphi^{s}}{\\varphi^{f}}\\frac{\\dot{J}^{s}}{J^{s}}\\mathbf{w}+\\mathbf{L}^{f}\\cdot\\mathbf{w}\\right)\\,.\\label{eq:bfsi-af}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nBFSI Continuous Variables\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BFSI-Continuous-Variables\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nJump conditions on the axioms of mass,\n momentum and energy balance are needed to determine which variables may be selected as nodal DOFs in the finite element implementation,\n and which tractions are naturally continuous across an interface.\n The full set of jump conditions for a hybrid biphasic material were derived in our recent study for the constitutive assumptions adopted in this formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Here,\n we summarize the salient results,\n which apply to an interface defined on the porous solid matrix of the hybrid biphasic domain,\n which includes the shared faces of adjoining biphasic elements.\n Thus,\n the velocity of the interface is given by the velocity \n\\begin_inset Formula $\\mathbf{v}^{s}$\n\\end_inset\n\n of the solid constituent of the hybrid biphasic material.\n We employ the notation \n\\begin_inset Formula $\\left[\\left[f\\right]\\right]=f_{+}-f_{-}$\n\\end_inset\n\n to denote the jump in the function \n\\begin_inset Formula $f$\n\\end_inset\n\n across the interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $f_{+}$\n\\end_inset\n\n and \n\\begin_inset Formula $f_{-}$\n\\end_inset\n\n denoting the values of \n\\begin_inset Formula $f$\n\\end_inset\n\n on either side of \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n The unit normal on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n is \n\\series bold\n\n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n\n\\series default\n,\n which points away from the \n\\begin_inset Formula $+$\n\\end_inset\n\n side.\n A variable \n\\begin_inset Formula $f$\n\\end_inset\n\n which is continuous across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $\\left[\\left[f\\right]\\right]=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nBased on the jump condition on the axiom of mass balance,\n the normal component of the mass flux of the fluid relative to the solid is continuous across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\left[\\left[\\rho_{T}^{f}\\mathbf{w}\\right]\\right]\\cdot\\mathbf{n}=0$\n\\end_inset\n\n.\n Furthermore,\n a sufficient condition to satisfy the jump on the axiom of energy balance is to enforce continuity of the fluid specific free enthalpy (also known as the Gibbs function),\n \n\\begin_inset Formula $\\left[\\left[\\psi^{f}+p/\\rho_{T}^{f}\\right]\\right]=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\psi^{f}$\n\\end_inset\n\n is the fluid specific free energy.\n This jump condition applies only when there is fluid on both sides of the interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n In an isothermal framework the specific free enthalpy is a function of state that only depends on \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n,\n therefore this energy jump condition implies that \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n must be continuous across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n thus\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[J^{f}\\right]\\right]=0\\,,\\label{eq:bfsi-Jf-jump}\n\\end{equation}\n\n\\end_inset\n\nalso implying that \n\\begin_inset Formula $\\left[\\left[p\\right]\\right]=0$\n\\end_inset\n\n.\n Given eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-Jf\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\left[\\left[\\rho_{T}^{f}\\right]\\right]=0$\n\\end_inset\n\n and the mass balance jump condition reduces to \n\\begin_inset Formula $\\left[\\left[\\mathbf{w}\\right]\\right]\\cdot\\mathbf{n}=0$\n\\end_inset\n\n,\n implying that the relative fluid flux component normal to \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n must be continuous.\n For the tangential component of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n we appeal to the analysis of Hou et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hou89\"\nliteral \"false\"\n\n\\end_inset\n\n,\n who showed that a valid pseudo-noslip condition requires this tangential component to be continuous.\n Combining these two jump conditions produces\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\mathbf{w}\\right]\\right]=0\\,.\\label{eq:bfsi-fluid-flux-jump}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe momentum jump condition requires that the mixture traction be continuous across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\left[\\left[-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}+\\boldsymbol{\\tau}^{f}\\right]\\right]\\cdot\\mathbf{n}=\\mathbf{0}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\left[\\left[p\\right]\\right]=0$\n\\end_inset\n\n based on the energy jump,\n this mixture momentum jump condition reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\boldsymbol{\\sigma}^{e}+\\boldsymbol{\\tau}^{f}\\right]\\right]\\cdot\\mathbf{n}=0\\,.\\label{eq:bfsi-mix-mtm-jump}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n another relation which is sufficient to satisfy the jump condition on the energy balance is the continuity of the true fluid traction (force acting on fluid per fluid area),\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\frac{\\boldsymbol{\\tau}^{f}}{\\varphi^{f}}\\right]\\right]\\cdot\\mathbf{n}=0\\,.\\label{eq:bfsi-fluid-mtm-jump}\n\\end{equation}\n\n\\end_inset\n\nThis jump condition eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n which also applies only if fluid is present on both sides of \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n is interesting because it implies that the viscous stress (and thus,\n the viscosity) of a fluid flowing in a porous solid matrix scales with the porosity of that medium,\n such that \n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}=\\phi^{f}\\boldsymbol{\\tau}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n would be the true fluid viscous stress.\n Thus,\n we can use FEBio's various constitutive relations for the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n of Newtonian or non-Newtonian fluids and adapt those models to a biphasic mixture where \n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}$\n\\end_inset\n\n is evaluated as \n\\begin_inset Formula $\\varphi^{f}\\boldsymbol{\\tau}$\n\\end_inset\n\n.\n Accordingly,\n the contribution of \n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}$\n\\end_inset\n\n would properly reduce to zero in the limit as fluid content reduces to zero (\n\\begin_inset Formula $\\varphi^{f}\\to0$\n\\end_inset\n\n),\n in which case the mixture momentum jump in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-mix-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n would reduce to \n\\begin_inset Formula $\\left[\\left[-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}\\right]\\right]\\cdot\\mathbf{n}=\\left[\\left[\\boldsymbol{\\sigma}^{e}\\right]\\right]\\cdot\\mathbf{n}=0$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $\\left[\\left[p\\right]\\right]=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nLetting \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n be nodal DOFs automatically enforces the jump conditions eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-Jf-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-flux-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n acting as essential boundary conditions,\n along with the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n.\n Finally,\n subtracting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-mix-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we obtain the momentum jump condition for the solid constituent,\n \n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[-\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}^{f}+\\boldsymbol{\\sigma}^{e}\\right]\\right]\\cdot\\mathbf{n}=0\\,.\\label{eq:bfsi-solid-mtm-jump}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA BFSI domain may be reduced to a FSI domain by letting \n\\begin_inset Formula $\\varphi_{r}^{s}\\to0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{k}^{-1}\\to\\mathbf{0}$\n\\end_inset\n\n;\n the solid matrix would still be ascribed a material response but its stiffness would need to be negligible.\n The number of nodal DOFs would remain the same.\n The CFD domain is a special case of the FSI domain where the mesh displacement is uniformly \n\\begin_inset Formula $\\mathbf{u}=\\mathbf{0}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nHowever,\n when the biphasic domain interfaces with a non-porous solid domain across \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n,\n the jump conditions eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-Jf-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n on \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-solid-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n on \n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}$\n\\end_inset\n\n don't apply.\n In that case,\n the jump condition eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-flux-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n on the relative fluid volumetric flux should reduce to \n\\begin_inset Formula $\\mathbf{w}=\\mathbf{0}$\n\\end_inset\n\n for the BFSI domain on \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n,\n although the user would have to enforce this no-slip condition explicitly by prescribing it as an essential boundary condition.\n The mixture momentum jump \n\\begin_inset Formula $\\left[\\left[-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}+\\boldsymbol{\\tau}^{f}\\right]\\right]\\cdot\\mathbf{n}=\\mathbf{0}$\n\\end_inset\n\n implies that the mixture traction on the BFSI side of an interface with a solid is equal to the solid traction on the solid side.\n This jump condition would also need to be enforced explicitly.\n\\end_layout\n\n\\begin_layout Section\nFluid-Solutes Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Fluid-Solutes-Analyses\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFluid-solutes analyses combine solute transport with fluid mechanics.\n The fluid-solutes domain,\n denoted by \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n,\n is a hybrid multiphasic domain without a deformable porous solid matrix \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Like the CFD domain,\n the CFD-solutes domain is fixed in space,\n and the fluid flows within it and possibly across its boundaries.\n The boundaries \n\\begin_inset Formula $\\partial\\Omega^{f}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n,\n and interfaces within this domain (such as faces of adjoining finite elements),\n are denoted generically by \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n In the current implementation,\n finite element domains that meet at these interfaces share common nodes and element faces,\n though it is possible to consider future extensions where tied interfaces are used to connect dissimilar meshes.\n The formulation and finite element implementation of this type of domain are presented below,\n using only salient governing equations simplified from the more general hybrid multiphasic theory presented in our recent study \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nGoverning Equations\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Solutes-Equations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe model the domain \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n as a mixture of an isothermal,\n compressible,\n and viscous solvent which is electrically neutral,\n and isothermal,\n intrinsically incompressible,\n and dilute solute constituents which may be electrically charged.\n All solute accelerations are neglected,\n since they contribute insignificantly in comparison with diffusive forces,\n except in problems such as centrifugation,\n where they can be represented by an external inertial body force.\n Unlike the standard (Darcy flow through a porous-deformable) multiphasic domain \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07b,Ateshian13,Ateshian14\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the fluid-solutes domain is fixed in space.\n We denote the mixture constituents using a superscripted \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\alpha=f,\\iota$\n\\end_inset\n\n for solvent and solutes,\n respectively.\n The volume fraction \n\\begin_inset Formula $\\varphi^{\\iota}$\n\\end_inset\n\n of each solute species \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n is assumed to be negligible (\n\\begin_inset Formula $\\varphi^{\\iota}\\ll1$\n\\end_inset\n\n),\n therefore the volume fraction of the solvent is \n\\begin_inset Formula $\\varphi^{f}=dV^{f}/dV\\approx1$\n\\end_inset\n\n.\n The apparent density of each constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n is \n\\begin_inset Formula $\\rho^{\\alpha}=dm^{\\alpha}/dV$\n\\end_inset\n\n,\n which represents the ratio of the elemental mass \n\\begin_inset Formula $dm^{\\alpha}$\n\\end_inset\n\n of constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the elemental fluid mixture volume.\n Under these assumptions,\n the true density of the solvent simplifies to \n\\begin_inset Formula $\\rho_{T}^{f}=\\rho^{f}$\n\\end_inset\n\n while the true density \n\\begin_inset Formula $\\rho_{T}^{\\iota}$\n\\end_inset\n\n of each solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n is constant due to its intrinsic incompressibility.\n Since the solvent is compressible,\n as was done in the fluid mechanics formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"false\"\n\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Fluid-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n the solvent volume ratio \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n was employed as a kinematic state variable to represent the fluid volumetric strain,\n\\begin_inset Formula \n\\begin{equation}\nJ^{f}=\\frac{\\rho_{r}^{f}}{\\rho^{f}}\\thinspace,\\label{eq:Fluid-Dil-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}^{f}$\n\\end_inset\n\n is the referential solvent density (\n\\begin_inset Formula $\\rho^{f}$\n\\end_inset\n\n in the reference configuration),\n which is a user-specified material constant.\n An alternative representation of fluid volumetric strain is the fluid dilatation,\n \n\\begin_inset Formula $e^{f}=J^{f}-1$\n\\end_inset\n\n,\n which is used in the actual finite element code.\n\\end_layout\n\n\\begin_layout Standard\nThe axiom of mass balance for the mixture takes the form \n\\begin_inset Formula \n\\begin{equation}\n\\frac{D^{f}J^{f}}{Dt}=J^{f}\\left(\\divg\\mathbf{v}^{f}-\\sum_{\\iota}\\frac{\\hat{\\rho}^{\\iota}}{\\rho_{T}^{\\iota}}\\right)\\thinspace,\\label{eq:Mix-Mass-Bal-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n is the velocity of the fluid constituent and \n\\begin_inset Formula $\\hat{\\rho}^{\\iota}$\n\\end_inset\n\n is the mass density supply to \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n due to reactions with all other solutes.\n It is assumed that reactions do not add or remove solvent mass (\n\\begin_inset Formula $\\hat{\\rho}^{f}=0$\n\\end_inset\n\n).\n Here,\n \n\\begin_inset Formula $D^{f}\\left(\\cdot\\right)/Dt=\\partial\\left(\\cdot\\right)/\\partial t+\\grad\\left(\\cdot\\right)\\cdot\\mathbf{v}^{f}$\n\\end_inset\n\n is the material time derivative operator in the spatial frame,\n following the fluid motion.\n The mixture mass balance reproduces the kinematic constraint between the fluid velocity and dilatation for the fluid mechanics formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"false\"\n\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Fluid-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n with the addition of reactive mass supply terms.\n The axiom of mass balance for each solute takes the form \n\\begin_inset Formula \n\\begin{equation}\n\\hat{c}^{\\iota}=\\frac{\\partial c^{\\iota}}{\\partial t}+\\divg\\left(c^{\\iota}\\mathbf{v}^{\\iota}\\right)=\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}+\\divg\\mathbf{j}^{\\iota}\\thinspace,\\label{eq:Mass-Bal-Solute-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{c}^{\\iota}=\\hat{\\rho}^{\\iota}/M^{\\iota}$\n\\end_inset\n\n is the molar concentration supply and \n\\begin_inset Formula $c^{\\iota}=\\rho^{\\iota}/M^{\\iota}$\n\\end_inset\n\n is the molar concentration of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{j}^{\\iota}=c^{\\iota}\\left(\\mathbf{v}^{\\iota}-\\mathbf{v}^{f}\\right)$\n\\end_inset\n\n is the molar flux of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n relative to the solvent.\n As shown previously \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22a\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the molar solute flux may be evaluated from the solute momentum balance when inertia terms have been neglected,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{j}^{\\iota}=\\tilde{\\kappa}^{\\iota}d_{0}^{\\iota}\\left(-\\grad\\tilde{c}^{\\iota}+\\frac{M^{\\iota}}{R\\theta}\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}\\right)\\thinspace,\\label{eq:Solute-Flux-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{\\kappa}^{\\iota}$\n\\end_inset\n\n is the partition coefficient of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n relative to an ideal solution \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ogston61,Laurent63\"\nliteral \"false\"\n\n\\end_inset\n\n.\n It is given by \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\kappa}^{\\iota}=\\frac{\\kappa^{\\iota}}{\\gamma^{\\iota}}\\exp\\left(-\\frac{z^{\\iota}F_{c}\\psi}{R\\theta}\\right)\\thinspace,\\label{eq:Partition-Coeff-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\kappa^{\\iota}$\n\\end_inset\n\n is the solubility of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n in the mixture,\n or the fraction of fluid volume accessible to the solute \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mauck03\"\nliteral \"false\"\n\n\\end_inset\n\n (\n\\begin_inset Formula $\\kappa^{\\iota}=1$\n\\end_inset\n\n in a fluid mixture),\n and \n\\begin_inset Formula $\\gamma^{\\iota}$\n\\end_inset\n\n is the activity coefficient of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n.\n The ratio \n\\begin_inset Formula $\\kappa^{\\iota}/\\gamma^{\\iota}$\n\\end_inset\n\n is a non-dimensional property that describes the deviation of the solute chemical potential from that of an ideal,\n dilute solution \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Tinoco-Jr.95\"\nliteral \"false\"\n\n\\end_inset\n\n.\n We can represent this ratio as \n\\begin_inset Formula $\\hat{\\kappa}^{\\iota}\\equiv\\kappa^{\\iota}/\\gamma^{\\iota}$\n\\end_inset\n\n,\n and call it the effective solubility of \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n in the solution \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Ateshian2011\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Partition-Coeff-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $z^{\\iota}$\n\\end_inset\n\n is the charge number of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n \n\\begin_inset Formula $F_{c}$\n\\end_inset\n\n is Faraday's constant,\n \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is the electrical potential,\n \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant,\n and \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the absolute temperature.\n In \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Solute-Flux-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $d_{0}^{\\iota}$\n\\end_inset\n\n is the diffusivity of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n in the fluid,\n \n\\begin_inset Formula $\\mathbf{b}^{\\iota}$\n\\end_inset\n\n is the body force per mass acting on solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n is the effective concentration of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{c}^{\\iota}=c^{\\iota}/\\tilde{\\kappa}^{\\iota}\\,.\\label{eq:effective-actual-concentration}\n\\end{equation}\n\n\\end_inset\n\nThe effective solute concentration represents an alternative form of the solute electrochemical potential,\n expressed in units of molar concentration \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07b,Ateshian13\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The solute flux in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Solute-Flux-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n has contributions from diffusion (as represented by the term containing \n\\begin_inset Formula $\\grad\\tilde{c}^{\\iota}$\n\\end_inset\n\n),\n sedimentation (\n\\begin_inset Formula $\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}$\n\\end_inset\n\n when \n\\begin_inset Formula $\\mathbf{b}^{\\iota}$\n\\end_inset\n\n is proportional to an inertial force),\n electrophoresis (\n\\begin_inset Formula $\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}$\n\\end_inset\n\n when \n\\begin_inset Formula $\\mathbf{b}^{\\iota}$\n\\end_inset\n\n is proportional to the Lorentz force acting on charged solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n),\n and convection (\n\\begin_inset Formula $\\tilde{c}^{\\iota}\\mathbf{v}^{f}$\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe solute flux in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Solute-Flux-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may also be expressed in terms of its components,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{j}^{\\iota}=\\underbrace{-\\tilde{\\kappa}^{\\iota}d_{0}^{\\iota}\\grad\\tilde{c}^{\\iota}}_{\\mathbf{j}_{d}^{\\iota}}+\\underbrace{s^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}}_{\\mathbf{j}_{b}^{\\iota}}\\label{eq:solute-flux-components}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\ns^{\\iota}\\equiv d_{0}^{\\iota}\\frac{M^{\\iota}}{R\\theta}\\label{eq:sedimentation-coeff}\n\\end{equation}\n\n\\end_inset\n\nis the \n\\emph on\nsedimentation coefficient\n\\emph default\n.\n In the expression of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:solute-flux-components\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset Formula $\\mathbf{j}_{d}^{\\iota}$\n\\end_inset\n\n represents the diffusive flux,\n and \n\\begin_inset Formula $\\mathbf{j}_{b}^{\\iota}$\n\\end_inset\n\n is the sedimentation flux.\n Now the mass balance expression in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Bal-Solute-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be combined with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mix-Mass-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to produce\n\\begin_inset Formula \n\\begin{equation}\n\\hat{c}^{\\iota}-c^{\\iota}\\sum_{\\gamma}\\mathcal{V}^{\\gamma}\\hat{c}^{\\gamma}=\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}+\\divg\\left(\\mathbf{j}_{d}^{\\iota}+\\mathbf{j}_{b}^{\\iota}\\right)\\label{eq:solute-mass-alt}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathcal{V}^{\\iota}\\equiv\\nicefrac{M^{\\iota}}{\\rho_{T}^{\\iota}}$\n\\end_inset\n\n is the molar volume of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n.\n Since we have neglected the volume fraction of solutes in the current mixture formulation,\n it follows that \n\\begin_inset Formula $c^{\\iota}\\mathcal{V}^{\\iota}\\ll1$\n\\end_inset\n\n.\n Thus,\n we may neglect the second term on the left-hand side of the above equation,\n as well as the related term on the right-hand side of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mix-Mass-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe fluid momentum balance is given by\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{f}\\mathbf{a}^{f}=-\\grad\\tilde{p}+\\divg\\boldsymbol{\\tau}+\\rho^{f}\\mathbf{b}^{f}+\\sum_{\\iota}\\left(-R\\theta\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}+M^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}\\right)\\thinspace,\\label{eq:Fluid-Mtm-Bal-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{a}^{f}=D^{f}\\mathbf{v}^{f}/Dt$\n\\end_inset\n\n is the fluid acceleration,\n \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the fluid viscous stress,\n \n\\begin_inset Formula $\\mathbf{b}^{f}$\n\\end_inset\n\n is the body force per mass acting on the fluid \n\\begin_inset Formula $f$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is the effective fluid pressure,\n related to the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\begin{equation}\np=\\tilde{p}+R\\theta\\Phi\\sum_{\\iota}c^{\\iota}\\thinspace.\\label{eq:Total-fluid-p-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is the osmotic coefficient,\n a non-dimensional property that describes the deviation of the osmotic pressure from the ideal behavior known as van't Hoff's law \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"McNaught97\"\nliteral \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset Formula $R\\theta\\Phi\\sum_{\\iota}c^{\\iota}$\n\\end_inset\n\n is the osmotic or chemical contribution to the fluid pressure.\n Therefore,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n,\n which is an alternative form of the solvent mechano-chemical potential,\n may be interpreted as the mechanical or hydraulic contribution to \n\\begin_inset Formula $p$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nPreviously,\n we showed that the effective pressure must only depend on the fluid volume ratio,\n such that \n\\begin_inset Formula $\\tilde{p}=\\tilde{p}\\left(J^{f}\\right)$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Based on mass,\n momentum and energy jump conditions for this type of mixture,\n it can also be shown that \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n (or alternatively \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n) is continuous across interfaces \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n and thus may be used as a nodal degree of freedom (DOF).\n We may choose a constitutive relation valid for arbitrary (but strictly positive) values of \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{p}=K\\left(\\frac{1}{J^{f}}-1\\right)\\,,\\label{eq:Eff-Pressure-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K$\n\\end_inset\n\n is the solvent's bulk modulus (a user-specified material property).\n\\end_layout\n\n\\begin_layout Standard\nThe solute-dependent terms appearing on the right side of the solvent momentum balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Mtm-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n are typically neglected in classical CFD-solutes formulations,\n but we included them here since they emerged naturally to describe the phenomena of osmosis (solvent flow due to solute diffusion or sedimentation),\n and electro-osmosis (solvent flow due to solute electrophoresis).\n To reproduce classical formulations,\n by default we turned off these terms (within the summation on the right-hand side of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Mtm-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n and we added a user-defined flag (\n\\begin_inset Quotes eld\n\\end_inset\n\ninclude osmosis\n\\begin_inset Quotes erd\n\\end_inset\n\n) to optionally turn them back on.\n In addition,\n users can turn off osmotic pressure by setting the osmotic coefficient \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n to zero,\n in which case \n\\begin_inset Formula $p=\\tilde{p}$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Total-fluid-p-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Both of these approaches were investigated in the test problems described below.\n\\end_layout\n\n\\begin_layout Standard\nAs done in the standard multiphasic formulation (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n we assumed there can be no electric charge accumulation in the mixture,\n so we enforce the electroneutrality condition\n\\begin_inset Formula \n\\begin{equation}\n\\sum_{\\iota}z^{\\iota}c^{\\iota}=0\\,.\\label{eq:Electroneutrality-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nMultiplying the mass balances from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Bal-Solute-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by \n\\begin_inset Formula $z^{\\iota}$\n\\end_inset\n\n and taking the sum over all the constituents,\n making use of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Electroneutrality-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and adopting the assumption that reactive processes maintain electroneutrality (\n\\begin_inset Formula $\\sum_{\\iota}z^{\\iota}\\nu^{\\iota}=0$\n\\end_inset\n\n where \n\\begin_inset Formula $\\nu^{\\alpha}$\n\\end_inset\n\n is the net stoichiometric coefficient of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian14\"\nliteral \"false\"\n\n\\end_inset\n\n) produces the constraint\n\\begin_inset Formula \n\\begin{equation}\n0=\\divg\\sum_{\\iota}z^{\\iota}\\mathbf{j}^{\\iota}\\,,\\label{eq:Current-Density-Constraint}\n\\end{equation}\n\n\\end_inset\n\nor equivalently \n\\begin_inset Formula $\\divg\\mathbf{I}_{e}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{I}_{e}=F_{c}\\sum_{\\iota}z^{\\iota}\\mathbf{j}^{\\iota}$\n\\end_inset\n\n is the electric current density.\n\\end_layout\n\n\\begin_layout Standard\nIn the CFD-solutes implementation the fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n and effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n are the nodal degrees of freedom \n\\begin_inset Formula $\\left(\\mathbf{v}^{f},e^{f},\\tilde{c}^{\\iota}\\right)$\n\\end_inset\n\n,\n as further addressed in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CFD-Solutes-Jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n These choices imply that \n\\begin_inset Formula $\\mathbf{\\mathbf{v}}^{f}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n are continuous across finite element boundaries,\n as justified below when we review jump conditions across interfaces.\n Note that in the absence of solutes,\n the governing equations \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mix-Mass-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Mtm-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n simplify to the original CFD equations,\n with \n\\begin_inset Formula $p=\\tilde{p}$\n\\end_inset\n\n.\n More information on calculating the supply terms \n\\begin_inset Formula $\\hat{c}^{\\iota}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\hat{\\rho}^{\\iota}$\n\\end_inset\n\n can be found in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Chemical-Reactions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The electric potential \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n can be determined as described in the previous standard multiphasic solver \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian13,Ateshian14\"\nliteral \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nJump Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Solutes-Jump\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nJump conditions on the axioms of mass,\n momentum and energy balance are needed to determine which variables may be selected as nodal degrees of freedom in the finite element implementation,\n and which tractions are naturally continuous across an interface.\n The jump conditions for the CFD-solutes material were determined from the jump conditions of the general hybrid multiphasic material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Here,\n we summarize the relevant results,\n which apply to any interface \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n Here,\n since the domain is fixed in space,\n the interface \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n is stationary.\n We employ the notation \n\\begin_inset Formula $\\left[\\left[f\\right]\\right]=f_{+}-f_{-}$\n\\end_inset\n\n to denote the jump in an arbitrary function \n\\begin_inset Formula $f$\n\\end_inset\n\n across the interface \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $f_{+}$\n\\end_inset\n\n and \n\\begin_inset Formula $f_{-}$\n\\end_inset\n\n denoting the values of \n\\begin_inset Formula $f$\n\\end_inset\n\n on either side of \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n The unit normal on \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n is \n\\series bold\n\n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n\n\\series default\n,\n which points away from the \n\\begin_inset Formula $+$\n\\end_inset\n\n side.\n A variable \n\\begin_inset Formula $f$\n\\end_inset\n\n which is continuous across \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $\\left[\\left[f\\right]\\right]=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nBased on the jump condition on the axiom of mass balance for the fluid,\n the normal component of the mass flux of the fluid is continuous across \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\left[\\left[\\rho^{f}\\mathbf{v}^{f}\\right]\\right]\\cdot\\mathbf{n}=0$\n\\end_inset\n\n.\n Furthermore,\n a sufficient condition to satisfy the jump on the axiom of energy balance is to enforce continuity of the solvent specific free enthalpy (or Gibbs function),\n such that \n\\begin_inset Formula $\\left[\\left[\\psi^{f}+p/\\rho_{T}^{f}\\right]\\right]=\\left[\\left[\\mu^{f}\\right]\\right]=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\psi^{f}$\n\\end_inset\n\n is the fluid specific free energy.\n In the multiphasic mixture literature,\n the solvent specific free enthalpy \n\\begin_inset Formula $\\mu^{f}$\n\\end_inset\n\n is also called the mechano-chemical potential \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian16\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This jump condition applies only when there is solvent on both sides of the interface,\n such as across \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n In an isothermal framework,\n the solvent mechano-chemical potential is a function of state that only depends on \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n such that \n\\begin_inset Formula $\\mu^{f}=\\mu^{f}\\left(J^{f}\\right)$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim22a\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Therefore this energy jump condition implies that \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n must be continuous across \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n thus\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[J^{f}\\right]\\right]=0\\,,\\label{eq:J-Jump-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nalso implying that \n\\begin_inset Formula $\\left[\\left[\\tilde{p}\\right]\\right]=0$\n\\end_inset\n\n.\n Given \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Dil-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $\\left[\\left[\\rho^{f}\\right]\\right]=0$\n\\end_inset\n\n and the fluid mass balance jump condition reduces to \n\\begin_inset Formula $\\left[\\left[\\mathbf{v}^{f}\\right]\\right]\\cdot\\mathbf{n}=0$\n\\end_inset\n\n,\n implying that the fluid velocity component normal to \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n must be continuous.\n For the tangential component of \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n we use the no-slip condition for viscous fluids,\n which states that the velocity component tangential to \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n must be continuous across that interface,\n as is standard in CFD solvers.\n Combining these two jump conditions produces\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\mathbf{v}^{f}\\right]\\right]=0\\,.\\label{eq:Mass-Jump-Final-CFDSol}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFrom the solute mass jump condition,\n we can show that the normal component of the molar solute flux is continuous across \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\mathbf{j}^{\\iota}\\right]\\right]\\cdot\\mathbf{n}=0\\,.\\label{eq:Mass-Jump-Sol-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nThe momentum jump condition requires that the mixture traction be continuous across \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\left[\\left[-p\\mathbf{I}+\\boldsymbol{\\tau}\\right]\\right]\\cdot\\mathbf{n}=\\mathbf{0}$\n\\end_inset\n\n.\n Another relation which is thermodynamically sufficient to satisfy the jump condition on the energy balance is the continuity of the viscous fluid traction,\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\boldsymbol{\\tau}\\right]\\right]\\cdot\\mathbf{n}=0\\,.\\label{eq:Fluid-Mtm-Jump-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nThis jump condition \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Mtm-Jump-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n which also applies only if fluid is present on both sides of \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n implies that \n\\begin_inset Formula $\\left[\\left[p\\right]\\right]=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n the last relation that is thermodynamically sufficient to satisfy the energy balance jump condition applies to the electrochemical potential for the solutes,\n where \n\\begin_inset Formula $\\left[\\left[\\tilde{\\mu}^{\\iota}\\right]\\right]=0$\n\\end_inset\n\n.\n For a solute,\n the general constitutive relation for the electrochemical potential is \n\\begin_inset Formula $\\tilde{\\mu}^{\\iota}=\\frac{R\\theta}{M^{\\iota}}\\ln\\frac{c^{\\iota}}{\\tilde{\\kappa}^{\\alpha}c_{0}^{\\iota}}$\n\\end_inset\n\n,\n and the jump can be simplified to \n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\tilde{c}^{\\iota}\\right]\\right]=0\\,.\\label{eq:Conc-Jump-CFDSol}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThus,\n letting \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n be nodal degrees of freedom automatically enforces the jump conditions \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:J-Jump-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Jump-Final-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Conc-Jump-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n acting as essential boundary conditions.\n As stated earlier,\n the jump conditions are the same as the previous CFD formulation in the case of zero solute concentration (all \n\\begin_inset Formula $\\tilde{c}^{\\iota}=0$\n\\end_inset\n\n) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Chapter\nThe Nonlinear FE Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Nonlinear-FE-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis chapter discusses the basic principles of the nonlinear finite element method.\n The chapter begins with a short introduction to the weak formulation and the principle of virtual work.\n Next,\n the important concept of linearization is discussed and applied to the principle of virtual work.\n Finally the Newton-Raphson procedure and its application to the nonlinear finite element method are described.\n\\end_layout\n\n\\begin_layout Section\nWeak formulation for Solid Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Solid-weak-formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nGenerally,\n the finite element formulation is established in terms of a weak form of the differential equations under consideration.\n In the context of solid mechanics this implies the use of the virtual work equation:\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\int\\limits_{v}\\boldsymbol{\\sigma}:\\delta\\mathbf{d}\\,dv-\\int\\limits_{v}\\mathbf{f}\\cdot\\delta\\mathbf{v}\\,dv-\\int\\limits_{\\partial v}\\mathbf{t}\\cdot\\delta\\mathbf{v}\\,da=0\\,.\\label{eq171}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n is a virtual velocity and \n\\begin_inset Formula $\\delta\\mathbf{d}$\n\\end_inset\n\n is the virtual rate of deformation tensor.\n This equation is known as the \n\\emph on\nspatial virtual work equation \n\\emph default\nsince it is formulated using spatial quantities only.\n We can also define the \n\\emph on\nmaterial virtual work equation \n\\emph default\nby expressing the principle of virtual work using only material quantities.\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\int\\limits_{V}\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\,dV-\\int\\limits_{V}\\mathbf{f}_{0}\\cdot\\delta\\mathbf{v}\\,dV-\\int\\limits_{\\partial V}\\mathbf{t}_{0}\\cdot\\delta\\mathbf{v}\\,dA=0\\,.\\label{eq172}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{f}_{0}=J\\mathbf{f}$\n\\end_inset\n\n is the body force per unit undeformed volume and \n\\begin_inset Formula $\\mathbf{t}_{0}=\\mathbf{t}\\left(da/dA\\right)$\n\\end_inset\n\n is the traction vector per unit initial area.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solid-linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEquation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq171\"\nnolink \"false\"\n\n\\end_inset\n\n is the starting point for the nonlinear finite element method.\n It is highly nonlinear and any method attempting to solve this equation,\n such as the Newton-Raphson method,\n necessarily has to be iterative.\n\\end_layout\n\n\\begin_layout Standard\nTo linearize the finite element equations,\n the directional derivative of the virtual work in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq171\"\nnolink \"false\"\n\n\\end_inset\n\n must be calculated.\n In an iterative procedure,\n the quantity \n\\begin_inset Formula $\\boldsymbol{\\phi}$\n\\end_inset\n\n will be approximated by a trial solution \n\\begin_inset Formula $\\boldsymbol{\\phi}_{k}$\n\\end_inset\n\n.\n Linearization of the virtual work equation around this trial solution gives \n\\begin_inset Formula \n\\begin{equation}\n\\delta W\\left(\\boldsymbol{\\phi}_{k},\\delta\\mathbf{v}\\right)+D\\delta W\\left(\\boldsymbol{\\phi}_{k},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]=0\\,.\\label{eq173}\n\\end{equation}\n\n\\end_inset\n\nThe directional derivative of the virtual work will eventually lead to the definition of the stiffness matrix.\n In order to proceed,\n it is convenient to split the virtual work into an internal and external virtual work component:\n \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]=D\\delta W_{int}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]-D\\delta W_{ext}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right],\\label{eq174}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{int}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)=\\int\\limits_{v}\\boldsymbol{\\sigma}:\\delta\\mathbf{d}\\,dv\\,,\\label{eq175}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{ext}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)=\\int\\limits_{v}\\mathbf{f}\\cdot\\delta\\mathbf{v}\\,dv+\\int\\limits_{\\partial v}\\mathbf{t}\\cdot\\delta\\mathbf{v}\\,da\\,.\\label{eq176}\n\\end{equation}\n\n\\end_inset\n\nThe result is listed here without details of the derivation – see \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n for details.\n The linearization of the internal virtual work is given by \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{int}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]=\\int\\limits_{v}\\delta\\mathbf{d}:\\boldsymbol{\\mathcal{C}}:\\boldsymbol{\\varepsilon}\\,dv+\\int\\limits_{v}\\boldsymbol{\\sigma}:\\left[\\left(\\nabla\\mathbf{u}\\right)^{T}\\cdot\\nabla\\delta\\mathbf{v}\\right]\\,dv\\,.\\label{eq177}\n\\end{equation}\n\n\\end_inset\n\nNotice that this equation is symmetric in \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n.\n This symmetry will,\n upon discretization,\n yield a symmetric tangent matrix.\n\\end_layout\n\n\\begin_layout Standard\nThe external virtual work has contributions from both body forces and surface tractions.\n The precise form of the linearized external virtual work depends on the form of these forces.\n For surface tractions,\n normal pressure forces may be represented in FEBio.\n The linearized external work for this type of traction is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{ext}^{p}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right] & =\\frac{1}{2}\\int\\limits_{A_{\\xi}}p\\frac{\\partial\\mathbf{x}}{\\partial\\xi}\\cdot\\left[\\left(\\frac{\\partial\\mathbf{u}}{\\partial\\eta}\\times\\delta\\mathbf{v}\\right)+\\left(\\frac{\\partial\\delta\\mathbf{v}}{\\partial\\eta}\\times\\mathbf{u}\\right)\\right]d\\xi\\,d\\eta\\\\\n & -\\frac{1}{2}\\int\\limits_{A_{\\xi}}p\\frac{\\partial\\mathbf{x}}{\\partial\\eta}\\cdot\\left[\\left(\\frac{\\partial\\mathbf{u}}{\\partial\\xi}\\times\\delta\\mathbf{v}\\right)+\\left(\\frac{\\partial\\delta\\mathbf{v}}{\\partial\\xi}\\times\\mathbf{u}\\right)\\right]d\\xi\\,d\\eta\\,.\n\\end{aligned}\n\\label{eq178}\n\\end{equation}\n\n\\end_inset\n\nDiscretization of this equation will also lead to a symmetric component of the tangent matrix.\n\\end_layout\n\n\\begin_layout Standard\nFEBio currently supports gravity as a body force,\n \n\\begin_inset Formula $\\mathbf{f}=\\rho\\mathbf{g}$\n\\end_inset\n\n.\n Since this force is independent of the geometry,\n the contribution to the linearized external work is zero.\n Another type of body force implemented in FEBio is the centrifugal force.\n For a body rotating with a constant angular speed \n\\begin_inset Formula $\\omega$\n\\end_inset\n\n,\n about an axis passing through the point \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n and directed along the unit vector \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n the body force is given by \n\\begin_inset Formula $\\mathbf{f}=\\rho\\omega^{2}\\mathbf{r}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{r}$\n\\end_inset\n\n is the vector distance from a point \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n to the axis of rotation,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{r}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\left(\\mathbf{x}-\\mathbf{c}\\right)\\,.\\label{eq179}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigCentrifugalBodyForce.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe resulting linearized external work is given by \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{ext}^{f}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]=\\int\\limits_{v}\\rho\\omega^{2}\\delta\\mathbf{v}\\cdot\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{u}\\,dv,\\label{eq180}\n\\end{equation}\n\n\\end_inset\n\nwhich produces a symmetric expression that will yield a symmetric matrix.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solid-discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe basis of the finite element method is that the domain of the problem (that is,\n the volume of the object under consideration) is divided into smaller subunits,\n called \n\\emph on\nfinite elements\n\\emph default\n.\n In the case of \n\\emph on\nisoparametric elements \n\\emph default\nit is further assumed that each element has a local coordinate system,\n named the \n\\emph on\nnatural coordinates\n\\emph default\n,\n and the coordinates and shape of the element are discretized using the same functions.\n The discretization process is established by interpolating the geometry in terms of the coordinates \n\\begin_inset Formula $\\mathbf{X}_{a}$\n\\end_inset\n\n of the \n\\emph on\nnodes\n\\emph default\n that define the geometry of a finite element,\n and the \n\\emph on\nshape functions\n\\emph default\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{X}=\\sum\\limits_{a=1}^{n}N_{a}\\left(\\xi_{1},\\xi_{2},\\xi_{3}\\right)\\mathbf{X}_{a}\\,,\\label{eq181}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $n$\n\\end_inset\n\n is the number of nodes and \n\\begin_inset Formula $\\xi_{i}$\n\\end_inset\n\n are the natural coordinates.\n Similarly,\n the motion is described in terms of the current position \n\\begin_inset Formula $\\mathbf{x}_{a}\\left(t\\right)$\n\\end_inset\n\n of the \n\\emph on\nsame\n\\emph default\n particles:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}\\left(t\\right)=\\sum\\limits_{a=1}^{n}N_{a}\\mathbf{x}_{a}\\left(t\\right)\\,.\\label{eq182}\n\\end{equation}\n\n\\end_inset\n\nQuantities such as displacement,\n velocity and virtual velocity can be discretized in a similar way.\n\\end_layout\n\n\\begin_layout Standard\nIn deriving the discretized equilibrium equations,\n the integrations performed over the entire volume can be written as a sum of integrations constrained to the volume of an element.\n For this reason,\n the discretized equations are defined in terms of integrations over a particular element \n\\begin_inset Formula $e$\n\\end_inset\n\n.\n The discretized equilibrium equations for this particular element per node is given by \n\\begin_inset Formula \n\\begin{equation}\n\\delta W^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},N_{a}\\delta\\mathbf{v}\\right)=\\delta\\mathbf{v}_{a}\\cdot\\left(\\mathbf{T}_{a}^{\\left(e\\right)}-\\mathbf{F}_{a}^{\\left(e\\right)}\\right)\\,,\\label{eq183}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}T_{a}^{\\left(e\\right)} & =\\int\\limits_{v^{\\left(e\\right)}}\\boldsymbol{\\sigma}\\cdot\\nabla N_{a}\\,dv\\,,\\\\\nF_{a}^{\\left(e\\right)} & =\\int\\limits_{v^{\\left(e\\right)}}N_{a}\\mathbf{f}\\,dv+\\int\\limits_{\\partial v^{\\left(e\\right)}}N_{a}\\mathbf{t}\\,da\\,.\n\\end{aligned}\n\\label{eq184}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of the internal virtual work can be split into a \n\\emph on\nmaterial\n\\emph default\n and an \n\\emph on\ninitial stress\n\\emph default\n component \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right] & =\\int\\limits_{v^{\\left(e\\right)}}\\delta\\mathbf{d}:\\boldsymbol{\\mathcal{C}}:\\boldsymbol{\\varepsilon}\\,dv+\\int\\limits_{v^{\\left(e\\right)}}\\boldsymbol{\\sigma}:\\left[\\left(\\nabla\\mathbf{u}\\right)^{T}\\cdot\\nabla\\delta\\mathbf{v}\\right]\\,dv\\\\\n & =D\\delta W_{c}^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]+D\\delta W_{\\sigma}^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]\\,.\n\\end{aligned}\n\\label{eq185}\n\\end{equation}\n\n\\end_inset\n\nThe constitutive component can be discretized as follows:\n \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{c}^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},\\delta\\mathbf{v}\\right)\\left[\\mathbf{u}\\right]=\\delta\\mathbf{v}_{a}\\cdot\\left(\\int\\limits_{v^{\\left(e\\right)}}\\mathbf{B}_{a}^{T}\\mathbf{D}\\mathbf{B}_{b}\\,dv\\right)\\mathbf{u}_{b}\\,.\\label{eq186}\n\\end{equation}\n\n\\end_inset\n\nThe term in parentheses defines the constitutive component of the tangent matrix relating node \n\\begin_inset Formula $a$\n\\end_inset\n\nto node \n\\begin_inset Formula $b$\n\\end_inset\n\nin element \n\\begin_inset Formula $e$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{c,ab}^{\\left(e\\right)}=\\int\\limits_{v^{\\left(e\\right)}}\\mathbf{B}_{a}^{T}\\mathbf{DB}_{b}\\,dv\\,.\\label{eq187}\n\\end{equation}\n\n\\end_inset\n\nHere,\n the linear strain-displacement matrix \n\\series bold\n\n\\begin_inset Formula $\\mathbf{B}$\n\\end_inset\n\n\n\\series default\n relates the displacements to the small-strain tensor in Voigt Notation:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\varepsilon}=\\sum\\limits_{a=1}^{n}\\mathbf{B}_{a}\\mathbf{u}_{a}\\,.\\label{eq188}\n\\end{equation}\n\n\\end_inset\n\nOr,\n written out completely,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{B}_{a}=\\left[\\begin{array}{ccc}\n\\partial N_{a}/\\partial x & 0 & 0\\\\\n0 & \\partial N_{a}/\\partial y & 0\\\\\n0 & 0 & \\partial N_{a}/\\partial z\\\\\n\\partial N_{a}/\\partial y & \\partial N_{a}/\\partial x & 0\\\\\n0 & \\partial N_{a}/\\partial z & \\partial N_{a}/\\partial y\\\\\n\\partial N_{a}/\\partial z & 0 & \\partial N_{a}/\\partial z\n\\end{array}\\right]\\,.\\label{eq189}\n\\end{equation}\n\n\\end_inset\n\nThe spatial constitutive matrix \n\\series bold\n\n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n \n\\series default\nis constructed from the components of the fourth-order tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n using the following table;\n \n\\begin_inset Formula $D_{IJ}=\\mathcal{C}_{ijkl}$\n\\end_inset\n\nwhere\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nI/J\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ni/k\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nj/l\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe initial stress component can be written as follows:\n \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{\\sigma}^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},N_{a}\\delta\\mathbf{v}\\right)\\left[N_{b}\\mathbf{u}_{b}\\right]=\\int\\limits_{v^{\\left(e\\right)}}\\left(\\nabla N_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\nabla N_{b}\\right)\\mathbf{I}\\,dv.\\label{eq190}\n\\end{equation}\n\n\\end_inset\n\nFor the pressure component of the external virtual work,\n we find \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{p}^{\\left(e\\right)}\\left(\\boldsymbol{\\phi},N_{a}\\delta\\mathbf{v}_{a}\\right)\\left[N_{b}\\mathbf{u}_{b}\\right]=\\delta\\mathbf{v}_{a}\\cdot\\mathbf{K}_{p,ab}^{\\left(e\\right)}\\cdot\\mathbf{u}_{b}\\,,\\label{eq191}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{p,ab}^{\\left(e\\right)} & =\\mathcal{E}\\mathbf{k}_{p,ab}^{\\left(e\\right)},\\\\\n\\mathbf{k}_{p,ab}^{\\left(e\\right)} & =\\frac{1}{2}\\int_{A_{\\xi}}p\\frac{\\partial\\mathbf{x}}{\\partial\\xi}\\left(\\frac{\\partial N_{a}}{\\partial\\eta}N_{b}-\\frac{\\partial N_{b}}{\\partial\\eta}N_{a}\\right)d\\xi\\,d\\eta\\\\\n & +\\frac{1}{2}\\int_{A_{\\xi}}p\\frac{\\partial\\mathbf{x}}{\\partial\\eta}\\left(\\frac{\\partial N_{a}}{\\partial\\xi}N_{b}-\\frac{\\partial N_{b}}{\\partial\\xi}N_{a}\\right)d\\xi\\,d\\eta\\,.\n\\end{aligned}\n\\label{eq192}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nWeak formulation for biphasic materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:sec:Biphasic-weak-formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA weak form of the statement conservation of linear momemtum for the quasi-static case is obtained by using Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq103\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq105\"\nnolink \"false\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\int_{b}\\left[\\delta\\mathbf{v}^{s}\\cdot\\left(\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}\\right)+\\delta p\\,\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)\\right]dv=0\\,,\\label{eq193}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $b$\n\\end_inset\n\n is the domain of interest defined on the solid matrix,\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{s}$\n\\end_inset\n\n is a virtual velocity of the solid and \n\\begin_inset Formula $\\delta p$\n\\end_inset\n\n is a virtual pressure of the fluid \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Un06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\begin_inset Formula $dv$\n\\end_inset\n\n is an elemental volume of \n\\begin_inset Formula $b$\n\\end_inset\n\n.\n Using the divergence theorem,\n this expression may be rearranged as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\mathbf{t}\\,da+\\int_{\\partial b}\\delta p\\,w_{n}\\,da+\\int_{b}\\delta\\mathbf{v}^{s}\\cdot\\rho\\mathbf{b}\\,dv\\\\\n & -\\int_{b}\\boldsymbol{\\sigma}:\\grad\\delta\\mathbf{v}^{s}\\,dv-\\int_{b}\\left(\\mathbf{w}\\cdot\\grad\\delta p-\\delta p\\,\\divg\\mathbf{v}^{s}\\right)\\,dv\n\\end{aligned}\n\\,,\\label{eq194}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{d}^{s}=\\left(\\grad\\delta\\mathbf{v}^{s}+\\grad^{T}\\delta\\mathbf{v}^{s}\\right)/2$\n\\end_inset\n\n is the virtual rate of deformation tensor,\n \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the total traction on the surface \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the component of the fluid flux normal to \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n representing the unit outward normal to \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n.\n \n\\begin_inset Formula $da$\n\\end_inset\n\n represents an elemental area of \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n.\n In this type of problem,\n essential boundary conditions are prescribed for \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n and natural boundary conditions are prescribed for \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n.\n In the expression of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq194\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta W\\left(\\boldsymbol{\\chi}^{s},p,\\delta\\mathbf{v}^{s},\\delta p\\right)$\n\\end_inset\n\n represents the virtual work.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSince the system of equations in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq194\"\nnolink \"false\"\n\n\\end_inset\n\n is highly nonlinear,\n its solution requires an iterative scheme such as Newton's method.\n This requires the linearization of \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n at some trial solution \n\\begin_inset Formula $\\left(\\boldsymbol{\\chi}_{k}^{s},p_{k}\\right)$\n\\end_inset\n\n,\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n in \n\\begin_inset Formula $\\boldsymbol{\\chi}^{s}$\n\\end_inset\n\n and an increment \n\\begin_inset Formula $\\Delta p$\n\\end_inset\n\n in \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W+D\\delta W\\left[\\Delta\\mathbf{u}\\right]+D\\delta W\\left[\\Delta p\\right]=0,\\label{eq195}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $Df\\left[\\Delta q\\right]$\n\\end_inset\n\n represents the directional derivative of \n\\begin_inset Formula $f$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta q$\n\\end_inset\n\n.\n For convenience,\n the virtual work may be separated into its internal and external parts,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\delta W_{\\text{ext}}-\\delta W_{\\text{int}},\\label{eq196}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\mbox{int}}=\\int_{b}\\boldsymbol{\\sigma}:\\delta\\mathbf{d}^{s}\\,dv+\\int_{b}\\left(\\mathbf{w}\\cdot\\grad\\delta p-\\delta p\\,\\frac{\\dot{J}}{J}\\right)\\,dv,\\label{eq197}\n\\end{equation}\n\n\\end_inset\n\nwhere we have substituted \n\\begin_inset Formula $\\divg\\mathbf{v}^{s}=\\dot{J}/J$\n\\end_inset\n\n,\n and \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\mathbf{t}\\,da+\\int_{\\partial b}\\delta p\\,w_{n}\\,da+\\int_{b}\\delta\\mathbf{v}^{s}\\cdot\\rho\\mathbf{b}\\,dv\\,.\\label{eq198}\n\\end{equation}\n\n\\end_inset\n\nThe evaluation of the directional derivatives can be performed following a standard approach \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n In particular,\n a backward difference scheme is used to evaluate \n\\begin_inset Formula $\\dot{J}\\approx\\left(J-J^{-\\Delta t}\\right)/\\Delta t$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $J^{-\\Delta t}$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $J$\n\\end_inset\n\n at the previous time step.\n For the internal part of the virtual work,\n the directional derivative along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{\\text{int}}\\left[\\Delta\\mathbf{u}\\right] & =\\int_{b}\\delta\\mathbf{d}^{s}:\\boldsymbol{\\mathcal{C}}:\\Delta\\boldsymbol{\\varepsilon}\\,dv+\\int_{b}\\boldsymbol{\\sigma}:\\left(\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\delta\\mathbf{v}^{s}\\right)\\,dv\\\\\n & -\\int_{b}\\frac{\\delta p}{\\Delta t}\\divg\\Delta\\mathbf{u}\\,dv\\\\\n & -\\int_{b}\\grad\\delta p\\cdot\\left(\\boldsymbol{\\mathcal{K}}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\left(\\grad p-\\rho_{T}^{w}\\mathbf{b}^{w}\\right)\\,dv\\\\\n & +\\int_{b}\\grad\\delta p\\cdot\\mathbf{k}\\cdot\\rho_{T}^{w}\\left(\\grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{b}^{w}+\\grad\\mathbf{b}^{w}\\cdot\\Delta\\mathbf{u}\\right)dv\\,,\n\\end{aligned}\n\\label{eq199}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the fourth-order spatial elasticity tensor for the mixture and \n\\begin_inset Formula $\\Delta\\boldsymbol{\\varepsilon}=\\left(\\grad\\Delta\\mathbf{u}+\\grad^{T}\\Delta\\mathbf{u}\\right)/2$\n\\end_inset\n\n.\n Based on the relation of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq104\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the spatial elasticity tensor may also be expanded as \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\boldsymbol{\\mathcal{C}}^{e}+p\\left(-\\mathbf{I}\\otimes\\mathbf{I}+2\\mathbf{I}\\,\\overline{\\underline{\\otimes}}\\,\\mathbf{I}\\right),\\label{eq200}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{e}$\n\\end_inset\n\n is the spatial elasticity tensor for the solid matrix \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Curnier94\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It is related to the material elasticity tensor \n\\begin_inset Formula $\\boldsymbol{\\mathbb{C}}^{e}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}^{e}=J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):\\boldsymbol{\\mathbb{C}}^{e}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right),\\label{eq201}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient of the solid matrix,\n \n\\begin_inset Formula $\\boldsymbol{\\mathbb{C}}^{e}=\\partial\\mathbf{S}^{e}/\\partial\\mathbf{E}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is the Lagrangian strain tensor and \n\\begin_inset Formula $\\mathbf{S}^{e}$\n\\end_inset\n\n is the second Piola-Kirchhoff stress tensor,\n related to the Cauchy stress tensor via \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}=J^{-1}\\mathbf{F}\\cdot\\mathbf{S}^{e}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{K}}$\n\\end_inset\n\n is a fourth-order tensor that represents the spatial measure of the rate of change of permeability with strain.\n It is related to its material frame equivalent \n\\begin_inset Formula $\\boldsymbol{\\mathbb{K}}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{K}}=J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):\\boldsymbol{\\mathbb{K}}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right),\\label{eq202}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathbb{K}}=\\partial\\mathbf{K}/\\partial\\mathbf{E}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{K}$\n\\end_inset\n\n is the permeability tensor in the material frame,\n such that \n\\begin_inset Formula $\\mathbf{k}=J^{-1}\\mathbf{F}\\cdot\\mathbf{K}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\mathbf{K}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n are symmetric tensors,\n it follows that \n\\begin_inset Formula $\\boldsymbol{\\mathcal{K}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\mathbb{K}}$\n\\end_inset\n\n exhibit two minor symmetries (e.g.,\n \n\\begin_inset Formula $\\mathcal{K}_{jikl}=\\mathcal{K}_{ijkl}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathcal{K}_{ijlk}=\\mathcal{K}_{ijkl})$\n\\end_inset\n\n.\n However,\n unlike the elasticity tensor,\n it is not necessary that these tensors exhibit major symmetry (e.g.,\n \n\\begin_inset Formula $\\mathcal{K}_{klij}\\ne\\mathcal{K}_{ijkl}$\n\\end_inset\n\n in general).\n\\end_layout\n\n\\begin_layout Standard\nThe directional derivative of \n\\begin_inset Formula $\\delta W_{\\mbox{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta p$\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{\\text{int}}\\left[\\Delta p\\right]=-\\int_{b}\\grad\\delta p\\cdot\\mathbf{k}\\cdot\\grad\\Delta p\\,dv-\\int_{b}\\Delta p\\,\\divg\\delta\\mathbf{v}^{s}\\,dv\\,.\\label{eq203}\n\\end{equation}\n\n\\end_inset\n\nNote that letting \n\\begin_inset Formula $p=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta p=0$\n\\end_inset\n\n in the above equations recovers the virtual work relations for nonlinear elasticity of compressible solids.\n The resulting simplified equation emerging from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq199\"\nnolink \"false\"\n\n\\end_inset\n\n is symmetric to interchanges of \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta\\mathbf{v}^{s}$\n\\end_inset\n\n,\n producing a symmetric stiffness matrix in the finite element formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n However,\n the general relations of Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq199\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq203\"\nnolink \"false\"\n\n\\end_inset\n\n do not exhibit symmetry to interchanges of \n\\begin_inset Formula $\\left(\\Delta\\mathbf{u},\\Delta p\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(\\delta\\mathbf{v}^{s},\\delta p\\right)$\n\\end_inset\n\n,\n implying that the finite element stiffness matrix for a solid-fluid mixture is not symmetric under general conditions.\n\\end_layout\n\n\\begin_layout Standard\nThe directional derivatives of the external virtual work \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n depend on the type of boundary conditions being considered.\n For a prescribed total normal traction \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{t}=t_{n}\\mathbf{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}^{t}=\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot t_{n}\\mathbf{n}\\,da,\\label{eq204}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{\\text{ext}}^{t}\\left[\\Delta\\mathbf{u}\\right] & =\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot t_{n}\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\\frac{da}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,,\\\\\nD\\delta W_{\\text{ext}}^{t}\\left[\\Delta p\\right] & =0,\n\\end{aligned}\n\\label{eq205}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}=\\frac{\\partial\\mathbf{x}}{\\partial\\eta^{\\alpha}},\\quad\\alpha=1,2\\label{eq206}\n\\end{equation}\n\n\\end_inset\n\nare covariant basis (tangent) vectors on \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{g}_{1}\\times\\mathbf{g}_{2}}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,.\\label{eq207}\n\\end{equation}\n\n\\end_inset\n\nFor a prescribed normal effective traction \n\\begin_inset Formula $t_{n}^{e}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{t}=\\left(-p+t_{n}^{e}\\right)\\mathbf{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $p$\n\\end_inset\n\n is not prescribed,\n then \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}^{e}=\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\left(-p+t_{n}^{e}\\right)\\mathbf{n}\\,da\\,,\\label{eq208}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{\\text{ext}}^{e}\\left[\\Delta\\mathbf{u}\\right] & =\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\left(-p+t_{n}^{e}\\right)\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\\frac{da}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,,\\\\\nD\\delta W_{\\text{ext}}^{e}\\left[\\Delta p\\right] & =-\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\Delta p\\,\\mathbf{n}\\,da\\,.\n\\end{aligned}\n\\label{eq209}\n\\end{equation}\n\n\\end_inset\n\nFor a prescribed normal fluid flux \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}^{w}=\\int_{\\partial b}\\delta p\\,w_{n}\\,da\\,,\\label{eq210}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{\\text{ext}}^{w}\\left[\\Delta\\mathbf{u}\\right] & =\\int_{\\partial b}\\delta p\\,w_{n}\\,\\mathbf{n}\\cdot\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\\frac{da}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,,\\\\\nD\\delta W_{\\text{ext}}^{w}\\left[\\Delta p\\right] & =0\\,.\n\\end{aligned}\n\\label{eq211}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n for a prescribed external body force,\n recognizing that \n\\begin_inset Formula $\\rho\\mathbf{b}=\\rho^{s}\\mathbf{b}^{s}+\\rho^{w}\\mathbf{b}^{w}$\n\\end_inset\n\n and assuming that the body forces \n\\begin_inset Formula $\\mathbf{b}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{b}^{w}$\n\\end_inset\n\n do not depend on \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{\\text{ext}}^{b}\\right)\\left[\\Delta\\mathbf{u}\\right] & =\\int_{b}\\delta\\mathbf{v}^{s}\\cdot\\left[\\left(\\rho^{s}grad\\mathbf{b}^{s}+\\rho^{w}grad\\mathbf{b}^{w}\\right)\\cdot\\Delta\\mathbf{u}+\\left(\\divg\\Delta\\mathbf{u}\\right)\\rho_{T}^{w}\\mathbf{b}^{w}\\right]\\,dv\\,,\\\\\nD\\left(\\delta W_{\\text{ext}}^{b}\\right)\\left[\\Delta p\\right] & =0\\,.\n\\end{aligned}\n\\label{eq212}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nStabilization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-Stabilization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFinite element models of deformable porous media are known to exhibit oscillations in fluid pressure caused by relatively coarse meshes near free-draining boundaries,\n where a boundary layer in fluid pressure normally forms during the early time response to loading.\n These spurious oscillations typically occur when the mesh is not able to resolve this boundary layer.\n Stabilization methods serve as an alternative to mesh refinement,\n when the latter is not feasible or practical.\n In FEBio we have implemented a stabilization method based on the work of Aguilar et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Aguilar08\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Basically,\n this method proposes that the fluid flux \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}=-\\mathbf{k}\\cdot\\grad\\left(p+\\tau\\dot{p}\\right)\\label{eq:stab-fluid-flux}\n\\end{equation}\n\n\\end_inset\n\ninstead of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq107\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n represents the stabilization parameter.\n A representative value for \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n may be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\tau\\approx\\frac{h^{2}}{E\\cdot k}\\,,\\label{eq:stab-tau}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $h$\n\\end_inset\n\n is the element thickness on the free-draining boundary,\n \n\\begin_inset Formula $E$\n\\end_inset\n\n is a representative measure of the solid matrix elastic modulus and \n\\begin_inset Formula $k$\n\\end_inset\n\n is a representative measure of the hydraulic permeability tensor \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n.\n The contribution of this form of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n to the virtual work expression in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq194\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G & =\\int_{b}\\mathbf{w}\\cdot\\grad\\delta p\\,dv\\\\\n & =-\\int_{b}\\grad\\delta p\\cdot\\mathbf{k}\\cdot\\grad\\left(p+\\tau\\dot{p}\\right)\\,dv\n\\end{aligned}\n\\,.\\label{eq:stab-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nThe linearizations of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n are then given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G\\left[\\Delta\\mathbf{u}\\right] & =-\\int_{b}\\grad\\delta p\\cdot\\left(\\boldsymbol{\\mathcal{K}}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\grad\\left(p+\\tau\\dot{p}\\right)\\,dv\\\\\nD\\delta G\\left[\\Delta p\\right] & =-\\int_{b}\\left(1+\\frac{\\tau}{\\Delta t}\\right)\\grad\\delta p\\cdot\\mathbf{k}\\cdot\\grad\\Delta p\\,dv\n\\end{aligned}\n\\,.\\label{eq:stab-linearization}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{s} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\mathbf{v}_{a}\\,, & \\delta p & =\\sum\\limits_{a=1}^{m}N_{a}\\delta p_{a}\\,,\\\\\n\\Delta\\mathbf{u} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\mathbf{u}_{b}\\,, & \\Delta p & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta p_{b}\\,,\n\\end{aligned}\n\\label{eq213}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}$\n\\end_inset\n\n represents the interpolation functions over an element,\n \n\\begin_inset Formula $\\delta\\mathbf{v}_{a},\\delta p_{a},\\Delta\\mathbf{u}_{b}\\mbox{\\thinspace and\\thinspace}\\Delta p_{b}$\n\\end_inset\n\n respectively represent nodal values of \n\\begin_inset Formula $\\delta\\mathbf{v}^{s}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta p$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta p$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $m$\n\\end_inset\n\n is the number of nodes in an element.\n Then the discretized form of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq197\"\nnolink \"false\"\n\n\\end_inset\n\n may be written as \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{int}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a} & \\delta p_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{r}_{a}^{u}\\\\\nr_{a}^{p}\n\\end{array}\\right]\\,,\\label{eq214}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $n_{e}$\n\\end_inset\n\n is the number of elements in \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n \n\\begin_inset Formula $n_{\\mbox{int}}^{\\left(e\\right)}$\n\\end_inset\n\n is the number of integration points in the \n\\begin_inset Formula $e-$\n\\end_inset\n\nth element,\n \n\\begin_inset Formula $W_{k}$\n\\end_inset\n\n is the quadrature weight associated with the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point,\n and \n\\begin_inset Formula $J_{\\eta}$\n\\end_inset\n\n is the Jacobian of the transformation from the spatial frame to the parametric space of the element.\n In the above expression,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{r}_{a}^{u}=\\boldsymbol{\\sigma}\\cdot\\nabla N_{a}\\,,\\quad r_{a}^{p}=\\mathbf{w}\\cdot\\nabla N_{a}-N_{a}\\mbox{div}\\mathbf{v}^{s}\\,,\\label{eq215}\n\\end{equation}\n\n\\end_inset\n\nand it is understood that \n\\begin_inset Formula $J_{\\eta}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{r}_{a}^{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $r_{a}^{p}$\n\\end_inset\n\n are evaluated at the parametric coordinates of the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n the discretized form of \n\\begin_inset Formula $D\\delta W_{\\mbox{int}}$\n\\end_inset\n\n in Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq199\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq203\"\nnolink \"false\"\n\n\\end_inset\n\n may be written as \n\\begin_inset Formula \n\\begin{equation}\n-D\\delta W_{\\text{int}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a} & \\delta p_{a}\\end{array}\\right]\\cdot\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{k}_{ab}^{up}\\\\\n\\mathbf{k}_{ab}^{pu} & k_{ab}^{pp}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\\,,\\label{eq216}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =\\nabla N_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\nabla N_{b}+\\left(\\nabla N_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\nabla N_{b}\\right)\\mathbf{I}\\\\\n & -N_{a}\\left[N_{b}\\left(\\rho^{s}\\nabla\\mathbf{b}^{s}+\\rho^{w}\\nabla\\mathbf{b}^{w}\\right)+\\rho_{T}^{w}\\mathbf{b}^{w}\\otimes\\nabla N_{b}\\right]\\,,\\\\\n\\mathbf{k}_{ab}^{up} & =-N_{b}\\nabla N_{a}\\,,\\\\\n\\mathbf{k}_{ab}^{pu} & =-\\left(\\nabla N_{a}\\cdot\\boldsymbol{\\mathcal{K}}\\cdot\\nabla N_{b}\\right)\\cdot\\left(\\nabla p-\\rho_{T}^{w}\\mathbf{b}^{w}\\right)-\\frac{1}{\\Delta t}N_{a}\\cdot\\nabla N_{b}\\\\\n & +\\rho_{T}^{w}\\left(\\mathbf{b}^{w}\\otimes\\nabla N_{b}+N_{b}\\nabla^{T}\\mathbf{b}^{w}\\right)\\cdot\\mathbf{k}\\cdot\\nabla N_{a}\\,,\\\\\nk_{ab}^{pp} & =-\\nabla N_{a}\\cdot\\mathbf{k}\\cdot\\nabla N_{b},\n\\end{aligned}\n\\label{eq217}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\Delta t$\n\\end_inset\n\n is a discrete increment in time.\n In a numerical implementation,\n it has been found that evaluating \n\\begin_inset Formula $\\divg\\mathbf{v}^{s}$\n\\end_inset\n\n from \n\\begin_inset Formula $\\dot{J}/J$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n,\n yields more accurate solutions than evaluating it from the trace of \n\\begin_inset Formula $\\grad\\mathbf{v}^{s}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07c\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Contributions from the stabilization method presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"subsec:Biphasic-Stabilization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be similarly evaluated.\n\\end_layout\n\n\\begin_layout Standard\nFor the various types of contributions to the external virtual work,\n a similar discretization produces \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a} & \\delta p_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{r}_{a}^{u}\\\\\nr_{a}^{p}\n\\end{array}\\right]\\,,\\label{eq218}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n-D\\delta W_{\\text{ext}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a} & \\delta p_{a}\\end{array}\\right]\\cdot\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{k}_{ab}^{up}\\\\\n\\mathbf{k}_{ab}^{pu} & k_{ab}^{pp}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\\,,\\label{eq219}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\nJ_{\\eta}=\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\,.\\label{eq220}\n\\end{equation}\n\n\\end_inset\n\nIn this case,\n \n\\begin_inset Formula $m$\n\\end_inset\n\n represents the number of nodes on an element face.\n For a prescribed normal traction \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n as given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq204\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq205\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{r}_{a}^{u} & =t_{n}N_{a}\\mathbf{n}\\,, & r_{a}^{u} & =0\\,,\\\\\n\\mathbf{K}_{ab}^{uu} & =t_{n}N_{a}\\frac{1}{J_{\\eta}}\\boldsymbol{\\mathbf{\\mathcal{A}}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right\\} \\,, & \\mathbf{k}_{ab}^{up} & =\\mathbf{0}\\,,\\\\\n\\mathbf{k}_{ab}^{pu} & =\\mathbf{0}\\,, & k_{ab}^{pp} & =0\\,,\n\\end{aligned}\n\\label{eq221}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathbf{\\mathcal{A}}}\\left\\{ \\mathbf{v}\\right\\} =-\\boldsymbol{\\mathbf{\\mathcal{E}}}\\cdot\\mathbf{v}$\n\\end_inset\n\n is the skew-symmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\mathbf{\\mathcal{E}}}$\n\\end_inset\n\n is the third-order permutation pseudo-tensor.\n For a prescribed traction \n\\begin_inset Formula $t_{n}^{e}$\n\\end_inset\n\n as given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq208\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq209\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{r}_{a}^{u} & =\\left(-p+t_{n}^{e}\\right)N_{a}\\mathbf{n}\\,, & r_{a}^{u} & =0\\,,\\\\\n\\mathbf{K}_{ab}^{uu} & =\\left(-p+t_{n}^{e}\\right)N_{a}\\frac{1}{J_{\\eta}}\\boldsymbol{\\mathbf{\\mathcal{A}}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right\\} \\,, & \\mathbf{k}_{ab}^{up} & =\\mathbf{0}\\,,\\\\\n\\mathbf{k}_{ab}^{pu} & =\\mathbf{0}\\,, & k_{ab}^{pp} & =0\\,.\n\\end{aligned}\n\\label{eq222}\n\\end{equation}\n\n\\end_inset\n\nFor a prescribed normal fluid flux \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n as given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq210\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq211\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{r}_{a}^{u} & =\\mathbf{0}\\,, & r_{a}^{u} & =w_{n}N_{a}\\,,\\\\\n\\mathbf{K}_{ab}^{uu} & =\\mathbf{0}\\,, & \\mathbf{k}_{ab}^{up} & =\\mathbf{0}\\,,\\\\\n\\mathbf{k}_{ab}^{pu} & =w_{n}N_{a}\\frac{1}{J_{\\eta}}\\mathbf{n}\\times\\left(\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right)\\,, & k_{ab}^{pp} & =0\\,.\n\\end{aligned}\n\\label{eq223}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nWeak Formulation for Biphasic-Solute Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:BS-weak-formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work integral for this problem is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\int_{b}\\delta\\mathbf{v}\\cdot\\divg\\boldsymbol{\\sigma}\\,dv+\\int_{b}\\delta\\tilde{p}\\,\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)\\,dv\\\\\n & +\\int_{b}\\delta\\tilde{c}\\left[\\frac{\\partial\\left(\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial t}+\\divg\\left(\\mathbf{j}+\\phi^{w}\\tilde{\\kappa}\\tilde{c}\\,\\mathbf{v}^{s}\\right)\\right]\\,dv\\,,\n\\end{aligned}\n\\label{eq224}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n is the virtual velocity of the solid,\n \n\\begin_inset Formula $\\delta\\tilde{p}$\n\\end_inset\n\n is the virtual effective fluid pressure,\n and \n\\begin_inset Formula $\\delta\\tilde{c}$\n\\end_inset\n\n is the virtual molar energy of the solute.\n \n\\begin_inset Formula $b$\n\\end_inset\n\n represents the mixture domain in the spatial frame and \n\\begin_inset Formula $dv$\n\\end_inset\n\n is an elemental mixture volume in \n\\begin_inset Formula $b$\n\\end_inset\n\n.\n In the last integral of \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n,\n note that \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\left(\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial t}+\\divg\\left(\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\,\\mathbf{v}^{s}\\right)=\\frac{1}{J}\\frac{D^{s}}{Dt}\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)\\,,\\label{eq225}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $D^{s}f/Dt\\equiv\\partial f/\\partial t+\\mathbf{v}^{s}\\cdot\\grad f$\n\\end_inset\n\n is the material time derivative of a scalar function \n\\begin_inset Formula $f$\n\\end_inset\n\n in the spatial frame,\n following the solid.\n Similarly,\n note that \n\\begin_inset Formula $\\divg\\mathbf{v}^{s}=J^{-1}\\left(D^{s}J/Dt\\right)$\n\\end_inset\n\n.\n Using the divergence theorem,\n the virtual work integral may be separated into internal and external contributions,\n \n\\begin_inset Formula $\\delta W=\\delta W_{\\text{ext}}-\\delta W_{\\text{int}}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{\\text{int}} & =\\int_{b}\\boldsymbol{\\sigma}:\\delta\\mathbf{d}^{s}\\,dv+\\int_{b}\\left(\\mathbf{w}\\cdot\\grad\\delta\\tilde{p}-\\frac{\\delta\\tilde{p}}{J}\\frac{D^{s}J}{Dt}\\right)\\,dv\\\\\n & +\\int_{b}\\left[\\mathbf{j}\\cdot\\grad\\delta\\tilde{c}-\\frac{\\delta\\tilde{c}}{J}\\frac{D^{s}}{Dt}\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)\\right]dv\\,,\\\\\n\\delta W_{\\text{ext}} & =\\int_{\\partial b}\\left(\\delta\\mathbf{v}\\cdot\\mathbf{t}+\\delta\\tilde{p}\\,w_{n}+\\delta\\tilde{c}\\,j_{n}\\right)\\,da\\,,\n\\end{aligned}\n\\label{eq226}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n being evaluated on the domain's boundary surface \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n.\n In the first expression \n\\begin_inset Formula $\\delta\\mathbf{d}^{s}=\\left(\\grad\\delta\\mathbf{v}+\\grad^{T}\\delta\\mathbf{v}\\right)/2$\n\\end_inset\n\n represents the virtual solid rate of deformation.\n\\end_layout\n\n\\begin_layout Standard\nTo solve this nonlinear system using an iterative Newton scheme,\n the virtual work must be linearized at trial solutions,\n along increments in \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W+D\\delta W\\left[\\Delta\\mathbf{u}\\right]+D\\delta W\\left[\\Delta\\tilde{p}\\right]+D\\delta W\\left[\\Delta\\tilde{c}\\right]\\approx0\\,,\\label{eq227}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n for any function \n\\begin_inset Formula $f\\left(q\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $Df\\left[\\Delta q\\right]$\n\\end_inset\n\n represents the directional derivative of \n\\begin_inset Formula $f$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta q$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n To operate the directional derivative on the integrand of \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n,\n it is first necessary to convert the integrals from the spatial to the material domain \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{int}}=\\int_{B}\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\,dV+\\int_{B}\\left(\\mathbf{W}\\cdot\\grad\\delta\\tilde{p}-\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\,dV+\\int_{B}\\left[\\mathbf{J}\\cdot\\grad\\delta\\tilde{c}-\\delta\\tilde{c}\\frac{\\partial}{\\partial t}\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)\\right]dV\\,,\\label{eq228}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $B$\n\\end_inset\n\n represents the mixture domain in the material frame,\n \n\\begin_inset Formula $dV$\n\\end_inset\n\n is an elemental mixture volume in \n\\begin_inset Formula $B$\n\\end_inset\n\n,\n and \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{S} & =J\\,\\mathbf{F}^{-1}\\cdot\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}\\,,\\\\\n\\delta\\mathbf{\\dot{E}} & =\\mathbf{F}^{T}\\cdot\\delta\\mathbf{d}^{s}\\cdot\\mathbf{F}\\,,\\\\\n\\mathbf{W} & =J\\,\\mathbf{F}^{-1}\\cdot\\mathbf{w}\\,,\\\\\n\\mathbf{J} & =J\\,\\mathbf{F}^{-1}\\cdot\\mathbf{j}\\,.\n\\end{aligned}\n\\label{eq229}\n\\end{equation}\n\n\\end_inset\n\nThe second Piola-Kirchhoff stress tensor \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n,\n and material flux vectors \n\\begin_inset Formula $\\mathbf{W}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{J}$\n\\end_inset\n\n,\n are respectively related to \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{j}$\n\\end_inset\n\n by the Piola transformations for tensors and vectors \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97,Marsden94\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Substituting \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq229\"\nnolink \"false\"\n\n\\end_inset\n\n into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq120\"\nnolink \"false\"\n\n\\end_inset\n\n produces \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{W} & =-\\mathbf{\\tilde{K}}\\cdot\\left(\\grad\\tilde{p}+R\\theta\\frac{\\tilde{\\kappa}}{d_{0}}J^{-1}\\mathbf{C}\\cdot\\mathbf{D}\\cdot\\grad\\tilde{c}\\right)\\,,\\\\\n\\mathbf{J} & =\\tilde{\\kappa}\\mathbf{D}\\cdot\\left(-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}J^{-1}\\mathbf{C}\\cdot\\mathbf{W}\\right)\\,,\n\\end{aligned}\n\\label{eq230}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{\\tilde{K}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n are the material representations of the permeability and diffusivity tensors,\n related to \n\\begin_inset Formula $\\mathbf{\\tilde{k}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n via the Piola transformation,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{\\tilde{K}} & =J\\,\\mathbf{F}^{-1}\\cdot\\mathbf{\\tilde{k}}\\cdot\\mathbf{F}^{-T}\\,,\\\\\n\\mathbf{D} & =J\\,\\mathbf{F}^{-1}\\cdot\\mathbf{d}\\cdot\\mathbf{F}^{-T}\\,.\n\\end{aligned}\n\\label{eq231}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n is rather involved and a summary of the resulting lengthy expressions is provided below.\n In consideration of the dearth of experimental data relating \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n to the complete state of solid matrix strain (such as \n\\begin_inset Formula $\\mathbf{C})$\n\\end_inset\n\n,\n this implementation assumes that the dependence of these functions on the strain is restricted to a dependence on the volume ratio \n\\begin_inset Formula $J=\\left(\\det\\mathbf{C}\\right)^{1/2}$\n\\end_inset\n\n.\n Furthermore,\n it is assumed that the free solution diffusivity \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n is independent of the strain.\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n is described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:BS-linearization-external\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Following the linearization procedure,\n the resulting expressions may be discretized by nodally interpolating \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n over finite elements,\n producing a set of equations in matrix form,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:BS-linearization-external\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe formulation presented in this study is implemented in FEBio by introducing an additional module dedicated to solute transport in deformable porous media.\n Classes are implemented to describe material functions for \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n (and \n\\begin_inset Formula $d_{0})$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n,\n which allow the formulation of any desired constitutive relation for these functions of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n,\n along with corresponding derivatives of these functions with respect to \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n.\n The implementation accepts essential boundary conditions on \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n,\n or natural boundary conditions on \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $j_{n}$\n\\end_inset\n\n;\n initial conditions may also be specified for \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n.\n Analysis results for pressure and concentration may be displayed either as \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n,\n or as \n\\begin_inset Formula $p$\n\\end_inset\n\n and \n\\begin_inset Formula $c$\n\\end_inset\n\n by inverting the relations of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq118\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of Internal Virtual Work\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work integral \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq228\"\nnolink \"false\"\n\n\\end_inset\n\n may be linearized term by term along increments in \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}$\n\\end_inset\n\n using the general form \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\int_{B}F\\,dV\\right)\\left[\\Delta q\\right]=\\int_{B}DF\\left[\\Delta q\\right]\\,dV=\\int_{b}f\\,dv\\,.\\label{eq232}\n\\end{equation}\n\n\\end_inset\n\nFor notational simplicity,\n the integral sign is omitted and the linearization of each term is presented in the form \n\\begin_inset Formula $DF\\left[\\Delta q\\right]\\,dV=f\\,dv$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the first term in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{S}:\\delta\\dot{\\mathbf{E}}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\left[\\delta\\mathbf{d}^{s}:\\boldsymbol{\\mathcal{C}}:\\Delta\\boldsymbol{\\varepsilon}+\\boldsymbol{\\sigma}:\\left(\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\delta\\mathbf{v}\\right)\\right]\\,dv\\,,\\label{eq233}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the spatial elasticity tensor of the mixture,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\boldsymbol{\\mathcal{C}}^{e}-\\left(\\tilde{p}+R\\theta\\,\\Phi\\tilde{\\kappa}\\tilde{c}\\right)\\left(\\mathbf{I}\\otimes\\mathbf{I}-2\\mathbf{I}\\,\\overline{\\underline{\\otimes}}\\,\\mathbf{I}\\right)-R\\theta\\tilde{c}\\,J\\frac{\\partial\\left(\\Phi\\tilde{\\kappa}\\right)}{\\partial J}\\mathbf{I}\\otimes\\mathbf{I}\\,,\\label{eq234}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{e}$\n\\end_inset\n\n is the spatial elasticity tensor of the solid matrix,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}^{e}=J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):2\\frac{\\partial\\mathbf{S}^{e}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right)\\,.\\label{eq235}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of the second term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{W}\\cdot\\Grad\\delta\\tilde{p}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\grad\\delta\\tilde{p}\\cdot\\mathbf{w}_{u}^{\\prime}\\,dv\\,,\\label{eq236}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{u}^{\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{W}\\left[\\Delta\\mathbf{u}\\right]=-\\left(\\tilde{\\boldsymbol{\\mathcal{K}}}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\left(\\grad\\tilde{p}+R\\theta\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\grad\\tilde{c}\\right)\\\\\n & -\\frac{R\\theta}{d_{0}}\\tilde{\\mathbf{k}}\\cdot\\left(J^{2}\\frac{\\partial\\left(J^{-1}\\tilde{\\kappa}\\right)}{\\partial J}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}+2\\tilde{\\kappa}\\,\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\mathbf{d}\\cdot\\grad\\tilde{c}-\\tilde{\\kappa}\\frac{R\\theta}{d_{0}}\\tilde{\\mathbf{k}}\\cdot\\left(\\boldsymbol{\\mathcal{D}}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\grad\\tilde{c}\\,,\n\\end{aligned}\n\\label{eq237}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{\\boldsymbol{\\mathcal{K}}} & =J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):2\\frac{\\partial\\tilde{\\mathbf{K}}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right)\\,,\\\\\n\\boldsymbol{\\mathcal{D}} & =J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):2\\frac{\\partial\\mathbf{D}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right)\\,,\n\\end{aligned}\n\\label{eq238}\n\\end{equation}\n\n\\end_inset\n\nrepresenting the spatial tangents,\n with respect to the strain,\n of the effective permeability and solute diffusivity,\n respectively.\n These fourth-order tensors exhibit minor symmetries but not major symmetry,\n as described recently \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\tilde{\\mathbf{K}}$\n\\end_inset\n\n is given by substituting \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq120\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula $_{3}$\n\\end_inset\n\n into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq231\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula $_{1}$\n\\end_inset\n\n,\n the evaluation of \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\mathcal{K}}}$\n\\end_inset\n\n is rather involved and it can be shown that \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{K}}}=2\\left(\\tilde{\\mathbf{k}}\\otimes\\mathbf{I}-2\\tilde{\\mathbf{k}}\\,\\underline{\\otimes}\\,\\mathbf{I}\\right)-\\left(\\tilde{\\mathbf{k}}\\,\\overline{\\underline{\\otimes}}\\,\\tilde{\\mathbf{k}}\\right):\\boldsymbol{\\mathcal{G}}\\,,\\label{eq239}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{G}} & =2\\left(\\mathbf{k}^{-1}\\otimes\\mathbf{I}-2\\mathbf{k}^{-1}\\,\\overline{\\underline{\\otimes}}\\,\\mathbf{I}\\right)-\\left(\\mathbf{k}^{-1}\\,\\underline{\\otimes}\\,\\mathbf{k}^{-1}\\right):\\boldsymbol{\\mathcal{K}}\\\\\n & +\\frac{R\\theta\\tilde{c}}{d_{0}}J\\frac{\\partial}{\\partial J}\\left(\\frac{\\tilde{\\kappa}}{\\varphi^{w}}\\right)\\left(\\mathbf{I}-\\frac{\\mathbf{d}}{d_{0}}\\right)\\otimes\\mathbf{I}\\\\\n & +\\frac{R\\theta\\tilde{c}}{d_{0}}\\frac{\\tilde{\\kappa}}{\\varphi^{w}}\\left(\\mathbf{I}\\otimes\\mathbf{I}-2\\mathbf{I}\\,\\underline{\\otimes}\\,\\mathbf{I}-\\frac{1}{d_{0}}\\boldsymbol{\\mathcal{D}}\\right)\n\\end{aligned}\n\\label{eq240}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{K}}=J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):2\\frac{\\partial\\mathbf{K}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right)\\,.\\label{eq241}\n\\end{equation}\n\n\\end_inset\n\nThe next term in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n linearizes to \n\\begin_inset Formula \n\\begin{equation}\n-D\\left(\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=-\\delta\\tilde{p}\\frac{1}{\\Delta t}\\divg\\Delta\\mathbf{u}\\,dv\\,,\\label{eq242}\n\\end{equation}\n\n\\end_inset\n\nwhere we used a backward difference scheme to approximate the time derivative,\n \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial J}{\\partial t}\\approx\\frac{1}{\\Delta t}\\left(J-J^{-\\Delta t}\\right)\\,,\\label{eq243}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\Delta t$\n\\end_inset\n\n represents the time increment relative to the previous time point.\n The next term is given by \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{J}\\cdot\\Grad\\delta\\tilde{c}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\grad\\delta\\tilde{c}\\cdot\\mathbf{j}_{u}^{\\prime}\\,dv,\\label{eq244}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{u}^{\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{J}\\left[\\Delta\\mathbf{u}\\right]=\\left(J\\frac{\\partial\\tilde{\\kappa}}{\\partial J}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{d}+\\tilde{\\kappa}\\boldsymbol{\\mathcal{D}}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\left(-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}\\right)\\\\\n & +\\tilde{\\kappa}\\mathbf{d}\\cdot\\left(-\\varphi^{s}\\left(\\divg\\Delta\\mathbf{u}\\right)\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\left(2\\Delta\\boldsymbol{\\varepsilon}-\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}\\right)\\cdot\\mathbf{w}\\right)+\\tilde{\\kappa}\\frac{\\tilde{c}}{d_{0}}\\mathbf{d}\\cdot\\mathbf{w}_{u}^{\\prime}\n\\end{aligned}\n\\,.\\label{eq245}\n\\end{equation}\n\n\\end_inset\n\nUsing a backward difference scheme for the time derivative,\n the last term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta\\tilde{c}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial t}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\frac{\\delta\\tilde{c}}{\\Delta t}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}\\right)}{\\partial J}\\tilde{c}\\divg\\Delta\\mathbf{u}\\,dv\\,.\\label{eq246}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the various terms in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\right)\\left[\\Delta\\tilde{p}\\right]\\,dV=-\\Delta\\tilde{p}\\,\\divg\\delta\\mathbf{v}\\,dv\\,,\\label{eq247}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{W}\\cdot\\Grad\\delta\\tilde{p}-\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\left[\\Delta\\tilde{p}\\right]\\,dV=-\\grad\\delta\\tilde{p}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad\\Delta\\tilde{p}\\,dv\\,,\\label{eq248}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{J}\\cdot\\Grad\\delta\\tilde{c}-\\delta\\tilde{c}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial t}\\right)\\left[\\Delta\\tilde{p}\\right]\\,dV=-\\frac{\\tilde{\\kappa}\\tilde{c}}{d_{0}}\\grad\\delta\\tilde{c}\\cdot\\mathbf{d}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad\\Delta\\tilde{p}\\,dv\\,.\\label{eq249}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization along \n\\begin_inset Formula $\\Delta\\tilde{c}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the first term in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\tilde{c}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\right)\\left[\\Delta\\tilde{c}\\right]\\,dV=\\Delta\\tilde{c}\\left(\\boldsymbol{\\sigma}_{c}^{\\prime}:\\delta\\mathbf{d}-R\\theta\\frac{\\partial\\left(\\Phi\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial\\tilde{c}}\\divg\\delta\\mathbf{v}\\right)\\,dv\\,,\\label{eq250}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}_{c}^{\\prime}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\mathbf{S}^{e}}{\\partial\\tilde{c}}\\cdot\\mathbf{F}^{T}\\label{eq251}\n\\end{equation}\n\n\\end_inset\n\nrepresents the spatial tangent of the stress with respect to the effective concentration.\n The next term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{W}\\cdot\\Grad\\delta\\tilde{p}\\right)\\left[\\Delta\\tilde{c}\\right]\\,dV=\\grad\\delta\\tilde{p}\\cdot\\mathbf{w}_{c}^{\\prime}\\,dv\\,,\\label{eq252}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{c}^{\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{W}\\left[\\Delta\\tilde{c}\\right]=-\\Delta\\tilde{c}\\,\\tilde{\\mathbf{k}}_{c}^{\\prime}\\cdot\\left(\\grad\\tilde{p}+R\\theta\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\grad\\tilde{c}\\right)\\\\\n & -R\\theta\\tilde{\\mathbf{k}}\\cdot\\left[\\Delta\\tilde{c}\\left(\\frac{\\partial}{\\partial\\tilde{c}}\\left(\\frac{\\tilde{\\kappa}}{d_{0}}\\right)\\mathbf{d}+\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}_{c}^{\\prime}\\right)\\cdot\\grad\\tilde{c}+\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\grad\\Delta\\tilde{c}\\right]\\,,\n\\end{aligned}\n\\label{eq253}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mathbf{k}}_{c}^{\\prime}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\tilde{\\mathbf{K}}}{\\partial\\tilde{c}}\\cdot\\mathbf{F}^{T}\\label{eq254}\n\\end{equation}\n\n\\end_inset\n\nis the spatial tangent of the effective hydraulic permeability with respect to the effective concentration.\n\\end_layout\n\n\\begin_layout Standard\nThe next term reduces to \n\\begin_inset Formula \n\\begin{equation}\n-D\\left(\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\left[\\Delta\\tilde{c}\\right]\\,dV=0\\,.\\label{eq255}\n\\end{equation}\n\n\\end_inset\n\nThe following term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{J}\\cdot\\Grad\\delta\\tilde{c}\\right)\\left[\\Delta\\tilde{c}\\right]\\,dV=\\grad\\delta\\tilde{c}\\cdot\\mathbf{j}_{c}^{\\prime}\\,dv\\,,\\label{eq256}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{c}^{\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{J}\\left[\\Delta\\tilde{c}\\right]\\\\\n & =\\Delta\\tilde{c}\\left(\\frac{\\partial\\tilde{\\kappa}}{\\partial\\tilde{c}}\\mathbf{d}+\\tilde{\\kappa}\\mathbf{d}_{c}^{\\prime}\\right)\\cdot\\left(-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}\\right)\\\\\n & -\\varphi^{w}\\tilde{\\kappa}\\mathbf{d}\\cdot\\grad\\Delta\\tilde{c}+\\tilde{\\kappa}\\frac{\\tilde{c}}{d_{0}}\\mathbf{d}\\cdot\\mathbf{w}_{c}^{\\prime}\\,,\n\\end{aligned}\n\\label{eq257}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}_{c}^{\\prime}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\mathbf{D}}{\\partial\\tilde{c}}\\cdot\\mathbf{F}^{T}\\label{eq258}\n\\end{equation}\n\n\\end_inset\n\nis the spatial tangent of the diffusivity with respect to the effective concentration.\n\\end_layout\n\n\\begin_layout Standard\nThe last term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial t}\\delta\\tilde{c}\\right)\\left[\\Delta\\tilde{c}\\right]\\,dV=\\delta\\tilde{c}\\frac{\\varphi^{w}}{\\Delta t}\\frac{\\partial\\left(\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial\\tilde{c}}\\Delta\\tilde{c}\\,dv\\,,\\label{eq259}\n\\end{equation}\n\n\\end_inset\n\nwhere we similarly used a backward difference scheme to discretize the time derivative.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of External Virtual Work\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-linearization-external\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq226\"\nnolink \"false\"\n\n\\end_inset\n\n depends on whether natural boundary conditions are prescribed as area densities or total net values over an area.\n Thus,\n in the case when \n\\begin_inset Formula $\\mathbf{t}\\,da$\n\\end_inset\n\n (net force),\n \n\\begin_inset Formula $w_{n}da$\n\\end_inset\n\n (net volumetric flow rate),\n or \n\\begin_inset Formula $j_{n}da$\n\\end_inset\n\n (net molar flow rate) are prescribed over the elemental area \n\\begin_inset Formula $da$\n\\end_inset\n\n,\n there is no variation in \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n and it follows that \n\\begin_inset Formula $D\\delta W_{\\text{ext}}=0$\n\\end_inset\n\n.\n Alternatively,\n in the case when \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n or \n\\begin_inset Formula $j_{n}$\n\\end_inset\n\n are prescribed,\n the linearization may be performed by evaluating the integral in the parametric space of the boundary surface \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n with parametric coordinates \n\\begin_inset Formula $\\left(\\eta^{1},\\eta^{2}\\right)$\n\\end_inset\n\n.\n Accordingly,\n for a point \n\\begin_inset Formula $\\mathbf{x}\\left(\\eta^{1},\\eta^{2}\\right)$\n\\end_inset\n\n on \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n surface tangents (covariant basis vectors) are given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}=\\frac{\\partial\\mathbf{x}}{\\partial\\eta^{\\alpha}},\\quad\\left(\\alpha=1,2\\right)\\label{eq260}\n\\end{equation}\n\n\\end_inset\n\nand the outward unit normal is \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{g}_{1}\\times\\mathbf{g}_{2}}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,.\\label{eq261}\n\\end{equation}\n\n\\end_inset\n\nThe elemental area on \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n is \n\\begin_inset Formula $da=\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|d\\eta^{1}d\\eta^{2}$\n\\end_inset\n\n.\n Consequently,\n the external virtual work integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\int_{\\partial b}\\left(\\delta\\mathbf{v}\\cdot\\mathbf{t}+\\delta\\tilde{p}\\,w_{n}+\\delta\\tilde{c}\\,j_{n}\\right)\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|d\\eta^{1}d\\eta^{2}\\,.\\label{eq262}\n\\end{equation}\n\n\\end_inset\n\nThe directional derivative of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n may then be applied directly to its integrand,\n since the parametric space is invariant \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIf we restrict traction boundary conditions to the special case of normal tractions,\n then \n\\begin_inset Formula $\\mathbf{t}=t_{n}\\mathbf{n}$\n\\end_inset\n\n where \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is the prescribed normal traction component.\n Then it can be shown that the linearization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n produces \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta W_{\\text{ext}}\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int_{\\partial b}\\left(t_{n}\\delta\\mathbf{v}+w_{n}\\delta\\tilde{p}\\,\\mathbf{n}+j_{n}\\delta\\tilde{c}\\,\\mathbf{n}\\right)\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\times\\mathbf{g}_{2}+\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}\\right)d\\eta^{1}d\\eta^{2}\\,.\\label{eq263}\n\\end{equation}\n\n\\end_inset\n\nThe linearizations along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}$\n\\end_inset\n\n reduce to zero,\n \n\\begin_inset Formula $D\\left(\\delta W_{\\text{ext}}\\right)\\left[\\Delta\\tilde{p}\\right]=0$\n\\end_inset\n\n and \n\\begin_inset Formula $D\\left(\\delta W_{\\text{ext}}\\right)\\left[\\Delta\\tilde{c}\\right]=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo discretize the virtual work relations,\n let \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\mathbf{v}_{a}\\,, & \\Delta\\mathbf{u} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\mathbf{u}_{b}\\,,\\\\\n\\delta\\tilde{p} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\tilde{p}_{a}\\,, & \\Delta\\tilde{p} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\tilde{p}_{b}\\,,\\\\\n\\delta\\tilde{c} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\tilde{c}_{a}\\,, & \\Delta\\tilde{c} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\tilde{c}_{b}\\,,\n\\end{aligned}\n\\label{eq264}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}$\n\\end_inset\n\n represents the interpolation functions over an element,\n \n\\begin_inset Formula $\\delta\\mathbf{v}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{p}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{c}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\tilde{p}_{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}_{a}$\n\\end_inset\n\n respectively represent the nodal values of \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{p}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{c}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}$\n\\end_inset\n\n;\n \n\\begin_inset Formula $m$\n\\end_inset\n\n is the number of nodes in an element.\n\\end_layout\n\n\\begin_layout Standard\nThe discretized form of \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq226\"\nnolink \"false\"\n\n\\end_inset\n\n may be written as \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{int}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{r}_{a}^{u}\\\\\nr_{a}^{p}\\\\\nr_{a}^{c}\n\\end{array}\\right]\\,,\\label{eq265}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $n_{e}$\n\\end_inset\n\n is the number of elements in \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n \n\\begin_inset Formula $n_{\\text{int}}^{\\left(e\\right)}$\n\\end_inset\n\n is the number of integration points in the \n\\begin_inset Formula $e-$\n\\end_inset\n\nth element,\n \n\\begin_inset Formula $W_{k}$\n\\end_inset\n\n is the quadrature weight associated with the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point,\n and \n\\begin_inset Formula $J_{\\eta}$\n\\end_inset\n\n is the Jacobian of the transformation from the current spatial configuration to the parametric space of the element.\n In the above expression,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{r}_{a}^{u} & =\\boldsymbol{\\sigma}\\cdot\\grad N_{a}\\,,\\\\\nr_{a}^{p} & =\\mathbf{w}\\cdot\\grad N_{a}-N_{a}\\frac{1}{J}\\frac{\\partial J}{\\partial t}\\,,\\\\\nr_{a}^{c} & =\\mathbf{j}\\cdot\\grad N_{a}-N_{a}\\frac{1}{J}\\frac{\\partial}{\\partial t}\\left(J\\varphi^{w}\\tilde{\\kappa}\\tilde{c}\\right)\\,,\n\\end{aligned}\n\\label{eq266}\n\\end{equation}\n\n\\end_inset\n\nand it is understood that \n\\begin_inset Formula $J_{\\eta}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{r}_{a}^{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $r_{a}^{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $r_{a}^{c}$\n\\end_inset\n\n are evaluated at the parametric coordinates of the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point.\n Since the parametric space is invariant,\n time derivatives are evaluated in a material frame.\n For example,\n the time derivative \n\\begin_inset Formula $D^{s}J\\left(\\mathbf{x},t\\right)/Dt$\n\\end_inset\n\n appearing in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq226\"\nnolink \"false\"\n\n\\end_inset\n\n becomes \n\\begin_inset Formula $\\partial J\\left(\\eta_{k},t\\right)/\\partial t$\n\\end_inset\n\n when evaluated at the parametric coordinates \n\\begin_inset Formula $\\eta_{k}=\\left(\\eta_{k}^{1},\\eta_{k}^{2},\\eta_{k}^{3}\\right)$\n\\end_inset\n\n of the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n the discretized form of \n\\begin_inset Formula $D\\delta W_{\\text{int}}=D\\delta W_{\\text{int}}\\left[\\Delta\\mathbf{u}\\right]+D\\delta W_{\\text{int}}\\left[\\Delta\\tilde{p}\\right]+D\\delta W_{\\text{int}}\\left[\\Delta\\tilde{c}\\right]$\n\\end_inset\n\n may be written as \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{\\text{int}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{k}_{ab}^{up} & \\mathbf{k}_{ab}^{uc}\\\\\n\\mathbf{k}_{ab}^{pu} & k_{ab}^{pp} & k_{ab}^{pc}\\\\\n\\mathbf{k}_{ab}^{cu} & k_{ab}^{cp} & k_{ab}^{cc}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\tilde{p}_{b}\\\\\n\\Delta\\tilde{c}_{b}\n\\end{array}\\right]\\,,\\label{eq267}\n\\end{equation}\n\n\\end_inset\n\nwhere the terms in the first column are the discretized form of the linearization along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{ab}^{uu}=\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad N_{b}+\\left(\\grad N_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad N_{b}\\right)\\mathbf{I}\\,,\\label{eq268}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{pu}=\\left(\\mathbf{w}_{b}^{u}\\right)^{T}\\cdot\\grad N_{a}+N_{a}\\mathbf{q}_{b}^{pu}\\,,\\label{eq269}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{cu}=\\left(\\mathbf{j}_{b}^{u}\\right)^{T}\\cdot\\grad N_{a}+N_{a}\\mathbf{q}_{b}^{cu}\\,,\\label{eq270}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{b}^{u} & =J\\frac{\\partial\\tilde{\\kappa}}{\\partial J}\\left[\\mathbf{d}\\cdot\\left(-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}\\right)\\right]\\otimes\\grad N_{b}+\\tilde{\\kappa}\\left({-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}}\\right)\\cdot\\mathbf{d}\\cdot\\grad N_{b}\\\\\n & +\\tilde{\\kappa}\\left({-\\varphi^{s}\\left({\\mathbf{d}\\cdot\\grad\\tilde{c}}\\right)\\otimes\\grad N_{b}+\\frac{\\tilde{c}}{d_{0}}\\left[{2\\left({\\grad N_{b}\\cdot\\mathbf{w}}\\right)\\mathbf{d}-\\left({\\mathbf{d}\\cdot\\mathbf{w}}\\right)\\otimes\\grad N_{b}}\\right]}\\right)+\\tilde{\\kappa}\\frac{\\tilde{c}}{d_{0}}\\mathbf{d}\\cdot\\mathbf{w}_{b}^{u}\\;,\n\\end{aligned}\n\\label{eq272}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{q}_{b}^{pu}=-\\frac{1}{\\Delta t}\\grad N_{b},\\label{eq273}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{q}_{b}^{cu}=\\tilde{c}\\frac{\\partial\\left({J\\phi^{w}\\tilde{\\kappa}}\\right)}{\\partial J}\\mathbf{q}_{b}^{pu}.\\label{eq274}\n\\end{equation}\n\n\\end_inset\n\nThe terms in the second column of the stiffness matrix in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq267\"\nnolink \"false\"\n\n\\end_inset\n\n are the discretized form of the linearization along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{up}=-N_{b}\\grad N_{a},\\label{eq275}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{pp}=-\\grad N_{a}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad N_{b},\\label{eq276}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{cp}=-\\frac{\\tilde{\\kappa}\\tilde{c}}{d_{0}}\\grad N_{a}\\cdot\\mathbf{d}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad N_{b}.\\label{eq277}\n\\end{equation}\n\n\\end_inset\n\nThe terms in the third column of the stiffness matrix in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq267\"\nnolink \"false\"\n\n\\end_inset\n\n are the discretized form of the linearization along \n\\begin_inset Formula $\\Delta\\tilde{c}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{uc}=N_{b}\\left(\\boldsymbol{\\sigma}_{c}^{\\prime}\\cdot\\grad N_{a}-R\\theta\\frac{\\partial\\left(\\Phi\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial\\tilde{c}}\\grad N_{a}\\right)\\,,\\label{eq278}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{pc}=\\grad N_{a}\\cdot\\mathbf{w}_{b}^{c}\\,,\\label{eq279}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{cc}=\\grad N_{a}\\cdot\\mathbf{j}_{b}^{c}+N_{a}q_{b}^{c}\\,,\\label{eq280}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{b}^{c} & =-N_{b}\\tilde{\\mathbf{k}}_{c}^{\\prime}\\cdot\\left(\\grad\\tilde{p}+R\\theta\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\grad\\tilde{c}\\right)\\\\\n & -R\\theta\\tilde{\\mathbf{k}}\\cdot\\left[N_{b}\\left(\\frac{\\partial}{\\partial\\tilde{c}}\\left(\\frac{\\tilde{\\kappa}}{d_{0}}\\right)\\mathbf{d}+\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}_{c}^{\\prime}\\right)\\cdot\\grad\\tilde{c}+\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\grad N_{b}\\right]\\,,\n\\end{aligned}\n\\label{eq281}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{j}_{b}^{c}=N_{b}\\left(\\frac{\\partial\\tilde{\\kappa}}{\\partial\\tilde{c}}\\mathbf{d}+\\tilde{\\kappa}\\mathbf{d}_{c}^{\\prime}\\right)\\cdot\\left(-\\varphi^{w}\\grad\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}\\right)+\\tilde{\\kappa}\\mathbf{d}\\cdot\\left(-\\varphi^{w}\\grad N_{b}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}_{b}^{c}\\right)\\,,\\label{eq282}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nq_{b}^{c}=-N_{b}\\frac{\\phi^{w}}{\\Delta t}\\frac{\\partial\\left(\\tilde{\\kappa}\\tilde{c}\\right)}{\\partial\\tilde{c}}\\,.\\label{eq283}\n\\end{equation}\n\n\\end_inset\n\nThe discretization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq226\"\nnolink \"false\"\n\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\nN_{a}t_{n}\\mathbf{n}\\\\\nN_{a}w_{n}\\\\\nN_{a}j_{n}\n\\end{array}\\right]\\,,\\label{eq284}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}=\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|$\n\\end_inset\n\n.\n The summation is performed over all surface elements on which these boundary conditions are prescribed.\n The discretization of \n\\begin_inset Formula $-D\\delta W_{\\text{ext}}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\n-D\\delta W_{\\text{ext}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{k}_{ab}^{pu} & 0 & 0\\\\\n\\mathbf{k}_{ab}^{cu} & 0 & 0\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\tilde{p}_{b}\\\\\n\\Delta\\tilde{c}_{b}\n\\end{array}\\right]\\,,\\label{eq285}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =t_{n}N_{a}\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right\\} \\,,\\\\\n\\mathbf{k}_{ab}^{pu} & =-w_{n}N_{a}\\left(\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right)\\times\\mathbf{n}\\,,\\\\\n\\mathbf{k}_{ab}^{cu} & =-j_{n}N_{a}\\left(\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right)\\times\\mathbf{n}\\,.\n\\end{aligned}\n\\label{eq286}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{A}}\\left\\{ \\mathbf{v}\\right\\} $\n\\end_inset\n\n is the antisymmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n (such that \n\\begin_inset Formula $\\boldsymbol{\\mathcal{A}}\\left\\{ \\mathbf{v}\\right\\} \\cdot\\mathbf{q}=\\mathbf{v}\\times\\mathbf{q}$\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{q})$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nWeak Formulation for Multiphasic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:MP-weak-formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work integral for a mixture of intrinsically incompressible constituents combines the balance of momentum for the mixture,\n the balance of mass for the mixture,\n and the balance of mass for each of the solutes.\n In addition,\n for charged mixtures,\n the condition of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq127\"\nnolink \"false\"\n\n\\end_inset\n\n may be enforced as a penalty constraint on each solute mass balance equation:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\int_{b}\\delta\\mathbf{v}\\cdot\\mbox{div}\\boldsymbol{\\sigma}\\,dv\\\\\n & +\\int_{b}\\delta\\tilde{p}\\,\\mbox{div}\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)\\,dv\\\\\n & +\\sum\\limits_{\\alpha\\ne s,w}\\int_{b}\\delta\\tilde{c}^{\\alpha}\\left[\\frac{1}{J^{s}}\\frac{D^{s}}{Dt}\\left(J^{s}\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)+\\mbox{div}\\mathbf{j}^{\\alpha}+\\sum\\limits_{\\beta\\ne s,w}z^{\\beta}\\mbox{div}\\mathbf{j}^{\\beta}\\right]\\,dv\\,,\n\\end{aligned}\n\\label{eq287}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n is the virtual velocity of the solid,\n \n\\begin_inset Formula $\\delta\\tilde{p}$\n\\end_inset\n\n is the virtual effective fluid pressure,\n and \n\\begin_inset Formula $\\delta\\tilde{c}^{\\alpha}$\n\\end_inset\n\n is the virtual molar energy of solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $b$\n\\end_inset\n\n represents the mixture domain in the spatial frame and \n\\begin_inset Formula $dv$\n\\end_inset\n\n is an elemental volume in \n\\begin_inset Formula $b$\n\\end_inset\n\n.\n Applying the divergence theorem,\n \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n may be split into internal and external contributions to the virtual work,\n \n\\begin_inset Formula $\\delta W=\\delta W_{\\text{ext}}-\\delta W_{\\text{int}}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{\\text{int}} & =\\int_{b}\\boldsymbol{\\sigma}:\\delta\\mathbf{D}\\,dv+\\int_{b}\\left(\\mathbf{w}\\cdot\\mbox{grad}\\delta\\tilde{p}-\\frac{\\delta\\tilde{p}}{J^{s}}\\frac{D^{s}J^{s}}{Dt}\\right)\\,dv\\\\\n & +\\sum\\limits_{\\alpha\\ne s,w}\\int_{b}\\left[\\mathbf{j}^{\\alpha}\\cdot\\mbox{grad}\\delta\\tilde{c}^{\\alpha}-\\frac{\\delta\\tilde{c}^{\\alpha}}{J^{s}}\\frac{D^{s}}{Dt}\\left(J^{s}\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)\\right]\\,dv\\\\\n & +\\sum\\limits_{\\alpha\\ne s,w}\\int_{b}\\mbox{grad}\\delta\\tilde{c}^{\\alpha}\\cdot\\sum\\limits_{\\beta\\ne s,w}z^{\\beta}\\mathbf{j}^{\\beta}\\,dv\\,,\n\\end{aligned}\n\\label{eq288}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\int_{\\partial b}\\left[\\delta\\mathbf{v}\\cdot\\mathbf{t}+\\delta\\tilde{p}\\,w_{n}+\\sum\\limits_{\\alpha\\ne s,w}\\delta\\tilde{c}^{\\alpha}\\left(j_{n}^{\\alpha}+\\sum\\limits_{\\beta\\ne s,w}z^{\\beta}j_{n}^{\\beta}\\right)\\right]\\,da\\,.\\label{eq289}\n\\end{equation}\n\n\\end_inset\n\nIn these expressions,\n \n\\begin_inset Formula $\\delta\\mathbf{D}=\\left(\\mbox{grad}\\delta\\mathbf{v}+\\mbox{grad}^{T}\\delta\\mathbf{v}\\right)/2$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n is the boundary of \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $da$\n\\end_inset\n\n is an elemental area on \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n.\n In this finite element formulation,\n \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n are used as nodal variables,\n and essential boundary conditions may be prescribed on these variables.\n Natural boundary conditions are prescribed to the mixture traction,\n \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n normal fluid flux,\n \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n and normal solute flux,\n \n\\begin_inset Formula $j_{n}^{\\alpha}=\\mathbf{j}^{\\alpha}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the outward unit normal to \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n.\n To solve the system \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n for nodal values of \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n,\n it is necessary to linearize these equations,\n as shown for example in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:BS-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:BS-linearization-external\"\nnolink \"false\"\n\n\\end_inset\n\n for biphasic-solute materials.\n If the mixture is charged,\n it is also necessary to solve for the electric potential \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n by solving the algebraic relation of the electroneutrality condition in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq124\"\nnolink \"false\"\n\n\\end_inset\n\n,\n which may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\nc^{F}+\\sum\\limits_{\\beta\\ne s,w}z^{\\beta}\\tilde{\\kappa}^{\\beta}\\tilde{c}^{\\beta}=0\\,.\\label{eq290}\n\\end{equation}\n\n\\end_inset\n\nIn the special case of a triphasic mixture,\n where solutes consist of two counter-ions (\n\\begin_inset Formula $\\alpha=+,-)$\n\\end_inset\n\n,\n this equation may be solved in closed form to produce \n\\begin_inset Formula \n\\begin{equation}\n\\psi=\\frac{1}{z^{\\alpha}}\\frac{R\\theta}{F_{c}}\\ln\\left(\\frac{2z^{\\alpha}\\hat{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}}{-c^{F}\\pm\\sqrt{\\left(c^{F}\\right)^{2}+4\\left(z^{\\alpha}\\right)^{2}\\left(\\hat{\\kappa}^{+}\\tilde{c}^{+}\\right)\\left(\\hat{\\kappa}^{-}\\tilde{c}^{-}\\right)}}\\right),\\quad\\alpha=+,-,\\label{eq291}\n\\end{equation}\n\n\\end_inset\n\nOnly the positive root is valid in the argument of the logarithm function.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-linearization-du\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the first term in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\left[\\delta\\mathbf{d}^{s}:\\boldsymbol{\\mathcal{C}}:\\Delta\\boldsymbol{\\varepsilon}+\\boldsymbol{\\sigma}:\\left(\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\delta\\mathbf{v}\\right)\\right]\\,dv\\,,\\label{eq292}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the spatial elasticity tensor of the mixture,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\boldsymbol{\\mathcal{C}}^{e}-\\left(\\tilde{p}+R\\theta\\,\\Phi\\sum\\limits_{\\beta}\\tilde{\\kappa}^{\\beta}\\tilde{c}^{\\beta}\\right)\\left(\\mathbf{I}\\otimes\\mathbf{I}-2\\mathbf{I}\\odot\\mathbf{I}\\right)-R\\theta\\sum\\limits_{\\beta}\\tilde{c}^{\\beta}\\,J\\frac{\\partial\\left(\\Phi\\tilde{\\kappa}^{\\beta}\\right)}{\\partial J}\\mathbf{I}\\otimes\\mathbf{I}\\,,\\label{eq293}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{e}$\n\\end_inset\n\n is the spatial elasticity tensor of the solid matrix,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}^{e}=J^{-1}\\left(\\mathbf{F}\\oslash\\mathbf{F}\\right):2\\frac{\\partial\\mathbf{S}^{e}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\oslash\\mathbf{F}^{T}\\right)\\,.\\label{eq294}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of the second term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{W}\\cdot\\Grad\\delta\\tilde{p}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\grad\\delta\\tilde{p}\\cdot\\mathbf{w}_{u}^{\\prime}\\,dv\\,,\\label{eq295}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{u}^{\\prime} & \\equiv-\\left(\\tilde{\\boldsymbol{\\mathcal{K}}}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\left(\\grad\\tilde{p}+R\\theta\\sum\\limits_{\\beta}\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\mathbf{d}^{\\beta}\\cdot\\grad\\tilde{c}^{\\beta}\\right)\\\\\n & -\\tilde{\\mathbf{k}}\\cdot R\\theta\\sum\\limits_{\\beta}\\left(\\left[J\\frac{\\partial}{\\partial J}\\left(\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\right)-\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\right]\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{d}^{\\beta}+\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\left(2\\Delta\\boldsymbol{\\varepsilon}\\cdot\\mathbf{d}^{\\beta}+\\boldsymbol{\\mathcal{D}}^{\\beta}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\right)\\cdot\\grad\\tilde{c}^{\\beta}\n\\end{aligned}\n\\label{eq296}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{\\boldsymbol{\\mathcal{K}}} & =J^{-1}\\left(\\mathbf{F}\\oslash\\mathbf{F}\\right):2\\frac{\\partial\\tilde{\\mathbf{K}}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\oslash\\mathbf{F}^{T}\\right)\\,,\\\\\n\\boldsymbol{\\mathcal{D}}^{\\alpha} & =J^{-1}\\left(\\mathbf{F}\\oslash\\mathbf{F}\\right):2\\frac{\\partial\\mathbf{D}^{\\alpha}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\oslash\\mathbf{F}^{T}\\right)\\,,\n\\end{aligned}\n\\label{eq297}\n\\end{equation}\n\n\\end_inset\n\nrepresenting the spatial tangents,\n with respect to the strain,\n of the effective permeability and solute diffusivity,\n respectively.\n These fourth-order tensors exhibit minor symmetries but not major symmetry,\n as described recently \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\tilde{\\mathbf{K}}$\n\\end_inset\n\n is given by substituting \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq120\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula $_{3}$\n\\end_inset\n\n into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq231\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula $_{1}$\n\\end_inset\n\n,\n the evaluation of \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\mathcal{K}}}$\n\\end_inset\n\n is rather involved and it can be shown that \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{K}}}=\\left(\\tilde{\\mathbf{k}}\\oslash\\tilde{\\mathbf{k}}\\right):\\left[\\left(\\mathbf{k}^{-1}\\oslash\\mathbf{k}^{-1}\\right):\\boldsymbol{\\mathcal{K}}+\\boldsymbol{\\mathcal{G}}\\right]\\,,\\label{eq298}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{K}}=J^{-1}\\left(\\mathbf{F}\\oslash\\mathbf{F}\\right):2\\frac{\\partial\\mathbf{K}}{\\partial\\mathbf{C}}:\\left(\\mathbf{F}^{T}\\oslash\\mathbf{F}^{T}\\right)\\,,\\label{eq300}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{G}}=\\frac{R\\theta}{\\varphi^{w}}\\sum_{\\alpha}\\frac{\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\left(\\begin{aligned}\\left(\\frac{1}{\\varphi^{w}}-\\frac{J}{\\tilde{\\kappa}^{\\alpha}}\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial J}\\right)\\left(\\mathbf{I}-\\frac{\\mathbf{d}^{\\alpha}}{d_{0}^{\\alpha}}\\right)\\otimes\\mathbf{I}\\\\\n+2\\left(\\mathbf{I}\\odot\\frac{\\mathbf{d}^{\\alpha}}{d_{0}^{\\alpha}}+\\frac{\\mathbf{d}^{\\alpha}}{d_{0}^{\\alpha}}\\odot\\mathbf{I}-\\mathbf{I}\\odot\\mathbf{I}\\right)\\\\\n-\\frac{\\mathbf{d}^{\\alpha}}{d_{0}^{\\alpha}}\\otimes\\mathbf{I}+\\frac{\\boldsymbol{\\mathcal{D}}^{\\alpha}}{d_{0}^{\\alpha}}\n\\end{aligned}\n\\right)\\,.\\label{eq299}\n\\end{equation}\n\n\\end_inset\n\nThe next term in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n linearizes to \n\\begin_inset Formula \n\\begin{equation}\n-D\\left(\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=-\\delta\\tilde{p}\\frac{1}{\\Delta t}\\divg\\Delta\\mathbf{u}\\,dv\\,,\\label{eq301}\n\\end{equation}\n\n\\end_inset\n\nwhere we used a backward difference scheme to approximate the time derivative,\n \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial J}{\\partial t}\\approx\\frac{1}{\\Delta t}\\left(J-J^{-\\Delta t}\\right)\\,,\\label{eq302}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\Delta t$\n\\end_inset\n\n represents the time increment relative to the previous time point.\n The next term is given by \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{J}^{\\alpha}\\cdot\\Grad\\delta\\tilde{c}^{\\alpha}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\grad\\delta\\tilde{c}^{\\alpha}\\cdot\\mathbf{j}_{u}^{\\alpha\\prime}\\,dv\\,,\\label{eq303}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{u}^{\\alpha\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{J}\\left[\\Delta\\mathbf{u}\\right]=\\left(J\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial J}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{d}^{\\alpha}+\\tilde{\\kappa}^{\\alpha}\\boldsymbol{\\mathcal{D}}^{\\alpha}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\cdot\\mathbf{g}^{\\alpha}\\\\\n & +\\tilde{\\kappa}^{\\alpha}\\mathbf{d}^{\\alpha}\\cdot\\left(-\\varphi^{s}\\left(\\divg\\Delta\\mathbf{u}\\right)\\grad\\tilde{c}^{\\alpha}+\\left(2\\Delta\\boldsymbol{\\varepsilon}-\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}\\right)\\cdot\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}+\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}_{u}^{\\prime}\\right)\n\\end{aligned}\n\\,.\\label{eq304}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}^{\\alpha}=-\\varphi^{w}\\grad\\tilde{c}^{\\alpha}+\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}\\,.\\label{eq305}\n\\end{equation}\n\n\\end_inset\n\nUsing a backward difference scheme for the time derivative,\n the last term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta\\tilde{c}^{\\alpha}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)}{\\partial t}\\right)\\left[\\Delta\\mathbf{u}\\right]\\,dV=\\frac{\\delta\\tilde{c}^{\\alpha}}{\\Delta t}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\right)}{\\partial J}\\tilde{c}^{\\alpha}\\divg\\Delta\\mathbf{u}\\,dv\\,.\\label{eq306}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-linearization-dp\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the various terms in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\right)\\left[\\Delta\\tilde{p}\\right]\\,dV=-\\Delta\\tilde{p}\\,\\divg\\delta\\mathbf{v}\\,dv\\,,\\label{eq307}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{W}\\cdot\\Grad\\delta\\tilde{p}-\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\left[\\Delta\\tilde{p}\\right]\\,dV=-\\grad\\delta\\tilde{p}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad\\Delta\\tilde{p}\\,dv\\,,\\label{eq308}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{J}^{\\alpha}\\cdot\\Grad\\delta\\tilde{c}^{\\alpha}-\\delta\\tilde{c}^{\\alpha}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)}{\\partial t}\\right)\\left[\\Delta\\tilde{p}\\right]\\,dV=-\\frac{\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\grad\\delta\\tilde{c}^{\\alpha}\\cdot\\mathbf{d}^{\\alpha}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad\\Delta\\tilde{p}\\,dv\\,.\\label{eq309}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization along \n\\begin_inset Formula $\\Delta\\tilde{c}^{\\gamma}$\n\\end_inset\n\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-linearization-dc\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the first term in \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\tilde{c}^{\\gamma}$\n\\end_inset\n\n yields \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{S}:\\delta\\mathbf{\\dot{E}}\\right)\\left[\\Delta\\tilde{c}^{\\gamma}\\right]dV=\\Delta\\tilde{c}^{\\gamma}\\left(\\boldsymbol{\\sigma}_{\\gamma}^{\\prime}:\\delta\\mathbf{d}-R\\theta\\frac{\\partial\\left(\\Phi\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)}{\\partial\\tilde{c}^{\\gamma}}\\divg\\delta\\mathbf{v}\\right)\\,dv\\,,\\label{eq310}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}_{\\gamma}^{\\prime}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\mathbf{S}^{e}}{\\partial\\tilde{c}^{\\gamma}}\\cdot\\mathbf{F}^{T}\\label{eq311}\n\\end{equation}\n\n\\end_inset\n\nrepresents the spatial tangent of the stress with respect to the effective concentration.\n The next term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{W}\\cdot\\Grad\\delta\\tilde{p}\\right)\\left[\\Delta\\tilde{c}^{\\gamma}\\right]\\,dV=\\grad\\delta\\tilde{p}\\cdot\\mathbf{w}_{\\gamma}^{\\prime}\\,dv\\,,\\label{eq312}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{\\gamma}^{\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{W}\\left[\\Delta\\tilde{c}^{\\gamma}\\right]=-\\tilde{\\mathbf{k}}_{\\gamma}^{\\prime}\\cdot\\left(\\grad\\tilde{p}+R\\theta\\sum\\limits_{\\beta}\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\mathbf{d}^{\\beta}\\cdot\\grad\\tilde{c}^{\\beta}\\right)\\Delta\\tilde{c}^{\\gamma}\\\\\n & -R\\theta\\tilde{\\mathbf{k}}\\cdot\\frac{\\tilde{\\kappa}^{\\gamma}}{d_{0}^{\\gamma}}\\mathbf{d}^{\\gamma}\\cdot\\grad\\Delta\\tilde{c}^{\\gamma}-\\Delta\\tilde{c}^{\\gamma}R\\theta\\tilde{\\mathbf{k}}\\cdot\\sum\\limits_{\\beta}\\left(\\frac{\\partial}{\\partial\\tilde{c}^{\\gamma}}\\left(\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\right)\\mathbf{d}^{\\beta}+\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\mathbf{d}_{\\gamma}^{\\beta\\prime}\\right)\\cdot\\grad\\tilde{c}^{\\beta}\n\\end{aligned}\n\\label{eq313}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mathbf{k}}_{\\gamma}^{\\prime}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\tilde{\\mathbf{K}}}{\\partial\\tilde{c}^{\\gamma}}\\cdot\\mathbf{F}^{T},\\quad\\mathbf{d}_{\\gamma}^{\\beta\\prime}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\mathbf{d}^{\\beta}}{\\partial\\tilde{c}^{\\gamma}}\\cdot\\mathbf{F}^{T}\\label{eq314}\n\\end{equation}\n\n\\end_inset\n\nare the spatial tangents of the effective hydraulic permeability and solute diffusivity with respect to the effective concentration.\n\\end_layout\n\n\\begin_layout Standard\nThe next term reduces to \n\\begin_inset Formula \n\\begin{equation}\n-D\\left(\\delta\\tilde{p}\\frac{\\partial J}{\\partial t}\\right)\\left[\\Delta\\tilde{c}^{\\gamma}\\right]\\,dV=0\\,.\\label{eq315}\n\\end{equation}\n\n\\end_inset\n\nThe following term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\mathbf{J}^{\\alpha}\\cdot\\Grad\\delta\\tilde{c}^{\\alpha}\\right)\\left[\\Delta\\tilde{c}^{\\gamma}\\right]\\,dV=\\grad\\delta\\tilde{c}^{\\alpha}\\cdot\\mathbf{j}_{\\gamma}^{\\alpha\\prime}\\,dv\\,,\\label{eq316}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{\\gamma}^{\\alpha\\prime} & \\equiv J^{-1}\\mathbf{F}\\cdot D\\mathbf{J}^{\\alpha}\\left[\\Delta\\tilde{c}^{\\gamma}\\right]=\\Delta\\tilde{c}^{\\gamma}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}}\\mathbf{d}^{\\alpha}+\\tilde{\\kappa}^{\\alpha}\\mathbf{d}_{\\gamma}^{\\alpha\\prime}\\right)\\cdot\\mathbf{g}^{\\alpha}\\\\\n & +\\tilde{\\kappa}^{\\alpha}\\mathbf{d}^{\\alpha}\\cdot\\left(-\\varphi^{w}\\delta_{\\alpha\\gamma}\\grad\\Delta\\tilde{c}^{\\gamma}+\\frac{\\Delta\\tilde{c}^{\\gamma}}{d_{0}^{\\alpha}}\\left(\\delta_{\\alpha\\gamma}-\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\frac{\\partial d_{0}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}}\\right)\\mathbf{w}+\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}_{\\gamma}^{\\prime}\\right)\\,.\n\\end{aligned}\n\\label{eq317}\n\\end{equation}\n\n\\end_inset\n\nThe last term is \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)}{\\partial t}\\delta\\tilde{c}^{\\alpha}\\right)\\left[\\Delta\\tilde{c}^{\\gamma}\\right]\\,dV=\\delta\\tilde{c}^{\\alpha}\\frac{\\varphi^{w}}{\\Delta t}\\frac{\\partial\\left(\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)}{\\partial\\tilde{c}^{\\gamma}}\\Delta\\tilde{c}^{\\gamma}\\,dv\\,,\\label{eq318}\n\\end{equation}\n\n\\end_inset\n\nwhere we similarly used a backward difference scheme to discretize the time derivative.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of External Virtual Work\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-linearization-external\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq289\"\nnolink \"false\"\n\n\\end_inset\n\n depends on whether natural boundary conditions are prescribed as area densities or total net values over an area.\n Thus,\n in the case when \n\\begin_inset Formula $\\mathbf{t}\\,da$\n\\end_inset\n\n (net force),\n \n\\begin_inset Formula $w_{n}da$\n\\end_inset\n\n (net volumetric flow rate),\n or \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}da$\n\\end_inset\n\n (net effective molar flow rate) are prescribed over the elemental area \n\\begin_inset Formula $da$\n\\end_inset\n\n,\n there is no variation in \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n and it follows that \n\\begin_inset Formula $D\\delta W_{\\text{ext}}=0$\n\\end_inset\n\n.\n Alternatively,\n in the case when \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n or \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}$\n\\end_inset\n\n are prescribed,\n the linearization may be performed by evaluating the integral in the parametric space of the boundary surface \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n with parametric coordinates \n\\begin_inset Formula $\\left(\\eta^{1},\\eta^{2}\\right)$\n\\end_inset\n\n.\n Accordingly,\n for a point \n\\begin_inset Formula $\\mathbf{x}\\left(\\eta^{1},\\eta^{2}\\right)$\n\\end_inset\n\n on \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n surface tangents (covariant basis vectors) are given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}=\\frac{\\partial\\mathbf{x}}{\\partial\\eta^{\\alpha}},\\quad\\left(\\alpha=1,2\\right)\\label{eq319}\n\\end{equation}\n\n\\end_inset\n\nand the outward unit normal is \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{g}_{1}\\times\\mathbf{g}_{2}}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,.\\label{eq320}\n\\end{equation}\n\n\\end_inset\n\nThe elemental area on \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n is \n\\begin_inset Formula $da=\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|d\\eta^{1}d\\eta^{2}$\n\\end_inset\n\n.\n Consequently,\n the external virtual work integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\int_{\\partial b}\\left(\\delta\\mathbf{v}\\cdot\\mathbf{t}+\\delta\\tilde{p}\\,w_{n}+\\sum\\limits_{\\alpha\\ne s,w}\\delta\\tilde{c}^{\\alpha}\\tilde{j}_{n}^{\\alpha}\\right)\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\,d\\eta^{1}d\\eta^{2}\\,,\\label{eq321}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{j}_{n}^{\\alpha}=j_{n}^{\\alpha}+\\sum\\limits_{\\beta\\ne s,w}z^{\\beta}j_{n}^{\\beta}\\,.\\label{eq322}\n\\end{equation}\n\n\\end_inset\n\nThe directional derivative of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n may then be applied directly to its integrand,\n since the parametric space is invariant \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIf we restrict traction boundary conditions to the special case of normal tractions,\n then \n\\begin_inset Formula $\\mathbf{t}=t_{n}\\mathbf{n}$\n\\end_inset\n\n where \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is the prescribed normal traction component.\n Then it can be shown that the linearization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n produces \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta W_{\\text{ext}}\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int_{\\partial b}\\left(t_{n}\\delta\\mathbf{v}+w_{n}\\delta\\tilde{p}\\,\\mathbf{n}+\\sum\\limits_{\\alpha\\ne s,w}\\delta\\tilde{c}^{\\alpha}\\tilde{j}_{n}^{\\alpha}\\,\\mathbf{n}\\right)\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\times\\mathbf{g}_{2}+\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}\\right)\\,d\\eta^{1}d\\eta^{2}\\,.\\label{eq323}\n\\end{equation}\n\n\\end_inset\n\nThe linearizations along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}^{\\gamma}$\n\\end_inset\n\n reduce to zero,\n \n\\begin_inset Formula $D\\left(\\delta W_{\\text{ext}}\\right)\\left[\\Delta\\tilde{p}\\right]=0$\n\\end_inset\n\n and \n\\begin_inset Formula $D\\left(\\delta W_{\\text{ext}}\\right)\\left[\\Delta\\tilde{c}^{\\gamma}\\right]=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo discretize the virtual work relations,\n let \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\mathbf{v}_{a}\\,, & \\Delta\\mathbf{u} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\mathbf{u}_{b}\\,,\\\\\n\\delta\\tilde{p} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\tilde{p}_{a}\\,, & \\Delta\\tilde{p} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\tilde{p}_{b}\\,,\\\\\n\\delta\\tilde{c}^{\\alpha} & =\\sum\\limits_{a=1}^{m}N_{a}\\delta\\tilde{c}_{a}^{\\alpha}\\,, & \\Delta\\tilde{c}^{\\gamma} & =\\sum\\limits_{b=1}^{m}N_{b}\\Delta\\tilde{c}_{b}^{\\gamma}\\,,\n\\end{aligned}\n\\label{eq324}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}$\n\\end_inset\n\n represents the interpolation functions over an element,\n \n\\begin_inset Formula $\\delta\\mathbf{v}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{p}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{c}_{a}^{\\alpha}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\tilde{p}_{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}_{a}^{\\gamma}$\n\\end_inset\n\n respectively represent the nodal values of \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{p}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\tilde{c}^{\\alpha}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\tilde{c}^{\\gamma}$\n\\end_inset\n\n;\n \n\\begin_inset Formula $m$\n\\end_inset\n\n is the number of nodes in an element.\n\\end_layout\n\n\\begin_layout Standard\nThe discretized form of \n\\begin_inset Formula $\\delta W_{\\text{int}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq226\"\nnolink \"false\"\n\n\\end_inset\n\n may be written as \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{int}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}^{\\alpha} & \\delta\\tilde{c}_{a}^{\\beta}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{r}_{a}^{u}\\\\\nr_{a}^{p}\\\\\nr_{a}^{\\alpha}\\\\\nr_{a}^{\\beta}\n\\end{array}\\right]\\,,\\label{eq325}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $n_{e}$\n\\end_inset\n\n is the number of elements in \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n \n\\begin_inset Formula $n_{\\text{int}}^{\\left(e\\right)}$\n\\end_inset\n\n is the number of integration points in the \n\\begin_inset Formula $e-$\n\\end_inset\n\nth element,\n \n\\begin_inset Formula $W_{k}$\n\\end_inset\n\n is the quadrature weight associated with the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point,\n and \n\\begin_inset Formula $J_{\\eta}$\n\\end_inset\n\n is the Jacobian of the transformation from the current spatial configuration to the parametric space of the element.\n In the above expression,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{r}_{a}^{u} & =\\boldsymbol{\\sigma}\\cdot\\grad N_{a}\\,,\\\\\nr_{a}^{p} & =\\mathbf{w}\\cdot\\grad N_{a}-N_{a}\\frac{1}{J}\\frac{\\partial J}{\\partial t}\\,,\\\\\nr_{a}^{\\alpha} & =\\mathbf{j}^{\\alpha}\\cdot\\grad N_{a}-N_{a}\\frac{1}{J}\\frac{\\partial}{\\partial t}\\left(J\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}\\right)\\,,\n\\end{aligned}\n\\label{eq326}\n\\end{equation}\n\n\\end_inset\n\nand it is understood that \n\\begin_inset Formula $J_{\\eta}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{r}_{a}^{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $r_{a}^{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $r_{a}^{\\alpha}$\n\\end_inset\n\n are evaluated at the parametric coordinates of the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point.\n Since the parametric space is invariant,\n time derivatives are evaluated in a material frame.\n For example,\n the time derivative \n\\begin_inset Formula $D^{s}J\\left(\\mathbf{x},t\\right)/Dt$\n\\end_inset\n\n appearing in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq226\"\nnolink \"false\"\n\n\\end_inset\n\n becomes \n\\begin_inset Formula $\\partial J\\left(\\eta_{k},t\\right)/\\partial t$\n\\end_inset\n\n when evaluated at the parametric coordinates \n\\begin_inset Formula $\\eta_{k}=\\left(\\eta_{k}^{1},\\eta_{k}^{2},\\eta_{k}^{3}\\right)$\n\\end_inset\n\n of the \n\\begin_inset Formula $k-$\n\\end_inset\n\nth integration point.\n All time derivatives are discretized using a backward difference scheme.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n the discretized form of \n\\begin_inset Formula $D\\delta W_{\\text{int}}=D\\delta W_{\\text{int}}\\left[\\Delta\\mathbf{u}\\right]+D\\delta W_{\\text{int}}\\left[\\Delta\\tilde{p}\\right]+\\sum\\nolimits_{\\gamma}D\\delta W_{\\text{int}}\\left[\\Delta\\tilde{c}^{\\gamma}\\right]$\n\\end_inset\n\n may be written as \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{\\text{int}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}^{\\alpha} & \\delta\\tilde{c}_{a}^{\\beta}\\end{array}\\right]\\cdot\\left[\\begin{array}{cccc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{k}_{ab}^{up} & \\mathbf{k}_{ab}^{u\\alpha} & \\mathbf{k}_{ab}^{u\\beta}\\\\\n\\mathbf{k}_{ab}^{pu} & k_{ab}^{pp} & k_{ab}^{p\\alpha} & k_{ab}^{p\\beta}\\\\\n\\mathbf{k}_{ab}^{\\alpha u} & k_{ab}^{\\alpha p} & k_{ab}^{\\alpha\\alpha} & k_{ab}^{\\alpha\\beta}\\\\\n\\mathbf{k}_{ab}^{\\beta u} & k_{ab}^{\\beta p} & k_{ab}^{\\beta\\alpha} & k_{ab}^{\\beta\\beta}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\tilde{p}_{b}\\\\\n\\Delta\\tilde{c}_{b}^{\\alpha}\\\\\n\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{array}\\right]\\,,\\label{eq327}\n\\end{equation}\n\n\\end_inset\n\nwhere the terms in the first column are the discretized form of the linearization along \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{ab}^{uu}=\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad N_{b}+\\left(\\grad N_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad N_{b}\\right)\\mathbf{I}\\,,\\label{eq328}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{pu}=\\left(\\mathbf{w}_{b}^{u}\\right)^{T}\\cdot\\grad N_{a}+N_{a}\\mathbf{q}_{b}^{pu}\\,,\\label{eq329}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{\\alpha u}=\\left(\\mathbf{j}_{b}^{\\alpha u}+\\sum\\nolimits_{\\beta}z^{\\beta}\\mathbf{j}_{b}^{\\beta u}\\right)^{T}\\cdot\\grad N_{a}+N_{a}\\mathbf{q}_{b}^{\\alpha u}\\,,\\label{eq330}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{b}^{u} & =\\mathbf{g}^{p}\\cdot\\tilde{\\boldsymbol{\\mathcal{K}}}\\cdot\\grad N_{b}\\\\\n & -R\\theta\\sum_{\\beta}\\frac{1}{d_{0}^{\\beta}}\\left(J\\frac{\\partial\\tilde{\\kappa}^{\\beta}}{\\partial J}-\\tilde{\\kappa}^{\\beta}\\right)\\tilde{\\mathbf{k}}\\cdot\\mathbf{d}^{\\beta}\\cdot\\left(\\grad\\tilde{c}^{\\beta}\\otimes\\grad N_{b}\\right)\\\\\n & -R\\theta\\sum_{\\beta}\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\left[\\tilde{\\mathbf{k}}\\cdot\\left(\\grad N_{b}\\otimes\\grad\\tilde{c}^{\\beta}\\right)\\cdot\\mathbf{d}^{\\beta}+\\left(\\grad N_{b}\\cdot\\mathbf{d}^{\\beta}\\cdot\\grad\\tilde{c}^{\\beta}\\right)\\tilde{\\mathbf{k}}\\right]\\\\\n & -R\\theta\\sum_{\\beta}\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\tilde{\\mathbf{k}}\\cdot\\left(\\grad\\tilde{c}^{\\beta}\\cdot\\boldsymbol{\\mathcal{D}}^{\\beta}\\cdot\\grad N_{b}\\right)\\,,\n\\end{aligned}\n\\label{eq330b}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}^{p}=-\\grad\\tilde{p}-R\\theta\\sum\\limits_{\\beta}\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\mathbf{d}^{\\beta}\\cdot\\grad\\tilde{c}^{\\beta}\\,.\\label{eq344}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{b}^{\\alpha u} & =J\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial J}\\mathbf{d}^{\\alpha}\\cdot\\left(\\mathbf{g}^{\\alpha}\\otimes\\grad N_{b}\\right)+\\tilde{\\kappa}^{\\alpha}\\mathbf{g}^{\\alpha}\\cdot\\boldsymbol{\\mathcal{D}}^{\\alpha}\\cdot\\grad N_{b}\\\\\n & +\\tilde{\\kappa}^{\\alpha}\\mathbf{d}^{\\alpha}\\cdot\\left(\\grad N_{b}\\otimes\\mathbf{w}-\\mathbf{w}\\otimes\\grad N_{b}+\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}+\\mathbf{w}_{b}^{u}\\right)\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\\\\n & -\\varphi^{s}\\tilde{\\kappa}^{\\alpha}\\mathbf{d}^{\\alpha}\\cdot\\grad\\tilde{c}^{\\alpha}\\otimes\\grad N_{b}\\,,\n\\end{aligned}\n\\label{eq331}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}^{\\alpha}=-\\varphi^{w}\\grad\\tilde{c}^{\\alpha}+\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}\\,,\\label{eq331b}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{q}_{b}^{pu}=-\\frac{1}{\\Delta t}\\grad N_{b}\\,,\\label{eq332}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{q}_{b}^{\\alpha u}=\\tilde{c}^{\\alpha}\\frac{\\partial\\left(J\\varphi^{w}\\tilde{\\kappa}^{\\alpha}\\right)}{\\partial J}\\mathbf{q}_{b}^{pu}\\,.\\label{eq333}\n\\end{equation}\n\n\\end_inset\n\nThe terms in the second column of the stiffness matrix in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq267\"\nnolink \"false\"\n\n\\end_inset\n\n are the discretized form of the linearization along \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{up}=-N_{b}\\grad N_{a}\\,,\\label{eq334}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{pp}=-\\grad N_{a}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad N_{b}\\,,\\label{eq335}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{\\alpha p}=\\grad N_{a}\\cdot\\left(\\mathbf{j}_{b}^{\\alpha p}+\\sum\\limits_{\\beta}z^{\\beta}\\mathbf{j}_{b}^{\\beta p}\\right)\\,,\\label{eq336}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{j}_{b}^{\\alpha p}=-\\frac{\\tilde{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{d}^{\\alpha}\\cdot\\tilde{\\mathbf{k}}\\cdot\\grad N_{b}\\,.\\label{eq337}\n\\end{equation}\n\n\\end_inset\n\nThe terms in the third column of the stiffness matrix in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq267\"\nnolink \"false\"\n\n\\end_inset\n\n are the discretized form of the linearization along \n\\begin_inset Formula $\\Delta\\tilde{c}^{\\gamma}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{u\\alpha}=N_{b}\\left(\\boldsymbol{\\sigma}_{\\alpha}^{\\prime}-R\\theta\\left[\\Phi\\tilde{\\kappa}^{\\alpha}+\\sum\\limits_{\\beta}\\left(\\frac{\\partial\\Phi}{\\partial\\tilde{c}^{\\alpha}}\\tilde{\\kappa}^{\\beta}+\\Phi\\frac{\\partial\\tilde{\\kappa}^{\\beta}}{\\partial\\tilde{c}^{\\alpha}}\\right)\\tilde{c}^{\\beta}\\right]\\mathbf{I}\\right)\\cdot\\grad N_{a}\\,,\\label{eq338}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{p\\alpha}=\\grad N_{a}\\cdot\\mathbf{w}_{b}^{\\alpha}\\,,\\label{eq339}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{\\alpha\\gamma}=\\grad N_{a}\\cdot\\left(\\mathbf{j}_{b}^{\\alpha\\gamma}+\\sum\\limits_{\\beta}z^{\\beta}\\mathbf{j}_{b}^{\\beta\\gamma}\\right)+N_{a}q_{b}^{\\alpha\\gamma}\\,,\\label{eq340}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}_{b}^{\\gamma} & =N_{b}\\left(\\tilde{\\mathbf{k}}_{\\gamma}^{\\prime}\\cdot\\mathbf{g}^{p}-R\\theta\\tilde{\\mathbf{k}}\\cdot\\sum\\limits_{\\beta}\\left(\\frac{\\partial}{\\partial\\tilde{c}^{\\gamma}}\\left(\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\right)\\mathbf{d}^{\\beta}+\\frac{\\tilde{\\kappa}^{\\beta}}{d_{0}^{\\beta}}\\mathbf{d}_{c}^{\\beta\\gamma}\\right)\\cdot\\grad\\tilde{c}^{\\beta}\\right)\\\\\n & -R\\theta\\tilde{\\mathbf{k}}\\cdot\\frac{\\tilde{\\kappa}^{\\gamma}}{d_{0}^{\\gamma}}\\mathbf{d}^{\\gamma}\\cdot\\grad N_{b}\\,,\n\\end{aligned}\n\\label{eq341}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{j}_{b}^{\\alpha\\gamma} & =N_{b}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}}\\mathbf{d}^{\\alpha}+\\tilde{\\kappa}^{\\alpha}\\mathbf{d}_{c}^{\\alpha\\gamma}\\right)\\cdot\\mathbf{g}^{\\alpha}\\\\\n & +\\frac{\\tilde{\\kappa}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{d}^{\\alpha}\\cdot\\left[\\delta_{\\alpha\\gamma}\\left(N_{b}\\mathbf{w}-\\varphi^{w}d_{0}^{\\alpha}\\grad N_{b}\\right)+\\tilde{c}^{\\alpha}\\left(\\mathbf{w}_{b}^{\\gamma}-\\frac{N_{b}}{d_{0}^{\\alpha}}\\frac{\\partial d_{0}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}}\\mathbf{w}\\right)\\right]\\,,\n\\end{aligned}\n\\label{eq342}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nq_{b}^{\\alpha\\gamma}=-N_{b}\\frac{\\varphi^{w}}{\\Delta t}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}}\\tilde{c}^{\\alpha}+\\delta_{\\alpha\\gamma}\\tilde{\\kappa}^{\\alpha}\\right)\\,.\\label{eq343}\n\\end{equation}\n\n\\end_inset\n\nThe discretization of \n\\begin_inset Formula $\\delta W_{\\text{ext}}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq321\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\series bold\n \n\\series default\nhas the form \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{ext}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\text{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}^{\\alpha} & \\delta\\tilde{c}_{a}^{\\beta}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\nN_{a}t_{n}\\mathbf{n}\\\\\nN_{a}w_{n}\\\\\nN_{a}\\tilde{j}_{n}^{\\alpha}\\\\\nN_{a}\\tilde{j}_{n}^{\\beta}\n\\end{array}\\right]\\,,\\label{eq345}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}=\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|$\n\\end_inset\n\n.\n The summation is performed over all surface elements on which these boundary conditions are prescribed.\n The discretization of \n\\begin_inset Formula $-D\\delta W_{\\text{ext}}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\n-D\\delta W_{\\text{ext}}=\\sum\\limits_{e=1}^{n_{e}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}\\sum\\limits_{a=1}^{m}\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a} & \\delta\\tilde{p}_{a} & \\delta\\tilde{c}_{a}^{\\alpha} & \\delta\\tilde{c}_{a}^{\\beta}\\end{array}\\right]\\cdot\\left[\\begin{array}{cccc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{0} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{k}_{ab}^{pu} & 0 & 0 & 0\\\\\n\\mathbf{k}_{ab}^{\\alpha u} & 0 & 0 & 0\\\\\n\\mathbf{k}_{ab}^{\\beta u} & 0 & 0 & 0\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\tilde{p}_{b}\\\\\n\\Delta\\tilde{c}_{b}^{\\alpha}\\\\\n\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{array}\\right]\\,,\\label{eq346}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =t_{n}N_{a}\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right\\} \\,,\\\\\n\\mathbf{k}_{ab}^{pu} & =-w_{n}N_{a}\\left(\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right)\\times\\mathbf{n},\\\\\n\\mathbf{k}_{ab}^{\\alpha u} & =-\\tilde{j}_{n}^{\\alpha}N_{a}\\left(\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}-\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}\\right)\\times\\mathbf{n}.\n\\end{aligned}\n\\label{eq347}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{A}}\\left\\{ \\mathbf{v}\\right\\} $\n\\end_inset\n\n is the antisymmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n (such that \n\\begin_inset Formula $\\boldsymbol{\\mathcal{A}}\\left\\{ \\mathbf{v}\\right\\} \\cdot\\mathbf{q}=\\mathbf{v}\\times\\mathbf{q}$\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{q})$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nElectric Potential and Partition Coefficient Derivatives\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Potential-partition-derivatives\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the mixture is charged it is necessary to solve for the electric potential \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n using the electroneutrality condition in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq124\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This equation may be rewritted as a polynomial in \n\\begin_inset Formula $\\zeta$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{i=0}^{n}a_{i}\\zeta^{i}\\,,\\label{eq348}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\zeta=\\exp\\left(-\\frac{F_{c}\\psi}{R\\theta}\\right)\\,,\\label{eq349}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\na_{i}=\\begin{cases}\nz^{\\alpha}\\hat{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha} & i=z^{\\alpha}-z^{\\min}\\\\\nc^{F} & i=-z^{\\min}\n\\end{cases}\\,.\\label{eq350}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $z^{\\min}=\\min_{\\alpha}z^{\\alpha}$\n\\end_inset\n\n and the polynomial degress is \n\\begin_inset Formula $n=z^{\\max}-z^{\\min}$\n\\end_inset\n\n where \n\\begin_inset Formula $z^{\\max}=\\max_{\\alpha}z^{\\alpha}$\n\\end_inset\n\n.\n Since more than one solute may carry the same charge \n\\begin_inset Formula $z^{\\alpha}$\n\\end_inset\n\n,\n the coefficients \n\\begin_inset Formula $a_{i}$\n\\end_inset\n\n should be evaluated from the summation of \n\\begin_inset Formula $z^{\\alpha}\\hat{\\kappa}^{\\alpha}\\tilde{c}^{\\alpha}$\n\\end_inset\n\n over all such solutes.\n Only real positive roots are valid,\n since \n\\begin_inset Formula $\\psi=-R\\theta\\left(\\ln\\zeta\\right)/F_{c}$\n\\end_inset\n\n according to \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq349\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Using Descartes' rule of signs,\n an inspection of the coefficients \n\\begin_inset Formula $a_{i}$\n\\end_inset\n\n shows tht there is only one sign change in the polynomial,\n regardless of the sign of \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n,\n implying that there will always be only one positive root \n\\begin_inset Formula $\\zeta$\n\\end_inset\n\n,\n which must thus be real.\n Therefore,\n there cannot be any ambiguity in the calculation of \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n,\n irrespective of the polynomial degree.\n Newton's method is used to solve for the positive real root when \n\\begin_inset Formula $n>2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nUsing the above relations,\n it follows that \n\\begin_inset Formula $\\tilde{\\kappa}^{\\alpha}=\\hat{\\kappa}^{\\alpha}\\zeta^{z^{\\alpha}}$\n\\end_inset\n\n.\n An examination of the equations resulting from the linearization of the internal virtual work shows that it is necessary to evaluate derivatives of \n\\begin_inset Formula $\\tilde{\\kappa}^{\\alpha}$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $J$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}^{\\gamma}$\n\\end_inset\n\n,\n which are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial J} & =\\frac{\\partial\\hat{\\kappa}^{\\alpha}}{\\partial J}\\zeta^{z^{\\alpha}}+z^{\\alpha}\\tilde{\\kappa}^{\\alpha}\\frac{1}{\\zeta}\\frac{\\partial\\zeta}{\\partial J}\\\\\n\\frac{\\partial\\tilde{\\kappa}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}} & =\\frac{\\partial\\hat{\\kappa}^{\\alpha}}{\\partial\\tilde{c}^{\\gamma}}\\zeta^{z^{\\alpha}}+z^{\\alpha}\\tilde{\\kappa}^{\\alpha}\\frac{1}{\\zeta}\\frac{\\partial\\zeta}{\\partial\\tilde{c}^{\\gamma}}\n\\end{aligned}\n\\,.\\label{eq351}\n\\end{equation}\n\n\\end_inset\n\nIn these expressions,\n the derivatives of \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}$\n\\end_inset\n\n are obtained from the user-defined constitutive relations for the solubility.\n Derivatives of \n\\begin_inset Formula $\\zeta$\n\\end_inset\n\n may be evaluated by differentiating the electroneutrality condition to produce \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{1}{\\zeta}\\frac{\\partial\\zeta}{\\partial J} & =-\\frac{\\frac{\\partial c^{F}}{\\partial J}+\\sum\\nolimits_{\\beta}z^{\\beta}\\zeta^{z^{\\beta}}\\tilde{c}^{\\beta}\\frac{\\partial\\hat{\\kappa}^{\\beta}}{\\partial J}}{\\sum\\nolimits_{\\beta}\\left(z^{\\beta}\\right)^{2}\\tilde{\\kappa}^{\\beta}\\tilde{c}^{\\beta}}\\\\\n\\frac{1}{\\zeta}\\frac{\\partial\\zeta}{\\partial\\tilde{c}^{\\gamma}} & =-\\frac{z^{\\gamma}\\tilde{\\kappa}^{\\gamma}+\\sum\\nolimits_{\\beta}z^{\\beta}\\zeta^{z^{\\beta}}\\tilde{c}^{\\beta}\\frac{\\partial\\hat{\\kappa}^{\\beta}}{\\partial\\tilde{c}^{\\gamma}}}{\\sum\\nolimits_{\\beta}\\left(z^{\\beta}\\right)^{2}\\tilde{\\kappa}^{\\beta}\\tilde{c}^{\\beta}}\n\\end{aligned}\n\\,.\\label{eq352}\n\\end{equation}\n\n\\end_inset\n\nThe derivative \n\\begin_inset Formula $\\partial c^{F}/\\partial J$\n\\end_inset\n\n may be evaluated from \n\\begin_inset Formula \n\\begin{equation}\nc^{F}=\\frac{1-\\varphi_{r}^{s}}{J-\\varphi_{r}^{s}}c_{r}^{F}\\,,\\label{eq353}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential solid volume fraction (volume of solid in current configuration per volume of the mixture in the reference configuration) and \n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n is the referential fixed charge density (equivalent charge in current configuration per volume of the mixture in the reference configuration).\n\\end_layout\n\n\\begin_layout Subsection\nChemical Reactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-chemical-reactions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nVirtual Work and Linearization\n\\end_layout\n\n\\begin_layout Standard\nThe contribution to \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n due to chemical reactions is given by \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n,\n where \n\\begin_inset Formula \n\\[\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\hat{\\varphi}^{w}+\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\sum_{\\alpha}\\nu^{\\alpha}\\mathcal{V}^{\\alpha}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\int_{b}\\delta\\tilde{p}\\left[\\hat{\\varphi}^{w}+\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\overline{\\mathcal{V}}\\right]\\,dv+\\sum\\limits_{\\iota}\\nu^{\\iota}\\int_{b}\\delta\\tilde{c}^{\\iota}\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\,dv\\,.\\label{eq354}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n along a solid displacement increment \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G\\left[\\Delta\\mathbf{u}\\right] & =\\int_{b}\\delta\\tilde{p}\\,\\left(\\hat{\\varphi}^{w}\\,\\divg\\Delta\\mathbf{u}+\\hat{\\boldsymbol{\\varphi}}_{\\varepsilon}^{w}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\,dv\\\\\n & +\\overline{\\mathcal{V}}\\int_{b}\\delta\\tilde{p}\\left[\\hat{\\zeta}\\,\\divg\\Delta\\mathbf{u}+\\left(J-\\varphi_{r}^{s}\\right)\\hat{\\boldsymbol{\\zeta}}_{\\varepsilon}:\\Delta\\boldsymbol{\\varepsilon}\\right]\\,dv\\\\\n & +\\sum_{\\iota}\\nu^{\\iota}\\int_{b}\\delta\\tilde{c}^{\\iota}\\left[\\hat{\\zeta}\\left(1-\\frac{\\partial\\varphi_{r}^{s}}{\\partial J}\\right)\\,\\divg\\Delta\\mathbf{u}+\\left(J-\\varphi_{r}^{s}\\right)\\hat{\\boldsymbol{\\zeta}}_{\\varepsilon}:\\Delta\\boldsymbol{\\varepsilon}\\right]\\,dv\n\\end{aligned}\n\\,,\\label{eq354b}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\boldsymbol{\\varphi}}_{\\varepsilon}^{w}=\\mathbf{F}\\cdot\\frac{\\partial\\hat{\\varphi}^{w}}{\\partial\\mathbf{E}}\\cdot\\mathbf{F}^{T}\\,,\\quad\\hat{\\boldsymbol{\\zeta}}_{\\varepsilon}=J^{-1}\\mathbf{F}\\cdot\\frac{\\partial\\hat{\\zeta}}{\\partial\\mathbf{E}}\\cdot\\mathbf{F}^{T}\\,,\\label{eq354c}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\frac{\\partial\\varphi_{r}^{s}}{\\partial J}=\\frac{\\sum_{\\sigma}\\frac{M^{\\sigma}}{\\rho_{T}^{\\sigma}}c^{\\sigma}\\left(1+\\sum_{\\sigma}\\frac{M^{\\sigma}}{\\rho_{T}^{\\sigma}}c^{\\sigma}\\right)+\\left(J-\\varphi_{0}^{s}\\right)\\sum_{\\sigma}\\frac{M^{\\sigma}}{\\rho_{T}^{\\sigma}}\\frac{1}{\\tilde{\\kappa}^{\\sigma}}\\frac{\\partial\\tilde{\\kappa}^{\\sigma}}{\\partial J}c^{\\sigma}}{\\left(1+\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}\\right)^{2}}\n\\]\n\n\\end_inset\n\nfor solutes-as-SBMs,\n and\n\\begin_inset Formula \n\\[\n\\frac{\\partial\\varphi_{r}^{s}}{\\partial J}=\\frac{\\sum_{\\sigma}\\frac{\\rho_{r}^{\\sigma}}{\\rho_{T}^{\\sigma}}\\left(J\\varphi^{w}+\\sum_{\\sigma}\\frac{\\rho_{r}^{\\sigma}}{\\rho_{T}^{\\sigma}}\\right)+J\\varphi^{w}\\left(J-\\varphi_{0}^{s}\\right)\\sum_{\\sigma}\\frac{\\rho_{r}^{\\sigma}}{\\rho_{T}^{\\sigma}}\\frac{1}{\\tilde{\\kappa}^{\\sigma}}\\frac{\\partial\\tilde{\\kappa}^{\\sigma}}{\\partial J}}{\\left(J\\varphi^{w}+\\sum\\limits_{\\sigma}\\frac{\\rho_{r}^{\\sigma}}{\\rho_{T}^{\\sigma}}\\right)^{2}}\n\\]\n\n\\end_inset\n\nfor SBMs,\n as obtained from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:phirs-solutes-as-SBMs\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Currently,\n \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n is assumed to be independent of \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n in FEBio;\n it follows that the linearization along the effective fluid pressure increment \n\\begin_inset Formula $\\Delta\\tilde{p}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\tilde{p}\\right]=\\int_{b}\\delta\\tilde{p}\\,\\frac{\\partial\\hat{\\varphi}^{w}}{\\partial\\tilde{p}}\\Delta p\\,dv\\,.\\label{eq354d}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n the linearization along a concentration increment \n\\begin_inset Formula $\\Delta\\tilde{c}^{\\iota}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G\\left[\\Delta\\tilde{c}^{\\iota}\\right] & =\\int_{b}\\delta\\tilde{p}\\,\\frac{\\partial\\hat{\\varphi}^{w}}{\\partial\\tilde{c}^{\\iota}}\\Delta\\tilde{c}^{\\iota}\\,dv\\\\\n & +\\overline{\\mathcal{V}}\\int_{b}\\delta\\tilde{p}\\left(1-\\varphi^{s}\\right)\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\iota}}\\Delta\\tilde{c}^{\\iota}\\,dv\\\\\n & +\\sum_{\\gamma}\\nu^{\\gamma}\\int_{b}\\delta\\tilde{c}^{\\gamma}\\left(\\left(1-\\varphi^{s}\\right)\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\iota}}-\\frac{1}{J}\\frac{\\partial\\varphi_{r}^{s}}{\\partial\\tilde{c}^{\\iota}}\\hat{\\zeta}\\right)\\Delta\\tilde{c}^{\\iota}\\,dv\n\\end{aligned}\n\\,.\\label{eq354e}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\frac{\\partial\\varphi_{r}^{s}}{\\partial\\tilde{c}^{\\iota}}=\\begin{cases}\n\\frac{\\left(J-\\varphi_{r}^{s}\\right)^{2}}{J-\\varphi_{0}^{s}}\\sum_{\\sigma}\\frac{M^{\\sigma}}{\\rho_{T}^{\\sigma}}\\left(\\tilde{\\kappa}^{\\sigma}+\\tilde{c}^{\\sigma}\\frac{\\partial\\tilde{\\kappa}^{\\sigma}}{\\partial\\tilde{c}^{\\sigma}}\\right) & \\iota=\\sigma\\\\\n\\frac{\\left(J-\\varphi_{r}^{s}\\right)^{2}}{J-\\varphi_{0}^{s}}\\sum_{\\sigma}\\frac{M^{\\alpha}}{\\rho_{T}^{\\sigma}}\\tilde{c}^{\\sigma}\\frac{\\partial\\tilde{\\kappa}^{\\sigma}}{\\partial\\tilde{c}^{\\iota}} & \\iota\\ne\\sigma\n\\end{cases}\n\\]\n\n\\end_inset\n\nfor solutes-as-SBMs,\n as obtained from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:phirs-solutes-as-SBMs\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe discretized form of these expressions is given by\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\sum_{a}\\delta\\tilde{p}_{a}r_{a}^{p}+\\sum_{\\gamma}\\sum_{a}\\delta\\tilde{c}_{a}^{\\gamma}r_{a}^{\\gamma}\\,,\\label{eq354f}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}r_{a}^{p} & =\\int_{b}N_{a}\\,\\left[\\hat{\\varphi}^{w}+\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\bar{\\mathcal{V}}\\right]\\,dv\\\\\nr_{a}^{\\gamma} & =\\nu^{\\gamma}\\int_{b}N_{a}\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\,dv\n\\end{aligned}\n\\,.\\label{eq354g}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G\\left[\\Delta\\mathbf{u}\\right] & =\\sum_{a}\\delta\\tilde{p}_{a}\\sum_{b}\\mathbf{k}_{ab}^{pu}\\cdot\\Delta\\mathbf{u}_{b}\\\\\n & +\\sum_{\\gamma}\\sum_{a}\\delta\\tilde{c}_{a}^{\\gamma}\\sum_{b}\\mathbf{k}_{ab}^{\\gamma u}\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\,,\\label{eq354h}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ab}^{pu} & =\\int_{b}N_{a}\\left(\\hat{\\varphi}^{w}\\mathbf{I}+\\hat{\\boldsymbol{\\varphi}}_{\\varepsilon}^{w}\\right)\\cdot\\grad N_{b}\\,dv\\\\\n & +\\bar{\\mathcal{V}}\\int_{a}N_{a}\\left[\\hat{\\zeta}\\mathbf{I}+J\\varphi^{w}\\hat{\\boldsymbol{\\zeta}}_{\\varepsilon}\\right]\\cdot\\grad N_{b}\\,dv\\\\\n\\mathbf{k}_{ab}^{\\gamma u} & =\\nu^{\\gamma}\\int_{a}N_{a}\\left[\\left(1-\\frac{\\partial\\varphi_{r}^{s}}{\\partial J}\\right)\\hat{\\zeta}\\mathbf{I}+J\\varphi^{w}\\hat{\\boldsymbol{\\zeta}}_{\\varepsilon}\\right]\\cdot\\grad N_{b}\\,dv\n\\end{aligned}\n\\,.\\label{eq354i}\n\\end{equation}\n\n\\end_inset\n\nThen,\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\tilde{p}\\right]=\\sum_{a}\\delta\\tilde{p}_{a}\\sum_{b}k_{ab}^{pp}\\Delta\\tilde{p}_{b}\\,,\\label{eq354j}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\nk_{ab}^{pp}=\\int_{b}N_{a}\\,\\frac{\\partial\\hat{\\varphi}^{w}}{\\partial\\tilde{p}}N_{b}\\,dv\\,.\\label{eq354k}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G\\left[\\Delta\\tilde{c}^{\\iota}\\right] & =\\sum_{a}\\delta\\tilde{p}_{a}\\sum_{b}k_{ab}^{p\\iota}\\Delta\\tilde{c}_{b}^{\\iota}\\\\\n & +\\sum_{\\gamma}\\sum_{a}\\delta\\tilde{c}_{a}^{\\gamma}\\sum_{b}k_{ab}^{\\gamma\\iota}\\Delta\\tilde{c}_{b}^{\\iota}\n\\end{aligned}\n\\,,\\label{eq354l}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}k_{ab}^{p\\iota} & =\\int_{b}N_{a}N_{b}\\frac{\\partial\\hat{\\varphi}^{w}}{\\partial\\tilde{c}^{\\iota}}\\,dv+\\bar{\\mathcal{V}}\\int_{b}N_{a}N_{b}\\left(1-\\varphi^{s}\\right)\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\iota}}\\,dv\\\\\nk_{ab}^{\\gamma\\iota} & =\\nu^{\\gamma}\\int_{b}N_{a}N_{b}\\left(\\left(1-\\varphi^{s}\\right)\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\iota}}-\\frac{1}{J}\\frac{\\partial\\varphi_{r}^{s}}{\\partial\\tilde{c}^{\\iota}}\\hat{\\zeta}\\right)\\,dv\n\\end{aligned}\n\\,.\\label{eq354m}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nUpdating Solid-Bound Molecule Concentrations\n\\end_layout\n\n\\begin_layout Standard\nThe solid-bound molecule concentrations \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n are evaluated at integration points of each element;\n they do not represent nodal degrees of freedom.\n The values of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n are updated at the end of each iteration in the solution of the nonlinear equations for the nodal degrees of freedom,\n using trapezoidal integration on \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq155\"\nnolink \"false\"\n\n\\end_inset\n\n.\n According to \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq164\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq166\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we have \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}=\\left(J-\\varphi_{r}^{s}\\right)M^{\\sigma}\\nu^{\\sigma}\\hat{\\zeta}$\n\\end_inset\n\n,\n which is evaluated as the average of values at \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n,\n then\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\rho_{r}^{\\sigma}\\right)_{n+1}=\\left(\\rho_{r}^{\\sigma}\\right)_{n}+\\left(\\hat{\\rho}_{r}^{\\sigma}\\right)_{n+\\frac{1}{2}}\\Delta t\\label{eq:sbm-incremental-eq}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Delta t=t_{n+1}-t_{n}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nComputational Fluid Dynamics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Computational-Fluid-Dynamics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA more detailed description of the FEBio fluid solver can be found in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nWeak Formulation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Weak-Formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe nodal unknowns in this formulation are \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n (or \n\\begin_inset Formula $e$\n\\end_inset\n\n),\n which may be solved using the momentum balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:momentum-balance\"\nnolink \"false\"\n\n\\end_inset\n\n and the kinematic constraint between \n\\begin_inset Formula $J$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:divv-kinematic-relation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The virtual work integral for a Galerkin finite element formulation \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\int_{\\Omega}\\delta\\mathbf{v}\\cdot\\left(\\divg\\boldsymbol{\\sigma}+\\rho\\left(\\mathbf{b}-\\mathbf{a}\\right)\\right)dv\\\\\n & +\\int_{\\Omega}\\delta J\\left(\\frac{\\dot{J}}{J}-\\divg\\mathbf{v}\\right)dv\\,,\n\\end{aligned}\n\\label{eq:virtual work}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n is a virtual velocity and \n\\begin_inset Formula $\\delta J$\n\\end_inset\n\n is a virtual energy density;\n \n\\begin_inset Formula $\\Omega$\n\\end_inset\n\n is the fluid finite element domain and \n\\begin_inset Formula $dv$\n\\end_inset\n\n is a differential volume in \n\\begin_inset Formula $\\Omega$\n\\end_inset\n\n.\n This virtual work statement may be directly related to the axiom of energy balance,\n specialized to conditions of isothermal flow of viscous compressible fluids (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CFD-Energy-Balance\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Using the divergence theorem,\n we may rewrite the weak form of this integral as the difference between external and internal virtual work,\n \n\\begin_inset Formula $\\delta W=\\delta W_{ext}-\\delta W_{int}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int} & =\\int_{\\Omega}\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{v}\\,dv+\\int_{\\Omega}\\delta\\mathbf{v}\\cdot\\left(\\grad p+\\rho\\mathbf{a}\\right)\\,dv\\\\\n & -\\int_{\\Omega}\\left(\\delta J\\frac{\\dot{J}}{J}+\\grad\\delta J\\cdot\\mathbf{v}\\right)\\,dv\\,,\n\\end{aligned}\n\\label{eq:virtual-work-internal-1}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{ext}=\\int_{\\partial\\Omega}\\delta\\mathbf{v}\\cdot\\mathbf{t}^{\\tau}\\,da+\\int_{\\Omega}\\delta\\mathbf{v}\\cdot\\rho\\mathbf{b}\\,dv-\\int_{\\partial\\Omega}\\delta J\\,v_{n}\\,da\\,.\\label{eq:virtual-work-external-1}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\partial\\Omega$\n\\end_inset\n\n is the boundary of \n\\begin_inset Formula $\\Omega$\n\\end_inset\n\n and \n\\begin_inset Formula $da$\n\\end_inset\n\n is a differential area on \n\\begin_inset Formula $\\partial\\Omega$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the viscous component of the traction \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $v_{n}=\\mathbf{v}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the velocity normal to the boundary \n\\begin_inset Formula $\\partial\\Omega$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n representing the outward normal on \n\\begin_inset Formula $\\partial\\Omega$\n\\end_inset\n\n.\n From these expressions,\n it becomes evident that essential (Dirichlet) boundary conditions may be prescribed on \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n,\n while natural (Neumann) boundary conditions may be prescribed on \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n and \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n.\n The appearance of velocity in both essential and natural boundary conditions may seem surprising at first.\n To better understand the nature of these boundary conditions,\n it is convenient to separate the velocity into its normal and tangential components,\n \n\\begin_inset Formula $\\mathbf{v}=v_{n}\\mathbf{n}+\\mathbf{v}_{t}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{v}_{t}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{v}$\n\\end_inset\n\n.\n In particular,\n for inviscid flow,\n the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n and its corresponding traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n are both zero,\n leaving \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n as the sole natural boundary condition;\n similarly,\n \n\\begin_inset Formula $J$\n\\end_inset\n\n becomes the only essential boundary condition in such flows,\n since \n\\begin_inset Formula $\\mathbf{v}_{t}$\n\\end_inset\n\n is unknown \n\\emph on\na priori\n\\emph default\n on a frictionless boundary and must be obtained from the solution of the analysis.\n \n\\end_layout\n\n\\begin_layout Standard\nIn general,\n prescribing \n\\begin_inset Formula $J$\n\\end_inset\n\n is equivalent to prescribing the elastic fluid pressure,\n since \n\\begin_inset Formula $p$\n\\end_inset\n\n is only a function of \n\\begin_inset Formula $J$\n\\end_inset\n\n.\n On a boundary where no conditions are prescribed explicitly,\n we conclude that \n\\begin_inset Formula $v_{n}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n,\n which represents a frictionless wall.\n Conversely,\n it is possible to prescribe \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n on a boundary to produce a desired inflow or outflow while simultaneously stabilizing the flow conditions by prescribing a suitable viscous traction.\n Prescribing essential boundary conditions \n\\begin_inset Formula $\\mathbf{v}_{t}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n determines the tangential velocity on a boundary as well as the elastic fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n leaving the option to also prescribe the normal component of the viscous traction,\n \n\\begin_inset Formula $t_{n}^{\\tau}=\\mathbf{t}^{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n to completely determine the normal traction \n\\begin_inset Formula $t_{n}=\\mathbf{t}\\cdot\\mathbf{n}$\n\\end_inset\n\n (or else \n\\begin_inset Formula $t_{n}^{\\tau}$\n\\end_inset\n\n naturally equals zero).\n Mixed boundary conditions represent common physical features:\n Prescribing \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}_{t}$\n\\end_inset\n\n completely determines the velocity \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n on a boundary;\n prescribing \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n completely determines the traction \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n on a boundary.\n Note that \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n are mutually exclusive boundary conditions,\n and the same holds for \n\\begin_inset Formula $\\mathbf{v}_{t}$\n\\end_inset\n\n and the tangential component of the viscous traction,\n \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{t}^{\\tau}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nTemporal Discretization and Linearization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Discretization-Linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe time derivatives,\n \n\\begin_inset Formula $\\partial\\mathbf{v}/\\partial t$\n\\end_inset\n\n which appears in the expression for \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:acceleration\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\partial J/\\partial t$\n\\end_inset\n\n which similarly appears in \n\\begin_inset Formula $\\dot{J}$\n\\end_inset\n\n,\n may be discretized upon the choice of a time integration scheme,\n such as the generalized-\n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n method \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Generalized-Alpha-Method\"\nnolink \"false\"\n\n\\end_inset\n\n).\n In this scheme,\n \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n is evaluated at an intermediate time step \n\\begin_inset Formula $t_{n+\\alpha}=\\alpha t_{n+1}+\\left(1-\\alpha\\right)t_{n}$\n\\end_inset\n\n between the current time step \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n and previous time step \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n,\n though different values of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n are used for the primary variables and their time derivatives.\n The velocity and volume ratio are evaluated as \n\\begin_inset Formula $\\mathbf{v}_{n+\\alpha_{f}}$\n\\end_inset\n\n and \n\\begin_inset Formula $J_{n+\\alpha_{f}}$\n\\end_inset\n\n at the intermediate time step \n\\begin_inset Formula $t_{n+\\alpha_{f}}$\n\\end_inset\n\n,\n whereas their time derivatives are evaluated as \n\\begin_inset Formula $\\left(\\partial\\mathbf{v}/\\partial t\\right)_{n+\\alpha_{m}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(\\partial J/\\partial t\\right)_{n+\\alpha_{m}}$\n\\end_inset\n\n at the intermediate time step \n\\begin_inset Formula $t_{n+\\alpha_{m}}$\n\\end_inset\n\n.\n The parameters \n\\begin_inset Formula $\\alpha_{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{m}$\n\\end_inset\n\n are evaluated from the spectral radius for an infinite time step,\n \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Generalized-Alpha-Method\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The solution of the nonlinear equation \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n is obtained by linearizing this relation as\n\\begin_inset Formula \n\\begin{equation}\n\\delta W+D\\delta W\\left[\\Delta\\mathbf{v}\\right]+D\\delta W\\left[\\Delta J\\right]\\approx0\\,,\\label{eq:linearized-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere the operator \n\\begin_inset Formula $D\\delta W\\left[\\cdot\\right]$\n\\end_inset\n\n represents the directional derivative of \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n at \n\\begin_inset Formula $\\left(\\mathbf{v},J\\right)$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{v}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\Delta J$\n\\end_inset\n\n of \n\\begin_inset Formula $J$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The aim of this analysis is to solve for the velocity \n\\begin_inset Formula $\\mathbf{v}_{n+1}$\n\\end_inset\n\n and volume ratio \n\\begin_inset Formula $J_{n+1}$\n\\end_inset\n\n at the current time step \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n.\n Using the split form of \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n between external and internal work contributions,\n this relation may be expanded as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\delta W_{int}\\left[\\Delta\\mathbf{v}\\right]+D\\delta W_{int}\\left[\\Delta J\\right]-D\\delta W_{ext}\\left[\\Delta\\mathbf{v}\\right]\\\\\n & -D\\delta W_{ext}\\left[\\Delta J\\right]\\approx\\delta W_{ext}-\\delta W_{int}\\,.\n\\end{aligned}\n\\label{eq:linearized-work-split}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn this framework the finite element mesh is defined on the spatial domain \n\\begin_inset Formula $\\Omega$\n\\end_inset\n\n,\n which is fixed (time-invariant) in conventional CFD treatments.\n Thus,\n we can linearize \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n along increments \n\\begin_inset Formula $\\Delta\\mathbf{v}$\n\\end_inset\n\n in the velocity \n\\begin_inset Formula $\\mathbf{v}_{n+1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta J$\n\\end_inset\n\n in the volume ratio \n\\begin_inset Formula $J_{n+1}$\n\\end_inset\n\n,\n by simply bringing the directional derivative operator into the integrals of eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:virtual-work-internal-1\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:virtual-work-external-1\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The linearization of \n\\begin_inset Formula $\\mathbf{v}_{n+\\alpha_{f}}$\n\\end_inset\n\n and \n\\begin_inset Formula $J_{n+\\alpha_{f}}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\mathbf{v}_{n+\\alpha_{f}}\\left[\\Delta\\mathbf{v}\\right] & =\\alpha_{f}\\Delta\\mathbf{v}\\\\\nDJ_{n+\\alpha_{f}}\\left[\\Delta J\\right] & =\\alpha_{f}\\Delta J\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhereas that of \n\\begin_inset Formula $\\left(\\partial\\mathbf{v}/\\partial t\\right)_{n+\\alpha_{m}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(\\partial J/\\partial t\\right)_{n+\\alpha_{m}}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\frac{\\partial\\mathbf{v}}{dt}\\right)_{n+\\alpha_{m}}\\left[\\Delta\\mathbf{v}\\right]=\\frac{\\alpha_{m}}{\\gamma}\\frac{\\Delta\\mathbf{v}}{\\Delta t}\\,,\\label{eq:discretized-a}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\frac{\\partial J}{\\partial t}\\right)_{n+\\alpha_{m}}\\left[\\Delta\\mathbf{v}\\right]=\\frac{\\alpha_{m}}{\\gamma}\\frac{\\Delta J}{\\Delta t}\\,.\\label{eq:discretized-J}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\Delta t$\n\\end_inset\n\n is the current time increment and \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is the Newmark integration parameter \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{v}$\n\\end_inset\n\n is then\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{int}\\right)\\left[\\Delta\\mathbf{v}\\right] & =\\alpha_{f}\\int_{\\Omega}\\grad\\delta\\mathbf{v}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\grad\\Delta\\mathbf{v}\\,dv\\\\\n & +\\alpha_{f}\\int_{\\Omega}\\delta\\mathbf{v}\\cdot\\rho\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{\\mathbf{I}}{\\Delta t}+\\mathbf{L}\\right)\\cdot\\Delta\\mathbf{v}+\\grad\\Delta\\mathbf{v}\\cdot\\mathbf{v}\\right)\\,dv\\\\\n & -\\alpha_{f}\\int_{\\Omega}\\left(\\frac{\\delta J}{J}\\grad J+\\grad\\delta J\\right)\\cdot\\Delta\\mathbf{v}\\,dv\n\\end{aligned}\n\\label{eq:Wint-linearization-v}\n\\end{equation}\n\n\\end_inset\n\nwhere we have introduced the fourth-order tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{\\tau}$\n\\end_inset\n\n representing the tangent of the viscous stress with respect to the rate of deformation,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}^{\\tau}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial\\mathbf{D}}\\,.\\label{eq:stress-tangent-D}\n\\end{equation}\n\n\\end_inset\n\nNote that \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{\\tau}$\n\\end_inset\n\n exhibits minor symmetries because of the symmetries of \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n;\n in Cartesian components,\n we have \n\\begin_inset Formula $\\mathcal{C}_{ijkl}^{\\tau}=\\mathcal{C}_{jikl}^{\\tau}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathcal{C}_{ijkl}^{\\tau}=\\mathcal{C}_{ijlk}^{\\tau}$\n\\end_inset\n\n.\n In general,\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{\\tau}$\n\\end_inset\n\n does not exhibit major symmetry (\n\\begin_inset Formula $\\mathcal{C}_{ijkl}^{\\tau}\\ne\\mathcal{C}_{klij}^{\\tau}$\n\\end_inset\n\n),\n though the common constitutive relations adopted in fluid mechanics produce such symmetry as shown below.\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta J$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{int}\\right)\\left[\\Delta J\\right] & =\\alpha_{f}\\int_{b}\\Delta J\\,\\boldsymbol{\\tau}_{J}^{\\prime}:\\grad\\delta\\mathbf{v}\\,dv-\\alpha_{f}\\int_{\\Omega}\\delta\\mathbf{v}\\cdot\\Delta J\\frac{\\rho}{J}\\mathbf{a}\\,dv\\\\\n & +\\alpha_{f}\\int_{\\Omega}\\delta\\mathbf{v}\\cdot\\left(p^{\\prime}\\grad\\Delta J+\\Delta J\\,p^{\\prime\\prime}\\grad J\\right)\\,dv\\\\\n & -\\alpha_{f}\\int_{\\Omega}\\frac{\\delta J}{J}\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}-\\frac{\\dot{J}}{J}\\right)\\Delta J+\\grad\\Delta J\\cdot\\mathbf{v}\\right)\\,dv\n\\end{aligned}\n\\label{eq:Wint-linearization-J}\n\\end{equation}\n\n\\end_inset\n\nwhere we have used \n\\begin_inset Formula $DJ\\left[\\Delta J\\right]=\\Delta J$\n\\end_inset\n\n;\n \n\\begin_inset Formula $p^{\\prime}$\n\\end_inset\n\n and \n\\begin_inset Formula $p^{\\prime\\prime}$\n\\end_inset\n\n respectively represent the first and second derivatives of \n\\begin_inset Formula $p\\left(J\\right)$\n\\end_inset\n\n.\n We have also defined \n\\begin_inset Formula $\\boldsymbol{\\tau}_{J}^{\\prime}$\n\\end_inset\n\n as the tangent of the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $J$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\tau}_{J}^{\\prime}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial J}\\,.\\label{eq:stress-tangent-J}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the external work,\n when \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n and \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n are prescribed,\n these linearizations simplify to\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta W_{ext}\\right)\\left[\\Delta\\mathbf{v}\\right]=0\\,,\\label{eq:Wext-linearization-v}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta W_{ext}\\right)\\left[\\Delta J\\right]=-\\alpha_{f}\\int_{b}\\delta\\mathbf{v}\\cdot\\Delta J\\,\\frac{\\rho}{J}\\mathbf{b}\\,dv\\,.\\label{eq:Wext-linearization-J}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe may define the fluid dilatation \n\\begin_inset Formula $e=J-1$\n\\end_inset\n\n as an alternative essential variable,\n since initial and boundary conditions \n\\begin_inset Formula $e=0$\n\\end_inset\n\n are more convenient to handle in a numerical scheme than \n\\begin_inset Formula $J=1$\n\\end_inset\n\n.\n It follows that \n\\begin_inset Formula $\\grad J=\\grad e$\n\\end_inset\n\n and \n\\begin_inset Formula $\\partial J/\\partial t=\\partial e/\\partial t$\n\\end_inset\n\n.\n Therefore the changes to the above equations are minimal,\n simply requiring the substitution \n\\begin_inset Formula $J=1+e$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta J=\\Delta e$\n\\end_inset\n\n.\n Steady-state analyses may be obtained by setting the terms involving \n\\begin_inset Formula $\\Delta t^{-1}$\n\\end_inset\n\n to zero in eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:discretized-a\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:discretized-J\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Wint-linearization-v\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Wint-linearization-J\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nSpatial Discretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Spatial-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe velocity \n\\begin_inset Formula $\\mathbf{v}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n and Jacobian \n\\begin_inset Formula $J\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n are spatially interpolated over the domain \n\\begin_inset Formula $\\Omega$\n\\end_inset\n\n using the same interpolation functions \n\\begin_inset Formula $N_{a}\\left(\\mathbf{x}\\right)$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $a=1$\n\\end_inset\n\n to \n\\begin_inset Formula $n$\n\\end_inset\n\n where \n\\begin_inset Formula $n$\n\\end_inset\n\n is the number of nodes in an element),\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}\\left(\\mathbf{x},t\\right)=\\sum_{a=1}^{n}N_{a}\\left(\\mathbf{x}\\right)\\mathbf{v}_{a}\\,,\\quad J\\left(\\mathbf{x},t\\right)=\\sum_{a=1}^{n}N_{a}\\left(\\mathbf{x}\\right)J_{a}\\,.\\label{eq:v-J-interpolation}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{v}_{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $J_{a}$\n\\end_inset\n\n are nodal values of \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n that evolve with time.\n In contrast to classical mixed formulations for incompressible flow \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Reddy01\"\nliteral \"true\"\n\n\\end_inset\n\n,\n which solve for the pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n using \n\\begin_inset Formula $\\divg\\mathbf{v}=0$\n\\end_inset\n\n instead of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:divv-kinematic-relation\"\nnolink \"false\"\n\n\\end_inset\n\n,\n equal order interpolation is acceptable in this formulation since the governing equations for \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n involve spatial derivatives of both variables (\n\\begin_inset Formula $\\grad\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\grad J$\n\\end_inset\n\n).\n The expressions of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:v-J-interpolation\"\nnolink \"false\"\n\n\\end_inset\n\n may be used to evaluate \n\\begin_inset Formula $\\mathbf{L}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\divg\\mathbf{v}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\grad J$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\dot{J}$\n\\end_inset\n\n,\n etc.\n Similar interpolations are used for virtual increments \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta J$\n\\end_inset\n\n,\n as well as real increments \n\\begin_inset Formula $\\Delta\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta J$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen substituted into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:virtual-work-internal\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find that the discretized form of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n may be written as\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{int}=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\left(\\mathbf{f}_{a}^{\\sigma}+\\mathbf{f}_{a}^{\\rho}\\right)+f_{a}^{J}\\delta J_{a}\\,,\\label{eq:discretized-Wint}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\sigma} & =\\int_{\\Omega}\\left(\\boldsymbol{\\tau}\\cdot\\grad N_{a}+N_{a}\\grad p\\right)\\,dv\\,,\\\\\n\\mathbf{f}_{a}^{\\rho} & =\\int_{\\Omega}N_{a}\\rho\\mathbf{a}\\,dv\\,,\\\\\nf_{a}^{J} & =\\int_{\\Omega}-\\left(N_{a}\\frac{\\dot{J}}{J}+\\grad N_{a}\\cdot\\mathbf{v}\\right)\\,dv\\,.\n\\end{aligned}\n\\label{eq:discretized-internal-forces}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n the discretized form of \n\\begin_inset Formula $D\\delta W_{int}\\left[\\Delta\\mathbf{v}\\right]$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Wint-linearization-v\"\nnolink \"false\"\n\n\\end_inset\n\n becomes\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{int}\\right)\\left[\\Delta\\mathbf{v}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\left(\\mathbf{K}_{ab}^{vv}+\\mathbf{M}_{ab}^{vv}\\right)\\cdot\\Delta\\mathbf{v}_{b}\\\\\n & +\\sum_{a}\\delta J_{a}\\sum_{b}\\mathbf{k}_{ab}^{Jv}\\cdot\\Delta\\mathbf{v}_{b}\\,,\n\\end{aligned}\n\\label{eq:discretized-DWint-v}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{vv} & =\\alpha_{f}\\int_{\\Omega}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{v}\\cdot\\grad N_{b}\\,dv\\,,\\\\\n\\mathbf{M}_{ab}^{vv} & =\\alpha_{f}\\int_{\\Omega}N_{a}\\rho\\left(N_{b}\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{\\mathbf{I}}{\\Delta t}+\\grad\\mathbf{v}\\right)+\\left(\\grad N_{b}\\cdot\\mathbf{v}\\right)\\mathbf{I}\\right)\\,dv\\,,\\\\\n\\mathbf{k}_{ab}^{Jv} & =-\\alpha_{f}\\int_{\\Omega}\\left(\\frac{N_{a}}{J}\\grad J+\\grad N_{a}\\right)N_{b}\\,dv\\,,\n\\end{aligned}\n\\label{eq:discretized-stiffness-v}\n\\end{equation}\n\n\\end_inset\n\nwhereas that of \n\\begin_inset Formula $D\\delta W_{int}\\left[\\Delta J\\right]$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Wint-linearization-J\"\nnolink \"false\"\n\n\\end_inset\n\n becomes\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{int}\\right)\\left[\\Delta J\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\left(\\mathbf{k}_{ab}^{vJ}+\\mathbf{m}_{ab}^{vJ}\\right)\\,\\Delta J_{b}\\\\\n & +\\sum_{a}\\delta J_{a}\\sum_{b}k_{ab}^{JJ}\\,\\Delta J_{b}\n\\end{aligned}\n\\label{eq:discretized-DWint-J}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ab}^{vJ} & =\\alpha_{f}\\int_{\\Omega}\\left[N_{b}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad N_{a}+N_{a}\\left(p^{\\prime}\\grad N_{b}+N_{b}p^{\\prime\\prime}\\grad J\\right)\\right]\\,dv\\,,\\\\\n\\mathbf{m}_{ab}^{vJ} & =-\\alpha_{f}\\int_{\\Omega}N_{a}N_{b}\\frac{\\rho}{J}\\mathbf{a}\\,dv\\,,\\\\\nk_{ab}^{JJ} & =-\\alpha_{f}\\int_{\\Omega}\\frac{N_{a}}{J}\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}-\\frac{\\dot{J}}{J}\\right)N_{b}+\\grad N_{b}\\cdot\\mathbf{v}\\right)\\,dv\\,.\n\\end{aligned}\n\\label{eq:discretized-stiffness-J}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the external work in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:virtual-work-external\"\nnolink \"false\"\n\n\\end_inset\n\n,\n its discretized form is\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{ext}=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\left(\\mathbf{f}_{a}^{\\text{t}}+\\mathbf{f}_{a}^{\\text{b}}\\right)+\\delta J_{a}f_{a}^{v}\\,,\\label{eq:discretized-Wext}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\text{t}} & =\\int_{\\partial\\Omega}N_{a}\\mathbf{t}^{\\tau}\\,da\\,,\\\\\n\\mathbf{f}_{a}^{\\text{b}} & =\\int_{\\Omega}N_{a}\\rho\\mathbf{b}\\,dv\\,,\\\\\nf_{a}^{v} & =-\\int_{\\Omega}N_{a}v_{n}\\,da\\,.\n\\end{aligned}\n\\label{eq:discretized-external-forces}\n\\end{equation}\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $D\\left(\\delta W_{ext}\\right)\\left[\\Delta J\\right]$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Wext-linearization-J\"\nnolink \"false\"\n\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\delta W_{ext}\\right)\\left[\\Delta J\\right]=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{k}_{ab}^{\\text{b}}\\,\\Delta J_{b}\\,,\\label{eq:discretized-DWext-J}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{\\text{b}}=-\\alpha_{f}\\int_{\\Omega}N_{a}N_{b}\\frac{\\rho}{J}\\mathbf{b}\\,dv\\,.\\label{eq:discretized-stiffness-ext}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSpecial Boundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Special-Boundary-Conditions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nBackflow Stabilization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Backflow-Stabilization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor arterial blood flow,\n backflow stabilization has been proposed previously to deal with truncated domains where the entire artery is not modeled explicitly \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Bazilevs09,Esmaily-Moghadam11\"\nliteral \"true\"\n\n\\end_inset\n\n;\n for these types of problems,\n letting \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{0}$\n\\end_inset\n\n or prescribing a constant pressure at the outflow boundary may not prevent flow reversals that compromise convergence of an analysis.\n Instead,\n these authors proposed a velocity-dependent traction boundary condition,\n \n\\begin_inset Formula $\\mathbf{t}=\\beta\\rho\\left(\\mathbf{v}\\otimes\\mathbf{v}\\right)\\cdot\\mathbf{n}$\n\\end_inset\n\n with a tensile normal component,\n that counters the backflow (only when \n\\begin_inset Formula $v_{n}<0$\n\\end_inset\n\n).\n Here,\n \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is a non-dimensional user-defined parameter;\n a value of \n\\begin_inset Formula $\\beta=0$\n\\end_inset\n\n turns off this feature,\n while a value of \n\\begin_inset Formula $\\beta=1$\n\\end_inset\n\n generally shows good numerical performance.\n We adapt this previously proposed formulation by letting the normal component of the viscous traction be given by \n\\begin_inset Formula \n\\begin{equation}\nt_{n}^{\\tau}=\\begin{cases}\n\\beta\\rho_{r}v_{n}^{2} & v_{n}<0\\\\\n0 & v_{n}\\ge0\n\\end{cases}\\,.\\label{eq:BFS-normal-traction}\n\\end{equation}\n\n\\end_inset\n\nThe choice of \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n in lieu of \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is for convenience,\n to avoid the dependence of \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n on \n\\begin_inset Formula $J$\n\\end_inset\n\n (which is negligible for nearly incompressible flow).\n Then,\n the contribution of this traction to the virtual external work \n\\begin_inset Formula $\\delta W_{ext}$\n\\end_inset\n\n is \n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\int_{\\partial\\Omega}\\delta\\mathbf{v}\\cdot t_{n}^{\\tau}\\mathbf{n}\\,da\\,.\\label{eq:BFSckflow-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{v}$\n\\end_inset\n\n in the velocity is given by\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{v}\\right]=\\int_{\\partial\\Omega}\\delta\\mathbf{v}\\cdot\\mathbf{K}_{n}\\cdot\\Delta\\mathbf{v}\\,da\\,,\\label{eq:BFS-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{n}=\\begin{cases}\n2\\beta\\rho_{r}v_{n}\\left(\\mathbf{n}\\otimes\\mathbf{n}\\right) & v_{n}<0\\\\\n\\mathbf{0} & v_{n}\\ge0\n\\end{cases}\\,.\\label{eq:BFS-stiffness}\n\\end{equation}\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\mathbf{f}_{a}^{n}\\,,\\quad\\mathbf{f}_{a}^{n}=\\int_{\\partial\\Omega}N_{a}t_{n}^{\\tau}\\mathbf{n}\\,da\\,,\\label{eq:BFS-discretized-work}\n\\end{equation}\n\n\\end_inset\n\nwhereas the discretized form of \n\\begin_inset Formula $D\\delta G\\left[\\Delta\\mathbf{v}\\right]$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{v}\\right]=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{n}\\cdot\\Delta\\mathbf{v}_{b}\\,,\\quad\\mathbf{K}_{ab}^{n}=\\int_{\\partial\\Omega}N_{a}N_{b}\\mathbf{K}_{n}\\,da\\,.\\label{eq:BFS-discretized-tangent}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA (viscous) tangential traction is implemented as a separate flow stabilization method in the next section,\n applicable to inlet or outlet surfaces,\n without a conditional requirement based on the sign of \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nTangential Flow Stabilization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Tangential-Flow-Stabilization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor certain outlet conditions,\n using the natural boundary condition \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n may lead to flow instabilities.\n It is possible to minimize these effects by prescribing a tangential viscous traction onto the boundary surface,\n which opposes this tangential flow.\n Optionally,\n this condition may be combined with the backflow stabilization described above.\n\\end_layout\n\n\\begin_layout Standard\nSimilar to the previous section,\n we introduce a non-dimensional parameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n with the tangential traction given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}_{t}^{\\tau}=-\\beta\\rho_{r}\\left|\\mathbf{v}_{t}\\right|\\mathbf{v}_{t}\\,.\\label{eq:tangential-stabilization}\n\\end{equation}\n\n\\end_inset\n\nThis form shows that \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}$\n\\end_inset\n\n opposes tangential flow.\n The external virtual work for this traction is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\int_{\\partial\\Omega}\\delta\\mathbf{v}\\cdot\\mathbf{t}_{t}^{\\tau}\\,da\\,.\\label{eq:TS-external-work}\n\\end{equation}\n\n\\end_inset\n\nIts linearization along an increment \n\\begin_inset Formula $\\Delta\\mathbf{v}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{v}\\right]=\\int_{\\partial\\Omega}\\delta\\mathbf{v}\\cdot\\mathbf{K}_{t}\\cdot\\Delta\\mathbf{v}\\,da\\,,\\label{eq:TS-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere it can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{t}=-\\beta\\rho_{r}\\left|\\mathbf{v}_{t}\\right|\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}+\\frac{\\mathbf{v}_{t}}{\\left|\\mathbf{v}_{t}\\right|}\\otimes\\frac{\\mathbf{v}_{t}}{\\left|\\mathbf{v}_{t}\\right|}\\right)\\,.\\label{eq:TS-stiffness}\n\\end{equation}\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\mathbf{f}_{a}^{\\tau}\\,,\\quad\\mathbf{f}_{a}^{\\tau}=\\int_{\\partial\\Omega}N_{a}\\mathbf{t}_{t}^{\\tau}\\,da\\,.\\label{eq:TS-discretized-work}\n\\end{equation}\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $D\\delta G\\left[\\Delta\\mathbf{v}\\right]$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{v}\\right]=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{t}\\cdot\\Delta\\mathbf{v}_{b}\\,,\\quad\\mathbf{K}_{ab}^{t}=\\int_{\\partial\\Omega}N_{a}N_{b}\\mathbf{K}_{t}\\,da\\,.\\label{eq:TS-discretized-tangent}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFlow Resistance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CFD-Flow-Resistance\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFlow resistance is typically implemented when modeling arterial flow,\n where the finite element domain only describes a portion of an arterial network \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Vignon-Clementel06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n A flow resistance may be imposed on downstream boundaries to simulate the resistance produced by the vascular network with its branches and bifurcations.\n The resistance is equivalent to a mean pressure which is proportional to the volumetric flow rate \n\\begin_inset Formula $Q$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\np=RQ\\,,\\quad Q=\\int_{\\partial\\Omega}v_{n}\\,da\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $R$\n\\end_inset\n\n is the resistance.\n Using the pressure-dilatation relation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:ideal-fluid\"\nnolink \"false\"\n\n\\end_inset\n\n,\n equivalent to \n\\begin_inset Formula $p=-K\\cdot e$\n\\end_inset\n\n,\n this pressure may be prescribed as an essential boundary condition on the dilatation \n\\begin_inset Formula $e$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nWeak Formulation for FSI\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Weak-Formulation-FSI\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Formulation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:FSI-General-Formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strong form of the virtual work statement is \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\int_{b}\\delta\\mathbf{v}^{s}\\cdot\\divg\\boldsymbol{\\sigma}^{s}\\,dv\\\\\n & +\\int_{b}\\delta\\mathbf{w}\\cdot\\left[\\divg\\boldsymbol{\\sigma}^{f}+\\rho^{f}\\left(\\mathbf{b}-\\mathbf{a}^{f}\\right)\\right]\\,dv\\\\\n & -\\int_{b}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)-\\divg\\mathbf{w}-\\frac{\\dot{J}^{s}}{J^{s}}\\right]\\,dv\n\\end{aligned}\n\\,.\\label{eq:fsi-virtual-work-strong}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $b$\n\\end_inset\n\n is the mixture domain (whose boundaries are defined on the solid),\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{s}$\n\\end_inset\n\n is the virtual solid velocity,\n \n\\begin_inset Formula $\\delta\\mathbf{w}$\n\\end_inset\n\n is the virtual relative fluid velocity,\n and \n\\begin_inset Formula $\\delta J^{f}$\n\\end_inset\n\n is the virtual energy density.\n The weak form of this statement may be written as the difference \n\\begin_inset Formula $\\delta W=\\delta W_{ext}-\\delta W_{int}$\n\\end_inset\n\n between external virtual work \n\\begin_inset Formula $\\delta W_{ext}$\n\\end_inset\n\n and internal virtual work \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int} & =\\int_{b}\\boldsymbol{\\sigma}^{s}:\\grad\\delta\\mathbf{v}^{s}\\,dv\\\\\n & +\\int_{b}\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{w}\\,dv+\\int_{b}\\delta\\mathbf{w}\\cdot\\left(\\grad p+\\rho^{f}\\mathbf{a}^{f}\\right)\\,dv\\\\\n & +\\int_{b}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)-\\frac{\\dot{J}^{s}}{J^{s}}\\right]\\,dv\\\\\n & +\\int_{b}\\mathbf{w}\\cdot\\grad\\delta J^{f}\\,dv\n\\end{aligned}\n\\,,\\label{eq:fsi-internal-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{ext} & =\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\mathbf{t}^{s}\\,da\\\\\n & +\\int_{\\partial b}\\delta\\mathbf{w}\\cdot\\mathbf{t}^{\\tau}\\,da+\\int_{b}\\delta\\mathbf{w}\\cdot\\rho^{f}\\mathbf{b}\\,dv\\\\\n & +\\int_{\\partial b}\\delta J^{f}w_{n}\\,da\n\\end{aligned}\n\\,.\\label{eq:fsi-external-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{t}^{s}=\\boldsymbol{\\sigma}^{s}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the solid traction,\n \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the fluid viscous traction,\n and \n\\begin_inset Formula $w_{n}=\\mathbf{w}_{n}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the normal component of the relative fluid velocity on the boundary \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n,\n whose outward unit normal is \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n In practice,\n both \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{t}^{s}$\n\\end_inset\n\n should contribute negligibly to \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n,\n but they cannot be set exactly to zero since we need some small elasticity to regularize the deforming mixture mesh.\n\\end_layout\n\n\\begin_layout Subsection\nTime Integration\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:FSI-Time-Integration\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the generalized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nmethod we evaluate displacements and velocities at the intermediate time \n\\begin_inset Formula $t_{n+\\alpha_{f}}=t_{n}+\\alpha_{f}\\left(t_{n+1}-t_{n}\\right)$\n\\end_inset\n\n,\n such that\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\boldsymbol{\\chi}_{n+\\alpha_{f}}^{s} & =\\left(1-\\alpha_{f}\\right)\\boldsymbol{\\chi}_{n}^{s}+\\alpha_{f}\\boldsymbol{\\chi}_{n+1}^{s}\\\\\n\\mathbf{u}_{n+\\alpha_{f}}^{s} & =\\left(1-\\alpha_{f}\\right)\\mathbf{u}_{n}^{s}+\\alpha_{f}\\mathbf{u}_{n+1}^{s}\\\\\n\\mathbf{v}_{n+\\alpha_{f}}^{s} & =\\left(1-\\alpha_{f}\\right)\\mathbf{v}_{n}^{s}+\\alpha_{f}\\mathbf{v}_{n+1}^{s}\\\\\n\\mathbf{w}_{n+\\alpha_{f}} & =\\left(1-\\alpha_{f}\\right)\\mathbf{w}_{n}+\\alpha_{f}\\mathbf{w}_{n+1}\\\\\nJ_{n+\\alpha_{f}}^{f} & =\\left(1-\\alpha_{f}\\right)J_{n}^{f}+\\alpha_{f}J_{n+1}^{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nIn particular,\n it follows that\n\\begin_inset Formula \n\\[\n\\mathbf{F}_{n+\\alpha_{f}}^{s}=\\frac{\\partial\\boldsymbol{\\chi}_{n+\\alpha_{f}}^{s}}{\\partial\\mathbf{X}}=\\left(1-\\alpha_{f}\\right)\\mathbf{F}_{n}^{s}+\\alpha_{f}\\mathbf{F}_{n+1}^{s}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\nJ_{n+\\alpha_{f}}^{s}=\\det\\mathbf{F}_{n+\\alpha_{f}}^{s}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\dot{J}_{n+\\alpha_{f}}^{s}=J_{n+\\alpha_{f}}^{s}\\mathbf{F}_{n+\\alpha_{f}}^{-T}:\\Grad\\mathbf{v}_{n+\\alpha_{f}}^{s}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\mathbf{L}_{n+\\alpha_{f}}^{s}=\\Grad\\mathbf{v}_{n+\\alpha_{f}}^{s}\\cdot\\mathbf{F}_{n+\\alpha_{f}}^{-1}\n\\]\n\n\\end_inset\n\nIn practice however,\n we get better numerical results when using\n\\begin_inset Formula \n\\[\n\\dot{J}_{n+\\alpha_{f}}^{s}=\\frac{J_{n+1}^{s}-J_{n}^{s}}{\\Delta t}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\mathbf{L}_{n+\\alpha_{f}}^{s}=\\frac{\\mathbf{F}_{n+1}-\\mathbf{F}_{n}}{\\Delta t}\\cdot\\mathbf{F}_{n+\\alpha_{f}}^{-1}\n\\]\n\n\\end_inset\n\nSimilarly,\n we evaluate velocity derivatives at the intermediate time \n\\begin_inset Formula $t_{n+\\alpha_{m}}=t_{n}+\\alpha_{m}\\left(t_{n+1}-t_{n}\\right)$\n\\end_inset\n\n,\n such that\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\dot{\\mathbf{v}}_{n+\\alpha_{m}}^{s} & =\\left(1-\\alpha_{m}\\right)\\dot{\\mathbf{v}}_{n}^{s}+\\alpha_{m}\\dot{\\mathbf{v}}_{n+1}^{s}\\\\\n\\dot{\\mathbf{w}}_{n+\\alpha_{m}} & =\\left(1-\\alpha_{m}\\right)\\dot{\\mathbf{w}}_{n}+\\alpha_{m}\\dot{\\mathbf{w}}_{n+1}\\\\\n\\dot{J}_{n+\\alpha_{m}}^{f} & =\\left(1-\\alpha_{m}\\right)\\dot{J}_{n}^{f}+\\alpha_{m}\\dot{J}_{n+1}^{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThe parameters \n\\begin_inset Formula $\\alpha_{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{m}$\n\\end_inset\n\n are evaluated from a single parameter \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n using\n\\begin_inset Formula \n\\begin{equation}\n\\alpha_{f}=\\frac{1}{1+\\rho_{\\infty}}\\,,\\quad\\alpha_{m}=\\frac{1}{2}\\frac{3-\\rho_{\\infty}}{1+\\rho_{\\infty}}\\,,\\label{eq:fsi-ga-alphas-1st}\n\\end{equation}\n\n\\end_inset\n\nfor first-order systems,\n or\n\\begin_inset Formula \n\\begin{equation}\n\\alpha_{f}=\\frac{1}{1+\\rho_{\\infty}}\\,,\\quad\\alpha_{m}=\\frac{2-\\rho_{\\infty}}{1+\\rho_{\\infty}}\\,,\\label{eq:fsi-ga-alphas-2nd}\n\\end{equation}\n\n\\end_inset\n\nfor second-order systems,\n where \n\\begin_inset Formula $0\\le\\rho_{\\infty}\\le1$\n\\end_inset\n\n.\n This parameter is the spectral radius for an infinite time step,\n which controls the amount of damping of high frequencies;\n a value of zero produces the greatest amount of damping,\n anihilating the highest frequency in one step,\n whereas a value of one preserves the highest frequency.\n Since the solid motion is governed by a second-order differential equation in time,\n we adopt the formulas for second-order systems.\n\\end_layout\n\n\\begin_layout Standard\nTo complete the integration scheme,\n we evaluate\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\beta & =\\frac{1}{4}\\left(1+\\alpha_{m}-\\alpha_{f}\\right)^{2}\\\\\n\\gamma & =\\frac{1}{2}+\\alpha_{m}-\\alpha_{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nthen\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{v}_{n+1}^{s} & =\\mathbf{v}_{n}^{s}+\\Delta t\\left[\\left(1-\\gamma\\right)\\dot{\\mathbf{v}}_{n}^{s}+\\gamma\\dot{\\mathbf{v}}_{n+1}^{s}\\right]\\\\\n\\mathbf{u}_{n+1}^{s} & =\\mathbf{u}_{n}^{s}+\\Delta t\\mathbf{v}_{n}^{s}+\\frac{\\Delta t^{2}}{2}\\left[\\left(1-2\\beta\\right)\\dot{\\mathbf{v}}_{n}^{s}+2\\beta\\dot{\\mathbf{v}}_{n+1}^{s}\\right]\\\\\n\\mathbf{w}_{n+1} & =\\mathbf{w}_{n}+\\Delta t\\left[\\left(1-\\gamma\\right)\\dot{\\mathbf{w}}_{n}+\\gamma\\dot{\\mathbf{w}}_{n+1}\\right]\\\\\nJ_{n+1}^{f} & =J_{n}^{f}+\\Delta t\\left[\\left(1-\\gamma\\right)\\dot{J}_{n}^{f}+\\gamma\\dot{J}_{n+1}^{f}\\right]\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThus,\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\dot{\\mathbf{v}}_{n+1}^{s} & =\\frac{1}{\\beta\\Delta t}\\left(\\frac{\\mathbf{u}_{n+1}^{s}-\\mathbf{u}_{n}^{s}}{\\Delta t}-\\mathbf{v}_{n}^{s}\\right)+\\left(1-\\frac{1}{2\\beta}\\right)\\dot{\\mathbf{v}}_{n}^{s}\\\\\n\\dot{\\mathbf{w}}_{n+1} & =\\frac{\\mathbf{w}_{n+1}-\\mathbf{w}_{n}}{\\gamma\\Delta t}+\\left(1-\\frac{1}{\\gamma}\\right)\\dot{\\mathbf{w}}_{n}=\\frac{\\mathbf{w}_{n+\\alpha_{f}}-\\mathbf{w}_{n}}{\\alpha_{f}\\gamma\\Delta t}-\\left(\\frac{1}{\\gamma}-1\\right)\\dot{\\mathbf{w}}_{n}\\\\\n\\dot{J}_{n+1}^{f} & =\\frac{J_{n+1}^{f}-J_{n}^{f}}{\\gamma\\Delta t}+\\left(1-\\frac{1}{\\gamma}\\right)\\dot{J}_{n}^{f}=\\frac{J_{n+\\alpha_{f}}^{f}-J_{n}^{f}}{\\alpha_{f}\\gamma\\Delta t}-\\left(\\frac{1}{\\gamma}-1\\right)\\dot{J}_{n}^{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nAccording to this method \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n,\n the virtual work is evaluated using intermediate time step values,\n at \n\\begin_inset Formula $t_{n+\\alpha_{f}}$\n\\end_inset\n\n for all parameters except \n\\begin_inset Formula $\\dot{\\mathbf{v}}^{s}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\dot{\\mathbf{w}}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\dot{J}^{f}$\n\\end_inset\n\n,\n which are evaluated at \n\\begin_inset Formula $t_{n+\\alpha_{m}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\end_layout\n\n\\begin_layout Standard\nThe discretization of the internal work produces\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{int}=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\int_{b}\\mathbf{f}_{a}^{u}\\,dv+\\delta\\mathbf{w}_{a}\\cdot\\int_{b}\\mathbf{f}_{a}^{w}\\,dv+\\delta J_{a}^{f}\\int_{b}f_{a}^{J}\\,dv\\label{eq:fsi-discretized-internal-work}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta W_{int} & =\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\int_{b}\\boldsymbol{\\sigma}^{s}\\cdot\\grad N_{a}\\,dv\\\\\n & +\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\int_{b}\\boldsymbol{\\tau}\\cdot\\grad N_{a}\\,dv+\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\int_{b}N_{a}\\left(\\grad p+\\rho^{f}\\mathbf{a}^{f}\\right)\\,dv\\\\\n & +\\sum_{a}\\delta J_{a}^{f}\\int_{b}N_{a}\\left[\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)-\\frac{\\dot{J}^{s}}{J^{s}}\\right]\\,dv\\\\\n & +\\sum_{a}\\delta J_{a}^{f}\\int_{b}\\mathbf{w}\\cdot\\grad N_{a}\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{u} & =\\boldsymbol{\\sigma}^{s}\\cdot\\grad N_{a}\\\\\n\\mathbf{f}_{a}^{w} & =\\boldsymbol{\\tau}\\cdot\\grad N_{a}+N_{a}\\left(\\grad p+\\rho^{f}\\mathbf{a}^{f}\\right)\\\\\nf_{a}^{J} & =N_{a}\\left[\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)-\\frac{\\dot{J}^{s}}{J^{s}}\\right]+\\mathbf{w}\\cdot\\grad N_{a}\n\\end{aligned}\n\\label{eq:fsi-discretized-residuals}\n\\end{equation}\n\n\\end_inset\n\nWe used the following interpolations:\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{s} & =\\sum_{a}N_{a}\\delta\\mathbf{v}_{a}^{s} & \\Delta\\mathbf{u} & =\\sum_{b}N_{b}\\Delta\\mathbf{u}_{b}\\\\\n\\grad\\delta\\mathbf{v}^{s} & =\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\otimes\\grad N_{a} & \\grad\\Delta\\mathbf{u} & =\\sum_{b}\\Delta\\mathbf{u}_{b}\\otimes\\grad N_{b}\\\\\n\\divg\\delta\\mathbf{v}^{s} & =\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\grad N_{a} & \\divg\\Delta\\mathbf{u} & =\\sum_{b}\\Delta\\mathbf{u}_{b}\\cdot\\grad N_{b}\\\\\n\\delta\\mathbf{w} & =\\sum_{a}N_{a}\\delta\\mathbf{w}_{a} & \\Delta\\mathbf{w} & =\\sum_{b}N_{b}\\Delta\\mathbf{w}_{b}\\\\\n\\grad\\delta\\mathbf{w} & =\\sum_{a}\\delta\\mathbf{w}_{a}\\otimes\\grad N_{a} & \\grad\\Delta\\mathbf{w} & =\\sum_{b}\\Delta\\mathbf{w}_{b}\\otimes\\grad N_{b}\\\\\n\\delta J^{f} & =\\sum_{a}N_{a}\\delta J_{a}^{f} & \\Delta J^{f} & =\\sum_{b}N_{b}\\Delta J_{b}^{f}\\\\\n\\grad\\delta J^{f} & =\\sum_{a}\\delta J_{a}^{f}\\grad N_{a} & \\grad\\Delta J^{f} & =\\sum_{b}\\Delta J_{b}^{f}\\grad N_{b}\n\\end{aligned}\n\\label{eq:fsi-interpolations}\n\\end{equation}\n\n\\end_inset\n\nNote that the \n\\begin_inset Formula $\\grad\\equiv\\frac{\\partial}{\\partial\\mathbf{x}}$\n\\end_inset\n\n operator should be evaluated using \n\\begin_inset Formula $\\mathbf{x}_{n+\\alpha_{f}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe solution of the nonlinear equation \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n is obtained by linearizing this relation as\n\\begin_inset Formula \n\\begin{equation}\n\\delta W+D\\delta W\\left[\\Delta\\mathbf{u}\\right]+D\\delta W\\left[\\Delta\\mathbf{w}\\right]+D\\delta W\\left[\\Delta J^{f}\\right]\\approx0\\,,\\label{eq:fsi-linearized-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere the operator \n\\begin_inset Formula $D\\delta W\\left[\\cdot\\right]$\n\\end_inset\n\n represents the directional derivative of \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n at \n\\begin_inset Formula $\\left(\\mathbf{u}^{s},\\mathbf{w},J^{f}\\right)$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{u}^{s}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{w}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\Delta J^{f}$\n\\end_inset\n\n of \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nTo linearize the virtual work,\n we need to express the integrals appearing in \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta W_{ext}$\n\\end_inset\n\n over the material frame of the finite element solid domain.\n For notational convenience,\n we let \n\\begin_inset Formula $J\\equiv J^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{F}\\equiv\\mathbf{F}^{s}$\n\\end_inset\n\n.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\int_{b}\\boldsymbol{\\sigma}^{s}:\\grad\\delta\\mathbf{v}^{s}\\,dv & =\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}^{s}:\\Grad\\delta\\mathbf{v}^{s}\\,dV\\end{aligned}\n\\,,\\label{eq:fsi-work-material-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{S}^{s}=J\\cdot\\mathbf{F}^{-1}\\cdot\\boldsymbol{\\sigma}^{s}\\cdot\\mathbf{F}^{-T}$\n\\end_inset\n\n is the second Piola-Kirchhoff stress for the solid material.\n Similarly,\n\\begin_inset Formula \n\\begin{equation}\n\\int_{b}\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{w}\\,dv=\\int_{B}\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}:\\Grad\\delta\\mathbf{w}\\,dV\\,.\\label{eq:fsi-work-material-2}\n\\end{equation}\n\n\\end_inset\n\nNext,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & \\int_{b}\\delta\\mathbf{w}\\cdot\\left(\\grad p+\\rho^{f}\\mathbf{a}^{f}\\right)\\,dv\\\\\n & =\\int_{B}\\delta\\mathbf{w}\\cdot\\left(J\\mathbf{F}^{-T}\\cdot\\Grad p+J\\rho^{f}\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\rho^{f}\\left(\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}\\right)\\cdot\\mathbf{W}\\right)\\,dV\n\\end{aligned}\n\\label{eq:fsi-work-material-3-4}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\int_{b}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)-\\frac{\\dot{J}}{J}\\right]\\,dv=\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV\\label{eq:fsi-work-material-5}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\int_{b}\\mathbf{w}\\cdot\\grad\\delta J^{f}\\,dv=\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV\\label{eq:fsi-work-material-6}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{W}=J\\mathbf{F}^{-1}\\cdot\\mathbf{w}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nKeep in mind that we are solving for the motions \n\\begin_inset Formula $\\boldsymbol{\\chi}_{n+1}^{\\iota}$\n\\end_inset\n\n at \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n.\n Therefore,\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\mathbf{u}^{s}\\left[\\Delta\\mathbf{u}\\right] & =\\alpha_{f}\\Delta\\mathbf{u}\\\\\nD\\mathbf{F}\\left[\\Delta\\mathbf{u}\\right] & =\\alpha_{f}\\Grad\\Delta\\mathbf{u}\\\\\nDJ\\left[\\Delta\\mathbf{u}\\right] & =\\alpha_{f}J\\left(\\divg\\Delta\\mathbf{u}\\right)\\\\\nD\\dot{J}\\left[\\Delta\\mathbf{u}\\right] & =D\\left(J\\mathbf{F}^{-T}:\\Grad\\mathbf{v}^{s}\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\alpha_{f}J\\left[\\left(\\divg\\mathbf{v}^{s}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)-\\left(\\grad\\Delta\\mathbf{u}\\right)^{T}:\\mathbf{L}^{s}\\right]\\\\\nD\\mathbf{v}^{s}\\left[\\Delta\\mathbf{u}\\right] & =\\frac{\\alpha_{f}\\gamma}{\\beta\\Delta t}\\Delta\\mathbf{u}\\\\\nD\\mathbf{w}\\left[\\Delta\\mathbf{w}\\right] & =\\alpha_{f}\\Delta\\mathbf{w}\\\\\nDJ^{f}\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\Delta J^{f}\\\\\nD\\rho^{f}\\left[\\Delta J^{f}\\right] & =-\\alpha_{f}\\frac{\\rho^{f}}{J^{f}}\\Delta J^{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nIn general,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{s}$\n\\end_inset\n\n (and thus,\n \n\\begin_inset Formula $\\mathbf{S}^{s}$\n\\end_inset\n\n) is only a function of the solid strain,\n such as the right Cauchy-Green tensor \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{F}^{T}\\cdot\\mathbf{F}$\n\\end_inset\n\n or the Green-Lagrange strain \n\\begin_inset Formula $\\mathbf{E}=\\left(\\mathbf{C}-\\mathbf{I}\\right)/2$\n\\end_inset\n\n.\n \n\\begin_inset Formula \n\\[\nD\\mathbf{E}\\left[\\Delta\\mathbf{u}\\right]=\\frac{\\alpha_{f}}{2}\\left(\\Grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{F}+\\mathbf{F}^{T}\\cdot\\Grad\\Delta\\mathbf{u}\\right)\n\\]\n\n\\end_inset\n\nTherefore,\n following the standard approach in solid mechanics,\n the linearization of \n\\begin_inset Formula $\\mathbf{S}^{s}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{S}^{s} & =D\\mathbf{S}^{s}\\left[\\Delta\\mathbf{u}\\right]=\\frac{\\partial\\mathbf{S}^{s}}{\\partial\\mathbf{E}}:D\\mathbf{E}\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\alpha_{f}\\boldsymbol{\\mathbb{C}}^{s}:\\frac{1}{2}\\left(\\Grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{F}+\\mathbf{F}^{T}\\cdot\\Grad\\Delta\\mathbf{u}\\right)\\\\\n & =\\alpha_{f}\\boldsymbol{\\mathbb{C}}^{s}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right):\\Delta\\boldsymbol{\\varepsilon}\n\\end{aligned}\n\\label{eq:fsi-solid-stress-linearization}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn general,\n \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is a function of the fluid volume ratio \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n and the rate of deformation of the fluid,\n \n\\begin_inset Formula $\\mathbf{D}^{f}=\\frac{1}{2}\\left(\\mathbf{L}^{f}+\\left(\\mathbf{L}^{f}\\right)^{T}\\right)$\n\\end_inset\n\n.\n However,\n since \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n is not a degree of freedom,\n we need to let \n\\begin_inset Formula $\\mathbf{D}^{f}=\\mathbf{D}^{w}+\\mathbf{D}^{s}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{D}^{w}=\\frac{1}{2}\\left(\\mathbf{L}^{w}+\\left(\\mathbf{L}^{w}\\right)^{T}\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{D}^{s}=\\frac{1}{2}\\left(\\mathbf{L}^{s}+\\left(\\mathbf{L}^{s}\\right)^{T}\\right)$\n\\end_inset\n\n.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\nD\\boldsymbol{\\tau}=\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(D\\mathbf{D}^{f}\\left[\\Delta\\mathbf{w}\\right]+D\\mathbf{D}^{f}\\left[\\Delta\\mathbf{u}\\right]\\right)+\\frac{\\partial\\boldsymbol{\\tau}}{\\partial J^{f}}DJ^{f}\\left[\\Delta J^{f}\\right]\\label{eq:fsi-fluid-stress-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}^{\\tau}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial\\mathbf{D}^{f}}\\label{eq:fsi-viscous-tangent-tensor}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of \n\\begin_inset Formula $\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}^{s}:\\Grad\\delta\\mathbf{v}^{s}\\,dV$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\left(\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}^{s}:\\Grad\\delta\\mathbf{v}^{s}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{b}\\alpha_{f}\\left(\\boldsymbol{\\sigma}^{s}:\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\delta\\mathbf{v}^{s}+\\grad\\delta\\mathbf{v}^{s}:\\boldsymbol{\\mathcal{C}}^{s}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\,dv\n\\end{aligned}\n\\label{eq:fsi-term-1-Du}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\mathcal{C}}^{s}=J^{-1}\\left(\\mathbf{F}\\,\\underline{\\otimes}\\,\\mathbf{F}\\right):\\boldsymbol{\\mathbb{C}}^{s}:\\left(\\mathbf{F}^{T}\\,\\underline{\\otimes}\\,\\mathbf{F}^{T}\\right)\n\\]\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}^{s}:\\Grad\\delta\\mathbf{v}^{s}\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right]=0\\label{eq:fsi-term-1-Dw}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}^{s}:\\Grad\\delta\\mathbf{v}^{s}\\,dV\\right)\\left[\\Delta J^{f}\\right]=0\\label{eq:fsi-term-1-DJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretized version produces\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{b}\\alpha_{f}\\left(\\boldsymbol{\\sigma}^{s}:\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\delta\\mathbf{v}^{s}+\\grad\\delta\\mathbf{v}^{s}:\\boldsymbol{\\mathcal{C}}^{s}:\\Delta\\boldsymbol{\\varepsilon}\\right)\\,dv\\\\\n & =\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{uu}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{ab}^{uu}=\\alpha_{f}\\left(\\left(\\grad N_{a}\\cdot\\boldsymbol{\\sigma}^{s}\\cdot\\grad N_{b}\\right)\\mathbf{I}+\\left(\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{s}\\cdot\\grad N_{b}\\right)\\right)\\label{eq:fsi-term-1-Kuu}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{ab}^{uw}=\\mathbf{0}\\label{eq:term-1-Kuw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{uJ}=\\mathbf{0}\\label{eq:term-1-kuJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of \n\\begin_inset Formula $\\int_{B}\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}:\\Grad\\delta\\mathbf{w}\\,dV$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\left(\\int_{B}\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}:\\Grad\\delta\\mathbf{w}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{b}\\alpha_{f}\\left(\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{w}\\cdot\\Delta\\mathbf{G}+\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)\\,dv\n\\end{aligned}\n\\label{eq:fsi-term-2-Du}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\int_{B}\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}:\\Grad\\delta\\mathbf{w}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right] & =\\int_{B}D\\left(\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}\\right)\\left[\\Delta\\mathbf{u}\\right]:\\Grad\\delta\\mathbf{w}\\,dV\\\\\n & =\\int_{B}\\left(D\\boldsymbol{\\tau}\\left[\\Delta\\mathbf{u}\\right]\\cdot J\\mathbf{F}^{-T}+\\boldsymbol{\\tau}\\cdot D\\left(J\\mathbf{F}^{-T}\\right)\\left[\\Delta\\mathbf{u}\\right]\\right):\\Grad\\delta\\mathbf{w}\\,dV\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere we used\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\mathcal{C}}^{\\tau}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial\\mathbf{D}^{f}}\\,.\n\\]\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\int_{B}\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}:\\Grad\\delta\\mathbf{w}\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right] & =\\int_{b}\\alpha_{f}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\grad\\Delta\\mathbf{w}\\,dv\\end{aligned}\n\\label{eq:fsi-term-2-Dw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\int_{B}\\boldsymbol{\\tau}\\cdot J\\mathbf{F}^{-T}:\\Grad\\delta\\mathbf{w}\\,dV\\right)\\left[\\Delta J^{f}\\right]=\\int_{b}\\alpha_{f}\\Delta J^{f}\\boldsymbol{\\tau}_{J}^{\\prime}:\\grad\\delta\\mathbf{w}\\,dv\\label{eq:fsi-term-2-DJ}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}_{J}^{\\prime}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial J^{f}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretized version produces\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{b}\\alpha_{f}\\left(\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{w}\\cdot\\Delta\\mathbf{G}+\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)\\,dv\\\\\n & =\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{wu}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{ab}^{wu}=\\alpha_{f}\\boldsymbol{\\tau}\\cdot\\left(\\grad N_{a}\\otimes\\grad N_{b}-\\grad N_{b}\\otimes\\grad N_{a}\\right)+\\left(\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}\\right)\\cdot\\mathbf{M}\\label{eq:fsi-term-2-Kwu}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\int_{b}\\alpha_{f}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\grad\\Delta\\mathbf{w}\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{ww}\\,dv\\cdot\\Delta\\mathbf{w}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{ab}^{ww}=\\alpha_{f}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}\\label{eq:fsi-term-2-Kww}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\int_{b}\\alpha_{f}\\Delta J^{f}\\boldsymbol{\\tau}_{J}^{\\prime}:\\grad\\delta\\mathbf{w}\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{wJ}\\,dv\\,\\Delta J_{b}^{f}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}_{ab}^{wJ}=\\alpha_{f}N_{b}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad N_{a}\\label{eq:fsi-term-2-kwJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of \n\\begin_inset Formula $\\int_{B}\\delta\\mathbf{w}\\cdot J\\mathbf{F}^{-T}\\cdot\\Grad p\\,dV$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot J\\mathbf{F}^{-T}\\cdot\\Grad p\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{b}\\alpha_{f}\\delta\\mathbf{w}\\cdot\\Delta\\mathbf{G}^{T}\\cdot\\grad p\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-3-Du}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot J\\mathbf{F}^{-T}\\cdot\\Grad p\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right]=0}\\label{eq:fsi-term-3-Dw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot J\\mathbf{F}^{-T}\\cdot\\Grad p\\,dV\\right)\\left[\\Delta J^{f}\\right]\\\\\n & =\\int_{b}\\alpha_{f}\\delta\\mathbf{w}\\cdot\\left(\\Delta J^{f}p^{\\prime\\prime}\\left(J^{f}\\right)\\grad J^{f}+p^{\\prime}\\left(J^{f}\\right)\\grad\\Delta J^{f}\\right)\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-3-DJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretization of these terms produces\n\\begin_inset Formula \n\\[\n\\int_{b}\\alpha_{f}\\delta\\mathbf{w}\\cdot\\Delta\\mathbf{G}^{T}\\cdot\\grad p\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{wu}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{wu}=\\alpha_{f}N_{a}\\left(\\grad p\\otimes\\grad N_{b}-\\grad N_{b}\\otimes\\grad p\\right)}\\label{eq:fsi-term-3-Kwu}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{ww}=\\mathbf{0}}\\label{eq:term-3-Kww}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\int_{b}\\alpha_{f}\\delta\\mathbf{w}\\cdot\\left(\\Delta J^{f}p^{\\prime\\prime}\\left(J^{f}\\right)\\grad J^{f}+p^{\\prime}\\left(J^{f}\\right)\\grad\\Delta J^{f}\\right)\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{wJ}\\,dv\\Delta J_{b}^{f}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{wJ}=\\alpha_{f}N_{a}\\left(N_{b}p^{\\prime\\prime}\\left(J^{f}\\right)\\grad J^{f}+p^{\\prime}\\left(J^{f}\\right)\\grad N_{b}\\right)}\\label{eq:fsi-term-3-kwJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of \n\\begin_inset Formula $\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}\\left[J\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\left(\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}\\right)\\cdot\\mathbf{W}\\right]\\,dV$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}\\left[J\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\left(\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}\\right)\\cdot\\mathbf{W}\\right]\\,dV\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}\\left[J\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\left(\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}\\right)\\cdot\\mathbf{W}\\right]\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\rho^{f}\\left[\\left(\\divg\\Delta\\mathbf{u}\\right)\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\frac{\\gamma}{\\beta\\Delta t}\\grad\\Delta\\mathbf{u}\\cdot\\mathbf{w}+\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}\\Delta\\mathbf{u}\\right]\\,dv\\\\\n & +\\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\rho^{f}\\mathbf{L}^{f}\\cdot\\Delta\\mathbf{G}\\cdot\\mathbf{w}\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-4-Du}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}\\left[J\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\left(\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}\\right)\\cdot\\mathbf{W}\\right]\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right]\\\\\n & =\\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\rho^{f}\\left[\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma\\Delta t}\\mathbf{I}+\\mathbf{L}^{f}\\right)\\cdot\\Delta\\mathbf{w}+\\grad\\Delta\\mathbf{w}\\cdot\\mathbf{w}\\right]\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-4-Dw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}\\left[J\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\left(\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}\\right)\\cdot\\mathbf{W}\\right]\\,dV\\right)\\left[\\Delta J^{f}\\right]\\\\\n & =-\\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho^{f}}{J^{f}}\\Delta J^{f}\\mathbf{a}^{f}\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-4-DJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretized version is given by\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\rho^{f}\\left[\\left(\\divg\\Delta\\mathbf{u}\\right)\\left(\\dot{\\mathbf{w}}+\\dot{\\mathbf{v}}^{s}\\right)+\\frac{\\gamma}{\\beta\\Delta t}\\grad\\Delta\\mathbf{u}\\cdot\\mathbf{w}+\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}\\Delta\\mathbf{u}\\right]\\,dv\\\\\n & +\\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\rho^{f}\\mathbf{L}^{f}\\cdot\\Delta\\mathbf{G}\\cdot\\mathbf{w}\\,dv\\\\\n & =\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{wu}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{ab}^{wu} & =\\alpha_{f}N_{a}\\rho^{f}\\left[\\mathbf{a}^{f}\\otimes\\grad N_{b}-\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{L}^{f}+\\left(\\frac{\\gamma}{\\beta\\Delta t}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)+\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}N_{b}\\right)\\mathbf{I}\\right]\\end{aligned}\n}\\label{eq:fsi-term-4-Kwu}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{b}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\rho^{f}\\left[\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma\\Delta t}\\mathbf{I}+\\mathbf{L}^{f}\\right)\\cdot\\Delta\\mathbf{w}+\\grad\\Delta\\mathbf{w}\\cdot\\mathbf{w}\\right]\\,dv\\\\\n & =\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{ww}\\,dv\\cdot\\Delta\\mathbf{w}_{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{ww}=\\alpha_{f}N_{a}\\rho^{f}\\left[N_{b}\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma\\Delta t}\\mathbf{I}+\\mathbf{L}^{f}\\right)+\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}\\right]}\\label{eq:fsi-term-4-Kww}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n-\\int_{B}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho^{f}}{J^{f}}\\Delta J^{f}\\mathbf{a}^{f}\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{wJ}\\,dv\\Delta J_{b}^{f}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{wJ}=-\\alpha_{f}N_{a}N_{b}\\frac{\\rho^{f}}{J^{f}}\\mathbf{a}^{f}}\\label{eq:fsi-term-4-kwJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of \n\\begin_inset Formula $\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & D\\left(\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(DJ\\left[\\Delta\\mathbf{u}\\right]\\dot{J}^{f}+\\Grad J^{f}\\cdot D\\mathbf{W}\\left[\\Delta\\mathbf{u}\\right]\\right)-D\\dot{J}\\left[\\Delta\\mathbf{u}\\right]\\right]\\,dV\\\\\n & =\\int_{b}\\delta J^{f}\\alpha_{f}\\left[\\left(\\frac{\\dot{J}^{f}}{J^{f}}-\\divg\\mathbf{v}^{s}-\\frac{\\gamma}{\\beta\\Delta t}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)+\\mathbf{w}\\cdot\\left[\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}-\\grad^{T}\\Delta\\mathbf{u}\\right]\\cdot\\frac{\\grad J^{f}}{J^{f}}+\\left(\\grad\\Delta\\mathbf{u}\\right)^{T}:\\mathbf{L}^{s}\\right]\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{b}\\delta J^{f}\\alpha_{f}\\left[\\left(\\frac{\\dot{J}^{f}}{J^{f}}-\\divg\\mathbf{v}^{s}-\\frac{\\gamma}{\\beta\\Delta t}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)+\\mathbf{w}\\cdot\\Delta\\mathbf{G}^{T}\\cdot\\frac{\\grad J^{f}}{J^{f}}+\\left(\\grad\\Delta\\mathbf{u}\\right)^{T}:\\mathbf{L}^{s}\\right]\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-5-Du}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right]\\\\\n & =\\int_{b}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\Delta\\mathbf{w}\\cdot\\grad J^{f}\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-5-Dw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(J\\dot{J}^{f}+\\Grad J^{f}\\cdot\\mathbf{W}\\right)-\\dot{J}\\right]\\,dV\\right)\\left[\\Delta J^{f}\\right]\\\\\n & =\\int_{b}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\left(\\left[\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma\\Delta t}-\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\mathbf{w}\\cdot\\grad J^{f}\\right)\\right]\\Delta J^{f}+\\mathbf{w}\\cdot\\grad\\Delta J^{f}\\right)\\,dv\n\\end{aligned}\n}\\label{eq:fsi-term-5-DJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretization of these terms produces\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{b}\\delta J^{f}\\alpha_{f}\\left[\\left(\\frac{\\dot{J}^{f}}{J^{f}}-\\divg\\mathbf{v}^{s}-\\frac{\\gamma}{\\beta\\Delta t}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)+\\mathbf{w}\\cdot\\Delta\\mathbf{G}^{T}\\cdot\\frac{\\grad J^{f}}{J^{f}}+\\left(\\grad\\Delta\\mathbf{u}\\right)^{T}:\\mathbf{L}^{s}\\right]\\,dv\\\\\n & =\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{Ju}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{k}_{ab}^{Ju} & =\\alpha_{f}N_{a}\\left[\\left(\\frac{\\dot{J}^{f}}{J^{f}}-\\divg\\mathbf{v}^{s}-\\frac{\\gamma}{\\beta\\Delta t}\\right)\\grad N_{b}+\\left(\\grad N_{b}\\otimes\\frac{\\grad J^{f}}{J^{f}}-\\frac{\\grad J^{f}}{J^{f}}\\otimes\\grad N_{b}\\right)\\cdot\\mathbf{w}+\\left(\\mathbf{L}^{s}\\right)^{T}\\cdot\\grad N_{b}\\right]\\end{aligned}\n}\\label{eq:fsi-term-5-kJu}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\int_{b}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\Delta\\mathbf{w}\\cdot\\grad J^{f}\\,dv=\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{Jw}\\,dv\\cdot\\Delta\\mathbf{w}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{Jw}=\\alpha_{f}\\frac{N_{a}N_{b}}{J^{f}}\\grad J^{f}}\\label{eq:fsi-term-5-kJw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{b}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\left(\\left[\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma\\Delta t}-\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\mathbf{w}\\cdot\\grad J^{f}\\right)\\right]\\Delta J^{f}+\\mathbf{w}\\cdot\\grad\\Delta J^{f}\\right)\\,dv\\\\\n & =\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\int_{b}k_{ab}^{JJ}\\,dv\\Delta J_{b}^{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{k_{ab}^{JJ}=\\alpha_{f}N_{a}\\frac{1}{J^{f}}\\left(N_{b}\\left[\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma\\Delta t}-\\frac{1}{J^{f}}\\left(\\dot{J}^{f}+\\mathbf{w}\\cdot\\grad J^{f}\\right)\\right]+\\mathbf{w}\\cdot\\grad N_{b}\\right)}\\label{eq:fsi-term-5-kJJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of \n\\begin_inset Formula $-\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n-\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int_{b}\\alpha_{f}\\mathbf{w}\\cdot\\Delta\\mathbf{G}^{T}\\cdot\\grad\\delta J^{f}\\,dv}\\label{eq:fsi-term-6-Du}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & D\\left(\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{B}\\Grad\\delta J^{f}\\cdot D\\mathbf{W}\\left[\\Delta\\mathbf{w}\\right]\\,dV\\\\\n & =\\int_{B}\\alpha_{f}\\Delta\\mathbf{w}\\cdot\\grad\\delta J^{f}\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right]=\\int_{B}\\alpha_{f}\\Delta\\mathbf{w}\\cdot\\grad\\delta J^{f}\\,dv}\\label{eq:fsi-term-6-Dw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\Grad\\delta J^{f}\\cdot\\mathbf{W}\\,dV\\right)\\left[\\Delta J^{f}\\right]=0}\\label{eq:fsi-term-6-DJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretization of these terms produces\n\\begin_inset Formula \n\\[\n\\int_{b}\\alpha_{f}\\mathbf{w}\\cdot\\Delta\\mathbf{G}^{T}\\cdot\\grad\\delta J^{f}\\,dv=\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\int_{v}\\mathbf{k}_{ab}^{Ju}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{Ju}=\\alpha_{f}\\left(\\grad N_{b}\\otimes\\grad N_{a}-\\grad N_{a}\\otimes\\grad N_{b}\\right)\\cdot\\mathbf{w}}\\label{eq:fsi-term-6-kJu}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\int_{B}\\alpha_{f}\\Delta\\mathbf{w}\\cdot\\grad\\delta J^{f}\\,dv=\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{Jw}\\,dv\\cdot\\Delta\\mathbf{w}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{Jw}=\\alpha_{f}N_{b}\\grad N_{a}}\\label{eq:fsi-term-6-kJw}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{k_{ab}^{JJ}=0}\\label{eq:fsi-term-6-kJJ}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nBody force term \n\\begin_inset Formula $\\int_{b}\\delta\\mathbf{w}\\cdot\\rho^{f}\\mathbf{b}\\,dv$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe body force term may be discretized as\n\\begin_inset Formula \n\\[\n\\int_{b}\\delta\\mathbf{w}\\cdot\\rho^{f}\\mathbf{b}\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\int_{b}N_{a}\\rho^{f}\\mathbf{b}\\,dv\n\\]\n\n\\end_inset\n\nTo linearizes this expression,\n we first evaluate it in the material domain,\n\\begin_inset Formula \n\\[\n\\int_{b}\\delta\\mathbf{w}\\cdot\\rho^{f}\\mathbf{b}\\,dv=\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}J\\mathbf{b}\\,dV\n\\]\n\n\\end_inset\n\nNow,\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}J\\mathbf{b}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int_{b}\\alpha_{f}\\rho^{f}\\left(\\divg\\Delta\\mathbf{u}\\right)\\delta\\mathbf{w}\\cdot\\mathbf{b}\\,dv}\\label{eq:fsi-bf-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}J\\mathbf{b}\\,dV\\right)\\left[\\Delta\\mathbf{w}\\right]=\\int_{b}\\delta\\mathbf{w}\\cdot\\rho^{f}D\\mathbf{b}\\left[\\Delta\\mathbf{w}\\right]\\,dv}\\label{eq:fsi-bf-2}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\delta\\mathbf{w}\\cdot\\rho^{f}J\\mathbf{b}\\,dV\\right)\\left[\\Delta J^{f}\\right]=-\\int_{b}\\Delta J^{f}\\frac{\\alpha_{f}\\rho^{f}}{J^{f}}\\delta\\mathbf{w}\\cdot\\mathbf{b}\\,dv}\\label{eq:fsi-bf-3}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discrete forms of these expressions are given by\n\\begin_inset Formula \n\\[\n\\int_{b}\\alpha_{f}\\rho^{f}\\left(\\divg\\Delta\\mathbf{u}\\right)\\delta\\mathbf{w}\\cdot\\mathbf{b}\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{wu}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{wu}=\\alpha_{f}\\rho^{f}N_{a}\\mathbf{b}\\otimes\\grad N_{b}}\\label{eq:fsi-bf-4}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{ww}=\\alpha_{f}\\rho^{f}N_{a}N_{b}\\frac{\\partial\\mathbf{b}}{\\partial\\mathbf{w}}}\\label{eq:fsi-bf-5}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n-\\int_{b}\\Delta J^{f}\\frac{\\alpha_{f}\\rho^{f}}{J^{f}}\\delta\\mathbf{w}\\cdot\\mathbf{b}\\,dv=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{k}_{ab}^{wJ}\\,dv\\,\\Delta J_{b}^{f}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{wJ}=-\\alpha_{f}N_{a}N_{b}\\frac{\\rho^{f}}{J^{f}}\\mathbf{b}}\\label{eq:fsi-bf-6}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFluid traction acting on solid interface\n\\end_layout\n\n\\begin_layout Standard\nAt a fluid-solid interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n the fluid traction \n\\begin_inset Formula $\\mathbf{t}^{f}=\\boldsymbol{\\sigma}^{f}\\cdot\\mathbf{n}$\n\\end_inset\n\n acts on the solid surface,\n \n\\begin_inset Formula $\\mathbf{t}^{s}=-\\mathbf{t}^{f}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the outward normal to the fluid surface.\n The resulting virtual work on the solid domain is\n\\begin_inset Formula \n\\[\n\\delta G=\\int_{\\Gamma}\\delta\\mathbf{v}^{s}\\cdot\\mathbf{t}_{n+\\alpha_{f}}^{s}\\,da=\\int_{\\Gamma}-\\delta\\mathbf{v}^{s}\\cdot\\boldsymbol{\\sigma}^{f}\\cdot\\mathbf{n}\\,da=\\int_{\\Gamma_{\\eta}}-\\delta\\mathbf{v}^{s}\\cdot\\boldsymbol{\\sigma}^{f}\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\nThe linearization of this work is given by\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta\\mathbf{u}\\right]=\\int_{\\Gamma_{\\eta}}-\\delta\\mathbf{v}^{s}\\cdot\\alpha_{f}\\left[\\left(\\boldsymbol{\\mathcal{C}}^{v}:\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)+\\boldsymbol{\\sigma}^{f}\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\times\\mathbf{g}_{2}+\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}\\right)\\right]\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\mathbf{g}_{\\alpha}=\\frac{\\partial\\mathbf{x}}{\\partial\\eta^{\\alpha}}\n\\]\n\n\\end_inset\n\nare covariant basis vectors on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n.\n Similarly,\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta\\mathbf{w}\\right]=\\int_{\\Gamma_{\\eta}}-\\delta\\mathbf{v}^{s}\\cdot\\alpha_{f}\\left[\\left(\\boldsymbol{\\mathcal{C}}^{v}:\\grad\\Delta\\mathbf{w}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\right]\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta J^{f}\\right]=\\int_{\\Gamma_{\\eta}}-\\Delta J^{f}\\delta\\mathbf{v}^{s}\\cdot\\alpha_{f}\\left[\\left(-p^{\\prime}\\left(J^{f}\\right)+\\boldsymbol{\\tau}_{J}^{\\prime}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\right]\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe discretized form of these equations is\n\\begin_inset Formula \n\\[\n\\delta G=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\int_{\\Gamma}\\mathbf{f}_{a}\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{f}_{a}=-N_{a}\\boldsymbol{\\sigma}^{f}\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)}\\label{eq:fsi-ft-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta\\mathbf{u}\\right]=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\sum_{b}\\int_{\\Gamma_{\\eta}}\\mathbf{K}_{ab}^{uu}\\,d\\eta^{1}d\\eta^{2}\\cdot\\Delta\\mathbf{u}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{uu}=-\\alpha_{f}N_{a}\\left(\\left[\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}^{v}\\cdot\\grad N_{b}\\right]\\cdot\\mathbf{M}+\\boldsymbol{\\sigma}^{f}\\cdot\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}-\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}\\right\\} \\right)}\\label{eq:fsi-ft-2}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta\\mathbf{w}\\right]=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\sum_{b}\\int_{\\Gamma_{\\eta}}\\mathbf{K}_{ab}^{uw}\\,d\\eta^{1}d\\eta^{2}\\cdot\\Delta\\mathbf{w}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{uw}=-\\alpha_{f}N_{a}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}^{v}\\cdot\\grad N_{b}}\\label{eq:fsi-ft-3}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta J^{f}\\right]=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\sum_{b}\\int_{\\Gamma_{\\eta}}\\mathbf{k}_{ab}^{uJ}\\,d\\eta^{1}d\\eta^{2}\\,\\Delta J_{b}^{f}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{k}_{ab}^{uJ}=-\\alpha_{f}N_{a}N_{b}\\left(-p^{\\prime}\\left(J^{f}\\right)+\\boldsymbol{\\tau}_{J}^{\\prime}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)}\\label{eq:fsi-ft-4}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSpecial Boundary Conditions\n\\end_layout\n\n\\begin_layout Subsubsection\nBackflow Stabilization\n\\end_layout\n\n\\begin_layout Standard\nLet the normal component of the viscous traction be given by \n\\begin_inset Formula \n\\begin{equation}\nt_{n}^{\\tau}=\\begin{cases}\n\\beta\\rho_{r}w_{n}^{2} & w_{n}<0\\\\\n0 & w_{n}\\ge0\n\\end{cases}\\,.\\label{eq:fsi-BFS-normal-traction}\n\\end{equation}\n\n\\end_inset\n\nThen,\n the contribution of this traction to the virtual external work \n\\begin_inset Formula $\\delta W_{ext}$\n\\end_inset\n\n is \n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\int_{\\partial\\Omega}\\delta\\mathbf{w}\\cdot t_{n}^{\\tau}\\mathbf{n}\\,da\\,.\\label{eq:fsi-BFSckflow-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{w}$\n\\end_inset\n\n in the relative velocity is given by\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{w}\\right]=\\int_{\\partial\\Omega}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\mathbf{K}_{n}\\cdot\\Delta\\mathbf{w}\\,da\\,,\\label{eq:fsi-BFS-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{n}=\\begin{cases}\n2\\beta\\rho_{r}w_{n}\\left(\\mathbf{n}\\otimes\\mathbf{n}\\right) & v_{n}<0\\\\\n\\mathbf{0} & v_{n}\\ge0\n\\end{cases}\\,.\\label{eq:fsi-BFS-stiffness}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta\\mathbf{u}\\right]=\\int_{\\partial\\Omega_{\\eta}}\\delta\\mathbf{w}\\cdot\\alpha_{f}t_{n}^{\\tau}\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\int_{\\partial\\Omega_{\\eta}}\\mathbf{f}_{a}^{n}\\,d\\eta^{1}d\\eta^{2},\\quad\\boxed{\\mathbf{f}_{a}^{n}=N_{a}t_{n}^{\\tau}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)}\\,,\\label{eq:fsi-BFS-discretized-work}\n\\end{equation}\n\n\\end_inset\n\nwhereas the discretized form of \n\\begin_inset Formula $D\\delta G\\left[\\Delta\\mathbf{v}\\right]$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{v}\\right]=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\left(\\int_{\\partial\\Omega_{\\eta}}\\mathbf{K}_{ab}^{wu}\\,d\\eta^{1}d\\eta^{2}\\cdot\\Delta\\mathbf{u}+\\int_{\\partial\\Omega_{\\eta}}\\mathbf{K}_{ab}^{ww}\\,d\\eta^{1}d\\eta^{2}\\cdot\\Delta\\mathbf{w}_{b}\\right)\\,,\\label{eq:BFS-discretized-tangent-1}\n\\end{equation}\n\n\\end_inset\n\nwith\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{ww}=\\alpha_{f}N_{a}N_{b}\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\mathbf{K}_{n}}\\,,\\label{eq:fsi-BFS-Kww}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{wu}=\\alpha_{f}N_{a}t_{n}^{\\tau}\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}-\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}\\right\\} }\\label{eq:fsi-BFS-Kwu}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTangential Stabilization\n\\end_layout\n\n\\begin_layout Standard\nThe tangential traction given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}_{t}^{\\tau}=-\\beta\\rho_{r}\\left|\\mathbf{w}_{t}\\right|\\mathbf{w}_{t}\\,.\\label{eq:fsi-tangential-stabilization}\n\\end{equation}\n\n\\end_inset\n\nThis form shows that \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}$\n\\end_inset\n\n opposes tangential flow.\n The external virtual work for this traction is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\int_{\\partial\\Omega}\\delta\\mathbf{w}\\cdot\\mathbf{t}_{t}^{\\tau}\\,da\\,.\\label{eq:fsi-TS-external-work}\n\\end{equation}\n\n\\end_inset\n\nIts linearization along an increment \n\\begin_inset Formula $\\Delta\\mathbf{w}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{w}\\right]=\\int_{\\partial\\Omega}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\mathbf{K}_{t}\\cdot\\Delta\\mathbf{w}\\,da\\,,\\label{eq:fsi-TS-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere it can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{t}=-\\beta\\rho_{r}\\left|\\mathbf{w}_{t}\\right|\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}+\\frac{\\mathbf{w}_{t}}{\\left|\\mathbf{w}_{t}\\right|}\\otimes\\frac{\\mathbf{w}_{t}}{\\left|\\mathbf{w}_{t}\\right|}\\right)\\,.\\label{eq:fsi-TS-stiffness}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\[\nD\\delta G\\left[\\Delta\\mathbf{u}\\right]=\\int_{\\partial\\Omega_{\\eta}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\left(\\mathbf{t}_{t}^{\\tau}\\otimes\\mathbf{n}\\right)\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\times\\mathbf{g}_{2}+\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}\\right)\\,d\\eta^{1}d\\eta^{2}\\,.\n\\]\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\int_{\\partial\\Omega}\\mathbf{f}_{a}^{\\tau}\\,d\\eta^{1}d\\eta^{2}\\,,\\quad\\boxed{\\mathbf{f}_{a}^{\\tau}=N_{a}\\mathbf{t}_{t}^{\\tau}\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,.\\label{eq:fsi-TS-discretized-work}\n\\end{equation}\n\n\\end_inset\n\nThe discretized form of \n\\begin_inset Formula $D\\delta G\\left[\\Delta\\mathbf{v}\\right]$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G\\left[\\Delta\\mathbf{v}\\right]=\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\int_{\\partial\\Omega_{\\eta}}\\left(\\mathbf{K}_{ab}^{wu}\\cdot\\Delta\\mathbf{u}_{b}+\\mathbf{K}_{ab}^{ww}\\cdot\\Delta\\mathbf{w}_{b}\\right)\\,d\\eta^{1}d\\eta^{2}\\,,\\label{eq:TS-discretized-tangent-1}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{ww}=\\alpha_{f}N_{a}N_{b}\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\mathbf{K}_{t}}\\,,\\label{eq:fsi-TS-Kww}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{wu}=\\alpha_{f}N_{a}\\left(\\mathbf{t}_{t}^{\\tau}\\otimes\\mathbf{n}\\right)\\cdot\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\mathbf{g}_{1}-\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\mathbf{g}_{2}\\right\\} }\\,.\\label{eq:fsi-TS-Kwu}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nWeak Formulation for BFSI\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:BFSI-Weak-Formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nVirtual Work and Weak Form \n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BFSI-Virtual-Work\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work statement is used to enforce the three governing equations needed to solve for the nodal DOFs \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n namely the mixture mass balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-kin-constr\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the fluid momentum balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-momentum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and the solid momentum balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-solid-momentum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n We may rewrite the momentum balance equations to facilitate the enforcement of natural traction boundary conditions given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-fluid-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-solid-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Using \n\\begin_inset Formula $\\boldsymbol{\\tau}^{f}=\\varphi^{f}\\boldsymbol{\\tau}$\n\\end_inset\n\n,\n these become \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\varphi^{s}\\left(-\\rho_{T}^{f}\\mathbf{a}^{f}+\\rho_{T}^{s}\\mathbf{a}^{s}\\right)= & -\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\mathbf{\\boldsymbol{\\tau}}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}+\\divg\\left(-\\varphi^{s}\\boldsymbol{\\tau}+\\boldsymbol{\\sigma}^{e}\\right)\\\\\n & +\\varphi^{s}\\left(-\\rho_{T}^{f}\\mathbf{b}^{f}+\\rho_{T}^{s}\\mathbf{b}^{s}\\right)+\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\,,\\\\\n\\rho_{T}^{f}\\mathbf{a}^{f}= & -\\grad p+\\frac{1}{\\phi^{f}}\\mathbf{\\boldsymbol{\\tau}}\\cdot\\grad\\varphi^{f}+\\divg\\boldsymbol{\\tau}\\\\\n & +\\rho_{T}^{f}\\mathbf{b}^{f}-\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\,.\n\\end{aligned}\n\\label{eq:bfsi-gov-eqn-jump}\n\\end{equation}\n\n\\end_inset\n\nThe virtual work statement for a Galerkin finite element formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\int_{\\Omega^{b}}\\delta\\mathbf{v}^{s}\\cdot\\left[\\divg\\left(-\\varphi^{s}\\boldsymbol{\\tau}+\\boldsymbol{\\sigma}^{e}\\right)-\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}+\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right]\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}^{s}\\cdot\\varphi^{s}\\left(-\\rho_{T}^{f}\\left(\\mathbf{b}^{f}-\\mathbf{a}^{f}\\right)+\\rho_{T}^{s}\\left(\\mathbf{b}^{s}-\\mathbf{a}^{s}\\right)\\right)\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\left[\\divg\\boldsymbol{\\tau}-\\grad p+\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\phi^{f}-\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right]\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\rho_{T}^{f}\\left(\\mathbf{b}^{f}-\\mathbf{a}^{f}\\right)\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta J^{f}\\left[\\divg\\mathbf{w}+\\frac{\\dot{J}^{s}}{J^{s}}-\\frac{1}{J^{f}}\\left(\\varphi^{f}\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)\\right]\\,dv\\,.\n\\end{aligned}\n\\label{eq:bfsi-virtual-work-strong}\n\\end{equation}\n\n\\end_inset\n\nThese integrals are evaluated in the current configuration of \n\\begin_inset Formula $\\Omega^{b}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{s}$\n\\end_inset\n\n is the virtual solid velocity,\n \n\\begin_inset Formula $\\delta\\mathbf{w}$\n\\end_inset\n\n is the virtual relative fluid volumetric flux,\n and \n\\begin_inset Formula $\\delta J^{f}$\n\\end_inset\n\n is the virtual fluid energy density.\n Integrating by parts and using the divergence theorem,\n the weak form of this statement may be written as \n\\begin_inset Formula $\\delta W=\\delta W_{ext}-\\delta W_{int}$\n\\end_inset\n\n where the internal virtual work is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int} & =\\int_{\\Omega^{b}}\\left[\\left(-\\varphi^{s}\\boldsymbol{\\tau}+\\boldsymbol{\\sigma}^{e}\\right):\\grad\\delta\\mathbf{v}^{s}-\\delta\\mathbf{v}^{s}\\cdot\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right]\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}^{s}\\cdot\\varphi^{s}\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}-\\rho_{T}^{f}\\mathbf{a}^{f}+\\rho_{T}^{s}\\mathbf{a}^{s}\\right)\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{w}\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\left(\\grad p+\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\left(\\rho_{T}^{f}\\mathbf{a}^{f}-\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\varphi^{f}\\right)\\,dv\\\\\n & +\\int_{\\Omega^{b}}\\mathbf{w}\\cdot\\grad\\delta J^{f}\\,dv\\,,\\\\\n & +\\int_{\\Omega^{b}}\\delta J^{f}\\left[\\frac{1}{J^{f}}\\left(\\varphi^{f}\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)-\\frac{\\dot{J}^{s}}{J^{s}}\\right]\\,dv\n\\end{aligned}\n\\label{eq:bfsi-int-virtual-work}\n\\end{equation}\n\n\\end_inset\n\n and the external part is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{ext} & =\\int_{\\partial\\Omega^{b}}\\delta\\mathbf{v}^{s}\\cdot\\mathbf{t}^{\\sigma}\\,da\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}^{s}\\cdot\\varphi^{s}\\left(-\\rho_{T}^{f}\\mathbf{b}^{f}+\\rho_{T}^{s}\\mathbf{b}^{s}\\right)\\,dv\\\\\n & +\\int_{\\partial\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\mathbf{t}^{\\tau}\\,da+\\int_{\\Omega^{f}}\\delta\\mathbf{w}\\cdot\\rho_{T}^{f}\\mathbf{b}^{f}\\,dv\\\\\n & +\\int_{\\partial\\Omega^{b}}\\delta J^{f}w_{n}\\,da\\,.\n\\end{aligned}\n\\label{eq:bfsi-ext-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{\\sigma}=-\\varphi^{s}\\mathbf{t}^{\\tau}+\\mathbf{t}^{e}\\,.\\label{eq:bfsi-sigma-traction}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{t}^{e}=\\boldsymbol{\\sigma}^{e}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the elastic traction,\n \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the true fluid viscous traction,\n and \n\\begin_inset Formula $w_{n}=\\mathbf{w}_{n}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the normal component of the relative fluid flux on the boundary \n\\begin_inset Formula $\\partial\\Omega^{b}$\n\\end_inset\n\n,\n whose outward unit normal is \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n The traction \n\\begin_inset Formula $\\mathbf{t}^{\\sigma}$\n\\end_inset\n\n emerges from the jump condition in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-solid-mtm-jump\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The integrands of the surface integrals represent the natural boundary conditions for this formulation.\n If boundary conditions are not set explicitly on \n\\begin_inset Formula $\\partial\\Omega^{b}$\n\\end_inset\n\n,\n the natural boundary conditions are \n\\begin_inset Formula $\\mathbf{t}^{\\sigma}=\\mathbf{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n.\n These natural boundary conditions are consistent with the jump conditions presented above.\n Essential boundary conditions are prescribed on the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n relative volumetric fluid flux \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n and fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n which are also consistent with the above jump conditions.\n In particular,\n an essential no-slip boundary condition may be prescribed on \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n by setting \n\\begin_inset Formula $\\mathbf{w}=\\mathbf{0}$\n\\end_inset\n\n.\n A symmetry plane may be prescribed with the essential boundary condition \n\\begin_inset Formula $u_{n}\\equiv\\mathbf{u}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n and the natural boundary conditions \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn this formulation,\n the mixture traction is defined as \n\\begin_inset Formula $\\mathbf{t}=-p\\mathbf{n}+\\mathbf{t}^{e}+\\varphi^{f}\\mathbf{t}^{\\tau}$\n\\end_inset\n\n,\n which may also be written as \n\\begin_inset Formula $\\mathbf{t}=-p\\mathbf{n}+\\mathbf{t}^{\\sigma}+\\mathbf{t}^{\\tau}$\n\\end_inset\n\n.\n Because of the way we chose to split the internal and external virtual work in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-int-virtual-work\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-ext-virtual-work\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n is not a natural boundary condition in this formulation.\n In this expression for \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}^{\\sigma}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n may be prescribed as natural boundary conditions,\n whereas \n\\begin_inset Formula $p$\n\\end_inset\n\n may be prescribed as an essential boundary condition on \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n using \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n However,\n there are two general scenarios where \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n needs to be prescribed on a region of the boundary \n\\begin_inset Formula $\\partial\\Omega^{b}$\n\\end_inset\n\n with incomplete prior knowledge of \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}^{\\sigma}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n:\n (1) When a BFSI boundary \n\\begin_inset Formula $\\Gamma^{b}$\n\\end_inset\n\n represents a free surface (such as the fluid surface in channel flow),\n the mixture traction boundary condition requires that \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{0}$\n\\end_inset\n\n,\n in which case it is necessary to explicitly enforce \n\\begin_inset Formula $-p\\mathbf{n}+\\mathbf{t}^{\\sigma}+\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n as a constraint equation on that boundary,\n to impart the free surface \n\\begin_inset Formula $\\Gamma^{b}$\n\\end_inset\n\n its natural shape.\n (2) At a biphasic-solid interface \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n must balance the traction acting on the adjoining solid domain.\n Since \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n is continuous across \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n due to shared nodes,\n the solid natural boundary condition \n\\begin_inset Formula $\\mathbf{t}^{\\sigma}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n is already accounted for by the deformation,\n so that it is only necessary to prescribe the portion \n\\begin_inset Formula $-p\\mathbf{n}+\\mathbf{t}^{\\tau}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n on the solid domain,\n thus \n\\begin_inset Formula $-p\\mathbf{n}+\\mathbf{t}^{\\tau}+\\mathbf{t}^{s}=\\mathbf{0}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{t}^{s}$\n\\end_inset\n\n is the (equal and opposite) traction acting on the solid domain.\n In both cases,\n the form of this traction boundary condition is the same,\n with \n\\begin_inset Formula $\\mathbf{t}^{e}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{t}^{s}$\n\\end_inset\n\n representing the tractions acting on \n\\begin_inset Formula $\\Gamma^{b}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n,\n respectively.\n\\end_layout\n\n\\begin_layout Standard\nFor both of these cases,\n the resulting virtual work on the free surface \n\\begin_inset Formula $\\Gamma^{b}$\n\\end_inset\n\n or the interface \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\delta F=-\\int_{\\Gamma}\\delta\\mathbf{v}^{s}\\cdot\\left(-p\\mathbf{n}+\\mathbf{t}^{\\tau}\\right)\\,da\\,,\\label{eq:bfsi-traction-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere the elemental area \n\\begin_inset Formula $da$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n may be evaluated from the covariant basis vectors \n\\begin_inset Formula $\\mathbf{g}_{\\alpha}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha=1,2$\n\\end_inset\n\n),\n\\begin_inset Formula \n\\begin{equation}\nda=\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\,d\\eta^{1}d\\eta^{2}\\,,\\label{eq:bfsi-diff-area}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}=\\frac{\\partial\\mathbf{x}\\left(\\eta^{1},\\eta^{2}\\right)}{\\partial\\eta^{\\alpha}}\\,,\\label{eq:bfsi-covar-vectors}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{x}\\left(\\eta^{1},\\eta^{2}\\right)$\n\\end_inset\n\n is the parametric representation of \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n defined on the solid constituent.\n The outward normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\Omega^{b}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n is evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{g}_{1}\\times\\mathbf{g}_{2}}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,.\\label{eq:bfsi-norm-vector}\n\\end{equation}\n\n\\end_inset\n\nAs a result,\n the virtual work can be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\delta F=-\\int_{\\Gamma}\\delta\\mathbf{v}^{s}\\cdot\\left(-p\\mathbf{I}+\\boldsymbol{\\tau}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\,d\\eta^{1}d\\eta^{2}\\,.\\label{eq:bfsi-traction-final}\n\\end{equation}\n\n\\end_inset\n\nIn FEBio this boundary condition is called \n\\emph on\nBFSI traction\n\\emph default\n,\n which the user must explicitly prescribe on free surfaces \n\\begin_inset Formula $\\Gamma^{b}$\n\\end_inset\n\n and deformable interfaces \n\\begin_inset Formula $\\Gamma^{bs}$\n\\end_inset\n\n.\n The code automatically determines which of these two types of interfaces is being considered.\n\\end_layout\n\n\\begin_layout Subsection\nBFSI Linearization \n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:BFSI-Linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUsing the virtual work integral \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n such that \n\\begin_inset Formula \n\\begin{equation}\n\\delta W+D\\delta W\\left[\\Delta\\mathbf{u}\\right]+D\\delta W\\left[\\Delta\\mathbf{w}\\right]+D\\delta W\\left[\\Delta J^{f}\\right]\\approx0\\,,\\label{eq:Virtual-Work-Expanded-BFSI}\n\\end{equation}\n\n\\end_inset\n\n it may be expanded as\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\delta W_{int}\\left[\\Delta\\mathbf{u}\\right]+D\\delta W_{int}\\left[\\Delta\\mathbf{w}\\right]+D\\delta W_{int}\\left[\\Delta J^{f}\\right]\\\\\n & -D\\delta W_{ext}\\left[\\Delta\\mathbf{u}\\right]-D\\delta W_{ext}\\left[\\Delta\\mathbf{w}\\right]-D\\delta W_{ext}\\left[\\Delta J^{f}\\right]\\\\\n & \\approx\\delta W_{ext}-\\delta W_{int}\\,.\n\\end{aligned}\n\\label{eq:linearized-work-split-bfsi}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearizations of integrals are performed in the material frame of the solid domain of \n\\begin_inset Formula $\\Omega^{b}$\n\\end_inset\n\n,\n allowing us to linearize \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n along increments \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{w}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\Delta J^{f}$\n\\end_inset\n\n by simply bringing the directional derivative operator inside the integrals of Eqs.\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-int-virtual-work\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-ext-virtual-work\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For notational convenience,\n we let \n\\begin_inset Formula $J\\equiv J^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{F}\\equiv\\mathbf{F}^{s}$\n\\end_inset\n\n.\n Thus,\n the conversion of the internal virtual work to the material frame of the solid produces \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int}= & \\int_{\\Omega^{b}}\\left(-\\varphi_{r}^{s}\\mathbf{T}_{d}+\\mathbf{F}\\cdot\\boldsymbol{\\sigma}^{e}\\cdot\\mathbf{F}^{T}\\right):\\Grad\\delta\\mathbf{v}\\cdot\\mathbf{F}^{-1}dV\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\varphi_{r}^{s}\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\mathbf{F}^{-T}\\cdot\\Grad\\left(\\frac{\\varphi^{f}}{\\varphi^{s}}\\right)-\\rho_{T}^{f}\\mathbf{a}^{f}+\\rho_{T}^{s}\\dot{\\mathbf{v}}^{s}\\right)dV\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-J^{2}\\mathbf{F}^{-T}\\cdot\\mathbf{K}^{-1}\\cdot\\mathbf{F}^{-1}\\cdot\\mathbf{w}dV\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot J\\left(\\rho_{T}^{f}\\mathbf{a}^{f}+\\mathbf{F}^{-T}\\cdot\\Grad p\\right)dV\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot J\\left(-\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\mathbf{F}^{-T}\\cdot\\Grad\\varphi^{f}+J\\mathbf{F}^{-T}\\cdot\\mathbf{K}^{-1}\\cdot\\mathbf{F}^{-1}\\cdot\\mathbf{w}\\right)dV\\\\\n & +\\int_{\\Omega^{b}}J\\boldsymbol{\\tau}:\\Grad\\delta\\mathbf{w}\\cdot\\mathbf{F}^{-1}dV\\\\\n & +\\int_{\\Omega^{b}}J\\Grad\\delta J^{f}\\cdot\\mathbf{F}^{-1}\\cdot\\mathbf{w}+\\delta J^{f}\\left(J\\frac{\\varphi^{f}}{J^{f}}\\frac{D^{f}J^{f}}{Dt}-\\dot{J}^{s}\\right)dV\\thinspace,\n\\end{aligned}\n\\label{eq:int-virtual-work-material-BFSI}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{S}=J\\,\\mathbf{F}^{-1}\\cdot\\boldsymbol{\\sigma}^{e}\\cdot\\mathbf{F}^{-T}$\n\\end_inset\n\n is the second Piola-Kirchhoff stress for the solid constituent of the mixture,\n \n\\begin_inset Formula $\\mathbf{W}=J\\mathbf{F}^{-1}\\cdot\\mathbf{w}$\n\\end_inset\n\n is the Piola transformation of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $dV=J^{-1}dv$\n\\end_inset\n\n is an elemental volume of \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n in its material frame \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n In addition,\n \n\\begin_inset Formula $\\mathbf{K}^{-1}=J^{-1}\\mathbf{F}^{T}\\cdot\\mathbf{k}^{-1}\\cdot\\mathbf{F}$\n\\end_inset\n\n is the inverse of the permeability tensor in the material frame.\n Note that in the material frame,\n the fluid acceleration \n\\begin_inset Formula $\\mathbf{a}^{f}$\n\\end_inset\n\n is \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}^{f}=\\frac{1}{\\varphi^{f}}\\left(\\dot{\\mathbf{w}}+\\varphi^{f}\\dot{\\mathbf{v}}^{s}-\\frac{\\varphi^{s}}{\\varphi^{f}}\\frac{\\dot{J}}{J}\\mathbf{w}+\\frac{1}{J}\\left(\\frac{1}{\\varphi^{f}}\\Grad\\mathbf{w}+\\Grad\\mathbf{v}^{s}-\\frac{1}{\\varphi^{f^{2}}}\\mathbf{w}\\otimes\\Grad\\varphi^{f}\\right)\\cdot\\mathbf{W}\\right)\\,.\\label{eq:material-fluid-accel-BFSI}\n\\end{equation}\n\n\\end_inset\n\n The linearization of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n is then performed along an increment \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n,\n and the integral is reverted back to the spatial frame,\n yielding for the displacement equations\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta\\mathbf{u}\\right]= & \\int_{\\Omega^{b}}\\alpha_{f}\\left(\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}^{e}:\\grad\\delta\\mathbf{v}+\\grad\\delta\\mathbf{v}:\\boldsymbol{\\mathcal{C}}:\\grad\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\varphi^{s}\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{v}\\cdot\\left(\\grad\\Delta\\mathbf{u}+\\frac{\\varphi^{s}}{\\varphi^{f}}\\divg\\Delta\\mathbf{u}\\mathbf{I}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\varphi^{s}\\grad\\delta\\mathbf{v}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{D}^{w}+\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\varphi^{s}\\grad\\delta\\mathbf{v}:\\frac{1}{\\varphi^{f^{2}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:2\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{w}\\otimes\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\varphi^{s}\\grad\\delta\\mathbf{v}:\\frac{1}{\\varphi^{f^{2}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{w}\\otimes\\left(-\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\varphi^{s}\\grad\\delta\\mathbf{v}:\\frac{1}{\\varphi^{f^{2}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{w}\\otimes\\left(-\\left(\\divg\\Delta\\mathbf{u}\\right)\\grad\\varphi^{f}+\\varphi^{s}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\divg\\Delta\\mathbf{u}+\\grad^{T}\\Delta\\mathbf{u}\\right)\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\frac{1}{\\varphi^{s}}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{D}^{w}+\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:2\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{w}\\otimes\\grad\\varphi^{f}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{w}\\otimes\\left(-\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\varphi^{f}\\right)\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{w}\\otimes\\left(-\\divg\\Delta\\mathbf{u}\\grad\\varphi^{f}+\\varphi^{s}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\varphi^{s}\\left(\\divg\\Delta\\mathbf{u}\\right)\\dot{\\mathbf{v}}^{s}+\\varphi^{f}\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\left[\\left(\\left(-\\frac{1}{\\varphi^{f}}\\right)\\frac{\\dot{J}}{J}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\divg\\Delta\\mathbf{u}-\\grad^{T}\\Delta\\mathbf{u}:\\mathbf{L}^{s}\\right]\\mathbf{w}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\divg\\Delta\\mathbf{u}\\grad\\mathbf{w}+\\frac{\\gamma}{\\beta\\Delta t}\\grad\\Delta\\mathbf{u}\\right)\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{3}}}\\left[\\left(\\left(\\varphi^{s}+1\\right)\\frac{1}{\\varphi^{f}}\\grad\\varphi^{f}\\divg\\Delta\\mathbf{u}-\\varphi^{s}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)\\cdot\\mathbf{w}\\right]\\mathbf{w}dv\n\\end{aligned}\n\\label{eq:Wint-lin-u-u-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\mathbf{L}^{f}\\cdot\\grad\\Delta\\mathbf{u}\\cdot\\mathbf{w}+\\varphi^{s}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{a}^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\rho^{s}\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}\\Delta\\mathbf{u}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\left(-2\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{k}^{-1}+\\grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{k}^{-1}\\right)\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\left(\\mathbf{k}^{-1}\\cdot\\grad\\Delta\\mathbf{u}+\\left(\\mathbf{k}^{-1}\\underbar{\\otimes}\\mathbf{k}^{-1}\\right):\\mathsf{k}:\\grad\\Delta\\mathbf{u}\\right)\\cdot\\mathbf{w}dv\\thinspace,\n\\end{aligned}\n\\]\n\n\\end_inset\n\n for the fluid flux equations \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta\\mathbf{u}\\right]= & \\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(-\\frac{\\varphi^{s}}{\\varphi^{f}}\\left[\\left(-\\frac{1}{\\varphi^{f}}\\frac{\\dot{J}}{J}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\divg\\Delta\\mathbf{u}-\\grad^{T}\\Delta\\mathbf{u}:\\mathbf{L}^{s}\\right]\\mathbf{w}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\varphi^{s}\\left(\\divg\\Delta\\mathbf{u}\\right)\\dot{\\mathbf{v}}^{s}+\\varphi^{f}\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\divg\\Delta\\mathbf{u}\\grad\\mathbf{w}+\\frac{\\gamma}{\\beta\\Delta t}\\grad\\Delta\\mathbf{u}\\right)\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\frac{1}{\\varphi^{f^{2}}}\\left[\\left(\\left(\\varphi^{s}+1\\right)\\frac{1}{\\varphi^{f}}\\grad\\varphi^{f}\\left(\\divg\\Delta\\mathbf{u}\\right)-\\varphi^{s}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)\\cdot\\mathbf{w}\\right]\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\mathbf{L}^{f}\\cdot\\grad\\Delta\\mathbf{u}\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\left(1-\\frac{\\varphi^{s}}{\\varphi^{f}}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)\\rho_{T}^{f}\\mathbf{a}^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\left(\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}-\\grad^{T}\\Delta\\mathbf{u}\\right)\\cdot\\grad pdv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\left(2\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{k}^{-1}-\\grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{k}^{-1}\\right)\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\left(\\mathbf{k}^{-1}\\cdot\\grad\\Delta\\mathbf{u}+\\left(\\mathbf{k}^{-1}\\underbar{\\otimes}\\mathbf{k}^{-1}\\right):\\mathsf{k}:\\grad\\Delta\\mathbf{u}\\right)\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}+\\grad^{T}\\Delta\\mathbf{u}\\right)\\cdot\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\frac{1}{\\varphi^{f}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{D}^{w}+\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)\\cdot\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\frac{1}{\\varphi^{f^{3}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:2\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{w}\\otimes\\grad\\varphi^{f}\\cdot\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{1}{\\varphi^{f^{3}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{w}\\otimes\\left(-\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\varphi^{f}\\right)\\cdot\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{1}{\\varphi^{f^{3}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\mathbf{w}\\otimes\\left(-\\left(\\divg\\Delta\\mathbf{u}\\right)\\grad\\varphi^{f}+\\varphi^{s}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)\\cdot\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{w}\\cdot\\left(\\left(1-\\frac{\\varphi^{s}}{\\varphi^{f}}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}-\\grad\\Delta\\mathbf{u}\\right)dv\n\\end{aligned}\n\\label{eq:Wint-lin-w-u-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & +\\int_{\\Omega^{b}}\\alpha_{f}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{D}^{w}+\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:2\\frac{\\varphi^{s}}{\\varphi^{f^{3}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{w}\\otimes\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\frac{1}{\\varphi^{f^{2}}}\\mathbf{w}\\otimes\\left(-\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\frac{1}{\\varphi^{f^{2}}}\\mathbf{w}\\otimes\\left(-\\left(\\divg\\Delta\\mathbf{u}\\right)\\grad\\varphi^{f}+\\varphi^{s}\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)dv\\thinspace,\n\\end{aligned}\n\\]\n\n\\end_inset\n\n and for the fluid dilatation equations \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta\\mathbf{u}\\right]= & \\int_{\\Omega^{b}}\\alpha_{f}\\grad\\delta J^{f}\\cdot\\left(\\divg\\Delta\\mathbf{u}\\mathbf{I}-\\grad\\Delta\\mathbf{u}\\right)\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\left(\\dot{J}^{f}\\divg\\Delta\\mathbf{u}+\\grad J^{f}\\cdot\\left(\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{I}-\\grad\\Delta\\mathbf{u}\\right)\\cdot\\mathbf{w}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\delta J^{f}\\alpha_{f}\\left(\\divg\\mathbf{v}^{s}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\divg\\Delta\\mathbf{u}+\\grad^{T}\\Delta\\mathbf{u}:\\mathbf{L}^{s}dv\\thinspace,\n\\end{aligned}\n\\label{eq:Wint-lin-J-u-BFSI}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{M}=\\frac{\\gamma}{\\beta\\Delta t}\\mathbf{I}-\\mathbf{L}^{s}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the fourth-order spatial elasticity tensor associated with \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n As done in the CFD formulation (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CFD-Discretization-Linearization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"true\"\n\n\\end_inset\n\n,\n we have introduced the fourth-order tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{\\tau}$\n\\end_inset\n\n representing the tangent of the viscous stress with respect to the rate of deformation,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}^{\\tau}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial\\mathbf{D}^{f}}\\,,\\label{eq:visc-stress-tan-D-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{D}^{f}$\n\\end_inset\n\n is the fluid rate of deformation tensor (the symmetric part of \n\\begin_inset Formula $\\mathbf{L}^{f}$\n\\end_inset\n\n).\n The tensors \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}_{d}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n depend on the choice of constitutive relations for the fluid and solid constituents of \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n,\n respectively.\n In addition,\n \n\\begin_inset Formula $\\mathsf{k}$\n\\end_inset\n\n is the spatial fourth order permeability tensor with respect to right Cauchy-Green tensor \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n.\n The linearized equations include the generalized-\n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n parameters because the virtual work is evaluated at the intermediate time step,\n while the increment itself (\n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n in this case) is at the current time step \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nFollowing the same procedure,\n the linearizations of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{w}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta\\mathbf{w}\\right]= & \\int_{\\Omega^{b}}-\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\delta\\mathbf{v}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(\\grad\\Delta\\mathbf{w}-\\frac{1}{\\varphi^{f}}\\Delta\\mathbf{w}\\otimes\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\left(\\frac{\\alpha_{m}}{\\gamma\\alpha_{f}\\Delta t}-\\frac{\\varphi^{s}}{\\varphi^{f}}\\frac{\\dot{J}}{J}-\\frac{1}{\\varphi^{f^{2}}}\\left(\\grad\\varphi^{f}\\cdot\\mathbf{w}\\right)\\right)\\mathbf{I}+\\mathbf{L}^{f}\\right)\\cdot\\Delta\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{2}}}\\grad\\Delta\\mathbf{w}\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\frac{\\left(\\varphi^{s}\\right)^{2}}{\\left(\\varphi^{f}\\right)^{2}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(\\grad\\Delta\\mathbf{w}-\\frac{1}{\\varphi^{f}}\\Delta\\mathbf{w}\\otimes\\grad\\varphi^{f}\\right)\\cdot\\grad\\left(\\frac{\\varphi^{f}}{\\varphi^{s}}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot-\\alpha_{f}\\mathbf{k}^{-1}\\cdot\\Delta\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\left(\\frac{\\alpha_{m}}{\\gamma\\alpha_{f}\\Delta t}-\\frac{\\varphi^{s}}{\\varphi^{f}}\\frac{\\dot{J}}{J}-\\frac{1}{\\varphi^{f^{2}}}\\left(\\grad\\varphi^{f}\\cdot\\mathbf{w}\\right)\\right)\\mathbf{I}+\\mathbf{L}^{f}\\right)\\cdot\\Delta\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{2}}}\\grad\\Delta\\mathbf{w}\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\mathbf{k}^{-1}\\cdot\\Delta\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\frac{1}{\\varphi^{f^{2}}}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(\\frac{1}{\\varphi^{f}}\\Delta\\mathbf{w}\\otimes\\grad\\varphi^{f}-\\grad\\Delta\\mathbf{w}\\right)\\cdot\\grad\\varphi^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{1}{\\varphi^{f}}\\grad\\delta\\mathbf{w}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(\\grad\\Delta\\mathbf{w}-\\frac{1}{\\varphi^{f}}\\Delta\\mathbf{w}\\otimes\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\grad\\delta J^{f}\\cdot\\Delta\\mathbf{w}+\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\grad J^{f}\\cdot\\Delta\\mathbf{w}dv\\thinspace,\n\\end{aligned}\n\\label{eq:Wint-lin-w-BFSI}\n\\end{equation}\n\n\\end_inset\n\n whereas the linearizations of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta J^{f}$\n\\end_inset\n\n is \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta J^{f}\\right]= & \\int_{\\Omega^{b}}-\\alpha_{f}\\varphi^{s}\\boldsymbol{\\tau}_{J}^{\\prime}:\\grad\\delta\\mathbf{v}\\Delta J^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\varphi^{s}\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}+\\frac{\\rho_{T}^{f}}{J^{f}}\\mathbf{a}^{f}\\right)\\Delta J^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\left(p^{\\prime\\prime}\\grad J^{f}\\Delta J^{f}+p^{\\prime}\\grad\\Delta J^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\left(\\frac{\\rho_{T}^{f}}{J^{f}}\\mathbf{a}^{f}\\Delta J^{f}+\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}_{J}^{\\prime}\\Delta J^{f}\\cdot\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\boldsymbol{\\tau}_{J}^{\\prime}:\\grad\\delta\\mathbf{w}\\Delta J^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\left(\\varphi^{f}\\frac{\\alpha_{m}}{\\gamma\\alpha_{f}\\Delta t}-\\frac{1}{J^{f}}\\left(\\varphi^{f}\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)\\right)\\Delta J^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta J^{f}\\alpha_{f}\\frac{1}{J^{f}}\\grad\\Delta J^{f}\\cdot\\mathbf{w}dv\\thinspace.\n\\end{aligned}\n\\label{eq:Wint-lin-J-BFSI}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $p^{\\prime}$\n\\end_inset\n\n and \n\\begin_inset Formula $p^{\\prime\\prime}$\n\\end_inset\n\n respectively represent the first and second derivatives of \n\\begin_inset Formula $p\\left(J^{f}\\right)$\n\\end_inset\n\n.\n We have also defined \n\\begin_inset Formula $\\boldsymbol{\\tau}_{J}^{\\prime}$\n\\end_inset\n\n as the tangent of the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\tau}_{J}^{\\prime}=\\frac{\\partial\\boldsymbol{\\tau}}{\\partial J^{f}}\\,.\\label{eq:visc-stress-tan-J-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the external work,\n when \n\\begin_inset Formula $\\mathbf{t}^{\\sigma}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n,\n all \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n are prescribed,\n the linearizations simplify to \n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{ext}\\left[\\Delta\\mathbf{u}\\right]=\\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot\\alpha_{f}\\divg\\Delta\\mathbf{u}\\rho_{T}^{f}\\mathbf{b}^{f}dv\\,,\\label{eq:Wext-lin-u-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{ext}\\left[\\Delta\\mathbf{w}\\right]=0\\,,\\label{eq:Wext-lin-w-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{ext}\\left[\\Delta J^{f}\\right]= & \\int_{\\Omega^{b}}\\delta\\mathbf{w}\\cdot-\\alpha_{f}\\frac{\\rho_{T}^{f}}{J^{f}}\\mathbf{b}^{f}\\Delta J^{f}dv\\\\\n & +\\int_{\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{J^{f}}\\mathbf{b}^{f}\\Delta J^{f}dv\\,.\n\\end{aligned}\n\\label{eq:Wext-lin-J-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs discussed in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CFD-Mass-Momentum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we define the fluid dilatation \n\\begin_inset Formula $e^{f}=J^{f}-1$\n\\end_inset\n\n as an alternative essential variable,\n since initial and boundary conditions \n\\begin_inset Formula $e^{f}=0$\n\\end_inset\n\n are more convenient to handle in a numerical scheme than \n\\begin_inset Formula $J^{f}=1$\n\\end_inset\n\n.\n It follows that \n\\begin_inset Formula $\\grad J^{f}=\\grad e^{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\dot{J}^{f}=\\dot{e}^{f}$\n\\end_inset\n\n.\n Therefore the changes to the above equations are minimal,\n simply requiring the substitution \n\\begin_inset Formula $J^{f}=1+e^{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta J^{f}=\\Delta e^{f}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nBFSI Spatial Discretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:BFSI-Spatial-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe degrees of freedom \n\\begin_inset Formula $\\mathbf{u}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{w}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $J^{f}\\left(\\mathbf{x},t\\right)$\n\\end_inset\n\n are spatially interpolated over the domain \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n using the same interpolation functions \n\\begin_inset Formula $N_{a}\\left(\\mathbf{x}\\right)$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $a=1$\n\\end_inset\n\n to \n\\begin_inset Formula $n$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $n$\n\\end_inset\n\n is the number of nodes in an element,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{u}\\left(\\mathbf{x},t\\right) & =\\sum_{a=1}^{n}N_{a}\\left(\\mathbf{x}\\right)\\mathbf{u}_{a}\\,,\\\\\n\\mathbf{w}\\left(\\mathbf{x},t\\right) & =\\sum_{a=1}^{n}N_{a}\\left(\\mathbf{x}\\right)\\mathbf{w}_{a}\\,,\\\\\nJ^{f}\\left(\\mathbf{x},t\\right) & =\\sum_{a=1}^{n}N_{a}\\left(\\mathbf{x}\\right)J_{a}^{f}\\,.\n\\end{aligned}\n\\label{eq:spatial-interpol-BFSI}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{u}_{a}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{w}_{a}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $J_{a}^{f}$\n\\end_inset\n\n are the nodal values of the degrees of freedom that evolve over time.\n These relations may be used to evaluate \n\\begin_inset Formula $\\mathbf{L}^{s}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{L}^{w}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\divg\\mathbf{w}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\dot{\\mathbf{w}}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\grad J^{f}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\dot{J^{f}}$\n\\end_inset\n\n,\n etc.\n Similar interpolations are used for virtual increments \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta J^{f}$\n\\end_inset\n\n,\n as well as real increments \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta J^{f}$\n\\end_inset\n\n.\n In practice,\n interpolations \n\\begin_inset Formula $N_{a}$\n\\end_inset\n\n are performed in the parametric space of each finite element,\n which is a material frame.\n\\end_layout\n\n\\begin_layout Standard\nNow,\n the discretized form of \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n,\n using eq.\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-int-virtual-work\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n may be written as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int} & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\mathbf{f}_{a}^{u}\\\\\n & +\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\mathbf{f}_{a}^{w}\\\\\n & +\\sum_{a}\\delta J_{a}^{f}f_{a}^{J}\\,,\n\\end{aligned}\n\\label{eq:Wint-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{u}= & \\int_{\\Omega^{b}}\\left(-\\varphi^{s}\\boldsymbol{\\tau}+\\boldsymbol{\\sigma}^{e}\\right)\\cdot\\grad N_{a}dv\\\\\n & +\\int_{\\Omega^{b}}N_{a}\\left(\\varphi^{s}\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}-\\rho_{T}^{f}\\mathbf{a}^{f}+\\rho_{T}^{s}\\dot{\\mathbf{v}}^{s}\\right)-\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)dv\\,,\\\\\n\\mathbf{f}_{a}^{w}= & \\int_{\\Omega^{b}}\\boldsymbol{\\tau}\\cdot\\grad N_{a}+N_{a}\\left(\\rho_{T}^{f}\\mathbf{a}^{f}+\\grad p-\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\grad\\varphi^{f}+\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)dv\\,,\\\\\nf_{a}^{J}= & \\int_{\\Omega^{b}}\\grad N_{a}\\cdot\\mathbf{w}+N_{a}\\left(\\frac{\\varphi^{f}}{J^{f}}\\frac{D^{f}J^{f}}{Dt}-\\frac{\\dot{J}^{s}}{J^{s}}\\right)dv\\,.\n\\end{aligned}\n\\label{eq:Wint-discret-parts-BFSI}\n\\end{equation}\n\n\\end_inset\n\n The discretized form of \n\\begin_inset Formula $D\\delta W_{int}\\left[\\Delta\\mathbf{u}\\right]$\n\\end_inset\n\n becomes \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta\\mathbf{u}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{uu}\\cdot\\Delta\\mathbf{u}_{b}\\\\\n & +\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{wu}\\cdot\\Delta\\mathbf{u}_{b}\\\\\n & +\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\mathbf{k}_{ab}^{Ju}\\cdot\\Delta\\mathbf{u}_{b}\\,,\n\\end{aligned}\n\\label{eq:Wint-lin-u-discret-FSI}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu}= & \\int_{\\Omega^{b}}\\alpha_{f}\\left(\\left(\\boldsymbol{\\sigma}^{e}:\\grad N_{b}\\otimes\\grad N_{a}\\right)\\mathbf{I}+\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\varphi^{s}\\boldsymbol{\\tau}\\cdot\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad N_{a}\\otimes\\grad N_{b}+\\grad N_{b}\\otimes\\grad N_{a}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\varphi^{s}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}\\cdot\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{D}^{w}+\\mathbf{M}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\varphi^{f}\\otimes\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(\\grad N_{b}\\otimes\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(-\\grad\\varphi^{f}\\otimes\\grad N_{b}+\\varphi^{s}\\grad\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\otimes\\grad N_{b}+\\grad N_{b}\\otimes\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\frac{1}{\\varphi^{s}}\\grad\\grad N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f}}\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}\\cdot\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{D}^{w}+\\mathbf{M}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\varphi^{f}\\otimes\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(\\grad N_{b}\\otimes\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(-\\grad\\varphi^{f}\\otimes\\grad N_{b}+\\varphi^{s}\\grad\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f}}\\rho_{T}^{f}N_{a}\\left(\\varphi^{s}\\dot{\\mathbf{v}}^{s}\\otimes\\grad N_{b}+\\varphi^{f}\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}N_{b}\\mathbf{I}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\rho_{T}^{f}N_{a}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{2}}}\\left[\\left(\\left(-\\frac{1}{\\varphi^{f}}\\right)\\frac{\\dot{J}}{J}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\mathbf{w}\\otimes\\grad N_{b}-\\mathbf{w}\\otimes\\left(\\mathbf{L}^{s^{T}}\\cdot\\grad N_{b}\\right)\\right]dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{L}^{w}\\cdot\\mathbf{w}\\otimes\\grad N_{b}+\\frac{\\gamma}{\\beta\\Delta t}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\rho_{T}^{f}\\frac{\\varphi^{s}}{\\varphi^{f^{4}}}\\left(\\varphi^{s}+1\\right)\\left(\\grad\\varphi^{f}\\cdot\\mathbf{w}\\right)\\mathbf{w}\\otimes\\grad N_{b}dv\n\\end{aligned}\n\\label{eq:Wint-lin-uu-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\rho_{T}^{f}\\frac{\\varphi^{s^{2}}}{\\varphi^{f^{3}}}\\mathbf{w}\\otimes\\grad\\grad^{T}N_{b}\\cdot\\mathbf{w}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f}}\\rho_{T}^{f}N_{a}\\left(\\mathbf{L}^{f}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)+\\varphi^{s}\\mathbf{a}^{f}\\otimes\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}\\rho^{s}N_{b}\\mathbf{I}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(-2\\left(\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)\\otimes\\grad N_{b}+\\grad N_{b}\\otimes\\left(\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{k}^{-1}+\\left(\\mathbf{k}^{-1}\\underbar{\\otimes}\\mathbf{k}^{-1}\\right):\\mathsf{k}:\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}\\right)dv\\,,\n\\end{aligned}\n\\]\n\n\\end_inset\n\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{wu}= & \\int_{\\Omega^{b}}\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}N_{a}\\left(\\varphi^{s}\\dot{\\mathbf{v}}^{s}\\otimes\\grad N_{b}+\\varphi^{f}\\frac{\\alpha_{m}}{\\alpha_{f}\\beta\\Delta t^{2}}N_{b}\\mathbf{I}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}N_{a}\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(-\\frac{1}{\\varphi^{f}}\\frac{\\dot{J}}{J}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\mathbf{w}\\otimes\\grad N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}N_{a}\\frac{\\varphi^{s}}{\\varphi^{f}}\\mathbf{w}\\otimes\\left(\\mathbf{L}^{s^{T}}\\cdot\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(\\mathbf{L}^{w}\\cdot\\mathbf{w}\\right)\\otimes\\grad N_{b}+\\frac{\\gamma}{\\beta\\Delta t}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{4}}}\\left(\\varphi^{s}+1\\right)\\left(\\grad\\varphi^{f}\\cdot\\mathbf{w}\\right)\\mathbf{w}\\otimes\\grad N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{3}}}\\varphi^{s}\\mathbf{w}\\otimes\\left(\\grad\\grad^{T}N_{b}\\cdot\\mathbf{w}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}N_{a}\\mathbf{L}^{f}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(1-\\frac{\\varphi^{s}}{\\varphi^{f}}\\right)\\rho_{T}^{f}\\mathbf{a}^{f}\\otimes\\grad N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(\\grad p\\otimes\\grad N_{b}-\\grad N_{b}\\otimes\\grad p\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(2\\left(\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)\\otimes\\grad N_{b}-\\grad N_{b}\\otimes\\left(\\mathbf{k}^{-1}\\cdot\\mathbf{w}\\right)\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\left(\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{k}^{-1}+\\left(\\mathbf{k}^{-1}\\underbar{\\otimes}\\mathbf{k}^{-1}\\right):\\mathsf{k}:\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\varphi^{f}\\otimes\\grad N_{b}+\\grad N_{b}\\otimes\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}\\cdot\\varphi^{s}\\grad\\grad N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{1}{\\varphi^{f}}\\grad\\varphi^{f}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}\\cdot\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{D}^{w}+\\mathbf{M}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{1}{\\varphi^{f^{3}}}\\grad\\varphi^{f}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\varphi^{f}\\otimes\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\frac{1}{\\varphi^{f^{3}}}\\grad\\varphi^{f}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(\\grad N_{b}\\otimes\\grad\\varphi^{f}\\right)dv\n\\end{aligned}\n\\label{eq:Wint-lin-wu-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned} & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{1}{\\varphi^{f^{3}}}\\grad\\varphi^{f}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(-\\grad\\varphi^{f}\\otimes\\grad N_{b}+\\varphi^{s}\\grad\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\boldsymbol{\\tau}\\cdot\\left(\\left(1-\\frac{\\varphi^{s}}{\\varphi^{f}}\\right)\\grad N_{a}\\otimes\\grad N_{b}-\\grad N_{b}\\otimes\\grad N_{a}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}\\cdot\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{D}^{w}+\\mathbf{M}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{1}{\\varphi^{f^{2}}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad\\varphi^{f}\\otimes\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{1}{\\varphi^{f^{2}}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(\\grad N_{b}\\otimes\\grad\\varphi^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\frac{1}{\\varphi^{f^{2}}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(-\\grad\\varphi^{f}\\otimes\\grad N_{b}+\\varphi^{s}\\grad\\grad N_{b}\\right)dv\\,,\n\\end{aligned}\n\\]\n\n\\end_inset\n\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ab}^{Ju}= & \\int_{\\Omega^{b}}\\alpha_{f}\\left(\\grad N_{b}\\otimes\\mathbf{w}-\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}\\right)\\cdot\\grad N_{a}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{1}{J^{f}}\\left(\\dot{J}^{f}\\grad N_{b}+\\left(\\grad N_{b}\\otimes\\mathbf{w}-\\grad N_{b}\\cdot\\mathbf{w}\\mathbf{I}\\right)\\cdot\\grad J^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}\\left(\\frac{\\dot{J}}{J}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\grad N_{b}+\\mathbf{L}^{s^{T}}\\cdot\\grad N_{b}dv\\,,\n\\end{aligned}\n\\label{eq:Wint-lin-Ju-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n whereas that of \n\\begin_inset Formula $D\\delta W_{int}\\left[\\Delta\\mathbf{w}\\right]$\n\\end_inset\n\n becomes \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta\\mathbf{w}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{uw}\\cdot\\Delta\\mathbf{w}_{b}\\\\\n & +\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{ww}\\cdot\\Delta\\mathbf{w}_{b}\\\\\n & +\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\mathbf{k}_{ab}^{Jw}\\cdot\\Delta\\mathbf{w}_{b}\\,,\n\\end{aligned}\n\\label{eq:Wint-lin-w-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uw}= & \\int_{\\Omega^{b}}\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\left(\\frac{1}{\\varphi^{f}}N_{b}\\grad\\varphi^{f}-\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}N_{a}\\left(\\left(\\frac{\\alpha_{m}}{\\gamma\\alpha_{f}\\Delta t}-\\frac{\\varphi^{s}}{\\varphi^{f}}\\frac{\\dot{J}}{J}-\\frac{1}{\\varphi^{f^{2}}}\\left(\\grad\\varphi^{f}\\cdot\\mathbf{w}\\right)\\right)\\mathbf{I}+\\mathbf{L}^{f}\\right)N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{2}}}N_{a}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\left(\\varphi^{s}\\right)^{2}}{\\left(\\varphi^{f}\\right)^{2}}\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}\\cdot\\boldsymbol{\\mathcal{C}}_{d}\\cdot\\left(-\\frac{1}{\\varphi^{f}}N_{b}\\grad\\varphi^{f}+\\grad N_{b}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{a}N_{b}\\mathbf{k}^{-1}dv\\,,\\\\\n\\mathbf{K}_{ab}^{ww}= & \\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\rho_{T}^{f}}{\\varphi^{f}}\\left(\\left(\\frac{\\alpha_{m}}{\\gamma\\alpha_{f}\\Delta t}-\\frac{\\varphi^{s}}{\\varphi^{f}}\\frac{\\dot{J}}{J}-\\frac{1}{\\varphi^{f^{2}}}\\left(\\grad\\varphi^{f}\\cdot\\mathbf{w}\\right)\\right)\\mathbf{I}+\\mathbf{L}^{f}\\right)N_{b}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\rho_{T}^{f}}{\\varphi^{f^{2}}}\\left(\\grad N_{b}\\cdot\\mathbf{w}\\right)\\mathbf{I}dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(\\frac{1}{\\varphi^{f^{2}}}\\grad\\varphi^{f}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\left(\\frac{1}{\\varphi^{f}}N_{b}\\grad\\varphi^{f}-\\grad N_{b}\\right)+N_{b}\\mathbf{k}^{-1}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}\\frac{1}{\\varphi^{f}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\left(-\\frac{1}{\\varphi^{f}}N_{b}\\grad\\varphi^{f}+\\grad N_{b}\\right)dv\\,,\\\\\n\\mathbf{k}_{ab}^{Jw}= & \\int_{\\Omega^{b}}\\alpha_{f}N_{b}\\left(\\grad N_{a}+N_{a}\\frac{1}{J^{f}}\\grad J^{f}\\right)dv\\,,\n\\end{aligned}\n\\label{eq:Wint-lin-w-discret-parts-BFSI}\n\\end{equation}\n\n\\end_inset\n\n and finally,\n for \n\\begin_inset Formula $D\\delta W_{int}\\left[\\Delta J^{f}\\right]$\n\\end_inset\n\n the equations become \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{int}\\left[\\Delta J^{f}\\right]= & \\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{k}_{ab}^{uJ}\\Delta J_{b}^{f}\\\\\n & +\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\mathbf{k}_{ab}^{wJ}\\Delta J_{b}^{f}\\\\\n & +\\sum_{a}\\delta J_{a}^{f}\\sum_{b}k_{ab}^{JJ}\\Delta J_{b}^{f}\\,,\n\\end{aligned}\n\\label{eq:Wint-lin-J-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ab}^{uJ}= & \\int_{\\Omega^{b}}\\alpha_{f}\\varphi^{s}N_{a}N_{b}\\left(\\frac{\\varphi^{s}}{\\varphi^{f}}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad\\frac{\\varphi^{f}}{\\varphi^{s}}+\\frac{\\rho_{T}^{f}}{J^{f}}\\mathbf{a}^{f}\\right)dv\\\\\n & +\\int_{\\Omega^{b}}-\\alpha_{f}N_{b}\\varphi^{s}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad N_{a}dv\\,,\\\\\n\\mathbf{k}_{ab}^{wJ}= & \\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\left(p^{\\prime}\\grad N_{b}+N_{b}\\left(p^{\\prime\\prime}\\grad J^{f}-\\frac{\\rho_{T}^{f}}{J^{f}}\\mathbf{a}^{f}-\\frac{1}{\\varphi^{f}}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad\\varphi^{f}\\right)\\right)dv\\Delta J_{b}^{f}\\\\\n & +\\int_{\\Omega^{b}}\\alpha_{f}N_{b}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad N_{a}dv\\,,\\\\\nk_{ab}^{JJ}= & \\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\frac{1}{J^{f}}\\left(\\left(\\varphi^{f}\\frac{\\alpha_{m}}{\\gamma\\alpha_{f}\\Delta t}-\\frac{1}{J^{f}}\\left(\\varphi^{f}\\dot{J}^{f}+\\grad J^{f}\\cdot\\mathbf{w}\\right)\\right)N_{b}+\\grad N_{b}\\cdot\\mathbf{w}\\right)dv\\,,\n\\end{aligned}\n\\label{eq:Wint-lin-J-discret-parts-BFSI}\n\\end{equation}\n\n\\end_inset\n\n for external virtual work in eq.\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-ext-virtual-work\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the discretized equations are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{ext} & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\mathbf{f}_{a}^{u}\\\\\n & +\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\mathbf{f}_{a}^{w}\\\\\n & +\\sum_{a}\\delta J_{a}^{f}f_{a}^{J}\\,,\n\\end{aligned}\n\\label{eq:Wext-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a,ext}^{u} & =\\int_{\\partial\\Omega^{b}}N_{a}\\mathbf{t}^{\\sigma}da+\\int_{\\Omega^{b}}N_{a}\\varphi^{s}\\left(-\\rho_{T}^{f}\\mathbf{b}^{f}+\\rho_{T}^{s}\\mathbf{b}^{s}\\right)dv\\,,\\\\\n\\mathbf{f}_{a,ext}^{w} & =\\int_{\\partial\\Omega^{b}}N_{a}\\mathbf{t}^{\\tau}da+\\int_{\\Omega^{b}}\\rho_{T}^{f}N_{a}\\mathbf{b}^{f}dv\\,,\\\\\nf_{a,ext}^{J} & =\\int_{\\partial\\Omega^{b}}N_{a}w_{n}da\\,,\n\\end{aligned}\n\\label{eq:Wext-discret-parts-BFSI}\n\\end{equation}\n\n\\end_inset\n\nand the discretized forms of the linearized external virtual work are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W_{ext}\\left[\\Delta\\mathbf{u}\\right] & =\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{wu}\\cdot\\Delta\\mathbf{u}_{b}\\,,\\\\\nD\\delta W_{ext}\\left[\\Delta J^{f}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{k}_{ab}^{uJ}\\Delta J_{b}^{f}+\\sum_{a}\\delta\\mathbf{w}_{a}\\cdot\\sum_{b}\\mathbf{k}_{ab}^{wJ}\\Delta J_{b}^{f}\\,,\n\\end{aligned}\n\\label{eq:Wext-lin-discret-BFSI}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab,ext}^{wu}= & \\int_{\\Omega^{b}}\\alpha_{f}N_{a}\\rho_{T}^{f}\\mathbf{b}^{f}\\otimes\\grad N_{b}dv\\,,\\\\\n\\mathbf{k}_{ab,ext}^{uJ}= & -\\int_{\\Omega^{b}}\\alpha_{f}\\varphi^{s}\\frac{\\rho_{T}^{f}}{J^{f}}N_{a}N_{b}\\mathbf{b}^{f}dv\\,,\\\\\n\\mathbf{k}_{ab,ext}^{wJ}= & -\\int_{\\Omega^{b}}\\alpha_{f}\\frac{\\rho_{T}^{f}}{J^{f}}N_{a}N_{b}\\mathbf{b}^{f}dv\\,.\n\\end{aligned}\n\\label{eq:Wext-lin-discret-parts-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nCombining these results,\n from the linearized virtual work equation of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:linearized-work-split\"\nnolink \"false\"\n\n\\end_inset\n\n we can represent the system of equations in a compact matrix form as\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{uw} & \\mathbf{k}_{ab}^{uJ}-\\mathbf{k}_{ab,ext}^{uJ}\\\\\n\\mathbf{K}_{ab}^{wu}-\\mathbf{K}_{ab,ext}^{wu} & \\mathbf{K}_{ab}^{ww} & \\mathbf{k}_{ab}^{wJ}-\\mathbf{k}_{ab,ext}^{wJ}\\\\\n\\mathbf{k}_{ab}^{Ju} & \\mathbf{k}_{ab}^{Jw} & k_{ab}^{JJ}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}\\\\\n\\Delta\\mathbf{w}\\\\\n\\Delta J^{f}\n\\end{array}\\right]=\\left[\\begin{array}{c}\n\\mathbf{f}_{a,ext}^{u}-\\mathbf{f}_{a}^{u}\\\\\n\\mathbf{f}_{a,ext}^{w}-\\mathbf{f}_{a}^{w}\\\\\nf_{a,ext}^{J}-f_{a}^{J}\n\\end{array}\\right]\\,.\\label{eq:Virtual-work-matrix-BFSI}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nBFSI Traction Interface \n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:BFSI-Traction-Interface\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work \n\\begin_inset Formula $\\delta F$\n\\end_inset\n\n on a biphasic-FSI interface was given in eq.\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bfsi-traction-final\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The linearizations of \n\\begin_inset Formula $\\delta F$\n\\end_inset\n\n are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta F\\left[\\Delta\\mathbf{u}\\right]= & \\int_{\\partial\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{T}_{d}\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\delta\\mathbf{v}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}:\\alpha_{f}\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{D}^{w}+\\mathbf{M}\\cdot\\grad\\Delta\\mathbf{u}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\delta\\mathbf{v}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}:\\alpha_{f}\\left(2\\frac{\\varphi^{s}}{\\varphi^{f^{3}}}\\left(\\divg\\Delta\\mathbf{u}\\right)\\mathbf{w}\\otimes\\grad\\varphi^{f}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\delta\\mathbf{v}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}:\\alpha_{f}\\left(-\\frac{1}{\\varphi^{f^{2}}}\\mathbf{w}\\otimes\\left(-\\grad^{T}\\Delta\\mathbf{u}\\cdot\\grad\\varphi^{f}\\right)\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}\\delta\\mathbf{v}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}:\\alpha_{f}\\left(\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{w}\\otimes\\left(-\\frac{1}{J}\\left(\\divg\\Delta\\mathbf{u}\\right)\\grad J+\\grad\\left(\\divg\\Delta\\mathbf{u}\\right)\\right)\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\delta\\mathbf{v}\\cdot\\alpha_{f}\\boldsymbol{\\tau}\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\times\\mathbf{g}_{2}+\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\,,\\\\\nD\\delta F\\left[\\Delta\\mathbf{w}\\right] & =\\int_{\\partial\\Omega^{b}}-\\delta\\mathbf{v}\\cdot\\alpha_{f}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\left(\\frac{1}{\\varphi^{f}}\\grad\\Delta\\mathbf{w}-\\frac{1}{\\varphi^{f^{2}}}\\Delta\\mathbf{w}\\otimes\\grad\\varphi^{f}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\,,\\\\\nD\\delta F\\left[\\Delta J^{f}\\right] & =\\int_{\\partial\\Omega^{b}}-\\delta\\mathbf{v}\\cdot\\alpha_{f}\\left(-p^{\\prime}\\mathbf{I}+\\boldsymbol{\\tau}_{J}^{\\prime}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\Delta J^{f}\\thinspace d\\eta^{1}d\\eta^{2}\\,.\n\\end{aligned}\n\\label{eq:BFSI-int-lin}\n\\end{equation}\n\n\\end_inset\n\n The discretized forms of this virtual work is \n\\begin_inset Formula \n\\begin{equation}\n\\delta F=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\mathbf{f}_{a}\\,,\\label{eq:BSI-int-discret}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}_{a}=\\int_{\\partial\\Omega^{b}}-N_{a}\\left(-p\\mathbf{I}+\\boldsymbol{\\tau}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\,.\\label{eq:BFSI-int-discret-parts}\n\\end{equation}\n\n\\end_inset\n\nThe discretized forms of the linearizations are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta F\\left[\\Delta\\mathbf{u}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{uu}\\cdot\\Delta\\mathbf{u}_{b}\\,,\\\\\nD\\delta F\\left[\\Delta\\mathbf{w}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{K}_{ab}^{uw}\\cdot\\Delta\\mathbf{w}_{b}\\,,\\\\\nD\\delta F\\left[\\Delta J^{f}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\mathbf{k}_{ab}^{uJ}\\Delta J_{b}^{f}\\,,\n\\end{aligned}\n\\label{eq:BFSI-int-lin-discret}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu}= & \\int_{\\partial\\Omega^{b}}\\alpha_{f}N_{a}\\frac{\\varphi^{s}}{\\varphi^{f}}\\left(\\boldsymbol{\\tau}\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\right)\\otimes\\grad N_{b}\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\alpha_{f}N_{a}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}_{d}\\cdot\\grad N_{b}\\cdot\\left(-\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\mathbf{D}^{w}+\\mathbf{M}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\alpha_{f}N_{a}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(2\\frac{\\varphi^{s}}{\\varphi^{f^{3}}}\\grad\\varphi^{f}\\otimes\\grad N_{b}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\alpha_{f}N_{a}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\left(\\frac{1}{\\varphi^{f^{2}}}\\grad N_{b}\\otimes\\grad\\varphi^{f}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}\\alpha_{f}N_{a}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\mathbf{w}\\cdot\\frac{\\varphi^{s}}{\\varphi^{f^{2}}}\\left(-\\frac{1}{J}\\grad J\\otimes\\grad N_{b}+\\grad\\grad N_{b}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\partial\\Omega^{b}}-\\alpha_{f}N_{a}\\left(-p\\mathbf{I}+\\boldsymbol{\\tau}\\right)\\cdot\\boldsymbol{\\mathcal{A}}\\left\\{ -\\mathbf{g}_{2}\\frac{\\partial N_{b}}{\\partial\\eta^{1}}+\\mathbf{g}_{1}\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\right\\} \\thinspace d\\eta^{1}d\\eta^{2}\\,,\\\\\n\\mathbf{K}_{ab}^{uw}= & \\int_{\\partial\\Omega^{b}}-\\alpha_{f}N_{a}\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\cdot\\boldsymbol{\\mathcal{C}}_{d}\\cdot\\frac{1}{\\varphi^{f}}\\left(\\grad N_{b}-\\frac{1}{\\varphi^{f}}N_{b}\\grad\\varphi^{f}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\,,\\\\\n\\mathbf{k}_{ab}^{uJ}= & \\int_{\\partial\\Omega^{b}}-\\alpha_{f}N_{a}N_{b}\\left(-p^{\\prime}\\mathbf{I}+\\boldsymbol{\\tau}_{J}^{\\prime}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\thinspace d\\eta^{1}d\\eta^{2}\\,.\n\\end{aligned}\n\\label{eq:BFSI-int-lin-discret-parts}\n\\end{equation}\n\n\\end_inset\n\n Note that \n\\begin_inset Formula $\\boldsymbol{\\mathcal{A}}\\left\\{ \\cdot\\right\\} $\n\\end_inset\n\n represents the skew-symmetric tensor form of the bracketed vector.\n\\end_layout\n\n\\begin_layout Section\nWeak Formulation for Fluid-Solutes Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Weak-Fluid-Solutes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nVirtual Work and Weak form for Fluid-Solutes\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work statement is used to enforce the governing equations needed to solve for the nodal \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n and all \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n,\n namely the mixture mass balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mix-Mass-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the fluid momentum balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Mtm-Bal-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and the solute mass balances \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Bal-Solute-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n in addition to the current density constraint \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Current-Density-Constraint\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The virtual work statement for a Galerkin finite element formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W= & \\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(-\\grad\\tilde{p}+\\divg\\boldsymbol{\\tau}-\\sum_{\\iota}R\\theta\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}+\\rho^{f}\\left(\\mathbf{b}^{f}-\\mathbf{a}^{f}\\right)+M^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}\\right)dv\\\\\n & +\\int_{\\Omega^{f}}\\delta J^{f}\\left(-\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\divg\\mathbf{v}^{f}\\right)dv\\\\\n & +\\sum_{\\iota}\\int_{\\Omega^{f}}\\delta\\tilde{c}^{\\iota}\\left(\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}+\\divg\\mathbf{j}^{\\iota}-\\hat{c}^{\\iota}+\\sum_{\\beta}z^{\\beta}\\divg\\mathbf{j}^{\\iota}\\right)dv\\,.\n\\end{aligned}\n\\label{eq:CFDSol-Virtual-Work}\n\\end{equation}\n\n\\end_inset\n\nThese integrals are evaluated in the current (time-invariant) configuration of \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{f}$\n\\end_inset\n\n is the virtual fluid velocity,\n \n\\begin_inset Formula $\\delta J^{f}$\n\\end_inset\n\n is the virtual fluid energy density,\n and \n\\begin_inset Formula $\\delta\\tilde{c}^{\\iota}$\n\\end_inset\n\n is the virtual molar energy of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n.\n Integrating by parts and using the divergence theorem,\n the weak form of this statement may be written as \n\\begin_inset Formula $0=\\delta W=\\delta W_{ext}-\\delta W_{int}$\n\\end_inset\n\n where the internal virtual work is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int}= & \\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(\\grad\\tilde{p}+\\rho^{f}\\mathbf{a}^{f}+\\sum_{\\iota}R\\theta\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}\\right)+\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{v}^{f}dv\\\\\n & +\\int_{\\Omega^{f}}\\delta J^{f}\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\grad\\delta J^{f}\\cdot\\mathbf{v}^{f}dv\\\\\n & +\\sum_{\\iota}\\int_{\\Omega^{f}}\\left(\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(\\mathbf{j}_{d}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}_{d}^{\\gamma}\\right)-\\delta\\tilde{c}^{\\iota}\\left(\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}-\\hat{c}^{\\iota}\\right)\\right)dv\\,.\n\\end{aligned}\n\\label{eq:Int-Virtual-Work-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nThe external virtual work is \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{ext}= & \\int_{\\Gamma^{f}}\\delta\\mathbf{v}^{f}\\cdot\\mathbf{t}^{\\tau}da+\\int_{\\Gamma^{f}}\\delta J^{f}v_{n}^{f}da+\\int_{\\Gamma^{f}}\\delta\\tilde{c}^{\\iota}\\tilde{j}_{n}^{\\iota}da\\\\\n & +\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(\\rho^{f}\\mathbf{b}^{f}+\\sum_{\\iota}M^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}\\right)dv\\\\\n & -\\sum_{\\iota}\\int_{\\Omega^{f}}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\tilde{\\mathbf{j}}_{b}^{\\iota}dv\\,,\n\\end{aligned}\n\\label{eq:Ext-Virt-Work-CFDSol}\n\\end{equation}\n\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{j}_{n}^{\\iota}=\\left(\\mathbf{j}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}^{\\gamma}\\right)\\cdot\\mathbf{n}\\,.\\label{eq:Normal-Sol-Flux-CFDSol}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the fluid viscous traction,\n \n\\begin_inset Formula $v_{n}^{f}=\\mathbf{v}^{f}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the normal component of the fluid velocity on the boundary \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}$\n\\end_inset\n\n is the normal effective diffusive flux of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n on the boundary \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the outward normal to \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n The integrands of the surface integrals represent the natural boundary conditions for this formulation.\n If boundary conditions are not set explicitly on \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n,\n the natural boundary conditions are \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $v_{n}^{f}=0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}=0$\n\\end_inset\n\n.\n These natural boundary conditions are consistent with the jump conditions presented above in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Fluid-Mtm-Jump-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Jump-Final-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Jump-Sol-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nEssential boundary conditions are prescribed on the fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n and effective solute concentration \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n,\n which are also consistent with the above jump conditions in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:J-Jump-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Mass-Jump-Final-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Conc-Jump-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For example,\n an essential no-slip boundary condition may be prescribed on \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n by setting \n\\begin_inset Formula $\\mathbf{v}^{f}=\\mathbf{0}$\n\\end_inset\n\n.\n A symmetry plane may be prescribed from the natural boundary conditions \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $v_{n}^{f}=0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}=0$\n\\end_inset\n\n.\n In this formulation,\n the mixture traction is defined as \n\\begin_inset Formula $\\mathbf{t}=-p\\mathbf{n}+\\mathbf{t}^{\\tau}$\n\\end_inset\n\n.\n Because of the way we chose to split the internal and external virtual work in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Int-Virtual-Work-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Ext-Virt-Work-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n is not a natural boundary condition in this formulation.\n Thus,\n when \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{0}$\n\\end_inset\n\n is desired,\n it has to be enforced by satisfying the natural boundary condition \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n,\n and selecting suitable values for the essential boundary conditions \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n using \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Total-fluid-p-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Eff-Pressure-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to produce \n\\begin_inset Formula $p=0$\n\\end_inset\n\n,\n as explained next.\n\\end_layout\n\n\\begin_layout Standard\nThe actual fluid gauge pressure in this fluid-solutes mixture is \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n as given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Total-fluid-p-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n To prescribe a desired value \n\\begin_inset Formula $p=p^{*}$\n\\end_inset\n\n on a boundary,\n we implemented a \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid pressure\n\\begin_inset Quotes erd\n\\end_inset\n\n boundary condition that prescribed the correct value of \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n according to the current values of all \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n,\n using the user-selected constitutive model for \n\\begin_inset Formula $\\tilde{p}\\left(J^{f}\\right)$\n\\end_inset\n\n.\n For example,\n using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Eff-Pressure-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n this \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid pressure\n\\begin_inset Quotes erd\n\\end_inset\n\n boundary condition actually prescribes \n\\begin_inset Formula $J^{f}=1/\\left(1+\\left(p^{*}-R\\theta\\Phi\\sum_{\\iota}\\tilde{\\kappa}^{\\iota}\\tilde{c}^{\\iota}\\right)/K\\right)$\n\\end_inset\n\n as an essential boundary condition at nodes of \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n The values of \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\kappa}^{\\iota}$\n\\end_inset\n\n are obtained from the finite element of the domain \n\\begin_inset Formula $\\Omega^{f}$\n\\end_inset\n\n right underneath the face \n\\begin_inset Formula $\\Gamma^{f}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization for Fluid-Solutes\n\\end_layout\n\n\\begin_layout Standard\nWe can rewrite the internal work of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:CFDSol-Virtual-Work\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta W_{int} & =\\delta W_{int}^{v}+\\delta W_{int}^{J}+\\delta W_{int}^{c}\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta W_{int}^{v} & =\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(\\grad\\tilde{p}+\\rho^{f}\\mathbf{a}^{f}+\\sum_{\\iota}R\\theta\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}\\right)+\\boldsymbol{\\tau}:\\grad\\delta\\mathbf{v}^{f}dv\\\\\n\\delta W_{int}^{J} & =\\int_{\\Omega^{f}}\\delta J^{f}\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\grad\\delta J^{f}\\cdot\\mathbf{v}^{f}dv\\\\\n\\delta W_{int}^{c} & =\\sum_{\\iota}\\int_{\\Omega^{f}}\\left(\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(\\mathbf{j}_{d}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}_{d}^{\\gamma}\\right)-\\delta\\tilde{c}^{\\iota}\\left(\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}-\\hat{c}^{\\iota}\\right)\\right)dv\\,.\n\\end{aligned}\n\\]\n\n\\end_inset\n\nRecall that \n\\begin_inset Formula $\\hat{c}^{\\iota}=\\nu^{\\iota}\\hat{\\zeta}$\n\\end_inset\n\n.\n Also note that\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{a}^{f} & =\\frac{\\partial\\mathbf{v}^{f}}{\\partial t}+\\grad\\mathbf{v}^{f}\\cdot\\mathbf{v}^{f}\\\\\n\\frac{D^{f}J^{f}}{Dt} & =\\frac{\\partial J^{f}}{\\partial t}+\\grad J^{f}\\cdot\\mathbf{v}^{f}\\\\\n\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt} & =\\tilde{c}^{\\iota}\\left(\\tilde{\\kappa}^{\\iota}+J^{f}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\right)\\frac{D^{f}J^{f}}{Dt}+J^{f}\\left(\\tilde{\\kappa}^{\\iota}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\frac{D^{f}\\tilde{c}^{\\gamma}}{Dt}\\right)\\\\\n\\frac{D^{f}c^{\\iota}}{Dt} & =\\frac{\\partial c^{\\iota}}{\\partial t}+\\grad c^{\\iota}\\cdot\\mathbf{v}^{f}\\\\\n & =\\tilde{\\kappa}^{\\iota}\\frac{\\partial\\tilde{c}^{\\iota}}{\\partial t}+\\tilde{c}^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial t}+\\left(\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}+\\tilde{c}^{\\iota}\\grad\\tilde{\\kappa}^{\\iota}\\right)\\cdot\\mathbf{v}^{f}\\\\\n\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial t} & =\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\frac{\\partial J^{f}}{\\partial t}+\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\frac{\\partial\\tilde{c}^{\\gamma}}{\\partial t}\\\\\n\\grad\\tilde{\\kappa}^{\\iota} & =\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\grad J+\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\grad\\tilde{c}^{\\gamma}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nUsing the temporal discretization approach outlined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CFD-Discretization-Linearization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the linearizations of each of these terms along increments in the degrees of freedom are obtained using\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\mathbf{a}^{f}\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\frac{\\alpha_{m}}{\\gamma}\\frac{\\Delta\\mathbf{v}^{f}}{\\Delta t}+\\alpha_{f}\\left(\\grad\\Delta\\mathbf{v}^{f}\\cdot\\mathbf{v}^{f}+\\mathbf{L}^{f}\\cdot\\Delta\\mathbf{v}^{f}\\right)\\\\\nD\\left(\\frac{D^{f}J^{f}}{Dt}\\right)\\left[\\Delta\\mathbf{v}_{f}\\right] & =\\alpha_{f}\\grad J^{f}\\cdot\\Delta\\mathbf{v}_{f}\\\\\nD\\boldsymbol{\\tau}\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\alpha_{f}\\boldsymbol{\\mathcal{C}}^{\\tau}:\\frac{1}{2}\\left(\\grad\\Delta\\mathbf{v}^{f}+\\grad^{T}\\Delta\\mathbf{v}^{f}\\right)\\\\\nD\\left(\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\alpha_{f}\\grad\\left(J^{f}c^{\\iota}\\right)\\cdot\\Delta\\mathbf{v}^{f}\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\grad\\tilde{p}\\right)\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\left(\\tilde{p}_{J}^{\\prime}\\grad\\Delta J^{f}+\\Delta J^{f}\\tilde{p}_{J}^{\\prime\\prime}\\grad J^{f}\\right)\\\\\nD\\rho^{f}\\left[\\Delta J^{f}\\right] & =-\\alpha_{f}\\frac{\\rho^{f}}{J^{f}}\\Delta J^{f}\\\\\nD\\boldsymbol{\\tau}\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\boldsymbol{\\tau}_{J}^{\\prime}\\Delta J^{f}\\\\\nD\\left(\\frac{D^{f}J^{f}}{Dt}\\right)\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{\\Delta J^{f}}{\\Delta t}+\\grad\\Delta J^{f}\\cdot\\mathbf{v}^{f}\\right)\\\\\nD\\tilde{\\kappa}^{\\iota}\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\Delta J^{f}\\\\\nD\\left(\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}\\right)\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\tilde{c}^{\\iota}\\left(\\tilde{\\kappa}^{\\iota}+J^{f}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\right)\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{\\Delta J^{f}}{\\Delta t}+\\grad\\Delta J^{f}\\cdot\\mathbf{v}^{f}\\right)\\\\\n & +\\alpha_{f}\\tilde{c}^{\\iota}\\frac{D^{f}J^{f}}{Dt}\\left(2\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}+J^{f}\\frac{\\partial^{2}\\tilde{\\kappa}^{\\iota}}{\\partial\\left(J^{f}\\right)^{2}}\\right)\\Delta J^{f}\\\\\n & +\\alpha_{f}\\left(\\tilde{\\kappa}^{\\iota}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\frac{D^{f}\\tilde{c}^{\\gamma}}{Dt}\\right)\\Delta J^{f}\\\\\n & +\\alpha_{f}J^{f}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\sum_{\\gamma}\\left(\\frac{\\partial^{2}\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}\\partial\\tilde{c}^{\\gamma}}\\right)\\frac{D^{f}\\tilde{c}^{\\gamma}}{Dt}\\right)\\Delta J^{f}\\\\\nD\\hat{c}^{\\iota}\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\nu^{\\iota}\\frac{\\partial\\hat{\\zeta}}{\\partial J^{f}}\\Delta J^{f}\\\\\nD\\mathbf{j}_{d}^{\\iota}\\left[\\Delta J^{f}\\right] & =-\\alpha_{f}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\Delta J^{f}d_{0}^{\\iota}\\grad\\tilde{c}^{\\iota}\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\tilde{\\kappa}^{\\iota}\\tilde{c}^{\\iota}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\alpha_{f}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}+\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\iota}\\right)\\Delta\\tilde{c}^{\\beta}\\\\\nD\\left(\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\alpha_{f}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}\\grad\\Delta\\tilde{c}^{\\beta}+\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\Delta\\tilde{c}^{\\beta}\\grad\\tilde{c}^{\\iota}\\right)\\\\\nD\\left(\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\alpha_{f}J^{f}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}+\\tilde{c}^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\right)\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}+\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\right)\\Delta\\tilde{c}^{\\beta}+\\grad\\Delta\\tilde{c}^{\\beta}\\cdot\\mathbf{v}^{f}\\right)\\\\\nD\\hat{c}^{\\iota}\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\alpha_{f}\\nu^{\\iota}\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\beta}}\\Delta\\tilde{c}^{\\beta}\\\\\nD\\mathbf{j}_{d}^{\\iota}\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =-\\alpha_{f}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}d_{0}^{\\beta}\\grad\\Delta\\tilde{c}^{\\beta}+\\Delta\\tilde{c}^{\\beta}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}d_{0}^{\\iota}+\\tilde{\\kappa}^{\\iota}\\frac{\\partial d_{0}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\right)\\grad\\tilde{c}^{\\iota}\\right)\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThen we can evaluate the linearizations as follows,\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta W_{int}^{v}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\rho^{f}\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{\\Delta\\mathbf{v}^{f}}{\\Delta t}+\\left(\\grad\\Delta\\mathbf{v}^{f}\\cdot\\mathbf{v}^{f}+\\mathbf{L}^{f}\\cdot\\Delta\\mathbf{v}^{f}\\right)\\right)dv\\\\\n & +\\alpha_{f}\\int_{\\Omega^{f}}\\grad\\delta\\mathbf{v}^{f}:\\boldsymbol{\\mathcal{C}}^{\\tau}:\\frac{1}{2}\\left(\\grad\\Delta\\mathbf{v}^{f}+\\grad^{T}\\Delta\\mathbf{v}^{f}\\right)dv\\\\\nD\\left(\\delta W_{int}^{v}\\right)\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(\\tilde{p}_{J}^{\\prime}\\grad\\Delta J^{f}+\\Delta J^{f}\\tilde{p}_{J}^{\\prime\\prime}\\grad J^{f}+\\Delta J^{f}\\sum_{\\iota}R\\theta\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\grad\\tilde{c}^{\\iota}\\right)dv\\\\\n & -\\alpha_{f}\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\frac{\\rho^{f}}{J^{f}}\\Delta J^{f}\\mathbf{a}^{f}dv+\\alpha_{f}\\int_{\\Omega^{f}}\\boldsymbol{\\tau}_{J}^{\\prime}\\Delta J^{f}:\\grad\\delta\\mathbf{v}^{f}dv\\\\\nD\\left(\\delta W_{int}^{v}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot R\\theta\\sum_{\\iota}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}\\grad\\Delta\\tilde{c}^{\\beta}+\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\Delta\\tilde{c}^{\\beta}\\grad\\tilde{c}^{\\iota}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta W_{int}^{J}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\left(\\delta J^{f}\\frac{1}{J^{f}}\\grad J^{f}+\\grad\\delta J^{f}\\right)\\cdot\\Delta\\mathbf{v}^{f}dv\\\\\nD\\left(\\delta W_{int}^{J}\\right)\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\delta J^{f}\\frac{1}{J^{f}}\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}-\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\right)\\Delta J^{f}+\\grad\\Delta J^{f}\\cdot\\mathbf{v}^{f}\\right)dv\\\\\nD\\left(\\delta W_{int}^{J}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =0\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta W_{int}^{c}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\sum_{\\iota}\\int_{\\Omega^{f}}-\\delta\\tilde{c}^{\\iota}\\frac{1}{J^{f}}\\grad\\left(J^{f}c^{\\iota}\\right)\\cdot\\alpha_{f}\\Delta\\mathbf{v}_{f}\\\\\nD\\left(\\delta W_{int}^{c}\\right)\\left[\\Delta J^{f}\\right] & =\\sum_{\\iota}\\int_{\\Omega^{f}}\\delta\\tilde{c}^{\\iota}\\frac{\\alpha_{f}\\Delta J^{f}}{\\left(J^{f}\\right)^{2}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}\\\\\n & -\\delta\\tilde{c}^{\\iota}\\alpha_{f}\\frac{\\tilde{c}^{\\iota}}{J^{f}}\\left(\\tilde{\\kappa}^{\\iota}+J^{f}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\right)\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{\\Delta J^{f}}{\\Delta t}+\\grad\\Delta J^{f}\\cdot\\mathbf{v}^{f}\\right)\\\\\n & -\\delta\\tilde{c}^{\\iota}\\alpha_{f}\\frac{\\tilde{c}^{\\iota}}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\left(2\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}+J^{f}\\frac{\\partial^{2}\\tilde{\\kappa}^{\\iota}}{\\partial\\left(J^{f}\\right)^{2}}\\right)\\Delta J^{f}\\\\\n & -\\delta\\tilde{c}^{\\iota}\\alpha_{f}\\frac{1}{J^{f}}\\left(\\tilde{\\kappa}^{\\iota}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\frac{D^{f}\\tilde{c}^{\\gamma}}{Dt}\\right)\\Delta J^{f}\\\\\n & -\\delta\\tilde{c}^{\\iota}\\alpha_{f}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\sum_{\\gamma}\\left(\\frac{\\partial^{2}\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}\\partial\\tilde{c}^{\\gamma}}\\right)\\frac{D^{f}\\tilde{c}^{\\gamma}}{Dt}\\right)\\Delta J^{f}\\\\\n & +\\alpha_{f}\\delta\\tilde{c}^{\\iota}\\nu^{\\iota}\\frac{\\partial\\hat{\\zeta}}{\\partial J^{f}}\\Delta J^{f}-\\alpha_{f}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}d_{0}^{\\iota}\\grad\\tilde{c}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}d_{0}^{\\gamma}\\grad\\tilde{c}^{\\gamma}\\right)\\Delta J^{f}dv\\\\\nD\\left(\\delta W_{int}^{c}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\sum_{\\iota}\\int_{\\Omega^{f}}-\\delta\\tilde{c}^{\\iota}\\alpha_{f}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}+\\tilde{c}^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\right)\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}+\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\right)\\Delta\\tilde{c}^{\\beta}+\\grad\\Delta\\tilde{c}^{\\beta}\\cdot\\mathbf{v}^{f}\\right)\\\\\n & +\\delta\\tilde{c}^{\\iota}\\alpha_{f}\\nu^{\\iota}\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\beta}}\\Delta\\tilde{c}^{\\beta}\\\\\n & -\\alpha_{f}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}d_{0}^{\\beta}\\grad\\Delta\\tilde{c}^{\\beta}+\\Delta\\tilde{c}^{\\beta}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}d_{0}^{\\iota}+\\tilde{\\kappa}^{\\iota}\\frac{\\partial d_{0}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\right)\\grad\\tilde{c}^{\\iota}\\right)\\\\\n & -\\alpha_{f}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(z^{\\beta}\\tilde{\\kappa}^{\\beta}d_{0}^{\\beta}\\grad\\Delta\\tilde{c}^{\\beta}+\\Delta\\tilde{c}^{\\beta}\\sum_{\\gamma}z^{\\gamma}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}d_{0}^{\\gamma}+\\tilde{\\kappa}^{\\gamma}\\frac{\\partial d_{0}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\right)\\grad\\tilde{c}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n we can rewrite eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Ext-Virt-Work-CFDSol\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta W_{ext}= & \\int_{\\Gamma^{f}}\\delta\\mathbf{v}^{f}\\cdot\\mathbf{t}^{\\tau}da+\\int_{\\Gamma^{f}}\\delta J^{f}v_{n}^{f}da+\\int_{\\Gamma^{f}}\\delta\\tilde{c}^{\\iota}\\tilde{j}_{n}^{\\iota}da\\\\\n & +\\delta G_{ext}^{v}+\\delta G_{ext}^{c}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta G_{ext}^{v} & =\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(\\rho^{f}\\mathbf{b}^{f}+\\sum_{\\iota}M^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}\\right)dv\\\\\n\\delta G_{ext}^{c} & =-\\sum_{\\iota}\\int_{\\Omega^{f}}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(\\mathbf{j}_{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}_{b}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nand we can linearize \n\\begin_inset Formula $\\delta G_{ext}^{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta G_{ext}^{c}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta G_{ext}^{v}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\mathbf{0}\\\\\nD\\left(\\delta G_{ext}^{v}\\right)\\left[\\Delta J^{f}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(-\\frac{\\rho^{f}}{J^{f}}\\mathbf{b}^{f}+\\sum_{\\iota}M^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}\\right)\\Delta J^{f}dv\\\\\nD\\left(\\delta G_{ext}^{v}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\alpha_{f}\\int_{\\Omega^{f}}\\delta\\mathbf{v}^{f}\\cdot\\left(M^{\\beta}\\tilde{\\kappa}^{\\beta}\\mathbf{b}^{\\beta}+\\sum_{\\gamma}M^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\gamma}\\mathbf{b}^{\\gamma}\\right)\\Delta\\tilde{c}^{\\beta}dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta G_{ext}^{c}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\mathbf{0}\\\\\nD\\left(\\delta G_{ext}^{c}\\right)\\left[\\Delta J^{f}\\right] & =-\\sum_{\\iota}\\alpha_{f}\\int_{\\Omega^{f}}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(s^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}s^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}\\tilde{c}^{\\gamma}\\mathbf{b}^{\\gamma}\\right)\\Delta J^{f}dv\\\\\nD\\left(\\delta G_{ext}^{c}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =-\\sum_{\\iota}\\alpha_{f}\\int_{\\Omega^{f}}\\grad\\delta\\tilde{c}^{\\iota}\\cdot\\left(\\left(\\delta_{\\iota\\beta}+z^{\\beta}\\right)s^{\\beta}\\tilde{\\kappa}^{\\beta}\\mathbf{b}^{\\beta}+s^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}s^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\gamma}\\mathbf{b}^{\\gamma}\\right)\\Delta\\tilde{c}^{\\beta}\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nunder the assumption that \n\\begin_inset Formula $D\\mathbf{b}^{\\alpha}\\left[\\Delta\\mathbf{v}^{f}\\right]=\\mathbf{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization for Fluid-Solutes\n\\end_layout\n\n\\begin_layout Standard\nTo evaluate the discretized form of the virtual work and its linearizations we use\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta\\mathbf{v}^{f} & =\\sum_{a}N_{a}\\delta\\mathbf{v}_{a}^{f} & \\Delta\\mathbf{v}^{f} & =\\sum_{b}N_{b}\\Delta\\mathbf{v}_{b}^{f}\\\\\n\\delta J^{f} & =\\sum_{a}N_{a}\\delta J_{a}^{f} & \\Delta J^{f} & =\\sum_{b}N_{b}\\Delta J_{b}^{f}\\\\\n\\delta\\tilde{c}^{\\iota} & =\\sum_{a}N_{a}\\delta\\tilde{c}_{a}^{\\iota} & \\Delta\\tilde{c}^{\\beta} & =\\sum_{b}N_{b}\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}$\n\\end_inset\n\n are shape functions.\n Then,\n the discretized form of the virtual work takes the form\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta W_{int}^{v} & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\mathbf{f}_{a}^{v}\\\\\n\\delta W_{int}^{J} & =\\sum_{a}\\delta J_{a}^{f}f_{a}^{J}\\\\\n\\delta W_{int}^{c} & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}f_{a}^{\\iota}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{f}_{a}^{v} & =\\int_{\\Omega^{f}}N_{a}\\left(\\grad\\tilde{p}+\\rho^{f}\\mathbf{a}^{f}+\\sum_{\\iota}R\\theta\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}\\right)+\\boldsymbol{\\tau}\\cdot\\grad N_{a}dv\\\\\nf_{a}^{J} & =\\int_{\\Omega^{f}}N_{a}\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\grad N_{a}\\cdot\\mathbf{v}^{f}dv\\\\\nf_{a}^{\\iota} & =\\int_{\\Omega^{f}}-N_{a}\\left(\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}-\\hat{c}^{\\iota}\\right)+\\grad N_{a}\\cdot\\left(\\mathbf{j}_{d}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}_{d}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt} & =\\frac{c^{\\iota}}{J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\tilde{\\kappa}^{\\iota}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\frac{D^{f}\\tilde{\\kappa}^{\\iota}}{Dt}\\end{aligned}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta G_{ext}^{v} & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\mathbf{b}_{a}^{v}\\\\\n\\delta G_{ext}^{c} & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}b_{a}^{\\iota}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{b}_{a}^{v} & =\\int_{\\Omega^{f}}N_{a}\\left(\\rho^{f}\\mathbf{b}^{f}+\\sum_{\\iota}M^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}\\right)dv\\\\\nb_{a}^{\\iota} & =\\int_{\\Omega^{f}}N_{a}\\divg\\left(\\mathbf{j}_{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}_{b}^{\\gamma}\\right)\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nNote that\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\frac{D^{f}J^{f}}{Dt} & =\\frac{\\partial J^{f}}{\\partial t}+\\grad J^{f}\\cdot\\mathbf{v}^{f}\\\\\n\\frac{D^{f}c^{\\iota}}{Dt} & =\\frac{\\partial c^{\\iota}}{\\partial t}+\\grad c^{\\iota}\\cdot\\mathbf{v}^{f}\\\\\n & =\\tilde{\\kappa}^{\\iota}\\frac{\\partial\\tilde{c}^{\\iota}}{\\partial t}+\\tilde{c}^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial t}+\\left(\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}+\\tilde{c}^{\\iota}\\grad\\tilde{\\kappa}^{\\iota}\\right)\\cdot\\mathbf{v}^{f}\\\\\n\\frac{D^{f}\\tilde{\\kappa}^{\\iota}}{Dt} & =\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\frac{D\\tilde{c}^{\\gamma}}{Dt}\\\\\n\\grad\\tilde{\\kappa}^{\\iota} & =\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\grad J+\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}\\grad\\tilde{c}^{\\gamma}\\\\\n\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt} & =c^{\\iota}\\frac{D^{f}J^{f}}{Dt}+J^{f}\\frac{D^{f}c^{\\iota}}{Dt}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta W_{int}^{v}$\n\\end_inset\n\n gets discretized into\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta W_{int}^{v}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\sum_{b}\\alpha_{f}\\mathbf{K}_{ab}^{vv}\\cdot\\Delta\\mathbf{v}_{b}^{f}\\\\\nD\\left(\\delta W_{int}^{v}\\right)\\left[\\Delta J^{f}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\sum_{b}\\alpha_{f}\\mathbf{k}_{ab}^{vJ}\\Delta J_{b}^{f}\\\\\nD\\left(\\delta W_{int}^{v}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\sum_{b}\\alpha_{f}\\mathbf{k}_{ab}^{v\\beta}\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{K}_{ab}^{vv} & =\\int_{\\Omega^{f}}\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}^{\\tau}\\cdot\\grad N_{b}dv\\\\\n & +\\int_{\\Omega^{f}}N_{a}\\rho^{f}\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{N_{b}}{\\Delta t}+\\grad N_{b}\\cdot\\mathbf{v}^{f}\\right)\\mathbf{I}+N_{b}\\mathbf{L}^{f}\\right)dv\\\\\n\\mathbf{k}_{ab}^{vJ} & =\\int_{\\Omega^{f}}N_{a}\\left(\\tilde{p}_{J}^{\\prime}\\grad N_{b}+N_{b}\\left(\\tilde{p}_{J}^{\\prime\\prime}\\grad J^{f}+R\\theta\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}\\grad\\tilde{c}^{\\gamma}\\right)\\right)dv\\\\\n & +\\int_{\\Omega^{f}}N_{b}\\boldsymbol{\\tau}_{J}^{\\prime}\\cdot\\grad N_{a}dv-\\alpha_{f}\\int_{\\Omega^{f}}N_{a}N_{b}\\frac{\\rho^{f}}{J^{f}}\\mathbf{a}^{f}dv\\\\\n\\mathbf{k}_{ab}^{v\\beta} & =\\int_{\\Omega^{f}}N_{a}R\\theta\\left(\\tilde{\\kappa}^{\\beta}\\grad N_{b}+N_{b}\\sum_{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\grad\\tilde{c}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta W_{int}^{J}$\n\\end_inset\n\n gets discretized into\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta W_{int}^{J}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\alpha_{f}\\mathbf{k}_{ab}^{Jv}\\cdot\\Delta\\mathbf{v}_{b}^{f}\\\\\nD\\left(\\delta W_{int}^{J}\\right)\\left[\\Delta J^{f}\\right] & =\\sum_{a}\\delta J_{a}^{f}\\sum_{b}\\alpha_{f}k_{ab}^{JJ}\\Delta J_{b}^{f}\\\\\nD\\left(\\delta W_{int}^{J}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =0\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{k}_{ab}^{Jv} & =\\int_{\\Omega^{f}}N_{b}\\left(N_{a}\\frac{1}{J^{f}}\\grad J^{f}+\\grad N_{a}\\right)dv\\\\\nk_{ab}^{JJ} & =\\int_{\\Omega^{f}}\\frac{N_{a}}{J^{f}}\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}-\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\right)N_{b}+\\grad N_{b}\\cdot\\mathbf{v}^{f}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nFinally,\n the linearization of \n\\begin_inset Formula $\\delta W_{int}^{c}$\n\\end_inset\n\n gets discretized into\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta W_{int}^{c}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}\\sum_{b}\\alpha_{f}\\mathbf{k}_{ab}^{\\iota v}\\cdot\\Delta\\mathbf{v}_{b}^{f}\\\\\nD\\left(\\delta W_{int}^{c}\\right)\\left[\\Delta J^{f}\\right] & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}\\sum_{b}\\alpha_{f}k_{ab}^{\\iota J}\\Delta J_{b}^{f}\\\\\nD\\left(\\delta W_{int}^{c}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}\\sum_{b}\\alpha_{f}k^{\\iota\\beta}\\,\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{k}_{ab}^{\\iota v} & =-\\int_{\\Omega^{f}}N_{a}N_{b}\\frac{1}{J^{f}}\\grad\\left(J^{f}c^{\\iota}\\right)\\,dv\\\\\nk_{ab}^{\\iota J} & =\\int_{\\Omega^{f}}N_{a}N_{b}\\frac{1}{\\left(J^{f}\\right)^{2}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt}\\\\\n & -N_{a}\\tilde{c}^{\\iota}\\left(\\frac{\\tilde{\\kappa}^{\\iota}}{J^{f}}+\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\right)\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{N_{b}}{\\Delta t}+\\grad N_{b}\\cdot\\mathbf{v}^{f}\\right)\\\\\n & -N_{a}N_{b}\\frac{\\tilde{c}^{\\iota}}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\left(2\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}+J^{f}\\frac{\\partial^{2}\\tilde{\\kappa}^{\\iota}}{\\partial\\left(J^{f}\\right)^{2}}\\right)\\\\\n & -N_{a}N_{b}\\left(\\left(\\frac{\\tilde{\\kappa}^{\\iota}}{J^{f}}+\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\right)\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\sum_{\\gamma}\\left(\\frac{1}{J^{f}}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\gamma}}+\\frac{\\partial^{2}\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}\\partial\\tilde{c}^{\\gamma}}\\right)\\frac{D^{f}\\tilde{c}^{\\gamma}}{Dt}\\right)\\\\\n & +N_{a}N_{b}\\nu^{\\iota}\\underbrace{\\frac{\\partial\\hat{\\zeta}}{\\partial J^{f}}}_{=0\\text{ currently}}-N_{b}\\grad N_{a}\\cdot\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}d_{0}^{\\iota}\\grad\\tilde{c}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}d_{0}^{\\gamma}\\grad\\tilde{c}^{\\gamma}\\right)dv\\\\\nk^{\\iota\\beta} & =\\int_{\\Omega^{f}}-N_{a}\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}+\\tilde{c}^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\right)\\left(\\left(\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}\\frac{1}{\\Delta t}+\\frac{1}{J^{f}}\\frac{D^{f}J^{f}}{Dt}\\right)N_{b}+\\grad N_{b}\\cdot\\mathbf{v}^{f}\\right)\\\\\n & +N_{a}N_{b}\\nu^{\\iota}\\frac{\\partial\\hat{\\zeta}}{\\partial\\tilde{c}^{\\beta}}\\\\\n & -\\grad N_{a}\\cdot\\left(\\delta_{\\iota\\beta}\\tilde{\\kappa}^{\\beta}d_{0}^{\\beta}\\grad N_{b}+N_{b}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}d_{0}^{\\iota}+\\tilde{\\kappa}^{\\iota}\\frac{\\partial d_{0}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\right)\\grad\\tilde{c}^{\\iota}\\right)\\\\\n & -\\grad N_{a}\\cdot\\left(z^{\\beta}\\tilde{\\kappa}^{\\beta}d_{0}^{\\beta}\\grad N_{b}+N_{b}\\sum_{\\gamma}z^{\\gamma}\\left(\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}d_{0}^{\\gamma}+\\tilde{\\kappa}^{\\gamma}\\frac{\\partial d_{0}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\right)\\grad\\tilde{c}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\frac{1}{J^{f}}\\grad\\left(J^{f}c^{\\iota}\\right) & =\\frac{1}{J^{f}}c^{\\iota}\\grad J^{f}+\\tilde{\\kappa}^{\\iota}\\grad\\tilde{c}^{\\iota}+\\tilde{c}^{\\iota}\\grad\\tilde{\\kappa}^{\\iota}\\\\\n\\frac{1}{J^{f}}\\frac{D^{f}\\left(J^{f}c^{\\iota}\\right)}{Dt} & =\\frac{c^{\\iota}}{J^{f}}\\frac{D^{f}J^{f}}{Dt}+\\tilde{\\kappa}^{\\iota}\\frac{D^{f}\\tilde{c}^{\\iota}}{Dt}+\\tilde{c}^{\\iota}\\frac{D^{f}\\tilde{\\kappa}^{\\iota}}{Dt}\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the external work contributions from body forces,\n the discretized forms are\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\delta G_{ext}^{v} & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\mathbf{b}_{a}^{v}\\\\\n\\delta G_{ext}^{c} & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}b_{a}^{\\iota}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{b}_{a}^{v} & =\\int_{\\Omega^{f}}N_{a}\\left(\\rho^{f}\\mathbf{b}^{f}+\\sum_{\\iota}M^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}\\right)dv\\\\\nb_{a}^{\\iota} & =-\\int_{\\Omega^{f}}\\grad N_{a}\\cdot\\left(\\mathbf{j}_{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}_{b}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta G_{ext}^{v}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\mathbf{0}\\\\\nD\\left(\\delta G_{ext}^{v}\\right)\\left[\\Delta J^{f}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\sum_{b}\\alpha_{f}\\mathbf{b}_{ab}^{vJ}\\Delta J_{b}^{f}\\\\\nD\\left(\\delta G_{ext}^{v}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\sum_{a}\\delta\\mathbf{v}_{a}^{f}\\cdot\\sum_{b}\\alpha_{f}\\mathbf{b}_{ab}^{v\\beta}\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{b}_{ab}^{vJ} & =\\int_{\\Omega^{f}}N_{a}N_{b}\\left(-\\frac{\\rho^{f}}{J^{f}}\\mathbf{b}^{f}+\\sum_{\\gamma}M^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}\\tilde{c}^{\\iota}\\mathbf{b}^{\\gamma}\\right)dv\\\\\n\\mathbf{b}_{ab}^{v\\beta} & =\\int_{\\Omega^{f}}N_{a}N_{b}\\left(M^{\\beta}\\tilde{\\kappa}^{\\beta}\\mathbf{b}^{\\beta}+\\sum_{\\gamma}M^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\gamma}\\mathbf{b}^{\\gamma}\\right)dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\left(\\delta G_{ext}^{c}\\right)\\left[\\Delta\\mathbf{v}^{f}\\right] & =\\mathbf{0}\\\\\nD\\left(\\delta G_{ext}^{c}\\right)\\left[\\Delta J^{f}\\right] & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}\\sum_{b}\\alpha_{f}b_{ab}^{\\iota J}\\Delta J_{b}^{f}\\\\\nD\\left(\\delta G_{ext}^{c}\\right)\\left[\\Delta\\tilde{c}^{\\beta}\\right] & =\\sum_{\\iota}\\sum_{a}\\delta\\tilde{c}_{a}^{\\iota}\\sum_{b}\\alpha_{f}b_{ab}^{\\iota\\beta}\\Delta\\tilde{c}_{b}^{\\beta}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}b_{ab}^{\\iota J} & =-\\int_{\\Omega^{f}}N_{b}\\grad N_{a}\\cdot\\left(s^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}s^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}\\tilde{c}^{\\gamma}\\mathbf{b}^{\\gamma}\\right)dv\\\\\nb_{ab}^{\\iota\\beta} & =-\\int_{\\Omega^{f}}N_{b}\\grad N_{a}\\cdot\\left(\\left(\\delta_{\\iota\\beta}+z^{\\beta}\\right)s^{\\beta}\\tilde{\\kappa}^{\\beta}\\mathbf{b}^{\\beta}+s^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\iota}\\mathbf{b}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}s^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\gamma}\\mathbf{b}^{\\gamma}\\right)\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nIn the special case when the body force acting on each constituent is the same,\n \n\\begin_inset Formula $\\mathbf{b}^{f}=\\mathbf{b}^{\\iota}\\equiv\\mathbf{b}$\n\\end_inset\n\n,\n these expressions simplify further,\n\\begin_inset Formula \n\\[\n\\begin{aligned}b_{ab}^{\\iota J} & =-\\int_{\\Omega^{f}}N_{b}\\left(w^{\\iota J}+w_{e}^{cJ}\\right)\\grad N_{a}\\cdot\\mathbf{b}dv\\\\\nb_{ab}^{\\iota\\beta} & =-\\int_{\\Omega^{f}}N_{b}\\left(\\left(\\delta_{\\iota\\beta}+z^{\\beta}\\right)s^{\\beta}\\tilde{\\kappa}^{\\beta}+w^{\\iota\\beta}+w_{e}^{c\\beta}\\right)\\grad N_{a}\\cdot\\mathbf{b}\\,dv\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}w^{\\iota J} & \\equiv s^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial J^{f}}\\tilde{c}^{\\iota} & w_{e}^{cJ} & \\equiv\\sum_{\\gamma}z^{\\gamma}s^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial J^{f}}\\tilde{c}^{\\gamma}\\\\\nw^{\\iota\\beta} & \\equiv s^{\\iota}\\frac{\\partial\\tilde{\\kappa}^{\\iota}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\iota} & w_{e}^{c\\beta} & \\equiv\\sum_{\\gamma}z^{\\gamma}s^{\\gamma}\\frac{\\partial\\tilde{\\kappa}^{\\gamma}}{\\partial\\tilde{c}^{\\beta}}\\tilde{c}^{\\gamma}\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nNewton-Raphson Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Newton-Raphson-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Newton-Raphson method (also known as \n\\begin_inset Quotes eld\n\\end_inset\n\nNewton's method\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n \n\\begin_inset Quotes eld\n\\end_inset\n\nFull Newton method\n\\begin_inset Quotes erd\n\\end_inset\n\n or \n\\begin_inset Quotes eld\n\\end_inset\n\nthe Newton method\n\\begin_inset Quotes erd\n\\end_inset\n\n) is the basis for solving the nonlinear finite element equations.\n This section will describe the \n\\emph on\nFull Newton method\n\\emph default\n and the Broyden-Fletcher-Goldfarb-Shanno (BFGS) method \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Matthies79\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The latter variation is actually a \n\\emph on\nquasi-Newton method\n\\emph default\n.\n It is important since it provides several advantages over the full Newton method and it is this method that is implemented in FEBio \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Matthies79\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nFull Newton Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Full-Newton-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Newton-Raphson equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq173\"\nnolink \"false\"\n\n\\end_inset\n\n can be written in terms of the discretized equilibrium equations that were derived in the previous section as follows:\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{v}^{T}\\cdot\\mathbf{K}\\cdot\\mathbf{u}=-\\delta\\mathbf{v}^{T}\\cdot\\mathbf{R}.\\label{eq355}\n\\end{equation}\n\n\\end_inset\n\nSince the virtual velocities \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\nare arbitrary,\n a discretized Newton-Raphson scheme can be formulated as follows:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{array}{cc}\n\\mathbf{K}\\left(\\mathbf{x}_{k}\\right)\\cdot\\mathbf{u}=-\\mathbf{R}\\left(\\mathbf{x}_{k}\\right); & \\mathbf{x}_{k+1}=\\mathbf{x}_{k}+\\mathbf{u}\\end{array}.\\label{eq356}\n\\end{equation}\n\n\\end_inset\n\nThis is the basis of the Newton-Raphson method.\n For each iteration \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n both the stiffness matrix and the residual vector are re-evaluated and a displacement increment \n\\series bold\nu \n\\series default\nis calculated by pre-multiplying both sides of the above equation by \n\\begin_inset Formula $\\mathbf{K}^{-1}$\n\\end_inset\n\n.\n This procedure is repeated until some convergence criteria are satisfied.\n\\end_layout\n\n\\begin_layout Standard\nThe formation of the stiffness matrix and,\n especially,\n calculation of its inverse,\n are computationally expensive.\n Quasi-Newton methods do not require the reevaluation of the stiffness matrix for every iteration.\n Instead,\n a quick update is calculated.\n One particular method that has been quite successful in the field of computational solid mechanics is the BFGS method,\n which is described in the next section.\n\\end_layout\n\n\\begin_layout Subsection\nBFGS Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BFGS-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe BFGS method updates the stiffness matrix (or rather its inverse) to provide an approximation to the exact matrix.\n A displacement increment is defined as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}_{k}=\\mathbf{x}_{k}-\\mathbf{x}_{k-1}\\,,\\label{eq357}\n\\end{equation}\n\n\\end_inset\n\nand an increment in the residual is defined as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{G}_{k}=\\mathbf{R}_{k-1}-\\mathbf{R}_{k}\\,.\\label{eq358}\n\\end{equation}\n\n\\end_inset\n\nThe updated matrix \n\\begin_inset Formula $\\mathbf{K}_{k}$\n\\end_inset\n\n should satisfy the quasi-Newton equation:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{k}\\mathbf{d}_{k}=\\mathbf{G}_{k}\\,.\\label{eq359}\n\\end{equation}\n\n\\end_inset\n\nIn order to calculate this update,\n as displacement increment is first calculated:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{u}=\\mathbf{K}_{k-1}^{-1}\\mathbf{R}_{k-1}\\,.\\label{eq360}\n\\end{equation}\n\n\\end_inset\n\nThis displacement vector defines a \n\\begin_inset Quotes eld\n\\end_inset\n\ndirection\n\\begin_inset Quotes erd\n\\end_inset\n\n for the actual displacement increment.\n A line search (see next section) can now be applied to determine the optimal displacement increment:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}_{k}=\\mathbf{x}_{k-1}+s\\mathbf{u}\\,,\\label{eq361}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $s$\n\\end_inset\n\nis determined from the line search.\n With the updated position calculated,\n \n\\begin_inset Formula $\\mathbf{R}_{k}$\n\\end_inset\n\ncan be evaluated.\n Also,\n using equations \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq357\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq358\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{d}_{k}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{G}_{k}$\n\\end_inset\n\n can be evaluted.\n The stiffness update can now be expressed as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{k}^{-1}=\\mathbf{A}_{k}^{T}\\mathbf{K}_{k-1}^{-1}\\mathbf{A}_{k}\\,,\\label{eq362}\n\\end{equation}\n\n\\end_inset\n\nwhere the matrix \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n\n\\series default\n is an \n\\begin_inset Formula $n\\times n$\n\\end_inset\n\n matrix of the simple form:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{A}_{k}=\\mathbf{1}+\\mathbf{v}_{k}\\mathbf{w}_{k}^{T}\\,.\\label{eq363}\n\\end{equation}\n\n\\end_inset\n\nThe vectors \n\\series bold\n\n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n \n\\series default\nand \n\\series bold\n\n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n\n\\series default\n are given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}_{k}=-\\left(\\frac{\\mathbf{d}_{k}^{T}\\mathbf{G}_{k}}{\\mathbf{d}_{k}^{T}\\mathbf{K}_{k-1}\\mathbf{d}_{k}}\\right)^{1/2}\\mathbf{K}_{k-1}\\mathbf{d}_{k}-\\mathbf{G}_{k}\\,,\\label{eq364}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}_{k}=\\frac{\\mathbf{d}_{k}}{\\mathbf{d}_{k}^{T}\\mathbf{G}_{k}}\\,.\\label{eq365}\n\\end{equation}\n\n\\end_inset\n\nThe vector \n\\begin_inset Formula $\\mathbf{K}_{k-1}\\mathbf{d}_{k}$\n\\end_inset\n\n is equal to \n\\begin_inset Formula $s\\mathbf{R}_{k-1}$\n\\end_inset\n\n and has already been calculated.\n\\end_layout\n\n\\begin_layout Standard\nTo avoid numerically dangerous updates,\n the condition number \n\\begin_inset Formula $c$\n\\end_inset\n\n of the updating matrix \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n\n\\series default\n is calculated:\n\\begin_inset Formula \n\\begin{equation}\nc=\\left(\\frac{\\mathbf{d}_{k}^{T}\\mathbf{G}_{k}}{\\mathbf{d}_{k}^{T}\\mathbf{K}_{k-1}\\mathbf{d}_{k}}\\right)^{1/2}\\,.\\label{eq365b}\n\\end{equation}\n\n\\end_inset\n\nThe update is not performed when this number exceeds a preset tolerance.\n\\end_layout\n\n\\begin_layout Standard\nConsidering the actual computations involved,\n it should be noted that using the matrix updates defined above,\n the calculation of the search direction in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq360\"\nnolink \"false\"\n\n\\end_inset\n\n can be rewritten as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{u}=\\left(\\mathbf{1}+\\mathbf{w}_{k-1}\\mathbf{v}_{k-1}^{T}\\right)\\cdots\\left(\\mathbf{1}+\\mathbf{w}_{1}\\mathbf{v}_{1}^{T}\\right)\\mathbf{K}_{0}^{-1}\\left(\\mathbf{1}+\\mathbf{v}_{1}\\mathbf{w}_{1}^{T}\\right)\\cdots\\left(\\mathbf{1}+\\mathbf{v}_{k-1}\\mathbf{w}_{k-1}^{T}\\right)\\mathbf{R}_{k-1}\\,.\\label{eq366}\n\\end{equation}\n\n\\end_inset\n\nHence,\n the search direction can be computed without explicitly calculating the updated matrices or performing any additional costly matrix factorizations as required in the full Newton-Raphson method.\n\\end_layout\n\n\\begin_layout Subsection\nLine Search Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Line-Search-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA powerful technique often used to improve the convergence rate of Newton based methods is the \n\\emph on\nline search method\n\\emph default\n.\n In this method,\n the direction of the displacement vector \n\\series bold\n\n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n \n\\series default\nis considered as optimal,\n but the magnitude is controlled by a parameter \n\\begin_inset Formula $s$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}_{k+1}=\\mathbf{x}_{k}+s\\mathbf{u}\\,.\\label{eq367}\n\\end{equation}\n\n\\end_inset\n\nThe value of \n\\begin_inset Formula $s$\n\\end_inset\n\nis usually chosen so that the total potential energy \n\\begin_inset Formula $W\\left(s\\right)=W\\left(\\mathbf{x}_{k}+s\\mathbf{u}\\right)$\n\\end_inset\n\nat the end of the iteration is minimized in the direction of \n\\series bold\n\n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n\n\\series default\n.\n This is equivalent to the requirement that the residual force \n\\begin_inset Formula $\\mathbf{R}\\left(\\mathbf{x}_{k}+s\\mathbf{u}\\right)$\n\\end_inset\n\nat the end of the iteration is orthogonal to \n\\series bold\n\n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n\n\\series default\n:\n \n\\begin_inset Formula \n\\begin{equation}\nR\\left(s\\right)=\\mathbf{u}^{T}\\mathbf{R}\\left(\\mathbf{x}_{k}+s\\mathbf{u}\\right)=0\\,.\\label{eq368}\n\\end{equation}\n\n\\end_inset\n\nHowever,\n in practice it is sufficient to obtain a value of \n\\begin_inset Formula $s$\n\\end_inset\n\nsuch that,\n \n\\begin_inset Formula \n\\begin{equation}\n\\left|R\\left(s\\right)\\right|<\\rho\\left|R\\left(0\\right)\\right|\\,,\\label{eq369}\n\\end{equation}\n\n\\end_inset\n\nwhere typically a value of \n\\begin_inset Formula $\\rho=0.9$\n\\end_inset\n\n is used.\n Under normal conditions the value \n\\begin_inset Formula $s=1$\n\\end_inset\n\n automatically satisfies equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq369\"\nnolink \"false\"\n\n\\end_inset\n\n and therefore few extra operations are involved.\n However,\n when this is not the case,\n a more suitable value for \n\\begin_inset Formula $s$\n\\end_inset\n\nneeds to be obtained.\n For this reason it is convenient to approximate \n\\begin_inset Formula $R\\left(s\\right)$\n\\end_inset\n\nas a quadratic in \n\\begin_inset Formula $s$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\nR\\left(s\\right)\\approx\\left(1-s\\right)R\\left(0\\right)+R\\left(1\\right)s^{2}=0\\,,\\label{eq370}\n\\end{equation}\n\n\\end_inset\n\nwhich yields a value for \n\\begin_inset Formula $s$\n\\end_inset\n\nas \n\\begin_inset Formula \n\\begin{equation}\ns=\\frac{r}{2}\\pm\\sqrt{\\left(\\frac{r}{2}\\right)^{2}-r}\\,,\\quad r=\\frac{R\\left(0\\right)}{R\\left(1\\right)}\\,.\\label{eq371}\n\\end{equation}\n\n\\end_inset\n\nIf \n\\begin_inset Formula $r<0$\n\\end_inset\n\n,\n the square root is positive and a first improved value for \n\\begin_inset Formula $s$\n\\end_inset\n\nis obtained:\n \n\\begin_inset Formula \n\\begin{equation}\ns_{1}=\\frac{r}{2}+\\sqrt{\\left(\\frac{r}{2}\\right)^{2}-r}\\,.\\label{eq372}\n\\end{equation}\n\n\\end_inset\n\nIf \n\\begin_inset Formula $r>0$\n\\end_inset\n\n the \n\\begin_inset Formula $s$\n\\end_inset\n\ncan be obtained by using the value that minimizes the quadratic function,\n that is,\n \n\\begin_inset Formula $s_{1}=r/2$\n\\end_inset\n\n.\n This procedure is now repeated with \n\\begin_inset Formula $R\\left(1\\right)$\n\\end_inset\n\n replaced by \n\\begin_inset Formula $R\\left(s_{1}\\right)$\n\\end_inset\n\n until equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq369\"\nnolink \"false\"\n\n\\end_inset\n\n is satisfied.\n\\end_layout\n\n\\begin_layout Subsection\nConjugate gradient solution method\n\\end_layout\n\n\\begin_layout Standard\nThe Newton-Raphson solution algorithms are highly effective for nearly linear problems,\n but they have two major limitations.\n Firstly,\n for very large models,\n the size of the stiffness matrix K can grow very large as well.\n The factorization of this matrix,\n which is needed when using direct linear solvers,\n can become very time consuming and requires a large amount of memory.\n This is the main factor that limits the size of model that can be solved on a given computer.\n Secondly,\n where the stiffness is small,\n it can overestimate the displacement,\n resulting in excessive distortion of the mesh and the familiar negative Jacobian failure.\n This can occur when a material has a low initial stiffness that increases at higher strains (we work around this by using a large number of small timesteps to reduce the nonlinearity on each increment),\n when buckling causes a temporary low or negative stiffness,\n when a part of the model is not sufficiently constrained,\n or when contacts cause sudden changes of stiffness.\n For these reasons the Newton-Raphson algorithms often fail to converge for very nonlinear materials,\n models with contacts or unstable structures that can buckle or crumple under load.\n\\end_layout\n\n\\begin_layout Standard\nAn alternative strategy is to estimate a direction to move each node directly from the residual R:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{u=-}\\boldsymbol{R\\left(x_{k}\\right)}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThen a line search is needed to find the appropriate distance to move in that direction.\n This is the steepest descent method,\n which has the disadvantage that it converges slowly in a series of decreasing zig-zag steps.\n Its convergence is much improved by adding a fraction β of the previous step:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{u}_{k}=-\\boldsymbol{R}\\left(\\boldsymbol{x}_{k}\\right)+\\beta\\boldsymbol{u}_{k-1}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThere are many different ways to calculate β;\n one method which has excellent convergence for nonlinear problems is due to Hager and Zhang:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{y}_{i}=\\boldsymbol{R}_{i-1}-\\boldsymbol{R}_{i}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\beta_{i}=\\frac{-1}{\\boldsymbol{u}_{i-1}\\cdot\\boldsymbol{y}_{i}}\\left(\\boldsymbol{y}_{i}-2\\boldsymbol{u}_{i-1}\\frac{\\left\\Vert \\boldsymbol{y}_{i}\\right\\Vert ^{2}}{\\boldsymbol{u}_{i-1}\\cdot\\boldsymbol{y}_{i}}\\right)\\cdot\\boldsymbol{R}_{i}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\eta_{i}=\\frac{-1}{\\left\\Vert \\boldsymbol{u}_{i-1}\\right\\Vert min\\left\\{ \\eta,\\left\\Vert \\boldsymbol{R}_{i-1}\\right\\Vert \\right\\} }\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\beta_{i}=max\\left\\{ \\beta_{i},\\eta_{i}\\right\\} \n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA limitation of this method is that the distance each node moves is proportional to the residual;\n stiffer nodes produce a larger residual for a given displacement and therefore move further,\n whereas in fact they need to move less than more compliant nodes.\n This results in mesh distortion where the element size varies,\n for midside nodes in quadratic elements or for mixed materials.\n This problem can be reduced by using a preconditioner,\n dividing the residual at each node by an estimate of its stiffness.\n This requires changes also to the calculation of β:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\beta_{i}=\\frac{-1}{\\boldsymbol{u}_{i-1}\\cdot\\boldsymbol{y}_{i}}\\left(\\boldsymbol{y_{i}^{T}P}-2\\boldsymbol{u}_{i-1}\\frac{\\boldsymbol{y_{i}^{T}Py}_{i}}{\\boldsymbol{u}_{i-1}\\cdot\\boldsymbol{y}_{i}}\\right)\\cdot\\boldsymbol{R}_{i}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\eta_{i}=0.4\\frac{\\Delta\\boldsymbol{u}_{i-1}^{T}\\boldsymbol{R}_{i}}{\\boldsymbol{u_{i-1}^{T}P^{-1}u_{i-1}}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\beta_{i}=max\\left\\{ \\beta_{i},\\eta_{i}\\right\\} \n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{u}_{i+1}=\\boldsymbol{P}\\boldsymbol{R}_{i}+\\beta_{i}\\boldsymbol{u}_{i}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe preconditioner \n\\series bold\nP\n\\series default\n can be found in various ways;\n a convenient approximation is to use the reciprocal of the main diagonal of the stiffness matrix:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{P}^{-1}=diag\\left(\\boldsymbol{K}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis is calculated once at the beginning of the solution.\n\\end_layout\n\n\\begin_layout Section\nGeneralized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nMethod\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Generalized-Alpha-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe generalized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nmethod is used for temporal discretization of governing equations in fluid mechanics.\n For this method we combine the degrees of freedom into \n\\begin_inset Formula $\\mathbf{Y}_{n}=\\left\\{ \\mathbf{v},J\\right\\} _{n}$\n\\end_inset\n\n,\n where the subscript \n\\begin_inset Formula $n$\n\\end_inset\n\n denotes time \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n;\n similarly,\n we let \n\\begin_inset Formula $\\dot{\\mathbf{Y}}_{n}=\\left\\{ \\frac{\\partial\\mathbf{v}}{\\partial t},\\frac{\\partial J}{\\partial t}\\right\\} _{n}$\n\\end_inset\n\n.\n According to this method \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n,\n the virtual work is evaluated at \n\\begin_inset Formula $\\delta W\\left(\\dot{\\mathbf{Y}}_{n+\\alpha_{m}},\\mathbf{Y}_{n+\\alpha_{f}}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $t_{n+\\alpha}=t_{n}+\\alpha\\Delta t$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta t=t_{n+1}-t_{n}$\n\\end_inset\n\n.\n Here,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{Y}_{n+\\alpha_{f}} & =\\alpha_{f}\\mathbf{Y}_{n+1}+\\left(1-\\alpha_{f}\\right)\\mathbf{Y}_{n}\\\\\n\\dot{\\mathbf{Y}}_{n+\\alpha_{m}} & =\\alpha_{m}\\dot{\\mathbf{Y}}_{n+1}+\\left(1-\\alpha_{m}\\right)\\dot{\\mathbf{Y}}_{n}\n\\end{aligned}\n\\,.\\label{eq:ga-Y-Ydot}\n\\end{equation}\n\n\\end_inset\n\nThe parameters \n\\begin_inset Formula $\\alpha_{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{m}$\n\\end_inset\n\n are evaluated from a single parameter \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n using\n\\begin_inset Formula \n\\begin{equation}\n\\alpha_{f}=\\frac{1}{1+\\rho_{\\infty}}\\,,\\quad\\alpha_{m}=\\frac{1}{2}\\frac{3-\\rho_{\\infty}}{1+\\rho_{\\infty}}\\,,\\label{eq:ga-alphas}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $0\\le\\rho_{\\infty}\\le1$\n\\end_inset\n\n.\n This parameter is the spectral radius for an infinite time step,\n which controls the amount of damping of high frequencies;\n a value of zero produces the greatest amount of damping,\n anihilating the highest frequency in one step,\n whereas a value of one preserves the highest frequency.\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\delta W\\left(\\dot{\\mathbf{Y}}_{n+\\alpha_{m}},\\mathbf{Y}_{n+\\alpha_{f}}\\right)$\n\\end_inset\n\n reported in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CFD-Discretization-Linearization\"\nnolink \"false\"\n\n\\end_inset\n\n is effectively performed along an increment \n\\begin_inset Formula $\\Delta\\mathbf{Y}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{Y}_{n+1}$\n\\end_inset\n\n so that the solution to \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n produces \n\\begin_inset Formula $\\mathbf{Y}_{n+1}$\n\\end_inset\n\n.\n Based on Newmark integration,\n we have\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{Y}}_{n+1}=\\frac{\\mathbf{Y}_{n+1}-\\mathbf{Y}_{n}}{\\gamma\\Delta t}-\\left(\\frac{1}{\\gamma}-1\\right)\\dot{\\mathbf{Y}}_{n}\\,.\\label{eq:ga-Newmark-integration}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n according to the generalized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nmethod,\n\\begin_inset Formula \n\\begin{equation}\n\\gamma=\\frac{1}{2}+\\alpha_{m}-\\alpha_{f}\\,.\\label{eq:ga-ksi-gamma}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n in this scheme,\n \n\\begin_inset Formula $\\dot{\\mathbf{Y}}_{n+\\alpha_{m}}$\n\\end_inset\n\n is evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{Y}}_{n+\\alpha_{m}}=\\left(1-\\frac{\\alpha_{m}}{\\gamma}\\right)\\dot{\\mathbf{Y}}_{n}+\\frac{\\xi}{\\Delta t}\\left(\\mathbf{Y}_{n+\\alpha_{f}}-\\mathbf{Y}_{n}\\right)\\,,\\quad\\xi\\equiv\\frac{\\alpha_{m}}{\\alpha_{f}\\gamma}.\\label{eq:ga-Ydot-evaluation}\n\\end{equation}\n\n\\end_inset\n\nUsing \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:ga-Y-Ydot\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:ga-Ydot-evaluation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{Y}_{n+\\alpha_{f}}\\left[\\Delta\\mathbf{Y}\\right] & =\\alpha_{f}\\Delta\\mathbf{Y}\\\\\nD\\dot{\\mathbf{Y}}_{n+\\alpha_{m}}\\left[\\Delta\\mathbf{Y}\\right] & =\\frac{\\alpha_{m}}{\\gamma}\\frac{\\Delta\\mathbf{Y}}{\\Delta t}\n\\end{aligned}\n\\label{eq:ga-Y-Ydot-linearization}\n\\end{equation}\n\n\\end_inset\n\nGiven the solution \n\\begin_inset Formula $\\left(\\dot{\\mathbf{Y}}_{n+\\alpha_{m}},\\mathbf{Y}_{n+\\alpha_{f}}\\right)$\n\\end_inset\n\n,\n the solution at \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n is evaluated from\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{Y}_{n+1} & =\\mathbf{Y}_{n}+\\frac{\\mathbf{Y}_{n+\\alpha_{f}}-\\mathbf{Y}_{n}}{\\alpha_{f}}\\,,\\\\\n\\dot{\\mathbf{Y}}_{n+1} & =\\dot{\\mathbf{Y}}_{n}+\\frac{\\dot{\\mathbf{Y}}_{n+\\alpha_{m}}-\\dot{\\mathbf{Y}}_{n}}{\\alpha_{m}}\\,.\n\\end{aligned}\n\\label{eq:ga-Y-updates}\n\\end{equation}\n\n\\end_inset\n\nFour different options are presented in \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n for initializing \n\\begin_inset Formula $\\mathbf{Y}_{n+1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\dot{\\mathbf{Y}}_{n+1}$\n\\end_inset\n\n at the beginning of time step \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n;\n the first three of these have been implemented in FEBio.\n For steady flows these authors recommend disregarding \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n and setting \n\\begin_inset Formula $\\alpha_{f}=\\alpha_{m}=\\gamma=1$\n\\end_inset\n\n to recover the backward Euler scheme.\n\\end_layout\n\n\\begin_layout Chapter\nElement Library\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Element-Library\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio provides several element types for finite element discretization.\n This chapter describes these elements in more detail.\n\\end_layout\n\n\\begin_layout Section\nSolid Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Solid-elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe 3D solid elements available in FEBio are \n\\emph on\nisoparametric elements\n\\emph default\n.\n All of the solid elements are formulated in a global Cartesian coordinate system.\n For all these elements,\n a local coordinate system (so-called \n\\emph on\nisoparametric coordinates\n\\emph default\n) is defined as well.\n The global position vector \n\\series bold\n\n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n\n\\series default\n can be written as a function of the isoparametric coordinates in the following sense:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}\\left(r,s,t\\right)=\\sum\\limits_{i=1}^{n}N_{i}\\left(r,s,t\\right)\\mathbf{x}_{i}.\\label{eq373}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $n$\n\\end_inset\n\n is the number of nodes,\n \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n \n\\begin_inset Formula $s$\n\\end_inset\n\n and \n\\begin_inset Formula $t$\n\\end_inset\n\n are the isoparametric coordinates,\n \n\\begin_inset Formula $N_{i}$\n\\end_inset\n\n are the element shape functions and \n\\begin_inset Formula $\\mathbf{x}_{i}$\n\\end_inset\n\n are the spatial coordinates of the element nodes.\n The same parametric interpolation is used for the interpolation of other scalar and vector quantities.\n\\end_layout\n\n\\begin_layout Standard\nAll elements in FEBio are integrated numerically.\n This implies that integrals over the volume of the element \n\\begin_inset Formula $v^{e}$\n\\end_inset\n\n are approximated by a sum:\n \n\\begin_inset Formula \n\\begin{equation}\n\\int\\limits_{v^{e}}f\\left(\\mathbf{x}\\right)\\,dv=\\int\\limits_{\\Square^{e}}f\\left({\\mathbf{r}}\\right)J(\\mathbf{r})d\\Square\\,\\cong\\sum\\limits_{i=1}^{m}f\\left(\\mathbf{r}_{i}\\right)J_{i}w_{i}\\,.\\label{eq374}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\Square$\n\\end_inset\n\n is the biunit cube,\n \n\\begin_inset Formula $m$\n\\end_inset\n\n is the number of integration points,\n \n\\begin_inset Formula $\\mathbf{r}_{i}$\n\\end_inset\n\nare the location of the integration points in isoparametric coordinates,\n \n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the transformation \n\\begin_inset Formula $\\mathbf{x}=\\mathbf{x}\\left(r,s,t\\right)$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $w_{i}$\n\\end_inset\n\n is a weight associated with the integration point.\n The integration is performed over the element's volume in the natural coordinate system.\n\\end_layout\n\n\\begin_layout Standard\nMost fully integrated solid elements are unsuitable for the analysis of (nearly-) incompressible material behavior.\n To deal with this type of deformation,\n a three-field element implementation is available in FEBio \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nHexahedral Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Hexahedral-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio implements an 8-node trilinear hexahedral element.\n This element is also known as a \n\\emph on\nbrick \n\\emph default\nelement.\n The shape functions for these elements are defined in function of the isoparametric coordinates \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n \n\\begin_inset Formula $s$\n\\end_inset\n\n and \n\\begin_inset Formula $t,$\n\\end_inset\n\n and are given below.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}N_{1} & =\\frac{1}{8}\\left(1-r\\right)\\left(1-s\\right)\\left(1-t\\right)\\\\\nN_{2} & =\\frac{1}{8}\\left(1+r\\right)\\left(1-s\\right)\\left(1-t\\right)\\\\\nN_{3} & =\\frac{1}{8}\\left(1+r\\right)\\left(1+s\\right)\\left(1-t\\right)\\\\\nN_{4} & =\\frac{1}{8}\\left(1-r\\right)\\left(1+s\\right)\\left(1-t\\right)\\\\\nN_{5} & =\\frac{1}{8}\\left(1-r\\right)\\left(1-s\\right)\\left(1+t\\right)\\\\\nN_{6} & =\\frac{1}{8}\\left(1+r\\right)\\left(1-s\\right)\\left(1+t\\right)\\\\\nN_{7} & =\\frac{1}{8}\\left(1+r\\right)\\left(1+s\\right)\\left(1+t\\right)\\\\\nN_{8} & =\\frac{1}{8}\\left(1-r\\right)\\left(1+s\\right)\\left(1+t\\right)\n\\end{aligned}\n\\,.\\label{eq375}\n\\end{equation}\n\n\\end_inset\n\nThe following integration rule is implemented for this element type.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n8-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPentahedral Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Pentahedral-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPentahedral elements (also knows as \n\\begin_inset Quotes eld\n\\end_inset\n\nwedge\n\\begin_inset Quotes erd\n\\end_inset\n\n elements) consist of six nodes and five faces.\n Their shape functions are defined in function of the isoparametric coordinates \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n \n\\begin_inset Formula $s$\n\\end_inset\n\n and \n\\begin_inset Formula $t$\n\\end_inset\n\n and are given as follows.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}N_{1} & =\\frac{1}{2}\\left(1-r-s\\right)\\left(1-t\\right)\\\\\nN_{2} & =\\frac{1}{2}r\\left(1-t\\right)\\\\\nN_{3} & =\\frac{1}{2}s\\left(1-t\\right)\\\\\nN_{4} & =\\frac{1}{2}\\left(1-r-s\\right)\\left(1+t\\right)\\\\\nN_{5} & =\\frac{1}{2}r\\left(1+t\\right)\\\\\nN_{6} & =\\frac{1}{2}s\\left(1+t\\right)\n\\end{aligned}\n\\,.\\label{eq376}\n\\end{equation}\n\n\\end_inset\n\nThe following integration rule is implemented for this element type.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n6-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.666666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.666666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.666666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.666666667\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.577350269\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTetrahedral Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tetrahedral-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLinear 4-node tetrahedral elements are also available in FEBio.\n Their shape functions are defined in function of the isoparametric coordinates \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n \n\\begin_inset Formula $s$\n\\end_inset\n\n and \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}N_{1} & =1-r-s-t\\\\\nN_{2} & =r\\\\\nN_{3} & =s\\\\\nN_{4} & =t\n\\end{aligned}\n\\,.\\label{eq377}\n\\end{equation}\n\n\\end_inset\n\nThe following integration rules are implemented for this element type.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n1-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.166666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n4-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.58541020 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.58541020 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.58541020 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigSolidElementsTM.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nDifferent solid element types that are available in FEBio\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nQuadratic Tetrahedral Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Quadratic-Tetrahedral-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio implements a 10-node quadratic tetrahedral element.\n It has four corner nodes and six nodes located at the midpoint of the edges.\n The shape functions in terms area coordinates are given below.\n The area coordinates relate to the isoparametric coordinates as follows.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}t_{1} & =1-r-s-t\\\\\nt_{2} & =r\\\\\nt_{3} & =s\\\\\nt_{4} & =t\n\\end{aligned}\n\\,.\\label{eq378}\n\\end{equation}\n\n\\end_inset\n\nThe shape functions follow.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}H_{i} & =t_{i}\\left(2t_{i}-1\\right),\\,i=1\\cdots4\\\\\nH_{5} & =4t_{1}t_{2}\\\\\nH_{6} & =4t_{2}t_{3}\\\\\nH_{7} & =4t_{3}t_{1}\\\\\nH_{8} & =4t_{1}t_{4}\\\\\nH_{9} & =4t_{2}t_{4}\\\\\nH_{10} & =4t_{3}t_{4}\n\\end{aligned}\n\\,.\\label{eq379}\n\\end{equation}\n\n\\end_inset\n\nThe following integration rules are implemented for this element type.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n4-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.58541020\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.58541020\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.58541020\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.13819660\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.041666667 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n8-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01583591\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.023087995 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01583591\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.023087995 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01583591\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.023087995 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.328054697\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.023087995 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.679143178\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.018578672 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.679143178\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.018578672 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.679143178\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.018578672 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.106952274\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.018578672 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"13\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n11-point Gauss-Lobatto rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.002777778 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.002777778 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.002777778 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.002777778 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011111111 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011111111 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011111111 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011111111 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011111111 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011111111 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.088888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio also implements a 15-node quadratic tetrahedral element.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigQuadraticTetrahedralElements.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nQuadratic tetrahedral elements available in FEBio.\n Left,\n a 10-node quadratic tet.\n Right,\n a 15-node quadratic tet.\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following integration rules are implemented for this element type.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n8-point Gauss rule\n\\series default\n\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\n Note that weights sum up to one and not to the volume of the tet in the natural coordinate system (i.e.\n 1/6).\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0158359099\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.138527967 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0158359099\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.138527967 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0158359099\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.138527967 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.3280546970\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.138527967 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.6791431780\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.111472033 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.6791431780\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.111472033 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.6791431780\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.111472033 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.1069522740\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.111472033 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"13\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n11-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-0.01315555556 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.007622222222 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.785714285714286\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.007622222222 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.785714285714286 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.007622222222 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.071428571428571 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.785714285714286 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.007622222222 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.024888888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.024888888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.024888888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.024888888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.024888888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.100596423833201 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.399403576166799 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.024888888889 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"17\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n15-point Gauss rule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nr\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ns\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nw\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.030283678097089 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.006026785714286 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.000000000000000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.006026785714286 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.000000000000000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.006026785714286 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.333333333333333\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.000000000000000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.006026785714286 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011645249086029 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.727272727272727\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011645249086029 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.727272727272727\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011645249086029 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.090909090909091\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.727272727272727\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.011645249086029 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.010949141561386 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.010949141561386 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.010949141561386 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.010949141561386 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.010949141561386 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.433449846426336\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.066550153573664\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.010949141561386 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nShell Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Shell-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHistorically,\n shells have been formulated using two different approaches \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hughes80\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The difference between these approaches lies in the way the rotational degrees of freedom are defined.\n In the first approach,\n the rotational degrees of freedom are defined as angles.\n In addition,\n the plane stress condition needs to be enforced to take thickness variations into account.\n This approach is very useful for infinitesimal strains,\n but becomes very difficult to pursue in finite deformation due to the fact that finite rotations do not commute.\n Another disadvantage of this approach is that it requires a modification to the material formulation to enforce the plane stress condition.\n For complex materials this modification is very difficult or even impossible to obtain.\n\\end_layout\n\n\\begin_layout Standard\nThe alternative approach is to use an \n\\emph on\nextensible director \n\\emph default\nto describe the rotational degrees of freedom.\n With this approach it is not necessary to enforce the plane-stress condition and the full 3D constitutive relations can be employed.\n This approach is adapted in FEBio as described here.\n\\end_layout\n\n\\begin_layout Standard\nThe shell formulation implemented in FEBio is still a work in progress.\n The goal is to implement an extensible director formulation with strain enhancements to deal with the well-known locking effect in incompressible and bending problems \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Betsch96\"\nliteral \"true\"\n\n\\end_inset\n\n.\n With the current state of the implementation,\n it is advised to use quadratic elements in such problems.\n\\end_layout\n\n\\begin_layout Standard\nStarting with FEBio 2.6,\n two shell formulations have become available:\n The original formulation,\n where nodes are located at the mid-surface through the thickness of the shell,\n and a new formulation where nodes are located on the top face of the shell.\n The original formulation uses nodal displacements and directors as degrees of freedom;\n the new formulation uses top and bottom face nodal displacements.\n The new formulation is designed to properly accommodate shells attached to the surface of a solid element,\n or shells sandwiched between two solid elements,\n with minimal alterations to the rest of the code.\n The original formulation does not strictly enforce continuity of all the relevant degrees of freedom in those situations.\n However,\n this original formulation is maintained in the code for backward compatibility.\n\\end_layout\n\n\\begin_layout Standard\nMost of the shell elements available in FEBio use a \n\\emph on\ncompatible strain\n\\emph default\n formulation,\n where the calculation of strain components is based only on nodal displacements,\n similar to hexahedral or pentrahedral elements.\n Users should be aware that this compatible strain formulation is very susceptible to element locking when the shell thickness is much smaller than the shell size (e.g.,\n when the aspect ratio is less than 0.01).\n Therefore,\n these shell formulations should be used with caution,\n keeping in mind this important constraint.\n Conversely,\n these shell elements perform very well when they are attached to solid elements (e.g.,\n skin over muscle),\n or sandwiched between shell elements (e.g.,\n cell membrane separating cytoplasm from extra-cellular matrix).\n\\end_layout\n\n\\begin_layout Standard\nThe element-locking limitation of compatible strain shell formulations has motivated the development of specialized shell formulations that attempt to overcome locking.\n The FE literature on this subject is rather extensive and we refer the reader to the excellent review chapter by Bischoff et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bischoff18\"\nliteral \"false\"\n\n\\end_inset\n\n on this topic.\n Methods for overcoming locking include the assumed natural strain (ANS) formulation for transverse shear strains \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"MacNeal78,Bathe86\"\nliteral \"false\"\n\n\\end_inset\n\n and transverse normal strains \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Betsch95,Bischoff97\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The ANS formulation may be supplemented with the enhanced assumed strain (EAS) method \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo90\"\nliteral \"false\"\n\n\\end_inset\n\n and extended to large deformations \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Klinkel99,Vu-Quoc03,Simo93\"\nliteral \"false\"\n\n\\end_inset\n\n.\n FEBio includes the ANS (\n\\emph on\nq4ans\n\\emph default\n) and EAS (\n\\emph on\nq4eas\n\\emph default\n) quadrilateral shell element formulations of Vu-Quoc and Tan \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Vu-Quoc03\"\nliteral \"false\"\n\n\\end_inset\n\n,\n using a seven-parameter EAS interpolation,\n which is otherwise substantially similar to the five-parameter interpolation presented in an earlier study by Klinkel et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Klinkel99\"\nliteral \"false\"\n\n\\end_inset\n\n.\n These shell elements are not suitable for attachment to a solid element,\n nor sandwiching between two solid elements.\n Since they don't experience element locking,\n they should be loaded more slowly than compatible strain shell elements.\n The formulations presented below are for the compatible strain shell elements.\n\\end_layout\n\n\\begin_layout Subsection\nShell with mid-surface nodal displacements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Shell-formulation-standalone\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe create a shell formulation by reducing a solid element interpolation which is linear along the parametric coordinate \n\\begin_inset Formula $\\xi_{3}$\n\\end_inset\n\n.\n We start with the general interpolation for a solid element,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}\\left(\\xi_{i}\\right)=\\sum\\limits_{a=1}^{n}N_{a}\\left(\\xi_{i}\\right)\\mathbf{x}_{a}\\,,\\label{eq380}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $i=1,2,3$\n\\end_inset\n\n and \n\\begin_inset Formula $n$\n\\end_inset\n\n is the number of nodes,\n and specialize it to the case of a shell as \n\\begin_inset Formula \n\\begin{equation}\nN_{a}\\left(\\xi_{i}\\right)=\\begin{cases}\n\\frac{1-\\xi_{3}}{2}M_{a}\\left(\\xi_{\\alpha}\\right) & 1\\leqslant a\\leqslant m\\\\\n\\frac{1+\\xi_{3}}{2}M_{a}\\left(\\xi_{\\alpha}\\right) & m+1\\leqslant a\\leqslant n\n\\end{cases}\\,,\\label{eq381}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\alpha=1,2$\n\\end_inset\n\n,\n \n\\begin_inset Formula $m=n/2$\n\\end_inset\n\n is the number of shell element nodes,\n and \n\\begin_inset Formula $M_{a}\\left(\\xi_{\\alpha}\\right)$\n\\end_inset\n\n are the interpolation functions within the mid-shell surface.\n The description of the mid-shell surface is thus given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\hat{x}}\\left(\\xi_{\\alpha}\\right)=\\sum\\limits_{a=1}^{n}N_{a}\\left(\\xi_{1},\\xi_{2},0\\right)\\mathbf{x}_{a}\\equiv\\sum\\limits_{b=1}^{m}M_{b}\\left(\\xi_{\\alpha}\\right)\\mathbf{\\hat{x}}_{b}\\,,\\label{eq382}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\hat{x}}_{b}=\\frac{1}{2}\\left(\\mathbf{x}_{b}+\\mathbf{x}_{b+m}\\right)\\label{eq383}\n\\end{equation}\n\n\\end_inset\n\nare the nodal positions for the mid-shell surface.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigShellElementsTM.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nExample of shell elements with four mid-surface nodal positions \n\\begin_inset Formula $\\mathbf{\\hat{x}}_{b}$\n\\end_inset\n\n and directors \n\\begin_inset Formula $\\mathbf{d}_{b}$\n\\end_inset\n\n (\n\\begin_inset Formula $b=1-4)$\n\\end_inset\n\n,\n reduced from a solid element.\n \n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe also define the director across the shell surface as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}\\left(\\xi_{\\alpha}\\right)=\\mathbf{x}\\left(\\xi_{1},\\xi_{2},1\\right)-\\mathbf{x}\\left(\\xi_{1},\\xi_{2},-1\\right)=\\sum\\limits_{b=1}^{m}M_{b}\\left(\\xi_{\\alpha}\\right)\\mathbf{d}_{b}\\,,\\label{eq384}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}_{b}=\\mathbf{x}_{b+m}-\\mathbf{x}_{b},\\quad b=1-m\\label{eq385}\n\\end{equation}\n\n\\end_inset\n\nare the nodal directors.\n Note that the magnitude of the nodal director represents the shell thickness,\n \n\\begin_inset Formula $h\\left(\\xi_{\\alpha}\\right)=\\left\\Vert \\mathbf{d}\\left(\\xi_{\\alpha}\\right)\\right\\Vert $\n\\end_inset\n\n and the shell thicknesses at the nodes are \n\\begin_inset Formula $h_{b}=\\left\\Vert \\mathbf{d}_{b}\\right\\Vert $\n\\end_inset\n\n.\n With these definitions we find that the interpolation across the parametric space of the shell element is \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}\\left(\\xi_{i}\\right)=\\mathbf{\\hat{x}}\\left(\\xi_{\\alpha}\\right)+\\frac{1}{2}\\xi_{3}\\mathbf{d}\\left(\\xi_{\\alpha}\\right)=\\sum\\limits_{b=1}^{m}M_{b}\\left(\\xi_{\\alpha}\\right)\\left(\\mathbf{\\hat{x}}_{b}+\\frac{1}{2}\\xi_{3}\\mathbf{d}_{b}\\right)\\,.\\label{eq386}\n\\end{equation}\n\n\\end_inset\n\nFrom this relation we can obtain the covariant basis vectors as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{g}_{\\alpha}\\left(\\xi_{i}\\right) & =\\frac{\\partial\\mathbf{x}}{\\partial\\xi_{\\alpha}}=\\sum\\limits_{b=1}^{m}\\frac{\\partial M_{b}}{\\partial\\xi_{\\alpha}}\\left(\\mathbf{\\hat{x}}_{b}+\\frac{1}{2}\\xi_{3}\\mathbf{d}_{b}\\right)\\\\\n\\mathbf{g}_{3}\\left(\\xi_{i}\\right) & =\\frac{\\partial\\mathbf{x}}{\\partial\\xi_{3}}=\\frac{1}{2}\\sum\\limits_{b=1}^{m}M_{b}\\left(\\xi_{\\alpha}\\right)\\mathbf{d}_{b}\n\\end{aligned}\n\\,,\\label{eq387}\n\\end{equation}\n\n\\end_inset\n\nfrom which we may evaluate the contravariant basis vectors \n\\begin_inset Formula $\\mathbf{g}^{j}$\n\\end_inset\n\n using the identity \n\\begin_inset Formula $\\mathbf{g}_{i}\\cdot\\mathbf{g}^{j}=\\delta_{i}^{j}$\n\\end_inset\n\n.\n Then,\n the gradients of the shape functions are given by \n\\begin_inset Formula \n\\begin{equation}\n\\grad M_{b}=\\frac{\\partial M_{b}}{\\partial\\xi_{\\alpha}}\\mathbf{g}^{\\alpha},\\quad\\grad\\left(\\frac{1}{2}\\xi_{3}M_{b}\\right)=\\frac{1}{2}\\left(\\xi_{3}\\grad M_{b}+M_{b}\\mathbf{g}^{3}\\right)\\,.\\label{eq388}\n\\end{equation}\n\n\\end_inset\n\nIt follows from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq386\"\nnolink \"false\"\n\n\\end_inset\n\n that the virtual displacement is \n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{u}\\left(\\xi_{i}\\right)=\\sum\\limits_{a=1}^{m}M_{a}\\left(\\xi_{\\alpha}\\right)\\left(\\delta\\mathbf{\\hat{u}}_{a}+\\frac{1}{2}\\xi_{3}\\delta\\mathbf{d}_{a}\\right)\\,,\\label{eq389}\n\\end{equation}\n\n\\end_inset\n\nand the incremental displacement is \n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\mathbf{u}\\left(\\xi_{i}\\right)=\\sum\\limits_{b=1}^{m}M_{b}\\left(\\xi_{\\alpha}\\right)\\left(\\Delta\\mathbf{\\hat{u}}_{b}+\\frac{1}{2}\\xi_{3}\\Delta\\mathbf{d}_{b}\\right)\\,.\\label{eq390}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio,\n for historical reasons,\n the nodal director \n\\begin_inset Formula $\\mathbf{d}_{b}$\n\\end_inset\n\n is currently called \n\\emph on\nrotation\n\\emph default\n.\n This is a misnomer and users should treat this \n\\emph on\nrotation\n\\emph default\n as the vector \n\\begin_inset Formula $\\mathbf{d}_{a}$\n\\end_inset\n\n whose components have units of length.\n Thus,\n fixing or prescribing \n\\emph on\nrotation\n\\emph default\n components in the input file effectively places these constraints on the components of the nodal director;\n similarly,\n requesting \n\\emph on\nrotation\n\\emph default\n in the output files will produce the components of the director.\n\\end_layout\n\n\\begin_layout Standard\nWhen this type of shell is connected face-to-face with a solid element,\n the nodes located at \n\\begin_inset Formula $\\mathbf{\\hat{x}}_{b}$\n\\end_inset\n\n automatically share their displacement degrees of freedom \n\\begin_inset Formula $\\mathbf{u}_{b}$\n\\end_inset\n\n with the corresponding nodes from the face of the solid element.\n However,\n no continuity is enforced between the directors \n\\begin_inset Formula $\\mathbf{d}_{b}$\n\\end_inset\n\n and the solid element deformation.\n One consequence of this condition is that a shell sandwiched between two solid elements will not detect out-of-plane shear and normal stresses transmitted by the solid element(s).\n Another consequence is that bending of the solid element(s) will not produce a bending moment in the shell.\n Therefore,\n these shell elements are best used as shell-only structures.\n\\end_layout\n\n\\begin_layout Subsubsection\nElastic Shell\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Elastic-Shell\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor an elastic shell,\n the internal virtual work becomes \n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{\\text{int}}^{e}=\\int\\limits_{\\Omega^{e}}\\boldsymbol{\\sigma}:\\grad\\delta\\mathbf{u}\\,dv=\\sum\\limits_{a=1}^{n}\\left[\\begin{array}{cc}\n\\delta\\mathbf{\\hat{u}}_{a} & \\delta\\mathbf{d}_{a}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{u}\\\\\n\\mathbf{f}_{a}^{d}\n\\end{array}\\right]\\,,\\label{eq391}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}_{a}^{u}=\\int\\limits_{\\Omega^{e}}\\boldsymbol{\\sigma}\\cdot\\grad M_{a}\\,dv,\\quad\\mathbf{f}_{a}^{d}=\\int\\limits_{\\Omega^{e}}\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1}{2}\\xi_{3}M_{a}\\right)\\,dv\\,.\\label{eq392}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of the internal virtual work is \n\\begin_inset Formula \n\\begin{equation}\n\\begin{array}{c}\nD\\left(\\delta W_{\\text{int}}^{e}\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int\\limits_{\\Omega^{e}}\\tr\\left(\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad^{T}\\delta\\mathbf{u}\\right)\\,dv\\\\\n+\\int\\limits_{\\Omega^{e}}\\grad\\delta\\mathbf{u}:\\boldsymbol{\\mathcal{C}}:\\grad^{T}\\Delta\\mathbf{u}\\,dv\n\\end{array}\\,.\\label{eq393}\n\\end{equation}\n\n\\end_inset\n\nThe first of these integrals may be discretized as \n\\begin_inset Formula \n\\begin{equation}\n\\int\\limits_{\\Omega^{e}}\\tr\\left(\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad^{T}\\delta\\mathbf{u}\\right)\\,dv=\\sum\\limits_{a=1}^{m}\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{cc}\n\\delta\\mathbf{\\hat{u}}_{a} & \\delta\\mathbf{d}_{a}\\end{array}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{ud}\\\\\n\\mathbf{K}_{ab}^{du} & \\mathbf{K}_{ab}^{dd}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{\\hat{u}}_{b}\\\\\n\\Delta\\mathbf{d}_{b}\n\\end{array}\\right]\\,,\\label{eq394}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =\\int\\limits_{\\Omega^{e}}\\left(\\grad M_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad M_{b}\\right)\\mathbf{I}\\,dv\\\\\n\\mathbf{K}_{ab}^{ud} & =\\int\\limits_{\\Omega^{e}}\\left(\\grad M_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1}{2}\\xi_{3}M_{b}\\right)\\right)\\mathbf{I}\\,dv\\\\\n\\mathbf{K}_{ab}^{du} & =\\int\\limits_{\\Omega^{e}}\\left(\\grad\\left(\\frac{1}{2}\\xi_{3}M_{a}\\right)\\cdot\\boldsymbol{\\sigma}\\cdot\\grad M_{b}\\right)\\mathbf{I}\\,dv\\\\\n\\mathbf{K}_{ab}^{dd} & =\\int\\limits_{\\Omega^{e}}\\left(\\grad\\left(\\frac{1}{2}\\xi_{3}M_{a}\\right)\\cdot\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1}{2}\\xi_{3}M_{b}\\right)\\right)\\mathbf{I}\\,dv\n\\end{aligned}\n\\,.\\label{eq395}\n\\end{equation}\n\n\\end_inset\n\nThe second integral in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq393\"\nnolink \"false\"\n\n\\end_inset\n\n becomes \n\\begin_inset Formula \n\\begin{equation}\n\\int\\limits_{\\Omega^{e}}\\grad\\delta\\mathbf{u}:\\boldsymbol{\\mathcal{C}}:\\grad^{T}\\Delta\\mathbf{u}\\,dv=\\sum\\limits_{a=1}^{m}\\sum\\limits_{b=1}^{m}\\left[\\begin{array}{cc}\n\\delta\\mathbf{\\hat{u}}_{a} & \\delta\\mathbf{d}_{a}\\end{array}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{ud}\\\\\n\\mathbf{K}_{ab}^{du} & \\mathbf{K}_{ab}^{dd}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{\\hat{u}}_{b}\\\\\n\\Delta\\mathbf{d}_{b}\n\\end{array}\\right]\\,,\\label{eq396}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =\\int\\limits_{\\Omega^{e}}\\grad M_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad M_{b}\\,dv\\\\\n\\mathbf{K}_{ab}^{ud} & =\\int\\limits_{\\Omega^{e}}\\grad M_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad\\left(\\frac{1}{2}\\xi_{3}M_{b}\\right)\\,dv\\\\\n\\mathbf{K}_{ab}^{du} & =\\int\\limits_{\\Omega^{e}}\\grad\\left(\\frac{1}{2}\\xi_{3}M_{a}\\right)\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad M_{b}\\,dv\\\\\n\\mathbf{K}_{ab}^{dd} & =\\int\\limits_{\\Omega^{e}}\\grad\\left(\\frac{1}{2}\\xi_{3}M_{a}\\right)\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad\\left(\\frac{1}{2}\\xi_{3}M_{b}\\right)\\,dv\n\\end{aligned}\n\\,.\\label{eq397}\n\\end{equation}\n\n\\end_inset\n\nSimilar expressions may be derived for the external work and inertia forces.\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio a 3-point Gaussian quadrature rule is used for the through-the-thickness integration.\n FEBio currently supports four- and eight-node quadrilateral and three- and six-node triangular shell elements.\n\\end_layout\n\n\\begin_layout Subsubsection\nQuadrilateral shells\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Quadrilateral-shells\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor four-node quadrilateral shells,\n the shape functions are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}M_{1} & =\\frac{1}{4}\\left(1-r\\right)\\left(1-s\\right)\\\\\nM_{2} & =\\frac{1}{4}\\left(1+r\\right)\\left(1-s\\right)\\\\\nM_{3} & =\\frac{1}{4}\\left(1+r\\right)\\left(1+s\\right)\\\\\nM_{4} & =\\frac{1}{4}\\left(1-r\\right)\\left(1+s\\right)\n\\end{aligned}\n\\,.\\label{eq398}\n\\end{equation}\n\n\\end_inset\n\nFor eight-node quadrilateral shells the shape functions are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}M_{1} & =\\frac{1}{4}\\left(1-r\\right)\\left(1-s\\right)-\\frac{1}{2}\\left(M_{8}+M_{5}\\right) & M_{5} & =\\frac{1}{2}\\left(1-r^{2}\\right)\\left(1-s\\right)\\\\\nM_{2} & =\\frac{1}{4}\\left(1+r\\right)\\left(1-s\\right)-\\frac{1}{2}\\left(M_{5}+M_{6}\\right) & M_{6} & =\\frac{1}{2}\\left(1-s^{2}\\right)\\left(1+r\\right)\\\\\nM_{3} & =\\frac{1}{4}\\left(1+r\\right)\\left(1+s\\right)-\\frac{1}{2}\\left(M_{6}+M_{7}\\right) & M_{7} & =\\frac{1}{2}\\left(1-r^{2}\\right)\\left(1+s\\right)\\\\\nM_{4} & =\\frac{1}{4}\\left(1-r\\right)\\left(1+s\\right)-\\frac{1}{2}\\left(M_{7}+M_{8}\\right) & M_{8} & =\\frac{1}{2}\\left(1-s^{2}\\right)\\left(1-r\\right)\n\\end{aligned}\n\\,.\\label{eq399}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTriangular shells\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Triangular-shells\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor three-node triangular shell elements,\n the shape functions are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}M_{1} & =1-r-s\\\\\nM_{2} & =r\\\\\nM_{3} & =s\n\\end{aligned}\n\\,.\\label{eq400}\n\\end{equation}\n\n\\end_inset\n\nFor six-node triangular shell elements they are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}M_{1} & =r_{1}\\left(2r_{1}-1\\right) & M_{4} & =4r_{1}r_{2}\\\\\nM_{2} & =r_{2}\\left(2r_{2}-1\\right) & M_{5} & =4r_{2}r_{3}\\\\\nM_{3} & =r_{3}\\left(2r_{3}-1\\right) & M_{6} & =4r_{3}r_{1}\\\\\nr_{1} & =1-r-s & r_{2} & =r & r_{3} & =s\n\\end{aligned}\n\\,.\\label{eq401}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigShellElementTypesTM.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nDifferent shell elements available in FEBio.\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nShells with top and bottom face nodal displacements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Shells-front-back\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe create a shell formulation by reducing a 3D element interpolation which is linear along \n\\begin_inset Formula $\\xi_{3}$\n\\end_inset\n\n.\n The nodal positions at the bottom of the shell (\n\\begin_inset Formula $\\xi_{3}=-1$\n\\end_inset\n\n) are denoted by \n\\begin_inset Formula $\\mathbf{y}_{a}$\n\\end_inset\n\n and those on the top of the shell (\n\\begin_inset Formula $\\xi_{3}=+1$\n\\end_inset\n\n) are denoted by \n\\begin_inset Formula $\\mathbf{x}_{a}$\n\\end_inset\n\n,\n thus\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}\\left(\\xi_{i}\\right)=\\sum_{a}M_{a}\\left(\\xi_{1},\\xi_{2}\\right)\\left(\\frac{1+\\xi_{3}}{2}\\mathbf{x}_{a}+\\frac{1-\\xi_{3}}{2}\\mathbf{y}_{a}\\right)=\\sum_{a}M_{a}\\left(\\xi_{1},\\xi_{2}\\right)\\left(\\mathbf{x}_{a}-\\frac{1-\\xi_{3}}{2}\\mathbf{d}_{a}\\right)\\label{eq:solid-element-interpolation}\n\\end{equation}\n\n\\end_inset\n\nThe vector from \n\\begin_inset Formula $\\mathbf{y}_{a}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\mathbf{x}_{a}$\n\\end_inset\n\n is the director,\n \n\\begin_inset Formula $\\mathbf{d}_{a}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\n\\mathbf{d}_{a}=\\mathbf{x}_{a}-\\mathbf{y}_{a}\n\\]\n\n\\end_inset\n\nFrom this relation we can get the shell covariant basis vectors,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{g}_{\\alpha}\\left(\\xi_{i}\\right) & =\\frac{\\partial\\mathbf{x}}{\\partial\\xi_{\\alpha}}=\\sum_{a}\\frac{\\partial M_{a}}{\\partial\\xi_{\\alpha}}\\left(\\frac{1+\\xi_{3}}{2}\\mathbf{x}_{a}+\\frac{1-\\xi_{3}}{2}\\mathbf{y}_{a}\\right)=\\sum_{a}\\frac{\\partial M_{a}}{\\partial\\xi_{\\alpha}}\\left(\\mathbf{x}_{a}-\\frac{1-\\xi_{3}}{2}\\mathbf{d}_{a}\\right)\\\\\n\\mathbf{g}_{3}\\left(\\xi_{i}\\right) & =\\frac{\\partial\\mathbf{x}}{\\partial\\xi_{3}}=\\sum_{a}\\frac{1}{2}M_{a}\\left(\\xi_{1},\\xi_{2}\\right)\\left(\\mathbf{x}_{a}-\\mathbf{y}_{a}\\right)=\\sum_{a}\\frac{1}{2}M_{a}\\left(\\xi_{1},\\xi_{2}\\right)\\mathbf{d}_{a}\n\\end{aligned}\n\\label{eq:shell-covariant-basis}\n\\end{equation}\n\n\\end_inset\n\nfrom which we may evaluate the contravariant basis vectors \n\\begin_inset Formula $\\mathbf{g}^{i}$\n\\end_inset\n\n.\n Let the front-face and back-face displacements be denoted by \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n respectively.\n It follows that \n\\begin_inset Formula $\\mathbf{x}_{a}=\\mathbf{X}_{a}+\\mathbf{u}_{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{y}_{a}=\\mathbf{Y}_{a}+\\mathbf{w}_{a}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{X}_{a}$\n\\end_inset\n\n represents the shell nodal positions in the reference configuration,\n provided as nodal coordinates in the input file,\n and \n\\begin_inset Formula $\\mathbf{Y}_{a}=\\mathbf{X}_{a}-\\mathbf{D}_{a}$\n\\end_inset\n\n is evaluated from the user-defined referential shell thickness,\n and the surface surface normals evaluated at each node.\n If the shell surface is not planar in the reference configuration,\n users must be careful to select shell thicknesses that don't produce inverted elements (negative Jacobians) as a result of this extrapolation.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigShellElementsFB.png\n\tlyxscale 45\n\tscale 45\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nExample of shell element with front-face nodal positions \n\\begin_inset Formula $\\mathbf{x}_{b}$\n\\end_inset\n\n and back-face nodal positions \n\\begin_inset Formula $\\mathbf{y}_{b}$\n\\end_inset\n\n (\n\\begin_inset Formula $b=1-4)$\n\\end_inset\n\n,\n reduced from a solid element.\n \n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIt follows that the virtual displacement is\n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{u}\\left(\\xi_{i}\\right)=\\sum_{a}M_{a}\\left(\\frac{1+\\xi_{3}}{2}\\delta\\mathbf{u}_{a}+\\frac{1-\\xi_{3}}{2}\\delta\\mathbf{w}_{a}\\right)\\,,\\label{eq:shell-virtual-velocity}\n\\end{equation}\n\n\\end_inset\n\nand the incremental displacement is\n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\mathbf{u}\\left(\\xi_{i}\\right)=\\sum_{b}M_{b}\\left(\\frac{1+\\xi_{3}}{2}\\Delta\\mathbf{u}_{b}+\\frac{1-\\xi_{3}}{2}\\Delta\\mathbf{w}_{b}\\right)\\,,\\label{eq:shell-incremental-displacement}\n\\end{equation}\n\n\\end_inset\n\nso that\n\\begin_inset Formula \n\\begin{equation}\n\\grad\\delta\\mathbf{u}=\\sum_{a}\\delta\\mathbf{u}_{a}\\otimes\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\right)+\\delta\\mathbf{w}_{a}\\otimes\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{a}\\right)\\,,\\label{eq:gradient-virtual-velocity}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\grad\\Delta\\mathbf{u}=\\sum_{b}\\Delta\\mathbf{u}_{b}\\otimes\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)+\\Delta\\mathbf{w}_{b}\\otimes\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)\\label{eq:gradient-incremental-displacement}\n\\end{equation}\n\n\\end_inset\n\nNote that\n\\begin_inset Formula \n\\begin{equation}\n\\grad M_{b}=\\frac{\\partial M_{b}}{\\partial\\xi_{\\alpha}}\\mathbf{g}^{\\alpha}\\,,\\label{eq:shell-shape-gradient-M}\n\\end{equation}\n\n\\end_inset\n\nso that\n\\begin_inset Formula \n\\begin{equation}\n\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)=\\frac{1}{2}\\left(\\left(1+\\xi_{3}\\right)\\grad M_{b}+M_{b}\\mathbf{g}^{3}\\right)\\label{eq:shell-shape-gradient-Mu}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)=\\frac{1}{2}\\left(\\left(1-\\xi_{3}\\right)\\grad M_{b}-M_{b}\\mathbf{g}^{3}\\right)\\label{eq:shell-shape-gradient-Md}\n\\end{equation}\n\n\\end_inset\n\nTo evaluate the deformation gradient in this shell element,\n we use\n\\begin_inset Formula \n\\[\n\\mathbf{F}=\\Grad\\mathbf{x}=\\sum_{b}\\mathbf{u}_{b}\\otimes\\Grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)+\\mathbf{w}_{b}\\otimes\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this formulation,\n when a shell element is connected face-to-face with a solid element,\n the nodal displacements of the solid element face are set to coincide with the back-face nodal displacements \n\\begin_inset Formula $\\mathbf{w}_{b}$\n\\end_inset\n\n of the shell.\n When a user prescribes displacement components on that shared face,\n they apply to the front-face displacements \n\\begin_inset Formula $\\mathbf{u}_{b}$\n\\end_inset\n\n.\n Similarly,\n prescribed pressures and contact pressures act on the shell top face.\n\\end_layout\n\n\\begin_layout Standard\nWhen a shell element is sandwiched between two solid elements,\n the nodal displacements of the solid element facing the shell bottom face are set to coincide with the shell back-face nodal displacements \n\\begin_inset Formula $\\mathbf{w}_{b}$\n\\end_inset\n\n,\n whereas the nodal displacements of the solid element facing the shell top face are set to coincide with the shell front-face nodal displacements \n\\begin_inset Formula $\\mathbf{u}_{a}$\n\\end_inset\n\n.\n If the shell thickness exceeds the thickness of the solid element connected to its bottom face,\n results become unpredictable.\n\\end_layout\n\n\\begin_layout Subsubsection\nElastic Shell\n\\end_layout\n\n\\begin_layout Standard\nFor an elastic solid,\n the internal virtual work is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int} & =\\int_{v}\\boldsymbol{\\sigma}:\\grad\\delta\\mathbf{u}\\,dv\\\\\n & =\\sum_{a}\\left[\\begin{array}{cc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{u}\\\\\n\\mathbf{f}_{a}^{w}\n\\end{array}\\right]\n\\end{aligned}\n\\,,\\label{eq:virtual-work-internal}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{u} & =\\int_{v}\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\right)\\,dv\\\\\n\\mathbf{f}_{a}^{w} & =\\int_{v}\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{a}\\right)\\,dv\n\\end{aligned}\n\\,.\\label{eq:shell-internal-force}\n\\end{equation}\n\n\\end_inset\n\nFor the external work of body forces,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{ext} & =\\int_{v}\\delta\\mathbf{u}\\cdot\\rho\\mathbf{b}\\,dv\\\\\n & =\\sum_{a=1}^{m}\\delta\\mathbf{u}_{a}\\cdot\\mathbf{f}_{a}^{u}+\\delta\\mathbf{w}_{a}\\cdot\\mathbf{f}_{a}^{w}\n\\end{aligned}\n\\,,\\label{eq:virtual-work-external}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{u} & =\\int_{v}\\frac{1+\\xi_{3}}{2}M_{a}\\rho\\mathbf{b}\\,dv\\\\\n\\mathbf{f}_{a}^{w} & =\\int_{v}\\frac{1-\\xi_{3}}{2}M_{a}\\rho\\mathbf{b}\\,dv\n\\end{aligned}\n\\,.\\label{eq:shell-external-force}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the internal virtual work is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{int}\\right)\\left[\\Delta\\mathbf{u}\\right] & =\\int_{v}\\tr\\left(\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad^{T}\\delta\\mathbf{u}\\right)\\,dv\\\\\n & +\\int_{v}\\grad\\delta\\mathbf{u}:\\boldsymbol{\\mathcal{C}}:\\grad^{T}\\Delta\\mathbf{u}\\,dv\n\\end{aligned}\n\\,.\\label{eq:linearized-internal-work}\n\\end{equation}\n\n\\end_inset\n\nSo\n\\begin_inset Formula \n\\begin{equation}\n\\int_{v}\\tr\\left(\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad^{T}\\delta\\mathbf{u}\\right)\\,dv=\\sum_{a}\\sum_{b}\\left[\\begin{array}{cc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a}\\end{array}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{uw}\\\\\n\\mathbf{K}_{ab}^{wu} & \\mathbf{K}_{ab}^{ww}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\mathbf{w}_{b}\n\\end{array}\\right]\\,,\\label{eq:discretized-geometric-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =\\int_{v}\\left(\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)\\right)\\mathbf{I}\\,dv\\\\\n\\mathbf{K}_{ab}^{uw} & =\\int_{v}\\left(\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)\\right)\\mathbf{I}\\,dv\\\\\n\\mathbf{K}_{ab}^{wu} & =\\int_{v}\\left(\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)\\right)\\mathbf{I}\\,dv\\\\\n\\mathbf{K}_{ab}^{ww} & =\\int_{v}\\left(\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\sigma}\\cdot\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)\\right)\\mathbf{I}\\,dv\n\\end{aligned}\n\\,.\\label{eq:shell-geometric-stiffness}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\begin{equation}\n\\int_{v}\\grad\\delta\\mathbf{u}:\\boldsymbol{\\mathcal{C}}:\\grad^{T}\\Delta\\mathbf{u}\\,dv=\\sum_{a}\\sum_{b}\\left[\\begin{array}{cc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a}\\end{array}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{uw}\\\\\n\\mathbf{K}_{ab}^{wu} & \\mathbf{K}_{ab}^{ww}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\mathbf{w}_{b}\n\\end{array}\\right]\\,,\\label{eq:discretized-material-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =\\int_{v}\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)\\,dv\\\\\n\\mathbf{K}_{ab}^{uw} & =\\int_{v}\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)\\,dv\\\\\n\\mathbf{K}_{ab}^{wu} & =\\int_{v}\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad\\left(\\frac{1+\\xi_{3}}{2}M_{b}\\right)\\,dv\\\\\n\\mathbf{K}_{ab}^{ww} & =\\int_{v}\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{a}\\right)\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad\\left(\\frac{1-\\xi_{3}}{2}M_{b}\\right)\\,dv\n\\end{aligned}\n\\,.\\label{eq:shell-material-stiffness}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the external work is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left(\\delta W_{ext}\\right) & =\\sum_{a=1}^{m}\\sum_{b=1}^{m}\\int_{v}\\left(\\frac{1+\\xi_{3}}{2}M_{a}\\delta\\mathbf{u}_{a}+\\frac{1-\\xi_{3}}{2}M_{a}\\delta\\mathbf{w}_{a}\\right)\\cdot\\rho\\grad\\mathbf{b}\\cdot\\left(\\frac{1+\\xi_{3}}{2}\\Delta\\mathbf{u}_{b}+\\frac{1-\\xi_{3}}{2}M_{b}\\Delta\\mathbf{w}_{b}\\right)\\,dv\\\\\n & =\\sum_{a}\\sum_{b}\\left[\\begin{array}{cc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a}\\end{array}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{uw}\\\\\n\\mathbf{K}_{ab}^{wu} & \\mathbf{K}_{ab}^{ww}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\mathbf{w}_{b}\n\\end{array}\\right]\n\\end{aligned}\n\\,,\\label{eq:linearized-external-work}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ab}^{uu} & =\\int_{V}\\left(\\frac{1+\\xi_{3}}{2}\\right)^{2}M_{a}M_{b}\\rho_{0}\\grad\\mathbf{b}\\,dV\\\\\n\\mathbf{K}_{ab}^{uw} & =\\int_{V}\\left(\\frac{1+\\xi_{3}}{2}\\right)\\left(\\frac{1-\\xi_{3}}{2}\\right)M_{a}M_{b}\\rho_{0}\\grad\\mathbf{b}\\,dV\\\\\n\\mathbf{K}_{ab}^{wu} & =\\int_{V}\\left(\\frac{1+\\xi_{3}}{2}\\right)\\left(\\frac{1-\\xi_{3}}{2}\\right)M_{a}M_{b}\\rho_{0}\\grad\\mathbf{b}\\,dV\\\\\n\\mathbf{K}_{ab}^{ww} & =\\int_{V}\\left(\\frac{1-\\xi_{3}}{2}\\right)^{2}M_{a}M_{b}\\rho_{0}\\grad\\mathbf{b}\\,dV\n\\end{aligned}\n\\,.\\label{eq:shell-external-stiffness}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nExternal work of surface forces\n\\end_layout\n\n\\begin_layout Standard\nWe assume that surface forces are applied on the shell top face (\n\\begin_inset Formula $\\xi_{3}=+1$\n\\end_inset\n\n).\n Therefore,\n the external work of surface forces has the form\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{ext}=\\int_{\\partial v}\\delta\\mathbf{u}\\left(\\xi_{1},\\xi_{2},+1\\right)\\cdot\\mathbf{t}\\,da=\\sum_{a}\\delta\\mathbf{u}_{a}\\cdot\\int_{\\partial v}M_{a}\\left(\\xi_{1},\\xi_{2}\\right)\\mathbf{t}\\,da\\label{eq:fbs-ext-work}\n\\end{equation}\n\n\\end_inset\n\nIn other words,\n the treatment of surface forces on a shell becomes identical to the treatment of surface forces on the face of a solid.\n No special treatment is needed.\n\\end_layout\n\n\\begin_layout Subsubsection\nShell on top of solid element\n\\end_layout\n\n\\begin_layout Standard\nWhen a shell is coincident with the face of a solid element,\n we assume that the face of the solid element coincides with the bottom face (\n\\begin_inset Formula $\\xi_{3}=-1$\n\\end_inset\n\n) of the shell element.\n This means that the solid element nodal displacements \n\\begin_inset Formula $\\mathbf{u}_{b}$\n\\end_inset\n\n on that face coincide with the shell nodal displacements \n\\begin_inset Formula $\\mathbf{w}_{b}$\n\\end_inset\n\n.\n Therefore,\n when we use UnpackLM for those solid elements,\n we should reassign the DOF ID's of the \n\\begin_inset Formula $\\mathbf{u}_{b}$\n\\end_inset\n\n displacements to those of the \n\\begin_inset Formula $\\mathbf{w}_{b}$\n\\end_inset\n\n displacements stored in that same node.\n\\end_layout\n\n\\begin_layout Subsubsection\nShell sandwiched between solid elements\n\\end_layout\n\n\\begin_layout Standard\nWhen a shell is sandwiched between two solid elements,\n we reassign the DOF ID's of the the solid \n\\begin_inset Formula $\\mathbf{u}_{b}$\n\\end_inset\n\n displacements facing the bottom of the shell to those of the shell \n\\begin_inset Formula $\\mathbf{w}_{b}$\n\\end_inset\n\n displacements stored in that same node.\n The DOF ID's of solid \n\\begin_inset Formula $\\mathbf{u}_{b}$\n\\end_inset\n\n displacements facing the top of the shell remain unchanged;\n they will coincide with those of the corresponding solid element nodes.\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid-Shell Interface\n\\end_layout\n\n\\begin_layout Standard\nWhen the node of a deformable shell belongs to a rigid body,\n we need to substitute the nodal degrees of freedom with the rigid body degrees of freedom.\n The positions of the shell top face and bottom face nodes are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{x}_{b} & =\\mathbf{r}+\\boldsymbol{\\Lambda}\\cdot\\left(\\mathbf{X}_{b}-\\mathbf{R}\\right)\\equiv\\mathbf{r}+\\mathbf{a}_{b}\\\\\n\\mathbf{y}_{b} & =\\mathbf{r}+\\boldsymbol{\\Lambda}\\cdot\\left(\\mathbf{Y}_{b}-\\mathbf{R}\\right)\\equiv\\mathbf{r}+\\mathbf{b}_{b}\n\\end{aligned}\n\\,,\\label{eq:fbs-rigid-shell}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{r}$\n\\end_inset\n\n is the current position of the rigid body center of mass and \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n is its initial position;\n \n\\begin_inset Formula $\\boldsymbol{\\Lambda}$\n\\end_inset\n\n is the rotation tensor for the rigid body.\n We assume that \n\\begin_inset Formula $\\mathbf{x}_{b}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{y}_{b}$\n\\end_inset\n\n are connected to the same rigid body.\n From these relations it follows that virtual displacements are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{u}_{a} & =\\delta\\mathbf{r}-\\hat{\\mathbf{a}}_{a}\\cdot\\delta\\boldsymbol{\\theta}\\\\\n\\delta\\mathbf{w}_{b} & =\\delta\\mathbf{r}-\\hat{\\mathbf{b}}_{a}\\cdot\\delta\\boldsymbol{\\theta}\n\\end{aligned}\n\\,,\\label{eq:fbs-rsi-virtual}\n\\end{equation}\n\n\\end_inset\n\nand incremental displacements are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\Delta\\mathbf{u}_{b} & =\\Delta\\mathbf{r}-\\hat{\\mathbf{a}}_{b}\\cdot\\Delta\\boldsymbol{\\theta}\\\\\n\\Delta\\mathbf{w}_{b} & =\\Delta\\mathbf{r}-\\hat{\\mathbf{b}}_{b}\\cdot\\Delta\\boldsymbol{\\theta}\n\\end{aligned}\n\\,,\\label{eq:fbs-rsi-incremental}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\mathbf{a}}$\n\\end_inset\n\n is the skew-symmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $\\hat{\\mathbf{a}}\\cdot\\mathbf{v}=\\mathbf{a}\\times\\mathbf{v}$\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n.\n When nodes are flexible (when they do not belong to any rigid body),\n the virtual work has the general form\n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\sum_{a=1}^{m}\\delta\\mathbf{u}_{a}\\cdot\\mathbf{f}_{a}^{u}+\\delta\\mathbf{w}_{a}\\cdot\\mathbf{f}_{a}^{w}+\\delta p_{a}\\,f_{a}^{p}=\\sum_{a=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a} & \\delta\\mathbf{w}_{a} & \\delta p_{a}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{u}\\\\\n\\mathbf{f}_{a}^{w}\\\\\nf_{a}^{p}\n\\end{array}\\right]\\,,\\label{eq:fbs-rsi-work}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n denotes any additional degree-of-freedom at that node.\n If node \n\\begin_inset Formula $a$\n\\end_inset\n\n is rigid we get\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\begin{array}{ccc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a} & \\delta p_{a}\\end{array}\\right]=\\left[\\begin{array}{ccc}\n\\delta\\mathbf{r} & \\delta\\boldsymbol{\\theta} & \\delta p_{a}\\end{array}\\right]\\left[\\begin{array}{ccc}\n\\mathbf{I} & \\mathbf{I} & \\mathbf{0}\\\\\n\\hat{\\mathbf{a}}_{a} & \\hat{\\mathbf{b}}_{a} & \\mathbf{0}\\\\\n\\mathbf{0} & \\mathbf{0} & 1\n\\end{array}\\right]\\,.\\label{eq:fbs-rsi-a-rigid}\n\\end{equation}\n\n\\end_inset\n\nIf node \n\\begin_inset Formula $b$\n\\end_inset\n\n is rigid we get\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\mathbf{w}_{b}\\\\\n\\Delta p_{b}\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\n\\mathbf{I} & -\\hat{\\mathbf{a}}_{b} & \\mathbf{0}\\\\\n\\mathbf{I} & -\\hat{\\mathbf{b}}_{b} & \\mathbf{0}\\\\\n\\mathbf{0} & \\mathbf{0} & 1\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}\\\\\n\\Delta\\boldsymbol{\\theta}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\\,.\\label{eq:fbs-rsi-b-rigid}\n\\end{equation}\n\n\\end_inset\n\nWhen node \n\\begin_inset Formula $a$\n\\end_inset\n\n belongs to a rigid body,\n the expression for \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n must be substituted with\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W & =\\sum_{a=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a} & \\delta p_{a}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{u}\\\\\n\\mathbf{f}_{a}^{w}\\\\\nf_{a}^{p}\n\\end{array}\\right]\\\\\n & =\\sum_{a=1}^{m}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{r} & \\delta\\boldsymbol{\\omega} & \\delta p_{a}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{u}+\\mathbf{f}_{a}^{w}\\\\\n\\hat{\\mathbf{a}}_{a}^{n+\\alpha}\\cdot\\mathbf{f}_{a}^{u}+\\hat{\\mathbf{b}}_{a}^{n+\\alpha}\\cdot\\mathbf{f}_{a}^{w}\\\\\nf_{a}^{p}\n\\end{array}\\right]\n\\end{aligned}\n\\,.\\label{eq:fbs-rsi-work-2}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n the linearized virtual work has the general form\n\\begin_inset Formula \n\\begin{equation}\nD\\delta W=\\sum_{a}\\sum_{b}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a} & \\delta p_{a}\\end{array}\\right]\\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu} & \\mathbf{K}_{ab}^{uw} & \\mathbf{k}_{ab}^{up}\\\\\n\\mathbf{K}_{ab}^{wu} & \\mathbf{K}_{ab}^{ww} & \\mathbf{k}_{ab}^{wp}\\\\\n\\mathbf{k}_{ab}^{pu} & \\mathbf{k}_{ab}^{pw} & k_{ab}^{pp}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\mathbf{w}_{b}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\\,.\\label{eq:fbs-rsi-D-work}\n\\end{equation}\n\n\\end_inset\n\nWhen node \n\\begin_inset Formula $a$\n\\end_inset\n\n is rigid but node \n\\begin_inset Formula $b$\n\\end_inset\n\n is not,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W & =\\sum_{a}\\sum_{b}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{r} & \\delta\\boldsymbol{\\theta} & \\delta p_{a}\\end{array}\\right]\\times\\\\\n & \\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu}+\\mathbf{K}_{ab}^{wu} & \\mathbf{K}_{ab}^{uw}+\\mathbf{K}_{ab}^{ww} & \\mathbf{k}_{ab}^{up}+\\mathbf{k}_{ab}^{wp}\\\\\n\\hat{\\mathbf{a}}_{a}\\cdot\\mathbf{K}_{ab}^{uu}+\\hat{\\mathbf{b}}_{a}\\cdot\\mathbf{K}_{ab}^{wu} & \\hat{\\mathbf{a}}_{a}\\cdot\\mathbf{K}_{ab}^{uw}+\\hat{\\mathbf{b}}_{a}\\cdot\\mathbf{K}_{ab}^{ww} & \\hat{\\mathbf{a}}_{a}\\cdot\\mathbf{k}_{ab}^{up}+\\hat{\\mathbf{b}}_{a}\\cdot\\mathbf{k}_{ab}^{wp}\\\\\n\\mathbf{k}_{ab}^{pu} & \\mathbf{k}_{ab}^{pw} & k_{ab}^{pp}\n\\end{array}\\right]\\\\\n & \\times\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{b}\\\\\n\\Delta\\mathbf{w}_{b}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\n\\end{aligned}\n\\,.\\label{eq:fbs-rsi-D-work-2}\n\\end{equation}\n\n\\end_inset\n\nIf nodes \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n are both rigid,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W & =\\sum_{a}\\sum_{b}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{r} & \\delta\\boldsymbol{\\theta} & \\delta p_{a}\\end{array}\\right]\\times\\\\\n & \\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu}+\\mathbf{K}_{ab}^{wu}+\\mathbf{K}_{ab}^{uw}+\\mathbf{K}_{ab}^{ww} & \\left(\\begin{aligned}-\\left(\\mathbf{K}_{ab}^{uu}+\\mathbf{K}_{ab}^{wu}\\right)\\cdot\\hat{\\mathbf{a}}_{b}\\\\\n-\\left(\\mathbf{K}_{ab}^{uw}+\\mathbf{K}_{ab}^{ww}\\right)\\cdot\\hat{\\mathbf{b}}_{b}\n\\end{aligned}\n\\right) & \\mathbf{k}_{ab}^{up}+\\mathbf{k}_{ab}^{wp}\\\\\n\\left(\\begin{aligned}\\hat{\\mathbf{a}}_{a}\\cdot\\left(\\mathbf{K}_{ab}^{uu}+\\mathbf{K}_{ab}^{uw}\\right)\\\\\n+\\hat{\\mathbf{b}}_{a}\\cdot\\left(\\mathbf{K}_{ab}^{wu}+\\mathbf{K}_{ab}^{ww}\\right)\n\\end{aligned}\n\\right) & \\left(\\begin{aligned}-\\left(\\hat{\\mathbf{a}}_{a}\\cdot\\mathbf{K}_{ab}^{uu}+\\hat{\\mathbf{b}}_{a}\\cdot\\mathbf{K}_{ab}^{wu}\\right)\\cdot\\hat{\\mathbf{a}}_{b}\\\\\n-\\left(\\hat{\\mathbf{a}}_{a}\\cdot\\mathbf{K}_{ab}^{uw}+\\hat{\\mathbf{b}}_{a}\\cdot\\mathbf{K}_{ab}^{ww}\\right)\\cdot\\hat{\\mathbf{b}}_{b}\n\\end{aligned}\n\\right) & \\hat{\\mathbf{a}}_{a}\\cdot\\mathbf{k}_{ab}^{up}+\\hat{\\mathbf{b}}_{a}\\cdot\\mathbf{k}_{ab}^{wp}\\\\\n\\mathbf{k}_{ab}^{pu}+\\mathbf{k}_{ab}^{pw} & \\hat{\\mathbf{a}}_{b}\\cdot\\mathbf{k}_{ab}^{pu}+\\hat{\\mathbf{b}}_{b}\\cdot\\mathbf{k}_{ab}^{pw} & k_{ab}^{pp}\n\\end{array}\\right]\\\\\n & \\times\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}\\\\\n\\Delta\\boldsymbol{\\theta}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\n\\end{aligned}\n\\,.\\label{eq:fbs-rsi-D-work-3}\n\\end{equation}\n\n\\end_inset\n\nIf node \n\\begin_inset Formula $a$\n\\end_inset\n\n is not rigid and node \n\\begin_inset Formula $b$\n\\end_inset\n\n is rigid,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta W & =\\sum_{a}\\sum_{b}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{u}_{a} & \\delta\\mathbf{w}_{a} & \\delta p_{a}\\end{array}\\right]\\times\\\\\n & \\left[\\begin{array}{ccc}\n\\mathbf{K}_{ab}^{uu}+\\mathbf{K}_{ab}^{wu} & -\\mathbf{K}_{ab}^{uu}\\cdot\\hat{\\mathbf{a}}_{b}-\\mathbf{K}_{ab}^{uw}\\cdot\\hat{\\mathbf{b}}_{b} & \\mathbf{k}_{ab}^{up}\\\\\n\\mathbf{K}_{ab}^{wu}+\\mathbf{K}_{ab}^{ww} & -\\mathbf{K}_{ab}^{wu}\\cdot\\hat{\\mathbf{a}}_{b}-\\mathbf{K}_{ab}^{ww}\\cdot\\hat{\\mathbf{b}}_{b} & \\mathbf{k}_{ab}^{wp}\\\\\n\\mathbf{k}_{ab}^{pu}+\\mathbf{k}_{ab}^{pw} & \\hat{\\mathbf{a}}_{b}\\cdot\\mathbf{k}_{ab}^{pu}+\\hat{\\mathbf{b}}_{b}\\cdot\\mathbf{k}_{ab}^{pw} & k_{ab}^{pp}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}\\\\\n\\Delta\\boldsymbol{\\theta}\\\\\n\\Delta p_{b}\n\\end{array}\\right]\n\\end{aligned}\n\\,.\\label{eq:fbs-rsi-D-work-4}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nConstitutive Models\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Constitutive-Models\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis chapter describes the theoretical background behind the constitutive models that are available in FEBio.\n Most materials are derived from a hyperelastic strain-energy function.\n Please consult Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Hyperelasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more background information on this class of materials.\n\\end_layout\n\n\\begin_layout Section\nLinear Elasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Linear-Elasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the theory of linear elasticity the Cauchy stress tensor is a linear function of the small strain tensor \n\\begin_inset Formula $\\boldsymbol{\\varepsilon}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\boldsymbol{\\mathcal{C}}:\\boldsymbol{\\varepsilon}\\,.\\label{eq402}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the fourth-order elasticity tensor that contains the material properties.\n In the most general case this tensor has 21 independent parameters.\n However,\n in the presence of material symmetry the number of independent parameters is greatly reduced.\n For example,\n in the case of isotropic linear elasticity only two independent parameters remain.\n In this case,\n the elasticity tensor is given by \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}=\\lambda\\mathbf{I}\\otimes\\mathbf{I}+2\\mu\\mathbf{I}\\,\\overline{\\underline{\\otimes}}\\,\\mathbf{I}$\n\\end_inset\n\n,\n or equivalently,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{C}_{ijkl}=\\lambda\\delta_{ij}\\delta_{kl}+\\mu\\left(\\delta_{ik}\\delta_{jl}+\\delta_{il}\\delta_{jk}\\right)\\,.\\label{eq403}\n\\end{equation}\n\n\\end_inset\n\nThe material coefficients \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are known as the Lamé parameters.\n Using this equation,\n the stress-strain relationship can be written as \n\\begin_inset Formula \n\\begin{equation}\n\\sigma_{ij}=\\lambda\\varepsilon_{kk}\\delta_{ij}+2\\mu\\varepsilon_{ij}\\,.\\label{eq404}\n\\end{equation}\n\n\\end_inset\n\nIf the stress and strain are represented inVoigt notation,\n the constitutive equation can be rewritten in matrix form as \n\\begin_inset Formula \n\\begin{equation}\n\\left[\\begin{array}{c}\n\\sigma_{11}\\\\\n\\sigma_{22}\\\\\n\\sigma_{33}\\\\\n\\sigma_{12}\\\\\n\\sigma_{23}\\\\\n\\sigma_{13}\n\\end{array}\\right]=\\left[\\begin{array}{cccccc}\n\\lambda+2\\mu & \\lambda & \\lambda & 0 & 0 & 0\\\\\n\\lambda & \\lambda+2\\mu & \\lambda & 0 & 0 & 0\\\\\n\\lambda & \\lambda & \\lambda+2\\mu & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\mu & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\mu & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\mu\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\varepsilon_{11}\\\\\n\\varepsilon_{22}\\\\\n\\varepsilon_{33}\\\\\n\\gamma_{12}\\\\\n\\gamma_{23}\\\\\n\\gamma_{13}\n\\end{array}\\right]\\,.\\label{eq405}\n\\end{equation}\n\n\\end_inset\n\nThe shear strain measures \n\\begin_inset Formula $\\gamma_{ij}=2\\varepsilon_{ij}$\n\\end_inset\n\n are called the \n\\emph on\nengineering strains\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Standard\nThe following table relates the Lamé parameters to the more familiar Young's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n and Poisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n or to the bulk modulus \n\\begin_inset Formula $K$\n\\end_inset\n\n and shear modulus \n\\begin_inset Formula $G$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E,\\nu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda,\\mu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $K,G$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E,\\nu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\begin{array}{l}\nE=\\frac{\\mu}{\\lambda+\\mu}\\left(2\\mu+3\\lambda\\right)\\\\\n\\nu=\\frac{\\lambda}{2\\left(\\lambda+\\mu\\right)}\n\\end{array}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\begin{array}{l}\nE=\\frac{9KG}{3K+G}\\\\\n\\nu=\\frac{3K-2G}{6K+2G}\n\\end{array}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda,\\mu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\begin{array}{l}\n\\lambda=\\frac{\\nu E}{\\left(1+\\nu\\right)\\left(1-2\\nu\\right)}\\\\\n\\mu=\\frac{E}{2\\left(1+\\nu\\right)}\n\\end{array}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\begin{array}{l}\n\\lambda=K-\\frac{2}{3}G\\\\\n\\mu=G\n\\end{array}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $K,G$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\begin{array}{l}\nK=\\frac{E}{3\\left(1-2v\\right)}\\\\\nG=\\frac{E}{2\\left(1+v\\right)}\n\\end{array}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\begin{array}{l}\nK=\\lambda+\\frac{2}{3}\\mu\\\\\nG=\\mu\n\\end{array}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe theoretical range of the Young's modulus and Poisson's ratio for an isotropic material have the ranges \n\\begin_inset Formula \n\\begin{equation}\n0<E<\\infty\\,,\\label{eq406}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n-1\\leqslant\\nu<0.5\\,.\\label{eq407}\n\\end{equation}\n\n\\end_inset\n\nMaterials with Poisson's ratio (close to) 0.5 are known as (nearly-) incompressible materials.\n For these materials,\n the bulk modulus approaches infinity.\n Most materials have a positive Poisson's ratio.\n However there do exist some materials with a negative ratio.\n These materials are known as \n\\emph on\nauxetic\n\\emph default\n materials and they have the remarkable property that they expand under tension.\n\\end_layout\n\n\\begin_layout Standard\nThe linear stress-strain relationship can also be derived from a strain-energy function such as in the case of hyperelastic materials.\n In this case the linear strain-energy is given by \n\\begin_inset Formula \n\\begin{equation}\nW=\\frac{1}{2}\\boldsymbol{\\varepsilon}:\\boldsymbol{\\mathcal{C}}:\\boldsymbol{\\varepsilon}\\,.\\label{eq408}\n\\end{equation}\n\n\\end_inset\n\nThe stress is then similarly derived from \n\\begin_inset Formula $\\boldsymbol{\\sigma}=\\frac{\\partial W}{\\partial\\boldsymbol{\\mathbf{\\varepsilon}}}$\n\\end_inset\n\n.\n In the case of isotropic elasticity,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq408\"\nnolink \"false\"\n\n\\end_inset\n\n can be simplified:\n \n\\begin_inset Formula \n\\begin{equation}\nW=\\frac{1}{2}\\lambda\\left(\\tr\\boldsymbol{\\mathbf{\\varepsilon}}\\right)^{2}+\\mu\\boldsymbol{\\mathbf{\\varepsilon}}:\\boldsymbol{\\mathbf{\\varepsilon}}\\,.\\label{eq409}\n\\end{equation}\n\n\\end_inset\n\nThe Cauchy stress is now given in tensor form by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\lambda\\left(\\tr\\boldsymbol{\\varepsilon}\\right)\\mathbf{I}+2\\mu\\boldsymbol{\\varepsilon}\\,.\\label{eq410}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nCompressible Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Compressible-materials\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nIsotropic Elasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Isotropic-Elasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linear elastic material model as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Linear-Elasticity\"\nnolink \"false\"\n\n\\end_inset\n\n is only valid for small strains and small rotations.\n A first modification to this model to the range of nonlinear deformations is given by the St.\n Venant-Kirchhoff model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n,\n which in FEBio is referred to as \n\\emph on\nisotropic elasticity\n\\emph default\n.\n This model is objective for large strains and rotations.\n For the isotropic case it can be derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\begin{equation}\nW=\\frac{1}{2}\\lambda\\left(\\tr\\mathbf{E}\\right)^{2}+\\mu\\mathbf{E}:\\mathbf{E}\\,.\\label{eq411}\n\\end{equation}\n\n\\end_inset\n\nThe second Piola-Kirchhoff stress can be derived from this:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=\\frac{\\partial W}{\\partial\\mathbf{E}}=\\lambda\\left(\\tr\\mathbf{E}\\right)\\mathbf{I}+2\\mu\\mathbf{E}\\,.\\label{eq412}\n\\end{equation}\n\n\\end_inset\n\nNote that these equations are similar to the corresponding equations in the linear elastic case,\n except that the small strain tensor is replaced by the Green-Lagrange elasticity tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n\n\\series default\n.\n The material elasticity tensor is then given by,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathbb{C}}=\\frac{\\partial\\mathbf{S}}{\\partial\\mathbf{E}}=\\lambda\\mathbf{I}\\otimes\\mathbf{I}+2\\mu\\mathbf{I}\\odot\\mathbf{I}\\,.\\label{eq413}\n\\end{equation}\n\n\\end_inset\n\nIt is important to note that although this model is objective,\n it should only be used for small strains.\n For large strains,\n the response can be somewhat strange if not completely unrealistic.\n For example,\n it can be shown that under uni-axial tension the stress becomes infinite and the volume tends to zero for finite strains.\n Therefore,\n for large strains it is highly recommended to avoid this material and instead use one of the other non-linear material models described below.\n The Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{1}{J}\\left(\\lambda\\tr\\mathbf{E}-\\mu\\right)\\mathbf{b}+\\frac{\\mu}{J}\\mathbf{b}^{2}\\,,\\label{eq414}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tr\\mathbf{E}=\\left(\\tr\\mathbf{b}-3\\right)/2$\n\\end_inset\n\n ,\n whereas the spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\frac{\\lambda}{J}\\mathbf{b}\\otimes\\mathbf{b}+\\frac{2}{J}\\mu\\mathbf{b}\\odot\\mathbf{b}\\,.\\label{eq415}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOrthotropic Elasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Orthotropic-Elasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAn extension of the St.\n Venant-Kirchhoff model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n to orthotropic symmetry is provided in FEBio,\n referred to as \n\\emph on\northotropic elasticity\n\\emph default\n.\n This model is objective for large strains and rotations.\n It can be derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\begin{equation}\nW=\\sum\\limits_{a=1}^{3}\\mu_{a}\\mathbf{A}_{a}^{0}:\\mathbf{E}^{2}+\\frac{1}{2}\\sum\\limits_{b=1}^{3}\\lambda_{ab}\\left(\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right)\\left(\\mathbf{A}_{b}^{0}:\\mathbf{E}\\right)\\,,\\label{eq416}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{A}_{a}^{0}=\\mathbf{a}_{a}^{0}\\otimes\\mathbf{a}_{a}^{0}$\n\\end_inset\n\n is the structural tensor corresponding to one of the three mutually orthogonal planes of symmetry whose unit outward normal is \n\\begin_inset Formula $\\mathbf{a}_{a}^{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\mathbf{a}_{a}^{0}\\cdot\\mathbf{a}_{b}^{0}=\\delta_{ab}$\n\\end_inset\n\n).\n The material constants are the three shear moduli \n\\begin_inset Formula $\\mu_{a}$\n\\end_inset\n\n and six moduli \n\\begin_inset Formula $\\lambda_{ab}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\lambda_{ba}=\\lambda_{ab}$\n\\end_inset\n\n.\n They may be related to the Young's moduli \n\\begin_inset Formula $E_{a}$\n\\end_inset\n\n,\n shear moduli \n\\begin_inset Formula $G_{ab}$\n\\end_inset\n\n and Poisson's ratios \n\\begin_inset Formula $\\nu_{ab}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\begin{equation}\n\\begin{array}{l}\n\\left[\\begin{array}{cccccc}\n\\lambda_{11}+2\\mu_{1} & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0\\\\\n\\lambda_{12} & \\lambda_{22}+2\\mu_{2} & \\lambda_{23} & 0 & 0 & 0\\\\\n\\lambda_{13} & \\lambda_{23} & \\lambda_{33}+2\\mu_{3} & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\left(\\mu_{1}+\\mu_{2}\\right)/2 & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\left(\\mu_{2}+\\mu_{3}\\right)/2 & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\left(\\mu_{3}+\\mu_{1}\\right)/2\n\\end{array}\\right]^{-1}=\\\\\n\\left[\\begin{array}{cccccc}\n1/E_{1} & -\\nu_{12}/E_{1} & -\\nu_{13}/E_{1} & 0 & 0 & 0\\\\\n-\\nu_{21}/E_{2} & 1/E_{2} & -\\nu_{23}/E_{2} & 0 & 0 & 0\\\\\n-\\nu_{31}/E_{3} & -\\nu_{32}/E_{3} & 1/E_{3} & 0 & 0 & 0\\\\\n0 & 0 & 0 & 1/G_{12} & 0 & 0\\\\\n0 & 0 & 0 & 0 & 1/G_{23} & 0\\\\\n0 & 0 & 0 & 0 & 0 & 1/G_{31}\n\\end{array}\\right]\\,.\n\\end{array}\\label{eq417}\n\\end{equation}\n\n\\end_inset\n\nThe second Piola-Kirchhoff stress can be derived from this strain energy density function:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{S} & =\\frac{\\partial W}{\\partial\\mathbf{E}}=\\sum\\limits_{a=1}^{3}\\mu_{a}\\left(\\mathbf{A}_{a}^{0}\\cdot\\mathbf{E}+\\mathbf{E}\\cdot\\mathbf{A}_{a}^{0}\\right)\\\\\n & +\\frac{1}{2}\\sum\\limits_{b=1}^{3}\\lambda_{ab}\\left[\\left(\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right)\\mathbf{A}_{b}^{0}+\\left(\\mathbf{A}_{b}^{0}:\\mathbf{E}\\right)\\mathbf{A}_{a}^{0}\\right]\\,.\n\\end{aligned}\n\\label{eq418}\n\\end{equation}\n\n\\end_inset\n\nNote that these equations are similar to the corresponding equations in the linear orthotropic elastic case,\n except that the small strain tensor is replaced by the Green-Lagrange elasticity tensor \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n.\n The material elasticity tensor is then given by,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathbb{C}}=\\frac{\\partial\\mathbf{S}}{\\partial\\mathbf{E}}=\\sum\\limits_{a=1}^{3}\\mu_{a}\\left(\\mathbf{A}_{a}^{0}\\odot\\mathbf{I}+\\mathbf{I}\\odot\\mathbf{A}_{a}^{0}\\right)+\\frac{1}{2}\\sum\\limits_{b=1}^{3}\\lambda_{ab}\\left(\\mathbf{A}_{a}^{0}\\otimes\\mathbf{A}_{b}^{0}+\\mathbf{A}_{b}^{0}\\otimes\\mathbf{A}_{a}^{0}\\right)\\,.\\label{eq419}\n\\end{equation}\n\n\\end_inset\n\nIt is important to note that although this model is objective,\n it should only be used for small strains.\n For large strains,\n the response can be somewhat strange if not completely unrealistic.\n For example,\n it can be shown that under uni-axial tension the stress becomes infinite and the volume tends to zero for finite strains.\n Therefore,\n for large strains it is highly recommended to avoid this material and instead use one of the other non-linear material models described below.\n The Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\sigma} & =\\sum\\limits_{a=1}^{3}\\frac{\\mu_{a}}{2J}\\left(\\mathbf{A}_{a}\\cdot\\left(\\mathbf{b}-\\mathbf{I}\\right)+\\left(\\mathbf{b}-\\mathbf{I}\\right)\\cdot\\mathbf{A}_{a}\\right)\\\\\n & +\\frac{1}{2}\\sum\\limits_{b=1}^{3}\\frac{\\lambda_{ab}}{2J}\\left[\\left(\\mathbf{A}_{a}:\\mathbf{I}-1\\right)\\mathbf{A}_{b}+\\left(\\mathbf{A}_{b}:\\mathbf{I}-1\\right)\\mathbf{A}_{a}\\right]\\,,\n\\end{aligned}\n\\label{eq420}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{A}_{a}=\\mathbf{F}\\cdot\\mathbf{A}_{a}^{0}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n and the spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\sum\\limits_{a=1}^{3}\\frac{\\mu_{a}}{J}\\left(\\mathbf{A}_{a}\\odot\\mathbf{b}+\\mathbf{b}\\odot\\mathbf{A}_{a}\\right)+\\frac{1}{2}\\sum\\limits_{b=1}^{3}\\frac{\\lambda_{ab}}{J}\\left({\\mathbf{A}_{a}\\otimes\\mathbf{A}_{b}+\\mathbf{A}_{b}\\otimes\\mathbf{A}_{a}}\\right)\\,.\\label{eq421}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nNeo-Hookean Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Neo-Hookean-Hyperelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis is a compressible neo-Hookean material.\n It is derived from the following hyperelastic strain energy function \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\nW=\\frac{\\mu}{2}\\left(I_{1}-3\\right)-\\mu\\ln J+\\frac{\\lambda}{2}\\left(\\ln J\\right)^{2}\\,.\\label{eq422}\n\\end{equation}\n\n\\end_inset\n\nThe parameters \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n are the Lamé parameters from linear elasticity.\n This model reduces to the isotropic linear elastic model for small strains and rotations.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress is given by,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{\\mu}{J}\\left(\\mathbf{b}-\\mathbf{I}\\right)+\\frac{\\lambda}{J}\\left(\\ln J\\right)\\mathbf{I}\\,,\\label{eq423}\n\\end{equation}\n\n\\end_inset\n\nand the spatial elasticity tensor is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\frac{\\lambda}{J}\\mathbf{I}\\otimes\\mathbf{I}+\\frac{2}{J}\\left(\\mu-\\lambda\\ln J\\right)\\mathbf{I}\\odot\\mathbf{I}\\,.\\label{eq424}\n\\end{equation}\n\n\\end_inset\n\nThe neo-Hookean material is an extension of Hooke's law for the case of large deformations.\n It is useable for certain plastics and rubber-like substances.\n A generalization of this model is the Mooney-Rivlin material,\n which is often used to describe the elastic response of biological tissue.\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio this constitutive model uses a standard displacement-based element formulation and a \"coupled\" strain energy,\n so care must be taken when modeling materials with nearly-incompressible material behavior to avoid element locking.\n\\end_layout\n\n\\begin_layout Subsection\nNatural Neo-Hookean\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Natural-Neo-Hookean\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis is a compressible isotropic neo-Hookean material that uses the natural (Hencky) strain tensor invariants to formulate its strain energy density.\n These invariants are reviewed in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Criscione00\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The left Hencky strain is evaluated from \n\\begin_inset Formula $\\boldsymbol{\\eta}=\\ln\\mathbf{V}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is the left stretch tensor in the polar decomposition of the deformation gradient \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{V}\\cdot\\mathbf{R}$\n\\end_inset\n\n.\n To evaluate \n\\begin_inset Formula $\\boldsymbol{\\eta}$\n\\end_inset\n\n we first evaluate the left Cauchy-Green tensor \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{V}^{2}$\n\\end_inset\n\n from \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n as in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:left-Cauchy-Green\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and get its eigenvalues \n\\begin_inset Formula $\\lambda_{i}^{2}$\n\\end_inset\n\n and eigenvectors \n\\begin_inset Formula $\\mathbf{n}_{i}$\n\\end_inset\n\n.\n Then\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\eta}=\\sum_{i=1}^{3}\\left(\\ln\\lambda_{i}\\right)\\mathbf{n}_{i}\\otimes\\mathbf{n}_{i}\\,.\\label{eq:left-Hencky}\n\\end{equation}\n\n\\end_inset\n\nThe invariants \n\\begin_inset Formula $K_{i}$\n\\end_inset\n\n of the natural strain tensor are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}K_{1} & =\\tr\\boldsymbol{\\eta}=\\ln J & \\text{amount of dilatation}\\\\\nK_{2} & =\\left|\\dev\\boldsymbol{\\eta}\\right|=\\sqrt{\\dev\\boldsymbol{\\eta}:\\dev\\boldsymbol{\\eta}} & \\text{amount of distortion}\\\\\nK_{3} & =3\\sqrt{6}\\det\\boldsymbol{\\Phi} & \\text{mode of distortion}\n\\end{aligned}\n\\label{eq:Hencky-isotropic-invariants}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n as usual,\n and\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Phi}=\\frac{1}{K_{2}}\\dev\\boldsymbol{\\eta}\\,.\\label{eq:Hencky-3rd-basis}\n\\end{equation}\n\n\\end_inset\n\nIt can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\eta}=\\frac{1}{3}K_{1}\\mathbf{I}+K_{2}\\boldsymbol{\\Phi}\\,.\\label{eq:left-Hencky-redux}\n\\end{equation}\n\n\\end_inset\n\nNote that \n\\begin_inset Formula $K_{2}\\boldsymbol{\\Phi}\\to\\mathbf{0}$\n\\end_inset\n\n as \n\\begin_inset Formula $K_{2}\\to0$\n\\end_inset\n\n.\n It also follows that \n\\begin_inset Formula $\\boldsymbol{\\eta}:\\boldsymbol{\\eta}=\\frac{1}{3}K_{1}^{2}+K_{2}^{2}$\n\\end_inset\n\n.\n As explained in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Criscione00\"\nliteral \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $K_{1}\\in\\left(-\\infty,\\infty\\right)$\n\\end_inset\n\n with positive \n\\begin_inset Formula $K_{1}$\n\\end_inset\n\n implying expansion and negative \n\\begin_inset Formula $K_{1}$\n\\end_inset\n\n implying contraction.\n Similarly,\n \n\\begin_inset Formula $K_{2}\\in[0,\\infty)$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $K_{2}=0$\n\\end_inset\n\n implying no distortion.\n Finally,\n \n\\begin_inset Formula $K_{3}\\in\\left[-1,1\\right]$\n\\end_inset\n\n with \n\\begin_inset Formula $K_{3}=1$\n\\end_inset\n\n representing uniaxial extension,\n \n\\begin_inset Formula $K_{3}=-1$\n\\end_inset\n\n representing uniaxial contraction and \n\\begin_inset Formula $K_{3}=0$\n\\end_inset\n\n representing pure shear.\n\\end_layout\n\n\\begin_layout Standard\nFor the natural neo-Hookean material the strain energy density is\n\\begin_inset Formula \n\\begin{equation}\nW=\\frac{\\kappa}{2}K_{1}^{2}+\\mu K_{2}^{2}\\label{eq:NNH-sed}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n is the material's bulk modulus and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is its shear modulus.\n To evaluate the Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and spatial elasticity tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n,\n we use the framework of isotropic elasticity in principal directions (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Isotropic-Elasticity-Principal\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n This requires us to express \n\\begin_inset Formula $K_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n in terms of the eigenvalues \n\\begin_inset Formula $\\lambda_{i}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}K_{1} & =\\ln\\left(\\lambda_{1}\\lambda_{2}\\lambda_{3}\\right)\\\\\nK_{2} & =\\frac{1}{3}\\sqrt{\\left(\\ln\\frac{\\lambda_{1}^{2}}{\\lambda_{2}\\lambda_{3}}\\right)^{2}+\\left(\\ln\\frac{\\lambda_{2}^{2}}{\\lambda_{3}\\lambda_{1}}\\right)^{2}+\\left(\\ln\\frac{\\lambda_{3}^{2}}{\\lambda_{1}\\lambda_{2}}\\right)^{2}}\n\\end{aligned}\n\\,.\\label{eq:NNH-invariants}\n\\end{equation}\n\n\\end_inset\n\nNow the stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IEPD-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n where,\n based on eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IEPD-principal-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the principal normal stresses are evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\sigma_{i}=\\frac{1}{3J}\\left[\\left(3\\kappa+4\\mu\\right)\\ln\\lambda_{i}+\\left(3\\kappa-2\\mu\\right)\\left(\\ln\\lambda_{j}+\\ln\\lambda_{k}\\right)\\right]\\,,\\label{eq:NNH-principal-stress}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula $i,j,k$\n\\end_inset\n\n forming a permutation over \n\\begin_inset Formula $1,2,3$\n\\end_inset\n\n.\n Similarly,\n the spatial elasticity tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IEPD-elasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n where we substitute\n\\begin_inset Formula \n\\begin{equation}\nJ^{-1}\\lambda_{i}^{2}\\frac{\\partial^{2}W}{\\partial\\lambda_{i}^{2}}-\\sigma_{i}=\\frac{1}{3J}\\left[\\left(3\\kappa+4\\mu\\right)\\left(1-2\\ln\\lambda_{i}\\right)-2\\left(3\\kappa-2\\mu\\right)\\left(\\ln\\lambda_{j}+\\ln\\lambda_{k}\\right)\\right]\\label{eq:NNH-elasticity-coef1}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nJ^{-1}\\lambda_{j}\\lambda_{k}\\frac{\\partial^{2}W}{\\partial\\lambda_{j}\\partial\\lambda_{k}}=\\frac{3\\kappa-2\\mu}{3J}\\,.\\label{eq:NNH-elasticity-coef2}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n in the limiting case when pairs of eigenvalues are repeated,\n we substitute\n\\begin_inset Formula \n\\begin{equation}\n\\lim_{\\lambda_{k}^{2}\\to\\lambda_{j}^{2}}2\\frac{\\lambda_{k}^{2}\\sigma_{j}-\\lambda_{j}^{2}\\sigma_{k}}{\\lambda_{j}^{2}-\\lambda_{k}^{2}}=\\frac{2\\mu}{J}-\\frac{4\\left(3\\kappa+\\mu\\right)\\ln\\lambda_{j}+2\\left(3\\kappa-2\\mu\\right)\\ln\\lambda_{i}}{3J}\\,.\\label{eq:NNH-elasticity-coef3}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOgden Unconstrained\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ogden-Unconstrained\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Ogden unconstrained material is defined using the following hyperelastic strain energy function:\n \n\\begin_inset Formula \n\\begin{equation}\nW\\left(\\lambda_{1},\\lambda_{2},\\lambda_{3}\\right)=\\frac{1}{2}c_{p}\\left(J-1\\right)^{2}+\\sum\\limits_{k=1}^{N}\\frac{c_{k}}{m_{k}^{2}}\\left(\\lambda_{1}^{m_{k}}+\\lambda_{2}^{m_{k}}+\\lambda_{3}^{m_{k}}-3-m_{k}\\ln J\\right)\\,.\\label{eq425}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\lambda_{i}$\n\\end_inset\n\n are the principal stretches and \n\\begin_inset Formula $c_{p}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $c_{k}$\n\\end_inset\n\n and \n\\begin_inset Formula $m_{k}$\n\\end_inset\n\n are material parameters.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress tensor for this material may be obtained using the general formula for isotropic elasticity in principal directions given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IEPD-stress\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with \n\\begin_inset Formula \n\\begin{equation}\n\\sigma_{i}=c_{p}\\left(J-1\\right)+\\sum\\limits_{k=1}^{N}\\frac{1}{J}\\frac{c_{k}}{m_{k}}\\left(\\lambda_{i}^{m_{k}}-1\\right)\\,.\\label{eq426}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n the spatial elasticity tensor is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}} & =\\sum\\limits_{i=1}^{3}\\left(c_{p}+\\sum\\limits_{k=1}^{N}\\frac{1}{J}\\frac{c_{k}}{m_{k}}\\left[\\left(m_{k}-2\\right)\\lambda_{i}^{m_{k}}+2\\right]\\right)\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\\\\n & +\\sum\\limits_{i=1}^{3}\\sum\\limits_{j=i+1}^{3}c_{p}\\left(2J-1\\right)\\left(\\mathbf{a}_{i}\\otimes\\mathbf{a}_{j}+\\mathbf{a}_{j}\\otimes\\mathbf{a}_{i}\\right)\\\\\n & +\\sum\\limits_{i=1}^{3}\\sum\\limits_{j=i+1}^{3}2\\frac{\\lambda_{j}^{2}\\sigma_{i}-\\lambda_{i}^{2}\\sigma_{j}}{\\lambda_{i}^{2}-\\lambda_{j}^{2}}\\left(\\mathbf{a}_{i}\\odot\\mathbf{a}_{j}+\\mathbf{a}_{j}\\odot\\mathbf{a}_{i}\\right)\\,,\n\\end{aligned}\n\\label{eq427}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{a}_{i}=\\mathbf{n}_{i}\\otimes\\mathbf{n}_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}_{i}$\n\\end_inset\n\n are the eigenvectors of \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n.\n In the limit when eigenvalues coincide,\n \n\\begin_inset Formula \n\\begin{equation}\n\\lim\\limits_{\\lambda_{j}\\to\\lambda_{i}}2\\frac{\\sigma_{i}\\lambda_{j}^{2}-\\sigma_{j}\\lambda_{i}^{2}}{\\lambda_{i}^{2}-\\lambda_{j}^{2}}=2c_{p}\\left(1-J\\right)+\\sum\\limits_{k=1}^{N}\\frac{1}{J}\\frac{c_{k}}{m_{k}}\\left[2+\\left(m_{k}-2\\right)\\lambda_{i}^{m_{k}}\\right]\\,.\\label{eq428}\n\\end{equation}\n\n\\end_inset\n\nIn the reference configuration the elasticity tensor reduces to \n\\begin_inset Formula \n\\begin{equation}\n\\left.\\boldsymbol{\\mathcal{C}}\\right|_{\\mathbf{b}=\\mathbf{I}}=c_{p}\\mathbf{I}\\otimes\\mathbf{I}+\\left(\\sum\\limits_{k=1}^{N}c_{k}\\right)\\mathbf{I}\\odot\\mathbf{I}\\,,\\label{eq429}\n\\end{equation}\n\n\\end_inset\n\nwhich has the form of Hooke's law for infinitesimal isotropic elasticity (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Linear-Elasticity\"\nnolink \"false\"\n\n\\end_inset\n\n),\n with equivalent Lamé coefficients \n\\begin_inset Formula $c_{p}\\equiv\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $2\\mu\\equiv\\sum\\nolimits_{k=1}^{N}c_{k}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nHolmes-Mow\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Holmes-Mow\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe coupled hyperelastic strain-energy function for this material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(I_{1},I_{2},J\\right)=\\frac{1}{2}c\\left(e^{Q}-1\\right)\\,,\\label{eq430}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\nand \n\\begin_inset Formula $I_{2}$\n\\end_inset\n\nare the first and second invariants of the right Cauchy-Green tensor and \n\\begin_inset Formula $J$\n\\end_inset\n\nthe jacobian of the deformation.\n Furthermore,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}Q & =\\frac{\\beta}{\\lambda+2\\mu}\\left[\\left(2\\mu-\\lambda\\right)\\left(I_{1}-3\\right)+\\lambda\\left(I_{2}-3\\right)-\\left(\\lambda+2\\mu\\right)\\ln J^{2}\\right]\\,,\\\\\nc & =\\frac{\\lambda+2\\mu}{2\\beta}\\,,\n\\end{aligned}\n\\label{eq431}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are the Lamé parameters.\n The corresponding Cauchy stress tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{1}{2J}e^{Q}\\left(\\left[2\\mu+\\lambda\\left(I_{1}-1\\right)\\right]\\mathbf{b}-\\lambda\\mathbf{b}^{2}-\\left(\\lambda+2\\mu\\right)\\mathbf{I}\\right)\\,,\\label{eq432}\n\\end{equation}\n\n\\end_inset\n\nand the spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\frac{4\\beta}{\\lambda+2\\mu}Je^{-Q}\\boldsymbol{\\sigma}\\otimes\\boldsymbol{\\sigma}+J^{-1}e^{Q}\\left[\\lambda\\left(\\mathbf{b}\\otimes\\mathbf{b}-\\mathbf{b}\\odot\\mathbf{b}\\right)+\\left(\\lambda+2\\mu\\right)\\mathbf{I}\\odot\\mathbf{I}\\right]\\,.\\label{eq433}\n\\end{equation}\n\n\\end_inset\n\nIn the limit as \n\\begin_inset Formula $\\beta\\to0$\n\\end_inset\n\n we find that\n\\begin_inset Formula \n\\begin{equation}\n\\lim_{\\beta\\to0}\\Psi\\left(I_{1},I_{2},J\\right)=\\frac{1}{4}\\left[2\\mu\\left(I_{1}-3\\right)+\\lambda\\left(I_{2}-I_{1}\\right)-\\left(\\lambda+2\\mu\\right)\\ln J^{2}\\right]\\label{eq:433-1}\n\\end{equation}\n\n\\end_inset\n\nin which case\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\frac{1}{2J}\\left(\\left(2\\mu-\\lambda\\right)\\mathbf{b}+\\lambda\\left(I_{1}\\mathbf{b}-\\mathbf{b}^{2}\\right)-\\left(\\lambda+2\\mu\\right)\\left(\\ln J\\right)\\mathbf{I}\\right)\\label{eq:433-2}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nConewise Linear Elasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Conewise-Linear-Elasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nCurnier et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Curnier94\"\nliteral \"true\"\n\n\\end_inset\n\n formulated a model for describing bimodular elastic solids exhibiting orthotropic material symmetry.\n This can be derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\begin{equation}\nW=\\sum\\limits_{a=1}^{3}\\mu_{a}\\mathbf{A}_{a}^{0}:\\mathbf{E}^{2}+\\frac{1}{2}\\lambda_{aa}\\left[\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right]\\left(\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right)+\\sum\\limits_{b=1,\\,b\\ne a}^{3}\\frac{1}{2}\\lambda_{ab}\\left(\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right)\\left(\\mathbf{A}_{b}^{0}:\\mathbf{E}\\right)\\,,\\label{eq434}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{A}_{a}^{0}=\\mathbf{a}_{a}^{0}\\otimes\\mathbf{a}_{a}^{0}$\n\\end_inset\n\n is the structural tensor corresponding to one of the three mutually orthogonal planes of symmetry whose unit outward normal is \n\\begin_inset Formula $\\mathbf{a}_{a}^{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\mathbf{a}_{a}^{0}\\cdot\\mathbf{a}_{b}^{0}=\\delta_{ab})$\n\\end_inset\n\n.\n The bimodular response is described by \n\\begin_inset Formula \n\\begin{equation}\n\\lambda_{aa}\\left[\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right]=\\begin{cases}\n\\lambda_{+aa} & \\mathbf{A}_{a}^{0}:\\mathbf{E}\\geqslant0\\\\\n\\lambda_{-aa} & \\mathbf{A}_{a}^{0}:\\mathbf{E}<0\n\\end{cases}\\,.\\label{eq435}\n\\end{equation}\n\n\\end_inset\n\nThe material constants are the three shear moduli \n\\begin_inset Formula $\\mu_{a}$\n\\end_inset\n\n,\n three tensile moduli \n\\begin_inset Formula $\\lambda_{+aa}$\n\\end_inset\n\n,\n three compressive moduli \n\\begin_inset Formula $\\lambda_{-aa}$\n\\end_inset\n\n,\n and three moduli \n\\begin_inset Formula $\\lambda_{ab}$\n\\end_inset\n\n (\n\\begin_inset Formula $b\\ne a)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\lambda_{ba}=\\lambda_{ab}$\n\\end_inset\n\n.\n The second Piola-Kirchhoff stress can be derived from this strain energy density function:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{S} & =\\frac{\\partial W}{\\partial\\mathbf{E}}=\\sum\\limits_{a=1}^{3}\\mu_{a}\\left(\\mathbf{A}_{a}^{0}\\cdot\\mathbf{E}+\\mathbf{E}\\cdot\\mathbf{A}_{a}^{0}\\right)\\\\\n & +\\lambda_{aa}\\left[\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right]\\left(\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right)\\mathbf{A}_{a}^{0}+\\sum\\limits_{b=1,\\,b\\ne a}^{3}\\lambda_{ab}\\left(\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right)\\mathbf{A}_{b}^{0}\\,.\n\\end{aligned}\n\\label{eq436}\n\\end{equation}\n\n\\end_inset\n\nThe material elasticity tensor is then given by,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & \\boldsymbol{\\mathbb{C}}=\\frac{\\partial\\mathbf{S}}{\\partial\\mathbf{E}}=\\sum\\limits_{a=1}^{3}\\mu_{a}\\left(\\mathbf{A}_{a}^{0}\\odot\\mathbf{I}+\\mathbf{I}\\odot\\mathbf{A}_{a}^{0}\\right)\\\\\n & +\\lambda_{aa}\\left[\\mathbf{A}_{a}^{0}:\\mathbf{E}\\right]\\mathbf{A}_{a}^{0}\\otimes\\mathbf{A}_{a}^{0}+\\sum\\limits_{b=1,\\,b\\ne a}^{3}\\lambda_{ab}\\mathbf{A}_{a}^{0}\\otimes\\mathbf{A}_{b}^{0}\n\\end{aligned}\n\\,.\\label{eq437}\n\\end{equation}\n\n\\end_inset\n\nIt is important to note that although this model is objective,\n it should only be used for small strains.\n For large strains,\n the response may be unrealistic.\n The Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\sigma} & =J^{-1}\\left(\\sum\\limits_{a=1}^{3}\\frac{\\mu_{a}}{2}\\left(\\mathbf{A}_{a}\\cdot\\left(\\mathbf{b}-\\mathbf{I}\\right)+\\left(\\mathbf{b}-\\mathbf{I}\\right)\\cdot\\mathbf{A}_{a}\\right)\\right.\\\\\n & \\left.+\\lambda_{aa}\\left[K_{a}\\right]K_{a}\\mathbf{A}_{a}+\\sum\\limits_{b=1,\\,b\\ne a}^{3}\\lambda_{ab}K_{a}\\mathbf{A}_{b}\\right)\\,,\n\\end{aligned}\n\\label{eq438}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{A}_{a}=\\mathbf{F}\\cdot\\mathbf{A}_{a}^{0}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n and \n\\begin_inset Formula $K_{a}=\\frac{1}{2}\\left({\\mathbf{A}_{a}:\\mathbf{I}-1}\\right)$\n\\end_inset\n\n.\n The spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=J^{-1}\\left(\\sum\\limits_{a=1}^{3}\\mu_{a}\\left(\\mathbf{A}_{a}\\odot\\mathbf{b}+\\mathbf{b}\\odot\\mathbf{A}_{a}\\right)+\\lambda_{aa}\\left[K_{a}\\right]\\mathbf{A}_{a}\\otimes\\mathbf{A}_{a}+\\sum\\limits_{b=1,\\,b\\ne a}^{3}\\lambda_{ab}\\mathbf{A}_{a}\\otimes\\mathbf{A}_{b}\\right)\\,.\\label{eq439}\n\\end{equation}\n\n\\end_inset\n\nIn the special case of cubic symmetry the number of material constants reduces to four,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\lambda_{+11} & =\\lambda_{+22}=\\lambda_{+33}\\equiv\\lambda_{+1}\\\\\n\\lambda_{-11} & =\\lambda_{-22}=\\lambda_{-33}\\equiv\\lambda_{-1}\\\\\n\\lambda_{12} & =\\lambda_{23}=\\lambda_{31}\\equiv\\lambda_{2}\\\\\n\\mu_{1} & =\\mu_{2}=\\mu_{3}\\equiv\\mu\n\\end{aligned}\n\\,.\\label{eq440}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDonnan Equilibrium Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Donnan-Equilibrium-Swelling-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe swelling pressure is described by the equations for ideal Donnan equilibrium,\n assuming that the material is porous,\n with a charged solid matrix,\n and the external bathing environment consists of a salt solution of monovalent counter-ions.\n Since osmotic swelling must be resisted by a solid material,\n this material is not stable on its own.\n It must be combined with an elastic material that resists the swelling.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is the stress from the Donnan equilibrium response \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian09a\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-\\pi\\,\\mathbf{I}\\,,\\label{eq441}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n is the osmotic pressure,\n given by \n\\begin_inset Formula \n\\begin{equation}\n\\pi=R\\theta\\left(\\sqrt{\\left(c^{F}\\right)^{2}+\\left(\\bar{c}^{\\ast}\\right)^{2}}-\\bar{c}^{\\ast}\\right),\\label{eq442}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula $\\bar{c}^{*}$\n\\end_inset\n\n is the bath osmolarity (twice the concentration) and \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n is the fixed charge density in the current configuration,\n related to the reference configuration via,\n \n\\begin_inset Formula \n\\begin{equation}\nc^{F}=\\frac{\\varphi_{0}^{w}}{J-1+\\varphi_{0}^{w}}c_{0}^{F}\\,,\\label{eq443}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the relative volume,\n \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant and \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the absolute temperature.\n\\end_layout\n\n\\begin_layout Standard\nNote that \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n may be negative or positive.\n The gel porosity \n\\begin_inset Formula $\\varphi_{0}^{w}$\n\\end_inset\n\n is unitless and must be in the range \n\\begin_inset Formula $0<\\varphi_{0}^{w}<1$\n\\end_inset\n\n.\n The corresponding spatial elasticity tensor is \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Azeloglu08\"\nliteral \"true\"\n\n\\end_inset\n\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}} & =\\frac{R\\theta J\\left(c^{F}\\right)^{2}}{\\left(J-1+\\varphi_{0}^{w}\\right)\\sqrt{\\left(c^{F}\\right)^{2}+\\left(\\bar{c}^{\\ast}\\right)^{2}}}\\mathbf{I}\\otimes\\mathbf{I}\\\\\n & +R\\theta\\left[\\sqrt{\\left(c^{F}\\right)^{2}+\\left(\\bar{c}^{\\ast}\\right)^{2}}-\\bar{c}^{\\ast}\\right]\\left(2\\mathbf{I}\\odot\\mathbf{I}-\\mathbf{I}\\otimes\\mathbf{I}\\right)\\,.\n\\end{aligned}\n\\label{eq444}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPerfect Osmometer Equilibrium Osmotic Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Perfect-Osmometer-Equilibrium\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe swelling pressure is described by the equations for a perfect osmometer,\n assuming that the material is porous,\n containing an interstitial solution whose solutes cannot be exchanged with the external bathing environment.\n Similarly,\n solutes in the external bathing solution cannot be exchanged with the interstitial fluid of the porous material.\n Therefore,\n osmotic pressurization occurs when there is an imbalance between the interstitial and bathing solution osmolarities.\n Since osmotic swelling must be resisted by a solid matrix,\n this material is not stable on its own.\n It must be combined with an elastic material that resists the swelling.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is the stress from the perfect osmometer equilibrium response \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian06\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-\\pi\\,\\mathbf{I}\\,,\\label{eq445}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n is the osmotic pressure,\n given by \n\\begin_inset Formula \n\\begin{equation}\n\\pi=R\\theta\\left(\\bar{c}-\\bar{c}^{\\ast}\\right)\\,.\\label{eq446}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant and \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the absolute temperature,\n \n\\begin_inset Formula $\\bar{c}^{\\ast}$\n\\end_inset\n\n is the external bath osmolarity and \n\\begin_inset Formula $\\bar{c}$\n\\end_inset\n\n is the interstitial fluid osmolarity in the current configuration,\n related to the reference configuration osmolarity \n\\begin_inset Formula $\\bar{c}_{0}$\n\\end_inset\n\n via,\n \n\\begin_inset Formula \n\\begin{equation}\n\\bar{c}=\\frac{\\varphi_{0}^{w}}{J-1+\\varphi_{0}^{w}}\\bar{c}_{0}\\,.\\label{eq447}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThough this material is porous,\n this is not a full-fledged poroelastic material.\n The behavior described by this material is strictly valid only after the transient response of interstitial fluid and solute fluxes has subsided.\n The corresponding spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=R\\theta\\left[\\frac{J\\,\\bar{c}}{J-1+\\varphi_{0}^{w}}\\mathbf{I}\\otimes\\mathbf{I}+\\left(\\bar{c}-\\bar{c}^{\\ast}\\right)\\left(2\\mathbf{I}\\odot\\mathbf{I}-\\mathbf{I}\\otimes\\mathbf{I}\\right)\\right]\\,.\\label{eq448}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLarge Poisson's Ratio Ligament\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Large-Poisson-Ratio-Lig\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material captures the transversely isotropic behavior of tendon and ligaments while enforcing a large Poisson's ratio.\n The material utilizes a three part strain energy equation:\n \n\\begin_inset Formula \n\\[\nW=W_{\\text{fiber}}+W_{\\text{matrix}}+W_{\\text{vol}}\\,,\n\\]\n\n\\end_inset\n\nwhere:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}W_{\\text{fiber}} & =\\frac{1}{2}\\frac{c_{1}}{c_{2}}\\left(e^{c_{2}\\left(\\lambda-1\\right)^{2}-1}\\right)\\,,\\\\\nW_{\\text{matrix}} & =\\frac{\\mu}{2}\\left(I_{1}-3\\right)-\\mu\\ln\\left(\\sqrt{I_{3}}\\right)\\,,\\\\\nW_{\\text{vol}} & =\\frac{\\kappa}{2}\\left(\\ln\\left(\\frac{I_{5}-I_{1}I_{4}+I_{2}}{I_{4}^{2(m-v_{0})}e^{-4m\\left(\\lambda-1\\right)}}\\right)\\right)^{2}\\,.\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThe transversely isotropic strain energy \n\\begin_inset Formula $W_{\\text{fiber}}$\n\\end_inset\n\n takes into account the behavior of the collagen fibers.\n The isotropic strain energy \n\\begin_inset Formula $W_{\\text{matrix}}$\n\\end_inset\n\n takes into account the mechanical contribution of the extrafibrillar matrix and provides the majority of support when loaded transverse to the fiber direction.\n The variables \n\\begin_inset Formula $c_{\\mathrm{1}}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $c_{2}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are material parameters controlling the stress-strain response of the material.\n\\end_layout\n\n\\begin_layout Standard\nThe volumetric strain energy \n\\begin_inset Formula $W_{\\text{vol}}$\n\\end_inset\n\n acts as a penalty term which enforces a Poisson's ratio based on user selection of the parameters \n\\begin_inset Formula $m$\n\\end_inset\n\n and \n\\begin_inset Formula $v_{\\mathrm{0}}$\n\\end_inset\n\n.\n The variable \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n acts as a penalty parameter.\n Raising \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n will cause the prescribed Poisson's ratio to be enforced.\n The Poisson's ratio in question is given by the following function:\n \n\\begin_inset Formula \n\\[\nv_{\\text{apparent}}=-\\frac{\\lambda^{m-v_{0}}e^{-m\\left(\\lambda-1\\right)}-1}{\\lambda-1}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPorous Neo-Hookean Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Porous-Neo-Hookean-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a porous neo-Hookean material with referential porosity \n\\begin_inset Formula $\\varphi_{r}^{w}$\n\\end_inset\n\n.\n The pores are compressible but the skeleton is intrinsically incompressible.\n Thus,\n upon pore closure,\n the material behavior needs to switch from compressible to incompressible.\n\\end_layout\n\n\\begin_layout Standard\nIn the current configuration,\n the porosity is given by\n\\begin_inset Formula \n\\[\n\\varphi^{w}=\\frac{J-1+\\varphi_{r}^{w}}{J}\\,.\n\\]\n\n\\end_inset\n\nWe may define a new variable,\n\\begin_inset Formula \n\\[\n\\bar{J}\\equiv\\frac{J-1+\\varphi_{r}^{w}}{\\varphi_{r}^{w}}=\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\,,\n\\]\n\n\\end_inset\n\nwhich represents the pore volume ratio.\n It is equal to \n\\begin_inset Formula $1$\n\\end_inset\n\n when \n\\begin_inset Formula $J=1$\n\\end_inset\n\n and is equal to \n\\begin_inset Formula $J$\n\\end_inset\n\n when \n\\begin_inset Formula $\\varphi_{r}^{w}=1$\n\\end_inset\n\n (or \n\\begin_inset Formula $\\varphi_{r}^{s}=1-\\varphi_{r}^{w}=0$\n\\end_inset\n\n).\n Now,\n\\begin_inset Formula \n\\[\n\\varphi^{w}=\\varphi_{r}^{w}\\frac{\\bar{J}}{J}\\,,\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\frac{\\partial\\bar{J}}{\\partial J}=\\frac{1}{\\varphi_{r}^{w}}\\,.\n\\]\n\n\\end_inset\n\nPore closure occurs when \n\\begin_inset Formula $\\varphi^{w}=0$\n\\end_inset\n\n,\n which corresponds to \n\\begin_inset Formula $J=\\varphi_{r}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\bar{J}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nLet us also define a modified deformation gradient,\n\\begin_inset Formula \n\\[\n\\bar{\\mathbf{F}}=\\left(\\frac{\\bar{J}}{J}\\right)^{1/3}\\mathbf{F}\\,,\n\\]\n\n\\end_inset\n\nsuch that \n\\begin_inset Formula $\\det\\bar{\\mathbf{F}}=\\bar{J}$\n\\end_inset\n\n.\n Let the corresponding modified right Cauchy-Green tensor be given by\n\\begin_inset Formula \n\\[\n\\bar{\\mathbf{C}}=\\bar{\\mathbf{F}}^{T}\\cdot\\bar{\\mathbf{F}}=\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\mathbf{C}\\,,\n\\]\n\n\\end_inset\n\nso that\n\\begin_inset Formula \n\\[\n\\frac{\\partial\\bar{\\mathbf{C}}}{\\partial\\mathbf{C}}=\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\left(\\frac{\\varphi_{r}^{s}}{3\\left(J-\\varphi_{r}^{s}\\right)}\\mathbf{C}\\otimes\\mathbf{C}^{-1}+\\mathbf{I}\\odot\\mathbf{I}\\right)\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe constitutive relation for the strain energy density of the compressible porous neo-Hookean material may be given by\n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\frac{\\mu}{2}\\left(\\bar{I}_{1}-3\\right)-\\mu\\ln\\bar{J}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\bar{I}_{1}=\\text{tr}\\bar{\\mathbf{C}}$\n\\end_inset\n\n.\n This relation shows that the material develops an infinite strain energy density as \n\\begin_inset Formula $\\bar{J}$\n\\end_inset\n\n approaches zero.\n From this expression,\n the 2nd Piola-Kirchhoff stress is given by\n\\begin_inset Formula \n\\[\n\\mathbf{S}=2\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{C}}=\\mu\\left[\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\mathbf{I}+\\frac{1}{J-\\varphi_{r}^{s}}\\left(\\varphi_{r}^{s}\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\frac{I_{1}}{3}-J\\right)\\mathbf{C}^{-1}\\right]\\,.\n\\]\n\n\\end_inset\n\nWhen \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{I}$\n\\end_inset\n\n we can verify that \n\\begin_inset Formula $\\mathbf{S}=\\mathbf{0}$\n\\end_inset\n\n.\n The corresponding Cauchy stress is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\frac{\\mu}{J}\\left[\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\mathbf{b}+\\frac{1}{J-\\varphi_{r}^{s}}\\left(\\varphi_{r}^{s}\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\frac{I_{1}}{3}-J\\right)\\mathbf{I}\\right]\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n\\end_layout\n\n\\begin_layout Standard\nThe material elasticity tensor is given by\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbb{C} & =2\\frac{\\partial\\mathbf{S}}{\\partial\\mathbf{C}}\\\\\n & =\\frac{2}{3}g\\left(J\\right)\\left(\\mathbf{I}\\otimes\\mathbf{C}^{-1}+\\mathbf{C}^{-1}\\otimes\\mathbf{I}\\right)+\\left(J\\frac{dg}{dJ}\\frac{I_{1}}{3}+J\\frac{dh}{dJ}\\right)\\mathbf{C}^{-1}\\otimes\\mathbf{C}^{-1}\\\\\n & -2\\left[g\\left(J\\right)\\frac{I_{1}}{3}+h\\left(J\\right)\\right]\\mathbf{C}^{-1}\\odot\\mathbf{C}^{-1}\n\\end{aligned}\n\\,,\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\begin{aligned}f\\left(J\\right) & =\\mu\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\\\\ng\\left(J\\right) & =\\frac{\\varphi_{r}^{s}}{J-\\varphi_{r}^{s}}f\\left(J\\right)\\\\\nh\\left(J\\right) & =-\\mu\\frac{J}{J-\\varphi_{r}^{s}}\n\\end{aligned}\n\\,,\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\begin{aligned}J\\frac{dg}{dJ} & =\\mu\\frac{\\left(2\\varphi_{r}^{s}-3J\\right)\\varphi_{r}^{s}}{3\\left(J-\\varphi_{r}^{s}\\right)^{2}}\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\\\\nJ\\frac{dh}{dJ} & =\\mu\\frac{J\\varphi_{r}^{s}}{\\left(J-\\varphi_{r}^{s}\\right)^{2}}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nThen,\n the spatial elasticity tensor may be evaluated as\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\mathcal{C}}=J^{-1}\\left[\\frac{2}{3}g\\left(J\\right)\\left(\\mathbf{b}\\otimes\\mathbf{I}+\\mathbf{I}\\otimes\\mathbf{b}\\right)+\\left(J\\frac{dg}{dJ}\\frac{I_{1}}{3}+J\\frac{dh}{dJ}\\right)\\mathbf{I}\\otimes\\mathbf{I}-2\\left[g\\left(J\\right)\\frac{I_{1}}{3}+h\\left(J\\right)\\right]\\mathbf{I}\\odot\\mathbf{I}\\right]\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the limit of infinitesimal strains and rotations,\n when \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{I}$\n\\end_inset\n\n and \n\\begin_inset Formula $J=1$\n\\end_inset\n\n,\n we find that\n\\begin_inset Formula \n\\[\n\\begin{aligned}f\\left(1\\right) & =\\mu & J\\frac{dg}{dJ} & =\\mu\\frac{\\left(2\\varphi_{r}^{s}-3\\right)\\varphi_{r}^{s}}{3\\left(1-\\varphi_{r}^{s}\\right)^{2}}\\\\\ng\\left(J\\right) & =\\mu\\frac{\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}} & J\\frac{dh}{dJ} & =\\mu\\frac{\\varphi_{r}^{s}}{\\left(1-\\varphi_{r}^{s}\\right)^{2}}\\\\\nh\\left(J\\right) & =-\\mu\\frac{1}{1-\\varphi_{r}^{s}}\\mu\n\\end{aligned}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\mathcal{C}}=\\frac{2\\mu}{3}\\left(\\frac{1}{\\left(\\varphi_{r}^{w}\\right)^{2}}-1\\right)\\mathbf{I}\\otimes\\mathbf{I}+2\\mu\\mathbf{I}\\odot\\mathbf{I}\\,.\n\\]\n\n\\end_inset\n\nThus,\n by comparison to a standard neo-Hookean material,\n this porous neo-Hookean material has an effective Young's modulus equal to\n\\begin_inset Formula \n\\[\nE=\\frac{3\\mu}{1+\\frac{1}{2}\\left(\\varphi_{r}^{w}\\right)^{2}}\\,,\n\\]\n\n\\end_inset\n\nand an effective Poisson's ratio equal to\n\\begin_inset Formula \n\\[\n\\nu=\\frac{1-\\left(\\varphi_{r}^{w}\\right)^{2}}{2+\\left(\\varphi_{r}^{w}\\right)^{2}}\\,.\n\\]\n\n\\end_inset\n\nThe two material properties that need to be provided are \n\\begin_inset Formula $E$\n\\end_inset\n\n and the referential porosity \n\\begin_inset Formula $\\varphi_{r}^{w}$\n\\end_inset\n\n (or referential solid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}=1-\\varphi_{r}^{w}$\n\\end_inset\n\n).\n Poisson's ratio in the limit of infinitesimal strains is dictated by the porosity according to the above formula.\n In particular,\n a highly porous material (\n\\begin_inset Formula $\\varphi_{r}^{w}\\to1$\n\\end_inset\n\n) has an effective (infinitesimal strain) Poisson ratio that approaches zero (\n\\begin_inset Formula $\\nu\\to0$\n\\end_inset\n\n) and \n\\begin_inset Formula $E\\to2\\mu$\n\\end_inset\n\n.\n A low porosity material (\n\\begin_inset Formula $\\varphi_{r}^{w}\\to0$\n\\end_inset\n\n) has \n\\begin_inset Formula $\\nu\\to\\frac{1}{2}$\n\\end_inset\n\n and \n\\begin_inset Formula $E\\to3\\mu$\n\\end_inset\n\n,\n which is the expected behavior of an incompressible neo-Hookean solid.\n Note that setting \n\\begin_inset Formula $\\varphi_{r}^{w}=0$\n\\end_inset\n\n would not produce good numerical behavior,\n since the Cauchy stress in an incompressible material would need to be supplemented by a pressure term (a Lagrange multiplier that enforces the incompressibility constraint).\n Nevertheless,\n this compressible porous neo-Hookean material behaves well even for values of \n\\begin_inset Formula $\\varphi^{w}$\n\\end_inset\n\n as low as \n\\begin_inset Formula $\\sim0.015$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nCell Growth\n\\end_layout\n\n\\begin_layout Standard\nThe cell growth material implements a swelling pressure \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n such that the Cauchy stress is given by\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=-\\pi\\mathbf{I}\\,,\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\pi=RT\\left(\\frac{c_{r}}{J-\\varphi_{r}^{s}}-c_{e}\\right)\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n represents the referential molar concentration of intracellular solutes (moles of solutes per mixture volume in the reference configuration),\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential intracellular solid volume fraction,\n and \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the determinant of the deformation gradient,\n representing the volume ratio in the current configuration.\n The extracellular osmolarity is \n\\begin_inset Formula $c_{e}$\n\\end_inset\n\n.\n This model assumes that neither intracellular solutes nor extracellular solutes may transport across the cell membrane passively.\n When a cell divides,\n it must use active transport mechanisms to bring in membrane-impermeant extracellular solutes inside,\n some of which are converted into intracellular solid matrix (e.g.,\n cytoskeletal structures).\n As the intracellular osmolarity increases,\n water is transported into the cell,\n thus causing it to swell.\n The process of cell division is not modeled explicitly in this continuum representation,\n though the net effect is that cell proliferation leads to an increase in intracellular osmotic pressure,\n which generally translates into an increase in volume (unless the cell growth is constrained significantly).\n In a cell growth model,\n the initial condition (when \n\\begin_inset Formula $J=1$\n\\end_inset\n\n) should be selected for \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n such that \n\\begin_inset Formula $\\pi=0$\n\\end_inset\n\n,\n thus\n\\begin_inset Formula \n\\[\n\\frac{c_{r}}{1-\\varphi_{r}^{s}}=c_{e}\\quad\\text{initial condition}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe spatial elasticity tensor associated with this osmotic pressure is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\Pi}=-\\left(\\pi+J\\frac{\\partial\\pi}{\\partial J}\\right)\\mathbf{I}\\otimes\\mathbf{I}+2\\pi\\mathbf{I}\\odot\\mathbf{I}\\,,\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\nJ\\frac{\\partial\\pi}{\\partial J}=-RT\\frac{J}{\\left(J-\\varphi_{r}^{s}\\right)^{2}}c_{r}\\,.\n\\]\n\n\\end_inset\n\nThis elasticity tensor has the same form as that of an isotropic elastic material whose effective Young's modulus \n\\begin_inset Formula $E_{Y}$\n\\end_inset\n\n and Poisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n are given by\n\\begin_inset Formula \n\\[\n\\begin{aligned}E_{Y} & =\\pi\\frac{\\left(\\pi+3J\\frac{\\partial\\pi}{\\partial J}\\right)}{J\\frac{\\partial\\pi}{\\partial J}}\\\\\n\\nu & =\\frac{\\pi+J\\frac{\\partial\\pi}{\\partial J}}{2J\\frac{\\partial\\pi}{\\partial J}}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nIn the reference configuration,\n when \n\\begin_inset Formula $\\pi=0$\n\\end_inset\n\n,\n it follows that \n\\begin_inset Formula $E_{Y}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\nu=\\frac{1}{2}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=\\tilde{\\boldsymbol{\\sigma}}_{m}+\\tilde{\\boldsymbol{\\sigma}}_{f}\\,.\\label{eq471-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFiber with Exponential Power Law\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Exponential-Power\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model describes a constitutive model for fibers,\n where a single fiber family follows an exponential power law strain energy function.\n The Cauchy stress is given by:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=2J^{-1}H\\left(I_{n}-I_{0}\\right)I_{n}\\frac{\\partial\\Psi_{r}}{\\partial I_{n}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq:fepl-stress}\n\\end{equation}\n\n\\end_inset\n\nand the corresponding spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=4J^{-1}H\\left(I_{n}-I_{0}\\right)I_{n}^{2}\\frac{\\partial^{2}\\Psi_{r}}{\\partial I_{n}^{2}}\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq:fepl-elasticity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{n}=\\lambda_{n}^{2}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the square of the fiber stretch,\n \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the fiber orientation in the reference configuration,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}_{r}=\\sin\\varphi\\cos\\theta\\,\\mathbf{e}_{1}+\\sin\\varphi\\sin\\theta\\,\\mathbf{e}_{2}+\\cos\\varphi\\,\\mathbf{e}_{3}\\,,\\label{eq:fepl-fiber-nr}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{n}_{r}/\\lambda_{n}$\n\\end_inset\n\n.\n The function \n\\begin_inset Formula $H\\left(\\cdot\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n Thus,\n the stress and elasticity tensors are non-zero only when \n\\begin_inset Formula $I_{n}>I_{0}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $I_{0}=\\lambda_{0}^{2}$\n\\end_inset\n\n is the square of the stretch at which the fiber's tensile response engages.\n By default we may take \n\\begin_inset Formula $I_{0}=1$\n\\end_inset\n\n,\n though the actual value of \n\\begin_inset Formula $I_{0}$\n\\end_inset\n\n may be set by the user.\n The fiber strain energy density is given by \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\frac{\\xi}{\\alpha\\beta}\\left(\\exp\\left[\\alpha\\left(I_{n}-I_{0}\\right)^{\\beta}\\right]-1\\right)\\,,\\label{eq:fepl-fiber-Psi}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\nand \n\\begin_inset Formula $\\beta\\geqslant2$\n\\end_inset\n\n.\n From this expression we get\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{\\partial\\Psi_{r}}{\\partial I_{n}} & =\\xi\\left(I_{n}-I_{0}\\right)^{\\beta-1}\\exp\\left[\\alpha\\left(I_{n}-I_{0}\\right)^{\\beta}\\right]\\\\\n\\frac{\\partial^{2}\\Psi_{r}}{\\partial I_{n}^{2}} & =\\xi\\left(\\beta\\left(1+\\alpha\\left(I_{n}-I_{0}\\right)^{\\beta}\\right)-1\\right)\\left(I_{n}-I_{0}\\right)^{\\beta-2}\\exp\\left[\\alpha\\left(I_{n}-I_{0}\\right)^{\\beta}\\right]\n\\end{aligned}\n\\,.\\label{eq:fepl-fiber-dPsi}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote:\n In the limit when \n\\begin_inset Formula $\\alpha\\to0$\n\\end_inset\n\n,\n this expression produces a power law,\n \n\\begin_inset Formula \n\\begin{equation}\n\\lim\\limits_{\\alpha\\to0}\\Psi_{r}=\\frac{\\xi}{\\beta}\\left(I_{n}-I_{0}\\right)^{\\beta}\\,.\\label{eq:fepl-Psi-alpha0}\n\\end{equation}\n\n\\end_inset\n\nNote:\n According to \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fepl-elasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fepl-fiber-dPsi\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n when \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n the fiber modulus is zero at the strain origin (\n\\begin_inset Formula $I_{n}=I_{0})$\n\\end_inset\n\n.\n Therefore,\n use \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n when a smooth transition in the stress is desired from compression to tension.\n\\end_layout\n\n\\begin_layout Standard\nThere is an option to also add a shear modulus \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n to account for the interaction of a fiber with the ground matrix.\n This additional contribution does not depend on whether the fiber is in tension.\n It has a strain energy density\n\\begin_inset Formula \n\\begin{equation}\nW=\\frac{\\mu}{4}\\left(K_{n}-1-2\\left(I_{n}-1\\right)\\right)\\,,\\quad K_{n}=\\mathbf{n}_{r}\\cdot\\mathbf{C}^{2}\\cdot\\mathbf{n}_{r}\\label{eq:fepl-shear-sed}\n\\end{equation}\n\n\\end_inset\n\nThe corresponding stress is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\sigma} & =2\\frac{I_{n}}{J}\\left(\\frac{\\partial W}{\\partial I_{n}}\\mathbf{n}\\otimes\\mathbf{n}+\\frac{\\partial W}{\\partial K_{n}}\\left(\\mathbf{n}\\otimes\\mathbf{n}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{n}\\otimes\\mathbf{n}\\right)\\right)\\\\\n & =\\frac{\\mu}{2}\\frac{I_{n}}{J}\\left(\\mathbf{n}\\otimes\\mathbf{n}\\cdot\\left(\\mathbf{b}-\\mathbf{I}\\right)+\\left(\\mathbf{b}-\\mathbf{I}\\right)\\cdot\\mathbf{n}\\otimes\\mathbf{n}\\right)\n\\end{aligned}\n\\label{eq:fepl-shear-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n The elasticity tensor is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}} & =4\\frac{I_{n}^{2}}{J}\\left(\\frac{\\partial^{2}W}{\\partial I_{n}^{2}}\\mathbf{N}\\otimes\\mathbf{N}+\\frac{\\partial^{2}W}{\\partial K_{n}^{2}}\\left(\\mathbf{N}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{N}\\right)\\otimes\\left(\\mathbf{N}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{N}\\right)\\right)\\\\\n & +4\\frac{I_{n}^{2}}{J}\\frac{\\partial^{2}W}{\\partial I_{n}\\partial K_{n}}\\left(\\mathbf{N}\\otimes\\left(\\mathbf{N}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{N}\\right)+\\left(\\mathbf{N}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{N}\\right)\\otimes\\mathbf{N}\\right)\\\\\n & +\\frac{4I_{n}}{J}\\frac{\\partial W}{\\partial K_{n}}\\left(\\mathbf{N}\\odot\\mathbf{b}+\\mathbf{b}\\odot\\mathbf{N}\\right)\\\\\n & =\\mu\\frac{I_{n}}{J}\\left(\\mathbf{N}\\odot\\mathbf{b}+\\mathbf{b}\\odot\\mathbf{N}\\right)\n\\end{aligned}\n\\,,\\label{eq:fepl-shear-elasticity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{N}=\\mathbf{n}\\otimes\\mathbf{n}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nFiber with Natural Neo-Hookean Response\n\\end_layout\n\n\\begin_layout Standard\nThis model is an adaptation of the natural neo-Hookean material presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"subsec:Natural-Neo-Hookean\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Consider that the state of strain in a fiber is given by the unidirectional natural (left Hencky) strain along the fiber,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\eta}=\\ln\\lambda_{n}\\,\\mathbf{n}\\otimes\\mathbf{n}\\label{eq:fnh-strain}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n is the stretch ratio along the current fiber unit vector \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n For this special state of strain the invariants \n\\begin_inset Formula $K_{i}$\n\\end_inset\n\n of the natural strain tensor reduce to \n\\begin_inset Formula $K_{1}=\\ln\\lambda_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $K_{2}=\\sqrt{3/2}\\ln\\lambda_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $K_{3}=1$\n\\end_inset\n\n.\n In this case,\n a natural neo-Hookean fiber response is given by\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\frac{\\xi}{2}H\\left(\\ln\\lambda_{n}\\right)\\left(\\ln\\lambda_{n}\\right)^{2}\\,.\\label{eq:fnh-sed}\n\\end{equation}\n\n\\end_inset\n\nThe corresponding Cauchy stress is\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\xi J^{-1}H\\left(\\ln\\lambda_{n}\\right)\\ln\\lambda_{n}\\mathbf{n}\\otimes\\mathbf{n}\\label{eq:fnh-stress}\n\\end{equation}\n\n\\end_inset\n\nand the elasticity tensor is\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\xi J^{-1}H\\left(\\ln\\lambda_{n}\\right)\\left(1-2\\ln\\lambda_{n}\\right)\\mathbf{N}\\otimes\\mathbf{N}\\label{eq:fnh-elasticity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n is the fiber modulus.\n This expression shows that the components of the elasticity tensor become negative when \n\\begin_inset Formula $1-2\\ln\\lambda_{n}<0$\n\\end_inset\n\n,\n or equivalently when \n\\begin_inset Formula $\\lambda_{n}>e^{\\frac{1}{2}}$\n\\end_inset\n\n.\n However,\n the stress always remains positive.\n\\end_layout\n\n\\begin_layout Standard\nIf we want the tensile response to engage only beyond a threshold stretch ratio \n\\begin_inset Formula $\\lambda_{0}$\n\\end_inset\n\n,\n we may rewrite the strain energy density as\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\frac{\\xi}{2}H\\left(\\ln\\lambda_{n}-\\ln\\lambda_{0}\\right)\\left(\\ln\\lambda_{n}-\\ln\\lambda_{0}\\right)^{2}=\\frac{\\xi}{2}H\\left(\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)\\left(\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)^{2}\\,.\\label{eq:fnh-sed-redux}\n\\end{equation}\n\n\\end_inset\n\nThen\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\xi J^{-1}H\\left(\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq:fnh-stress-redux}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=\\xi J^{-1}H\\left(\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)\\left(1-2\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)\\mathbf{N}\\otimes\\mathbf{N}\\,.\\label{eq:fnh-elasticity-redux}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nHGO Unconstrained\n\\end_layout\n\n\\begin_layout Standard\nA coupled formulation of the Holzapfel-Gasser-Ogden material is implemented in the \n\\begin_inset Quotes eld\n\\end_inset\n\nHGO unconstrained\n\\begin_inset Quotes erd\n\\end_inset\n\n material.\n \n\\end_layout\n\n\\begin_layout Standard\nThe strain energy density is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\frac{c}{2}\\left(I_{1}-3\\right)-c\\ln J+\\frac{k_{1}}{2k_{2}}\\sum_{\\alpha}\\left(\\exp\\left(k_{2}\\left\\langle E_{\\alpha}\\right\\rangle ^{2}\\right)-1\\right)+\\frac{K_{0}}{2}\\left(\\frac{J^{2}-1}{2}-\\ln J\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain is\n\\begin_inset Formula \n\\[\nE_{\\alpha}=\\kappa\\left(I_{1}-3\\right)+\\left(1-3\\kappa\\right)\\left(I_{4\\alpha}-1\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{1}=\\tr\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{4\\alpha}=\\mathbf{a}_{\\alpha r}\\cdot\\mathbf{C}\\cdot\\mathbf{a}_{\\alpha r}$\n\\end_inset\n\n.\n The Macaulay brackets around \n\\begin_inset Formula $\\left\\langle E_{\\alpha}\\right\\rangle $\n\\end_inset\n\n indicate that this term is zero when \n\\begin_inset Formula $E_{\\alpha}<0$\n\\end_inset\n\n and equal to \n\\begin_inset Formula $E_{\\alpha}$\n\\end_inset\n\n when this strain is positive.There are two fiber families along the vectors \n\\begin_inset Formula $\\mathbf{a}_{\\alpha r}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha=1,2$\n\\end_inset\n\n),\n lying in the \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2}\\right\\} $\n\\end_inset\n\n plane of the local material axes \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n,\n making an angle \n\\begin_inset Formula $\\pm\\gamma$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n.\n Each fiber family has a dispersion \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $0\\le\\kappa\\le\\frac{1}{3}$\n\\end_inset\n\n.\n When \n\\begin_inset Formula $\\kappa=0$\n\\end_inset\n\n there is no fiber dispersion,\n implying that all the fibers in that family act along the angle \n\\begin_inset Formula $\\pm\\gamma$\n\\end_inset\n\n;\n the value \n\\begin_inset Formula $\\kappa=\\frac{1}{3}$\n\\end_inset\n\n represents an isotropic fiber dispersion.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{\\sigma}=\\frac{1}{J}\\left[c\\left(\\mathbf{b}-\\mathbf{1}\\right)+\\frac{k}{2}\\left(J^{2}-1\\right)\\mathbf{1}\\right]+\\frac{1}{J}\\left[{\\displaystyle \\underset{\\alpha}{\\sum}2k_{1}\\left\\langle E_{\\alpha}\\right\\rangle \\exp\\left(k_{2}\\left\\langle E_{\\alpha}\\right\\rangle ^{2}\\right)\\mathbf{h}_{\\alpha}}\\right]\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere,\n \n\\begin_inset Formula $\\mathbf{h}_{\\alpha}=\\kappa\\mathbf{b}+\\left(1-3\\kappa\\right)\\mathbf{A}_{\\alpha}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{A}_{\\alpha}=\\mathbf{a}_{\\alpha}\\otimes\\mathbf{a}_{\\alpha}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe spatial elasticity tensor is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{c}=\\frac{1}{J}\\left\\{ kJ^{2}\\mathbf{1}\\otimes\\mathbf{1}+\\left[2c-k\\left(J^{2}-1\\right)\\right]\\mathbf{1}\\odot\\mathbf{1}\\right\\} +\\frac{1}{J}\\left[{\\displaystyle \\underset{\\alpha}{\\sum}4k_{1}\\left(1+2k_{2}\\left\\langle E_{\\alpha}\\right\\rangle ^{2}\\right)\\exp\\left(k_{2}\\left\\langle E_{\\alpha}\\right\\rangle ^{2}\\right)\\mathbf{h}_{\\alpha}\\otimes\\mathbf{h}_{\\alpha}}\\right]\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nNearly-Incompressible Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Nearly-Incompressible-Materials\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nMooney-Rivlin Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mooney-Rivlin-Hyperelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model is a hyperelastic Mooney-Rivlin type with uncoupled deviatoric and volumetric behavior.\n The uncoupled strain energy W is given by:\n \n\\begin_inset Formula \n\\begin{equation}\nW=c_{1}\\left(\\tilde{I}_{1}-3\\right)+c_{2}\\left(\\tilde{I}_{2}-3\\right)+\\frac{1}{2}K\\left(\\ln J\\right)^{2}\\,.\\label{eq449}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $c_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $c_{2}$\n\\end_inset\n\n are the Mooney-Rivlin material coefficients,\n \n\\begin_inset Formula $\\tilde{I}_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{I}_{2}$\n\\end_inset\n\n are the invariants of the deviatoric part of the right Cauchy-Green deformation tensor,\n \n\\begin_inset Formula $\\mathbf{\\tilde{C}}=\\mathbf{\\tilde{F}}^{T}\\cdot\\mathbf{\\tilde{F}}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{\\tilde{F}}=J^{-1/3}\\mathbf{F}$\n\\end_inset\n\n,\n \n\\series bold\n\\emph on\n\n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n\n\\series default\n\\emph default\n is the deformation gradient and \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the Jacobian of the deformation.\n When \n\\begin_inset Formula $c_{2}=0$\n\\end_inset\n\n,\n this model reduces to an uncoupled version of the incompressible neo-Hookean constitutive model.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=p\\mathbf{I}+\\frac{2}{J}\\left[\\left(c_{1}+c_{2}\\tilde{I}_{1}\\right)\\mathbf{\\tilde{b}}-c_{2}\\mathbf{\\tilde{b}}^{2}-\\frac{1}{3}\\left(c_{1}\\tilde{I}_{1}+2c_{2}\\tilde{I}_{2}\\right)\\mathbf{I}\\right]\\,.\\label{eq450}\n\\end{equation}\n\n\\end_inset\n\nThe spatial elasticity tensor is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{C}}=p\\left(\\mathbf{I}\\otimes\\mathbf{I}-2\\mathbf{I}\\odot\\mathbf{I}\\right)-\\frac{2}{3}\\left(\\dev\\boldsymbol{\\sigma}\\otimes\\mathbf{I}+\\mathbf{I}\\otimes\\dev\\boldsymbol{\\sigma}\\right)+\\boldsymbol{\\mathcal{C}}_{w}\\,,\\label{eq451}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}}_{w} & =\\frac{4}{3J}\\left(c_{1}\\tilde{I}_{1}+2c_{2}\\tilde{I}_{2}\\right)\\left(\\mathbf{I}\\odot\\mathbf{I}-\\frac{1}{3}\\mathbf{I}\\otimes\\mathbf{I}\\right)+\\frac{4c_{2}}{J}\\left(\\mathbf{\\tilde{b}}\\otimes\\mathbf{\\tilde{b}}-\\mathbf{\\tilde{b}}\\odot\\mathbf{\\tilde{b}}\\right)\\\\\n & -\\frac{4c_{2}}{3J}\\left[\\left(\\tilde{I}_{1}\\mathbf{\\tilde{b}}-\\mathbf{\\tilde{b}}^{2}\\right)\\otimes\\mathbf{I}+\\mathbf{I}\\otimes\\left(\\tilde{I}_{1}\\mathbf{\\tilde{b}}-\\mathbf{\\tilde{b}}^{2}\\right)\\right]+\\frac{8c_{2}\\tilde{I}_{2}}{9J}\\mathbf{I}\\otimes\\mathbf{I}\\,.\n\\end{aligned}\n\\label{eq452}\n\\end{equation}\n\n\\end_inset\n\nThis material model uses a three-field element formulation,\n interpolating displacements as linear field variables and pressure and volume ratio as piecewise constant in each element \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nOgden Hyperelastic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ogden-Hyperelastic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Ogden material is defined using the following hyperelastic strain energy function:\n \n\\begin_inset Formula \n\\begin{equation}\nW\\left(\\lambda_{1},\\lambda_{2},\\lambda_{3},J\\right)=\\sum\\limits_{i=1}^{N}\\frac{c_{i}}{m_{i}^{2}}\\left(\\tilde{\\lambda}_{1}^{m_{i}}+\\tilde{\\lambda}_{2}^{m_{i}}+\\tilde{\\lambda}_{3}^{m_{i}}-3\\right)+U\\left(J\\right)\\,.\\label{eq453}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\tilde{\\lambda}_{i}$\n\\end_inset\n\n are the deviatoric principal stretches and \n\\begin_inset Formula $c_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $m_{i}$\n\\end_inset\n\n are material parameters.\n The term \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n is the volumetric component and \n\\begin_inset Formula $J$\n\\end_inset\n\n is the determinant of the deformation gradient.\n\\end_layout\n\n\\begin_layout Standard\nNote that the neo-Hookean and Mooney-Rivlin models can also be obtained from the general Ogden strain energy function using special choices for \n\\begin_inset Formula $c_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $m_{i}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nVeronda-Westmann Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Veronda-Westmann-Hyperelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model is similar to the Mooney-Rivlin model in that it also uses an uncoupled strain energy.\n However,\n in this case the strain energy is given by an exponential form:\n \n\\begin_inset Formula \n\\begin{equation}\nW=C_{1}\\left[e^{\\left(C_{2}\\left(\\tilde{I}_{1}-3\\right)\\right)}-1\\right]-\\frac{C_{1}C_{2}}{2}\\left(\\tilde{I}_{2}-3\\right)+U\\left(J\\right)\\,.\\label{eq454}\n\\end{equation}\n\n\\end_inset\n\nThe dilatational term \n\\begin_inset Formula $U$\n\\end_inset\n\n is identical to the Mooney-Rivlin model.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is found from \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=p\\mathbf{I}+\\dev\\tilde{\\boldsymbol{\\sigma}}\\,,\\label{eq455}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=\\frac{2}{J}\\left[\\left(W_{1}+I_{1}W_{2}\\right)\\tilde{\\mathbf{b}}-W_{2}\\tilde{\\mathbf{b}}^{2}\\right]\\,.\\label{eq456}\n\\end{equation}\n\n\\end_inset\n\nThe strain energy derivatives are given by \n\\begin_inset Formula \n\\begin{equation}\nW_{1}=C_{1}C_{2}e^{C_{2}\\left(I_{1}-3\\right)}\\,,\\label{eq457}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nW_{2}=-\\frac{C_{1}C_{2}}{2}\\,.\\label{eq458}\n\\end{equation}\n\n\\end_inset\n\nThis material model was the result from the research of the elastic response of skin tissue \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Veronda70\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nArruda-Boyce Hyperelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Arruda-Boyce-Hyperelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nArruda and Boyce proposed a model for the deformation of rubber materials \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Arruda93\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Their main motivation was to develop a model that accurately captures the behavior of rubbers in different loading scenarios and that can be described with a limited number of physically motivated parameters.\n Their model is based on the Langevin chain statistics,\n which models a rubber chain segment between chemical crosslinks as a number \n\\begin_inset Formula $N$\n\\end_inset\n\n of rigid links of equal length \n\\begin_inset Formula $l$\n\\end_inset\n\n.\n The parameter \n\\begin_inset Formula $N$\n\\end_inset\n\n is related to the locking stretch \n\\begin_inset Formula $\\lambda_{L}$\n\\end_inset\n\n,\n the stretch at which the chains reach their full extended state,\n \n\\begin_inset Formula $\\lambda_{L}=\\sqrt{N}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nTheir proposed strain-energy is a truncated Taylor series of the inverse Langevin function.\n A formulation that retains the first five terms of this function takes on the following form:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{W}=\\mu\\sum\\limits_{i=1}^{5}\\frac{\\alpha_{i}}{N^{i-1}}\\left(\\tilde{I}_{1}^{i}-3^{i}\\right)+U\\left(J\\right)\\,,\\label{eq459}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is a shear-modulus like parameter and the coefficients \n\\begin_inset Formula $\\alpha_{i}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\begin{equation}\n\\alpha_{1}=\\frac{1}{2}\\,,\\quad\\alpha_{2}=\\frac{1}{20}\\,,\\quad\\alpha_{3}=\\frac{11}{1050}\\,,\\quad\\alpha_{4}=\\frac{19}{7000}\\,,\\quad\\alpha_{5}=\\frac{519}{673750}\\,.\\label{eq460}\n\\end{equation}\n\n\\end_inset\n\nThe Cauchy stress is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=p\\mathbf{I}+\\frac{2}{J}\\dev\\left(W_{1}\\mathbf{\\tilde{b}}\\right)=p\\mathbf{I}+\\frac{2W_{1}}{J}\\left(\\mathbf{\\tilde{b}}-\\frac{1}{3}\\tilde{I}_{1}\\mathbf{I}\\right)\\,,\\label{eq461}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\nW_{1}=\\frac{\\partial\\tilde{W}}{\\partial\\tilde{I}_{1}}=\\mu\\sum\\limits_{i=1}^{5}\\alpha_{i}i\\left(\\frac{\\tilde{I}_{1}}{N}\\right)^{i-1}\\,.\\label{eq462}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTransversely Isotropic Hyperelastic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Transversely-Isotropic-Hyperelas-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis constitutive model can be used to represent a material that has a single preferred fiber direction and was developed for application to biological soft tissues \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss96,Puso98,Quapp98\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It can be used to model tissues such as tendons,\n ligaments and muscle.\n The elastic response of the tissue is assumed to arise from the resistance of the fiber family and an isotropic matrix.\n It is assumed that the strain energy function can be written as follows:\n \n\\begin_inset Formula \n\\begin{equation}\nW=F_{1}\\left(\\tilde{I}_{1},\\tilde{I}_{2}\\right)+F_{2}\\left(\\tilde{\\lambda}\\right)+\\frac{K}{2}\\left[\\ln\\left(J\\right)\\right]^{2}\\,.\\label{eq463}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\tilde{I}_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{I}_{2}$\n\\end_inset\n\n are the first and second invariants of the deviatoric version of the right Cauchy Green deformation tensor \n\\begin_inset Formula $\\mathbf{\\tilde{C}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\lambda}$\n\\end_inset\n\n is the deviatoric part of the stretch along the fiber direction (\n\\begin_inset Formula $\\tilde{\\lambda}^{2}=\\mathbf{A}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{A}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n is the initial fiber direction).\n The function \n\\begin_inset Formula $F_{1}$\n\\end_inset\n\n represents the material response of the isotropic ground substance matrix,\n while \n\\begin_inset Formula $F_{2}$\n\\end_inset\n\nrepresents the contribution from the fiber family.\n The strain energy of the fiber family is as follows:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\lambda}\\frac{\\partial F_{2}}{\\partial\\tilde{\\lambda}}=\\begin{cases}\n0 & \\tilde{\\lambda}\\leqslant1\\\\\nC_{3}\\left(e^{C_{4}\\left(\\tilde{\\lambda}-1\\right)}-1\\right) & 1<\\tilde{\\lambda}<\\lambda_{m}\\\\\nC_{5}+C_{6}\\tilde{\\lambda} & \\tilde{\\lambda}\\geqslant\\lambda_{m}\n\\end{cases}\\,.\\label{eq464}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n is the stretch at which the fibers are straightened,\n \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\n scales the exponential stresses,\n \n\\begin_inset Formula $C_{4}$\n\\end_inset\n\n is the rate of uncrimping of the fibers,\n and \n\\begin_inset Formula $C_{5}$\n\\end_inset\n\n is the modulus of the straightened fibers.\n \n\\begin_inset Formula $C_{6}$\n\\end_inset\n\n is determined from the requirement that the stress is continuous at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nC_{6}=\\frac{1}{\\lambda_{m}}\\left[C_{3}\\left(e^{C_{4}\\left(\\lambda_{m}-1\\right)}-1\\right)-C_{5}\\right]\\,.\\label{eq464b}\n\\end{equation}\n\n\\end_inset\n\nIt also follows that\n\\begin_inset Formula \n\\begin{equation}\nF_{2}\\left(\\tilde{\\lambda}\\right)=\\begin{cases}\n0 & \\tilde{\\lambda}\\leqslant1\\\\\nC_{3}\\left(e^{-C_{4}}\\left[\\Ei\\left(C_{4}\\tilde{\\lambda}\\right)-\\Ei\\left(C_{4}\\right)\\right]-\\ln\\tilde{\\lambda}\\right) & 1<\\tilde{\\lambda}<\\lambda_{m}\\\\\n\\left(C_{5}+\\frac{C_{6}}{2}\\tilde{\\lambda}\\right)\\tilde{\\lambda}+C_{7} & \\tilde{\\lambda}\\geqslant\\lambda_{m}\n\\end{cases}\\,,\\label{eq464c}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\nC_{7}=C_{3}\\left(e^{-C_{4}}\\left[\\Ei\\left(C_{4}\\lambda_{m}\\right)-\\Ei\\left(C_{4}\\right)\\right]-\\ln\\lambda_{m}\\right)-\\left(C_{5}+\\frac{C_{6}}{2}\\lambda_{m}\\right)\\lambda_{m}\\,.\\label{eq464d}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a three-field element formulation,\n interpolating displacements as linear field variables and pressure and volume ratio as piecewise constant on each element \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nEllipsoidal Fiber Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ellipsoidal-Fiber-Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis constitutive model describes a material that is composed of an ellipsoidal continuous fiber distribution in an uncoupled formulation.\n The deviatoric part of the stress is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian09a,Ateshian07a,Lanir83\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=\\int_{0}^{2\\pi}\\int_{0}^{\\pi}H\\left(\\tilde{I}_{n}-1\\right)\\tilde{\\boldsymbol{\\sigma}}_{n}\\left(\\mathbf{n}\\right)\\sin\\varphi\\,d\\varphi\\,d\\theta\\,,\\label{eq465}\n\\end{equation}\n\n\\end_inset\n\nand the corresponding elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{C}}}=\\int_{0}^{2\\pi}\\int_{0}^{\\pi}H\\left(\\tilde{I}_{n}-1\\right)\\tilde{\\boldsymbol{\\mathcal{C}}}_{n}\\left(\\mathbf{n}\\right)\\sin\\phi\\,d\\phi\\,d\\theta\\,.\\label{eq466}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula $\\tilde{I}_{n}=\\tilde{\\lambda}_{n}^{2}=\\mathbf{N}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{N}$\n\\end_inset\n\n is the square of the fiber stretch \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n is the unit vector along the fiber direction (in the reference configuration),\n which in spherical angles is directed along \n\\begin_inset Formula $\\left(\\theta,\\varphi\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{\\tilde{F}}\\cdot\\mathbf{N}/\\tilde{\\lambda}_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $H\\left(\\cdot\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n The fiber stress is determined from a fiber strain energy function in the usual manner:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}_{n}\\left(\\mathbf{n}\\right)=2J^{-1}\\tilde{I}_{n}\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{I}_{n}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq467}\n\\end{equation}\n\n\\end_inset\n\nwhereas the fiber elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{C}}}_{n}\\left(\\mathbf{n}\\right)=4J^{-1}\\tilde{I}_{n}^{2}\\frac{\\partial^{2}\\tilde{\\Psi}}{\\partial\\tilde{I}_{n}^{2}}\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq468}\n\\end{equation}\n\n\\end_inset\n\nwhere in this material \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\Psi}\\left(\\mathbf{n},\\tilde{I}_{n}\\right)=\\xi\\left(\\mathbf{n}\\right)\\left(\\tilde{I}_{n}-1\\right)^{\\beta\\left(\\mathbf{n}\\right)}\\,.\\label{eq469}\n\\end{equation}\n\n\\end_inset\n\nThe materials parameters \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n are determined from:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\xi\\left(\\mathbf{n}\\right) & =\\left(\\frac{\\cos^{2}\\theta\\sin^{2}\\varphi}{\\xi_{1}^{2}}+\\frac{\\sin^{2}\\theta\\sin^{2}\\varphi}{\\xi_{2}^{2}}+\\frac{\\cos^{2}\\varphi}{\\xi_{3}^{2}}\\right)^{-1/2}\\,,\\\\\n\\beta\\left(\\mathbf{n}\\right) & =\\left(\\frac{\\cos^{2}\\theta\\sin^{2}\\varphi}{\\beta_{1}^{2}}+\\frac{\\sin^{2}\\theta\\sin^{2}\\varphi}{\\beta_{2}^{2}}+\\frac{\\cos^{2}\\varphi}{\\beta_{3}^{2}}\\right)^{-1/2}\\,.\n\\end{aligned}\n\\label{eq470}\n\\end{equation}\n\n\\end_inset\n\nSince fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a material that acts as the ground matrix.\n The total stress is then given by the sum of the fiber stress and the ground matrix stress:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=\\tilde{\\boldsymbol{\\sigma}}_{m}+\\tilde{\\boldsymbol{\\sigma}}_{f}\\,.\\label{eq471}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFiber with Exponential Power Law Uncoupled\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Exponential-Power-UC\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model describes a constitutive model for fibers,\n where a single fiber family follows an exponential power law strain energy function.\n The deviatoric part of the Cauchy stress is given by:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=2J^{-1}H\\left(\\tilde{I}_{n}-1\\right)\\tilde{I}_{n}\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{I}_{n}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq472}\n\\end{equation}\n\n\\end_inset\n\nand the corresponding spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{C}}}=4J^{-1}H\\left(\\tilde{I}_{n}-1\\right)\\tilde{I}_{n}^{2}\\frac{\\partial^{2}\\tilde{\\Psi}}{\\partial\\tilde{I}_{n}^{2}}\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\otimes\\mathbf{n}\\,,\\label{eq473}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{I}_{n}=\\tilde{\\lambda}_{n}^{2}=\\mathbf{N}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{N}$\n\\end_inset\n\n is the square of the fiber stretch,\n \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n is the fiber orientation in the reference configuration,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\sin\\varphi\\cos\\theta\\,\\mathbf{e}_{1}+\\sin\\varphi\\sin\\theta\\,\\mathbf{e}_{2}+\\cos\\varphi\\,\\mathbf{e}_{3}\\,,\\label{eq474}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{\\tilde{F}}\\cdot\\mathbf{N}/\\tilde{\\lambda}_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $H\\left(\\cdot\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n The fiber strain energy density is given by \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\Psi}=\\frac{\\xi}{\\alpha\\beta}\\left(\\exp\\left[\\alpha\\left(\\tilde{I}_{n}-1\\right)^{\\beta}\\right]-1\\right)\\,,\\label{eq475}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\nand \n\\begin_inset Formula $\\beta\\geqslant2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nNote:\n In the limit when \n\\begin_inset Formula $\\alpha\\to0$\n\\end_inset\n\n,\n this expression produces a power law,\n \n\\begin_inset Formula \n\\begin{equation}\n\\lim\\limits_{\\alpha\\to0}\\tilde{\\Psi}=\\frac{\\xi}{\\beta}\\left(\\tilde{I}_{n}-1\\right)^{\\beta}\\,.\\label{eq476}\n\\end{equation}\n\n\\end_inset\n\nNote:\n When \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n,\n the fiber modulus is zero at the strain origin (\n\\begin_inset Formula $\\tilde{I}_{n}=1)$\n\\end_inset\n\n.\n Therefore,\n use \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\nwhen a smooth transition in the stress is desired from compression to tension.\n\\end_layout\n\n\\begin_layout Subsection\nFung Orthotropic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fung-Orthotropic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe hyperelastic strain energy function for a Fung Orthotropic material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung93,Fung79\"\nliteral \"true\"\n\n\\end_inset\n\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\Psi}=\\frac{1}{2}c\\left(e^{\\tilde{Q}}-1\\right)+U\\left(J\\right)\\,,\\label{eq477}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{Q}=c^{-1}\\sum\\limits_{a=1}^{3}\\left[2\\mu_{a}\\mathbf{M}_{a}:\\mathbf{\\tilde{E}}^{2}+\\sum\\limits_{b=1}^{3}\\lambda_{ab}\\left(\\mathbf{M}_{a}:\\mathbf{\\tilde{E}}\\right)\\left(\\mathbf{M}_{b}:\\mathbf{\\tilde{E}}\\right)\\right]\\,.\\label{eq478}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{\\tilde{E}}=\\frac{1}{2}\\left(\\mathbf{\\tilde{C}}-\\mathbf{I}\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{M}_{a}=\\mathbf{A}_{a}\\otimes\\mathbf{A}_{a}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{A}_{a}$\n\\end_inset\n\n are orthonormal vectors that define the initial direction of material axes.\n The orthotropic Lamé coefficients should be chosen such that the stiffness matrix,\n \n\\begin_inset Formula \n\\begin{equation}\n\\left[\\begin{array}{cccccc}\n\\lambda_{11}+2\\mu_{1} & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0\\\\\n\\lambda_{12} & \\lambda_{22}+2\\mu_{2} & \\lambda_{23} & 0 & 0 & 0\\\\\n\\lambda_{13} & \\lambda_{23} & \\lambda_{33}+2\\mu_{3} & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{1}+\\mu_{2}\\right) & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{2}+\\mu_{3}\\right) & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{1}+\\mu_{3}\\right)\n\\end{array}\\right]\\label{eq479}\n\\end{equation}\n\n\\end_inset\n\nis positive definite.\n\\end_layout\n\n\\begin_layout Subsection\nTension-Compression Nonlinear Orthotropic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:TC-Nonlinear-Orthotropic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model is based on the following uncoupled hyperelastic strain energy function \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07c\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(\\mathbf{C},\\lambda_{1},\\lambda_{2},\\lambda_{3}\\right)=\\tilde{\\Psi}_{\\text{iso}}\\left(\\mathbf{\\tilde{C}}\\right)+\\sum\\limits_{i=1}^{3}\\tilde{\\Psi}_{i}^{TC}\\left(\\tilde{\\lambda}_{i}\\right)+U\\left(J\\right)\\,.\\label{eq480}\n\\end{equation}\n\n\\end_inset\n\nThe isotropic strain energy \n\\begin_inset Formula $\\tilde{\\Psi}_{\\text{iso}}$\n\\end_inset\n\n and the dilatational energy \n\\begin_inset Formula $U$\n\\end_inset\n\n are the same as for the Mooney-Rivlin material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mooney-Rivlin-Hyperelasticity\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The tension-compression term is defined as follows:\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\Psi}_{i}^{TC}\\left(\\tilde{\\lambda}_{i}\\right)=\\begin{cases}\n\\xi_{i}\\left(\\tilde{\\lambda}_{i}-1\\right)^{\\beta_{i}} & \\tilde{\\lambda}_{i}>1\\\\\n0 & \\tilde{\\lambda}_{i}\\leqslant1\n\\end{cases}\\,,\\xi_{i}\\geqslant0\\quad\\mbox{(no sum over }i\\text{).}\\label{eq481}\n\\end{equation}\n\n\\end_inset\n\nThe \n\\begin_inset Formula $\\tilde{\\lambda}_{i}$\n\\end_inset\n\n parameters are the deviatoric fiber stretches of the local material fibers,\n \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\lambda}_{i}=\\left(\\mathbf{A}_{i}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{A}_{i}\\right)^{1/2}\\,.\\label{eq482}\n\\end{equation}\n\n\\end_inset\n\nThe local material fibers are defined (in the reference frame) as an orthonormal set of vectors \n\\begin_inset Formula $\\mathbf{A}_{i}$\n\\end_inset\n\n.\n The corresponding deviatoric part of the Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=J^{-1}\\sum\\limits_{i=1}^{3}\\frac{1}{\\tilde{\\lambda}_{i}}\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{\\lambda}_{i}}\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\,,\\label{eq483}\n\\end{equation}\n\n\\end_inset\n\nand the spatial elasticity tensor is \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{C}}}=J^{-1}\\sum\\limits_{i=1}^{3}\\frac{1}{\\tilde{\\lambda}_{i}}\\frac{\\partial}{\\partial\\tilde{\\lambda}_{i}}\\left(\\frac{1}{\\tilde{\\lambda}_{i}}\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{\\lambda}_{i}}\\right)\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\otimes\\mathbf{a}_{i}\\,,\\label{eq484}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{a}_{i}=\\mathbf{\\tilde{F}}\\cdot\\mathbf{A}_{i}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nHolmes-Mow Uncoupled\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Holmes-Mow-Uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe uncoupled hyperelastic strain-energy function for this material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(\\mathbf{C}\\right)=\\tilde{\\Psi}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right)\\,,\\label{eq:HMU-SED}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\Psi}\\left(\\tilde{\\mathbf{C}}\\right)=\\frac{1}{2}\\frac{\\mu}{\\beta}\\left(e^{\\tilde{Q}}-1\\right)\\,,\\label{eq:HMU-DSED}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{Q}=\\beta\\left(\\tilde{I}_{1}-3\\right)\\,.\\label{eq:HMU-Q}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the shear modulus and \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is the exponential nonlinearity coefficient.\n The corresponding spatial stress and elasticity tensors are\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}=\\frac{\\mu}{J}e^{\\tilde{Q}}\\tilde{\\mathbf{b}}\\,,\\label{eq:HMU-stress}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\mathcal{C}}}=2\\beta\\frac{\\mu}{J}e^{\\tilde{Q}}\\tilde{\\mathbf{b}}\\otimes\\tilde{\\mathbf{b}}\\label{eq:HMU-elasticity}\n\\end{equation}\n\n\\end_inset\n\nrespectively.\n Note that \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n does not reduce to zero when \n\\begin_inset Formula $\\tilde{\\mathbf{b}}=\\mathbf{I}$\n\\end_inset\n\n,\n but \n\\begin_inset Formula $\\dev\\tilde{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n does.\n These expressions can be substituted into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-Cauchy-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-spatial-elasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to evaluate the final expressions for the Cauchy stress and spatial elasticity tensors,\n respectively.\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nViscoelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Viscoelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen some of the energy stored during mechanical loading of a material gets dissipated into heat,\n the material's response is called inelastic.\n Inelastic behaviors include viscoelasticity and plasticity,\n although this presentation focuses exclusively on viscoelasticity.\n In continuum mechanics,\n we can identify three forms of viscoelasticity in materials.\n Viscoelasticity may occur due to (1) friction between molecules of the same material,\n (2) friction between solid and fluid matter in a porous deformable medium,\n and (3) breaking and reforming of weak bonds between molecules of the same material.\n The first form is classically described as the viscous response of a material,\n whereby the friction between molecules of the same material is represented by the material's viscosity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Coleman63\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The second form is classically described by poroelasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Cryer63,Rice76\"\nliteral \"false\"\n\n\\end_inset\n\n or biphasic \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mow80\"\nliteral \"false\"\n\n\\end_inset\n\n theory,\n whereby the frictional drag between the porous solid and its interstitial fluid is represented by the hydraulic permeability (also known as Darcy's law of permeation),\n see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Material\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The last form was proposed by Green and Tobolsky \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Green46,Tobolsky60\"\nliteral \"false\"\n\n\\end_inset\n\n as the fundamental mechanism for viscoelasticity in polymers.\n Recently,\n we presented a constrained reactive mixture approach to formulate a reactive viscoelasticity theory that embodies the molecular mechanism of viscoelasticity proposed by those authors \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian15,Ateshian23\"\nliteral \"false\"\n\n\\end_inset\n\n,\n see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Viscoelasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n A conventional approach for modeling viscoelasticity uses the framework of internal state variables \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Coleman67\"\nliteral \"false\"\n\n\\end_inset\n\n,\n whereby we may decompose the deformation gradient \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n into deformations occurring in a microstructural model of the material,\n consisting of a spring and dashpot in series \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lubliner85,Simo87,Holzapfel96\"\nliteral \"false\"\n\n\\end_inset\n\n,\n see Section\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Internal-State-Variable\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nInternal State Variable Theory\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Internal-State-Variable\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe may model a continuum version of the standard linear solid using quasilinear viscoelasticity,\n as shown for example by Simo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87\"\nliteral \"false\"\n\n\\end_inset\n\n,\n though we make minor modifications and extensions to his presentation here.\n Consider that the microstructural model of this viscoelastic material consists of a spring and dashpot in series (a Maxwell model),\n combined with a spring in parallel,\n which is equivalent to the Maxwell representation of a standard linear solid in linear viscoelasticity (Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:spring-dashpot-model\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The total deformation gradient,\n associated with the parallel spring,\n is \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{F}_{s}\\cdot\\mathbf{F}_{d}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{F}_{s}$\n\\end_inset\n\n is the internal state variable representing the deformation gradient associated with the series spring of the Maxwell model,\n and \n\\begin_inset Formula $\\mathbf{F}_{d}$\n\\end_inset\n\n is that associated with the dashpot.\n The second Piola-Kirchhoff stress in the parallel spring is given by\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigStandardLinearSolid.png\n\tlyxscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nSpring-dashpot model for Maxwell representation of the standard solid.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:spring-dashpot-model\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}^{e}\\left(\\mathbf{F}\\right)=\\frac{\\partial\\Psi_{r}^{e}}{\\partial\\mathbf{E}}\\,,\\label{eq:IVT-parallel-spring-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{E}=\\frac{1}{2}\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}-\\mathbf{I}\\right)$\n\\end_inset\n\n is the Lagrange strain tensor derived from \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n.\n In this quasilinear viscoelasticity theory,\n the second Piola-Kirchhoff stress in the Maxwell spring is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{M}\\left(\\mathbf{F}_{s}\\right)=\\gamma\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\right)\\,,\\label{eq:IVT-series-spring-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\gamma\\ge0$\n\\end_inset\n\n is a user-defined non-dimensional parameter that produces standard hyperelasticity in the limit when \n\\begin_inset Formula $\\gamma=0$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $\\mathbf{S}^{e}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-series-spring-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n uses the same constitutive model as in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-parallel-spring-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n but its argument is different (\n\\begin_inset Formula $\\mathbf{F}_{s}$\n\\end_inset\n\n instead of \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n).\n By recognizing that \n\\begin_inset Formula $\\mathbf{M}$\n\\end_inset\n\n should also be the stress in the Maxwell dashpot,\n we use eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-series-spring-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to find that\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{S}}^{e}\\left(\\mathbf{F}_{s}\\left(t\\right)\\right)+\\frac{1}{\\tau}\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\left(t\\right)\\right)=\\dot{\\mathbf{S}}^{e}\\left(\\mathbf{F}\\left(t\\right)\\right)\\,,\\quad\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\left(0\\right)\\right)=\\mathbf{0}\\,,\\label{eq:IVT-ODE}\n\\end{equation}\n\n\\end_inset\n\nwhere the time constant \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n is the ratio of the damping coefficient in the dashpot to the Maxwell spring stiffness,\n and the dot operator represents the material time derivative in the material frame.\n The solution to this \n\\emph on\nlinear\n\\emph default\n ordinary differential equation (hence,\n quasi\n\\emph on\n-linear\n\\emph default\n viscoelasticity theory) is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\left(t\\right)\\right)=\\int_{0}^{t}e^{-\\left(t-s\\right)/\\nu}\\frac{d\\mathbf{S}^{e}\\left(\\mathbf{F}\\left(s\\right)\\right)}{ds}\\,ds\\,.\\label{eq:IVT-P-solution}\n\\end{equation}\n\n\\end_inset\n\nNow,\n the total stress \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n in this material,\n which is the sum of stresses in the two springs,\n \n\\begin_inset Formula $\\mathbf{S}=\\mathbf{S}^{e}\\left(\\mathbf{F}\\right)+\\gamma\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\right)$\n\\end_inset\n\n,\n reduces to the familiar form\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=\\int_{0}^{t}G\\left(t-s\\right)\\frac{d\\mathbf{S}^{e}\\left(\\mathbf{F}\\left(s\\right)\\right)}{ds}\\,ds\\,,\\label{eq:IVT-total-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\nG\\left(t\\right)=1+\\gamma e^{-t/\\tau}\\label{eq:IVT-relaxation-fcn}\n\\end{equation}\n\n\\end_inset\n\nis called the relaxation function.\n A convenient and efficient numerical scheme for solving for \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-total-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n was given previously by Puso and Weiss \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso98\"\nliteral \"false\"\n\n\\end_inset\n\n,\n where they used \n\\begin_inset Formula $\\mathbf{H}$\n\\end_inset\n\n to denote \n\\begin_inset Formula $\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\right)$\n\\end_inset\n\n (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Puso-Weiss-Scheme\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Of course,\n the last step in this analysis is to evaluate the Cauchy stress from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-total-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n using the standard push-forward formula \n\\begin_inset Formula $\\boldsymbol{\\sigma}=J^{-1}\\mathbf{F}\\cdot\\mathbf{S}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe strain energy density associated with this viscoelastic material corresponds to that stored in the two springs.\n Its continuum form is\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\Psi_{r}^{e}\\left(\\mathbf{F}\\right)+\\gamma\\Psi_{r}^{e}\\left(\\mathbf{F}_{s}\\right)\\,.\\label{eq:IVT-SED}\n\\end{equation}\n\n\\end_inset\n\nIt is important to note that this presentation does not provide an explicit solution for \n\\begin_inset Formula $\\mathbf{F}_{s}\\left(t\\right)$\n\\end_inset\n\n.\n In principle,\n it is possible to extract a suitable measure of strain associated with \n\\begin_inset Formula $\\mathbf{F}_{s}\\left(t\\right)$\n\\end_inset\n\n by inverting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-P-solution\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n since \n\\begin_inset Formula $\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\left(t\\right)\\right)$\n\\end_inset\n\n must be evaluated explicitly in this scheme as part of the solution for \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-total-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This inversion requires the adoption of suitable constitutive assumptions about the multiplicative decomposition \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{F}_{s}\\cdot\\mathbf{F}_{d}$\n\\end_inset\n\n;\n it may possibly be numerically expensive.\n\\end_layout\n\n\\begin_layout Standard\nIn the context of reactive viscoelasticity (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Viscoelasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-SED\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n we can refer to \n\\begin_inset Formula $\\Psi_{r}^{e}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n as the strong bond strain energy density,\n since it persists under a non-zero strain,\n whereas \n\\begin_inset Formula $\\gamma\\Psi_{r}^{e}\\left(\\mathbf{F}_{s}\\right)$\n\\end_inset\n\n represents the weak bond strain energy density,\n since it decays to zero under a constant strain as \n\\begin_inset Formula $t\\to\\infty$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nStrain Energy Density in Standard Solid\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SED-Standard-Solid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n in the special case of a stress-relaxation response to an instantaneous step strain,\n evaluated from \n\\begin_inset Formula $\\mathbf{F}\\left(0^{+}\\right)$\n\\end_inset\n\n,\n we may let \n\\begin_inset Formula $\\mathbf{F}_{s}\\left(0^{+}\\right)=\\mathbf{F}\\left(0^{+}\\right)$\n\\end_inset\n\n since the dashpot cannot deform instantaneously,\n under the reasonable constitutive assumption that there is no rigid body rotation associated with the dashpot distinct from that of the Maxwell series spring,\n thus \n\\begin_inset Formula $\\mathbf{F}_{d}\\left(0^{+}\\right)=\\mathbf{I}$\n\\end_inset\n\n.\n If we adopt this assumption for all times \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n then we can also conclude that the right stretch tensor \n\\begin_inset Formula $\\mathbf{U}_{s}$\n\\end_inset\n\n of the Maxwell spring deformation gradient tends toward \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n in the limit as \n\\begin_inset Formula $t\\to\\infty$\n\\end_inset\n\n,\n as the dashpot elongates or contracts to match the right-stretch tensor of the parallel spring,\n also implying that \n\\begin_inset Formula $\\Psi_{r}^{e}\\left(\\mathbf{F}_{s}\\right)\\to0$\n\\end_inset\n\n in this limit.\n\\end_layout\n\n\\begin_layout Standard\nBuilding upon this example,\n we can assume more generally that \n\\begin_inset Formula $\\mathbf{U}=\\mathbf{U}_{s}\\cdot\\mathbf{U}_{d}$\n\\end_inset\n\n under the constitutive assumption that the rotation tensor \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n in the polar decomposition of \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{R}\\cdot\\mathbf{U}$\n\\end_inset\n\n is the same in both branches of the microstructural model.\n However,\n for self-consistency,\n we must also assume that \n\\begin_inset Formula $\\mathbf{U}_{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{U}_{d}$\n\\end_inset\n\n share the same eigenvectors as \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n to preserve the symmetry of all three right-stretch tensors.\n Thus,\n a valid constitutive model for the multiplicative decomposition is to let \n\\begin_inset Formula $\\mathbf{U}_{s}=\\mathbf{U}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{U}_{d}=\\mathbf{U}^{1-\\alpha}$\n\\end_inset\n\n,\n where the exponent \n\\begin_inset Formula $\\alpha\\left(t\\right)$\n\\end_inset\n\n is a scalar function of time that can be obtained by solving eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:IVT-total-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the form \n\\begin_inset Formula $\\gamma\\mathbf{S}^{e}\\left(\\mathbf{U}^{\\alpha}\\right)=\\mathbf{S}\\left(\\mathbf{U}\\right)-\\mathbf{S}^{e}\\left(\\mathbf{U}\\right)$\n\\end_inset\n\n at the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n This constitutive model is insensitive to flipping the spring and dashopt sequence in the Maxwell element,\n as should be expected.\n Now,\n for an instantaneous step strain,\n we would have \n\\begin_inset Formula $\\alpha\\left(0^{+}\\right)=1$\n\\end_inset\n\n,\n whereas the steady-state response to a step strain would produce \n\\begin_inset Formula $\\lim_{t\\to\\infty}\\alpha\\left(t\\right)=0$\n\\end_inset\n\n.\n Logically,\n it would be reasonable to expect that \n\\begin_inset Formula $\\alpha\\left(t\\right)$\n\\end_inset\n\n must remain in the range \n\\begin_inset Formula $0\\le\\alpha\\le1$\n\\end_inset\n\n,\n since we cannot physically justify that the Maxwell spring would stretch (expand or contract) by a greater amount than the parallel spring.\n A more formal enforcement of thermodynamic constraints of this internal variable theory \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Coleman67\"\nliteral \"false\"\n\n\\end_inset\n\n,\n not provided here,\n should confirm this expectation.\n\\end_layout\n\n\\begin_layout Subsubsection\nPuso and Weiss Numerical Scheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Puso-Weiss-Scheme\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere we consider the slightly more general case where the relaxation function is given by a Prony series\n\\begin_inset Formula \n\\begin{equation}\nG\\left(t\\right)=\\gamma_{0}+\\sum\\limits_{i=1}^{N}\\gamma_{i}\\exp\\left(-t/\\tau_{i}\\right)\\,.\\label{eq486}\n\\end{equation}\n\n\\end_inset\n\nWith this function chosen for the relaxation function,\n we can write the total stress as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}\\left(t\\right)=\\int\\limits_{0}^{t}\\left(\\gamma_{0}+\\sum\\limits_{i=1}^{N}\\gamma_{i}\\exp\\left(\\left(-t+s\\right)/\\tau_{i}\\right)\\frac{d\\mathbf{S}^{e}}{ds}\\right)\\,ds\\,.\\label{eq487}\n\\end{equation}\n\n\\end_inset\n\nIntroducing the internal variables,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{H}^{\\left(i\\right)}\\left(t\\right)=\\int\\limits_{0}^{t}\\exp\\left[-\\left(t-s\\right)/\\tau_{i}\\right]\\frac{d\\mathbf{S}^{e}}{ds}\\,ds\\,,\\label{eq488}\n\\end{equation}\n\n\\end_inset\n\nwe can rewrite \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq487\"\nnolink \"false\"\n\n\\end_inset\n\n as follows,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}\\left(t\\right)=\\gamma_{0}\\mathbf{S}^{e}\\left(t\\right)+\\sum\\limits_{i=1}^{N}\\gamma_{i}\\mathbf{H}^{\\left(i\\right)}\\left(t\\right)\\,.\\label{eq489}\n\\end{equation}\n\n\\end_inset\n\nIn FEBio,\n \n\\begin_inset Formula $\\gamma_{0}=1$\n\\end_inset\n\n,\n so \n\\begin_inset Formula $\\mathbf{S}^{e}$\n\\end_inset\n\n is the long-term elastic response of the material.\n\\end_layout\n\n\\begin_layout Standard\nThe question now remains how to evaluate the internal variables.\n From equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq488\"\nnolink \"false\"\n\n\\end_inset\n\n it appears that we have to integrate over the entire time domain.\n However,\n we can find a recurrence relationship that will allow us to evaluate the internal variables at a time \n\\begin_inset Formula $t+\\Delta t$\n\\end_inset\n\n given the values at time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{H}^{\\left(i\\right)}\\left(t+\\Delta t\\right) & =\\int\\limits_{0}^{t+\\Delta t}\\exp\\left[-\\left(t+\\Delta t-s\\right)/\\tau_{i}\\right]\\frac{d\\mathbf{S}^{e}}{ds}\\,ds\\\\\n & =\\exp\\left(-\\Delta t/\\tau_{i}\\right)\\int\\limits_{0}^{t}\\exp\\left[-\\left(t-s\\right)/\\tau_{i}\\right]\\frac{d\\mathbf{S}^{e}}{ds}\\,ds+\\int\\limits_{t}^{t+\\Delta t}\\exp\\left[-\\left(t+\\Delta t-s\\right)/\\tau_{i}\\right]\\frac{d\\mathbf{S}^{e}}{ds}\\,ds\\\\\n & =\\exp\\left(-\\Delta t/\\tau_{i}\\right)\\mathbf{H}^{\\left(i\\right)}\\left(t\\right)+\\int\\limits_{t}^{t+\\Delta t}\\exp\\left[-\\left(t+\\Delta t-s\\right)/\\tau_{i}\\right]\\frac{d\\mathbf{S}^{e}}{ds}\\,ds\\,.\n\\end{aligned}\n\\label{eq490}\n\\end{equation}\n\n\\end_inset\n\nThe last term can now be simplified using the midpoint rule to approximate the derivate.\n In that case we find the recurrence relation:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{H}^{\\left(i\\right)}\\left(t+\\Delta t\\right)=\\exp\\left(-\\Delta t/\\tau_{i}\\right)\\mathbf{H}^{\\left(i\\right)}\\left(t\\right)+\\frac{1-\\exp\\left(-\\Delta t/\\tau_{i}\\right)}{\\Delta t/\\tau_{i}}\\left(\\mathbf{S}^{e}\\left(t+\\Delta t\\right)-\\mathbf{S}^{e}\\left(t\\right)\\right)\\,.\\label{eq491}\n\\end{equation}\n\n\\end_inset\n\nThe following procedure can now be applied to calculate the new stress.\n Given \n\\begin_inset Formula $\\mathbf{S}_{n}^{e}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{H}_{n}^{\\left(i\\right)}$\n\\end_inset\n\n corresponding to time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n find \n\\begin_inset Formula $\\mathbf{S}_{n+1}^{e}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{H}_{n+1}^{\\left(i\\right)}$\n\\end_inset\n\n corresponding to time \n\\begin_inset Formula $t+\\Delta t$\n\\end_inset\n\n:\n\\end_layout\n\n\\begin_layout Enumerate\ncalculate elastic stress:\n \n\\begin_inset Formula \n\\[\n\\mathbf{S}_{n+1}^{e}=2\\frac{\\partial\\Psi_{r}^{e}}{\\partial\\mathbf{C}_{n+1}}\\,,\n\\]\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Enumerate\nevaluate internal variables:\n \n\\begin_inset Formula \n\\[\n\\mathbf{H}_{n+1}^{i}=\\exp\\left(-\\Delta t/\\tau_{i}\\right)\\mathbf{H}_{n}^{i}+\\frac{1-\\exp\\left(-\\Delta t/\\tau_{i}\\right)}{\\Delta t/\\tau_{i}}\\left(\\mathbf{S}_{n+1}^{e}-\\mathbf{S}_{n}^{e}\\right)\\,,\n\\]\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Enumerate\nfind the total stress:\n \n\\begin_inset Formula \n\\[\n\\mathbf{S}_{n+1}=\\gamma_{0}\\mathbf{S}_{n+1}^{e}+\\sum\\limits_{i=1}^{N}\\gamma_{i}\\mathbf{H}_{n+1}^{i}\\,.\n\\]\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nReactive Viscoelasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Viscoelasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReactive viscoelasticity models a material as a mixture of strong bonds,\n which are permanent,\n and weak bonds,\n which break and reform in response to loading \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian15,Ateshian22\"\nliteral \"true\"\n\n\\end_inset\n\n.\n This framework is based on constrained reactive mixtures of solids (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Mixture-of-Solids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Strong bonds produce the equilibrium elastic response,\n whereas weak bonds produce the transient viscous response.\n Strong bonds are in a stress-free state when in their reference configuration \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n.\n Their deformation gradient is defined as usual,\n \n\\begin_inset Formula $\\mathbf{F}\\left(\\mathbf{X},t\\right)=\\partial\\boldsymbol{\\chi}\\left(\\mathbf{X},t\\right)/\\partial\\mathbf{X}$\n\\end_inset\n\n.\n When weak bonds break in response to loading at some time \n\\begin_inset Formula $u$\n\\end_inset\n\n,\n they reform instantaneously in a stress-free configuration \n\\begin_inset Formula $\\mathbf{X}^{u}$\n\\end_inset\n\n that coincides with the current configuration at time \n\\begin_inset Formula $u$\n\\end_inset\n\n,\n thus,\n \n\\begin_inset Formula $\\mathbf{X}^{u}=\\boldsymbol{\\chi}\\left(\\mathbf{X},u\\right)$\n\\end_inset\n\n.\n Therefore,\n a reaction transforms intact loaded bonds into reformed unloaded bonds.\n Weak bonds that reform at time \n\\begin_inset Formula $u$\n\\end_inset\n\n may be called \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration bonds.\n The deformation gradient of \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration weak bonds relative to their reference configuration \n\\begin_inset Formula $\\mathbf{X}^{u}$\n\\end_inset\n\n is denoted by \n\\begin_inset Formula $\\mathbf{F}^{u}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n,\n which may be evaluated from the chain rule,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}\\left(\\mathbf{X},t\\right)=\\mathbf{F}^{u}\\left(\\mathbf{X},t\\right)\\cdot\\bar{\\mathbf{R}}\\left(\\mathbf{X},u\\right)\\cdot\\mathbf{U}\\left(\\mathbf{X},u\\right)\\,,\\label{eq492}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n is the right-stretch tensor of \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{R}\\cdot\\mathbf{U}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\bar{\\mathbf{R}}=\\left(\\mathbf{R}^{u}\\right)^{T}\\cdot\\mathbf{R}$\n\\end_inset\n\n is the relative rotation between generation \n\\begin_inset Formula $u$\n\\end_inset\n\n and the master generation.\n The strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n in a reactive viscoelastic material is given by \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{F}\\right)=\\Psi_{r}^{e}\\left(\\mathbf{F}\\right)+\\sum\\limits_{u}w^{u}\\Psi_{0}^{b}\\left(\\mathbf{F}^{u}\\right)\\,,\\label{eq493}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Psi_{r}^{e}$\n\\end_inset\n\n is the strain energy density of strong bonds and \n\\begin_inset Formula $\\Psi_{0}^{b}$\n\\end_inset\n\n is the strain energy density of weak bonds,\n when they all belong to the same generation.\n In this expression,\n \n\\begin_inset Formula $w^{u}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n is the mass fraction of \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration weak bonds,\n which evolves over time as described below.\n The summation is taken over all generations \n\\begin_inset Formula $u$\n\\end_inset\n\n that were created prior to the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n Based on eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the mixture Cauchy stress \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n in a reactive viscoelastic material is similarly given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}\\left(\\mathbf{F}^{s}\\right)=\\boldsymbol{\\sigma}^{e}\\left(\\mathbf{F}^{s}\\right)+\\sum\\limits_{u}w^{u}J^{-1}\\left(u\\right)\\boldsymbol{\\sigma}_{0}^{b}\\left(\\mathbf{F}^{u}\\right)\\,,\\label{eq494}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the stress in the strong bonds and \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}^{b}$\n\\end_inset\n\n is the stress in the weak bonds.\n These stresses are related to the respective strain energy densities of strong and weak bonds according to \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{e}\\left(\\mathbf{F}^{s}\\right)=\\frac{1}{J^{s}}\\frac{\\partial\\Psi_{r}^{e}\\left(\\mathbf{F}^{s}\\right)}{\\partial\\mathbf{F}^{s}}\\cdot\\left(\\mathbf{F}^{s}\\right)^{T},\\quad\\boldsymbol{\\sigma}_{0}^{b}\\left(\\mathbf{F}^{u}\\right)=\\frac{1}{J^{u}}\\frac{\\partial\\Psi_{0}^{b}\\left(\\mathbf{F}^{u}\\right)}{\\partial\\mathbf{F}^{u}}\\cdot\\left(\\mathbf{F}^{u}\\right)^{T}\\,.\\label{eq495}\n\\end{equation}\n\n\\end_inset\n\nThe mass fractions \n\\begin_inset Formula $w^{u}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n are obtained by solving the equation of mass balance for reactive constrained mixtures,\n \n\\begin_inset Formula \n\\begin{equation}\n\\dot{w}^{u}=\\hat{w}^{u}\\left(\\mathbf{F},w^{\\gamma}\\right)\\,,\\label{eq496}\n\\end{equation}\n\n\\end_inset\n\nwhere the mass fraction supply \n\\begin_inset Formula $\\hat{w}^{u}$\n\\end_inset\n\n must be specified as a constitutive function of the deformation gradient \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n and the mass fractions \n\\begin_inset Formula $w^{\\gamma}$\n\\end_inset\n\n from all generations.\n Since mass must be conserved over all generations,\n it follows that \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{u}\\hat{w}^{u}=0,\\quad\\sum\\limits_{u}w^{u}=1\\,.\\label{eq497}\n\\end{equation}\n\n\\end_inset\n\nAny number of valid solutions may exist for \n\\begin_inset Formula $w^{u}$\n\\end_inset\n\n,\n based on constitutive assumptions for \n\\begin_inset Formula $\\hat{w}^{u}$\n\\end_inset\n\n.\n For example,\n for \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration bonds reforming in an unloaded state during the time interval \n\\begin_inset Formula $u\\leqslant t<v$\n\\end_inset\n\n,\n and subsequently breaking in response to loading at \n\\begin_inset Formula $t=v$\n\\end_inset\n\n,\n Type I bond kinetics provides a solution of the form \n\\begin_inset Formula \n\\begin{equation}\nw^{u}\\left(\\mathbf{X},t\\right)=\\begin{cases}\n0 & t<u\\\\\nf^{u}\\left(\\mathbf{X},t\\right) & u\\leqslant t<v\\\\\nf^{u}\\left(\\mathbf{X},v\\right)g\\left(\\mathbf{F}\\left(v\\right);\\mathbf{X},t-v\\right) & v\\leqslant t\n\\end{cases}\\,,\\label{eq498}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\nf^{u}\\left(\\mathbf{X},t\\right)=1-\\sum\\limits_{\\gamma<u}w^{\\gamma}\\left(\\mathbf{X},t\\right)\\,,\\label{eq499}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $g\\left(\\mathbf{F}\\left(v\\right);\\mathbf{X},t-v\\right)$\n\\end_inset\n\n is a reduced relaxation function which may assume any number of valid forms.\n (A reduced relaxation function \n\\begin_inset Formula $g\\left(t\\right)$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $g\\left(0\\right)=1$\n\\end_inset\n\n and \n\\begin_inset Formula $g\\left(t\\to\\infty\\right)=0$\n\\end_inset\n\n,\n and decreases monotonically with \n\\begin_inset Formula $t$\n\\end_inset\n\n.) In particular,\n \n\\begin_inset Formula $g$\n\\end_inset\n\n may depend on the state of strain at time \n\\begin_inset Formula $v$\n\\end_inset\n\n when the \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration starts breaking and reforming.\n In the recursive expression of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq498\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the earliest generation \n\\begin_inset Formula $u=-\\infty$\n\\end_inset\n\n,\n which is initially at rest,\n produces \n\\begin_inset Formula $w^{u}\\left(t\\right)=1$\n\\end_inset\n\n for \n\\begin_inset Formula $t<v$\n\\end_inset\n\n and \n\\begin_inset Formula $w^{u}\\left(t\\right)=g\\left(\\mathbf{F}\\left(v\\right);\\mathbf{X},t-v\\right)$\n\\end_inset\n\n for \n\\begin_inset Formula $t\\geqslant v$\n\\end_inset\n\n;\n this latter expression seeds the recursion for subsequent generations.\n Therefore,\n providing a functional form for \n\\begin_inset Formula $g$\n\\end_inset\n\n suffices to produce the solution for all bond generations \n\\begin_inset Formula $u$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor Type II bond kinetics,\n the solution for the mass fractions is given by \n\\begin_inset Formula \n\\begin{equation}\nw^{u}\\left(t\\right)=\\begin{cases}\n0 & t<u\\\\\n1-g\\left(t-u\\right) & u\\leqslant t<v\\\\\ng\\left(t-v\\right)-g\\left(t-u\\right) & v\\leqslant t\n\\end{cases}\\,.\\label{eq500}\n\\end{equation}\n\n\\end_inset\n\nFor this type of bond kinetics,\n the reduced relaxation function \n\\begin_inset Formula $g$\n\\end_inset\n\n cannot depend on the magnitude of the strain,\n because strain-dependence might violate the constraint \n\\begin_inset Formula $0\\leqslant w^{u}\\leqslant1$\n\\end_inset\n\n.\n Thus,\n type II bond kinetics is only valid for quasilinear viscoelasticity,\n whereas type I bond kinetics also encompasses nonlinear viscoelasticity.\n\\end_layout\n\n\\begin_layout Standard\nFor all bond kinetics,\n it is also possible to constrain the occurrence of the breaking-and-reforming reaction to specific forms of the strain.\n For example,\n the reaction may be allowed to proceed only in the case of dilatational strain,\n or only in the case of distortional strain.\n\\end_layout\n\n\\begin_layout Standard\nThe finite element implementation of reactive viscoelasticity stores the value of time \n\\begin_inset Formula $v$\n\\end_inset\n\n,\n mass fraction of reformed bonds \n\\begin_inset Formula $f^{u}\\left(\\mathbf{X},v\\right)$\n\\end_inset\n\n,\n and the right stretch tensor \n\\begin_inset Formula $\\mathbf{U}\\left(\\mathbf{X},v\\right)$\n\\end_inset\n\n needed to evaluate \n\\begin_inset Formula $w^{u}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq498\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{F}^{v}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq492\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nConstitutive Model for \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constitutive-Model-Rbar\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn reactive viscoelasticity we adopt the constitutive assumption that \n\\begin_inset Formula $\\mathbf{U}^{u}=\\mathbf{I}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\mathbf{F}^{u}=\\mathbf{R}^{u}$\n\\end_inset\n\n,\n at time \n\\begin_inset Formula $t^{u}$\n\\end_inset\n\n when generation \n\\begin_inset Formula $u$\n\\end_inset\n\n must come into existence in a stress-free state.\n Since \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq492\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n must be a proper orthogonal transformation,\n we may select\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\bar{\\mathbf{R}} & =\\mathbf{I}\\,.\\end{aligned}\n\\label{eq:A-R-identity}\n\\end{equation}\n\n\\end_inset\n\nSince this choice is invariant to any transformation \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n it remains valid for all material symmetries,\n ranging from triclinic to isotropic.\n Therefore,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq492\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n now takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}^{u}\\left(\\mathbf{X},t\\right)=\\mathbf{F}\\left(\\mathbf{X},t\\right)\\cdot\\mathbf{U}^{-1}\\left(\\mathbf{X},u\\right)\\label{eq:rv-Fu-model}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n is the right stretch tensor of \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThere remains one special case that must be addressed with a separate choice for \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n.\n In biomechanics we often find it convenient to model fibrous or fibrillar materials using one-dimensional fibers that can only sustain tension.\n Typically,\n such fibers are represented with a strain energy density function\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\Psi_{0}\\left(\\mathbf{C}\\right) & =H\\left(I_{n}-1\\right)\\Psi_{n}\\left(I_{n}\\right)\\,, & I_{n} & =\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}\\end{aligned}\n\\label{eq:A-fiber-models}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the unit vector along the fiber in its reference configuration,\n and \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n is the square of the stretch ratio along the fiber.\n The Heaviside unit step function \n\\begin_inset Formula $H\\left(I_{n}-1\\right)$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:A-fiber-models\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n ensures that the fiber contributes strain energy only when it is under tension (\n\\begin_inset Formula $I_{n}>1$\n\\end_inset\n\n);\n thus,\n the constitutive model \n\\begin_inset Formula $\\Psi_{n}\\left(I_{n}\\right)$\n\\end_inset\n\n for the tensile response of the fiber must reduce to zero when \n\\begin_inset Formula $I_{n}=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn the reactive viscoelasticity framework,\n each generation \n\\begin_inset Formula $u$\n\\end_inset\n\n comes into existence at time \n\\begin_inset Formula $t^{u}$\n\\end_inset\n\n,\n thus the constitutive model for the fiber must account for the fact that the fiber orientation is no longer along \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n at time \n\\begin_inset Formula $t^{u}$\n\\end_inset\n\n.\n Indeed,\n the total weak bond free energy in a reactive viscoelastic material now reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\sum_{u}w^{u}\\Psi_{0}^{b}\\left(\\mathbf{F}^{u}\\left(t\\right)\\right) & =\\sum_{u}w^{u}H\\left(I_{n}^{u}-1\\right)\\Psi_{n}\\left(I_{n}^{u}\\right)\\\\\nI_{n}^{u} & =\\mathbf{n}_{r}^{u}\\cdot\\mathbf{C}^{u}\\cdot\\mathbf{n}_{r}^{u}\n\\end{aligned}\n\\label{eq:A-fiber-SED}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}_{r}^{u}$\n\\end_inset\n\n is the fiber orientation at time \n\\begin_inset Formula $t^{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{n}^{u}$\n\\end_inset\n\n is the square of the stretch ratio of the fiber relative to its reference configuration at time \n\\begin_inset Formula $t^{u}$\n\\end_inset\n\n.\n Recall that the elemental line along \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n gets transformed in the material frame at any time \n\\begin_inset Formula $t$\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\lambda_{n}\\mathbf{n}=\\mathbf{U}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\lambda_{n} & =\\sqrt{\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}}\\,, & \\mathbf{n} & =\\frac{1}{\\lambda_{n}}\\mathbf{U}\\cdot\\mathbf{n}_{r}\\end{aligned}\n\\label{eq:A-fiber-current}\n\\end{equation}\n\n\\end_inset\n\nAt time \n\\begin_inset Formula $t^{u}$\n\\end_inset\n\n when generation \n\\begin_inset Formula $u$\n\\end_inset\n\n forms in a stress-free state,\n it follows that the fiber material is now based on the orientation \n\\begin_inset Formula $\\mathbf{n}_{r}^{u}\\equiv\\mathbf{n}\\left(t^{u}\\right)$\n\\end_inset\n\n,\n evaluated using \n\\begin_inset Formula $\\mathbf{U}\\left(\\mathbf{X},t^{u}\\right)$\n\\end_inset\n\n as shown in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:A-fiber-current\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In general,\n \n\\begin_inset Formula $\\mathbf{n}_{r}^{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n need not be collinear,\n therefore we can find the rotation \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n that transforms \n\\begin_inset Formula $\\mathbf{n}_{r}^{u}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\bar{\\mathbf{R}}=\\left(1-\\cos\\gamma\\right)\\mathbf{m}\\otimes\\mathbf{m}+\\cos\\gamma\\mathbf{I}-\\mathbb{E}\\cdot\\left(\\sin\\gamma\\,\\mathbf{m}\\right)\\label{eq:A-R-fiber}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula $\\cos\\gamma=\\mathbf{n}_{r}^{u}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\sin\\gamma\\,\\mathbf{m}=\\mathbf{n}_{r}^{u}\\times\\mathbf{n}_{r}$\n\\end_inset\n\n.\n In this expression,\n \n\\begin_inset Formula $\\mathbf{m}$\n\\end_inset\n\n is the unit vector along the rotation axis,\n \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is the rotation angle about the axis,\n and \n\\begin_inset Formula $\\mathbb{E}$\n\\end_inset\n\n represents the (pseudo-)third-order permutation tensor whose components are equal to the permutation symbol \n\\begin_inset Formula $\\varepsilon_{ijk}$\n\\end_inset\n\n.\n Thus,\n \n\\begin_inset Formula $-\\mathbb{E}\\cdot\\boldsymbol{\\omega}$\n\\end_inset\n\n is the antisymmetric second-order tensor \n\\begin_inset Formula $\\boldsymbol{\\Omega}$\n\\end_inset\n\n whose dual vector is \n\\begin_inset Formula $\\boldsymbol{\\omega}$\n\\end_inset\n\n,\n from which it follows that \n\\begin_inset Formula $\\boldsymbol{\\Omega}\\cdot\\mathbf{a}=\\boldsymbol{\\omega}\\times\\mathbf{a}$\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n.\n Here again,\n since \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n represents a relative rotation between the material vectors \n\\begin_inset Formula $\\mathbf{n}_{r}^{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n,\n it is invariant to any transformation \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn practice it is not necessary to evaluate \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:A-R-fiber\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for each generation \n\\begin_inset Formula $u$\n\\end_inset\n\n of each fiber in a material model;\n instead one can reset the fiber direction from \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\mathbf{n}_{r}^{u}=\\mathbf{n}\\left(u\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is evaluated as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:A-fiber-current\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n requiring only the storage of \n\\begin_inset Formula $\\mathbf{U}\\left(\\mathbf{X},u\\right)$\n\\end_inset\n\n for each generation \n\\begin_inset Formula $u$\n\\end_inset\n\n.\n This scheme may also be used with continuous fiber distributions \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hou16\"\nliteral \"false\"\n\n\\end_inset\n\n.\n As explained in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian22\"\nliteral \"false\"\n\n\\end_inset\n\n,\n this constitutive model for \n\\begin_inset Formula $\\bar{\\mathbf{R}}$\n\\end_inset\n\n ensures that cyclical loading of a reactive viscoelastic fibrous material never produces compressive fiber stresses.\n\\end_layout\n\n\\begin_layout Subsubsection\nReduced Relaxation Functions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Reduced-Relaxation-Functions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReduced relaxation functions are monotonically decreasing functions of time \n\\begin_inset Formula $g\\left(t\\right)$\n\\end_inset\n\n that satisfy \n\\begin_inset Formula $g\\left(0\\right)=1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lim_{t\\to\\infty}g\\left(t\\right)=0$\n\\end_inset\n\n.\n Many different forms of reduced relaxation functions are available in FEBio,\n given in the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio User's Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n The simplest and most commonly used relaxation function is the exponential function \n\\begin_inset Formula $g\\left(t\\right)=e^{-t/\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n is the relaxation constant.\n In viscoelasticity theory it is common to use a combination of relaxation functions with distinct relaxation constants \n\\begin_inset Formula $\\tau_{i}$\n\\end_inset\n\n,\n described as a Prony series of the form \n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=\\frac{\\sum_{i}\\gamma_{i}e^{-t/\\tau_{i}}}{\\sum_{i}\\gamma_{i}}\\,.\\label{eq:rrf-Prony}\n\\end{equation}\n\n\\end_inset\n\nThe coefficients \n\\begin_inset Formula $\\gamma_{i}$\n\\end_inset\n\n are normalized by \n\\begin_inset Formula $\\sum_{i}\\gamma_{i}$\n\\end_inset\n\n to enforce \n\\begin_inset Formula $g\\left(0\\right)=1$\n\\end_inset\n\n.\n Alternatively,\n we could have written\n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=\\sum_{i}\\hat{\\gamma}_{i}e^{-t/\\tau_{i}}\\,,\\quad\\sum_{i}\\hat{\\gamma}_{i}=1\\,.\\label{eq:rrf-Prony-alt}\n\\end{equation}\n\n\\end_inset\n\nThis type of relaxation function \n\\begin_inset Formula $g\\left(t\\right)$\n\\end_inset\n\n is said to have a discrete spectrum of coefficients \n\\begin_inset Formula $\\hat{\\gamma}_{i}$\n\\end_inset\n\n corresponding to each \n\\begin_inset Formula $\\tau_{i}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIt is also possible to define a continuous relaxation spectrum \n\\begin_inset Formula $\\hat{\\gamma}\\left(\\tau\\right)$\n\\end_inset\n\n such that the reduced relaxation function is given by\n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=\\int_{0}^{\\infty}\\hat{\\gamma}\\left(\\tau\\right)e^{-t/\\tau}\\,d\\tau\\,.\\label{eq:rrf-g-continuous-spectrum}\n\\end{equation}\n\n\\end_inset\n\nTo satisfy \n\\begin_inset Formula $g\\left(0\\right)=1$\n\\end_inset\n\n the continous relaxation spectrum \n\\begin_inset Formula $\\hat{\\gamma}\\left(\\tau\\right)$\n\\end_inset\n\n must satisfy\n\\begin_inset Formula \n\\begin{equation}\n\\int_{0}^{\\infty}\\hat{\\gamma}\\left(\\tau\\right)\\,d\\tau=1\\,.\\label{eq:rrf-continuous-spectrum-constraint}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n Fung \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung72\"\nliteral \"false\"\n\n\\end_inset\n\n proposed a relaxation spectrum of the form\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\gamma}\\left(\\tau\\right)=\\begin{cases}\n\\frac{1}{\\ln\\frac{\\tau_{2}}{\\tau_{1}}}\\frac{1}{\\tau} & \\tau_{1}\\le\\tau\\le\\tau_{2}\\\\\n0 & \\text{otherwise}\n\\end{cases}\\,.\\label{eq:rrf-Fung-spectrum-72}\n\\end{equation}\n\n\\end_inset\n\nWhen substituted into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-g-continuous-spectrum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n it produces\n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=\\frac{\\Gamma\\left(0,\\frac{t}{\\tau_{2}}\\right)-\\Gamma\\left(0,\\frac{t}{\\tau_{1}}\\right)}{\\ln\\frac{\\tau_{2}}{\\tau_{1}}}=\\frac{-\\Ei\\left(-\\frac{t}{\\tau_{2}}\\right)+\\Ei\\left(-\\frac{t}{\\tau_{1}}\\right)}{\\ln\\frac{\\tau_{2}}{\\tau_{1}}}\\,,\\label{eq:rrf-Fung-72}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Gamma\\left(a,z\\right)$\n\\end_inset\n\n is the incomplete gamma function and \n\\begin_inset Formula $\\Ei\\left(z\\right)$\n\\end_inset\n\n is the exponential integral function,\n which satisfy \n\\begin_inset Formula $\\Gamma\\left(0,z\\right)=-\\Ei\\left(-z\\right)$\n\\end_inset\n\n.\n An alternative model proposed later by Fung \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung81\"\nliteral \"false\"\n\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\gamma}\\left(\\tau\\right)=\\begin{cases}\n\\frac{1}{\\tau_{2}-\\tau_{1}} & \\tau_{1}\\le\\tau\\le\\tau_{2}\\\\\n0 & \\text{otherwise}\n\\end{cases}\\,,\\label{eq:rrf-Fung-spectrum-81}\n\\end{equation}\n\n\\end_inset\n\nwhich produces\n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=\\frac{\\tau_{2}e^{-t/\\tau_{2}}-\\tau_{1}e^{-t/\\tau_{1}}+t\\left[\\Ei\\left(-\\frac{t}{\\tau_{2}}\\right)-\\Ei\\left(-\\frac{t}{\\tau_{1}}\\right)\\right]}{\\tau_{2}-\\tau_{1}}\\,.\\label{eq:rrf-Fung-81}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA generalization of Fung's earlier continuous relaxation spectrum may be derived from the work of Malkin \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Malkin06\"\nliteral \"false\"\n\n\\end_inset\n\n who proposed to use a function proportional to \n\\begin_inset Formula $\\tau^{-\\beta}$\n\\end_inset\n\n.\n If we constrain this spectrum to the range \n\\begin_inset Formula $\\tau_{1}\\le\\tau\\le\\tau_{2}$\n\\end_inset\n\n it takes the form (Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:Malkin-relaxation-spectrum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigRelaxationSpectrumMalkin.png\n\tlyxscale 50\n\twidth 4in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nContinuous relaxation spectrum \n\\begin_inset Formula $\\hat{\\gamma}\\left(\\tau\\right)$\n\\end_inset\n\n of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-Malkin-spectrum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n based on the work of Malkin \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Malkin06\"\nliteral \"false\"\n\n\\end_inset\n\n,\n for two representative values of \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:Malkin-relaxation-spectrum\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\gamma}\\left(\\tau\\right)=\\begin{cases}\n\\frac{\\beta-1}{\\tau_{1}^{1-\\beta}-\\tau_{2}^{1-\\beta}}\\frac{1}{\\tau^{\\beta}} & \\tau_{1}\\le\\tau\\le\\tau_{2}\\\\\n0 & \\text{otherwise}\n\\end{cases}\\,.\\label{eq:rrf-Malkin-spectrum}\n\\end{equation}\n\n\\end_inset\n\nWhen substituted into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-g-continuous-spectrum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n this continuous relaxation spectrum produces the reduced relaxation function (Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:Malkin-reduced-relaxation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigRelaxationMalkin.png\n\tlyxscale 50\n\twidth 4in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nMalkin's reduced relaxation function \n\\begin_inset Formula $g\\left(t\\right)$\n\\end_inset\n\n,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-Malkin\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n for \n\\begin_inset Formula $\\tau_{1}=10^{-2}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tau_{2}=10^{3}$\n\\end_inset\n\n,\n for two representative values of \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:Malkin-reduced-relaxation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=\\frac{\\left(\\beta-1\\right)t^{1-\\beta}}{\\tau_{1}^{1-\\beta}-\\tau_{2}^{1-\\beta}}\\left[\\Gamma\\left(\\beta-1,\\frac{t}{\\tau_{2}}\\right)-\\Gamma\\left(\\beta-1,\\frac{t}{\\tau_{1}}\\right)\\right]\\,.\\label{eq:rrf-Malkin}\n\\end{equation}\n\n\\end_inset\n\nIn the limit as \n\\begin_inset Formula $\\beta\\to1$\n\\end_inset\n\n,\n the expression of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-Malkin\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-Fung-72\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For proper evaluation of the \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n function we must have \n\\begin_inset Formula $\\beta\\ge1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nAnother example for a continuous relaxation spectrum is the exponential spectrum (Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:exponential-relaxation-spectrum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigRelaxationSpectrumExponential.png\n\tlyxscale 50\n\twidth 4in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nContinuous exponential relaxation spectrum \n\\begin_inset Formula $\\hat{\\gamma}\\left(\\tau\\right)$\n\\end_inset\n\n of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-exponential-spectrum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n for three representative values of \n\\begin_inset Formula $\\tau_{0}$\n\\end_inset\n\n.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:exponential-relaxation-spectrum\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\gamma}\\left(\\tau\\right)=\\frac{1}{\\tau_{0}}e^{-\\tau/\\tau_{0}}\\,,\\quad0\\le\\tau<\\infty\\,,\\label{eq:rrf-exponential-spectrum}\n\\end{equation}\n\n\\end_inset\n\nwhich produces\n\\begin_inset Formula \n\\begin{equation}\ng\\left(t\\right)=2\\sqrt{\\frac{t}{\\tau_{0}}}K_{1}\\left(2\\sqrt{\\frac{t}{\\tau_{0}}}\\right)\\,,\\label{eq:rrf-exponential}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K_{1}\\left(z\\right)$\n\\end_inset\n\n is the modified Bessel function of the second kind,\n of order 1 (Figure \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"fig:relaxation-continuous-exponential\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigContinuousExponentialRelaxation.png\n\tlyxscale 50\n\twidth 5in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nReduced relaxation function \n\\begin_inset Formula $g\\left(t\\right)$\n\\end_inset\n\n of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rrf-exponential\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for continuous exponential relaxation spectrum,\n for three representative values of \n\\begin_inset Formula $\\tau_{0}$\n\\end_inset\n\n.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:relaxation-continuous-exponential\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nViscous Friction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Viscous-Friction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress in a viscous material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Coleman63\"\nliteral \"false\"\n\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\boldsymbol{\\sigma}^{e}+\\boldsymbol{\\tau}\\,,\\label{eq:viscous-friction-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the elastic part of the stress as given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hyperelastic-stress-C\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the viscous stress,\n which depends on the rate of deformation tensor \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n (the symmetric part of the velocity gradient).\n For example,\n in an isotropic Newtonian viscous response,\n \n\\begin_inset Formula $\\boldsymbol{\\tau}=\\lambda\\left(\\text{tr}\\mathbf{D}\\right)\\mathbf{I}+2\\mu\\mathbf{D}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are viscosity coefficients.\n Since \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n reduces to zero under static loading (when \n\\begin_inset Formula $\\mathbf{D}=\\mathbf{0}$\n\\end_inset\n\n),\n it follows that \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the equilibrium response of this type of viscoelastic material.\n For a viscous fluid,\n the elastic stress tensor simplifies to \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}=-p\\mathbf{I}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure.\n In a compressible fluid,\n \n\\begin_inset Formula $p$\n\\end_inset\n\n is a function of the volumetric strain (e.g.,\n a function of \n\\begin_inset Formula $J$\n\\end_inset\n\n) and the absolute temperature.\n\\end_layout\n\n\\begin_layout Standard\nBy the nature of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-friction-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we may interpret this type of viscous material microscopically as a spring and dashpot in parallel,\n often called a Voigt model in linear viscoelasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bland16,Coleman61\"\nliteral \"false\"\n\n\\end_inset\n\n,\n though the general continuum mechanics expression of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-friction-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may encompass nonlinear behaviors for \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n and for \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n.\n The energy density dissipated in this type of material is \n\\begin_inset Formula $\\boldsymbol{\\tau}:\\mathbf{D}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThough we include this type of viscoelastic model here for completeness,\n in practice,\n investigators in biomechanics have rarely adopted this model to describe biological tissues,\n because it predicts that the stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n becomes infinite under a step increase in strain.\n\\end_layout\n\n\\begin_layout Section\nReactive Damage Mechanics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Damage-Mechanics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nBond-Breaking Reaction\n\\end_layout\n\n\\begin_layout Standard\nThe reactive damage mechanics framework was first described in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Nims16\"\nliteral \"false\"\n\n\\end_inset\n\n.\n It is based on constrained reactive mixtures of solids (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Mixture-of-Solids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) and used to model damage in an elastic solid as a reaction that transforms intact (elastic) bonds into broken bonds,\n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{E}^{i}\\to\\mathcal{E}^{b}\\,.\\label{eq:dmg-reaction}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathcal{E}^{\\alpha}$\n\\end_inset\n\n is the material associated with bonds \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha=i$\n\\end_inset\n\n for intact bonds and \n\\begin_inset Formula $\\alpha=b$\n\\end_inset\n\n for broken bonds).\n The material is modeled as a constrained mixture of these two constituents \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n Whereas intact bonds may store free energy,\n broken bond sustain none.\n This framework assumes that isothermal conditions prevail.\n Thus,\n any heat generated by the dissipative damage reaction must be radiated from the continuum to preserve a constant temperature.\n In an isothermal framework,\n the free energy density is also equal to the strain energy density.\n\\end_layout\n\n\\begin_layout Standard\nThe referential mass density of the solid mixture is \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n (mass of solid per volume in its referential,\n stress-free configuration),\n which remains constant throughout an analysis.\n The material associated with intact bonds has an apparent mass density \n\\begin_inset Formula $\\rho_{r}^{i}$\n\\end_inset\n\n while that associated with broken bonds is \n\\begin_inset Formula $\\rho_{r}^{b},$\n\\end_inset\n\nsuch that the mixture mass balance is satisfied by\n\\begin_inset Formula \n\\begin{equation}\n\\rho_{r}=\\rho_{r}^{i}+\\rho_{r}^{b}\\,.\\label{eq:dmg-mass-balance}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nStrain Energy Density and Stress\n\\end_layout\n\n\\begin_layout Standard\nLet the specific free energy stored in intact bonds be represented by \n\\begin_inset Formula $\\psi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n;\n that of broken bonds is zero.\n Therefore,\n the free energy density of the mixture is\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{F}\\right)=\\rho_{r}^{i}\\psi\\left(\\mathbf{F}\\right)\\,.\\label{eq:dmg-FED}\n\\end{equation}\n\n\\end_inset\n\nWe may define the mass fraction \n\\begin_inset Formula $w^{\\alpha}$\n\\end_inset\n\n of bond species \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\nw^{\\alpha}=\\frac{\\rho_{r}^{\\alpha}}{\\rho_{r}}\\,.\\label{eq:dmg-mass-fraction}\n\\end{equation}\n\n\\end_inset\n\nNow,\n the mixture mass balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten as \n\\begin_inset Formula $\\sum_{\\alpha}w^{\\alpha}=1$\n\\end_inset\n\n,\n or more specifically,\n\\begin_inset Formula \n\\begin{equation}\nw^{i}+w^{b}=1\\,.\\label{eq:dmg-massfraction-balance}\n\\end{equation}\n\n\\end_inset\n\nWe may also rewrite the mixture free energy density in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-FED\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{F}\\right)=w^{i}\\rho_{r}\\psi\\left(\\mathbf{F}\\right)=\\left(1-w^{b}\\right)\\rho_{r}\\psi\\left(\\mathbf{F}\\right)\\,,\\label{eq:dmg-FED-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere we have made use of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-massfraction-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The corresponding Cauchy stress may be evaluated using the standard hyperelasticity formula,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=J^{-1}\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{F}}\\cdot\\mathbf{F}^{T}=\\left(1-w^{b}\\right)\\frac{\\rho_{r}}{J}\\frac{\\partial\\psi}{\\partial\\mathbf{F}}\\cdot\\mathbf{F}^{T}.\\label{eq:dmg-stress}\n\\end{equation}\n\n\\end_inset\n\nThese relation show that the free energy density and stress of a damaged material are scaled by the mass fraction \n\\begin_inset Formula $w^{i}=1-w^{b}$\n\\end_inset\n\n of remaining intact bonds.\n Comparing these formulas to those of classical damage mechanics \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Kachanov58,Rabotnov80,Chaboche81,Lemaitre84,Lemaitre85,Simo87a\"\nliteral \"false\"\n\n\\end_inset\n\n,\n it becomes immediately apparent that the classical damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n appearing in those theories is equivalent to the mass fraction \n\\begin_inset Formula $w^{b}$\n\\end_inset\n\n of broken bonds,\n\\begin_inset Formula \n\\begin{equation}\nD\\equiv w^{b}.\\label{eq:dmg-variable}\n\\end{equation}\n\n\\end_inset\n\nTo further clarify this equivalence,\n we may let \n\\begin_inset Formula $\\Psi_{0}\\equiv\\rho_{r}\\psi$\n\\end_inset\n\n represent the free energy density of an intact elastic solid,\n such that eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-FED-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten as \n\\begin_inset Formula $\\Psi_{r}=\\left(1-D\\right)\\Psi_{0}$\n\\end_inset\n\n.\n Similarly,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten as \n\\begin_inset Formula $\\boldsymbol{\\sigma}=\\left(1-D\\right)\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n is the stress in the intact elastic solid,\n derived from the hyperelasticity relation \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}=J^{-1}\\left(\\partial\\Psi_{0}/\\partial\\mathbf{F}\\right)\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor nearly-incompressible hyperelastic materials (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Nearly-Incompressible-Hyperelast\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n the strain energy density of the intact material has the form of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-SED\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\Psi_{0}\\left(\\mathbf{C}\\right)=\\tilde{\\Psi}_{0}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right)$\n\\end_inset\n\n.\n In this case,\n we assume that the damage only affects the distortional part of the strain energy density \n\\begin_inset Formula $\\tilde{\\Psi}_{0}\\left(\\tilde{\\mathbf{C}}\\right)$\n\\end_inset\n\n,\n consistent with the general framework advocated in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holzapfel00\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Thus,\n for \n\\emph on\nuncoupled damage\n\\emph default\n,\n we assume that \n\\begin_inset Formula $\\Psi_{r}\\left(\\mathbf{C}\\right)=\\left(1-D\\right)\\tilde{\\Psi}_{0}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right)$\n\\end_inset\n\n.\n The resulting damage stress similarly takes the form \n\\begin_inset Formula $\\boldsymbol{\\sigma}=\\left(1-D\\right)\\dev\\tilde{\\boldsymbol{\\sigma}}_{0}+p\\mathbf{I}$\n\\end_inset\n\n,\n consistent with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-Cauchy-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{0}$\n\\end_inset\n\n is evaluated from \n\\begin_inset Formula $\\tilde{\\Psi}_{0}\\left(\\tilde{\\mathbf{C}}\\right)$\n\\end_inset\n\n as given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-Cauchy-stress-tilde\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset Formula $p$\n\\end_inset\n\n is evaluated from \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n as given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:UC-p\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen investigating the damage mechanics of tension-bearing fibrous materials,\n described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Tension-Bearing-Fiber-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it is important to use the unconstrained version of the fiber and damage mechanics models,\n even when the fibers are embedded in a ground matrix with a nearly-incompressible response (uncoupled formulation).\n This is a necessary requirement since uncoupled fiber formulations are now understood to be non-physical.\n Nevertheless,\n for historical reasons,\n FEBio allows users to use uncoupled fiber formulations in an uncoupled damage material.\n\\end_layout\n\n\\begin_layout Subsection\nDamage Criterion\n\\end_layout\n\n\\begin_layout Standard\nAt each material point \n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n in the continuum,\n damage occurs when a scalar damage (or failure) measure \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n achieves a critical value \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n over the loading history,\n\\begin_inset Formula \n\\begin{equation}\n\\Xi_{m}\\left(\\mathbf{X}\\right)=\\max_{-\\infty<s\\le t}\\Xi\\left(\\mathbf{F}\\left(\\mathbf{X},s\\right)\\right)\\,.\\label{eq:dmg-critical-measure}\n\\end{equation}\n\n\\end_inset\n\nThe scalar damage measure \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n must be invariant to orthogonal transformations \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n that preserve material symmetry,\n or else the damage formulation would not be observer-independent.\n For example,\n for isotropic materials,\n \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n must be an isotropic function of the deformation,\n in which case it should be expressed as \n\\begin_inset Formula $\\Xi\\left(\\mathbf{U}\\right)$\n\\end_inset\n\n or \n\\begin_inset Formula $\\Xi\\left(\\mathbf{E}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n is the right stretch tensor in the polar decomposition \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{R}\\cdot\\mathbf{U}$\n\\end_inset\n\n of the deformation gradient,\n and\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{E}=\\frac{1}{2}\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}-\\mathbf{I}\\right)=\\frac{1}{2}\\left(\\mathbf{U}^{2}-\\mathbf{I}\\right)\\label{eq:Lagrange-strain-tensor}\n\\end{equation}\n\n\\end_inset\n\nis the Green-Lagrange strain tensor.\n It follows that\n\\begin_inset Formula \n\\[\n\\left(\\frac{\\partial\\mathbf{E}}{\\partial\\mathbf{U}}\\right)_{ijmn}=\\frac{1}{2}\\left(\\frac{1}{2}\\left(\\delta_{im}U_{nj}+\\delta_{in}U_{mj}\\right)+\\frac{1}{2}\\left(U_{im}\\delta_{jn}+U_{in}\\delta_{jm}\\right)\\right)\\,.\n\\]\n\n\\end_inset\n\nFor anisotropic materials where \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is the unit normal to a symmetry plane and \n\\begin_inset Formula $\\mathbf{A}=\\mathbf{a}\\otimes\\mathbf{a}$\n\\end_inset\n\n,\n the damage measure \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n must satisfy\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U},\\mathbf{A}\\right)=\\Xi\\left(\\mathbf{Q}\\cdot\\mathbf{U}\\cdot\\mathbf{Q}^{T},\\mathbf{Q}\\cdot\\mathbf{A}\\cdot\\mathbf{Q}^{T}\\right)\\,,\\label{eq:dmg-criterion-invariance}\n\\end{equation}\n\n\\end_inset\n\nfor transformations \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n that satisfy \n\\begin_inset Formula $\\mathbf{Q}\\cdot\\mathbf{A}\\cdot\\mathbf{Q}^{T}=\\mathbf{A}$\n\\end_inset\n\n (or \n\\begin_inset Formula $\\mathbf{Q}\\cdot\\mathbf{a}=\\mathbf{a}$\n\\end_inset\n\n).\n We may replace \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n with \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n in the above expression.\n\\end_layout\n\n\\begin_layout Standard\nWe assume that the amount of damage (the fraction \n\\begin_inset Formula $D$\n\\end_inset\n\n of broken bonds) is given by the function of state\n\\begin_inset Formula \n\\begin{equation}\nD=F\\left(\\Xi_{m}\\right)\\,,\\label{eq:dmg-CDF}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $0\\le F\\left(\\Xi_{m}\\right)\\le1$\n\\end_inset\n\n.\n As shown in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Nims16\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the Clausius-Duhem inequality imposes the constraint that \n\\begin_inset Formula $F\\left(\\Xi_{m}\\right)$\n\\end_inset\n\n must be a monotonically increasing function of its argument.\n Therefore,\n we may understand \n\\begin_inset Formula $F$\n\\end_inset\n\n to represent a cumulative density function (CDF),\n whose derivative \n\\begin_inset Formula $f\\left(\\Xi_{m}\\right)=F^{\\prime}\\left(\\Xi_{m}\\right)$\n\\end_inset\n\n is a probability distribution function (PDF) that describes the probability of bonds breaking at the specific threshold \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nReaction Kinetics and Thermodynamics\n\\end_layout\n\n\\begin_layout Standard\nThe axiom of mass balance in a reactive constrained mixture reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\rho}_{r}^{\\alpha}=\\hat{\\rho}_{r}^{\\alpha}\\,,\\label{eq:dmg-mass-balance-alpha}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dot{\\rho}_{r}^{\\alpha}$\n\\end_inset\n\n is the material time derivative of \n\\begin_inset Formula $\\rho_{r}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\alpha}$\n\\end_inset\n\n is a function of state representing the referential mass supply density to constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n due to reactions with all other constituents.\n In the damage framework,\n the above relations show that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\rho_{r}^{i} & =\\rho_{r}\\left(1-F\\left(\\Xi_{m}\\right)\\right)\\\\\n\\rho_{r}^{b} & =\\rho_{r}F\\left(\\Xi_{m}\\right)\n\\end{aligned}\n\\,.\\label{eq:dmg-mass-densities}\n\\end{equation}\n\n\\end_inset\n\nSubstituting these expressions into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-mass-balance-alpha\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n shows that the referential mass density supplies are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\hat{\\rho}_{r}^{i} & =-\\rho_{r}\\dot{F}\\left(\\Xi_{m}\\right)\\\\\n\\hat{\\rho}_{r}^{b} & =\\rho_{r}\\dot{F}\\left(\\Xi_{m}\\right)\n\\end{aligned}\n\\,,\\label{eq:dmg-mass-supplies}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\dot{F}\\left(\\Xi_{m}\\right)=\\begin{cases}\n\\left.f\\left(\\Xi\\right)\\dot{\\Xi}\\right|_{\\Xi_{m}} & \\text{advancing damage}\\\\\n0 & \\text{otherwise}\n\\end{cases}\\,.\\label{eq:dmg-CDF-mtd}\n\\end{equation}\n\n\\end_inset\n\nIn these expressions,\n the damage is advancing when \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n increases over two consecutive time points.\n In this expression for \n\\begin_inset Formula $\\dot{F}$\n\\end_inset\n\n we need to evaluate\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\Xi}\\left(\\mathbf{U}\\right)=\\frac{\\partial\\Xi}{\\partial\\mathbf{U}}:\\dot{\\mathbf{U}}=\\mathbf{N}:\\dot{\\mathbf{U}}\\,,\\label{eq:dmg-Xsi-dot}\n\\end{equation}\n\n\\end_inset\n\nwhere we defined \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}\\equiv\\frac{\\partial\\Xi}{\\partial\\mathbf{U}}\\,\\label{eq:dmg-surface-normal}\n\\end{equation}\n\n\\end_inset\n\nto represent the tensorial normal to the damage hypersurface,\n which needs to be evaluated at \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn this isothermal damage framework it can be shown from the energy balance that a heat supply density \n\\begin_inset Formula $\\rho_{r}r$\n\\end_inset\n\n must radiate the bond-breaking energy out of the continuum to maintain isothermal conditions,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\rho_{r}r=\\hat{\\rho}_{r}^{i}\\psi\\left(\\mathbf{F}\\right)=-\\rho_{r}\\dot{F}\\left(\\Xi_{m}\\right)\\psi\\left(\\mathbf{F}\\right)\\,.\\label{eq:dmg-heat-supply}\n\\end{equation}\n\n\\end_inset\n\nSince \n\\begin_inset Formula $F$\n\\end_inset\n\n is a monotonically increasing function of \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n,\n its material time derivative \n\\begin_inset Formula $\\dot{F}$\n\\end_inset\n\n is always positive when the damage is increasing,\n and zero otherwise as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-Xsi-dot\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Since the specific strain energy \n\\begin_inset Formula $\\psi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n is always positive,\n it follows that the specific heat supply \n\\begin_inset Formula $r=-\\dot{F}\\left(\\Xi_{m}\\right)\\psi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-heat-supply\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is negative or zero,\n consistent with the expectation that heat needs to leave the continuum to maintain isothermal conditions.\n\\end_layout\n\n\\begin_layout Subsection\nConstitutive Models for Damage and Yield Criteria\n\\end_layout\n\n\\begin_layout Standard\nConstitutive models for the damage or yield measure \n\\begin_inset Formula $\\Xi\\left(\\mathbf{U}\\right)$\n\\end_inset\n\n may be derived from energy- or stress-based potentials or (less commonly) strain measures.\n A summary of constitutive models for damage or yield criteria currently implemented in FEBio is presented below.\n\\end_layout\n\n\\begin_layout Subsubsection\nStrain Energy Density\n\\end_layout\n\n\\begin_layout Standard\nIt may be assumed that damage occurs when the strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n achieves a certain threshold \n\\begin_inset Formula $\\Psi_{m}$\n\\end_inset\n\n.\n In that case,\n the constitutive model for the damage measure is\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=\\Psi_{r}\\left(\\mathbf{U}\\right)\\,.\\label{eq:dmg-model-SED}\n\\end{equation}\n\n\\end_inset\n\nThis damage measure is valid for isotropic or anisotropic materials,\n since the strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n must satisfy the frame-invariance of eq.\n\\begin_inset CommandInset ref\nLatexCommand pageref\nreference \"eq:dmg-criterion-invariance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by construction.\n The resulting damage surface normal is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{U}}=\\frac{1}{2}\\left(\\mathbf{S}\\cdot\\mathbf{U}+\\mathbf{U}\\cdot\\mathbf{S}\\right)\\,,\\label{eq:dmg-model-SED-N}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{S}=\\partial\\Psi_{r}/\\partial\\mathbf{E}=J\\mathbf{F}^{-1}\\cdot\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}$\n\\end_inset\n\n is the second Piola-Kirchhoff stress associated with the material.\n\\end_layout\n\n\\begin_layout Subsubsection\nSimo Damage Criterion\n\\end_layout\n\n\\begin_layout Standard\nSimo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87,Simo87a\"\nliteral \"false\"\n\n\\end_inset\n\n proposed a damage criterion related to the strain energy density,\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=\\sqrt{2\\Psi_{r}\\left(\\mathbf{U}\\right)}\\,.\\label{eq:dmg-model-Simo}\n\\end{equation}\n\n\\end_inset\n\nThis damage measure is valid for isotropic or anisotropic materials,\n since the strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n must satisfy the frame-invariance of eq.\n\\begin_inset CommandInset ref\nLatexCommand pageref\nreference \"eq:dmg-criterion-invariance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by construction.\n Its resulting damage surface normal is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{1}{\\sqrt{2\\Psi_{r}}}\\frac{1}{2}\\left(\\mathbf{S}\\cdot\\mathbf{U}+\\mathbf{U}\\cdot\\mathbf{S}\\right)\\,,\\label{eq:dmg-model-Simo-N}\n\\end{equation}\n\n\\end_inset\n\nwhere we have used the result of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-model-SED-N\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The normal reduces to the null tensor in the limit when \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n tend to zero.\n\\end_layout\n\n\\begin_layout Subsubsection\nSpecific Strain Energy\n\\end_layout\n\n\\begin_layout Standard\nIt may be assumed that damage occurs when the specific strain energy \n\\begin_inset Formula $\\Psi_{r}/\\rho_{r}$\n\\end_inset\n\n achieves a certain threshold \n\\begin_inset Formula $\\psi_{m}$\n\\end_inset\n\n.\n In that case,\n the constitutive model for the damage measure is\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=\\frac{1}{\\rho_{r}}\\Psi_{r}\\left(\\mathbf{U}\\right)\\,.\\label{eq:dmg-model-SSE}\n\\end{equation}\n\n\\end_inset\n\nThis damage measure is valid for isotropic or anisotropic materials,\n since the strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n must satisfy the frame-invariance of eq.\n\\begin_inset CommandInset ref\nLatexCommand pageref\nreference \"eq:dmg-criterion-invariance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by construction.\n The resulting damage surface normal is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{1}{\\rho_{r}}\\frac{\\partial\\Psi_{r}}{\\partial\\mathbf{U}}=\\frac{1}{2\\rho_{r}}\\left(\\mathbf{S}\\cdot\\mathbf{U}+\\mathbf{U}\\cdot\\mathbf{S}\\right)\\,,\\label{eq:dmg-model-SSE-N}\n\\end{equation}\n\n\\end_inset\n\nwhere we have used the result of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-model-SED-N\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nVon Mises Stress\n\\end_layout\n\n\\begin_layout Standard\nFor this criterion it is assumed that damage or yield is initiated by increases in the von Mises (or effective) stress,\n \n\\begin_inset Formula $\\sigma_{Y}$\n\\end_inset\n\n.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=\\sigma_{Y}\\left(\\mathbf{U}\\right)=\\sqrt{\\frac{3}{2}\\dev\\boldsymbol{\\sigma}:\\dev\\boldsymbol{\\sigma}}\\,,\\label{eq:dmg-model-VMS}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dev\\boldsymbol{\\sigma}$\n\\end_inset\n\n is the deviatoric part of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n.\n To evaluate the damage surface normal in this case,\n we must use the chain rule,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}:\\frac{\\partial\\boldsymbol{\\sigma}}{\\partial\\mathbf{F}}:\\frac{\\partial\\mathbf{F}}{\\partial\\mathbf{U}}\\,.\\label{eq:dmg-normal-chain-rule}\n\\end{equation}\n\n\\end_inset\n\nFrom the hyperelasticity relation in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n it can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\boldsymbol{\\sigma}}{\\partial\\mathbf{F}}=\\left(\\boldsymbol{\\mathcal{C}}+\\mathbf{I}\\oslash\\boldsymbol{\\sigma}+\\boldsymbol{\\sigma}\\obslash\\mathbf{I}-\\boldsymbol{\\sigma}\\otimes\\mathbf{I}\\right)\\cdot\\mathbf{F}^{-T}\\,,\\label{eq:dmg-stress-tangent}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the fourth-order spatial elasticity tensor associated with the strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n.\n Then,\n it can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{1}{2}\\mathbf{R}^{T}\\cdot\\mathbf{M}\\cdot\\mathbf{R}\\cdot\\mathbf{U}^{-1}+\\frac{1}{2}\\mathbf{U}^{-1}\\cdot\\mathbf{R}^{T}\\cdot\\mathbf{M}^{T}\\cdot\\mathbf{R}\\,,\\label{eq:dmg-N-redux}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{M}=\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}:\\boldsymbol{\\mathcal{C}}+2\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}\\cdot\\boldsymbol{\\sigma}-\\left(\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}:\\boldsymbol{\\sigma}\\right)\\mathbf{I}\\,.\\label{eq:dmg-N-M}\n\\end{equation}\n\n\\end_inset\n\nFrom the von Mises criterion in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-model-VMS\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}=\\frac{3}{2\\sigma_{Y}}\\dev\\boldsymbol{\\sigma}\\,.\\label{eq:dmg-VMS-tangent}\n\\end{equation}\n\n\\end_inset\n\nSubstituting Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-M\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-VMS-tangent\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n provides the surface normal \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nMaximum Normal Stress\n\\end_layout\n\n\\begin_layout Standard\nFor this criterion,\n the damage measure takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=\\sigma_{1}\\,,\\label{eq:dmg-model-MNS}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{1}$\n\\end_inset\n\n is the maximum principal stress (under the assumption that the three principal stresses of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n are ordered such that \n\\begin_inset Formula $\\sigma_{1}\\ge\\sigma_{2}\\ge\\sigma_{3}$\n\\end_inset\n\n).\n To evaluate the damage surface normal,\n we may use Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-M\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}=\\frac{\\partial\\sigma_{1}}{\\partial\\boldsymbol{\\sigma}}=\\mathbf{n}_{1}\\otimes\\mathbf{n}_{1}\\,.\\label{eq:dmg-MNS-tangent}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{n}_{1}$\n\\end_inset\n\n is a unit vector along the principal direction of maximum normal stress (the eigenvector of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n corresponding to the eigenvalue \n\\begin_inset Formula $\\sigma_{1}$\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsubsection\nMaximum Shear Stress\n\\end_layout\n\n\\begin_layout Standard\nFor this criterion,\n the damage measure takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=\\frac{\\sigma_{1}-\\sigma_{3}}{2}\\,,\\label{eq:dmg-model-MSS}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\sigma_{3}$\n\\end_inset\n\n are the maximum and minimum principal normal stresses (under the assumption that the three principal stresses of \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n are ordered such that \n\\begin_inset Formula $\\sigma_{1}\\ge\\sigma_{2}\\ge\\sigma_{3}$\n\\end_inset\n\n).\n To evaluate the damage surface normal,\n we may use Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-M\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}=\\frac{1}{2}\\left(\\mathbf{n}_{1}\\otimes\\mathbf{n}_{1}-\\mathbf{n}_{3}\\otimes\\mathbf{n}_{3}\\right)\\,.\\label{eq:dmg-MSS-tangent}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{n}_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}_{3}$\n\\end_inset\n\n are unit vectors along the principal directions of maximum and minimum normal stress,\n respectively.\n\\end_layout\n\n\\begin_layout Subsubsection\nDrucker Shear Stress\n\\end_layout\n\n\\begin_layout Standard\nThis criterion is based on the yield criterion for plasticity introduced in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Drucker49\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Its damage (or yield) measure takes the form\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=k=\\left(J_{2}^{3}-cJ_{3}^{2}\\right)^{1/6}\\,,\\label{eq:dmg-model-Drucker}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{2}=\\frac{1}{2}\\dev\\boldsymbol{\\sigma}:\\dev\\boldsymbol{\\sigma}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $J_{3}=\\det\\left(\\dev\\boldsymbol{\\sigma}\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n is the yield limit in simple shear and \n\\begin_inset Formula $c$\n\\end_inset\n\n is a user-specified non-dimensional material constant which must lie in the range \n\\begin_inset Formula $-\\frac{27}{8}\\le c\\le\\frac{9}{4}$\n\\end_inset\n\n.\n To better understand the meaning of \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n consider uniaxial loading of a bar which yields at the normal stress value of \n\\begin_inset Formula $\\sigma_{y}$\n\\end_inset\n\n.\n In this case,\n\\begin_inset Formula \n\\begin{equation}\nk=\\frac{\\sigma_{y}}{\\sqrt{3}}\\left(1-\\frac{4}{27}c\\right)^{1/6}\\quad\\frac{\\sigma_{y}}{\\sqrt{3}}\\left(\\frac{2}{3}\\right)^{1/6}\\le k\\le\\frac{\\sigma_{y}}{\\sqrt{3}}\\left(\\frac{3}{2}\\right)^{1/6}\\,.\\label{eq:dmg-Drucker-range}\n\\end{equation}\n\n\\end_inset\n\nIn the special case when \n\\begin_inset Formula $c=0$\n\\end_inset\n\n the Drucker criterion reduces to the von Mises criterion,\n with \n\\begin_inset Formula $k=\\sigma_{y}/\\sqrt{3}$\n\\end_inset\n\n.\n To evaluate the damage or yield surface normal,\n we may use Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dmg-N-M\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n where\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\Xi}{\\partial\\boldsymbol{\\sigma}}=\\frac{1}{k^{5}}\\left(\\frac{J_{2}^{2}}{2}\\dev\\boldsymbol{\\sigma}-\\frac{cJ_{3}^{2}}{3}\\dev\\left(\\dev\\boldsymbol{\\sigma}\\right)^{-1}\\right)\\,.\\label{eq:dmg-Drucker-tangent}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nMaximum Normal Lagrange Strain\n\\end_layout\n\n\\begin_layout Standard\nThe Lagrange strain tensor \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is related to \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n via eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Lagrange-strain-tensor\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Its maximum principal value is denoted by \n\\begin_inset Formula $E_{1}$\n\\end_inset\n\n.\n For this criterion,\n we let\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=E_{1}\\,.\\label{eq:dmg-model-MNLS}\n\\end{equation}\n\n\\end_inset\n\nThen,\n the damage surface normal is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{\\partial E_{1}}{\\partial\\mathbf{U}}=\\sqrt{1+2E_{1}}\\mathbf{n}_{1}\\otimes\\mathbf{n}_{1}\\,,\\label{eq:dmg-MNLS-N}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}_{1}$\n\\end_inset\n\n is a unit vector along the principal direction of normal strain.\n\\end_layout\n\n\\begin_layout Standard\nNote that the maximum normal Lagrange strain is a kinematic measure,\n thus it does not represent an intrinsic material property.\n Though FEBio allows users to specify this criterion,\n it is strongly recommended to employ stress- or strain-energy-density-based failure or yield criteria in practice.\n\\end_layout\n\n\\begin_layout Subsubsection\nOctahedral Lagrange Strain\n\\end_layout\n\n\\begin_layout Standard\nThe octahedral Lagrange strain is given by\n\\begin_inset Formula \n\\begin{equation}\ne\\left(\\mathbf{U}\\right)=\\sqrt{\\frac{2}{3}\\dev\\mathbf{E}:\\dev\\mathbf{E}}\\,,\\label{eq:octahedral-Lagrange-strain}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dev\\mathbf{E}$\n\\end_inset\n\n is the deviatoric part of the Lagrange strain tensor,\n\\begin_inset Formula \n\\begin{equation}\n\\dev\\mathbf{E}=\\mathbf{E}-\\frac{1}{3}\\tr\\left(\\mathbf{E}\\right)\\mathbf{I}\\,,\\label{eq:deviatoric-Lagrange-strain}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Lagrange-strain-tensor\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For this damage measure we let\n\\begin_inset Formula \n\\begin{equation}\n\\Xi\\left(\\mathbf{U}\\right)=e\\left(\\mathbf{U}\\right)\\,.\\label{eq:dmg-model-OLS}\n\\end{equation}\n\n\\end_inset\n\nThe damage surface normal can be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}=\\frac{\\partial e}{\\partial\\mathbf{E}}:\\frac{\\partial\\mathbf{E}}{\\partial\\mathbf{U}}=\\frac{1}{3e}\\left(\\dev\\mathbf{E}\\cdot\\mathbf{U}+\\mathbf{U}\\cdot\\dev\\mathbf{E}\\right)\\,,\\label{eq:dmg-OLS-N}\n\\end{equation}\n\n\\end_inset\n\nwhere we used\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial e}{\\partial\\mathbf{E}}=\\frac{2}{3e}\\dev\\mathbf{E}\\label{eq:dmg-e-tangent}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\mathbf{E}}{\\partial\\mathbf{U}}=\\frac{1}{2}\\frac{\\partial\\mathbf{U}^{2}}{\\partial\\mathbf{U}}=\\frac{1}{2}\\left(\\mathbf{I}\\odot\\mathbf{U}+\\mathbf{U}\\odot\\mathbf{I}\\right)\\,.\\label{eq:dmg-E-U-tangent}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote that the octahedral Lagrange strain is a kinematic measure,\n thus it does not represent an intrinsic material property.\n Though FEBio allows users to specify this criterion,\n it is strongly recommended to employ stress- or strain-energy-density-based failure or yield criteria in practice.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Plasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Plasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReactive plasticity models a material as a mixture of bonds that break in response to loading and reform in a stressed state \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This framework is based on constrained reactive mixtures of solids (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Mixture-of-Solids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nElastic-Perfectly Plastic Response\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Elastic-Perfectly-Plastic-Response\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section describes a reactive framework in which all loaded bonds in an elemental region break and reform simultaneously into a stressed state with a new reference configuration,\n resulting in elastic-perfectly plastic behavior.\n The theory outlined here is similar to reactive viscoelasticity (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Viscoelasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian15,Nims17\"\nliteral \"false\"\n\n\\end_inset\n\n,\n although bonds now reform in a stressed rather than a stress-free state.\n\\end_layout\n\n\\begin_layout Standard\nThe elastic response of this material is achieved when bonds have not yet failed in response to loading.\n In this case it is assumed that the bonds belong to generation \n\\begin_inset Formula $s$\n\\end_inset\n\n (the master constituent) whose reference configuration is represented by material points located at \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n.\n When bonds break and reform at time \n\\begin_inset Formula $t^{\\sigma}$\n\\end_inset\n\n,\n a new \n\\begin_inset Formula $\\sigma-$\n\\end_inset\n\ngeneration is formed.\n Consider that bonds of the \n\\begin_inset Formula $\\sigma-$\n\\end_inset\n\ngeneration yield based on a scalar \n\\shape italic\nyield measure\n\\shape default\n \n\\begin_inset Formula $\\Phi\\left(\\mathbf{U}^{\\sigma}\\right)$\n\\end_inset\n\n (e.g.,\n the von Mises stress),\n where \n\\begin_inset Formula $\\mathbf{U}^{\\sigma}$\n\\end_inset\n\n is the right stretch tensor from the polar decomposition \n\\begin_inset Formula $\\mathbf{F}^{\\sigma}=\\mathbf{R}\\cdot\\mathbf{U}^{\\sigma}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n is the rotation tensor,\n assumed to also be the rotation tensor of \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n.\n Thus,\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{U}^{s}=\\mathbf{U}^{\\sigma}\\cdot\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nLet the \n\\shape italic\nyield threshold\n\\shape default\n for the \n\\begin_inset Formula $\\sigma-$\n\\end_inset\n\ngeneration be given by \n\\begin_inset Formula $\\Phi_{m}$\n\\end_inset\n\n,\n which represents the threshold value at which yielding begins.\n For \n\\begin_inset Formula $\\sigma-$\n\\end_inset\n\ngeneration bonds the \n\\shape italic\nyield criterion\n\\shape default\n may thus be defined as\n\\begin_inset Formula \n\\begin{equation}\n\\varphi\\left(\\mathbf{U}^{\\sigma}\\right)=\\Phi\\left(\\mathbf{U}^{\\sigma}\\right)-\\Phi_{m}\\le0\\,,\\label{eq:plasticity-yield-criterion}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi\\left(\\mathbf{U}^{\\sigma}\\right)$\n\\end_inset\n\n represents the \n\\shape italic\nyield surface\n\\shape default\n of \n\\begin_inset Formula $\\sigma-$\n\\end_inset\n\ngeneration bonds whose \n\\shape italic\ntensorial normal\n\\shape default\n is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}^{\\sigma}=\\frac{\\partial\\varphi}{\\partial\\mathbf{U}^{\\sigma}}=\\frac{\\partial\\Phi\\left(\\mathbf{U}^{\\sigma}\\right)}{\\partial\\mathbf{U}^{\\sigma}}\\,.\\label{eq:plasticity-yield-surface-normal}\n\\end{equation}\n\n\\end_inset\n\nWhen yield thresholds are formulated in stress space,\n the dependence on the deformation takes the form \n\\begin_inset Formula $\\Phi=\\Phi\\left(\\boldsymbol{\\sigma}^{\\sigma}\\left(\\mathbf{U}^{\\sigma}\\right)\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nConsider two consecutive generations \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n,\n denoted by \n\\begin_inset Formula $u$\n\\end_inset\n\n and \n\\begin_inset Formula $v$\n\\end_inset\n\n,\n such that the bond-breaking-and-reforming reaction is \n\\begin_inset Formula $\\mathcal{E}^{u}\\to\\mathcal{E}^{v}$\n\\end_inset\n\n.\n Upon breaking of the \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration to form the \n\\begin_inset Formula $v-$\n\\end_inset\n\ngeneration the plastic consistency condition is given by \n\\begin_inset Formula $d\\varphi=0$\n\\end_inset\n\n,\n which reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}^{u}\\left(u\\right):\\left(\\mathbf{U}^{v}\\left(v\\right)-\\mathbf{U}^{u}\\left(u\\right)\\right)=0\\,.\\label{eq:plastic-consistency-condition}\n\\end{equation}\n\n\\end_inset\n\nThe constitutive model for \n\\begin_inset Formula $\\mathbf{F}^{\\sigma s}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{F}^{vs}\\right)^{-1}=\\left(\\mathbf{F}^{us}\\right)^{-1}\\cdot\\left(\\mathbf{I}-\\lambda\\hat{\\mathbf{N}}^{v}\\right)\\label{eq:plasticity-Fvsi-normality}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\mathbf{N}}^{v}=\\frac{\\mathbf{N}^{v}}{\\sqrt{\\mathbf{N}^{v}:\\mathbf{N}^{v}}}\\,\n\\end{equation}\n\n\\end_inset\n\nis the unit tensor along \n\\begin_inset Formula $\\mathbf{N}^{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is a non-dimensional scalar which may be determined analytically by enforcing the plastic consistency condition in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plastic-consistency-condition\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n When the plastic deformation is assumed to be isochoric the solution for \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is obtained while enforcing \n\\begin_inset Formula $\\det\\mathbf{F}^{\\sigma s}=1$\n\\end_inset\n\n.\n For the earliest yielded generation \n\\begin_inset Formula $u$\n\\end_inset\n\n,\n the preceding \n\\begin_inset Formula $s-$\n\\end_inset\n\ngeneration is in the elastic regime;\n therefore,\n \n\\begin_inset Formula $\\mathbf{F}^{us}=\\mathbf{I}$\n\\end_inset\n\n at the start of the recursive relation in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticity-Fvsi-normality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe stress response of this solid mixture is given generically by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mixture-stress-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n specialized to the case where each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n is assumed to have the same constitutive model \n\\begin_inset Formula $\\Psi_{0}$\n\\end_inset\n\n for its strain energy density,\n \n\\begin_inset Formula $\\Psi_{r}^{\\sigma}=J^{\\sigma s}\\Psi_{0}$\n\\end_inset\n\n.\n For example,\n in the case of the three consecutive \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n generations \n\\begin_inset Formula $s$\n\\end_inset\n\n,\n \n\\begin_inset Formula $u$\n\\end_inset\n\n and \n\\begin_inset Formula $v$\n\\end_inset\n\n,\n the strain energy density is \n\\begin_inset Formula $\\Psi_{r}\\left(\\mathbf{F}^{s}\\right)=\\sum_{\\sigma}w^{\\sigma}J^{\\sigma s}\\Psi_{0}\\left(\\mathbf{U}^{\\sigma}\\right)$\n\\end_inset\n\n and the stress is\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=w^{s}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{U}^{s}\\right)+w^{u}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{U}^{u}\\right)+w^{v}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{U}^{v}\\right)\\,,\\label{eq:epp-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{U}^{u}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{U}^{v}$\n\\end_inset\n\n are evaluated from \n\\begin_inset Formula $\\mathbf{U}^{s}\\cdot\\left(\\mathbf{F}^{\\sigma s}\\right)^{-1}=\\mathbf{U}^{\\sigma}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\sigma=u,v$\n\\end_inset\n\n) and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticity-Fvsi-normality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n is given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:febio-stress-function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For an elastic-perfectly plastic response the bond mass fractions are constitutively assumed to satisfy\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}w^{s} & =1-H\\left(t-t^{u}\\right)\\end{aligned}\n\\,,\\quad w^{u}=H\\left(t-t^{u}\\right)-H\\left(t-t^{v}\\right)\\,,\\quad\\begin{aligned}w^{v} & =H\\left(t-t^{v}\\right)\\,,\\end{aligned}\n\\label{eq:epp-mass-fractions}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $H\\left(\\cdot\\right)$\n\\end_inset\n\n is the Heaviside unit step function.\n The corresponding constitutive models for \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}$\n\\end_inset\n\n may be obtained by substituting these expressions into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-alpha-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The onset of yielding of generations \n\\begin_inset Formula $u$\n\\end_inset\n\n and \n\\begin_inset Formula $v$\n\\end_inset\n\n is the time \n\\begin_inset Formula $t$\n\\end_inset\n\n when \n\\begin_inset Formula $\\varphi\\left(\\mathbf{U}^{s}\\right)=0$\n\\end_inset\n\n (\n\\begin_inset Formula $\\mathcal{E}^{s}\\to\\mathcal{E}^{u}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $t=t^{u}$\n\\end_inset\n\n) and \n\\begin_inset Formula $\\varphi\\left(\\mathbf{U}^{u}\\right)=0$\n\\end_inset\n\n (\n\\begin_inset Formula $\\mathcal{E}^{u}\\to\\mathcal{E}^{v}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $t=t^{v}$\n\\end_inset\n\n),\n respectively.\n In summary \n\\begin_inset Formula $w^{\\sigma}=1$\n\\end_inset\n\n during the lifetime of generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n (\n\\begin_inset Formula $t^{\\sigma}\\le t<t^{\\sigma+1}$\n\\end_inset\n\n) and \n\\begin_inset Formula $0$\n\\end_inset\n\n at other times \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nKinematic \n\\begin_inset Quotes eld\n\\end_inset\n\nHardening\n\\begin_inset Quotes erd\n\\end_inset\n\n Response\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Kinematic-Hardening-Response\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe framework presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Elastic-Perfectly-Plastic-Response\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n has considered an elastic-perfectly plastic response,\n i.e.,\n all the bonds yield when a single yield threshold \n\\begin_inset Formula $\\Phi_{m}$\n\\end_inset\n\n is met.\n However,\n a wealth of experimental results show a more progressive yielding,\n rather than a sudden onset,\n and an increase in the stress with increasing plastic deformation,\n a phenomenon alternately termed strain hardening or work hardening.\n Real materials typically exhibit the Bauschinger effect,\n where loading to yield in one direction changes the yield threshold in the reverse direction.\n The hardening behavior that accounts for this effect is known as kinematic hardening;\n for a load reversal,\n it predicts yielding occurs when the change in load achieves twice the monotonic yield strength.\n The reactive plasticity framework can be extended to allow for kinematic hardening by introducing multiple families of bonds.\n In the current FEBio implementation each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n shares the same yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n but distinct associated yield thresholds \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n,\n and it follows the elastic-perfectly plastic behavior for multiple generations outlined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Elastic-Perfectly-Plastic-Response\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The superposition of multiple bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n in parallel naturally develops behavior consistent with kinematic hardening,\n as different bond families yield at different thresholds.\n\\end_layout\n\n\\begin_layout Standard\nWe consider \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n \n\\shape italic\nbond families\n\\shape default\n \n\\begin_inset Formula $\\beta=0,\\dots,n_{f}-1$\n\\end_inset\n\n,\n which may yield under different thresholds,\n where each bond family may evolve over multiple generations \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n This framework requires us to update our notation to include a subscript \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n for suitable variables introduced in the presentation above.\n In particular,\n the reference configuration of generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n in bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is now denoted by \n\\begin_inset Formula $\\mathbf{X}_{\\beta}^{\\sigma}$\n\\end_inset\n\n and the corresponding deformation gradient is \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{\\sigma}$\n\\end_inset\n\n.\n We assume that the free energy density of each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is \n\\begin_inset Formula $\\Psi_{\\beta r}=J_{\\beta}^{\\sigma s}\\Psi_{0}\\left(\\mathbf{F}_{\\beta}^{\\sigma}\\right)$\n\\end_inset\n\n,\n when the mixture consists entirely of bonds of family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n in generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n.\n The master reference configuration of all bond families remains \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n and the associated (total) deformation gradient is still \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n.\n Therefore,\n each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n requires a constitutive relation for the function of state \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{\\sigma s}$\n\\end_inset\n\n in the updated form of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-total-def-grad\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n such as that given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticity-Fvsi-normality\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n where each term should now include a subscript \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe referential mass density of bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is \n\\begin_inset Formula $\\rho_{r\\beta}$\n\\end_inset\n\n,\n such that the mixture referential mass density is given by \n\\begin_inset Formula $\\rho_{r}=\\sum_{\\beta}\\rho_{r\\beta}$\n\\end_inset\n\n.\n The referential mass density of generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n in bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is \n\\begin_inset Formula $\\rho_{r\\beta}^{\\sigma}$\n\\end_inset\n\n,\n which satisfies \n\\begin_inset Formula $\\sum_{\\sigma}\\rho_{r\\beta}^{\\sigma}=\\rho_{r\\beta}$\n\\end_inset\n\n,\n as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:general-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For convenience,\n we define\n\\begin_inset Formula \n\\begin{equation}\nw_{\\beta}\\equiv\\frac{\\rho_{r\\beta}}{\\rho_{r}}\\,,\\quad\\sum_{\\beta}w_{\\beta}=1\\,,\\label{eq:multiplebonds-family-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhich represents the mass fraction of each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n within the constrained solid mixture,\n and\n\\begin_inset Formula \n\\begin{equation}\nw_{\\beta}^{\\sigma}=\\frac{\\rho_{r\\beta}^{\\sigma}}{\\rho_{r\\beta}}\\,,\\quad\\sum_{\\sigma}w_{\\beta}^{\\sigma}=1\\,,\\label{eq:multiplebonds-bond-species-mass-fractions}\n\\end{equation}\n\n\\end_inset\n\nwhich represents the mass fraction of each generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n within the bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n From these definitions,\n it follows that bond family mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n are time-invariant (thus user-selected for a particular material response),\n whereas generation mass fractions \n\\begin_inset Formula $w_{\\beta}^{\\sigma}$\n\\end_inset\n\n evolve with bond-breaking-and-reforming reactions.\n\\end_layout\n\n\\begin_layout Standard\nThe mixture strain energy density \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n is now given by\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\sum_{\\beta}w_{\\beta}\\sum_{\\sigma}w_{\\beta}^{\\sigma}J_{\\beta}^{\\sigma s}\\Psi_{0}\\left(\\mathbf{F}_{\\beta}^{\\sigma}\\right)\\,,\\label{eq:multiplebonds-mixture-energy}\n\\end{equation}\n\n\\end_inset\n\nwhereas the mixture stress is\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sum_{\\beta}w_{\\beta}\\underbrace{\\sum_{\\sigma}w_{\\beta}^{\\sigma}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{\\sigma}\\right)}_{\\boldsymbol{\\sigma}_{\\beta}}\\,,\\quad\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{\\sigma}\\right)=\\frac{1}{J_{\\beta}^{\\sigma}}\\frac{\\partial\\Psi_{0}}{\\partial\\mathbf{F}_{\\beta}^{\\sigma}}\\cdot\\left(\\mathbf{F}_{\\beta}^{\\sigma}\\right)^{T}\\,.\\label{eq:multiplebonds-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nTo simplify the remainder of this presentation,\n we introduce the concept of \n\\shape italic\nyielded bonds\n\\shape default\n,\n denoted by \n\\begin_inset Formula $y$\n\\end_inset\n\n,\n to represent bonds of the current extant generation in a plasticity formulation.\n The yielded bond fraction for each family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\nw_{\\beta}^{y}=\\sum_{\\sigma\\neq s}w_{\\beta}^{\\sigma}=1-w_{\\beta}^{s}\\label{eq:hardening-yielded-mass-fraction}\n\\end{equation}\n\n\\end_inset\n\nwhere the summation runs over all possible yielded generations \n\\begin_inset Formula $\\sigma\\neq s$\n\\end_inset\n\n.\n In particular,\n at time \n\\begin_inset Formula $t=u$\n\\end_inset\n\n,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-yielded-mass-fraction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to the statement \n\\begin_inset Formula $w_{\\beta}^{y}=w_{\\beta}^{u}$\n\\end_inset\n\n.\n We then define the relative deformation gradient of yielded bonds as \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{y}$\n\\end_inset\n\n,\n which equals \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{\\sigma}$\n\\end_inset\n\n for the extant generation \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n in family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n With these notational changes,\n we may write the yielding reactions in the form\n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{E}^{s}\\to\\underbrace{\\mathcal{E}^{u}\\rightarrow\\mathcal{E}^{v}\\rightarrow\\dots}_{\\mathcal{E}^{y}}\\,.\\label{eq:hardening-yielded-rxn}\n\\end{equation}\n\n\\end_inset\n\nThen the mixture stress in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:multiplebonds-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten as \n\\begin_inset Formula $\\boldsymbol{\\sigma}=\\sum_{\\beta}w_{\\beta}\\boldsymbol{\\sigma}_{\\beta}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{\\beta}=w_{\\beta}^{s}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}^{s}\\right)+\\left(1-w_{\\beta}^{s}\\right)\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{y}\\right)$\n\\end_inset\n\n.\n We may also define the total fraction \n\\begin_inset Formula $w^{s}$\n\\end_inset\n\n of intact bonds in the mixture as \n\\begin_inset Formula $w^{s}=\\sum_{\\beta}w_{\\beta}w_{\\beta}^{s}$\n\\end_inset\n\n,\n and the total fraction of yielded bonds as \n\\begin_inset Formula $w^{y}=\\sum_{\\beta}w_{\\beta}w_{\\beta}^{y}=1-w^{s}$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $w^{s}+w^{y}=1$\n\\end_inset\n\n.\n Then \n\\begin_inset Formula $\\boldsymbol{\\sigma}=w^{s}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}^{s}\\right)+\\sum_{\\beta}w_{\\beta}\\left(1-w_{\\beta}^{s}\\right)\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{y}\\right)$\n\\end_inset\n\n.\n The summation in this last expression does not simplify further since \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{y}$\n\\end_inset\n\n is different for each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nLet each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n exhibit an elastic-perfectly plastic response,\n following the model of Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Elastic-Perfectly-Plastic-Response\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Once the yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n is reached,\n all the intact bonds of that family yield at once,\n such that \n\\begin_inset Formula $w_{\\beta}^{s}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{y}=1$\n\\end_inset\n\n as shown for the mixture stress response in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-families-superposition\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-c.\n Now consider that there are three bond families,\n \n\\begin_inset Formula $\\beta=0,1,2$\n\\end_inset\n\n which are weighted evenly,\n \n\\begin_inset Formula $w_{\\beta}=1/3\\,\\forall\\beta$\n\\end_inset\n\n.\n The stress response for this illustrative example is shown in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-families-superposition\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nd-f.\n Though each bond family is elastic-perfectly plastic,\n their superposition develops \n\\begin_inset Quotes eld\n\\end_inset\n\nhardening\n\\begin_inset Quotes erd\n\\end_inset\n\n-like behavior.\n At the onset of yielding,\n when family \n\\begin_inset Formula $\\beta=0$\n\\end_inset\n\n yields,\n its bond mass fractions are \n\\begin_inset Formula $w_{0}^{s}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{0}^{y}=1$\n\\end_inset\n\n,\n implying that this entire family has yielded.\n However,\n since the family has a mass fraction \n\\begin_inset Formula $w_{0}=1/3$\n\\end_inset\n\n in the solid mixture,\n two-thirds of the bonds in the mixture remain intact at this juncture,\n \n\\begin_inset Formula $1-w_{0}^{s}=2/3$\n\\end_inset\n\n.\n As subsequent families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n yield,\n their bonds transition from intact to yielded generations in the same manner.\n Though the resulting stress response in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-families-superposition\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nd is classically described as a \n\\begin_inset Quotes eld\n\\end_inset\n\nhardening\n\\begin_inset Quotes erd\n\\end_inset\n\n behavior,\n the reactive plasticity mixture framework proposes a different interpretation,\n namely that there are multiple elastic-perfectly plastic bond families in the material,\n each with a different threshold of yielding.\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigReactivePlasticityStress.png\n\tlyxscale 50\n\twidth 6in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe phenomenon described as \n\\begin_inset Quotes eld\n\\end_inset\n\nkinematic hardening\n\\begin_inset Quotes erd\n\\end_inset\n\n in classical plasticity may be represented by the superposition of multiple elastic-perfectly plastic bond families with different yield thresholds.\n The elastic-perfectly plastic stress response of a single bond family \n\\begin_inset Formula $\\beta=0$\n\\end_inset\n\n in the reactive framework is presented in (a),\n with the initial linear response contributed by the intact bonds \n\\begin_inset Formula $s$\n\\end_inset\n\n;\n upon yielding at the threshold \n\\begin_inset Formula $\\Phi_{m0}$\n\\end_inset\n\n,\n the perfectly plastic response consists of multiple generations of breaking and reforming bonds \n\\begin_inset Formula $\\sigma=u,v,\\dots$\n\\end_inset\n\n.\n The evolution of mass fractions \n\\begin_inset Formula $w_{0}^{s}$\n\\end_inset\n\n of intact and \n\\begin_inset Formula $w_{0}^{y}$\n\\end_inset\n\n of yielded bonds is presented in (b) and (c),\n respectively.\n The stress response obtained from the superposition of three bond families \n\\begin_inset Formula $\\beta=0,1,2$\n\\end_inset\n\n is shown in (d),\n where each family occupies the same mass fraction \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n in the mixture,\n \n\\begin_inset Formula $w_{0}=w_{1}=w_{2}=\\frac{1}{3}$\n\\end_inset\n\n,\n reproducing the classical kinematic hardening behavior.\n Green dashed lines help indicate changes in slope due to yielding of each bond family.\n The corresponding mixture mass fractions of (e) intact bonds \n\\begin_inset Formula $w^{s}$\n\\end_inset\n\n,\n and (f) yielded bonds \n\\begin_inset Formula $w^{y}=1-w^{s}$\n\\end_inset\n\n further illustrates the occurence of each yielding reaction.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:hardening-families-superposition\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n the family mass fraction \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n and the associated yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n must be provided by constitutive assumption,\n along with a single constitutive model for \n\\begin_inset Formula $\\Psi_{0}$\n\\end_inset\n\n which applies to all generations of all bond families.\n The total number \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n of bond families must also be provided.\n Parameters \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ w_{\\beta},\\Phi_{m\\beta}\\right\\} ,\\,\\beta\\in\\left[0,n_{f}-1\\right]$\n\\end_inset\n\n suffice to define an elastoplastic material which exhibits classical kinematic hardening behavior,\n for a given elastic response \n\\begin_inset Formula $\\Psi_{0}$\n\\end_inset\n\n and yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nConstitutive Modeling of Yield Response\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Yield-Constitutive-Modeling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n we provide basic constitutive relations for the parameters \n\\begin_inset Formula $\\left\\{ w_{\\beta},\\Phi_{m\\beta}\\right\\} ,\\,\\beta\\in\\left[0,n_{f}-1\\right]$\n\\end_inset\n\n which define an elastoplastic material.\n We also demonstrate how these various parameters affect the uniaxial stress-strain response of a material.\n The example in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-families-superposition\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nd shows how superposition of multiple elastic-perfectly plastic bond families may create a hardening-like curve.\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigReactivePlasticityYieldThresholds.png\n\tlyxscale 30\n\twidth 3in\n\n\\end_inset\n\n\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nSchematic stress-strain curve illustrating derivation of constitutive models for \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n.\n \n\\begin_inset Formula $E$\n\\end_inset\n\n is Young's modulus of the elastic solid (whose value does not matter here),\n \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n are the effective yield thresholds for the global material (given),\n \n\\begin_inset Formula $\\varepsilon_{\\beta}$\n\\end_inset\n\n are the yield strains and \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n are the true yield thresholds for each bond family,\n which need to be determined,\n and \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n are the family mass fractions (given).\n\\color black\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:hardening-constitutive-diagram\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\nIn particular,\n we present a constitutive modeling framework that requires at most six scalar parameters,\n regardless of the value of \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSince each family behaves elastically until it yields,\n a family's yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n is generally not the value recorded on a stress-strain curve when the slope changes (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-constitutive-diagram\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n That value may be called the \n\\shape italic\napparent yield threshold\n\\shape default\n \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n,\n which can be related to the true yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n by assuming a linear elastic stress-strain relationship prior to yielding.\n For simplicity,\n we assume that \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n values are evenly distributed between an \n\\shape italic\ninitial yield threshold\n\\shape default\n \n\\begin_inset Formula $\\Upsilon_{\\text{0}}$\n\\end_inset\n\n and a \n\\shape italic\nfinal yield threshold\n\\shape default\n \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n,\n parameters which may be identified from a stress strain curve (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-b).\n Beyond \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n,\n the material either behaves as if it is perfectly plastic (a scenario which may be valid around the ultimate strength,\n for example),\n or it transitions to a linear hardening regime.\n The constitutive model thus specifies\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\Upsilon_{\\beta} & =\\Upsilon_{\\text{0}}+\\beta\\frac{\\Upsilon_{\\text{max}}-\\Upsilon_{\\text{0}}}{n_{f}-1}, & \\beta & =0,\\ldots,n_{f}-1\\\\\n\\Phi_{m\\beta} & =\\Phi_{m,\\beta-1}+\\frac{\\Upsilon_{\\beta}-\\Upsilon_{\\beta-1}}{1-\\sum_{b=0}^{\\beta-1}w_{b}}, & \\Phi_{m0} & =\\Upsilon_{0}\n\\end{aligned}\n\\label{eq:hardening-phi-model-no-r}\n\\end{equation}\n\n\\end_inset\n\nThe relationships between \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n embodied in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-phi-model-no-r\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n are illustrated graphically in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-constitutive-diagram\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Through this relationship,\n only the values of \n\\begin_inset Formula $\\Upsilon_{\\text{0}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n must be specified,\n along with \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe family mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n govern the influence of each family on the overall material response.\n The simplest model for \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n involves specifying the mass fraction of the first yielding family \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n,\n which controls the slope of the initial post-yield response (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na),\n and then evenly weighting the rest of the bond families,\n \n\\begin_inset Formula $w_{\\beta}=\\left(1-w_{0}\\right)/\\left(n_{f}-1\\right)$\n\\end_inset\n\n.\n However,\n in cases where the material transitions to a linear hardening regime,\n we can recover this behavior by adding one more bond family,\n \n\\begin_inset Formula $\\beta=n_{f}$\n\\end_inset\n\n,\n that never yields,\n thus remaining elastic.\n The associated mass fraction \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n\n\\shape italic\n\\emph on\n for \n\\begin_inset Formula $\\beta=n_{f}$\n\\end_inset\n\n is called the \n\\emph default\nelastic mass fraction\n\\shape default\n and denoted \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n;\n a non-zero value for this parameter may be specified whenever we wish to include linear hardening behavior (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nb).\n Given initial and elastic mass fractions \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n,\n the simplest constitutive assumption for the remaining \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n's assumes the remaining mass is evenly divided,\n such that\n\\begin_inset Formula \n\\begin{equation}\nw_{\\beta}=\\frac{1-w_{e}-w_{0}}{n_{f}-1},\\,\\beta\\in\\left[1,n_{f}-1\\right]\\,.\\label{eq:hardening-m-model-no-r}\n\\end{equation}\n\n\\end_inset\n\nThe effect of the mass fraction parameters \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n is explored parametrically in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nc and Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nd,\n respectively.\n In general,\n most ductile materials have \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n very close to unity,\n which provides hardening behavior over a finite strain range.\n As \n\\begin_inset Formula $w_{0}\\to1$\n\\end_inset\n\n the stress-strain behavior approaches perfect plasticity.\n In contrast,\n when \n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n,\n the material response becomes perfectly plastic once the final yield threshold \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n has been exceeded.\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigReactivePlasticityYieldModel.png\n\tlyxscale 20\n\twidth 6in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nModeling uniaxial stress-strain curves using the constitutive model for scalar bond family parameters given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Yield-Constitutive-Modeling\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Identification of parameters on idealized stress-strain curves showing (a) a plateau in the stress,\n or (b) exhibiting a region of linear hardening.\n The yielding behavior is fully described by the set of parameters \n\\begin_inset Formula $\\left\\{ n_{f},\\Upsilon_{0},\\Upsilon_{\\text{max}},w_{0},w_{e},r\\right\\} $\n\\end_inset\n\n.\n Parametric variations of (c) \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n and (d) \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n illustrate their influence on the stress-strain response;\n other parameters are held fixed.\n In (c-d) \n\\begin_inset Formula $n_{f}=10$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Upsilon_{0}=600$\n\\end_inset\n\n MPa,\n \n\\begin_inset Formula $\\Upsilon_{\\text{max}}=1000$\n\\end_inset\n\n MPa,\n and \n\\begin_inset Formula $r=1$\n\\end_inset\n\n.\n In (c),\n \n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n and in (d) \n\\begin_inset Formula $w_{0}=0.75$\n\\end_inset\n\n.\n For all cases,\n \n\\begin_inset Formula $E=200\\,\\text{GPa}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\nu=0.3$\n\\end_inset\n\n.\n\\color black\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:hardening-family-parametric-curves\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\nAs \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n increases,\n a region of linear hardening is seen on a plot of the true stress against strain.\n For most ductile materials,\n \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n is usually \n\\begin_inset Formula $0$\n\\end_inset\n\n or on the order of \n\\begin_inset Formula $0.001$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIt is also possible to refine the constitutive relations of Eqs.\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-phi-model-no-r\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-m-model-no-r\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by introducing a \n\\shape italic\nbias factor\n\\shape default\n \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n which allows a geometric progression rather than uniform spacing of the apparent yield thresholds and family mass fractions.\n The bias factor \n\\begin_inset Formula $r$\n\\end_inset\n\n has the effect of modifying the shape of the hardening region between \n\\begin_inset Formula $\\Upsilon_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Upsilon_{\\text{max }}$\n\\end_inset\n\n (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nb).\n The modified constitutive relations for \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n take the form\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}c & =\\frac{1-r}{1-r^{n_{f}-1}} & \\Phi_{m0} & =\\Upsilon_{0}\\\\\n\\Upsilon_{1} & =\\Upsilon_{0}+c\\left(\\Upsilon_{\\text{max}}-\\Upsilon_{0}\\right), & \\Phi_{m1} & =\\Upsilon_{0}+\\frac{\\Upsilon_{1}-\\Upsilon_{0}}{1-w_{0}}\\\\\n\\Upsilon_{\\beta} & =\\Upsilon_{\\beta-1}+r\\left(\\Upsilon_{\\beta-1}-\\Upsilon_{\\beta-2}\\right), & \\Phi_{m\\beta} & =\\Phi_{m,\\beta-1}+\\frac{\\Upsilon_{\\beta}-\\Upsilon_{\\beta-1}}{1-\\sum_{b=0}^{\\beta-1}w_{b}}\n\\end{aligned}\n\\label{eq:hardening-phi-model-bias}\n\\end{equation}\n\n\\end_inset\n\nThe mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n are similarly biased,\n where \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n are specified and\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}w_{1} & =c\\left(1-w_{e}-w_{0}\\right)\\\\\nw_{\\beta} & =w_{\\beta-1}r\n\\end{aligned}\n\\label{eq:hardening-m-bias}\n\\end{equation}\n\n\\end_inset\n\nThe full set of parameters is then given by \n\\begin_inset Formula $\\left\\{ n_{f},\\Upsilon_{0},\\Upsilon_{\\text{max}},w_{0},w_{e},r\\right\\} $\n\\end_inset\n\n.\n Setting \n\\begin_inset Formula $r=1$\n\\end_inset\n\n recovers the uniform distribution presented in Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-phi-model-no-r\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-m-model-no-r\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-b graphically describes the influence of each parameter on simplified stress-strain curves,\n showing how these parameters may be extracted from experimental data.\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Elastoplastic Damage Mechanics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Plasticity-with-damage\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPlastic deformation (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) is often coupled with damage,\n as the finite deformation and plastic flow of a loaded material typically induces some amount of failure.\n Within the constrained reactive mixture framework adopted in FEBio (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Mixture-of-Solids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n damage is produced by bonds breaking permanently (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n which reduces the generation mass fractions \n\\begin_inset Formula $w_{\\beta}^{\\sigma}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In our treatment of elastoplastic damage we assume that both intact and yielded bonds may become damaged.\n Damage to intact bonds may represent some initial damage value for a material with defects,\n or damage due to intermolecular failure of bonds that never yielded;\n we refer to this as \n\\emph on\nelastic damage\n\\emph default\n.\n Damage to yielded bonds represents \n\\emph on\nplastic damage\n\\emph default\n.\n The mechanism of damage and the failure measure may be different for these two types of bonds,\n particularly since a stress- or energy-based failure measure may not be appropriate for plastic damage.\n Intact bonds belong to the \n\\begin_inset Formula $s-$\n\\end_inset\n\ngeneration which is present at \n\\begin_inset Formula $t=-\\infty$\n\\end_inset\n\n.\n Once yielding occurs,\n all successive generations of that family are labeled as yielded bonds \n\\begin_inset Formula $y$\n\\end_inset\n\n.\n This distinction is necessary so we can then distinguish between damage to intact bonds (elastic damage) and damage to yielded bonds (plastic damage),\n since intact bonds which get damaged never have the ability to yield.\n It is important to note that the nature of the plastic deformation described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n remains unchanged.\n Damage modifies the material behavior by reducing the fraction of bonds in various generations,\n which scales the response accordingly.\n\\end_layout\n\n\\begin_layout Standard\nIn a reactive constrained mixture framework the insertion of damage into the reactive plasticity formulation is straightforward.\n Since bonds break permanently in a damage reaction,\n there is no need to define a function of state \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{\\sigma s}$\n\\end_inset\n\n to describe a (non-existing) reformed configuration.\n Furthermore,\n the specific free energy of broken bonds is zero.\n The scalar \n\\shape italic\nelastic damage criterion\n\\shape default\n \n\\begin_inset Formula $\\Xi^{e}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n,\n which is taken to have the same functional form for all bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n is the analog to the yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n for plasticity.\n As shown in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the main contrast with reactive plasticity is that not all bonds in the family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n break simultaneously at a single \n\\shape italic\nelastic damage threshold\n\\shape default\n \n\\begin_inset Formula $\\Xi_{m\\beta}^{e}$\n\\end_inset\n\n.\n Instead,\n the fraction of broken bonds varies as a function of \n\\begin_inset Formula $\\Xi^{e}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n,\n denoted by \n\\begin_inset Formula $F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $0\\le F^{e}\\left(\\Xi_{\\beta}^{e}\\right)\\le1$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n is a function of state;\n it must be a monotonically increasing function of its argument to satisfy the Clausius-Duhem inequality \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Nims16\"\nliteral \"false\"\n\n\\end_inset\n\n.\n We may view \n\\begin_inset Formula $F^{e}$\n\\end_inset\n\n as a cumulative distribution function (CDF),\n whose corresponding probability distribution function (PDF) represents the probability of damage at a particular value of \n\\begin_inset Formula $\\Xi_{\\beta}^{e}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nTheoretical Formulation\n\\end_layout\n\n\\begin_layout Standard\nWe first briefly sketch the structure of the elastoplastic damage theory in FEBio for a single bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n Since each bond family in reactive plasticity yields all at once,\n we can easily split an elastoplastic damage theory into two parts to represent elastic and plastic damage regimes.\n Assume that the first yielding reaction for bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n occurs at time \n\\begin_inset Formula $t=u_{\\beta}$\n\\end_inset\n\n.\n Prior to this initial yielding,\n the damage behavior described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n applies,\n and the material composition is generally a mixture of intact (\n\\begin_inset Formula $\\sigma=s$\n\\end_inset\n\n) and broken (\n\\begin_inset Formula $\\sigma=b$\n\\end_inset\n\n) bonds satisfying the reaction \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{s}\\to\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n.\n The corresponding bond mass fractions satisfy \n\\begin_inset Formula $1=w_{\\beta}^{s}+w_{\\beta}^{b}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{y}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $w_{\\beta}^{b}=F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n is the elastic damage in bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n At \n\\begin_inset Formula $t=u_{\\beta}$\n\\end_inset\n\n,\n the remaining intact bonds \n\\begin_inset Formula $w_{\\beta}^{s}=1-w_{\\beta}^{b}$\n\\end_inset\n\n all yield,\n following the reaction in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:hardening-yielded-rxn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The family mass balance is then given as \n\\begin_inset Formula $1=w_{\\beta}^{y}+w_{\\beta}^{b}$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $w_{\\beta}^{s}=0$\n\\end_inset\n\n after yielding.\n The mass fraction of broken bonds \n\\begin_inset Formula $w^{b}=\\sum_{\\beta}w_{\\beta}w_{\\beta}^{b}$\n\\end_inset\n\n is equal to the elastic damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n as defined in classical damage mechanics.\n\\end_layout\n\n\\begin_layout Standard\nFor time \n\\begin_inset Formula $t>u_{\\beta}$\n\\end_inset\n\n,\n yielded bonds may continue to yield,\n but they may also sustain damage according to the reaction \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{y}\\to\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n,\n which reduces their mass fraction \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n.\n Damage to yielded bonds may occur based on a function of state (often described as a plastic strain,\n though it is not an observable kinematic variable),\n which is distinct from the measure of elastic damage.\n Therefore,\n we denote the \n\\shape italic\nplastic damage measure\n\\shape default\n as \n\\begin_inset Formula $\\Xi^{p}\\left(\\mathbf{F}_{\\beta}^{ys}\\right)$\n\\end_inset\n\n and its cumulative distribution function by \n\\begin_inset Formula $F^{p}\\left(\\Xi_{\\beta}^{p}\\right)$\n\\end_inset\n\n,\n under the assumption that all bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n share the same functional forms for \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $F^{p}$\n\\end_inset\n\n.\n For each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n only the remaining undamaged fraction \n\\begin_inset Formula $1-F^{p}\\left(\\Xi_{\\beta}^{p}\\right)$\n\\end_inset\n\n of yielded bonds may break and reform as the next yielded generation.\n\\end_layout\n\n\\begin_layout Standard\nThe modern understanding is that plastic strain is ill-defined and not a suitable state variable.\n It must be recognized that,\n just as \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n is a constitutively-prescribed function of state and does not carry the meaning of a plastic deformation gradient,\n the plastic damage measure \n\\begin_inset Formula $\\Xi^{p}\\left(\\mathbf{F}_{\\beta}^{ys}\\right)$\n\\end_inset\n\n is also a function of state.\n This quantity is called a plastic strain for convenience only.\n\\end_layout\n\n\\begin_layout Standard\nWhen plastic deformation occurs simultaneously with damage,\n the mass fraction of each successive yielded generation will have decreased.\n The following treatment now considers the superposition of multiple plastic bond families,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Kinematic-Hardening-Response\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nDamage to Intact Bonds\n\\end_layout\n\n\\begin_layout Standard\nAt any given time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n there is a maximum value of \n\\begin_inset Formula $\\Xi^{e}$\n\\end_inset\n\n that has been achieved over the past deformation history.\n This maximum value may be distinct for each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n since intact bond families may yield at different values of \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n;\n it is thus denoted by \n\\begin_inset Formula $\\Xi_{m\\beta}^{e}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\Xi_{m\\beta}^{e}=\\max_{\\begin{aligned}-\\infty<\\tau\\le t<u_{\\beta}\\end{aligned}\n}\\Xi^{e}\\left(\\mathbf{F}^{s}\\left(\\tau\\right)\\right)\\,.\\label{eq:plasticdmg-intact-ximax-family}\n\\end{equation}\n\n\\end_inset\n\nAny damage sustained by intact bonds reduces their mass fraction,\n such that\n\\begin_inset Formula \n\\begin{equation}\n\\left\\{ \\begin{aligned}w_{\\beta}^{s} & =1-F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)\\\\\nw_{\\beta}^{y} & =0\\\\\nw_{\\beta}^{b} & =F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)\n\\end{aligned}\n\\right.\\,,\\quad t<u_{\\beta}\\,,\\label{eq:plasticdmg-massfraction-pre-yield}\n\\end{equation}\n\n\\end_inset\n\nand hence the mass balance of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:multiplebonds-bond-species-mass-fractions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is satisfied.\n Since all remaining intact bonds yield at \n\\begin_inset Formula $t=u_{\\beta}$\n\\end_inset\n\n and thus no intact bonds are left to sustain damage,\n \n\\begin_inset Formula $F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)$\n\\end_inset\n\n remains constant constant for bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n when \n\\begin_inset Formula $t\\geq u_{\\beta}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nDamage to Yielded Bonds\n\\end_layout\n\n\\begin_layout Standard\nAt time \n\\begin_inset Formula $t=u_{\\beta}$\n\\end_inset\n\n,\n the yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n for family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is reached and all remaining intact bonds in family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n yield such that\n\\begin_inset Formula \n\\begin{equation}\n\\left\\{ \\begin{aligned}w_{\\beta}^{s} & =0\\\\\nw_{\\beta}^{y} & =1-F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)\\\\\nw_{\\beta}^{b} & =F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)\n\\end{aligned}\n\\right.\\,,\\quad t=u_{\\beta}\\,.\\label{eq:plasticdmg-mass-fraction-yield}\n\\end{equation}\n\n\\end_inset\n\nOnce they have formed,\n yielded bonds may sustain plastic damage.\n The maximum value of the plastic damage measure \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n experienced by family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n up until the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n is denoted by \n\\begin_inset Formula $\\Xi_{m\\beta}^{p}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\Xi_{m\\beta}^{p}=\\max_{\\begin{aligned}u_{\\beta}\\leq\\tau<t\\end{aligned}\n}\\Xi^{p}\\left(\\mathbf{F}_{\\beta}^{y}\\left(\\tau\\right)\\right)\\,.\\label{eq:plasticdmg-yielded-ximax-family}\n\\end{equation}\n\n\\end_inset\n\nFor \n\\begin_inset Formula $t>u_{\\beta}$\n\\end_inset\n\n,\n yielded bonds may continue to yield,\n breaking and reforming into successive generations.\n However,\n in contrast to Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the mass fraction \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n of yielded bonds in family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n no longer remains constant over successive yield generations,\n due to the plastic damage reaction.\n Each time a yielded bond breaks and reforms into a new generation,\n \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n is given by the undamaged fraction of yielded bonds,\n\\begin_inset Formula \n\\begin{equation}\n\\left\\{ \\begin{aligned}w_{\\beta}^{s} & =0\\\\\nw_{\\beta}^{y} & =\\left(1-F^{p}\\left(\\Xi_{m\\beta}^{p}\\right)\\right)\\left(1-F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)\\right)\\\\\nw_{\\beta}^{b} & =F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)+F^{p}\\left(\\Xi_{m\\beta}^{p}\\right)\\left(1-F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)\\right)\n\\end{aligned}\n\\right.\\,,\\quad t>u_{\\beta}\\,.\\label{eq:plasticdmg-mass-fraction-post-yield}\n\\end{equation}\n\n\\end_inset\n\nEquations\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticdmg-massfraction-pre-yield\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticdmg-mass-fraction-yield\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticdmg-mass-fraction-post-yield\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n govern the temporal behavior of the bond species mass fractions.\n\\end_layout\n\n\\begin_layout Subsubsection\nStrain Energy Density,\n Stress,\n and Damage\n\\end_layout\n\n\\begin_layout Standard\nRecognizing that damaged (broken) bonds do not store free energy,\n the referential mixture free energy density in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:multiplebonds-mixture-energy\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may be rewritten as\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}=\\sum_{\\beta}w_{\\beta}\\left(w_{\\beta}^{s}\\Psi_{0}\\left(\\mathbf{F}^{s}\\right)+w_{\\beta}^{y}J^{ys}\\Psi_{0}\\left(\\mathbf{F}_{\\beta}^{y}\\right)\\right)\\,,\\label{eq:plasticdmg-free-energy}\n\\end{equation}\n\n\\end_inset\n\nwhere the bond mass fractions \n\\begin_inset Formula $w_{\\beta}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n are given in Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticdmg-massfraction-pre-yield\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:plasticdmg-mass-fraction-post-yield\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n prior to,\n during,\n and after yielding of each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n Similarly,\n the mixture stress may be evaluated from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:multiplebonds-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sum_{\\beta}w_{\\beta}\\left(w_{\\beta}^{s}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}^{s}\\right)+w_{\\beta}^{y}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{y}\\right)\\right)\\,,\\label{eq:plasticdmg-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere the stresses \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n are given by the standard hyperelasticity relation in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:multiplebonds-mixture-stress\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n These expressions may be simplified further when assuming that the functional form of \n\\begin_inset Formula $\\psi_{\\beta}$\n\\end_inset\n\n remains the same for all bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n Finally,\n the reactive mixture equivalent of the damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n may be evaluated for elastoplastic damage as the fraction of all bonds that are broken,\n\\begin_inset Formula \n\\begin{equation}\nD=w^{b}=\\sum_{\\beta}w_{\\beta}w_{\\beta}^{b}\\,.\\label{eq:plasticdmg-damage-variable}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nDamage Measures\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Yielded-damage-reaction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor elastic damage,\n we may use the same functional measure as proposed for plastic yielding (e.g.,\n the von Mises stress);\n this implies that the functions \n\\begin_inset Formula $\\Xi^{e}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n have the same form.\n For plastic damage,\n experimental results show that during plastic flow damage is coupled with measures of plastic strain \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lemaitre05\"\nliteral \"false\"\n\n\\end_inset\n\n,\n necessitating a (pseudo-)strain-based plastic damage measure \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n.\n For yielded bonds in a bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n we can use the constitutively-determined mapping \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n to define plastic right Cauchy-Green and Lagrange strain tensors through\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{C}_{\\beta}^{ys} & =\\left(\\mathbf{F}_{\\beta}^{ys}\\right)^{T}\\cdot\\mathbf{F}_{\\beta}^{ys}\\\\\n\\mathbf{E}_{\\beta}^{ys} & =\\frac{1}{2}\\left(\\mathbf{C}_{\\beta}^{ys}-\\mathbf{I}\\right)\n\\end{aligned}\n\\,.\n\\end{equation}\n\n\\end_inset\n\nOne possible constitutive relation for \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n,\n which remains valid for general deformations,\n is to set it equal to the \n\\shape italic\neffective plastic strain\n\\shape default\n \n\\begin_inset Formula $e_{\\beta}^{p}$\n\\end_inset\n\n for the various bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\ne_{\\beta}^{p}=\\sqrt{\\frac{2}{3}\\dev\\mathbf{E}_{\\beta}^{ys}:\\dev\\mathbf{E}_{\\beta}^{ys}}\\,.\\label{eq:plasticdmg-effective-plastic-strain}\n\\end{equation}\n\n\\end_inset\n\nIn a numerical implementation,\n the effective plastic strain \n\\begin_inset Formula $e_{0}^{p}$\n\\end_inset\n\n of the first bond family to yield may be reported as the effective plastic strain in the entire material,\n for consistency with plastic strain measures in classical models of plasticity.\n\\end_layout\n\n\\begin_layout Standard\nQuantities in this section do not represent plastic strains or plastic strain tensors,\n though we adopt the terminology due to similarities.\n Recall that the non-observable function of state \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}=\\mathbf{F}_{\\beta}^{ys}\\left(\\mathbf{F}^{s},\\rho_{r\\beta}^{\\alpha}\\right)$\n\\end_inset\n\n is a time-invariant mapping providing the reference configuration of a yielded bond \n\\begin_inset Formula $y$\n\\end_inset\n\n with respect to the reference configuration of the master constituent \n\\begin_inset Formula $s$\n\\end_inset\n\n,\n for family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n The quantities \n\\begin_inset Formula $\\mathbf{C}_{\\beta}^{ys}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{E}_{\\beta}^{ys}$\n\\end_inset\n\n then also represent non-observable functions of state calculated as strain tensors.\n Consequently,\n \n\\begin_inset Formula $e_{\\beta}^{p}$\n\\end_inset\n\n is a measure of the relative motion of the reference configuration of bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n ,\n expressed as a scalar \n\\begin_inset Quotes eld\n\\end_inset\n\nstrain\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Physically,\n this amounts to the modeling assumption that once the breaking-and-reforming process takes a bond family out of a local neighborhood centered about its original position,\n the bond begins to degrade with further breaking-and-reforming processes.\n That each of these quantities exists for every bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n emphasizes the lack of any true or unique plastic strain measure in this framework.\n\\end_layout\n\n\\begin_layout Subsubsection\nCumulative Damage Distribution Functions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cumulative-Damage-Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe final set of constitutive relations required to fully define an elastoplastic damage material are the two CDFs,\n \n\\begin_inset Formula $F^{e}\\left(\\Xi_{m\\beta}^{e}\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $F^{p}\\left(\\Xi_{m\\beta}^{p}\\right)$\n\\end_inset\n\n.\n As shown by \n\\begin_inset CommandInset citation\nLatexCommand citet\nkey \"Nims16\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the only requirement imposed by the Clausius-Duhem inequality is that these be monotonically increasing functions.\n \n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigReactivePlasticityDamage.png\n\tlyxscale 20\n\twidth 6in\n\n\\end_inset\n\n\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nParametric study of the effect of the damage parameter \n\\begin_inset Formula $\\gamma^{p}$\n\\end_inset\n\n for a Weibull distribution,\n with no intact damage taking place.\n (a) As \n\\begin_inset Formula $\\gamma^{p}$\n\\end_inset\n\n increases,\n the onset of noticeable damage shifts to higher strains and becomes more rapid.\n (b) Plot of the damage variable \n\\begin_inset Formula $D=\\sum_{\\beta}w_{\\beta}F^{p}\\left(\\Xi_{m\\beta}^{p}\\right)$\n\\end_inset\n\n.\n The response becomes more nonlinear as \n\\begin_inset Formula $\\gamma^{p}$\n\\end_inset\n\n deviates from unity.\n Other plasticity and damage parameters are \n\\begin_inset Formula $n_{f}=20$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Upsilon_{0}=600$\n\\end_inset\n\n MPa,\n \n\\begin_inset Formula $\\Upsilon_{\\text{max}}=1000$\n\\end_inset\n\n MPa,\n \n\\begin_inset Formula $w_{0}=0.75$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $r=1$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\kappa^{p}=0.03$\n\\end_inset\n\n.\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:damage-parametric\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n Whereas these CDFs may be characterized directly from experimental data,\n here we illustrate the FEBio elastoplastic damage framework using a Weibull distribution of the form\n\\begin_inset Formula \n\\begin{equation}\nF\\left(\\Xi\\right)=1-\\exp\\left(-\\left(\\frac{\\Xi}{\\kappa}\\right)^{\\gamma}\\right)\\,,\\label{eq:Weibull-cdf}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n) is the value of \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n at which the fraction \n\\begin_inset Formula $1-e^{-1}$\n\\end_inset\n\n of bonds have failed and the exponent \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n (unitless) controls the slope of the response,\n such that \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n approaches a step function with a jump at \n\\begin_inset Formula $\\Xi=\\kappa$\n\\end_inset\n\n as \n\\begin_inset Formula $\\gamma\\to\\infty$\n\\end_inset\n\n.\n Therefore,\n each damage function has two free parameters \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n.\n Based on experimental evidence,\n we may let \n\\begin_inset Formula $\\Xi^{e}$\n\\end_inset\n\n be given by the von Mises (effective) stress,\n while \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n is taken to be the effective plastic strain (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Yielded-damage-reaction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:damage-parametric\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n shows the effect of the Weibull parameter \n\\begin_inset Formula $\\gamma^{p}$\n\\end_inset\n\n on the stress-strain and damage-strain responses,\n with \n\\begin_inset Formula $\\kappa^{p}$\n\\end_inset\n\n fixed.\n The damage response as a function of plastic strain is identically the prescribed CDF (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:damage-parametric\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nb).\n The shape of the CDF changes from logarithmic-like to exponential as \n\\begin_inset Formula $\\gamma^{p}$\n\\end_inset\n\n increases,\n demonstrating the ability of this formulation to recover a broad variety of experimentally measured damage-strain behaviors \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonora97\"\nliteral \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nHydraulic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Hydraulic-Permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHydraulic permeability is a material function needed for biphasic and multiphasic materials.\n\\end_layout\n\n\\begin_layout Subsection\nConstant Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Isotropic-Permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the permeability is isotropic,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}=k\\,\\mathbf{I}\\,.\\label{eq501}\n\\end{equation}\n\n\\end_inset\n\nFor this material model,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n is constant.\n Generally,\n this assumption is only reasonable when strains are small.\n\\end_layout\n\n\\begin_layout Subsection\nExponential Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Exponential-Isotropic-Permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis isotropic material has a permeability that varies as a function of the determinant \n\\begin_inset Formula $J$\n\\end_inset\n\n of the deformation gradient.\n Its general form is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}=k\\left(J\\right)\\mathbf{I}\\,,\\label{eq:isotropic-permeability}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\nk\\left(J\\right)=k_{0}\\exp\\left(M\\frac{J-1}{J-\\varphi_{0}}\\right)\\,.\\label{eq:perm-exp-iso}\n\\end{equation}\n\n\\end_inset\n\nPore closure occurs as \n\\begin_inset Formula $J$\n\\end_inset\n\n approaches \n\\begin_inset Formula $\\varphi_{0}$\n\\end_inset\n\n from above,\n in which case the permeability reduces to zero,\n\\begin_inset Formula \n\\begin{equation}\n\\lim_{J\\to\\varphi_{0}}k\\left(J\\right)=0\\,.\\label{eq:perm-pore-closure}\n\\end{equation}\n\n\\end_inset\n\nIn the special case when \n\\begin_inset Formula $M=0$\n\\end_inset\n\n,\n the permeability becomes constant.\n In the limit of infinitesimal strains,\n the permeability has the form\n\\begin_inset Formula \n\\begin{equation}\nk\\left(J\\right)=k_{0}\\left(1+\\frac{M}{1-\\varphi_{0}}\\left(J-1\\right)+\\mathcal{O}\\left(\\left(J-1\\right)^{2}\\right)\\right)\\,.\\label{eq:perm-expiso-Taylor}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the type of isotropic permeability given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:isotropic-permeability\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the spatial permeability tangent \n\\begin_inset Formula $\\boldsymbol{\\mathcal{K}}$\n\\end_inset\n\n with respect to the solid matrix strain has the general form\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\mathcal{K}}=\\left(k+Jk^{\\prime}\\right)\\mathbf{I}\\otimes\\mathbf{I}-2k\\mathbf{I}\\odot\\mathbf{I}\\,,\\label{eq:isotropic-perm-tangent}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\nk^{\\prime}\\left(J\\right)=M\\frac{1-\\varphi_{0}}{\\left(J-\\varphi_{0}\\right)^{2}}k\\left(J\\right)\\label{eq:perm-exp-iso-tangent}\n\\end{equation}\n\n\\end_inset\n\nin this case.\n\\end_layout\n\n\\begin_layout Subsection\nHolmes-Mow\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Holmes-Mow-permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis isotropic material uses a strain-dependent permeability tensor as formulated by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}=k\\left(J\\right)\\mathbf{I}\\,,\\label{eq502}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\nk\\left(J\\right)=k_{0}\\left(\\frac{J-\\varphi_{0}}{1-\\varphi_{0}}\\right)^{\\alpha}e^{\\frac{1}{2}M\\left(J^{2}-1\\right)}\\,.\\label{eq503}\n\\end{equation}\n\n\\end_inset\n\nWhen \n\\begin_inset Formula $\\alpha>0$\n\\end_inset\n\n,\n pore closure occurs as \n\\begin_inset Formula $J\\to\\varphi_{0}$\n\\end_inset\n\n from above,\n in which case \n\\begin_inset Formula $k$\n\\end_inset\n\n reduces to \n\\begin_inset Formula $0$\n\\end_inset\n\n.\n Setting \n\\begin_inset Formula $\\alpha=0$\n\\end_inset\n\n and \n\\begin_inset Formula $M=0$\n\\end_inset\n\n produces a constant permeability.\n In the limit of infinitesimal strains,\n\\begin_inset Formula \n\\begin{equation}\nk\\left(J\\right)=k_{0}\\left(1+\\left(M+\\frac{\\alpha}{1-\\varphi_{0}}\\right)\\left(J-1\\right)+\\mathcal{O}\\left(\\left(J-1\\right)^{2}\\right)\\right)\\,.\\label{eq:perm-HM-Taylor}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe spatial tangent of the permeability tensor with respect to strain may be evaluated from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:isotropic-perm-tangent\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n using\n\\begin_inset Formula \n\\begin{equation}\nk^{\\prime}\\left(J\\right)=\\left(J\\,M+\\frac{\\alpha}{J-\\varphi_{0}}\\right)k\\left(J\\right)\\,.\\label{eq:perm-HM-tangent}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nReferentially Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Isotropic-Permeabi\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent permeability tensor that accommodates strain-induced anisotropy \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}=\\left(k_{0r}\\mathbf{I}+\\frac{k_{1r}}{J^{2}}\\mathbf{b}+\\frac{k_{2r}}{J^{4}}\\mathbf{b}^{2}\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)e^{M\\left(J^{2}-1\\right)/2}\\,,\\label{eq504}\n\\end{equation}\n\n\\end_inset\n\nNote that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I})$\n\\end_inset\n\n is isotropic and given by \n\\begin_inset Formula $\\mathbf{k}=\\left(k_{0r}+k_{1r}+k_{2r}\\right)\\mathbf{I}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nReferentially Orthotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Orthotropic-Permea\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent permeability tensor that is orthotropic in the reference configuration,\n and accommodates strain-induced anisotropy \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{k}=k_{0}\\mathbf{I}+\\sum\\limits_{a=1}^{3}k_{1}^{a}\\mathbf{m}_{a}+k_{2}^{a}\\left(\\mathbf{m}_{a}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}_{a}\\right)\\,,\\label{eq505}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & k_{0}=k_{0r}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{0}}}e^{M_{^{0}}\\left(J^{2}-1\\right)/2}\\,,\\\\\n & k_{1}^{a}=\\frac{k_{1r}^{a}}{J^{2}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{a}}}e^{M_{^{a}}\\left(J^{2}-1\\right)/2}\\,,\\\\\n & k_{2}^{a}=\\frac{k_{2r}^{a}}{2J^{4}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{a}}}e^{M_{a}\\left(J^{2}-1\\right)/2}\\,.\n\\end{aligned}\n\\quad a=1,2,3\\label{eq506}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{m}_{a}$\n\\end_inset\n\n are second order tensors representing the spatial structural tensors describing the orthogonal planes of symmetry,\n given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}_{a}=\\mathbf{F}\\cdot\\left(\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}\\right)\\cdot\\mathbf{F}^{T},\\quad a=1-3,\\label{eq507}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n are orthonormal vectors normal to the planes of symmetry.\n Note that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I}$\n\\end_inset\n\n) is given by \n\\begin_inset Formula $\\mathbf{k}=k_{0r}\\mathbf{I}+\\sum\\limits_{a=1}^{3}\\left(k_{1r}^{a}+k_{2r}^{a}\\right)\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nReferentially Transversely Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-TISO-permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent permeability tensor that is transversely isotropic in the reference configuration,\n and accommodates strain-induced anisotropy \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k} & =k_{0r}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{0}}e^{M_{^{0}}\\left(J^{2}-1\\right)/2}\\mathbf{I}\\\\\n & +\\left(\\frac{k_{1r}^{T}}{J^{2}}\\left(\\mathbf{b}-\\mathbf{m}\\right)+\\frac{k_{2r}^{T}}{2J^{4}}\\left[2\\mathbf{b}^{2}-\\left(\\mathbf{m}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}\\right)\\right]\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{T}}e^{M_{T}\\left(J^{2}-1\\right)/2}\\\\\n & +\\left(\\frac{1}{J^{2}}k_{1r}^{A}\\mathbf{m}+\\frac{1}{2J^{4}}k_{2r}^{A}\\left(\\mathbf{m}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}\\right)\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{A}}e^{M_{A}\\left(J^{2}-1\\right)/2}\\,,\n\\end{aligned}\n\\label{eq508}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{m}$\n\\end_inset\n\n is a second order tensor representing the spatial structural tensor describing the axial direction,\n given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}=\\mathbf{F}\\cdot\\left(\\mathbf{V}\\otimes\\mathbf{V}\\right)\\cdot\\mathbf{F}^{T}\\,,\\label{eq509}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is a unit vector along the axial direction.\n Note that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I}$\n\\end_inset\n\n) is given by \n\\begin_inset Formula $\\mathbf{k}=\\left(k_{0r}+k_{1r}^{T}+k_{2r}^{T}\\right)\\mathbf{I}+\\left(k_{1r}^{A}-k_{1r}^{T}+k_{2r}^{A}-k_{2r}^{T}\\right)\\left(\\mathbf{V}\\otimes\\mathbf{V}\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nSolute Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Solute-Diffusivity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDiffusivity materials provide a constitutive relation for the solute diffusivity in a biphasic-solute material.\n In general,\n the diffusivity tensor \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n may be a function of strain and solute concentration.\n\\end_layout\n\n\\begin_layout Subsection\nConstant Isotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Isotropic-Diffusivity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the permeability is isotropic,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}=d\\,\\mathbf{I}\\,.\\label{eq510}\n\\end{equation}\n\n\\end_inset\n\nFor this material model,\n \n\\begin_inset Formula $d$\n\\end_inset\n\n is constant.\n This assumption is only true when strains are small.\n Note that the user must specify \n\\begin_inset Formula $d\\leqslant d_{0}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n is the solute diffusivity in free solution,\n since a solute cannot diffuse through the biphasic-solute mixture faster than in free solution.\n\\end_layout\n\n\\begin_layout Subsection\nConstant Orthotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Orthotropic-Diffusivity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the permeability is orthotropic,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}=\\sum\\limits_{a=1}^{3}d^{a}\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}\\,,\\label{eq511}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n are orthonormal vectors normal to the planes of symmetry.\n For this material model,\n the \n\\begin_inset Formula $d^{a}$\n\\end_inset\n\n are constant.\n Therefore this model should be used only when strains are small.\n Note that the user must specify \n\\begin_inset Formula $d^{a}\\leqslant d_{0}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n is the solute diffusivity in free solution,\n since a solute cannot diffuse through the biphasic-solute mixture faster than in free solution.\n\\end_layout\n\n\\begin_layout Subsection\nReferentially Isotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Isotropic-Diffusiv\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent diffusivity tensor that is isotropic in the reference configuration and accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}=\\left(d_{0r}\\mathbf{I}+\\frac{d_{1r}}{J^{2}}\\mathbf{b}+\\frac{d_{2r}}{J^{4}}\\mathbf{b}^{2}\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)e^{M\\left(J^{2}-1\\right)/2},\\label{eq512}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J$\n\\end_inset\n\n is the jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient,\n and \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n Note that the diffusivity in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I}$\n\\end_inset\n\n) is isotropic and given by \n\\begin_inset Formula $\\mathbf{d}=\\left(d_{0r}+d_{1r}+d_{2r}\\right)\\mathbf{I}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nReferentially Orthotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Orthotropic-Diffus\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent diffusivity tensor that is orthotropic in the reference configuration and accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{d}=d_{0}\\mathbf{I}+\\sum\\limits_{a=1}^{3}d_{1}^{a}\\mathbf{m}_{a}+d_{2}^{a}\\left(\\mathbf{m}_{a}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}_{a}\\right)\\,,\\label{eq513}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}d_{0} & =d_{0r}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{0}}}e^{M_{^{0}}\\left(J^{2}-1\\right)/2}\\,,\\\\\nd_{1}^{a} & =\\frac{d_{1r}^{a}}{J^{2}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{a}}}e^{M_{^{a}}\\left(J^{2}-1\\right)/2}\\,,\\\\\nd_{2}^{a} & =\\frac{d_{2r}^{a}}{2J^{4}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{a}}}e^{M_{a}\\left(J^{2}-1\\right)/2}\\,.\n\\end{aligned}\n\\quad a=1,2,3\\label{eq514}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where\n\\series bold\n \n\\series default\n\n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient.\n \n\\begin_inset Formula $\\mathbf{m}_{a}$\n\\end_inset\n\n are second order tensor representing the spatial structural tensors describing the orthogonal planes of symmetry,\n given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}_{a}=\\mathbf{F}\\cdot\\left(\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}\\right)\\cdot\\mathbf{F}^{T},\\quad a=1-3\\,,\\label{eq515}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n are orthonormal vectors normal to the planes of symmetry.\n Note that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I}$\n\\end_inset\n\n) is given by \n\\begin_inset Formula $\\mathbf{k}=k_{0r}\\mathbf{I}+\\sum\\limits_{a=1}^{3}\\left(k_{1r}^{a}+k_{2r}^{a}\\right)\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nSolute Solubility\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Solute-Solubility\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSolubility constitutive equations provide a relation for \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n as a function of solid matrix strain and effective solute concentrations.\n\\end_layout\n\n\\begin_layout Subsection\nConstant Solubility\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Solubility\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material model,\n \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n is constant.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nOsmotic Coefficient\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Osmotic-Coefficient\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nOsmotic coefficient constitutive equations provide a relation for \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n as a function of solid matrix strain and effective solute concentrations.\n\\end_layout\n\n\\begin_layout Subsection\nConstant Osmotic Coefficient\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Osmotic-Coefficient\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material model,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is constant.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nActive Contraction Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Active-Contraction-Model\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA time varying \n\\begin_inset Quotes eld\n\\end_inset\n\nelastance\n\\begin_inset Quotes erd\n\\end_inset\n\n active contraction model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Guccione93\"\nliteral \"true\"\n\n\\end_inset\n\n was added to the transversely isotropic materials.\n When active contraction is activated,\n the total Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is defined as the sum of the active stress tensor \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}=T^{a}\\mathbf{a}\\otimes\\mathbf{a}$\n\\end_inset\n\n and the passive stress tensor \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{p}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\boldsymbol{\\sigma}^{p}+\\boldsymbol{\\sigma}^{a},\\label{eq516}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\series bold\n\\emph on\na\n\\series default\n\\emph default\n is the deformed fiber vector (unit length),\n defined as \n\\begin_inset Formula $\\lambda\\mathbf{a}=\\mathbf{F}\\cdot\\mathbf{a}$\n\\end_inset\n\n.\n The time varying elastance model is a modification of the standard Hill equation that scales the standard equation by an activation curve \n\\begin_inset Formula $C\\left(t\\right)$\n\\end_inset\n\n.\n The active fiber stress \n\\begin_inset Formula $T^{a}$\n\\end_inset\n\n is defined as:\n \n\\begin_inset Formula \n\\begin{equation}\nT^{a}=T_{\\max}\\frac{Ca_{0}^{2}}{Ca_{0}^{2}+ECa_{50}^{2}}C\\left(t\\right),\\label{eq517}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $T_{max}=135.7\\,\\text{kPa}$\n\\end_inset\n\n is the isometric tension under maximal activation at the peak intracellular calcium concentration of\n\\emph on\n \n\\begin_inset Formula $\\text{Ca}_{0}=4.35\\,\\mu\\text{M}$\n\\end_inset\n\n\n\\emph default\n.\n The length dependent calcium sensitivity is governed by the following equation:\n \n\\begin_inset Formula \n\\begin{equation}\nECa_{50}=\\frac{\\left(\\text{Ca}_{0}\\right)_{\\max}}{\\sqrt{\\exp\\left[B(l-l_{0})\\right]-1}}\\,,\\label{eq518}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left(\\text{Ca}_{0}\\right)_{\\max}=4.35\\,\\mu\\text{M}$\n\\end_inset\n\n is the maximum peak intracellular calcium concentration,\n \n\\begin_inset Formula $B=4.75\\,\\mu\\text{m}^{-1}$\n\\end_inset\n\n governs the shape of the peak isometric tension-sarcomere length relation,\n \n\\begin_inset Formula $l_{0}=1.58\\,\\mu\\text{m}$\n\\end_inset\n\n is the sarcomere length at which no active tension develops,\n and \n\\begin_inset Formula $l$\n\\end_inset\n\n is the sarcomere length which is the product of the fiber stretch \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and the sarcomere unloaded length \n\\begin_inset Formula $l_{r}=2.04\\,\\mu\\text{m}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nPrescribed Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Prescribed-Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPrescribed active contraction models allow the user to directly specify the time history of the active contractile stress.\n\\end_layout\n\n\\begin_layout Subsection\nUniaxial Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uniaxial-Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this model,\n the active stress is acting along a prescribed direction given by the unit vector \n\\begin_inset Formula $\\mathbf{a}_{r}$\n\\end_inset\n\n in the reference configuration.\n The \n\\begin_inset Formula $2^{\\text{nd}}$\n\\end_inset\n\n Piola-Kirchhoff stress is \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}^{a}=T^{a}\\mathbf{a}_{r}\\otimes\\mathbf{a}_{r}\\,,\\label{eq519}\n\\end{equation}\n\n\\end_inset\n\nand the Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{a}=J^{-1}T^{a}\\mathbf{a}\\otimes\\mathbf{a}\\,,\\label{eq520}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $T^{a}$\n\\end_inset\n\n is the prescribed contractile stress and \n\\begin_inset Formula $\\mathbf{a}=\\mathbf{F}\\cdot\\mathbf{a}_{r}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\mathbf{S}^{a}$\n\\end_inset\n\n is not a function of deformation,\n the material and spatial tangents are both zero.\n\\end_layout\n\n\\begin_layout Subsection\nTransversely Isotropic Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:TISO-Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn this case,\n the active stress is isotropic in a plane transverse to the direction \n\\begin_inset Formula $\\mathbf{a}_{r}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}^{a}=T^{a}\\left(\\mathbf{I}-\\mathbf{a}_{r}\\otimes\\mathbf{a}_{r}\\right)\\,,\\label{eq521}\n\\end{equation}\n\n\\end_inset\n\nand the corresponding Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{a}=J^{-1}T^{a}\\left(\\mathbf{B}-\\mathbf{a}\\otimes\\mathbf{a}\\right)\\,,\\label{eq522}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{B}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n The material and spatial tangents are zero.\n\\end_layout\n\n\\begin_layout Subsection\nIsotropic Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Isotropic-Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAn isotropic active contractile stress is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}^{a}=T^{a}\\mathbf{I}\\label{eq523}\n\\end{equation}\n\n\\end_inset\n\nand the corresponding Cauchy stress is \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{a}=J^{-1}T^{a}\\mathbf{B}\\,.\\label{eq524}\n\\end{equation}\n\n\\end_inset\n\nThe material and spatial tangents are zero.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nChemical Reaction Production Rate\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:CR-Production-Rate\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nProduction rate constitutive equations provide a relation for \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n as a function of solid matrix strain,\n solute concentrations,\n and the concentrations of solid-bound molecular species.\n\\end_layout\n\n\\begin_layout Subsection\nMass Action Forward\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mass-Action-Forward\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAccording to the law of mass action for forward reactions,\n \n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\zeta}=k\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu_{R}^{\\alpha}}\\,.\\label{eq525}\n\\end{equation}\n\n\\end_inset\n\nA constitutive relation for the specific reaction rate \n\\begin_inset Formula $k\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)$\n\\end_inset\n\n must also be provided.\n\\end_layout\n\n\\begin_layout Subsection\nMass Action Reversible\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mass-Action-Reversible\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAccording to the law of mass action for reversible reactions,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\hat{\\zeta}_{F} & =k_{F}\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu_{R}^{\\alpha}}\\\\\n\\hat{\\zeta}_{R} & =k_{R}\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu_{P}^{\\alpha}}\\\\\n\\hat{\\zeta} & =\\hat{\\zeta}_{F}-\\hat{\\zeta}_{R}=\\hat{\\zeta}_{F}\\left[1-K_{c}\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu^{\\alpha}}\\right]\\,,\n\\end{aligned}\n\\label{eq526}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K_{c}=k_{R}/k_{F}$\n\\end_inset\n\n is a function that reduces to the equilibrium constant of the reversible reaction at chemical equilibrium (when \n\\begin_inset Formula $\\hat{\\zeta}=0)$\n\\end_inset\n\n.\n Constitutive relations for the specific forward and reverse reaction rates,\n \n\\begin_inset Formula $k_{F}\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $k_{R}\\left(\\theta,\\mathbf{F},\\rho_{r}^{\\sigma}\\right)$\n\\end_inset\n\n respectively,\n must also be provided.\n\\end_layout\n\n\\begin_layout Subsection\nMichaelis-Menten\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Michaelis-Menten\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMichaelis-Menten is a model for enzyme kinetics as represented by the reactions \n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{E}^{e}+\\mathcal{E}^{s}\\rightleftharpoons\\mathcal{E}^{es}\\to\\mathcal{E}^{e}+\\mathcal{E}^{p}\\,,\\label{eq527}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathcal{E}^{e}$\n\\end_inset\n\n is the enzyme,\n \n\\begin_inset Formula $\\mathcal{E}^{s}$\n\\end_inset\n\n is the substrate,\n \n\\begin_inset Formula $\\mathcal{E}^{es}$\n\\end_inset\n\n is the enzyme-substrate complex,\n and \n\\begin_inset Formula $\\mathcal{E}^{p}$\n\\end_inset\n\n is the product.\n The molar mass supply \n\\begin_inset Formula $\\hat{c}^{p}$\n\\end_inset\n\n producing \n\\begin_inset Formula $\\mathcal{E}^{p}$\n\\end_inset\n\n is related to the concentration of the substrate \n\\begin_inset Formula $\\mathcal{E}^{s}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\begin{equation}\n\\hat{c}^{p}=\\frac{V_{max}c^{s}}{K_{m}+c^{s}}\\,,\\label{eq528}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $V_{max}$\n\\end_inset\n\n is the maximum rate achieved by the system,\n at maximum (saturating) substrate concentrations.\n \n\\begin_inset Formula $K_{m}$\n\\end_inset\n\n is the substrate concentration at which the reaction rate is half of \n\\begin_inset Formula $V_{max}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThis relation may be derived by applying the law of mass action to the two reactions in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq527\"\nnolink \"false\"\n\n\\end_inset\n\n.\n under the simplifying assumption that the reversible reaction between the enzyme and substrate reaches steady state much faster than the subsequent forward reaction forming the product.\n If the first and second reactions are denoted by subscripts 1 and 2,\n respectively,\n the law of mass action for the first (reversible) and second (forwar) reaction produces \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\hat{\\zeta}_{1} & =k_{F1}c^{e}c^{s}-k_{R1}c^{es},\\quad\\hat{\\zeta}_{2}=k_{F2}c^{es}\\,,\\\\\n\\hat{c}^{s} & =-\\hat{\\zeta}_{1},\\quad\\hat{c}^{p}=\\hat{\\zeta}_{2},\\quad\\hat{c}^{es}=\\hat{\\zeta}_{1}-\\hat{\\zeta}_{2}\\,.\n\\end{aligned}\n\\label{eq529}\n\\end{equation}\n\n\\end_inset\n\nThe total enzyme concentration remains constant at \n\\begin_inset Formula $c_{0}^{e}=c^{e}+c^{es}$\n\\end_inset\n\n,\n so that\n\\begin_inset Formula $\\hat{\\zeta}_{1}=k_{F1}c_{0}^{e}c^{s}-\\left(k_{F1}c^{s}+k_{R1}\\right)c^{es}$\n\\end_inset\n\n.\n Assuming that the first reaction equilibrates much faster than the second is equivalent to letting \n\\begin_inset Formula $\\hat{\\zeta}_{1}\\approx0$\n\\end_inset\n\n,\n in which case \n\\begin_inset Formula \n\\begin{equation}\nc^{es}\\approx\\frac{c_{0}^{e}c^{s}}{c^{s}+K_{m}}\\,,\\label{eq530}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K_{m}=k_{R1}/k_{F1}$\n\\end_inset\n\n.\n Then,\n \n\\begin_inset Formula \n\\[\n\\hat{\\zeta}_{2}=\\frac{V_{\\max}c^{s}}{c^{s}+K_{m}}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $V_{\\max}=k_{F2}c_{0}^{e}$\n\\end_inset\n\n represents the maximum value of \n\\begin_inset Formula $\\hat{\\zeta}_{2}$\n\\end_inset\n\n,\n when \n\\begin_inset Formula $K_{m}\\ll c^{s}$\n\\end_inset\n\n.\n In practice,\n choosing \n\\begin_inset Formula $k_{F1}\\gg k_{F2}$\n\\end_inset\n\n can produce the desired effect.\n\\end_layout\n\n\\begin_layout Section\nSpecific Reaction Rate\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Specific-Reaction-Rate\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSpecific reaction rate constitutive equations provide a relation for \n\\begin_inset Formula $k$\n\\end_inset\n\n as a function of solid matrix strain and the concentrations of solid-bound molecular species.\n\\end_layout\n\n\\begin_layout Subsection\nConstant Specific Reaction Rate\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Specific-Reaction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material model,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n is constant.\n\\end_layout\n\n\\begin_layout Subsection\nHuiskes Remodeling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Huiskes-Remodeling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material,\n which is based on the bone remodeling framework of Weinans et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weinans92\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the specific reaction rate depends on the deviation of the specific strain energy from a threshold value,\n \n\\begin_inset Formula \n\\begin{equation}\nk\\left(\\mathbf{F},\\rho_{r}^{s}\\right)=\\frac{B}{\\left(J-\\varphi_{r}^{s}\\right)M^{s}}\\left(\\frac{\\Psi_{r}}{\\rho_{r}^{s}}-\\psi_{0}\\right)\\,,,\\label{eq531}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $B$\n\\end_inset\n\n is a constant,\n \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n is the strain energy density of the solid,\n \n\\begin_inset Formula $\\rho_{r}^{s}$\n\\end_inset\n\n is the referential mass density of the solid,\n \n\\begin_inset Formula $\\psi_{0}$\n\\end_inset\n\n is the threshold value for the specific strain energy.\n In this relation,\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is evaluated from the solid deformation and \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is evaluated from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:referential-solid-volume-fraction\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn the subsequent study by Mullender et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mullender94\"\nliteral \"false\"\n\n\\end_inset\n\n it was proposed to let osteocytes serve as sensing cells,\n such that the remodeling rate may depend on cells in the neighborhood of the the point at which remodeling is calculated.\n In that case,\n the model of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq531\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is extended to include the contribution from neighboring sensing cells,\n \n\\begin_inset Formula \n\\begin{equation}\nk\\left(\\mathbf{F},\\rho_{r}^{s}\\right)=\\frac{1}{\\left(J-\\varphi_{r}^{s}\\right)M^{s}}\\left(B\\left(\\frac{\\Psi_{r}}{\\rho_{r}^{s}}-\\psi_{0}\\right)+\\sum_{i=1}^{N}e^{-d_{i}/D}B_{i}\\left(\\frac{\\Psi_{ri}}{\\rho_{ri}^{s}}-\\psi_{0}\\right)\\right)\\,.\\label{eq:eq531b}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $N$\n\\end_inset\n\n represents the number of elements in the neighborhood of the current material point element,\n which fall within a characteristic sensing distance \n\\begin_inset Formula $D$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $d_{i}$\n\\end_inset\n\n represents the distance between these neighboring points and the current material point.\n\\end_layout\n\n\\begin_layout Section\nViscous Fluids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Viscous-Fluids\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe most common family of constitutive relations employed for viscous fluids,\n including Newtonian fluids,\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\tau}\\left(J,\\mathbf{D}\\right)=\\left(\\kappa-\\frac{2}{3}\\mu\\right)\\left(\\tr\\mathbf{D}\\right)\\,\\mathbf{I}+2\\mu\\,\\mathbf{D}\\,,\\label{eq:viscous-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n and \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n are,\n respectively,\n the dynamic shear and bulk viscosity coefficients (both positive),\n which may generally depend on \n\\begin_inset Formula $J$\n\\end_inset\n\n and,\n for non-Newtonian fluids,\n on invariants of \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n.\n In practice,\n most constitutive models for non-Newtonian viscous fluids only use a dependence on \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n,\n since it is the only non-zero invariant in viscometric flows \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Reddy08\"\nliteral \"true\"\n\n\\end_inset\n\n.\n In this case,\n substituting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-stress\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stress-tangent-D\"\nnolink \"false\"\n\n\\end_inset\n\n produces\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\mathcal{C}}^{\\tau} & =\\left(\\kappa-\\frac{2}{3}\\mu\\right)\\mathbf{I}\\otimes\\mathbf{I}+\\frac{2}{\\dot{\\gamma}}\\left(\\frac{\\partial\\kappa}{\\partial\\dot{\\gamma}}-\\frac{2}{3}\\frac{\\partial\\mu}{\\partial\\dot{\\gamma}}\\right)\\left(\\tr\\mathbf{D}\\right)\\mathbf{I}\\otimes\\mathbf{D}\\\\\n & +2\\left(\\frac{2}{\\dot{\\gamma}}\\frac{\\partial\\mu}{\\partial\\dot{\\gamma}}\\mathbf{D}\\otimes\\mathbf{D}+\\mu\\,\\mathbf{I}\\odot\\mathbf{I}\\right)\\,.\n\\end{aligned}\n\\label{eq:viscous-tangent-D}\n\\end{equation}\n\n\\end_inset\n\nThe term containing \n\\begin_inset Formula $\\mathbf{I}\\otimes\\mathbf{D}$\n\\end_inset\n\n is the only one that does not exhibit major symmetry.\n In Newtonian fluids,\n \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n and \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n are independent of \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n;\n in incompressible fluids they are independent of \n\\begin_inset Formula $J$\n\\end_inset\n\n (since \n\\begin_inset Formula $J=1$\n\\end_inset\n\n remains constant and \n\\begin_inset Formula $\\tr\\mathbf{D}=0$\n\\end_inset\n\n).\n Thus,\n for both of these cases the term containing \n\\begin_inset Formula $\\mathbf{I}\\otimes\\mathbf{D}$\n\\end_inset\n\n drops out and \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{\\tau}$\n\\end_inset\n\n exhibits major symmetry.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-stress\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the tangent \n\\begin_inset Formula $\\boldsymbol{\\tau}_{J}^{\\prime}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stress-tangent-J\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\tau}_{J}^{\\prime}=\\left(\\frac{\\partial\\kappa}{\\partial J}-\\frac{2}{3}\\frac{\\partial\\mu}{\\partial J}\\right)\\left(\\tr\\mathbf{D}\\right)\\,\\mathbf{I}+2\\frac{\\partial\\mu}{\\partial J}\\,\\mathbf{D}\\,.\\label{eq:viscous-tangent-J}\n\\end{equation}\n\n\\end_inset\n\nExplicit forms for the dependence of \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n or \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n on \n\\begin_inset Formula $J$\n\\end_inset\n\n are not illustrated here,\n since viscosity generally shows negligible dependence on pressure (thus \n\\begin_inset Formula $J$\n\\end_inset\n\n) over typical ranges of pressures in fluids,\n hence \n\\begin_inset Formula $\\boldsymbol{\\tau}_{J}^{\\prime}\\approx\\mathbf{0}$\n\\end_inset\n\n in most analyses.\n\\end_layout\n\n\\begin_layout Standard\nMany fluid mechanics textbooks employ Stoke's condition (\n\\begin_inset Formula $\\kappa=0$\n\\end_inset\n\n) for the purpose of equating the elastic pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n with the mean normal stress \n\\begin_inset Formula $-\\frac{1}{3}\\tr\\boldsymbol{\\sigma}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Panton06\"\nliteral \"true\"\n\n\\end_inset\n\n;\n in FEBio,\n \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n is kept as a user-defined material property,\n which may be set to zero if desired.\n A common example of a non-Newtonian fluid is the Carreau model,\n where \n\\begin_inset Formula $\\boldsymbol{\\tau}=2\\mu\\left(\\dot{\\gamma}\\right)\\mathbf{D}$\n\\end_inset\n\n,\n which is a special case of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-stress\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\kappa=2\\mu/3$\n\\end_inset\n\n and\n\\begin_inset Formula \n\\begin{equation}\n\\mu=\\mu_{\\infty}+\\left(\\mu_{0}-\\mu_{\\infty}\\right)\\left(1+\\left(\\lambda\\dot{\\gamma}\\right)^{2}\\right)^{\\left(n-1\\right)/2}\\,,\\label{eq:Carreau-model-viscosity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is a time constant,\n \n\\begin_inset Formula $n$\n\\end_inset\n\n is a parameter governing the power-law response,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n is the viscosity when \n\\begin_inset Formula $\\dot{\\gamma}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu_{\\infty}$\n\\end_inset\n\n is the viscosity as \n\\begin_inset Formula $\\dot{\\gamma}\\to\\infty$\n\\end_inset\n\n.\n Other common models of non-Newtonian viscous fluids are summarized in \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Cho91\"\nliteral \"true\"\n\n\\end_inset\n\n,\n though it should be noted that some of these models produce infinite values when evaluating \n\\begin_inset Formula $\\dot{\\gamma}^{-1}\\partial\\mu/\\partial\\dot{\\gamma}$\n\\end_inset\n\n as \n\\begin_inset Formula $\\dot{\\gamma}\\to0$\n\\end_inset\n\n,\n which is problematic in the evaluation of \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{\\tau}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:viscous-tangent-D\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor nearly incompressible fluids,\n a simple constitutive relation may be adopted for the pressure,\n\\begin_inset Formula \n\\begin{equation}\np\\left(J\\right)=K\\left(1-J\\right)\\,,\\label{eq:ideal-fluid}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K$\n\\end_inset\n\n is the bulk modulus of the fluid in the limit when \n\\begin_inset Formula $J=1$\n\\end_inset\n\n.\n It follows that \n\\begin_inset Formula $p^{\\prime}\\left(J\\right)=-K$\n\\end_inset\n\n and \n\\begin_inset Formula $p^{\\prime\\prime}\\left(J\\right)=0$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Wint-linearization-J\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This constitutive relation is adopted for nearly-incompressible CFD analyses in FEBio,\n though alternative formulations may be easily implemented.\n\\end_layout\n\n\\begin_layout Chapter\nDynamics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Dynamics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio can perform a nonlinear dynamic analysis by iteratively solving the following nonlinear semi-discrete finite element equations \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bathe82\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & \\mathbf{M\\ddot{d}}_{n+1}^{k}+\\mathbf{K}\\Delta\\mathbf{d}^{k}=\\mathbf{T}_{n+1}^{k}-\\mathbf{F}_{n+1}\\\\\n & \\mathbf{d}_{n+1}^{k}=\\mathbf{d}_{n+1}^{k-1}+\\Delta\\mathbf{d}^{k}\n\\end{aligned}\n\\,.\\label{eq717}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{M}$\n\\end_inset\n\n\n\\series default\n is the mass matrix,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{K}$\n\\end_inset\n\n\n\\series default\n the stiffness matrix,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n\n\\series default\n the internal force (stress) vector and \n\\series bold\n\n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n\n\\series default\n the externally applied loads.\n The upperscript index \n\\begin_inset Formula $k$\n\\end_inset\n\n refers to the iteration number,\n the subscript \n\\begin_inset Formula $n$\n\\end_inset\n\nrefers to the time increment.\n The trapezoidal (or midpoint) rule is used to perform the time integration.\n This results in the following approximations for the displacement and velocity updates.\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{d}_{n+1} & =\\mathbf{d}_{n}+\\frac{h}{2}\\left(\\mathbf{\\dot{d}}_{n}+\\mathbf{\\dot{d}}_{n+1}\\right)\\\\\n\\mathbf{\\dot{d}}_{n+1} & =\\mathbf{\\dot{d}}_{n}+\\frac{h}{2}\\left(\\mathbf{\\ddot{d}}_{n}+\\mathbf{\\ddot{d}}_{n+1}\\right)\n\\end{aligned}\n\\,.\\label{eq718}\n\\end{equation}\n\n\\end_inset\n\nUsing \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq718\"\nnolink \"false\"\n\n\\end_inset\n\n we can solve for \n\\begin_inset Formula $\\mathbf{\\ddot{d}}_{n+1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\ddot{d}}_{n+1}^{k}=\\frac{4}{h^{2}}\\left(\\mathbf{d}_{n+1}^{k-1}-\\mathbf{d}_{n}+\\Delta\\mathbf{d}^{k}\\right)-\\frac{4}{h}\\mathbf{\\dot{d}}_{n}-\\mathbf{\\ddot{d}}_{n}\\,.\\label{eq719}\n\\end{equation}\n\n\\end_inset\n\nSubstituting this into equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq717\"\nnolink \"false\"\n\n\\end_inset\n\n results in the following linear system of equations.\n \n\\begin_inset Formula \n\\begin{equation}\n\\left(\\frac{4}{h^{2}}\\mathbf{M}+\\mathbf{K}\\right)\\Delta\\mathbf{d}^{k}=\\mathbf{T}_{n+1}^{k}-\\mathbf{F}_{n+1}-\\mathbf{M}\\left(\\frac{4}{h^{2}}\\left(\\mathbf{d}_{n+1}^{k-1}-\\mathbf{d}_{n}\\right)-\\frac{4}{h}\\mathbf{\\dot{d}}_{n}-\\mathbf{\\ddot{d}}_{n}\\right)\\,.\\label{eq720}\n\\end{equation}\n\n\\end_inset\n\nSolving this equation for \n\\begin_inset Formula $\\Delta\\mathbf{d}^{k}$\n\\end_inset\n\n and using \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq717\"\nnolink \"false\"\n\n\\end_inset\n\n gives the new displacement vector \n\\begin_inset Formula $\\mathbf{d}_{n+1}^{k}$\n\\end_inset\n\n .\n The acceleration vector \n\\begin_inset Formula $\\mathbf{\\ddot{d}}_{n+1}^{k}$\n\\end_inset\n\n can then be found from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq719\"\nnolink \"false\"\n\n\\end_inset\n\n and the velocity vector \n\\begin_inset Formula $\\mathbf{\\dot{d}}_{n+1}^{k}$\n\\end_inset\n\n from\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq719\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This algorithm is repeated until convergence is reached.\n\\end_layout\n\n\\begin_layout Section\nNewmark Integration\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Newmark-Integration\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo solve a differential equation which is second-order in time,\n we need to perform a numerical integration in the time domain.\n Let \n\\begin_inset Formula $\\theta\\left(t\\right)$\n\\end_inset\n\n denote the function of interest and let \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n represent consecutive time steps such that \n\\begin_inset Formula $\\Delta t=t_{n+1}-t_{n}$\n\\end_inset\n\n.\n The function \n\\begin_inset Formula $\\theta\\left(t\\right)$\n\\end_inset\n\n may be represented at each time point as \n\\begin_inset Formula $\\theta_{n}=\\theta\\left(t_{n}\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\theta_{n+1}=\\theta\\left(t_{n+1}\\right)$\n\\end_inset\n\n.\n The Newmark integration formulas are used to evaluate \n\\begin_inset Formula $\\theta_{n+1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\dot{\\theta}_{n+1}$\n\\end_inset\n\n at time \n\\begin_inset Formula $\\theta_{n+1}$\n\\end_inset\n\n,\n assuming that they can be integrated from a judiciously selected \n\\begin_inset Formula $\\ddot{\\theta}\\left(t_{n+\\gamma}\\right)$\n\\end_inset\n\n in the time interval \n\\begin_inset Formula $\\left[t_{n},t_{n+1}\\right]$\n\\end_inset\n\n.\n Using the mean value theorem for definite integrals,\n we know that an exact solution may be found for the integral according to\n\\begin_inset Formula \n\\begin{equation}\n\\int_{t_{n}}^{t_{n+1}}\\ddot{\\theta}\\left(t\\right)\\,dt=\\dot{\\theta}_{n+1}-\\dot{\\theta}_{n}\\equiv\\ddot{\\theta}\\left(t_{n+\\gamma}\\right)\\Delta t\\,,\\label{eq:Newmark-mean-value-theorem}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is generally unknown \n\\emph on\na priori\n\\emph default\n.\n In the Newmark integration scheme we let\n\\begin_inset Formula \n\\begin{equation}\n\\ddot{\\theta}\\left(t_{n+\\gamma}\\right)=\\gamma\\ddot{\\theta}_{n+1}+\\left(1-\\gamma\\right)\\ddot{\\theta}_{n}\\,,\\label{eq:Newmark-gamma-deriv}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is a user-selected parameter in the range \n\\begin_inset Formula $0$\n\\end_inset\n\n to \n\\begin_inset Formula $1$\n\\end_inset\n\n.\n It follows that\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\theta}_{n+1}=\\dot{\\theta}_{n}+\\Delta t\\left[\\gamma\\ddot{\\theta}_{n+1}+\\left(1-\\gamma\\right)\\ddot{\\theta}_{n}\\right]\\,.\\label{eq:Newmark-velocity}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWe can similarly integrate this function twice to obtain \n\\begin_inset Formula $\\theta_{n+1}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\n\\int_{t_{n}}^{t_{n+1}}\\int_{t_{n}}^{t}\\ddot{\\theta}\\left(\\tau\\right)\\,d\\tau\\,dt=\\int_{t_{n}}^{t_{n+1}}\\left(\\dot{\\theta}\\left(t\\right)-\\dot{\\theta}_{n}\\right)\\,dt=\\theta_{n+1}-\\theta_{n}-\\dot{\\theta}_{n}\\Delta t\\equiv\\ddot{\\theta}\\left(t_{n+2\\beta}\\right)\\frac{\\Delta t^{2}}{2}\n\\]\n\n\\end_inset\n\nwhere we let\n\\begin_inset Formula \n\\[\n\\ddot{\\theta}\\left(t_{n+2\\beta}\\right)=2\\beta\\ddot{\\theta}_{n+1}+\\left(1-2\\beta\\right)\\ddot{\\theta}_{n}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n represents a parameter that varies from \n\\begin_inset Formula $0$\n\\end_inset\n\n to \n\\begin_inset Formula $\\frac{1}{2}$\n\\end_inset\n\n.\n It follows that\n\\begin_inset Formula \n\\begin{equation}\n\\theta_{n+1}=\\theta_{n}+\\dot{\\theta}_{n}\\Delta t+\\frac{\\Delta t^{2}}{2}\\left[2\\beta\\ddot{\\theta}_{n+1}+\\left(1-2\\beta\\right)\\ddot{\\theta}_{n}\\right]\\,,\\label{eq:Newmark-displacement}\n\\end{equation}\n\n\\end_inset\n\nor alternatively,\n\\begin_inset Formula \n\\begin{equation}\n\\ddot{\\theta}_{n+1}=\\frac{1}{\\beta\\Delta t^{2}}\\left(\\theta_{n+1}-\\theta_{n}-\\dot{\\theta}_{n}\\Delta t\\right)-\\left(\\frac{1}{2\\beta}-1\\right)\\ddot{\\theta}_{n}\\,,\\label{eq:Newmark-acceleration}\n\\end{equation}\n\n\\end_inset\n\nfrom which we may re-evaluate \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Newmark-velocity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\theta}_{n+1}=\\dot{\\theta}_{n}+\\Delta t\\left[\\frac{\\gamma}{\\beta\\Delta t^{2}}\\left(\\theta_{n+1}-\\theta_{n}-\\dot{\\theta}_{n}\\Delta t\\right)+\\left(1-\\frac{\\gamma}{2\\beta}\\right)\\ddot{\\theta}_{n}\\right]\\,.\\label{eq:Newmark-velocity-redux}\n\\end{equation}\n\n\\end_inset\n\nStability of this integration scheme is guaranteed when \n\\begin_inset Formula \n\\begin{equation}\n\\gamma\\ge\\frac{1}{2}\\,,\\quad\\beta\\ge\\frac{\\left(\\gamma+\\frac{1}{2}\\right)^{2}}{4}\\,.\\label{eq:Newmark-stability}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nElastodynamics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Elastodynamics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGoverning Equations\n\\end_layout\n\n\\begin_layout Standard\nThe linear momentum balance for elastodynamics is \n\\begin_inset Formula \n\\begin{equation}\n\\rho\\mathbf{a}=\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}\\,,\\label{eq:edy-solid-momentum}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the density,\n \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is the acceleration,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is the Cauchy stress,\n and \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is the body force per mass.\n The angular momentum balance is satisfied by letting \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{T}=\\boldsymbol{\\sigma}$\n\\end_inset\n\n.\n The integrated form of the mass balance equations yields\n\\begin_inset Formula \n\\begin{equation}\n\\rho=\\frac{\\rho_{r}}{J}\\,,\\label{eq:edy-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is the density in the reference configuration and \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient.\n The acceleration is given by the material time derivative of the velocity \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n,\n evaluated either in a spatial or a material frame,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}=\\dot{\\mathbf{v}}\\label{eq:edy-acceleration}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nVirtual Work\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work for the domain \n\\begin_inset Formula $b$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\int_{b}\\delta\\mathbf{v}\\cdot\\left(\\divg\\boldsymbol{\\sigma}+\\rho\\left(\\mathbf{b}-\\mathbf{a}\\right)\\right)\\,dv\\,,\\label{eq:edy-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n is the virtual velocity.\n Using the divergence theorem,\n this virtual work may be expressed as the difference \n\\begin_inset Formula $\\delta W=\\delta W_{ext}-\\delta W_{int}$\n\\end_inset\n\n between external virtual work \n\\begin_inset Formula $\\delta W_{ext}$\n\\end_inset\n\n and internal virtual work \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta W_{int} & =\\int_{b}\\boldsymbol{\\sigma}:\\grad\\delta\\mathbf{v}\\,dv+\\int_{b}\\delta\\mathbf{v}\\cdot\\rho\\mathbf{a}\\,dv\\,,\\\\\n\\delta W_{ext} & =\\int_{\\partial b}\\delta\\mathbf{v}\\cdot\\mathbf{t}\\,da+\\int_{b}\\delta\\mathbf{v}\\cdot\\rho\\mathbf{b}\\,dv\\,,\n\\end{aligned}\n\\label{eq:edy-int-ext-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the traction on the boundary \n\\begin_inset Formula $\\partial b$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nGeneralized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nMethod for Elastodynamics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Generalized-alpha-elastodynamics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the generalized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nmethod,\n we evaluate displacements and velocities at the intermediate time \n\\begin_inset Formula $t_{n+\\alpha_{f}}=t_{n}+\\alpha_{f}\\left(t_{n+1}-t_{n}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\alpha_{f}$\n\\end_inset\n\n is a user-defined parameter (\n\\begin_inset Formula $0<\\alpha_{f}\\le1$\n\\end_inset\n\n),\n such that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\chi}_{n+\\alpha_{f}} & =\\left(1-\\alpha_{f}\\right)\\boldsymbol{\\chi}_{n}+\\alpha_{f}\\boldsymbol{\\chi}_{n+1}\\,,\\\\\n\\mathbf{u}_{n+\\alpha_{f}} & =\\left(1-\\alpha_{f}\\right)\\mathbf{u}_{n}+\\alpha_{f}\\mathbf{u}_{n+1}\\,,\\\\\n\\mathbf{v}_{n+\\alpha_{f}} & =\\left(1-\\alpha_{f}\\right)\\mathbf{v}_{n}+\\alpha_{f}\\mathbf{v}_{n+1}\\,,\n\\end{aligned}\n\\label{eq:edy-motion-interpolation}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\chi}$\n\\end_inset\n\n is the motion and \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n is the displacement.\n In particular,\n it follows that the deformation gradient and its determinant are given at the intermediate time by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}_{n+\\alpha_{f}}=\\frac{\\partial\\boldsymbol{\\chi}_{n+\\alpha_{f}}}{\\partial\\mathbf{X}}=\\left(1-\\alpha_{f}\\right)\\mathbf{F}_{n}+\\alpha_{f}\\mathbf{F}_{n+1}\\,,\\label{eq:edy-mp-defgrad}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nJ_{n+\\alpha_{f}}=\\det\\mathbf{F}_{n+\\alpha_{f}}\\,.\\label{eq:edy-mp-detJ}\n\\end{equation}\n\n\\end_inset\n\nThe material time derivative of \n\\begin_inset Formula $J_{n+\\alpha_{f}}$\n\\end_inset\n\n,\n and the velocity gradient \n\\begin_inset Formula $\\mathbf{L}_{n+\\alpha_{f}}$\n\\end_inset\n\n are normally evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}_{n+\\alpha_{f}}=J_{n+\\alpha_{f}}\\mathbf{F}_{n+\\alpha_{f}}^{-T}:\\Grad\\mathbf{v}_{n+\\alpha_{f}}\\,,\\label{eq:edy-Jdot-exact}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{L}_{n+\\alpha_{f}}=\\Grad\\mathbf{v}_{n+\\alpha_{f}}\\cdot\\mathbf{F}_{n+\\alpha_{f}}^{-1}\\,.\\label{eq:edy-L-exact}\n\\end{equation}\n\n\\end_inset\n\nIn practice however,\n we get better numerical results when using\n\\begin_inset Formula \n\\begin{equation}\n\\dot{J}_{n+\\alpha_{f}}=\\frac{J_{n+1}-J_{n}}{\\Delta t}\\,,\\label{eq:edy-Jdot-approx}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{L}_{n+\\alpha_{f}}=\\frac{\\mathbf{F}_{n+1}-\\mathbf{F}_{n}}{\\Delta t}\\cdot\\mathbf{F}_{n+\\alpha_{f}}^{-1}\\,.\\label{eq:edy-L-approx}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAccording to the generalized\n\\begin_inset Formula $-\\alpha$\n\\end_inset\n\n method,\n we evaluate the velocity derivative at a different intermediate time \n\\begin_inset Formula $t_{n+\\alpha_{m}}=t_{n}+\\alpha_{m}\\left(t_{n+1}-t_{n}\\right)$\n\\end_inset\n\n,\n such that\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{v}}_{n+\\alpha_{m}}=\\left(1-\\alpha_{m}\\right)\\dot{\\mathbf{v}}_{n}+\\alpha_{m}\\dot{\\mathbf{v}}_{n+1}\\,.\\label{eq:edy-vdot-interpolation}\n\\end{equation}\n\n\\end_inset\n\nSince elastodynamics represent a second-order system of equations in time,\n the parameters \n\\begin_inset Formula $\\alpha_{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{m}$\n\\end_inset\n\n are evaluated from a single parameter \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n using \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bazilevs08\"\nliteral \"true\"\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\alpha_{f}=\\frac{1}{1+\\rho_{\\infty}}\\,,\\quad\\alpha_{m}=\\frac{2-\\rho_{\\infty}}{1+\\rho_{\\infty}}\\,,\\label{eq:ga-alphas-2-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $0\\le\\rho_{\\infty}\\le1$\n\\end_inset\n\n.\n This parameter is the spectral radius for an infinite time step,\n which controls the amount of damping of high frequencies;\n a value of zero produces the greatest amount of damping,\n anihilating the highest frequency in one step,\n whereas a value of one preserves the highest frequency.\n\\end_layout\n\n\\begin_layout Standard\nTo complete the integration scheme \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n,\n we evaluate\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\beta & =\\frac{1}{4}\\left(1+\\alpha_{m}-\\alpha_{f}\\right)^{2}\\\\\n\\gamma & =\\frac{1}{2}+\\alpha_{m}-\\alpha_{f}\n\\end{aligned}\n\\,,\\label{eq:edy-Newmark}\n\\end{equation}\n\n\\end_inset\n\nthen we use the Newmark integration formulas (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Newmark-Integration\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{v}_{n+1} & =\\mathbf{v}_{n}+\\Delta t\\left[\\left(1-\\gamma\\right)\\dot{\\mathbf{v}}_{n}+\\gamma\\dot{\\mathbf{v}}_{n+1}\\right]\\\\\n\\mathbf{u}_{n+1} & =\\mathbf{u}_{n}+\\Delta t\\mathbf{v}_{n}+\\frac{\\Delta t^{2}}{2}\\left[\\left(1-2\\beta\\right)\\dot{\\mathbf{v}}_{n}+2\\beta\\dot{\\mathbf{v}}_{n+1}\\right]\\\\\n\\dot{\\mathbf{v}}_{n+1} & =\\frac{1}{\\beta\\Delta t}\\left(\\frac{\\mathbf{u}_{n+1}-\\mathbf{u}_{n}}{\\Delta t}-\\mathbf{v}_{n}\\right)+\\left(1-\\frac{1}{2\\beta}\\right)\\dot{\\mathbf{v}}_{n}\n\\end{aligned}\n\\,.\\label{eq:edy-current-time}\n\\end{equation}\n\n\\end_inset\n\nAt the start of each time step,\n we initialize the variables as follows:\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{u}_{n+1} & =\\mathbf{u}_{n}\\\\\n\\dot{\\mathbf{v}}_{n+1} & =\\left(1-\\frac{1}{2\\beta}\\right)\\dot{\\mathbf{v}}_{n}-\\frac{1}{\\beta\\Delta t}\\mathbf{v}_{n}\\\\\n\\mathbf{v}_{n+1} & =\\left(1-\\frac{\\gamma}{\\beta}\\right)\\mathbf{v}_{n}+\\Delta t\\left(1-\\frac{\\gamma}{2\\beta}\\right)\\dot{\\mathbf{v}}_{n}\n\\end{aligned}\n\\,.\\label{eq:edy-initializations}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization\n\\end_layout\n\n\\begin_layout Standard\nThe solution of the nonlinear equation \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n is obtained by linearizing this relation as\n\\begin_inset Formula \n\\begin{equation}\n\\delta W+D\\delta W\\left[\\Delta\\mathbf{u}\\right]\\approx0\\,,\\label{eq:edy-linearized-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere the operator \n\\begin_inset Formula $D\\delta W\\left[\\cdot\\right]$\n\\end_inset\n\n represents the directional derivative of \n\\begin_inset Formula $\\delta W$\n\\end_inset\n\n at \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{u}_{n+1}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n According to the generalized\n\\begin_inset Formula $-\\alpha$\n\\end_inset\n\n method \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Jansen00\"\nliteral \"true\"\n\n\\end_inset\n\n,\n the virtual work is evaluated using intermediate time step values,\n at \n\\begin_inset Formula $t_{n+\\alpha_{f}}$\n\\end_inset\n\n for all parameters except \n\\begin_inset Formula $\\dot{\\mathbf{v}}$\n\\end_inset\n\n,\n which is evaluated at \n\\begin_inset Formula $t_{n+\\alpha_{m}}$\n\\end_inset\n\n.\n It follows from these definitions that the linearizations of critical variables are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{u}\\left[\\Delta\\mathbf{u}\\right] & =\\alpha_{f}\\Delta\\mathbf{u}\\\\\nD\\mathbf{F}\\left[\\Delta\\mathbf{u}\\right] & =\\alpha_{f}\\Grad\\Delta\\mathbf{u}\\\\\nDJ\\left[\\Delta\\mathbf{u}\\right] & =\\alpha_{f}J\\left(\\divg\\Delta\\mathbf{u}\\right)\\\\\nD\\dot{J}\\left[\\Delta\\mathbf{u}\\right] & =D\\left(J\\mathbf{F}^{-T}:\\Grad\\mathbf{v}\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\alpha_{f}J\\left[\\left(\\divg\\mathbf{v}+\\frac{\\gamma}{\\beta\\Delta t}\\right)\\left(\\divg\\Delta\\mathbf{u}\\right)-\\left(\\grad\\Delta\\mathbf{u}\\right)^{T}:\\mathbf{L}\\right]\\\\\nD\\mathbf{v}\\left[\\Delta\\mathbf{u}\\right] & =\\frac{\\alpha_{f}\\gamma}{\\beta\\Delta t}\\Delta\\mathbf{u}\\\\\nD\\dot{\\mathbf{v}}\\left[\\Delta\\mathbf{u}\\right] & =\\frac{\\alpha_{m}}{\\beta\\Delta t^{2}}\\Delta\\mathbf{u}\n\\end{aligned}\n\\,.\\label{eq:edy-linearizations}\n\\end{equation}\n\n\\end_inset\n\nTo linearize the virtual work,\n we need to express the integrals appearing in \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\delta W_{ext}$\n\\end_inset\n\n over the material frame of the finite element solid domain.\n\\end_layout\n\n\\begin_layout Subsubsection\nInternal Work\n\\end_layout\n\n\\begin_layout Standard\nThe first term in the internal work becomes\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\int_{b}\\boldsymbol{\\sigma}:\\grad\\delta\\mathbf{v}\\,dv & =\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}:\\Grad\\delta\\mathbf{v}\\,dV\\end{aligned}\n\\,,\\label{eq:work-material-1-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{S}=J\\cdot\\mathbf{F}^{-1}\\cdot\\boldsymbol{\\sigma}\\cdot\\mathbf{F}^{-T}$\n\\end_inset\n\n is the second Piola-Kirchhoff stress for the solid material.\n In general,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n (and thus,\n \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n) is only a function of the solid strain,\n such as the right Cauchy-Green tensor \n\\begin_inset Formula $\\mathbf{C}=\\mathbf{F}^{T}\\cdot\\mathbf{F}$\n\\end_inset\n\n or the Green-Lagrange strain \n\\begin_inset Formula $\\mathbf{E}=\\left(\\mathbf{C}-\\mathbf{I}\\right)/2$\n\\end_inset\n\n.\n \n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{E}\\left[\\Delta\\mathbf{u}\\right]=\\frac{\\alpha_{f}}{2}\\left(\\Grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{F}+\\mathbf{F}^{T}\\cdot\\Grad\\Delta\\mathbf{u}\\right)\\,.\\label{eq:edy-E-linearization}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n following the standard approach in solid mechanics,\n the linearization of \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{S}\\left[\\Delta\\mathbf{u}\\right] & =\\frac{\\partial\\mathbf{S}}{\\partial\\mathbf{E}}:D\\mathbf{E}\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\alpha_{f}\\boldsymbol{\\mathbb{C}}:\\frac{1}{2}\\left(\\Grad^{T}\\Delta\\mathbf{u}\\cdot\\mathbf{F}+\\mathbf{F}^{T}\\cdot\\Grad\\Delta\\mathbf{u}\\right)\\\\\n & =\\alpha_{f}\\boldsymbol{\\mathbb{C}}:\\left(\\mathbf{F}^{T}\\oslash\\mathbf{F}^{T}\\right):\\Delta\\boldsymbol{\\varepsilon}\n\\end{aligned}\n\\,,\\label{eq:edy-solid-stress-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathbb{C}}$\n\\end_inset\n\n is the material elasticity tensor.\n Now,\n the linearization of the first term in \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned} & D\\left(\\int_{B}\\mathbf{F}\\cdot\\mathbf{S}:\\Grad\\delta\\mathbf{v}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{v}\\alpha_{f}\\left(\\grad\\delta\\mathbf{v}:\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}+\\grad\\delta\\mathbf{v}:\\boldsymbol{\\mathcal{C}}:\\grad\\Delta\\mathbf{u}\\right)\\,dv\n\\end{aligned}\n}\\,.\\label{eq:edy-Dint-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n is the spatial elasticity tensor.\n Similarly,\n the second term in \n\\begin_inset Formula $\\delta W_{int}$\n\\end_inset\n\n produces\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{D\\left(\\int_{B}\\delta\\mathbf{v}\\cdot\\rho_{r}\\mathbf{a}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int_{b}\\delta\\mathbf{v}\\cdot\\frac{\\alpha_{m}}{\\beta\\Delta t^{2}}\\rho\\Delta\\mathbf{u}\\,dv}\\,.\\label{eq:edy-Dint-2}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nExternal Work\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the body force term is\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\int_{B}\\delta\\mathbf{v}\\cdot\\rho_{r}\\mathbf{b}\\,dV\\right)\\left[\\Delta\\mathbf{u}\\right]=\\int_{b}\\delta\\mathbf{v}\\cdot\\alpha_{f}\\rho\\grad\\mathbf{b}\\cdot\\Delta\\mathbf{u}\\,dv\\,.\\label{eq:edy-Dext}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of the traction force term is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\left(\\int_{\\Gamma_{\\eta}}\\delta\\mathbf{v}\\cdot\\mathbf{t}\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\,d\\eta^{1}d\\eta^{2}\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\int_{\\Gamma_{\\eta}}\\alpha_{f}\\delta\\mathbf{v}\\cdot\\left(\\mathbf{t}\\otimes\\mathbf{n}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\\,d\\eta^{1}d\\eta^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}}\\delta\\mathbf{v}\\cdot D\\mathbf{t}\\left[\\Delta\\mathbf{u}\\right]\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\,d\\eta^{1}d\\eta^{2}\n\\end{aligned}\n\\,.\\label{eq:edy-Dext-final}\n\\end{equation}\n\n\\end_inset\n\nNote that \n\\begin_inset Formula $D\\mathbf{t}\\left[\\Delta\\mathbf{u}\\right]$\n\\end_inset\n\n depends on the nature of the surface traction.\n For a prescribed traction we have \n\\begin_inset Formula $D\\mathbf{t}\\left[\\Delta\\mathbf{u}\\right]=\\mathbf{0}$\n\\end_inset\n\n.\n A contact analysis needs more elaborate derivations (not yet implemented as of FEBio 2.7).\n In the above expression we used\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|\\left[\\Delta\\mathbf{u}\\right] & =\\mathbf{n}\\cdot D\\left(\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right)\\left[\\Delta\\mathbf{u}\\right]\\\\\n & =\\alpha_{f}\\mathbf{n}\\cdot\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\n\\end{aligned}\n\\label{eq:edy-Dext-DA}\n\\end{equation}\n\n\\end_inset\n\nwhere the unit outward normal is evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{g}_{1}\\times\\mathbf{g}_{2}}{\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,.\\label{eq:edy-unit-normal}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\end_layout\n\n\\begin_layout Standard\nWe use the following interpolations:\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v} & =\\sum_{a}N_{a}\\delta\\mathbf{v}_{a} & \\Delta\\mathbf{u} & =\\sum_{b}N_{b}\\Delta\\mathbf{u}_{b}\\\\\n\\grad\\delta\\mathbf{v} & =\\sum_{a}\\delta\\mathbf{v}_{a}\\otimes\\grad N_{a} & \\grad\\Delta\\mathbf{u} & =\\sum_{b}\\Delta\\mathbf{u}_{b}\\otimes\\grad N_{b}\\\\\n\\divg\\delta\\mathbf{v} & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\grad N_{a} & \\divg\\Delta\\mathbf{u} & =\\sum_{b}\\Delta\\mathbf{u}_{b}\\cdot\\grad N_{b}\n\\end{aligned}\n\\,,\\label{eq:interpolations-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}\\left(\\eta^{1},\\eta^{2},\\eta^{3}\\right)$\n\\end_inset\n\n are shape functions of the element parametric coordinates \n\\begin_inset Formula $\\left(\\eta^{1},\\eta^{2},\\eta^{3}\\right)$\n\\end_inset\n\n.\n Note that the \n\\begin_inset Formula $\\grad\\equiv\\frac{\\partial}{\\partial\\mathbf{x}}$\n\\end_inset\n\n operator should be evaluated at \n\\begin_inset Formula $t_{n+\\alpha_{f}}$\n\\end_inset\n\n,\n using \n\\begin_inset Formula $\\mathbf{x}_{n+\\alpha_{f}}$\n\\end_inset\n\n.\n For example,\n in the case of a scalar function \n\\begin_inset Formula $f$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\grad f & =\\frac{\\partial f}{\\partial\\mathbf{x}_{n+\\alpha_{f}}}=\\frac{\\partial f}{\\partial\\eta^{i}}\\mathbf{g}_{n+\\alpha_{f}}^{i}\\\\\n\\mathbf{g}_{n+\\alpha_{f}}^{i} & =\\frac{\\partial\\eta^{i}}{\\partial\\mathbf{x}_{n+\\alpha_{f}}}\n\\end{aligned}\n\\,,\n\\]\n\n\\end_inset\n\nwhere the contravariant basis vectors \n\\begin_inset Formula $\\mathbf{g}_{n+\\alpha_{f}}^{i}$\n\\end_inset\n\n may be evaluated from the covariant basis vectors\n\\begin_inset Formula \n\\[\n\\mathbf{g}_{i}^{n+\\alpha_{f}}=\\frac{\\partial\\mathbf{x}_{n+\\alpha_{f}}}{\\partial\\eta^{i}}=\\left(1-\\alpha_{f}\\right)\\frac{\\partial\\mathbf{x}_{n}}{\\partial\\eta^{i}}+\\alpha_{f}\\frac{\\partial\\mathbf{x}_{n+1}}{\\partial\\eta^{i}}\n\\]\n\n\\end_inset\n\nusing \n\\begin_inset Formula $\\mathbf{g}_{i}^{n+\\alpha_{f}}\\cdot\\mathbf{g}_{n+\\alpha_{f}}^{j}=\\delta_{i}^{j}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe discretization of the internal work produces\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{int}=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\int_{b}\\left(\\mathbf{f}_{a}^{u}+\\mathbf{f}_{a}^{\\rho}\\right)\\,dv\\,,\\label{eq:edy-discretized-internal-work}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{f}_{a}^{u} & =\\boldsymbol{\\sigma}\\cdot\\grad N_{a}\\\\\n\\mathbf{f}_{a}^{\\rho} & =N_{a}\\rho\\mathbf{a}\n\\end{aligned}\n}\\,.\\label{eq:edy-discretized-residuals}\n\\end{equation}\n\n\\end_inset\n\nThe discretization of the stress and elasticity terms in the internal work is\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{v}\\alpha_{f}\\left(\\grad\\delta\\mathbf{v}:\\grad\\Delta\\mathbf{u}\\cdot\\boldsymbol{\\sigma}+\\grad\\delta\\mathbf{v}:\\boldsymbol{\\mathcal{C}}:\\grad\\Delta\\mathbf{u}\\right)\\,dv\\\\\n & =\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\int_{v}\\mathbf{K}_{ab}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\,,\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}=\\alpha_{f}\\left(\\left(\\grad N_{a}\\cdot\\boldsymbol{\\sigma}\\cdot\\grad N_{b}\\right)\\mathbf{I}+\\grad N_{a}\\cdot\\boldsymbol{\\mathcal{C}}\\cdot\\grad N_{b}\\right)}\\,.\\label{eq:edy-Wint-Kab}\n\\end{equation}\n\n\\end_inset\n\nThe discretization of the mass term in the internal work is\n\\begin_inset Formula \n\\[\n\\int_{b}\\delta\\mathbf{v}\\cdot\\frac{\\alpha_{m}}{\\beta\\Delta t^{2}}\\rho\\Delta\\mathbf{u}\\,dv=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\sum_{b}\\int_{b}\\mathbf{M}_{ab}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\\,,\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{M}_{ab}=\\frac{\\alpha_{m}}{\\beta\\Delta t^{2}}\\rho N_{a}N_{b}\\mathbf{I}}\\,.\\label{eq:edy-Wint-Mab}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the external work of body forces,\n\\begin_inset Formula \n\\[\n\\int_{b}\\delta\\mathbf{v}\\cdot\\rho\\mathbf{b}\\,dv=\\sum_{a}\\delta\\mathbf{v}_{a}\\cdot\\int_{b}\\mathbf{f}_{a}^{\\mathbf{b}}\\,dv\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{f}_{a}^{\\mathbf{b}}=N_{a}\\rho^{s}\\mathbf{b}}\\,,\\label{eq:edy-Wext-fb}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\int_{b}\\delta\\mathbf{v}^{s}\\cdot\\alpha_{f}\\rho^{s}\\grad\\mathbf{b}\\cdot\\Delta\\mathbf{u}\\,dv=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\sum_{b}\\int_{b}\\mathbf{K}_{ab}^{\\mathbf{b}}\\,dv\\cdot\\Delta\\mathbf{u}_{b}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{\\mathbf{b}}=\\alpha_{f}N_{a}N_{b}\\rho^{s}\\grad\\mathbf{b}}\\,.\\label{eq:edy-Wext-Kb}\n\\end{equation}\n\n\\end_inset\n\nFor prescribed tractions,\n\\begin_inset Formula \n\\[\n\\int_{\\partial b}\\delta\\mathbf{v}^{s}\\cdot\\mathbf{t}^{s}\\,da=\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\int_{\\Gamma_{\\eta}}\\mathbf{f}_{a}^{t}\\,d\\eta^{1}d\\eta^{2}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{f}_{a}^{t}=N_{a}\\mathbf{t}^{s}\\,\\left|\\mathbf{g}_{1}\\times\\mathbf{g}_{2}\\right|}\\,,\\label{eq:edy-Wext-ft}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\int_{\\Gamma_{\\eta}}\\alpha_{f}\\delta\\mathbf{v}^{s}\\cdot\\left(\\mathbf{t}^{s}\\otimes\\mathbf{n}\\right)\\cdot\\left(\\mathbf{g}_{1}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{2}}-\\mathbf{g}_{2}\\times\\frac{\\partial\\Delta\\mathbf{u}}{\\partial\\eta^{1}}\\right)\\,d\\eta^{1}d\\eta^{2}\\\\\n & =\\sum_{a}\\delta\\mathbf{v}_{a}^{s}\\cdot\\sum_{b}\\int_{\\Gamma_{\\eta}}\\mathbf{K}_{ab}^{t}\\,d\\eta^{1}d\\eta^{2}\\cdot\\Delta\\mathbf{u}_{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{ab}^{t}=\\alpha_{f}N_{a}\\left(\\mathbf{t}^{s}\\otimes\\mathbf{n}\\right)\\cdot\\left(\\frac{\\partial N_{b}}{\\partial\\eta^{2}}\\hat{\\mathbf{g}}_{1}-\\frac{\\partial N_{b}}{\\partial\\eta^{1}}\\hat{\\mathbf{g}}_{2}\\right)}\\,,\\label{eq:edy-Wext-Kt}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\mathbf{g}}$\n\\end_inset\n\n is the skew-symmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{g}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nEnergy-Momentum Conservation Scheme\n\\end_layout\n\n\\begin_layout Standard\nThe time discretization scheme may be selected in a manner that enforces linear and angular momentum,\n and energy conservation over consecutive time steps \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n,\n when boundary conditions and external loads are time-independent.\n Based on the prior literature \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo92a,Gonzalez00,Puso02\"\nliteral \"true\"\n\n\\end_inset\n\n,\n this momentum and energy conservation may be achieved by using the midpoint rule (\n\\begin_inset Formula $\\rho_{\\infty}=1$\n\\end_inset\n\n,\n leading to \n\\begin_inset Formula $\\alpha_{f}=\\alpha_{m}=\\frac{1}{2}$\n\\end_inset\n\n),\n and evaluating the virtual work at \n\\begin_inset Formula $t_{n+\\frac{1}{2}}$\n\\end_inset\n\n.\n However,\n since the virtual work strictly enforces momentum balance only,\n there is no guarantee that energy conservation will be satisfied as a result of time discretization.\n Therefore,\n we need to enforce a specific scheme to satisfy energy balance.\n\\end_layout\n\n\\begin_layout Subsubsection\nEnergy Balance\n\\end_layout\n\n\\begin_layout Standard\nFor an elastic solid,\n in the absence of heat exchanges (i.e.,\n in elastodynamics),\n the equation of energy balance reduces\n\\begin_inset Formula \n\\begin{equation}\n\\rho\\dot{\\varepsilon}=\\boldsymbol{\\sigma}:\\mathbf{D}\\,,\\label{eq:edy-energy-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the specific internal energy and \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n is the rate of deformation tensor.\n Recall that \n\\begin_inset Formula $\\varepsilon=\\psi+\\theta\\eta$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n is the specific free energy,\n \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n is the absolute temperature and \n\\begin_inset Formula $\\eta$\n\\end_inset\n\n is the specific entropy.\n Since \n\\begin_inset Formula $\\eta=0$\n\\end_inset\n\n in elasticity (due to the temperature remaining constant),\n the above energy balance may be combined with the mass balance \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:edy-mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\rho\\dot{\\psi}=\\frac{\\rho_{r}}{J}\\dot{\\psi}=\\boldsymbol{\\sigma}:\\mathbf{D}\\,,\\label{eq:edy-energy-r1}\n\\end{equation}\n\n\\end_inset\n\nor\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\Psi}_{r}=J\\boldsymbol{\\sigma}:\\mathbf{D}\\,,\\label{eq:edy-energy-r2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Psi_{r}=\\rho_{r}\\psi$\n\\end_inset\n\n is the free energy density (per volume of the material in the reference configuration).\n\\end_layout\n\n\\begin_layout Standard\nIn our time integration scheme,\n to satisfy energy balance,\n this equation needs to be evaluated at \n\\begin_inset Formula $t_{n+\\alpha_{f}}$\n\\end_inset\n\n,\n thus\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\dot{\\Psi}_{r}\\right)_{n+\\alpha_{f}}=J_{n+\\alpha_{f}}\\boldsymbol{\\sigma}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}\\,.\\label{eq:edy-mp-energy}\n\\end{equation}\n\n\\end_inset\n\nHowever,\n the solution for \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{n+\\alpha_{f}}\\equiv\\boldsymbol{\\sigma}\\left(\\mathbf{F}_{n+\\alpha_{f}}\\right)$\n\\end_inset\n\n obtained from the momentum balance may not necessarity satisfy this equation.\n Thus,\n to satisfy energy balance over consecutive time steps,\n we want to evaluate an effective stress \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{n+\\alpha_{f}}$\n\\end_inset\n\n such that\n\\begin_inset Formula \n\\begin{equation}\nJ_{n+\\alpha_{f}}\\tilde{\\boldsymbol{\\sigma}}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}=\\frac{\\left(\\Psi_{r}\\right)_{n+1}-\\left(\\Psi_{r}\\right)_{n}}{\\Delta t}\\,.\\label{eq:edy-energy-discretized}\n\\end{equation}\n\n\\end_inset\n\nTo find a solution for \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{n+\\alpha_{f}}$\n\\end_inset\n\n,\n we follow the procedure of Gonzalez \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gonzalez00\"\nliteral \"true\"\n\n\\end_inset\n\n and let\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\boldsymbol{\\sigma}}_{n+\\alpha_{f}}=\\boldsymbol{\\sigma}_{n+\\alpha_{f}}+f\\mathbf{D}_{n+\\alpha_{f}}\\,,\\label{eq:edy-eff-stress-model}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $f$\n\\end_inset\n\n is some scalar function to be determined.\n Substituting this relation,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:edy-eff-stress-model\"\nnolink \"false\"\n\n\\end_inset\n\n,\n into the previous equation,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:edy-energy-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n,\n produces\n\\begin_inset Formula \n\\begin{equation}\nf=\\left(\\frac{\\left(\\Psi_{r}\\right)_{n+1}-\\left(\\Psi_{r}\\right)_{n}}{J_{n+\\alpha_{f}}^{s}\\Delta t}-\\boldsymbol{\\sigma}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}\\right)\\frac{1}{\\mathbf{D}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}}\\,.\\label{eq:edy-eff-stress-f}\n\\end{equation}\n\n\\end_inset\n\nHence,\n the equation for an effective stress needed to satisfy energy balance between consecutive time steps is\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\tilde{\\boldsymbol{\\sigma}}_{n+\\alpha_{f}}=\\boldsymbol{\\sigma}_{n+\\alpha_{f}}+\\left(\\frac{\\left(\\Psi_{r}\\right)_{n+1}-\\left(\\Psi_{r}\\right)_{n}}{J_{n+\\alpha_{f}}\\Delta t}-\\boldsymbol{\\sigma}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}\\right)\\frac{\\mathbf{D}_{n+\\alpha_{f}}}{\\mathbf{D}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}}}\\,.\\label{eq:edy-eff-stress-final}\n\\end{equation}\n\n\\end_inset\n\nIn the limit when \n\\begin_inset Formula $\\mathbf{D}_{n+\\alpha_{f}}:\\mathbf{D}_{n+\\alpha_{f}}=0$\n\\end_inset\n\n,\n we use \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{n+\\alpha_{f}}=\\boldsymbol{\\sigma}_{n+\\alpha_{f}}$\n\\end_inset\n\n.\n Recall that this scheme produces conservation of linear and angular momentum and total energy only with \n\\begin_inset Formula $\\rho_{\\infty}=1$\n\\end_inset\n\n,\n or equivalently,\n \n\\begin_inset Formula $\\alpha_{f}=\\alpha_{m}=\\frac{1}{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\beta=\\frac{1}{2}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma=1$\n\\end_inset\n\n.\n Therefore,\n this effective stress calculation is only applied when the user employs \n\\begin_inset Formula $\\rho_{\\infty}=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nRigid Body Dynamics\n\\end_layout\n\n\\begin_layout Subsection\nRigid Body Rotation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Body-Rotation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential Map\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Exponential-Map\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConventionally,\n the rigid body rotation tensor \n\\begin_inset Formula $\\boldsymbol{\\Lambda}$\n\\end_inset\n\n corresponding to a rotation of angle \n\\begin_inset Formula $\\chi$\n\\end_inset\n\n about the unit vector \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n may be expressed in terms of the vector \n\\begin_inset Formula $\\boldsymbol{\\chi}=\\chi\\mathbf{n}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}\\left(\\boldsymbol{\\chi}\\right)=\\cos\\chi\\,\\mathbf{I}-\\sin\\chi\\,\\boldsymbol{\\mathcal{E}}\\cdot\\mathbf{n}+\\left(1-\\cos\\chi\\right)\\mathbf{n}\\otimes\\mathbf{n}\\label{eq:rbr-rotation-tensor}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{E}}$\n\\end_inset\n\n is the third-order permutation pseudo-tensor with Cartesian components \n\\begin_inset Formula $\\varepsilon_{ijk}$\n\\end_inset\n\n.\n Making use of the trigonometric identity,\n\\begin_inset Formula \n\\begin{equation}\n\\cos\\chi=1-2\\sin^{2}\\frac{1}{2}\\chi\\,,\\label{eq:rbr-identity-1}\n\\end{equation}\n\n\\end_inset\n\nthis expression may be rearranged as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}\\left(\\boldsymbol{\\chi}\\right)=\\mathbf{I}-\\frac{\\sin\\chi}{\\chi}\\,\\boldsymbol{\\mathcal{E}}\\cdot\\boldsymbol{\\chi}+\\frac{2}{\\chi^{2}}\\sin^{2}\\frac{1}{2}\\chi\\left(\\boldsymbol{\\mathcal{E}}\\cdot\\boldsymbol{\\chi}\\right)^{2}\\,,\\label{eq:rbr-rotation-tensor-alt-1}\n\\end{equation}\n\n\\end_inset\n\nwhere we have made use of the identity\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\boldsymbol{\\mathcal{E}}\\cdot\\boldsymbol{\\chi}\\right)^{2}=\\boldsymbol{\\chi}\\otimes\\boldsymbol{\\chi}-\\chi^{2}\\mathbf{I}\\,.\\label{eq:rbr-identity-2}\n\\end{equation}\n\n\\end_inset\n\nLetting\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\boldsymbol{\\chi}}=-\\boldsymbol{\\mathcal{E}}\\cdot\\boldsymbol{\\chi}\\label{eq:rbr-antisymmetric-tensor}\n\\end{equation}\n\n\\end_inset\n\nrepresent the antisymmetric tensor with axial vector \n\\begin_inset Formula $\\boldsymbol{\\chi}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(\\boldsymbol{\\chi}\\right)$\n\\end_inset\n\n may now be represented as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}\\left(\\boldsymbol{\\chi}\\right)\\equiv\\exp\\left[\\hat{\\boldsymbol{\\chi}}\\right]=\\mathbf{I}+\\frac{\\sin\\chi}{\\chi}\\hat{\\boldsymbol{\\chi}}+\\frac{2}{\\chi^{2}}\\sin^{2}\\left(\\frac{1}{2}\\chi\\right)\\,\\hat{\\boldsymbol{\\chi}}^{2}\\,,\\label{eq:rbr-rotation-tensor-alt-2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\exp\\left[\\hat{\\boldsymbol{\\chi}}\\right]$\n\\end_inset\n\n is known as the\n\\emph on\n exponential map\n\\emph default\n.\n Thus,\n the exponential map provides the rotation tensor for a rotation \n\\begin_inset Formula $\\chi$\n\\end_inset\n\n about the unit vector \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\cdot\\boldsymbol{\\chi}=\\boldsymbol{\\chi}$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $\\hat{\\boldsymbol{\\chi}}\\cdot\\boldsymbol{\\chi}=\\boldsymbol{\\chi}\\times\\boldsymbol{\\chi}=\\mathbf{0}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nLet \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n be any orthogonal transformation,\n then\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{Q}\\cdot\\exp\\left[\\hat{\\boldsymbol{\\chi}}\\right]\\cdot\\mathbf{Q}^{T} & =\\mathbf{I}+\\frac{\\sin\\chi}{\\chi}\\mathbf{Q}\\cdot\\hat{\\boldsymbol{\\chi}}\\cdot\\mathbf{Q}^{T}+\\frac{2}{\\chi^{2}}\\sin^{2}\\frac{1}{2}\\chi\\left(\\mathbf{Q}\\cdot\\hat{\\boldsymbol{\\chi}}\\cdot\\mathbf{Q}^{T}\\right)^{2}\\\\\n & =\\exp\\left[\\mathbf{Q}\\cdot\\hat{\\boldsymbol{\\chi}}\\cdot\\mathbf{Q}^{T}\\right]\\equiv\\exp\\left[\\hat{\\boldsymbol{\\theta}}\\right]\n\\end{aligned}\n\\label{eq:rbr-orthogonal-transformation}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\boldsymbol{\\theta}}=\\mathbf{Q}\\cdot\\hat{\\boldsymbol{\\chi}}\\cdot\\mathbf{Q}^{T}$\n\\end_inset\n\n and its corresponding axial vector is \n\\begin_inset Formula $\\boldsymbol{\\theta}=\\mathbf{Q}\\cdot\\boldsymbol{\\chi}$\n\\end_inset\n\n,\n implying that \n\\begin_inset Formula $\\theta=\\chi$\n\\end_inset\n\n.\n This property of the exponential map is used in the next derivation.\n\\end_layout\n\n\\begin_layout Standard\nConsider a vector \n\\begin_inset Formula $\\mathbf{Z}$\n\\end_inset\n\n in the reference configuration of a rigid body.\n Upon rigid body rotation,\n this vector is currently at\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{z}\\left(t\\right)=\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\mathbf{Z}\\,.\\label{eq:rbr-rotation-t}\n\\end{equation}\n\n\\end_inset\n\nThe corresponding axial vector of \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(t\\right)$\n\\end_inset\n\n is \n\\begin_inset Formula $\\boldsymbol{\\chi}\\left(t\\right)$\n\\end_inset\n\n.\n At a subsequent time \n\\begin_inset Formula $t^{\\prime}$\n\\end_inset\n\n,\n we would similarly have \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{z}\\left(t^{\\prime}\\right)=\\boldsymbol{\\Lambda}\\left(t^{\\prime}\\right)\\cdot\\mathbf{Z}=\\exp\\left[\\hat{\\boldsymbol{\\theta}}\\right]\\cdot\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\mathbf{Z}\\,,\\label{eq:rbr-orthogonal-rotation-t-prime}\n\\end{equation}\n\n\\end_inset\n\nwhere here,\n \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n is the incremental (finite) rotation from \n\\begin_inset Formula $t$\n\\end_inset\n\n to \n\\begin_inset Formula $t^{\\prime}$\n\\end_inset\n\n.\n Alternatively,\n we may choose to write\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{z}\\left(t^{\\prime}\\right)=\\boldsymbol{\\Lambda}\\left(t^{\\prime}\\right)\\cdot\\mathbf{Z}=\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\exp\\left[\\hat{\\boldsymbol{\\Theta}}\\right]\\cdot\\mathbf{Z}\\,,\\label{eq:rbr-incremental-finite-rotation}\n\\end{equation}\n\n\\end_inset\n\nsuch that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\Lambda}\\left(t^{\\prime}\\right) & =\\exp\\left[\\hat{\\boldsymbol{\\theta}}\\right]\\cdot\\boldsymbol{\\Lambda}\\left(t\\right)\\\\\n & =\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\exp\\left[\\hat{\\boldsymbol{\\Theta}}\\right]\n\\end{aligned}\n\\,,\\label{eq:rbr-material-spatial-increments}\n\\end{equation}\n\n\\end_inset\n\nimplying that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\exp\\left[\\hat{\\boldsymbol{\\theta}}\\right] & =\\exp\\left[\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\hat{\\boldsymbol{\\Theta}}\\cdot\\boldsymbol{\\Lambda}^{T}\\left(t\\right)\\right]\\\\\n\\boldsymbol{\\theta} & =\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\boldsymbol{\\Theta}\n\\end{aligned}\n\\,.\\label{eq:rbr-material-spatial-redux}\n\\end{equation}\n\n\\end_inset\n\nNote from these relations that \n\\begin_inset Formula $\\theta=\\Theta$\n\\end_inset\n\n.\n Thus,\n \n\\begin_inset Formula $\\boldsymbol{\\Theta}$\n\\end_inset\n\n is the material representation of the incremental rotation from \n\\begin_inset Formula $t$\n\\end_inset\n\n to \n\\begin_inset Formula $t^{\\prime}$\n\\end_inset\n\n,\n while \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n is the corresponding spatial representation.\n\\end_layout\n\n\\begin_layout Subsubsection\nCayley Transform\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cayley-Transform\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAn alternative to the exponential map is the Cayley transform,\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}\\left(\\boldsymbol{\\chi}\\right)=\\cay\\left[\\hat{\\boldsymbol{\\chi}}\\right]=\\mathbf{I}+\\frac{2}{1+\\left(\\frac{1}{2}\\chi\\right)^{2}}\\left(\\frac{1}{2}\\hat{\\boldsymbol{\\chi}}+\\frac{1}{4}\\hat{\\boldsymbol{\\chi}}^{2}\\right)\\label{eq:rbr-Cayley-transform}\n\\end{equation}\n\n\\end_inset\n\nwhich is a second order approximation to the exponential map.\n This formula is a correction to that appearing in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso02\"\nliteral \"true\"\n\n\\end_inset\n\n (which has \n\\begin_inset Formula $\\frac{1}{2}\\chi^{2}$\n\\end_inset\n\n in the denominator).\n According to Puso \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso02\"\nliteral \"true\"\n\n\\end_inset\n\n,\n the Cayley transform must be used to enforce conservation of momentum and energy in a midpoint rule discretization scheme,\n whenever the rigid body is connected to a deformable body,\n or whenever two rigid bodies are connected by a joint.\n Comparing the above expression to the exponential map \n\\begin_inset Formula $\\exp\\left[\\hat{\\boldsymbol{\\theta}}\\right]$\n\\end_inset\n\n using \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rbr-rotation-tensor-alt-2\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find that \n\\begin_inset Formula $\\boldsymbol{\\chi}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n are related via\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\chi}=2\\tan\\frac{\\theta}{2}\\,\\mathbf{n}\\,.\\label{eq:exponential-Cayley-relation}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization Along Rotational Increment\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Linearization-Along-Rotational\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n represent a spatial rotational increment,\n such that a rotation tensor compounded by an infinitesimal incremental rotation is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}_{\\varepsilon}=\\cay\\left[\\varepsilon\\hat{\\boldsymbol{\\theta}}\\right]\\cdot\\boldsymbol{\\Lambda}\\,.\\label{eq:rbr-spatial-rotation-increment}\n\\end{equation}\n\n\\end_inset\n\nUsing the Cayley transform for illustration,\n the linearization of \n\\begin_inset Formula $\\boldsymbol{\\Lambda}$\n\\end_inset\n\n along the increment \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n is obtained from\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\boldsymbol{\\Lambda}\\left[\\boldsymbol{\\theta}\\right] & =\\left.\\frac{d}{d\\varepsilon}\\right|_{\\varepsilon=0}\\cay\\left[\\varepsilon\\hat{\\boldsymbol{\\theta}}\\right]\\cdot\\boldsymbol{\\Lambda}\\\\\n & =\\left.\\frac{d}{d\\varepsilon}\\right|_{\\varepsilon=0}\\left(\\mathbf{I}+\\frac{2}{1+\\frac{1}{4}\\varepsilon^{2}\\theta^{2}}\\left(\\frac{1}{2}\\varepsilon\\hat{\\boldsymbol{\\theta}}+\\frac{1}{4}\\varepsilon^{2}\\hat{\\boldsymbol{\\theta}}^{2}\\right)\\right)\\cdot\\boldsymbol{\\Lambda}\\\\\n & =\\hat{\\boldsymbol{\\theta}}\\cdot\\boldsymbol{\\Lambda}\n\\end{aligned}\n\\,.\\label{eq:rbr-spatial-rotation-linearization}\n\\end{equation}\n\n\\end_inset\n\nThe same result may be obtained with the exponential map.\n Similarly,\n using an infinitesimal material rotational increment such that \n\\begin_inset Formula $\\boldsymbol{\\Lambda}_{\\varepsilon}=\\boldsymbol{\\Lambda}\\cdot\\cay\\left[\\varepsilon\\hat{\\boldsymbol{\\Theta}}\\right]$\n\\end_inset\n\n,\n we may find\n\\begin_inset Formula \n\\begin{equation}\nD\\boldsymbol{\\Lambda}\\left[\\boldsymbol{\\Theta}\\right]=\\boldsymbol{\\Lambda}\\cdot\\hat{\\boldsymbol{\\Theta}}\\,.\\label{eq:rbr-material-rotation-increment}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Rigid Body Motion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Rigid-Body\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf the point \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n is connected to a rigid body,\n its motion is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\mathbf{r}\\left(t\\right)+\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\mathbf{Z}=\\mathbf{r}\\left(t\\right)+\\mathbf{z}\\left(t\\right)\\label{eq:rbm-position}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{r}\\left(t\\right)$\n\\end_inset\n\n is the position of the rigid body center of mass and \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(t\\right)$\n\\end_inset\n\n is the body's rotation tensor,\n which satisfies \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(t_{0}\\right)=\\mathbf{I}$\n\\end_inset\n\n at the initial time \n\\begin_inset Formula $t_{0}$\n\\end_inset\n\n;\n here,\n \n\\begin_inset Formula $\\mathbf{z}\\left(t\\right)=\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\mathbf{Z}$\n\\end_inset\n\n is the distance of the point from the body's center of mass,\n and \n\\begin_inset Formula $\\mathbf{X}=\\mathbf{r}\\left(t_{0}\\right)+\\mathbf{Z}$\n\\end_inset\n\n is the initial position.\n The velocity of that point is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\dot{\\mathbf{x}} & =\\dot{\\mathbf{r}}\\left(t\\right)+\\dot{\\boldsymbol{\\Lambda}}\\left(t\\right)\\cdot\\mathbf{Z}\\end{aligned}\n\\,,\\label{eq:rbm-velocity}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\boldsymbol{\\Lambda}}\\left(t\\right)=\\hat{\\boldsymbol{\\omega}}\\left(t\\right)\\cdot\\boldsymbol{\\Lambda}\\left(t\\right)=\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\hat{\\mathbf{W}}\\left(t\\right)\\,.\\label{eq:rbm-lambda-dot}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\hat{\\boldsymbol{\\omega}}$\n\\end_inset\n\n is an antisymmetric tensor with axial vector \n\\begin_inset Formula $\\boldsymbol{\\omega}$\n\\end_inset\n\n which represents the spatial angular velocity vector;\n similarly,\n \n\\begin_inset Formula $\\hat{\\mathbf{W}}$\n\\end_inset\n\n is an antisymmetric tensor with axial vector \n\\begin_inset Formula $\\mathbf{W}$\n\\end_inset\n\n (the material angular velocity),\n such that \n\\begin_inset Formula $\\boldsymbol{\\omega}=\\boldsymbol{\\Lambda}\\cdot\\mathbf{W}$\n\\end_inset\n\n and\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\boldsymbol{\\omega}}=\\boldsymbol{\\Lambda}\\cdot\\hat{\\mathbf{W}}\\cdot\\boldsymbol{\\Lambda}^{T}\\,.\\label{eq:rbm-material-spatial-angular-velocity}\n\\end{equation}\n\n\\end_inset\n\nWe may now rewrite\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\dot{\\mathbf{x}} & =\\dot{\\mathbf{r}}\\left(t\\right)+\\hat{\\boldsymbol{\\omega}}\\left(t\\right)\\cdot\\mathbf{z}\\left(t\\right)\\\\\n & =\\dot{\\mathbf{r}}\\left(t\\right)+\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\hat{\\mathbf{W}}\\left(t\\right)\\cdot\\mathbf{Z}\n\\end{aligned}\n\\,,\\label{eq:rbm-rigid-point-velocity}\n\\end{equation}\n\n\\end_inset\n\nso that the acceleration of the point is\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\ddot{\\mathbf{x}} & =\\ddot{\\mathbf{r}}\\left(t\\right)+\\left(\\hat{\\boldsymbol{\\alpha}}\\left(t\\right)+\\hat{\\boldsymbol{\\omega}}^{2}\\left(t\\right)\\right)\\cdot\\mathbf{z}\\\\\n & =\\ddot{\\mathbf{r}}\\left(t\\right)+\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\left(\\hat{\\mathbf{A}}\\left(t\\right)+\\hat{\\mathbf{W}}^{2}\\left(t\\right)\\right)\\cdot\\mathbf{Z}\n\\end{aligned}\n\\,,\\label{eq:rbm-rigid-point-acceleration}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\alpha}=\\dot{\\boldsymbol{\\omega}}=\\boldsymbol{\\Lambda}\\cdot\\mathbf{A}$\n\\end_inset\n\n is the spatial angular acceleration vector,\n \n\\begin_inset Formula $\\mathbf{A}=\\dot{\\mathbf{W}}$\n\\end_inset\n\n is the material angular acceleration vector.\n As shown below,\n the time discretization is performed in the material frame.\n\\end_layout\n\n\\begin_layout Subsection\nRigid Body Momentum Balance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Body-Momentum\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor a rigid body,\n the conservation of linear momentum is given by\n\\begin_inset Formula \n\\begin{equation}\n\\frac{d}{dt}\\left(m\\dot{\\mathbf{r}}\\right)=\\dot{\\mathbf{p}}=\\mathbf{f}^{ext}\\left(t\\right)\\label{eq:rbm-linear-momentum-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $m$\n\\end_inset\n\n is the mass of the rigid body,\n \n\\begin_inset Formula $\\dot{\\mathbf{r}}$\n\\end_inset\n\n is the velocity of the center of mass,\n \n\\begin_inset Formula $\\mathbf{p}=m\\dot{\\mathbf{r}}$\n\\end_inset\n\n is the linear momentum,\n and \n\\begin_inset Formula $\\mathbf{f}^{ext}\\left(t\\right)$\n\\end_inset\n\n represents the sum of external forces acting on the body.\n Here,\n \n\\begin_inset Formula $m$\n\\end_inset\n\n is constant for a rigid body.\n There are typically four contributions to \n\\begin_inset Formula $\\mathbf{f}^{ext}\\left(t\\right)$\n\\end_inset\n\n:\n Body forces \n\\begin_inset Formula $\\mathbf{f}_{b}^{ext}\\left(t\\right)=m\\mathbf{b}\\left(t\\right)$\n\\end_inset\n\n (where \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n represents the body force per mass,\n such as gravitational acceleration),\n other user-prescribed forces \n\\begin_inset Formula $\\mathbf{f}_{p}^{ext}\\left(t\\right)$\n\\end_inset\n\n (which act at the center of mass),\n forces \n\\begin_inset Formula $\\mathbf{f}_{c}^{ext}\\left(t\\right)$\n\\end_inset\n\n produced by rigid body connectors (such as revolute and prismatic joints,\n or contact forces),\n and forces \n\\begin_inset Formula $\\mathbf{f}_{f}^{ext}\\left(t\\right)$\n\\end_inset\n\n produced by rigid-flexible connections (where deformable materials interface with the rigid body),\n in which case \n\\begin_inset Formula $\\mathbf{f}_{f}^{ext}$\n\\end_inset\n\n is evaluated from the traction \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n over that interface,\n with \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n representing the stress in the deformable material.\n\\end_layout\n\n\\begin_layout Standard\nThe conservation of angular momentum is similarly given by\n\\begin_inset Formula \n\\begin{equation}\n\\frac{d}{dt}\\left(\\mathbf{J}\\cdot\\boldsymbol{\\omega}\\right)=\\dot{\\mathbf{h}}=\\boldsymbol{\\omega}\\times\\mathbf{h}+\\mathbf{J}\\cdot\\boldsymbol{\\alpha}=\\mathbf{m}^{ext}\\left(t\\right)\\label{eq:rbm-angular-momentum-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{J}$\n\\end_inset\n\n is the rigid body mass moment of inertia about its center of mass,\n \n\\begin_inset Formula $\\boldsymbol{\\omega}$\n\\end_inset\n\n is its angular velocity,\n \n\\begin_inset Formula $\\mathbf{h}=\\mathbf{J}\\cdot\\boldsymbol{\\omega}$\n\\end_inset\n\n is its angular momentum,\n \n\\begin_inset Formula $\\boldsymbol{\\alpha}=\\dot{\\boldsymbol{\\omega}}$\n\\end_inset\n\n is the rigid body angular acceleration,\n and \n\\begin_inset Formula $\\mathbf{m}^{ext}\\left(t\\right)$\n\\end_inset\n\n is the sum of moments acting on the rigid body.\n External moments include contributions from user-prescribed moments/torques \n\\begin_inset Formula $\\mathbf{m}_{p}^{ext}\\left(t\\right)$\n\\end_inset\n\n,\n from rigid body connectors,\n \n\\begin_inset Formula $\\mathbf{m}_{c}^{ext}\\left(t\\right)=\\mathbf{z}_{c}\\left(t\\right)\\times\\mathbf{f}_{c}^{ext}\\left(t\\right)$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{z}_{c}\\left(t\\right)$\n\\end_inset\n\n is the connector insertion relative to the rigid body center of mass,\n and rigid-flexible interfaces,\n \n\\begin_inset Formula $\\mathbf{m}_{f}^{ext}\\left(t\\right)=\\mathbf{z}_{f}\\left(t\\right)\\times\\mathbf{f}_{f}^{ext}\\left(t\\right)$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{z}_{f}\\left(t\\right)$\n\\end_inset\n\n is the position of the interface point relative to the rigid body center of mass.\n Since body forces \n\\begin_inset Formula $\\mathbf{f}_{b}^{ext}$\n\\end_inset\n\n and user-prescribed forces \n\\begin_inset Formula $\\mathbf{f}_{p}^{ext}$\n\\end_inset\n\n act at the center of mass,\n they do not contribute to \n\\begin_inset Formula $\\mathbf{m}^{ext}\\left(t\\right)$\n\\end_inset\n\n.\n Note that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{J}\\left(t\\right)=\\boldsymbol{\\Lambda}\\left(t\\right)\\cdot\\mathbf{J}_{r}\\cdot\\boldsymbol{\\Lambda}^{T}\\left(t\\right)\\,,\\label{eq:rbm-moi-rotation}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{J}_{r}$\n\\end_inset\n\n is the mass moment of inertia about the center of mass in the reference configuration and \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(t\\right)$\n\\end_inset\n\n is the rotation tensor representing the orientation of the rigid body at time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\boldsymbol{\\Lambda}=\\mathbf{I}$\n\\end_inset\n\n in the reference configuration.\n \n\\end_layout\n\n\\begin_layout Standard\nThe virtual work statement is given by\n\\begin_inset Formula \n\\begin{equation}\n\\delta W=\\delta\\mathbf{r}\\cdot\\left(\\mathbf{f}^{ext}\\left(t\\right)-\\dot{\\mathbf{p}}\\right)+\\delta\\boldsymbol{\\theta}\\cdot\\left(\\mathbf{m}^{ext}\\left(t\\right)-\\dot{\\mathbf{h}}\\right)\\,,\\label{eq:rbm-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{r}$\n\\end_inset\n\n is the virtual velocity of the center of mass and \n\\begin_inset Formula $\\delta\\boldsymbol{\\theta}$\n\\end_inset\n\n is the virtual angular velocity of the rigid body.\n \n\\end_layout\n\n\\begin_layout Subsection\nTime Discretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Time-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nNewmark Integration for Rigid Body Dynamics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Newmark-Integration-RBD\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n represent consecutive time points.\n According to the Newmark integration scheme,\n the rigid body center of mass velocity and acceleration at \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n may be expressed in terms of their values at \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\dot{\\mathbf{r}}_{n+1} & =\\dot{\\mathbf{r}}_{n}+\\Delta t\\left[\\left(1-\\gamma\\right)\\ddot{\\mathbf{r}}_{n}+\\gamma\\ddot{\\mathbf{r}}_{n+1}\\right]\\\\\n\\ddot{\\mathbf{r}}_{n+1} & =\\frac{1}{\\beta\\Delta t}\\left[\\frac{1}{\\Delta t}\\left(\\mathbf{r}_{n+1}-\\mathbf{r}_{n}\\right)-\\dot{\\mathbf{r}}_{n}\\right]+\\left(1-\\frac{1}{2\\beta}\\right)\\ddot{\\mathbf{r}}_{n}\n\\end{aligned}\n\\,,\\label{eq:td-Newmark-COM}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n are Newmark parameters that satisfy \n\\begin_inset Formula $0\\le2\\beta\\le1$\n\\end_inset\n\n and \n\\begin_inset Formula $0\\le\\gamma\\le1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nLet the rigid body rotation tensor \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(t\\right)$\n\\end_inset\n\n be expressed as \n\\begin_inset Formula $\\boldsymbol{\\Lambda}\\left(t\\right)=\\exp\\left[\\boldsymbol{\\xi}\\left(t\\right)\\right]$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\boldsymbol{\\xi}\\left(t\\right)$\n\\end_inset\n\n is the material rotation of the rigid body from its reference configuration.\n Thus,\n \n\\begin_inset Formula $\\boldsymbol{\\Lambda}_{n}=\\exp\\left[\\boldsymbol{\\xi}_{n}\\right]$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\Lambda}_{n+1}=\\exp\\left[\\boldsymbol{\\xi}_{n+1}\\right]$\n\\end_inset\n\n respectively represent the rigid body rotation tensors at \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n.\n (In practice,\n \n\\begin_inset Formula $\\boldsymbol{\\xi}$\n\\end_inset\n\n is stored as a quaternion to facilitate the multiplication of rotation tensors.) These tensors are related by the incremental spatial rotation \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n or material rotation \n\\begin_inset Formula $\\boldsymbol{\\Theta}$\n\\end_inset\n\n from \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n to \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}_{n+1}=\\cay\\left[\\boldsymbol{\\theta}\\right]\\cdot\\boldsymbol{\\Lambda}_{n}=\\boldsymbol{\\Lambda}_{n}\\cdot\\cay\\left[\\boldsymbol{\\Theta}\\right]\\,.\\label{eq:td-incremental-rotation}\n\\end{equation}\n\n\\end_inset\n\nHere,\n it should be understood that the material frame for this incremental rotation is the configuration at time \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n,\n while the spatial frame is the configuration at \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n.\n For rotational motion,\n the Newmark scheme is applied in the material frame as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{W}_{n+1} & =\\frac{\\gamma}{\\beta\\Delta t}\\boldsymbol{\\Theta}-\\mathbf{W}_{n}+\\left(2-\\frac{\\gamma}{\\beta}\\right)\\left(\\mathbf{W}_{n}+\\frac{\\Delta t}{2}\\mathbf{A}_{n}\\right)\\\\\n\\mathbf{A}_{n+1} & =\\frac{1}{\\gamma\\Delta t}\\left(\\mathbf{W}_{n+1}-\\mathbf{W}_{n}\\right)+\\left(1-\\frac{1}{\\gamma}\\right)\\mathbf{A}_{n}\\\\\n & =\\frac{1}{\\beta\\Delta t}\\left(\\frac{1}{\\Delta t}\\boldsymbol{\\Theta}-\\mathbf{W}_{n}\\right)+\\left(1-\\frac{1}{2\\beta}\\right)\\mathbf{A}_{n}\n\\end{aligned}\n\\,.\\label{eq:td-Newmark-mrot}\n\\end{equation}\n\n\\end_inset\n\nThen,\n using the relations \n\\begin_inset Formula $\\boldsymbol{\\omega}=\\boldsymbol{\\Lambda}\\cdot\\mathbf{W}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\alpha}=\\boldsymbol{\\Lambda}\\cdot\\mathbf{A}$\n\\end_inset\n\n at \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n,\n along with \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:td-incremental-rotation\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we may express these relations in the spatial frame as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\omega}_{n+1} & =\\cay\\left[\\boldsymbol{\\theta}\\right]\\cdot\\left(\\frac{\\gamma}{\\beta\\Delta t}\\boldsymbol{\\theta}-\\boldsymbol{\\omega}_{n}+\\left(2-\\frac{\\gamma}{\\beta}\\right)\\left(\\boldsymbol{\\omega}_{n}+\\frac{\\Delta t}{2}\\boldsymbol{\\alpha}_{n}\\right)\\right)\\\\\n\\boldsymbol{\\alpha}_{n+1} & =\\cay\\left[\\boldsymbol{\\theta}\\right]\\cdot\\left(\\frac{1}{\\beta\\Delta t}\\left(\\frac{1}{\\Delta t}\\boldsymbol{\\theta}-\\boldsymbol{\\omega}_{n}\\right)+\\left(1-\\frac{1}{2\\beta}\\right)\\boldsymbol{\\alpha}_{n}\\right)\n\\end{aligned}\n\\,.\\label{eq:td-Newmark-srot}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a nonlinear solution scheme we solve for \n\\begin_inset Formula $\\boldsymbol{\\Theta}$\n\\end_inset\n\n incrementally.\n According to \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rbr-material-rotation-increment\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the linearization of \n\\begin_inset Formula $\\cay\\left[\\boldsymbol{\\Theta}\\right]$\n\\end_inset\n\n along an increment \n\\begin_inset Formula $\\Delta\\boldsymbol{\\Theta}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\cay\\left[\\boldsymbol{\\Theta}\\right]\\right)\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\cay\\left[\\boldsymbol{\\Theta}\\right]\\cdot\\widehat{\\Delta\\boldsymbol{\\Theta}}\\,,\\label{eq:td-linearization-rot}\n\\end{equation}\n\n\\end_inset\n\nso that\n\\begin_inset Formula \n\\begin{equation}\nD\\boldsymbol{\\Lambda}_{n+1}\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\boldsymbol{\\Lambda}_{n+1}\\cdot\\widehat{\\Delta\\boldsymbol{\\Theta}}\\,.\\label{eq:td-linearization-Lambda}\n\\end{equation}\n\n\\end_inset\n\nThe linearizations of \n\\begin_inset Formula $\\mathbf{W}_{n+1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{A}_{n+1}$\n\\end_inset\n\n,\n as given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:td-Newmark-mrot\"\nnolink \"false\"\n\n\\end_inset\n\n,\n along an increment \n\\begin_inset Formula $\\Delta\\boldsymbol{\\Theta}$\n\\end_inset\n\n requires us to first evaluate \n\\begin_inset Formula $D\\boldsymbol{\\Theta}\\left[\\Delta\\boldsymbol{\\Theta}\\right]$\n\\end_inset\n\n.\n According to Puso \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso02\"\nliteral \"true\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nD\\boldsymbol{\\Theta}\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\mathbf{T}\\left(\\boldsymbol{\\Theta}\\right)\\cdot\\Delta\\boldsymbol{\\Theta}\\,,\\label{td-Theta-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{T}\\left(\\boldsymbol{\\Theta}\\right)=\\mathbf{I}+\\frac{1}{2}\\hat{\\boldsymbol{\\Theta}}+\\frac{1}{4}\\boldsymbol{\\Theta}\\otimes\\boldsymbol{\\Theta}\\,.\\label{eq:td-T-Theta-relation}\n\\end{equation}\n\n\\end_inset\n\nThus,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{W}_{n+1}\\left[\\Delta\\boldsymbol{\\Theta}\\right] & =\\frac{\\gamma}{\\beta\\Delta t}\\mathbf{T}\\left(\\boldsymbol{\\Theta}\\right)\\cdot\\Delta\\boldsymbol{\\Theta}\\\\\nD\\mathbf{A}_{n+1}\\left[\\Delta\\boldsymbol{\\Theta}\\right] & =\\frac{1}{\\beta\\Delta t^{2}}\\mathbf{T}\\left(\\boldsymbol{\\Theta}\\right)\\cdot\\Delta\\boldsymbol{\\Theta}\n\\end{aligned}\n\\,.\\label{eq:tf-W-A-linearizations}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneralized-\n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n Method for Rigid Body Dynamics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Generalized-alpha-RBD\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the generalized-\n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n method,\n we evaluate forces and moments at time \n\\begin_inset Formula $t_{n+\\alpha_{f}}=\\left(1-\\alpha_{f}\\right)t_{n}+\\alpha_{f}t_{n+1}$\n\\end_inset\n\n and the time rate of change of linear and angular momenta at time \n\\begin_inset Formula $t_{n+\\alpha_{m}}=\\left(1-\\alpha_{m}\\right)t_{n}+\\alpha_{m}t_{n+1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\alpha_{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\alpha_{m}$\n\\end_inset\n\n may be evaluated from the spectral radius for an infinite time step,\n \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Generalized-Alpha-Method\"\nnolink \"false\"\n\n\\end_inset\n\n).\n For second-order systems these parameters may be evaluated from \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bazilevs08\"\nliteral \"true\"\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\alpha_{f}=\\frac{1}{1+\\rho_{\\infty}}\\,,\\quad\\alpha_{m}=\\frac{2-\\rho_{\\infty}}{1+\\rho_{\\infty}}\\,,\\label{eq:ga-alphas-2nd}\n\\end{equation}\n\n\\end_inset\n\nThen,\n the Newmark parameters are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\beta & =\\frac{1}{4}\\left(1+\\alpha_{m}-\\alpha_{f}\\right)^{2}\\,,\\\\\n\\gamma & =\\frac{1}{2}+\\alpha_{m}-\\alpha_{f}\\,.\n\\end{aligned}\n\\label{eq:ga-Newmark-params}\n\\end{equation}\n\n\\end_inset\n\nAccordingly,\n to solve numerically for \n\\begin_inset Formula $\\delta W=0$\n\\end_inset\n\n over the time domain,\n we express \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rbm-virtual-work\"\nnolink \"false\"\n\n\\end_inset\n\n in the discretized time domain as\n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{r}\\cdot\\left(\\mathbf{f}_{n+\\alpha_{f}}^{ext}-\\dot{\\mathbf{p}}_{n+\\alpha_{m}}\\right)+\\delta\\boldsymbol{\\theta}\\cdot\\left(\\mathbf{m}_{n+\\alpha_{f}}^{ext}-\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\\right)=0\\,,\\label{eq:ga-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nor equivalently,\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\begin{array}{cc}\n\\delta\\mathbf{r} & \\delta\\boldsymbol{\\theta}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{n+\\alpha_{f}}^{ext}-\\dot{\\mathbf{p}}_{n+\\alpha_{m}}\\\\\n\\mathbf{m}_{n+\\alpha_{f}}^{ext}-\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\n\\end{array}\\right]=0\\,,\\label{eq:ga-virtual-work-alt}\n\\end{equation}\n\n\\end_inset\n\nThus,\n the residual vector is given by\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{R}\\right]=\\left[\\begin{array}{c}\n\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\\\\n\\mathbf{m}_{n+\\alpha_{f}}^{ext}\n\\end{array}\\right]-\\left[\\begin{array}{c}\n\\dot{\\mathbf{p}}_{n+\\alpha_{m}}\\\\\n\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\n\\end{array}\\right]\\label{eq:ga-residual-vector}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\dot{\\mathbf{p}}_{n+\\alpha_{m}} & =\\left(1-\\alpha_{m}\\right)\\dot{\\mathbf{p}}_{n}+\\alpha_{m}\\dot{\\mathbf{p}}_{n+1}\\\\\n\\dot{\\mathbf{h}}_{n+\\alpha_{m}} & =\\left(1-\\alpha_{m}\\right)\\dot{\\mathbf{h}}_{n}+\\alpha_{m}\\dot{\\mathbf{h}}_{n+1}\n\\end{aligned}\n\\,.\\label{eq:ga-momenta-interpolation}\n\\end{equation}\n\n\\end_inset\n\nAccording to the Newmark integration scheme,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\dot{\\mathbf{p}}_{n+1} & =\\frac{\\mathbf{p}_{n+1}-\\mathbf{p}_{n}}{\\gamma\\Delta t}+\\left(1-\\frac{1}{\\gamma}\\right)\\dot{\\mathbf{p}}_{n}\\\\\n\\dot{\\mathbf{h}}_{n+1} & =\\frac{\\mathbf{h}_{n+1}-\\mathbf{h}_{n}}{\\gamma\\Delta t}+\\left(1-\\frac{1}{\\gamma}\\right)\\dot{\\mathbf{h}}_{n}\n\\end{aligned}\n\\,,\\label{eq:ga-momenta-Newmark}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{p}_{n+1}=m\\dot{\\mathbf{r}}_{n+1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{h}_{n+1}=\\mathbf{J}_{n+1}\\cdot\\boldsymbol{\\omega}_{n+1}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe nonlinear system \n\\begin_inset Formula $\\mathbf{R}=\\mathbf{0}$\n\\end_inset\n\n is solved using a Newton scheme that requires linearizing \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n along increments \n\\begin_inset Formula $\\Delta\\mathbf{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\boldsymbol{\\theta}$\n\\end_inset\n\n.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{R}+D\\mathbf{R}\\left[\\Delta\\mathbf{r}\\right]+D\\mathbf{R}\\left[\\Delta\\boldsymbol{\\theta}\\right]\\approx\\mathbf{0}\\,.\\label{eq:ga-Newton-scheme}\n\\end{equation}\n\n\\end_inset\n\nThe increments \n\\begin_inset Formula $\\Delta\\mathbf{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\boldsymbol{\\theta}$\n\\end_inset\n\n are evaluated at \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n and the iterative Newton scheme requires updates of the form\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{r}_{n+1}^{j+1} & =\\mathbf{r}_{n+1}^{j}+\\Delta\\mathbf{r}\\\\\n\\cay\\left[\\boldsymbol{\\theta}^{j+1}\\right] & =\\cay\\left[\\Delta\\boldsymbol{\\theta}\\right]\\cdot\\cay\\left[\\boldsymbol{\\theta}^{j}\\right]\n\\end{aligned}\n\\,,\\label{eq:ga-Newton-iterations}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $j$\n\\end_inset\n\n represents the Newton iteration.\n At each Newton iteration,\n the current value of \n\\begin_inset Formula $\\cay\\left[\\boldsymbol{\\theta}^{j+1}\\right]$\n\\end_inset\n\n is used to perform the update\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\Lambda}_{n+1}^{j+1}=\\cay\\left[\\boldsymbol{\\theta}^{j+1}\\right]\\cdot\\boldsymbol{\\Lambda}_{n}\\,,\\label{eq:ga-rotation-iterations}\n\\end{equation}\n\n\\end_inset\n\nuntil convergence is achieved.\n\\end_layout\n\n\\begin_layout Standard\nIn practice,\n it is convenient to store \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\boldsymbol{\\theta}$\n\\end_inset\n\n in quaternions,\n recognizing that\n\\begin_inset Formula \n\\[\n\\cay\\left[\\theta\\mathbf{n}\\right]=\\exp\\left[\\left(2\\tan^{-1}\\frac{\\theta}{2}\\right)\\mathbf{n}\\right]\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the unit vector along \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\theta=\\left\\Vert \\boldsymbol{\\theta}\\right\\Vert $\n\\end_inset\n\n.\n Thus,\n it is \n\\begin_inset Formula $\\left(2\\tan^{-1}\\frac{\\theta}{2}\\right)\\mathbf{n}$\n\\end_inset\n\n which is stored in the quaternion,\n instead of \n\\begin_inset Formula $\\theta\\mathbf{n}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn the linearization of \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n,\n the contributions from the rate of change of linear momentum \n\\begin_inset Formula $\\dot{\\mathbf{p}}_{n+\\alpha_{m}}$\n\\end_inset\n\n reduce to\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\dot{\\mathbf{p}}_{n+\\alpha_{m}}\\left[\\Delta\\mathbf{r}\\right] & =\\frac{\\alpha_{m}}{\\beta\\Delta t^{2}}m\\Delta\\mathbf{r}\\\\\nD\\dot{\\mathbf{p}}_{n+\\alpha_{m}}\\left[\\Delta\\boldsymbol{\\theta}\\right] & =\\mathbf{0}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nTo evaluate the contributions from the rate of change of angular momentum \n\\begin_inset Formula $\\dot{\\mathbf{h}}_{n+\\alpha_{m}}$\n\\end_inset\n\n,\n we start from \n\\begin_inset Formula $D\\dot{\\mathbf{h}}_{n+\\alpha_{m}}=\\alpha_{m}D\\dot{\\mathbf{h}}_{n+1}$\n\\end_inset\n\n.\n Then,\n it becomes necessary to transform the variables to the material frame,\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\dot{\\mathbf{h}}_{n+1} & =\\boldsymbol{\\omega}_{n+1}\\times\\mathbf{h}_{n+1}+\\mathbf{J}_{n+1}\\cdot\\boldsymbol{\\alpha}_{n+1}\\\\\n & =\\boldsymbol{\\Lambda}_{n+1}\\cdot\\left(\\hat{\\mathbf{W}}_{n+1}\\cdot\\mathbf{J}_{r}\\cdot\\mathbf{W}_{n+1}+\\mathbf{J}_{r}\\cdot\\mathbf{A}_{n+1}\\right)\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nIt follows that\n\\begin_inset Formula \n\\[\nD\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\\left[\\Delta\\mathbf{r}\\right]=\\mathbf{0}\\,.\n\\]\n\n\\end_inset\n\nThen,\n using the relations in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Newmark-Integration-RBD\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it can be shown that\n\\begin_inset Formula \n\\[\nD\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\alpha_{m}\\left[\\frac{1}{\\beta\\Delta t}\\left(\\left(\\gamma\\hat{\\boldsymbol{\\omega}}_{n+1}+\\frac{1}{\\Delta t}\\mathbf{I}\\right)\\cdot\\mathbf{J}_{n+1}-\\gamma\\hat{\\mathbf{h}}_{n+1}\\right)\\cdot\\mathbf{T}\\left(\\boldsymbol{\\theta}\\right)-\\hat{\\dot{\\mathbf{h}}}_{n+1}\\right]\\cdot\\Delta\\boldsymbol{\\theta}\\equiv\\alpha_{m}\\mathbf{K}\\cdot\\Delta\\boldsymbol{\\theta}\\,.\n\\]\n\n\\end_inset\n\nAlternatively,\n we may use the discretization in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:ga-momenta-Newmark\"\nnolink \"false\"\n\n\\end_inset\n\n to produce\n\\begin_inset Formula \n\\[\nD\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\frac{\\alpha_{m}}{\\gamma\\Delta t}D\\mathbf{h}_{n+1}\\left[\\Delta\\boldsymbol{\\Theta}\\right]\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\nD\\mathbf{h}_{n+1}\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\left(\\frac{\\gamma}{\\beta\\Delta t}\\mathbf{J}_{n+1}\\cdot\\mathbf{T}\\left(\\boldsymbol{\\theta}\\right)-\\hat{\\mathbf{h}}_{n+1}\\right)\\cdot\\Delta\\boldsymbol{\\theta}\n\\]\n\n\\end_inset\n\nso that\n\\begin_inset Formula \n\\[\nD\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\\left[\\Delta\\boldsymbol{\\Theta}\\right]=\\frac{\\alpha_{m}}{\\Delta t}\\left(\\frac{1}{\\beta\\Delta t}\\mathbf{J}_{n+1}\\cdot\\mathbf{T}\\left(\\boldsymbol{\\theta}\\right)-\\frac{1}{\\gamma}\\hat{\\mathbf{h}}_{n+1}\\right)\\cdot\\Delta\\boldsymbol{\\theta}\\equiv\\alpha_{m}\\mathbf{K}\\cdot\\Delta\\boldsymbol{\\theta}\n\\]\n\n\\end_inset\n\nTherefore,\n the contribution to \n\\begin_inset Formula $D\\mathbf{R}$\n\\end_inset\n\n from the linear and angular momenta produces a stiffness matrix called the mass matrix,\n\\begin_inset Formula \n\\[\nD\\left[\\begin{array}{c}\n\\dot{\\mathbf{p}}_{n+\\alpha_{m}}\\\\\n\\dot{\\mathbf{h}}_{n+\\alpha_{m}}\n\\end{array}\\right]=\\alpha_{m}\\left[\\begin{array}{cc}\n\\frac{m}{\\beta\\Delta t^{2}}\\mathbf{I} & \\mathbf{0}\\\\\n\\mathbf{0} & \\mathbf{K}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}\\\\\n\\Delta\\boldsymbol{\\theta}\n\\end{array}\\right]\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider that the moments \n\\begin_inset Formula $\\mathbf{m}_{n+\\alpha_{f}}^{ext}$\n\\end_inset\n\n about the rigid body center of mass are produced by the forces \n\\begin_inset Formula $\\mathbf{f}_{n+\\alpha_{f}}^{ext}$\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\[\n\\mathbf{m}_{n+\\alpha_{f}}^{ext}=\\mathbf{z}_{n+\\alpha_{f}}\\times\\mathbf{f}_{n+\\alpha_{f}}^{ext}=\\hat{\\mathbf{z}}_{n+\\alpha_{f}}\\cdot\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{z}_{n+\\alpha_{f}}$\n\\end_inset\n\n is the moment arm at \n\\begin_inset Formula $t_{n+\\alpha_{f}}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{z}_{n+\\alpha_{f}} & =\\left(1-\\alpha_{f}\\right)\\mathbf{z}_{n}+\\alpha_{f}\\mathbf{z}_{n+1}\\\\\n & =\\left[\\left(1-\\alpha_{f}\\right)\\boldsymbol{\\Lambda}_{n}+\\alpha_{f}\\boldsymbol{\\Lambda}_{n+1}\\right]\\cdot\\mathbf{Z}\\\\\n & \\equiv\\boldsymbol{\\Lambda}_{n+\\alpha_{f}}\\cdot\\mathbf{Z}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{Z}$\n\\end_inset\n\n is the moment arm in the reference configuration.\n Note that\n\\begin_inset Formula \n\\[\n\\begin{aligned}D\\mathbf{z}_{n+\\alpha_{f}}\\left[\\Delta\\mathbf{r}\\right] & =\\mathbf{0}\\\\\nD\\mathbf{z}_{n+\\alpha_{f}}\\left[\\Delta\\boldsymbol{\\theta}\\right] & =\\alpha_{f}D\\boldsymbol{\\Lambda}_{n+1}\\left[\\Delta\\boldsymbol{\\theta}\\right]\\cdot\\mathbf{Z}=-\\alpha_{f}\\hat{\\mathbf{z}}_{n+1}\\cdot\\Delta\\boldsymbol{\\theta}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThus,\n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{c}\nD\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\mathbf{r}\\right]\\\\\nD\\mathbf{m}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\mathbf{r}\\right]\n\\end{array}\\right]=\\left[\\begin{array}{c}\nD\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\mathbf{r}\\right]\\\\\n\\mathbf{z}_{n+\\alpha_{f}}\\times D\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\mathbf{r}\\right]\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{c}\nD\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\boldsymbol{\\theta}\\right]\\\\\nD\\mathbf{m}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\boldsymbol{\\theta}\\right]\n\\end{array}\\right]=\\left[\\begin{array}{c}\nD\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\boldsymbol{\\theta}\\right]\\\\\n\\mathbf{z}_{n+\\alpha_{f}}\\times D\\mathbf{f}_{n+\\alpha_{f}}^{ext}\\left[\\Delta\\boldsymbol{\\theta}\\right]-\\left(\\alpha_{f}\\hat{\\mathbf{z}}_{n+1}\\cdot\\Delta\\boldsymbol{\\theta}\\right)\\times\\mathbf{f}_{n+\\alpha_{f}}^{ext}\n\\end{array}\\right]\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nContact and Coupling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Contact-and-Coupling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio allows the user to connect the different parts of the model in various ways.\n Deformable parts can be connected to rigid bodies.\n Deformable objects can be brought in contact with each other.\n Rigid bodies can be connected with rigid joints.\n This chapter describes the different ways to couple parts together.\n\\end_layout\n\n\\begin_layout Section\nSliding Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Sliding-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section summarizes the theoretical developments of the two body contact problem.\n After introducing some notation and terminology,\n the contact integral is presented,\n which contains the contribution to the virtual work equation from the contact tractions.\n Since the nonlinear contact problem is solved using a Newton based iterative method,\n the contact integral is linearized.\n Next,\n anticipating a finite element implementation,\n the contact integral and its linearization are discretized using a standard finite element approach.\n Finally the augmented Lagrangian method for enforcing the contact constraints is described.\n\\end_layout\n\n\\begin_layout Subsection\nContact Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Sliding-Contact-Kinematics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor the most part the notation of this section follows \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen02\"\nliteral \"true\"\n\n\\end_inset\n\n,\n with a few simplifications here and there since the implementation in FEBio is currently for quasi-static,\n frictionless,\n two body contact problem.\n\\end_layout\n\n\\begin_layout Standard\nThe volume occupied by body \n\\begin_inset Formula $i$\n\\end_inset\n\n in the reference configuration is denoted by \n\\begin_inset Formula $\\Omega^{\\left(i\\right)}\\subset\\mathbb{R}^{3}$\n\\end_inset\n\nwhere \n\\begin_inset Formula $i=1,2$\n\\end_inset\n\n.\n The boundary of body \n\\begin_inset Formula $i$\n\\end_inset\n\n is denoted by \n\\begin_inset Formula $\\Gamma^{\\left(i\\right)}$\n\\end_inset\n\n and is divided into three regions \n\\begin_inset Formula $\\Gamma^{\\left(i\\right)}=\\Gamma_{\\sigma}^{\\left(i\\right)}\\cup\\Gamma_{u}^{\\left(i\\right)}\\cup\\Gamma_{c}^{\\left(i\\right)}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\Gamma_{\\sigma}^{\\left(i\\right)}$\n\\end_inset\n\n is the boundary where tractions are applied,\n \n\\begin_inset Formula $\\Gamma_{u}^{\\left(i\\right)}$\n\\end_inset\n\n the boundary where the solution is prescribed and \n\\begin_inset Formula $\\Gamma_{c}^{\\left(i\\right)}$\n\\end_inset\n\n the part of the boundary that will be in contact with the other body.\n It is assumed that \n\\begin_inset Formula $\\Gamma_{\\sigma}^{\\left(i\\right)}\\cap\\Gamma_{u}^{\\left(i\\right)}\\cap\\Gamma_{c}^{\\left(i\\right)}=\\emptyset$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe deformation of body \n\\begin_inset Formula $i$\n\\end_inset\n\n is defined by \n\\begin_inset Formula $\\boldsymbol{\\varphi}^{\\left(i\\right)}$\n\\end_inset\n\n.\n The boundary of the deformed body \n\\begin_inset Formula $i$\n\\end_inset\n\n,\n that is the boundary of \n\\begin_inset Formula $\\boldsymbol{\\varphi}^{\\left(i\\right)}\\left(\\Omega^{\\left(i\\right)}\\right)$\n\\end_inset\n\n is denoted by \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}=\\gamma_{\\sigma}^{\\left(i\\right)}\\cup\\gamma_{u}^{\\left(i\\right)}\\cup\\gamma_{c}^{\\left(i\\right)}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\gamma_{\\sigma}^{\\left(i\\right)}=\\boldsymbol{\\varphi}^{\\left(i\\right)}\\left(\\Gamma_{\\sigma}^{\\left(i\\right)}\\right)$\n\\end_inset\n\n is the boundary in the current configuration where the tractions are applied and similar definitions for \n\\begin_inset Formula $\\gamma_{u}^{\\left(i\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma_{c}^{\\left(i\\right)}$\n\\end_inset\n\n.\n See the figure below for a graphical illustration of the defined regions.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigTwoBodyContactProblem.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nThe two-body contact problem.\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPoints in body 1 are denoted by \n\\series bold\n\n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n\n\\series default\n in the reference configuration and \n\\series bold\n\n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n\n\\series default\n in the current configuration.\n For body 2 these points are denoted by \n\\series bold\n\n\\begin_inset Formula $\\mathbf{Y}$\n\\end_inset\n\n\n\\series default\n and \n\\series bold\n\n\\begin_inset Formula $\\mathbf{y}$\n\\end_inset\n\n\n\\series default\n.\n To define contact,\n the location where the two bodies are in contact with each other must be established.\n If body 1 is the \n\\emph on\nslave body\n\\emph default\n and body 2 is the \n\\emph on\nmaster body\n\\emph default\n,\n then for a given point \n\\series bold\n\n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n\n\\series default\n on the slave reference contact surface there is a point \n\\begin_inset Formula $\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)$\n\\end_inset\n\n on the master contact surface that is in some sense closest to point \n\\series bold\n\n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n\n\\series default\n.\n This closest point is defined in a closest point projection sense:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)=\\arg\\min\\limits_{\\mathbf{Y}\\in\\Gamma_{c}^{\\left(2\\right)}}\\left\\Vert \\boldsymbol{\\varphi}^{\\left(1\\right)}\\left(\\mathbf{X}\\right)-\\boldsymbol{\\varphi}^{\\left(2\\right)}\\left(\\mathbf{Y}\\right)\\right\\Vert \\,.\\label{eq564}\n\\end{equation}\n\n\\end_inset\n\nWith the definition of \n\\begin_inset Formula $\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)$\n\\end_inset\n\n established the \n\\emph on\ngap function\n\\emph default\n can be defined,\n which is a measure for the distance between \n\\series bold\n\n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n \n\\series default\nand \n\\begin_inset Formula $\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\ng\\left(\\mathbf{X}\\right)=-\\boldsymbol{\\nu}\\cdot\\left(\\boldsymbol{\\varphi}^{\\left(1\\right)}\\left(\\mathbf{X}\\right)-\\boldsymbol{\\varphi}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\right)\\,,\\label{eq565}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\nu}$\n\\end_inset\n\n is the local surface normal of surface \n\\begin_inset Formula $\\gamma_{c}^{\\left(2\\right)}$\n\\end_inset\n\n evaluated at \n\\begin_inset Formula $\\mathbf{\\bar{y}}=\\boldsymbol{\\varphi}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $g>0$\n\\end_inset\n\n when \n\\series bold\n\n\\begin_inset Formula $\\mathbf{X}$\n\\end_inset\n\n \n\\series default\nhas penetrated body 2,\n so that the constraint condition to be satisfied at all time is \n\\begin_inset Formula $g\\leqslant0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nWeak Form of Two Body Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Weak-Form-Two-Body\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe balance of linear momentum can be written for each of the two bodies in the reference configuration,\n \n\\begin_inset Formula \n\\begin{equation}\nG^{\\left(i\\right)}\\left(\\boldsymbol{\\varphi}^{\\left(i\\right)},\\mathbf{w}^{\\left(i\\right)}\\right)=\\int\\limits_{\\Omega^{\\left(i\\right)}}\\Grad\\mathbf{w}^{\\left(i\\right)}:\\mathbf{P}^{\\left(i\\right)}\\,d\\Omega-\\int\\limits_{\\Omega^{\\left(i\\right)}}\\mathbf{w}^{\\left(i\\right)}\\cdot\\mathbf{F}^{\\left(i\\right)}\\,d\\Omega-\\int\\limits_{\\Gamma_{s}^{\\left(i\\right)}}\\mathbf{w}^{\\left(i\\right)}\\cdot\\mathbf{T}^{\\left(i\\right)}\\,d\\Gamma-\\int\\limits_{\\Gamma_{c}^{\\left(i\\right)}}\\mathbf{w}^{\\left(i\\right)}\\cdot\\mathbf{T}^{\\left(i\\right)}\\,d\\Gamma=0\\,,\\label{eq566}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{w}^{\\left(i\\right)}$\n\\end_inset\n\n is a weighting function and \n\\series bold\n\n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n\n\\series default\n is the \n\\begin_inset Formula $1^{\\text{st}}$\n\\end_inset\n\n Piola-Kirchhoff stress tensor.\n The last term corresponds to the virtual work of the contact tractions on body \n\\begin_inset Formula $i$\n\\end_inset\n\n.\n For notational convenience,\n the notations \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\nand \n\\begin_inset Formula $w$\n\\end_inset\n\n are introduced to denote the collection of the respective mappings \n\\begin_inset Formula $\\boldsymbol{\\varphi}^{\\left(i\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{w}^{\\left(i\\right)}$\n\\end_inset\n\n (for \n\\begin_inset Formula $i=$\n\\end_inset\n\n1,2).\n In other words,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\varphi & :\\bar{\\Omega}^{\\left(1\\right)}\\cup\\bar{\\Omega}^{\\left(2\\right)}\\to\\mathbb{R}^{3}\\,,\\\\\nw & :\\bar{\\Omega}^{\\left(1\\right)}\\cup\\bar{\\Omega}^{\\left(2\\right)}\\to\\mathbb{R}^{3}\\,.\n\\end{aligned}\n\\label{eq567}\n\\end{equation}\n\n\\end_inset\n\nThe variational principle for the two body system is the sum of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq566\"\nnolink \"false\"\n\n\\end_inset\n\n for body 1 and 2 and can be expressed as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}G\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right) & :=\\sum\\limits_{i=1}^{2}G^{\\left(i\\right)}\\left(\\boldsymbol{\\varphi}^{\\left(i\\right)},\\mathbf{w}^{\\left(i\\right)}\\right)\\\\\n & =\\underbrace{\\sum\\limits_{i=1}^{2}\\left\\{ \\int\\limits_{\\Omega^{\\left(i\\right)}}\\Grad\\mathbf{w}^{\\left(i\\right)}:\\mathbf{P}^{\\left(i\\right)}\\,d\\Omega-\\int\\limits_{\\Omega^{\\left(i\\right)}}\\mathbf{w}^{\\left(i\\right)}\\cdot\\mathbf{F}^{\\left(i\\right)}\\,d\\Omega-\\int\\limits_{\\Gamma_{s}^{\\left(i\\right)}}\\mathbf{w}^{\\left(i\\right)}\\cdot\\mathbf{T}^{\\left(i\\right)}\\,d\\Gamma\\right\\} }_{G^{\\text{int},\\text{ext}}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)}\\\\\n & \\underbrace{-\\sum\\limits_{i=1}^{2}\\int\\limits_{\\Gamma_{c}^{\\left(i\\right)}}\\mathbf{w}^{\\left(i\\right)}\\cdot\\mathbf{T}^{\\left(i\\right)}\\,d\\Gamma}_{G^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)}\\,.\n\\end{aligned}\n\\label{eq568}\n\\end{equation}\n\n\\end_inset\n\nOr in short,\n \n\\begin_inset Formula \n\\begin{equation}\nG\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)=G^{\\text{int},\\text{ext}}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)+G^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)\\,.\\label{eq569}\n\\end{equation}\n\n\\end_inset\n\nNote that the minus sign is included in the definition of the contact integral \n\\begin_inset Formula $G^{c}$\n\\end_inset\n\n.\n The contact integral can be written as an integration over the contact surface of body 1 by balancing linear momentum across the contact surface:\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{\\left(2\\right)}\\left(\\mathbf{\\bar{y}}\\left(\\mathbf{x}\\right)\\right)d\\Gamma^{\\left(2\\right)}=-\\mathbf{t}^{\\left(1\\right)}\\left(\\mathbf{x}\\right)\\,d\\Gamma^{\\left(1\\right)}\\,.\\label{eq570}\n\\end{equation}\n\n\\end_inset\n\nThe contact integral can now be rewritten over the contact surface of body 1:\n \n\\begin_inset Formula \n\\begin{equation}\nG^{c}=-\\int\\limits_{\\Gamma_{c}^{\\left(1\\right)}}\\mathbf{t}^{\\left(1\\right)}\\left(\\mathbf{x}\\right)\\cdot\\left[\\mathbf{w}^{\\left(1\\right)}\\left(\\mathbf{x}\\right)-\\mathbf{w}^{\\left(2\\right)}\\left(\\mathbf{\\bar{y}}\\left(\\mathbf{x}\\right)\\right)\\right]\\,d\\Gamma\\,.\\label{eq571}\n\\end{equation}\n\n\\end_inset\n\nIn the case of frictionless contact,\n the contact traction is taken as perpendicular to surface 2 and therefore can be written as,\n \n\\begin_inset Formula $\\mathbf{t}^{\\left(1\\right)}=t_{N}\\boldsymbol{\\nu}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\boldsymbol{\\nu}$\n\\end_inset\n\n is the (outward) surface normal and \n\\begin_inset Formula $t_{N}$\n\\end_inset\n\n is to be determined from the solution strategy.\n For example in a Lagrange multiplier method the \n\\begin_inset Formula $t_{N}$\n\\end_inset\n\n's would be the Lagrange multipliers.\n\\end_layout\n\n\\begin_layout Standard\nBy noting that the variation of the gap function is given by \n\\begin_inset Formula \n\\begin{equation}\n\\delta g=-\\boldsymbol{\\nu}\\cdot\\left(\\mathbf{w}^{\\left(1\\right)}\\left(\\mathbf{x}\\right)-\\mathbf{w}^{\\left(2\\right)}\\left(\\mathbf{\\bar{y}}\\left(\\mathbf{x}\\right)\\right)\\right)\\,,\\label{eq572}\n\\end{equation}\n\n\\end_inset\n\nequation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq571\"\nnolink \"false\"\n\n\\end_inset\n\n can be simplified as,\n \n\\begin_inset Formula \n\\begin{equation}\nG^{c}=\\int\\limits_{\\Gamma_{c}^{\\left(1\\right)}}t_{N}\\delta g\\,d\\Gamma\\,.\\label{eq573}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of the Contact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Linearization-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a Newton-Raphson implementation the contact integral must be linearized with respect to the current configuration:\n \n\\begin_inset Formula \n\\begin{equation}\n\\Delta G^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)=\\int\\limits_{\\Gamma_{c}^{\\left(1\\right)}}\\Delta\\left(t_{N}\\delta g\\right)\\,d\\Gamma\\,.\\label{eq574}\n\\end{equation}\n\n\\end_inset\n\nExamining the normal contact term first,\n the directional derivative of \n\\begin_inset Formula $t_{N}$\n\\end_inset\n\n is given (for the case of the penalty regularization) by:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\Delta t_{N} & =\\Delta\\left\\{ \\varepsilon_{N}\\left\\langle g\\right\\rangle \\right\\} \\\\\n & =H\\left(g\\right)\\varepsilon_{N}\\Delta g\n\\end{aligned}\n\\,,\\label{eq575}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{N}$\n\\end_inset\n\n is the penalty factor and \n\\begin_inset Formula $H\\left(g\\right)$\n\\end_inset\n\n is the Heaviside function.\n The quantity \n\\begin_inset Formula $\\Delta\\left(\\delta g\\right)$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\Delta\\left(\\delta g\\right) & =g\\left[\\boldsymbol{\\nu}\\cdot\\delta\\varphi_{,\\gamma}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)+\\kappa_{\\alpha\\gamma}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\delta\\bar{\\xi}_{\\alpha}\\right]m^{\\gamma\\beta}\\\\\n & \\left[\\boldsymbol{\\nu}\\cdot\\Delta\\varphi_{,\\beta}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)+\\kappa_{\\alpha\\beta}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\Delta\\bar{\\xi}^{\\alpha}\\right]\\\\\n & +\\delta\\bar{\\xi}^{\\beta}\\boldsymbol{\\nu}\\cdot\\left[\\Delta\\varphi_{,\\beta}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\right]+\\Delta\\bar{\\xi}^{\\beta}\\boldsymbol{\\nu}\\cdot\\left[\\delta\\boldsymbol{\\varphi}_{,\\beta}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\right]\\\\\n & +\\kappa_{\\alpha\\beta}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\delta\\bar{\\xi}^{\\beta}\\Delta\\bar{\\xi}^{\\alpha}\\,.\n\\end{aligned}\n\\label{eq576}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization of the Contact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Discretization-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral,\n which is repeated here,\n \n\\begin_inset Formula \n\\begin{equation}\nG^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)=\\int\\limits_{\\Gamma^{\\left(1\\right)}}t_{N}\\delta g\\,d\\Gamma\\,,\\label{eq577}\n\\end{equation}\n\n\\end_inset\n\nwill now be discretized using a standard finite element procedure.\n First it is noted that the integration can be written as a sum over the surface element areas:\n \n\\begin_inset Formula \n\\begin{equation}\nG^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)=\\sum\\limits_{e=1}^{N_{sel}}\\int\\limits_{\\Gamma^{\\left(1\\right)e}}t_{N}\\delta g\\,d\\Gamma\\,,\\label{eq578}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{sel}$\n\\end_inset\n\n is the number of surface elements.\n The integration can be approximated using a quadrature rule,\n \n\\begin_inset Formula \n\\begin{equation}\nG^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)\\cong\\sum\\limits_{e=1}^{N_{sel}}\\left\\{ \\sum\\limits_{i=1}^{N_{int}^{e}}w^{i}j\\left(\\boldsymbol{\\xi}_{i}\\right)t_{N}\\left(\\boldsymbol{\\xi}_{i}\\right)\\delta g\\left(\\boldsymbol{\\xi}_{i}\\right)\\right\\} \\,,\\label{eq579}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{int}^{e}$\n\\end_inset\n\n are the number of integration points for element \n\\begin_inset Formula $e$\n\\end_inset\n\n.\n It is now assumed that the integration points coincide with the element's nodes (e.g.\n for a quadrilateral surface element we have \n\\begin_inset Formula $\\boldsymbol{\\xi}_{1}=\\left(-1,-1\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\xi}_{2}=\\left(1,-1\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\xi}_{3}=\\left(1,1\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\xi}_{4}=\\left(-1,1\\right))$\n\\end_inset\n\n.\n With this quadrature rule,\n we have \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}w^{\\left(1\\right)}\\left(\\boldsymbol{\\xi}_{i}\\right) & =\\mathbf{c}_{i}^{\\left(1\\right)}\\\\\nw^{\\left(2\\right)}\\left(\\bar{\\boldsymbol{\\xi}}_{i}\\right) & =\\sum\\limits_{j=1}^{n}N_{j}\\left(\\bar{\\boldsymbol{\\xi}}_{i}\\right)\\mathbf{c}_{j}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq580}\n\\end{equation}\n\n\\end_inset\n\nso that,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta g\\left(\\boldsymbol{\\xi}_{i}\\right)=-\\boldsymbol{\\nu}\\cdot\\left(\\mathbf{c}_{i}^{\\left(1\\right)}-\\sum\\limits_{j=1}^{n}N_{j}^{\\left(2\\right)}\\left(\\bar{\\boldsymbol{\\xi}}_{i}\\right)\\mathbf{c}_{j}^{\\left(2\\right)}\\right)\\,.\\label{eq581}\n\\end{equation}\n\n\\end_inset\n\nIf the following vectors are defined,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\boldsymbol{\\Phi}^{T} & =\\left[\\begin{array}{cccc}\n\\mathbf{c}_{i}^{\\left(1\\right)} & \\mathbf{c}_{1}^{\\left(2\\right)} & \\cdots & \\mathbf{c}_{n}^{\\left(2\\right)}\\end{array}\\right]\\\\\n\\mathbf{N}^{T} & =\\left[\\begin{array}{cccc}\n\\boldsymbol{\\nu} & -\\boldsymbol{\\nu}N_{1}^{\\left(2\\right)} & \\cdots & -\\boldsymbol{\\nu}N_{n}^{\\left(2\\right)}\\end{array}\\right]\n\\end{aligned}\n\\,,\\label{eq582}\n\\end{equation}\n\n\\end_inset\n\nequation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq579\"\nnolink \"false\"\n\n\\end_inset\n\n can then be rewritten as follows,\n \n\\begin_inset Formula \n\\begin{equation}\nG^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)\\cong\\sum\\limits_{e=1}^{N_{sel}}\\left\\{ \\sum\\limits_{i=1}^{N_{int}^{e}}w^{i}j\\left(\\boldsymbol{\\xi}_{i}\\right)t_{N}\\left(\\boldsymbol{\\xi}_{i}\\right)\\delta\\boldsymbol{\\Phi}^{T}\\mathbf{N}^{T}\\right\\} \\,.\\label{eq583}\n\\end{equation}\n\n\\end_inset\n\nThe specific form for \n\\begin_inset Formula $t_{N}$\n\\end_inset\n\nwill depend on the method employed for enforcing the contact constraint.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization of the Contact Stiffness\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Discretization-Contact-Stiffness\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA similar procedure can now be used to calculate the discretized contact stiffness matrix.\n The linearization of the contact integral is repeated here:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\Delta G^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right) & =\\sum\\limits_{e=1}^{N_{sel}}\\int\\limits_{\\Gamma^{\\left(1\\right)e}}\\Delta\\left(t_{N}\\delta g\\right)\\,d\\Gamma\\\\\n & =\\sum\\limits_{e=1}^{N_{sel}}\\sum\\limits_{i=1}^{N_{int}^{e}}w_{i}j\\left(\\boldsymbol{\\xi}_{i}\\right)\\Delta\\left(t_{N}\\delta g\\right)\\left(\\boldsymbol{\\xi}_{i}\\right)\n\\end{aligned}\n\\,.\\label{eq584}\n\\end{equation}\n\n\\end_inset\n\nUsing matrix notation we can rewrite equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq584\"\nnolink \"false\"\n\n\\end_inset\n\n as,\n \n\\begin_inset Formula \n\\begin{equation}\n\\Delta W^{c}\\left(\\boldsymbol{\\varphi},\\mathbf{w}\\right)=\\sum\\limits_{e=1}^{N_{sel}}\\sum\\limits_{i}^{N_{int}^{e}}w_{i}j\\left(\\mathbf{\\xi}_{i}\\right)\\delta\\boldsymbol{\\Phi}\\cdot\\mathbf{k}^{c}\\Delta\\boldsymbol{\\Phi}\\,,\\label{eq585}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\boldsymbol{\\Phi}$\n\\end_inset\n\n is as above and \n\\begin_inset Formula $\\Delta\\boldsymbol{\\Phi}$\n\\end_inset\n\n similar to \n\\begin_inset Formula $\\delta\\boldsymbol{\\Phi}$\n\\end_inset\n\n with \n\\begin_inset Formula $\\delta$\n\\end_inset\n\n replaced with \n\\begin_inset Formula $\\Delta$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{k}^{c}$\n\\end_inset\n\n is \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}^{c} & =\\varepsilon_{N}H\\left(\\lambda_{N}^{k}+\\varepsilon_{N}g\\right)\\mathbf{NN}^{T}+t_{N}\\left\\{ g\\left[m^{11}\\mathbf{\\bar{N}}_{1}\\mathbf{\\bar{N}}_{1}^{T}\\right.\\right.\\\\\n & +m^{12}\\left(\\mathbf{\\bar{N}}_{1}\\mathbf{\\bar{N}}_{2}^{T}+\\mathbf{\\bar{N}}_{2}\\mathbf{\\bar{N}}_{1}^{T}\\right)+\\left.m^{22}\\mathbf{\\bar{N}}_{2}\\mathbf{\\bar{N}}_{2}^{T}\\right]-\\mathbf{D}_{1}\\mathbf{N}_{1}^{T}\\\\\n & -\\mathbf{D}_{2}\\mathbf{N}_{2}^{T}-\\mathbf{N}_{1}\\mathbf{D}_{1}^{T}-\\mathbf{N}_{2}\\mathbf{D}_{2}^{T}+\\left.\\kappa_{12}\\left(\\mathbf{D}_{1}\\mathbf{D}_{2}^{T}+\\mathbf{D}_{2}\\mathbf{D}_{1}^{T}\\right)\\right\\} \n\\end{aligned}\n\\,,\\label{eq586}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{N}= & \\left[\\begin{array}{c}\n\\boldsymbol{\\nu}\\\\\n-N_{1}\\left(\\bar{\\boldsymbol{\\xi}}\\right)\\boldsymbol{\\nu}\\\\\n\\vdots\\\\\n-N_{4}\\left(\\bar{\\boldsymbol{\\xi}}\\right)\\boldsymbol{\\nu}\n\\end{array}\\right]\\,, & \\mathbf{T}_{\\alpha} & =\\left[\\begin{array}{c}\n\\boldsymbol{\\tau}_{\\alpha}\\\\\n-N_{1}\\left(\\bar{\\boldsymbol{\\xi}}\\right)\\boldsymbol{\\tau}_{\\alpha}\\\\\n\\vdots\\\\\n-N_{4}\\left(\\bar{\\boldsymbol{\\xi}}\\right)\\boldsymbol{\\tau}_{\\alpha}\n\\end{array}\\right]\\,, & \\mathbf{N}_{\\alpha} & =\\left[\\begin{array}{c}\n\\mathbf{0}\\\\\n-N_{1,\\alpha}\\left(\\bar{\\boldsymbol{\\xi}}\\right)\\boldsymbol{\\nu}\\\\\n\\vdots\\\\\n-N_{4,\\alpha}\\left(\\bar{\\boldsymbol{\\xi}}\\right)\\boldsymbol{\\nu}\n\\end{array}\\right]\\,.\\end{aligned}\n\\label{eq587}\n\\end{equation}\n\n\\end_inset\n\nThe following vectors are also defined which depend on the vectors of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq587\"\nnolink \"false\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{D}_{1} & =\\frac{1}{\\det\\mathbf{A}}\\left[A_{22}\\left(\\mathbf{T}_{1}+g\\mathbf{N}_{1}\\right)-A_{12}\\left(\\mathbf{T}_{2}+g\\mathbf{N}_{2}\\right)\\right]\\\\\n\\mathbf{D}_{2} & =\\frac{1}{\\det\\mathbf{A}}\\left[A_{11}\\left(\\mathbf{T}_{2}+g\\mathbf{N}_{2}\\right)-A_{12}\\left(\\mathbf{T}_{1}+g\\mathbf{N}_{1}\\right)\\right]\\\\\n\\mathbf{\\bar{N}}_{1} & =\\mathbf{N}_{1}-\\kappa_{12}\\mathbf{D}_{2}\\\\\n\\mathbf{\\bar{N}}_{2} & =\\mathbf{N}_{2}-\\kappa_{12}\\mathbf{D}_{1}\n\\end{aligned}\n\\,,\\label{eq588}\n\\end{equation}\n\n\\end_inset\n\nwhere the matrix \n\\series bold\n\n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n\n\\series default\n is defined as,\n \n\\begin_inset Formula \n\\begin{equation}\nA_{ij}=m_{ij}+g\\kappa_{ij}\\,.\\label{eq589}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $m_{ij}=\\boldsymbol{\\tau}_{i}\\cdot\\boldsymbol{\\tau}_{j}$\n\\end_inset\n\n is the surface metric tensor and \n\\begin_inset Formula $\\kappa_{ij}=\\boldsymbol{\\nu}\\cdot\\boldsymbol{\\varphi}_{t,ij}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\right)$\n\\end_inset\n\n denotes the components of the surface curvature at \n\\begin_inset Formula $\\bar{\\boldsymbol{\\xi}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nAugmented Lagrangian Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Augmented-Lagrangian-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe augmented Lagrangian method is used in FEBio to enforce the contact constraints to a user-specified tolerance.\n This implies that the normal contact tractions are given by,\n \n\\begin_inset Formula \n\\begin{equation}\nt_{N}=\\left\\langle \\lambda_{N}+\\varepsilon_{N}g\\right\\rangle \\,.\\label{eq590}\n\\end{equation}\n\n\\end_inset\n\nNote that this assumption is consistent with the approach that was used in establishing the discretization of the linearization of the contact integral \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq586\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq590\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset Formula $\\varepsilon_{N}$\n\\end_inset\n\n is a penalty factor that is chosen arbitrarily.\n\\end_layout\n\n\\begin_layout Standard\nThe Newton-Raphson iterative method is now used to solve the nonlinear contact problem where Uzawa's method (REF) is employed to calculate the Lagrange multipliers \n\\begin_inset Formula $\\lambda_{N}$\n\\end_inset\n\n.\n This implies that the Lagrange multipliers are kept fixed during the Newton-Raphson iterations.\n After convergence the multipliers are updated and a new NR procedure is started.\n This procedure can be summarized by the following four steps.\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nInitialize \n\\series default\nthe augmented Lagrangian iteration counter \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n and the initial guesses for the multipliers:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\lambda_{N_{n+1}}^{\\left(0\\right)} & =\\lambda_{N_{n}}\\,,\\\\\nk & =0\\,.\n\\end{aligned}\n\\label{eq591}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nSolve\n\\series default\n for \n\\begin_inset Formula $\\mathbf{d}_{n+1}^{\\left(k\\right)}$\n\\end_inset\n\n,\n the solution vector corresponding to the fixed \n\\begin_inset Formula $k$\n\\end_inset\n\nth iterate for the multipliers,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{F}^{int}\\left(\\mathbf{d}_{n+1}^{\\left(k\\right)}\\right)+\\mathbf{F}^{c}\\left(\\mathbf{d}_{n+1}^{\\left(k\\right)}\\right)=\\mathbf{F}_{n+1}^{ext}\\,,\\label{eq592}\n\\end{equation}\n\n\\end_inset\n\nwhere the contact tractions used to compute \n\\begin_inset Formula $\\mathbf{F}^{c}$\n\\end_inset\n\n,\n the contact force,\n are governed by \n\\begin_inset Formula \n\\begin{equation}\nt_{N_{n+1}}^{\\left(k\\right)}=\\left\\langle \\lambda_{N_{n+1}}^{\\left(k\\right)}+\\varepsilon_{N}g_{n+1}^{k}\\right\\rangle \\,.\\label{eq593}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nUpdate\n\\series default\n the Lagrange multipliers and iteration counters:\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\lambda_{N_{n+1}}^{\\left(k+1\\right)} & =\\left\\langle \\lambda_{N_{n+1}}^{\\left(k\\right)}+\\varepsilon_{N}g_{n+1}^{\\left(k\\right)}\\right\\rangle \\,,\\\\\nk & =k+1\\,.\n\\end{aligned}\n\\label{eq594}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nReturn\n\\series default\n to the solution phase.\n \n\\end_layout\n\n\\begin_layout Standard\nSteps 2-4 of the above algorithm are generally repeated until all contact constraints are satisfied to a user-specified tolerance or little change in the solution vector from augmentation to augmentation is noted.\n\\end_layout\n\n\\begin_layout Subsection\nAutomatic Penalty Calculation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Automatic-Penalty-Calculation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe determination of the penalty factor \n\\begin_inset Formula $\\varepsilon_{N}$\n\\end_inset\n\n can be a difficult task,\n since a good value may depend on both material parameters and geometrical factors.\n In FEBio the value of this penalty factor can be determined automatically.\n In this case FEBio will calculate a penalty factor for each facet using the following formula.\n \n\\begin_inset Formula \n\\begin{equation}\n\\varepsilon_{i}=\\frac{f_{SI}E_{i}A_{i}}{V_{i}}\\,.\\label{eq595}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $E_{i}$\n\\end_inset\n\n is the effective Young's modulus along the facet normal,\n \n\\begin_inset Formula $A_{i}$\n\\end_inset\n\n the surface area of the facet,\n \n\\begin_inset Formula $V_{i}$\n\\end_inset\n\n the volume of the element to which this facet belongs and \n\\begin_inset Formula $f_{SI}$\n\\end_inset\n\n a user defined scale factor.\n The parameter \n\\begin_inset Formula $E_{i}$\n\\end_inset\n\n is evaluated from the elasticity tensor \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}$\n\\end_inset\n\n and the facet unit normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n according to \n\\begin_inset Formula \n\\begin{equation}\n\\frac{1}{E_{i}}=\\left(\\mathbf{n}\\otimes\\mathbf{n}\\right):\\boldsymbol{\\mathcal{C}}^{-1}:\\left(\\mathbf{n}\\otimes\\mathbf{n}\\right)\\,,\\label{eq596}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{C}}^{-1}$\n\\end_inset\n\n is the compliance tensor.\n\\end_layout\n\n\\begin_layout Subsection\nFacet-To-Facet Sliding\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Alternative-Formulations\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs of FEBio version 1.2,\n two alternative formulations for sliding contact are available.\n The first method,\n which is referred to as the \n\\emph on\nfacet-to-facet sliding\n\\emph default\n,\n is very similar to the formulation described above.\n It only differs in that it uses a Gaussian quadrature rule instead of nodal integration.\n Because of the more accurate integration rule,\n it was noted that this method in many situations was more stable and resulted in better convergence.\n\\end_layout\n\n\\begin_layout Subsection\nSliding-Elastic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Sliding-Elastic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis algorithm was presented in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n and differs considerably from the previous two.\n Consider a domain \n\\begin_inset Formula $b$\n\\end_inset\n\n consisting of two bodies \n\\begin_inset Formula $b^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $b^{(2)}$\n\\end_inset\n\n with respective boundaries \n\\begin_inset Formula $\\partial b^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\partial b^{(2)}$\n\\end_inset\n\n.\n The two bodies are in contact over portions of\n\\family roman\n\\series medium\n\\shape up\n\\size normal\n\\emph off\n\\bar no\n\\noun off\n\\color none\n \n\\begin_inset Formula $\\partial b^{(1)}$\n\\end_inset\n\n\n\\family default\n\\series default\n\\shape default\n\\size default\n\\emph default\n\\bar default\n\\noun default\n\\color inherit\n and \n\\begin_inset Formula $\\partial b^{(2)}$\n\\end_inset\n\n,\n respectively denoted \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n.\n The contribution of contact to the external virtual work may be written as\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum_{i=1}^{2}\\intop_{\\gamma^{(i)}}\\delta\\mathbf{v}^{(i)}\\cdot\\mathbf{t}^{(i)}\\,da^{(i)}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n is a virtual velocity,\n \n\\begin_inset Formula $\\mathbf{t}^{(i)}$\n\\end_inset\n\n represents the traction on \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $da^{(i)}$\n\\end_inset\n\n is an elemental area of \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n.\n In contact analyses,\n the tractions on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n are equal and opposite,\n \n\\begin_inset Formula $\\mathbf{t}^{(1)}=-\\mathbf{t}^{(2)}$\n\\end_inset\n\n,\n and the contact surfaces are shared,\n hence we may select one surface to perform the integration over.\n The virtual work arising from contact may be written as an integral over the primary surface \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n only,\n yielding\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\intop_{\\gamma^{(1)}}\\left(\\delta\\mathbf{v}^{(1)}-\\delta\\mathbf{v}^{(2)}\\right)\\cdot\\mathbf{t}^{(1)}\\,da^{(1)}\\label{eq:contact_int}\n\\end{equation}\n\n\\end_inset\n\neq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact_int\"\nnolink \"false\"\n\n\\end_inset\n\n is commonly referred to as the contact integral.\n\\end_layout\n\n\\begin_layout Standard\nTo evaluate the directional derivatives of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n along increments in the displacements \n\\begin_inset Formula $\\Delta\\mathbf{u}^{(i)}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n,\n as required for an iterative technique such as Newton's method,\n it is necessary to formulate the integration over an invariant domain so that the directional derivative may be brought inside the integral without concern for variations in the domain of integration.\n In our approach the integral is formulated over the invariant parametric space of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b,Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nEach surface \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n is expressed in parametric form using coordinates \n\\begin_inset Formula $\\eta_{(i)}^{\\alpha}$\n\\end_inset\n\n,\n and material points \n\\begin_inset Formula $X^{(i)}$\n\\end_inset\n\n are identified through their parametric coordinates.\n On each surface \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n,\n covariant basis vectors are given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{(i)}=\\frac{\\partial\\mathbf{x}^{(i)}}{\\partial\\eta_{(i)}^{\\alpha}}\\,.\\label{eq:basis_vectors}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{x}^{(i)}\\left(\\eta_{(i)}^{\\alpha},t\\right)$\n\\end_inset\n\n is the spatial representation of surface \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n as it deforms over time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n in terms of contravariant surface parametric coordinates \n\\begin_inset Formula $\\eta_{(i)}^{\\alpha}$\n\\end_inset\n\n.\n These covariant basis vectors are tangent to \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n,\n and it follows that the unit outward normal to \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{(i)}=\\frac{\\mathbf{g}_{1}^{(i)}\\times\\mathbf{g}_{2}^{(i)}}{\\left|\\mathbf{g}_{1}^{(i)}\\times\\mathbf{g}_{2}^{(i)}\\right|}\\,.\\label{eq:n}\n\\end{equation}\n\n\\end_inset\n\nFurthermore,\n the elemental area on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n is evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}da^{(i)} & =J_{\\eta}^{(i)}\\,d\\eta_{(i)}^{1}d\\eta_{(i)}^{2}\\,, & J_{\\eta}^{(i)} & =\\left|\\mathbf{g}_{1}^{(i)}\\times\\mathbf{g}_{2}^{(i)}\\right|\\,.\\end{aligned}\n\\label{eq:da_jeta}\n\\end{equation}\n\n\\end_inset\n\nTherefore the contact integral of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact_int\"\nnolink \"false\"\n\n\\end_inset\n\n may be placed into matrix form and rewritten as\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mathbf{t}^{(1)}\\\\\n-\\mathbf{t}^{(1)}\n\\end{bmatrix}\\,J_{\\eta}^{(1)}\\,d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\,,\\label{eq:contact-int-invariant}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Gamma_{\\eta}^{(1)}$\n\\end_inset\n\n represents the invariant parametric space of surface \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and integration is performed over points \n\\begin_inset Formula $X^{(1)}$\n\\end_inset\n\n with prescribed parametric coordinates \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\Gamma_{\\eta}^{(1)}$\n\\end_inset\n\n represents a material frame,\n it is possible to linearize \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n by applying the directional derivative operator directly to the integrand,\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\int_{\\Gamma_{\\eta}^{(1)}}D\\left(\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mathbf{t}^{(1)}\\\\\n-\\mathbf{t}^{(1)}\n\\end{bmatrix}\\,J_{\\eta}^{(1)}\\right)d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\,,\\label{eq:dgc}\n\\end{equation}\n\n\\end_inset\n\nwhere it is understood that for any function \n\\begin_inset Formula $f$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\nDf\\equiv\\sum_{i=1}^{2}Df\\left[\\Delta\\mathbf{u}^{(i)}\\right]\\,.\n\\end{equation}\n\n\\end_inset\n\nTo proceed with this linearization,\n it is necessary to formulate the kinematics of points on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n and provide expressions for the contact traction \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n that can differentiate between frictional stick and slip.\n\\end_layout\n\n\\begin_layout Subsubsection\nSlip Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Slip-Kinematics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDuring contact slip,\n \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n move relative to one another as their configurations evolve.\n Performing a contact analysis requires mapping points between these surfaces.\n For the spatial position \n\\begin_inset Formula $\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)$\n\\end_inset\n\n of each material point on the primary surface,\n we define the intersection point \n\\begin_inset Formula $\\mathbf{x}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)$\n\\end_inset\n\n on the secondary surface as the point intersected by a ray directed along the unit outward normal to the primary surface \n\\begin_inset Formula $\\mathbf{n}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}^{(2)}=\\mathbf{x}^{(1)}+g\\mathbf{n}^{(1)},\\label{eq:slip_x2}\n\\end{equation}\n\n\\end_inset\n\nwhere the gap function \n\\begin_inset Formula $g$\n\\end_inset\n\n is defined to be positive when the surfaces \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n are separated,\n and negative when they penetrate,\n\\begin_inset Formula \n\\begin{equation}\ng=\\left(\\mathbf{x}^{(2)}-\\mathbf{x}^{(1)}\\right)\\cdot\\mathbf{n}^{(1)}.\\label{eq:slip_gap}\n\\end{equation}\n\n\\end_inset\n\nThe ray intersects \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n at a spatial position \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n through which different material points \n\\begin_inset Formula $X^{(2)}$\n\\end_inset\n\n identified by parametric coordinates \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n may convect.\n Computationally,\n this ray intersection and contact detection is performed with an Octree method \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Foley96\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe projection approach of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nnolink \"false\"\n\n\\end_inset\n\n,\n described in our previous study \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"true\"\n\n\\end_inset\n\n and commonly termed \n\\emph on\nray-tracing\n\\emph default\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Poulios15\"\nliteral \"true\"\n\n\\end_inset\n\n,\n can be characterized as an inverse projection relative to the classical contact mechanics approach used for NTS contact \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen02,Wriggers06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Although the definition of this projection and its associated gap function is not new,\n it has typically been employed mostly for mortar contact (e.g.\n as in Tur et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Tur09\"\nliteral \"true\"\n\n\\end_inset\n\n).\n The benefits associated with a projection method such as eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nnolink \"false\"\n\n\\end_inset\n\n for non-mortar contact have been developed in detail \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Poulios15\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Here it suffices to note that avoiding a reliance on secondary surface normal vectors eliminates many contact-searching difficulties that plague NTS algorithms \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zavarise09\"\nliteral \"true\"\n\n\\end_inset\n\n,\n and also serves to greatly reduce the complexity of the linearizations and the resulting stiffness matrices.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nStick Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Stick-Kinematics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEquations \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_gap\"\nnolink \"false\"\n\n\\end_inset\n\n describe searching for evolving contact and are only valid during slip.\n In stick no relative motion occurs between contacting points,\n thus the intersection point \n\\begin_inset Formula $X^{\\left(2\\right)}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n does not evolve during the iterative solution process,\n allowing the development of kinematics of sticking contact.\n Implicit in this condition is the assumption that the contact projection was previously resolved,\n and thus contact searching is not performed again;\n rather,\n the contact point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n is given by the parametric coordinates of intersection \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n found from the previous time point,\n which will be denoted as \n\\begin_inset Formula $\\eta_{(2)p}^{\\alpha}$\n\\end_inset\n\n,\n with the subscripted \n\\begin_inset Formula $p$\n\\end_inset\n\n referring to the previous time.\n Thus,\n we write the spatial position of \n\\begin_inset Formula $X^{\\left(2\\right)}$\n\\end_inset\n\n in stick as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}_{s}^{(2)}=\\mathbf{x}^{(2)}\\left(\\eta_{(2)p}^{\\alpha},t\\right)=\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)+\\mathbf{g}_{s}\\,,\\label{eq:stick_x2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n is a vectorial gap function,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{s}=\\mathbf{x}_{s}^{(2)}-\\mathbf{x}^{(1)}\\,.\\label{eq:stick_gap}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n is the vectorial distance,\n at the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n between material points which were in contact at the previous time step;\n for perfect stick we must have \n\\begin_inset Formula $\\mathbf{g}_{s}=\\mathbf{0}$\n\\end_inset\n\n.\n In a finite element implementation however,\n it is important to note that the intersection point \n\\begin_inset Formula $\\mathbf{x}_{s}^{(2)}$\n\\end_inset\n\n is not in general the point that would be found from a ray directed from \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n along the unit outward normal \n\\begin_inset Formula $\\mathbf{n}^{(1)}$\n\\end_inset\n\n.\n This is because stick will not be enforced exactly,\n so the points \n\\begin_inset Formula $\\mathbf{x}^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{x}_{s}^{(2)}$\n\\end_inset\n\n will separate slightly when using a penalty method for enforcing this constraint.\n How to minimize this separation and enforce perfect stick behavior is the subject of the forthcoming sections.\n\\end_layout\n\n\\begin_layout Subsubsection\nVelocities\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Velocities\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe kinematics developed above can be used to formulate velocities.\n The formulation for stick developed in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Coulomb-Friction\"\nnolink \"false\"\n\n\\end_inset\n\n does not rely on velocity constraints,\n therefore we are only concerned with velocities of opposing contact points in slip.\n The development of velocities presented below anticipates the need for a relative velocity as required by Coulomb's law of kinetic friction,\n which aligns the friction force with this slip direction.\n \n\\end_layout\n\n\\begin_layout Standard\nSince the parametric coordinates \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n of integration points represent material points on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n,\n the velocity \n\\begin_inset Formula $\\mathbf{v}^{\\left(1\\right)}$\n\\end_inset\n\n of these points on the primary surface is evaluated from the material time derivative in the material frame,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)=\\frac{\\partial\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)}{\\partial t}\\,.\\label{eq:v1}\n\\end{equation}\n\n\\end_inset\n\nIn contrast,\n since material may convect through the intersection point of the ray with \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n,\n the total velocity \n\\begin_inset Formula $\\mathbf{v}^{(2)}$\n\\end_inset\n\n at the intersection point on \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n needs to be evaluated using the material time derivative in the spatial frame,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)=\\frac{\\partial\\mathbf{x}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)}{\\partial t}+\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}\\,.\\label{eq:both_v2}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\partial\\mathbf{x}^{(2)}/\\partial t$\n\\end_inset\n\n represents the velocity of the intersection point on \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n,\n whereas \n\\begin_inset Formula $\\dot{\\eta}_{(2)}^{\\alpha}$\n\\end_inset\n\n are the contravariant components of the convective velocity of material passing through this intersection point.\n In effect,\n \n\\begin_inset Formula $\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}$\n\\end_inset\n\n represents the relative (slip) velocity between the material on \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n and that on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n.\n Importantly,\n as noted below when performing time discretization and linearization,\n by definition \n\\begin_inset Formula $\\partial\\mathbf{x}^{(2)}/\\partial t$\n\\end_inset\n\n is evaluated while keeping \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n constant.\n\\end_layout\n\n\\begin_layout Standard\nWe now use these relations to produce a more practical formulation of the slip velocity for our frictional contact implementation.\n Taking the material time derivative of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nnolink \"false\"\n\n\\end_inset\n\n and using the contact persistency condition \n\\begin_inset Formula $\\dot{g}=0$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen93\"\nliteral \"true\"\n\n\\end_inset\n\n produces \n\\begin_inset Formula $\\mathbf{v}^{\\left(2\\right)}=\\mathbf{v}^{\\left(1\\right)}+g\\dot{\\mathbf{n}}^{\\left(1\\right)}$\n\\end_inset\n\n.\n Substituting Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:v1\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:both_v2\"\nnolink \"false\"\n\n\\end_inset\n\n into this expression yields a frame-invariant measure of relative velocity between \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Poulios15,Curnier95\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{r}\\equiv\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}=g\\dot{\\mathbf{n}}^{(1)}+\\frac{\\partial\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)}{\\partial t}-\\frac{\\partial\\mathbf{x}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)}{\\partial t},\\label{eq:vr}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dot{\\mathbf{n}}^{(1)}$\n\\end_inset\n\n is the material time derivative of \n\\begin_inset Formula $\\mathbf{n}^{(1)}$\n\\end_inset\n\n in the parametric material frame of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n,\n evaluated from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:n\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{n}}^{(1)}=\\mathbf{P}_{N}\\cdot\\frac{\\dot{\\mathbf{g}}_{1}^{(1)}\\times\\mathbf{g}_{2}^{(1)}+\\mathbf{g}_{1}^{(1)}\\times\\dot{\\mathbf{g}}_{2}^{(1)}}{J_{\\eta}^{(1)}}\\,.\\label{eq:n_dot}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\mathbf{g}}_{\\alpha}^{(1)}$\n\\end_inset\n\n denotes the material time derivative of \n\\begin_inset Formula $\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n in the material frame,\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{g}}_{\\alpha}^{(1)}\\left(\\eta_{\\left(1\\right)}^{\\beta},t\\right)=\\frac{\\partial\\mathbf{g}_{\\alpha}^{(1)}\\left(\\eta_{\\left(1\\right)}^{\\beta},t\\right)}{\\partial t}\\,,\n\\end{equation}\n\n\\end_inset\n\nand we define the tangential plane projection tensor \n\\begin_inset Formula $\\mathbf{P}_{N}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}_{N}=\\mathbf{I}-\\mathbf{n}^{(1)}\\otimes\\mathbf{n}^{(1)}\\,.\\label{eq:Pn}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPrevious authors have utilized \n\\begin_inset Formula $\\mathbf{v}^{r}=\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}$\n\\end_inset\n\n directly instead of the right-hand-side of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:vr\"\nnolink \"false\"\n\n\\end_inset\n\n;\n this expression requires the evaluation of time derivatives of parametric coordinates \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Wriggers90,Laursen93,Sauer15\"\nliteral \"true\"\n\n\\end_inset\n\n,\n which necessitates special integration algorithms to handle crossing element boundaries \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Saracibar97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The relative velocity measure on the right-hand-side of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:vr\"\nnolink \"false\"\n\n\\end_inset\n\n obviates the need for any such special treatment.\n In particular,\n the choice of \n\\begin_inset Formula $\\partial\\mathbf{x}^{(2)}/\\partial t$\n\\end_inset\n\n ensures that element boundaries will never be crossed when calculating this velocity,\n since it is evaluated while keeping \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n constant.\n\\end_layout\n\n\\begin_layout Standard\nThe tangential frictional traction in slip depends only on the tangential component of the relative velocity,\n therefore we may define the slip direction \n\\begin_inset Formula $\\mathbf{s}^{(1)}$\n\\end_inset\n\n as the unit vector of the projection \n\\begin_inset Formula $\\mathbf{P}_{N}\\cdot\\mathbf{v}^{r}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\mathbf{v}^{r}$\n\\end_inset\n\n onto the tangent plane of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{s}^{(1)}=\\frac{\\mathbf{P}_{N}\\cdot\\mathbf{v}^{r}}{\\left|\\mathbf{P}_{N}\\cdot\\mathbf{v}^{r}\\right|}\\,,\\label{eq:s1}\n\\end{equation}\n\n\\end_inset\n\nThese definitions of contact kinematics may now be used to formulate frictional contact.\n\\end_layout\n\n\\begin_layout Subsubsection\nCoulomb Frictional Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Coulomb-Friction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis work considers Coulomb frictional contact,\n with no distinction made between static and kinetic coefficients of friction.\n Although classical Coulomb friction is the most frequently adopted behavior,\n it should be noted that other constitutive equations,\n including micromechanically-inspired formulations which consider local phenomena,\n have been proposed as well \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Wriggers90,Wriggers06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n During frictional contact,\n the contact traction \n\\begin_inset Formula $\\mathbf{t}^{(i)}$\n\\end_inset\n\n on the opposing surfaces is determined by the sticking or slipping behavior.\n For Coulomb friction,\n the relationship between sticking and slipping is described by a slip criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n,\n where on the primary surface\n\\begin_inset Formula \n\\begin{equation}\n\\Phi=\\left|\\mathbf{t}_{T}^{(1)}\\right|-\\mu\\left|t_{n}\\right|\\,.\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $t_{n}=\\mathbf{t}^{(1)}\\cdot\\mathbf{n}^{(1)}$\n\\end_inset\n\n is the normal component of the contact traction (negative in compression),\n \n\\begin_inset Formula $\\mathbf{t}_{T}^{(1)}=\\mathbf{t}^{(1)}-t_{n}\\mathbf{n}^{(1)}$\n\\end_inset\n\n is the tangential component of the contact traction,\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the friction coefficient.\n The value of the slip criterion determines the stick-slip status,\n\\begin_inset Formula \n\\begin{equation}\n\\Phi\\begin{cases}\n<0 & \\text{sticking}\\\\\n=0 & \\text{slipping}\n\\end{cases}\\label{eq:slip-criterion}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAlgorithmically,\n stick and slip are typically based on a predictor-corrector approach derived from an analogy with elastoplasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Giannakopoulos89\"\nliteral \"true\"\n\n\\end_inset\n\n,\n leading to constitutive relations for the rate of the traction and thus requiring numerical integration \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Saracibar97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Variations of this approach have been utilized in differing forms \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Wriggers90,Laursen93,Sauer15\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nIn contrast,\n this presentation proposes to treat stick and slip separately,\n controlled by an exact return mapping based on the slip criterion.\n The return mapping defines a rule for correcting a calculated traction which exceeds the slip limit and is thus not permissible.\n Stick will be treated as a special case of a tied interface,\n whereas in slip the traction will be directly prescribed.\n The formulation of Coulomb frictional contact is presented for both penalty and augmented Lagrangian regularization schemes.\n\\end_layout\n\n\\begin_layout Subsubsection\nPenalty Scheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Penalty-Scheme\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDuring stick,\n no relative motion may occur between surfaces;\n thus,\n points on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n that were in contact at the previous time must remain in contact.\n The stick traction may be obtained by penalizing any relative motion between such points,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=\\varepsilon\\mathbf{g}_{s}\\,,\\label{eq:stick_traction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the penalty parameter and we have employed eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_gap\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nDuring slip,\n we first calculate the normal component of the contact traction by penalizing the normal gap of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_gap\"\nnolink \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nt_{n}=\\varepsilon g\\,.\\label{eq:slip_tn}\n\\end{equation}\n\n\\end_inset\n\nThe total traction vector is then directly obtained as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=t_{n}\\left(\\mathbf{n}^{(1)}+\\mu\\mathbf{s}^{(1)}\\right)\\,.\\label{eq:slip_traction}\n\\end{equation}\n\n\\end_inset\n\nHere we have achieved an exact expression for the tangential traction in slip and remark that since \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is strictly negative in contact,\n the frictional contact traction \n\\begin_inset Formula $\\mu t_{n}\\mathbf{s}^{(1)}$\n\\end_inset\n\n acting on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n is in the direction opposing the motion of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n relative to \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n A trial state and return map,\n presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Stick-Slip-Algorithm\"\nnolink \"false\"\n\n\\end_inset\n\n,\n is employed to differentiate between stick and slip.\n\\end_layout\n\n\\begin_layout Subsubsection\nAugmented Lagrangian Scheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Augmented-Lagrangian\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe augmented Lagrangian scheme employed in this study is first order and utilizes Uzawa's algorithm \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bertsekas82\"\nliteral \"true\"\n\n\\end_inset\n\n,\n where multipliers are updated outside of the Newton step,\n producing a double loop algorithm (see the texts by Laursen \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen02\"\nliteral \"true\"\n\n\\end_inset\n\n and Wriggers \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Wriggers06\"\nliteral \"true\"\n\n\\end_inset\n\n for a discussion of Uzawa's algorithm applied to frictional contact problems).\n Such an approach preserves the quadratic convergence of Newton's method near solution points.\n The presented scheme is a modification of the approach suggested by Simo and Laursen \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo92c\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nDuring stick,\n the traction is calculated by augmenting the vector gap \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=\\boldsymbol{\\lambda}_{s}+\\varepsilon\\mathbf{g}_{s}\\,,\\label{eq:AL_stick_traction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n is the vectorial Lagrange multiplier in stick.\n In slip,\n we first calculate the normal component of the contact traction by augmenting the normal gap \n\\begin_inset Formula $g$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nt_{n}=\\lambda_{n}+\\varepsilon g\\,,\\label{eq:AL_tn}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n is the normal Lagrange multiplier.\n The total traction vector in slip is then defined to be \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=t_{n}\\left(\\mathbf{n}^{(1)}+\\mu\\mathbf{s}^{(1)}\\right)\\,,\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL_tn\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In this approach the Lagrange multiplier \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n augments the traction \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n in stick,\n but in slip only the normal component of traction \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is augmented by \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n and the tangential traction is directly prescribed from the augmented normal component.\n This approach has the advantage of preserving an exact mapping to the proper tangential traction in slip,\n which is consistent with the augmented normal traction.\n As in the penalty case,\n a trial state and return map controlled by the slip criterion is employed to differentiate between stick and slip,\n presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Stick-Slip-Algorithm\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe Lagrange multipliers \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n are held constant during each Newton step.\n Outside of the Newton loop,\n in this study we propose a novel update scheme where one of these multipliers is considered active and is updated from the kinematic data (\n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n or \n\\begin_inset Formula $g$\n\\end_inset\n\n),\n and the other is considered passive and is derived from the active multiplier.\n The contact status is determined via eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-criterion\"\nnolink \"false\"\n\n\\end_inset\n\n.\n If the current status is stick (\n\\begin_inset Formula $\\Phi<0$\n\\end_inset\n\n),\n we update \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n (active) and derive \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n (passive),\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\boldsymbol{\\lambda}_{s} & \\leftarrow\\boldsymbol{\\lambda}_{s}+\\varepsilon\\mathbf{g}_{s}\\\\\n\\lambda_{n} & =\\boldsymbol{\\lambda}_{s}\\cdot\\mathbf{n}^{(1)}\n\\end{aligned}\n\\label{eq:AL-stick}\n\\end{equation}\n\n\\end_inset\n\nAlternatively,\n if the current contact status is slip (\n\\begin_inset Formula $\\Phi=0$\n\\end_inset\n\n),\n we update \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n (active) and derive \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n (passive),\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\lambda_{n} & \\leftarrow\\lambda_{n}+\\varepsilon g\\\\\n\\boldsymbol{\\lambda}_{s} & =\\lambda_{n}\\left(\\mathbf{n}^{(1)}+\\mu\\mathbf{s}^{(1)}\\right)\n\\end{aligned}\n\\label{eq:AL-slip}\n\\end{equation}\n\n\\end_inset\n\nAn active-passive strategy for the multipliers ensures consistency when the contact status switches between stick and slip and is made possible due to this formulation's use of a single penalty parameter \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n,\n in conjunction with an exact return mapping for slip.\n \n\\end_layout\n\n\\begin_layout Standard\nAugmentations proceed until a tolerance related to a convergence criterion is met.\n In this formulation,\n two separate convergence criteria and their associated tolerances are defined.\n The first criterion considers the relative change of the norms of the active Lagrange multipliers between successive iterations,\n where the associated unitless tolerance \n\\begin_inset Formula $P_{tol}$\n\\end_inset\n\n specifies the largest allowable change.\n Convergence is achieved when\n\\begin_inset Formula \n\\begin{equation}\n\\left|\\frac{L^{r}-L^{r-1}}{L^{r}}\\right|<P_{tol}\\,,\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $L^{r}$\n\\end_inset\n\n represents the total norm of the active multipliers across the contact surface at augmentation step \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n calculated by summing all the individual norms,\n\\begin_inset Formula \n\\begin{equation}\nL^{r}=\\sum_{e=1}^{n_{e}^{(1)}}\\sum_{k=1}^{n_{int}^{(e)}}l_{k,e}^{r}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $n_{e}^{(1)}$\n\\end_inset\n\n is the number of element faces on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $n_{int}^{(e)}$\n\\end_inset\n\n is the number of integration points on the \n\\begin_inset Formula $e\\text{th}$\n\\end_inset\n\n element face,\n and \n\\begin_inset Formula $l_{k,e}^{r}$\n\\end_inset\n\n is the norm of the active Lagrange multiplier at the \n\\begin_inset Formula $k\\text{th}$\n\\end_inset\n\n integration point on the \n\\begin_inset Formula $e\\text{th}$\n\\end_inset\n\n element of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n at augmentation step \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n defined by\n\\begin_inset Formula \n\\begin{equation}\nl_{k,e}^{r}=\\begin{cases}\n\\boldsymbol{\\lambda}_{s}^{r}\\cdot\\boldsymbol{\\lambda}_{s}^{r} & \\Phi<0,\\quad\\text{sticking}\\\\\n\\left(\\lambda_{n}^{r}\\right)^{2} & \\Phi=0,\\quad\\text{slipping}\n\\end{cases}\n\\end{equation}\n\n\\end_inset\n\nThe second criterion is a gap tolerance,\n where augmentations will continue until the magnitude of the gap is lower than the specified tolerance \n\\begin_inset Formula $G_{tol}$\n\\end_inset\n\n at every location.\n Convergence of the augmentations requires\n\\begin_inset Formula \n\\begin{equation}\n\\text{max}\\,\\left(\\left|\\mathbf{g}_{s}\\right|,\\left|g\\right|\\right)<G_{tol}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left|\\mathbf{g}_{s}\\right|$\n\\end_inset\n\n is associated with points currently sticking and \n\\begin_inset Formula $\\left|g\\right|$\n\\end_inset\n\n with those in slip.\n The tolerance \n\\begin_inset Formula $G_{tol}$\n\\end_inset\n\n has units of length,\n allowing enforcement of the non-penetration and stick constraints to arbitrarily small precision.\n\\end_layout\n\n\\begin_layout Subsubsection\nStick-Slip Algorithm\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Stick-Slip-Algorithm\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDetermination of whether stick or slip is active is accomplished by a trial state and return map,\n and follows the same procedure for both penalty and augmented Lagrangian regularizations.\n We begin by calculating a trial traction \n\\begin_inset Formula $\\tilde{\\mathbf{t}}^{(1)}$\n\\end_inset\n\n assuming stick,\n utilizing either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_traction\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL_stick_traction\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The trial normal and tangential components \n\\begin_inset Formula $\\tilde{t}_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\mathbf{t}}_{T}^{(1)}$\n\\end_inset\n\n are evaluated from \n\\begin_inset Formula $\\tilde{\\mathbf{t}}^{(1)}$\n\\end_inset\n\n and inserted into the slip criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\Phi=\\left|\\tilde{\\mathbf{t}}_{T}^{(1)}\\right|-\\mu\\left|\\tilde{t}_{n}\\right|.\n\\end{equation}\n\n\\end_inset\n\nBased on the slip criterion and trial traction vector,\n we perform a return mapping and obtain the traction vector as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=\\begin{cases}\n\\tilde{\\mathbf{t}}^{(1)} & \\Phi<0\\,,\\quad\\text{sticking}\\\\\nt_{n}(\\mathbf{n}^{(1)}+\\mu\\mathbf{s}^{(1)}) & \\Phi=0\\,,\\quad\\text{slipping}\n\\end{cases}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is given by either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_tn\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL_tn\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In the case of first contact,\n a trial stick traction cannot be calculated,\n as stick requires a previous intersection point.\n Consequently,\n first contact is treated as a case of slip in this framework.\n The alternative of treating first contact as frictionless is unsatisfying,\n as the lack of friction at the first iteration can lead to premature failure and thus precludes the modeling of certain problems that rely on frictional forces for stability (such as load-control analyses).\n After one iteration,\n the traction can be evaluated via the return map described above.\n\\end_layout\n\n\\begin_layout Standard\nComputationally,\n care must be taken to ensure that augmentation does not unnecessarily change the stick-slip status.\n For normal contact,\n the update \n\\begin_inset Formula $\\lambda_{n}\\leftarrow\\lambda_{n}+\\varepsilon g$\n\\end_inset\n\n will augment the normal traction until the non-penetration contact constraint is adequately satisfied,\n with no adverse consequence if \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n slightly overshoots the final target value during an intermediate augmentation.\n For tangential contact however,\n augmentation of the tangential traction that overshoots the final target value may cross the boundary between stick and slip,\n thus changing the nature of the solution.\n For example,\n at the first augmentation step,\n the stick traction is \n\\begin_inset Formula $\\mathbf{t}^{(1)}=\\varepsilon\\mathbf{g}_{s}$\n\\end_inset\n\n and the multiplier \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n is augmented from its initial zero value to \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}=\\varepsilon\\mathbf{g}_{s}$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-stick\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Thus,\n at the start of the next iteration,\n when \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n has not yet changed,\n the traction is calculated as \n\\begin_inset Formula $\\mathbf{t}^{(1)}=\\boldsymbol{\\lambda}_{s}+\\varepsilon\\mathbf{g}_{s}=2\\varepsilon\\mathbf{g}_{s}$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL_stick_traction\"\nnolink \"false\"\n\n\\end_inset\n\n,\n which essentially counts the gap function twice and can in some cases shift the contact from stick to slip.\n To circumvent potential error introduced by this step,\n our implementation freezes the stick-slip status until the completion of the first iteration following an augmentation step.\n After the first iteration,\n the gap function has been reduced by the augmentation and the traction is split appropriately between the remaining gap and the multiplier.\n In this way,\n the double-counting of the gap function does not unnecessarily shift the contact status,\n but augmentation is able to modify the stick-slip status if accurate enforcement of the contact constraints requires it.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo evaluate the linearization in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dgc\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n requires directional derivatives of kinematic quantities,\n some of which are dependent on the stick-slip status.\n In an attempt to simplify the presentation,\n the continuum linearization of only a few kinematic quantities is described below,\n and the majority of the linearization will be deferred until after the discretization presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:SE-Discretization\"\nnolink \"false\"\n\n\\end_inset\n\n.\n We note that,\n as a consequence of the double-loop Uzawa algorithm discussed in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Augmented-Lagrangian\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the Lagrange multipliers are updated outside of each Newton step and thus \n\\begin_inset Formula $D\\lambda_{n}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $D\\boldsymbol{\\lambda}_{s}=\\mathbf{0}$\n\\end_inset\n\n in the following linearizations.\n Furthermore,\n in forthcoming sections,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}^{(i)}$\n\\end_inset\n\n refers to an increment in displacement in the trial solution \n\\begin_inset Formula $\\boldsymbol{\\chi}$\n\\end_inset\n\n.\n All other expressions which employ \n\\begin_inset Formula $\\Delta$\n\\end_inset\n\n are slight abuses of notation,\n used to compactly denote changes in a quantity from the previous time step.\n \n\\end_layout\n\n\\begin_layout Paragraph\nStick\n\\end_layout\n\n\\begin_layout Standard\nParametric coordinates on the primary surface are always invariant since they represent material points \n\\begin_inset Formula $X^{(1)}$\n\\end_inset\n\n where the contact integral is to be evaluated (integration points),\n and in stick the parametric coordinates of contact on the secondary surface are similarly fixed by definition.\n Accordingly,\n directional derivatives of \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{x}^{(i)}$\n\\end_inset\n\n are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{(1)} & =\\Delta\\mathbf{u}^{(1)}\\,, & D\\mathbf{x}^{(2)} & =\\Delta\\mathbf{u}^{(2)}\\\\\nD\\delta\\mathbf{v}^{(1)} & =\\mathbf{0}\\,, & D\\delta\\mathbf{v}^{(2)} & =\\mathbf{0}\n\\end{aligned}\n\\label{eq:stick_xv_linearizations}\n\\end{equation}\n\n\\end_inset\n\nFrom the above expressions and Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:basis_vectors\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:da_jeta\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find directional derivatives of \n\\begin_inset Formula $\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $J_{\\eta}^{(1)}$\n\\end_inset\n\n to be\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{g}_{\\alpha}^{(1)} & =\\frac{\\partial\\Delta\\mathbf{u}^{(1)}}{\\partial\\eta_{(1)}^{\\alpha}}\\\\\nDJ_{\\eta}^{(1)} & =\\mathbf{n}^{(1)}\\cdot\\left(\\hat{\\mathbf{g}}_{1}^{(1)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{(1)}}{\\partial\\eta_{(1)}^{2}}-\\hat{\\mathbf{g}}_{2}^{(1)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{(1)}}{\\partial\\eta_{(1)}^{1}}\\right)\n\\end{aligned}\n\\label{eq:Dgcov_Djeta}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\mathbf{g}}_{\\alpha}^{(1)}$\n\\end_inset\n\n is a skew-symmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n;\n thus \n\\begin_inset Formula $\\hat{\\mathbf{g}}_{\\alpha}^{(1)}\\cdot\\mathbf{z}=\\mathbf{\\mathbf{g}}_{\\alpha}^{(1)}\\times\\mathbf{z}$\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{z}$\n\\end_inset\n\n.\n Given the definitions of Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_gap\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n along with the relations of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_xv_linearizations\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{t}^{(1)}=\\varepsilon\\left(\\Delta\\mathbf{u}^{(2)}-\\Delta\\mathbf{u}^{(1)}\\right)\\,.\\label{eq:Dt_stick}\n\\end{equation}\n\n\\end_inset\n\nThe linearization operator may be brought inside the contact integral of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to find\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\begin{aligned}D\\delta G_{c}= & \\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}1\\\\\n-1\n\\end{bmatrix}D\\mathbf{t}^{(1)}J_{\\eta}^{(1)}\\right.\\\\\n & \\left.+\\begin{bmatrix}\\mathbf{t}^{(1)}\\\\\n-\\mathbf{t}^{(1)}\n\\end{bmatrix}DJ_{\\eta}^{(1)}\\right)\\,d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\end{aligned}\n\\label{eq:stick-linearized-2}\n\\end{equation}\n\n\\end_inset\n\nwhere we recall that \n\\begin_inset Formula $D\\delta\\mathbf{v}^{(1)}=D\\delta\\mathbf{v}^{(2)}=\\mathbf{0}$\n\\end_inset\n\n from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_xv_linearizations\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Paragraph\nSlip\n\\end_layout\n\n\\begin_layout Standard\nAs in stick,\n the contact integral over \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n is performed over integration points of prescribed parametric coordinates \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n.\n However,\n the point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n in contact with \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n has parametric coordinates \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n that change with variations in \n\\begin_inset Formula $\\mathbf{x}^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}^{(1)}$\n\\end_inset\n\n,\n in accordance with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Thus,\n directional derivatives of \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{x}^{(i)}$\n\\end_inset\n\n are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{(1)} & =\\Delta\\mathbf{u}^{(1)}\\,, & D\\mathbf{x}^{(2)} & =\\Delta\\mathbf{u}^{(2)}+\\mathbf{g}_{\\alpha}^{(2)}D\\eta_{(2)}^{\\alpha}\\\\\nD\\delta\\mathbf{v}^{(1)} & =\\mathbf{0}\\,, & D\\delta\\mathbf{v}^{(2)} & =\\frac{\\partial\\delta\\mathbf{v}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}D\\eta_{(2)}^{\\alpha}\n\\end{aligned}\n\\label{eq:slip_xv_linearizations}\n\\end{equation}\n\n\\end_inset\n\nwhere we recall that \n\\begin_inset Formula $\\mathbf{x}^{(2)}$\n\\end_inset\n\n is the spatial location of the intersection point \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n at the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nWe evaluate \n\\begin_inset Formula $D\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n in terms of increments in solid displacements by means of our modification \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n of a method proposed by Laursen and Simo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen93\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Briefly,\n recognizing that \n\\begin_inset Formula $\\left(\\mathbf{x}^{(2)}-\\mathbf{x}^{(1)}\\right)\\cdot\\mathbf{g}_{\\alpha}^{(1)}=0$\n\\end_inset\n\n,\n the directional derivative of this expression is evaluated and the resulting linear system is inverted to yield\n\\begin_inset Formula \n\\begin{equation}\nD\\eta_{(2)}^{\\alpha}=\\left(\\Delta\\mathbf{u}^{(1)}-\\Delta\\mathbf{u}^{(2)}\\right)\\cdot\\bar{\\mathbf{g}}_{(2)}^{\\alpha}-a^{\\alpha\\beta}g\\mathbf{n}^{(1)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{(1)}}{\\partial\\eta_{(1)}^{\\beta}}\\label{eq:Deta}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $a^{\\alpha\\beta}=(A_{\\alpha\\beta})^{-1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $A_{\\alpha\\beta}=\\mathbf{g}_{\\alpha}^{(1)}\\cdot\\mathbf{g}_{\\beta}^{(2)}$\n\\end_inset\n\n,\n and\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\bar{\\mathbf{g}}_{(1)}^{\\beta}=a^{\\alpha\\beta}\\mathbf{g}_{\\alpha}^{(2)}, & \\quad\\bar{\\mathbf{g}}_{(2)}^{\\alpha}=a^{\\alpha\\beta}\\mathbf{g}_{\\beta}^{(1)}.\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\bar{\\mathbf{g}}_{(i)}^{\\alpha}$\n\\end_inset\n\n are approximate contravariant basis vectors on \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n.\n In the limit of perfect contact (\n\\begin_inset Formula $g\\to0$\n\\end_inset\n\n),\n \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n become true mating surfaces and a number of relations emerge,\n including \n\\begin_inset Formula $\\bar{\\mathbf{g}}_{(i)}^{\\alpha}\\to\\mathbf{g}_{(i)}^{\\alpha}$\n\\end_inset\n\n since \n\\begin_inset Formula $a^{\\alpha\\beta}\\to\\mathbf{g}_{(2)}^{\\alpha}\\cdot\\mathbf{g}_{(1)}^{\\beta}$\n\\end_inset\n\n (see the discussion following eq.(40) in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n).\n In the present formulation,\n this simplification to perfect contact is not adopted,\n as it was determined that retaining all terms provides better convergence and stability.\n\\end_layout\n\n\\begin_layout Standard\nFrom eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n it follows that \n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{t}^{(1)}=Dt_{n}\\left(\\mathbf{n}+\\mu\\mathbf{s}^{(1)}\\right)+t_{n}\\left(D\\mathbf{n}^{(1)}+\\mu D\\mathbf{s}^{(1)}\\right)\\,.\\label{eq:Dt_slip}\n\\end{equation}\n\n\\end_inset\n\nBy eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_tn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $Dt_{n}=\\varepsilon Dg$\n\\end_inset\n\n,\n and\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}Dg & =\\left(\\Delta\\mathbf{u}^{(2)}-\\Delta\\mathbf{u}^{(1)}+\\mathbf{g}_{\\alpha}^{(2)}D\\eta_{(2)}^{\\alpha}\\right)\\cdot\\mathbf{n}^{(1)}\\end{aligned}\n\\,.\\label{eq:Dg_slip}\n\\end{equation}\n\n\\end_inset\n\nApplying the linearization operator to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:s1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n yields\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{s}^{(1)}=\\frac{1}{\\left|\\mathbf{P}_{N}\\cdot\\mathbf{v}^{r}\\right|}(\\mathbf{I}-\\mathbf{s}^{(1)}\\otimes\\mathbf{s}^{(1)})\\cdot\\bigg(D\\mathbf{P}_{N}\\cdot\\mathbf{v}^{r}+\\mathbf{P}_{N}\\cdot D\\mathbf{v}^{r}\\bigg)\\label{eq:Ds}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $D\\mathbf{v}^{r}=Dg\\,\\dot{\\mathbf{n}}^{(1)}+g\\,D\\dot{\\mathbf{n}}^{(1)}+D\\left(\\partial\\mathbf{x}^{\\left(1\\right)}/\\partial t\\right)-D\\left(\\partial\\mathbf{x}^{\\left(2\\right)}/\\partial t\\right)$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:vr\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nTo linearize partial time derivatives of positions \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n,\n we adopt Euler integration and find \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}\\left(\\eta_{\\left(i\\right)}^{\\alpha},t\\right)}{\\partial t}\\approx\\frac{\\mathbf{x}^{\\left(i\\right)}\\left(\\eta_{\\left(i\\right)}^{\\alpha},t\\right)-\\mathbf{x}^{\\left(i\\right)}\\left(\\eta_{\\left(i\\right)}^{\\alpha},t-\\Delta t\\right)}{\\Delta t}=\\frac{\\Delta\\mathbf{x}^{\\left(i\\right)}}{\\Delta t}\\label{eq:Euler-integration}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Delta t$\n\\end_inset\n\n represents the time increment and \n\\begin_inset Formula $\\Delta\\mathbf{x}^{(i)}$\n\\end_inset\n\n denotes the change in \n\\begin_inset Formula $\\mathbf{x}^{(i)}$\n\\end_inset\n\n from the previous time step.\n Since \n\\begin_inset Formula $\\eta_{\\left(i\\right)}^{\\alpha}$\n\\end_inset\n\n is kept constant when evaluating the partial time derivative,\n the linearization of this expression reduces to \n\\begin_inset Formula \n\\begin{equation}\nD\\left(\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}\\left(\\eta_{\\left(i\\right)}^{\\alpha},t\\right)}{\\partial t}\\right)\\approx\\frac{\\Delta\\mathbf{u}^{(i)}}{\\Delta t}\\,.\\label{eq:Dx2_velocity}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n linearizing Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:n\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Pn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n produces\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{n}^{(1)} & =\\frac{1}{J_{\\eta}^{(1)}}\\mathbf{P}_{N}\\cdot\\left(\\hat{\\mathbf{g}}_{1}^{(1)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{(1)}}{\\partial\\eta_{(1)}^{2}}-\\hat{\\mathbf{g}}_{2}^{(1)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{(1)}}{\\partial\\eta_{(1)}^{1}}\\right)\\end{aligned}\n\\label{eq:Dn}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{P}_{N}=-\\left(D\\mathbf{n}^{(1)}\\otimes\\mathbf{n}^{(1)}+\\mathbf{n}^{(1)}\\otimes D\\mathbf{n}^{(1)}\\right)\\,.\\label{eq:DPn}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:SE-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the continuous variables on the primary and secondary surfaces be interpolated over each element face according to\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{(1)} & =\\sum_{a=1}^{m^{(1)}}N_{a}^{(1)}\\delta\\mathbf{v}_{a}^{(1)}\\,, & \\delta\\mathbf{v}^{(2)} & =\\sum_{b=1}^{m_{k}^{(2)}}N_{b}^{(2)}\\delta\\mathbf{v}_{b}^{(2)}\\\\\n\\Delta\\mathbf{u}^{(1)} & =\\sum_{c=1}^{m^{(1)}}N_{c}^{(1)}\\Delta\\mathbf{u}_{c}^{(1)}\\,, & \\Delta\\mathbf{u}^{(2)} & =\\sum_{d=1}^{m_{k}^{(2)}}N_{d}^{(2)}\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{aligned}\n\\label{eq:u-v-discretization}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}^{(i)}$\n\\end_inset\n\n represent interpolation functions on the element faces of \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $m^{(1)}$\n\\end_inset\n\n is the number of nodes and interpolation functions on each primary element face,\n \n\\begin_inset Formula $m_{k}^{(2)}$\n\\end_inset\n\n is the number of nodes and interpolation functions on the secondary element face which is intersected by the ray issued from the \n\\begin_inset Formula $k\\text{th}$\n\\end_inset\n\n integration point on the primary element face,\n and \n\\begin_inset Formula $\\delta\\mathbf{v}_{a}^{(i)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\mathbf{u}_{a}^{(i)}$\n\\end_inset\n\n represent respective nodal values of \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\mathbf{u}^{(i)}$\n\\end_inset\n\n.\n From this point forward,\n the summation signs will be written simply as \n\\begin_inset Formula $\\sum_{a}$\n\\end_inset\n\n,\n where it is assumed they have the same meaning described above.\n\\end_layout\n\n\\begin_layout Paragraph\nStick\n\\end_layout\n\n\\begin_layout Standard\nApplying the discretization to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the contact integral becomes\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{f}_{a}^{(1)}\\\\\n\\mathbf{f}_{b}^{(2)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\label{eq:contact-int-discretized}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{(1)} & =N_{a}^{(1)}\\mathbf{t}^{(1)}, & \\mathbf{f}_{b}^{(2)} & =-N_{b}^{(2)}\\mathbf{t}^{(1)}\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n is defined by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the penalty case and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL_stick_traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n when augmented Lagrangian regularization is used.\n Individual terms may now be discretized and placed into matrix notation,\n facilitating their substitution into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-linearized-2\"\nnolink \"false\"\n\n\\end_inset\n\n.\n A straightforward application of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:u-v-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dgcov_Djeta\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula $_{2}$\n\\end_inset\n\n yields\n\\begin_inset Formula \n\\begin{equation}\nDJ_{\\eta}^{(1)}=\\begin{bmatrix}\\sum_{c}J_{\\eta}^{(1)}\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)} & \\sum_{d}\\mathbf{0}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\\label{eq:Djeta-discretized}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{A}_{c}^{(1)}=\\frac{1}{J_{\\eta}^{(1)}}\\left(\\frac{\\partial N_{c}^{(1)}}{\\partial\\eta_{(1)}^{1}}\\hat{\\mathbf{g}}_{2}^{(1)}-\\frac{\\partial N_{c}^{(1)}}{\\partial\\eta_{(1)}^{2}}\\hat{\\mathbf{g}}_{1}^{(1)}\\right)\\label{eq:Ac}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)} & \\Delta\\mathbf{u}_{d}^{(2)}\\end{bmatrix}^{T}$\n\\end_inset\n\n is the vector of incremental changes in the degrees of freedom to the \n\\begin_inset Formula $c\\text{th}$\n\\end_inset\n\n node associated with the element face on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n and to the \n\\begin_inset Formula $d\\text{th}$\n\\end_inset\n\n node of the element face on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n associated with the current integration point on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n.\n Furthermore,\n discretizing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dt_stick\"\nnolink \"false\"\n\n\\end_inset\n\n produces\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{t}^{(1)}=\\begin{bmatrix}\\sum_{c}-\\varepsilon N_{c}^{(1)}\\mathbf{I} & \\sum_{d}\\varepsilon N_{d}^{(2)}\\mathbf{I}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\\,.\\label{eq:Dt-stick-discretized}\n\\end{equation}\n\n\\end_inset\n\nSubstituting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Djeta-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dt-stick-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-linearized-2\"\nnolink \"false\"\n\n\\end_inset\n\n and applying the discretization of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:u-v-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n yields the directional derivative of the virtual work in stick as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}= & \\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b}^{(2)}\\end{bmatrix}\\\\\n & \\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\begin{bmatrix}\\sum_{c}-N_{a}^{(1)}N_{c}^{(1)} & \\sum_{d}N_{a}^{(1)}N_{d}^{(2)}\\\\\n\\sum_{c}N_{b}^{(2)}N_{c}^{(1)} & \\sum_{d}-N_{b}^{(2)}N_{d}^{(2)}\n\\end{bmatrix}\\varepsilon\\mathbf{I}\\right.\\\\\n & \\left.+\\begin{bmatrix}\\sum_{c}-N_{a}^{(1)} & \\sum_{d}0\\\\\n\\sum_{c}N_{b}^{(2)} & \\sum_{d}0\n\\end{bmatrix}\\left(-\\mathbf{t}^{(1)}\\otimes\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)}\\right)\\right)\\\\\n & \\times J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\label{eq:stick-stiffness}\n\\end{equation}\n\n\\end_inset\n\n An examination of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n shows that the stiffness matrix associated with this contact formulation is not symmetric in stick.\n \n\\end_layout\n\n\\begin_layout Standard\nAlthough the stiffness matrix of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n can be cast in a more traditional form,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}= & \\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b}^{(2)}\\end{bmatrix}\\\\\n & \\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\sum_{c}\\mathbf{K}_{ac}^{(1,1)} & \\sum_{d}\\mathbf{K}_{ad}^{(1,2)}\\\\\n\\sum_{c}\\mathbf{K}_{bc}^{(2,1)} & \\sum_{d}\\mathbf{K}_{bd}^{(2,2)}\n\\end{bmatrix}\\,J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nas was done in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"true\"\n\n\\end_inset\n\n,\n splitting up like terms is a more natural way to implement the final equations,\n and provides some insight into the resulting matrix structure.\n A discussion of how to numerically evaluate the integrals in the above equations is deferred until Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Integration-Scheme\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Paragraph\nSlip\n\\end_layout\n\n\\begin_layout Standard\nIn the case of slip,\n the contact integral of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n can be split into normal and tangential parts,\n \n\\begin_inset Formula $\\delta G_{c}=\\delta G_{c}^{n}+\\delta G_{c}^{t}$\n\\end_inset\n\n,\n such that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c}= & \\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}t_{n}\\mathbf{n}^{(1)}\\\\\n-t_{n}\\mathbf{n}^{(1)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mu t_{n}\\mathbf{s}^{(1)}\\\\\n-\\mu t_{n}\\mathbf{s}^{(1)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:slip-contact-int-split}\n\\end{equation}\n\n\\end_inset\n\nDiscretizing this expression yields\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{f}_{a}^{(1)}\\\\\n\\mathbf{f}_{b}^{(2)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\label{eq:contact-int-discretized-2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{(1)} & =t_{n}N_{a}^{(1)}\\left(\\mathbf{n}^{(1)}+\\mu\\mathbf{s}^{(1)}\\right), & \\mathbf{f}_{b}^{(2)} & =-t_{n}N_{b}^{(2)}\\left(\\mathbf{n}^{(1)}+\\mu\\mathbf{s}^{(1)}\\right)\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_tn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the penalty formulation and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL_tn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n with augmented Lagrangian regularization.\n\\end_layout\n\n\\begin_layout Standard\nFor the following linearization and discretization the normal and tangential components,\n representing frictionless and frictional contributions to the contact integral,\n will be treated separately and may then be added together as in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-contact-int-split\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nFrictionless Terms\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Frictionless-Terms\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the frictionless contribution \n\\begin_inset Formula $\\delta G_{c}^{n}$\n\\end_inset\n\n makes use of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_xv_linearizations\"\nnolink \"false\"\n\n\\end_inset\n\n to find\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}^{n}= & \\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{0} & \\frac{\\partial\\delta\\mathbf{v}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\end{bmatrix}\\cdot\\begin{bmatrix}t_{n}\\mathbf{n}^{(1)}\\\\\n-t_{n}\\mathbf{n}^{(1)}\n\\end{bmatrix}D\\eta_{(2)}^{\\alpha}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}t_{n}\\mathbf{n}^{(1)}\\\\\n-t_{n}\\mathbf{n}^{(1)}\n\\end{bmatrix}DJ_{\\eta}^{(1)}\\right.\\\\\n & \\left.+\\begin{bmatrix}\\mathbf{n}^{(1)}\\\\\n-\\mathbf{n}^{(1)}\n\\end{bmatrix}Dt_{n}J_{\\eta}^{(1)}+\\begin{bmatrix}t_{n}\\\\\n-t_{n}\n\\end{bmatrix}D\\mathbf{n}^{(1)}J_{\\eta}^{(1)}\\right)\\,d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:frictionless-int-linearization}\n\\end{equation}\n\n\\end_inset\n\nEvaluating eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictionless-int-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n requires \n\\begin_inset Formula $D\\mathbf{n}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $Dt_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $D\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n.\n Directly inserting Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_xv_linearizations\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:u-v-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n into Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Deta\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dg_slip\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dn\"\nnolink \"false\"\n\n\\end_inset\n\n yields\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\eta_{(2)}^{\\alpha} & =\\begin{bmatrix}\\sum_{c}N_{c}^{(1)}\\bar{\\mathbf{g}}_{(2)}^{\\alpha}-ga^{\\alpha\\beta}\\frac{\\partial N_{c}^{(1)}}{\\partial\\eta_{(1)}^{\\beta}}\\mathbf{n}^{(1)} & \\sum_{d}-N_{d}^{(2)}\\bar{\\mathbf{g}}_{(2)}^{\\alpha}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\\\\\nDt_{n} & =\\begin{bmatrix}\\sum_{c}-\\varepsilon N_{c}^{(1)}\\bar{\\mathbf{N}}^{(1)}\\cdot\\mathbf{n}^{(1)}-t_{n}\\mathbf{N}^{(1)}\\cdot\\bar{\\mathbf{m}}_{c}^{(1)} & \\sum_{d}\\varepsilon N_{d}^{(2)}\\bar{\\mathbf{N}}^{(1)}\\cdot\\mathbf{n}^{(1)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\\\\\nD\\mathbf{n}^{(1)} & =\\begin{bmatrix}\\sum_{c}-\\mathbf{P}_{N}\\cdot\\mathbf{A}_{c}^{(1)} & \\sum_{d}\\mathbf{0}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\label{eq:Deta-Dtn-Dn-discretized}\n\\end{equation}\n\n\\end_inset\n\nwith the definitions\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{N}^{(1)} & =\\mathbf{n}^{(1)}\\otimes\\mathbf{n}^{(1)}, & \\bar{\\mathbf{N}}^{(1)} & =\\mathbf{I}-\\mathbf{g}_{\\beta}^{(1)}\\otimes\\bar{\\mathbf{g}}_{(1)}^{\\beta}\\\\\n\\bar{\\mathbf{m}}_{c}^{(1)} & =\\frac{\\partial N_{c}^{(1)}}{\\partial\\eta_{(1)}^{\\beta}}\\bar{\\mathbf{g}}_{(1)}^{\\beta}, & \\bar{\\mathbf{m}}_{b}^{(2)} & =\\frac{\\partial N_{b}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\bar{\\mathbf{g}}_{(2)}^{\\alpha}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nApplying the discretization in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:u-v-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictionless-int-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n and employing the linearizations of Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Djeta-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Deta-Dtn-Dn-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n to evaluate the resulting matrix products yields the directional derivative of \n\\begin_inset Formula $\\delta G_{c}^{n}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}^{n}= & \\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b}^{(2)}\\end{bmatrix}\\\\\n & \\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\begin{bmatrix}\\sum_{c}-N_{a}^{(1)}N_{c}^{(1)} & \\sum_{d}N_{a}^{(1)}N_{d}^{(2)}\\\\\n\\sum_{c}N_{b}^{(2)}N_{c}^{(1)} & \\sum_{d}-N_{b}^{(2)}N_{d}^{(2)}\n\\end{bmatrix}\\varepsilon\\tilde{\\mathbf{N}}^{(1)}\\right.\\\\\n & +\\begin{bmatrix}\\sum_{c}-N_{a}^{(1)} & \\sum_{d}0\\\\\n\\sum_{c}N_{b}^{(2)} & \\sum_{d}0\n\\end{bmatrix}t_{n}\\left(\\mathbf{A}_{c}^{(1)}+\\bar{\\mathbf{M}}_{c}^{(1)}\\cdot\\mathbf{N}^{(1)}\\right)\\\\\n & +\\begin{bmatrix}\\sum_{c}0 & \\sum_{d}0\\\\\n\\sum_{c}N_{c}^{(1)} & \\sum_{d}-N_{d}^{(2)}\n\\end{bmatrix}t_{n}\\bar{\\mathbf{M}}_{b}^{(2)}\\\\\n & \\left.+\\begin{bmatrix}\\sum_{c}0 & \\sum_{d}0\\\\\n\\sum_{c}G_{bc} & \\sum_{d}0\n\\end{bmatrix}t_{n}\\mathbf{N}^{(1)}\\right)J_{\\eta}^{(1)}\\,d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\label{eq:frictionless-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{\\mathbf{N}}^{(1)} & =\\mathbf{n}^{(1)}\\otimes\\bar{\\mathbf{N}}^{(1)}\\cdot\\mathbf{n}^{(1)}\\\\\n\\bar{\\mathbf{M}}_{c}^{(1)} & =\\mathbf{n}^{(1)}\\otimes\\bar{\\mathbf{m}}_{c}^{(1)}\\\\\n\\bar{\\mathbf{M}}_{b}^{(2)} & =-\\mathbf{n}^{(1)}\\otimes\\bar{\\mathbf{m}}_{b}^{(2)}\\\\\nG_{bc} & =ga^{\\alpha\\beta}\\frac{\\partial N_{b}}{\\partial\\eta_{(2)}^{\\alpha}}\\frac{\\partial N_{c}}{\\partial\\eta_{(1)}^{\\beta}}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand it is apparent that the resulting frictionless stiffness matrix is also nonsymmetric.\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictionless-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n is very similar to that which may be found by reducing our previous frictionless biphasic contact algorithm to the case of contact between two nonporous solids (eliminating all fluid degrees of freedom) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"true\"\n\n\\end_inset\n\n,\n although the present framework is more general,\n since that previous study evaluated expressions in the limit as \n\\begin_inset Formula $g\\to0$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nFrictional Terms\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Frictional-Terms\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the frictional contribution follows from the second term of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-contact-int-split\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}^{t}= & \\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{0} & \\frac{\\partial\\delta\\mathbf{v}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mu t_{n}\\mathbf{s}^{(1)}\\\\\n-\\mu t_{n}\\mathbf{s}^{(1)}\n\\end{bmatrix}D\\eta_{(2)}^{\\alpha}J_{\\eta}^{(1)}\\,d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}\\mu t_{n}\\mathbf{s}^{(1)}\\\\\n-\\mu t_{n}\\mathbf{s}^{(1)}\n\\end{bmatrix}DJ_{\\eta}^{(1)}\\right.\\\\\n & \\left.+\\begin{bmatrix}\\mu\\mathbf{s}^{(1)}\\\\\n-\\mu\\mathbf{s}^{(1)}\n\\end{bmatrix}Dt_{n}J_{\\eta}^{(1)}+\\begin{bmatrix}\\mu t_{n}\\\\\n-\\mu t_{n}\n\\end{bmatrix}D\\mathbf{s}^{(1)}J_{\\eta}^{(1)}\\right)\\,d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nThe remaining quantity to be determined in this expression is the linearization \n\\begin_inset Formula $D\\mathbf{s}^{(1)}$\n\\end_inset\n\n;\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Ds\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $D\\dot{\\mathbf{n}}^{(1)}$\n\\end_inset\n\n must also be evaluated.\n As \n\\begin_inset Formula $\\dot{\\mathbf{n}}^{(1)}$\n\\end_inset\n\n depends on \n\\begin_inset Formula $\\dot{\\mathbf{g}}_{\\alpha}^{(1)}$\n\\end_inset\n\n via eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:n_dot\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we must first propose a temporal discretization scheme for these rate quantities.\n \n\\end_layout\n\n\\begin_layout Standard\nIn a similar fashion to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Euler-integration\"\nnolink \"false\"\n\n\\end_inset\n\n,\n let the material time derivative in the material frame of the covariant basis vectors of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n be discretized as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{g}}_{\\alpha}^{(1)}\\approx\\frac{\\mathbf{g}_{\\alpha}^{(1)}\\left(\\eta_{(i)}^{\\beta},t\\right)-\\mathbf{g}_{\\alpha}^{(1)}\\left(\\eta_{(i)}^{\\beta},t-\\Delta t\\right)}{\\Delta t}=\\frac{\\Delta\\mathbf{g}_{\\alpha}^{(1)}}{\\Delta t}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Delta\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n denotes the change in \n\\begin_inset Formula $\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n from the previous time step.\n A temporally discretized form of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:n_dot\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n may now be written as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{n}}^{(1)}\\approx\\frac{1}{\\Delta t}\\mathbf{c}^{(1)}\\label{eq:ndot-euler}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{c}^{(1)} & =\\frac{1}{J_{\\eta}^{(1)}}\\mathbf{P}_{N}\\cdot\\mathbf{m}^{(1)}\\\\\n\\mathbf{m}^{(1)} & =\\Delta\\mathbf{g}_{1}^{(1)}\\times\\mathbf{g}_{2}^{(1)}+\\mathbf{g}_{1}^{(1)}\\times\\Delta\\mathbf{g}_{2}^{(1)}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nThe directional derivative of \n\\begin_inset Formula $\\dot{\\mathbf{n}}^{(1)}$\n\\end_inset\n\n may now be evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\dot{\\mathbf{n}}^{(1)}= & \\frac{1}{\\Delta t}\\begin{bmatrix}\\sum_{c}\\mathbf{Q}^{(1)}\\cdot\\mathbf{P}_{N}\\cdot\\mathbf{A}_{c}^{(1)}-\\mathbf{P}_{N}\\cdot\\left(\\mathbf{A}_{c}^{(1)}+\\bar{\\mathbf{A}}_{c}^{(1)}\\right)-\\mathbf{c}^{(1)}\\otimes\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)}\\\\\n\\sum_{d}\\mathbf{0}\n\\end{bmatrix}^{T}\\\\\n & \\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{Q}^{(1)}=\\frac{1}{J_{\\eta}^{(1)}}\\left(\\left(\\mathbf{n}^{(1)}\\cdot\\mathbf{m}^{(1)}\\right)\\mathbf{I}+\\mathbf{n}^{(1)}\\otimes\\mathbf{m}^{(1)}\\right)\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\bar{\\mathbf{A}}_{c}^{(1)}$\n\\end_inset\n\n is defined by analogy with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Ac\"\nnolink \"false\"\n\n\\end_inset\n\n to be\n\\begin_inset Formula \n\\begin{equation}\n\\bar{\\mathbf{A}}_{c}^{(1)}=\\frac{1}{J_{\\eta}^{(1)}}\\left(\\frac{\\partial N_{c}^{(1)}}{\\partial\\eta_{(1)}^{1}}\\Delta\\hat{\\mathbf{g}}_{2}^{(1)}-\\frac{\\partial N_{c}^{(1)}}{\\partial\\eta_{(1)}^{2}}\\Delta\\hat{\\mathbf{g}}_{1}^{(1)}\\right)\\,,\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Delta\\hat{\\mathbf{g}}_{\\alpha}^{(1)}$\n\\end_inset\n\n is a skew-symmetric tensor whose dual vector is \n\\begin_inset Formula $\\Delta\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nUtilizing the discrete-time equations Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Euler-integration\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:ndot-euler\"\nnolink \"false\"\n\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:vr\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{r}\\approx\\frac{\\mathbf{r}^{(1)}}{\\Delta t}=\\frac{g\\mathbf{c}^{(1)}+\\Delta\\mathbf{x}^{(1)}-\\Delta\\mathbf{x}^{(2)}}{\\Delta t}\n\\end{equation}\n\n\\end_inset\n\nand thus eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Ds\"\nnolink \"false\"\n\n\\end_inset\n\n may be discretized in time,\n \n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{s}^{(1)}=\\mathbf{P}_{S}\\cdot\\left(D\\mathbf{P}_{N}\\cdot\\mathbf{r}^{(1)}+\\mathbf{P}_{N}\\cdot D\\mathbf{r}^{(1)}\\right)\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $D\\mathbf{r}^{(1)}=Dg\\,\\mathbf{c}^{(1)}+g\\,D\\mathbf{c}^{(1)}+\\Delta\\mathbf{u}^{(1)}-\\Delta\\mathbf{u}^{(2)}$\n\\end_inset\n\n and\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\left|\\mathbf{P}_{N}\\cdot\\mathbf{v}^{r}\\right| & =\\frac{\\Delta h}{\\Delta t}\\\\\n\\mathbf{P}_{S} & =\\frac{1}{\\Delta h}\\left(\\mathbf{I}-\\mathbf{s}^{(1)}\\otimes\\mathbf{s}^{(1)}\\right)\n\\end{aligned}\n\\label{eq:delta-h}\n\\end{equation}\n\n\\end_inset\n\nIn eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:delta-h\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta h$\n\\end_inset\n\n is a spatial increment defined to simplify notation and \n\\begin_inset Formula $\\mathbf{P}_{S}$\n\\end_inset\n\n is a projection tensor.\n\\end_layout\n\n\\begin_layout Standard\nDefining the tensors\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{R}^{(1)} & =\\frac{1}{g}\\left(\\left(\\mathbf{n}^{(1)}\\cdot\\mathbf{r}^{(1)}\\right)\\mathbf{I}+\\mathbf{n}^{(1)}\\otimes\\mathbf{r}^{(1)}\\right)\\\\\n\\mathbf{B}^{(1)} & =\\mathbf{P}_{S}\\cdot\\mathbf{c}^{(1)}\\otimes\\bar{\\mathbf{N}}^{(1)}\\cdot\\mathbf{n}^{(1)}-\\mathbf{P}_{S}\\cdot\\mathbf{P}_{N}\\\\\n\\mathbf{L}^{(1)} & =g\\mathbf{P}_{S}\\cdot\\left(\\mathbf{P}_{N}\\cdot\\mathbf{Q}^{(1)}+\\mathbf{R}^{(1)}-\\mathbf{I}\\right)\\cdot\\mathbf{P}_{N}\\\\\n\\mathbf{J}_{c}^{(1)} & =\\mathbf{L}^{(1)}\\cdot\\mathbf{A}_{c}^{(1)}-g\\mathbf{P}_{S}\\cdot\\mathbf{P}_{N}\\cdot\\bar{\\mathbf{A}}_{c}^{(1)}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand vectors\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{h}_{c+}^{(1)} & =\\mathbf{N}^{(1)}\\cdot\\bar{\\mathbf{m}}_{c}^{(1)}+\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)}\\\\\n\\mathbf{h}_{c-}^{(1)} & =\\mathbf{N}^{(1)}\\cdot\\bar{\\mathbf{m}}_{c}^{(1)}-\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)}\n\\end{aligned}\n\\,,\n\\end{equation}\n\n\\end_inset\n\nthe directional derivative \n\\begin_inset Formula $D\\mathbf{s}^{(1)}$\n\\end_inset\n\n may be written fairly compactly as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{s}^{(1)}= & \\begin{bmatrix}\\sum_{c}-N_{c}^{(1)}\\mathbf{B}^{(1)}-g\\mathbf{P}_{S}\\cdot\\mathbf{c}^{(1)}\\otimes\\mathbf{h}_{c+}^{(1)}+\\mathbf{J}_{c}^{(1)} & \\sum_{d}N_{d}^{(2)}\\mathbf{B}^{(1)}\\end{bmatrix}\\\\\n & \\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nFinally,\n the tangential stiffness matrix is found to be\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}^{t}= & \\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b}^{(2)}\\end{bmatrix}\\\\\n & \\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\begin{bmatrix}\\sum_{c}-N_{a}^{(1)}N_{c}^{(1)} & \\sum_{d}N_{a}^{(1)}N_{d}^{(2)}\\\\\n\\sum_{c}N_{b}^{(2)}N_{c}^{(1)} & \\sum_{d}-N_{b}^{(2)}N_{d}^{(2)}\n\\end{bmatrix}\\mu\\left(\\varepsilon\\tilde{\\mathbf{S}}^{(1)}+t_{n}\\mathbf{B}^{(1)}\\right)\\right.\\\\\n & +\\begin{bmatrix}\\sum_{c}-N_{a}^{(1)} & \\sum_{d}0\\\\\n\\sum_{c}N_{b}^{(2)} & \\sum_{d}0\n\\end{bmatrix}\\mu t_{n}\\Big(\\mathbf{s}^{(1)}\\otimes\\mathbf{h}_{c-}^{(1)}+g\\mathbf{P}_{S}\\cdot\\mathbf{c}^{(1)}\\otimes\\mathbf{h}_{c+}^{(1)}-\\mathbf{J}_{c}^{(1)}\\Big)\\\\\n & +\\begin{bmatrix}\\sum_{c}0 & \\sum_{d}0\\\\\n\\sum_{c}N_{c}^{(1)} & \\sum_{d}-N_{d}^{(2)}\n\\end{bmatrix}\\mu t_{n}\\left(-\\mathbf{s}^{(1)}\\otimes\\bar{\\mathbf{m}}_{b}^{(2)}\\right)\\\\\n & \\left.+\\begin{bmatrix}\\sum_{c}0 & \\sum_{d}0\\\\\n\\sum_{c}G_{bc} & \\sum_{d}0\n\\end{bmatrix}\\mu t_{n}\\mathbf{S}^{(1)}\\right)\\,J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\label{eq:frictional-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{S}^{(1)} & =\\mathbf{s}^{(1)}\\otimes\\mathbf{n}^{(1)}\\\\\n\\tilde{\\mathbf{S}}^{(1)} & =\\mathbf{s}^{(1)}\\otimes\\bar{\\mathbf{N}}^{(1)}\\cdot\\mathbf{n}^{(1)}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nThe stiffness matrix of the frictional contribution to the virtual work,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictional-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n,\n is nonsymmetric.\n Summing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictionless-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictional-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n produces the total stiffness matrix for the case of frictional slip,\n which is also nonsymmetric.\n\\end_layout\n\n\\begin_layout Subsubsection\nIntegration Scheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SE-Integration-Scheme\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn this formulation,\n a Gaussian quadrature integration scheme is adopted.\n The general form of the contact integral (e.g.\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-discretized-2\"\nnolink \"false\"\n\n\\end_inset\n\n) may be integrated numerically as\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum_{e=1}^{n_{e}^{(1)}}\\sum_{k=1}^{n_{int}^{(e)}}W_{k}J_{\\eta}^{(1)}\\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b,k}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mathbf{f}_{a}^{(1)}\\\\\n\\mathbf{f}_{b,k}^{(2)}\n\\end{bmatrix}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $n_{e}^{(1)}$\n\\end_inset\n\n is the number of element faces on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $n_{int}^{(e)}$\n\\end_inset\n\n is the number of integration points on the \n\\begin_inset Formula $e\\text{th}$\n\\end_inset\n\n element face of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $W_{k}$\n\\end_inset\n\n is the weight associated with the \n\\begin_inset Formula $k\\text{th}$\n\\end_inset\n\n integration point,\n and where it should be understood that terms associated with \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n (such as \n\\begin_inset Formula $J_{\\eta}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\mathbf{v}_{a}^{(1)}$\n\\end_inset\n\n,\n etc.) are evaluated at the parametric coordinates \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n,\n associated with the \n\\begin_inset Formula $k\\text{th }$\n\\end_inset\n\n integration point,\n and terms associated with \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n (such as \n\\begin_inset Formula $\\mathbf{f}_{b,k}^{(2)}$\n\\end_inset\n\n) are evaluated at the parametric coordinates of contact \n\\begin_inset Formula $\\eta_{(2)p}^{\\alpha}$\n\\end_inset\n\n or \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n,\n defined by Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for cases of stick and slip,\n respectively.\n The subscript \n\\begin_inset Formula $k$\n\\end_inset\n\n appearing in the terms associated with \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n has been added to emphasize that there may be up to \n\\begin_inset Formula $n_{int}^{(e)}$\n\\end_inset\n\n distinct element faces on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n associated with all the integration points on the \n\\begin_inset Formula $e\\text{th}$\n\\end_inset\n\n element face of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n based on the location of the contact point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n as defined by either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick_x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n or \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip_x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nIn a similar fashion,\n the contact stiffness may be integrated numerically as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}= & \\sum_{e=1}^{n_{e}^{(1)}}\\sum_{k=1}^{n_{int}^{(e)}}W_{k}J_{\\eta}^{(1)}\\begin{bmatrix}\\sum_{a}\\delta\\mathbf{v}_{a}^{(1)} & \\sum_{b}\\delta\\mathbf{v}_{b,k}^{(2)}\\end{bmatrix}\\\\\n & \\cdot\\begin{bmatrix}\\sum_{c}\\mathbf{K}_{ac}^{(1,1)} & \\sum_{d}\\mathbf{K}_{ad,k}^{(1,2)}\\\\\n\\sum_{c}\\mathbf{K}_{bc,k}^{(2,1)} & \\sum_{d}\\mathbf{K}_{bd,k}^{(2,2)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d,k}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nwhere the matrix of tensors \n\\begin_inset Formula $\\mathbf{K}_{ab}^{(i,j)}$\n\\end_inset\n\n is a general representation of the stiffness terms given explicitly in either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n or Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictionless-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictional-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In this expression,\n \n\\begin_inset Formula $\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)} & \\Delta\\mathbf{u}_{d,k}^{(2)}\\end{bmatrix}^{T}$\n\\end_inset\n\n is the vector of incremental changes in the degrees of freedom of the \n\\begin_inset Formula $c\\text{th}$\n\\end_inset\n\n node of the \n\\begin_inset Formula $e\\text{th }$\n\\end_inset\n\n element face on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n and the \n\\begin_inset Formula $d\\text{th }$\n\\end_inset\n\n node of the element face on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n which contains the contact point \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n associated with the \n\\begin_inset Formula $k\\text{th }$\n\\end_inset\n\n integration point on the \n\\begin_inset Formula $e\\text{th}$\n\\end_inset\n\n element face of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n;\n the specific form of \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n will be dictated by the stick/slip status.\n A more detailed treatment of Gaussian quadrature,\n and a discussion of the benefits of this scheme versus nodal integration,\n may be found in our previous work \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nBiphasic Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nContact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Material\"\nnolink \"false\"\n\n\\end_inset\n\n for a review of biphasic materials,\n reference \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"true\"\n\n\\end_inset\n\n for additional details on biphasic frictionless contact,\n and reference \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman22\"\nliteral \"false\"\n\n\\end_inset\n\n for biphasic frictional contact.\n The presentation here follows that of \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman22\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The contact interface is defined between surfaces \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n Due to continuity requirements on the traction and fluxes,\n the external virtual work resulting from contact tractions \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}$\n\\end_inset\n\n and solvent fluxes \n\\begin_inset Formula $w_{n}^{\\left(i\\right)}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2)$\n\\end_inset\n\n,\n may be combined into the contact integral \n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\int_{\\gamma^{(1)}}\\left(\\left(\\delta\\mathbf{v}^{(1)}-\\delta\\mathbf{v}^{(2)}\\right)\\cdot\\mathbf{t}^{(1)}+\\left(\\delta p^{(1)}-\\delta p^{(2)}\\right)w_{n}\\right)da^{(1)}\\label{eq:contact-int}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n is the contact traction on the primary surface,\n which is equal and opposite to that on the secondary surface,\n \n\\begin_inset Formula $\\mathbf{t}^{(1)}=-\\mathbf{t}^{(2)}$\n\\end_inset\n\n;\n \n\\begin_inset Formula $w_{n}\\equiv w_{n}^{(1)}$\n\\end_inset\n\n is the outward normal component of the fluid flux \n\\begin_inset Formula $\\mathbf{w}^{(1)}$\n\\end_inset\n\n from the primary surface,\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n are virtual velocities,\n \n\\begin_inset Formula $\\delta p^{(i)}$\n\\end_inset\n\n are virtual fluid pressures,\n and \n\\begin_inset Formula $da^{(1)}$\n\\end_inset\n\n is an elemental area on the primary surface \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n.\n The contact integral is then written over the invariant parametric space of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n which is denoted by \n\\begin_inset Formula $\\Gamma_{\\eta}^{(1)}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"false\"\n\n\\end_inset\n\n,\n facilitating its linearization as required for use with an iterative solution method such as Newton's method.\n The elemental area is given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}da^{(1)} & =J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}, & J_{\\eta}^{(1)} & =\\left|\\mathbf{g}_{1}^{(1)}\\times\\mathbf{g}_{2}^{(1)}\\right|\\end{aligned}\n\\label{eq:da-jeta}\n\\end{equation}\n\n\\end_inset\n\nusing the covariant basis vectors\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{(i)}=\\frac{\\partial\\mathbf{x}^{(i)}}{\\partial\\eta_{(i)}^{\\alpha}}\\label{eq:basis-vectors}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{x}^{(i)}\\left(\\eta_{(i)}^{\\alpha},t\\right)$\n\\end_inset\n\n is the spatial representation of surface \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n as it deforms in time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n in terms of contravariant surface parametric coordinates \n\\begin_inset Formula $\\eta_{(i)}^{\\alpha}$\n\\end_inset\n\n.\n Casting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int\"\nnolink \"false\"\n\n\\end_inset\n\n into convenient matrix notation and switching the domain of integration to the parametric frame yields the invariant biphasic contact integral\n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta p^{(1)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mathbf{t}^{(1)}\\\\\nw_{n}\n\\end{bmatrix}+\\begin{bmatrix}\\delta\\mathbf{v}^{(2)} & \\delta p^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}-\\mathbf{t}^{(1)}\\\\\n-w_{n}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\label{eq:contact-int-invariant-1}\n\\end{equation}\n\n\\end_inset\n\nSince \n\\begin_inset Formula $\\Gamma_{\\eta}^{(1)}$\n\\end_inset\n\n represents an invariant material frame,\n the linearization of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n can be accomplished by applying the directional derivative operator directly to the integrand,\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\int_{\\Gamma_{\\eta}^{(1)}}D\\left(\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta p^{(1)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mathbf{t}^{(1)}\\\\\nw_{n}\n\\end{bmatrix}J_{\\eta}^{(1)}+\\begin{bmatrix}\\delta\\mathbf{v}^{(2)} & \\delta p^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}-\\mathbf{t}^{(1)}\\\\\n-w_{n}\n\\end{bmatrix}J_{\\eta}^{(1)}\\right)d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\label{eq:D-contact-int}\n\\end{equation}\n\n\\end_inset\n\nwhere it is understood that for any function \n\\begin_inset Formula $f$\n\\end_inset\n\n in this biphasic analysis,\n\\begin_inset Formula \n\\begin{equation}\nDf\\equiv\\sum_{i=1}^{2}Df\\left[\\Delta\\mathbf{u}^{(i)}\\right]+Df\\left[\\Delta p^{(i)}\\right]\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nBiphasic Friction Formulation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-Frictional-Formulation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAn examination of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reveals that a contact framework must provide expressions for both the normal fluid flux \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and the contact traction \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n.\n For the contact traction,\n this work implements a Coulomb-like framework for frictional contact \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In this framework the relationship between sticking and slipping,\n and thus the contact traction \n\\begin_inset Formula $\\mathbf{t}^{(i)}$\n\\end_inset\n\n on the opposing surfaces,\n is described by a slip criterion \n\\begin_inset Formula $\\Psi$\n\\end_inset\n\n,\n where on the primary surface\n\\begin_inset Formula \n\\begin{equation}\n\\Psi=\\left|\\mathbf{t}_{T}^{(1)}\\right|-\\mu_{\\text{eff}}\\left|t_{n}\\right|\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mu_{\\text{eff}}=\\mu_{\\text{eq}}\\left[1+\\left(1-\\varphi\\right)\\frac{p}{t_{n}}\\right]\\label{eq:mueff-biphasic}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $t_{n}=\\mathbf{t}^{(1)}\\cdot\\mathbf{n}^{(1)}$\n\\end_inset\n\n is the normal component of the contact traction (negative in compression),\n \n\\begin_inset Formula $\\mathbf{t}_{T}^{(1)}=\\mathbf{t}^{(1)}-t_{n}\\mathbf{n}^{(1)}$\n\\end_inset\n\n is the tangential component of the contact traction,\n and the effective friction coefficient \n\\begin_inset Formula $\\mu_{\\text{eff}}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mueff-biphasic\"\nnolink \"false\"\n\n\\end_inset\n\n with no distinction made between static and kinetic coefficients of friction.\n The user-defined parameter \n\\begin_inset Formula $\\mu_{\\text{eq}}$\n\\end_inset\n\n represents the friction coefficient when the fluid pressure has subsided (\n\\begin_inset Formula $p=0$\n\\end_inset\n\n),\n whereas the user-defined parameter \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n represents the fraction of the apparent contact area where solid-on-solid contact takes place \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian09b\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The ratio \n\\begin_inset Formula $-p/t_{n}$\n\\end_inset\n\n represents the local fluid load support at each point on the contact surface.\n Since the friction coefficient \n\\begin_inset Formula $\\mu_{\\text{eff}}$\n\\end_inset\n\n cannot be negative,\n the theoretical upper bound on the local fluid load support is \n\\begin_inset Formula $\\left.-p/t_{n}\\right|_{\\max}=\\left(1-\\varphi\\right)^{-1}$\n\\end_inset\n\n.\n This upper bound is enforced whenever numerical errors produce a greater value of the local fluid load support.\n\\end_layout\n\n\\begin_layout Standard\nThe value of the slip criterion determines the stick-slip status via\n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\begin{cases}\n<0 & \\text{sticking}\\\\\n=0 & \\text{slipping}\n\\end{cases}\\label{eq:slip-criterion-1}\n\\end{equation}\n\n\\end_inset\n\nFollowing our prior study \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n,\n this work treats stick and slip separately,\n controlled by an exact return mapping predicated on the value of the slip criterion.\n The return mapping defines a rule for correcting a calculated stick traction which exceeds the slip limit and is thus not permissible.\n Stick is treated as a special case of a tied biphasic interface (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Tied-Biphasic-Contact\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n whereas in slip the traction is directly prescribed as a natural boundary condition.\n The formulation of biphasic frictional contact is presented for both penalty and augmented Lagrangian regularization schemes.\n\\end_layout\n\n\\begin_layout Subsection\nContact Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSlip Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Slip-Kinematics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe kinematics of slip are developed by mapping points between the surfaces \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n as they move relative to one another.\n The relationship between spatial points \n\\begin_inset Formula $\\mathbf{x}^{(i)}\\left(\\eta_{(i)}^{\\alpha},t\\right)$\n\\end_inset\n\n on each surface is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}^{(2)}=\\mathbf{x}^{(1)}+g\\mathbf{n}^{(1)}\\label{eq:slip-x2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}^{(1)}$\n\\end_inset\n\n is the unit outward normal to \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{(1)}=\\frac{\\mathbf{g}_{1}^{(1)}\\times\\mathbf{g}_{2}^{(1)}}{\\left|\\mathbf{g}_{1}^{(1)}\\times\\mathbf{g}_{2}^{(1)}\\right|}\\label{eq:n-1}\n\\end{equation}\n\n\\end_inset\n\nand the gap function \n\\begin_inset Formula $g$\n\\end_inset\n\n is defined as\n\\begin_inset Formula \n\\begin{equation}\ng=\\left(\\mathbf{x}^{(2)}-\\mathbf{x}^{(1)}\\right)\\cdot\\mathbf{n}^{(1)}\\label{eq:slip-gap}\n\\end{equation}\n\n\\end_inset\n\nHere we note that \n\\begin_inset Formula $\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)$\n\\end_inset\n\n is the spatial position of a material point \n\\begin_inset Formula $X^{(1)}$\n\\end_inset\n\n on the primary surface,\n and \n\\begin_inset Formula $\\mathbf{x}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)$\n\\end_inset\n\n is the corresponding spatial intersection point on the secondary surface,\n through which different material points \n\\begin_inset Formula $X^{(2)}$\n\\end_inset\n\n identified by parametric coordinates \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n may convect.\n At any given instant,\n \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n are termed the parametric coordinates of intersection.\n\\end_layout\n\n\\begin_layout Subsubsection\nStick Kinematics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Stick-Kinematics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nImplicit in the concept of stick is the assumption that the contact projection was previously resolved;\n thus points are not mapped between surfaces during stick.\n Rather,\n the current contact point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n is given by the parametric coordinates of intersection \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n from the previous time point,\n now denoted as \n\\begin_inset Formula $\\eta_{(2)p}^{\\alpha}$\n\\end_inset\n\n with the subscripted \n\\begin_inset Formula $p$\n\\end_inset\n\n referring to the previous time.\n The spatial position of the material point \n\\begin_inset Formula $X^{(2)}$\n\\end_inset\n\n identified by \n\\begin_inset Formula $\\eta_{(2)p}^{\\alpha}$\n\\end_inset\n\n is then given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}_{s}^{(2)}=\\mathbf{x}^{(2)}\\left(\\eta_{(2)p}^{\\alpha},t\\right)=\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)+\\mathbf{g}_{s}\\label{eq:stick-x2}\n\\end{equation}\n\n\\end_inset\n\nwhere the vector gap \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n in stick is defined to be\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{s}=\\mathbf{x}_{s}^{(2)}-\\mathbf{x}^{(1)}\\label{eq:stick-gap}\n\\end{equation}\n\n\\end_inset\n\nHere \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n is the vectorial distance,\n at the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n between material points which were in contact at the previous time step;\n for perfect stick we must have \n\\begin_inset Formula $\\mathbf{g}_{s}=\\mathbf{0}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nVelocities\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Velocities\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nCoulomb's law of kinetic friction requires that the friction force be aligned with the relative slip velocity between the two surfaces.\n Despite the name,\n Coulomb's law is a constitutive relation,\n and hence must obey the Principle of Material-Frame Indifference;\n this requires a frame-invariant relative velocity.\n As points in stick do not experience relative motion,\n the development of velocities below is only concerned with opposing contact points in slip.\n\\end_layout\n\n\\begin_layout Standard\nAs parametric coordinates \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n of integration points on the primary surface \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n represent material points,\n the velocity \n\\begin_inset Formula $\\mathbf{v}^{(1)}$\n\\end_inset\n\n of these points is evaluated from the material time derivative in the material frame,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)=\\frac{\\partial\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)}{\\partial t}\\label{eq:v1-1}\n\\end{equation}\n\n\\end_inset\n\nIn contrast,\n different material points \n\\begin_inset Formula $X^{(2)}$\n\\end_inset\n\n may convect through the intersection point \n\\begin_inset Formula $\\mathbf{x}^{(2)}$\n\\end_inset\n\n,\n and so the velocity \n\\begin_inset Formula $\\mathbf{v}^{(2)}$\n\\end_inset\n\n at the intersection point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n is evaluated from the material time derivative in the spatial frame,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)=\\frac{\\partial\\mathbf{x}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)}{\\partial t}+\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}\\label{eq:v2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\partial\\mathbf{x}^{(2)}/\\partial t$\n\\end_inset\n\n represents the velocity of the intersection point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n,\n while \n\\begin_inset Formula $\\dot{\\eta}_{(2)}^{\\alpha}$\n\\end_inset\n\n are the contravariant components of the convective velocity of material passing through the intersection point \n\\begin_inset Formula $\\mathbf{x}^{(2)}$\n\\end_inset\n\n.\n The quantity \n\\begin_inset Formula $\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}$\n\\end_inset\n\n represents the relative slip velocity between the material on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n and that on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n.\n Importantly,\n by definition \n\\begin_inset Formula $\\partial\\mathbf{x}^{(2)}/\\partial t$\n\\end_inset\n\n is evaluated while holding \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n constant.\n From these relations,\n a more practical formulation of the slip velocity can be achieved \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Taking the material time derivative of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-x2\"\nnolink \"false\"\n\n\\end_inset\n\n and recalling the contact persistency condition \n\\begin_inset Formula $\\dot{g}=0$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Wriggers07\"\nliteral \"false\"\n\n\\end_inset\n\n produces \n\\begin_inset Formula $\\mathbf{v}^{(2)}=\\mathbf{v}^{(1)}+g\\dot{\\mathbf{n}}^{(1)}.$\n\\end_inset\n\n Substituting Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:v1-1\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:v2\"\nnolink \"false\"\n\n\\end_inset\n\n into this expression yields the desired frame-invariant measure of relative velocity between \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Curnier95\"\nliteral \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{r}\\equiv\\dot{\\eta}_{(2)}^{\\alpha}\\mathbf{g}_{\\alpha}^{(2)}=g\\dot{\\mathbf{n}}^{(1)}+\\frac{\\partial\\mathbf{x}^{(1)}\\left(\\eta_{(1)}^{\\alpha},t\\right)}{\\partial t}-\\frac{\\partial\\mathbf{x}^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)}{\\partial t},\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dot{\\mathbf{n}}^{(1)}$\n\\end_inset\n\n is evaluated from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:n-1\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{n}}^{(1)}=\\mathbf{P}_{N}\\cdot\\frac{\\dot{\\mathbf{g}}_{1}^{(1)}\\times\\mathbf{g}_{2}^{(1)}+\\mathbf{g}_{1}^{(1)}\\times\\dot{\\mathbf{g}}_{2}^{(1)}}{J_{\\eta}^{(1)}}\\,.\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\mathbf{g}}_{\\alpha}^{(1)}$\n\\end_inset\n\n is the material time derivative of \n\\begin_inset Formula $\\mathbf{g}_{\\alpha}^{(1)}$\n\\end_inset\n\n in the material frame,\n evaluated from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:basis-vectors\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{g}}_{\\alpha}^{(1)}\\left(\\eta_{\\left(1\\right)}^{\\beta},t\\right)=\\frac{\\partial\\mathbf{g}_{\\alpha}^{(1)}\\left(\\eta_{\\left(1\\right)}^{\\beta},t\\right)}{\\partial t}\\,,\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{P}_{N}$\n\\end_inset\n\n is the tangential plane projection tensor,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}_{N}=\\mathbf{I}-\\mathbf{n}^{(1)}\\otimes\\mathbf{n}^{(1)}\\,.\n\\end{equation}\n\n\\end_inset\n\nA unit vector in the slip direction can then be found by projecting the relative velocity \n\\begin_inset Formula $\\mathbf{v}^{r}$\n\\end_inset\n\n onto the tangent plane of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n yielding\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{s}^{(1)}=\\frac{\\mathbf{P}_{N}^{(1)}\\cdot\\mathbf{v}^{r}}{\\left|\\mathbf{P}_{N}^{(1)}\\cdot\\mathbf{v}^{r}\\right|}\\,.\\label{eq:s-def}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Scheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Penalty-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe defining characteristic of frictional stick is a lack of relative motion between points which were previously in contact.\n Consequently,\n the stick traction is obtained by penalizing relative motion between such points,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=\\varepsilon\\mathbf{g}_{s}\\,,\\label{eq:stick-traction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the contact penalty parameter and we have utilized eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-gap\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to define the gap vector \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n during stick.\n\\end_layout\n\n\\begin_layout Standard\nDuring slip,\n the normal component of the contact traction is first calculated by penalizing the normal component \n\\begin_inset Formula $g$\n\\end_inset\n\n of the gap,\n given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-gap\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nt_{n}=\\varepsilon g\\,.\\label{eq:slip-tn}\n\\end{equation}\n\n\\end_inset\n\nThe total traction vector in slip is then directly prescribed as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=t_{n}\\left(\\mathbf{n}^{(1)}+\\mu_{\\text{eff}}\\mathbf{s}^{(1)}\\right)\\,,\\label{eq:slip-traction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{s}^{(1)}$\n\\end_inset\n\n is the unit vector in the slip direction,\n given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:s-def\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n We note that eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-traction\"\nnolink \"false\"\n\n\\end_inset\n\n has the same form as the classical Coulomb friction considered in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Sliding-Elastic\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n,\n with the standard Coulomb friction coefficient replaced by \n\\begin_inset Formula $\\mu_{\\text{eff}}$\n\\end_inset\n\n,\n as evaluated from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mueff-biphasic\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n A trial state and return map,\n adapted from Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Stick-Slip-Algorithm\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n and presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SB-Stick-Slip-Algorithm\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n is employed to differentiate between stick and slip.\n\\end_layout\n\n\\begin_layout Standard\nThe jump conditions for a biphasic mixture require continuity of the fluid pressure across an interface and therefore an expression for the normal fluid flux \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n can be obtained by penalizing the fluid pressure gap \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n between contacting points \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}w_{n} & =\\varepsilon_{p}\\pi\\,, & t_{n}<0\\\\\np^{(i)} & =0\\,, & t_{n}=0\n\\end{aligned}\n\\label{eq:wn}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p^{(i)}=0$\n\\end_inset\n\n prescribes zero fluid pressure (free-draining conditions) on the portions of the boundary where no contact takes place.\n We define the fluid pressure gap as \n\\begin_inset Formula \n\\begin{equation}\n\\pi=p^{(1)}-p^{(2)}\\label{eq:pi-biphasic-penalty}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\varepsilon_{p}$\n\\end_inset\n\n is the pressure penalty parameter which has units of hydraulic permeability per unit length (e.g.\n m\n\\begin_inset Formula $^{3}$\n\\end_inset\n\n/N\n\\color black\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\color inherit\ns,\n similar to the hydraulic permeability of a membrane).\n Equation\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wn\"\nnolink \"false\"\n\n\\end_inset\n\n is valid for both stick and slip.\n Note that \n\\begin_inset Formula $p^{\\left(1\\right)}=p^{\\left(1\\right)}\\left(\\eta_{\\left(1\\right)}^{\\alpha}\\right)$\n\\end_inset\n\n is evaluated at a point with parametric coordinates \n\\begin_inset Formula $\\eta_{\\left(1\\right)}^{\\alpha}$\n\\end_inset\n\n on the primary surface,\n whereas the pressure on the secondary surface,\n \n\\begin_inset Formula $p^{(2)}=p^{(2)}\\left(\\eta_{(2)}^{\\alpha},t\\right)$\n\\end_inset\n\n,\n is evaluated at the parametric coordinates of the intersection of a ray issued from that primary surface point and normal to \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n.\n As detailed previously \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n and summarized in Section\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Biphasic-Gap-Function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the parametric coordinates of intersection are dependent on the stick-slip status,\n so care must be taken to evaluate \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wn\"\nnolink \"false\"\n\n\\end_inset\n\n using the contact kinematics determined by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-criterion-1\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In practice this is accomplished by calculating \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n once the stick-slip status has been resolved for each iteration.\n\\end_layout\n\n\\begin_layout Subsection\nAugmented Lagrangian Scheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Augmented-Lagrangian\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe augmented Lagrangian scheme presented herein was developed in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Augmented-Lagrangian\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n as a modification of the approach proposed by Simo and Laursen \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo92\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Briefly,\n this is a first-order augmentation scheme that utilizes Uzawa's algorithm \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bertsekas82\"\nliteral \"false\"\n\n\\end_inset\n\n,\n where the multipliers are updated outside of the Newton step,\n producing a double loop algorithm \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Wriggers07\"\nliteral \"false\"\n\n\\end_inset\n\n and preserving quadratic convergence of Newton's method near solution points.\n\\end_layout\n\n\\begin_layout Standard\nDuring stick,\n the traction is calculated by augmenting the vector gap \n\\begin_inset Formula $\\mathbf{g}_{s}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=\\boldsymbol{\\lambda}_{s}+\\varepsilon\\mathbf{g}_{s}\\,,\\label{eq:AL-stick-traction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n is the vectorial Lagrange multiplier in stick.\n In slip,\n the normal component of the contact traction is first calculated by augmenting the normal gap \n\\begin_inset Formula $g$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nt_{n}=\\lambda_{n}+\\varepsilon g\\,,\\label{eq:AL-tn}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n is the normal Lagrange multiplier.\n The total traction vector in slip is then directly prescribed as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=t_{n}\\left(\\mathbf{n}^{(1)}+\\mu_{\\text{eff}}\\mathbf{s}^{(1)}\\right)\\label{eq:augmented-t}\n\\end{equation}\n\n\\end_inset\n\nwhere eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-tn\"\nnolink \"false\"\n\n\\end_inset\n\n has been used.\n The update formulas for the Lagrange multipliers can be found in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Augmented-Lagrangian\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Here it suffices to note that by augmenting only the normal gap and employing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:augmented-t\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we have ensured an exact mapping to the proper tangential traction in slip,\n which is consistent with the augmented normal traction.\n As in the penalty case,\n a trial state and return map,\n presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SB-Stick-Slip-Algorithm\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and controlled by the slip criterion,\n is used to differentiate between stick and slip.\n\\end_layout\n\n\\begin_layout Standard\nIn this augmentation scheme,\n the normal fluid flux is given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}w_{n} & =\\lambda_{p}+\\varepsilon_{p}\\pi\\,, & t_{n}<0\\\\\np^{(i)} & =0\\,, & t_{n}=0\n\\end{aligned}\n\\label{eq:AL-wn}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{p}$\n\\end_inset\n\n is the fluid pressure Lagrange multiplier,\n and the update formula for \n\\begin_inset Formula $\\lambda_{p}$\n\\end_inset\n\n has been detailed in our prior work on frictionless biphasic contact \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"false\"\n\n\\end_inset\n\n and is given by\n\\begin_inset Formula \n\\begin{equation}\n\\lambda_{p}\\leftarrow\\lambda_{p}+\\varepsilon_{p}\\pi\n\\end{equation}\n\n\\end_inset\n\nUnlike \n\\begin_inset Formula $\\boldsymbol{\\lambda}_{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\lambda_{p}$\n\\end_inset\n\n is not dependent on the stick-slip status.\n However,\n as noted before,\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-wn\"\nnolink \"false\"\n\n\\end_inset\n\n must be evaluated after the stick-slip status has been resolved.\n \n\\end_layout\n\n\\begin_layout Subsection\nStick-Slip Algorithm\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Stick-Slip-Algorithm\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDetermining whether stick or slip is occurring at a given instant is accomplished by a trial state and return map,\n and has the same form for both penalty and augmented Lagrangian regularization schemes \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n We initially assume stick and calculate a trial traction \n\\begin_inset Formula $\\tilde{\\mathbf{t}}^{(1)}$\n\\end_inset\n\n,\n utilizing either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-traction\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-stick-traction\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The trial normal and tangential components \n\\begin_inset Formula $\\tilde{t}_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\mathbf{t}}_{T}^{(1)}$\n\\end_inset\n\n are evaluated from \n\\begin_inset Formula $\\tilde{\\mathbf{t}}^{(1)}$\n\\end_inset\n\n and inserted into the slip criterion \n\\begin_inset Formula $\\Psi$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\Psi=\\left|\\tilde{\\mathbf{t}}_{T}^{(1)}\\right|-\\mu_{\\text{eff}}\\left|\\tilde{t}_{n}\\right|.\n\\end{equation}\n\n\\end_inset\n\nBased on the slip criterion and trial traction vector,\n the traction vector is calculated from the return mapping as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{(1)}=\\begin{cases}\n\\tilde{\\mathbf{t}}^{(1)} & \\Psi<0\\,,\\quad\\text{sticking}\\\\\nt_{n}(\\mathbf{n}^{(1)}+\\mu_{\\text{eff}}\\mathbf{s}^{(1)}) & \\Psi=0\\,,\\quad\\text{slipping}\n\\end{cases}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is given by either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-tn\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-tn\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset FormulaMacro\n\\newcommand{\\mueff}{\\mu_{\\text{eff}}}\n{\\mu_{\\text{eff}}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\mueq}{\\mu_{\\text{eq}}}\n{\\mu_{\\text{eq}}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\mumin}{\\mu_{\\text{min}}}\n{\\mu_{\\text{min}}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\no}{\\mathbf{\\mathbf{n}}^{(1)}}\n{\\mathbf{n}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\so}{\\mathbf{s}^{(1)}}\n{\\text{\\mathbf{s}}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\jeta}{J_{\\eta}^{(1)}}\n{J_{\\eta}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\wn}{w_{n}^{(1)}}\n{w_{n}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\jn}{j_{n}^{(1)}}\n{j_{n}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Na}{N_{a}^{(1)}}\n{N_{a}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Nb}{N_{b}^{(2)}}\n{N_{b}^{(2)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Nc}{N_{c}^{(1)}}\n{N_{c}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Nd}{N_{d}^{(2)}}\n{N_{d}^{(2)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\mc}{\\bar{\\mathbf{m}}_{c}^{(1)}}\n{\\bar{\\mathbf{m}}_{c}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\mb}{\\bar{\\mathbf{m}}_{b}^{(2)}}\n{\\bar{\\mathbf{m}}_{b}^{(2)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Mb}{\\bar{\\mathbf{M}}_{b}^{(2)}}\n{\\bar{\\mathbf{M}}_{b}^{(2)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Mc}{\\bar{\\mathbf{M}}_{c}^{(1)}}\n{\\bar{\\mathbf{M}}_{c}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Ac}{\\mathbf{A}_{c}^{(1)}}\n{\\mathbf{A}_{c}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\No}{\\mathbf{N}^{(1)}}\n{\\mathbf{N}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Nbo}{\\mathbf{\\bar{\\mathbf{N}}}^{(1)}}\n{\\bar{\\mathbf{N}}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Nt}{\\tilde{\\mathbf{\\mathbf{N}}}^{(1)}}\n{\\tilde{\\mathbf{N}}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\So}{\\mathbf{\\mathbf{S}}^{(1)}}\n{\\mathbf{S}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Sb}{\\mathbf{\\bar{\\mathbf{S}}}^{(1)}}\n{\\bar{\\mathbf{S}}^{(1)}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Nh}{\\hat{\\mathbf{N}}^{(1)}}\n{\\hat{\\mathbf{N}}^{(1)}}\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nLinearization and Discretization Outline\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SB-Linearization-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following sections provide the linearization and discretization of stick and slip for biphasic frictional contact,\n culminating in the final forms of the residual vectors and stiffness matrices which have been implemented into FEBio.\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:SB-Definitions-Notation\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n begins with an important discussion of kinematic definitions and notation which is assumed to hold without repetition.\n Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Stick\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Slip\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n linearize and discretize biphasic stick and slip,\n respectively.\n Importantly,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Stick\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n sets up and uses more formal notation and definitions,\n before we dispense of this cumbersome notation for the remainder of the treatment.\n \n\\end_layout\n\n\\begin_layout Subsection\nDefinitions and Notation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:SB-Definitions-Notation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEvaluating the linearizations of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n requires directional derivatives of kinematic quantities,\n some of which depend on the stick-slip status.\n To simplify the presentation,\n the continuum linearization is presented only for a few select quantities.\n The remainder of the linearization is deferred until after discretization,\n as many expressions are much easier to manipulate in discretized form.\n To keep the equations more manageable,\n the discretization of slip is split into frictionless and frictional terms,\n and the final form of the stiffness matrices follows this split.\n The full model is easily obtained by summing the frictionless and frictional contributions.\n Here we emphasize that,\n as a consequence of the double-loop Uzawa algorithm discussed in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SB-Augmented-Lagrangian\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n (reprised from \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n),\n all Lagrange multipliers are updated outside of each Newton step,\n thus \n\\begin_inset Formula $D\\lambda=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is any Lagrange multiplier,\n i.e.\n \n\\begin_inset Formula $\\lambda=\\lambda_{n},\\boldsymbol{\\lambda}_{s},\\lambda_{p}$\n\\end_inset\n\n,\n etc.\n As a consequence,\n Lagrange multipliers do not appear in any of the linearized or discretized equations.\n In what follows,\n Greek indices are associated with covariant basis vectors on the contacting surfaces,\n and thus vary from 1 to 2.\n Repeated Greek indices indicate implicit summation over their range.\n\\end_layout\n\n\\begin_layout Standard\nMany kinematic quantities remain unchanged from Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Sliding-Elastic\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n on elastic frictional contact \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n We thus accept without repetition the definitions of \n\\begin_inset Formula $\\No$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Nbo$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mc$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mb$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Nt$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Mc$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Mb$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{c}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{m}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{Q}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\bar{\\mathbf{A}}_{c}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Ac$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{P}_{N}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{P}_{S},$\n\\end_inset\n\n \n\\begin_inset Formula $\\mathbf{R}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{B}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{L}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{J}_{c}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\So$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\tilde{\\mathbf{S}}^{(1)}$\n\\end_inset\n\n.\n These terms are all defined in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Frictionless-Terms\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Frictional-Terms\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Directional derivatives which will not be duplicated here include \n\\begin_inset Formula $D\\no$\n\\end_inset\n\n,\n \n\\begin_inset Formula $D\\dot{\\mathbf{n}}^{(1)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $D\\so$\n\\end_inset\n\n,\n \n\\begin_inset Formula $Dt_{n}=\\varepsilon Dg$\n\\end_inset\n\n,\n \n\\begin_inset Formula $Dg$\n\\end_inset\n\n,\n \n\\begin_inset Formula $D\\jeta$\n\\end_inset\n\n,\n \n\\begin_inset Formula $D\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $D\\mathbf{P}_{N}$\n\\end_inset\n\n (see Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Linearization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Frictionless-Terms\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Frictional-Terms\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe final residual vectors and stiffness matrices presented below are written in integral form.\n In the FEBio implementation,\n a Gaussian quadrature scheme is adopted to perform numerical integration.\n A detailed treatment of Gaussian quadrature,\n and equations for numerically integrating the contact integrals and stiffnesses,\n may be found in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Integration-Scheme\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nAs a final implementation detail,\n we note that multiplying all biphasic entries in the residual vector and stiffness matrices by the time step leads to better convergence.\n In this context,\n biphasic refers to all terms which are not purely related to solid-solid contact.\n In the residual,\n this is all terms which are not \n\\begin_inset Formula $\\mathbf{f}_{m}^{(i)}$\n\\end_inset\n\n (see below).\n Similarly,\n this includes all stiffness entries except the solid-solid \n\\begin_inset Formula $\\mathbf{K}_{mn}^{(i,j)}$\n\\end_inset\n\n terms (see below).\n\\end_layout\n\n\\begin_layout Subsection\nBiphasic Stick\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Stick\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biph-Stick-Linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn stick parametric coordinates are fixed on both surfaces,\n hence directional derivatives of \n\\begin_inset Formula $\\mathbf{x}^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $p^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\delta p^{(i)}$\n\\end_inset\n\n are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{(1)} & =\\Delta\\mathbf{u}^{(1)} & D\\mathbf{x}^{(2)} & =\\Delta\\mathbf{u}^{(2)}\\\\\nDp^{(1)} & =\\Delta p^{(1)} & Dp^{(2)} & =\\Delta p^{(2)}\\\\\nD\\delta\\mathbf{v}^{(1)} & =\\mathbf{0} & D\\delta\\mathbf{v}^{(2)} & =\\mathbf{0}\\\\\nD\\delta p^{(1)} & =0 & D\\delta p^{(2)} & =0\n\\end{aligned}\n\\label{eq:biphasic-stick-linearization}\n\\end{equation}\n\n\\end_inset\n\nFrom the definitions of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-gap\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and utilizing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-stick-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it follows that\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{t}^{(1)}=\\varepsilon\\left(\\Delta\\mathbf{u}^{(2)}-\\Delta\\mathbf{u}^{(1)}\\right)\\,.\\label{eq:biph-stick-Dt}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n application of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-stick-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n to Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:pi-biphasic-penalty\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n yields\n\\begin_inset Formula \n\\begin{equation}\nDw_{n}=\\varepsilon_{p}\\left(\\Delta p^{(1)}-\\Delta p^{(2)}\\right)\\label{eq:biph-stick-Dwn}\n\\end{equation}\n\n\\end_inset\n\nThe biphasic contact integral of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n written over an invariant domain,\n can then be linearized directly to find\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c}= & \\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}1\\\\\n-1\n\\end{bmatrix}D\\mathbf{t}^{(1)}J_{\\eta}^{(1)}+\\begin{bmatrix}\\mathbf{t}^{(1)}\\\\\n-\\mathbf{t}^{(1)}\n\\end{bmatrix}DJ_{\\eta}^{(1)}\\right)\\right.\\\\\n & \\left.+\\begin{bmatrix}\\delta p^{(1)} & \\delta p^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}1\\\\\n-1\n\\end{bmatrix}Dw_{n}J_{\\eta}^{(1)}+\\begin{bmatrix}w_{n}\\\\\n-w_{n}\n\\end{bmatrix}DJ_{\\eta}^{(1)}\\right)\\right)d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:biph-cont-int-linearized}\n\\end{equation}\n\n\\end_inset\n\nNote that for convenience,\n terms in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-cont-int-linearized\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n have been grouped differently than in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:D-contact-int\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biph-Stick-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the continuous variables on the primary and secondary surfaces be interpolated over element faces according to \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10,Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{(1)} & =\\sum_{a=1}^{m^{(1)}}N_{a}^{(1)}\\delta\\mathbf{v}_{a}^{(1)} & \\delta\\mathbf{v}^{(2)} & =\\sum_{b=1}^{m_{k}^{(2)}}N_{b}^{(2)}\\delta\\mathbf{v}_{b}^{(2)}\\\\\n\\Delta\\mathbf{u}^{(1)} & =\\sum_{c=1}^{m^{(1)}}N_{c}^{(1)}\\Delta\\mathbf{u}_{c}^{(1)} & \\Delta\\mathbf{u}^{(2)} & =\\sum_{d=1}^{m_{k}^{(2)}}N_{d}^{(2)}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\delta p^{(1)} & =\\sum_{a=1}^{m^{(1)}}N_{a}^{(1)}\\delta p_{a}^{(1)} & \\delta p^{(2)} & =\\sum_{b=1}^{m_{k}^{(2)}}N_{b}^{(2)}\\delta p_{b}^{(2)}\\\\\n\\Delta p^{(1)} & =\\sum_{c=1}^{m^{(1)}}N_{c}^{(1)}\\Delta p_{c}^{(1)} & \\Delta p^{(2)} & =\\sum_{d=1}^{m_{k}^{(2)}}N_{d}^{(2)}\\Delta p_{d}^{(2)}\n\\end{aligned}\n\\label{eq:biphasic-stick-var-discretization}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $N_{a}^{(i)}$\n\\end_inset\n\n represent interpolation functions on the element faces of \n\\begin_inset Formula $\\gamma^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $m^{(1)}$\n\\end_inset\n\n is the number of nodes and interpolation functions on each primary element face,\n \n\\begin_inset Formula $m_{k}^{(2)}$\n\\end_inset\n\n is the number of nodes and interpolation functions on the secondary element face which is intersected by the ray issued from the \n\\begin_inset Formula $k\\text{th}$\n\\end_inset\n\n integration point on the primary element face,\n and \n\\begin_inset Formula $\\delta\\mathbf{v}_{a}^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}_{a}^{(i)}$\n\\end_inset\n\n,\n\\begin_inset Formula $\\delta p_{a}^{(i)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Delta p_{a}^{(i)}$\n\\end_inset\n\n represent respective nodal values of \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n ,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta p^{(i)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Delta p^{(i)}$\n\\end_inset\n\n.\n From this point forward the summation signs will be written simply as \n\\begin_inset Formula $\\sum_{a}$\n\\end_inset\n\n,\n where it is assumed they have the same meaning described above.\n\\end_layout\n\n\\begin_layout Standard\nInserting the discretization of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-stick-var-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n yields\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum_{a}\\begin{bmatrix}\\delta\\mathbf{v}_{a}^{(1)} & \\delta p_{a}^{(1)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{f}_{a}^{(1)}\\\\\nw_{a}^{(1)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\sum_{b}\\begin{bmatrix}\\delta\\mathbf{v}_{b}^{(2)} & \\delta p_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{f}_{b}^{(2)}\\\\\nw_{b}^{(2)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:biph-stick-int-disc}\n\\end{equation}\n\n\\end_inset\n\nwhere the residuals are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{(1)}= & N_{a}^{(1)}\\mathbf{t}^{(1)}\\,, & \\mathbf{f}_{b}^{(2)}= & -N_{b}^{(2)}\\mathbf{t}^{(1)}\\\\\nw_{a}^{(1)}= & N_{a}^{(1)}w_{n}\\,\\,, & w_{b}^{(2)}= & -N_{b}^{(2)}w_{n}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n is obtained from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the penalty case and from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-stick-traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n if augmented Lagrangian regularization is employed.\n Similarly,\n \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n is calculated from either eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-wn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for penalty and augmented Lagrange methods,\n respectively.\n \n\\end_layout\n\n\\begin_layout Standard\nWe may now discretize individual terms and place them into matrix notation,\n anticipating their substitution into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-cont-int-linearized\"\nnolink \"false\"\n\n\\end_inset\n\n.\n By placing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-stick-int-disc\"\nnolink \"false\"\n\n\\end_inset\n\n into Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-stick-Dt\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-stick-Dwn\"\nnolink \"false\"\n\n\\end_inset\n\n and inserting the resulting linearizations into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-cont-int-linearized\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we obtain the stiffness matrix for biphasic stick as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c} & =\\sum_{a}\\begin{bmatrix}\\delta\\mathbf{v}_{a}^{(1)} & \\delta p_{a}^{(1)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\sum_{c}\\begin{bmatrix}\\mathbf{K}_{ac}^{(1,1)} & \\mathbf{0}\\\\\n\\mathbf{g}_{ac}^{(1,1)} & g_{ac}^{(1,1)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\right.\\\\\n & \\qquad\\qquad\\qquad\\left.+\\sum_{d}\\begin{bmatrix}\\mathbf{K}_{ad}^{(1,2)} & \\mathbf{0}\\\\\n\\mathbf{g}_{ad}^{(1,2)} & g_{ad}^{(1,2)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\sum_{b}\\begin{bmatrix}\\delta\\mathbf{v}_{b}^{(2)} & \\delta p_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\sum_{c}\\begin{bmatrix}\\mathbf{K}_{bc}^{(2,1)} & \\mathbf{0}\\\\\n\\mathbf{g}_{bc}^{(2,1)} & g_{bc}^{(2,1)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\right.\\\\\n & \\qquad\\qquad\\qquad\\left.+\\sum_{d}\\begin{bmatrix}\\mathbf{K}_{bd}^{(2,2)} & \\mathbf{0}\\\\\n\\mathbf{g}_{bd}^{(2,2)} & g_{bd}^{(2,2)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:biphasic-stick-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{(1,1)} & =-\\varepsilon_{n}N_{a}^{(1)}N_{c}^{(1)}\\mathbf{I}+N_{a}^{(1)}\\mathbf{t}^{(1)}\\otimes\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)}\\\\\n\\mathbf{K}_{ad}^{(1,2)} & =\\varepsilon_{n}N_{a}^{(1)}N_{d}^{(2)}\\mathbf{I}\\\\\n\\mathbf{K}_{bc}^{(2,1)} & =\\varepsilon_{n}N_{b}^{(2)}N_{c}^{(1)}\\mathbf{I}-N_{b}^{(2)}\\mathbf{t}^{(1)}\\otimes\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)}\\\\\n\\mathbf{K}_{bd}^{(2,2)} & =-\\varepsilon_{n}N_{b}^{(2)}N_{d}^{(2)}\\mathbf{I}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{g}_{ac}^{(1,1)} & =N_{a}^{(1)}w_{n}\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)} & g_{ac}^{(1,1)} & =\\varepsilon_{p}N_{a}^{(1)}N_{c}^{(1)}\\\\\n\\mathbf{g}_{ad}^{(1,2)} & =\\mathbf{0} & g_{ad}^{(1,2)} & =-\\varepsilon_{p}N_{a}^{(1)}N_{d}^{(2)}\\\\\n\\mathbf{g}_{bc}^{(2,1)} & =-N_{b}^{(2)}w_{n}\\mathbf{A}_{c}^{(1)}\\cdot\\mathbf{n}^{(1)} & g_{bc}^{(2,1)} & =-\\varepsilon_{p}N_{b}^{(2)}N_{c}^{(1)}\\\\\n\\mathbf{g}_{bd}^{(2,2)} & =\\mathbf{0} & g_{bd}^{(2,2)} & =\\varepsilon_{p}N_{b}^{(2)}N_{d}^{(2)}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nIn eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-stick-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)} & \\Delta p_{c}^{(1)}\\end{bmatrix}^{T}$\n\\end_inset\n\n is the vector of incremental changes in the degrees of freedom of the \n\\begin_inset Formula $c$\n\\end_inset\n\nth node of the current element face on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n.\n Similarly,\n \n\\begin_inset Formula $\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)} & \\Delta p_{d}^{(2)}\\end{bmatrix}^{T}$\n\\end_inset\n\n represents the incremental changes in the degrees of freedom of the \n\\begin_inset Formula $d$\n\\end_inset\n\nth node of the element face on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n which contains the intersection point \n\\begin_inset Formula $X^{(2)}$\n\\end_inset\n\n associated with the \n\\begin_inset Formula $k$\n\\end_inset\n\nth integration point on the current element face on \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b,Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Integration-Scheme\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for a description of the Gaussian quadrature integration scheme used to evaluate residuals and stiffness matrices (e.g.\n Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-stick-int-disc\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-stick-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Briefly,\n it should be noted that for all terms associated with \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n (e.g.\n \n\\begin_inset Formula $\\mathbf{K}_{ad}^{(1,2)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Delta\\mathbf{u}_{d}^{(2)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta p_{b}^{(2)}$\n\\end_inset\n\n) there may be \n\\begin_inset Formula $k\\in\\left[1,\\,n_{int}^{(e)}\\right]$\n\\end_inset\n\n distinct element faces on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n associated with all the integration points \n\\begin_inset Formula $X^{(1)}$\n\\end_inset\n\n on the \n\\begin_inset Formula $e$\n\\end_inset\n\nth element face of \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n,\n based on the location of \n\\begin_inset Formula $X^{(2)}$\n\\end_inset\n\n obtained from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:stick-x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Sliding-Elastic\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n on sliding-elastic frictional contact,\n we split the contact stiffness matrices in a different way,\n which allowed us to clearly separate like terms.\n Here,\n due to the complexity of the biphasic contact formulation,\n it was determined that the form of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-stick-stiffness\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n provides more clarity.\n\\end_layout\n\n\\begin_layout Subsection\nBiphasic Slip\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Slip\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLinearization\n\\end_layout\n\n\\begin_layout Standard\nDuring slip,\n the contact integral over \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n is performed over integration points \n\\begin_inset Formula $X^{(1)}$\n\\end_inset\n\n with prescribed parametric coordinates \n\\begin_inset Formula $\\eta_{(1)}^{\\alpha}$\n\\end_inset\n\n.\n However,\n the point on \n\\begin_inset Formula $\\gamma^{(2)}$\n\\end_inset\n\n in contact with \n\\begin_inset Formula $\\gamma^{(1)}$\n\\end_inset\n\n has parametric coordinates \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n which change with variations in \n\\begin_inset Formula $\\mathbf{x}^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}^{(1)}$\n\\end_inset\n\n,\n in accordance with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-gap\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Consequently,\n directional derivatives of \n\\begin_inset Formula $\\mathbf{x}^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $p^{(i)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta\\mathbf{v}^{(i)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\delta p^{(i)}$\n\\end_inset\n\n are given by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{(1)} & =\\Delta\\mathbf{u}^{(1)} & D\\mathbf{x}^{(2)} & =\\Delta\\mathbf{u}^{(2)}+\\mathbf{g}_{\\alpha}^{(2)}D\\eta_{(2)}^{\\alpha}\\\\\nDp^{(1)} & =\\Delta p^{(1)} & Dp^{(2)} & =\\Delta p^{(2)}+\\frac{\\partial p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}D\\eta_{(2)}^{\\alpha}\\\\\nD\\delta\\mathbf{v}^{(1)} & =\\mathbf{0} & D\\delta\\mathbf{v}^{(2)} & =\\frac{\\partial\\delta\\mathbf{v}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}D\\eta_{(2)}^{\\alpha}\\\\\nD\\delta p^{(1)} & =0 & D\\delta p^{(2)} & =\\frac{\\partial\\delta p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}D\\eta_{(2)}^{\\alpha}\n\\end{aligned}\n\\label{eq:biphasic-slip-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $D\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n is evaluated by our modification \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"false\"\n\n\\end_inset\n\n of a method proposed by Laursen and Simo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen93\"\nliteral \"false\"\n\n\\end_inset\n\n,\n and may be found in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Frictionless-Terms\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The linearization of the slip traction proceeds as before \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n,\n with the addition of a term involving the linearization of \n\\begin_inset Formula $\\mueff$\n\\end_inset\n\n.\n From eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mueff-biphasic\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n it follows that\n\\begin_inset Formula \n\\begin{equation}\nD\\mueff=\\mueq\\left(1-\\varphi\\right)\\frac{\\Delta p^{(1)}}{t_{n}}-\\mueq\\left(1-\\varphi\\right)\\frac{p^{(1)}}{t_{n}^{2}}Dt_{n}\\label{eq:Dmueff}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $Dt_{n}=\\varepsilon Dg$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-tn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n;\n this term has been provided previously in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dg_slip\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Equation\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dmueff\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n was derived by recalling that \n\\begin_inset Formula $\\mueq$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n are constants.\n We then obtain\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{t}^{(1)}=Dt_{n}\\left(\\no+\\mueff\\so\\right)+t_{n}\\left(D\\no+\\left(D\\mueff\\right)\\so+\\mueff D\\so\\right)\n\\end{equation}\n\n\\end_inset\n\nFinally,\n from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n (evaluated at \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n determined by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-x2\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n it follows that\n\\begin_inset Formula \n\\begin{equation}\nDw_{n}=\\varepsilon_{p}\\left(\\Delta p^{(1)}-\\Delta p^{(2)}-\\frac{\\partial p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}D\\eta_{(2)}^{\\alpha}\\right)\\label{eq:Dwn}\n\\end{equation}\n\n\\end_inset\n\nNote that the form of \n\\begin_inset Formula $Dw_{n}$\n\\end_inset\n\n given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dwn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n contains additional terms not present in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biph-stick-Dwn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This is because the parametric coordinates of intersection may vary in slip,\n but are invariant in stick;\n as a consequence,\n whether or not the linearization of \n\\begin_inset Formula $p^{(2)}$\n\\end_inset\n\n depends on \n\\begin_inset Formula $\\eta_{(2)}^{\\alpha}$\n\\end_inset\n\n is determined by the stick-slip status.\n Per the discussion following eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:pi-biphasic-penalty\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n in the main text,\n the fluid flux must be calculated after the stick-slip status has been resolved.\n\\end_layout\n\n\\begin_layout Subsubsection\nDiscretization\n\\end_layout\n\n\\begin_layout Standard\nLet the continuous variables on the primary and secondary surfaces be interpolated over each element face according to\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{(1)} & =\\sum_{a}N_{a}^{(1)}\\delta\\mathbf{v}_{a}^{(1)} & \\delta\\mathbf{v}^{(2)} & =\\sum_{b}N_{b}^{(2)}\\delta\\mathbf{v}_{b}^{(2)}\\\\\n\\Delta\\mathbf{u}^{(1)} & =\\sum_{c}N_{c}^{(1)}\\Delta\\mathbf{u}_{c}^{(1)} & \\Delta\\mathbf{u}^{(2)} & =\\sum_{d}N_{d}^{(2)}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\delta p^{(1)} & =\\sum_{a}N_{a}^{(1)}\\delta p_{a}^{(1)} & \\delta p^{(2)} & =\\sum_{b}N_{b}^{(2)}\\delta p_{b}^{(2)}\\\\\n\\Delta p^{(1)} & =\\sum_{c}N_{c}^{(1)}\\Delta p_{c}^{(1)} & \\Delta p^{(2)} & =\\sum_{d}N_{d}^{(2)}\\Delta p_{d}^{(2)}\n\\end{aligned}\n\\label{eq:biphasic-slip-var-discretization}\n\\end{equation}\n\n\\end_inset\n\nIn the case of slip,\n the contact integral of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:contact-int-invariant-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n can be split into normal and tangential parts,\n \n\\begin_inset Formula $\\delta G_{c}=\\delta G_{c}^{n}+\\delta G_{c}^{t}$\n\\end_inset\n\n,\n such that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c}= & \\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}t_{n}\\mathbf{n}^{(1)}\\\\\n-t_{n}\\mathbf{n}^{(1)}\n\\end{bmatrix}+\\begin{bmatrix}\\delta p^{(1)} & \\delta p^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}w_{n}\\\\\n-w_{n}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta\\mathbf{v}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\mueff t_{n}\\mathbf{s}^{(1)}\\\\\n-\\mueff t_{n}\\mathbf{s}^{(1)}\n\\end{bmatrix}\\,J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:biphasic-slip-discretized}\n\\end{equation}\n\n\\end_inset\n\nThis split will be useful for the full linearization.\n Discretizing this expression yields\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum_{a}\\begin{bmatrix}\\delta\\mathbf{v}_{a}^{(1)} & \\delta p_{a}^{(1)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{f}_{a}^{(1)}\\\\\nw_{a}^{(1)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\sum_{b}\\begin{bmatrix}\\delta\\mathbf{v}_{b}^{(2)} & \\delta p_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\mathbf{f}_{b}^{(2)}\\\\\nw_{b}^{(2)}\n\\end{bmatrix}J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{(1)}= & N_{a}^{(1)}\\mathbf{t}^{(1)}\\,, & \\mathbf{f}_{b}^{(2)}= & -N_{b}^{(2)}\\mathbf{t}^{(1)}\\\\\nw_{a}^{(1)}= & N_{a}^{(1)}w_{n}\\,\\,, & w_{b}^{(2)}= & -N_{b}^{(2)}w_{n}\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{t}^{(1)}$\n\\end_inset\n\n is given by Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-tn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for the penalty method and Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-tn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:augmented-t\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for augmented Lagrangian regularization;\n similarly,\n \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:wn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n or eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:AL-wn\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for penalty and augmented Lagrangian schemes,\n respectively.\n For the following linearization and discretization the normal and tangential components,\n representing frictionless and frictional contributions to the contact integral,\n will be treated separately and may then be added together as in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Paragraph\nFrictionless Terms\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of the frictional part of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n makes use of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-linearization\"\nnolink \"false\"\n\n\\end_inset\n\n to find\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c} & =\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta p^{(1)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}t_{n}\\mathbf{n}^{(1)}\\\\\nw_{n}\n\\end{bmatrix}\\frac{1}{J_{\\eta}^{(1)}}DJ_{\\eta}^{(1)}+\\begin{bmatrix}t_{n}\\\\\n0\n\\end{bmatrix}D\\mathbf{n}^{(1)}+\\begin{bmatrix}\\mathbf{n}^{(1)}\\\\\n0\n\\end{bmatrix}Dt_{n}\\right.\\\\\n & \\qquad\\qquad\\left.+\\begin{bmatrix}0\\\\\n1\n\\end{bmatrix}Dw_{n}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\frac{\\partial\\delta\\mathbf{v}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}} & \\frac{\\partial\\delta p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\end{bmatrix}\\cdot\\begin{bmatrix}-t_{n}\\mathbf{n}^{(1)}\\\\\n-w_{n}\n\\end{bmatrix}D\\eta_{(2)}^{\\alpha}\\,J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(2)} & \\delta p^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}-t_{n}\\mathbf{n}^{(1)}\\\\\n-w_{n}\n\\end{bmatrix}\\frac{1}{J_{\\eta}^{(1)}}DJ_{\\eta}^{(1)}+\\begin{bmatrix}-t_{n}\\\\\n0\n\\end{bmatrix}D\\mathbf{n}^{(1)}+\\begin{bmatrix}-\\mathbf{n}^{(1)}\\\\\n0\n\\end{bmatrix}Dt_{n}\\right.\\\\\n & \\qquad\\qquad\\left.\\begin{bmatrix}0\\\\\n-1\n\\end{bmatrix}Dw_{n}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:Dgc-biph-fricless-slip}\n\\end{equation}\n\n\\end_inset\n\nwhere we note that the virtual variables on the secondary surface now enter the linearization,\n as parametric coordinates on the secondary surface vary during slip according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:slip-gap\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The only linearization which has not been previously discretized is \n\\begin_inset Formula $Dw_{n}$\n\\end_inset\n\n,\n and it follows from placing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-var-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dwn\"\nnolink \"false\"\n\n\\end_inset\n\n that\n\\begin_inset Formula \n\\begin{equation}\nDw_{n}=\\sum_{c}\\begin{bmatrix}-\\varepsilon_{p}\\Nc\\mathbf{p}^{(2)}+\\varepsilon_{p}P_{c}\\no & \\varepsilon_{p}\\Nc\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}+\\sum_{d}\\begin{bmatrix}\\varepsilon_{p}\\Nd\\mathbf{p}^{(2)} & -\\varepsilon_{p}\\Nd\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\label{eq:Dwn-discrete}\n\\end{equation}\n\n\\end_inset\n\nwhere we define\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{p}^{(2)} & =\\frac{\\partial p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\bar{\\mathbf{g}}_{(2)}^{\\alpha}\\\\\nP_{c} & =ga^{\\alpha\\beta}\\frac{\\partial p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\frac{\\partial\\Nc}{\\partial\\eta_{(1)}^{\\beta}}\n\\end{aligned}\n\\label{eq:def-p-pc}\n\\end{equation}\n\n\\end_inset\n\nand for convenience we note that\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}D\\eta_{(2)}^{\\alpha}=\\sum_{c}\\begin{bmatrix}\\Nc\\mathbf{p}^{(2)}-P_{c}\\no & 0\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}+\\sum_{d}\\begin{bmatrix}-\\Nd\\mathbf{p}^{(2)} & 0\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\label{eq:dp-deta}\n\\end{equation}\n\n\\end_inset\n\nDirectional derivatives of virtual variables on the secondary surface will lead to expressions of the form \n\\begin_inset Formula $\\left(\\partial\\Nb/\\partial\\eta_{(2)}^{\\lambda}\\right)D\\eta_{(2)}^{\\lambda}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial N_{b}^{(2)}}{\\partial\\eta_{(2)}^{\\lambda}}D\\eta_{(2)}^{\\lambda}=\\begin{bmatrix}\\sum_{c}N_{c}^{(1)}\\bar{\\mathbf{m}}_{b}^{(2)}-G_{bc}\\mathbf{n}^{(1)} & \\sum_{d}-N_{d}^{(2)}\\bar{\\mathbf{m}}_{b}^{(2)}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta\\mathbf{u}_{d}^{(2)}\n\\end{bmatrix}\\label{eq:dNb-deta}\n\\end{equation}\n\n\\end_inset\n\nIn eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dNb-deta\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the pressure degrees of freedom do not enter into the linearization.\n In an effort to keep the expression more compact,\n we have not included these variables.\n However,\n this expression could easily be cast into the form of e.g.\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dp-deta\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by adding zeros where necessary.\n Discretizing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dgc-biph-fricless-slip\"\nnolink \"false\"\n\n\\end_inset\n\n and making use of linearizations found previously \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n,\n along with Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dwn-discrete\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:dp-deta\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n allows the resulting stiffness matrix to be expressed as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c} & =\\sum_{a}\\begin{bmatrix}\\delta\\mathbf{v}_{a}^{(1)} & \\delta p_{a}^{(1)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\sum_{c}\\begin{bmatrix}\\mathbf{K}_{ac}^{(1,1)} & \\mathbf{0}\\\\\n\\mathbf{g}_{ac}^{(1,1)} & g_{ac}^{(1,1)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\right.\\\\\n & \\qquad\\qquad\\qquad\\left.+\\sum_{d}\\begin{bmatrix}\\mathbf{K}_{ad}^{(1,2)} & \\mathbf{0}\\\\\n\\mathbf{g}_{ad}^{(1,2)} & g_{ad}^{(1,2)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\sum_{b}\\begin{bmatrix}\\delta\\mathbf{v}_{b}^{(2)} & \\delta p_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\sum_{c}\\begin{bmatrix}\\mathbf{K}_{bc}^{(2,1)} & \\mathbf{0}\\\\\n\\mathbf{g}_{bc}^{(2,1)} & g_{bc}^{(2,1)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\right.\\\\\n & \\qquad\\qquad\\qquad\\left.+\\sum_{d}\\begin{bmatrix}\\mathbf{K}_{bd}^{(2,2)} & \\mathbf{0}\\\\\n\\mathbf{g}_{bd}^{(2,2)} & g_{bd}^{(2,2)}\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:biphasic-fricless-slip-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{(1,1)} & =-N_{a}^{(1)}N_{c}^{(1)}\\left(\\varepsilon\\Nt\\right)-\\Na\\left(t_{n}\\Ac+t_{n}\\Mc\\cdot\\No\\right)\\\\\n\\mathbf{K}_{ad}^{(1,2)} & =N_{a}^{(1)}N_{d}^{(2)}\\left(\\varepsilon\\Nt\\right)\\\\\n\\mathbf{K}_{bc}^{(2,1)} & =N_{b}^{(2)}N_{c}^{(1)}\\left(\\varepsilon\\Nt\\right)+N_{b}^{(2)}\\left(t_{n}\\Ac+t_{n}\\Mc\\cdot\\No\\right)\\\\\n & \\qquad\\qquad+\\Nc\\left(t_{n}\\Mb\\right)+G_{bc}\\left(t_{n}\\No\\right)\\\\\n\\mathbf{K}_{bd}^{(2,2)} & =-N_{b}^{(2)}N_{d}^{(2)}\\left(\\varepsilon\\Nt\\right)-\\Nd\\left(t_{n}\\Mb\\right)\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{g}_{ac}^{(1,1)} & =-N_{a}^{(1)}N_{c}^{(1)}\\left(\\varepsilon_{p}\\mathbf{p}^{(2)}\\right)+N_{a}^{(1)}\\left(w_{n}\\Ac\\cdot\\no+\\varepsilon_{p}P_{c}\\no\\right)\\\\\n\\mathbf{g}_{ad}^{(1,2)} & =N_{a}^{(1)}N_{d}^{(2)}\\left(\\varepsilon_{p}\\mathbf{p}^{(2)}\\right)\\\\\n\\mathbf{g}_{bc}^{(2,1)} & =N_{b}^{(2)}N_{c}^{(1)}\\left(\\varepsilon_{p}\\mathbf{p}^{(2)}\\right)-N_{b}^{(2)}\\left(w_{n}\\Ac\\cdot\\no+\\varepsilon_{p}P_{c}\\no\\right)-N_{c}^{(1)}\\left(w_{n}\\mb\\right)+G_{bc}\\left(w_{n}\\no\\right)\\\\\n\\mathbf{g}_{bd}^{(2,2)} & =-N_{b}^{(2)}N_{d}^{(2)}\\left(\\varepsilon_{p}\\mathbf{p}^{(2)}\\right)+N_{d}^{(2)}\\left(w_{n}\\mb\\right)\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}g_{ac}^{(1,1)} & =-N_{a}^{(1)}N_{c}^{(1)}\\left(-\\varepsilon_{p}\\right)\\\\\ng_{ad}^{(1,2)} & =N_{a}^{(1)}N_{d}^{(2)}\\left(-\\varepsilon_{p}\\right)\\\\\ng_{bc}^{(2,1)} & =N_{b}^{(2)}N_{c}^{(1)}\\left(-\\varepsilon_{p}\\right)\\\\\ng_{bd}^{(2,2)} & =-N_{b}^{(2)}N_{d}^{(2)}\\left(-\\varepsilon_{p}\\right)\n\\end{aligned}\n\\end{equation}\n\n\\end_inset\n\nThe expressions above are very similar to those which can be found in our frictionless biphasic contact paper \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10\"\nliteral \"false\"\n\n\\end_inset\n\n,\n although the present framework is more general,\n as that previous study evaluated expressions in the limit as \n\\begin_inset Formula $g\\to0$\n\\end_inset\n\n (see Zimmerman and Ateshian \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman18\"\nliteral \"false\"\n\n\\end_inset\n\n for a detailed discussion of this assumption and the benefits of relaxing it).\n\\end_layout\n\n\\begin_layout Paragraph\nFrictional Terms\n\\end_layout\n\n\\begin_layout Standard\nLinearizing the frictional contribution follows from the second term of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-discretized\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c} & =\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(1)} & \\delta p^{(1)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}t_{n}\\so\\\\\n0\n\\end{bmatrix}D\\mueff+\\begin{bmatrix}\\mueff\\so\\\\\n0\n\\end{bmatrix}Dt_{n}\\right.\\\\\n & \\left.+\\begin{bmatrix}\\mueff t_{n}\\\\\n0\n\\end{bmatrix}D\\so+\\begin{bmatrix}\\mueff t_{n}\\so\\\\\n0\n\\end{bmatrix}\\frac{1}{\\jeta}D\\jeta\\right)\\jeta d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\delta\\mathbf{v}^{(2)} & \\delta p^{(2)}\\end{bmatrix}\\cdot\\left(\\begin{bmatrix}-t_{n}\\so\\\\\n0\n\\end{bmatrix}D\\mueff+\\begin{bmatrix}-\\mueff\\so\\\\\n0\n\\end{bmatrix}Dt_{n}\\right.\\\\\n & \\left.+\\begin{bmatrix}-\\mueff t_{n}\\\\\n0\n\\end{bmatrix}D\\so+\\begin{bmatrix}-\\mueff t_{n}\\so\\\\\n0\n\\end{bmatrix}\\frac{1}{\\jeta}D\\jeta\\right)\\jeta d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\int_{\\Gamma_{\\eta}^{(1)}}\\begin{bmatrix}\\frac{\\partial\\delta\\mathbf{v}^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}} & \\frac{\\partial\\delta p^{(2)}}{\\partial\\eta_{(2)}^{\\alpha}}\\end{bmatrix}\\cdot\\begin{bmatrix}-\\mueff t_{n}\\so\\\\\n0\n\\end{bmatrix}D\\eta_{(2)}^{\\alpha}\\jeta d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:Dgc-biph-fric-slip}\n\\end{equation}\n\n\\end_inset\n\nThe remaining quantity in this expression to be determined is the discretization of \n\\begin_inset Formula $D\\mueff$\n\\end_inset\n\n;\n from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dmueff\"\nnolink \"false\"\n\n\\end_inset\n\n and eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-var-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n it follows that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mueff & =\\sum_{c}\\begin{bmatrix}\\varepsilon\\Nc\\mueq\\Gamma_{p}\\frac{1}{t_{n}^{2}}\\Nbo\\cdot\\no+\\mueq\\Gamma_{p}\\frac{1}{t_{n}}\\No\\cdot\\mc & \\Nc\\mueq\\left(1-\\varphi\\right)\\frac{1}{t_{n}}\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{\\mathbf{u}}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\\\\n & +\\sum_{d}\\begin{bmatrix}-\\varepsilon_{n}\\Nd\\mueq\\Gamma_{p}\\frac{1}{t_{n}^{2}}\\Nbo\\cdot\\no & 0\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{\\mathbf{u}}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\n\\end{aligned}\n\\label{eq:Dmueff-discrete}\n\\end{equation}\n\n\\end_inset\n\nwhere we made the definition \n\\begin_inset Formula \n\\begin{equation}\n\\Gamma_{p}=\\left(1-\\varphi\\right)p^{(1)}\n\\end{equation}\n\n\\end_inset\n\njust to be used in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dmueff-discrete\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for space considerations.\n Finally,\n inserting Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-slip-var-discretization\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dmueff-discrete\"\nnolink \"false\"\n\n\\end_inset\n\n into eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Dgc-biph-fric-slip\"\nnolink \"false\"\n\n\\end_inset\n\n yields the stiffness matrix for the frictional terms \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G_{c} & =\\sum_{a}\\begin{bmatrix}\\delta\\mathbf{v}_{a}^{(1)} & \\delta p_{a}^{(1)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\sum_{c}\\begin{bmatrix}\\mathbf{K}_{ac}^{(1,1)} & \\mathbf{k}_{ac}^{(1,1)}\\\\\n\\mathbf{0} & 0\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\right.\\\\\n & \\qquad\\qquad\\qquad\\left.+\\sum_{d}\\begin{bmatrix}\\mathbf{K}_{ad}^{(1,2)} & \\mathbf{k}_{ad}^{(1,2)}\\\\\n\\mathbf{0} & 0\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\\\\\n & +\\sum_{b}\\begin{bmatrix}\\delta\\mathbf{v}_{b}^{(2)} & \\delta p_{b}^{(2)}\\end{bmatrix}\\cdot\\int_{\\Gamma_{\\eta}^{(1)}}\\left(\\sum_{c}\\begin{bmatrix}\\mathbf{K}_{bc}^{(2,1)} & \\mathbf{k}_{bc}^{(2,1)}\\\\\n\\mathbf{0} & 0\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{c}^{(1)}\\\\\n\\Delta p_{c}^{(1)}\n\\end{bmatrix}\\right.\\\\\n & \\qquad\\qquad\\qquad\\left.+\\sum_{d}\\begin{bmatrix}\\mathbf{K}_{bd}^{(2,2)} & \\mathbf{k}_{bd}^{(2,2)}\\\\\n\\mathbf{0} & 0\n\\end{bmatrix}\\cdot\\begin{bmatrix}\\Delta\\mathbf{u}_{d}^{(2)}\\\\\n\\Delta p_{d}^{(2)}\n\\end{bmatrix}\\right)J_{\\eta}^{(1)}d\\eta_{(1)}^{1}d\\eta_{(1)}^{2}\n\\end{aligned}\n\\label{eq:biphasic-fric-slip-stiffness}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{(1,1)} & =-N_{a}^{(1)}N_{c}^{(1)}\\left(\\varepsilon\\mueq\\tilde{\\mathbf{S}}^{(1)}+\\mueff t_{n}\\mathbf{B}^{(1)}\\right)-N_{a}^{(1)}\\left(t_{n}\\so\\otimes\\bar{\\mathbf{h}}_{c-}^{(1)}\\right.\\\\\n & \\qquad\\qquad\\left.+\\mueff t_{n}g\\mathbf{P}_{S}\\cdot\\mathbf{c}^{(1)}\\otimes\\mathbf{h}_{c+}^{(1)}-\\mueff t_{n}\\mathbf{J}_{c}^{(1)}\\right)\\\\\n\\mathbf{K}_{ad}^{(1,2)} & =N_{a}^{(1)}N_{d}^{(2)}\\left(\\varepsilon\\mueq\\tilde{\\mathbf{S}}^{(1)}+\\mueff t_{n}\\mathbf{B}^{(1)}\\right)\\\\\n\\mathbf{K}_{bc}^{(2,1)} & =N_{b}^{(2)}N_{c}^{(1)}\\left(\\varepsilon\\mueq\\tilde{\\mathbf{S}}^{(1)}+\\mueff t_{n}\\mathbf{B}^{(1)}\\right)+N_{b}^{(2)}\\left(t_{n}\\so\\otimes\\bar{\\mathbf{h}}_{c-}^{(1)}\\right.\\\\\n & \\qquad\\qquad\\left.+\\mueff t_{n}g\\mathbf{P}_{S}\\cdot\\mathbf{c}^{(1)}\\otimes\\mathbf{h}_{c+}^{(1)}-\\mueff t_{n}\\mathbf{J}_{c}^{(1)}\\right)\\\\\n & \\qquad\\qquad+\\Nc\\left(-\\mueff t_{n}\\so\\otimes\\mb\\right)+G_{bc}\\left(\\mueff t_{n}\\So\\right)\\\\\n\\mathbf{K}_{bd}^{(2,2)} & =-N_{b}^{(2)}N_{d}^{(2)}\\left(\\varepsilon\\mueq\\tilde{\\mathbf{S}}^{(1)}+\\mueff t_{n}\\mathbf{B}^{(1)}\\right)-\\Nd\\left(-\\mueff t_{n}\\so\\otimes\\mb\\right)\n\\end{aligned}\n\\label{eq:biphasic-fric-slip-solid-stiffness}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ac}^{(1,1)} & =-N_{a}^{(1)}N_{c}^{(1)}\\left(-\\mueq\\left(1-\\varphi\\right)\\so\\right)\\\\\n\\mathbf{k}_{ad}^{(1,2)} & =\\mathbf{0}\\\\\n\\mathbf{k}_{bc}^{(2,1)} & =N_{b}^{(2)}N_{c}^{(1)}\\left(-\\mueq\\left(1-\\varphi\\right)\\so\\right)\\\\\n\\mathbf{k}_{bd}^{(2,2)} & =\\mathbf{0}\n\\end{aligned}\n\\label{eq:biphasic-fric-slip-pressure-stiffness}\n\\end{equation}\n\n\\end_inset\n\nIn eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-fric-slip-solid-stiffness\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we have defined\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\bar{\\mathbf{h}}_{c-}^{(1)} & =\\mueq\\No\\cdot\\mc-\\mueff\\Ac\\cdot\\no\\end{aligned}\n\\label{eq:h-bar-c-minus}\n\\end{equation}\n\n\\end_inset\n\nby analogy with our definitions for \n\\begin_inset Formula $\\mathbf{h}_{c+}^{(1)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{h}_{c-}^{(1)}$\n\\end_inset\n\n in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SE-Frictional-Terms\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The stiffness matrix of the frictional contribution to the virtual work,\n given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-fric-slip-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n,\n is nonsymmetric.\n Summing Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-fricless-slip-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-fric-slip-stiffness\"\nnolink \"false\"\n\n\\end_inset\n\n produces the total stiffness matrix for the case of frictional biphasic slip;\n this total stiffness matrix is also nonsymmetric.\n However,\n as noted before \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"Ateshian10\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the stiffness matrix for biphasic materials is nonsymmetric by construction,\n so there is no expectation that the contact stiffness matrices be symmetric.\n\\end_layout\n\n\\begin_layout Standard\nEquation\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-fric-slip-solid-stiffness\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n contains the solid-solid stiffness terms.\n Comparing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:biphasic-fric-slip-solid-stiffness\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n with eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:frictional-stiffness\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n shows that the same form of the equations is recovered.\n Interestingly,\n however,\n some of the terms in sliding-elastic (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Sliding-Elastic\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) which were multiplied by the friction coefficient \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are now multiplied by \n\\begin_inset Formula $\\mueff$\n\\end_inset\n\n,\n which indicates certain terms which are important in biphasic contact.\n Of course,\n in the absence of pressurized fluid,\n \n\\begin_inset Formula $\\mueff\\to\\mu$\n\\end_inset\n\n and we recover the sliding-elastic formulation.\n\\end_layout\n\n\\begin_layout Section\nBiphasic-Solute Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Solute-Contact\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nContact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Solute-Material\"\nnolink \"false\"\n\n\\end_inset\n\n for a review of biphasic-solute materials.\n The contact interface is defined between surfaces \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n Due to continuity requirements on the traction and fluxes,\n the external virtual work resulting from contact tractions \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}$\n\\end_inset\n\n,\n solvent fluxes \n\\begin_inset Formula $w_{n}^{\\left(i\\right)}$\n\\end_inset\n\n and solute fluxes \n\\begin_inset Formula $j_{n}^{\\left(i\\right)}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2)$\n\\end_inset\n\n,\n may be combined into the contact integral \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)w_{n}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)j_{n}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\,.\n\\end{aligned}\n\\label{eq625}\n\\end{equation}\n\n\\end_inset\n\nIn the current implementation,\n only frictionless contact is taken into consideration,\n so that the contact traction has only a normal component,\n \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}=t_{n}\\mathbf{n}^{\\left(i\\right)}$\n\\end_inset\n\n.\n To evaluate and linearize \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n,\n define the covariant basis vectors on each surface as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{\\left(i\\right)}=\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}}{\\partial\\eta_{\\left(i\\right)}^{\\alpha}},\\quad\\alpha=1,2,\\label{eq626}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n represents the spatial position of points on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\eta_{\\left(i\\right)}^{\\alpha}$\n\\end_inset\n\n represent the parametric coordinates of that point.\n The unit outward normal on each surface is then given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{\\left(i\\right)}=\\frac{\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}}{\\left|\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}\\right|}.\\label{eq627}\n\\end{equation}\n\n\\end_inset\n\nNow the contact integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)t_{n}\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)w_{n}^{\\left(1\\right)}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)j_{n}^{\\left(1\\right)}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\;,\n\\end{aligned}\n\\label{eq628}\n\\end{equation}\n\n\\end_inset\n\nand the linearization \n\\begin_inset Formula $D\\delta G_{c}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\sum\\limits_{i=1}^{2}D\\delta G_{c}\\left[\\Delta\\mathbf{u}^{\\left(i\\right)}\\right]+D\\delta G_{c}\\left[\\Delta\\tilde{p}^{\\left(i\\right)}\\right]+D\\delta G_{c}\\left[\\Delta\\tilde{c}^{\\left(i\\right)}\\right]\\,.\\label{eq629}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGap Function\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe gap function \n\\begin_inset Formula $g$\n\\end_inset\n\n,\n representing the distance between the contact surfaces,\n is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}^{\\left(2\\right)}=\\mathbf{x}^{\\left(1\\right)}+g\\mathbf{n}^{\\left(1\\right)},\\quad g=\\left(\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right)\\cdot\\mathbf{n}^{\\left(1\\right)}\\,.\\label{eq630}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of variables associated with motion,\n pressure,\n and concentration,\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{\\left(1\\right)} & =\\Delta\\mathbf{u}^{\\left(1\\right)} & D\\mathbf{x}^{\\left(2\\right)} & =\\Delta\\mathbf{u}^{\\left(2\\right)}+\\mathbf{g}_{\\alpha}^{\\left(2\\right)}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\tilde{p}^{\\left(1\\right)} & =\\Delta\\tilde{p}^{\\left(1\\right)} & D\\tilde{p}^{\\left(2\\right)} & =\\Delta\\tilde{p}^{\\left(2\\right)}+\\frac{\\partial\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\tilde{c}^{\\left(1\\right)} & =\\Delta\\tilde{c}^{\\left(1\\right)} & D\\tilde{c}^{\\left(2\\right)} & =\\Delta\\tilde{c}^{\\left(2\\right)}+\\frac{\\partial\\tilde{c}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\delta\\mathbf{v}^{\\left(1\\right)} & =\\mathbf{0} & D\\mathbf{v}^{\\left(2\\right)} & =\\frac{\\partial\\delta\\mathbf{v}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\delta\\tilde{p}^{\\left(1\\right)} & =0 & D\\delta\\tilde{p}^{\\left(2\\right)} & =\\frac{\\partial\\delta\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\delta\\tilde{c}^{\\left(1\\right)} & =0 & D\\delta\\tilde{c}^{\\left(2\\right)} & =\\frac{\\partial\\delta\\tilde{c}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\n\\end{aligned}\n\\,,\\label{eq631}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\eta_{\\left(2\\right)}^{\\alpha}=\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\cdot a^{\\alpha\\beta}\\mathbf{g}_{\\beta}^{\\left(1\\right)}-a^{\\alpha\\beta}g\\mathbf{n}^{\\left(1\\right)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\beta}}\\,,\\label{eq632}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula $a^{\\alpha\\beta}=\\left(A_{\\alpha\\beta}\\right)^{-1}$\n\\end_inset\n\n and \n\\begin_inset Formula $A_{\\alpha\\beta}=\\mathbf{g}_{\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{g}_{\\beta}^{\\left(2\\right)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-Penalty-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the normal component of the contact traction be described by the penalty function,\n \n\\begin_inset Formula \n\\begin{equation}\nt_{n}=\\begin{cases}\n\\varepsilon_{n}g & g<0\\\\\n0 & g\\geqslant0\n\\end{cases}\\,,\\label{eq633}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{n}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n.\n Similarly,\n let \n\\begin_inset Formula \n\\begin{equation}\n\\begin{cases}\nw_{n}=\\varepsilon_{p}\\pi=\\varepsilon_{p}\\left(\\tilde{p}^{\\left(1\\right)}-\\tilde{p}^{\\left(2\\right)}\\right) & t_{n}<0\\\\\n\\tilde{p}^{\\left(i\\right)}=\\tilde{p}^{\\ast} & t_{n}=0\n\\end{cases}\\,,\\label{eq634}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{cases}\nj_{n}=\\varepsilon_{c}\\chi=\\varepsilon_{c}\\left(\\tilde{c}^{\\left(1\\right)}-\\tilde{c}^{\\left(2\\right)}\\right) & t_{n}<0\\\\\n\\tilde{c}^{\\left(i\\right)}=\\tilde{c}^{\\ast} & t_{n}=0\n\\end{cases}\\,,\\label{eq635}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varepsilon_{c}$\n\\end_inset\n\n are penalty factors associated with \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $j_{n}$\n\\end_inset\n\n,\n respectively.\n It follows that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}Dt_{n} & =\\varepsilon_{n}\\left(\\Delta\\mathbf{u}^{\\left(2\\right)}-\\Delta\\mathbf{u}^{\\left(1\\right)}+\\mathbf{g}_{\\alpha}^{\\left(2\\right)}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\right)\\cdot\\mathbf{n}^{\\left(1\\right)}\\\\\nDw_{n} & =\\varepsilon_{p}\\left(\\Delta\\tilde{p}^{\\left(1\\right)}-\\Delta\\tilde{p}^{\\left(2\\right)}-\\frac{\\partial\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\right)\\\\\nDj_{n} & =\\varepsilon_{c}\\left(\\Delta\\tilde{c}^{\\left(1\\right)}-\\Delta\\tilde{c}^{\\left(2\\right)}-\\frac{\\partial\\tilde{c}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\right)\n\\end{aligned}\n\\,.\\label{eq636}\n\\end{equation}\n\n\\end_inset\n\nGiven these relations,\n it can be shown that the directional derivatives of the various terms appearing in the integrand of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\left(t_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right)=\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\varepsilon_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\left(\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +J_{\\eta}^{\\left(1\\right)}t_{n}\\frac{\\partial\\delta\\mathbf{v}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\cdot\\left(\\mathbf{n}^{\\left(2\\right)}\\otimes\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\right)\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +t_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\times\\mathbf{g}_{2}^{\\left(1\\right)}+\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq637}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\left(w_{n}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\right)=\\\\\n & J_{\\eta}^{\\left(1\\right)}\\varepsilon_{p}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\left(\\Delta\\tilde{p}^{\\left(1\\right)}-\\Delta\\tilde{p}^{\\left(2\\right)}\\right)\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\left[\\varepsilon_{p}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\frac{\\partial\\tilde{p}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha}+w_{n}\\frac{\\partial\\delta\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\right]\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +w_{n}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\times\\mathbf{g}_{2}^{\\left(1\\right)}+\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq638}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & D\\left(j_{n}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\right)=\\\\\n & J_{\\eta}^{\\left(1\\right)}\\varepsilon_{c}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)\\left(\\Delta\\tilde{c}^{\\left(1\\right)}-\\Delta\\tilde{c}^{\\left(2\\right)}\\right)\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\left[\\varepsilon_{c}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)\\frac{\\partial\\tilde{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha}+j_{n}\\frac{\\partial\\delta\\tilde{c}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\right]\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +j_{n}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\times\\mathbf{g}_{2}^{\\left(1\\right)}+\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq639}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}^{\\left(1\\right)}=\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:BS-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral may be discretized as \n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left[t_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{n}^{\\left(1\\right)}+w_{n}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)+j_{n}\\left(\\delta\\tilde{c}^{\\left(1\\right)}-\\delta\\tilde{c}^{\\left(2\\right)}\\right)\\right]\\,.\\label{eq640}\n\\end{equation}\n\n\\end_inset\n\nThe variables may be interpolated over each element face according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\mathbf{v}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\mathbf{v}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\mathbf{u}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\mathbf{u}_{c}^{\\left(1\\right)} & \\Delta\\mathbf{u}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\delta\\tilde{p}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\tilde{p}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\tilde{p}_{c}^{\\left(1\\right)} & \\Delta\\tilde{p}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\delta\\tilde{c}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\tilde{c}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\tilde{c}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\tilde{c}_{c}^{\\left(1\\right)} & \\Delta\\tilde{c}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\tilde{c}_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq641}\n\\end{equation}\n\n\\end_inset\n\nThen,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{\\left(1\\right)}\\\\\nw_{a}^{\\left(1\\right)}\\\\\nj_{a}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & \\left.+\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{b,k}^{\\left(1\\right)} & \\delta\\tilde{p}_{b,k}^{\\left(1\\right)} & \\delta\\tilde{c}_{b,k}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{b,k}^{\\left(1\\right)}\\\\\nw_{b,k}^{\\left(1\\right)}\\\\\nj_{b,k}^{\\left(1\\right)}\n\\end{array}\\right]\\right)\\,,\n\\end{aligned}\n\\label{eq642}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}t_{n}\\mathbf{n}^{\\left(1\\right)} & \\mathbf{f}_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}t_{n}\\mathbf{n}^{\\left(1\\right)}\\\\\nw_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}w_{n} & w_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}w_{n}\\\\\nj_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}j_{n} & j_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}j_{n}\n\\end{aligned}\n\\,.\\label{eq643}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}-D\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\\\\n & \\times\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{ccc}\n\\mathbf{K}_{ac}^{\\left(1,1\\right)} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{g}_{ac}^{\\left(1,1\\right)} & g_{ac}^{\\left(1,1\\right)} & 0\\\\\n\\mathbf{h}_{ac}^{\\left(1,1\\right)} & 0 & h_{ac}^{\\left(1,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{p}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\right.\\\\\n & +\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{ccc}\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{g}_{ad,k}^{\\left(1,2\\right)} & g_{ad,k}^{\\left(1,2\\right)} & 0\\\\\n\\mathbf{h}_{ad,k}^{\\left(1,2\\right)} & 0 & h_{ad,k}^{\\left(1,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\left(2\\right)}\n\\end{array}\\right]\\right)\\\\\n & +\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{ccc}\n\\delta\\mathbf{v}_{b,k}^{\\left(2\\right)} & \\delta\\tilde{p}_{b,k}^{\\left(2\\right)} & \\delta\\tilde{c}_{b,k}^{\\left(2\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{ccc}\n\\mathbf{K}_{bc,k}^{\\left({2,1}\\right)} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{g}_{bc,k}^{\\left(2,1\\right)} & g_{bc,k}^{\\left(2,1\\right)} & 0\\\\\n\\mathbf{h}_{bc,k}^{\\left(2,1\\right)} & 0 & h_{bc,k}^{\\left(2,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{p}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{ccc}\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{g}_{bd,k}^{\\left(2,2\\right)} & g_{bd,k}^{\\left(2,2\\right)} & 0\\\\\n\\mathbf{h}_{bd,k}^{\\left(2,2\\right)} & 0 & h_{bd,k}^{\\left(2,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\left(2\\right)}\n\\end{array}\\right]\\right)\\right)\\,,\n\\end{aligned}\n\\label{eq644}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{n}N_{c}^{\\left(1\\right)}\\mathbf{N}^{\\left(1\\right)}+t_{n}\\mathbf{A}_{c}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{n}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{N}^{\\left(1\\right)}\\\\\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & =-N_{c}^{\\left(1\\right)}\\left(\\varepsilon_{n}N_{b}^{\\left(2\\right)}\\mathbf{N}^{\\left(1\\right)}+t_{n}\\mathbf{M}_{b}^{\\left(2\\right)}\\right)-t_{n}N_{b}^{\\left(2\\right)}\\mathbf{A}_{c}^{\\left(1\\right)}\\\\\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & =N_{d}^{\\left(2\\right)}\\left(\\varepsilon_{n}N_{b}^{\\left(2\\right)}\\mathbf{N}^{\\left(1\\right)}+t_{n}\\mathbf{M}_{b}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq645}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{g}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{p}N_{c}^{\\left(1\\right)}\\mathbf{p}^{\\left(1\\right)}-w_{n}\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot\\mathbf{n}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{g}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{p}^{\\left(1\\right)}\\\\\n\\mathbf{g}_{bc,k}^{\\left(2,1\\right)} & =N_{c}^{\\left(1\\right)}\\left(-\\varepsilon_{p}N_{b}^{\\left(2\\right)}\\mathbf{p}^{\\left(1\\right)}+w_{n}\\mathbf{m}_{b}^{\\left(2\\right)}\\right)+w_{n}N_{b}^{\\left(2\\right)}\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot\\mathbf{n}^{\\left(1\\right)}\\\\\n\\mathbf{g}_{bd,k}^{\\left(2,2\\right)} & =N_{d}^{\\left(2\\right)}\\left(\\varepsilon_{p}N_{b}^{\\left(2\\right)}\\mathbf{p}^{\\left(1\\right)}-w_{n}\\mathbf{m}_{b}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq646}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}g_{ac}^{\\left(1,1\\right)} & =-\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\ng_{ad,k}^{\\left(1,2\\right)} & =\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\ng_{bc,k}^{\\left(2,1\\right)} & =\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\ng_{bd,k}^{\\left(2,2\\right)} & =-\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq647}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{h}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{c}N_{c}^{\\left(1\\right)}\\mathbf{q}^{\\left(1\\right)}-j_{n}\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot\\mathbf{n}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{h}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{c}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{q}^{\\left(1\\right)}\\\\\n\\mathbf{h}_{bc,k}^{\\left(2,1\\right)} & =N_{c}^{\\left(1\\right)}\\left(-\\varepsilon_{c}N_{b}^{\\left(2\\right)}\\mathbf{q}^{\\left(1\\right)}+j_{n}\\mathbf{m}_{b}^{\\left(2\\right)}\\right)+j_{n}N_{b}^{\\left(2\\right)}\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot\\mathbf{n}^{\\left(1\\right)}\\\\\n\\mathbf{h}_{bd,k}^{\\left(2,2\\right)} & =N_{d}^{\\left(2\\right)}\\left(\\varepsilon_{c}N_{b}^{\\left(2\\right)}\\mathbf{q}^{\\left(1\\right)}-j_{n}\\mathbf{m}_{b}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq648}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}h_{ac}^{\\left(1,1\\right)} & =-\\varepsilon_{c}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\nh_{ad,k}^{\\left(1,2\\right)} & =\\varepsilon_{c}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\nh_{bc,k}^{\\left(2,1\\right)} & =\\varepsilon_{c}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\nh_{bd,k}^{\\left(2,2\\right)} & =-\\varepsilon_{c}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq649}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{N}^{\\left(1\\right)} & =\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}\\\\\n\\mathbf{A}_{c}^{\\left(1\\right)} & =\\frac{1}{J_{\\eta}^{\\left(1\\right)}}\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\mathbf{g}_{2}^{\\left(1\\right)}-\\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\mathbf{g}_{1}^{\\left(1\\right)}\\right\\} \\\\\n\\mathbf{M}_{b}^{\\left(2\\right)} & =\\mathbf{n}^{\\left(2\\right)}\\otimes\\mathbf{m}_{b}^{\\left(2\\right)}\\\\\n\\mathbf{m}_{b}^{\\left(2\\right)} & =\\frac{\\partial N_{b}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\\\\n\\mathbf{p}^{\\left(1\\right)} & =\\frac{\\partial\\tilde{p}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha}\\\\\n\\mathbf{q}^{\\left(1\\right)} & =\\frac{\\partial\\tilde{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha}\n\\end{aligned}\n\\,.\\label{eq650}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMultiphasic Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Multiphasic-Contact\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nContact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for a review of multiphasic materials.\n The contact interface is defined between surfaces \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n Due to continuity requirements on the traction and fluxes,\n the external virtual work resulting from contact tractions \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}$\n\\end_inset\n\n,\n solvent fluxes \n\\begin_inset Formula $w_{n}^{\\left(i\\right)}$\n\\end_inset\n\n and solute fluxes \n\\begin_inset Formula $j_{n}^{\\alpha\\left(i\\right)}$\n\\end_inset\n\n for solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2)$\n\\end_inset\n\n,\n may be combined into the contact integral \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)w_{n}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\\\\n & +\\sum\\limits_{\\alpha}\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)j_{n}^{\\alpha\\left(1\\right)}\\,da^{\\left(1\\right)}\n\\end{aligned}\n\\,.\\label{eq651}\n\\end{equation}\n\n\\end_inset\n\nIn the current implementation,\n only frictionless contact is taken into consideration,\n so that the contact traction has only a normal component,\n \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}=t_{n}\\mathbf{n}^{\\left(i\\right)}$\n\\end_inset\n\n.\n To evaluate and linearize \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n,\n define the covariant basis vectors on each surface as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{\\left(i\\right)}=\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}}{\\partial\\eta_{\\left(i\\right)}^{\\alpha}},\\quad\\alpha=1,2,\\label{eq652}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n represents the spatial position of points on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\eta_{\\left(i\\right)}^{\\alpha}$\n\\end_inset\n\n represent the parametric coordinates of that point.\n The unit outward normal on each surface is then given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{\\left(i\\right)}=\\frac{\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}}{\\left|\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}\\right|}.\\label{eq653}\n\\end{equation}\n\n\\end_inset\n\nNow the contact integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)t_{n}\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)w_{n}^{\\left(1\\right)}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\sum\\limits_{\\alpha}\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)j_{n}^{\\alpha\\left(1\\right)}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\n\\end{aligned}\n\\,,\\label{eq654}\n\\end{equation}\n\n\\end_inset\n\nand the linearization \n\\begin_inset Formula $D\\delta G_{c}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\sum\\limits_{i=1}^{2}D\\delta G_{c}\\left[\\Delta\\mathbf{u}^{\\left(i\\right)}\\right]+D\\delta G_{c}\\left[\\Delta\\tilde{p}^{\\left(i\\right)}\\right]+\\sum\\limits_{\\alpha}D\\delta G_{c}\\left[\\Delta\\tilde{c}^{\\alpha\\left(i\\right)}\\right]\\,.\\label{eq655}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGap Function\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe gap function \n\\begin_inset Formula $g$\n\\end_inset\n\n,\n representing the distance between the contact surfaces,\n is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}^{\\left(2\\right)}=\\mathbf{x}^{\\left(1\\right)}+g\\mathbf{n}^{\\left(1\\right)},\\quad g=\\left(\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right)\\cdot\\mathbf{n}^{\\left(1\\right)}\\,.\\label{eq656}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of variables associated with motion,\n pressure,\n and concentration,\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{\\left(1\\right)}= & \\Delta\\mathbf{u}^{\\left(1\\right)} & D\\mathbf{x}^{\\left(2\\right)} & =\\Delta\\mathbf{u}^{\\left(2\\right)}+\\mathbf{g}_{\\alpha}^{\\left(2\\right)}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\tilde{p}^{\\left(1\\right)}= & \\Delta\\tilde{p}^{\\left(1\\right)} & D\\tilde{p}^{\\left(2\\right)} & =\\Delta\\tilde{p}^{\\left(2\\right)}+\\frac{\\partial\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\tilde{c}^{\\gamma\\left(1\\right)}= & \\Delta\\tilde{c}^{\\gamma\\left(1\\right)} & D\\tilde{c}^{\\gamma\\left(2\\right)} & =\\Delta\\tilde{c}^{\\gamma\\left(2\\right)}+\\frac{\\partial\\tilde{c}^{\\gamma\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\delta\\mathbf{v}^{\\left(1\\right)} & =\\mathbf{0} & D\\mathbf{v}^{\\left(2\\right)} & =\\frac{\\partial\\delta\\mathbf{v}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\delta\\tilde{p}^{\\left(1\\right)} & =0 & D\\delta\\tilde{p}^{\\left(2\\right)} & =\\frac{\\partial\\delta\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\\\\nD\\delta\\tilde{c}^{\\gamma\\left(1\\right)} & =0 & D\\delta\\tilde{c}^{\\gamma\\left(2\\right)} & =\\frac{\\partial\\delta\\tilde{c}^{\\gamma\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\n\\end{aligned}\n\\,,\\label{eq657}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\eta_{\\left(2\\right)}^{\\alpha}=\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\cdot a^{\\alpha\\beta}\\mathbf{g}_{\\beta}^{\\left(1\\right)}-a^{\\alpha\\beta}g\\mathbf{n}^{\\left(1\\right)}\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\beta}}\\,,\\label{eq658}\n\\end{equation}\n\n\\end_inset\n\nwith \n\\begin_inset Formula $a^{\\alpha\\beta}=\\left(A_{\\alpha\\beta}\\right)^{-1}$\n\\end_inset\n\n and \n\\begin_inset Formula $A_{\\alpha\\beta}=\\mathbf{g}_{\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{g}_{\\beta}^{\\left(2\\right)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-Penalty-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the normal component of the contact traction be described by the penalty function,\n \n\\begin_inset Formula \n\\begin{equation}\nt_{n}=\\begin{cases}\n\\varepsilon_{n}g & g<0\\\\\n0 & g\\geqslant0\n\\end{cases}\\,,\\label{eq659}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{n}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n.\n Similarly,\n let \n\\begin_inset Formula \n\\begin{equation}\n\\begin{cases}\nw_{n}=\\varepsilon_{p}\\pi=\\varepsilon_{p}\\left(\\tilde{p}^{\\left(1\\right)}-\\tilde{p}^{\\left(2\\right)}\\right) & t_{n}<0\\\\\n\\tilde{p}^{\\left(i\\right)}=\\tilde{p}^{\\ast} & t_{n}=0\n\\end{cases}\\,,\\label{eq660}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{cases}\nj_{n}^{\\alpha}=\\varepsilon_{c}\\chi^{\\alpha}=\\varepsilon_{c}\\left(\\tilde{c}^{\\alpha\\left(1\\right)}-\\tilde{c}^{\\alpha\\left(2\\right)}\\right) & t_{n}<0\\\\\n\\tilde{c}^{\\alpha\\left(i\\right)}=\\tilde{c}^{\\alpha\\ast} & t_{n}=0\n\\end{cases}\\,,\\label{eq661}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varepsilon_{c}$\n\\end_inset\n\n are penalty factors associated with \n\\begin_inset Formula $w_{n}\\equiv w_{n}^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $j_{n}^{\\alpha}\\equiv j_{n}^{\\alpha\\left(1\\right)}$\n\\end_inset\n\n,\n respectively.\n It follows that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}Dt_{n} & =\\varepsilon_{n}\\left(\\Delta\\mathbf{u}^{\\left(2\\right)}-\\Delta\\mathbf{u}^{\\left(1\\right)}+\\mathbf{g}_{\\alpha}^{\\left(2\\right)}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\right)\\cdot\\mathbf{n}^{\\left(1\\right)}\\\\\nDw_{n} & =\\varepsilon_{p}\\left(\\Delta\\tilde{p}^{\\left(1\\right)}-\\Delta\\tilde{p}^{\\left(2\\right)}-\\frac{\\partial\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\right)\\\\\nDj_{n}^{\\gamma} & =\\varepsilon_{c}\\left(\\Delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\Delta\\tilde{c}^{\\gamma\\left(2\\right)}-\\frac{\\partial\\tilde{c}^{\\gamma\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\Delta\\eta_{\\left(2\\right)}^{\\alpha}\\right)\n\\end{aligned}\n\\,.\\label{eq662}\n\\end{equation}\n\n\\end_inset\n\nGiven these relations,\n it can be shown that the directional derivatives of the various terms appearing in the integrand of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(t_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right)=\\\\\n & J_{\\eta}^{\\left(1\\right)}\\varepsilon_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\left(\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & -J_{\\eta}^{\\left(1\\right)}t_{n}\\frac{\\partial\\delta\\mathbf{v}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\cdot\\left(\\mathbf{n}^{\\left(2\\right)}\\otimes\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\right)\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +t_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq665}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(w_{n}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\right)=\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\varepsilon_{p}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\left(\\Delta\\tilde{p}^{\\left(1\\right)}-\\Delta\\tilde{p}^{\\left(2\\right)}\\right)\\\\\n & +J_{\\eta}^{\\left(1\\right)}\\left[\\varepsilon_{p}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\frac{\\partial\\tilde{p}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha}+w_{n}\\frac{\\partial\\delta\\tilde{p}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\right]\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +w_{n}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq666}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(j_{n}^{\\gamma}\\left(\\delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\delta\\tilde{c}^{\\gamma\\left(2\\right)}\\right)\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\right)=\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\varepsilon_{c}\\left(\\delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\delta\\tilde{c}^{\\gamma\\left(2\\right)}\\right)\\left(\\Delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\Delta\\tilde{c}^{\\gamma\\left(2\\right)}\\right)\\\\\n & +J_{\\eta}^{\\left(1\\right)}\\varepsilon_{c}\\left(\\delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\delta\\tilde{c}^{\\gamma\\left(2\\right)}\\right)\\frac{\\partial\\tilde{c}^{\\gamma\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\alpha}^{\\left(1\\right)}\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +J_{\\eta}^{\\left(1\\right)}j_{n}^{\\gamma}\\frac{\\partial\\delta\\tilde{c}^{\\gamma\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\alpha}^{\\left(1\\right)}\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +j_{n}^{\\gamma}\\left(\\delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\delta\\tilde{c}^{\\gamma\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\label{eq667}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}^{\\left(1\\right)}=\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:MP-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral may be discretized as \n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left[t_{n}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{n}^{\\left(1\\right)}+w_{n}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)+\\sum\\limits_{\\gamma}j_{n}^{\\gamma}\\left(\\delta\\tilde{c}^{\\gamma\\left(1\\right)}-\\delta\\tilde{c}^{\\gamma\\left(2\\right)}\\right)\\right]\\,.\\label{eq668}\n\\end{equation}\n\n\\end_inset\n\nThe variables may be interpolated over each element face according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\mathbf{v}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\mathbf{v}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\mathbf{u}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\mathbf{u}_{c}^{\\left(1\\right)} & \\Delta\\mathbf{u}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\delta\\tilde{p}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\tilde{p}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\tilde{p}_{c}^{\\left(1\\right)} & \\Delta\\tilde{p}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\delta\\tilde{c}^{\\gamma\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\tilde{c}_{a}^{\\gamma\\left(1\\right)} & \\delta\\tilde{c}^{\\gamma\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\tilde{c}_{b}^{\\gamma\\left(2\\right)}\\\\\n\\Delta\\tilde{c}^{\\gamma\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\tilde{c}_{c}^{\\gamma\\left(1\\right)} & \\Delta\\tilde{c}^{\\gamma\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\tilde{c}_{d}^{\\gamma\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq669}\n\\end{equation}\n\n\\end_inset\n\nThen,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\alpha\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\beta\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{\\left(1\\right)}\\\\\nw_{a}^{\\left(1\\right)}\\\\\nj_{a}^{\\alpha\\left(1\\right)}\\\\\nj_{a}^{\\beta\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{b}^{\\left(2\\right)} & \\delta\\tilde{p}_{b}^{\\left(2\\right)} & \\delta\\tilde{c}_{b}^{\\alpha\\left(2\\right)} & \\delta\\tilde{c}_{b}^{\\beta\\left(2\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{b,k}^{\\left(2\\right)}\\\\\nw_{b,k}^{\\left(2\\right)}\\\\\nj_{b,k}^{\\alpha\\left(2\\right)}\\\\\nj_{b,k}^{\\beta\\left(2\\right)}\n\\end{array}\\right]\\right)\\,,\n\\end{aligned}\n\\label{eq670}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\left(1\\right)} & =t_{n}N_{a}^{\\left(1\\right)}\\mathbf{n}^{\\left(1\\right)} & \\mathbf{f}_{b,k}^{\\left(2\\right)} & =-t_{n}N_{b}^{\\left(2\\right)}\\mathbf{n}^{\\left(1\\right)}\\\\\nw_{a}^{\\left(1\\right)} & =w_{n}N_{a}^{\\left(1\\right)} & w_{b,k}^{\\left(2\\right)} & =-w_{n}N_{b}^{\\left(2\\right)}\\\\\nj_{a}^{\\gamma\\left(1\\right)} & =j_{n}^{\\gamma}N_{a}^{\\left(1\\right)} & j_{b,k}^{\\gamma\\left(2\\right)} & =-j_{n}^{\\gamma}N_{b}^{\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq671}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}-D\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\times\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\alpha\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\beta\\left(1\\right)}\\end{array}\\right]\\cdot\\right.\\\\\n & \\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{ac}^{\\left(1,1\\right)} & 0 & 0 & 0\\\\\n\\mathbf{g}_{ac}^{\\left(1,1\\right)} & g_{ac}^{\\left(1,1\\right)} & 0 & 0\\\\\n\\mathbf{h}_{ac}^{\\alpha\\left(1,1\\right)} & 0 & h_{ac}^{\\alpha\\alpha\\left(1,1\\right)} & 0\\\\\n\\mathbf{h}_{ac}^{\\beta\\left(1,1\\right)} & 0 & 0 & h_{ac}^{\\beta\\beta\\left(1,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{p}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\alpha\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\beta\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & 0 & 0 & 0\\\\\n\\mathbf{g}_{ad,k}^{\\left(1,2\\right)} & g_{ad,k}^{\\left(1,2\\right)} & 0 & 0\\\\\n\\mathbf{h}_{ad,k}^{\\alpha\\left(1,2\\right)} & 0 & h_{ad,k}^{\\alpha\\alpha\\left(1,2\\right)} & 0\\\\\n\\mathbf{h}_{ad,k}^{\\beta\\left(1,2\\right)} & 0 & 0 & h_{ad,k}^{\\beta\\beta\\left(1,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\alpha\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\beta\\left(2\\right)}\n\\end{array}\\right]\\right)\\\\\n & +\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{b,k}^{\\left(2\\right)} & \\delta\\tilde{p}_{b,k}^{\\left(2\\right)} & \\delta\\tilde{c}_{b,k}^{\\alpha\\left(2\\right)} & \\delta\\tilde{c}_{b,k}^{\\beta\\left(2\\right)}\\end{array}\\right]\\cdot\\\\\n & \\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & 0 & 0 & 0\\\\\n\\mathbf{g}_{bc,k}^{\\left(2,1\\right)} & g_{bc,k}^{\\left(2,1\\right)} & 0 & 0\\\\\n\\mathbf{h}_{bc,k}^{\\alpha\\left(2,1\\right)} & 0 & h_{bc,k}^{\\alpha\\alpha\\left(2,1\\right)} & 0\\\\\n\\mathbf{h}_{bc,k}^{\\beta\\left(2,1\\right)} & 0 & 0 & h_{bc,k}^{\\beta\\beta\\left(2,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{p}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\alpha\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\beta\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & 0 & 0 & 0\\\\\n\\mathbf{g}_{bd,k}^{\\left(2,2\\right)} & g_{bd,k}^{\\left(2,2\\right)} & 0 & 0\\\\\n\\mathbf{h}_{bd,k}^{\\alpha\\left(2,2\\right)} & 0 & h_{bd,k}^{\\alpha\\alpha\\left(2,2\\right)} & 0\\\\\n\\mathbf{h}_{bd,k}^{\\beta\\left(2,2\\right)} & 0 & 0 & h_{bd,k}^{\\beta\\beta\\left(2,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\alpha\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\beta\\left(2\\right)}\n\\end{array}\\right]\\right)\\right)\\,,\n\\end{aligned}\n\\label{eq672}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{n}N_{c}^{\\left(1\\right)}\\mathbf{N}^{\\left(1\\right)}+t_{n}\\mathbf{A}_{c}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{n}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{N}^{\\left(1\\right)}\\\\\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & =-N_{c}^{\\left(1\\right)}\\left(\\varepsilon_{n}N_{b}^{\\left(2\\right)}\\mathbf{N}^{\\left(1\\right)}+t_{n}\\mathbf{M}_{b}^{\\left(2\\right)}\\right)-t_{n}N_{b}^{\\left(2\\right)}\\mathbf{A}_{c}^{\\left(1\\right)}\\\\\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & =N_{d}^{\\left(2\\right)}\\left(\\varepsilon_{n}N_{b}^{\\left(2\\right)}\\mathbf{N}^{\\left(1\\right)}+t_{n}\\mathbf{M}_{b}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq673}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{g}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{p}N_{c}^{\\left(1\\right)}\\mathbf{p}^{\\left(1\\right)}-w_{n}\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot\\mathbf{n}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{g}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{p}^{\\left(1\\right)}\\\\\n\\mathbf{g}_{bc,k}^{\\left(2,1\\right)} & =N_{c}^{\\left(1\\right)}\\left(-\\varepsilon_{p}N_{b}^{\\left(2\\right)}\\mathbf{p}^{\\left(1\\right)}+w_{n}\\mathbf{m}_{b}^{\\left(2\\right)}\\right)+w_{n}N_{b}^{\\left(2\\right)}\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot\\mathbf{n}^{\\left(1\\right)}\\\\\n\\mathbf{g}_{bd,k}^{\\left(2,2\\right)} & =N_{d}^{\\left(2\\right)}\\left(\\varepsilon_{p}N_{b}^{\\left(2\\right)}\\mathbf{p}^{\\left(1\\right)}-w_{n}\\mathbf{m}_{b}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq674}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}g_{ac}^{\\left(1,1\\right)} & =-\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)} & g_{ad,k}^{\\left(1,2\\right)} & =\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\ng_{bc,k}^{\\left(2,1\\right)} & =\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)} & g_{bd,k}^{\\left(2,2\\right)} & =-\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq675}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{h}_{ac}^{\\gamma\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(N_{c}^{\\left(1\\right)}\\varepsilon_{c}\\mathbf{q}^{\\gamma\\left(1\\right)}-\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot j_{n}^{\\gamma}\\mathbf{n}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{h}_{ad,k}^{\\gamma\\left(1,2\\right)} & =-N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\varepsilon_{c}\\mathbf{q}^{\\gamma\\left(1\\right)}\\\\\n\\mathbf{h}_{bc,k}^{\\gamma\\left(2,1\\right)} & =N_{b}^{\\left(2\\right)}\\left(-N_{c}^{\\left(1\\right)}\\varepsilon_{c}\\mathbf{q}^{\\gamma\\left(1\\right)}+\\mathbf{A}_{c}^{\\left(1\\right)}\\cdot j_{n}^{\\gamma}\\mathbf{n}^{\\left(1\\right)}\\right)+N_{c}^{\\left(1\\right)}j_{n}^{\\gamma}\\mathbf{m}_{b}^{\\left(2\\right)}\\\\\n\\mathbf{h}_{bd,k}^{\\gamma\\left(2,2\\right)} & =N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\\varepsilon_{c}\\mathbf{q}^{\\gamma\\left(1\\right)}-N_{d}^{\\left(2\\right)}j_{n}^{\\gamma}\\mathbf{m}_{b}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq676}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}h_{ac}^{\\gamma\\gamma\\left(1,1\\right)} & =-\\varepsilon_{c}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\nh_{ad,k}^{\\gamma\\gamma\\left(1,2\\right)} & =\\varepsilon_{c}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\nh_{bc,k}^{\\gamma\\gamma\\left(2,1\\right)} & =\\varepsilon_{c}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\nh_{bd,k}^{\\gamma\\gamma\\left(2,2\\right)} & =-\\varepsilon_{c}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq677}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{N}^{\\left(1\\right)} & =\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)} & \\mathbf{A}_{c}^{\\left(1\\right)} & =\\frac{1}{J_{\\eta}^{\\left(1\\right)}}\\boldsymbol{\\mathcal{A}}\\left\\{ \\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\mathbf{g}_{2}^{\\left(1\\right)}-\\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\mathbf{g}_{1}^{\\left(1\\right)}\\right\\} \\\\\n\\mathbf{M}_{b}^{\\left(2\\right)} & =\\mathbf{n}^{\\left(2\\right)}\\otimes\\mathbf{m}_{b}^{\\left(2\\right)} & \\mathbf{m}_{b}^{\\left(2\\right)} & =\\frac{\\partial N_{b}^{\\left(2\\right)}}{\\partial\\eta_{\\left(2\\right)}^{\\alpha}}\\mathbf{g}_{\\left(2\\right)}^{\\alpha}\\\\\n\\mathbf{p}^{\\left(1\\right)} & =\\frac{\\partial\\tilde{p}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha} & \\mathbf{q}^{\\gamma\\left(1\\right)} & =\\frac{\\partial\\tilde{c}^{\\gamma\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{\\alpha}}\\mathbf{g}_{\\left(1\\right)}^{\\alpha}\n\\end{aligned}\n\\,.\\label{eq678}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nTied Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Tied-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn some situations it is useful to connect two non-conforming meshes together.\n This can be done by defining a tied contact interface.\n In FEBio,\n the tied contact works very similar to the sliding contact interface.\n We need to define a slave surface and a master surface,\n where it is assumed that the slave surface nodes will be tied to the master surface faces.\n\\end_layout\n\n\\begin_layout Subsection\nGap Function\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nJust as in sliding contact,\n we need to define a gap function that measures the distance between the slave and master surface.\n In order to do that,\n we first define the projection of a slave node to the master surface.\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)=\\arg\\min\\limits_{\\mathbf{Y}\\in\\Gamma^{\\left(2\\right)}}\\left\\Vert \\mathbf{X}-\\mathbf{Y}\\right\\Vert \\,.\\label{eq679}\n\\end{equation}\n\n\\end_inset\n\nThis definition is similar to that of the sliding interface,\n except that now the projection is done in the material reference frame.\n This implies that the projection only needs to be calculated once,\n at the beginning of the analysis.\n We can now proceed to the definition of the gap function.\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}\\left(\\mathbf{X}\\right)=\\boldsymbol{\\varphi}^{\\left(1\\right)}\\left(\\mathbf{X}\\right)-\\boldsymbol{\\varphi}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\,.\\label{eq680}\n\\end{equation}\n\n\\end_inset\n\nAn important observation is that the gap function is now a vector quantity since the gap needs to be closed in all direction,\n not just the normal direction as is the case in sliding contact.\n\\end_layout\n\n\\begin_layout Subsection\nTied Contact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWith the definition of the gap function at hand (equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq680\"\nnolink \"false\"\n\n\\end_inset\n\n),\n we can define the contribution to the virtual work equation from the tied contact reaction forces.\n \n\\begin_inset Formula \n\\begin{equation}\nW_{t}=\\int\\limits_{\\Gamma_{c}}\\mathbf{T}\\cdot\\delta\\mathbf{g}\\,d\\Gamma\\,.\\label{eq681}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n \n\\series default\nis the reaction force that enforces the constraint \n\\begin_inset Formula $\\mathbf{g}\\left(\\mathbf{X}\\right)=0$\n\\end_inset\n\n.\n Since we anticipate the use of an augmented Lagrangian formalism,\n we can write this reaction force as follows,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{T}=\\boldsymbol{\\lambda}+\\varepsilon\\mathbf{g}\\,.\\label{eq682}\n\\end{equation}\n\n\\end_inset\n\nThe vector quantity \n\\begin_inset Formula $\\boldsymbol{\\lambda}$\n\\end_inset\n\n is the Lagrangian multiplier and \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is a penalty factor.\n\\end_layout\n\n\\begin_layout Subsection\nLinearization of the Contact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Contact-Linearization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSince equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq681\"\nnolink \"false\"\n\n\\end_inset\n\n is nonlinear we need to calculate the linearization.\n For tied contact,\n this is simply given by the following equation.\n \n\\begin_inset Formula \n\\begin{equation}\n\\Delta W_{t}=\\int\\limits_{\\Gamma_{c}}\\varepsilon\\Delta\\mathbf{g}\\cdot\\delta\\mathbf{g}\\,d\\Gamma\\,.\\label{eq683}\n\\end{equation}\n\n\\end_inset\n\nWhere \n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{g}=\\mathbf{w}^{\\left(1\\right)}-\\mathbf{w}^{\\left(2\\right)}\\label{eq684}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\mathbf{g}=\\Delta\\boldsymbol{\\varphi}^{\\left(1\\right)}\\left(\\mathbf{X}\\right)-\\Delta\\boldsymbol{\\varphi}^{\\left(2\\right)}\\left(\\mathbf{\\bar{Y}}\\left(\\mathbf{X}\\right)\\right)\\,.\\label{eq685}\n\\end{equation}\n\n\\end_inset\n\nWe also introduced the notation \n\\begin_inset Formula $\\mathbf{w}^{\\left(i\\right)}=\\delta\\boldsymbol{\\varphi}^{\\left(i\\right)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe discretization of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq683\"\nnolink \"false\"\n\n\\end_inset\n\n will lead to a contribution to the stiffness matrix.\n Notice that due to symmetry between \n\\begin_inset Formula $\\delta\\mathbf{g}$\n\\end_inset\n\n and\n\\begin_inset Formula $\\Delta\\mathbf{g}$\n\\end_inset\n\n this matrix will be symmetric.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Contact-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq681\"\nnolink \"false\"\n\n\\end_inset\n\n can be discretized as follows.\n First,\n we split the integration over all the slave surface elements.\n \n\\begin_inset Formula \n\\begin{equation}\nW_{t}=\\sum\\limits_{e=1}^{nel}\\int\\limits_{\\Gamma_{c}^{\\left(e\\right)}}\\mathbf{T}\\cdot\\delta\\mathbf{g}\\,d\\Gamma^{\\left(e\\right)}\\,.\\label{eq686}\n\\end{equation}\n\n\\end_inset\n\nThe integration can be approximated by a quadrature rule,\n \n\\begin_inset Formula \n\\begin{equation}\nW_{t}=\\sum\\limits_{e=1}^{nel}\\sum\\limits_{i=1}^{N_{int}^{\\left(e\\right)}}w^{i}j\\left(\\xi_{i}\\right)\\mathbf{T}\\left(\\xi_{i}\\right)\\cdot\\delta\\mathbf{g}\\left(\\xi_{i}\\right)\\,.\\label{eq687}\n\\end{equation}\n\n\\end_inset\n\nIf we use a nodally integrated elements,\n we have \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w}^{\\left(1\\right)}\\left(\\xi_{i}\\right) & =\\mathbf{c}_{i}^{\\left(1\\right)}\\,,\\\\\n\\mathbf{w}^{\\left(2\\right)}\\left(\\xi_{i}\\right) & =\\sum\\limits_{j}N_{j}\\left(\\bar{\\xi}_{i}\\right)\\mathbf{c}_{j}^{\\left(2\\right)}\\,,\n\\end{aligned}\n\\label{eq688}\n\\end{equation}\n\n\\end_inset\n\nso that,\n \n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{g}\\left(\\xi_{i}\\right)=\\mathbf{c}_{i}^{\\left(1\\right)}-\\sum\\limits_{j}N_{j}\\left(\\bar{\\xi}_{i}\\right)\\mathbf{c}_{j}^{\\left(2\\right)}\\,.\\label{eq689}\n\\end{equation}\n\n\\end_inset\n\nWe can now write the contact integral \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq686\"\nnolink \"false\"\n\n\\end_inset\n\n in its final form,\n \n\\begin_inset Formula \n\\begin{equation}\nW_{t}=\\sum\\limits_{e=1}^{nel}\\sum\\limits_{i=1}^{N_{int}^{\\left(e\\right)}}w^{i}j\\left(\\xi_{i}\\right)\\left(\\mathbf{N}\\left(\\xi_{i}\\right)\\mathbf{T}\\left(\\xi_{i}\\right)\\right)\\cdot\\delta\\boldsymbol{\\Phi}\\,,\\label{eq690}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\delta\\boldsymbol{\\Phi}^{T}\\left(\\xi_{i}\\right)=\\left[\\begin{array}{ccccc}\n\\mathbf{c}_{i}^{\\left(1\\right)} & \\mathbf{c}_{1}^{\\left(2\\right)} & \\mathbf{c}_{2}^{\\left(2\\right)} & \\cdots & \\mathbf{c}_{n}^{\\left(2\\right)}\\end{array}\\right]\\,,\\label{eq691}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}\\left(\\xi_{i}\\right)=\\left[\\begin{array}{cccc}\n\\mathbf{I} & -\\mathbf{N}_{1} & \\cdots & -\\mathbf{N}_{n}\\end{array}\\right]\\,,\\label{eq692}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{N}_{i}=\\left[\\begin{array}{ccc}\nN_{i} & 0 & 0\\\\\n0 & N_{i} & 0\\\\\n0 & 0 & N_{i}\n\\end{array}\\right]\\,.\\label{eq693}\n\\end{equation}\n\n\\end_inset\n\nFor the linearized tied contact integral \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq683\"\nnolink \"false\"\n\n\\end_inset\n\n,\n a similar discretization procedure leads to,\n \n\\begin_inset Formula \n\\begin{equation}\n\\Delta W_{t}=\\sum\\limits_{e=1}^{nel}\\sum\\limits_{i=1}^{N_{int}^{\\left(e\\right)}}w^{i}j\\left(\\xi_{i}\\right)\\Delta\\boldsymbol{\\Phi}\\cdot\\mathbf{K}_{c}\\delta\\boldsymbol{\\Phi}\\,,\\label{eq694}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{K}_{c}=\\varepsilon\\mathbf{N}^{T}\\mathbf{N}\\,.\\label{eq695}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nTied Biphasic Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Tied-Biphasic-Contact\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nContact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Material\"\nnolink \"false\"\n\n\\end_inset\n\n for a review of biphasic materials,\n and \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"true\"\n\n\\end_inset\n\n for additional details on biphasic contact.\n The contact interface is defined between surfaces \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n Due to continuity requirements on the traction and fluxes,\n the external virtual work resulting from contact tractions \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}$\n\\end_inset\n\n and solvent fluxes \n\\begin_inset Formula $w_{n}^{\\left(i\\right)}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2)$\n\\end_inset\n\n may be combined into the contact integral \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\left(1\\right)}da^{\\left(1\\right)}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)w_{n}^{\\left(1\\right)}da^{\\left(1\\right)}\\,.\n\\end{aligned}\n\\label{eq696}\n\\end{equation}\n\n\\end_inset\n\nTo evaluate and linearize \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n,\n define the covariant basis vectors on each surface as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{\\left(i\\right)}=\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}}{\\partial\\eta_{\\left(i\\right)}^{\\alpha}},\\quad\\alpha=1,2,\\label{eq697}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n represents the spatial position of points on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\eta_{\\left(i\\right)}^{\\alpha}$\n\\end_inset\n\n represent the parametric coordinates of that point.\n The unit outward normal on each surface is then given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{\\left(i\\right)}=\\frac{\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}}{\\left|\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}\\right|}\\,.\\label{eq698}\n\\end{equation}\n\n\\end_inset\n\nNow the contact integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}\\,\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)w_{n}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\n\\end{aligned}\n\\,,\\label{eq699}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{t}\\equiv\\mathbf{t}^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{n}\\equiv w_{n}^{\\left(1\\right)}$\n\\end_inset\n\n.\n The linearization \n\\begin_inset Formula $D\\delta G_{c}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\sum\\limits_{i=1}^{2}D\\delta G_{c}\\left[\\Delta\\mathbf{u}^{\\left(i\\right)}\\right]+D\\delta G_{c}\\left[\\Delta p^{\\left(i\\right)}\\right]\\,.\\label{eq700}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGap Function\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe vector gap function \n\\begin_inset Formula $\\mathbf{g}$\n\\end_inset\n\n,\n representing the distance between the contact surfaces,\n is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}=\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\,.\\label{eq701}\n\\end{equation}\n\n\\end_inset\n\nThe premise of a tied interface is that the parametric coordinates of \n\\begin_inset Formula $\\mathbf{x}^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n are both invariants (i.e.,\n they are determined in the reference configuration and remain unchanged over time).\n The parametric coordinates of \n\\begin_inset Formula $\\mathbf{x}^{\\left(1\\right)}$\n\\end_inset\n\n correspond to the integration points on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n,\n and those of \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n are evaluated once,\n in the reference configuration,\n by shooting a ray from the integration point on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n to intersect \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n It follows from this premise that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{\\left(1\\right)} & =\\Delta\\mathbf{u}^{\\left(1\\right)} & D\\mathbf{x}^{\\left(2\\right)} & =\\Delta\\mathbf{u}^{\\left(2\\right)}\\\\\nDp^{\\left(1\\right)} & =\\Delta p^{\\left(1\\right)} & Dp^{\\left(2\\right)} & =\\Delta p^{\\left(2\\right)}\\\\\nD\\delta\\mathbf{v}^{\\left(1\\right)} & =\\mathbf{0} & D\\delta\\mathbf{v}^{\\left(2\\right)} & =\\mathbf{0}\\\\\nD\\delta p^{\\left(1\\right)} & =0 & D\\delta p^{\\left(2\\right)} & =0\n\\end{aligned}\n\\,.\\label{eq702}\n\\end{equation}\n\n\\end_inset\n\nIf \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n are not initially conforming,\n continuity of fluid pressure and normal flux will only be enforced within the contact interface;\n unlike the sliding biphasic contact interface (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Contact\"\nnolink \"false\"\n\n\\end_inset\n\n),\n free-draining conditions are not set automatically on regions of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n where a solution for \n\\begin_inset Formula $\\mathbf{g}$\n\\end_inset\n\n was not found.\n Therefore,\n these regions naturally enforce zero fluid flux (impermeable boundary),\n unless an explicit boundary condition on the pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is prescribed over those regions.\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Penalty-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the tied contact traction be described by the penalty function,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}=\\varepsilon_{n}\\mathbf{g}\\,,\\label{eq703}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{n}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n.\n Similarly,\n let \n\\begin_inset Formula \n\\begin{equation}\nw_{n}=\\varepsilon_{p}\\pi=\\varepsilon_{p}\\left(p^{\\left(1\\right)}-p^{\\left(2\\right)}\\right)\\,,\\label{eq704}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{p}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n.\n It follows that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{t} & =\\varepsilon_{n}\\left(\\Delta\\mathbf{u}^{\\left(2\\right)}-\\Delta\\mathbf{u}^{\\left(1\\right)}\\right)\\,,\\\\\nDw_{n} & =\\varepsilon_{p}\\left(\\Delta p^{\\left(1\\right)}-\\Delta p^{\\left(2\\right)}\\right)\\,.\n\\end{aligned}\n\\label{eq705}\n\\end{equation}\n\n\\end_inset\n\nGiven these relations,\n it can be shown that the directional derivatives of the various terms appearing in the integrand of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(J_{\\eta}^{\\left(1\\right)}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}\\right)=\\\\\n & \\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\varepsilon_{n}J_{\\eta}^{\\left(1\\right)}\\mathbf{I}\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}\\otimes\\left[\\left(\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}-\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\right]\n\\end{aligned}\n\\,,\\label{eq706}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(w_{n}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)J_{\\eta}^{\\left(1\\right)}\\right)=\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\varepsilon_{p}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)\\left(\\Delta p^{\\left(1\\right)}-\\Delta p^{\\left(2\\right)}\\right)\\\\\n & +w_{n}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq707}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}^{\\left(1\\right)}=\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Discretization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral may be discretized as \n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left[\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}+w_{n}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)\\right]\\,.\\label{eq708}\n\\end{equation}\n\n\\end_inset\n\nThe variables may be interpolated over each element face according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\mathbf{v}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\mathbf{v}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\mathbf{u}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\mathbf{u}_{c}^{\\left(1\\right)} & \\Delta\\mathbf{u}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\delta p^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta p_{a}^{\\left(1\\right)} & \\delta p^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta p_{b}^{\\left(2\\right)}\\\\\n\\Delta p^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta p_{c}^{\\left(1\\right)} & \\Delta p^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta p_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq709}\n\\end{equation}\n\n\\end_inset\n\nThen,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta p_{a}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{\\left(1\\right)}\\\\\nw_{a}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & \\left.+\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{b,k}^{\\left(1\\right)} & \\delta p_{b,k}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{b,k}^{\\left(1\\right)}\\\\\nw_{b,k}^{\\left(1\\right)}\n\\end{array}\\right]\\right)\n\\end{aligned}\n\\,,\\label{eq710}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}\\mathbf{t} & \\mathbf{f}_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}\\mathbf{t}\\\\\nw_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}w_{n} & w_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}w_{n}\n\\end{aligned}\n\\,.\\label{eq711}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}-D\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\\\\n & \\times\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta p_{a}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{ac}^{\\left(1,1\\right)} & \\mathbf{0}\\\\\n\\mathbf{k}_{ac}^{\\left(1,1\\right)} & k_{ac}^{\\left(1,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta p_{c}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\right.\\\\\n & +\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{ad,k}^{\\left(1,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta p_{d}^{\\left(2\\right)}\n\\end{array}\\right]\\right)\\\\\n & +\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{b,k}^{\\left(2\\right)} & \\delta p_{b,k}^{\\left(2\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & \\mathbf{0}\\\\\n\\mathbf{k}_{bc,k}^{\\left(2,1\\right)} & k_{bc,k}^{\\left(2,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta p_{c}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{bd,k}^{\\left(2,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta p_{d}^{\\left(2\\right)}\n\\end{array}\\right]\\right)\\right)\\,,\n\\end{aligned}\n\\label{eq712}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{n}N_{c}^{\\left(1\\right)}\\mathbf{I}+\\mathbf{t}\\otimes\\mathbf{a}_{c}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{n}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{I}\\\\\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & =-N_{b}^{\\left(2\\right)}\\left(\\varepsilon_{n}N_{c}^{\\left(1\\right)}\\mathbf{I}+\\mathbf{t}\\otimes\\mathbf{a}_{c}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & =\\varepsilon_{n}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\\mathbf{I}\n\\end{aligned}\n\\,,\\label{eq713}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}w_{n}\\mathbf{a}_{c}^{\\left(1\\right)}\\\\\n\\mathbf{k}_{bc,k}^{\\left(2,1\\right)} & =-N_{b}^{\\left(2\\right)}w_{n}\\mathbf{a}_{c}^{\\left(1\\right)}\n\\end{aligned}\n\\,,\\label{eq714}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}k_{ac}^{\\left(1,1\\right)} & =-\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{ad,k}^{\\left(1,2\\right)} & =\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\nk_{bc,k}^{\\left(2,1\\right)} & =\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{bd,k}^{\\left(2,2\\right)} & =-\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq715}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}_{c}^{\\left(1\\right)}=\\frac{1}{J_{\\eta}^{\\left(1\\right)}}\\mathbf{n}^{\\left(1\\right)}\\times\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\\,.\\label{eq716}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nTied Multiphasic Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Tied-Multiphasic-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for a review of multiphasic materials,\n and \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian12\"\nliteral \"true\"\n\n\\end_inset\n\n for additional details on contact interfaces involving solutes.\n The contact interface is defined between surfaces \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n Due to continuity requirements on the traction and fluxes,\n the external virtual work resulting from contact tractions \n\\begin_inset Formula $\\mathbf{t}^{\\left(i\\right)}$\n\\end_inset\n\n,\n solvent fluxes \n\\begin_inset Formula $w_{n}^{\\left(i\\right)}$\n\\end_inset\n\n and solute fluxes \n\\begin_inset Formula $j_{n}^{\\alpha\\left(i\\right)}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2)$\n\\end_inset\n\n may be combined into the contact integral \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)w_{n}^{\\left(1\\right)}\\,da^{\\left(1\\right)}\\\\\n & +\\sum_{\\alpha}\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)j_{n}^{\\alpha\\left(1\\right)}\\,da^{\\left(1\\right)}\\,.\n\\end{aligned}\n\\label{eq696-1}\n\\end{equation}\n\n\\end_inset\n\nNote that the summation in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq696-1\"\nnolink \"false\"\n\n\\end_inset\n\n is performed only over solutes that are present on both sides of the contact interface.\n No special treatment is needed for solutes that only belong to one side,\n since the natural boundary condition for these solutes enforces zero normal flux across the contact interface.\n\\end_layout\n\n\\begin_layout Standard\nTo evaluate and linearize \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n,\n define the covariant basis vectors on each surface as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{\\left(i\\right)}=\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}}{\\partial\\eta_{\\left(i\\right)}^{\\alpha}},\\quad\\alpha=1,2,\\label{eq697-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n represents the spatial position of points on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\eta_{\\left(i\\right)}^{\\alpha}$\n\\end_inset\n\n represent the parametric coordinates of that point.\n The unit outward normal on each surface is then given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{\\left(i\\right)}=\\frac{\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}}{\\left|\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}\\right|}\\,.\\label{eq698-1}\n\\end{equation}\n\n\\end_inset\n\nNow the contact integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}\\,\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{p}^{\\left(1\\right)}-\\delta\\tilde{p}^{\\left(2\\right)}\\right)w_{n}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\sum_{\\alpha}\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)j_{n}^{\\alpha}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\n\\end{aligned}\n\\,,\\label{eq699-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{t}\\equiv\\mathbf{t}^{\\left(1\\right)}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{n}\\equiv w_{n}^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $j_{n}^{\\alpha}\\equiv j_{n}^{\\alpha\\left(1\\right)}$\n\\end_inset\n\n.\n The linearization \n\\begin_inset Formula $D\\delta G_{c}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\sum\\limits_{i=1}^{2}D\\delta G_{c}\\left[\\Delta\\mathbf{u}^{\\left(i\\right)}\\right]+D\\delta G_{c}\\left[\\Delta\\tilde{p}^{\\left(i\\right)}\\right]+\\sum_{\\alpha}D\\delta G_{c}\\left[\\Delta\\tilde{c}^{\\alpha\\left(i\\right)}\\right]\\,.\\label{eq700-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGap Function\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-MP-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe vector gap function \n\\begin_inset Formula $\\mathbf{g}$\n\\end_inset\n\n,\n representing the distance between the contact surfaces,\n is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}=\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\,.\\label{eq701-1}\n\\end{equation}\n\n\\end_inset\n\nThe premise of a tied interface is that the parametric coordinates of \n\\begin_inset Formula $\\mathbf{x}^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n are both invariants (i.e.,\n they are determined in the reference configuration and remain unchanged over time).\n The parametric coordinates of \n\\begin_inset Formula $\\mathbf{x}^{\\left(1\\right)}$\n\\end_inset\n\n correspond to the integration points on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n,\n and those of \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n are evaluated once,\n in the reference configuration,\n by shooting a ray from the integration point on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n to intersect \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n It follows from this premise that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{x}^{\\left(i\\right)} & =\\Delta\\mathbf{u}^{\\left(i\\right)} & D\\delta\\mathbf{v}^{\\left(i\\right)} & =\\mathbf{0}\\\\\nD\\tilde{p}^{\\left(i\\right)} & =\\Delta\\tilde{p}^{\\left(i\\right)} & D\\delta\\tilde{p}^{\\left(i\\right)} & =0\\\\\nD\\tilde{c}^{\\alpha\\left(i\\right)} & =\\Delta\\tilde{c}^{\\alpha\\left(i\\right)} & D\\delta\\tilde{c}^{\\alpha\\left(i\\right)} & =0\n\\end{aligned}\n\\quad i=1,2\\,.\\label{eq702-1}\n\\end{equation}\n\n\\end_inset\n\nIf \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n are not initially conforming,\n continuity of effective fluid pressure and solute concentration,\n as well as normal fluid and solute fluxes,\n will only be enforced within the contact interface;\n unlike the sliding multiphasic contact interface (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Multiphasic-Contact\"\nnolink \"false\"\n\n\\end_inset\n\n),\n ambient conditions are not set automatically on regions of \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n where a solution for \n\\begin_inset Formula $\\mathbf{g}$\n\\end_inset\n\n was not found.\n Therefore,\n these regions naturally enforce zero fluid and solute flux (impermeable boundary),\n unless an explicit boundary condition on the effective pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n or solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n are prescribed over those regions.\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-MP-Penalty-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the tied contact traction be described by the penalty function,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}=\\varepsilon_{n}\\mathbf{g}\\,,\\label{eq703-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{n}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n.\n Similarly,\n let \n\\begin_inset Formula \n\\begin{equation}\nw_{n}=\\varepsilon_{p}\\pi=\\varepsilon_{p}\\left(\\tilde{p}^{\\left(1\\right)}-\\tilde{p}^{\\left(2\\right)}\\right)\\,,\\label{eq704-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{p}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n,\n and\n\\begin_inset Formula \n\\begin{equation}\nj_{n}^{\\alpha}=\\varepsilon_{c}\\chi^{\\alpha}=\\varepsilon_{c}^{\\alpha}\\left(\\tilde{c}^{\\alpha\\left(1\\right)}-\\tilde{c}^{\\alpha\\left(2\\right)}\\right)\\,,\\label{eq704-1b}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{c}^{\\alpha}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $j_{n}^{\\alpha}$\n\\end_inset\n\n.\n It follows that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{t} & =\\varepsilon_{n}\\left(\\Delta\\mathbf{u}^{\\left(2\\right)}-\\Delta\\mathbf{u}^{\\left(1\\right)}\\right)\\,,\\\\\nDw_{n} & =\\varepsilon_{p}\\left(\\Delta\\tilde{p}^{\\left(1\\right)}-\\Delta\\tilde{p}^{\\left(2\\right)}\\right)\\,,\\\\\nDj_{n}^{\\alpha} & =\\varepsilon_{c}^{\\alpha}\\left(\\Delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\Delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)\\,.\n\\end{aligned}\n\\label{eq705-1}\n\\end{equation}\n\n\\end_inset\n\nGiven these relations,\n it can be shown that the directional derivatives of the various terms appearing in the integrand of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(J_{\\eta}^{\\left(1\\right)}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}\\right)=\\\\\n & \\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\varepsilon_{n}J_{\\eta}^{\\left(1\\right)}\\mathbf{I}\\cdot\\left(\\Delta\\mathbf{u}^{\\left(1\\right)}-\\Delta\\mathbf{u}^{\\left(2\\right)}\\right)\\\\\n & +\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}\\otimes\\left[\\left(\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}-\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}\\right]\n\\end{aligned}\n\\,,\\label{eq706-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(w_{n}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)J_{\\eta}^{\\left(1\\right)}\\right)=\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\varepsilon_{p}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)\\left(\\Delta\\tilde{p}^{\\left(1\\right)}-\\Delta\\tilde{p}^{\\left(2\\right)}\\right)\\\\\n & +w_{n}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq707-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(j_{n}^{\\alpha}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)J_{\\eta}^{\\left(1\\right)}\\right)=\\\\\n & -J_{\\eta}^{\\left(1\\right)}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)\\varepsilon_{c}^{\\alpha}\\left(\\Delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\Delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)\\\\\n & +j_{n}^{\\alpha}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)\\mathbf{n}^{\\left(1\\right)}\\cdot\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\frac{\\partial\\Delta\\mathbf{u}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\n\\end{aligned}\n\\,,\\label{eq707-1b}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}^{\\left(1\\right)}=\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Discretization-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral may be discretized as \n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left[\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}+w_{n}\\left(\\delta p^{\\left(1\\right)}-\\delta p^{\\left(2\\right)}\\right)+\\sum_{\\alpha}j_{n}^{\\alpha}\\left(\\delta\\tilde{c}^{\\alpha\\left(1\\right)}-\\delta\\tilde{c}^{\\alpha\\left(2\\right)}\\right)\\right]\\,.\\label{eq708-1}\n\\end{equation}\n\n\\end_inset\n\nThe variables may be interpolated over each element face according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\mathbf{v}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\mathbf{v}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\mathbf{u}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\mathbf{u}_{c}^{\\left(1\\right)} & \\Delta\\mathbf{u}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\delta\\tilde{p}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta p^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\tilde{p}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\tilde{p}_{c}^{\\left(1\\right)} & \\Delta\\tilde{p}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\delta\\tilde{c}^{\\alpha\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\tilde{c}_{a}^{\\alpha\\left(1\\right)} & \\delta c^{\\alpha\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\tilde{c}_{b}^{\\alpha\\left(2\\right)}\\\\\n\\Delta\\tilde{c}^{\\alpha\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\tilde{c}_{c}^{\\alpha\\left(1\\right)} & \\Delta\\tilde{c}^{\\alpha\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\tilde{c}_{d}^{\\alpha\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq709-1}\n\\end{equation}\n\n\\end_inset\n\nThen,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}^{\\alpha\\left(1\\right)} & \\delta\\tilde{c}^{\\beta\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{\\left(1\\right)}\\\\\nw_{a}^{\\left(1\\right)}\\\\\nj_{n}^{\\alpha\\left(1\\right)}\\\\\nj_{n}^{\\beta\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & \\left.+\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{b,k}^{\\left(1\\right)} & \\delta\\tilde{p}_{b,k}^{\\left(1\\right)} & \\delta\\tilde{c}_{b,k}^{\\alpha\\left(1\\right)} & \\delta\\tilde{c}_{b,k}^{\\beta\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{b,k}^{\\left(1\\right)}\\\\\nw_{b,k}^{\\left(1\\right)}\\\\\nj_{b,k}^{\\alpha\\left(1\\right)}\\\\\nj_{b,k}^{\\beta\\left(1\\right)}\n\\end{array}\\right]\\right)\n\\end{aligned}\n\\,,\\label{eq710-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}\\mathbf{t} & \\mathbf{f}_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}\\mathbf{t}\\\\\nw_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}w_{n} & w_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}w_{n}\\\\\nj_{a}^{\\gamma\\left(1\\right)} & =N_{a}^{\\left(1\\right)}j_{n}^{\\gamma} & j_{b,k}^{\\gamma\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}j_{n}^{\\gamma}\n\\end{aligned}\n\\,.\\label{eq711-1}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}-D\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\\\\n & \\times\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\tilde{p}_{a}^{\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\alpha\\left(1\\right)} & \\delta\\tilde{c}_{a}^{\\beta\\left(1\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{ac}^{\\left(1,1\\right)} & \\mathbf{0} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{k}_{ac}^{\\left(1,1\\right)} & k_{ac}^{\\left(1,1\\right)} & 0 & 0\\\\\n\\mathbf{k}_{ac}^{\\alpha\\left(1,1\\right)} & 0 & k_{ac}^{\\alpha\\alpha\\left(1,1\\right)} & 0\\\\\n\\mathbf{k}_{ac}^{\\beta\\left(1,1\\right)} & 0 & 0 & k_{ac}^{\\beta\\beta\\left(1,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{p}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\alpha\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\beta\\left(1\\right)}\n\\end{array}\\right]\\right.\\right.\\\\\n & +\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & \\mathbf{0} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{ad,k}^{\\left(1,2\\right)} & 0 & 0\\\\\n\\mathbf{0} & 0 & k_{ad,k}^{\\alpha\\alpha\\left(1,2\\right)} & 0\\\\\n\\mathbf{0} & 0 & 0 & k_{ad,k}^{\\beta\\beta\\left(1,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\alpha\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\beta\\left(2\\right)}\n\\end{array}\\right]\\right)\\\\\n & +\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\delta\\mathbf{v}_{b,k}^{\\left(2\\right)} & \\delta\\tilde{p}_{b,k}^{\\left(2\\right)} & \\delta\\tilde{c}_{b,k}^{\\alpha\\left(2\\right)} & \\delta\\tilde{c}_{b,k}^{\\beta\\left(2\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & \\mathbf{0} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{k}_{bc,k}^{\\left(2,1\\right)} & k_{bc,k}^{\\left(2,1\\right)} & 0 & 0\\\\\n\\mathbf{k}_{bc,k}^{\\alpha\\left(2,1\\right)} & 0 & k_{bc,k}^{\\alpha\\alpha\\left(2,1\\right)} & 0\\\\\n\\mathbf{k}_{bc,k}^{\\beta\\left(2,1\\right)} & 0 & 0 & k_{bc,k}^{\\beta\\beta\\left(2,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{p}_{c}^{\\left(1\\right)}\\\\\n\\Delta\\tilde{c}_{c}^{\\alpha\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cccc}\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & \\mathbf{0} & \\mathbf{0} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{bd,k}^{\\left(2,2\\right)} & 0 & 0\\\\\n\\mathbf{0} & 0 & k_{bd,k}^{\\alpha\\alpha\\left(2,2\\right)} & 0\\\\\n\\mathbf{0} & 0 & 0 & k_{bd,k}^{\\beta\\beta\\left(2,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{u}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{p}_{d}^{\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\alpha\\left(2\\right)}\\\\\n\\Delta\\tilde{c}_{d}^{\\beta\\left(2\\right)}\n\\end{array}\\right]\\right)\\right)\\,,\n\\end{aligned}\n\\label{eq712-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}\\left(\\varepsilon_{n}N_{c}^{\\left(1\\right)}\\mathbf{I}+\\mathbf{t}\\otimes\\mathbf{a}_{c}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{n}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\mathbf{I}\\\\\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & =-N_{b}^{\\left(2\\right)}\\left(\\varepsilon_{n}N_{c}^{\\left(1\\right)}\\mathbf{I}+\\mathbf{t}\\otimes\\mathbf{a}_{c}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & =\\varepsilon_{n}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\\mathbf{I}\n\\end{aligned}\n\\,,\\label{eq713-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{k}_{ac}^{\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}w_{n}\\mathbf{a}_{c}^{\\left(1\\right)}\\\\\n\\mathbf{k}_{bc,k}^{\\left(2,1\\right)} & =-N_{b}^{\\left(2\\right)}w_{n}\\mathbf{a}_{c}^{\\left(1\\right)}\\\\\n\\mathbf{k}_{ac}^{\\alpha\\left(1,1\\right)} & =N_{a}^{\\left(1\\right)}j_{n}^{\\alpha}\\mathbf{a}_{c}^{\\left(1\\right)}\\\\\n\\mathbf{k}_{bc,k}^{\\alpha\\left(2,1\\right)} & =-N_{b}^{\\left(2\\right)}j_{n}^{\\alpha}\\mathbf{a}_{c}^{\\left(1\\right)}\n\\end{aligned}\n\\,,\\label{eq714-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}k_{ac}^{\\left(1,1\\right)} & =-\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{ad,k}^{\\left(1,2\\right)} & =\\varepsilon_{p}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\nk_{bc,k}^{\\left(2,1\\right)} & =\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{bd,k}^{\\left(2,2\\right)} & =-\\varepsilon_{p}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,,\\label{eq715-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}k_{ac}^{\\gamma\\gamma\\left(1,1\\right)} & =-\\varepsilon_{c}^{\\gamma}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{ad,k}^{\\gamma\\gamma\\left(1,2\\right)} & =\\varepsilon_{c}^{\\gamma}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\nk_{bc,k}^{\\gamma\\gamma\\left(2,1\\right)} & =\\varepsilon_{c}^{\\gamma}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{bd,k}^{\\gamma\\gamma\\left(2,2\\right)} & =-\\varepsilon_{c}^{\\gamma}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\label{eq715-1b}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}_{c}^{\\left(1\\right)}=\\frac{1}{J_{\\eta}^{\\left(1\\right)}}\\mathbf{n}^{\\left(1\\right)}\\times\\left(\\mathbf{g}_{2}^{\\left(1\\right)}\\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{1}}-\\mathbf{g}_{1}^{\\left(1\\right)}\\frac{\\partial N_{c}^{\\left(1\\right)}}{\\partial\\eta_{\\left(1\\right)}^{2}}\\right)\\,.\\label{eq716-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nTied Fluid Interface\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Tied-Fluid-Interface\"\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Standard\nUnder isothermal conditions which preclude any heat flux and for which the entropy is zero,\n the interface jump conditions derived from mass,\n linear momentum and energy balance across an interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n which is permeable to the fluid are\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\rho\\mathbf{u}^{\\Gamma}\\right]\\right]\\cdot\\mathbf{n}^{\\Gamma}=0\\,,\\label{eq:jump-mass}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\boldsymbol{\\sigma}-\\rho\\mathbf{u}^{\\Gamma}\\otimes\\mathbf{u}^{\\Gamma}\\right]\\right]\\cdot\\mathbf{n}^{\\Gamma}=\\mathbf{0}\\,,\\label{eq:jump-linear-momentum}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\rho\\left(\\psi+\\frac{1}{2}\\mathbf{u}^{\\Gamma}\\cdot\\mathbf{u}^{\\Gamma}\\right)\\mathbf{u}^{\\Gamma}-\\boldsymbol{\\sigma}^{T}\\cdot\\mathbf{u}^{\\Gamma}\\right]\\right]\\cdot\\mathbf{n}^{\\Gamma}=0\\label{eq:jump-energy}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{u}^{\\Gamma}\\equiv\\mathbf{v}-\\mathbf{v}^{\\Gamma}\\label{eq:Gamma-diffusive-velocity}\n\\end{equation}\n\n\\end_inset\n\n \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n are the mass density,\n velocity,\n stress tensor and specific free energy of the fluid,\n respectively,\n and \n\\begin_inset Formula $\\mathbf{v}^{\\Gamma}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}^{\\Gamma}$\n\\end_inset\n\n are the velocity of,\n and outward unit normal to \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n respectively.\n A tied fluid interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n which allows fluid to cross \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n must enforce these continuity requirements.\n For fluid analyses,\n we let\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\tau}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure and \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the viscous shear stress.\n Susbtituting this relation into the energy jump of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:jump-energy\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n neglecting the contribution of \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n relative to that of \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n and making use of the jump condition from mass balance in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:jump-mass\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find that\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\left[\\psi+\\frac{p}{\\rho}+\\frac{1}{2}\\mathbf{u}^{\\Gamma}\\cdot\\mathbf{u}^{\\Gamma}\\right]\\right]=0\\,.\\label{eq:jump-energy-redux}\n\\end{equation}\n\n\\end_inset\n\nSince \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n and \n\\begin_inset Formula $p$\n\\end_inset\n\n are functions of state that only depend on the fluid volume ratio \n\\begin_inset Formula $J$\n\\end_inset\n\n (in an isothermal analysis,\n see eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:elastic-pressure\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n letting \n\\begin_inset Formula $J$\n\\end_inset\n\n be continuous across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n implies from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-integrated\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n that \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is continuous across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n;\n thus,\n from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mass-balance-integrated\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n we conclude that \n\\begin_inset Formula $\\mathbf{u}^{\\Gamma}\\cdot\\mathbf{n}$\n\\end_inset\n\n is continuous,\n hence eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:jump-energy-redux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is satisfied.\n In other words,\n letting \n\\begin_inset Formula $\\left[\\left[J\\right]\\right]=0$\n\\end_inset\n\n is a sufficient (but not necessary) condition to satisfy the jump conditions from mass and energy balance,\n as long as we also enforce \n\\begin_inset Formula $\\left[\\left[\\mathbf{u}^{\\Gamma}\\right]\\right]\\cdot\\mathbf{n}=0$\n\\end_inset\n\n.\n For a viscous fluid,\n we also assume that the no-slip condition holds,\n implying that our jump requirement for the fluid velocity is even more stringent,\n namely \n\\begin_inset Formula $\\left[\\left[\\mathbf{u}^{\\Gamma}\\right]\\right]=\\mathbf{0}$\n\\end_inset\n\n (or equivalently,\n \n\\begin_inset Formula $\\left[\\left[\\mathbf{v}\\right]\\right]=\\mathbf{0}$\n\\end_inset\n\n).\n According to the above relations between the stress and pressure,\n and recognizing that \n\\begin_inset Formula $\\left[\\left[J\\right]\\right]=0$\n\\end_inset\n\n implies \n\\begin_inset Formula $\\left[\\left[p\\right]\\right]=0$\n\\end_inset\n\n,\n we may also simplify the linear momentum jump condition of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:jump-linear-momentum\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to \n\\begin_inset Formula $\\left[\\left[\\boldsymbol{\\tau}\\right]\\right]\\cdot\\mathbf{n}^{\\Gamma}=\\mathbf{0}$\n\\end_inset\n\n.\n Therefore,\n the jump conditions that we need to enforce for a tied fluid interface \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\left[\\left[J\\right]\\right] & =0\\\\\n\\left[\\left[\\mathbf{v}\\right]\\right] & =\\mathbf{0}\\\\\n\\left[\\left[\\boldsymbol{\\tau}\\right]\\right]\\cdot\\mathbf{n}^{\\Gamma} & =\\mathbf{0}\n\\end{aligned}\n\\,.\\label{eq:jump-tied-fluid}\n\\end{equation}\n\n\\end_inset\n\nThese jump conditions are consistent with the choice of nodal degrees of freedom for fluid analyses (namely,\n \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n).\n On a tied (or sliding) fluid interface,\n we don't have continuity of the mesh,\n therefore we can only enforce continuity of \n\\begin_inset Formula $J$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n by enforcing continuity of the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n and viscous traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}\\equiv\\boldsymbol{\\tau}\\cdot\\mathbf{n}^{\\Gamma}$\n\\end_inset\n\n.\n However,\n in our fluid finite element formulation,\n \n\\begin_inset Formula $p$\n\\end_inset\n\n is not a natural boundary condition since it is calculated directly from \n\\begin_inset Formula $J$\n\\end_inset\n\n.\n Instead,\n as explained in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Computational-Fluid-Dynamics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the natural boundary condition for the fluid dilatation governing equation is continuity of \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n across \n\\begin_inset Formula $\\Gamma$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\left[\\left[\\mathbf{v}\\right]\\right]\\cdot\\mathbf{n}^{\\Gamma}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nContact Integral\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Fluid-Contact-Integral\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Fluid-Mechanics\"\nnolink \"false\"\n\n\\end_inset\n\n for a review of fluid materials,\n and \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian18\"\nliteral \"true\"\n\n\\end_inset\n\n for additional details on the FEBio fluid solver.\n The tied fluid interface is defined between surfaces \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n Due to continuity requirements on the viscous traction and normal fluid velocity,\n the external virtual work resulting from tractions \n\\begin_inset Formula $\\mathbf{t}^{\\tau\\left(i\\right)}$\n\\end_inset\n\n and normal velocities \n\\begin_inset Formula $v_{n}^{\\left(i\\right)}$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2)$\n\\end_inset\n\n may be combined into the tied interface integral \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\tau\\left(1\\right)}da^{\\left(1\\right)}\\\\\n & -\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta J^{\\left(1\\right)}-\\delta J^{\\left(2\\right)}\\right)v_{n}^{\\left(1\\right)}da^{\\left(1\\right)}\\,.\n\\end{aligned}\n\\label{eq696-2}\n\\end{equation}\n\n\\end_inset\n\nTo evaluate and linearize \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n,\n define the covariant basis vectors on each surface as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}_{\\alpha}^{\\left(i\\right)}=\\frac{\\partial\\mathbf{x}^{\\left(i\\right)}}{\\partial\\eta_{\\left(i\\right)}^{\\alpha}},\\quad\\alpha=1,2,\\label{eq697-2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n represents the spatial position of points on \n\\begin_inset Formula $\\gamma^{\\left(i\\right)}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\eta_{\\left(i\\right)}^{\\alpha}$\n\\end_inset\n\n represent the parametric coordinates of that point.\n The unit outward normal on each surface is then given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}^{\\left(i\\right)}=\\frac{\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}}{\\left|\\mathbf{g}_{1}^{\\left(i\\right)}\\times\\mathbf{g}_{2}^{\\left(i\\right)}\\right|}\\,.\\label{eq698-2}\n\\end{equation}\n\n\\end_inset\n\nNow the contact integral may be rewritten as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\tau}\\,\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\\\\\n & +\\int_{\\gamma^{\\left(1\\right)}}\\left(\\delta J^{\\left(1\\right)}-\\delta J^{\\left(2\\right)}\\right)v_{n}\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|\\,d\\eta_{\\left(1\\right)}^{1}d\\eta_{\\left(1\\right)}^{2}\n\\end{aligned}\n\\,,\\label{eq699-2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{t}^{\\tau}\\equiv\\mathbf{t}^{\\tau\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $v_{n}\\equiv v_{n}^{\\left(1\\right)}$\n\\end_inset\n\n.\n The linearization \n\\begin_inset Formula $D\\delta G_{c}$\n\\end_inset\n\n of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n has the form \n\\begin_inset Formula \n\\begin{equation}\nD\\delta G_{c}=\\sum\\limits_{i=1}^{2}D\\delta G_{c}\\left[\\Delta\\mathbf{v}^{\\left(i\\right)}\\right]+D\\delta G_{c}\\left[\\Delta J^{\\left(i\\right)}\\right]\\,.\\label{eq700-2}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGap Functions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Fluid-Gap-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe premise of a tied interface is that the parametric coordinates of coincident points \n\\begin_inset Formula $\\mathbf{x}^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n are both invariants (i.e.,\n they are determined in the reference configuration and remain unchanged over time).\n The parametric coordinates of \n\\begin_inset Formula $\\mathbf{x}^{\\left(1\\right)}$\n\\end_inset\n\n correspond to the integration points on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n,\n and those of \n\\begin_inset Formula $\\mathbf{x}^{\\left(2\\right)}$\n\\end_inset\n\n are evaluated once by shooting a ray from the integration point on \n\\begin_inset Formula $\\gamma^{\\left(1\\right)}$\n\\end_inset\n\n to intersect \n\\begin_inset Formula $\\gamma^{\\left(2\\right)}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe vector gap function \n\\begin_inset Formula $\\mathbf{g}$\n\\end_inset\n\n,\n representing the difference between velocities across the interface,\n is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{g}=\\mathbf{v}^{\\left(2\\right)}-\\mathbf{v}^{\\left(1\\right)}\\,,\\label{TFI-vt-gap}\n\\end{equation}\n\n\\end_inset\n\nWe may similarly define the scalar gap function \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n,\n representing the difference between fluid dilatations (or volume ratios) across the interface,\n\\begin_inset Formula \n\\begin{equation}\n\\pi=J^{\\left(2\\right)}-J^{\\left(1\\right)}\\,.\\label{eq:TFI-e-gap}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Penalty-Method-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLet the tied interface viscous traction be described by the penalty function,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{t}^{\\tau}=\\varepsilon_{t}\\mathbf{g}\\,,\\label{eq:TFI-t-penalty}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{t}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n.\n The penalty factor \n\\begin_inset Formula $\\varepsilon_{t}$\n\\end_inset\n\n is expressed in units of viscosity per length.\n Therefore,\n in an auto-penalty scheme,\n it may be scaled to the ratio of fluid viscosity to element thickness.\n Similarly,\n let \n\\begin_inset Formula \n\\begin{equation}\nv_{n}=\\varepsilon_{n}\\pi=\\varepsilon_{n}\\left(J^{\\left(2\\right)}-J^{\\left(1\\right)}\\right)\\,,\\label{eq:TFI-v-penalty}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varepsilon_{n}$\n\\end_inset\n\n is a penalty factor associated with \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $\\varepsilon_{n}$\n\\end_inset\n\n has units of velocity.\n It follows that \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\mathbf{t}^{\\tau} & =\\varepsilon_{t}\\left(\\Delta\\mathbf{v}^{\\left(2\\right)}-\\Delta\\mathbf{v}^{\\left(1\\right)}\\right)\\,,\\\\\nDv_{n} & =\\varepsilon_{n}\\left(\\Delta J^{\\left(2\\right)}-\\Delta J^{\\left(1\\right)}\\right)\\,.\n\\end{aligned}\n\\label{eq:TFI-penalty-linearization}\n\\end{equation}\n\n\\end_inset\n\nGiven these relations,\n it can be shown that the directional derivatives of the various terms appearing in the integrand of \n\\begin_inset Formula $\\delta G_{c}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned} & -D\\left(J_{\\eta}^{\\left(1\\right)}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\tau}\\right)=\\\\\n & \\varepsilon_{t}J_{\\eta}^{\\left(1\\right)}\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\left(\\Delta\\mathbf{v}^{\\left(1\\right)}-\\Delta\\mathbf{v}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq:TFI-G-linearization-v}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\nD\\left(v_{n}\\left(\\delta J^{\\left(1\\right)}-\\delta J^{\\left(2\\right)}\\right)J_{\\eta}^{\\left(1\\right)}\\right)=-\\varepsilon_{n}J_{\\eta}^{\\left(1\\right)}\\left(\\delta J^{\\left(1\\right)}-\\delta J^{\\left(2\\right)}\\right)\\left(\\Delta J^{\\left(1\\right)}-\\Delta J^{\\left(2\\right)}\\right)\\,,\\label{eq:TFI-G-linearization-e}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{\\eta}^{\\left(1\\right)}=\\left|\\mathbf{g}_{1}^{\\left(1\\right)}\\times\\mathbf{g}_{2}^{\\left(1\\right)}\\right|$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDiscretization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-BP-Discretization-2\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact integral may be discretized as \n\\begin_inset Formula \n\\begin{equation}\n\\delta G_{c}=\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left[\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{t}^{\\tau}-\\left(\\delta J^{\\left(1\\right)}-\\delta J^{\\left(2\\right)}\\right)v_{n}\\right]\\,.\\label{eq:TFI-discrete-1}\n\\end{equation}\n\n\\end_inset\n\nThe variables may be interpolated over each element face according to \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta\\mathbf{v}^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta\\mathbf{v}^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta\\mathbf{v}_{b}^{\\left(2\\right)}\\\\\n\\Delta\\mathbf{v}^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta\\mathbf{v}_{c}^{\\left(1\\right)} & \\Delta\\mathbf{v}^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta\\mathbf{v}_{d}^{\\left(2\\right)}\\\\\n\\delta J^{\\left(1\\right)} & =\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}N_{a}^{\\left(1\\right)}\\delta J_{a}^{\\left(1\\right)} & \\delta J^{\\left(2\\right)} & =\\sum\\limits_{b=1}^{m^{\\left(2\\right)}}N_{b}^{\\left(2\\right)}\\delta J_{b}^{\\left(2\\right)}\\\\\n\\Delta J^{\\left(1\\right)} & =\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}N_{c}^{\\left(1\\right)}\\Delta J_{c}^{\\left(1\\right)} & \\Delta J^{\\left(2\\right)} & =\\sum\\limits_{d=1}^{m^{\\left(2\\right)}}N_{d}^{\\left(2\\right)}\\Delta J_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq:TFI-discrete-2}\n\\end{equation}\n\n\\end_inset\n\nThen,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta J_{a}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{a}^{\\left(1\\right)}\\\\\nv_{a}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & \\left.+\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{b,k}^{\\left(1\\right)} & \\delta J_{b,k}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\mathbf{f}_{b,k}^{\\left(1\\right)}\\\\\nv_{b,k}^{\\left(1\\right)}\n\\end{array}\\right]\\right)\n\\end{aligned}\n\\,,\\label{eq:TFI-discrete-3}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{f}_{a}^{\\left(1\\right)} & =N_{a}^{\\left(1\\right)}\\mathbf{t}^{\\tau} & \\mathbf{f}_{b,k}^{\\left(2\\right)} & =-N_{b}^{\\left(2\\right)}\\mathbf{t}^{\\tau}\\\\\nw_{a}^{\\left(1\\right)} & =-N_{a}^{\\left(1\\right)}v_{n} & w_{b,k}^{\\left(2\\right)} & =N_{b}^{\\left(2\\right)}v_{n}\n\\end{aligned}\n\\,.\\label{eq:TFI-discrete-4}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}-D\\delta G_{c} & =\\sum\\limits_{e=1}^{n_{e}^{\\left(1\\right)}}\\sum\\limits_{k=1}^{n_{\\mbox{int}}^{\\left(e\\right)}}W_{k}J_{\\eta}^{\\left(1\\right)}\\\\\n & \\times\\left(\\sum\\limits_{a=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{a}^{\\left(1\\right)} & \\delta J_{a}^{\\left(1\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{ac}^{\\left(1,1\\right)} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{ac}^{\\left(1,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{v}_{c}^{\\left(1\\right)}\\\\\n\\Delta J_{c}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\right.\\\\\n & +\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{ad,k}^{\\left(1,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{v}_{d}^{\\left(2\\right)}\\\\\n\\Delta J_{d}^{\\left(2\\right)}\n\\end{array}\\right]\\right)\\\\\n & +\\sum\\limits_{b=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\delta\\mathbf{v}_{b,k}^{\\left(2\\right)} & \\delta J_{b,k}^{\\left(2\\right)}\\end{array}\\right]\\cdot\\left(\\sum\\limits_{c=1}^{m^{\\left(1\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{bc,k}^{\\left(2,1\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{v}_{c}^{\\left(1\\right)}\\\\\n\\Delta J_{c}^{\\left(1\\right)}\n\\end{array}\\right]\\right.\\\\\n & +\\left.\\left.\\sum\\limits_{d=1}^{m_{k}^{\\left(2\\right)}}\\left[\\begin{array}{cc}\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & \\mathbf{0}\\\\\n\\mathbf{0} & k_{bd,k}^{\\left(2,2\\right)}\n\\end{array}\\right]\\cdot\\left[\\begin{array}{c}\n\\Delta\\mathbf{v}_{d}^{\\left(2\\right)}\\\\\n\\Delta J_{d}^{\\left(2\\right)}\n\\end{array}\\right]\\right)\\right)\\,,\n\\end{aligned}\n\\label{eq:TFI-discrete-5}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{K}_{ac}^{\\left(1,1\\right)} & =\\varepsilon_{t}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\left(\\mathbf{I}-\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{ad,k}^{\\left(1,2\\right)} & =-\\varepsilon_{t}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\left(\\mathbf{I}-\\mathbf{n}^{\\left(2\\right)}\\otimes\\mathbf{n}^{\\left(2\\right)}\\right)\\\\\n\\mathbf{K}_{bc,k}^{\\left(2,1\\right)} & =-\\varepsilon_{t}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\left(\\mathbf{I}-\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{bd,k}^{\\left(2,2\\right)} & =\\varepsilon_{t}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\\left(\\mathbf{I}-\\mathbf{n}^{\\left(2\\right)}\\otimes\\mathbf{n}^{\\left(2\\right)}\\right)\n\\end{aligned}\n\\,,\\label{eq:TFI-discrete-6}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}k_{ac}^{\\left(1,1\\right)} & =-K\\varepsilon_{n}N_{a}^{\\left(1\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{ad,k}^{\\left(1,2\\right)} & =K\\varepsilon_{n}N_{a}^{\\left(1\\right)}N_{d}^{\\left(2\\right)}\\\\\nk_{bc,k}^{\\left(2,1\\right)} & =K\\varepsilon_{n}N_{b}^{\\left(2\\right)}N_{c}^{\\left(1\\right)}\\\\\nk_{bd,k}^{\\left(2,2\\right)} & =-K\\varepsilon_{n}N_{b}^{\\left(2\\right)}N_{d}^{\\left(2\\right)}\n\\end{aligned}\n\\,.\\label{eq:TFI-discrete-7}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nRigid Connectors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Rigid-Connectors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA \n\\emph on\nrigid connector\n\\emph default\n connects two rigid bodies denoted by \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(2\\right)$\n\\end_inset\n\n.\n The connector origin (e.g.,\n its insertion point) on rigid body \n\\begin_inset Formula $\\left(i\\right)$\n\\end_inset\n\n is located at\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}^{\\left(i\\right)}=\\mathbf{r}^{\\left(i\\right)}+\\boldsymbol{\\Lambda}^{\\left(i\\right)}\\cdot\\mathbf{Z}^{\\left(i\\right)}=\\mathbf{r}^{\\left(i\\right)}+\\mathbf{z}^{\\left(i\\right)}\\,,\\label{eq:rc-origin-positions}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{z}^{\\left(i\\right)}$\n\\end_inset\n\n is the connector origin position relative to the center of mass at the current time,\n whereas \n\\begin_inset Formula $\\mathbf{Z}^{\\left(i\\right)}$\n\\end_inset\n\n is its relative position in the reference configuration,\n when the rotation tensor \n\\begin_inset Formula $\\boldsymbol{\\Lambda}^{\\left(i\\right)}$\n\\end_inset\n\n is equal to the identity tensor.\n The connector is exerting a reaction force \n\\begin_inset Formula $\\mathbf{f}^{\\left(i\\right)}$\n\\end_inset\n\n at \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n and a reaction moment \n\\begin_inset Formula $\\mathbf{m}^{\\left(i\\right)}$\n\\end_inset\n\n on rigid body \n\\begin_inset Formula $\\left(i\\right)$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}+\\mathbf{f}^{\\left(2\\right)}=\\mathbf{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}+\\mathbf{m}^{\\left(2\\right)}=\\mathbf{0}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nRigid body joints,\n such as spherical,\n revolute,\n prismatic,\n cylindrical and planar joints,\n are a special category of rigid connectors that have a large stiffness spring connecting the joint origins on each rigid body,\n in a manner that only allows relative motion along the joint translational degree(s) of freedom.\n Similarly,\n a large stiffness torsional spring connects the rigid bodies in a manner that only allows relative rotation along the joint rotational degree(s) of freedom.\n\\end_layout\n\n\\begin_layout Standard\nOther connectors include springs,\n dampers,\n and contractile forces that may connect rigid bodies.\n Optionally,\n a joint may include a linear damper connecting its origins,\n and an angular damper restricting its relative rotation.\n\\end_layout\n\n\\begin_layout Subsection\nVirtual Work\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Connector-Virtual-Work\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe virtual work of a connector represents the work of external forces on the rigid body;\n it is given by\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\sum_{i=1}^{2}\\delta\\mathbf{v}^{\\left(i\\right)}\\cdot\\mathbf{f}^{\\left(i\\right)}+\\delta\\boldsymbol{\\theta}^{\\left(i\\right)}\\cdot\\mathbf{m}^{\\left(i\\right)}\\,,\\label{eq:rc-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\delta\\mathbf{v}^{\\left(i\\right)}$\n\\end_inset\n\n is the virtual velocity of the joint origin and \n\\begin_inset Formula $\\delta\\boldsymbol{\\theta}^{\\left(i\\right)}$\n\\end_inset\n\n is the virtual angular velocity of rigid body \n\\begin_inset Formula $\\left(i\\right)$\n\\end_inset\n\n.\n Using the above relations for \n\\begin_inset Formula $\\mathbf{f}^{\\left(i\\right)}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{m}^{\\left(i\\right)}$\n\\end_inset\n\n,\n it reduces to\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\left(\\delta\\mathbf{v}^{\\left(1\\right)}-\\delta\\mathbf{v}^{\\left(2\\right)}\\right)\\cdot\\mathbf{f}^{\\left(1\\right)}+\\left(\\delta\\boldsymbol{\\theta}^{\\left(1\\right)}-\\delta\\boldsymbol{\\theta}^{\\left(2\\right)}\\right)\\cdot\\mathbf{m}^{\\left(1\\right)}\\,.\\label{eq:rc-virtual-work-redux}\n\\end{equation}\n\n\\end_inset\n\nThe analysis thus returns the values of the reaction force and moment acting on rigid body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n.\n The virtual velocities at the joint may be evaluated from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rc-origin-positions\"\nnolink \"false\"\n\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{v}^{\\left(i\\right)}=\\delta\\mathbf{r}^{\\left(i\\right)}-\\hat{\\mathbf{z}}^{\\left(i\\right)}\\cdot\\delta\\boldsymbol{\\theta}^{\\left(i\\right)}\\,,\\label{eq:rc-origin-virtual-displacement}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\mathbf{z}}$\n\\end_inset\n\n is the skew-symmetric tensor whose dual vector is \n\\begin_inset Formula $\\mathbf{z}$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $\\hat{\\mathbf{z}}\\cdot\\mathbf{v}=\\mathbf{z}\\times\\mathbf{v}$\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n.\n Substituting this expression into \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rc-virtual-work-redux\"\nnolink \"false\"\n\n\\end_inset\n\n allows us to express the virtual work in terms of the virtual velocities of the centers of mass and the virtual angular velocities of the rigid bodies,\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\theta}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\theta}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}^{\\left(1\\right)}\\\\\n\\hat{\\mathbf{z}}^{\\left(1\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}+\\mathbf{m}^{\\left(1\\right)}\\\\\n-\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\hat{\\mathbf{z}}^{\\left(2\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}-\\mathbf{m}^{\\left(1\\right)}\n\\end{array}\\right]\\,.\\label{eq:rc-virtual-work-final}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen using time discretization in the interval \n\\begin_inset Formula $\\left[t_{n},t_{n+1}\\right]$\n\\end_inset\n\n,\n the external forces and moments may be evaluated at the intermediate time point \n\\begin_inset Formula $t_{n+\\alpha}$\n\\end_inset\n\n using\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{f}_{n+\\alpha}^{\\left(1\\right)} & =\\alpha\\mathbf{f}_{n+1}^{\\left(1\\right)}+\\left(1-\\alpha\\right)\\mathbf{f}_{n}^{\\left(1\\right)}\\\\\n\\left(\\hat{\\mathbf{z}}^{\\left(1\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\\right)_{n+\\alpha} & =\\alpha\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)}\\cdot\\mathbf{f}_{n+1}^{\\left(1\\right)}+\\left(1-\\alpha\\right)\\hat{\\mathbf{z}}_{n}^{\\left(1\\right)}\\cdot\\mathbf{f}_{n}^{\\left(1\\right)}\\\\\n\\mathbf{m}_{n+\\alpha}^{\\left(1\\right)} & =\\alpha\\mathbf{m}_{n+1}^{\\left(1\\right)}+\\left(1-\\alpha\\right)\\mathbf{m}_{n}^{\\left(1\\right)}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nWe solve for \n\\begin_inset Formula $\\delta G=0$\n\\end_inset\n\n using Newton's method in the usual manner,\n by evaluating the linearization of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n along increments in the rigid body degrees of freedom at \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\delta G+\\sum_{i=1}^{2}D\\delta G\\left[\\Delta\\mathbf{r}^{\\left(i\\right)}\\right]+D\\delta G\\left[\\Delta\\boldsymbol{\\theta}^{\\left(i\\right)}\\right]\\approx0\\,.\\label{eq:rc-Newton-method}\n\\end{equation}\n\n\\end_inset\n\nAssuming that\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{f}_{n+1}^{\\left(1\\right)}=\\left[\\begin{array}{cccc}\n\\mathbf{K}_{fr}^{\\left(1\\right)} & \\mathbf{K}_{f\\theta}^{\\left(1\\right)} & \\mathbf{K}_{fr}^{\\left(2\\right)} & \\mathbf{K}_{f\\theta}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:rc-Df}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{m}_{n+1}^{\\left(1\\right)}=\\left[\\begin{array}{cccc}\n\\mathbf{K}_{mr}^{\\left(1\\right)} & \\mathbf{K}_{m\\theta}^{\\left(1\\right)} & \\mathbf{K}_{mr}^{\\left(2\\right)} & \\mathbf{K}_{m\\theta}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:rc-Dm}\n\\end{equation}\n\n\\end_inset\n\nit follows that\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}D\\delta G= & \\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\theta}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\theta}^{\\left(2\\right)}\\end{array}\\right]\\times\\\\\n & \\alpha\\left[\\begin{array}{cccc}\n\\mathbf{K}_{fr}^{\\left(1\\right)} & \\mathbf{K}_{f\\theta}^{\\left(1\\right)} & \\mathbf{K}_{fr}^{\\left(2\\right)} & \\mathbf{K}_{f\\theta}^{\\left(2\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)}\\cdot\\mathbf{K}_{fr}^{\\left(1\\right)}+\\mathbf{K}_{mr}^{\\left(1\\right)} & \\hat{\\mathbf{f}}_{n+\\alpha}^{\\left(1\\right)}\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)}+\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)}\\cdot\\mathbf{K}_{f\\theta}^{\\left(1\\right)}+\\mathbf{K}_{m\\theta}^{\\left(1\\right)} & \\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)}\\cdot\\mathbf{K}_{fr}^{\\left(2\\right)}+\\mathbf{K}_{mr}^{\\left(2\\right)} & \\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)}\\cdot\\mathbf{K}_{f\\theta}^{\\left(2\\right)}+\\mathbf{K}_{m\\theta}^{\\left(2\\right)}\\\\\n-\\mathbf{K}_{fr}^{\\left(1\\right)} & -\\mathbf{K}_{f\\theta}^{\\left(1\\right)} & -\\mathbf{K}_{fr}^{\\left(2\\right)} & -\\mathbf{K}_{f\\theta}^{\\left(2\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\cdot\\mathbf{K}_{fr}^{\\left(1\\right)}-\\mathbf{K}_{mr}^{\\left(1\\right)} & -\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\cdot\\mathbf{K}_{f\\theta}^{\\left(1\\right)}-\\mathbf{K}_{m\\theta}^{\\left(1\\right)} & -\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\cdot\\mathbf{K}_{fr}^{\\left(2\\right)}-\\mathbf{K}_{mr}^{\\left(2\\right)} & \\hat{\\mathbf{-f}}_{n+\\alpha}^{\\left(1\\right)}\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}-\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\cdot\\mathbf{K}_{f\\theta}^{\\left(2\\right)}-\\mathbf{K}_{m\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\\\\n & \\times\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\n\\end{aligned}\n\\label{eq:rc-linearized-work}\n\\end{equation}\n\n\\end_inset\n\nIt becomes immediately apparent that the stiffness matrix for a rigid connector is not symmetric.\n Therefore,\n rigid body dynamics should be analyzed using non-symmetric solvers.\n\\end_layout\n\n\\begin_layout Subsection\nJoint Axes\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Axes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nJoint axes are used to define the directions of degrees of freedom in rigid joints,\n which represent one of the major categories of rigid connectors.\n The axes are defined with respect to a body-based coordinate system centered at the origin of a joint,\n given by the orthonormal triad \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1}^{\\left(i\\right)},\\mathbf{e}_{2}^{\\left(i\\right)},\\mathbf{e}_{3}^{\\left(i\\right)}\\right\\} $\n\\end_inset\n\n for rigid body \n\\begin_inset Formula $i$\n\\end_inset\n\n (\n\\begin_inset Formula $i=1,2$\n\\end_inset\n\n).\n In the reference configuration,\n the bases coincide on both rigid bodies,\n \n\\begin_inset Formula $\\mathbf{e}_{j}^{\\left(1\\right)}=\\mathbf{e}_{j}^{\\left(2\\right)}\\equiv\\mathbf{E}_{j}$\n\\end_inset\n\n (\n\\begin_inset Formula $j=1,2,3$\n\\end_inset\n\n),\n where \n\\begin_inset Formula $\\mathbf{E}_{j}$\n\\end_inset\n\n is the \n\\begin_inset Formula $j-$\n\\end_inset\n\nth basis vector in the reference configuration.\n Thus,\n at any time \n\\begin_inset Formula $t$\n\\end_inset\n\n,\n we may evaluate the basis vectors as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{e}_{j}^{\\left(i\\right)}=\\boldsymbol{\\Lambda}^{\\left(i\\right)}\\cdot\\mathbf{E}_{j}\\label{eq:ja-basis-currnet-time}\n\\end{equation}\n\n\\end_inset\n\nUsing this relation,\n the linearization of a basis vector along increments in the rigid body motion is given by\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{e}_{j}^{\\left(i\\right)}=-\\hat{\\mathbf{e}}_{j}^{\\left(i\\right)}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(i\\right)}\\,,\\label{eq:ja-basis-vector-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhich shows a dependence only on \n\\begin_inset Formula $\\Delta\\boldsymbol{\\theta}^{\\left(i\\right)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nRelative Joint Motion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Relative-Joint-Motion\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe position of a joint in each rigid body \n\\begin_inset Formula $i$\n\\end_inset\n\n is given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rc-origin-positions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The relative motion across a joint is given by the relative translation\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\,,\\label{eq:rjm-relative-translation}\n\\end{equation}\n\n\\end_inset\n\nand the relative rotation\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{Q}=\\boldsymbol{\\Lambda}^{\\left(2\\right)}\\cdot\\left(\\boldsymbol{\\Lambda}^{\\left(1\\right)}\\right)^{T}\\equiv\\exp\\left[\\hat{\\boldsymbol{\\theta}}\\right]\\,,\\label{eq:rjm-relative-rotation}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\hat{\\boldsymbol{\\theta}}=-\\boldsymbol{\\mathcal{E}}\\cdot\\boldsymbol{\\theta}$\n\\end_inset\n\n is the skew-symmetric tensor with dual vector \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n.\n As usual,\n \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n is a vector whose direction represents the axis of rotation and whose magnitude is the angle of (counter-clockwise) rotation about that axis.\n To report the relative motion of a joint,\n we may project \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\theta}$\n\\end_inset\n\n along the basis vectors \n\\begin_inset Formula $\\mathbf{e}_{j}^{\\left(1\\right)}=\\boldsymbol{\\Lambda}^{\\left(1\\right)}\\cdot\\mathbf{E}_{j}$\n\\end_inset\n\n of the first rigid body.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}x_{j} & =\\mathbf{x}\\cdot\\mathbf{e}_{j}^{\\left(1\\right)}\\\\\n\\theta_{j} & =\\boldsymbol{\\theta}\\cdot\\mathbf{e}_{j}^{\\left(1\\right)}\n\\end{aligned}\n\\,.\\label{eq:rjm-projected-motion}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nJoint Reaction Forces and Moments\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Forces-Moments\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReaction forces are used to constrain the degrees of freedom of a joint that connects two rigid bodies.\n Typically,\n these forces are generated by very stiff springs and dampers that only allow unrestricted motion along the joint degrees of freedom.\n\\end_layout\n\n\\begin_layout Subsubsection\nReaction Forces from Springs\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Springs\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reaction force \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}$\n\\end_inset\n\n generated by a spring acting on rigid body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{f}^{\\left(1\\right)}=\\boldsymbol{\\lambda}+\\varepsilon_{c}\\mathbf{P}\\cdot\\mathbf{g}}\\,,\\label{eq:jf-spring-force}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{g}=\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}}\\label{eq:jf-gap-function}\n\\end{equation}\n\n\\end_inset\n\nis a gap function that represents the vector distance between the joint origins,\n \n\\begin_inset Formula $\\boldsymbol{\\lambda}$\n\\end_inset\n\n is the (optional) Lagrange multiplier used when invoking the augmented Lagrangian method,\n and \n\\begin_inset Formula $\\varepsilon_{c}$\n\\end_inset\n\n is a penalty parameter that represents the spring stiffness.\n The tensor \n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n is a projection that limits the reaction force to the directions that are not free to move.\n In general,\n there are three possible options for \n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n:\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}=\\begin{cases}\n\\mathbf{P}_{1}=\\mathbf{I} & \\text{constrain relative translation along all directions}\\\\\n\\mathbf{P}_{2}=\\mathbf{I}-\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)} & \\text{constrain relative translation within plane normal to }\\mathbf{n}^{\\left(1\\right)}\\\\\n\\mathbf{P}_{3}=\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)} & \\text{constrain relative translation along }\\mathbf{n}^{\\left(1\\right)}\n\\end{cases}\\,.\\label{eq:jf-P-projection}\n\\end{equation}\n\n\\end_inset\n\nFor example,\n \n\\begin_inset Formula $\\mathbf{P}_{1}=\\mathbf{I}$\n\\end_inset\n\n in a spherical or revolute joint,\n whereas \n\\begin_inset Formula $\\mathbf{P}_{2}=\\mathbf{I}-\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n in an unconstrained prismatic joint,\n with \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n representing the axis of motion;\n and \n\\begin_inset Formula $\\mathbf{P}_{3}=\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n in an unconstrained planar joint,\n with \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n representing the normal to the plane.\n Note that in all cases we choose to define the axis in the basis of rigid body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n.\n If the constraint is enforced properly,\n then \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n should be the same as \n\\begin_inset Formula $\\mathbf{n}^{\\left(2\\right)}$\n\\end_inset\n\n,\n within a user-defined numerical tolerance.\n In practice,\n we let \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}\\equiv\\mathbf{e}_{1}^{\\left(1\\right)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe linearization of \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:jf-spring-force\"\nnolink \"false\"\n\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{f}^{\\left(1\\right)}=\\varepsilon_{c}\\left(\\mathbf{P}\\cdot D\\mathbf{g}+D\\mathbf{P}\\cdot\\mathbf{g}\\right)\\,.\\label{eq:jf-linearized-spring-force}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of the gap function produces\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}\\cdot D\\mathbf{g}=\\left[\\begin{array}{cccc}\n-\\mathbf{P} & \\mathbf{P}\\cdot\\hat{\\mathbf{z}}^{\\left(1\\right)} & \\mathbf{P} & -\\mathbf{P}\\cdot\\hat{\\mathbf{z}}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:jf-linearization-1st}\n\\end{equation}\n\n\\end_inset\n\nwhereas the linearization of the three possible projections yields\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{P}\\cdot\\mathbf{g}=\\mathbf{Q}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\,,\\label{eq:jf-linearization-2nd}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{Q}=\\begin{cases}\n\\mathbf{Q}_{1} & =\\mathbf{0}\\\\\n\\mathbf{Q}_{2} & =\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\cdot\\mathbf{g}\\right)\\mathbf{I}+\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{g}\\right]\\cdot\\hat{\\mathbf{n}}^{\\left(1\\right)}\\\\\n\\mathbf{Q}_{3} & =-\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\cdot\\mathbf{g}\\right)\\mathbf{I}+\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{g}\\right]\\cdot\\hat{\\mathbf{n}}^{\\left(1\\right)}\n\\end{cases}\\,.\\label{eq:jf-linearization-Q}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n in the expression for \n\\begin_inset Formula $D\\mathbf{f}^{\\left(1\\right)}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rc-Df\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we have\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{fr}^{\\left(1\\right)} & =-\\varepsilon_{c}\\mathbf{P}\\\\\n\\mathbf{K}_{f\\theta}^{\\left(1\\right)} & =\\varepsilon_{c}\\left(\\mathbf{P}\\cdot\\hat{\\mathbf{z}}^{\\left(1\\right)}+\\mathbf{Q}\\right)\\\\\n\\mathbf{K}_{fr}^{\\left(2\\right)} & =\\varepsilon_{c}\\mathbf{P}\\\\\n\\mathbf{K}_{f\\theta}^{\\left(2\\right)} & =-\\varepsilon_{c}\\mathbf{P}\\cdot\\hat{\\mathbf{z}}^{\\left(2\\right)}\n\\end{aligned}\n}\\,.\\label{eq:jf-spring-stiffness}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReaction Moments from Torsional Springs\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Torsional-Springs\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reaction moment \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}$\n\\end_inset\n\n generated by a torsional spring acting on rigid body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{m}^{\\left(1\\right)}=\\boldsymbol{\\mu}+\\varepsilon_{r}\\boldsymbol{\\gamma}}\\label{eq:jm-joint-moment}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\boldsymbol{\\gamma}=\\frac{1}{2}\\sum_{j=1}^{k}\\mathbf{e}_{j}^{\\left(1\\right)}\\times\\mathbf{e}_{j}^{\\left(2\\right)}}\\label{eq:jm-angular-gap}\n\\end{equation}\n\n\\end_inset\n\nis the angular gap function between the bases of rigid bodies \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(2\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{\\mu}$\n\\end_inset\n\n is the (optional) Lagrange multiplier used when invoking the augmented Lagrangian method,\n and \n\\begin_inset Formula $\\varepsilon_{r}$\n\\end_inset\n\n is a penalty parameter representing the torsional spring stiffness.\n We consider three cases for the choice of \n\\begin_inset Formula $\\boldsymbol{\\gamma}$\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\gamma}=\\begin{cases}\nk=3 & \\text{constrain relative rotation along all directions}\\\\\nk=1 & \\text{maintain free relative rotation along }\\mathbf{e}_{1}^{\\left(i\\right)}\\\\\nk=0 & \\text{maintain free relative rotation along all directions}\n\\end{cases}\\label{eq:jm-cases}\n\\end{equation}\n\n\\end_inset\n\nFor example,\n \n\\begin_inset Formula $k=3$\n\\end_inset\n\n is used to model the reaction moment in a prismatic joint,\n whereas \n\\begin_inset Formula $k=1$\n\\end_inset\n\n is used with revolute,\n cylindrical and planar joints.\n\\end_layout\n\n\\begin_layout Standard\nNow,\n the linearization produces\n\\begin_inset Formula \n\\[\nD\\mathbf{m}^{\\left(1\\right)}=\\varepsilon_{r}D\\boldsymbol{\\gamma}\\,,\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\nD\\boldsymbol{\\gamma}=\\mathbf{W}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}-\\mathbf{W}^{T}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{W} & =\\frac{1}{2}\\sum_{j=1}^{k}\\hat{\\mathbf{e}}_{j}^{\\left(2\\right)}\\cdot\\hat{\\mathbf{e}}_{j}^{\\left(1\\right)}\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nTherefore,\n in the expression for \n\\begin_inset Formula $D\\mathbf{m}^{\\left(1\\right)}$\n\\end_inset\n\n in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rc-Dm\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we have\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{mr}^{\\left(1\\right)} & =\\mathbf{0}\\\\\n\\mathbf{K}_{m\\theta}^{\\left(1\\right)} & =\\varepsilon_{r}\\mathbf{W}\\\\\n\\mathbf{K}_{mr}^{\\left(2\\right)} & =\\mathbf{0}\\\\\n\\mathbf{K}_{m\\theta}^{\\left(2\\right)} & =-\\varepsilon_{r}\\mathbf{W}^{T}\n\\end{aligned}\n}\\,.\\label{eq:jm-spring-stiffness}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReaction Forces from Dampers\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Linear-Dampers\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reaction force \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}$\n\\end_inset\n\n on a damper is\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{f}^{\\left(1\\right)}=\\chi_{c}\\mathbf{P}\\cdot\\dot{\\mathbf{g}}}\\,,\\label{eq:df-damper-force}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dot{\\mathbf{g}}$\n\\end_inset\n\n is the time rate of change of the gap function,\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\dot{\\mathbf{g}}=\\mathbf{v}^{\\left(2\\right)}-\\mathbf{v}^{\\left(1\\right)}}\\,,\\label{eq:df-relative-velocity}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n represents a projection as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Joint-Springs\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In particular,\n \n\\begin_inset Formula $\\mathbf{P}_{1}$\n\\end_inset\n\n is used with spherical and revolute joints;\n \n\\begin_inset Formula $\\mathbf{P}_{2}$\n\\end_inset\n\n is used with prismatic and cylindrical joints;\n and \n\\begin_inset Formula $\\mathbf{P}_{3}$\n\\end_inset\n\n is used with planar joints.\n The parameter \n\\begin_inset Formula $\\chi_{c}$\n\\end_inset\n\n represents the damping coefficient.\n\\end_layout\n\n\\begin_layout Standard\nThe velocities of the insertion points are given by \n\\begin_inset Formula $\\mathbf{v}^{\\left(i\\right)}=\\dot{\\mathbf{r}}^{\\left(i\\right)}+\\boldsymbol{\\omega}^{\\left(i\\right)}\\times\\mathbf{z}^{\\left(i\\right)}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{\\omega}^{\\left(i\\right)}$\n\\end_inset\n\n is the rigid body angular velocity.\n The linearization of \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{f}^{\\left(1\\right)}=\\varepsilon_{c}\\left(\\mathbf{P}\\cdot D\\dot{\\mathbf{g}}+D\\mathbf{P}\\cdot\\dot{\\mathbf{g}}\\right)\\,,\\label{eq:df-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}\\cdot D\\dot{\\mathbf{g}}=\\left[\\begin{array}{cccc}\n-\\mathbf{A} & \\mathbf{B}^{\\left(1\\right)} & \\mathbf{A} & -\\mathbf{B}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:df-linearization-1st}\n\\end{equation}\n\n\\end_inset\n\nwith\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{A} & =\\frac{\\gamma}{\\beta\\Delta t}\\mathbf{P}\\\\\n\\mathbf{B}^{\\left(i\\right)} & =\\mathbf{P}\\cdot\\left(\\frac{\\gamma}{\\beta\\Delta t}\\hat{\\mathbf{z}}^{\\left(i\\right)}\\cdot\\mathbf{T}^{T}\\left(\\boldsymbol{\\theta}^{\\left(i\\right)}\\right)+\\hat{\\boldsymbol{\\omega}}\\cdot\\hat{\\mathbf{z}}^{\\left(i\\right)}\\right)\n\\end{aligned}\n\\,.\\label{eq:df-A-B}\n\\end{equation}\n\n\\end_inset\n\nRecall that \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n are the Newmark parameters,\n and \n\\begin_inset Formula $\\mathbf{T}\\left(\\boldsymbol{\\theta}\\right)$\n\\end_inset\n\n is given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:td-T-Theta-relation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Similarly,\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{P}\\cdot\\dot{\\mathbf{g}}=\\mathbf{V}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\,,\\label{eq:df-linearization-2nd}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{V}=\\begin{cases}\n\\mathbf{V}_{1} & =\\mathbf{0}\\\\\n\\mathbf{V}_{2} & =\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\cdot\\dot{\\mathbf{g}}\\right)\\mathbf{I}+\\mathbf{n}^{\\left(1\\right)}\\otimes\\dot{\\mathbf{g}}\\right]\\cdot\\hat{\\mathbf{n}}^{\\left(1\\right)}\\\\\n\\mathbf{V}_{3} & =-\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\cdot\\dot{\\mathbf{g}}\\right)\\mathbf{I}+\\mathbf{n}^{\\left(1\\right)}\\otimes\\dot{\\mathbf{g}}\\right]\\cdot\\hat{\\mathbf{n}}^{\\left(1\\right)}\n\\end{cases}\\,.\\label{eq:df-linearization-V}\n\\end{equation}\n\n\\end_inset\n\nCombining these results produces\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{fr}^{\\left(1\\right)} & =-\\chi_{c}\\mathbf{A}\\\\\n\\mathbf{K}_{f\\theta}^{\\left(1\\right)} & =\\chi_{c}\\left(\\mathbf{B}^{\\left(1\\right)}+\\mathbf{V}\\right)\\\\\n\\mathbf{K}_{fr}^{\\left(2\\right)} & =\\chi_{c}\\mathbf{A}\\\\\n\\mathbf{K}_{f\\theta}^{\\left(2\\right)} & =-\\chi_{c}\\mathbf{B}^{\\left(2\\right)}\n\\end{aligned}\n}\\label{eq:df-stiffness}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReaction Moments from Torsional Dampers\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Torsional-Dampers\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reaction moment \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}$\n\\end_inset\n\n arising from a torsional damper is \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{m}^{\\left(1\\right)}=\\chi_{r}\\mathbf{P}\\cdot\\boldsymbol{\\omega}}\\,,\\label{eq:td-moment}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\omega}=\\boldsymbol{\\omega}^{\\left(2\\right)}-\\boldsymbol{\\omega}^{\\left(1\\right)}\\label{eq:td-relative-velocity}\n\\end{equation}\n\n\\end_inset\n\nis the angular velocity of body \n\\begin_inset Formula $\\left(2\\right)$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{P}$\n\\end_inset\n\n is the projection used in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Joint-Springs\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In particular,\n \n\\begin_inset Formula $\\mathbf{P}_{1}$\n\\end_inset\n\n is used for joints that cannot undergo relative rotations along any direction,\n such as prismatic joints;\n \n\\begin_inset Formula $\\mathbf{P}_{2}$\n\\end_inset\n\n is used for joints that can rotate freely along a single axis,\n such as revolute,\n cylindrical and planar joints;\n in addition,\n \n\\begin_inset Formula $\\mathbf{P}=\\mathbf{0}$\n\\end_inset\n\n for spherical joints.\n The linearization of \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{m}^{\\left(1\\right)}=\\chi_{r}\\left(\\mathbf{P}\\cdot D\\boldsymbol{\\omega}+D\\mathbf{P}\\cdot\\boldsymbol{\\omega}\\right)\\,,\\label{eq:td-linearization}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}\\cdot D\\boldsymbol{\\omega}=\\mathbf{C}^{\\left(2\\right)}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}-\\mathbf{C}^{\\left(1\\right)}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\label{eq:td-linearization-1st}\n\\end{equation}\n\n\\end_inset\n\nwith\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{C}^{\\left(i\\right)}=\\frac{\\gamma}{\\beta\\Delta t}\\mathbf{P}\\cdot\\mathbf{T}^{T}\\left(\\boldsymbol{\\theta}^{\\left(i\\right)}\\right)\\,,\\label{eq:td-B}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{P}\\cdot\\boldsymbol{\\omega}=\\mathbf{W}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\,,\\label{eq:td-linearization-2nd}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{W}=\\begin{cases}\n\\mathbf{W}_{1} & =\\mathbf{0}\\\\\n\\mathbf{W}_{2} & =\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\cdot\\boldsymbol{\\omega}\\right)\\mathbf{I}+\\mathbf{n}^{\\left(1\\right)}\\otimes\\boldsymbol{\\omega}\\right]\\cdot\\hat{\\mathbf{n}}^{\\left(1\\right)}\\\\\n\\mathbf{W}_{3} & =-\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\cdot\\boldsymbol{\\omega}\\right)\\mathbf{I}+\\mathbf{n}^{\\left(1\\right)}\\otimes\\boldsymbol{\\omega}\\right]\\cdot\\hat{\\mathbf{n}}^{\\left(1\\right)}\n\\end{cases}\\,.\\label{eq:td-W}\n\\end{equation}\n\n\\end_inset\n\nCombining these results now produces\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{mr}^{\\left(1\\right)} & =\\mathbf{0}\\\\\n\\mathbf{K}_{m\\theta}^{\\left(1\\right)} & =\\chi_{r}\\left(-\\mathbf{C}^{\\left(1\\right)}+\\mathbf{W}\\right)\\\\\n\\mathbf{K}_{mr}^{\\left(2\\right)} & =\\mathbf{0}\\\\\n\\mathbf{K}_{m\\theta}^{\\left(2\\right)} & =\\chi_{r}\\mathbf{C}^{\\left(2\\right)}\n\\end{aligned}\n}\\,.\\label{eq:td-stiffness}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSummary of Reaction Forces and Moment in Joints\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Summary-Forces-Moments-Joints\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"7\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nJoint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpherical\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRevolute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrismatic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCylindrical\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPlanar\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLock\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear spring\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{Q}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{Q}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{Q}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{Q}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{3},\\mathbf{Q}_{3}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{Q}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntorsional spring\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $k=0$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $k=1$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $k=3$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $k=1$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $k=1$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $k=3$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear damper\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{V}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{V}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{V}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{V}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{3},\\mathbf{V}_{3}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{V}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntorsional damper\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{0},\\mathbf{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{W}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{W}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{W}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{2},\\mathbf{W}_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mathbf{P}_{1},\\mathbf{W}_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Joint Forces and Moments\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Joint-Forces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Force at Joint\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Prescribed-Force\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a joint has a translational degree of freedom along \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n,\n there may be a need to prescribe the force \n\\begin_inset Formula $f$\n\\end_inset\n\n along that direction.\n This means that the reaction force \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}$\n\\end_inset\n\n may be supplemented with the force \n\\begin_inset Formula $f\\,\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}^{\\left(1\\right)}=f\\mathbf{n}^{\\left(1\\right)}\\,,\\label{eq:pfj-1}\n\\end{equation}\n\n\\end_inset\n\nand the linearization produces\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{f}^{\\left(1\\right)}=-f\\,\\hat{\\mathbf{n}}^{\\left(1\\right)}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\,.\\label{eq:pfj-2}\n\\end{equation}\n\n\\end_inset\n\nThus,\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{f\\theta}^{\\left(1\\right)}=-f\\,\\hat{\\mathbf{n}}^{\\left(1\\right)}}\\,,\\label{eq:pfj-3}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{fr}^{\\left(1\\right)}=\\mathbf{K}_{fr}^{\\left(2\\right)}=\\mathbf{K}_{f\\theta}^{\\left(2\\right)}=\\mathbf{0}}\\,.\\label{eq:pfj-4}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Moment at Joint\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Prescribed-Moment\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a joint has a rotational degree of freedom along \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n,\n there may be a need to prescribe the moment \n\\begin_inset Formula $m$\n\\end_inset\n\n along that direction.\n This means that the reaction moment \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}$\n\\end_inset\n\n may be supplemented with the moment \n\\begin_inset Formula $m\\,\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}^{\\left(1\\right)}=m\\,\\mathbf{n}^{\\left(1\\right)}\\,,\\label{eq:pmj-1}\n\\end{equation}\n\n\\end_inset\n\nand the linearization produces\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{m}^{\\left(1\\right)}=-m\\,\\hat{\\mathbf{n}}^{\\left(1\\right)}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\,.\\label{eq:pmj-2}\n\\end{equation}\n\n\\end_inset\n\nThus,\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{m\\theta}^{\\left(1\\right)}=-m\\,\\hat{\\mathbf{n}}^{\\left(1\\right)}}\\,,\\label{eq:pmj-3}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{mr}^{\\left(1\\right)}=\\mathbf{K}_{mr}^{\\left(2\\right)}=\\mathbf{K}_{m\\theta}^{\\left(2\\right)}=\\mathbf{0}}\\,.\\label{eq:pmj-4}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Joint Motion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Joint-Motion\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Displacement at Joint\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Prescribed-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a joint has a translational degree of freedom along \n\\begin_inset Formula $\\mathbf{n}^{\\left(1\\right)}$\n\\end_inset\n\n,\n there may be a need to prescribe the relative displacement \n\\begin_inset Formula $d$\n\\end_inset\n\n along that direction.\n This can be achieved by supplementing the joint reaction force with a force that closes the gap between the current and desired translation,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}^{\\left(1\\right)}=\\varepsilon_{c}\\left[\\left(\\mathbf{n}^{\\left(1\\right)}\\otimes\\mathbf{n}^{\\left(1\\right)}\\right)\\cdot\\mathbf{g}-d\\,\\mathbf{n}^{\\left(1\\right)}\\right]=\\varepsilon_{c}\\left(\\mathbf{P}_{3}\\cdot\\mathbf{g}-d\\,\\mathbf{n}^{\\left(1\\right)}\\right)\\,,\\label{eq:pdj-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{P}_{3}$\n\\end_inset\n\n is given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:jf-P-projection\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Then,\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{f}^{\\left(1\\right)}=D\\mathbf{P}_{3}\\cdot\\mathbf{g}+\\mathbf{P}_{3}\\cdot D\\mathbf{g}+d\\,\\hat{\\mathbf{n}}^{\\left(1\\right)}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\label{eq:pdj-2}\n\\end{equation}\n\n\\end_inset\n\nUsing the results of Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Joint-Springs\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the stiffnesses are supplemented with\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{fr}^{\\left(1\\right)} & =-\\varepsilon_{c}\\mathbf{P}_{3}\\\\\n\\mathbf{K}_{f\\theta}^{\\left(1\\right)} & =\\varepsilon_{c}\\left(\\mathbf{P}_{3}\\cdot\\hat{\\mathbf{z}}^{\\left(1\\right)}+\\mathbf{Q}+d\\,\\hat{\\mathbf{n}}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{K}_{fr}^{\\left(2\\right)} & =\\varepsilon_{c}\\mathbf{P}_{3}\\\\\n\\mathbf{K}_{f\\theta}^{\\left(2\\right)} & =-\\varepsilon_{c}\\mathbf{P}_{3}\\cdot\\hat{\\mathbf{z}}^{\\left(2\\right)}\n\\end{aligned}\n}\\,.\\label{eq:pdj-3}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Rotation at Joint\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Joint-Prescribed-Rotation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe relative rotation between the rigid bodies is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{Q}=\\boldsymbol{\\Lambda}^{\\left(2\\right)}\\cdot\\boldsymbol{\\Lambda}^{\\left(1\\right)T}=\\sum_{j}\\mathbf{e}_{j}^{\\left(2\\right)}\\otimes\\mathbf{e}_{j}^{\\left(1\\right)}\\,.\\label{eq:prj-1}\n\\end{equation}\n\n\\end_inset\n\nWe want it to be equal to a rotation by \n\\begin_inset Formula $\\boldsymbol{\\chi}$\n\\end_inset\n\n as expressed in the basis \n\\begin_inset Formula $\\mathbf{e}_{j}^{\\left(1\\right)}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{A}=\\exp\\left[\\boldsymbol{\\chi}\\right]\\,.\\label{eq:prj-2}\n\\end{equation}\n\n\\end_inset\n\nWe enforce this constraint by requiring that\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{A}\\cdot\\mathbf{Q}^{T}=\\mathbf{I}\\,.\\label{eq:prj-3}\n\\end{equation}\n\n\\end_inset\n\nWe may thus evaluate\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{R}=\\mathbf{A}\\cdot\\mathbf{Q}^{T}\\,,\\label{eq:prj-4}\n\\end{equation}\n\n\\end_inset\n\nand expect that \n\\begin_inset Formula $\\mathbf{R}=\\mathbf{I}$\n\\end_inset\n\n when the constraint is enforced.\n Note that \n\\begin_inset Formula $\\mathbf{R}^{T}\\cdot\\mathbf{R}=\\mathbf{I}$\n\\end_inset\n\n because of the orthogonality of \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n,\n i.e.,\n \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n is always orthogonal,\n even when the constraint is not enforced.\n The axial vector of \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n may be denoted by \n\\begin_inset Formula $\\boldsymbol{\\xi}$\n\\end_inset\n\n,\n i.e.,\n \n\\begin_inset Formula $\\mathbf{R}=\\exp\\left[\\boldsymbol{\\xi}\\right]$\n\\end_inset\n\n,\n and the constraint is enforced when \n\\begin_inset Formula $\\boldsymbol{\\xi}=\\mathbf{0}$\n\\end_inset\n\n.\n Therefore,\n a moment \n\\begin_inset Formula $\\varepsilon_{r}\\boldsymbol{\\xi}$\n\\end_inset\n\n needs to be prescribed,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{m}^{\\left(1\\right)}=\\varepsilon_{r}\\boldsymbol{\\xi}\\,.\\label{eq:prj-5}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSince \n\\begin_inset Formula $\\mathbf{R}$\n\\end_inset\n\n is orthogonal,\n we can linearize it along an increment \n\\begin_inset Formula $\\Delta\\boldsymbol{\\xi}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $D\\mathbf{R}\\left[\\Delta\\boldsymbol{\\xi}\\right]=\\Delta\\hat{\\boldsymbol{\\xi}}\\cdot\\mathbf{R}$\n\\end_inset\n\n,\n from which it follows that \n\\begin_inset Formula $\\Delta\\hat{\\boldsymbol{\\xi}}=D\\mathbf{R}\\cdot\\mathbf{R}^{T}$\n\\end_inset\n\n with\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{R}=\\mathbf{A}\\cdot D\\mathbf{Q}^{T}=\\mathbf{A}\\cdot\\left(\\Delta\\hat{\\boldsymbol{\\theta}}^{\\left(1\\right)}\\cdot\\mathbf{Q}^{T}-\\mathbf{Q}^{T}\\cdot\\Delta\\hat{\\boldsymbol{\\theta}}^{\\left(2\\right)}\\right)\\,,\\label{eq:prj-6}\n\\end{equation}\n\n\\end_inset\n\nso that\n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\hat{\\boldsymbol{\\xi}}=\\mathbf{A}\\cdot\\Delta\\hat{\\boldsymbol{\\theta}}^{\\left(1\\right)}\\cdot\\mathbf{A}^{T}-\\mathbf{R}\\cdot\\Delta\\hat{\\boldsymbol{\\theta}}^{\\left(2\\right)}\\cdot\\mathbf{R}^{T}\\,.\\label{eq:prj-7}\n\\end{equation}\n\n\\end_inset\n\nFrom this expression we get\n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\boldsymbol{\\xi}=\\mathbf{A}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}-\\mathbf{R}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\\,.\\label{eq:prj-8}\n\\end{equation}\n\n\\end_inset\n\nThus,\n\\begin_inset Formula \n\\begin{equation}\nD\\mathbf{m}^{\\left(1\\right)}=\\varepsilon_{r}\\left(\\mathbf{A}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}-\\mathbf{R}\\cdot\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\\right)\\,.\\label{eq:prj-9}\n\\end{equation}\n\n\\end_inset\n\nIt follows that\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{K}_{mr}^{\\left(1\\right)}=\\mathbf{K}_{mr}^{\\left(2\\right)}=\\mathbf{0}}\\,,\\label{eq:prj-10}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{K}_{m\\theta}^{\\left(1\\right)} & =\\varepsilon_{r}\\mathbf{A}\\\\\n\\mathbf{K}_{m\\theta}^{\\left(2\\right)} & =-\\varepsilon_{r}\\mathbf{R}\n\\end{aligned}\n}\\,.\\label{eq:prj-11}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOther Rigid Connectors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Other-Rigid-Connectors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSpring Between Rigid Bodies\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Spring-Between-Rigid-Bodies\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a spring between points \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n on rigid bodies \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(2\\right)$\n\\end_inset\n\n.\n The spring force is oriented along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}}{\\left|\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right|}\\,.\\label{eq:rbs-1}\n\\end{equation}\n\n\\end_inset\n\nWe may write\n\\begin_inset Formula \n\\begin{equation}\nL=\\left|\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right|\\,,\\quad L_{0}=\\left|\\mathbf{X}^{\\left(2\\right)}-\\mathbf{X}^{\\left(2\\right)}\\right|\\,.\\label{eq:rbs-2}\n\\end{equation}\n\n\\end_inset\n\nThen,\n the spring force acting on body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}^{\\left(1\\right)}=k\\left(L-L_{0}\\right)\\mathbf{n}=k\\left(\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}-L_{0}\\mathbf{n}\\right)\\,,\\label{eq:rbs-3}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $k$\n\\end_inset\n\n is the spring constant.\n It is understood that the special case \n\\begin_inset Formula $L_{0}=0$\n\\end_inset\n\n produces \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}=k\\left(\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right)$\n\\end_inset\n\n.\n We do not need to use the augmented Lagrangian method here,\n therefore \n\\begin_inset Formula $\\mathbf{f}^{\\left(1\\right)}=k\\mathbf{c}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{c}=\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}-L_{0}\\frac{\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}}{\\left|\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}\\right|}\\,.\\label{eq:rbs-4}\n\\end{equation}\n\n\\end_inset\n\nSince the spring may freely pivot about its insertion point,\n we let \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}=\\mathbf{0}$\n\\end_inset\n\n.\n Thus,\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{f}^{\\left(1\\right)} & =k\\left(\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}-L_{0}\\frac{\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}}{\\left|\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}\\right|}\\right)\\\\\n\\mathbf{m}^{\\left(1\\right)} & =\\mathbf{0}\n\\end{aligned}\n}\\label{eq:rbs-5}\n\\end{equation}\n\n\\end_inset\n\nand the virtual work associated with this spring is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}^{\\left(1\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\n\\end{array}\\right]\\,.\\label{eq:rbs-6}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n may be expressed as\n\\begin_inset Formula \n\\begin{equation}\n-D\\delta G=\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(2\\right)}\\end{array}\\right]\\left[\\mathbf{K}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:rbs-7}\n\\end{equation}\n\n\\end_inset\n\nwhere the stiffness matrix is\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{K}\\right]=k\\alpha\\left[\\begin{array}{cccc}\n\\mathbf{P} & -\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & -\\mathbf{P} & \\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\\\\n-\\mathbf{P} & \\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & \\mathbf{P} & -\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:rbs-8}\n\\end{equation}\n\n\\end_inset\n\nwith\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}=\\left(1-\\frac{L_{0}}{L_{n+\\alpha}}\\right)\\mathbf{I}+\\frac{L_{0}}{L_{n+\\alpha}}\\mathbf{n}_{n+\\alpha}\\otimes\\mathbf{n}_{n+\\alpha}\\,.\\label{eq:rbs-9}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nDamper Between Rigid Bodies\n\\end_layout\n\n\\begin_layout Standard\nConsider a damper (e.g.,\n a dashpot) inserted between points on two rigid bodies.\n The force generated by this dashpot is\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}^{\\left(1\\right)}=\\varepsilon_{c}\\dot{\\mathbf{c}}\\,.\\label{eq:rbd-1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\dot{\\mathbf{c}}$\n\\end_inset\n\n is the relative velocity between insertion points,\n\\begin_inset Formula \n\\begin{equation}\n\\dot{\\mathbf{c}}=\\mathbf{v}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{v}_{n+\\alpha}^{\\left(1\\right)}\\,.\\label{eq:rbd-2}\n\\end{equation}\n\n\\end_inset\n\nThis relative velocity is related to the rigid body degrees of freedom by\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{x}_{n+\\alpha}^{\\left(i\\right)} & =\\mathbf{r}_{n+\\alpha}^{\\left(i\\right)}+\\mathbf{z}_{n+\\alpha}^{\\left(i\\right)}\\\\\n\\mathbf{v}_{n}^{\\left(i\\right)} & =\\dot{\\mathbf{r}}_{n}^{\\left(i\\right)}+\\boldsymbol{\\omega}_{n}\\times\\mathbf{z}_{n}^{\\left(i\\right)}\\\\\n\\mathbf{v}_{n+1}^{\\left(i\\right)} & =\\dot{\\mathbf{r}}_{n+1}^{\\left(i\\right)}+\\boldsymbol{\\omega}_{n+1}\\times\\mathbf{z}_{n+1}^{\\left(i\\right)}\\\\\n\\mathbf{v}_{n+\\alpha}^{\\left(i\\right)} & =\\alpha\\mathbf{v}_{n+1}^{\\left(i\\right)}+\\left(1-\\alpha\\right)\\mathbf{v}_{n}^{\\left(i\\right)}\n\\end{aligned}\n\\,.\\label{eq:rbd-3}\n\\end{equation}\n\n\\end_inset\n\nSince the dashpot may freely rotate about its insertion points,\n we set \n\\begin_inset Formula $\\mathbf{m}^{\\left(1\\right)}=\\mathbf{0}$\n\\end_inset\n\n.\n It follows that\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{f}^{\\left(1\\right)} & =\\varepsilon_{c}\\left(\\mathbf{v}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{v}_{n+\\alpha}^{\\left(1\\right)}\\right)\\\\\n\\mathbf{m}^{\\left(1\\right)} & =\\mathbf{0}\n\\end{aligned}\n}\\,,\\label{eq:rbd-4}\n\\end{equation}\n\n\\end_inset\n\nand the virtual work resulting from the dashpot is\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}^{\\left(1\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\n\\end{array}\\right]\\,.\\label{eq:rbd-5}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n may be written as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}-D\\delta G & =\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(2\\right)}\\end{array}\\right]\\varepsilon_{c}\\alpha\\left[\\begin{array}{cccc}\n\\mathbf{A} & -\\mathbf{B}^{\\left(1\\right)} & -\\mathbf{A} & \\mathbf{B}^{\\left(2\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{A} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{B}^{\\left(1\\right)} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{A} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{B}^{\\left(2\\right)}\\\\\n-\\mathbf{A} & \\mathbf{B}^{\\left(1\\right)} & \\mathbf{A} & -\\mathbf{B}^{\\left(2\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{A} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{B}^{\\left(1\\right)} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{A} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{B}^{\\left(2\\right)}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\end{aligned}\n\\,,\\label{eq:rbd-6}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{K}\\right]=\\varepsilon_{c}\\alpha\\left[\\begin{array}{cccc}\n\\mathbf{A} & -\\mathbf{B}^{\\left(1\\right)} & -\\mathbf{A} & \\mathbf{B}^{\\left(2\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{A} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{B}^{\\left(1\\right)} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{A} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{B}^{\\left(2\\right)}\\\\\n-\\mathbf{A} & \\mathbf{B}^{\\left(1\\right)} & \\mathbf{A} & -\\mathbf{B}^{\\left(2\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{A} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{B}^{\\left(1\\right)} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{A} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{B}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:rbd-7}\n\\end{equation}\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{A} & =\\frac{\\gamma}{\\beta\\Delta t}\\mathbf{I}\\\\\n\\mathbf{B}^{\\left(i\\right)} & =\\frac{\\gamma}{\\beta\\Delta t}\\hat{\\mathbf{z}}_{n+1}^{\\left(i\\right)}\\cdot\\mathbf{T}^{T}\\left(\\boldsymbol{\\theta}^{\\left(i\\right)}\\right)+\\hat{\\boldsymbol{\\omega}}_{n+1}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(i\\right)}\n\\end{aligned}\n\\,.\\label{eq:rbd-8}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nContractile Force Between Rigid Bodies\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Contractile-Force-Rigid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nConsider a contractile force between points \n\\begin_inset Formula $\\mathbf{x}^{\\left(i\\right)}$\n\\end_inset\n\n on rigid bodies \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(2\\right)$\n\\end_inset\n\n.\n The force is oriented along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{n}=\\frac{\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}}{\\left|\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right|}\\,.\\label{eq:rcf-1}\n\\end{equation}\n\n\\end_inset\n\nLet \n\\begin_inset Formula $L=\\left|\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right|$\n\\end_inset\n\n and \n\\begin_inset Formula $L_{0}=\\left|\\mathbf{X}^{\\left(2\\right)}-\\mathbf{X}^{\\left(2\\right)}\\right|$\n\\end_inset\n\n,\n so that the contractile force acting on body \n\\begin_inset Formula $\\left(1\\right)$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}^{\\left(1\\right)}=f_{0}\\mathbf{n}=\\frac{f_{0}}{L}\\left(\\mathbf{x}^{\\left(2\\right)}-\\mathbf{x}^{\\left(1\\right)}\\right)\\,.\\label{eq:rcf-2}\n\\end{equation}\n\n\\end_inset\n\nWe do not need to use the augmented Lagrangian method here,\n thus\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\begin{aligned}\\mathbf{f}^{\\left(1\\right)} & =f_{0}\\frac{\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}}{\\left|\\mathbf{x}_{n+\\alpha}^{\\left(2\\right)}-\\mathbf{x}_{n+\\alpha}^{\\left(1\\right)}\\right|}\\\\\n\\mathbf{m}^{\\left(1\\right)} & =\\mathbf{0}\n\\end{aligned}\n}\\label{eq:rcf-3}\n\\end{equation}\n\n\\end_inset\n\nwhere we assume that the contractile force pivots freely at the rigid body insertiones.\n The virtual work resulting from this contractile force is thus\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(2\\right)}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}^{\\left(1\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\mathbf{f}^{\\left(1\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{f}^{\\left(1\\right)}\n\\end{array}\\right]\\,,\\label{eq:rcf-4}\n\\end{equation}\n\n\\end_inset\n\nand its linearization is\n\\begin_inset Formula \n\\begin{equation}\n-D\\delta G=\\left[\\begin{array}{cccc}\n\\delta\\mathbf{r}^{\\left(1\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(1\\right)} & \\delta\\mathbf{r}^{\\left(2\\right)} & \\delta\\boldsymbol{\\omega}^{\\left(2\\right)}\\end{array}\\right]\\left[\\mathbf{K}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{\\left(1\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(1\\right)}\\\\\n\\Delta\\mathbf{r}^{\\left(2\\right)}\\\\\n\\Delta\\boldsymbol{\\theta}^{\\left(2\\right)}\n\\end{array}\\right]\\,.\\label{eq:rcf-5}\n\\end{equation}\n\n\\end_inset\n\nHere,\n the stiffness matrix is given by\n\\begin_inset Formula \n\\begin{equation}\n\\left[\\mathbf{K}\\right]=\\alpha\\frac{f_{0}}{L_{n+\\alpha}}\\left[\\begin{array}{cccc}\n\\mathbf{P} & -\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & -\\mathbf{P} & \\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(1\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\\\\n-\\mathbf{P} & \\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & \\mathbf{P} & -\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\\\\\n-\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(1\\right)} & \\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{\\left(2\\right)}\\cdot\\mathbf{P}\\cdot\\hat{\\mathbf{z}}_{n+1}^{\\left(2\\right)}\n\\end{array}\\right]\\,,\\label{eq:rcf-6}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{P}=\\mathbf{I}-\\mathbf{n}_{n+\\alpha}\\otimes\\mathbf{n}_{n+\\alpha}\\,.\\label{eq:rcf-7}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nRigid-Deformable Coupling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Rigid-Deformable-Coupling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio deformable bodies can be coupled with rigid bodies \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Maker95\"\nliteral \"true\"\n\n\\end_inset\n\n.\n At these rigid-deformable interfaces,\n the coupling of nodal degrees of freedom of deformable elements that attach to rigid bodies requires a modification of the global stiffness matrix and residual vector.\n This section describes the coupling between rigid and deformable bodies.\n\\end_layout\n\n\\begin_layout Standard\nThe position of a node shared by any number of deformable finite elements is denoted by \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n in the current configuration.\n If the node belongs to one or more deformable elements but is not connected to a rigid body,\n then \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n is given in terms of the nodal displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{x}=\\mathbf{X}+\\mathbf{u}$\n\\end_inset\n\n;\n the corresponding nodal virtual velocity is \n\\begin_inset Formula $\\delta\\mathbf{v}$\n\\end_inset\n\n and the linearization of \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n along an incremental displacement is denoted by \n\\begin_inset Formula $\\Delta\\mathbf{u}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe contribution to the virtual work of the nodal force \n\\begin_inset Formula $\\mathbf{f}^{a}$\n\\end_inset\n\n at node \n\\begin_inset Formula $a$\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\delta\\mathbf{v}^{a}\\cdot\\mathbf{f}_{n+\\alpha}^{a}\\,,\\label{eq:rdc-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nwhera \n\\begin_inset Formula $\\delta\\mathbf{v}^{a}$\n\\end_inset\n\n is the virtual velocity of node \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{f}_{n+\\alpha}^{a}$\n\\end_inset\n\n is the global nodal force,\n evaluated at the intermediate time \n\\begin_inset Formula $t_{n+\\alpha}$\n\\end_inset\n\n as \n\\begin_inset Formula $\\mathbf{f}_{n+\\alpha}^{a}=\\alpha\\mathbf{f}_{n+1}^{a}+\\left(1-\\alpha\\right)\\mathbf{f}_{n}^{a}$\n\\end_inset\n\n.\n The linearization of this virtual work along the incremental displacement \n\\begin_inset Formula $\\Delta\\mathbf{u}^{b}$\n\\end_inset\n\n of node \n\\begin_inset Formula $b$\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G=-\\alpha\\delta\\mathbf{v}^{a}\\cdot\\mathbf{K}^{ab}\\cdot\\Delta\\mathbf{u}^{b},\\label{eq:rdc-linearization-vw}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{K}^{ab}=\\left(\\partial\\mathbf{f}^{a}/\\partial\\mathbf{x}^{b}\\right)_{n+1}$\n\\end_inset\n\n is the contribution to the global stiffness matrix from the interactions of the degrees of freedom of nodes \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nNow we consider the cases when either node \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n or node \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n or both,\n are attached to a rigid body.\n Our objective is to determine how to modify the global residual vector and stiffness matrix to account for the coupling of deformable and rigid body degrees of freedom.\n\\end_layout\n\n\\begin_layout Standard\nWhen node \n\\begin_inset Formula $a$\n\\end_inset\n\n is attached to rigid body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n its position is given in terms of that rigid body's degrees of freedom by the general relation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rbm-position\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The corresponding virtual velocity is given in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rc-origin-virtual-displacement\"\nnolink \"false\"\n\n\\end_inset\n\n,\n reproduced here as\n\\begin_inset Formula \n\\begin{equation}\n\\delta\\mathbf{v}^{a}=\\delta\\mathbf{r}^{a}-\\hat{\\mathbf{z}}_{n+\\alpha}^{a}\\cdot\\delta\\boldsymbol{\\theta}^{a}\\,,\\label{eq:rdc-rb-virtual-velocity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{z}_{n+\\alpha}^{a}=\\boldsymbol{\\Lambda}_{n+\\alpha}^{a}\\cdot\\mathbf{Z}^{a}$\n\\end_inset\n\n is the position of node \n\\begin_inset Formula $a$\n\\end_inset\n\n relative to the center of mass of rigid body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n at the intermediate time \n\\begin_inset Formula $t_{n+\\alpha}$\n\\end_inset\n\n.\n Now,\n the contribution of the global nodal force \n\\begin_inset Formula $\\mathbf{f}_{n+\\alpha}^{a}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\delta G$\n\\end_inset\n\n must be modified from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rdc-virtual-work\"\nnolink \"false\"\n\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\begin{equation}\n\\delta G=\\delta\\mathbf{v}^{a}\\cdot\\mathbf{f}_{n+\\alpha}^{a}=\\left[\\begin{array}{cc}\n\\delta\\mathbf{r}^{a} & \\delta\\boldsymbol{\\theta}^{a}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{f}_{n+\\alpha}^{a}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{a}\\cdot\\mathbf{f}_{n+\\alpha}^{a}\n\\end{array}\\right]\\,.\\label{eq:rdc-rb-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nIn other words,\n the displacement degrees of freedom of node \n\\begin_inset Formula $a$\n\\end_inset\n\n should be eliminated from the global system of equations and replaced with the translation and rotation degrees of freedom of rigid body \n\\begin_inset Formula $a$\n\\end_inset\n\n.\n The force vector \n\\begin_inset Formula $\\mathbf{f}_{n+\\alpha}^{a}$\n\\end_inset\n\n should be made to contribute to the translation degrees of freedom of the center of mass of rigid body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n whereas the moment \n\\begin_inset Formula $\\mathbf{z}_{n+\\alpha}^{a}\\times\\mathbf{f}_{n+\\alpha}^{a}$\n\\end_inset\n\n should contribute to the rotation degrees of freedom of the rigid body.\n\\end_layout\n\n\\begin_layout Standard\nWhen node \n\\begin_inset Formula $b$\n\\end_inset\n\n is connected to rigid body \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n the incremental displacement \n\\begin_inset Formula $\\Delta\\mathbf{u}^{b}$\n\\end_inset\n\n should be replaced with the rigid body incremental motions,\n\\begin_inset Formula \n\\begin{equation}\n\\Delta\\mathbf{u}^{b}=\\Delta\\mathbf{r}^{b}-\\hat{\\mathbf{z}}_{n+1}^{b}\\cdot\\Delta\\boldsymbol{\\theta}^{b}\\,.\\label{eq:rdc-rb-incremental-disp}\n\\end{equation}\n\n\\end_inset\n\nNow,\n the contribution \n\\begin_inset Formula $\\mathbf{K}^{ab}$\n\\end_inset\n\n to the global stiffness matrix needs to be modified from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rdc-linearization-vw\"\nnolink \"false\"\n\n\\end_inset\n\n according to the three possible cases:\n\\end_layout\n\n\\begin_layout Itemize\nNode \n\\begin_inset Formula $a$\n\\end_inset\n\n belongs to rigid body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n node \n\\begin_inset Formula $b$\n\\end_inset\n\n belongs to flexible elements only,\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G=-\\alpha\\left[\\begin{array}{cc}\n\\delta\\mathbf{r}^{a} & \\delta\\boldsymbol{\\theta}^{a}\\end{array}\\right]\\left[\\begin{array}{c}\n\\mathbf{K}^{ab}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{a}\\cdot\\mathbf{K}^{ab}\n\\end{array}\\right]\\left[\\Delta\\mathbf{u}^{b}\\right]\\,.\\label{eq:rdc-arigid-bflex}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Itemize\nNode \n\\begin_inset Formula $b$\n\\end_inset\n\n belongs to rigid body \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n node \n\\begin_inset Formula $a$\n\\end_inset\n\n belongs to flexible elements only,\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G=-\\alpha\\left[\\delta\\mathbf{v}^{a}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}^{ab} & -\\mathbf{K}^{ab}\\cdot\\hat{\\mathbf{z}}_{n+1}^{b}\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{b}\\\\\n\\Delta\\boldsymbol{\\theta}^{b}\n\\end{array}\\right]\\,.\\label{eq:rdc-aflex-brigid}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Itemize\nNode \n\\begin_inset Formula $a$\n\\end_inset\n\n belongs to rigid body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n node \n\\begin_inset Formula $b$\n\\end_inset\n\n belongs to rigid body \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\nD\\delta G=-\\alpha\\left[\\begin{array}{cc}\n\\delta\\mathbf{r}^{a} & \\delta\\boldsymbol{\\theta}^{a}\\end{array}\\right]\\left[\\begin{array}{cc}\n\\mathbf{K}^{ab} & -\\mathbf{K}^{ab}\\cdot\\hat{\\mathbf{z}}_{n+1}^{b}\\\\\n\\hat{\\mathbf{z}}_{n+\\alpha}^{a}\\cdot\\mathbf{K}^{ab} & -\\hat{\\mathbf{z}}_{n+\\alpha}^{a}\\cdot\\mathbf{K}^{ab}\\cdot\\hat{\\mathbf{z}}_{n+1}^{b}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta\\mathbf{r}^{b}\\\\\n\\Delta\\boldsymbol{\\theta}^{b}\n\\end{array}\\right]\\,.\\label{eq:rdc-arigid-brigid}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nNonlinear Constraints\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Nonlinear-Constraints\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNonlinear constraints are equations of the form \n\\begin_inset Formula $f\\left(x_{i}\\right)=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $x_{i}$\n\\end_inset\n\n represent nodal degrees of freedom.\n These constraints may be enforced using the penalty method,\n with or without augmented Lagrangian,\n or the method of Lagrange multipliers.\n Each method has its own advantages and disadvantages.\n\\end_layout\n\n\\begin_layout Subsection\nPenalty Method with Augmented Lagrangian\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Penalty-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe penalty method employs the penalty parameter \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n to add \n\\begin_inset Formula $W_{p}=\\frac{1}{2}\\varepsilon f^{2}$\n\\end_inset\n\n to the total work,\n where \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n serves as the 'stiffness' or spring constant in the 'spring' whose 'elongation' is \n\\begin_inset Formula $f$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $W_{p}$\n\\end_inset\n\n represents the stored energy in the spring.\n The contribution to the virtual work for this type of analysis is \n\\begin_inset Formula $\\delta W_{p}=\\varepsilon f\\,\\delta f$\n\\end_inset\n\n,\n where\n\\begin_inset Formula \n\\begin{equation}\n\\delta f=\\sum_{i}\\frac{\\partial f}{\\partial x_{i}}\\delta x_{i}\\label{eq:nlc-virtual-force}\n\\end{equation}\n\n\\end_inset\n\nIf we are concerned that the value of \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n may become too large to produce good numerical convergence,\n we may augment this expression by using \n\\begin_inset Formula $\\delta W_{p}=\\left(\\lambda+\\varepsilon f\\right)\\delta f$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is the Lagrangian augmentation parameter,\n which gets updated at each iteration to help us avoid using a value of \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n that's too large.\n We may let \n\\begin_inset Formula $c=\\lambda+\\varepsilon f$\n\\end_inset\n\n represent the constraint that needs to be satisfied.\n Substituing eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:nlc-virtual-force\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n into this relation allows us to express \n\\begin_inset Formula $\\delta W_{p}$\n\\end_inset\n\n in matrix form,\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{p}=\\left[\\begin{array}{ccc}\n\\delta x_{1} & \\cdots & \\delta x_{n}\\end{array}\\right]\\left[\\begin{array}{c}\nc\\frac{\\partial f}{\\partial x_{1}}\\\\\n\\vdots\\\\\nc\\frac{\\partial f}{\\partial x_{n}}\n\\end{array}\\right]=\\delta\\boldsymbol{\\mathbf{r}}\\cdot\\mathbf{\\boldsymbol{f_{\\mathit{p}}}}\\,.\\label{eq:nlc-pm-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta W_{p}$\n\\end_inset\n\n along increments \n\\begin_inset Formula $\\Delta x_{i}$\n\\end_inset\n\n of the degrees of freedom employs\n\\begin_inset Formula \n\\begin{equation}\nDf=\\sum_{j}\\frac{\\partial f}{\\partial x_{j}}\\Delta x_{j}\\label{eq:nlc-pm-f-linearization}\n\\end{equation}\n\n\\end_inset\n\nand takes the form\n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{p}=\\left[\\begin{array}{ccc}\n\\delta x_{1} & \\cdots & \\delta x_{n}\\end{array}\\right]\\left[\\begin{array}{ccc}\n\\varepsilon\\left(\\frac{\\partial f}{\\partial x_{1}}\\right)^{2}+c\\frac{\\partial^{2}f}{\\partial x_{1}^{2}} & \\cdots & \\varepsilon\\frac{\\partial f}{\\partial x_{1}}\\frac{\\partial f}{\\partial x_{n}}+c\\frac{\\partial^{2}f}{\\partial x_{1}\\partial x_{n}}\\\\\n\\vdots & \\ddots & \\vdots\\\\\n\\varepsilon\\frac{\\partial f}{\\partial x_{n}}\\frac{\\partial f}{\\partial x_{1}}+c\\frac{\\partial^{2}f}{\\partial x_{1}\\partial x_{n}} & \\cdots & \\varepsilon\\left(\\frac{\\partial f}{\\partial x_{n}}\\right)^{2}+c\\frac{\\partial^{2}f}{\\partial x_{n}^{2}}\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta x_{1}\\\\\n\\vdots\\\\\n\\Delta x_{n}\n\\end{array}\\right]=\\delta\\boldsymbol{\\mathbf{r}}\\cdot\\mathbf{\\boldsymbol{K_{\\mathit{p}}}\\boldsymbol{\\Delta r}}\\,,\\label{eq:nlc-pm-stiffness}\n\\end{equation}\n\n\\end_inset\n\nThe stiffness matrix for the penalty constraint is symmetric.\n\\end_layout\n\n\\begin_layout Standard\nLagrangian augmentation is performed after the (quasi-)Newton method has converged.\n At that time,\n the augmented Lagrangians are updated as follows\n\\begin_inset Formula \n\\begin{equation}\n\\lambda^{\\left(k+1\\right)}\\leftarrow\\lambda^{\\left(k\\right)}+\\varepsilon f\\label{eq:nlc-pm-augment}\n\\end{equation}\n\n\\end_inset\n\nThe penalty method with augmented Lagrangian does not increase the number of equations that need to be solved globally.\n However,\n it does require users to specify the parameter \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n,\n and to provide a convergence tolerance for Lagrange augmentation.\n\\end_layout\n\n\\begin_layout Example\n\\begin_inset CommandInset label\nLatexCommand label\nname \"exa:Linear-Constraints-Penalty\"\n\n\\end_inset\n\nFor linear constraints of the form\n\\begin_inset Formula \n\\begin{equation}\nf=a_{0}+\\sum_{j}a_{j}x_{j}=0\\label{eq:nlc-linear-constraint}\n\\end{equation}\n\n\\end_inset\n\nwe find that \n\\begin_inset Formula $\\frac{\\partial f}{\\partial x_{i}}=a_{i}$\n\\end_inset\n\n and the second derivatives are all zero,\n so that the constributions to the global system of equations take the form\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}_{\\mathit{p}}=\\left[\\begin{array}{c}\nca_{1}\\\\\n\\vdots\\\\\nca_{n}\n\\end{array}\\right],\\;\\mathbf{K}_{\\mathit{p}}=\\left[\\begin{array}{ccc}\n\\varepsilon a_{1}^{2} & \\cdots & \\varepsilon a_{1}a_{n}\\\\\n\\vdots & \\ddots & \\vdots\\\\\n\\varepsilon a_{n}a_{1} & \\cdots & \\varepsilon a_{n}^{2}\n\\end{array}\\right].\\label{eq:nlc-pm-linear}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nMethod of Lagrange Multipliers\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Lagrange-Multipliers\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen using the method of Lagrange multipliers we have to include equations that solve for the multipliers.\n We let the work done by the constraint be represented by \n\\begin_inset Formula $W_{c}=\\lambda f$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is the multiplier (if \n\\begin_inset Formula $f$\n\\end_inset\n\n is a vector equation,\n we can use \n\\begin_inset Formula $W_{c}=\\boldsymbol{\\lambda}\\cdot\\mathbf{f}$\n\\end_inset\n\n).\n The total work is then \n\\begin_inset Formula $\\widetilde{W}=W+W_{c}$\n\\end_inset\n\n where \n\\begin_inset Formula $W$\n\\end_inset\n\n is the work of the unconstrained problem (i.e.\n the contributions from the internal and external forces.) Then,\n we evaulate the first varition of \n\\begin_inset Formula $\\delta\\widetilde{W}=\\delta W+\\delta W_{c}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\delta W_{c}=\\delta f+\\delta\\lambda\\,f$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\delta f$\n\\end_inset\n\n is given as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:nlc-virtual-force\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\delta W_{c}$\n\\end_inset\n\n may be written in matrix form as\n\\begin_inset Formula \n\\begin{equation}\n\\delta W_{c}=\\left[\\begin{array}{cccc}\n\\delta x_{1} & \\cdots & \\delta x_{n} & \\delta\\lambda\\end{array}\\right]\\left[\\begin{array}{c}\n\\lambda\\frac{\\partial f}{\\partial x_{1}}\\\\\n\\vdots\\\\\n\\lambda\\frac{\\partial f}{\\partial x_{n}}\\\\\nf\n\\end{array}\\right]=\\delta\\boldsymbol{r\\cdot}\\mathbf{f}_{c}\\,.\\label{eq:nlc-lm-virtual-work}\n\\end{equation}\n\n\\end_inset\n\nThe linearization of \n\\begin_inset Formula $\\delta W_{c}$\n\\end_inset\n\n along increments \n\\begin_inset Formula $\\Delta x_{j}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Delta\\lambda$\n\\end_inset\n\n takes the form\n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{c}=\\sum_{i}\\delta x_{i}\\frac{\\partial f}{\\partial x_{i}}\\Delta\\lambda+\\sum_{i}\\delta x_{i}\\sum_{j}\\lambda\\frac{\\partial^{2}f}{\\partial x_{i}\\partial x_{j}}\\Delta x_{j}+\\delta\\lambda\\,\\sum_{j}\\frac{\\partial f}{\\partial x_{j}}\\Delta x_{j}\\,.\\label{eq:nlc-lm-linearization}\n\\end{equation}\n\n\\end_inset\n\nThe corresponding stiffness matrix is\n\\begin_inset Formula \n\\begin{equation}\nD\\delta W_{c}=\\left[\\begin{array}{cccc}\n\\delta x_{1} & \\cdots & \\delta x_{n} & \\delta\\lambda\\end{array}\\right]\\left[\\begin{array}{cccc}\n\\lambda\\frac{\\partial^{2}f}{\\partial x_{1}^{2}} & \\cdots & \\lambda\\frac{\\partial^{2}f}{\\partial x_{1}\\partial x_{n}} & \\frac{\\partial f}{\\partial x_{1}}\\\\\n\\vdots & \\ddots & \\vdots & \\vdots\\\\\n\\lambda\\frac{\\partial^{2}f}{\\partial x_{n}\\partial x_{1}} & \\cdots & \\lambda\\frac{\\partial^{2}f}{\\partial x_{n}^{2}} & \\frac{\\partial f}{\\partial x_{n}}\\\\\n\\frac{\\partial f}{\\partial x_{1}} & \\cdots & \\frac{\\partial f}{\\partial x_{n}} & 0\n\\end{array}\\right]\\left[\\begin{array}{c}\n\\Delta x_{1}\\\\\n\\vdots\\\\\n\\Delta x_{n}\\\\\n\\Delta\\lambda\n\\end{array}\\right]=\\delta\\boldsymbol{r\\cdot}\\mathbf{K}_{c}\\boldsymbol{\\Delta r}\\,,\\label{eq:nlc-lm-stiffness}\n\\end{equation}\n\n\\end_inset\n\nThis resulting system of equations requires \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n to part of the solution,\n and to be updated at each iteration \n\\begin_inset Formula $k$\n\\end_inset\n\n according to \n\\begin_inset Formula $\\lambda^{\\left(k+1\\right)}\\leftarrow\\lambda^{\\left(k\\right)}+\\Delta\\lambda$\n\\end_inset\n\n.\n The stiffness matrix is symmetric for this analysis.\n\\end_layout\n\n\\begin_layout Standard\nThe method of Lagrange multipliers increases the number of equations that need to be solved globally.\n However,\n it does not require users to specify additional parameters.\n Note that the contribution to the stiffness matrix introduces a zero coefficient on the diagonal of the stiffness matrix for the equation corresponding to \n\\begin_inset Formula $\\Delta\\lambda$\n\\end_inset\n\n.\n This means that these equations can only be solved with linear solvers that support pivoting.\n (Most linear solvers in FEBio,\n including the default pardiso solver,\n can handle zero-diagonal elements.) \n\\end_layout\n\n\\begin_layout Example\n\\begin_inset CommandInset label\nLatexCommand label\nname \"exa:Linear-System-Multiplier\"\n\n\\end_inset\n\nFor linear constraints of the form\n\\begin_inset Formula \n\\[\nf=a_{0}+\\sum_{j}a_{j}x_{j}=0\n\\]\n\n\\end_inset\n\nwe find that \n\\begin_inset Formula $\\frac{\\partial f}{\\partial x_{i}}=a_{i}$\n\\end_inset\n\n and second derivatives are all zero,\n so that the contributions to the global residual and stiffness matrix take the form\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{f}_{c}=\\left[\\begin{array}{c}\n\\lambda a_{1}\\\\\n\\vdots\\\\\n\\lambda a_{n}\\\\\nf\n\\end{array}\\right],\\:\\boldsymbol{\\mathbf{K}_{c}}=\\left[\\begin{array}{cccc}\n0 & \\cdots & 0 & a_{1}\\\\\n\\vdots & \\ddots & \\vdots & \\vdots\\\\\n0 & \\cdots & 0 & a_{n}\\\\\na_{1} & \\cdots & a_{n} & 0\n\\end{array}\\right]\\label{eq:nlc-lm-linear}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nOptimization\n\\end_layout\n\n\\begin_layout Standard\nThis chapter describes the theoretical framework of FEBio's optimization module.\n This module can be used to optimize model parameters (like material parameters,\n or load values) in order to achieve an objective.\n The objective is generally the minimization of the \n\\emph on\nobjective function\n\\emph default\n,\n to be introduced below.\n \n\\end_layout\n\n\\begin_layout Section\nThe Objective Function\n\\end_layout\n\n\\begin_layout Standard\nThe objective function,\n which is to be minimized,\n can be defined as follows,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\varphi\\left(\\mathbf{a}\\right)=\\sum\\limits_{i=1}^{n}\\left[y_{i}-f\\left(x_{i};\\mathbf{a}\\right)\\right]^{2}.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n the \n\\begin_inset Formula $\\left(x_{i},y_{i}\\right)$\n\\end_inset\n\n are user-defined data pairs and \n\\begin_inset Formula $f\\left(x;\\mathbf{a}\\right)$\n\\end_inset\n\n is the function that extracts the corresponding data from the model.\n The optimization module tries to find the model parameters \n\\series bold\na\n\\series default\n that minimize the function \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n.\n It does this by repeatedly evaluating the function \n\\begin_inset Formula $y$\n\\end_inset\n\n,\n which will usually call FEBio to solve a forward FE problem.\n \n\\end_layout\n\n\\begin_layout Section\nThe Levenberg-Marquardt Method\n\\end_layout\n\n\\begin_layout Standard\nOne of the methods that is currently implemented in FEBio's optimization method is the constrained Levenberg-Marquardt method via the \n\\emph on\nlevmar \n\\emph default\nlibrary.\n (see \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{http://users.ics.forth.gr/~lourakis/levmar/}\n\\end_layout\n\n\\end_inset\n\n for more information on this library.)\n\\end_layout\n\n\\begin_layout Standard\nThe Levenberg-Marquardt method is a numerical algorithm that minimizes a function that is defined as a sum of squares of nonlinear functions,\n i.e.\n the objective function as defined above.\n It combines the steepest-descent method with a Gauss-Newton method to find the parameters that minimize the objective function.\n \n\\end_layout\n\n\\begin_layout Standard\nThe LM method requires a set of measured values \n\\begin_inset Formula $(x_{i},y_{i})$\n\\end_inset\n\n and an initial guess for the \n\\series bold\na\n\\series default\n vector.\n It then tries to find a better estimate for \n\\series bold\na \n\\series default\nby replacing it with \n\\begin_inset Formula $\\mathbf{a}+\\delta$\n\\end_inset\n\n.\n The function \n\\begin_inset Formula $f(x_{i};\\mathbf{a}+\\delta)$\n\\end_inset\n\n is linearly approximated.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nf\\left(x_{i};\\mathbf{a}+\\delta\\right)\\approx f\\left(x_{i};\\mathbf{a}\\right)+\\mathbf{J}_{i}\\delta\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $\\mathbf{J}_{i}$\n\\end_inset\n\nis the Jacobian of \n\\emph on\n\n\\begin_inset Formula $f$\n\\end_inset\n\n\n\\emph default\n with respect to \n\\begin_inset Formula $\\delta$\n\\end_inset\n\n.\n Substituting this in the objective function and minimizing with respect to \n\\begin_inset Formula $\\delta$\n\\end_inset\n\n leads to,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n(\\mathbf{J^{\\mathrm{T}}J\\mathrm{)\\delta=}J^{\\mathrm{T}}\\mathrm{(\\mathbf{y}-\\mathbf{f}(\\boldsymbol{a}))}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\series bold\ny\n\\series default\n is the vector of \n\\begin_inset Formula $y_{i},$\n\\end_inset\n\nand \n\\series bold\nf\n\\series default\n is the vector of \n\\begin_inset Formula $f(x_{i};\\boldsymbol{a})$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe main idea of the LM method is to replace this linear equation with the following.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n(\\mathbf{J^{\\mathrm{T}}J\\mathrm{+\\mu(\\mathbf{J}^{\\mathrm{T}}\\mathbf{J})_{\\mathit{ii}})\\delta=}J^{\\mathrm{T}}\\mathrm{(\\mathbf{y}-\\mathbf{f}(\\boldsymbol{a}))}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\mu$\n\\end_inset\n\nis a damping parameter that is controlled by the algorithm.\n When \n\\begin_inset Formula $\\mu$\n\\end_inset\n\nis small,\n the method approximates Gauss-Newton,\n when \n\\begin_inset Formula $\\mu$\n\\end_inset\n\nis large it is closer to a steepdest-descent method.\n The algorithm will modify try to modify \n\\begin_inset Formula $\\mu$\n\\end_inset\n\nsuch that an improvement to the parameter vector \n\\series bold\na\n\\series default\n can be found in each iteration.\n The method will terminate when the value of the objective function falls below a user-specified tolerance (the \n\\emph on\nobj_tol\n\\emph default\n parameter in FEBio).\n \n\\end_layout\n\n\\begin_layout Standard\nThe evaluation of the Jacobian requires evaluating the derivatives of \n\\begin_inset Formula $f$\n\\end_inset\n\n with respect to \n\\series bold\na\n\\series default\n.\n These derivatives are approximated via forward difference formulas.\n For example,\n the \n\\begin_inset Formula $k$\n\\end_inset\n\n-th component of the gradient is approximated as follows.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\frac{\\partial f}{\\partial a_{k}}\\approx\\frac{1}{\\delta a_{k}}\\left[f\\left(a_{1},\\cdots,a_{k}+\\delta a_{k},\\cdots,a_{m}\\right)-f\\left(a_{1},\\cdots,a_{k},\\cdots,a_{m}\\right)\\right]\n\\]\n\n\\end_inset\n\nThe value for \n\\begin_inset Formula $\\delta a_{k}$\n\\end_inset\n\nis determined from the following formula.\n \n\\begin_inset Formula \n\\[\n\\delta a_{k}=\\varepsilon\\left(1+a_{k}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere,\n \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the forward difference scale factor (the \n\\shape italic\nfdiff_scale \n\\shape default\noption in FEBio).\n In FEBio,\n the initial value for the damping parameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\ncan be set with the \n\\emph on\ntau \n\\emph default\nparameter.\n\\end_layout\n\n\\begin_layout Chapter\n\\start_of_appendix\nTensor Calculus\n\\end_layout\n\n\\begin_layout Section\nSecond-Order Tensors\n\\end_layout\n\n\\begin_layout Subsection\nDefinition\n\\end_layout\n\n\\begin_layout Standard\nLet \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n be a transformation,\n which transforms any vector into another vector,\n e.g.,\n \n\\begin_inset Formula \n\\[\n\\mathbf{T}\\cdot\\mathbf{a}=\\mathbf{b}\\quad\\mathbf{T}\\cdot\\mathbf{c}=\\mathbf{d}\n\\]\n\n\\end_inset\n\n If \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n has the following properties,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{T}\\cdot\\left(\\mathbf{a}+\\mathbf{c}\\right)=\\mathbf{T}\\cdot\\mathbf{a}+\\mathbf{T}\\cdot\\mathbf{c}\\label{eq6-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{T}\\cdot\\left(\\alpha\\mathbf{a}\\right)=\\alpha\\mathbf{T}\\cdot\\mathbf{a}\\label{eq7-1}\n\\end{equation}\n\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n are arbitrary vectors,\n and \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n is an arbitrary scalar,\n then \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n is called a \n\\shape italic\nlinear transformation\n\\shape default\n,\n a \n\\shape italic\nsecond-order tensor\n\\shape default\n,\n or simply a \n\\shape italic\ntensor\n\\shape default\n.\n Vectors are first-order tensors and scalars are zeroth-order tensors.\n \n\\end_layout\n\n\\begin_layout Subsection\nCartesian Components of a Tensor\n\\end_layout\n\n\\begin_layout Standard\nLet \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n form an orthonormal basis in a Cartesian coordinate system \n\\begin_inset Formula $x_{1},x_{2},x_{3}$\n\\end_inset\n\n.\n Then the Cartesian components of \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n are \n\\begin_inset Formula \n\\[\na_{1}=\\mathbf{e}_{1}\\cdot\\mathbf{a},\\quad a_{2}=\\mathbf{e}_{2}\\cdot\\mathbf{a},\\quad a_{3}=\\mathbf{e}_{3}\\cdot\\mathbf{a}\n\\]\n\n\\end_inset\n\n or equivalently,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{a_{j}=\\mathbf{e}_{j}\\cdot\\mathbf{a}}\\label{eq7b}\n\\end{equation}\n\n\\end_inset\n\n (Recall that \n\\begin_inset Formula $\\mathbf{a}=a_{i}\\mathbf{e}_{i}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\mathbf{a}\\cdot\\mathbf{e}_{j}=a_{i}\\mathbf{e}_{i}\\cdot\\mathbf{e}_{j}=a_{i}\\delta_{ij}=a_{j}$\n\\end_inset\n\n.) \n\\end_layout\n\n\\begin_layout Standard\nThe Cartesian components of a tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n are obtained as follows.\n Let \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{a}=\\mathbf{b}$\n\\end_inset\n\n.\n The components of \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n are given by \n\\begin_inset Formula $b_{i}=\\mathbf{e}_{i}\\cdot\\mathbf{b}=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{a}$\n\\end_inset\n\n.\n But \n\\begin_inset Formula $\\mathbf{a}=a_{j}\\mathbf{e}_{j}$\n\\end_inset\n\n,\n so \n\\begin_inset Formula $b_{i}=a_{j}\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}$\n\\end_inset\n\n is the component along \n\\begin_inset Formula $\\mathbf{e}_{i}$\n\\end_inset\n\n of the vector \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{e}_{j}$\n\\end_inset\n\n.\n By convention,\n we denote this component as \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{T_{ij}=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}}\\quad\\text{components of tensor }\\mathbf{T}\\label{eq7c}\n\\end{equation}\n\n\\end_inset\n\n components of tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThus,\n \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{T}\\cdot\\mathbf{a}=a_{j}\\mathbf{T}\\cdot\\mathbf{e}_{j}=b_{k}\\mathbf{e}_{k}$\n\\end_inset\n\n.\n Taking the dot product on both sides with \n\\begin_inset Formula $\\mathbf{e}_{i}$\n\\end_inset\n\n yields \n\\begin_inset Formula $a_{j}\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}=a_{j}T_{ij}=b_{k}\\mathbf{e}_{i}\\cdot\\mathbf{e}_{k}=b_{k}\\delta_{ik}=b_{i}$\n\\end_inset\n\n,\n or\n\\begin_inset Formula \n\\[\n\\boxed{b_{i}=T_{ij}a_{j}}\n\\]\n\n\\end_inset\n\n in indicial form.\n In matrix form,\n \n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{c}\nb_{1}\\\\\nb_{2}\\\\\nb_{3}\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nT_{11} & T_{12} & T_{13}\\\\\nT_{21} & T_{22} & T_{23}\\\\\nT_{31} & T_{32} & T_{33}\n\\end{array}\\right]\\left[\\begin{array}{c}\na_{1}\\\\\na_{2}\\\\\na_{3}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n The matrix of tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n can also be denoted by \n\\begin_inset Formula $\\left[\\mathbf{T}\\right]$\n\\end_inset\n\n or \n\\begin_inset Formula $\\left[T_{ij}\\right]$\n\\end_inset\n\n.\n The columns of \n\\begin_inset Formula $\\left[\\mathbf{T}\\right]$\n\\end_inset\n\n are given by \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{e}_{i}$\n\\end_inset\n\n,\n e.g.,\n \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{T}\\cdot\\mathbf{e}_{2}\\right]=\\left[T_{j2}\\mathbf{e}_{2}\\right]=\\left[\\begin{array}{ccc}\nT_{11} & T_{12} & T_{13}\\\\\nT_{21} & T_{22} & T_{23}\\\\\nT_{31} & T_{32} & T_{33}\n\\end{array}\\right]\\left[\\begin{array}{c}\n0\\\\\n1\\\\\n0\n\\end{array}\\right]=\\left[\\begin{array}{c}\nT_{12}\\\\\nT_{22}\\\\\nT_{32}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n This result,\n when generalized,\n leads to the useful identity \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{T}\\cdot\\mathbf{e}_{i}=T_{ji}\\mathbf{e}_{j}}=T_{1i}\\mathbf{e}_{1}+T_{2i}\\mathbf{e}_{2}+T_{3i}\\mathbf{e}_{3}\\label{eq8-1}\n\\end{equation}\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Example\nScaling transformation\n\\end_layout\n\n\\begin_layout Example\nA scaling transformation \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n with different scale factors along \n\\begin_inset Formula $x_{1},x_{2},x_{3}$\n\\end_inset\n\n should satisfy the following relations by definition:\n\\begin_inset Formula \n\\[\n\\mathbf{T}\\cdot\\mathbf{a}=s_{1}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{1}\\right)\\mathbf{e}_{1}+s_{2}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{2}\\right)\\mathbf{e}_{2}+s_{3}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{3}\\right)\\mathbf{e}_{3}\n\\]\n\n\\end_inset\n\nVerify that \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n is a tensor.\n Also find the matrix of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n in \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Example\n\n\\shape italic\nSolution\n\\shape default\n.\n Is \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n a tensor?\n Let any \n\\begin_inset Formula $\\mathbf{a}=a_{i}\\mathbf{e}_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{b}=b_{i}\\mathbf{e}_{i}$\n\\end_inset\n\n,\n then\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{T}\\cdot\\left(\\alpha\\mathbf{a}\\right) & =s_{1}\\left(\\alpha\\mathbf{a}\\cdot\\mathbf{e}_{1}\\right)\\mathbf{e}_{1}+s_{2}\\left(\\alpha\\mathbf{a}\\cdot\\mathbf{e}_{2}\\right)\\mathbf{e}_{2}+s_{3}\\left(\\alpha\\mathbf{a}\\cdot\\mathbf{e}_{3}\\right)\\mathbf{e}_{3}\\\\\n & =\\alpha\\left(s_{1}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{1}\\right)\\mathbf{e}_{1}+s_{2}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{2}\\right)\\mathbf{e}_{2}+s_{3}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{3}\\right)\\mathbf{e}_{3}\\right)\\\\\n & =\\alpha\\mathbf{T}\\cdot\\mathbf{a}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{T}\\cdot\\left(\\mathbf{a}+\\mathbf{b}\\right) & =s_{1}\\left(\\left(\\mathbf{a}+\\mathbf{b}\\right)\\cdot\\mathbf{e}_{1}\\right)\\mathbf{e}_{1}+s_{2}\\left(\\left(\\mathbf{a}+\\mathbf{b}\\right)\\cdot\\mathbf{e}_{2}\\right)\\mathbf{e}_{2}+s_{3}\\left(\\left(\\mathbf{a}+\\mathbf{b}\\right)\\cdot\\mathbf{e}_{3}\\right)\\mathbf{e}_{3}\\\\\n & =s_{1}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{1}\\right)\\mathbf{e}_{1}+s_{2}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{2}\\right)\\mathbf{e}_{2}+s_{3}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{3}\\right)\\mathbf{e}_{3}\\\\\n & +s_{1}\\left(\\mathbf{b}\\cdot\\mathbf{e}_{1}\\right)\\mathbf{e}_{1}+s_{2}\\left(\\mathbf{b}\\cdot\\mathbf{e}_{2}\\right)\\mathbf{e}_{2}+s_{3}\\left(\\mathbf{b}\\cdot\\mathbf{e}_{3}\\right)\\mathbf{e}_{3}\\\\\n & =\\mathbf{T}\\cdot\\mathbf{a}+\\mathbf{T}\\cdot\\mathbf{b}\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Example\nNow that we have demonstrated that \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n is a tensor,\n its components are given by \n\\begin_inset Formula $T_{ij}=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula \n\\[\n\\begin{gathered}\\left[T_{i1}\\right]=\\left[\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{1}\\right]=\\left[\\mathbf{e}_{i}\\cdot s_{1}\\mathbf{e}_{1}\\right]=\\left[s_{1}\\delta_{i1}\\right]=\\left[\\begin{array}{c}\ns_{1}\\\\\n0\\\\\n0\n\\end{array}\\right]\\\\\n\\left[T_{i2}\\right]=\\left[\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{2}\\right]=\\left[\\mathbf{e}_{i}\\cdot s_{2}\\mathbf{e}_{2}\\right]=\\left[s_{2}\\delta_{i2}\\right]=\\left[\\begin{array}{c}\n0\\\\\ns_{2}\\\\\n0\n\\end{array}\\right]\\\\\n\\left[T_{i3}\\right]=\\left[\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{3}\\right]=\\left[\\mathbf{e}_{i}\\cdot s_{3}\\mathbf{e}_{3}\\right]=\\left[s_{3}\\delta_{i3}\\right]=\\left[\\begin{array}{c}\n0\\\\\n0\\\\\ns_{3}\n\\end{array}\\right]\n\\end{gathered}\n\\]\n\n\\end_inset\n\n Then,\n the matrix of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{T}\\right]=\\left[\\begin{array}{ccc}\ns_{1} & 0 & 0\\\\\n0 & s_{2} & 0\\\\\n0 & 0 & s_{3}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nSum of Tensors\n\\end_layout\n\n\\begin_layout Standard\nThe sum of two tensors \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n is denoted by \n\\begin_inset Formula $\\mathbf{T}+\\mathbf{S}$\n\\end_inset\n\n and defined by \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\left(\\mathbf{T}+\\mathbf{S}\\right)\\cdot\\mathbf{a}=\\mathbf{T}\\cdot\\mathbf{a}+\\mathbf{S}\\cdot\\mathbf{a}}\\label{eq9-1}\n\\end{equation}\n\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n.\n Thus \n\\begin_inset Formula $\\mathbf{T}+\\mathbf{S}$\n\\end_inset\n\n is also a tensor,\n whose components are \n\\begin_inset Formula \n\\[\n\\left(\\mathbf{T}+\\mathbf{S}\\right)_{ij}=\\mathbf{e}_{i}\\cdot\\left(\\mathbf{T}+\\mathbf{S}\\right)\\cdot\\mathbf{e}_{j}=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}+\\mathbf{e}_{i}\\cdot\\mathbf{S}\\cdot\\mathbf{e}_{j}=T_{ij}+S_{ij}\n\\]\n\n\\end_inset\n\n In matrix notation,\n \n\\begin_inset Formula $\\left[\\mathbf{T}+\\mathbf{S}\\right]=\\left[\\mathbf{T}\\right]+\\left[\\mathbf{S}\\right]$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Subsection\nDyadic Product of Vectors\n\\end_layout\n\n\\begin_layout Standard\nThe dyadic product of two vectors \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is denoted by \n\\begin_inset Formula $\\mathbf{a}\\otimes\\mathbf{b}$\n\\end_inset\n\n (or \n\\begin_inset Formula $\\mathbf{ab}$\n\\end_inset\n\n) and defined as the transformation which satisfies \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\cdot\\mathbf{c}=\\left(\\mathbf{b}\\cdot\\mathbf{c}\\right)\\mathbf{a}}\\label{eq10-1}\n\\end{equation}\n\n\\end_inset\n\n For any \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n and \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n we have \n\\begin_inset Formula \n\\[\n\\begin{gathered}\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\cdot\\left(\\alpha\\mathbf{c}+\\beta\\mathbf{d}\\right)=\\left(\\mathbf{b}\\cdot\\left(\\alpha\\mathbf{c}+\\beta\\mathbf{d}\\right)\\right)\\mathbf{a}=\\left(\\mathbf{b}\\cdot\\alpha\\mathbf{c}\\right)\\mathbf{a}+\\left(\\mathbf{b}\\cdot\\beta\\mathbf{d}\\right)\\mathbf{a}\\\\\n=\\alpha\\left(\\mathbf{b}\\cdot\\mathbf{c}\\right)\\mathbf{a}+\\beta\\left(\\mathbf{b}\\cdot\\mathbf{d}\\right)\\mathbf{a}=\\alpha\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\cdot\\mathbf{c}+\\beta\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\cdot\\mathbf{d}\n\\end{gathered}\n\\]\n\n\\end_inset\n\n thus \n\\begin_inset Formula $\\mathbf{a}\\otimes\\mathbf{b}$\n\\end_inset\n\n is a tensor.\n Its Cartesian components with respect to \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are \n\\begin_inset Formula \n\\[\n\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)_{ij}=\\mathbf{e}_{i}\\cdot\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\cdot\\mathbf{e}_{j}=\\mathbf{e}_{i}\\cdot\\left(\\mathbf{b}\\cdot\\mathbf{e}_{j}\\right)\\mathbf{a}=b_{j}\\mathbf{e}_{i}\\cdot\\mathbf{a}=a_{i}b_{j}\n\\]\n\n\\end_inset\n\n In matrix form,\n \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{a}\\otimes\\mathbf{b}\\right]=\\left[\\begin{array}{ccc}\na_{1}b_{1} & a_{1}b_{2} & a_{1}b_{3}\\\\\na_{2}b_{1} & a_{2}b_{2} & a_{2}b_{3}\\\\\na_{3}b_{1} & a_{3}b_{2} & a_{3}b_{3}\n\\end{array}\\right]=\\left[\\begin{array}{c}\na_{1}\\\\\na_{2}\\\\\na_{3}\n\\end{array}\\right]\\left[\\begin{array}{ccc}\nb_{1} & b_{2} & b_{3}\\end{array}\\right]=\\left[\\mathbf{a}\\right]\\left[\\mathbf{b}\\right]^{T}\n\\]\n\n\\end_inset\n\n Note that in general,\n \n\\begin_inset Formula $\\mathbf{a}\\otimes\\mathbf{b}\\ne\\mathbf{b}\\otimes\\mathbf{a}$\n\\end_inset\n\n,\n i.e.,\n the dyadic product is \n\\shape italic\nnot commutative\n\\shape default\n.\n Also note that \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{e}_{1}\\otimes\\mathbf{e}_{1}\\right]=\\left[\\begin{array}{ccc}\n1 & 0 & 0\\\\\n0 & 0 & 0\\\\\n0 & 0 & 0\n\\end{array}\\right],\\quad\\left[\\mathbf{e}_{1}\\otimes\\mathbf{e}_{2}\\right]=\\left[\\begin{array}{ccc}\n0 & 1 & 0\\\\\n0 & 0 & 0\\\\\n0 & 0 & 0\n\\end{array}\\right],\\quad\\left[\\mathbf{e}_{1}\\otimes\\mathbf{e}_{3}\\right]=\\left[\\begin{array}{ccc}\n0 & 0 & 1\\\\\n0 & 0 & 0\\\\\n0 & 0 & 0\n\\end{array}\\right]\\quad\\text{etc.}\n\\]\n\n\\end_inset\n\nthus it is possible to represent a second-order tensor in terms of its Cartesian components in \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n as \n\\begin_inset Formula $\\mathbf{T}=T_{11}\\mathbf{e}_{1}\\otimes\\mathbf{e}_{1}+T_{12}\\mathbf{e}_{1}\\otimes\\mathbf{e}_{2}+T_{13}\\mathbf{e}_{1}\\otimes\\mathbf{e}_{3}+\\ldots+T_{33}\\mathbf{e}_{3}\\otimes\\mathbf{e}_{3}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{T}=T_{ij}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}}\\label{eq11-1}\n\\end{equation}\n\n\\end_inset\n\nThis turns out to be an important result that can be generalized to higher order tensors,\n e.g.,\n third-order tensors can be represented in terms of their Cartesian components as \n\\begin_inset Formula $\\mathbb{T}=T_{ijk}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}$\n\\end_inset\n\n,\n and similarly for higher orders.\n \n\\end_layout\n\n\\begin_layout Example\nThe scaling transformation derived in a previous example can be represented as \n\\begin_inset Formula \n\\[\n\\mathbf{T}=s_{1}\\mathbf{e}_{1}\\otimes\\mathbf{e}_{1}+s_{2}\\mathbf{e}_{2}\\otimes\\mathbf{e}_{2}+s_{3}\\mathbf{e}_{3}\\otimes\\mathbf{e}_{3}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTrace of a Second-Order Tensor\n\\end_layout\n\n\\begin_layout Standard\nThe trace of any dyad \n\\begin_inset Formula $\\mathbf{a}\\otimes\\mathbf{b}$\n\\end_inset\n\n is defined by \n\\begin_inset Formula \n\\[\n\\boxed{\\tr\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)=\\mathbf{a}\\cdot\\mathbf{b}}\n\\]\n\n\\end_inset\n\n and \n\\begin_inset Formula \n\\[\n\\boxed{\\tr\\left(\\alpha\\mathbf{a}\\otimes\\mathbf{b}+\\beta\\mathbf{c}\\otimes\\mathbf{d}\\right)=\\alpha\\tr\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)+\\beta\\tr\\left(\\mathbf{c}\\otimes\\mathbf{d}\\right)}\n\\]\n\n\\end_inset\n\nThe trace operator yields a scalar function.\n In component form,\n \n\\begin_inset Formula \n\\[\n\\tr\\left(a_{i}b_{j}\\right)=a_{i}b_{i}\n\\]\n\n\\end_inset\n\n For any tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n we can write \n\\begin_inset Formula $\\mathbf{T}=T_{ij}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula \n\\[\n\\tr\\mathbf{T}=T_{ij}\\tr\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right)=T_{ij}\\mathbf{e}_{i}\\cdot\\mathbf{e}_{j}=T_{ij}\\delta_{ij}=T_{ii}=T_{11}+T_{22}+T_{33}\n\\]\n\n\\end_inset\n\n The trace of a tensor is the sum of its diagonal components.\n \n\\end_layout\n\n\\begin_layout Subsection\nProduct of Two Tensors\n\\end_layout\n\n\\begin_layout Standard\nThe products of two tensors \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n are denoted by \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{S}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{S}\\cdot\\mathbf{T}$\n\\end_inset\n\n and defined respectively by \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\left(\\mathbf{T}\\cdot\\mathbf{S}\\right)\\cdot\\mathbf{a}=\\mathbf{T}\\cdot\\left(\\mathbf{S}\\cdot\\mathbf{a}\\right)}\\label{eq12-1}\n\\end{equation}\n\n\\end_inset\n\n and \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\left(\\mathbf{S}\\cdot\\mathbf{T}\\right)\\cdot\\mathbf{a}=\\mathbf{S}\\cdot\\left(\\mathbf{T}\\cdot\\mathbf{a}\\right)}\\label{eq12b}\n\\end{equation}\n\n\\end_inset\n\nClearly,\n \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{S}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{S}\\cdot\\mathbf{T}$\n\\end_inset\n\n are tensors as well.\n Their components in \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are given by \n\\begin_inset Formula \n\\[\n\\left(\\mathbf{T}\\cdot\\mathbf{S}\\right)_{ij}=\\mathbf{e}_{i}\\cdot\\left(\\mathbf{T}\\cdot\\mathbf{S}\\right)\\cdot\\mathbf{e}_{j}=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\left(\\mathbf{S}\\cdot\\mathbf{e}_{j}\\right)=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\left(S_{kj}\\mathbf{e}_{k}\\right)=S_{kj}\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{k}=T_{ik}S_{kj}\n\\]\n\n\\end_inset\n\n In matrix form,\n \n\\begin_inset Formula $\\left[\\mathbf{T}\\cdot\\mathbf{S}\\right]=\\left[\\mathbf{T}\\right]\\left[\\mathbf{S}\\right]$\n\\end_inset\n\n.\n Similarly,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\left(\\mathbf{S}\\cdot\\mathbf{T}\\right)_{ij}=S_{ik}T_{kj}\\quad\\text{and}\\quad\\left[\\mathbf{S}\\cdot\\mathbf{T}\\right]=\\left[\\mathbf{S}\\right]\\left[\\mathbf{T}\\right]\n\\]\n\n\\end_inset\n\n In general,\n \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{S}\\ne\\mathbf{S}\\cdot\\mathbf{T}$\n\\end_inset\n\n,\n however \n\\begin_inset Formula $\\left(\\mathbf{T}\\cdot\\mathbf{S}\\right)\\cdot\\mathbf{V}=\\mathbf{T}\\cdot\\left(\\mathbf{S}\\cdot\\mathbf{V}\\right)$\n\\end_inset\n\n,\n i.e.,\n the tensor product is \n\\shape italic\nassociative\n\\shape default\n but \n\\shape italic\nnot commutative\n\\shape default\n.\n \n\\end_layout\n\n\\begin_layout Subsection\nIdentity Tensor and Tensor Inverse\n\\end_layout\n\n\\begin_layout Standard\nThe identity tensor,\n denoted by \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n,\n is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{I}\\cdot\\mathbf{a}=\\mathbf{a}}\\label{eq13-1}\n\\end{equation}\n\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n.\n The Cartesian components o f \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n in \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are given by \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nI_{ij}=\\mathbf{e}_{i}\\cdot\\mathbf{I}\\cdot\\mathbf{e}_{j}=\\mathbf{e}_{i}\\cdot\\mathbf{e}_{j}=\\delta_{ij}\n\\]\n\n\\end_inset\n\n or \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{I}\\right]=\\left[\\begin{array}{ccc}\n1 & 0 & 0\\\\\n0 & 1 & 0\\\\\n0 & 0 & 1\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\nGiven \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n if \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n exists such that \n\\begin_inset Formula $\\mathbf{S}\\cdot\\mathbf{T}=\\mathbf{I}$\n\\end_inset\n\n,\n we call \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n the \n\\emph on\ninverse\n\\emph default\n of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{S}=\\mathbf{T}^{-1}$\n\\end_inset\n\n.\n The inverse exists as long as \n\\begin_inset Formula $\\det\\mathbf{T}\\neq0$\n\\end_inset\n\n.\n Also note that \n\\begin_inset Formula $\\left(\\mathbf{T}^{-1}\\right)^{-1}=\\mathbf{T}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{T}^{-1}\\cdot\\mathbf{T}=\\mathbf{T}\\cdot\\mathbf{T}^{-1}=\\mathbf{I}$\n\\end_inset\n\n.\n Also note that\n\\begin_inset Formula \n\\[\n\\boxed{\\left(\\mathbf{U}\\cdot\\mathbf{V}\\right)^{-1}=\\mathbf{V}^{-1}\\cdot\\mathbf{U}^{-1}}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTranspose of a Tensor\n\\end_layout\n\n\\begin_layout Standard\nGiven a tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n its transpose is denoted by \n\\begin_inset Formula $\\mathbf{T}^{T}$\n\\end_inset\n\n which is defined by \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{a}\\cdot\\left(\\mathbf{T}\\cdot\\mathbf{b}\\right)=\\mathbf{b}\\cdot\\left(\\mathbf{T}^{T}\\cdot\\mathbf{\\mathbf{a}}\\right)}\\label{eq14-1}\n\\end{equation}\n\n\\end_inset\n\n In component form,\n \n\\begin_inset Formula \n\\[\nT_{ij}^{T}=\\mathbf{e}_{i}\\cdot\\mathbf{T}^{T}\\cdot\\mathbf{e}_{j}=\\mathbf{e}_{j}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{i}=T_{ji}\n\\]\n\n\\end_inset\n\n Also note that \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\left(\\mathbf{S}\\cdot\\mathbf{T}\\right)^{T}=\\mathbf{T}^{T}\\cdot\\mathbf{S}^{T}}\\label{eq15-1}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\left(\\mathbf{S}^{T}\\right)^{T}=\\mathbf{S}$\n\\end_inset\n\n and\n\\begin_inset Formula $\\left(\\mathbf{S}+\\mathbf{T}\\right)^{T}=\\mathbf{S}^{T}+\\mathbf{T}^{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nDouble Product of Tensors\n\\end_layout\n\n\\begin_layout Standard\nThe double product of tensors is analogous to the dot product of vectors.\n Given two tensors \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n the double product (or \n\\shape italic\ndouble contraction\n\\shape default\n) is defined as \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{S}:\\mathbf{T}=\\tr\\left(\\mathbf{S}^{T}\\cdot\\mathbf{T}\\right)}\\label{eq16-1}\n\\end{equation}\n\n\\end_inset\n\n Thus,\n for any tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tr\\mathbf{T}=\\mathbf{I}:\\mathbf{T}$\n\\end_inset\n\n.\n In component form,\n \n\\begin_inset Formula \n\\[\n\\mathbf{S}:\\mathbf{T}=S_{ij}T_{ij}\n\\]\n\n\\end_inset\n\nThe double product of second order tensors is commutative.\n \n\\end_layout\n\n\\begin_layout Example\nShow that \n\\begin_inset Formula $\\mathbf{a}\\cdot\\mathbf{T}\\cdot\\mathbf{b}=\\mathbf{T}:\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right):\\left(\\mathbf{c}\\otimes\\mathbf{d}\\right)=\\left(\\mathbf{a}\\cdot\\mathbf{c}\\right)\\left(\\mathbf{b}\\cdot\\mathbf{d}\\right)$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Example\nUsing indicial notation,\n \n\\begin_inset Formula \n\\[\n\\mathbf{a}\\cdot\\mathbf{T}\\cdot\\mathbf{b}=a_{i}T_{ij}b_{j}=T_{ij}a_{i}b_{j}=\\mathbf{T}:\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\,,\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right):\\left(\\mathbf{c}\\otimes\\mathbf{d}\\right)=a_{i}b_{j}c_{i}d_{j}=\\left(a_{i}c_{i}\\right)\\left(b_{j}d_{j}\\right)=\\left(\\mathbf{a}\\cdot\\mathbf{c}\\right)\\left(\\mathbf{b}\\cdot\\mathbf{d}\\right)\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDeterminant of a Tensor\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsubsec:determinant\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe determinant of a tensor is equal to the determinant of its components in \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\det\\mathbf{T}=\\det\\left[\\mathbf{T}\\right]_{\\mathbf{e}_{i}}=\\left|\\begin{array}{ccc}\nT_{11} & T_{12} & T_{13}\\\\\nT_{21} & T_{22} & T_{23}\\\\\nT_{31} & T_{32} & T_{33}\n\\end{array}\\right|=\\left(\\mathbf{T}\\cdot\\mathbf{e}_{1}\\times\\mathbf{T}\\cdot\\mathbf{e}_{2}\\right)\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{3}=\\varepsilon_{ijk}T_{i1}T_{j2}T_{k3}\\label{eq17-1}\n\\end{equation}\n\n\\end_inset\n\n In particular,\n the determinant of a diagonal matrix is the product of the diagonal components,\n \n\\begin_inset Formula \n\\begin{equation}\n\\det\\left[\\mathbf{T}\\right]_{\\mathbf{e}_{i}}=\\left|\\begin{array}{ccc}\nT_{11} & 0 & 0\\\\\n0 & T_{22} & 0\\\\\n0 & 0 & T_{33}\n\\end{array}\\right|=\\varepsilon_{123}T_{11}T_{22}T_{33}=T_{11}T_{22}T_{33}\\label{eq18-1}\n\\end{equation}\n\n\\end_inset\n\n The determinant satisfies the following relations,\n \n\\begin_inset Formula \n\\begin{equation}\n\\det\\mathbf{T}^{-1}=\\frac{1}{\\det\\mathbf{T}}\\label{eq19-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\begin{equation}\n\\det\\left(\\mathbf{S}\\cdot\\mathbf{T}\\right)=\\left(\\det\\mathbf{S}\\right)\\left(\\det\\mathbf{T}\\right)\\label{eq20-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOrthogonal Tensor\n\\end_layout\n\n\\begin_layout Standard\nAn orthogonal tensor \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n is a linear transformation which preserves the length of a vector and the angle between vectors.\n Thus,\n by definition,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\left|\\mathbf{Q}\\cdot\\mathbf{a}\\right|=\\left|\\mathbf{a}\\right|\\quad\\text{and}\\quad\\cos\\left(\\mathbf{Q}\\cdot\\mathbf{a},\\mathbf{Q}\\cdot\\mathbf{b}\\right)=\\cos\\left(\\mathbf{a},\\mathbf{b}\\right)\n\\]\n\n\\end_inset\n\n for any vectors \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n.\n It follows from this definition and the definition of the dot product of vectors (\n\\begin_inset Formula $\\mathbf{a}\\cdot\\mathbf{b}=\\left|\\mathbf{a}\\right|\\left|\\mathbf{b}\\right|\\cos\\left(\\mathbf{a},\\mathbf{b}\\right))$\n\\end_inset\n\n,\n that \n\\begin_inset Formula \n\\[\n\\left(\\mathbf{Q}\\cdot\\mathbf{a}\\right)\\cdot\\left(\\mathbf{Q}\\cdot\\mathbf{b}\\right)=\\mathbf{a}\\cdot\\mathbf{b}\n\\]\n\n\\end_inset\n\n But \n\\begin_inset Formula $\\left(\\mathbf{Q}\\cdot\\mathbf{a}\\right)\\cdot\\left(\\mathbf{Q}\\cdot\\mathbf{b}\\right)=\\mathbf{b}\\cdot\\left(\\mathbf{Q}^{T}\\cdot\\mathbf{Q}\\right)\\mathbf{a}=\\mathbf{a}\\cdot\\mathbf{b}=\\mathbf{b}\\cdot\\mathbf{I}\\cdot\\mathbf{a}$\n\\end_inset\n\n,\n which implies that \n\\begin_inset Formula $\\mathbf{b}\\cdot\\left(\\mathbf{Q}^{T}\\cdot\\mathbf{Q}-\\mathbf{I}\\right)\\cdot\\mathbf{a}=0$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n are arbitrary,\n an orthogonal tensor must satisfy \n\\begin_inset Formula $\\mathbf{Q}^{T}\\cdot\\mathbf{Q}=\\mathbf{I}$\n\\end_inset\n\n.\n In indicial form,\n \n\\begin_inset Formula $Q_{im}^{T}Q_{mj}=Q_{mi}Q_{mj}=\\delta_{ij}$\n\\end_inset\n\n,\n and in matrix form,\n \n\\begin_inset Formula $\\left[\\mathbf{Q}\\right]^{T}\\left[\\mathbf{Q}\\right]=\\left[\\mathbf{I}\\right]$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nNote that \n\\begin_inset Formula $\\mathbf{Q}^{T}\\cdot\\mathbf{Q}=\\mathbf{I}$\n\\end_inset\n\n implies that \n\\begin_inset Formula $\\mathbf{Q}^{T}=\\mathbf{Q}^{-1}$\n\\end_inset\n\n,\n i.e.,\n the transpose of an orthogonal tensor is equal to its inverse,\n since \n\\begin_inset Formula $\\mathbf{Q}^{-1}\\cdot\\mathbf{Q}=\\mathbf{Q}\\cdot\\mathbf{Q}^{-1}=\\mathbf{I}$\n\\end_inset\n\n.\n It follows that \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbf{Q}^{T}\\cdot\\mathbf{Q}=\\mathbf{Q}\\cdot\\mathbf{Q}^{T}=\\mathbf{I}}\\label{eq21-1}\n\\end{equation}\n\n\\end_inset\n\n The determinant of an orthogonal tensor is given by \n\\begin_inset Formula \n\\[\n\\det\\mathbf{Q}=\\left(\\mathbf{Q}\\cdot\\mathbf{e}_{1}\\times\\mathbf{Q}\\cdot\\mathbf{e}_{2}\\right)\\cdot\\mathbf{Q}\\cdot\\mathbf{e}_{3}=\\left(\\mathbf{e}'_{1}\\times\\mathbf{e}'_{2}\\right)\\cdot\\mathbf{e}'_{3}=\\pm\\mathbf{e}'_{3}\\cdot\\mathbf{e}'_{3}=\\pm1\n\\]\n\n\\end_inset\n\n Here,\n \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n is the orthonormal basis resulting from the transformation of \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n.\n If \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n maintains the handedness of \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n (e.g.,\n if both \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n form a right-handed basis),\n then \n\\begin_inset Formula $\\det\\mathbf{Q}=+1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n is called a \n\\shape italic\nproper\n\\shape default\n orthogonal transformation (also equivalent to a rigid body rotation).\n Otherwise,\n in the case of a reflection which reverses the handedness of the basis vectors,\n \n\\begin_inset Formula $\\det\\mathbf{Q}=-1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n is called \n\\shape italic\nimproper\n\\shape default\n (e.g.,\n \n\\begin_inset Formula $\\mathbf{e}'_{1}=\\mathbf{e}_{1},\\,\\mathbf{e}'_{2}=-\\mathbf{e}_{2},\\,\\mathbf{e}'_{3}=\\mathbf{e}_{3}$\n\\end_inset\n\n).\n \n\\end_layout\n\n\\begin_layout Subsection\nTransformation Laws for Cartesian Components of Vectors and Tensors\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/FigOrthoBases.png\n\tlyxscale 50\n\twidth 1.39in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nOrthonormal bases \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig3\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\nLet \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n be two orthogonal bases in a Cartesian coordinate system.\n \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n could be made to coincide with \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n through a rigid body rotation (i.e.,\n a transformation that preserves vector length and angles),\n \n\\begin_inset Formula \n\\[\n\\mathbf{e}'_{i}=\\mathbf{Q}\\cdot\\mathbf{e}_{i}=Q_{mi}\\mathbf{e}_{m}\n\\]\n\n\\end_inset\n\n where \n\\begin_inset Formula $Q_{mi}Q_{mj}=Q_{im}Q_{jm}=\\delta_{ij}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $Q_{mi}=\\mathbf{e}_{m}\\cdot\\mathbf{Q}\\cdot\\mathbf{e}_{i}=\\mathbf{e}_{m}\\cdot\\mathbf{e}'_{i}=\\cos\\left(\\mathbf{e}_{m},\\mathbf{e}'_{i}\\right)$\n\\end_inset\n\n,\n the components of \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n are direction cosines between \n\\begin_inset Formula $\\mathbf{e}_{m}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{e}'_{i}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Example\nRotation about \n\\begin_inset Formula $x_{3}$\n\\end_inset\n\n\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/FigRotationAboutX3.png\n\tlyxscale 50\n\twidth 2.58in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nRotation about \n\\begin_inset Formula $x_{3}$\n\\end_inset\n\n.\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig36\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\left[\\mathbf{Q}\\right]=\\left[\\begin{array}{ccc}\n\\cos\\theta & -\\sin\\theta & 0\\\\\n\\sin\\theta & \\cos\\theta & 0\\\\\n0 & 0 & 1\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Example\nReflection about \n\\begin_inset Formula $x_{2}-x_{3}$\n\\end_inset\n\n plane,\n \n\\begin_inset Formula $\\mathbf{e}'_{1}=\\mathbf{Q}\\cdot\\mathbf{e}_{1}=-\\mathbf{e}_{1},\\,\\mathbf{e}'_{2}=\\mathbf{Q}\\cdot\\mathbf{e}_{2}=\\mathbf{e}_{2},\\,\\mathbf{e}'_{3}=\\mathbf{Q}\\cdot\\mathbf{e}_{3}=\\mathbf{e}_{3}$\n\\end_inset\n\n.\n\\begin_inset Formula \n\\[\n\\left[\\mathbf{Q}\\right]=\\left[\\begin{array}{ccc}\n-1 & 0 & 0\\\\\n0 & 1 & 0\\\\\n0 & 0 & 1\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n,\n its components with respect to \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n are \n\\begin_inset Formula $a_{i}=\\mathbf{e}_{i}\\cdot\\mathbf{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $a'_{i}=\\mathbf{e}'_{i}\\cdot\\mathbf{a}$\n\\end_inset\n\n,\n respectively.\n Using the above relation,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\na'_{i}=\\mathbf{e}'_{i}\\cdot\\mathbf{a}=\\mathbf{a}\\cdot\\mathbf{Q}\\cdot\\mathbf{e}_{i}=Q_{mi}\\mathbf{a}\\cdot\\mathbf{e}_{m}=Q_{mi}a_{m},\n\\]\n\n\\end_inset\n\n or \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{a'_{i}=Q_{mi}a_{m}}\\label{eq22-1}\n\\end{equation}\n\n\\end_inset\n\n In matrix form,\n \n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{c}\na'_{1}\\\\\na'_{2}\\\\\na'_{3}\n\\end{array}\\right]_{\\mathbf{e'}_{i}}=\\left[\\begin{array}{ccc}\nQ_{11} & Q_{21} & Q_{31}\\\\\nQ_{12} & Q_{22} & Q_{32}\\\\\nQ_{13} & Q_{23} & Q_{33}\n\\end{array}\\right]\\left[\\begin{array}{c}\na_{1}\\\\\na_{2}\\\\\na_{3}\n\\end{array}\\right]_{\\mathbf{e}_{i}}\n\\]\n\n\\end_inset\n\nor \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{a}\\right]^{\\prime}=\\left[\\mathbf{Q}\\right]^{T}\\left[\\mathbf{a}\\right]\\quad\\text{or}\\quad\\left[\\mathbf{a}\\right]_{\\mathbf{e'}_{i}}=\\left[\\mathbf{Q}\\right]^{T}\\left[\\mathbf{a}\\right]_{\\mathbf{e}_{i}}\n\\]\n\n\\end_inset\n\nHere \n\\begin_inset Formula $\\left[\\mathbf{a}\\right]^{\\prime}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left[\\mathbf{a}\\right]$\n\\end_inset\n\n are matrices of the \n\\shape italic\nsame\n\\shape default\n vector,\n expressed in two different coordinate systems.\n This is \n\\shape italic\nnot the same\n\\shape default\n as \n\\begin_inset Formula $\\mathbf{a}'=\\mathbf{Q}^{T}\\cdot\\mathbf{a}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{a}'$\n\\end_inset\n\n is the linear transformation of \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{Q}^{T}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nNow consider a tensors \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n Its components with respect to \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ \\mathbf{e}'_{1},\\mathbf{e}'_{2},\\mathbf{e}'_{3}\\right\\} $\n\\end_inset\n\n are given by \n\\begin_inset Formula $T_{ij}=\\mathbf{e}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{j}$\n\\end_inset\n\n and \n\\begin_inset Formula $T'_{ij}=\\mathbf{e}'_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{e}'_{j}$\n\\end_inset\n\n,\n respectively.\n Thus,\n \n\\begin_inset Formula $T'_{ij}=\\left(\\mathbf{Q}\\cdot\\mathbf{e}_{i}\\right)\\cdot\\mathbf{T}\\cdot\\left(\\mathbf{Q}\\cdot\\mathbf{e}_{j}\\right)=Q_{mi}\\mathbf{e}_{m}\\cdot\\mathbf{T}\\cdot Q_{nj}\\mathbf{e}_{n}=Q_{mi}Q_{nj}\\mathbf{e}_{m}\\cdot\\mathbf{T}\\cdot\\mathbf{e}_{n}=Q_{mi}Q_{nj}T_{mn}$\n\\end_inset\n\n,\n or \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{T'_{ij}=Q_{mi}Q_{nj}T_{mn}}\\label{eq23-1}\n\\end{equation}\n\n\\end_inset\n\n In matrix form,\n \n\\begin_inset Formula $\\left[\\mathbf{T}\\right]^{\\prime}=\\left[\\mathbf{Q}\\right]^{T}\\left[\\mathbf{T}\\right]\\left[\\mathbf{Q}\\right]$\n\\end_inset\n\n,\n or \n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{ccc}\nT'_{11} & T'_{12} & T'_{13}\\\\\nT'_{21} & T'_{22} & T'_{23}\\\\\nT'_{31} & T'_{32} & T'_{33}\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nQ_{11} & Q_{21} & Q_{31}\\\\\nQ_{12} & Q_{22} & Q_{32}\\\\\nQ_{13} & Q_{23} & Q_{33}\n\\end{array}\\right]\\left[\\begin{array}{ccc}\nT_{11} & T_{12} & T_{13}\\\\\nT_{21} & T_{22} & T_{23}\\\\\nT_{31} & T_{32} & T_{33}\n\\end{array}\\right]\\left[\\begin{array}{ccc}\nQ_{11} & Q_{12} & Q_{13}\\\\\nQ_{21} & Q_{22} & Q_{23}\\\\\nQ_{31} & Q_{32} & Q_{33}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n Equivalently,\n we can show that \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{T_{ij}=Q_{im}Q_{jn}T'_{mn}}\\label{eq24-1}\n\\end{equation}\n\n\\end_inset\n\n or \n\\begin_inset Formula $\\left[\\mathbf{T}\\right]=\\left[\\mathbf{Q}\\right]\\left[\\mathbf{T}\\right]^{\\prime}\\left[\\mathbf{Q}\\right]^{T}$\n\\end_inset\n\n.\n As for vectors,\n we note that \n\\begin_inset Formula $\\left[\\mathbf{T}\\right]$\n\\end_inset\n\n and \n\\begin_inset Formula $\\left[\\mathbf{T}\\right]^{\\prime}$\n\\end_inset\n\n are the matrices of the \n\\shape italic\nsame\n\\shape default\n tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n with respect to two different coordinate systems.\n This is \n\\shape italic\nnot the same\n\\shape default\n as \n\\begin_inset Formula $\\mathbf{T}'=\\mathbf{Q}^{T}\\cdot\\mathbf{T}\\cdot\\mathbf{Q}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Subsection\nSymmetric and Antisymmetric Tensors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsubsec:symmetric\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA \n\\shape italic\nsymmetric\n\\shape default\n tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $\\mathbf{T}^{T}=\\mathbf{T}$\n\\end_inset\n\n,\n i.e.,\n \n\\begin_inset Formula $T_{ji}=T_{ij}$\n\\end_inset\n\n,\n or in matrix form,\n \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{T}\\right]=\\left[\\begin{array}{ccc}\nT_{11} & T_{12} & T_{13}\\\\\nT_{12} & T_{22} & T_{23}\\\\\nT_{13} & T_{23} & T_{33}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n An \n\\shape italic\nantisymmetric\n\\shape default\n (or \n\\shape italic\nskew-symmetric\n\\shape default\n) tensor \n\\begin_inset Formula $\\boldsymbol{\\Omega}$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $\\boldsymbol{\\Omega}^{T}=-\\boldsymbol{\\Omega}$\n\\end_inset\n\n,\n i.e.,\n \n\\begin_inset Formula $\\Omega_{ji}=-\\Omega_{ij}$\n\\end_inset\n\n and thus \n\\begin_inset Formula $\\Omega_{11}=\\Omega_{22}=\\Omega_{33}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\n\\left[\\boldsymbol{\\Omega}\\right]=\\left[\\begin{array}{ccc}\n0 & \\Omega_{12} & -\\Omega_{31}\\\\\n-\\Omega_{12} & 0 & \\Omega_{23}\\\\\n\\Omega_{31} & -\\Omega_{23} & 0\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\nAny tensor can be written as the sum of a symmetric and antisymmetric tensor,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{T}=\\mathbf{T}^{S}+\\mathbf{T}^{A}\\quad\\text{where}\\quad\\mathbf{T}^{S}=\\frac{1}{2}\\left(\\mathbf{T}+\\mathbf{T}^{T}\\right)\\quad\\text{and}\\quad\\mathbf{T}^{A}=\\frac{1}{2}\\left(\\mathbf{T}-\\mathbf{T}^{T}\\right)\n\\]\n\n\\end_inset\n\nThis is a unique decomposition.\n It can be checked that \n\\begin_inset Formula $\\mathbf{T}^{S}$\n\\end_inset\n\n is symmetric and \n\\begin_inset Formula $\\mathbf{T}^{A}$\n\\end_inset\n\n is antisymmetric.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ndual vector\n\\shape default\n \n\\begin_inset Formula $\\boldsymbol{\\omega}$\n\\end_inset\n\n of an antisymmetric tensor \n\\begin_inset Formula $\\boldsymbol{\\Omega}$\n\\end_inset\n\n satisfies \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\boldsymbol{\\Omega}\\cdot\\mathbf{a}=\\boldsymbol{\\omega}\\times\\mathbf{a}}\\label{eq25-1}\n\\end{equation}\n\n\\end_inset\n\n for any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n.\n Thus \n\\begin_inset Formula $\\Omega_{ij}=\\mathbf{e}_{i}\\cdot\\boldsymbol{\\Omega}\\cdot\\mathbf{e}_{j}=\\mathbf{e}_{i}\\cdot\\left(\\boldsymbol{\\omega}\\times\\mathbf{e}_{j}\\right)=\\omega_{k}\\mathbf{e}_{i}\\cdot\\left(\\mathbf{e}_{k}\\times\\mathbf{e}_{j}\\right)=\\omega_{k}\\mathbf{e}_{i}\\cdot\\varepsilon_{kjl}\\mathbf{e}_{l}=\\omega_{k}\\varepsilon_{kjl}\\delta_{il}$\n\\end_inset\n\n or \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\Omega_{ij}=-\\varepsilon_{ijk}\\omega_{k}}\\label{eq26-1}\n\\end{equation}\n\n\\end_inset\n\nIn matrix form,\n \n\\begin_inset Formula \n\\[\n\\left[\\boldsymbol{\\Omega}\\right]=\\left[\\begin{array}{ccc}\n0 & -\\omega_{3} & \\omega_{2}\\\\\n\\omega_{3} & 0 & -\\omega_{1}\\\\\n-\\omega_{2} & \\omega_{1} & 0\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\nConversely,\n it can also be shown that \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\omega_{i}=-\\frac{1}{2}\\varepsilon_{ijk}\\Omega_{jk}}\\label{eq27-1}\n\\end{equation}\n\n\\end_inset\n\nAs a homework problem,\n it may be shown that \n\\begin_inset Formula $\\varepsilon_{ijk}T_{jk}=\\varepsilon_{ijk}T_{jk}^{A}$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $\\varepsilon_{ijk}T_{jk}^{S}=0$\n\\end_inset\n\n for any symmetric tensor \n\\begin_inset Formula $\\mathbf{T}^{S}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nEigenvalues and Eigenvectors of Real Symmetric Tensors\n\\end_layout\n\n\\begin_layout Standard\nA second-order tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n has three pairs of eigenvalues \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and eigenvectors \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n that each satisfy\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{T}\\cdot\\mathbf{v}=\\lambda\\mathbf{v}\\label{eq:eigen-def}\n\\end{equation}\n\n\\end_inset\n\nThe eigenvalues \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n are the roots of the characteristic equation of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n which is the cubic polynomial produced by setting \n\\begin_inset Formula $\\det\\left(\\mathbf{T}-\\lambda\\mathbf{I}\\right)=0$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\begin{equation}\n-\\lambda^{3}+I_{1}\\lambda^{2}-I_{2}\\lambda+I_{3}=0\\label{eq:eigen-char-eqn}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}I_{1} & =\\tr\\mathbf{T}\\\\\nI_{2} & =\\frac{1}{2}\\left(I_{1}^{2}-\\tr\\mathbf{T}^{2}\\right)\\\\\nI_{3} & =\\det\\mathbf{T}\n\\end{aligned}\n\\label{eq:eigen-invariants}\n\\end{equation}\n\n\\end_inset\n\nare called \n\\emph on\ninvariants\n\\emph default\n of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nAccording to the Cayley-Hamilton theorem,\n a tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n satisfies its own characteristic equation,\n\\begin_inset Formula \n\\begin{equation}\n-\\mathbf{T}^{3}+I_{1}\\mathbf{T}^{2}-I_{2}\\mathbf{T}+I_{3}\\mathbf{I}=\\mathbf{0}\\label{eq:Cayley-Hamilton-theorem}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n the cubic power of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n can be expressed in terms of its lower powers according to \n\\begin_inset Formula $\\mathbf{T}^{3}=I_{1}\\mathbf{T}^{2}-I_{2}\\mathbf{T}+I_{3}\\mathbf{I}$\n\\end_inset\n\n.\n Taking the trace of this equation allows us to solve for \n\\begin_inset Formula $I_{3}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}I_{3} & =\\frac{1}{3}\\left(\\tr\\mathbf{T}^{3}-I_{1}\\tr\\mathbf{T}^{2}+I_{2}\\tr\\mathbf{T}\\right)\\\\\n & =\\frac{1}{3}\\left(\\tr\\mathbf{T}^{3}-I_{1}^{3}+3I_{1}I_{2}\\right)\n\\end{aligned}\n\\label{eq:eigen-I3-soln}\n\\end{equation}\n\n\\end_inset\n\nMultiplying eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:Cayley-Hamilton-theorem\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{T}^{-1}$\n\\end_inset\n\n also produces \n\\begin_inset Formula \n\\[\nI_{3}\\mathbf{T}^{-1}=\\mathbf{T}^{2}-I_{1}\\mathbf{T}+I_{2}\\mathbf{I}\n\\]\n\n\\end_inset\n\nUsing all these relations,\n we may differentiate the three invariants of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n to get\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\frac{\\partial I_{1}}{\\partial\\mathbf{T}} & =\\mathbf{I}\\\\\n\\frac{\\partial I_{2}}{\\partial\\mathbf{T}} & =I_{1}\\mathbf{I}-\\mathbf{T}^{T}\\\\\n\\frac{\\partial I_{3}}{\\partial\\mathbf{T}} & =I_{3}\\mathbf{T}^{-T}\n\\end{aligned}\n\\label{eq:eigen-dinv-dT}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Theorem*\nThe eigenvalues of real symmetric tensors are real (proof not provided here).\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Theorem*\nIf the eigenvalues of a real symmetric tensor are all distinct,\n the eigenvectors are orthogonal to each other.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\n\n\\shape italic\nProof:\n\n\\shape default\n Given \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{v}_{1}=\\lambda_{1}\\mathbf{v}_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{v}_{2}=\\lambda_{2}\\mathbf{v}_{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\lambda_{1}\\ne\\lambda_{2}$\n\\end_inset\n\n ,\n then \n\\begin_inset Formula $\\mathbf{v}_{2}\\cdot\\mathbf{T}\\cdot\\mathbf{v}_{1}=\\lambda_{1}\\mathbf{v}_{1}\\cdot\\mathbf{v}_{2}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}_{1}\\cdot\\mathbf{T}\\cdot\\mathbf{v}_{2}=\\lambda_{2}\\mathbf{v}_{1}\\cdot\\mathbf{v}_{2}=\\mathbf{v}_{2}\\cdot\\mathbf{T}^{T}\\cdot\\mathbf{v}_{1}=\\mathbf{v}_{2}\\cdot\\mathbf{T}\\cdot\\mathbf{v}_{1}$\n\\end_inset\n\n ,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Rightarrow\\lambda_{1}\\mathbf{v}_{1}\\cdot\\mathbf{v}_{2}=\\lambda_{2}\\mathbf{v}_{1}\\cdot\\mathbf{v}_{2}\\quad\\text{or}\\quad\\left(\\lambda_{1}-\\lambda_{2}\\right)\\mathbf{v}_{1}\\cdot\\mathbf{v}_{2}=0\\quad\\Rightarrow\\mathbf{v}_{1}\\cdot\\mathbf{v}_{2}=0\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen two of the eigenvalues are repeated (a double root of the characteristic equation),\n the resulting eigenvectors are not necessarily orthogonal to each other;\n however,\n they remain orthogonal to the third eigenvector.\n This means that any vector lying in the plane normal to the third eigenvector is an eigenvector corresponding to the double root.\n Similarly,\n when all three eigenvalues are repeated (a triple root),\n any vector becomes an eigenvector of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Example\nIn hydrostatics the stress tensor is \n\\begin_inset Formula $\\mathbf{T}=-p\\mathbf{I}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is the hydrostatic pressure.\n In this case,\n \n\\begin_inset Formula $-p$\n\\end_inset\n\n is a triple root of the characteristic equation of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n Any vector \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $\\mathbf{T}\\cdot\\mathbf{v}=-p\\mathbf{v}$\n\\end_inset\n\n,\n and is thus an eigenvector of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn continuum mechanics the eigenvectors \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n of a tensor are generally normalized,\n\\begin_inset Formula \n\\[\n\\mathbf{n}\\equiv\\frac{\\mathbf{v}}{\\left|\\mathbf{v}\\right|}\n\\]\n\n\\end_inset\n\nThus,\n we can always find a set of three orthonormal eigenvectors \n\\begin_inset Formula $\\left\\{ \\mathbf{n}_{1},\\mathbf{n}_{2},\\mathbf{n}_{3}\\right\\} $\n\\end_inset\n\n for any real symmetric tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n,\n even when the eigenvalues are repeated.\n Given a tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n with eigenvalues \n\\begin_inset Formula $\\lambda_{1},\\lambda_{2},\\lambda_{3}$\n\\end_inset\n\n and eigenvectors \n\\begin_inset Formula $\\mathbf{n}_{1},\\mathbf{n}_{2},\\mathbf{n}_{3}$\n\\end_inset\n\n,\n the components of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n in the orthonormal basis \n\\begin_inset Formula $\\left\\{ \\mathbf{n}_{1},\\mathbf{n}_{2},\\mathbf{n}_{3}\\right\\} $\n\\end_inset\n\n can be obtained from \n\\begin_inset Formula \n\\[\nT_{i1}=\\mathbf{v}_{i}\\cdot\\mathbf{T}\\cdot\\mathbf{n}_{1}=\\lambda_{1}\\mathbf{n}_{i}\\cdot\\mathbf{n}_{1}=\\lambda_{1}\\delta_{i1}\\quad T_{i2}=\\lambda_{2}\\delta_{i2}T_{i3}=\\lambda_{3}\\delta_{i3}\n\\]\n\n\\end_inset\n\n Thus,\n \n\\begin_inset Formula \n\\[\n\\left[\\mathbf{T}\\right]_{\\mathbf{n}_{i}}=\\left[\\begin{array}{ccc}\n\\lambda_{1} & 0 & 0\\\\\n0 & \\lambda_{2} & 0\\\\\n0 & 0 & \\lambda_{3}\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nT_{1} & 0 & 0\\\\\n0 & T_{2} & 0\\\\\n0 & 0 & T_{3}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n Since \n\\begin_inset Formula $\\mathbf{T}=T_{ij}\\mathbf{n}_{i}\\otimes\\mathbf{n}_{j}=T_{i1}\\mathbf{n}_{i}\\otimes\\mathbf{n}_{1}+T_{i2}\\mathbf{n}_{i}\\otimes\\mathbf{n}_{2}+T_{i3}\\mathbf{n}_{i}\\otimes\\mathbf{n}_{3}$\n\\end_inset\n\n,\n we find that \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{T}=\\lambda_{1}\\mathbf{n}_{1}\\otimes\\mathbf{n}_{1}+\\lambda_{2}\\mathbf{n}_{2}\\otimes\\mathbf{n}_{2}+\\lambda_{3}\\mathbf{n}_{3}\\otimes\\mathbf{n}_{3}\\label{eq:eigen-spectral-rep}\n\\end{equation}\n\n\\end_inset\n\n This is known as the \n\\shape italic\nspectral representation\n\\shape default\n of the tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n.\n In particular,\n since the eigenvalues of the identity tensor are \n\\begin_inset Formula $\\lambda_{1}=\\lambda_{2}=\\lambda_{3}=1$\n\\end_inset\n\n,\n and since any vector is an eigenvector of \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n,\n we can select the basis vectors \n\\begin_inset Formula $\\mathbf{e}_{1},\\,\\mathbf{e}_{2},\\,\\mathbf{e}_{3}$\n\\end_inset\n\n so that the spectral representation of \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n may be given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{I}=\\mathbf{e}_{i}\\otimes\\mathbf{e}_{i}=\\mathbf{e}_{1}\\otimes\\mathbf{e}_{1}+\\mathbf{e}_{2}\\otimes\\mathbf{e}_{2}+\\mathbf{e}_{3}\\otimes\\mathbf{e}_{3}\\label{eq:eigen-identity-spectral}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOrthogonal Transformation of Tensors\n\\end_layout\n\n\\begin_layout Standard\nAn orthogonal transformation \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n transforms any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n into the vector \n\\begin_inset Formula $\\mathbf{Q}\\cdot\\mathbf{a}$\n\\end_inset\n\n,\n which we may denote as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{a}^{*}=\\mathbf{Q}\\cdot\\mathbf{a}\\label{eq:OT-vector}\n\\end{equation}\n\n\\end_inset\n\nRecall that a tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n may be expressed in its spectral representation as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:eigen-spectral-rep\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Each of its eigenvectors \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is transformed by \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n into \n\\begin_inset Formula $\\mathbf{n}^{*}=\\mathbf{Q}\\cdot\\mathbf{n}$\n\\end_inset\n\n.\n Since eigenvalues of \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n are invariant to orthogonal transformations,\n it follows that\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{T}^{*} & =\\lambda_{1}\\mathbf{n}_{1}^{*}\\otimes\\mathbf{n}_{1}^{*}+\\lambda_{2}\\mathbf{n}_{2}^{*}\\otimes\\mathbf{n}_{2}^{*}+\\lambda_{3}\\mathbf{n}_{3}^{*}\\otimes\\mathbf{n}_{3}^{*}\\\\\n & =\\lambda_{1}\\left(\\mathbf{Q}\\cdot\\mathbf{n}_{1}\\right)\\otimes\\left(\\mathbf{Q}\\cdot\\mathbf{n}_{1}\\right)+\\lambda_{2}\\left(\\mathbf{Q}\\cdot\\mathbf{n}_{2}\\right)\\otimes\\left(\\mathbf{Q}\\cdot\\mathbf{n}_{2}\\right)+\\lambda_{3}\\left(\\mathbf{Q}\\cdot\\mathbf{n}_{3}\\right)\\otimes\\left(\\mathbf{Q}\\cdot\\mathbf{n}_{3}\\right)\\\\\n & =\\mathbf{Q}\\cdot\\left(\\lambda_{1}\\mathbf{n}_{1}\\otimes\\mathbf{n}_{1}+\\lambda_{2}\\mathbf{n}_{2}\\otimes\\mathbf{n}_{2}+\\lambda_{3}\\mathbf{n}_{3}\\otimes\\mathbf{n}_{3}\\right)\\cdot\\mathbf{Q}^{T}\\\\\n & =\\mathbf{Q}\\cdot\\mathbf{T}\\cdot\\mathbf{Q}^{T}\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThus,\n the transformation of the second-order tensor \n\\begin_inset Formula $\\mathbf{T}$\n\\end_inset\n\n by \n\\begin_inset Formula $\\mathbf{Q}$\n\\end_inset\n\n is \n\\begin_inset Formula $\\mathbf{T}^{*}=\\mathbf{Q}\\cdot\\mathbf{T}\\cdot\\mathbf{Q}^{T}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nHigher Order Tensors\n\\end_layout\n\n\\begin_layout Standard\nNote that \n\\begin_inset Formula $\\mathbf{a}\\cdot\\mathbf{T}\\cdot\\mathbf{b}=\\mathbf{a}\\otimes\\mathbf{b}:\\mathbf{T}=\\mathbf{T}:\\mathbf{a}\\otimes\\mathbf{b}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\mathbf{a}\\otimes\\mathbf{b}:\\mathbf{c}\\otimes\\mathbf{d}=\\left(\\mathbf{a}\\cdot\\mathbf{c}\\right)\\left(\\mathbf{b}\\cdot\\mathbf{d}\\right)$\n\\end_inset\n\n.\n Similarly,\n \n\\begin_inset Formula $\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)\\cdot\\left(\\mathbf{c}\\otimes\\mathbf{d}\\right)=\\left(\\mathbf{b}\\cdot\\mathbf{c}\\right)\\mathbf{a}\\otimes\\mathbf{d}$\n\\end_inset\n\n.\n We will be using generalizations of these relations when examining higher order tensors.\n\\end_layout\n\n\\begin_layout Subsection\nThird-Order Tensors\n\\end_layout\n\n\\begin_layout Standard\nA third-order tensor \n\\begin_inset Formula $\\mathbb{T}$\n\\end_inset\n\n is a linear transformation that transforms any vector \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n into a second-order tensor \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbb{T}\\cdot\\mathbf{a}=\\mathbf{S}}\\label{eq62-1}\n\\end{equation}\n\n\\end_inset\n\nIn general,\n the dyadic product of three vectors is a third-order tensor which satisfies\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\left(\\mathbf{a}\\otimes\\mathbf{b}\\otimes\\mathbf{c}\\right)\\cdot\\left(\\alpha\\mathbf{u}+\\beta\\mathbf{v}\\right)=\\left(\\alpha\\mathbf{c}\\cdot\\mathbf{u}+\\beta\\mathbf{c}\\cdot\\mathbf{v}\\right)\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)}\\quad\\forall\\,\\mathbf{u},\\mathbf{v}\\,.\\label{eq62b}\n\\end{equation}\n\n\\end_inset\n\nAny third-order tensor \n\\begin_inset Formula $\\mathbb{T}$\n\\end_inset\n\n can be expressed in terms of its Cartesian components \n\\begin_inset Formula $T_{ijk}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathbb{T}=T_{ijk}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}}\\label{eq62d}\n\\end{equation}\n\n\\end_inset\n\nand the Cartesian components of a third-order tensor may be evaluated from\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{T_{ijk}=\\mathbf{e}_{i}\\cdot\\left(\\mathbb{T}\\cdot\\mathbf{e}_{k}\\right)\\cdot\\mathbf{e}_{j}}\\label{eq62c}\n\\end{equation}\n\n\\end_inset\n\nIt follows from Eqs.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq62b\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq62d\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n that\n\\begin_inset Formula \n\\[\n\\mathbb{T}\\cdot\\mathbf{a}=T_{ijk}\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}\\right)\\cdot\\mathbf{a}=T_{ijk}\\left(\\mathbf{e}_{k}\\cdot\\mathbf{a}\\right)\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right)=T_{ijk}a_{k}\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right)=\\mathbf{S}=S_{ij}\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right)\n\\]\n\n\\end_inset\n\nso that the indicial form of eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq62-1\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is\n\\begin_inset Formula \n\\begin{equation}\nT_{ijk}a_{k}=S_{ij}\\label{eq62e}\n\\end{equation}\n\n\\end_inset\n\nIn particular,\n\\begin_inset Formula \n\\begin{equation}\n\\mathbb{T}\\cdot\\mathbf{e}_{k}=T_{ijk}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\label{eq62f}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe double dot product of a third-order tensor with a second-order tensor is defined by\n\\begin_inset Formula \n\\[\n\\boxed{\\left(\\mathbf{a}\\otimes\\mathbf{b}\\otimes\\mathbf{c}\\right):\\left(\\mathbf{d}\\otimes\\mathbf{e}\\right)=\\left(\\mathbf{b}\\cdot\\mathbf{d}\\right)\\left(\\mathbf{c}\\cdot\\mathbf{e}\\right)\\mathbf{a}}\\,,\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\n\\boxed{\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right):\\left(\\mathbf{c}\\otimes\\mathbf{d}\\otimes\\mathbf{e}\\right)=\\left(\\mathbf{a}\\cdot\\mathbf{c}\\right)\\left(\\mathbf{b}\\cdot\\mathbf{d}\\right)\\mathbf{e}}\\,.\n\\]\n\n\\end_inset\n\nTherefore,\n the double dot product of a third-order tensor with a second-order tensor is a vector given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbb{T}:\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)=\\left(\\mathbb{T}\\cdot\\mathbf{b}\\right)\\cdot\\mathbf{a}\\,.\\label{eq63f}\n\\end{equation}\n\n\\end_inset\n\nProof:\n Using \n\\begin_inset Formula $\\mathbb{T}\\cdot\\mathbf{b}=T_{ijk}b_{k}\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right)$\n\\end_inset\n\n,\n we find that \n\\begin_inset Formula $\\left(\\mathbb{T}\\cdot\\mathbf{b}\\right)\\cdot\\mathbf{a}=T_{ijk}b_{k}\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right)\\cdot\\mathbf{a}=T_{ijk}b_{k}\\left(\\mathbf{a}\\cdot\\mathbf{e}_{j}\\right)\\mathbf{e}_{i}=T_{ijk}a_{j}b_{k}\\mathbf{e}_{i}$\n\\end_inset\n\n.\n Similarly,\n \n\\begin_inset Formula $\\mathbb{T}:\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)=T_{ijk}\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}\\right):\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)=T_{ijk}\\left(\\mathbf{e}_{j}\\cdot\\mathbf{a}\\right)\\left(\\mathbf{e}_{k}\\cdot\\mathbf{b}\\right)\\mathbf{e}_{i}=T_{ijk}a_{j}b_{k}\\mathbf{e}_{i}$\n\\end_inset\n\n,\n thus completing the proof.\n \n\\end_layout\n\n\\begin_layout Standard\nFor any second-order tensor \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n,\n it also follows that\n\\begin_inset Formula \n\\[\n\\mathbb{T}:\\mathbf{S}=T_{ijk}S_{jk}\\mathbf{e}_{i}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Example\nIf we introduce the notation \n\\begin_inset Formula $\\mathbb{E}$\n\\end_inset\n\n as the third-order (pseudo-)tensor of Cartesian components \n\\begin_inset Formula $\\varepsilon_{ijk}$\n\\end_inset\n\n,\n the relation between an antisymmetric tensor and its dual vector can also be written as\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\boldsymbol{\\Omega}=-\\mathbb{E}\\cdot\\boldsymbol{\\omega}}\\label{eq26b}\n\\end{equation}\n\n\\end_inset\n\nSimilarly,\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\boldsymbol{\\omega}=-\\frac{1}{2}\\mathbb{E}:\\boldsymbol{\\Omega}}\\label{eq27b}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFourth-Order Tensors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fourth-Order-Tensors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe dyadic product of four vectors is a fourth-order tensor \n\\begin_inset Formula $\\mathbf{a}\\otimes\\mathbf{b}\\otimes\\mathbf{c}\\otimes\\mathbf{d}$\n\\end_inset\n\n,\n defined as\n\\begin_inset Formula \n\\begin{equation}\n\\left(\\mathbf{a}\\otimes\\mathbf{b}\\otimes\\mathbf{c}\\otimes\\mathbf{d}\\right)\\cdot\\mathbf{v}=\\left(\\mathbf{d}\\cdot\\mathbf{v}\\right)\\left(\\mathbf{a}\\otimes\\mathbf{b}\\otimes\\mathbf{c}\\right)\\label{eq:63g}\n\\end{equation}\n\n\\end_inset\n\nThe Cartesian components of a fourth-order tensor \n\\begin_inset Formula $\\mathcal{T}$\n\\end_inset\n\n are given by\n\\begin_inset Formula \n\\begin{equation}\nT_{ijkl}=\\left(\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\right):\\mathcal{T}:\\left(\\mathbf{e}_{k}\\otimes\\mathbf{e}_{l}\\right)\\label{eq:63h}\n\\end{equation}\n\n\\end_inset\n\nsuch that\n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{T}=T_{ijkl}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}\\otimes\\mathbf{e}_{l}\\label{eq:63i}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n a fourth-order tensor transforms a vector into a third-order tensor,\n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{T}\\cdot\\mathbf{a}=\\left(T_{ijkl}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}\\otimes\\mathbf{e}_{l}\\right)\\cdot\\mathbf{a}=T_{ijkl}a_{l}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}\\equiv S_{ijk}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\otimes\\mathbf{e}_{k}=\\mathbb{S}\\label{eq:63j}\n\\end{equation}\n\n\\end_inset\n\nThe double dot product of a fourth-order tensor with a second-order tensor is a second-order tensor defined as\n\\begin_inset Formula \n\\begin{equation}\n\\boxed{\\mathcal{T}:\\left(\\mathbf{a}\\otimes\\mathbf{b}\\right)=\\left(\\mathcal{T}\\cdot\\mathbf{b}\\right)\\cdot\\mathbf{a}}\\label{eq:63k}\n\\end{equation}\n\n\\end_inset\n\nfrom which it can be shown that\n\\begin_inset Formula \n\\begin{equation}\n\\mathcal{T}:\\mathbf{S}=T_{ijmn}S_{mn}\\mathbf{e}_{i}\\otimes\\mathbf{e}_{j}\\label{eq:63l}\n\\end{equation}\n\n\\end_inset\n\nor equivalently,\n \n\\begin_inset Formula $\\left(\\mathcal{T}:\\mathbf{S}\\right)_{ij}=T_{ijkl}S_{kl}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nA fourth-order tensor can exhibit three levels of symmetry,\n which can be represented using Cartesian components as\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}T_{ijkl} & =T_{jikl} & \\text{left minor symmetry}\\\\\nT_{ijkl} & =T_{ijlk} & \\text{right minor symmetry}\\\\\nT_{ijkl} & =T_{klij} & \\text{major symmetry}\n\\end{aligned}\n\\label{eq:tens4-symmetries}\n\\end{equation}\n\n\\end_inset\n\nWhereas a general fourth-order tensor may have 81 distinct components,\n a tensor with one minor symmetry has 54 distinct components;\n a tensor with both minor symmetries has 36 distinct components;\n and a tensor with minor and major symmetries has 21distinct components.\n We may represent the major symmetry of \n\\begin_inset Formula $\\mathcal{T}$\n\\end_inset\n\n as \n\\begin_inset Formula $\\mathcal{T}=\\mathcal{T}^{T}$\n\\end_inset\n\n,\n whose Cartesian representation is provided above.\n It follows from this definition that\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{S}:\\mathcal{T} & =\\mathcal{T}^{T}:\\mathbf{S}\\\\\n\\left(\\mathbf{S}:\\mathcal{T}\\right)_{ij} & =S_{kl}T_{klij}=T_{ijkl}^{T}S_{kl}\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nAdditional Tensor Products\n\\end_layout\n\n\\begin_layout Standard\nEarlier we saw that the dyadic product of vectors can produce tensors.\n Similarly,\n we can define dyadic products of tensors which produce higher-order tensors.\n In particular,\n the following products of second-order tensors \n\\begin_inset Formula $\\mathbf{A}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{B}$\n\\end_inset\n\n produce fouth-order tensors,\n satisfying\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\left(\\mathbf{A}\\otimes\\mathbf{B}\\right):\\mathbf{S} & =\\left(\\mathbf{B}:\\mathbf{S}\\right)\\mathbf{A}\\\\\n\\left(\\mathbf{A}\\oslash\\mathbf{B}\\right):\\mathbf{S} & =\\mathbf{A}\\cdot\\mathbf{S}\\cdot\\mathbf{B}^{T}\\\\\n\\left(\\mathbf{A}\\obslash\\mathbf{B}\\right):\\mathbf{S} & =\\mathbf{A}\\cdot\\mathbf{S}^{T}\\cdot\\mathbf{B}^{T}\\\\\n\\left(\\mathbf{A}\\odot\\mathbf{B}\\right):\\mathbf{S} & =\\frac{1}{2}\\left(\\mathbf{A}\\oslash\\mathbf{B}+\\mathbf{A}\\obslash\\mathbf{B}\\right):\\mathbf{S}=\\frac{1}{2}\\left(\\mathbf{A}\\cdot\\mathbf{S}\\cdot\\mathbf{B}^{T}+\\mathbf{A}\\cdot\\mathbf{S}^{T}\\cdot\\mathbf{B}^{T}\\right)\n\\end{aligned}\n\\label{eq:tensor-products}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{S}$\n\\end_inset\n\n is any second-order tensor.\n Using Cartesian components of tensors,\n the indicial form of these tensor products are\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\left(\\mathbf{A}\\otimes\\mathbf{B}\\right)_{ijkl} & =A_{ij}B_{kl}\\\\\n\\left(\\mathbf{A}\\oslash\\mathbf{B}\\right)_{ijkl} & =A_{ik}B_{jl}\\\\\n\\left(\\mathbf{A}\\obslash\\mathbf{B}\\right)_{ijkl} & =A_{il}B_{jk}\\\\\n\\left(\\mathbf{A}\\odot\\mathbf{B}\\right)_{ijkl} & =\\frac{1}{2}\\left(A_{ik}B_{jl}+A_{il}B_{jk}\\right)\n\\end{aligned}\n\\label{eq:tensor-products-Cartesian}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset bibtex\nLatexCommand bibtex\nbibfiles \"FEBio3\"\noptions \"bibtotoc,plain\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_body\n\\end_document\n"
  },
  {
    "path": "Documentation/FEBio_User_Manual.lyx",
    "content": "#LyX 2.4 created this file. For more info see https://www.lyx.org/\n\\lyxformat 620\n\\begin_document\n\\begin_header\n\\save_transient_properties true\n\\origin unavailable\n\\textclass extbook\n\\begin_preamble\n\\usepackage{latexsym}\n\\hypersetup{colorlinks=true,citecolor=blue,filecolor=blue,urlcolor=blue,linkcolor=blue}\n\\end_preamble\n\\use_default_options false\n\\begin_modules\ntheorems-ams\neqs-within-sections\nfigs-within-sections\n\\end_modules\n\\maintain_unincluded_children no\n\\language english\n\\language_package none\n\\inputencoding auto-legacy\n\\fontencoding auto\n\\font_roman \"default\" \"default\"\n\\font_sans \"helvet\" \"default\"\n\\font_typewriter \"default\" \"default\"\n\\font_math \"auto\" \"auto\"\n\\font_default_family sfdefault\n\\use_non_tex_fonts false\n\\font_sc false\n\\font_roman_osf false\n\\font_sans_osf false\n\\font_typewriter_osf false\n\\font_sf_scale 100 100\n\\font_tt_scale 100 100\n\\use_microtype false\n\\use_dash_ligatures false\n\\graphics default\n\\default_output_format default\n\\output_sync 0\n\\bibtex_command default\n\\index_command default\n\\paperfontsize 11\n\\spacing single\n\\use_hyperref true\n\\pdf_bookmarks true\n\\pdf_bookmarksnumbered true\n\\pdf_bookmarksopen false\n\\pdf_bookmarksopenlevel 1\n\\pdf_breaklinks false\n\\pdf_pdfborder true\n\\pdf_colorlinks false\n\\pdf_backref false\n\\pdf_pdfusetitle true\n\\papersize default\n\\use_geometry true\n\\use_package amsmath 2\n\\use_package amssymb 2\n\\use_package cancel 0\n\\use_package esint 0\n\\use_package mathdots 0\n\\use_package mathtools 0\n\\use_package mhchem 0\n\\use_package stackrel 0\n\\use_package stmaryrd 0\n\\use_package undertilde 0\n\\cite_engine natbib\n\\cite_engine_type numerical\n\\biblio_style plain\n\\use_bibtopic false\n\\use_indices false\n\\paperorientation portrait\n\\suppress_date false\n\\justification true\n\\use_refstyle 0\n\\use_formatted_ref 0\n\\use_minted 0\n\\use_lineno 0\n\\index Index\n\\shortcut idx\n\\color #008000\n\\end_index\n\\spellchecker_ignore english feb\n\\spellchecker_ignore english xplt\n\\spellchecker_ignore english fluid-FSI\n\\spellchecker_ignore english Broyden's\n\\spellchecker_ignore english qnmethod\n\\spellchecker_ignore english rtol\n\\spellchecker_ignore english Globals\n\\spellchecker_ignore english Fc\n\\spellchecker_ignore english triphasic\n\\spellchecker_ignore english Glc\n\\spellchecker_ignore english SolidBoundMolecules\n\\spellchecker_ignore english sbm\n\\spellchecker_ignore english 'fem\n\\spellchecker_ignore english '\n\\spellchecker_ignore english mymat\n\\spellchecker_ignore english neo-Hookean\n\\spellchecker_ignore english NodeSet\n\\spellchecker_ignore english DiscreteSet\n\\spellchecker_ignore english ElementSet\n\\spellchecker_ignore english SurfacePair\n\\spellchecker_ignore english PartList\n\\spellchecker_ignore english trilinear\n\\spellchecker_ignore english pentahedral\n\\spellchecker_ignore english pentrahedral\n\\spellchecker_ignore english Bischoff\n\\spellchecker_ignore english et\n\\spellchecker_ignore english al\n\\spellchecker_ignore english EAS\n\\spellchecker_ignore english Vu-Quoc\n\\spellchecker_ignore english Klinkel\n\\spellchecker_ignore english normals\n\\spellchecker_ignore english Jacobian\n\\spellchecker_ignore english 'shell\n\\spellchecker_ignore english stress'\n\\spellchecker_ignore english strain'\n\\spellchecker_ignore english ShellDomain\n\\spellchecker_ignore english BeamDomain\n\\spellchecker_ignore english nodesets\n\\spellchecker_ignore english delem\n\\spellchecker_ignore english allParts\n\\spellchecker_ignore english bc\n\\spellchecker_ignore english nodeset\n\\spellchecker_ignore english MeshDomains\n\\spellchecker_ignore english SolidDomain\n\\spellchecker_ignore english udg-hex\n\\spellchecker_ignore english sri-solid\n\\spellchecker_ignore english Gauss-Lobatto\n\\spellchecker_ignore english Lobatto\n\\spellchecker_ignore english tet\n\\spellchecker_ignore english nodally\n\\spellchecker_ignore english iso\n\\spellchecker_ignore english elastic-shell-eas\n\\spellchecker_ignore english MeshData\n\\spellchecker_ignore english NodeData\n\\spellchecker_ignore english SurfaceData\n\\spellchecker_ignore english ElementData\n\\spellchecker_ignore english procedurally\n\\spellchecker_ignore english Emap\n\\spellchecker_ignore english const\n\\spellchecker_ignore english EdgeData\n\\spellchecker_ignore english Adaptor\n\\spellchecker_ignore english FEAMR\n\\spellchecker_ignore english adaptor\n\\spellchecker_ignore english remeshing\n\\spellchecker_ignore english Remeshing\n\\spellchecker_ignore english visco-elastic\n\\spellchecker_ignore english MeshAdaptor\n\\spellchecker_ignore english adapators\n\\spellchecker_ignore english iters\n\\spellchecker_ignore english elems\n\\spellchecker_ignore english mmg\n\\spellchecker_ignore english remesh\n\\spellchecker_ignore english hausdorff\n\\spellchecker_ignore english XY\n\\spellchecker_ignore english dof\n\\spellchecker_ignore english ic\n\\spellchecker_ignore english init\n\\spellchecker_ignore english vx\n\\spellchecker_ignore english Prestrain\n\\spellchecker_ignore english prestrain\n\\spellchecker_ignore english eq\n\\spellchecker_ignore english Nodesets\n\\spellchecker_ignore english sx\n\\spellchecker_ignore english sy\n\\spellchecker_ignore english sz\n\\spellchecker_ignore english lc\n\\spellchecker_ignore english pos\n\\spellchecker_ignore english rb\n\\spellchecker_ignore english RCR\n\\spellchecker_ignore english Windkessel\n\\spellchecker_ignore english loadcurve\n\\spellchecker_ignore english Rz\n\\spellchecker_ignore english Rv\n\\spellchecker_ignore english Rw\n\\spellchecker_ignore english vy\n\\spellchecker_ignore english vz\n\\spellchecker_ignore english euler\n\\spellchecker_ignore english Ey\n\\spellchecker_ignore english Ez\n\\spellchecker_ignore english dofs\n\\spellchecker_ignore english maxaug\n\\spellchecker_ignore english minaug\n\\spellchecker_ignore english gaptol\n\\spellchecker_ignore english angtol\n\\spellchecker_ignore english revolute\n\\spellchecker_ignore english laugon\n\\spellchecker_ignore english Revolute\n\\spellchecker_ignore english xy-plane\n\\spellchecker_ignore english LoadData\n\\spellchecker_ignore english loadcontroller\n\\spellchecker_ignore english loadpoint\n\\spellchecker_ignore english fluidflux\n\\spellchecker_ignore english soluteflux\n\\spellchecker_ignore english heatflux\n\\spellchecker_ignore english hc\n\\spellchecker_ignore english Backflow\n\\spellchecker_ignore english backflow\n\\spellchecker_ignore english unitless\n\\spellchecker_ignore english Poiseuille\n\\spellchecker_ignore english wx\n\\spellchecker_ignore english wy\n\\spellchecker_ignore english wz\n\\spellchecker_ignore english axisymmetric\n\\spellchecker_ignore english Fluid-FSI\n\\spellchecker_ignore english FSI\n\\spellchecker_ignore english Biphasic-FSI\n\\spellchecker_ignore english biphasic-FSI\n\\spellchecker_ignore english inhomogeneous\n\\spellchecker_ignore english inhomogeneity\n\\spellchecker_ignore english non-const\n\\spellchecker_ignore english blt\n\\spellchecker_ignore english bsf\n\\spellchecker_ignore english tol\n\\spellchecker_ignore english centrifual\n\\spellchecker_ignore english ay\n\\spellchecker_ignore english az\n\\spellchecker_ignore english --\n\\spellchecker_ignore english FEBio's\n\\spellchecker_ignore english Laursen's\n\\spellchecker_ignore english symmetrized\n\\spellchecker_ignore english SBP\n\\spellchecker_ignore english SBS\n\\spellchecker_ignore english SMP\n\\spellchecker_ignore english CP\n\\spellchecker_ignore english aug\n\\spellchecker_ignore english fric\n\\spellchecker_ignore english coeff\n\\spellchecker_ignore english ktmult\n\\spellchecker_ignore english seg\n\\spellchecker_ignore english Macauley\n\\spellchecker_ignore english Symmetrized\n\\spellchecker_ignore english nonsymmetric\n\\spellchecker_ignore english -\n\\spellchecker_ignore english in-situ\n\\spellchecker_ignore english augtol\n\\spellchecker_ignore english Vmax\n\\spellchecker_ignore english Fmax\n\\spellchecker_ignore english Lmax\n\\spellchecker_ignore english Sv\n\\spellchecker_ignore english Ftl\n\\spellchecker_ignore english Ftv\n\\spellchecker_ignore english xml\n\\spellchecker_ignore english dmp\n\\spellchecker_ignore english levmar\n\\spellchecker_ignore english DWORD\n\\leftmargin 1in\n\\topmargin 1in\n\\rightmargin 1in\n\\bottommargin 1in\n\\secnumdepth 3\n\\tocdepth 3\n\\paragraph_separation indent\n\\paragraph_indentation default\n\\is_math_indent 0\n\\math_numbering_side default\n\\quotes_style english\n\\dynamic_quotes 0\n\\papercolumns 1\n\\papersides 2\n\\paperpagestyle default\n\\tablestyle default\n\\tracking_changes false\n\\output_changes false\n\\change_bars false\n\\postpone_fragile_content false\n\\html_math_output 0\n\\html_css_as_file 0\n\\html_be_strict false\n\\docbook_table_output 0\n\\docbook_mathml_prefix 1\n\\end_header\n\n\\begin_body\n\n\\begin_layout Standard\n\\begin_inset FormulaMacro\n\\newcommand{\\Dev}{\\operatorname{Dev}}\n{\\text{Dev}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\dev}{\\operatorname{dev}}\n{\\text{dev}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\grad}{\\operatorname{grad}}\n{\\text{grad}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\divg}{\\operatorname{div}}\n{\\text{div}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\Ei}{\\operatorname{Ei}}\n{\\text{Ei}}\n\\end_inset\n\n\n\\begin_inset FormulaMacro\n\\newcommand{\\tr}{\\operatorname{tr}}\n{\\text{tr}}\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Title\n\\begin_inset Graphics\n\tfilename Figures/FigFEBioTitle.png\n\twidth 4.88in\n\n\\end_inset\n\n\n\\series bold\n\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nUser's Manual Version 4.12\n\\end_layout\n\n\\begin_layout Date\n\n\\series bold\nLast Updated:\n February 25,\n 2026\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Paragraph*\nContributors\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSteve Maas (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:steve.maas@utah.edu}{steve.maas@utah.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nDr.\n Jeffrey Weiss (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:jeff.weiss@utah.edu}{jeff.weiss@utah.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nDr.\n Gerard Ateshian (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{mailto:ateshian@columbia.edu}{ateshian@columbia.edu}\n\\end_layout\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout Paragraph*\nContact address\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMusculoskeletal Research Laboratories,\n University of Utah\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n72 S.\n Central Campus Drive,\n Room 2646\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nSalt Lake City,\n Utah\n\\end_layout\n\n\\begin_layout Paragraph*\nWebsite\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWeiss Lab:\n \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{https://weisslabutah.org}\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\nFEBio:\n \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{http://febio.org}\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Paragraph*\nForum\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{https://forums.febio.org/}\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Paragraph*\nAcknowledgments\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\n\n\\backslash\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDevelopment of the FEBio project is supported in part by a grant from the U.S.\n National Institutes of Health (R01GM083925).\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Graphics\n\tfilename Figures/NIHlogo.png\n\tlyxscale 25\n\twidth 2cm\n\tspecial height=0.75in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\n\\begin_inset CommandInset toc\nLatexCommand tableofcontents\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nIntroduction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:introduction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nOverview of FEBio\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:overview\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio is a nonlinear finite element solver that is specifically designed for biomechanical applications.\n It offers modeling scenarios,\n constitutive models and boundary conditions that are relevant to many research areas in biomechanics,\n thus offering a powerful tool for solving 3D problems in computational biomechanics.\n The software is open-source and the source code,\n as well as pre-compiled executables for Windows,\n OS-X,\n and Linux platforms are available for download at \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{http://febio.org}\n\\end_layout\n\n\\end_inset\n\n.\n This chapter presents a brief overview of the available features of FEBio.\n\\end_layout\n\n\\begin_layout Standard\nFEBio is a multi-physics code and can solve problems in structural mechanics,\n biphasic and multiphasic physics,\n fluid mechanics,\n and fluid-solid interaction (FSI).\n Both (quasi-) static (or steady-state) and dynamic (or transient) analyses can be performed in each of the different physics modules.\n For instance,\n in the structural mechanics module,\n the (quasi-) static response of the system is sought in a quasi-static analysis and the effects of inertia are ignored.\n In a dynamic analysis,\n the inertial effects are included in the governing equations to calculate the time dependent response of the system.\n In the biphasic module,\n a coupled solid-fluid problem is solved.\n In a transient biphasic analysis the time dependent response of both the solid and the fluid phase is determined.\n For the steady-state analysis the final relaxed state is recovered.\n Similarly,\n for multiphasic problems,\n both the time dependent transient response as well as the steady-state response can be determined.\n For fluid analyses,\n dynamic and steady-state responses may be specified.\n\\end_layout\n\n\\begin_layout Standard\nMany nonlinear constitutive models are available,\n allowing the user to model the often complicated biological tissue behavior.\n Several isotropic constitutive models are supported such as Neo-Hookean,\n Mooney-Rivlin,\n Ogden,\n Arruda-Boyce and Veronda-Westmann.\n All these models have a nonlinear stress-strain response and are objective for large deformations.\n In addition to the isotropic models there are several transversely isotropic and orthotropic constitutive models available.\n These models exhibit anisotropic behavior in a single or multiple preferred directions and are useful for representing biological tissues such as tendons,\n muscles,\n cartilage and other tissues that contain fibers.\n FEBio also contains a \n\\shape italic\nrigid body \n\\shape default\nconstitutive model.\n This model can be used to represent materials or structures whose deformation is negligible compared to that of other materials in the overall model.\n Several constitutive models are available for representing the solid phase of biphasic and multiphasic materials,\n which are materials that contain both a solid phase and a fluid phase.\n For incompressible materials FEBio employs special algorithms for enforcing the incompressibility constraint.\n A three-field formulation is used for tri-linear hexahedral and wedge elements.\n This algorithm allows the user to capture the accurate response of highly incompressible materials.\n\\end_layout\n\n\\begin_layout Standard\nFEBio can now also solve first-order computational homogenization problems.\n In such problems,\n the response of the macro-model is determined by the averaged local response of a representative volume element (RVE).\n The deformation of the macro-model,\n and more specifically the local deformation gradient,\n is applied to a RVE model which in turns determines the stress (and tangent) of the macro-model.\n\\end_layout\n\n\\begin_layout Standard\nFEBio supports a wide range of boundary conditions and loads to model interactions between materials that are relevant to problems in biomechanics.\n Deformable models can be connected to rigid bodies.\n With this feature,\n the user can model prescribed rotations and torques for rigid segments,\n thereby allowing the coupling of rigid body mechanics with deformable continuum mechanics.\n FEBio provides the ability to represent frictionless and frictional contact between rigid and/or deformable materials using sliding interfaces.\n A sliding surface is defined between two surfaces that are allowed to separate and slide across each other but are not allowed to penetrate.\n Variations of the sliding interface,\n such as tied interfaces,\n tied-sliding (tension-compression) and rigid walls,\n are available as well.\n As of version 1.2 it is also possible to model the fluid flow across two contacting biphasic materials.\n Finally,\n the user may specify a body force to model the effects such as,\n gravity,\n base acceleration or centripetal acceleration.\n\\end_layout\n\n\\begin_layout Standard\nFEBio has a large library of element formulations.\n These include linear and quadratic tetrahedral,\n hexahedral and pentahedral (wedge) elements.\n FEBio also supports triangular quadrilateral shell elements,\n with linear and quadratic interpolations.\n\\end_layout\n\n\\begin_layout Standard\nIn order to facilitate the process of customizing FEBio,\n a plugin framework is available.\n Plugins allow researchers to add new materials,\n boundary conditions,\n loads,\n and more,\n without the need to edit and recompile the FEBio source code itself.\n \n\\end_layout\n\n\\begin_layout Standard\nFEBio is a nonlinear implicit FE solver and does not have mesh generation capabilities.\n The finite element mesh,\n as well as all constitutive parameters and loading is defined in an input file,\n the format of which is described in detail in this document.\n This input file needs to be generated by preprocessing software.\n The preferred preprocessor for FEBio is called \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/febio-studio/}{\n\\backslash\nemph{FEBioStudio}}\n\\end_layout\n\n\\end_inset\n\n.\n FEBioStudio can convert some other formats to the FEBio input specification.\n For instance,\n NIKE3D \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Maker95\"\nliteral \"true\"\n\n\\end_inset\n\n and Abaqus input files can be imported in FEBioStudio and can be exported as a FEBio input file.\n \n\\end_layout\n\n\\begin_layout Section\nAbout this document\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:about\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis document is part of a set of three manuals that accompany FEBio:\n the User's Manual,\n describing how to use FEBio (this manual),\n a \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://help.febio.org/doxygen/html/index.html}{\n\\backslash\nemph{Developer's Manual}}\n\\end_layout\n\n\\end_inset\n\n for users who wish to modify or add features to the code,\n and a \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n,\n which describes the theory behind the FEBio algorithms.\n\\begin_inset Foot\nstatus open\n\n\\begin_layout Plain Layout\nThe User and Theory manuals are also available in pdf form,\n but the Developer's manual is only available online.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis document discusses how to use FEBio and describes the input file format in detail.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:running-FEBio\"\nnolink \"false\"\n\n\\end_inset\n\n describes how to run FEBio and explains the various command line options.\n It also discusses the different files that are required and created by FEBio.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Free-Format-Input\"\nnolink \"false\"\n\n\\end_inset\n\n describes the format of the FEBio input file.\n An XML-based format is used,\n organizing the data in a convenient hierarchical structure.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Materials\"\nnolink \"false\"\n\n\\end_inset\n\n gives a detailed overview of the available constitutive models.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Restart-Input-file\"\nnolink \"false\"\n\n\\end_inset\n\n discusses the restart capabilities of FEBio.\n The restart feature allows the user to interrupt a run and continue it at a later time,\n optionally making changes to the problem data.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Multi-step-Analysis\"\nnolink \"false\"\n\n\\end_inset\n\n describes the multi-step analysis feature,\n which allows the user to split up the entire analysis into several steps.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Parameter-Optimization\"\nnolink \"false\"\n\n\\end_inset\n\n explains how to setup and run a parameter optimization problem using FEBio's optimization module.\n Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Troubleshooting\"\nnolink \"false\"\n\n\\end_inset\n\n provides helpful information for troubleshooting an FEBio model and offers guidelines that help users avoid common problems.\n Chapter \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Configuration-File\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n discusses the configuration file,\n which allow users to customize how FEBio runs on their system.\n For instance,\n users can configure the default linear solver that will be used for solving models.\n Chapter \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:FEBio-Plugins\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n explains how use plugins with FEBio.\n \n\\end_layout\n\n\\begin_layout Section\nFEBio Basics\n\\end_layout\n\n\\begin_layout Standard\nThis section provides a brief overview of how FEBio works.\n For more details regarding the algorithms in FEBio,\n please consult the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen FEBio starts,\n first it will load the configuration file where it will find instructions on what linear solver to use,\n what plugins to load,\n etc.\n Please see section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Configuration-File\"\nnolink \"false\"\n\n\\end_inset\n\n for more information regarding the configuration file.\n \n\\end_layout\n\n\\begin_layout Standard\nUsually FEBio will read the input file that was specified on the command line next.\n The input file contains all the information that FEBio needs to build the FE Model and solve it.\n At the very least the input file will define the mesh,\n the materials,\n boundary conditions,\n time stepping,\n and analysis parameters.\n This document describes the structure of the FEBio input file in detail.\n See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Free-Format-Input\"\nnolink \"false\"\n\n\\end_inset\n\nand \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nNext,\n FEBio will start solving the model defined by the input file and proceed as follows:\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nLoop over all analysis steps\n\\series default\n.\n A model can define multiple analysis steps.\n In each step the boundary conditions,\n time stepping,\n and analysis parameters can be modified (e.g.\n a two-step analysis where a model is loaded statically in the first step.\n In the second step the load is released and the dynamic response is sought.)\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nFor each analysis step,\n loop over all time steps\n\\series default\n:\n In each analysis step,\n loads are usually applied incrementally for stability reasons over a period of time.\n The time parameter can represent the actual physical time (e.g.\n in dynamic simulations),\n or a pseudo-time (e.g.\n quasi-static loading of an elastic model.).\n For more on time stepping see \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Control-Section\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nSolve each time step\n\\series default\n:\n for each time step FEBio will solve the corresponding finite element equations.\n Usually they are nonlinear and thus are solved with a nonlinear solution strategy.\n In FEBio this is a variation of the Newton method.\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Control-Section\"\nnolink \"false\"\n\n\\end_inset\n\nfor more information.\n \n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nCheck convergence\n\\series default\n:\n Since Newton's method is an iterative method convergence is determined by comparing the norms of the solution and residual to the user-defined values in the input file.\n What norms are used will depend on the physics of the problem,\n as well as on several user control parameters.\n See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Control-Section\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nAugmentation\n\\series default\n:\n In models that define some type of constraint (e.g.\n contact,\n rigid joints,\n etc.),\n FEBio will do an additional calculation after a time step converges,\n called an augmentation.\n This is because FEBio does not calculate the exact Lagrange multiplier that enforce the constraints,\n but instead uses an iterative algorithm (called augmented Lagrangian method) to approximate the Lagrange multipliers.\n During the augmentation,\n FEBio updates the approximate Lagrange multipliers.\n If the updates to the multipliers are small,\n FEBio will terminate the time step,\n otherwise it will restart the time step with the updated multipliers.\n \n\\end_layout\n\n\\begin_layout Standard\nSee the figure below for an overview of the basic FEBio flow.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Graphics\n\tfilename Figures/FEBio flow.png\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nUnits in FEBio\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:units\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio does not assume a specific unit system.\n It is up to the user to enter numbers that are defined in consistent units.\n For example,\n when entering material parameters in SI units,\n the user must enter all loads,\n contact parameters,\n and other boundary conditions in SI units as well.\n The units of all the parameters are given when they are defined in this manual.\n We use a generic designation of units for all the parameters using the following symbols.\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"3\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\shape italic\nSymbol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\emph on\nName\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\shape italic\nSI unit\n\\series default\n\\shape default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nL\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLength\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmeter (m)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nM\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nkilogram (kg)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTime\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond (s)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTemperature\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKelvin (K)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nn\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAmount of substance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmole (mol)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nF\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nForce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNewton (kg\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\nm/s\n\\begin_inset Formula $^{2}$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nP\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPressure,\n stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPascal (Pa=N/m\n\\begin_inset Formula $^{2}$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nQ\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElectric charge\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCoulomb (C=A\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\ns)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUnits are given using the bracket notation.\n For instance,\n the unit for density is [\n\\series bold\nM/L\n\\series default\n\n\\begin_inset Formula $^{3}$\n\\end_inset\n\n] and the unit for permeability is [\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{4}$\n\\end_inset\n\n\n\\series bold\n/F\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt].\n\n\\series default\n When using SI units,\n this corresponds to units of kg/m\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n for density and m\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/N.s for permeability,\n respectively.\n Unitless parameters are designated by empty brackets (\n\\series bold\n[ ]\n\\series default\n).\n The units for angles are either [\n\\series bold\ndeg\n\\series default\n] for degrees or [\n\\series bold\nrad\n\\series default\n] for radians.\n\\end_layout\n\n\\begin_layout Standard\nWhen adopting a consistent set of units,\n first choose a primary set of units,\n and then determine the remaining derived units.\n For example,\n in typical problems in solid mechanics,\n the primary set consists of three units.\n If you choose [\n\\series bold\nM\n\\series default\n]=kg,\n [\n\\series bold\nL\n\\series default\n]=m,\n and [\n\\series bold\nt\n\\series default\n]=s,\n then [\n\\series bold\nF\n\\series default\n]=N and [\n\\series bold\nP\n\\series default\n]=Pa.\n Alternatively,\n if you choose [\n\\series bold\nL\n\\series default\n]=mm,\n [\n\\series bold\nF\n\\series default\n]=N and [\n\\series bold\nT\n\\series default\n]=s as the primary set,\n then [\n\\series bold\nP\n\\series default\n]=MPa (since 1 N/mm\n\\begin_inset Formula $^{\\mathrm{2}}=$\n\\end_inset\n\n 10\n\\begin_inset Formula $^{\\mathrm{6}}$\n\\end_inset\n\nN/m\n\\begin_inset Formula $^{\\mathrm{2}}=$\n\\end_inset\n\n 1 MPa) and [\n\\series bold\nM\n\\series default\n]=tonne (tonne = N \n\\begin_inset Formula $\\cdot$\n\\end_inset\n\ns\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/mm).\n The primary set of units must be independent.\n For instance,\n in the last example,\n you cannot choose [\n\\series bold\nP\n\\series default\n] as a primary unit as it can be expressed in terms of [\n\\series bold\nF\n\\series default\n] and [\n\\series bold\nL\n\\series default\n] (i.e.\n [\n\\series bold\nP\n\\series default\n]=[\n\\series bold\nF/L\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{\\mathbf{2}}}$\n\\end_inset\n\n]).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"16\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell multicolumn=\"1\" alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nPrimary Units\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlength\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nN\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\namount of substance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnmol\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncharge\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nC\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntemperature\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nK\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell multicolumn=\"1\" alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDerived Units\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nN/mm\n\\begin_inset Formula $^{2}$\n\\end_inset\n\n,\n MPa\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npermeability\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm\n\\begin_inset Formula $^{4}$\n\\end_inset\n\n/N\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\ns,\n mm\n\\begin_inset Formula $^{2}$\n\\end_inset\n\n/Pa\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\ns\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm\n\\begin_inset Formula $^{2}$\n\\end_inset\n\n/s\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconcentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnmol/mm\n\\begin_inset Formula $^{3}$\n\\end_inset\n\n,\n mM\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncharge density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnEq/mm\n\\begin_inset Formula $^{3}$\n\\end_inset\n\n,\n mEq/L\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvoltage\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmV\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA/mm\n\\begin_inset Formula $^{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nRunning FEBio\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:running-FEBio\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio is a command line application which means it does not have its own Graphical User Interface (GUI) and must be run from a shell or command line.\n FEBio runs on several different computing platforms including Windows,\n Mac OSX,\n and many versions of Linux.\n The command line input and output options are described in this chapter.\n\\end_layout\n\n\\begin_layout Section\nRunning FEBio on Windows\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:running-FEBio-Windows\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThere are several ways to run FEBio on Windows.\n The easiest way is by simply selecting the FEBio program from the Programs menu or by double-clicking the FEBio icon in the installation folder.\n However,\n this runs FEBio with the installation folder as the working folder,\n and unless the FEBio input files are in this folder,\n you will need to know the relative or absolute path to your input files.\n A more practical approach is to run FEBio from a command prompt.\n Before you can do this,\n you need to know two things:\n how to open a command prompt and how to add the FEBio installation folder to your PATH environment variable so that you can run FEBio from any folder on your system.\n\\begin_inset Foot\nstatus open\n\n\\begin_layout Plain Layout\nThe current FEBio installers should automatically setup the environment path so that FEBio can be run from anywhere on the file system.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nWindows\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:windows\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn order to run FEBio from a command prompt,\n the path to the FEBio executable must be set in the PATH environment variable.\n The environment variables,\n including the PATH variable,\n are specified in the Environment Variables dialog box.\n There are a few different ways to get to this dialog box.\n The easiest way is to write \n\\begin_inset Quotes eld\n\\end_inset\n\nenvironment\n\\begin_inset Quotes erd\n\\end_inset\n\n in the Windows search bar,\n and select the \n\\begin_inset Quotes eld\n\\end_inset\n\nEdit the system's environment variables\n\\begin_inset Quotes erd\n\\end_inset\n\n suggestion.\n This will open the \n\\begin_inset Quotes eld\n\\end_inset\n\nSystem Properties\n\\begin_inset Quotes erd\n\\end_inset\n\n dialog box.\n On the Advanced tab,\n click the Environment variables button.\n This will open the Environment Variables dialog box.\n Find the \n\\shape italic\npath\n\\shape default\n variable and click the \n\\shape italic\nEdit\n\\shape default\n button.\n On older versions of Windows a semicolon delimited text string appears.\n At the end of that string (don't delete the current value) type a semi-colon and then the absolute path to the FEBio installation folder (e.g.\n C:/Program Files/FEBio/bin/).\n On Windows 10 or newer,\n the values are displayed in a list.\n Either click New or double-click on the first empty slot in the list and type the path the febio executable.\n Then click the \n\\shape italic\nOK\n\\shape default\n-button on all open dialog boxes.\n\\end_layout\n\n\\begin_layout Standard\nTo open a command prompt,\n type \n\\emph on\ncmd\n\\emph default\n in the Windows search and press Enter.\n A command prompt window should appear.\n You can now use the \n\\shape italic\ncd \n\\shape default\n(change directory) command to navigate to the folder that contains the FEBio input files.\n To run FEBio,\n simply type \n\\shape italic\nfebio4 \n\\shape default\n(with or without additional arguments) and press \n\\shape italic\nEnter\n\\shape default\n.\n Note that if you already had a command prompt open when changing the PATH environment variable,\n you'll nee to close this window and reopen it after the changes to the environment variables are applied.\n  \n\\end_layout\n\n\\begin_layout Subsection\nRunning FEBio from Explorer\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:running-Explorer\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA third method of running FEBio,\n which often is very convenient,\n is to run FEBio from Windows Explorer.\n To do this,\n first open Explorer and browse to the folder that contains your FEBio input files.\n Next,\n right-click on the input file and select \n\\shape italic\nOpen With\n\\shape default\n.\n Now select \n\\shape italic\nChoose default program\n\\shape default\n.\n A dialog box appears with a list of programs to open the input file.\n If FEBio is not on this list yet,\n click the \n\\shape italic\nBrowse\n\\shape default\n button.\n Locate the FEBio executable (e.g.\n in C:/Program Files/FEBio2/bin),\n select it and press the \n\\shape italic\nOpen\n\\shape default\n button.\n Now select FEBio in the \n\\shape italic\nOpen With\n\\shape default\n dialog box and press Ok.\n\\end_layout\n\n\\begin_layout Standard\nAfter you have done this once,\n the process simplifies.\n After you right-click the input file,\n FEBio should now show up in the \n\\shape italic\nOpen With \n\\shape default\nmenu item and can be selected immediately without having to go through all the previous steps.\n\\end_layout\n\n\\begin_layout Section\nRunning FEBio on Linux or MAC\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Running-Linux-MAC\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nRunning FEBio on Linux or Mac is as easy as opening up a shell window and typing FEBio on the command line.\n However,\n you may need to define an alias to the folder that contains the FEBio executable if you want to run FEBio from any folder on your system.\n Since this depends on your shell,\n you need to consult your Linux documentation on how to do this.\n E.g.\n if you are using c-shell,\n you can define an alias as follows:\n\\end_layout\n\n\\begin_layout LyX-Code\nalias febio '/path/to/febio/executable/'\n\\end_layout\n\n\\begin_layout Standard\nIf you don't want to define this alias every time you open a shell window,\n you can place it in your shell start up file (e.g.\n \n\\shape italic\n.cshrc\n\\shape default\n for c-shell).\n\\end_layout\n\n\\begin_layout Section\nThe Command Line\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Command-Line\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio is started from a shell window (or the \n\\shape italic\ncommand prompt\n\\shape default\n in Windows).\n The command line is the same for all platforms:\n\\end_layout\n\n\\begin_layout LyX-Code\nfebio4 [-o1 [arg1] | -o2 [arg2] | ...\n ]\n\\end_layout\n\n\\begin_layout Standard\nWhere -o1,\n -o2 are options and \n\\shape italic\narg1\n\\shape default\n,\n \n\\shape italic\narg2\n\\shape default\n,\n ...\n are additional arguments.\n The different options (of which most are optional) are given by the following list:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"21\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncommand line option\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nargument\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-i\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the FEBio input file.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfeb file name\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-r\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the FEBio restart file.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrestart file or dump file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-g,-g1,-g2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDebug flags\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-p\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify name of the FEBio plot file.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot file name\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-o\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the name of the FEBio log file.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlog file name\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-s\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the optimization input file.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptimization input file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-d\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRun an FEBio diagnostic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiagnostic input file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-break\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet a breakpoint where FEBio will pause the run.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreakpoint\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-dump[=\n\\emph on\nn\n\\emph default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nActivate restart option and (optionally) set dump file name\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndump file name\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-dump_stride[=n]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the dump interval.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-config\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the FEBio configuration file to use.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconfiguration file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-noconfig\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDon't read configuration file.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-nosplash\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDon't print the splash window to the screen on startup.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-import\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nImport a plugin file.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplugin file name\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-silent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRun FEBio in silent mode,\n which suppresses screen output.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-task[=\n\\emph on\nname\n\\emph default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRun an FEBio task\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntask input file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-info\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint febio version information to screen or file\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(optional) output file name\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-noappend\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDo not append the log and plot files on restart.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-norun\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDo not run FEBio.\n (Only process command line.)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-output_negative_jacobians[=n]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the max nr of negative jacobians that will be printed.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nList of command line options.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA more detailed description of these options follows.\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-i\n\\series default\n The -i option is used to specify the name of the input file.\n The input file is expected to follow the format specifications as described in Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Free-Format-Input\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 \n\\series bold\n-i input.feb\n\\end_layout\n\n\\begin_layout Standard\nThis is the most common way to start an FEBio run.\n However,\n FEBio allows the omission of the -i when only a filename is given.\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 input.feb\n\\end_layout\n\n\\begin_layout Standard\nOn Windows,\n this allows for starting FEBio by double-clicking on an input file (assuming you have chosen FEBio as the default program to open .feb files).\n Note that if additional options are specified on the command line the -i must be present.\n\\end_layout\n\n\\begin_layout Standard\nIt is also allowed to omit the .feb extension for FEBio input files.\n If no extension is given,\n FEBio will assume that .feb is the file extension.\n Thus,\n the following command will run the file \n\\emph on\ninput.feb\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 input\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-r\n\\series default\n The -r option allows you to restart a previous analysis.\n The filename that must follow this option is an FEBio \n\\shape italic\nrestart input file \n\\shape default\nor a \n\\shape italic\ndump file\n\\shape default\n.\n The restart input file and dump file are described in more detail in \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Restarting-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The -i and -r options are mutually exclusive;\n only one of them may appear on the command line.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 \n\\series bold\n-r file.feb\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-g,-g1,-g2\n\\series default\n The –g options run FEBio in \n\\shape italic\ndebug mode\n\\shape default\n,\n in which case FEBio will print more information to the log and plot files.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Debugging-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on running FEBio in debug mode.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–g\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-p\n\\series default\n The –p option allows the user to specify the name of the \n\\shape italic\nplot file\n\\shape default\n.\n The plot file is a binary file that contains the main results of the analysis.\n FEBio usually provides a default name for this file;\n however,\n the user can override the default name using this option.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:FEBio-Output\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on the output files generated by FEBio.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–p out.xplt\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-o \n\\series default\nThe –o option allows the user to set the name of the \n\\shape italic\nlog file\n\\shape default\n.\n The log file will contain a record of the screen output that was generated during a run.\n FEBio usually provides a default name for this file (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:FEBio-Output\"\nnolink \"false\"\n\n\\end_inset\n\n),\n but the user can override it with this command line option.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–o out.log\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-s\n\\series default\n This option instructs FEBio to run a material parameter optimization on the specified input file.\n The optimization module is described in detail in Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Parameter-Optimization\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The –s option is followed by the optimization control file which contains among other things the parameters that need to be optimized.\n Note that the restart feature does not work with the optimization module.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 –i file.feb \n\\series bold\n–s control.opt\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-d\n\\series default\n This option will run a FEBio diagnostics.\n A diagnostic is a special type of test that can be used to verify an implementation.\n For example,\n the \n\\shape italic\ntangent diagnostic \n\\shape default\nallows users to check the consistency between the material's stress and tangent implementations.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 \n\\series bold\n–d diagnostic.feb\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-break\n\\series default\n With this option a break point can be set which sets a time point or an event at which FEBio will interrupt the run and show the FEBio prompt.\n The following example sets a break point at time 1.0.\n FEBio will interrupt the run after the time step at time 1.0 is reached (i.e.\n has converged).\n (See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Setting-break-points\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on setting break points.)\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 –i file.feb \n\\series bold\n–break 1.0\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-dump\n\\series default\n It is possible to restart a previous run using the restart capability in FEBio.\n This is useful when a run terminates unexpectedly.\n If that happens,\n the user can restart the analysis from the last converged timestep.\n Before this feature can be used,\n the user must request the creation of a \n\\shape italic\ndump file\n\\shape default\n.\n This file will store all the information that FEBio will need to restart the analysis.\n FEBio will usually provide a default name for the dump file,\n but the \n\\emph on\n–dump\n\\emph default\n command line option allows the user to override the default name for the dump file.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Restarting-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n and Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Restart-Input-file\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on how to use the restart feature.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–dump out.dmp\n\\end_layout\n\n\\begin_layout Standard\nTo control when FEBio will write the dumpfile,\n the -dump command can be appended by a number that defines the dump level.\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Restarting-a-Run\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more information.\n \n\\end_layout\n\n\\begin_layout Description\n-dump_stride Sets the interval between writing dump files.\n The default is 1 (although the dump level may affect this as well.) The following example will write a dump file after every 10 timesteps.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb –dump out.dmp \n\\series bold\n-dump_stride=10\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-config\n\\series default\n As of version 1.2,\n FEBio uses a \n\\shape italic\nconfiguration file\n\\shape default\n to store platform specific settings.\n Usually FEBio assumes that the location for this configuration file is the same as the executable.\n However,\n the user can specify a different location and filename using the –config command line option.\n If the user does not have a configuration file or does not wish to use one,\n this can be specified using the \n\\emph on\n–noconfig\n\\emph default\n option.\n More details on the configuration file can be found in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Configuration-file\"\nnolink \"false\"\n\n\\end_inset\n\n and Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Configuration-File\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–config C:\n\\begin_inset Formula $\\backslash$\n\\end_inset\n\npath\n\\begin_inset Formula $\\backslash$\n\\end_inset\n\nto\n\\begin_inset Formula $\\backslash$\n\\end_inset\n\nfebio.xml\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-noconfig\n\\series default\n Do not read and process the FEBio configuration file.\n FEBio will start with its default configuration.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-nosplash\n\\series default\n When the –nosplash command is entered on the command line,\n FEBio will not print the welcome message to the screen.\n This is useful when calling FEBio from another application and when the user wishes to suppress any screen output from FEBio.\n Other options for suppressing output can be set in the control section of the FEBio input file (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Control-Parameters\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–nosplash\n\\end_layout\n\n\\begin_layout Description\n-import This command will load the plugin that is specified following this command line option.\n Although it is more convenient to list plugins in the FEBio configuration file,\n this option allows users to load a plugin from the command line,\n if such a need would arise.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 \n\\series bold\n–import myplugin.dll\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-silent\n\\series default\n When the –silence option is specified on the command line,\n FEBio will not generate any output to the screen.\n Unless explicitly instructed not to,\n FEBio will still create a log file which will have the convergence information.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–silent\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-task\n\\series default\n FEBio will always run a \n\\emph on\ntask\n\\emph default\n.\n The default task is to solve a forward model defined by the input file specified using the -i command line option.\n However,\n other tasks are available,\n such as optimization and restart,\n as well as user-created tasks created in FEBio plugins.\n The name of the task is defined after an equal sign (=).\n An optional task control file can be specified as well.\n For instance,\n an alternative way for running an optimization problem is as follows.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–task=optimize control.opt\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\n-info \n\\series default\nPrints version information to the screen.\n \n\\end_layout\n\n\\begin_layout Description\n-noappend Do not append the log and plot file on restart.\n When restarting an analysis,\n FEBio will append the log and plot files with new data.\n However,\n when using this flag,\n new log and plot files will be generated for the run.\n \n\\end_layout\n\n\\begin_layout Description\n-norun Do not run FEBio.\n Only command line is processed.\n For instance,\n to get the version info from FEBio without running it,\n use the following command.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -info -norun\n\\end_layout\n\n\\begin_layout Description\n-output_negative_jacobians Sets whether FEBio will output any negative Jacobians.\n A negative Jacobian is often an indication of a problem during the solution process and knowing where these negative Jacobians occurred may be helpful for debugging.\n By default,\n FEBio will only report that negative Jacobians happened,\n but won't print any other information.\n The command can also be appended by a number that indicates the max number of Jacobians will be printed.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n> febio4 -i input.feb \n\\series bold\n–output_negative_jacobians=100\n\\end_layout\n\n\\begin_layout Section\nThe FEBio Prompt\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:FEBio-prompt\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe FEBio prompt is shown when you start FEBio without any command arguments (referred to as \n\\emph on\ninteractive mode\n\\emph default\n),\n or when a run is interrupted either by the user (using ctrl+c \n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\nThis feature does not work on some Linux platforms and may abruptly terminate the run.\n\\end_layout\n\n\\end_inset\n\n;\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Interrupting-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n for more details),\n or by reaching a breakpoint (referred to as \n\\emph on\nbreak mode\n\\emph default\n).\n The FEBio prompt will look something like this:\n\\end_layout\n\n\\begin_layout LyX-Code\nfebio>\n\\end_layout\n\n\\begin_layout Standard\nYou can now enter one of the following commands.\n Note that some commands are only available in a specific mode (either \n\\emph on\ninteractive \n\\emph default\nor \n\\emph on\nbreak\n\\emph default\n mode).\n Also see additional comments below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"26\" columns=\"5\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncommand line option\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nargument\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninteractive\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAdd a breakpoint,\n which stops FEBio at a particular point.(1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreakpoint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreaks\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint a list of breakpoints.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nclear\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nClears one or more breakpoints.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreakpoint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconfig\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLoad (or reload) configuration file.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconfiguration file\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncont\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContinue the current task\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconv\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nForce conversion of the current time step.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndebug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nToggle debug mode (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[on/off]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nevents\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint a list of events\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfail\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nForce the current time step to fail.\n (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhelp\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint a list of available commands\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nimport\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nImport a plugin file\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplugin file name\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nout\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOutput to file the current stiffness matrix and right-hand side\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[-txt]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nWrite the current model state to the plot file.\n (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplugins\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint a list of plugins.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint the value of an internal variable.\n (6)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvariable name\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nquit\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStop the current model or exit of no model is running\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrestart\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nToggle the restart flag.\n (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrun\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRun a febio input file.\n (8)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(command line options)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nset\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsets certain model and configuration variables.\n (9)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvariable name (space) value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsvg\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nWrite the sparse matrix profile to an svg file.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsvg file name\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint progress time statistics.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nunload\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUnload a plugin from FEBio.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplugin name\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nversion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint version information.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwhere\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrints the current callback event\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlist\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLists all the factory classes (10)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(command line options)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n✓\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nList of options for the FEBio command prompt.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nA breakpoint is a particular time point or an event at which FEBio will pause the run.\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Setting-break-points\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on break points.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nforce\n\\emph default\n command is useful,\n for example,\n when a time step is having difficulty satisfying the convergence criteria.\n The user can then manually force the convergence of the time step.\n However,\n if the convergence difficulties are due to instabilities,\n forcing a time step to converge could cause the solution to become unstable or even incorrect.\n Also be aware that even if the solution recovers on later timesteps,\n the manually converged step might be incorrect.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\ndebug \n\\emph default\ncommand toggles the debug flag.\n Adding \n\\shape italic\non \n\\shape default\n(\n\\shape italic\noff\n\\shape default\n) will turn the debug mode on (resp.\n off).\n In debug mode,\n FEBio will store additional information to the log and plot file that could be useful in debugging the run.\n It is important to note that since FEBio will store all non-converged states to the plot file,\n this file may become very large in a short number of time steps.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Debugging-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on debugging.\n\\end_layout\n\n\\begin_layout Enumerate\nIf the current time step is not converging and if the auto-time stepper is enabled,\n the fail command will stop the current time step and retry it with a smaller time step size.\n If the auto-time stepper is not enabled,\n the fail command will simply exit the application.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nplot \n\\emph default\ncommand is useful when you want to store the non-converged state at the current iteration.\n Note that this command only stores the state at the current iteration.\n If you turn on debug mode,\n all the iterations are stored to the plot file.\n\\end_layout\n\n\\begin_layout Enumerate\nThe following variables can be printed.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Description\nnnz Number of nonzeroes in global stiffness matrix.\n\\end_layout\n\n\\begin_layout Description\ntime The current simulation time\n\\end_layout\n\n\\begin_layout Description\nneq The number of equations \n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nWhen the restart flag is set,\n FEBio will create a dump file at the end of each converged time step.\n This dump file can then later be used to restart the analysis from the last converged time step.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Restarting-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n and Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Restart-Input-file\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on FEBio's restart feature.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nrun \n\\emph default\ncommand is used to start an FEBio task.\n This command takes the same options that you can enter on the command line.\n For example,\n to run a file named \n\\shape italic\ntest.feb \n\\shape default\nfrom the FEBio prompt,\n type the following:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\nrun –i test.feb\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe following configuration file settings can be set from the febio command prompt.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Description\noutput_negative_jacobians turn on or off the detailed output when negative jacobians are encountered.\n\\end_layout\n\n\\begin_layout Description\nprint_model_params turn on or off the output of the parameter values of parameters that are load controlled.\n \n\\end_layout\n\n\\begin_layout Description\nshow_warnings_and_errors turn on or off the printing of warning and error messages.\n \n\\end_layout\n\n\\begin_layout Standard\nTo turn the option on (off),\n follow the set command by the variable name and then a value of 1 (0).\n For example,\n \n\\end_layout\n\n\\begin_layout LyX-Code\nset output_negative_jacobians 1\n\\end_layout\n\n\\begin_layout Standard\nIf you enter the set command without any additional options,\n a list will be printed of the current configuration settings.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\emph on\nlist \n\\emph default\ncommand prints a list of all available FEBio features.\n Additional options can be specified to configure the output.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Description\n-m Specify the module name.\n (e.g.\n list -m solid)\n\\end_layout\n\n\\begin_layout Description\n-c Specify the category.\n (e.g.\n list -m FEMATERIAL_ID)\n\\end_layout\n\n\\begin_layout Description\n-n Set the max nr of lines in the output (e.g.\n list -n 100)\n\\end_layout\n\n\\begin_layout Description\n-s Specify a general pattern that should match.\n (e.g.\n list -s neo)\n\\end_layout\n\n\\end_deeper\n\\begin_layout Section\nThe Configuration File\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Configuration-file\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs of version 1.2,\n FEBio uses a \n\\shape italic\nconfiguration file \n\\shape default\nto store platform-specific settings,\n such as the default linear solver and the list of plugins that need to be loaded at startup.\n The configuration file uses an xml format to store data and is detailed in Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Configuration-File\"\nnolink \"false\"\n\n\\end_inset\n\n.\n For backward compatibility,\n it is still possible to run FEBio without the configuration file.\n In that case,\n the default settings prior to version 1.2 are used.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio –i myfile.feb -noconfig\n\\end_layout\n\n\\begin_layout Standard\nThe default configuration file needs to be stored in the same location as the executable and named \n\\shape italic\nfebio.xml\n\\shape default\n.\n Alternatively,\n the location and the name of the file can also be specified on the command line using the –config option.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n> febio -i myfile.feb –config /home/my/folder/FEBio/febio.xml\n\\end_layout\n\n\\begin_layout Section\nUsing Multiple Processors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Using-Multiple-Processors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs of version 2.0,\n FEBio uses OpenMP to parallelize several of the finite element calculations,\n improving the performance considerably.\n Both the right-hand-side and the stiffness matrix evaluations for many types of problems have been parallelized.\n On a system with four processors,\n a speedup of 2-3 can be expected,\n depending on the size and type of model.\n Models with complex material behavior (such as EFD-type materials,\n biphasic,\n multiphasic materials,\n etc.) will benefit most from these parallelization efforts.\n In addition,\n FEBio implements the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{http://software.intel.com/en-us/intel-mkl/}{\n\\backslash\nemph{MKL}}\n\\end_layout\n\n\\end_inset\n\n version of the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{http://www.pardiso-project.org/}{\n\\backslash\nemph{PARDISO}}\n\\end_layout\n\n\\end_inset\n\n linear solver,\n which is a parallel linear solver that uses OpenMP.\n\\end_layout\n\n\\begin_layout Standard\nTo use multiple processors,\n set the environment variable OMP_NUM_THREADS to the number of desired threads.\n You should set the number of threads to be equal or less than the number of processors on your system (Setting it higher may actually decrease performance).\n For example,\n on a system with four processors you can set the environment as follows.\n On Linux using the Bash shell,\n execute:\n\\end_layout\n\n\\begin_layout LyX-Code\n> export OMP_NUM_THREADS=4\n\\end_layout\n\n\\begin_layout Standard\nUsing the c-shell,\n execute:\n\\end_layout\n\n\\begin_layout LyX-Code\n> setenv OMP_NUM_THREADS 4\n\\end_layout\n\n\\begin_layout Standard\nOr at a Windows command prompt:\n\\end_layout\n\n\\begin_layout LyX-Code\n> set OMP_NUM_THREADS=4\n\\end_layout\n\n\\begin_layout Standard\nOn Windows,\n you can add this environment variable as well from the System Properties dialog box.\n\\end_layout\n\n\\begin_layout Paragraph\n\n\\series bold\nA note on repeatability\n\\end_layout\n\n\\begin_layout Standard\nIdeally,\n when the same model is run repeatedly,\n either on the same machine or on different machines,\n it should produce the same convergence statistics and results.\n However,\n in practice this is not always the case and sometimes the convergence stats and even the results can differ.\n In most cases,\n the discrepancies should be small,\n however in some cases,\n and especially in models that are prone to ill-conditioning (e.g.\n contact),\n the discrepancies may be more significant.\n The underlying reason is that in different compute environments it can not be guaranteed that all calculations are executed in the exact same order and,\n due to numerical round-off,\n the results of these calculations will not always be the same.\n On a single machine,\n this can happen when the model is run using multiple processors.\n (However,\n running the same model on a machine with a single processor should always produce the exact same results.) This behavior can also be observed when running the same model on different machines,\n especially if these machines have different OS.\n \n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n although FEBio aims to be backward compatible,\n running the same model with different versions of FEBio,\n may also show discrepancies for similar reasons.\n In this case,\n the discrepancies are likely caused by algorithmic optimizations or changes in compiler settings.\n \n\\end_layout\n\n\\begin_layout Section\nFEBio Output\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:FEBio-Output\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nScreen output\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n FEBio will print convergence and progress information to the screen that informs users how the analysis is progressing.\n The output depends on the particular module and solver that was chosen in the FEBio input file,\n as well as some user-defined settings,\n but in general provides the following information.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nTime stepping\n\\series default\n \n\\series bold\ninformation\n\\series default\n:\n The current time step that FEBio is solving and a notification when the time step completed (or an error message why the time step failed to complete)\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nConvergence information\n\\series default\n:\n for each time step,\n FEBio usually has to solve a nonlinear problem for which an iterative nonlinear solver is used.\n Several \n\\begin_inset Quotes eld\n\\end_inset\n\nnorms\n\\begin_inset Quotes erd\n\\end_inset\n\n are printed to inform the user how FEBio is progressing toward the solution of the nonlinear problem.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nSummary:\n \n\\series default\nAt the end of the analysis a summary is printed with heuristics that inform the user how well the model ran.\n \n\\end_layout\n\n\\begin_layout Standard\nBy default,\n all screen output will also be printed to the log file.\n \n\\end_layout\n\n\\begin_layout Standard\nThe summary printed at the end of a run also provides some useful timing information.\n Below follows an explanation of the different reported timings.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntiming\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInput time\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time it took to process the FEBio input file.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInitialization time\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to initialize and check the model\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSolve time\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe total time it took to solve the model.\n This is the sum of the following timings:\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIO-time\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to write the output files (i.e.\n the plot,\n dump,\n and data files)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreforming stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to reform the stiffness matrix (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nevaluating stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to evaluate the stiffness matrix (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nevaluating residual\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to evaluate the residual (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmodel update\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to update the model (4)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nQN updates\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time to evaluate the Quasi-Newton updates (5)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime in linear solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe time spent in the linear solver (6)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nTable listing descriptions of the different timings reported at the end of an FEBio job.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nInternally,\n FEBio uses a sparse-matrix storage format to store the global stiffness matrix efficiently.\n A matrix reformation refers to the process of calculating the structure of this sparse matrix.\n The structure depends mostly on the nodal connectivity and for many problems this only needs to be calculated once at the start of the run.\n However,\n for some problems the connectivity may change during an analysis and this may require a reformation of the global stiffness matrix.\n (For instance,\n changes in contacting facets.) \n\\end_layout\n\n\\begin_layout Enumerate\nThe global stiffness matrix needs to be evaluated often as it is needed by the Newton solver for finding the solution.\n The total time to evaluate this stiffness matrix may often be a significant portion of the total runtime,\n however,\n there are several parameters that affect this.\n For instance,\n the max_ups parameter sets the number of quasi-Newton updates.\n During these updates,\n the full stiffness is not re-evaluated.\n Instead,\n a rank-one update is performed,\n which often significantly reduces the time to evaluate the stiffness matrices.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe residual,\n which is the difference between \n\\begin_inset Quotes eld\n\\end_inset\n\ninternal\n\\begin_inset Quotes erd\n\\end_inset\n\n forces (i.e.\n stresses) and external forces,\n is evaluated during each quasi-Newton iteration.\n Although the residual is calculated many times during an analysis,\n its evaluation rarely takes up a significant time of the total run time.\n \n\\end_layout\n\n\\begin_layout Enumerate\nDuring the model update,\n all quantities that depend on the solution variables will be updated.\n This includes nodal positions,\n element stress,\n etc.\n This update is also called for each quasi-Newton iteration.\n Although the update is usually fast,\n the fact that it is required for every iteration may cause the total time spent in the model update to be significant.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe QN update refers to the process of calculating a rank-one update of the stiffness matrix.\n This is usually a fast process and does not take up a significant amount of time.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe time in the linear solver usually takes up most of the total runtime.\n Most of this time is spent calculating the factorization of the global stiffness matrix.\n \n\\end_layout\n\n\\begin_layout Subsection\nOutput files\n\\end_layout\n\n\\begin_layout Standard\nAfter running FEBio,\n two or three files are created:\n the \n\\shape italic\nlog file\n\\shape default\n,\n the \n\\shape italic\nplot file,\n\n\\shape default\n and optionally the \n\\shape italic\ndump file\n\\shape default\n.\n The log file is a text file that contains the same output (and usually more) that was written to the screen.\n The \n\\shape italic\nplot file\n\\shape default\n contains the results of the analysis.\n Since this is a binary file,\n the results must be analyzed using post processing software such as \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/febio-studio/}{\n\\backslash\nemph{FEBio Studio}}\n\\end_layout\n\n\\end_inset\n\n.\n In some cases,\n the user may wish to request the creation of a \n\\shape italic\ndump file\n\\shape default\n.\n This file contains temporary results of the run.\n If an analysis terminates unexpectedly or with an error,\n this file can be used to restart the analysis from the last converged time step.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Restarting-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n and Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Restart-Input-file\"\nnolink \"false\"\n\n\\end_inset\n\n for more details.\n The names of these files can be specified with the command options –p (plot file),\n -a (dump file),\n -o (log file).\n If one or more of the file names following these flags are omitted,\n then the omitted file name(s) will be given a default name.\n The default file names are derived from the input file name.\n For example,\n if the input file name is \n\\shape italic\ninput.feb \n\\shape default\nthe logfile will have the name \n\\shape italic\ninput.log\n\\shape default\n,\n the plot file is called \n\\shape italic\ninput.xplt\n\\shape default\n and the dump file is called \n\\shape italic\ninput.dmp\n\\shape default\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nNote 1.\n\n\\emph default\n The name of the log and plot file can also be specified in the FEBio input file.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:LoadData-Section\"\nnolink \"false\"\n\n\\end_inset\n\n for more information.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nNote 2.\n\n\\emph default\n When running an optimization problem the name of the log file is derived from the optimization control file.\n See Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Parameter-Optimization\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on running optimization problems with FEBio.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nNote 3\n\\emph default\n.\n FEBio may also generate additional \n\\emph on\ndata files\n\\emph default\n.\n Although the log file is used as the default output file for data requested in the \n\\emph on\nlogfile \n\\emph default\nsection of the input file,\n users can specify the location of the output file.\n In that case,\n the data will be written to the output file instead of the logfile.\n\\end_layout\n\n\\begin_layout Section\nAdvanced Options\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Advanced-Options\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nInterrupting a Run\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Interrupting-a-Run\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe user can pause the run by pressing ctrl+c.\n This will bring up the FEBio prompt,\n and the user can enter a command.\n The FEBio prompt will also be shown when FEBio reaches a break point.\n See Section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:FEBio-prompt\"\nnolink \"false\"\n\n\\end_inset\n\n for a list of available commands.\n Note that it may take a while before the FEBio prompt is displayed after the user requests a ctrl+c interruption.\n This may be because the program is in the middle of a call to the linear solver or another time-consuming part of the analysis procedure that cannot be interrupted.\n NOTE:\n This feature may not work properly on all systems,\n although it should always work on Windows systems.\n\\end_layout\n\n\\begin_layout Subsection\nDebugging a Run\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Debugging-a-Run\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs stated in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Command-Line\"\nnolink \"false\"\n\n\\end_inset\n\n,\n FEBio can be run in debug-mode by specifying one of the –g options on the command line.\n When running in debug mode,\n FEBio performs additional checks and prints out more information to the screen and to the plot file.\n It will also store all non-converged states to the plot file.\n These non-converged states can be very useful for determining the cause of non-convergence or slow convergence.\n Because of this additional work,\n the problem may run slightly slower.\n Note that debug mode can be turned on/off while running an analysis by first interrupting the run with ctrl+c and then using the \n\\shape italic\ndebug \n\\shape default\ncommand to toggle the debug mode on or off.\n It is important to note that since FEBio will store all non-converged states to the plot file,\n this file may become very large in a short number of time steps.\n An alternative approach is to use the \n\\shape italic\nplot\n\\shape default\n command to write out select non-converged states.\n\\end_layout\n\n\\begin_layout Subsection\nSetting break points\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Setting-break-points\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAlthough ctrl+c can be used to interrupt a run,\n it might sometimes be difficult to interrupt a run at a desired point.\n For this reason,\n you can set break points that will pause the execution and bring up the FEBio prompt.\n Breakpoints are set on the command line using the \n\\series bold\n-break\n\\series default\n option,\n followed by the break condition.\n In addition,\n breakpoints can also be set via the FEBio prompt using the \n\\series bold\nbreak \n\\series default\noption.\n \n\\end_layout\n\n\\begin_layout Standard\nTo break at a particular time point,\n simply add the value of the time after the \n\\emph on\n-break\n\\emph default\n option.\n This example will pause the run after time 0.45 completed (or the first time point past 0.45):\n \n\\end_layout\n\n\\begin_layout LyX-Code\n>febio -i input.feb -break 0.45\n\\end_layout\n\n\\begin_layout Standard\nInstead of pausing at a particular time,\n you can also interrupt the run when a certain event happens.\n To break at an event,\n specify the event name at which to pause the run.\n The following list of events are defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"12\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nEvent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nALWAYS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak on any event\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nINIT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after model initialization\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTEP_ACTIVE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after step activation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAJOR_ITERS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after major iteration (i.e.\n time step) converged\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMINOR_ITERS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after minor iteration (i.e.\n Newton iteration)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSOLVED\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after the model is solved\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUPDATE_TIME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak before time is incremented\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAUGMENT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak before augmentation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTEP_SOLVED\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after step is solved\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nREFORM\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak after global matrix and right hand side is reformed\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMATRIX_SOLVE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbreak right before the linear system solve\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to pause the execution after a matrix reformation (and right-hand-side evaluation),\n enter the following command line.\n\\end_layout\n\n\\begin_layout LyX-Code\n>febio -i input.feb -break REFORM\n\\end_layout\n\n\\begin_layout Standard\nOnce the code reaches a breakpoint,\n the febio prompt will be presented.\n There are several commands that relate to breakpoints.\n \n\\end_layout\n\n\\begin_layout Description\nbreak Add a breakpoint\n\\end_layout\n\n\\begin_layout Description\nbreaks Prints list of current breakpoints.\n\\end_layout\n\n\\begin_layout Description\nclear Clear one or all breakpoints.\n\\end_layout\n\n\\begin_layout Subsection\nRestarting a Run\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Restarting-a-Run\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the creation of a restart dump file is requested,\n the analysis can be restarted from the last converged timestep.\n This is useful when the run terminated unexpectedly or when the user wishes to modify some parameters during the analysis.\n To request the creation of a dump file,\n simply add the\n\\emph on\n -dump\n\\emph default\n flag on the command line.\n This will generate a \n\\shape italic\n\\emph on\ndump\n\\emph default\n \n\\shape default\nfile which then can be used to restart the analysis.\n You can specify an optional dump level,\n which sets how often the dump file is created,\n and an optional dump file name.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n> febio -i input.feb -dump\n\\end_layout\n\n\\begin_layout Standard\nWith the optional arguments,\n the complete syntax would be:\n\\end_layout\n\n\\begin_layout LyX-Code\n>febio -i input.feb -dump=1 out.dmp\n\\end_layout\n\n\\begin_layout Standard\nThe dump level can be set to the following values:\n\\end_layout\n\n\\begin_layout Enumerate\nwrite dump file after each converged time step\n\\end_layout\n\n\\begin_layout Enumerate\nwrite dump file after each completed analysis step\n\\end_layout\n\n\\begin_layout Enumerate\nwrite dump file after each converged\n\\shape italic\n must-point\n\\end_layout\n\n\\begin_layout Standard\nTo restart an analysis,\n use the \n\\series bold\n-r\n\\series default\n command line option.\n This option requires a filename as a parameter,\n and this name can be either the name of a dump file or the name of a restart input file.\n The latter case is a text file that allows the user to redefine some parameters when restarting the run.\n The format of this file is described in Chapter \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Restart-Input-file\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nFEBio File Conventions\n\\end_layout\n\n\\begin_layout Standard\nAlthough users are free to choose file names and file extensions,\n it might be beneficial to adhere to the following file naming conventions.\n \n\\end_layout\n\n\\begin_layout Description\nfeb FEBio input file.\n \n\\end_layout\n\n\\begin_layout Description\nxplt FEBio plot file.\n\\end_layout\n\n\\begin_layout Description\nlog FEBio log file.\n \n\\end_layout\n\n\\begin_layout Description\ndmp FEBio dump file (for restarts).\n \n\\end_layout\n\n\\begin_layout Description\nopt FEBio optimization control file.\n \n\\end_layout\n\n\\begin_layout Description\nrst FEBio restart input file.\n \n\\end_layout\n\n\\begin_layout Section\nFEBio Tasks\n\\end_layout\n\n\\begin_layout Standard\nAt its core,\n FEBio always executes a \n\\emph on\ntask\n\\emph default\n.\n You can think of a task as the outer loop that instructs FEBio what to do.\n The default task will run the FEBio input file provided on the command line and calculate the solution.\n Other tasks perform a restart,\n solve an optimization problem,\n or run a diagnostic.\n Users can also create custom tasks via plugins and direct FEBio what exactly it should do.\n Many of these tasks perform tests on specific FEBio features and thus can be helpful for plugin developers to ensure that their plugin works correctly with these specific FEBio features.\n \n\\end_layout\n\n\\begin_layout Standard\nThe task to execute can be specified on the command line using the \n\\series bold\n-task\n\\series default\n command line option.\n The name of the task is specified as follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n>febio4 -i input.feb \n\\series bold\n-task=task_name [task_input_file]\n\\end_layout\n\n\\begin_layout Standard\nThe name of the task follows after an equal sign ('=') (Notice that there are no spaces before or after the equal sign).\n Some tasks require an additional input file,\n which can be specified directly following the -task command line option.\n (There should be a space between the -task command line option and the optional task input file.)\n\\end_layout\n\n\\begin_layout Standard\nIn this section we'll document the tasks that are currently supported by FEBio.\n The following table lists the available tasks.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"14\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntask\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndiagnose\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRun a diagnostic.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\njfnk tangent test\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a tangent test on the JFNK solver\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\noptimize\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns an optimization problem.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nparam_run\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a parameter study\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nparameter_sweep\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a parameter study\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nquick_restart_test\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a restart test\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrci_solve\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a model using the RCI solver\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nreset_test\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a test on the FEBio reset functionality\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrestart\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a cold restart problem.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrestart_test\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns a test on the restart feature.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nsolve\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns the forward model (default task)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nstiffness_test\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAllows users to export the stiffness matrix from FEBio.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ntest\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRuns the model as a test suite problem.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nTable of available FEBio tasks.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSome more details about these tasks follow.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ndiagnose:\n \n\\series default\nThe \n\\emph on\ndiagnose \n\\emph default\ntask runs a diagnostic.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\njfnk_tangent_test:\n \n\\series default\nThe \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\njfnk tangent test\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n runs a test on the tangent produced by the JFNK linear solver.\n The JFNK linear solver is an iterative solver that aims to solve the nonlinear Newton problem without ever evaluating the stiffness matrix explicitly.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\noptimize:\n\n\\series default\n Runs an optimization problem.\n (See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Parameter-Optimization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n or more details on how to run optimization problems in FEBio.)\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nparam_run:\n\n\\series default\n The \n\\emph on\nparam_run\n\\emph default\n task runs a parameter study on the input model.\n In other words,\n it runs the input file multiple times,\n each time using a different value for certain parameters.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nparameter_sweep\n\\series default\n:\n This tasks runs a parameter study on the input file.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nquick_restart_test:\n \n\\series default\nThe \n\\emph on\nquick_restart_test \n\\emph default\nruns a test on the restart feature after model initialization.\n This task initializes the model and then writes a dump file.\n After that,\n the model is cleared and the dump file is read to re-initialize the model.\n Any issues that occur during re-initialization will be reported.\n If no problems are encountered,\n the task finishes.\n (Note that the model is not solved.) \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nrci_solve:\n\n\\series default\n The \n\\emph on\nrci_solve \n\\emph default\ntask solves a model using the RCI solver.\n The RCI (Reverse Communication Interface) solver is a feature that allows users more control over how FEBio solves the model.\n This feature was added to give plugins that interface with third party solvers the ability to transfer some of the solution progress decisions (e.g.\n when to advance the time step) to the third party library.\n This tasks tests that interface and ensures that it produces the same result as the standard solver.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nreset_test:\n \n\\series default\nFEBio's reset feature restores the original state of the model.\n This feature is used in several other tasks that need to solve the forward model repeatedly (e.g.\n optimization,\n parameter study).\n This task can be used to test the reset feature and ensure that it indeed properly resets the model.\n The test will run the forward model twice.\n After the first run,\n some stats are collected.\n The model is then reset and run again.\n The same stats are collected after the second run and compared with those from the first run.\n If the model reset was successful,\n the stats should be identical.\n If not,\n there is a problem with model reset.\n This task also produces a separate log file for each run that can be compared.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nrestart:\n \n\\series default\nRuns a cold restart of a previously run model.\n See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Restart-Input-file\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on how to run restart problems.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nrestart_test:\n \n\\series default\nThis task tests the restart feature to ensure that it indeed correctly restarts a model.\n After each converged time step,\n the model's state is serialized (in memory) to a dump stream and then immediately read back from the dump stream.\n If the restart feature is working properly,\n the model should produce the exact same output to both log and plot file as the forward model (i.e.\n the model run with that default task).\n Users can compare outputs to see if there might be any problem with the restart feature.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nsolve:\n \n\\series default\nThis solves the forward model provided by the FEBio input file.\n This is the default task when the -task command line option is not provided.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nstiffness_test:\n \n\\series default\nThe \n\\emph on\nstiffness_test \n\\emph default\ntask allows users to export the stiffness matrix and right-hand-side vector out of FEBio.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ntest:\n \n\\series default\nThis task is used by the FEBio test suite and adds the solution norm as a log variable that can be queried once the model completes.\n (This is used in the test suite to see if the solution norm has changed compared to a golden standard.)\n\\end_layout\n\n\\begin_layout Chapter\nFree Format Input\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Free-Format-Input\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis chapter describes the XML-based input format used by FEBio.\n Since this format follows standard XML conventions,\n the files can be viewed with any file viewer that supports XML files.\n Since the free format input file is a text file,\n it can be edited with any text editor.\n\\end_layout\n\n\\begin_layout Standard\nAn XML file is composed of a hierarchical list of \n\\shape italic\nelements\n\\shape default\n.\n The first element is called the \n\\shape italic\nroot element\n\\shape default\n.\n Elements can have multiple \n\\shape italic\nchild elements\n\\shape default\n.\n All elements are enclosed by two \n\\shape italic\ntags:\n\n\\shape default\n a tag defining the element and an \n\\shape italic\nend tag\n\\shape default\n.\n A simple example of an XML file might look like this:\n\\end_layout\n\n\\begin_layout LyX-Code\n<root>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <child>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <subchild> ...\n </subchild>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </child>\n\\end_layout\n\n\\begin_layout LyX-Code\n</root>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nvalue\n\\shape default\n of an element is enclosed between the name and the end tag.\n\\end_layout\n\n\\begin_layout LyX-Code\n<element> here is the value </element>\n\\end_layout\n\n\\begin_layout Standard\nNote that the XML format is case-sensitive.\n\\end_layout\n\n\\begin_layout Standard\nXML elements can also have \n\\shape italic\nattributes\n\\shape default\n in name/value pairs.\n The attribute value must always be quoted using quotation marks (\") or apostrophes (')\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\n Support for apostrophes was not added until FEBio version 2.1.\n\\end_layout\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout LyX-Code\n<element attr=\"value\">...</element>\n\\end_layout\n\n\\begin_layout LyX-Code\n<element attr='value'>...</element>\n\\end_layout\n\n\\begin_layout Standard\nIf an XML element has no value,\n an abbreviated syntax can be used.\n The following two lines are identical.\n\\end_layout\n\n\\begin_layout LyX-Code\n<element [attribute list]></element>\n\\end_layout\n\n\\begin_layout Standard\nor\n\\end_layout\n\n\\begin_layout LyX-Code\n<element [attribute list]/>\n\\end_layout\n\n\\begin_layout Standard\nComments can be added as follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n<!-- This is a comment -->\n\\end_layout\n\n\\begin_layout Standard\nThe first line in the document – the XML declaration – defines the XML version and the character encoding used in the document.\n An example can be:\n\\end_layout\n\n\\begin_layout LyX-Code\n<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\\end_layout\n\n\\begin_layout Section\nFree Format Overview\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Free-Format-Overview\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe free format organizes the FEBio input data into hierarchical XML elements.\n The root element is called \n\\shape italic\nfebio_spec\n\\shape default\n.\n This root element also defines the format version number (Note that FEBio and the input format specification follow different version numberings).\n This document describes version 4.0 of the FEBio specification\n\\begin_inset Foot\nstatus open\n\n\\begin_layout Plain Layout\nFEBio continues to read some older formats,\n but they are considered to be obsolete.\n\\end_layout\n\n\\end_inset\n\n (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Format-Specification-Versions\"\nnolink \"false\"\n\n\\end_inset\n\n below for more details on the different input specification formats).\n The root element will therefore be defined as follows:\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_spec version=\"4.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n<!-- contents of file -->\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_spec>\n\\end_layout\n\n\\begin_layout Standard\nThe different sections introduced in this chapter are child elements of this root element.\n The following sections are currently defined:\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nModule\n\\shape default\n\\emph default\n defines the physics module for solving the model.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nGlobals\n\\shape default\n\\emph default\n Defines the global variables in the model\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nControl\n\\shape default\n\\emph default\n specifies control and solver parameters.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nMaterial\n\\shape default\n\\emph default\n Specifies the materials used in the problem and the material parameters.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nMesh\n\\shape default\n\\emph default\n Defines the mesh of the problem,\n including nodal coordinates and element connectivity.\n \n\\end_layout\n\n\\begin_layout Description\nMeshDomains Assigns materials and other formulation attributes to element sets.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nMeshData\n\\shape default\n\\emph default\n Defines element,\n facet,\n edge or nodal data that can be mapped to material parameters or certain boundary conditions and loads.\n \n\\end_layout\n\n\\begin_layout Description\nMeshAdaptor Defines mesh adaptors that modify the mesh.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nInitial\n\\shape default\n\\emph default\n Defines initial conditions for dynamic problems,\n such as initial velocities,\n and for transient quasi-static problems.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nBoundary\n\\shape default\n\\emph default\n Defines the boundary conditions that are applied on the geometry.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nLoads\n\\shape default\n\\emph default\n Defines the loads applied to the model.\n This includes nodal loads,\n boundary loads and volume loads (or sources for heat transfer problems).\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nContact\n\\shape default\n\\emph default\n This section defines all contact interfaces.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nConstraints\n\\shape default\n\\emph default\n This section defines rigid and nonlinear constraints.\n \n\\end_layout\n\n\\begin_layout Description\nRigid This sections defines rigid components,\n such as rigid constraints,\n rigid loads,\n and rigid connectors.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nDiscrete\n\\shape default\n\\emph default\n This section defines all the discrete elements (i.e.\n springs).\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nLoadData\n\\shape default\n\\emph default\n Defines the load controllers,\n which can control model parameters as a function of time.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nStep\n\\shape default\n\\emph default\n Defines different analysis steps,\n where in each analysis the boundary,\n loads,\n contact and initial conditions can be redefined.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nOutput\n\\shape default\n\\emph default\n Defines additional data that needs to be stored to file.\n \n\\end_layout\n\n\\begin_layout Standard\nAlthough there is some flexibility in the order in which these sections can be listed,\n it is recommended to list them in the same order as given above.\n Not all sections are required.\n Empty sections can be omitted.\n A minimal file must contain at least the Module,\n Control,\n Material,\n Mesh,\n and MeshDomains sections.\n The rest of this chapter describes each of these sections in more detail.\n\\end_layout\n\n\\begin_layout Subsection\nFormat Specification Versions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Format-Specification-Versions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis document describes version 4.0 of the FEBio input specification.\n This format differs in several aspects from the previous versions of the input specification.\n This section describes the major changes between the different versions.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 4.0\n\\series default\n:\n The latest and recommended version of the FEBio input specification described in this document.\n The main motivation for this format was to improve consistency between the feb file structure and the way the FEBio model is constructed in FEBio Studio.\n This format is very similar to version 3.0 and only differs in a few sections:\n Some solver parameters have moved.\n The Rigid section was restructured.\n Node and element sets can be specified more concisely.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 3.0\n\\series default\n:\n The major focus of this revision was adding support for FEBio 3.0's features for modeling heterogeneous parameters.\n Heterogeneous parameters can be created via mathematical expressions or via the MeshData section.\n It also introduces a more consistent way for referencing model parameters.\n The \n\\emph on\nGeometry \n\\emph default\nsection was replaced with the \n\\emph on\nMesh \n\\emph default\nand \n\\emph on\nMeshDomains \n\\emph default\nsections in order to further separate the definition of the mesh and its physical attributes (e.g.\n material assignments).\n This format is \n\\series bold\nstill supported\n\\series default\n but considered obsolete.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 2.5\n\\series default\n:\n This format differs from its predecessor in some important aspects:\n all nodesets,\n surfaces,\n etc.,\n that are used by boundary conditions,\n loads,\n contact,\n etc.,\n must be defined in the Geometry section.\n Boundary conditions,\n loads,\n contact,\n etc.,\n are now defined by referencing the sets in the Geometry section.\n This format also adds the \n\\shape italic\nMeshData \n\\shape default\nsection and re-formats the \n\\shape italic\nDiscrete \n\\shape default\nsection.\n Rigid node sets and prescribed rigid degrees of freedom are moved to the \n\\shape italic\nBoundary\n\\shape default\n section.\n This format is \n\\series bold\nstill supported\n\\series default\n but considered obsolete.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 2.0\n\\series default\n:\n This is the first major revision of the input file format and redefines many of the file sections:\n The \n\\shape italic\nElements \n\\shape default\nsection uses a different organization.\n Elements are now grouped by material and element type.\n Multiple \n\\shape italic\nElements \n\\shape default\nsections can now be defined to create multiple parts.\n Surfaces can now be defined in the \n\\shape italic\nGeometry \n\\shape default\nsection and referenced by boundary conditions and contact definitions.\n A new \n\\shape italic\nContact\n\\shape default\n section contains all the contact definitions.\n A new \n\\shape italic\nDiscrete \n\\shape default\nsection was defined that contains all the materials and definitions of the discrete elements (e.g.\n springs).\n The \n\\shape italic\nBoundary \n\\shape default\nsection is also redesigned.\n This format is \n\\series bold\nstill supported \n\\series default\nbut considered obsolete.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 1.3\n\\series default\n:\n This was an experimental version that redefined the \n\\shape italic\nGeometry \n\\shape default\nsection,\n but was later abandoned in favor of version 2.0.\n This version is \n\\series bold\nno longer supported\n\\series default\n.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 1.2\n\\series default\n:\n A \n\\shape italic\nLoads \n\\shape default\nsection was added and all surface and body loads are now defined in this section instead of the \n\\shape italic\nBoundary \n\\shape default\nsection.\n This version is \n\\series bold\nmostly supported\n\\series default\n but considered obsolete.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 1\n\\series default\n.\n\\series bold\n1\n\\series default\n:\n Rigid body constraints are no longer defined in the rigid material definition but instead placed in a new \n\\shape italic\nConstraints \n\\shape default\nsection.\n This version is \n\\series bold\nno longer supported\n\\series default\n.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nVersion 1.0\n\\series default\n:\n The original input format specification.\n This version is \n\\series bold\nno longer supported\n\\series default\n.\n \n\\end_layout\n\n\\begin_layout Standard\nAs of FEBio 4.0,\n only versions 1.2,\n 2.0,\n 2.5,\n and 3.0 are supported.\n Versions 1.2 and 2.0 are considered obsolete and it is highly recommended to convert older files to the newest specification for use with newer versions of FEBio.\n This can be done for instance using FEBioStudio.\n\\end_layout\n\n\\begin_layout Subsection\n\n\\series bold\nNotes on backward compatibility\n\\end_layout\n\n\\begin_layout Standard\nSome features that were available in versions prior to 4.0 are no longer supported or require a different syntax.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nParameters \n\\emph default\nsection is no longer supported.\n This section allowed users to define file parameters that could be used as parameter values.\n This section was removed since it was difficult to support in FEBio Studio and a better mechanism was implemented for defining model-level parameters.\n (See the \n\\emph on\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:User-Variables\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n \n\\emph default\nsection.) \n\\end_layout\n\n\\begin_layout Enumerate\nSome fiber materials defined the \n\\emph on\ntheta\n\\emph default\n and \n\\emph on\nphi\n\\emph default\n parameters for setting the fiber orientation.\n These parameters are no longer supported.\n Instead,\n the fiber direction should be specified via the \n\\emph on\nfiber\n\\emph default\n property,\n or if the \n\\emph on\nfiber \n\\emph default\nproperty is not available,\n the \n\\emph on\nmat_axis \n\\emph default\nproperty.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<fiber type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <theta>90</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi>0</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fiber>\n\\end_layout\n\n\\end_deeper\n\\begin_layout Subsection\nMultiple Input Files\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Multiple-Input-Files\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio supports distributing the model definition across multiple input files.\n This can greatly facilitate defining large,\n complex models and allows the re-use of model input files without the need to create the entire model input file from scratch.\n When using multiple input files to define a model,\n you must create a control input file that may reference other input files.\n This control file will be used to run the model in FEBio.\n There are two ways of including a file into the control file:\n The \n\\emph on\nInclude \n\\emph default\nsection,\n and the \n\\emph on\nfrom \n\\emph default\nattribute\n\\emph on\n.\n\\end_layout\n\n\\begin_layout Standard\nNote that all input files must start with the \n\\emph on\nfebio_spec\n\\emph default\n tag and use the same format specification.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nInclude Keyword\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Include-Keyword\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nInclude \n\\shape default\nkeyword\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\nSupported from FEBio version 2.3 and up.\n\\end_layout\n\n\\end_inset\n\n can be used to include the contents of another FEBio input file.\n The filename is entered as the value of the tag.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Include>example.feb</Include>\n\\end_layout\n\n\\begin_layout Standard\nThe included file must be a valid FEBio file in that it must begin with the \n\\shape italic\nfebio_spec \n\\shape default\ntag and contain sections defined in this document.\n However,\n the included file does not need to define a complete model definition.\n For instance,\n it can contain only the \n\\shape italic\nMesh\n\\shape default\n section.\n\\end_layout\n\n\\begin_layout Standard\nNote that the contents of the entire file will be included.\n This is different from the \n\\shape italic\nfrom \n\\shape default\nattribute discussed below,\n which can be used to include only certain sections from files.\n\\end_layout\n\n\\begin_layout Subsubsection\nThe 'from' Attribute\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:The-'from'-Attribute\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nfrom \n\\shape default\nattribute can be used to include sections from other files.\n All the main sections defined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Free-Format-Overview\"\nnolink \"false\"\n\n\\end_inset\n\n support the \n\\shape italic\nfrom \n\\shape default\nattribute which can be used to load the section from another input file.\n For example,\n to load the \n\\shape italic\nMaterial\n\\shape default\n section from the file mat.feb,\n defining the \n\\shape italic\nMaterial\n\\shape default\n section in the control input file as follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Material from=\"mat.feb\"/>\n\\end_layout\n\n\\begin_layout Standard\nFEBio will now read the \n\\shape italic\nMaterial\n\\shape default\n section from this child file.\n The child file must be a valid FEBio input file,\n meaning it must begin with the \n\\shape italic\nfebio_spec\n\\shape default\n root section,\n but does not have to be complete.\n For example,\n the file mat.feb only needs to define the Material section.\n However,\n the child file may contain other sections.\n In that case,\n only the section referenced in the control file will be read from the child file.\n For example,\n if the file \n\\shape italic\nin.feb \n\\shape default\ncontains both the \n\\shape italic\nMaterial\n\\shape default\n and the \n\\shape italic\nMesh\n\\shape default\n section,\n the control file can read both these sections as follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Material from=\"in.feb\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Mesh from=\"in.feb\"/>\n\\end_layout\n\n\\begin_layout Standard\nTo give a more concrete example,\n assume that the \n\\shape italic\nMaterial\n\\shape default\n,\n \n\\shape italic\nMesh\n\\shape default\n,\n and \n\\shape italic\nBoundary\n\\shape default\n sections are defined in the files \n\\shape italic\nmat.feb\n\\shape default\n,\n \n\\shape italic\ngeom.feb\n\\shape default\n,\n and \n\\shape italic\nbc.feb\n\\shape default\n respectively.\n The control input file could then look like the following.\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_spec version=\"4.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Module type=\"solid\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <time_steps>10</time_steps>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <step_size>0.1</step_size>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Material from=\"mat.feb\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Mesh from=\"geom.feb\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Boundary from=\"bc.feb\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_spec>\n\\end_layout\n\n\\begin_layout Standard\nNotice that the \n\\shape italic\nControl \n\\shape default\nsection is still defined in the control file.\n The control file can contain a combination of explicit section definitions and referenced sections using the \n\\shape italic\nfrom \n\\shape default\nattribute.\n As mentioned above,\n the control file is used to run the model in FEBio.\n So,\n if the control file is called \n\\shape italic\nmodel.feb\n\\shape default\n then the model is run as follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n>febio –i model.feb\n\\end_layout\n\n\\begin_layout Standard\nWhen FEBio parses the control file it will automatically parse the referenced child files it encounters in the control input file.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nModule Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Module-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe module section defines the type of analysis to perform with FEBio.\n This section must be defined as the first section in the input file.\n It takes on the following format:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Module type=\"[type]\"/>\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\shape italic\ntype\n\\shape default\n can be any of the following values:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStructural mechanics analysis:\n quasi-static or dynamic,\n implicit and explicit solver available \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBiphasic analysis:\n steady-state or transient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBiphasic analysis including solute transport:\n steady-state or transient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmultiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMultiphasic analysis including solute transport and chemical reactions\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid mechanics analysis:\n steady-state or dynamic\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid-FSI\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid mechanics with fluid-structure interactions:\n steady-state or dynamic\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid-solutes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA fluid mechanics analysis with support for solute transport.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmultiphasic-FSI\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe FSI module with support for multiphasic materials.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe module tag can also be used to specify the units of the model.\n Although FEBio doesn't care about units,\n if the user specifies the unit system,\n the proper units of variables are stored in the plot file and visible in FEBio Studio when opening the plot file.\n \n\\end_layout\n\n\\begin_layout Standard\nTo specify units,\n use the following syntax:\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Module type=\"[type]\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <units>[unit system]</units>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Module>\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\emph on\nunit system\n\\emph default\n can be any of the following values.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nunits\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSI\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUnits follow the standard definition of the international SI unit system.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm-N-s\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMillimeter-Newton-seconds unit system\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm-kg-s\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMillimeter-kilogram-seconds unit system\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\num-nN-s\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMicron-nanoNewton-seconds unit system\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCGS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCentimeter-gram-seconds unit system\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf the units are not defined,\n FEBio will not output any units for plot variables.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_spec version=\"4.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  \n\\series bold\n<Module type=\"solid\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <!-- rest of file -->\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_spec>\n\\end_layout\n\n\\begin_layout Standard\nThe following example defines the model's units explicitly.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_spec version=\"4.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  \n\\series bold\n<Module type=\"solid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n    <units>SI</units>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n  </Module>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <!-- rest of file -->\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_spec>\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nNotes\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIn version 1.2 the Module section was optional.\n If omitted it was assumed that the \n\\shape italic\nsolid \n\\shape default\nmodule was used.\n Since version 2.0 the Module section is required and must be the first section in the file.\n\\end_layout\n\n\\begin_layout Enumerate\nOlder versions of FEBio (format specification 1.2 and before) allowed you to run a poroelastic (now called biphasic) problem by simply defining a poroelastic material.\n This is no longer possible.\n You need to define the proper Module section to run a biphasic analysis.\n If you have a file that no longer works as of version 1.4 of FEBio,\n you'll need to insert the following Module section in the file as the first section of the file.\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_spec version=\"1.2\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Module type=\"biphasic\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <!-- rest of the file unaltered -->\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_spec>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nControl Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Control-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe control section is defined by the \n\\shape italic\nControl \n\\shape default\nelement.\n This section defines all parameters that are used to control the evolution of the solution as well as parameters for the nonlinear solution procedure.\n These parameters are defined as child elements of the \n\\shape italic\nControl\n\\shape default\n element and depend somewhat on the analysis as defined by the \n\\shape italic\nModule\n\\shape default\n section.\n The control section defines the control parameters,\n the solver parameters,\n and optionally,\n the time_stepper parameters.\n \n\\end_layout\n\n\\begin_layout Standard\nNote that for multistep analyses (see chapter \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Multi-step-Analysis\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) the Control section is specified for each step separately and defined as a child element of each \n\\emph on\nstep\n\\emph default\n section.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <analysis>STATIC</analysis>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <time_steps>50</time_steps>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <step_size>0.02</step_size>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solver>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <max_refs>25</max_refs>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <diverge_reform>1</diverge_reform>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <reform_each_time_step>1</reform_each_time_step>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <dtol>0.001</dtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <etol>0.01</etol>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <rtol>0</rtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <lstol>0.9</lstol>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <min_residual>1e-20</min_residual>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <rhoi>-2</rhoi>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <qn_method type=\"BFGS\">\n\\end_layout\n\n\\begin_layout LyX-Code\n            <max_ups>10</max_ups>\n\\end_layout\n\n\\begin_layout LyX-Code\n        </qn_method>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solver>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <time_stepper>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <dtmin>0.0002</dtmin>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <dtmax>0.02</dtmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <max_retries>5</max_retries>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <opt_iter>6</opt_iter>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </time_stepper>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Control>\n\\end_layout\n\n\\begin_layout Subsection\nControl Parameters\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Control-Parameters\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following parameters are common for all analysis types.\n If not specified they are assigned default values,\n which are found in the last column.\n An asterisk (*) after the name indicates a required parameter.\n The numbers behind the description refer to the comments following the table.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"3\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nanalysis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the analysis type (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstatic \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime_steps*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTotal number of time steps.\n (= \n\\shape italic\nntime\n\\shape default\n)(2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstep_size*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe initial time step size.\n (= \n\\shape italic\ndt\n\\shape default\n) (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the level of state dumps to the plot file (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_MAJOR_ITRS \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot_range\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the range of the states that will be stored to the plot file (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,-1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot_stride\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the stride of the states that will be stored to the plot file (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot_zero_state\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag that controls whether the \n\\begin_inset Quotes eld\n\\end_inset\n\nzero\n\\begin_inset Quotes erd\n\\end_inset\n\n state will be written to the plot file,\n even if it is not defined in the range (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noutput_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nControls when to output data to file (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOUTPUT_MAJOR_ITRS \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noutput_stride\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the output stride for writing out\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nadaptor_resolve\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nre-solve time step after mesh adaptations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (yes)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\nanalysis \n\\shape default\nelement sets the analysis type.\n The text value of this element is determined by the module,\n but the numeric value should be 0 for (quasi-)static or steady-state analysis and 1 for dynamic or transient analysis.\n In a quasi-static analysis,\n inertial effects are ignored and an equilibrium solution is sought.\n Note that in this analysis mode it is still possible to simulate time-dependent effects such as viscoelasticity.\n In a dynamic analysis the inertial effects are included.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstatic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(quasi-) static analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsteady-state\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsteady-state response of a transient (quasi-static) biphasic,\n biphasic-solute,\n multiphasic,\n fluid or fluid-FSI analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndynamic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndynamic analysis for solid,\n fluid and fluid-FSI analyses\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe total simulation time of the analysis is determined by \n\\shape italic\nntime \n\\shape default\n* \n\\shape italic\ndt\n\\shape default\n.\n Note that when the auto-time stepper is enabled (see below),\n the actual number of time steps and time step size may be different than specified in the input file.\n However,\n the total simulation time will always be determined by \n\\shape italic\nntime \n\\shape default\n* \n\\shape italic\ndt.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\nplot_level \n\\shape default\nallows the user to control exactly when the solution is to be saved to the plot file.\n The following values are allowed:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_NEVER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDon't save the solution to the plot file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_MAJOR_ITRS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSave the solution after each converged timestep \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_MINOR_ITRS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSave the solution for every quasi-Newton iteration \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_MUST_POINTS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOnly save the solution at the must points \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_FINAL\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOnly store the final converged state.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_STEP_FINAL\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStore only the final converged state of each analysis step.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPLOT_AUGMENTATIONS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStore states at the start of each augmentation.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe PLOT_MUST_POINTS option must be used in conjunction of a \n\\shape italic\nmust-point \n\\shape default\ncurve.\n See the comments on the \n\\shape italic\ndtmax \n\\shape default\nparameter for more information on must-point curves.\n When the \n\\shape italic\nplot_level \n\\shape default\noption is set to PLOT_MUST_POINTS,\n only the time-points defined in the must-point curve are stored to the plotfile.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nWhen using the fixed time stepper,\n several parameters control which time steps are stored to the plot file.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\nThe \n\\shape italic\nplot_range \n\\shape default\nparameter sets the range of the states that will be stored to the plot file.\n The range is defined by two values that specify the first and last time step that will be stored to the plot file.\n The value \n\\begin_inset Quotes eld\n\\end_inset\n\n0\n\\begin_inset Quotes erd\n\\end_inset\n\n refers to the initial time step,\n usually time zero,\n and negative values count backwards from the final time step (as defined by the \n\\shape italic\ntime_steps\n\\shape default\n parameter).\n For instance,\n the default values,\n\\end_layout\n\n\\begin_layout LyX-Code\n<plot_range>0,-1</plot_range>\n\\end_layout\n\n\\begin_layout Standard\nstore all time steps to the plot file,\n including the initial \n\\begin_inset Quotes eld\n\\end_inset\n\nzero\n\\begin_inset Quotes erd\n\\end_inset\n\n time step.\n As another example,\n to store only the last five time steps,\n set\n\\end_layout\n\n\\begin_layout LyX-Code\n<plot_range>-5,-1</plot_range>\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n all time steps within the range will be stored to the plot file.\n Time steps can be skipped using the \n\\shape italic\nplot_stride \n\\shape default\nparameter.\n For instance,\n to store only every 10 steps,\n set\n\\end_layout\n\n\\begin_layout LyX-Code\n<plot_stride>10</plot_stride>\n\\end_layout\n\n\\begin_layout Standard\nNote that the first and last time step defined by the range will always be stored,\n regardless of the plot stride.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\nzero\n\\begin_inset Quotes erd\n\\end_inset\n\n time step refers to the initial state of the model,\n before any calculations are done.\n This state will only be stored to the plot file if the minimal value of the plot range is set to zero.\n To force storing this state to the plot file,\n set the \n\\shape italic\nplot_zero_state \n\\shape default\nparameter to one.\n\\end_layout\n\n\\begin_layout LyX-Code\n<plot_zero_state>1</plot_zero_state>\n\\end_layout\n\n\\begin_layout Standard\nThis will store the zero time step to the plot file,\n even when it is not specified inside the plot range.\n\\end_layout\n\n\\begin_layout Standard\nAgain,\n the \n\\shape italic\nplot_range\n\\shape default\n,\n \n\\shape italic\nplot_stride\n\\shape default\n,\n and \n\\shape italic\nplot_zero_state\n\\shape default\n parameters are only used by the fixed time stepper.\n Currently,\n these parameters are not used with the auto time stepper.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\shape italic\noutput_level\n\\shape default\n can be used to control when FEBio outputs the data files.\n The following values are supported.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOUTPUT_NEVER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDon't generate any output\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOUTPUT_MUST_POINTS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOnly output at must points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOUTPUT_MAJOR_ITRS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOutput at end of each time step\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOUTPUT_MINOR_ITRS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOutput at each iteration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOUTPUT_FINAL\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOnly output the data at the last converged time step.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Subsection\nTime Stepper parameters\n\\end_layout\n\n\\begin_layout Standard\nThe optional \n\\emph on\ntime_stepper \n\\emph default\nelement sets the parameters that control the FEBio auto-time stepper.\n This auto-time stepper will adjust the time step size depending on the convergence stats of the previous time step.\n It defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndtmin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMinimum time step size (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndtmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum time step size (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nopt_iter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOptimal,\n or desired,\n number of iterations per time step (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_retries\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum number of times a time step is restarted.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naggressiveness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAlgorithm for cutting the time step size after a failed time step (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncutback\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTime step scale factor,\n used when aggressiveness = 1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\ndtmin \n\\shape default\nand \n\\shape italic\ndtmax\n\\shape default\n values are used to constrain the range of possible time step values.\n The \n\\shape italic\nopt_iter \n\\shape default\ndefines the estimated optimal number of quasi-Newton iterations.\n If the actual number of iterations is less than or equal to this value the time step size is increased,\n otherwise it is decreased,\n as detailed further below.\n\\end_layout\n\n\\begin_layout Enumerate\nWhen a time step fails (e.g.\n due to a negative Jacobian),\n FEBio will retry the time step with a smaller time step size.\n The \n\\shape italic\nmax_retries \n\\shape default\nparameter determines the maximum number of times a timestep may be retried before FEBio error terminates.\n The new time step size is determined by the ratio of the time step size before restarts started and \n\\shape italic\nmax_retries+1\n\\shape default\n.\n For example,\n if the time step size is 0.1 and \n\\shape italic\nmax_retries \n\\shape default\nis set to 4,\n then the time step size is adjusted by 0.02:\n The first retry will have a step size of 0.08;\n the next will be 0.06,\n and so on.\n Specifically,\n the new time step size is determined as follows.\n (This is only for the case when the \n\\emph on\naggressiveness \n\\emph default\nparameter is set to zero.) \n\\begin_inset Newline newline\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\Delta t^{k+1}=\\Delta t^{k}-\\Delta t^{0}/\\left(m_{\\mathrm{max}}+1\\right)\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\Delta t^{k}$\n\\end_inset\n\nis the current time step size,\n \n\\begin_inset Formula $\\Delta t^{k+1}$\n\\end_inset\n\n is the new time step size,\n \n\\begin_inset Formula $\\Delta t^{0}$\n\\end_inset\n\n is the time step size before retries started,\n and \n\\begin_inset Formula $m_{\\mathrm{max}}$\n\\end_inset\n\nis the max number of retries (i.e.\n \n\\emph on\nmax_\n\\emph default\nretries).\n\\begin_inset Newline newline\n\\end_inset\n\nWhen the time step succeeds,\n the time step size will be adjusted by comparing the \n\\emph on\nopt_iter \n\\emph default\nparameter to the actual number of iterations it took to converge.\n If the actual number of iterations is larger than \n\\emph on\nopt_iter\n\\emph default\n,\n the time step size will be decreased.\n \n\\begin_inset Newline newline\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\Delta t^{k+1}=\\Delta t^{k}-\\left(\\Delta t^{k}-\\Delta t_{\\mathrm{min}}\\right)\\left(1-r\\right)\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\Delta t^{k}$\n\\end_inset\n\nis the current time step size,\n \n\\begin_inset Formula $\\Delta t^{k+1}$\n\\end_inset\n\n is the new time step size,\n \n\\begin_inset Formula $\\Delta t_{\\mathrm{min}}$\n\\end_inset\n\nis the minimum time step size and \n\\begin_inset Formula $r=\\sqrt{n_{\\mathrm{opt}}/n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $n$\n\\end_inset\n\n is the number of iterations and \n\\begin_inset Formula $n_{\\mathrm{opt}}$\n\\end_inset\n\nis the optimal number of iterations as specified by the user (i.e.\n \n\\emph on\nopt_iter\n\\emph default\n).\n\\begin_inset Newline newline\n\\end_inset\n\nOn the other hand,\n if the actual number of iterations is less than \n\\emph on\nopt_iter\n\\emph default\n,\n then the time step size will be increased.\n The exact equation follows.\n \n\\begin_inset Newline newline\n\\end_inset\n\n\n\\begin_inset Formula \n\\[\n\\Delta t^{k+1}=\\Delta t^{k}+\\left(\\Delta t_{\\mathrm{max}}-\\Delta t^{k}\\right)\\mathrm{min}\\left(0.2,r-1\\right)\n\\]\n\n\\end_inset\n\nIn addition,\n the new time step size is enforced to be less than 5 times the old time step size,\n to prevent the time step size from growing too fast.\n \n\\begin_inset Newline newline\n\\end_inset\n\nAfter calculating the new time step size,\n it will be ensured that the new timestep size falls within the range defined by \n\\emph on\ndtmin\n\\emph default\n and \n\\emph on\ndtmax \n\\emph default\nand that must-points are respected.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe user can specify a load curve for the \n\\shape italic\ndtmax \n\\shape default\nparameter.\n This load curve is referred to as the \n\\shape italic\nmust-point \n\\shape default\ncurve and serves two purposes.\n Firstly,\n it defines the value of the \n\\shape italic\ndtmax \n\\shape default\nparameter as a function of time.\n This can be useful,\n for instance,\n to enforce smaller time steps during rapid loading or larger time steps when approaching steady-state in a transient analysis.\n Secondly,\n the time points of the \n\\shape italic\ndtmax \n\\shape default\nloadcurve define so-called \n\\shape italic\nmust-points\n\\shape default\n.\n A must-point is a time point where FEBio must pass through.\n This is useful for synchronizing the auto-time stepper with the loading scenario.\n For instance,\n when loading starts at time 0.5,\n adding a must-point at this time will guarantee that the timestepper evaluates the model at that time.\n In conjunction with the PLOT_MUST_POINT value of the \n\\shape italic\nplot_level \n\\shape default\nparameter,\n this option can also be used to only write results to the plotfile at the specified time points.\n Consider the following example.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<dtmax lc=\"1\">0.1</dtmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n...\n\\end_layout\n\n\\begin_layout LyX-Code\n<loadcontroller id=\"1\" type=\"loadcurve\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <interpolate>STEP</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>0,0</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>0.5,\n 0.1</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>1.0,\n 0.2</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n</loadcontroller>\n\\end_layout\n\n\\begin_layout Standard\nThis example defines load curve 1 as the \n\\shape italic\nmust-point \n\\shape default\ncurve.\n This curve defines three points where FEBio will pass through (namely 0,\n 0.5 and 1.0).\n The values of each time point is the value of the maximum time-step size (\n\\shape italic\ndtmax\n\\shape default\n).\n Since the curve is defined as a step-function,\n each value is valid up to the corresponding time-point.\n Thus,\n between time 0 and time 0.5,\n the maximum time step value is 0.1.\n Between 0.5 and 1.0 the maximum time step value is 0.2.\n If the \n\\shape italic\nplot_level \n\\shape default\nparameter is set to PLOT_MUST_POINTS,\n then only the three defined time points will be stored to the plotfile.\n\\end_layout\n\n\\begin_layout Standard\nNote that when specifying a loadcurve for the dtmax parameter,\n the value of this parameter will be ignored.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nWhen FEBio fails to converge a time step,\n the time step size is reduced and then FEBio tries to solve the time step again.\n The algorithm for determining the reduced time step size,\n depends on the \n\\emph on\naggressiveness \n\\emph default\nparameter.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\n\n\\emph on\naggressiveness = 0\n\\emph default\n (default):\n \n\\begin_inset Formula $\\Delta t^{k+1}=\\Delta t^{k}-\\Delta t^{0}/\\left(m_{\\mathrm{max}}+1\\right)$\n\\end_inset\n\n,\n (see comment 2 for more details).\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\naggressiveness = 1:\n \n\\emph default\n\n\\begin_inset Formula $\\Delta t^{k+1}=\\gamma\\Delta t^{k}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is the cutback factor (i.e.\n the \n\\emph on\ncutback \n\\emph default\nparameter).\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Subsection\nCommon Solver Parameters\n\\end_layout\n\n\\begin_layout Standard\nMany of the solvers define the same parameters.\n They are listed in the table below.\n Additional parameters that are only defined for a specific type of analysis are listed in the following sections.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"23\" columns=\"3\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\netol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConvergence tolerance on energy (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConvergence tolerance on residual (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlstol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConvergence tolerance on line search (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.9\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlsmin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminimum line search step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlsiter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum number of line search iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nls_check_jacobians\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCheck for negative Jacobians during linesearch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_refs\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMax number of stiffness reformations (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n15\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nqn_method\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nQuasi-Newton update method (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBFGS\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_ups\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMax number of BFGS/Broyden stiffness updates (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiverge_reform\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag for reforming stiffness matrix when the solution diverges (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmin_residual\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets minimal value for residual norm (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1e-20\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_residual\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the max value for residual norm\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric_stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse symmetric stiffness matrix flag (6)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nequation_scheme\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the equation allocation scheme\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nequation_order\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the equation allocation ordering\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptimize_bw\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOptimize bandwidth of stiffness matrix (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear_solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the preferred linear solver to use.\n (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(default)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncheck_zero_diagonal\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCheck for zero diagonals in the stiffness matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzero_diagonal_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTolerance value for checking zero diagonals\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nforce_partition\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFor a partition in the stiffness matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreform_each_time_step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReform the stiffness matrix at the start of each time step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (enabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreform_augment\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReform stiffness matrix after each augmentation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nFEBio determines convergence of a time step based on three convergence criteria:\n displacement,\n residual and energy (that is,\n residual multiplied by displacement).\n Each of these criteria requires a tolerance value that will determine convergence when the relative change will drop below this value.\n For example,\n a displacement tolerance of \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n means that the ratio of the displacement increment (i.e.\n the solution of the linearized FE equations,\n \n\\begin_inset Formula $\\Delta\\mathbf{U}=-\\mathbf{K}_{k}^{-1}\\mathbf{R}_{k}$\n\\end_inset\n\n) norm at the current iteration \n\\begin_inset Formula $k+$\n\\end_inset\n\n\n\\shape italic\n1\n\\shape default\n to the norm of the total displacement (\n\\begin_inset Formula $\\mathbf{U}_{k+1}=\\mathbf{U}_{k}+\\Delta\\mathbf{U}$\n\\end_inset\n\n) must be less than \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n:\n\\begin_inset Formula \n\\[\n\\frac{\\left\\Vert \\Delta\\mathbf{U}\\right\\Vert }{\\left\\Vert \\mathbf{U}_{k+1}\\right\\Vert }<\\varepsilon\n\\]\n\n\\end_inset\n\nFor the residual and energy norms,\n it is the ratio of the current residual norm (resp.\n energy norm) to the initial one that needs to be less than the specified convergence tolerance.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\nTo disable a specific convergence criterion,\n set the corresponding tolerance to zero.\n For example,\n by default,\n the residual tolerance is zero,\n so that this convergence criterion is not used.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\shape italic\nlstol\n\\shape default\n parameter controls the scaling of the vector direction obtained from the line search.\n A line search method is used to improve the convergence of the nonlinear (quasi-) Newton solution algorithm.\n After each quasi-Newton iteration,\n this algorithm searches in the direction of the displacement increment for a solution that has less energy (that is,\n residual multiplied with the displacement increment) than the previous iteration.\n In many problems this will automatically be the case.\n However,\n in some problems that are very nonlinear (e.g.\n contact),\n the line search can improve convergence significantly.\n The line search can be disabled by setting the \n\\shape italic\nlstol\n\\shape default\n parameter to zero,\n although this is not recommended.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nqn_method\n\\emph default\n property determines the quasi-Newton update method.\n The particular method is set via the \n\\emph on\ntype \n\\emph default\nattribute.\n Additional parameters are then specified as child elements of this tag.\n\n\\shape italic\n\\emph on\n \n\\shape default\n\\emph default\nSee section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Quasi-Newton-Solver-Parameters\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\noptimize_bw\n\\shape default\n parameter enables bandwidth minimization for the global stiffness matrix.\n This can drastically decrease the memory requirements and running times when using the skyline solver.\n It is highly recommended when using the skyline solver.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<optimize_bw>1</optimize_bw>\n\\end_layout\n\n\\begin_layout Standard\nWhen using a different linear solver (e.g.,\n pardiso or SuperLU),\n the bandwidth optimization can still be performed if so desired.\n However,\n for these solvers there will be little or no effect since these solvers are not as sensitive to the bandwidth as the skyline solver.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nIf no force is acting on the model,\n then convergence might be problematic due to numerical noise in the calculations.\n For example,\n this can happen in a displacement driven contact problem where one of the contacting bodies is moved before initial contact is made.\n When this happens,\n the residual norm will be very small.\n When it drops below the tolerance set by \n\\shape italic\nmin_residual\n\\shape default\n,\n FEBio will assume that there is no force acting on the system and will converge the time step.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nsymmetric_stiffness \n\\emph default\nflag is used to set the symmetry of the global stiffness matrix.\n Only specify this flag if you want to override the default symmetry,\n which depends somewhat on the analysis type.\n Forcing a symmetric matrix when the problem is non-symmetric may have negative impact on convergence.\n The values for this parameter are:\n\\begin_inset Newline newline\n\\end_inset\n\n- 0 = unsymmetic\n\\begin_inset Newline newline\n\\end_inset\n\n- 1 = symmetric\n\\begin_inset Newline newline\n\\end_inset\n\n- 2 = structurally symmetric\n\\begin_inset Newline newline\n\\end_inset\n\n- 3 = preferred\n\\begin_inset Newline newline\n\\end_inset\n\nIf the option is set to 3 (preferred),\n then one of the other formats will be chosen depending on the model components in the model.\n For instance,\n if a non-symmetric contact formulation is used,\n then a non-symmetric stiffness matrix will be used.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe default linear solver used by FEBio is usually configured in the FEBio configuration file.\n However,\n it can also be specified directly in the FEBio input file as part of the solver parameters using the \n\\emph on\nlinear_solver\n\\emph default\n tag.\n See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Configuring-Linear-Solvers\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on configuring linear solvers.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<linear_solver type=\"pardiso\"/>\n\\end_layout\n\n\\end_deeper\n\\begin_layout Subsection\nQuasi-Newton Solver Parameters\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Quasi-Newton-Solver-Parameters\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMost solvers in FEBio use the quasi-Newton method for solving the nonlinear FE equations.\n Several different QN algorithms are implemented as described below.\n The specific QN method is selected using the \n\\emph on\nqn_method\n\\emph default\n property of the \n\\emph on\nsolver\n\\emph default\n.\n The following QN methods are supported.\n \n\\end_layout\n\n\\begin_layout Description\nBFGS The Broyden-Fletcher-G-S method.\n (default if qn_method is not specified).\n This method assumes the global stiffness matrix is symmetric.\n It can still be used with non–symmetric matrices,\n but the convergence might be compromised.\n \n\\end_layout\n\n\\begin_layout Description\nBroyden The Broyden method is similar to BFGS,\n but works with both symmetric and non-symmetric matrices.\n Therefore,\n it is the recommended method for problems that generate a non-symmetric stiffness matrix,\n such as (some) contact formulations,\n biphasic,\n multi-phasic,\n fluid,\n fluid FSI.\n \n\\end_layout\n\n\\begin_layout Description\nJFNK This is an implementation of the Jacobian-Free-Newton-Krylov method.\n When using this option,\n you must also use an iterative linear solver such as FGMRES.\n \n\\end_layout\n\n\\begin_layout Standard\nBoth the BFGS and Broyden methods calculate the global stiffness matrix at the beginning of each time step.\n For each iteration,\n a matrix update is then done.\n The maximum number of such updates is set with \n\\shape italic\nmax_ups\n\\shape default\n.\n When FEBio reaches this number,\n or if the solution diverges and \n\\emph on\ndiverge_reform\n\\emph default\n is set to 1,\n it reforms the global stiffness matrix (that is,\n it recalculates it) and factorizes it,\n essentially taking a \"full Newton\" iteration.\n Then FEBio continues with BFGS/Broyden iterations.\n The \n\\shape italic\nmax_refs \n\\shape default\nparameter is used to set the maximum of such reformations FEBio can do,\n before it fails the timestep.\n In that case,\n FEBio will either terminate or,\n if the auto-time stepper is enabled,\n retry with a smaller time step size.\n \n\\end_layout\n\n\\begin_layout Standard\nThe BFGS and Broyden take the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_ups\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\shape italic\n\\emph on\nThis is the maximum number of updates between stiffness matrix reformations.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the max condition number.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1e5\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_buffer_size\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the max buffer size.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (ignored)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncycle_buffer\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRecycle buffers when max_ups is larger than max_buffer_size\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote that when max_ups is set to 0,\n FEBio effectively will use a Full-Newton method since the stiffness matrix is reformed each iteration.\n In this case it is recommended to increase the number of \n\\shape italic\nmax_refs \n\\shape default\n(to e.g.\n 50) in the solver settings,\n since the default value might cause FEBio to terminate prematurely when convergence is slow.\n\\end_layout\n\n\\begin_layout Subsection\nSolver Parameters for a Structural Mechanics Analysis\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Parameters-Solid-Analysis\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA structural mechanics analysis is defined by using the \n\\shape italic\nsolid \n\\shape default\ntype in Module section.\n The solver parameters are given in the table below:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance on displacement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.001\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrhoi\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpectral radius parameter \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-2\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nalpha\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngeneralized alpha method parameter alpha\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeta\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngeneralized alpha method beta\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.25\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngamma\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngeneralized alpha method gamma\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlogSolve\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse an acceleration for Newton iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\narc_length\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecifies arc-length method (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\narc_length_scale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the arc-length scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThere are two time-integrators implemented in FEBio:\n the classical Newmark integration and the generalized alpha method.\n Setting rhoi to -2,\n will use the former,\n where as setting rhoi to the range [0,1] will use the latter.\n In the latter case,\n the values for gamma,\n beta,\n and gamma are determined from the spectral radius parameter,\n rhoi.\n \n\\end_layout\n\n\\begin_layout Enumerate\nWhen using the arc-length method,\n there are some important restrictions.\n Only zero-prescribed boundary conditions are allowed.\n The loads must be specified via nodal loads and cannot be time-dependent.\n The time_steps and step_size control parameters are interpreted as arc-length steps and step_size respectively.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen using the \n\\begin_inset Quotes eld\n\\end_inset\n\nCG-solid\n\\begin_inset Quotes erd\n\\end_inset\n\n solver,\n the following parameters are defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncgmethod\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSelect solution algorithm (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npreconditioner\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSelect the preconditioner (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlsiter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of line search iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe solution algorithm can be:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\n0 :\n Hager-Zhang conjugate gradient (This is the default and should be preferred since its faster.)\n\\end_layout\n\n\\begin_layout Enumerate\n1 :\n steepest descent\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nSet 0 for no preconditioner,\n 1 for diagonal stiffness preconditioner.\n The diagonal stiffness preconditioner greatly improves convergence when there are different size elements,\n mixed materials,\n or quadratic elements with midside nodes.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<solver type=\"CG-solid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <cgmethod>0</cgmethod>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <preconditioner>1</preconditioner>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lsiter>10</lsiter>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solver>\n\\end_layout\n\n\\begin_layout Subsection\nSolver Parameters for Biphasic Analysis\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Parameters-Biphasic-Analysis\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA biphasic analysis is defined by using the \n\\shape italic\nbiphasic \n\\shape default\ntype in Module section.\n Since a biphasic analysis couples a fluid problem to a solid mechanics problem,\n all control parameters above can be used in a biphasic analysis.\n In addition,\n the following parameters can be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nptol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the fluid pressure convergence tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixed_formulation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse a mixed formulation (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComment\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nThe mixed formulation uses discontinuous shape functions for the pressure interpolation.\n It still uses the full shape functions for displacement.\n \n\\end_layout\n\n\\begin_layout Subsection\nSolver Parameters for Solute and Multiphasic Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Parameters-Solute-Analysis\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the type attribute of the Module section is set to \n\\shape italic\nsolute\n\\shape default\n or \n\\emph on\nmultiphasic\n\\emph default\n,\n an analysis is solved that includes solute transport.\n All parameters for a biphasic analysis can be used (including the ones for a structural mechanics analysis).\n In addition,\n the following parameters can be specified:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nctol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the concentration convergence tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nforce_positive_concentrations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nForce the concentration values always to be positive.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (enabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSolver Parameters for Fluid and Fluid-FSI Analyses\n\\end_layout\n\n\\begin_layout Standard\nA fluid analysis is defined by using the \n\\emph on\nfluid\n\\emph default\n type in Module section (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Module-Section\"\nnolink \"false\"\n\n\\end_inset\n\n).\n In addition to the common parameters,\n the following parameters can be specified:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance on velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.001\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nftol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance on dilatation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.001\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrhoi\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpectral radius parameter \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreform_each_time_step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag for reforming stiffness matrix at the start of each time step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npredictor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the predictor method\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmin_volume_ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the minimum value for the volume ratio.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (disabled)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA fluid-structure interaction analysis is defined by using the \n\\emph on\nfluid-FSI\n\\emph default\n type in Module section.\n It uses the parameters of \n\\emph on\nsolid\n\\emph default\n and \n\\emph on\nfluid\n\\emph default\n analyses.\n\\end_layout\n\n\\begin_layout Standard\nTransient fluid and fluid-FSI analyses may often run very efficiently using Broyden's method (\n\\emph on\nqnmethod\n\\emph default\n set to 1) with \n\\emph on\nmax_ups\n\\emph default\n set to 50;\n efficiency may be further increased by setting \n\\emph on\nreform_each_time_step\n\\emph default\n to 0,\n which will postpone reforming the stiffness matrix at subsequent time steps until \n\\emph on\nmax_ups\n\\emph default\n updates have been exhausted.\n When using Broyden's method with fluid and fluid-FSI analyses,\n set \n\\emph on\nrtol\n\\emph default\n to a non-zero value to ensure an accurate solution,\n for example \n\\emph on\nrtol\n\\emph default\n=0.001 (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Control-Parameters\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe spectral radius parameter \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n determines the time integration scheme:\n Values in the range \n\\begin_inset Formula $0\\le\\rho_{\\infty}\\le1$\n\\end_inset\n\n use the generalized \n\\begin_inset Formula $\\alpha-$\n\\end_inset\n\nmethod (see \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n).\n With \n\\begin_inset Formula $\\rho_{\\infty}=0$\n\\end_inset\n\n,\n damping of higher frequency components (such as those produced by a step increase in velocity) occurs theoretically within a single time step;\n in contrast,\n with \n\\begin_inset Formula $\\rho_{\\infty}=1$\n\\end_inset\n\n,\n no damping occurs,\n potentially leading to instability in the solution process.\n When solving transient flows that exhibit eddies or shed vortices,\n a value of \n\\begin_inset Formula $\\rho_{\\infty}=0.5$\n\\end_inset\n\n represents a good balance between too much and too little numerical damping.\n\\end_layout\n\n\\begin_layout Subsection\nExplicit-Solid Solver\n\\end_layout\n\n\\begin_layout Standard\nFEBio supports an explicit-solid solver,\n which solves dynamic structural mechanics problems using an explicit time-integration scheme.\n This means that the solution at a given timestep is calculated using only the solution from the previous timestep.\n The advantage of this approach is that no stiffness matrix needs to be calculated and no linear system of equations needs to be solved (at least,\n with mass-lumping enabled).\n As a result,\n this solver can solve dynamic problems very fast.\n However,\n the downside of this approach is that it is only conditionally stable and imposes an upper limit on the time step size.\n This limit is often much smaller than the time step sizes that can be taken by implicit time-integration schemes (as used by the default Newton-based solvers in FEBio) and thus often will require many more time steps to solve the model.\n Thus,\n the explicit solver is most useful in dynamic problems that already require relatively small time step sizes,\n such as impact-contact models,\n or models that use non-elastic material behavior that may undergo rapid changes in their constitutive behavior (e.g.\n damage).\n \n\\end_layout\n\n\\begin_layout Standard\nThe explicit solver in FEBio uses the midpoint time integration rule and uses mass lumping.\n Mass lumping is a technique that replaces the consistent mass matrix with a diagonal approximation.\n In fact,\n it's this diagonalization of the mass matrix that really prevents the need to solve a linear system of equations.\n (Since solving a diagonal linear system of equations is trivial.) \n\\end_layout\n\n\\begin_layout Standard\nThe explicit solver can be selected by setting the \n\\series bold\ntype \n\\series default\nattribute of the \n\\series bold\nsolver \n\\series default\nelement to \n\\emph on\nexplicit-solid\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<solver \n\\series bold\ntype=\"explicit-solid\"\n\\series default\n>\n\\end_layout\n\n\\begin_layout LyX-Code\n...\n\\end_layout\n\n\\begin_layout LyX-Code\n</solver>\n\\end_layout\n\n\\begin_layout Standard\nThe following parameters can be specified for the explicit-solid solver.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmass_lumping\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nChooses the mass lumping strategy.\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndyn_damping\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe damping factor for dynamic damping.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe parameters of the explicit-solid solver\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n \n\\end_layout\n\n\\begin_layout Enumerate\nAs mentioned,\n mass lumping is a technique to approximate the mass matrix with a diagonal matrix.\n Several approaches are supported in FEBio.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\nmass_lumping = 0:\n no mass-lumping (This is currently not implemented,\n but the value is reserved in case support will be added in the future.) \n\\end_layout\n\n\\begin_layout Enumerate\nmass_lumping = 1 :\n use row summation approach.\n Useful for linear elements,\n but may produce negative nodal masses for quadratic elements and thus should not be used for models containing higher-order elements.\n \n\\end_layout\n\n\\begin_layout Enumerate\nmass_lumping = 2:\n HRZ lumping technique.\n Better suited for higher-order elements,\n but should not be used for shells.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nDynamic damping is a technique to dampen the solution in order to obtain an equilibrium solution (e.g.\n for false-transient analyses).\n In FEBio,\n dynamic damping is applied to the velocity updates and the\n\\emph on\n dyn_damping \n\\emph default\nparameter specifies the damping factor.\n The default value is 1 so no damping is applied.\n Note that the lower the damping factor,\n the more damping,\n but also the longer it may take to find the equilibrium solution.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nImportant!\n \n\\series default\nAs a final note,\n the explicit-solver is still considered an experimental feature in FEBio and may not work correctly with all of the features of FEBio yet.\n Use at your own risk and please report any problems you may encounter to the FEBio developers.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nGlobals Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Globals-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nGlobals\n\\shape default\n section is used to define some global variables,\n such as global constants,\n solute data and solid bound molecule data,\n as well user-defined global variables.\n\\end_layout\n\n\\begin_layout Subsection\nConstants\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constants\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nGlobal constants are predefined variables that some modules may rely on and currently include the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n [\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n/\n\\series bold\nn\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nT\n\\series default\n],\n absolute temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n [\n\\series bold\nT\n\\series default\n],\n and Faraday constant \n\\begin_inset Formula $F_{c}$\n\\end_inset\n\n [\n\\series bold\nQ\n\\series default\n/\n\\series bold\nn\n\\series default\n].\n These constants must be expressed in units consistent with the rest of the analysis:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314e-6</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>298</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Fc>96485e-9</Fc>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Subsection\nSolutes\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solutes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn biphasic-solute,\n triphasic,\n and multiphasic analyses,\n a unique identifier must be associated with each solute in order to enforce consistent nodal degrees of freedom across boundaries of different materials.\n This unique identification is achieved by listing each solute species that appears in the entire finite element model and associating it with a unique \n\\shape italic\nid\n\\shape default\n,\n \n\\shape italic\nname\n\\emph on\n,\n and providing its\n\\shape default\n\\emph default\n charge number \n\\begin_inset Formula $z^{\\alpha}$\n\\end_inset\n\n,\n molar mass \n\\begin_inset Formula $M^{\\alpha}$\n\\end_inset\n\n,\n and density \n\\begin_inset Formula $\\rho_{T}^{\\alpha}$\n\\end_inset\n\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Solutes>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solute id=\"1\" name=\"Na\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>1</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <molar_mass>22.99</molar_mass>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <density>0.97</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solute id=\"2\" name=\"Cl\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>-1</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <molar_mass>35.45</molar_mass>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <density>3.21</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solute id=\"3\" name=\"Glc\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>0</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <molar_mass>180.16</molar_mass>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <density>1.54</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Solutes>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\nThese solute identification numbers should be referenced in the \n\\shape italic\nsol\n\\shape default\n attribute of solutes when defining a biphasic-solute (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-BS-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n),\n triphasic or multiphasic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Multiphasic-Mat\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe molar mass and density of solutes are needed only when solutes are involved in chemical reactions.\n When not specified,\n default values for these properties are set to 1.\n\\end_layout\n\n\\begin_layout Subsection\nSolid-Bound Molecules\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solid-Bound-Molecules\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn multiphasic analyses with chemical reactions involving solid-bound molecules,\n a unique identifier must be associated with each such molecule in order to enforce consistent properties across the entire model.\n This unique identification is achieved by listing each solid-bound species that appears in the entire finite element model and associating it with a unique \n\\shape italic\nid\n\\shape default\n,\n \n\\shape italic\nname,\n\n\\shape default\n charge number,\n molar mass and density:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <SolidBoundMolecules>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solid_bound id=\"1\" name=\"CS\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>-2</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <molar_mass>463.37</molar_mass>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <density>1.5<density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solid_bound >\n\\end_layout\n\n\\begin_layout LyX-Code\n  </SolidBoundMolecules >\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nid\n\\shape default\n number should be referenced in the \n\\shape italic\nsbm\n\\shape default\n attribute of solid-bound molecules when included in the definition of a multiphasic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The charge number is used in the calculation of the fixed charge density contributed by this solid-bound molecule to the overall solid matrix fixed-charge density.\n The density is used in the calculation of the contribution of this molecule to the referential solid volume fraction.\n The density and molar mass are used in the calculation of the molar volume of this molecule in chemical reactions.\n\\end_layout\n\n\\begin_layout Subsection\nUser Variables\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:User-Variables\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUsers can define custom,\n global variables,\n that can be referenced in other model parameters.\n This offers a convenient way to parameterize a model.\n Global user variables can also be used in optimizations.\n Currently,\n only scalar values can be defined.\n User variables are defined in the \n\\emph on\nVariables \n\\emph default\nchild element.\n \n\\end_layout\n\n\\begin_layout Standard\nTo define a global variable,\n specify the \n\\emph on\nvar \n\\emph default\nelement,\n add the \n\\emph on\nname \n\\emph default\nattribute to name the variable,\n and specify the variable's value as the tag's value.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following example defines a global variable,\n called A,\n and assigns the value 1.23 to it.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Variables>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n  <var name=\"A\">1.23</var>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Variables>\n\\end_layout\n\n\\begin_layout Standard\nAs mentioned,\n these global variables can be used to define other model variables,\n for example,\n in variables that are defined with mathematical expressions.\n To use a global variable,\n prefix the name with 'fem.'.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <material name=\"mymat\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n    <E type=\"math\">3*fem.A</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </material>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMaterial Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Material-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material section is defined by the \n\\shape italic\nMaterial\n\\shape default\n element.\n This section defines all the materials and material parameters that are used in the model.\n A material is defined by the \n\\shape italic\nmaterial\n\\shape default\n child element.\n This element has two attributes:\n \n\\shape italic\nid,\n\n\\shape default\n which specifies a number that is used to reference the material,\n and \n\\shape italic\ntype\n\\shape default\n,\n which specifies the type of the material.\n The \n\\shape italic\nmaterial\n\\shape default\n element can also have a third optional attribute called \n\\shape italic\nname\n\\shape default\n,\n which can be used to identify the material by a text description.\n A material definition might look like this:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout Standard\nOr,\n if the optional \n\\shape italic\nname\n\\shape default\n attribute is present:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"rigid body\" name=\"femur\">\n\\end_layout\n\n\\begin_layout Standard\nThe material parameters that have to be entered depend on the material type.\n A complete list of available materials and their parameters is provided in Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMesh Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Geometry-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nMesh \n\\emph default\nsection contains all the mesh data,\n including nodal coordinates and element connectivity.\n It has the following sub-sections:\n\\end_layout\n\n\\begin_layout Description\nNodes defines the nodal coordinates of the mesh\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\nElements \n\\series default\ndefines the elements of the mesh.\n If the name attribute is provided,\n then this also defines an element set.\n Element sets defined this way are also referred to as \n\\emph on\nparts\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nNodeSet\n\\shape default\n\\emph default\n defines a node set \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nEdge\n\\shape default\n\\emph default\n defines an edge,\n i.e.\n a set of line elements \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nSurface\n\\shape default\n\\emph default\n defines a surface,\n i.e.\n a set of facets \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nDiscreteSet\n\\emph default\n \n\\shape default\ndefines a set of discrete elements (e.g.\n springs) \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nElementSet\n\\shape default\n\\emph default\n defines an element set \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nSurfacePair\n\\emph default\n \n\\shape default\ndefine a surface pair that can be used by a contact definition.\n \n\\end_layout\n\n\\begin_layout Description\nPartList define a named list of parts (i.e.\n element sets defined via the Elements tag)\n\\end_layout\n\n\\begin_layout Standard\nAt least one \n\\emph on\nNodes \n\\emph default\nsection must be defined and one \n\\emph on\nElements\n\\emph default\n section.\n The other sections are optional.\n The \n\\shape italic\nNodeSet\n\\shape default\n,\n \n\\shape italic\nEdge\n\\shape default\n,\n \n\\shape italic\nSurface\n\\shape default\n,\n \n\\shape italic\nDiscreteSet\n\\shape default\n,\n \n\\shape italic\nElementSet\n\\shape default\n,\n \n\\shape italic\nSurfacePair\n\\emph on\n,\n and \n\\emph default\nPartList \n\\shape default\nsections define sets of nodes,\n edges,\n facets,\n discrete elements,\n elements,\n surface pairs,\n and part lists,\n respectively,\n and can be referenced by other sections of the model file.\n For instance,\n boundary conditions can be defined by referencing the sets to which the boundary condition will be applied.\n\\end_layout\n\n\\begin_layout Subsection\nNodes Section\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nNodes \n\\shape default\nsection contains nodal coordinates.\n It has an optional attribute called \n\\shape italic\nname\n\\shape default\n.\n If this attribute is defined,\n the \n\\shape italic\nNodes \n\\shape default\nsection also defines a node set.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Nodes [name=\"<set name>\"]>\n\\end_layout\n\n\\begin_layout Standard\nThe nodes are defined using the \n\\shape italic\nnode \n\\shape default\ntag which is a child of the \n\\shape italic\nNodes \n\\shape default\nsection.\n Repeat the following XML-element for each node:\n\\end_layout\n\n\\begin_layout LyX-Code\n<node id=\"n\">x,y,z</node>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nid\n\\shape default\n attribute is the global identifier of the node and must be a unique number within the model definition.\n Nodal ids must be defined in increasing order,\n but do not have to be sequential.\n This \n\\shape italic\nid\n\\shape default\n is used as a reference in the element connectivity section.\n\\end_layout\n\n\\begin_layout Standard\nMultiple \n\\shape italic\nNodes \n\\shape default\nsections can be defined,\n but each node can only be defined once.\n For example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Nodes name=\"set01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node id=\"1\">0,0,0</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n  ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node id=\"101\">1,1,1</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Nodes>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Nodes name =\"set02\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node id=\"102\">2,1,1</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n  ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node id=\"999\">2,2,2</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Nodes>\n\\end_layout\n\n\\begin_layout Subsection\nElements Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Elements-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nElements\n\\shape default\n sections contain a list of the element connectivity data.\n Multiple \n\\shape italic\nElements \n\\shape default\nsections can be defined.\n The \n\\shape italic\nElements \n\\shape default\nsection has the following attributes.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nAttribute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement type \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nname\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nunique name that identifies this domain \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach \n\\shape italic\nElements \n\\shape default\nsection contains multiple \n\\shape italic\nelem \n\\shape default\nelements that define the element connectivities.\n Each \n\\shape italic\nelem \n\\shape default\ntag has a \n\\shape italic\nid \n\\shape default\nattribute that defines the element number.\n For example,\n the following \n\\shape italic\nElements\n\\shape default\n section defines a list of hexahedral elements:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Elements type=\"hex8\" name=\"part1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elem id=\"1\">1,2,3,4,5,6,7,8</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elem id=\"2\">9,10,11,12,13,14,15,16</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n  ...\n\\end_layout\n\n\\begin_layout LyX-Code\n</Elements>\n\\end_layout\n\n\\begin_layout Standard\nFEBio classifies elements in three categories,\n namely \n\\shape italic\nsolids,\n shells\n\\shape default\n\\emph on\n,\n \n\\emph default\nand \n\\emph on\nbeams\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nSolid Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solid-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following solid element types are defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhex8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8-node trilinear hexahedral element \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhex20\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n20-node quadratic hexahedral elements \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhex27\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n27-node quadratic hexahedral elements \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenta6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6-node linear pentahedral (wedge) element \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenta15\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n15-node quadratic pentahedral (wedge) element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npyra5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5-node linear pyramidal element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npyra13\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n13-node quadratic pyramidal element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntet4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4-node linear tetrahedral element \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntet10\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10-node quadratic tetrahedral element \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntet15\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n15-node quadratic tetrahedral element \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe node numbering has to be defined as in the figure below.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigNodeNumberingSolidElements.png\n\tlyxscale 50\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\nNode numbering for solid elements\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nShell Elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Shell-Elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio currently supports the following shell elements:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntri3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3-node linear triangular shell element (compatible strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntri6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6-node quadratic triangular shell element (compatible strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nquad4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4-node linear quadrilateral shell element (compatible strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nquad8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8-node quadratic quadrilateral shell element (compatible strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nquad9\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n9-node quadratic quadrilateral shell element (compatible strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nq4ans\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4-node linear quadrilateral shell element (assumed natural strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nq4eas\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4-node linear quadrilateral shell element (enhanced assumed strain)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor shell elements that use a \n\\emph on\ncompatible strain\n\\emph default\n formulation,\n the calculation of strain components is based on nodal displacements,\n similar to hexahedral or pentrahedral elements.\n Users should be aware that this compatible strain formulation is very susceptible to element locking when the shell thickness is much smaller than the shell size (e.g.,\n when the aspect ratio is less than 0.01).\n Therefore,\n these shell formulations should be used with caution,\n keeping in mind this important constraint.\n Conversely,\n these shell elements perform very well when they are attached to solid elements (e.g.,\n skin over muscle),\n or sandwiched between shell elements (e.g.,\n cell membrane separating cytoplasm from extra-cellular matrix),\n as described in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hou18\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigNodeNumberingShellElements.png\n\tlyxscale 50\n\tscale 75\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\nNode numbering for shell elements\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe element-locking limitation of compatible strain shell formulations has motivated the development of specialized shell formulations that attempt to overcome locking.\n The FE literature on this subject is rather extensive and we refer the reader to the excellent review chapter by Bischoff et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bischoff18\"\nliteral \"true\"\n\n\\end_inset\n\n on this topic.\n Methods for overcoming locking include the assumed natural strain (ANS) formulation for transverse shear strains \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"MacNeal78,Bathe86\"\nliteral \"true\"\n\n\\end_inset\n\n and transverse normal strains \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Betsch95,Bischoff97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The ANS formulation may be supplemented with the enhanced assumed strain (EAS) method \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo90\"\nliteral \"true\"\n\n\\end_inset\n\n and extended to large deformations \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Klinkel99,Vu-Quoc03,Simo93\"\nliteral \"true\"\n\n\\end_inset\n\n.\n FEBio includes the ANS (\n\\emph on\nq4ans\n\\emph default\n) and EAS (\n\\emph on\nq4eas\n\\emph default\n) quadrilateral shell element formulations of Vu-Quoc and Tan \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Vu-Quoc03\"\nliteral \"true\"\n\n\\end_inset\n\n,\n using a seven-parameter EAS interpolation,\n which is otherwise substantially similar to the five-parameter interpolation presented in an earlier study by Klinkel et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Klinkel99\"\nliteral \"true\"\n\n\\end_inset\n\n.\n These shell elements are not suitable for attachment to a solid element,\n nor sandwiching between two solid elements.\n Since they don't experience element locking,\n they should be loaded more slowly than compatible strain shell elements.\n The most accurate of these shell formulation is \n\\emph on\nq4eas\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n the nodes of a shell element define its \n\\emph on\ntop\n\\emph default\n face.\n The location of the shell element \n\\emph on\nbottom\n\\emph default\n face is calculated from the nodal values of the shell thickness,\n along the opposite direction of nodal normals.\n The nodal normal is evaluated by averaging the top face normal of all shell elements sharing that node.\n When a shell element is attached to a solid element (e.g.,\n when the shell nodes also represent the nodes of one face of the solid element),\n FEBio automatically accounts for the shell thickness,\n reducing the height of the solid element in the direction of the shell normal.\n It is the user's responsibility to ensure that the shell thickness is less than that of the solid element;\n otherwise,\n a negative Jacobian error will be automatically generated.\n\\end_layout\n\n\\begin_layout Standard\nPrior to FEBio 2.6,\n the nodes of a shell element defined its \n\\emph on\nmiddle\n\\emph default\n face (halfway between top and bottom,\n such that the shell element extends above and below the face defined by the shell nodes).\n This older formulation can be recovered by setting \n\\emph on\nshell_formulation\n\\emph default\n to 0 in the \n\\emph on\nControl\n\\emph default\n section.\n This setting only works with compatible strain shell formulations.\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n stresses and strains reported for a shell element are evaluated by averaging all the integration point values within the shell element,\n thus producing values that reflect the response along the middle surface.\n To display shell stresses or strains at the top or bottom faces,\n use plot variables that start with 'shell top...' or 'shell bottom...',\n such as 'shell top stress' or 'shell bottom nodal strain'.\n\\end_layout\n\n\\begin_layout Standard\nWhen shell domains intersect such that three or more shell elements share the same edge,\n as in a T-connection,\n users should set \n\\emph on\nshell_normal_nodal\n\\emph default\n to 0 in the \n\\emph on\nShellDomain\n\\emph default\n section.\n This setting ensures that the bottom face of a shell element is calculated using the top face normal,\n instead of nodal normals,\n since the latter would be ill-defined for nodes belonging to such an edge.\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n please note that shell elements cannot be stacked in FEBio.\n\\end_layout\n\n\\begin_layout Subsubsection\nBeam Elements\n\\end_layout\n\n\\begin_layout Standard\nFEBio supports some truss and beam element formulations.\n Currently,\n only linear trusses and linear and quadratic beam elements are supported.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nline2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2-node linear truss or beam element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nline3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3-node quadratic beam element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhether a truss or beam formulation is used,\n is determined in the corresponding BeamDomain section.\n \n\\end_layout\n\n\\begin_layout Subsection\nNodeSet Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:NodeSet-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nNodeSet \n\\shape default\nsection allows users to define node sets.\n These node sets can then later be used in the definition of boundary conditions and loads.\n A node set is defined by the \n\\shape italic\nNodeSet \n\\shape default\ntag.\n This tag takes one required attribute,\n \n\\shape italic\nname\n\\shape default\n,\n which defines the name of the node set.\n A node set definition is followed by a list of node IDs.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<NodeSet name=\"nodeset1\">1,\n 2,\n 101,\n 102</NodeSet>\n\\end_layout\n\n\\begin_layout Standard\nFor larger nodesets,\n it is recommended to split the list across several lines.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<NodeSet name=\"nodeset1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n   1,\n 2,\n 3,\n 4,\n\\end_layout\n\n\\begin_layout LyX-Code\n   5,\n 6,\n 7,\n 8,\n\\end_layout\n\n\\begin_layout LyX-Code\n   9,\n 10,\n 11,\n 12\n\\end_layout\n\n\\begin_layout LyX-Code\n</NodeSet>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\end_layout\n\n\\begin_layout Subsection\nEdge Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Edge-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nEdge \n\\shape default\nsection allows users to define a set of edges.\n These edges can be used to define boundary conditions or loads.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Edge name=\"edge1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <line2 lid=\"1\">1,2</line2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <line2 lid=\"2\">2,3</line2>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Edge>\n\\end_layout\n\n\\begin_layout Standard\nHere,\n the \n\\shape italic\nlid \n\\shape default\nattribute defines the \n\\shape italic\nlocal \n\\shape default\nid of the edge,\n local with respect to the edge definition.\n The local ids must begin at 1 and defined sequentially.\n\\end_layout\n\n\\begin_layout Standard\nThe edge elements are defined by using a tag that depends on the type of the edge element.\n The following edge elements are currently supported.\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\nline2\n\\shape default\n 2-node line element \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\nline3\n\\shape default\n 3-node line element \n\\end_layout\n\n\\begin_layout Standard\nEdges cannot overlap element boundaries and must be a valid edges of elements.\n \n\\end_layout\n\n\\begin_layout Subsection\nSurface Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Surface-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nSurface \n\\shape default\nsection allows users to define surfaces.\n These surfaces can then later be used to define the boundary conditions and contact definitions.\n A surface definition is followed by a list of surface elements,\n following the format described below.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Surface name=\"named_surface\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <quad4 id=\"1\">1,2,3,4</quad4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <...>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Surface>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nSurface \n\\shape default\ntakes one required attribute,\n namely the \n\\shape italic\nname\n\\shape default\n.\n This attribute sets the name of the surface.\n This name will be used later to refer back to this surface.\n\\end_layout\n\n\\begin_layout Standard\nThe following surface elements are available:\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\nquad4\n\\shape default\n 4-node quadrilateral element \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\nquad8\n\\shape default\n 8-node serendipity quadrilateral element \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\ntri3\n\\shape default\n 3-node triangular element \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\ntri6\n\\shape default\n 6-node quadratic triangular element \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\ntri7\n\\shape default\n 7-node quadratic triangular element \n\\end_layout\n\n\\begin_layout Standard\nThe value for the surface element is the nodal connectivity:\n\\end_layout\n\n\\begin_layout LyX-Code\n<quad4 id=\"n\">n1,n2,n3,n4</quad4>\n\\end_layout\n\n\\begin_layout LyX-Code\n<tri3 id=\"n\">n1,n2,n3</tri3>\n\\end_layout\n\n\\begin_layout Standard\nSurface elements cannot overlap element boundaries.\n That is,\n the surface element must belong to a specific element.\n Surface elements do not contribute to the total number of elements in the mesh.\n They are also not to be confused with shell elements.\n\\end_layout\n\n\\begin_layout Subsection\nElementSet Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:ElementSet-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nElementSet \n\\shape default\nsection can be used to define an element set.\n Element sets can be used to output data for only a subset of elements.\n An element set is defined through the \n\\shape italic\nElementSet \n\\shape default\ntag,\n which takes one attribute,\n namely \n\\shape italic\nname\n\\shape default\n that specifies the name of the element set.\n The value of the tag is the list of element IDs.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<ElementSet name=\"set01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  1,\n 2,\n 3,\n 4\n\\end_layout\n\n\\begin_layout LyX-Code\n  5,\n 6,\n 7,\n 8\n\\end_layout\n\n\\begin_layout LyX-Code\n</ElementSet>\n\\end_layout\n\n\\begin_layout Subsection\nDiscreteSet Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:DiscreteSet-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nDiscreteSet \n\\shape default\nsection defines sets of discrete elements.\n These sets can then be used to define e.g.\n springs.\n\\end_layout\n\n\\begin_layout LyX-Code\n<DiscreteSet name=\"springs\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <delem>1,2</delem>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <delem>3,4</delem>\n\\end_layout\n\n\\begin_layout LyX-Code\n</DiscreteSet>\n\\end_layout\n\n\\begin_layout Subsection\nSurfacePair Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SurfacePair-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nSurfacePair \n\\shape default\nsection defines a pair of surfaces that can be used to define surface-to-surface interactions (e.g.\n contact).\n\\end_layout\n\n\\begin_layout LyX-Code\n<SurfacePair name=\"contact1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <primary>surface1</primary>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <secondary>surface2</secondary>\n\\end_layout\n\n\\begin_layout LyX-Code\n</SurfacePair>\n\\end_layout\n\n\\begin_layout Standard\nThe surfaces (i.e.\n \n\\shape italic\nsurface1 \n\\shape default\nand \n\\shape italic\nsurface2\n\\shape default\n) are surfaces defined in \n\\shape italic\nSurface \n\\shape default\nsections.\n Because surfaces must already be defined before they can be referenced in the \n\\shape italic\nSurfacePair \n\\shape default\nsection,\n the \n\\shape italic\nSurface \n\\shape default\nsections must be defined before the \n\\shape italic\nSurfacePair \n\\shape default\nsection.\n\\end_layout\n\n\\begin_layout Subsection\nPartList\n\\end_layout\n\n\\begin_layout Standard\nA \n\\emph on\npart \n\\emph default\nis an element set that is defined via the \n\\emph on\nElements\n\\emph default\n tag.\n Parts can be grouped together in part lists.\n A part list is defined via the \n\\emph on\nPartList \n\\emph default\ntag.\n The value of this tag is a list of the names of the parts that belong to the list.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<PartList name=\"allParts\">part1,part2</PartList>\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\emph on\npart1 \n\\emph default\nand \n\\emph on\npart2 \n\\emph default\nare parts defined via Elements sections.\n Because part lists depend on the Elements sections,\n the PartList tag must be defined after all Elements sections have been defined.\n \n\\end_layout\n\n\\begin_layout Standard\nPart lists can be used,\n for instance,\n in body loads if the body load is to be applied to multiple parts.\n \n\\end_layout\n\n\\begin_layout Subsection\nImplicit Partitions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Implicit-Partitions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the explicit mesh partitions that can be defined in the Mesh section,\n as detailed above,\n FEBio also supports \n\\emph on\nimplicit partitions\n\\emph default\n.\n This allows you to assign a different type of partition to a model component than the target partition.\n For instance,\n if a boundary condition requires a node set,\n using implicit partitioning,\n you can assign a surface or an element set.\n \n\\end_layout\n\n\\begin_layout Standard\nConsider the following example.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"zero displacement\" node_set=\"set1\"/>\n\\end_layout\n\n\\begin_layout Standard\nFEBio expects a node set with the name set1 defined in the Mesh section.\n However,\n if set1 is a surface,\n then this surface can be assigned to the boundary condition using the following syntax.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"zero displacement\" node_set=\"@surface:set1\"/>\n\\end_layout\n\n\\begin_layout Standard\nThe presence of the specifier @surface informs FEBio that set1 is a surface and the node set needs to be extracted from the surface definition.\n \n\\end_layout\n\n\\begin_layout Standard\nMesh partitions can only be assigned this way if the target partition is a subset of the source partition.\n For instance,\n it is not possible to assign a node set to a surface partition.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following specifiers are available.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecifier\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTarget partition\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n@surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReferences a surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodeset\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n@edge\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReferences an edge\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodeset\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n@elem_set\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReferences an element set (either via Elements or ElementSet)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodeset\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n@part_list\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReferences a part list\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodeset,\n surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote that the @part_list is the only specifier that can be used to assign a part list to a surface.\n This can be useful for defining contact surfaces where the surface might have a complex shape and is difficult to extract manually.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMeshDomains Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:MeshDomains-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nMeshDomains \n\\emph default\nsection assigns various physical attributes to the element sets of the mesh.\n For each of the \n\\emph on\nElements \n\\emph default\nsections in the \n\\emph on\nMesh \n\\emph default\nsection,\n define a \n\\emph on\nSolidDomain\n\\emph default\n,\n\n\\emph on\n ShellDomain\n\\emph default\n,\n or \n\\emph on\nBeamDomain \n\\emph default\nsection,\n depending on whether the elements are solid,\n shells,\n or beams (or trusses).\n Each domain also requires a material name that defines the material properties of the domain.\n \n\\end_layout\n\n\\begin_layout Subsection\nSolidDomain Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SolidDomain-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUse the \n\\emph on\nSolidDomain \n\\emph default\nsection to define a solid domain.\n This section takes the following parameters:\n \n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\nname \n\\series default\nThe name of the element set (defined via an \n\\emph on\nElements \n\\emph default\nsection).\n This element set must only contain solid elements.\n \n\\end_layout\n\n\\begin_layout Description\nmat The name of the material that will be assigned to this domain\n\\end_layout\n\n\\begin_layout Description\ntype (optional) the specific formulation used for this domain.\n \n\\end_layout\n\n\\begin_layout Standard\nThe optional \n\\series bold\ntype \n\\series default\nattribute can be added to specify a specific solid formulation.\n If omitted,\n FEBio will determine the proper domain type by inspecting the material and element types used.\n However,\n users can explicitly set the type attribute.\n The following solid domain types are defined.\n If the domain type is specified explicitly,\n the user must make sure that the assigned material is compatible with the domain type.\n (For instance,\n you cannot assign a biphasic material to an elastic-solid.) \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"15\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstandard solid formulation (default for most materials) (1,2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthree-field-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA three-field formulation used for modeling (nearly) incompressible materials.\n (default for uncoupled materials) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid solid domain.\n (default for rigid materials.)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nudg-hex\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuniform deformation gradient hex formulation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsri-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFormulation that uses a selective reduced integration rule\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nremodeling-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFormulation used for elastic remodeling problems.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-mm-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFormulation used for multi-scale domains.\n (requires the \n\\begin_inset Quotes eld\n\\end_inset\n\nmicro-material\n\\begin_inset Quotes erd\n\\end_inset\n\n) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nut4-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUniform nodal strain tetrahedron.\n (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbiphasic-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for biphasic domains.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbiphasic-solute-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for biphasic-solute domains.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmultiphasic-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for multiphasic domains.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntriphasic-solid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for triphasic domains (deprecated,\n use multiphasic-solid instead).\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid-3D\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for fluid domains.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid-FSI-3D\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for fluid-FSI domains.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Standard\n1.\n For the elastic-solid domain,\n the elem_type attribute can be specified to further specialize the quadrature rule.\n The values of the rule depend on the specific element type.\n The tables below show the available integration rules for the different element types.\n The values marked with an asterisk (*) are the default.\n\\end_layout\n\n\\begin_layout Itemize\nFor the \n\\shape italic\nhex8 \n\\shape default\nelement,\n the following values are defined.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nhex8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEX8G8*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration using 2x2x2 integration points.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEX8G6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAlternative integration rule for bricks using 6 integration point\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Itemize\nFor the \n\\shape italic\nhex20 \n\\shape default\nelement,\n the following values are defined.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nhex20\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEX20G8*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration using 2x2x2 integration points.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Itemize\nFor the \n\\shape italic\ntet4 \n\\shape default\nelement,\n the following values are allowed.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntet4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET4G1*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using one integration point.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET4G4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using 4 integration points.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Itemize\nFor the \n\\shape italic\ntet10 \n\\shape default\nelement,\n the following integration rules are supported.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntet10\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET10G4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using 4 integration points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET10G8*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using 8 integration points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET10GL11\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGauss-Lobatto integration rule using 11 integration points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Lobatto integration rule differs from a regular Gauss integration rule in that it includes the vertices of the tetrahedral element.\n The \n\\shape italic\nLobatto11\n\\shape default\n integration rule uses the 10 tetrahedral nodes,\n plus one integration rule located at the center of the element.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Itemize\nFor the \n\\shape italic\ntet15\n\\shape default\n element,\n the following integration rules are defined.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntet15\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET15G8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using 8 integration points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET15G11\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using 11 integration points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET15G15*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration rule using 15 integration points\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Itemize\nFor the \n\\shape italic\npenta15 \n\\shape default\nelement,\n the following values are defined.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\npenta15\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPENTA15G8*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian integration using 2x2x2 integration points.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\n2.\n The \n\\begin_inset Quotes eld\n\\end_inset\n\nsolid-elastic\n\\begin_inset Quotes erd\n\\end_inset\n\n defines the parameters \n\\emph on\nsecant_stress\n\\emph default\n and \n\\emph on\nsecant_tangent\n\\emph default\n.\n When,\n specified,\n the formulation will use a secant approximation of the corresponding variable.\n When setting \n\\emph on\nsecant_stress\n\\emph default\n is set to 1,\n the stress is calculated from a secant approximation that uses the material's strain-energy density.\n Similarly,\n when \n\\emph on\nsecant_tangent \n\\emph default\nis specified,\n the material tangent is approximated using the PK2 stress.\n \n\\end_layout\n\n\\begin_layout Standard\n3.The ut4-solid is a special formulation for tetrahedral elements that uses a \n\\nospellcheck on\nnodally-averaged\n\\nospellcheck default\n integration rule,\n as proposed by Gee et al \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gee09\"\nliteral \"true\"\n\n\\end_inset\n\n.\n This formulation requires additional parameters.\n To override the default values,\n use the following alternative syntax:\n\\end_layout\n\n\\begin_layout LyX-Code\n   <SolidDomain name=\"Part1\" \n\\series bold\ntype=\"ut4-solid\"\n\\series default\n mat=\"material1\">\n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n  <alpha>0.05</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <\n\\nospellcheck on\niso\n\\nospellcheck default\n_stab>0</\n\\nospellcheck on\niso\n\\nospellcheck default\n_stab>\n\\end_layout\n\n\\begin_layout LyX-Code\n</SolidDomain>\n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\nThe \n\\shape italic\nalpha \n\\shape default\nparameter defines the amount of \n\\begin_inset Quotes eld\n\\end_inset\n\nblending\n\\begin_inset Quotes erd\n\\end_inset\n\n between the regular \n\\nospellcheck on\ntet-contribution\n\\nospellcheck default\n and the \n\\nospellcheck on\nnodally\n\\nospellcheck default\n integrated contribution.\n The value must be between 0 and 1,\n where 0 means no contribution from the regular tet and 1 means no contribution from the nodally averaged tet.\n The \n\\shape italic\niso_stab \n\\shape default\nparameter is a flag that chooses between two slightly different formulations of the nodally integrated tet.\n When set to 0,\n the stabilization is applied to the entire virtual work,\n whereas when set to 1 the stabilization is applied only to the isochoric part.\n See the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n for a detailed description of this formulation.\n\\end_layout\n\n\\begin_layout Subsection\nShellDomain Section\n\\end_layout\n\n\\begin_layout Standard\nUse the \n\\emph on\nShellDomain \n\\emph default\nsection to define a shell domain.\n This section takes two parameters:\n \n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\nname \n\\series default\nThe name of the element set (defined via an \n\\emph on\nElements \n\\emph default\nsection).\n This element set must only contain shell elements.\n \n\\end_layout\n\n\\begin_layout Description\nmat The name of the material that will be assigned to this domain\n\\end_layout\n\n\\begin_layout Description\ntype (optional) the specific shell formulation \n\\end_layout\n\n\\begin_layout Standard\nSimilar to solid domains,\n the particular shell formulation can be requested via the \n\\series bold\ntype\n\\series default\n attribute.\n If omitted,\n FEBio will determine the formulation from the material and shell element types.\n If specified explicitly,\n the user must ensure that the correct material is assigned to the domain.\n The following shell domain types are available.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-shell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstandard shell formulation (default for most materials) (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthree-field-shell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA three-field formulation used for modeling (nearly) incompressible materials.\n (default for uncoupled materials)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid-shell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid shell domain.\n (default for rigid materials.)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-shell-old\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\nold\n\\begin_inset Quotes erd\n\\end_inset\n\n shell formulation,\n used in FEBio version 2.0 and older.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-shell-eas\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEnhanced-Assumed-Strain shell;\n an alternative shell formulation for incompressible shells.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-shell-ans\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAssumed-Natural-Strain shell;\n an alternative shell formulation for incompressible shells.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbiphasic-shell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for biphasic shell domains.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbiphasic-solute-shell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for biphasic-solute shell domains.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmultiphasic-shell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe standard formulation for multiphasic shell domains.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe default shell thickness can be specified as a parameter of the ShellDomain section.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<ShellDomain name=\"skin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <shell_thickness>0.01</shell_thickness>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ShellDomain>\n\\end_layout\n\n\\begin_layout Standard\nIf the shell thickness parameter is not specified,\n the initial shell thickness is set to zero,\n and it is assumed that the actual shell thickness is specified in the MeshData section.\n \n\\end_layout\n\n\\begin_layout Subsection\nBeamDomain Section\n\\end_layout\n\n\\begin_layout Standard\nBeam and trusses can be defined with the \n\\emph on\nBeamDomain \n\\emph default\nsection.\n This tag takes the following attributes.\n \n\\end_layout\n\n\\begin_layout Description\nname This is the name of the part to which this domain will be assigned.\n The part is defined via a named \n\\emph on\nElements \n\\emph default\nsection.\n \n\\end_layout\n\n\\begin_layout Description\nmat The name of the material that will be assigned to this domain.\n \n\\end_layout\n\n\\begin_layout Description\ntype The specific formulation that will be used.\n \n\\end_layout\n\n\\begin_layout Standard\nThe particular beam formulation that can be assigned is listed below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear-truss\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA linear truss formulation.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic-truss\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAn extension of the linear-truss formulation that allows the use of standard FEBio materials (EXPERIMENTAL!)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear-beam\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA linear beam formulation.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMeshData Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:MeshData-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nMeshData \n\\shape default\nsection is where data is specified that can be mapped to the mesh.\n The data can then be applied to a parameter of the model,\n e.g.\n a material parameter,\n or a pressure load.\n \n\\end_layout\n\n\\begin_layout Standard\nTo define a mesh data section add one of the following tags.\n\\end_layout\n\n\\begin_layout Description\nNodeData Define data on a node set\n\\end_layout\n\n\\begin_layout Description\nSurfaceData Define data on a surface\n\\end_layout\n\n\\begin_layout Description\nElementData Define data on an element set\n\\end_layout\n\n\\begin_layout Standard\nThe following attributes can be added.\n\\end_layout\n\n\\begin_layout Description\nname All mesh data sections require a name,\n which can be specified with the \n\\emph on\nname \n\\emph default\nattribute.\n This name will be used by model parameters to reference the particular mesh data.\n \n\\end_layout\n\n\\begin_layout Standard\nData maps can be defined in two ways:\n 1) explicitly,\n where the value of each node (face,\n element) can be specified,\n or 2) implicitly,\n via data generators that define the values of each node (face,\n element) procedurally.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen mesh data maps are defined explicitly,\n the following attributes should be specified in addition to the name attribute.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\ndata_type \n\\series default\ndefines the type of data \n\\end_layout\n\n\\begin_layout Standard\nThe following table lists the supported data types.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndata type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscalar\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA single floating point value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvec2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA 2D vector defined as \n\\begin_inset Formula $x$\n\\end_inset\n\n,\n \n\\begin_inset Formula $y$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvec3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA 3D vector defined as \n\\begin_inset Formula $x$\n\\end_inset\n\n,\n \n\\begin_inset Formula $y$\n\\end_inset\n\n,\n \n\\begin_inset Formula $z$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmat3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA 3x3 tensor,\n row-major sorted\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmat3s\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA symmetric 3x3 tensor,\n defined as \n\\begin_inset Formula $xx$\n\\end_inset\n\n,\n \n\\begin_inset Formula $yy$\n\\end_inset\n\n,\n \n\\begin_inset Formula $zz,xy,yz,xz$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf the data type is not defined,\n the \n\\shape italic\nscalar \n\\shape default\ndata type is assumed.\n\\end_layout\n\n\\begin_layout Subsection\nData Generators\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:generator\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nData generators can be used to populate the values of a data map procedurally.\n To use a data generator,\n specify the \n\\emph on\ntype\n\\emph default\n attribute.\n If the \n\\emph on\ntype\n\\emph default\n attribute is defined,\n the child tags must be the parameters of the generator.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<MeshData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <NodeData name=\"E_map\" \n\\series bold\ntype=\"math\"\n\\series default\n>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <math>X*X + Y*Y + Z*Z</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <NodeData>\n\\end_layout\n\n\\begin_layout LyX-Code\n</MeshData>\n\\end_layout\n\n\\begin_layout Standard\nData generators that can be used for a specific mesh data are described in the sections below.\n \n\\end_layout\n\n\\begin_layout Subsection\nElementData\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:ElementData\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section defines data that can be mapped to the elements of an element set.\n The element set is specified via the \n\\emph on\nelem_set \n\\emph default\nattribute.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\nDefining\n\\begin_inset space ~\n\\end_inset\n\nmaps\n\\series default\n A map is defined by using the \n\\emph on\nname\n\\emph default\n attribute to give the map a name.\n This name can then be used in mapped parameters (see Appendix A).\n\\end_layout\n\n\\begin_layout LyX-Code\n<MeshData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ElementData \n\\series bold\nname=\"my_map\"\n\\series default\n elem_set=\"part1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <elem lid=\"1\">1.23</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <elem lid=\"2\">1.25</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </ElementData>\n\\end_layout\n\n\\begin_layout LyX-Code\n</MeshData>\n\\end_layout\n\n\\begin_layout Standard\nThe following data generators are available for element data sections.\n \n\\end_layout\n\n\\begin_layout Description\nsurface-to-surface_map The \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nsurface-to-surface map\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n generator defines a data field on the domain bounded by two surfaces.\n The data values are interpolated using a user-defined function between the surfaces.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<MeshData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ElementData name=\"Emap\" \n\\series bold\ngenerator=\"surface-to-surface map\"\n\\series default\n elem_set=\"Part1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <function type=\"math\">x*x+1</function>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <bottom_surface surface=\"surface1\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <top_surface surface=\"surface2\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ElementData>\n\\end_layout\n\n\\begin_layout LyX-Code\n</MeshData>\n\\end_layout\n\n\\begin_layout Description\n\n\\series bold\nspecial\n\\begin_inset space ~\n\\end_inset\n\ngenerators\n\\series default\n A few special data generators do not generate maps,\n but instead modify model data in a more direct way.\n The following table lists the currently supported special generators.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nProperty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nData type/format\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify a local fiber direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvec3/const\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmat_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify local element material axes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvec3/const\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell thickness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the shell element thickness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfloat/shape\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to assign shell thicknesses to an element set,\n use the following syntax.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<MeshData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ElementData \n\\series bold\ntype=\"shell thickness\"\n\\series default\n elem_set=\"part1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <elem lid=\"1\">1.0,\n 1.0,\n 1.0,\n 1.0</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <elem lid=\"2\">1.0,\n 1.0,\n 1.0,\n 1.0</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </ElementData>\n\\end_layout\n\n\\begin_layout LyX-Code\n</MeshData>\n\\end_layout\n\n\\begin_layout Subsection\nSurfaceData\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:SurfaceData\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section allows users to define data that can be mapped to the surfaces of certain boundary conditions and loads.\n This section only defines user-defined data maps,\n i.e.\n the \n\\shape italic\nname\n\\shape default\n,\n \n\\shape italic\ndata_type \n\\shape default\nand \n\\shape italic\ndata_format \n\\shape default\nattributes must be used.\n The surface is defined via the \n\\shape italic\nsurface \n\\shape default\nattribute.\n\\end_layout\n\n\\begin_layout LyX-Code\n<SurfaceData name=\"values\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <face lid=\"1\">1.23</face>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <face lid=\"2\">3.45</face>\n\\end_layout\n\n\\begin_layout LyX-Code\n</SurfaceData>\n\\end_layout\n\n\\begin_layout Standard\nThe surface definition is defined in the \n\\shape italic\nMesh \n\\shape default\nsection.\n Note that the ids refer to the local ids of the surface facets.\n By default,\n the \n\\shape italic\nscalar \n\\shape default\ndata type and \n\\shape italic\nconst \n\\shape default\ndata format are assumed so only one value per facet is expected.\n Other data types and formats can be specified with the \n\\shape italic\ndata_type \n\\shape default\nand \n\\shape italic\ndata_format \n\\shape default\nattributes.\n\\end_layout\n\n\\begin_layout LyX-Code\n<SurfaceData name=\"data\" data_type=\"vec3\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <face lid=\"1\">0.0,\n 1.0,\n 0.0</face>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <face lid=\"2\">0.0,\n 2.0,\n 0.0</face>\n\\end_layout\n\n\\begin_layout LyX-Code\n</SurfaceData>\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the surface data may be described with a data generator.\n The data generator is specified via the \n\\emph on\ntype \n\\emph default\nattribute.\n The following data generators are available for surface data.\n \n\\end_layout\n\n\\begin_layout Description\nparabolic_map The \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nparabolic map\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n defines a scalar data field with a parabolic profile on a surface.\n The data is determined by solving a heat-type problem with a constant source and homogeneous boundary conditions on the edge of the surface.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<MeshData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <SurfaceData name=\"pressure\" \n\\series bold\ntype=\"parabolic map\"\n\\series default\n surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <value>1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <SurfaceData>\n\\end_layout\n\n\\begin_layout LyX-Code\n</MeshData>\n\\end_layout\n\n\\begin_layout Subsection\nEdgeData\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:EdgeData\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section allows users to define user-defined data maps that will be mapped to an edge defined in the Mesh section.\n\\end_layout\n\n\\begin_layout LyX-Code\n<EdgeData name=\"data\" edge=\"edge1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <edge lid=\"1\">1.0</edge>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <edge lid=\"2\">2.0</edge>\n\\end_layout\n\n\\begin_layout LyX-Code\n</EdgeData>\n\\end_layout\n\n\\begin_layout Standard\nNotice that the \n\\shape italic\nlocal \n\\shape default\nIDs are required here.\n\\end_layout\n\n\\begin_layout Subsection\nNodeData\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:NodeData\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section allows users to define data that will be mapped to node sets.\n\\end_layout\n\n\\begin_layout LyX-Code\n<NodeData name=\"data\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node lid=\"1\">1.0</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node lid=\"2\">2.0</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n</NodeData>\n\\end_layout\n\n\\begin_layout Standard\nFor node data the \n\\shape italic\ndata_format \n\\shape default\nattribute,\n if specified,\n will be ignored.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMesh Adaptor Section\n\\end_layout\n\n\\begin_layout Standard\nThe FEBio Adaptive Mesh Refinement framework (FEAMR,\n pronounced “femur”) allows users to make modifications to the mesh during the analysis of the model.\n There are currently two mesh adaptor applications supported,\n namely mesh erosion,\n which removes elements from the mesh,\n and remeshing,\n which can be used to adaptively refine or coarsen the mesh based on the solution.\n The mesh adaptors are controlled via user-defined criteria that determine the conditions under which to modify the mesh.\n\\end_layout\n\n\\begin_layout Standard\nA mesh adaptor is applied at the end of a timestep,\n after the Newton-iterations and augmentations have converged.\n If the mesh adaptor modifies the mesh,\n then the timestep is solved again,\n using the new mesh.\n Several adaptors define the \n\\emph on\ncriterion\n\\emph default\n property,\n which evaluates the variable or metric that is used by the adaptor,\n and optionally,\n can be used to filter the elements to which the adaptor will be applied.\n How the criterion is used,\n depends on the adaptor.\n For instance,\n the erosion adaptor uses it to identify the elements to deactivate.\n Remeshing adaptors will use it to set the new element size.\n It is allowed to use several mesh adaptors in a single model,\n but keep in mind that the order in which the mesh adaptors are listed in the input file is important.\n For instance,\n if two mesh adaptors are defined,\n the second adaptor will operate on the mesh created by the first adaptor.\n Mesh adaptors used for adaptive refinement will also map data between the old mesh and the new mesh.\n User data,\n defined in the MeshData section,\n is always mapped between meshes.\n If the model is history-dependent,\n it might be important to map additional data.\n For instance,\n a visco-elastic material needs to map stress-history variables.\n By default,\n mapping data (except for user-data) is not done unless the user sets a specific flag (map_data).\n New mesh adaptors and adaptor criteria can be defined via the plugin framework.\n \n\\end_layout\n\n\\begin_layout Subsection\nThe MeshAdaptor Section\n\\end_layout\n\n\\begin_layout Standard\nMesh adaptors are added to the FEBio Input file (version 3.0 and above) in a new section,\n called \n\\series bold\nMeshAdaptor\n\\series default\n.\n This section must appear after the Mesh and MeshDomains sections.\n The MeshAdaptor section may also be defined in a \n\\emph on\nstep\n\\emph default\n section in order to apply the adaptor only during that step.\n Inside the MeshAdaptor section,\n each mesh adaptor is defined via the \n\\series bold\nmesh_adaptor\n\\series default\n tag.\n This tag allows the following attributes:\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nattribute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nset the type of the mesh adaptor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nelem_set\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element set to apply the adaptor to (1) (Optional) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\series bold\nelem_set \n\\series default\nattribute is optional,\n but can be used to apply the adaptor to only a part of the mesh.\n This is useful to limit the effect of an adaptor to a part of the mesh.\n The element set must reference a domain (i.e.\n defined via Elements) of a mesh,\n or a set of domains.\n It cannot be an element set defined through the ElementSet tag.\n (Note that not all mesh adaptors support this attribute.) If omitted,\n the adaptor is applied to the entire mesh.\n \n\\end_layout\n\n\\begin_layout Standard\nMany mesh adapators require a mesh adaptor criterion.\n The criterion defines the metric that will assign a value to each element.\n This value can then in turn be used by the adaptor to decide what to do with a particular element.\n Adaptor criteria are defined in section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mesh-Adaptor-Criteria\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nThe erosion adaptor\n\\end_layout\n\n\\begin_layout Standard\nThe erosion adaptor can be used to deactivate elements in a mesh,\n based on a user-defined criterion.\n It defines the following properties:\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmax_iters\n\\series default\n:\n Set the max number of adaption iterations,\n for each time step.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nremove_islands\n\\series default\n:\n If true,\n removes small element sets that have become disconnected from the main mesh.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmax_elems\n\\series default\n:\n Set the max number of elements to erode,\n per adaptation.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nsort\n\\series default\n:\n sort the element values returned by the criterion.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ncriterion\n\\series default\n:\n Set the criterion that determines the element metric to evaluate and which elements to erode.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nerode_surfaces\n\\series default\n:\n Sets the policy of handling surface facets that attach to eroded elements.\n The value can be set to one of the following values:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Itemize\n\n\\series bold\nno\n\\series default\n:\n don't erode surfaces (default)\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nyes\n\\series default\n:\n erode surfaces if the underlying element is eroded.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ngrow\n\\series default\n:\n add the newly exposed facets of eroded elements to the surface.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nreconstruct\n\\series default\n:\n reconstruct surface.\n This only works if the surface is defined via a part.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n \n\\end_layout\n\n\\begin_layout Standard\nIn this example,\n elements that have an (integration point averaged) effective stress larger than 0.3,\n are eroded.\n Only three elements are eroded per adaptation cycle.\n\\end_layout\n\n\\begin_layout LyX-Code\n<mesh_adaptor type=\"erosion\"> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <max_iters>1</max_iters>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max_elems>3</max_elems>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <sort>1</sort>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <criterion type=\"min-max filter\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <min>0.3</min>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <clamp>0</clamp>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <data type=\"stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </criterion>\n\\end_layout\n\n\\begin_layout LyX-Code\n</mesh_adaptor> \n\\end_layout\n\n\\begin_layout Subsubsection\nThe mmg_remesh adaptor\n\\end_layout\n\n\\begin_layout Standard\nThe mmg_remesh adaptor can be used for adaptive mesh refinement of linear tetrahedral meshes.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmax_iters\n\\series default\n:\n The maximum number of adaption iterations per time step.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmax_elements\n\\series default\n:\n The maximum number of elements.\n If the number of elements of the mesh is larger,\n the adaptor is not applied.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmin_element_size\n\\series default\n:\n Set the desired minimal element size.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nhausdorff\n\\series default\n:\n The Hausdorff distance.\n This is used to preserve curved sections.\n (default = 0.01).\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ngradation\n\\series default\n:\n This parameter sets the spatial gradient of element size.\n (default = 1.3).\n Value should be larger than 1.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmesh_coarsen\n\\series default\n:\n Allow mesh-coarsening \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nnormalize_data\n\\series default\n:\n normalize the element values returned by the criterion between 0 and 1.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nrelative_size\n\\series default\n:\n Sets whether the size function is to be interpreted as a relative size function (i.e.\n a scale function to the current element size),\n or an absolute element size metric.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ncriterion\n\\series default\n:\n The metric used to assign values to elements.\n (See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mesh-Adaptor-Criteria\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nsize_function\n\\series default\n:\n set the mesh size function.\n This function is applied to the (optionally normalized) element values returned by the criterion.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<mesh_adaptor type=\"mmg_remesh\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max_iters>1</max_iters>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <criterion type=\"stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative_size>1</relative_size>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <normalize_data>0</normalize_data>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mesh_coarsen>0</mesh_coarsen>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <size_function type=\"step\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <x0>10</x0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <left_val>1</left_val>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <right_val>0.5</right_val>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </size_function>\n\\end_layout\n\n\\begin_layout LyX-Code\n</mesh_adaptor> \n\\end_layout\n\n\\begin_layout Subsubsection\nhex_refined2d\n\\end_layout\n\n\\begin_layout Standard\nThe hex_refined2d adaptor refines elements in the XY plane.\n (It assumes that the mesh consists of a single layer of hex elements,\n aligned in the XY coordinate plane.) Elements are split by splitting each edge into 2.\n This may result in a non-conforming mesh,\n where an element node in the refined mesh lies on an edge in the parent mesh.\n Such \n\\emph on\nhanging nodes \n\\emph default\nare constrained using linear constraints and are forced to remain in the same relative position as the parent edge.\n This ensures that the refined mesh remains \n\\begin_inset Quotes eld\n\\end_inset\n\nwatertight\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following parameters are available.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_elem_refine\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe max number of elements to refine per refinement step.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncriterion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe adaption criterion to use\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElements with criterion values above this threshold will be refined.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\ntet_refine\n\\end_layout\n\n\\begin_layout Standard\nApplies a uniform refinement of a tetrahedral mesh.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nhex_refine\n\\end_layout\n\n\\begin_layout Standard\nThe hex_refined adaptor refines elements by splitting each edge into 2.\n This may result in a non-conforming mesh,\n where an element node in the refined mesh lies on an edge in the parent mesh.\n Such \n\\emph on\nhanging nodes \n\\emph default\nare constrained using linear constraints and are forced to remain in the same relative position as the parent edge.\n This ensures that the refined mesh remains \n\\begin_inset Quotes eld\n\\end_inset\n\nwatertight\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following parameters are available.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_elem_refine\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe max number of elements to refine per refinement step.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncriterion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe adaption criterion to use\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElements with criterion values above this threshold will be refined.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nMesh Adaptor Criteria\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mesh-Adaptor-Criteria\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe purpose of the mesh adaptor criteria is to assign values to elements.\n Some adaptors also act as filters that limit the effect of the adaptor to certain elements.\n These values will then be used by the mesh adaptor to modify the mesh.\n How these values are used depends on the adaptor.\n For instance,\n the erosion uses it to decide which elements to erode.\n The mesh refinement adaptors use it to define the size function.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following criteria are currently available.\n A detailed description of these adaptor criteria is given below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_variable\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEvaluates average of element’s nodal values of a model primary variable.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement_selection\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the element selection to which the adaptor should be applied (only for erosion)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEvaluate the (integration point average) effective stress of each element.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndamage\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEvaluate the (integration point average) damage value.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative error\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEvaluate a relative error measure.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspring force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEvaluate the axial force of the spring.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspring stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEvaluate the stretch of a spring.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmath\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the element value based on a mathematical expression.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmin-max filter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSelects elements whose value falls within a min-max range.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSelect elements that are in contact.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nmax_variable\n\\end_layout\n\n\\begin_layout Standard\nThe max_variable criterion evaluates elements based on a primary variable’s degrees of freedom.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ndof\n\\series default\n:\n The degree of freedom that defines the variable to inspect.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nelement_selection\n\\end_layout\n\n\\begin_layout Standard\nSets the selected elements directly.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nelement_list:\n\n\\series default\n a comma-separated list of element indices.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nmath\n\\end_layout\n\n\\begin_layout Standard\nSets the scale factor for each element.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nscale\n\\series default\n:\n the scale factor to use.\n This can be a constant,\n or a mathematical expression of position (use X,\n Y,\n Z for the nodal coordinates),\n or a scalar map defined in the MeshData section.\n\\end_layout\n\n\\begin_layout Standard\nExample:\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<mesh_adaptor type=\"mmg_remesh\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max_iters>1</max_iters>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <criterion type=\"math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <math>1*(1 - (X^2+Y^2)) + (X^2+Y^2)*(0.5*(Z-1)^2 - Z*(Z - 2))</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </criterion>\n\\end_layout\n\n\\begin_layout LyX-Code\n</mesh_adaptor> \n\\end_layout\n\n\\begin_layout Subsubsection\nstress\n\\end_layout\n\n\\begin_layout Standard\nThis criterion selects elements that exceed a maximum stress value.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmetric\n\\series default\n:\n The stress measure to use:\n 0=effective stress,\n 1=max principal stress.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nmax_damage\n\\end_layout\n\n\\begin_layout Standard\nThis criterion evaluates the maximum damage value for each element.\n This can only be used with materials that define the damage property.\n\\end_layout\n\n\\begin_layout Subsubsection\nrelative error\n\\end_layout\n\n\\begin_layout Standard\nThis criterion evaluates a relative error metric for each element.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nerror:\n\n\\series default\n If nonzero,\n this criterion will evaluate a relative size metric for each element.\n Otherwise,\n the actual relative error is evaluated.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ndata:\n\n\\series default\n Specify the metric to evaluate the element values.\n Any of the other criteria defined above can be used.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nmin-max filter\n\\end_layout\n\n\\begin_layout Standard\nThis criterion evaluates a metric for each element,\n but then filters out the elements that have values within a min-max range.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmin\n\\series default\n:\n set the minimum value of the filter \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmax\n\\series default\n:\n set the maximum value of the filter \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nclamp\n\\series default\n:\n If true,\n the element values will be clamped to the range.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ndata\n\\series default\n:\n Specify the metric to evaluate the element values.\n Any of the other criteria defined above can be used.\n \n\\end_layout\n\n\\begin_layout Subsubsection\ncontact gap\n\\end_layout\n\n\\begin_layout Standard\nThis criterion evaluates the contact gap for all elements that are in contact.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\ngap\n\\series default\n:\n contact gap threshold value.\n\\end_layout\n\n\\begin_layout Subsection\nLimitations\n\\end_layout\n\n\\begin_layout Standard\nPlease take into account the following limitations of the FEAMR framework.\n \n\\end_layout\n\n\\begin_layout Enumerate\nAlthough contact can be used with the FEAMR framework,\n currently no contact data is mapped from the old mesh to the new mesh.\n It is advised to only use penalty-based contact interfaces.\n \n\\end_layout\n\n\\begin_layout Enumerate\nIt is not advised to erode elements that are in contact,\n since this may not produce the correct result.\n If you want to use erosion with contact,\n make sure to set the \n\\emph on\nerode_surfaces \n\\emph default\nto an appropriate value.\n \n\\end_layout\n\n\\begin_layout Enumerate\nMapping history-data is supported (if the proper flag is set),\n but can be very time consuming since FEBio maps all material point data between meshes.\n\\end_layout\n\n\\begin_layout Enumerate\nThe FEAMR framework does not work with the auto time-stepper.\n It is advised to use fixed time stepping when using mesh adaptation.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nInitial Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Initial-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nInitial\n\\shape default\n section defines all the initial conditions that may be applied to the analysis.\n An initial condition allows users to set the initial values of solution variables (e.g.\n velocity) as well as initialize the values of some special components.\n \n\\end_layout\n\n\\begin_layout Standard\nAn initial condition is defined via the \n\\shape italic\nic \n\\shape default\nelement and requires the \n\\shape italic\ntype \n\\shape default\nattribute and \n\\shape italic\nnode_set \n\\shape default\nattribute.\n The \n\\emph on\ntype\n\\emph default\n attribute defines the particular initial condition that will be applied.\n The \n\\shape italic\nnode_set \n\\shape default\nattribute defines the set of nodes that this initial condition affects.\n This node set must be defined in the Mesh section of the input file.\n Using implicit partitions (see section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Implicit-Partitions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) surfaces,\n element sets,\n and part lists can also be assigned to the node_set attribute.\n\\end_layout\n\n\\begin_layout Standard\nAn optional name attribute can be defined to assign a unique name to the initial condition.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic name=\"ic01\" type=\"velocity\" node_set=\"set1\">\n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<value>1,0,0</value>\n\\end_layout\n\n\\end_deeper\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nThe init_dof Initial Condition\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ninit_dof \n\\emph default\ninitial condition can be used to initialize any degree of freedom of the model.\n It supports the following parameters.\n This feature is deprecated and it is better to use one of the specific initial conditions listed in the sections below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe degree of freedom identifier\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value for the corresponding nodal degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic name=\"ic01\" type=\"init_dof\" node_set=\"set1\">\n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<dof>vx</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n<value>1</value>\n\\end_layout\n\n\\end_deeper\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nThe displacement Initial Condition\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ndisplacement \n\\emph default\ninitial condition can be used to set the initial displacement for a solid-mechanics analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value for the initial displacement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIt should be noted that the initial displacement will be reflected in the plot file at time=0,\n but not the stress.\n However,\n internally,\n the stress at time=0 will be taken into account as the calculation is performed for subsequent times.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic type=\"displacement\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1.0,0.0,0.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nThe velocity Initial Condition\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nvelocity \n\\emph default\ninitial condition can be used to set the initial velocity for a dynamic solid-mechanics analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value for the initial velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic type=\"velocity\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1.0,0.0,0.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nThe shell velocity Initial Condition\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nshell velocity \n\\emph default\ninitial condition can be used to set the initial velocity for the for the back-nodes of shells in a dynamic solid-mechanics analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value for the initial velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic type=\"shell velocity\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1.0,0.0,0.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nThe Initial Fluid Velocity Initial Condition\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ninitial fluid velocity \n\\emph default\ninitial condition can be used to set the initial velocity for a fluid mechanics analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value for the initial fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic type=\"initial fluid velocity\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1.0,0.0,0.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nInitial Fluid Dilatation\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\ninitial fluid dilatation\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n initial condition can be used to set the initial value of the fluid dilatation in a fluid analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value for the initial fluid dilatation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ic type=\"initial fluid dilatation\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1e-8</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ic>\n\\end_layout\n\n\\begin_layout Subsection\nThe Prestrain Initial Condition\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prestrain-Initial-Condition\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe prestrain initial condition can be used to accomplish one of two things.\n It can be used to convert a forward analysis in an equivalent prestrain analysis.\n Or it can be used to fix the prestrain gradient.\n In either case the current geometry is used as the new reference geometry of the following analysis step.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ninit\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInitialize prestrain field\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (=true)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nreset\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMake current geometry the reference geometry of the following steps\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (=true)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following example shows how a forward analysis can be converted to a prestrain analysis.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<ic type=\"prestrain\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <init>1</init>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <reset>1</reset>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ic> \n\\end_layout\n\n\\begin_layout Standard\nNote that this in itself may not be sufficient to define the equivalent prestrain analysis.\n For instance,\n if the forward model applied prescribed displacements,\n then these boundary conditions must be replaced with fixed boundary conditions.\n\\end_layout\n\n\\begin_layout Standard\nIf the \n\\emph on\ninit\n\\emph default\n parameter is set to 0 (false),\n then the prestrain gradient will be fixated in the current geometry.\n This means that any remaining distortion will be applied to the prestrain correction factor before the current geometry is converted to the new reference geometry.\n \n\\end_layout\n\n\\begin_layout Subsection\nFluid Pressure Initial Condition\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Pressure-IC\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\ninitial fluid pressure\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n initial condition can be used in biphasic and multiphasic analyses,\n and in fluid,\n fluid-FSI,\n and fluid-solutes analyses.\n In all cases it sets the initial value of the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n.\n If the analysis includes solutes (multiphasic or fluid-solutes),\n it is the user's responsibility to evaluate \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n using eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n knowing the values of the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n and absolute temperature \n\\begin_inset Formula $T$\n\\end_inset\n\n specified in the Globals section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Globals-Section\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n and the values of initial effective solute concentrations \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n in the given multiphasic or fluid-solutes domain.\n\\end_layout\n\n\\begin_layout Standard\nIn biphasic and multiphasic analyses the value of \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is prescribed directly as a nodal degree of freedom.\n In a fluid,\n fluid-FSI,\n or fluid-solutes analysis,\n the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is converted internally to the corresponding initial value of the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n based on the constitutive models presented in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nInitial Shell Fluid Pressure\n\\end_layout\n\n\\begin_layout Standard\nSimilar to the \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Pressure-IC\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n but applied to the back nodes of shell elements.\n \n\\end_layout\n\n\\begin_layout Subsection\nInitial Concentration\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\n\"initial concentration\" \n\\emph default\ninitial condition sets the initial concentration values of solutes in a biphasic-solute or multiphasic analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe solute's degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value of the solute concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nInitial Shell Concentration\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\n\"initial shell concentration\" \n\\emph default\ninitial condition sets the initial concentration values of solutes in a biphasic-solute or multiphasic analysis on the back nodes of a shell element.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe solute's degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value of the solute concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nRigid Kinematics\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nrigid kinematics\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n initial condition sets the initial velocity of a rigid body from the following equation.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{v}_{0}=\\mathbf{V}+\\mathbf{\\omega}\\times\\left(\\mathbf{r_{0}}-\\mathbf{c}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is the linear velocity,\n \n\\begin_inset Formula $\\omega$\n\\end_inset\n\n is the angular velocity,\n \n\\begin_inset Formula $\\boldsymbol{\\mathbf{r_{0}}}$\n\\end_inset\n\nis the initial position of the rigid body,\n and \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n is a position vector indicating the center of rotation.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvelocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe velocity vector \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nangular_velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe angular velocity vector \n\\begin_inset Formula $\\omega$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncenter_of_rotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe center or rotation vector \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nBoundary Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Boundary-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nBoundary\n\\shape default\n section defines all fixed and prescribed boundary conditions that may be applied to the model.\n Individual boundary conditions are defined via the \n\\emph on\nbc \n\\emph default\ntag and the particular boundary condition is defined via the \n\\emph on\ntype \n\\emph default\nattribute.\n An optional \n\\emph on\nname \n\\emph default\nattribute can be added to uniquely name the boundary condition.\n \n\\end_layout\n\n\\begin_layout Standard\nFor example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc name=\"bc1\" type=\"zero displacement\" node_set=\"nodeset1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <x_dof>1</x_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <y_dof>1</y_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <z_dof>1</z_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nMost of these boundary conditions require the node set to which to apply the boundary condition to.\n That nodeset can be defined via the \n\\emph on\nnode_set \n\\emph default\nattribute.\n The value of this attribute should be set to the name of a nodeset defined in the Mesh section.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:NodeSet-Section\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on how to define node sets.\n Nodesets can also be defined via surfaces or element sets.\n To define a node set via a surface,\n use the following syntax.\n \n\\end_layout\n\n\\begin_layout LyX-Code\nnode_set=\"@surface:surface1\"\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\emph on\nsurface1 \n\\emph default\nis a surface defined in the \n\\emph on\nMesh\n\\emph default\n section.\n Similarly,\n defining a node set via an element set is done using \n\\emph on\n@elem_set:.\n\n\\emph default\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Implicit-Partitions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on how to implicit partitions.\n \n\\end_layout\n\n\\begin_layout Subsection\nZero Displacement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Zero-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo fix the nodal displacement degrees of freedom (dof) use the \n\\shape italic\nzero displacement\n\\shape default\n boundary condition.\n This element has two required attributes:\n the \n\\emph on\ntype \n\\emph default\nis set to \n\\begin_inset Quotes eld\n\\end_inset\n\nzero displacement\n\\begin_inset Quotes erd\n\\end_inset\n\n and the node set to which this boundary condition applies is defined via \n\\shape italic\nnode_set\n\\shape default\n.\n The optional \n\\emph on\nname \n\\emph default\nattribute can be used to give the boundary condition a name.\n \n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition has three parameters,\n one for each degree of freedom.\n Set the value of each parameter to 1 to constrain the corresponding dof.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"zero displacement\" node_set=\"nodeset1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <x_dof>1</x_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <y_dof>1</y_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <z_dof>1</z_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Subsection\nZero Shell Displacement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Zero-Shell-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo fix the nodal back-displacement degrees of freedom (dof) of a shell use the \n\\shape italic\nzero \n\\shape default\n\\emph on\nshell displacement\n\\emph default\n boundary condition.\n This element has two required attributes:\n the \n\\emph on\ntype \n\\emph default\nis set to \n\\begin_inset Quotes eld\n\\end_inset\n\nzero shell displacement\n\\begin_inset Quotes erd\n\\end_inset\n\n and the node set to which this boundary condition applies is defined via \n\\shape italic\nnode_set\n\\shape default\n.\n The optional \n\\emph on\nname \n\\emph default\nattribute can be used to give the boundary condition a name.\n \n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition has three parameters,\n one for each degree of freedom.\n Set the value of each parameter to 1 to constrain the corresponding dof.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"zero shell displacement\" node_set=\"nodeset1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <sx_dof>1</sx_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <sy_dof>1</sy_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <sz_dof>1</sz_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition can only be applied to nodes that belong to shells.\n \n\\end_layout\n\n\\begin_layout Subsection\nZero Fluid Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Zero-Fluid-Pressure\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo fix the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n in a biphasic or multiphasic analysis,\n use the \n\\emph on\nzero fluid pressure\n\\emph default\n boundary condition.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc node_set=\"set01\" type=\"zero fluid pressure\"/>\n\\end_layout\n\n\\begin_layout Subsection\nZero Concentration\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Zero-Concentration\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo fix an effective solute concentration \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n degree of freedom (c_dof) in a multiphasic or fluid-solutes analysis,\n use the \n\\emph on\nzero concentration\n\\emph default\n boundary conditions.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc node_set=\"set01\" type=\"zero concentration\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c_dof>c1</c_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Subsection\nZero Fluid Dilatation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Zero-Dilatation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo fix the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n in a fluid or fluid-FSI or fluid-solutes analysis,\n use the \n\\emph on\nzero fluid dilatation\n\\emph default\n boundary condition.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc node_set=\"set01\" type=\"zero fluid dilatation\"/>\n\\end_layout\n\n\\begin_layout Standard\nZero fluid dilatation is the same as setting the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n to zero in fluid analyses.\n If you want to set the actual fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n to some value (such as zero) in a fluid analysis,\n use the \n\\emph on\nfluid pressure\n\\emph default\n boundary condition (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Pressure-BC\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Displacement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe prescribed displacement boundary condition can be used to prescribe the value of the nodal displacement degrees of freedom.\n This boundary condition takes the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe displacement dof to prescribe,\n x,\n y,\n or z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag that defines whether the value is absolute or relative (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set to 1,\n then the value is relative with respect to the current position of the node at the time the boundary condition becomes active.\n This only has an effect in multi-step analyses.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed displacement\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Standard\nAlthough the \n\\shape italic\nprescribe displacement \n\\shape default\nelement with a value of zero can also be used to fix a certain nodal degree of freedom,\n the user should use the \n\\emph on\nzero displacement\n\\emph default\n boundary condition whenever possible,\n since this option causes the equation corresponding to the constrained degree of freedom to be removed from the linear system of equations.\n This results in fewer equations that need to be solved for and thus reduces the run time of the FE analysis.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Shell Displacement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Shell-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprescribed shell displacement\n\\emph default\n boundary condition can be used to prescribe the value of the nodal back-displacement degrees of freedom of a shell.\n This boundary condition takes the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe displacement dof to prescribe,\n sx,\n sy,\n or sz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag that defines whether the value is absolute or relative (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set to 1,\n then the value is relative with respect to the current position of the node at the time the boundary condition becomes active.\n This only has an effect in multi-step analyses.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed shell displacement\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>sx</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Standard\nAlthough the \n\\shape italic\nprescribe shell displacement \n\\shape default\nelement with a value of zero can also be used to fix a certain nodal degree of freedom,\n the user should use the \n\\emph on\nzero shell displacement\n\\emph default\n boundary condition whenever possible,\n since this option causes the equation corresponding to the constrained degree of freedom to be removed from the linear system of equations.\n This results in fewer equations that need to be solved for and thus reduces the run time of the FE analysis.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Fluid Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Fluid-Pressure\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprescribed fluid pressure\n\\emph default\n boundary condition can be used to prescribe the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n on the boundary of a biphasic or multiphasic material.\n This boundary condition takes the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag that defines whether the value is absolute or relative (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set to 1,\n then the value is relative with respect to the current position of the node at the time the boundary condition becomes active.\n This only has an effect in multi-step analyses.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed fluid pressure\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Standard\nAlthough the \n\\shape italic\nprescribed \n\\shape default\n\\emph on\nfluid pressure\n\\shape italic\n\\emph default\n \n\\shape default\nelement with a value of zero can also be used to fix the effective fluid pressure,\n the user should use the \n\\emph on\nzero fluid pressure\n\\emph default\n boundary condition whenever possible,\n since this option causes the equation corresponding to that degree of freedom to be removed from the linear system of equations.\n This results in fewer equations that need to be solved for and thus reduces the run time of the FE analysis.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Concentration\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Concentration\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprescribed concentration\n\\emph default\n boundary condition can be used to prescribe the value of the effective solute concentration \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n in a multiphasic or fluid-solutes analysis.\n This boundary condition takes the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe solute concentration dof to prescribe,\n c1,\n c2,\n etc.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag that defines whether the value is absolute or relative (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set to 1,\n then the value is relative with respect to the current effective solute concentration at the time the boundary condition becomes active.\n This only has an effect in multi-step analyses.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed concentration\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>c1</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Standard\nAlthough the \n\\shape italic\nprescribed concentration \n\\shape default\nelement with a value of zero can also be used to fix that degree of freedom,\n the user should use the \n\\emph on\nzero concentration\n\\emph default\n boundary condition whenever possible,\n since this option causes the equation corresponding to the constrained degree of freedom to be removed from the linear system of equations.\n This results in fewer equations that need to be solved for and thus reduces the run time of the FE analysis.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Deformation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Deformation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprescribed deformation \n\\emph default\nboundary condition can be used to prescribe the displacement of a node using the following equation.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{u}=s\\left(\\boldsymbol{F}-\\boldsymbol{I}\\right)\\boldsymbol{X}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\boldsymbol{u}$\n\\end_inset\n\n is the displacement vector,\n \n\\begin_inset Formula $\\boldsymbol{X}$\n\\end_inset\n\n the reference position vector,\n \n\\begin_inset Formula $\\boldsymbol{F}$\n\\end_inset\n\na \n\\begin_inset Quotes eld\n\\end_inset\n\ndeformation gradient\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\boldsymbol{I}$\n\\end_inset\n\n the identity tensor,\n and \n\\begin_inset Formula $s$\n\\end_inset\n\n a scale factor.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following parameters are defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nscale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe scale factor \n\\begin_inset Formula $s$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nF\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\boldsymbol{I}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed deformation\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">0.1</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <F>2,0,0,\n 0,2,0,\n 0,0.25,0</F>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Fluid Dilatation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Fluid-Dilatation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprescribed fluid dilatation\n\\emph default\n boundary condition can be used to prescribe the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n on the boundary of a fluid,\n fluid-FSI or fluid-solutes material.\n This boundary condition takes the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag that defines whether the value is absolute or relative (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set to 1,\n then the value is relative with respect to the current position of the node at the time the boundary condition becomes active.\n This only has an effect in multi-step analyses.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed fluid dilatation\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">-1e-8</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Standard\nAlthough the \n\\shape italic\nprescribed \n\\shape default\n\\emph on\nfluid dilatation\n\\shape italic\n\\emph default\n \n\\shape default\nelement with a value of zero can also be used to fix the effective fluid pressure,\n the user should use the \n\\emph on\nzero fluid dilatation\n\\emph default\n boundary condition whenever possible,\n since this option causes the equation corresponding to that degree of freedom to be removed from the linear system of equations.\n This results in fewer equations that need to be solved for and thus reduces the run time of the FE analysis.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Fluid Velocity\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nprescribed fluid velocity\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n boundary condition can be used to to prescribe the fluid velocity on the boundary of a fluid domain.\n This boundary condition takes the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe degree of freedom to prescribe\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed values\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIf set,\n the value is relative to the value at the last analysis step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Shell Fluid Pressure\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nprescribed shell fluid pressure\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n boundary condition can be used to prescribe the fluid pressure on the back nodes of a shell element in a biphasic or multiphasic analysis.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed values\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIf set,\n the value is relative to the value at the last analysis step\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nNormal Displacement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Normal-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nnormal displacement \n\\emph default\nboundary condition prescribes the displacement of a node along the normal vector to the surface on which the node lies.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{u}=s\\boldsymbol{N}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\boldsymbol{u}$\n\\end_inset\n\n is the displacement vector,\n \n\\begin_inset Formula $\\boldsymbol{N}$\n\\end_inset\n\n is the normal to the surface in the reference configuration,\n and \n\\begin_inset Formula $s$\n\\end_inset\n\n is a scale factor.\n \n\\end_layout\n\n\\begin_layout Standard\nNote that this boundary condition requires a \n\\emph on\nsurface \n\\emph default\nattribute,\n instead of the \n\\emph on\nnode_set \n\\emph default\nattribute.\n The following parameters are defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nscale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe scale factor \n\\begin_inset Formula $s$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nsurface_hint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag indicating the type of surface (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nsurface_hint \n\\emph default\nparameter indicates the shape of the surface.\n Set this value to 1 if the surface is spherical.\n This will calculate a more accurate normal vector.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"normal displacement\" surface=\"surf1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">0.1</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <surface_hint>1</surface_hint>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Subsection\nRigid Deformation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Deformation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid deformation \n\\emph default\nboundary condition can be used to prescribe the motion of a nodeset via a rigid transformation.\n The displacement is given by the following equation.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\boldsymbol{u}=\\boldsymbol{Q}\\left(\\boldsymbol{X}-\\boldsymbol{r}_{0}\\right)+\\boldsymbol{r}_{t}-\\boldsymbol{X}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\boldsymbol{u}$\n\\end_inset\n\n is the displacement vector,\n \n\\begin_inset Formula $\\boldsymbol{Q}$\n\\end_inset\n\n is a rotation tensor,\n \n\\begin_inset Formula $\\boldsymbol{X}$\n\\end_inset\n\n is the initial position of the node,\n and \n\\begin_inset Formula $\\boldsymbol{r}_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{r}_{t}$\n\\end_inset\n\n are the reference and current position respectively of the rigid coordinate system.\n The following parameters are defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\npos\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPosition vector of the rigid coordinate system\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n{0,0,0}\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nrot\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRotation vector of the rigid coordinate system (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n{0,0,0}\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe rotation of the rigid coordinate system is given by a rotation vector.\n The direction of this vector defines the (instantaneous) axis or rotation,\n and the magnitude is the rotation angle (in radians) around this axis.\n \n\\end_layout\n\n\\begin_layout Subsection\nRigid \n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA node set can be attached to a rigid body using the \n\\shape italic\nrigid \n\\shape default\nboundary condition.\n Rigid nodes are not assigned degrees of freedom.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"rigid\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>2</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nrb \n\\shape default\nparameter defines the material (which must be a \n\\begin_inset Quotes eld\n\\end_inset\n\nrigid body\n\\begin_inset Quotes erd\n\\end_inset\n\n material) that in turn defines the rigid body.\n The node set must be defined in the \n\\emph on\nMesh\n\\shape italic\n\\emph default\n \n\\shape default\nsection.\n\\end_layout\n\n\\begin_layout Subsection\nMultiphasic Fluid Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Multiphasic-Fluid-Pressure\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a multiphasic mixture the nodal degree of freedom for fluid pressure represents the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n,\n which is related to the actual fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n One may assign the value of \n\\begin_inset Formula $p$\n\\end_inset\n\n directly on a boundary,\n by using the \n\\emph on\nactual fluid pressure \n\\emph default\nboundary condition.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc surface=\"FluidPressure1\" type=\"actual fluid pressure\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pressure>0</pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <shell_bottom>0</shell_bottom>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\nThis surface load evaluates \n\\begin_inset Formula $p$\n\\end_inset\n\n from nodal values of \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n on the surface,\n and using the average values of \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n and \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n at the integration points of the underlying element.\n This surface load is particularly useful when the effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n are not prescribed as boundary conditions on that surface,\n but evolve with the analysis solution.\n the \n\\emph on\nshell_bottom\n\\emph default\n boolean flag (0=false,\n 1=true) should be set to true when prescribing this pressure on the bottom of a multiphasic shell domain.\n\\end_layout\n\n\\begin_layout Subsection\nFluid Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Pressure-BC\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo set the actual fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n in a fluid,\n fluid-FSI,\n or fluid-solutes analysis,\n use the \n\\emph on\nfluid pressure\n\\emph default\n boundary condition.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"fluid pressure\" surface=\"FluidPressure1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pressure lc=\"1\">100</pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\nThe optional \n\\shape italic\nlc \n\\shape default\nparameter associates a load curve (a user-defined scale factor that evolves with time) which multiplies the value of <pressure>.\n The surface must be defined in the \n\\emph on\nMesh\n\\shape italic\n\\emph default\n \n\\shape default\nsection.\n For a biphasic analysis the actual fluid pressure is equal to the effective fluid pressure (\n\\begin_inset Formula $p=\\tilde{p}$\n\\end_inset\n\n),\n in which case use \n\\emph on\nprescribed fluid pressure\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Fluid-Pressure\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nFluid Resistance\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Resistance\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid,\n fluid-FSI or fluid-solutes analysis,\n this boundary condition prescribes an elastic pressure \n\\begin_inset Formula $p=R\\,q+p_{d}$\n\\end_inset\n\n on a surface,\n where \n\\begin_inset Formula $R$\n\\end_inset\n\n is the flow resistance and \n\\begin_inset Formula $q$\n\\end_inset\n\n is the volumetric flow rate across the surface (calculated internally);\n \n\\begin_inset Formula $p_{d}$\n\\end_inset\n\n is an optional offset pressure.\n The pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is converted to a dilatation and prescribed as an essential boundary condition at the nodes of the facets.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"fluid resistance\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <R lc=\"1\">7.93e+07</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pressure_offset lc=\"2\">10640</pressure_offset>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Subsection\nFluid RC\n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition models a fluid surface that has an RC-equivalent circuit for outflow conditions.\n \n\\end_layout\n\n\\begin_layout Subsection\nFluid RCR\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-RCR\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition models a three-element Windkessel outflow condition on a surface in a fluid,\n fluid-FSI or fluid-solutes analysis,\n as described in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Vignon-Clementel06\"\nliteral \"false\"\n\n\\end_inset\n\n.\n It prescribes an elastic pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n on a surface,\n which satisfies the ordinary differential equation\n\\begin_inset Formula \n\\[\np+\\tau\\frac{dp}{dt}=\\left(R_{d}+R\\right)q+R\\tau\\frac{dq}{dt}+p_{d}+\\tau\\frac{dp_{d}}{dt},\\quad\\tau=R_{d}C\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $R$\n\\end_inset\n\n is the upstream flow resistance (series resistance),\n \n\\begin_inset Formula $R_{d}$\n\\end_inset\n\n is the downstream flow resistance and \n\\begin_inset Formula $C$\n\\end_inset\n\n is the downstream flow capacitance (parallel \n\\begin_inset Formula $R_{d}C$\n\\end_inset\n\n element downstream of \n\\begin_inset Formula $R$\n\\end_inset\n\n),\n whereas \n\\begin_inset Formula $q$\n\\end_inset\n\n is the volumetric flow rate across the surface (calculated internally);\n \n\\begin_inset Formula $p_{d}$\n\\end_inset\n\n is an optional downstream offset pressure,\n which may be associated with a loadcurve to produce a function of time.\n The actual pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n obtained by solving this differential equation is converted to a dilatation and prescribed as an essential boundary condition at the nodes of the facets.\n Since the pressure is the solution of an ordinary differential equation,\n the user may also optionally specify the initial condition for the pressure.\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc name=\"FluidRCR1\" type=\"fluid RCR\" surface=\"FluidRCR1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <R>1</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Rd>10</Rd>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <capacitance>0.5</capacitance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pressure_offset>0</pressure_offset>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <initial_pressure>0</initial_pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\nBy setting \n\\begin_inset Formula $C=0$\n\\end_inset\n\n we recover the fluid resistance surface load described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Resistance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with flow resistance equal to \n\\begin_inset Formula $R_{d}+R$\n\\end_inset\n\n.\n By setting \n\\begin_inset Formula $R$\n\\end_inset\n\n to zero we can have simulate a parallel \n\\begin_inset Formula $R_{d}C$\n\\end_inset\n\n circuit.\n\\end_layout\n\n\\begin_layout Standard\nInternally,\n the ordinary differential equation presented above is solved numerically using a standard finite difference scheme,\n\\begin_inset Formula \n\\[\np_{n+1}=\\left(\\frac{R_{d}}{1+\\frac{\\tau}{\\Delta t}}+R\\right)q_{n+1}+\\frac{\\tau}{\\Delta t+\\tau}\\left(p_{n}-p_{d,n}-Rq_{n}\\right)+p_{d,n+1}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $f_{n+1}$\n\\end_inset\n\n is the value of any function \n\\begin_inset Formula $f\\left(t\\right)$\n\\end_inset\n\n at the current time step \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $f_{n}$\n\\end_inset\n\n is its value at the previous time step \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Delta t=t_{n+1}-t_{n}$\n\\end_inset\n\n is the time increment.\n\\end_layout\n\n\\begin_layout Subsection\nLinear Constraints\n\\end_layout\n\n\\begin_layout Standard\nA linear constraint can be used to couple nodal degrees of freedom.\n The linear constraint has one dependent degree of freedom \n\\begin_inset Formula $u^{*}$\n\\end_inset\n\n,\n and several independent degrees of freedom \n\\begin_inset Formula $u_{i}$\n\\end_inset\n\n.\n The linear constraint is defined as follows:\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nu^{*}=c_{0}+c_{1}u_{1}+\\ldots+c_{n}u_{n}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n the \n\\begin_inset Formula $c_{i}$\n\\end_inset\n\n are user-specified scale factors and \n\\begin_inset Formula $c_{0}$\n\\end_inset\n\ncan be used to specified an offset.\n \n\\end_layout\n\n\\begin_layout Standard\nThe dependent degree of freedom \n\\begin_inset Formula $u^{*}$\n\\end_inset\n\nwill be removed from the linear system of equations.\n Consequently,\n this nodal degree of freedom should not be used in any other boundary condition.\n \n\\end_layout\n\n\\begin_layout Standard\nTo define a linear constraint,\n set the \n\\emph on\ntype \n\\emph default\nattribute to \n\\emph on\nlinear constraint\n\\emph default\n.\n The dependent degree of freedom,\n i.e.\n the dof that will be removed,\n is specified via a node number and a dof identifier.\n Each child dof is defined via the \n\\emph on\nchild_dof \n\\emph default\ntag and similarly requires the corresponding node and dof identifier,\n as well as the value parameter.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"linear constraint\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node>5</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <child_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <node>8</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <value>1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </child_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <child_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <node>9</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <value>-1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </child_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Subsection\nMatching OSM Coefficient\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\nmatching_osm_coef\n\\begin_inset Quotes erd\n\\end_inset\n\n boundary condition is a surface boundary condition that imposes the same osmotic coefficient in the bath as in the multiphasic material bounded by that surface.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"matching_osm_coeff\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ambient_pressure>1</ambient_pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ambient_osmolarity>1e-3</ambient_osmolarity>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <shell_bottom>0</shell_bottom>\n\\end_layout\n\n\\begin_layout LyX-Code\n</bc>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nRigid Section\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nRigid\n\\emph default\n section is used to define all rigid constraints,\n rigid body initial kinematics,\n and rigid connectors.\n The motion of a point \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n connected to a rigid body is given by\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{x}=\\mathbf{r}\\left(t\\right)+\\mathbf{R}\\left(t\\right)\\cdot\\mathbf{Z}=\\mathbf{r}\\left(t\\right)+\\mathbf{z}\\left(t\\right)\\label{eq:rbm-position}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{r}\\left(t\\right)$\n\\end_inset\n\n is the position of the rigid body center of mass and \n\\begin_inset Formula $\\mathbf{R}\\left(t\\right)$\n\\end_inset\n\n is the body's (proper orthogonal) rotation tensor,\n which satisfies \n\\begin_inset Formula $\\mathbf{R}\\left(t_{0}\\right)=\\mathbf{I}$\n\\end_inset\n\n at the initial time \n\\begin_inset Formula $t_{0}$\n\\end_inset\n\n;\n here,\n \n\\begin_inset Formula $\\mathbf{z}\\left(t\\right)=\\mathbf{R}\\left(t\\right)\\cdot\\mathbf{Z}$\n\\end_inset\n\n is the position vector of the point relative to the body's center of mass,\n and \n\\begin_inset Formula $\\mathbf{X}=\\mathbf{r}\\left(t_{0}\\right)+\\mathbf{Z}$\n\\end_inset\n\n is the initial position.\n In FEBio,\n the rotation is evaluated as \n\\begin_inset Formula $\\mathbf{R}\\left(t\\right)=\\exp\\left[\\boldsymbol{\\xi}\\left(t\\right)\\right]$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{\\xi}\\left(t\\right)$\n\\end_inset\n\n is the material rotation of the rigid body from its reference configuration.\n In practice,\n \n\\begin_inset Formula $\\boldsymbol{\\xi}$\n\\end_inset\n\n is stored as a quaternion to facilitate the multiplication of rotation tensors.\n The exponential map \n\\begin_inset Formula $\\exp\\left[\\boldsymbol{\\xi}\\left(t\\right)\\right]$\n\\end_inset\n\n is given in full form as\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{R}\\left(t\\right)=\\exp\\left[\\boldsymbol{\\xi}\\left(t\\right)\\right]=\\cos\\xi\\,\\mathbf{I}-\\sin\\xi\\,\\boldsymbol{\\mathcal{E}}\\cdot\\mathbf{n}+\\left(1-\\cos\\xi\\right)\\mathbf{n}\\otimes\\mathbf{n}\\label{eq:rbr-rotation-tensor}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\mathcal{E}}$\n\\end_inset\n\n is the third-order permutation pseudo-tensor with rectangular components \n\\begin_inset Formula $\\varepsilon_{ijk}$\n\\end_inset\n\n (permutation symbol).\n Here,\n \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the unit vector along the axis of rotation and \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n is the angle of rotation about \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $\\boldsymbol{\\xi}=\\xi\\mathbf{n}$\n\\end_inset\n\n.\n Both \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n may evolve over time.\n Either none or all the components of \n\\begin_inset Formula $\\boldsymbol{\\xi}\\left(t\\right)$\n\\end_inset\n\n must be prescribed (or fixed) for a rigid body.\n\\end_layout\n\n\\begin_layout Standard\nThe Rigid section can contain the following subsections.\n \n\\end_layout\n\n\\begin_layout Description\nrigid_bc Define a rigid constraint.\n \n\\end_layout\n\n\\begin_layout Description\nrigid_ic Define a rigid initial condition.\n\\end_layout\n\n\\begin_layout Description\nrigid_load Define a load on a rigid body.\n\\end_layout\n\n\\begin_layout Description\nrigid_connector Define a connection or joint between two rigid bodies.\n \n\\end_layout\n\n\\begin_layout Subsection\nRigid Constraints\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Constraints\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe rigid constraints section allows users to prescribe the kinematics applied to a single rigid body.\n A rigid constraint is added using the \n\\emph on\nrigid_bc \n\\emph default\ntag in the \n\\emph on\nRigid \n\\emph default\nparent tag.\n The particular constraint is defined via the \n\\emph on\ntype \n\\emph default\nattribute.\n An optional \n\\emph on\nname \n\\emph default\nattribute can be added to uniquely name the constraint.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_fixed Constraint\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_fixed \n\\emph default\nconstraint fixes one or multiple rigid degrees of freedom.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nRx_dof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfix x-displacement degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nRy_dof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfix y-displacement degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nRz_dof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfix z-displacement degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nRu_dof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfix x-rotational degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nRv_dof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfix y-rotational degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nRw_dof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfix z-rotational degree of freedom\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe name of the rigid material is defined in the Material section.\n The referenced material must be a \n\\begin_inset Quotes eld\n\\end_inset\n\nrigid body\n\\begin_inset Quotes erd\n\\end_inset\n\n material.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following example fixes the displacement degrees of freedom.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_bc type=\"rigid_fixed\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>RigidMaterial1</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Rx_dof>1</Rx_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Ry_dof>1</Ry_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Rz_dof>1</Rz_dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_bc>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_displacement Constraint\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_displacement\n\\emph default\n constraint prescribes the value of a rigid displacement degree of freedom.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid degree of freedom to prescribe.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefines the value as relative or absolute.\n (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the name attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Enumerate\nThe values allowed for the \n\\emph on\ndof \n\\emph default\nparameter are:\n x,\n y,\n or z.\n \n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set,\n the value is taken relative to the dof value at the start of the step.\n\\end_layout\n\n\\begin_layout Standard\nThe following example prescribes the x-displacement degree of freedom.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_bc type=\"rigid_displacement\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">3.14</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_bc>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_rotation Constraint\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_rotation\n\\emph default\n constraint prescribes the value of a rigid rotational degree of freedom.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid degree of freedom to prescribe.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefines the value as relative or absolute.\n (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the name attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Enumerate\nThe values allowed for the \n\\emph on\ndof \n\\emph default\nparameter are:\n Ru,\n Rv,\n or Rw.\n \n\\end_layout\n\n\\begin_layout Enumerate\nIf the relative flag is set,\n the value is taken relative to the dof value at the start of the step.\n\\end_layout\n\n\\begin_layout Standard\nThe following example prescribes a rotation around the x-axis.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_bc type=\"rigid_rotation\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>Ru</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">3.14</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_bc>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_rotation_vector Constraint\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_rotation_vector \n\\emph default\nconstraint allows the user to prescribe the rotation of a rigid body using a rotation vector.\n In essence,\n this combines the effect of using three separate rigid rotation constraints,\n one for each component of the rotation vector,\n and thus might be a more convenient choice if the rotation of the rigid body is fully prescribed.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of the rotation vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of the rotation vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of the rotation vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the name attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Standard\nThe following example prescribes a rotation around the z-axis using a load controller.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_bc type=\"rigid_rotation_vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vx>0</vx>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vy>0</vy>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vz lc=\"1\">3.14</vz>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_bc>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_euler_angles Constraint\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_euler_angles \n\\emph default\nconstraint allows the user to prescribe the rotation of a rigid body using three Euler angles.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nEx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nX Euler angle (degrees)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nEy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nY Euler angle (degrees)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nEz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nZ Euler angle (degrees)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the name attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Standard\nThe following example prescribes a rotation around the z-axis using a load controller.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_bc type=\"rigid_euler_vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vx>0</vx>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vy>0</vy>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vz lc=\"1\">180.0</vz>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_bc>\n\\end_layout\n\n\\begin_layout Subsection\nRigid Initial Conditions\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_ic \n\\emph default\nsection defines the initial conditions on rigid bodies.\n Rigid initial conditions can be used to set the initial values for the rigid body's linear and angular velocity.\n The particular initial condition is selected using the \n\\emph on\ntype \n\\emph default\nattribute.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nInitial Rigid Velocity\n\\end_layout\n\n\\begin_layout Standard\nIn dynamic analysis,\n the initial velocity of a rigid body can be set via the \n\\emph on\ninitial_rigid_velocity\n\\emph default\n rigid constraint.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe initial velocity vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Standard\nThe following example defines an initial velocity for a rigid body.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_ic type=\"initial_rigid_velocity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1,0,0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_ic>\n\\end_layout\n\n\\begin_layout Subsubsection\nInitial Rigid Angular Velocity\n\\end_layout\n\n\\begin_layout Standard\nIn dynamic analysis,\n the initial angular velocity of a rigid body can be set via the \n\\emph on\ninitial_rigid_angular_velocity\n\\emph default\n rigid constraint.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name.\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe initial angular velocity vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Standard\nThe following example defines an initial angular velocity for a rigid body.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_ic type=\"initial_rigid_angular_velocity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value>1,0,0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_ic>\n\\end_layout\n\n\\begin_layout Subsection\nRigid Loads\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_load \n\\emph default\nsection can be used to applied loads to a rigid body.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_force Load\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_force\n\\emph default\n load applies a load directly to the rigid degree of freedom.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid degree of freedom to prescribe.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed force value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nload_type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefines the type of load.\n (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Enumerate\nThe values allowed for the \n\\emph on\ndof \n\\emph default\nparameter are:\n Rx,\n Ry,\n Rz.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe load_type allows the following values:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\n0 = a load (force/moment) is applied directly to the degree of freedom.\n \n\\end_layout\n\n\\begin_layout Enumerate\n1 = a follower load applied is applied to the rigid body.\n The load is applied in the rigid body's coordinate system,\n so it rotates with the rigid body.\n This only works for Rx,\n Ry,\n Rz.\n \n\\end_layout\n\n\\begin_layout Enumerate\n2 = target load.\n The load's value is ramped up from its initial value at the start of the step,\n and ramped up linearly to the value specified in the \n\\emph on\nvalue \n\\emph default\nparameter.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\nThe following example applies a force in the x-direction.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_load type=\"rigid_force\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>1</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>Rx</dofs>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">3.14</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_moment Load\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_moment\n\\emph default\n load applies a moment about a particular coordinate axis.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ndof\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid degree of freedom to prescribe.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed moment value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefines the type of load.\n (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Enumerate\nThe values allowed for the \n\\emph on\ndof \n\\emph default\nparameter are:\n Ru,\n Rv,\n Rw.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe relative flag indicates whether the applied value should be absolute or relative to the value at the end of the previous analysis step.\n\\end_layout\n\n\\begin_layout Standard\nThe following example applies a moment about the x-axis.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_load type=\"rigid_moment\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>Ru</dofs>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">3.14</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_follower_force Load\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_follower_force\n\\emph default\n load applies a force in the local rigid body coordinate system.\n The direction of the force in global coordinates therefore depends on orientation of the rigid body.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLocation where the force is applied.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe vector force value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRelative flag (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the position in the reference coordinate system of the point where the force is applied.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe relative flag indicates whether the applied value should be absolute or relative to the value at the end of the previous analysis step.\n\\end_layout\n\n\\begin_layout Standard\nThe following example applies a moment about the x-axis.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_load type=\"rigid_follower_force\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion>1,2,3</dofs>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force>100,0,0</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_follower_moment Load\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_follower_\n\\emph default\nmoment load applies a moment in the local rigid body coordinate system.\n The direction of the moment in global coordinates therefore depends on orientation of the rigid body.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrb\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rigid body's material name (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe vector moment value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThis is the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute assigned to the corresponding rigid body material as defined in the Material section.\n\\end_layout\n\n\\begin_layout Standard\nThe following example applies a moment about the x-axis.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_load type=\"rigid_follower_moment\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rb>rigid</rb>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <moment>100,0,0</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid_cable Load\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nrigid_cable\n\\emph default\n load emulates the effect of a force applied on one end of a cable that is connected to one or more rigid bodies.\n It requires the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe scalar force magnitude\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nforce_direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe unit vector defining the force direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIndicates whether the through-points are defined in global or relative coordinates (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid_cable_point\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA point on a rigid body that the cable runs through.\n (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nrelative \n\\emph default\nflag indicates whether the location of the cable's via-points are defined in global coordinates (relative=0) or in the local coordinates of the corresponding rigid body (relative=1).\n \n\\end_layout\n\n\\begin_layout Enumerate\nOne or more rigid cable points must be defined that define the points that the cable runs through.\n The first point defined is the anchor,\n where the cable is attached to.\n The other points define the via points,\n i.e.\n a point where the cable runs through.\n Each cable point is defined by the rigid body that it connects to and a point on the rigid body that the cable runs through.\n The point's coordinates are either in the global or the local coordinate system of the rigid body,\n as determined by the \n\\emph on\nrelative \n\\emph default\nflag.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following example illustrates a rigid cable setup.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_load type=\"rigid_cable\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force lc=\"1\">100</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_direction>1,0,0</force_direction>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>1</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rigid_cable_point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rigid_body_id>rigid1</rigid_body_id>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <position>0,\n -1,\n 1</position>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </rigid_cable_point>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rigid_cable_point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rigid_body_id>rigid2</rigid_body_id>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <position>0,\n 1,\n 1</position>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </rigid_cable_point>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_load>\n\\end_layout\n\n\\begin_layout Subsection\nRigid Connectors\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Connectors\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe rigid connector section allows users to define different type of connections between two rigid bodies.\n The specific connector is defined via the \n\\emph on\ntype \n\\emph default\nattribute.\n \n\\end_layout\n\n\\begin_layout Standard\nThe rigid connectors fall into two categories.\n The first category define different joints that constrain the relative motion of one rigid body with respect to the second one.\n \n\\end_layout\n\n\\begin_layout Standard\nRigid joints produce nonlinear constraints between rigid bodies,\n which prevent relative motion except along the degrees of freedom of the joint.\n The term 'rigid' refers to the bodies,\n not to the joints.\n Each rigid joint needs to define two rigid bodies (\n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b)$\n\\end_inset\n\n,\n a joint origin common to both bodies,\n and a set of axes that determine the relative orientation of the joint degrees of freedom.\n These axes define orthonormal basis vectors \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1}^{a},\\mathbf{e}_{2}^{a},\\mathbf{e}_{3}^{a}\\right\\} $\n\\end_inset\n\n and \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1}^{b},\\mathbf{e}_{2}^{b},\\mathbf{e}_{3}^{b}\\right\\} $\n\\end_inset\n\n on each rigid body,\n with both bases being coincident,\n \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n,\n at the start of the analysis,\n and given in world coordinates.\n\\end_layout\n\n\\begin_layout Standard\nThe rigid joint nonlinear constraints produce reaction forces and moments that are enforced with penalty parameters and Lagrange multipliers.\n The penalty parameters,\n \n\\emph on\nforce_penalty\n\\emph default\n and \n\\emph on\nmoment_penalty\n\\emph default\n,\n may be conceptualized as stiffnesses of linear/torsional springs that prevent relative translations/rotations of the rigid bodies along degrees of freedom that must remain constrained for that joint.\n The penalty values should be selected based on a rough estimation of the maximum reactions forces and moments acting at these joints,\n divided by the maximum amount of linear/angular separation that your analysis can tolerate for that joint.\n Alternatively,\n set the \n\\emph on\nforce_penalty\n\\emph default\n and \n\\emph on\nmoment_penalty\n\\emph default\n parameters to 1 and turn on the \n\\emph on\nauto_penalty\n\\emph default\n;\n this setting will automatically adjust the \n\\emph on\nforce_penalty\n\\emph default\n and \n\\emph on\nmoment_penalty\n\\emph default\n to an appropriate value.\n\\end_layout\n\n\\begin_layout Standard\nThe augmented Lagrangian method is used by default,\n where the joint reaction force and moment are treated as Lagrange multipliers,\n augmented at each time step by the product of the force/moment penalty parameter and the linear/angular gap.\n Use the parameter \n\\emph on\nmaxaug\n\\emph default\n to control the maximum allowable number of augmentations at each time step (\n\\emph on\nmaxaug\n\\emph default\n=0 produces the penalty method);\n use a non-zero value for the parameter \n\\emph on\nminaug\n\\emph default\n to ensure a minimum number of augmentations.\n Augmentations will proceed until the relative change in the reaction force/moment magnitude is less than \n\\emph on\ntolerance\n\\emph default\n,\n and/or the linear gap is less than \n\\emph on\ngaptol\n\\emph default\n,\n and/or the angular gap is less than \n\\emph on\nangtol\n\\emph default\n.\n Setting any of these parameters to zero disables that check.\n\\end_layout\n\n\\begin_layout Standard\nThe nonlinear constraints that enforce these joints produce a non-symmetric stiffness matrix.\n Therefore,\n when using rigid joints,\n use the analysis setting for a non-symmetric formulation,\n see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Parameters-Solid-Analysis\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following rigid joints can be defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid spherical joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConnect rigid bodies at point.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid revolute joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRotation about a single prescribed axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid prismatic joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTranslation along a single prescribed axis.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid cylindrical joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRotation and translation about prescribed axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid planar joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2D plane translation and rotation about normal.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid lock\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrevent any relative motion between rigid bodies.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe second category of connectors apply a force on the rigid bodies that is proportional to some relative motion.\n The term 'rigid' refers to the bodies,\n not to the connectors.\n\\end_layout\n\n\\begin_layout Standard\nThe following rigid connectors can be defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid spring\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplies a spring that connects the two rigid bodies.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid damper\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplies a damper based on relative linear velocity.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid angular damper\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplies a damper based on relative angular velocity.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid contractile force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConstant contractile force between rigid bodies.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid planar joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2D plane translation and rotation about normal.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrigid lock\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrevent any relative motion between rigid bodies.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAll of these joints and connectors define two parameters that reference the two rigid bodies.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nbody_a\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe first rigid body in the connector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nbody_b\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe second rigid body in the connector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn addition,\n the joints defined above are all based on the Augmented Lagrangian method,\n and consequently,\n also share the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAugmentation flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecifies the augmentation tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nminaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe minimum number of augmentations.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmaxaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe maximum number of augmentations.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ngaptol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGap tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nangtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAngular separation tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nforce_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPenalty factor applied to force augmentations.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPenalty factor applied to moment augmentations.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nauto_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag to calculate penalty factor automatically.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAdditional parameters,\n unique to a specific connector,\n are defined in the sections below.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Spherical Joint\n\\end_layout\n\n\\begin_layout Standard\nA rigid spherical joint connects rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n at a point in space,\n allowing three degrees of freedom for rotation about that point.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_origin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe position of the joint.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_rotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe prescribed rotation flag.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation_x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the prescribed rotation.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation_y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the prescribed rotation.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation_z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the prescribed rotation.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment_x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nX-component of applied moment \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment_y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nY-component of applied moment \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment_z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nZ-component of applied moment \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntolerance \n\\shape default\nelement defines the augmentation tolerance.\n That is,\n when the relative change in the constraint forces and moments (the Lagrange multipliers) are less than this value.\n The \n\\shape italic\ngaptol\n\\shape default\n element defines the tolerance for spatial separation of the joint origin on the two bodies (in units of length).\n Setting either of these elements to zero disables the enforcement of that tolerance.\n The \n\\shape italic\nforce_penalty\n\\shape default\n parameter (with units of force per length) represents the stiffness that prevents the joint origin on the two bodies from separating.\n The \n\\shape italic\nbody_a\n\\shape default\n and \n\\shape italic\nbody_b \n\\shape default\nelements are the material numbers of the two rigid bodies.\n The \n\\shape italic\njoint_origin \n\\shape default\nelement defines the position of the joint (the origin of the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} )$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n Note that this point does not have to be inside or on the surface of either of the two bodies.\n The \n\\shape italic\nrotation_axis \n\\shape default\nelement defines the orientation of the joint rotation axis in world coordinates at the start of the analysis.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the rotation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<moment_penalty>1e0</moment_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_rotation>1</prescribed_rotation>\n\\end_layout\n\n\\begin_layout LyX-Code\n<rotation_x lc=\"1\">1</rotation_x>\n\\end_layout\n\n\\begin_layout LyX-Code\n<rotation_y lc=\"2\">1</rotation_y>\n\\end_layout\n\n\\begin_layout LyX-Code\n<rotation_z>0</rotation_z>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nprescribed_rotation\n\\shape default\n element is a flag that indicates that the motion of the joint is prescribed (1 for prescribed,\n 0 for free).\n The \n\\shape italic\nrotation_x\n\\shape default\n,\n \n\\shape italic\nrotation_y\n\\shape default\n and \n\\shape italic\nrotation_z\n\\shape default\n elements specify the components \n\\begin_inset Formula $\\left(\\theta_{1},\\theta_{2},\\theta_{3}\\right)$\n\\end_inset\n\n of rotation (with units of radians),\n with optional associated load curves.\n The rotation occurs about the axis directed along \n\\begin_inset Formula $\\theta_{1}\\mathbf{e}_{1}^{a}+\\theta_{2}\\mathbf{e}_{2}^{a}+\\theta_{3}\\mathbf{e}_{3}^{a}$\n\\end_inset\n\n,\n with a magnitude \n\\begin_inset Formula $\\sqrt{\\theta_{1}^{2}+\\theta_{2}^{2}+\\theta_{3}^{2}}$\n\\end_inset\n\n.\n Either all or none of the rotation components must be prescribed,\n since all rotation components are needed to define a rotation tensor.\n The \n\\shape italic\nmoment_penalty\n\\shape default\n parameter (with units of moment per radians) represents the torsional stiffness that enforces tracking of the prescribed rotations between the two bodies.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n moments may be prescribed on body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n about the world coordinate axes,\n using the additional tag\n\\end_layout\n\n\\begin_layout LyX-Code\n<moment_x lc=\"3\">1.e-3</moment_x>\n\\end_layout\n\n\\begin_layout LyX-Code\n<moment_y lc=\"4\">3.e-3</moment_y>\n\\end_layout\n\n\\begin_layout LyX-Code\n<moment_z lc=\"5\">2.e-3</moment_z>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nmoment\n\\shape default\n elements specify the components of the moment vector in world coordinates,\n with optional associated load curves.\n The \n\\shape italic\nmoment\n\\shape default\n elements should not be used simultaneously with a prescribed rotation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid spherical joint\" name=\"Joint01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0.1</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gaptol>0</gaptol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_penalty>1e0</force_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_origin>0,0,0</joint_origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Revolute Joint\n\\end_layout\n\n\\begin_layout Standard\nThe rigid revolute joint connector allows the user to connect two rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n at a point in space and allow rotation about a single prescribed axis.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_origin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe position of the joint.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe rotation axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntransverse_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe transverse axis.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_rotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed rotation flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed rotation around axis.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplied moment about axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntolerance \n\\shape default\nelement defines the augmentation tolerance.\n That is,\n when the relative change in the constraint forces and moments (the Lagrange multipliers) are less than this value.\n The \n\\shape italic\ngaptol\n\\shape default\n element defines the tolerance for spatial separation of the joint origin on the two bodies (in units of length).\n The \n\\shape italic\nangtol\n\\shape default\n element defines the tolerance for angular separation of the joint axis on the two bodies (in units of radians).\n Setting any of these three elements to zero disables the enforcement of that tolerance.\n The \n\\shape italic\nforce_penalty\n\\shape default\n parameter (with units of force per length) represents the stiffness that prevents the joint origin on the two bodies from separating.\n The \n\\shape italic\nmoment_penalty\n\\shape default\n parameter (with units of moment per radians) represents the torsional stiffness that enforces parallelism of the joint axis on the two bodies.\n The \n\\shape italic\nbody_a\n\\shape default\n and \n\\shape italic\nbody_b \n\\shape default\nelements are the material numbers of the two rigid bodies.\n The \n\\shape italic\njoint_origin \n\\shape default\nelement defines the position of the joint origin (the origin of the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} )$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n Note that this point does not have to be inside or on the surface of either of the two bodies.\n The \n\\shape italic\nrotation_axis \n\\shape default\nelement defines the orientation of the joint rotation axis \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the rotation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_rotation>1</prescribed_rotation>\n\\end_layout\n\n\\begin_layout LyX-Code\n<rotation lc=\"1\">3.14159</rotation>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nprescribed_rotation\n\\shape default\n element is a flag that indicates that the motion of the joint is prescribed (1 for prescribed,\n 0 for free).\n The \n\\shape italic\nrotation\n\\shape default\n element specifies the amount of rotation (with units of radians) with an optional associated load curve.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n a moment may be prescribed on body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n about the joint axis,\n using the additional tag\n\\end_layout\n\n\\begin_layout LyX-Code\n<moment lc=\"1\">5.e-3</moment>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nmoment\n\\shape default\n element specifies the magnitude of the moment,\n with an optional associated load curve.\n The \n\\shape italic\nmoment\n\\shape default\n element should not be used simultaneously with a prescribed rotation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid revolute joint\" name=\"Joint 1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gaptol>1e-4</gaptol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angtol>1e-4</angtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_penalty>1e0</force_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <moment_penalty>1e0</moment_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>3</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_origin>-150,0,2</joint_origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rotation_axis>0,0,1</rotation_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Prismatic Joint\n\\end_layout\n\n\\begin_layout Standard\nThe rigid prismatic joint connector allows the user to connect two rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n at a point in space and allow translation along a single prescribed axis.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_origin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe position of the joint.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntranslation_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe translation axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntransverse_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe transverse axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_translation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntranslation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation along axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplied force along axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntolerance \n\\shape default\nelement defines the augmentation tolerance.\n That is,\n when the relative change in the constraint forces and moments (the Lagrange multipliers) are less than this value.\n The \n\\shape italic\ngaptol\n\\shape default\n element defines the tolerance for spatial separation of the joint origin on the two bodies (in units of length).\n The \n\\shape italic\nangtol\n\\shape default\n element defines the tolerance for angular separation of the joint axis on the two bodies (in units of radians).\n Setting any of these three elements to zero disables the enforcement of that tolerance.\n The \n\\shape italic\nforce_penalty\n\\shape default\n parameter (with units of force per length) represents the stiffness that prevents the joint origin on the two bodies from separating.\n The \n\\shape italic\nmoment_penalty\n\\shape default\n parameter (with units of moment per radians) represents the torsional stiffness that enforces parallelism of the joint axis on the two bodies.\n The \n\\shape italic\nbody_a\n\\shape default\n and \n\\shape italic\nbody_b \n\\shape default\nelements are the material numbers of the two rigid bodies.\n The \n\\shape italic\njoint_origin \n\\shape default\nelement defines the position of the joint (the origin of the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} )$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n Note that this point does not have to be inside or on the surface of either of the two bodies.\n The \n\\shape italic\ntranslation_axis \n\\shape default\nelement defines the orientation \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n of the joint translation axis in world coordinates at the start of the analysis.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the translation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_translation>1</prescribed_translation>\n\\end_layout\n\n\\begin_layout LyX-Code\n<translation lc=\"1\">5.0</translation>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nprescribed_translation\n\\shape default\n element is a flag that indicates that the motion of the joint is prescribed (1 for prescribed,\n 0 for free).\n The \n\\shape italic\ntranslation\n\\shape default\n element specifies the amount of translation (with units of length) with an optional associated load curve.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n a force may be prescribed on body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n,\n along the joint axis using the additional tag\n\\end_layout\n\n\\begin_layout LyX-Code\n<force lc=\"1\">5.e-3</force>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nforce\n\\shape default\n element specifies the magnitude of the force,\n with an optional associated load curve.\n The \n\\shape italic\nforce\n\\shape default\n element should not be used simultaneously with a prescribed translation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid prismatic joint\" name=\"Joint02 \">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gaptol>1e-4</gaptol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angtol>1e-4</angtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_penalty>1e0</force_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <moment_penalty>1e0</moment_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>4</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>1</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_origin>-150,0,2</joint_origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <translation_axis>1,0,0</translation_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prescribed_translation>150</prescribed_translation>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <translation lc=\"1\">1</translation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Cylindrical Joint\n\\end_layout\n\n\\begin_layout Standard\nA rigid cylindrical joint connects rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n at a point in space,\n allowing one degree of freedom for rotation about an axis through that point,\n and another degree of freedom for translation along that axis.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_origin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe position of the joint.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe joint axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntransverse_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe transverse axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_translation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntranslation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation along axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplied force along axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_rotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed rotation flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed rotation around axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nmoment\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApplied moment about axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntolerance \n\\shape default\nelement defines the augmentation tolerance.\n That is,\n when the relative change in the constraint forces and moments (the Lagrange multipliers) are less than this value.\n The \n\\shape italic\ngaptol\n\\shape default\n element defines the tolerance for spatial separation of the joint origin on the two bodies (in units of length).\n The \n\\shape italic\nangtol\n\\shape default\n element defines the tolerance for angular separation of the joint axis on the two bodies (in units of radians).\n Setting any of these three elements to zero disables the enforcement of that tolerance.\n The \n\\shape italic\nforce_penalty\n\\shape default\n parameter (with units of force per length) represents the stiffness that prevents the joint origin on the two bodies from separating.\n The \n\\shape italic\nmoment_penalty\n\\shape default\n parameter (with units of moment per radians) represents the torsional stiffness that enforces parallelism of the joint axis on the two bodies.\n The \n\\shape italic\nbody_a\n\\shape default\n and \n\\shape italic\nbody_b \n\\shape default\nelements are the material numbers of the two rigid bodies.\n The \n\\shape italic\njoint_origin \n\\shape default\nelement defines the position of the joint (the origin of the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} )$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n Note that this point does not have to be inside or on the surface of either of the two bodies.\n The \n\\shape italic\njoint_axis \n\\shape default\nelement defines the orientation \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n of the joint rotation and translation axis in world coordinates at the start of the analysis.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the rotation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_rotation>1</prescribed_rotation>\n\\end_layout\n\n\\begin_layout LyX-Code\n<rotation lc=\"1\">1.570796327</rotation>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nprescribed_rotation\n\\shape default\n element is a flag that indicates that the motion of the joint is prescribed (1 for prescribed,\n 0 for free).\n The \n\\shape italic\nrotation\n\\shape default\n element specifies the amount of rotation (with units of radians) with an optional associated load curve.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the translation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_translation>1</prescribed_translation>\n\\end_layout\n\n\\begin_layout LyX-Code\n<translation lc=\"2\">10.0</translation>\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n a moment may be prescribed about the joint axis using the additional tag\n\\end_layout\n\n\\begin_layout LyX-Code\n<moment lc=\"1\">5.e-3</moment>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nmoment\n\\shape default\n element specifies the magnitude of the moment,\n with an optional associated load curve.\n The \n\\shape italic\nmoment\n\\shape default\n element should not be used simultaneously with a prescribed rotation.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n a force may be prescribed along the joint axis using the additional tag\n\\end_layout\n\n\\begin_layout LyX-Code\n<force lc=\"1\">2.0</force>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nforce\n\\shape default\n element specifies the magnitude of the force,\n with an optional associated load curve.\n The \n\\shape italic\nforce\n\\shape default\n element should not be used simultaneously with a prescribed translation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid cylindrical joint\" name=\"Joint03\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gaptol>1e-4</gaptol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angtol>1e-4</angtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_penalty>1e0</force_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <moment_penalty>1e0</moment_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_origin>0,0,0</joint_origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_axis>0,0,1</joint_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Planar Joint\n\\end_layout\n\n\\begin_layout Standard\nA rigid planar joint connects rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n allowing one degree of freedom for rotation about the axis \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n through that point,\n and two degrees of freedom for translations in the plane perpendicular to that axis,\n along \n\\begin_inset Formula $\\mathbf{e}_{2}^{a}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{e}_{3}^{a}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_origin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe position of the joint.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe joint axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntranslation_axis_1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe first translation axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_rotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed rotation flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrotation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed rotation around axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_translation_1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation 1 flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntranslation_1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation along axis 1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nprescribed_translation_2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation 2 flag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntranslation_2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed translation along axis 2\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntolerance \n\\shape default\nelement defines the augmentation tolerance.\n That is,\n when the relative change in the constraint forces and moments (the Lagrange multipliers) are less than this value.\n The \n\\shape italic\ngaptol\n\\shape default\n element defines the tolerance for spatial separation of the joint origin on the two bodies (in units of length).\n The \n\\shape italic\nangtol\n\\shape default\n element defines the tolerance for angular separation of the joint axes on the two bodies (in units of radians).\n Setting any of these three elements to zero disables the enforcement of that tolerance.\n The \n\\shape italic\nforce_penalty\n\\shape default\n parameter (with units of force per length) represents the stiffness that prevents the joint origin on the two bodies from separating along the rotation axis.\n The \n\\shape italic\nmoment_penalty\n\\shape default\n parameter (with units of moment per radians) represents the torsional stiffness that enforces parallelism of the joint rotation axis on the two bodies.\n The \n\\shape italic\nbody_a\n\\shape default\n and \n\\shape italic\nbody_b \n\\shape default\nelements are the material numbers of the two rigid bodies.\n The \n\\shape italic\njoint_origin \n\\shape default\nelement defines the position of the joint (the origin of the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} )$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n Note that this point does not have to be inside or on the surface of either of the two bodies.\n The \n\\shape italic\nrotation_axis \n\\shape default\nelement defines the orientation of the joint rotation axis \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n The \n\\shape italic\ntranslation_axis_1\n\\shape default\n element defines the orientation of the joint translation axis \n\\begin_inset Formula $\\mathbf{e}_{2}^{a}$\n\\end_inset\n\n in the plane perpendicular to the joint rotation axis,\n in world coordinates at the start of the analysis.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the rotation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_rotation>1</prescribed_rotation>\n\\end_layout\n\n\\begin_layout LyX-Code\n<rotation lc=\"1\">1.570796327</rotation>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nprescribed_rotation\n\\shape default\n element is a flag that indicates that the motion of the joint is prescribed (1 for prescribed,\n 0 for free).\n The \n\\shape italic\nrotation\n\\shape default\n element specifies the amount of rotation (with units of radians) with an optional associated load curve.\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the translation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed along \n\\begin_inset Formula $\\mathbf{e}_{2}^{a}$\n\\end_inset\n\n using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_translation_1>1</prescribed_translation_1>\n\\end_layout\n\n\\begin_layout LyX-Code\n<translation_1 lc=\"2\">10.0</translation_1>\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n the translation of body \n\\begin_inset Formula $b$\n\\end_inset\n\n relative to body \n\\begin_inset Formula $a$\n\\end_inset\n\n may be prescribed along \n\\begin_inset Formula $\\mathbf{e}_{3}^{a}$\n\\end_inset\n\n using the additional tags\n\\end_layout\n\n\\begin_layout LyX-Code\n<prescribed_translation_2>1</prescribed_translation_2>\n\\end_layout\n\n\\begin_layout LyX-Code\n<translation_2 lc=\"2\">10.0</translation_2>\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid planar joint\" name=\"Joint01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gaptol>1e-4</gaptol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angtol>1e-4</angtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_penalty>1e0</force_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <moment_penalty>1e0</moment_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_origin>0,0,0</joint_origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rotation_axis>0,-0.5,0.8660254</rotation_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <translation_axis_1>1,0,0</translation_axis_1>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Lock Joint\n\\end_layout\n\n\\begin_layout Standard\nA rigid lock connects rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n,\n preventing relative motion between them.\n It requires the specification of a joint origin and two orthogonal axes \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{e}_{2}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\njoint_origin\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe position of the joint.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nfirst_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe first axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nsecond_axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe second axis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntolerance \n\\shape default\nelement defines the augmentation tolerance.\n That is,\n when the relative change in the constraint forces and moments (the Lagrange multipliers) are less than this value.\n The \n\\shape italic\ngaptol\n\\shape default\n element defines the tolerance for spatial separation of the joint origin on the two bodies (in units of length).\n The \n\\shape italic\nangtol\n\\shape default\n element defines the tolerance for angular separation of the joint axes on the two bodies (in units of radians).\n Setting any of these three elements to zero disables the enforcement of that tolerance.\n The \n\\shape italic\nforce_penalty\n\\shape default\n parameter (with units of force per length) represents the stiffness that prevents the joint origin on the two bodies from separating along the rotation axis.\n The \n\\shape italic\nmoment_penalty\n\\shape default\n parameter (with units of moment per radians) represents the torsional stiffness that enforces parallelism of the joint rotation axis on the two bodies.\n The \n\\shape italic\nbody_a\n\\shape default\n and \n\\shape italic\nbody_b \n\\shape default\nelements are the material numbers of the two rigid bodies.\n The \n\\shape italic\njoint_origin \n\\shape default\nelement defines the position of the joint (the origin of the basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} )$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n Note that this point does not have to be inside or on the surface of either of the two bodies.\n The \n\\shape italic\nfirst_axis \n\\shape default\nelement defines the orientation of the axis \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n The \n\\shape italic\nsecond_axis\n\\shape default\n element defines the orientation of the second axis \n\\begin_inset Formula $\\mathbf{e}_{2}$\n\\end_inset\n\n in world coordinates at the start of the analysis.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid lock\" name=\"Joint01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gaptol>1e-4</gaptol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angtol>1e-4</angtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force_penalty>1e0</force_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <moment_penalty>1e0</moment_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <joint_origin>0,0,0</joint_origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <first_axis>1,0,0</first_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <second_axis>0,1,0</second_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Spring\n\\end_layout\n\n\\begin_layout Standard\nThe rigid spring applies a linear spring that connects two rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n at arbitrary points (not necessarily nodes).\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspring constant (in units of force per length)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion_a\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInsertion point of the spring on body a\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion_b\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInsertion point of the spring on body b\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nfree_length\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe length of the unloaded spring (1) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the free_length parameter is zero,\n then the initial length of the spring,\n as defined by the two insertion points,\n is taken as the free length.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid spring\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion_a>0,0,1</insertion_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion_b>0,0,3</insertion_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Damper\n\\end_layout\n\n\\begin_layout Standard\nThe rigid damper applies a linear damper that connects two rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n at arbitrary points (not necessarily nodes).\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nc\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDamping constant\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion_a\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInsertion point of the spring on body a\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion_b\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIn section point of the spring on body b\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid damper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion_a>0,0,1</insertion_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion_b>0,0,3</insertion_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>1e-7</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Angular Damper\n\\end_layout\n\n\\begin_layout Standard\nThe rigid angular damper applies an angular damper that connects two rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nc\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAngular damping constant\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid angular damper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>1e-3</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid Contractile Force\n\\end_layout\n\n\\begin_layout Standard\nThe rigid contractile force applies an actuator force between arbitrary points (not necessarily nodes) on two rigid bodies \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to the shared parameters above,\n it defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nf0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe applied force (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion_a\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInsertion point of the spring on body a\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ninsertion_b\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIn section point of the spring on body b\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe f0 parameter represents the actuator force (positive for contraction).\n Optionally,\n it may be associated with a load curve.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector type=\"rigid contractile force\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_a>1</body_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_b>2</body_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion_a>0,0,1</insertion_a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <insertion_b>0,0,3</insertion_b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <f0 lc=\"1\">1</f0>\n\\end_layout\n\n\\begin_layout LyX-Code\n</rigid_connector>\n\\end_layout\n\n\\begin_layout Section\nLoads Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Loads-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nLoads \n\\shape default\nsection defines all nodal,\n edge,\n surface,\n and body loads that can be applied to the model.\n\\end_layout\n\n\\begin_layout Subsection\nNodal Loads\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Nodal-Loads\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNodal loads are applied by the \n\\shape italic\nnodal_load \n\\shape default\nelement.\n The nodal load is defined by the \n\\emph on\ntype \n\\emph default\nattribute.\n The following table lists the available nodal loads.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal_load\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA load that is added to the corresponding dofs in the global force vector.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal_force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA force applied to each node of a node set\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal_target_force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA force that ramps the current nodal load to a target value.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nnodal_load\n\\end_layout\n\n\\begin_layout Standard\nThe nodal_load adds a value directly to the corresponding degrees of freedom in the global external load vector.\n When the loads are applied to displacement degrees of freedom,\n the forces always point in the same direction and do not deform with the geometry (i.e.\n they are non-follower forces).\n For other degrees of freedom they define a constant normal flux.\n\\end_layout\n\n\\begin_layout LyX-Code\n<nodal_load type=\"nodal_load\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n</nodal_load>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ndof\n\\shape default\n parameter defines the degree of freedom.\n The following values are allowed:\n\\end_layout\n\n\\begin_layout Description\nx apply force in \n\\begin_inset Formula $x$\n\\end_inset\n\n-direction\n\\end_layout\n\n\\begin_layout Description\ny apply force in \n\\begin_inset Formula $y$\n\\end_inset\n\n-direction\n\\end_layout\n\n\\begin_layout Description\nz apply force in \n\\begin_inset Formula $z$\n\\end_inset\n\n-direction\n\\end_layout\n\n\\begin_layout Description\np apply normal volumetric fluid flow rate\n\\end_layout\n\n\\begin_layout Description\nc\n\\begin_inset Formula $n$\n\\end_inset\n\n apply normal molar solute flow rate\n\\end_layout\n\n\\begin_layout Description\nt apply normal heat flux (heat transfer analysis)\n\\end_layout\n\n\\begin_layout Standard\nFor solutes,\n replace \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\begin_inset Formula $n$\n\\end_inset\n\n\n\\begin_inset Quotes erd\n\\end_inset\n\n with the solute id from the global solute table (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n);\n for example,\n \n\\begin_inset Quotes eld\n\\end_inset\n\nc2\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nAn optional \n\\shape italic\nloadcurve\n\\shape default\n can be specified for the \n\\shape italic\nscale \n\\shape default\nparameter with the \n\\shape italic\nlc\n\\shape default\n attribute.\n\\end_layout\n\n\\begin_layout Subsubsection\nnodal_force\n\\end_layout\n\n\\begin_layout Standard\nThis nodal load defines a force that is applied to each node in the node set.\n The \n\\emph on\nvalue \n\\emph default\nparameter sets the force vector.\n An optional load curve can be defined to scale the vector.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<nodal_load type=\"nodal_force\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1,0,0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n</nodal_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nnodal_target_force\n\\end_layout\n\n\\begin_layout Standard\nThis nodal load defines a force that is applied to each node in the node set.\n The force will ramp up from the value at the end of the last time step,\n to the desired value defined in the \n\\emph on\nforce \n\\emph default\nvariable.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<nodal_load type=\"nodal_force_target\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force>1,0,0</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n</nodal_load>\n\\end_layout\n\n\\begin_layout Standard\nNote that,\n in order to reach the target load,\n the loadcurve assigned to \n\\emph on\nscale\n\\emph default\n must start at 0 and end at 1.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nNodal Fluidflux\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nnodal fluidflux\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n boundary condition implements an equivalent nodal force load.\n This load will be applied directly to the load vector of the system.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe applied flux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout LyX-Code\n<nodal_load type=\"nodal fluidflux\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</nodal_load>\n\\end_layout\n\n\\begin_layout Subsection\nSurface Loads\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Surface-Loads\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA surface load can be applied using the \n\\shape italic\nsurface_load \n\\shape default\nelement.\n This element takes two attributes,\n namely \n\\shape italic\ntype\n\\shape default\n,\n which defines the type of surface load that will be applied,\n and \n\\shape italic\nsurface \n\\shape default\nwhich defines the surface that this load will be applied to.\n\\end_layout\n\n\\begin_layout Subsubsection\nPressure Load\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Pressure-Load\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPressure loads are applied to the surface of the geometry and are defined by the \n\\shape italic\nsurface_load \n\\shape default\nelement with the type attribute set to \n\\shape italic\npressure\n\\shape default\n.\n These pressure forces are also known as \n\\shape italic\nfollower forces\n\\shape default\n;\n they change direction as the body is deformed and,\n in this case,\n are always oriented along the local surface normal.\n The sign convention is so that a positive pressure will act opposite to the normal,\n so it will compress the material.\n The surface that the load is applied to is specified with the \n\\emph on\nsurface \n\\emph default\nattribute.The surface must be defined in the \n\\emph on\nMesh\n\\shape italic\n\\emph default\n \n\\shape default\nsection.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe applied pressure values (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 [\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric_stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric stiffness flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlinear flag (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell_bottom\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell bottom flag (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\npressure \n\\shape default\nelement defines the pressure value [\n\\series bold\nP\n\\series default\n].\n The optional parameter \n\\shape italic\nlc\n\\shape default\n defines a loadcurve for the pressure evolution.\n If \n\\shape italic\nlc\n\\shape default\n is not defined a constant pressure is applied.\n\\end_layout\n\n\\begin_layout Enumerate\nThe optional \n\\emph on\nsymmetric_stiffness\n\\emph default\n parameter (0 or 1) determines whether to use the exact form of the stiffness matrix for this surface load (0=non-symmetric),\n or the symmetric form (1=default).\n The non-symmetric form is numerically more robust,\n but it may also require using a globally non-symmetric stiffness matrix (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Parameters-Solid-Analysis\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Enumerate\nIf the optional \n\\emph on\nlinear \n\\emph default\nparameter is set to 1,\n a constant,\n deformation independent pressure load is applied.\n This is not recommend for large deformations.\n \n\\end_layout\n\n\\begin_layout Enumerate\nWhen the \n\\emph on\nshell_bottom\n\\emph default\n flag is set to 1,\n the surface is assumed to be a shell surface and the pressure is applied to the bottom part of the shell surface.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"pressure\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pressure lc=\"1\">1.0</pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <symmetric_stiffness>0</symmetric_stiffness>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nTraction Load\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Traction-Load\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA traction load applies a traction to a surface.\n The direction of the traction remains unchanged as the mesh deforms.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <traction>0,0,1</traction>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe traction vector is determined by two quantities.\n The direction and magnitude is defined by the \n\\shape italic\ntraction \n\\shape default\nelement.\n In addition,\n the magnitude can be scaled using the \n\\shape italic\nscale \n\\shape default\nelement.\n An optional load curve can be defined for the \n\\shape italic\nscale \n\\shape default\nelement using the \n\\shape italic\nlc \n\\shape default\nattribute.\n This allows the traction load to become time dependent.\n If the \n\\shape italic\nlc \n\\shape default\nattribute is omitted a constant traction load is applied.\n\\end_layout\n\n\\begin_layout Subsubsection\nSurface Force\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Surface-Force\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA surface force applies an equivalent uniform traction load to a surface.\n The direction of the force remains unchanged as the mesh deforms.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"force\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force>0,0,1</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe force vector is determined by two quantities.\n The direction and magnitude is defined by the \n\\shape italic\nforce \n\\shape default\nelement.\n In addition,\n the magnitude can be scaled using the \n\\shape italic\nscale \n\\shape default\nelement.\n An optional load curve can be defined for the \n\\shape italic\nscale \n\\shape default\nelement using the \n\\shape italic\nlc \n\\shape default\nattribute.\n This allows the surface force to become time dependent.\n If the \n\\shape italic\nlc \n\\shape default\nattribute is omitted a constant force is applied.\n\\end_layout\n\n\\begin_layout Subsubsection\nBearing Load\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Bearing-Load\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA bearing load can be applied on a cylindrical surface that represents a bearing journal,\n or the portion of a shaft that is supported by a bearing journal.\n This load gets prescribed as a non-uniform compressive pressure distribution on that surface,\n representing the radial component of the bearing load.\n (The axial component of a bearing load may be prescribed using the surface force presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Surface-Force\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.) The non-uniform pressure distribution may be sinusoidal or parabolic.\n It gets distributed over a ±90 degree arc relative to the loading direction,\n which is specified as a force vector.\n The user is expected to specify the bearing force in a plane perpendicular to the axis of the cylindrical bearing surface.\n Otherwise,\n only the projection of the bearing force onto that plane will be prescribed as the radial bearing load.\n For example,\n a bearing load may be specified as\n\\end_layout\n\n\\begin_layout Standard\n<surface_load type=\"bearing load\" surface=\"BearingSurface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1000.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <force>0.707107,0.707107,0</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <profile>1</profile>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nIn this example the bearing load is in the xy-plane,\n oriented at 45 degrees from the x-axis.\n The surface \n\\emph on\nBearingSurface1\n\\emph default\n should be a cylinder with its axis along the z-direction.\n The pressure distribution is parabolic (profile=1),\n and the alternative option is sinusoidal (profile=0).\n In general,\n a parabolic distribution produces a higher (often more realistic) central peak pressure on the bearing surface.\n An optional loadcurve can be associated with the \n\\emph on\nscale\n\\emph default\n as shown in this example,\n to scale the prescribed bearing force as a function of time.\n Alternatively,\n the user may assign a loadcurve to the \n\\emph on\nforce\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout Standard\nAdditional options are available for a bearing load,\n including \n\\emph on\nsymmetric_stiffness\n\\emph default\n,\n \n\\emph on\nlinear\n\\emph default\n and \n\\emph on\nshell_bottom\n\\emph default\n as outlined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Pressure-Load\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n When the \n\\emph on\nlinear\n\\emph default\n flag is set to false,\n the pressure prescribed on the bearing surface is adjusted to account for the change in area and orientation of each face,\n caused by the finite deformation of the bearing surface.\n However,\n the bearing radial force direction remains unchanged with deformation.\n It is recommended to set the \n\\emph on\nsymmetric_stiffness\n\\emph default\n flag to false when performing a finite deformation analysis.\n\\end_layout\n\n\\begin_layout Standard\nThe bearing load is always prescribed as a compressive load on the selected bearing surface.\n Therefore,\n it is recommended to select a complete cylindrical surface on which the bearing load is prescribed;\n the solver automatically determines which faces of the selected surface must be loaded in compression and which of those have zero pressure prescribed on them,\n based on the vectorial orientation of the bearing \n\\emph on\nforce\n\\emph default\n.\n If the user selects a semi-cylindrical surface as the bearing surface,\n it becomes the user's responsibility to ensure that the prescribed bearing force bisects the 180 degree arc of that surface.\n Otherwise,\n the analysis may produce unexpected results,\n due to unevenness of the pressure distribution about the force's line of action.\n If the user selects a surface with a smaller arc than ±90 degrees,\n the pressure will not reduce to zero at the straight edges of the surface,\n though the pressure will be scaled to produce the prescribed force magnitude along the bearing force direction.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nPipette Aspiration\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Pipette-Aspiration\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPipette aspiration is a common tool in biomechanics:\n A pipette is pressed against a tissue surface (contact problem) and a known negative pressure is typically applied through the pipette to aspirate the tissue.\n Then,\n some relevant material property may be extracted from the observed aspirated height of the tissue under the prescribed pressure.\n This pressure load is similar to the pressure load described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Pressure-Load\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n however \n\\emph on\nit must be used on a surface which is also the primary surface\n\\emph default\n in a \n\\emph on\nsliding-elastic\n\\emph default\n contact interface (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Sliding-Interfaces\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The contact interface must be between a \n\\emph on\ncircular\n\\emph default\n pipette with inner radius \n\\emph on\n\n\\begin_inset Formula $R$\n\\end_inset\n\n\n\\emph default\n and the tissue.\n (In other words,\n the pipette has to be modeled explicitly in this contact analysis).\n The purpose of this \n\\emph on\npipette aspiration\n\\emph default\n load is to prescribe the pressure only on the faces of the selected contact surface which happen to be inside the pipette footprint.\n Therefore,\n the pressure is not applied on the portion of the contact surface which has a non-zero contact pressure,\n or on the faces that fall outside of the pipette footprint.\n The parameters that need to be defined for this pressure load are:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe applied pressure values (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 [\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nradius\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe inner radius \n\\begin_inset Formula $R$\n\\end_inset\n\n of the pipette\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 [\n\\series bold\nL\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncenter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA vector representing the position of the center of the circular pipette footprint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0 [\n\\series bold\nL\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormal\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA vector represent the normal direction representing the pipette axis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis pressure load only works with circular pipettes.\n However,\n the pipette wall thickness is only modeled via the explicit representation of the pipette,\n and the pipette tip need not be flat (e.g.,\n it could be semi-circular or beveled,\n etc.).\n The radius \n\\begin_inset Formula $R$\n\\end_inset\n\n represents the circular edge of the contact region in the absence of deformation (in the reference condition).\n The model may employ planes of symmetry,\n since those do not affect the specification of \n\\emph on\nradius\n\\emph default\n,\n \n\\emph on\ncenter\n\\emph default\n,\n or \n\\emph on\nnormal\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nMixture Normal Traction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mixture-Normal-Traction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section applies to biphasic,\n biphasic-solute,\n triphasic and multiphasic analyses.\n In a mixture of intrinsically incompressible solid and fluid constituents,\n the formulation adopted in FEBio implies that the total traction is a natural boundary condition (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://help.febio.org/docs/FEBioTheory-4-5/TM45-Section-3.2.html}{\n\\backslash\nemph{FEBio Theory \n\\end_layout\n\n\\begin_layout Plain Layout\n\nManual}}\n\\end_layout\n\n\\end_inset\n\n).\n If this boundary condition is not explicitly prescribed,\n the code automatically assumes that it is equal to zero.\n Therefore,\n boundaries of mixtures are traction-free by default.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nmixture traction \n\\shape default\n\n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n is the traction vector corresponding to the mixture (or total) stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n;\n thus \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is the outward unit normal to the boundary surface.\n Since \n\\begin_inset Formula $\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure and \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the \n\\shape italic\neffective stress\n\\shape default\n resulting from strains in the solid matrix,\n it is also possible to represent the total traction as \n\\begin_inset Formula $\\mathbf{t}=-p\\mathbf{n}+\\mathbf{t}^{e}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{t}^{e}=\\boldsymbol{\\sigma}^{e}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the \n\\shape italic\neffective traction\n\\shape default\n.\n Currently,\n FEBio allows the user to specify only the normal component of the traction,\n either \n\\begin_inset Formula $t_{n}=\\mathbf{t}\\cdot\\mathbf{n}$\n\\end_inset\n\n (the normal component of the mixture traction) or \n\\begin_inset Formula $t_{n}^{e}=\\mathbf{t}^{e}\\cdot\\mathbf{n}$\n\\end_inset\n\n (the normal component of the effective traction):\n\\end_layout\n\n\\begin_layout Standard\nA mixture normal traction is defined by the \n\\shape italic\nsurface_load \n\\shape default\nelement using \n\\shape italic\nnormal_traction \n\\shape default\nfor the type attribute.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"normal_traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <traction [lc=\"1\"]>1.0</traction>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <effective>1</effective>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <linear>0</linear>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntraction \n\\shape default\nelement defines the magnitude of the traction force.\n The optional attribute \n\\shape italic\nlc \n\\shape default\ndefines a loadcurve that controls the time dependency of the traction force magnitude.\n If omitted a constant traction is applied.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\neffective \n\\shape default\nelement defines whether the traction is applied as an effective traction or a total mixture traction.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nlinear \n\\shape default\nelement defines whether the traction remains normal to the deformed surface or the reference surface.\n If set to true the traction remains normal to the reference surface.\n When false it defines a follower force that remains normal to the deformed surface.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nsurface \n\\shape default\nelement defines the surface to which the traction is applied.\n It consists of child elements defining the individual surface facets.\n\\end_layout\n\n\\begin_layout Standard\nUnlike the mixture and effective traction,\n the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is a nodal variable (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Zero-Displacement\"\nnolink \"false\"\n\n\\end_inset\n\n).\n There may be common situations where the user must apply a combination of related fluid pressure and traction boundary conditions.\n For example,\n if a biphasic surface is subjected to a non-zero fluid pressure \n\\begin_inset Formula $p_{0}$\n\\end_inset\n\n,\n the corresponding boundary conditions are \n\\begin_inset Formula $p=p_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $t_{n}=-p_{0}$\n\\end_inset\n\n (or \n\\begin_inset Formula $t_{n}^{e}=0$\n\\end_inset\n\n).\n In FEBio,\n both boundary conditions must be applied.\n For example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prescribed bc=\"p\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </prescribed>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <surface_load type=\"normal_traction\" surface=\"surf1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <traction lc=\"1\">-1.0</traction>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <effective>0</effective>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <linear>0</linear>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </surface_load>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcontroller id=\"1\" type=\"loadcurve\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <loadpoint>0,0</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <loadpoint>1,2.0</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Flux\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a biphasic mixture of intrinsically incompressible solid and fluid constituents,\n the \n\\begin_inset Formula $\\mathbf{u}-p$\n\\end_inset\n\n formulation adopted in FEBio implies that the normal component of the relative fluid flux is a natural boundary condition.\n If this boundary condition is not explicitly prescribed,\n the code automatically assumes that it is equal to zero.\n Therefore,\n biphasic boundaries are impermeable by default.\n (To implement a free-draining boundary,\n the fluid pressure nodal degrees of freedom should be set to zero.)\n\\end_layout\n\n\\begin_layout Standard\nThe flux of fluid relative to the solid matrix is given by the vector \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n.\n Since viscosity is not explicitly modeled in a biphasic material,\n the tangential component of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n on a boundary surface may not be prescribed.\n Only the normal component of the relative fluid flux,\n \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n represents a natural boundary condition.\n To prescribe a value for \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n on a surface,\n use:\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluidflux\" surface=\"surf1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flux lc=\"1\">1.0</flux>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <linear>0</linear>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mixture>0</mixture>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nflux \n\\shape default\nparameter defines the flux that will be applied to the surface.\n The optional parameter \n\\shape italic\nlc\n\\shape default\n defines a loadcurve for the normal flux evolution.\n If omitted a constant fluid flux is applied.\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\shape italic\nlinear \n\\shape default\nis set to zero (default) it means that the flux matches the prescribed value even if the surface on which it is applied changes in area as it deforms.\n Therefore,\n the net volumetric flow rate across the surface changes with changes in area.\n This type of boundary condition is useful if the fluid flux is known in the current configuration.\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\shape italic\nlinear \n\\shape default\nis set to non-zero it means that the prescribed flux produces a volumetric flow rate based on the undeformed surface area in the reference configuration.\n Therefore,\n the flux in the current configuration does not match the prescribed value.\n This type of boundary condition is useful if the net volumetric flow rate across the surface is known.\n For example:\n Let \n\\begin_inset Formula $Q$\n\\end_inset\n\n be the known volumetric flow rate,\n let \n\\begin_inset Formula $A_{\\mathrm{0}}$\n\\end_inset\n\n be the surface area in the reference configuration (a constant).\n Using \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nlinear\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n means that the user prescribes \n\\begin_inset Formula $Q$\n\\end_inset\n\n/\n\\begin_inset Formula $A_{\\mathrm{0}}$\n\\end_inset\n\n as the flux boundary condition.\n (However,\n regardless of the \n\\shape italic\ntype\n\\shape default\n,\n the fluid flux saved in the output file has a normal component equal to \n\\begin_inset Formula $Q$\n\\end_inset\n\n/\n\\begin_inset Formula $A$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $A=$\n\\end_inset\n\narea in current configuration.)\n\\end_layout\n\n\\begin_layout Standard\nPrescribing \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n on a free surface works only if the nodal displacements of the corresponding faces are also prescribed.\n If the nodal displacements are not known a priori,\n the proper boundary condition calls for prescribing the normal component of the mixture velocity,\n \n\\begin_inset Formula $v_{n}=\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)\\cdot\\mathbf{n}$\n\\end_inset\n\n.\n To prescribe the value of \n\\begin_inset Formula $v_{n}$\n\\end_inset\n\n on a surface,\n use\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluidflux\" surface=\"surf1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flux lc=\"1\">1.0</flux>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <linear>0</linear>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mixture>1</mixture>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n this boundary condition may be used when modeling a permeation problem through a biphasic material,\n when the upstream fluid velocity is prescribed,\n \n\\begin_inset Formula $v_{n}=v_{0}$\n\\end_inset\n\n.\n If the upstream face is free,\n the companion boundary condition would be to let \n\\begin_inset Formula $t_{n}^{e}=0$\n\\end_inset\n\n on that face as well.\n\\end_layout\n\n\\begin_layout Subsubsection\nMultiphasic Solute Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solute-Flux\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn biphasic-solute and multiphasic analyses the molar flux of a solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n given by the vector \n\\begin_inset Formula $\\mathbf{j}^{\\iota}=\\varphi^{w}c^{\\iota}\\left(\\mathbf{v}^{\\iota}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n,\n is evaluated relative to the solid matrix;\n it includes a diffusive component as well as a convective component contributed by the fluid flux \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n relative to the solid,\n see eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bs-fluxes-w-j\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Since solute viscosity is not explicitly modeled,\n the tangential component of \n\\begin_inset Formula $\\mathbf{j}^{\\iota}$\n\\end_inset\n\n on a boundary surface may not be prescribed.\n Only the normal component of the relative solute flux,\n \n\\begin_inset Formula $j_{n}^{\\iota}=\\mathbf{j}^{\\iota}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n represents a natural boundary condition.\n Since a multiphasic mixture may contain electrically charged solutes,\n the solute flux that must be prescribed as a boundary condition is the effective solute flux \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}=j_{n}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}j_{n}^{\\gamma}$\n\\end_inset\n\n,\n as explained in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Effective-Solute-Flux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n To prescribe a value for \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}$\n\\end_inset\n\n on a surface,\n use:\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"soluteflux\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flux lc=\"1\">1.0</flux>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <linear>0</linear>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solute_id>solute_name</solute_id>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <shell_bottom>0</shell_bottom>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe parameter \n\\shape italic\nsolute_id\n\\shape default\n specifies to which solute this flux condition applies,\n referencing the corresponding list of solute names in the Globals section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nflux \n\\shape default\nelement defines the flux magnitude.\n The optional parameter \n\\shape italic\nlc\n\\shape default\n defines a loadcurve for the normal flux evolution.\n If omitted a constant flux is applied.\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\shape italic\nlinear \n\\shape default\nis set to 0 (default) it means that the flux matches the prescribed value even if the surface on which it is applied changes in area as it deforms.\n Therefore,\n the net molar flow rate across the surface changes with changes in area.\n This type of boundary condition is useful if the solute molar flux is known in the current configuration.\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\shape italic\nlinear \n\\shape default\nis set to non-zero it means that the prescribed flux produces a molar flow rate based on the undeformed surface area in the reference configuration.\n Therefore,\n the flux in the current configuration does not match the prescribed value.\n This type of boundary condition is useful if the net molar flow rate across the surface is known.\n For example:\n Let \n\\begin_inset Formula $Q$\n\\end_inset\n\n be the known molar flow rate (in units of moles per time [\n\\series bold\nn\n\\series default\n/\n\\series bold\nt\n\\series default\n]),\n let \n\\begin_inset Formula $A_{\\mathrm{0}}$\n\\end_inset\n\n be the surface area in the reference configuration (a constant).\n Using \n\\shape italic\nlinear\n\\shape default\n means that the user prescribes \n\\begin_inset Formula $Q$\n\\end_inset\n\n/\n\\begin_inset Formula $A_{\\mathrm{0}}$\n\\end_inset\n\n as the flux boundary condition (in units of moles per area per time [\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]).\n However,\n regardless of the \n\\shape italic\ntype\n\\shape default\n,\n the solute molar flux saved in the output file has a normal component equal to \n\\begin_inset Formula $Q$\n\\end_inset\n\n/\n\\begin_inset Formula $A$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $A=$\n\\end_inset\n\narea in current configuration.\n The \n\\emph on\nshell_bottom\n\\emph default\n tag should be set to 1 (true) when prescribing the flux on the bottom surface of a multiphasic shell.\n\\end_layout\n\n\\begin_layout Subsubsection\nSolute Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solute-Flux-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn fluid-solutes analyses the molar flux of solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n given by the vector \n\\begin_inset Formula $\\mathbf{j}^{\\iota}=c^{\\iota}\\left(\\mathbf{v}^{\\iota}-\\mathbf{v}^{f}\\right)$\n\\end_inset\n\n,\n is evaluated relative to the solvent (fluid);\n it only includes a diffusive contribution relative to the solvent.\n Since solute viscosity is not explicitly modeled,\n the tangential component of \n\\begin_inset Formula $\\mathbf{j}^{\\iota}$\n\\end_inset\n\n on a boundary surface may not be prescribed.\n Only the normal component of the relative solute flux,\n \n\\begin_inset Formula $j_{n}^{\\iota}=\\mathbf{j}^{\\iota}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n represents a natural boundary condition.\n Since a fluid-solutes mixture may contain electrically charged solutes,\n the solute flux that must be prescribed as a boundary condition is the effective solute flux \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}=j_{n}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}j_{n}^{\\gamma}$\n\\end_inset\n\n,\n such as that in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:normal-effective-solute-flux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n To prescribe a value for \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}$\n\\end_inset\n\n on a surface,\n use:\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"solute flux\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flux lc=\"1\">1.0</flux>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solute_id>solute_name</solute_id>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe parameter \n\\shape italic\nsolute_id\n\\shape default\n specifies to which solute this flux condition applies,\n referencing the corresponding list of solute names in the Globals section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nflux \n\\shape default\nelement defines the flux magnitude.\n The optional parameter \n\\shape italic\nlc\n\\shape default\n defines a loadcurve for the normal flux evolution.\n If omitted a constant flux is applied.\n\\end_layout\n\n\\begin_layout Subsubsection\nMultiphasic Solute Natural Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solute-Natural-Flux\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis surface load can be prescribed on a multiphasic boundary where a natural solute flux is desired.\n On this boundary,\n the solute is convected with the solvent according to \n\\begin_inset Formula $j_{n}=cw_{n}$\n\\end_inset\n\n.\n The implication of this surface load is that there is no diffusive hindrance to solute transport on the external side of the boundary.\n Here,\n \n\\begin_inset Formula $c$\n\\end_inset\n\n is the average solute concentration and \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n is the average solvent volumetric flux in the finite element connected to the boundary face on which this surface load is prescribed.\n The normal solvent flux \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n is evaluated as \n\\begin_inset Formula $\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n using the normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n on the boundary.\n There are no user-specified parameters in this surface load (other than specifying the solute for which it applies,\n and whether the boundary is the bottom of a shell domain).\n To prescribe a solute natural flux on a surface,\n use:\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"solute natural flux\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solute_id>solute_name</solute_id>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <shell_bottom>0</shell_bottom>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <update>0</update>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe parameter \n\\shape italic\nsolute_id\n\\shape default\n specifies to which solute this flux condition applies,\n referencing the corresponding list of solute names in the Globals section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The parameter \n\\emph on\nupdate\n\\emph default\n is a boolean flag (0=\n\\emph on\nfalse\n\\emph default\n,\n 1=\n\\emph on\ntrue\n\\emph default\n,\n default is \n\\emph on\nfalse\n\\emph default\n) which,\n when set to \n\\emph on\ntrue\n\\emph default\n,\n initializes the effective solute concentration of surface nodes to the average value of concentrations within the underlying elements,\n at each iteration.\n You may set this flag to \n\\emph on\ntrue\n\\emph default\n when the iterative numerical solution fails to converge.\n This surface load is only needed for biphasic-solute and multiphasic analyses,\n since \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}=0$\n\\end_inset\n\n is the natural boundary condition for fluid-solutes analyses.\n\\end_layout\n\n\\begin_layout Subsubsection\nHeat Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Heat-Flux\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA heat flux can be defined for heat transfer analyses using the \n\\shape italic\nsurface_load \n\\shape default\nelement with type set to \n\\shape italic\nheatflux \n\\shape default\nelement.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"heatflux\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flux lc=\"1\">1.0</flux>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe heat flux element takes two parameters,\n namely \n\\shape italic\nflux\n\\shape default\n which defines the flux that will be applied.\n It has an optional \n\\shape italic\nlc \n\\shape default\nattribute which defines the load curve for this parameter.\n If omitted,\n a constant heat flux will be applied.\n The other (optional) parameter is the \n\\shape italic\nvalue \n\\shape default\nparameter which can be used to define a different flux value for each surface facet.\n\\end_layout\n\n\\begin_layout Subsubsection\nConvective Heat Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Convective-Heat-Flux\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA convective heat flux can be defined to a surface that is cooled (or heated) by exposure to an ambient temperature.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"convective_heatflux\" surface=\"surf1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Ta lc=\"1\">1.0</Ta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <hc>1.0</hc>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nhc \n\\shape default\nparameter defines the heat transfer coefficient.\n The ambient temperature is defined by the \n\\shape italic\nTa \n\\shape default\nparameter.\n It takes an optional load curve defined through the \n\\shape italic\nlc \n\\shape default\nattribute.\n The \n\\shape italic\nvalue \n\\shape default\nparameter with the \n\\shape italic\nsurface_data \n\\shape default\nattribute can be defined to define a different temperature value for each surface facet (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:generator\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Traction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Traction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis,\n the Cauchy stress is given by \n\\begin_inset Formula $\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is the elastic pressure and \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the viscous stress.\n The traction on a fluid surface with outward normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is given by \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}=-p\\mathbf{n}+\\mathbf{t}^{\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the viscous component of the traction.\n The \n\\emph on\nfluid traction\n\\emph default\n surface load prescribes \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n on a boundary surface.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <traction>0,0,1</traction>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Pressure\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis,\n the Cauchy stress is given by \n\\begin_inset Formula $\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is the elastic pressure and \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the viscous stress.\n The \n\\emph on\nfluid pressure\n\\emph default\n surface load prescribes \n\\begin_inset Formula $p$\n\\end_inset\n\n on a boundary surface.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid pressure\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pressure>0.05</pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition internally solves for the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n for the user-prescribed pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n using the user-selected equation of state from the list given in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Then \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\nis prescribed as an essential boundary condition on the selected surface.\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Normal Traction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Normal-Traction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis,\n the normal component \n\\begin_inset Formula $t_{n}^{\\tau}=\\mathbf{t}^{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n of the viscous traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n) may be prescribed on a boundary surface in a fluid analysis.\n The\n\\emph on\n fluid normal traction\n\\emph default\n surface load prescribes \n\\begin_inset Formula $t_{n}^{\\tau}$\n\\end_inset\n\n on a boundary surface.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid normal traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <traction>1</traction>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Backflow Stabilization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Backflow-Stabilization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis,\n this boundary condition prescribes the normal component of the viscous traction (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n),\n \n\\begin_inset Formula $t_{n}^{\\tau}=\\mathbf{t}^{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n such that it opposes backflow,\n i.e.,\n when \n\\begin_inset Formula $v_{n}=\\mathbf{v}\\cdot\\mathbf{n}$\n\\end_inset\n\n is negative.\n Specifically,\n \n\\begin_inset Formula $t_{n}^{\\tau}=\\beta\\rho_{r}v_{n}^{2}$\n\\end_inset\n\n when \n\\begin_inset Formula $v_{n}<0$\n\\end_inset\n\n and \n\\begin_inset Formula $0$\n\\end_inset\n\n otherwise,\n where \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is a unitless user-defined parameter (typical values may range from 0 to 1) and \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is the referential fluid density.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid backflow stabilization\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<beta lc=\"1\">1</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Tangential Stabilization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Tangential-Stabilization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis,\n this boundary condition prescribes the tangential component of the viscous traction (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n),\n \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{t}^{\\tau}$\n\\end_inset\n\n,\n such that it opposes the tangential fluid flow \n\\begin_inset Formula $\\mathbf{v}_{t}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{v}$\n\\end_inset\n\n on the selected boundary surface,\n with \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n representing the outward normal to this surface.\n Specifically,\n \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}=-\\beta\\rho_{r}\\left|\\mathbf{v}_{t}\\right|\\mathbf{v}_{t}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is a unitless user-defined parameter (typical values may range from 0 to 1) and \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is the referential fluid density.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid tangential stabilization\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<beta lc=\"1\">1</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Tangential Damping\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nfluid tangential damping\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n prescribes a shear traction that opposes tangential fluid velocity on a boundary surface.\n This can help stabilize inflow conditions.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndamping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid tangential damping\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<penalty>1</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Viscous Traction\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nfluid viscous traction\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n is a fluid surface that has a prescribed viscous traction vector on it.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nScale factor for the load\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 []\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe traction vector that will be applied\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0 [P]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Normal Velocity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Normal-Velocity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis the fluid velocity relative to the mesh,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n on a boundary surface with outward normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n may be decomposed into the normal component,\n \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n and tangential component,\n \n\\begin_inset Formula $\\mathbf{w}_{\\tau}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{w}=\\mathbf{w}-w_{n}\\mathbf{n}$\n\\end_inset\n\n.\n The surface load \n\\emph on\nfluid normal velocity\n\\emph default\n may be used to prescribe a desired value of \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n on a boundary surface.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid normal velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value lc=\"1\">1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity>1</velocity>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nOptionally,\n FEBio can generate a parabolic velocity profile over the named surface,\n This option can be turned on using the \n\\emph on\nparabolic\n\\emph default\n element (0=default,\n 1=parabolic).\n In this case,\n the \n\\emph on\nvelocity\n\\emph default\n element provides the average value of this parabolic velocity profile,\n while \n\\emph on\nvalue\n\\emph default\n remains a scale factor optionally tied to a loadcurve.\n The parabolic profile is calculated under the assumption of steady Poiseuille flow in an infinite tube whose cross-section has the shape of the named surface.\n The parabolic velocity distribution reduces to zero only on portions of the boundary curve of the named surface where the user has fixed all three components of the fluid velocity (no-slip condition).\n On remaining portions of this boundary curve,\n symmetry conditions are assumed for the three-dimensional parabolic normal velocity profile.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid normal velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity lc=\"1\">-1.0</velocity>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <parabolic>1</parabolic>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nIf an arbitrary distribution with different scalar value needs to be specified for each facet,\n use the following syntax,\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid normal velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value surface_data=\"inlet\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity lc=\"1\">-1.0</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nand provide the facet values in a user-defined \n\\emph on\nSurfaceData\n\\emph default\n map with \n\\emph on\nname=\"inlet\"\n\\emph default\n and \n\\emph on\ndata_type=\"scalar\"\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:generator\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n this condition only prescribes the natural boundary condition \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n,\n leaving \n\\begin_inset Formula $\\mathbf{w}_{\\tau}$\n\\end_inset\n\n free.\n Optionally,\n it is possible to also enforce \n\\begin_inset Formula $\\mathbf{w}_{\\tau}=\\mathbf{0}$\n\\end_inset\n\n by prescribing essential boundary conditions on the wx,\n wy and wz components of \n\\begin_inset Formula $\\mathbf{w}=w_{n}\\mathbf{n}$\n\\end_inset\n\n at the nodes of each facet,\n based on the value of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n at each node (obtained by averaging \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n from adjoining facets).\n This option may be turned on using the \n\\emph on\nprescribe_nodal_velocities\n\\emph default\n element (0=default,\n 1=prescribed),\n for example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid normal velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity>-1.0</velocity>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prescribe_nodal_velocities>1</prescribe_nodal_velocities>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nWhen the fluid is prescribed on a surface boundary,\n better numerical convergence is achieved if the fluid dilatation is also prescribed on the rim (the boundary curve) of that surface boundary.\n While this can be done explicitly,\n a better option is to set the fluid dilatation on the rim to match the average pressure on the surface boundary,\n calculated during the analysis.\n To select this option,\n use the \n\\emph on\nprescribe_rim_pressure\n\\emph default\n element (0=default,\n 1=prescribed),\n for example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid normal velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity>-1.0</velocity>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <parabolic>1</parabolic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prescribe_nodal_velocities>1</prescribe_nodal_velocities>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prescribe_rim_pressure>1</prescribe_rim_pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nNote that this \n\\emph on\nprescribe_rim_pressure \n\\emph default\noption will not work if the mesh of the boundary surface has no internal nodes.\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Velocity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Velocity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid or fluid-FSI analysis,\n this boundary condition prescribes the fluid velocity vector relative to the mesh,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n on a named surface,\n simultaneously satisfying the natural boundary condition for \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n and prescribing essential boundary conditions on the wx,\n wy and wz components of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n at the nodes of each facet,\n based on the value of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n at each node (obtained by averaging \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n from adjoining facets).\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity>0,0,1</velocity>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nIf a different vector value needs to be specified for each facet,\n use the following syntax,\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid velocity\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <velocity surface_data=\"inlet\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Standard\nand provide the facet values in a user-defined \n\\emph on\nSurfaceData\n\\emph default\n map with \n\\emph on\nname=\"inlet\"\n\\emph default\n and \n\\emph on\ndata_type=\"vec3\"\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:generator\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Rotational Velocity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Rotational-Velocity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis boundary condition prescribes a rotational velocity on an axisymmetric surface.\n It is the user's responsibility to ensure that the surface is axisymmetric.\n The angular velocity \n\\begin_inset Formula $\\boldsymbol{\\omega}=\\omega\\mathbf{n}$\n\\end_inset\n\n is prescribed by specifying the angular speed \n\\begin_inset Formula $\\omega$\n\\end_inset\n\n and the unit vector along the axis of rotation (axis of symmetry) \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n.\n The user-defined vector \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is automatically normalized.\n If the axis does not pass through the origin,\n the user may specify any point \n\\begin_inset Formula $\\mathbf{p}$\n\\end_inset\n\n on the axis.\n The fluid velocity relative to the mesh,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n for nodes on the selected surface is evaluated from \n\\begin_inset Formula $\\mathbf{w}=\\boldsymbol{\\omega}\\times\\mathbf{r}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{r}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\left(\\mathbf{x}-\\mathbf{p}\\right)$\n\\end_inset\n\n is the shortest vector from the axis to the node and \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n is the nodal position.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid rotational velocity\" surface=\"FluidRotationalVelocity01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<angular_speed lc=\"1\">1</angular_speed>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<axis>0,0,1</axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<origin>0,0,0</origin>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>//!\n \n\\end_layout\n\n\\begin_layout Standard\nThough this boundary condition may be used in fluid-FSI analyses,\n it is most relevant for standard fluid analyses where the mesh is stationary.\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid-FSI Traction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-FSI-Traction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid-FSI analysis,\n the fluid traction across an interface between a \n\\emph on\nfluid-FSI\n\\emph default\n material and a solid domain must be prescribed explicitly as a traction boundary condition on the solid domain.\n Otherwise,\n the solid domain cannot sense the fluid pressure and viscous traction.\n Users must identify each surface on which this FSI surface load must be prescribed.\n There are no user-defined parameters for this surface load.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"fluid-FSI traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThis surface load must also be prescribed on free fluid surfaces (such as the surface of the fluid in open channel flow) to allow those surfaces to deform in response to the fluid stresses (e.g.,\n producing surface waves).\n (Do not apply this surface load on inlet or outlet boundaries through which fluid flows.)\n\\end_layout\n\n\\begin_layout Subsubsection\nBiphasic-FSI Traction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-FSI-Traction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid-FSI analysis,\n the fluid traction across an interface between a \n\\emph on\nbiphasic-FSI\n\\emph default\n material and a solid domain must be prescribed explicitly as a traction boundary condition on the solid domain.\n Otherwise,\n the solid domain cannot sense the fluid pressure and viscous traction.\n Users must identify each surface on which this FSI surface load must be prescribed.\n There are no user-defined parameters for this surface load.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"biphasic-FSI traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThis surface load must also be prescribed on free fluid surfaces (such as the surface of the fluid in open channel flow) to allow those surfaces to deform in response to the fluid stresses (e.g.,\n producing surface waves).\n (Do not apply this surface load on inlet or outlet boundaries through which fluid flows.)\n\\end_layout\n\n\\begin_layout Subsubsection\nMultiphasic-FSI Traction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:multiphasic-FSI-Traction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid-FSI analysis,\n the fluid traction across an interface between a \n\\emph on\nmultiphasic-FSI\n\\emph default\n material and a solid domain must be prescribed explicitly as a traction boundary condition on the solid domain.\n Otherwise,\n the solid domain cannot sense the fluid pressure and viscous traction.\n Users must identify each surface on which this FSI surface load must be prescribed.\n There are no user-defined parameters for this surface load.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"multiphasic-FSI traction\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load>\n\\end_layout\n\n\\begin_layout Standard\nThis surface load must also be prescribed on free fluid surfaces (such as the surface of the fluid in open channel flow) to allow those surfaces to deform in response to the fluid stresses (e.g.,\n producing surface waves).\n (Do not apply this surface load on inlet or outlet boundaries through which fluid flows.)\n\\end_layout\n\n\\begin_layout Subsubsection\nSolute Backflow Stabilization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solute-Backflow-Stabilization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a fluid-solutes analysis this surface load checks for the presence of solvent (fluid) backflow,\n i.e.,\n when \n\\begin_inset Formula $v_{n}^{f}=\\mathbf{v}^{f}\\cdot\\mathbf{n}$\n\\end_inset\n\n is negative.\n When this condition is encountered,\n the effective concentration of the selected solute (\n\\emph on\nsolute_id\n\\emph default\n) is set to its converged value from the previous time step.\n Otherwise,\n the natural boundary condition \n\\begin_inset Formula $\\tilde{j}_{n}=0$\n\\end_inset\n\n (zero diffusive solute flux relative to the solvent) prevails.\n\\end_layout\n\n\\begin_layout LyX-Code\n<surface_load type=\"solute backflow stabilization\" surface=\"surface1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<solute_id>neutral</solute_id>\n\\end_layout\n\n\\begin_layout LyX-Code\n</surface_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Mixture Viscous Traction\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nfluid mixture viscous traction\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n is a fluid surface that has a prescribed viscous traction vector on it.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nScale factor for the load\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 []\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe traction vector that will be applied\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0,0,0 [P]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPressure Stabilization\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\npressure_stabilization\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n surface load is a pseudo-surface load that is used to calculate the pressure stabilization time constant based on the properties of biphasic elements under that surface.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstabilize\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTurn stabilization on or off (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\non\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nstabilize\n\\emph default\n parameter is currently ignored so adding this feature will always perform the stabilization.\n \n\\end_layout\n\n\\begin_layout Subsection\nBody Loads\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Body-Loads\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA body load can be used to define a source term to the model.\n The following sections define the different type of body loads that can be applied.\n In the case of body forces,\n these body loads represent the body force per mass,\n \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n,\n which appears in the momentum balance as\n\\begin_inset Formula \n\\[\n\\rho\\mathbf{a}=\\divg\\boldsymbol{\\sigma}+\\rho\\mathbf{b}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the mass density,\n \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is the acceleration (set to zero in quasi-static analyses),\n and \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n is the Cauchy stress tensor.\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Body Force\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Body-Force\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis constant body force per mass \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n is defined as a 3D vector.\n Each component can be associated with a load curve to define a time dependent body force.\n Only the non-zero components need to be defined.\n This type of body force is spatially homogeneous,\n though it may vary with time when associated with a load curve:\n\\end_layout\n\n\\begin_layout LyX-Code\n<body_load type=\"const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <x lc=\"1\">1.0</x>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <y lc=\"2\">1.0</y>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <z lc=\"3\">1.0</z>\n\\end_layout\n\n\\begin_layout LyX-Code\n</body_load>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nlc \n\\shape default\nattribute defines the load curve to use for the corresponding component.\n The values of the components can be used to define scale factors for the load values.\n\\end_layout\n\n\\begin_layout Subsubsection\nNon-Constant Body Force\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Non-Constant-Body-Force\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis body force per mass \n\\begin_inset Formula $\\mathbf{b}$\n\\end_inset\n\n may be spatially inhomogeneous.\n The spatial inhomogeneity may be specified using a formula with variables X,\n Y,\n Z.\n For example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<body_load type=\"non-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <x>X+Y+Z</x>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <y>X-Y-Z</y>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <z>X*X+Z</z>\n\\end_layout\n\n\\begin_layout LyX-Code\n</body_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nCentrifugal Body Force\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Centrifugal-Force\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA centrifugal body force per mass \n\\begin_inset Formula $\\mathbf{b}=\\omega^{2}r\\mathbf{e}_{r}$\n\\end_inset\n\n may be used for bodies undergoing steady-state rotation with angular speed \n\\begin_inset Formula $\\omega$\n\\end_inset\n\n about a rotation axis directed along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n and passing through the rotation center \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $r$\n\\end_inset\n\n represents the distance of each material point from the axis of rotation,\n and \n\\begin_inset Formula $\\mathbf{e}_{r}$\n\\end_inset\n\n is the radial unit vector from the axis of rotation,\n both of which are calculated internally from the knowledge of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout LyX-Code\n<body_load type=\"centrifugal\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angular_speed>1.0</angular_speed>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rotation_axis>0.707,0.707,0</rotation_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rotation_center>0,0,0</rotation_center>\n\\end_layout\n\n\\begin_layout LyX-Code\n</body_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nHeat Source\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Heat-Source\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA heat source can be defined using the \n\\shape italic\nheat_source \n\\shape default\ntype.\n\\end_layout\n\n\\begin_layout LyX-Code\n<body_load type=\"heat_source\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Q>1.0</Q>\n\\end_layout\n\n\\begin_layout LyX-Code\n</body_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nSurface Attraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Surface-Attraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA surface attraction body force can be used to attract a mesh toward a boundary surface,\n for the purpose of creating a mesh bias.\n This can be useful for fluid analyses where a finer mesh is needed in the vicinity of a no-slip boundary.\n The benefit of this tool over other existing tools for creating a biased mesh is its ability to work with structured and unstructured meshes,\n as well as linear and higher-order elements.\n\\end_layout\n\n\\begin_layout Standard\nFor each element in the mesh,\n its shortest distance \n\\begin_inset Formula $r$\n\\end_inset\n\n to the selected boundary surface is evaluated using a surface projection algorithm.\n Then,\n an attractive body force per mass,\n\\begin_inset Formula \n\\[\n\\mathbf{b}=s\\exp\\left(-\\frac{r}{\\rho}\\right)\\mathbf{n}\n\\]\n\n\\end_inset\n\nis evaluated along the unit vector \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n directed along the shortest projection distance.\n Here,\n \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is a characteristic boundary layer thickness (\n\\emph on\nblt\n\\emph default\n) and \n\\begin_inset Formula $s$\n\\end_inset\n\n is a scale factor for the body force (\n\\emph on\nbsf\n\\emph default\n),\n whose value may be adjusted relative to account for the stiffness of the elastic solid material assigned to the mesh.\n\\end_layout\n\n\\begin_layout LyX-Code\n<body_load type=\"surface attraction\" elem_set=\"EB1\" surface=\"PressureLoad1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <blt>0.5</blt>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <bsf lc=\"1\">20</bsf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <search_radius>0.1</search_radius>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <search_tol>0.01</search_tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n</body_load>\n\\end_layout\n\n\\begin_layout Standard\nNote that this body force requires the specification of an attractive surface;\n optionally,\n it also accepts the specification of the element set subjected to this attractive body force.\n The \n\\emph on\nsearch_radius\n\\emph default\n and \n\\emph on\nsearch_tol\n\\emph default\n parameters are used for the projection algorithm.\n They have the same meaning as in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Sliding-Interfaces\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nMoving frame\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Moving-frame\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nmoving frame \n\\emph default\nload can be used to model the effects of being embedded in an accelerating and rotating coordinate system.\n Whereas the \n\\emph on\ncentrifugal body force \n\\emph default\nonly emulates the effect of a centrifugal force and assumes a static analysis,\n the moving frame load includes Coriolis effect and the effects of an accelerating coordinate system and can be used in a dynamic analysis.\n \n\\end_layout\n\n\\begin_layout Standard\nThe motion of the moving frame is decomposed into a translation and a rotation.\n The position of a material point \n\\series bold\nr\n\\series default\n in the fixed inertial frame can then be related to the position in the rotating frame \n\\series bold\nr'\n\\series default\n via,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{r}=\\mathbf{Y}+\\mathbf{R}\\mathbf{r\\mathit{'}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\series bold\nY\n\\series default\n is the position of the origin of the moving frame and \n\\series bold\nR\n\\series default\n is the rotation matrix.\n \n\\end_layout\n\n\\begin_layout Standard\nSubstituting this into the equations of motion (i.e.\n the balance of linear momentum),\n results in a modified equation of motion where the effects of the moving frame can be combined into a single body load.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{f}=\\varrho\\left(\\mathbf{R}^{T}\\mathbf{A}+\\mathbf{\\dot{W}}\\times\\mathbf{r}'+\\mathbf{W}\\times\\left(\\mathbf{W}\\times\\mathbf{r}'\\right)+2\\mathbf{W}\\times\\mathbf{v}'\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\series bold\nA\n\\series default\n is the linear acceleration of the moving frame,\n \n\\series bold\nW\n\\series default\n is the angular velocity of the moving frame,\n measured in the moving frame (i.e.\n \n\\begin_inset Formula $\\mathbf{W}=\\mathbf{R}^{T}\\mathbf{w},$\n\\end_inset\n\nwhere \n\\series bold\nw\n\\series default\n is the angular velocity in the fixed frame),\n and \n\\series bold\nv\n\\series default\n' is the velocity of the material point,\n measured in the moving frame.\n \n\\end_layout\n\n\\begin_layout Standard\nThe moving frame is defined via the two vectors,\n the angular velocity of the rotating frame \n\\series bold\nw\n\\series default\n,\n and the linear acceleration of the rotating frame's origin \n\\series bold\nA\n\\series default\n.\n The components of both vectors may be defined via load curves to make them time-dependent.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of linear acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nay\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of linear acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of linear acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote that if you wish to include the effect of gravity,\n do not add a separate body force load.\n Instead,\n modify the linear acceleration to include gravity:\n \n\\begin_inset Formula $\\boldsymbol{a=}A-\\boldsymbol{g}.$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Loads>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <body_load type=\"moving frame\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <wx>0</wx>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <wy>0</wy>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <wz lc=\"1\">1</wz>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ax>0</ax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ay>0</ay>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <az>0</az>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </body_load>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Loads> \n\\end_layout\n\n\\begin_layout Subsubsection\nMass Damping\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\nmass damping\n\\begin_inset Quotes erd\n\\end_inset\n\n \n\\emph default\nbody load applies a body force that is proportional to the linear momentum density \n\\begin_inset Formula $\\mathbf{p}=\\rho\\mathbf{v}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the material density and \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n the velocity.\n An additional scale factor can be used to control the strength of the force,\n \n\\begin_inset Formula $\\mathbf{f}=C\\mathbf{p}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nC\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndamping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Centrifugal Force\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:fluid-centrifugal-force\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA fluid centrifugal body force per mass \n\\begin_inset Formula $\\mathbf{b}=\\omega^{2}r\\mathbf{e}_{r}$\n\\end_inset\n\n may be used for bodies undergoing steady-state rotation with angular speed \n\\begin_inset Formula $\\omega$\n\\end_inset\n\n about a rotation axis directed along \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n and passing through the rotation center \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $r$\n\\end_inset\n\n represents the distance of each material point from the axis of rotation,\n and \n\\begin_inset Formula $\\mathbf{e}_{r}$\n\\end_inset\n\n is the radial unit vector from the axis of rotation,\n both of which are calculated internally from the knowledge of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{c}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout LyX-Code\n<body_load type=\"fluid centrifugal force\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <angular_speed>1.0</angular_speed>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rotation_axis>0.707,0.707,0</rotation_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <rotation_center>0,0,0</rotation_center>\n\\end_layout\n\n\\begin_layout LyX-Code\n</body_load>\n\\end_layout\n\n\\begin_layout Subsubsection\nFluid Moving Frame\n\\end_layout\n\n\\begin_layout Standard\nThis body load is identical to the moving frame load described in \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Moving-frame\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n but applied to a fluid domain.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of linear acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nay\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of linear acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of linear acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nContact Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Contact-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nContact \n\\shape default\nsection defines all the contact interfaces.\n Contact boundary conditions are defined with the \n\\shape italic\ncontact\n\\shape default\n sub-element.\n The \n\\shape italic\ntype\n\\shape default\n attribute specifies the type of contact interface that is defined.\n The \n\\shape italic\nsurface_pair \n\\shape default\nattribute defines the surface pair to use for this contact definition.\n The surface pair is defined the the \n\\emph on\nMesh\n\\shape italic\n\\emph default\n \n\\shape default\nsection.\n For example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<contact type=\"sliding-elastic\" surface_pair=\"contact1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <!-- parameter go here -->\n\\end_layout\n\n\\begin_layout LyX-Code\n</contact>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ntype\n\\shape default\n can be one of the following options:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"2in\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nType\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsliding-node-on-facet,\n sliding-facet-on-facet,\n sliding-elastic,\n sliding-biphasic,\n sliding-biphasic-solute,\n sliding-multiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA sliding interface that may separate (with biphasic contact for \n\\shape italic\nsliding\n\\shape default\n-biphasic,\n biphasic-solute contact for \n\\shape italic\nsliding-biphasic-s\n\\shape default\nolute,\n and multiphasic contact for \n\\shape italic\nsliding-multiphasic\n\\shape default\n) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid_wall\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA sliding interface with rigid wall as secondary surface \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid_joint\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA joint between two rigid bodies \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntied-elastic,\n tied-node-on-facet,\n tied-facet-on-facet,\n sticky,\n tied-biphasic,\n tied-multiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA tied interface (solid-solid,\n solid-rigid,\n solid-biphasic,\n rigid-biphasic) or tied-biphasic interface (biphasic-biphasic).\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact potential\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA potential based sliding contact interface \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nA note on auto-contact\n\\end_layout\n\n\\begin_layout Standard\nThe two surfaces that participate in the contact are specified via a surface pair.\n These surface pairs are defined in the SurfacePair subsection of the Mesh section of the input file.\n Typically the surfaces are chosen based on the user's knowledge of where the contact will occur.\n However,\n for complex geometries it might not be clear where the contact will occur,\n or identifying the surfaces may be cumbersome.\n For this reason,\n FEBio supports an auto-contact option,\n where users only need to specify the parts of the primary and secondary surfaces.\n From these part lists,\n FEBio will extract the surfaces automatically.\n To use the auto-contact,\n simply specify a part list instead of the surface names when defining the surface pair.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<PartList name=\"SlidingElastic1Primary\">Part1</PartList>\n\\end_layout\n\n\\begin_layout LyX-Code\n<PartList name=\"SlidingElastic1Secondary\">Part2</PartList> \n\\end_layout\n\n\\begin_layout LyX-Code\n<SurfacePair name=\"SlidingElastic1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n   <primary>\n\\series bold\n@part_list:SlidingElastic1Primary\n\\series default\n</primary>\n\\end_layout\n\n\\begin_layout LyX-Code\n   <secondary>\n\\series bold\n@part_list:SlidingElastic1Secondary\n\\series default\n</secondary>\n\\end_layout\n\n\\begin_layout LyX-Code\n</SurfacePair> \n\\end_layout\n\n\\begin_layout Standard\nNote the \n\\series bold\n@part_list:\n\n\\series default\n prefix in front of the part list name.\n \n\\end_layout\n\n\\begin_layout Standard\nAlthough using the auto-contact can greatly simply defining contact between parts,\n it is important to keep in mind that there are a few drawbacks.\n First and foremost,\n the surfaces extracted from parts will likely be much larger than the contact area and this will result in a performance cost.\n Second,\n for complex geometries the extract surfaces may have a complex topology that may confuse some of the contact detection algorithms.\n In those situations it might be beneficial to decrease the search_radius parameter (for contact algorithms that define this parameter).\n \n\\end_layout\n\n\\begin_layout Subsection\nSliding Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Sliding-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA sliding interface can be used to setup a non-penetration constraint between two surfaces.\n As of version 1.2,\n three different sliding contact algorithms are available.\n Although all three are based on the same contact enforcement method,\n they all differ slightly in their implementation and have been shown to give different performance for different contact scenarios.\n Each sliding contact implementation is identified by a different \n\\shape italic\ntype \n\\shape default\nattribute.\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nsliding-node-on-facet\n\\begin_inset space ~\n\\end_inset\n\n(N2F)\n\\shape default\n\\emph default\n This is FEBio's original implementation of sliding contact.\n It is based on Laursen's contact formulation \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen95\"\nliteral \"true\"\n\n\\end_inset\n\n which poses the contact problem as a nonlinear constrained optimization problem.\n In FEBio,\n the Lagrange multipliers that enforce the contact constraints are computed either using a penalty method or the augmented Lagrangian method.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nsliding-facet-on-facet\n\\begin_inset space ~\n\\end_inset\n\n(F2F)\n\\shape default\n\\emph default\n This implementation is identical to the \n\\shape italic\nsliding-node-on-facet\n\\shape default\n implementation but uses a more accurate integration rule:\n where the former method uses nodal integration,\n this method uses Gaussian quadrature to integrate the contact equations.\n This method has been demonstrated to give additional stability and often converges when the former method does not.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nsliding-elastic\n\\begin_inset space ~\n\\end_inset\n\n(SE)\n\\shape default\n\\emph default\n This sliding contact interface also uses facet-on-facet contact but differs in the linearization of the contact forces,\n which results in a different contact stiffness matrix compared to the previous two methods.\n It can be used for frictionless or frictional contact.\n It may optionally be set to sustain tension to prevent contact surfaces from separating along the direction normal to the interface,\n while still allowing tangential sliding.\n This method sometimes performs better than the previous two methods for problems that are dominated by compression.\n However,\n the formulation is inherently non-symmetric and therefore will require additional memory and running time.\n A symmetrized version of this implementation is available (see below),\n but the symmetric version does not converge as well as the non-symmetric version.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nsliding-biphasic\n\\begin_inset space ~\n\\end_inset\n\n(SBP)\n\\shape default\n\\emph default\n This method extends the \n\\emph on\nsliding-elastic\n\\emph default\n algorithm to support frictionless biphasic contact (see the next section).\n This method is described in detail in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b\"\nliteral \"true\"\n\n\\end_inset\n\n for the frictionless case,\n and in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman22\"\nliteral \"true\"\n\n\\end_inset\n\n for the frictional algorithm.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nsliding-biphasic-solute\n\\begin_inset space ~\n\\end_inset\n\n(SBS)\n\\shape default\n\\emph default\n This method is similar to \n\\shape italic\nsliding-biphasic\n\\shape default\n.\n This contact implementation supports biphasic-solute contact (see the next section) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian12\"\nliteral \"true\"\n\n\\end_inset\n\n.\n When using biphasic-solute materials,\n the non-symmetric version must be used.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nsliding-multiphasic\n\\begin_inset space ~\n\\end_inset\n\n(SMP)\n\\shape default\n\\emph default\n This method is similar to \n\\shape italic\nsliding-biphasic-solute\n\\shape default\n.\n This contact implementation supports multiphasic contact (see the next section) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian12\"\nliteral \"true\"\n\n\\end_inset\n\n.\n When using multiphasic materials,\n the non-symmetric version must be used.\n \n\\end_layout\n\n\\begin_layout Description\ncontact \n\\series bold\npotential\n\\series default\n \n\\series bold\n(CP)\n\\series default\n The \n\\begin_inset Quotes eld\n\\end_inset\n\ncontact potential\n\\begin_inset Quotes erd\n\\end_inset\n\n contact interface implements the method by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"KAMENSKY2018522\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This method defines a contact potential that generates a (repulsing) force that prevents penetration from occurring.\n This contact formulation is not based on the augmented Lagrangian formulation and does not have any of the parameters in the table below.\n Instead,\n please see section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:contact-potential\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on this method.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following table lists the properties that are defined for sliding interfaces (except contact potential).\n It is important to note that the three different sliding implementations cannot be used interchangeably:\n not all features are available for each method.\n The third,\n fourth and fifth column indicate if a parameter is available for a particular implementation.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"26\" columns=\"8\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"2in\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\" width=\"0.38in\">\n<column alignment=\"center\" valignment=\"top\" width=\"0.38in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nN2F\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nF2F\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nSE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nSBP\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nSBS SMP\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormal penalty scale factor (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdated auto-penalty calculation at each step (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo_pass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo-pass flag (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmented Lagrangian flag (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naug.\n Lagrangian convergence tolerance (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngaptol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance for gap value (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 (off) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminimum number of augmentations (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaxaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of augmentations (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfric_coeff\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfrictional coefficient (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfric_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntangential penalty factor (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nktmult\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntangential stiffness multiplier (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nseg_up\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of segment updates (6)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsmooth_aug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsmoothed Lagrangian augmentation (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsmooth_fls\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsmoothed fluid load support for friction calculation (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact_frac\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolid-on-solid contact area fraction for friction calculation (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric_stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric stiffness matrix flag (8)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nProjection search tolerance (9)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_radius\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch radius (10)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntension\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntension flag (11)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nflip_primary\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nflip normal on primary surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nflip_secondary\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nflip normal on secondary surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell_bottom_primary\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact with bottom of shell on primary surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell_bottom_secondary\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact with bottom of shell on secondary surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noffset\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noffset distance between contacting surfaces (12)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n$\n\\backslash\nbullet$\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nIf the augmented Lagrangian flag is turned off (see comment 4),\n the penalty method is used to enforce the contact constraint.\n In this method,\n the contact traction is determined by the gap (i.e.\n penetration distance) multiplied by the user-defined \n\\shape italic\npenalty\n\\shape default\n factor.\n In the augmented Lagrangian method,\n the \n\\shape italic\npenalty\n\\shape default\n parameter is also used but has a slightly different meaning.\n In this case,\n it scales the Lagrange multiplier increment.\n Due to the different meanings,\n the user might have to adjust the penalty factor when switching between penalty method and augmented Lagrangian method.\n In general the penalty method requires a larger penalty factor to reach the same gap than the augmented Lagrangian method.\n See comment 4 for more information on when to use which method.\n \n\\end_layout\n\n\\begin_layout Enumerate\nChoosing a good initial penalty parameter can often be a difficult task,\n since this parameter depends on material properties as well as on mesh dimensions.\n For this reason,\n an algorithm has been implemented in FEBio that attempts to calculate a good initial value for the penalty factor \n\\begin_inset Formula $\\varepsilon_{i}$\n\\end_inset\n\nfor a particular node/integration point \n\\begin_inset Formula $i$\n\\end_inset\n\n on the contact interface:\n \n\\begin_inset Formula \n\\[\n\\varepsilon_{i}=\\frac{EA}{V}.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $A$\n\\end_inset\n\n is the area of the element the integration point belongs to,\n \n\\begin_inset Formula $V$\n\\end_inset\n\n is the element volume and \n\\begin_inset Formula $E$\n\\end_inset\n\n is a measure of the elasticity modulus,\n which is calculated from the elasticity tensor of the element.\n Although the meaning of \n\\begin_inset Formula $E$\n\\end_inset\n\n depends on the precise material formulation,\n in general one can regard it as a measure of the small strain Young's modulus of the material,\n when \n\\emph on\nupdate_penalty\n\\emph default\n is 0.\n If update_penalty is set to 1,\n \n\\begin_inset Formula $E$\n\\end_inset\n\n is recalculated at each iteration to account for the current deformation.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\nTo use this feature,\n add the following element to the contact section:\n\\end_layout\n\n\\begin_layout LyX-Code\n<auto_penalty>1</auto_penalty>\n\\end_layout\n\n\\begin_layout Standard\nWhen the auto-penalty flag is on,\n the value of the \n\\shape italic\npenalty \n\\shape default\nparameter serves as a scale factor for the automatically-calculated penalty factor.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nEach sliding interface consists of a \n\\emph on\nprimary\n\\emph default\n surface and a \n\\emph on\nsecondary \n\\emph default\nsurface.\n The primary surface is the surface over which the contact equations are integrated and on which the contact tractions are calculated.\n The secondary surface is used to measure the gap function and to define the necessary kinematic quantities such as surface normals and tangents.\n This approach is usually referred to as the \n\\shape italic\nsingle-pass \n\\shape default\nmethod.\n When using the single-pass algorithm,\n the results can be influenced by the choice of primary and secondary surfaces.\n It is best to use the most tessellated surface as the primary and the coarsest as the secondary surface.\n To resolve the bias issue,\n one can also use a \n\\shape italic\ntwo-pass\n\\shape default\n algorithm for enforcement of the contact constraint.\n In this case,\n a single pass is performed first,\n after which the primary and secondary surfaces are swapped and another pass is performed.\n When using the two-pass method,\n the definition of primary and secondary surfaces is arbitrary.\n In most problems,\n the single pass is sufficient to enforce contact;\n with a judicious choice of primary-secondary pair and contact parameters,\n good results can be obtained.\n If however,\n the single pass does not give good answers,\n for example,\n when due to the geometry's curvature the gap cannot be small enough with a single pass,\n the two-pass method can be used,\n although at the expense of more calculations.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\nIf one of the contacting surfaces is rigid,\n a slightly different approach is recommended.\n In this case,\n it is best to pick the rigid surface as the secondary surface and to use a single pass algorithm.\n The reason is that the nodal degrees of freedom on the rigid surface are condensed to the rigid degrees of freedom and if the rigid surface is the primary surface,\n the reaction forces may not propagate correctly to the secondary surface.\n This is especially true if the rigid degrees of freedom are fixed.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nIn the presence of a sliding interface (and other contact interfaces),\n FEBio needs to calculate the contact tractions that prevent the two participating surfaces from penetrating.\n In general these tractions can be found using the method of Lagrange multipliers.\n However,\n the direct calculation of these multipliers has several computational disadvantages and therefore FEBio approximates the multipliers using one of two alternative methods:\n the penalty method and the augmented Lagrangian method.\n In the former method,\n the multipliers are approximated by the gap (i.e.\n penetration distance) scaled by a suitably chosen penalty factor.\n In many cases,\n this method is sufficient to get good results.\n Since the correctness of a contact solution is directly determined by the amount of penetration at the converged state,\n the user has direct control over the quality of the solution.\n By increasing the penalty factor,\n the penetration is reduced.\n However,\n in some cases,\n especially in large compression problems,\n the penalty factor required to achieve an acceptable amount of penetration has to be so large that it causes numerical instabilities in the non-linear solution algorithm due to ill-conditioning of the stiffness matrix.\n In these cases,\n the augmented Lagrangian method might be a better choice.\n In this method,\n the multipliers are determined iteratively where,\n in each iteration,\n the multiplier's increments are determined with a penalty-like method.\n The advantage of this method is twofold:\n due to the iterative nature,\n the method will work with a smaller penalty factor,\n and in the limit,\n the exact Lagrange multipliers can be recovered.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\nTo turn on the augmented Lagrangian method,\n simply add the following line to the contact section:\n\\end_layout\n\n\\begin_layout LyX-Code\n<laugon>1</laugon>\n\\end_layout\n\n\\begin_layout Standard\nTo turn off the augmented Lagrangian method,\n either set the value to 0 or remove the parameter altogether.\n The convergence tolerance is set as follows:\n\\end_layout\n\n\\begin_layout LyX-Code\n<tolerance>0.01</tolerance>\n\\end_layout\n\n\\begin_layout Standard\nWith this parameter set,\n the augmented Lagrangian method will iterate until the relative increment in the multipliers is less than the tolerance.\n For instance,\n setting the tolerance parameter to 0.01 implies that the augmented Lagrangian method will converge when there is less than a 1\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n{\n\\end_layout\n\n\\end_inset\n\n%\n\\begin_inset ERT\nstatus collapsed\n\n\\begin_layout Plain Layout\n\n}\n\\end_layout\n\n\\end_inset\n\n change in the L2 norm of the augmented Lagrangian multiplier vector between successive augmentations.\n Alternatively,\n the user can also specify a tolerance for the gap value.\n In this case,\n the iterations will terminate when the gap norm,\n which is defined as the averaged L2 norm,\n (\n\\begin_inset Formula $\\sqrt{\\sum\\nolimits_{i}<g_{i}>^{2}}/N$\n\\end_inset\n\n,\n \n\\begin_inset Formula $<\\cdot>$\n\\end_inset\n\nthe Macauley Bracket) is less than the user-specified value:\n\\end_layout\n\n\\begin_layout LyX-Code\n<gaptol>0.001</gaptol>\n\\end_layout\n\n\\begin_layout Standard\nHowever,\n one must be careful when specifying a gap tolerance.\n First note that the gap tolerance is an absolute value (unlike the \n\\shape italic\ntolerance \n\\shape default\nwhich is a relative value),\n so this parameter depends on the dimensions of the model.\n Also,\n there are cases when a gap tolerance simply cannot be reached due to the geometry of the model in which case the augmentations may never converge.\n\\end_layout\n\n\\begin_layout Standard\nNote that both convergence parameters may be defined at once.\n In that case,\n FEBio will try to satisfy both convergence criteria.\n On the other hand,\n setting a value of zero will turn off the convergence criteria.\n For example,\n the default value for \n\\shape italic\ngaptol\n\\shape default\n is zero,\n so that FEBio will not check the gap norm by default.\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n the \n\\shape italic\nminaug \n\\shape default\nand \n\\shape italic\nmaxaug\n\\shape default\n can be used to set a minimum and maximum number of augmentations.\n When the \n\\shape italic\nmaxaug \n\\shape default\nvalue is reached,\n FEBio will move on to the next timestep,\n regardless of whether the force and gap tolerances have been met.\n When specifying a value for \n\\shape italic\nminaug\n\\shape default\n,\n FEBio will perform at least minaug augmentations.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\shape italic\nsliding-node-on-facet,\n \n\\shape default\n\\emph on\nsliding-elastic\n\\emph default\n,\n and \n\\emph on\nsliding-biphasic \n\\emph default\ncontact implementations support friction.\n For \n\\emph on\nnode-on-facet\n\\emph default\n,\n three parameters control the frictional response:\n \n\\shape italic\nfric_coeff \n\\shape default\nis the material's friction coefficient and its value must be in the range from 0.0 to 1.0;\n \n\\shape italic\nfric_penalty \n\\shape default\nis the penalty factor that regulates the tangential traction forces,\n much like the \n\\shape italic\npenalty \n\\shape default\nparameter regulates the normal traction force component;\n the parameter \n\\shape italic\nktmult \n\\shape default\nis a scale factor for the tangential stiffness matrix.\n It is default to 1.0,\n but it is observed that reducing this value might sometimes improve convergence.\n For \n\\emph on\nsliding-elastic\n\\emph default\n and \n\\emph on\nsliding-biphasic\n\\emph default\n only \n\\emph on\nfric_coeff\n\\emph default\n is needed to use frictional contact.\n In addition,\n for \n\\emph on\nsliding-biphasic\n\\emph default\n,\n the friction algorithm requires the specification of the solid-on-solid contact area fraction,\n which is a number between 0 and 1 (see \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n).\n Frictional contact is inherently dissipative and thus history-dependent.\n The solution may vary with the size of time increments,\n the cycle of loading,\n or Lagrangian augmentations (when \n\\emph on\nlaugon\n\\emph default\n=1).\n\\end_layout\n\n\\begin_layout Enumerate\nIn a contact problem,\n FEBio calculates the projection of each node of the primary surface onto the secondary surface.\n As a node slides across the secondary surface,\n the corresponding surface facet can change.\n However,\n in some cases,\n switching facets is undesirable since it might cause instabilities in the solution process or a state in which the node oscillates continuously between two adjacent facets and thus prevents FEBio from meeting the displacement convergence tolerance.\n The parameter \n\\shape italic\nseg_up \n\\shape default\nallows the user to control the number of segment updates FEBio will perform during each time step.\n For example,\n a value of 4 tells FEBio it can do the segment updates during the first four iterations.\n After that,\n nodes will not be allowed to switch to new segments.\n The default value is 0,\n which means that FEBio will do a segment update each iteration of each timestep.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe augmented Lagrangian method may produce a non-smooth contact pressure distribution at the contact surface,\n even though stresses within the underlying elements may remain relatively smoother.\n Turning this flag on changes the method of updating the Lagrange multiplier to use the projection of the element stress to the corresponding contact face.\n This feature only works with some element types (HEX8G8,\n HEX8G1,\n TET4G4,\n TET4G1,\n PENTA6G6,\n TET10G1,\n TET10G4,\n TET10G8,\n TET10GL11,\n TET15G4,\n TET15G8,\n TET15G11,\n TET15G15,\n PYRA5G8,\n PYRA13G8).\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\nIn the \n\\emph on\nsliding-biphasic\n\\emph default\n friction algorithm the friction coefficient depends on the fluid pressure and load support.\n Smoothing this fluid load support may produce a smoother distribution of the friction coefficient over the contact interface.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\shape italic\nsliding-elastic,\n sliding-biphasic\n\\shape default\n,\n \n\\shape italic\nsliding-biphasic-solute\n\\shape default\n and \n\\shape italic\nsliding-multiphasic\n\\shape default\n contact implementations for sliding contact are inherently non-symmetric formulations.\n Symmetrized versions of these algorithms do not perform as well as the nonsymmetric version so it is recommended to use the latter.\n The following line in the \n\\emph on\ncontact\n\\emph default\n element controls which version of the algorithm is used.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<symmetric_stiffness>0</symmetric_stiffness>\n\\end_layout\n\n\\begin_layout Standard\nA value of 1 uses the symmetric version,\n whereas a value of 0 uses the non-symmetric version.\n A similar line may be included in the \n\\emph on\nControl\n\\emph default\n element to use a non-symmetric global stiffness matrix.\n In some cases,\n a non-symmetric contact stiffness may converge well even when the global stiffness matrix is symmetric.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\shape italic\nsearch_tol\n\\shape default\n parameter defines the search tolerance of the algorithm that projects nodes of the primary surface onto facets of the secondary surface.\n A node that falls outside an element,\n but whose distance to the closest element's edge is less than the search tolerance is still considered inside.\n This can alleviate convergence problems when nodes are projected onto edges of elements and due to numerical error may be projected outside the surface.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\nsearch_radius\n\\shape default\n is a dimensional search radius used by the algorithm that projects points onto facets.\n When the distance between the point and the facet exceeds the dimensional search radius,\n that projection is ignored.\n This can alleviate convergence problems when surfaces have multiple folds and the projection produces multiple solutions,\n only one of which (the closest distance) is valid.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\ntension\n\\shape default\n flag determines whether the contact interface can sustain tension and compression (\n\\shape italic\ntension\n\\shape default\n=1) or only compression (\n\\shape italic\ntension\n\\shape default\n=0).\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\noffset\n\\emph default\n distance makes it possible to maintain the contact surfaces at a distance.\n This option is useful,\n for example,\n in fluid-structure interaction problems,\n where solid structures separated by a fluid domain are pushed against each other.\n Specifying a contact interface between the (internal) surfaces of the solid structures would prevent them from overlapping,\n but adding an offset distance will also prevent the fluid mesh from getting squished too thin or from inverting.\n These internal surfaces would also typically have FSI Traction interfaces defined on them (see Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-FSI-Traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Biphasic-FSI-Traction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nBiphasic Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nsliding-biphasic \n\\shape default\nimplementation for sliding interfaces can deal with biphasic contact surfaces (including biphasic-on-biphasic,\n biphasic-on-elastic,\n biphasic-on-rigid) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10b,Zimmerman22\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It allows for the possibility to track fluid flow across the contact interface.\n In other words,\n fluid can flow from one side of the contact interface to the other when both contact surfaces are biphasic.\n To use this feature,\n the user must define an additional contact parameter,\n namely:\n\\end_layout\n\n\\begin_layout LyX-Code\n<pressure_penalty>1.0</pressure_penalty>\n\\end_layout\n\n\\begin_layout Standard\nIn the same way that the \n\\shape italic\npenalty \n\\shape default\nparameter controls the contact tractions,\n this parameter controls the penalty value that is used to calculate the Lagrange multipliers for the pressure constraint.\n If the \n\\shape italic\nlaugon \n\\shape default\nflag is set,\n the augmented Lagrangian method is used to enforce the pressure constraint.\n And if the \n\\shape italic\nauto_penalty \n\\shape default\nflag is defined (which is the recommended approach),\n an initial guess for the pressure penalty is calculated automatically using the following formula:\n\\begin_inset Formula \n\\[\n\\varepsilon_{p}=\\frac{kA}{V},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $A$\n\\end_inset\n\n is the element's area,\n \n\\begin_inset Formula $V$\n\\end_inset\n\n is the element's volume and \n\\begin_inset Formula $k$\n\\end_inset\n\n is a measure of the permeability which is defined as one third of the trace of the material's initial permeability tensor.\n\\end_layout\n\n\\begin_layout Standard\nWhen either contact surface is biphasic,\n the surface outside the contact area(s) is automatically set to free-draining conditions (equivalent to setting the fluid pressure to zero).\n\\end_layout\n\n\\begin_layout Standard\nAs detailed in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman22\"\nliteral \"true\"\n\n\\end_inset\n\n and the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n,\n the \n\\emph on\nsliding-biphasic\n\\emph default\n contact algorithm can include frictional contact.\n The effective friction coefficient \n\\begin_inset Formula $\\mu_{\\text{eff}}$\n\\end_inset\n\n in this frictional contact algorithm depends on the temporally evolving local fluid load support (the ratio of fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n to the normal component of the mixture contact traction \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n),\n according to\n\\begin_inset Formula \n\\[\n\\mu_{\\text{eff}}=\\mu_{\\text{eq}}\\left(1+\\left(1-\\varphi\\right)\\frac{p}{t_{n}}\\right)\\,.\n\\]\n\n\\end_inset\n\nRecall that \n\\begin_inset Formula $t_{n}$\n\\end_inset\n\n is negative whereas \n\\begin_inset Formula $p$\n\\end_inset\n\n is positive in compression.\n Here,\n \n\\begin_inset Formula $\\mu_{\\text{eq}}$\n\\end_inset\n\n (\n\\emph on\nfric_coeff\n\\emph default\n) is the friction coefficient in the limit when the fluid load support has reduced to zero,\n and \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n (\n\\emph on\ncontact_frac\n\\emph default\n) is the fraction of the apparent contact area over which the solid matrix of the primary surface contacts that of the secondary surface.\n Thus,\n \n\\begin_inset Formula $1-\\varphi$\n\\end_inset\n\n is the fraction of the apparent contact area where fluid contacts fluid or solid.\n For perfectly smooth contact surfaces one may assume that \n\\begin_inset Formula $\\varphi=\\varphi_{1}^{s}\\varphi_{2}^{s}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varphi_{1}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi_{2}^{s}$\n\\end_inset\n\n are the solid area (or volume) fractions of the primary and secondary contact surfaces,\n respectively.\n For example,\n when both contact surfaces are non-porous (\n\\begin_inset Formula $\\varphi_{1}^{s}=\\varphi_{2}^{s}=1$\n\\end_inset\n\n),\n the effective friction coefficient reduces to \n\\begin_inset Formula $\\mu_{\\text{eff}}=\\mu_{\\text{eq}}$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen performing biphasic-on-rigid contact a two-pass analysis should not be used;\n the rigid surface should be the secondary surface.\n\\end_layout\n\n\\begin_layout Subsection\nBiphasic-Solute and Multiphasic Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Biphasic-Solute-Multiphasic-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nsliding-biphasic-solute \n\\shape default\nimplementation for sliding interfaces can deal with biphasic-solute contact surfaces (including biphasic-solute-on-biphasic-solute,\n biphasic-solute-on-biphasic,\n biphasic-solute-on-elastic,\n biphasic-solute-on-rigid) and the \n\\shape italic\nsliding-multiphasic\n\\shape default\n contact interface can similarly deal with multiphasic contact surfaces.\n These contact interfaces allow for the possibility to track fluid and solute flow across the contact interface \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian12\"\nliteral \"true\"\n\n\\end_inset\n\n.\n In other words,\n fluid and solute can flow from one side of the contact interface to the other.\n To use this feature,\n the user must define additional contact parameters,\n namely:\n\\end_layout\n\n\\begin_layout LyX-Code\n<pressure_penalty>1.0</pressure_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n<concentration_penalty>1.0</concentration_penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n<ambient_pressure>0</ambient_pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n<ambient_concentration>0</ambient_concentration> (for sliding-biphasic-solute)\n\\end_layout\n\n\\begin_layout Standard\nor\n\\end_layout\n\n\\begin_layout LyX-Code\n<ambient_concentration sol=\"id\">0</ambient_concentration> (for sliding-multiphasic)\n\\end_layout\n\n\\begin_layout Standard\nIn the same way that the \n\\shape italic\npenalty \n\\shape default\nparameter controls the contact tractions,\n these penalty parameters control the penalty values that are used to calculate the Lagrange multipliers for the pressure and concentration constraints.\n If the \n\\shape italic\nlaugon \n\\shape default\nflag is set,\n the augmented Lagrangian method is used to enforce the pressure and concentration constraints.\n And if the \n\\shape italic\nauto_penalty \n\\shape default\nflag is defined,\n an initial guess for the pressure and concentration penalty is calculated automatically using the following formulas:\n \n\\begin_inset Formula \n\\[\n\\varepsilon_{p}=\\frac{k\\cdot A}{V}\\,,\\quad\\varepsilon_{c}=\\frac{d\\cdot A}{V}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $A$\n\\end_inset\n\n is the element's area,\n \n\\begin_inset Formula $V$\n\\end_inset\n\n is the element's volume,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n is a measure of the fluid permeability which is defined as one third of the trace of the material's initial permeability tensor,\n and \n\\begin_inset Formula $d$\n\\end_inset\n\n is a measure of the solute diffusivity which is defined as one third of the trace of the material's initial diffusivity tensor.\n\\end_layout\n\n\\begin_layout Standard\nWhen either contact surface is biphasic-solute or multiphasic,\n the surface outside the contact area(s) is automatically set to ambient conditions (equivalent to setting the effective fluid pressure and effective solute concentration to the <ambient_pressure> and <ambient_concentration> values,\n respectively).\n Ambient conditions may also be associated with a load curve,\n for example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<ambient_pressure lc=\"2\">1.0</ambient_pressure>\n\\end_layout\n\n\\begin_layout LyX-Code\n<ambient_concentration lc=\"3\">1.0</ambient_concentration>\n\\end_layout\n\n\\begin_layout Standard\nWhen performing biphasic-solute-on-rigid or multiphasic-on-rigid contact,\n a two-pass analysis should not be used;\n the rigid surface should be the secondary surface.\n\\end_layout\n\n\\begin_layout Subsection\nRigid Wall Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-Wall-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA rigid wall interface is similar to a sliding interface,\n except that the secondary surface is a rigid wall.\n The following properties are defined for rigid wall interfaces:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAugmented Lagrangian flag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmentation tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplane\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe plane equation for the rigid wall\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nN/A \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noffset\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe normal offset of the plane defined by the \n\\shape italic\nplane \n\\shape default\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nplane \n\\shape default\nproperty defines the reference plane for the rigid wall.\n Its value is an array of four values:\n\\begin_inset Formula $a,b,c,d_{0}$\n\\end_inset\n\n.\n The actual plane is defined by specifying the \n\\shape italic\noffset \n\\shape default\nto the reference plane.\n The offset parameter takes a loadcurve as an optional attribute to define the motion of the plane as a function of time.\n The loadcurve defines the offset \n\\begin_inset Formula $h$\n\\end_inset\n\n from the initial position in the direction of the plane normal:\n \n\\begin_inset Formula \n\\[\n\\begin{array}{cc}\nax+by+cz+d\\left(t\\right)=0, & d\\left(t\\right)=d_{0}+h\\left(t\\right)\\end{array}\n\\]\n\n\\end_inset\n\nSo,\n for example,\n a rigid wall that initially lies in the xy-coordinate plane and moves in the z-direction would be specified as follows:\n\\end_layout\n\n\\begin_layout LyX-Code\n<plane>0,0,1,0</plane>\n\\end_layout\n\n\\begin_layout LyX-Code\n<offset lc=\"1\">1.0</offset>\n\\end_layout\n\n\\begin_layout Subsection\nTied Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA tied interface can be used to connect two non-conforming meshes \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Laursen93\"\nliteral \"true\"\n\n\\end_inset\n\n.\n A tied interface requires the definition of both a primary and a secondary surface.\n It is assumed that the nodes of the primary surface are connected to the faces of the secondary surface.\n The following control parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<penalty>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty factor \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tolerance>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmentation tolerance \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTied Elastic Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Elastic-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA tied elastic interface is similar to the tied interface.\n It may be used for tying solid materials.\n It enforces continuity of the displacement across the interface.\n The following control parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"12\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormal penalty scale factor (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure penalty scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate auto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo_pass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo-pass flag (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmented Lagrangian flag (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naug.\n Lagrangian convergence tolerance (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngaptol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance for gap value (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric_stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric stiffness matrix flag (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nProjection search tolerance (8)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_radius\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch radius (10)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTied Biphasic Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Biphasic-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA tied biphasic interface is similar to the tied interface.\n It may be used for tying any combination of solid,\n biphasic,\n and rigid materials.\n It enforces continuity of the fluid pressure across the interface when both materials are biphasic.\n The following control parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"12\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormal penalty scale factor (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure penalty scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate auto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo_pass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo-pass flag (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmented Lagrangian flag (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naug.\n Lagrangian convergence tolerance (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngaptol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance for gap value (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 (off) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric_stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric stiffness matrix flag (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nProjection search tolerance (8)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_radius\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch radius (10)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nTied Multiphasic Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tied-Multiphasic-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA tied multiphasic interface is similar to the tied biphasic interface.\n It may be used for tying any combination of solid,\n biphasic,\n multiphasic and rigid materials.\n It enforces continuity of the effective fluid pressure and effective solute concentrations across the interface when both materials are biphasic or multiphasic.\n The following control parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"13\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormal penalty scale factor (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure penalty scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconcentration_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconcentration penalty scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nauto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate_penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate auto-penalty calculation flag (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo_pass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntwo-pass flag (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmented Lagrangian flag (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naug.\n Lagrangian convergence tolerance (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngaptol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance for gap value (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0 (off) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric_stiffness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric stiffness matrix flag (7)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nProjection search tolerance (8)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_radius\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch radius (10)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSticky Interfaces\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Sticky-Interfaces\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA sticky interface is similar to a tied interface except that it allows for initial separation of the tied surfaces and breaking of the tie after a user-defined normal traction is exceeded.\n The tie is only applied when the surfaces contact and sustained as long as the normal traction is less than the threshold.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmentation flag \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty factor \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugmentation tolerance \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminimum number of required augmentations \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaxaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of augmentations \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsearch_tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntolerance for nodal projection onto secondary surface facet \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_traction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthreshold for normal traction (1) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsnap_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminimum distance for tie activation (2) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact surfaces are defined as in the \n\\shape italic\ntied \n\\shape default\ninterface (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Tied-Interfaces\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\nmax_traction \n\\shape default\nparameter can be used to break the tied interface after the normal traction exceeds the specified value.\n Initially,\n this value is set to zero,\n in which case FEBio will ignore this value and the tie cannot be broken.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\nsnap_tol \n\\shape default\nparameter is used in determining the minimum distance that a primary surface node must have approached the secondary surface facet in order to snap onto the secondary surface.\n The initial value is zero,\n meaning a node must have penetrated the secondary surface before it will be tied to it.\n \n\\end_layout\n\n\\begin_layout Subsection\nContact Potential\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:contact-potential\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe contact potential method is based on the formulation by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"KAMENSKY2018522\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In this formulation,\n a repulsive potential is defined between two opposing surfaces.\n A force is generated that aims to prevent physical contact between the two surfaces.\n As a result,\n there will always be a small gap between the contacting surfaces.\n Note that this is the opposite of what happens for the other sliding contact interfaces,\n which require physical contact (and even a little bit of penetration) before a contact force can be generated.\n \n\\end_layout\n\n\\begin_layout Standard\nThe type attribute for this sliding interface is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\ncontact potential\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n and the following parameters can be defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nkc\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nforce scale factor (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\np\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\norder of polynomial that defines the potential (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR_in\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInner radius (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR_out\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOuter radius (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR0_min\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMinimum separation in reference frame\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nw_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthreshold for angle criterion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nkc \n\\emph default\nparameter defines the scale factor for the force.\n This has a similar purpose as the penalty factor in other sliding contact interfaces.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe parameter \n\\emph on\np \n\\emph default\ndefines the order of the potential.\n The paper discusses why the value 4 is in a sense an optimal value so it is recommend to use the default value.\n\\end_layout\n\n\\begin_layout Enumerate\nThe potential is divided in an \n\\begin_inset Quotes eld\n\\end_inset\n\ninner\n\\begin_inset Quotes erd\n\\end_inset\n\n and \n\\begin_inset Quotes eld\n\\end_inset\n\nouter\n\\begin_inset Quotes erd\n\\end_inset\n\n region.\n If \n\\emph on\nr \n\\emph default\nis the distance from a point on the primary contact surface to the opposing secondary surface,\n then the \n\\begin_inset Quotes eld\n\\end_inset\n\ninner\n\\begin_inset Quotes erd\n\\end_inset\n\n region is defined by the criterion \n\\begin_inset Formula $0<r<R_{in}$\n\\end_inset\n\nand the \n\\begin_inset Quotes eld\n\\end_inset\n\nouter\n\\begin_inset Quotes erd\n\\end_inset\n\n region is defined by \n\\begin_inset Formula $R_{in}<r<R_{out}$\n\\end_inset\n\n.\n The contact force is stronger in the inner region.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nR0_min\n\\emph default\n parameter defines the minimum distance between two potential contacting points in the reference configuration.\n In other words,\n two points whose initial distance was smaller than \n\\emph on\nR0_min\n\\emph default\n will not be considered as a valid contact pair.\n This parameter is important in self-contact to avoid neighboring points from generating contact forces.\n The default value of 0 indicates that this criterion is not used.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nw_tol \n\\emph default\nparameter defines a local normal criterion for a potential pair:\n If the absolute value of the cosine of the angle between the two local normals is \n\\series bold\nless\n\\series default\n than w_tol,\n the two points are \n\\series bold\nnot\n\\series default\n considered in contact.\n In other words,\n the two normals have to point in the \n\\begin_inset Quotes eld\n\\end_inset\n\nsame direction\n\\begin_inset Quotes erd\n\\end_inset\n\n in order for the two contacting points to be in contact.\n This criterion can be useful in cases of self-contact in order to avoid invalid contact pairs.\n A value of 0 effectively ignores this criterion.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<contact type=\"contact potential\" name=\"FacetOnFacetSliding1\" surface_pair=\"FacetOnFacetSliding1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <kc>1e-6</kc>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <p>4</p>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R_in>0.01</R_in>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R_out>0.05</R_out>\n\\end_layout\n\n\\begin_layout LyX-Code\n</contact>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nConstraints Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Constraints-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Constraints section allows the user to enforce additional constraints to the model.\n\\end_layout\n\n\\begin_layout Subsection\nSymmetry Plane\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Symmetry-Plane\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA symmetry plane enforces zero solid displacement normal to a user-selected plane.\n This constraint is prescribed on a deformable surface of the model and is enforced for every node on that surface.\n It is the user's responsibility to select only planar surfaces.\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint type=\"symmetry plane\" surface=\"SymmetryPlane01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<laugon>1</laugon>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<penalty>1e6</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<tol>1e-6</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<minaug>0</minaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<maxaug>50</maxaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nsurface\n\\emph default\n (SymmetryPlane01 in this example),\n is defined in the \n\\emph on\nMesh\n\\emph default\n section.\n Let \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n denote the nodal displacement vector and let \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n denote the unit normal to the plane;\n the symmetry plane constraint enforces \n\\begin_inset Formula $u_{n}\\equiv\\mathbf{u}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n using a penalty method,\n optionally with augmented Lagrangian.\n The reaction force needed to enforce this constraint is evaluated as \n\\begin_inset Formula $\\varepsilon u_{n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the user-specified \n\\emph on\npenalty\n\\emph default\n parameter (with units of force per length).\n To use the augmented Lagrangian method,\n set \n\\emph on\nlaugon\n\\emph default\n to 1,\n and the Lagrange multiplier \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n will be augmented as \n\\begin_inset Formula $\\lambda\\leftarrow\\lambda+\\varepsilon u_{n}$\n\\end_inset\n\n.\n Augmentations terminate when the relative change in \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is less than the user-specified tolerance \n\\emph on\ntol\n\\emph default\n,\n or when the number of augmentations exceeds \n\\emph on\nmaxaug\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Subsection\nFrictionless Fluid Wall\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Frictionless-Fluid-Wall\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nOne of the natural boundary conditions for fluid analyses is to enforce zero fluid velocity normal to a boundary,\n implying that this boundary is a frictionless fluid wall.\n In some analyses however,\n this natural boundary condition may not be enforced sufficiently well.\n In such cases,\n use this frictionless fluid wall linear constraint to produce a more strict enforcement of this condition.\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint type=\"frictionless fluid wall\" name=\"FrictionlessFluidWall1\" surface=\"FrictionlessFluidWall1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<laugon>0</laugon>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<tol>0.2</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<penalty>1</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<minaug>0</minaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<maxaug>10</maxaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint>\n\\end_layout\n\n\\begin_layout Standard\nIn fluid-structure interaction analyses,\n this constraint may be used in conjunction with the \n\\emph on\nsymmetry plane\n\\emph default\n described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Symmetry-Plane\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n using a lower value of the \n\\emph on\npenalty\n\\emph default\n parameter,\n as may be necessary in practice.\n\\end_layout\n\n\\begin_layout Subsection\nFixed Normal Displacement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fixed-Normal-Displacement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA fixed normal displacement constraint enforces zero solid displacement normal to a user-selected surface.\n This constraint is prescribed on a deformable surface of the model and is enforced for every node on that surface.\n It uses the local surface normal in the selected surface's reference configuration.\n If the selected surface is planar,\n the functionality of this constraint is equivalent to the Symmetry Plane constraint described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Symmetry-Plane\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint type=\"fixed normal displacement\" surface=\"FixedNormalDisplacement01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<laugon>0</laugon>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<penalty>1000</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<tol>0.2</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<minaug>0</minaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<maxaug>10</maxaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<shell_bottom>0</shell_bottom>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nsurface\n\\emph default\n (FixedNormalDisplacement01 in this example),\n is defined in the \n\\emph on\nMesh\n\\emph default\n section.\n Let \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n denote the nodal displacement vector and let \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n denote the unit normal at each node of the surface;\n the fixed normal displacement constraint enforces \n\\begin_inset Formula $u_{n}\\equiv\\mathbf{u}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n using a penalty method,\n optionally with augmented Lagrangian.\n The reaction force needed to enforce this constraint is evaluated as \n\\begin_inset Formula $\\varepsilon u_{n}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the user-specified \n\\emph on\npenalty\n\\emph default\n parameter (with units of force per length).\n To use the augmented Lagrangian method,\n set \n\\emph on\nlaugon\n\\emph default\n to 1,\n and the Lagrange multiplier \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n will be augmented as \n\\begin_inset Formula $\\lambda\\leftarrow\\lambda+\\varepsilon u_{n}$\n\\end_inset\n\n.\n Augmentations terminate when the relative change in \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is less than the user-specified tolerance \n\\emph on\ntol\n\\emph default\n,\n or when the number of augmentations exceeds \n\\emph on\nmaxaug\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n this constraint may be used on a cylindrical shaft,\n over the portion of the shaft which is supported by a bearing journal.\n\\end_layout\n\n\\begin_layout Subsection\nNormal Fluid Velocity Constraint\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Normal-Fluid-Velocity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA normal fluid velocity constraint forces the fluid velocity to remain normal to the selected surface.\n It is enforced for every node on that surface.\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint type=\"normal fluid velocity\" surface=\"NormalFlowSurface01\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<laugon>1</laugon>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<penalty>1e6</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<tol>1e-6</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<minaug>0</minaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<maxaug>50</maxaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nsurface\n\\emph default\n (NormalFlowSurface01 in this example),\n is defined in the \n\\emph on\nMesh\n\\emph default\n section.\n Let \n\\begin_inset Formula $\\mathbf{v}$\n\\end_inset\n\n denote the nodal fluid velocity vector and let \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n denote the unit normal at each node;\n the normal flow constraint enforces \n\\begin_inset Formula $\\mathbf{v}=\\left(\\mathbf{v}\\cdot\\mathbf{n}\\right)\\mathbf{n}$\n\\end_inset\n\n,\n or equivalently,\n \n\\begin_inset Formula $\\mathbf{v}_{\\tau}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{v}=\\mathbf{0}$\n\\end_inset\n\n,\n using a penalty method,\n optionally with augmented Lagrangian.\n The reaction force needed to enforce this constraint is evaluated as \n\\begin_inset Formula $\\varepsilon\\mathbf{v}_{\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n is the user-specified \n\\emph on\npenalty\n\\emph default\n parameter (with units of force per velocity).\n To use the augmented Lagrangian method,\n set \n\\emph on\nlaugon\n\\emph default\n to 1,\n and the vectorial Lagrange multiplier \n\\begin_inset Formula $\\boldsymbol{\\lambda}$\n\\end_inset\n\n will be augmented as \n\\begin_inset Formula $\\boldsymbol{\\lambda}\\leftarrow\\boldsymbol{\\lambda}+\\varepsilon\\mathbf{v}_{\\tau}$\n\\end_inset\n\n.\n Augmentations terminate when the relative change in \n\\begin_inset Formula $\\boldsymbol{\\lambda}$\n\\end_inset\n\n is less than the user-specified tolerance \n\\emph on\ntol\n\\emph default\n,\n or when the number of augmentations exceeds \n\\emph on\nmaxaug\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Standard\nThis constraint should only be used on a surface where the fluid pressure or dilatation has been fixed or prescribed.\n It is not compatible with boundary conditions that prescribe the fluid velocity.\n To prescribe a fluid velocity that remains normal to the selected surface,\n use the surface load \n\\emph on\nfluid normal velocity\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Normal-Velocity\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nUniform Fluid Velocity Constraint\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uniform-Fluid-Velocity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen an inlet boundary is subjected to a prescribed fluid pressure,\n the profile of the inlet fluid velocity remains unconstrained,\n and this lack of constraint often leads to poor numerical performance due to the production of velocity spikes.\n To avoid this problem,\n use this uniform fluid velocity constraint to help stabilize inlet conditions along the direction normal to the boundary.\n This constraint evaluates the average fluid velocity normal to the selected boundary,\n \n\\begin_inset Formula $\\bar{v}_{n}$\n\\end_inset\n\n,\n then prescribes a linear constraint at every node such that \n\\begin_inset Formula $\\mathbf{v}\\cdot\\mathbf{n}=\\bar{v}_{n}$\n\\end_inset\n\n is enforced.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint name=\"UniformFluidFlow1\" surface=\"UniformFluidFlow1\" type=\"uniform fluid flow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<laugon>0</laugon>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<tol>0.1</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<penalty>10000</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<minaug>0</minaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<maxaug>50</maxaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint>\n\\end_layout\n\n\\begin_layout Standard\nIf a very strong enforcement of this constraint is desired,\n increase the \n\\emph on\npenalty\n\\emph default\n and/or turn Lagrange augmentation on (\n\\emph on\nlaugon\n\\emph default\n=1).\n This constraint is not compatible with boundary conditions that prescribe the normal component of the fluid velocity on that same boundary.\n\\end_layout\n\n\\begin_layout Subsection\nThe Prestrain Update Rules\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:The-Prestrain-Update\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe prestrain update rules for are implemented via non-linear constraints in FEBio.\n This is done because non-linear constraints automatically participate in FEBio’s augmentation mechanism,\n which allows the user to update and rerun the time step.\n The following section details how to setup the update rules in the FEBio input file and discusses the currently supported rules.\n\\end_layout\n\n\\begin_layout Subsubsection\nUsing Update rules\n\\end_layout\n\n\\begin_layout Standard\nIn order to apply an update rule,\n a constraint definition must be added to the Constraints section of the input file.\n The type attribute is used to specify which update rule to use.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Constraints>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <constraint type=\"[name of update rule]\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <!-- parameters go here -->\n\\end_layout\n\n\\begin_layout LyX-Code\n  </constraint>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Constraints> \n\\end_layout\n\n\\begin_layout Standard\nThe following table lists the available update rules.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nprestrain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEliminate distortion due to incompatibility\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nin-situ stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEnforce the given in-situ fiber stretches\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBelow,\n the supported update rules are presented in more detail.\n All of them share parameters for controlling the convergence of the update algorithm and these shared parameters are listed in the following table.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nupdate\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nupdate flag (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\ntolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nmin_iters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminimum number of iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nmax_iters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n-1 (i.e.\n ignored)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n \n\\end_layout\n\n\\begin_layout Enumerate\nBy specifying a loadcurve for the update flag,\n the update can be delayed.\n This can be useful if,\n for instance,\n the prestrain is applied incrementally and the update rule should not be applied until the full prestrain field is applied.\n In that case,\n specifying a loadcurve for the update flag that is zero while the prestrain is applied,\n will delay the update process.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nprestrain update rule\n\\end_layout\n\n\\begin_layout Standard\nThe idea behind this rule is to eliminate the distortion induced by the incompatibility of the initial prestrain gradient with the reference geometry.\n Thus,\n we retain the original reference geometry at the cost of an altered effective prestrain field.\n The update rule is given by the following equation.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{G^{\\mathit{k+1}}}=\\mathbf{G^{\\mathit{k}}}\\cdot\\mathbf{F_{\\mathit{c}}}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis update rule does not define any additional parameters aside the ones from above.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint type=\"prestrain\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0.01</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint> \n\\end_layout\n\n\\begin_layout Subsubsection\nThe in-situ stretch update rule\n\\end_layout\n\n\\begin_layout Standard\nThe idea behind this update rule is to enforce the fiber stretch induced by the initial prestrain gradient.\n As with the in-situ stretch generator option,\n this rule has an isochoric version and an uniaxial version for the update rule.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{G}_{iso}=\\mathbf{Q}\\left[\\begin{array}{ccc}\n\\lambda^{-1}\\\\\n & \\lambda^{1/2}\\\\\n &  & \\lambda^{1/2}\n\\end{array}\\right]\\mathbf{Q^{\\mathrm{\\mathit{T}}}},\\qquad\\mathbf{G}_{uni}=\\mathbf{Q}\\left[\\begin{array}{ccc}\n\\lambda^{-1}\\\\\n & 1\\\\\n &  & 1\n\\end{array}\\right]\\mathbf{Q^{\\mathit{T}}}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $\\lambda^{2}=\\mathbf{a}_{0}\\cdot\\mathbf{C}^{k}\\cdot\\mathbf{a}_{0}$\n\\end_inset\n\n,\n is the fiber stretch induced by the distortion.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisochoric\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nChoose the isochoric update rule or not\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1(=true)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint type=\"in-situ stretch\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tolerance>0.01</tolerance>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint> \n\\end_layout\n\n\\begin_layout Subsection\nNode Distance\n\\end_layout\n\n\\begin_layout Standard\nA \n\\begin_inset Quotes eld\n\\end_inset\n\nnode distance\n\\begin_inset Quotes erd\n\\end_inset\n\n constraint can be used to enforce a user-defined distance between two nodes.\n The target distance can be defined as a distance relative to the initial separation between the two nodes,\n or as an absolute distance.\n This constraint takes the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlaugon\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTurn on augmented Lagrangian flag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0(=false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naugtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAugmentation tolerance.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npenalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPenalty factor for constraint enforcement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIDs of the two nodes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[-1,-1] (invalid)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nminimum nr of augmentations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaxaug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum nr of augmentations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntarget\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTarget distance between two nodes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse relative or absolute distance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (=true)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe default configuration of this constraint is such that the initial distance between the two nodes is preserved.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Standard\nThis example illustrates how the \n\\emph on\nnode distance\n\\emph default\n constraint can be used to reduce the distance between two nodes to zero:\n the target is set to zero and the relative flag is off so that the target is interpreted as an absolute distance.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<constraint name=\"NodeDistance3\" type=\"node distance\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <laugon>0</laugon>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <augtol>0.01</augtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <penalty lc=\"1\">1</penalty>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node>17,158</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <minaug>0</minaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <maxaug>10</maxaug>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <target>0</target>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n</constraint>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nDiscrete Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Discrete-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section defines the materials used by the discrete elements and assigns these materials to the discrete elements sets defined in the \n\\emph on\nMesh\n\\emph default\n section.\n The materials are defined via the \n\\shape italic\ndiscrete_material \n\\shape default\nelement and the materials are assigned to discrete element sets using the \n\\shape italic\ndiscrete \n\\shape default\nelement.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Discrete>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <discrete_material id=\"1\" type=\"linear spring\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </discrete_material>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <discrete_material id=\"2\" type=\"linear spring\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>2.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </discrete_material>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <discrete dmat=\"1\" discrete_set=\"springs1\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <discrete dmat=\"2\" discrete_set=\"springs2\">\n\\end_layout\n\n\\begin_layout LyX-Code\n</Discrete>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ndiscrete_material \n\\shape default\nand the \n\\shape italic\ndiscrete \n\\shape default\nelements are defined in more detail below.\n\\end_layout\n\n\\begin_layout Subsection\nDiscrete Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Discrete-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ndiscrete_material \n\\shape default\nsection defines a material that can be assigned to a discrete element set.\n The \n\\shape italic\nid \n\\shape default\nattribute defines the material ID and the \n\\shape italic\ntype \n\\shape default\nattribute defines the material type.\n\\end_layout\n\n\\begin_layout Subsubsection\nLinear Spring\n\\end_layout\n\n\\begin_layout Standard\nThe linear spring has a linear force-displacement relation.\n It requires the E parameter that defines the spring constant.\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<discrete_material id=\"1\" type=\"linear spring\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>3.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n</discrete_material>\n\\end_layout\n\n\\begin_layout Subsubsection\nNonlinear spring\n\\end_layout\n\n\\begin_layout Standard\nThe nonlinear spring allows users to define a custom relation between a measure of stretch and the spring force.\n The following parameters can be defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nForce scale factor.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmeasure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMeasure of stretch.\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1D function that defines the stretch-force relationship\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nmeasure \n\\emph default\nparameter can be set to one of the following values:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\n\n\\series bold\nelongation\n\\series default\n:\n Use the spring elongation,\n the difference between current and original length,\n as the measure of stretch.\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nstrain\n\\series default\n:\n Use the strain,\n i.e.\n the ratio of elongation over original length,\n as the measure of stretch\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\series bold\nstretch\n\\series default\n:\n Use the ratio of current length over original length as measure of stretch.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<discrete_material id=\"1\" name=\"set1\" type=\"nonlinear spring\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<scale>1</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<measure>elongation</measure>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<force type=\"math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<math>x</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n</discrete_material>\n\\end_layout\n\n\\begin_layout Subsubsection\nHill\n\\end_layout\n\n\\begin_layout Standard\nThe Hill discrete material defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum shortening velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nac\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactivation level\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrain (i.e.\n stretch - 1) at which Fmax occurs.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nL0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninitial reference length\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSv\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax velocity scale\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFtl\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormalized tension-length curve\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFtv\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormalized tension-velocity curve\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe force in the Hill discrete element is the sum of the passive element and the active element.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nF=F_{p}+F_{a}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe passive force is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nF_{p}=\\begin{cases}\nFmax\\left(\\exp\\left(Ksh\\left(l-1\\right)/Lmax-1\\right)/\\exp\\left(Ksh-1\\right)\\right) & ,l>1\\\\\n0 & ,l\\leqslant1\n\\end{cases}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere l is the relative stretch defined by,\n \n\\begin_inset Formula $l=lm/l0$\n\\end_inset\n\n and \n\\begin_inset Formula $lm$\n\\end_inset\n\nis the discrete element length and \n\\begin_inset Formula $l0$\n\\end_inset\n\nis either the L0 parameter,\n or the initial discrete element length (if L0 is set to zero).\n \n\\end_layout\n\n\\begin_layout Standard\nThe active force is given by,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nFa=ac*Fmax*Ftl\\left(l\\right)*Ftv\\left(v\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $v$\n\\end_inset\n\n,\n a measure of the relative discrete element's growth speed,\n is defined by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nv=vm/\\left(Vmax*Sv\\left(ac\\right)\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $vm$\n\\end_inset\n\nis the actual discrete element's growth speed.\n \n\\end_layout\n\n\\begin_layout Standard\nThe properties Sv,\n Ftl,\n and Ftv,\n are optional and will evaluate to 1 if omitted.\n They can be defined as load curves.\n See the example below.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<discrete_material id=\"1\" name=\"test\" type=\"Hill\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Vmax>1</Vmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ac>0.1</ac>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Fmax>50</Fmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Ksh>5</Ksh>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Lmax>1.5</Lmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <L0>10</L0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Ftl type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <interpolate>smooth</interpolate> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n    <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0.0,\t0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0.1,\t0.000258139</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.2,\t0.00161616</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.3,\t0.00740118</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.4,\t0.0272783</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.5,\t0.0820396</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.6,\t0.201851</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.7,\t0.406524</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.8,\t0.670275</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>0.9,\t0.904792</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.0,\t0.999955</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.1,\t0.904792</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.2,\t0.670275</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.3,\t0.406524</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.4,\t0.201851</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.5,\t0.0820396</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.6,\t0.0272783</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.7,\t0.00740118</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.8,\t0.00161616</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>1.9,\t0.000258139</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \t<pt>2.0,\t0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</points>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Ftl> \n\\end_layout\n\n\\begin_layout LyX-Code\n</discrete_material> \n\\end_layout\n\n\\begin_layout Subsection\nDiscrete Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Discrete-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAfter the discrete materials are defined,\n the materials are assigned to the discrete element sets that are defined in the \n\\emph on\nMesh\n\\emph default\n section using the \n\\shape italic\ndiscrete \n\\shape default\nelement.\n This element requires two attributes:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nattribute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndmat\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete material ID\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete_set\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element set defined in the \n\\emph on\nMesh \n\\emph default\nsection.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample\n\\emph default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<discrete dmat=\"1\" discrete_set=\"set1\"/>\n\\end_layout\n\n\\begin_layout Subsection\nRigid Cable\n\\end_layout\n\n\\begin_layout Standard\nA rigid cable can be used to apply a load to a series of rigid bodies that are connected at fixed points (fixed with respect to the rigid body).\n The cable runs through these points and the user can prescribe the force at the end of the cable.\n The rigid cable requires the following parameters.\n\\end_layout\n\n\\begin_layout Description\nforce The magnitude of the force applied at the end of the cable.\n\\end_layout\n\n\\begin_layout Description\nforce_direction The direction of the force (FEBio will normalize this vector if needed.)\n\\end_layout\n\n\\begin_layout Description\nrelative If set to 1 the coordinates of the fixed points are relative to the rigid body frame of reference,\n otherwise they are in global coordinates.\n\\end_layout\n\n\\begin_layout Description\npoint For each fixed point,\n enter the coordinates of the point.\n This tag requires the rb attribute to denote the rigid body it is attached to.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following shows an example.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_cable>\n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<force lc=\"1\">1000</force>\n\\end_layout\n\n\\begin_layout LyX-Code\n<force_direction>0,0,-1</force_direction>\n\\end_layout\n\n\\begin_layout LyX-Code\n<relative>1</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<point rb=\"1\">0,0,0</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n<point rb=\"2\">0,0,0</point>\n\\end_layout\n\n\\end_deeper\n\\begin_layout LyX-Code\n</rigid_cable>\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a cable that runs through the centers of mass of two rigid bodies.\n The force is applied in the -z direction at the end of the cable (i.e.\n point 2).\n \n\\end_layout\n\n\\begin_layout LyX-Code\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nStep Section\n\\end_layout\n\n\\begin_layout Standard\nThe analysis can be divided into separate steps,\n where in each step,\n different boundary and loading conditions can be applied.\n Each analysis step is defined via a \n\\emph on\nstep \n\\emph default\nsection.\n In each step,\n the following sections can be defined:\n \n\\end_layout\n\n\\begin_layout Itemize\nControl\n\\end_layout\n\n\\begin_layout Itemize\nInitial\n\\end_layout\n\n\\begin_layout Itemize\nBoundary\n\\end_layout\n\n\\begin_layout Itemize\nLoads\n\\end_layout\n\n\\begin_layout Itemize\nConstraints\n\\end_layout\n\n\\begin_layout Itemize\nContact\n\\end_layout\n\n\\begin_layout Itemize\nRigid\n\\end_layout\n\n\\begin_layout Standard\nWhen a boundary condition,\n (load,\n constraint,\n contact,\n etc.,) is applied to a step,\n then this boundary condition (load,\n constraint,\n contact,\n etc.) is only active during that step.\n Boundary conditions (loads,\n constraints,\n contacts,\n etc.) that are defined in the main body of the file will persist through all the step.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<!-- main body of file -->\n\\end_layout\n\n\\begin_layout LyX-Code\n ...\n\\end_layout\n\n\\begin_layout LyX-Code\n<Step> \n\\end_layout\n\n\\begin_layout LyX-Code\n <step id=\"1\"> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    … \n\\end_layout\n\n\\begin_layout LyX-Code\n  </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n </step>\n\\end_layout\n\n\\begin_layout LyX-Code\n <step id=\"2\">\n\\end_layout\n\n\\begin_layout LyX-Code\n   <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    … \n\\end_layout\n\n\\begin_layout LyX-Code\n   </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n </step> \n\\end_layout\n\n\\begin_layout LyX-Code\n  … \n\\end_layout\n\n\\begin_layout LyX-Code\n</Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nLoadData Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:LoadData-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nLoadData\n\\shape default\n section is used to define load controllers.\n A load controller allows users to manipulate the value of most model parameters as an explicit or implicit function of time.\n A specific load controller is defined using the \n\\emph on\ntype\n\\emph default\n attribute.\n\\end_layout\n\n\\begin_layout Standard\nIn order to make a model parameter time-dependent,\n first define a load controller in the LoadData section.\n Then,\n use the id attribute assigned to the load controller as the value of the \n\\emph on\nlc\n\\emph default\n attribute.\n For example,\n consider the following load controller was defined in the LoadData section.\n\\end_layout\n\n\\begin_layout LyX-Code\n<load_controller \n\\series bold\nid=\"1\"\n\\series default\n type=\"math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <math>2.0*sin(pi*t)</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n</load_controller>\n\\end_layout\n\n\\begin_layout Standard\nThe id assigned to this load controller is 1.\n Then,\n this load controller can be used,\n for instance,\n in a boundary condition.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<bc type=\"prescribed displacement\" node_set=\"\n\\shape italic\n\\emph on\nset1\n\\shape default\n\\emph default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <dof>x</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <value \n\\series bold\nlc=\"1\"\n\\series default\n>3.14</value>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relative>0</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n<bc>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nvalue \n\\emph default\nparameter of this boundary condition is now made time dependent.\n Note that the actual value of this parameter will be the value of the load controller at a particular point in time,\n multiplied with the value specified by the tag's value (3.14 in the example).\n \n\\end_layout\n\n\\begin_layout Subsection\nThe loadcurve controller\n\\end_layout\n\n\\begin_layout Standard\nA loadcurve controller is defined by the \n\\shape italic\nloadcurve \n\\shape default\ntype.\n Each loadcurve is defined by repeating the \n\\shape italic\npoint \n\\shape default\nelement for all data points:\n\\end_layout\n\n\\begin_layout LyX-Code\n<load_controller id=\"1\" \n\\series bold\ntype=\"loadcurve\"\n\\series default\n>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <interpolate>LINEAR</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <extend>CONSTANT</extend>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <point> 0,\n 0 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n    <point> 1,\n 1 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n</loadcurve>\n\\end_layout\n\n\\begin_layout Standard\nFor a loadcurve,\n the type is optional,\n since it is the default controller if the type attribute is omitted.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nid \n\\shape default\nattribute is the loadcurve number and is used in other sections of the input file as a means to reference this curve.\n\\end_layout\n\n\\begin_layout Standard\nThe optional parameters \n\\shape italic\ninterpolate \n\\shape default\nand \n\\shape italic\nextend \n\\shape default\ndefine how the value of the loadcurve is interpolated from the data points.\n The interpolate defines the interpolation function and \n\\shape italic\nextend \n\\shape default\ndefines how the values of the loadcurve are determined outside of the interval defined by the first and last data point.\n The following tables list the possible values.\n The default values are marked with an asterisk (*).\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninterpolate\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTEP\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse a step interpolation function \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLINEAR*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse a linear interpolation function \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSMOOTH\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe values are interpolated using a cubic polynomial.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigLoadCurveTypes.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nThe different values for the \n\\emph on\ninterpolate\n\\emph default\n parameter of load curves\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nExtend\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCONSTANT*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value of the curve is the value of the closest endpoint \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEXTRAPOLATE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value is extrapolated linearly from the endpoints \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nREPEAT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe curve is repeated \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nREPEAT OFFSET\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe curve is repeated but offset from the endpoints \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigLoadCurveExtend.png\n\tlyxscale 50\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nThe different values for the \n\\emph on\nextend\n\\emph default\n parameter of the load curve.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nThe math controller\n\\end_layout\n\n\\begin_layout Standard\nUse the \n\\begin_inset Quotes eld\n\\end_inset\n\nmath\n\\begin_inset Quotes erd\n\\end_inset\n\n value for the \n\\emph on\ntype\n\\emph default\n attribute to define a math controller.\n This controller allows users to use a mathematical expression that defines a function of time.\n Use the \n\\begin_inset Quotes eld\n\\end_inset\n\nt\n\\begin_inset Quotes erd\n\\end_inset\n\n parameter to reference time.\n\\end_layout\n\n\\begin_layout LyX-Code\n<load_controller id=\"1\" \n\\series bold\ntype=\"math\"\n\\series default\n>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <math>2.0*sin(pi*t)</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n</load_controller>\n\\end_layout\n\n\\begin_layout Subsection\nThe math-interval controller\n\\end_layout\n\n\\begin_layout Standard\nThe math-interval controller is similar to the math controller,\n but the math expression is defined only over a time interval.\n The behavior outside the interval can be specified separately.\n The following parameters can be defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninterval\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmin and max value of the time interval.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nleft_extend\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHow the function is extrapolated before the min value.(1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nright_extend\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHow the function is extrapolated after the max value.\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmath\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe mathematical expression.\n Use \n\\begin_inset Quotes eld\n\\end_inset\n\nt\n\\begin_inset Quotes erd\n\\end_inset\n\n to reference time.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComment\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nThe values for \n\\emph on\nleft_extend \n\\emph default\nand \n\\emph on\nright_extend\n\\emph default\n can be set to one of the following values.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nExtend\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nzero\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value is set to zero.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nconstant\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value is the constant value at the end point\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nrepeat\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe curve is repeated\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<load_controller id=\"1\" name=\"LC1\" type=\"math-interval\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<interval>0,1</interval>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<left_extend>constant</left_extend>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<right_extend>constant</right_extend>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<math>sin(t)</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n</load_controller>\n\\end_layout\n\n\\begin_layout Subsection\nThe PID controller\n\\end_layout\n\n\\begin_layout Standard\nThe PID controller allows users to create simple control systems where the value of one model parameter is used to control the output of another model parameter.\n The PID controller calculates the output value as a sum of three terms:\n a term proportional to the error (i.e.\n the difference between a user-defined target value and the measurement),\n a derivative term,\n and an integral term.\n \n\\end_layout\n\n\\begin_layout Standard\nThis controller requires the following parameters:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nvar\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecify the model parameter that is used as process variable,\n i.e.\n the measurement.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\ntarget\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe target value for the process variable.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nKp\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nweight factor for the derivative term\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nKd\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nweight factor for the derivative term\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\emph on\nKi\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nweight factor for the integral term.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example:\n\\end_layout\n\n\\begin_layout LyX-Code\n<load_controller id=\"1\" \n\\series bold\ntype=\"PID\"\n\\series default\n>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <var>fem.rigid_body[1].euler.z</var>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <target>1.5708</target>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Kp>5</Kp>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Kd>1</Kd>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Ki>4</Ki>\n\\end_layout\n\n\\begin_layout LyX-Code\n</load_controller>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nOutput Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Output-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio usually splits the output in two files:\n the \n\\shape italic\nlogfile,\n\n\\shape default\n which contains the same information that was written to the screen during the analysis,\n and the \n\\shape italic\nplotfile,\n\n\\shape default\n which contains the results.\n The contents of these output files can be customized in the \n\\shape italic\nOutput\n\\shape default\n section.\n\\end_layout\n\n\\begin_layout Subsection\nLogfile\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Logfile\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe logfile records the same output that is printed to the screen.\n In addition,\n the user can request FEBio to output additional data to the logfile.\n This feature is called \n\\shape italic\ndata logging\n\\shape default\n.\n To use this feature,\n simply define the following element in the \n\\shape italic\nOutput\n\\shape default\n section of the input file:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Output>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <logfile [file=\"<log file>\"]>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <node_data [attributes]>item list</node_data>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <element_data [attributes]>item list</element_data>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rigid_body_data [attributes]>item list</rigid_body_data>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </logfile>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Output>\n\\end_layout\n\n\\begin_layout Standard\nThe optional attribute \n\\shape italic\nfile \n\\shape default\ndefines the name of the logfile.\n If omitted,\n a default name is used that is derived from the FEBio input file.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:FEBio-Output\"\nnolink \"false\"\n\n\\end_inset\n\n for details on default naming conventions for output files.\n\\end_layout\n\n\\begin_layout Standard\nAdditional data is stored to the logfile by adding one or more of the following elements:\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nnode_data\n\\shape default\n\\emph default\n request nodal data \n\\end_layout\n\n\\begin_layout Description\nface_data request surface data\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nelement_data\n\\shape default\n\\emph default\n request element data \n\\end_layout\n\n\\begin_layout Description\ndomain_data request domain data (i.e.\n element data integrated over a domain)\n\\end_layout\n\n\\begin_layout Description\nsurface_data request surface data (i.e.\n face data evaluated over a surface)\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nrigid_body_data\n\\shape default\n\\emph default\n request rigid body data \n\\end_layout\n\n\\begin_layout Description\nrigid_connector_data request data on a rigid connector\n\\end_layout\n\n\\begin_layout Description\nmodel_data request data on the model\n\\end_layout\n\n\\begin_layout Standard\nEach of these data classes takes the following attributes:\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\ndata\n\\shape default\n\\emph default\n an expression defining the data that is to be stored \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nname\n\\shape default\n\\emph default\n a descriptive name for the data (optional;\n default = data expression) \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nfile\n\\shape default\n\\emph default\n the name of the output file where the data is stored.\n (optional;\n default = logfile) \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\ndelim\n\\shape default\n\\emph default\n the delimiter used to separate data in multi-column format (optional;\n default = space) \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nformat\n\\shape default\n\\emph default\n an optional format string (optional;\n default = not used) \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\ndata \n\\shape default\nattribute is the most important one and is mandatory.\n It contains a list of variable names,\n separated by a semi-colon.\n The available variable names depend on the data class and are defined below.\n For example,\n the data expression:\n\\end_layout\n\n\\begin_layout LyX-Code\ndata=\"x;y;z\"\n\\end_layout\n\n\\begin_layout Standard\nwill store the variables \n\\begin_inset Formula $x$\n\\end_inset\n\n,\n \n\\begin_inset Formula $y$\n\\end_inset\n\n and \n\\begin_inset Formula $z$\n\\end_inset\n\n in separate columns.\n See below for more examples.\n\\end_layout\n\n\\begin_layout Standard\nThe optional \n\\shape italic\nname \n\\shape default\nattribute is a descriptive name for the data.\n It is used in the logfile to refer to this data and can be used to quickly find the data record in the logfile.\n If omitted,\n the data expression is used as the name.\n\\end_layout\n\n\\begin_layout Standard\nThe\n\\shape italic\n file \n\\shape default\nattribute defines the name of the output file where the data is to be stored.\n This attribute is optional and when not specified the data will be stored in the logfile.\n\\end_layout\n\n\\begin_layout Standard\nThe optional \n\\shape italic\ndelim\n\\shape default\n attribute defines the delimiter that is used in multi-column format.\n As described above,\n data can be stored in multiple columns and the delimiter is used to separate the columns.\n The default is a single space.\n\\end_layout\n\n\\begin_layout Standard\nThe optional \n\\shape italic\nformat \n\\shape default\nattribute defines a format string that will be used to format the output.\n If this attribute is present,\n the \n\\shape italic\ndelim \n\\shape default\nattribute will be ignored.\n The format string is composed of literal characters and special formatting characters.\n The special formatting characters are preceded by the percentage character (%).\n The following formatting characters are currently defined.\n\\end_layout\n\n\\begin_layout Description\n%i replace with the index of the corresponding item (i.e.\n node numbers for node data) \n\\end_layout\n\n\\begin_layout Description\n%g replace with a data value.\n Use a %g for each data item.\n \n\\end_layout\n\n\\begin_layout Description\n%t insert tab character in output.\n \n\\end_layout\n\n\\begin_layout Description\n%n insert newline character in output.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<node_data data=\"x;y;z\" format='<node id=\"%i\">%g,%g,%g</node>'></node_data>\n\\end_layout\n\n\\begin_layout Standard\nwill print the following output (e.g.\n for node 1).\n\\end_layout\n\n\\begin_layout LyX-Code\n<node id=\"1\">0.1,0.2,0.3</node>\n\\end_layout\n\n\\begin_layout Standard\nNotice the use of the apostrophe (') in the format string.\n This is necessary in order to include the quotation marks as part of the format string.\n Also note that each data string will automatically be printed on a new line,\n so there is no need to end the format string with a newline character.\n\\end_layout\n\n\\begin_layout Standard\nThe value of the data elements is a list of items for which the data is to be stored.\n For example,\n for the \n\\shape italic\nnode_data \n\\shape default\nelement the value is a list of nodes,\n for the \n\\shape italic\nelement_data \n\\shape default\nelement it is a list of FE elements and for the \n\\shape italic\nrigid_body \n\\shape default\nelement it is a list of rigid bodies.\n The value may be omitted in which case the data for all items will be stored.\n For instance,\n omitting the value for the \n\\shape italic\nnode_data \n\\shape default\nelement will store the data for all nodes.\n\\end_layout\n\n\\begin_layout Standard\nAs stated above,\n the data is either stored in the logfile or in a separate file.\n In any case,\n a record is made in the logfile.\n When storing the data in the logfile,\n the following entry will be found in the logfile at the end of each converged timestep for each data element:\n\\end_layout\n\n\\begin_layout LyX-Code\nData Record #<n>\n\\end_layout\n\n\\begin_layout LyX-Code\nStep = <time step>\n\\end_layout\n\n\\begin_layout LyX-Code\nTime = <time value>\n\\end_layout\n\n\\begin_layout LyX-Code\nData = <data name>\n\\end_layout\n\n\\begin_layout LyX-Code\n<actual data goes here>\n\\end_layout\n\n\\begin_layout Standard\nThe record number \n\\begin_inset Formula $n$\n\\end_inset\n\n corresponds to the \n\\begin_inset Formula $n$\n\\end_inset\n\nth data element in the input file.\n The \n\\shape italic\nStep \n\\shape default\nvalue is the current time step.\n The \n\\shape italic\nTime \n\\shape default\nvalue is the current solution time.\n The \n\\shape italic\nData \n\\shape default\nvalue is the name of the data element as provided by the \n\\shape italic\nname \n\\shape default\nattribute (or the \n\\shape italic\ndata \n\\shape default\nattribute if \n\\shape italic\nname \n\\shape default\nis omitted).\n The actual data immediately follows this record.\n If multiple column output is used,\n the columns are separated by the \n\\shape italic\ndelim \n\\shape default\nattribute of the data element.\n\\end_layout\n\n\\begin_layout Standard\nWhen storing the data in a separate file,\n the format is slightly different:\n\\end_layout\n\n\\begin_layout LyX-Code\nData Record #<n>\n\\end_layout\n\n\\begin_layout LyX-Code\nStep = <time step>\n\\end_layout\n\n\\begin_layout LyX-Code\nTime = <time value>\n\\end_layout\n\n\\begin_layout LyX-Code\nData = <data name>\n\\end_layout\n\n\\begin_layout LyX-Code\nFile = <file name>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nFile \n\\shape default\nvalue is the name of the physical file.\n Note that this is the name to which the time step number is appended.\n In addition,\n the physical file that stores the data contains the following header:\n\\end_layout\n\n\\begin_layout LyX-Code\n*Step = <time step>\n\\end_layout\n\n\\begin_layout LyX-Code\n*Time = <time value>\n\\end_layout\n\n\\begin_layout LyX-Code\n*Data = <data name>\n\\end_layout\n\n\\begin_layout LyX-Code\n<actual data goes here>\n\\end_layout\n\n\\begin_layout Standard\nIn either case,\n the actual data is a multi-column list,\n separated by the delimiter specified with the \n\\shape italic\ndelim \n\\shape default\nattribute (or a space when omitted).\n The first column always contains the item number.\n For example,\n the following data element:\n\\end_layout\n\n\\begin_layout LyX-Code\n<node_data\n\\end_layout\n\n\\begin_layout LyX-Code\ndata=\"x;y;z\" name=\"nodal coordinates\" delim=\",\">1:4:1</node_data>\n\\end_layout\n\n\\begin_layout Standard\nwill result in the following record in the logfile:\n\\end_layout\n\n\\begin_layout LyX-Code\nData Record #1\n\\end_layout\n\n\\begin_layout LyX-Code\nStep = 1\n\\end_layout\n\n\\begin_layout LyX-Code\nTime = 0.1\n\\end_layout\n\n\\begin_layout LyX-Code\nData = \"nodal coordinates\"\n\\end_layout\n\n\\begin_layout LyX-Code\n1,0.000,0.000,0.000\n\\end_layout\n\n\\begin_layout LyX-Code\n2,1.000,0.000,0.000\n\\end_layout\n\n\\begin_layout LyX-Code\n3,1.000,1.000,0.000\n\\end_layout\n\n\\begin_layout LyX-Code\n4,0.000,1.000,0.000\n\\end_layout\n\n\\begin_layout Standard\nThis data record is repeated for each converged time step.\n Please see \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Logfile-Variables\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for a list of available log variables.\n \n\\end_layout\n\n\\begin_layout Subsection\nPlotfile\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Plotfile\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n all the results are stored in a binary database,\n referred to as the \n\\shape italic\nplotfile\n\\shape default\n.\n The preferred storage format is the FEBio binary database format (referred to as the \n\\shape italic\nxplt \n\\shape default\nformat)\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\n As of FEBio version 2.0,\n the LSDYNA database is no longer supported.\n The FEBio database format is the only format that will be supported from now on.\n\\end_layout\n\n\\end_inset\n\n.\n This section describes how to customize the data that is stored to the plotfile.\n\\end_layout\n\n\\begin_layout Standard\nTo define the contents of the plotfile the \n\\shape italic\nplotfile \n\\shape default\nelement needs to be added in the \n\\emph on\nOutput \n\\emph default\nsection of the FEBio input file.\n\\end_layout\n\n\\begin_layout LyX-Code\n<plotfile [type=\"febio\"] [file=\"name.xplt\"]/>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ntype \n\\emph default\nattribute defines the output format.\n As of version 4.4,\n the user can choose between \n\\begin_inset Quotes eld\n\\end_inset\n\nfebio\n\\begin_inset Quotes erd\n\\end_inset\n\n and \n\\begin_inset Quotes eld\n\\end_inset\n\nvtk\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n When choosing \n\\begin_inset Quotes eld\n\\end_inset\n\nfebio\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n which is the default,\n the results will be stored in the standard binary \n\\emph on\nxplt \n\\emph default\nformat.\n When choosing \n\\begin_inset Quotes eld\n\\end_inset\n\nvtk\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n the output is stored as individual VTK (legacy format) files,\n one for each completed time step.\n Note that due to limitations of the vtk format,\n not all plot variables can be exported to VTK file.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nfile\n\\shape default\n attribute is also optional and allows the user to define the file name of the plotfile.\n If this attribute is omitted,\n FEBio will use a default file name for the plotfile.\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n FEBio will store the most common data variables to the plot file.\n However,\n it is advised to always specify the specific contents of the plotfile.\n This can be done by adding \n\\shape italic\nvar \n\\shape default\nelements in the \n\\shape italic\nplotfile \n\\shape default\nsection as described below.\n\\end_layout\n\n\\begin_layout Standard\nPlotfile variables are defined using the \n\\shape italic\nvar \n\\shape default\nkeyword.\n This tag takes one attribute,\n namely the \n\\shape italic\ntype\n\\shape default\n of the variable.\n The \n\\emph on\ntype\n\\emph default\n defines the specific output variable that will be stored to the plot file.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"\n\\emph on\nname\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\shape italic\nname \n\\shape default\nis the name of the output variable.\n (For a complete list of supported output variables,\n please see \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Plotfile-Variables\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout Standard\nAs of FEBio 2.4,\n additional information can be added in the type attribute of the variable definition.\n The general format is as follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"\n\\emph on\nname\n\\emph default\n[\n\\emph on\nfilter\n\\emph default\n]=\n\\emph on\nalias\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout Standard\nThe filter is defined by appending the name of the plot variable with the filter inside square brackets.\n The filter can be either an integer,\n or a string.\n If the filter is a string,\n it must be surrounded by single quotes.\n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"\n\\emph on\nname\n\\emph default\n['\n\\emph on\nfilter\n\\emph default\n string']=\n\\emph on\nalias\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout Standard\nThe optional alias can be used to rename the variable.\n The alias will be used as the name of the plot variable in the plot file,\n and this is the name that will show up in post-processing software such as FEBio Studio.\n \n\\end_layout\n\n\\begin_layout Standard\nSome plot variables use filters to resolve possible ambiguities.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"solute concentration['solute1']\"/>\n\\end_layout\n\n\\begin_layout Standard\nThis example will store the solute concentration of a solute named 'solute1' to the plot file.\n\\end_layout\n\n\\begin_layout Standard\nIn addition,\n an alias can be used to define a more descriptive name of the plot variable.\n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"solute concentration['Na']=Na concentration\"/>\n\\end_layout\n\n\\begin_layout Standard\nThis variable will store the solute concentration of a solute named 'Na' to the plot file using the name 'Na concentration'.\n\\end_layout\n\n\\begin_layout Standard\nThe following example shows how to export a particular fiber vector from a solid mixture.\n Since solid mixtures can have several fiber distributions associated,\n it is necessary to specify which component of the mixture the plot variable refers to.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"fiber vector['solid[0]']=v1\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"fiber vector['solid[1]']=v2\"/>\n\\end_layout\n\n\\begin_layout Standard\nSome plot variables require the specification of a named surface (* in table below).\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<var type=\"fluid surface force\" surface=\"airfoil\"/>\n\\end_layout\n\n\\begin_layout Standard\nThe named surface should be described in a \n\\emph on\nSurface\n\\emph default\n element (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Surface-Section\"\nnolink \"false\"\n\n\\end_inset\n\n) within \n\\emph on\nMesh\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Geometry-Section\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nThe following example shows a complete definition of the plotfile section and stores nodal displacements and element stresses.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<plotfile type=\"febio\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var type=\"displacement\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var type=\"stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</plotfile>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nMaterials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following sections describe the material parameters for each of the available constitutive models,\n along with a short description of each material.\n A more detailed theoretical description of the constitutive models can be found in the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Section\nElastic Solids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Elastic-Solids\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section describes the elastic materials,\n which are materials defined by a hyperelastic strain-energy function.\n A distinction will be made between so-called \n\\shape italic\nunconstrained \n\\shape default\nand \n\\shape italic\nuncoupled \n\\shape default\nmaterials.\n The former describe materials that can undergo volumetric compression.\n The latter are used for modeling (nearly-) incompressible materials,\n thus their constitutive models are constrained to produce an isochoric deformation,\n as long as the user has selected a sufficiently large bulk modulus to enforce that constraint.\n\\end_layout\n\n\\begin_layout Subsection\nSpecifying Fiber Orientation or Material Axes\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Specifying-Fiber-Orientation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSome of the materials are transversely isotropic,\n requiring the specification of an initial material direction,\n which is called a \n\\shape italic\nfiber\n\\shape default\n direction in FEBio.\n Other materials are orthotropic,\n requiring the specification of initial material axes that define the three planes of symmetry for those materials.\n Only one of these specifications should be provided.\n By default,\n material axes are aligned with the global Cartesian basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n and the fiber direction is along \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n.\n Local fiber or material axes orientation may be specified in several ways.\n FEBio gives the option to automatically generate the orientation,\n based on some user-specified parameters.\n However,\n the user can override this feature and specify the fiber or axes directions for each element manually in the \n\\shape italic\nElementData\n\\shape default\n section.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:MeshData-Section\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on how to do this.\n\\end_layout\n\n\\begin_layout Subsubsection\nTransversely Isotropic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Transversely-Isotropic-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor transversely isotropic materials fiber orientation is specified with the \n\\shape italic\nfiber \n\\shape default\nelement.\n This element takes one attribute,\n namely \n\\shape italic\ntype\n\\shape default\n,\n which specifies the type of the fiber generator.\n The possible values are specified in the following table.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\n\\shape italic\ntype\n\\shape default\n Value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlocal\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse local element numbering (1) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvector\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefine the fiber orientation as a constant vector (2) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspherical\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecifies a spherical fiber distribution (3) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncylindrical\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecifies a cylindrical fiber distribution (4) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nangles\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecifies the fiber direction using spherical angles (5) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf the \n\\shape italic\ntype \n\\shape default\nattribute is omitted,\n the fiber distribution will follow the local element nodes 1 and 2.\n This would be the same as setting the fiber attribute to \n\\shape italic\nlocal \n\\shape default\nand setting the value to \n\\begin_inset Quotes eld\n\\end_inset\n\n1,2\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nIn this case,\n the fiber direction is determined by local element node numbering.\n The value is interpreted as the local node numbers of the nodes that define the direction of the fiber.\n The following example defines a local fiber axis by local element nodes 1 and 2.\n This option is very useful when the local fiber direction can be interpreted as following one of the mesh edges.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<fiber type=\"local\">1,2</fiber>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigLocalFiberDirectionOption.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\n\\emph on\nlocal\n\\emph default\n fiber direction option\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe fiber orientation is specified by a vector.\n The value is the direction of the fiber.\n The following defines all element fiber directions in the direction of the vector [1,0,0]:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigVectorFiberDirectionOption.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\n\\emph on\nvector\n\\emph default\n fiber direction option\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe fiber orientation is determined by a point in space and the global location of each element integration point.\n The value is the location of the point.\n The following example defines a spherical fiber distribution centered at [0,0,1]:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<fiber type=\"spherical\">0,0,1</fiber>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigSphericalFiberDirectionOption.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\n\\emph on\nspherical\n\\emph default\n fiber direction option\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\n\n\\shape italic\ncylindrical\n\\shape default\n:\n This type generates a fiber distribution that is cylindrical.\n The following subparameters must be defined.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Description\n\n\\shape italic\n\\emph on\ncenter\n\\emph default\n \n\\shape default\ndefines the center of the cylinder \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\naxis\n\\shape default\n\\emph default\n defines the axis of the cylinder \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nvector\n\\shape default\n\\emph default\n defines a vector that will be transported around the cylinder \n\\end_layout\n\n\\begin_layout LyX-Code\n<fiber type=\"cylindrical\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <center>0,0,0</center>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <axis>0,0,1</axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vector>0,1,0</vector>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fiber>\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\n\n\\shape italic\nangles\n\\shape default\n:\n This type generates a fiber orientation via the specification of spherical angles (azimuth and declination) relative to the local material axes (or global coordinate system,\n if no local material axes are defined).\n The following subparameters must be defined.\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Description\n\n\\shape italic\n\\emph on\ntheta\n\\emph default\n \n\\shape default\nazimuth angle (in degrees) \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nphi\n\\shape default\n\\emph default\n declination angle (in degrees) \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigSCS.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\align center\n\n\\series bold\nSpherical angles\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber is oriented along \n\\begin_inset Formula \n\\[\n\\mathbf{e}_{r}=\\cos\\theta\\sin\\phi\\,\\mathbf{e}_{1}+\\sin\\theta\\sin\\phi\\,\\mathbf{e}_{2}+\\cos\\phi\\,\\mathbf{e}_{3},0\\leqslant\\theta<2\\pi,\\;0\\leqslant\\phi\\leqslant\\pi,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal vectors representing the local element coordinate system (when specified,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or global coordinate system.\n\\end_layout\n\n\\begin_layout LyX-Code\n<fiber type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <theta>20</center>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fiber>\n\\end_layout\n\n\\begin_layout Standard\nWhen specifying a fiber direction \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n,\n FEBio generates a set of orthogonal material axes as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n generated with \n\\begin_inset Formula $\\mathbf{d}=\\boldsymbol{j}$\n\\end_inset\n\n,\n or else \n\\begin_inset Formula $\\mathbf{d}=\\boldsymbol{k}$\n\\end_inset\n\n if \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is collinear with \n\\begin_inset Formula $\\boldsymbol{j}$\n\\end_inset\n\n (where the triple \n\\series bold\n\n\\begin_inset Formula $\\boldsymbol{i,j,k}$\n\\end_inset\n\n\n\\series default\n refers to unit vectors defining the global coordinate system,\n i.e.\n \n\\begin_inset Formula $\\boldsymbol{i}=\\left(1,0,0\\right)$\n\\end_inset\n\n,\n etc.).\n Because of the non-uniqueness of these material axes (only \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n is along a uniquely defined direction in a \n\\shape italic\nfiber\n\\shape default\n element),\n caution should be used when material axes are compounded,\n as may occur in nested materials such as solid mixtures described in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n & \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n To enforce uniqueness,\n use the \n\\shape italic\nmat_axis\n\\shape default\n element instead of the \n\\shape italic\nfiber\n\\shape default\n element.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Subsubsection\nOrthotropic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Orthotropic-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor orthotropic materials,\n the user needs to specify two fiber directions \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n\n\\series default\n and \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n.\n From these FEBio will generate an orthonormal set of material axes vectors as follows:\n \n\\begin_inset Formula \n\\[\n\\mathbf{e}_{1}=\\frac{\\mathbf{a}}{\\left\\Vert \\mathbf{a}\\right\\Vert },\\quad\\mathbf{e}_{2}=\\mathbf{e}_{3}\\times\\mathbf{e}_{1},\\quad\\mathbf{e}_{3}=\\frac{\\mathbf{a}\\times\\mathbf{d}}{\\left\\Vert \\mathbf{a}\\times\\mathbf{d}\\right\\Vert }\\,.\n\\]\n\n\\end_inset\n\nThe vectors \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n \n\\series default\nand \n\\series bold\n\n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n \n\\series default\nare defined using the \n\\shape italic\nmat_axis \n\\shape default\nelement.\n This element takes a \n\\shape italic\ntype\n\\shape default\n attribute,\n which can take on the following values:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"none\" valignment=\"top\" width=\"68pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlocal\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse local element numbering (1) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvector\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the vectors \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n\n\\series default\n and \n\\series bold\n\n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n\n\\series default\n directly.\n (2) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nangles\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify the angles \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n [deg].\n (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nWhen specifying \n\\shape italic\nlocal \n\\shape default\nas the material axis type,\n the value is interpreted as a list of three local element node numbers.\n When specifying zero for all three,\n the default (1,2,4) is used.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nWhen using the \n\\shape italic\nvector \n\\shape default\ntype,\n you need to define the two generator vectors \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $d$\n\\end_inset\n\n.\n These are specified as child elements of the \n\\shape italic\nmat_axis \n\\shape default\nelement:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <a>1,0,0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <d>0,1,0</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n</mat_axis>\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nWhen using the \n\\shape italic\nangle \n\\shape default\ntype,\n you need to define the two angles \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n in degrees.\n These are specified as child elements of the \n\\shape italic\nmat_axis \n\\shape default\nelement:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout LyX-Code\n<mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n</mat_axis>\n\\end_layout\n\n\\begin_layout Standard\nThe material axes \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{r},\\mathbf{e}_{\\theta},\\mathbf{e}_{\\varphi}\\right\\} $\n\\end_inset\n\n are related to the global Cartesian basis (or local element axes) \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n via\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{e}_{r} & =\\cos\\theta\\sin\\phi\\,\\mathbf{e}_{1}+\\sin\\theta\\sin\\phi\\,\\mathbf{e}_{2}+\\cos\\phi\\,\\mathbf{e}_{3}\\\\\n\\mathbf{e}_{\\theta} & =-\\sin\\theta\\,\\mathbf{e}_{1}+\\cos\\theta\\,\\mathbf{e}_{2}\\\\\n\\mathbf{e}_{\\varphi} & =-\\cos\\theta\\cos\\phi\\,\\mathbf{e}_{1}-\\sin\\theta\\cos\\phi\\,\\mathbf{e}_{2}+\\sin\\phi\\,\\mathbf{e}_{3}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUncoupled Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUncoupled,\n nearly-incompressible hyperelastic materials are described by a strain energy function that features an additive decomposition of the hyperelastic strain energy into deviatoric and dilational parts \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi\\left(\\mathbf{C}\\right)=\\tilde{\\Psi}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right),\\label{eq1}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{\\tilde{C}}=\\mathbf{\\mathbf{\\tilde{F}}^{T}\\cdot\\tilde{F}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{\\tilde{F}}=J^{-1/3}\\mathbf{F}$\n\\end_inset\n\n is the deviatoric part of the deformation gradient.\n The resulting 2\n\\begin_inset Formula $^{\\mathrm{nd}}$\n\\end_inset\n\n Piola-Kirchhoff stress is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{S}=J^{-2/3}\\Dev\\left[\\mathbf{\\tilde{S}}\\right]+pJ\\mathbf{C}^{-1},\\label{eq2}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\tilde{S}}=2\\frac{\\partial\\tilde{\\Psi}}{\\partial\\mathbf{\\tilde{C}}}\\quad,\\label{eq3}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\begin{equation}\np:=\\frac{dU}{dJ},\\label{eq4}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\Dev\\left[\\cdot\\right]$\n\\end_inset\n\n is the deviatoric operator in the material frame.\n\\end_layout\n\n\\begin_layout Standard\nThe corresponding Cauchy stress is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\dev\\left[\\tilde{\\boldsymbol{\\sigma}}\\right]+p\\mathbf{I},\\label{eq5}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}=J^{-1}\\mathbf{\\tilde{F}}\\cdot\\mathbf{\\tilde{S}}\\cdot\\mathbf{\\tilde{F}}^{T}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\dev\\left[\\cdot\\right]$\n\\end_inset\n\n is the deviatoric operator in the spatial frame.\n\\end_layout\n\n\\begin_layout Standard\nFor these materials,\n the entire bulk (volumetric) behavior is determined by the function \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $p$\n\\end_inset\n\n represents the entire hydrostatic stress.\n The function \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n is constructed to have a value of 0 for \n\\begin_inset Formula $J=$\n\\end_inset\n\n1 and to have a positive value for all other values of \n\\begin_inset Formula $J>$\n\\end_inset\n\n0.\n The computational literature has employed different functional forms for \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n.\n To compare one's results to literature references that employ a \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n that differs from the default FEBio formulation,\n use one of the \n\\begin_inset Quotes eld\n\\end_inset\n\npressure_model\n\\begin_inset Quotes erd\n\\end_inset\n\n options listed below.\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n all of these materials make use of three-field elements (when available for a particular element type),\n as described by Simo and Taylor \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n This element uses a trilinear interpolation of the displacement field and piecewise-constant interpolations for the pressure and volume ratio.\n\\end_layout\n\n\\begin_layout Standard\nThe properties that should be specified for all uncoupled materials are:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus \n\\begin_inset Formula $k$\n\\end_inset\n\n appearing in \n\\begin_inset Formula $U\\left(J\\right)$\n\\end_inset\n\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 [\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<pressure_model>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nChoose from 0 to 3 for various models (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nThe pressure models are as follows:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\n0:\n \n\\begin_inset Formula $U\\left(J\\right)=\\frac{k}{2}\\left(\\ln J\\right)^{2}$\n\\end_inset\n\n (FEBio default)\n\\end_layout\n\n\\begin_layout Enumerate\n1:\n \n\\begin_inset Formula $U\\left(J\\right)=\\frac{k}{4}\\left(J^{2}-2\\ln J-1\\right)$\n\\end_inset\n\n (Nike3D's Ogden material)\n\\end_layout\n\n\\begin_layout Enumerate\n2:\n \n\\begin_inset Formula $U\\left(J\\right)=\\frac{k}{2}\\left(J-1\\right)^{2}$\n\\end_inset\n\n (ABAQUS)\n\\end_layout\n\n\\begin_layout Enumerate\n3:\n \n\\begin_inset Formula $U\\left(J\\right)=\\frac{k}{2}\\left(\\frac{1}{2}\\left(J^{2}-1\\right)-\\ln J\\right)$\n\\end_inset\n\n (Gasser-Ogden-Holzapfel material implementation in ABAQUS)\n\\end_layout\n\n\\end_deeper\n\\begin_layout Standard\nThe uncoupled materials and the associated three-field element are very effective for representing nearly incompressible material behavior.\n Fully incompressible behavior can be obtained (for all uncoupled materials) using an augmented Lagrangian method.\n To use the three-field element formulation and augmented Lagrangian method,\n please see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:SolidDomain-Section\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n \n\\begin_inset Quotes eld\n\\end_inset\n\nthree-field-solid\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n For enforcement of the incompressibility constraint to a user-defined tolerance,\n the user must define two material parameters for the three-field-solid:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<laugon>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTurn augmented Lagrangian on for this material or off (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (off)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<atol>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAugmentation tolerance (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.01\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\nA value of \n\\shape italic\n1 \n\\shape default\n(one) turns augmentation on,\n where a value of 0 (zero) turns it off.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe augmentation tolerance determines the convergence condition that is used for the augmented Lagrangian method:\n convergence is reached when the relative ratio of the Lagrange multiplier norm of the previous augmentation \n\\begin_inset Formula $\\left\\Vert \\lambda_{k}\\right\\Vert $\n\\end_inset\n\n to the current one \n\\begin_inset Formula $\\left\\Vert \\lambda_{k+1}\\right\\Vert $\n\\end_inset\n\n is less than the specified value:\n \n\\begin_inset Formula \n\\[\n\\left|\\frac{\\left\\Vert \\lambda_{k+1}\\right\\Vert -\\left\\Vert \\lambda_{k}\\right\\Vert }{\\left\\Vert \\lambda_{k+1}\\right\\Vert }\\right|<\\varepsilon\n\\]\n\n\\end_inset\n\nThus,\n a value of 0.01 implies that the change in norm between the previous augmentation loop and the current loop is less than 1%.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>5</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.4</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>10000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <laugon>1</laugon> \n\\begin_inset Formula $\\leftarrow$\n\\end_inset\n\n turns on augmented Lagrangian iterations\n\\end_layout\n\n\\begin_layout LyX-Code\n  <atol>0.05</atol> \n\\begin_inset Formula $\\leftarrow$\n\\end_inset\n\n sets the augmentation tolerance\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nArruda-Boyce\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Arruda-Boyce\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material describes an incompressible Arruda-Boyce model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Arruda93\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters are required:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninitial modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<N>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnumber of links in chain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe uncoupled strain energy function for the Arruda-Boyce material is given by:\n \n\\begin_inset Formula \n\\[\n\\Psi=\\mu\\sum\\limits_{i=1}^{5}\\frac{C_{i}}{N^{i-1}}\\left(\\tilde{I}_{1}^{i}-3^{i}\\right)+U\\left(J\\right),\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula $C_{1}=\\frac{1}{2},C_{2}=\\frac{1}{20},C_{3}=\\frac{11}{1050},C_{4}=\\frac{19}{7000},C_{5}=\\frac{519}{673750}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\nthe first invariant of the right Cauchy-Green tensor.\n The volumetric strain function \n\\begin_inset Formula $U$\n\\end_inset\n\n is defined as follows,\n \n\\begin_inset Formula \n\\[\nU\\left(J\\right)=\\frac{1}{2}k\\left(\\ln J\\right)^{2}\\,.\n\\]\n\n\\end_inset\n\nThis material model was proposed by Arruda and Boyce \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Arruda93\"\nliteral \"true\"\n\n\\end_inset\n\n and is based on an eight-chain representation of the macromolecular network structure of polymer chains.\n The strain energy form represents a truncated Taylor series of the inverse Langevin function,\n which arises in the statistical treatment of non-Gaussian chains.\n The parameter \n\\begin_inset Formula $N$\n\\end_inset\n\n is related to the locking stretch \n\\begin_inset Formula $\\lambda_{L}$\n\\end_inset\n\n,\n the stretch at which the chains reach their full extended state,\n by \n\\begin_inset Formula $\\lambda_{L}=\\sqrt{N}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"Arruda-Boyce\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.09</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <N>26.5</N>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal Fiber Distribution Uncoupled\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:EFD-Uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for an ellipsoidal continuous fiber distribution in an uncoupled formulation is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nEFD uncoupled\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Since fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a stable uncoupled material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\beta_{1},\\beta_{2},\\beta_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\xi_{1},\\xi_{2},\\xi_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe stress \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n for this material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lanir83,Ateshian07a,Ateshian09a\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\tilde{\\boldsymbol{\\sigma}}=\\int_{0}^{2\\pi}\\int_{0}^{\\pi}H\\left(\\tilde{I}_{n}-1\\right)\\tilde{\\sigma}_{n}\\left(\\mathbf{n}\\right)\\sin\\varphi\\,d\\varphi\\,d\\theta.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $\\tilde{I}_{n}=\\tilde{\\lambda}_{n}^{2}=\\mathbf{N}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{N}$\n\\end_inset\n\n is the square of the fiber stretch \n\\begin_inset Formula $\\tilde{\\lambda}_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n is the unit vector along the fiber direction (in the reference configuration),\n which in spherical angles is directed along \n\\begin_inset Formula $\\left(\\theta,\\varphi\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{\\tilde{F}}\\cdot\\mathbf{N}/\\tilde{\\lambda}_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $H\\left(.\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n The fiber stress is determined from a fiber strain energy function in the usual manner,\n \n\\begin_inset Formula \n\\[\n\\tilde{\\sigma}_{n}=\\frac{2\\tilde{I}_{n}}{J}\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{I}_{n}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\n\\]\n\n\\end_inset\n\nwhere in this material,\n \n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}\\left(\\mathbf{n},\\tilde{I}_{n}\\right)=\\xi\\left(\\mathbf{n}\\right)\\left(\\tilde{I}_{n}-1\\right)^{\\beta\\left(\\mathbf{n}\\right)}\\,.\n\\]\n\n\\end_inset\n\nThe materials parameters \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n are determined from:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}\\xi\\left(\\mathbf{n}\\right) & =\\left(\\frac{\\cos^{2}\\theta\\sin^{2}\\varphi}{\\xi_{1}^{2}}+\\frac{\\sin^{2}\\theta\\sin^{2}\\varphi}{\\xi_{2}^{2}}+\\frac{\\cos^{2}\\varphi}{\\xi_{3}^{2}}\\right)^{-1/2}\\\\\n\\beta\\left(\\mathbf{n}\\right) & =\\left(\\frac{\\cos^{2}\\theta\\sin^{2}\\varphi}{\\beta_{1}^{2}}+\\frac{\\sin^{2}\\theta\\sin^{2}\\varphi}{\\beta_{2}^{2}}+\\frac{\\cos^{2}\\varphi}{\\beta_{3}^{2}}\\right)^{-1/2}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nThe orientation of the material axis can be defined as explained in detail in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"EFD uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>10,\n 12,\n 15</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5,\n 3,\n 3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal Fiber Distribution Mooney-Rivlin\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:EFD-Mooney-Rivlin\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Mooney-Rivlin material with an ellipsoidal continuous fiber distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nEFD Mooney-Rivlin\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin parameter c1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin parameter c2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\beta_{1},\\beta_{2},\\beta_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\xi_{1},\\xi_{2},\\xi_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe stress \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n for this material is given by,\n \n\\begin_inset Formula \n\\[\n\\tilde{\\boldsymbol{\\sigma}}=\\tilde{\\boldsymbol{\\sigma}}_{MR}+\\tilde{\\boldsymbol{\\sigma}}_{f}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{MR}$\n\\end_inset\n\n is the stress from the Mooney-Rivlin basis (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mooney-Rivlin\"\nnolink \"false\"\n\n\\end_inset\n\n),\n and \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{f}$\n\\end_inset\n\n is the stress contribution from the ellipsoidal fiber distribution (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:EFD-Uncoupled\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The orientation of the material axes can be defined as explained in detail in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"EFD Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>4.5,4.5,4.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ksi>1,1,1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>20000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal Fiber Distribution Veronda-Westmann\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:EFD-Veronda-Westmann\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Veronda-Westmann material with an ellipsoidal continuous fiber distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nEFD Veronda-Westmann\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFirst VW coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond VW coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left({\\beta_{1},\\beta_{2},\\beta_{3}}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left({\\xi_{1},\\xi_{2},\\xi_{3}}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe stress \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n for this material is given by,\n \n\\begin_inset Formula \n\\[\n\\tilde{\\boldsymbol{\\sigma}}=\\tilde{\\boldsymbol{\\sigma}}_{VW}+\\tilde{\\boldsymbol{\\sigma}}_{f}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{VW}$\n\\end_inset\n\n is the stress from the Veronda-Westmann basis (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Veronda-Westmann\"\nnolink \"false\"\n\n\\end_inset\n\n),\n and \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}_{f}$\n\\end_inset\n\n is the stress contribution from the ellipsoidal fiber distribution (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:EFD-Uncoupled\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"EFD Veronda-Westmann\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.5</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>4.5,4.5,4.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ksi>1,1,1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFung Orthotropic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fung-Orthotropic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for orthotropic Fung elasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung79,Fung93\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nFung orthotropic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"10\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E_{1}$\n\\end_inset\n\n Young's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E_{2}$\n\\end_inset\n\n Young's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E_{3}$\n\\end_inset\n\n Young's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $G_{12}$\n\\end_inset\n\n shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $G_{23}$\n\\end_inset\n\n shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $G_{31}$\n\\end_inset\n\n shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\nu_{12}$\n\\end_inset\n\n Poisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\nu_{23}$\n\\end_inset\n\n Poisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\nu_{31}$\n\\end_inset\n\n Poisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $c$\n\\end_inset\n\n coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe hyperelastic strain energy function is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian09\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\Psi=\\frac{1}{2}c\\left(e^{\\tilde{Q}}-1\\right)+U\\left(J\\right)\\,,\\label{eq6}\n\\end{equation}\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\[\n\\tilde{Q}=c^{-1}\\sum\\limits_{a=1}^{3}\\left[2\\mu_{a}\\mathbf{M}_{a}:\\mathbf{\\tilde{E}}^{2}+\\sum\\limits_{b=1}^{3}\\lambda_{ab}\\left(\\mathbf{M}_{a}:\\mathbf{\\tilde{E}}\\right)\\left(\\mathbf{M}_{b}:\\mathbf{\\tilde{E}}\\right)\\right].\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{\\tilde{E}}=\\left(\\mathbf{\\tilde{C}}-\\mathbf{I}\\right)/2$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{M}_{a}=\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n defines the initial direction of material axis \n\\begin_inset Formula $a$\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n on how to define the material axes for orthotropic materials.\n The Lamé constants \n\\begin_inset Formula $\\mu_{a}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lambda_{ab}$\n\\end_inset\n\n (\n\\begin_inset Formula $a,b=1,2,3$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\lambda_{ba}=\\lambda_{ab})$\n\\end_inset\n\n are related to Young's moduli \n\\begin_inset Formula $E_{a}$\n\\end_inset\n\n,\n shear moduli \n\\begin_inset Formula $G_{ab}$\n\\end_inset\n\n and Poisson's ratios \n\\begin_inset Formula $\\nu_{ab}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\left[\\begin{array}{cccccc}\n\\lambda_{11}+2\\mu_{1} & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0\\\\\n\\lambda_{12} & \\lambda_{22}+2\\mu_{2} & \\lambda_{23} & 0 & 0 & 0\\\\\n\\lambda_{13} & \\lambda_{23} & \\lambda_{33}+2\\mu_{3} & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{1}+\\mu_{2}\\right) & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{2}+\\mu_{3}\\right) & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{1}+\\mu_{3}\\right)\n\\end{array}\\right]^{-1}\\\\\n & =\\left[\\begin{array}{cccccc}\n\\frac{1}{E_{1}} & -\\frac{\\nu_{12}}{E_{1}} & -\\frac{\\nu_{13}}{E_{1}} & 0 & 0 & 0\\\\\n-\\frac{\\nu_{21}}{E_{2}} & \\frac{1}{E_{2}} & -\\frac{\\nu_{23}}{E_{2}} & 0 & 0 & 0\\\\\n-\\frac{\\nu_{31}}{E_{3}} & -\\frac{\\nu_{32}}{E_{3}} & \\frac{1}{E_{3}} & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\frac{1}{G_{12}} & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\frac{1}{G_{23}} & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\frac{1}{G_{31}}\n\\end{array}\\right]\n\\end{aligned}\n.\n\\]\n\n\\end_inset\n\nThe orthotropic Lamé parameters should produce a positive definite stiffness matrix.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"Fung orthotropic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E1>124</E1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E2>124</E2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E3>36</E3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G12>67</G12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G23>40</G23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G31>40</G31>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v12>-0.075</v12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v23>0.87</v23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v31>0.26</v31>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>1</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>120000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nGent Material\n\\end_layout\n\n\\begin_layout Standard\nThe uncoupled Gent material is defined using the \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\nGent\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n type string.\n It defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Jm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $J_{m}=I_{m}-1$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $I_{m}$\n\\end_inset\n\n max value for first invariant \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain energy of this material is defined via an uncoupled formulation,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\tilde{\\Psi}_{r}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right)\n\\]\n\n\\end_inset\n\nHere,\n the deviatoric strain energy is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}=-\\frac{\\mu J_{m}}{2}\\ln\\left(1-\\frac{\\tilde{I}_{1}-3}{J_{m}}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"Gent\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G>3.14</G>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Jm>1.5</Jm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1e5</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Holmes-Mow\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Holmes-Mow-Uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the uncoupled Holmes-Mow material is \n\\emph on\nuncoupled Holmes-Mow\n\\emph default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stiffening coefficient \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model uncouples deviatoric and volumetric behaviors,\n \n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\tilde{\\Psi}_{r}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right)\n\\]\n\n\\end_inset\n\nThe deviatoric strain-energy function is given by\n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}_{r}=\\frac{1}{2}\\frac{\\mu}{\\beta}\\left(e^{\\tilde{Q}}-1\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the shear modulus and\n\\begin_inset Formula \n\\[\n\\tilde{Q}=\\beta\\left(\\tilde{I}_{1}-3\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is the exponential stiffening coefficient.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material1\" type=\"uncoupled Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.5</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>2</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>5000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nHolzapfel-Gasser-Ogden\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Holzapfel-Gasser-Ogden\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the uncoupled Holzapfel-Gasser-Ogden material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gasser06\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\emph on\nHolzapfel-Gasser-Ogden\n\\emph default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus of ground matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber exponential coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gamma>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber mean orientation angle \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\ndeg\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kappa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber dispersion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model uncouples deviatoric and volumetric behaviors.\n The deviatoric strain-energy function is given by:\n \n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\tilde{\\Psi}_{r}\\left(\\tilde{\\mathbf{C}}\\right)+U\\left(J\\right)\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}_{r}=\\frac{c}{2}\\left(\\tilde{I}_{1}-3\\right)+\\frac{k_{1}}{2k_{2}}\\sum_{\\alpha=1}^{2}\\left(\\exp\\left(k_{2}\\left\\langle \\tilde{E}_{\\alpha}\\right\\rangle ^{2}\\right)-1\\right)\n\\]\n\n\\end_inset\n\nand the default volumetric strain energy function is\n\\begin_inset Formula \n\\[\nU\\left(J\\right)=\\frac{k}{2}\\left(\\frac{J^{2}-1}{2}-\\ln J\\right)\n\\]\n\n\\end_inset\n\nThe fiber strain is\n\\begin_inset Formula \n\\[\n\\tilde{E}_{\\alpha}=\\kappa\\left(\\tilde{I}_{1}-3\\right)+\\left(1-3\\kappa\\right)\\left(\\tilde{I}_{4\\alpha}-1\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{I}_{1}=\\tr\\tilde{\\mathbf{C}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{I}_{4\\alpha}=\\mathbf{a}_{\\alpha r}\\cdot\\tilde{\\mathbf{C}}\\cdot\\mathbf{a}_{\\alpha r}$\n\\end_inset\n\n.\n The Macaulay brackets around \n\\begin_inset Formula $\\left\\langle \\tilde{E}_{\\alpha}\\right\\rangle $\n\\end_inset\n\n indicate that this term is zero when \n\\begin_inset Formula $\\tilde{E}_{\\alpha}<0$\n\\end_inset\n\n and equal to \n\\begin_inset Formula $\\tilde{E}_{\\alpha}$\n\\end_inset\n\n when this strain is positive.\n\\end_layout\n\n\\begin_layout Standard\nThere are two fiber families along the vectors \n\\begin_inset Formula $\\mathbf{a}_{\\alpha r}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha=1,2$\n\\end_inset\n\n),\n lying in the \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2}\\right\\} $\n\\end_inset\n\n plane of the local material axes \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n,\n making an angle \n\\begin_inset Formula $\\pm\\gamma$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n.\n Each fiber family has a dispersion \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $0\\le\\kappa\\le\\frac{1}{3}$\n\\end_inset\n\n.\n When \n\\begin_inset Formula $\\kappa=0$\n\\end_inset\n\n there is no fiber dispersion,\n implying that all the fibers in that family act along the angle \n\\begin_inset Formula $\\pm\\gamma$\n\\end_inset\n\n;\n the value \n\\begin_inset Formula $\\kappa=\\frac{1}{3}$\n\\end_inset\n\n represents an isotropic fiber dispersion.\n All other intermediate values of \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n produce a \n\\begin_inset Formula $\\pi-$\n\\end_inset\n\nperiodic von Mises fiber distribution,\n as described in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gasser06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\begin_inset Formula $c$\n\\end_inset\n\n is the shear modulus of the ground matrix;\n \n\\begin_inset Formula $k_{1}$\n\\end_inset\n\n is the fiber modulus and \n\\begin_inset Formula $k_{2}$\n\\end_inset\n\n is the exponential coefficient.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"Holzapfel-Gasser-Ogden\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>7.64</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k1>996.6</k1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k2>524.6</k2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gamma>49.98</gamma>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kappa>0.226</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1e5</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nMooney-Rivlin\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mooney-Rivlin\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for uncoupled Mooney-Rivlin materials is \n\\shape italic\nMooney-Rivlin\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCoefficient of first invariant term\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCoefficient of second invariant term\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model is a hyperelastic Mooney-Rivlin type with uncoupled deviatoric and volumetric behavior.\n The strain-energy function is given by:\n \n\\begin_inset Formula \n\\[\n\\Psi=C_{1}\\left(\\tilde{I}_{1}-3\\right)+C_{2}\\left(\\tilde{I}_{2}-3\\right)+\\frac{1}{2}K\\left(\\ln J\\right)^{2}\\,.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $C_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $C_{2}$\n\\end_inset\n\n are the Mooney-Rivlin material coefficients.\n The variables \n\\begin_inset Formula $\\tilde{I}_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{I}_{2}$\n\\end_inset\n\n are the first and second invariants of the deviatoric right Cauchy-Green deformation tensor \n\\begin_inset Formula $\\mathbf{\\tilde{C}}$\n\\end_inset\n\n.\n The coefficient \n\\begin_inset Formula $K$\n\\end_inset\n\n is a bulk modulus-like penalty parameter and \n\\begin_inset Formula $J$\n\\end_inset\n\n is the determinant of the deformation gradient tensor.\n When \n\\begin_inset Formula $C_{2}=0$\n\\end_inset\n\n,\n this model reduces to an uncoupled version of the neo-Hookean constitutive model.\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a three-field element formulation,\n interpolating displacements as linear field variables and pressure and volume ratio as piecewise constant on each element \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThis material model is useful for modeling certain types of isotropic materials that exhibit some limited compressibility,\n i.e.\n 100 < (\n\\begin_inset Formula $K$\n\\end_inset\n\n/\n\\begin_inset Formula $C_{\\mathrm{1}})$\n\\end_inset\n\n < 10000.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>20.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nMuscle Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Muscle-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model implements the constitutive model developed by Silvia S.\n Blemker \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Blemker04\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The material type for the muscle material is \n\\shape italic\nmuscle material\n\\shape default\n.\n The model is designed to simulate the passive and active material behavior of skeletal muscle.\n It defines the following parameters:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nalong fiber shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncross fiber shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<p1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential stress coefficients\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\n \n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<p2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber uncrimping factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Lofl>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptimal fiber length\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<smax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum isometric stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<activation>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactivation level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe main difference between this material formulation compared to other transversely hyperelastic materials is that it is formulated using a set of new invariants,\n originally due to Criscione \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Criscione01\"\nliteral \"true\"\n\n\\end_inset\n\n,\n instead of the usual five invariants proposed by A.J.M.\n Spencer \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Spencer84\"\nliteral \"true\"\n\n\\end_inset\n\n.\n For this particular material,\n only two of the five Criscione invariants are used.\n The strain energy function is defined as follows:\n \n\\begin_inset Formula \n\\[\n\\Psi\\left(B_{1},B_{2},\\lambda\\right)=G_{1}\\tilde{B}_{1}^{2}+G_{2}\\tilde{B}_{2}^{2}+F_{m}\\left(\\tilde{\\lambda}\\right)+U\\left(J\\right).\n\\]\n\n\\end_inset\n\nThe function \n\\begin_inset Formula $F_{m}$\n\\end_inset\n\nis the strain energy contribution of the muscle fibers.\n It is defined as follows:\n \n\\begin_inset Formula \n\\[\n\\lambda\\frac{\\partial F_{m}}{\\partial\\lambda}=\\sigma_{\\max}\\left(f_{m}^{\\text{passive}}\\left(\\lambda\\right)+\\alpha f_{m}^{\\text{active}}\\left(\\lambda\\right)\\right)\\frac{\\lambda}{\\lambda_{ofl}},\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\[\nf_{m}^{\\text{passive}}\\left(\\lambda\\right)=\\begin{cases}\n0 & \\lambda\\leqslant\\lambda_{ofl}\\\\\nP_{1}\\left(e^{P_{2}\\left(\\lambda/\\lambda_{ofl}-1\\right)}-1\\right) & \\lambda_{ofl}<\\lambda<\\lambda^{\\ast}\\\\\nP_{3}\\lambda/\\lambda_{ofl}+P_{4} & \\lambda\\geqslant\\lambda^{\\ast}\n\\end{cases}\\,,\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\[\nf_{m}^{\\text{active}}\\left(\\lambda\\right)=\\begin{cases}\n9\\left(\\lambda/\\lambda_{ofl}-0.4\\right)^{2} & \\lambda\\leqslant0.6\\lambda_{ofl}\\\\\n9\\left(\\lambda/\\lambda_{ofl}-1.6\\right)^{2} & \\lambda\\geqslant1.4\\lambda_{ofl}\\\\\n1-4\\left(1-\\lambda/\\lambda_{ofl}\\right)^{2} & 0.6\\lambda_{ofl}<\\lambda<1.4\\lambda_{ofl}\n\\end{cases}\\,.\n\\]\n\n\\end_inset\n\nThe values \n\\begin_inset Formula $P_{3}$\n\\end_inset\n\nand \n\\begin_inset Formula $P_{4}$\n\\end_inset\n\n are determined by requiring \n\\begin_inset Formula $C^{0}$\n\\end_inset\n\nand \n\\begin_inset Formula $C^{1}$\n\\end_inset\n\ncontinuity at \n\\begin_inset Formula $\\lambda=\\lambda^{\\ast}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe parameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n is the activation level and can be specified using the \n\\emph on\nactivation\n\\shape italic\n\\emph default\n \n\\shape default\nelement.\n You can specify a loadcurve using the \n\\shape italic\nlc \n\\shape default\nattribute.\n The value is interpreted as a scale factor when a loadcurve is defined or as the constant activation level when no loadcurve is defined.\n\\end_layout\n\n\\begin_layout Standard\nThe muscle fiber direction is specified similarly to the transversely isotropic Mooney-Rivlin model.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"muscle material\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <g1>500</g1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <g2>500</g2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <p1>0.05</p1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <p2>6.6</p2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <smax>3e5</smax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Lofl>1.07</Lofl>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lambda>1.4</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1e6</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nOgden\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ogden\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material describes an incompressible hyperelastic Ogden material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c[n]>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCoefficient of n\n\\begin_inset Formula $^{\\mathrm{th}}$\n\\end_inset\n\n term,\n where n can range from 1 to 6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<m[n]>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponent of n\n\\begin_inset Formula $^{\\mathrm{th}}$\n\\end_inset\n\n term,\n where n can range from 1 to 6 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe uncoupled hyperelastic strain energy function for this material is given in terms of the eigenvalues of the deformation tensor:\n \n\\begin_inset Formula \n\\[\n\\Psi=\\sum\\limits_{i=1}^{N}\\frac{c_{i}}{m_{i}^{2}}\\left(\\tilde{\\lambda}_{1}^{m_{i}}+\\tilde{\\lambda}_{2}^{m_{i}}+\\tilde{\\lambda}_{3}^{m_{i}}-3\\right)+U\\left(J\\right).\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\tilde{\\lambda}_{i}^{2}$\n\\end_inset\n\n are the eigenvalues of \n\\begin_inset Formula $\\mathbf{\\tilde{C}}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $c_{i}$\n\\end_inset\n\nand \n\\begin_inset Formula $m_{i}$\n\\end_inset\n\nare material coefficients and \n\\begin_inset Formula $N$\n\\end_inset\n\n ranges from 1 to 6.\n Note that you only have to include the material parameters for the terms you intend to use.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"Ogden\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <m1>2.4</m1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTendon Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tendon-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the tendon material is \n\\shape italic\ntendon material\n\\shape default\n.\n The tendon material is similar to the muscle material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Blemker04\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The only difference is the fiber function.\n For tendon material this is defined as:\n \n\\begin_inset Formula \n\\[\n\\lambda\\frac{\\partial F_{t}}{\\partial\\lambda}=\\sigma\\left(\\lambda\\right),\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\sigma\\left(\\lambda\\right)=\\begin{cases}\n0 & \\lambda\\leqslant1\\\\\nL_{1}\\left(e^{L_{2}\\left(\\lambda-1\\right)}-1\\right) & 1<\\lambda<\\lambda^{\\ast}\\\\\nL_{3}\\lambda+L_{4} & \\lambda\\geqslant\\lambda^{\\ast}\n\\end{cases}\\,.\n\\]\n\n\\end_inset\n\nThe parameters \n\\begin_inset Formula $L_{3}$\n\\end_inset\n\nand \n\\begin_inset Formula $L_{4}$\n\\end_inset\n\nare determined by requiring \n\\begin_inset Formula $C^{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $C^{1}$\n\\end_inset\n\n continuity at \n\\begin_inset Formula $\\lambda^{\\ast}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe material parameters for this material are listed below.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nalong fiber shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncross fiber shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential stress coefficients\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber uncrimping factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe tendon fiber direction is specified similarly to the transversely isotropic Mooney-Rivlin model.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"tendon material\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <g1>5e4</g1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <g2>5e4</g2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <l1>2.7e6/l1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <l2>46.4</l2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lambda>1.03</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1e7</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTension-Compression Nonlinear Orthotropic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Tension-Compression-Nonlinear-Ortho\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the tension-compression nonlinear orthotropic material is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nTC nonlinear orthotropic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters are defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFirst Mooney-Rivlin material parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond Mooney-Rivlin material parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n parameter (see below)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n parameter (see below)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mat_axis>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefines the material axes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material is based on the following uncoupled hyperelastic strain energy function \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian07c\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\Psi\\left(\\mathbf{C},\\lambda_{1},\\lambda_{2},\\lambda_{3}\\right)=\\tilde{\\Psi}_{iso}\\left(\\mathbf{\\tilde{C}}\\right)+\\sum\\limits_{i=1}^{3}\\tilde{\\Psi}_{i}^{TC}\\left(\\tilde{\\lambda}_{i}\\right)+U\\left(J\\right)\\,.\n\\]\n\n\\end_inset\n\nThe isotropic strain energy \n\\begin_inset Formula $\\tilde{\\Psi}_{iso}$\n\\end_inset\n\nand the dilatational energy \n\\begin_inset Formula $U$\n\\end_inset\n\n are the same as for the Mooney-Rivlin material.\n The tension-compression term is defined as follows:\n \n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}_{i}^{TC}\\left(\\tilde{\\lambda}_{i}\\right)=\\begin{cases}\n\\xi_{i}\\left(\\tilde{\\lambda}_{i}-1\\right)^{\\beta_{i}} & \\tilde{\\lambda}_{i}>1\\\\\n0 & \\tilde{\\lambda}_{i}\\leqslant1\n\\end{cases}\\quad\\xi_{i}\\geqslant0\\quad\\left(\\text{no sum over }i\\right)\\,.\n\\]\n\n\\end_inset\n\nThe \n\\begin_inset Formula $\\tilde{\\lambda}_{i}$\n\\end_inset\n\n parameters are the deviatoric fiber stretches of the local material fibers:\n \n\\begin_inset Formula \n\\[\n\\tilde{\\lambda}_{i}=\\left(\\mathbf{a}_{i}^{0}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{a}_{i}^{0}\\right)^{1/2}\\,.\n\\]\n\n\\end_inset\n\nThe local material fibers are defined (in the reference frame) as an orthonormal set of vectors \n\\begin_inset Formula $\\mathbf{a}_{i}^{0}$\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n for more information.\n As with all uncoupled materials,\n this material uses the three-field element formulation.\n\\end_layout\n\n\\begin_layout Standard\nA complete example for this material follows.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"7\" name=\"cartilage\" type=\"TC nonlinear orthotropic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>4.3,4.3,4.3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ksi>4525,\n 4525,\n 4525</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTransversely Isotropic Mooney-Rivlin\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Transversely-Isotropic-Mooney-Rivlin\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for transversely isotropic Mooney-Rivlin materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ntrans iso Mooney-Rivlin\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin coefficient 1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin coefficient 2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stress coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c4>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber uncrimping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c5>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nModulus of straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam_max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fiber>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber distribution option\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<active_contraction>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOptional active contraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis constitutive model can be used to represent a material that has a single preferred fiber direction and was developed for application to biological soft tissues \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso98,Quapp98,Weiss96\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It can be used to model tissues such as tendons,\n ligaments and muscle.\n The elastic response of the tissue is assumed to arise from the resistance of the fiber family and an isotropic matrix.\n It is assumed that the uncoupled strain energy function can be written as follows:\n \n\\begin_inset Formula \n\\[\n\\Psi=F_{1}\\left(\\tilde{I}_{1},\\tilde{I}_{2}\\right)+F_{2}\\left(\\tilde{\\lambda}\\right)+\\frac{K}{2}\\left[\\ln\\left(J\\right)\\right]^{2}.\n\\]\n\n\\end_inset\n\nHere \n\\begin_inset Formula $\\tilde{I}_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{I}_{2}$\n\\end_inset\n\n are the first and second invariants of the deviatoric version of the right Cauchy Green deformation tensor \n\\begin_inset Formula $\\mathbf{\\tilde{C}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{\\lambda}$\n\\end_inset\n\n is the deviatoric part of the stretch along the fiber direction (\n\\begin_inset Formula $\\tilde{\\lambda}^{2}=\\mathbf{a}_{0}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{a}_{0}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{a}_{0}$\n\\end_inset\n\n is the initial fiber direction),\n and \n\\begin_inset Formula $J=\\det\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n is the Jacobian of the deformation (volume ratio).\n The function \n\\begin_inset Formula $F_{1}$\n\\end_inset\n\n represents the material response of the isotropic ground substance matrix and is the same as the Mooney-Rivlin form specified above,\n while \n\\begin_inset Formula $F_{2}$\n\\end_inset\n\n represents the contribution from the fiber family.\n The strain energy of the fiber family is as follows:\n \n\\begin_inset Formula \n\\[\nF_{2}\\left(\\tilde{\\lambda}\\right)=\\begin{cases}\n0 & \\tilde{\\lambda}\\leqslant1\\\\\nC_{3}\\left(e^{-C_{4}}\\left(\\Ei\\left(C_{4}\\tilde{\\lambda}\\right)-\\Ei\\left(C_{4}\\right)\\right)-\\ln\\tilde{\\lambda}\\right) & 1<\\tilde{\\lambda}<\\lambda_{m}\\\\\nC_{5}\\left(\\tilde{\\lambda}-1\\right)+C_{6}\\ln\\tilde{\\lambda} & \\tilde{\\lambda}\\geqslant\\lambda_{m}\n\\end{cases}\\,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Ei\\left(\\cdot\\right)$\n\\end_inset\n\n is the exponential integral function.\n The resulting fiber stress is evaluated from \n\\begin_inset Formula \n\\[\n\\tilde{\\lambda}\\frac{\\partial F_{2}}{\\partial\\tilde{\\lambda}}=\\begin{cases}\n0 & \\tilde{\\lambda}\\leqslant1\\\\\nC_{3}\\left(e^{C_{4}\\left(\\tilde{\\lambda}-1\\right)}-1\\right) & 1<\\tilde{\\lambda}<\\lambda_{m}\\\\\nC_{5}\\tilde{\\lambda}+C_{6} & \\tilde{\\lambda}\\geqslant\\lambda_{m}\n\\end{cases}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $C_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $C_{2}$\n\\end_inset\n\n are the Mooney-Rivlin material coefficients,\n\n\\shape italic\n lam_max\n\\shape default\n (\n\\begin_inset Formula $\\lambda_{m})$\n\\end_inset\n\n is the stretch at which the fibers are straightened,\n \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\nscales the exponential stresses,\n \n\\begin_inset Formula $C_{4}$\n\\end_inset\n\n is the rate of uncrimping of the fibers,\n and \n\\begin_inset Formula $C_{5}$\n\\end_inset\n\nis the modulus of the straightened fibers.\n \n\\begin_inset Formula $C_{6}$\n\\end_inset\n\n is determined from the requirement that the stress is continuous at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a three-field element formulation,\n interpolating displacements as linear field variables and pressure and volume ratio as piecewise constant on each element \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe fiber orientation can be specified as explained in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n An optional active contraction model can also be defined for this material.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fiber-Active-Contraction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a transversely isotropic material with a Mooney-Rivlin basis.\n It defines a homogeneous fiber direction and uses the active fiber contraction feature.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"trans iso Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>13.85</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>2.07</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>61.44</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>640.7</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>1.03</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTransversely Isotropic Veronda-Westmann\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Transversely-Isotropic-Veronda-Westmann\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for transversely isotropic Veronda-Westmann materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ntrans iso Veronda-Westmann\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss2002\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVeronda-Westmann coefficient 1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVeronda-Westmann coefficient 2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stress coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c4>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber uncrimping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c5>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nModulus of straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam_max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fiber>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber distribution option.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<active_contraction>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOptional active contraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis uncoupled hyperelastic material differs from the Transversely Isotropic Mooney-Rivlin model in that it uses the Veronda-Westmann model for the isotropic matrix.\n The interpretation of the material parameters,\n except \n\\begin_inset Formula $C_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $C_{2}$\n\\end_inset\n\nis identical to this material model.\n\\end_layout\n\n\\begin_layout Standard\nThe fiber distribution option is explained in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n An optional active contraction model can also be defined for this material.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fiber-Active-Contraction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for more details.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a transversely isotropic material model with a Veronda-Westmann basis.\n The fiber direction is implicitly implied as \n\\shape italic\nlocal\n\\shape default\n.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"trans iso Veronda-Westmann\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>13.85</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>2.07</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>61.44</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>640.7</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>1.03</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Solid Mixture\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Solid-Mixture\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for for a constrained mixture of uncoupled solids is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n This material describes a mixture of quasi-incompressible elastic solids.\n It is a container for any combination of the materials described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContainer tag for solid material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe mixture may consist of any number of solids.\n The stress tensor for the solid mixture is the sum of the stresses for all the solids.\n The bulk modulus of the uncoupled solid mixture is the sum of the bulk moduli of the individual <solid> materials.\n A bulk modulus specified outside of the <solid> materials will be ignored.\n\\end_layout\n\n\\begin_layout Standard\nMaterial axes may be optionally specified within the <material> level,\n as well as within each <solid>.\n Within the <material> level,\n these represent the local element axes relative to the global coordinate system.\n Within the <solid>,\n they represent local material axes relative to the element.\n If material axes are specified at both levels,\n they are properly compounded to produce local material axes relative to the global coordinate system.\n Material axes specified in the <ElementData> section are equivalent to a specification at the <material> level:\n they correspond to local element axes relative to the global system.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>30e3</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <a>1,0,0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <d>0,1,0</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>2.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"EFD uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <a>0.8660254,0.5,0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <d>0,0,1</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5,\n 1,\n 1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5,\n 3,\n 3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"EFD uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <a>0.8660254,-0.5,0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <d>0,0,1</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5,\n 1,\n 1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5,\n 3,\n 3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nVeronda-Westmann\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Veronda-Westmann\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for incompressible Veronda-Westmann materials is \n\\shape italic\nVeronda-Westmann\n\\shape default\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Veronda70\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFirst VW coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond VW coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model is similar to the Mooney-Rivlin model in that it also uses an uncoupled deviatoric dilatational strain energy:\n \n\\begin_inset Formula \n\\[\n\\Psi=C_{1}\\left[e^{\\left(C_{2}\\left(\\tilde{I}_{1}-3\\right)\\right)}-1\\right]-\\frac{C_{1}C_{2}}{2}\\left(\\tilde{I}_{2}-3\\right)+U\\left(J\\right)\\,.\n\\]\n\n\\end_inset\n\nThe dilatational term is identical to the one used in the Mooney-Rivlin model.\n This model can be used to describe certain types of biological materials that display exponential stiffening with increasing strain.\n It has been used to describe the response of skin tissue \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Veronda70\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"Veronda-Westmann\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1000.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>2000.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nMooney-Rivlin Von Mises Distributed Fibers\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mooney-Rivlin-Von-Mises\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\n(Sclera and other thin soft tissues)\n\\end_layout\n\n\\begin_layout Standard\nAuthors:\n Cécile L.M.\n Gouget and Michaël J.A.\n Girard\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a thin material where fiber orientation follows a von Mises distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\nMooney-Rivlin von Mises Fibers\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"14\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin coefficient 1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin coefficient 2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stress coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c4>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber uncrimping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c5>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nModulus of straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam_max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tp>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPreferred fiber orientation in radian (angle within the plane of the fibers,\n defined with respect to the first direction given in mat_axis)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[rad] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kf>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber concentration factor \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<vmc>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nChoice of von Mises distribution\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n=1 semi-circular von Mises distribution\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n=2 constrained von Mises mixture distribution\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<var_n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponent (only for vmc=2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gipt>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of integration points (value is a multiple of 10)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mat_axis>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReference frame of the plane of the fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis constitutive model is designed for thin soft tissues.\n Fibers are multi-directional:\n they are distributed within the plane tangent to the tissue surface and they follow a unimodal distribution.\n It is a constitutive model that is suitable for ocular tissues (e.g.\n sclera and cornea) but can be used for other thin soft tissues.\n The proposed strain energy function is as follows:\n \n\\begin_inset Formula \n\\[\n\\Psi=F_{1}\\left(\\tilde{I}_{1},\\tilde{I}_{2}\\right)+\\int\\limits_{\\theta_{P}-\\pi/2}^{\\theta_{P}+\\pi/2}P\\left(\\theta\\right)F_{2}\\left(\\tilde{\\lambda}\\left[\\theta\\right]\\right)d\\theta+\\frac{K}{2}\\left[\\ln\\left(J\\right)\\right]^{2},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $P$\n\\end_inset\n\n is the 2D unimodal distribution function of the fibers which satisfies the normalization condition:\n \n\\begin_inset Formula \n\\[\n\\int\\limits_{\\theta_{P}-\\pi/2}^{\\theta_{P}+\\pi/2}P\\left(\\theta\\right)d\\theta=1.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $\\theta_{P}$\n\\end_inset\n\n is the preferred fiber orientation relative to a local coordinate system (parameter tp).\n The functions \n\\begin_inset Formula $F_{1}$\n\\end_inset\n\nand \n\\begin_inset Formula $F_{2}$\n\\end_inset\n\n are described in the \n\\begin_inset Quotes eld\n\\end_inset\n\nTransversely Isotropic Mooney-Rivlin\n\\begin_inset Quotes erd\n\\end_inset\n\n model.\n The user can choose between 2 distribution functions using the parameter vmc.\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\n1.\n Semi-circular von Mises distribution (vmc \n\\series default\n=\n\\series bold\n 1) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Girard09\"\nliteral \"true\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe semi-circular von Mises distribution is one of the simplest unimodal distribution in circular statistics.\n It can be expressed as \n\\begin_inset Formula \n\\[\nP\\left(\\theta\\right)=\\frac{1}{\\pi I_{0}\\left(k_{f}\\right)}\\exp\\left[k_{f}\\cos\\left(2\\left(\\theta-\\theta_{p}\\right)\\right)\\right],\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{0}$\n\\end_inset\n\n is the modified Bessel function of the first kind (order 0),\n and \n\\begin_inset Formula $k_{f}$\n\\end_inset\n\n is the fiber concentration factor.\n \n\\begin_inset Formula $k_{f}$\n\\end_inset\n\n controls the amount of fibers that are concentrated along the orientation \n\\begin_inset Formula $\\theta_{P}$\n\\end_inset\n\n as illustrated in the figure below.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Box Frameless\nposition \"t\"\nhor_pos \"c\"\nhas_inner_box 1\ninner_pos \"t\"\nuse_parbox 0\nuse_makebox 0\nwidth \"100col%\"\nspecial \"none\"\nheight \"1in\"\nheight_special \"totalheight\"\nthickness \"0.4pt\"\nseparation \"3pt\"\nshadowsize \"4pt\"\nframecolor \"black\"\nbackgroundcolor \"none\"\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigMooneyRivlinVonMises.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\nPolar representation of the semi-circular\n\\series bold\n \n\\series default\nvon-Mises distribution describing in-plane collagen fiber alignment.\n In this case,\n the preferred fiber orientation \n\\begin_inset Formula $\\theta_{P}$\n\\end_inset\n\n is equal to zero degrees.\n When the fiber concentration factor \n\\begin_inset Formula $k$\n\\end_inset\n\n is equal to zero,\n the collagen fibers have an isotropic distribution in a plane tangent to the scleral wall.\n As \n\\begin_inset Formula $k$\n\\end_inset\n\n increases,\n the collagen fibers align along the preferred fiber orientation \n\\begin_inset Formula $\\theta_{P}$\n\\end_inset\n\n.\n Note that the distributions were plotted on a circle of unit one to ease visualization.\n\\end_layout\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\n2.\n Constrained von Mises Mixture Distribution (vmc \n\\series default\n=\n\\series bold\n 2) \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gouget12\"\nliteral \"true\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe semi-circular von Mises distribution is ideal for its simplicity but in some instance it fails to accurately describe the isotropic subpopulation of fibers present in thin soft tissues.\n An improved mathematical description is proposed here as a weighted mixture of the semi-circular uniform distribution and the semi-circular von Mises distribution.\n It can be expressed as:\n \n\\begin_inset Formula \n\\[\nP\\left(\\theta\\right)=\\frac{1-\\beta}{\\pi}+\\frac{\\beta}{\\pi I_{0}\\left(k_{f}\\right)}\\exp\\left[k_{f}\\cos\\left(2\\left(\\theta-\\theta_{p}\\right)\\right)\\right],\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n needs to be constrained for uniqueness and stability.\n \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is expressed as:\n \n\\begin_inset Formula \n\\[\n\\beta=\\left(\\frac{I_{1}\\left(k_{f}\\right)}{I_{0}\\left(k_{f}\\right)}\\right)^{n},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n is the modified Bessel function of the first kind (order 1),\n and n is an exponent to be determined experimentally (parameter var_n).\n n=2 has been found to be suitable for the sclera based on fiber distribution measurements using small angle light scattering.\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nNote about Numerical Integration\n\\end_layout\n\n\\begin_layout Standard\nAll numerical integrations are performed using a 10-point Gaussian quadrature rule.\n The number of Gaussian integration points can be increased for numerical stability.\n This is controlled through the parameter \n\\emph on\ngipt\n\\emph default\n.\n We recommend to use at least \n\\emph on\ngipt\n\\emph default\n = 20 (2\n\\begin_inset Formula $\\times$\n\\end_inset\n\n10 integration points).\n Note that the more integration points are used,\n the slower the model will run.\n By increasing the number of integration points,\n one should observe convergence in the numerical accuracy.\n The parameter gipt is required to be a multiple of 10.\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nDefinition of the Fiber Plane\n\\end_layout\n\n\\begin_layout Standard\nThe user must specify two directions to define a local coordinate system of the plane in which the fibers lay.\n As explained in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n,\n this can be done by defining two directions with the parameter \n\\emph on\nmat_axis\n\\emph default\n.\n The first direction (vector \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n\n\\series default\n of \n\\emph on\nmat_axis\n\\emph default\n) must be the reference direction used to define the angle \n\\emph on\ntp\n\\emph default\n (\n\\begin_inset Formula $\\theta_{P}$\n\\end_inset\n\n).\n The second direction (vector \n\\series bold\n\n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n\n\\series default\n of mat_axis) can be any other direction in the plane of the fibers.\n Thus,\n a fiber at angle \n\\begin_inset Formula $\\theta_{P}$\n\\end_inset\n\n will be along the vector \n\\begin_inset Formula $\\mathbf{v}=\\cos\\theta_{P}\\mathbf{e}_{1}+\\sin\\theta_{P}\\mathbf{e}_{2}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{e}_{1}=\\mathbf{a}/\\left\\Vert \\mathbf{a}\\right\\Vert $\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{e}_{2}=\\left(\\mathbf{e}_{3}\\times\\mathbf{a}\\right)/\\left\\Vert \\mathbf{e}_{3}\\times\\mathbf{a}\\right\\Vert $\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{e}_{3}=\\left(\\mathbf{a}\\times\\mathbf{d}\\right)/\\left\\Vert \\mathbf{a}\\times\\mathbf{d}\\right\\Vert $\n\\end_inset\n\n,\n as was defined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nNote on parameters kf and tp\n\\end_layout\n\n\\begin_layout Standard\nThe parameters kf and tp can be specified either in the Material section or in the \n\\shape italic\nElementData\n\\shape default\n section with the tag MRVonMisesParameters.\n With this second option the user can define different fiber characteristics for each element separately,\n which can be useful if fiber orientations or concentrations vary spatially.\n The parameter mat_axis can also be defined either in the Material section or in the \n\\shape italic\nElementData\n\\shape default\n section.\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nExample\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a thin soft tissue material where fibers are distributed according to a constrained von Mises mixture distribution.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material 1\" type=\"Mooney-Rivlin von Mises Fibers\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>50.0</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>5.0</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>1</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>10</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000000.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kf>1</kf>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vmc>2</vmc>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var_n>2.0</var_n>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tp>0.0</tp>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gipt>40</gipt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">4,1,3</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nIsotropic Lee-Sacks uncoupled\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Kamensky-uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material implements a Fung-type material,\n as presented in Kamensy,\n CMAME 2018.\n The material formulation is selected by setting the type attribute to \"\n\\family typewriter\nuncoupled\n\\family default\n \n\\family typewriter\nisotropic Lee-Sacks\n\\family default\n\".\n It defines the following parameters:\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear-modulus of neo-Hookean matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc1 parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc2 parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tangent_scale>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale factor for c0 when used in tangent (default = 1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy density function for this material combines a neo-Hookean matrix with a Fung-type exponential term,\n and is defined as follows.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi=\\frac{c_{0}}{2}\\left(I_{1}-3\\right)+\\frac{c_{1}}{2}\\left(e^{c_{2}\\left(I_{1}-3\\right)^{2}}-1\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs reported in Kamensky,\n the exponential term may cause convergence difficulties under certain loading conditions.\n It was reported that increasing the value of \n\\begin_inset Formula $c_{0}$\n\\end_inset\n\nduring the stiffness evaluation may improve convergence.\n The default value for \n\\emph on\ntangent_scale \n\\emph default\nis 1,\n and thus tangent scaling is not applied.\n A tangent scale factor of 20 was used in Kamensky.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"material1\" type=\"\n\\family typewriter\nuncoupled\n\\family default\n \n\\family typewriter\nisotropic Lee-Sacks\n\\family default\n\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c0>10</c0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>0.209</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>9.046</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nYeoh Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Yeoh-Material\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material implements a Yeoh material,\n as presented online (https://en.wikipedia.org/wiki/Yeoh_hyperelastic_model).\n The material formulation is selected by setting the type attribute to \"Yeoh\".\n It defines the following parameters:\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>...<c6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElastic moduli of Yeoh material (up to 6)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy density function for this material combines a neo-Hookean matrix with a Fung-type exponential term,\n and is defined as follows.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi=\\sum_{i=1}^{6}c_{i}\\left(\\tilde{I}_{1}-3\\right)^{i}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"material1\" type=\"Yeoh\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>0.75</c0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>-0.04</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.08</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFiber Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSome of the material models described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n which include a fiber model may provide the option to specify an active contraction along that fiber.\n This section lists the types of fiber active contraction models.\n\\end_layout\n\n\\begin_layout Subsubsection\nActive Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe default active contraction model is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nactive_contraction\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n It is based on the formulation of Guccione et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Guccione93\"\nliteral \"false\"\n\n\\end_inset\n\n and reviewed in the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n The active stress is evaluated as \n\\begin_inset Formula $T^{a}\\mathbf{a}\\otimes\\mathbf{a}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is the unit vector along the fiber in the current configuration,\n and\n\\begin_inset Formula \n\\[\nT^{a}=T_{\\max}\\frac{Ca_{0}^{2}}{Ca_{0}^{2}+ECa_{50}^{2}}C\\left(t\\right)\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\nECa_{50}=\\frac{\\left(\\text{Ca}_{0}\\right)_{\\max}}{\\sqrt{\\exp\\left[\\beta\\left(\\lambda\\left(t\\right)l_{r}-l_{0}\\right)\\right]-1}}\n\\]\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\lambda\\left(t\\right)$\n\\end_inset\n\n is the fiber stretch ratio at the current time.\n The activation curve \n\\begin_inset Formula $C\\left(t\\right)$\n\\end_inset\n\n is represented by the \n\\emph on\nascl\n\\emph default\n property that takes an optional attribute,\n \n\\shape italic\nlc\n\\shape default\n,\n which defines the loadcurve.\n The list of parameters required for this model is as follows.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ascl>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nActivation scale factor \n\\begin_inset Formula $C\\left(t\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ca0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIntracellular calcium concentration \n\\begin_inset Formula $Ca_{0}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<camax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum peak intracellular calcium concentration \n\\begin_inset Formula $\\left(\\text{Ca}_{0}\\right)_{\\max}$\n\\end_inset\n\n (default=ca0)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntension-sarcomere length relation constant \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNo tension sarcomere length \n\\begin_inset Formula $l_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<refl>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUnloaded sarcomere length \n\\begin_inset Formula $l_{r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Tmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIsometric tension under maximal activation at camax \n\\begin_inset Formula $T_{\\max}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a transversely isotropic material with a Mooney-Rivlin basis.\n It defines a homogeneous fiber direction and uses the active fiber contraction feature.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"trans iso Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>13.85</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>2.07</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>61.44</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>640.7</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>1.03</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <active_contraction type=\"active_contraction\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ascl lc=\"1\">1</ascl>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ca0>4.35</ca0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>4.75</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <l0>1.58</l0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <refl>2.04</refl>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </active_contraction>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nForce-Velocity Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Force-Velocity-Active-Contractio\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model was formulated by Estrada et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Estrada20\"\nliteral \"false\"\n\n\\end_inset\n\n and is called \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nforce-velocity-Estrada\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n It is a modification of the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nactive_contraction\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material based on the formulation of Guccione et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Guccione93\"\nliteral \"false\"\n\n\\end_inset\n\n described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Active-Contraction\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n based on the fading-memory formulation poroposed by Hunter et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hunter98\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The active stress is evaluated as \n\\begin_inset Formula $T^{a}\\mathbf{a}\\otimes\\mathbf{a}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n is the unit vector along the fiber in the current configuration,\n and\n\\begin_inset Formula \n\\[\nT^{a}=e\\left(t\\right)\\underbrace{T_{\\max}\\frac{Ca_{0}^{2}}{Ca_{0}^{2}+ECa_{50}^{2}}}_{\\begin{array}{c}\n\\text{instantaneous length}\\\\\n\\text{dependence}\n\\end{array}}\\underbrace{\\frac{1+aQ\\left(t,\\lambda\\left(t\\right)\\right)}{1-Q\\left(t,\\lambda\\left(t\\right)\\right)}}_{\\text{force-velocity}}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\nECa_{50}=\\frac{\\left(\\text{Ca}_{0}\\right)_{\\max}}{\\sqrt{\\exp\\left[\\beta\\left(\\lambda\\left(t\\right)l_{r}-l_{0}\\right)\\right]-1}}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\nQ\\left(t,\\lambda\\left(t\\right)\\right)=\\sum_{k=1}^{3}A_{k}\\int_{-\\infty}^{t}e^{-\\alpha_{k}\\left(t-\\tau\\right)}\\dot{\\lambda}\\left(\\tau\\right)\\,d\\tau\n\\]\n\n\\end_inset\n\nIn these expressions,\n \n\\begin_inset Formula $\\lambda\\left(t\\right)$\n\\end_inset\n\n is the fiber stretch ratio at the current time.\n Here,\n \n\\begin_inset Formula $T^{a}$\n\\end_inset\n\n \n\\begin_inset Quotes eld\n\\end_inset\n\nis the product of three distinct components:\n a time-varying normalized activation,\n \n\\begin_inset Formula $e\\left(t\\right)$\n\\end_inset\n\n,\n that defines the time course of force generation throughout the cardiac cycle;\n an instantaneous length-dependent term that scales the peak possible isometric tension based on the current fiber stretch;\n and a force-velocity term that dampens the instantaneous force generation based on the rate of shortening of the fibers\n\\begin_inset Quotes erd\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Estrada20\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The activation curve \n\\begin_inset Formula $e\\left(t\\right)$\n\\end_inset\n\n is represented by the \n\\emph on\nascl\n\\emph default\n property that takes an optional attribute,\n \n\\shape italic\nlc\n\\shape default\n,\n which defines the time-dependent loadcurve.\n The list of parameters required for this model is as follows.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ascl>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nActivation scale factor \n\\begin_inset Formula $e\\left(t\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ca0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIntracellular calcium concentration \n\\begin_inset Formula $Ca_{0}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<camax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum peak intracellular calcium concentration \n\\begin_inset Formula $\\left(\\text{Ca}_{0}\\right)_{\\max}$\n\\end_inset\n\n (default=ca0)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntension-sarcomere length relation constant \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNo tension sarcomere length \n\\begin_inset Formula $l_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<refl>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUnloaded sarcomere length \n\\begin_inset Formula $l_{r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Tmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIsometric tension under maximal activation at camax \n\\begin_inset Formula $T_{\\max}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<A1>,\n <A2>,\n <A3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameters \n\\begin_inset Formula $A_{k}$\n\\end_inset\n\n for \n\\begin_inset Formula $k=1,2,3$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha1>,\n <alpha2>,\n <alpha3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameters \n\\begin_inset Formula $\\alpha_{k}$\n\\end_inset\n\n for \n\\begin_inset Formula $k=1,2,3$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<a_t>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $a$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<force_velocity>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet to true (1) by default.\n Set to false (0) to turn off the force-velocity term\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a transversely isotropic material with a Mooney-Rivlin basis.\n It defines a homogeneous fiber direction and uses the active fiber contraction feature.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"trans iso Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>13.85</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>2.07</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>61.44</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>640.7</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>1.03</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <active_contraction type=\"force-velocity-Estrada\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ascl lc=\"1\">1</ascl>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ca0>4.35</ca0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>4.75</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <l0>1.58</l0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <refl>1.58</refl>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<Tmax>135.7</Tmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<alpha1>30.30303</alpha1>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<A1>50</A1>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<a_t>0.5</a_t>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<force_velocity>1</force_velocity>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </active_contraction>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUnconstrained Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Unconstrained-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUnlike the materials in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n,\n these materials do not necessarily assume an additive decomposition of the bulk and deviatoric parts of the strain energy or stress.\n Further,\n these materials can only be used with the standard displacement-based finite element formulation,\n rather than the three-field element formulatoin.\n They should not be used for nearly-incompressible material behavior due to the potential for element locking.\n\\end_layout\n\n\\begin_layout Subsubsection\nArruda-Boyce Unconstrained\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Arruda-Boyce-Unconstrained\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for an unconstrained Arruda-Boyce material is \n\\emph on\nArruda-Boyce unconstrained\n\\emph default\n.\n This isotropic Arruda-Boyce \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Arruda93\"\nliteral \"false\"\n\n\\end_inset\n\n 8-chain model has been used in the field of polymer modeling and in cervical biomechanics \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shi22\"\nliteral \"false\"\n\n\\end_inset\n\n.\n It is based on the simple freely-jointed-chain model and can be derived using the Langevin equation of statistical mechanics.\n The strain energy density for this material takes the form\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\Psi(I_{1},J)=\\xi\\left(\\sqrt{\\frac{I_{1}}{3N}}\\beta+\\ln\\frac{\\beta}{\\sinh\\beta}\\right)-\\frac{\\xi}{3}\\sqrt{N}\\beta_{0}\\ln J+\\frac{1}{2}\\kappa(\\ln J^{2}-1-2\\ln J)\\label{eq2-1}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\noindent\nwhere \n\\begin_inset Formula $I_{1}=\\tr\\mathbf{C}$\n\\end_inset\n\n is the first invariant of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n,\n \n\\begin_inset Formula ${\\displaystyle \\beta=\\mathcal{L}^{-1}\\left[\\sqrt{\\frac{I_{1}}{3N}}\\right]}$\n\\end_inset\n\n is the inverse Langevin equation,\n and \n\\begin_inset Formula ${\\displaystyle \\beta_{0}=\\mathcal{L}^{-1}\\left[\\sqrt{\\frac{1}{N}}\\right]}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\xi=nk\\Theta$\n\\end_inset\n\n is the initial fiber modulus with \n\\begin_inset Formula $n,k,\\Theta$\n\\end_inset\n\n respectively representing the number of chains per unit volume,\n Boltzmann's constant,\n and the absolute temperature.\n The parameter \n\\begin_inset Formula $N=\\zeta^{2}$\n\\end_inset\n\n is the number of chain segments,\n and \n\\begin_inset Formula $\\zeta$\n\\end_inset\n\n is the locking stretch that represents the extensibility of the material.\n\\end_layout\n\n\\begin_layout Standard\nA Taylor series expansion is used to evaluate the inverse Langevin equation.\n The parameter \n\\begin_inset Formula $n_{\\mathrm{term}}$\n\\end_inset\n\n is used to control the number of terms used to evaluate the series;\n \n\\begin_inset Formula $n_{\\mathrm{term}}$\n\\end_inset\n\n must be an integer between 3 and 30.\n\\end_layout\n\n\\begin_layout Standard\nLangevin mechanics describes a stochastic process with non-Gaussian distribution.\n It will reduce to a Gaussian distribution when the system approaches equilibrium.\n Thus,\n when the material is far from its maximum extensibility,\n i.e.,\n when \n\\begin_inset Formula $\\zeta=\\sqrt{N}$\n\\end_inset\n\n is much larger than the stretch,\n this model will reduce to a neo-Hookean material.\n\\end_layout\n\n\\begin_layout Standard\nThis material The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInitial fiber modulus \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<N>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of chain segments \n\\begin_inset Formula $N$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n_term>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of terms in the Taylor series expansion \n\\begin_inset Formula $n_{\\text{term}}$\n\\end_inset\n\n (\n\\begin_inset Formula $3\\le n_{\\text{term}}\\le30$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kappa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n of ground matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Soft Tissue\" type=\"Arruda-Boyce unconstrained\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<N>5</N>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<n_term>30</n_term>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<kappa>1</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCarter-Hayes\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Carter-Hayes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Carter-Hayes material is \n\\shape italic\nCarter-Hayes\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus at reference density \n\\begin_inset Formula $E_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<rho0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreference density \n\\begin_inset Formula $\\rho_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gamma>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponent of solid-bound molecule density for calculation of Young's modulus \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<sbm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nindex of solid bound molecule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model describes an unconstrained neo-Hookean material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n whose Young's modulus is a power-law function of the referential apparent density \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n of a solid-bound molecule.\n It is derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\frac{E_{Y}}{2\\left(1+\\nu\\right)}\\left[\\frac{1}{2}\\left(\\tr\\mathbf{C}-3\\right)-\\ln J+\\frac{\\nu}{1-2\\nu}\\left(\\ln J\\right)^{2}\\right].\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n is the right Cauchy-Green deformation tensor and \n\\begin_inset Formula $J$\n\\end_inset\n\n is the determinant of the deformation gradient tensor.\n\\end_layout\n\n\\begin_layout Standard\nYoung's modulus depends on \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n according to a power law \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Carter76,Carter77\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\nE_{Y}=E_{0}\\left(\\frac{\\rho_{r}^{\\sigma}}{\\rho_{0}}\\right)^{\\gamma}\\,.\n\\]\n\n\\end_inset\n\nThis type of material references a solid-bound molecule that belongs to a multiphasic mixture.\n Therefore this material may only be used as the solid (or a component of the solid) in a multiphasic mixture (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The solid-bound molecule must be defined in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Bound-Molecules\"\nnolink \"false\"\n\n\\end_inset\n\n) and must be included in the multiphasic mixture using a <solid_bound> tag.\n The parameter \n\\shape italic\nsbm\n\\shape default\n must refer to the global index of that solid-bound molecule.\n The value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n is specified within the <solid_bound> tag.\n If a chemical reaction is defined within that multiphasic mixture that alters the value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n,\n lower and upper bounds may be specified for this referential density within the <solid_bound> tag to prevent \n\\begin_inset Formula $E_{Y}$\n\\end_inset\n\n from reducing to zero or achieving excessively elevated values.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"solid matrix\" type=\"multiphasic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid_bound sbm=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rho0>1</rho0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rhomin>0.1</rhomin>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rhomax>5</rhomax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid_bound>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fixed_charge_density>0</fixed_charge_density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Carter-Hayes\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <sbm>1</sbm>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E0>10000</E0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rho0>1</rho0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <gamma>2.0</gamma>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <permeability type=\"perm-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <perm>1</perm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </permeability>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <osmotic_coefficient type=\"osm-coef-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <osmcoef>1</osmcoef>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </osmotic_coefficient>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <reaction name=\"solid remodeling\" type=\"mass-action-forward\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Vbar>0</Vbar>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <vP sbm=\"1\">1</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <forward_rate type=\"Huiskes reaction rate\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <B>1</B>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <psi0>0.01</psi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </forward_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </reaction>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCell Growth\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cell-Growth\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for cell growth is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ncell growth\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian2012\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phir>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nintracellular solid volume fraction in reference (strain-free) configuration,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cr>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nintracellular molar content of membrane-impermeant solute (moles per volume of the cell in the reference configuration),\n \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ce>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nextracellular osmolarity,\n \n\\begin_inset Formula $c_{e}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=-\\pi\\mathbf{I},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n is the osmotic pressure,\n given by \n\\begin_inset Formula \n\\[\n\\pi=RT\\left(\\frac{c_{r}}{J-\\varphi_{r}^{s}}-c_{e}\\right),\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the relative volume.\n The values of the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n and absolute temperature \n\\begin_inset Formula $T$\n\\end_inset\n\n must be specified as global constants.\n\\end_layout\n\n\\begin_layout Standard\nCell growth may be modeled by simply increasing the mass of the intracellular solid matrix and membrane-impermeant solute.\n This is achieved by allowing the parameters \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n to increase over time as a result of growth,\n by associating them with user-defined load curves.\n Since cell growth is often accompanied by cell division,\n and since daughter cells typically achieve the same solid and solute content as their parent,\n it may be convenient to assume that \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n increase proportionally,\n though this is not an obligatory relationship.\n To ensure that the initial configuration is a stress-free reference configuration,\n let \n\\begin_inset Formula $c_{r}=\\left(1-\\varphi_{r}^{s}\\right)c_{e}$\n\\end_inset\n\n in the initial state prior to growth.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample (using units of mm,\n N,\n s,\n nmol,\n K)\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>298</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314e-06</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Fc>0</Fc>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <material id=\"1\" name=\"Cell\" type=\"cell growth\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <phir lc=\"1\">1</phir>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <cr lc=\"2\">1</cr>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ce>300</ce>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </material>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"1\" type=\"smooth\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,0.3</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,0.6</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"2\" type=\"smooth\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,210</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,420</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nKinematic Growth\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Kinematic-Growth\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for kinematic growth is \n\\emph on\nkinematic growth\n\\emph default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic material undergoing kinematic growth\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<growth>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the growth model (\n\\emph on\nvolume growth\n\\emph default\n,\n \n\\emph on\narea growth\n\\emph default\n,\n \n\\emph on\nfiber growth\n\\emph default\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nKinematic growth,\n based on the framework first proposed in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Rodriguez94\"\nliteral \"false\"\n\n\\end_inset\n\n,\n uses the multiplicative decomposition of the deformation gradient into\n\\begin_inset Formula \n\\[\n\\mathbf{F}=\\mathbf{F}^{e}\\cdot\\mathbf{F}^{g}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{F}^{e}$\n\\end_inset\n\n represents the elastic deformation and \n\\begin_inset Formula $\\mathbf{F}^{g}$\n\\end_inset\n\n represents the growth.\n The related Jacobians are\n\\begin_inset Formula \n\\[\nJ^{e}=\\det\\mathbf{F}^{e}\\quad J^{g}=\\det\\mathbf{F}^{g}\\,.\n\\]\n\n\\end_inset\n\nA constitutive model for the growth tensor was provided by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Menzel12\"\nliteral \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\n\\mathbf{F}^{g}=\\vartheta^{\\text{iso}}\\mathbf{I}+\\left(\\vartheta^{\\text{ani}}-1\\right)\\mathbf{n}_{r}\\otimes\\mathbf{n}_{r}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the referential unit vector for a fiber direction (or normal to an area),\n \n\\begin_inset Formula $\\vartheta^{\\text{iso}}$\n\\end_inset\n\n represents isotropic growth,\n and \n\\begin_inset Formula $\\vartheta^{\\text{ani}}$\n\\end_inset\n\n represents anisotropic growth.\n\\end_layout\n\n\\begin_layout Standard\nFor \n\\emph on\nvolume growth\n\\emph default\n,\n let \n\\begin_inset Formula $\\vartheta^{\\text{iso}}=\\vartheta^{g}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\vartheta^{\\text{ani}}=1$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\vartheta^{g}$\n\\end_inset\n\n is called the growth \n\\emph on\nmultiplier\n\\emph default\n in FEBio,\n which is typically prescribed using a loadcurve.\n For \n\\emph on\narea growth\n\\emph default\n in the plane transverse to the normal direction \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n let \n\\begin_inset Formula $\\vartheta^{\\text{iso}}=\\sqrt{\\vartheta^{g}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\vartheta^{\\text{ani}}=2-\\sqrt{\\vartheta^{g}}$\n\\end_inset\n\n.\n Finally,\n for \n\\emph on\nfiber growth\n\\emph default\n,\n let \n\\begin_inset Formula $\\vartheta^{\\text{iso}}=1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\vartheta^{\\text{ani}}=\\vartheta^{g}$\n\\end_inset\n\n.\n For \n\\emph on\ngeneral growth\n\\emph default\n the user specifies the desired values of \n\\begin_inset Formula $\\vartheta^{\\text{iso}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\vartheta^{\\text{ani}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nInternally,\n the calculations within FEBio require the evaluation of the inverse of \n\\begin_inset Formula $\\mathbf{F}^{g}$\n\\end_inset\n\n,\n which is generall given by\n\\begin_inset Formula \n\\[\n\\left(\\mathbf{F}^{g}\\right)^{-1}=\\frac{1}{\\vartheta^{\\text{iso}}+\\vartheta^{\\text{ani}}-1}\\left(\\left(1+\\frac{\\vartheta^{\\text{ani}}-1}{\\vartheta^{\\text{iso}}}\\right)\\mathbf{I}-\\frac{\\vartheta^{\\text{ani}}-1}{\\vartheta^{\\text{iso}}}\\mathbf{n}_{r}\\otimes\\mathbf{n}_{r}\\right)\n\\]\n\n\\end_inset\n\nThe mass density of a growing material is evaluated from \n\\begin_inset Formula $\\rho=\\rho_{r}/J$\n\\end_inset\n\n where \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n is the (invariant) referential mass density of the growing solid (when there is neither growth nor deformation) and \n\\begin_inset Formula $J=\\det\\mathbf{F}=\\det\\mathbf{F}^{e}\\det\\mathbf{F}^{g}\\equiv J^{e}J^{g}$\n\\end_inset\n\n.\n It follows that the mass density change produced only by the growth is \n\\begin_inset Formula $\\rho J^{e}\\equiv\\rho_{rg}=\\rho_{r}/J^{g}$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $J^{g}=\\det\\mathbf{F}^{g}$\n\\end_inset\n\n may be used to evaluate the evolving solid mass density \n\\begin_inset Formula $\\rho_{rg}$\n\\end_inset\n\n due only to growth.\n From the general expression for \n\\begin_inset Formula $\\mathbf{F}^{g}$\n\\end_inset\n\n above it follows that\n\\begin_inset Formula \n\\[\nJ^{g}=\\det\\mathbf{F}^{g}=\\left(\\vartheta^{\\text{iso}}\\right)^{2}\\left(\\vartheta^{\\text{iso}}+\\vartheta^{\\text{ani}}-1\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCubic CLE\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cubic-CLE\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a conewise linear elastic (CLE) material with cubic symmetry is \n\\shape italic\ncubic CLE\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lp1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTensile diagonal first Lamé coefficient \n\\begin_inset Formula $\\lambda_{+1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lm1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCompressive diagonal first Lamé coefficient \n\\begin_inset Formula $\\lambda_{-1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOff-diagonal first Lamé coefficient \n\\begin_inset Formula $\\lambda_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond Lamé coefficient \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis bimodular elastic material is specialized from the orthotropic conewise linear elastic material described by Curnier et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Curnier94\"\nliteral \"true\"\n\n\\end_inset\n\n,\n to the case of cubic symmetry.\n It is derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\mu\\mathbf{I}:\\mathbf{E}^{2}+\\sum\\limits_{a=1}^{3}\\frac{1}{2}\\lambda_{1}\\left[\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right]\\left(\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right)^{2}+\\frac{1}{2}\\lambda_{2}\\sum\\limits_{\\begin{array}{c}\nb=1\\\\\nb\\ne a\n\\end{array}}^{3}\\left(\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right)\\left(\\mathbf{A}_{b}^{r}:\\mathbf{E}\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\lambda_{1}\\left[\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right]=\\begin{cases}\n\\lambda_{+1} & \\mathbf{A}_{a}^{r}:\\mathbf{E}\\geqslant0\\\\\n\\lambda_{-1} & \\mathbf{A}_{a}^{r}:\\mathbf{\\,}.E<0\n\\end{cases}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is the Lagrangian strain tensor and \n\\begin_inset Formula $\\mathbf{A}_{a}^{r}=\\mathbf{a}_{a}^{r}\\otimes\\mathbf{a}_{a}^{r}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{a}_{a}^{r}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n ) are orthonormal vectors aligned with the material axes.\n This material response was originally formulated for infinitesimal strain analyses;\n its behavior under finite strains may not be physically realistic.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"cubic CLE\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lp1>13.01</lp1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lm1>0.49</lm1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <l2>0.66</l2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.16</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nDonnan Equilibrium Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Donnan-Equilibrium-Swelling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Donnan equilibrium swelling pressure is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nDonnan equilibrium\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The swelling pressure is described by the equations for ideal Donnan equilibrium,\n assuming that the material is porous,\n with a charged solid matrix,\n and the external bathing environment consists of a salt solution of monovalent counter-ions \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Overbeek56,Lai91\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Since osmotic swelling must be resisted by a solid matrix,\n this material is not stable on its own.\n It must be combined with an elastic material that resists the swelling,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phiw0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngel porosity (fluid volume fraction) in reference (strain-free) configuration,\n \n\\begin_inset Formula $\\varphi_{0}^{w}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cF0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfixed-charge density in reference (strain-free) configuration,\n \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<bosm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexternal bath osmolarity,\n \n\\begin_inset Formula $\\bar{c}^{\\ast}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Phi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nosmotic coefficient,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is the stress from the Donnan equilibrium response \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian09a\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=-\\pi\\mathbf{I},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n is the osmotic pressure,\n given by \n\\begin_inset Formula \n\\[\n\\pi=RT\\Phi\\left(\\sqrt{\\left(c^{F}\\right)^{2}+\\left(\\bar{c}^{\\ast}\\right)^{2}}-\\bar{c}^{\\ast}\\right),\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n is the fixed-charge density in the current configuration,\n related to the reference configuration via \n\\begin_inset Formula \n\\[\nc^{F}=\\frac{\\varphi_{0}^{w}}{J-1+\\varphi_{0}^{w}}c_{0}^{F},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the relative volume.\n The values of the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n and absolute temperature \n\\begin_inset Formula $T$\n\\end_inset\n\n must be specified as global constants.\n For ideal Donnan law,\n use \n\\begin_inset Formula $\\Phi=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nNote that \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n may be negative or positive;\n the gel porosity is unitless and must be in the range \n\\begin_inset Formula $0<\\varphi_{0}^{w}<1$\n\\end_inset\n\n.\n A self-consistent set of units must be used for this model.\n For example:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(m,\n N,\n s,\n mol,\n K)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(mm,\n N,\n s,\n nmol,\n K) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $R$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8.314 J/mol\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\nK\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8.314×10\n\\begin_inset Formula $^{\\mathrm{-6}}$\n\\end_inset\n\n mJ/nmol\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\nK \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nK\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nK \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEq/m\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n= mEq/L\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnEq/mm\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n= mEq/L \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\bar{c}^{\\ast}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmol/m\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n= mM\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnmol/mm\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n= mM \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\xi_{i}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPa\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMPa \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\pi$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPa\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMPa \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThough this material is porous,\n this is not a full-fledged biphasic material as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for example.\n The behavior described by this material is strictly valid only after the transient response of interstitial fluid and ion fluxes has subsided (thus Donnan \n\\shape italic\nequilibrium\n\\shape default\n).\n\\end_layout\n\n\\begin_layout Standard\nDonnan osmotic swelling reduces to zero when either \n\\begin_inset Formula $c_{0}^{F}=0$\n\\end_inset\n\n or \n\\begin_inset Formula $\\bar{c}^{\\ast}\\to\\infty$\n\\end_inset\n\n.\n Therefore,\n entering any other values for \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\bar{c}^{\\ast}$\n\\end_inset\n\n at the initial time point of an analysis produces an instantaneous,\n non-zero swelling pressure.\n Depending on the magnitude of this pressure relative to the solid matrix stiffness,\n the nonlinear analysis may not converge due to this sudden swelling.\n Therefore,\n it is recommended to prescribe a load curve for either <cF0> or <bosm>,\n to ease into the initial swelling prior to the application of other loading conditions.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample (using units of mm,\n N,\n s,\n nmol,\n K)\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Donnan equilibrium\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <phiw0>0.8</phiw0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <cF0 lc=\"1\">1</cF0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <bosm>300</bosm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"ellipsoidal fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>0.01,0.01,0.01</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3,3,3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,0</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,150</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314e-6</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>310</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal Fiber Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ellipsoidal-Fiber-Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for an ellipsoidal continuous fiber distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nellipsoidal fiber distribution\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Since fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a stable compressible material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left({\\beta_{1},\\beta_{2},\\beta_{3}}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left({\\xi_{1},\\xi_{2},\\xi_{3}}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this fibrous material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lanir83,Ateshian07a,Ateshian09a\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\int_{0}^{2\\pi}\\int_{0}^{\\pi}H\\left(I_{n}-1\\right)\\sigma_{n}\\left(\\mathbf{n}\\right)\\sin\\varphi\\,d\\varphi\\,d\\theta.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $I_{n}=\\lambda_{n}^{2}=\\mathbf{N}\\cdot\\mathbf{C}\\cdot\\mathbf{N}$\n\\end_inset\n\n is the square of the fiber stretch \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n is the unit vector along the fiber direction,\n in the reference configuration,\n which in spherical angles is directed along \n\\begin_inset Formula $\\left(\\theta,\\varphi\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{N}/\\lambda_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $H\\left(.\\right)$\n\\end_inset\n\nis the unit step function that enforces the tension-only contribution.\n\\end_layout\n\n\\begin_layout Standard\nThe fiber stress is determined from a fiber strain energy function,\n \n\\begin_inset Formula \n\\[\n\\sigma_{n}=\\frac{2I_{n}}{J}\\frac{\\partial\\Psi}{\\partial I_{n}}\\mathbf{n}\\otimes\\mathbf{n},\n\\]\n\n\\end_inset\n\nwhere in this material,\n \n\\begin_inset Formula \n\\[\n\\Psi\\left(\\mathbf{n},I_{n}\\right)=\\xi\\left(\\mathbf{n}\\right)\\left(I_{n}-1\\right)^{\\beta\\left(\\mathbf{n}\\right)}.\n\\]\n\n\\end_inset\n\nThe materials parameters \n\\begin_inset Formula $\\beta$\n\\end_inset\n\nand \n\\begin_inset Formula $\\xi$\n\\end_inset\n\nare assumed to vary ellipsoidally with \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n,\n according to \n\\begin_inset Formula \n\\[\n\\begin{aligned}\\xi\\left(\\mathbf{n}\\right) & =\\left(\\frac{\\cos^{2}\\theta\\sin^{2}\\varphi}{\\xi_{1}^{2}}+\\frac{\\sin^{2}\\theta\\sin^{2}\\varphi}{\\xi_{2}^{2}}+\\frac{\\cos^{2}\\varphi}{\\xi_{3}^{2}}\\right)^{-1/2}\\\\\n\\beta\\left(\\mathbf{n}\\right) & =\\left(\\frac{\\cos^{2}\\theta\\sin^{2}\\varphi}{\\beta_{1}^{2}}+\\frac{\\sin^{2}\\theta\\sin^{2}\\varphi}{\\beta_{2}^{2}}+\\frac{\\cos^{2}\\varphi}{\\beta_{3}^{2}}\\right)^{-1/2}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nThe orientation of the material axis can be defined as explained in detail in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"ellipsoidal fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>10,\n 12,\n 15</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5,\n 3,\n 3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal Fiber Distribution Neo-Hookean\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:EFD-Neo-Hookean\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Neo-Hookean material with an ellipsoidal continuous fiber distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nEFD neo-Hookean\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\beta_{1},\\beta_{2},\\beta_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\xi_{1},\\xi_{2},\\xi_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is given by,\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\boldsymbol{\\sigma}_{NH}+\\boldsymbol{\\sigma}_{f}.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{NH}$\n\\end_inset\n\n is the stress from the Neo-Hookean basis (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Neo-Hookean\"\nnolink \"false\"\n\n\\end_inset\n\n),\n and \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{f}$\n\\end_inset\n\n is the stress contribution from the fibers (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Ellipsoidal-Fiber-Distribution\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"EFD neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>4.5,4.5,4.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ksi>1,1,1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal Fiber Distribution with Donnan Equilibrium Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:EFD-Donnan-Equilibrium\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a swelling pressure combined with an ellipsoidal continuous fiber distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nEFD Donnan equilibrium\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The swelling pressure is described by the equations for ideal Donnan equilibrium,\n assuming that the material is porous,\n with a charged solid matrix,\n and the external bathing environment consists of a salt solution of monovalent counter-ions.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phiw0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngel porosity (fluid volume fraction) in reference (strain-free) configuration,\n \n\\begin_inset Formula $\\varphi_{0}^{w}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cF0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfixed-charge density in reference (strain-free) configuration,\n \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<bosm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexternal bath osmolarity,\n \n\\begin_inset Formula $\\bar{c}^{\\ast}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\beta_{1},\\beta_{2},\\beta_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\left(\\xi_{1},\\xi_{2},\\xi_{3}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is given by,\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\boldsymbol{\\sigma}_{DE}+\\boldsymbol{\\sigma}_{f}\\,.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $\\boldsymbol{\\sigma}_{f}$\n\\end_inset\n\n is the stress contribution from the fibers,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{DE}$\n\\end_inset\n\n is the stress from the Donnan equilibrium response,\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Donnan-Equilibrium-Swelling\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample (using units of mm,\n N,\n s,\n nmol,\n K)\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"EFD Donnan equilibrium\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phiw0>0.8</phiw0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <cF0 lc=\"1\">1</cF0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <bosm>300</bosm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>3,3,3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ksi>0.01,0.01,0.01</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,0</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,150</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314e-6</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>310</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFung Orthotropic Compressible\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fung-Orthotropic-Compressible\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for unconstrained orthotropic Fung elasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung79,Fung93\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nFung-ortho-compressible\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E_{1}$\n\\end_inset\n\n Young's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E_{2}$\n\\end_inset\n\n Young's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E_{3}$\n\\end_inset\n\n Young's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $G_{12}$\n\\end_inset\n\n shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $G_{23}$\n\\end_inset\n\n shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $G_{31}$\n\\end_inset\n\n shear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\nu_{12}$\n\\end_inset\n\n Poisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\nu_{23}$\n\\end_inset\n\n Poisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\nu_{31}$\n\\end_inset\n\n Poisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $c$\n\\end_inset\n\n coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n bulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe hyperelastic strain energy function is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian09\"\nliteral \"true\"\n\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\n\\Psi=\\frac{1}{2}c\\left(e^{Q}-1\\right)+U\\left(J\\right),\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\[\nQ=c^{-1}\\sum\\limits_{a=1}^{3}\\left[2\\mu_{a}\\mathbf{M}_{a}:\\mathbf{E}^{2}+\\sum\\limits_{b=1}^{3}\\lambda_{ab}\\left(\\mathbf{M}_{a}:\\mathbf{E}\\right)\\left(\\mathbf{M}_{b}:\\mathbf{E}\\right)\\right],\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\[\nU\\left(J\\right)=\\frac{\\kappa}{2}\\left(\\ln J\\right)^{2}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{E}=\\left(\\mathbf{C}-\\mathbf{I}\\right)/2$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{M}_{a}=\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}$\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n defines the initial direction of material axis \n\\begin_inset Formula $a$\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n on how to define the material axes for orthotropic materials.\n The Lamé constants \n\\begin_inset Formula $\\mu_{a}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lambda_{ab}$\n\\end_inset\n\n (\n\\begin_inset Formula $a,b=1,2,3$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\lambda_{ba}=\\lambda_{ab})$\n\\end_inset\n\n are related to Young's moduli \n\\begin_inset Formula $E_{a}$\n\\end_inset\n\n,\n shear moduli \n\\begin_inset Formula $G_{ab}$\n\\end_inset\n\n and Poisson's ratios \n\\begin_inset Formula $\\nu_{ab}$\n\\end_inset\n\n via \n\\begin_inset Formula \n\\[\n\\begin{aligned} & \\left[\\begin{array}{cccccc}\n\\lambda_{11}+2\\mu_{1} & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0\\\\\n\\lambda_{12} & \\lambda_{22}+2\\mu_{2} & \\lambda_{23} & 0 & 0 & 0\\\\\n\\lambda_{13} & \\lambda_{23} & \\lambda_{33}+2\\mu_{3} & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{1}+\\mu_{2}\\right) & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{2}+\\mu_{3}\\right) & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\frac{1}{2}\\left(\\mu_{1}+\\mu_{3}\\right)\n\\end{array}\\right]^{-1}\\\\\n & =\\left[\\begin{array}{cccccc}\n\\frac{1}{E_{1}} & -\\frac{\\nu_{12}}{E_{1}} & -\\frac{\\nu_{13}}{E_{1}} & 0 & 0 & 0\\\\\n-\\frac{\\nu_{21}}{E_{2}} & \\frac{1}{E_{2}} & -\\frac{\\nu_{23}}{E_{2}} & 0 & 0 & 0\\\\\n-\\frac{\\nu_{31}}{E_{3}} & -\\frac{\\nu_{32}}{E_{3}} & \\frac{1}{E_{3}} & 0 & 0 & 0\\\\\n0 & 0 & 0 & \\frac{1}{G_{12}} & 0 & 0\\\\\n0 & 0 & 0 & 0 & \\frac{1}{G_{23}} & 0\\\\\n0 & 0 & 0 & 0 & 0 & \\frac{1}{G_{31}}\n\\end{array}\\right]\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThe orthotropic Lamé parameters should produce a positive definite stiffness matrix.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"Fung-ortho-compressible\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E1>124</E1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E2>124</E2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E3>36</E3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G12>67</G12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G23>40</G23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G31>40</G31>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v12>-0.075</v12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v23>0.87</v23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v31>0.26</v31>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>1</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>120</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nGent Compressible\n\\end_layout\n\n\\begin_layout Standard\nThe compressible Gent material is defined using the \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\ncompressible Gent\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n type string.\n It defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Jm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $J_{m}=I_{m}-1$\n\\end_inset\n\n,\n with \n\\begin_inset Formula $I_{m}$\n\\end_inset\n\n max value for first invariant \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain energy of this material is defined as follows,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi_{r}=-\\frac{\\mu J_{m}}{2}\\ln\\left(1-\\frac{I_{1}-3}{J_{m}}\\right)+\\frac{\\kappa}{2}\\left(\\frac{J^{2}-1}{2}-\\ln J\\right)^{4}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the shear modulus.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"compressible Gent\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G>3.14</G>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Jm>1.5</Jm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1e5</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nIsotropic Hencky\n\\end_layout\n\n\\begin_layout Standard\nThe material type for isotropic Hencky hyperelasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Xiao02\"\nliteral \"false\"\n\n\\end_inset\n\n is \n\\shape italic\nisotropic\n\\emph on\n \n\\shape default\nHencky\n\\emph default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material is an implementation of a hyperelastic constitutive material that reduces to the classical linear elastic material for small strains,\n but is objective for large deformations and rotations.\n The hyperelastic strain-energy function is given by:\n \n\\begin_inset Formula \n\\[\nW=\\frac{1}{2}\\lambda\\left(\\tr\\mathbf{H}\\right)^{2}+\\mu\\mathbf{H}:\\mathbf{H}\n\\]\n\n\\end_inset\n\nHere,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{H}$\n\\end_inset\n\n\n\\series default\n is the right Hencky strain tensor and \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are the Lamé parameters,\n which are related to the more familiar Young's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n and Poisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n as follows:\n \n\\begin_inset Formula \n\\[\n\\lambda=\\frac{\\nu E}{\\left(1+\\nu\\right)\\left(1-2\\nu\\right)},\\mu=\\frac{E}{2\\left(1+\\nu\\right)}\\,.\n\\]\n\n\\end_inset\n\nThe Cauchy stress for this material takes the form\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\frac{\\lambda}{J}\\left(\\mathbf{I}:\\mathbf{h}\\right)\\mathbf{I}+\\frac{2\\mu}{J}\\mathbf{h}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{h}$\n\\end_inset\n\n is the left Hencky strain tensor.\n In the limit of infinitesimal strains and rotations,\n \n\\begin_inset Formula $J\\to1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{h}\\to\\boldsymbol{\\varepsilon}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\boldsymbol{\\varepsilon}$\n\\end_inset\n\n is the infinitesimal strain tensor.\n\\end_layout\n\n\\begin_layout Standard\nIt is often convenient to express the material properties using the bulk modulus \n\\begin_inset Formula $K$\n\\end_inset\n\n and shear modulus \n\\begin_inset Formula $G$\n\\end_inset\n\n.\n To convert to Young's modulus and Poisson's ratio,\n use the following formulas:\n \n\\begin_inset Formula \n\\[\nE=\\frac{9KG}{3K+G},\\quad\\nu=\\frac{3K-2G}{6K+2G}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"isotropic Hencky\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nHolmes-Mow\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Holmes-Mow\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Holmes-Mow material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\shape italic\nHolmes-Mow\n\\shape default\n.\n This isotropic hyperelastic material has been used to represent the solid matrix of articular cartilage \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90,Ateshian97\"\nliteral \"true\"\n\n\\end_inset\n\n and intervertebral disc \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Iatridis98\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stiffening coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe coupled hyperelastic strain-energy function for this material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\nW\\left(I_{1},I_{2},J\\right)=\\frac{1}{2}c\\left(e^{Q}-1\\right),\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{2}$\n\\end_inset\n\n are the first and second invariants of the right Cauchy-Green tensor and \n\\begin_inset Formula $J$\n\\end_inset\n\nis the jacobian of the deformation gradient.\n Furthermore,\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}Q & =\\frac{\\beta}{\\lambda+2\\mu}\\left[\\left(2\\mu-\\lambda\\right)\\left(I_{1}-3\\right)+\\lambda\\left(I_{2}-3\\right)-\\left(\\lambda+2\\mu\\right)\\ln J^{2}\\right]\\\\\nc & =\\frac{\\lambda+2\\mu}{2\\beta}\n\\end{aligned}\n\\,,\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are the Lamé parameters which are related to the more familiar Young's modulus and Poisson's ratio in the usual manner:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}\\lambda & =\\frac{E\\nu}{\\left(1+\\nu\\right)\\left(1-2\\nu\\right)}\\\\\n\\mu & =\\frac{E}{2\\left(1+\\nu\\right)}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.35</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>0.25</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nHolzapfel-Gasser-Ogden Unconstrained\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:HGO-Unconstrained\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the unconstrained Holzapfel-Gasser-Ogden material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gasser06\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\emph on\nHGO unconstrained\n\\emph default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus of ground matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber exponential coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gamma>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber mean orientation angle \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\ndeg\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kappa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber dispersion\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy function is given by:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}\\Psi_{r} & =\\frac{c}{2}\\left(I_{1}-3\\right)-c\\ln J+\\frac{k_{1}}{2k_{2}}\\sum_{\\alpha}\\left(\\exp\\left(k_{2}\\left\\langle E_{\\alpha}\\right\\rangle ^{2}\\right)-1\\right)\\\\\n & +\\frac{K_{0}}{2}\\left(\\frac{J^{2}-1}{2}-\\ln J\\right)\n\\end{aligned}\n\\]\n\n\\end_inset\n\nThe fiber strain is\n\\begin_inset Formula \n\\[\nE_{\\alpha}=\\kappa\\left(I_{1}-3\\right)+\\left(1-3\\kappa\\right)\\left(I_{4\\alpha}-1\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{1}=\\tr\\mathbf{C}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{4\\alpha}=\\mathbf{a}_{\\alpha r}\\cdot\\mathbf{C}\\cdot\\mathbf{a}_{\\alpha r}$\n\\end_inset\n\n.\n The Macaulay brackets around \n\\begin_inset Formula $\\left\\langle \\tilde{E}_{\\alpha}\\right\\rangle $\n\\end_inset\n\n indicate that this term is zero when \n\\begin_inset Formula $E_{\\alpha}<0$\n\\end_inset\n\n and equal to \n\\begin_inset Formula $E_{\\alpha}$\n\\end_inset\n\n when this strain is positive.\n\\end_layout\n\n\\begin_layout Standard\nThere are two fiber families along the vectors \n\\begin_inset Formula $\\mathbf{a}_{\\alpha r}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha=1,2$\n\\end_inset\n\n),\n lying in the \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2}\\right\\} $\n\\end_inset\n\n plane of the local material axes \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n,\n making an angle \n\\begin_inset Formula $\\pm\\gamma$\n\\end_inset\n\n with respect to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n.\n Each fiber family has a dispersion \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $0\\le\\kappa\\le\\frac{1}{3}$\n\\end_inset\n\n.\n When \n\\begin_inset Formula $\\kappa=0$\n\\end_inset\n\n there is no fiber dispersion,\n implying that all the fibers in that family act along the angle \n\\begin_inset Formula $\\pm\\gamma$\n\\end_inset\n\n;\n the value \n\\begin_inset Formula $\\kappa=\\frac{1}{3}$\n\\end_inset\n\n represents an isotropic fiber dispersion.\n All other intermediate values of \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n produce a \n\\begin_inset Formula $\\pi-$\n\\end_inset\n\nperiodic von Mises fiber distribution,\n as described in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gasser06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\begin_inset Formula $c$\n\\end_inset\n\n is the shear modulus of the ground matrix;\n \n\\begin_inset Formula $k_{1}$\n\\end_inset\n\n is the fiber modulus and \n\\begin_inset Formula $k_{2}$\n\\end_inset\n\n is the exponential coefficient.\n\\end_layout\n\n\\begin_layout Standard\nUnlike the uncoupled Holzapfel-Gasser-Ogden material presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Holzapfel-Gasser-Ogden\"\nnolink \"false\"\n\n\\end_inset\n\n,\n this unconstrained version does not enforce isochoric deformation.\n This unconstrained model may be used to describe the porous solid matrix of a biphasic or multiphasic tissue model,\n where pore volume may change in response to influx or efflux of interstitial fluid.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" type=\"HGO unconstrained\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>7.64</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k1>996.6</k1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k2>524.6</k2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gamma>49.98</gamma>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kappa>0.226</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>7.64e3</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nIsotropic Elastic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Isotropic-Elastic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for isotropic elasticity is \n\\shape italic\nisotropic elastic\n\\shape default\n\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\n This material replaces the now-obsolete \n\\shape italic\nlinear elastic \n\\shape default\nand \n\\shape italic\nSt.Venant-Kirchhoff\n\\shape default\n materials.\n These materials are still available for backward compatibility although it is recommended to use the \n\\shape italic\nisotropic elastic\n\\shape default\n material instead.\n\\end_layout\n\n\\end_inset\n\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material is an implementation of a hyperelastic constitutive material that reduces to the classical linear elastic material for small strains,\n but is objective for large deformations and rotations.\n The hyperelastic strain-energy function is given by:\n \n\\begin_inset Formula \n\\[\nW=\\frac{1}{2}\\lambda\\left(\\tr\\mathbf{E}\\right)^{2}+\\mu\\mathbf{E}:\\mathbf{E}.\n\\]\n\n\\end_inset\n\nHere,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n\n\\series default\n is the Euler-Lagrange strain tensor and \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n are the Lamé parameters,\n which are related to the more familiar Young's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n and Poisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n as follows:\n \n\\begin_inset Formula \n\\[\n\\lambda=\\frac{\\nu E}{\\left(1+\\nu\\right)\\left(1-2\\nu\\right)},\\mu=\\frac{E}{2\\left(1+\\nu\\right)}\\,.\n\\]\n\n\\end_inset\n\nIt is often convenient to express the material properties using the bulk modulus \n\\begin_inset Formula $K$\n\\end_inset\n\n and shear modulus \n\\begin_inset Formula $G$\n\\end_inset\n\n.\n To convert to Young's modulus and Poisson's ratio,\n use the following formulas:\n \n\\begin_inset Formula \n\\[\nE=\\frac{9KG}{3K+G},\\quad\\nu=\\frac{3K-2G}{6K+2G}\\,.\n\\]\n\n\\end_inset\n\n\n\\shape italic\nRemark:\n \n\\shape default\nNote that although this material is objective,\n it is not advised to use this model for large strains since the behavior may be unphysical.\n For example,\n it can be shown that for a uniaxial tension the stress grows unbounded and the volume tends to zero for finite strains.\n Also for large strains,\n the Young's modulus and Poisson's ratio input values have little relationship to the actual material parameters.\n Therefore it is advisable to use this material only for small strains and/or large rotations.\n To represent isotropic elastic materials under large strain and rotation,\n it is best to use some of the other available nonlinear material models described in this chapter,\n such as the Holmes-Mow material in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Holmes-Mow\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the neo-Hookean material in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Neo-Hookean\"\nnolink \"false\"\n\n\\end_inset\n\n,\n or the unconstrained Ogden material in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Ogden-Unconstrained\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"isotropic elastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nOrthotropic Elastic\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Orthotropic-Elastic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for orthotropic elasticity is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\northotropic elastic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus in the x-direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus in the y-direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus in the z-direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus in the xy-plane\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus in the yz-plane\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<G31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus in the xz-plane\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio between x- and y-direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio between y- and z-direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio between z- and x-direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe stress-strain relation for this material is given by \n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{c}\nE_{11}\\\\\nE_{22}\\\\\nE_{33}\\\\\n2E_{23}\\\\\n2E_{31}\\\\\n2E_{12}\n\\end{array}\\right]=\\left[\\begin{array}{cccccc}\n1/E_{1} & -\\nu_{21}/E_{2} & -\\nu_{31}/E_{3} & 0 & 0 & 0\\\\\n-\\nu_{12}/E_{1} & 1/E_{2} & -\\nu_{32}/E_{3} & 0 & 0 & 0\\\\\n-\\nu_{13}/E_{1} & -\\nu_{23}/E_{2} & 1/E_{3} & 0 & 0 & 0\\\\\n0 & 0 & 0 & 1/G_{23} & 0 & 0\\\\\n0 & 0 & 0 & 0 & 1/G_{31} & 0\\\\\n0 & 0 & 0 & 0 & 0 & 1/G_{12}\n\\end{array}\\right]\\left[\\begin{array}{c}\nT_{11}\\\\\nT_{22}\\\\\nT_{33}\\\\\nT_{23}\\\\\nT_{31}\\\\\nT_{12}\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\nMaterial axes may be specified as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"3\" type=\"orthotropic elastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <a>0.866,0.5,0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <d>-0.5,0.866,0</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E1>1</E1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E2>2</E2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E3>3</E3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v12>0</v12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v23>0</v23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v31>0</v31>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G12>1</G12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G23>1</G23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <G31>1</G31>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nOrthotropic CLE\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Orthotropic-CLE\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a conewise linear elastic (CLE) material with orthtropic symmetry is \n\\shape italic\northotropic CLE\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"12\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lp11>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTensile diagonal first Lamé coefficient along direction 1 \n\\begin_inset Formula $\\lambda_{+11}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lp22>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTensile diagonal first Lamé coefficient along direction 2 \n\\begin_inset Formula $\\lambda_{+22}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lp11>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTensile diagonal first Lamé coefficient along direction 3 \n\\begin_inset Formula $\\lambda_{+33}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lm11>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCompressive diagonal first Lamé coefficient along direction 1 \n\\begin_inset Formula $\\lambda_{-11}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lm22>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCompressive diagonal first Lamé coefficient along direction 2 \n\\begin_inset Formula $\\lambda_{-22}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lm33>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCompressive diagonal first Lamé coefficient along direction 3 \n\\begin_inset Formula $\\lambda_{-33}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l12>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOff-diagonal first Lamé coefficient in 1-2 plane \n\\begin_inset Formula $\\lambda_{12}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l23>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOff-diagonal first Lamé coefficient in 2-3 plane \n\\begin_inset Formula $\\lambda_{23}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<l31>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOff-diagonal first Lamé coefficient in 3-1 plane \n\\begin_inset Formula $\\lambda_{31}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond Lamé coefficient along direction 1 \n\\begin_inset Formula $\\mu_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond Lamé coefficient along direction 2 \n\\begin_inset Formula $\\mu_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond Lamé coefficient along direction 3 \n\\begin_inset Formula $\\mu_{3}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis bimodular elastic material is the orthotropic conewise linear elastic material described by Curnier et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Curnier94\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It is derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\sum\\limits_{a=1}^{3}\\mu_{a}\\mathbf{A}_{a}^{r}:\\mathbf{E}^{2}+\\frac{1}{2}\\lambda_{aa}\\left[\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right]\\left(\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right)^{2}+\\sum\\limits_{\\begin{array}{c}\nb=1\\\\\nb\\ne a\n\\end{array}}^{3}\\frac{1}{2}\\lambda_{ab}\\left(\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right)\\left(\\mathbf{A}_{b}^{r}:\\mathbf{E}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\lambda_{ba}=\\lambda_{ab}$\n\\end_inset\n\n and \n\\begin_inset Formula \n\\[\n\\lambda_{aa}\\left[\\mathbf{A}_{a}^{r}:\\mathbf{E}\\right]=\\begin{cases}\n\\lambda_{+aa} & \\mathbf{A}_{a}^{r}:\\mathbf{E}\\geqslant0\\\\\n\\lambda_{-aa} & \\mathbf{A}_{a}^{r}:\\mathbf{E}<0\n\\end{cases},\\quad a=1,2,3\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is the Lagrangian strain tensor and \n\\begin_inset Formula $\\mathbf{A}_{a}^{r}=\\mathbf{a}_{a}^{r}\\otimes\\mathbf{a}_{a}^{r}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{a}_{a}^{r}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3)$\n\\end_inset\n\n are orthonormal vectors aligned with the material axes.\n This material response was originally formulated for infinitesimal strain analyses;\n its behavior under finite strains may not be physically realistic.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\" orthotropic CLE\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lp11>13.01</lp11>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lp22>13.01</lp22>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lp33>13.01</lp33>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lm11>0.49</lm11>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lm22>0.49</lm22>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lm33>0.49</lm33>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <l12>0.66</l12>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <l23>0.66</l23>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <l31>0.66</l31>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu1>0.16</mu1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu2>0.16</mu2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu3>0.16</mu3>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nOsmotic Pressure from Virial Expansion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Osmotic-Pressure-Virial\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for osmotic pressure from virial expansion is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nosmotic virial expansion\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phiw0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid volume fraction in reference (strain-free) configuration,\n \n\\begin_inset Formula $\\varphi_{r}^{w}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cr>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConcentration of interstitial solute causing the osmotic pressure (moles per volume of the mixture in the reference configuration),\n \n\\begin_inset Formula $c_{r}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFirst virial coefficient \n\\begin_inset Formula $c_{1}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n/\n\\series bold\nn\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSecond virial coefficient \n\\begin_inset Formula $c_{2}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nn\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThird virial coefficient \n\\begin_inset Formula $c_{3}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{7}}$\n\\end_inset\n\n/\n\\series bold\nn\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=-\\pi\\mathbf{I},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n is the osmotic pressure,\n given by \n\\begin_inset Formula \n\\[\n\\pi=c_{1}c+c_{2}c^{2}+c_{3}c^{3},\\quad c=\\frac{\\varphi_{r}^{w}c_{r}}{J-1+\\varphi_{r}^{w}}\\,,\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $c$\n\\end_inset\n\n is the solute concentration in the current configuration,\n and \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the relative volume.\n\\end_layout\n\n\\begin_layout Standard\nThis osmotic swelling pressure in the interstitial fluid of a porous material represents an entropic mechanism whose magnitude is independent of the external bath osmolarity.\n Typically,\n this material should be used in a solid mixture where the swelling pressure is resisted by a solid matrix in tension.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solid type=\"osmotic virial expansion\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phiw0>0.8</phiw0>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <cr lc=\"1\">100</cr>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <c1>2.436e-6</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <c3>0</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solid type=\"spherical fiber distribution\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </material>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"1\" type=\"smooth\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,0</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,1</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nNatural Neo-Hookean\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Natural-Neo-Hookean\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a natural Neo-Hookean material is \n\\shape italic\nnatural neo-Hookean\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model describes an unconstrained Neo-Hookean material using the natural strain tensor \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Criscione00\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It has a non-linear stress-strain behavior,\n but reduces to the classical linear elasticity model for small strains \n\\shape italic\nand\n\\shape default\n small rotations.\n It is derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\[\nW=\\frac{\\kappa}{2}K_{1}^{2}+\\mu K_{2}^{2},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n is the bulk modulus and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the shear modulus,\n\\begin_inset Formula \n\\[\n\\kappa=\\frac{E}{3\\left(1-2\\nu\\right)}\\,,\\quad\\mu=\\frac{E}{2\\left(1+\\nu\\right)}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $K_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n are the first and second invariants of the left natural (Hencky) strain tensor \n\\series bold\n\n\\begin_inset Formula $\\boldsymbol{\\eta}=\\ln\\mathbf{V}$\n\\end_inset\n\n\n\\series default\n where \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is the left stretch tensor in the polar decomposition \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{V}\\cdot\\mathbf{R}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a standard displacement-based element formulation,\n so care must be taken when modeling materials with nearly-incompressible material behavior to avoid element locking.\n For this case,\n use the \n\\shape italic\nMooney-Rivlin \n\\shape default\nmaterial described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mooney-Rivlin\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"natural neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>200e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nNeo-Hookean\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Neo-Hookean\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Neo-Hookean material is \n\\shape italic\nneo-Hookean\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model describes an unconstrained Neo-Hookean material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Bonet97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It has a non-linear stress-strain behavior,\n but reduces to the classical linear elasticity model for small strains \n\\shape italic\nand\n\\shape default\n small rotations.\n It is derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\[\nW=\\frac{\\mu}{2}\\left(I_{1}-3\\right)-\\mu\\ln J+\\frac{\\lambda}{2}\\left(\\ln J\\right)^{2}.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{2}$\n\\end_inset\n\n are the first and second invariants of the right Cauchy-Green deformation tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series default\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n is the determinant of the deformation gradient tensor.\n The relationship between the material parameters,\n E,\n and v,\n and the parameters used in the strain-energy function,\n is as follows.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mu=\\dfrac{E}{2(1+\\nu)}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\lambda=\\dfrac{\\nu E}{(1+\\nu)(1-2\\nu)}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a standard displacement-based element formulation,\n so care must be taken when modeling materials with nearly-incompressible material behavior to avoid element locking.\n For this case,\n use the \n\\shape italic\nMooney-Rivlin \n\\shape default\nmaterial described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mooney-Rivlin\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCoupled Mooney-Rivlin\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Coupled-Mooney-Rivlin\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe coupled Mooney-Rivlin material describes an unconstrained formulation of the Mooney-Rivlin material.\n The material type for this material is \n\\shape italic\ncoupled Mooney-Rivlin\n\\shape default\n.\n The following material parameters can be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin c1 parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin c2 parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Quotes eld\n\\end_inset\n\nBulk-modulus\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy function is given by the following expression.\n \n\\begin_inset Formula \n\\[\nW=c_{1}\\left(I_{1}-3\\right)+c_{2}\\left(I_{2}-3\\right)-2\\left(c_{1}+2c_{2}\\right)\\ln J+\\frac{k}{2}\\left(\\ln J\\right)^{2}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{2}$\n\\end_inset\n\n are the first and second invariants of the right Cauchy-Green deformation tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series default\n and \n\\begin_inset Formula $J$\n\\end_inset\n\n is the determinant of the deformation gradient tensor.\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a standard displacement-based element formulation,\n so care must be taken when modeling materials with nearly-incompressible material behavior to avoid element locking.\n For (nearly-) incompressible materials,\n use the \n\\shape italic\nMooney-Rivlin \n\\shape default\nmaterial described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mooney-Rivlin\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"coupled Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>1.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCoupled Veronda-Westmann\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Coupled-Veronda-Westmann\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the coupled Veronda-Westmann material is \n\\shape italic\ncoupled Veronda-Westmann\n\\shape default\n.\n The following material parameters can be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVeronda-Westmann c1 parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVeronda-Westmann c2 parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Quotes eld\n\\end_inset\n\nBulk-modulus\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe coupled Veronda-Westmann material is an unconstrained formulation of the Veronda-Westmann material and is defined by the following strain-energy function.\n \n\\begin_inset Formula \n\\[\nW=c_{1}\\left(e^{c_{2}\\left(I_{1}-3\\right)}-1\\right)-\\frac{c_{1}c_{2}}{2}\\left(I_{2}-3\\right)+\\frac{\\lambda}{2}\\left(\\ln J\\right)^{2}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{2}$\n\\end_inset\n\n are the first and second invariants of the right Cauchy-Green deformation tensor \n\\series bold\nC\n\\series default\n and \n\\begin_inset Formula $J$\n\\end_inset\n\nis the determinant of the deformation gradient tensor.\n\\end_layout\n\n\\begin_layout Standard\nThis material model uses a standard displacement-based element formulation,\n so care must be taken when modeling materials with nearly-incompressible material behavior to avoid element locking.\n For (nearly-) incompressible materials,\n use the \n\\shape italic\nVeronda-Westmann \n\\shape default\nmaterial described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Veronda-Westmann\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"coupled Veronda-Westmann\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>1.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nOgden Unconstrained\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ogden-Unconstrained\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material describes an unconstrained hyperelastic Ogden material\n\\size small\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo91\"\nliteral \"true\"\n\n\\end_inset\n\n\n\\size default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c[n]>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCoefficient of n\n\\begin_inset Formula $^{\\mathrm{th}}$\n\\end_inset\n\n term,\n where n can range from 1 to 6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<m[n]>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponent of n\n\\begin_inset Formula $^{\\mathrm{th\\thinspace}}$\n\\end_inset\n\nterm,\n where n can range from 1 to 6 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cp>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk-like modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe hyperelastic strain energy function for this material is given in terms of the eigenvalues of the right or left stretch tensor:\n \n\\begin_inset Formula \n\\[\nW\\left(\\lambda_{1},\\lambda_{2},\\lambda_{3}\\right)=\\frac{1}{2}c_{p}\\left(J-1\\right)^{2}+\\sum\\limits_{i=1}^{N}\\frac{c_{i}}{m_{i}^{2}}\\left(\\lambda_{1}^{m_{i}}+\\lambda_{2}^{m_{i}}+\\lambda_{3}^{m_{i}}-3-m_{i}\\ln J\\right).\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\lambda_{i}^{2}$\n\\end_inset\n\n are the eigenvalues of the right or left Cauchy deformation tensor,\n \n\\begin_inset Formula $c_{p}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $c_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $m_{i}$\n\\end_inset\n\nare material coefficients and \n\\begin_inset Formula $N$\n\\end_inset\n\n ranges from 1 to 6.\n Any material parameters that are not specified by the user are assumed to be zero.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"Ogden unconstrained\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <m1>2.4</m1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <cp>2</cp>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPerfect Osmometer Equilibrium Osmotic Pressure\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Perfect-Osmometer-Equilibrium\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a perfect osmometer equilibrium swelling pressure is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nperfect osmometer\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The swelling pressure is described by the equations for a perfect osmometer,\n assuming that the material is porous,\n containing an interstitial solution whose solutes cannot be exchanged with the external bathing environment;\n similarly,\n solutes in the external bathing solution cannot be exchanged with the interstitial fluid of the porous material.\n Therefore,\n osmotic pressurization occurs when there is an imbalance between the interstitial and bathing solution osmolarities.\n Since osmotic swelling must be resisted by a solid matrix,\n this material is not stable on its own.\n It must be combined with an elastic material that resists the swelling,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phiw0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngel porosity (fluid volume fraction) in reference (strain-free) configuration,\n \n\\begin_inset Formula $\\varphi_{0}^{w}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<iosm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninterstitial fluid osmolarity in reference configuration,\n \n\\begin_inset Formula $\\bar{c}_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<bosm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexternal bath osmolarity,\n \n\\begin_inset Formula $\\bar{c}^{\\ast}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this material is the stress from the perfect osmometer equilibrium response:\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=-\\pi\\mathbf{I},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\pi$\n\\end_inset\n\n is the osmotic pressure,\n given by \n\\begin_inset Formula \n\\[\n\\pi=RT\\left(\\bar{c}-\\bar{c}^{\\ast}\\right)\\,.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $\\bar{c}$\n\\end_inset\n\n is the interstitial fluid in the current configuration,\n related to the reference configuration via \n\\begin_inset Formula \n\\[\n\\bar{c}=\\frac{\\varphi_{0}^{w}}{J-1+\\varphi_{0}^{w}}\\bar{c}_{0}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the relative volume.\n The values of the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n and absolute temperature \n\\begin_inset Formula $T$\n\\end_inset\n\n must be specified as global constants.\n\\end_layout\n\n\\begin_layout Standard\nThough this material is porous,\n this is not a full-fledged biphasic material as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for example.\n The behavior described by this material is strictly valid only after the transient response of interstitial fluid and solute fluxes has subsided.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample (using units of mm,\n N,\n s,\n nmol,\n K)\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nHyperosmotic loading of a cell with a membrane-impermeant solute,\n starting from isotonic conditions.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"perfect osmometer\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <phiw0>0.8</ phiw0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <iosm>300</cF0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <bosm lc=\"1\">1</bosm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,300</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,1500</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314e-6</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>310</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPorous Neo-Hookean\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Porous-Neo-Hookean\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a porous neo-Hookean solid is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nporous neo-Hookean\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material describes a porous neo-Hookean material with referential solid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n (i.e.,\n porosity \n\\begin_inset Formula $\\varphi_{r}^{w}=1-\\varphi_{r}^{s}$\n\\end_inset\n\n).\n The pores are compressible but the skeleton is intrinsically incompressible.\n Thus,\n upon pore closure,\n the material behavior needs to switch from compressible to incompressible.\n This material may be used to model the porous solid matrix of a biphasic or multiphasic material,\n in which case it inherits the value of \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n from that parent material.\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a porous Neo-Hookean material is \n\\shape italic\nporous neo-Hookean\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n (\n\\begin_inset Formula $0\\le\\varphi_{r}^{s}<1$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model is derived from the following hyperelastic strain-energy function:\n \n\\begin_inset Formula \n\\[\nW=\\frac{\\mu}{2}\\left(\\bar{I}_{1}-3\\right)-\\mu\\ln\\bar{J}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\bar{J}$\n\\end_inset\n\n represents the pore volume ratio,\n evaluated from\n\\begin_inset Formula \n\\[\n\\bar{J}=\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\,,\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\bar{I}_{1}=\\text{tr}\\bar{\\mathbf{C}}$\n\\end_inset\n\n,\n with\n\\begin_inset Formula \n\\[\n\\bar{\\mathbf{F}}=\\left(\\frac{\\bar{J}}{J}\\right)^{1/3}\\mathbf{F}\\,,\\quad\\bar{\\mathbf{C}}=\\bar{\\mathbf{F}}^{T}\\cdot\\bar{\\mathbf{F}}=\\left(\\frac{\\bar{J}}{J}\\right)^{2/3}\\mathbf{C}\\,,\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula $\\bar{J}=\\det\\bar{\\mathbf{F}}$\n\\end_inset\n\n.\n The porosity \n\\begin_inset Formula $\\varphi^{w}$\n\\end_inset\n\n in the current configuration evolves with the volume ratio \n\\begin_inset Formula $J$\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\[\n\\varphi^{w}=\\frac{J-1+\\varphi_{r}^{w}}{J}=\\frac{J-\\varphi_{r}^{s}}{J}=\\bar{J}\\left(1-\\varphi_{r}^{s}\\right)\\,.\n\\]\n\n\\end_inset\n\nThus,\n pore closure occurs when \n\\begin_inset Formula $J=\\varphi_{r}^{s}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\bar{J}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nBy comparison to a standard neo-Hookean material,\n this porous neo-Hookean material has an effective Young's modulus equal to\n\\begin_inset Formula \n\\[\nE=\\frac{3\\mu}{1+\\frac{1}{2}\\left(\\varphi_{r}^{w}\\right)^{2}}\\,,\n\\]\n\n\\end_inset\n\nand an effective Poisson's ratio equal to\n\\begin_inset Formula \n\\[\n\\nu=\\frac{1-\\left(\\varphi_{r}^{w}\\right)^{2}}{2+\\left(\\varphi_{r}^{w}\\right)^{2}}\\,.\n\\]\n\n\\end_inset\n\nTherefore,\n the two material properties that need to be provided are \n\\begin_inset Formula $E$\n\\end_inset\n\n and the referential porosity \n\\begin_inset Formula $\\varphi_{r}^{s}=1-\\varphi_{r}^{w}$\n\\end_inset\n\n.\n Poisson's ratio in the limit of infinitesimal strains is dictated by the porosity according to the above formula.\n In particular,\n a highly porous material (\n\\begin_inset Formula $\\varphi_{r}^{w}\\to1$\n\\end_inset\n\n) has an effective Poisson ratio that approaches zero (\n\\begin_inset Formula $\\nu\\to0$\n\\end_inset\n\n) and \n\\begin_inset Formula $E\\to2\\mu$\n\\end_inset\n\n in the range of infinitesimal strains.\n A low porosity material (\n\\begin_inset Formula $\\varphi_{r}^{w}\\to0$\n\\end_inset\n\n) has \n\\begin_inset Formula $\\nu\\to\\frac{1}{2}$\n\\end_inset\n\n and \n\\begin_inset Formula $E\\to3\\mu$\n\\end_inset\n\n,\n which is the expected behavior of an incompressible neo-Hookean solid.\n Note that setting \n\\begin_inset Formula $\\varphi_{r}^{w}=0$\n\\end_inset\n\n (\n\\begin_inset Formula $\\varphi_{r}^{s}=1$\n\\end_inset\n\n) would not produce good numerical behavior,\n since the Cauchy stress in an incompressible material would need to be supplemented by a hydrostatic pressure term (a Lagrange multiplier that enforces the incompressibility constraint).\n Nevertheless,\n this compressible porous neo-Hookean material behaves well even for values of \n\\begin_inset Formula $\\varphi^{w}$\n\\end_inset\n\n as low as \n\\begin_inset Formula $\\sim0.015$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"porous neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1.0</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.2</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLung Material \n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:LungMaterial\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis is a material used for modeling human lung tissue as described by Birzle\n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Birzle2019\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus,\n \n\\begin_inset Formula $E$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio,\n \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBirzle-Wall c1 parameter,\n \n\\begin_inset Formula $c_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBirzle-Wall c3 parameter,\n \n\\begin_inset Formula $c_{3}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<d1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBirzle-Wall exponential d1 parameter,\n \n\\begin_inset Formula $d_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<d3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBirzle-Wall exponential d3 parameter,\n \n\\begin_inset Formula $d_{3}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis model is derived from the following hyperelastic strain-energy function:\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nW=c\\left(I_{1}-3\\right)+\\frac{c}{\\beta}\\left(I_{3}^{-\\beta}-1\\right)+c_{1}\\left(I_{3}^{-\\frac{1}{3}}I_{1}-3\\right)^{d_{1}}+c_{3}\\left(I_{3}^{\\frac{1}{3}}-1\\right)^{d_{3}},\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $I_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $I_{2}$\n\\end_inset\n\n are the first and second invariants of the right Cauchy-Green deformation tensor \n\\series bold\n\n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n\n\\series default\n,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nc=\\frac{E}{4\\left(1+\\nu\\right)},\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nand\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\beta=\\frac{\\nu}{1-2\\nu}.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"lung\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1913.7</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3413</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>278.2</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>5.766</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <d1>3</d1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <d3>6</d3>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSolid Mixture\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solid-Mixture\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a constrained mixture of solids is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nsolid mixture\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material describes a mixture of elastic solids that share the same reference configuration and same deformation gradient.\n It is a container for any combination of the unconstrained elastic materials described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContainer tag for unconstrained solid material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe mixture may consist of any number of solids.\n The stress tensor for the solid mixture is the sum of the stresses for all the solids.\n\\end_layout\n\n\\begin_layout Standard\nMaterial axes may be optionally specified within the <material> level,\n as well as within each <solid>.\n Within the <material> level,\n these represent the local element axes relative to the global coordinate system.\n Within the <solid>,\n they represent local material axes relative to the element.\n If material axes are specified at both levels,\n they are properly compounded to produce local material axes relative to the global coordinate system.\n Material axes specified in the <ElementData> section are equivalent to a specification at the <material> level:\n they correspond to local element axes relative to the global system.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"ellipsoidal fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <a>0.8660254,\n 0.5,\n 0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <d>0,0,1</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5,\n 1,\n 1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5,\n 3,\n 3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"ellipsoidal fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"vector\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <a>0.8660254,-0.5,\n 0</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <d>0,0,1</d>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5,\n 1,\n 1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5,\n 3,\n 3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSpherical Fiber Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Spherical-Fiber-Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a spherical (isotropic) continuous fiber distribution is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nspherical fiber distribution\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Since fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a stable compressible material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameters \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this fibrous material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lanir83,Ateshian07a,Ateshian09a\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\int_{0}^{2\\pi}\\int_{0}^{\\pi}H\\left(I_{n}-1\\right)\\sigma_{n}\\left(\\mathbf{n}\\right)\\sin\\varphi\\,d\\varphi\\,d\\theta\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $I_{n}=\\lambda_{n}^{2}=\\mathbf{N}\\cdot\\mathbf{C}\\cdot\\mathbf{N}$\n\\end_inset\n\n is the square of the fiber stretch \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n is the unit vector along the fiber direction,\n in the reference configuration,\n which in spherical angles is directed along \n\\begin_inset Formula $\\left(\\theta,\\varphi\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{N}/\\lambda_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $H\\left(.\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n\\end_layout\n\n\\begin_layout Standard\nThe fiber stress is determined from a fiber strain energy function,\n \n\\begin_inset Formula \n\\[\n\\sigma_{n}=\\frac{2I_{n}}{J}\\frac{\\partial\\Psi}{\\partial I_{n}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\n\\]\n\n\\end_inset\n\nwhere in this material,\n the fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi=\\frac{\\xi}{\\alpha}\\left(\\exp\\left[\\alpha\\left(I_{n}-1\\right)^{\\beta}\\right]-1\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\beta\\geqslant2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nNote:\n In the limit when \n\\begin_inset Formula $\\alpha\\to0$\n\\end_inset\n\n,\n this expressions produces a power law,\n \n\\begin_inset Formula \n\\[\n\\lim\\limits_{\\alpha\\to0}\\Psi=\\xi\\left(I_{n}-1\\right)^{\\beta}\n\\]\n\n\\end_inset\n\nNote:\n When \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n,\n the fiber modulus is zero at the strain origin (\n\\begin_inset Formula $I_{n}=1)$\n\\end_inset\n\n.\n Therefore,\n use \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n when a smooth transition in the stress is desired from compression to tension.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"spherical fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>10</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSpherical Fiber Distribution from Solid-Bound Molecule\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Spherical-Fiber-Distribution-SBM\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a spherical (isotropic) continuous fiber distribution with fiber modulus dependent on solid-bound molecule content is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nspherical fiber distribution sbm\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Since fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a stable compressible material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber modulus \n\\begin_inset Formula $\\xi_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gamma>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber modulus exponent \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<rho0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber mass density \n\\begin_inset Formula $\\rho_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsbm\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nindex of solid-bound molecule \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress for this fibrous material is given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Lanir83,Ateshian07a,Ateshian09a\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\int_{0}^{2\\pi}\\int_{0}^{\\pi}H\\left(I_{n}-1\\right)\\sigma_{n}\\left(\\mathbf{n}\\right)\\sin\\varphi\\,d\\varphi\\,d\\theta\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $I_{n}=\\lambda_{n}^{2}=\\mathbf{N}\\cdot\\mathbf{C}\\cdot\\mathbf{N}$\n\\end_inset\n\n is the square of the fiber stretch \n\\begin_inset Formula $\\lambda_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{N}$\n\\end_inset\n\n is the unit vector along the fiber direction,\n in the reference configuration,\n which in spherical angles is directed along \n\\begin_inset Formula $\\left(\\theta,\\varphi\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{N}/\\lambda_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $H\\left(.\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n\\end_layout\n\n\\begin_layout Standard\nThe fiber stress is determined from a fiber strain energy function,\n \n\\begin_inset Formula \n\\[\n\\sigma_{n}=\\frac{2I_{n}}{J}\\frac{\\partial\\Psi}{\\partial I_{n}}\\mathbf{n}\\otimes\\mathbf{n}\\,,\n\\]\n\n\\end_inset\n\nwhere in this material,\n the fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi=\\frac{\\xi}{\\alpha}\\left(\\exp\\left[\\alpha\\left(I_{n}-1\\right)^{\\beta}\\right]-1\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\beta\\geqslant2$\n\\end_inset\n\n.\n The fiber modulus is dependent on the solid-bound molecule referential density \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n according to the power law relation \n\\begin_inset Formula \n\\[\n\\xi=\\xi_{0}\\left(\\frac{\\rho_{r}^{\\sigma}}{\\rho_{0}}\\right)^{\\gamma}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{0}$\n\\end_inset\n\n is the density at which \n\\begin_inset Formula $\\xi=\\xi_{0}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThis type of material references a solid-bound molecule that belongs to a multiphasic mixture.\n Therefore this material may only be used as the solid (or a component of the solid) in a multiphasic mixture (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The solid-bound molecule must be defined in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Bound-Molecules\"\nnolink \"false\"\n\n\\end_inset\n\n) and must be included in the multiphasic mixture using a <solid_bound> tag.\n The parameter \n\\shape italic\nsbm\n\\shape default\n must refer to the global index of that solid-bound molecule.\n The value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n is specified within the <solid_bound> tag.\n If a chemical reaction is defined within that multiphasic mixture that alters the value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n,\n lower and upper bounds may be specified for this referential density within the <solid_bound> tag to prevent \n\\begin_inset Formula $\\xi$\n\\end_inset\n\n from reducing to zero or achieving excessively elevated values.\n\\end_layout\n\n\\begin_layout Standard\nNote:\n In the limit when \n\\begin_inset Formula $\\alpha\\to0$\n\\end_inset\n\n,\n the expression for \n\\begin_inset Formula $\\Psi$\n\\end_inset\n\n produces a power law,\n \n\\begin_inset Formula \n\\[\n\\lim\\limits_{\\alpha\\to0}\\Psi=\\xi\\left(I_{n}-1\\right)^{\\beta}\n\\]\n\n\\end_inset\n\nNote:\n When \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n,\n the fiber modulus is zero at the strain origin (\n\\begin_inset Formula $I_{n}=1)$\n\\end_inset\n\n.\n Therefore,\n use \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n when a smooth transition in the stress is desired from compression to tension.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<solid type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"spherical fiber distribution sbm\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>2.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi0>10</ksi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <gamma>2</gamma>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <rho0>1</rho0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <sbm>1</sbm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solid>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCoupled Transversely Isotropic Mooney-Rivlin\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Coupled-Transversely-Isotropic-MR\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material describes a transversely isotropic Mooney-Rivlin material using a fully-coupled formulation.\n It is define through the \n\\shape italic\ncoupled trans-iso Mooney-Rivlin \n\\shape default\nmaterial type.\n The following material parameters must be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin \n\\begin_inset Formula $c_{1}$\n\\end_inset\n\n parameter.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMooney-Rivlin \n\\begin_inset Formula $c_{2}$\n\\end_inset\n\n parameter.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential multiplier \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber modulus in linear region\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlam_max\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum fiber straightening stretch \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbulk-like modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy function for this constitutive model is defined by \n\\begin_inset Formula \n\\[\nW=c_{1}\\left(I_{1}-3\\right)+c_{2}\\left(I_{2}-3\\right)-2\\left(c_{1}+2c_{2}\\right)\\ln J+F\\left(\\lambda\\right)+U\\left(J\\right)\n\\]\n\n\\end_inset\n\nThe first three terms define the coupled Mooney-Rivlin matrix response.\n The third term is the fiber response which is a function of the fiber stretch \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and is defined as in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss96\"\nliteral \"true\"\n\n\\end_inset\n\n.\n For \n\\begin_inset Formula $U$\n\\end_inset\n\n,\n the following form is chosen in FEBio.\n \n\\begin_inset Formula \n\\[\nU\\left(J\\right)=\\frac{1}{2}k\\left(\\ln J\\right)^{2}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\nis the Jacobian of the deformation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"coupled trans-iso Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.1</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>1</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>1</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>1.34</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>1.3</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCoupled Transversely Isotropic Veronda-Westmann\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Coupled-Transversely-Isotropic-VW\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material describes a transversely isotropic Veronda-Westmann material using a fully-coupled formulation.\n It is define through the \n\\shape italic\ncoupled trans-iso Veronda-Westmann \n\\shape default\nmaterial type.\n The following material parameters must be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVeronda-Westmann \n\\begin_inset Formula $c_{1}$\n\\end_inset\n\n parameter.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVeronda-Westmann \n\\begin_inset Formula $c_{2}$\n\\end_inset\n\n parameter.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential multiplier \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber scale factor\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber modulus in linear region\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlam_max\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum fiber straightening stretch \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbulk-like modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy function for this constitutive model is defined by \n\\begin_inset Formula \n\\[\nW=c_{1}\\left(e^{c_{2}\\left(I_{1}-3\\right)}-1\\right)-\\frac{1}{2}c_{1}c_{2}\\left(I_{2}-3\\right)+F\\left(\\lambda\\right)+U\\left(J\\right)\\,.\n\\]\n\n\\end_inset\n\nThe first two terms define the coupled Veronda-Westmann matrix response.\n The third term is the fiber response which is a function of the fiber stretch \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n and is defined as in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss96\"\nliteral \"true\"\n\n\\end_inset\n\n.\n For \n\\begin_inset Formula $U$\n\\end_inset\n\n,\n the following form is chosen in FEBio.\n \n\\begin_inset Formula \n\\[\nU\\left(J\\right)=\\frac{1}{2}k\\left(\\ln J\\right)^{2}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\nis the Jacobian of the deformation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"coupled trans-iso Veronda-Westmann\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>0.1</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c3>1</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c4>1</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c5>1.34</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_max>1.3</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLarge Poisson's Ratio Ligament\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Large-Poisson's-Ratio\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material describes a coupled,\n transversely isotropic material that will conform to a particular Poisson's ratio when stretched along the fiber direction \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Swedberg2014\"\nliteral \"true\"\n\n\\end_inset\n\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber coefficient \n\\begin_inset Formula $c_{\\mathrm{1}}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc2 \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber coefficient \n\\begin_inset Formula $c_{\\mathrm{2}}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmu\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMatrix coefficient \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nv0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio parameter \n\\begin_inset Formula $v_{\\mathrm{0}}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nm\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio parameter \n\\begin_inset Formula $m$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVolumetric penalty coefficient \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain energy function for this constitutive model is a three part expression:\n \n\\begin_inset Formula \n\\[\nW=W_{\\text{fiber}}+W_{\\text{matrix}}+W_{\\text{vol}},\n\\]\n\n\\end_inset\n\nwhere:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}W_{\\text{fiber}} & =\\frac{1}{2}\\frac{c_{1}}{c_{2}}\\left(e^{c_{2}\\left({\\lambda-1}\\right)^{2}-1}\\right),\\\\\nW_{\\text{matrix}} & =\\frac{\\mu}{2}\\left(I_{1}-3\\right)-\\mu\\ln\\left(\\sqrt{I_{3}}\\right),\\\\\nW_{\\text{vol}} & =\\frac{\\kappa}{2}\\left(\\ln\\left(\\frac{I_{5}-I_{1}I_{4}+I_{2}}{I_{4}^{2(m-v_{0})}e^{-4m\\left(\\lambda-1\\right)}}\\right)\\right)^{2}.\n\\end{aligned}\n\\]\n\n\\end_inset\n\nIn the equations above,\n \n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n is the stretch ratio of the material along the fiber direction.\n The desired Poisson's ratio must first be selected based on available data for uniaxial tension along the fiber direction.\n The function with which to fit the Poisson's ratio data is:\n \n\\begin_inset Formula \n\\[\nv=-\\frac{\\lambda^{m-v_{0}}e^{-m\\left(\\lambda-1\\right)}-1}{\\lambda-1}\\,.\n\\]\n\n\\end_inset\n\nThe volumetric penalty coefficient \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n must be selected to be large enough to enforce the Poisson's function above.\n If this material is to be used in a biphasic representation,\n \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n must be selected based on experimental stress-relaxation data,\n since \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n has an effect on the biphasic behavior of the material.\n Once \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n,\n \n\\begin_inset Formula $v_{0}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $m$\n\\end_inset\n\n are chosen,\n \n\\begin_inset Formula $c_{\\mathrm{1}}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $c_{\\mathrm{2}}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n should be selected by fitting the stress-strain behavior of the material to experimental data.\n The Cauchy stress of the material is given by:\n \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\frac{2}{J}\\left(\\left(W_{1}+I_{1}W_{2}\\right)\\mathbf{B}-W_{2}\\mathbf{B}^{2}+W_{3}\\left(I_{3}\\mathbf{1}\\right)+W_{4}I_{4}\\left(\\mathbf{a}\\otimes\\mathbf{a}\\right)+W_{5}I_{4}\\left(\\mathbf{a}\\otimes\\mathbf{a}\\cdot\\mathbf{B}+\\mathbf{B}\\cdot\\mathbf{a}\\otimes\\mathbf{a}\\right)\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J$\n\\end_inset\n\n is the jacobian of the deformation gradient \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{B}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n\n\\series default\n is the left Cauchy-Green deformation tensor,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{1}$\n\\end_inset\n\n\n\\series default\n is the \n\\begin_inset Formula $3\\times3$\n\\end_inset\n\n identity tensor\n\\series bold\n,\n \n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n \n\\series default\nis the fiber orientation vector in the deformed configuration.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material1\" type=\"PRLig\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c1>90</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c2>160</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.025</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v0>5.85</v0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <m>-100</m>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1.55</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nShenoy-Wang material\n\\end_layout\n\n\\begin_layout Standard\nThis material implements the constitutive model by Wang et al.\n (Biophysical Journal,\n 107,\n 2014,\n pp:2592 - 2603),\n which proposes a mechanism for long-range force transmission in fibrous matrices enabled by tension-driven alignment of fibers.\n \n\\end_layout\n\n\\begin_layout Standard\nIt defines the following parameters.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmu\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmatrix shear modulus \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nk\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmatrix bulk modulus \n\\begin_inset Formula $k$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEf\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber stiffness modulus \n\\begin_inset Formula $E_{f}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlam_c\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntransition point \n\\begin_inset Formula $\\lambda_{c}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlam_t\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntransition interval size \n\\begin_inset Formula $\\lambda_{t}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nn\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrain hardening exponent \n\\emph on\nn\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nm\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrain hardening exponent \n\\emph on\nm\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe strain-energy density function is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nW=W_{\\text{b}}+W_{\\text{f}},\n\\]\n\n\\end_inset\n\nwhere:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}W_{\\text{b}} & =\\frac{\\mu}{2}\\left(\\overline{I}_{1}-3\\right)+\\frac{\\kappa}{2}\\left(J-1\\right)^{2},\\\\\nW_{\\text{f}} & =\\stackrel[a=1]{3}{\\sum}f\\left(\\lambda_{a}\\right),\n\\end{aligned}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nand \n\\begin_inset Formula $f$\n\\end_inset\n\nis defined via its derivative,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\frac{\\partial f}{\\partial\\lambda_{a}}\\left(\\lambda_{a}\\right)=\\begin{cases}\n0, & \\lambda_{a}<\\lambda_{1}\\\\\n\\frac{E_{f}\\left(\\frac{\\lambda_{a}-\\lambda_{1}}{\\lambda_{2}-\\lambda_{1}}\\right)^{n}\\left(\\lambda_{a}-\\lambda_{1}\\right)}{n+1}, & \\lambda_{1}\\leq\\lambda_{a}<\\lambda_{2}\\\\\nE_{f}\\left[\\frac{\\lambda_{2}-\\lambda_{1}}{n+1}+\\frac{\\left(1+\\lambda_{a}-\\lambda_{2}\\right)^{m+1}-1}{m+1}\\right], & \\lambda_{a}\\geq\\lambda_{2}\n\\end{cases}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFinally,\n the parameters \n\\begin_inset Formula $\\lambda_{1}$\n\\end_inset\n\nand \n\\begin_inset Formula $\\lambda_{2}$\n\\end_inset\n\nare given as follows.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{align*}\n\\lambda_{1} & =\\lambda_{c}-\\lambda_{t}/2\\\\\n\\lambda_{2} & =\\lambda_{c}+\\lambda_{t}/2\n\\end{align*}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material1\" type=\"Shenoy\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.7692</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1.667</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Ef>134.6</Ef>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_c>1.02</lam_c>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam_t>0.255</lam_t>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <n>5</n>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <m>30</m>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nFibers\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fibers\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFiber materials are used to model one-dimensional structures oriented along a unit vector.\n The associated strain energy density is a function of the normal strain along that vector.\n These fiber models are constructed such that the strain energy is non-zero only when the fiber is in tension,\n under the idealized assumption that fibers do not sustain any load in compression.\n This assumption produces an inherent instability in the material response of fibers,\n therefore such models must be combined with elastic materials that can sustain compression and tension,\n thus serving as models of a ground matrix.\n\\end_layout\n\n\\begin_layout Standard\nFEBio accommodates fiber models that can be combined with unconstrained or uncoupled models of the ground matrix.\n Historically,\n unconstrained models of a ground matrix have been combined with unconstrained fiber models,\n whereas uncoupled models of a ground matrix have been combined with uncoupled fiber models.\n The manual sections presented below follow this conventional approach.\n\\end_layout\n\n\\begin_layout Standard\nHowever,\n it should be noted that some authors have expressed concerns about using uncoupled fiber formulations in fiber-matrix material models \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Sansour08,Helfenstein10,Gultekin19\"\nliteral \"true\"\n\n\\end_inset\n\n,\n due to two fundamental concerns:\n When highly nonlinear fibers become much stiffer than the ground matrix upon loading,\n it may become difficult to enforce an isochoric deformation,\n as would be expected in an uncoupled formulation.\n Furthermore,\n in an uncoupled formulation,\n the strain is split into its deviatoric (isochoric) and volumetric parts and the fiber strain is evaluated only from the deviatoric part.\n This deviatoric fiber strain is not the true fiber strain,\n yet it is used to determine whether the fiber is in tension.\n These factors may result in non-physical deformations,\n as described by Helfenstein et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Helfenstein10\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe solution advocated by these investigators has been to use an uncoupled formulation for the ground matrix only,\n while the fiber models should remain unconstrained \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Sansour08,Helfenstein10,Gultekin19\"\nliteral \"true\"\n\n\\end_inset\n\n.\n This can be done in FEBio by using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n,\n where the material representing the ground matrix is taken from the list of uncoupled materials in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n,\n whereas fiber models are taken from the list of unconstrained models in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Fiber-Models\"\nnolink \"false\"\n\n\\end_inset\n\n or Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Continuous-Fiber\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUnconstrained Fiber Models\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Unconstrained-Fiber-Models\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSince fibers can only sustain tension,\n the materials listed here are not stable on their own.\n They must be combined with a stable material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n,\n within a <solid> tag.\n These fiber models may also be used in continuous fiber distribution models as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Continuous-Fiber\"\nnolink \"false\"\n\n\\end_inset\n\n,\n within a <fibers> tag.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Exponential-Power Law\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:fiber-exp-pow\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a single fiber with an exponential-power law is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-exp-pow\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Since fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a stable compressible material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\xi,$\n\\end_inset\n\n representing a measure of the fiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\alpha,$\n\\end_inset\n\n coefficient of exponential argument\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\beta,$\n\\end_inset\n\n power of exponential argument\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda_{0},$\n\\end_inset\n\n stretch threshold for tensile response\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber is oriented along the unit vector \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal basis vectors representing the local element coordinate system when specified (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or else the global Cartesian coordinate system.\n The Cauchy stress for this fibrous material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=H\\left(I_{n}-I_{0}\\right)\\frac{2I_{n}}{J}\\frac{\\partial\\Psi}{\\partial I_{n}}\\mathbf{n}\\otimes\\mathbf{n},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{n}=\\lambda_{n}^{2}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the square of the fiber stretch,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{n}_{r}/\\lambda_{n}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $I_{0}=\\lambda_{0}^{2}$\n\\end_inset\n\n is the square of the stretch threshold for the tensile response (\n\\begin_inset Formula $\\lambda_{0}=1$\n\\end_inset\n\n by default) and \n\\begin_inset Formula $H\\left(.\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution.\n The fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi=\\frac{\\xi}{\\alpha\\beta}\\left(\\exp\\left[\\alpha\\left(I_{n}-I_{0}\\right)^{\\beta}\\right]-1\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\beta\\geqslant2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nNote:\n In the limit when \n\\begin_inset Formula $\\alpha\\to0$\n\\end_inset\n\n,\n this expressions produces a power law,\n \n\\begin_inset Formula \n\\[\n\\lim\\limits_{\\alpha\\to0}\\,\\Psi=\\frac{\\xi}{\\beta}\\left(I_{n}-I_{0}\\right)^{\\beta}\n\\]\n\n\\end_inset\n\nNote:\n When \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n,\n the fiber modulus is zero at the strain origin (\n\\begin_inset Formula $I_{n}=I_{0})$\n\\end_inset\n\n.\n Therefore,\n use \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n when a smooth transition in the stress is desired from compression to tension.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nSingle fiber oriented along \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n embedded in a neo-Hookean ground matrix.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-pow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n        <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nTwo fibers in the plane orthogonal to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n oriented at ±25 degrees relative to \n\\begin_inset Formula $\\mathbf{e}_{3}$\n\\end_inset\n\n,\n embedded in a neo-Hookean ground matrix.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1000.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.45</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-pow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <theta>90</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phi>25</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-pow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <theta>-90</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phi>25</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Neo-Hookean Law\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-with-Neo-Hookean\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-NH\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\mu,$\n\\end_inset\n\n representing a measure of the fiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi_{n}\\left(I_{n}\\right)=\\frac{\\mu}{4}\\left(I_{n}-1\\right)^{2}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu>0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nFiber model as specified in a continuous fiber distribution (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Continuous-Fiber\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout LyX-Code\n<fibers type=\"fiber-NH\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>1</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fibers>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Natural Neo-Hookean Law\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-with-Neo-Hookean-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-natural-NH\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\xi,$\n\\end_inset\n\n fiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda_{0},$\n\\end_inset\n\n stretch threshold for tensile response\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi_{n}\\left(\\lambda_{n}\\right)=\\frac{\\xi}{2}H\\left(\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)\\left(\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}\\right)^{2}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\lambda_{0}\\ge1$\n\\end_inset\n\n.\n The tensile response engages at the tensile stretch ratio \n\\begin_inset Formula $\\lambda_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\lambda_{0}=1$\n\\end_inset\n\n by default).The natural (logarithmic or Hencky) strain along the fiber is \n\\begin_inset Formula $\\ln\\lambda_{n}$\n\\end_inset\n\n,\n whereas \n\\begin_inset Formula $\\ln\\frac{\\lambda_{n}}{\\lambda_{0}}=\\ln\\lambda_{n}-\\ln\\lambda_{0}$\n\\end_inset\n\n is the natural strain relative to the tensile stretch threshold.\n This model produces a stress response which varies linearly with the natural strain.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nFiber model as specified in a solid mixture (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>0.13</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-natural-NH\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <lam0>1.25</lam0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber type=\"vector\">0,0,1</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Toe-Linear Response\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Pow-Linear\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-pow-linear\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E,$\n\\end_inset\n\n the fiber modulus in the linear range (\n\\begin_inset Formula $E\\geqslant0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\beta,$\n\\end_inset\n\n the power-law exponent in the toe region (\n\\begin_inset Formula $\\beta\\geqslant2)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda_{0},$\n\\end_inset\n\n the stretch ratio when the toe region transitions to the linear region (\n\\begin_inset Formula $\\lambda_{0}>1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi_{n}\\left(I_{n}\\right)=\\begin{cases}\n0 & I_{n}<1\\\\\n\\frac{\\xi}{\\beta}\\left(I_{n}-1\\right)^{\\beta} & 1\\leqslant I_{n}\\leqslant I_{0}\\\\\nB\\left(I_{n}-I_{0}\\right)-E\\left(I_{n}^{1/2}-I_{0}^{1/2}\\right)+\\frac{\\xi}{\\beta}\\left(I_{0}-1\\right)^{\\beta} & I_{0}<I_{n}\n\\end{cases}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{0}=\\lambda_{0}^{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\n\\xi=\\frac{E}{4\\left(\\beta-1\\right)}I_{0}^{-3/2}\\left(I_{0}-1\\right)^{2-\\beta},\\,B=\\xi\\left(I_{0}-1\\right)^{\\beta-1}+\\frac{E}{2}I_{0}^{-1/2}\n\\]\n\n\\end_inset\n\nFor this material type,\n the fiber elasticity at the strain origin reduces to zero unless \n\\begin_inset Formula $\\beta=2$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nFiber model as specified in a solid mixture (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout LyX-Code\n<solid type=\"fiber-pow-linear\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <theta>20</center>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>2.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam0>1.06</lam0>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solid>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Exp-Pow-Linear Response\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Exp-Pow-Linear\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-exp-pow-linear\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E,$\n\\end_inset\n\n the fiber modulus in the linear range (\n\\begin_inset Formula $E\\geqslant0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\alpha,$\n\\end_inset\n\n coefficient of exponential argument\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\beta,$\n\\end_inset\n\n the power-law exponent in the toe region (\n\\begin_inset Formula $\\beta\\geqslant2)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda_{0},$\n\\end_inset\n\n the stretch ratio when the toe region transitions to the linear region (\n\\begin_inset Formula $\\lambda_{0}>1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\Psi_{n}=\\begin{cases}\n0 & I_{n}<1\\\\\n\\frac{\\xi}{\\alpha\\beta}\\left(\\exp\\left[\\alpha\\left(I_{n}-1\\right)^{\\beta}\\right]-1\\right) & 1\\le I_{n}\\le I_{0}\\\\\nB\\left(I_{n}-I_{0}\\right)-E\\left(I_{n}^{1/2}-I_{0}^{1/2}\\right)+\\frac{\\xi}{\\alpha\\beta}\\left(\\exp\\left[\\alpha\\left(I_{0}-1\\right)^{\\beta}\\right]-1\\right) & I_{n}>I_{0}\n\\end{cases}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{0}=\\lambda_{0}^{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\n\\xi=\\frac{E\\left(I_{0}-1\\right)^{2-\\beta}\\exp\\left[-\\alpha\\left(I_{0}-1\\right)^{\\beta}\\right]}{4I_{0}^{3/2}\\left(\\beta-1+\\alpha\\beta\\left(I_{0}-1\\right)^{\\beta}\\right)}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\nB=E\\frac{2I_{0}\\left(\\beta-\\frac{1}{2}+\\alpha\\beta\\left(I_{0}-1\\right)^{\\beta}\\right)-1}{4I_{0}^{3/2}\\left(\\beta-1+\\alpha\\beta\\left(I_{0}-1\\right)^{\\beta}\\right)}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material type,\n the fiber elasticity at the strain origin reduces to zero unless \n\\begin_inset Formula $\\beta=2$\n\\end_inset\n\n.\n This model reduces to \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nfiber-pow-linear\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n when \n\\begin_inset Formula $\\alpha=0$\n\\end_inset\n\n.\n Alternatively,\n in the limit when \n\\begin_inset Formula $I_{0}=1$\n\\end_inset\n\n,\n the above parameters reduce to \n\\begin_inset Formula $\\xi=0$\n\\end_inset\n\n and \n\\begin_inset Formula $B=E/2$\n\\end_inset\n\n and the strain energy density takes the quadratic form \n\\begin_inset Formula \n\\[\n\\Psi_{n}=\\begin{cases}\n0 & I_{n}<1\\\\\n\\frac{E}{2}\\left(I_{n}^{1/2}-1\\right)^{2} & I_{n}>1\n\\end{cases}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{n}^{1/2}=\\lambda_{n}$\n\\end_inset\n\n is the stretch ratio along the fiber.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nFiber model as specified in a solid mixture (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout LyX-Code\n<solid type=\"fiber-exp-pow-linear\">\n\\end_layout\n\n\\begin_layout LyX-Code\n <E>1080</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n <alpha>1400</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n <beta>2.73</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n <lam0>1.012</lam0>\n\\end_layout\n\n\\begin_layout LyX-Code\n <fiber type=\"vector\">0,0,1</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solid>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber Exp-Linear\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-exp-linear\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This constitutive fiber model has an initial exponential rise and then grows linearly after a user-specified stretch transition point.\n This fiber material is based on the trans-iso Mooney-Rivlin model introduced in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss96\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This material by itself is not stable,\n so it is recommend to use it as part of a solid mixture.\n \n\\end_layout\n\n\\begin_layout Standard\nThe strain energy is as follows:\n \n\\begin_inset Formula \n\\[\nF_{2}\\left(\\lambda\\right)=\\begin{cases}\n0 & \\lambda<1\\\\\nC_{3}\\left(e^{-C_{4}}\\left(\\Ei\\left(C_{4}\\lambda\\right)-\\Ei\\left(C_{4}\\right)\\right)-\\ln\\lambda\\right) & 1\\le\\lambda<\\lambda_{m}\\\\\nC_{5}\\left(\\lambda-\\lambda_{m}\\right)+C_{6}\\ln\\frac{\\lambda}{\\lambda_{m}}+C_{3}\\left(e^{-C_{4}}\\Ei\\left(C_{4}\\lambda_{m}\\right)-\\Ei\\left(C_{4}\\right)-\\ln\\lambda_{m}\\right) & \\lambda_{m}\\le\\lambda\n\\end{cases}\\,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Ei\\left(\\cdot\\right)$\n\\end_inset\n\n is the exponential integral function.\n The resulting fiber stress is evaluated from \n\\begin_inset Formula \n\\[\n\\lambda\\frac{\\partial F_{2}}{\\partial\\lambda}=\\begin{cases}\n0 & \\tilde{\\lambda}\\leqslant1\\\\\nC_{3}\\left(e^{C_{4}\\left(\\lambda-1\\right)}-1\\right) & 1<\\tilde{\\lambda}<\\lambda_{m}\\\\\nC_{5}\\lambda+C_{6} & \\tilde{\\lambda}\\geqslant\\lambda_{m}\n\\end{cases}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n is the stretch at which the fibers are straightened,\n \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\nscales the exponential stresses,\n \n\\begin_inset Formula $C_{4}$\n\\end_inset\n\n is the rate of uncrimping of the fibers,\n and \n\\begin_inset Formula $C_{5}$\n\\end_inset\n\nis the modulus of the straightened fibers.\n \n\\begin_inset Formula $C_{6}$\n\\end_inset\n\n is determined from the requirement that the stress is continuous at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n (see below).\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stress coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c4>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber uncrimping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c5>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nModulus of straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhile this material enforces continuity of the strain energy density and stress at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n,\n it does not guarantee continuity of the elasticity.\n The continuity of the elasticity is only satisfied if the parameter \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\n satisfies\n\\begin_inset Formula \n\\[\nC_{3}=\\frac{C_{5}}{C_{4}}e^{-C_{4}\\left(\\lambda_{m}-1\\right)}\n\\]\n\n\\end_inset\n\nThe parameter \n\\begin_inset Formula $C_{6}$\n\\end_inset\n\n is chosen so that the stress defined above is continuous \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n and is determined by,\n\\begin_inset Formula \n\\[\nC_{6}=C_{3}\\left(e^{C_{4}\\left(\\lambda_{m}-1\\right)}-1\\right)-C_{5}\\lambda_{m}\n\\]\n\n\\end_inset\n\nTo use the form of \n\\begin_inset Formula $F_{2}$\n\\end_inset\n\n that satisfies continuity of the elasticity at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n the user may set \n\\begin_inset Formula $C_{3}=0$\n\\end_inset\n\n in the input file,\n and specify \n\\begin_inset Formula $C_{4}$\n\\end_inset\n\n and \n\\begin_inset Formula $C_{5}$\n\\end_inset\n\n as explained above.\n When \n\\begin_inset Formula $C_{3}=0$\n\\end_inset\n\n the code will automatically recalculate \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\n internally using the formula above.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1e-6</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-linear\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c3>0.0023</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c4>43</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c5>3</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <lambda>1.05</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber as Entropy Chain\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Entropy-Chain\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nfiber-entropy-chain\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n It was proposed by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shi22\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This fiber model is based on statistical mechanics to reflect the entropy-driven nature of a biological fiber.\n The model is derived from the freely-jointed-chain mechanism.\n Its strain energy is directly related to the entropic change of the chains in the material,\n given by\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi_{n}\\left(I_{n}\\right)=\\xi N\\left(\\sqrt{\\frac{I_{n}}{N}}\\beta+\\ln\\frac{\\beta}{\\sinh\\beta}\\right)-\\frac{\\xi\\sqrt{N}}{2}\\beta_{0}I_{n}-\\alpha_{00}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\noindent\nwhere \n\\begin_inset Formula $I_{n}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the square of the stretch ratio along the referential fiber direction \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n.\n The inverse Langevin equation relates the parameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n to \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $N$\n\\end_inset\n\n according to \n\\begin_inset Formula ${\\displaystyle \\beta=\\mathcal{L}^{-1}\\left[\\sqrt{\\frac{I_{n}}{N}}\\right]}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\beta_{0}$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n when \n\\begin_inset Formula $I_{n}=1$\n\\end_inset\n\n,\n and\n\\begin_inset Formula $\\alpha_{00}$\n\\end_inset\n\n is needed to produce a state of zero energy at \n\\begin_inset Formula $I_{n}=1$\n\\end_inset\n\n.\n The parameter \n\\begin_inset Formula $\\xi=nk\\Theta$\n\\end_inset\n\n is the initial fiber stiffness,\n with \n\\begin_inset Formula $n$\n\\end_inset\n\n,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Theta$\n\\end_inset\n\n respectively representing the number of chains per unit volume,\n Boltzmann's constant,\n and the absolute temperature.\n The parameter \n\\begin_inset Formula $N=\\zeta^{2}$\n\\end_inset\n\n is the number of chain segments,\n and \n\\begin_inset Formula $\\zeta$\n\\end_inset\n\n is the locking stretch,\n representing the extensibility of the fiber.\n The Langevin function is given by \n\\begin_inset Formula $\\mathcal{L}\\left(x\\right)=\\coth x-\\frac{1}{x}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen evaluating the inverse Langevin equation,\n a Taylor series expansion is applied for computational stability.\n The parameter \n\\begin_inset Formula $n_{\\mathrm{term}}$\n\\end_inset\n\n is used to control the number of terms used to evaluate the equation;\n it must be an integer between 3 and 30.\n\\end_layout\n\n\\begin_layout Standard\n\\noindent\nThe following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInitial modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<N>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSquare of locking stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n_term>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of Taylor series terms\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShear modulus for fiber-matrix interaction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Soft Tissue\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<solid type=\"Arruda-Boyce unconstrained\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <N>2</N>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <n_term>30</n_term>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <kappa>1</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n        </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <solid type=\"continuous fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n          <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n            <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n            <phi>0</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n          </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n          <fibers type=\"fiber-entropy-chain\">\n\\end_layout\n\n\\begin_layout LyX-Code\n            <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n            <N>2</N>\n\\end_layout\n\n\\begin_layout LyX-Code\n            <n_term>30</n_term>\n\\end_layout\n\n\\begin_layout LyX-Code\n          </fibers>\n\\end_layout\n\n\\begin_layout LyX-Code\n          <distribution type=\"von-Mises-3d\">\n\\end_layout\n\n\\begin_layout LyX-Code\n            <b>0.3</b>\n\\end_layout\n\n\\begin_layout LyX-Code\n          </distribution>\n\\end_layout\n\n\\begin_layout LyX-Code\n          <scheme type=\"fibers-3d-fei\">\n\\end_layout\n\n\\begin_layout LyX-Code\n            <resolution>196</resolution>\n\\end_layout\n\n\\begin_layout LyX-Code\n          </scheme>\n\\end_layout\n\n\\begin_layout LyX-Code\n        </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n      </material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUncoupled Fiber Models\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Fiber-Models\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSince fibers can only sustain tension,\n the materials listed here are not stable on their own.\n They must be combined with a stable material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n,\n within a <solid> tag.\n These fiber models may also be used in continuous fiber distribution models as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Continuous-Fiber\"\nnolink \"false\"\n\n\\end_inset\n\n,\n using a <fibers> tag.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Exponential-Power Law,\n Uncoupled Formulation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:fiber-exp-pow-uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a single fiber with an exponential-power law,\n in an uncoupled strain energy formulation,\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-exp-pow-uncoupled\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Since fibers can only sustain tension,\n this material is not stable on its own.\n It must be combined with a stable uncoupled material that acts as a ground matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\xi,$\n\\end_inset\n\n representing a measure of the fiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\alpha,$\n\\end_inset\n\n coefficient of exponential argument\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\beta,$\n\\end_inset\n\n power of exponential argument\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber is oriented along the unit vector \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal basis vectors representing the local element coordinate system when specified (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or else the global Cartesian coordinate system.\n The stress \n\\begin_inset Formula $\\tilde{\\boldsymbol{\\sigma}}$\n\\end_inset\n\n for this fibrous material is given by \n\\begin_inset Formula \n\\[\n\\tilde{\\boldsymbol{\\sigma}}=H\\left(\\tilde{I}_{n}-1\\right)\\frac{2\\tilde{I}_{n}}{J}\\frac{\\partial\\tilde{\\Psi}}{\\partial\\tilde{I}_{n}}\\mathbf{n}\\otimes\\mathbf{n},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{I}_{n}=\\tilde{\\lambda}_{n}^{2}=\\mathbf{n}_{r}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the square of the fiber stretch,\n \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{\\tilde{F}}\\cdot\\mathbf{n}_{r}/\\tilde{\\lambda}_{n}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $H\\left(.\\right)$\n\\end_inset\n\n is the unit step function that enforces the tension-only contribution..\n The fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}=\\frac{\\xi}{\\alpha\\beta}\\left(\\exp\\left[\\alpha\\left(\\tilde{I}_{n}-1\\right)^{\\beta}\\right]-1\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\xi>0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\beta\\geqslant2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nNote:\n In the limit when \n\\begin_inset Formula $\\alpha\\to0$\n\\end_inset\n\n,\n this expressions produces a power law,\n \n\\begin_inset Formula \n\\[\n\\lim\\limits_{\\alpha\\to0}\\tilde{\\Psi}=\\frac{\\xi}{\\beta}\\left(\\tilde{I}_{n}-1\\right)^{\\beta}.\n\\]\n\n\\end_inset\n\nNote:\n When \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n,\n the fiber modulus is zero at the strain origin (\n\\begin_inset Formula $\\tilde{I}_{n}=1)$\n\\end_inset\n\n.\n Therefore,\n use \n\\begin_inset Formula $\\beta>2$\n\\end_inset\n\n when a smooth transition in the stress is desired from compression to tension.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nSingle fiber oriented along \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n embedded in a Mooney-Rivlin ground matrix.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>10e3</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-pow-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n        <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nTwo fibers in the plane orthogonal to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n oriented at ±25 degrees relative to \n\\begin_inset Formula $\\mathbf{e}_{3}$\n\\end_inset\n\n,\n embedded in a Mooney-Rivlin ground matrix.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>10e3</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-pow-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n        <theta>90</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <phi>25</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiber-exp-pow-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ksi>5</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>3</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n        <theta>-90</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <phi>25</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber Kiousis Uncoupled\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fiber-Kiousus-Uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-Kiousis-uncoupled\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n It is based on the material model proposed by Kiousis et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Kiousis08,Kiousis09\"\nliteral \"true\"\n\n\\end_inset\n\n for modeling the balloon in a balloon-stent \n\\end_layout\n\n\\begin_layout Standard\n+system.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<d1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $d_{1},$\n\\end_inset\n\n represents a measure of the fiber modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<d2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $d_{2},$\n\\end_inset\n\n square of the stretch ratio when the fiber engages\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $n,$\n\\end_inset\n\n power exponent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}_{n}\\left(I_{n}\\right)=\\begin{cases}\n\\frac{d_{1}}{n}\\left(\\tilde{I}_{n}-d_{2}\\right)^{n} & \\tilde{I}_{n}\\ge d_{2}\\\\\n0 & \\tilde{I}_{n}<d_{2}\n\\end{cases}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{I}_{n}=\\tilde{\\lambda}_{n}^{2}=\\mathbf{n}_{r}\\cdot\\mathbf{\\tilde{C}}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the square of the fiber stretch and \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the unit vector along the fiber direction in its reference configuration.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nFiber model as specified in a continuous fiber distribution (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Continuous-Fiber\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout LyX-Code\n<fibers type=\"fiber-Kiousis-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <d1>500</d1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <d2>2.25</d2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <n>3</n>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fibers>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFiber with Toe-Linear Response,\n Uncoupled Formulation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:fiber-toe-linear-uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nfiber-pow-linear-uncoupled\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $E,$\n\\end_inset\n\n the fiber modulus in the linear range (\n\\begin_inset Formula $E\\geqslant0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\beta,$\n\\end_inset\n\n the power-law exponent in the toe region (\n\\begin_inset Formula $\\beta\\geqslant2)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lam0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\lambda_{0},$\n\\end_inset\n\n the stretch ratio when the toe region transitions to the linear region (\n\\begin_inset Formula $\\lambda_{0}>1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber strain energy density is given by \n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}_{n}\\left(\\tilde{I}_{n}\\right)=\\begin{cases}\n0 & \\tilde{I}_{n}<1\\\\\n\\frac{\\xi}{\\beta}\\left(\\tilde{I}_{n}-1\\right)^{\\beta} & 1\\leqslant\\tilde{I}_{n}\\leqslant I_{0}\\\\\nB\\left(\\tilde{I}_{n}-I_{0}\\right)-E\\left(\\tilde{I}_{n}^{1/2}-I_{0}^{1/2}\\right)+\\frac{\\xi}{\\beta}\\left(I_{0}-1\\right)^{\\beta} & I_{0}<\\tilde{I}_{n}\n\\end{cases}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $I_{0}=\\lambda_{0}^{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\n\\xi=\\frac{E}{4\\left(\\beta-1\\right)}I_{0}^{-3/2}\\left(I_{0}-1\\right)^{2-\\beta},\\,B=\\xi\\left(I_{0}-1\\right)^{\\beta-1}+\\frac{E}{2}I_{0}^{-1/2}\\,.\n\\]\n\n\\end_inset\n\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nFiber model as specified in a solid mixture (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout LyX-Code\n<solid type=\"fiber-pow-linear-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <theta>20</center>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>2.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <lam0>1.06</lam0>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solid>\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Fiber Exp-Linear\n\\end_layout\n\n\\begin_layout Standard\nThis constitutive fiber model has an initial exponential rise and then grows linearly after a user-specified stretch transition point.\n This fiber material is based on the trans-iso Mooney-Rivlin model introduced in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weiss96\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This material by itself is not stable,\n so it is recommend to use it as part of a solid mixture.\n \n\\end_layout\n\n\\begin_layout Standard\nThe strain energy is as follows:\n \n\\begin_inset Formula \n\\[\nF_{2}\\left(\\tilde{\\lambda}\\right)=\\begin{cases}\n0 & \\tilde{\\lambda}<1\\\\\nC_{3}\\left(e^{-C_{4}}\\left(\\Ei\\left(C_{4}\\tilde{\\lambda}\\right)-\\Ei\\left(C_{4}\\right)\\right)-\\ln\\tilde{\\lambda}\\right) & 1\\le\\tilde{\\lambda}<\\lambda_{m}\\\\\nC_{5}\\left(\\tilde{\\lambda}-\\lambda_{m}\\right)+C_{6}\\ln\\frac{\\tilde{\\lambda}}{\\lambda_{m}}+C_{3}\\left(e^{-C_{4}}\\Ei\\left(C_{4}\\lambda_{m}\\right)-\\Ei\\left(C_{4}\\right)-\\ln\\lambda_{m}\\right) & \\lambda_{m}\\le\\tilde{\\lambda}\n\\end{cases}\\,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Ei\\left(\\cdot\\right)$\n\\end_inset\n\n is the exponential integral function.\n The resulting fiber stress is evaluated from \n\\begin_inset Formula \n\\[\n\\tilde{\\lambda}\\frac{\\partial F_{2}}{\\partial\\tilde{\\lambda}}=\\begin{cases}\n0 & \\tilde{\\lambda}\\leqslant1\\\\\nC_{3}\\left(e^{C_{4}\\left(\\tilde{\\lambda}-1\\right)}-1\\right) & 1<\\tilde{\\lambda}<\\lambda_{m}\\\\\nC_{5}\\tilde{\\lambda}+C_{6} & \\tilde{\\lambda}\\geqslant\\lambda_{m}\n\\end{cases}\\,.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n is the stretch at which the fibers are straightened,\n \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\nscales the exponential stresses,\n \n\\begin_inset Formula $C_{4}$\n\\end_inset\n\n is the rate of uncrimping of the fibers,\n and \n\\begin_inset Formula $C_{5}$\n\\end_inset\n\nis the modulus of the straightened fibers.\n \n\\begin_inset Formula $C_{6}$\n\\end_inset\n\n is determined from the requirement that the stress is continuous at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n (see below).\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c3>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExponential stress coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c4>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber uncrimping coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c5>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nModulus of straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber stretch for straightened fibers\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fiber>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFiber distribution option\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhile this material enforces continuity of the strain energy density and stress at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n,\n it does not enforce continuity of the elasticity.\n To enforce continuity of all three parameters,\n we need to let\n\\begin_inset Formula \n\\[\nC_{3}=\\frac{C_{5}}{C_{4}}e^{-C_{4}\\left(\\lambda_{m}-1\\right)}\n\\]\n\n\\end_inset\n\nThe parameter \n\\begin_inset Formula $C_{6}$\n\\end_inset\n\n is chosen so that the stress defined above is continuous \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n and is determined by,\n\\begin_inset Formula \n\\[\nC_{6}=C_{3}\\left(e^{C_{4}\\left(\\lambda_{m}-1\\right)}-1\\right)-C_{5}\\lambda_{m}\n\\]\n\n\\end_inset\n\nTo enforce contintuity of the elasticity at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n the user may also set \n\\begin_inset Formula $C_{3}=0$\n\\end_inset\n\n in the input file,\n and specify \n\\begin_inset Formula $C_{4}$\n\\end_inset\n\n and \n\\begin_inset Formula $C_{5}$\n\\end_inset\n\n as explained above.\n When \n\\begin_inset Formula $C_{3}=0$\n\\end_inset\n\n the code will automatically recalculate \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\n internally.\n Using the above formula to calculate \n\\begin_inset Formula $C_{3}$\n\\end_inset\n\n manually for use in the first form can also enforce continuity of the elastic modulus at \n\\begin_inset Formula $\\lambda_{m}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>10</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>2.5e-07</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"uncoupled fiber-exp-linear\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c3>0.00234</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c4>43</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c5>3</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <lambda>1.05</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber type=\"vector\">1,0,0</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Fiber as Entropy Chain\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:UC-Fiber-Entropy-Chain\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material type is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nuncoupled fiber-entropy-chain\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n It was proposed by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shi22\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This fiber model is based on statistical mechanics to reflect the entropy-driven nature of a biological fiber.\n The model is derived from the freely-jointed-chain mechanism.\n Its strain energy is directly related to the entropic change of the chains in the material,\n given by\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi_{n}(\\tilde{I}_{n})=\\xi N\\left(\\sqrt{\\frac{\\tilde{I}_{n}}{N}}\\beta+\\text{ln}\\frac{\\beta}{\\sinh\\beta}\\right)-\\frac{\\xi\\sqrt{N}}{2}\\beta_{0}\\tilde{I}_{n}-\\alpha_{00}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\noindent\nwhere \n\\begin_inset Formula $\\tilde{I}_{n}=\\mathbf{n}_{r}\\cdot\\tilde{\\mathbf{C}}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the deviatoric measure of the stretch ratio along the referential fiber direction \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n.\n The inverse Langevin equation relates the parameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n to \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $N$\n\\end_inset\n\n according to \n\\begin_inset Formula ${\\displaystyle \\beta=\\mathcal{L}^{-1}\\left(\\sqrt{\\frac{I_{n}}{N}}\\right)}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\beta_{0}$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n when \n\\begin_inset Formula $I_{n}=1$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\alpha_{00}$\n\\end_inset\n\n is needed to produce a state of zero energy at \n\\begin_inset Formula $I_{n}=1$\n\\end_inset\n\n.\n The parameter \n\\begin_inset Formula $\\xi=nk\\Theta$\n\\end_inset\n\n is the initial fiber stiffness,\n with \n\\begin_inset Formula $n$\n\\end_inset\n\n,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Theta$\n\\end_inset\n\n respectively representing the number of chains per unit volume,\n Boltzmann's constant,\n and the absolute temperature.\n The parameter \n\\begin_inset Formula $N=\\zeta^{2}$\n\\end_inset\n\n is the number of chain segments,\n and \n\\begin_inset Formula $\\zeta$\n\\end_inset\n\n is the locking stretch,\n representing the extensibility of the fiber.\n The Langevin function is given by \n\\begin_inset Formula $\\mathcal{L}\\left(x\\right)=\\coth x-\\frac{1}{x}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen evaluating the inverse Langevin equation,\n a Taylor series expansion is applied for computational stability.\n The parameter \n\\begin_inset Formula $n_{\\mathrm{term}}$\n\\end_inset\n\n is used to control the number of terms used to evaluate the equation;\n it must be an integer between 3 and 30.\n\\end_layout\n\n\\begin_layout Standard\n\\noindent\nThe following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<ksi>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nInitial modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<N>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSquare of locking stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n_term>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of Taylor series terms\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Soft Tissue\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<k>1e4</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <c1>10.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<solid type=\"uncoupled fiber-entropy-chain\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <N>2.3</N>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <n_term>25</n_term>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <fiber type=\"vector\">0,0,1</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nContinuous Fiber Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Continuous-Fiber-Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA continuous fiber distribution has a strain energy density that integrates the contributions from fiber bundles oriented along all directions emanating from a point in the continuum,\n\\begin_inset Formula \n\\[\n\\Psi_{r}\\left(\\mathbf{C}\\right)=\\int_{A}H\\left(I_{n}-1\\right)R\\left(\\mathbf{n}_{r}\\right)\\Psi_{n}\\left(I_{n}\\right)\\,dA\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n is the unit vector along the fiber orientation in the reference configuration,\n \n\\begin_inset Formula $I_{n}=\\mathbf{n}_{r}\\cdot\\mathbf{C}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the normal component of \n\\begin_inset Formula $\\mathbf{C}$\n\\end_inset\n\n along \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n (also the square of the stretch ratio along that direction),\n and \n\\begin_inset Formula $A$\n\\end_inset\n\n represents the unit sphere (for 3D fiber distributions) or unit circle (for 2D fiber distributions) over which the integration is performed \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hou16\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Thus,\n \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n spans all directions from the origin to points on the unit sphere or unit circle.\n In the integrand,\n \n\\begin_inset Formula $\\Psi_{n}$\n\\end_inset\n\n represents the strain energy density of the fiber bundle oriented along \n\\begin_inset Formula $\\mathbf{n}_{r}$\n\\end_inset\n\n;\n \n\\begin_inset Formula $H\\left(\\cdot\\right)$\n\\end_inset\n\n is the Heaviside unit step function that includes only fibers that are in tension;\n and \n\\begin_inset Formula $R\\left(\\mathbf{n}_{r}\\right)$\n\\end_inset\n\n is the fiber density distribution function that specifies the spatial fractional distribution of fibers.\n This function satisfies the constraint\n\\begin_inset Formula \n\\[\n\\int_{A}R\\left(\\mathbf{n}_{r}\\right)\\,dA=1\\,.\n\\]\n\n\\end_inset\n\nFiber constitutive models may be taken from the list given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Fiber-Models\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor a material with an uncoupled strain energy density the corresponding expression is\n\\begin_inset Formula \n\\[\n\\tilde{\\Psi}_{r}\\left(\\tilde{\\mathbf{C}}\\right)=\\int_{A}H\\left(\\tilde{I}_{n}-1\\right)R\\left(\\mathbf{n}_{r}\\right)\\tilde{\\Psi}_{n}\\left(\\tilde{I}_{n}\\right)\\,dA\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{I}_{n}=\\mathbf{n}_{r}\\cdot\\tilde{\\mathbf{C}}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n.\n In this case,\n fiber constitutive models may be taken from the list given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Fiber-Models\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUnconstrained Continuous Fiber Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Unconstrained-Continuous-Fiber\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for an unconstrained continuous fiber distribution material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\ncontinuous fiber distribution\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fibers>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fiber material response \n\\begin_inset Formula $\\Psi_{n}\\left(I_{n}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<distribution>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fiber density distribution \n\\begin_inset Formula $R\\left(\\mathbf{n}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<scheme>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumerical integration scheme\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <fibers> tag encloses a description of the fiber constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Fiber-Models\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <distribution> tag encloses a description of the fiber density distribution function,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Distribution\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <scheme> tag specifies the numerical integration scheme,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Scheme\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"continuous fiber distribution\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fibers type=\"fiber-exponential-power-law\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <alpha>0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <beta>2</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fibers>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <distribution type=\"spherical\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    </distribution>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <scheme type=\"fibers-3d-gkt\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <nph>7</nph>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <nth>11</nth>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </scheme>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUncoupled Continuous Fiber Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Continuous-Fiber\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for an uncoupled continuous fiber distribution material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\ncontinuous fiber distribution uncoupled\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fibers>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fiber material response \n\\begin_inset Formula $\\tilde{\\Psi}_{n}\\left(\\tilde{I}_{n}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<distribution>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fiber density distribution \n\\begin_inset Formula $R\\left(\\mathbf{n}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<scheme>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumerical integration scheme\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <fibers> tag encloses a description of the fiber constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Fiber-Models\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <distribution> tag encloses a description of the fiber density distribution function,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Distribution\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <scheme> tag specifies the numerical integration scheme,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Scheme\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0.3</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"continuous fiber distribution uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fibers type=\"fiber-exponential-power-law-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <alpha>0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <beta>2</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fibers>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <distribution type=\"spherical\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    </distribution>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <scheme type=\"fibers-3d-gkt\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <nph>7</nph>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <nth>11</nth>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </scheme>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFiber ODF Constitutive Model\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nFiber ODF\n\\emph default\n constitutive model is a special version of the \n\\emph on\nContinuous Fiber Distribution\n\\emph default\n constitutive model,\n with its own distribution type,\n and a unique integration scheme.\n It utilizes a set of non-parametric orientation distribution functions which are each defined by a position in space,\n and a series of spherical harmonic coefficients.\n This allows for the definition of extremely detailed ODFs of any shape.\n\\end_layout\n\n\\begin_layout Standard\nThis constitutive model is intended to be used in conjunction with the \n\\emph on\nFiber ODF Analysis\n\\emph default\n tool in FEBio Studio,\n which directly analyzes 3D image data and generates ODFs based on fibers in the image.\n For more information on this tool and the algorithms involved please see the Fiber ODF Analysis section in the FEBio Studio User Manual,\n and the manuscripts associated with this tool \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"rauff_algorithmic_2024,rauff_nonparametric_2022\"\nliteral \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen an FE simulation is run using this material,\n some preprocessing is done on the ODFs in order to reduce the computational load during the simulation.\n The following is a description of these preprocessing steps.\n For a more detailed description,\n please see the associated manuscript \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"rauff_algorithmic_2024\"\nliteral \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nIn order to prevent sharp changes in the fiber direction between elements,\n a unique ODF is interpolated for each element in the mesh based on the element’s physical distance from the nearby ODFs in the discrete ODF field.\n This interpolation is done using a weighted-mean approach utilizing the objective distance metric afforded by the Fisher-Rao inner product space \n\\begin_inset CommandInset citation\nLatexCommand cite\nkey \"rauff_algorithmic_2024\"\nliteral \"false\"\n\n\\end_inset\n\n.\n This results in a unique ODF for each FE element in the domain of the constitutive model which smoothly transition between ODFs defined in each of the original image's subdivisions.\n If only a single ODF is defined for the domain (in other words,\n if the image was not subdivided before the analysis) then this step is skipped and the original ODF is used for all elements in the domain.\n\\end_layout\n\n\\begin_layout Standard\nConstitutive models using continuous fiber distributions require an efficient integration scheme over the unit sphere to compute stress contributions from the fiber family.\n Because there is no analytical representation of these non-parametric ODFs,\n this integration is performed by sampling the values of the ODFs at its defined points.\n To ensure faithful representation of the ODF,\n all ODF calculations up to this point in the process use a high resolution,\n pre-defined set evenly-spaced points on the unit sphere.\n Integrating the stress contributions across the ODFs at their full resolution proved to be prohibitively computationally expensive,\n as this integration must occur at every integration point in the FE mesh for each stress calculation in the analysis.\n\\end_layout\n\n\\begin_layout Standard\nTo address this issue,\n we developed a mechanism to reduce the number of sampling points on the ODF using an approach based on finite element remeshing technology.\n The gradient of each ODF is calculated,\n and a triangulated mesh of the ODF probability surface and its gradient is passed to the mmg remeshing library.\n The mmg algorithm remeshes a surface of triangular elements,\n adjusting the relative nodal density of the ODF mesh based on the magnitude of the gradient,\n resulting in a higher sampling density in areas of high curvature while reducing the overall number of sample points,\n thereby preserving sharp changes in orientation (Figure \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:fiberODFRmesh\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n \n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/fiberODFRemesh.png\n\twidth 100text%\n\n\\end_inset\n\n\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nSpherical representations of an ODF showing the results of the remeshing algorithm.\n (A) A full-resolution,\n spherical representation of an ODF.\n (B) An enlarged portion of the same ODF showing the area inside the black square in panel A.\n In this panel,\n the ODF’s mesh has been made visible to show the density of sampling points at the full-resolution of 40,962 points.\n (C) The remeshed surface of the same ODF showing the area inside the black square in panel A.\n The density of the ODF’s sampling points has been greatly reduced in the regions of the ODF where there is little variation in the ODF’s value,\n while maintaining high density in regions where there are sharp changes in value.\n This preserves the general shape of the ODF while significantly reducing the number of sampling points.\n \n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:fiberODFRmesh\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\nThe number of integration points in the remeshed ODFs varies depending on the ODF topology,\n but the number points is generally reduced by about two orders of magnitude.\n Since the resampling of the ODFs only takes place once,\n at the beginning of the FE analysis,\n the time required for the remeshing step is small relative to the overall time involved in a FE analysis.\n The reduction in points dramatically increases the speed of stress computations without significantly altering the results.\n After the interpolation and remeshing steps,\n the material initialization is complete,\n and the FE analysis continues normally.\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a fiber ODF distribution material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfiberODF\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fibers>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fiber material response \n\\begin_inset Formula $\\tilde{\\Psi}_{n}\\left(\\tilde{I}_{n}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fiber-odf>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA fiber-odf object specifying the position and spherical harmonic coefficients\n\\end_layout\n\n\\begin_layout Plain Layout\ndefining an ODF.\n This material may have any number of these objects defined.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <fibers> tag encloses a description of the fiber constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Fiber-Models\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The Fiber ODF material takes any number of \n\\emph on\n<\n\\emph default\nfiber-odf\n\\emph on\n>\n\\emph default\n tags as parameters,\n each with a position in space,\n and a list of spherical harmonic coefficients.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0.3</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"fiberODF\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fibers type=\"fiber-exponential-power-law-uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <alpha>0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <beta>2</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <ksi>1</ksi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fibers>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber-odf>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <shp_harmonics>8.61001e-05,6.31376e-05,\n etc...</shp_harmonics>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <position>340,300,224</position>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fiber-odf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fiber-odf>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <shp_harmonics>-3.28478e-05,3.90659e-06,\n etc...</shp_harmonics>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <position>400,300,224</position>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fiber-odf>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsection\nDistribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA fiber density distribution function is needed in the specification of a continuous fiber distribution.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSpherical\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Spherical\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber density distribution type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nspherical\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n models an isotropic 3D distribution.\n This distribution corresponds to \n\\begin_inset Formula \n\\[\nR\\left(\\mathbf{n}\\right)=\\frac{1}{4\\pi}\\,.\n\\]\n\n\\end_inset\n\nIt requires no additional parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigSphericalDistribution.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<distribution type=\"spherical\">\n\\end_layout\n\n\\begin_layout LyX-Code\n</distribution>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nEllipsoidal\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ellipsoidal\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber density distribution type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nellipsoidal\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n models a generally orthotropic 3D distribution.\n It corresponds to \n\\begin_inset Formula \n\\[\nR\\left(\\mathbf{n}\\right)=C^{-1}\\left[\\left(\\frac{n_{1}}{a}\\right)^{2}+\\left(\\frac{n_{2}}{b}\\right)^{2}+\\left(\\frac{n_{3}}{c}\\right)^{2}\\right]^{-1/2}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left(n_{1},n_{2},n_{3}\\right)$\n\\end_inset\n\n are the components of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n in the local Cartesian basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n of each element,\n when specified (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n) or the global basis by default;\n and \n\\begin_inset Formula $C$\n\\end_inset\n\n is calculated to satisfy the integration constraint on \n\\begin_inset Formula $R\\left(\\mathbf{n}\\right)$\n\\end_inset\n\n.\n The parameters \n\\begin_inset Formula $\\left(a,b,c\\right)$\n\\end_inset\n\n represents the semi-principal axes of the ellipsoid and must be positive.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<spa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe semi-principal axes \n\\begin_inset Formula $\\left(a,b,c\\right)$\n\\end_inset\n\n of the ellipsoid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe value of \n\\begin_inset Formula $C$\n\\end_inset\n\n is automatically adjusted to account for the values of the semi-principal axes \n\\begin_inset Formula $\\left(a,b,c\\right)$\n\\end_inset\n\n.\n Therefore,\n only the relative ratios of these parameters matter.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigEllipsoidalDistribution.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<distribution type=\"ellipsoidal\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <spa>3,2,1</spa>\n\\end_layout\n\n\\begin_layout LyX-Code\n</distribution>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\n\\begin_inset Formula $\\pi-$\n\\end_inset\n\nPeriodic von Mises Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Periodic-von-Mises\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber density distribution type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nvon-Mises-3d\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n models a transversely isotropic 3D distribution \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Gasser06\"\nliteral \"true\"\n\n\\end_inset\n\n.\n It corresponds to \n\\begin_inset Formula \n\\[\nR\\left(\\mathbf{n}\\right)=\\frac{1}{\\pi}\\sqrt{\\frac{b}{2\\pi}}\\frac{\\exp\\left(2bn_{1}^{2}\\right)}{\\mbox{erfi}\\left(\\sqrt{2b}\\right)}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left(n_{1},n_{2},n_{3}\\right)$\n\\end_inset\n\n are the components of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n in the local Cartesian basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n of each element,\n when specified (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n) or the global basis by default;\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n is the concentration parameter (\n\\begin_inset Formula $b>0)$\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<b>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe concentration parameter \n\\begin_inset Formula $b$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigPiPeriodicVonMisesDistribution.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<distribution type=\"von-Mises-3d\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <b>0.5</b>\n\\end_layout\n\n\\begin_layout LyX-Code\n</distribution>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCircular\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Circular\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber density distribution type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ncircular\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n models a transversely isotropic 2D distribution.\n This distribution corresponds to \n\\begin_inset Formula \n\\[\nR\\left(\\mathbf{n}\\right)=\\frac{1}{2\\pi}\\,.\n\\]\n\n\\end_inset\n\nIt requires no additional parameters.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<distribution type=\"circular\">\n\\end_layout\n\n\\begin_layout LyX-Code\n</distribution>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nElliptical\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Elliptical\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber density distribution type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nelliptical\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n models an orthotropic 2D distribution.\n This distribution corresponds to \n\\begin_inset Formula \n\\[\nR\\left(\\mathbf{n}\\right)=C^{-1}\\left[\\left(\\frac{n_{1}}{a}\\right)^{2}+\\left(\\frac{n_{2}}{b}\\right)^{2}\\right]^{-1/2}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left(n_{1},n_{2},n_{3}\\right)$\n\\end_inset\n\n are the components of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n in the local Cartesian basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n of each element,\n when specified (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n) or the global basis by default,\n implying that the elliptical distribution lies in the \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2}\\right\\} $\n\\end_inset\n\n plane;\n and \n\\begin_inset Formula $\\left(a,b\\right)$\n\\end_inset\n\n are the semi-principal axes of the ellipse.\n Here,\n \n\\begin_inset Formula $C=4bK\\left(e\\right)$\n\\end_inset\n\n where \n\\begin_inset Formula $K$\n\\end_inset\n\n is the complete elliptic integral of the first kind and \n\\begin_inset Formula \n\\[\ne=\\sqrt{1-\\frac{b^{2}}{a^{2}}}\n\\]\n\n\\end_inset\n\nis the ellipse eccentricity.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<spa1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe semi-principal axis \n\\begin_inset Formula $a$\n\\end_inset\n\n of the ellipse\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<spa2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe semi-principal axis \n\\begin_inset Formula $b$\n\\end_inset\n\n of the ellipse\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigEllipticalDistribution.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<distribution type=\"elliptical\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <spa1>8</spa1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <spa2>1</spa2>\n\\end_layout\n\n\\begin_layout LyX-Code\n</distribution>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nvon Mises Distribution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:von-Mises-Distribution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fiber density distribution type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nvon-Mises-2d\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n models an orthotropic 2D distribution.\n This distribution corresponds to \n\\begin_inset Formula \n\\[\nR\\left(\\mathbf{n}\\right)=\\frac{\\exp\\left[b\\left(2n_{1}^{2}-1\\right)\\right]}{2\\pi I_{0}\\left(b\\right)}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\left(n_{1},n_{2},n_{3}\\right)$\n\\end_inset\n\n are the components of \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n in the local Cartesian basis \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n of each element,\n when specified (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n) or the global basis by default,\n with the distribution lying in the \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2}\\right\\} $\n\\end_inset\n\n plane;\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n is the concentration parameter (\n\\begin_inset Formula $b>0)$\n\\end_inset\n\n.\n \n\\begin_inset Formula $I_{0}$\n\\end_inset\n\n is the modified Bessel function of the first kind of order 0.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<b>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe concentration parameter \n\\begin_inset Formula $b$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigVonMisesDistribution.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<distribution type=\"von-Mises-2d\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <b>3</b>\n\\end_layout\n\n\\begin_layout LyX-Code\n</distribution>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nScheme\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Scheme\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA numerical integration scheme is needed in the specification of a continuous fiber distribution to perform the integration over the unit sphere (3D) or the unit circle (2D).\n Use the uncoupled version of the scheme when modeling an uncoupled continuous fiber distribution.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nGauss-Kronrod Trapezoidal Rule\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Gauss-Kronrod-Trapezoidal-Rule\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe scheme type for the Gauss-Kronrod trapezoidal rule is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfibers-3d-gkt\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n for unconstrained and uncoupled continuous fiber distributions.\n This integration rule should only be used with 3D fiber density distributions.\n This scheme automatically limits the range of integration to fibers that are in tension \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Hou16\"\nliteral \"true\"\n\n\\end_inset\n\n.\n A Gauss-Kronrod quadrature rule is employed for integration across latitudes of the unit sphere.\n A trapezoidal rule is used for integration across longitudes.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<nph>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of integration points across latitudes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<nth>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of integration points across longitudes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameter <nph> must be one of the values 7,\n 11,\n 15,\n 19,\n 23 and 27.\n The parameter <nth> may be any number greater than 0.\n Odd values for <nth> produce more accurate results than even values.\n A recommended combination is nph=7 and nth=31.\n The total number of integration points is \n\\begin_inset Formula $N=$\n\\end_inset\n\nnph ×nth.\n Increasing values of \n\\begin_inset Formula $N$\n\\end_inset\n\n require increasing computational time.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<scheme type=\"fibers-3d-gkt\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <nph>7</nph>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <nth>31</nth>\n\\end_layout\n\n\\begin_layout LyX-Code\n</scheme>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nFinite Element Integration Rule\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Finite-Element-Integration\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe scheme type for the finite element integration rule is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfibers-3d-fei\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n for unconstrained and uncoupled continuous fiber distributions.\n This integration rule should only be used with 3D fiber density distributions.\n This scheme discretizes the unit sphere into a set of \n\\begin_inset Formula $N$\n\\end_inset\n\n spherical triangles of nearly identical surface areas.\n The unit normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n passes through the centroid of each surface element.\n The integration is performed as a summation over \n\\begin_inset Formula $N$\n\\end_inset\n\n.\n For each direction \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n the stress is evaluated only if the fiber bundle is in tension along that direction.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<resolution>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe number of integration points \n\\begin_inset Formula $N$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameter <resolution> must be one of the values 20,\n 34,\n 60,\n 74,\n 196,\n 210,\n 396,\n 410,\n 596,\n 610,\n 796,\n 810,\n 996,\n 1010,\n 1196,\n 1210,\n 1396,\n 1410,\n 1596,\n 1610,\n and 1796.\n A conservative choice for producing accurate results under general loading conditions is \n\\begin_inset Formula $N=$\n\\end_inset\n\n1610.\n Increasing values of \n\\begin_inset Formula $N$\n\\end_inset\n\n require increasing computational time.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigFiniteElementIntegration.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample \n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<scheme type=\"fibers-3d-fei\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <resolution>1610</resolution>\n\\end_layout\n\n\\begin_layout LyX-Code\n</scheme>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nTrapezoidal Rule\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Trapezoidal-Rule\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe scheme type for the trapezoidal integration rule is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfibers-2d-trapezoidal\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n for unconstrained and uncoupled continuous fiber distributions.\n This integration rule should only be used with 2D fiber density distributions.\n This scheme discretizes the unit circle into a set of \n\\begin_inset Formula $N$\n\\end_inset\n\n circular arcs of identical lengths.\n The unit normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n passes through the centroid of each arc element.\n The integration is performed as a summation over \n\\begin_inset Formula $N$\n\\end_inset\n\n.\n For each direction \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n the stress is evaluated only if the fiber bundle is in tension along that direction.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<nth>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of integration points \n\\begin_inset Formula $N$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameter <nth> may be any number greater than 0.\n Odd values for <nth> produce more accurate results than even values.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<scheme type=\"fibers-2d-trapezoidal\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <nth>31</nth>\n\\end_layout\n\n\\begin_layout LyX-Code\n</scheme>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nViscoelastic Solids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Viscoelastic-Solids\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nUncoupled Viscoelastic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Viscoelastic-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThese materials produce a viscoelastic response only for the deviatoric stress response.\n They must be used whenever the elastic response is uncoupled,\n as in the materials described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe material type for these materials is \n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled viscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nunits\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nviscoelastic coefficient \n\\begin_inset Formula $\\gamma_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t1>-<t6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelaxation times\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g1>-<g6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nviscoelastic coefficients\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic component (must be an uncoupled elastic solid)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{S}\\left(t\\right)=\\int\\limits_{-\\infty}^{t}G\\left(t-s\\right)\\frac{d\\mathbf{S}^{e}}{ds}\\,ds\n\\]\n\n\\end_inset\n\nFor a uncoupled viscoelastic material,\n the second Piola Kirchhoff stress can be written as follows \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso98\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\mathbf{S}\\left(t\\right)=pJ\\,\\mathbf{C}^{-1}+J^{-2/3}\\int\\limits_{-\\infty}^{t}G\\left(t-s\\right)\\frac{d\\left(\\Dev\\left[\\mathbf{\\tilde{S}}^{e}\\right]\\right)}{ds}\\,ds\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{\\tilde{S}}^{e}$\n\\end_inset\n\n is the elastic stress derived from \n\\begin_inset Formula $\\tilde{W}\\left(\\mathbf{\\tilde{C}}\\right)$\n\\end_inset\n\n (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n) and \n\\begin_inset Formula $G$\n\\end_inset\n\n is the relaxation function.\n It is assumed that the relaxation function is given by the following discrete relaxation spectrum:\n \n\\begin_inset Formula \n\\[\nG\\left(t\\right)=\\gamma_{0}+\\sum\\limits_{i=1}^{N}\\gamma_{i}\\exp\\left(-t/\\tau_{i}\\right),\n\\]\n\n\\end_inset\n\nNote that the user does not have to enter all the \n\\begin_inset Formula $\\tau_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma_{i}$\n\\end_inset\n\n coefficients.\n Instead,\n only the values that are used need to be entered.\n So,\n if \n\\begin_inset Formula $N$\n\\end_inset\n\n is 2,\n only \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tau_{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\gamma_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma_{2}$\n\\end_inset\n\nhave to be entered.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nelastic \n\\shape default\nparameter describes the elastic material as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material 1\" type=\"uncoupled viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <g1>0.95</g1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <t1>0.01</t1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0.0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsection\nUnconstrained Viscoelastic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Unconstrained-Viscoelastic-Materi\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for viscoelastic materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nviscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault value\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nunits\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nviscoelastic coefficient \n\\begin_inset Formula $\\gamma_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t1>-<t6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelaxation times\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g1>-<g6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nviscoelastic coefficients\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelastic component (must be an unconstrained elastic solid)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor a viscoelastic material,\n the second Piola Kirchhoff stress can be written as follows \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Puso98\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\mathbf{S}\\left(t\\right)=\\int\\limits_{-\\infty}^{t}G\\left(t-s\\right)\\frac{d\\mathbf{S}^{e}}{ds}\\,ds\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{S}^{e}$\n\\end_inset\n\n is the elastic stress and \n\\begin_inset Formula $G$\n\\end_inset\n\n is the relaxation function.\n It is assumed that the relaxation function is given by the following discrete relaxation spectrum:\n \n\\begin_inset Formula \n\\[\nG\\left(t\\right)=\\gamma_{0}+\\sum\\limits_{i=1}^{N}\\gamma_{i}\\exp\\left(-t/\\tau_{i}\\right)\\,,\n\\]\n\n\\end_inset\n\nNote that the user does not have to enter all the \n\\begin_inset Formula $\\tau_{i}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma_{i}$\n\\end_inset\n\n coefficients.\n Instead,\n only the values that are used need to be entered.\n So,\n if \n\\begin_inset Formula $N$\n\\end_inset\n\n is 2,\n only \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\tau_{2}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\gamma_{1}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma_{2}$\n\\end_inset\n\nhave to be entered.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nelastic \n\\shape default\nparameter describes the elastic material as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material 1\" type=\"viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <g1>0.95</g1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <t1>0.01</t1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Viscoelastic Solid\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Viscoelastic-Solid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReactive viscoelasticity models a material as a mixture of strong bonds,\n which are permanent,\n and weak bonds,\n which break and reform in response to loading \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian15\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Strong bonds produce the equilibrium elastic response,\n whereas weak bonds produce the transient viscous response.\n For a compressive reactive viscoelastic solid,\n the strain energy density is given by \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{F}\\right)=\\Psi_{r}^{e}\\left(\\mathbf{F}\\right)+\\sum\\limits_{u}w^{u}\\Psi_{0}^{b}\\left(\\mathbf{F}^{u}\\right)\\label{eq:rvs-sed}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Psi_{r}^{e}$\n\\end_inset\n\n is the strain energy density of strong bonds and \n\\begin_inset Formula $\\Psi_{0}^{b}$\n\\end_inset\n\n is the strain energy density of weak bonds,\n when they all belong to the same generation.\n \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient of the strong bonds and the initial weak bond generation,\n wherease \n\\begin_inset Formula $\\mathbf{F}^{u}$\n\\end_inset\n\n is the relative deformation gradient for the \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration weak bonds,\n such that \n\\begin_inset Formula $\\mathbf{F}^{u}\\left(u\\right)=\\mathbf{R}\\left(u\\right)$\n\\end_inset\n\n (the rotation tensor in the polar decomposition of \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n) at time \n\\begin_inset Formula $u$\n\\end_inset\n\n,\n thus \n\\begin_inset Formula $\\mathbf{F}^{u}\\left(t\\right)=\\mathbf{F}\\left(t\\right)\\cdot\\mathbf{U}^{-1}\\left(u\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{U}$\n\\end_inset\n\n is the right-stretch tensor of \n\\begin_inset Formula $\\mathbf{F}=\\mathbf{R}\\cdot\\mathbf{U}$\n\\end_inset\n\n.\n In this expression,\n \n\\begin_inset Formula $w^{u}\\left(\\mathbf{X},t\\right)$\n\\end_inset\n\n is the mass fraction of \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration weak bonds,\n which evolves over time as described next.\n The summation is taken over all generations \n\\begin_inset Formula $u$\n\\end_inset\n\n that were created prior to the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nAny number of valid solutions may exist for \n\\begin_inset Formula $w^{u}$\n\\end_inset\n\n,\n based on constitutive assumptions for the weak bond mass fraction supply \n\\begin_inset Formula $\\hat{w}^{u}$\n\\end_inset\n\n.\n In particular,\n for \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration bonds reforming in an unloaded state during the time interval \n\\begin_inset Formula $u\\leqslant t<v$\n\\end_inset\n\n,\n and subsequently breaking in response to loading at \n\\begin_inset Formula $t=v$\n\\end_inset\n\n,\n Type I bond kinetics provides a solution of the form \n\\begin_inset Formula \n\\begin{equation}\nw^{u}\\left(\\mathbf{X},t\\right)=\\begin{cases}\n0 & t<u\\\\\nf^{u}\\left(\\mathbf{X},t\\right) & u\\leqslant t<v\\\\\nf^{u}\\left(\\mathbf{X},v\\right)g\\left(\\mathbf{F}\\left(v\\right);\\mathbf{X},t-v\\right) & v\\leqslant t\n\\end{cases}\\label{eq:rvs-mass-fractions}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\begin{equation}\nf^{u}\\left(\\mathbf{X},t\\right)=1-\\sum\\limits_{\\gamma<u}w^{\\gamma}\\left(\\mathbf{X},t\\right)\\label{eq:rvs-reformed-bond-fraction}\n\\end{equation}\n\n\\end_inset\n\nand \n\\begin_inset Formula $g\\left(\\mathbf{F}\\left(v\\right);\\mathbf{X},t-v\\right)$\n\\end_inset\n\n is a reduced relaxation function which may assume any number of valid forms.\n (A reduced relaxation function \n\\begin_inset Formula $g\\left(t\\right)$\n\\end_inset\n\n satisfies \n\\begin_inset Formula $g\\left(0\\right)=1$\n\\end_inset\n\n and \n\\begin_inset Formula $g\\left(t\\to\\infty\\right)=0$\n\\end_inset\n\n,\n and decreases monotonically with \n\\begin_inset Formula $t$\n\\end_inset\n\n.) In particular,\n \n\\begin_inset Formula $g$\n\\end_inset\n\n may depend on the strain at time \n\\begin_inset Formula $v$\n\\end_inset\n\n relative to the reference configuration of the \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration.\n In the recursive expression above,\n the earliest generation \n\\begin_inset Formula $u=-\\infty$\n\\end_inset\n\n,\n which is initially at rest,\n produces \n\\begin_inset Formula $w^{u}\\left(t\\right)=1$\n\\end_inset\n\n for \n\\begin_inset Formula $t<v$\n\\end_inset\n\n and \n\\begin_inset Formula $w^{u}\\left(t\\right)=g\\left(\\mathbf{F}^{u}\\left(v\\right);\\mathbf{X},t-v\\right)$\n\\end_inset\n\n for \n\\begin_inset Formula $t\\geqslant v$\n\\end_inset\n\n;\n this latter expression seeds the recursion for subsequent generations.\n Therefore,\n providing a functional form for \n\\begin_inset Formula $g$\n\\end_inset\n\n suffices to produce the solution for all bond generations \n\\begin_inset Formula $u$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor Type II bond kinetics,\n the solution for the mass fractions is given by \n\\begin_inset Formula \n\\begin{equation}\nw^{u}\\left(t\\right)=\\begin{cases}\n0 & t<u\\\\\n1-g\\left(t-u\\right) & u\\leqslant t<v\\\\\ng\\left(t-v\\right)-g\\left(t-u\\right) & v\\leqslant t\n\\end{cases}\\,.\\label{eq:rvs-mass-fractions-II}\n\\end{equation}\n\n\\end_inset\n\nFor this type of bond kinetics,\n the reduced relaxation function \n\\begin_inset Formula $g$\n\\end_inset\n\n cannot depend on the magnitude of the strain,\n because strain-dependence might violate the constraint \n\\begin_inset Formula $0\\leqslant w^{u}\\leqslant1$\n\\end_inset\n\n.\n Thus,\n type II bond kinetics is only valid for quasilinear viscoelasticity,\n whereas type I bond kinetics also encompasses nonlinear viscoelasticity.\n\\end_layout\n\n\\begin_layout Standard\nFor all bond kinetics,\n it is also possible to constrain the occurrence of the breaking-and-reforming reaction to specific forms of the strain.\n For example,\n the reaction may be allowed to proceed only in the case of dilatational strain,\n or only in the case of distortional strain.\n\\end_layout\n\n\\begin_layout Standard\nFor a material with an uncoupled formulation,\n the strain energy density has the form \n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{F}\\right)=U\\left(J\\right)+\\tilde{\\Psi}_{r}^{e}\\left(\\mathbf{\\tilde{F}}\\right)+\\sum\\limits_{u}w^{u}\\tilde{\\Psi}_{0}^{b}\\left(\\mathbf{\\tilde{F}}^{u}\\right)\\label{eq:rvs-sed-uncoupled}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{\\tilde{F}}=J^{-1/3}\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a compressive reactive viscoelastic solid is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive viscoelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n For the uncoupled formulation the material type is \n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled reactive viscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"middle\" width=\"40page%\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kinetics>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multirow=\"3\" alignment=\"left\" valignment=\"middle\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBond kinetics type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<trigger>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain invariants that trigger weak bond breakage and reformation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<wmin>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBond mass fraction threshold \n\\begin_inset Formula $w_{\\min}$\n\\end_inset\n\n for triggering new generation or merging and culling oldest generation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<emin>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain threshold \n\\begin_inset Formula $e_{\\min}$\n\\end_inset\n\n for triggering new generation (\n\\begin_inset Formula $e_{\\min}\\ge0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElastic (strong bond) material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<bond>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nWeak bond material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<relaxation>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReduced relaxation function\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<recruitment>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nWeak bond recruitment function\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <kinetics> parameter should be set to 1 for Type I bond kinetics or 2 for Type II bond kinetics.\n The <trigger> parameter should be set 0 when weak bonds break and reform in response to any form of the strain;\n it should be set to 1 when the trigger is distortional strain;\n and it should be set to 2 when the trigger is dilatational strain,\n\\begin_inset Formula \n\\begin{equation}\n\\text{trigger}=\\begin{cases}\n0\\text{ for any strain} & \\left\\Vert \\Delta\\mathbf{E}\\right\\Vert \\\\\n1\\text{ for distortional strain} & \\left\\Vert \\dev\\Delta\\boldsymbol{\\eta}\\right\\Vert \\\\\n2\\text{ for dilatational strain} & \\left|\\ln\\left(\\det\\Delta\\mathbf{F}\\right)\\right|\n\\end{cases}\\label{eq:rvs-trigger}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Delta\\mathbf{E}=\\frac{1}{2}\\left(\\Delta\\mathbf{F}^{T}\\cdot\\Delta\\mathbf{F}-\\mathbf{I}\\right)$\n\\end_inset\n\n is the incremental Lagrange strain,\n \n\\begin_inset Formula $\\Delta\\boldsymbol{\\eta}=\\ln\\Delta\\mathbf{V}$\n\\end_inset\n\n is the incremental natural strain,\n where \n\\begin_inset Formula $\\Delta\\mathbf{V}$\n\\end_inset\n\n is the left stretch tensor of \n\\begin_inset Formula $\\Delta\\mathbf{F}$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $\\Delta\\mathbf{F}=\\mathbf{F}\\left(t_{n+1}\\right)\\cdot\\mathbf{F}^{-1}\\left(t^{m}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $t_{n+1}$\n\\end_inset\n\n is the current time and \n\\begin_inset Formula $t^{m}$\n\\end_inset\n\n is the birth time of the most recent weak bond generation.\n\\end_layout\n\n\\begin_layout Standard\nThe <elastic> and <bond> materials may be selected from the list of unconstrained elastic materials given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n (for \n\\begin_inset Quotes eld\n\\end_inset\n\nreactive viscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n) or from the list of uncoupled elastic materials in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n (for \n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled reactive viscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n).\n The <relaxation> material may be selected from the list of reduced relaxation functions provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Relaxation-Functions\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe <recruitment> material may be selected from the list of weak bond recruitment functions \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Cumulative-Distribution-Function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This optional material specification can be used to model nonlinear viscoelasticity where the weak bond response becomes more significant with increasing strain,\n substituting eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rvs-sed\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n with\n\\begin_inset Formula \n\\begin{equation}\n\\Psi_{r}\\left(\\mathbf{F}\\right)=\\Psi_{r}^{e}\\left(\\mathbf{F}\\right)+\\sum\\limits_{u}w^{u}F\\left(\\Xi\\left(u\\right)\\right)\\Psi_{0}^{b}\\left(\\mathbf{F}^{u}\\right)\\,.\\label{eq:rvs-reformed-with-recruitment}\n\\end{equation}\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n is a measure of the current state of strain,\n similar to the measures shown in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rvs-trigger\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Thus,\n \n\\begin_inset Formula $\\Xi=\\left\\Vert \\mathbf{E}\\right\\Vert $\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\Xi=\\left\\Vert \\dev\\boldsymbol{\\eta}\\right\\Vert $\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\Xi=\\left|\\ln\\left(\\det\\mathbf{F}\\right)\\right|$\n\\end_inset\n\n,\n based on the choice of the <trigger> parameter.\n In effect,\n \n\\begin_inset Formula $F\\left(\\Xi\\left(u\\right)\\right)$\n\\end_inset\n\n enhances the weak bond response of generation \n\\begin_inset Formula $u$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe parameters <wmin> and <emin> can be adjusted to improve computational efficiency by reducing the number of generations \n\\begin_inset Formula $u$\n\\end_inset\n\n in an analysis.\n In practice,\n the value of <emin> should be no greater than one-hundredth of the equilibrium strain in a loading configuration.\n The parameter <wmin> must be in the range \n\\begin_inset Formula $0\\le w_{\\min}\\le1$\n\\end_inset\n\n.\n In practice,\n a good balance between accuracy and efficiency is achieved by using \n\\begin_inset Formula $w_{\\min}\\le0.05$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"RV solid\" type=\"reactive viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kinetics>1</kinetics>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <trigger>0</trigger>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <wmin>0.05</wmin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <emin>0.001</emin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>0.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <bond type=\"Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>0.5</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </bond>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relaxation type=\"relaxation-exponential\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <tau>4</tau>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </relaxation>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <recruitment type=\"recruitment power\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mu0>1</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mu1>30</mu1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>2</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <scale>1</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsection\nRelaxation Functions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Relaxation-Functions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Exponential\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-exponential\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(t\\right)=e^{-t/\\tau}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential Distortional\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Exp-Distortional\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-exp-distortion\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(\\mathbf{F}\\left(v\\right);t-v\\right)=e^{-\\left(t-v\\right)/\\tau\\left[K_{2}\\left(v\\right)\\right]}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\tau\\left(K_{2}\\left(v\\right)\\right)=\\tau_{0}+\\tau_{1}\\cdot\\left(K_{2}\\left(v\\right)\\right)^{\\alpha}\n\\]\n\n\\end_inset\n\nIn general,\n \n\\begin_inset Formula $K_{2}=\\left|\\dev\\boldsymbol{\\eta}\\right|$\n\\end_inset\n\n where \n\\begin_inset Formula $\\boldsymbol{\\eta}=\\ln\\mathbf{V}$\n\\end_inset\n\n is the spatial natural (left Hencky) strain tensor and \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is the left stretch tensor.\n \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n is evaluated at the time \n\\begin_inset Formula $v$\n\\end_inset\n\n when weak bonds from the \n\\begin_inset Formula $u-$\n\\end_inset\n\ngeneration start breaking.\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential Distortional User-Specified\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Exp-Dist-User\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-exp-dist-user\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(\\mathbf{F}\\left(v\\right);t-v\\right)=e^{-\\left(t-v\\right)/\\tau\\left[K_{2}\\left(v\\right)\\right]}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tau\\left(K_{2}\\left(v\\right)\\right)$\n\\end_inset\n\n is given as a mathematical expression (type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath\n\\begin_inset Quotes qrd\n\\end_inset\n\n),\n or as a loadcurve (type=\n\\begin_inset Quotes qrd\n\\end_inset\n\npoint\n\\begin_inset Quotes qrd\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"RVE\" type=\"reactive viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kinetics>1</kinetics>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <trigger>1</trigger>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>0.13</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <bond type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>0.52</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </bond>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relaxation type=\"relaxation-exp-dist-user\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <tau type=\"math\">1.0+2.0*(K2^0.5)</tau>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </relaxation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"RVE UC\" type=\"uncoupled reactive viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kinetics>1</kinetics>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <trigger>1</trigger>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>25</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>0.025</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <bond type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>0.025</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </bond>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relaxation type=\"relaxation-exp-dist-user\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <tau type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <interpolate>SMOOTH</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <point>0.00,\n 1.00</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <point>0.25,\n 2.00</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <point>0.50,\n 2.41</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <point>0.75,\n 2.73</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <point>1.00,\n 3.00</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </tau>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </relaxation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential Continuous Spectrum\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CSexp\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis relaxation function is derived from a continuous exponential relaxation spectrum,\n see \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n The material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-CSexp\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameter needs to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis parameter must satisfy \n\\begin_inset Formula $\\tau>0$\n\\end_inset\n\n.\n The reduced relaxation function for this material type is given by\n\\begin_inset Formula \n\\[\ng\\left(t\\right)=2\\sqrt{\\frac{t}{\\tau}}K_{1}\\left(2\\sqrt{\\frac{t}{\\tau}}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K_{1}\\left(z\\right)$\n\\end_inset\n\n is the modified Bessel function of the second kind,\n of order 1.\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential Continuous Spectrum Distortional User-Specified\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CSexp-dist-user\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:CSexp\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for the description of this relaxation function.\n When the material parameters vary with the distortional strain \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n,\n the material type is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-CSexp-dist-user\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameter needs to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe definition of \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for examples of how to specify these functions.\n\\end_layout\n\n\\begin_layout Subsubsection\nFung\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fung\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis relaxation function,\n which is derived from a continuous relaxation spectrum,\n was introduced by Fung \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung81\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Fung\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{2}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThese parameters must satisfy \n\\begin_inset Formula $\\tau_{2}>\\tau_{1}$\n\\end_inset\n\n.\n The reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(t\\right)=\\frac{\\tau_{2}e^{-t/\\tau_{2}}-\\tau_{1}e^{-t/\\tau_{1}}+t\\left[\\Ei\\left(-\\frac{t}{\\tau_{2}}\\right)-\\Ei\\left(-\\frac{t}{\\tau_{1}}\\right)\\right]}{\\tau_{2}-\\tau_{1}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Ei\\left(\\cdot\\right)$\n\\end_inset\n\n is the exponential integral function.\n\\end_layout\n\n\\begin_layout Subsubsection\nMalkin\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Malkin\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis relaxation function is derived from the continuous relaxation spectrum given in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Malkin06\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Malkin\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n (\n\\begin_inset Formula $\\ge1$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThese parameters must satisfy \n\\begin_inset Formula $\\tau_{2}>\\tau_{1}>0$\n\\end_inset\n\n.\n When \n\\begin_inset Formula $\\beta>1$\n\\end_inset\n\n,\n the reduced relaxation function for this material type is given by\n\\begin_inset Formula \n\\[\ng\\left(t\\right)=\\frac{\\left(\\beta-1\\right)t^{1-\\beta}}{\\tau_{1}^{1-\\beta}-\\tau_{2}^{1-\\beta}}\\left[\\Gamma\\left(\\beta-1,\\frac{t}{\\tau_{2}}\\right)-\\Gamma\\left(\\beta-1,\\frac{t}{\\tau_{1}}\\right)\\right]\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Gamma\\left(a,z\\right)$\n\\end_inset\n\n is the incomplete gamma function.\n In the limit as \n\\begin_inset Formula $\\beta\\to1$\n\\end_inset\n\n this function reduces to the form given by \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Fung72\"\nliteral \"false\"\n\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\ng\\left(t\\right)=\\frac{\\Gamma\\left(0,\\frac{t}{\\tau_{2}}\\right)-\\Gamma\\left(0,\\frac{t}{\\tau_{1}}\\right)}{\\ln\\frac{\\tau_{2}}{\\tau_{1}}}=\\frac{\\Ei\\left(-\\frac{t}{\\tau_{2}}\\right)-\\Ei\\left(-\\frac{t}{\\tau_{1}}\\right)}{\\ln\\frac{\\tau_{1}}{\\tau_{2}}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Ei\\left(z\\right)$\n\\end_inset\n\n is the exponential integral function.\n\\end_layout\n\n\\begin_layout Subsubsection\nMalkin Distortional\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Malkin-distortional\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Malkin\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for the description of this relaxation function.\n When the material parameters vary with the distortional strain \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\tau_{1}\\left(K_{2}\\right) & =\\tau_{10}+\\tau_{11}\\exp\\left(-\\frac{K_{2}}{s_{1}}\\right)\\\\\n\\tau_{2}\\left(K_{2}\\right) & =\\tau_{20}+\\tau_{21}\\exp\\left(-\\frac{K_{2}}{s_{2}}\\right)\n\\end{aligned}\n\\]\n\n\\end_inset\n\n the material type is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Malkin-distortion\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t1c0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\tau_{10}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t1c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\tau_{11}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t1s0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $s_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t2c0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\tau_{20}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t2c1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\tau_{21}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t2s0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $s_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoiwer exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe definition of \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for examples of how to specify these functions.\n\\end_layout\n\n\\begin_layout Subsubsection\nMalkin Distortional User-Specified\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Malkin-dist-user\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSee Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Malkin\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for the description of this relaxation function.\n When the material parameters vary with the distortional strain \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n,\n the material type is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Malkin-dist-user\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau1 type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau2 type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe definition of \n\\begin_inset Formula $K_{2}$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for examples of how to specify these functions.\n\\end_layout\n\n\\begin_layout Subsubsection\nPark\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Park\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Park\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(t\\right)=\\frac{1}{1+\\left(\\frac{t}{\\tau}\\right)^{\\beta}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPark Distortional\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Park-Distortional\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Park-distortion\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent at zero strain \n\\begin_inset Formula $\\beta_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent at zero strain \n\\begin_inset Formula $\\beta_{1}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(\\mathbf{F}\\left(v\\right);t-v\\right)=\\frac{1}{1+\\left(\\frac{t-v}{\\tau}\\right)^{\\beta}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\tau=\\tau_{0}+\\tau_{1}\\cdot\\left(K_{2}\\left(v\\right)\\right)^{\\alpha}\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\[\n\\beta=\\beta_{0}+\\beta_{1}\\cdot\\left(K_{2}\\left(v\\right)\\right)^{\\alpha}\n\\]\n\n\\end_inset\n\nThe definition of \n\\begin_inset Formula $K_{2}\\left(v\\right)$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nPark Distortional User-Specified\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Park-Dist-User\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Park-dist-user\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(\\mathbf{F}\\left(v\\right);t-v\\right)=\\frac{1}{1+\\left(\\frac{t-v}{\\tau}\\right)^{\\beta}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tau\\left(K_{2}\\left(v\\right)\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\beta\\left(K_{2}\\left(v\\right)\\right)$\n\\end_inset\n\n are user-specified functions,\n given either as mathematical expressions or loadcurves.\n The definition of \n\\begin_inset Formula $K_{2}\\left(v\\right)$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for examples of how to specify these functions.\n\\end_layout\n\n\\begin_layout Subsubsection\nPower\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Power\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-power\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(t\\right)=\\frac{1}{\\left(1+\\frac{t}{\\tau}\\right)^{\\beta}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPower Distortional\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Power-Distortional\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-power-distortion\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent at zero strain \n\\begin_inset Formula $\\beta_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent at zero strain \n\\begin_inset Formula $\\beta_{1}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(\\mathbf{F}\\left(v\\right);t-v\\right)=\\frac{1}{\\left(1+\\frac{t-v}{\\tau}\\right)^{\\beta}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\tau=\\tau_{0}+\\tau_{1}\\cdot\\left(K_{2}\\left(v\\right)\\right)^{\\alpha}\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula \n\\[\n\\beta=\\beta_{0}+\\beta_{1}\\cdot\\left(K_{2}\\left(v\\right)\\right)^{\\alpha}\n\\]\n\n\\end_inset\n\nThe definition of \n\\begin_inset Formula $K_{2}\\left(v\\right)$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nPower\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Power-dist-user\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-power-dist-user\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta type=\n\\begin_inset Quotes qrd\n\\end_inset\n\nmath | point\n\\begin_inset Quotes qrd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(\\mathbf{F}\\left(v\\right);t-v\\right)=\\frac{1}{\\left(1+\\frac{t-v}{\\tau}\\right)^{\\beta}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tau\\left(K_{2}\\left(v\\right)\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\beta\\left(K_{2}\\left(v\\right)\\right)$\n\\end_inset\n\n are user-specified functions,\n given either as mathematical expressions or loadcurves.\n The definition of \n\\begin_inset Formula $K_{2}\\left(v\\right)$\n\\end_inset\n\n is given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nnolink \"false\"\n\n\\end_inset\n\n.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Exp-Dist-User\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n for examples of how to specify these functions.\n\\end_layout\n\n\\begin_layout Subsubsection\nProny\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prony\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for this relaxation function is \n\\begin_inset Quotes eld\n\\end_inset\n\nrelaxation-Prony\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nWeight factor \n\\begin_inset Formula $\\gamma_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\vdots$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\vdots$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\vdots$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<g6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nWeight factor \n\\begin_inset Formula $\\gamma_{6}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\vdots$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\vdots$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\vdots$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t6>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCharacteristic relaxation time \n\\begin_inset Formula $\\tau_{6}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe reduced relaxation function for this material type is given by \n\\begin_inset Formula \n\\[\ng\\left(t\\right)=\\frac{\\sum_{i=1}^{6}\\gamma_{i}e^{-t/\\tau_{i}}}{\\sum_{i=1}^{6}\\gamma_{i}}\n\\]\n\n\\end_inset\n\nThe coefficients \n\\begin_inset Formula $\\gamma_{i}$\n\\end_inset\n\n are normalized by \n\\begin_inset Formula $\\sum_{i}\\gamma_{i}$\n\\end_inset\n\n to enforce \n\\begin_inset Formula $g\\left(0\\right)=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nWeak Bond Recruitment Functions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Weak-Bond-Recruitment\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWeak bond recruitment functions make it possible to model highly nonlinear viscoelastic responses,\n where the viscous part of the response increases with increasing strain.\n\\end_layout\n\n\\begin_layout Subsubsection\nPower\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-power\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a power weak bond recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment power\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{1}$\n\\end_inset\n\n (default=0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (default=2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<scale>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nScale factor \n\\begin_inset Formula $s$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the recruitment function is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\mu_{0}+\\mu_{1}\\left(\\frac{\\Xi}{s}\\right)^{\\alpha},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n is the measure of strain that triggers a new reactive weak bond generation.\n This function is unbounded with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n.\n Users should typically employ \n\\begin_inset Formula $\\mu_{0}=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment power\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu0>1</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu1>10</mu1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale>0.06</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-exponential\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for an exponential weak bond recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment exponential\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{1}$\n\\end_inset\n\n (default=0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPower exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<scale>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nScale factor \n\\begin_inset Formula $s$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the recruitment function is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\mu_{0}\\exp\\left(\\mu_{1}\\left(\\frac{\\Xi}{s}\\right)^{\\alpha}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n is the measure of strain that triggers a new reactive weak bond generation.\n This function is unbounded with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n.\n Users should typically employ \n\\begin_inset Formula $\\mu_{0}=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment exponential\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu0>1</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu1>2</mu1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>0.5</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <scale>0.06</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nPolynomial\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-poly\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a polynomial weak bond recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment polynomial\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n (default=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{1}$\n\\end_inset\n\n (default=0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{2}$\n\\end_inset\n\n (default=0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the recruitment function is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\mu_{0}+\\mu_{1}\\Xi+\\mu_{2}\\Xi^{2}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n is the measure of strain that triggers a new reactive weak bond generation.\n This function is unbounded with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n.\n Users should typically employ \n\\begin_inset Formula $\\mu_{0}=1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment poly\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu0>1</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu1>0</mu1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu3>50</mu3>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nUser\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-user\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a user-defined bond recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment user\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<function>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify a user function type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the recruitment function \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n is specified by the user.\n It can be a constant,\n linear ramp,\n mathematical expression,\n a series of points,\n or a step function.\n It is the user's responsibility to ensure that the function starts at 1 and rises monotonically.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment user\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <function type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <interpolate>linear</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <extend>constant</extend>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0.063,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0.13,1.8</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </function>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nLog-Normal\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-log-normal\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a log-normal recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment log-normal\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<sigma>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n (\n\\begin_inset Formula $\\sigma>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum increase \n\\begin_inset Formula $r$\n\\end_inset\n\n in bond recruitment from 1 (default is 0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the recruitment function is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=1+\\frac{r}{2}\\text{erfc}\\left[-\\frac{\\ln\\left(\\Xi/\\mu\\right)}{\\sigma\\sqrt{2}}\\right]\\,.\n\\]\n\n\\end_inset\n\nIts minimum value is \n\\begin_inset Formula $1$\n\\end_inset\n\n and its maximum value is \n\\begin_inset Formula $1+r$\n\\end_inset\n\n as \n\\begin_inset Formula $\\Xi/\\mu\\to\\infty$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment log-normal\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.3</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <sigma>0.25</sigma>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max>4</max>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nWeibull\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-Weibull\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Weibull recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment Weibull\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum increase \n\\begin_inset Formula $r$\n\\end_inset\n\n in bond recruitment from 1 (default is 0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the recruitment functions is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=1+r\\left(1-\\exp\\left[-\\left(\\Xi/\\mu\\right)^{\\alpha}\\right]\\right)\\,.\n\\]\n\n\\end_inset\n\nIts minimum value is \n\\begin_inset Formula $1$\n\\end_inset\n\n and its maximum value is \n\\begin_inset Formula $1+r$\n\\end_inset\n\n as \n\\begin_inset Formula $\\Xi/\\mu\\to\\infty$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.3</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2.0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max>4</max>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nQuintic Polynomial\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-quintic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a quintic polynomial bond recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment quintic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mumin>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{\\min}$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu_{\\min}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mumax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{\\max}$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu_{\\max}>\\mu_{\\min})$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum increase \n\\begin_inset Formula $r$\n\\end_inset\n\n in bond recruitment from 1 (default is 0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the bond recruitment function is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\begin{cases}\n1 & \\Xi\\leqslant\\mu_{\\min}\\\\\n1+rx^{3}\\left(10-15x+6x^{2}\\right) & \\mu_{\\min}<\\Xi\\leqslant\\mu_{\\max}\\\\\n1+r & \\mu_{\\max}<\\Xi\n\\end{cases}\\,,\\quad x=\\frac{\\Xi-\\mu_{\\min}}{\\mu_{\\max}-\\mu_{\\min}}\\,.\n\\]\n\n\\end_inset\n\nIts minimum value is \n\\begin_inset Formula $1$\n\\end_inset\n\n and its maximum value is \n\\begin_inset Formula $1+r$\n\\end_inset\n\n when \n\\begin_inset Formula $\\Xi\\ge\\mu_{\\text{max}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment quintic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mumin>0.2</mumin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mumax>0.4</mumax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>4</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Subsubsection\nGamma\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:recruitment-gamma\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a gamma recruitment function is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrecruitment gamma\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha>0$\n\\end_inset\n\n) \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu>0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum increase \n\\begin_inset Formula $r$\n\\end_inset\n\n in bond recruitment from 1 (default is 0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the bond recruitment functions is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=1+r\\frac{\\gamma\\left(\\alpha,\\Xi/\\mu\\right)}{\\Gamma\\left(\\alpha,0\\right)}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\gamma\\left(\\alpha,z\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Gamma\\left(\\alpha,z\\right)$\n\\end_inset\n\n are the lower and upper incomplete gamma functions,\n respectively.\n The normalization by \n\\begin_inset Formula $\\Gamma\\left(\\alpha,0\\right)$\n\\end_inset\n\n ensures that this function is bounded by \n\\begin_inset Formula $1\\le F\\left(\\Xi\\right)\\le1+r$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<recruitment type=\"recruitment gamma\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>3</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.4</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max>4</max>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Damage Mechanics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Damage-Mechanics\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA material may accumulate damage over a single cycle or multiple cycles of loading which alters its properties.\n In the classical framework of damage mechanics this attenuation of the material properties is described by a single scalar damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n when the material is isotropic (\n\\begin_inset Formula $0\\leqslant D\\leqslant1)$\n\\end_inset\n\n.\n For anisotropic materials however,\n classical frameworks require that we introduce a function of the fourth-order damage tensor \n\\begin_inset Formula $\\mathcal{D}$\n\\end_inset\n\n to account for anisotropic damage.\n In FEBio,\n we use a reactive damage mechanics framework where the elastic response is proportional to the total number of intact bonds in the material and where,\n at any given time in the loading history,\n \n\\begin_inset Formula $D$\n\\end_inset\n\n represents the mass fraction of bonds that have broken.\n In this reactive framework,\n it is possible to also model damage in anisotropic materials by assuming that multiple bond types exist in the material,\n each of which may get damaged under different circumstances.\n Each bond type \n\\begin_inset Formula $b$\n\\end_inset\n\n may be described by a distinct solid constituent within a solid mixture (see Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n),\n each having its own scalar damage variable \n\\begin_inset Formula $D^{b}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor a given bond type,\n the strain energy density \n\\begin_inset Formula $\\Psi_{r}\\left(D,\\mathbf{F}\\right)$\n\\end_inset\n\n of a damaged material is given by \n\\begin_inset Formula \n\\[\n\\Psi_{r}=\\left(1-D\\right)\\Psi_{0}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Psi_{0}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n is the strain energy density when all bonds of that type are intact.\n Here,\n \n\\begin_inset Formula $1-D$\n\\end_inset\n\n represents the mass fraction of bonds that remains intact.\n Similarly,\n the Cauchy stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}\\left(D,\\mathbf{F}\\right)$\n\\end_inset\n\n of the damaged material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\left(1-D\\right)\\boldsymbol{\\sigma}_{0}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n is the stress in the intact material,\n at a given strain,\n as derived from \n\\begin_inset Formula $\\Psi_{0}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n.\n The intact material may be based on any of the elastic materials described in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe evolution of the damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n is determined by a user-selected scalar damage criterion measure \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n (\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n is the capital form of \n\\begin_inset Formula $\\xi)$\n\\end_inset\n\n.\n For example,\n \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n may represent the strain energy density,\n or von Mises stress,\n or maximum principal normal strain,\n etc.\n If \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n exceeds a given threshold at some state of deformation \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n,\n then damage may initiate or progress further.\n If all bonds fail at a single threshold value \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n,\n the material undergoes fracture.\n More commonly,\n bonds may fail with increasing probability as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n increases over a given range.\n Consequently,\n the evolution of damage may be based on a user-selected cumulative distribution function (c.d.f.) \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $D\\left(t\\right)=F\\left(\\Xi_{m}\\right)$\n\\end_inset\n\n where \n\\begin_inset Formula $\\Xi_{m}$\n\\end_inset\n\n is the maximum value of \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n achieved over the loading history up until the current time \n\\begin_inset Formula $t$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Damage Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Damage\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material types for damage materials are \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nelastic damage\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n and \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled elastic damage\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<damage>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the cumulative distribution function \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the damage criterion \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <elastic> tag encloses a description of the constitutive relation of the intact elastic material and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for unconstrained materials (used with \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nelastic damage\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n) and Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for uncoupled materials (used with \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled elastic damage\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n).\n The <damage> tag encloses a description of the cumulative distribution function and associated material properties,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Cumulative-Distribution-Function\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <criterion> tag encloses a description of the damage criterion,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"elastic damage\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>0.13</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>8</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mu>0.3</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <criterion type=\"DC max normal Lagrange strain\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  </criterion>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nCumulative Distribution Functions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cumulative-Distribution-Function\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nCumulative distribution functions provide the function \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n that determines the evolution of the damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n based on the maximum value of the failure criterion \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n up until the current time.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nSimo\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-Simo\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset label\nLatexCommand label\nname \"para:mylabel47\"\n\n\\end_inset\n\n The material type for Simo's c.d.f.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF Simo\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<a>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<b>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n (\n\\begin_inset Formula $0\\leqslant\\beta\\leqslant1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the c.d.f.\n is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=1-\\beta-\\left(1-\\beta\\right)\\frac{\\alpha}{\\Xi}\\left(1-e^{-\\Xi/\\alpha}\\right)\\,.\n\\]\n\n\\end_inset\n\nNote that \n\\begin_inset Formula \n\\[\n\\lim\\limits_{\\Xi\\to\\infty}F\\left(\\Xi\\right)=1-\\beta\n\\]\n\n\\end_inset\n\nrepresents the maximum allowable damage.\n Therefore,\n \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n regulates the maximum allowable damage,\n whereas \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n controls the rate at which \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n increases with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigDamageCDFSimo.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF Simo\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <a>0.1</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <b>0</b>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n</damage>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLog-Normal\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-log-normal\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a log-normal c.d.f.\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF log-normal\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<sigma>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n (\n\\begin_inset Formula $\\sigma>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Dmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum allowable damage (optional,\n default is 1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the c.d.f.\n is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\frac{1}{2}\\text{erfc}\\left[-\\frac{\\ln\\left(\\Xi/\\mu\\right)}{\\sigma\\sqrt{2}}\\right]\\,.\n\\]\n\n\\end_inset\n\nNote that \n\\begin_inset Formula \n\\[\nF\\left(\\mu\\right)=1/2,\n\\]\n\n\\end_inset\n\nwhich shows that \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n at which half of the bonds break.\n Here,\n \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n regulates the rate at which damage increases with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n with smaller values of \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n producing a more rapid increase.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigDamageCDFLogNormal.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF log-normal\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>1</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <sigma>0.5</sigma>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n</damage>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nWeibull\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-Weibull\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Weibull c.d.f.\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF Weibull\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Dmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum allowable damage (optional,\n default is 1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the c.d.f.\n is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=1-\\exp\\left[-\\left(\\Xi/\\mu\\right)^{\\alpha}\\right]\\,.\n\\]\n\n\\end_inset\n\nNote that \n\\begin_inset Formula \n\\[\nF\\left(\\mu\\right)=1-e^{-1}\\approx0.63\\,,\n\\]\n\n\\end_inset\n\nwhich shows that \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n at which the fraction \n\\begin_inset Formula $1-e^{-1}$\n\\end_inset\n\n of bonds break.\n Here,\n \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n regulates the rate at which damage increases with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n with higher values of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n producing a more rapid increase.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigDamageCDFWeibull.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>1</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>5.0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n</damage>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nQuintic Polynomial\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-quintic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a quintic polynomial c.d.f.\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF quintic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mumin>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{\\min}$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu_{\\min}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mumax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu_{\\max}$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu_{\\max}>\\mu_{\\min})$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Dmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum allowable damage (optional,\n default is 1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the c.d.f.\n is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\begin{cases}\n0 & \\Xi\\leqslant\\mu_{\\min}\\\\\nx^{3}\\left(10-15x+6x^{2}\\right) & \\mu_{\\min}<\\Xi\\leqslant\\mu_{\\max}\\\\\n1 & \\mu_{\\max}<\\Xi\n\\end{cases}\\,,\\quad x=\\frac{\\Xi-\\mu_{\\min}}{\\mu_{\\max}-\\mu_{\\min}}\\,.\n\\]\n\n\\end_inset\n\nNote that \n\\begin_inset Formula \n\\[\nF\\left(\\frac{1}{2}\\left(\\mu_{\\min}+\\mu_{\\max}\\right)\\right)=\\frac{1}{2}\\,,\n\\]\n\n\\end_inset\n\nwhich shows that \n\\begin_inset Formula $\\left(\\mu_{\\min}+\\mu_{\\max}\\right)/2$\n\\end_inset\n\n is the value of \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n at which half of the bonds break.\n The range \n\\begin_inset Formula $\\mu_{\\max}-\\mu_{\\min}$\n\\end_inset\n\n regulates the rate at which damage increases with increasing \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n with a narrower range producing a more rapid increase.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigDamageCDFQuintic.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF quintic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mumin>0.3</mumin>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mumax>1.7</mumax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n</damage>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nGamma\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-gamma\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a gamma c.d.f.\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF gamma\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha>0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\mu>0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the c.d.f.\n is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=\\frac{\\gamma\\left(\\alpha,\\Xi/\\mu\\right)}{\\Gamma\\left(\\alpha,0\\right)}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\gamma\\left(\\alpha,z\\right)$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Gamma\\left(\\alpha,z\\right)$\n\\end_inset\n\n are the lower and upper incomplete gamma functions,\n respectively.\n The normalization by \n\\begin_inset Formula $\\Gamma\\left(\\alpha,0\\right)$\n\\end_inset\n\n ensures that this function is bounded by \n\\begin_inset Formula $0\\le F\\left(\\Xi\\right)\\le1$\n\\end_inset\n\n.\n The figure below uses \n\\begin_inset Formula $\\mu=0.15$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigDamageCDFgamma.png\n\tlyxscale 50\n\twidth 4in\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF gamma\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>3</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>0.15</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n</damage>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nStep\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-Step\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a step c.d.f.\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF step\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n (same units as \n\\begin_inset Formula $\\Xi)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Dmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaximum allowable damage (optional,\n default is 1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the c.d.f.\n is given by \n\\begin_inset Formula \n\\[\nF\\left(\\Xi\\right)=H\\left(\\Xi\\right)\\quad,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $H\\left(\\cdot\\right)$\n\\end_inset\n\n is the Heaviside unit step function.\n The step c.d.f.\n may be used to model fracture.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Graphics\n\tfilename Figures/FigDamageCDFStep.png\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF step\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mu>1.0</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n</damage>\n\\end_layout\n\n\\begin_layout Subsubsection\nUser\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:CDF-user\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a user-defined CDF is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCDF user\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cdf>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecify a user function type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material the CDF \n\\begin_inset Formula $F\\left(\\Xi\\right)$\n\\end_inset\n\n is specified by the user.\n It can be a constant,\n linear ramp,\n mathematical expression,\n a series of points,\n or a step function.\n It is the user's responsibility to ensure that the function rises monotonically from 0 to 1 and does not exceed unity.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<damage type=\"CDF user\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <cdf type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <interpolate>control points</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <extend>constant</extend>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0.49,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t  <pt>0.5,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>0.99,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>1.01,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>1.49,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>1.5,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <pt>2.5,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </cdf>\n\\end_layout\n\n\\begin_layout LyX-Code\n</recruitment>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDamage or Yield Criterion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Damage-Yield-Criterion\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe damage criterion provides the functional form of \n\\begin_inset Formula $\\Xi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n that determines the evolution of damage.\n It can also be used to define the yield criterion in the reactive plasticity framework.\n All the functions currently modeled in FEBio are defined over the range \n\\begin_inset Formula $\\Xi\\in\\left[0,\\infty\\right[$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nSimo\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:DC-Simo\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for Simo's damage criterion \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nDC Simo\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\sqrt{2\\Psi_{0}\\left(\\mathbf{F}\\right)}\n\\]\n\n\\end_inset\n\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC Simo\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nStrain Energy Density\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Strain-Energy-Density\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for strain energy density damage criterion is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nDC strain energy density\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\Psi_{0}\\left(\\mathbf{F}\\right)\n\\]\n\n\\end_inset\n\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC strain energy density\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nSpecific Strain Energy\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Specific-Strain-Energy\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for specific strain energy damage criterion is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC specific strain energy\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\Psi_{0}\\left(\\mathbf{F}\\right)/\\rho\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho$\n\\end_inset\n\n is the elastic material's density.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC specific strain energy\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nVon Mises Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Von-Mises-Stress\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for von Mises stress damage or yield criterion is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC von Mises stress\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\sqrt{\\frac{3}{2}\\dev\\boldsymbol{\\sigma}:\\dev\\boldsymbol{\\sigma}}=\\sqrt{\\frac{1}{2}\\left[\\left(\\sigma_{1}-\\sigma_{2}\\right)^{2}+\\left(\\sigma_{2}-\\sigma_{3}\\right)^{2}+\\left(\\sigma_{3}-\\sigma_{1}\\right)^{2}\\right]}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{1},\\sigma_{2},\\sigma_{3}$\n\\end_inset\n\n are the principal values of \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nMaximum Shear Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Maximum-Shear-Stress\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for maximum shear stress damage or yield criterion is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC max shear stress\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\max\\left(\\frac{\\left|\\sigma_{1}-\\sigma_{2}\\right|}{2},\\frac{\\left|\\sigma_{2}-\\sigma_{3}\\right|}{2},\\frac{\\left|\\sigma_{3}-\\sigma_{1}\\right|}{2}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{1},\\sigma_{2},\\sigma_{3}$\n\\end_inset\n\n are the principal values of \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC max shear stress\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nMaximum Normal Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Maximum-Normal-Stress\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for maximum normal stress damage or yield criterion is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC max normal stress\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\max\\left(\\sigma_{1},\\sigma_{2},\\sigma_{3}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{1},\\sigma_{2},\\sigma_{3}$\n\\end_inset\n\n are the principal values of \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC max normal stress\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nDrucker Shear Stress\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Drucker-Shear-Stress\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Drucker shear stress criterion is \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC Drucker shear stress\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n.\n It is based on the yield criterion for plasticity introduced in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Drucker49\"\nliteral \"true\"\n\n\\end_inset\n\n.\n For this criterion,\n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=k=\\left(J_{2}^{3}-cJ_{3}^{2}\\right)^{1/6}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J_{2}=\\frac{1}{2}\\dev\\boldsymbol{\\sigma}_{0}:\\dev\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n,\n \n\\begin_inset Formula $J_{3}=\\det\\left(\\dev\\boldsymbol{\\sigma}_{0}\\right)$\n\\end_inset\n\n,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n is the yield limit in simple shear and \n\\begin_inset Formula $c$\n\\end_inset\n\n is a user-specified non-dimensional material constant which must lie in the range \n\\begin_inset Formula $-\\frac{27}{8}\\le c\\le\\frac{9}{4}$\n\\end_inset\n\n.\n To better understand the meaning of \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n consider uniaxial loading of a bar which yields at the normal stress value of \n\\begin_inset Formula $\\sigma_{y}$\n\\end_inset\n\n.\n In this case,\n\\begin_inset Formula \n\\[\nk=\\frac{\\sigma_{y}}{\\sqrt{3}}\\left(1-\\frac{4}{27}c\\right)^{1/6}\\quad\\frac{\\sigma_{y}}{\\sqrt{3}}\\left(\\frac{2}{3}\\right)^{1/6}\\le k\\le\\frac{\\sigma_{y}}{\\sqrt{3}}\\left(\\frac{3}{2}\\right)^{1/6}\n\\]\n\n\\end_inset\n\nIn the special case when \n\\begin_inset Formula $c=0$\n\\end_inset\n\n the Drucker criterion reduces to the von Mises criterion,\n with \n\\begin_inset Formula $k=\\sigma_{y}/\\sqrt{3}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC Drucker shear stress\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <c>2.25</c>\n\\end_layout\n\n\\begin_layout LyX-Code\n</criterion>\n\\end_layout\n\n\\begin_layout Subsubsection\nDrucker-Prager Criterion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Drucker-Prager-criterion\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Drucker-Prager criterion is \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC Drucker-Prager\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n.\n It is based on the yield criterion for plasticity introduced in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Drucker52\"\nliteral \"true\"\n\n\\end_inset\n\n.\n For this criterion,\n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\sigma_{e}-b\\sigma_{m}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{e}$\n\\end_inset\n\n is the von Mises stress,\n \n\\begin_inset Formula $\\sigma_{m}=\\frac{1}{3}\\tr\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n is the hydrostatic stress,\n \n\\begin_inset Formula $b$\n\\end_inset\n\n is a parameter that depends on the yield (or failure) stress \n\\begin_inset Formula $\\sigma_{t}$\n\\end_inset\n\n in tension,\n and \n\\begin_inset Formula $\\sigma_{c}$\n\\end_inset\n\n in compression (\n\\begin_inset Formula $\\sigma_{c}\\ge0$\n\\end_inset\n\n),\n \n\\begin_inset Formula \n\\[\nb=3\\frac{\\sigma_{t}-\\sigma_{c}}{\\sigma_{c}+\\sigma_{t}}\n\\]\n\n\\end_inset\n\nThis parameter \n\\begin_inset Formula $b$\n\\end_inset\n\n is negative when \n\\begin_inset Formula $\\sigma_{c}>\\sigma_{t}$\n\\end_inset\n\n.\n In the special case when \n\\begin_inset Formula $b=0$\n\\end_inset\n\n the Drucker-Prager criterion reduces to the von Mises criterion.\n When used as a plastic yield criterion (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n the yield stress \n\\begin_inset Formula $\\sigma_{y}$\n\\end_inset\n\n for this type of material is given by\n\\begin_inset Formula \n\\[\n\\sigma_{y}=2\\frac{\\sigma_{c}\\sigma_{t}}{\\sigma_{c}+\\sigma_{t}}\n\\]\n\n\\end_inset\n\nwhich reduces to \n\\begin_inset Formula $\\sigma_{y}=\\sigma_{t}$\n\\end_inset\n\n for uniaxial tension,\n \n\\begin_inset Formula $\\sigma_{y}=-\\sigma_{c}$\n\\end_inset\n\n in uniaxial compression,\n and more generally,\n \n\\begin_inset Formula $\\sigma_{y}=\\sigma_{c}=\\sigma_{t}$\n\\end_inset\n\n when \n\\begin_inset Formula $\\sigma_{t}=\\sigma_{c}$\n\\end_inset\n\n.\n The parameter \n\\begin_inset Formula $a$\n\\end_inset\n\n and \n\\begin_inset Formula $b$\n\\end_inset\n\n are related to the parameters \n\\begin_inset Formula $A$\n\\end_inset\n\n and \n\\begin_inset Formula $B$\n\\end_inset\n\n appearing on Wikipedia's description of the \n\\begin_inset CommandInset href\nLatexCommand href\nname \"Drucker-Prager yield criterion\"\ntarget \"https://en.wikipedia.org/wiki/Drucker–Prager_yield_criterion\"\nliteral \"false\"\n\n\\end_inset\n\n via\n\\begin_inset Formula \n\\[\n\\begin{aligned}a & =\\sqrt{3}A & b & =3\\sqrt{3}B\\end{aligned}\n\\]\n\n\\end_inset\n\nGiven these relationships,\n users may consult that web page for finding the relation between these parameters and cohesion \n\\begin_inset Formula $c$\n\\end_inset\n\n and angle of internal friction \n\\begin_inset Formula $\\phi$\n\\end_inset\n\n (as well as the \n\\begin_inset CommandInset href\nLatexCommand href\nname \"Mohr-Coulomb yield surface\"\ntarget \"https://en.wikipedia.org/wiki/Mohr–Coulomb_theory#Typical_values_of_cohesion_and_angle_of_internal_friction\"\nliteral \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC Drucker-Prager\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <b>-1</b>\n\\end_layout\n\n\\begin_layout LyX-Code\n</criterion>\n\\end_layout\n\n\\begin_layout Subsubsection\nDeshpande-Fleck Criterion\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Deshpande-Fleck-criterion\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Deshpande-Fleck criterion is \n\\emph on\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC Deshpande-Fleck\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n.\n It is based on the yield criterion for plasticity introduced in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Deshpande01\"\nliteral \"true\"\n\n\\end_inset\n\n.\n For this criterion,\n we let\n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\sqrt{\\frac{\\sigma_{e}^{2}+\\left(3\\beta\\sigma_{m}\\right)^{2}}{1+\\beta^{2}}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\sigma_{e}$\n\\end_inset\n\n is the von Mises stress,\n \n\\begin_inset Formula $\\sigma_{m}=\\frac{1}{3}\\tr\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n is the hydrostatic stress,\n \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is a material parameter that controls the contribution of the hydrostatic stress to this criterion (\n\\begin_inset Formula $\\beta=0$\n\\end_inset\n\n recovers the von Mises yield criterion).\n This criterion was introducted to model yielding of polymer foams,\n for which Deshpande and Fleck recommended using \n\\begin_inset Formula $\\beta=\\nicefrac{1}{3}$\n\\end_inset\n\n.\n On \n\\begin_inset CommandInset href\nLatexCommand href\nname \"Wikipedia\"\ntarget \"https://en.wikipedia.org/wiki/Drucker–Prager_yield_criterion#Deshpande–Fleck_yield_criterion_or_isotropic_foam_yield_criterion\"\nliteral \"false\"\n\n\\end_inset\n\n,\n this criterion is described as a modification of the Drucker-Prager criterion,\n but note that Deshpande and Fleck describe this material model as \n\\begin_inset Quotes eld\n\\end_inset\n\nphenomenological\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n i.e.,\n intended to fit experimental data well,\n though they do suggest that \n\\begin_inset Formula $3\\beta$\n\\end_inset\n\n represents the ratio of hydrostatic strength to shear strength.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC Deshpande-Fleck\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>0.3333</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n</criterion>\n\\end_layout\n\n\\begin_layout Subsubsection\nMaximum Normal Lagrange Strain\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Maximum-Normal-Lagrange\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for maximum normal Lagrange strain damage criterion is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC max normal Lagrange strain\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n \n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\max\\left(E_{1},E_{2},E_{3}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $E_{1},E_{2},E_{3}$\n\\end_inset\n\n are the principal values of \n\\begin_inset Formula $\\mathbf{E}=\\left(\\mathbf{F}^{T}\\cdot\\mathbf{F}-\\mathbf{I}\\right)/2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<criterion type=\"DC max normal Lagrange strain\"/>\n\\end_layout\n\n\\begin_layout Subsubsection\nOctahedral Shear Strain\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Octahedral-Shear-Strain\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for octahedral strain damage criterion is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nDC octahedral shear strain\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n For this criterion,\n\\begin_inset Formula \n\\[\n\\Xi\\left(\\mathbf{F}\\right)=\\sqrt{\\frac{2}{3}\\dev\\mathbf{E}:\\dev\\mathbf{E}}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is the Lagrange strain tensor.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Fatigue\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Fatigue\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReactive fatigue is a framework that leverages the reactive damage framework described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In reactive damage,\n the intact bonds of an elastic material may break upon loading,\n causing damage.\n This single reactive process governs reactive damage.\n In reactive fatigue we assume that three reactions may take place simultaneously:\n (1) Intact bonds of an elastic material may break in response to loading (\n\\emph on\nelastic damage\n\\emph default\n),\n (2) Intact bonds of an elastic material may fatigue in response to repetitive loading,\n thereby reducing their damage threshold but maintaining their elasticity,\n and (3) Fatigued bonds of elastic material may break in response to loading (\n\\emph on\nfatigue damage\n\\emph default\n).\n The two damage reactions (1 and 3 in this list) behave identically with reactive damage materials described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Thus,\n each of these reactions requires a cumulative distribution function (CDF,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Cumulative-Distribution-Function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) which provides the probability that bonds will break at a given threshold of the damage criterion \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n It is the user's responsibility to ensure that the CDF of fatigued bonds produces failure at a lesser threshold than the CDF of elastic bonds.\n\\end_layout\n\n\\begin_layout Standard\nThe fatigue reaction (2 in the list given above) progresses with a reaction rate\n\\begin_inset Formula \n\\[\nk^{if}=k_{0}^{if}\\left(\\left|\\dot{\\Xi}^{i}\\right|w^{b}\\right)^{\\beta}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $k_{0}^{if}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n are user-specified parameters.\n \n\\begin_inset Formula $\\dot{\\Xi}^{i}$\n\\end_inset\n\n is the time rate of change of the damage criterion \n\\begin_inset Formula $\\Xi^{i}$\n\\end_inset\n\n of intact bonds of the elastic solid,\n whereas \n\\begin_inset Formula $w^{b}$\n\\end_inset\n\n is the mass fraction of broken bonds in the elastic solid (also known as the \n\\emph on\ndamage\n\\emph default\n \n\\begin_inset Formula $D$\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nRemarks:\n\n\\emph default\n The parameter \n\\begin_inset Formula $k_{0}^{if}$\n\\end_inset\n\n determines the rate at which the fatigue reaction proceeds,\n for a given \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n Decreasing \n\\begin_inset Formula $k_{0}^{if}$\n\\end_inset\n\n shifts the \n\\begin_inset Formula $S-N$\n\\end_inset\n\n curve (failure stress versus number of cycles) to the right.\n The parameter \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n affects the shape of the \n\\begin_inset Formula $S-N$\n\\end_inset\n\n curve,\n for a given \n\\begin_inset Formula $k_{0}^{if}$\n\\end_inset\n\n.\n Increasing \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n causes the \n\\begin_inset Formula $S-N$\n\\end_inset\n\n curve to become more sigmoidal.\n It is acceptable to use the same criterion for elastic and fatigue damage.\n To model fatigue failure,\n the CDF for fatigue damage should be to the left of the CDF for elastic damage on a CDF vs \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n graph (i.e.,\n amount of damage at a given \n\\begin_inset Formula $\\Xi$\n\\end_inset\n\n should be lower for elastic damage than for fatigue damage,\n \n\\begin_inset Formula $F^{i}\\left(\\Xi\\right)<F^{f}\\left(\\Xi\\right)$\n\\end_inset\n\n).\n The fatigue reaction may not be initiated unless some elastic damage has taken place,\n since \n\\begin_inset Formula $k^{if}=0$\n\\end_inset\n\n when \n\\begin_inset Formula $w^{b}=0$\n\\end_inset\n\n.\n However,\n the amount of elastic damage needed to initiate the fatigue reaction may be very small,\n \n\\begin_inset Formula $w^{b}\\ll1$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Reactive Fatigue Material\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Fatigue\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material types for reactive fatigue materials are \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nreactive fatigue\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n and \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled reactive fatigue\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the reaction rate constant \n\\begin_inset Formula $k_{0}^{if}$\n\\end_inset\n\n for the fatigue reaction (units vary)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the power exponent \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n for the fatigue reaction (\n\\begin_inset Formula $\\beta>0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic_damage>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the cumulative distribution function \n\\begin_inset Formula $F^{i}\\left(\\Xi^{i}\\right)$\n\\end_inset\n\n for damage of intact bonds\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fatigue_damage>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the cumulative distribution function \n\\begin_inset Formula $F^{f}\\left(\\Xi^{f}\\right)$\n\\end_inset\n\n for damage of fatigue bonds\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic_criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic damage criterion \n\\begin_inset Formula $\\Xi^{i}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fatigue_criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fatigue damage criterion \n\\begin_inset Formula $\\Xi^{f}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <elastic> tag encloses a description of the constitutive relation of the intact elastic material and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for unconstrained materials (used with \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nelastic damage\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n) and Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for uncoupled materials (used with \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nuncoupled elastic damage\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n).\n The <elastic_damage> or <fatigue_damage> tags enclose a description of the cumulative distribution function and associated material properties for elastic or fatigue damage,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Cumulative-Distribution-Function\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <elastic_criterion> and <fatigue_criterion> tags enclose a description of the damage criterion for intact and fatigued bonds of the elastic solid,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"elastic damage\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k0>0.001</k0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta>2</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>200000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic_damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>20</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mu>200</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic_damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fatigue_damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>10</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mu>100</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fatigue_damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic_criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fatigue_criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Plasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Plasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nKinematic \n\\begin_inset Quotes eld\n\\end_inset\n\nHardening\n\\begin_inset Quotes erd\n\\end_inset\n\n Response\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Kinematic-Hardening-Response\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nReactive plasticity models a material as a mixture of bonds that break in response to loading and reform in a stressed state \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"true\"\n\n\\end_inset\n\n.\n This framework is based on constrained reactive mixtures of solids \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10a\"\nliteral \"true\"\n\n\\end_inset\n\n.\n A reactively plastic solid consists of a mixture of \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n bond families,\n all of which share the same elastic response characterized by the user-defined strain energy density \n\\begin_inset Formula $\\Psi_{0}$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n each of which yields at a different threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n of a user-defined yield measure \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n (such as the von Mises stress,\n see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n When all \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n bond families have yielded,\n the response becomes perfectly plastic,\n implying that the yield measure no longer increases with increasing strain.\n When \n\\begin_inset Formula $n^{f}>1$\n\\end_inset\n\n the stress-strain response exhibits the characteristic kinematic \n\\begin_inset Quotes eld\n\\end_inset\n\nhardening\n\\begin_inset Quotes erd\n\\end_inset\n\n response consistent with the Bauschinger effect of classical plasticity theory \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Skelton97\"\nliteral \"true\"\n\n\\end_inset\n\n.\n Optionally,\n an additional bond family may be included that never yields,\n remaining elastic over the entire loading history and imparting a linear \n\\begin_inset Quotes eld\n\\end_inset\n\nhardening\n\\begin_inset Quotes erd\n\\end_inset\n\n regime.\n\\end_layout\n\n\\begin_layout Standard\nThe deformation gradient of a reactive plasticity material is \n\\begin_inset Formula $\\mathbf{F}^{s}$\n\\end_inset\n\n and its associated mixture stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n is evaluated as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}\\left(\\mathbf{F}^{s}\\right)=\\sum_{\\beta}w_{\\beta}\\left(w_{\\beta}^{s}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}^{s}\\right)+w_{\\beta}^{y}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{y}\\right)\\right)\\,,\\label{eq:rp-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n is the user-defined mass fraction of each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n in the mixture,\n \n\\begin_inset Formula $w_{\\beta}^{s}$\n\\end_inset\n\n is the evolving mass fraction of intact bonds in family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n which still exhibit an elastic response,\n and \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n is the evolving mass fraction of bonds in family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n which have yielded (broken and reformed in a new reference configuration).\n These mass fractions satisfy \n\\begin_inset Formula $\\sum_{\\beta}w_{\\beta}=1$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{s}+w_{\\beta}^{y}=1$\n\\end_inset\n\n.\n The bond breaking-and-reforming reaction is denoted by \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{s}\\to\\mathcal{E}_{\\beta}^{y}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{s}$\n\\end_inset\n\n is the species associated with intact bonds while \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{y}$\n\\end_inset\n\n is associated with yielded bonds,\n in bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n The mass fractions of bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n satisfy \n\\begin_inset Formula $w_{\\beta}^{s}=1$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{y}=0$\n\\end_inset\n\n prior to yielding,\n and \n\\begin_inset Formula $w_{\\beta}^{s}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{y}=1$\n\\end_inset\n\n after yielding.\n Here,\n \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{y}$\n\\end_inset\n\n is the deformation gradient of yielded bonds \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n relative to the deformation when the yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n was first reached;\n it is calculated from the relation \n\\begin_inset Formula $\\mathbf{F}^{s}=\\mathbf{F}_{\\beta}^{y}\\cdot\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n is a function of state that maps the yielded configuration \n\\begin_inset Formula $\\mathbf{X}^{y}$\n\\end_inset\n\n relative to the global reference configuration \n\\begin_inset Formula $\\mathbf{X}^{s}$\n\\end_inset\n\n.\n The specific form of the constitutive model for \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n used in FEBio can be found in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"true\"\n\n\\end_inset\n\n and the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n Currently that constitutive model is not user-definable,\n though the user has the option to enforce isochoric plastic flow,\n implying that \n\\begin_inset Formula $\\det\\mathbf{F}_{\\beta}^{ys}=1$\n\\end_inset\n\n.\n The stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n in the above relation for \n\\begin_inset Formula $\\boldsymbol{\\sigma}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n may be evaluated for any argument \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n as\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}\\right)=\\frac{1}{J}\\frac{\\partial\\Psi_{0}}{\\partial\\mathbf{F}}\\cdot\\mathbf{F}^{T}\\,,\\label{eq:rp-hyperelasticity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPlastic Yield Surface\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Plastic-Yield-Surface\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe plastic yield surface is defined as\n\\begin_inset Formula \n\\[\n\\varphi\\left(\\mathbf{F}\\right)=\\Phi\\left(\\mathbf{F}\\right)-\\Phi_{m}\\le0\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Phi\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n represents the yield measure (e.g.,\n the von Mises stress),\n as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The value of \n\\begin_inset Formula $\\Phi_{m}$\n\\end_inset\n\n may be a constant (for elastic-perfectly-plastic behavior),\n or it may evolve along a plastic flow curve,\n as described in the next section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Plastic-Flow-Curve\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n (In a strict theoretical sense,\n \n\\begin_inset Formula $\\Phi_{m}$\n\\end_inset\n\n is a constant in the framework of constrained reactive mixtures,\n however FEBio uses an implementation where multiple plastically-yielding bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n may coexist,\n each having its own value \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n.)\n\\end_layout\n\n\\begin_layout Subsection\nPlastic Flow Curve\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Plastic-Flow-Curve\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSince each bond family behaves elastically until it yields,\n a family's yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n is generally not the value recorded on a stress-strain curve when the slope changes.\n That value may be called the \n\\shape italic\napparent yield threshold\n\\shape default\n \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n,\n which can be related to the true yield threshold \n\\begin_inset Formula $\\Phi_{m\\beta}=\\Phi\\left(\\varepsilon_{\\beta}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varepsilon_{\\beta}$\n\\end_inset\n\n is the true (logarithmic or Hencky) strain at which bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n yields,\n by assuming a linear elastic stress-strain relationship prior to yielding,\n \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)=E\\varepsilon$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $E$\n\\end_inset\n\n is the elastic material's Young's modulus.\n This assumption makes it possible to relate the bond family mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n to the values of \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n via\n\\begin_inset Formula \n\\[\n\\begin{aligned}\\Phi_{m\\beta} & =\\Phi_{m,\\beta-1}+\\frac{\\Upsilon_{\\beta}-\\Upsilon_{\\beta-1}}{1-\\sum_{b=0}^{\\beta-1}w_{b}}\\,, & \\Phi_{m0} & =\\Upsilon_{0}\\,.\\end{aligned}\n\\]\n\n\\end_inset\n\nMore details can be found in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"true\"\n\n\\end_inset\n\n.\n FEBio offers several different methods for specifying the plastic flow curve parameters \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n,\n which are used internally to evaluate \n\\begin_inset Formula $\\Phi_{m\\beta}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n:\n\\end_layout\n\n\\begin_layout Subsubsection\nUser-Specified Flow Curve\n\\end_layout\n\n\\begin_layout Standard\nA user-specified flow curve may be provided using the plastic flow curve material type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nPFC user\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The user enters a list of points representing \n\\begin_inset Formula $\\left(\\varepsilon_{\\beta},\\Upsilon_{\\beta}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\varepsilon_{\\beta}$\n\\end_inset\n\n is the true strain corresponding to the apparent yield threshold \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\varepsilon_{\\beta}$\n\\end_inset\n\n represents the total strain,\n not the plastic strain.\n The first point provided in this list,\n \n\\begin_inset Formula $\\left(\\varepsilon_{0},\\Upsilon_{0}\\right)$\n\\end_inset\n\n,\n corresponds to the yield strain \n\\begin_inset Formula $\\varepsilon_{0}$\n\\end_inset\n\n and yield stress \n\\begin_inset Formula $\\Upsilon_{0}$\n\\end_inset\n\n for this material,\n such that the ratio \n\\begin_inset Formula $\\Upsilon_{0}/\\varepsilon_{0}$\n\\end_inset\n\n is used internally to calculate Young's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n for the selected material used in this plasticity analysis.\n It is the user's responsibility to ensure this consistency.\n The number of bond families,\n \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n,\n is automatically set to the number of user-specified points on this flow curve.\n To maintain computational efficiency it is recommended to keep this number small,\n e.g.,\n no greater than 30 points.\n Specifying only one point produces an elastic-perfectly plastic material response.\n\\end_layout\n\n\\begin_layout Standard\nThe response produced by the reactive plasticity material will agree nearly exactly with the user-specified points as long as the strain \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n remains in the infinitesimal range.\n However,\n as the strain \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n becomes finite,\n the reactive plasticity response may deviate non-negligibly from the user-specified flow curve,\n due to the nonlinearity of the hyperelastic relation describing the elastic response of the material.\n In practice,\n for isotropic elastic responses,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nnatural neo-Hookean\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Natural-Neo-Hookean\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) will produce the least amount of deviation when using large strains,\n since its uniaxial stress-strain response is nearly linear with \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)=E\\varepsilon\\,e^{-\\left(1-2\\nu\\right)\\varepsilon}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $E$\n\\end_inset\n\n is Young's modulus and \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n is Poisson's ratio.\n As \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n approaches \n\\begin_inset Formula $\\frac{1}{2}$\n\\end_inset\n\n this expression produces an exact linear response under uniaxial loading,\n \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)=E\\varepsilon$\n\\end_inset\n\n,\n even under finite deformation.\n When using \n\\begin_inset Formula $\\nu<\\frac{1}{2}$\n\\end_inset\n\n,\n care must be taken not to exceed \n\\begin_inset Formula $\\varepsilon_{\\text{max}}=\\left(1-2\\nu\\right)^{-1}$\n\\end_inset\n\n,\n since the slope of \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)$\n\\end_inset\n\n becomes zero at that value and negative beyond it.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nMathematical Expression for Flow Curve\n\\end_layout\n\n\\begin_layout Standard\nAlternatively,\n a mathematical expression may be supplied to describe \n\\begin_inset Formula $\\Upsilon\\left(\\varepsilon\\right)$\n\\end_inset\n\n,\n using the plastic flow curve material type \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nPFC math\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters must also be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<nf>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of bond families \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n (\n\\begin_inset Formula $n_{f}>0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<e0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe value of \n\\begin_inset Formula $\\varepsilon_{0}$\n\\end_inset\n\n that produces \n\\begin_inset Formula $\\Upsilon\\left(\\varepsilon_{0}\\right)=\\Upsilon_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<emax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe highest value of \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n expected in this analysis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIt is the user's responsibility to ensure that the supplied mathematical function produces \n\\begin_inset Formula $\\Upsilon_{0}/\\varepsilon_{0}$\n\\end_inset\n\n equal to Young's modulus for the elastic material model being used.\n The actual response produced by the reactive plasticity material will agree nearly exactly with the mathematical expression for \n\\begin_inset Formula $\\Upsilon\\left(\\varepsilon\\right)$\n\\end_inset\n\n as long as the strain \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n remains in the infinitesimal range.\n However,\n as the strain \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n becomes finite,\n the reactive plasticity response may deviate non-negligibly from the user-specified flow curve,\n due to the nonlinearity of the hyperelastic relation describing the elastic response of the material.\n In practice,\n for isotropic elastic responses,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nnatural neo-Hookean\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Natural-Neo-Hookean\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) will produce the least amount of deviation when using large strains,\n since its uniaxial stress-strain response is nearly linear with \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)=E\\varepsilon\\,e^{-\\left(1-2\\nu\\right)\\varepsilon}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $E$\n\\end_inset\n\n is Young's modulus and \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n is Poisson's ratio.\n As \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n approaches \n\\begin_inset Formula $\\frac{1}{2}$\n\\end_inset\n\n this expression produces an exact linear response under uniaxial loading,\n \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)=E\\varepsilon$\n\\end_inset\n\n,\n even under finite deformation.\n When using \n\\begin_inset Formula $\\nu<\\frac{1}{2}$\n\\end_inset\n\n,\n care must be taken not to exceed \n\\begin_inset Formula $\\varepsilon_{\\text{max}}=\\left(1-2\\nu\\right)^{-1}$\n\\end_inset\n\n,\n since the slope of \n\\begin_inset Formula $\\Phi\\left(\\varepsilon\\right)$\n\\end_inset\n\n becomes zero at that value and negative beyond it.\n \n\\end_layout\n\n\\begin_layout Subsubsection\nCustom Flow Curve\n\\end_layout\n\n\\begin_layout Standard\nThis custom method for specifying the flow curve was provided in the paper that introduced reactive plasticity \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"true\"\n\n\\end_inset\n\n,\n therefore it is called \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nPFC paper\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<nf>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of bond families \n\\begin_inset Formula $n_{f}$\n\\end_inset\n\n (\n\\begin_inset Formula $n_{f}>0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Y0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLowest yield threshold \n\\begin_inset Formula $\\Upsilon_{0}$\n\\end_inset\n\n (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-b)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Ymax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHighest yield threshold \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-b)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<w0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFraction \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n of bonds that yield at \n\\begin_inset Formula $\\Upsilon_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $0\\le w_{0}\\le1$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<we>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFraction \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n of bonds than never yield (\n\\begin_inset Formula $0\\le w_{e}\\le1$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<r>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBias factor \n\\begin_inset Formula $r$\n\\end_inset\n\n for consecutive increments of \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n over the range \n\\begin_inset Formula $\\left[\\Upsilon_{0},\\Upsilon_{\\text{max}}\\right]$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis method assumes that \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n values are evenly distributed between the \n\\shape italic\ninitial yield threshold\n\\shape default\n \n\\begin_inset Formula $\\Upsilon_{\\text{0}}$\n\\end_inset\n\n (e.g.,\n the yield stress) and a \n\\shape italic\nfinal yield threshold\n\\shape default\n \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n,\n parameters which may be identified from a stress strain curve (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-b).\n Beyond \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n,\n the material either behaves as if it is perfectly plastic (a scenario which may be valid around the ultimate strength,\n for example),\n or it transitions to a linear hardening regime.\n \n\\end_layout\n\n\\begin_layout Standard\nThe family mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n govern the influence of each family on the overall material response.\n The simplest model for \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n involves specifying the mass fraction of the first yielding family \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n,\n which controls the slope of the initial post-yield response (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na),\n and then evenly weighting the rest of the bond families.\n In cases where the material transitions to a linear hardening regime,\n we can recover this behavior by adding one more bond family,\n \n\\begin_inset Formula $\\beta=n_{f}$\n\\end_inset\n\n,\n that never yields,\n thus remaining elastic.\n The associated mass fraction \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n\n\\shape italic\n\\emph on\n for \n\\begin_inset Formula $\\beta=n_{f}$\n\\end_inset\n\n is called the \n\\emph default\nelastic mass fraction\n\\shape default\n and denoted \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n;\n a non-zero value for this parameter may be specified whenever we wish to include linear hardening behavior (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nb).\n The effect of the mass fraction parameters \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n is explored parametrically in Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nc and Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nd,\n respectively.\n In general,\n most ductile materials have \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n very close to unity,\n which provides hardening behavior over a finite strain range.\n As \n\\begin_inset Formula $w_{0}\\to1$\n\\end_inset\n\n the stress-strain behavior approaches perfect plasticity.\n In contrast,\n when \n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n,\n the material response becomes perfectly plastic once the final yield threshold \n\\begin_inset Formula $\\Upsilon_{\\text{max}}$\n\\end_inset\n\n has been exceeded.\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/FigReactivePlasticityYieldModel.png\n\tlyxscale 20\n\twidth 6in\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nModeling uniaxial stress-strain curves using the parameters given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Kinematic-Hardening-Response\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Identification of parameters on idealized stress-strain curves showing (a) a plateau in the stress,\n or (b) exhibiting a region of linear hardening.\n The yielding behavior is fully described by the set of parameters \n\\begin_inset Formula $\\left\\{ n_{f},\\Upsilon_{0},\\Upsilon_{\\text{max}},w_{0},w_{e},r\\right\\} $\n\\end_inset\n\n.\n Parametric variations of (c) \n\\begin_inset Formula $w_{0}$\n\\end_inset\n\n and (d) \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n illustrate their influence on the stress-strain response;\n other parameters are held fixed.\n In (c-d) \n\\begin_inset Formula $n_{f}=10$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Upsilon_{0}=600$\n\\end_inset\n\n MPa,\n \n\\begin_inset Formula $\\Upsilon_{\\text{max}}=1000$\n\\end_inset\n\n MPa,\n and \n\\begin_inset Formula $r=1$\n\\end_inset\n\n.\n In (c),\n \n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n and in (d) \n\\begin_inset Formula $w_{0}=0.75$\n\\end_inset\n\n.\n\\color black\n\n\\begin_inset CommandInset label\nLatexCommand label\nname \"fig:hardening-family-parametric-curves\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\nAs \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n increases,\n a region of linear hardening is seen on a plot of the true stress against strain.\n For most ductile materials,\n \n\\begin_inset Formula $w_{e}$\n\\end_inset\n\n is usually \n\\begin_inset Formula $0$\n\\end_inset\n\n or on the order of \n\\begin_inset Formula $0.001$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIt is also possible to introduce a \n\\shape italic\nbias factor\n\\shape default\n \n\\begin_inset Formula $r$\n\\end_inset\n\n,\n which allows a geometric progression rather than uniform spacing of the apparent yield thresholds and family mass fractions.\n The bias factor \n\\begin_inset Formula $r$\n\\end_inset\n\n has the effect of modifying the shape of the hardening region between \n\\begin_inset Formula $\\Upsilon_{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Upsilon_{\\text{max }}$\n\\end_inset\n\n (Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\nb).\n The full set of material parameters is then given by \n\\begin_inset Formula $\\left\\{ n_{f},\\Upsilon_{0},\\Upsilon_{\\text{max}},w_{0},w_{e},r\\right\\} $\n\\end_inset\n\n.\n Setting \n\\begin_inset Formula $r=1$\n\\end_inset\n\n recovers the uniform distribution.\n Figure\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"fig:hardening-family-parametric-curves\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\na-b graphically describes the influence of each parameter on simplified stress-strain curves,\n showing how these parameters may be extracted from experimental data.\n\\end_layout\n\n\\begin_layout Standard\nDefault values in FEBio are \n\\begin_inset Formula $n_{f}=1$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{0}=1$\n\\end_inset\n\n,\n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Upsilon_{0}=\\Upsilon_{\\text{max}}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $r=0.9$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nSpecification of Reactive Elasto-Plastic Solid\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Reactive-Elastoplastic-Solid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type of a reactive elasto-plastic solid with kinematic hardening is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive plasticity\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<isochoric>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag (0 or 1) for enforcing isochoric plastic flow (1 implies \n\\begin_inset Formula $\\det\\mathbf{F}_{\\beta}^{ys}=1,\\,\\forall\\beta$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<yield_criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<flow_curve>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the plastic flow curve (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Plastic-Flow-Curve\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <elastic> tag encloses a description of the constitutive relation of the intact elastic material and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for unconstrained materials.\n The <yield_criterion> tag encloses a description of the yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <flow_curve> tag encloses a description of the relation between bond mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n and apparent yield stresses \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n for the bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Plastic-Flow-Curve\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.The default value in FEBio is isochoric=1.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Idealized elastic-perfectly plastic response of steel (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>8.05e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>200e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.30</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Y0>450</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Annealed mild steel (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>7.85e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>205e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.29</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>15</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Y0>220</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Ymax>490</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <w0>0.973</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <we>0</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <r>1</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Annealed copper (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>7.764e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>120e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.34</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>15</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Y0>60</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Ymax>288</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <w0>0.988</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <we>0</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <r>1</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Unaged maraging steel (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>8.00e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>165e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.33</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>22</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Y0>398</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Ymax>1010</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <w0>0</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <we>0</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <r>0.9</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Annealed aluminum 1100 (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>2.71e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>68e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.33</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>18</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Y0>63</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Ymax>112</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <w0>0.994</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <we>0</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <r>0.6</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Mild steel (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"natural neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>2.71e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>206e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.30</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>15</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <emin>0.0008403</emin>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <emax>1.3</emax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <plastic_response>545.46*(0.011024+eps)^0.2589</plastic_response>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Steel (in-lbf-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Steel\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"natural neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>29911000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC user\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <plastic_response type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <interpolate>SMOOTH</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point> 0.002,\n 59822 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point> 0.002841,\n 64450 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point> 0.00469,\n 68500 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point> 0.00953,\n 72000 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point> 0.0193,\n 75000 </point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </plastic_response>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n elastic-perfectly plastic response,\n using PFC math flow curve\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Elastoplastic Math\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>200000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>1</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <e0>0.001</e0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <emax>1</emax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <plastic_response type=\"math\">200</plastic_response>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\end_layout\n\n\\begin_layout LyX-Code\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Elasto-Plastic Damage Mechanics\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Plasticity-with-damage\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPlastic deformation (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) is often coupled with damage,\n as the finite deformation and plastic flow of a loaded material typically induces some amount of failure.\n Within the constrained reactive mixture framework adopted in FEBio,\n damage is produced by bonds breaking permanently (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n which reduces the generation mass fractions \n\\begin_inset Formula $w_{\\beta}^{s}$\n\\end_inset\n\n or \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Zimmerman21\"\nliteral \"true\"\n\n\\end_inset\n\n.\n In our treatment of elastoplastic damage we assume that both intact and yielded bonds may become damaged.\n Intact bonds belong to the \n\\begin_inset Formula $s-$\n\\end_inset\n\ngeneration which is present at \n\\begin_inset Formula $t=-\\infty$\n\\end_inset\n\n.\n Once yielding occurs,\n all successive generations of that family are labeled as yielded bonds \n\\begin_inset Formula $y$\n\\end_inset\n\n.\n This distinction is necessary so we can then distinguish between damage to intact bonds (elastic damage) and damage to yielded bonds (plastic damage),\n since intact bonds which get damaged never have the ability to yield.\n Damage to intact bonds is represented by the reaction\n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{s}\\to\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n is the species associated with broken bonds in bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n It may represent some initial damage value for a material with defects,\n or damage due to intermolecular failure of bonds that never yielded;\n this damage alters the value of \n\\begin_inset Formula $w_{\\beta}^{s}$\n\\end_inset\n\n and we refer to it as \n\\emph on\nelastic damage\n\\emph default\n.\n Damage to yielded bonds represents \n\\emph on\nplastic damage\n\\emph default\n;\n it is represented by the reaction \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{y}\\to\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n and it alters the value of \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n.\n The mechanism of damage and the failure measure may be different for these two types of bonds,\n particularly since a stress- or energy-based failure measure may not be appropriate for plastic damage.\n It is important to note that the nature of the plastic deformation described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n remains unchanged.\n Damage modifies the material behavior by reducing the fraction of bonds in various generations,\n which scales the response accordingly.\n\\end_layout\n\n\\begin_layout Standard\nIn a reactive constrained mixture framework the insertion of damage into the reactive plasticity formulation is straightforward.\n Since bonds break permanently in a damage reaction,\n there is no need to define a function of state \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n to describe a (non-existing) reformed configuration.\n Furthermore,\n the specific free energy of broken bonds is zero.\n The scalar \n\\shape italic\nelastic damage criterion\n\\shape default\n \n\\begin_inset Formula $\\Xi^{e}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n,\n which is taken to have the same functional form for all bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n is the analog to the yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n for plasticity.\n As shown in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the main contrast with reactive plasticity is that not all bonds in the family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n break simultaneously at a single \n\\shape italic\nelastic damage threshold\n\\shape default\n \n\\begin_inset Formula $\\Xi_{m\\beta}^{e}$\n\\end_inset\n\n.\n Instead,\n the fraction of broken bonds varies as a function of \n\\begin_inset Formula $\\Xi^{e}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n,\n denoted by \n\\begin_inset Formula $F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n,\n such that \n\\begin_inset Formula $0\\le F^{e}\\left(\\Xi_{\\beta}^{e}\\right)\\le1$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n is a function of state;\n it must be a monotonically increasing function of its argument to satisfy the Clausius-Duhem inequality \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Nims16\"\nliteral \"true\"\n\n\\end_inset\n\n.\n We may view \n\\begin_inset Formula $F^{e}$\n\\end_inset\n\n as a cumulative distribution function (CDF),\n whose corresponding probability distribution function (PDF) represents the probability of damage at a particular value of \n\\begin_inset Formula $\\Xi_{\\beta}^{e}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nTheoretical Formulation\n\\end_layout\n\n\\begin_layout Standard\nHere,\n we sketch the structure of the elastoplastic damage theory in FEBio.\n Since each bond family in reactive plasticity yields all at once,\n we can easily split an elastoplastic damage theory into two parts to represent elastic and plastic damage regimes.\n Assume that the first yielding reaction for bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n occurs at time \n\\begin_inset Formula $t=u_{\\beta}$\n\\end_inset\n\n.\n Prior to this initial yielding,\n the damage behavior described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n applies,\n and the material composition is generally a mixture of intact (\n\\begin_inset Formula $\\text{\\ensuremath{\\mathcal{E}_{\\beta}^{s}}}$\n\\end_inset\n\n) and broken (\n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n) bonds satisfying the reaction \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{s}\\to\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n.\n The corresponding bond mass fractions satisfy \n\\begin_inset Formula $1=w_{\\beta}^{s}+w_{\\beta}^{b}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{\\beta}^{y}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $w_{\\beta}^{b}=F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n is the elastic damage in bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n At \n\\begin_inset Formula $t=u_{\\beta}$\n\\end_inset\n\n,\n the remaining intact bonds \n\\begin_inset Formula $w_{\\beta}^{s}=1-w_{\\beta}^{b}$\n\\end_inset\n\n all yield,\n following the reaction \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{s}\\to\\mathcal{E}_{\\beta}^{y}$\n\\end_inset\n\n.\n The family mass balance is then given as \n\\begin_inset Formula $1=w_{\\beta}^{y}+w_{\\beta}^{b}$\n\\end_inset\n\n,\n since \n\\begin_inset Formula $w_{\\beta}^{s}=0$\n\\end_inset\n\n after yielding.\n \n\\end_layout\n\n\\begin_layout Standard\nFor time \n\\begin_inset Formula $t>u_{\\beta}$\n\\end_inset\n\n,\n yielded bonds may continue to yield,\n but they may also sustain damage according to the reaction \n\\begin_inset Formula $\\mathcal{E}_{\\beta}^{y}\\to\\mathcal{E}_{\\beta}^{b}$\n\\end_inset\n\n,\n which reduces their mass fraction \n\\begin_inset Formula $w_{\\beta}^{y}$\n\\end_inset\n\n.\n Damage to yielded bonds may occur based on a function of state (often described as a plastic strain,\n though it is not an observable kinematic variable),\n which is distinct from the measure of elastic damage.\n Therefore,\n we denote the \n\\shape italic\nplastic damage measure\n\\shape default\n as \n\\begin_inset Formula $\\Xi^{p}\\left(\\mathbf{F}_{\\beta}^{ys}\\right)$\n\\end_inset\n\n and its cumulative distribution function by \n\\begin_inset Formula $F^{p}\\left(\\Xi_{\\beta}^{p}\\right)$\n\\end_inset\n\n,\n under the assumption that all bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n share the same functional forms for \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $F^{p}$\n\\end_inset\n\n.\n For each bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n only the remaining undamaged fraction \n\\begin_inset Formula $1-F^{p}\\left(\\Xi_{\\beta}^{p}\\right)$\n\\end_inset\n\n of yielded bonds may break and reform as the next yielded generation.\n\\end_layout\n\n\\begin_layout Standard\nIt must be recognized that,\n just as \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n is a constitutively-prescribed function of state and does not carry the meaning of a plastic deformation gradient,\n the plastic damage measure \n\\begin_inset Formula $\\Xi^{p}\\left(\\mathbf{F}_{\\beta}^{ys}\\right)$\n\\end_inset\n\n is also a function of state.\n This quantity is called a plastic strain for convenience only.\n\\end_layout\n\n\\begin_layout Subsubsection\nStress and Damage\n\\end_layout\n\n\\begin_layout Standard\nRecognizing that damaged (broken) bonds do not store free energy,\n the mixture stress is given by\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=\\sum_{\\beta}w_{\\beta}\\left(w_{\\beta}^{s}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}^{s}\\right)+w_{\\beta}^{y}\\boldsymbol{\\sigma}_{0}\\left(\\mathbf{F}_{\\beta}^{y}\\right)\\right)\\,,\\label{eq:plasticdmg-mixture-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere the stresses \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{0}$\n\\end_inset\n\n are given by the standard hyperelasticity relation in Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:rp-hyperelasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The reactive mixture equivalent of the damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n may be evaluated for elastoplastic damage as the fraction of all bonds that are broken,\n\\begin_inset Formula \n\\begin{equation}\nD=w^{b}=\\sum_{\\beta}w_{\\beta}w_{\\beta}^{b}\\,.\\label{eq:plasticdmg-damage-variable}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nDamage Measures\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Yielded-damage-reaction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor elastic damage,\n we may use the same functional measure as proposed for plastic yielding (e.g.,\n the von Mises stress);\n this implies that the functions \n\\begin_inset Formula $\\Xi^{e}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n have the same form.\n For plastic damage of bonds in family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n we use the constitutively-determined mapping \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}$\n\\end_inset\n\n to define plastic right Cauchy-Green and Lagrange strain tensors through\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{C}_{\\beta}^{ys} & =\\left(\\mathbf{F}_{\\beta}^{ys}\\right)^{T}\\cdot\\mathbf{F}_{\\beta}^{ys}\\\\\n\\mathbf{E}_{\\beta}^{ys} & =\\frac{1}{2}\\left(\\mathbf{C}_{\\beta}^{ys}-\\mathbf{I}\\right)\n\\end{aligned}\n\\,.\n\\end{equation}\n\n\\end_inset\n\nOne possible constitutive relation for \n\\begin_inset Formula $\\Xi^{p}$\n\\end_inset\n\n,\n which remains valid for general deformations,\n is to set it equal to the \n\\shape italic\neffective plastic strain\n\\shape default\n \n\\begin_inset Formula $e_{\\beta}^{p}$\n\\end_inset\n\n for the various bond families \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\ne_{\\beta}^{p}=\\sqrt{\\frac{2}{3}\\dev\\mathbf{E}_{\\beta}^{ys}:\\dev\\mathbf{E}_{\\beta}^{ys}}\\,.\\label{eq:plasticdmg-effective-plastic-strain}\n\\end{equation}\n\n\\end_inset\n\nIn a numerical implementation,\n the effective plastic strain \n\\begin_inset Formula $e_{0}^{p}$\n\\end_inset\n\n of the first bond family to yield may be reported as the effective plastic strain in the entire material,\n for consistency with plastic strain measures in classical models of plasticity.\n\\end_layout\n\n\\begin_layout Standard\nQuantities in this section do not represent plastic strains or plastic strain tensors,\n though we adopt the terminology due to similarities.\n Recall that the non-observable function of state \n\\begin_inset Formula $\\mathbf{F}_{\\beta}^{ys}=\\mathbf{F}_{\\beta}^{ys}\\left(\\mathbf{F}^{s},\\rho_{r\\beta}^{\\alpha}\\right)$\n\\end_inset\n\n is a time-invariant mapping providing the reference configuration of a yielded bond \n\\begin_inset Formula $y$\n\\end_inset\n\n with respect to the reference configuration of the master constituent \n\\begin_inset Formula $s$\n\\end_inset\n\n,\n for family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n.\n The quantities \n\\begin_inset Formula $\\mathbf{C}_{\\beta}^{ys}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{E}_{\\beta}^{ys}$\n\\end_inset\n\n then also represent non-observable functions of state calculated as strain tensors.\n Consequently,\n \n\\begin_inset Formula $e_{\\beta}^{p}$\n\\end_inset\n\n is a measure of the relative motion of the reference configuration of bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n ,\n expressed as a scalar \n\\begin_inset Quotes eld\n\\end_inset\n\nstrain\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Physically,\n this amounts to the modeling assumption that once the breaking-and-reforming process takes a bond family out of a local neighborhood centered about its original position,\n the bond begins to degrade with further breaking-and-reforming processes.\n That each of these quantities exists for every bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n emphasizes the lack of any true or unique plastic strain measure in this framework.\n\\end_layout\n\n\\begin_layout Subsection\nSpecification of Reactive Elasto-Plastic Damage Solid\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Reactive-Elastoplastic-Damage\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type of a reactive elasto-plastic damage solid with kinematic hardening is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive plastic damage\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<isochoric>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFlag (0 or 1) for enforcing isochoric plastic flow (1 implies \n\\begin_inset Formula $\\det\\mathbf{F}_{\\beta}^{ys}=1,\\,\\forall\\beta$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<yield_criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<flow_curve>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the plastic flow curve (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Plastic-Flow-Curve\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<plastic_damage>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the plastic damage CDF \n\\begin_inset Formula $F^{p}\\left(\\Xi_{\\beta}^{p}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<plastic_damage_criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the plastic damage criterion \n\\begin_inset Formula $\\Xi^{p}\\left(\\mathbf{F}_{\\beta}^{ys}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic_damage>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic damage CDF \n\\begin_inset Formula $F^{e}\\left(\\Xi_{\\beta}^{e}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<elastic_damage_criterion>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the elastic damage criterion \n\\begin_inset Formula $\\Xi^{e}\\left(\\mathbf{F}^{s}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <elastic> tag encloses a description of the constitutive relation of the intact elastic material and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for unconstrained materials.\n The <yield_criterion> tag encloses a description of the yield criterion \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <flow_curve> tag encloses a description of the relation between bond mass fractions \n\\begin_inset Formula $w_{\\beta}$\n\\end_inset\n\n and apparent yield stresses \n\\begin_inset Formula $\\Upsilon_{\\beta}$\n\\end_inset\n\n for the bond family \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Plastic-Flow-Curve\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The plastic and elastic damage CDFs,\n in the <plastic_damage> and <elastic_damage> tags respectively,\n may be selected from Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Cumulative-Distribution-Function\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n whereas the corresponding damage criteria <plastic_damage_criterion> and <elastic_damage_criterion> may be selected from Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Damage-Yield-Criterion\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Default values are \n\\begin_inset Formula $n_{f}=1$\n\\end_inset\n\n,\n \n\\begin_inset Formula $w_{0}=1$\n\\end_inset\n\n,\n\\begin_inset Formula $w_{e}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\Upsilon_{0}=\\Upsilon_{\\text{max}}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $r=0.9$\n\\end_inset\n\n,\n and isochoric=1.\n If only plastic damage needs to be modeled,\n the tags <elastic_damage> and <elastic_damage_criterion> may be omitted.\n If <plastic_damage> and <plastic_damage_criterion> are also omitted the material response becomes identical to the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive plasticity\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Reactive-Elastoplastic-Solid\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n \n\\shape default\n\\emph on\nA-533 Grade B class 1 nuclear pressure vessel steel\n\\shape italic\n\\emph default\n (mm-N-s units)\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>8.00e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>206.9e3</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.29</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <yield_criterion type=\"\n\\shape italic\n\\emph on\nDC von Mises stress\n\\shape default\n\\emph default\n\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <nf>10</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Y0>458</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Ymax>730</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <w0>0.985</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <we>0.00072</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <r>1</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <plastic_damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mu>3.3</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>3</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </plastic_damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <plastic_damage_criterion type=\"DC octahedral shear strain\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Viscoplasticity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Viscoplasticity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA reactive viscoelastic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Viscoelastic-Solid\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) consists of an \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material,\n which imparts the equilibrium elastic response,\n and a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nbond\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material,\n which imparts the viscous response.\n The \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n response may be attributed to strong molecular bonds (e.g.,\n covalent bonds) in a material region,\n that don't break in the absence of damage,\n whereas the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nbond\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n response is attributed to weak molecular bonds (e.g.,\n hydrogen bonds) in the material region,\n that break under loading,\n and reform in a stress-free state.\n\\end_layout\n\n\\begin_layout Standard\nTo model viscoplasticity in a reactive framework,\n one may use a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive plasticity\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Plasticity\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) as the type of \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n material appearing in a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive viscoelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material.\n The \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nbond\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material may be modeled as recommended previously (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Viscoelastic-Solid\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\emph on\nExample:\n Reactive visoelasticity with reactive plasticity\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" name=\"Material2\" type=\"reactive viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<wmin>0.001</wmin>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<kinetics>1</kinetics>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<trigger>0</trigger>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<emin>0</emin>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<elastic type=\"reactive plasticity\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<rtol>0.0001</rtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<secant_tangent>1</secant_tangent>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<E>73000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<yield_criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Y0>330</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Ymax>489</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<w0>0.967</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<we>0</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<nf>15</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<r>1</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<bond type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<E>7300</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</bond>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<relaxation type=\"relaxation-exponential\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<tau>1.5</tau>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</relaxation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nReactive Viscoelasticity and Viscoplasticity with Damage\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Reactive-Viscoelasticity-Damage\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA reactive viscoelastic material may undergo damage as detailed in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian23b\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Reactive viscoelastic materials (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Viscoelastic-Solid\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) consist of an \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material,\n which imparts the equilibrium elastic response,\n and a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nbond\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material,\n which imparts the viscous response.\n The \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n response may be attributed to strong molecular bonds (e.g.,\n covalent bonds) in a material region,\n that don't break in the absence of damage,\n whereas the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nbond\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n response is attributed to weak molecular bonds (e.g.,\n hydrogen bonds) in the material region,\n that break under loading,\n and reform in a stress-free state.\n\\end_layout\n\n\\begin_layout Standard\nIt follows from this definition that the \n\\begin_inset Quotes eld\n\\end_inset\n\nelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n material of a \n\\begin_inset Quotes eld\n\\end_inset\n\nreactive viscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n material may undergo damage,\n as described for example in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n This damage is cause by the breaking of strong molecular bonds in the material region,\n and the associated damage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n represents the mass fraction of the material region whose strong bonds have broken.\n However,\n when strong molecular bonds break,\n the gap between molecular constituents of that material region may increase,\n also preventing the formation of weak moleculer bonds (e.g.,\n since hydrogen bonds may only form when there is sufficient proximity between moeities associated with these bonds).\n Based on the work of Simo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87\"\nliteral \"false\"\n\n\\end_inset\n\n,\n we can adopt the constitutive assumption that the fraction of weak bonds that fails is the same as the fraction \n\\begin_inset Formula $D$\n\\end_inset\n\n of strong bonds that fail \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian23b\"\nliteral \"false\"\n\n\\end_inset\n\n.\n Therefore,\n the stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n produced by strong bonds in a material region may be attenuated as \n\\begin_inset Formula $\\left(1-D\\right)\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n just like the stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{b}$\n\\end_inset\n\n produced by weak bonds may be reduced to \n\\begin_inset Formula $\\left(1-D\\right)\\boldsymbol{\\sigma}^{b}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\nreactive viscoelastic\n\\begin_inset Quotes erd\n\\end_inset\n\n material in FEBio automatically recognizes if its \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n component happens to be a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic damage\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material,\n or a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nreactive plastic damage\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material.\n In either of these cases,\n it will prescribe the damage \n\\begin_inset Formula $D$\n\\end_inset\n\n calculated from the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n response to the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nbond\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n response.\n In other words,\n it is possible to model viscoelastic damage and viscoplastic damage in FEBio.\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\emph on\nExample:\n Reactive viscoelasticity with elastic damage\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material1\" type=\"reactive viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<wmin>0.001</wmin>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<kinetics>1</kinetics>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<trigger>0</trigger>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<emin>0.0001</emin>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<elastic type=\"elastic damage\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<E>1000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<alpha>2.5</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<mu>125</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<ploc>0</ploc>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<bond type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<E>2000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</bond>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<relaxation type=\"relaxation-exponential\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<tau>5</tau>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</relaxation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\emph on\nExample:\n Reactive viscoelasticity with reactive plastic damage\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" name=\"Material2\" type=\"reactive viscoelastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<wmin>0.001</wmin>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<kinetics>1</kinetics>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<trigger>0</trigger>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<emin>0</emin>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<elastic type=\"reactive plastic damage\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<rtol>0.0001</rtol>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<secant_tangent>1</secant_tangent>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<E>73000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<yield_criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<flow_curve type=\"PFC paper\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Y0>330</Y0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Ymax>489</Ymax>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<w0>0.967</w0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<we>0</we>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<nf>15</nf>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<r>1</r>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</flow_curve>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<plastic_damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<alpha>0.65</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<mu>7.95</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<ploc>0</ploc>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</plastic_damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<plastic_damage_criterion type=\"DC octahedral shear strain\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<bond type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<E>7300</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</bond>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<relaxation type=\"relaxation-exponential\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<tau>1.5</tau>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</relaxation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nViscoelastic Damage\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Viscoelastic-Damage\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA viscoelastic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Viscoelastic-Solids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) is fundamentally a continuum representation of the Standard Solid in classical viscoelasticity,\n valid under finite deformation in the context of quasilinear viscoelasticity.\n The standard solid consists of a spring \n\\begin_inset Formula $e$\n\\end_inset\n\n in parallel with a Maxwell element (a spring \n\\begin_inset Formula $s$\n\\end_inset\n\n and dashpot \n\\begin_inset Formula $d$\n\\end_inset\n\n in series).\n The spring stiffness in the Maxwell element is assumed to be equal to \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n times the stiffness of the parallel spring.\n The elastic spring response may be a nonlinear function of the strain but the viscoelastic response must be linear (i.e.,\n it must obey Boltzmann's superposition principle,\n equivalent to stating that the Maxwell element response is governed by a linear ordinary differential equation,\n or ODE).\n In general,\n a viscoelastic material of this type may undergo damage,\n using the framework provided by Simo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In FEBio we model this framework by adopting additional constitutive assumptions not provided by Simo,\n but outlined in \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian23b\"\nliteral \"false\"\n\n\\end_inset\n\n and in the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n The FEBio formulation for viscoelastic damage relies on a proper formulation of the strain energy density of the Standard Solid,\n which is then employed in the analysis of viscoelastic damage.\n The strain energy density \n\\begin_inset Formula $\\Psi_{r}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n of the viscoelastic solid is given by \n\\begin_inset Formula \n\\[\n\\Psi_{r}\\left(\\mathbf{F}\\right)=\\Psi_{r}^{e}\\left(\\mathbf{F}\\right)+\\gamma\\Psi_{r}^{e}\\left(\\mathbf{F}_{s}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the (observable) deformation gradient of the material (also of the elastic spring \n\\begin_inset Formula $e$\n\\end_inset\n\n),\n \n\\begin_inset Formula $\\mathbf{F}_{s}$\n\\end_inset\n\n is the deformation gradient associated with the Maxwell spring \n\\begin_inset Formula $s$\n\\end_inset\n\n (an internal variable which evolves based on a first-order ODE),\n and \n\\begin_inset Formula $\\Psi_{r}^{e}$\n\\end_inset\n\n is the strain energy density of the elastic spring.\n Let \n\\begin_inset Formula $\\mathbf{S}^{e}$\n\\end_inset\n\n denote the second Piola-Kirchhoff stress in the elastic spring,\n obtained from the standard hyperelasticity relation \n\\begin_inset Formula $\\mathbf{S}^{e}=\\partial\\Psi_{r}\\left(\\mathbf{F}\\right)/\\partial\\mathbf{E}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{E}$\n\\end_inset\n\n is the Lagrange strain tensor evaluated from \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n.\n It follows from the above relation for the strain energy density that \n\\begin_inset Formula $\\mathbf{S}\\left(\\mathbf{F}\\right)=\\mathbf{S}^{e}\\left(\\mathbf{F}\\right)+\\gamma\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nDamage \n\\begin_inset Formula $D^{e}$\n\\end_inset\n\n is produced in the elastic spring \n\\begin_inset Formula $e$\n\\end_inset\n\n according to the framework described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n based on a user-defined damage criterion \n\\begin_inset Formula $\\Xi^{e}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n (such as von Mises stress or maximum principal normal stress),\n such that \n\\begin_inset Formula $0\\le D^{e}\\le1$\n\\end_inset\n\n represents the range from zero to complete damage.\n The resulting stress in the elastic spring is now given by the attenuated value \n\\begin_inset Formula $\\left(1-D^{e}\\right)\\mathbf{S}^{e}$\n\\end_inset\n\n\n\\begin_inset Formula $\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n.\n Based on Simo \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Simo87\"\nliteral \"false\"\n\n\\end_inset\n\n,\n FEBio assumes that the damage in the Maxwell element is similarly given by \n\\begin_inset Formula $\\left(1-D^{e}\\right)\\gamma\\mathbf{S}^{e}\\left(\\mathbf{F}_{s}\\right)$\n\\end_inset\n\n.\n Thus,\n the stress in a damaged viscoelastic material in FEBio is evaluated from \n\\begin_inset Formula $\\left(1-D^{e}\\right)\\mathbf{S}\\left(\\mathbf{F}\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe specification of a material with \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nviscoelastic damage\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n takes the same form as that of a viscoelastic material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Viscoelastic-Solids\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n except that the \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n component should now consist of an \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nelastic damage\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n material type,\n whose specification is outlined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n Viscoelastic damage\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Material1\" type=\"viscoelastic damage\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<t1>5</t1>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<t2>1</t2>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<t3>1</t3>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<t4>1</t4>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<t5>1</t5>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<t6>1</t6>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g0>1</g0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g1>2</g1>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g2>0</g2>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g3>0</g3>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g4>0</g4>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g5>0</g5>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<g6>0</g6>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<elastic type=\"elastic damage\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<E>1000</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<damage type=\"CDF Weibull\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<Dmax>1</Dmax>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<alpha>2.5</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<mu>125</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t\t<ploc>0</ploc>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t</damage>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<criterion type=\"DC von Mises stress\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nMultigeneration Solids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Multigeneration-Solids\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis type of material \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian10a\"\nliteral \"true\"\n\n\\end_inset\n\n implements a mechanism for multigenerational interstitial growth of solids whereby each growth generation \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n has a distinct reference configuration \n\\begin_inset Formula $\\mathbf{X}^{\\gamma}$\n\\end_inset\n\n determined at the time \n\\begin_inset Formula $t^{\\gamma}$\n\\end_inset\n\n of its deposition.\n Therefore,\n the solid matrix of a growing material consists of a multiplicity of intermingled porous bodies,\n each representing a generation \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n,\n all of which are constrained to move together in the current configuration \n\\begin_inset Formula $\\mathbf{x}$\n\\end_inset\n\n.\n The deformation gradient of each generation is \n\\begin_inset Formula $\\mathbf{F}^{\\gamma}=\\partial\\mathbf{x}/\\partial\\mathbf{X}^{\\gamma}$\n\\end_inset\n\n.\n The first generation (\n\\begin_inset Formula $\\gamma=1)$\n\\end_inset\n\n is assumed to be present at time \n\\begin_inset Formula $t^{1}=0$\n\\end_inset\n\n,\n therefore its reference configuration is \n\\begin_inset Formula $\\mathbf{X}^{1}\\equiv\\mathbf{X}$\n\\end_inset\n\n and its deformation gradient \n\\begin_inset Formula $\\mathbf{F}^{1}=\\partial\\mathbf{x}/\\partial\\mathbf{X}^{1}$\n\\end_inset\n\n is equivalent to \n\\begin_inset Formula $\\mathbf{F}=\\partial\\mathbf{x}/\\partial\\mathbf{X}$\n\\end_inset\n\n.\n Each generation's reference configuration \n\\begin_inset Formula $\\mathbf{X}^{\\gamma}$\n\\end_inset\n\n has a one-to-one mapping \n\\begin_inset Formula $\\mathbf{F}^{\\gamma1}=\\partial\\mathbf{X}^{\\gamma}/\\partial\\mathbf{X}^{1}$\n\\end_inset\n\n with the master reference configuration \n\\begin_inset Formula $\\mathbf{X}^{1}$\n\\end_inset\n\n,\n which is that of the first generation.\n This mapping is postulated based on a constitutive assumption with regard to that generation's state of stress at the time of its deposition.\n In the current implementation,\n the newly deposited generation is assumed to be in a stress-free state,\n even though the underlying material is in a loaded configuration.\n Therefore,\n the mapping between generation \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n and the first generation is simply \n\\begin_inset Formula $\\mathbf{F}^{\\gamma1}=\\mathbf{F}^{1}\\left(\\mathbf{X}^{1},t^{\\gamma}\\right)\\equiv\\mathbf{F}\\left(\\mathbf{X},t^{\\gamma}\\right)$\n\\end_inset\n\n.\n In other words,\n when generation \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n first comes into existence,\n its reference configuration is the current configuration at time \n\\begin_inset Formula $t^{\\gamma}$\n\\end_inset\n\n.\n Note that \n\\begin_inset Formula $\\mathbf{F}^{\\gamma1}$\n\\end_inset\n\n is a time-invariant (though not necessarily homogeneous) quantity that is determined uniquely at the birth of a generation.\n\\end_layout\n\n\\begin_layout Standard\nThe state of stress in a multigeneration solid is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}=\\sum\\limits_{\\gamma}\\frac{1}{J^{\\gamma1}}\\boldsymbol{\\sigma}^{\\gamma}\\left(\\mathbf{F}^{\\gamma}\\right)\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{\\gamma}\\left(\\mathbf{F}^{\\gamma}\\right)$\n\\end_inset\n\n is the state of stress in the generation \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n,\n as would be evaluated from a strain energy density function whose reference configuration is \n\\begin_inset Formula $\\mathbf{X}^{\\gamma}$\n\\end_inset\n\n.\n In the above equation,\n \n\\begin_inset Formula $J^{\\gamma1}=\\det\\mathbf{F}^{\\gamma1}$\n\\end_inset\n\n and the factor \n\\begin_inset Formula $1/J^{\\gamma1}$\n\\end_inset\n\n ensures that the strain energy density of each generation is properly normalized the volume of the material in the master reference configuration \n\\begin_inset Formula $\\mathbf{X}^{1}$\n\\end_inset\n\n,\n when summing up the stresses in all the generations.\n\\end_layout\n\n\\begin_layout Standard\nMultigeneration solids typically exhibit residual stresses when \n\\begin_inset Formula $\\mathbf{F}^{\\gamma1}$\n\\end_inset\n\n is inhomogeneous.\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Multigeneration Solids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Multigen\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a multigeneration solid is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nmultigeneration\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\nAs of FEBio version 2.0,\n the format for definining multi-generation materials has changed.\n The previous format where the generations are defined in the \n\\shape italic\nGlobals \n\\shape default\nsection is no longer supported.\n\\end_layout\n\n\\end_inset\n\n.\n This material describes a mixture of elastic solids,\n each created in a specific generation.\n It is a container for any combination of the elastic materials described.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<generation>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefinition of a generation.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <generation> tag defines a new generation.\n It takes the following child elements.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<start_time>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Quotes eld\n\\end_inset\n\nbirth\n\\begin_inset Quotes erd\n\\end_inset\n\n-time for this generation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the constitutive model for this generation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nsolid \n\\shape default\nelement defins the solid matrix constitutive relation and associated material properties.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Growing Solid\" type=\"multigeneration\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <generation id=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <start_time>0.0</start_time>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solid type=\"Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <beta>0.1</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </generation>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <generation id=\"2\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <start_time>1.0</start_time>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solid type=\"Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <E>1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <beta>0.1</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </generation>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe corresponding value of \n\\begin_inset Formula $t^{\\gamma}$\n\\end_inset\n\n for each of the generations is provided in the <Globals> section.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Generations>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <gen id=\"1\">0.0</gen>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <gen id=\"2\">1.0</gen>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Generations>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nBiphasic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBiphasic materials may be used to model a porous medium consisting of a mixture of a porous-permeable solid matrix and an interstitial fluid.\n Each of these mixture constituents is assumed to be intrinsically incompressible.\n This means that the true densities of the solid and fluid are invariant in space and time;\n this assumption further implies that a biphasic mixture will undergo zero volume change when subjected to a hydrostatic fluid pressure.\n Yet the mixture is compressible because the pores of the solid matrix may gain or lose fluid under general loading conditions.\n The Cauchy stress \n\\begin_inset Formula ${\\rm \\boldsymbol{\\sigma}}$\n\\end_inset\n\n in a biphasic material is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}=-p\\mathbf{I}+{\\rm \\boldsymbol{\\sigma}}^{e}\\,,\\label{eq:bp-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n is the interstitial fluid pressure and \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the stress resulting from the deformation of the porous solid matrix.\n Therefore,\n the constitutive relation of the solid matrix should be chosen from the list of unconstrained materials provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The user is referred to the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n for a general description of the biphasic theory.\n\\end_layout\n\n\\begin_layout Standard\nIn addition to selecting a constitutive relation for the solid matrix,\n a constitutive relation must also be selected for the hydraulic permeability of the interstitial fluid flowing within the porous deformable solid matrix.\n The hydraulic permeability relates the volumetric flux of the fluid relative to the solid,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n,\n to the interstitial fluid pressure gradient,\n \n\\begin_inset Formula $\\nabla p$\n\\end_inset\n\n,\n according to\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{w}=-\\mathbf{k}\\cdot\\grad p\\,,\\label{eq:bp-flux}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n is the hydraulic permeability tensor.\n (Note that this expression does not account for the contribution of external body forces on the fluid.)\n\\end_layout\n\n\\begin_layout Standard\nThe governing equations for biphasic materials are the mixture momentum balance under quasi-static conditions,\n in the absence of external body force,\n \n\\begin_inset Formula \n\\begin{equation}\n\\divg{\\rm \\boldsymbol{\\sigma}}=-\\grad p+\\divg\\boldsymbol{\\sigma}^{e}=\\mathbf{0}\\,,\\label{eq:bp-momentum-balance}\n\\end{equation}\n\n\\end_inset\n\nand the mixture mass balance,\n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=0\\,,\\label{eq:bp-mixture-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{v}^{s}$\n\\end_inset\n\n is the solid velocity.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Biphasic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Biphasic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a biphasic material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nbiphasic\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the solid matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n in the reference configuration (\n\\begin_inset Formula $0<\\varphi_{r}^{s}<1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fluid_density>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid density \n\\begin_inset Formula $\\rho_{T}^{w}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<permeability>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the hydraulic permeability\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solvent_supply>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fluid supply \n\\begin_inset Formula $\\hat{\\varphi}^{w}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tau>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStabilization parameter \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <solid> tag encloses a description of the solid matrix constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <permeability> tag encloses a description of the permeability constitutive relation and associated material properties,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Permeability-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The parameter <phi0> must be greater than 0 (no solid) and less than 1 (no porosity).\n The volume fraction of fluid (also known as the porosity) in the reference configuration is given by \n\\begin_inset Formula $1-\\varphi_{r}^{s}$\n\\end_inset\n\n.\n The fluid density \n\\begin_inset Formula $\\rho_{T}^{w}$\n\\end_inset\n\n specified in <fluid_density> and the solid density \n\\begin_inset Formula $\\rho_{T}^{s}$\n\\end_inset\n\n specified in <density> within the <solid> tag are needed only when body forces are prescribed.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Biphasic tissue\" type=\"biphasic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid name=\"Elasticity\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.2</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <permeability name=\"Permeability\" type=\"perm-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n (description of permeability material)\n\\end_layout\n\n\\begin_layout LyX-Code\n  </permeability>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe stabilization parameter \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n can be used to reduced oscillations in the fluid pressure field produced under certain loading conditions,\n when the finite element mesh is relatively coarse in the vicinity of a free-draining boundary (a boundary on which the fluid pressure is set to zero).\n When this occurs it is best to refine the mesh (create thinner elements in the direction normal to the boundary) to overcome these oscillations.\n When mesh refinement is not feasible or practical,\n one may use a non-zero stabilization parameter \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n.\n FEBio uses an implementation based on the formulation of Aguilar et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Aguilar08\"\nliteral \"false\"\n\n\\end_inset\n\n.\n In our adaptation of this formulation the parameter \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n has units of time.\n Its value is best estimated by identifying the thickness \n\\begin_inset Formula $h$\n\\end_inset\n\n of elements on the free-draining boundary,\n the characteristic modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n of the solid matrix and hydraulic permeability \n\\begin_inset Formula $k$\n\\end_inset\n\n in these elements.\n Then \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n may be set approximately to \n\\begin_inset Formula $h^{2}/\\left(E\\cdot k\\right)$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nPermeability Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Permeability-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPermeability materials provide a constitutive relation for the hydraulic permeability of a biphasic material.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Isotropic-Permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for constant isotropic permeability is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nperm-const-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic permeability \n\\begin_inset Formula $k$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis isotropic material model uses the biphasic theory for describing the time-dependent material behavior of materials that consist of both a solid and fluid phase \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mow80\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nWhen the permeability is isotropic,\n \n\\begin_inset Formula \n\\[\n\\mathbf{k}=k\\,\\mathbf{I}\n\\]\n\n\\end_inset\n\nFor this material model,\n \n\\begin_inset Formula $k$\n\\end_inset\n\n is constant.\n Generally,\n this assumption is only reasonable when strains are small.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<permeability name=\"Permeability\" type=\"perm-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm>0.001</perm>\n\\end_layout\n\n\\begin_layout LyX-Code\n</permeability>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nExponential Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Exponential-Isotropic-Permeability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for exponential isotropic permeability is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nperm-exp-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic hydraulic permeability \n\\begin_inset Formula $k_{0}$\n\\end_inset\n\n in the reference state\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential strain-dependence coefficient \n\\begin_inset Formula $M$\n\\end_inset\n\n (\n\\begin_inset Formula $M\\ge0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis isotropic material model has the general form\n\\begin_inset Formula \n\\[\n\\mathbf{k}=k\\left(J\\right)\\,\\mathbf{I}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n is the determinant of the deformation gradient.\n For this material model,\n\\begin_inset Formula \n\\[\nk\\left(J\\right)=k_{0}\\exp\\left(M\\frac{J-1}{J-\\varphi_{0}}\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi_{0}$\n\\end_inset\n\n is the referential solid volume fraction of the porous solid matrix.\n Pore closure occurs as \n\\begin_inset Formula $J$\n\\end_inset\n\n approaches \n\\begin_inset Formula $\\varphi_{0}$\n\\end_inset\n\n from above,\n in which case the permeability reduces to zero,\n\\begin_inset Formula \n\\[\n\\lim_{J\\to\\varphi_{0}}k\\left(J\\right)=0\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/FigPermExpIso.png\n\tlyxscale 50\n\tscale 50\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nExponential isotropic permeability,\n showing dependence of \n\\begin_inset Formula $k\\left(J\\right)/k_{0}$\n\\end_inset\n\n on material parameter \n\\begin_inset Formula $M$\n\\end_inset\n\n,\n when \n\\begin_inset Formula $\\varphi_{0}=0.2$\n\\end_inset\n\n and \n\\begin_inset Formula $\\varphi_{0}<J\\le1.2$\n\\end_inset\n\n.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nRepresentative variations of \n\\begin_inset Formula $k\\left(J\\right)/k_{0}$\n\\end_inset\n\n are shown in the figure.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<permeability name=\"Permeability\" type=\"perm-exp-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm>0.001</perm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M>1.5</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n</permeability>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nHolmes-Mow\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:perm-Holmes-Mow\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for Holmes-Mow permeability is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nperm-Holmes-Mow\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic hydraulic permeability \n\\begin_inset Formula $k_{0}$\n\\end_inset\n\n in the reference state\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential strain-dependence coefficient \n\\begin_inset Formula $M$\n\\end_inset\n\n (\n\\begin_inset Formula $M\\ge0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha\\ge0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis isotropic material is similar to the constant isotropic permeability material described above,\n except that it uses a strain-dependent permeability tensor \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Holmes90\"\nliteral \"true\"\n\n\\end_inset\n\n:\n \n\\begin_inset Formula \n\\[\n\\mathbf{k}=k\\left(J\\right)\\mathbf{I},\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\[\nk\\left(J\\right)=k_{0}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha}e^{\\frac{1}{2}M\\left(J^{2}-1\\right)}\\,,\n\\]\n\n\\end_inset\n\nand \n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where\n\\series bold\n \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n \n\\series default\nis the deformation gradient.\n Here,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential solid volume fraction in the current configuration and \n\\begin_inset Formula $\\varphi_{0}=\\varphi_{r}^{s}\\left(0\\right)$\n\\end_inset\n\n is its value in the reference configuration.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a permeability material of the Holmes-Mow type.\n\\end_layout\n\n\\begin_layout LyX-Code\n<permeability type=\"perm-Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm>0.001</perm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M>1.5</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n</permeability>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReferentially Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Isotropic-Permeabi\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a biphasic material with strain-dependent permeability which is isotropic in the reference configuration is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nperm-ref-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic permeability \n\\begin_inset Formula $k_{0r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic permeability \n\\begin_inset Formula $k_{1r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic permeability \n\\begin_inset Formula $k_{2r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential strain-dependence coefficient \n\\begin_inset Formula $M$\n\\end_inset\n\n (\n\\begin_inset Formula $M\\geqslant0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent permeability tensor that accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\[\n\\mathbf{k}=\\left(k_{0r}\\mathbf{I}+\\frac{k_{1r}}{J^{2}}\\mathbf{b}+\\frac{k_{2r}}{J^{4}}\\mathbf{b}^{2}\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha}e^{M\\left(J^{2}-1\\right)/2},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient,\n and \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n Here,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential solid volume fraction in the current configuration and \n\\begin_inset Formula $\\varphi_{0}=\\varphi_{r}^{s}\\left(0\\right)$\n\\end_inset\n\n is its value in the reference configuration.Note that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I})$\n\\end_inset\n\n is isotropic and given by \n\\begin_inset Formula $\\mathbf{k}=\\left(k_{0r}+k_{1r}+k_{2r}\\right)\\mathbf{I}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<permeability name=\"Permeability\" type=\"perm-ref-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm0>0.001</perm0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm1>0.005</perm1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm2>0.002</perm2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M>1.5</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n</permeability>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReferentially Orthotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Orthotropic-Permea\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a poroelastic material with strain-dependent permeability which is orthotropic in the reference configuration is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nperm-ref-ortho\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic hydraulic permeability \n\\begin_inset Formula $k_{0r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm1> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic permeabilities \n\\begin_inset Formula $k_{1r}^{a}$\n\\end_inset\n\n along orthogonal directions (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm2> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic permeabilities \n\\begin_inset Formula $k_{2r}^{a}$\n\\end_inset\n\n along orthogonal directions (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic exponential strain-dependence coefficient \n\\begin_inset Formula $M_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $M_{0}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\northotropic exponential strain-dependence coefficient \n\\begin_inset Formula $M_{a}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n,\n \n\\begin_inset Formula $M_{a}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic power-law exponent \n\\begin_inset Formula $\\alpha_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{0}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent \n\\begin_inset Formula $\\alpha_{a}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha_{a}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent permeability tensor that accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\[\n\\mathbf{k}=k_{0}\\mathbf{I}+\\sum\\limits_{a=1}^{3}k_{1}^{a}\\mathbf{m}_{a}+k_{2}^{a}\\left(\\mathbf{m}_{a}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}_{a}\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}k_{0} & =k_{0r}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha_{^{0}}}e^{M_{^{0}}\\left(J^{2}-1\\right)/2}\\\\\nk_{1}^{a} & =\\frac{k_{1r}^{a}}{J^{2}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha_{^{a}}}e^{M_{^{a}}\\left(J^{2}-1\\right)/2}\\\\\nk_{2}^{a} & =\\frac{k_{2r}^{a}}{2J^{4}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha_{^{a}}}e^{M_{a}\\left(J^{2}-1\\right)/2}\n\\end{aligned}\n,\\quad a=1,2,3\\,.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient.\n \n\\begin_inset Formula $\\mathbf{m}_{a}$\n\\end_inset\n\n are second order tensors representing the spatial structural tensors describing the orthogonal planes of symmetry,\n given by \n\\begin_inset Formula \n\\[\n\\mathbf{m}_{a}=\\mathbf{F}\\cdot\\left(\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}\\right)\\cdot\\mathbf{F}^{T},\\quad a=1-3,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n are orthonormal vectors normal to the planes of symmetry (defined as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Orthotropic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Here,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential solid volume fraction in the current configuration and \n\\begin_inset Formula $\\varphi_{0}=\\varphi_{r}^{s}\\left(0\\right)$\n\\end_inset\n\n is its value in the reference configuration.\n Note that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I})$\n\\end_inset\n\n is given by \n\\begin_inset Formula $\\mathbf{k}=k_{0r}\\mathbf{I}+\\sum\\limits_{a=1}^{3}\\left(k_{1r}^{a}+k_{2r}^{a}\\right)\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<permeability name=\"Permeability\" type=\"perm-ref-ortho\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm0>0.001</perm0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm1>0.01,\n 0.02,\n 0.03</perm1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm2>0.001,\n 0.002,\n 0.003</perm2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M0>0.5</M0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M>1.5,\n 2.0,\n 2.5</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha0>1.5</alpha0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2,\n 2.5,\n 3</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n</permeability>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReferentially Transversely Isotropic Permeability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Trans-Iso-Perm\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a biphasic material with strain-dependent permeability which is transversely isotropic in the reference configuration is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nperm-ref-trans-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic hydraulic permeability \n\\begin_inset Formula $k_{0r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm1A>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naxial hydraulic permeability \n\\begin_inset Formula $k_{1r}^{A}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm2A>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naxial hydraulic permeability \n\\begin_inset Formula $k_{2r}^{A}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm1T>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntransverse hydraulic permeability \n\\begin_inset Formula $k_{1r}^{T}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<perm2T>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntransverse hydraulic permeability \n\\begin_inset Formula $k_{2r}^{T}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic exponential strain-dependence coefficient \n\\begin_inset Formula $M_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $M_{0}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<MA>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naxial exponential strain-dependence coefficient \n\\begin_inset Formula $M_{A}$\n\\end_inset\n\n (\n\\begin_inset Formula $M_{A}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<MT>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntransverse exponential strain-dependence coefficient \n\\begin_inset Formula $M_{T}$\n\\end_inset\n\n (\n\\begin_inset Formula $M_{T}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic power-law exponent \n\\begin_inset Formula $\\alpha_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{0}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alphaA>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naxial power-law exponent \n\\begin_inset Formula $\\alpha_{A}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{A}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alphaT>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntransverse power-law exponent \n\\begin_inset Formula $\\alpha_{T}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{T}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent permeability tensor that accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}\\mathbf{k} & =k_{0r}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha_{0}}e^{M_{^{0}}\\left(J^{2}-1\\right)/2}\\mathbf{I}\\\\\n & +\\left(\\frac{k_{1r}^{T}}{J^{2}}\\left(\\mathbf{b}-\\mathbf{m}\\right)+\\frac{k_{2r}^{T}}{2J^{4}}\\left[2\\mathbf{b}^{2}-\\left(\\mathbf{m}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}\\right)\\right]\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha_{T}}e^{M_{T}\\left(J^{2}-1\\right)/2}\\\\\n & +\\left(\\frac{1}{J^{2}}k_{1r}^{A}\\mathbf{m}+\\frac{1}{2J^{4}}k_{2r}^{A}\\left(\\mathbf{m}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}\\right)\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{0}}\\right)^{\\alpha_{A}}e^{M_{A}\\left(J^{2}-1\\right)/2}\n\\end{aligned}\n,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient,\n and \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n \n\\begin_inset Formula $\\mathbf{m}$\n\\end_inset\n\n is a second order tensor representing the spatial structural tensor describing the axial direction,\n given by \n\\begin_inset Formula \n\\[\n\\mathbf{m}=\\mathbf{F}\\cdot\\left(\\mathbf{V}\\otimes\\mathbf{V}\\right)\\cdot\\mathbf{F}^{T}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}$\n\\end_inset\n\n is a unit vector along the axial direction (defined as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Here,\n \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential solid volume fraction in the current configuration and \n\\begin_inset Formula $\\varphi_{0}=\\varphi_{r}^{s}\\left(0\\right)$\n\\end_inset\n\n is its value in the reference configuration.\n Note that the permeability in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I})$\n\\end_inset\n\n is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{k_{|F=I}}=\\left(k_{0r}+k_{1r}^{T}+k_{2r}^{T}\\right)\\mathbf{I}+\\left(k_{1r}^{A}-k_{1r}^{T}+k_{2r}^{A}-k_{2r}^{T}\\right)\\left(\\mathbf{V}\\otimes\\mathbf{V}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<permeability name=\"Permeability\" type=\"perm-ref-trans-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm0>0.002</perm0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm1A>0.01</perm1A>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm2A>0.01</perm2A>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm1T>0.001</perm1T>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <perm2T>0.05</perm2T>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M0>1.0</M0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <MA>0.5</MA>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <MT>1.5</MT>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha0>1.0</alpha0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alphaA>0.5</alphaA>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alphaT>2.0</alphaT>\n\\end_layout\n\n\\begin_layout LyX-Code\n</permeability>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nFluid Supply Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Supply-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFluid supply materials may be used to simulate an external source of fluid,\n such as supply from microvasculature that is not modeled explicitly.\n The fluid supply term,\n \n\\begin_inset Formula $\\hat{\\varphi}^{w}$\n\\end_inset\n\n,\n appears in the mass balance relation for the mixture,\n \n\\begin_inset Formula \n\\[\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\hat{\\varphi}^{w}\\,.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $\\hat{\\varphi}^{w}$\n\\end_inset\n\n has units of reciprocal time [\n\\series bold\nt\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{-1}}$\n\\end_inset\n\n];\n it represents the rate at which the volume fraction of fluid changes with time.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nStarling Equation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Starling-Equation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for Starling's equation for fluid supply is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nStarling\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kp>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic filtration coefficient,\n \n\\begin_inset Formula $k_{p}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<pv>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid pressure in external source,\n \n\\begin_inset Formula $p_{v}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fluid supply is given by Starling's equation,\n \n\\begin_inset Formula \n\\[\n\\hat{\\varphi}^{w}=k_{p}\\left(p_{v}-p\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure in the biphasic material.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a fluid supply material of the Starling type.\n\\end_layout\n\n\\begin_layout LyX-Code\n<solvent_supply type=\"Starling\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kp>0.001</kp>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pv>0.1</pv>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solvent_supply>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nBiphasic-Solute Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Biphasic-Solute-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBiphasic-solute materials may be used to model the transport of a solvent and a solute in a neutral porous solid matrix,\n under isothermal conditions.\n Each of these mixture constituents is assumed to be intrinsically incompressible.\n This means that their true densities are invariant in space and time;\n this assumption further implies that a biphasic-solute mixture will undergo zero volume change when subjected to a hydrostatic fluid pressure.\n Yet the mixture is compressible because the pores of the solid matrix may gain or lose fluid under general loading conditions.\n Therefore,\n the constitutive relation of the solid matrix should be chosen from the list of unconstrained materials provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The volume fraction of the solute is assumed to be negligible relative to the volume fractions of the solid or solvent.\n This means that the mixture will not change in volume as the solute concentration changes.\n As the solute transports through the mixture,\n it may experience frictional interactions with the solvent and the solid.\n This friction may act as a hindrance to the solute transport,\n or may help convect the solute through the mixture.\n The distinction between frictional exchanges with the solvent and solid is embodied in the specification of two diffusivities for the solute:\n The free diffusivity,\n which represents diffusivity in the absence of a solid matrix (frictional exchange only with the solvent) and the mixture diffusivity,\n which represents the combined frictional interactions with the solvent and solid matrix.\n The user is referred to the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n for a more detailed description of the biphasic-solute theory.\n\\end_layout\n\n\\begin_layout Standard\nDue to steric volume exclusion and short-range electrostatic interactions,\n the solute may not have access to all of the pores of the solid matrix.\n In other words,\n only a fraction \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n of the pores is able to accommodate a solute of a particular size (\n\\begin_inset Formula $0<\\kappa\\leqslant1)$\n\\end_inset\n\n.\n Furthermore,\n the activity \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n of the solute (the extent by which the solute concentration influences its chemical potential) may be similarly altered by the surrounding porous solid matrix.\n Therefore,\n the combined effects of volume exclusion and attenuation of activity may be represented by the effective solubility \n\\begin_inset Formula $\\tilde{\\kappa}=\\kappa/\\gamma$\n\\end_inset\n\n,\n such that the chemical potential \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n of the solute is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mu=\\mu_{0}\\left(\\theta\\right)+\\frac{R\\theta}{M}\\ln\\frac{c}{\\tilde{\\kappa}}\\,.\\label{eq:bs-solute-chemical-potential}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n is the solute chemical potential at some reference temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n;\n \n\\begin_inset Formula $c$\n\\end_inset\n\n is the solute concentration on a solution-volume basis (number of moles of solute per volume of interstitial fluid in the mixture);\n \n\\begin_inset Formula $M$\n\\end_inset\n\n is the solute molecular weight (an invariant quantity);\n and \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant.\n In a biphasic-solute material,\n a constitutive relation is needed for \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n;\n in general,\n \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n may be a function of the solid matrix strain and the solute concentration.\n In FEBio,\n the dependence of the effective solubility on the solid matrix strain is currently constrained to a dependence on \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nIn a biphasic-solute material,\n the interstitial fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is influenced by both mechanical and chemical environments.\n In other words,\n this pressure includes both mechanical and osmotic contributions,\n the latter arising from the presence of the solute.\n The solvent mechano-chemical potential \n\\begin_inset Formula $\\tilde{\\mu}^{w}$\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mu}^{w}=\\mu_{0}^{w}\\left(\\theta\\right)+\\frac{1}{\\rho_{T}^{w}}\\left(p-R\\theta\\Phi c\\right)\\,,\\label{eq:bs-solvent-chemical-potential}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu_{0}^{w}$\n\\end_inset\n\n is the solvent chemical potential at some reference temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n;\n \n\\begin_inset Formula $\\rho_{T}^{w}$\n\\end_inset\n\n is the true density of the solvent (an invariant property for an intrinsically incompressible fluid);\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is the osmotic coefficient which represents the extent by which the solute concentration influences the solvent chemical potential.\n In a biphasic-solute material,\n a constitutive relation is needed for \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n;\n in general,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n may be a function of the solid matrix strain and the solute concentration.\n In FEBio,\n the dependence of the osmotic coefficient on the solid matrix strain is currently constrained to a dependence on \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe solute mechano-chemical potential is nearly equal to its chemical potential because the solute volume fraction is assumed to be negligible.\n In general,\n momentum and energy balances evaluated across a boundary surface in a biphasic-solute mixture require that the mechano-chemical potentials of solvent and solute be continuous across that surface.\n These continuity requirements are enforced automatically in FEBio by defining the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and solute concentration \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{p} & =p-R\\theta\\Phi c\\\\\n\\tilde{c} & =\\frac{c}{\\tilde{\\kappa}}\n\\end{aligned}\n\\,.\\label{eq:bs-effective-p-c}\n\\end{equation}\n\n\\end_inset\n\nTherefore,\n nodal variables in FEBio consist of the solid matrix displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n,\n and the effective solute concentration \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n.\n Essential boundary conditions must be imposed on these variables,\n and not on the actual pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n or concentration \n\\begin_inset Formula $c$\n\\end_inset\n\n.\n (In a biphasic material however,\n since \n\\begin_inset Formula $c=0$\n\\end_inset\n\n,\n the effective and actual fluid pressures are the same,\n \n\\begin_inset Formula $p=\\tilde{p}$\n\\end_inset\n\n.)\n\\end_layout\n\n\\begin_layout Standard\nThe mixture stress in a biphasic-solute material is given by \n\\begin_inset Formula $\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the stress arising from the solid matrix strain.\n The mixture traction on a surface with unit outward normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n.\n This traction is continuous across the boundary surface.\n Therefore,\n the corresponding natural boundary condition for a biphasic-solute mixture is \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{0}$\n\\end_inset\n\n.\n (In other words,\n if no boundary condition is imposed on the solid matrix displacement or mixture traction,\n the natural boundary condition is in effect.)\n\\end_layout\n\n\\begin_layout Standard\nThe natural boundary conditions for the solvent and solute are similarly \n\\begin_inset Formula $\\mathbf{w}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{j}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n is the volumetric flux of solvent relative to the solid and \n\\begin_inset Formula $\\mathbf{j}$\n\\end_inset\n\n is the molar flux of solute relative to the solid.\n In general,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{j}$\n\\end_inset\n\n are given by \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w} & =-\\mathbf{\\tilde{k}}\\cdot\\left(\\nabla\\tilde{p}+R\\theta\\frac{\\tilde{\\kappa}}{d_{0}}\\mathbf{d}\\cdot\\nabla\\tilde{c}\\right)\\\\\n\\mathbf{j} & =\\tilde{\\kappa}\\mathbf{d}\\cdot\\left(-\\varphi^{w}\\nabla\\tilde{c}+\\frac{\\tilde{c}}{d_{0}}\\mathbf{w}\\right)\n\\end{aligned}\n\\,,\\label{eq:bs-fluxes-w-j}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mathbf{k}}=\\left[\\mathbf{k}^{-1}+\\frac{R\\theta}{\\varphi^{w}}\\frac{\\tilde{\\kappa}c}{d_{0}}\\left(\\mathbf{I}-\\frac{\\mathbf{d}}{d_{0}}\\right)\\right]^{-1}\\label{eq:bs-effective-perm}\n\\end{equation}\n\n\\end_inset\n\nis the effective hydraulic permeability of the interstitial fluid solution (solvent and solute) through the porous solid matrix;\n \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n is the hydraulic permeability of the solvent through the porous solid matrix;\n \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n is the solute diffusivity through the mixture (frictional interactions with solvent and solid);\n and \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n is the solute free diffusivity (frictional interactions with solvent only).\n \n\\begin_inset Formula $\\varphi^{w}\\approx1-\\varphi^{s}$\n\\end_inset\n\n is the solid matrix porosity in the current configuration.\n The above expressions for the solvent and solute flux do not account for external body forces.\n\\end_layout\n\n\\begin_layout Standard\nThe governing equations for a biphasic-solute material are the momentum balance for the mixture,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bp-momentum-balance\"\nnolink \"false\"\n\n\\end_inset\n\n,\n the mass balance for the mixture,\n which reduces to Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bp-mixture-mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n under the assumption of dilute solutions,\n and the mass balance for the solute,\n \n\\begin_inset Formula \n\\begin{equation}\n\\frac{\\partial\\left(\\varphi^{w}c\\right)}{\\partial t}+\\divg\\left(\\mathbf{j}+\\varphi^{w}c\\mathbf{v}^{s}\\right)=0\\,.\\label{eq:bs-solute-mass-balance}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGuidelines for Biphasic-Solute Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Guidelines-for-Biphasic-Solute\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Boundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Boundary-Conditions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn most analyses,\n it may be assumed that the ambient fluid pressure in the external environment is zero,\n thus \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n,\n where the subscripted asterisk is used to denote environmental conditions.\n The ambient solute concentration may be represented by \n\\begin_inset Formula $c_{\\ast}$\n\\end_inset\n\n.\n It follows that the effective fluid pressure in the external environment is \n\\begin_inset Formula $\\tilde{p}_{\\ast}=-R\\theta\\Phi_{\\ast}c_{\\ast}$\n\\end_inset\n\n and the effective concentration is \n\\begin_inset Formula $\\tilde{c}_{\\ast}=c_{\\ast}/\\tilde{\\kappa}_{\\ast}$\n\\end_inset\n\n.\n Therefore,\n in biphasic-solute analyses,\n whenever the external environment contains a solute at a concentration of \n\\begin_inset Formula $c_{\\ast}$\n\\end_inset\n\n,\n the user must remember to prescribe non-zero boundary conditions for the effective solute concentration \n\\shape italic\nand\n\\shape default\n the effective fluid pressure.\n\\end_layout\n\n\\begin_layout Standard\nLetting \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n also implies that prescribed mixture normal tractions (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mixture-Normal-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n) represent only the traction above ambient conditions.\n Note that users are not obligated to assume that \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n.\n However,\n if a non-zero value is assumed for the ambient pressure,\n then users must remember to incorporate this non-zero value whenever prescribing mixture normal tractions.\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Initial Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Initial-Conditions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a biphasic-solute material is initially exposed to a given external environment with effective pressure \n\\begin_inset Formula $\\tilde{p}_{\\ast}$\n\\end_inset\n\n and effective concentration \n\\begin_inset Formula $\\tilde{c}_{\\ast}$\n\\end_inset\n\n,\n the initial conditions inside the material should be set to \n\\begin_inset Formula $\\tilde{p}=\\tilde{p}_{\\ast}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}=\\tilde{c}_{\\ast}$\n\\end_inset\n\n in order to produce the correct initial state.\n The values of \n\\begin_inset Formula $\\tilde{p}_{\\ast}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}_{\\ast}$\n\\end_inset\n\n should be evaluated as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Boundary-Conditions-1\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Biphasic-Solute Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-BS-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a biphasic-solute material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nbiphasic-solute\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n Constitutive relations must be provided for the solid matrix,\n the hydraulic permeability \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n,\n the solute diffusivities \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n and \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n,\n the effective solubility \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n and the osmotic coefficient \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n.\n Therefore,\n the following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solid matrix \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n in the reference configuration \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<permeability>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the hydraulic permeability \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<osmotic_coefficient>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the osmotic coefficient \n\\begin_inset Formula $\\Phi\\quad\\mathbf{d}d_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solute>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solute properties \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <solid> tag encloses a description of the solid matrix constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The solid volume fraction in the reference configuration,\n <phi0>,\n must be greater than 0 (no solid) and less than 1 (only solid).\n The volume fraction of fluid (also known as the porosity) in the reference configuration is given by \n\\begin_inset Formula $1-\\varphi_{0}$\n\\end_inset\n\n.\n The <permeability> tag encloses a description of the permeability constitutive relation and associated material properties,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Permeability-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe <solute> tag provides a description of the solute in the biphasic-solute mixture.\n This tag includes the required \n\\shape italic\nsol\n\\shape default\n attribute,\n which should reference a solute \n\\shape italic\nid\n\\shape default\n from the <Solutes> description in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The following parameters must be defined in this description:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diffusivity>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solute diffusivities \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n and \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solubility>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solute effective solubility \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <diffusivity> and <solubility> tags enclose descriptions of materials that may be selected from the lists presented in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Diffusivity-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solubility-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n,\n respectively.\n Each solute tag must include a \n\\begin_inset Quotes eld\n\\end_inset\n\nsol\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Biological tissue\" type=\"biphasic-solute\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid name=\"Elasticity\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.2</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <permeability name=\"Permeability\" type=\"perm-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n (description of permeability material)\n\\end_layout\n\n\\begin_layout LyX-Code\n  </permeability>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <osmotic_coefficient name=\"Osmotic\" type=\"osm-coef-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n (description of osmotic coefficient material)\n\\end_layout\n\n\\begin_layout LyX-Code\n  </osmotic_coefficient>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solute sol=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <diffusivity name=\"Diffusivity\" type=\"diff-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      ...\n (description of diffusivity material)\n\\end_layout\n\n\\begin_layout LyX-Code\n    </diffusivity>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solubility name=\"Solubility\" type=\"solub-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      ...\n (description of solubility material)\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solubility>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nWhen a biphasic-solute material is employed in an analysis,\n it is also necessary to specify the values of the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n [\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n/\n\\series bold\nn\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nT\n\\series default\n] and absolute temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n [\n\\series bold\nT\n\\series default\n] under <Constants> in the <Globals> section,\n using a self-consistent set of units.\n A solute must also be defined in the <Solutes> section,\n whose id should be associated with the \n\\begin_inset Quotes eld\n\\end_inset\n\nsol\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute in the solute material description.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>298</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Solutes>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solute id=\"1\" name=\"neutral\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>0</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Solutes>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\nIt is also possible to create models with biphasic-solute materials that use different solutes in different regions.\n In that case,\n introduce additional solute entries in the <Solutes> section and refer to those solute ids in the biphasic-solute material descriptions.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDiffusivity Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Diffusivity-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nDiffusivity materials provide a constitutive relation for the solute diffusivity in a biphasic-solute material.\n In general,\n the diffusivity tensor \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n may be a function of strain and solute concentration.\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Isotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Isotropic-Diffusivity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for constant isotropic diffusivity materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ndiff-const-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<free_diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfree diffusivity \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n of solute (diffusivity in solution)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconstant isotropic diffusivity \n\\begin_inset Formula $d$\n\\end_inset\n\n of solute in biphasic-solute mixture\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the permeability is isotropic,\n \n\\begin_inset Formula \n\\[\n\\mathbf{d}=d\\,\\mathbf{I}\n\\]\n\n\\end_inset\n\nFor this material model,\n \n\\begin_inset Formula $d$\n\\end_inset\n\n is constant.\n This assumption is only true when strains are small.\n Note that the user must specify \n\\begin_inset Formula $d\\leqslant d_{0}$\n\\end_inset\n\n,\n since a solute cannot diffuse through the biphasic-solute mixture faster than in free solution.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<diffusivity name=\"Permeability\" type=\"diff-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <free_diff>1e-9</ free_diff >\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff>0.5e-9</diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n</diffusivity>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Orthotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Orthotropic-Diffusivity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for constant orthotropic diffusivity materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ndiff-const-ortho\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<free_diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfree diffusivity \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n of solute (diffusivity in solution)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivities \n\\begin_inset Formula $d^{a}$\n\\end_inset\n\n along orthogonal directions (\n\\begin_inset Formula $a=1,2,3)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the permeability is orhotropic,\n \n\\begin_inset Formula \n\\[\n\\mathbf{d}=\\sum\\limits_{a=1}^{3}d^{a}\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n are orthonormal vectors normal to the planes of symmetry (defined as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n).\n For this material model,\n \n\\begin_inset Formula $d^{a}$\n\\end_inset\n\n's are constant.\n Therefore this model should be used only when strains are small.\n Note that the user must specify \n\\begin_inset Formula $d^{a}\\leqslant d_{0}$\n\\end_inset\n\n,\n since a solute cannot diffuse through the biphasic-solute mixture faster than in free solution.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<diffusivity name=\"Permeability\" type=\"diff-const-ortho\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <free_diff>0.005</free_diff >\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff>0.002,0.001,0.003</diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n</diffusivity>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReferentially Isotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Isotropic-Diffusiv\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a strain-dependent diffusivity tensor which is isotropic in the reference configuration is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\ndiff-ref-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<free_diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfree diffusivity \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivity \n\\begin_inset Formula $d_{0r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivity \n\\begin_inset Formula $d_{1r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivity \n\\begin_inset Formula $d_{2r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponential strain-dependence coefficient \n\\begin_inset Formula $M$\n\\end_inset\n\n (\n\\begin_inset Formula $M\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent diffusivity tensor that accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\[\n\\mathbf{d}=\\left(d_{0r}\\mathbf{I}+\\frac{d_{1r}}{J^{2}}\\mathbf{b}+\\frac{d_{2r}}{J^{4}}\\mathbf{b}^{2}\\right)\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)e^{M\\left(J^{2}-1\\right)/2},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $J$\n\\end_inset\n\nis the jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient,\n and \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n Note that the diffusivity in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I})$\n\\end_inset\n\n is isotropic and given by \n\\begin_inset Formula $\\mathbf{d}=\\left(d_{0r}+d_{1r}+d_{2r}\\right)\\mathbf{I}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<diffusivity name=\"Diffusivity\" type=\"diff-ref-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.2</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <free_diff>0.005</free_diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff0>0.001</diff0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff1>0.005</diff1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff2>0.002</diff2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M>1.5</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n</diffusivity>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nReferentially Orthotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Referentially-Orthotropic-Diffus\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a strain-dependent diffusivity which is orthotropic in the reference configuration is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\ndiff-ref-ortho\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<free_diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfree diffusivity \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic diffusivity \n\\begin_inset Formula $d_{0r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff1> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivities \n\\begin_inset Formula $d_{1r}^{a}$\n\\end_inset\n\n along orthogonal directions (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diff2> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivities \n\\begin_inset Formula $d_{2r}^{a}$\n\\end_inset\n\n along orthogonal directions (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic exponential strain-dependence coefficient \n\\begin_inset Formula $M_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $M_{0}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<M> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\northotropic exponential strain-dependence coefficient \n\\begin_inset Formula $M_{a}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n,\n \n\\begin_inset Formula $M_{a}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisotropic power-law exponent \n\\begin_inset Formula $\\alpha_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{0}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alpha>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent \n\\begin_inset Formula $\\alpha_{a}$\n\\end_inset\n\n (\n\\begin_inset Formula $a=1,2,3$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\alpha_{a}\\geqslant0$\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a strain-dependent diffusivity tensor that accommodates strain-induced anisotropy:\n \n\\begin_inset Formula \n\\[\n\\mathbf{d}=d_{0}\\mathbf{I}+\\sum\\limits_{a=1}^{3}d_{1}^{a}\\mathbf{m}_{a}+d_{2}^{a}\\left(\\mathbf{m}_{a}\\cdot\\mathbf{b}+\\mathbf{b}\\cdot\\mathbf{m}_{a}\\right),\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula \n\\[\n\\begin{aligned}d_{0} & =d_{0r}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{0}}}e^{M_{^{0}}\\left(J^{2}-1\\right)/2}\\\\\nd_{1}^{a} & =\\frac{d_{1r}^{a}}{J^{2}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{a}}}e^{M_{^{a}}\\left(J^{2}-1\\right)/2}\\\\\nd_{2}^{a} & =\\frac{d_{2r}^{a}}{2J^{4}}\\left(\\frac{J-\\varphi_{r}^{s}}{1-\\varphi_{r}^{s}}\\right)^{\\alpha_{^{a}}}e^{M_{a}\\left(J^{2}-1\\right)/2}\n\\end{aligned}\n,\\quad a=1,2,3\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient.\n \n\\begin_inset Formula $\\mathbf{m}_{a}$\n\\end_inset\n\n are second order tensors representing the spatial structural tensors describing the orthogonal planes of symmetry,\n given by \n\\begin_inset Formula \n\\[\n\\mathbf{m}_{a}=\\mathbf{F}\\cdot\\left(\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}\\right)\\cdot\\mathbf{F}^{T},\\quad a=1-3,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{V}_{a}$\n\\end_inset\n\n are orthonormal vectors normal to the planes of symmetry (defined as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Note that the diffusivity in the reference state (\n\\begin_inset Formula $\\mathbf{F}=\\mathbf{I})$\n\\end_inset\n\n is given by \n\\begin_inset Formula $\\mathbf{d}=d_{0r}\\mathbf{I}+\\sum\\limits_{a=1}^{3}\\left(d_{1r}^{a}+d_{2r}^{a}\\right)\\mathbf{V}_{a}\\otimes\\mathbf{V}_{a}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<diffusivity name=\"Diffusivity\" type=\"diff-ref-ortho\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.2</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <free_diff>0.005</free_diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff00>0.001</diff00>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff1>0.01,\n 0.02,\n 0.03</diff1>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diff2>0.001,\n 0.002,\n 0.003</diff2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M0>0.5</M0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <M>1.5,\n 2.0,\n 2.5</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha0>1.5</alpha0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <alpha>2,\n 2.5,\n 3</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n</diffusivity>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nAlbro Isotropic Diffusivity\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Albro-Isotropic-Diffusivity\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a porosity and concentration-dependent diffusivity which is isotropic is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\ndiff-Albro-iso\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<free_diff>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfree diffusivity \n\\begin_inset Formula $d_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cdinv>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninverse of characteristic concentration for concentration-dependence \n\\begin_inset Formula $c_{D}^{-1}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n/\n\\series bold\nn\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<alphad> \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncoefficient of porosity-dependence \n\\begin_inset Formula $\\alpha_{D}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis material uses a porosity and concentration-dependent diffusivity tensor that remains isotropic with deformation:\n \n\\begin_inset Formula \n\\[\n\\mathbf{d}=d_{0}\\exp\\left(-\\alpha_{D}\\frac{1-\\varphi^{w}}{\\varphi^{w}}-\\frac{c}{c_{D}}\\right)\\mathbf{I},\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $c_{D}^{-1}=1/c_{D}$\n\\end_inset\n\n and the porosity \n\\begin_inset Formula $\\varphi^{w}$\n\\end_inset\n\n varies with deformation according to the kinematic constraint \n\\begin_inset Formula \n\\[\n\\varphi^{w}=1-\\frac{\\varphi_{r}^{s}}{J}\\,.\n\\]\n\n\\end_inset\n\n\n\\begin_inset Formula $J$\n\\end_inset\n\n is the Jacobian of the deformation,\n i.e.\n \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{F}$\n\\end_inset\n\n is the deformation gradient and \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the referential solid volume fraction.\n Here,\n \n\\begin_inset Formula $c$\n\\end_inset\n\n represents the actual concentration of the solute whose diffusivity is given by \n\\begin_inset Formula $\\mathbf{d}$\n\\end_inset\n\n.\n This constitutive relation is based on the experimental findings reported by Albro et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Albro09\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<solute sol=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <diffusivity type=\"diff-Albro-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <free_diff>123e-6</free_diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alphad>18</alphad>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <cdinv>0.0625</cdinv>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </diffusivity>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solubility type=\"solub-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solub>1</solub>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solubility>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solute>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSolubility Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solubility-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Solubility\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Solubility\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for constant solubility materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolub-const\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solub>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolubility \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material model,\n \n\\begin_inset Formula $\\tilde{\\kappa}$\n\\end_inset\n\n is constant.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<solubility name=\"Solubility\" type=\"solub-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solub>1</solub>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solubility>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nOsmotic Coefficient Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Osmotic-Coefficient-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Osmotic Coefficient\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Osmotic-Coefficient\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for constant osmotic coefficient materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nosm-coef-const\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<osmcoef>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOsmotic coefficient \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this material model,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is constant.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<osmotic_coefficient name=\"Osmotic coefficient\" type=\"osm-coef-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <osmcoef>1</osmcoef>\n\\end_layout\n\n\\begin_layout LyX-Code\n</osmotic_coefficient>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nTriphasic and Multiphasic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Triphasic-Multiphasic-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTriphasic materials may be used to model the transport of a solvent and a pair of monovalent salt counter-ions (two solutes with charge numbers \n\\begin_inset Formula $+$\n\\end_inset\n\n1 and -1) in a charged porous solid matrix,\n under isothermal conditions.\n Multiphasic materials may be used to model the transport of a solvent and any number of neutral or charged solutes;\n a triphasic mixture is a special case of a multiphasic mixture.\n Each of the mixture constituents is assumed to be intrinsically incompressible.\n This means that their true densities are invariant in space and time;\n this assumption further implies that a multiphasic mixture will undergo zero volume change when subjected to a hydrostatic fluid pressure.\n Yet the mixture is compressible because the pores of the solid matrix may gain or lose fluid under general loading conditions.\n Therefore,\n the constitutive relation of the solid matrix should be chosen from the list of unconstrained materials provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The volume fraction of the solutes is assumed to be negligible relative to the volume fractions of the solid or solvent.\n This means that the mixture will not change in volume as the solute concentrations change.\n As the solutes transport through the mixture,\n they may experience frictional interactions with the solvent and the solid.\n This friction may act as a hindrance to the solute transport,\n or may help convect the solutes through the mixture.\n The distinction between frictional exchanges with the solvent and solid is embodied in the specification of two diffusivities for each solute:\n The free diffusivity,\n which represents diffusivity in the absence of a solid matrix (frictional exchange only with the solvent) and the mixture diffusivity,\n which represents the combined frictional interactions with the solvent and solid matrix.\n The user is referred to the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n for a more detailed description of triphasic and multiphasic theory.\n\\end_layout\n\n\\begin_layout Standard\nDue to steric volume exclusion and short-range electrostatic interactions,\n a solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n may not have access to all of the pores of the solid matrix.\n In other words,\n only a fraction \n\\begin_inset Formula $\\kappa^{\\alpha}$\n\\end_inset\n\n of the pores is able to accommodate solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (\n\\begin_inset Formula $0<\\kappa^{\\alpha}\\leqslant1)$\n\\end_inset\n\n.\n Furthermore,\n the activity \n\\begin_inset Formula $\\gamma^{\\alpha}$\n\\end_inset\n\n of solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n (the extent by which the solute concentration influences its chemical potential) may be similarly altered by the surrounding porous solid matrix.\n Therefore,\n the combined effects of volume exclusion and attenuation of activity may be represented by the effective solubility \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}=\\kappa^{\\alpha}/\\gamma^{\\alpha}$\n\\end_inset\n\n,\n such that the chemical potential \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n of the solute is given by \n\\begin_inset Formula \n\\begin{equation}\n\\mu^{\\alpha}=\\mu_{0}^{\\alpha}\\left(\\theta\\right)+\\frac{R\\theta}{M^{\\alpha}}\\ln\\frac{c^{\\alpha}}{\\hat{\\kappa}^{\\alpha}}\\,.\\label{eq:mp-solute-chemical-potential}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $\\mu_{0}^{\\alpha}$\n\\end_inset\n\n is the solute chemical potential at some reference temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n;\n \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n is the solute concentration on a solution-volume basis (number of moles of solute per volume of interstitial fluid in the mixture);\n \n\\begin_inset Formula $M^{\\alpha}$\n\\end_inset\n\n is the solute molecular weight (an invariant quantity);\n and \n\\begin_inset Formula $R$\n\\end_inset\n\n is the universal gas constant.\n In a triphasic material,\n a constitutive relation is needed for \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}$\n\\end_inset\n\n;\n in general,\n \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}$\n\\end_inset\n\n may be a function of the solid matrix strain and the solute concentration.\n In FEBio,\n the dependence of the effective solubility on the solid matrix strain is currently constrained to a dependence on \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe solid matrix of a multiphasic material may be charged and its charge density is given by \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n.\n This charge density may be either negative or positive.\n The charge density varies with the deformation,\n increasing when the pore volume decreases.\n Based on the balance of mass for the solid,\n \n\\begin_inset Formula \n\\begin{equation}\nc^{F}=\\frac{1-\\varphi_{r}^{s}}{J-\\varphi_{r}^{s}}c_{r}^{F},\\label{eq:mp-CFD-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n is the solid volume fraction and \n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n is the fixed charge density in the reference configuration.\n\\end_layout\n\n\\begin_layout Standard\nIn the multiphasic theory it is assumed that electroneutrality is satisfied at all times.\n In other words,\n the net charge of the mixture is always zero (neutral).\n This electroneutrality condition is represented by a constraint equation on the ion concentrations,\n \n\\begin_inset Formula \n\\begin{equation}\nc^{F}+\\sum\\limits_{\\alpha}z^{\\alpha}c^{\\alpha}=0\\,,\\label{eq:mp-electroneutrality}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $z^{\\alpha}$\n\\end_inset\n\n is the charge number of solute \n\\begin_inset Formula $\\alpha z^{-}=-1$\n\\end_inset\n\n.\n Since the concentrations of the cation and anion inside the triphasic material are not the same,\n an electrical potential difference is produced between the interstitial and external environments.\n The electric potential in the triphasic mixture is denoted by \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n and its effect combines with the chemical potential of each solute to produce the electrochemical potential \n\\begin_inset Formula $\\tilde{\\mu}^{\\alpha}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mu}^{\\alpha}=\\mu_{0}^{\\alpha}\\left(\\theta\\right)+\\frac{R\\theta}{M^{\\alpha}}\\ln\\frac{c^{\\alpha}}{\\hat{\\kappa}^{\\alpha}}+\\frac{z^{\\alpha}}{M^{\\alpha}}F_{c}\\psi\\,.\\label{eq:mp-solute-ecp}\n\\end{equation}\n\n\\end_inset\n\nIn this expression,\n \n\\begin_inset Formula $F_{c}$\n\\end_inset\n\n represents Faraday's constant.\n It is also possible to rearrange this expression as \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mu}^{\\alpha}=\\mu_{0}^{\\alpha}\\left(\\theta\\right)+\\frac{R\\theta}{M^{\\alpha}}\\ln\\left[\\exp\\left(z^{\\alpha}\\frac{F_{c}\\psi}{R\\theta}\\right)\\frac{c^{\\alpha}}{\\hat{\\kappa}^{\\alpha}}\\right]\\,.\\label{eq:mp-solute-ecp-redux}\n\\end{equation}\n\n\\end_inset\n\nIn a multiphasic material,\n the interstitial fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is influenced by both mechanical and chemical environments.\n In other words,\n this pressure includes both mechanical and osmotic contributions,\n the latter arising from the presence of the solutes.\n The solvent mechano-chemical potential \n\\begin_inset Formula $\\tilde{\\mu}^{w}$\n\\end_inset\n\n is given by \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\mu}^{w}=\\mu_{0}^{w}\\left(\\theta\\right)+\\frac{1}{\\rho_{T}^{w}}\\left(p-R\\theta\\Phi\\sum\\limits_{\\alpha}c^{\\alpha}\\right)\\,,\\label{eq:mp-solven-mcp}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mu_{0}^{w}$\n\\end_inset\n\n is the solvent chemical potential at some reference temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n;\n \n\\begin_inset Formula $\\rho_{T}^{w}$\n\\end_inset\n\n is the true density of the solvent (an invariant property for an intrinsically incompressible fluid);\n and \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n is the osmotic coefficient which represents the extent by which the solute concentrations influence the solvent chemical potential.\n In a multiphasic material,\n a constitutive relation is needed for \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n;\n in general,\n \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n may be a function of the solid matrix strain and the solute concentrations.\n In FEBio,\n the dependence of the osmotic coefficient on the solid matrix strain is currently constrained to a dependence on \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe solute mechano-electrochemical potential is nearly equal to its electrochemical potential because the solute volume fraction is assumed to be negligible.\n The solvent mechano-electrochemical potential is the same as its mechano-chemical potential,\n since the solvent is neutral in a multiphasic mixture.\n In general,\n momentum and energy balances evaluated across a boundary surface in a multiphasic mixture require that the mechano-electrochemical potentials of solvent and solutes be continuous across that surface.\n These continuity requirements are enforced automatically in FEBio by defining the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and solute concentration \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n as \n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{p} & =p-R\\theta\\Phi\\sum\\nolimits_{\\alpha}c^{\\alpha}\\\\\n\\tilde{c}^{\\alpha} & =c^{\\alpha}/\\tilde{\\kappa}^{\\alpha}\n\\end{aligned}\n\\,,\\label{eq:mp-effective-p-c}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{\\kappa}^{\\alpha}=\\hat{\\kappa}^{\\alpha}\\exp\\left(-z^{\\alpha}\\frac{F_{c}\\psi}{R\\theta}\\right)\\label{eq:mp-partition-coeff}\n\\end{equation}\n\n\\end_inset\n\nis the partition coefficient for solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n The partition coefficient incorporates the combined effects of solubility and long-range electrostatic interactions to determine the ratio of interstitial to external concentration for that solute.\n Therefore,\n the effective concentration represents a measure of the \n\\shape italic\nactivity\n\\shape default\n of the solute,\n as understood in chemistry.\n The effective fluid pressure represents that part of the pressure which is over and above osmotic effects.\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio,\n nodal variables consist of the solid matrix displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n,\n and the effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n.\n Essential boundary conditions must be imposed on these variables,\n and not on the actual pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n or concentrations \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n.\n (In a biphasic material however,\n since \n\\begin_inset Formula $c^{\\alpha}=0$\n\\end_inset\n\n,\n the effective and actual fluid pressures are the same,\n \n\\begin_inset Formula $p=\\tilde{p}$\n\\end_inset\n\n.)\n\\end_layout\n\n\\begin_layout Standard\nThe mixture stress in a triphasic material is given by \n\\begin_inset Formula $\\boldsymbol{\\sigma}=-p\\mathbf{I}+\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{e}$\n\\end_inset\n\n is the stress arising from the solid matrix strain.\n The mixture traction on a surface with unit outward normal \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n is \n\\begin_inset Formula $\\mathbf{t}=\\boldsymbol{\\sigma}\\cdot\\mathbf{n}$\n\\end_inset\n\n.\n This traction is continuous across the boundary surface.\n Therefore,\n the corresponding natural boundary condition for a multiphasic mixture is \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{0}$\n\\end_inset\n\n.\n (In other words,\n if no boundary condition is imposed on the solid matrix displacement or mixture traction,\n the natural boundary condition is in effect.)\n\\end_layout\n\n\\begin_layout Standard\nThe natural boundary conditions for the solvent and solutes are similarly \n\\begin_inset Formula $\\mathbf{w}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{j}^{\\alpha}\\cdot\\mathbf{n}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n is the volumetric flux of solvent relative to the solid and \n\\begin_inset Formula $\\mathbf{j}^{\\alpha}$\n\\end_inset\n\n is the molar flux of solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n relative to the solid.\n In general,\n \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{j}^{\\alpha}$\n\\end_inset\n\n are given by\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\mathbf{w} & =-\\mathbf{\\tilde{k}}\\cdot\\left(\\nabla\\tilde{p}+R\\theta\\sum\\limits_{\\alpha}\\frac{\\tilde{\\kappa}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{d}^{\\alpha}\\cdot\\nabla\\tilde{c}^{\\alpha}\\right),\\\\\n\\mathbf{j}^{\\alpha} & =\\tilde{\\kappa}^{\\alpha}\\,\\mathbf{d}^{\\alpha}\\cdot\\left(-\\varphi^{w}\\nabla\\tilde{c}^{\\alpha}+\\frac{\\tilde{c}^{\\alpha}}{d_{0}^{\\alpha}}\\mathbf{w}\\right),\n\\end{aligned}\n\\label{eq:mp-fluxes-w-j}\n\\end{equation}\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{\\tilde{k}}=\\left[\\mathbf{k}^{-1}+\\frac{R\\theta}{\\varphi^{w}}\\sum\\limits_{\\alpha}\\frac{\\tilde{\\kappa}^{\\alpha}c^{\\alpha}}{d_{0}^{\\alpha}}\\left(\\mathbf{I}-\\frac{\\mathbf{d}^{\\alpha}}{d_{0}^{\\alpha}}\\right)\\right]^{-1}\\label{eq:mp-effective-perm}\n\\end{equation}\n\n\\end_inset\n\nis the effective hydraulic permeability of the interstitial fluid solution (solvent and solutes) through the porous solid matrix;\n \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n is the hydraulic permeability of the solvent through the porous solid matrix;\n \n\\begin_inset Formula $\\mathbf{d}^{\\alpha}$\n\\end_inset\n\n is the diffusivity of solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n through the mixture (frictional interactions with solvent and solid);\n and \n\\begin_inset Formula $d_{0}^{\\alpha}$\n\\end_inset\n\n is its free diffusivity (frictional interactions with solvent only).\n \n\\begin_inset Formula $\\varphi^{w}\\approx1-\\varphi^{s}$\n\\end_inset\n\n is the solid matrix porosity in the current configuration.\n The above expressions for the solvent and solute flux do not account for external body forces.\n\\end_layout\n\n\\begin_layout Standard\nAlso see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Guidelines-for-Multiphasic\"\nnolink \"false\"\n\n\\end_inset\n\n for additional guidelines for running multiphasic materials.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGuidelines for Multiphasic Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Guidelines-Multiphasic-Analyses\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nInitial State of Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Initial-State-Swelling\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUnder traction-free conditions,\n a multiphasic material is usually in a state of swelling due to the osmotic pressure difference between the interstitial fluid and the external environment.\n This osmotic pressure arises from the difference in interstitial versus external concentrations of cations and anions,\n caused by the charged solid matrix and the requirement to satisfy electroneutrality.\n An osmotic pressure difference arising from such electrostatic interactions is known as the \n\\shape italic\nDonnan osmotic pressure\n\\shape default\n.\n When the Donnan pressure is non-zero,\n traction-free conditions do not produce stress-free conditions for the solid matrix,\n since the matrix must expand until its stressed state resists the swelling pressure.\n\\end_layout\n\n\\begin_layout Standard\nThe Donnan pressure reduces to zero when the fixed charged density is zero,\n or when the external environment is infinitely hypertonic (having ion concentrations infinitely greater than the interstitial fixed charge density).\n Since these two conditions represent special cases,\n it is generally necessary to devise methods for achieving the desired initial state of swelling,\n in an analysis where loads or displacements need to be prescribed over and above this swollen state.\n Swelling occurs as a result of the influx of solvent into the porous solid matrix.\n This influx is a time-dependent process that could require extensive analysis time.\n Therefore,\n it is computationally efficacious to achieve the initial state of swelling by using a multi-step analysis (Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Multi-step-Analysis\"\nnolink \"false\"\n\n\\end_inset\n\n) where the first step is a steady-state analysis (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Control-Parameters\"\nnolink \"false\"\n\n\\end_inset\n\n).\n In this steady-state step,\n the fixed charge density may be ramped up from zero to the desired value using a load curve for that property or,\n alternatively,\n the external environmental conditions may be reduced from a very high hypertonic state down to the desired level.\n In the second step,\n prescribed displacement boundary conditions (when needed) may be specified to be of type \n\\shape italic\nrelative\n\\shape default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Zero-Displacement\"\nnolink \"false\"\n\n\\end_inset\n\n),\n so that they become superposed over and above the initial swelling state.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Module type=\"multiphasic\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <analysis>STEADY-STATE</analysis>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <bc type=\"prescribe\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <dof>z</dof>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <scale>1</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <relative>1</relative>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </prescribe>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Step>\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Boundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-BC-Multiphasic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn most analyses,\n it may be assumed that the ambient fluid pressure and electric potential in the external environment are zero,\n thus \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\psi_{\\ast}=0$\n\\end_inset\n\n,\n where the subscripted asterisk is used to denote environmental conditions.\n Since the external environment does not include a solid matrix,\n the fixed charge density there is zero.\n For example,\n in a triphasic analysis,\n \n\\begin_inset Formula $c_{\\ast}^{+}=c_{\\ast}^{-}\\equiv c_{\\ast}$\n\\end_inset\n\n.\n It follows that the effective fluid pressure in the external environment is \n\\begin_inset Formula $\\tilde{p}_{\\ast}=-R\\theta\\Phi_{\\ast}\\sum\\nolimits_{\\alpha}c_{\\ast}^{\\alpha}$\n\\end_inset\n\n and the effective concentrations are \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}=c_{\\ast}^{\\alpha}/\\hat{\\kappa}_{\\ast}^{\\alpha}\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n.\n Therefore,\n in multiphasic analyses,\n whenever the external environment contains solutes with non-zero concentrations \n\\begin_inset Formula $c_{\\ast}^{\\alpha}$\n\\end_inset\n\n,\n the user must remember to prescribe non-zero boundary conditions for the effective solute concentrations \n\\shape italic\nand\n\\shape default\n the effective fluid pressure.\n\\end_layout\n\n\\begin_layout Standard\nLetting \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n also implies that prescribed mixture normal tractions (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mixture-Normal-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n) represent only the traction above ambient conditions.\n Note that users are not obligated to assume that \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n.\n However,\n if a non-zero value is assumed for the ambient pressure,\n then users must remember to incorporate this non-zero value whenever prescribing mixture normal tractions.\n Similarly,\n users are not required to assume that \n\\begin_inset Formula $\\psi_{\\ast}=0$\n\\end_inset\n\n;\n when a non-zero value is assumed for the electric potential of the external environment,\n the prescribed boundary conditions for the effective concentrations should be evaluated using the corresponding partition coefficient,\n \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}=c_{\\ast}^{\\alpha}/\\tilde{\\kappa}_{\\ast}^{\\alpha}\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Initial Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-IC-Multiphasic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a multiphasic material is initially exposed to a given external environment with effective pressure \n\\begin_inset Formula $\\tilde{p}_{\\ast}$\n\\end_inset\n\n and effective concentrations \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n,\n the initial conditions inside the material should be set to \n\\begin_inset Formula $\\tilde{p}=\\tilde{p}_{\\ast}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}^{\\alpha}=\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n in order to expedite the evaluation of the initial state of swelling.\n The values of \n\\begin_inset Formula $\\tilde{p}_{\\ast}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n should be evaluated as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Boundary-Conditions-1\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Effective Solute Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Effective-Solute-Flux\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe finite element formulation for multiphasic materials in FEBio requires that the natural boundary condition for solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n be prescribed as \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}\\equiv j_{n}^{\\alpha}+\\sum\\nolimits_{\\beta}z^{\\beta}j_{n}^{\\beta}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}$\n\\end_inset\n\n is the effective solute flux.\n For a mixture containing only neutral solutes (\n\\begin_inset Formula $z^{\\beta}=0,\\,\\forall\\beta$\n\\end_inset\n\n ),\n it follows that \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}=j_{n}^{\\alpha}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Electric Current Density\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Electric-Current\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe electric current density in a mixture is a linear superposition of the ion fluxes,\n \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{I}_{e}=F_{c}\\sum\\limits_{\\alpha}z^{\\alpha}\\mathbf{j}^{\\alpha}\\,.\\label{eq:mp-electric-current-density}\n\\end{equation}\n\n\\end_inset\n\nSince only the normal component \n\\begin_inset Formula $j_{n}^{\\alpha}=\\mathbf{j}^{\\alpha}\\cdot\\mathbf{n}$\n\\end_inset\n\n of ion fluxes may be prescribed at a boundary,\n it follows that only the normal component \n\\begin_inset Formula $I_{n}=\\mathbf{I}_{e}\\cdot\\mathbf{n}$\n\\end_inset\n\n of the current density may be prescribed.\n To prescribe \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n,\n it is necessary to know the nature of the ion species in the mixture,\n and how the current is being applied.\n For example,\n if the ions in the triphasic mixture consist of Na\n\\begin_inset Formula $^{\\mathrm{+}}$\n\\end_inset\n\n and Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n,\n and if the current is being applied using silver/silver chloride (Ag/AgCl) electrodes,\n a chemical reaction occurs at the anode where Ag combines with Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n to produce AgCl,\n donating an electron to the electrode to transport the current.\n At the cathode,\n the reverse process takes place.\n Therefore,\n in this system,\n there is only flux of Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n and no flux of Na\n\\begin_inset Formula $^{\\mathrm{+}}$\n\\end_inset\n\n (\n\\begin_inset Formula $j_{n}^{+}=0$\n\\end_inset\n\n) at the electrode-mixture interface,\n so that the prescribed boundary condition should be \n\\begin_inset Formula $j_{n}^{-}=-I_{n}/F_{c}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $z^{+}=+1$\n\\end_inset\n\n and \n\\begin_inset Formula $z^{-}=-1$\n\\end_inset\n\n in a triphasic mixture,\n the corresponding effective fluxes are given by \n\\begin_inset Formula $\\tilde{j}_{n}^{+}=2j_{n}^{+}-j_{n}^{-}=I_{n}/F_{c}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{j}_{n}^{-}=j_{n}^{+}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsubsection\nElectrical Grounding\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Electrical-Grounding\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf a multiphasic mixture containing ions is completely surrounded by ion-impermeant boundaries it is necessary to ground the mixture to prevent unconstrained fluctuations of the electric potential.\n Grounding is performed by prescribing the effective concentration of one or more ions,\n at a location inside the mixture or on one of its boundaries corresponding to the location of the grounding electrode.\n The choice of ion(s) to constrain may be guided by the type of grounding electrode and the reference electrolyte bath in which it is inserted.\n\\end_layout\n\n\\begin_layout Subsubsection\nNeglecting Osmotic Effects\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Ignoring-Osmotic-Effects\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nOsmotic effects can be neglected in a multiphasic analysis by setting the osmotic coefficient to zero,\n \n\\begin_inset Formula $\\Phi=0$\n\\end_inset\n\n.\n In that case,\n the effective fluid pressure and the actual fluid pressure become equivalent measures,\n \n\\begin_inset Formula $\\tilde{p}=p$\n\\end_inset\n\n,\n as for the case of a biphasic mixture.\n\\end_layout\n\n\\begin_layout Subsubsection\nSolutes as 'Solid-Bound Molecules'\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solutes-as-SBMs\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn multiphasic analyses,\n FEBio solves for the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n,\n and the effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\alpha}$\n\\end_inset\n\n,\n at all the nodes,\n representing all the degrees of freedom in an analysis.\n Once the solution has converged at each time point,\n FEBio solves for the referential mass densities \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n of solid-bound molecules by solving the corresponding mass balance equation in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-sbm-mass-balance\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n at integration points,\n in a single numerical integration step.\n This approach reduces the number of degrees of freedom that need to be solved simultaneously by the FEBio solver.\n However,\n occasionally,\n the numerical solution for \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n may produce numerical round-off errors that may get amplified as the analysis time progresses.\n \n\\end_layout\n\n\\begin_layout Standard\nAn alternative to using solid-bound molecules is to define solutes \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n whose diffusivity \n\\begin_inset Formula $\\mathbf{d}^{\\sigma}$\n\\end_inset\n\n in the mixture is set to \n\\begin_inset Formula $\\mathbf{0}$\n\\end_inset\n\n (however,\n the free diffusivity \n\\begin_inset Formula $d_{0}^{\\sigma}$\n\\end_inset\n\n should not be set to zero,\n to prevent a division by zero;\n its exact value is not important,\n thus let \n\\begin_inset Formula $d_{0}^{\\sigma}=1$\n\\end_inset\n\n).\n Then,\n FEBio treats these solutes as equivalent to solid-bound molecules:\n (1) Their concentration does not contribute to the osmolarity of the interstitial fluid;\n (2) their concentration contributes to the fixed charge density \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n in the current configuration;\n (3) these solutes do not contribute to the effective hydraulic permeability \n\\begin_inset Formula $\\tilde{\\mathbf{k}}$\n\\end_inset\n\n of the porous multiphasic mixture;\n (4) these solutes can be involved in chemical reactions;\n (5) while their initial effective concentration may be prescribed,\n it must not contribute to the prescribed initial effective fluid pressure,\n and the user should not prescribe any boundary conditions on the effective concentration of these solutes.\n Using these 'solid-bound' solutes increases the number of degrees of freedom in an FEBio analysis;\n however,\n unlike solid-bound molecules,\n the solution for the effective concentration of these solutes remains as accurate as all other degrees of freedom in an analysis.\n\\end_layout\n\n\\begin_layout Standard\nTo evaluate the contribution of these solutes to the referential solid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n and to chemical reactions,\n it should be recalled that the concentration \n\\begin_inset Formula $c^{\\sigma}$\n\\end_inset\n\n is related to the referential mass density \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n via eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:concentration-density-relations\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Using the expression for \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-referential-solid-vol-frac\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n we find that\n\\begin_inset Formula \n\\[\n\\varphi_{r}^{s}=\\varphi_{0}^{s}+\\left(J-\\varphi_{r}^{s}\\right)\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}\n\\]\n\n\\end_inset\n\nwhich can be solved for \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n using the concentrations \n\\begin_inset Formula $c^{\\sigma}$\n\\end_inset\n\n,\n\\begin_inset Formula \n\\[\n\\varphi_{r}^{s}=\\frac{\\varphi_{0}^{s}+J\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}}{1+\\sum\\limits_{\\sigma}\\frac{M^{\\sigma}c^{\\sigma}}{\\rho_{T}^{\\sigma}}}\\,.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Multiphasic Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Multiphasic-Mat\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a multiphasic material is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nmultiphasic\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n Constitutive relations must be provided for the solid matrix,\n the mixture fixed charge density,\n the hydraulic permeability \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n,\n the osmotic coefficient \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n,\n and the properties of each solute:\n the solute diffusivity in the mixture \n\\begin_inset Formula $\\mathbf{d}^{\\alpha}$\n\\end_inset\n\n,\n the solute free diffusivity \n\\begin_inset Formula $d_{0}^{\\alpha}$\n\\end_inset\n\n,\n and the solute effective solubility \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}$\n\\end_inset\n\n.\n Therefore,\n the following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solid matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n in the reference configuration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fixed_charge_density>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfixed charge density \n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n in the reference configuration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<permeability>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the hydraulic permeability \n\\begin_inset Formula $\\mathbf{k}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solvent_supply>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solvent supply \n\\begin_inset Formula $\\hat{\\varphi}^{w}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<osmotic_coefficient>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the osmotic coefficient \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solute>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solute properties\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid_bound>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of solid-bound molecule\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <solid> tag encloses a description of the solid matrix constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The solid volume fraction in the reference configuration,\n <phi0>,\n must be greater than 0 (no solid) and less than 1 (only solid).\n The volume fraction of fluid (also known as the porosity) in the reference configuration is given by \n\\begin_inset Formula $1-\\varphi_{0}$\n\\end_inset\n\n.\n The <fixed_charge_density> may be negative,\n positive,\n or zero.\n The <permeability> and <osmotic_coefficient> tags enclose descriptions of the permeability and osmotic coefficient constitutive relations and their associated material properties,\n as may be selected from the list presented in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Permeability-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Osmotic-Coefficient-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe optional <solute> tag provides a description of each solute in the multiphasic mixture.\n Multiple solutes may be defined.\n Each tag includes the required \n\\shape italic\nsol\n\\shape default\n attribute,\n which should reference a solute \n\\shape italic\nid\n\\shape default\n from the <Solutes> description in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The following parameters must be defined in this description:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<diffusivity>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solute diffusivities \n\\begin_inset Formula $\\mathbf{d}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $d_{0}^{\\alpha}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solubility>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecification of the solute effective solubility \n\\begin_inset Formula $\\hat{\\kappa}^{\\alpha}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <diffusivity> and <solubility> tags enclose descriptions of materials that may be selected from the lists presented in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Diffusivity-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solubility-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n,\n respectively.\n Each solute tag must include a \n\\begin_inset Quotes eld\n\\end_inset\n\nsol\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute\n\\end_layout\n\n\\begin_layout Standard\nThe optional <solid_bound> tag specifies which solid-bound molecule should be included in the multiphasic mixture.\n Multiple solid-bound molecules may be specified.\n Each tag should include the required \n\\shape italic\nsbm\n\\shape default\n attribute,\n which references an \n\\shape italic\nid\n\\shape default\n from the <SolidBoundMolecules> description in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Bound-Molecules\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The following parameter must be defined in this description:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<rho0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninitial value of the referential apparent density of the solid-bound molecule \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<rhomin>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptional minimum allowable value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n (zero by default)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<rhomax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptional maximum allowable value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n (none by default)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf a chemical reaction involves this solid-bound molecule its referential apparent density may evolve over time.\n The user may place lower and upper bounds on the allowable range of an evolving \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" name=\"Media\" type=\"multiphasic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.2</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fixed_charge_density>-40</fixed_charge_density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid name=\"Solid Matrix\" type=\"Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>0.28</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <beta>0</beta>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <permeability name=\"Permeability\" type=\"perm-Holmes-Mow\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <perm>1e-3</perm>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <M>0</M>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <alpha>0</alpha>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </permeability>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <osmotic_coefficient name=\"Ideal\" type=\"osm-coef-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <osmcoef>1.0</osmcoef>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </osmotic_coefficient>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solute sol=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <diffusivity name=\"Diffusivity\" type=\"diff-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <free_diff>1e-3</free_diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <diff>1e-3</diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </diffusivity>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solubility name=\"Solubility\" type=\"solub-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <solub>1.0</solub>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solubility>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solute sol=\"2\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <diffusivity name=\"Diffusivity\" type=\"diff-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <free_diff>1e-3</free_diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <diff>1e-3</diff>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </diffusivity>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solubility name=\"Solubility\" type=\"solub-const\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <solub>1.0</solub>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solubility>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nWhen a multiphasic material is employed in an analysis,\n it is also necessary to specify the values of the universal gas constant \n\\begin_inset Formula $R$\n\\end_inset\n\n [\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n/\n\\series bold\nn\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nT\n\\series default\n],\n absolute temperature \n\\begin_inset Formula $\\theta$\n\\end_inset\n\n [\n\\series bold\nT\n\\series default\n],\n and Faraday's constant \n\\begin_inset Formula $F_{c}$\n\\end_inset\n\n [\n\\series bold\nQ\n\\series default\n/\n\\series bold\nn\n\\series default\n] in the <Globals> section,\n using a self-consistent set of units.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Globals>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <R>8.314e-6</R>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T>298</T>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Fc>96500e-9</Fc>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Constants>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Solutes>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solute id=\"1\" name=\"Na\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>1</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <solute id=\"2\" name=\"Cl\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <charge_number>-1</charge_number>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </solute>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Solutes>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Globals>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"17\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"none\" valignment=\"top\" width=\"90pt\">\n<column alignment=\"none\" valignment=\"top\" width=\"129pt\">\n<row>\n<cell multicolumn=\"1\" alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\" special=\"p{219pt}\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSelf-consistent units for a triphasic analysis\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell multicolumn=\"1\" alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\" special=\"p{219pt}\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nPrimary Units\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlength\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nforce\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nN \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmole\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnmol \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncharge\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nC \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntemperature\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nK \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell multicolumn=\"1\" alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\" special=\"p{219pt}\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDerived Units\n\\end_layout\n\n\\end_inset\n</cell>\n<cell multicolumn=\"2\" alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nN/mm\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n,\n MPa \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npermeability\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm\n\\begin_inset Formula $^{\\mathrm{4}}$\n\\end_inset\n\n/N\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\ns,\n mm\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/MPa \n\\begin_inset Formula $\\cdot$\n\\end_inset\n\ns \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiffusivity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmm\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/s \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconcentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnmol/mm\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n,\n mM \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncharge density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnEq/mm\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n,\n mEq/L \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvoltage\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmV \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA/mm\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIt is also possible to create models with multiphasic materials that use different solutes in different regions.\n In that case,\n introduce additional solute entries in the <Solutes> section and refer to those solute ids in the multiphasic material descriptions.\n Generally,\n two adjoining multiphasic regions may share the same solute (e.g.,\n Na in both regions),\n in which case that solute may transport freely across the interface separating these regions;\n or they may share no solute,\n in which case the interface is impermeable to all solutes.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSolvent Supply Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solvent-Supply-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSolvent supply materials may be used to simulate an external source of solvent,\n such as supply from microvasculature that is not modeled explicitly.\n The solvent supply term,\n \n\\begin_inset Formula $\\hat{\\varphi}^{w}$\n\\end_inset\n\n,\n appears in the mass balance relation for the mixture,\n \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\hat{\\varphi}^{w}\\,.\\label{eq:solvent-supply-mass-balance}\n\\end{equation}\n\n\\end_inset\n\n\n\\begin_inset Formula $\\hat{\\varphi}^{w}$\n\\end_inset\n\n has units of reciprocal time [\n\\series bold\nt\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{-1}}$\n\\end_inset\n\n];\n it represents the rate at which the volume fraction of solvent changes with time.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nStarling Equation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Starling-Equation-MP\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for Starling's equation for fluid supply is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nStarling\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kp>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhydraulic filtration coefficient,\n \n\\begin_inset Formula $k_{p}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{2}}$\n\\end_inset\n\n/\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<pv>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective fluid pressure in external source,\n \n\\begin_inset Formula $\\tilde{p}_{v}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<qc sol=\n\\begin_inset Quotes erd\n\\end_inset\n\nn\n\\begin_inset Quotes erd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nosmotic filtration coefficient,\n \n\\begin_inset Formula $q_{c}^{\\alpha}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n/\n\\series bold\nn\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<cv sol=\n\\begin_inset Quotes erd\n\\end_inset\n\nn\n\\begin_inset Quotes erd\n\\end_inset\n\n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective solute concentration in external source,\n \n\\begin_inset Formula $\\tilde{c}_{v}^{\\alpha}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe fluid supply is given by Starling's equation,\n \n\\begin_inset Formula \n\\[\n\\hat{\\varphi}^{w}=k_{p}\\left(\\tilde{p}_{v}-\\tilde{p}\\right)+\\sum\\limits_{\\alpha}q_{c}^{\\alpha}\\left(\\tilde{c}_{v}^{\\alpha}-\\tilde{c}^{\\alpha}\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is the effective fluid pressure in the multiphasic material.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example defines a solvent supply material of the Starling type for a multiphasic mixture containing one solute.\n\\end_layout\n\n\\begin_layout LyX-Code\n<solvent_supply type=\"Starling\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kp>0.001</kp>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pv>0.1</pv>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <qc sol=\"1\">1e-5</qc>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <cv sol=\"1\">150</cv>\n\\end_layout\n\n\\begin_layout LyX-Code\n</solvent_supply>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nChemical Reactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Chemical-Reactions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following sections describe FEBio's formulation for analyzing mechanochemical events in neutral or charged deformable porous media under finite deformation.\n The formulation allows full coupling of mechanical and chemical effects,\n providing a framework where material properties and response functions may depend on solid matrix strain as well as solute concentration.\n\\end_layout\n\n\\begin_layout Standard\nIf you use these capabilities in your published research,\n we request that you cite the following publication describing its development \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Ateshian2011\"\nliteral \"true\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nGuidelines for Chemical Reaction Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Guidelines-Chemical-Reactions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nChemical reactions may be modeled within a multiphasic mixture.\n The reaction may involve solutes (\n\\begin_inset Formula $\\alpha=\\iota)$\n\\end_inset\n\n and solid-bound molecules (\n\\begin_inset Formula $\\alpha=\\sigma)$\n\\end_inset\n\n that move with the solid matrix (\n\\begin_inset Formula $\\mathbf{v}^{\\sigma}=\\mathbf{v}^{s},\\,\\forall\\sigma)$\n\\end_inset\n\n.\n Consider a general chemical reaction,\n \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\nu_{R}^{\\alpha}\\mathcal{E}^{\\alpha}\\to\\sum\\limits_{\\alpha}\\nu_{P}^{\\alpha}\\mathcal{E}^{\\alpha},\\label{eq:cr-forward-reaction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathcal{E}^{\\alpha}$\n\\end_inset\n\n is the chemical species representing constituent \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n in the mixture;\n \n\\begin_inset Formula $\\nu_{R}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\nu_{P}^{\\alpha}$\n\\end_inset\n\n represent stoichiometric coefficients of the reactants and products,\n respectively.\n To maintain consistency with classical chemical kinetics,\n the analysis of chemical reactions employs molar concentrations \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n and molar supplies \n\\begin_inset Formula $\\hat{c}^{\\alpha}$\n\\end_inset\n\n on a solution-volume basis for all reactants and products,\n whether they are solutes or solid-bound molecular species.\n\\end_layout\n\n\\begin_layout Standard\nSince the molar supply of reactants and products is constrained by stoichiometry,\n it follows that all molar supplies \n\\begin_inset Formula $\\hat{c}^{\\alpha}$\n\\end_inset\n\n in a specific chemical reaction may be related to a \n\\shape italic\nmolar production rate\n\\shape default\n \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n according to \n\\begin_inset Formula \n\\begin{equation}\n\\hat{c}^{\\alpha}=\\nu^{\\alpha}\\hat{\\zeta}\\,,\\label{eq:cr-concentration-supply}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\nu^{\\alpha}$\n\\end_inset\n\n represents the net stoichiometric coefficient for \n\\begin_inset Formula $\\mathcal{E}^{\\alpha}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\begin{equation}\n\\nu^{\\alpha}=\\nu_{P}^{\\alpha}-\\nu_{R}^{\\alpha}.\\label{eq:cr-net-stoich-coeff}\n\\end{equation}\n\n\\end_inset\n\nThus,\n formulating constitutive relations for \n\\begin_inset Formula $\\hat{c}^{\\alpha}$\n\\end_inset\n\n is equivalent to providing a single relation for \n\\begin_inset Formula $\\hat{\\zeta}\\left(\\theta,\\mathbf{F},c^{\\alpha}\\right)$\n\\end_inset\n\n.\n When the chemical reaction is reversible,\n \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\nu_{R}^{\\alpha}\\mathcal{E}^{\\alpha}\\rightleftharpoons\\sum\\limits_{\\alpha}\\nu_{P}^{\\alpha}\\mathcal{E}^{\\alpha},\\label{eq:cr-reversible-reaction}\n\\end{equation}\n\n\\end_inset\n\nthe relations of \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-concentration-supply\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-net-stoich-coeff\"\nnolink \"false\"\n\n\\end_inset\n\n still apply but the constitutive relation for \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n would be different.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nConsider the dissociation of CaCl\n\\begin_inset Formula $_{\\mathrm{2}}$\n\\end_inset\n\n into ions Ca\n\\begin_inset Formula $^{\\mathrm{2+}}$\n\\end_inset\n\n and Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n,\n \n\\begin_inset Formula \n\\[\n\\mbox{CaCl}_{2}\\rightleftharpoons\\mbox{Ca}^{2+}+2\\mbox{Cl}^{-}.\n\\]\n\n\\end_inset\n\nThe mixture contains three constituents.\n The stoichiometric coefficients of the reactants are \n\\begin_inset Formula $\\nu_{R}^{\\mbox{CaCl}_{2}}=1$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\nu_{R}^{\\mbox{Ca}^{2+}}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\nu_{R}^{\\mbox{Cl}^{-}}=0$\n\\end_inset\n\n,\n and those of the products are \n\\begin_inset Formula $\\nu_{P}^{\\mbox{CaCl}_{2}}=0$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\nu_{P}^{\\mbox{Ca}^{2+}}=1$\n\\end_inset\n\n,\n \n\\begin_inset Formula $\\nu_{P}^{\\mbox{Cl}^{-}}=2$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe reaction production rate \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n enters into the governing equations of multiphasic mixtures via the mass balance relation for each solute,\n \n\\begin_inset Formula \n\\begin{equation}\n\\frac{1}{J}\\frac{D^{s}\\left[J\\left(1-\\varphi^{s}\\right)c^{\\iota}\\right]}{Dt}+\\divg\\mathbf{j}^{\\iota}=\\left(1-\\varphi^{s}\\right)\\nu^{\\iota}\\hat{\\zeta}\\,,\\label{eq:cr-solute-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nthe mass balance for the mixture,\n \n\\begin_inset Formula \n\\begin{equation}\n\\divg\\left(\\mathbf{v}^{s}+\\mathbf{w}\\right)=\\left(1-\\varphi^{s}\\right)\\hat{\\zeta}\\overline{\\mathcal{V}}\\,,\\label{eq:cr-mixture-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\overline{\\mathcal{V}}=\\sum\\limits_{\\alpha}\\nu^{\\alpha}\\mathcal{V}^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathcal{V}^{\\alpha}=M^{\\alpha}/\\rho_{T}^{\\alpha}$\n\\end_inset\n\n is the molar volume of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n,\n and the mass balance for solid-bound constituents,\n \n\\begin_inset Formula \n\\begin{equation}\nD^{s}\\rho_{r}^{\\sigma}/Dt=\\hat{\\rho}_{r}^{\\sigma}\\,,\\label{eq:cr-sbm-mass-balance}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n is the referential apparent mass density (mass of \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n per mixture volume in the reference configuration),\n and \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}$\n\\end_inset\n\n is the referential apparent mass supply of solid constituent \n\\begin_inset Formula $\\sigma$\n\\end_inset\n\n,\n related to molar concentrations and supplies via \n\\begin_inset Formula \n\\begin{equation}\nc^{\\sigma}=\\frac{\\rho_{r}^{\\sigma}}{\\left(J-\\varphi_{r}^{s}\\right)M^{\\sigma}},\\quad\\hat{c}^{\\sigma}=\\frac{\\hat{\\rho}_{r}^{\\sigma}}{\\left(J-\\varphi_{r}^{s}\\right)M^{\\sigma}}\\,.\\label{eq:concentration-density-relations}\n\\end{equation}\n\n\\end_inset\n\nInternally,\n the content of solid-bound species is stored in \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:concentration-density-relations\"\nnolink \"false\"\n\n\\end_inset\n\n is used to evaluate \n\\begin_inset Formula $c^{\\sigma}$\n\\end_inset\n\n when needed for the calculation of \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n.\n If a solid-bound molecule is involved in a chemical reaction,\n equation \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-sbm-mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n is integrated to produce an updated value of \n\\begin_inset Formula $\\rho_{r}^{\\sigma}$\n\\end_inset\n\n,\n using \n\\begin_inset Formula $\\hat{\\rho}_{r}^{\\sigma}=\\left(J-\\varphi_{r}^{s}\\right)M^{\\sigma}\\nu^{\\sigma}\\hat{\\zeta}$\n\\end_inset\n\n based on \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-concentration-supply\"\nnolink \"false\"\n\n\\end_inset\n\n and \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:concentration-density-relations\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nEvolving solid content due to chemical reactions implies that the referential solid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n may not remain constant.\n This value is updated at every time point using \n\\begin_inset Formula \n\\begin{equation}\n\\varphi_{r}^{s}=\\varphi_{0}^{s}+\\sum\\limits_{\\sigma}\\frac{\\rho_{r}^{\\sigma}}{\\rho_{T}^{\\sigma}}\\,,\\label{eq:cr-referential-solid-vol-frac}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\varphi_{0}^{s}$\n\\end_inset\n\n is the solid volume fraction specified by the multiphasic material parameter \n\\shape italic\nphi0\n\\shape default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Multiphasic-Mat\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Thus,\n \n\\begin_inset Formula $\\varphi_{0}^{s}$\n\\end_inset\n\n may be used to account for the solid volume fraction not contributed explicitly by solid-bound molecules.\n Based on kinematics,\n the solid volume fraction in the current configuration is given by \n\\begin_inset Formula $\\varphi^{s}=\\varphi_{r}^{s}/J$\n\\end_inset\n\n.\n Therefore,\n since \n\\begin_inset Formula $0\\leqslant\\varphi^{s}\\leqslant1$\n\\end_inset\n\n by definition,\n it follows that \n\\begin_inset Formula $0\\leqslant\\varphi_{r}^{s}\\leqslant J$\n\\end_inset\n\n,\n implying that the referential solid volume fraction may evolve to values greater than unity when growth leads to swelling of the multiphasic mixture.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n if solid-bound molecules are charged and their content evolves over time,\n the referential fixed charge density may also evolve with chemical reactions according to \n\\begin_inset Formula \n\\begin{equation}\nc_{r}^{F}=c_{0}^{F}+\\frac{1}{1-\\varphi_{r}^{s}}\\sum\\limits_{\\sigma}\\frac{z^{\\sigma}\\rho_{r}^{\\sigma}}{M^{\\sigma}}\\,,\\label{eq:cr-referential-FCD}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n is the referential fixed charge density specified by the multiphasic material parameter \n\\shape italic\nfixed_charge_density\n\\shape default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Multiphasic-Mat\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Thus,\n \n\\begin_inset Formula $c_{0}^{F}$\n\\end_inset\n\n may be used to account for the fixed charge density not contributed explicitly by solid-bound molecules.\n\\end_layout\n\n\\begin_layout Standard\nA chemical reaction is properly balanced when \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}\\nu^{\\alpha}M^{\\alpha}=0\\,,\\label{eq:cr-balanced-reaction}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $M^{\\alpha}$\n\\end_inset\n\n is the molar mass of \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n.\n This constraint implies that the net gain in mass of products must be the same as the net loss in mass of reactants.\n However,\n this constraint is not verified in the code,\n allowing users to model chemical reactions with implicit constituents (constituents that are neither explicitly modeled as solutes nor as solid-bound molecules,\n for which \n\\begin_inset Formula $\\nu^{\\alpha}$\n\\end_inset\n\n and \n\\begin_inset Formula $M^{\\alpha}$\n\\end_inset\n\n are not given).\n For example,\n a chemical reaction where cells consume glucose to form a protein from amino-acids building blocks may have the form \n\\begin_inset Formula \n\\[\n\\mbox{cells}+\\mbox{glucose}+\\mbox{amino\\thinspace acids}\\to\\mbox{cells}+\\mbox{protein}+\\mbox{waste\\thinspace products}\\,.\n\\]\n\n\\end_inset\n\nThe user may opt to model only the glucose reactant and the protein product explicitly,\n while the presence of all other species in this reaction is implicit.\n In these types of analyses the user must beware of potential inconsistencies in the evolving mass of reactants and products since only some of those constituents are modeled explicitly.\n In particular,\n the evolution of \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n as given in \n\\series bold\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-referential-solid-vol-frac\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\series default\n can only account for the explicitly modeled solid-bound molecules.\n Furthermore,\n when some reactants and products are implicit,\n the value of the reaction molar volume \n\\begin_inset Formula $\\overline{\\mathcal{V}}$\n\\end_inset\n\n calculated in the code becomes inaccurate and may produce unexpected results in the evaluation of the mixture mass balance relation in \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-mixture-mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Therefore,\n the user is given the option to override the value of \n\\begin_inset Formula $\\overline{\\mathcal{V}}$\n\\end_inset\n\n calculated in the code.\n In particular,\n if the precise molar volumes of all the species in a reaction are not known,\n assuming that \n\\begin_inset Formula $\\overline{\\mathcal{V}}\\approx0$\n\\end_inset\n\n is a reasonable choice equivalent to assuming that all the constituents have approximately the same density \n\\begin_inset Formula $\\rho_{T}^{\\alpha}$\n\\end_inset\n\n,\n as may be deduced from \n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-balanced-reaction\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSince the electroneutrality condition is enforced in multiphasic mixtures in FEBio,\n it follows that chemical reactions must not violate this condition.\n Enforcing electroneutrality in a chemical reaction is equivalent to satisfying \n\\begin_inset Formula \n\\begin{equation}\n\\sum\\limits_{\\alpha}z^{\\alpha}\\nu^{\\alpha}=0\\,.\\label{eq:cr-electroneutrality}\n\\end{equation}\n\n\\end_inset\n\nThis constraint is checked within the code and an error is generated when it is violated.\n\\end_layout\n\n\\begin_layout Standard\nA constitutive relation must be provided for the molar production rate \n\\begin_inset Formula $\\hat{\\zeta}\\left(\\theta,\\mathbf{F},c^{\\alpha}\\right)$\n\\end_inset\n\n of each chemical reaction.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification for Chemical Reactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Chem-React\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a chemical reaction is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nreaction\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The <reaction> tag must appear inside the definition of a multiphasic mixture and the reaction is defined only for that mixture.\n Multiple chemical reactions may be defined in a given mixture.\n Each <reaction> tag must include a \n\\shape italic\ntype\n\\shape default\n attribute that identifies the constitutive relation for \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe stoichiometric coefficients \n\\begin_inset Formula $\\nu_{R}^{\\alpha}$\n\\end_inset\n\n of the reactants and \n\\begin_inset Formula $\\nu_{P}^{\\alpha}$\n\\end_inset\n\n for the products must be specified in every reaction.\n Optionally,\n the net reaction molar volume \n\\begin_inset Formula $\\overline{\\mathcal{V}}$\n\\end_inset\n\n may be specified explicitly to override the internal value calculated in the code.\n Therefore,\n the following parameters need to be defined in a chemical reaction:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<vR>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreactant stoichiometric coefficient \n\\begin_inset Formula $\\nu_{R}^{\\alpha}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<vP>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nproduct stoichiometric coefficient \n\\begin_inset Formula $\\nu_{P}^{\\alpha}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Vbar>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptional override value for \n\\begin_inset Formula $\\overline{\\mathcal{V}}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n/\n\\series bold\nn\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach <vR> and <vP> tag must include either the \n\\shape italic\nsol\n\\shape default\n attribute,\n which should reference a solute \n\\shape italic\nid\n\\shape default\n from the <Solutes> description in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solutes\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or the \n\\shape italic\nsbm\n\\shape default\n attribute,\n which should reference a solid-bound molecule \n\\shape italic\nid\n\\shape default\n from the <SolidBoundMolecules> description in the <Globals> section (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Bound-Molecules\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Only solutes and solid-bound molecules that have been included in the parent multiphasic mixture may be specified as reactants or products of a chemical reaction.\n\\end_layout\n\n\\begin_layout Standard\nAdditional parameters may be needed in the definition of a chemical reaction,\n depending on the specific form of the constitutive relation for the production rate.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nChemical Reaction Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Chemical-Reaction-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLaw of Mass Action for Forward Reactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mass-Action-Forward\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Law of Mass Action for a forward reaction is \n\\shape italic\nmass-action-forward\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<forward_rate>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecific forward reaction rate \n\\begin_inset Formula $k$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this type of reaction the constitutive relation for the molar production rate is given by \n\\begin_inset Formula \n\\[\n\\hat{\\zeta}=k\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu_{R}^{\\alpha}}\\,.\n\\]\n\n\\end_inset\n\nThe constitutive form of the specific forward reaction rate must be selected from the list of materials given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specific-Reaction-Rate\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The units of \n\\begin_inset Formula $\\hat{\\zeta}$\n\\end_inset\n\n are [\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] and those of \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n are [\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n].\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nConsider the forward reaction that produces solid copper sulfide from solid copper and solid sulfur,\n \n\\begin_inset Formula \n\\[\n\\mbox{Cu}+\\mbox{S}\\to\\mbox{CuS}\n\\]\n\n\\end_inset\n\nAll three species are modeled explicitly in the mixture as solid-bound molecules,\n with id's 1 (Cu),\n 2 (for S) and 3 (for CuS).\n The chemical reaction material is given by:\n\\end_layout\n\n\\begin_layout LyX-Code\n<reaction name=\"copper sulfide production\" type=\"mass-action-forward\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vR sbm=\"1\">1</vR>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vR sbm=\"2\">1</vR>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vP sbm=\"3\">1</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <forward_rate type=\"constant reaction rate\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>1e-3</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </forward_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n</reaction>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nLaw of Mass Action for Reversible Reactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mass-Action-Reversible\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Law of Mass Action for a reversible reaction is \n\\shape italic\nmass-action-reversible\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<forward_rate>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecific forward reaction rate \n\\begin_inset Formula $k_{F}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<reverse_rate>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecific reverse reaction rate \n\\begin_inset Formula $k_{R}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor this type of reaction the constitutive relation for the molar production rate is given by \n\\begin_inset Formula \n\\[\n\\hat{\\zeta}=\\hat{\\zeta}_{F}-\\hat{\\zeta}_{R}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula \n\\[\n\\begin{aligned}\\hat{\\zeta}_{F} & =k_{F}\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu_{R}^{\\alpha}}\\\\\n\\hat{\\zeta}_{R} & =k_{R}\\prod\\limits_{\\alpha}\\left(c^{\\alpha}\\right)^{\\nu_{P}^{\\alpha}}\n\\end{aligned}\n\\,.\n\\]\n\n\\end_inset\n\nThe constitutive form of the specific forward and reverse reaction rates must be selected from the list of materials given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specific-Reaction-Rate\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The units of \n\\begin_inset Formula $\\hat{\\zeta}_{F}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\hat{\\zeta}_{R}$\n\\end_inset\n\n are [\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] and those of \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n are [\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n].\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nConsider the reversible dissociation of CaCl salt into Ca\n\\begin_inset Formula $^{\\mathrm{2+}}$\n\\end_inset\n\n and Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n in water,\n \n\\begin_inset Formula \n\\[\n\\mbox{CaCl}_{2}\\rightleftharpoons\\mbox{Ca}^{2+}+2\\mbox{Cl}^{-}\n\\]\n\n\\end_inset\n\nAll three species are modeled explicitly in the mixture as solutes,\n with id's 1 (for CaCl\n\\begin_inset Formula $_{\\mathrm{2}})$\n\\end_inset\n\n,\n 2 (for Ca\n\\begin_inset Formula $^{\\mathrm{2+}})$\n\\end_inset\n\n and 3 (for Cl\n\\begin_inset Formula $^{\\mathrm{-}})$\n\\end_inset\n\n.\n The chemical reaction material is given by:\n\\end_layout\n\n\\begin_layout LyX-Code\n<reaction name=\"CaCl2 dissociation\" type=\"mass-action-reversible\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vR sol=\"1\">1</vR>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vP sol=\"2\">1</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vP sol=\"3\">2</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <forward_rate type=\"constant\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>1.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </forward_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <reverse_rate type=\"constant\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>0.1</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </reverse_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n</reaction>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nMichaelis-Menten Reaction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Michaelis-Menten-Reaction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Michaelis-Menten reaction is \n\\shape italic\nMichaelis-Menten\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<forward_rate>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum rate at saturating substrate concentration \n\\begin_inset Formula $V_{\\max}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Km>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsubstrate concentration when reaction rate is half of \n\\begin_inset Formula $V_{\\max}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<c0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noptional minimum substrate concentration to trigger reaction \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nn\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Michaelis-Menten reaction may be used to model enzyme kinetics where the enzyme \n\\begin_inset Formula $\\mathcal{E}^{e}$\n\\end_inset\n\n triggers the conversion of the substrate \n\\begin_inset Formula $\\mathcal{E}^{s}$\n\\end_inset\n\n into the product \n\\begin_inset Formula $\\mathcal{E}^{p}$\n\\end_inset\n\n.\n The product molar supply is given by \n\\begin_inset Formula \n\\[\n\\hat{c}^{p}=\\begin{cases}\n\\frac{V_{\\max}c^{s}}{K_{m}+c^{s}} & c^{s}\\geqslant c_{0}\\\\\n0 & c^{s}<c_{0}\n\\end{cases}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $c^{s}$\n\\end_inset\n\n is the substrate concentration.\n The default value of \n\\begin_inset Formula $c_{0}$\n\\end_inset\n\n is 0.\n This relation may be derived,\n with some simplifying assumptions,\n by applying the law of mass action to the combination of two reactions,\n \n\\begin_inset Formula \n\\[\n\\mathcal{E}^{e}+\\mathcal{E}^{s}\\rightleftharpoons\\mathcal{E}^{es}\\to\\mathcal{E}^{e}+\\mathcal{E}^{p}\n\\]\n\n\\end_inset\n\nSince the enzyme is not modeled explicitly in the Michaelis-Menten approximation to these two reactions,\n this reaction model is effectively equivalent to \n\\begin_inset Formula \n\\[\n\\mathcal{E}^{s}\\to\\mathcal{E}^{p}.\n\\]\n\n\\end_inset\n\nTherefore,\n this reaction accepts only one reactant tag <vR> and one product tag <vP>.\n For consistency with the formulation of this reaction,\n the stoichiometric coefficients should be set to \n\\begin_inset Formula $\\nu_{R}^{s}=\\nu_{P}^{p}=1$\n\\end_inset\n\n,\n so that \n\\begin_inset Formula $\\hat{c}^{p}=\\hat{\\zeta}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe constitutive form of the specific forward reaction rate \n\\begin_inset Formula $V_{\\max}$\n\\end_inset\n\n must be selected from the list of materials given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specific-Reaction-Rate\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<reaction name=\"enzyme kinetics\" type=\"Michaelis-Menten\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Vbar>0</Vbar>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vR sol=\"1\">1</vR>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vP sol=\"2\">1</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <forward_rate type=\"constant\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>1.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </forward_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Km>5.0</Km>\n\\end_layout\n\n\\begin_layout LyX-Code\n</reaction>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSpecific Reaction Rate Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Specific-Reaction-Rate\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following sections define specific materials that can be used to define the reaction rate of a chemical reaction.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nConstant Reaction Rate\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constant-Reaction-Rate\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a constant specific reaction rate is \n\\shape italic\nconstant reaction rate\n\\shape default\n.\n The following parameter must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconstant specific reaction rate \n\\begin_inset Formula $k$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\shape italic\nunits vary\n\\shape default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<reaction name=\"enzyme kinetics\" type=\"Michaelis-Menten\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Vbar>0</Vbar>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vR sol=\"1\">1</vR>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vP sol=\"2\">1</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <forward_rate type=\"constant reaction rate\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>1.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </forward_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n</reaction>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nHuiskes Reaction Rate\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Huiskes-Reaction-Rate\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for the Huiskes reaction rate is \n\\shape italic\nHuiskes reaction rate\n\\shape default\n.\n This material model employs the bone remodeling framework proposed by Weinans et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weinans92\"\nliteral \"false\"\n\n\\end_inset\n\n and extended by Mullender et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mullender94\"\nliteral \"false\"\n\n\\end_inset\n\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<B>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreaction rate per specific strain energy \n\\begin_inset Formula $B$\n\\end_inset\n\n (default=0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\shape italic\nunits vary\n\\shape default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<psi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecific strain energy at homeostasis \n\\begin_inset Formula $\\psi_{0}$\n\\end_inset\n\n (default=0) \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nF\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nL\n\\series default\n/\n\\series bold\nM\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<D>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncharacteristic distance from sensors (default=0)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Huiskes specific reaction rate has the form \n\\begin_inset Formula \n\\[\nk=\\frac{1}{\\left(J-\\varphi_{r}^{s}\\right)M^{s}}\\left(B\\left(\\frac{\\Psi_{r}}{\\rho_{r}^{s}}-\\psi_{0}\\right)+\\sum_{i=1}^{N}e^{-d_{i}/D}B_{i}\\left(\\frac{\\Psi_{ri}}{\\rho_{ri}^{s}}-\\psi_{0}\\right)\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\Psi_{r}$\n\\end_inset\n\n is the strain energy density of the solid (strain energy in current configuration per mixture volume in the reference configuration) and \n\\begin_inset Formula $\\rho_{r}^{s}=\\sum\\nolimits_{\\sigma}\\rho_{r}^{\\sigma}$\n\\end_inset\n\n is the referential apparent solid density (mass of solid in current configuration per mixture volume in reference configuration).\n The ratio \n\\begin_inset Formula $\\Psi_{r}/\\rho_{r}^{s}$\n\\end_inset\n\n is the specific strain energy (strain energy per mass of solid in the current configuration).\n The Huiskes specific reaction rate may assume positive and negative values;\n it reduces to zero at homeostasis,\n when \n\\begin_inset Formula $\\Psi_{r}/\\rho_{r}^{s}=\\psi_{0}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nPer Mullender et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mullender94\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the remodeling rate \n\\begin_inset Formula $k$\n\\end_inset\n\n at the current material point may depend not only on the specific strain energy at the current point,\n but also on its value in the neighborhood of the current point (e.g.,\n due to the presence of neihboring sensing cells that provide some form of feedback at the current location).\n Here,\n \n\\begin_inset Formula $N$\n\\end_inset\n\n represents the number of elements in the neighborhood of the current material point element,\n which fall within a characteristic sensing distance \n\\begin_inset Formula $D$\n\\end_inset\n\n,\n and \n\\begin_inset Formula $d_{i}$\n\\end_inset\n\n represents the distance between these neighboring points and the current material point.\n\\end_layout\n\n\\begin_layout Standard\nWhen \n\\begin_inset Formula $D=0$\n\\end_inset\n\n it follows that \n\\begin_inset Formula $N=0$\n\\end_inset\n\n and the model uses the original formulation of Weinans et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weinans92\"\nliteral \"false\"\n\n\\end_inset\n\n.\n When \n\\begin_inset Formula $D>0$\n\\end_inset\n\n,\n as per Mullender et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Mullender94\"\nliteral \"false\"\n\n\\end_inset\n\n,\n the finite element code first searches for all the neighboring elements \n\\begin_inset Formula $N$\n\\end_inset\n\n which fall within a distance of \n\\begin_inset Formula $d_{i}\\le4\\times D$\n\\end_inset\n\n of the element at which \n\\begin_inset Formula $k$\n\\end_inset\n\n needs to be evaluated (here,\n using \n\\begin_inset Formula $d_{i}\\le4\\times D$\n\\end_inset\n\n implies that the exponential term becomes negligible beyond this distance,\n since \n\\begin_inset Formula $e^{-4}=0.0183156$\n\\end_inset\n\n).\n This search is conducted once,\n at the start of the analysis,\n implying that it is not updated at subsequent time points when the deformation may alter distances.\n With increasing values of \n\\begin_inset Formula $D$\n\\end_inset\n\n,\n the number \n\\begin_inset Formula $N$\n\\end_inset\n\n of neighboring elements may increase considerably,\n especially in three-dimensional models,\n which will considerably slow down the computation of \n\\begin_inset Formula $k$\n\\end_inset\n\n,\n therefore users must be careful with choosing an appropriate value for \n\\begin_inset Formula $D$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSince the referential solid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n of a multiphasic mixture evolves with its composition as per eq.\n\\begin_inset CommandInset ref\nLatexCommand pageref\nreference \"eq:cr-referential-solid-vol-frac\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n users must be careful not to allow \n\\begin_inset Formula $J-\\varphi_{r}^{s}\\to0$\n\\end_inset\n\n as Huikes remodeling takes place,\n or else the computation of \n\\begin_inset Formula $k$\n\\end_inset\n\n from the above formula becomes nearly singular.\n Typically this can be done by specifying the true density of the solid,\n \n\\begin_inset Formula $\\rho_{T}^{s}$\n\\end_inset\n\n (<density> in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Bound-Molecules\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n to be much greater than the maximum apparent density \n\\begin_inset Formula $\\rho_{r}^{s}$\n\\end_inset\n\n allowed in the solid remodeling reaction (<rhomax> in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Multiphasic-Mat\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n Then,\n the contribution of the evolving \n\\begin_inset Formula $\\rho_{r}^{s}$\n\\end_inset\n\n to \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand pageref\nreference \"eq:cr-referential-solid-vol-frac\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n will remain negligible.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout Standard\nTo reproduce the interstitial solid remodeling theory proposed by Weinans et al.\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Weinans92\"\nliteral \"true\"\n\n\\end_inset\n\n,\n consider the forward reaction \n\\begin_inset Formula \n\\[\n\\mbox{cells}+\\mbox{solutes}\\to\\mbox{cells}+\\mbox{solid}\\,,\n\\]\n\n\\end_inset\n\nwhere cells convert solutes (e.g.,\n nutrients) into synthesized solid when the specific strain energy exceeds the homeostatic value.\n If the cells and solutes are implicit in this reaction (e.g.,\n if it is assumed that their concentrations change negligibly),\n the production rate of the solid may be given by \n\\begin_inset Formula $\\hat{\\zeta}=k$\n\\end_inset\n\n using the law of mass action for a forward rection,\n where \n\\begin_inset Formula $k$\n\\end_inset\n\n has the form given above.\n\\end_layout\n\n\\begin_layout LyX-Code\n<reaction name=\"solid remodeling\" type=\"mass-action-forward\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <vP sbm=\"1\">1</vP>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <forward_rate type=\"Huiskes reaction rate\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <B>1.0</B>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <psi0>0.01</psi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <D>0</D>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </forward_rate>\n\\end_layout\n\n\\begin_layout LyX-Code\n</reaction>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nRigid Body\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Rigid-Body\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA rigid body can be defined using the rigid material model.\n The material type for a rigid body is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nrigid body\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters are defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<density>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDensity of rigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<center_of_mass>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPosition of the center of mass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nL\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<E>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nYoung's modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<v>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPoisson's ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf the \n\\shape italic\ncenter_of_mass \n\\shape default\nparameter is omitted,\n FEBio will calculate the center of mass automatically.\n In this case,\n a density \n\\shape italic\nmust\n\\shape default\n be specified.\n If the \n\\shape italic\ncenter_of_mass \n\\shape default\nis defined the \n\\shape italic\ndensity \n\\shape default\nparameter may be omitted.\n Omitting both will generate an error message.\n\\end_layout\n\n\\begin_layout Standard\nThe Young's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\nand Poisson ratio \n\\begin_inset Formula $v$\n\\end_inset\n\n currently have no effect on the results.\n The only place where they are used is in the calculation of a material stiffness for some auto-penalty contact formulation.\n If you are using contact it is advised to enter sensible values.\n Otherwise these parameters may be omitted.\n\\end_layout\n\n\\begin_layout Standard\nThe degrees of freedom of a rigid body are initially unconstrained\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\n This is different from previous versions of FEBio where rigid bodies were initially fully constrained.\n\\end_layout\n\n\\end_inset\n\n.\n This implies that a rigid body is free to move in all three directions and rotate about any of the coordinate axes.\n To constrain a rigid body degree of freedom you may use the \n\\emph on\nConstraints\n\\emph default\n sections (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Rigid-Constraints\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"rigid body\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1.0</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <center_of_mass>0,0,0</center_of_mass>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nActive Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Active-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nActive contraction materials may be used to impose a (typically) contractile stress within a solid material,\n by incorporating the contraction material within a solid mixture.\n The total stress in the solid mixture is simply \n\\begin_inset Formula $\\boldsymbol{\\sigma}=\\boldsymbol{\\sigma}^{s}+\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{s}$\n\\end_inset\n\n is the solid stress (due to strain and strain history),\n and \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n is the active contractile stress.\n The calculation of the contractile stress is the same,\n wether the solid mixture consists of uncoupled or unconstrained materials.\n\\end_layout\n\n\\begin_layout Subsection\nContraction in Mixtures of Uncoupled Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Contraction-Mixtures-Uncoupled\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the solid mixture consists of uncoupled materials,\n the active contraction material should be selected from the list below.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Prescribed Uniaxial Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Uniaxial-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for prescribed active contraction along a single direction (or fiber) in an uncoupled solid mixture is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled\n\\shape default\n \n\\shape italic\nprescribed uniaxial active contraction\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable uncoupled material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<T0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T_{0},$\n\\end_inset\n\n representing the prescribed stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the reference configuration,\n the fiber is oriented along the unit vector \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal basis vectors representing the local element coordinate system when specified (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or else the global Cartesian coordinate system.\n The active stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n for this material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T_{0}\\mathbf{n}\\otimes\\mathbf{n}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{n}_{r}$\n\\end_inset\n\n is the stretched fiber orientation in the current (deformed) configuration.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nUniaxial contraction along \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n in a mixture containing a Mooney-Rivlin solid.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"prescribed uniaxial active contraction uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T0 lc=\"2\">1</T0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Prescribed Transversely Isotropic Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-TISO-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for prescribed isotropic active contraction in a plane transverse to a given direction (or fiber),\n in an uncoupled solid mixture,\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nprescribed trans iso active contraction uncoupled\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable uncoupled material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<T0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T_{0},$\n\\end_inset\n\n representing the prescribed stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the reference configuration,\n the fiber is oriented along the unit vector \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal basis vectors representing the local element coordinate system when specified (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or else the global Cartesian coordinate system.\n The active stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n for this material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T_{0}\\left(\\mathbf{B}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{e}_{1}$\n\\end_inset\n\n is the stretched fiber orientation in the current (deformed) configuration and \n\\begin_inset Formula $\\mathbf{B}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nIsotropic contraction in plane transverse to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n in a mixture containing a Mooney-Rivlin solid.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>1000</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"prescribed trans iso active contraction uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T0 lc=\"2\">1</T0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nUncoupled Prescribed Isotropic Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Uncoupled-Isotropic-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for prescribed isotropic active contraction,\n in an uncoupled solid mixture,\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nprescribed isotropic active contraction uncoupled\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable uncoupled material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nuncoupled solid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"none\" valignment=\"top\" width=\"189pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<T0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T_{0},$\n\\end_inset\n\n representing the prescribed stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe active stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n for this material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T_{0}\\mathbf{B}\\,.\n\\]\n\n\\end_inset\n\nNote:\n If the solid material in the mixture is (nearly) incompressible,\n this isotropic contraction will cause no change in the deformation.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nIsotropic contraction in a mixture containing a Mooney-Rivlin solid.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>5.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"prescribed isotropic active contraction uncoupled\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T0 lc=\"2\">1</T0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Fiber Stress\n\\end_layout\n\n\\begin_layout Standard\nAn active fiber stress,\n based on a Hill formulation,\n can be added via the material \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nuncoupled active fiber stress\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable compressible material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nuncoupled solid mixture\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Uncoupled-Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The stress is given by,\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T\\left(\\tilde{\\lambda}\\right)\\mathbf{\\mathrm{dev}A}.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\mathbf{A=a\\otimes a},$\n\\end_inset\n\nwith \n\\series bold\na\n\\series default\n the unit vector describing the fiber direction in the spatial frame,\n \n\\emph on\n\n\\begin_inset Formula $\\tilde{\\lambda}$\n\\end_inset\n\n\n\\emph default\nis the deviatoric fiber stretch,\n \n\\emph on\nJ \n\\emph default\nis the jacobian of the deformation,\n and \n\\begin_inset Formula \n\\[\nT\\left(\\tilde{\\lambda}\\right)=s_{max}a\\left(t\\right)s_{TL}\\left(\\tilde{\\lambda}\\right)s_{TV}\\left(\\dot{\\tilde{\\lambda}}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameters are defined below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale factor for defining maximum stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactivation level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstl\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFunction defining the stress-stretch relatioship for the material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstv\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFunction defining the stress-stretch rate relatioship for the material.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameters\n\\begin_inset Formula $s_{TL}$\n\\end_inset\n\nand \n\\begin_inset Formula $s_{TV}$\n\\end_inset\n\nare functions that need to be defined in place.\n There are currently two ways of defining these functions,\n either via a mathematical expression or a list of sample points.\n An example is given below.\n If these parametes are omitted,\n they are replaced by the constant 1 in the equation for the stress above.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nAn example defining the stl parameter via a mathematical expression.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"uncoupled active fiber stress\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <smax>1.5</smax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <a lc=\"1\">0.1</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <stl type=\"math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <math>(l-1)^2</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </stl>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nAn example defining the stl parameter via a point list.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"uncoupled solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <k>100.0</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c1>1.0</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"uncoupled active fiber stress\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <smax>1.5</smax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <a lc=\"1\">0.1</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <stl type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <interpolate>linear</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <pt>0,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <pt>1,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <pt>2,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </stl>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nContraction in Mixtures of Unconstrained Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Contraction-Mixtures-Unconstrained\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsubsec:mylabel27\"\n\n\\end_inset\n\n When the solid mixture consists of unconstrained materials,\n the active contraction material should be selected from the list below.\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Uniaxial Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Uniaxial-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for prescribed active contraction along a single direction (or fiber) in a solid mixture of unconstrained materials is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nprescribed uniaxial active contraction\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable compressible material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<T0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T_{0},$\n\\end_inset\n\n representing the prescribed stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the reference configuration,\n the fiber is oriented along the unit vector \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal basis vectors representing the local element coordinate system when specified (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or else the global Cartesian coordinate system.\n The active stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n for this material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T_{0}\\mathbf{n}\\otimes\\mathbf{n}\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{e}_{1}$\n\\end_inset\n\n is the stretched fiber orientation in the current (deformed) configuration.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nUniaxial contraction along \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n in a mixture containing a neo-Hookean solid.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"prescribed uniaxial active contraction\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T0 lc=\"2\">1</T0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Transversely Isotropic Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-TISO-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for prescribed isotropic active contraction in a plane transverse to a given direction (or fiber),\n in a solid mixture of unconstrained materials,\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nprescribed trans iso active contraction\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable compressible material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<T0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T_{0},$\n\\end_inset\n\n representing the prescribed stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the reference configuration,\n the fiber is oriented along the unit vector \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\left\\{ \\mathbf{e}_{1},\\mathbf{e}_{2},\\mathbf{e}_{3}\\right\\} $\n\\end_inset\n\n are orthonormal basis vectors representing the local element coordinate system when specified (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Specifying-Fiber-Orientation\"\nnolink \"false\"\n\n\\end_inset\n\n),\n or else the global Cartesian coordinate system.\n The active stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n for this material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T_{0}\\left(\\mathbf{B}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\,,\n\\]\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{n}=\\mathbf{F}\\cdot\\mathbf{e}_{1}$\n\\end_inset\n\n is the stretched fiber orientation in the current (deformed) configuration and \n\\begin_inset Formula $\\mathbf{B}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n is the left Cauchy-Green tensor.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nIsotropic contraction in plane transverse to \n\\begin_inset Formula $\\mathbf{e}_{1}$\n\\end_inset\n\n,\n in a mixture containing a neo-Hookean solid.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"prescribed trans iso active contraction\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T0 lc=\"2\">1</T0>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <mat_axis type=\"angles\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <theta>0</theta>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <phi>90</phi>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Isotropic Active Contraction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Isotropic-Contraction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for prescribed isotropic active contraction,\n in a solid mixture of unconstrained materials,\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nprescribed isotropic active contraction\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable compressible material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nsolid mixture\n\\shape default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The following material parameters need to be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<T0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $T_{0},$\n\\end_inset\n\n representing the prescribed stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe active stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{a}$\n\\end_inset\n\n for this material is given by \n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T_{0}\\mathbf{B}\\,.\n\\]\n\n\\end_inset\n\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nIsotropic contraction in a mixture containing a neo-Hookean solid.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"prescribed isotropic active contraction\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <T0 lc=\"2\">1</T0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Subsubsection\nPrescribed Fiber Stress\n\\end_layout\n\n\\begin_layout Standard\nAn active fiber stress,\n based on a Hill formulation,\n can be added via the material \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nactive fiber stress\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This material must be combined with a stable compressible material that acts as a passive matrix,\n using a \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nsolid mixture\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n container as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solid-Mixture\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The stress is given by,\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\sigma}^{a}=J^{-1}T\\left(\\lambda\\right)\\mathbf{A}.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\mathbf{A=a\\otimes a},$\n\\end_inset\n\nwith \n\\series bold\na\n\\series default\n the unit vector describing the fiber direction in the spatial frame,\n \n\\emph on\n\n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n\n\\emph default\nis the fiber stretch,\n \n\\emph on\nJ \n\\emph default\nis the jacobian of the deformation,\n and \n\\begin_inset Formula \n\\[\nT\\left(\\lambda\\right)=s_{max}a\\left(t\\right)s_{TL}\\left(\\lambda\\right)s_{TV}\\left(\\dot{\\lambda}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameters are defined below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsmax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale factor for defining maximum stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactivation level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstl\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFunction defining the stress-stretch relatioship for the material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstv\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFunction defining the stress-stretch rate relatioship for the material.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameters\n\\begin_inset Formula $s_{TL}$\n\\end_inset\n\nand \n\\begin_inset Formula $s_{TV}$\n\\end_inset\n\nare functions that need to be defined in place.\n There are currently two ways of defining these functions,\n either via a mathematical expression or a list of sample points.\n An example is given below.\n If these parametes are omitted,\n they are replaced by the constant 1 in the equation for the stress above.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Standard\nAn example defining the stl parameter via a mathematical expression.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"active fiber stress\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <smax>150</smax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <a lc=\"1\">0.1</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <stl type=\"math\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <math>(l-1)^2</math>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </stl>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nAn example defining the stl parameter via a point list.\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"solid mixture\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <mat_axis type=\"local\">0,0,0</mat_axis>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"active fiber stress\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <smax>150</smax>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <a lc=\"1\">0.1</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <stl type=\"point\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <interpolate>linear</interpolate>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <points>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <pt>0,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <pt>1,0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <pt>2,1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n      </points>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </stl>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Section\nViscous Fluids\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Fluids\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFluid mechanics analyses may be used to examine fluid flow over fixed domains.\n Fluid-structure interactions (FSI) may be used to examine fluid flow over deforming domains.\n It is also possible to perform solute (mass) transport analyses in fluid mixtures using the fluid-solutes module.\n FEBio's fluid,\n fluid-FSI and fluid-solutes modules treat the fluid as isothermal (constant and uniform temperature) and compressible;\n incompressible flow is simulated by prescribing a physically realistic bulk modulus \n\\begin_inset Formula $K$\n\\end_inset\n\n to model the fluid's elastic compressibility (for example,\n \n\\begin_inset Formula $K=2.2\\,\\text{GPa}$\n\\end_inset\n\n for water at room temperature).\n The Cauchy stress \n\\begin_inset Formula ${\\rm \\boldsymbol{\\sigma}}^{f}$\n\\end_inset\n\n in a fluid is given by \n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}^{f}=-p\\mathbf{I}+\\boldsymbol{\\tau}\\,,\\label{eq:cfd-stress}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $p$\n\\end_inset\n\n is the fluid pressure arising from the elastic and osmotic responses and \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n is the viscous stress resulting from the fluid viscosity and its rate of deformation.\n FEBio's fluid solver uses the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n instead of the \n\\emph on\neffective fluid pressure\n\\emph default\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n,\n as a nodal degree of freedom.\n The effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n may differ from the actual fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n if solutes are present in the fluid mixture,\n as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In fluid analyses the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is also the elastic contribution to the fluid pressure,\n resulting from changes in \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n.\n FEBio provides several alternative constitutive models for the elastic response \n\\begin_inset Formula $\\tilde{p}\\left(e^{f}\\right)$\n\\end_inset\n\n,\n including\n\\begin_inset Formula \n\\begin{equation}\n\\begin{aligned}\\tilde{p}\\left(e^{f}\\right) & =-Ke^{f} & \\text{linear}\\\\\n\\tilde{p}\\left(e^{f}\\right) & =K\\left(\\frac{1}{1+e^{f}}-1\\right) & \\text{nonlinear}\\\\\n\\tilde{p}\\left(e^{f}\\right) & =-K\\frac{\\ln\\left(1+e^{f}\\right)}{1+e^{f}} & \\text{log nonlinear}\n\\end{aligned}\n\\label{eq:fluid-pressure-state}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $K$\n\\end_inset\n\n is the fluid's bulk modulus (a physical property that may be looked up or measured).\n The reason for selecting \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n instead of \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is that the dilatation (volumetric strain) is a kinematic measure,\n just like the fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n (the other nodal degree of freedom) and its associated rate of deformation tensor,\n whereas \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is a function of state (it needs to be formulated as a function of the deformation,\n just like the viscous fluid stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n).\n Here,\n \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is called the \n\\emph on\nelastic pressure\n\\emph default\n because it represents the elastic response (equivalent to the stress-strain response in hyperelasticity for solid mechanics) of the fluid under isothermal conditions.\n The \n\\emph on\nlinear\n\\emph default\n model in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n is useful for nearly-incompressible flow,\n typically used for liquids.\n However,\n if one expects high volumetric strains in the flow (e.g.,\n at a stagnation point in a high-velocity fluid flow problem),\n it may be better to employ one of the two \n\\emph on\nnonlinear\n\\emph default\n formulations in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n In particular,\n for an ideal gas,\n the gauge pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is given by\n\\begin_inset Formula \n\\begin{equation}\n\\tilde{p}=P_{r}\\left(\\frac{\\theta}{J^{f}\\theta_{r}}-1\\right)\\quad J^{f}=1+e^{f}\\label{eq:ideal-gas-pressure}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\theta_{r}$\n\\end_inset\n\n is the (arbitrarily selected) reference temperature and \n\\begin_inset Formula $P_{r}$\n\\end_inset\n\n is the corresponding referential absolute pressure (at \n\\begin_inset Formula $\\theta=\\theta_{r}$\n\\end_inset\n\n and \n\\begin_inset Formula $J^{f}=1$\n\\end_inset\n\n).\n When an ideal gas undergoes an isothermal process (\n\\begin_inset Formula $\\theta=\\theta_{r}$\n\\end_inset\n\n),\n eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:ideal-gas-pressure\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n reduces to the \n\\emph on\nnonlinear\n\\emph default\n model in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with bulk modulus \n\\begin_inset Formula $K=P_{r}$\n\\end_inset\n\n.\n Thus,\n the \n\\emph on\nnonlinear\n\\emph default\n model is consistent with ideal gas law under isothermal processes.\n Isothermal processes produce zero heat flux,\n \n\\begin_inset Formula $\\mathbf{q}=\\mathbf{0}$\n\\end_inset\n\n,\n however heat is generated by the fluid viscosity at the rate \n\\begin_inset Formula $\\boldsymbol{\\tau}:\\mathbf{D}^{f}$\n\\end_inset\n\n where \n\\begin_inset Formula $\\mathbf{D}^{f}$\n\\end_inset\n\n is the rate of deformation tensor for the fluid (see below).\n Therefore,\n to maintain isothermal conditions,\n we must assume that this heat is implicitly radiated into or out of the fluid domain.\n For isentropic processes it can be shown that the temperature variation in an ideal gas obeys \n\\begin_inset Formula $\\theta/\\theta_{r}=\\left(J^{f}\\right)^{1-\\gamma}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is the ratio of isobaric to isochoric specific heat capacities,\n \n\\begin_inset Formula $\\gamma=c_{p0}/c_{v0}$\n\\end_inset\n\n.\n In that case,\n the pressure in an ideal gas evolves according to \n\\begin_inset Formula $\\tilde{p}=P_{r}\\left(\\left(1+e^{f}\\right)^{-\\gamma}-1\\right)$\n\\end_inset\n\n,\n which represents a fourth alternative to the three models presented in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with \n\\begin_inset Formula $K=P_{r}$\n\\end_inset\n\n.\n Contrary to the simplifying assumptions found in thermodynamic textbooks,\n an isentropic process is not automatically adiabatic (although the reverse is true).\n Therefore,\n when modeling ideal gases undergoing isentropic processes,\n it is also assumed that the equation of energy balance is implicitly satisfied by suitably radiating heat into or out of the fluid domain.\n\\end_layout\n\n\\begin_layout Standard\nThe fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n is evaluated from the dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n (the relative change in volume,\n or volumetric strain) using the user-specified model from eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n (the default is \n\\emph on\nlinear\n\\emph default\n),\n where \n\\begin_inset Formula $e^{f}=J^{f}-1$\n\\end_inset\n\n and \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n is the fluid volume ratio.\n The fluid density \n\\begin_inset Formula $\\rho^{f}$\n\\end_inset\n\n varies with \n\\begin_inset Formula $J^{f}$\n\\end_inset\n\n according to\n\\begin_inset Formula \n\\begin{equation}\n\\rho^{f}=\\frac{\\rho_{r}^{f}}{J^{f}}\\,,\\label{eq:cfd-density}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\rho_{r}^{f}$\n\\end_inset\n\n is the fluid density in the reference configuration (when the pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n is zero).\n The dependence of \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n on the fluid rate of deformation tensor \n\\begin_inset Formula $\\mathbf{D}^{f}=\\frac{1}{2}\\left(\\grad\\mathbf{v}^{f}+\\grad^{T}\\mathbf{v}^{f}\\right)$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{v}^{f}=$\n\\end_inset\n\nfluid velocity,\n is given by a constitutive model which may be chosen from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Viscous-Fluid-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n Though Newtonian and non-Newtonian fluids may be analyzed in this framework,\n all fluids are purely viscous (no viscoelasticity is included in this formulation).\n Setting the viscosity to zero is allowable,\n for the purpose of analyzing inviscid flow.\n The user is referred to the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n for a general description of this isothermal compressible viscous flow framework.\n\\end_layout\n\n\\begin_layout Standard\nFor fluid analyses,\n which are performed over a fixed mesh,\n FEBio solves for the components of the fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n and fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n at each node.\n In contrast,\n fluid-FSI analyses are performed over deforming meshes and FEBio treats fluid-FSI domains as a mixture of a viscous fluid \n\\begin_inset Formula $f$\n\\end_inset\n\n and a massless,\n frictionless porous solid \n\\begin_inset Formula $s$\n\\end_inset\n\n.\n Thus,\n the fluid encounters zero resistance as it flows through the deforming mesh (porous solid).\n The solid constituent of a fluid-FSI domain regularizes the mesh deformation;\n it is a hyperelastic solid whose constitutive relation is selected by the user;\n the recommended choice is the unconstrained neo-Hookean solid defined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Neo-Hookean\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\nu=0$\n\\end_inset\n\n and \n\\begin_inset Formula $E$\n\\end_inset\n\n set to a very small (but non-zero) value.\n For fluid-FSI analyses,\n FEBio solves for the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n the fluid velocity \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n relative to the mesh,\n and the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n.\n The fluid velocity is then calculated as \n\\begin_inset Formula \n\\begin{equation}\n\\mathbf{v}^{f}=\\mathbf{v}^{s}+\\mathbf{w}\\,,\\label{eq:FSI-fluid-velocity}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{v}^{s}$\n\\end_inset\n\n is the mesh velocity (the material time derivative of the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n).\n Since the mesh is fixed in a standard fluid analysis,\n \n\\begin_inset Formula $\\mathbf{v}^{s}=\\mathbf{0}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}^{f}=\\mathbf{w}$\n\\end_inset\n\n in that case.\n Therefore,\n we use \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n to refer to the fluid velocity degrees of freedom for fluid and fluid-FSI analyses.\n For both types of analyses,\n the no-slip condition for viscous fluids flowing along a boundary is satisfied by setting \n\\begin_inset Formula $\\mathbf{w}=\\mathbf{0}$\n\\end_inset\n\n on that boundary.\n\\end_layout\n\n\\begin_layout Standard\nOn any fluid boundary,\n the outward surface normal may be denoted by \n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n and the traction vector on the fluid is given by \n\\begin_inset Formula $\\mathbf{t}^{f}=\\boldsymbol{\\sigma}^{f}\\cdot\\mathbf{n}=-p\\,\\mathbf{n}+\\mathbf{t}^{\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n is the viscous traction.\n Essential (Dirichlet) boundary conditions may be prescribed on \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n,\n while natural (Neumann) boundary conditions may be prescribed on \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n.\n The appearance of velocity in both essential and natural boundary conditions may seem surprising at first.\n To better understand the nature of these boundary conditions,\n it is convenient to separate the velocity into its normal and tangential components,\n \n\\begin_inset Formula $\\mathbf{w}=w_{n}\\mathbf{n}+\\mathbf{w}_{t}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{w}_{t}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{w}$\n\\end_inset\n\n.\n In particular,\n for inviscid flow,\n the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n and its corresponding traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n are both zero,\n leaving \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n as the sole natural boundary condition;\n similarly,\n \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n becomes the only essential boundary condition in such flows,\n since \n\\begin_inset Formula $\\mathbf{w}_{t}$\n\\end_inset\n\n is unknown \n\\emph on\na priori\n\\emph default\n on a frictionless boundary and must be obtained from the solution of the analysis.\n \n\\end_layout\n\n\\begin_layout Standard\nIn general,\n prescribing \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n is equivalent to prescribing the elastic fluid pressure,\n since \n\\begin_inset Formula $p$\n\\end_inset\n\n is only a function of \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n according to eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n On a boundary where no conditions are prescribed explicitly,\n we conclude that \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n,\n which represents a frictionless wall.\n Conversely,\n it is possible to prescribe \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n on a boundary to produce a desired inflow or outflow while simultaneously stabilizing the flow conditions by prescribing a suitable viscous traction.\n Prescribing essential boundary conditions \n\\begin_inset Formula $\\mathbf{w}_{t}$\n\\end_inset\n\n and \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n determines the tangential velocity on a boundary as well as the elastic fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n,\n leaving the option to also prescribe the normal component of the viscous traction,\n \n\\begin_inset Formula $t_{n}^{\\tau}=\\mathbf{t}^{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n,\n to completely determine the normal traction \n\\begin_inset Formula $t_{n}^{f}=\\mathbf{t}^{f}\\cdot\\mathbf{n}$\n\\end_inset\n\n (or else \n\\begin_inset Formula $t_{n}^{\\tau}$\n\\end_inset\n\n naturally equals zero).\n Mixed boundary conditions represent common physical features:\n Prescribing \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{w}_{t}$\n\\end_inset\n\n completely determines the velocity \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n on a boundary;\n prescribing \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n and \n\\begin_inset Formula $e$\n\\end_inset\n\n completely determines the traction \n\\begin_inset Formula $\\mathbf{t}^{f}=\\boldsymbol{\\sigma}^{f}\\cdot\\mathbf{n}$\n\\end_inset\n\n on a boundary.\n Note that \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n are mutually exclusive boundary conditions,\n and the same holds for \n\\begin_inset Formula $\\mathbf{w}_{t}$\n\\end_inset\n\n and the tangential component of the viscous traction,\n \n\\begin_inset Formula $\\mathbf{t}_{t}^{\\tau}=\\left(\\mathbf{I}-\\mathbf{n}\\otimes\\mathbf{n}\\right)\\cdot\\mathbf{t}^{\\tau}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nFor viscous fluids,\n the no-slip boundary condition at an impermeable wall is enforced by setting \n\\begin_inset Formula $\\mathbf{w}_{t}=\\mathbf{0}$\n\\end_inset\n\n,\n whereas the wall impermeability condition implies \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n.\n Therefore,\n these conditions may be combined by prescribing wx,\n wy and wz components of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n to zero.\n FEBio offers a range of options for conveniently prescribing natural and mixed conditions on boundary surfaces in fluid analyses (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Surface-Loads\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Fluid Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Specification-Fluid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a fluid material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfluid\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<density>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid referential density \n\\begin_inset Formula $\\rho_{r}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nM\n\\series default\n/\n\\series bold\nL\n\\series default\n\n\\begin_inset Formula $^{\\mathrm{3}}$\n\\end_inset\n\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nBulk modulus \n\\begin_inset Formula $K$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<viscous>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of viscous model for \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <viscous> tag encloses a description of the viscous stress constitutive relation and associated material properties,\n as may be selected from the list provided in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Viscous-Fluid-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The parameters <density> and <k> must be greater than 0.\n These parameters are always required.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Water\" type=\"fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<density>1</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<k>2.2e+09</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<viscous type=\"Newtonian fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<mu>0.001</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t\t<kappa>0</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t</viscous>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nViscous Fluid Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Viscous-Fluid-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nViscous fluid materials provide a constitutive relation for the dependence of the viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n on the rate of deformation tensor \n\\begin_inset Formula $\\mathbf{D}^{f}$\n\\end_inset\n\n of a fluid.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nNewtonian Fluid\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Newtonian-Fluid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Newtonian fluid is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nNewtonian fluid\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity \n\\begin_inset Formula $\\mu$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kappa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbulk viscosity \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=\\left(\\kappa-\\frac{2}{3}\\mu\\right)\\left(\\tr\\mathbf{D}\\right)\\mathbf{I}+2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nStokes' condition is a constitutive assumption that sets \n\\begin_inset Formula $\\tr\\boldsymbol{\\tau}=0$\n\\end_inset\n\n,\n implying that \n\\begin_inset Formula $\\kappa=0$\n\\end_inset\n\n.\n This assumption is only valid for some fluids (e.g.,\n monoatomic gas \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Panton06\"\nliteral \"true\"\n\n\\end_inset\n\n).\n Users may use or ignore this assumption by selecting an appropriate value for \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Newtonian fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu>0.001</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<kappa>0</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nBingham Fluid\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Bingham-Fluid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Bingham fluid \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Papanastasiou87\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nBingham\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at infinite shear rate,\n \n\\begin_inset Formula $\\mu_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<tauy>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyield stress,\n \n\\begin_inset Formula $\\tau_{y}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\mu=\\mu_{\\infty}+\\frac{\\tau_{y}}{\\dot{\\gamma}}\\left(1-e^{-n\\dot{\\gamma}}\\right)\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n is the engineering shear rate.\n In the limit as \n\\begin_inset Formula $\\dot{\\gamma}\\to0$\n\\end_inset\n\n the viscosity is given by \n\\begin_inset Formula $\\mu=\\mu_{\\infty}+n\\tau_{y}$\n\\end_inset\n\n.\n If we define the scalar shear stress \n\\begin_inset Formula $\\tau=\\sqrt{\\boldsymbol{\\tau}:\\boldsymbol{\\tau}/2}$\n\\end_inset\n\n,\n if follows that \n\\begin_inset Formula $\\tau=\\mu_{\\infty}\\dot{\\gamma}+\\tau_{y}\\left(1-e^{-n\\dot{\\gamma}}\\right)$\n\\end_inset\n\n.\n Effectively,\n this constitutive model represents a bilinear response for \n\\begin_inset Formula $\\tau$\n\\end_inset\n\n versus \n\\begin_inset Formula $\\dot{\\gamma}$\n\\end_inset\n\n,\n with a slope of \n\\begin_inset Formula $\\mu_{\\infty}+n\\tau_{y}$\n\\end_inset\n\n when \n\\begin_inset Formula $\\tau<\\tau_{y}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mu=\\mu_{\\infty}$\n\\end_inset\n\n when \n\\begin_inset Formula $\\tau>\\tau_{y}$\n\\end_inset\n\n.\n The exponential function rounds the corner at the intersection of these two lines.\n For an ideal Bingham fluid one would need to let \n\\begin_inset Formula $n\\to\\infty$\n\\end_inset\n\n.\n In practice,\n values of \n\\begin_inset Formula $n$\n\\end_inset\n\n between 5 and 50 work well,\n with the lower end of this range producing faster convergence of the finite element solution.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Bingham\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu>1</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<tauy>40</tauy>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<n>40</n>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCarreau Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Carreau-Fluid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Carreau model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Cho91\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCarreau\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at zero shear rate,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mui>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at infinite shear rate,\n \n\\begin_inset Formula $\\mu_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime constant\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\mu=\\mu_{\\infty}+\\left(\\mu_{0}-\\mu_{\\infty}\\right)\\left(1+\\left(\\lambda\\dot{\\gamma}\\right)^{2}\\right)^{\\left(n-1\\right)/2}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n is the engineering shear rate.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Carreau\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu0>0.056</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mui>0.0035</mui>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<lambda>3.3</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<n>0.36</n>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCarreau-Yasuda Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Carreau-Yasuda-Model\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Carreau-Yasuda model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Cho91\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCarreau-Yasuda\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at zero shear rate,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mui>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at infinite shear rate,\n \n\\begin_inset Formula $\\mu_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime constant\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<a>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npower-law exponent divider\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\mu=\\mu_{\\infty}+\\left(\\mu_{0}-\\mu_{\\infty}\\right)\\left(1+\\left(\\lambda\\dot{\\gamma}\\right)^{a}\\right)^{\\left(n-1\\right)/a}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n is the engineering shear rate.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Carreau-Yasuda\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu0>0.056</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mui>0.0035</mui>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<lambda>1.9</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<n>0.22</n>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<a>1.25</a>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nPowell-Eyring Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Powell-Eyring-Model\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Powell-Eyring model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Cho91\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nPowell-Eyring\n\\shape italic\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at zero shear rate,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mui>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at infinite shear rate,\n \n\\begin_inset Formula $\\mu_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime constant\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\mu=\\mu_{\\infty}+\\left(\\mu_{0}-\\mu_{\\infty}\\right)\\frac{\\sinh^{-1}\\lambda\\dot{\\gamma}}{\\lambda\\dot{\\gamma}}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n is the engineering shear rate.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Powell-Eyring\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu0>0.056</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mui>0.0035</mui>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<lambda>5.4</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nCross Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Cross-Model\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Cross model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Cho91\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nCross\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at zero shear rate,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n] \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mui>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at infinite shear rate,\n \n\\begin_inset Formula $\\mu_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<lambda>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime constant\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<m>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexponent\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\mu=\\mu_{\\infty}+\\left(\\mu_{0}-\\mu_{\\infty}\\right)\\frac{1}{1+\\left(\\lambda\\dot{\\gamma}\\right)^{m}}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n is the engineering shear rate.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Cross\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu0>0.056</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mui>0.0035</mui>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<lambda>1.0</lambda>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<m>1.0</m>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nQuemada Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Quemada-Fluid\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a Quemada model \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Quemada78\"\nliteral \"true\"\n\n\\end_inset\n\n is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\nQuemada\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<mu0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshear viscosity at zero shear rate suspension volume fraction,\n \n\\begin_inset Formula $\\mu_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nP\n\\series default\n\n\\begin_inset Formula $\\cdot$\n\\end_inset\n\n\n\\series bold\nt\n\\series default\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<H>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsuspension volume fraction,\n \n\\begin_inset Formula $H$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nintrinsic relative viscosity at zero shear rate,\n \n\\begin_inset Formula $k_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kinf>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nintrinsic relative viscosity at infinite shear rate,\n \n\\begin_inset Formula $k_{\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gc>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncritical shear rate,\n \n\\begin_inset Formula $\\dot{\\gamma}_{c}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[\n\\series bold\nt\n\\series default\n\n\\begin_inset Formula $^{-1}$\n\\end_inset\n\n]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe viscous shear stress for this material model is\n\\begin_inset Formula \n\\[\n\\boldsymbol{\\tau}=2\\mu\\mathbf{D}\n\\]\n\n\\end_inset\n\nwhere\n\\begin_inset Formula \n\\[\n\\mu=\\frac{\\mu_{0}}{\\left(1-k\\frac{H}{2}\\right)^{2}}\n\\]\n\n\\end_inset\n\nand\n\\begin_inset Formula \n\\[\nk=\\frac{k_{0}+k_{\\infty}\\sqrt{\\dot{\\gamma}_{r}}}{1+\\sqrt{\\dot{\\gamma}_{r}}}\\,,\\quad\\dot{\\gamma}_{r}=\\frac{\\dot{\\gamma}}{\\dot{\\gamma}_{c}}\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $\\dot{\\gamma}=\\sqrt{2\\mathbf{D}:\\mathbf{D}}$\n\\end_inset\n\n is the engineering shear rate.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<viscous type=\"Quemada\">\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<mu0>0.03</mu0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<H>0.5</H>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<k0>3.691</k0>\n\\end_layout\n\n\\begin_layout LyX-Code\n\t<kinf>1.778</kinf>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <gc>2.30</gc>\n\\end_layout\n\n\\begin_layout LyX-Code\n</viscous>\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Fluid-FSI Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Fluid-FSI-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a fluid-FSI material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n This type of material is used for fluid flow through a deforming mesh.\n The material is treated as a special case of fluid-solid mixture,\n with the solid material used to regularize the mesh deformation.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fluid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of fluid model \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of elastic solid model for mesh deformation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <fluid> tag encloses a description of the fluid,\n as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Fluid\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <solid> tag encloses a description of the solid,\n which should be modeled as an unconstrained elastic solid,\n as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The recommended choice is the unconstrained neo-Hookean solid defined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Neo-Hookean\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\nu=0$\n\\end_inset\n\n and \n\\begin_inset Formula $E$\n\\end_inset\n\n set to a very small (but non-zero) value.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Fluid-FSI domain\" type=\"fluid-FSI\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fluid type=\"fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1000</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>2.2e9</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <viscous type=\"Newtonian fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <mu>0.001</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <kappa>0</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </viscous>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fluid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>0</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1e-9</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe value of <density> in the <solid> material is internally set to zero regardless of the user-defined value,\n since the deforming mesh should not be subjected to inertial forces.\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Biphasic-FSI Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Fluid-BFSI-Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a biphasic-FSI material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nbiphasic-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n This is a hybrid biphasic material used for modeling dynamic viscous fluid flow and frictional filtration through a porous-deformable solid.\n In contrast to the standard biphasic material described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n,\n this material accommodates fluid viscosity and fluid dynamics;\n therefore it can be interfaced seamlessly with a fluid-FSI material to allow viscous fluid transport across that interface.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<phi0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolid volume fraction \n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n in the reference configuration (\n\\begin_inset Formula $0<\\varphi_{r}^{s}<1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n[ ]\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fluid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the fluid material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the porous solid material\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<permeability>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of the hydraulic permeability\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe parameter <phi0> must be greater than 0 (no solid) and less than 1 (no porosity).\n The <fluid> tag encloses a description of the fluid,\n as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Fluid\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <solid> tag encloses a description of the solid matrix constitutive relation and associated material properties,\n such as those selected from the list provided in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Elastic-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n-\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Multigeneration-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <permeability> tag encloses a description of the permeability constitutive relation and associated material properties,\n as may be selected from the list presented in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Permeability-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n To simulate the special case of zero friction between the fluid and solid (such as in the fluid-FSI material),\n it is permissible to set the permeability to zero.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"2\" name=\"Material2\" type=\"biphasic-FSI\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <phi0>0.25</phi0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fluid type=\"fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>1e+09</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <viscous type=\"Newtonian fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <mu>1</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <kappa>0</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </viscous>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fluid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>2e-9</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>200</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <permeability type=\"perm-const-iso\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <perm>0.005625</perm>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </permeability>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe value of <density> in the <fluid> and <solid> materials represents the true mass density of each constituent.\n\\end_layout\n\n\\begin_layout Subsection\nGeneral Specification of Fluid-Solutes Materials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:General-Fluid-Solutes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for a fluid-solutes material is \n\\shape italic\n\n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n This type of material is used for fluid flow and solute mass transport through a fixed mesh.\n The following parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<fluid>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of fluid model\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"none\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<osmotic_coefficient>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of osmotic coefficient for fluid-solutes mixture\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<solute>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecification of (any number of) solute(s)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe <fluid> tag encloses a description of the fluid,\n as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Specification-Fluid\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The <solid> tag encloses a description of the solid,\n which should be modeled as an unconstrained elastic solid,\n as given in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Unconstrained-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The recommended choice is the unconstrained neo-Hookean solid defined in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Neo-Hookean\"\nnolink \"false\"\n\n\\end_inset\n\n,\n with \n\\begin_inset Formula $\\nu=0$\n\\end_inset\n\n and \n\\begin_inset Formula $E$\n\\end_inset\n\n set to a very small (but non-zero) value.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"Fluid-FSI domain\" type=\"fluid-FSI\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fluid type=\"fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>1000</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <k>2.2e9</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <viscous type=\"Newtonian fluid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <mu>0.001</mu>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <kappa>0</kappa>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </viscous>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fluid>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <solid type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <density>0</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1e-9</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </solid>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe value of <density> in the <solid> material is internally set to zero regardless of the user-defined value,\n since the deforming mesh should not be subjected to inertial forces.\n\\end_layout\n\n\\begin_layout Section\nPrestrain material\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio a prestrain can be defined for most materials that allows the user to prestrain or prestress the model before loading is applied.\n This is useful for modeling biologically tissues that exhibit residual strain,\n or whose initial state cannot assumed to be stress-free (e.g.\n arteries from in-vivo image data.) \n\\end_layout\n\n\\begin_layout Standard\nIf you use this feature in your published research,\n please cite the corresponding paper that details the theoretical framework and the implementation in FEBio \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Maas2016\"\nliteral \"true\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Subsection\nIntroduction\n\\end_layout\n\n\\begin_layout Standard\nIn the modeling of biological tissues there are various reasons why the model needs to start from a prestressed configuration.\n For example,\n the biological tissue may exhibit residual stress or in situ stress.\n Or the geometry is taken from in vivo data and is subjected to loads in the reference configuration.\n In these cases,\n the reference configuration cannot be considered stress-free and this so-called prestress must somehow be accounted for.\n In large strain analysis,\n stresses are usually not additive and in addition,\n it must somehow be ensured that these prestresses are in equilibrium in the reference configuration.\n In general,\n this makes prestressing the reference configuration challenging in large strain analyses.\n\\end_layout\n\n\\begin_layout Standard\nProgress can be made by assuming that the material is hyperelastic and that a prestrain can be found that defines the prestress via the hyperelastic constitutive formulation.\n Usually this prestrain is characterized by a second-order tensor,\n termed the prestrain gradient and here denoted by \n\\begin_inset Formula $\\mathnormal{\\boldsymbol{F}}_{p}$\n\\end_inset\n\n.\n The interpretation of this tensor is that its inverse,\n applied to a small neighborhood of a point in the reference configuration,\n would render this neighborhood stress-free.\n The total elastic response of the material is then defined via the elastic deformation gradient.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{F_{e}=}\\boldsymbol{F\\cdot F_{p}}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\boldsymbol{F}$\n\\end_inset\n\nis the deformation gradient that is the result of subsequent loading of the reference configuration.\n Finally,\n the Cauchy stress and spatial elasticity tangent are given by\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma=\\mathcal{F}\\left(F_{e}\\right),}\\boldsymbol{\\;c=\\mathcal{G\\left(\\mathnormal{F_{e}}\\right)}}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $\\boldsymbol{\\mathcal{F}}$\n\\end_inset\n\n and \n\\emph on\n\n\\begin_inset Formula $\\mathcal{\\boldsymbol{G}}$\n\\end_inset\n\n\n\\emph default\n are the same response functions from the “natural” material,\n i.e.\n the response of the material that starts from a stress-free configuration.\n \n\\end_layout\n\n\\begin_layout Standard\nIt must be noted that although \n\\begin_inset Formula $\\mathnormal{\\boldsymbol{F}}_{p}$\n\\end_inset\n\n is termed the prestrain gradient,\n in general it will not be the derivative of a deformation map.\n Consequently,\n a global stress-free reference configuration may not exist and the mapping of a neighborhood in the reference configuration to a stress-free state can at best be defined only locally.\n \n\\end_layout\n\n\\begin_layout Standard\nIn addition,\n it must also be recognized that the prestrain gradient is not unique.\n Due to the requirements of objectivity in the presence of material symmetries,\n the prestrain gradients \n\\begin_inset Formula $\\mathnormal{\\boldsymbol{F}}_{p}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathnormal{\\boldsymbol{F'}}_{p}=\\boldsymbol{F_{p}Q}$\n\\end_inset\n\n ,\n where \n\\begin_inset Formula $\\boldsymbol{Q}$\n\\end_inset\n\n is any orthogonal tensor,\n must result in the same material response.\n This implies that the prestrain can only be dependent on the right stretch tensor \n\\begin_inset Formula $\\boldsymbol{V_{p}}$\n\\end_inset\n\n.\n However,\n in general even the right-stretch tensor cannot be defined uniquely.\n For instance,\n an isotropic material would only depend on the eigenvalues of this tensor and thus any tensor of the form \n\\begin_inset Formula $\\boldsymbol{RDR^{T}}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\boldsymbol{R}$\n\\end_inset\n\n is any orthogonal tensor and \n\\begin_inset Formula $\\boldsymbol{D}$\n\\end_inset\n\n is a diagonal tensor with the three eigenvalues of \n\\begin_inset Formula $\\boldsymbol{V_{p}}$\n\\end_inset\n\non its diagonal,\n would render the same material response.\n In the presence of material symmetries,\n the situation is similar although \n\\begin_inset Formula $\\boldsymbol{R}$\n\\end_inset\n\n will only be part of the symmetry group.\n\\end_layout\n\n\\begin_layout Standard\nDespite the fact that \n\\begin_inset Formula $\\mathnormal{\\boldsymbol{F}}_{p}$\n\\end_inset\n\n is not unique,\n it is far from arbitrary.\n The most important restriction is that the resulting prestress field \n\\begin_inset Formula $\\boldsymbol{\\sigma}_{p}$\n\\end_inset\n\nmust be in equilibrium with the prestressed reference configuration.\n In other words it must hold that\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\ndiv\\boldsymbol{\\sigma}_{p}=\\boldsymbol{0}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nin the interior of the reference domain (in the absence of body forces) and that on the free boundaries of the domain\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\sigma}_{p}\\cdot\\boldsymbol{n}=0\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a finite element simulation,\n when these conditions are not satisfied,\n the mesh will distort and a new reference state is obtained as well as an altered prestrain field.\n We will denote the deformation gradient between the original (incompatible) reference configuration and prestressed reference configuration by \n\\begin_inset Formula $\\boldsymbol{F}_{c}$\n\\end_inset\n\n.\n When the mesh distorts the effectively applied prestrain field is also altered.\n In general,\n this is not desired and a correction to the prestrain field is necessary that either eliminates the distortion or finds a new reference geometry in which the desired prestrain field is supported.\n\\end_layout\n\n\\begin_layout Standard\nThe prestrain framework as currently implemented assumes that the total effective prestrain that is compatible with the possibly altered prestressed reference configuration is given by the following multiplicative decomposition.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{F}_{p}=\\boldsymbol{F}_{c}\\cdot\\boldsymbol{\\hat{F}}_{p}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBoth the prestrain gradient and the distortion are in general unknown.\n The framework implements an iterative algorithm that updates \n\\begin_inset Formula $\\boldsymbol{F}_{c}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\boldsymbol{\\hat{F}}_{p}$\n\\end_inset\n\n until an effective prestrain gradient is found that is compatible with the possibly distorted reference configuration.\n Without loss of generality the framework assumes that the altered prestrain gradient \n\\begin_inset Formula $\\boldsymbol{\\hat{F}}_{p}$\n\\end_inset\n\nis itself given by the multiplicative decomposition\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\boldsymbol{\\hat{F}}_{p}=\\boldsymbol{G}\\cdot\\boldsymbol{F}_{0}\n\\end{equation}\n\n\\end_inset\n\nwhere \n\\begin_inset Formula $\\mathbf{F}_{0}$\n\\end_inset\n\n is an initial guess for the prestrain gradient and \n\\begin_inset Formula $\\mathbf{G}$\n\\end_inset\n\n is a correction factor.\n The algorithm starts by initializing \n\\begin_inset Formula $\\mathbf{F}_{0}$\n\\end_inset\n\n to a user-defined value and \n\\begin_inset Formula $\\mathbf{G}$\n\\end_inset\n\n to the identity.\n This initial value can be specified in a variety of ways and the code that generates this initial guess is called the prestrain generator.\n The framework provides several prestrain generators and users can easily add new ones.\n Then,\n a forward analysis is executed keeping \n\\begin_inset Formula $\\hat{\\mathbf{F}}_{p}$\n\\end_inset\n\nfixed.\n Unless the initial guess was compatible with the reference configuration,\n the mesh will distort.\n This distortion will define the new value for \n\\begin_inset Formula $\\mathbf{G}$\n\\end_inset\n\n which can be calculated directly from the nodal displacements in the usual manner.\n Next,\n the framework will update using a user-defined update rule.\n How \n\\begin_inset Formula $\\mathbf{G}$\n\\end_inset\n\n is updated depends on the particular application and the framework expects the user do provide the desired update rule.\n The current implementation provides two particular update rules:\n one to eliminate the distortion and one to enforce a particular form of the prestrain gradient.\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:The-Prestrain-Update\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n on how to use these update rules.\n In addition,\n users can easily implement new update rules.\n After this update,\n a new forward analysis is executed.\n This process is repeated until \n\\begin_inset Formula $\\mathbf{G}$\n\\end_inset\n\n converges.\n\\end_layout\n\n\\begin_layout Subsection\nThe Prestrain Material\n\\end_layout\n\n\\begin_layout Standard\nPrestrain can be applied by defining the \n\\emph on\nprestrain\n\\emph default\n material.\n This material acts as a wrapper to the underlying elastic constitutive model.\n There are two flavors of this material,\n one for coupled materials and one for uncoupled.\n In either case,\n the material defines two properties.\n The \n\\emph on\nelastic\n\\emph default\n property defines the elastic constitutive model.\n Any of the materials in FEBio could be used,\n however,\n the prestrain material has only been validated for hyperelastic materials.\n The second property,\n called \n\\emph on\nprestrain\n\\emph default\n,\n defines the prestrain generator.\n This property defines how the initial prestrain gradient is calculated.\n Currently,\n it can be defined as a full second order matrix or as a fiber stretch.\n The prestrain data can be defined at the material level or at the element level.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprestrain\n\\emph default\n property defines the way that the initial prestrain gradient tensor is calculated.\n An algorithm that calculates the initial prestrain based on user input is here referred to as a prestrain generator.\n The type attribute is used to create a specific instance of a prestrain generator.\n Currently,\n the following generators are available.\n\\end_layout\n\n\\begin_layout Subsubsection\nprestrain gradient\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nprestrain gradient \n\\emph default\ngenerator defines the full prestrain gradient matrix either for the entire material or for each element separately.\n It has the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nramp\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscale factor that ramps up the prestrain gradient (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family typewriter\nF0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3x3 target prestrain gradient matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe prestrain gradient that is applied is calculated by the following equation.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\mathbf{F}_{g}=\\mathbf{I}\\left(1-r\\right)+\\mathbf{F}_{0}r\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $F_{g}$\n\\end_inset\n\n is the applied prestrain gradient,\n \n\\begin_inset Formula $\\mathbf{I}$\n\\end_inset\n\n is the 3x3 identity tensor,\n \n\\begin_inset Formula $\\mathbf{F}_{0}$\n\\end_inset\n\n is the target prestrain gradient,\n defined by F0 parameter,\n and \n\\emph on\nr\n\\emph default\n is the ramp factor.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout Standard\nIn this example,\n the prestrain gradient is ramped up to a desired gradient value via a load controller.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"prestrain elastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elastic type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <E>1.0</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prestrain type=\"prestrain gradient\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <ramp lc=\n\\begin_inset Quotes qrd\n\\end_inset\n\n1\n\\begin_inset Quotes qrd\n\\end_inset\n\n>1.0</ramp>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <F0>2,0,0,0,2,0,0,0,2</F0>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </prestrain>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material> \n\\end_layout\n\n\\begin_layout Subsubsection\nin-situ stretch\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nin-situ stretch\n\\emph default\n generator option calculates a prestrain gradient based on a fiber stretch and the fiber vector defined by the elastic material of the prestrain material.\n This implies that this elastic material must define a \n\\emph on\nfiber\n\\emph default\n property.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ninitial values\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninitial fiber stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nisochoric\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuse the isochoric prestrain generator option\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis option generates one of the following prestrain gradients,\n depending on the isochoric option.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\begin{equation}\n\\hat{\\mathbf{F}}_{p,iso}=\\mathbf{Q}\\left[\\begin{array}{ccc}\n\\lambda\\\\\n & \\lambda^{-1/2}\\\\\n &  & \\lambda^{-1/2}\n\\end{array}\\right]\\mathbf{Q^{\\mathrm{\\mathit{T}}}},\\qquad\\hat{\\mathbf{F}}_{p,uni}=\\mathbf{Q}\\left[\\begin{array}{ccc}\n\\lambda\\\\\n & 1\\\\\n &  & 1\n\\end{array}\\right]\\mathbf{Q^{\\mathit{T}}}\n\\end{equation}\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf the \n\\emph on\nisochoric\n\\emph default\n option is set to 1,\n then \n\\begin_inset Formula $\\hat{\\mathbf{F}}_{p,iso}$\n\\end_inset\n\nis used.\n Otherwise,\n \n\\begin_inset Formula $\\hat{\\mathbf{F}}_{p,uni}$\n\\end_inset\n\nis used.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe rotation tensor is defined implicitly via the fiber vector \n\\begin_inset Formula $\\boldsymbol{a}$\n\\end_inset\n\n and the prestrain gradient tensor is effectively determined via,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\hat{\\mathbf{F}}_{p,iso}=\\lambda\\mathbf{A}+\\mu\\left(\\boldsymbol{1-}\\mathbf{A}\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $\\mathbf{A}=\\boldsymbol{a}\\otimes\\boldsymbol{a},$\n\\end_inset\n\n\n\\begin_inset Formula $\\lambda$\n\\end_inset\n\n the prescribed fiber stretch,\n and \n\\begin_inset Formula $\\mu=\\lambda^{-1/2}$\n\\end_inset\n\n or \n\\begin_inset Formula $\\mu=1$\n\\end_inset\n\n depending on the \n\\emph on\nisochoric \n\\emph default\noption.\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" type=\"prestrain elastic\">\n\\end_layout\n\n\\begin_layout LyX-Code\n   <elastic type=\"coupled trans-iso Mooney-Rivlin\">\n\\end_layout\n\n\\begin_layout LyX-Code\n        <k>0.1</k>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <density>1e-09</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <c1>0.01</c1>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <c2>0</c2>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <c3>0.0139</c3>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <c4>116.22</c4>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <c5>535.09</c5>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <lam_max>1.046</lam_max>\n\\end_layout\n\n\\begin_layout LyX-Code\n        <fiber type=\"vector\">-0.0804,-0.508,-0.858</fiber>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </elastic>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <prestrain type=\"in-situ stretch\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <stretch lc=\"1\">1.05</stretch>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <isochoric>1</isochoric>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </prestrain>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material> \n\\end_layout\n\n\\begin_layout Section\nContinuous-Discontinuous Damage\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Continuous-Damage-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn this damage formulation,\n the damage variable D is embedded inside the strain-energy function.\n The implementation follows largely the formulation in Balzani [\n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"BALZANI2012139\"\nliteral \"false\"\n\n\\end_inset\n\n] and is further detailed in [\n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"ALLAN2022105342\"\nliteral \"false\"\n\n\\end_inset\n\n].\n \n\\end_layout\n\n\\begin_layout Standard\nThe scalar damage variable \n\\begin_inset Formula $\\mathit{D}$\n\\end_inset\n\n is embedded in the strain energy function of the fibers,\n its relationship is dependent on the Continuous Damage solid constituent chosen.\n The scalar damage variable \n\\begin_inset Formula $\\mathit{D}$\n\\end_inset\n\n has continuous and discontinuous damage components defined by damage internal damage variables \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n and \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following material damage parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<Dmax>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $D_{\\infty}$\n\\end_inset\n\n(\n\\begin_inset Formula $0\\leqslant D_{\\infty}\\leqslant1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<beta_s>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\beta_{s}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\beta_{s}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<gamma_max>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\gamma_{\\infty}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\gamma_{\\infty}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<t0>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $t_{0}$\n\\end_inset\n\n (\n\\begin_inset Formula $t_{0}\\geq0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote the parameter \n\\begin_inset Formula $\\mathit{t_{0}}$\n\\end_inset\n\n is the time in the current loading situation where the damage model is initiated.\n\\end_layout\n\n\\begin_layout Standard\nThe scalar damage variable \n\\begin_inset Formula $\\mathit{D}$\n\\end_inset\n\n is a function of \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n,\n the continuous damage variable,\n and is given by\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\mathit{D(\\beta)=D_{s}\\left[1-\\exp\\left(\\frac{\\ln(1-r_{s})}{\\beta_{s}}\\beta\\right)\\right]},$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n is defined by\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\beta=\\left\\langle \\tilde{\\beta}-\\tilde{\\beta_{ini}}\\right\\rangle $\n\\end_inset\n\n with \n\\begin_inset Formula $\\tilde{\\beta}=\\intop_{0}^{t}\\dot{\\Psi^{0}}ds$\n\\end_inset\n\n,\n\\end_layout\n\n\\begin_layout Standard\nand \n\\begin_inset Formula $\\beta_{s}$\n\\end_inset\n\n is the value of the internal material variable \n\\begin_inset Formula $\\beta$\n\\end_inset\n\n that has reached a certain fraction \n\\begin_inset Formula $\\mathit{r_{s}}$\n\\end_inset\n\n(\n\\begin_inset Formula $0\\leqslant r_{s}\\leqslant1$\n\\end_inset\n\n) of the maximal damage value \n\\begin_inset Formula $\\mathit{D}_{s}$\n\\end_inset\n\n for a fixed maximum load level.\n At \n\\begin_inset Formula $\\beta=\\beta_{s}$\n\\end_inset\n\n,\n the overall damage \n\\begin_inset Formula $D=r_{s}*D_{s}$\n\\end_inset\n\n.\n For example,\n in Figure 1,\n \n\\begin_inset Formula $D_{s}=1,$\n\\end_inset\n\n\n\\begin_inset Formula $r_{s}=0.5,$\n\\end_inset\n\nand \n\\begin_inset Formula $\\beta_{s}=1$\n\\end_inset\n\n,\n meaning the overall damage in this cycle has reached 50% of the maximal damage value \n\\begin_inset Formula $\\mathit{D}_{s}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Graphics\n\tfilename Figures/beta.png\n\tscale 80\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\nOverall Damage variable \n\\begin_inset Formula $\\mathit{D}$\n\\end_inset\n\nas a function of Continuous Damage variable \n\\begin_inset Formula $\\mathit{\\beta}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nTo make sure damage evolution begins to accumulate only when damage is accrued,\n \n\\begin_inset Formula $\\tilde{\\beta_{ini}}$\n\\end_inset\n\n is the internal variable \n\\begin_inset Formula $\\tilde{\\beta}$\n\\end_inset\n\n at an initial damage state and \n\\begin_inset Formula $\\Psi^{0}$\n\\end_inset\n\n is the undamaged effective strain energy density of the fibrils.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula $\\mathit{D_{s}}$\n\\end_inset\n\n is a function increasing the maximally reachable damage value for increased maximum load levels.\n It is assumed that the maximum damage is itself a function of the internal variable \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n,\n describing the discontinuous damage variable and is given by\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\mathit{D_{s}(\\gamma)=D_{\\infty}\\left[1-\\exp\\left(\\frac{\\ln(1-r_{\\infty})}{\\gamma_{\\infty}}\\gamma\\right)\\right],}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n is defined by\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\gamma=\\underset{s\\epsilon\\left[0,t\\right]}{max}\\left\\langle \\varPsi^{0}-\\varPsi_{ini}^{0}\\right\\rangle ,\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nand \n\\begin_inset Formula $\\gamma_{\\infty}$\n\\end_inset\n\n is the value of the internal material variable \n\\begin_inset Formula $\\gamma$\n\\end_inset\n\n that has reached a certain fraction \n\\begin_inset Formula $\\mathit{r_{\\infty}}(0\\leqslant r_{\\infty}\\leqslant1)$\n\\end_inset\n\n of the maximal damage value \n\\begin_inset Formula $\\mathit{D}_{\\infty}$\n\\end_inset\n\n for a fixed maximum load level.\n At \n\\begin_inset Formula $\\gamma=\\gamma_{\\infty}$\n\\end_inset\n\n,\n the overall damage \n\\begin_inset Formula $D_{s}=r_{\\infty}*D_{\\infty}$\n\\end_inset\n\n (the same relationship can be adapted from Figure 1 with \n\\begin_inset Formula $\\gamma\\sim\\beta,r_{\\infty}\\sim r_{s},D_{\\infty}\\sim D_{s},D_{s}\\sim D$\n\\end_inset\n\n).\n \n\\begin_inset Formula $\\Psi_{ini}^{0}$\n\\end_inset\n\n denotes the strain energy density at an initial damage state obtained at the limit of the physiological domain (e.g.\n in relation to arterial expansion).\n It is the initial damage strain energy threshold value for the fibers.\n The time associated with the loading history is denoted by \n\\begin_inset Formula $\\mathit{s\\epsilon\\mathbb{\\mathbb{R}}}$\n\\end_inset\n\nand the time associated with the current loading situation is represented by \n\\begin_inset Formula $\\mathit{t\\epsilon\\mathbb{R^{+}}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe strain energy is assumed to be of the following form,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\Psi(C,D)=m(P(C,D))$\n\\end_inset\n\n,\n\\end_layout\n\n\\begin_layout Standard\nwhere the modified internal energy function \n\\begin_inset Formula $\\mathit{P(C,D)}=(1-D)\\bar{P}-c$\n\\end_inset\n\n with the scalar damage variable \n\\begin_inset Formula $\\mathit{D}$\n\\end_inset\n\n (defined above).\n \n\\begin_inset Formula $m(P(C,D))$\n\\end_inset\n\n is a convex and monotonically increasing function,\n whose first derivative is zero in the origin.\n Possible functions of \n\\begin_inset Formula $\\bar{P}$\n\\end_inset\n\n are given by\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\bar{P}_{1}:=J_{4}^{(a)}$\n\\end_inset\n\n,\n\\begin_inset Formula $\\bar{P}_{3}:=K_{1}^{(a)}$\n\\end_inset\n\n,\n\\begin_inset Formula $\\bar{P}_{2}:=K_{2}^{(a)}$\n\\end_inset\n\n,\n\\begin_inset Formula $\\bar{P}_{4}:=K_{3}^{(a)}$\n\\end_inset\n\n,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n(\n\\begin_inset Formula $\\bar{P}_{5}:=I_{1},\\bar{P}_{6}:=I_{2},\\bar{P}_{7}:=I_{3}$\n\\end_inset\n\n),\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $a$\n\\end_inset\n\n denotes the direction of the fibers.\n In this formulation we take \n\\begin_inset Formula $\\bar{P}=\\varPsi^{0}$\n\\end_inset\n\n.\n Note The fifth invariant is not polyconvex,\n therefore alternative polyconvex invariant functions are defined as\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $K_{1}^{(a)}=tr\\left[\\mathrm{Cof}CM_{(a)}\\right]=J_{5}^{(a)}-I_{1}J_{4}^{(a)}+I_{2}$\n\\end_inset\n\n,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $K_{2}^{(a)}=tr\\left[C(I-M_{(a)})\\right]=I_{1}-J_{4}^{(a)}$\n\\end_inset\n\n,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $K_{3}^{(a)}=tr\\left[\\mathrm{Cof}C(I-M_{(a)})\\right]=I_{1}J_{4}^{(a)}+J_{5}^{(a)}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe subtraction of \n\\begin_inset Formula $c$\n\\end_inset\n\n ensures the stress-free reference configuration for the undamaged case is satisfied,\n \n\\begin_inset Formula $c$\n\\end_inset\n\n is the value of the function \n\\begin_inset Formula $\\bar{P}$\n\\end_inset\n\n in the natural state and is given by\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $c$\n\\end_inset\n\n=\n\\begin_inset Formula $\\bar{P_{i}}(C=I)$\n\\end_inset\n\n for \n\\begin_inset Formula $i=1,...,7).$\n\\end_inset\n\n \n\\end_layout\n\n\\begin_layout Subsection\nDamage Fiber Power\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Damage-Fiber-Power\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for Damage Fiber Power is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ndamage fiber power\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<a1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha_{1}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{1}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<a2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\alpha_{2}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\alpha_{2}>1)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kappa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n (\n\\begin_inset Formula $0\\leqslant\\kappa\\leqslant2/3)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBy setting,\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\Psi^{0}=\\kappa\\mathit{I_{1}+\\left(1-\\frac{3}{2}\\kappa\\right)\\mathit{K_{3}}}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nand \n\\begin_inset Formula $\\mathit{m(P)=\\alpha_{1}(P)^{\\alpha_{2}},}$\n\\end_inset\n\nthe strain-energy form above can be made suitable for modeling damage,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\varPsi(C,D)=\\alpha_{1}\\left(\\left(1-D\\right)\\left[\\kappa I_{1}+\\left(1-\\frac{3}{2}\\kappa\\right)K_{3}\\right]-c\\right)^{\\alpha_{2}}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress takes on the following form,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\sigma=\\frac{2}{J}(1-D)\\frac{dm}{dP}\\left[\\kappa b+\\left(1-\\frac{3}{2}\\kappa\\right)\\left[bI_{4}+I_{1}I_{4}m-I_{4}a\\bigodot ba\\right]\\right]$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<solid type=\"damage fiber power\"> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <a1>1400</a1> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <a2>2.2</a2> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <kappa>1e-08</kappa> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <t0>0.98</t0> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>0.96</Dmax> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta_s>0.06</beta_s> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <gamma_max>17.98</gamma_max> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"angles\"> \t\t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n    <theta>-39.87</theta> \t\t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n    <phi>90</phi> \t\t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fiber> \t\t\t\n\\end_layout\n\n\\begin_layout LyX-Code\n</solid>\n\\end_layout\n\n\\begin_layout Subsection\nDamage Fiber Exponential\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Damage-Fiber-Exponential\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material type for Damage Fiber Exponential is \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\shape italic\ndamage fiber exponential\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\shape default\n.\n The following material parameters must be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k1>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\kappa_{1}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\kappa_{1}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<k2>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\kappa_{2}$\n\\end_inset\n\n (\n\\begin_inset Formula $\\kappa_{2}>0)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n<kappa>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter \n\\begin_inset Formula $\\kappa$\n\\end_inset\n\n (\n\\begin_inset Formula $0\\leqslant\\kappa\\leqslant1/3)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe effective strain-energy function is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\Psi^{0}=\\kappa I_{1}+(1-3\\kappa)I_{4}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nand\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\mathit{m(P)=\\frac{k_{1}}{2k_{2}}\\left\\{ \\exp\\left(k_{2}\\left\\langle P\\right\\rangle ^{2}\\right)-1\\right\\} ,}$\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $a$\n\\end_inset\n\n denotes the direction of the fibers.\n\\end_layout\n\n\\begin_layout Standard\nSo that,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\Psi=\\frac{k_{1}}{2k_{2}}\\left\\{ \\exp\\left(k_{2}\\left\\langle \\left(1-D_{a}\\right)\\left(\\kappa I_{1}-\\left(1-3\\kappa\\right)I_{4}\\right)-1\\right\\rangle ^{2}\\right)-1\\right\\} $\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe Cauchy stress then takes on the following form,\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Formula $\\sigma=\\frac{2}{J}(1-D)\\frac{dm}{dP}\\left[\\kappa b+(1-3\\kappa)I_{4}m\\right]$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<solid type=\"damage fiber exponential\"> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <k1>1288.97</k1> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <k2>400</k2> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <kappa>0.2</kappa> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <t0>0.9</t0> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <Dmax>0.99</Dmax> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <beta_s>0.001</beta_s> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <gamma_max>6.67</gamma_max> \n\\end_layout\n\n\\begin_layout LyX-Code\n  <fiber type=\"angles\"> \n\\end_layout\n\n\\begin_layout LyX-Code\n    <theta>-54.94</theta> \n\\end_layout\n\n\\begin_layout LyX-Code\n    <phi>90</phi> \n\\end_layout\n\n\\begin_layout LyX-Code\n  </fiber> \n\\end_layout\n\n\\begin_layout LyX-Code\n</solid>\n\\end_layout\n\n\\begin_layout Subsection\nDamage Fiber exp-linear\n\\end_layout\n\n\\begin_layout Standard\nThis continuous damage formulation uses a slightly modified damage formulation.\n The total damage is here defined as,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nD=D_{1}+D_{2}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $D_{1}$\n\\end_inset\n\ndefines the same damage term as defined above.\n The \n\\begin_inset Formula $D_{2}$\n\\end_inset\n\nis defined as follows,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nD_{2}=D_{3}\\left[a\\left(\\mathrm{exp}\\left(b\\beta\\right)-1\\right)+c\\left(\\mathrm{exp}\\left(d\\beta\\right)-1\\right)\\right]\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe material parameters \n\\emph on\na\n\\emph default\n and \n\\emph on\nc\n\\emph default\n control the magnitude of the continuous damage of the two phenomena:\n 1) slow,\n constant increased in damage,\n 2) sharp jump in damage near failure.\n The other material parameters,\n \n\\emph on\nb\n\\emph default\n and \n\\emph on\nd\n\\emph default\n,\n control the rate of continuous damage accumulation for the two phenomena mentioned above,\n and is a function of the collagen discontinuous damage.\n Furthermore,\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nD_{3}\\left(\\gamma\\right)=\\frac{D_{3\\infty}}{1+exp\\left(-\\left(\\gamma-\\gamma_{0}\\right)/r_{\\gamma}\\right)}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\begin_inset Formula $D_{3\\infty}$\n\\end_inset\n\n is the maximum achievable discontinuous damage possible for the model,\n \n\\begin_inset Formula $\\gamma_{0}$\n\\end_inset\n\n determines the shape of the curve,\n and \n\\begin_inset Formula $r_{\\gamma}$\n\\end_inset\n\n controls the rate of discontinuous damage accumulation.\n\\end_layout\n\n\\begin_layout Standard\nThe additional damage parameters to define the \n\\begin_inset Formula $D_{2}$\n\\end_inset\n\n term,\n are as follows.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD2_a\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $a$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD2_b\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $b$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD2_c\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $c$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD2_d\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $d$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD3_inf\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $D_{3\\infty}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD3_g0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $\\gamma_{0}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD3_rg\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $r_{\\gamma}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe effective strain-energy function is given by,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\Psi^{0}=\\sqrt{I_{4}}\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nand\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nm\\left(P\\right)=\\left\\{ \\begin{array}{cc}\nc_{3}\\left(\\exp\\left(-c_{4}\\right)\\left(Ei\\left(c_{4}\\left(P+1\\right)\\right)-Ei\\left(c_{4}\\right)\\right)-\\log\\left(P+1\\right)\\right), & P\\leqq\\lambda^{*}+1\\\\\nc_{5}P+c_{6}\\log\\left(P+1\\right)+d & P>\\lambda^{*}+1\n\\end{array}\\right.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n \n\\emph on\nd\n\\emph default\n is determined by requiring continuity at \n\\begin_inset Formula $\\lambda^{*}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nThe (elastic) material parameters for this material are as follows.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $c_{3}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $c_{4}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $c_{5}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $c_{6}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlambda\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter \n\\begin_inset Formula $\\lambda^{*},$\n\\end_inset\n\nthe transition between exponential and linear regions.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nFirst-Order Homogenization\n\\end_layout\n\n\\begin_layout Standard\nThis document describes how to use the first-order homogenization feature in FEBio.\n Homogenization refers to the process of evaluating the physical response of a macro model by explicitly solving a separate finite element model that represents the micro structure of the material.\n This micro-model is referred to as the Representative Volume Element (RVE).\n In the first-order approach it is assumed that the macro and micro scales are separated,\n which implies that the size of the RVE is negligible compared to the size of the macro model.\n\\end_layout\n\n\\begin_layout Subsection\nMacro model definition\n\\end_layout\n\n\\begin_layout Standard\nFor the most part,\n the definition of the macro-model is a standard finite element model with properly chosen boundary conditions.\n The use of first-order homogenization is controlled entirely by the material definition.\n \n\\end_layout\n\n\\begin_layout Standard\nTo use the first-order homogenization formulation use the “micro-material” material.\n This material has the following parameters.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRVE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe file name of the RVE model (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nN/A\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrve_type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe type of RVE model (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbc_set\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe name of the nodeset (in the RVE) that defines the outside surface (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(optional)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprobe\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefines a point in the macro model where the RVE will be tracked.\n (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Standard\n1.\n The RVE parameter defines the file name of the RVE model.\n The RVE is a standard FE model,\n although somewhat slimmed down.\n See the rve_type parameter definition and the section RVE model definition below.\n\\end_layout\n\n\\begin_layout Standard\n2.\n This parameter determines the type of RVE (and its assumed boundary conditions) that will be used and must be one of the following values.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nrve_type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrescribed displacement RVE:\n The position of all RVE boundary nodes is fully prescribed\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPeriodic RVE:\n RVE with periodic boundary constraints.\n Constraints are enforced with linear constraints.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n3.\n The bc_set defines the name of the nodeset (defined in the RVE model),\n that defines the outside surface.\n In general,\n this parameter does not need to be defined.\n It is only needed for problems using rve_type = 0,\n and for which the RVE geometry is not a closed cube.\n \n\\end_layout\n\n\\begin_layout Standard\n4.\n By default,\n FEBio will only output the deformation of the macro model to the plot file.\n It will not output any results from the RVE models.\n (Given there could be thousands of RVE models,\n this would be too cumbersome to do.) However,\n users can request FEBio to output the deformation of the RVE model at specific points in the model using the probe option.\n The probe tag requires the following child tags.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nprobe parameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement_id\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe ID of the element to probe\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngausspt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe index of the integration point\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfile\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noutput file name for RVE model\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nRVE model definition\n\\end_layout\n\n\\begin_layout Standard\nThe RVE model is a standard FE model,\n with a few caveats.\n\\end_layout\n\n\\begin_layout Itemize\nThe time stepping parameters will be ignored,\n since the macro model controls the time stepping of the RVE to make sure that both models advance in sync.\n \n\\end_layout\n\n\\begin_layout Itemize\nThe boundary conditions of the RVE depend on the rve_type parameter:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Itemize\nrve_type = 0:\n The RVE model should not have any boundary conditions applied.\n The RVE may define a nodeset that defines the outside surface of the RVE.\n This is generally not needed,\n but must be defined when the RVE geometry is not a closed cube.\n \n\\end_layout\n\n\\begin_layout Itemize\nrve_type = 1:\n The RVE should have periodic constraints applied for all opposing surfaces.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Itemize\nThe RVE model does not need an Output section,\n but may define the plotfile section.\n This will be used to define the conents of the probe's plotfiles.\n \n\\end_layout\n\n\\begin_layout Itemize\nThe size of the RVE is assumed to be much smaller than the macro model.\n It is allowed to use a different length unit for the coordinates of the RVE nodes.\n However,\n the unit of stress must be the same for both the macro model and the RVE model.\n \n\\end_layout\n\n\\begin_layout Chapter\nRestart Input file\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Restart-Input-file\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nIntroduction\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Restart-Introduction\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUsers can request FEBio to output a binary dump file.\n This dump file can be used to restart the analysis from the time point saved in the dump file.\n The user can restart the analysis either directly from this binary dump file or via a restart input file,\n which is described in this chapter.\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Restarting-a-Run\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on how to use the restart feature.\n \n\\end_layout\n\n\\begin_layout Standard\nThis chapter describes the format of the restart input file.\n This file is used to redefine some parameters when restarting a previously terminated run and can also be used to extend the analysis.\n The structure is very similar to the FEBio input file and also uses XML formatting.\n Since the file uses XML,\n the first line must be the XML header:\n\\end_layout\n\n\\begin_layout LyX-Code\n<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\\end_layout\n\n\\begin_layout Standard\nThe next line contains the root element of the restart file,\n and has to be:\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_restart version=\"2.0\">\n\\end_layout\n\n\\begin_layout Standard\nThe restart file is composed of the following sections.\n These sections are sub-elements of the \n\\shape italic\nfebio_restart \n\\shape default\nroot element.\n\\end_layout\n\n\\begin_layout Labeling\n\\labelwidthstring 00.00.0000\n\n\\series bold\n\\shape italic\n\\emph on\nArchive\n\\series default\n\\shape default\n\\emph default\n define the binary dump file used for restarting.\n \n\\end_layout\n\n\\begin_layout Labeling\n\\labelwidthstring 00.00.0000\n\n\\series bold\n\\shape italic\n\\emph on\nControl\n\\series default\n\\shape default\n\\emph default\n redefine some control parameters.\n \n\\end_layout\n\n\\begin_layout Labeling\n\\labelwidthstring 00.00.0000\n\n\\series bold\n\\shape italic\n\\emph on\nLoadData\n\\series default\n\\shape default\n\\emph default\n redefine or add some loadcurves.\n \n\\end_layout\n\n\\begin_layout Labeling\n\\labelwidthstring 00.00.0000\n\n\\series bold\nStep\n\\series default\n add an analysis step.\n\\end_layout\n\n\\begin_layout Standard\nAll sections are optional except for the Archive section.\n In the following paragraphs we describe the different sections in more detail.\n\\end_layout\n\n\\begin_layout Section\nThe Archive Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:The-Archive-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Archive section must be the first sub-element of the \n\\shape italic\nfebio_restart \n\\shape default\nroot element.\n This section defines the name of the binary dump file:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Archive>archive.dmp</Archive>\n\\end_layout\n\n\\begin_layout Standard\nThis file will be created by FEBio when requested by the user and contains the solution of the analysis up to the last converged time step.\n \n\\end_layout\n\n\\begin_layout Section\nThe Control Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:The-Control-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following control parameters can be redefined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance for displacements \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\netol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance for energy \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrtol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconvergence tolerance for residual \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlstol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nline search tolerance \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_refs\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of stiffness reformations \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_ups\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum number of BFGS updates \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrestart\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrestart file generation flag \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nplot_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefines the frequency of the plot file generation \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nThe LoadData Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:The-LoadData-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn the LoadData section some or all of the load curves can be redefined,\n or new load curves can be added.\n The syntax is identical to the LoadData section of the FEBio input file:\n\\end_layout\n\n\\begin_layout LyX-Code\n<LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <loadcurve id=\"1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>0,\n 0</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadpoint>1,\n 0.54</loadpoint>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n</LoadData>\n\\end_layout\n\n\\begin_layout Standard\nIn this case,\n the loadcurve \n\\shape italic\nid \n\\shape default\nis the loadcurve number of the loadcurve that the user wishes to redefine.\n \n\\end_layout\n\n\\begin_layout Standard\nNew loadcurves can be added as well.\n This is useful when in addition adding new Step sections,\n where new boundary conditions are defined.\n When adding new loadcurves,\n make sure to continue the numbering of the parent input file.\n That is,\n if the last loadcurve of the parent input file has and id of n,\n then the first new load curve defined here must have id of n+1.\n\\end_layout\n\n\\begin_layout Section\nThe Step Section \n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:The-Step-Section-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Step section can be used to add additional analysis steps to the model.\n These steps are executed after the initial model has completed.\n This section cannot be used to modify the steps defined in the parent file.\n See chapter \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Multi-step-Analysis\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on the Step section or multi-step analyses in general.\n \n\\end_layout\n\n\\begin_layout Standard\nUsing the Step section,\n additional boundary conditions,\n loads,\n contact definitions,\n etc.\n can be added,\n just like in a regular model input file.\n The syntax is the same as the Step section in the regular model input file.\n The only difference is that the type attribute is required to define the type of analysis.\n The type has the same role as the Module section in the regular FEBio input file.\n\\end_layout\n\n\\begin_layout Section\nExample\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Example\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nExample 1\n\\end_layout\n\n\\begin_layout Standard\nThe following example defines a restart input file.\n No parameters are redefined.\n Only the mandatory \n\\shape italic\nArchive \n\\shape default\nelement is defined.\n In this case the analysis will simply continue where it left off:\n\\end_layout\n\n\\begin_layout LyX-Code\n<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_restart version=\"1.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Archive>out.dmp</Archive>\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_restart>\n\\end_layout\n\n\\begin_layout Subsection\nExample 2\n\\end_layout\n\n\\begin_layout Standard\nIn the following example an additional step is defined,\n which defines a new boundary condition and a new loadcurve.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_restart version=\"2.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Archive>out.dmp</Archive>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Step type=\"solid\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <time_steps>10</time_steps>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <step_size>0.1</step_size>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <prescribe bc=\"x\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n         <scale lc=\"2\">1.0</scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n      </prescribe>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <loadcurve id=\"2\" type=\"linear\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>1,\n 0</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>2,\n 1</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </loadcurve>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_restart>\n\\end_layout\n\n\\begin_layout Standard\nNotice the \n\\begin_inset Quotes eld\n\\end_inset\n\ntype\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute in the Step section.\n This serves a similar purpose as the Module section in the FEBio input file and defines the solver that FEBio will use to solve this step.\n This attribute is required when defining steps in the restart file.\n (The Module section is not supported in the restart input file.)\n\\end_layout\n\n\\begin_layout Standard\nThe prescribed boundary condition references a node set named \n\\begin_inset Quotes eld\n\\end_inset\n\nset1\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n This node set must be defined in the parent input file.\n No new node sets (or surfaces,\n etc.) can be defined in the restart input file.\n \n\\end_layout\n\n\\begin_layout Chapter\nMulti-Step Analysis\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Multi-step-Analysis\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA multi-step analysis is defined using multiple steps,\n where in each step the user can redefine control parameters,\n boundary conditions,\n loads,\n contact interfaces,\n and other model components.\n This is useful,\n for instance,\n for defining time-dependent boundary conditions or for switching between different analysis types during the simulation.\n\\end_layout\n\n\\begin_layout Standard\nMulti-step models require a slightly different file organization compared to single-step models.\n More specifically,\n the Control section is not specified at the top of the input file.\n Instead,\n a \n\\series bold\n\\shape italic\nSteps\n\\series default\n \n\\shape default\nsection is added at the bottom of the file,\n which contains a list of \n\\series bold\nstep \n\\series default\nsub-sections for each step.\n Each step then defines its own \n\\emph on\nControl \n\\emph default\nsection,\n boundary conditions,\n loads,\n etc.\n \n\\end_layout\n\n\\begin_layout Section\nThe Step Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:The-Step-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe multi-step analysis feature organizes the input file in a slightly different way:\n the \n\\series bold\n\\emph on\nControl\n\\series default\n \n\\emph default\nsection is no longer defined at the top of the file,\n and instead,\n a new \n\\series bold\n\\emph on\nSteps\n\\series default\n\\emph default\n section is added to the bottom of the file,\n which will contain a list of analysis steps.\n Each analysis step requires its own \n\\series bold\n\\shape italic\nstep\n\\series default\n \n\\shape default\nsub-section.\n In this step section,\n the user can redefine the control section,\n boundary section,\n loads,\n contact,\n and other model components.\n The following format is suggested when defining a multi-step analysis:\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_spec version=\"4.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Module type=\"solid\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <!-- materials go here -->\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Material>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Mesh>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <!-- mesh definition goes here -->\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Mesh>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <MeshDomains>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <!-- mesh domains goes here -->\n\\end_layout\n\n\\begin_layout LyX-Code\n  </MeshDomains>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <!-- global boundary conditions -->\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <!-- load curve data goes here -->\n\\end_layout\n\n\\begin_layout LyX-Code\n  </LoadData>\n\\end_layout\n\n\\begin_layout LyX-Code\n  \n\\series bold\n<Steps>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n    <step>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n      <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n        <!-- local control settings -->\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n      </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n      <Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n        <!-- local boundary conditions -->\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n      </Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n    </step>\n\\end_layout\n\n\\begin_layout LyX-Code\n\n\\series bold\n  </Steps>\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_spec>\n\\end_layout\n\n\\begin_layout Standard\nThe first part of the file looks similar to a normal input file,\n except that the control section is not specified.\n Also,\n the Boundary,\n Loads,\n Constraints,\n Contact,\n sections should only contain global boundary conditions,\n i.e.\n boundary conditions that will remain active in all analysis steps.\n\\end_layout\n\n\\begin_layout Standard\nAt the end of the file the user defines as many \n\\series bold\nstep\n\\series default\n sections as needed.\n In each \n\\series bold\nstep\n\\series default\n section,\n the user can now define the control parameters and model components.\n\\end_layout\n\n\\begin_layout Standard\nThe sub-sections that can be defined in a step are:\n \n\\end_layout\n\n\\begin_layout Description\nControl Defines the steps control parameter,\n such time stepping,\n solver settings,\n etc.\n\\end_layout\n\n\\begin_layout Description\nInitial Applies initial conditions that take effect at the start of the step.\n \n\\end_layout\n\n\\begin_layout Description\nBoundary Defines boundary conditions that are only applied in this step.\n \n\\end_layout\n\n\\begin_layout Description\nLoads Defines loads that are only applied in this step.\n \n\\end_layout\n\n\\begin_layout Description\nConstraints Defines constraints that are only applied in this step.\n \n\\end_layout\n\n\\begin_layout Description\nRigid Defines rigid constraints,\n loads,\n and connectors in this step.\n\\end_layout\n\n\\begin_layout Description\nMeshAdaptor Defines mesh adaptors that will be applied in this step.\n \n\\end_layout\n\n\\begin_layout Subsection\nBoundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Boundary-Conditions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn a multi-step analysis boundary conditions can be applied that are only active during the step.\n This applies to the \n\\shape italic\nBoundary\n\\shape default\n,\n \n\\shape italic\nLoads\n\\shape default\n,\n \n\\shape italic\nContact \n\\shape default\nand \n\\shape italic\nConstraints \n\\shape default\nsection of the FEBio input file.\n These sections can thus be placed inside the \n\\shape italic\nStep \n\\shape default\nsection to define a boundary condition that is only active during the step.\n If one of these sections is defined before the first \n\\shape italic\nStep\n\\shape default\n section,\n the corresponding boundary conditions remain enforced during all the steps.\n\\end_layout\n\n\\begin_layout Subsection\nRelative Boundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Relative-Boundary-Conditions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nSome boundary conditions can be defined as relative boundary conditions.\n This means that the corresponding conditions will be applied to the final configuration of the previous step and not to the original reference configuration.\n For example,\n if a prescribed displacement is defined as relative the displacement will be taken with respect to the positions of the final configuration of the previous step.\n This makes it possible,\n for example,\n to switch between load and displacement controlled boundary conditions in multi-step analyses.\n\\end_layout\n\n\\begin_layout Section\nAn Example\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:An-Example\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following example illustrates the use of the multi-step feature of FEBio.\n This problem defines two steps.\n In the first step,\n a single element is stretched using a prescribed displacement boundary condition.\n In the second step,\n the boundary condition is removed and the analysis type is switched from quasi-static to a dynamic analysis.\n Note the presence of the global fixed boundary constraints,\n which will remain enforced during both steps:\n\\end_layout\n\n\\begin_layout Verbatim\n\n<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\\end_layout\n\n\\begin_layout Verbatim\n\n<febio_spec version=\"4.0\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<Module type=\"solid\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<Material>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<material id=\"1\" name=\"Material 1\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<density>1.0</density>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<E>1</E>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<v>0.45</v>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</material>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t</Material>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<Mesh>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<Nodes>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"1\">-2.0,-0.5,\n 0.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"2\">-2.0,-0.5,\n 1.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"3\">-2.0,\n 0.5,\n 0.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"4\">-2.0,\n 0.5,\n 1.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"5\"> 2.0,-0.5,\n 0.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"6\"> 2.0,-0.5,\n 1.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"7\"> 2.0,\n 0.5,\n 0.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"8\"> 2.0,\n 0.5,\n 1.0</node>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</Nodes>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<Elements type=\"hex8\" name=\"Part1\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<elem id=\"1\">1,5,7,3,2,6,8,4</elem>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</Elements>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<NodeSet name=\"set1\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"1\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"2\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"3\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"4\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</NodeSet>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<NodeSet name=\"set2\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"1\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"2\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"5\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"6\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</NodeSet>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<NodeSet name=\"set3\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"1\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"3\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"5\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"7\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</NodeSet>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<NodeSet name=\"set4\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"5\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"6\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"7\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<node id=\"8\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</NodeSet>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t</Mesh>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<MeshDomains>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<SolidDomain name=\"Part1\" mat=\"Material 1\"/>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t</MeshDomains>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<Boundary>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<bc type=\"zero displacement\" node_set=\"set1\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<dofs>x,y,z</dofs>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</bc>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t</Boundary>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<LoadData>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<load_controller id=\"1\" type=\"loadcurve\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<points>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<point>0,0</point>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<point>1,0.1</point>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t</points>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</load_controller>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t</LoadData>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t<Step>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<step id=\"1\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<Control>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<time_steps>10</time_steps>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<step_size>0.1</step_size>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t</Control>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<Boundary>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<bc type=\"prescribed displacement\" node_set=\"set4\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t\t<dof>x</dof>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t\t<scale lc=\"1\">1.0</scale>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t</bc>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t</Boundary>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</step>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t<step id=\"2\">\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t<Control>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<time_steps>50</time_steps>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<step_size>0.5</step_size>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t\t<analysis>DYNAMIC</analysis>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t\t</Control>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t\t</step>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\t</Step>\n\\end_layout\n\n\\begin_layout Verbatim\n\n</febio_spec>\n\\end_layout\n\n\\begin_layout Verbatim\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage newpage\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\nParameter Optimization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Parameter-Optimization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis chapter describes FEBio's parameter optimization module.\n This module tries to estimate model parameters by solving an inverse finite element problem,\n where the \n\\begin_inset Quotes eld\n\\end_inset\n\nsolution\n\\begin_inset Quotes erd\n\\end_inset\n\n of the problem is known,\n and the problem's model parameters are sought.\n For instance,\n the solution can be an experimentally determined reaction force curve,\n and the unknown parameters are the material parameters that will recreate the reaction force curve by solving a forward FE problem.\n Another common application is determining the model parameters that achieve a desired load,\n displacement,\n or other goal.\n \n\\end_layout\n\n\\begin_layout Standard\nIn any case,\n the optimization module tries to minimize an objective function of the form,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\varphi\\left(\\mathbf{a}\\right)=\\sum\\limits_{i=1}^{n}\\left[y_{i}-f\\left(x_{i};\\mathbf{a}\\right)\\right]^{2}.\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n the \n\\begin_inset Formula $\\left(x_{i},y_{i}\\right)$\n\\end_inset\n\n are user-defined data pairs and \n\\begin_inset Formula $f\\left(x;\\mathbf{a}\\right)$\n\\end_inset\n\nis the function that extracts the corresponding data from the model.\n The optimization module tries to find the model parameters \n\\series bold\na\n\\series default\n that minimize the function \n\\begin_inset Formula $\\varphi$\n\\end_inset\n\n.\n It does this by repeatedly evaluating the function \n\\begin_inset Formula $f$\n\\end_inset\n\n,\n which will usually call FEBio to solve a forward FE problem.\n \n\\end_layout\n\n\\begin_layout Standard\nThe optimization module requires a separate input file that describes all the information needed for solving an optimization problem.\n This input file is described next.\n\\end_layout\n\n\\begin_layout Section\nOptimization Input File\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Optimization-Input-File\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nLike all FEBio input files,\n the parameter optimization input file is an XML-formatted text file.\n The following sections are defined:\n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nTask\n\\shape default\n\\emph default\n optional section defining the task that is executed to solve the FE problem.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nOptions\n\\shape default\n\\emph default\n optional section defining the optimization control parameters.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nObjective\n\\shape default\n\\emph default\n defines the objective function that will be minimized.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nParameters\n\\shape default\n\\emph default\n defines the model parameters that are to be determined.\n \n\\end_layout\n\n\\begin_layout Description\n\n\\shape italic\n\\emph on\nConstraints\n\\shape default\n\\emph default\n defines linear constraints for the model parameters (requires a solution method that supports linear constraints,\n e.g.\n constrained levmar).\n \n\\end_layout\n\n\\begin_layout Standard\nIn the following paragraphs each section will be explained in detail.\n\\end_layout\n\n\\begin_layout Subsection\nTask Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Model-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe optional Task section can be used to define the task that is executed by FEBio when solving the FE model.\n By default,\n FEBio will solve the standard forward problem,\n but if needed,\n a custom task can be defined.\n \n\\end_layout\n\n\\begin_layout Subsection\nOptions Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Options-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section defines the control parameters for the optimization.\n The options that can be defined will depend on the chosen optimization method,\n which is set with an attribute of the Options section.\n The following table shows the currently supported optimization algorithms.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlevmar\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLevenberg-Marquardt \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconstrained levmar\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLevenberg-Marquardt with linear constraints (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npowell\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPowell's method\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nscan\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nParameter domain scanning (2)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\nconstrained levmar\n\\begin_inset Quotes erd\n\\end_inset\n\n uses the third-party levmar library\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{http://users.ics.forth.gr/~lourakis/levmar/}\n\\end_layout\n\n\\end_inset\n\n.\n For additional information please see the library's home page.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\nscan\n\\begin_inset Quotes erd\n\\end_inset\n\n method will iterate over the parameter range in increments defined by the fourth parameter value defined in the Parameter section.\n It returns the parameters that resulted in the lowest objective value of visited points,\n but it may not be the actual minimum.\n \n\\end_layout\n\n\\begin_layout Standard\nNote that the Options section is optional.\n When omitted,\n the levmar method is used with default values for the control parameters.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following parameters can be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDefault\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nobj_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConvergence tolerance for objective function.\n (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.001 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nf_diff_scale\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nForward difference scale factor (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.001 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlog_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the amount of output that is generated on screen and in the logfile by the FEBio solver (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLOG_NEVER \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntau\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStep size scale factor (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1.0e-3 \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSets the amount of output generated by the optimization module (5)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPRINT_ITERATIONS\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<Options type=\"constrained levmar\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <obj_tol>0.001</obj_tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <f_diff_scale>0.001</f_diff_scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <print_level>PRINT_ITERATIONS</print_level>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Options>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nThe objective function that is to be minimized is a function of the form:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\varphi\\left(\\mathbf{a}\\right)=\\sum\\limits_{i=1}^{n}\\left[y_{i}-f\\left(x_{i};\\mathbf{a}\\right)\\right]^{2}.\n\\]\n\n\\end_inset\n\nHere,\n \n\\begin_inset Formula $f\\left(x;\\mathbf{a}\\right)$\n\\end_inset\n\n is the function that describes the model,\n \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n \n\\series default\nis a vector with the (unknown) model parameters and the \n\\begin_inset Formula $\\left(x_{i},y_{i}\\right)$\n\\end_inset\n\n are the user-defined data that approximates the ideal model.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe optimization method currently implemented requires the calculation of the gradient of the model function \n\\begin_inset Formula $f$\n\\end_inset\n\n with respect to the model parameters \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n\n\\series default\n.\n Since this gradient is not known,\n it will be approximated using forward differences.\n For example,\n the \n\\begin_inset Formula $k$\n\\end_inset\n\n-th component of the gradient is approximated as follows.\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\frac{\\partial f}{\\partial a_{k}}\\approx\\frac{1}{\\delta a_{k}}\\left[f\\left(a_{1},\\cdots,a_{k}+\\delta a_{k},\\cdots,a_{m}\\right)-f\\left(a_{1},\\cdots,a_{k},\\cdots,a_{m}\\right)\\right]\n\\]\n\n\\end_inset\n\nThe value for \n\\begin_inset Formula $\\delta a_{k}$\n\\end_inset\n\nis determined from the following formula.\n \n\\begin_inset Formula \n\\[\n\\delta a_{k}=\\varepsilon\\left(1+a_{k}\\right)\n\\]\n\n\\end_inset\n\nwhere,\n \n\\begin_inset Formula $\\varepsilon$\n\\end_inset\n\nis the forward difference scale factor which can be set by the user with the \n\\shape italic\nfdiff_scale \n\\shape default\noption.\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe \n\\shape italic\nlog_level \n\\shape default\nallows the user to control exactly how much output from the time iterations of each optimization iteration is written to the screen and logfile.\n The following values are allowed:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLOG_DEFAULT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse default settings (may be omitted) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLOG_NEVER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDon't generate any output \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLOG_FILE_ONLY\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSave to the logfile only \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLOG_SCREEN_ONLY\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOnly print to the screen \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLOG_FILE_AND_SCREEN\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSave to the logfile and print to the screen \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Enumerate\nThe step size scale factor \n\\shape italic\ntau\n\\shape default\n can be set as an input only for the Constrained Levenberg-Marquardt method.\n It scales the initial guess for the damping parameter.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nprint_level \n\\emph default\nsets how much output is generated during the optimizations.\n In particular,\n it controls if the objective function values are printed are not.\n The following values are allowed:\n\\end_layout\n\n\\begin_deeper\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPRINT_ITERATIONS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint minimal iterations info\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPRINT_VERBOSE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint a lot of information,\n incl.\n objective function values\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\end_deeper\n\\begin_layout Subsection\nParameters Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Parameters-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section defines the material parameters that are to be determined.\n Each parameter is defined using the \n\\shape italic\nparam \n\\shape default\nelement:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Parameters>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <param name=\"[name]\">[guess],\n [min],\n [max],\n [scale]</param>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Parameters>\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nname \n\\shape default\nattribute gives the name of the parameter that is to be determined.\n The following section describes the format to reference a model parameter.\n Each parameter takes four values:\n [guess] is the initial guess for this parameter,\n [min] and [max] are the minimum and maximum values respectively for this parameter,\n and [scale] is a representative scale (magnitude) for this parameter (although the precise interpretation of this parameter depends on the particular solver).\n This value is used to normalize the optimization parameter and improve convergence.\n If not specified by the user,\n it defaults to the initial guess.\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to optimize the material parameters of a neo-Hookean solid,\n named \n\\shape italic\nmat1,\n \n\\emph on\nuse the following syntax.\n\\end_layout\n\n\\begin_layout LyX-Code\n<Parameters>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <param name=\"fem.material('mat1').E\">1.0,\n 0.0,\n 5.0,\n 1.0</param>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <param name=\"fem.material('mat1').v\">0.1,\n 0.0,\n 0.5,\n 1.0</param>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Parameters>\n\\end_layout\n\n\\begin_layout Standard\nThis example defines two material parameters.\n The first component of the name (here \n\\shape italic\nmat1\n\\shape default\n) is the name of the material as defined in the model input file.\n The second component (here \n\\emph on\nE\n\\emph default\n and \n\\emph on\nv\n\\emph default\n) are the names of the material parameters.\n See Appendix B for more information on how to reference model parameters.\n\\end_layout\n\n\\begin_layout Subsection\nObjective Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Function-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis section defines the functional model that is used to evaluate the objective function (i.e.\n the function \n\\begin_inset Formula $f$\n\\end_inset\n\n above).\n The particular functional model is defined via the \n\\emph on\ntype\n\\emph default\n attribute,\n and the following table lists the available options.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nValue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndata-fit\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFits data,\n usually specified as a function of time,\n to a model output parameter\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntarget\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOptimizes until one or more output model parameters achieve a target value.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement-data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe target data is defined as a tabulated list of element values.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnode-data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe target data is defined as a tabulated list of nodal values.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThese options will be described separately in the following sections.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsubsection\nThe data-fit model\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ndata-fit\n\\emph default\n model tries to fit predicted FE data to a user-defined data curve.\n The data curve usually represents some experimentally obtained result,\n e.g.\n reaction force as a function of time.\n The data-fit model needs a data source,\n i.e.\n a function for extracting the model data,\n and the data curve.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Objective type=\"data-fit\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fnc type=\"[enter type]\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </fnc>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <data>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <pt>0,\n 0</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n    <pt>1,\n 1</pt>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </data>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Objective>\n\\end_layout\n\n\\begin_layout Standard\nThe data source is defined by the type attribute of the fnc tag.\n The following table shows the supported values.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe function will track the evolution of a model parameter.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfilter_positive_only\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe function tracks the evolution of a model parameter,\n but filters only the positive values.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfilter_sum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe function is defined by summing nodal values.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nparameter\n\\end_layout\n\n\\begin_layout Standard\nIn the case of \n\\emph on\nparameter\n\\emph default\n,\n a child element defines the parameter that will act as the data source for the objective function.\n (See Appendix B on more information on referencing model parameters.)\n\\end_layout\n\n\\begin_layout LyX-Code\n<fnc type=\"parameter\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <param name=\"fem.rigidbody('rigid').Fz\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fnc>\n\\end_layout\n\n\\begin_layout Standard\nThis defines the data function as a function of time.\n You can also change the ordinate of the function to create other functional dependencies.\n For example,\n the following creates a load-displacement curve for a rigid body.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<fnc type=\"parameter\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ordinate name=\"fem.rigidbody('rigid').position.x\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <param name=\"fem.rigidbody('rigid').Fz\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fnc>\n\\end_layout\n\n\\begin_layout Standard\nAs another example,\n the following creates a stress-strain curve for a single solid element,\n using the left Hencky (logarithmic) strain component along the z-axis (denoted by \n\\emph on\nhz\n\\emph default\n),\n and the Cauchy normal stress component along z-axis (denoted by \n\\emph on\nsz\n\\emph default\n) in element number 1,\n\\end_layout\n\n\\begin_layout LyX-Code\n<fnc type=\"parameter\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <ordinate name=\"fem.element_data('hz',1)\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <param name=\"fem.element_data('sz',1)\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fnc>\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nfilter_positive_only\n\\end_layout\n\n\\begin_layout Standard\nThis option requires another data source that is needed to extract model data (e.g.\n parameter),\n but only positive values are returned.\n If the model parameter's value is negative,\n zero will be returned by the function.\n\\end_layout\n\n\\begin_layout LyX-Code\n<fnc type=\"filter_positive_only\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <source type=\"parameter\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <param name=\"fem.rigidbody('rigid').Fz\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </source>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fnc>\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nfilter_sum\n\\end_layout\n\n\\begin_layout Standard\nThis option will sum up nodal values and use that in the objective function.\n The summed value is always assumed to be a function of time.\n The following example illustrates how this option can be used to define an objective function from the sum of nodal reaction forces.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<fnc type=\"filter_sum\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <node_data data=\"Rx\" node_set=\"top surface\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</fnc>\n\\end_layout\n\n\\begin_layout Subsubsection\nThe target model\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ntarget \n\\emph default\nmodel tries to optimize model parameters such that one or more output variables achieve a user-defined target value.\n This model requires a list of variables and target values for each variable.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Objective type=\"target\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var name=\"[enter name]\">[target value]</var>\n\\end_layout\n\n\\begin_layout LyX-Code\n  ...\n\\end_layout\n\n\\begin_layout LyX-Code\n</Objective>\n\\end_layout\n\n\\begin_layout Standard\nThe name refers to a model output parameter.\n The example below will optimize until the X-euler angle of the rigid body 'Material2' becomes 0.5.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Objective type=\"target\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var name=\"fem.rigidbody('Material2').euler.y\">0.5</var>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Objective>\n\\end_layout\n\n\\begin_layout Subsubsection\nThe element-data model\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nelement-data \n\\emph default\nmodel uses a tabulated list of element values as the target for the optimization.\n In addition,\n it requires an output variable that will be matched against the element data.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Objective type=\"element-data\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var type=\"[enter variable name]\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <data>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <elem id=\"1\">0.123</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </data>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Objective>\n\\end_layout\n\n\\begin_layout Standard\nThe variable can be any of the element output data listed in the logfile section (See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:ElementSet-Section\"\nnolink \"false\"\n\n\\end_inset\n\n).\n \n\\end_layout\n\n\\begin_layout Subsubsection\nThe node-data model\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nnode-data \n\\emph default\nmodel uses a tabulated list of nodal values as the target for the optimization.\n In addition,\n it requires an output variable that will be matched against the nodal data.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<Objective type=\"node-data\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <var type=\"[enter variable name]\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <data>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <node id=\"1\">0.123</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </data>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Objective>\n\\end_layout\n\n\\begin_layout Standard\nThe variable can be any of the node output data listed in the logfile section (See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Node_Data-Class\"\nnolink \"false\"\n\n\\end_inset\n\n).\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nConstraints Section\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constraints-Section\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe Constrained Levenberg-Marquardt method allows linear constraints on the material parameters.\n If \n\\series bold\n\n\\begin_inset Formula $\\mathbf{a}$\n\\end_inset\n\n\n\\series default\n is the material parameter vector,\n then the linear constraint is of the form:\n \n\\begin_inset Formula \n\\[\nc_{1}a_{1}+c_{2}a_{2}+...+c_{n}a_{n}+b=0\\quad.\n\\]\n\n\\end_inset\n\nThe coefficients \n\\begin_inset Formula $c_{1},c_{2},...,c_{n},b$\n\\end_inset\n\n are the inputs of the constraint tag.\n For example,\n if the linear constraint is \n\\begin_inset Formula $2a_{1}-a_{2}+3=0$\n\\end_inset\n\n ,\n then the Constraints section would be:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Constraints>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <constraint>2,\n -1,\n 3</constraint>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Constraints>\n\\end_layout\n\n\\begin_layout Section\nRunning a Parameter Optimization\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Running-Parameter-Optimization\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs explained above,\n a parameter optimization problem is described using two input files.\n First,\n a standard FEBio input file that defines the mesh,\n materials,\n boundary conditions,\n etc.\n The second input file describes the parameter optimization problem,\n such as the objective and which model parameters are to be optimized.\n The format of the second file is described above.\n A parameter optimization can only be initiated from the command line.\n For example:\n\\end_layout\n\n\\begin_layout LyX-Code\n>febio –i model.feb –s optim.feb\n\\end_layout\n\n\\begin_layout Standard\nThe output of a parameter optimization analysis is a log file that contains the screen output of the FEBio run as well as the optimized parameter values.\n\\end_layout\n\n\\begin_layout Section\nAn Example Input File\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:An-Example-Input\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBelow follows a complete example of an optimization input file.\n\\end_layout\n\n\\begin_layout LyX-Code\n<?xml version=\"1.0\"?>\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_optimize version=\"2.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Options>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <obj_tol>0.001</obj_tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <f_diff_scale>0.001</f_diff_scale>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Options>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Parameters>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <param name=\"fem.material[0].E\">1,\n 0,\n 5</param>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <param name=\"fem.material[0].v\">-0.5,\n 0,\n 0.5</param>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Parameters>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Objective type=\"data-fit\">\n\\end_layout\n\n\\begin_layout LyX-Code\n    <fnc type=\"parameter\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <param name=\"fem.rigidbody('rigid').Fz\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </fnc>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <data>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>0.0,\n 0</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>0.5,\n 1</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n      <point>1.0,\n 2</point>\n\\end_layout\n\n\\begin_layout LyX-Code\n    </data>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Objective>\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_optimize>\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments\n\\shape default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nNotice that the xml root element is \n\\shape italic\nfebio_optimize\n\\shape default\n for the optimization input file.\n The version number has to be 2.0 (the 1.0 version is no longer supported).\n\\end_layout\n\n\\begin_layout Enumerate\nThe FEBio input file that contains the actual FE model data has to defined on the command line using the -i command option.\n This file is a standard FEBio input file that defines all geometry,\n materials,\n boundary conditions and more.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe initial values of the parameters are defined in the optimization input file.\n The values in the model input file are ignored.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\shape italic\nOptions \n\\shape default\nsection is included here,\n but can be omitted.\n If omitted default values will be used for all control parameters.\n\\end_layout\n\n\\begin_layout Chapter\nTroubleshooting\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Troubleshooting\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nRunning a nonlinear finite element analysis can be a very challenging task.\n The large deformations and complex constitutive models can make it very difficult to obtain a converged solution.\n There are many causes for nonconvergence,\n ranging from element inversions,\n material instability,\n failure to enforce contact constraints and many more.\n Sometimes it is possible that FEBio gives you a converged solution,\n but the solution is meaningless or at least not what was expected.\n\\end_layout\n\n\\begin_layout Standard\nFortunately,\n many of these issues can be prevented or solved with little effort.\n This chapter discusses some strategies to prevent common problems and to troubleshoot a problematic run.\n It offers some sanity checks before you run your model which can save you a lot of frustration down the road.\n And when things do go bad,\n we hope that the strategies suggested here may prove helpful.\n\\end_layout\n\n\\begin_layout Standard\nHowever,\n keep in mind,\n that the finite element method cannot solve all problems.\n It is a very powerful numerical method,\n but with limitations.\n Understanding these limitations and how they affect your modeling work is crucial in becoming a good analyst.\n\\end_layout\n\n\\begin_layout Section\nBefore You Run Your Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Before-You-Run\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn this section we'll discuss what a well-defined finite element model is and some things you may need to check before you run your model in FEBio.\n\\end_layout\n\n\\begin_layout Standard\nA well-defined finite element model contains a finite element mesh,\n a valid material and properly defined boundary and contact conditions in order to define a unique solution to the problem under study.\n We will look at each of these requirements in more detail in the following sections.\n\\end_layout\n\n\\begin_layout Subsection\nThe Finite Element Mesh\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Finite-Element-Mesh\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA finite element mesh is required to solve a problem with FEBio.\n The mesh defines a discretization of the problem domain in nodes and connected elements.\n FEBio only supports certain elements and thus the mesh must be composed of elements from this set.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Elements-Section\"\nnolink \"false\"\n\n\\end_inset\n\n for a discussion of the supported elements.\n In addition,\n FEBio assumes a specific ordering of the nodes of an element.\n FEBio cannot discover if the nodes are in the correct order,\n but if they are not,\n FEBio will most likely have trouble converging or throw negative jacobians.\n If FEBio discovers negative jacobians before the first time step,\n it is likely that the nodes of the elements are not defined in the proper order.\n\\end_layout\n\n\\begin_layout Standard\nFor shell elements,\n the initial thickness of an element is also important.\n When elements are too thick (the thickness is of the same order as the element size),\n FEBio may complain about negative jacobians.\n This may be particularly a problem in areas of high curvature.\n The only solution around this issue might be to remesh the problematic area.\n\\end_layout\n\n\\begin_layout Subsection\nMaterials\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Materials\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA material in FEBio defines the constitutive response of the domain to which the material is assigned.\n See Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Materials\"\nnolink \"false\"\n\n\\end_inset\n\n for a detailed list of all the available materials in FEBio.\n It is important to understand that the module defines which materials you can use.\n For example,\n the biphasic material cannot be used in the \n\\shape italic\nsolid \n\\shape default\nmodule.\n Although some cases of invalid material use are caught,\n in many situations the resulting behavior is undefined.\n The following table shows a list of some of FEBio's special materials and the modules in which they can be used.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"6\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nMaterial\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nSolid\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nBiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nSolute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nMultiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nHeat\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\shape italic\nbiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\shape italic\nbiphasic-solute\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\shape italic\ntriphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\shape italic\nmultiphasic\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\shape italic\nisotropic Fourier\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nYES\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn addition to using the proper materials for a given module,\n it is also important to understand that most material parameters have a limited range in which they define valid constitutive behavior.\n For example,\n in a neo-Hookean material the Young's modulus must be positive and the Poisson's ratio must larger than -1 and less than 0.5.\n FEBio has most of these limits implemented in the code and will throw an error when a parameter value is defined that falls outside the valid range.\n\\end_layout\n\n\\begin_layout Standard\nSince material parameters can be defined as functions of time through a loadcurve,\n FEBio will check all parameters at the beginning of each time step.\n When a parameter has become invalid the run will be aborted.\n\\end_layout\n\n\\begin_layout Standard\nSome constitutive models are only valid for a limited amount of deformation.\n All materials in FEBio are designed for large deformation (in the sense that they are objective under arbitrary deformation),\n but that does not imply unlimited deformation.\n When the deformation becomes too large,\n the material response may become nonphysical and although FEBio gives an answer,\n the result may be meaningless (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Understanding-the-Solution\"\nnolink \"false\"\n\n\\end_inset\n\n for a discussion on interpreting the result).\n Some materials also become unstable at extremely large deformations.\n A classical example is the Mooney-Rivlin material.\n For a certain range of values of the material parameters,\n and under certain loading conditions,\n the stress-strain response can have a zero slope at large deformations.\n In that case,\n FEBio will most likely not be able to converge since the slope of the stress-strain response is used to progress towards the solution.\n\\end_layout\n\n\\begin_layout Subsection\nBoundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Trouble-Boundary-Conditions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBoundary conditions are necessary to uniquely define the solution of the problem under study.\n Improperly defined boundary conditions can lead to underconstrained or overconstrained problems.\n\\end_layout\n\n\\begin_layout Standard\nIf the problem is underconstrained the solution is not uniquely defined and FEBio will not be able to find a solution.\n The most common example of an underconstrained problem is one where the rigid body modes are not constrained.\n A rigid body mode is a mode of deformation that does alter the stress in the body.\n Affine translation and rotation of the entire mesh are two ways to introduce a rigid body mode.\n In a (quasi-)static finite element analysis all rigid body modes must be properly constrained in order to find a unique solution.\n This can often very easily be achieved by constraining and edge or face of the model from moving at all.\n\\end_layout\n\n\\begin_layout Standard\nIf the problem is over-constrained,\n FEBio will usually find an answer but it is probably not the solution that was sought.\n The most common cause of an over-constrained model is one that has conflicting boundary conditions.\n For example,\n a force is applied to a node that is fixed.\n\\end_layout\n\n\\begin_layout Section\nDebugging a Model\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Debugging-a-Model\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a model is not running as expected,\n the first thing to try is rerun the problem using FEBio's debug mode.\n Debug mode can be activated by adding a –g on the command line.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n>febio –i file.feb -g\n\\end_layout\n\n\\begin_layout Standard\nWhen in debug mode,\n FEBio will do additional checks during the solution (e.g.\n check for NAN's,\n check for zeroes on the diagonal of the stiffness matrix,\n etc.) and will store all non-converged states to the plot file.\n Inspecting these non-converged states can often be useful to identify the cause of the problem.\n (E.g.\n when the model seems to disappear at some point might indicate a rigid body mode.)\n\\end_layout\n\n\\begin_layout Standard\nSince storing all non-converged states may make the plot-file too large for post-processing,\n debug mode can also be turned on during the analysis.\n To do this,\n start the problem normally (without the debug flag –g).\n Then,\n right before the problem starts to fail,\n interrupt the run (using ctrl+c) and wait for the FEBio prompt to appear.\n Once it appears,\n enter \n\\shape italic\ndebug \n\\shape default\n[ENTER],\n and then \n\\shape italic\ncont \n\\shape default\n[ENTER] to continue the run in debug mode.\n Alternatively,\n a break point can be specified on the command line or at the FEBio prompt,\n which will instruct FEBio to break at a particular point in the analysis.\n At that point,\n the febio prompt will appear,\n and the debug mode can be toggled.\n For more information on setting breakpoints,\n consult section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Setting-break-points\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n \n\\end_layout\n\n\\begin_layout Standard\nAnother option is to use the -g2 flag,\n instead of the -g flag.\n This flag will do all of the debug checks,\n but only output the non-converged state that caused febio to fail to the plot file.\n \n\\end_layout\n\n\\begin_layout Section\nCommon Issues\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Common-Issues\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn this section we take a look at some of the most common problems you may run into when solving a finite element problem with FEBio.\n We'll discuss possible causes of and solutions to these issues.\n\\end_layout\n\n\\begin_layout Subsection\nInverted elements\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Inverted-elements\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen an element inverts,\n the element becomes so distorted that in certain areas of the element the Jacobian becomes zero or negative.\n In order to obtain a physically realistic solution,\n the Jacobian of the deformation must be positive at all points of the domain.\n Thus,\n when a negative Jacobian is found in an element,\n FEBio cannot continue.\n Fortunately,\n FEBio's automatic time-stepping algorithm can usually circumvent this problem by cutting the time step back and retrying the step using a smaller time step.\n In addition,\n FEBio will recalculate the global stiffness matrix.\n The combination of a reduced time step and a refactored stiffness matrix will often be sufficient to overcome the negative Jacobian.\n However,\n when it is not,\n you may have run into a more serious problem.\n These are some of the common causes that may require additional user intervention.\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n when FEBio encounters inverted elements,\n it will print an error message stating that \n\\begin_inset Quotes eld\n\\end_inset\n\nnegative jacobians\n\\begin_inset Quotes erd\n\\end_inset\n\n were detected.\n You can request FEBio to print out a detailed list of the inverted elements,\n by setting the \n\\emph on\noutput_negative_jacobians\n\\emph default\n configuration flag in the FEBio configuration file.\n (see \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Configuration-File\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\begin_layout Subsubsection\nMaterial instability\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Material-instability\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAt large deformations,\n some materials can become unstable.\n This is usually caused by a softening of the material at large deformations.\n When the material softens too much the global stiffness matrix can become ill-conditioned and FEBio will not be able to find the correct solution.\n Unfortunately,\n there is no easy solution around this issue and you may need to consider using a different constitutive model.\n\\end_layout\n\n\\begin_layout Subsubsection\nTime step too large\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Time-step-too-large\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAlthough the auto-time stepper will automatically adjust the time step in case of trouble,\n it is designed to work within user defined limits and sometimes they are not set properly to solve the problem.\n The most common issue is that the minimum time step is set too large.\n Cutting back the minimum in addition to the initial time step can often help in preventing negative Jacobians.\n\\end_layout\n\n\\begin_layout Subsubsection\nElements too distorted\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Elements-too-distorted\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset label\nLatexCommand label\nname \"para:elements\"\n\n\\end_inset\n\n When elements get too distorted they might not be able to deform any further without inverting the element.\n In this case,\n it may be necessary to adjust the mesh in the area where the elements are inverting.\n\\end_layout\n\n\\begin_layout Subsubsection\nShells are too thick\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Shells-too-thick\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA shell is an element where it is assumed that one dimension is much smaller compared to the other two directions.\n When the mesh is really fine,\n this assumption may no longer be valid and it could happen that the shell becomes too thick.\n When a shell gets too thick it can create negative Jacobians in the out-of-plane integration points,\n especially in areas of high curvature.\n To overcome this,\n you may need to remesh the affected area,\n adjust the shell thickness or even replace the shells with solid elements.\n\\end_layout\n\n\\begin_layout Subsubsection\nRigid body modes\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid-body-modes\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA rigid body mode is a mode of deformation that does not alter the stress in the body.\n Uniform translation or rotation of the entire model are two possible rigid body modes.\n In the presence of a rigid body mode,\n the solution is not uniquely defined and FEBio will most likely throw negative Jacobians.\n The solution is to identify the rigid body mode and to eliminate it by applying additional constraints to the model.\n\\end_layout\n\n\\begin_layout Subsection\nFailure to converge\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Failure-to-converge\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio uses an iterative method to solve the nonlinear finite element equations.\n This means that for each time step the solution for that step is obtained by a succession of iterations,\n where each iteration brings the model (hopefully) closer to the solution for that step.\n An iterative method requires a convergence criterion to decide when to stop iterating and FEBio uses no less than three criteria.\n (In biphasic and multiphasic problems additional convergence criteria are used for the pressure and concentration degrees of freedom.) The convergence norm for each of these criteria is defined by the user in the control section of the input file.\n\\end_layout\n\n\\begin_layout Standard\nSometimes it happens that FEBio cannot converge on a time step.\n We'll take a look at some of the most common causes in the next following sections.\n Also take a look at possible causes of element inversions above.\n Sometimes,\n the issues that cause an element inversion may also manifest themselves in convergence problems before the element actually inverts.\n\\end_layout\n\n\\begin_layout Subsubsection\nNo loads applied\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:No-loads-applied\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nPerhaps surprisingly,\n when no loads are applied,\n FEBio will often struggle to find a solution.\n The reason (and solution!) is simple.\n Although no loads are applied,\n FEBio still performs many calculations and due to round-off errors,\n the result of these calculations may render the convergence norms not exactly zero.\n FEBio then tries to apply the convergence criteria to these really small norms and again due to round-off error will not be able to satisfy the convergence criteria.\n\\end_layout\n\n\\begin_layout Standard\nFEBio can actually detect this situation.\n It looks at the residual norm and when this is smaller than a user-defined limit it assumes that no loads are present and moves on to the next time step.\n However,\n this limit is actually problem dependent and it can happen that for your particular problem the limit is set too small.\n In that case,\n the solution is to increase the limit.\n This can be done by setting the \n\\shape italic\nmin_residual \n\\shape default\nparameter in the \n\\shape italic\nControl \n\\shape default\nsection of the input file.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<min_residual>1e-15</min_residual>\n\\end_layout\n\n\\begin_layout Standard\nIf the residual norm now drops below this value,\n FEBio will consider the time step converged and move on to the next time step.\n\\end_layout\n\n\\begin_layout Subsubsection\nConvergence Tolerance Too Tight \n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Convergence-Tolerance-Too-Tight\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn some cases,\n the default convergence tolerances are too tight and FEBio will not be able to converge.\n In that case,\n adjusting the affected convergence norms is a possible solution.\n This is done by editing the corresponding norms in the Control section of the input file.\n However,\n this should only be done when no other cause can be identified for the convergence problems.\n\\end_layout\n\n\\begin_layout Subsubsection\nForcing convergence\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Forcing-convergence\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIt is possible to force a time step to converge.\n This can be done by interrupting the run (using ctrl+c) and then enter the \n\\shape italic\nconv \n\\shape default\ncommand at the FEBio prompt.\n This will force the time step to converge and FEBio will continue with the next time step.\n This is not a recommended practice as the solution may become unstable and in fact it is unlikely that FEBio will be able to converge any of the following time steps.\n However,\n it can be useful in some scenarios.\n For example,\n if there is no load applied in the initial time step and FEBio has trouble getting past this initial step (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:No-loads-applied\"\nnolink \"false\"\n\n\\end_inset\n\n) or when you want to store the current state to the plot file without having to rerun the problem in debug mode (although the \n\\shape italic\nplot \n\\shape default\ncommand or \n\\shape italic\ndebug \n\\shape default\ncommand might be better alternatives).\n\\end_layout\n\n\\begin_layout Subsubsection\nProblems due to Contact\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Problems-due-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen contact interfaces are defined,\n convergence problems can often be traced back to problems with the contact interfaces.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Guidelines-for-Contact\"\nnolink \"false\"\n\n\\end_inset\n\n for more details on how to properly use contact interfaces in FEBio.\n\\end_layout\n\n\\begin_layout Section\nGuidelines for Dynamic Analyses\n\\end_layout\n\n\\begin_layout Standard\nA dynamic analysis differs from a quasi-static analysis in that the mass inertia of the model is taken into account.\n This adds an explicit time-dependency to the model (through the nodal velocities and accelerations) and therefore the solver needs to integrate the equations of motion in time.\n There are several integration schemes available in FEBio.\n \n\\end_layout\n\n\\begin_layout Subsection\nImplicit Dynamics Solver\n\\end_layout\n\n\\begin_layout Standard\nThe implicit solid solver implements the generalized alpha integration rule,\n which can be controlled via several parameters.\n Although the default settings will usually suffice,\n there might be instances where the parameters should be modified.\n (For example,\n see \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:False-Transient-Analysis\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n \n\\end_layout\n\n\\begin_layout Standard\nThe main parameter that controls the time integration of the solid solver is the \n\\family typewriter\nrhoi\n\\family default\n parameter.\n The following rules apply:\n\\end_layout\n\n\\begin_layout Enumerate\n\\begin_inset Formula $\\rho_{\\infty}=-1$\n\\end_inset\n\n,\n All other integration parameters are set to one.\n \n\\end_layout\n\n\\begin_layout Enumerate\n\\begin_inset Formula $0\\leqq\\rho_{\\infty}\\leqq1$\n\\end_inset\n\n,\n Newmark integration parameters are set to defaults,\n based on the value of \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Enumerate\nAny other value for \n\\begin_inset Formula $\\rho_{\\infty}$\n\\end_inset\n\n,\n default/user-specified values for Newmark integration parameters are not modified.\n \n\\end_layout\n\n\\begin_layout Subsection\nFalse Transient Analysis\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:False-Transient-Analysis\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nQuasi-static models may sometimes exhibit instabilities that may prevent the default Newton-based solver from converging.\n In such situations,\n a dynamic analysis can be tried instead,\n with the anticipation that the inertia of the model provides sufficient stability to solve the model successfully.\n Since in these situations,\n only the long-term quasi-static solution is sought,\n it is important to damp the oscillations introduced by the dynamics of the system.\n Such damping can be accomplished several ways.\n \n\\end_layout\n\n\\begin_layout Standard\nThe generalized-alpha time integration scheme of the implicit solver uses several parameters that control the overall energy-conservation of the model.\n By choosing these parameters such that the system looses energy over time,\n artificial damping can be introduced.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\family typewriter\nrhoi\n\\family default\n control parameter controls the amount of artificial damping.\n This parameters takes a value between 0 (more numerical damping) and 1 (least numerical damping).\n Thus,\n by setting this value to 0,\n the most numerical damping can be introduced.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\family typewriter\nrhoi\n\\family default\n control parameter sets the actual alpha,\n beta,\n gamma parameters of the generalized-alpha rule.\n Instead of using rhoi,\n users can set these actual parameters directly.\n To introduce the most numerical damping,\n set the values as follows.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<rhoi>-2</rhoi>             \n\\end_layout\n\n\\begin_layout LyX-Code\n<gamma>1.5</gamma>\n\\end_layout\n\n\\begin_layout LyX-Code\n<beta>1</beta>\n\\end_layout\n\n\\begin_layout Standard\nNote that rhoi needs to be set to -2 so that FEBio uses the actual alpha,\n beta,\n gamma parameters.\n \n\\end_layout\n\n\\begin_layout Section\nGuidelines for Contact Problems\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Guidelines-for-Contact\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio offers many contact interfaces which allow the user to model complex boundary interactions.\n However,\n this capability comes at a price and the use of contact interfaces may create several problems in the solution process.\n It is hoped that the guidelines presented in this section may prevent many of the most common problems with contact.\n\\end_layout\n\n\\begin_layout Standard\nMost of the contact algorithms implemented in FEBio offer two contact enforcement methods:\n a penalty method and an augmented Lagrangian method.\n Since the strategies for these two methods are slightly different,\n we will look at them separately.\n These algorithms are discussed in much more detail in the \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://febio.org/knowledgebase/}{\n\\backslash\nemph{FEBio Theory Manual}}\n\\end_layout\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nThe penalty method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:The-penalty-method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe penalty method enforces contact by \n\\begin_inset Quotes eld\n\\end_inset\n\npenalizing\n\\begin_inset Quotes erd\n\\end_inset\n\n (in an energy sense) any deviation from the contact constraint.\n In other words,\n when the two contacting surfaces penetrate,\n a force is generated proportional to the distance of penetration,\n which has the effect that the surfaces will now repel each other.\n The strength of this repelling force is controlled by the user through the \n\\shape italic\npenalty factor\n\\shape default\n.\n\\end_layout\n\n\\begin_layout Standard\nThe obvious downside of this method is that some penetration is necessary to generate the contact force.\n The larger the penalty factor,\n the smaller this penetration needs to be and thus the better the contact constraint is enforced.\n However,\n choosing the penalty factor too large may cause the problem to become unstable since the contact force (and corresponding stiffness) will dominate the other forces in the model.\n Choosing a good penalty factor can thus be often difficult and may require several attempts before getting it \n\\begin_inset Quotes eld\n\\end_inset\n\nright\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n To help with choosing a penalty factor,\n FEBio offers the \n\\shape italic\nauto_penalty \n\\shape default\noption for many of its contact interfaces which will estimate a good penalty factor based on element size and initial material stiffness.\n However,\n even then it may still take a few tries before getting the penalty factor good enough.\n\\end_layout\n\n\\begin_layout Subsection\nAugmented Lagrangian Method\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Augmented-Lagrangian-Method\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn theory,\n in the presence of constraints,\n the corresponding Lagrange multipliers must be calculated which,\n in the case of contact,\n correspond to the contact forces that enforce the contact constraint exactly.\n Unfortunately,\n the exact evaluation of these Lagrange multipliers is numerically very challenging and therefore in FEBio it was decided to evaluate these Lagrange multipliers using an iterative method,\n named the \n\\shape italic\nAugmented Lagrangian \n\\shape default\nmethod.\n Using this method,\n FEBio will solve a time step several times (these iterations are termed \n\\shape italic\naugmentations\n\\shape default\n),\n where each time the approximate Lagrange multipliers are updated (or \n\\shape italic\naugmented\n\\shape default\n).\n\\end_layout\n\n\\begin_layout Standard\nThe obvious drawback of this method is that now each time step has to be solved several times.\n However,\n the advantage of avoiding the numerical problems of obtaining the exact Lagrange multipliers and the fact that in most contact problems very little extra work is needed to solve these augmentations,\n makes this method very attractive.\n In addition,\n it often gives better enforcement of the contact constraint compared to the penalty method.\n\\end_layout\n\n\\begin_layout Standard\nSo why not just use the augmented Lagrangian method?\n Well,\n often the penalty method will give good results and since the penalty method is much faster,\n it is often the preferred choice of many analysts.\n\\end_layout\n\n\\begin_layout Standard\nIn many cases,\n users are only interested in the final time step.\n For these users it may be of interest that it is possible to use the augmented Lagrange method only in the final time step.\n This can be done by defining a loadcurve for the \n\\shape italic\ntolerance\n\\shape default\n contact parameter,\n which sets the convergence tolerance for the augmentations.\n (Note that you still need to set the enforcement method,\n i.e.\n the \n\\emph on\nlaugon\n\\emph default\n parameter,\n to AUGLAG (=1)).\n Then,\n define the corresponding loadcurve as zero everywhere except for the final time step.\n FEBio will now only use the augmented Lagrangian method in the final time step (and the penalty method all other steps).\n\\end_layout\n\n\\begin_layout Subsection\nInitial Separation\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Initial-Separation\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio often struggles with problems that have an initial separation.\n This is especially so when the problem is force-driven,\n meaning that the deformation of the model is driven by a force (opposed to displacement-driven problems).\n In general,\n when the contact interface is necessary to define a well-constrained problem it is best to avoid initial separation if possible.\n Another way around this issue is to first use a displacement to bring the surfaces in contact and then replace the displacement with a force.\n\\end_layout\n\n\\begin_layout Section\nCautionary Note for Steady-State Biphasic and Multiphasic Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Guidelines-Steady-State\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nBiphasic,\n biphasic-solute and multiphasic analyses are generally transient analyses,\n involving mass transport (solvent and solutes) through a porous permeable domain.\n The default setting for these analyses is the transient mode (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Module-Section\"\nnolink \"false\"\n\n\\end_inset\n\n).\n When a steady-state analysis is requested (\n\\emph on\n<analysis type=\"steady-state\"/>\n\\emph default\n),\n the code simplifies the governing equations by setting time derivatives of the solid displacement and solute concentrations to zero.\n It is important to understand that this simplification is only applicable to analyses where interface conditions between sub-domains,\n and boundary conditions with the external environment,\n allow mass exchanges for the solvent and all solutes.\n If a domain or sub-domain is completely sealed such that it prevents solvent or solute exchanges,\n a steady-state analysis should not be used,\n as it would not capture these sealed conditions and would lead to erroneous results.\n For those types of problems,\n always use a transient analysis to obtain the correct solution.\n In those cases,\n a steady-state response may be obtained efficiently by temporarily increasing the transport properties (hydraulic permeability and solute diffusivities) by orders of magnitude via load curves,\n and/or by increasing the size of time steps.\n\\end_layout\n\n\\begin_layout Section\nGuidelines for Multiphasic Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Guidelines-for-Multiphasic\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nInitial State of Swelling\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Initial-State-Swelling-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nUnder traction-free conditions,\n a multiphasic material is usually in a state of swelling due to the osmotic pressure difference between the interstitial fluid and the external environment.\n This osmotic pressure arises from the difference in interstitial versus external concentrations of cations and anions,\n caused by the charged solid matrix and the requirement to satisfy electroneutrality.\n An osmotic pressure difference arising from such electrostatic interactions is known as the \n\\shape italic\nDonnan osmotic pressure\n\\shape default\n.\n When the Donnan pressure is non-zero,\n traction-free conditions do not produce stress-free conditions for the solid matrix,\n since the matrix must expand until its stressed state resists the swelling pressure.\n\\end_layout\n\n\\begin_layout Standard\nThe Donnan pressure reduces to zero when the fixed charged density is zero,\n or when the external environment is infinitely hypertonic (having ion concentrations infinitely greater than the interstitial fixed charge density).\n Since these two conditions represent special cases,\n it is generally necessary to devise methods for achieving the desired initial state of swelling,\n in an analysis where loads or displacements need to be prescribed over and above this swollen state.\n Swelling occurs as a result of the influx of solvent into the porous solid matrix.\n This influx is a time-dependent process that could require extensive analysis time.\n Therefore,\n it is computationally efficacious to achieve the initial state of swelling by using a multi-step analysis (Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Multi-step-Analysis\"\nnolink \"false\"\n\n\\end_inset\n\n) where the first step is a steady-state analysis (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Control-Parameters\"\nnolink \"false\"\n\n\\end_inset\n\n).\n In this steady-state step,\n the fixed charge density may be ramped up from zero to the desired value using a load curve for that property or,\n alternatively,\n the external environmental conditions may be reduced from a very high hypertonic state down to the desired level.\n In the second step,\n prescribed displacement boundary conditions (when needed) may be specified to be of type \n\\shape italic\nrelative\n\\shape default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Zero-Displacement\"\nnolink \"false\"\n\n\\end_inset\n\n),\n so that they become superposed over and above the initial swelling state.\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<Module type=\"multiphasic\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n<!-- rest of file -->\n\\end_layout\n\n\\begin_layout LyX-Code\n<Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <analysis type=\"steady-state\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n<Step>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n    ...\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Control>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n    <prescribe type=\"relative\">\n\\end_layout\n\n\\begin_layout LyX-Code\n      <node id=\"22\" bc=\"z\" lc=\"4\">1</node>\n\\end_layout\n\n\\begin_layout LyX-Code\n      ...\n\\end_layout\n\n\\begin_layout LyX-Code\n    </prescribe>\n\\end_layout\n\n\\begin_layout LyX-Code\n  </Boundary>\n\\end_layout\n\n\\begin_layout LyX-Code\n</Step>\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Boundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Boundary-Conditions-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn most analyses,\n it may be assumed that the ambient fluid pressure and electric potential in the external environment are zero,\n thus \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n and \n\\begin_inset Formula $\\psi_{\\ast}=0$\n\\end_inset\n\n,\n where the subscripted asterisk is used to denote environmental conditions.\n Since the external environment does not include a solid matrix,\n the fixed charge density there is zero.\n For example,\n for a triphasic analysis,\n \n\\begin_inset Formula $c_{\\ast}^{+}=c_{\\ast}^{-}\\equiv c_{\\ast}$\n\\end_inset\n\n.\n It follows that the effective fluid pressure in the external environment is \n\\begin_inset Formula $\\tilde{p}_{\\ast}=-R\\theta\\Phi_{\\ast}\\sum\\nolimits_{\\alpha}c_{\\ast}^{\\alpha}$\n\\end_inset\n\n and the effective concentrations are \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}=c_{\\ast}^{\\alpha}/\\hat{\\kappa}_{\\ast}^{\\alpha}\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n.\n Therefore,\n in multiphasic analyses,\n whenever the external environment contains solutes with concentrations \n\\begin_inset Formula $c_{\\ast}^{\\alpha}$\n\\end_inset\n\n,\n the user must remember to prescribe non-zero boundary conditions for the effective solute concentrations \n\\shape italic\nand\n\\shape default\n the effective fluid pressure.\n\\end_layout\n\n\\begin_layout Standard\nLetting \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n also implies that prescribed mixture normal tractions (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Mixture-Normal-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n) represent only the traction above ambient conditions.\n Note that users are not obligated to assume that \n\\begin_inset Formula $p_{\\ast}=0$\n\\end_inset\n\n.\n However,\n if a non-zero value is assumed for the ambient pressure,\n then users must remember to incorporate this non-zero value whenever prescribing mixture normal tractions.\n Similarly,\n users are not required to assume that \n\\begin_inset Formula $\\psi_{\\ast}=0$\n\\end_inset\n\n;\n when a non-zero value is assumed for the electric potential of the external environment,\n the prescribed boundary conditions for the effective concentrations should be evaluated using the corresponding partition coefficient,\n \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}=c_{\\ast}^{\\alpha}/\\tilde{\\kappa}_{\\ast}^{\\alpha}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Initial Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Initial-Conditions-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen a multiphasic material is initially exposed to a given external environment with effective pressure \n\\begin_inset Formula $\\tilde{p}_{\\ast}$\n\\end_inset\n\n and effective concentrations \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n,\n the initial conditions inside the material should be set to \n\\begin_inset Formula $\\tilde{p}=\\tilde{p}_{\\ast}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}^{\\alpha}=\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n in order to expedite the evaluation of the initial state of swelling.\n The values of \n\\begin_inset Formula $\\tilde{p}_{\\ast}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{c}_{\\ast}^{\\alpha}$\n\\end_inset\n\n should be evaluated as described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Boundary-Conditions-1\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Effective Solute Flux\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Effective-Solute-Flux-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe finite element formulation for multiphasic materials in FEBio requires that the natural boundary condition for solute \n\\begin_inset Formula $\\alpha$\n\\end_inset\n\n be prescribed as \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}\\equiv j_{n}^{\\alpha}+\\sum\\nolimits_{\\beta}z^{\\beta}j_{n}^{\\beta}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}$\n\\end_inset\n\n is the effective solute flux.\n For a mixture containing only neutral solutes (\n\\begin_inset Formula $z^{\\beta}=0,\\,\\forall\\beta$\n\\end_inset\n\n ),\n it follows that \n\\begin_inset Formula $\\tilde{j}_{n}^{\\alpha}=j_{n}^{\\alpha}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nPrescribed Electric Current Density\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Prescribed-Electric-Current-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe electric current density in a mixture is a linear superposition of the ion fluxes,\n \n\\begin_inset Formula \n\\[\n\\mathbf{I}_{e}=F_{c}\\sum\\limits_{\\alpha}z^{\\alpha}\\mathbf{j}^{\\alpha}\\,.\n\\]\n\n\\end_inset\n\nSince only the normal component \n\\begin_inset Formula $j_{n}^{\\alpha}=\\mathbf{j}^{\\alpha}\\cdot\\mathbf{n}$\n\\end_inset\n\n of ion fluxes may be prescribed at a boundary,\n it follows that only the normal component \n\\begin_inset Formula $I_{n}=\\mathbf{I}_{e}\\cdot\\mathbf{n}$\n\\end_inset\n\n of the current density may be prescribed.\n To prescribe \n\\begin_inset Formula $I_{n}$\n\\end_inset\n\n,\n it is necessary to know the nature of the ion species in the mixture,\n and how the current is being applied.\n For example,\n if the ions in the triphasic mixture consist of Na\n\\begin_inset Formula $^{\\mathrm{+}}$\n\\end_inset\n\n and Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n,\n and if the current is being applied using silver/silver chloride (Ag/AgCl) electrodes,\n a chemical reaction occurs at the anode where Ag combines with Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n to produce AgCl,\n donating an electron to the electrode to transport the current.\n At the cathode,\n the reverse process takes place.\n Therefore,\n in this system,\n there is only flux of Cl\n\\begin_inset Formula $^{\\mathrm{-}}$\n\\end_inset\n\n and no flux of Na\n\\begin_inset Formula $^{\\mathrm{+}}$\n\\end_inset\n\n (\n\\begin_inset Formula $j_{n}^{+}=0)$\n\\end_inset\n\n at the electrode-mixture interface,\n so that the prescribed boundary condition should be \n\\begin_inset Formula $j_{n}^{-}=-I_{n}/F_{c}$\n\\end_inset\n\n.\n Since \n\\begin_inset Formula $z^{+}=+1$\n\\end_inset\n\n and \n\\begin_inset Formula $z^{-}=-1$\n\\end_inset\n\n in a triphasic mixture,\n the corresponding effective fluxes are given by \n\\begin_inset Formula $\\tilde{j}_{n}^{+}=2j_{n}^{+}-j_{n}^{-}=I_{n}/F_{c}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\tilde{j}_{n}^{-}=j_{n}^{+}=0$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Subsection\nElectrical Grounding\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Electrical-Grounding-1\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf a multiphasic mixture containing ions is completely surrounded by ion-impermeant boundaries it is necessary to ground the mixture to prevent unconstrained fluctuations of the electric potential.\n Grounding is performed by prescribing the effective concentration of one or more ions,\n at a location inside the mixture or on one of its boundaries corresponding to the location of the grounding electrode.\n The choice of ion(s) to constrain may be guided by the type of grounding electrode and the reference electrolyte bath in which it is inserted.\n\\end_layout\n\n\\begin_layout Section\nGuidelines for Fluid Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Guidelines-Fluid-Analyses\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nCurrently,\n the fluid solvers of FEBio can be used to solve for isothermal fluid mechanics problems (\n\\begin_inset Quotes eld\n\\end_inset\n\nfluid\n\\begin_inset Quotes erd\n\\end_inset\n\n solver),\n isothermal fluid-structure interaction problems (\n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n solver,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Structure-Interactions\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) and isothermal fluid mechanics with solute (mass) transport (\n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n solver).\n When solutes are included in an analysis,\n their treatment is very similar to the \n\\begin_inset Quotes eld\n\\end_inset\n\nmultiphasic\n\\begin_inset Quotes erd\n\\end_inset\n\n solver described in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n and solute concentrations \n\\begin_inset Formula $c^{\\iota}$\n\\end_inset\n\n in a fluid-solutes domain are thus related to the effective fluid pressure \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n and effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n to account for the possibility that the solutes are electrically charged.\n The theoretical treatment of solutes in the \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n solver follows that of the \n\\begin_inset Quotes eld\n\\end_inset\n\nmultiphasic\n\\begin_inset Quotes erd\n\\end_inset\n\n solver,\n with the following simplifications:\n (a) The effective solubility \n\\begin_inset Formula $\\hat{\\kappa}^{\\iota}$\n\\end_inset\n\n appearing in the expression for the partition coefficient \n\\begin_inset Formula $\\tilde{\\kappa}^{\\iota}$\n\\end_inset\n\n in eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n only embodies non-ideal physico-chemical behavior (whereas \n\\begin_inset Formula $\\hat{\\kappa}^{\\iota}$\n\\end_inset\n\n in a multiphasic mixture with a porous solid matrix can also embody the steric volume exclusion of solutes from the pore space);\n therefore,\n one should always set \n\\begin_inset Formula $\\hat{\\kappa}^{\\iota}=1$\n\\end_inset\n\n when modeling ideal behavior in a \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n analysis.\n (b) Since there is no porous solid matrix that can hinder solute transport,\n the diffusivity tensor \n\\begin_inset Formula $\\mathbf{d}^{\\iota}$\n\\end_inset\n\n of a solute in a \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n mixture is always equal to \n\\begin_inset Formula $d_{0}^{\\iota}\\mathbf{I}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $d_{0}^{\\iota}$\n\\end_inset\n\n is the solute diffusivity in free solution.\n Thus,\n even though FEBio requires users to specify a value for \n\\begin_inset Formula $\\mathbf{d}^{\\iota}$\n\\end_inset\n\n,\n that value is essentially ignored in the \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n solver code,\n where it is replaced by \n\\begin_inset Formula $d_{0}^{\\iota}\\mathbf{I}$\n\\end_inset\n\n.\n If users would like to neglect osmotic pressure in a \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n analysis,\n the osmotic coefficient \n\\begin_inset Formula $\\Phi$\n\\end_inset\n\n should be set to zero,\n producing \n\\begin_inset Formula $\\tilde{p}=p$\n\\end_inset\n\n.\n Moreover,\n in the absence of solutes (\n\\begin_inset Formula $c^{\\iota}=0$\n\\end_inset\n\n for all \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n,\n as in the \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid\n\\begin_inset Quotes erd\n\\end_inset\n\n and \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n solvers) this relation holds automatically.\n Therefore,\n when dealing with the \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid\n\\begin_inset Quotes erd\n\\end_inset\n\n and \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n solvers,\n we do not need to add the \n\\begin_inset Quotes eld\n\\end_inset\n\neffective\n\\begin_inset Quotes erd\n\\end_inset\n\n modifier when mentioning the fluid pressure.\n\\end_layout\n\n\\begin_layout Standard\nThere are two broad categories of materials available for \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n analyses:\n (1) \n\\emph on\nfluid-FSI\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Fluid-FSI-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n and (2) \n\\emph on\nbiphasic-FSI\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Fluid-BFSI-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n A \n\\emph on\nfluid-FSI\n\\emph default\n material is used to describe a fluid domain whose mesh is deformable;\n though a solid material (typically \n\\emph on\nneo-Hookean\n\\emph default\n) must be assigned to the mesh of a \n\\emph on\nfluid-FSI\n\\emph default\n material,\n its mass density is ignored and it is the user's responsibility to ascribe a negligible elasticity to this solid domain,\n whose sole purpose is to regularize the mesh deformation.\n A \n\\emph on\nbiphasic-FSI\n\\emph default\n material considers that the deformable domain is a biphasic material (see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Biphasic-Materials\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n),\n with a porous-permeable solid domain that has non-negligible mass density,\n solid volume fraction,\n solid matrix stiffness,\n and hydraulic permeability.\n The interstitial fluid of such a domain may also be ascribed a viscosity (Newtonian or non-Newtonian).\n\\end_layout\n\n\\begin_layout Subsection\nDegrees of Freedom and Boundary Conditions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-DOFs-BCs\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe nodal degrees of freedom for \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid\n\\begin_inset Quotes erd\n\\end_inset\n\n analyses are the nodal fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n and nodal fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n.\n For a \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-FSI\n\\begin_inset Quotes erd\n\\end_inset\n\n analysis the nodal fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n is replaced with the relative fluid flux \n\\begin_inset Formula $\\mathbf{w}=\\varphi^{f}\\left(\\mathbf{v}^{f}-\\mathbf{v}^{s}\\right)$\n\\end_inset\n\n and supplemented with the nodal solid displacement,\n \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n,\n whose material time derivative equals the solid velocity \n\\begin_inset Formula $\\mathbf{v}^{s}$\n\\end_inset\n\n.\n Here,\n \n\\begin_inset Formula $\\varphi^{f}$\n\\end_inset\n\n represents the fluid volume fraction (porosity) of the fluid-FSI domain,\n which is prescribed by the user in a \n\\emph on\nbiphasic-FSI\n\\emph default\n material (via the complementary solid volume fraction) or automatically set to \n\\begin_inset Formula $\\varphi^{f}=1$\n\\end_inset\n\n in a \n\\emph on\nfluid-FSI\n\\emph default\n material.\n Thus,\n the degrees of freedom of a fluid-FSI analysis reduce to those of a fluid (or fluid-solutes) analysis in the limit when \n\\begin_inset Formula $\\varphi^{f}\\to1$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}^{s}\\to\\mathbf{0}$\n\\end_inset\n\n.\n For \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n analyses,\n the nodal degrees of freedom include the effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n;\n as for \n\\begin_inset Quotes eld\n\\end_inset\n\nmultiphasic\n\\begin_inset Quotes erd\n\\end_inset\n\n analyses,\n it is assumed that solutes occupy a negligible volume fraction in the mixture (thus \n\\begin_inset Formula $\\varphi^{f}=1$\n\\end_inset\n\n for \n\\begin_inset Quotes eld\n\\end_inset\n\nfluid-solutes\n\\begin_inset Quotes erd\n\\end_inset\n\n analyses).\n\\end_layout\n\n\\begin_layout Standard\nThe fluid Cauchy stress is given by \n\\begin_inset Formula $\\boldsymbol{\\sigma}^{f}=-p\\mathbf{I}+\\boldsymbol{\\tau}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $p$\n\\end_inset\n\n is the actual (not the effective) fluid pressure,\n as per eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n The viscous stress \n\\begin_inset Formula $\\boldsymbol{\\tau}$\n\\end_inset\n\n depends on the fluid rate of deformation tensor \n\\begin_inset Formula $\\mathbf{D}^{f}$\n\\end_inset\n\n according to a user-selected constitutive relation,\n such as \n\\emph on\nNewtonian fluid\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Standard\nIn any of the fluid analysis types,\n the user may prescribe any of the components of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n as an essential boundary condition,\n or the corresponding component of the viscous traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\boldsymbol{\\tau}\\cdot\\mathbf{n}$\n\\end_inset\n\n as a natural boundary condition (\n\\begin_inset Formula $\\mathbf{n}$\n\\end_inset\n\n being the normal to the boundary surface).\n When neither a component of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n nor the corresponding component of \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n is prescribed explicitly,\n the latter is naturally assumed to be zero.\n Similarly,\n the user may prescribe \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n (or \n\\begin_inset Formula $\\tilde{p}\\left(e^{f}\\right)$\n\\end_inset\n\n,\n see Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Pressure\"\nnolink \"false\"\n\n\\end_inset\n\n) as an essential boundary condition,\n or \n\\begin_inset Formula $w_{n}=\\mathbf{w}\\cdot\\mathbf{n}$\n\\end_inset\n\n as a natural boundary condition.\n When neither is prescribed explicitly,\n it is naturally assumed that \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n.\n Natural boundary conditions are useful for creating symmetry planes in fluid analyses,\n where the normal fluid velocity and the viscous traction are zero:\n On those symmetry planes,\n no boundary conditions should be specified.\n\\end_layout\n\n\\begin_layout Standard\nIt is noteworthy that the fluid formulation in FEBio allows the prescription of the nodal value of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n as an essential boundary condition,\n and the surface value of \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n as a natural boundary condition.\n On boundaries where the fluid velocity is completely prescribed,\n i.e.,\n when normal and tangential components are known (and not zero),\n prescribing \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n and \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n instead of just \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n produces the best computational outcome.\n In those cases,\n the user should use the \n\\emph on\nfluid velocity\n\\emph default\n surface load,\n which combines both boundary conditions (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Velocity\"\nnolink \"false\"\n\n\\end_inset\n\n).\n If the user only wants to prescribe a known normal velocity \n\\begin_inset Formula $w_{n}$\n\\end_inset\n\n and leave the tangential velocity unspecified,\n use the \n\\emph on\nfluid normal velocity\n\\emph default\n surface load (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Normal-Velocity\"\nnolink \"false\"\n\n\\end_inset\n\n);\n this surface load also provides the option of setting the tangential fluid velocity to zero.\n Finally,\n if the user wants the velocity to remain normal to the selected surface (e.g.,\n an inlet or outlet surface on which \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n is prescribed or fixed),\n but does not know the velocity magnitude \n\\emph on\na priori\n\\emph default\n,\n use the \n\\emph on\nnormal fluid velocity \n\\emph default\nconstraint (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Normal-Fluid-Velocity\"\nnolink \"false\"\n\n\\end_inset\n\n) to enforce this condition.\n\\end_layout\n\n\\begin_layout Standard\nA viscous fluid satisfies the no-slip condition on a physical boundary surface.\n This means that \n\\begin_inset Formula $\\mathbf{w}=\\mathbf{0}$\n\\end_inset\n\n on no-slip boundaries.\n This boundary condition can be enforced simply by fixing the three components of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Displacement\"\nnolink \"false\"\n\n\\end_inset\n\n) and not specifying any value for \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n on that surface (which naturally enforces \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nComputational fluid dynamics analyses are often performed over a mesh that truncates the real fluid domain.\n Thus,\n fluid may enter the finite element domain across some upstream inlet boundary and leave the domain across some downstream outlet boundary.\n The exact flow conditions on these boundaries may not be known,\n thus they have to be approximated using best guesses.\n Arguably,\n the viscous traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n is the least intuitive boundary condition to guess.\n On an outlet boundary,\n it is necessary to prescribe or fix the dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n to overcome the natural boundary condition \n\\begin_inset Formula $w_{n}=0$\n\\end_inset\n\n.\n However,\n in most cases the tangential components of \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n on that boundary are not known,\n therefore they cannot be fixed or prescribed,\n leading to the natural condition \n\\begin_inset Formula $\\mathbf{t}^{\\tau}=\\mathbf{0}$\n\\end_inset\n\n.\n This natural condition may work fine in some cases,\n but it will often fail numerically when vortices being shed inside the finite element domain try to cross the outlet boundary.\n In those cases,\n consider using the \n\\emph on\nfluid backflow stabilization \n\\emph default\n(Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Backflow-Stabilization\"\nnolink \"false\"\n\n\\end_inset\n\n) and \n\\emph on\nfluid tangential stabilization\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Tangential-Stabilization\"\nnolink \"false\"\n\n\\end_inset\n\n) surface loads,\n which prescribe a velocity-dependent viscous traction \n\\begin_inset Formula $\\mathbf{t}^{\\tau}$\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nSimilarly,\n on an inlet boundary that shares an edge with a no-slip surface,\n prescribing the fluid velocity \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n on the inlet boundary may not accurately reproduce the velocity profile in the boundary layer that would be produced on the adjoining no-slip surface.\n This potential mismatch could result in numerical instabilities;\n in such cases,\n prescribing a dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n on the shared edge may mitigate this issue.\n\\end_layout\n\n\\begin_layout Subsection\nBiased Meshes for Boundary Layers\n\\end_layout\n\n\\begin_layout Standard\nIn fluid analyses,\n boundary layers will form in the vicinity of no-slip boundaries,\n where the velocity magnitude varies rapidly with the distance from a no-slip surface.\n The thickness of boundary layers decreases with increasing Reynolds number.\n To capture these boundary layers accurately in a fluid finite element analysis,\n it is necessary to refine the mesh to produce thinner elements closer to the no-slip boundaries.\n This is done most conveniently by using mesh biasing tools,\n available in various meshing software programs,\n or post-hoc boundary-layer meshing tool that modify an existing mesh.\n Both of these options are available in FEBioStudio.\n Boundary layer meshing should always be used in fluid analyses in FEBio.\n Whereas other finite element codes employ numerical stabilization techniques that partially alleviate the need for biased meshes,\n FEBio does not employ such techniques.\n Therefore,\n using uniform finite element meshes may fail to produce numerical convergence,\n even in some seemingly simple problems.\n Users should keep in mind that biased meshes don't necessarily require more nodes and elements,\n therefore there is no automatic computational cost for using biased meshes.\n\\end_layout\n\n\\begin_layout Subsection\nComputational Efficiency:\n Broyden's Method\n\\end_layout\n\n\\begin_layout Standard\nFluid analyses produce a non-symmetric stiffness matrix and the resulting system of equations may be efficiently solved using Broyden's quasi-Newton method,\n where an approximation to the matrix inverse is produced at each iteration after the first full-Newton iteration of a time step.\n In fluid analyses,\n it is often possible to achieve convergence at each time step without having to perform additional full-Newton updates.\n Therefore,\n it is recommended to set \n\\emph on\nmax_updates\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Control-Section\"\nnolink \"false\"\n\n\\end_inset\n\n) to a large number (e.g.,\n 50),\n along with setting \n\\emph on\ndiverge_reform\n\\emph default\n to 0 (false),\n which leads to a more efficient solution scheme.\n A further advantage arises in fluid analyses since the mesh remains invariant over time:\n it is often possible to continue using Broyden updates even across time steps,\n without performing a full-Newton iteration at the start of that step,\n by setting \n\\emph on\nreform_each_time_step\n\\emph default\n to 0 (false).\n In that case,\n the solver will continue using Broyden updates up to the value of \n\\emph on\nmax_updates\n\\emph default\n,\n before performing a full Newton update.\n\\end_layout\n\n\\begin_layout Subsection\nDynamic versus Steady-State Analyses\n\\end_layout\n\n\\begin_layout Standard\nFEBio runs fluid analyses in dynamic mode by default,\n because many fluid flow problems do not have a steady-state solution,\n since the governing equations are nonlinear.\n For example,\n many flows may exhibit vortex shedding as fluid flows across an obstacle,\n such as a flow constriction or a solid body.\n Even if some form of periodicity emerges under specific flow conditions,\n as in the von Karman vortex street,\n the corresponding solution is not in steady state.\n However,\n for some low Reynolds number laminar flows,\n a steady-state response may exist and FEBio will attempt to find that solution when using analysis type \n\\begin_inset Quotes eld\n\\end_inset\n\nsteady-state\n\\begin_inset Quotes erd\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Control-Section\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The user should be aware that,\n under steady-state analyses,\n the solution may not necessarily converge as efficiently to the final solution as would a dynamic analysis that allows the solution to reach steady state after a sufficiently long time.\n The best choice of analysis type for steady-state problems may need to be determined by trial and error.\n\\end_layout\n\n\\begin_layout Subsection\nIsothermal Compressible Flow versus Acoustics\n\\end_layout\n\n\\begin_layout Standard\nThe governing equations for fluid flow in FEBio represent isothermal conditions,\n thus eliminating temperature as a variable in the numerical analysis.\n Since the fluid solver accommodates some measure of fluid compressibility (via the fluid dilatation variable \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n),\n it is possible to solve for pressure wave propagation in fluids using this formulation.\n However,\n from a theoretical perspective,\n it should be recognized that the speed of sound (i.e.,\n the wave speed) in isothermal (constant temperature) flow is different from the speed of sound in isentropic (constant entropy) flow.\n The field of acoustics typically examines pressure wave propagations under isentropic conditions,\n which are found to be more consistent with experimental measurements of the speed of sound in air.\n Therefore,\n the FEBio fluid solver may not produce realistic wave speeds when simulating acoustics.\n A fluid solver for isentropic flow may be developed for FEBio in the future.\n\\end_layout\n\n\\begin_layout Subsection\nFluid-Structure Interactions\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Structure-Interactions\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFluid-structure interactions (FSI) may be modeled in FEBio using the \n\\emph on\nfluid-FSI\n\\emph default\n module (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Module-Section\"\nnolink \"false\"\n\n\\end_inset\n\n).\n In an FSI analysis the finite element mesh defining the fluid domain is deformable,\n so that the shape of the fluid domain does not have to remain fixed over time.\n The fluid flows through the deforming mesh,\n just as it does in standard computational fluid dynamics (CFD) analyses with fixed meshes.\n The deformation of the fluid domain may be controlled by prescribing suitable boundary conditions (so-called \n\\emph on\nmoving boundary problems\n\\emph default\n) and by interactions with solid domains.\n For example,\n fluid may flow inside a flexible tube and the fluid pressure may cause the tube to expand or contract;\n the tube itself may be pinched at some location,\n reducing the cross-section through which fluid flows.\n The tube wall is a deformable solid domain which may be modeled in FEBio using any of the solid material models described in Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Elastic-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n through \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Multigeneration-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n,\n or it may be modeled as a porous deformable domain containing a viscous fluid,\n in which case one should use the \n\\emph on\nbiphasic-FSI\n\\emph default\n material for the tube wall (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Fluid-BFSI-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The inner tube (the fluid domain) should be modeled as a \n\\emph on\nfluid-FSI\n\\emph default\n material (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:General-Fluid-FSI-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nIn FEBio,\n a \n\\emph on\nfluid-FSI\n\\emph default\n material is modeled as a specialized fluid-solid mixture where the solid matrix density is zero and its stiffness should be set to a negligible value.\n In contrast,\n a \n\\emph on\nbiphasic-FSI\n\\emph default\n material is a full-fledged fluid-solid mixture,\n where the solid may have mass and exhibit any desired nonlinear elastic or inelastic response.\n Both FSI materials require the user to define a \n\\emph on\nfluid\n\\emph default\n material from the same list as those available for CFD analyses (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n).\n It also requires the user to define a \n\\emph on\nsolid\n\\emph default\n material to regularize the mesh deformation,\n which may be selected from Sections\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Elastic-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n through \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Multigeneration-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n for a \n\\emph on\nbiphasic-FSI\n\\emph default\n material.\n In contrast,\n the \n\\emph on\nsolid\n\\emph default\n material of a \n\\emph on\nfluid-FSI\n\\emph default\n material is massless (the \n\\emph on\ndensity\n\\emph default\n value entered by the user is ignored and reset internally to zero).\n Among all the choices available in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Elastic-Solids\"\nnolink \"false\"\n\n\\end_inset\n\n,\n it is recommended to use the \n\\emph on\nneo-Hookean\n\\emph default\n elastic solid (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Neo-Hookean\"\nnolink \"false\"\n\n\\end_inset\n\n),\n setting Poisson's ratio \n\\begin_inset Formula $\\nu$\n\\end_inset\n\n to zero and Young's modulus \n\\begin_inset Formula $E$\n\\end_inset\n\n to a very low value (a few orders of magnitude smaller than the moduli of surrounding solid domains).\n\\end_layout\n\n\\begin_layout Standard\nThe degrees of freedom at each node of a FSI domain consist of the components of the solid displacement \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n (which describe the mesh deformation),\n the fluid velocity \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n relative to the solid,\n and the fluid dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n.\n Essential boundary conditions may be prescribed on any of these degrees of freedom.\n For example,\n in a moving boundary problem,\n the motion of a boundary may be prescribed by setting the relevant components of \n\\begin_inset Formula $\\mathbf{u}$\n\\end_inset\n\n.\n A no-slip boundary between an FSI material and a solid domain may be defined by simply setting \n\\begin_inset Formula $\\mathbf{w}=\\mathbf{0}$\n\\end_inset\n\n,\n regardless of the motion of that boundary.\n In general,\n all boundary conditions described for CFD analyses in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-DOFs-BCs\"\nnolink \"false\"\n\n\\end_inset\n\n may be applied identically to FSI analyses.\n\\end_layout\n\n\\begin_layout Standard\nCurrently in FEBio,\n the mesh of a \n\\emph on\nfluid/biphasic-FSI\n\\emph default\n material domain must be continuous with the mesh of surrounding solid domains.\n The surface between domains,\n which consists of the shared faces of adjoining finite elements from each domain,\n is called the FSI interface.\n Because the mesh is continuous,\n the solid displacement and the solid component of the traction are automatically continuous across FSI interfaces;\n when fluid is present on both sides of the interface,\n the relative fluid flux is also automatically continuous.\n In other words,\n when two FSI domains are adjoining (e.g.,\n fluid-FSI with fluid-FSI,\n or fluid-FSI with biphasic-FSI),\n all boundary conditions are automatically satisfied across those interfaces.\n However,\n when an FSI domain interfaces with a solid domain,\n the fluid component of the traction in the \n\\emph on\nfluid-FSI\n\\emph default\n and \n\\emph on\nbiphasic-FSI\n\\emph default\n domains must be explicitly transferred to the solid domain across that interface.\n This is done by prescribing a \n\\emph on\nfluid-FSI traction\n\\emph default\n or a \n\\emph on\nbiphasic-FSI traction\n\\emph default\n on that interface (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-FSI-Traction\"\nnolink \"false\"\n\n\\end_inset\n\n).\n This surface load does not require any user-defined parameters;\n its sole purpose is to identify those FSI interfaces where the fluid traction must be properly transferred to the surrounding solid domain.\n Omitting this boundary condition means that the fluid traction (i.e.,\n the fluid pressure and the normal and tangential viscous components of the fluid stress) have no effect on this interface;\n the interface may still deform in response to the motion of the solid domain.\n For example,\n when a fluid interacts with a rigid solid domain whose motion is entirely prescribed,\n it is not necessary to apply a \n\\emph on\nfluid-FSI traction\n\\emph default\n at the interface between the fluid and rigid solid domains.\n\\end_layout\n\n\\begin_layout Standard\nA \n\\emph on\nfluid-FSI traction\n\\emph default\n is also required on free fluid surfaces,\n such as the surface of a fluid in open channel flow,\n in order to properly capture the motion of that surface (such as waves).\n This condition is required since the net traction \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n on a free surface is zero.\n As the fluid-FSI domain is modeled as a specialized solid-fluid mixture,\n the net traction consists of the sum of solid and fluid tractions,\n \n\\begin_inset Formula $\\mathbf{t}=\\mathbf{t}^{s}+\\mathbf{t}^{f}$\n\\end_inset\n\n.\n Setting \n\\begin_inset Formula $\\mathbf{t}$\n\\end_inset\n\n to zero produces a traction \n\\begin_inset Formula $\\mathbf{t}^{s}=-\\mathbf{t}^{f}$\n\\end_inset\n\n on the solid mesh,\n allowing it to deform to the proper shape.\n\\end_layout\n\n\\begin_layout Standard\nHere are a few cautionary notes to keep in mind when running FSI analyses:\n (1) Depending on the problem being analyzed,\n fluid-structure interactions may cause significant mesh deformations that lead to mesh distortion.\n Currently,\n adaptive meshing is not yet available to relieve this mesh distortion,\n therefore analyses and meshes need to be designed to minimize this effect or to terminate before the solution becomes too corrupted.\n (2) It is possible to pinch a solid domain that surrounds a fluid domain until the flow cross-section has reduced considerably;\n however it is not possible to completely shut off the flow,\n as this would require reducing the volume of some finite elements to zero.\n (3) While it is possible to model the deformation of free fluid surfaces,\n such as the formation of surface waves in open channel flow or the deformation of fluid blobs floating in a gravity-free environment,\n it is not possible to model the separation of such fluid domains into sub-domains such as spraying fluid droplets;\n this means that analyses where such phenomena are expected to happen will likely fail.\n (4) Whereas standard CFD analyses may be jump-started by prescribing a relatively large fluid velocity at the very first time step of an analysis,\n these types of boundary conditions may lead to dynamic oscillations and potential instabilities in an FSI analysis;\n therefore,\n users must be more considerate when prescribing boundary conditions for FSI analyses.\n\\end_layout\n\n\\begin_layout Subsection\nFluid-Solutes Analyses\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Fluid-Solutes-Analyses\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFluid-solutes analyses introduce two types of molar fluxes into an analysis:\n (1) The diffusive molar flux \n\\begin_inset Formula $\\mathbf{j}_{d}^{\\iota}=-\\tilde{\\kappa}^{\\iota}d_{0}^{\\iota}\\grad\\tilde{c}^{\\iota}$\n\\end_inset\n\n and (2) the sedimentation flux \n\\begin_inset Formula $\\mathbf{j}_{b}^{\\iota}=s^{\\iota}c^{\\iota}\\mathbf{b}^{\\iota}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $s^{\\iota}\\equiv d_{0}^{\\iota}\\frac{M^{\\iota}}{R\\theta}$\n\\end_inset\n\n is the sedimentation coefficient and \n\\begin_inset Formula $\\mathbf{b}^{\\iota}$\n\\end_inset\n\n is the body force acting on solute \n\\begin_inset Formula $\\iota$\n\\end_inset\n\n.\n These molar fluxes represent the flux of the solute relative to the solvent.\n The natural boundary condition for solutes is \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}=0$\n\\end_inset\n\n,\n where \n\\begin_inset Formula \n\\begin{equation}\n\\tilde{j}_{n}^{\\iota}=\\left(\\mathbf{j}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}^{\\gamma}\\right)\\cdot\\mathbf{n}\\label{eq:normal-effective-solute-flux}\n\\end{equation}\n\n\\end_inset\n\nis the normal component of the effective molar flux \n\\begin_inset Formula $\\mathbf{j}^{\\iota}+\\sum_{\\gamma}z^{\\gamma}\\mathbf{j}^{\\gamma}$\n\\end_inset\n\n on a boundary,\n with \n\\begin_inset Formula $\\mathbf{j}^{\\iota}=\\mathbf{j}_{d}^{\\iota}+\\mathbf{j}_{b}^{\\iota}$\n\\end_inset\n\n representing the sum of diffusive and sedimentation fluxes.\n The effective molar flux is constructed in this manner to enforce the electroneutrality condition,\n\\begin_inset Formula \n\\begin{equation}\n\\sum_{\\iota}z^{\\iota}c^{\\iota}=0\\label{eq:electroneutrality-condition}\n\\end{equation}\n\n\\end_inset\n\nas also done in \n\\begin_inset Quotes eld\n\\end_inset\n\nmultiphasic\n\\begin_inset Quotes erd\n\\end_inset\n\n analyses (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Prescribed-Effective-Solute-Flux\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Standard\nZero natural flux \n\\begin_inset Formula $\\tilde{j}_{n}^{\\iota}$\n\\end_inset\n\n implies that the solute moves at the velocity of the solvent (the fluid).\n Therefore,\n if the fluid satisfies \n\\begin_inset Formula $v_{n}^{f}=0$\n\\end_inset\n\n on that boundary,\n the solute will not transport across it.\n But if the fluid is allowed to transport across the boundary (e.g.,\n using a prescribed fluid pressure),\n the solute can also transport across the boundary via convection.\n Therefore,\n for open flows or flows through conduits,\n where the fluid pressure is prescribed (e.g.,\n set to zero) at an outlet boundary,\n no specific boundary condition needs to be prescribed on the solute in order for the latter to convect freely with the solvent at that outlet boundary.\n However,\n if the solvent exhibits back flow on that boundary in a manner that requires the application of the \n\\emph on\nfluid backflow stabilization\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Backflow-Stabilization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) and \n\\emph on\nfluid tangential stabilization\n\\emph default\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Tangential-Stabilization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) surfaces loads,\n then consider also using the \n\\emph on\nsolute backflow stabilization\n\\emph default\n surface load (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Solute-Backflow-Stabilization\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n) on that boundary,\n to produce more physically-realistic solute flows on such truncated domains,\n and improve numerical convergence.\n\\end_layout\n\n\\begin_layout Standard\nThe fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n in a fluid-solutes analysis is given by eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n To prescribe the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n on the boundary of a fluid-solutes domain,\n use the fluid pressure boundary condition specified in Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Fluid-Pressure-BC\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nBy default,\n the fluid-solutes solver employs the \n\\emph on\nstaggered\n\\emph default\n \n\\emph on\nequation_scheme\n\\emph default\n format for the stiffness matrix,\n and solves for the nodal fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n nodal dilatation \n\\begin_inset Formula $e^{f}$\n\\end_inset\n\n and nodal effective solute concentrations \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n using a \n\\emph on\ncoupled\n\\emph default\n \n\\emph on\nsolve_strategy\n\\emph default\n.\n This default settings works well when the Peclet number (the ratio of solute convective velocity to solute diffusive velocity) is relatively low (e.g.,\n less than 100).\n For these problems,\n using the default value of \n\\emph on\nctol=0.01\n\\emph default\n is generally appropriate.\n However,\n if users notice significant convergence difficulties on the solute concentration as the Peclet number approaches 100 or greater,\n consider setting \n\\emph on\nctol=0\n\\emph default\n.\n When solving strongly convective solute transport problems with high Peclet numbers (e.g.,\n greater than 1000),\n diffusion will be negligible compared to convection.\n In that case,\n users should switch to \n\\emph on\nequation_scheme=block\n\\emph default\n and \n\\emph on\nsolve_strategy=sequential\n\\emph default\n and set \n\\emph on\nctol=0\n\\emph default\n to increase the chances of achieving a converged numerical solution.\n The rationale for these choices is explained in the FEBio journal article associated with this fluid-solutes solver \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim23\"\nliteral \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\begin_layout Standard\nAs done in the multiphasic solver,\n the solute volume fraction is considered negligible in fluid mixtures modeled in the fluid-solutes solver.\n Therefore,\n the volume of a fluid mixture will not change with increasing solute concentrations.\n When a solute is convected by the solvent,\n it means that the solvent exerts a frictional force on the solute,\n whose magnitude is inversely proportional to the solute diffusivity \n\\begin_inset Formula $d_{0}^{\\iota}$\n\\end_inset\n\n.\n In the fluid-solutes solver,\n the acceleration of solutes is considered negligible,\n implying that the velocity of the solute in the solvent reaches its terminal velocity instantaneously (thus,\n inertial forces are negligible compared to the drag force between solute and solvent).\n\\end_layout\n\n\\begin_layout Standard\nAccording to Newton's third law stating that every action force has an equal and opposite reaction force,\n the drag force exerted by the solvent on the solute is balanced by an equal and opposite reaction force exerted by the solute on the solvent.\n However,\n by default,\n this reaction force on the solvent is turned off in the fluid-solutes solver.\n In other words,\n the motion of the solvent affects that of the solute,\n but the motion of the solute does not affect the solvent.\n This default setting is adopted because it represent the common form of diffusion-reaction equations modeled in most of the mass transport literature.\n Therefore,\n when modeling solute diffusion problems in a stationary solvent (e.g.,\n in the absence of pressure or velocity-driven solvent flows),\n the diffusive flux of a solute will not drag the solvent,\n keeping it in a stationary state.\n When the solvent motion is driven by solute concentration gradients,\n we call it \n\\emph on\nosmosis\n\\emph default\n.\n There is no common term describing solvent motion caused by solute sedimentation (motion of solute under the action of a body force),\n so we use the term \n\\emph on\nosmosis\n\\emph default\n to describe this phenomenon as well.\n Users can override the default setting of the fluid-solutes solver by checking the \n\\emph on\ninclude osmosis \n\\emph default\nbox in the fluid-solutes material definition.\n When this box is checked,\n the solvent will have a force exerted on it by the solute (either due to diffusion or sedimentation,\n or both),\n causing it to move.\n\\end_layout\n\n\\begin_layout Standard\nHigh-Peclet number flows are notoriously difficult to solve numerically.\n In FEBio we have reported valid solutions for a Peclet number as high as \n\\begin_inset Formula $10^{11}$\n\\end_inset\n\n \n\\begin_inset CommandInset citation\nLatexCommand citep\nkey \"Shim23\"\nliteral \"false\"\n\n\\end_inset\n\n,\n thanks to the specific finite element formulation employed in the fluid-solutes solver,\n and the use of the sequential solver when needed.\n Nevertheless,\n the fluid-solutes solver is not fool-proof.\n In some problems,\n spurious increases in solute concentration can occur,\n seemingly randomly in the mesh,\n even when using a highly regular mesh.\n In many of these cases,\n this type of numerical artifact may go away by simply refining the mesh.\n However,\n users should be aware that this artifact may emerge 'silently',\n meaning that it does not necessarily produce slower numerical convergence that could hint at this problem.\n\\end_layout\n\n\\begin_layout Standard\nThe explanation for this behavior is that the numerical solutions to the effective solute concentrations at large Peclet numbers are effectively solutions to the partial differential equation \n\\begin_inset Formula $D^{f}\\left(J^{f}c^{\\iota}\\right)/Dt=0$\n\\end_inset\n\n,\n as reviewed in the Theory Manual.\n This integration is performed based on the current solution for \n\\begin_inset Formula $J^{f}=1+e^{f}$\n\\end_inset\n\n and \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n as well as the structure of the mesh.\n Any ill-conditioning in the mesh (such as a mesh which is too coarse) may introduce a numerical round-off error in the calculation of \n\\begin_inset Formula $\\tilde{c}^{\\iota}$\n\\end_inset\n\n,\n and there is no other sufficiently dominant term in the solute mass balance equation that can dampen or correct for such numerical round-offs.\n Thus,\n they may grow over time.\n Mesh refinements are required when encountering these circumstances.\n\\end_layout\n\n\\begin_layout Standard\nOther boundary conditions for the solute (such as the essential boundary condition \n\\begin_inset Formula $\\tilde{c}^{\\iota}=0$\n\\end_inset\n\n) may also improve predictions when using relatively coarse meshes,\n preventing the occurrence of spurious rises which often initiate on boundaries.\n Unfortunately,\n in some problems,\n this option forces users to adopt a non-physical boundary condition which may produce unacceptable results.\n This workaround may not be needed when using a sufficiently refined mesh,\n though finer meshes typically require longer solution times.\n Also note that highly irregular meshes near no-slip boundaries may cause spurious rises in solute concentration in high Peclet number flows.\n Therefore,\n it is recommended to use suitable meshing techniques for generating boundary layer meshes,\n such as stacking pentahedral elements atop tetrahedral elements when using unstructured tetrahedral meshes.\n For diffusion-dominated or reaction-dominated flows however,\n these numerical concerns may not arise.\n Nevertheless,\n a thorough understanding of the underlying physics of these problems is essential for properly prescribing boundary conditions,\n and the number and size of time increments in a finite element analysis.\n\\end_layout\n\n\\begin_layout Standard\nThe default settings for the fluid-solutes solver force it to reform the stiffness matrix at the start of each time step.\n This is a standard requirement in most of the FEBio solvers (including solid and structural mechanics,\n and standard biphasic and multiphasic domains),\n though it had previously been demonstrated that the fluid solver could avoid reforming the stiffness matrix even after achieving convergence at a given time step,\n producing a very efficient computational scheme.\n Unfortunately,\n this increased efficiency is not always an option with the fluid-solutes solver,\n because the stiffness matrix contains terms that depend significantly on the solute concentrations.\n As these concentrations evolve over time and space,\n it becomes essential to update the stiffness matrix to reflect those changes.\n Therefore,\n consider using the direct sparse solver from the Intel MKL library (\n\\emph on\nlinear_solver=Mkl dss\n\\emph default\n) to improve computational efficiency during matrix factorization,\n in comparison to the Pardiso solver from the same library.\n Moreover,\n in applications where the initial solute concentration is non-zero,\n and non-negligible compared to the prescribed change in concentration on an inlet boundary,\n it may be possible to solve a fluid-solutes problem more efficiently without reforming the stiffness matrix at each time step.\n\\end_layout\n\n\\begin_layout Section\nUnderstanding the Solution\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Understanding-the-Solution\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nOkay,\n FEBio found a solution.\n Great,\n but how do you know it is the right one?\n Well,\n it turns out this is in fact a harder question than it may seem.\n In any finite element model there are numerous assumptions and approximations and trying to discuss all of these is outside the scope of this section.\n Instead,\n we'll look at some of the most typical problems that FEBio users have run into.\n\\end_layout\n\n\\begin_layout Subsection\nMesh convergence\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Mesh-convergence\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe solution calculated by FEBio only applies to the mesh for which it was solved.\n However,\n the \n\\begin_inset Quotes eld\n\\end_inset\n\nexact\n\\begin_inset Quotes erd\n\\end_inset\n\n solution must be independent of the mesh and therefore a mesh convergence study is almost always necessary to make sure the FEBio solution is close to this exact solution.\n In practice this means that a model must be run several times with an increasingly finer mesh to find out at which mesh density the FEBio solution no longer changes.\n\\end_layout\n\n\\begin_layout Subsection\nConstraint enforcement\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Constraint-enforcement\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio uses many iterative algorithms for enforcing constraints.\n For each of these constraints the user must verify that the solution indeed satisfies these constraints sufficiently.\n The two most common constraints used in FEBio are incompressibility and contact.\n\\end_layout\n\n\\begin_layout Standard\nFor uncoupled materials,\n incompressibility (for hexahedral elements) is handled in FEBio using a three-field formulation in addition to the enforcement of the incompressibility constraint.\n This constraint is usually enforced using a penalty formulation (although an augmented Lagrangian method is also available).\n To inspect whether the incompressibility constraint is satisfied sufficiently the user can look at the volume ratio which has to be close to one.\n If it deviates from one too much the user needs to increase the \n\\begin_inset Quotes eld\n\\end_inset\n\nbulk modulus\n\\begin_inset Quotes erd\n\\end_inset\n\n of the material.\n\\end_layout\n\n\\begin_layout Standard\nFor coupled materials,\n incompressibility is not treated explicitly and care must be taken when using coupled materials in a near-incompressible regime (e.g.\n setting the Poisson's ratio too close to 0.5 for a neo-Hookean material).\n In that case,\n the solution will most likely \n\\begin_inset Quotes eld\n\\end_inset\n\nlock\n\\begin_inset Quotes erd\n\\end_inset\n\n which manifests itself in displacements that are too small.\n Inspection of the volume ratio may not be sufficient to identify locking.\n However,\n a mesh convergence study will often help in identifying this problem.\n\\end_layout\n\n\\begin_layout Standard\nWhen a contact constraint is not sufficiently enforced,\n the contacting surfaces will have penetrated.\n This problem is usually identified easily by looking at the deformed model in a post-processing software (e.g.\n FEBioStudio).\n Increasing the penalty factor and/or decreasing the augmented Lagrangian tolerance should solve this problem.\n\\end_layout\n\n\\begin_layout Section\nGuidelines for Using Prestrain\n\\end_layout\n\n\\begin_layout Standard\nThere are a variety of ways in which prestrain can be added to an FEBio model,\n but the recommended approach – a two-step approach – is outlined here.\n \n\\end_layout\n\n\\begin_layout Standard\nIn the first step,\n the prestrain is enforced on the model.\n This is accomplished by making sure the part that will be prestrained uses the prestrain elastic material (or uncoupled prestrain elastic for uncoupled hyperelastic materials).\n A suitable prestrain generator must be defined as well.\n In addition,\n one of the update rules can be specified as a nonlinear constraint in order to modify the effective prestrain field.\n The boundary conditions in this step should be minimal but sufficient to ensure a well-defined finite element problem.\n\\end_layout\n\n\\begin_layout Standard\nAfter the first step converges,\n the model is successfully prestrained.\n However,\n depending on the application,\n the reference geometry could have changed and this may complicate the application of further loads (most of FEBio will still use the original geometry as the reference geometry).\n To eliminate this potential source of confusion,\n it is recommended to apply the prestrain initial condition (see chapter 4) which essentially fixes the prestrain gradient and resets the reference geometry to the current geometry.\n FEBio will then apply any subsequent loads to this new reference geometry.\n One caveat of this approach is that the plotfile still stores the original reference geometry.\n To prevent inconsistencies in the plotfile it is recommend suppressing the creation of the plot file in the first step.\n This is done by setting the following control flag in the Control section of the first step.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<plot_level>PLOT_NEVER</plot_level>\n\\end_layout\n\n\\begin_layout Standard\nThen,\n in the Control section of the second step,\n restore the plot level value.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<plot_level>PLOT_DEFAULT</plot_level>\n\\end_layout\n\n\\begin_layout Standard\nNow,\n the new reference configuration resulting after the prestrain analysis step,\n will be the reference configuration that is used in the plot file.\n \n\\end_layout\n\n\\begin_layout Section\nGuidelines for using the CG-solid solver\n\\end_layout\n\n\\begin_layout Standard\nThe nonlinear conjugate gradient solver,\n CG-solid,\n is an alternative solution algorithm for static solid mechanics problems.\n It does not currently support dynamic problems,\n fluids,\n etc.\n This solver has advantages in several situations:\n \n\\end_layout\n\n\\begin_layout Itemize\nIt uses much less memory and therefore allows bigger problems to be solved on a given computer than the standard Newton-based solvers\n\\end_layout\n\n\\begin_layout Itemize\nIts performance scales better for large problems and it may be faster for very large problems (but see the limitations below);\n \n\\end_layout\n\n\\begin_layout Itemize\nFor nonlinear problems with buckling or other instabilities it can converge much more reliably than the standard solver;\n \n\\end_layout\n\n\\begin_layout Itemize\nFor many problems it will converge in a single timestep.\n \n\\end_layout\n\n\\begin_layout Standard\nHowever,\n it also has some disadvantages:\n \n\\end_layout\n\n\\begin_layout Itemize\nFor small models it is typically slower than the BFGS solver;\n \n\\end_layout\n\n\\begin_layout Itemize\nAlthough it usually converges in a single timestep,\n it requires a much larger number of iterations to do so.\n It is therefore inefficient if many timesteps are needed to produce intermediate results or follow the progression of a problem;\n\\end_layout\n\n\\begin_layout Itemize\nThe number of iterations it requires to converge is proportional to the number of elements across the longest dimension of the mesh.\n It therefore performs best for compact problems with similar numbers of elements in each direction.\n For example a cube with equal numbers of elements in each direction would solve much faster than a flat sheet or a long,\n thin beam with the same total number of elements;\n \n\\end_layout\n\n\\begin_layout Itemize\nIt does not currently work well for shell elements or freely moving rigid bodies.\n The way it works is quite different from the Newton-based solver and the problem may need to be defined differently for optimum results:\n \n\\end_layout\n\n\\begin_layout Itemize\nThe convergence criteria in FEBio measure the change in the solution on each iteration as a fraction of the total change for the timestep.\n Because the CG solver converges in a much larger number of smaller steps,\n the convergence criteria must be set smaller than for the BFGS solver.\n The default values are dtol=1e-6 and etol=0.001,\n but it may be necessary to reduce these further to ensure full convergence.\n Check the solution carefully to make sure that it is correct and reduce dtol further if necessary.\n\\end_layout\n\n\\begin_layout Itemize\nThe rate of convergence is quite slow near to the solution so if the convergence tolerance is set too small the solution may take much longer.\n \n\\end_layout\n\n\\begin_layout Itemize\nIt is usually best to use a single timestep if possible,\n rather than many smaller steps as is normal for the Newton solver.\n More timesteps will greatly increase the solution time.\n \n\\end_layout\n\n\\begin_layout Itemize\nUnlike the Newton-based solvers,\n prescribed displacements are applied only to the nodes they affect at first and take many iterations to propagate through the adjoining mesh.\n This means that they can often cause excessive deformation of the adjacent elements,\n for example a compressive prescribed displacement can push the surface layer of nodes right through the adjoining elements causing a negative Jacobian error.\n Unlike the BFGS solver,\n where prescribed displacements often give more stable convergence,\n it is better to apply forces where possible and to avoid prescribed displacements.\n \n\\end_layout\n\n\\begin_layout Itemize\nIf prescribed displacements are essential,\n it may be necessary to use smaller timesteps to avoid excessive distortion of the adjoining elements.\n In this case it is possible to slacken the convergence criteria for the intermediate steps and then apply a tighter tolerance for the final step to generate a correct solution.\n \n\\end_layout\n\n\\begin_layout Itemize\nThe preconditioner improves convergence when the stiffness of the nodes varies,\n for example because of different element sizes,\n different materials or features such as quadratic elements with midside nodes.\n For problems where all the elements are the same it offers no great advantage and may even cause slower convergence in some cases.\n It is therefore best to use it for irregular meshes,\n multiple materials,\n quadratic elements and contact problems,\n but for problems with regular meshes where all the elements are a similar size it may be better to turn it off.\n \n\\end_layout\n\n\\begin_layout Itemize\nFor problems with large deformation and displacements the solver may take a very large number of iterations to converge.\n The solver proceeds by many small,\n cautious steps,\n unlike the Newton-based solvers,\n which attempt to jump straight to the final answer on each iteration.\n It is therefore much more reliable but may need thousands or tens of thousands of iterations to reach the solution.\n \n\\end_layout\n\n\\begin_layout Section\nLimitations of FEBio\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Limitations-of-FEBio\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf you run into a problem that you can't seem to solve,\n maybe you ran into a limitation of FEBio.\n This section discusses some important limitations of the software and how they may affect the outcome of your analysis.\n\\end_layout\n\n\\begin_layout Subsection\nGeometrical instabilities\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Geometrical-instabilities\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA geometrical instability is a point in the solution at which several paths for continuing the solution are possible.\n Typical examples are buckling of a column.\n FEBio's Newton based solvers cannot handle buckling since usually at these points,\n the material's elasticity tangent is not uniquely defined.\n Although in some cases FEBio will be able to pass a buckling point,\n in most cases,\n FEBio will not be able to converge to a solution.\n\\end_layout\n\n\\begin_layout Subsection\nMaterial instabilities\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Material-instabilities\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nA material instability is a point in the stress-strain response where the slope of the stress-strain curve effectively becomes zero.\n Since FEBio's Newton based solvers use this slope to advance the solution,\n a zero slope would mean an infinite step and FEBio will not be able to continue.\n\\end_layout\n\n\\begin_layout Standard\nThe slope (or more accurately the elasticity tangent tensor) does not have to be zero to cause problems.\n For materials that soften at large deformations,\n this slope may become so small that it renders the global stiffness matrix ill-conditioned.\n It is unlikely that FEBio will be able to continue and even if it finds a solution,\n it may not be useful.\n\\end_layout\n\n\\begin_layout Subsection\nRemeshing\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Remeshing\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio is designed to solve problems that can undergo large deformations.\n However,\n when the deformations become too large it can happen that the finite element mesh cannot be distorted any further without inverting elements.\n In that case,\n FEBio will not be able to continue the solution and will most likely terminate with a negative jacobian error message.\n\\end_layout\n\n\\begin_layout Standard\nOne possible solution to this problem is to remesh the model at the point at which the deformation become too large,\n but currently FEBio does not have any remeshing capabilities.\n The only remedy at this point is to plan ahead and design your mesh in such a way that the elements will not invert in the range of deformation that you are interested in (e.g.\n place a butterfly mesh in sharp corners or make the mesh finer in areas of larger deformation).\n\\end_layout\n\n\\begin_layout Subsection\nForce-driven Problems\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Force-driven-Problems\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen the primary method of deforming the model is a force,\n the problem is said to be force-driven.\n (Opposed to a displacement-driven model where the deformation is caused by displacement boundary conditions.) Force-driven forces are inherently unstable and FEBio has limited capabilities of handling such problems\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\n As of FEBio 2.0,\n some features are available that may help with this issue,\n but they are still experimental.\n\\end_layout\n\n\\end_inset\n\n.\n The cause of this instability can easily be explained using a simple example.\n Imagine a box that is constrained in all directions except one.\n In the unconstrained direction,\n apply two equal but opposite forces on opposite faces.\n In order to prevent the box from flying away,\n the box must generate stresses which balance the applied forces exactly.\n Unfortunately,\n the numerical solution will only be approximate due to numerical round-off errors inherent in the calculation.\n Even the slightest deviation from balancing the forces exactly will create a net-force which in turn causes the model to fly off in the unconstrained direction (in which a rigid body mode now exists).\n\\end_layout\n\n\\begin_layout Standard\nUsually,\n these problems are circumvented by adjusting the boundary conditions so that rigid body modes cannot exist.\n In the previous example this can be done by only modeling half of the box and enforcing a symmetry boundary condition.\n However,\n in some cases this is not possible.\n This is often the case in force-driven contact problems,\n where the contact interface is necessary to define a well-constrained model.\n When there is an initial separation (or even when the surfaces have no initial overlap) the initial contact force is zero,\n which is equivalent to having no contact enforcement,\n and thus the model is under-constrained.\n Therefore,\n for force-driven contact problems it is important to have some initial contact before starting the analysis.\n\\end_layout\n\n\\begin_layout Subsection\nSolutions obtained on Multi-processor Machines\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Solutions-Multi-Processor\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAlthough technically not a limitation of FEBio it is important that users are aware that in some cases you may get a different convergence history or sometimes even a slightly different answer when you run the same model twice on a multi-processor machine.\n This is caused by the fact that on multi-processor machines the same calculations can be executed in a different order and due to the accumulation of numerical round-off errors may result in slightly different answers.\n The type of problems that are mostly affected by this are problems that are close too being ill-conditioned.\n Also,\n problems that have many time steps or require many iterations for each time step may be affected by this.\n\\end_layout\n\n\\begin_layout Standard\nIf you experience different answers for the same problem,\n try running the model on just one processor (if possible).\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Using-Multiple-Processors\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on how to run FEBio on multi-processor machines.\n\\end_layout\n\n\\begin_layout Section\nWhere to Get More Help\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Where-More-Help\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nWhen you get here,\n you may be ready to pull all your hair out,\n but fret not,\n all is not lost.\n In fact,\n some people may have run into a similar issue and found a solution.\n The first place to find these people is on the FEBio user's forum (\n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nurl{https://forums.febio.org/}\n\\end_layout\n\n\\end_inset\n\n).\n This forum contains hundreds of posts by FEBio users and is monitored by the FEBio developers who will always be more than happy to help you with your problems.\n In addition,\n this forum can be used to report bugs or to request a new feature.\n It is the ideal portal to find help with your problems so don't hesitate to use it!\n\\end_layout\n\n\\begin_layout Chapter\nConfiguration File\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:Configuration-File\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Section\nOverview\n\\end_layout\n\n\\begin_layout Standard\nFEBio requires a configuration file to run.\n The purpose of this file is to store platform-specific settings such as the default linear solver.\n See Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Configuration-file\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on how to use this file.\n This section details the format of this file.\n\\end_layout\n\n\\begin_layout Standard\nThe configuration file uses an xml format.\n The root element must be \n\\shape italic\nfebio_config\n\\shape default\n.\n The required attribute \n\\shape italic\nversion \n\\shape default\nspecifies the version number of the format.\n Currently this value must be set to \n\\begin_inset Quotes eld\n\\end_inset\n\n3.0\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The following elements are defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nParameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\series default\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefault_linear_solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSet the default linear solver for the platform (1) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nimport\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLoad a plugin file (2) \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nset\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefine an alias (3)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nif_debug\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nProcess child tags only for debug versions of FEBio (4)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nif_release\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nProcess child tags only for release versions of FEBio (4)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noutput_negative_jacobians\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOutput a detailed list of inverted elements when negative jacobians are encountered (5)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_model_params\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint the values of load controlled parameters at the start of each time step (6)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshow_warnings_and_errors\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint warnings and errors to the screen (7)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\shape italic\nComments:\n\\end_layout\n\n\\begin_layout Enumerate\nFEBio supports several linear solvers,\n such as Pardiso and Skyline.\n Not all solvers are available for all platforms.\n Only the Skyline solver is available for all platforms.\n As of version 1.2,\n the Pardiso solver is available on all platforms for which FEBio is distributed.\n See section \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Configuring-Linear-Solvers\"\nnolink \"false\"\n\n\\end_inset\n\n on how to configure the linear solvers.\n\\end_layout\n\n\\begin_layout Enumerate\nAs of FEBio 2.0 the user can create and use plugins designed for FEBio.\n These plugins extend the standard capabilities without the need to recompile the FEBio code.\n See Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:FEBio-Plugins\"\nnolink \"false\"\n\n\\end_inset\n\n for more information on using plugins in FEBio.\n \n\\end_layout\n\n\\begin_layout Enumerate\nAs of FEBio 2.5 the configuration file supports aliases which can be used to define plugin paths.\n The \n\\shape italic\nset \n\\shape default\ntag defines an alias.\n The name of the alias is specified via the \n\\shape italic\nname \n\\shape default\nattribute.\n Aliases are accessed using the $(\n\\shape italic\nname\n\\shape default\n) syntax.\n See below for an example.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nif_debug/if_release\n\\emph default\n tags allow users to customize settings based on whether a debug or release version of FEBio is processing the configuration file.\n (Note that the debug version refers to how FEBio was compiled.\n It does not refer to the -g command line option.) The versions of FEBio distributed through the website are release versions.\n A debug version of FEBio can only be obtained by building FEBio with the _DEBUG preprocessor command.\n \n\\end_layout\n\n\\begin_layout Enumerate\nWhen elements become inverted,\n FEBio prints an error message that \n\\begin_inset Quotes eld\n\\end_inset\n\nnegative jacobians\n\\begin_inset Quotes erd\n\\end_inset\n\n are encountered.\n If you wish to see a detailed list of the inverted elements,\n you can set the \n\\emph on\noutput_negative_jacobians\n\\emph default\n flag to 1.\n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nprint_model_params\n\\emph default\n parameter can be used to print values of load-controlled parameters to the screen and log file.\n If disabled,\n the values are not printed to the screen or log file.\n For models that use many load-controlled parameters this can be useful to reduce the amount of output generated by FEBio.\n By default,\n this parameter is on.\n \n\\end_layout\n\n\\begin_layout Enumerate\nThe \n\\emph on\nshow_warnings_and_errors \n\\emph default\nparameter can be used to decide whether or not to print warning and error messages.\n The default value is on.\n \n\\end_layout\n\n\\begin_layout Standard\nAn example configuration file that sets the default linear solver to pardiso,\n defines an alias,\n and loads a plugin.\n\\end_layout\n\n\\begin_layout LyX-Code\n<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\\end_layout\n\n\\begin_layout LyX-Code\n<febio_config version=\"3.0\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <default_linear_solver type=\"pardiso\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <set name=\"PluginsDir\">C:\n\\backslash\npath\n\\backslash\nto\n\\backslash\nplugins</set>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <import>$(PluginsDir)\n\\backslash\nmyplugin.dll</import>\n\\end_layout\n\n\\begin_layout LyX-Code\n</febio_config>\n\\end_layout\n\n\\begin_layout Section\nConfiguring Linear Solvers\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Configuring-Linear-Solvers\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFEBio offers several direct and iterative linear solvers that can be used to solve the linear system of equations of the FE model.\n Users can configure these linear solvers in the configuration file with the \n\\family typewriter\ndefault_linear_solver\n\\family default\n tag.\n The particular linear solver is selected via the \n\\family typewriter\ntype\n\\family default\n attribute.\n The following table lists the possible values,\n the solver type,\n and the supported matrix format.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsolver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nmatrix format\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npardiso\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA sparse direct solver,\n from the MKL library.\n (default)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nS/U\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nskyline\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA sparse direct solver,\n but not as efficient as pardiso.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nS\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmkl_dss\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe DSS solver from the MKL library.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nS/U\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfgmres\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAn implementation of the GMRES algorithm,\n from MKL .\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nU\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncg\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAn implementation of the CG algorithm,\n from MKL .\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nS\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nboomeramg\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAn algebraic multigrid solver,\n from the HYPRE library.\n \n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nS/U\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nschur\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA block based iterative solver.\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIt\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nB\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naccelerate\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApple-specific sparse solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD/It\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nS/U\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\nD=direct solver,\n It=iterative solver,\n S = symmetric format,\n U = unsymmetric format,\n B = block format.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIterative solvers can be configured with preconditioners,\n which attempt to reduce the condition number of the matrix.\n Preconditioning is highly recommended and often necessary for reducing the number of iterations needed by the iterative solver to converge the solution.\n Any of the linear solvers listed above can be used as a preconditioner.\n In addition,\n the following specialized preconditioners are also available.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\npreconditioner\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nmatrix format\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nilu0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIncomplete LU decomposition with no fill-in\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nunsymmetric\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nilut\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIncomplete LU decomposition with custom fill-in\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nunsymmetric\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nichol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIncompletly Cholesky decomposition with no fill-in\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiagonal\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDiagonal preconditioner made from the diagonal of the matrix\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nany\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following sections describe each of these solvers and preconditioners in more detail.\n\\end_layout\n\n\\begin_layout Subsection\nPardiso\n\\end_layout\n\n\\begin_layout Standard\nThe Pardiso solver is an efficient sparse direct linear solver and is the default linear solver.\n FEBio uses the implementation from the MKL library.\n It does not require any configuration parameters.\n It can take symmetric,\n unsymmetric,\n and structurally symmetric sparse matrix formats.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<default_linear_solver type=\"pardiso\"/>\n\\end_layout\n\n\\begin_layout Subsection\nSkyline\n\\end_layout\n\n\\begin_layout Standard\nThe Skyline solver was initially added for users who build their own copy of FEBio and did not have access to third party linear solvers.\n It is a sparse direct solver,\n but not as efficient as Pardiso and not recommended for large problems.\n When using this solver,\n it is highly recommended to set the \n\\family typewriter\noptimize_bw\n\\family default\n flag in the model file's Control section,\n which attempts to minimize the matrix bandwidth.\n It does not require any configuration parameters and only works with symmetric matrices.\n \n\\end_layout\n\n\\begin_layout Subsection\nMKL_DSS\n\\end_layout\n\n\\begin_layout Standard\nThe MKL library also offers another direct solver,\n called DSS (Direct-Sparse-Solver).\n This solver is very promising and appears to perform better for many problems than the pardiso solver.\n Currently,\n in FEBio4,\n pardiso remains the default mostly for backward compatibility reasons,\n but it is often worthwhile to see if the mkl_dss solver performs better.\n \n\\end_layout\n\n\\begin_layout Subsection\nFGMRES\n\\end_layout\n\n\\begin_layout Standard\nThis iterative linear solver,\n from the MKL library,\n implements the GMRES algorithm,\n which is an efficient Krylov-based iterative solution strategy.\n It requires several configuration parameters,\n listed below,\n and only works with unsymmetric matrices.\n In the table below,\n \n\\series bold\nN\n\\series default\n refers to the number of equations.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_iter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe maximum number of iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmin(N,150)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative residual tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1e-6\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nabs_tol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nabsolute residual tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1e-12\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe amount of information printed (0,1,\n or 2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfail_max_iters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIndicates whether reaching max iters is a failure or not (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (true)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npc_left\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe left preconditioner\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npc_right\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe right preconditioner\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nWhen using FGMRES as a preconditioner it is recommended to set the fail_max_iters flag to zero.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nExample 1.\n \n\\series default\nThis example shows how to set up the FGMRES solver with an ILU0 preconditioner.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<default_linear_solver type=\"fgmres\">\n\\begin_inset Newline newline\n\\end_inset\n\n  <max_iter>100</max_iter>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tol>1e-5</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <pc_left type=\"ilu0\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</default_linear_solver>\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nExample 2.\n \n\\series default\nThis example sets up an FGMRES solver with a Schur preconditioner.\n For the Schur system we use pardiso for solving the A-block outside of the Schur complement and approximate the A-block inside the Schur complement with its diagonal.\n This solver requires a block-structured matrix.\n In order to generate the block structure you need to set the \n\\family typewriter\nequation_scheme\n\\family default\n control parameter to 1 in the model input file.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<default_linear_solver type=\"fgmres\">\n\\begin_inset Newline newline\n\\end_inset\n\n <print_level>2</print_level>\n\\begin_inset Newline newline\n\\end_inset\n\n <max_iter>100</max_iter>\n\\begin_inset Newline newline\n\\end_inset\n\n <tol>1e-5</tol>\n\\begin_inset Newline newline\n\\end_inset\n\n <abs_tol>1e-9</abs_tol>\n\\begin_inset Newline newline\n\\end_inset\n\n <pc_left type=\"schur\">\n\\begin_inset Newline newline\n\\end_inset\n\n   <print_level>0</print_level>\n\\begin_inset Newline newline\n\\end_inset\n\n   <do_jacobi>0</do_jacobi>\n\\begin_inset Newline newline\n\\end_inset\n\n   <A_solver type=\"pardiso\"/>\n\\begin_inset Newline newline\n\\end_inset\n\n   <schur_pc>0</schur_pc>\n\\begin_inset Newline newline\n\\end_inset\n\n   <schur_A_solver type=\"diagonal\"/>\n\\begin_inset Newline newline\n\\end_inset\n\n   <schur_solver type=\"fgmres\">\n\\begin_inset Newline newline\n\\end_inset\n\n     <print_level>0</print_level>\n\\begin_inset Newline newline\n\\end_inset\n\n     <max_iter>100</max_iter>\n\\begin_inset Newline newline\n\\end_inset\n\n     <tol>0.05</tol>\n\\begin_inset Newline newline\n\\end_inset\n\n     <fail_max_iters>0</fail_max_iters>\n\\begin_inset Newline newline\n\\end_inset\n\n   </schur_solver>\n\\begin_inset Newline newline\n\\end_inset\n\n  </pc_left>\n\\begin_inset Newline newline\n\\end_inset\n\n </default_linear_solver>\n\\end_layout\n\n\\begin_layout Subsection\nCG\n\\end_layout\n\n\\begin_layout Standard\nThis iterative linear solver,\n from the MKL library,\n implements the Conjugate Gradient algorithm,\n which is an efficient Krylov-based iterative solution strategy for symmetric systems.\n It requires several configuration parameters,\n listed below,\n and only works with symmetric matrices.\n In the table below,\n \n\\series bold\nN\n\\series default\n refers to the number of equations.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_iter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe maximum number of iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmin(N,150)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative residual tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1e-6\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe amount of information printed (0,1,\n or 2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npc_left\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe left preconditioner\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(none)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nBoomerAMG\n\\end_layout\n\n\\begin_layout Standard\nThis solver,\n from the HYPRE library (\n\\begin_inset Flex URL\nstatus open\n\n\\begin_layout Plain Layout\n\nhttps://hypre.readthedocs.io/en/latest/index.html\n\\end_layout\n\n\\end_inset\n\n),\n is an adaptive multigrid solver.\n It often works better as a preconditioner to an iterative solver than as a stand-alone solver.\n \n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"16\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_iter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe maximum number of iterations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n20\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntol\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative residual tolerance\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1e-7\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe amount of information printed [0,1,\n or 2]\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax_levels\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe max number of multigrid levels\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n25\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncoarsen_type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMethod of grid coarsening (1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(library default)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuse_num_funcs\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse dimensionality of solution variable in coarsening\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelax_type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelaxation type (2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninterp_type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ninterpolation type (3)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrong_threshold\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrong threshold\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0.5\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\np_max_elmts\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nP max elements\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnum_sweeps\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of sweeps\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nagg_num_levels\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAggressive number of levels\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNodal option\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndo_jacobi\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDo jacobi preconditioning\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfail_max_iters\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFail on max iterations (4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1 (true)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\ncoarsen_type \n\\emph default\nselects the algorithm for the coarsening phase.\n The following values can be used.\n \n\\begin_inset Newline newline\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"12\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCLJP-coarsening (a parallel coarsening algorithm using independent sets)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nclassical Ruge-Stueben coarsening,\n no boundary treatment (not recommended!)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nclassical Ruge-Stueben coarsening,\n with boundary treatment\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFalgout coarsening\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n7\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCLJP-coarsening (using fix random vector,\n for debugging purposes only)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPMIS-coarsening (might lead to slower convergence)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n9\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPMIS-coarsening (using a fixed random vector ,for debugging purposes only)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHMIS-coarsening\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n11\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\none-pass Ruge-Stueben coarsening (not recommended!)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n21\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCGC coarsening\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n22\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCGC-E coarsening\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\nrelax_type \n\\emph default\nselects the algorithm for the relaxation phase.\n The following values can be used.\n\\begin_inset Newline newline\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"14\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nJacobi\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGauss-Seidel,\n sequential (very slow!)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGauss-Seidel,\n interior points in parallel,\n boundary sequential (slow!)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhybrid Gauss-Seidel or SOR,\n forward solve\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhybrid Gauss-Seidel or SOR,\n backward solve\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhybrid chaotic Gauss-Seidel (works only with OpenMP)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhybrid symmetric Gauss-Seidel or SSOR\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nl1-scaled hybrid symmetric Gauss-Seidel\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n9\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGaussian elimination (only on coarsest level)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n15\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCG (warning - not a fixed smoother - may require RGMRES)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n16\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nChebyshev\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n17\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFCF-Jacobi\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n18\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nl1-scaled Jacobi\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\n\n\\emph on\ninterp_type \n\\emph default\nselects the algorithm for the interpolation phase.\n The following values can be used.\n\\begin_inset Newline newline\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"16\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvalue\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nclassicial modified interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLS interpolation (for use with GSMG)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nclassical modified interpolation for hyperbolic PDEs\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndirect interpolation (with separation of weights)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmultipass interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmultipass interpolation (with separation of weights)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\family sans\n\\series medium\n\\shape up\n\\size normal\n\\emph off\n\\bar no\n\\noun off\n\\color none\nextended+i interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n7\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nextended+i (if no common C neighbor) interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstandard interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n9\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstandard interpolation (with separation of weights)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nclassical block interpolation (for use with nodal systems version only)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n11\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlike 10,\n with diagonalized blocks\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n12\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFF interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n13\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFF1 interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n14\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nextended interpolation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Enumerate\nWhen using this solver as a preconditioner,\n it is recommended to set the fail_max_iters flag to 0.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<default_linear_solver type=\"boomeramg\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max_iter>100</max_iter>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <print_level>3</print_level>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <tol>1e-05</tol>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <max_levels>25</max_levels>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <coarsen_type>-1</coarsen_type>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <use_num_funcs>1</use_num_funcs>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <relax_type>5</relax_type>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <interp_type>6</interp_type>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <strong_threshold>0.9</strong_threshold>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <p_max_elmts>4</p_max_elmts>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <num_sweeps>1</num_sweeps>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <agg_interp_type>0</agg_interp_type>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <agg_num_levels>0</agg_num_levels>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <nodal>0</nodal>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <do_jacobi>0</do_jacobi>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <fail_max_iters>0</fail_max_iters>\n\\end_layout\n\n\\begin_layout LyX-Code\n</default_linear_solver>\n\\end_layout\n\n\\begin_layout Subsection\nSchur\n\\end_layout\n\n\\begin_layout Standard\nThe Schur solver is a linear solver that takes a 2x2 block structured matrix and solves the linear system by decomposing it in its Schur-complement form.\n Thus,\n instead of solving the following system directly,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\n\\left[\\begin{array}{cc}\nA & B\\\\\nC & D\n\\end{array}\\right]\\left[\\begin{array}{c}\nu\\\\\nv\n\\end{array}\\right]=\\left[\\begin{array}{c}\na\\\\\nb\n\\end{array}\\right]\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nit first solves for v,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nSv=b-CA^{-1}a\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nwhere \n\\begin_inset Formula $S=D-CA^{-1}B$\n\\end_inset\n\n is the Schur complement of A.\n Then the Schur solver solves for u,\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Formula \n\\[\nu=A^{-1}\\left(a-Bv\\right)\n\\]\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIn order to use this solver,\n users need to select how they wish to solve the A-block,\n and how to solve the Schur complement.\n For the A-block users can choose any of the linear solvers listed above,\n either direct or iterative.\n For the Schur complement users can only choose a Krylov-based iterative solver (FGMRES or CG) because FEBio does not form the Schur complement explicitly.\n \n\\end_layout\n\n\\begin_layout Standard\nThis solver is often more effective as a preconditioner for FGMRES.\n In this case,\n the A-block and Schur complement only need to be solved approximately.\n See the examples section below for some example configurations.\n This solver requires a block-structured matrix.\n In order to generate the block structure you need to set the \n\\family typewriter\nequation_scheme\n\\family default\n control parameter to 1 in the model input file.\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"8\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_level\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe amount of information printed (0,1,\n or 2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nschur_block\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTake the Schur complement of A (=0) or D (=1)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndo_jacobi\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDo Jacobi preconditioning\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzero_D_block\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIs the D-block zero?\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA_solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe solver for the A-block (outside the Schur complement)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuser specified\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nschur_solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe iterative solver for solving Schur complement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuser specified\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nschur_A_solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe solver for the A block in the Schur complement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(same as A_solver)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nAccelerate\n\\end_layout\n\n\\begin_layout Standard\nThe Accelerate sparse solver is only available on Apple computers (\n\\begin_inset Flex URL\nstatus open\n\n\\begin_layout Plain Layout\n\nhttps://developer.apple.com/documentation/accelerate/sparse_solvers\n\\end_layout\n\n\\end_inset\n\n).\n It uses the native Accelerate framework of Mac OS.\n It may be used as a direct solver or an iterative solver.\n Users should consult the Apple documentation to determine optimal settings for this solver,\n based on their application.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndefault\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprint_condition_number\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrint the matrix condition number\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\niterative\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUse iterative solver\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0 (false)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfactorization\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFactorization method (0 to 6)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nU:5=QR,\n S:4=DLTTPP\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\norder_method\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOrdering algorithm (0 to 2)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nU:2=COLAMD,\n S:0=AMD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\niterative_method\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIterative method (0 to 4)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4=LSMR\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe factorization methods include:\n 0=Cholesky,\n 1=LDLT,\n 2=LDLTUnpivoted,\n 3=LDLTSBK,\n 4=LDLTTPP ,\n 5=QR,\n 6=CholeskyAtA (\n\\begin_inset Flex URL\nstatus open\n\n\\begin_layout Plain Layout\n\nhttps://developer.apple.com/documentation/accelerate/sparsefactorization_t\n\\end_layout\n\n\\end_inset\n\n).\n The ordering algorithms include:\n 0=AMD,\n 1=Metis,\n 2=COLAMD (\n\\begin_inset Flex URL\nstatus open\n\n\\begin_layout Plain Layout\n\nhttps://developer.apple.com/documentation/accelerate/sparseorder_t\n\\end_layout\n\n\\end_inset\n\n).\n The iterative methods include:\n 0=ConjugateGradient,\n 1=GMRES,\n 2=DQGMRES,\n 3=FGMRES,\n 4=LSMR (\n\\begin_inset Flex URL\nstatus open\n\n\\begin_layout Plain Layout\n\nhttps://developer.apple.com/documentation/accelerate/sparse_solvers/sparse_iterative_methods\n\\end_layout\n\n\\end_inset\n\n).\n\\end_layout\n\n\\begin_layout Subsection\nPardiso_64\n\\end_layout\n\n\\begin_layout Standard\nThe pardiso_64 solver is variant of the pardiso solver that can be used for very large problems since it uses 64 bit integers.\n Other than that,\n it is identical to the standard pardiso solver.\n \n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout LyX-Code\n<default_linear_solver type=\"pardiso_64\"/>\n\\end_layout\n\n\\begin_layout Chapter\nFEBio Plugins\n\\begin_inset CommandInset label\nLatexCommand label\nname \"chap:FEBio-Plugins\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs of version 2.0 FEBio supports plugins.\n Plugins are dynamic libraries that extend the capabilities of FEBio at runtime without the need to recompile the entire source code.\n This offers the user a powerful mechanism for extending the default feature set of FEBio with little effort.\n In addition,\n the development of the plugin is independent of the FEBio source code,\n which implies that upgrading to a newer version of FEBio will not require a recompilation of the plugin\n\\begin_inset Foot\nstatus collapsed\n\n\\begin_layout Plain Layout\nWhen upgrading to a newer version of FEBio,\n it may be necessary to recompile the plugin if the plugin interface was changed.\n As long as the plugin remains compatible with the new interface,\n no code changes are required.\n\\end_layout\n\n\\end_inset\n\n.\n Using plugins,\n users can create custom materials,\n boundary conditions,\n loads,\n output variables,\n etc.,\n and even couple FEBio to other codes.\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\begin_inset ERT\nstatus open\n\n\\begin_layout Plain Layout\n\n\n\\backslash\nhref{https://help.febio.org/doxygen/html/index.html}{\n\\backslash\nemph{FEBio Developer's \n\\end_layout\n\n\\begin_layout Plain Layout\n\nManual}}\n\\end_layout\n\n\\end_inset\n\n explains in detail how to create these plugins.\n In this section we outline how to use the plugins with FEBio.\n \n\\end_layout\n\n\\begin_layout Section\n\n\\series bold\nUsing Plugins\n\\end_layout\n\n\\begin_layout Standard\nA plugin is compiled into a dynamic library (dll or dylib) or shared object (so) and contains the compiled code of the library.\n In order to use this plugin,\n you must add the path to the plugin file in the FEBio configuration file (see Chapter\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"chap:Configuration-File\"\nnolink \"false\"\n\n\\end_inset\n\n for a discussion of the configuration file).\n For each plugin,\n an \n\\shape italic\nimport \n\\shape default\nitem has to be added in this configuration file.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\n<import>myplugin.dll</import>\n\\end_layout\n\n\\begin_layout Standard\nYou may need to add the full path to the plugin if FEBio has problems locating the plugin.\n\\end_layout\n\n\\begin_layout LyX-Code\n<import>C:\n\\backslash\npath\n\\backslash\nto\n\\backslash\nmy\n\\backslash\nplugins\n\\backslash\nmyplugin.dll</import>\n\\end_layout\n\n\\begin_layout Standard\nWhen FEBio starts,\n it will first load all the plugins that are defined in the configuration file before it reads the input file.\n This way,\n sections in the input file that require the plugin's capabilities will already be available for use.\n\\end_layout\n\n\\begin_layout Standard\nAn alternative way for loading a plugin is using the \n\\series bold\n-import\n\\series default\n command line option.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n>febio -i input.feb \n\\series bold\n-import myplugin.dll\n\\end_layout\n\n\\begin_layout Standard\nIn this example,\n FEBio will look for the file \n\\emph on\nmyplugin.dll\n\\emph default\n in the current working directory.\n If the plugin file is located in a different folder than the current working directory,\n you must prepend the relative or absolute path to the file.\n Note that if the path contains spaces,\n you need to wrap it in quotes.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n>febio -i input.feb -import \"C:\n\\backslash\npath\n\\backslash\nto\n\\backslash\nmy custom plugin\n\\backslash\nmyplugin.dll\"\n\\end_layout\n\n\\begin_layout Section\nError Messages\n\\end_layout\n\n\\begin_layout Standard\nIf FEBio cannot find a plugin it will print an error message.\n There are several reasons why a plugin fails to load.\n Below is a list of possible error messages and some actions that can be taken to prevent the error.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\emph on\n\\begin_inset Quotes eld\n\\end_inset\n\nFailed to load the file\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n:\n Most likely FEBio cannot find the plugin file specified in the configuration file.\n Make sure that the file name including the path is correct and that there are no spaces at the start or end of the import tag's value string.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\emph on\n\\begin_inset Quotes eld\n\\end_inset\n\nRequired plugin function PluginNumClasses not found\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n:\n The plugin is missing a required function that FEBio needs to communicate with the plugin.\n Notify the developer of the plugin to fix this.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\emph on\n\\begin_inset Quotes eld\n\\end_inset\n\nRequired plugin function PluginGetFactory not found\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n:\n The plugin is missing a required function that FEBio needs to communicate with the plugin.\n Notify the developer of the plugin to fix this.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\emph on\n\\begin_inset Quotes eld\n\\end_inset\n\nInvalid number of classes returned by PluginNumClasses\n\\begin_inset Quotes erd\n\\end_inset\n\n:\n\n\\emph default\n There was a problem with the plugin's initialization.\n Notify the developer of the plugin to fix this.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\emph on\n\\begin_inset Quotes eld\n\\end_inset\n\nRequired plugin function GetSDKVersion not found\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n:\n The plugin is missing a required function that FEBio needs to communicate with the plugin.\n Notify the developer of the plugin to fix this.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\emph on\n\\begin_inset Quotes eld\n\\end_inset\n\nInvalid SDK version\n\\begin_inset Quotes erd\n\\end_inset\n\n\n\\emph default\n:\n The plugin was compiled with a version of the SDK that is not compatible with the current FEBio version.\n This most likely means that the plugin must be rebuilt with a newer version of the SDK.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Newpage pagebreak\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Chapter\n\\start_of_appendix\nHeterogeneous model parameters\n\\end_layout\n\n\\begin_layout Standard\nMany model parameters can be made dependent on the reference coordinates.\n This is useful,\n for instance,\n for defining inhomogeneous materials,\n where some material parameters depend on the spatial position.\n \n\\end_layout\n\n\\begin_layout Standard\nThere are two mechanisms for creating heterogeneous parameters:\n a mathematical expression,\n or a map.\n The type of the parameter is set via the \n\\emph on\ntype \n\\emph default\nattribute,\n which can take on two values.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ntype\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmath\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefine a math parameter via a mathematical expression\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefine a mapped parameter\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconst\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDefine a constant parameter (default)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe two types of parameters are described in more detail in the sections below.\n \n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ntype \n\\emph default\nattribute can be omitted.\n In that case,\n it is assumed to be a const parameter,\n unless the tag's value is not a number.\n In that case,\n it is assumed to be a math parameter.\n \n\\end_layout\n\n\\begin_layout Standard\nFEBio performs parameter validation at the start of an analysis where the values of parameters are checked against the valid ranges as defined in the code.\n However,\n it is important to keep in mind that this validation is only performed on constant parameters.\n For math and map parameters,\n it is up to the user to ensure that all values over the relevant mesh partition are valid.\n \n\\end_layout\n\n\\begin_layout Section\nMath parameters\n\\end_layout\n\n\\begin_layout Standard\nFor math parameters,\n add the type=\n\\begin_inset Quotes erd\n\\end_inset\n\nmath\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute to the parameter's definition.\n The value of the parameter tag can then be any mathematical expression.\n The upper case letters X,\n Y,\n Z,\n are used to denote material coordinates.\n The letter \n\\emph on\nt\n\\emph default\n denotes time.\n The algebraic operators +,\n -,\n *,\n /,\n ^ are supported,\n as well as the list of functions listed in Appendix C.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"my_material\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E \n\\series bold\ntype=\"math\"\n\\series default\n>X+Y+Z</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nYou can also use the component's other parameters inside mathematical expressions.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"my_material\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <density>1.0</density>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E \n\\series bold\ntype=\"math\"\n\\series default\n>2*density+0.1</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe names of maps are also allowed in mathematical expressions.\n For instance,\n assume that a map,\n named \n\\begin_inset Quotes eld\n\\end_inset\n\nE_map\n\\begin_inset Quotes erd\n\\end_inset\n\n is defined in the MeshData section.\n Then the following is a valid mathematical expression.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<E \n\\series bold\ntype=\"math\"\n\\series default\n>2*E_map+X</E>\n\\end_layout\n\n\\begin_layout Section\nMapped parameters\n\\end_layout\n\n\\begin_layout Standard\nFor mapped parameters,\n use the attribute type=\n\\begin_inset Quotes eld\n\\end_inset\n\nmap\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n In this case,\n the value of the parameter is the name of the map that is defined in the MeshData section.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"my_material\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E \n\\series bold\ntype=\"map\"\n\\series default\n>map_E</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Standard\nThe map is defined in the MeshData section.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<ElementData name=\"map_E\" elem_set=\"Part1\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elem lid=\"1\">1.23</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <elem lid=\"2\">4.56</elem>\n\\end_layout\n\n\\begin_layout LyX-Code\n</ElementData>\n\\end_layout\n\n\\begin_layout Standard\nNote that it is also possible to use maps in mathematical expressions.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<material id=\"1\" name=\"my_material\" type=\"neo-Hookean\">\n\\end_layout\n\n\\begin_layout LyX-Code\n  <E type=\"math\">5.0*map_E</E>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <v>0.3</v>\n\\end_layout\n\n\\begin_layout LyX-Code\n</material>\n\\end_layout\n\n\\begin_layout Chapter\nReferencing Parameters\n\\end_layout\n\n\\begin_layout Standard\nThis appendix details how that model parameters can be referenced in the FEBio input files.\n This is used to\n\\end_layout\n\n\\begin_layout Itemize\nwrite model parameters to the output file.\n\\end_layout\n\n\\begin_layout Itemize\nreference model parameters in the parameter optimization input file.\n \n\\end_layout\n\n\\begin_layout Standard\nThe general format follows the following rule:\n\\end_layout\n\n\\begin_layout LyX-Code\nfem.property.property...property.parameter.component\n\\end_layout\n\n\\begin_layout Standard\nProperties and parameters are referenced by tags separated by a periods.\n The first tag must be the \n\\emph on\nfem\n\\emph default\n tag,\n which serves as the name of the model.\n (In future version this name can be user defined,\n but for now is fixed).\n Next,\n the name of a model property must follow.\n The following properties are currently defined:\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nmaterial\n\\series default\n:\n references a material,\n defined in the \n\\emph on\nMaterial \n\\emph default\nsection.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nbc_fixed\n\\series default\n:\n references a fixed bc,\n defined in the \n\\emph on\nBoundary\n\\emph default\n section.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nbc_prescribed\n\\series default\n:\n references a prescribed bc,\n defined in the \n\\emph on\nBoundary\n\\emph default\n section.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nnodal_load\n\\series default\n:\n references a nodal load,\n defined in the \n\\emph on\nLoads\n\\emph default\n section.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nsurface_load\n\\series default\n:\n references a surface load,\n defined in the \n\\emph on\nLoads\n\\emph default\n section.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nbody_load\n\\series default\n:\n references a body load,\n define in the \n\\emph on\nLoads\n\\emph default\n section.\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nrigid_body\n\\series default\n:\n references a rigid body.\n Rigid bodies are defined implicitly via rigid materials.\n \n\\end_layout\n\n\\begin_layout Standard\nAll of these properties define arrays.\n A specific property can be referenced via square brackets.\n For instance,\n to reference the first material in the model,\n do this\n\\end_layout\n\n\\begin_layout LyX-Code\nfem.material[0]\n\\end_layout\n\n\\begin_layout Standard\nAlternatively,\n a property can also be reference by name or by ID.\n In either case,\n use round brackets.\n For instance,\n to reference by ID,\n do\n\\end_layout\n\n\\begin_layout LyX-Code\nfem.material(1)\n\\end_layout\n\n\\begin_layout Standard\nThe ID refers to the \n\\begin_inset Quotes eld\n\\end_inset\n\nid\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute that can be specified for most model components.\n To reference by name,\n \n\\end_layout\n\n\\begin_layout LyX-Code\nfem.material('MyMaterial')\n\\end_layout\n\n\\begin_layout Standard\nNote the single quotes around the material name.\n The name is defined via the \n\\begin_inset Quotes eld\n\\end_inset\n\nname\n\\begin_inset Quotes erd\n\\end_inset\n\n attribute.\n\\end_layout\n\n\\begin_layout Standard\nNote that rigid bodies can only be referenced via their material name.\n\\end_layout\n\n\\begin_layout LyX-Code\nfem.rigidbody('MyRigidMaterial')\n\\end_layout\n\n\\begin_layout Standard\nEach property can have a list of parameters and sub-properties.\n To reference a parameter,\n just end the reference string with the name of the parameter.\n For example,\n\\end_layout\n\n\\begin_layout LyX-Code\nfem.material[0].E\n\\end_layout\n\n\\begin_layout Standard\nSub-properties are referenced similarly.\n For example,\n assume that the first material is a biphasic material that defines the solid property.\n \n\\end_layout\n\n\\begin_layout LyX-Code\nfem.material[0].solid.E\n\\end_layout\n\n\\begin_layout Standard\nIf the parameter itself is an array,\n a specific item in the array is referenced using square brackets.\n For example,\n for an EFD-neo Hookean material,\n \n\\end_layout\n\n\\begin_layout LyX-Code\nfem.material[0].ksi[0]\n\\end_layout\n\n\\begin_layout Standard\nIn cases where the model parameter does not define a scalar it is possible to reference the components of the parameter.\n For instance,\n a vec3 parameter defines an x,\n y,\n and z component.\n\\end_layout\n\n\\begin_layout LyX-Code\nfem.body_load[0].force.x\n\\end_layout\n\n\\begin_layout Standard\nNodal positions can also be referenced using the \n\\emph on\nmesh \n\\emph default\nproperty.\n \n\\end_layout\n\n\\begin_layout LyX-Code\nfem.mesh.node[0].position.x\n\\end_layout\n\n\\begin_layout Standard\nIn certain applications (e.g.\n optimization),\n it is also possible to reference output data,\n i.e.\n data that is normally used in the logfile section of the input file.\n This data is considered read-only.\n For example,\n the following references the 'sx' (xx-stress) of element with id 1.\n \n\\end_layout\n\n\\begin_layout LyX-Code\nfem.element_data('sx',\n 1)\n\\end_layout\n\n\\begin_layout Standard\nAnother example evaluates the enclosed volume of a surface (assuming the surface is closed).\n \n\\end_layout\n\n\\begin_layout LyX-Code\nfem.surface_data('volume',\n 'SomeSurface1')\n\\end_layout\n\n\\begin_layout Chapter\nMath Expression\n\\end_layout\n\n\\begin_layout Standard\nMany model parameters can be defined via mathematical expression.\n This appendix documents the syntax of this capability.\n \n\\end_layout\n\n\\begin_layout Section\nFunctions\n\\end_layout\n\n\\begin_layout Standard\nThe following functions are currently supported.\n \n\\end_layout\n\n\\begin_layout Description\nsin(x) The sine function.\n\\end_layout\n\n\\begin_layout Description\ncos(x) The cosine function\n\\end_layout\n\n\\begin_layout Description\ntan(x) The tangent function\n\\end_layout\n\n\\begin_layout Description\ncsc(x) The cosecans function\n\\end_layout\n\n\\begin_layout Description\nsec(x) The secans function\n\\end_layout\n\n\\begin_layout Description\ncot(x) The cotangent function\n\\end_layout\n\n\\begin_layout Description\nacos(x) The arccosine function\n\\end_layout\n\n\\begin_layout Description\nasin(x) The arcsine function\n\\end_layout\n\n\\begin_layout Description\ncosh(x) The hyperbolic cosine function\n\\end_layout\n\n\\begin_layout Description\nsinh(x) The hyperbolic sine function\n\\end_layout\n\n\\begin_layout Description\ntanh(x) The hyperbolic tangent function\n\\end_layout\n\n\\begin_layout Description\nln(x) The natural logarithm\n\\end_layout\n\n\\begin_layout Description\nlog(x) The logarithm with base 10\n\\end_layout\n\n\\begin_layout Description\nexp(x) The exponential function\n\\end_layout\n\n\\begin_layout Description\nsqrt(x) The square root function\n\\end_layout\n\n\\begin_layout Description\nabs(x) The absolute value function\n\\end_layout\n\n\\begin_layout Description\nsgn(x) The sign function\n\\end_layout\n\n\\begin_layout Description\nH(x) The Heaviside function (x = 0.5 at zero)\n\\end_layout\n\n\\begin_layout Description\nstep(x) The right-continuous unit step function \n\\end_layout\n\n\\begin_layout Description\nJ0(x) Bessel function of the first kind,\n order 0\n\\end_layout\n\n\\begin_layout Description\nJ1(x) Bessel function of the first kind,\n order 1\n\\end_layout\n\n\\begin_layout Description\nJn(x,n) Bessel function of the first kind,\n order \n\\emph on\nn\n\\end_layout\n\n\\begin_layout Description\nY0(x) Bessel function of the second kind,\n order 0\n\\end_layout\n\n\\begin_layout Description\nY1(x) Bessel function of the second kind,\n order 1\n\\end_layout\n\n\\begin_layout Description\nYn(x,n) Bessel function of the second kind,\n order \n\\emph on\nn\n\\end_layout\n\n\\begin_layout Description\nsinc(x) The \n\\emph on\nsinc\n\\emph default\n function (i.e.\n sin(x)/x) \n\\end_layout\n\n\\begin_layout Description\nfac(n) The factorial of \n\\emph on\nn\n\\end_layout\n\n\\begin_layout Description\nerf(x) The \n\\emph on\nerror\n\\emph default\n function\n\\end_layout\n\n\\begin_layout Description\nerfc(x) The complementary error function (erfc(x) = 1 - erf(x))\n\\end_layout\n\n\\begin_layout Description\ntgamma(x) The \n\\emph on\nGamma\n\\emph default\n function\n\\end_layout\n\n\\begin_layout Description\natan2(x,y) The two-parameter arctangent function\n\\end_layout\n\n\\begin_layout Description\npow(x,y) The y-th power of x\n\\end_layout\n\n\\begin_layout Description\nTn(n,x) The Chebyshev polynomial of order \n\\emph on\nn\n\\end_layout\n\n\\begin_layout Description\nbinomial(n,m) The binomial of (n,m)\n\\end_layout\n\n\\begin_layout Description\nmax(x1,...,xn) Returns the maximum value of the values listed\n\\end_layout\n\n\\begin_layout Description\nmin(x1,...,xn) Returns the minimum value of the values listed\n\\end_layout\n\n\\begin_layout Description\navg(x1,...,xn) Returns the average value of the values listed\n\\end_layout\n\n\\begin_layout Section\nConstants\n\\end_layout\n\n\\begin_layout Standard\nThe following predefined constants are supported:\n\\end_layout\n\n\\begin_layout Description\npi The value of pi ( = 3.1415926535897932385)\n\\end_layout\n\n\\begin_layout Description\ne The value of exp(1) (= 2.7182818284590452354)\n\\end_layout\n\n\\begin_layout Description\ngamma The Euler-Mascheroni constant (= 0.57721566490153286061)\n\\end_layout\n\n\\begin_layout Description\n_inf_ A very large number (= 1e308)\n\\end_layout\n\n\\begin_layout Chapter\nFEBio Binary Database Specification\n\\end_layout\n\n\\begin_layout Section\nIntroduction\n\\end_layout\n\n\\begin_layout Standard\nThis appendix describes the structure of the FEBio binary database (more commonly referred to as the FEBio plot file),\n which stores the results of a FEBio analysis.\n The FEBio binary database format is both self-describing and extendable.\n A user can understand the contents of the file by simply parsing the block structure of the file.\n The file format is made extensible by providing an abstract layer between the data and the file system.\n This layer defines the file structure as a hierarchy of data blocks.\n The result is that each file is now structured similarly as a file folder.\n Each block can be viewed as a file in the folder and each branch in the hierarchy as a sub-folder.\n\\end_layout\n\n\\begin_layout Standard\nThe database consists of four parts:\n \n\\end_layout\n\n\\begin_layout Enumerate\nthe \n\\series bold\nheader \n\\series default\ncontains some general info that may be useful for parsing the rest of the file.\n \n\\end_layout\n\n\\begin_layout Enumerate\nthe \n\\series bold\ndictionary \n\\series default\npresents a textual description for each data field in the file.\n This can be used to identify the contents of each data field.\n \n\\end_layout\n\n\\begin_layout Enumerate\nthe \n\\series bold\nmesh section \n\\series default\ndefines the mesh of the model.\n \n\\end_layout\n\n\\begin_layout Enumerate\nthe \n\\series bold\nstate sections \n\\series default\ncontain the actual data or results for the field variables.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following sections describe the details of the database format.\n In the next section,\n the block-structure of the file is explained.\n The sections thereafter describe the different parts of the database.\n \n\\end_layout\n\n\\begin_layout Subsection\nChanges in this version\n\\end_layout\n\n\\begin_layout Standard\nThis appendix describes version 3.0 of the binary database format.\n This version of the FEBio Binary Database Specification differs in a few important aspects from the previous version.\n It addresses the following issues:\n \n\\end_layout\n\n\\begin_layout Standard\n• Reduce plot file size by describing rigid bodies via rigid body mechanics.\n \n\\end_layout\n\n\\begin_layout Standard\n• Reduce plot file size by identifying non-mutable data,\n which is data that that does not change in different states.\n \n\\end_layout\n\n\\begin_layout Standard\n• Add support for more generic “objects” that may not be represented via the mesh.\n In FE simulations,\n additional structures can be present that interact with the FE parts,\n but are themselves not part of the FE mesh.\n Examples are rigid bodies and the various mechanisms to connect rigid bodies (e.g.\n springs,\n joints,\n etc.).\n \n\\end_layout\n\n\\begin_layout Standard\n• The State section was modified so that the position of non-rigid nodes is redefined in each section.\n \n\\end_layout\n\n\\begin_layout Section\nBlock Structure\n\\end_layout\n\n\\begin_layout Subsection\nOverview\n\\end_layout\n\n\\begin_layout Standard\nThe FEBio binary database uses an abstract layer to communicate with the file system.\n This achieves two goals.\n The first is that the content of a file becomes independent of the file system that wrote the file.\n Big endian systems can read files created with small endian systems and vice versa.\n The second goal is that the data is now stored in a hierarchical structure which is easy to search and modify.\n This means that future additions and changes can be made fairly easily without losing backward compatibility.\n In addition,\n the self-describing feature of the format even allows for some forward compatibility.\n\\end_layout\n\n\\begin_layout Standard\nAs mentioned above,\n the file is structured as a hierarchy of blocks.\n Each block consists of three fields:\n a DWORD identifier,\n followed by a DWORD containing the size of the data chunk in bytes and then the actual data.\n (A DWORD is a “double word” meaning an unsigned integer of 4 bytes.)\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/FigAppendixD_fig1.png\n\n\\end_inset\n\n\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nEach block in the plot file consists of three fields:\n a DWORD with an identifier,\n a DWORD containing the size of the block and finally the data of the block,\n which can be child blocks.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThis data may be either numeric data (such as the data of a field variable) or child blocks.\n If the block has children,\n it is referred to as a \n\\emph on\nbranch\n\\emph default\n.\n If the block only contains numeric data,\n it is referred to as a \n\\emph on\nleaf\n\\emph default\n.\n\\end_layout\n\n\\begin_layout Subsection\nParsing the FEBio plot file\n\\end_layout\n\n\\begin_layout Standard\nAlthough the FEBio plot file in essence is a hierarchy of blocks as described above,\n there are a few more caveats that are important for parsing the FEBio file.\n \n\\end_layout\n\n\\begin_layout Standard\nThe first DWORD of the file is a tag that identifies the FEBio plot file.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFEBio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x00464542\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFEBio identifier tag\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe first tag of the plot file is an identifier that can be used to see if the file is indeed an febio plot file.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nParsers should read this number and use it to identify whether the file is indeed a proper FEBio plot file.\n If the value differs,\n then that means that the file is either not a valid plot file,\n or that the endianness of the system that wrote the file is different than that of the system that is reading the file.\n In the latter case,\n a byte swap will be necessary when reading data from the file.\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\n\\emph on\nNOTE:\n Note that the FEBio tag is not followed by a size tag.\n The reason is that when FEBio is writing the file it does not know the length of the final file yet.\n\\end_layout\n\n\\begin_layout Standard\nAfter the FEBio tag,\n the content of the file follows,\n organized in the hierarchical block structure described above.\n At the highest level,\n the plot file has a single root block,\n followed by a mesh block and a series of state blocks.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTAG\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nROOT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01000000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRoot block of FEBio plot file\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMESH\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01040000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMesh block containing the mesh definition\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTATE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02000000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nState block containing results of a single time step\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe overall structure of the plot file consists of a root section,\n a mesh section,\n and multiple state sections.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAfter the root block,\n the mesh section follows which defines the mesh of the model.\n Then,\n at least one state blocks follow.\n There will be one state block for each time step in the FEBio plot file.\n Note that these blocks are not child blocks of the root block.\n In the following figure,\n the high-level structure of the FEBio plot file is depicted for a file that has two state blocks.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/FigAppendixD_fig2.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nExample structure for a plot file containing a root section and two data sections.\n Each section is composed of three fields.\n A DWORD containing the section ID,\n followed by a DWORD containing the size of the section block and finally the actual data (which may be child blocks).\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs explained above,\n the first DWORD will be the FEBio identifier.\n The first block in the file will always be the ROOT block,\n which will contain the header and dictionary.\n Then,\n the mesh section follows,\n which defines the mesh.\n After that,\n the state blocks follow,\n which will contain the actual results.\n Each block has three fields.\n The first field is a DWORD,\n identifying the section.\n For the first block,\n this will be ROOT,\n for the second block this will be MESH,\n and for the other blocks this will be STATE.\n The next DWORD is the size of the entire block.\n Finally,\n the actual data will follow,\n which for the ROOT,\n MESH and STATE blocks will contain child blocks.\n\\end_layout\n\n\\begin_layout Section\nRoot Section\n\\end_layout\n\n\\begin_layout Standard\nThe ROOT section is the first block in the file.\n This block contains the \n\\emph on\nheader\n\\emph default\n and \n\\emph on\ndictionary\n\\emph default\n sections as child blocks.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEADER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01010000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontains the header section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDICTIONARY\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontains the dictionary section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Root section has two child sections,\n the \n\\emph on\nheader \n\\emph default\nand the \n\\emph on\ndictionary \n\\emph default\nsections.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThese sections will be detailed below.\n\\end_layout\n\n\\begin_layout Subsection\nHeader Section\n\\end_layout\n\n\\begin_layout Standard\nThe first section of the root block is the header section.\n It stores the following data:\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVERSION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01010001\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nversion of the file format\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCOMPRESSION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01010004\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCompression flag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAUTHOR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01010005\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAuthor name\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSOFTWARE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01010006\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSoftware that generated this file\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Header Section\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nCurrently,\n the VERSION will always be 0x0008.\n The COMPRESSION flag indicates whether the mesh and state sections are compressed or not.\n Data compression is an optional feature for plot files,\n and will not be discussed in this document.\n The AUTHOR contains the name of the person who created the file.\n The SOFTWARE tag contains the name of the software that generated the file.\n (Note that not all header data might be present in a plot file.\n For instance,\n as of FEBio3,\n the AUTHOR tag is not written to the plot file.) \n\\end_layout\n\n\\begin_layout Subsection\nDictionary Section\n\\end_layout\n\n\\begin_layout Standard\nWhen running a FEBio analysis,\n FEBio will store the values of certain user-selected variables (e.g.\n stress,\n temperature,\n fluid pressure,\n etc.) to the plot file.\n In order for a parser to know which variables were written,\n it needs to read the dictionary section.\n This section stores a list of variables,\n including their names,\n which are stored in the plot file.\n Specifically,\n three attributes are stored for each data variable:\n a name that provides a description of the data,\n the data type (scalar,\n vector,\n tensor) and the storage format.\n In addition,\n the variables are grouped by category.\n Data variables can be defined for node sets,\n domains (element sets) and surfaces.\n In addition,\n global variables (which are not associated with any part of the model) can also be defined.\n Each of these categories has their own sub-section in the dictionary.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGLOBAL_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01021000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlists global data in model\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODESET_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01023000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlists data associated with the node sets\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDOMAIN_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01024000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlists data associated with domains\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSURFACE_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01025000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlists data associated with surfaces\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Dictionary Section\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNote that all of these sections are optional.\n For example,\n if a model does not define global data,\n that section will not be part of the plot file.\n\\end_layout\n\n\\begin_layout Standard\nEach of these sub-sections defines a list of dictionary items defined by the following tag.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDICTIONARY_ITEM\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020001\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of dictionary item\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe dictionary consists of \n\\emph on\ndictionary items\n\\emph default\n.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach dictionary item contains three fields.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nITEM_TYPE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020002\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntype of data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nITEM_FORMAT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020003\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstorage format of data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nITEM_NAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020004\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntextual description of data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nITEM_ARRAY_SIZE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020005\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSize of array variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nITEM_ARRAY_NAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01020006\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nName of array variable\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nStructure of dictionary item.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe ITEM_ARRAY_SIZE and ITEM_ARRAY_NAME are only used for array variables.\n The ITEM_ARRAY_SIZE defines the number of data items in the array.\n For each data item,\n the ITEM_ARRAY_NAME defines an optional name for that item.\n \n\\end_layout\n\n\\begin_layout Standard\nThe type of the data can be any of the following values.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"9\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nITEM_TYPE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nVALUE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFLOAT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsingle precision (s.p.) floating point\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3F\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3D vector of s.p.\n floats (stored in x,\n y,\n z order)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3FS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric 2nd order tensor of s.p.\n floats (stored in xx,\n yy,\n zz,\n xy,\n yz,\n xz order)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3FD\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiagonal matrix of s.p.\n floats (stored in xx,\n yy,\n zz order).\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTENS4FS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsymmetric fourth-order tensor of s.p.\n floats.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3F\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3x3 matrix of floats (stored in row order).\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nARRAY_FLOAT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nArray of floats.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nARRAY_VEC3F\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n7\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nArray of vec3f.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe supported data types.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs explained below,\n data will be stored for different regions of the mesh,\n where a region can be a node set,\n a surface,\n or a domain (element set).\n A region is composed of items where an item is a single shape in the region.\n For node sets,\n an item will refer to a node,\n for surfaces this will be a facet and for domains an item will refer to an element.\n The storage format defines how many values are written for each region and how the values relate to the geometry of the region or items.\n The following values are currently defined.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nITEM_FORMAT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nVALUE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\none value for each node of the region\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nITEM\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\none value for each item\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMULT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\none value for each node for each item\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe different storage format identifiers.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe easiest format to understand is the ITEM format which simply stores one value for each item of the region.\n Thus,\n one value for each node or for each facet or for each element,\n depending on the region type.\n The MULT format defines a value for each node of each item.\n For example,\n for a surface of quads,\n the data will contain four values for each facet,\n one for each of the four nodes.\n The NODE format stores a single value for each node of the region.\n Each type of region (node set,\n surface,\n or domain) implicitly also defines a set of nodes,\n namely all the nodes that are part of that region.\n With the NODE format,\n the user defines a single value for each of the nodes in this implicit node set.\n This will be explained in more detail below in the state section.\n \n\\end_layout\n\n\\begin_layout Standard\nNote that the storage formats are only important for surfaces and domains.\n For other categories (e.g.\n node sets),\n all formats are essentially equivalent,\n and can safely be ignored.\n \n\\end_layout\n\n\\begin_layout Section\nMesh Section\n\\end_layout\n\n\\begin_layout Standard\nThe Mesh section defines the mesh of the model and its decomposition into separate regions.\n A region can be a node set,\n a surface (i.e.\n facet set),\n or a domain (element set).\n The mesh section is defined by the following sub-sections.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODE_SECTION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01041000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of node section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDOMAIN_SECTION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of domain section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSURFACE_SECTION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of surface section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODESET_SECTION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of node set section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPARTS_SECTION\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01045000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of parts section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe child sections of the \n\\emph on\nMesh \n\\emph default\nsection.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nNode Section\n\\end_layout\n\n\\begin_layout Standard\nThe NODE section defines a set of nodes of the mesh.\n Each node section starts with a NODE_HEADER followed by a NODE_COORDS section.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODE_HEADER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01041100\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnode header\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODE_COORDS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01041200\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnode data list\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Node section consists of a header and a list of coordinates.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe header contains the following information.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01041101\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnumber of nodes (N) in this NODE section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDIM\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01041102\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndimension of node set (i.e.\n nr.\n of coords)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01041103\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nname of node set.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe header of the Node section.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe NODES parameter defines the number of nodes defined in this section.\n The DIM parameter sets the number of coordinates that will be defined for each node (e.g.\n 3 for 3D problems).\n Each NODE section also implicitly defines a node set.\n The NAME attribute defines the name of this node set.\n\\end_layout\n\n\\begin_layout Standard\nThen the NODE_COORDS section follows,\n which defines the nodal data for each node.\n This data field stores the nodal IDs and coordinates for all the nodes in the mesh.\n The size of the field is defined by N*DWORD + DIM*N*FLOAT where N is the number of nodes in the mesh (as defined in the header section),\n DIM is the dimension,\n and FLOAT is the size of a single precision floating point number (4 bytes).\n The order of the data is (e.g.\n if DIM equals 3),\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"1\" columns=\"9\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nID1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n...\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIDn\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxn\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyn\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzn\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n x[i] is the x-coordinate of node i and similarly for y and z.\n \n\\end_layout\n\n\\begin_layout Subsection\nDomain Section\n\\end_layout\n\n\\begin_layout Standard\nThe Domain section lists all domains (i.e.\n element sets) in the mesh.\n Each domain is identified by a DOMAIN section.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDOMAIN\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042100\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of a domain section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Domain section consists of a list of domains.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach domain is defined by two sub-sections,\n a \n\\emph on\ndomain header\n\\emph default\n and an \n\\emph on\nelement list\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDOMAIN_HEADER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042101\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of domain header\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nELEMENT_LIST\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042200\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlist of element connectivity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nEach domain section consists of a header section and an element list.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ndomain header\n\\emph default\n contains the following data fields.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nELEM_TYPE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042102\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPART_ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042103\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npart ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nELEMENTS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01032104\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnumber of elements\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01032105\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nan optional name for this domain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe domain header section.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe ELEM_TYPE defines the type of elements stored in the domain.\n It can have one of the following values.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"11\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nELEM_TYPE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nVALUE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEX8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8-node hexahedron solid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPENTA6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6-node pentahedron solid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4-node tetrahedron solid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nQUAD4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4-node quadrilateral shell element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTRI3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n4\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3-node triangular shell element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTRUSS2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n5\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2-node linear truss element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEX20\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n6\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n20-node quadratic hexahedral element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET10\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n7\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n10-node quadratic tetrahedral element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTET15\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n8\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n15-node quadratic tetrahedral element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHEX27\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n9\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n27-node quadratic hexahedral element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe supported element types.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe PART_ID corresponds to the ID of one of the parts defined in the Parts section.\n \n\\end_layout\n\n\\begin_layout Standard\nThe ELEMENTS field is the number of elements in the domain.\n\\end_layout\n\n\\begin_layout Standard\nAfter the domain header the element list follows.\n For each element it defines an ELEMENT data field.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nELEMENT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01042201\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement ID and connectivity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD*(NE+1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nEach element is defined by an ID and a list of nodes that defines the connectivity.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nIf NE is the number of nodes per element,\n then this data field stores NE+1 DWORDS.\n The first DWORD is the element ID,\n a unique number that identifies the element.\n The following NE DWORD’s define the element connectivity.\n \n\\end_layout\n\n\\begin_layout Subsection\nSurface Section\n\\end_layout\n\n\\begin_layout Standard\nThe surface section defines the surfaces of the mesh for which data is stored in the plot file.\n Each surface begins with a SURFACE section.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSURFACE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043100\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of a surface section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Surface section consists of a list of surface definitions.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe surface section follows a similar structure as the domain section,\n namely a\n\\emph on\n surface header\n\\emph default\n followed by a \n\\emph on\nfacet list\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSURFACE_HEADER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043101\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of surface header\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFACET_LIST\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043200\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfacet connectivity list\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nEach surface is defined by a header section and a facet list.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe surface header contains the following data fields.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSURFACE_ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043102\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsurface ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFACETS\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043103\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of facets\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043104\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nA name for this surface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAX_FACET_NODES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043105\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax nodes per facet\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe surface header section.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe SURFACE_ID is a unique identifier and FACETS is the number of facets in the surface.\n\\end_layout\n\n\\begin_layout Standard\nThe FACET_LIST follows the header and contains a FACET data field for each facet in the surface.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFACET\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01043201\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfacet definition\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD*NF\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nA facet definition.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nHere,\n NF = MAX_FACET_NODES + 2.\n The first DWORD is a unique identifier (which currently should be ignored).\n The second DWORD is the number of nodes for this facet.\n This also defines the facet type.\n (For instance,\n 3 nodes define a triangle,\n 4 nodes define a quadrilateral).\n Next,\n the node ID’s follow.\n \n\\end_layout\n\n\\begin_layout Subsection\nNode Set Section\n\\end_layout\n\n\\begin_layout Standard\nThe Node Set section defines all the node sets where a node set is a named collection of nodes.\n Each nodeset begins with the NODESET section.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODESET\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044100\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of a nodeset section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Node Set section consists of a list of NODESET sections.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nNext,\n a header section and a node list section follow.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODESET_HEADER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044101\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of nodeset header\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODE_LIST\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044200\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnode list\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nEach nodeset consists of a header and a list of node numbers.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe header is composed of the following chunks.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODESET_ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044102\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodeset ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODES\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044104\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of nodes\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01044103\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAn optional name for this nodeset\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe node set header.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe node list is a list of all the nodes that belong to this node set.\n All node indices are zero-based.\n\\end_layout\n\n\\begin_layout Subsection\nParts Section\n\\end_layout\n\n\\begin_layout Standard\nThe Parts section lists the parts defined in the plot file.\n For each part,\n a PART section is written.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPART\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01045100\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nbeginning of a part definition\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe Part section consists of a list of parts.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThen,\n for each part the following fields are written.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPART_ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01045101\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nID of part\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPART_NAME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x01045102\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nName of part\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCHAR64\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nA part definition.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe ID is a unique number that will be used in the domain definitions to refer to this part.\n The NAME is a textual description that can be used by the post-processor to present the part to the user.\n\\end_layout\n\n\\begin_layout Section\nState Section\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nstate\n\\emph default\n section is where all the actual data is stored.\n FEBio will store one state section for each time step in the analysis.\n Each state defines two sub-sections,\n namely the \n\\emph on\nstate header\n\\emph default\n and the \n\\emph on\nstate data\n\\emph default\n.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTATE_HEADER\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02010000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstate header section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTATE_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020000\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstate data section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe State section consists of a header and a data section.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nState Header\n\\end_layout\n\n\\begin_layout Standard\nThe STATE_HEADER contains the following data field.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTATE_TIME\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02010002\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ntime stamp of state\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFLOAT\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nThe state header section.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nState Data Section\n\\end_layout\n\n\\begin_layout Standard\nThe STATE_DATA section stores the data for this state.\n It is composed of several sub-sections where each section corresponds to a data category as defined in the dictionary.\n The following sub-sections can thus be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"5\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nGLOBAL_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020100\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefine global data section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNODE_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020300\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefine nodal data section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDOMAIN_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020400\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefine domain data section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSURFACE_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020500\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefine surface data section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nEach data section is composed of several child sections.\n \n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\series bold\nNote that FEBio currently doesn’t write global data sections.\n\\end_layout\n\n\\begin_layout Standard\nEach of these sub-sections follows a similar structure.\n For each of the variables defined in the dictionary corresponding to the data category,\n a STATE_DATA section is defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"3\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSTATE_DATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020001\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndefines a data section\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nState Data section.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach state data section has two fields.\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float table\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"3\" columns=\"4\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"center\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nTag\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndescription\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsize\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nREGION_ID\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020002\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nID of region\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDWORD\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDATA\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n0x02020003\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactual data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n?\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nState data sub-sections.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe REGION_ID refers to the ID of the region for which this data variable is defined.\n\\end_layout\n\n\\begin_layout Standard\nIt is important to note that for node sets,\n a value of zero for the REGION_ID refers to the “master” node set which is the set containing all the nodes in the model.\n This node set is defined implicitly and will not be part of the list of node sets.\n\\end_layout\n\n\\begin_layout Standard\nThe DATA field contains the actual data.\n The size of this field is variable and is determined by the data type and storage format as defined in the dictionary as well as the number of items in the corresponding region.\n For example,\n for a domain that has NE elements,\n the size of the DATA field,\n using a type of FLOAT and a storage format of FMT_ITEM will be NE*FLOAT.\n \n\\end_layout\n\n\\begin_layout Standard\nWhen a surface or domain stores its data in the FMT_NODE format,\n then the parser needs to figure out how many nodes are implicitly defined by the region and what the node order is.\n An implicit nodeset can be constructed by simple enumeration:\n each item of the region lists the nodes its visits and no nodes can be visited more than once.\n So for example,\n consider the surface of three faces shown in the figure below.\n \n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset Float figure\nplacement document\nalignment document\nwide false\nsideways false\nstatus open\n\n\\begin_layout Plain Layout\n\\begin_inset Graphics\n\tfilename Figures/FigAppendixD_fig3.png\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\\begin_inset Caption Standard\n\n\\begin_layout Plain Layout\nExample illustrating enumeration algorithm that defines the node ordering for the implicit node set defined by a surface.\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nEach facet has four nodes.\n The first four nodes of the implicit node set are simply the four nodes of the first element.\n The second element skips its first node,\n since it is already visited,\n add its second node (which becomes node 5) and its third (node 6) and skips its fourth node.\n Similarly,\n the third element adds its second (node 7) and third (node 8) and skips its third and fourth.\n Thus,\n this surface defines an implicit node set containing 6 nodes in the order as shown in figure 3.\n Consequently,\n if this surface stores its data in FMT_NODE format,\n then the corresponding data section will contain six values,\n one for each of the nodes in the implicit node set.\n\\end_layout\n\n\\begin_layout Chapter\nFEBio Output\n\\end_layout\n\n\\begin_layout Standard\nFEBio supports two main data output mechanisms:\n the plot file and the log file.\n The plot file is a binary format that stores the data in format that makes it easy to process with FEBio Studio.\n The log file uses a text format that is more convenient for processing data further via scripting.\n See \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Output-Section\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n to learn more about these features.\n This appendix list the variables that are supported by these two output formats.\n \n\\end_layout\n\n\\begin_layout Section\nPlotfile Variables\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Plotfile-Variables\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThere are four categories of plot file variables:\n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nNodal:\n\n\\series default\n These typically correspond to data that is directly available at nodes.\n For instance,\n the primary solution variables are usually stored as nodal variables.\n (E.g.\n \n\\begin_inset Quotes eld\n\\end_inset\n\ndisplacement\n\\begin_inset Quotes erd\n\\end_inset\n\n) \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nElement:\n\n\\series default\n These quantities correspond to data that is typically stored on the elements (e.g.\n stress).\n In FEBio,\n element data is usually stored at the integration points during the solution phase.\n Since this is often not a convenient format for post-processing,\n element data is processed before export and will be written to the plot file as either a constant,\n element-averaged value,\n or projected to the nodes of the element.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nSurface:\n\n\\series default\n These quantities correspond to data that is only available at the surface of the mesh (e.g.\n contact traction).\n If the surface data is defined at the nodes of the surface,\n it will be written directly to the plot file.\n However,\n if the data is associated with the surface facets - or more accurately,\n with the integration points of the surface facet - then it will be processed before export,\n and a constant,\n facet-averaged value,\n will be written to the plot file.\n \n\\end_layout\n\n\\begin_layout Itemize\n\n\\series bold\nRigid body:\n\n\\series default\n These are quantities that correspond to data stored on rigid bodies.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following table lists the possible values for the \n\\emph on\ntype\n\\emph default\n attribute of the plot file variable tag.\n \n\\end_layout\n\n\\begin_layout Standard\nAn asterisk (*) in the Category column denotes that the attribute is required and must be included in the definition of the plot variable.\n \n\\end_layout\n\n\\begin_layout Standard\nThe data type column specifies the type of the data.\n The following table lists the available data types.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"center\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nData Type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na single scalar value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na three-component vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na 3x3 matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3D\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na diagonal 3x3 matrix (only diagonal components are stored)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na symmetric 3x3 matrix (only 6 components are stored)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTENS4S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\na 4th order tensor with both major and minor symmetries.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe following table lists all the available plot variables.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"184\" columns=\"4\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"2in\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\">\n<column alignment=\"left\" valignment=\"top\" width=\"3in\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nType\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nCategory\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nData Type\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nacceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage element accelerations\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAlmansi strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAlmansi strain tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nconcentration gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe concentration gap across a biphasic contact interface.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact area\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNet contact area across contact interface.\n Evaluated by integrating the surface area at integration points where the contact pressure is not zero.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNet contact force across contact interface.\n Evaluated by integrating the contact traction over the contact surface.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact gap distance,\n evaluated at contact surface faces\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact penalty\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact penalty value\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact pressure,\n evaluated at contact surface faces\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact status\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe nr.\n of integration points of the surface element that are considered in contact.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact stick\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStick status in stick-slip frictional contact (1=stick,\n 0=slip).\n Values between 0 and 1 imply that a fraction of that face is in stick mode.\n Currently works only with sliding-tension-compression.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact traction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact traction vectors,\n evaluated at contact surface faces\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElectric current density,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-electric-current-density\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent element angular momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's total angular momentum at the current configuration.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent element center of mass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's center of mass at the current configuration.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent element kinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's total kinetic energy at the current configuration.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent element linear momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's total linear momentum at the current configuration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent element strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's total strain energy at the current configuration.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndamage\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDamage variable \n\\begin_inset Formula $D$\n\\end_inset\n\n (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Reactive-Damage-Mechanics\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndensity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe current density\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndeviatoric elasticity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTENS4S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe deviatoric elasticity tensor of an uncoupled material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndeviatoric strain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDeviatoric strain energy density \n\\begin_inset Formula $\\tilde{\\Psi}\\left(\\mathbf{\\tilde{C}}\\right)$\n\\end_inset\n\n,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq1\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndeviatoric strong bond SED\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain energy density of strong bonds in reactive viscoelastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndeviatoric weak bond SED\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain energy density of weak bonds in reactive viscoelastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndilatation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngradient of J in biphasic-FSI\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element direction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUnit vector along direction of discrete element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element percent elongation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nratio of change in length over original length\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element signed force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe dot product of the force and the element's direction vector.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrain energy of the discrete element's material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndisplacement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNodal displacements\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective elasticity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTENS4S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe elasticity of the elastic material in a biphasic/triphasic/multiphasic material.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective fluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid pressure for biphasic,\n biphasic-solute,\n triphasic,\n and multiphasic materials.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective shell fluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid pressure for biphasic,\n biphasic-solute,\n triphasic,\n and multiphasic materials of shell domains.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective shell solute concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEffective solute concentration for biphasic-solute,\n triphasic,\n and multiphasic materials of shell domains.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective solute concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEffective solute concentration for biphasic-solute,\n triphasic,\n and multiphasic materials.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective fluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n for biphasic materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bp-stress\"\nnolink \"false\"\n\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\tilde{p}$\n\\end_inset\n\n for biphasic-solute,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bs-effective-p-c\"\nnolink \"false\"\n\n\\end_inset\n\n and triphasic/multiphasic materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective friction coefficient\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEffective friction coefficient at a biphasic contact interface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective solute concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEffective solute concentration \n\\begin_inset Formula $\\tilde{c}$\n\\end_inset\n\n for biphasic-solute materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bs-effective-p-c\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and triphasic/multiphasic materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelasticity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTENS4S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpatial elasticity tensor components\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelectric potential\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElectric potential \n\\begin_inset Formula $\\psi$\n\\end_inset\n\n in triphasic/multiphasic materials,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"sec:Triphasic-Multiphasic-Materials\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement angular momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement total angular momentum\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement center of mass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement center of mass\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement kinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement total kinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement linear momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement total linear momentum\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement total strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nelement stress power\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement total stress power\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nenclosed volume\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVolume enclosed by named surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nenclosed volume change\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nChange in volume enclosed by named surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEuler angle\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body Euler angles\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfacet area\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe area of the surface element in the deformed configuration.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfatigue bond fraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfatigue bond fraction in a reactive-viscoelastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement's average fiber stretch\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber vector\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMaterial fiber vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfield\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nOutput the nodal values of a field variable.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfixed charge density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFixed charge density \n\\begin_inset Formula $c^{F}$\n\\end_inset\n\n in current configuration,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-CFD-mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid acceleration (material time derivative of fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid body force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's average body force applied to the element.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid bulk modulus\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's average bulk modulus,\n calculate from the pressure.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid density \n\\begin_inset Formula $\\rho^{f}$\n\\end_inset\n\n in fluid and fluid-FSI materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cfd-density\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid density rate\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's average fluid density rate.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid dilatation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid dilatation \n\\begin_inset Formula $e$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid elem\n\\begin_inset VSpace defskip\n\\end_inset\n\nent angular momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTotal angular momentum of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid element center of mass\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCenter of mass of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid element kinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTotal kinetic energy of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid element linear momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTotal linear momentum of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid element strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTotal strain energy of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage energy density of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid flow rate\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVolumetric fluid flow rate \n\\begin_inset Formula $Q=\\int_{A}\\mathbf{w}\\cdot\\mathbf{n}\\,dA$\n\\end_inset\n\n across a named surface (biphasic and multiphasic analyses)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid flux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid flux \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n in biphasic,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bp-flux\"\nnolink \"false\"\n\n\\end_inset\n\n,\n biphasic-solute,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bs-fluxes-w-j\"\nnolink \"false\"\n\n\\end_inset\n\n,\n triphasic/multiphasic,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-fluxes-w-j\"\nnolink \"false\"\n\n\\end_inset\n\n,\n fluid-FSI and biphasic-FSI materials.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNet fluid force across biphasic (sliding-biphasic),\n biphasic-solute (sliding-biphasic-solute) and multiphasic (sliding-multiphasic) contact interface.\n Evaluated by integrating the fluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n over the contact surface.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid force2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAlternative calculation of the net fluid force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid heat supply density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage heat supply density of a fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid kinetic energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage kinetic energy densify of a fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid load support\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe total fluid load support across a named surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid mass flow rate\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMass flow rate across a named surface,\n \n\\begin_inset Formula $\\dot{m}=\\int_{A}\\rho\\mathbf{v}\\cdot\\mathbf{n}\\,dA$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid pressure \n\\begin_inset Formula $p$\n\\end_inset\n\n in biphasic,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bp-stress\"\nnolink \"false\"\n\n\\end_inset\n\n,\n biphasic-solute,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bs-effective-p-c\"\nnolink \"false\"\n\n\\end_inset\n\n,\n triphasic/multiphasic materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-effective-p-c\"\nnolink \"false\"\n\n\\end_inset\n\n,\n and fluid materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:fluid-pressure-state\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid rate of deformation tensor \n\\begin_inset Formula $\\mathbf{D}$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid shear viscosity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage shear viscosity of a fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid strain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage shear energy density of fluid element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid stress \n\\begin_inset Formula $\\boldsymbol{\\sigma}$\n\\end_inset\n\n,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cfd-stress\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid stress power density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid stress power \n\\begin_inset Formula $\\boldsymbol{\\sigma}:\\mathbf{D}$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid surface energy flux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe total energy flux across a named surface for a fluid analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid surface force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNet force exerted by a fluid on a named surface,\n \n\\begin_inset Formula $\\mathbf{f}=-\\int_{A}\\mathbf{t}\\,dA$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid surface pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface*\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage fluid pressure on each face of a surface,\n evaluated from the nodal values of the fluid dilatation on that face\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid surface traction power\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNet traction power across a named surface of a fluid analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement fluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n in fluid and fluid-FSI materials,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid volume ratio\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid volume ratio \n\\begin_inset Formula $J$\n\\end_inset\n\n,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid vorticity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid vorticity,\n Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"sec:Fluids\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth left Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth left Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth left stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth left stretch\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth relative volume\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth relative volume\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth right Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth right Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth right stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ngrowth right stretch\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nheat flux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe average heat flux inside an element of a heat-transfer analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nideal gas pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncurrent pressure value of the \n\\begin_inset Quotes eld\n\\end_inset\n\nideal gas pressure\n\\begin_inset Quotes erd\n\\end_inset\n\n load\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nincremental displacement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe displacement increment from the previously written plot state.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nintact bond fraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfraction of intact bonds in a reactive-viscoelastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nkinetic energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe average kinetic energy density in an element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe average Lagrange strain in an element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nleft Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLeft Hencky strain \n\\begin_inset Formula $\\mathbf{h}=\\frac{1}{2}\\ln\\mathbf{b}$\n\\end_inset\n\n,\n where \n\\begin_inset Formula $\\mathbf{b}=\\mathbf{F}\\cdot\\mathbf{F}^{T}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nleft stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLeft stretch tensor \n\\begin_inset Formula $\\mathbf{V}=\\mathbf{b}^{1/2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nlocal fluid load support\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nLocal fluid load support on biphasic contact surfaces\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmesh_data\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n(multiple)\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStores the value of a mesh_data field to the plotfile.\n The output class depends on the mesh_data field.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture deviatoric strain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nDeviatoric strain energy density in mixture components\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture specific strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSpecific strain energy in mixture components\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture strain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain energy density in mixture components\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnested damage\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe average damage in an elastic mixture\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe acceleration at the nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal contact gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact gap distance,\n evaluated at contact surface nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal contact pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact pressure,\n evaluated at contact surface nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal contact traction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact traction vectors,\n evaluated at contact surface nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal fluid flux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid flux in biphasic,\n biphasic-solute,\n triphasic,\n multiphasic,\n fluid-FSI and biphasic-FSI materials\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFluid velocity \n\\begin_inset Formula $\\mathbf{v}^{f}$\n\\end_inset\n\n in fluid and fluid-FSI materials\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal shell director\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe \n\\begin_inset Quotes eld\n\\end_inset\n\ndirector\n\\begin_inset Quotes erd\n\\end_inset\n\n of a shell node.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe Cauchy stress,\n projected to the nodes.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal surface traction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact nodal tractions\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal vector gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nContact gap vector at surface nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnodal velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe nodal velocities\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noctahedral plastic strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\noctahedral plastic strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nosmolarity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement's average osmolarity in biphasic/triphasic/multiphasic analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nparameter\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStores heterogeneous material parameter data\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npressure gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPressure gap between opposing surfaces in a biphasic contact analysis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreaction forces\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe nodal reaction forces.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreceptor-ligand concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReceptor-ligand concentration in a biphasic-solute analysis.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nreferential fixed charge density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nReferential fixed charge density \n\\begin_inset Formula $c_{r}^{F}$\n\\end_inset\n\n,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-CFD-mass-balance\"\nnolink \"false\"\n\n\\end_inset\n\n,\n which may evolve with chemical reactions,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-referential-FCD\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\\begin_inset Formula $\\varphi_{r}^{s}$\n\\end_inset\n\n,\n which may evolve with chemical reactions,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:cr-referential-solid-vol-frac\"\nnolink \"false\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage element fluid velocity relative to mesh \n\\begin_inset Formula $\\mathbf{w}$\n\\end_inset\n\n in fluid and fluid-FSI materials\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative volume\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRelative volume \n\\begin_inset Formula $J=\\det\\mathbf{F}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nright Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRight Hencky strain \n\\begin_inset Formula $\\mathbf{H}=\\frac{1}{2}\\ln\\mathbf{C}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nright stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRight stretch tensor \n\\begin_inset Formula $\\mathbf{U}=\\mathbf{C}^{1/2}$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body center of mass acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid angular acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body angular acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid angular momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body angular momentum\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid angular position\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body rotation pseudo-vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nTotal force applied to the rigid body\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid kinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body kinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid linear momentum\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body linear momentum\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid position\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body center of mass position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid rotation vector\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body rotation pseudo-vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid torque\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body moment\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrigid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRigid body center of mass velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRVE generations\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNumber of generations in reactive viscoelastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRVE reforming bonds\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFraction of weak bonds available to break and reform in reactive viscoelastic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRVE strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain measure used in strain-dependent reactive viscoelastic relaxation and weak bond recruitment\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsbm concentrationMAT3D\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage element solid-bound molecule concentration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsbm referential apparent density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage element solid-bound molecule referential apparent density\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell bottom nodal strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShell Lagrange strain extrapolated to bottom nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell bottom nodal stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShell Cauchy stress extrapolated to bottom nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell bottom strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage of shell Lagrange strain extrapolated to bottom surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell bottom stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage of shell Cauchy stress extrapolated to bottom surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell director\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe shell director\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell relative volume\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRelative volume of shell element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage strain in shell element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell thickness\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShell thickness\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell top nodal strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShell Lagrange strain extrapolated to top nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell top nodal stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nShell Cauchy stress extrapolated to top nodes\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell top strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage of shell Lagrange strain extrapolated to top surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nshell top stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage of shell Cauchy stress extrapolated to top surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolute concentration\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSolute concentration \n\\begin_inset Formula $c$\n\\end_inset\n\n in biphasic-solute materials,\n or \n\\begin_inset Formula $c^{\\alpha}$\n\\end_inset\n\n in triphasic/multiphasic materials\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolute flux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSolute flux \n\\begin_inset Formula $\\mathbf{j}$\n\\end_inset\n\n in biphasic-solute materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:bs-fluxes-w-j\"\nnolink \"false\"\n\n\\end_inset\n\n,\n or \n\\begin_inset Formula $\\mathbf{j}^{\\alpha}$\n\\end_inset\n\n in triphasic/multiphasic materials,\n Eq.\n\\begin_inset CommandInset ref\nLatexCommand eqref\nreference \"eq:mp-fluxes-w-j\"\nnolink \"false\"\n\n\\end_inset\n\n.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nspecific strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage element specific strain energy\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR principal stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3D\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPrincipal stress values obtained via SPR projection\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCauchy stress obtained via SPR projection\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR-P1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCauchy stress obtained via SPR projection (first-order projection)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStrain energy density \n\\begin_inset Formula $\\Psi\\left(\\mathbf{C}\\right)$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nCauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsurface traction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSurface\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNodal surface tractions of nodes on a contact surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuncoupled pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe pressure of an uncoupled material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nut4 nodal stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nStresses at the nodes of the UT4 element\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvector gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe vector gap at nodes of a contact surface\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvelocity\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNode\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVEC3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNodal velocities (fluid nodal velocities in fluid analyses)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvolume fraction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage element volume fraction of a solid mixture\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nin-situ target stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe fiber stretch induced by the initial prestrain gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPK1 norm\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe norm of the PK1 stress of a micro material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe PK1 stress of the element.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprestrain stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe stretch induced by the effective prestrain gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nprestrain correction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthe 3x3 prestrain correction tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe infinitesimal strain tensor recovered using SPR (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3S\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe Lagrange strain recovered using SPR (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR prestrain correction\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMAT3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe 3x3 prestrain correction tensor (recovered with SPR) (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSPR relative volume\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nElement\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSCALAR\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe relative volume (i.e.\n det \n\\series bold\nF\n\\series default\n) recovered using the SPR method (1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nComments\n\\emph default\n:\n\\end_layout\n\n\\begin_layout Enumerate\nFEBio calculates element variables at the integration points.\n However,\n this is not convenient for output,\n so by default the integration point values are averaged over the element and a single value per element is stored.\n In FEBio Studio,\n this data is further processed and in order to get a unique nodal value,\n the values of adjacent elements are averaged.\n For linear elements and a sufficiently fine mesh,\n this typically produces an accurate and smooth representation of the datafield.\n However,\n for higher-order elements,\n this often produces artifacts,\n such as \n\\begin_inset Quotes eld\n\\end_inset\n\nbubbles\n\\begin_inset Quotes erd\n\\end_inset\n\n that degrade the accuracy.\n For these types of meshes,\n it may be better to use one of the other available recovery methods.\n At this time,\n FEBio offers two alternative recovery methods that often produce better results with higher-order meshes:\n \n\\end_layout\n\n\\begin_deeper\n\\begin_layout Enumerate\nSPR method:\n The SPR (Superconvergent Patch Recovery) method fits at each node a quadratic function to the surrounding integration point values.\n The value of this quadratic function at the node will be stored to the plotfile.\n \n\\end_layout\n\n\\begin_layout Enumerate\nNodal projection:\n The nodal projection method extrapolates the integration point values to the nearest nodes and stores those values to the plot file.\n \n\\end_layout\n\n\\end_deeper\n\\begin_layout Section\nLogfile Variables\n\\begin_inset CommandInset label\nLatexCommand label\nname \"sec:Logfile-Variables\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nAs discussed in \n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Logfile\"\nplural \"false\"\ncaps \"false\"\nnoprefix \"false\"\nnolink \"false\"\n\n\\end_inset\n\n,\n there are different category or classes of log variables.\n The following sections lists the available variables.\n \n\\end_layout\n\n\\begin_layout Subsection\nNode_Data Class\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Node_Data-Class\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nnode_data \n\\shape default\nclass defines a set of nodal variables.\n The data is stored for each node that is listed in the item list of the \n\\shape italic\nnode_data \n\\shape default\nelement or for all nodes if no list is defined.\n The following nodal variables are defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"16\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nNode variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of current nodal position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of current nodal position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of current nodal position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nux\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of nodal displacement\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of nodal displacement\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nuz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of nodal displacement\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of nodal velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of nodal velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of nodal velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of nodal acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nay\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of nodal acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of nodal acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of nodal reaction force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of nodal reaction force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of nodal reaction force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor analyses using biphasic,\n biphasic-solute,\n and triphasic materials,\n the following additional variables can be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nNode variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\np\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective fluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of solid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of solid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of solid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nce<n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective concentration of solute n,\n with n from 1 to 8 (e.g.\n ce1.\n ce2.\n etc.)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor heat transfer problems the following nodal variables are available.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nNode variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nT\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nNodal temperature\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor fluid analyses,\n the following variables can be defined.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"4\" columns=\"2\">\n<features tabularvalignment=\"middle\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nNode variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnfvx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnfvy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnfvz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to store the current nodal positions of all nodes,\n use the following \n\\shape italic\nnode_data \n\\shape default\nelement:\n\\end_layout\n\n\\begin_layout LyX-Code\n<node_data data=\"x;y;z\"></node_data>\n\\end_layout\n\n\\begin_layout Standard\nYou can store the total nodal displacement for nodes 1 through 100,\n and all even numbered nodes 200 through 400 as follows:\n\\end_layout\n\n\\begin_layout LyX-Code\n<node_data data=\"ux;uy;uz\">1:100:1,200:400:2</node_data>\n\\end_layout\n\n\\begin_layout Subsection\nFace_Data Class\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nface_data \n\\emph default\nclass defines a set of surface variables.\n This item requires the \n\\emph on\nsurface \n\\emph default\nattribute,\n which defines the surface that will be used to extract the data from.\n The surface must be defined in the \n\\emph on\nMesh\n\\emph default\n section.\n The data is stored for each surface facet that is listed in the item list,\n or for all facets of the surface if no list is defined.\n \n\\end_layout\n\n\\begin_layout Standard\nThe following surface variables are defined.\n Note that the actual value is the average over the facet's integration points values (if applicable).\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nElement variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe gap (separation) of the contact interface this surface belongs to\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact pressure\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe normal contact pressure of the contact interface this surface belongs to\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact_traction.x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the contact traction on the face\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact_traction.y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the contact traction on the face\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncontact_traction.z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the contact traction on the face\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\n\\emph on\nExample:\n\\end_layout\n\n\\begin_layout Standard\nThis example stores the contact gap and contact pressure of all the facets of surface \n\\begin_inset Quotes eld\n\\end_inset\n\nFacetOnFacetSliding1_primary\n\\begin_inset Quotes erd\n\\end_inset\n\n.\n The surface is defined in the Mesh section.\n \n\\end_layout\n\n\\begin_layout LyX-Code\n<logfile>\n\\end_layout\n\n\\begin_layout LyX-Code\n  <face_data data=\"contact gap;contact pressure\" surface=\"FacetOnFacetSliding1_primary\"/>\n\\end_layout\n\n\\begin_layout LyX-Code\n</logfile> \n\\end_layout\n\n\\begin_layout Subsection\nElement_Data Class\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Element_Data-Class\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nelement_data \n\\shape default\nclass defines a set of element variables.\n The data is stored for each element that is listed in the item list of the \n\\shape italic\nelement_data \n\\shape default\nelement or for all nodes if no list is defined.\n The following element variables are defined.\n Note that the actual value is the average over the element's integration points values (if applicable).\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"147\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nElement variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of current element centroid position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of current element centroid position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of current element centroid position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfirst eigenvalue of Cauchy stress tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond eigenvalue of Cauchy stress tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthird eigenvalue of Cauchy stress tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the Green-Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the Green-Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the Green-Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the Green-Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the Green-Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nExz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the Green-Lagrange strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nE1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfirst eigenvalue of Green-Lagrange strain tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nE2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond eigenvalue of Green-Lagrange strain tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nE3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthird eigenvalue of Green-Lagrange strain tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nex\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ney\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nez\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nexz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the infinitesimal strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nUxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nU1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfirst eigenvalue of right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nU2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond eigenvalue of right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nU3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthird eigenvalue of right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nVxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nV1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfirst eigenvalue of left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nV2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond eigenvalue of left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nV3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthird eigenvalue of left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nHxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nH1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfirst eigenvalue of right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nH2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond eigenvalue of right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nH3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthird eigenvalue of right Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the rileft Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nhxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nh1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfirst eigenvalue of left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nh2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsecond eigenvalue of left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nh3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthird eigenvalue of left Hencky strain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFyx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyx-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFzy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzy-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFzx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the deformation gradient\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nJ\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nrelative volume (determinant of deformation gradient)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxxxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxxxx component of spatial elasticity tensor (a.k.a.\n c11)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxxyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxxyy component of spatial elasticity tensor (a.k.a.\n c12)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyyyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyyyy component of spatial elasticity tensor (a.k.a.\n c22)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxxzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxxzz component of spatial elasticity tensor (a.k.a.\n c13)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyyzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyyzz component of spatial elasticity tensor (a.k.a.\n c23)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nczzzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzzzz component of spatial elasticity tensor (a.k.a.\n c33)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxxxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxxxy component of spatial elasticity tensor (a.k.a.\n c14)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyyxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyyxy component of spatial elasticity tensor (a.k.a.\n c24)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nczzxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzzxy component of spatial elasticity tensor (a.k.a.\n c34)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxyxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxyxy component of spatial elasticity tensor (a.k.a.\n c44)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxxyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxxyz component of spatial elasticity tensor (a.k.a.\n c15)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyyyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyyyz component of spatial elasticity tensor (a.k.a.\n c25)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nczzyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzzyz component of spatial elasticity tensor (a.k.a.\n c35)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxyyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxyyz component of spatial elasticity tensor (a.k.a.\n c45)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyzyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyzyz component of spatial elasticity tensor (a.k.a.\n c55)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxxxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxxxz component of spatial elasticity tensor (a.k.a.\n c16)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyyxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyyxz component of spatial elasticity tensor (a.k.a.\n c26)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nczzxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzzxz component of spatial elasticity tensor (a.k.a.\n c36)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxyxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxyxz component of spatial elasticity tensor (a.k.a.\n c46)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncyzyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyzyz component of spatial elasticity tensor (a.k.a.\n c56)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ncxzxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxzxz component of spatial elasticity tensor (a.k.a.\n c66)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsed\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nstrain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndevsed\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndeviatoric strain energy density\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nD\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndamage variable (or sum of damage variables in solid mixtures)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective left Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe effective (or von-Mises) norm of the left Hencky tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective right Hencky\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe effective (or von-Mises) norm of the right Hencky tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective left stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe effective (or von-Mises) norm of the left stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neffective right stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe effective (or von-Mises) norm of the right stretch tensor\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber_stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe element's fiber stretch,\n averaged over integration points.\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber_x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the element's fiber vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber_y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the element's fiber vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfiber_z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the element's fiber vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_xx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xx component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_xy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xy component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_xz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xz component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_yx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yx component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_yy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yy component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_yz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yz component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_zx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zx component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_zy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zy component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFt_zz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zz component of the total deformation gradient of a prestrain material \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture_stress[<n>].xx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xx-component of the stress of the mixture's solid component <n> (e.g.\n mixture_stress[0].xx)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture_stress[<n>].xy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xy-component of the stress of the mixture's solid component <n> (e.g.\n mixture_stress[0].xy)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture_stress[<n>].xz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xz-component of the stress of the mixture's solid component <n> (e.g.\n mixture_stress[0].xz)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture_stress[<n>].yy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yy-component of the stress of the mixture's solid component <n> (e.g.\n mixture_stress[0].yy)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture_stress[<n>].yz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yz-component of the stress of the mixture's solid component <n> (e.g.\n mixture_stress[0].yz)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmixture_stress[<n>].zz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zz-component of the stress of the mixture's solid component <n> (e.g.\n mixture_stress[0].zz)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xx component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xy component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xz component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPyx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yx component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yy component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yz component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xz component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPzy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zy component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zz component of the PK1 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns1x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the 1st Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns1y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the 1st Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns1z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the 1st Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns2x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the 2nd Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns2y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the 2nd Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns2z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the 2nd Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns3x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the 3rd Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns3y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the 3rd Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ns3z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the 3rd Eigen vector of the Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xx-component of the PK2 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yy-component of the PK2 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zz-component of the PK2 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xy-component of the PK2 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xz-component of the PK2 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nSyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yz-component of the PK2 stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfraction of yielded bonds\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwf\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfraction of fatigued bonds\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to store the (average) Cauchy stress for all elements,\n define the following data element:\n\\end_layout\n\n\\begin_layout LyX-Code\n<element_data data=\"sx;sy;sz;sxy;syz;sxz\" name=\"element stresses\"> </element_data>\n\\end_layout\n\n\\begin_layout Standard\nFor analyses using biphasic,\n biphasic-solute,\n and triphasic materials,\n the following additional variables can be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\nx\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"29\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nElement variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\np\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactual fluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc\n\\begin_inset Formula $n$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nactual concentration of solute \n\\begin_inset Formula $n$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of fluid flux\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of fluid flux\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nwz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of fluid flux\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nj\n\\begin_inset Formula $n$\n\\end_inset\n\nx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of flux of solute \n\\begin_inset Formula $n$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nj\n\\begin_inset Formula $n$\n\\end_inset\n\ny\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of flux of solute \n\\begin_inset Formula $n$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nj\n\\begin_inset Formula $n$\n\\end_inset\n\nz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of flux of solute \n\\begin_inset Formula $n$\n\\end_inset\n\n\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\njx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of flux of solute in biphasic-solute material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\njy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of flux of solute in biphasic-solute material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\njz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of flux of solute in biphasic-solute material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKpxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of the permeability tensor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKpxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of the permeability tensor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKpxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of the permeability tensor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKpyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of the permeability tensor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKpyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of the permeability tensor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKpzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of the permeability tensor.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIex\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the element current density in a multiphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIey\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the element current density in a multiphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIez\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the element current density in a multiphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nesxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xx component of the solid stress of a biphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nesxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xy component of the solid stress of a biphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nesxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe xz component of the solid stress of a biphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nesyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yy component of the solid stress of a biphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nesyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe yz component of the solid stress of a biphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\neszz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe zz component of the solid stress of a biphasic material\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsbm<n>\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nConcentration of solute-bound molecule <n> (e.g.\n smb1)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsbm<n>_referential_apparent_density\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nApparent density in reference configuration of solute-bound molecule <n>\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor fluid analyses,\n the following additional variables can be defined:\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"33\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nElement variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of element centroid position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of element centroid position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz--coordinate of element centroid position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfp\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid pressure\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfvx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfvy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfvz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of fluid velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfJ\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid volume ratio\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfd\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid density\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfsp\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfluid stress power\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of fluid acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfay\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of fluid acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfaz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of fluid acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfwx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of fluid vorticity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of fluid vorticity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfwz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of fluid vorticity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfsxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfsyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfszz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfsxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfsyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfsxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfmxs\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmaximum shear of fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfs1\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n1st principal fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfs2\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n2nd principal fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfs3\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n3rd principal fluid Cauchy stress\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfdxx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxx-component of fluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfdyy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyy-component of fluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfdzz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nzz-component of fluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfdxy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxy-component of fluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfdyz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nyz-component of fluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nfdxz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nxz-component of fluid rate of deformation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor discrete elements,\n the following log variables are defined.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"7\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDiscrete element variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element elongation\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe discrete element's elongation (i.e.\n current length minus initial length)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element force\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe scalar force along the element's axis.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ndiscrete element stretch\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe discrete element's stretch (i.e.\n ratio of current length over initial length)\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFde.x\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe x-component of the discrete element's force vector.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFde.y\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe y-component of the discrete element's force vector.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFde.z\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe z-component of the discrete element's force vector.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nDomain Data Class\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\ndomain_data_class \n\\emph default\ndefines variables that are calculated on an entire domain.\n The variables are often quantities integrated over the entire domain.\n In the table below,\n the notation <n> is often used to represent a number that ranges from 1 to 8,\n unless specified otherwise.\n So for instance,\n the variable \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nc<n>_integral\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n means \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nc1_integral\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n or \n\\begin_inset Quotes eld\n\\end_inset\n\n\n\\emph on\nc2_integral\n\\emph default\n\n\\begin_inset Quotes erd\n\\end_inset\n\n,\n and so forth.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"6\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\ndomain variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nc<n>_integral\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIntegral of concentration of solute <n> \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsbm<n>_integral\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIntegral of concentration of sbm <n>\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nnormalized internal energy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe integral over space and time of the internal energy,\n normalized by the volume of the domain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\navg\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nAverage of another log variable over domain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\npct\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nPercentile of another log variable over domain\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nSurface Data Class\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\emph on\nsurface_data \n\\emph default\nclass defines variables that are calculated over an entire surface.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nsurface variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nmax contact gap\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe maximum contact gap value evaluated over the entire surface.\n \n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Subsection\nRigid_Body_Data Class\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid_Body_Data-Class\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nrigid_body_data \n\\shape default\nclass defines a set of variables for each rigid body.\n The data is stored for each rigid body that is listed in the item list of the \n\\shape italic\nrigid_body_data \n\\shape default\nelement or for all rigid bodies if no list is defined.\n The following variables are defined.\n Note that the item referenced in the item list is the material number of the rigid body.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"54\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nRigid body variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-coordinate of center of mass position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-coordinate of center of mass position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-coordinate of center of mass position\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of center of mass velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of center of mass velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nvz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of center of mass velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nax\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of center of mass acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nay\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of center of mass acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of center of mass acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of rotation pseudo-vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of rotation pseudo-vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nthz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of rotation pseudo-vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nomx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nomy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nomz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of angular velocity\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nalx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\naly\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of angular acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nalz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of angular acceleration\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nqx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of rotation quaternion\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nqy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of rotation quaternion\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nqz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of rotation quaternion\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nqw\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nw-component of rotation quaternion\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of the rigid body reaction force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of the rigid body reaction force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of the rigid body reaction force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of the rigid body reaction torque\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of the rigid body reaction torque\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nMz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of the rigid body reaction torque\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nKE\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nkinetic energy\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEulerX\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nX-Euler angle\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEulerY\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nY-Euler angle\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nEulerZ\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nZ-Euler angle\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFHAsx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of the finite helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFHAsy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of the finite helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFHAsz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of the finite helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFHAwx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular rotation vector around finite helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFHAwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of angular rotation vector around finite helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nFHAwz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of angular rotation vector around finite helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIHAsx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of the instantaneous helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIHAsy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of the instantaneous helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIHAsz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of the instantaneous helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIHAwx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of angular rotation vector around instantaneous helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIHAwy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of angular rotation vector around instantaneous helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nIHAwz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of angular rotation vector around instantaneous helical axis\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR11\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n11-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR12\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n12-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR13\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n13-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR21\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n21-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR22\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n22-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR23\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n23-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR31\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n31-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR32\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n32-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nR33\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n33-component of the rigid body's rotation matrix\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to store the rigid body reaction force of rigid body 2 and 4 add the following data element.\n Note that the 2 and 4 refer to the rigid body material number as defined in the \n\\shape italic\nMaterial\n\\shape default\n section of the input file:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_body_data data=\"Fx;Fy;Fz\">2,4</rigid_body_data>\n\\end_layout\n\n\\begin_layout Subsection\nRigid_Connector_Data Class\n\\begin_inset CommandInset label\nLatexCommand label\nname \"subsec:Rigid_Connector_Data-Class\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nThe \n\\shape italic\nrigid_connector_data \n\\shape default\nclass defines a set of variables for each rigid joint or rigid connector.\n The data is stored for each rigid joint or rigid connector that is listed in the item list of the \n\\shape italic\nrigid_connector_data \n\\shape default\nelement or for all rigid connectors if no list is defined.\n The following variables are defined.\n Note that the item referenced in the item list is the rigid connector number in the order in which rigid connectors appear in the input file.\n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"13\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nRigid body variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCFx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of rigid connector force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCFy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of rigid connector force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCFz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of rigid connector force\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCMx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of rigid connector moment\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCMy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of rigid connector moment\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCMz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of rigid connector moment\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of rigid connector translation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of rigid connector translation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of rigid connector translation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCthx\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nx-component of rigid connector rotation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCthy\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\ny-component of rigid connector rotation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nRCthz\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"center\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nz-component of rigid connector rotation\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\nFor example,\n to store the reaction forces and moments at rigid joints 2 and 4 add the following data element:\n\\end_layout\n\n\\begin_layout LyX-Code\n<rigid_connector_data data=\"RCFx;RCFy;RCFz;RCMx;RCMy;RCMz\">2,4</rigid_connector_data>\n\\end_layout\n\n\\begin_layout Standard\nThe rigid connector translation and rotation variables return relative motions of the rigid bodies connected by rigid joints and other rigid connectors (Section\n\\begin_inset space ~\n\\end_inset\n\n\n\\begin_inset CommandInset ref\nLatexCommand ref\nreference \"subsec:Rigid-Connectors\"\nnolink \"false\"\n\n\\end_inset\n\n).\n The rotation components represent the components of a vector whose direction is along the axis of rotation,\n and whose magnitude is the (counter-clockwise) angle of rotation.\n These relative motions are calculated as the motion of \n\\emph on\nbody_b\n\\emph default\n relative to \n\\emph on\nbody_a\n\\emph default\n,\n expressed in the local Cartesian basis of \n\\emph on\nbody_a\n\\emph default\n,\n whose initial origin is at \n\\emph on\njoint_origin\n\\emph default\n.\n This Cartesian basis (which generally moves with \n\\emph on\nbody_a\n\\emph default\n) has its first axis along \n\\emph on\nrotation_axis\n\\emph default\n (for revolute and planar joints) or \n\\emph on\ntranslation_axis\n\\emph default\n (for prismatic joints) or \n\\emph on\njoint_axis\n\\emph default\n (for cylindrical joints);\n the second axis is along the \n\\emph on\ntransverse_axis\n\\emph default\n (for revolute,\n prismatic and cylindrical joints) or \n\\emph on\ntranslation_axis_1\n\\emph default\n (for planar joints).\n For spherical joints,\n springs,\n dampers,\n angular dampers and contractile forces,\n the axes are always aligned with the global Cartesian basis.\n\\end_layout\n\n\\begin_layout Standard\nFor springs,\n dampers and contractile forces,\n the returned translation components represent the relative position vector between insertion points on \n\\emph on\nbody_b\n\\emph default\n and \n\\emph on\nbody_a\n\\emph default\n.\n The magnitude of this vector represents the distance between those points.\n\\end_layout\n\n\\begin_layout Subsection\nModel Data Class\n\\end_layout\n\n\\begin_layout Standard\nThis category encompasses variables that are not necessarily tied to specific components of a model.\n \n\\end_layout\n\n\\begin_layout Standard\n\\align center\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\begin_inset Tabular\n<lyxtabular version=\"3\" rows=\"2\" columns=\"2\">\n<features islongtable=\"true\" longtabularalignment=\"center\">\n<column alignment=\"left\" valignment=\"top\" width=\"0pt\">\n<column alignment=\"left\" valignment=\"top\">\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nModel variables\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\n\n\\series bold\nDescription\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n<row>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" leftline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nsolution_norm\n\\end_layout\n\n\\end_inset\n</cell>\n<cell alignment=\"left\" valignment=\"top\" topline=\"true\" bottomline=\"true\" rightline=\"true\" usebox=\"none\">\n\\begin_inset Text\n\n\\begin_layout Plain Layout\nThe L2 norm of the solution vector\n\\end_layout\n\n\\end_inset\n</cell>\n</row>\n</lyxtabular>\n\n\\end_inset\n\n\n\\begin_inset VSpace defskip\n\\end_inset\n\n\n\\end_layout\n\n\\begin_layout Standard\n\\begin_inset CommandInset bibtex\nLatexCommand bibtex\nbtprint \"btPrintCited\"\nbibfiles \"FEBio3\"\noptions \"bibtotoc,plain\"\n\n\\end_inset\n\n\n\\end_layout\n\n\\end_body\n\\end_document\n"
  },
  {
    "path": "Documentation/README.txt",
    "content": "************************************************ FEBio README *************************************\n\nFEBio is a nonlinear finite element solver that is specifically designed for biomechanical applications. It offers modeling scenarios, constitutive models and boundary conditions that are relevant to many research areas in biomechanics. All features can be used together seamlessly, giving the user a powerful tool for solving 3D problems in computational biomechanics. The software is open-source, and pre-compiled executables for Windows, OS-X and Linux platforms are available.\n\nFEBio can be downloaded from http://mrl.sci.utah.edu/software/febio.  Please inform us of publications that use FEBio in research.  Information can be found on the Publications tab.  Support forums are at http://mrlforums.sci.utah.edu/forums/.\n\n\nFEBio Makefile Guide\n********************\n\nFEBio comes with the default linear solver Skyline.  Instructions are included in build/Makefile to add the Pardiso linear solver.  The source code download also contains Visual Studio and XCode configurations.\n\nfebio.xml\n*********\n\nFEBio now requires a configuration file febio.xml.  This needs to be in the same directory as the executable or can be specified on the command line such as: febio.lnx -i inputfile.feb -cnf someconfig.xml.  The febio.xml file in the bin directory specifies Pardiso as the linear solver.  This can be edited to specify another linear solver.  For example, replace <linear_solver type=\"pardiso\"></linear_solver> with <linear_solver type=\"skyline\"></linear_solver> to use the skyline solver."
  },
  {
    "path": "Documentation/ReleaseNotes.txt",
    "content": "===========================================================================\n         ________    _________   _________     __     _________\n        |        |\\ |        |\\ |        |\\   |  |\\  /         \\\\\n        |    ____|| |    ____|| |    __  ||   |__|| |    ___    ||\n        |   |\\___\\| |   |\\___\\| |   |\\_| ||    \\_\\| |   //  \\   ||\n        |   ||      |   ||      |   || | ||    __   |  ||    |  ||\n        |   ||__    |   ||__    |   ||_| ||   |  |\\ |  ||    |  ||\n        |       |\\  |       |\\  |         \\\\  |  || |  ||    |  ||\n        |    ___||  |    ___||  |    ___   || |  || |  ||    |  ||\n        |   |\\__\\|  |   |\\__\\|  |   |\\__|  || |  || |  ||    |  ||\n        |   ||      |   ||      |   ||  |  || |  || |  ||    |  ||\n        |   ||      |   ||___   |   ||__|  || |  || |   \\\\__/   ||\n        |   ||      |        |\\ |          || |  || |           ||\n        |___||      |________|| |__________|| |__||  \\_________//\n\n      F I N I T E   E L E M E N T S   F O R   B I O M E C H A N I C S\n\n\n  Musculoskeletal Research Laboratories\n  University of Utah\n  http://mrl.sci.utah.edu\n\n  Musculoskeletal Biomechanics Laboratory\n  Columbia University\n  http://me.columbia.edu/mbl\n\n  copyright (c) University of Utah 2006-2021 - All rights reserved\n\n\n===========================================================================\n\nR E L E A S E   N O T E S\n\n===========================================================================\n\nThis document describes the features that have been added to the latest\nFEBio release as well as the more important bug fixes. Please consult\nthe FEBio User's Manual for a more detailed description of the new features.\nTo report any bugs or request new features, please visit the FEBio forum \nat https://forums.febio.org/.\n\n===========================================================================\n\n R E L E A S E   4.12  2/25/2026\n\n===========================================================================\n\nNew Features:\n* Added option to \"contact potential\" to exclude neighbors from contact search.\n* Added support for including CDATA sections in xml file.\n* Added \"scale\" 1d-function, which simply scales the input value by a user-defined constant.\n\nImprovements:\n* Added additional checks around potential dereferencing null pointers in contact potential.\n* Reimplemented torsion spring on revolute joint so applied moment remains linear w.r.t. the rotation angle.\n* Mapped values are now stored with FMT_ITEM if applicable.\n* Updated FEBioConfig.cmake. SDKs now include FEBioConfig.cmake\n* Added FEBioConfig.cmake, a CMake build script that can be used in plugins to find and configure the FEBio paths. \n* Made some changes to FEBioConfig.cmake [skip ci]\n* Added option to set verbose mode, which will print info on when callbacks are entered and exited.\n\nBug Fixes:\n* Using different assembly method for forces of beam domain so that reaction forces are processed correctly.\n* Fixed bug in \"parameter\" plot variable when field was evaluated over multiple domains.\n* Fixed crash when elements are initially inverted.\n* Moved where entity references in xml reader are processed.\n* Fixed issues with output of \"contact penalty\" and \"contact status\". (#116)\n* Fixed initial value of E parameter in tension-only linear spring.\n* Added checks on FEModel* constructor argument for feature classes to avoid crashing when FEModel* is null in FEBio Studio.\n\nDocumentation:\n* A new Feature Manual is available here: https://febiosoftware.github.io/febio-feature-manual/\n\n\n===========================================================================\n\n R E L E A S E   4.11  1/5/2026\n\n===========================================================================\n\nNew Features:\n* Added plot variable for plastic yield stress, updated nonlinear iterative solution method for reactive plasticity to produce better results for yield criterion Deshpande-Fleck\n* Added plot variables for the Drucker shear stress, the Drucker-Prager stress criterion, and the Deshpande-Fleck stress criterion\n* Added Deshpande-Fleck yield/damage criterion\n* Added Drucker-Prager failure or yield criterion. Fixed factor of 2 error in tens4dmm::dyad4 calculation\n* Added support for \"const\" ElementData section.\n* Added pipette aspiration pressure load\n* Added support for reading and writing xml encode control characters.\n* Added support for MAT3F element data in VTK export.\n* Added support for FMT_MULT domain variables to VTK export. These fields are stored as nodal data.\n* Added \"incremental displacement\" plot variable that calculates the change in displacement from the previously stored state.\n* Implemented fiber-pow fiber material model.\n* Implemented fftw-based image blurring algorithm.\n* FEBio no longer requires a config file to run. Config files must now be explicitly added to the command line (use -config). \n* Implemented active contraction model valid for uncoupled materials.\n\nImprovements:\n* Added body force stiffness calculation for beam domains.\n* Updated Deshpande-Fleck yield criterion to be more consistent with the paper.\n* Modified parameterization of FEEllipsoidalFiberDensityDistribution to simplify assigning maps.\n* Added option to stiffness diagnostic to only test a limited number of equations.\n* Initialized \"spa\" member of the \"ellipsoidal\" density distribution to a valid value.\n* Some minor changes to continuous fiber framework to simplify some plugin development.\n* Added error message when rve fails to load.\n* Modified sign convention in \"rigid joint\" to be consistent with other rigid connectors.\n* The invalid command line option is now reported to the user.\n* Allowed some whitespace in the tag's value when defining maps.\n\nBug Fixes:\n* Fixed bug in FEBioMech/FEBioMechModule.cpp\n* Fixed method for evaluating isobaric specific heat capacity in real liquids, gases and vapors\n* Fixed minor bug to allow gap tolerance to be specified for AUGLAG in sliding-elastic, even when tol=0\n* Fixed crash in export of \"PK1 norm\" plot variable. (#111)\n* Fixed bug in writing mat3 data to vtk file.\n* Fixed bug in serialization of FEReaction.\n* Fixed issue with order of serializing data.\n* Fixed bug where nodeset was not set for nodedata generators.\n* Fixed issue in elasticity calculation in several materials.\n* Fixed several issues with stiffness matrices of rigid connectors. Also implemented Lagrange Multiplier method for all rigid connectors.\n* Alpha parameters are now only set in dynamic analyses. This fixes a bug where alpha parameters affected static analyses too. \n* Fixed problems when running micro-materials.\n* Fixed issues with stiffness matrix of \"rigid joint\".\n* Fixed issue with Gaussian filter in fftw blur.\n* The \"import\" plugin folder in config file is now supported on all OSes.\n* Fixed stiffness matrix for rigid connectors. [skip ci]\n* Stiffness matrix now handles rigid nodes correctly.\n* Fixed unit assignments in muscle material parameter list.\n\nDocumentation:\n* Description of FEBio tasks was added (section 2.10).\n* The tables of plot and log variables were expanded and are now available in Appendix E.\n* Many missing boundary conditions, initial conditions, and loads were added to the documentation.\n* Descriptions of several domain types were updated.\n* Documentation on FEBio's explicit solver was added to section 3.3.9.\n* Added documentation for Deshpande-Fleck damage/yield criterion\n* Added description of pipette aspiration pressure load.\n* Fixed reference in active contraction material in theory manual.\n* Updated description of time stepper in user manual.\n* Fixed documentation of fibers-3d-fei integration rule.\n* Updated developer's manual. Fixed some typos in the material plugin section and added a new section on creating plugins with fbs3.\n* Updated user manual to correct numbering error for reactive viscoelastic trigger (should be 0, 1, 2 not 1, 2, 3)\n\n\n\n===========================================================================\n\n R E L E A S E   4.10   7/23/2025\n\n===========================================================================\n\nNew Features:\n* Added plot variables that calculate total linear momentum, angular momentum, and energy for a solid domain.\n* Added plot variable for nodal shell director\n* Added range check for E parameter of FE2DFiberNeoHookean.\n* Added calculation of Almansi strain for elastic material points. Added plot variables for Almansi strain, left Cauchy-Green and right Cauchy-Green tensors\n* Added isotropic Hencky elastic model\n* Added tension_only flag to fiber-pow-linear\n* Added command line option to suppress log file output.\n* Added calculation of helical axis (instantaneous and finite) for rigid bodies and rigid connectors, and created log variables for output to file.\n* Added command line option to prevent shell title updates.\n* Added axial body force.\n* Implemented radial body load.\n* Added option to contact potential to use a higher-order quadrature rule. This will use a new QUAD4G16 for quadrilateral elements. \n* Added \"rigid kinematics\" initial condition that sets initial velocity based on rigid body kinematics.\n* Added quick restart diagnostic.\n* Implementation of the slide-line and tied-line constraint.\n* Added PK1 stress components to logfile output, as Pxx, Pxy, etc.\n* Added log file nodal data variables for effective fluid pressure pe and effective solute concentrations ce1 to ce8\n* Added mapping to FEDiffAlbroIso, DiffConstOrtho and DiffRefIso diffusivity types\n* Added mapping capability for FEDiffConstIso diffusivity type\n* Added \"sbm_referential_apparent_density\" log variable.\n\n\nImprovements:\n* Fixed some formatting issues in the FEBio EULA.\n* Added documentation for initial displacement condition\n* Added option to specify initial displacements\n* Added description of pressure_model in Uncoupled elastic solids. Improved description of enforcement of three-field-solid formulation.\n* Updated User Manual to include reactive fatigue materials\n* Added Doxygen comments to some core classes\n* Modules registered by plugins are now unregistered when the plugins are unloaded.\n* Improved performance of SPR projection by storing reusable data.\n* For mixtures, the density is now defined by the top-level parameter.\n* Changed type of perm parameters from double to FEParamDouble.\n* Added option to tag a parameter as obsolete. Now using this for unused biphasic and multiphasic solver parameters.\n* Made sure time increment is initialized before model initialization to avoid division by zero.\n* Modified kinematic growth formulation so that it does not need to permanently overwrite elastic material point data.\n* Fixed some logo issues in the documentation\n\n\nBug fixes:\n* Fixed issue with validation when not all properties are initialized yet. \n* Fixed issue with reading load curves from restart input.\n* Fixed bug in building matrix profile for tied-elastic interface.\n* Fixed problem with initialization of biphasic materials.\n* Unloading plugins on Linux and macOS now actually unloads the library (rather than doing nothing).\n* Fixed bug in evaluation of stiffness matrix for slide-line.\n* Changes to stiffness matrix diagnostic to better handle prescribed dofs.\n* Fixed issue with reassigning material IDs after mmg remeshing.\n* Fixed issue with serialization of array of FEParamDouble. Removed unnecessary Serialize of EFDMoonyRivlin.\n* Fixed potential crash in FBS when an analysis is allocated without an active module.\n* Reaction forces are not accumulated anymore in residual vector for rigid nodes. (Otherwise they are double-counted.)\n\n\n\n===========================================================================\n\n R E L E A S E   4.9   3/24/2025\n\n===========================================================================\n\nNew Features:\n* The \"fiber ODF\" material was added, which is a new Continuous Fiber Distribution constitutive model. It uses a non-parametric orientation distribution function that is defined by a series of spherical harmonic coefficients. This allows for the definition of extremely detailed ODFs of any shape.\n* Added \"edge contact gap\" plot variable which plots gap data on the edge.\n* Added log output variables for fluid max shear stress and fluid principal stresses\n* Added secant stress, secant tangent, and secant permeability tangent calculations to biphasic solid and biphasic shell domains\n* Added plot variable for nodal shell displacements\n* Added flip_secondary and gap_offset parameters to sticky contact. Fixed some issues with contact initiation.\n* Added filter option to contact gap, contact pressure, and contact traction plot variables.\n* Added progress and relative error measure to stiffness diagnostic.\n* Added noappend command option to prevent appending of plot file when restarting.\n* Added \"SPR relative volume\" plot variable.\n\n\nImprovements:\n* Editing pass to FEBio user manual (fix typos, removed obsolete info, added missing info.)\n* Now exporting FEIncompNeoHookean from febiomech dll. (In case plugins need it.)\n* Added documentation for FiberODF material [skip ci]\n* Made PlotFile::AddVariable public to make it easier for third party code to create plotfiles.\n* Updated reaction force log variables to be more consistent with \"reaction forces\" plot variable.\n* Added flags to tied-elastic to flip primary and secondary surfaces.\n* Made g0 parameter of visco-elastic material mappable.\n* Added check for matrix offset in pardiso solver (in case the sparse matrix was allocated externally).\n* Made perm parameter of \"perm-holmes-mow\" mappable.\n* Added units of length for length parameters in Surface Attraction Body Force\n* Added some additional parallelization to contact potential.\n* Updated User Manual to includee fluid max shear stress and fluid principal stresses as log output variables\n* Added description of Wall Shear Stress for fluids, in Theory Manual\n* All OSes now use MMG 5.7.3\n* Added helper function to levmar in order to avoid the need to link levmar (and blas) into FEBio Studio.\n* Updated nodal stress and nodal strain plot variables to exclude new shells.  Updated top/bottom stress/strain and top/bottom nodal stress/nodal strain to only include new shell elements. Updated calculation of nodal projection for quad8 and tri6 shells.\n* Updated shell top stress and shell bottom stress plot variables to work with quadratic shell elements.\n* Modified shell top stress/strain and shell bottom stress/strain to display extrapolated integration point values instead of top or bottom integration point averages\n* Modified plot variables for shell nodal stresses to say shell top nodal stress and shell bottom nodal stress. Added plot variables for shell top stress and shell bottom stress.\n* Added secant_stress and secant_tangent parameters to solid-elastic domain.\n* Added support for tri7 surface elements to \"normal displacement\".\n* Implemented surface constraint member function to choose whether a nodal integration rule should be installed on the surface. The fixed normal displacement now sets a non-nodal integration rule.\n* Added option to check for edge-face intersections in contact potential.\n* Added function for SPR values of vector<double> types, updated tet10 projection order default value\n\n\nBug Fixes:\n* Fixed issue with processing empty Module tag.\n* Linear constraints no longer assume node IDs start at 1.\n* Fixed small issue with resetting a timer.\n* Fixed issue with discrete element ID numbers.\n* Fixed bug caused when trying to compress plot files when compiling without zlib\n* Fixed bug in constructor of MKL solver if MKL was not used. Now not registering mkl solvers if mkl is not used.\n* Fixed destructor of PardisoProjectSolver to avoid error message at end of run\n* Fixed issue with parsing elem_type attribute of solid domain.\n* Fixed issue with contact potential when used with rigid bodies.\n* Fixed issue with the normal displacement constraint when node IDs don't start at 1.\n* Fixed problem with exporting fiber components to log file.\n* Fixed bug in FEPlotMixtureFluidFlowRate plot variable.\n* Fixed calculation of yielded bond fraction in reactive plasticity materials to account for rigid body rotations.\n* Updated damage criterion for von Mises stress to produce non-singular tangent at stress origin.\n\n\n\n===========================================================================\n\n R E L E A S E   4.8   11/7/2024\n\n===========================================================================\n\nNew Features:\n* Added new \"preferred\" option for matrix format. With this option, FEBio will select the matrix format depending on the analysis type and model components. \n* Added an \"element data\" criterion for mesh adaptors. This allows users to use any of the element log data fields in mesh adaptors. \n* Added new feature to use math expressions in element log data.\n* Implemented support \"pardiso_64\" linear solver that can be used with really large problems ( > 2e9 non-zero elements in stiffness matrix).\n* Added \"msglvl\" option  to produce additional output from pardiso and mkl_dss linear solvers.\n* Implemented \"surface-to-surface vector\" element data map generator.\n* Modified implementation of cubic-CLE and ortho-CLE materials to hew more closely to theoretical formulation\n* Added \"max contact gap\" log variable.\n* Added additional damage log output variables.\n* Added log variables for exporting damage components of nested materials.\n* Added option to \"moving frame\" load to specify the angular velocity in the moving frame instead of the fixed frame.\n* Added nodal_target_force load, which can be used to ramp down reaction forces produced by prescribed displacements.\n* Added \"relative volume\" mesh adaptor criterion\n* Added option to specify a filter function for the target objective.\n* Added ls_check_jacobians control parameter for toggling modified line search that catches negative jacobians.\n* Added a \"contact gap\" mesh adaptor criterion.\n* Added \"SPR infinitesimal strain\" plot variable.\n* Added \"normalized internal energy\" domain log variable.\n* Added log output variable for total deformation gradient (includes effect of prestrain if present.)\n\nImprovements:\n* Changed how element value is calculated from material point values in FEMeshAdaptorCriterion.\n* FEBioModel now also tracks stats per step.\n* Now printing warning if mass of rigid body is zero.\n* Modified timers so that they can record both inclusive (i.e. total) time and exclusive time. Added some additional timers.\n* Some changes to the update and linsolve timers to get more accurate timings.\n* Added additional output parameters for \"moving frame\" loads.\n* Made some minor performance enhancements for the explicit solver.\n* Implemented better way for getting parameter values from the model.\n* Modified MMG remesh so it can remesh internal surfaces.\n* Adaptor criterion work on elements instead of on integration points.\n* Modified definition of contact material point in several sliding and tied interfaced to facilitate plotting of contact gap and pressure gap in tied-biphasic and tied-multiphasic interfaces\n* Now storing parts lists defined in input file as domain lists on the mesh. Added new surface reconstruction algorithm for surfaces defined via part lists. \n* Updated the node-distance constraint so it can be used to enforce an absolute distance.\n* Added units to some parameters in FEHuiskeSupply and FERemodelingMaterial\n* Removed create handler mechanism (This has been moved to FEBio Studio since that's where it's actually used.)\n* Users can now set the max nr. of negative jacobians reported using the output_negative_jacobians flag.\n* Added missing description in long name of FEBondRelaxationParkDistortion\n* Modified FEHuiskesSupply to include characteristic distance from sensor cells, for consistency with FEHuiskesReactionRate.\n* Modified initialization of FEChemicalReaction to initialize FEReactionSpeciesRef objects, and to initialize forward and reverse reactions.  Modified FEReaction initialization to remove initialization of base class. These changes fix out-of-order initializations that became problematic in FEReactionRateHuiskes and similar classes\n* Updated Huiskes reaction rate to include characteristic distance from sensor cells\n* Modified error reporting for the perm-ref-ortho permeability calculation so it only reports the error a limited number of times.\n* Added command line option to set the output_negative_jacobian flag.\n* Added plot file option to choose whether to export eroded elements or not.\n* Implemented algorithm for growing surfaces when underlying elements are eroded.\n* Added explicit check for zero shell thicknesses.\n* Updated biphasic, biphasic-solute and triphasic materials to include local material axes, just like multiphasic materials\n* Fixed issue when Surface section is empty. Added some additional mesh validation. Warnings are now produced if nodesets, surfaces, etc. are empty.\n* Improperly formatted step section will now throw exception.\n* Added warning to tied interfaces when no contact pairs were found.\n* Updated default solver strategy for biphasic, biphasic-solute and multiphasic modules to Broyden\n* Changed default settings for biphasic, biphasic-solute and multiphasic analyses to use max_ups=0 (full Newton) and max_refs=25\n* Updated FESolventSupplyStarling to use FEParamDouble instead of double material parameters\n* Added general growth option for kinematic growth and updated documentation.\n* Made domain attribute optional for domain_data.\n* Modified warning message for experimental features.\n* Load controllers are now given a default name if no name is provided.\n* Made sure that must points are written to plotfile even when plot stride is not one.\n* Addressed some performance issues in FEOctree.\n* Element neighbor list is now stored on the mesh so it does not need to be recalculated repeatedly.\n* Minor fixes to Accelerate solver for Apple computers. Documented Accelerate solver in User Manual\n* Made sure plugin is unloaded when its interface is not compatible.\n* Refactored binarySearch function out of PointCurve class.\n* Modified calculation of second derivatives of elastic fluid pressure with respect to temperature and volume to use first derivative calculation, since first derivative may employ smoothed functions\n* Modified real vapor models to use stretched temperature scale for more efficient point interpolation\n* Added descriptions of timings reported at the end of a job.\n* Addressed some problems reading old 2.5 files.\n\nBug Fixes:\n* Fixed bug in reactive viscoelastic materials (unconstrained and uncoupled) to correctly accommodate viscoelastic fibrous materials that don't sustain stress in compression.  This affects reactive viscoelastic fibrous materials subjected to tension and compression.\n* Fixed bug in MKLDSSolver which prevented compilation of FEBio without MKL library\n* Fixed issue with merging vector data maps.\n* Fixed several issues related to restarts when using rigid components or when using dump=2 option.\n* Fixed some issues related to erosion in shells and contact.\n* Added omp critical section around logging in FEPermHolmesMow to avoid potential crash when run with multiple threads.\n* Fixed small bug in evaluation of elastic tangent in cubic-CLE and ortho-CLE materials\n* Fixed issue with gap tolerance in sliding-node-on-facet.\n* Fixed bug in processing expression string for FEMathValueVec3.\n* Fixed potential crash when using uninitialized FEMappedValueMat3d.\n* Fixed bug that incorrectly set the initial energy norm.\n* Fixed bug in discrete domain initialization.\n* Fixed issue that stress was not reported for von-Mises plasticity material.\n* Fixed bug in FECoreBase::GetParameterValue.\n* Fixed issue with reconstructing global nodeset.\n* Fixed issue with including materials from multiple include files.\n* Fixed issues with incorrect use of material point classes.\n* Fixed some memory leaks.\n* Fixed problem with evaluating multi-variable functions.\n* Fixed duplicated surface facet issue with surface erosion.\n* Moved ctol from biphasic-solute solver to biphasic solver (but set it to 0 in biphasic solver). This is needed to correctly pass ctol as a parameter in biphasic-solute analyses).\n* Fixed segfault caused by missing return values\n* Updated initialization of FEFluidSupplyStarling and FESolventSupplyStarling and their parent classes. Added FERemodelingSolid class to model reactive solid mixtures (initial implementation).\n* Fixed bug in initialization of FEGrowthTensor\n* Fixed issue with creating rigid bodies with shells on solids.\n* Added missing support for evaluating functions with variable number of parameters.\n* Fixed bug in FEElasticMixture::UpdateSpecializedMaterialPoints.\n* Added serialization for edge-to-surface sliding contact.\n* Fixed bug in evaluation of total deformation gradient log variable.\n* Fixed bug in export of global variables.\n* Fixed crash with FEPlotEffectiveElasticity.\n* Added function to FEMesh to extract element's zero-based index and used that to fix issue with domain boundary calculation.\n* Fixed problem with extracting boundary from mesh when element IDs don't start from 1.\n* Fixed bug in real vapor calculation of q.\n* Fixed bug in composing FEException message.\n* Added missing cases for FE_VEC3D and FE_MAT3D in FEPlotMeshData::SetFilter.\n\nDocumentation:\n* Fixed comments in code snippet of plugin registration documentation.\n* Fixed typo in uncoupled material section.\n* Updated some documentation of linear solvers\n* Fixed explanation that shell_normal_nodal is defined in the ShellDomain section, not the Control section.\n* Added note on auto-contact to user manual.\n* Updated documentation on this node-distance constraint.\n* Updated Theory and User manuals to reflect updated features of the Huiskes reaction rate and clarify material specifications and potential pitfalls\n* Minor updates to mesh adaptor documentation.\n* Updated documentation on plot data plugins.\n* Updated documentation on optimization\n* Replaced mrl.sci.utah.edu links with weisslabutah.org links in manuals.\n* Replaced pdf figures with pngs. Updated theory manual accordingly.\n* Documented Holmes-Mow elastic solid in the limit as beta goes to zero\n\n\n===========================================================================\n\n R E L E A S E   4.7   6/6/2024\n\n===========================================================================\n\nNew Features:\n* Added ability to select reactive plastic damage material as base (elastic) material of reactive viscoelastic materail, enabling viscoplastic damage analyses\n* Added C2-smooth option for loadcurves\n* Added \"volume change\" log variable.\n* A new body load \"moving frame\" was added to the solid module that simulates the effects of the model being embedded in a moving frame. A similar feature was added to the fluid module, where the load is called \"fluid moving frame\".\n* Added Quemada viscous fluid formulation\n* Added \"body force\" plot variable.\n* Added plot variable for enclosed volume change\n* Added plot variable for fluid body force. Improved formulation of stiffness matrix for body forces in fluid and fluid-FSI domains to include stiffness due to variation of fluid velocity. Minor tweaks to fluid-FSI solver.\n* Added the mat_axis domain property, which is used to initialize the material point material axis parameter.\n* Added option to export nodal reaction forces for explicit solver.\n* Added \"ideal gas pressure\" which applies a pressure based on the ideal gas law.\n* Added debug log output and a plot variable for outputting the current pressure of an ideal-gas-pressure load.\n* Introduced ReactionRateRuberti to model strain-dependent fiber degradation\n* Added FEVec3dValuator object to FEFiberExpPowSBM and FEFiberPowLinearSBM to make them behave consistently with other fiber models. \n* An (internal) mechanism was added for auto-differentation of Stress and Tangent routines. This is available to plugin developers who wish to avoid calculating the explicit form of the Stress and/or Tangent. \n* Added edge-to-surface sliding contact, which can be used to enforce contact between beams or trusses and a surface. Also renamed potential-based edge-to-surface contact.\n\nImprovements:\n* Added option to set element type to HEX8G6 in feb4 input file.\n* added missing s in some instances of output_negative_jacobians\n* Added default load curve to surface force load.\n* Added units of time for bond relaxation constants\n* Added descriptions for viscoelastic and reactive viscoelastic damage, and reactive viscoplasticity and reactive viscoplastic damage to User Manual\n* Changed long names of pressure- and cv-virial coefficients in real vapor, changed long name of viscosity-virial coefficient in Newtonian real vapor, added plot variable for fluid relative thermal Peclet number\n* Updated thermo-fluid module to use latest version of real vapor, added Newtonian real vapor and thermal conductivity real vapor material models. Added plot variables for tangents of fluid pressure with respect to volume and temperature. Used virtual functions to evaluate derivatives of functions of state with respect to volume and temperature\n* Added documentation for enclosed volume change\n* Updated Theory Manual to describe stiffness contribution from variations of fluid velocity to fluid body force calculation\n* Contact potential now also calculates total contact force.\n* Augmentations can now be done optionally for the sliding-elastic contact by attaching a load curve to the tolerance parameter.\n* Documented the stress and tangent of the \"HGO unconstrained\" material in the theory manual.\n* Improved consistency in PrepStep among fluid solvers, by calling UpdateModel instead of fem.Update in all the solvers.\n* Comments are now parsed in XMLReader::GetChar (this was to avoid a problem when a tag's value contains a comment)\n* Modified FiberExpPowSBM and FiberPowLinearSBM to use FEParamDouble properties.\n* Updated remodeling interface materials to calculate solid mass density for elastic solids, solid mixtures, and multiphasic materials\n* Implemented Init function for several contact material point classes.\n* Error messages for reset_test are printed to cerr instead of the log file.\n* Each contact interface now defines the laugon parameter so that the correct values can be specified.\n* added mechanism for plotting strain energy and specific strain energy for components of solid mixtures\n* Minor change to optimization output formatting.\n* X column now prints time for data-fit objective for optimization problems.\n* Implemented nodal integration rule for line elements. This is now default in edge-to-surface contact.\n* Added support for body forces for elastic-truss domain.\n\nBug fixes:\n* Removed update of rigid nodal velocities in FERigidSolver::PrepStep since it was incorrect.\n* Initialized m_rt material point member when created.\n* Made sure augmentations of prestrain constraint are not done when tolerance is zero.\n* Allowed prestrain gradient to work with shell domains.\n* Fixed issue with rigid solver to improve convergence\n* Made sure shell bottom flag is set in initialization.\n* Fixed bug in initialization of FEInitialVelocity and FEInitialShellVelocity.\n* Fixed plugin paths in Windows scp-to-repo script [skip ci]\n* Initialized m_ksi and m_ct parameters of FEFluidMovingFrameLoad to fix crash on Windows.\n* Fixed errors in evaluation of stress and tangent of \"HGO unconstrained\".\n* Fixed issue with reading xml tag values that are space-delimited.\n* Fixed issue with new FEPlotIdealGasPressure plot variable.\n* Fixed crash on Linux caused by buffer overflow in snprintf call\n* Fixed typo in ParseSurfaceData when reading vec2d data.\n* Fixed sprintf deprecation warnings by using snprintf\n* Fixed issues with plotting specific strain energy\n* Fixed plotting of specific strain energy when density is zero\n* Fixed crash due to recent change in linear solver cleanup.\n* Fixed issues with remodeling solids that belong to a solid mixture. Finalized implementation of strain energy density and specific strain energy for solid components of a mixture\n* Linear solver is not deallocated in Deactivate since this may cause issues when resetting model.\n* Made minor changes to multiphasic domains to properly initialize sbmrmin and sbmrmax\n* Fixed issue with material point tree when using elastic mixtures.\n* Added serialization for FESymmetryPlane.\n* Now ignoring some white space characters when reading tag values.\n* Fixed dynamic cast issues when writing discrete element force log data.\n* Fixed issues with processing edges from feb4 input file.\n* Removed mentioning of model title since that parameter no longer exists.\n\n\n===========================================================================\n\n R E L E A S E   4.6   3/29/2024\n\n===========================================================================\n\nNew Features:\n* Two new rigid constraints were added to prescribe the rotation of a rigid body: rigid_rotation_vector prescribes the rotation via a rotation vector. The rigid_euler_angles prescribes the rotation via Euler angles.\n* Added \"moving frame\" body load, which can be used to emulate the effects of being embedded in an accelerating and rotating coordinate system.\n* Added new weak bond recruitment functions for reactive viscoelastictiy, including Weibull, log-normal, quintic, and gamma. Added new damage CDF function gamma.\n* Added filtering capabilities for RVE generations, RVE bonds, RVE recruitment and RVE strain, to plot these variables for specific solid mixture constituents\n* Added some initial support for body forces for beams. (Will only work with constant body forces.)\n* Added \"field\" plot variable, which can be used to output any field variable. Field variables are the internal solution variables that FEBio solves for. The available field variables depend on the physics module. \n* Added \"rotation\" plot variable, which writes the nodal orientation of beam elements.\n* Added Lagrange multiplier method to linear constraints in FEAugLagLinearConstraint\n\nImprovements:\n* Array variables (e.g. solute concentrations) are now exported as FIELD variables in VTK export.\n* Added units to rigid force and mixture normal traction\n* Added option to use solute/sbm name in concentration dofs.\n* For dynamic analysis, the initial accelerations are now calculated based on the applied loads at time 0. This may improve the accuracy and convergence for dynamic problems. \n* Added initial values for some of the discrete Hill material parameters.\n* Modified mixture stress plot variable so that you can specify the material as well as the solid component.\n* Added additional plot variables for discrete element length and direction.\n* Added option to turn off deprecation warnings. These are now turned off for older file formats.\n* Simplified implementation of (uncoupled) fiber-exp-linear a bit.\n* Costly evaluations of the zero stiffness matrix contributions of constant body forces are now avoided, which may significantly speed up models that use constant body forces.\n* Discrete domains are now processed after they have been read in, which is more efficient.\n* Improved memory management of the dump stream used during serialization.\n* Modified fiber-exp-linear models to optionally enforce continuity of the strain energy density and elasticity when the user sets c3=0.\n* Changed default value of tolerance parameter to 0.1 in FEThermoFluidPressureLoad\n* Added units to 'orthotropic elastic' and 'orthotropic CLE' materials\n* Modified 'recruitment user' to use 'function' instead of 'cdf' for the recruitment function.\n* Modified reactive viscoelastic and uncouple reactive viscoelastic materials to use 'bond recruitment' functions instead of 'damage CDF' functions. Modified the weak bond recruitment algorithm from published paper formula to accommodate strain reversal in cyclical loading.\n* Added support for writing FMT_MULT mesh data to plot file.\n* Added check to make sure that the value of int parameters is indeed a number. If not, an exception is thrown.\n* Changed type for FEThermoFluidPressureLoad from 'fluid pressure' to 'fluid pressure constraint'\n* Added additional scenarios for material test.\n* Improved calculation of incomplete Gamma function needed for the evaluation of the Malkin reduced relaxation function. Limited the range of the Malkin exponent to be >= 1\n* Plotfile is now created after model has been initialized. This allows some of values calculated during initialization to be stored in the plot file (e.g. initial discrete element force).\n* Now using full Newton in tangent diagnostic, which seems to work a bit better.\n* Updated FEThermoFluidPressureLoad to include penalty, augmented Lagrangian, Lagrange multipler methods.\n\nBug fixes:\n* Fixed bug in contact potential initialization.\n* Fixes some issues to the velocity calculations in the biphasic and multiphasic solvers.\n* Fixed several issues with rigid body dynamics.\n* Fixed issue with surface data map that caused crash with cold restart.\n* Fixed problem with processing const surface data. Also provided more accurate error message when bc type is not recognized.\n* Fixed additional issue with reading old 2.5 files.\n* Fixed some issues with serialization of some material point data classes.\n* Fixed issue with merging maps for vec3d mesh data.\n* Fixed issue that was preventing old 2.5 files from running.\n* Fixed the reactive viscoelastic material response when weak bond recruitment is included, so that the response remains physical and meaningful during strain reversal, such as cyclical strain or cyclical stress\n* Fixed and updated several material parameter units.\n* Fixed strain energy density calculation for fiber-exp-linear when a non-zero c3 is specified by the user, to enforce continuity of the strain energy density.\n* Fixed bug that caused a crashed when plot file type was not initialized.\n* Fixed problem with restart when using VTK plot files.\n* Fixed some problems with serialization of model parameters.\n* Fixed cause of crash when using body forces with the rigid shell domain.\n* Fixed FEThermoFluidPressureLoad to make prescribed pressure spatially variable\n* Fixed implementation of nonlinear constraints in the fluid solver, thermofluid solver, and polar fluid solver.\n* Fixed issue with reading body loads that was always activating the loads, regardless of what step they are defined in.\n\nDocumentation:\n* Added description of dump_stride command line option and made some minor formatting changes to the febio command line section.\n* Updated user manual entry for uncoupled fiber-exp-linear and corrected formula for C6 in user manual .\n* Modified User Manual to describe continuous strain energy density for fiber-exp-linear and uncoupled version.\n* Updated User Manual to describe optional enforcement of strain energy density and elasticity continuity in fiber-exp-linear materials.\n* Minor edits to nonlinear constraints section of Theory Manual, such as adding equations numbers.\n* Updated Theory Manual to display representative continuous relaxation spectra and corresponding reduced relaxation functions\n* Fixed typo in Holmes-Mow material.\n* Updated theoretical description of nonlinear constraint enforcement.\n\n\n===========================================================================\n\n R E L E A S E   4.5   1/16/2024\n\n===========================================================================\n\nNew Features:\n* A simple beam formulation has been implemented in FEBio. The stress-strain relation is linear, but allows for large deformations. It can be used in both quasi-static and dynamic analysis.\n* Added option to stiffness_test diagnostic to output the actual and approximate stiffness matrices.\n* Added output_stride option to control data output frequency.\n* Added support for body loads to linear trusses. \n* Added support for exporting stresses for linear trusses.\n\nImprovements:\n* Made parameters of muscle and tendon materials mappable.\n* Added unit specification to offset parameter in sliding-elastic contact interface\n* Added check to see if XML writer failed to create the file.\n* Modified \"force\" parameter definition of the surface \"force\" load so that it shows units and cannot be assigned a load controller.\n* Added units and initial values for \"isotropic elastic\" material.\n* Added check in reading Elements section of input file to make sure element IDs are increasing.\n* Added support for writing element data with NODE format to VTK file (as point data).\n\nBug fixes:\n* Fixed some bugs in node-element list and element-element list classes.\n* Plot file now stores the FEFacetSets, not the FESurfaces because the latter can change in restarts, which could corrupt the plot file.\n* Fixed typo when reading rigid bc from 2.5 format. Also added check when setting parameter value to avoid crash.\n* Fixed bug in FindSSI function that assumed one-based node indexing.\n* Fixed issue with beam's reference frame. Line searches can now be used with beams.\n* Fixed minor issue in output of shell directors.\n\nDocumentation:\n* The plot file format is now described in Appendix D of the FEBio user manual.\n\n\n===========================================================================\n\n R E L E A S E   4.4   11/15/2023\n\n===========================================================================\n\nNew Features:\n* FEBio can now output directly in the VTK file format. (Use <plotfile type=\"vtk\"> to output febio results to a VTK file instead of the standard FEBio plot file.)\n* Added Yeoh constitutive model for uncoupled solid materials\n* Implemented dynamics for linear truss elements.\n* Added reaction rate for solutes modeled as SBMs for consistency with constant reaction rate for SBMs.\n* Added log variables \"EulerX\", \"EulerY\", and \"EulerZ\" for exporting rigid body Euler angles to the log file.\n\nImprovements:\n* Expanded description of building plugin on windows in developer's manual.\n* Added check for valid rigid body ID in rigid initial conditions.\n* Modified FEPrescribedActiveContractionFiber so that it works better with FEBio Studio.\n* Added check for nodal IDs\n* Updated theory for tied-fluid interface\n* Made output of \"damage\" plot variable consistent with output of \"D\" log variable.\n* Nodal IDs are now stored on the mesh when reading feb files and written to the plot file.\n* Improved calculation of multiphasic tangent stiffness by accounting for tangents of referential solid volume fraction with respect to strain. \n* Updated documentation to include description of Yeoh material\n* Added units to material parameters of Holzapfel-Gasser-Ogden materials\n* Added support for creating node sets from part lists when reading the febio input file.\n* Added check to ensure that the parent material of \"perm-Holmes-Mow\" is biphasic or multiphasic.\n* Added initialization of the \"E\" parameter of the linear spring material.\n\nBug Fixes:\n* Fixed issue with restarts that use surface, domain, or model log data.\n* Fixed restart issue for prescribed normal displacement.\n* Fixed some serialization issuess with solutes and tied multiphasic interface.\n* Fixed bug with calculation of stv parameter in FEActiveFiberStress and FEActiveFiberStressUC\n* Fixed issue with \"cylindrical\" fiber generator.\n* Fixed bug with selection of solute id for Nims reaction rate.\n* Fixed bug where exact_eigen now returns correct eigenvalues for a diagonal matrix\n* Fixed typo in user's manual for coupled MR.\n* Added dllexport to some fecore debug functions.\n* Fixed issue with old solid solver for dynamic problems.\n* Fixed bug with initialization of elastic fiber materials.\n* Fixed issue with output of contact gap log variable for node-on-facet contact interface.\n* Fixed couple bugs in \"tied-multiphasic\" contact interface.\n* Fixed issue with log output of contact pressure (issue #71).\n* Fixed issue with serialization of item lists and domain maps.\n* Fixed issue with serialization of mesh nodes.\n\n\n===========================================================================\n\n R E L E A S E   4.3   8/30/2023\n\n===========================================================================\n\nNew Features:\n* The \"CG-solver\" now supports preconditioners (#67)\n* Added fluid supply function to biphasic-FSI materials. \n* Added Starling fluid supply constitutive model for biphasic-FSI fluid supplies.\n* Implemented nrrd image reader.\n* Added support for storing global data in plot file. By default, PID controller values are now stored to the plot file.\n* Added \"mesh_data\" plot variable, which allows storing mesh data in the plot file.\n\nImprovements:\n* Added support for reading part lists from feb4 file.\n* Improved error detection in fluid dilatation calculation of FERealVapor.\n* Added check for node ID numbers in linear constraints.\n* Updated default analysis type for biphasic analyses to be TRANSIENT.\n* The \"m\" parameters of Ogden materials now supports maps.\n* Added support for reading vector of strings as xml tag.\n* Modified default settings for biphasic-solute and multiphasic analyses to be TRANSIENT (instead of STEADY-STATE).\n* Added units for coefficients c1 and c2 of Mooney-Rivlin materials that were missing them.\n* Added units to velocity in fluid normal velocity.\n* Added documentation on CG-solid solver.\n* Added error message when a fixed constraints has no dofs fixed.\n* Now, storing initial line coordinates of line object.\n* Updated some plot variables that were not thread-safe.\n* Added support for domain data stored as FMT_NODE.\n* The Surface-to-surface map is now evaluated at the nodes so that it can be replicated more accurately in a plot variable.\n* Updated error message when the fiber property of a material cannot be found when parsing ElementData section.\n* Added support for .fiber variable in ElementData. (which allows the \"old\" way of assigning user fiber data to mixture components.)\n\n\nBug Fixes:\n* Fixed bug in assembling stiffness of linear constraints.\n* Fixed bug in FEBioPlotFile::Open.\n* Fixed bug with assigning maps to array parameters.\n* Fixed issue with model reset.\n* Fixed issue with attributes getting processed twice.\n* Fixed issue with default name for line objects.\n* Fixed issue with initialization of rigid body COM so the correct values are available for PID controllers that reference these parameters.\n* Fixed bug in \"fluid surface force\" plot variable to update surface area and surface normal at each time point, producing correct results for fluid-FSI problems. Fluid problems not affected.\n\n\n===========================================================================\n\n R E L E A S E   4.2   6/20/2023\n\n===========================================================================\n\nNew Features:\n* A fluid-solutes module has been introduced, for fluid analyses with solute transport and reactive processes.\n\nImprovements:\n* Updated description of porous neo-Hookean material to indicate that solid volume fraction is inherited from parent material if biphasic or multiphasic\n* Exporting additional symbols from febiorve (to make them available to plugins on Windows).\n* Added out-of-bounds check to ImageMap::gradient.\n* Modified multiphasic framework so that solutes that represent solid-bound-molecules do contribute to the referential solid volume fraction of the multiphasic mixture, by modifying FEMultiphasic::SolidReferentialVolumeFraction. Also updated order of initialization of material points in FEMultiphasicSolidDomain::InitMaterialPoints and FEMultiphasicShellDomain::InitMaterialPoints.\n* Updated User Manual to improve description of elastic fluid materials and to explain that solutes that represent solid-bound-molecules can now contribute to the referential solid volume fraction\n* Deleted deprecated FEPoroTraction files\n* Deprecated FEMultipihasicFluidPressureLoad for versions > 4.0. Changed FEMulatiphasicFluidPressureBC description to \"actual fluid pressure\" to avoid conflict with \"fluid pressure\" BCs in fluid, fluid-FSI and fluid-solutes analyses. Minor fix to FEBioFluidSolutes.cpp.\n* Added FEMultiphasicFluidPressureBC as a BC that supersedes deprecated FEMultiphasicFluidPressureLoad.\n* Added plot variables for various strain measures derived from the growth tensor of a kinematic growth material. Updated the kinematic growth material so that it correctly stores the elastic and growth deformation gradients. Cleaned up the method for creating material point data for the kinematic growth material. Fixed the UpdateSpecializedMaterialPoints function of FEKinematicGrowth to properly replace the deformation gradient calculated from the deformation with the elastic deformation gradient evaluated from the multiplicative decomposition. This provides the correct deformation gradient for calculation of strain-dependent stress, permeability, etc., and associated tangents.\n* Added description of the \"contact potential\" formulation ot user manual.\n* Fixed bug in FEKinematicGrowth that prevented proper integration with solid mixtures.\n* Updated FEPoroTraction to FEMixtureNormalTraction. Modified FECarterHayes to accept sbm specification as name instead of number. Added units to FEInitialConcentration. Added long names to parameters of FESolidBoundMolecule. Minor tweaks to serialization of FETiedMultiphasicInterface.\n* Added default calculation of divergence of body force from trace of stiffness (its spatial gradient).\n* Updated body force class to include a function for the calculation of the divergence of the body force. Updated derived classes to perform that calculation. Fixed the calculation of the body force stiffness matrix in the fluid-solutes domain, using divergence of body force.\n* Added NodeNormal and UpdateNormals functions to FESurface.\n* Created version of fluid RCR boundary condition for fluid-solutes solver. Added units to parameters in FEFluidRCBC, FEFluidRCRBC, FEFluidRCRLoad, FEFluidResistanceBC, FEFluidResistanceLoad, FEFluidSolutesResistanceBC.  Updated new formulation for fluid-solutes module. Modified sequential solver in fluid-solutes module to ignore concentration residual when ctol=0. Updated solutes back flow stabilization for new formulation of fluid-solutes solver.\n* Added units of length for gap tolerance and search radius fields for several contact interfaces.\n* Added dynamic analysis capability to EAS and ANS shell domains using generalized-alpha method.  Added units to initial velocity condition. Modified secant stress and secant tangent routines to return material stress and elasticity tensors when requested.\n* Added FEBIOMECH_API to FEFiberMaterialPoint (for plugins on Windows).\n* Renamed FEInitialFluidPressure to FEInitialMixtureFluidPressure to avoid naming conflict with FEBioFluid library.\n* Added initial fluid pressure in FEBioFluid and separate version in FEBioFluidSolutes.  Added FEFluidSolutesResistanceBC class. Changed call to Dilatation virtual function in FEElasticFluid to exclude solute concentration.  Added units to various initial conditions.\n* Added default load curve flags on nodal force and nodal load parameters.\n* Added plot variables for fluid relative Reynolds number and solute relative Peclet number.\n* Added new fiber models FEFiberCDF and FEFiberCDFUncoupled.  Added plot variable for 'deviatoric elasticity' which saves the DevTangent of uncoupled materials.\n* Simplified evaluation of FEPorousNeoHookean::ReferentialSolidVolumeFraction.\n* Changed serialization from FEPorousNeoHookean.\n* Update FEPorousNeoHookean to be more robust to inclusion in an elastic mixture which is the solid part of a biphasic/biphasic-solute or multiphasic material.\n* Added FEStiffnessDiagnostic.\n* Added set command to febio prompt.\n* Added support for feb4 step section in restart input file.\n* Now storing covariant and contravariant vectors in shell element instead of recomputing each time.\n* Minor changes to omp logic in FEExplicitSolver::DoSolve.\n* Improved serialization for fluid-related solvers, materials, boundary conditions and domains.\n* Initialized L0 in bool FEElasticTrussDomain::Init().\n* Removed serialization of m_dof in FESurfaceLoad since it's already serialized in FEModelLoad.\n\nBug Fixes:\n* Fixed issue with name attribute of ElementData tag.\n* Fixed crash if no analysis steps are defined.\n* Modified how attributes are processed in FEBioMeshDataSection4::ParseElementData so that invalid attributes can be identified.\n* Modified how the fibers are defined for the HGO-coronary material so that they show up correctly in febio studio.\n* Fixed some issues with setting the preconditioner for the cg, fgmres, and bicgstab iterative solvers\n* Fixed issue with incomplete Cholesky and CG iterative solver.\n* Fixed error in description of CG iterative solver.\n* Fixed incorrect values for output_level.\n* Fixed issue with serialization of data maps.\n* Fixed bug in plotting solid mixture nested variables, when plotting component that doesn't exist.\n* Fixed serialization of FENodalLoad.\n* Fixed problem with copying valuators.\n* Fixed issue with initialization of load controllers added in restart input file.\n* Fixed bug in serialization of rigid nodeset.\n* Fixed issue with rigid shell initialization.\n* Fixed bug with setting dump file name.\n* Fixed several serialization issues.\n* Fixed serialization of FEMathValuator.\n\n\n===========================================================================\n\n R E L E A S E   4.1   3/27/2023\n\n===========================================================================\n\n* Fixed issue with exporting log data \"contact pressure\" and \"contact gap\".\n* Added element ID in Holmes-Mow permeability error message.\n* Fixed problem with initialization of FERigidBC in multi-step analysis.\n* Added all node, element, and facet sets defined in the input file to the plot file.\n* Fixed issue with linear constraints for multi-step problems.\n* Added config option (show_warnings_and_errors) to suppress warnings and error messages.\n* Added parameter to param_run task to generate standard febio output files.\n* Hide \"clamp_shells\" parameter in FERigidNodeSet since it has no effect currently.\n* Now printing const scaled value for prescribed FEParamDouble values.\n* Some changes that address minor inconsistencies between the BCs that replace their deprecated \"load\" versions.\n* Fixed error in description of Hill discrete material.\n* Added enums to solve_strategy parameter of FEFluidSolutesSolver.\n* Minor change to console update.\n* Corrected unit of P1 muscle material parameter in user manual.\n* Fixed issue caused when febio prompt recieves EOF on Linux.\n* Log data is now also evaluated and output at time 0.\n* Fixed issue with missing material point data in multigeneration material.\n* Fixed issue with initialization of FERealGas and FEOsmCoefManning.\n* Fixed issue with initialization of FEMappedValueVec3 class and FEElasticFiberMaterial_T.\n* Added check for invalid tags when reading rigid body force.\n* Replaced NANDetected with NANInSolutionDetected and NANInResidualDetected.\n* Minor formatting change when printing vec3d mapped parameters.\n* Changed range for FEDamageFiberExponential k2 parameter.\n* Fixed bug in allow-mixed-bcs logic in multistep analysis.\n* Added support for target rigid moments.\n* Added support for referencing domain parameters. This can now be used in the param_run task.\n* Fixed bug in periodic boundary related to projection search radius.\n* Updated reaction rates (const, expSED, Huiskes and Nims) to use FEParamDouble instead of double for material properties.\n* Fixed initiation of referential solid volume fraction in multiphasic solid and shell domains.\n* Added support for reading linear constraint offset and fixed issue related to offset.\n* Added plot variable for specific strain energy in multiphasic materials (to account for solid remodeling).\n* Added rigid body names to echo input.\n* Updated User Manual to described fixed pressure, prescribed pressure, fixed concentration, prescribed concentration, fluid pressure (for multiphasic analyses), and add description of update flag to solute natural flux surface load\n* Added FEMultiphasicFluidPressureLoad to allow prescribing the actual fluid pressure in a multiphasic analysis. Modified FESoluteNaturalFlux to provide better numerical convergence for this surface load.\n* Moved up initialization of rigid system so PID load controllers can reference rigid body parameters.\n* Fixed couple issues with explicit solver: initialized FETimeInfo and fixed bug in using prescribed rigid displacements.\n* Fixed issue with shell dofs in explicit solver. Made some minor performance improvements.\n* Renamed FEPrescribedFluidPressure to FEPrescribedNodalFluidPressure to avoid naming conflict.\n* Fixed bug in parsing vector<int> from input file.\n* Fixed bug in parsing input file comments that contain '-' characters.\n\n\n===========================================================================\n\n R E L E A S E   4.0   1/5/2023\n\n===========================================================================\n\n* FEBio4 is a major upgrade from FEBio3. Although it will continue to read most older files (version 3, 2.5, 2), a new format (version 4) is the preferred format. Files can be converted with FEBio Studio 2. Most of the important changes in this update are under the hood and improved the consistency between the internal representation of the febio model, the input file format, and the model representation in febio studio 2. The changes facilitated the direct integration of febio into febio studio. See the release notes of febio studio 2 for additional information regarding this integration. \n* Fixed sign issue with evaluating body loads on rigid bodies. (Only affects the output of the rigid body reaction forces.)\n* Added mat_axis property to solid mixtures, mostly to better support older file formats.\n* Fixed a crash when an error was detected in the update of the contact potential.\n* Added some error checking for -import command line option.\n* Fixed bug in evaluation of FENodalFluidFlux\n* Increased precicion when writing floating point values.\n* Fixed some issues with parsing comments from the xml file.\n* Rigid wall adds surface to mesh in feb3 import.\n* Rigid node sets can now be applied in multi-step analyses.\n* Fixed crash in parabolic map.\n* Fixed several issues with mesh data generators.\n* Added support for redefining load contollers in restart file format 3.\n* Fixed issue with BSpline initialization.\n* Added property validation for properties defined via empty tags.\n\n\n===========================================================================\n\n R E L E A S E   3.8   12/12/2022\n\n===========================================================================\n\n* Several issues with the cold restart capability were addressed. \n* An issue was caused that could cause a crash when rigid rotational dofs are coupled to a biphasic element.\n* Added dump stride parameter, which allows users to specify how often the dump file is written.\n* Added \"contact_traction\" log variable.\n* Added names of parent when printing model parameter values.\n* Suppor for the MKL-DSS solver was added, which seems to work better for certain types of models than the default pardiso solver.\n* Added gap offset option to facet-2-facet sliding.\n* Fixed issue with plot output logic when first step does not request output.\n* Fixed bug in \"element center of mass\" plot variable.\n* Parameters of \"orthotropic elastic\" material can now be mapped.\n* Added \"discrete element signed force\" plot variable.\n* Added log variables for discrete element force components.\n* Fixed bug in compressible Gent material.\n* Added \"facet area\" log variable.\n* Added \"deformation gradient\" plot variable.\n* Fixed issue with initializationg of FEParamMat3d parameters.\n* Added FEMathExpression that refactored some of the logic previously in FEMathValue. FEMathValueVec3 now also uses FEMathExpression so that it can support maps.\n* Added prescribed fiber active contraction models. (Similar to uniaxial, but fiber direction can be defined by user.)\n* Added support for mixture stress log variable.\n* The \"mixture stress\" plot variable now also works for uncoupled mixtures.\n* Fixed calculation of fixed charge density in multiphasic and triphasic materials (in FEBio3)\n* Added error exception in Holmes-Mow permeability to catch pathological case.\n* Added \"contact area\" log variable.\n* Updated Theory Manual to fix calculation of fixed charge density in reactive multiphasic materials that use charged solid-bound molecules\n* Added description of fluid RCR surface load in User Manual\n* Fixed the fluid RCR surface load and made it ready for release. Simplified the fluid resistance to be consistent with fluid RCR.\n* Allow the initial referential mass density of solid-bound molecules, m_rho0, to be a FEParamDouble.\n* Updated FEMembraneMassActionForward and FEMembraneMassActionReversible to use areal concentration of solid-bound molecules. Updated FEMembraneReactionRateIonChannel to accommodate solid-bound molecules in the calculation of the reaction rate.\n* Added exponential damage CDF (useful for reactive viscoelastic materials with exponentially increasing weak bond fractions with increasing strain)\n* Added aggressive interpolation type as user-specifiable option in BoomerAMG solver.\n* Modified FEFiberPowLinear and FEFiberPowLinearUncoupled to use FEParamDouble material coefficients.\n* Updated FEIdealGasIsentropic and FEIdealGasIsothermal to run properly in current version of FEBio.\n* Point Source Updates\n* Fixed bug in FEContinuousFiberDistribution::Stress\n* Fixed issue with exporting plot parameter for mapped parameters of nodal loads.\n* Added support for X, Y, Z in generic hyperelastic material.\n* Restricted nodal reaction forces plot variable to non-rigid materials.\n\n\n===========================================================================\n\n R E L E A S E   3.7   6/15/2022\n\n===========================================================================\n\n* Added support for setting solver parameters via restart input file.\n* Clarified error message when a property fails to initialize.\n* Improved speed of initialization of FSI problems.\n* Aded lung material and documentation.\n* Added warning that nonzero value for dtmax will be ignored when must points were defined.\n* Fixed some issues related to reset of contact problems. This bug could have affected outcome of parameter optimization for certain contact problems. \n* Modified the reset test to print out some useful stats.\n* Allowed export to specific domains for domain plot variables. Previously this was only supported for some surface plot variables. \n* Made a change to HGOCoronary so that fiber vector plot variable works for this material.\n* Contact potential now outputs nonzero values for contact pressure and contact area.\n* Implemented parameter scaling option for constrained levmar. To enable parameter scaling, set the control parameter \"scale_parameter\" to 1. \n* Box constraints are now enforced explicitly in constrained levmar. \n* Added model log data classes. Added log variable for printing solution vector norm.\n* For prestrain materials, the density now takes the prestrain into account.\n* Added Shenoy material and documented it in user manual.\n* Local coordinate system is now taken into account in FEHGOCoronary.\n* Updated reactive viscoelastic materials to properly account for damage when the elastic bond material is modeled with either a reactive damage or a reactive fatigue material.\n* Added integral of solute concentration log data.\n* Added log output option for printing integral of sbm concentration.\n* Fixed serialization of contact material point data.\n* Fixed issue with reallocation of linear system during restart.\n* Implemented missing Serialize functions for FELinearElasticFluid and FENonlinearElasticFluid.\n* Added serialization of FEBiphasicFSIDomain3D::m_btrans.\n* Added missing serialization of generic hyperelastic materials.\n* Fixed serialization of FEBearingLoad.\n* Fixed restart and serialization issues with linear constraints.\n* Fixed serialization issues for model parameters. Added missing serialization of FEInSituStretchGradient.\n* Added missing Serialize to FEReactivePlasticity.\n* Minor change to FEUT4Domain::Create to avoid problem with restart.\n* Added serialization for FEMathFunction.\n* Added missing serialization of FEBiphasicSolidDomain::m_nodePressure.\n* Added \"test\" solver, which doesn't solve anything, just returns zeroes. (Added to facilitate debugging of large problems.)\n* Fixed issue with allocation of default \"elastic\" property of FEFluid.\n* Implemented HGO-coronary material.\n* Added \"facet area\" plot variable.\n* Added small clarification about elasticity of some fiber models at strain origin.\n* Updated User Manual to include description of solute natural flux, CDF power, and plot variable RVE strain\n* Added new 'solute natural flux' surface load for biphasic-solute and multiphasic domains which prescribes a solute molar flux equal to (solute concentration)*(solvent volumetric flux) on a boundary, where concentration and fluid flux are averaged from the element underneath the selected boundary face. This allows a solute to naturally flow out of a boundary without the user having to prescribe a solute concentration or specific value of solute flux on that boundary.  Current implementation has a barebones stiffness matrix, may need to be fully populated in future implementation.\n* Minor cleaning up of reactive viscoelastic code.\n* Added a scaling parameter to FEDamageCDFPower.\n* E parameter of FEFiberPowLinearUncoupled a FEParamDouble.\n* Added description of auto time step algorithm for reducing the time step size in user manual.\n* Expanded user manual entry for fiber-exp-pow-linear\n* Fixed user manual entry for definition of formulas related to fiber-exp-pow-linear\n* Fixed bug in newly introduced fiber-exp-pow-linear, to enforce continuity of the elasticity at the exp-pow-linear transition.\n* Added FEGlobalVariable to prevent memory leak when adding global variables. Global variables are now also copied in FEModel::CopyFrom.\n* Added feature for developers, to plot scalar value of prescribed surface load.  Modified FEBearingLoad and FEFluidNormalVelocity to test it out.\n* Updated User Manual to describe new bearing load\n* Added functionality to prescribed a radial bearing load on a cylindrical surface (FEBearingLoad). Copied and adapted Quadric and QuadricFit functions from FEBioStudio to fit bearing surface and detect its axial and radial directions. Updated vec3d and matrix to reproduce missing functionalities that are available in their FEBioStudio equivalents. Updated surface element traits to include/return parametric coordinates of the surface centroid (in parametric space).\n* Added serialization of surface pairs.\n* Added operators for mat3d+/-/* mat3da and for mat3d+/-mat3ds operation\n* Added description of surface force load in User Manual\n* Added functionality for prescribing a force on a surface which is converted to a uniform traction.\n* Added description of fixed normal displacement constraint\n* Added functionality for prescribing a fixed normal displacement constraint.\n* Cleaned up FEForceVelocityMaterialPoint and provided missing default values for some of the material parameters.\n* Updated User Manual to describe the force-velocity-Estrada active fiber contraction model\n* Modified FERVEDamageMaterial to a working version\n* Fixed one more item to make FEDamageMaterialUC more consistent with FEDamageMaterial.\n* Added description of fiber-exp-pow-linear material\n* Added symbolic derivative for H (Heavyside function).\n* Modified FEDamageMaterialUC to be consistent with FEDamageMaterial. Replaced FEFiberPowerLinear (which was duplicate of FEFiberPowLinear) with FEFiberExpPowLinear, which uses exponential-power law in the toe region.\n* Deleted old (invalid) FEFatigueMaterial\n* Derived FEPolynomialHyperElastic from FEElasticMaterial instead of FEUncoupledMaterial, since this material won't work with the three-field formulation.\n* Fixed base class call in FEActiveFiberContraction::Init.\n* Added pre-stretch functions to FEElasticFiberMaterial to replicate capabilities previously implemented in FEElasticFiberMaterialUC\n* Added another example of parameter optimization specification in user manual. \n* Fixed trans iso materials that use active fiber contraction to not crash when active fiber contraction is not included.\n* Updated FETransIsoMooney-Rivlin and FETransIsoVerandaWestmann to use FEActiveContractionMaterial class to accommodate multiple active fiber contraction materials.\n* Derived FEActiveFiberContraction from FEActiveContractionMaterial.\n* Added Ana Estrada's model for force-velocity contraction of the cardiac muscle, superposed on a trans iso Mooney-Rivlin material.\n* Added new materials \"reactive fatigue\" and \"uncoupled reactive fatigue\".\n* Added check for repeated time coordinates in load curves.\n* Added \"infinitesimal strain\" plot variable.\n* Modified plot parameter so that it maps directly for FMT_MULT data maps without projecting to nodes.\n* Fixed bug in closest-point-projection and FESurfaceElement::facet_edge.\n* Added getter for FEDataMap to FEMappedValue.\n* Fixed some issues with the MDerive operator.\n* Fixed issue with evaluating math expression's derivatives.\n* Implemented (uncoupled) polynomial hyperelastic material.\n* Created missing UpdateSpecializedMaterialPoints function for FEBiphasic, FEBiphasicSolute, FETriphasic and FEMultiphasic materials.  Also updated Init() function for these materials.\n* Fixed output issue when reporting boolean load parameter values.\n* Modified plot logic so that for multi-step analysis, first state is written after first step activation.\n* Now checking if the contact interface is active when writing contact plot variables.\n* Added support for shells in \"density\" plot variable.\n* Fixed bug in FELogElemSolidStress.\n* Implemented support for shells in \"Lagrange Strain\".\n* The \"relative volume\" plot variable now also supports shells.\n* Fixed bad cast issue in FEMassDamping::force.\n* Added method to store and plot correct value of uncoupled pressure (for uncoupled materials), including when using 3-field formulation.\n* Added trace-free neo-Hookean material, which can be used in ground matrix of solid component of biphasic materials, for equivalence with Mooney-Rivlin material when comparing short-term biphasic and uncoupled elastic responses.\n* Fixed bug when passing material point array data in uncoupled elastic mixtures.\n* Added FEBondRelaxationMalkinDist continuous spectrum relaxation function with distortional strain-dependent time constants. Added FEDamageCDFPower power-law (pseudo-)damage CDF, which can be useful for specifying weak bond recruitment in reactive viscoelastic materials.\n* Fixed issues with CopyFrom for discrete domains.\n* Fixed bug reading dump level from cmd + dump at must-points (#37)\n* Added log variables for solid stress.\n* Added log output for permeability.\n* Renamed the \"general spring\" to \"nonlinear spring\"\n* Implemented new general spring material.\n* Added example for reactive plasticity with PFC math flow curve\n* Added regression coefficient (R2) as output of parameter optimization analyses (linear regression between y=fitted and x=data). Added ability to fit the y-value of points on point functions representing material parameters.\n* Added recruitment module to reactive viscoelastic and uncoupled reactive viscoelastic materials to further accommodate nonlinear viscoelastic response with increasing strain. Added plot variable RVE strain to report the strain measure used for recruitment. Extended Weibull CDF to incorporate location parameter.\n* Fixed bug with assignment operator of model parameters.\n* Fixed issue with allocation and serialization of discrete element sets.\n* Fixed bug in ParseBCFix when reading comma-separated list of dofs.\n* Added Appendix A in Theory Manual as review of tensor calculus\n* Added scale parameter to nonlinear spring.\n* Added log options to output discrete element stretch, elongation, force.\n* Added mesh_adaptor property to FEModel.\n* Added option to repeat must-points.\n\n\n===========================================================================\n\n R E L E A S E   3.6   2/18/2022\n\n===========================================================================\n\n* Added description of plot variables RVE generations, RVE reforming bonds, and effective friction coefficient\n* Updated description of plot variables in user manual.\n*\tDocumented the coupled and uncoupled Gent material.\n* Added new uncoupled Holmes-Mow isotropic elastic solid.\n* Updated Theory and User Manual to include description of uncoupled Holmes-Mow material\n* Added persistMatrix flag that cleans up linear solver and stiffness matrix after every time step. (This is useful for reducing memory requirements for homogenization problems.)\n* Added deviatoric strain energy density calculation for FEIncompNeoHookean\n* Fixed problem with appending plotfile with compression flag on.\n* Fixed memory leak.\n* Implemented missing functionality of \"prescribe_nodal_velocities\".\n* Updated Theory Manual to explain that uncoupled fiber formulations are non-physical and that uncoupled damage should not be used with fiber materials\n* Fixed issue with allocating FENodeSet that caused crash in restarts.\n* Fixed problem with serialization of mapped model parameters.\n* Added parameter list for FEUDGHexDomain.\n* Updated calculation of local fluid load support in sliding-biphasic contact interface to place theoretical upper bound on its value. Improves prediction of fluid load support over contact interface.\n* Updated User Manual and Theory Manual to define and include local fluid load support as a plot variable\n* Minor formatting change to Invalid Value error.\n* Mesh data isn't generated until after the feb file is read in. This allows that load data section no longer needs to be defined before mesh data sections. \n* Fixed bug in FESlidingInterface when using higher-order elements. \n* Added PK1 stress plot variable.\n* Fixed error in description of muscle material.\n* Updated User Manual to include description of parameters <emin> and <wmin> in reactive viscoelastic materials\n* Updated Theory and User Manuals to include a description of new relaxation functions implemented in FEBio and general theoretical background about continuous relaxation spectra\n* Cleaned up relaxation functions needed for reactive viscoelasticity (replaced double with ParamDouble for most of them).  Fixed Malkin relaxation function to employ two time constants tau1 and tau2. Fixed CSexp relaxation function to return 1 at t=0 without further calculations.\n* Removed ability to specify (weak) bond material as a scaled version of base (strong bond) material in reactive viscoelastic and uncoupled reactive viscoelastic, because of initialization conflicts. Removed constraint on specifying solid mixtures in both base and bond materials, now that a material point data array is used to distinguish material points of base and bond materials.\n* Cleaned up reactive viscoelastic material implementation, using material point array to differentiate base and bond materials.  Fixed small bug introduced in last commit.\n* Added missing CreateMaterialPointData function for FEContinuousFiberDistribution.\n* Added modified Bessel functions of the first and second kind, of orders 0 and 1.\n* Fixed issues with tri10 surface facets.\n* Added new reduced relaxation functions for reactive viscoelasticity (Malkin, Malkin-dist-user, CSexp, CSexp-dist-user). Added plot variable for rate of deformation tensor in solids. Added plot variables for mass fraction of reforming bonds in reactive viscoelasticity, strain energy density of strong and weak bonds, and deviatoric strain energy density for strong and weak bonds. Added initialization functions for bond relaxation materials that employ FEFunction1D objects for user-specified functions. Updated reactive viscoelasticity framework to reset fiber orientation of each weak bond generation based on fiber configuration at start of generation; created FEFiberMaterialPoint to store fiber pre-stretch tensor; updated continuous fiber distribution materials to set/reset fiber prestretch. Updated initialization of solid mixtures and uncoupled solid mixtures to prevent specification of mixture of mixtures. Modified FiberExpPowUncoupled to be consistent with FiberExpPow. Cleaned up reactive viscoelasticity materials to (a) update specialized material points in situ instead of within FEReactiveVEMaterialPoint; (b) prevent the specification of more than one mixture in these materials; (c) allow the weak bond material to be a scaled function of the elastic bond material (optional); (d) evaluate relaxation function using state of total strain at genesis time of weak bond generation (instead of relative strain); (e) properly set/reset fiber prestretch when applicable. Introduced FEScaledElasticMaterial and FEScaledUncoupledMaterial to allow item (c) in preceding sentence. Updated FETransIsoMooneyRivlin and FETransIsoVerondaWestmann fiber stress calculations to use public functions only.\n* Implemented symbolic derivative of pow.\n* Remove positivity constraint on Poisson's ratio v12 in \"orthotropic elastic\" material.\n* Fixed implementation of \"fluid flow rate\" plot variable for biphasic mixtures.\n* Added guidance section for dynamic problems to user manual.\n* Added error check for FERigidBodyFixedBC.\n* Fixed uninitialized member variable in FEMinMaxFilterAdaptorCriterion.\n* Fixed bug in coupled-trans-iso tangent and strain-energy-density calculation.\n* Added ramp parameter to FEConstPrestrainGradient.\n* Fixed issue with reading FEFunction1D object through loadcurves.\n* Added description of fiber exp-linear fiber models to user manual.\n* Fixed issue with reading nodedata when node set is defined implicitly via surface.\n* Added support for exporting vec3d mapped nodal data to plot file.\n* Plot stride now also respected when using FE_PLOT_USER1 plot level.\n* Updated User Manual to include description of pyra13 elements\n* Made some repairs to the CG-solid solver.\n* Minor edits to continuous damage section.\n* Added plot variable for \"rate of deformation\"\n* Added force and displacement norm to output for explicit solver.\n* Updated documentation on continuous damage formulation.\n* Added some documentation for homogenization.\n* Added new \"D2beta\" plot variable.\n* Param run now uses FEDataParameter for output variable. Added support for special domain log data that evaluates average and percentile of element data.\n* Implemented contact area for tied-elastic.\n* Implemented contact force for tied-elastic interface.\n* Refactored linear constraints. Added support for setting a loadcurve to the child dof's value parameter.\n* Cleaned up continuous elastic damage formulation.\n* Added PK2 stress log variables.\n* Added ':' as valid character for xml tag name.\n* Added '-' as valid character for xml tag name.\n* Fixed issue with beta plot variable and added psi0 plot variable for damage elastic fiber materials.\n* Fixed issue where incorrect termination status was reported.\n* Fixed HYPRE_Int typing issue in BoomerAMG function calls caused by recent change.\n* ́Added new bond relaxation function called relaxation-exp-pow, for reactive viscoelasticity.\n* Added SetResetFlag to FESBMPointSource.\n* Allow parameter optimization on the y value of vec2d parameters. This can be used to optimize individual points in the load curve description of a material response.\n* Fixed issue with output on CB_USER1 event.\n* Added CB_USER1 callback event, and FE_PLOT_USER1 plot option.\n* Added new parameter run feature, which allows users to run a model and set model parameters and output variables in separate input file.\n* Modified BoomerAMG function calls to re-type arguments\n* The RVE model now follows time stepping of macro model.\n* The micro material now uses RCI for solving RVEs.\n* Implemented RCI (Reverse Control Interface) for solving FE model.\n* Added additional check in FEVecPropertyT::SetProperty to make sure the correct class is allocated.\n* Made cp parameter of FEOgdenUnconstrained a mapped variable.\n* Added position of rigid bodies to default output for plot object.\n* Made coefficient parameters of Ogden materials variable. (issue #33)\n* Added mechanism for copying materials that have additional properties.\n* Added parameter to FECoreKernel::RegisterDomain to push a domain factory to the front of the list.\n* Added new bond relaxation models for reactive viscoelasticity (relaxation-exp-dist-user, relaxation-Park-dist-user, relaxation-power-dist-user). Fixed reactive viscoelastic materials by making them frame-invariant. Improved method of updating generations during iterative solution process. Fixed strain-dependent relaxation functions to depend on strain only at time of bond breaking. Added plot variables to save strain energy density in strong and weak bonds of reactive and uncoupled reactive viscoelastic materials.\n* Updated Theory and User manuals to version 3.5.1\n* Fixed initialization of newly introduced Prony bond relaxation function. Modified culling method in reactive viscoelasticity to prevent reduction in maximum number of generations. Added strain threshold for triggering new generation in reactive viscoelasticity.\n* Added reduced relaxation function for Prony series (can be used with reactive viscoelastic materials). Added safeguard in calculation of Fung relaxation function to prevent small negative values. Added plot variable for counting number of generations in reactive viscoelastic materials.\n* Improved method for culling generations in reactive viscoelastic materials.\n\n===========================================================================\n\n R E L E A S E   3.5.1   9/28/2021\n\n===========================================================================\n\n* Added \"cutback\" parameter that controls the reduction factor when using aggressive time stepping. (default is 0.5)\n* A bug was fixed in the \"kinetic energy density\" plot variable. \n* Added domain log variable \"volume\" for calculating domain volume.\n* An issue with tangent evaluation of Mooney-Rivlin and Veronda-Westmann materials was fixed. This fix may improve convergence of nested materials (e.g. visco-elastic, mixtures, etc.) that use these materials as a component. \n* Updated Theory and User Manuals to describe parameter lam0 in fiber-exp-pow, and new material fiber-natural-NH\n* Added fiber with natural neo-Hookean response. Modified fiber-exp-pow to allow tensile response to engage at any stretch ratio greater than unity. Modified parameters alpha and beta of fiber-exp-pow to FEParamDouble.\n* Added \"DC Octahedral natural strain\" as a damage criterion.\n* Fixed a bug in closest-point projection algorithm.\n* Fixed some issues with restart and added a new version for restart input file that uses the 3.0 format.\n* Updated User Manual to reflect specification of plastic flow curve in reactive plasticity and reactive plastic damage materials\n* Fixed small bug in newly introduced user-defined plastic flow curve. Changed name of parameter emin to e0 in math plastic flow curve.\n* The definition of reactive plastic and reactive plastic damage materials was modified. It now requires the definition of a \"flow_curve\" property that. This change is incompatible with the previous version so older files will need to be updated. \n* Implemented \"PK2 stress\" plot variable, which exports the second Piola-Kirchoff stress.\n* updated User Manual to use Young's modulus and Poisson's ratio as material parameters for natural neo-Hookean material\n* Changed the material parameters of natural neo-Hookean from bulk and shear moduli to Young's modulus and Poisson's ratio.\n* Added left stretch tensor as plot and log variable\n* Added left Hencky plot and log variables to User Manual\n* Added ability to specify element type in shell domain section.\n* Modified shell domain classes to allow using secant stress and secant tangent calculations for shell domains.\n* Modified symmetry plane constraint to work properly if symmetry plane includes some nodes that belong to a rigid body (such as a rigid interface).\n* Changed type strings for Kamensky materials.\n\n===========================================================================\n\n R E L E A S E   3.5   8/30/2021\n\n===========================================================================\n\n* Implemented new \"Kamensky\" material, an isotropic Fung-type material from Kamensky, CMAME 2018.\n* Added log variable for element volume (variable name \"V\").\n* Fix issue with penalty computation and plot (github issue #30)\n* Added ability to output log data for octahedral plastic strain, right stretch tensor components, and right Hencky strain tensor components. Added ability to add plot variables for right stretch tensor and right Hencky strain tensor.\n* Fixed bug in FEBioOpt/FEDataSource where node_data variables used as optimization parameters were not being read correctly. Added ability to specify node_data variables as ordinates in parameter optimization.\n* Updated reactive plastic damage material to (a) include elastic damage of non-yielding bonds, and (b) keep better track yielded bond fraction in the iterative solution process. \n* Fixed recently introduced bug in the plotting of intact bond fraction in reactive plastic damage materials.\n* Added new material \"fiber-Kiousis-uncoupled\".\n* Added \"contact potential\" contact interface and \"mass damping\" body load features.\n* Removed c1 > 0 restriction from Mooney-Rivlin material.\n* Added surface_data and domain_data log features. Added support for using these features in optimization module.\n* Added yield flags in reactive plasticity material to allow resetting yielded bond mass fraction to zero during an iterative process.\n* Fixed some issues with init and serialize of reactive plastic damage material that were causing problems with optimization.\n* Added flip_primary, flip_secondary, shell_bottom_primary and shell_bottom_secondary to sliding-biphasic interfaces (included mixed).\n* Changed default format for FEParamMat3ds to be ITEM.\n* Made additional changes to allow mapped FEParamMat3ds variables.\n* Modified FEEllipsoidalFiberDensityDistribution to accept mappable semi-principal axes for ellipsoid.  Fixed spelling in class name.\n* Add ability to map 'mat3s' symmetric 3x3 matrix in MeshData section.\n* Updated perm-ref-ortho to use all mappable  material parameters.\n* Modified sliding-elastic, sliding-biphasic, sliding-biphasic-mixed to use search_radius as length value instead of scale factor, for consistency with other sliding and tied interfaces.\n* Fixed bug in \"fluid normal velocity\" load where rim pressure was not initialized properly.\n* Updated \"fluid normal velocity\" load to set parabolic velocity distribution to zero only on sections of the rim where the user has prescribed zero fluid velocity.  This change makes it possible to prescribe parabolic velocity profiles on cross-sections with symmetry planes, and on rectangular faces.\n* Created linear and nonlinear elastic fluid materials to allow alternating between linear and nonlinear pressure-dilatation models.  Default setting is linear.  Updated fluid-FSI traction and biphasic-FSI traction algorithms to better identify orientation of surface normal of the interface relative to solid elements with fluid-FSI or biphasic-FSI materials.\n* Reverted constitutive model for fluid pressure to linear model p = -k*ef where k=bulk modulus, because nonlinear model broke the FSI problems from the published paper. \n* Improved efficiency of calculation of octahedral shear strain. Reactive-plastic damage material now works with erosion tool.\n* Added Bingham fluid.\n* Renamed some biphasic-FSI plot variables to be consistent with biphasic plot variables. Added missing nonlinear and surface pair constraints in fluid, fluid-FSI, fluid-solutes and multiphasic-FSI solvers.\n* Added several new plot variables related to continuous damage formulation. Also added a \"mixture stress\", which can be used to look at stress of the mixture components.\n* Allowing some zeroes on lumped mass matrix for explicit solver (as can be the case if corresponding dof is fixed).\n* Added \"linear\" flag to traction load, similar to pressure load.\n* Fixed issue with under-timing model updates.\n* Fixed bug in reporting timing info for multistep analyses.\n* Added support for shells to explicit solver.\n* Fixed issue with initializing contact surfaces for febio 2.0 format.\n\n===========================================================================\n\n R E L E A S E   3.4   5/17/2021\n\n===========================================================================\n\n* This version has an updated EULA (EULA version 4). This EULA adds a PRIVACY clause that details how and what information the software (i.e. FEBio Studio) communicates with an external server. \n* On Windows, this version was built with a newer version of Visual Studio (Visual Studio 2019). As a result, a new SDK has been released and it is highly recommended to rebuild any plugins against this new version. \n* A bug was fixed for shells that are directly attached to rigid bodies (i.e. shells that share nodes with rigid bodies). \n* The formulation of strain energy density for fiber-pow-linear-uncoupled material was fixed. In addition, a bug was that which produced discontinuous stress and elasticity at transition from toe region to linear region. The user manual was updated to reflect these changes. \n* A bug was fixed in CMakeLists that caused an error on Windows if not using MKL.\n* A bug was fixed in the \"porosity\" element log variable.\n* The description of the max_retries control parameter in the user manual was corrected.\n* The \"reaction forces\" plot variable now produces the equivalent nodal reaction forces for all nodes, even constrained nodes (fixed, prescribed, rigid). \n* Chapter on multi-step analysis was added back to user manual and several broken links were fixed. \n\n===========================================================================\n\n R E L E A S E   3.3.3   4/20/2021\n\n===========================================================================\n\n* Added \"porosity\" log variable.\n* Added \"fluid pressure\" surface load as an alternative to prescribing (or fixing) the fluid dilatation. User manual was updated to include description.\n* Changed default constitutive model for fluid pressure from p = - K ef to p = K (1/(1+ef)-1), which is valid for finite dilatation.\n* Generic math expressions (e.g. as used in the force parameter of nonlinear springs) can now use model parameters in the math expression.\n* Added three new interpolation types for load curves: cubic spline, control points, approximation.\n* Fixed logic in setting debug mode from febio command prompt.\n* Removed dependency on GSL.\n\n===========================================================================\n\n R E L E A S E   3.3.2   4/6/2021\n\n===========================================================================\n\n* This patch release fixes GSL support in FEBio for all OS. Not all previous builds, including the most recent ones, included support for GSL and consequently could produce incorrect results for certain types of models. For previous builds, materials that require GSL to evaluate the strain energy density, or other quantity, simply returned 0 in the absence of GSL support and did not produce an error message. The complete list of affected materials follows. The list shows the type string of the material, the source code class name, and the function where GSL is needed:   \n - \"relaxation-Fung\" (FEBondRelaxationFung): relaxation function\n - \"damage fiber exp-linear\" (FEDamageFiberExpLinear): strain energy density function\n - \"coupled trans-iso Mooney-Rivlin\" (FECoupledTransIsoMooneyRivlin): strain energy density function\n - \"coupled trans-iso Veronda-Westmann\" (FECoupledTransIsoVerondaWestmann): strain energy density function\n - \"CDF gamma\" (FEDamageCDFGamma): evaluation of damage cumulative distribution function (cdf) and damage probability density function (pdf). \n - \"fiber-exp-linear\" (FEFiberExpLinear): strain energy density function\n - \"uncoupled fiber-exp-linear\" (FEUncoupledFiberExpLinear): strain energy density function\n\nIn addition, the following materials returned 0 for the strain energy density, and will now throw an error instead if the model attempts to evaluate the strain-energy function. \n\n - \"viscoelastic\" (FEViscoElasticMaterial)\n - \"uncoupled viscoelastic\" (FEUncoupledViscoElasticMaterial)\n\n* A bug was fixed in the log feature that wrote all log data to the same output file. \n\n===========================================================================\n\n R E L E A S E   3.3.1   4/2/2021\n\n===========================================================================\n\n* A bug was fixed in the SPR projection algorithm. \n\n===========================================================================\n\n R E L E A S E   3.3   3/29/2021\n\n===========================================================================\n\n* The data log mechanism was extended to store surface data to the log file (github issue #10).\n* A bug was fixed in the enforcement of the concentration penalty in tied-multiphasic contact interface.\n* The explicit solver was repaired. It supports row-sum mass lumping (mass_lumping = 1) and Hinton-Rock-Zienkiewicz mass lumping (mass_lumping = 2) for higher order elements.\n* Breakpoint strings should now be case insensitive on Linux and macOS.\n* A check for NANS in the residual vector was added. If a Nan is detected, an error with node and dof info is printed.\n* The performance of the stiffness evaluations of multiphasic models using chemical reactions was improved significantly.\n* Loadcurves are now scaled based on the value of point functions that are defined via a load curve.\n* A check was added for invalid faces in surface definitions (i.e. faces that are not facets of an element).\n* The potfile now stores a status flag for each state. The flag depends on the action that triggered the write.\n* The \"contact nodal gap\" and \"contact pressure\" plot variables are fixed for \"sliding node-on-facet\" contact.\n* The self_contact flag in \"sliding node-on-facet\" was removed. Self-contact is now supported in all sliding contact algorithms. To use self-contact, just assign the same surface to the primary and secondary contact surface.\n* A bug was fixed regarding prescribed nodal loads in BiphasicSolute and Multiphasic analyses when no solutes are included in the model.\n* Added check for duplicate part names.\n* Implemented the \"defgrad\" data generator, which generates a deformation gradient from a nodal-displacement field. This can be used, for instance, in prestrain elastic materials to generate the prestrain gradient. \n* A bug was fixed in \"in-situ target stretch\" and \"prestrain stretch error\" related to how the fiber vector was taken into account. \n* A bug was fixed in formatting log data.\n* Support for a 13-node pyramid element was implemented.\n* Added check for valid material ID in rigid node set.\n* An issue was addressed with unloading of plugins at the end of FEBio's execution that was causing a crash on Windows.\n* A division by zero issue was fixed in the contact auto-penalty feature.\n\n\n===========================================================================\n\n R E L E A S E   3.2   2/5/2021\n\n===========================================================================\n\n* Fixed issue with the \"prestrain correction\" plot variable. \n* Added node-data objective function, which optimizes for nodal data. \n* Implemented torsional spring. \n* The data filename is now written to the log file, not the data file.\n* Elastic materials can evaluate stress via \"secant_stress\" parameter.(This can be useful for debugging new material implementations). Also renamed \"secant\" to \"secant_tangent\", which approximates elatic tangent. \n* Made \"value\" parameter of initial condition a mapped parameter.\n* Implemented a exp-linear damage fiber model.\n* Fixed Holzapfel-Gasser-Ogden formulation. Deleted unconstrained (compressible) version of this material.\n* Updated the Gasser-Ogden-Holzapfel model to be consistent with ABAQUS formulation.\n* Added \"HGO unconstrained\" material to model the unconstrained (compressible) Holzapfel-Gasser-Ogden material.  All parameters of \"Holzapfel-Gasser-Ogden\" are now mapped parameters.\n* Implemented rigid follower force and moment.\n* Fixed miscelleneous issues to improve backward compatibility with 2.5 format.\n* Modified projection logic in \"sliding-facet-to-facet\" contact. Added support for \"search_radius\" parameter.\n* An error is now thrown when data type of a map does not match the parameter's data type to which it is assigned.\n* Fixed bug in serialization of linear constraint set.\n* Added log output option for stess eigenvectors.\n* Implemented new \"azimuth constraint\", which fixes the angle coordinate of the cylindrical coordinates.\n* Introduced plot variables for local fluid load support and effective friction coefficient for biphasic and mixed biphasic contact interfaces.\n* Modified method to evaluate fluid load support in sliding biphasic contact by averaging the local fluid load support over the contact area (instead of dividing the fluid force magnitude by the contact force magnitude).\n* Made scale parameter of \"nodal load\" a model parameter.\n* Made \"phi0\" parameter of biphasic, biphasic-solute and triphasic materials a mapped parameter.\n* Improved calculation of rotation angle and extraction of rotation vector from quaternion.\n* Made active stress parameter of active contraction materials a mapped parameter.\n* Made value parameter of FENodalForce a mapped parameter.\n* Fixed issue with \"pretrain stretch\" plot variable.\n* Fixed crash when a parameter references invalid map.\n* Fixed bugs with element data ordinates in parameter optimization.\n* Added shell domain parameter \"shell_normal_nodal\"\n* Allow usage of auto-penalty when both contacting surfaces are rigid materials, as long as non-zero Young's modulus is specified for those rigid materials.\n* Added option to set TET4G4 integration rule in MeshDomains.\n* Improved performance of initialization of solid-shell interface elements.\n* Made \"ksi\" parameter of \"fiber-exp-pow-uncoupled\" a mapped parameter.\n* Added new \"damage fiber exp-linear\" material.\n* Fixed output of contact gap and contact pressure for the \"sliding node-on-facet\".\n* Added strain energy calculation to discrete elements. Added \"discrete strain energy\" plot variable.\n* Fixed some issues with storing contact plot variables for nodal-integrated contact interfaces.\n* Fixed crash when using \"prestrain uncoupled elastic\".\n* Fixed crash when nodal load type is not recognized.\n\n===========================================================================\n\n R E L E A S E   3.1.0   11/2/2020\n\n===========================================================================\n\n* The xplt format 3 is now used, which adds data on rigid bodies and rigid constraints to the plot file. \n\n* The \"fiber\" property was added to the \"tendon\" material. \n\n* Some backward compatibility issues were addressed: The deprecated \"print_level\" parameter is ignored and will not cause FEBio to stop. The deprecated \"value\" parameter of the prescribed BC is now mapped to the \"scale\" parameter. \n\n* An issue was fixed with reading mat_axis element data in MeshData section. \n\n* The \"solid stress\" plot variable was added for biphasic, multiphasic, and fluid-FSI materials. \n\n* The bulk-modulus (k) of uncoupled materials is only taken from the top-level material. This is different from previous FEBio versions, where the specification of the bulk modulus depended on the material. For backward compatibility, febio 2.5 files will still processed in a manner that produces the same behavior as in FEBio 2.10. For the 3.0 files, only top-level bulk moduli are allowed. \n\n* A bug was fixed that caused a crash when a file was using 9-node quadratic surface elements. \n\n* Support was added to reading mat3d data from ElementData section. \n\n* Added support for assigning data maps to F0 parameter of const prestrain gradient. \n\n===========================================================================\n\n R E L E A S E   3.0.2   10/5/2020\n\n===========================================================================\n\n* A bug was fixed with the \"local\" fiber generator option. FEBio was assuming zero-based indices, instead of one-based indices. \n\n===========================================================================\n\n R E L E A S E   3.0.1   8/27/2020\n\n===========================================================================\n\n* A bug was fixed with continuous fiber distribution materials that use the Gauss-Kronrod integration rule. \n\n* A bug was fixed in reading surfaces with quad9 elements. \n\n===========================================================================\n\n R E L E A S E   3.0.0   8/24/2020\n\n===========================================================================\n\n* FEBio 3.0 is the first version for which the source code is available on github (https://github.com/febiosoftware). The source code is distributed under the MIT license. \n\n* FEBio3 defines a new file structure. One of the major changes is that the Geometry section was replaced with a Mesh section, which defines the mesh independent of any physics attributes (i.e. materials), and a MeshDomains section, which maps materials and other parameters to element sets. Please see the FEBio3 user's manual for more information on the new file format. Despite this new format, FEBio3 will continue to read format 2.5 and format 2.0 with few exceptions. Also see the backward compatibility notes below. \n\n* FEBio3 introduces a new mechanism for defining heterogeneous model parameters. Many parameters can now be made dependent on position and other model parameters via mathematical expressions and parameter maps. This new mechanism is very flexible and works with many material parameters, boundary conditions, and loads. \n\n* Support for iterative linear solvers was significantly expanded, including some solvers based on Algebraic Multigrid Method (AMG). In addition, a Jacobian-Free-Krylov-Newton method is available as well. Some of these new solvers have shown great performance compared to the default direct solver. However, it must be emphasised that performance is highly problem-dependent and may require tuning of solver parameters. Please see the user manual for details on how to use and configure the iterative solvers. \n\n* FEBio3 introduces a generalization of \"load curves\", namely \"load controllers\". Load controllers essentially control the values of model parameters as a function of time and even as a function of other model parameters. This allows users to use load controllers to create feedback systems (e.g. PID control system). \n\n* FEBio3 also contains a framework for doing adaptive mesh refinement. The framework is currently still somewhat bare bones, but integrates with the plugin framework and users can develop their own refinement strategies as a plugin.\n\n* Plot variables for \"fluid pressure\", \"fixed charge density\" and \"osmolarity\", which were previously available only for biphasic and multiphasic materials, now provide output for the \"Donnan equilibrium\" material in a \"solid\" analysis.\n\n* A plot variable \"surface area\" has been added to calculate the surface area of a named surface.\n\n* A bug has been fixed in biphasic and multiphasic contact interfaces to properly set free-draining boundary conditions for the portions of contact surfaces which are not currently contacting.\n\n* A \"natural neo-Hookean\" material has been introduced which uses the natural (Hencky) strain.\n\n* To allow modeling shell domains that form a T-connection, a \"shell_normal_nodal\" flag has been added to the Control section to use face normals instead of nodal normals. Set \"shell_normal_nodal\" to 0 (false) when modeling T-connections.\n\n* Added option to evaluate the tangent stiffness matrix of a solid material using a secant method. Set the parameter \"secant\" to 1 in the material model. This can be useful when developing plugins for new materials and trying to debug (or avoid deriving) the analytical tangent stiffness.\n\n* fluid-FSI domains may now interface with shell domains. Set \"shell_bottom\" to 1 in \"fluid-FSI traction\" if the fluid-FSI domain attaches to the bottom of a shell domain.\n\n* Added a new hydraulic permeability material called \"perm-exp-iso\".\n\n* Added log variables to save relative motion (translation and rotation) of rigid connectors.\n\n* Updated elastodynamics capabilities to use generalize-alpha time integration scheme\n\n* Updated the \"fluid-FSI\" solver to allow running \"fluid\" problems.\n\n* Implemented method to save rigid connector forces/moments and relative motion (translation/rotation) to plot file.\n\n* Added \"update_penalty\" flag in contact interfaces to update the auto-penalty calculation at each iteration.\n\n* Added plot variable \"fluid surface pressure\" to evaluate the fluid pressure from the nodal dilatation in \"fluid\" and \"fluid-FSI\" analyses, on name surfaces.\n\n* Implemented 4-node finite deformation shell elements that employ the EAS (enhanced assumed strain) and ANS (assumed natural strain) methods.\n\n+ test suite:\n-------------\n\n* The test suite was updated using the new file format. In addition, some problems may show different convergence stats between febio2.9 and febio3. These difference are mostly due to numerical roundoff errors. Please inform the developers if you notice any major discrepancies between febio2 and febio3. \n\n+ backward compatibility notes:\n-------------------------------\n\n* plugins: The source code has significantly changed and plugins will need to updated. The changes should be fairly minor and revolve mostly around macros that were renamed. Please see the developer's manual for information regarding developing plugins for febio3. \n\n* Some fiber materials defined a \"theta\" and \"phi\" parameter that was used to define the local fiber orientation. For consistency reasons, these parameters are no longer supported. Instead, fiber orientation can be defined using the \"fiber\" property, or \"mat_axis\" property. \n\n<fiber type=\"angles\">\n  <theta>0</theta>\n  <phi>0</phi>\n</fiber\n\n* Some boundary conditions and loads defined a \"value\" parameter that could be used to define a heterogeneous map. These parameters are no longer supported since another mechanism for defining hetergeneous parameters was implemented in febio3. \n\n\n\n\n===========================================================================\n\n R E L E A S E   2.9.0   5/9/2019\n\n===========================================================================\n\n* This is the first version of FEBio that is released under the MIT open source license. This license allows users, including commercial entities, to copy, modify, and redistribute the source code of FEBio, under the conditions set forth by this license. See Copyright-FEBio.txt for more details and contact the developers for any questions.\n\n* Several options were added to the FEBio prompt: \"config\" for reloading config file, \"load\" to load a plugin, \"unload\" to unload a plugin. \n\n* The \"material axes\" plot variable was added that allows users to store the material axes (as a 3x3 matrix) to the plot file.\n\n* The \"SPR Lagrange strain\" plot variable was added that uses the SPR method for evaluating Lagrange strain at the nodes.\n\n* Support for \"mat3d\" material parameters was added to the MeshData section. \n\n* The quad4 shells now use the 3-field formulation by default. \n\n* The \"porous neo-Hookean\" material was added to model a porous solid whose stiffness becomes infinite as the pores collapse to zero volume. Porosity can be defined in the material or will be extracted from parent material if it is biphasic/multiphasic. \n\n* Several issue with the restart feature were fixed, including some issues with adding steps or modifying load curves via a restart input file. \n\n* Bug was fixed in using FESlidingInterfaceBW with optimization. \n\n* A new optimization feature was implemented that allows you to define a list of element values that can be used in the objective function of the optimizatsion. \n\n* Users can now also optimize the material axes parameters of a material. \n\n* A bug was fixed in evaluation of the stiffness of several rigid connectors, which may improve convergence of models that use rigid connectors.\n\n* A bug was fixed in the evaluation of stresses in continuous fiber distribution uncoupled materials.\n\n\n===========================================================================\n\n R E L E A S E   2.8.3   9/26/2018\n\n===========================================================================\n\n* A bug was fixed in FEBio's tensor library that affected the convergence and possibly the accuracy of the PRLig, muscle, and tendon materials. \n\n* A bug was fixed in the algorithm that inverts surface elements for higher order elements. This affects the DataMap plugin.\n\n===========================================================================\n\n R E L E A S E   2.8.2   8/17/2018\n\n===========================================================================\n\n* A bug was fixed in the parsing of the ElementData section, which was causing problems with reading heterogeneous parameters.\n\n* A bug was fixed in reading fiber data from the MeshData section for the 2.5 spec.\n\n===========================================================================\n\n R E L E A S E   2.8.1   7/25/2018\n\n===========================================================================\n\n* A bug was fixed related to reading the fiber values from  the ElementData section for FEBio spec 2.0.\n\n===========================================================================\n\n R E L E A S E   2.8.0   7/2/2018\n\n===========================================================================\n\n* A fluid-structure interaction (FSI) solver has been implemented, which allows deformation of the fluid mesh in computational fluid dynamics analyses, as well as interactions with surrounding solid domains.  The module for FSI analyses is \"fluid-FSI\" and the material type for fluid domains that deform is \"fluid-FSI\".  Boundary conditinos and surface loads applicable to fluid analyses have been updated to account for deforming boundary surfaces.\n\n* Domains can now be assigned to body loads. In order to do this, add the \"elem_set\" attribute to the body load definition. \n<body_load type=\"const\" elem_set=\"set01\"/>\nHere, set01 is an element set defined in the Geometry section of the input file.\n\n* Two flags were added to the sliding-elastic contact interface that allow users to flip the normals of the master and slave surfaces. In the contact definition, set flip_master to 1 to invert the normals on the master surface. Similarly, set flip_slave to 1 to invert the normals on the slave side.\n\n* A new time control parameter was added, named \"dtforce\", which forces the time stepper to take the maximum time step. The max time step size is given either by the dtmax variable, or the must-point curve, if defined.\n\n* The Biphasic solver now uses non-symmetric stiffness matrices by default.\n\n* Fixed bug in the evaluation of math expressions for the non-const body force load.\n\n* The FEBioHeat module has been removed from FEBio, but is still available as a plugin. Please see the plugin download page. \n\n* performance improvements: omp support was added to \"sliding facet-on-facet\" and the algorithm for allocating the global stiffness matrix was significantly improved. \n\n* Fixed bug in timer management that was causing inaccurate timings for solvers implemented as plugins. \n\n* Penta15 elements which are assigned a nearly-incompressible (uncoupled strain energy density) material no longer use the three-field formulation, for consistency with other quadratic elements.\n\n===========================================================================\n\n R E L E A S E   2.7.1   6/4/2018  \n\n===========================================================================\n\n* This minor update fixes an important bug, related to specifying material axes in the ElementData section (for 2.0 format) or MeshData section (2.5 format) for solid mixtures. \n\n===========================================================================\n\n R E L E A S E   2.7.0   4/2/2018  \n\n===========================================================================\n\n* A new computational fluid dynamics solver has been implemented, based on the formulation reported in Ateshian GA, Shim JJ, Maas SA, Weiss JA. \"Finite Element Framework for Computational Fluid Dynamics in FEBio.\", J Biomech Eng. 2018 Feb 1;140(2).\n\n* The optimization module has been completely revised in order to support more powerful features. The improved \"data-fit\" model allows users to optimize almost all model input parameters (not just material parameters) against almost all model output parameters by curve fitting the output parameter to a user-defined data curve. The new \"target\" data model allows users to optimize input parameters in order to reach user-defined values for one or more output parameters. \n\n* Several new elements were added: A new penta15 element was added, which represents a pentahedral element with quadratic shape functions. A 5-node pyramid element with linear shape functions was added as well.\n\n* FEBio's plugin framework now also supports \"solver\" plugins, which allow users to create entire new physics modules via the plugin framework. To illustrate this powerful new mechanism, a plugin called FEBioChem was developed that solves the nonlinear reaction-diffusion equations and can be used to model chemical reactions in a non-deforming mixture. This plugin can be downloaded from FEBio's plugin website.  \n\n* A plugin can now also be loaded from the command line using the -import command line option. This feature is useful for loading a plugin from within the IDE (e.g. Visual Studio) without the need for modifying the configuration file. Currently, only one plugin can be loaded this way. (Loading multiple plugins must still be done via the configuration file.)\n\n* Several contact algorithms have been renamed in order to obtain a more consistent naming convention for the various contact algorithms. \n  -- sliding_with_gaps           --> sliding-node-on-facet\n  -- facet-to-facet sliding      --> sliding-facet-on-facet\n  -- sliding-tension-compression --> sliding-elastic\n  -- tied                        --> tied-node-on-facet\n  -- facet-to-facet tied         --> tied-facet-on-facet\n  -- sliding2                    --> sliding-biphasic\n  -- sliding3                    --> sliding-biphasic-solute/sliding-multiphasic\n\n  The old names are considered obsolete but will continue to be supported for the time being. \n  \n* The sliding-elastic contact formulation now also supports frictional contact.\n\n* Improvement to update of solid bound molecules in multiphasic analyses.\n\n* A new symmetry plane feature was implemented that allows user to model axisymmetric problem in a more straightforward manner (without the need to create the actual geometry of the symmetry plane.)\n\n* This version introduces new array plot variables, which are convenient for storing arrays of data to the plot file especially when the size of the array is not known in advance but depends on the model input. These new plot variables are now used by many of the solute and solid-bound molecule plot variables (e.g. effective solute concentration). As a result, the plot file that is written by this version of FEBio must be read with the newest version of PostView (version 2.2). \n\n* A new and more accurate algorithm for calculating the total contact force of the sliding-facet-on-facet contact formulation was implemented.\n\n* The mat_axis parameter is now supported in the MeshData section. It can be defined as a ElementData with the var parameter set to \"mat_axis\". Then, for each element of a part the \"a\" and \"d\" parameters must be defined. \n\n* Support was added for initial rigid body velocities in the 2.5 format. It is defined in the rigid_body subsection of the Boundary section. Use the tag \"initial_velocity\" for defining the rigid body's initial velocity and \"initial_angular_velocity\" for defining its initial angular velocity. \n\n* Support was added for reading 16-bit (unsigned) RAW images. This is, for instance, used by the FEWarp plugin, which implements an FE based image-registration method. \n\n* Mathematical expressions can now be used to define surface data in the MeshData section. This data can then be used by most surface loads. This makes it essentially possible to prescribe surface loads via a mathematical expression. \n\n* A rigid cable feature was introduced that allows users to connect several rigid bodies via a cable. The cable can then be placed under tension by prescribing the cable force.\n\n* A new shell formulation has been introduced in FEBio, which accommodates elastic, biphasic and multiphasic materials.  These shells (tri3, tri6, quad4, quad8) now represent the default formulation; they supersede the formulation used in FEBio 2.6.2.  Boundary conditions for these shells include (x,y,z) displacements of nodes on the front face, and (sx,sy,sz) displacements of nodes on the back face of the shell. For biphasic shells, front and back face effective fluid pressure (p, q) may be prescribed.  For multiphasic shells, front and back face effective solute concentrations (c, d) may be prescribed.  These shells can be attached to solid elements or sandwiched between solid elements. The old shell formulation may still be used, by specifying <shell_formulation>0</shell_formulation> in the <Control> section.  The old shell formulation uses boundary conditions (x,y,z) for the displacements of nodes on the shell mid-surface, and (u,v,w) as components of directors (rotations) at those nodes.\n\n===========================================================================\n\n R E L E A S E   2.6.2   3/28/2017  \n\n===========================================================================\n\nBUG FIXED AND IMPROVEMENTS\n--------------------------\n\n1. A performance issue was fixed in the assembly of unsymmetric matrices. This fix may result in a signficant reduction of runtime for problems requiring a nonsymmetric stiffness matrix. Speedups of a factor 5x to 10x have been observed for these types of problems.\n\n2. A bug was fixed in the second derivatives of shape functions for tet10.\n\n3. The  EFD material was modified to evaluate stresses and tangent more efficiently.\n\n4. A bug was fixed in the tangent of the Ogden material. \n\n5. A more efficient method was implemented for calculating the total contact force for the sliding2 contact interface.\n\n6. A more efficient method was implemented for evaluating the contact area for sliding2 and sliding-biphasic.\n\n7. Several issues were addressed with the restart feature.\n\n8. when max_retries is set to zero, serialization is not done.\n\n9. A bug was fixed in solvent supply \"Starling\" to properly read the parameters qc and qv.\n\n10. More efficient implementation for rigid body forces.\n\nNEW FEATURES:\n-------------\n\n1. A new shell formulation was implemented that adds several new capabilities to shells, namely compressive loading of shells, attaching shells to solid elements, and placing shells between solid elements. In this formulation, \"rotation\" degrees of freedom represent nodal displacements on the shell bottom surface.  Since this formulation defines the top surface as the reference surface, slight difference can be noticed in convergence and results. The old formulation is still available, but now requires a special control flag. Set <shell_formulation> to 0 in the <Control> section to recover the old formulation.\n\n2. Quadratic shell elements have been implemented: quad8, quad9, tri6\n\n3. A tied multiphasic contact interface has been added to enforce continuity of solid displacements, effective fluid pressure, and effective solute concentrations at a tied interface between multiphasic materials.\n\n4. A 20-node cubic tetrahedral element was implemented (tet20). This element has 4 corner nodes, 2 nodes on each edge, and one node at the center of each face. \n\n5. The parameter update_penalty was added to the sliding-multiphasic interface to recalculate auto-penalty at the start of each time step.\n\n6. Two new chemical reactions were added: \"mass-action-forward-effective\", and \"mass-action-reversible-effective\".  These reactions employ the effective solute concentrations instead of actual solute concentrations, which will make a difference when the multiphasic mixture has non-zero fixed-charge density and charged solutes.\n\n7. The 2.5 file format now supports Part and Instance keywords for defining multiple-parts model. The Part keyword defines a part, including nodes, elements, nodesets, surfaces, etc. Then, the Instance keyword can be used to instantiate a particular part. The same part can be instantiated multiple times. \n\n8. Added plot_range, and plot_zero_state control parameters. The plot_range parameter specifies a range and only time steps that fall within this range will be stored to the plot file. The plot_zero_state flag is used to indicate whether to store the \"zero\" (i.e. reference) state even when it doesn't fall inside the plot range.\n\n9. Added reform_each_time_step control parameter. Setting this to 0 will prevent the quasi-Newton solver from reforming the stiffness matrix at the start of each time step. This may result in a significant reduction of runtime for quasi-linear problems that have many time steps, such as transient or dynamic analyses with small strains. \n\n10. Added \"rigid sliding\" contact interface, which generalizes the rigid wall feature. Now, any rigid surface can be used as part of the rigid sliding contact interface. Rigid surfaces have to be defined in the new \"Rigid\" section in the input file.\n\n11. Inhomogeneous linear constraints are now supported.\n\n12. Added \"rigid lock\" constraint to constrain two rigid bodies to move together.\n\n13. Added optional force and moment damping for revolute and prismatic joints to minimize oscillations during dynamic analyses. \n\n===========================================================================\n\n R E L E A S E   2.5.2    11/17/2016\n\n===========================================================================\n\n1. A bug was fixed in exporting element data to the log file.\n\n2. An issue was fixed with the definition of fixed bcs in the 2.0 format.\n\n===========================================================================\n\n R E L E A S E   2.5.0\n\n===========================================================================\n\nA. Features and Bug Fixes\n-------------------------\n\n1. FEBio 2.5 defines a new format specification (FEBio format specification version 2.5) which improves support for using set definitions. It also replaces the ElementData section, which used to be defined in the Geometry section, with the more versatile MeshData section. This new section not only allows users to define element data, but also surface data and nodal data. This data can then be mapped to boundary conditions and loads to create spatically varying data fields. The Discrete section is also reformatted so that large discrete element sets can be defined more concisely. \n\n2. SPR stresses now support HEX20 and HEX27 elements. The SPR stresses are more accurately recovered stressed and should be the preferred stress measures for higher order elements. \n\n3. Several improvements and bug fixes have been made for first order computational homogenization problems. Users can now also define \"probes\" which allow the deformation of the RVE models to be tracked.\n\n4. Higher order shell elements can now be defined. A quad8 shell defines an eight node quadratic quadrilateral shell. A tri6 shell defines a six node quadratic triangular shell element.  These shell elements overcome the locking behavior characteristic of quad4 and tri3 shells.  They should be used as the preferred choice. Shell rotation degrees of freedom refer to the director degrees of freedom, which is a vector whose length is the shell thickness in the current configuration, and whose direction represents the rotation degrees of freedom.\n\n5. A coupled and uncoupled formulation of the Gent hyperelastic material have been implemented.\n\n6. The reported timings at the end of the log file are broken down by the different solution steps. The reported timings are now more accurate (Windows only).\n\n7. The Broyden solution strategy was implemented and can be used as an alternative quasi-newton solver to the standard BFGS method. The Brodyden method performs much better than BFGS for non-symmetric problems (e.g. biphasic, multiphasic).\n\n8. Several preconditioners for the FGMRES iterative solver have been implemented. \n\nB. Changes to the plugin framework\n------------------------------------\n\n1. The FE_SDK_VERSION was increased to 2.5.0. This implies that plugins need to be rebuild before they can be used with the 2.5 version of FEBio. Some minor changes to the plugin may also be necessary. \n\n2. Material plugins that implement the Init function must now return a bool:\n\nbool MyMaterial::Init()\n{\n\tif (something is wrong) return false;\n\telse return true;\n}\n\n3. Plot variables now use the general FEDataStream class for storing data.\n\nbool MyPlotVariable::Save(FEDomain& dom, FEDataStream& a)\n{\n\tfor (int i=0; i<dom.Elements(); ++i)\n\t{\n\t\tmat3ds s = dom.TaxReturnValue(2016);\n\t\ta << s;\n\t}\n\treturn true;\n}\n\n4. The PluginNumClasses funcion is no longer required. If it is not defined, FEBio will keep calling the PluginGetFactory until it returns null. \n\n===========================================================================\n\n R E L E A S E   2.4.1  12/11/2015\n\n===========================================================================\n\n1. BUG FIX: Optimization\n----------------------------------------------------------------\nFixed bug in FEMaterial::GetParameter for optimization problems.\n\n===========================================================================\n\n R E L E A S E   2.4  9/25/2015\n\n===========================================================================\n\n1. FEATURE: new rigid body solver\n---------------------------------\nA new rigid body update algorithm was implemented that fixes some issues with the \nprevious implementation. The new solver is now used automatically so users don't need\nto do anything special. If for some reason the old solver is still needed, it is\nstill available by setting the Module tag to solid_old in the FEBio input file.\n\n<Module type=\"solid_old\"/>\n\n\n2. FEATURE: rigid connectors\n-----------------------------\nNonlinear constraints have been implemented to connect rigid bodies\nwith joints (rigid joints) or springs and dampers (rigid connectors).\nRigid joints include revolute (hinge), prismatic (slider),\ncylindrical, planar and spherical joints. The degrees of freedom of\nrigid joints may be optionally prescribed using load curves.  Reaction forces and moments\nat these rigid connectors may be saved to the logfile using a new\nrigid_connector_data class. \n\n3. FEATURE: Damage materials and damage criteria\n----------------------------------------\nA new elastic damage model has been implemented to describe damage in\nany of the elastic materials defined in FEBio, using a reactive\nframework where the damage variable D represents the fraction of bonds\nin that material that have broken.  This damage material requires the specification of\nan elastic material, a damage criterion (a function of state) that governs damage\ninitiation and progression, and a cumulative density function that describes the\nprobability of damage at a particular value of the damage criterion.\nThis damage model may be used within a solid mixture to describe heterogeneous and\nanisotropic damage responses.\n\n4. FEATURE: Biphasic and multiphasic tangents diagnostics\n-----------------------------------------------\nTangent diagnostics have been introduced for the biphasic and\nmultiphasic solvers. These can be used to validate the analytical\nexpressions for the tangents against numerical approximations.\n\n5. FEATURE: Plot field filters and alias\n-------------------------------\nSome plot fields support filters that can be used for disambiguation. For example,\nthe solute concentration can now be stored using the following tag.\n\n<var type=\"solute concentration['sol1']\"/>\n\nThe filter is entered between square brackets. For this plot variable, the \nfilter denotes the name of the solute (defined in the Globals section). If the\nfilter is a string (like in this example), it is entered between quote marks. \n\nAn alias can be defined for a plot file variable by appending the type definition\nwith an equal sign an the alias name. For instance,\n\n<var type=\"solute concentration['sol1']=sol1 concentration\"/>\n\nPost processors (such as PostView) can use the alias as an alternative for\ndisplaying the field variable name.\n\n6. FEATURE: Callback plugins\n-------------------\nA new plugin type is supported termed callback plugins. Callbacks are special\nfunctions that are called by FEBio at specific points during the initialzation\nand solution phase and are a convenient mechanism for interacting with FEBio.\nFEBio uses callbacks for updating the window title and to show progress. Other\napplications of callbacks can be the extraction of information during the \nnonlinear solution iterations. Via callback plugins, a plugin can now also define\ncallback functions.\n\n7. FEATURE: Initial condition plugins\n-------------------------------------\nNew initial conditions can now also be defined in plugins. These initial conditions\ncan be used inside multi-step analysis. Special initial conditions can be defined\nto transfer data between steps in a multi-step analysis or initialize variables that\nare not used in previous analysis steps (e.g. when combining static and dynamic\nanalyses in a multi-step analysis).\n\n\n8. FEATURE: Fixed BC's in multi-step analyses\n------------------------------------\nPreviously, fixed boundary conditions can only be defined at the model level and thus\npersisted throughout the entire simulation. Now, they can be defined inside the Boundary\nsections of Steps in a multi-step analysis (as prescribed boundary conditions).\n\n9. FEATURE: Simplifications for material plugins\n---------------------------------------\nThere are two new constructions supported for material plugins that can make creating\nmaterial plugins easier. First, there is automatic parameter checking which may eliminate\nthe need for defining the FEMaterial::Init() function. This solves the problem that \nerrors can only be reported from the Init() function by throwing exceptions. However, it is not\nalways safe to throw exceptions from within a plugin so automatic parameter checking\nis the preferred way for validating the value range. Second, material classes that define\nproperties no longer need to define the special functions for defining the material properties.\nInstead a template class FEPropertyT can now be used to define material properties. \n\n10. FEATURE: Fiber with toe-linear response\n---------------------------------------------------------------\nA new fiber constitutive model has been introduced to describe a toe\nregion with a power law, followed by a linear region.  This model is\navailable as a stand-alone fiber model, or as part of a continuous\nfiber distribution.\n\n 11. BUG FIX: stiffness matrix for (bi/tri/multi)-phasic problems\n---------------------------------------------------------------\nThe linearization of the solid matrix velocity in biphasic,\nbiphasic-solute, triphasic and multiphasic domains has been modified\nto correctly reflect the time-discretization used for evaluating the\nsolid velocity from the solid displacement.  These changes should\nslightly improve the convergence characteristics of the corresponding solvers.\n\n===========================================================================\n\n R E L E A S E   2.3  5/28/2015\n\n===========================================================================\n\n1. FEATURE: Pressure Boundary Load\n---------------------------------------\nThe pressure boundary load now implements a non-symmetric stiffness matrix in addition to the symmetric\nformulation. Although the symmetric formulation often results in good convergence behavior, it is not\nconsistent with the pressure residual unless the boundary of the pressure load surface is constrained.\nIt this latter condition is not satisfied, the consistent non-symmetric formulation can result in better\nconvergence behavior. To use the non-symmetric formulation, add the following to the pressure load definition.\n\n<symmetric_stiffness>1</symmetric_stiffness>\n\n2. FEATURE: Element, Surface and Node Sets\n------------------------------------------\nElements sets, surface, and node sets can now be defined in the input file. These can be used to define\nboundary conditions, loads, and contact definitions more concisely. See sections 3.8.4 of the FEBio user's\nmanual and following for an in-depth discussion.\n\n3. FEATURE: Parameters Section\n------------------------------\nA new Parameters section was added to FEBio file format, where users can define parameters that can be used\nin most variable definitions. Parameters are defined by name/value pairs in the Parameters section.\n\n<Parameters>\n  <param name=\"var1\">1.0</param>\n</Parameter>\n\nThen, parameters can be used as the values of most xml tags by preceding the parameter name with the '@' symbol. \n\n<material id=\"1\" type=\"neo-Hookean\">\n  <E>@var</E>\n</material>\n\n4. FEATURE: Include Section\n---------------------------\nAdded new Include section to the FEBio file format, which will include another .feb file verbatim. This can\nbe used (in addition to the \"from\" attribute) to split the model definition across multiple files.\n\n5. FEATURE: New Reaction Rate\n-----------------------------\nA new reaction rate was implemented where the reaction rate is proportional to exponential of strain energy density.\n\n6. FEATURE: Vector Load Curve\n-----------------------------\nA loadcurve can now be defined for vector parameters. The loadcurve will scale the vector.\n\n7. FEATURE: Improved Contact Detection\n--------------------------------------\nImproved contact detection for several contact implementations (sliding_with_gaps, facet-to-facet sliding, tied,\nfacet-to-facet tied, sticky). Certain special cases in the contact projection are now handled correctly. This\nresults in fewer contact detection failures which could cause stability issues in some contact problems.\n\n8. FEATURE: Volume Constraint\n-----------------------------\nA volume constraint is added that can be used to preserve the volume of an enclosed space. This could be used to\nmodel the containment of an incompressible fluid inside a solid.\n\n9. FEATURE: Point-to-Point Constraint\n-------------------------------------\nA point-to-point distance constraint was implemented which enforces a constant distance between two points.\n\n10. FEATURE: Conewise Materials\n-------------------------------\nTwo new materials have been introduced to model Conewise Linear Elastic solids (cubic and orthotropic symmetry):\n\"cubic CLE\" and \"orthotropic CLE\".\n\n11. FEATURE: Contact Node Relocation\n------------------------------------\nSupport was added for node relocation on initial contact for all contact interfaces. This feature forces nodes\nthat have an initial penetration to lie on the contact surface. To use this feature add <node_reloc>1</node_reloc>\nto the contact definition.\n\n12. FEATURE: Augmentation Load Curve\n------------------------------------\nThe augmentation flag of most contact interfaces now accepts a loadcurve attribute which can be used to control\nwhen augmentations are performed. This can be used for instance to enforce augmentation only on the last time step.\n\n13. FEATURE: Negative Jacobian Output\n-------------------------------------\nOutput of negative Jacobians can now be controlled via a flag in the configuration file. Set the\n<output_negative_jacobians> flag to 0 to turn off printing of all the negative jacobians. (By default, FEBio\nprints all the negative Jacobians). When set to 0 FEBio will only print a single warning when the time step\nhas to restart due to negative Jacobians.\n\n14. FEATURE: Retry after NANs\n-----------------------------\nFEBio will now retry the time step when NANs are encountered. (Previously, FEBio simply terminated.)\n\n15. FEATURE: Updated Sticky Interface\n-------------------------------------\nThe sticky interface (a variation of tied which allows for initial separation) now has two new parameters:\nthe <max_traction> parameter can be used to release the tie when the normal traction exceeds this value.\nThe <snap> parameter sets an initial distance of penetration before the slave node is tied to the master surface. \n\n16. FEATURE: Euler-Lagrange Strain Output\n-----------------------------------------\nA new output variable was added to output the Euler-Lagrange strain. Add <var type=\"Lagrange strain\"/> to the\nplotfile section to output the Lagrange strain directly to the plotfile. \n\n17. FEATURE: Constraints Repeated in Multiple Steps\n---------------------------------------------------\nConstraints can now be repeated in multiple steps. This can be useful when a constraint has to persist across\nmultiple steps. Assuming a constraint was defined in some step,\n\n<constraint name=\"my_constraint\" type=\"volume\">\n ...\n</constraint>\n\nto repeat the constraint in a subsequent step, simply reference it by name:\n<constraint name=\"my_constraint\"/>\n\n18. FEATURE: Break Points\n-------------------------\nBreak-points can be defined which will pause the run at the specified time and show the FEBio prompt. This can\nbe used for debugging application after a certain time has reached. To define a break-point, add the break\ncommand line option followed by the time value after which FEBio will pause. E.g.\n\nfebio2 i input.feb break 0.5\n\nThis will pause FEBio after time 0.5 has been reached. \n\n19. FEATURE: New Output Level\n-----------------------------\n- A new output_level flag is implemented that controls the frequency of writing log data to file. Add it to\nthe Control section of the input file.\n\n<output_level>OUTPUT_MAJOR_ITRS</output_level>\n\nThe possible values are:\n- OUTPUT_NEVER: Don't output anything\n- OUTPUT_MAJOR_ITRS: Output at the converged time step solutions (default)\n- OUTPUT_MUST_POINTS: Only output at must points\n- OUTPUT_FINAL: Only output final converged solution.\n\n20. FEATURE: Prescribed Active Contraction Materials\n----------------------------------------------------\nSix new materials have been introduced to model prescribed active contraction: \"prescribed uniaxial active\ncontraction\", \"uncoupled prescribed uniaxial active contraction\", \"prescribed trans iso active contraction\",\n\"uncoupled prescribed trans iso active contraction\", \"prescribed isotropic active contraction\", and\n\"uncoupled prescribed isotropic active contraction\".\n\n21. FEATURE: New Viscoelastic Material Classes\n----------------------------------------------\nTwo new classes of viscoelastic materials have been introduced: \"reactive viscoelastic\" and \"uncoupled\nreactive viscoelastic\" which may be used to model quasi-linear and nonlinear viscoelasticity.\n\n22. FEATURE: Local Coordinate Systems in Multigeneration Materials\n------------------------------------------------------------------\n- The \"multigeneration\" material has been updated to pass local coordinate systems down from parent to\nchildren materials.\n\n23. FEATURE: New Continuous Fiber Distribution Classes\n------------------------------------------------------\nIntroduced two new classes of continuous fiber distributions: \"continuous fiber distribution\" and\n\"continuous fiber distribution uncoupled\".  These materials provide a variety of fiber constitutive models,\nfiber distribution densities (2D and 3D), and integration schemes.\n\n24. FEATURE: Contact Gap and Traction Output for Periodic Boundary Conditions\n-----------------------------------------------------------------------------\nPeriodic boundary conditions now also output the contact gap and contact traction. (The contact gap is the\ndeviation from periodicity). \n\n1. BUG FIX: Restart Feature\n---------------------------\nSeveral issues with the restart feature were fixed.\n\n2. BUG FIX: Augmented Lagrangian for Incompressibility\n------------------------------------------------------\nAugmented Lagrangian formulation for incompressibility is fixed. \n\n3. BUG FIX: Contact Traction Export\n-----------------------------------\nA bug was fixed in the export of contact tractions of tied interface.\n\n4. BUG FIX: Multi-Step Constraints\n----------------------------------\nSeveral issues were fixed with using constraints in multi-step analyses.\n\n===========================================================================\n\n R E L E A S E   2.2  12/23/2014\n\n===========================================================================\n\n1. FEATURE: Tri6 Element Shape Functions\n-----------------------------------------\nImplemented modified shape functions for tri6 elements.\n\n2. FEATURE: Gasser, Ogden, Holzapfel material\n----------------------------------------------\nAdded a compressible version of FEGasserOgdenHolzapfel material.\n\n3. FEATURE: Damage Plotting for Multigeneration Materials\n---------------------------------------------------------\nAdded plotting of damage for multigeneration materials.\n\n4. FEATURE: Fiber Exp Linear Material\n-------------------------------------\nAdded coupled and uncoupled fiber-exp-linear material.\n\n5. FEATURE: Reactive Viscoelasticity Material\n---------------------------------------------\nAdded coupled and uncoupled reactive viscoelasticity materials.\n\n6. FEATURE: Ramp Rigid Body Force\n---------------------------------\nImplemented the \"ramp\" rigid body force which ramps the force from its current\nvalue to the desired value. This can be used to switch between displacement and\nload control in a multi-step analysis.\n\n7. FEATURE: Must-point Repeat Extend\n------------------------------------\nMust-point loadcurves now work with repeat extend type.\n\n8. FEATURE: Tied Interface Gap for Shells\n-----------------------------------------\nTied interface now takes initial gap into account for shells (only on slave side).\n\n9. FEATURE: Quad9 Surfaces\n--------------------------\nImplemented FE_QUAD9 surface element.\n\n10. FEATURE: Hex27 Element\n--------------------------\nImplemented hex27 element.\n\n11. FEATURE: NANS in Convergence Norms\n--------------------------------------\nChecks for NANS in the convergence norms is now always done. When a NAN is detected, the time step is retried.\n\n12. FEATURE: Self-Contact\n-------------------------\nAdded support for self-contact in FESlidingInterface.\n\n13. FEATURE: Osmotic Virial Expansion Material\n----------------------------------------------\nAdded new FEOsmoticVirialExpansion material.  Added bond relaxation functions for reactive viscoelastic\nmaterials. Updated FEElasticMultigeneration to improve nesting of nested materials. Updated\nFEFiberIntegrationGaussKronrod.  Updated FEReactiveViscoelastic material.\n\n14. FEATURE: Damage Materials\n-----------------------------\nImplemented FEDamageMaterial and FEDamageMaterialUC to model damage in any FEElasticMaterial and\nFEUncoupledMaterial.  FEDamageCriterion and FEDamageCriterionUC are parent classes to a variety of\ndamage criteria.  FEDamageCDF is a parent material to a variety of cumulative distribution functions\nthat relate the damage to the damage criterion. \nThe existing FEDamageMaterialPoint class was moved to its own file.\n\n15. FEATURE: Strain Energy Density\n----------------------------------\nIntroduced FEStrainEnergyDensity function in FEElasticMaterial and FEDevStrainEnergyDensity in\nFEUncoupledMaterial classes.  Implemented analytical expression for the calculation of the strain energy\ndensity in almost all the materials, except a few for which documentation is not available.\nFixed bug in the calculation of the Tangent and Stress in FELinearOrthtropic and FELinearTransIso.\nAdded link to GNU Scientific Library (http://www.gnu.org/software/gsl/) for the evaluation of special functions\n(namely, the exponential integral, for calculating strain energy density of fibers in FETransIsoMooneyRivlin\nand related materials). Use -DHAVE_GSL to compile and link with this library.\n\n16. FEATURE: Initial Temperature\n--------------------------------\nAdded support for initial temperatures.\n\n17. FEATURE: Reaction Rate Nims\n-------------------------------\nAdded FEReactionRateNims class for reaction rates.\n\n18. FEATURE: Continuous Fiber Distribution Material\n-------------------------------------------------------------\nAdded uncoupled continuous fiber distribution material with all contingent material functions.\nUpdated continuous fiber distribution models by adding two additional fiber integration schemes\n(Gauss-Kronrod and Triangular).  Fixed bug in the general scheme for continuous fiber distributions\nwhich prevented proper running on multiple threads. Removed obsolete FEEFD model (which had never been\ndocumented). Added the ability to define a local orientation for each continuous fiber distribution.\n\n1. BUG FIX: Tri6 and Tri7 Projection Algortithms\n------------------------------------------------\nFixed bug in projection algorithms for tri6 and tri7.\n\n2. BUG FIX: Hex27 Contact\n-------------------------\nFixed problems with hex27  contact.\n\n3. BUG FIX: Serialization\n-------------------------\nFixed serialization in several materials.\n\n4. BUG FIX: Multistep Pressure Stiffness\n----------------------------------------\nFixed bug in pressure stiffness evaluation for multistep problems.\n\n5. BUG FIX: Invalid Rigid Body\n------------------------------\nAdded check for invalid rigid body reference in rigid contact section. Made sure that a model with zero\nequations doesn't crash FEBio.\n\n6. BUG FIX: Quadratic Quads\n---------------------------\nFixed bug in xplt format related to storage of quadratic quads (now version 0.4). Added support for projection\non quad8 facets and implemented project_to_nodes for this element.\n\n7. BUG FIX: Concentration DOFs\n------------------------------\nFixed bug in parsing fixed concentration dofs.\n\n===========================================================================\n\n R E L E A S E   2.1  7/30/2014\n\n===========================================================================\n\n1. FEATURE: Rotation Matrix\n---------------------------\nAdded method for calculating rotation matrix from quaternion. Added support for exporting rigid body rotation matrix to log file.\n\n2. FEATURE: Coupled Materials\n-----------------------------\nAdded coupled Mooney-Rivlin and coupled Veronda-Westmann materials. Fixed problem with coupled trans-iso material.\n\n3. FEATURE: Newmark Integration\n-------------------------------\nImplemented general Newmark integration for dynamic analyses.\n\n4. FEATURE: Plugin Error Code\n-----------------------------\nFEBioPlugin::Load now returns an error code that can be used to identify if and why the plugin failed to load.\n\n5. FEATURE: Plotfile Compression\n--------------------------------\nImplemented compression for plot files.\n\n6. FEATURE: New Prompt Paradigm\n-------------------------------\nGiven no command line arguments, return to prompt after run/error.\n\n7. FEATURE: Rigid Body Dynamics\n-------------------------------\nImplemented rigid body dynamics, using Puso 2002 and Simo and Wong 1991 algorithms.  Modified update of rotation degrees of freedom to use Cayley transform.\n\n8. FEATURE: Euler Angles\n------------------------\nAdded function to calculate Euler angles from quaternion. Added Euler angle plot variable which plots Euler angles for rigid bodies.\n\n9. BUG FIX: Rigid Joint\n-----------------------\nFixed bug in rigid joint force and stiffness. \n\n10. BUG FIX: OpenMP\n------------------\nFixed several openmp directives. \n\n11. BUG FIX: Serialization\n--------------------------\nFixed serialization in viscoelastic material and FEElasticMixture. \n\n===========================================================================\n\n R E L E A S E   2.0  5/2/2014\n\n===========================================================================\n\n1. FEATURE: Plugins\n-------------------\nA plugin mechanism has been added for FEBio. Plugins are dynamic libraries which extend the capabilities of FEBio at runtime without the need to recompile the entire source code. This offers the user a powerful mechanism for extending the default feature set of FEBio with little effort.  This has involved a major restructuring of the code.\n\n2. FEATURE: OpenMP\n------------------\nOpenMP directives have been added to speed up several sections of the code. Users can expect improved performance and reduced runtimes for many areas of applications. Problem areas that rely on complex material formulations seem to benefit greatly from this new capability (e.g. bi- and multiphasic, EFD type materials, homogenization, etc.).\n\n3. FEATURE: New File format specitication\n-----------------------------------------\nFEBio 2.0 supports the new FEBio file format specification 2.0. This new format adds several new features including the ability to add surfaces and nodesets which can be referenced in the boundary conditions that use them. See the FEBio User's Manual for details regarding this format. The older format (version 1.2) is still supported but considered deprecated.\n\n4. BUG FIX: Restart\n-------------------\nFixed several bugs in the (cold) restart feature. This feature allows users to restart the code from a dump file, which is a file generated at the last converged state of a previous run. \n\n===========================================================================\n\n R E L E A S E   1.8.0  10/25/2013\n\n===========================================================================\n\n1. FEATURE: Chemical Reactions\n------------------------------\nChemical reactions may be modeled within a multiphasic mixture.\n\n2. FEATURE: Huiskes and Carter-Hayes Materials Modifications\n------------------------------------------------------------\nChanged names of constant and Huiskes reaction rates to be more descriptive.\nModified Carter-Hayes list of parameters.\n\n3. FEATURE: SPR Projection\n--------------------------\nImplemented the Superconvergent Patch Recovery stress recovery algorithm.\n\n4. FEATURE: Levenberg-Marquardt Methods\n---------------------------------------\nAllow users to choose between the older and the new Lourakis\nLevenberg-Marquardt methods.\n\n===========================================================================\n\n R E L E A S E   1.7.1  9/4/2013\n\n===========================================================================\n\n1. FEATURE: Reset Diagnostic Tool\n---------------------------------\nAdded reset diagnostic tool.\n\n2. BUG: Reset\n----------------\nFixed some problems related to the reset feature.\n\n===========================================================================\n\n R E L E A S E   1.7.0  6/25/2013\n\n===========================================================================\n\n1. FEATURE: Spherical Fiber Distribution exponential term\n---------------------------------------------------------\nExtended FESphericalFiberDistribution fiber model to include exponential term.\n\n2. FEATURE: Lourakis Levmar optimization\n----------------------------------------\nImplemented the Lourakis Levenberg-Marquardt method for parameter optimization.\nImplemented support for box and linear constraints and for reading measurement\ndata from a text file.\nhttp://users.ics.forth.gr/~lourakis/levmar/\n\n3. FEATURE: Gauss-points reported as one-based\n----------------------------------------------\nGauss-points for negative jacobians are now reported as one-based.\n\n4. FEATURE: Time for non-const body forces\n------------------------------------------\nAdded time as a variable for non-const body forces.\n\n5. FEATURE: Tied Contact and Tri6 elements\n------------------------------------------\nFixed tied interface to work with quadratic tri6 elements.\n\n6. FEATURE: Uncoupled Active Contraction material\n-------------------------------------------------\nImplemented new uncoupled active contraction material for use in solid mixtures.\n\n7. BUG: Tied Contact\n--------------------\nFixed bug in stiffness matrix for tied contact.\n\n8. BUG: Fiber material\n----------------------\nFixed bug in fiber material.\n\n===========================================================================\n\n R E L E A S E   1.6.1  5/1/2013\n\n===========================================================================\n\n1. FEATURE: Log File variable \"T\"\n---------------------------------\nAdded temperature log file variable (\"T\")\n\n1. BUG: Active Contraction\n--------------------------\nFixed definitions of active contraction parameters.\n\n2. BUG: Node Data Record\n------------------------\nFixed bug in node data record.\n\n3. BUG: Heat Solver log data\n----------------------------\nFixed problem in exporting log data for heat solver.\n\n===========================================================================\n\n R E L E A S E   1.6.0  4/2/2013\n\n===========================================================================\n\n1. FEATURE: Contact for 10-noded quadratic tetrahedron\n------------------------------------------------------\nImplemented tri6 contact for sliding2, sliding3, sliding-tension-compression and\ntied-biphasic contact.\n\n2. FEATURE: Integration rules for 10-noded quadratic tetrahedron\n----------------------------------------------------------------\nImplemented 7-point integration rule for tri6 elements and 8-node integration rule\nfor tet10 elements.  Implemented 7-point Gauss-Lobatto rule for tri6 elements and the\n11-node Lobatto rule for tet10 elements.\n\n3. FEATURE: 20-node hex and 8-node quad elements\n------------------------------------------------\nImplemented 20-node hex and 8-node quad quadratic elements.  Added nodal projection\nalgorithm for 20-node hex element.\n\n4. FEATURE: Gap output\n----------------------\nRevised gap output in sliding2, sliding3 and sliding-tension-compression to save gap\nonly inside contact region and zero outside.\n\n5. FEATURE: Multi-step contact\n------------------------------\nContact interfaces can now be used with multi-step analyses.\n\n6. FEATURE: Superconvergent Patch Recovery\n------------------------------------------\nAdded SPR stress plot field.  Added condition number criteria for the SPR stress.\n\n7. FEATURE: Additional output fields for contact surfaces\n---------------------------------------------------------\nAdded contact force, fluid force, and pressure gap output for contact surfaces.\n\n8. FEATURE: Multiphasic sliding contact\n---------------------------------------\nImplemented multiphasic sliding contact.  Allows specification of multiple ambient concentrations.\n\n9. FEATURE: Solvent supply and Starling equation\n------------------------------------------------\nImplemented solvent supply in biphasic and multiphasic materials and created constitutive\nmodel for Starling equation.\n\n10. FEATURE: Virial Expansion material\n--------------------------------------\nAdded Virial Expansion material.\n\n11. FEATURE: Self-contact for sliding_with_gaps\n-----------------------------------------------\nImplemented a self-contact algorithm for sliding_with_gaps.\n\n12. FEATURE: febio_spec version 1.3\n-----------------------------------\nImplemented support for febio_spec version 1.3.\n\n13. FEATURE: Heat Transfer\n--------------------------\nAdded heat sources.  Implemented convective heat flux. Added heat flux plot variable.\n\n14. FEATURE: Reset() for Biphasic, Triphasic and Multiphasic\n------------------------------------------------------------\nAdded Reset() function to FEBiphasicSoluteDomain, FETriphasicDomain and FEMultiphasicDomain.\n\n15. FEATURE: Parameter optimization\n-----------------------------------------------\nAdded ability to specify multiphasic parameters for optimization.  Added ability to\nspecify optimization parameters for biphasic-solute and triphasic materials.\nAdded ability to control logfile and screen output during parameter optimization via\n<log_level> option in optimization file.\n\n16. BUG: Tangent stiffness\n--------------------------\nFixed bug in calculation of tangent stiffness for uncoupled materials in FEEFDUncoupled,\nFEFungOrthotropic, FEFiberExpPowUncoupled. Simplified code for FESFDMooneyRivlin,\nFEEFDVerondaWestmann, FEEFDMooneyRivlin to use corrected FEEFDUncoupled and new material\nFESFDUncoupled. Simplified tangent calculation in FEMooneyRivlin.\n\n17. BUG: Referential Permeability tangent matrix\n------------------------------------------------\nFixed bug in referential permeability tangent matrix.\n\n\n===========================================================================\n\n R E L E A S E   1.5.2\n\n===========================================================================\n\n1. FEATURE: Multiphasic materials\n---------------------------------\nMultiphasic materials have been implemented in FEBio.  Multiphasic materials may\nbe used to model the transport of a solvent and any number of neutral or charged\nsolutes; biphasic-solute and triphasic mixtures may be analyzed as special cases\nof a multiphasic mixture.\n\n\n2. FEATURE: shell strain in xplt format\n---------------------------------------\nShell strains can now be stored in the new xplt output format. To incorporate\nshell strains, add the following to the plotfile section\n\n<var type=\"shell strain\"/>\n\nShell strains are averaged over the element and only the averaged strain is stored\nin the plot file.\n\n\n3. FEATURE: 10-noded quadratic tetrahedron\n------------------------------------------\nA quadratic 10-noded tetrahedral element has been implemented in FEBio. The element\nis available for all solid domains (solid, biphasic, multi-phasic, etc) and also\nwork with the sliding contact interfaces (sliding_with_gaps, facet-to-facet sliding).\nThe element defines four corner nodes and six edge midpoints. The higher order element\nallows for more accurate predictions with fewer elements. The elements are defined\nusing the tet10 tag.\n\n<tet10>1,2,3,4,5,6,7,8,9,10</tet10>\n\nThe first four nodes are the corner nodes, the next six nodes define the edge midpoints.\n\n\n4. FEATURE: sliding-tension-compression contact\n---------------------------------------\nA new contact algorithm was implemented that allows for sliding contact while\npreventing separation.  This contact interface may be used to model arbitrarily\noriented symmetry planes.  For example, axisymmetric problems may be analyzed\nusing only a thin wedge representation of the 3D geometry (a single element in the\ncircumferential direction), requiring fewer elements.\n\n\n5. FEATURE: Cylindrical fiber generation\n----------------------------------------\nA new fiber generator was implemented for transversely-isotropic materials that defines\nthe fiber orientation based on a cylindrical distribution. \n\n\n6. FEATURE: minaug and maxaug for all contact interfaces\n-----------------------------------------------------------\nFor all contact interfaces the user can specify\nthe minimum nr of augmentations (minaug) as well as the maximum nr of augmentations\n(maxaug) for the augmented Lagrangian updates.\n\n\n7. FEATURE: Faster gap calculation for sliding2, sliding3 and tied-biphasic contact\n-----------------------------------------------------------------------------------\nThe algorithm for calculating the gap between two contacting surfaces has been modified\nto use an octree search algorithm.  This algorithm operates much more efficiently than\nthe previous brute-force search algorithm, especially with contact surfaces having thousands\nof patches.\n\n\n8. FEATURE: Multigeneration growth of solids\n--------------------------------------------\nA new class of solid mixtures has been introduced where each solid represents a generation\nin a multigenerational growth framework.  Users define the time when a generation is\ndeposited and the corresponding solid only becomes active starting at that time.  The\nreference configuration of a generation is given by the current configuration at the\nstart of that generation.  This class of material can be used to induce residual stresses\nin a multigenerational solid.\n\n\n9. BUG: Modified auto-penalty calculation for contact\n----------------------------------------------------------\nThe auto-penalty algorithm for contact interfaces has been modified to address \nissues with anisotropic materials. It can have an effect on existing FEBio files.\n\n\n10. BUG: EFD integration\n------------------------\nThe new integration rule for the EFD materials has been replaced with the old one\nas a potential issue with the new rule is being investigated.\n\n\n===========================================================================\n\n R E L E A S E   1.5.1\n\n===========================================================================\n\n1. FEATURE: Tied-biphasic interface\n-----------------------------------\nA new tied-biphasic interface has been implemented. It combines a tied-interface, used\nfor tying two non-conforming surfaces, with a biphasic interface which allows fluid to\npass across the teid-interface.\n\n\n2. FEATURE: new contact search algorithm\n-----------------------------------------\nSome contact interfaces whose projection algorithm is based on slave-normal projection\nhave a new projection algorithm. This affects e.g. biphasic, triphasic and multi-phasic\ncontact interfaces.\n\n\n===========================================================================\n\n R E L E A S E   1.5.0\n\n===========================================================================\n\n1. FEATURE: solutes\n-------------------\nFEBio has been updated to allow different solutes to be used in different regions\nof a model.  For example, adjacent regions of biphasic-solute materials need not\nhave the same solute.  A solute table must now be provided in the input file, which\nlists all the solutes appearing in a particular analysis.  Each region that contains\nsolutes must provide the solute id's corresponding to the entries in the solute\ntable.  For biphasic-solute materials, backward compatibility is maintained with \nthe FEBio 1.4 file format, though only one solute may be used throughout an analysis\nin the old format.\n\n2. FEATURE: Triphasic materials\n------------------------------\nTriphasic materials have been implemented in this version.  A triphasic material consists\nof a solid, a solvent, and two solute species that are monovalent counter-ions.\nThe solid matrix may carry an electric charge (the fixed-charge density),\nin which case the triphasic material will undergo swelling due to Donnan osmotic\npressure.  Electric potential and current density are evaluated in all\ntriphasic analyses.\n\n3. FEATURE: Cell growth\n----------------------\nA cell growth material has been implemented, which describes the growth of cells as\ndriven by osmotic forces, due to the increasing (or decreasing) content of\nintracellular solid and membrane-impermeant solute.\n\n\n4. FEATURE: relative boundary conditions\n---------------------------------------\nRelative boundary conditions have been implemented for all nodal degrees of freedom\nof displacement, fluid pressure, and solute concentration.  Relative boundary conditions \nare meaningful only in multi-step analyses.  When a nodal degree of freedom is \nspecified to be relative at a particular step, the value prescribed for that node is \nsuperposed over the value of that degree of freedom at the end of the preceding step.\n\n5. BUG: tied-interfaces with triangular surfaces\n----------------------------------------------\nA bug was fixed in the tied interface projection algorithm. Slave nodes were\nnot projected correctly onto triangular master elements. The bug fix should \nimprove convergence and create better results for models using tied interfaces\nwith triangular surface elements.\n\n6. BUG: orthotropic elasticity\n----------------------------------------------\nA bug was found and fixed in the implementation of the orthotropic elastic\nmaterial.\n\n7. BUG: restart\n--------------\nSeveral issues with the restart functionality were addressed. \n\n8. BUG: file attribute in logfile and plotfile XML element\n--------------------------------------------------------\nSupport was added for the file attribute of the logfile and plotfile XML\nelements. This attribute allows users to define the file name of the log-\nand plotfiles. For example:\n\n<logfile file=\"log.txt\">...</logfile>\n\nwill name the logfile log.txt.\n\n\n\n===========================================================================\n\n R E L E A S E   1.4.0\n\n===========================================================================\n\n1. FEATURE: FEBio input file format\n-----------------------------------\n- The new nested format for visco-elastic materials is defined. The elastic\n  part is now defined as a subcomponent of material element. The old format\n  (which uses solid_id to refer to the elastic component) is still supported\n  but should be considered obsolete. \n- Modified implementation and input file format of solid mixtures. Format is\n  now consistent with nesting multiple solid material descriptions within a\n  single <material> tag.\n\n2. FEATURE: Plot file\n---------------------\n- New FEBio plot format\n- Contact traction is added as an option for the FEBio plot file. \n- The facet-to-facet sliding has been updated to output contact pressure when\n  Lagrangian augmentation is off and to evaluate net contact pressure in two-\n  pass analyses.\n- Added support for outputting nodal reaction forces to the plot file.\n- Added support for discrete elements in FEBio plot file.\n\n\n3. FEATURE: Data logging\n------------------------\n- Principal stress and princial strain can be requested as output for element\n  data. Use s1, s2, s3 for the principal Cauchy stress components and E1, E2, \n  E3 for the principal Lagrange strain components.\n- The deformation gradient can now be output as an element output option. Use\n  F11, F12, etc. to output the components of the deformation gradient to the \n  logfile\n- Include x;y;z coordinates in element data logfile output options.\n\n\n4. FEATURE: Command line\n------------------------\n- A short hand command line syntax is now allowed. For example you can run an\n  FEBio file using \n  >febio file.feb\n  Note that there is no -i. This is convenient since now you can run an FEBio\n  input file from Windows Explorer by right-clicking it and selecting FEBio to\n  run the file. \n- Added -silent command line option.\n- Added option to export version info from the command line.\n- The FEBio command prompt now comes up when the program is started without command\n  line options.\n\n\n5. FEATURE: Biphasic/Biphasic-solute\n------------------------------------\n- Solute transport in neutral porous solid has been implemented.\n- Added framework for mass supply in biphasic-solute problems.\n- Steady-state biphasic analysis mode has been implemented.\n- Implemented fluidflux boundary condition for mixture velocity.\n- Implemented biphasic-solute sliding3 contact.\n\n\n6. FEATURE: New materials\n-------------------------\n- Added Fung orthotropic compressible material.\n- Added cell-growth material.\n- Modified \"linear orthotropic\" material to allow user-specified preferred \n  material directions.\n- Added single fiber constitutive relations (exponential-power law relation)\n  for compressible and uncoupled representations.\n- A simple damage model was implemented for some hyperelastic materials.\n  Currently you can use \"damage neo-Hookean\", \n  \"damage Mooney-Rivlin\" and \"damage trans iso Mooney-Rivlin\".\n- A version of a compressible Ogden material was implemented. This material\n  should be preferred over the (uncoupled) Ogden material when the material\n  is compressible.\n\n\n7. FEATURE: New element\n-----------------------\nA new tetrahedral element formulation was implemented. This formulation uses\nnodal averaging of the deformation gradient to overcome the well-known locking\nproblems of linear tetrahedral elements. This formulation can be used by\nsetting the integration rule for tet elements to UT4. Additional parameters can\nbe defined that affect the behavior of this element. \n\n\n8. FEATURE: Body forces\n-----------------------\n- A point-body force feature was implemented. This body force applies a force\n  to the domain that is centered around a fixed point and decays exponentially\n  with increasing distance from this fixed point.\n- Implemented centrifugal body force.\n- Implemented the non-const body force.\n\n\n9. FEATURE: Must point load curve\n---------------------------------\nThe user can now set the load curve type for must point load curves. It used to\nbe set to STEP but can now be defined in the input file. \n\n\n10. FEATURE: New iterative solver\n---------------------------------\nThe MKL RCICG iterative solver has been implemented. To use it, select \"rcicg\"\nas the type of the linear_solver element in the FEBio input file.\n\n\n11. FEATURE: Diagnostics\n------------------------\n- Reimplemented tangent diagnostics feature which now offers two scenarios:\n  uni-axial and simple shear.\n\n\n12. BUG: Multi step\n--------------------\n- A bug was fixed that was applying inactive traction boundary conditions in a\n  multi-step analysis. \n- A bug in the auto-time stepper could cause negative time steps in multi-step,\n  resulting in the analysis running backwards in time.\n\n\n13. BUG: Rigid constraints\n--------------------------\nA bug was fixed in the initialization of rigid constraints. \n\n\n14. BUG: Sliding2\n-----------------\nA bug was fixed in the sliding2 contact interface for calculation of net contact\npressure.\n\n\n15. BUG: Multi-scale problems\n-----------------------------\n- Users can now use ctrl+c to interrupt multi-scale problems. \n- FEBio will now terminate a multi-scale problem if the micro RVE problem fails.\n\n\n16. BUG: Restart feature\n------------------------\nThe restart feature underwent a major revision and cleanup. Most issues with\nthis feature are now addressed.\n\n\n17. BUG: Eigenvalues\n---------------------\nA bug was fixed in the algorithm that calculates eigenvalues. This will affect\nall aspects of the code where eigenvalues are used. In particular, these are\nthe implementations of the Ogden-type materials.\n\n\n18. BUG: Arruda-Boyce material\n------------------------------\nThe stress and tangent calculations of the Arruda-Boyce material contained\nsome errors.\n\n\n\n===========================================================================\n\n R E L E A S E   1.3.0\n\n===========================================================================\n\n1. FEATURE: FEBio license\n-------------------------\nSupport was added for FEBio license files. This was implemented to allow\ncommercial institutes the right to use FEBio for their research and development.\nCompanies can now purchase a license key that they then use with FEBio.\nAcademic researchers can still use FEBio for free.\n\n2. FEATURE: FEBio format 1.1\n----------------------------\n- A new input format is define for FEBio, namely format 1.1. This format\n  addresses some issues in the older format and reflect some changes to the\n  internal code structure of FEBio. This applies mostly to rigid body constraints.\n  In the old format, these constraints were defined as part of the material\n  definition. In this new format, they have to be defined as part of the new\n  Constraints section.\n- Added support for reading empty xml elements.\n\n\n3. FEATURE: New materials\n-------------------------\n- Implemented referentially orthotropic and referentially transversely\n  isotropic permeability.\n- Added referentially isotropic permeability poroelastic material, FERefIsoPerm.\n- Added FEPerfectOsmometer class to describe equilibrium swelling of the Boyle-\n  van't Hoff type.\n- Modified FEDonnanEquilibrium and FEEllipsoidalFiberDistribution so that they\n  can be user-defined materials.\n- Modified FEElasticMixture to check for duplicate entries in <solid_ids>.\n- Simplified stress and tangent calculations in FERandomFiberNeoHookean an\n  FERandomFiberVerondaWestmann materials.\n- Implemented class FEElasticMixture : public FEElasticMaterial to represent\n  mixtures of elastic solids.\n\n\n4. FEATURE: Three-field hexes\n-----------------------------\nBy default, FEBio uses the three-field formulation for hexahedral elements.\nAn option was added allowing the user to turn three-field formulation on or\noff.\n\n\n5. FEATURE: Plof file\n---------------------\nSprings are now included in the LSDYNA plot file.\n\n6. FEATURE: Heat-flux BC\n------------------------\nImplemented the heat flux boundary condition for heat transfer problems.\n\n\n7. FEATURE: Fluid flux\n----------------------\nA prescribed fluid flux boundary condition was implemented for poroelastic\nanalysis. This boundary condition allows the user to define the fluid flux at\nthe surface of the FE model. \n\n\n8. FEATURE: Implemented biphasic-elastic contact\n------------------------------------------------\nA new sliding contact algorithm has been implemented. This sliding contact\nformulation also includes fluid flow across the contact interface for biphasic\nproblems. If both contacting surfaces are biphasic, the algorithm considers\nthe flow from one surface into the other by forcing the pressure to be the same\non either side of the contact interface.\n\n\n9. FEATURE: Multi-step feature\n------------------------------\nSupport was added for pressured loads and the constant traction loads in the\nmulti-step feature. When defining these boundary conditions as part of the Step\nboundary condition, they will only remain active during this step. In other\nsteps, they will be inactive. \n\n\n10. FEATURE: Tension-only springs\n---------------------------------\nAn alternative spring implementation was implemented that only exerts a force\nin tension. In compression, the spring force is zero.\n\n\n11. FEATURE: Micromaterial\n--------------------------\nThe micromaterial no longer forces skyline solver. The micro-scale problem will\nnow the same linear solver as the macro-problem (Padiso by default).\n\n\n12. BUG: Biphasic contact\n-------------------------\n- A bug was fixed in the biphasic contact augmented Lagrangian convergence\n  criterion on fluid pressure/flux.\n- A limitation was removed that prevented from correctly defining 3-body\n  biphasic contact problem.\n\n\n13. BUG: Memory leak\n--------------------\nA memory leak was fixed that was caused by not deleting the domains properly\ncuring a running restart.\n\n\n\n===========================================================================\n\n R E L E A S E   1.2.2\n\n===========================================================================\n\n1. FEATURE: Micro-material\n--------------------------\nUsing the new micro-material, FEBio has taken a first step in the solution\nof computational homogenization problems. For such problems, the macro-scale\nproblem's material response is described by the iterative solution of a \nmicro-scale problem. \nIn FEBio a new material, called \"micro-material\" is used to solve computational\nhomogenization problems. The material references the micro model which is \nspecified in a separate input file. \n\n\n2. FEATURE: Relative kinematics\n-------------------------------\nAn option was added to describe rigid body kinematics in a local coordinate system\nand connect the rigid bodies in a child-parent hierarchy. This facilitates the\ndescription of complex rigid body prescribed kinematics. \n\n\n3. FEATURE: Set minimum residual\n--------------------------------\nWhenever the residual drops below a minimal value, FEBio assumes that there are\nno force acting on the system and will consider the time step as converged. The\nuser can now set the value of this minimum residual using the <min_residual>\nparameter in the <Control> section of the input file. The default value is 1e-20.\n\n\n4. FEATURE: New constraints section\n-----------------------------------\nA new section was added that will deal with constraints. Although all constraints will\neventually be moved to this section, for now it was added to allow the user to describe rigid kinematics (see item 5).\n\n\n5. FEATURE: Redefine rigid kinematics in multi-step analysis\n------------------------------------------------------------\nThe user can now redefine the rigid body constraints using the Constraints section in\na multi-step analysis. The Constraints section may be repeated in each step, and the user may redefine the rigid body constraints in each step.\n\n\n6. FEATURE: Parameter optimization\n----------------------------------\nA new parameter optimization algorithm was implemented. Although parameter optimization\nhas been implemented for a while, it was not available since it used the NAG library. \nThe NAG routines have now been replaced with a custom implementation of the Levenberg-\nMarquardt method, making the parameter optimization module available for all FEBio users.\n\n\n7. FEATURE: Enhanced loadcurves\n------------------------------\nTwo new features were added to the load curves. First, the user can define the load curve\ntype, which can be set to step, linear or smooth. For the smooth type, FEBio will inter-\npolate the load curve values with a cubic polynomial creating smooth curves (C1 continuous).\nSecond, the user can now also define the extend mode which defines the values of the load\ncurve outside its specified domain. The new options are set as attributes to the <loadcurve>\nelement. For example, the following defines a smooth load curve with linear extrapolation\noutside the domain.\n\n<loadcurve type=\"smooth\" extend=\"extrapolate\">\n\nThe extend mode can take on the following values: \"constant\", \"extrapolate\", \"repeat\",\n\"repeat offset\". The default value is \"constant\". \n\n\n8. FEATURE: Nodally integrated tet\n----------------------------------\nA new nodally integrated tet element is available which performs better in incompressible\nand bending problems. Performs better in the sense that it suffers less from locking \nproblems that often cause problems using the default linear tet elements in FEBio.\n\n\n9. FEATURE: Constant surface traction boundary condition\n--------------------------------------------------------\nA new boundary condition was implemented which allows the user to apply a constant traction\nforce to a surface. Unlike the pressure boundary force, the surface traction will remain\nindependent of the deformation.\n\n\n10. BUG: Crash when using triangular shells\n-------------------------------------------\nA bug was found and fixed in the input routine for triangular shells. The bug was responsible\nfor unexpected crashes that could occur anywhere during the run.\n\n\n11. FEATURE: FEBio compiled on Vista 64bit and Win7 32bit\n---------------------------------------------------------\nExecutables have been compiled on Windows Vista, 64 bit and Windows7 32bit.  If the executable\nis set to XP Comptability mode, it will not run correctly.\n\n\n===========================================================================\n\n R E L E A S E   1.2.1\n\n===========================================================================\n\n\n1. FEATURE: mat_axis\n--------------------\nA mat_axis option was added for the ElementData section. The user can now \noverride the material axis for each element individually.\n\n===========================================================================\n\n R E L E A S E   1.2.0\n\n===========================================================================\n\n\n1. FEATURE: Sparse linear solvers\n---------------------------------\nFEBio now adds support for several fast sparse linear solvers, such as Pardiso\nand SuperLU. The preferred linear solver can either be specified in the \nconfiguration file or in the FEBio input file by adding the following line\nto the Control section (e.g. for Pardiso):\n\n<linear_solver type=\"pardiso\"></linear_solver>\n\nThese new sparse linear solvers are faster than the default skyline solver\nsince they take better advantage of the sparsity of the stiffness matrix and\ncan use multiple processors. Note that if you want to use multiple processors\nyou may need to set specific environment variables. We refer to the documentation\nof the linear solvers for more details. \n\n\n2. FEATURE: Contact improvements\n--------------------------------\nTwo new frictionless sliding contact algorithms were implemented which add more \nstability to contact analysis. The first is referred to as the facet-to-facet\nsliding, and the second one is reffered to as sliding2. To select a particular\nalgorithm, set the type attribute of the contact xml-element to the appropriate\nvalue (e.g. for facet-to-facet sliding). \n\n<contact type=\"facet-to-facet sliding\"> ... </contact>\n\nFor sliding2, simply enter the value \"sliding2\". The sliding2 algorithm is\ninherently unsymmetric, which means you'll need to use a non-symmetric linear\nsolver (both pardiso and superlu can handle non-symmetric matrices). However,\nthis algoritm does have a symmetric mode, although this mode was shown not to\nbe as robust as the non-symmetric. You can choose between the two modes by\nsetting the following flag in the contact section.\n\n<symmetric_stiffness>1</symmetric_stiffness>\n\nA value of 0 will use the non-symmetric mode. Please see the user's manual for\nmore details on these new contact algorithms.\n\n\n3. FEATURE: Biphasic contact\n----------------------------\nThe new sliding contact algorithm sliding2 can also deal with biphasic contact.\nHere two poroelastic materials are brought in contact and can exchange fluid\nacross the contact interface. The contact algorithm also automatically enforces\na free-draining boundary condition on the part of the surface that is not in\ncontact. To use biphasic contact you'll need to define two contacting poroelastic\nsurfaces and you need to set the contact type to \"sliding2\".\n\n<contact type=\"sliding2\"> ... </contact>\n\nIn addition, you'll need to define several control parameters which are described\nin detail in the user's manual.\n\n\n4.FEATURE: auto-penalty\n-----------------------\nA new auto-penalty algorithm was implemented for all sliding contact algorithms. \nThis algorithm derives the penalty factor from the initial elasticity tensor of\nthe material. The advantage is that the algorithm now works with any material. \nTo use the new algorithm, add the following xml-element to the contact section\nof the input file.\n\n<auto_penalty>1</auto_penalty>\n\nNote that the old algorithm is still available for now for backward compatibility\nbut is considered obselete and may be removed in future releases.\n\n\n5. FEATURE: Isotropic elastic\n-----------------------------\nA new material was implemented to replace the linear elastic and the St.Venant-\nKirchhoff material. This material is valid for both small deformations and \nlarge deformations and reduces to linear elasticity for small strains. An\nexample illustrating the use of this material:\n\n<material id=\"1\" type=\"isotropic elastic\">\n\t<E>1.0</E>\n\t<v>0.45</v>\n</material>\n\nNote that the linear elastic and the St.Venant-Kirchhoff materials are still\navailable for backward compatibility, but it is highly recommended to use\nthis new material instead.\n\n\n6. FEATURE: Convergence tolerances\n----------------------------------\nA simple mechanism was implemented to turn off particular convergence tolerances.\nAs of this version, setting a value of 0 (zero) for the displacement (dtol),\nenergy (etol) or residual (rtol) will deactivate the corresponding convergence \ncriteria. Similarly, setting a value of zero for the line search tolerance (lstol),\nwill deactivate the line search.\n\n\n7. FEATURE: Time command\n------------------------\nThe time command will display the time that has passed so far and also gives an\nestimate of the remaining time. The command can be entered at the FEBio command \nprompt which is activated after interrupting the run with ctrl+c.\n\n\n8. BUG: Running Linux input files on Windows\n--------------------------------------------\nAn issue was identified that prevented files created on a linux machine to be run\non windows. The problem had to do with way the different platforms deal with line\nendings. The issue has been resolved, and both platforms should now be able to read\nfiles that were created on the other.\n\n\n===========================================================================\n\n R E L E A S E   1.1.7\n\n===========================================================================\n\n1. FEATURE: Initial nodal velocities\n------------------------------------\nSupport fot initial nodal velocities was added to FEBio and to the FEBio\ninput file. To set initial nodal velocities, add the Initial section to the\ninput file. \n\n<febio_spec>\n        ...\n\t<Initial>\n\t\t<velocity>\n\t\t\t<node id=\"1\">1,0,0</node>\n\t\t\t...\n\t\t\t<node id=\"n\">1,0,0</node>\n\t\t</velocity>\n\t</Initial>\n</febio_spec>\n\nNote that you need to run the problem dynamically for this to have any effect.\nTo run a dynamic analysis, add the following element to the Control section\nof your input file.\n\n<analysis type=\"dynamic\"></analysis>\n\n\n2. FEATURE: Strain-dependant poroelasticity\n-------------------------------------------\nA new poroelastic material was added that has a strain dependant permeability\ntensor. This material uses the constitutive model proposed by Holmes and Mow.\n\n\n3. FEATURE: Customizing plot file output\n----------------------------------------\nYou can now replace the predefined LSDYNA data fields with FEBio datafields. \nTo do this, define the plotfile element in the Output section of your input file. \nYou can then define maps to redefined the meaning of an output field in the plot\nfile. For example, \n\n<Output>\n  <plotfile>\n    <map field=\"velocity\">CONTACT_TRACTION</map>\n  </plotfile>\n</Output>\n\nwill replace the velocity data with contact traction data.\n\n\n4. FEATURE: Frictional contact\n------------------------------\nThe sliding interface is augmented with a frictional algorithm. To use friction\nsimply add the following two parameters to the your contact section.\n\n<fric_coeff>0.1</fric_coeff>\n<fric_penalty>1</fric_penalty>\n\nThe first parameter sets the friction coefficient. The second parameter is the\nfrictional penalty parameter.\n\n\n5. FEATURE: Contact stiffness multipliers\n-----------------------------------------\nThe user can set a scalar multiplier for both the normal as well as the \ntangential stiffness. The \"ktmult\" parameter defines the multiplier for the\ntangential component (default = 0), and \"knmult\" the multiplier for the \nnormal component. These parameters need to be set in the sliding interface\ncontact section of your input file.\n\n<contact type=\"sliding_with_gaps\">\n  ...\n  <knmult>1</knmult>\n  <ktmult>0</ktmult>\n  <surface>\n  ...\n</contact>\n\n\n6. FEATURE: Configuration file\n------------------------------\nFEBio now use a configuration file to store platform dependant parameters. \nCurrently it only stores the default linear solver you wishes to use which\nis set to Pardiso on most platforms. The configuration file needs to be\nstored in the same location of the FEBio executable. See the manual for more\noptions about using this configuration file. \n\n7. BUG: Node numbering\n----------------------\nWhen a negative jacobian occurs the node numbering were reported as zero-based\nin stead of one-based. This has been fixed.\n\n\n===========================================================================\n\n R E L E A S E   1.1.6\n\n===========================================================================\n\n1. FEATURE: Multi-step analysis\n-------------------------------\nThe user can now split the analysis in separate steps where in each step\nhe can define different control parameters and boundary conditions. This\nis useful for instance for defining time dependant boundary conditions or\nfor switching between (quasi-) static and dynamic analysis. Multi-step\nproblems are defined slightly different. For each step you need to define\na <Step> section in the input file. The overall structure of the input file\nnow looks like this.\n\n<febio_spec>\n\t<Control>\n\t\t<!-- global control parameters (e.g. title) --!>\n\t</Control>\n\t<Material>\n\t\t<!-- material data --!>\n\t</Material>\n\t<Boundary>\n\t\t<!-- global boundary constraints --!>\n\t</Boundary>\n\t<Step>\n\t\t<Control>\n\t\t\t<!-- local control parameters --!>\n\t\t</Control>\n\t\t<Boudary>\n\t\t\t<!-- local boundary constraints --!>\n\t\t</Boundary>\n\t</Step>\n\t<Step>\n\t\t...\n\t</Step>\n</febio_spec>\n\nIn each step section, you can redefine the control parameters, as well as \nspecify boundary conditions that will only be enforced during this step.\nNote that the boundary constraints defined outside the step section are\nconsidered global and will remain active during all steps.\n\n\n2. FEATURE: Ogden material\n--------------------------\nThe Ogden material was added to the FEBio's material library. To use this\nmaterial set the material type to \"Ogden\". Please see the user manual for\na description of the material parameters.\n\n<material id=\"1\" type=\"Ogden\">\n\t<c1>1</c1>\n\t<m1>2</m1>\n\t<k>100</k>\n</material>\n\nNote that this material is formulated using an uncoupled formulation. Incom-\npressibility can be enforced by either increasing the bulk modulus (k) and\nby setting the augmentation flag (<laugon>) to \"1\".\n\n\n3. BUG FIX: Contact\n-------------------\nA bug was fixed in the frictionless contact algorithm. Improved convergence\ncan be expected for some contact problems. \n\n===========================================================================\n\n R E L E A S E   1.1.5\n\n===========================================================================\n\n1. FEATURE: Visco-elasticity\n----------------------------\nVisco-elasticity has been added to the material library. To use the visco-elastic\nmaterial you need to add two materials: (1) the visco-elastic material and (2)\na material describing the elastic equilibrium repsonse.\n\n<material id=\"1\" type=\"neo-Hookean\">\n\t<E>1</E>\n\t<v>0.33</v>\n</material>\n<material id=\"2\" type=\"visco-elastic\">\n\t<t1>0.01</t1>\n\t<g1>1</g1>\n\t<solid_id>1</solid_id>\n</material>\n\nThe solid_id element references the material id of the elastic material. \nThe visco-elastic material is described using a Prony series. Each term\nin this series is parameterized by a relaxation time (ti) and a scalar \nmodulus gi. Up to six terms can be prescribed.\n\n\n2. FEATURE: Continuous fiber material\n-------------------------------------\nA new material model was added that has a 2D random fiber distribution. \nThis model replaces the \"random\" fiber type that could be used to describe\nrandom fiber materials. Currently, this material uses a Mooney-Rivlin matrix.\n\n3. FEATURE: Body forces for shells\n----------------------------------\nBody forces can now also be allowed to shell geometry.\n\n4. FEATURE: Linear orthotropic material\n---------------------------------------\nA linear orthotropic material was added to FEBio's material library.\n\n5. FEATURE: Linear constraints\n------------------------------\nThe user can now apply linear constraints to the problem. A linear constraint\ndefines a degree of freedom in function of the other dofs. It is always of \nthe form.\n\n\tui = A1*u1+ ... + A(i-1)*u(i-1) + A(i+1)*u(i+1) + ... + An*un\n\nAs many linear constraints can be applied as desired. Only the non-zero\ncoefficients need to be specified.\n\n6. FEATURE: SuperLU solver\n--------------------------\nThe SuperLU solver has been implemented. To select it, add the following\nline to the Control section of the input file.\n\n<linear_solver type=\"superlu\"></linear_solver>\n\n7. BUG FIX: Off-diagonal strains in logfile\n-------------------------------------------\nThe off-diagonal strians reported in the logfile were off by a constant\nterm of 0.5.\n\n\n===========================================================================\n\n R E L E A S E   1.1.4\n\n===========================================================================\n\n1. FEATURE: New muscle and tendon material models\n-------------------------------------------------\nThe muscle and tendon material models due to Silvia Blemker have been\nimplemented. These models can be used to simulate the passive and active\nresponse of muscles and the passive response of tendons. See the user\nmanual for more details on these material models.\n\n2. FEATURE: Strain output in log file\n-------------------------------------\nThe element strain values can now also be outputted to the log file. See\nthe user manual for more details.\n\n2. BUGFIX: Plot file data for triangular shells\n-----------------------------------------------\nThe stress and strain data for triangular shells was not stored correctly\nin the plot database file. This has now been fixed. \n\n3. BUGFIX: Divergence logic\n---------------------------\nA small bug was fixed in the divergence logic.\n\n===========================================================================\n\n R E L E A S E   1.1.3\n\n===========================================================================\n\n1. FEATURE: Iterative linear solver\n-----------------------------------\nAn iterative linear solver has been added that can be used as an alternative\nto the default skyline solver to solve the system of linear equations Ku = R.\nThis iterative solver is attractive for large problems that either require too \nmuch memory or take too long to factorize the stiffness matrix. \nTo select the solver, add the following section to the Control section\nof your input file.\n\n<linear_solver type=\"conjugate gradient\">\n\t<tolerance>0.01</tolerance>\n\t<max_iterations>50</max_iterations>\n\t<print_level>0</print_level>\n</linear_solver>\n\nThis solver takes three parameters (shown in the example above with their \ndefault values). The \"tolerance\" parameter specifies the convergence tolerance\non the residual norm of the linear system, where the residual is defined here\nas r = R - Ku. The \"max_iterations\" parameter sets a maximum on the number of\niterations. The \"print_level\" parameter sets the amount of output that the\nlinear solver will generate. When set to zero (=default), no output is generated.\nWhen set to a non-zero value, convergence information is printed only to the\nscreen (not to the logfile!).\n\n\n2. FEATURE: Shell strains\n-------------------------\nFEBio can now output shell strains. To output shell strains set the \"shell_strain\"\nparameter to 1 in the \"plotfile\" section of the \"Output\" section of your input file.\n\n<Output>\n\t<plotfile>\n\t\t<shell_strain>1</shell_strain>\n\t</plotfile>\n</Output>\n\nThe default value for this flag is 0 (=off).\n\n\n3. FEATURE: Progress indicator in window title\n----------------------------------------------\nA progress indicator is displayed in the title of the console window\nor terminal for linux/mac). That way the user can monitor the progress of a\nrun even when the window is minimized.\n\n\n4. BUG FIX: NIKE3D wedge element\n--------------------------------\nFEBio read the node numbering incorrectly of the wedge element for NIKE3D\ninput files.\n\n\n5. BUG FIX: Linesearch\n----------------------\nA small bug was fixed in the linesearch algorithm.\n\n\n===========================================================================\n\n R E L E A S E   1.1.2\n\n===========================================================================\n\n1. FEATURE: Augmented Lagrangian for incompressible materials\n-------------------------------------------------------------\nAn augmented Lagrangian algorithm was added for the incompressibility\nenforcement. To use this feature, add the following two parameters\nto any material that uses a decoupled formulation (e.g. Mooney-Rivlin)\n\n<laugon>1</laugon>\n<atol>0.05</atol>\n\nThe first parameter activates the augmented Lagrangian for incompres-\nsibility and the second parameter specifies the augmented Lagrangian\nconvergence tolerance. \n\n\n2. FEATURE: TC nonlinear orthotropic\n------------------------------------\nA tension-compression nonlinear orthotropic material was added. To use\nthis material set the material type to \"TC nonlinear orthotropic\" and\ndefine the material parameters. See the user's manual for a description\nof the material parameters.\n\n\n3. FEATURE: Contact auto penalty calculation\n--------------------------------------------\nThe initial penalty factor can now be calculated automatically. Simply\nspecify the \"auto\" attribute and as value \"on\" or \"off\" to use the \nauto penalty estimation. Default is off.\n\n<penalty auto=\"on\">1</penalty>\n\nIn this case the specified value of the penalty element is a scale factor.\n\n\n4. FEATURE: Max condition number\n--------------------------------\nWhen the condition number of the stiffness matrix gets too large, the matrix\nbecomes ill-conditioned. When this happens, FEBio will reform the stiffness\nmatrix. The maximum value for the condition number (previously hardcoded at 1e5)\ncan now be set by the user. To set this value, add the \"cmax\" parameter to\nthe control section of your input file.\n\n<cmax>1e5</cmax>\n\n\n5. BUG FIX: Contact with tetrahedral elements\n---------------------------------------------\nThere were a few bugs found and fixed in the contact algorithm \nfor tetrahedral elements. One bug was rather severe since it could\ncrash FEBio.\n\n\n6. BUG FIX: Dilatational stiffness\n----------------------------------\nA bug was found and corrected in the routine that calculates the dilational\nstiffness. This may improve convergence behavior for all materials using a \ndecoupled formulation. \n\n\n7. BUG FIX: Auto timestepper\n----------------------------\nA bug was found and fixed in the auto timestepper routine. Specifically,\nthe max time step size was sometimes not respected when it was specified\nthrough a loadcurve.\n\n\n===========================================================================\n\n R E L E A S E   1.1.1\n\n===========================================================================\n\n1. FEATURE: Data check option\n-----------------------------\nA command line option was added that requests FEBio to check the file for \npotential problems, but without running the file. To use the feature add\nthe -c option on the command line. For instance, the following command line\nwill perform a data check on the file without running the problem.\n\n>febio -i in.feb -c [enter]\n\n\n2. FEATURE: Tied contact interface\n----------------------------------\nA tied contact interface was added. This interface allows you to connect two\nnon-conforming meshes. A tied contact is defined similarly as a sliding \ncontact interface, except the contact type is now set to \"tied\" and there \nis two pass option. For, instance the following example defines a simple\ntied interface.\n\n<contact type=\"tied\">\n\t<tolerance>0.01</tolerance>\n\t<penalty>50</penalty>\n\t<surface type=\"master\">\n\t\t<quad4>1,2,3,4</quad4>\n\t</surface>\n\t<surface type=\"slave\">\n\t\t<quad4>5,6,7,8</quad4>\n\t</surface>\n</contact>\n\n\n3. BUG FIX: Divergence logic\n----------------------------\nA small bug was fixed in the algorithm that determines whether an iteration\nis diverging or not.\n\n\n4. BUG FIX: Rigid body reaction forces\n--------------------------------------\nWhen using prescribed rigid body forces, the rigid body reaction forces where\nnot reported accurately.\n\n\n5. BUG FIX: Rigid body data logging\n-----------------------------------\nA bug was fixed in the data logging feature of rigid body data when the user\ndid not explicitly define the rigid material id(s).\n\n\n6. BUG FIX: Bug in bandwidth optimizer\n--------------------------------------\nA bug was found and fixed in the bandwidth optimization algorithm.\n\n\n7. BUG FIX: St.Venant-Kirchhoff\n-------------------------------\nThe St.Venant-Kirchhoff material was not recognized by FEBio when spelled\nlike this. Instead it expected a hyphen instead of the first dot. This is \nfixed and now the correct type for this material is spelled as follow:\n\"St.Venant-Kirchhoff\"\n\n\n===========================================================================\n\n R E L E A S E   1.1.0\n\n===========================================================================\n\n1. FEATURE: Bandwidth reduction algorithm\n-----------------------------------------\nA bandwidth reduction algorithm has been implemented for the default Skyline\nsolver. This algorithm attempts to minimize the bandwidth of the global\nstiffness matrix. The result can be a dramatic reduction in memory\nrequirements and computing time. To use the algorithm, simply add the\nfollowing option to the Control section of your input file.\n\n<optimize_bw>1</optimize_bw>\n\n\n2. FEATURE: Data Logging\n------------------------\nThe user can request specific data to be stored in the log file. Data can be\nrequested for nodes, elements and rigid bodies. To request additional data you\nneed to define the new Output section in your input file. In the Output\nsection, define a logfile section and in this section define the data that you\nwish to add to the log file. \n\n<Output>\n\t<logfile>\n\t\t<node_data data=\"x;y;z\"></node_data>\n\t</logfile>\n</Output>\n\n\n3. FEATURE: Rigid wall interface\n--------------------------------\nThe rigid wall interface is a simplification of the sliding contact interface\nwhere the master surface is a movable wall which can be defined implicitly\nusing a plane equation. No geometry needs to be defined for the wall. The\nposition of the wall can be controlled with a load curve.\n\n<contact type=\"rigid_wall\">\n\t<plane lc=\"1\">0,0,1,0</plane>\n\t<tolerance>0.1</tolerance>\n\t<penalty>1</penalty>\n</contact>\n\n\n4. FEATURE: User fiber distribution\n-----------------------------------\nAn option has been added for transversely isotropic materials to input a user\ndefined fiber distribution. The local fiber direction can now be defined on an\nelement per element basis. To use this option set the fiber type to \"user\" in\nthe material section. In addition, define the new ElementData section in the\nGeometry section of your input file. In this section specify for each element\nthe local fiber direction.\n\n<ElementData>\n\t<element id=\"1\">\n\t\t<fiber>1,0,0</fiber>\n\t</element> \n</ElementData>\n\n\n5. FEATURE: Shell formulation\n-----------------------------\nFEBio now supports shell elements. The current formulation allows either four\nnode quadrilateral or three node triangular shells with possible thickness\nvariations over the shells surface. Shells are defined in the Geometry\nsection of your input file. Use quad4 for a quadrilateral element and tri3\nfor a triangular element.\n\n<Elements>\n\t<quad4 id=\"1\" mat=\"1\">1,2,3,4</quad4>\n\t<tri3 id=\"2\" mat=\"1\">5,6,7</tri3>\n</Elements>\n\nThe shell thickness is defined in the ElementData section and needs to be\nspeficied for each node.\n\n<ElementData>\n\t<element id=\"1\">\n\t\t<thickness>0.02,0.02,0.02,0.02</thickness>\n\t</element>\n</ElementData>\n\n\n6. FEATURE: Restart File\n------------------------\nThe user can define a restart input file that can be used to alter some\ncontrol parameters when restarting a previously terminated run. To request the\ncreation of a restart file, add the following option to the Control section.\n\n<restart>1</restart>\n\nThis option requests the creation of a dump file that can be used to restart\nthe analysis. When restarting the analysis you can specify a restart input file\nthat may override some of the original control options.\n\n\n7. FEATURE: Rigid Body Forces\n-----------------------------\nPrescribed forces can now be applied directly onto the rigid bodys center of\nmass. To specify a force simply set the type attribute of the corresponding\nrigid bodys degree of freedom to force and set the value to the forces\nmagnitude. Applying a force to a rotational degree of freedom defines a torque\naround the center of mass.\n\n<material id=\"1\" type=\"rigid body\">\n\t<trans_x type=\"force\" lc=\"1\">5.0</trans_x>\n\t...\n</material>\n\n8. FEATURE: Plot Level\n----------------------\nThe user has now more control over when exactly a state is added to the\nplotfile. Choices include: never, all major iterations, all minor iterations,\nonly for must points, .... Enter the following option in the Control section of\nyour input file to set the plot level.\n\n<plot_level>PLOT_MAJOR_ITRS</plot_level>\n\n\n9. BUG FIX: Improved Contact\n----------------------------\nSeveral bugs were found and fixed in the contact implementation. Improved\nconvergence can be expected for contact analyses.\n\n\n10. BUG FIX: Linesearch\n-----------------------\nA bug was found and fixed in the linesearch algorithm. Problems that depend\nheavily on a linesearch will show improved convergence.\n\n\n11. BUG FIX: Memory allocation failure\n--------------------------------------\nFEBio now prints an error message when memory allocation for the global\nstiffness matrix fails.\n"
  },
  {
    "path": "Documentation/febio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<febio_config version=\"3.0\">\n\t<default_linear_solver type=\"pardiso\"></default_linear_solver>\n\t<import>FEBioHeat.dll</import>\n\t<import>FEBioChem.dll</import>\n</febio_config>\n"
  },
  {
    "path": "FEAMR/FEAMR.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEAMR.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEErosionAdaptor.h\"\n#include \"FEHexRefine.h\"\n#include \"FEHexRefine2D.h\"\n#include \"FETetRefine.h\"\n#include \"FEMMGRemesh.h\"\n#include \"FETestRefine.h\"\n#include \"FEVariableCriterion.h\"\n#include \"FEElementSelectionCriterion.h\"\n#include \"FEScaleAdaptorCriterion.h\"\n#include \"FEFilterAdaptorCriterion.h\"\n#include \"FEDomainErrorCriterion.h\"\n#include \"FEElementDataCriterion.h\"\n#include \"FETetQualityCriterion.h\"\n#include \"FEElementQualityCriterion.h\"\n#include \"FEAMRPlot.h\"\n\n//-----------------------------------------------------------------------------\nvoid FEAMR::InitModule()\n{\n// mesh adaptors\nREGISTER_FECORE_CLASS(FEErosionAdaptor, \"erosion\");\nREGISTER_FECORE_CLASS(FEHexRefine     , \"hex_refine\");\nREGISTER_FECORE_CLASS(FEHexRefine2D   , \"hex_refine2d\");\nREGISTER_FECORE_CLASS(FETetRefine     , \"tet_refine\");\nREGISTER_FECORE_CLASS(FEMMGRemesh     , \"mmg_remesh\");\nREGISTER_FECORE_CLASS(FETestRefine    , \"test_refine\", FECORE_EXPERIMENTAL);\n\n// adaptor criteria\nREGISTER_FECORE_CLASS(FEVariableCriterion        , \"max_variable\");\nREGISTER_FECORE_CLASS(FEElementSelectionCriterion, \"element_selection\");\nREGISTER_FECORE_CLASS(FEScaleAdaptorCriterion    , \"math\");\nREGISTER_FECORE_CLASS(FEMinMaxFilterAdaptorCriterion, \"min-max filter\");\nREGISTER_FECORE_CLASS(FEDomainErrorCriterion, \"relative error\");\nREGISTER_FECORE_CLASS(FEElementDataCriterion, \"element data\");\nREGISTER_FECORE_CLASS(FETetQualityCriterion, \"tet-quality\");\nREGISTER_FECORE_CLASS(FEMeanRatioQualityCriterion, \"mean-ratio\");\nREGISTER_FECORE_CLASS(FEScaledJacobianQualityCriterion, \"scaled Jacobian\");\n\n// plot variables\nREGISTER_FECORE_CLASS(FEPlotTetQuality, \"tet-quality\");\nREGISTER_FECORE_CLASS(FEPlotMeanRatio, \"mean-ratio\");\nREGISTER_FECORE_CLASS(FEPlotScaledJacobian, \"scaled-Jacobian\");\n}\n"
  },
  {
    "path": "FEAMR/FEAMR.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"feamr_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEAMR namespace encapsulates all classes that belong to the FEAMR library\nnamespace FEAMR\n{\n\t// initialize the module\n\tFEAMR_API void InitModule();\n\n} // namespace FEAMR\n"
  },
  {
    "path": "FEAMR/FEAMRPlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEAMRPlot.h\"\n#include <FECore/FEDomain.h>\n#include <FECore/FEMesh.h>\n#include \"FETetQualityCriterion.h\"\n#include \"FEElementQualityCriterion.h\"\n\nbool FEPlotTetQuality::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tif (el.Shape() == ET_TET4)\n\t\t{\n\t\t\tdouble q = FETetQualityCriterion::TetQuality(el);\n\t\t\ta << q;\n\t\t}\n\t\telse a << 0.0;\n\t}\n\treturn true;\n}\n\nbool FEPlotMeanRatio::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tdouble q = FEMeanRatioQualityCriterion::ElementQuality(el);\n\t\ta << q;\n\t}\n\treturn true;\n}\n\nbool FEPlotScaledJacobian::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tdouble q = FEScaledJacobianQualityCriterion::ElementQuality(el);\n\t\ta << q;\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FEAMR/FEAMRPlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPlotData.h>\n#include <FECore/FEElement.h>\n\nclass FEPlotTetQuality : public FEPlotDomainData\n{\npublic:\n\tFEPlotTetQuality(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotMeanRatio : public FEPlotDomainData\n{\npublic:\n\tFEPlotMeanRatio(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotScaledJacobian : public FEPlotDomainData\n{\npublic:\n\tFEPlotScaledJacobian(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n"
  },
  {
    "path": "FEAMR/FEDomainErrorCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDomainErrorCriterion.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEMesh.h>\n#include <FECore/FEMeshAdaptor.h> // for projectToNodes\n\nBEGIN_FECORE_CLASS(FEDomainErrorCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_error, \"error\");\n\tADD_PROPERTY(m_data, \"data\");\nEND_FECORE_CLASS();\n\nFEDomainErrorCriterion::FEDomainErrorCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_error = 0.0;\n\tm_data = nullptr;\n}\n\ndouble MinEdgeLengthSqr(FEMesh* pm, FEElement& el)\n{\n\tdouble h2 = 1e99;\n\tint ne = el.Nodes();\n\tfor (int i = 0; i < ne; ++i)\n\t{\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tif (i != j)\n\t\t\t{\n\t\t\t\tvec3d a = pm->Node(el.m_node[i]).m_r0;\n\t\t\t\tvec3d b = pm->Node(el.m_node[j]).m_r0;\n\n\t\t\t\tdouble L2 = (a - b).norm2();\n\t\t\t\tif (L2 < h2) h2 = L2;\n\t\t\t}\n\t\t}\n\t}\n\treturn h2;\n}\n\nFEMeshAdaptorSelection FEDomainErrorCriterion::GetElementSelection(FEElementSet* elemSet)\n{\n\t// get the mesh\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tint NE = mesh.Elements();\n\tint NN = mesh.Nodes();\n\n\t// calculate the recovered nodal stresses\n\tvector<double> sn(NN);\n\tprojectToNodes(mesh, sn, [=](FEMaterialPoint& mp) {\n\t\tdouble val = 0.0;\n\t\tm_data->GetMaterialPointValue(mp, val);\n\t\treturn val;\n\t});\n\n\t// the element list of elements that need to be refined\n\tFEMeshAdaptorSelection elemList;\n\n\t// find the min and max stress values\n\tFEElementIterator it(&mesh, elemSet);\n\tdouble smin = 1e99, smax = -1e99;\n\tfor (; it.isValid(); ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tint ni = el.GaussPoints();\n\t\tfor (int j = 0; j < ni; ++j)\n\t\t{\n\t\t\tdouble sj = 0.0;\n\t\t\tm_data->GetMaterialPointValue(*el.GetMaterialPoint(j), sj);\n\t\t\tif (sj < smin) smin = sj;\n\t\t\tif (sj > smax) smax = sj;\n\t\t}\n\t}\n\tif (fabs(smin - smax) < 1e-12) return elemList;\n\n\t// calculate errors\n\tdouble ev[FEElement::MAX_NODES];\n\tit.reset();\n\tfor (int i = 0; it.isValid(); ++it, ++i)\n\t{\n\t\tFEElement& el = *it;\n\t\tint ne = el.Nodes();\n\t\tint ni = el.GaussPoints();\n\n\t\t// get the nodal values\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tev[j] = sn[el.m_node[j]];\n\t\t}\n\n\t\t// evaluate element error\n\t\tdouble max_err = 0;\n\t\tfor (int j = 0; j < ni; ++j)\n\t\t{\n\t\t\tdouble sj = 0.0;\n\t\t\tm_data->GetMaterialPointValue(*el.GetMaterialPoint(j), sj);\n\n\t\t\tdouble snj = el.Evaluate(ev, j);\n\n\t\t\tdouble err = fabs(sj - snj) / (smax - smin);\n\t\t\tif (err > max_err) max_err = err;\n\t\t}\n\n\t\t// calculate size metric (if m_error defined)\n\t\tdouble s = max_err;\n\t\tif (m_error > 0)\n\t\t{\n\t\t\ts = (max_err > m_error ? m_error / max_err : 1.0);\n\t\t}\n\t\tif (s <= 0.0) s = 1.0;\n\t\telemList.push_back(el.GetID(), s);\n\t}\n\n\t// create the element list of elements that need to be refined\n\treturn elemList;\n}\n"
  },
  {
    "path": "FEAMR/FEDomainErrorCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include \"feamr_api.h\"\n\nclass FEMaterialPoint;\n\n//-----------------------------------------------------------------------------\nclass FEAMR_API FEDomainErrorCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEDomainErrorCriterion(FEModel* fem);\n\n\tFEMeshAdaptorSelection GetElementSelection(FEElementSet* elset) override;\n\nprivate:\n\tdouble\tm_error;\n\tFEMeshAdaptorCriterion*\tm_data;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEDomainShapeInterpolator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEDomainShapeInterpolator.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEOctreeSearch.h>\n\nFEDomainShapeInterpolator::FEDomainShapeInterpolator(FEDomain* domain)\n{\n\tm_dom = domain;\n\tm_mesh = m_dom->GetMesh();\n\tm_os = nullptr;\n}\n\nFEDomainShapeInterpolator::~FEDomainShapeInterpolator()\n{\n\tdelete m_os;\n}\n\nbool FEDomainShapeInterpolator::Init()\n{\n\tif (m_mesh == nullptr) return false;\n\n\tif (m_os == nullptr)\n\t{\n\t\tm_os = new FEOctreeSearch(m_dom);\n\t\tif (m_os->Init() == false) return false;\n\t}\n\n\tint nodes = m_trgPoints.size();\n\tm_data.resize(nodes);\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tData& di = m_data[i];\n\t\tvec3d ri = m_trgPoints[i];\n\n\t\t// find the element\n\t\tdi.r[0] = di.r[1] = di.r[2] = 0.0;\n\t\tdi.el = (FESolidElement*)m_os->FindElement(ri, di.r);\n\t\tif (di.el == nullptr)\n\t\t{\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\t\tassert(di.el->GetMeshPartition() == m_dom);\n\t}\n\n\treturn true;\n}\n\nvoid FEDomainShapeInterpolator::SetTargetPoints(const vector<vec3d>& trgPoints)\n{\n\tm_trgPoints = trgPoints;\n}\n\nbool FEDomainShapeInterpolator::SetTargetPoint(const vec3d& r)\n{\n\tm_trgPoints.clear();\n\tm_trgPoints.push_back(r);\n\treturn Init();\n}\n\nbool FEDomainShapeInterpolator::Map(std::vector<double>& tval, function<double(int sourceNode)> src)\n{\n\t// update solution\n\tint nodes = m_trgPoints.size();\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tData& di = m_data[i];\n\n\t\t// update values\n\t\tdouble v[FEElement::MAX_NODES] = { 0 };\n\t\tfor (int j = 0; j < di.el->Nodes(); ++j) v[j] = src(di.el->m_lnode[j]);\n\t\tdouble vl = di.el->evaluate(v, di.r[0], di.r[1], di.r[2]);\n\t\ttval[i] = vl;\n\t}\n\n\treturn true;\n}\n\ndouble FEDomainShapeInterpolator::Map(int inode, function<double(int sourceNode)> f)\n{\n\tData& di = m_data[inode];\n\tdouble v[FEElement::MAX_NODES];\n\tfor (int j = 0; j < di.el->Nodes(); ++j) v[j] = f(di.el->m_lnode[j]);\n\tdouble vl = di.el->evaluate(v, di.r[0], di.r[1], di.r[2]);\n\treturn vl;\n}\n\nvec3d FEDomainShapeInterpolator::MapVec3d(int inode, function<vec3d(int sourceNode)> f)\n{\n\tData& di = m_data[inode];\n\tvec3d v[FEElement::MAX_NODES];\n\tfor (int j = 0; j < di.el->Nodes(); ++j) v[j] = f(di.el->m_lnode[j]);\n\tvec3d vl = di.el->evaluate(v, di.r[0], di.r[1], di.r[2]);\n\treturn vl;\n}\n"
  },
  {
    "path": "FEAMR/FEDomainShapeInterpolator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEMeshDataInterpolator.h\"\n\nclass FEDomain;\nclass FESolidElement;\nclass FEMesh;\nclass FEOctreeSearch;\n\n//! Maps data by using element shape functions\nclass FEDomainShapeInterpolator : public FEMeshDataInterpolator\n{\n\tstruct Data\n\t{\n\t\tFESolidElement*\t\tel;\n\t\tdouble\t\t\t\tr[3];\n\t};\n\npublic:\n\tFEDomainShapeInterpolator(FEDomain* domain);\n\t~FEDomainShapeInterpolator();\n\n\tbool Init() override;\n\n\tvoid SetTargetPoints(const std::vector<vec3d>& trgPoints);\n\tbool SetTargetPoint(const vec3d& r) override;\n\n\tbool Map(std::vector<double>& tval, std::function<double(int sourceNode)> src) override;\n\n\tdouble Map(int inode, std::function<double(int sourceNode)> src) override;\n\tvec3d MapVec3d(int inode, std::function<vec3d(int sourceNode)> src) override;\n\nprivate:\n\tFEDomain*\tm_dom;\n\tFEMesh*\tm_mesh;\n\n\tFEOctreeSearch*\tm_os;\n\tstd::vector<vec3d>\tm_trgPoints;\n\tstd::vector<Data>\tm_data;\n};\n\n"
  },
  {
    "path": "FEAMR/FEElementDataCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEElementDataCriterion.h\"\n#include <FECore/FECoreKernel.h>\n\nBEGIN_FECORE_CLASS(FEElementDataCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_data, \"element_data\");\nEND_FECORE_CLASS();\n\nFEElementDataCriterion::FEElementDataCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_pd = nullptr;\n}\n\nbool FEElementDataCriterion::Init()\n{\n\tm_pd = fecore_new<FELogElemData>(m_data.c_str(), GetFEModel());\n\tif (m_pd == nullptr) return false;\n\treturn FEMeshAdaptorCriterion::Init();\n}\n\nbool FEElementDataCriterion::GetElementValue(FEElement& el, double& val)\n{\n\tif (m_pd)\n\t{\n\t\tval = m_pd->value(el);\n\t\treturn true;\n\t}\n\telse return false;\n}\n"
  },
  {
    "path": "FEAMR/FEElementDataCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/ElementDataRecord.h>\n\nclass FEElementDataCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEElementDataCriterion(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool GetElementValue(FEElement& el, double& val) override;\n\nprivate:\n\tstd::string m_data;\n\tFELogElemData* m_pd;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEElementQualityCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEElementQualityCriterion.h\"\n#include <FECore/FEElement.h>\n#include <FECore/FEMeshPartition.h>\n#include <FECore/FEMesh.h>\n\nBEGIN_FECORE_CLASS(FEMeanRatioQualityCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(minQuality, \"min_quality\")->setLongName(\"Minimum element quality\");\nEND_FECORE_CLASS();\n\nFEMeanRatioQualityCriterion::FEMeanRatioQualityCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tminQuality = 1.0;\n}\n\nbool FEMeanRatioQualityCriterion::GetElementValue(FEElement& el, double& value)\n{\n\tif ((el.Shape() != ET_TET4) && (el.Shape() != ET_HEX8) && (el.Shape() != ET_PENTA6)) return false;\n\tvalue = ElementQuality(el);\n\treturn (value < minQuality);\n}\n\n// Edge list for different element types\nstatic const int hexCornerEdges[8][3][2] =\n{\n\t{ {0,1}, { 0, 3}, {0,4} },\n\t{ {1,2}, { 1,0 }, {1,5} },\n\t{ {2,3}, { 2,1 }, {2,6} },\n\t{ {3,0}, { 3,2 }, {3,7} },\n\t{ {4,0}, { 4,7 }, {4,5} },\n\t{ {5,1}, { 5,4 }, {5,6} },\n\t{ {6,2}, { 6,5 }, {6,7} },\n\t{ {7,3}, { 7,6 }, {7,4} } \n};\n\nstatic const int wedgeCornerEdges[6][3][2] =\n{\n\t{ {0,1}, {0,2}, {0,3} },\n\t{ {1,2}, {1,0}, {1,4} },\n\t{ {2,0}, {2,1}, {2,5} },\n\t{ {3,4}, {3,0}, {3,5} },\n\t{ {4,5}, {4,1}, {4,3} },\n\t{ {5,3}, {5,2}, {5,4} }\n};\n\nstatic const int tetCornerEdges[4][3][2] =\n{\n\t{ {0,1}, {0,2}, {0,3} },\n\t{ {1,2}, {1,0}, {1,3} },\n\t{ {2,0}, {2,1}, {2,3} },\n\t{ {3,0}, {3,2}, {3,1} }\n};\n\ndouble FEMeanRatioQualityCriterion::ElementQuality(FEElement& el)\n{\n\tFEMeshPartition* partition = el.GetMeshPartition();\n\tif (partition == nullptr) return 0.0;\n\n\tFEMesh& mesh = *partition->GetMesh();\n\n\t// get the nodal coordinates\n\tvec3d r[FEElement::MAX_NODES];\n\tfor (int i = 0; i < el.Nodes(); ++i) r[i] = mesh.Node(el.m_node[i]).m_rt;\n\n\t// for each node, find 3 edges that meet at the node\n\tint n = el.Nodes();\n\tconst int(*edgeData)[3][2] = nullptr;\n\tswitch (el.Shape())\n\t{\n\tcase ET_HEX8  : edgeData = hexCornerEdges; break;\n\tcase ET_PENTA6: edgeData = wedgeCornerEdges; break;\n\tcase ET_TET4  : edgeData = tetCornerEdges; break;\n\tdefault: return 1.0;\n\t}\n\n\t// calculate mean ratio at each node and store minimum value\n\tdouble minMR = 1e99;\n\tvec3d e[3];\n\tdouble L[3];\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\t// get the 3 edges that meet at this node\n\t\tfor (int k = 0; k < 3; ++k)\n\t\t{\n\t\t\tint a = edgeData[i][k][0];\n\t\t\tint b = edgeData[i][k][1];\n\t\t\tvec3d va = r[a];\n\t\t\tvec3d vb = r[b];\n\t\t\te[k] = vb - va;\n\t\t\tL[k] = e[k].Length();\n\t\t}\n\n\t\t// get the jacobian determinant at this node\n\t\tdouble detJ = e[0] * (e[1] ^ e[2]);\n\t\tdouble JF2 = e[0].SqrLength() + e[1].SqrLength() + e[2].SqrLength();\n\n\t\t// calculate the mean ratio\n\t\tdouble MR = 3.0 * pow(detJ, 2.0 / 3.0) / JF2;\n\t\tif (MR < minMR) minMR = MR;\n\t}\n\n\treturn minMR;\n}\n\n//==================================================================\n\nBEGIN_FECORE_CLASS(FEScaledJacobianQualityCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(minQuality, \"min_quality\")->setLongName(\"Minimum element quality\");\nEND_FECORE_CLASS();\n\nFEScaledJacobianQualityCriterion::FEScaledJacobianQualityCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tminQuality = 1.0;\n}\n\nbool FEScaledJacobianQualityCriterion::GetElementValue(FEElement& el, double& value)\n{\n\tif ((el.Shape() != ET_TET4) && (el.Shape() != ET_HEX8) && (el.Shape() != ET_PENTA6)) return false;\n\tvalue = ElementQuality(el);\n\treturn (value < minQuality);\n}\n\ndouble FEScaledJacobianQualityCriterion::ElementQuality(FEElement& el)\n{\n\tFEMeshPartition* partition = el.GetMeshPartition();\n\tif (partition == nullptr) return 0.0;\n\n\tFEMesh& mesh = *partition->GetMesh();\n\n\t// get the nodal coordinates\n\tvec3d r[FEElement::MAX_NODES];\n\tfor (int i = 0; i < el.Nodes(); ++i) r[i] = mesh.Node(el.m_node[i]).m_rt;\n\n\t// for each node, find 3 edges that meet at the node\n\tint n = el.Nodes();\n\tconst int(*edgeData)[3][2] = nullptr;\n\tswitch (el.Shape())\n\t{\n\tcase ET_HEX8  : edgeData = hexCornerEdges; break;\n\tcase ET_PENTA6: edgeData = wedgeCornerEdges; break;\n\tcase ET_TET4  : edgeData = tetCornerEdges; break;\n\tdefault: return 0.0;\n\t}\n\n\t// calculate scaled jacobian at each node and store minimum value\n\tdouble minSJ = 1e99;\n\tvec3d e[3];\n\tdouble L[3];\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\t// get the 3 edges that meet at this node\n\t\tfor (int k = 0; k < 3; ++k)\n\t\t{\n\t\t\tint a = edgeData[i][k][0];\n\t\t\tint b = edgeData[i][k][1];\n\t\t\tvec3d va = r[a];\n\t\t\tvec3d vb = r[b];\n\t\t\te[k] = vb - va;\n\t\t\tL[k] = e[k].Length();\n\t\t}\n\n\t\t// get the jacobian determinant at this node\n\t\tdouble detJ = e[0] * (e[1] ^ e[2]);\n\n\t\t// calculate the scaled jacobian\n\t\tdouble SJ = detJ / (L[0] * L[1] * L[2]);\n\t\tif (SJ < minSJ) minSJ = SJ;\n\t}\n\n\treturn minSJ;\n}"
  },
  {
    "path": "FEAMR/FEElementQualityCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include \"feamr_api.h\"\n\n\nclass FEAMR_API FEMeanRatioQualityCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEMeanRatioQualityCriterion(FEModel* fem);\n\n\tbool GetElementValue(FEElement& el, double& value) override;\n\npublic:\n\tstatic double ElementQuality(FEElement& el);\n\nprivate:\n\tdouble minQuality = 0.0;\n\n\tDECLARE_FECORE_CLASS()\n};\n\nclass FEAMR_API FEScaledJacobianQualityCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEScaledJacobianQualityCriterion(FEModel* fem);\n\n\tbool GetElementValue(FEElement& el, double& value) override;\n\npublic:\n\tstatic double ElementQuality(FEElement& el);\n\nprivate:\n\tdouble minQuality = 0.0;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEAMR/FEElementSelectionCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEElementSelectionCriterion.h\"\n#include <FECore/FEMesh.h>\n\nBEGIN_FECORE_CLASS(FEElementSelectionCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_value, \"value\");\n\tADD_PARAMETER(m_elemList, \"element_list\");\nEND_FECORE_CLASS();\n\nFEElementSelectionCriterion::FEElementSelectionCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_value = 1.0;\n}\n\nFEMeshAdaptorSelection FEElementSelectionCriterion::GetElementSelection(FEElementSet* elemSet)\n{\n\tFEMesh& mesh = GetMesh();\n\tFEMeshAdaptorSelection elemList;\n\tFEElementIterator it(&mesh, elemSet);\n\tfor (; it.isValid(); ++it)\n\t{\n\t\tFEElement& el = *it;\n\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// see if this element is in the element_list\n\t\t\t// TODO: This is really slow. Need to speed this up!\n\t\t\tint eid = el.GetID();\n\t\t\tint n = -1;\n\t\t\tfor (int i = 0; i < m_elemList.size(); ++i)\n\t\t\t{\n\t\t\t\tif (m_elemList[i] == eid)\n\t\t\t\t{\n\t\t\t\t\tn = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// set the value\n\t\t\tif (n != -1)\n\t\t\t{\n\t\t\t\telemList.push_back(eid, m_value);\n\t\t\t}\n\t\t}\n\t}\n\treturn elemList;\n}\n"
  },
  {
    "path": "FEAMR/FEElementSelectionCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n\n//-----------------------------------------------------------------------------\nclass FEElementSelectionCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEElementSelectionCriterion(FEModel* fem);\n\tFEMeshAdaptorSelection GetElementSelection(FEElementSet* elset) override;\n\nprivate:\n\tdouble\tm_value;\n\tvector<int>\tm_elemList;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEErosionAdaptor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEErosionAdaptor.h\"\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEMesh.h>\n#include <FECore/FEDomain.h>\n#include <FECore/FESurface.h>\n#include <FECore/log.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FEElementList.h>\n#include <FECore/FEMeshTopo.h>\n#include <algorithm>\n#include <stack>\n#include <set>\n\nBEGIN_FECORE_CLASS(FEErosionAdaptor, FEMeshAdaptor)\n\tADD_PARAMETER(m_maxIters, \"max_iters\");\n\tADD_PARAMETER(m_maxelem, \"max_elems\");\n\tADD_PARAMETER(m_nsort, \"sort\");\n\tADD_PARAMETER(m_bremoveIslands, \"remove_islands\");\n\tADD_PARAMETER(m_erodeSurfaces, \"erode_surfaces\")->setEnums(\"no\\0yes\\0grow\\0reconstruct\\0\");\n\n\tADD_PROPERTY(m_criterion, \"criterion\");\nEND_FECORE_CLASS();\n\nFEErosionAdaptor::FEErosionAdaptor(FEModel* fem) : FEMeshAdaptor(fem)\n{\n\tm_maxIters = -1;\n\tm_maxelem = 0;\n\tm_nsort = 0;\n\tm_erodeSurfaces = SurfaceErodeOption::ERODE;\n\n\tm_criterion = nullptr;\n\tm_bremoveIslands = false;\n}\n\nbool FEErosionAdaptor::Apply(int iteration)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tif ((m_maxIters >= 0) && (iteration >= m_maxIters))\n\t{\n\t\tfeLog(\"\\tMax iterations reached.\");\n\t\treturn false;\n\t}\n\n\t// Make sure there is a criterion\n\tif (m_criterion == nullptr) return false;\n\n\t// get the element selection\n\tFEMeshAdaptorSelection selection = m_criterion->GetElementSelection(GetElementSet());\n\tif (selection.empty())\n\t{\n\t\tfeLog(\"\\tNothing to do.\\n\");\n\t\treturn false;\n\t}\n\n\t// see if we need to sort the data\n\t// (This is only necessary if the max_elem parameter is set)\n\tif ((m_maxelem > 0) && (m_nsort != 0))\n\t{\n\t\tif (m_nsort == 1)\n\t\t{\n\t\t\t// sort largest-to-smallest\n\t\t\tselection.Sort(FEMeshAdaptorSelection::SORT_DECREASING);\n\t\t}\n\t\telse if (m_nsort == 2)\n\t\t{\n\t\t\t// sort smallest-to-largest\n\t\t\tselection.Sort(FEMeshAdaptorSelection::SORT_INCREASING);\n\t\t}\n\t}\n\n\t// process the list\n\tint nsize = selection.size();\n\tif ((m_maxelem > 0) && (nsize > m_maxelem)) nsize = m_maxelem;\n\tint deactivatedElements = 0;\n\tfor (int i = 0; i < nsize; ++i)\n\t{\n\t\tFEMeshAdaptorSelection::Item& it = selection[i];\n\t\tFEElement& el = *mesh.FindElementFromID(it.m_elementId);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tel.setInactive();\n\t\t\tdeactivatedElements++;\n\t\t}\n\t}\n\tfeLog(\"\\tDeactivated elements: %d\\n\", deactivatedElements);\n\tif (deactivatedElements == 0) return false;\n\n\tFEMeshTopo topo;\n\tif (topo.Create(&mesh) == false)\n\t{\n\t\tfeLogError(\"Failed building mesh topo.\");\n\t\treturn false;\n\t}\n\n\t// remove any islands\n\tif (m_bremoveIslands) RemoveIslands(topo);\n\n\t// if any nodes were orphaned, we need to deactivate them as well\n\tDeactivateOrphanedNodes();\n\n\t// any facets attached to a eroded element will be eroded as well.\n\tswitch (m_erodeSurfaces)\n\t{\n\tcase SurfaceErodeOption::DONT_ERODE: break;// don't do anything\n\tcase SurfaceErodeOption::ERODE: ErodeSurfaces(); break;\n\tcase SurfaceErodeOption::GROW : GrowErodedSurfaces(topo); break;\n\tcase SurfaceErodeOption::RECONSTRUCT: ReconstructSurfaces(topo); break;\n\t}\n\n\t// remove any linear constraints of excluded nodes\n\tUpdateLinearConstraints();\n\n\t// update model\n\tUpdateModel();\n\n\treturn (nsize != 0);\n}\n\nvoid FEErosionAdaptor::RemoveIslands(FEMeshTopo& topo)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tint NE = topo.Elements();\n\tvector<int> tag(NE, -1);\n\n\t// find an unprocessed element\n\tstack<int> s;\n\tint m = 0;\t// island counter\n\tfor (int n = 0; n < NE; ++n)\n\t{\n\t\tFEElement* el = topo.Element(n);\n\t\tif (el->isActive() && (tag[n] == -1))\n\t\t{\n\t\t\t// see if this is an island\n\t\t\tvector<int> island;\n\n\t\t\ttag[n] = m++;\n\n\t\t\t// push it on the stack\n\t\t\ts.push(n);\n\t\t\twhile (s.empty() == false)\n\t\t\t{\n\t\t\t\t// pop the element\n\t\t\t\tint id = s.top(); s.pop();\n\t\t\t\tFEElement* el = topo.Element(id);\n\t\t\t\tisland.push_back(id);\n\n\t\t\t\t// loop over all the neighbors\n\t\t\t\tvector<int> nbrList = topo.ElementNeighborIndexList(id);\n\t\t\t\tfor (int i = 0; i < nbrList.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement* eli = topo.Element(nbrList[i]);\n\t\t\t\t\tif (eli && eli->isActive() && (tag[nbrList[i]] == -1))\n\t\t\t\t\t{\n\t\t\t\t\t\ttag[nbrList[i]] = m;\n\t\t\t\t\t\ts.push(nbrList[i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Next, see if the island should be deactivated. \n\t\t\t// It will be deactivated if all the nodes on the island are open\n\t\t\tbool isolated = true;\n\t\t\tfor (int i = 0; i < island.size(); ++i)\n\t\t\t{\n\t\t\t\tFEElement* el = topo.Element(island[i]);\n\n\t\t\t\tint neln = el->Nodes();\n\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tFENode& nj = mesh.Node(el->m_node[j]);\n\n\t\t\t\t\t// TODO: mechanics only!\n\t\t\t\t\tif ((nj.get_bc(0) != DOF_OPEN) ||\n\t\t\t\t\t\t(nj.get_bc(1) != DOF_OPEN) ||\n\t\t\t\t\t\t(nj.get_bc(2) != DOF_OPEN))\n\t\t\t\t\t{\n\t\t\t\t\t\tisolated = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isolated == false) break;\n\t\t\t}\n\n\t\t\tif (isolated)\n\t\t\t{\n\t\t\t\t// island is isolated so deactivate all elements\n\t\t\t\tfeLog(\"\\tIsland of %d elements removed\\n\", island.size());\n\t\t\t\tfor (int i = 0; i < island.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement* el = topo.Element(island[i]);\n\t\t\t\t\tel->setInactive();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEErosionAdaptor::DeactivateOrphanedNodes()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tint NN = mesh.Nodes();\n\tvector<int> tag(NN, 0);\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint neln = el.Nodes();\n\t\t\t\tfor (int n = 0; n < neln; ++n) tag[el.m_node[n]] = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (tag[i] == 0)\n\t\t{\n\t\t\tnode.SetFlags(FENode::EXCLUDE);\n\t\t\tint ndofs = node.dofs();\n\t\t\tfor (int j = 0; j < ndofs; ++j)\n\t\t\t\tnode.set_inactive(j);\n\t\t}\n\t}\n}\n\nvoid FEErosionAdaptor::ErodeSurfaces()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tint erodedFaces = 0;\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tfor (int j = 0; j < surf.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& face = surf.Element(j);\n\t\t\tFEElement* pe = face.m_elem[0].pe; assert(pe);\n\t\t\tif (pe && (pe->isActive() == false))\n\t\t\t{\n\t\t\t\tface.setInactive();\n\t\t\t\terodedFaces++;\n\t\t\t}\n\t\t}\n\t\tif (erodedFaces != 0) surf.Init();\n\t}\n}\n\nvoid FEErosionAdaptor::GrowErodedSurfaces(FEMeshTopo& topo)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\n\t\t// do a search to see if we need to modify this surface\n\t\tbool doErode = false;\n\t\tfor (int j = 0; j < surf.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& face = surf.Element(j);\n\t\t\tif (face.isActive())\n\t\t\t{\n\t\t\t\tFEElement* pe = face.m_elem[0].pe; assert(pe);\n\t\t\t\tif (pe && (pe->isActive() == false))\n\t\t\t\t{\n\t\t\t\t\tdoErode = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (doErode)\n\t\t{\n\t\t\tstd::vector<FEFaceList::FACE> newFaces;\n\t\t\tstd::set<FEElement*> processedElements;\n\t\t\tnewFaces.reserve(surf.Elements());\n\t\t\tint erodedFaces = 0;\n\t\t\tfor (int j = 0; j < surf.Elements(); ++j)\n\t\t\t{\n\t\t\t\tFESurfaceElement& face = surf.Element(j);\n\t\t\t\tif (face.isActive())\n\t\t\t\t{\n\t\t\t\t\tFEElement* pe = face.m_elem[0].pe; assert(pe);\n\t\t\t\t\tif (pe && (pe->isActive() == false))\n\t\t\t\t\t{\n\t\t\t\t\t\t// make sure we didn't process this element yet\n\t\t\t\t\t\tif (processedElements.find(pe) == processedElements.end())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint id = topo.GetElementIndexFromID(pe->GetID());\n\t\t\t\t\t\t\tstd::vector<FEElement*> nbrList = topo.ElementNeighborList(id);\n\t\t\t\t\t\t\tfor (int k = 0; k < nbrList.size(); ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFEElement* pk = nbrList[k];\n\t\t\t\t\t\t\t\tif ((pk != nullptr) && pk->isActive())\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint node[FEElement::MAX_NODES] = { 0 };\n\t\t\t\t\t\t\t\t\tint nf = pe->GetFace(k, node);\n\n\t\t\t\t\t\t\t\t\t// Note that we invert the element to\n\t\t\t\t\t\t\t\t\t// make sure the new face is outward pointing\n\t\t\t\t\t\t\t\t\tFEFaceList::FACE newFace;\n\t\t\t\t\t\t\t\t\tnewFace.ntype = nf;\n\t\t\t\t\t\t\t\t\tfor (int l = 0; l < nf; ++l)\n\t\t\t\t\t\t\t\t\t\tnewFace.node[l] = node[nf - 1 - l];\n\n\t\t\t\t\t\t\t\t\tif (!newFace.IsEqual(face.m_node.data()))\n\t\t\t\t\t\t\t\t\t\tnewFaces.push_back(newFace);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tprocessedElements.insert(pe);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tface.setInactive();\n\t\t\t\t\t\terodedFaces++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tFEFaceList::FACE newFace;\n\t\t\t\t\t\tnewFace.ntype = face.Nodes();\n\t\t\t\t\t\tfor (int k = 0; k < face.Nodes(); ++k)\n\t\t\t\t\t\t\tnewFace.node[k] = face.m_node[k];\n\t\t\t\t\t\tnewFaces.push_back(newFace);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfeLog(\"eroded faces: %d\", erodedFaces);\n\t\t\tif (erodedFaces != 0)\n\t\t\t{\n\t\t\t\tint faces = newFaces.size();\n\t\t\t\tsurf.Create(faces);\n\t\t\t\tfor (int j = 0; j < faces; ++j)\n\t\t\t\t{\n\t\t\t\t\tFEFaceList::FACE& f = newFaces[j];\n\t\t\t\t\tFESurfaceElement& face = surf.Element(j);\n\t\t\t\t\tface.setActive();\n\n\t\t\t\t\tif (f.ntype == 3)\n\t\t\t\t\t\tface.SetType(FE_TRI3G3);\n\t\t\t\t\telse if (f.ntype == 4)\n\t\t\t\t\t\tface.SetType(FE_QUAD4G4);\n\n\t\t\t\t\tfor (int k = 0; k < f.ntype; ++k)\n\t\t\t\t\t\tface.m_node[k] = f.node[k];\n\t\t\t\t}\n\n\t\t\t\tsurf.CreateMaterialPointData();\n\t\t\t\tsurf.Init();\n\n\t\t\t\t// also update the facet set if the surface has one\n\t\t\t\tFEFacetSet* fset = surf.GetFacetSet();\n\t\t\t\tif (fset)\n\t\t\t\t{\n\t\t\t\t\tfset->Create(surf);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEErosionAdaptor::UpdateLinearConstraints()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tfor (int j = 0; j < LCM.LinearConstraints();)\n\t{\n\t\tFELinearConstraint& lc = LCM.LinearConstraint(j);\n\t\tif (mesh.Node(lc.GetParentNode()).HasFlags(FENode::EXCLUDE))\n\t\t{\n\t\t\tLCM.RemoveLinearConstraint(j);\n\t\t}\n\t\telse ++j;\n\t}\n\n\t// also remove any linear constraints that have excluded child nodes\n\tfor (int j = 0; j < LCM.LinearConstraints();)\n\t{\n\t\tFELinearConstraint& lc = LCM.LinearConstraint(j);\n\n\t\tbool del = false;\n\t\tint n = lc.Size();\n\t\tfor (int k = 0; k < n; ++k)\n\t\t{\n\t\t\tif (mesh.Node(lc.GetChildDof(k).node).HasFlags(FENode::EXCLUDE))\n\t\t\t{\n\t\t\t\tdel = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (del) LCM.RemoveLinearConstraint(j); else ++j;\n\t}\n}\n\nvoid FEErosionAdaptor::ReconstructSurfaces(FEMeshTopo& topo)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// We assume that the surfaces are definded through part lists. \n\t// We don't actually store which surfaces are generated from part lists,\n\t// but they should have the same name. \n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\n\t\t// see if a part list exists with this name\n\t\tFEDomainList* domList = mesh.FindDomainList(surf.GetName());\n\t\tif (domList)\n\t\t{\n\t\t\t// build the outer surface\n\t\t\tFEFacetSet* facetSet = mesh.DomainBoundary(*domList);\n\n\t\t\tFEFacetSet* oldFacetSet = surf.GetFacetSet();\n\t\t\tif (oldFacetSet == nullptr)\n\t\t\t{\n\t\t\t\tsurf.Create(*facetSet);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toldFacetSet->Clear();\n\t\t\t\toldFacetSet->Add(facetSet);\n\t\t\t\tsurf.Create(*oldFacetSet);\n\t\t\t}\n\n\t\t\tsurf.CreateMaterialPointData();\n\t\t\tsurf.Init();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEAMR/FEErosionAdaptor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptor.h>\n#include <FECore/FEMeshAdaptorCriterion.h>\n\nclass FEMeshTopo;\n\nclass FEErosionAdaptor : public FEMeshAdaptor\n{\n\tenum SurfaceErodeOption {\n\t\tDONT_ERODE,\t\t// don't erode surface elements (default)\n\t\tERODE,\t\t\t// erode surface elements\n\t\tGROW,\t\t\t// add newly exposed surface elements\n\t\tRECONSTRUCT\t\t// reconstruct surfaces\n\t};\n\npublic:\n\tFEErosionAdaptor(FEModel* fem);\n\n\tbool Apply(int iteration) override;\n\nprivate:\n\tvoid RemoveIslands(FEMeshTopo& topo);\n\tvoid DeactivateOrphanedNodes();\n\tvoid ErodeSurfaces();\n\tvoid GrowErodedSurfaces(FEMeshTopo& topo);\n\tvoid ReconstructSurfaces(FEMeshTopo& topo);\n\tvoid UpdateLinearConstraints();\n\nprivate:\n\tint\t\tm_maxIters;\t\t\t// max iterations per time step\n\tbool\tm_bremoveIslands;\t// remove disconnected elements\n\tint\t\tm_maxelem;\t\t\t// the max nr of elements to erode per adaptation iteration\n\tint\t\tm_nsort;\t\t\t// sort option (0 = none, 1 = smallest to largest, 2 = largest to smallest)\n\tint\t\tm_erodeSurfaces;\t// option to erode surfaces\n\n\tFEMeshAdaptorCriterion* m_criterion;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEAMR/FEFilterAdaptorCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFilterAdaptorCriterion.h\"\n\nBEGIN_FECORE_CLASS(FEMinMaxFilterAdaptorCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_min, \"min\");\n\tADD_PARAMETER(m_max, \"max\");\n\tADD_PARAMETER(m_clamp, \"clamp\");\n\n\tADD_PROPERTY(m_data, \"data\");\nEND_FECORE_CLASS();\n\nFEMinMaxFilterAdaptorCriterion::FEMinMaxFilterAdaptorCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_min = -1.0e37;\n\tm_max =  1.0e37;\n\tm_clamp = true;\n\tm_data = nullptr;\n}\n\nbool FEMinMaxFilterAdaptorCriterion::GetElementValue(FEElement& el, double& value)\n{\n\tif (m_data == nullptr) return false;\n\tbool b = m_data->GetElementValue(el, value);\n\tif (b)\n\t{\n\t\tif (m_clamp)\n\t\t{\n\t\t\tif (value < m_min) value = m_min;\n\t\t\tif (value > m_max) value = m_max;\n\t\t}\n\t\telse if ((value < m_min) || (value > m_max))\n\t\t{\n\t\t\tb = false;\n\t\t}\n\t}\n\treturn b;\n}\n\nbool FEMinMaxFilterAdaptorCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tif (m_data == nullptr) return false;\n\tbool b = m_data->GetMaterialPointValue(mp, value);\n\tif (b)\n\t{\n\t\tif (m_clamp)\n\t\t{\n\t\t\tif (value < m_min) value = m_min;\n\t\t\tif (value > m_max) value = m_max;\n\t\t}\n\t\telse if ((value < m_min) || (value > m_max))\n\t\t{\n\t\t\tb = false;\n\t\t}\n\t}\n\treturn b;\n}\n"
  },
  {
    "path": "FEAMR/FEFilterAdaptorCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/FEMaterialPoint.h>\n#include \"feamr_api.h\"\n\nclass FEAMR_API FEMinMaxFilterAdaptorCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEMinMaxFilterAdaptorCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& el, double& value) override;\n\n\tbool GetElementValue(FEElement& el, double& value) override;\n\nprivate:\n\tdouble\tm_min;\n\tdouble\tm_max;\n\tbool\tm_clamp;\n\tFEMeshAdaptorCriterion*\tm_data;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEHexRefine.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEHexRefine.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEMeshTopo.h>\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEElementList.h>\n#include <FECore/FELinearConstraint.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FESurface.h>\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\nBEGIN_FECORE_CLASS(FEHexRefine, FERefineMesh)\n\tADD_PARAMETER(m_elemRefine, \"max_elem_refine\");\n\tADD_PARAMETER(m_maxValue, \"max_value\");\n\tADD_PROPERTY(m_criterion, \"criterion\");\nEND_FECORE_CLASS();\n\nFEHexRefine::FEHexRefine(FEModel* fem) : FERefineMesh(fem)\n{\n\tm_elemRefine = 0;\n\tm_maxValue = 0.0;\n\tm_criterion = nullptr;\n}\n\nbool FEHexRefine::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tif (mesh.IsType(ET_HEX8) == false)\n\t{\n\t\tfeLogError(\"Cannot apply hex refinement: Mesh is not a HEX8 mesh.\");\n\t\treturn false;\n\t}\n\n\treturn FERefineMesh::Init();\n}\n\nbool FEHexRefine::RefineMesh()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEMeshTopo& topo = *m_topo;\n\n\tFEElementList allElems(mesh);\n\n\tconst int NEL = mesh.Elements();\n\tconst int NDOM = mesh.Domains();\n\tm_N0 = mesh.Nodes();\n\tm_NC = topo.Edges();\n\tint NF = topo.Faces();\n\n\t// Build the lists of items to split\n\tBuildSplitLists(fem);\n\n\t// make sure we have work to do\n\tif (m_splitElems == 0) return false;\n\n\t// Next, the position and solution variables for all the nodes are updated.\n\t// Note that this has to be done before recreating the elements since \n\t// the old elements are still needed to determine the new positions and solutions. \n\tUpdateNewNodes(fem);\n\n\t// find the hanging nodes\n\t// and assign linear constraints to tie them down\n\tFindHangingNodes(fem);\n\n\t// Now, we can create new elements\n\tBuildNewDomains(fem);\n\n\t// recreate element sets\n\tfor (int i = 0; i < mesh.ElementSets(); ++i)\n\t{\n\t\tFEElementSet& eset = mesh.ElementSet(i);\n\n\t\t// get the domain list\n\t\t// NOTE: Don't get the reference, since then the same reference\n\t\t// is passed to Create below, which causes problems.\n\t\tFEDomainList domList = eset.GetDomainList();\n\t\tif (domList.IsEmpty()) { throw std::runtime_error(\"Error in FEHexRefine!\"); }\n\n\t\t// recreate the element set from the domain list\n\t\teset.Create(domList);\n\t}\n\n\t// update node sets\n\tfor (int i = 0; i < mesh.NodeSets(); ++i)\n\t{\n\t\tFENodeSet& nset = *mesh.NodeSet(i);\n\t\tUpdateNodeSet(nset);\n\t}\n\n\t// update all surfaces\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tif (UpdateSurface(surf) == false)\n\t\t{\n\t\t\tthrow std::runtime_error(\"Error in FEHexRefine!\");\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid FEHexRefine::BuildSplitLists(FEModel& fem)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// Get the elements that we need to refine\n\tint NEL = mesh.Elements();\n\tm_elemList.assign(NEL, -1);\n\tif (m_criterion)\n\t{\n\t\tFEMeshAdaptorSelection selection = m_criterion->GetElementSelection(GetElementSet());\n\t\tfor (int i = 0; i < selection.size(); ++i)\n\t\t{\n\t\t\tif (selection[i].m_elemValue > m_maxValue)\n\t\t\t{\n\t\t\t\tint eid = selection[i].m_elementId;\n\t\t\t\tint lid = topo.GetElementIndexFromID(eid);\n\t\t\t\tm_elemList[lid] = 1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// just do'em all\n\t\tm_elemList.assign(NEL, 1);\n\t}\n\n\t// We cannot split elements that have a hanging nodes\n\t// so remove those elements from the list\n\tint nrejected = 0;\n\tfor (int i = 0; i < NEL; ++i)\n\t{\n\t\tif (m_elemList[i] != -1)\n\t\t{\n\t\t\tFEElement& el = *topo.Element(i);\n\t\t\tint nel = el.Nodes();\n\t\t\tfor (int j = 0; j < nel; ++j)\n\t\t\t{\n\t\t\t\tif (mesh.Node(el.m_node[j]).HasFlags(FENode::HANGING))\n\t\t\t\t{\n\t\t\t\t\t// sorry, can't split this element\n\t\t\t\t\tm_elemList[i] = -1;\n\t\t\t\t\tnrejected++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (nrejected > 0)\n\t{\n\t\tfeLog(\"\\tElements rejected: %d\\n\", nrejected);\n\t}\n\n\t// count how many elements to split\n\tint N1 = mesh.Nodes();\n\tm_splitElems = 0;\n\tfor (int i = 0; i < m_elemList.size(); ++i) {\n\t\tif (m_elemList[i] == 1) {\n\t\t\tm_elemList[i] = N1++;\n\t\t\tm_splitElems++;\n\t\t}\n\t}\n\tif (m_splitElems == 0) return;\n\n\t// make sure we don't exceed the max elements per refinement step\n\tif ((m_elemRefine > 0) && (m_splitElems > m_elemRefine))\n\t{\n\t\tN1 = mesh.Nodes();\n\t\tm_splitElems = 0;\n\t\tfor (int i = 0; i < m_elemList.size(); ++i) {\n\t\t\tif (m_elemList[i] >= 0) {\n\t\t\t\tif (m_splitElems >= m_elemRefine)\n\t\t\t\t{\n\t\t\t\t\tm_elemList[i] = -1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tm_splitElems++;\n\t\t\t\t\tm_elemList[i] = N1++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tassert(m_splitElems == m_elemRefine);\n\t}\n\n\t// figure out which faces to refine\n\tint NF = topo.Faces();\n\tm_faceList.assign(NF, -1);\n\tfor (int i = 0; i < m_elemList.size(); ++i)\n\t{\n\t\tif (m_elemList[i] != -1)\n\t\t{\n\t\t\tconst std::vector<int>& elface = topo.ElementFaceList(i);\n\t\t\tfor (int j = 0; j < elface.size(); ++j) m_faceList[elface[j]] = 1;\n\t\t}\n\t}\n\n\t// count how many faces to split\n\tm_splitFaces = 0;\n\tfor (int i = 0; i < m_faceList.size(); ++i) {\n\t\tif (m_faceList[i] == 1) {\n\t\t\tm_faceList[i] = N1++;\n\t\t\tm_splitFaces++;\n\t\t}\n\t}\n\n\t// figure out which edges to refine\n\tm_edgeList.assign(m_NC, -1);\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tif (m_faceList[i] != -1)\n\t\t{\n\t\t\tconst std::vector<int>& faceEdge = topo.FaceEdgeList(i);\n\t\t\tfor (int j = 0; j < faceEdge.size(); ++j) m_edgeList[faceEdge[j]] = 1;\n\t\t}\n\t}\n\n\t// count how many edges to split\n\tm_splitEdges = 0;\n\tfor (int i = 0; i < m_NC; ++i) {\n\t\tif (m_edgeList[i] == 1) {\n\t\t\tm_edgeList[i] = N1++;\n\t\t\tm_splitEdges++;\n\t\t}\n\t}\n\n\tfeLog(\"\\tRefinement info:\\n\");\n\tfeLog(\"\\t  Elements to refine: %d\\n\", m_splitElems);\n\tfeLog(\"\\t  Facets to refine  : %d\\n\", m_splitFaces);\n\tfeLog(\"\\t  Edges to refine   : %d\\n\", m_splitEdges);\n}\n\nint findNodeInMesh(FEMesh& mesh, const vec3d& r, double tol = 1e-12)\n{\n\tint NN = mesh.Nodes();\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tvec3d ri = mesh.Node(i).m_r0;\n\t\t\tif ((ri - r).norm2() < tol) return i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nvoid FEHexRefine::UpdateNewNodes(FEModel& fem)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// we need to create a new node for each edge, face, and element that needs to be split\n\tint newNodes = m_splitEdges + m_splitFaces + m_splitElems;\n\n\t// for now, store the position of these new nodes in an array\n\tvector<vec3d> newPos(newNodes);\n\n\t// get the position of new nodes\n\tint n = 0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] != -1)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\t\tvec3d r0 = mesh.Node(edge.node[0]).m_r0;\n\t\t\tvec3d r1 = mesh.Node(edge.node[1]).m_r0;\n\t\t\tnewPos[n++] = (r0 + r1)*0.5;\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] != -1)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\t\t\tvec3d r0(0, 0, 0);\n\t\t\tint nn = face.ntype;\n\t\t\tfor (int j = 0; j < nn; ++j) r0 += mesh.Node(face.node[j]).m_r0;\n\t\t\tr0 /= (double)nn;\n\t\t\tnewPos[n++] = r0;\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Elements(); ++i)\n\t{\n\t\tif (m_elemList[i] != -1)\n\t\t{\n\t\t\tFESolidElement& el = dynamic_cast<FESolidElement&>(*topo.Element(i));\n\n\t\t\tvec3d r0(0, 0, 0);\n\t\t\tint nn = el.Nodes();\n\t\t\tfor (int j = 0; j < nn; ++j) r0 += mesh.Node(el.m_node[j]).m_r0;\n\t\t\tr0 /= (double)nn;\n\t\t\tnewPos[n++] = r0;\n\t\t}\n\t}\n\n\t// some of these new nodes may coincide with an existing node\n\t//If we find one, we eliminate it\n\tn = 0;\n\tint nremoved = 0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] != -1)\n\t\t{\n\t\t\tint nodeId = findNodeInMesh(mesh, newPos[n++]);\n\t\t\tif (nodeId >= 0)\n\t\t\t{\n\t\t\t\tif(mesh.Node(nodeId).HasFlags(FENode::HANGING))\n\t\t\t\t\tmesh.Node(nodeId).UnsetFlags(FENode::HANGING);\n\t\t\t\tm_edgeList[i] = -nodeId-2;\n\t\t\t\tnremoved++;\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] != -1)\n\t\t{\n\t\t\tint nodeId = findNodeInMesh(mesh, newPos[n++]);\n\t\t\tif (nodeId >= 0)\n\t\t\t{\n\t\t\t\tif(mesh.Node(nodeId).HasFlags(FENode::HANGING))\n\t\t\t\t\tmesh.Node(nodeId).UnsetFlags(FENode::HANGING);\n\t\t\t\tm_faceList[i] = -nodeId-2;\n\t\t\t\tnremoved++;\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Elements(); ++i)\n\t{\n\t\tif (m_elemList[i] != -1)\n\t\t{\n\t\t\tint nodeId = findNodeInMesh(mesh, newPos[n++]);\n\t\t\tif (nodeId >= 0)\n\t\t\t{\n\t\t\t\tif(mesh.Node(nodeId).HasFlags(FENode::HANGING))\n\t\t\t\t\tmesh.Node(nodeId).UnsetFlags(FENode::HANGING);\n\t\t\t\tm_elemList[i] = -nodeId-2;\n\t\t\t\tnremoved++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// we need to reindex nodes if some were removed\n\tif (nremoved > 0)\n\t{\n\t\tn = m_N0;\n\t\tfor (int i = 0; i < topo.Edges(); ++i)\n\t\t{\n\t\t\tif (m_edgeList[i] >= 0) m_edgeList[i] = n++;\n\t\t}\n\t\tfor (int i = 0; i < topo.Faces(); ++i)\n\t\t{\n\t\t\tif (m_faceList[i] >= 0) m_faceList[i] = n++;\n\t\t}\n\t\tfor (int i = 0; i < topo.Elements(); ++i)\n\t\t{\n\t\t\tif (m_elemList[i] >= 0) m_elemList[i] = n++;\n\t\t}\n\t\tassert(n == (m_N0 + newNodes - nremoved));\n\t\tnewNodes -= nremoved;\n\t}\n\n\t// now, generate new nodes\n\tmesh.AddNodes(newNodes);\n\n\t// assign dofs to new nodes\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tm_NN = mesh.Nodes();\n\tfor (int i = m_N0; i<m_NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.SetDOFS(MAX_DOFS);\n\t}\n\n\t// update the position of these new nodes\n\tn = 0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] >= 0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\t\tFENode& node = mesh.Node(m_edgeList[i]);\n\t\t\tvec3d r0 = mesh.Node(edge.node[0]).m_r0;\n\t\t\tvec3d r1 = mesh.Node(edge.node[1]).m_r0;\n\t\t\tnode.m_r0 = (r0 + r1)*0.5;\n\n\t\t\tr0 = mesh.Node(edge.node[0]).m_rt;\n\t\t\tr1 = mesh.Node(edge.node[1]).m_rt;\n\t\t\tnode.m_rt = (r0 + r1)*0.5;\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\t\t\tFENode& node = mesh.Node(m_faceList[i]);\n\t\t\tint nn = face.ntype;\n\n\t\t\tvec3d r0(0, 0, 0);\n\t\t\tfor (int j = 0; j < nn; ++j) r0 += mesh.Node(face.node[j]).m_r0;\n\t\t\tr0 /= (double)nn;\n\t\t\tnode.m_r0 = r0;\n\n\t\t\tvec3d rt(0, 0, 0);\n\t\t\tfor (int j = 0; j < nn; ++j) rt += mesh.Node(face.node[j]).m_rt;\n\t\t\trt /= (double)nn;\n\t\t\tnode.m_rt = rt;\n\t\t}\n\t}\n\tfor (int i=0; i < topo.Elements(); ++i)\n\t{\n\t\tif (m_elemList[i] >= 0)\n\t\t{\n\t\t\tFESolidElement& el = dynamic_cast<FESolidElement&>(*topo.Element(i));\n\n\t\t\tint nn = el.Nodes();\n\t\t\tFENode& node = mesh.Node(m_elemList[i]);\n\n\t\t\tvec3d r0(0, 0, 0);\n\t\t\tfor (int j = 0; j < nn; ++j) r0 += mesh.Node(el.m_node[j]).m_r0;\n\t\t\tr0 /= (double)nn;\n\t\t\tnode.m_r0 = r0;\n\n\t\t\tvec3d rt(0, 0, 0);\n\t\t\tfor (int j = 0; j < nn; ++j) rt += mesh.Node(el.m_node[j]).m_rt;\n\t\t\trt /= (double)nn;\n\t\t\tnode.m_rt = rt;\n\t\t}\n\t}\n\n\t// re-evaluate solution at nodes\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] >= 0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\t\tFENode& node0 = mesh.Node(edge.node[0]);\n\t\t\tFENode& node1 = mesh.Node(edge.node[1]);\n\n\t\t\tFENode& node = mesh.Node(m_edgeList[i]);\n\t\t\tfor (int j = 0; j < MAX_DOFS; ++j)\n\t\t\t{\n\t\t\t\tdouble v = (node0.get(j) + node1.get(j))*0.5;\n\t\t\t\tnode.set(j, v);\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\t\t\tFENode& node = mesh.Node(m_faceList[i]);\n\t\t\tint nn = face.ntype;\n\t\t\tfor (int j = 0; j < MAX_DOFS; ++j)\n\t\t\t{\n\t\t\t\tdouble v = 0.0;\n\t\t\t\tfor (int k = 0; k < nn; ++k) v += mesh.Node(face.node[k]).get(j);\n\t\t\t\tv /= (double)nn;\n\t\t\t\tnode.set(j, v);\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i=0; i<topo.Elements(); ++i)\n\t{\n\t\tif (m_elemList[i] >= 0)\n\t\t{\n\t\t\tFESolidElement& el = dynamic_cast<FESolidElement&>(*topo.Element(i));\n\t\t\tint nn = el.Nodes();\n\t\t\tFENode& node = mesh.Node(m_elemList[i]);\n\t\t\tfor (int j = 0; j < MAX_DOFS; ++j)\n\t\t\t{\n\t\t\t\tdouble v = 0.0;\n\t\t\t\tfor (int k = 0; k < nn; ++k) v += mesh.Node(el.m_node[k]).get(j);\n\t\t\t\tv /= (double)nn;\n\t\t\t\tnode.set(j, v);\n\t\t\t}\n\t\t}\n\t}\n\n\t// make the new node indices all positive\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] < -1) m_edgeList[i] = -m_edgeList[i]-2;\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] < -1) m_faceList[i] = -m_faceList[i]-2;\n\t}\n\tfor (int i = 0; i < topo.Elements(); ++i)\n\t{\n\t\tif (m_elemList[i] < -1) m_elemList[i] = -m_elemList[i] - 2;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function identifies the hanging nodes and assigns linear constraints to them.\nvoid FEHexRefine::FindHangingNodes(FEModel& fem)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\tm_hangingNodes = 0;\n\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\n\tconst int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\n\t// First, we removed any constraints on nodes that are no longer hanging\n\tint nremoved = 0;\n\tfor (int i = 0; i < LCM.LinearConstraints();)\n\t{\n\t\tFELinearConstraint& lc = LCM.LinearConstraint(i);\n\t\tint nodeID = lc.GetParentNode();\n\t\tif (mesh.Node(nodeID).HasFlags(FENode::HANGING) == false)\n\t\t{\n\t\t\tLCM.RemoveLinearConstraint(i);\n\t\t\tnremoved++;\n\t\t}\n\t\telse i++;\n\t}\n\tfeLog(\"\\tRemoved linear constraints : %d\\n\", nremoved);\n\n\t// we loop over non-split faces\n\tint nadded = 0;\n\tvector<int> tag(m_NC, 0);\n\tint NF = topo.Faces();\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tif (m_faceList[i] == -1)\n\t\t{\n\t\t\t// This face is not split\n\t\t\tconst std::vector<int>& fel = topo.FaceEdgeList(i);\n\t\t\tfor (int j = 0; j < fel.size(); ++j)\n\t\t\t{\n\t\t\t\tif ((m_edgeList[fel[j]] >= 0) && (tag[fel[j]] == 0))\n\t\t\t\t{\n\t\t\t\t\t// Tag the node as hanging so we can identify it easier later\n\t\t\t\t\tint nodeId = m_edgeList[fel[j]];\n\t\t\t\t\tFENode& node = mesh.Node(nodeId);\n\t\t\t\t\tnode.SetFlags(FENode::HANGING);\n\n\t\t\t\t\t// get the edge\n\t\t\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(fel[j]);\n\n\t\t\t\t\t// setup a linear constraint for this node\n\t\t\t\t\tfor (int k = 0; k < MAX_DOFS; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFELinearConstraint* lc = new FELinearConstraint(&fem);\n\t\t\t\t\t\tlc->SetParentDof(k, nodeId);\n\t\t\t\t\t\tlc->AddChildDof(k, edge.node[0], 0.5);\n\t\t\t\t\t\tlc->AddChildDof(k, edge.node[1], 0.5);\n\n\t\t\t\t\t\tLCM.AddLinearConstraint(lc);\n\t\t\t\t\t\tnadded++;\n\t\t\t\t\t}\n\n\t\t\t\t\t// set a tag to avoid double-counting\n\t\t\t\t\ttag[fel[j]] = 1;\n\n//\t\t\t\t\tfeLog(\"Added linear constraints to hanging node: %d (%d, %d)\\n\", nodeId, edge.node[0], edge.node[1]);\n\n\t\t\t\t\tm_hangingNodes++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// we loop over the non-split elements\n\tint NEL = topo.Elements();\n\tfor (int i = 0; i<NEL; ++i)\n\t{\n\t\tif (m_elemList[i] == -1)\n\t\t{\n\t\t\t// This element is not split\n\t\t\t// If any of its faces are split, then the corresponding node\n\t\t\t// will be hanging\n\t\t\tconst std::vector<int>& elface = topo.ElementFaceList(i);\n\t\t\tfor (int j = 0; j < elface.size(); ++j)\n\t\t\t{\n\t\t\t\tif (m_faceList[elface[j]] >= 0)\n\t\t\t\t{\n\t\t\t\t\t// Tag the node as hanging so we can identify it easier later\n\t\t\t\t\tint nodeId = m_faceList[elface[j]];\n\t\t\t\t\tFENode& node = mesh.Node(nodeId);\n\t\t\t\t\tnode.SetFlags(FENode::HANGING);\n\n\t\t\t\t\t// get the face\n\t\t\t\t\tconst FEFaceList::FACE& face = topo.Face(elface[j]);\n\n\t\t\t\t\t// setup a linear constraint for this node\n\t\t\t\t\tfor (int k = 0; k < MAX_DOFS; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFELinearConstraint* lc = new FELinearConstraint(&fem);\n\t\t\t\t\t\tlc->SetParentDof(k, nodeId);\n\t\t\t\t\t\tlc->AddChildDof(k, face.node[0], 0.25);\n\t\t\t\t\t\tlc->AddChildDof(k, face.node[1], 0.25);\n\t\t\t\t\t\tlc->AddChildDof(k, face.node[2], 0.25);\n\t\t\t\t\t\tlc->AddChildDof(k, face.node[3], 0.25);\n\n\t\t\t\t\t\tLCM.AddLinearConstraint(lc);\n\t\t\t\t\t\tnadded++;\n\t\t\t\t\t}\n\n//\t\t\t\t\tfeLog(\"Added linear constraints to hanging node: %d (%d, %d, %d, %d)\\n\", nodeId, face.node[0], face.node[1], face.node[2], face.node[3]);\n\n\t\t\t\t\tm_hangingNodes++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This element is not split\n\t\t\t// If any of its edges are split, then the corresponding node\n\t\t\t// will be hanging\n\t\t\tconst std::vector<int>& eledge = topo.ElementEdgeList(i);\n\t\t\tfor (int j = 0; j < eledge.size(); ++j)\n\t\t\t{\n\t\t\t\tif ((m_edgeList[eledge[j]] >= 0) && (tag[eledge[j]] == 0))\n\t\t\t\t{\n\t\t\t\t\t// Tag the node as hanging so we can identify it easier later\n\t\t\t\t\tint nodeId = m_edgeList[eledge[j]];\n\t\t\t\t\tFENode& node = mesh.Node(nodeId);\n\t\t\t\t\tnode.SetFlags(FENode::HANGING);\n\n\t\t\t\t\t// get the edge\n\t\t\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(eledge[j]);\n\n\t\t\t\t\t// setup a linear constraint for this node\n\t\t\t\t\tfor (int k = 0; k < MAX_DOFS; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFELinearConstraint* lc = new FELinearConstraint(&fem);\n\t\t\t\t\t\tlc->SetParentDof(k, nodeId);\n\t\t\t\t\t\tlc->AddChildDof(k, edge.node[0], 0.5);\n\t\t\t\t\t\tlc->AddChildDof(k, edge.node[1], 0.5);\n\n\t\t\t\t\t\tLCM.AddLinearConstraint(lc);\n\t\t\t\t\t\tnadded++;\n\t\t\t\t\t}\n\n\t\t\t\t\t// set a tag to avoid double-counting\n\t\t\t\t\ttag[eledge[j]] = 1;\n\n//\t\t\t\t\tfeLog(\"Added linear constraints to hanging node: %d (%d, %d)\\n\", nodeId, edge.node[0], edge.node[1]);\n\n\t\t\t\t\tm_hangingNodes++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\tfeLog(\"\\tHanging nodes ............ : %d\\n\", m_hangingNodes);\n\tfeLog(\"\\tAdded linear constraints . : %d\\n\", nadded);\n}\n\nvoid FEHexRefine::BuildNewDomains(FEModel& fem)\n{\n\t// This lookup table defines how a hex will be split in eight smaller hexes\n\tconst int LUT[8][8] = {\n\t\t{ 0,  8, 24, 11, 16, 20, 26, 23 },\n\t\t{ 8,  1,  9, 24, 20, 17, 21, 26 },\n\t\t{ 11, 24, 10,  3, 23, 26, 22, 19 },\n\t\t{ 24,  9,  2, 10, 26, 21, 18, 22 },\n\t\t{ 16, 20, 26, 23,  4, 12, 25, 15 },\n\t\t{ 20, 17, 21, 26, 12,  5, 13, 25 },\n\t\t{ 23, 26, 22, 19, 15, 25, 14,  7 },\n\t\t{ 26, 21, 18, 22, 25, 13,  6, 14 },\n\t};\n\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\tint nelems = 0;\n\tconst int NDOM = mesh.Domains();\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\t// get the old domain\n\t\tFEDomain& oldDom = mesh.Domain(i);\n\t\tint NE0 = oldDom.Elements();\n\n\t\t// count how many elements to split in this domain\n\t\tint newElems = 0;\n\t\tfor (int j = 0; j < NE0; ++j)\n\t\t{\n\t\t\tif (m_elemList[nelems + j] != -1) newElems++;\n\t\t}\n\n\t\t// make sure we have something to do\n\t\tif (newElems > 0)\n\t\t{\n\t\t\t// create a copy of old domain (since we want to retain the old domain)\n\t\t\tFEDomain* newDom = fecore_new<FESolidDomain>(oldDom.GetTypeStr(), &fem);\n\t\t\tnewDom->Create(NE0, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\t\t\tfor (int j = 0; j < NE0; ++j)\n\t\t\t{\n\t\t\t\tFEElement& el0 = oldDom.ElementRef(j);\n\t\t\t\tFEElement& el1 = newDom->ElementRef(j);\n\t\t\t\tel1.SetMatID(el0.GetMatID());\n\t\t\t\tel1.setStatus(el0.status());\n\t\t\t\tfor (int k = 0; k < el0.Nodes(); ++k) el1.m_node[k] = el0.m_node[k];\n\t\t\t}\n\n\t\t\t// reallocate the old domain\n\t\t\toldDom.Create(8 * newElems + (NE0 - newElems), FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\n\t\t\t// set new element nodes\n\t\t\tint nel = 0;\n\t\t\tfor (int j = 0; j < NE0; ++j, nelems++)\n\t\t\t{\n\t\t\t\tFEElement& el0 = newDom->ElementRef(j);\n\n\t\t\t\tif (m_elemList[nelems] != -1)\n\t\t\t\t{\n\t\t\t\t\tconst std::vector<int>& ee = topo.ElementEdgeList(nelems); assert(ee.size() == 12);\n\t\t\t\t\tconst std::vector<int>& ef = topo.ElementFaceList(nelems); assert(ef.size() == 6);\n\n\t\t\t\t\t// build the look-up table\n\t\t\t\t\tint ENL[27] = { 0 };\n\t\t\t\t\tENL[ 0] = el0.m_node[0];\n\t\t\t\t\tENL[ 1] = el0.m_node[1];\n\t\t\t\t\tENL[ 2] = el0.m_node[2];\n\t\t\t\t\tENL[ 3] = el0.m_node[3];\n\t\t\t\t\tENL[ 4] = el0.m_node[4];\n\t\t\t\t\tENL[ 5] = el0.m_node[5];\n\t\t\t\t\tENL[ 6] = el0.m_node[6];\n\t\t\t\t\tENL[ 7] = el0.m_node[7];\n\t\t\t\t\tENL[ 8] = m_edgeList[ee[0]];\n\t\t\t\t\tENL[ 9] = m_edgeList[ee[1]];\n\t\t\t\t\tENL[10] = m_edgeList[ee[2]];\n\t\t\t\t\tENL[11] = m_edgeList[ee[3]];\n\t\t\t\t\tENL[12] = m_edgeList[ee[4]];\n\t\t\t\t\tENL[13] = m_edgeList[ee[5]];\n\t\t\t\t\tENL[14] = m_edgeList[ee[6]];\n\t\t\t\t\tENL[15] = m_edgeList[ee[7]];\n\t\t\t\t\tENL[16] = m_edgeList[ee[8]];\n\t\t\t\t\tENL[17] = m_edgeList[ee[9]];\n\t\t\t\t\tENL[18] = m_edgeList[ee[10]];\n\t\t\t\t\tENL[19] = m_edgeList[ee[11]];\n\t\t\t\t\tENL[20] = m_faceList[ef[0]];\n\t\t\t\t\tENL[21] = m_faceList[ef[1]];\n\t\t\t\t\tENL[22] = m_faceList[ef[2]];\n\t\t\t\t\tENL[23] = m_faceList[ef[3]];\n\t\t\t\t\tENL[24] = m_faceList[ef[4]];\n\t\t\t\t\tENL[25] = m_faceList[ef[5]];\n\t\t\t\t\tENL[26] = m_elemList[nelems];\n\n\t\t\t\t\t// assign nodes to new elements\n\t\t\t\t\tfor (int k = 0; k < 8; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEElement& el1 = oldDom.ElementRef(nel++);\n\n\t\t\t\t\t\tel1.m_node[0] = ENL[LUT[k][0]];\n\t\t\t\t\t\tel1.m_node[1] = ENL[LUT[k][1]];\n\t\t\t\t\t\tel1.m_node[2] = ENL[LUT[k][2]];\n\t\t\t\t\t\tel1.m_node[3] = ENL[LUT[k][3]];\n\t\t\t\t\t\tel1.m_node[4] = ENL[LUT[k][4]];\n\t\t\t\t\t\tel1.m_node[5] = ENL[LUT[k][5]];\n\t\t\t\t\t\tel1.m_node[6] = ENL[LUT[k][6]];\n\t\t\t\t\t\tel1.m_node[7] = ENL[LUT[k][7]];\n\n\t\t\t\t\t\tel1.SetMatID(el0.GetMatID());\n\t\t\t\t\t\tel1.setStatus(el0.status());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// if the element is not split, we just copy the nodes from\n\t\t\t\t\t// the old domain\n\t\t\t\t\tFEElement& el1 = oldDom.ElementRef(nel++);\n\t\t\t\t\tel1.SetMatID(el0.GetMatID());\n\t\t\t\t\tel1.setStatus(el0.status());\n\t\t\t\t\tfor (int k = 0; k < el0.Nodes(); ++k) el1.m_node[k] = el0.m_node[k];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// we don't need this anymore\n\t\t\tdelete newDom;\n\t\t}\n\t}\n\tmesh.RebuildLUT();\n\n\t// re-init domains\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.CreateMaterialPointData();\n\t\tdom.Reset();\t// NOTE: we need to call this to actually call the Init function on the material points.\n\t\tdom.Init();\n\t\tdom.Activate();\n\t}\n}\n\nvoid FEHexRefine::UpdateNodeSet(FENodeSet& nset)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tvector<int> tag(m_NN, 0);\n\n\tfor (int j = 0; j < nset.Size(); ++j) tag[nset[j]] = 1;\n\n\tfor (int j = 0; j < topo.Edges(); ++j)\n\t{\n\t\tif (m_edgeList[j] >= 0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(j);\n\n\t\t\tif ((tag[edge.node[0]] == 1) && (tag[edge.node[1]] == 1))\n\t\t\t{\n\t\t\t\tnset.Add(m_edgeList[j]);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (int j = 0; j < topo.Faces(); ++j)\n\t{\n\t\tif (m_faceList[j] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(j);\n\n\t\t\tassert(face.ntype == 4);\n\t\t\tif ((tag[face.node[0]] == 1) &&\n\t\t\t\t(tag[face.node[1]] == 1) &&\n\t\t\t\t(tag[face.node[2]] == 1) &&\n\t\t\t\t(tag[face.node[3]] == 1))\n\t\t\t{\n\t\t\t\tnset.Add(m_faceList[j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool FEHexRefine::UpdateSurface(FESurface& surf)\n{\n\t// look-up table for splitting quads\n\tconst int LUT[4][4] = {\n\t\t{ 0, 4, 8, 7 },\n\t\t{ 4, 1, 5, 8 },\n\t\t{ 7, 8, 6, 3 },\n\t\t{ 8, 5, 2, 6 }\n\t};\n\n\tFEMeshTopo& topo = *m_topo;\n\tint NF0 = surf.Elements();\n\n\t// figure out which facets to split\n\tvector<int> faceList = topo.FaceIndexList(surf);\n\tassert((int)faceList.size() == NF0);\n\n\t// count how many faces to split\n\tint splitFaces = 0;\n\tfor (int i = 0; i < faceList.size(); ++i)\n\t{\n\t\tint iface = faceList[i];\n\t\tif (m_faceList[iface] >= 0) splitFaces++;\n\t}\n\tif (splitFaces == 0) return true;\n\n\t// create a copy of the domain\n\tFESurface oldSurf(GetFEModel());\n\toldSurf.Create(NF0);\n\tfor (int i = 0; i < NF0; ++i)\n\t{\n\t\tFESurfaceElement& el0 = surf.Element(i);\n\t\tFESurfaceElement& el1 = oldSurf.Element(i);\n\t\tel1.SetType(el0.Type());\n\t\tint nf = el0.Nodes();\n\t\tfor (int j = 0; j < nf; ++j) el1.m_node[j] = el0.m_node[j];\n\t}\n\n\t// reallocate the domain (Assumes Quad faces!)\n\tint NF1 = NF0 - splitFaces + 4 * (splitFaces);\n\tsurf.Create(NF1);\n\n\t// reinitialize the surface\n\tint n = 0;\n\tfor (int i = 0; i < NF0; ++i)\n\t{\n\t\tFESurfaceElement& el0 = oldSurf.Element(i);\n\n\t\tint iface = faceList[i];\n\t\tif (m_faceList[iface] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(iface);\n\t\t\tconst vector<int>& edge = topo.FaceEdgeList(iface);\n\n\t\t\tint NL[9];\n\t\t\tNL[0] = face.node[0];\n\t\t\tNL[1] = face.node[1];\n\t\t\tNL[2] = face.node[2];\n\t\t\tNL[3] = face.node[3];\n\t\t\tNL[4] = m_edgeList[edge[0]];\n\t\t\tNL[5] = m_edgeList[edge[1]];\n\t\t\tNL[6] = m_edgeList[edge[2]];\n\t\t\tNL[7] = m_edgeList[edge[3]];\n\t\t\tNL[8] = m_faceList[iface];\n\n\t\t\tfor (int j = 0; j < 4; ++j)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el1 = surf.Element(n++);\n\t\t\t\tel1.SetType(FE_QUAD4G4);\n\t\t\t\tel1.m_node[0] = NL[LUT[j][0]];\n\t\t\t\tel1.m_node[1] = NL[LUT[j][1]];\n\t\t\t\tel1.m_node[2] = NL[LUT[j][2]];\n\t\t\t\tel1.m_node[3] = NL[LUT[j][3]];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFESurfaceElement& el1 = surf.Element(n++);\n\t\t\tel1.SetType(FE_QUAD4G4);\n\t\t\tel1.m_node[0] = el0.m_node[0];\n\t\t\tel1.m_node[1] = el0.m_node[1];\n\t\t\tel1.m_node[2] = el0.m_node[2];\n\t\t\tel1.m_node[3] = el0.m_node[3];\n\t\t}\n\t}\n\n\treturn surf.Init();\n}\n"
  },
  {
    "path": "FEAMR/FEHexRefine.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FERefineMesh.h\"\n\nclass FEHexRefine : public FERefineMesh\n{\npublic:\n\tFEHexRefine(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool RefineMesh() override;\n\nprotected:\n\tvoid BuildSplitLists(FEModel& fem);\n\tvoid UpdateNewNodes(FEModel& fem);\n\tvoid FindHangingNodes(FEModel& fem);\n\tvoid BuildNewDomains(FEModel& fem);\n\tvoid UpdateNodeSet(FENodeSet& nset);\n\tbool UpdateSurface(FESurface& surf);\n\nprivate:\n\tint\t\tm_elemRefine;\t\t// max nr of elements to refine per step\n\tdouble\tm_maxValue;\n\n\tvector<int>\tm_elemList;\n\tvector<int>\tm_edgeList;\t// list of edge flags to see whether the edge was split\n\tvector<int>\tm_faceList;\t// list of face flags to see whether the face was split\n\tint\t\t\tm_N0;\n\tint\t\t\tm_NC;\n\tint\t\t\tm_NN;\n\n\tint\tm_splitElems;\n\tint m_splitFaces;\n\tint m_splitEdges;\n\tint\tm_hangingNodes;\n\n\tFEMeshAdaptorCriterion*\tm_criterion;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEHexRefine2D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEHexRefine2D.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEMeshTopo.h>\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEElementList.h>\n#include <FECore/FELinearConstraint.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/FESurface.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEHexRefine2D, FERefineMesh)\n\tADD_PARAMETER(m_elemRefine, \"max_elem_refine\");\n\tADD_PROPERTY(m_criterion, \"criterion\");\n\tADD_PARAMETER(m_maxValue, \"max_value\");\nEND_FECORE_CLASS();\n\nFEHexRefine2D::FEHexRefine2D(FEModel* fem) : FERefineMesh(fem)\n{\n\tm_maxValue = 0.0;\n\tm_elemRefine = 0;\n\tm_criterion = nullptr;\n}\n\nbool FEHexRefine2D::Init()\n{\n\tFEMesh& mesh = GetMesh();\n\n\tif (mesh.IsType(ET_HEX8) == false)\n\t{\n\t\tfeLogError(\"Cannot apply hex refinement: Mesh is not a HEX8 mesh.\");\n\t\treturn true;\n\t}\n\n\treturn FERefineMesh::Init();\n}\n\nbool FEHexRefine2D::RefineMesh()\n{\n\tFEMeshTopo& topo = *m_topo;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = GetMesh();\n\tFEElementList allElems(mesh);\n\n\tconst int NEL = mesh.Elements();\n\tconst int NDOM = mesh.Domains();\n\tm_N0 = mesh.Nodes();\n\tm_NC = topo.Edges();\n\tint NF = topo.Faces();\n\n\t// Build the lists of items to split\n\tif (BuildSplitLists(fem) == false) return false;\n\n\t// make sure we have work to do\n\tif (m_splitElems == 0) return false;\n\n\t// Next, the position and solution variables for all the nodes are updated.\n\t// Note that this has to be done before recreating the elements since \n\t// the old elements are still needed to determine the new positions and solutions. \n\tUpdateNewNodes(fem);\n\n\t// find the hanging nodes\n\t// and assign linear constraints to tie them down\n\tFindHangingNodes(fem);\n\n\t// Now, we can create new elements\n\tBuildNewDomains(fem);\n\n\t// update node sets\n\tfor (int i = 0; i < mesh.NodeSets(); ++i)\n\t{\n\t\tFENodeSet& nset = *mesh.NodeSet(i);\n\t\tUpdateNodeSet(nset);\n\t}\n\n\t// update all surfaces\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tif (UpdateSurface(surf) == false) return false;\n\t}\n\n\t// update all element sets\n\tfor (int i = 0; i < mesh.ElementSets(); ++i)\n\t{\n\t\tFEElementSet& set = mesh.ElementSet(i);\n\t\tif (UpdateElementSet(set) == false) return false;\n\t}\n\n\treturn true;\n}\n\nbool FEHexRefine2D::BuildSplitLists(FEModel& fem)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// Get the elements that we need to refine\n\tint NEL = mesh.Elements();\n\tm_elemList.assign(NEL, -1);\n\tif (m_criterion)\n\t{\n\t\tFEMeshAdaptorSelection selection = m_criterion->GetElementSelection(GetElementSet());\n\t\tfor (int i = 0; i < selection.size(); ++i)\n\t\t{\n\t\t\tif (selection[i].m_elemValue > m_maxValue)\n\t\t\t{\n\t\t\t\tint eid = selection[i].m_elementId;\n\t\t\t\tint lid = topo.GetElementIndexFromID(eid);\n\t\t\t\tm_elemList[lid] = 1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// just do'em all\n\t\tm_elemList.assign(NEL, 1);\n\t}\n\n\t// We cannot split elements that have hanging nodes\n\t// so remove those elements from the list\n\tint nrejected = 0;\n\tfor (int i = 0; i < NEL; ++i)\n\t{\n\t\tif (m_elemList[i] != -1)\n\t\t{\n\t\t\tFEElement& el = *topo.Element(i);\n\t\t\tint nel = el.Nodes();\n\t\t\tfor (int j = 0; j < nel; ++j)\n\t\t\t{\n\t\t\t\tif (mesh.Node(el.m_node[j]).HasFlags(FENode::HANGING))\n\t\t\t\t{\n\t\t\t\t\t// sorry, can't split this element\n\t\t\t\t\tm_elemList[i] = -1;\n\t\t\t\t\tnrejected++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (nrejected > 0)\n\t{\n\t\tfeLog(\"\\tElements rejected: %d\\n\", nrejected);\n\t}\n\n\t// count how many elements to split\n\tm_splitElems = 0;\n\tfor (int i = 0; i < m_elemList.size(); ++i) {\n\t\tif (m_elemList[i] == 1) {\n\t\t\tm_splitElems++;\n\t\t}\n\t}\n\tif (m_splitElems == 0) return true;\n\n\t// make sure we don't exceed the max elements per refinement step\n\tif ((m_elemRefine > 0) && (m_splitElems > m_elemRefine))\n\t{\n\t\tm_splitElems = 0;\n\t\tfor (int i = 0; i < m_elemList.size(); ++i) {\n\t\t\tif (m_elemList[i] == 0) {\n\t\t\t\tif (m_splitElems >= m_elemRefine)\n\t\t\t\t{\n\t\t\t\t\tm_elemList[i] = -1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tm_splitElems++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tassert(m_splitElems == m_elemRefine);\n\t}\n\n\t// figure out which faces to refine\n\tint NF = topo.Faces();\n\tm_faceList.assign(NF, -1);\n\n\t// Faces in the XY plane will be split in four. Other faces will be split in two. \n\t// So, we need to figure out which faces lie in the XY plane and which do not. \n\t// We will mark faces that are not in the XY plane by a -2\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\n\t\t// calculate face normal\n\t\tvec3d r0 = mesh.Node(face.node[0]).m_r0;\n\t\tvec3d r1 = mesh.Node(face.node[1]).m_r0;\n\t\tvec3d r2 = mesh.Node(face.node[2]).m_r0;\n\t\tvec3d n = (r1 - r0) ^ (r2 - r0); n.unit();\n\t\tif (fabs(n.z) < 0.999)\n\t\t{\n\t\t\t// this normal is not perpendicular to XY plane, so mark it\n\t\t\tm_faceList[i] = -2;\n\t\t}\n\t}\n\n\tfor (int i = 0; i < m_elemList.size(); ++i)\n\t{\n\t\tif (m_elemList[i] != -1)\n\t\t{\n\t\t\tint splitFaces = 0;\n\t\t\tconst std::vector<int>& elface = topo.ElementFaceList(i);\n\t\t\tfor (int j = 0; j < elface.size(); ++j)\n\t\t\t{\n\t\t\t\tif (m_faceList[elface[j]] == -1)\n\t\t\t\t{\n\t\t\t\t\t// this face will be split in 4\n\t\t\t\t\tm_faceList[elface[j]] = 1;\n\t\t\t\t\tsplitFaces++;\n\t\t\t\t}\n\t\t\t\telse if (m_faceList[elface[j]] == -2)\n\t\t\t\t{\n\t\t\t\t\t// this face will be split in 2\n\t\t\t\t\tm_faceList[elface[j]] = 2;\n\t\t\t\t\tsplitFaces++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// There should always be at least two faces to split.\n\t\t\tif (splitFaces < 2)\n\t\t\t{\n\t\t\t\tfeLog(\"Cannot refine element due to error.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// count how many faces to split\n\tint N1 = mesh.Nodes();\n\tm_splitFaces = 0;\n\tfor (int i = 0; i < m_faceList.size(); ++i) \n\t{\n\t\tif (m_faceList[i] == 1) {\n\t\t\tm_faceList[i] = N1++;\n\t\t\tm_splitFaces++;\n\t\t}\n\t\telse if (m_faceList[i] == 2)\n\t\t{\n\t\t\tm_splitFaces++;\n\t\t\tm_faceList[i] = -2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_faceList[i] = -1;\n\t\t}\n\t}\n\n\t// figure out which edges to refine\n\tm_edgeList.assign(m_NC, -1);\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tif (m_faceList[i] >= 0)\n\t\t{\n\t\t\tconst std::vector<int>& faceEdge = topo.FaceEdgeList(i);\n\t\t\tfor (int j = 0; j < faceEdge.size(); ++j) m_edgeList[faceEdge[j]] = 1;\n\t\t}\n\t}\n\n\t// count how many edges to split\n\tm_splitEdges = 0;\n\tfor (int i = 0; i < m_NC; ++i) {\n\t\tif (m_edgeList[i] == 1) {\n\t\t\tm_edgeList[i] = N1++;\n\t\t\tm_splitEdges++;\n\t\t}\n\t}\n\n\tfeLog(\"\\tRefinement info:\\n\");\n\tfeLog(\"\\t  Elements to refine: %d\\n\", m_splitElems);\n\tfeLog(\"\\t  Facets to refine  : %d\\n\", m_splitFaces);\n\tfeLog(\"\\t  Edges to refine   : %d\\n\", m_splitEdges);\n\n\treturn true;\n}\n\n// in FEHexRefine.cpp\nint findNodeInMesh(FEMesh& mesh, const vec3d& r, double tol = 1e-9);\n\nvoid FEHexRefine2D::UpdateNewNodes(FEModel& fem)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// we need to create a new node for each edge and face that needs to be split\n\tint newNodes = m_splitEdges + m_splitFaces;\n\n\t// for now, store the position of these new nodes in an array\n\tvector<vec3d> newPos(newNodes);\n\n\t// get the position of new nodes\n\tint n = 0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] >= 0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\t\tvec3d r0 = mesh.Node(edge.node[0]).m_r0;\n\t\t\tvec3d r1 = mesh.Node(edge.node[1]).m_r0;\n\t\t\tnewPos[n++] = (r0 + r1)*0.5;\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\t\t\tvec3d r0(0, 0, 0);\n\t\t\tint nn = face.ntype;\n\t\t\tfor (int j = 0; j < nn; ++j) r0 += mesh.Node(face.node[j]).m_r0;\n\t\t\tr0 /= (double)nn;\n\t\t\tnewPos[n++] = r0;\n\t\t}\n\t}\n\n\t// some of these new nodes may coincide with an existing node\n\t//If we find one, we eliminate it\n\tn = 0;\n\tint nremoved = 0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] >= 0)\n\t\t{\n\t\t\tint nodeId = findNodeInMesh(mesh, newPos[n++]);\n\t\t\tif (nodeId >= 0)\n\t\t\t{\n\t\t\t\tassert(nodeId < m_N0);\n\t\t\t\tif(mesh.Node(nodeId).HasFlags(FENode::HANGING))\n\t\t\t\t\tmesh.Node(nodeId).UnsetFlags(FENode::HANGING);\n\t\t\t\tm_edgeList[i] = nodeId;\n\t\t\t\tnremoved++;\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] >= 0)\n\t\t{\n\t\t\tint nodeId = findNodeInMesh(mesh, newPos[n++]);\n\t\t\tif (nodeId >= 0)\n\t\t\t{\n\t\t\t\tassert(nodeId < m_N0);\n\t\t\t\tif(mesh.Node(nodeId).HasFlags(FENode::HANGING))\n\t\t\t\t\tmesh.Node(nodeId).UnsetFlags(FENode::HANGING);\n\t\t\t\tm_faceList[i] = nodeId;\n\t\t\t\tnremoved++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// we need to reindex nodes if some were removed\n\tif (nremoved > 0)\n\t{\n\t\tn = m_N0;\n\t\tfor (int i = 0; i < topo.Edges(); ++i)\n\t\t{\n\t\t\tif (m_edgeList[i] >= m_N0) m_edgeList[i] = n++;\n\t\t}\n\t\tfor (int i = 0; i < topo.Faces(); ++i)\n\t\t{\n\t\t\tif (m_faceList[i] >= m_N0) m_faceList[i] = n++;\n\t\t}\n\t\tassert(n == (m_N0 + newNodes - nremoved));\n\t\tnewNodes -= nremoved;\n\t}\n\n\t// now, generate new nodes\n\tmesh.AddNodes(newNodes);\n\n\t// assign dofs to new nodes\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tm_NN = mesh.Nodes();\n\tfor (int i = m_N0; i<m_NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.SetDOFS(MAX_DOFS);\n\t}\n\n\t// update the position of these new nodes\n\tn = 0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] >= m_N0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\t\tFENode& node = mesh.Node(m_edgeList[i]);\n\t\t\tvec3d r0 = mesh.Node(edge.node[0]).m_r0;\n\t\t\tvec3d r1 = mesh.Node(edge.node[1]).m_r0;\n\t\t\tnode.m_r0 = (r0 + r1)*0.5;\n\n\t\t\tr0 = mesh.Node(edge.node[0]).m_rt;\n\t\t\tr1 = mesh.Node(edge.node[1]).m_rt;\n\t\t\tnode.m_rt = (r0 + r1)*0.5;\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] >= m_N0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\t\t\tFENode& node = mesh.Node(m_faceList[i]);\n\t\t\tint nn = face.ntype;\n\n\t\t\tvec3d r0(0, 0, 0);\n\t\t\tfor (int j = 0; j < nn; ++j) r0 += mesh.Node(face.node[j]).m_r0;\n\t\t\tr0 /= (double)nn;\n\t\t\tnode.m_r0 = r0;\n\n\t\t\tvec3d rt(0, 0, 0);\n\t\t\tfor (int j = 0; j < nn; ++j) rt += mesh.Node(face.node[j]).m_rt;\n\t\t\trt /= (double)nn;\n\t\t\tnode.m_rt = rt;\n\t\t}\n\t}\n\n\t// re-evaluate solution at nodes\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tif (m_edgeList[i] >= m_N0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\t\tFENode& node0 = mesh.Node(edge.node[0]);\n\t\t\tFENode& node1 = mesh.Node(edge.node[1]);\n\n\t\t\tFENode& node = mesh.Node(m_edgeList[i]);\n\t\t\tfor (int j = 0; j < MAX_DOFS; ++j)\n\t\t\t{\n\t\t\t\tdouble v = (node0.get(j) + node1.get(j))*0.5;\n\t\t\t\tnode.set(j, v);\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i = 0; i < topo.Faces(); ++i)\n\t{\n\t\tif (m_faceList[i] >= m_N0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(i);\n\t\t\tFENode& node = mesh.Node(m_faceList[i]);\n\t\t\tint nn = face.ntype;\n\t\t\tfor (int j = 0; j < MAX_DOFS; ++j)\n\t\t\t{\n\t\t\t\tdouble v = 0.0;\n\t\t\t\tfor (int k = 0; k < nn; ++k) v += mesh.Node(face.node[k]).get(j);\n\t\t\t\tv /= (double)nn;\n\t\t\t\tnode.set(j, v);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function identifies the hanging nodes and assigns linear constraints to them.\nvoid FEHexRefine2D::FindHangingNodes(FEModel& fem)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\tm_hangingNodes = 0;\n\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\n\t// First, we remove any constraints on nodes that are no longer hanging\n\tint nremoved = 0;\n\tfor (int i = 0; i < LCM.LinearConstraints();)\n\t{\n\t\tFELinearConstraint& lc = LCM.LinearConstraint(i);\n\t\tint nodeID = lc.GetParentNode();\n\t\tif (mesh.Node(nodeID).HasFlags(FENode::HANGING) == false)\n\t\t{\n\t\t\tLCM.RemoveLinearConstraint(i);\n\t\t\tnremoved++;\n\t\t}\n\t\telse i++;\n\t}\n\tfeLog(\"\\tRemoved linear constraints : %d\\n\", nremoved);\n\n\t// Total nr of degrees of freedom\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\n\t// we loop over non-split faces\n\tint nadded = 0;\n\tvector<int> tag(m_NC, 0);\n\tint NF = topo.Faces();\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tif (m_faceList[i] == -1)\n\t\t{\n\t\t\t// This face is not split\n\t\t\tconst std::vector<int>& fel = topo.FaceEdgeList(i);\n\t\t\tfor (int j = 0; j < fel.size(); ++j)\n\t\t\t{\n\t\t\t\tif ((m_edgeList[fel[j]] >= 0) && (tag[fel[j]] == 0))\n\t\t\t\t{\n\t\t\t\t\t// Tag the node as hanging so we can identify it easier later\n\t\t\t\t\tint nodeId = m_edgeList[fel[j]];\n\t\t\t\t\tFENode& node = mesh.Node(nodeId);\n\t\t\t\t\tnode.SetFlags(FENode::HANGING);\n\n\t\t\t\t\t// get the edge\n\t\t\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(fel[j]);\n\n\t\t\t\t\t// setup a linear constraint for this node\n\t\t\t\t\tfor (int k = 0; k < MAX_DOFS; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFELinearConstraint* lc = new FELinearConstraint(&fem);\n\t\t\t\t\t\tlc->SetParentDof(k, nodeId);\n\t\t\t\t\t\tlc->AddChildDof(k, edge.node[0], 0.5);\n\t\t\t\t\t\tlc->AddChildDof(k, edge.node[1], 0.5);\n\n\t\t\t\t\t\tLCM.AddLinearConstraint(lc);\n\t\t\t\t\t\tnadded++;\n\t\t\t\t\t}\n\n\t\t\t\t\t// set a tag to avoid double-counting\n\t\t\t\t\ttag[fel[j]] = 1;\n\n\t\t\t\t\tm_hangingNodes++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfeLog(\"\\tHanging nodes ............ : %d\\n\", m_hangingNodes);\n\tfeLog(\"\\tAdded linear constraints . : %d\\n\", nadded);\n}\n\nvoid FEHexRefine2D::BuildNewDomains(FEModel& fem)\n{\n\t// This lookup table defines how a hex will be split in four smaller hexes\n\tconst int LUT[4][8] = {\n\t\t{  0,  8, 16, 11,  4, 12, 17, 15 },\n\t\t{  8,  1,  9, 16, 12,  5, 13, 17 },\n\t\t{ 11, 16, 10,  3, 15, 17, 14,  7 },\n\t\t{ 16,  9,  2, 10, 17, 13,  6, 14 }\n\t};\n\n\tFEMeshTopo& topo = *m_topo;\n\tFEMesh& mesh = fem.GetMesh();\n\n\tint nelems = 0;\n\tconst int NDOM = mesh.Domains();\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\t// get the old domain\n\t\tFEDomain& oldDom = mesh.Domain(i);\n\t\tint NE0 = oldDom.Elements();\n\n\t\t// count how many elements to split in this domain\n\t\tint newElems = 0;\n\t\tfor (int j = 0; j < NE0; ++j)\n\t\t{\n\t\t\tif (m_elemList[nelems + j] != -1) newElems++;\n\t\t}\n\n\t\t// make sure we have something to do\n\t\tif (newElems > 0)\n\t\t{\n\t\t\t// create a copy of old domain (since we want to retain the old domain)\n\t\t\tFEDomain* newDom = fecore_new<FESolidDomain>(oldDom.GetTypeStr(), &fem);\n\t\t\tnewDom->Create(NE0, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\t\t\tfor (int j = 0; j < NE0; ++j)\n\t\t\t{\n\t\t\t\tFEElement& el0 = oldDom.ElementRef(j);\n\t\t\t\tFEElement& el1 = newDom->ElementRef(j);\n\t\t\t\tfor (int k = 0; k < el0.Nodes(); ++k) el1.m_node[k] = el0.m_node[k];\n\t\t\t\tel1.m_val = el0.m_val;\n\t\t\t\tel1.SetMatID(el0.GetMatID());\n\t\t\t}\n\n\t\t\t// reallocate the old domain\n\t\t\toldDom.Create(4 * newElems + (NE0 - newElems), FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\n\t\t\t// set new element nodes\n\t\t\tint nel = 0;\n\t\t\tfor (int j = 0; j < NE0; ++j, nelems++)\n\t\t\t{\n\t\t\t\tFEElement& el0 = newDom->ElementRef(j);\n\n\t\t\t\tif (m_elemList[nelems] != -1)\n\t\t\t\t{\n\t\t\t\t\tconst std::vector<int>& ee = topo.ElementEdgeList(nelems); assert(ee.size() == 12);\n\t\t\t\t\tconst std::vector<int>& ef = topo.ElementFaceList(nelems); assert(ef.size() == 6);\n\n\t\t\t\t\t// build the look-up table\n\t\t\t\t\tint ENL[27] = { 0 };\n\t\t\t\t\tENL[ 0] = el0.m_node[0];\n\t\t\t\t\tENL[ 1] = el0.m_node[1];\n\t\t\t\t\tENL[ 2] = el0.m_node[2];\n\t\t\t\t\tENL[ 3] = el0.m_node[3];\n\t\t\t\t\tENL[ 4] = el0.m_node[4];\n\t\t\t\t\tENL[ 5] = el0.m_node[5];\n\t\t\t\t\tENL[ 6] = el0.m_node[6];\n\t\t\t\t\tENL[ 7] = el0.m_node[7];\n\t\t\t\t\tENL[ 8] = m_edgeList[ee[0]];\n\t\t\t\t\tENL[ 9] = m_edgeList[ee[1]];\n\t\t\t\t\tENL[10] = m_edgeList[ee[2]];\n\t\t\t\t\tENL[11] = m_edgeList[ee[3]];\n\t\t\t\t\tENL[12] = m_edgeList[ee[4]];\n\t\t\t\t\tENL[13] = m_edgeList[ee[5]];\n\t\t\t\t\tENL[14] = m_edgeList[ee[6]];\n\t\t\t\t\tENL[15] = m_edgeList[ee[7]];\n\t\t\t\t\tENL[16] = m_faceList[ef[4]]; assert(ENL[16] >= 0);\n\t\t\t\t\tENL[17] = m_faceList[ef[5]]; assert(ENL[17] >= 0);\n\n\t\t\t\t\t// assign nodes to new elements\n\t\t\t\t\tfor (int k = 0; k < 4; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEElement& el1 = oldDom.ElementRef(nel++);\n\t\t\t\t\t\tel1.SetMatID(el0.GetMatID());\n\t\t\t\t\t\tel1.m_val = el0.m_val;\n\n\t\t\t\t\t\tel1.m_node[0] = ENL[LUT[k][0]];\n\t\t\t\t\t\tel1.m_node[1] = ENL[LUT[k][1]];\n\t\t\t\t\t\tel1.m_node[2] = ENL[LUT[k][2]];\n\t\t\t\t\t\tel1.m_node[3] = ENL[LUT[k][3]];\n\t\t\t\t\t\tel1.m_node[4] = ENL[LUT[k][4]];\n\t\t\t\t\t\tel1.m_node[5] = ENL[LUT[k][5]];\n\t\t\t\t\t\tel1.m_node[6] = ENL[LUT[k][6]];\n\t\t\t\t\t\tel1.m_node[7] = ENL[LUT[k][7]];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// if the element is not split, we just copy the nodes from\n\t\t\t\t\t// the old domain\n\t\t\t\t\tFEElement& el1 = oldDom.ElementRef(nel++);\n\t\t\t\t\tfor (int k = 0; k < el0.Nodes(); ++k) el1.m_node[k] = el0.m_node[k];\n\t\t\t\t\tel1.m_val = el0.m_val;\n\t\t\t\t\tel1.SetMatID(el0.GetMatID());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// we don't need this anymore\n\t\t\tdelete newDom;\n\t\t}\n\t}\n\tmesh.RebuildLUT();\n\n\t// re-init domains\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.CreateMaterialPointData();\n\t\tdom.Reset();\t// NOTE: we need to call this to actually call the Init function on the material points.\n\t\tdom.Init();\n\t\tdom.Activate();\n\t}\n}\n\nvoid FEHexRefine2D::UpdateNodeSet(FENodeSet& nset)\n{\n\tFEMeshTopo& topo = *m_topo;\n\tvector<int> tag(m_NN, 0);\n\n\tfor (int j = 0; j < nset.Size(); ++j) tag[nset[j]] = 1;\n\n\tfor (int j = 0; j < topo.Edges(); ++j)\n\t{\n\t\tif (m_edgeList[j] >= 0)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(j);\n\n\t\t\tif ((tag[edge.node[0]] == 1) && (tag[edge.node[1]] == 1))\n\t\t\t{\n\t\t\t\tnset.Add(m_edgeList[j]);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (int j = 0; j < topo.Faces(); ++j)\n\t{\n\t\tif (m_faceList[j] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(j);\n\n\t\t\tassert(face.ntype == 4);\n\t\t\tif ((tag[face.node[0]] == 1) &&\n\t\t\t\t(tag[face.node[1]] == 1) &&\n\t\t\t\t(tag[face.node[2]] == 1) &&\n\t\t\t\t(tag[face.node[3]] == 1))\n\t\t\t{\n\t\t\t\tnset.Add(m_faceList[j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool FEHexRefine2D::UpdateSurface(FESurface& surf)\n{\n\t// look-up table for splitting quads in 4\n\tconst int LUT[4][4] = {\n\t\t{ 0, 4, 8, 7 },\n\t\t{ 4, 1, 5, 8 },\n\t\t{ 7, 8, 6, 3 },\n\t\t{ 8, 5, 2, 6 }\n\t};\n\n\tFEMeshTopo& topo = *m_topo;\n\tint NF0 = surf.Elements();\n\n\t// set all element pointers to zero\n\tfor (int i = 0; i < NF0; ++i)\n\t{\n\t\tFESurfaceElement& el = surf.Element(i);\n\t\tel.m_elem[0].Reset();\n\t\tel.m_elem[1].Reset();\n\t}\n\n\t// figure out which facets to split\n\tvector<int> faceList = topo.FaceIndexList(surf);\n\tassert((int)faceList.size() == NF0);\n\n\t// count how many faces to split\n\tint split4 = 0, split2 = 0;\n\tfor (int i = 0; i < faceList.size(); ++i)\n\t{\n\t\tint iface = faceList[i];\n\t\tif (m_faceList[iface] >= 0) split4++;\n\t\tif (m_faceList[iface] == -2) split2++;\n\t}\n\tif (split4 + split2 == 0) return surf.Init();\n\n\t// create a copy of the domain\n\tFESurface oldSurf(GetFEModel());\n\toldSurf.Create(NF0);\n\tfor (int i = 0; i < NF0; ++i)\n\t{\n\t\tFESurfaceElement& el0 = surf.Element(i);\n\t\tFESurfaceElement& el1 = oldSurf.Element(i);\n\t\tel1.SetType(el0.Type());\n\t\tint nf = el0.Nodes();\n\t\tfor (int j = 0; j < nf; ++j) el1.m_node[j] = el0.m_node[j];\n\t}\n\n\t// reallocate the domain (Assumes Quad faces!)\n\tint NF1 = NF0 - split4 + 4 * (split4) - split2 + 2*split2;\n\tsurf.Create(NF1);\n\n\t// reinitialize the surface\n\tint n = 0;\n\tfor (int i = 0; i < NF0; ++i)\n\t{\n\t\tFESurfaceElement& el0 = oldSurf.Element(i);\n\n\t\tint iface = faceList[i];\n\t\tif (m_faceList[iface] >= 0)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(iface);\n\t\t\tconst vector<int>& edge = topo.FaceEdgeList(iface);\n\n\t\t\tint NL[9];\n\t\t\tNL[0] = face.node[0];\n\t\t\tNL[1] = face.node[1];\n\t\t\tNL[2] = face.node[2];\n\t\t\tNL[3] = face.node[3];\n\t\t\tNL[4] = m_edgeList[edge[0]];\n\t\t\tNL[5] = m_edgeList[edge[1]];\n\t\t\tNL[6] = m_edgeList[edge[2]];\n\t\t\tNL[7] = m_edgeList[edge[3]];\n\t\t\tNL[8] = m_faceList[iface];\n\n\t\t\tfor (int j = 0; j < 4; ++j)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el1 = surf.Element(n++);\n\t\t\t\tel1.SetType(FE_QUAD4G4);\n\t\t\t\tel1.m_node[0] = NL[LUT[j][0]];\n\t\t\t\tel1.m_node[1] = NL[LUT[j][1]];\n\t\t\t\tel1.m_node[2] = NL[LUT[j][2]];\n\t\t\t\tel1.m_node[3] = NL[LUT[j][3]];\n\t\t\t}\n\t\t}\n\t\telse if (m_faceList[iface] == -2)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = topo.Face(iface);\n\t\t\tconst vector<int>& edge = topo.FaceEdgeList(iface);\n\n\t\t\tint NL[2][4];\n\n\t\t\t// there should be two edges that are split, and two that are not\n\t\t\tint eid[4] = { m_edgeList[edge[0]], m_edgeList[edge[1]], m_edgeList[edge[2]], m_edgeList[edge[3]] };\n\n\t\t\tif ((eid[0] >= 0) && (eid[2] >= 0))\n\t\t\t{\n\t\t\t\tassert((eid[1] == -1) && (eid[3] == -1));\n\t\t\t\tNL[0][0] = face.node[0]; NL[1][0] = face.node[1];\n\t\t\t\tNL[0][1] =       eid[0]; NL[1][1] = face.node[2];\n\t\t\t\tNL[0][2] =       eid[2]; NL[1][2] =       eid[2];\n\t\t\t\tNL[0][3] = face.node[3]; NL[1][3] =       eid[0];\n\t\t\t}\n\t\t\telse if ((eid[1] >= 0) && (eid[3] >= 0))\n\t\t\t{\n\t\t\t\tassert((eid[0] == -1) && (eid[2] == -1));\n\t\t\t\tNL[0][0] = face.node[0]; NL[1][0] = face.node[2];\n\t\t\t\tNL[0][1] = face.node[1]; NL[1][1] = face.node[3];\n\t\t\t\tNL[0][2] =       eid[1]; NL[1][2] =       eid[3];\n\t\t\t\tNL[0][3] =       eid[3]; NL[1][3] =       eid[1];\n\t\t\t}\n\t\t\telse { assert(false); }\n\n\t\t\tfor (int j = 0; j < 2; ++j)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el1 = surf.Element(n++);\n\t\t\t\tel1.SetType(FE_QUAD4G4);\n\t\t\t\tel1.m_node[0] = NL[j][0];\n\t\t\t\tel1.m_node[1] = NL[j][1];\n\t\t\t\tel1.m_node[2] = NL[j][2];\n\t\t\t\tel1.m_node[3] = NL[j][3];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFESurfaceElement& el1 = surf.Element(n++);\n\t\t\tel1.SetType(FE_QUAD4G4);\n\t\t\tel1.m_node[0] = el0.m_node[0];\n\t\t\tel1.m_node[1] = el0.m_node[1];\n\t\t\tel1.m_node[2] = el0.m_node[2];\n\t\t\tel1.m_node[3] = el0.m_node[3];\n\t\t}\n\t}\n\tsurf.CreateMaterialPointData();\n\n\treturn surf.Init();\n}\n\nbool FEHexRefine2D::UpdateElementSet(FEElementSet& eset)\n{\n\t// get the domain list\n\t// NOTE: Don't get the reference, since then the same reference\n\t// is passed to Create below, which causes problems.\n\tFEDomainList domList = eset.GetDomainList();\n\tif (domList.IsEmpty()) { throw std::runtime_error(\"Error in FEHexRefine2D!\"); }\n\n\t// recreate the element set from the domain list\n\teset.Create(domList);\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEAMR/FEHexRefine2D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FERefineMesh.h\"\n\nclass FEHexRefine2D : public FERefineMesh\n{\npublic:\n\tFEHexRefine2D(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool RefineMesh() override;\n\nprotected:\n\tbool BuildSplitLists(FEModel& fem);\n\tvoid UpdateNewNodes(FEModel& fem);\n\tvoid FindHangingNodes(FEModel& fem);\n\tvoid BuildNewDomains(FEModel& fem);\n\tvoid UpdateNodeSet(FENodeSet& nset);\n\tbool UpdateSurface(FESurface& surf);\n\tbool UpdateElementSet(FEElementSet& set);\n\nprivate:\n\tdouble\tm_maxValue;\n\tint\t\tm_elemRefine;\t\t// max nr of elements to refine per step\n\n\tvector<int>\tm_elemList;\n\tvector<int>\tm_edgeList;\t// list of edge flags to see whether the edge was split\n\tvector<int>\tm_faceList;\t// list of face flags to see whether the face was split\n\tint\t\t\tm_N0;\n\tint\t\t\tm_NC;\n\tint\t\t\tm_NN;\n\n\tint\tm_splitElems;\n\tint m_splitFaces;\n\tint m_splitEdges;\n\tint\tm_hangingNodes;\n\n\tFEMeshAdaptorCriterion*\tm_criterion;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FELeastSquaresInterpolator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FELeastSquaresInterpolator.h\"\n#include <FECore/FENNQuery.h>\n#include <algorithm>\nusing namespace std;\n\nclass KDTree\n{\npublic:\n\tKDTree()\n\t{\n\t\tm_parent = nullptr;\n\t\tm_left = nullptr;\n\t\tm_right = nullptr;\n\t}\n\n\t~KDTree()\n\t{\n\t\tdelete m_left;\n\t\tdelete m_right;\n\t}\n\n\tvoid build(vector<vec3d> pts, int depth = 0)\n\t{\n\t\tsize_t n = pts.size();\n\t\tif (n == 1)\n\t\t{\n\t\t\tm_r = pts[0];\n\t\t\treturn;\n\t\t}\n\n\t\tint axis = depth % 3;\n\t\tstd::sort(pts.begin(), pts.end(), [=](const vec3d& a, const vec3d& b) {\n\t\t\tif (axis == 0) return (a.x < b.x);\n\t\t\tif (axis == 1) return (a.y < b.y);\n\t\t\tif (axis == 2) return (a.z < b.z);\n\t\t\treturn false;\n\t\t});\n\n\t\tint med = n / 2;\n\n\t\tm_r = pts[med];\n\n\t\t// build left list\n\t\tif (med > 0)\n\t\t{\n\t\t\tvector<vec3d> l(pts.begin(), pts.begin() + med);\n\t\t\tm_left = new KDTree(this);\n\t\t\tm_left->build(l, depth + 1);\n\t\t}\n\n\t\t// build right list\n\t\tif (med < n - 1)\n\t\t{\n\t\t\tvector<vec3d> r(pts.begin() + med + 1, pts.end());\n\t\t\tm_right = new KDTree(this);\n\t\t\tm_right->build(r, depth + 1);\n\t\t}\n\t}\n\npublic:\n\tvec3d\tm_r;\n\tKDTree*\tm_parent;\n\tKDTree*\tm_left;\n\tKDTree*\tm_right;\n\npublic:\n\tKDTree(KDTree* parent) : m_parent(parent)\n\t{\n\t\tm_left = nullptr;\n\t\tm_right = nullptr;\n\t}\n};\n\nclass NearestNeighborSearch\n{\npublic:\n\tNearestNeighborSearch() {}\n\n\tvoid Init(const std::vector<vec3d>& points, int k)\n\t{\n\t\tm_k = k;\n\t\tm_points = points;\n\n//\t\tm_kdtree.build(m_points);\n\t}\n\n\tint findNearestNeighbors(const vec3d& x, std::vector<int>& closestNodes)\n\t{\n\t\treturn findNeirestNeighbors(m_points, x, m_k, closestNodes);\n\t}\n\nprotected:\n\tint\t\t\t\tm_k;\n\tvector<vec3d>\tm_points;\n\n\tKDTree\tm_kdtree;\n};\n\nFELeastSquaresInterpolator::Data::Data() {}\nFELeastSquaresInterpolator::Data::Data(const Data& d)\n{\n\tA = d.A;\n\tindex = d.index;\n\tW = d.W;\n\tX = d.X;\n\tcpl = d.cpl;\n}\nvoid FELeastSquaresInterpolator::Data::operator = (const Data& d)\n{\n\tA = d.A;\n\tindex = d.index;\n\tW = d.W;\n\tX = d.X;\n\tcpl = d.cpl;\n}\n\nFELeastSquaresInterpolator::FELeastSquaresInterpolator()\n{\n\tm_dim = 3;\n\tm_nnc = 8;\n\tm_checkForMatch = false;\n}\n\n//! Set dimension (2 or 3)\nvoid FELeastSquaresInterpolator::SetDimension(int d)\n{\n\tassert((d == 2) || (d == 3));\n\tm_dim = d;\n}\n\nvoid FELeastSquaresInterpolator::SetNearestNeighborCount(int nnc) { m_nnc = nnc; }\n\nvoid FELeastSquaresInterpolator::SetCheckForMatch(bool b) { m_checkForMatch = b; }\n\nvoid FELeastSquaresInterpolator::SetSourcePoints(const vector<vec3d>& srcPoints)\n{\n\tm_src = srcPoints;\n}\n\nvoid FELeastSquaresInterpolator::SetTargetPoints(const vector<vec3d>& trgPoints)\n{\n\tm_trg = trgPoints;\n}\n\nbool FELeastSquaresInterpolator::SetTargetPoint(const vec3d& trgPoint)\n{\n\tm_trg.clear();\n\tm_trg.push_back(trgPoint);\n\treturn Init();\n}\n\nbool FELeastSquaresInterpolator::Init()\n{\n\tif (m_nnc < 4) return false;\n\tif (m_src.empty()) return false;\n\tif (m_trg.empty()) return false;\n\n\tint N0 = m_src.size();\n\tint N1 = m_trg.size();\n\n\tm_data.resize(N1);\n\n\t// initialize nearest neighbor search\n\tNearestNeighborSearch NNS;\n\tNNS.Init(m_src, m_nnc);\n\n\t// do nearest-neighbor search\n\tfor (int i = 0; i < N1; ++i)\n\t{\n\t\tvec3d ri = m_trg[i];\n\t\tint M = NNS.findNearestNeighbors(ri, m_data[i].cpl);\n\t\tassert(M > 4);\n\t\tm_data[i].cpl.resize(M);\n\t}\n\n\tfor (int i = 0; i < N1; ++i)\n\t{\n\t\tData& d = m_data[i];\n\t\tvec3d x = m_trg[i];\n\n\t\tvector<int>& closestNodes = m_data[i].cpl;\n\t\tint M = closestNodes.size();\n\n\t\t// the last node is the farthest and determines the radius\n\t\tvec3d& r = m_src[closestNodes[M - 1]];\n\t\tdouble L = sqrt((r - x)*(r - x));\n\n\t\t// add some offset to make sure none of the points will have a weight of zero.\n\t\t// Such points would otherwise be ignored, which reduces the net number of interpolation\n\t\t// points and make the MLS system ill-conditioned\n\t\tL += L * 0.05;\n\n\t\t// evaluate weights and displacements\n\t\td.X.resize(M);\n\t\td.W.resize(M);\n\t\tfor (int m = 0; m < M; ++m)\n\t\t{\n\t\t\tvec3d rm = m_src[closestNodes[m]];\n\t\t\tvec3d rj = x - rm;\n\n\t\t\td.X[m] = rj;\n\n\t\t\tdouble D = sqrt(rj*rj);\n\t\t\tdouble wj = 1.0 - D / L;\n\t\t\td.W[m] = wj;\n\t\t}\n\n\t\t// setup least squares problems\n\t\td.A.resize(m_dim + 1, m_dim + 1);\n\t\td.A.zero();\n\t\tfor (int m = 0; m < M; ++m)\n\t\t{\n\t\t\tvec3d ri = d.X[m];\n\t\t\tdouble P[4] = { 1.0, ri.x, ri.y, ri.z };\n\t\t\tfor (int a = 0; a <= m_dim; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b <= m_dim; ++b)\n\t\t\t\t{\n\t\t\t\t\td.A(a, b) += d.W[m] * P[a] * P[b];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// solve the linear system of equations\n\t\td.index.resize(m_dim + 1);\n\t\td.A.lufactor(d.index);\n\t}\n\n\treturn true;\n}\n\nbool FELeastSquaresInterpolator::Map(std::vector<double>& tval, function<double(int sourceNode)> src)\n{\n\tif (m_data.size() != m_trg.size()) return false;\n\n\tfor (int i = 0; i < m_trg.size(); ++i)\n\t{\n\t\tData& d = m_data[i];\n\n\t\tvector<int>& closestNodes = m_data[i].cpl;\n\t\tint M = closestNodes.size();\n\n\t\t// evaluate weights and positions\n\t\tvector<vec3d>& X = d.X;\n\t\tvector<double>& W = d.W;\n\n\t\t// update nodal values\n\t\tvector<double> b(m_dim + 1, 0.0);\n\t\tfor (int m = 0; m < M; ++m)\n\t\t{\n\t\t\tvec3d ri = d.X[m];\n\t\t\tdouble P[4] = { 1.0, ri.x, ri.y, ri.z };\n\n\t\t\tdouble vm = src(closestNodes[m]);\n\n\t\t\tfor (int a = 0; a <= m_dim; ++a)\n\t\t\t{\n\t\t\t\tb[a] += W[m] * P[a] * vm;\n\t\t\t}\n\t\t}\n\n\t\t// solve the linear system of equations\n\t\td.A.lusolve(b, d.index);\n\n\t\ttval[i] = b[0];\n\t}\n\n\treturn true;\n}\n\ndouble FELeastSquaresInterpolator::Map(int inode, function<double(int sourceNode)> f)\n{\n\tData& d = m_data[inode];\n\n\tvector<int>& closestNodes = m_data[inode].cpl;\n\tint M = closestNodes.size();\n\n\t// evaluate weights and positions\n\tvector<vec3d>& X = d.X;\n\tvector<double>& W = d.W;\n\n\tif (m_checkForMatch)\n\t{\n\t\tif (W[0] > 0.9999)\n\t\t{\n\t\t\treturn f(closestNodes[0]);\n\t\t}\n\t}\n\n\t// update nodal values\n\tvector<double> b(m_dim + 1, 0.0);\n\tfor (int m = 0; m < M; ++m)\n\t{\n\t\tvec3d ri = d.X[m];\n\t\tdouble P[4] = { 1.0, ri.x, ri.y, ri.z };\n\n\t\tdouble vm = f(closestNodes[m]);\n\n\t\tfor (int a = 0; a <= m_dim; ++a)\n\t\t{\n\t\t\tb[a] += W[m] * P[a] * vm;\n\t\t}\n\t}\n\n\t// solve the linear system of equations\n\td.A.lusolve(b, d.index);\n\n\treturn b[0];\n}\n\nvec3d FELeastSquaresInterpolator::MapVec3d(int inode, function<vec3d(int sourceNode)> f)\n{\n\tData& d = m_data[inode];\n\n\tvector<int>& closestNodes = m_data[inode].cpl;\n\tint M = closestNodes.size();\n\n\t// evaluate weights and positions\n\tvector<vec3d>& X = d.X;\n\tvector<double>& W = d.W;\n\n\tif (m_checkForMatch)\n\t{\n\t\tif (W[0] > 0.9999)\n\t\t{\n\t\t\treturn f(closestNodes[0]);\n\t\t}\n\t}\n\n\t// update nodal values\n\tvector<double> bx(m_dim+1, 0.0), by(m_dim + 1, 0.0), bz(m_dim + 1, 0.0);\n\tfor (int m = 0; m < M; ++m)\n\t{\n\t\tvec3d ri = d.X[m];\n\t\tdouble P[4] = { 1.0, ri.x, ri.y, ri.z };\n\n\t\tvec3d vm = f(closestNodes[m]);\n\n\t\tfor (int a = 0; a <= m_dim; ++a)\n\t\t{\n\t\t\tbx[a] += W[m] * P[a] * vm.x;\n\t\t\tby[a] += W[m] * P[a] * vm.y;\n\t\t\tbz[a] += W[m] * P[a] * vm.z;\n\t\t}\n\t}\n\n\t// solve the linear system of equations\n\td.A.lusolve(bx, d.index);\n\td.A.lusolve(by, d.index);\n\n\td.A.lusolve(bz, d.index);\n\n\treturn vec3d(bx[0], by[0], bz[0]);\n}\n"
  },
  {
    "path": "FEAMR/FELeastSquaresInterpolator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEMeshDataInterpolator.h\"\n\n//! Helper class for mapping data between two point sets using moving least squares.\nclass FELeastSquaresInterpolator : public FEMeshDataInterpolator\n{\n\tclass Data\n\t{\n\tpublic:\n\t\tData();\n\t\tData(const Data& d);\n\t\tvoid operator = (const Data& d);\n\n\tpublic:\n\t\tmatrix\t\t\tA;\n\t\tstd::vector<int>\t\tindex;\n\t\tstd::vector<double>\tW;\n\t\tstd::vector<vec3d>\tX;\n\t\tstd::vector<int>\t\tcpl;\n\t};\n\npublic:\n\t//! constructor\n\tFELeastSquaresInterpolator();\n\n\t//! Set dimension (2 or 3)\n\tvoid SetDimension(int d);\n\n\t//! Set the number of nearest neighbors to use (should be larger than 4)\n\tvoid SetNearestNeighborCount(int nnc);\n\n\t//! Set check for match flag. This will return the source value\n\t//! if the target point coincides\n\tvoid SetCheckForMatch(bool b);\n\n\t//! set the source points\n\tvoid SetSourcePoints(const std::vector<vec3d>& srcPoints);\n\n\t//! set the target points\n\tvoid SetTargetPoints(const std::vector<vec3d>& trgPoints);\n\tbool SetTargetPoint(const vec3d& trgPoint) override;\n\n\t//! initialize MLQ data\n\tbool Init() override;\n\n\t//! map source data onto target data\n\t//! input: sval - values of the source points\n\t//! output: tval - values at the target points\n\tbool Map(std::vector<double>& tval, std::function<double(int sourceNode)> src) override;\n\n\t// evaluate map\n\tdouble Map(int inode, std::function<double(int sourceNode)> src) override;\n\tvec3d MapVec3d(int inode, std::function<vec3d(int sourceNode)> src) override;\n\nprivate:\n\tint\t\tm_dim;\n\tint\t\tm_nnc;\n\tbool\tm_checkForMatch;\n\tstd::vector<vec3d>\tm_src;\t// source points\n\tstd::vector<vec3d>\tm_trg;\t// target points\n\n\tstd::vector< Data >\t\t\tm_data;\n};\n"
  },
  {
    "path": "FEAMR/FEMMGRemesh.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMMGRemesh.h\"\n#include <FECore/FEMeshTopo.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEDomain.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FESurface.h>\n#include <FECore/log.h>\n#include <FECore/FEOctreeSearch.h>\n#include <FECore/FENNQuery.h>\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/FEDomainMap.h>\n#include \"FELeastSquaresInterpolator.h\"\n#include \"FEMeshShapeInterpolator.h\"\n#include \"FEDomainShapeInterpolator.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEMaterial.h>\n#ifdef HAS_MMG\n#include \"mmg/mmg3d/libmmg3d.h\"\n\nclass FEMMGRemesh::MMG\n{\npublic:\n\tMMG(FEMMGRemesh* mmgRemesh) : m_mmgRemesh(mmgRemesh) {}\n\tbool build_mmg_mesh(MMG5_pMesh mmgMesg, MMG5_pSol mmgSol, FEMeshTopo& topo);\n\tbool build_new_mesh(MMG5_pMesh mmgMesh, MMG5_pSol mmgSol, FEModel& fem);\n\npublic:\n\tFEMMGRemesh*\tm_mmgRemesh;\n\tstd::vector<double>\tm_metric;\t// refinement metric\n\tstd::vector<int>\tm_nodeSetTag;\t// surface tags for node sets\n};\n\n#endif\n\nBEGIN_FECORE_CLASS(FEMMGRemesh, FERefineMesh)\n\tADD_PARAMETER(m_hmin, \"min_element_size\");\n\tADD_PARAMETER(m_hausd, \"hausdorff\");\n\tADD_PARAMETER(m_hgrad, \"gradation\");\n\tADD_PARAMETER(m_relativeSize, \"relative_size\");\n\tADD_PARAMETER(m_meshCoarsen, \"mesh_coarsen\");\n\tADD_PARAMETER(m_normalizeData, \"normalize_data\");\n\tADD_PROPERTY(m_criterion, \"criterion\");\n\tADD_PROPERTY(m_sfunc, \"size_function\", 0);\nEND_FECORE_CLASS();\n\nFEMMGRemesh::FEMMGRemesh(FEModel* fem) : FERefineMesh(fem)\n{\n\tm_maxelem = 0;\n\tm_relativeSize = true;\n\tm_meshCoarsen = false;\n\tm_normalizeData = false;\n\n\tm_hmin = 0.0;\n\tm_hausd = 0.01;\n\tm_hgrad = 1.3;\n\n\tm_criterion = nullptr;\n\n\tm_transferMethod = TRANSFER_MLQ;\n\tm_nnc = 8;\n\n\tm_sfunc = nullptr;\n\n#ifdef HAS_MMG\n\tmmg = new FEMMGRemesh::MMG(this);\n#endif\n}\n\nbool FEMMGRemesh::Init()\n{\n\tFEMesh& mesh = GetMesh();\n\tif (mesh.IsType(ET_TET4) == false) return false;\n\n\treturn FERefineMesh::Init();\n}\n\nbool FEMMGRemesh::RefineMesh()\n{\n#ifdef HAS_MMG\n\tFEMesh& mesh = GetMesh();\n\n\t// initialize the MMG mesh\n\tMMG5_pMesh mmgMesh = NULL;\n\tMMG5_pSol  mmgSol = NULL;\n\tMMG3D_Init_mesh(MMG5_ARG_start,\n\t\tMMG5_ARG_ppMesh, &mmgMesh, MMG5_ARG_ppMet, &mmgSol,\n\t\tMMG5_ARG_end);\n\n\t// --- build the MMG mesh ---\n\tFEMeshTopo& topo = *m_topo;\n\tif (mmg->build_mmg_mesh(mmgMesh, mmgSol, topo) == false) return false;\n\t\n\t// set the control parameters\n\tMMG3D_Set_dparameter(mmgMesh, mmgSol, MMG3D_DPARAM_hmin, m_hmin);\n\tMMG3D_Set_dparameter(mmgMesh, mmgSol, MMG3D_DPARAM_hausd, m_hausd);\n\tMMG3D_Set_dparameter(mmgMesh, mmgSol, MMG3D_DPARAM_hgrad, m_hgrad);\n\n\t// run the mesher\n\tint ier = MMG3D_mmg3dlib(mmgMesh, mmgSol);\n\n\tif (ier == MMG5_STRONGFAILURE) {\n\t\tfeLogError(\"MMG was not able to remesh the mesh.\");\n\t\treturn false;\n\t}\n\telse if (ier == MMG5_LOWFAILURE)\n\t{\n\t\tfeLogError(\"MMG return low failure error\");\n\t}\n\n\t// build the new mesh\n\tbool bret = mmg->build_new_mesh(mmgMesh, mmgSol, *GetFEModel());\n\n\t// Clean up\n\tMMG3D_Free_all(MMG5_ARG_start,\n\t\tMMG5_ARG_ppMesh, &mmgMesh, MMG5_ARG_ppMet, &mmgSol,\n\t\tMMG5_ARG_end);\n\n\treturn bret;\n\n#else\n\treturn false;\n#endif\n}\n\n#ifdef HAS_MMG\n\nbool FEMMGRemesh::MMG::build_mmg_mesh(MMG5_pMesh mmgMesh, MMG5_pSol mmgSol, FEMeshTopo& topo)\n{\n\tFEMeshAdaptorCriterion* criterion = m_mmgRemesh->GetCriterion();\n\tassert(criterion);\n\tif (criterion == nullptr) return false;\n\tFEElementSet* elset = m_mmgRemesh->GetElementSet();\n\tFEMeshAdaptorSelection elemList = criterion->GetElementSelection(elset);\n\tif (elemList.size() == 0) return false;\n\n\tFEMesh& mesh = *topo.GetMesh();\n\tint NN = mesh.Nodes();\n\tint NE = topo.Elements();\n\tint NF = topo.Faces();\n\n\t// allocate mesh size\n\tif (MMG3D_Set_meshSize(mmgMesh, NN, NE, 0, NF, 0, 0) != 1)\n\t{\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\t// set the vertex coordinates\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tFENode& vi = mesh.Node(i);\n\t\tvec3d r = vi.m_r0;\n\t\tMMG3D_Set_vertex(mmgMesh, r.x, r.y, r.z, 0, i + 1);\n\t}\n\n\t// set the tetrahedra\n\tint c = 1;\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint ne = dom.Elements();\n\t\tfor (int j = 0; j < ne; ++j, ++c)\n\t\t{\n\t\t\tFEElement& e = dom.ElementRef(j);\n\t\t\tint* n = &e.m_node[0];\n\t\t\tMMG3D_Set_tetrahedron(mmgMesh, n[0] + 1, n[1] + 1, n[2] + 1, n[3] + 1, i, c);\n\t\t}\n\t}\n\n\t// set the facet markers\n\tvector<int> faceMarker(NF, 0);\n\n\tint faceMark = 1;\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tvector<int> faceIndexList = topo.FaceIndexList(surf);\n\t\tfor (int j = 0; j < faceIndexList.size(); ++j)\n\t\t{\n\t\t\tfaceMarker[faceIndexList[j]] = faceMark;\n\t\t}\n\t\tfaceMark++;\n\t}\n\n\t// for node sets we are going to create artificial surfaces\n\tm_nodeSetTag.assign(mesh.NodeSets(), -1);\n\tfor (int i = 0; i < mesh.NodeSets(); ++i)\n\t{\n\t\tFENodeSet& nset = *mesh.NodeSet(i);\n\t\tif (nset.Size() != mesh.Nodes())\n\t\t{\n\t\t\tvector<int> nodeTags(mesh.Nodes(), 0);\n\t\t\tfor (int j = 0; j < nset.Size(); ++j) nodeTags[nset[j]] = 2;\n\n\t\t\t// see if this is indeed a surface node set\n\t\t\tfor (int j = 0; j < NF; ++j)\n\t\t\t{\n\t\t\t\tconst FEFaceList::FACE& face = topo.Face(j);\n\t\t\t\tconst int* fn = face.node;\n\t\t\t\tif ((nodeTags[fn[0]] != 0) && (nodeTags[fn[1]] != 0) && (nodeTags[fn[2]] != 0))\n\t\t\t\t{\n\t\t\t\t\tnodeTags[fn[0]] = 1;\n\t\t\t\t\tnodeTags[fn[1]] = 1;\n\t\t\t\t\tnodeTags[fn[2]] = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint twos = 0;\n\t\t\tfor (int j = 0; j < mesh.Nodes(); ++j) if (nodeTags[j] == 2) twos++;\n\n\t\t\tif (twos == 0)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j < NF; ++j)\n\t\t\t\t{\n\t\t\t\t\tconst FEFaceList::FACE& face = topo.Face(j);\n\t\t\t\t\tconst int* fn = face.node;\n\t\t\t\t\tif ((nodeTags[fn[0]] == 1) && (nodeTags[fn[1]] == 1) && (nodeTags[fn[2]] == 1))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (faceMarker[j] == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfaceMarker[j] = faceMark;\n\t\t\t\t\t\t\tm_nodeSetTag[i] = faceMark;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (m_nodeSetTag[i] == -1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tm_nodeSetTag[i] = faceMarker[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (faceMarker[j] != m_nodeSetTag[i])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfaceMark++;\n\t\t}\n\t}\n\n\t// create the faces\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tconst FEFaceList::FACE& f = topo.Face(i);\n\t\tconst int* n = &f.node[0];\n\t\tMMG3D_Set_triangle(mmgMesh, n[0] + 1, n[1] + 1, n[2] + 1, faceMarker[i], i + 1);\n\t}\n\n\t// Now, we build the \"solution\", i.e. the target element size.\n\t// If no elements are selected, we set a homogenous remeshing using the element size parameter.\n\t// set the \"solution\", i.e. desired element size\n\tif (MMG3D_Set_solSize(mmgMesh, mmgSol, MMG5_Vertex, NN, MMG5_Scalar) != 1)\n\t{\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\tif (m_metric.empty())\n\t{\n\t\t// build the edge length table\n\t\tint ET_TET[6][2] = { { 0,1 },{ 1,2 },{ 2,0 },{ 0,3 },{ 1,3 },{ 2,3 } };\n\t\tvector<pair<double, int> > edgeLength(NN, pair<double, int>(0.0, 0));\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = *topo.Element(i);\n\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t{\n\t\t\t\tint a = el.m_node[ET_TET[j][0]];\n\t\t\t\tint b = el.m_node[ET_TET[j][1]];\n\n\t\t\t\tvec3d ra = mesh.Node(a).m_r0;\n\t\t\t\tvec3d rb = mesh.Node(b).m_r0;\n\n\t\t\t\tdouble L = (ra - rb).norm2();\n\n//\t\t\t\tif (L > edgeLength[a].first) edgeLength[a].first = L;\n//\t\t\t\tif (L > edgeLength[b].first) edgeLength[b].first = L;\n\n\t\t\t\tedgeLength[a].first += L; edgeLength[a].second++;\n\t\t\t\tedgeLength[b].first += L; edgeLength[b].second++;\n\t\t\t}\n\t\t}\n\n\t\tm_metric.resize(NN, 0.0);\n\t\tfor (int i = 0; i < NN; ++i)\n\t\t{\n\t\t\tif (edgeLength[i].second != 0)\n\t\t\t{\n\t\t\t\tedgeLength[i].first /= (double)edgeLength[i].second;\n\t\t\t\tedgeLength[i].first = sqrt(edgeLength[i].first);\n\t\t\t}\n\t\t\tm_metric[i] = edgeLength[i].first;\n\t\t}\n\t}\n\n\tif (elset)\n\t{\n\t\t// elements that are not in the element set will be flagged as required.\n\t\tFEElementIterator it(&mesh);\n\t\tint c = 1;\n\t\tfor (; it.isValid(); ++it, ++c)\n\t\t{\n\t\t\tFEElement& el = *it;\n\t\t\tif (elset->Contains(el) == false)\n\t\t\t{\n\t\t\t\tMMG3D_Set_requiredTetrahedron(mmgMesh, c);\n\t\t\t}\n\t\t}\n\t}\n\n\t// scale factors\n\tvector<double> nodeScale(NN, 0.0);\n\n\t// see if want to normalize the data\n\tif (m_mmgRemesh->m_normalizeData)\n\t{\n\t\t// Find data range\n\t\tdouble vmin, vmax;\n\t\tfor (int i = 0; i < (int)elemList.size(); ++i)\n\t\t{\n\t\t\tdouble v = elemList[i].m_elemValue;\n\t\t\tif ((i == 0) || (v < vmin)) vmin = v;\n\t\t\tif ((i == 0) || (v > vmax)) vmax = v;\n\t\t}\n\t\tif (vmax == vmin) vmax++;\n\n\t\t// normalize data\n\t\tfor (int i = 0; i < (int)elemList.size(); ++i)\n\t\t{\n\t\t\tdouble v = elemList[i].m_elemValue;\n\t\t\telemList[i].m_elemValue = (v - vmin) / (vmax - vmin);\n\t\t}\n\t}\n\n\t// map to nodal data\n\tvector<int> tag(NN, 0);\n\tfor (int i = 0; i < (int)elemList.size(); ++i)\n\t{\n\t\tFEElement& el = *mesh.FindElementFromID(elemList[i].m_elementId);\n\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t{\n\t\t\tdouble s = elemList[i].m_elemValue;\n\t\t\tFEFunction1D* fs = m_mmgRemesh->m_sfunc;\n\t\t\tif (fs)\n\t\t\t{\n\t\t\t\ts = fs->value(s);\n\t\t\t}\n\t\t\tassert(s > 0.0);\n\t\t\tif (s <= 0.0) return false;\n\t\t\tnodeScale[el.m_node[j]] += s;\n\t\t\ttag[el.m_node[j]]++;\n\t\t}\n\t}\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tif (tag[i] > 0) nodeScale[i] /= (double)tag[i];\n\t\telse nodeScale[i] = (m_mmgRemesh->m_relativeSize ? 1.0 : m_metric[i]);\n\t}\n\n\t// adjust for relative scale flag\n\tif (m_mmgRemesh->m_relativeSize)\n\t{\n\t\tfor (int k = 0; k < NN; k++) {\n\t\t\tnodeScale[k] *= m_metric[k];\n\t\t}\n\t}\n\n\t// determine new size field\n\tbool meshCoarsen = m_mmgRemesh->m_meshCoarsen;\n\tfor (int k = 0; k < NN; k++) \n\t{\n\t\tdouble s = nodeScale[k];\n\t\tif ((meshCoarsen) || (s < m_metric[k])) m_metric[k] = s;\n\t}\n\n\t// set the new metric\n\tfor (int k = 0; k < NN; k++) {\n\t\tMMG3D_Set_scalarSol(mmgSol, m_metric[k], k + 1);\n\t}\n\n\treturn true;\n}\n\nbool FEMMGRemesh::MMG::build_new_mesh(MMG5_pMesh mmgMesh, MMG5_pSol mmgSol, FEModel& fem)\n{\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// get the new mesh sizes\n\tint nodes, elems, faces;\n\tMMG3D_Get_meshSize(mmgMesh, &nodes, &elems, NULL, &faces, NULL, NULL);\n\n\t// get old node positions\n\tvector<vec3d> oldNodePos(N0);\n\tfor (int i = 0; i < N0; ++i) oldNodePos[i] = mesh.Node(i).m_r0;\n\n\t// copy nodal positions\n\tvector<vec3d> nodePos0(nodes);\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tvec3d r;\n\t\tMMG3D_Get_vertex(mmgMesh, &r.x, &r.y, &r.z, NULL, NULL, NULL);\n\t\tnodePos0[i] = r;\n\t}\n\n\t// copy the metric\n\tm_metric.resize(nodes, 0.0);\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tMMG3D_Get_scalarSol(mmgSol, &m_metric[i]);\n\t}\n\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tvector<vec3d> nodePos(nodes);\n\tvector<vector<double> > nodeVal(nodes, vector<double>(MAX_DOFS, 0.0));\n\n\t// allocate data mapper\n\tFEMeshDataInterpolator* mapper = nullptr;\n\tswitch (m_mmgRemesh->m_transferMethod)\n\t{\n\tcase TRANSFER_SHAPE: mapper = new FEMeshShapeInterpolator(&mesh); break;\n\tcase TRANSFER_MLQ:\n\t{\n\t\tFELeastSquaresInterpolator* MLQ = new FELeastSquaresInterpolator;\n\t\tMLQ->SetNearestNeighborCount(m_mmgRemesh->m_nnc);\n\t\tMLQ->SetDimension(m_mmgRemesh->m_nsdim);\n\t\tMLQ->SetSourcePoints(oldNodePos);\n\t\tmapper = MLQ;\n\t}\n\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\t// map nodal positions and nodal data\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tvec3d ri = nodePos0[i];\n\n\t\tif (mapper->SetTargetPoint(ri) == false)\n\t\t{\n\t\t\tassert(false);\n\t\t\tdelete mapper;\n\t\t\tthrow std::runtime_error(\"Fatal error in MMG remesh during nodal mapping.\");\n\t\t\treturn false;\n\t\t}\n\n\t\t// get the nodal coordinates\n\t\tnodePos[i] = mapper->MapVec3d([&mesh](int sourceNode) {\n\t\t\treturn mesh.Node(sourceNode).m_rt;\n\t\t});\n\n\t\t// update values\n\t\tfor (int l = 0; l < MAX_DOFS; ++l)\n\t\t{\n\t\t\tnodeVal[i][l] = mapper->Map([&mesh, l](int sourceNode) {\n\t\t\t\treturn mesh.Node(sourceNode).get(l);\n\t\t\t});\n\t\t}\n\t}\n\tdelete mapper;\n\n\t// reallocate nodes\n\tmesh.CreateNodes(nodes);\n\n\t// assign dofs to new nodes\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.SetDOFS(MAX_DOFS);\n\t\tnode.m_r0 = nodePos0[i];\n\t\tnode.m_rt = nodePos[i];\n\t\tif (m_mmgRemesh->m_nsdim == 2) node.m_rt.z = node.m_r0.z;\n\t\tfor (int j = 0; j < node.m_ID.size(); ++j) {\n\t\t\tnode.set(j, nodeVal[i][j]);\n\t\t}\n\t\tnode.UpdateValues();\n\t}\n\n\t// recreate domains\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(mesh.Domain(i));\n\n\t\tint nelems = 0;\n\t\tfor (int j = 0; j < elems; ++j)\n\t\t{\n\t\t\tint n[4], gid, breq;\n\t\t\tMMG3D_Get_tetrahedron(mmgMesh, n, n + 1, n + 2, n + 3, &gid, &breq);\n\t\t\tif (gid == i) nelems++;\n\t\t}\n\n\t\tdom.Create(nelems, dom.GetElementSpec());\n\t\tint c = 0;\n\t\tfor (int j = 0; j < elems; ++j)\n\t\t{\n\t\t\tint n[4], gid;\n\t\t\tMMG3D_Get_tetrahedron(mmgMesh, n, n + 1, n + 2, n + 3, &gid, NULL);\n\n\t\t\tif (gid == i)\n\t\t\t{\n\t\t\t\tFESolidElement& el = dom.Element(c++);\n\t\t\t\tel.m_node[0] = n[0] - 1;\n\t\t\t\tel.m_node[1] = n[1] - 1;\n\t\t\t\tel.m_node[2] = n[2] - 1;\n\t\t\t\tel.m_node[3] = n[3] - 1;\n\t\t\t}\n\t\t}\n\n\t\t// re-init domain\n\t\tFEMaterial* mat = dom.GetMaterial();\n\t\tdom.SetMatID(mat->GetID() - 1);\n\t\tdom.CreateMaterialPointData();\n\t\tdom.Reset();\t// NOTE: we need to call this to actually call the Init function on the material points.\n\t\tdom.Init();\n\t\tdom.Activate();\n\t}\n\tmesh.RebuildLUT();\n\n\t// recreate element sets\n\tfor (int i = 0; i < mesh.ElementSets(); ++i)\n\t{\n\t\tFEElementSet& eset = mesh.ElementSet(i);\n\n\t\t// get the domain list\n\t\t// NOTE: Don't get the reference, since then the same reference\n\t\t// is passed to Create below, which causes problems.\n\t\tFEDomainList domList = eset.GetDomainList();\n\t\tif (domList.IsEmpty()) { throw std::runtime_error(\"Error in FEMMGRemesh!\"); }\n\n\t\t// recreate the element set from the domain list\n\t\teset.Create(domList);\n\t}\n\n\t// recreate surfaces\n\tint faceMark = 1;\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\n\t\t// count faces\n\t\tint nfaces = 0;\n\t\tfor (int j = 0; j < faces; ++j)\n\t\t{\n\t\t\tint n[3], gid, breq;\n\t\t\tMMG3D_Get_triangle(mmgMesh, n, n + 1, n + 2, &gid, &breq);\n\t\t\tif (gid == faceMark) nfaces++;\n\t\t}\n\t\tassert(nfaces > 0);\n\t\tsurf.Create(nfaces);\n\t\tint c = 0;\n\t\tfor (int j = 0; j < faces; ++j)\n\t\t{\n\t\t\tint n[3], gid;\n\t\t\tMMG3D_Get_triangle(mmgMesh, n, n + 1, n + 2, &gid, NULL);\n\t\t\tif (gid == faceMark)\n\t\t\t{\n\t\t\t\tFESurfaceElement& face = surf.Element(c++);\n\t\t\t\tface.SetType(FE_TRI3G3);\n\t\t\t\tface.m_node[0] = n[0] - 1;\n\t\t\t\tface.m_node[1] = n[1] - 1;\n\t\t\t\tface.m_node[2] = n[2] - 1;\n\t\t\t}\n\t\t}\n\t\tassert(c == nfaces);\n\t\tsurf.CreateMaterialPointData();\n\t\tsurf.Init();\n\n\t\t// also update the facet set if the surface has one\n\t\tFEFacetSet* fset = surf.GetFacetSet();\n\t\tif (fset)\n\t\t{\n\t\t\tfset->Create(surf);\n\t\t}\n\n\t\tfaceMark++;\n\t}\n\n\t// update nodesets\n\tfor (int i = 0; i < mesh.NodeSets(); ++i)\n\t{\n\t\tint tag = m_nodeSetTag[i];\n\t\tFENodeSet& nset = *mesh.NodeSet(i);\n\t\tif (nset.Size() != N0)\n\t\t{\n\t\t\tvector<int> nodeTags(mesh.Nodes(), 0);\n\t\t\tfor (int j = 0; j < faces; ++j)\n\t\t\t{\n\t\t\t\tint n[3], gid;\n\t\t\t\tMMG3D_Get_triangle(mmgMesh, n, n + 1, n + 2, &gid, NULL);\n\t\t\t\tif (gid == tag)\n\t\t\t\t{\n\t\t\t\t\tnodeTags[n[0] - 1] = 1;\n\t\t\t\t\tnodeTags[n[1] - 1] = 1;\n\t\t\t\t\tnodeTags[n[2] - 1] = 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tstd::vector<int> nodeList;\n\t\t\tfor (int i = 0; i < nodeTags.size(); ++i) if (nodeTags[i] == 1) nodeList.push_back(i);\n\n\t\t\tif (nodeList.size() > 0)\n\t\t\t{\n\t\t\t\tnset.Clear();\n\t\t\t\tnset.Add(nodeList);\n\t\t\t}\n\n\t\t\tfaceMark++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// assume this node set is determined by the entire mesh\n\t\t\tnset.Clear();\n\t\t\tint N = mesh.Nodes();\n\t\t\tfor (int i = 0; i < N; ++i) nset.Add(i);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n#endif\n"
  },
  {
    "path": "FEAMR/FEMMGRemesh.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <vector>\n#include \"FERefineMesh.h\"\n#include <FECore/FEFunction1D.h>\n#include <FECore/FEModelParam.h>\n\nclass FEMMGRemesh : public FERefineMesh\n{\n\tclass MMG;\n\npublic:\n\tFEMMGRemesh(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool RefineMesh() override;\n\nprivate:\n\tFEMeshAdaptorCriterion* GetCriterion() { return m_criterion; }\n\nprivate:\n\tbool\tm_relativeSize;\n\tbool\tm_meshCoarsen;\n\tbool\tm_normalizeData;\n\n\tdouble\tm_hmin;\t\t// minimum element size\n\tdouble\tm_hausd;\t// Hausdorff value\n\tdouble\tm_hgrad;\t// gradation\n\n\tFEMeshAdaptorCriterion*\tm_criterion;\n\n\tFEFunction1D*\tm_sfunc;\t// sizing function\n\n\tMMG*\tmmg;\n\n\tfriend class MMG;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEMeshDataInterpolator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEMeshDataInterpolator.h\"\n\nFEMeshDataInterpolator::FEMeshDataInterpolator()\n{\n}\n\nFEMeshDataInterpolator::~FEMeshDataInterpolator()\n{\n}\n\nbool FEMeshDataInterpolator::Init()\n{ \n\treturn true; \n}\n"
  },
  {
    "path": "FEAMR/FEMeshDataInterpolator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <vector>\n#include <functional>\n#include <FECore/matrix.h>\n\n// Base classes for classes that interpolate data on a mesh\nclass FEMeshDataInterpolator\n{\npublic:\n\tFEMeshDataInterpolator();\n\tvirtual ~FEMeshDataInterpolator();\n\n\t// do one-time initalization\n\tvirtual bool Init();\n\n\t// set the next target point\n\t// should return false if the target point cannot be evaluated\n\tvirtual bool SetTargetPoint(const vec3d& r) = 0;\n\n\t//! map source data onto target data\n\t//! output: tval - values at the target points\n\t//! input: sval - values of the source points\n\tvirtual bool Map(std::vector<double>& tval, std::function<double(int sourceNode)> src) = 0;\n\n\tvirtual double Map(int inode, std::function<double(int sourceNode)> src) = 0;\n\tvirtual vec3d MapVec3d(int inode, std::function<vec3d(int sourceNode)> src) = 0;\n\n\tdouble Map(std::function<double(int sourceNode)> f);\n\tvec3d MapVec3d(std::function<vec3d(int sourceNode)> f);\n};\n\ninline double FEMeshDataInterpolator::Map(std::function<double(int sourceNode)> f) { return Map(0, f); }\ninline vec3d FEMeshDataInterpolator::MapVec3d(std::function<vec3d(int sourceNode)> f) { return MapVec3d(0, f); }\n\n"
  },
  {
    "path": "FEAMR/FEMeshShapeInterpolator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEMeshShapeInterpolator.h\"\n#include <FECore/FEOctreeSearch.h>\n#include <FECore/FEMesh.h>\n\n//=============================================================================================\nFEMeshShapeInterpolator::FEMeshShapeInterpolator(FEMesh* mesh) : m_mesh(mesh)\n{\n\tm_os = nullptr;\n}\n\nFEMeshShapeInterpolator::~FEMeshShapeInterpolator()\n{\n\tdelete m_os;\n}\n\nbool FEMeshShapeInterpolator::Init()\n{\n\tif (m_mesh == nullptr) return false;\n\n\tif (m_os == nullptr)\n\t{\n\t\tm_os = new FEOctreeSearch(m_mesh);\n\t\tif (m_os->Init() == false) return false;\n\t}\n\n\tint nodes = m_trgPoints.size();\n\tm_data.resize(nodes);\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tData& di = m_data[i];\n\t\tvec3d ri = m_trgPoints[i];\n\n\t\t// find the element\n\t\tdi.r[0] = di.r[1] = di.r[2] = 0.0;\n\t\tdi.el = (FESolidElement*)m_os->FindElement(ri, di.r);\n\t\tif (di.el == nullptr)\n\t\t{\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid FEMeshShapeInterpolator::SetTargetPoints(const vector<vec3d>& trgPoints)\n{\n\tm_trgPoints = trgPoints;\n}\n\nbool FEMeshShapeInterpolator::SetTargetPoint(const vec3d& r)\n{\n\tm_trgPoints.clear();\n\tm_trgPoints.push_back(r);\n\treturn Init();\n}\n\nbool FEMeshShapeInterpolator::Map(std::vector<double>& tval, function<double(int sourceNode)> src)\n{\n\t// update solution\n\tint nodes = m_trgPoints.size();\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\tData& di = m_data[i];\n\n\t\t// update values\n\t\tdouble v[FEElement::MAX_NODES] = { 0 };\n\t\tfor (int j = 0; j < di.el->Nodes(); ++j) v[j] = src(di.el->m_node[j]);\n\t\tdouble vl = di.el->evaluate(v, di.r[0], di.r[1], di.r[2]);\n\t\ttval[i] = vl;\n\t}\n\n\treturn true;\n}\n\ndouble FEMeshShapeInterpolator::Map(int inode, function<double(int sourceNode)> f)\n{\n\tData& di = m_data[inode];\n\tdouble v[FEElement::MAX_NODES];\n\tfor (int j = 0; j < di.el->Nodes(); ++j) v[j] = f(di.el->m_node[j]);\n\tdouble vl = di.el->evaluate(v, di.r[0], di.r[1], di.r[2]);\n\treturn vl;\n}\n\nvec3d FEMeshShapeInterpolator::MapVec3d(int inode, function<vec3d(int sourceNode)> f)\n{\n\tData& di = m_data[inode];\n\tvec3d v[FEElement::MAX_NODES];\n\tfor (int j = 0; j < di.el->Nodes(); ++j) v[j] = f(di.el->m_node[j]);\n\tvec3d vl = di.el->evaluate(v, di.r[0], di.r[1], di.r[2]);\n\treturn vl;\n}\n"
  },
  {
    "path": "FEAMR/FEMeshShapeInterpolator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEMeshDataInterpolator.h\"\n\n//=======================================================================================\nclass FEMesh;\nclass FEOctreeSearch;\nclass FESolidElement;\n\n//! Interpolates data by using the element shape functions\nclass FEMeshShapeInterpolator : public FEMeshDataInterpolator\n{\n\tstruct Data\n\t{\n\t\tFESolidElement*\t\tel;\n\t\tdouble\t\t\t\tr[3];\n\t};\n\npublic:\n\tFEMeshShapeInterpolator(FEMesh* mesh);\n\t~FEMeshShapeInterpolator();\n\n\tbool Init() override;\n\n\tvoid SetTargetPoints(const std::vector<vec3d>& trgPoints);\n\tbool SetTargetPoint(const vec3d& r) override;\n\n\tbool Map(std::vector<double>& tval, std::function<double(int sourceNode)> src) override;\n\n\tdouble Map(int inode, std::function<double(int sourceNode)> src) override;\n\tvec3d MapVec3d(int inode, std::function<vec3d(int sourceNode)> src) override;\n\nprivate:\n\tFEMesh*\tm_mesh;\n\tFEOctreeSearch*\tm_os;\n\tstd::vector<vec3d>\tm_trgPoints;\n\tstd::vector<Data>\tm_data;\n};\n"
  },
  {
    "path": "FEAMR/FERefineMesh.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERefineMesh.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEEdgeList.h>\n#include <FECore/FEElementList.h>\n#include <FECore/FEFaceList.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEMeshTopo.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FESurfacePairConstraint.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FEDomainMap.h>\n#include <FECore/FESurfaceMap.h>\n#include <FECore/DumpMemStream.h>\n#include <FECore/log.h>\n#include \"FELeastSquaresInterpolator.h\"\n#include \"FEMeshShapeInterpolator.h\"\n#include \"FEDomainShapeInterpolator.h\"\n\nBEGIN_FECORE_CLASS(FERefineMesh, FEMeshAdaptor)\n\tADD_PARAMETER(m_maxiter, \"max_iters\");\n\tADD_PARAMETER(m_maxelem, \"max_elements\");\n\tADD_PARAMETER(m_bmap_data, \"map_data\");\n\tADD_PARAMETER(m_nnc      , \"nnc\");\n\tADD_PARAMETER(m_nsdim  , \"nsdim\");\n\tADD_PARAMETER(m_transferMethod, \"transfer_method\");\nEND_FECORE_CLASS();\n\nFERefineMesh::FERefineMesh(FEModel* fem) : FEMeshAdaptor(fem), m_topo(nullptr)\n{\n\tm_meshCopy = nullptr;\n\tm_bmap_data = false;\n\tm_transferMethod = TRANSFER_SHAPE;\n\tm_nnc = 8;\n\tm_nsdim = 3;\n\n\tm_maxiter = -1;\n\tm_maxelem = -1;\n}\n\nFERefineMesh::~FERefineMesh()\n{\n\tif (m_meshCopy) delete m_meshCopy;\n\tm_meshCopy = nullptr;\n\n\tClearMapData();\n}\n\n// Apply mesh refinement\nbool FERefineMesh::Apply(int iteration)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// check for max iterations\n\tif ((m_maxiter > 0) && (iteration >= m_maxiter))\n\t{\n\t\tfeLog(\"Skipping refinement: Max iterations reached.\");\n\t\treturn false;\n\t}\n\n\t// see if we reached max elements\n\tif ((m_maxelem > 0) && (mesh.Elements() >= m_maxelem))\n\t{\n\t\tfeLog(\"Skipping refinement: Element limit reached.\\n\");\n\t\treturn false;\n\t}\n\n\t// build the mesh-topo\n\tif (BuildMeshTopo() == false)\n\t{\n\t\tthrow std::runtime_error(\"Error building topo structure.\");\n\t}\n\n\t// build data maps\n\tfeLog(\"-- Building map data:\\n\");\n\tif (BuildMapData() == false)\n\t{\n\t\tthrow std::runtime_error(\"Failed mapping data.\");\n\t}\n\n\t// refine the mesh (This is done by sub-classes)\n\tfeLog(\"-- Starting Mesh refinement.\\n\");\n\tif (RefineMesh() == false)\n\t{\n\t\tfeLog(\"Nothing to refine.\");\n\t\treturn false;\n\t}\n\tfeLog(\"-- Mesh refinement completed.\\n\");\n\n\t// map data to new mesh\n\tfeLog(\"-- Transferring map data to new mesh:\\n\");\n\tTransferMapData();\n\n\t// update the model\n\tUpdateModel();\n\n\t// print some mesh statistics\n\tint NN = mesh.Nodes();\n\tint NE = mesh.Elements();\n\tfeLog(\"\\n Mesh Statistics:\\n\");\n\tfeLog(\" \\tNumber of nodes    : %d\\n\", NN);\n\tfeLog(\" \\tNumber of elements : %d\\n\", NE);\n\tfeLog(\"\\n\");\n\n\t// all done!\n\treturn true;\n}\n\nvoid FERefineMesh::ClearMapData()\n{\n\t// clear domain maps\n\tfor (size_t i = 0; i < m_domainMapList.size(); ++i)\n\t{\n\t\tstd::vector<FEDomainMap*>& map_i = m_domainMapList[i];\n\t\tfor (size_t j = 0; j < map_i.size(); ++j) delete map_i[j];\n\t}\n\tm_domainMapList.clear();\n\n\t// clear user maps\n\tfor (int i = 0; i < m_userDataList.size(); ++i) delete m_userDataList[i];\n\tm_userDataList.clear();\n}\n\nbool FERefineMesh::BuildMeshTopo()\n{\n\tFEModel& fem = *GetFEModel();\n\tif (m_topo) { delete m_topo; m_topo = nullptr; }\n\tm_topo = new FEMeshTopo;\n\treturn m_topo->Create(&fem.GetMesh());\n}\n\nvoid FERefineMesh::CopyMesh()\n{\n\tif (m_meshCopy) delete m_meshCopy;\n\n\tm_meshCopy = new FEMesh(nullptr);\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tm_meshCopy->CopyFrom(mesh);\n}\n\nFEDomainMap* createElemDataMap(FEModel& fem, FEDomain& dom, vector<vec3d>& nodePos, FEDomainMap* map, FEMeshDataInterpolator* dataMapper)\n{\n\tassert(map->StorageFormat() == Storage_Fmt::FMT_NODE);\n\n\tFEDataType dataType = map->DataType();\n\tint dataSize = 0;\n\tswitch (dataType)\n\t{\n\tcase FEDataType::FE_DOUBLE: dataSize = 1; break;\n\tcase FEDataType::FE_VEC3D: dataSize = 3; break;\n\tcase FEDataType::FE_MAT3D: dataSize = 9; break;\n\tcase FEDataType::FE_MAT3DS: dataSize = 6; break;\n\tdefault:\n\t\tassert(false);\n\t\treturn nullptr;\n\t}\n\n\t// count nr of integration points\n\tint NMP = 0;\n\tfor (int i = 0; i < dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tNMP += el.GaussPoints();\n\t}\n\n\tint N0 = nodePos.size();\n\n\t// create new domain map\n\tFEDomainMap* elemData = new FEDomainMap(map->DataType(), Storage_Fmt::FMT_MATPOINTS);\n\tFEElementSet* eset = new FEElementSet(&fem);\n\teset->Create(&dom);\n\telemData->Create(eset);\n\n\tvector<double> srcData(N0);\n\tvector<double> trgData(NMP);\n\n\tvector< vector<double> > mappedData(NMP, vector<double>(9, 0.0));\n\n\t// loop over all the new nodes\n\tfor (int l = 0; l < dataSize; ++l)\n\t{\n\t\tfor (int i = 0; i < N0; ++i)\n\t\t{\n\t\t\tdouble vm = 0.0;\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FEDataType::FE_DOUBLE: vm = map->value<double>(0, i); break;\n\t\t\tcase FEDataType::FE_VEC3D:\n\t\t\t{\n\t\t\t\tvec3d v = map->value<vec3d>(0, i);\n\t\t\t\tif (l == 0) vm = v.x;\n\t\t\t\tif (l == 1) vm = v.y;\n\t\t\t\tif (l == 2) vm = v.z;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FEDataType::FE_MAT3D:\n\t\t\t{\n\t\t\t\tint LUT[9][2] = { {0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {1,2}, {2,0}, {2,1}, {2,2} };\n\t\t\t\tmat3d v = map->value<mat3d>(0, i);\n\t\t\t\tvm = v(LUT[l][0], LUT[l][1]);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FEDataType::FE_MAT3DS:\n\t\t\t{\n\t\t\t\tint LUT[6][2] = { {0,0}, {0,1}, {0,2}, {1,1}, {1,2}, {2,2} };\n\t\t\t\tmat3ds v = map->value<mat3ds>(0, i);\n\t\t\t\tvm = v(LUT[l][0], LUT[l][1]);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t\tsrcData[i] = vm;\n\t\t}\n\n\t\tdataMapper->Map(trgData, [&srcData](int sourcePoint) {\n\t\t\treturn srcData[sourcePoint];\n\t\t});\n\n\t\tfor (int i = 0; i < NMP; ++i)\n\t\t{\n\t\t\tmappedData[i][l] = trgData[i];\n\t\t}\n\t}\n\n\t// write mapped data to domain map\n\tint n = 0;\n\tfor (int i = 0; i < dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\tvector<double>& vj = mappedData[n++];\n\n\t\t\tfor (int l = 0; l < dataSize; ++l)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FEDataType::FE_DOUBLE: elemData->setValue(i, j, vj[0]); break;\n\t\t\t\tcase FEDataType::FE_VEC3D:\n\t\t\t\t{\n\t\t\t\t\tvec3d v;\n\t\t\t\t\tv.x = vj[0];\n\t\t\t\t\tv.y = vj[1];\n\t\t\t\t\tv.z = vj[2];\n\t\t\t\t\telemData->setValue(i, j, v);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tcase FEDataType::FE_MAT3D:\n\t\t\t\t{\n\t\t\t\t\tmat3d v;\n\t\t\t\t\tv(0, 0) = vj[0]; v(0, 1) = vj[1]; v(0, 2) = vj[2];\n\t\t\t\t\tv(1, 0) = vj[3]; v(1, 1) = vj[4]; v(1, 2) = vj[5];\n\t\t\t\t\tv(2, 0) = vj[6]; v(2, 1) = vj[7]; v(2, 2) = vj[8];\n\t\t\t\t\telemData->setValue(i, j, v);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tcase FEDataType::FE_MAT3DS:\n\t\t\t\t{\n\t\t\t\t\tmat3ds v;\n\t\t\t\t\tv(0, 0) = vj[0];\n\t\t\t\t\tv(0, 1) = vj[1];\n\t\t\t\t\tv(0, 2) = vj[2];\n\t\t\t\t\tv(1, 1) = vj[3];\n\t\t\t\t\tv(1, 2) = vj[4];\n\t\t\t\t\tv(2, 2) = vj[5];\n\t\t\t\t\telemData->setValue(i, j, v);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn elemData;\n}\n\nbool createNodeDataMap(FEDomain& dom, FEDomainMap* map, FEDomainMap* nodeMap)\n{\n\tFEDataType dataType = map->DataType();\n\tint dataSize = 0;\n\tswitch (dataType)\n\t{\n\tcase FEDataType::FE_DOUBLE: dataSize = 1; break;\n\tcase FEDataType::FE_VEC3D: dataSize = 3; break;\n\tcase FEDataType::FE_MAT3D: dataSize = 9; break;\n\tcase FEDataType::FE_MAT3DS: dataSize = 6; break;\n\tdefault:\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\t// temp storage \n\tdouble si[FEElement::MAX_INTPOINTS * 9];\n\tdouble sn[FEElement::MAX_NODES * 9];\n\n\t// allocate node data\n\tint NN = dom.Nodes();\n\tvector<double> nodeData(NN*dataSize);\n\n\t// build tag list\n\tvector<int> tag(NN, 0);\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint ne = e.Nodes();\n\t\tfor (int k = 0; k < ne; ++k)\n\t\t{\n\t\t\ttag[e.m_lnode[k]]++;\n\t\t}\n\t}\n\n\t// get the data format\n\tint dataFormat = map->StorageFormat();\n\tif ((dataFormat != FMT_MATPOINTS) && (dataFormat != FMT_MULT)) return false;\n\n\t// loop over all elements\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint ne = e.Nodes();\n\n\t\tint ni = (dataFormat == FMT_MATPOINTS ? e.GaussPoints() : ne);\n\n\t\tfor (int j = 0; j < dataSize; ++j)\n\t\t{\n\t\t\t// get the integration point values\n\t\t\tfor (int k = 0; k < ni; ++k)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FEDataType::FE_DOUBLE:\n\t\t\t\t\tsi[k] = map->value<double>(i, k);\n\t\t\t\t\tbreak;\n\t\t\t\tcase FEDataType::FE_VEC3D:\n\t\t\t\t{\n\t\t\t\t\tvec3d v = map->value<vec3d>(i, k);\n\t\t\t\t\tif (j == 0) si[k] = v.x;\n\t\t\t\t\tif (j == 1) si[k] = v.y;\n\t\t\t\t\tif (j == 2) si[k] = v.z;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tcase FEDataType::FE_MAT3D:\n\t\t\t\t{\n\t\t\t\t\tmat3d v = map->value<mat3d>(i, k);\n\t\t\t\t\tint LUT[9][2] = { {0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {1,2}, {2,0}, {2,1}, {2,2} };\n\t\t\t\t\tsi[k] = v(LUT[j][0], LUT[j][1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tcase FEDataType::FE_MAT3DS:\n\t\t\t\t{\n\t\t\t\t\tmat3ds v = map->value<mat3ds>(i, k);\n\t\t\t\t\tint LUT[6][2] = { {0,0}, {0,1}, {0,2}, {1,1}, {1,2}, {2,2} };\n\t\t\t\t\tsi[k] = v(LUT[j][0], LUT[j][1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// project to nodes\n\t\t\tif (dataFormat == FMT_MATPOINTS)\n\t\t\t{\n\t\t\t\te.project_to_nodes(si, sn);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (int k = 0; k < ne; ++k) sn[k] = si[k];\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < ne; ++k)\n\t\t\t{\n\t\t\t\tnodeData[e.m_lnode[k] * dataSize + j] += sn[k];\n\t\t\t}\n\t\t}\n\t}\n\n\t// normalize data\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tif (tag[i] > 0)\n\t\t{\n\t\t\tfor (int j = 0; j < dataSize; ++j)\n\t\t\t\tnodeData[i*dataSize + j] /= (double)tag[i];\n\t\t}\n\t}\n\n\t// check node data map\n\tif (nodeMap->StorageFormat() != Storage_Fmt::FMT_NODE) return false;\n\tif (nodeMap->DataType() != dataType) return false;\n\tif (nodeMap->DataCount() != NN) return false;\n\n\t// assign data\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tswitch (dataType)\n\t\t{\n\t\tcase FEDataType::FE_DOUBLE: nodeMap->setValue(i, nodeData[i]); break;\n\t\tcase FEDataType::FE_VEC3D:\n\t\t{\n\t\t\tvec3d v;\n\t\t\tv.x = nodeData[i*dataSize];\n\t\t\tv.y = nodeData[i*dataSize + 1];\n\t\t\tv.z = nodeData[i*dataSize + 2];\n\t\t\tnodeMap->setValue(i, v);\n\t\t}\n\t\tbreak;\n\t\tcase FEDataType::FE_MAT3D:\n\t\t{\n\t\t\tmat3d v;\n\t\t\tv(0, 0) = nodeData[i*dataSize]; v(0, 1) = nodeData[i*dataSize + 1]; v(0, 2) = nodeData[i*dataSize + 2];\n\t\t\tv(1, 0) = nodeData[i*dataSize + 3]; v(1, 1) = nodeData[i*dataSize + 4]; v(1, 2) = nodeData[i*dataSize + 5];\n\t\t\tv(2, 0) = nodeData[i*dataSize + 6]; v(2, 1) = nodeData[i*dataSize + 7]; v(2, 2) = nodeData[i*dataSize + 8];\n\t\t\tnodeMap->setValue(i, v);\n\t\t}\n\t\tbreak;\n\t\tcase FEDataType::FE_MAT3DS:\n\t\t{\n\t\t\tmat3ds v;\n\t\t\tv(0, 0) = nodeData[i*dataSize];\n\t\t\tv(0, 1) = nodeData[i*dataSize + 1];\n\t\t\tv(0, 2) = nodeData[i*dataSize + 2];\n\t\t\tv(1, 1) = nodeData[i*dataSize + 3];\n\t\t\tv(1, 2) = nodeData[i*dataSize + 4];\n\t\t\tv(2, 2) = nodeData[i*dataSize + 5];\n\t\t\tnodeMap->setValue(i, v);\n\t\t}\n\t\tbreak;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid NodeToElemData(FEModel& fem, FEDomain& dom, FEDomainMap* nodeMap, FEDomainMap* elemMap, FEMeshDataInterpolator* dataMapper)\n{\n\tassert(nodeMap->StorageFormat() == Storage_Fmt::FMT_NODE);\n\n\tFEDataType dataType = nodeMap->DataType();\n\tint dataSize = 0;\n\tswitch (dataType)\n\t{\n\tcase FEDataType::FE_DOUBLE: dataSize = 1; break;\n\tcase FEDataType::FE_VEC3D: dataSize = 3; break;\n\tcase FEDataType::FE_MAT3D: dataSize = 9; break;\n\tcase FEDataType::FE_MAT3DS: dataSize = 6; break;\n\tdefault:\n\t\tassert(false);\n\t\tthrow std::runtime_error(\"Error in FEMMGRemesh::MMG::NodeToElemData\");\n\t\treturn;\n\t}\n\n\t// count nr of points\n\tint NN = nodeMap->DataCount();\n\n\t// count nr of target points\n\tint NE = dom.Elements();\n\tint NP = 0;\n\tfor (int i = 0; i < dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tNP += el.Nodes();\n\t}\n\n\tvector<double> srcData(NN);\n\tvector<double> trgData(NP);\n\n\tvector< vector<double> > mappedData(NP, vector<double>(9, 0.0));\n\n\t// loop over all the new nodes\n\tfor (int l = 0; l < dataSize; ++l)\n\t{\n\t\tfor (int i = 0; i < NN; ++i)\n\t\t{\n\t\t\tdouble vm = 0.0;\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FEDataType::FE_DOUBLE: vm = nodeMap->value<double>(0, i); break;\n\t\t\tcase FEDataType::FE_VEC3D:\n\t\t\t{\n\t\t\t\tvec3d v = nodeMap->value<vec3d>(0, i);\n\t\t\t\tif (l == 0) vm = v.x;\n\t\t\t\tif (l == 1) vm = v.y;\n\t\t\t\tif (l == 2) vm = v.z;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FEDataType::FE_MAT3D:\n\t\t\t{\n\t\t\t\tint LUT[9][2] = { {0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {1,2}, {2,0}, {2,1}, {2,2} };\n\t\t\t\tmat3d v = nodeMap->value<mat3d>(0, i);\n\t\t\t\tvm = v(LUT[l][0], LUT[l][1]);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FEDataType::FE_MAT3DS:\n\t\t\t{\n\t\t\t\tint LUT[6][2] = { {0,0}, {0,1}, {0,2}, {1,1}, {1,2}, {2,2} };\n\t\t\t\tmat3ds v = nodeMap->value<mat3ds>(0, i);\n\t\t\t\tvm = v(LUT[l][0], LUT[l][1]);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t\tsrcData[i] = vm;\n\t\t}\n\n\t\tdataMapper->Map(trgData, [&srcData](int sourcePoint) {\n\t\t\treturn srcData[sourcePoint];\n\t\t});\n\n\t\tfor (int i = 0; i < NP; ++i)\n\t\t{\n\t\t\tmappedData[i][l] = trgData[i];\n\t\t}\n\t}\n\n\t// create new element set\n\tFEElementSet* eset = new FEElementSet(&fem);\n\teset->Create(&dom);\n\telemMap->Create(eset);\n\n\t// write mapped data to domain map\n\tint n = 0;\n\tfor (int i = 0; i < dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tfor (int k = 0; k < el.Nodes(); ++k)\n\t\t{\n\t\t\tvector<double>& vj = mappedData[n++];\n\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FEDataType::FE_DOUBLE: elemMap->setValue(i, k, vj[0]); break;\n\t\t\tcase FEDataType::FE_VEC3D:\n\t\t\t{\n\t\t\t\tvec3d v;\n\t\t\t\tv.x = vj[0];\n\t\t\t\tv.y = vj[1];\n\t\t\t\tv.z = vj[2];\n\t\t\t\telemMap->setValue(i, k, v);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FEDataType::FE_MAT3D:\n\t\t\t{\n\t\t\t\tmat3d v;\n\t\t\t\tv(0, 0) = vj[0]; v(0, 1) = vj[1]; v(0, 2) = vj[2];\n\t\t\t\tv(1, 0) = vj[3]; v(1, 1) = vj[4]; v(1, 2) = vj[5];\n\t\t\t\tv(2, 0) = vj[6]; v(2, 1) = vj[7]; v(2, 2) = vj[8];\n\t\t\t\telemMap->setValue(i, k, v);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FEDataType::FE_MAT3DS:\n\t\t\t{\n\t\t\t\tmat3ds v;\n\t\t\t\tv(0, 0) = vj[0];\n\t\t\t\tv(0, 1) = vj[1];\n\t\t\t\tv(0, 2) = vj[2];\n\t\t\t\tv(1, 1) = vj[3];\n\t\t\t\tv(1, 2) = vj[4];\n\t\t\t\tv(2, 2) = vj[5];\n\t\t\t\telemMap->setValue(i, k, v);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool FERefineMesh::BuildMapData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// make a copy of the old mesh\n\t// we need it for mapping data\n\tCopyMesh();\n\n\t// clear all map data\n\tClearMapData();\n\tm_domainMapList.clear();\n\tm_domainMapList.resize(mesh.Domains());\n\n\t// only map domain data if requested\n\tif (m_bmap_data)\n\t{\n\t\tif (BuildDomainMapData() == false)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// do the same thing for the user-defined mesh data\n\treturn BuildUserMapData();\n}\n\nbool FERefineMesh::BuildDomainMapData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// loop over all domains\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\n\t\tfeLog(\" Processing domain: %s\\n\", dom.GetName().c_str());\n\n\t\tif (BuildDomainMapData(dom, i) == false)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool FERefineMesh::BuildDomainMapData(FEDomain& dom, int domIndex)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// write all material point data to a data stream\n\tDumpMemStream ar(fem);\n\tar.Open(true, true);\n\tar.WriteTypeInfo(true);\n\n\t// loop over all integration points\n\tint totalPoints = 0;\n\tfor (int j = 0; j < dom.Elements(); ++j)\n\t{\n\t\tFEElement& el = dom.ElementRef(j);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int k = 0; k < nint; ++k)\n\t\t{\n\t\t\tFEMaterialPoint* mp = el.GetMaterialPoint(k);\n\t\t\tmp->Serialize(ar);\n\t\t}\n\t\ttotalPoints += nint;\n\t}\n\n\t// figure out how much data was written for each material point\n\tsize_t bytes = ar.bytesSerialized();\n\n\tsize_t bytesPerPoint = bytes / totalPoints;\n\tassert((bytes%totalPoints) == 0);\n\n\t// re-open for reading\n\tar.Open(false, true);\n\n\t// create an element set (we need this for the domain map below)\n\tFEDomain& oldDomain = m_meshCopy->Domain(domIndex);\n\tFEElementSet* elemSet = new FEElementSet(&fem);\n\telemSet->Create(&oldDomain);\n\n\t// next, we need to figure out the datamaps for each data item\n\tvector<FEDomainMap*> mapList;\n\tDumpStream::DataBlock d;\n\twhile (ar.bytesSerialized() < bytesPerPoint)\n\t{\n\t\tar.readBlock(d);\n\n\t\tconst char* typeStr = nullptr;\n\t\tFEDomainMap* map = nullptr;\n\t\tswitch (d.dataType())\n\t\t{\n\t\tcase TypeID::TYPE_DOUBLE: map = new FEDomainMap(FEDataType::FE_DOUBLE, Storage_Fmt::FMT_MATPOINTS); typeStr = \"double\"; break;\n\t\tcase TypeID::TYPE_VEC3D: map = new FEDomainMap(FEDataType::FE_VEC3D, Storage_Fmt::FMT_MATPOINTS); typeStr = \"vec3d\"; break;\n\t\tcase TypeID::TYPE_MAT3D: map = new FEDomainMap(FEDataType::FE_MAT3D, Storage_Fmt::FMT_MATPOINTS); typeStr = \"mat3d\"; break;\n\t\tcase TypeID::TYPE_MAT3DS: map = new FEDomainMap(FEDataType::FE_MAT3DS, Storage_Fmt::FMT_MATPOINTS); typeStr = \"mat3ds\"; break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\tthrow std::runtime_error(\"Error in mapping data.\");\n\t\t}\n\n\t\tmap->Create(elemSet, 0.0);\n\n\t\tfeLog(\"\\tData map %d: %s\\n\", mapList.size(), typeStr);\n\n\t\tmapList.push_back(map);\n\t}\n\n\tfeLog(\" %d data maps identified.\\n\", mapList.size());\n\n\t// rewind for processing\n\tar.Open(false, true);\n\n\tfor (int j = 0; j < dom.Elements(); ++j)\n\t{\n\t\tFEElement& el = dom.ElementRef(j);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int k = 0; k < nint; ++k)\n\t\t{\n\t\t\tint m = 0;\n\t\t\tsize_t bytesRead = 0;\n\t\t\twhile (bytesRead < bytesPerPoint)\n\t\t\t{\n\t\t\t\tsize_t size0 = ar.bytesSerialized();\n\t\t\t\tbool b = ar.readBlock(d); assert(b);\n\t\t\t\tsize_t size1 = ar.bytesSerialized();\n\t\t\t\tbytesRead += size1 - size0;\n\n\t\t\t\tFEDomainMap* map = mapList[m];\n\n\t\t\t\tswitch (d.dataType())\n\t\t\t\t{\n\t\t\t\tcase TypeID::TYPE_DOUBLE: { double v = d.value<double>(); map->setValue(j, k, v); } break;\n\t\t\t\tcase TypeID::TYPE_MAT3D: { mat3d  v = d.value<mat3d >(); map->setValue(j, k, v); } break;\n\t\t\t\tcase TypeID::TYPE_MAT3DS: { mat3ds v = d.value<mat3ds>(); map->setValue(j, k, v); } break;\n\t\t\t\t}\n\n\t\t\t\tm++;\n\t\t\t\tassert(m <= mapList.size());\n\t\t\t}\n\t\t}\n\t}\n\n\t// Now, we need to project all the data onto the nodes\n\tfor (int j = 0; j < mapList.size(); ++j)\n\t{\n\t\tfeLog(\"\\tProcessing data map %d ...\", j);\n\t\tFEDomainMap* elemMap = mapList[j];\n\n\t\tFEDataType dataType = elemMap->DataType();\n\t\tFEDomainMap* nodeMap = new FEDomainMap(dataType, Storage_Fmt::FMT_NODE);\n\n\t\tFEElementSet* elset = const_cast<FEElementSet*>(elemMap->GetElementSet());\n\t\tnodeMap->Create(elset);\n\n\t\tbool bret = createNodeDataMap(dom, elemMap, nodeMap); assert(bret);\n\t\tm_domainMapList[domIndex].push_back(nodeMap);\n\t\tfeLog(\"done.\\n\");\n\t}\n    \n    return true;\n}\n\nbool FERefineMesh::BuildUserMapData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tm_userDataList.clear();\n\tint dataMaps = mesh.DataMaps();\n\tif (dataMaps > 0) feLog(\" Processing user data maps:\\n\");\n\n\tfor (int i = 0; i < dataMaps; ++i)\n\t{\n\t\tFEDataMap* dataMap = mesh.GetDataMap(i);\n\n\t\t// process domain map\n\t\tFEDomainMap* dmap = dynamic_cast<FEDomainMap*>(dataMap);\n\t\tif (dmap)\n\t\t{\n\t\t\tFEDataType dataType = dmap->DataType();\n\t\t\tif ((dataType != FEDataType::FE_DOUBLE) && \n\t\t\t\t(dataType != FEDataType::FE_VEC3D)) return false;\n\n\t\t\tfeLog(\"\\tProcessing user data map \\\"%s\\\" ...\", dmap->GetName().c_str());\n\n\t\t\tconst FEElementSet* elset = dmap->GetElementSet();\n\t\t\tconst FEDomainList& domainList = elset->GetDomainList();\n\t\t\tif (domainList.Domains() != 1) return false;\n\t\t\tFEDomain& dom = const_cast<FEDomain&>(*domainList.GetDomain(0));\n\n\t\t\tFEElementSet* oldElemSet = m_meshCopy->FindElementSet(dom.GetName()); assert(oldElemSet);\n\n\t\t\tFEDomainMap* nodeMap = new FEDomainMap(dataType, Storage_Fmt::FMT_NODE);\n\t\t\tnodeMap->Create(oldElemSet);\n\n\t\t\t// create a node data map of this domain map\n\t\t\tcreateNodeDataMap(dom, dmap, nodeMap);\n\t\t\tm_userDataList.push_back(nodeMap);\n\n\t\t\tfeLog(\"done.\\n\");\n\t\t}\n\n\t\tFESurfaceMap* smap = dynamic_cast<FESurfaceMap*>(dataMap);\n\t\tif (smap)\n\t\t{\n\t\t\tassert(false);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n// Transfer data to new mesh\nvoid FERefineMesh::TransferMapData()\n{\n\t// transfer domain data\n\tif (m_bmap_data) TransferDomainMapData();\n\n\t// transfer user-defined maps\n\tTransferUserMapData();\n}\n\n// Transfer domain data back to the new mesh\nvoid FERefineMesh::TransferDomainMapData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// loop over all domains\n\tif (m_bmap_data && m_domainMapList.size())\n\t{\n\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\n\t\t\tstd::vector<FEDomainMap*>& nodeMap_i = m_domainMapList[i];\n\t\t\tint mapCount = nodeMap_i.size();\n\n\t\t\tif (mapCount > 0)\n\t\t\t{\n\t\t\t\tfeLog(\" Mapping data for domain \\\"%s\\\":\\n\", dom.GetName().c_str());\n\n\t\t\t\t// we need an element set for the domain maps below\n\t\t\t\tFEElementSet* elemSet = new FEElementSet(&fem);\n\t\t\t\telemSet->Create(&dom);\n\n\t\t\t\t// build source point list\n\t\t\t\tFEDomain& oldDomain = m_meshCopy->Domain(i);\n\t\t\t\tvector<vec3d> srcPoints; srcPoints.reserve(oldDomain.Nodes());\n\t\t\t\tfor (int i = 0; i < oldDomain.Nodes(); ++i)\n\t\t\t\t{\n\t\t\t\t\tvec3d r = oldDomain.Node(i).m_r0;\n\t\t\t\t\tsrcPoints.push_back(r);\n\t\t\t\t}\n\n\t\t\t\t// build target node list\n\t\t\t\tvector<vec3d> trgPoints; trgPoints.reserve(dom.Elements());\n\t\t\t\tfor (int i = 0; i < dom.Elements(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\t\t\tvec3d r = mp.m_r0;\n\t\t\t\t\t\ttrgPoints.push_back(r);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// set up mapper\n\t\t\t\tFEMeshDataInterpolator* mapper = nullptr;\n\t\t\t\tswitch (m_transferMethod)\n\t\t\t\t{\n\t\t\t\tcase TRANSFER_SHAPE:\n\t\t\t\t{\n\t\t\t\t\tFEDomain* oldDomain = &m_meshCopy->Domain(i);\n\t\t\t\t\tFEDomainShapeInterpolator* dsm = new FEDomainShapeInterpolator(oldDomain);\n\t\t\t\t\tdsm->SetTargetPoints(trgPoints);\n\t\t\t\t\tmapper = dsm;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tcase TRANSFER_MLQ:\n\t\t\t\t{\n\t\t\t\t\tFELeastSquaresInterpolator* MLQ = new FELeastSquaresInterpolator;\n\t\t\t\t\tMLQ->SetNearestNeighborCount(m_nnc);\n\t\t\t\t\tMLQ->SetDimension(m_nsdim);\n\t\t\t\t\tMLQ->SetSourcePoints(srcPoints);\n\t\t\t\t\tMLQ->SetTargetPoints(trgPoints);\n\t\t\t\t\tmapper = MLQ;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (mapper->Init() == false)\n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t\tthrow std::runtime_error(\"Failed to initialize LLQ\");\n\t\t\t\t}\n\n\t\t\t\t// loop over all the domain maps\n\t\t\t\tvector<FEDomainMap*> elemMapList(mapCount);\n\t\t\t\tfor (int j = 0; j < mapCount; ++j)\n\t\t\t\t{\n\t\t\t\t\tfeLog(\"\\tMapping map %d ...\", j);\n\t\t\t\t\tFEDomainMap* nodeMap = nodeMap_i[j];\n\n\t\t\t\t\t// map node data to integration points\n\t\t\t\t\tFEDomainMap* elemMap = createElemDataMap(fem, dom, srcPoints, nodeMap, mapper);\n\n\t\t\t\t\telemMapList[j] = elemMap;\n\t\t\t\t\tfeLog(\"done.\\n\");\n\t\t\t\t}\n\n\t\t\t\t// now we need to reconstruct the data stream\n\t\t\t\tDumpMemStream ar(fem);\n\t\t\t\tar.Open(true, true);\n\n\t\t\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\t\tint nint = el.GaussPoints();\n\n\t\t\t\t\tfor (int k = 0; k < nint; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int l = 0; l < mapCount; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEDomainMap* map = elemMapList[l];\n\t\t\t\t\t\t\tswitch (map->DataType())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase FEDataType::FE_DOUBLE: { double v = map->value<double>(j, k); ar << v; } break;\n\t\t\t\t\t\t\tcase FEDataType::FE_VEC3D: { vec3d  v = map->value<vec3d >(j, k); ar << v; } break;\n\t\t\t\t\t\t\tcase FEDataType::FE_MAT3D: { mat3d  v = map->value<mat3d >(j, k); ar << v; } break;\n\t\t\t\t\t\t\tcase FEDataType::FE_MAT3DS: { mat3ds v = map->value<mat3ds>(j, k); ar << v; } break;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tassert(false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// time to serialize everything back to the new integration points\n\t\t\t\tar.Open(false, true);\n\t\t\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\t\tint nint = el.GaussPoints();\n\n\t\t\t\t\tfor (int k = 0; k < nint; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(k);\n\t\t\t\t\t\tmp.Serialize(ar);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Transfer user data maps to new mesh\nvoid FERefineMesh::TransferUserMapData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// map mesh data\n\tfor (int i = 0; i < m_userDataList.size(); ++i)\n\t{\n\t\tFEDomainMap* elemMap = dynamic_cast<FEDomainMap*>(mesh.GetDataMap(i));\n\t\tFEDomainMap* nodeMap = m_userDataList[i];\n\n\t\tfeLog(\"\\tMapping user map \\\"%s\\\" ...\", elemMap->GetName().c_str());\n\n\t\t// build the source point list\n\t\tvector<vec3d> srcPoints;\n\t\tconst FEElementSet* oldSet = nodeMap->GetElementSet();\n\t\tFEMesh* oldMesh = oldSet->GetMesh(); assert(oldMesh == m_meshCopy);\n\t\tFENodeList oldNodeList = oldSet->GetNodeList();\n\t\tsrcPoints.resize(oldNodeList.Size());\n\t\tfor (int i = 0; i < oldNodeList.Size(); ++i)\n\t\t{\n\t\t\tFENode& oldNode_i = oldMesh->Node(oldNodeList[i]);\n\t\t\tvec3d r = oldNode_i.m_r0;\n\t\t\tsrcPoints[i] = r;\n\t\t}\n\n\t\t// build target points.\n\t\tconst FEDomainList& domainList = elemMap->GetElementSet()->GetDomainList();\n\t\tFEDomain& dom = const_cast<FEDomain&>(*domainList.GetDomain(0));\n\t\tint NE = dom.Elements();\n\t\tvector<vec3d> trgPoints; trgPoints.reserve(NE);\n\t\tfor (int n = 0; n < NE; ++n)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(n);\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int l = 0; l < ne; ++l)\n\t\t\t{\n\t\t\t\tvec3d r = mesh.Node(el.m_node[l]).m_r0;\n\t\t\t\ttrgPoints.push_back(r);\n\t\t\t}\n\t\t}\n\n\t\t// set up mapper\n\t\tFEMeshDataInterpolator* mapper = nullptr;\n\t\tswitch (m_transferMethod)\n\t\t{\n\t\tcase TRANSFER_SHAPE:\n\t\t{\n\t\t\tFEDomain* oldDomain = m_meshCopy->FindDomain(dom.GetName());\n\t\t\tFEDomainShapeInterpolator* dsm = new FEDomainShapeInterpolator(oldDomain);\n\t\t\tdsm->SetTargetPoints(trgPoints);\n\t\t\tmapper = dsm;\n\t\t}\n\t\tbreak;\n\t\tcase TRANSFER_MLQ:\n\t\t{\n\t\t\tFELeastSquaresInterpolator* MLQ = new FELeastSquaresInterpolator;\n\t\t\tMLQ->SetNearestNeighborCount(m_nnc);\n\t\t\tMLQ->SetDimension(m_nsdim);\n\t\t\tMLQ->SetSourcePoints(srcPoints);\n\t\t\tMLQ->SetTargetPoints(trgPoints);\n\t\t\tmapper = MLQ;\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn;\n\t\t}\n\t\tif (mapper->Init() == false)\n\t\t{\n\t\t\tassert(false);\n\t\t\tthrow std::runtime_error(\"Failed to initialize LLQ\");\n\t\t}\n\n\t\t// map node data to integration points\n\t\tNodeToElemData(fem, dom, nodeMap, elemMap, mapper);\n\n\t\tfeLog(\"done.\\n\");\n\t}\n}\n"
  },
  {
    "path": "FEAMR/FERefineMesh.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEMeshAdaptor.h>\nclass FEModel;\n\nclass FEMeshTopo;\nclass FEBoundaryCondition;\nclass FESurfaceLoad;\nclass FESurfacePairConstraint;\nclass FESurface;\nclass FENodeSet;\nclass FEDomainMap;\n\n//-----------------------------------------------------------------------------\n// Base class for mesh refinement algorithms\nclass FERefineMesh : public FEMeshAdaptor\n{\nprotected:\n\t// Supported transfer methods for mapping data between meshes\n\tenum TransferMethod {\n\t\tTRANSFER_SHAPE,\n\t\tTRANSFER_MLQ\n\t};\n\t\npublic:\n\tFERefineMesh(FEModel* fem);\n\t~FERefineMesh();\n\n\t// Apply mesh refinement\n\tbool Apply(int iteration) override;\n\nprotected:\n\t// Derived classes need to override this function\n\t// The return value should be true if the mesh was refined\n\t// or false otherwise. \n\tvirtual bool RefineMesh() = 0;\n\nprotected:\n\tbool BuildMeshTopo();\n\tvoid CopyMesh();\n\n\tbool BuildMapData();\n\tvoid TransferMapData();\n\tvoid ClearMapData();\n\n\tbool BuildDomainMapData();\n\tbool BuildDomainMapData(FEDomain& dom, int domIndex);\n\tvoid TransferDomainMapData();\n\n\tbool BuildUserMapData();\n\tvoid TransferUserMapData();\n\nprotected:\n\tFEMeshTopo*\tm_topo;\t\t//!< mesh topo structure\n\n\tint\t\tm_maxiter;\t\t// max nr of iterations per time step\n\tint\t\tm_maxelem;\t\t// max nr of elements\n\n\tint\t\tm_transferMethod;\t\t//!< method for transferring data between meshes\n\tbool\tm_bmap_data;\t\t\t//!< map data flag\n\tint\t\tm_nnc;\t\t\t\t\t//!< nearest-neighbor-count for MLQ transfer method\n\tint\t\tm_nsdim;\t\t\t\t//!< nearest-neighbor search dimension (2 or 3)\n\n\tFEMesh*\tm_meshCopy;\t\t//!< copy of \"old\" mesh, before refinement\n\n\tstd::vector< std::vector<FEDomainMap*> >\tm_domainMapList;\t// list of nodal data for each domain\n\tstd::vector< FEDomainMap* >\tm_userDataList;\t\t\t\t\t\t// list of nodal data for user-defined mesh data\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEScaleAdaptorCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEScaleAdaptorCriterion.h\"\n\nBEGIN_FECORE_CLASS(FEScaleAdaptorCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_scale, \"math\");\nEND_FECORE_CLASS();\n\nFEScaleAdaptorCriterion::FEScaleAdaptorCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_scale = 1.0;\n}\n\nbool FEScaleAdaptorCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tvalue = m_scale(mp);\n\treturn true;\n}\n"
  },
  {
    "path": "FEAMR/FEScaleAdaptorCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include <FECore/FEModelParam.h>\n\nclass FEMaterialPoint;\n\n//-----------------------------------------------------------------------------\nclass FEScaleAdaptorCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEScaleAdaptorCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\nprivate:\n\tFEParamDouble\tm_scale;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FETestRefine.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FETestRefine.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEMeshTopo.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FESurface.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FETestRefine, FERefineMesh)\nEND_FECORE_CLASS();\n\nFETestRefine::FETestRefine(FEModel* fem) : FERefineMesh(fem)\n{\n}\n\nstruct TRI\n{\n\tint n[3];\n};\n\nbool FETestRefine::RefineMesh()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tif (m_topo == nullptr) return false;\n\tFEMeshTopo& topo = *m_topo;\n\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(mesh.Domain(0));\n\tint NEL = dom.Elements();\n\tint N0 = mesh.Nodes();\n\n\t// now we recreate the domains\n\tconst int NDOM = mesh.Domains();\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\t// get the old domain\n\t\tFEDomain& oldDom = mesh.Domain(i);\n\t\tint NE0 = oldDom.Elements();\n\n\t\t// create a copy of old domain (since we want to retain the old domain)\n\t\tFEDomain* newDom = fecore_new<FESolidDomain>(oldDom.GetTypeStr(), &fem);\n\t\tnewDom->Create(NE0, FEElementLibrary::GetElementSpecFromType(FE_TET4G4));\n\t\tfor (int j = 0; j < NE0; ++j)\n\t\t{\n\t\t\tFEElement& el0 = oldDom.ElementRef(j);\n\t\t\tFEElement& el1 = newDom->ElementRef(j);\n\t\t\tfor (int k = 0; k < el0.Nodes(); ++k) el1.m_node[k] = el0.m_node[k];\n\t\t}\n\n\t\t// reallocate the old domain\n\t\toldDom.Create(NE0, FEElementLibrary::GetElementSpecFromType(FE_TET4G4));\n\n\t\t// set new element nodes\n\t\tint nel = 0;\n\t\tfor (int j = 0; j < NE0; ++j)\n\t\t{\n\t\t\tFEElement& el0 = newDom->ElementRef(j);\n\t\t\tFEElement& el1 = oldDom.ElementRef(nel++);\n\n\t\t\tel1.m_node[0] = el0.m_node[0];\n\t\t\tel1.m_node[1] = el0.m_node[1];\n\t\t\tel1.m_node[2] = el0.m_node[2];\n\t\t\tel1.m_node[3] = el0.m_node[3];\n\t\t}\n\n\t\t// we don't need this anymore\n\t\tdelete newDom;\n\t}\n\tmesh.RebuildLUT();\n\n\t// re-init domains\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.CreateMaterialPointData();\n\t\tdom.Reset();\t// NOTE: we need to call this to actually call the Init function on the material points.\n\t\tdom.Init();\n\t\tdom.Activate();\n\t}\n\n\t// recreate element sets\n\tfor (int i = 0; i < mesh.ElementSets(); ++i)\n\t{\n\t\tFEElementSet& eset = mesh.ElementSet(i);\n\n\t\t// get the domain list\n\t\t// NOTE: Don't get the reference, since then the same reference\n\t\t// is passed to Create below, which causes problems.\n\t\tFEDomainList domList = eset.GetDomainList();\n\t\tif (domList.IsEmpty()) { throw std::runtime_error(\"Error in FEMMGRemesh!\"); }\n\n\t\t// recreate the element set from the domain list\n\t\teset.Create(domList);\n\t}\n\n\t// recreate surfaces\n\tint faceMark = 1;\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tint NF0 = surf.Elements();\n\n\t\tvector<int> faceList = topo.FaceIndexList(surf);\n\t\tassert(faceList.size() == NF0);\n\n\t\tvector<TRI> tri(NF0);\n\t\tfor (int j = 0; j < NF0; ++j)\n\t\t{\n\t\t\tFESurfaceElement& el = surf.Element(j);\n\t\t\tTRI t;\n\t\t\tt.n[0] = el.m_node[0];\n\t\t\tt.n[1] = el.m_node[1];\n\t\t\tt.n[2] = el.m_node[2];\n\t\t\ttri[j] = t;\n\t\t}\n\n\t\tint NF1 = NF0;\n\t\tsurf.Create(NF1);\n\t\tint nf = 0;\n\t\tfor (int j = 0; j < NF0; ++j)\n\t\t{\n\t\t\tTRI& t = tri[j];\n\n\t\t\tFESurfaceElement& fj = surf.Element(nf++);\n\t\t\tfj.SetType(FE_TRI3G3);\n\t\t\tfj.m_node[0] = t.n[0];\n\t\t\tfj.m_node[1] = t.n[1];\n\t\t\tfj.m_node[2] = t.n[2];\n\t\t}\n\n\t\tsurf.CreateMaterialPointData();\n\t\tsurf.Init();\n\n\t\t// also update the facet set if the surface has one\n\t\tFEFacetSet* fset = surf.GetFacetSet();\n\t\tif (fset)\n\t\t{\n\t\t\tfset->Create(surf);\n\t\t}\n\n\t\tfaceMark++;\n\t}\n\n\t// re-activate the model\n\tUpdateModel();\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEAMR/FETestRefine.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FERefineMesh.h\"\n\nclass FETestRefine : public FERefineMesh\n{\npublic:\n\tFETestRefine(FEModel* fem);\n\n\tbool RefineMesh() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEAMR/FETetQualityCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FETetQualityCriterion.h\"\n#include <FECore/FEElement.h>\n#include <FECore/FEMeshPartition.h>\n#include <FECore/FEMesh.h>\n\n\nBEGIN_FECORE_CLASS(FETetQualityCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(minQuality, \"min_quality\")->setLongName(\"Minimum tet quality\");\nEND_FECORE_CLASS();\n\nFETetQualityCriterion::FETetQualityCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tminQuality = 0.0;\n}\n\nbool FETetQualityCriterion::GetElementValue(FEElement& el, double& value)\n{\n\tif (el.Shape() != ET_TET4) return false;\n\tvalue = TetQuality(el);\n\treturn (value >= minQuality);\n}\n\ndouble FETetQualityCriterion::TetQuality(FEElement& el)\n{\n\tFEMeshPartition* partition = el.GetMeshPartition();\n\tif (partition == nullptr) return 0.0;\n\n\tFEMesh& mesh = *partition->GetMesh();\n\n\t// get the tet's nodal coordinates\n\tvec3d p[4];\n\tfor (int i = 0; i < 4; ++i) p[i] = mesh.Node(el.m_node[i]).m_rt;\n\n\t// setup system of equation\n\tmat3d A;\n\tA[0][0] = p[1].x - p[0].x; A[0][1] = p[1].y - p[0].y; A[0][2] = p[1].z - p[0].z;\n\tA[1][0] = p[2].x - p[0].x; A[1][1] = p[2].y - p[0].y; A[1][2] = p[2].z - p[0].z;\n\tA[2][0] = p[3].x - p[0].x; A[2][1] = p[3].y - p[0].y; A[2][2] = p[3].z - p[0].z;\n\tA = A.inverse();\n\n\t// setup RHS\n\tvec3d b;\n\tb.x = 0.5 * (p[1].x * p[1].x - p[0].x * p[0].x + p[1].y * p[1].y - p[0].y * p[0].y + p[1].z * p[1].z - p[0].z * p[0].z);\n\tb.y = 0.5 * (p[2].x * p[2].x - p[0].x * p[0].x + p[2].y * p[2].y - p[0].y * p[0].y + p[2].z * p[2].z - p[0].z * p[0].z);\n\tb.z = 0.5 * (p[3].x * p[3].x - p[0].x * p[0].x + p[3].y * p[3].y - p[0].y * p[0].y + p[3].z * p[3].z - p[0].z * p[0].z);\n\n\t// find the center of the circum sphere\n\tvec3d c = A * b;\n\n\t// find the radius of the circum sphere\n\tdouble R2 = (p[0].x - c.x) * (p[0].x - c.x) + (p[0].y - c.y) * (p[0].y - c.y) + (p[0].z - c.z) * (p[0].z - c.z);\n\tdouble R = sqrt(R2);\n\n\t// find the shortest edge\n\tconst int ET[6][2] = { { 0, 1 }, { 1, 2 }, { 2, 0 }, { 0, 3 }, { 1, 3 }, { 2, 3 } };\n\n\tdouble L2, L2min = 1e99;\n\tfor (int i = 0; i < 6; ++i)\n\t{\n\t\tint j = ET[i][0];\n\t\tint k = ET[i][1];\n\t\tL2 = (p[j].x - p[k].x) * (p[j].x - p[k].x) + (p[j].y - p[k].y) * (p[j].y - p[k].y) + (p[j].z - p[k].z) * (p[j].z - p[k].z);\n\t\tif (L2 < L2min) L2min = L2;\n\t}\n\tdouble L = sqrt(L2min);\n\n\treturn R / L;\n}\n"
  },
  {
    "path": "FEAMR/FETetQualityCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include \"feamr_api.h\"\n\nclass FEAMR_API FETetQualityCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFETetQualityCriterion(FEModel* fem);\n\n\tbool GetElementValue(FEElement& el, double& value) override;\n\npublic:\n\tstatic double TetQuality(FEElement& el);\n\nprivate:\n\tdouble minQuality = 0.0;\n\n\tDECLARE_FECORE_CLASS()\n};\n\n"
  },
  {
    "path": "FEAMR/FETetRefine.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FETetRefine.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEMeshTopo.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FESurface.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FETetRefine, FERefineMesh)\nEND_FECORE_CLASS();\n\nFETetRefine::FETetRefine(FEModel* fem) : FERefineMesh(fem)\n{\n}\n\nstruct TRI\n{\n\tint n[3];\n};\n\nbool FETetRefine::RefineMesh()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tif (m_topo == nullptr) return false;\n\tFEMeshTopo& topo = *m_topo;\n\n\tFEMesh& mesh = GetMesh();\n\n\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(mesh.Domain(0));\n\tint NEL = dom.Elements();\n\n\t// we need to create a new node for each edge\n\tint N0 = mesh.Nodes();\n\tint newNodes = topo.Edges();\n\tmesh.AddNodes(newNodes);\n\tint N1 = N0 + newNodes;\n\n\t// update the position of these new nodes\n\tint n = N0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\tFENode& node = mesh.Node(n++);\n\n\t\tvec3d r0 = mesh.Node(edge.node[0]).m_r0;\n\t\tvec3d r1 = mesh.Node(edge.node[1]).m_r0;\n\t\tnode.m_r0 = (r0 + r1)*0.5;\n\n\t\tr0 = mesh.Node(edge.node[0]).m_rt;\n\t\tr1 = mesh.Node(edge.node[1]).m_rt;\n\t\tnode.m_rt = (r0 + r1)*0.5;\n\t}\n\tassert(n == N1);\n\n\t// assign dofs to new nodes\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tint NN = mesh.Nodes();\n\tfor (int i = N0; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.SetDOFS(MAX_DOFS);\n\t}\n\n\t// re-evaluate solution at nodes\n\tn = N0;\n\tfor (int i = 0; i < topo.Edges(); ++i)\n\t{\n\t\tconst FEEdgeList::EDGE& edge = topo.Edge(i);\n\t\tFENode& node0 = mesh.Node(edge.node[0]);\n\t\tFENode& node1 = mesh.Node(edge.node[1]);\n\n\t\tFENode& node = mesh.Node(n++);\n\t\tfor (int j = 0; j < MAX_DOFS; ++j)\n\t\t{\n\t\t\tdouble v = (node0.get(j) + node1.get(j))*0.5;\n\t\t\tnode.set(j, v);\n\t\t}\n\t\tnode.UpdateValues();\n\t}\n\tassert(n == N1);\n\n\tconst int LUT[8][4] = {\n\t\t{ 0, 4, 6, 7 },\n\t\t{ 4, 1, 5, 8 },\n\t\t{ 2, 6, 5, 9 },\n\t\t{ 7, 8, 9, 3 },\n\t\t{ 4, 8, 6, 7 },\n\t\t{ 4, 8, 5, 6 },\n\t\t{ 5, 6, 8, 9 },\n\t\t{ 6, 7, 8, 9 },\n\t};\n\n\t// now we recreate the domains\n\tconst int NDOM = mesh.Domains();\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\t// get the old domain\n\t\tFEDomain& oldDom = mesh.Domain(i);\n\t\tint NE0 = oldDom.Elements();\n\n\t\t// create a copy of old domain (since we want to retain the old domain)\n\t\tFEDomain* newDom = fecore_new<FESolidDomain>(oldDom.GetTypeStr(), &fem);\n\t\tnewDom->Create(NE0, FEElementLibrary::GetElementSpecFromType(FE_TET4G4));\n\t\tfor (int j = 0; j < NE0; ++j)\n\t\t{\n\t\t\tFEElement& el0 = oldDom.ElementRef(j);\n\t\t\tFEElement& el1 = newDom->ElementRef(j);\n\t\t\tfor (int k = 0; k < el0.Nodes(); ++k) el1.m_node[k] = el0.m_node[k];\n\t\t}\n\n\t\t// reallocate the old domain\n\t\toldDom.Create(8 * NE0, FEElementLibrary::GetElementSpecFromType(FE_TET4G4));\n\n\t\t// set new element nodes\n\t\tint nel = 0;\n\t\tfor (int j = 0; j < NE0; ++j)\n\t\t{\n\t\t\tFEElement& el0 = newDom->ElementRef(j);\n\n\t\t\tstd::vector<int> ee = topo.ElementEdgeList(j); assert(ee.size() == 6);\n\n\t\t\t// build the look-up table\n\t\t\tint ENL[10] = { 0 };\n\t\t\tENL[0] = el0.m_node[0];\n\t\t\tENL[1] = el0.m_node[1];\n\t\t\tENL[2] = el0.m_node[2];\n\t\t\tENL[3] = el0.m_node[3];\n\t\t\tENL[4] = N0 + ee[0];\n\t\t\tENL[5] = N0 + ee[1];\n\t\t\tENL[6] = N0 + ee[2];\n\t\t\tENL[7] = N0 + ee[3];\n\t\t\tENL[8] = N0 + ee[4];\n\t\t\tENL[9] = N0 + ee[5];\n\n\t\t\tfor (int k = 0; k < 8; ++k)\n\t\t\t{\n\t\t\t\tFEElement& el1 = oldDom.ElementRef(nel++);\n\n\t\t\t\tel1.m_node[0] = ENL[LUT[k][0]];\n\t\t\t\tel1.m_node[1] = ENL[LUT[k][1]];\n\t\t\t\tel1.m_node[2] = ENL[LUT[k][2]];\n\t\t\t\tel1.m_node[3] = ENL[LUT[k][3]];\n\n\t\t\t\tint a = 0;\n\t\t\t}\n\t\t}\n\n\t\t// we don't need this anymore\n\t\tdelete newDom;\n\t}\n\tmesh.RebuildLUT();\n\n\t// re-init domains\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.CreateMaterialPointData();\n\t\tdom.Reset();\t// NOTE: we need to call this to actually call the Init function on the material points.\n\t\tdom.Init();\n\t\tdom.Activate();\n\t}\n\n\tconst int FLUT[4][3] = {\n\t\t{ 0, 3, 5 },\n\t\t{ 1, 4, 3 },\n\t\t{ 2, 5, 4 },\n\t\t{ 3, 4, 5 },\n\t};\n\n\t// recreate element sets\n\tfor (int i = 0; i < mesh.ElementSets(); ++i)\n\t{\n\t\tFEElementSet& eset = mesh.ElementSet(i);\n\n\t\t// get the domain list\n\t\t// NOTE: Don't get the reference, since then the same reference\n\t\t// is passed to Create below, which causes problems.\n\t\tFEDomainList domList = eset.GetDomainList();\n\t\tif (domList.IsEmpty()) { throw std::runtime_error(\"Error in FEMMGRemesh!\"); }\n\n\t\t// recreate the element set from the domain list\n\t\teset.Create(domList);\n\t}\n\n\t// recreate surfaces\n\tint faceMark = 1;\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tint NF0 = surf.Elements();\n\n\t\tvector<int> faceList = topo.FaceIndexList(surf);\n\t\tassert(faceList.size() == NF0);\n\n\t\tvector<TRI> tri(NF0);\n\t\tfor (int j = 0; j < NF0; ++j)\n\t\t{\n\t\t\tFESurfaceElement& el = surf.Element(j);\n\t\t\tTRI t;\n\t\t\tt.n[0] = el.m_node[0];\n\t\t\tt.n[1] = el.m_node[1];\n\t\t\tt.n[2] = el.m_node[2];\n\t\t\ttri[j] = t;\n\t\t}\n\n\t\tint NF1 = NF0 * 4;\n\t\tsurf.Create(NF1);\n\t\tint nf = 0;\n\t\tfor (int j = 0; j < NF0; ++j)\n\t\t{\n\t\t\tTRI& t = tri[j];\n\n\t\t\tstd::vector<int> ee = topo.FaceEdgeList(faceList[j]); assert(ee.size() == 3);\n\n\t\t\t// build the look-up table\n\t\t\tint FNL[6] = { 0 };\n\t\t\tFNL[0] = t.n[0];\n\t\t\tFNL[1] = t.n[1];\n\t\t\tFNL[2] = t.n[2];\n\n\t\t\t// the edges may not be ordered correctly, so we need to do a search here.\n\t\t\tfor (int k = 0; k < 3; k++)\n\t\t\t{\n\t\t\t\tint a = t.n[k];\n\t\t\t\tint b = t.n[(k+1)%3];\n\t\t\t\tint e = -1;\n\t\t\t\tfor (int l = 0; l < 3; ++l)\n\t\t\t\t{\n\t\t\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(ee[l]);\n\t\t\t\t\tif ((edge.node[0] == a) && (edge.node[1] == b)) { e = l; break; }\n\t\t\t\t\tif ((edge.node[1] == a) && (edge.node[0] == b)) { e = l; break; }\n\t\t\t\t}\n\t\t\t\tassert(e >= 0);\n\t\t\t\tFNL[k + 3] = N0 + ee[e];\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < 4; ++k)\n\t\t\t{\n\t\t\t\tFESurfaceElement& fj = surf.Element(nf++);\n\t\t\t\tfj.SetType(FE_TRI3G3);\n\t\t\t\tfj.m_node[0] = FNL[FLUT[k][0]];\n\t\t\t\tfj.m_node[1] = FNL[FLUT[k][1]];\n\t\t\t\tfj.m_node[2] = FNL[FLUT[k][2]];\n\t\t\t}\n\t\t}\n\n\t\tsurf.CreateMaterialPointData();\n\t\tsurf.Init();\n\n\t\t// also update the facet set if the surface has one\n\t\tFEFacetSet* fset = surf.GetFacetSet();\n\t\tif (fset)\n\t\t{\n\t\t\tfset->Create(surf);\n\t\t}\n\n\t\tfaceMark++;\n\t}\n\n\t// update node sets\n\tconst int NSETS = mesh.NodeSets();\n\tfor (int i=0; i<NSETS; ++i)\n\t{\n\t\tFENodeSet& nset = *mesh.NodeSet(i);\n\t\tvector<int> tag(mesh.Nodes(), 0);\n\t\tfor (int j = 0; j < nset.Size(); ++j) tag[nset[j]] = 1;\n\n\t\tfor (int j = 0; j < topo.Edges(); ++j)\n\t\t{\n\t\t\tconst FEEdgeList::EDGE& edge = topo.Edge(j);\n\n\t\t\tif ((tag[edge.node[0]] == 1) && (tag[edge.node[1]] == 1))\n\t\t\t{\n\t\t\t\tnset.Add(N0 + j);\n\t\t\t}\n\t\t}\n\t}\n\n\t// re-activate the model\n\tUpdateModel();\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEAMR/FETetRefine.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FERefineMesh.h\"\n\nclass FETetRefine : public FERefineMesh\n{\npublic:\n\tFETetRefine(FEModel* fem);\n\n\tbool RefineMesh() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/FEVariableCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEVariableCriterion.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEMesh.h>\n\nBEGIN_FECORE_CLASS(FEVariableCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_dof, \"dof\");\nEND_FECORE_CLASS();\n\nFEVariableCriterion::FEVariableCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_dof = -1;\n}\n\nbool FEVariableCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tif (m_dof == -1) return false;\n\n\tFEElement* pe = mp.m_elem;\n\tif (pe == nullptr) return false;\n\n\tif ((mp.m_index < 0) || (mp.m_index >= pe->GaussPoints())) return false;\n\n\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(pe->GetMeshPartition());\n\tif (dom == nullptr) return false;\n\n\tFEMesh& mesh = *dom->GetMesh();\n\n\tdouble vn[FEElement::MAX_NODES];\n\tfor (int i = 0; i < pe->Nodes(); ++i)\n\t{\n\t\tvn[i] = mesh.Node(pe->m_node[i]).get(m_dof);\n\t}\n\n\tvalue = pe->Evaluate(vn, mp.m_index);\n\treturn true;\n}\n"
  },
  {
    "path": "FEAMR/FEVariableCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n\n//-----------------------------------------------------------------------------\nclass FEVariableCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEVariableCriterion(FEModel* fem);\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\nprivate:\n\tint\t\tm_dof;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEAMR/SpherePointsGenerator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2025 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include <algorithm>\n#include <cassert>\n#include \"SpherePointsGenerator.h\"\n\nSpherePointsGenerator* SpherePointsGenerator::m_instance = nullptr;\n\nvoid SpherePointsGenerator::Instantiate()\n{\n    if (m_instance == nullptr) {\n        m_instance = new SpherePointsGenerator();\n    }\n}\n\nsize_t SpherePointsGenerator::GetNumNodes(int n)\n{\n    Instantiate();\n\n    m_instance->GeneratePoints(n);\n\n    return m_instance->m_nodes[n].size();\n}\n\nsize_t SpherePointsGenerator::GetNumFaces(int n)\n{\n    Instantiate();\n\n    m_instance->GeneratePoints(n);\n\n    return m_instance->m_faces[n].size();\n}\n\nstd::vector<vec3d>& SpherePointsGenerator::GetNodes(int n)\n{\n    Instantiate();\n\n    m_instance->GeneratePoints(n);\n\n    return m_instance->m_nodes[n];\n}\n\nstd::vector<std::array<int, 3>>& SpherePointsGenerator::GetFaces(int n)\n{\n    Instantiate();\n\n    m_instance->GeneratePoints(n);\n\n    return m_instance->m_faces[n];\n}\n\nvoid SpherePointsGenerator::GeneratePoints(int n)\n{\n    if(m_nodes.find(n) == m_nodes.end() || m_faces.find(n) == m_faces.end())\n    {\n        m_nodes[n] = std::vector<vec3d>();\n        m_faces[n] = std::vector<std::array<int, 3>>();\n\n        std::vector<vec3d>& vertices = m_nodes[n];\n        std::vector<std::array<int, 3>>& faces = m_faces[n];\n\n        // Create icosahedron\n        create_icosahedron(vertices, faces);\n    \n        // Align equator with x-y plane\n        align_equator(vertices);\n        \n        // Subdivide\n        subdivide(vertices, faces, n);\n    }\n}\n\nvec3d SpherePointsGenerator::midpoint(const vec3d& a, const vec3d& b)\n{\n    vec3d out = (a + b).normalized();\n\n    // Ensure that points on the equator are exactly on the x-y plane\n    if(abs(out.z) < 1e-5)\n    {\n        out.z = 0;\n    }\n\n    return out;\n}\n\nvoid SpherePointsGenerator::create_icosahedron(std::vector<vec3d>& vertices, std::vector<std::array<int, 3>>& faces)\n{\n    double phi = (1.0 + sqrt(5.0)) / 2.0;\n    \n    // Match Python vertices exactly\n    std::vector<vec3d> verts =\n    {\n        {0, 1, phi}, {0, -1, phi}, {0, 1, -phi}, {0, -1, -phi},\n        {1, phi, 0}, {-1, phi, 0}, {1, -phi, 0}, {-1, -phi, 0},\n        {phi, 0, 1}, {-phi, 0, 1}, {phi, 0, -1}, {-phi, 0, -1}\n    };\n    \n    // Normalize vertices\n    for (auto& v : verts)\n    {\n        v.Normalize();\n    }\n    \n    vertices = verts;\n    \n    // Use same face connectivity as in Python\n    faces =\n    {\n        {0, 1, 8}, {0, 8, 4}, {0, 4, 5}, {0, 5, 9}, {0, 9, 1},\n        {1, 9, 7}, {1, 7, 6}, {1, 6, 8}, {8, 6, 10}, {8, 10, 4},\n        {4, 10, 2}, {4, 2, 5}, {5, 2, 11}, {5, 11, 9}, {9, 11, 7},\n        {3, 2, 10}, {3, 10, 6}, {3, 6, 7}, {3, 7, 11}, {3, 11, 2}\n    };\n}\n\n// Rotation function to match Python's implementation\nvoid SpherePointsGenerator::rotate(std::vector<vec3d>& vertices, const vec3d& axis, double angle_rad)\n{\n    // Normalize axis\n    vec3d norm_axis = axis.normalized();\n    \n    // Create rotation matrix using Rodrigues' formula (same as Python)\n    double cos_a = cosf(angle_rad);\n    double sin_a = sinf(angle_rad);\n    double one_minus_cos = 1.0f - cos_a;\n    \n    // K matrix (skew-symmetric cross-product matrix)\n    double K[3][3] =\n    {\n        {0, -norm_axis.z, norm_axis.y},\n        {norm_axis.z, 0, -norm_axis.x},\n        {-norm_axis.y, norm_axis.x, 0}\n    };\n    \n    // K^2 matrix\n    double K2[3][3] =\n    {\n        {-norm_axis.y*norm_axis.y - norm_axis.z*norm_axis.z, norm_axis.x*norm_axis.y, norm_axis.x*norm_axis.z},\n        {norm_axis.x*norm_axis.y, -norm_axis.x*norm_axis.x - norm_axis.z*norm_axis.z, norm_axis.y*norm_axis.z},\n        {norm_axis.x*norm_axis.z, norm_axis.y*norm_axis.z, -norm_axis.x*norm_axis.x - norm_axis.y*norm_axis.y}\n    };\n    \n    // R = I + sin(θ)K + (1-cos(θ))K²\n    double R[3][3] =\n    {\n        {1.0f + sin_a*K[0][0] + one_minus_cos*K2[0][0], sin_a*K[0][1] + one_minus_cos*K2[0][1], sin_a*K[0][2] + one_minus_cos*K2[0][2]},\n        {sin_a*K[1][0] + one_minus_cos*K2[1][0], 1.0f + sin_a*K[1][1] + one_minus_cos*K2[1][1], sin_a*K[1][2] + one_minus_cos*K2[1][2]},\n        {sin_a*K[2][0] + one_minus_cos*K2[2][0], sin_a*K[2][1] + one_minus_cos*K2[2][1], 1.0f + sin_a*K[2][2] + one_minus_cos*K2[2][2]}\n    };\n    \n    // Apply rotation to each vertex\n    for (auto& v : vertices)\n    {\n        double x = v.x, y = v.y, z = v.z;\n        v.x = R[0][0]*x + R[0][1]*y + R[0][2]*z;\n        v.y = R[1][0]*x + R[1][1]*y + R[1][2]*z;\n        v.z = R[2][0]*x + R[2][1]*y + R[2][2]*z;\n    }\n}\n\n// Match Python's align_equator implementation\nvoid SpherePointsGenerator::align_equator(std::vector<vec3d>& vertices)\n{\n    // Step 1: Detect \"belt\" axis\n    double abs_mean_x = 0, abs_mean_y = 0, abs_mean_z = 0;\n    for (const auto& v : vertices)\n    {\n        abs_mean_x += abs(v.x);\n        abs_mean_y += abs(v.y);\n        abs_mean_z += abs(v.z);\n    }\n    \n    int n = (int)vertices.size();\n    abs_mean_x /= n;\n    abs_mean_y /= n;\n    abs_mean_z /= n;\n    \n    int belt_axis = (abs_mean_x < abs_mean_y && abs_mean_x < abs_mean_z) ? 0 : \n                   (abs_mean_y < abs_mean_z) ? 1 : 2;\n    \n    // Step 2: Find vertices in the belt\n    std::vector<vec3d> band_vertices;\n    std::vector<double> abs_coords;\n    \n    for (const auto& v : vertices)\n    {\n        double coord = (belt_axis == 0) ? v.x : (belt_axis == 1) ? v.y : v.z;\n        abs_coords.push_back(abs(coord));\n    }\n    \n    // Calculate median of absolute coordinates (approximate)\n    std::vector<double> sorted_abs_coords = abs_coords;\n    std::sort(sorted_abs_coords.begin(), sorted_abs_coords.end());\n    double belt_band = sorted_abs_coords[sorted_abs_coords.size() / 2];\n    \n    for (size_t i = 0; i < vertices.size(); i++)\n    {\n        double coord = (belt_axis == 0) ? vertices[i].x : (belt_axis == 1) ? vertices[i].y : vertices[i].z;\n        if (abs(abs(coord) - belt_band) < 1e-3f)\n        {\n            band_vertices.push_back(vertices[i]);\n        }\n    }\n    \n    if (band_vertices.empty())\n    {\n        \n        assert(false); // No vertices in belt\n    }\n    \n    // Pick first band vertex and normalize\n    vec3d target_vertex = band_vertices[0].normalized();\n    \n    // Step 3: Rotate this vertex to Z axis\n    vec3d z_axis(0, 0, 1);\n    vec3d axis = vec3d(\n        target_vertex.y * z_axis.z - target_vertex.z * z_axis.y,\n        target_vertex.z * z_axis.x - target_vertex.x * z_axis.z,\n        target_vertex.x * z_axis.y - target_vertex.y * z_axis.x\n    );\n    \n    axis.Normalize();\n    double dot = target_vertex.x * z_axis.x + target_vertex.y * z_axis.y + target_vertex.z * z_axis.z;\n    double angle = acos(std::clamp(dot, -1.0, 1.0));\n    \n    // Rotate all vertices\n    rotate(vertices, axis, angle);\n    \n    // Ensure that points on the equator are exactly on the x-y plane\n    for(int index = 0; index < vertices.size(); index++)\n    {\n        if(abs(vertices[index].z) < 1e-5)\n        {\n            vertices[index].z = 0;\n        }\n    }\n}\n\nint SpherePointsGenerator::get_midpoint_index(int i1, int i2, std::vector<vec3d>& vertices, \n                       std::unordered_map<std::pair<int, int>, int, pair_hash>& midpoint_cache)\n{\n    std::pair<int, int> key = (i1 < i2) ? std::make_pair(i1, i2) : std::make_pair(i2, i1);\n    \n    if (midpoint_cache.count(key))\n    {\n        return midpoint_cache[key];\n    }\n    \n    // Calculate midpoint \n    vec3d mid = midpoint(vertices[i1], vertices[i2]);\n    vertices.push_back(mid);\n    int idx = (int)vertices.size() - 1;\n    midpoint_cache[key] = idx;\n    return idx;\n}\n\nvoid SpherePointsGenerator::subdivide(std::vector<vec3d>& vertices, std::vector<std::array<int, 3>>& faces, int depth)\n{\n    for (int i = 0; i < depth; ++i)\n    {\n        std::unordered_map<std::pair<int, int>, int, pair_hash> midpoint_cache;\n        std::vector<std::array<int, 3>> new_faces;\n        \n        for (const auto& tri : faces)\n        {\n            int v1 = tri[0], v2 = tri[1], v3 = tri[2];\n            \n            // Get midpoints\n            int a = get_midpoint_index(v1, v2, vertices, midpoint_cache);\n            int b = get_midpoint_index(v2, v3, vertices, midpoint_cache);\n            int c = get_midpoint_index(v3, v1, vertices, midpoint_cache);\n            \n            // Create new faces \n            new_faces.push_back({v1, a, c});\n            new_faces.push_back({v2, b, a});\n            new_faces.push_back({v3, c, b});\n            new_faces.push_back({a, b, c});\n        }\n        \n        faces = new_faces;\n    }\n}"
  },
  {
    "path": "FEAMR/SpherePointsGenerator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2025 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include <vector>\n#include <unordered_map>\n#include <array>\n#include <FECore/vec3d.h>\n#include \"feamr_api.h\"\n\nstruct pair_hash {\n    template <class T1, class T2>\n    std::size_t operator()(const std::pair<T1, T2>& p) const {\n        auto h1 = std::hash<T1>{}(p.first);\n        auto h2 = std::hash<T2>{}(p.second);\n        return h1 ^ (h2 << 1);\n    }\n};\n\nenum meshSizes { SMALL = 4, FULL = 6};\n\nclass FEAMR_API SpherePointsGenerator\n{\npublic:\n    static size_t GetNumNodes(int n);\n    static size_t GetNumFaces(int n);\n    static std::vector<vec3d>& GetNodes(int n);\n    static std::vector<std::array<int, 3>>& GetFaces(int n);\n\nprivate:\n\tSpherePointsGenerator() {}\n    static void Instantiate();\n\n    void GeneratePoints(int n);\n\n    vec3d midpoint(const vec3d& a, const vec3d& b);\n\n    // Create modified icosahedron which has a belt around the equator\n    void create_icosahedron(std::vector<vec3d>& vertices, std::vector<std::array<int, 3>>& faces);\n\n    void rotate(std::vector<vec3d>& vertices, const vec3d& axis, double angle_rad);\n\n    // Aligns the equator of the sphere with the x-y plane\n    void align_equator(std::vector<vec3d>& vertices);\n\n    // Get or create midpoint between two vertices\n    int get_midpoint_index(int i1, int i2, std::vector<vec3d>& vertices, \n        std::unordered_map<std::pair<int, int>, int, pair_hash>& midpoint_cache);\n\n    // Subdivide icosahedral mesh\n    void subdivide(std::vector<vec3d>& vertices, std::vector<std::array<int, 3>>& faces, int depth);\n\n    \nprivate:\n    static SpherePointsGenerator* m_instance;\n\n    std::unordered_map<int, std::vector<vec3d>> m_nodes;\n    std::unordered_map<int, std::vector<std::array<int, 3>>> m_faces;\n};\n"
  },
  {
    "path": "FEAMR/feamr_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef feamr_EXPORTS\n\t\t\t#define FEAMR_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEAMR_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEAMR_API\n\t#endif\n#else\n\t#define FEAMR_API\n#endif\n\n"
  },
  {
    "path": "FEAMR/sphericalHarmonics.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"sphericalHarmonics.h\"\n#include \"SpherePointsGenerator.h\"\n#include <algorithm>\n#include <unordered_map>\n#include <vector>\n\n#ifdef HAS_MMG\n#include <mmg/mmgs/libmmgs.h>\n#endif\n\n#ifndef M_PI\n#define M_PI 3.141592653589793238462643\n#endif\n\nusing std::vector;\nusing std::unordered_map;\nusing sphere = SpherePointsGenerator;\n\nenum NUMTYPE { REALTYPE, IMAGTYPE, COMPLEXTYPE };\n\ndouble fact(int val)\n{\n    double ans = 1;\n    \n    for(int i = 1; i <= val; i++)\n    {\n        ans *= i;\n    } \n    \n    return ans;\n}\n\nvoid getSphereCoords(std::vector<vec3d>& coords, std::vector<double>& theta, std::vector<double>& phi)\n{\n    theta.resize(coords.size());\n    phi.resize(coords.size());\n\n    // get spherical coordinates\n    for(int index = 0; index < coords.size(); index++)\n    {\n        double val = coords[index].z;\n\n        if (val<=-1)\n        {\n            theta[index] = M_PI;\n        } \n        else if (val>=1)\n        {\n             theta[index] = 0;\n        } \n        else \n        {\n            theta[index] = acos(coords[index].z);\n        }\n    }\n\n    for(int index = 0; index < coords.size(); index++)\n    {\n        double x = coords[index].x;\n        double y = coords[index].y;\n\n        if(x > 0)\n        {\n            phi[index] = atan(y/x);\n        }\n        else if(x < 0 && y >= 0)\n        {\n            phi[index] = atan(y/x) + M_PI;\n        }\n        else if(x < 0 && y < 0)\n        {\n            phi[index] = atan(y/x) - M_PI;\n        }\n        else if(x == 0 && y > 0)\n        {\n            phi[index] = M_PI/2;\n        }\n        else if(x == 0 && y < 0)\n        {\n            phi[index] = -M_PI/2;\n        }\n    }\n\n}\n\nstd::unique_ptr<matrix> compSH(int order, std::vector<double>& theta, std::vector<double>& phi)\n{\n    int numPts = theta.size();\n    int numCols = (order+1)*(order+2)/2;\n    std::unique_ptr<matrix> out = std::make_unique<matrix>(numPts, numCols);\n    out->fill(0,0,numPts, numCols, 0.0);\n\n    for(int k = 0; k <= order; k+=2)\n    {\n        for(int m = -k; m <= k; m++)\n        {\n            int j = (k*k + k + 2)/2 + m - 1;\n\n            int numType = COMPLEXTYPE;\n            double factor = 1;\n            if(m < 0)\n            {\n                numType = REALTYPE;\n                factor = sqrt(2);\n            }\n            else if(m > 0)\n            {\n                numType = IMAGTYPE;\n                factor = sqrt(2);\n            }\n\n            for(int index = 0; index < numPts; index++)\n            {\n                (*out)[index][j] = factor*harmonicY(k, m, theta[index], phi[index], numType);\n            }\n        }\n    }\n\n    return std::move(out);\n}\n\ndouble harmonicY(int degree, int order, double theta, double phi, int numType)\n{\n    // Will be true if order is both positive and odd\n    // In that case, we need to negate the answer to match the output\n    // in Adam's MATLAB code.\n    int negate = 1;\n    if(order % 2 == 1) negate = -1;\n    \n    order = abs(order);\n\n    double a = (2*degree+1)/(4*M_PI);\n    double b = fact(degree-order)/fact(degree+order);\n\n    double normalization = sqrt(a*b);\n\n    double e;\n    switch (numType)\n    {\n    case REALTYPE:\n        e = cos(order*phi);\n        break;\n    case IMAGTYPE:\n        e = sin(order*phi);\n        break;\n    case COMPLEXTYPE:\n        e = 1;\n        break;\n    \n    default:\n        break;\n    }\n\n    return normalization*__assoc_legendre_p(degree, order, cos(theta))*pow(-1, degree)*e*negate;\n}\n\nvoid reconstructODF(std::vector<double>& sphHarm, std::vector<double>& ODF, std::vector<double>& theta, std::vector<double>& phi)\n{\n    int order = (sqrt(8*sphHarm.size() + 1) - 3)/2;\n\n    ODF.resize(theta.size());  \n    auto T = compSH(order, theta, phi);\n    (*T).mult(sphHarm, ODF);\n\n    // Normalize ODF\n    double sum = 0;\n    for(int index = 0; index < ODF.size(); index++)\n    {\n        if(ODF[index] < 0)\n        {\n            ODF[index] = 0;\n        }\n        \n        sum += ODF[index];\n    }\n\n    for(int index = 0; index < ODF.size(); index++)\n    {\n        ODF[index] /= sum;\n    }\n}\n\nvoid altGradient(int order, std::vector<double>& ODF, std::vector<double>& gradient)\n{\n    auto& faces = sphere::GetFaces(FULL);\n\n    gradient.resize(ODF.size());\n    std::fill(gradient.begin(), gradient.end(), 0);\n\n    std::vector<int> count(ODF.size(), 0);\n\n    for(int index = 0; index < faces.size(); index++)\n    {\n        int n0 = faces[index][0];\n        int n1 = faces[index][1];\n        int n2 = faces[index][2];\n\n        double val0 = ODF[n0];\n        double val1 = ODF[n1];\n        double val2 = ODF[n2];\n\n        double diff0 = abs(val0 - val1);\n        double diff1 = abs(val0 - val2);\n        double diff2 = abs(val1 - val2);\n\n        gradient[n0] += diff0 + diff1;\n        gradient[n1] += diff0 + diff2;\n        gradient[n2] += diff1 + diff2;\n\n        count[n0]++;\n        count[n1]++;\n        count[n2]++;\n    }\n\n    for(int index = 0; index < gradient.size(); index++)\n    {\n        gradient[index] /= count[index];\n    }\n}\n\n#ifdef HAS_MMG\nvoid remesh(std::vector<double>& gradient, double lengthScale, double hausd, double grad, std::vector<vec3d>& nodePos, std::vector<vec3i>& elems)\n{\n    auto& nodes = sphere::GetNodes(FULL);\n    auto& faces = sphere::GetFaces(FULL);\n\n\tint NN = nodes.size();\n\tint NF = faces.size();;\n    int NC;\n\n    // we only want to remesh half of the sphere, so here we discard \n    // any nodes that have a z coordinate < 0, and any elements defined\n    // with those nodes.\n    unordered_map<int, int> newNodeIDs;\n    int newNodeID = 1;\n    for(int index = 0; index < NN; index++)\n    {\n        if(nodes[index].z >= 0)\n        {\n            newNodeIDs[index] = newNodeID;\n            newNodeID++;\n        }\n    }\n\n    unordered_map<int, int> newElemIDs;\n    int newElemID = 1;\n    for(int index = 0; index < NF; index++)\n    {\n        if(newNodeIDs.count(faces[index][0]) == 0 || newNodeIDs.count(faces[index][1]) == 0 || newNodeIDs.count(faces[index][2]) == 0)\n        {\n            continue;\n        }\n\n        newElemIDs[index] = newElemID;\n        newElemID++;\n    }\n\n\t// build the MMG mesh\n\tMMG5_pMesh mmgMesh;\n\tMMG5_pSol  mmgSol;\n\tmmgMesh = NULL;\n\tmmgSol = NULL;\n\tMMGS_Init_mesh(MMG5_ARG_start,\n\t\tMMG5_ARG_ppMesh, &mmgMesh,\n\t\tMMG5_ARG_ppMet, &mmgSol,\n\t\tMMG5_ARG_end);\n\n\t// allocate mesh size\n\tif (MMGS_Set_meshSize(mmgMesh, newNodeID-1, newElemID-1, 0) != 1)\n\t{\n\t\tassert(false);\n\t}\n\n\t// build the MMG mesh\n\tfor (int i = 0; i < NN; ++i)\n\t{\n        if(newNodeIDs.count(i))\n        {\n            MMGS_Set_vertex(mmgMesh, nodes[i].x, nodes[i].y, nodes[i].z, 0, newNodeIDs[i]);\n        }\n\t}\n\n\tfor (int i = 0; i < NF; ++i)\n\t{\n        if(newElemIDs.count(i))\n        {\n            MMGS_Set_triangle(mmgMesh, newNodeIDs[faces[i][0]], newNodeIDs[faces[i][1]], newNodeIDs[faces[i][2]], 0, newElemIDs[i]);\n        }\n\t}\n\t\n    // Now, we build the \"solution\", i.e. the target element size.\n\t// If no elements are selected, we set a homogenous remeshing using the element size parameter.\n\t// set the \"solution\", i.e. desired element size\n\tif (MMGS_Set_solSize(mmgMesh, mmgSol, MMG5_Vertex, newNodeID-1, MMG5_Scalar) != 1)\n\t{\n\t\tassert(false);\n\t}\n\n    int n0 = faces[0][0];\n    int n1 = faces[0][1];\n\n    vec3d pos0 = nodes[n0];\n    vec3d pos1 = nodes[n1];\n\n    double minLength = (pos0 - pos1).Length();\n    double maxLength = minLength*lengthScale;\n\n    double min = *std::min_element(gradient.begin(), gradient.end());\n    double max = *std::max_element(gradient.begin(), gradient.end());\n    double range = max-min;\n\n\n    for (int k = 0; k < NN; k++) {\n        if(newNodeIDs.count(k))\n        {\n            double val = (maxLength - minLength)*(1-(gradient[k] - min)/range) + minLength;\n            MMGS_Set_scalarSol(mmgSol, val, newNodeIDs[k]);\n        }\n    }\n\n\t// set the control parameters\n\tMMGS_Set_dparameter(mmgMesh, mmgSol, MMGS_DPARAM_hmin, minLength);\n\tMMGS_Set_dparameter(mmgMesh, mmgSol, MMGS_DPARAM_hausd, hausd);\n\tMMGS_Set_dparameter(mmgMesh, mmgSol, MMGS_DPARAM_hgrad, grad);\n\n    // prevent MMG from outputing information to stdout\n    MMGS_Set_iparameter(mmgMesh, mmgSol, MMGS_IPARAM_verbose, -1);\n\n\t// run the mesher\n\tint ier = MMGS_mmgslib(mmgMesh, mmgSol);\n\n\tif (ier == MMG5_STRONGFAILURE)\n    {\n\t\tassert(false);\n\t}\n\telse if (ier == MMG5_LOWFAILURE)\n\t{\n\t\tassert(false);\n\t}\n\n\t// get the new mesh sizes\n\tMMGS_Get_meshSize(mmgMesh, &NN, &NF, &NC);\n\n    nodePos.resize(NN);\n\n\t// get the vertex coordinates\n\tfor (int i = 0; i < NN; ++i)\n\t{\n        double x,y,z;\n        int g;\n\t\tint isCorner = 0;\n\t\tMMGS_Get_vertex(mmgMesh, &x, &y, &z, &g, &isCorner, NULL);\n\n        nodePos[i] = vec3d(x,y,z);\n\t}\n\n    elems.resize(NF);\n\n    // create elements\n\tfor (int i=0; i<NF; ++i)\n\t{\n        int n0, n1, n2, id;\n\n        MMGS_Get_triangle(mmgMesh, &n0, &n1, &n2, &id, NULL);\n\t\tn0--;\n\t\tn1--;\n\t\tn2--;\n\n        elems[i] = vec3i(n0, n1,n2);\n\t}\n\n\t// Clean up\n\tMMGS_Free_all(MMG5_ARG_start,\n\t\tMMG5_ARG_ppMesh, &mmgMesh, MMG5_ARG_ppMet, &mmgSol,\n\t\tMMG5_ARG_end);\n}\n\nvoid remeshFull(std::vector<double>& gradient, double lengthScale, double hausd, double grad, std::vector<vec3d>& nodePos, std::vector<vec3i>& elems)\n{\n\tauto& nodes = sphere::GetNodes(FULL);\n    auto& faces = sphere::GetFaces(FULL);\n\n\tint NN = nodes.size();\n\tint NF = faces.size();\n    int NC;\n\n\t// build the MMG mesh\n\tMMG5_pMesh mmgMesh;\n\tMMG5_pSol  mmgSol;\n\tmmgMesh = NULL;\n\tmmgSol = NULL;\n\tMMGS_Init_mesh(MMG5_ARG_start,\n\t\tMMG5_ARG_ppMesh, &mmgMesh,\n\t\tMMG5_ARG_ppMet, &mmgSol,\n\t\tMMG5_ARG_end);\n\n\t// allocate mesh size\n\tif (MMGS_Set_meshSize(mmgMesh, NN, NF, 0) != 1)\n\t{\n\t\tassert(false);\n\t}\n\n\t// build the MMG mesh\n\tfor (int i = 0; i < NN; ++i)\n\t{\n        MMGS_Set_vertex(mmgMesh, nodes[i].x, nodes[i].y, nodes[i].z, 0, i+1);\n\t}\n\n\tfor (int i = 0; i < NF; ++i)\n\t{\n        MMGS_Set_triangle(mmgMesh, faces[i][0]+1, faces[i][1]+1, faces[i][2]+1, 0, i+1);\n\t}\n\t\n    // Now, we build the \"solution\", i.e. the target element size.\n\t// If no elements are selected, we set a homogenous remeshing using the element size parameter.\n\t// set the \"solution\", i.e. desired element size\n\tif (MMGS_Set_solSize(mmgMesh, mmgSol, MMG5_Vertex, NN, MMG5_Scalar) != 1)\n\t{\n\t\tassert(false);\n\t}\n\n    int n0 = faces[0][0];\n    int n1 = faces[0][1];\n\n    vec3d pos0 = nodes[n0];\n    vec3d pos1 = nodes[n1];\n\n    double minLength = (pos0 - pos1).Length();\n    double maxLength = minLength*lengthScale;\n\n    double min = *std::min_element(gradient.begin(), gradient.end());\n    double max = *std::max_element(gradient.begin(), gradient.end());\n    double range = max-min;\n\n\n    for (int k = 0; k < NN; k++)\n    {\n        double val = (maxLength - minLength)*(1-(gradient[k] - min)/range) + minLength;\n        MMGS_Set_scalarSol(mmgSol, val, k+1);\n    }\n\n\t// set the control parameters\n\tMMGS_Set_dparameter(mmgMesh, mmgSol, MMGS_DPARAM_hmin, minLength);\n\tMMGS_Set_dparameter(mmgMesh, mmgSol, MMGS_DPARAM_hausd, hausd);\n\tMMGS_Set_dparameter(mmgMesh, mmgSol, MMGS_DPARAM_hgrad, grad);\n\n    // prevent MMG from outputing information to stdout\n    MMGS_Set_iparameter(mmgMesh, mmgSol, MMGS_IPARAM_verbose, -1);\n\n\t// run the mesher\n\tint ier = MMGS_mmgslib(mmgMesh, mmgSol);\n\n\tif (ier == MMG5_STRONGFAILURE) \n    {\n        assert(false);\n\t}\n\telse if (ier == MMG5_LOWFAILURE)\n\t{\n\t\tassert(false);\n\t}\n\n\t// get the new mesh sizes\n\tMMGS_Get_meshSize(mmgMesh, &NN, &NF, &NC);\n\n    nodePos.resize(NN);\n\n\t// get the vertex coordinates\n\tfor (int i = 0; i < NN; ++i)\n\t{\n        double x,y,z;\n        int g;\n\t\tint isCorner = 0;\n\t\tMMGS_Get_vertex(mmgMesh, &x, &y, &z, &g, &isCorner, NULL);\n\n        nodePos[i] = vec3d(x,y,z);\n\t}\n\n    elems.resize(NF);\n\n    // create elements\n\tfor (int i=0; i<NF; ++i)\n\t{\n        int n0, n1, n2, id;\n\n        MMGS_Get_triangle(mmgMesh, &n0, &n1, &n2, &id, NULL);\n\t\tn0--;\n\t\tn1--;\n\t\tn2--;\n\n        elems[i] = vec3i(n0, n1,n2);\n\t}\n\n\t// Clean up\n\tMMGS_Free_all(MMG5_ARG_start,\n\t\tMMG5_ARG_ppMesh, &mmgMesh, MMG5_ARG_ppMet, &mmgSol,\n\t\tMMG5_ARG_end);\n}\n#else\nvoid remesh(std::vector<double>& gradient, double lengthScale, double hausd, double grad, std::vector<vec3d>& nodePos, std::vector<vec3i>& elems) {}\nvoid remeshFull(std::vector<double>& gradient, double lengthScale, double hausd, double grad, std::vector<vec3d>& nodePos, std::vector<vec3i>& elems) {}\n#endif\n\n// Taken from std::assoc_legendre definition in GCC\ntemplate<typename _Tp>\n_Tp\n__poly_legendre_p(unsigned int __l, _Tp __x)\n{\n\n    if (isnan(__x))\n    return std::numeric_limits<_Tp>::quiet_NaN();\n    else if (__x == +_Tp(1))\n    return +_Tp(1);\n    else if (__x == -_Tp(1))\n    return (__l % 2 == 1 ? -_Tp(1) : +_Tp(1));\n    else\n    {\n        _Tp __p_lm2 = _Tp(1);\n        if (__l == 0)\n        return __p_lm2;\n\n        _Tp __p_lm1 = __x;\n        if (__l == 1)\n        return __p_lm1;\n\n        _Tp __p_l = 0;\n        for (unsigned int __ll = 2; __ll <= __l; ++__ll)\n        {\n            //  This arrangement is supposed to be better for roundoff\n            //  protection, Arfken, 2nd Ed, Eq 12.17a.\n            __p_l = _Tp(2) * __x * __p_lm1 - __p_lm2\n                - (__x * __p_lm1 - __p_lm2) / _Tp(__ll);\n            __p_lm2 = __p_lm1;\n            __p_lm1 = __p_l;\n        }\n\n        return __p_l;\n    }\n}\n\ntemplate<typename _Tp>\n_Tp\n__assoc_legendre_p(unsigned int __l, unsigned int __m, _Tp __x,\n            _Tp __phase)\n{\n\n    if (__m > __l)\n    return _Tp(0);\n    else if (isnan(__x))\n    return std::numeric_limits<_Tp>::quiet_NaN();\n    else if (__m == 0)\n    return __poly_legendre_p(__l, __x);\n    else\n    {\n        _Tp __p_mm = _Tp(1);\n        if (__m > 0)\n        {\n            //  Two square roots seem more accurate more of the time\n            //  than just one.\n            _Tp __root = std::sqrt(_Tp(1) - __x) * std::sqrt(_Tp(1) + __x);\n            _Tp __fact = _Tp(1);\n            for (unsigned int __i = 1; __i <= __m; ++__i)\n            {\n                __p_mm *= __phase * __fact * __root;\n                __fact += _Tp(2);\n            }\n        }\n        if (__l == __m)\n        return __p_mm;\n\n        _Tp __p_mp1m = _Tp(2 * __m + 1) * __x * __p_mm;\n        if (__l == __m + 1)\n        return __p_mp1m;\n\n        _Tp __p_lm2m = __p_mm;\n        _Tp __P_lm1m = __p_mp1m;\n        _Tp __p_lm = _Tp(0);\n        for (unsigned int __j = __m + 2; __j <= __l; ++__j)\n        {\n            __p_lm = (_Tp(2 * __j - 1) * __x * __P_lm1m\n                    - _Tp(__j + __m - 1) * __p_lm2m) / _Tp(__j - __m);\n            __p_lm2m = __P_lm1m;\n            __P_lm1m = __p_lm;\n        }\n\n        return __p_lm;\n    }\n}"
  },
  {
    "path": "FEAMR/sphericalHarmonics.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include <memory>\n#include <FECore/matrix.h>\n#include \"feamr_api.h\"\n\nFEAMR_API void getSphereCoords(std::vector<vec3d>& coords, std::vector<double>& theta, std::vector<double>& phi);\n\nFEAMR_API std::unique_ptr<matrix> compSH(int order, std::vector<double>& theta, std::vector<double>& phi);\n\nFEAMR_API double harmonicY(int degree, int order, double theta, double phi, int numType);\n\nFEAMR_API void reconstructODF(std::vector<double>& sphHarm, std::vector<double>& ODF, std::vector<double>& theta, std::vector<double>& phi);\n\nFEAMR_API void altGradient(int order, std::vector<double>& sphHarm, std::vector<double>& gradient);\n\nFEAMR_API void remesh(std::vector<double>& gradient, double lengthScale, double hausd, double grad, std::vector<vec3d>& nodePos, std::vector<vec3i>& elems);\nFEAMR_API void remeshFull(std::vector<double>& gradient, double lengthScale, double hausd, double grad, std::vector<vec3d>& nodePos, std::vector<vec3i>& elems);\n\n// Taken from std::assoc_legendre definition in GCC\ntemplate<typename _Tp>\n_Tp\n__assoc_legendre_p(unsigned int __l, unsigned int __m, _Tp __x,\n            _Tp __phase = _Tp(+1));\n"
  },
  {
    "path": "FEAMR/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n"
  },
  {
    "path": "FEBio/Command.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <string.h>\n\nclass Command\n{\npublic:\n\tCommand()\n\t{\n\t\tm_pszname = 0;\n\t\tm_pszdesc = 0;\n\t}\n\tvirtual ~Command() {}\n\n\tvoid SetName(const char* szname)\n\t{\n\t\tint l = (int)strlen(szname);\n\t\tassert(l);\n\t\tm_pszname = new char[l+1];\n\t\tstrcpy(m_pszname, szname);\n\t}\n\n\tvoid SetDescription(const char* szdesc)\n\t{\n\t\tint l = (int)strlen(szdesc);\n\t\tassert(l);\n\t\tm_pszdesc = new char[l+1];\n\t\tstrcpy(m_pszdesc, szdesc);\n\t}\n\n\tconst char* GetName() { return m_pszname; }\n\tconst char* GetDescription() { return m_pszdesc; }\n\n\tvirtual int run(int nargs, char** argv) = 0;\n\nprotected:\n\tchar*\tm_pszname;\n\tchar*\tm_pszdesc;\n};\n"
  },
  {
    "path": "FEBio/CommandManager.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"CommandManager.h\"\n\nCommandManager*\tCommandManager::m_pMngr;\n"
  },
  {
    "path": "FEBio/CommandManager.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"Command.h\"\n#include <list>\n\nclass CommandManager\n{\npublic:\n\tstatic CommandManager* GetInstance()\n\t{ \n\t\tstatic bool bfirst = true;\n\t\tif (bfirst) { m_pMngr = new CommandManager; bfirst = false; }\n\t\treturn m_pMngr; \n\t}\n\npublic:\n\tvoid AddCommand(Command* pcmd) { m_Cmd.push_back(pcmd); }\n\tint Size() { return (int)m_Cmd.size(); }\n\n\tCommand* Find(const char* szcmd)\n\t{\n\t\tint N = (int)m_Cmd.size();\n\t\tif (N == 0) return 0;\n\n\t\tstd::list<Command*>::iterator ic = m_Cmd.begin();\n\t\tfor (int i=0; i<N; ++i, ++ic)\n\t\t{\n\t\t\tif (strcmp(szcmd, (*ic)->GetName()) == 0) return (*ic);\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\ttypedef std::list<Command*>::iterator CmdIterator;\n\n\tCmdIterator First() { return m_Cmd.begin(); }\n\nprotected:\n\tstd::list<Command*>\tm_Cmd;\n\nprotected:\n\tstatic CommandManager* m_pMngr;\n};\n"
  },
  {
    "path": "FEBio/FEBio.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioApp.h\"\n\n//-----------------------------------------------------------------------------\n// The starting point of the application\n//\nint main(int argc, char* argv[])\n{\n\t// create the FEBio app\n\tFEBioApp febio;\n\n\t// initialize the app\n\tif (febio.Init(argc, argv) == false) return 1;\n\n\t// start the FEBio app\n\tint nret = febio.Run();\n\n\t// Don't forget to cleanup\n\tfebio.Finish();\n\n\treturn nret;\n}\n"
  },
  {
    "path": "FEBio/FEBioApp.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioApp.h\"\n#include \"console.h\"\n#include \"CommandManager.h\"\n#include <FECore/log.h>\n#include \"console.h\"\n#include \"breakpoint.h\"\n#include <FEBioLib/febio.h>\n#include <FEBioLib/version.h>\n#include \"febio_cb.h\"\n#include \"Interrupt.h\"\n\nFEBioApp* FEBioApp::m_This = nullptr;\n\nFEBioApp::FEBioApp()\n{\n\tassert(m_This == nullptr);\n\tm_This = this;\n\n\tm_fem = nullptr;\n}\n\nFEBioApp* FEBioApp::GetInstance()\n{\n\tassert(m_This);\n\treturn m_This;\n}\n\nfebio::CMDOPTIONS& FEBioApp::CommandOptions()\n{\n\treturn m_ops;\n}\n\nbool FEBioApp::Init(int argc, char* argv[])\n{\n\t// Initialize kernel\n\tFECoreKernel::SetInstance(febio::GetFECoreKernel());\n\n\t// parse the command line\n\tif (ParseCmdLine(argc, argv) == false) return false;\n\n\t// say hello\n\tConsoleStream s;\n\tif (m_ops.bsplash && (!m_ops.bsilent)) febio::Hello(s);\n\n\t// Initialize FEBio library\n\tfebio::InitLibrary();\n\n\t// copy some flags to configuration\n\tm_config.SetOutputLevel(m_ops.bsilent ? 0 : 1);\n\n\t// read the configration file if specified\n\tif (m_ops.szcnf[0])\n    {\n        fprintf(stdout, \"Reading configuration file: %s\\n\", m_ops.szcnf);\n\n\t\tif (febio::Configure(m_ops.szcnf, m_config) == false)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: An error occurred reading the configuration file.\\n\");\n\t\t\treturn false;\n\t\t}\n    }\n    else\n    {\n        fprintf(stdout, \"Starting without configuration file\\n\");\n    }\n\n    if (m_config.m_noutput != 0) fprintf(stdout, \"Default linear solver: %s\\n\", febio::GetFECoreKernel()->GetLinearSolverType());\n\n\t// read command line plugin if specified\n\tif (m_ops.szimp[0] != 0)\n\t{\n\t\tfebio::ImportPlugin(m_ops.szimp);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioApp::Configure(const char* szconfig)\n{\n\treturn febio::Configure(szconfig, m_config);\n}\n\n//-----------------------------------------------------------------------------\nint FEBioApp::Run()\n{\n\t// activate interruption handler\n\tInterruption I;\n\n\t// run FEBio either interactively or directly\n\tif (m_ops.binteractive)\n\t\treturn prompt();\n\telse\n\t\treturn RunModel();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioApp::Finish()\n{\n\tfebio::FinishLibrary();\n\n\tConsole::GetHandle()->CleanUp();\n}\n\n//-----------------------------------------------------------------------------\n// get the current model\nFEBioModel* FEBioApp::GetCurrentModel()\n{\n\treturn m_fem;\n}\n\n//-----------------------------------------------------------------------------\n// set the currently active model\nvoid FEBioApp::SetCurrentModel(FEBioModel* fem)\n{\n\tm_fem = fem;\n}\n\n//-----------------------------------------------------------------------------\n// Run an FEBio input file. \nint FEBioApp::RunModel()\n{\n\t// create the FEBioModel object\n\tFEBioModel fem;\n\tSetCurrentModel(&fem);\n\n\t// add console stream to log file\n\tif (m_ops.bsilent == false)\n\t{\n\t\tfem.GetLogFile().SetLogStream(new ConsoleStream);\n\t\tif (m_ops.bupdateTitle == false)\n\t\t\tConsole::GetHandle()->Deactivate(); // This doesn't really deactive the console, it just prevents the title from getting updated.\n\t}\n\telse\n\t\tConsole::GetHandle()->Deactivate();\n\n\t// register callbacks\n\tfem.AddCallback(update_console_cb, CB_MAJOR_ITERS | CB_INIT | CB_SOLVED | CB_STEP_ACTIVE, 0);\n\tfem.AddCallback(interrupt_cb, CB_ALWAYS, 0);\n\tfem.AddCallback(break_point_cb, CB_ALWAYS, 0);\n\n\t// set options that were passed on the command line\n\tfem.SetDebugLevel(m_ops.ndebug);\n\tfem.SetDumpLevel(m_ops.dumpLevel);\n\tfem.SetDumpStride(m_ops.dumpStride);\n\n\t// set the output filenames\n\tfem.SetLogFilename(m_ops.szlog);\n\tfem.SetPlotFilename(m_ops.szplt);\n\tfem.SetDumpFilename(m_ops.szdmp);\n\n\tfem.SetAppendOnRestart(m_ops.bappendFiles);\n\n\tif (!m_ops.boutputLog) fem.SetLogLevel(0);\n\n\t// read the input file if specified\n\tint nret = 0;\n\tif (m_ops.szfile[0])\n\t{\n\t\t// read the input file\n\t\tif (fem.Input(m_ops.szfile) == false) nret = 1;\n\t}\n\n\t// solve the model with the task and control file\n\tif (nret == 0)\n\t{\n\t\t// apply configuration overrides\n\t\tApplyConfig(fem);\n\n\t\tbool bret = febio::SolveModel(fem, m_ops.sztask, m_ops.szctrl);\n\n\t\tnret = (bret ? 0 : 1);\n\t}\n\n\t// reset the current model pointer\n\tSetCurrentModel(nullptr);\n\n\treturn nret;\n}\n\n//-----------------------------------------------------------------------------\n// apply configuration changes to model\nvoid FEBioApp::ApplyConfig(FEBioModel& fem)\n{\n\tif (m_config.m_printParams != -1)\n\t{\n\t\tfem.SetPrintParametersFlag(m_config.m_printParams != 0);\n\t}\n\tfem.ShowWarningsAndErrors(m_config.m_bshowErrors);\n}\n\n//-----------------------------------------------------------------------------\n//! Prints the FEBio prompt. If the user did not enter anything on the command\n//! line when running FEBio then commands can be entered at the FEBio prompt.\n//! This function returns the command arguments as a CMDOPTIONS structure.\nint FEBioApp::prompt()\n{\n\t// get a pointer to the console window\n\tConsole* pShell = Console::GetHandle();\n\n\t// set the title\n\tpShell->SetTitle(\"FEBio4\");\n\n\t// process commands\n\tProcessCommands();\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//!  Parses the command line and returns a CMDOPTIONS structure\n//\nbool FEBioApp::ParseCmdLine(int nargs, char* argv[])\n{\n\tfebio::CMDOPTIONS& ops = m_ops;\n\n\t// set default options\n\tops.ndebug = 0;\n\tops.bsplash = true;\n\tops.bsilent = false;\n\tops.binteractive = true;\n\tops.bappendFiles = true;\n\n\t// these flags indicate whether the corresponding file name\n\t// was defined on the command line. Otherwise, a default name will be generated.\n\tbool blog = false;\n\tbool bplt = false;\n\tbool bdmp = false;\n\tbool brun = true;\n\n\t// initialize file names\n\tops.szfile[0] = 0;\n\tops.szplt[0] = 0;\n\tops.szlog[0] = 0;\n\tops.szdmp[0] = 0;\n\tops.sztask[0] = 0;\n\tops.szctrl[0] = 0;\n\tops.szimp[0] = 0;\n\n\t// set initial configuration file name\n\tif (ops.szcnf[0] == 0)\n\t{\n\t\tchar szpath[512] = { 0 };\n\t\tfebio::get_app_path(szpath, 511);\n\t\tsnprintf(ops.szcnf, sizeof(ops.szcnf), \"%sfebio.xml\", szpath);\n\n        // if there is an febio.xml file next to the binary, we use it, otherwise\n        // we set the contig name to an empty string\n        FILE* file = fopen(ops.szcnf, \"r\");\n        if (file) \n        {\n            fclose(file);\n        }\n        else\n        {\n            ops.szcnf[0] = 0;\n        }\n\t}\n\n\t// loop over the arguments\n\tchar* sz;\n\tfor (int i=1; i<nargs; ++i)\n\t{\n\t\tsz = argv[i];\n\n\t\tif (strcmp(sz,\"-r\") == 0)\n\t\t{\n\t\t\tif (ops.sztask[0] != 0) { fprintf(stderr, \"-r is incompatible with other command line option.\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, \"restart\");\n\t\t\tstrcpy(ops.szctrl, argv[++i]);\n\t\t\tops.binteractive = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-noappend\") == 0)\n\t\t{\n\t\t\tops.bappendFiles = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-d\") == 0)\n\t\t{\n\t\t\tif (ops.sztask[0] != 0) { fprintf(stderr, \"-d is incompatible with other command line option.\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, \"diagnose\");\n\t\t\tstrcpy(ops.szctrl, argv[++i]);\n\t\t\tops.binteractive = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-p\") == 0)\n\t\t{\n\t\t\tbplt = true;\n\t\t\tstrcpy(ops.szplt, argv[++i]);\n\t\t}\n\t\telse if (strncmp(sz, \"-dump_stride\", 12) == 0)\n\t\t{\n\t\t\tif (sz[12] == '=')\n\t\t\t{\n\t\t\t\tops.dumpStride = atoi(sz + 13);\n\t\t\t\tif (ops.dumpStride < 1)\n\t\t\t\t{\n\t\t\t\t\tfprintf(stderr, \"FATAL ERROR: invalid dump stride.\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: missing '=' after -dump_stride.\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (strncmp(sz, \"-dump\", 5) == 0)\n\t\t{\n\t\t\tops.dumpLevel = FE_DUMP_MAJOR_ITRS;\n\t\t\tif (sz[5] == '=') ops.dumpLevel = atoi(sz + 6);\n\t\t\tif ((ops.dumpLevel < 0) || (ops.dumpLevel > 3))\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: invalid restart level.\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (i<nargs - 1)\n\t\t\t{\n\t\t\t\tchar* szi = argv[i + 1];\n\t\t\t\tif (szi[0] != '-')\n\t\t\t\t{\n\t\t\t\t\t// assume this is the name of the dump file\n\t\t\t\t\tstrcpy(ops.szdmp, argv[++i]);\n\t\t\t\t\tbdmp = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(sz, \"-o\") == 0)\n\t\t{\n\t\t\tblog = true;\n\t\t\tstrcpy(ops.szlog, argv[++i]);\n\t\t}\n\t\telse if (strcmp(sz, \"-i\") == 0)\n\t\t{\n\t\t\t++i;\n\t\t\tconst char* szext = strrchr(argv[i], '.');\n\t\t\tif (szext == 0)\n\t\t\t{\n\t\t\t\t// we assume a default extension of .feb if none is provided\n\t\t\t\tsnprintf(ops.szfile, sizeof(ops.szfile), \"%s.feb\", argv[i]);\n\t\t\t}\n\t\t\telse strcpy(ops.szfile, argv[i]);\n\t\t\tops.binteractive = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-s\") == 0)\n\t\t{\n\t\t\tif (ops.sztask[0] != 0) { fprintf(stderr, \"-s is incompatible with other command line option.\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, \"optimize\");\n\t\t\tstrcpy(ops.szctrl, argv[++i]);\n\t\t}\n\t\telse if ((strcmp(sz, \"-g\") == 0) || (strcmp(sz, \"-g1\") == 0))\n\t\t{\n\t\t\tops.ndebug = 1;\n\t\t}\n\t\telse if (strcmp(sz, \"-g2\") == 0)\n\t\t{\n\t\t\tops.ndebug = 2;\n\t\t}\n\t\telse if (strcmp(sz, \"-nosplash\") == 0)\n\t\t{\n\t\t\t// don't show the welcome message\n\t\t\tops.bsplash = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-silent\") == 0)\n\t\t{\n\t\t\t// no output to screen\n\t\t\tops.bsilent = true;\n\t\t}\n\t\telse if (strcmp(sz, \"-no_title\") == 0)\n\t\t{\n\t\t\tops.bupdateTitle = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-no_log\") == 0)\n\t\t{\n\t\t\tops.boutputLog = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-cnf\") == 0)\t// obsolete: use -config instead\n\t\t{\n\t\t\tstrcpy(ops.szcnf, argv[++i]);\n\t\t}\n\t\telse if (strcmp(sz, \"-config\") == 0)\n\t\t{\n\t\t\tstrcpy(ops.szcnf, argv[++i]);\n\t\t}\n\t\telse if (strcmp(sz, \"-noconfig\") == 0)\n\t\t{\n\t\t\tops.szcnf[0] = 0;\n\t\t}\n\t\telse if (strncmp(sz, \"-task\", 5) == 0)\n\t\t{\n\t\t\tif (sz[5] != '=') { fprintf(stderr, \"command line error when parsing task\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, sz+6);\n\n\t\t\tif (i<nargs-1)\n\t\t\t{\n\t\t\t\tchar* szi = argv[i+1];\n\t\t\t\tif (szi[0] != '-')\n\t\t\t\t{\n\t\t\t\t\t// assume this is a control file for the specified task\n\t\t\t\t\tstrcpy(ops.szctrl, argv[++i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(sz, \"-break\") == 0)\n\t\t{\n\t\t\tchar szbuf[32]={0};\n\t\t\tstrcpy(szbuf, argv[++i]);\n\n\t\t\tadd_break_point(szbuf);\n\t\t}\n\t\telse if (strcmp(sz, \"-info\")==0)\n\t\t{\n\t\t\tFILE* fp = stdout;\n\t\t\tif ((i<nargs-1) && (argv[i+1][0] != '-'))\n\t\t\t{\n\t\t\t\tfp = fopen(argv[++i], \"wt\");\n\t\t\t\tif (fp == 0) fp = stdout;\n\t\t\t}\n\t\t\tfprintf(fp, \"compiled on \" __DATE__ \"\\n\");\n\n\t\t\tchar* szver = febio::getVersionString();\n\n#ifndef NDEBUG\n\t\t\tfprintf(fp, \"FEBio version  = %s (DEBUG)\\n\", szver);\n#else\n\t\t\tfprintf(fp, \"FEBio version  = %s\\n\", szver);\n#endif\n\t\t\tif (fp != stdout) fclose(fp);\n\t\t}\n\t\telse if (strcmp(sz, \"-norun\") == 0)\n\t\t{\n\t\t\tbrun = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-import\") == 0)\n\t\t{\n\t\t\tif ((i < nargs - 1) && (argv[i+1][0] != '-'))\n\t\t\t\tstrcpy(ops.szimp, argv[++i]);\n\t\t\telse\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: insufficient number of arguments for -import.\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (strncmp(sz, \"-output_negative_jacobians\", 26) == 0)\n\t\t{\n\t\t\tint n = -1;\n\t\t\tif (sz[26] == '=')\n\t\t\t{\n\t\t\t\tconst char* szval = sz + 27;\n\t\t\t\tn = atoi(szval);\n\t\t\t}\n\t\t\tNegativeJacobian::m_maxout = n;\n\t\t}\n\t\telse if (sz[0] == '-')\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: Invalid command line option '%s'.\\n\", sz);\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// if no input file is given yet, we'll assume this is the input file\n\t\t\tif (ops.szfile[0] == 0)\n\t\t\t{\n\t\t\t\tconst char* szext = strrchr(sz, '.');\n\t\t\t\tif (szext == 0)\n\t\t\t\t{\n\t\t\t\t\t// we assume a default extension of .feb if none is provided\n\t\t\t\t\tsnprintf(ops.szfile, sizeof(ops.szfile), \"%s.feb\", sz);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstrcpy(ops.szfile, sz);\n\t\t\t\t}\n\t\t\t\tops.binteractive = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: Invalid command line option '%s'\\n\", sz);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// do some sanity checks\n\tif (strcmp(ops.sztask, \"optimize\") == 0)\n\t{\n\t\t// make sure we have an input file\n\t\tif (ops.szfile[0]==0)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: no model input file was defined (use -i to define the model input file)\\n\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// if no task is defined, we assume a std solve is wanted\n\tif (ops.sztask[0] == 0) strcpy(ops.sztask, \"solve\");\n\n\t// derive the other filenames\n\tif (ops.szfile[0])\n\t{\n\t\tchar szbase[256]; strcpy(szbase, ops.szfile);\n\t\tchar* ch = strrchr(szbase, '.');\n\t\tif (ch) *ch = 0;\n\n\t\tchar szlogbase[256];\n\t\tif (ops.szctrl[0])\n\t\t{\n\t\t\tstrcpy(szlogbase, ops.szctrl);\n\t\t\tch = strrchr(szlogbase, '.');\n\t\t\tif (ch) *ch = 0;\n\t\t}\n\t\telse strcpy(szlogbase, szbase);\n\n\t\tif (!blog) snprintf(ops.szlog, sizeof(ops.szlog), \"%s.log\", szlogbase);\n\t\tif (!bplt) snprintf(ops.szplt, sizeof(ops.szplt), \"%s.xplt\", szbase);\n\t\tif (!bdmp) snprintf(ops.szdmp, sizeof(ops.szdmp), \"%s.dmp\", szbase);\n\t}\n\telse if (ops.szctrl[0])\n\t{\n\t\tchar szbase[256]; strcpy(szbase, ops.szfile);\n\t\tstrcpy(szbase, ops.szctrl);\n\t\tchar* ch = strrchr(szbase, '.');\n\t\tif (ch) *ch = 0;\n\n\t\tif (!blog) snprintf(ops.szlog, sizeof(ops.szlog), \"%s.log\", szbase);\n\t\tif (!bplt) snprintf(ops.szplt, sizeof(ops.szplt), \"%s.xplt\", szbase);\n\t\tif (!bdmp) snprintf(ops.szdmp, sizeof(ops.szdmp), \"%s.dmp\", szbase);\n\t}\n\n\treturn brun;\n}\n\nvoid FEBioApp::ProcessCommands()\n{\n\t// get a pointer to the console window\n\tConsole* pShell = Console::GetHandle();\n\n\t// get the command manager\n\tCommandManager* CM = CommandManager::GetInstance();\n\n\t// enter command loop\n\tint nargs;\n\tchar* argv[32];\n\twhile (1)\n\t{\n\t\t// get a command from the shell\n\t\tpShell->GetCommand(nargs, argv);\n\t\tif (nargs > 0)\n\t\t{\n\t\t\t// find the command that has this name\n\t\t\tCommand* pcmd = CM->Find(argv[0]);\n\t\t\tif (pcmd)\n\t\t\t{\n\t\t\t\tint nret = pcmd->run(nargs, argv);\n\t\t\t\tif (nret == 1) break;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tprintf(\"Unknown command: %s\\n\", argv[0]);\n\t\t\t}\n\t\t}\n        else if (nargs == 0) break;\n\n\t\t// make sure to clear the progress on the console\n\t\tFEBioModel* fem = GetCurrentModel();\n\t\tif ((fem == nullptr) || fem->IsSolved()) pShell->SetProgress(0);\n\t}\n}\n"
  },
  {
    "path": "FEBio/FEBioApp.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioLib/cmdoptions.h>\n#include <FEBioLib/FEBioConfig.h>\n\nclass FEBioModel;\n\nclass FEBioApp\n{\npublic:\n\tFEBioApp();\n\n\tbool Init(int nargs, char* argv[]);\n\n\tbool Configure(const char* szconfig);\n\n\tint Run();\n\n\tvoid Finish();\n\n\tvoid ProcessCommands();\n\n\tfebio::CMDOPTIONS& CommandOptions();\n\n\tbool ParseCmdLine(int argc, char* argv[]);\n\n\t// run an febio model\n\tint RunModel();\n\npublic:\n\t// get the current model\n\tFEBioModel* GetCurrentModel();\n\nprotected:\n\t// show FEBio prompt\n\tint prompt();\n\n\t// set the currently active model\n\tvoid SetCurrentModel(FEBioModel* fem);\n\n\t// apply configuration changes to model\n\tvoid ApplyConfig(FEBioModel& fem);\n\npublic:\n\tstatic FEBioApp* GetInstance();\n\nprivate:\n\tfebio::CMDOPTIONS\tm_ops;\t\t\t\t// command line options\n\tFEBioConfig\tm_config;\t\t\t// configuration options\n\n\tFEBioModel*\t\tm_fem;\t\t\t// current model (or null if not model is running)\n\n\tstatic FEBioApp*\tm_This;\n};\n"
  },
  {
    "path": "FEBio/FEBioCommand.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <FEBioLib/plugin.h>\n\n// TODO: On Windows the GetCurrentTime macro gets in here via plugin.h. \n// I need to look into how to prevent this\n#ifdef GetCurrentTime\n#undef GetCurrentTime\n#endif\n#include <cstdlib>\n#include <FEBioLib/FEBioModel.h>\n#include <FEBioLib/version.h>\n#include <FECore/FEException.h>\n#include <FECore/FESolver.h>\n#include <FECore/CompactMatrix.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEGlobalMatrix.h>\n#include \"FEBioCommand.h\"\n#include \"console.h\"\n#include <FEBioLib/cmdoptions.h>\n#include <FEBioLib/febio.h>\n#include \"FEBioApp.h\"\n#include \"breakpoint.h\"\n#include <iostream>\n#include <fstream>\n\n\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\n#define szcmp    _stricmp\n#else\n#define szcmp    strcmp\n#endif\n\n//-----------------------------------------------------------------------------\nREGISTER_COMMAND(FEBioCmd_break        , \"break\"  , \"add a break point\");\nREGISTER_COMMAND(FEBioCmd_breaks       , \"breaks\" , \"print list of break points\");\nREGISTER_COMMAND(FEBioCmd_clear_breaks , \"clear\"  , \"clear one or all break points\");\nREGISTER_COMMAND(FEBioCmd_Config       , \"config\" , \"(re-)load a FEBio configuration file\");\nREGISTER_COMMAND(FEBioCmd_Cont         , \"cont\"   , \"continues the current model\");\nREGISTER_COMMAND(FEBioCmd_Conv         , \"conv\"   , \"force conversion of iteration\");\nREGISTER_COMMAND(FEBioCmd_Debug        , \"debug\"  , \"toggle debug mode\");\nREGISTER_COMMAND(FEBioCmd_Events       , \"events\" , \"print list of events\");\nREGISTER_COMMAND(FEBioCmd_Fail         , \"fail\"   , \"force iteratoin failer\");\nREGISTER_COMMAND(FEBioCmd_Help         , \"help\"   , \"print available commands\");\nREGISTER_COMMAND(FEBioCmd_hist         , \"hist\"   , \"lists history of commands\");\nREGISTER_COMMAND(FEBioCmd_LoadPlugin   , \"import\" , \"load a plugin\");\nREGISTER_COMMAND(FEBioCmd_Plot         , \"plot\"   , \"store current state to plot file\");\nREGISTER_COMMAND(FEBioCmd_out          , \"out\"    , \"write matrix and rhs file\");\nREGISTER_COMMAND(FEBioCmd_Plugins      , \"plugins\", \"list the plugins that are loaded\");\nREGISTER_COMMAND(FEBioCmd_Print        , \"print\"  , \"print values of variables\");\nREGISTER_COMMAND(FEBioCmd_Quit         , \"quit\"   , \"terminate the run and quit\");\nREGISTER_COMMAND(FEBioCmd_Restart      , \"restart\", \"toggle restart mode\");\nREGISTER_COMMAND(FEBioCmd_Run          , \"run\"    , \"run an FEBio input file\");\nREGISTER_COMMAND(FEBioCmd_set          , \"set\"    , \"set value of some model and config parameters\");\nREGISTER_COMMAND(FEBioCmd_svg          , \"svg\"    , \"write matrix sparsity pattern to svg file\");\nREGISTER_COMMAND(FEBioCmd_Time         , \"time\"   , \"print progress time statistics\");\nREGISTER_COMMAND(FEBioCmd_UnLoadPlugin , \"unload\" , \"unload a plugin\");\nREGISTER_COMMAND(FEBioCmd_Version      , \"version\", \"print version information\");\nREGISTER_COMMAND(FEBioCmd_where        , \"where\"  , \"current callback event\");\nREGISTER_COMMAND(FEBioCmd_list         , \"list\"   , \"list factory classes\");\n\nint need_active_model()\n{\n\tprintf(\"No active model.\\n\");\n\treturn 0;\n}\n\nint model_already_running()\n{\n\tprintf(\"A model is running. You must stop the active model before running this command.\\n\");\n\treturn 0;\n}\n\nint invalid_nr_args()\n{\n\tprintf(\"Invalid number of arguments.\\n\");\n\treturn 0;\n}\n\nint unknown_args()\n{\n\tprintf(\"Unrecognized arguments.\\n\");\n\treturn 0;\n}\n\n\n//-----------------------------------------------------------------------------\nFEBioCommand::FEBioCommand()\n{\n}\n\nFEBioCommand::~FEBioCommand()\n{\n}\n\nFEBioModel* FEBioCommand::GetFEM()\n{\n\treturn FEBioApp::GetInstance()->GetCurrentModel();\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Run::run(int nargs, char** argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem) return model_already_running();\n\n\tFEBioApp* febio = FEBioApp::GetInstance();\n\n\tif (febio->ParseCmdLine(nargs, argv) == false) return 0;\n\n\t// run FEBio on the ops\n\tfebio->RunModel();\n\n\t// reset the title after computation.\n\tConsole* pShell = Console::GetHandle();\n\tpShell->SetTitle(\"FEBio4\");\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Restart::run(int nargs, char** argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr) return need_active_model();\n\n\tint dumpLevel = fem->GetDumpLevel();\n\tif (dumpLevel == FE_DUMP_NEVER) fem->SetDumpLevel(FE_DUMP_MAJOR_ITRS);\n\telse fem->SetDumpLevel(FE_DUMP_NEVER);\n\n\tdumpLevel = fem->GetDumpLevel();\n\tprintf(\"Restart level set to: \");\n\tswitch (dumpLevel)\n\t{\n\tcase FE_DUMP_NEVER      : printf(\"NEVER (0)\\n\"); break;\n\tcase FE_DUMP_MAJOR_ITRS : printf(\"MAJOR_ITRS (1)\\n\"); break;\n\tcase FE_DUMP_STEP       : printf(\"STEP (2)\\n\"); break;\n\tcase FE_DUMP_MUST_POINTS: printf(\"MUST POINTS (3)\\n\"); break;\n\tdefault:\n\t\tprintf(\"(unknown value)\\n\");\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_LoadPlugin::run(int nargs, char** argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem) return model_already_running();\n\n\tif (nargs < 2) fprintf(stderr, \"missing file name\\n\");\n\telse febio::ImportPlugin(argv[1]);\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_UnLoadPlugin::run(int nargs, char* argv[])\n{\t\n\tFEBioModel* fem = GetFEM();\n\tif (fem) return model_already_running();\n\n\tFEBioPluginManager* PM = FEBioPluginManager::GetInstance();\n\tif (PM == 0) return -1;\n\n\tif (nargs == 1)\n\t{\n\t\t// unload all plugins\n\t\twhile (PM->Plugins() > 0)\n\t\t{\n\t\t\tconst FEBioPlugin& pl = PM->GetPlugin(0);\n\t\t\tstring sname = pl.GetName();\n\t\t\tbool b = PM->UnloadPlugin(0);\n\t\t\tif (b) fprintf(stdout, \"Success unloading %s\\n\", sname.c_str());\n\t\t\telse fprintf(stdout, \"Failed unloading %s\\n\", sname.c_str());\n\t\t}\n\t}\n\telse if (nargs == 2)\n\t{\n\t\tconst char* c = argv[1];\n\t\tif (c[0] == '%')\n\t\t{\n\t\t\tint n = atoi(c+1);\n\t\t\tif ((n > 0) && (n <= PM->Plugins()))\n\t\t\t{\n\t\t\t\tconst FEBioPlugin& pl = PM->GetPlugin(n - 1);\n\t\t\t\tstring sname = pl.GetName();\n\t\t\t\tbool b = PM->UnloadPlugin(n - 1);\n\t\t\t\tif (b) fprintf(stdout, \"Success unloading %s\\n\", sname.c_str());\n\t\t\t\telse fprintf(stdout, \"Failed unloading %s\\n\", sname.c_str());\n\t\t\t}\n\t\t\telse fprintf(stderr, \"Invalid plugin index\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbool b = PM->UnloadPlugin(argv[1]);\n\t\t\tif (b) fprintf(stdout, \"Success unloading %s\\n\", argv[1]);\n\t\t\telse fprintf(stdout, \"Failed unloading %s\\n\", argv[1]);\n\t\t}\n\t}\n\telse fprintf(stderr, \"syntax error\\n\");\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Version::run(int nargs, char** argv)\n{\n\tchar* szver = febio::getVersionString();\n\n#ifndef NDEBUG\n\tfprintf(stderr, \"\\nFEBio version %s (DEBUG)\\n\", szver);\n#else\n\tfprintf(stderr, \"\\nFEBio version %s\\n\", szver);\n#endif\n\tfprintf(stderr, \"SDK Version %d.%d\\n\", FE_SDK_MAJOR_VERSION, FE_SDK_SUB_VERSION);\n\tfprintf(stderr, \"compiled on \" __DATE__ \"\\n\\n\");\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Help::run(int nargs, char** argv)\n{\n\tCommandManager* pCM = CommandManager::GetInstance();\n\tint N = pCM->Size();\n\tif (N == 0) return 0;\n\n\tprintf(\"\\nCommand overview:\\n\");\n\n\tCommandManager::CmdIterator it = pCM->First();\n\tfor (int i=0; i<N; ++i, ++it)\n\t{\n\t\tconst char* szn = (*it)->GetName();\n\t\tint l = (int)strlen(szn);\n\t\tprintf(\"\\t%s \", szn);\n\t\twhile (l++ - 15 < 0) putchar('.');\n\t\tconst char* szd = (*it)->GetDescription();\n\t\tprintf(\" : %s\\n\", szd);\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Events::run(int nargs, char** argv)\n{\n\tprintf(\"\\n\");\n\tprintf(\"\\tALWAYS        : break on any event.\\n\");\n\tprintf(\"\\tINIT          : break after model initialization.\\n\");\n\tprintf(\"\\tSTEP_ACTIVE   : break after step activation.\\n\");\n\tprintf(\"\\tMAJOR_ITERS   : break after major iteration converged.\\n\");\n\tprintf(\"\\tMINOR_ITERS   : break after minor iteration.\\n\");\n\tprintf(\"\\tSOLVED        : break after model is solved.\\n\");\n\tprintf(\"\\tUPDATE_TIME   : break before time is incremented.\\n\");\n\tprintf(\"\\tAUGMENT       : break before augmentation.\\n\");\n\tprintf(\"\\tSTEP_SOLVED   : break after step is solved.\\n\");\n\tprintf(\"\\tMATRIX_REFORM : break after global matrix is reformed.\\n\");\n\tprintf(\"\\n\");\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Config::run(int nargs, char* argv[])\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem) return model_already_running();\n\n\tFEBioApp* feApp = FEBioApp::GetInstance();\n\tfebio::CMDOPTIONS& ops = feApp->CommandOptions();\n\n\tif (nargs == 1)\n\t{\n\t\tfeApp->Configure(ops.szcnf);\n\t}\n\telse if (nargs == 2)\n\t{\n\t\tsnprintf(ops.szcnf, sizeof(ops.szcnf), \"%s\", argv[1]);\n\t\tfeApp->Configure(ops.szcnf);\n\t}\n\telse return invalid_nr_args();\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Plugins::run(int nargs, char** argv)\n{\n\tFEBioPluginManager* PM = FEBioPluginManager::GetInstance();\n\tif (PM == 0) return 0;\n\n\tint NP = PM->Plugins();\n\tif (NP == 0)\n\t{\n\t\tfprintf(stdout, \"no plugins loaded\\n\");\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < NP; ++i)\n\t\t{\n\t\t\tconst FEBioPlugin& pl = PM->GetPlugin(i);\n\t\t\tfprintf(stdout, \"%%%d: %s\\n\", i + 1, pl.GetName());\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nclass ExitRequest : public std::runtime_error\n{\npublic:\n\tExitRequest() throw() : std::runtime_error(\"Early termination by user request\") {}\n};\n\nint FEBioCmd_Quit::run(int nargs, char** argv)\n{\n\tif (GetFEM()) throw ExitRequest();\n\treturn 1;\n}\n\n//-----------------------------------------------------------------------------\n\nint FEBioCmd_Cont::run(int nargs, char** argv)\n{\n\tif (GetFEM() == nullptr) return need_active_model();\n\treturn 1;\n}\n\n//-----------------------------------------------------------------------------\n\nint FEBioCmd_Conv::run(int nargs, char **argv)\n{\n\tif (GetFEM() == nullptr) return need_active_model();\n\tthrow ForceConversion();\n\treturn 1;\n}\n\n//-----------------------------------------------------------------------------\n\nint FEBioCmd_Debug::run(int nargs, char** argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr)\n\t{\n\t\tprintf(\"No active model.\\n\");\n\t\treturn 0;\n\t}\n\n\tFEAnalysis* pstep = fem->GetCurrentStep();\n\tint ndebug = fem->GetDebugLevel();\n\tif (nargs == 1) ndebug = (ndebug? 0 : 1);\n\telse\n\t{\n\t\tif      (strcmp(argv[1], \"on\" ) == 0) ndebug = 1;\n\t\telse if (strcmp(argv[1], \"off\") == 0) ndebug = 0;\n\t\telse { fprintf(stderr, \"%s is not a valid option for debug.\\n\", argv[1]); return 0; }\n\t}\n\tfem->SetDebugLevel(ndebug);\n\n\tprintf(\"Debug mode is %s\\n\", (ndebug?\"on\":\"off\"));\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n\nint FEBioCmd_Fail::run(int nargs, char **argv)\n{\n\tthrow IterationFailure();\n}\n\n//-----------------------------------------------------------------------------\n\nint FEBioCmd_Plot::run(int nargs, char **argv)\n{\n\tassert(false);\n\treturn 1;\n}\n\n//-----------------------------------------------------------------------------\n\nint FEBioCmd_Print::run(int nargs, char **argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr) return need_active_model();\n\n\tFEAnalysis* pstep = fem->GetCurrentStep();\n\n\tif (nargs >= 2)\n\t{\n\t\tif (strcmp(argv[1], \"time\") == 0)\n\t\t{\n\t\t\tprintf(\"Time : %lg\\n\", fem->GetCurrentTime());\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// assume it is a material parameter\n\t\t\tFEParamValue val = fem->GetParameterValue(ParamString(argv[1]));\n\t\t\tif (val.isValid())\n\t\t\t{\n\t\t\t\tswitch (val.type())\n\t\t\t\t{\n\t\t\t\tcase FE_PARAM_DOUBLE: printf(\"%lg\\n\", val.value<double>()); break;\n\t\t\t\tdefault:\n\t\t\t\t\tprintf(\"(cannot print value)\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tprintf(\"The variable %s is not recognized\\n\", argv[1]);\n\t\t\t}\n\t\t}\n\t}\n\telse invalid_nr_args();\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_Time::run(int nargs, char **argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr) return need_active_model();\n\n\tdouble sec = fem->GetSolveTimer().peek();\n\tdouble sec0 = sec;\n\n\tint nhour, nmin, nsec;\n\n\tnhour = (int) (sec / 3600.0); sec -= nhour*3600;\n\tnmin  = (int) (sec /   60.0); sec -= nmin*60;\n\tnsec  = (int) (sec);\n\tprintf(\"Elapsed time       :  %d:%02d:%02d\\n\", nhour, nmin, nsec);\n\n\tdouble endtime = fem->GetEndTime();\n\n\tFETimeInfo& tp = fem->GetTime();\n\tdouble pct = (tp.currentTime - tp.timeIncrement) / endtime;\n\tif (pct != 0)\n\t{\n\t\tdouble sec1 = sec0*(1.0/pct - 1.0);\n\t\tnhour = (int) (sec1 / 3600.0); sec1 -= nhour*3600;\n\t\tnmin  = (int) (sec1 /   60.0); sec1 -= nmin*60;\n\t\tnsec  = (int) (sec1);\n\t\tprintf(\"Est. time remaining:  %d:%02d:%02d\\n\", nhour, nmin, nsec);\n\t}\n\telse\n\t\tprintf(\"Est. time remaining:  (not available)\\n\");\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_svg::run(int nargs, char **argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr) return need_active_model();\n\n\tFESolver* solver = fem->GetCurrentStep()->GetFESolver();\n\tSparseMatrix* M = solver->GetStiffnessMatrix()->GetSparseMatrixPtr();\n\tstd::vector<double> R = solver->GetLoadVector();\n\tCompactMatrix* A = dynamic_cast<CompactMatrix*>(M);\n\tif (A && (fem->GetFileTitle().empty() == false))\n\t{\n\t\tint rows = A->Rows();\n\t\tint cols = A->Columns();\n\n\t\tint i0 = 0, j0 = 0;\n\t\tint i1 = -1, j1 = -1;\n\t\tif (nargs == 3)\n\t\t{\n\t\t\ti1 = atoi(argv[1]);\n\t\t\tif (i1 < 0) { i0 = rows + i1; i1 = rows - 1; }\n\t\t\tj1 = atoi(argv[2]);\n\t\t\tif (j1 < 0) { j0 = cols + j1; j1 = cols - 1; }\n\t\t}\n\n\t\tconst char* szfile = fem->GetFileTitle().c_str();\n\t\tchar buf[1024] = { 0 }, szsvg[1024] = { 0 };\n\t\tstrcpy(buf, szfile);\n\t\tchar* ch = strrchr(buf, '.');\n\t\tif (ch) *ch = 0;\n\t\tsnprintf(szsvg, sizeof(szsvg), \"%s.svg\", buf);\n\n\t\tstd::filebuf fb;\n\t\tfb.open(szsvg, std::ios::out);\n\t\tstd::ostream out(&fb);\n\t\tfebio::print_svg(A, out, i0, j0, i1, j1);\n\t\tfb.close();\n\n\t\tcout << \"\\nFile written \" << szsvg << endl;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_out::run(int nargs, char **argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr) return need_active_model();\n\n\tint mode = 0; // binary\n\tif (nargs > 1)\n\t{\n\t\tif (strcmp(argv[1], \"-txt\") == 0) mode = 1; // text \n\t}\n\n\tFESolver* solver = fem->GetCurrentStep()->GetFESolver();\n\tSparseMatrix* M = solver->GetStiffnessMatrix()->GetSparseMatrixPtr();\n\tstd::vector<double> R = solver->GetLoadVector();\n\tCompactMatrix* A = dynamic_cast<CompactMatrix*>(M);\n\tif (A && (fem->GetFileTitle().empty() == false))\n\t{\n\t\tconst char* szfile = fem->GetFileTitle().c_str();\n\t\tchar buf[1024] = { 0 }, szK[1024] = { 0 }, szR[1024] = { 0 };\n\t\tstrcpy(buf, szfile);\n\t\tchar* ch = strrchr(buf, '.');\n\t\tif (ch) *ch = 0;\n\t\tsnprintf(szK, sizeof(szK), \"%s.out\", buf);\n\t\tsnprintf(szR, sizeof(szR), \"%s_rhs.out\", buf);\n\n\t\tfebio::write_hb(*A, szK, mode);\n\t\tfebio::write_vector(R, szR, mode);\n\n\t\tcout << \"\\nFiles written: \" << szK << \", \" << szR << endl;\n\t}\n\telse cout << \"ERROR: Don't know how to write matrix format.\\n\";\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_where::run(int nargs, char **argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (fem == nullptr) return need_active_model();\n\n\tunsigned int nevent = fem->CurrentEvent();\n\tif (nevent == 0) cout << \"Not inside callback event\\n\";\n\telse cout << \"Callback event: \";\n\n\tswitch (nevent)\n\t{\n\tcase CB_INIT         : cout << \"INIT\"; break;\n\tcase CB_STEP_ACTIVE  : cout << \"STEP_ACTIVE\"; break;\n\tcase CB_MAJOR_ITERS  : cout << \"MAJOR_ITERS\"; break;\n\tcase CB_MINOR_ITERS  : cout << \"MINOR_ITERS\"; break;\n\tcase CB_SOLVED       : cout << \"SOLVED\"; break;\n\tcase CB_UPDATE_TIME  : cout << \"UPDATE_TIME\"; break;\n\tcase CB_AUGMENT      : cout << \"AUGMENT\"; break;\n\tcase CB_STEP_SOLVED  : cout << \"STEP_SOLVED\"; break;\n\tcase CB_MATRIX_REFORM: cout << \"MATRIX_REFORM\"; break;\n\tdefault:\n\t\tcout << \"(unknown)\";\n\t}\n\tcout << endl;\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioCmd_break::run(int nargs, char **argv)\n{\n\tif (nargs != 2) return invalid_nr_args();\n\n\tconst char* szbuf = argv[1];\n\n\tadd_break_point(szbuf);\n\n\treturn 0;\n}\n\nint FEBioCmd_breaks::run(int nargs, char **argv)\n{\n\tprint_break_points();\n\treturn 0;\n}\n\nint FEBioCmd_clear_breaks::run(int nargs, char **argv)\n{\n\tif (nargs == 1)\n\t\tclear_break_points(-1);\n\telse if (nargs == 2)\n\t{\n\t\tint bp = atoi(argv[1]);\n\t\tclear_break_points(bp - 1);\n\t}\n\treturn 0;\n}\n\nint FEBioCmd_hist::run(int nargs, char **argv)\n{\n\tConsole* console = Console::GetHandle();\n\tvector<string> hist = console->GetHistory();\n\tint i = 1;\n\tfor (string& s : hist)\n\t{\n\t\tcout << \"%\" << i++ << \": \" << s << endl;\n\t}\n\n\treturn 0;\n}\n\nconst char* super_id_to_string(SUPER_CLASS_ID superID)\n{\n\tconst char* szclass = FECoreKernel::SuperClassString(superID);\n\tif (szclass == nullptr) szclass = \"(unknown)\";\n\treturn szclass;\n}\n\nint FEBioCmd_list::run(int nargs, char** argv)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\n\tconst char* szmod  = 0;\n\tconst char* sztype = 0;\n\tconst char* szpat = 0;\n\tint nmax = 0;\n\tint lpat = 0;\n\n\t// process argumets\n\tstd::cin.clear();\n\tint nc = 1;\n\twhile (nc < nargs)\n\t{\n\t\tif (strcmp(argv[nc], \"-m\") == 0)\n\t\t{\n\t\t\tif (nargs == 2)\n\t\t\t{\n\t\t\t\tint mods = fecore.Modules();\n\t\t\t\tfor (int i = 0; i < mods; ++i)\n\t\t\t\t{\n\t\t\t\t\tconst char* szmod = fecore.GetModuleName(i);\n\t\t\t\t\tprintf(\"%d: %s\\n\", i+1, szmod);\n\t\t\t\t}\n\t\t\t\tprintf(\"\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (nargs <= nc + 1) return invalid_nr_args();\n\t\t\tszmod = argv[++nc];\n\t\t}\n\t\telse if (strcmp(argv[nc], \"-c\") == 0)\n\t\t{\n\t\t\tif (nargs <= nc + 1) return invalid_nr_args();\n\t\t\tsztype = argv[++nc];\n\t\t}\n\t\telse if (strcmp(argv[nc], \"-n\") == 0)\n\t\t{\n\t\t\tif (nargs <= nc + 1) return invalid_nr_args();\n\t\t\tnmax = atoi(argv[++nc]);\n\t\t}\n\t\telse if (strcmp(argv[nc], \"-s\") == 0)\n\t\t{\n\t\t\tif (nargs <= nc + 1) return invalid_nr_args();\n\t\t\tszpat = argv[++nc];\n\t\t\tlpat = (int)strlen(szpat);\n\t\t\tif (lpat == 0) szpat = 0;\n\t\t}\n\t\telse return unknown_args();\n\t\t++nc;\n\t}\n\n\tint facs = fecore.FactoryClasses();\n\tint n = 1, m = 0;\n\tfor (int i = 0; i < facs; ++i)\n\t{\n\t\tconst FECoreFactory* fac = fecore.GetFactoryClass(i);\n\t\tif (fac == nullptr) { printf(\"%3d: %s\\n\", n++, \"(null)\"); m++; }\n\t\telse\n\t\t{\n\t\t\tSUPER_CLASS_ID superID = fac->GetSuperClassID();\n\t\t\tconst char* szclass = super_id_to_string(superID);\n\n\t\t\tint moduleId = fac->GetModuleID();\n\t\t\tconst char* szmodule = fecore.GetModuleNameFromId(moduleId);\n\t\t\tif ((szmod == 0) || (szmodule && (strcmp(szmodule, szmod) == 0)))\n\t\t\t{\n\t\t\t\tif ((sztype == 0) || (szcmp(szclass, sztype) == 0))\n\t\t\t\t{\n\t\t\t\t\tconst char* facType = fac->GetTypeStr();\n\t\t\t\t\tif ((szpat == 0) || (strstr(facType, szpat)))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (szmodule) printf(\"%3d: %s.%s [%s]\\n\", n++, szmodule, facType, szclass);\n\t\t\t\t\t\telse printf(\"%3d: %s [%s]\\n\", n++, facType, szclass);\n\t\t\t\t\t\tm++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ((nmax != 0) && (m >= nmax))\n\t\t{\n\t\t\tchar ch = 0;\n\t\t\tdo {\n\t\t\t\tprintf(\"Continue (y or n)?\");\n\t\t\t\tstd::cin.get(ch);\n\t\t\t} while ((ch != 'y') && (ch != 'n'));\n\t\t\tm = 0;\n\t\t\tif (ch == 'n') break;\n\t\t}\n\t}\n\tprintf(\"\\n\");\n\n\treturn 0;\n}\n\nint FEBioCmd_set::run(int nargs, char** argv)\n{\n\tFEBioModel* fem = GetFEM();\n\tif (nargs == 1)\n\t{\n\t\tprintf(\"output_negative_jacobians = %d\\n\", NegativeJacobian::m_maxout);\n\t\tif (fem)\n\t\t{\n\t\t\tprintf(\"print_model_params        = %d\\n\", (fem->GetPrintParametersFlag() ? 1 : 0));\n\t\t\tprintf(\"show_warnings_and_errors  = %d\\n\", (fem->ShowWarningsAndErrors() ? 1 : 0));\n\t\t}\n\t\treturn 0;\n\t}\n\n\tif (nargs != 3)\n\t{\n\t\tprintf(\"insufficient arguments.\");\n\t\treturn 0;\n\t}\n\n\tint n = atoi(argv[2]);\n\n\tif (strcmp(argv[1], \"output_negative_jacobians\") == 0)\n\t{\n\t\tNegativeJacobian::m_maxout = n;\n\t\tprintf(\"output_negative_jacobians = %d\", n);\n\t}\n\telse if (fem && strcmp(argv[1], \"print_model_params\") == 0)\n\t{\n\t\tfem->SetPrintParametersFlag(n != 0);\n\t\tprintf(\"print_model_params = %d\", n);\n\t}\n\telse if (fem && strcmp(argv[1], \"show_warnings_and_errors\") == 0)\n\t{\n\t\tfem->ShowWarningsAndErrors(n != 0);\n\t\tprintf(\"show_warnings_and_errors = %d\", n);\n\t}\n\telse\n\t{\n\t\tprintf(\"don't know \\\"%s\\\"\", argv[1]);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBio/FEBioCommand.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"Command.h\"\n#include \"CommandManager.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBioModel;\n\n//-----------------------------------------------------------------------------\n//! Base class of FEBio commands\n\nclass FEBioCommand : public Command\n{\npublic:\n\tFEBioCommand();\n\tvirtual ~FEBioCommand(void);\n\nprotected:\n\tFEBioModel* GetFEM();\n};\n\n//-----------------------------------------------------------------------------\n// helper class for registering FEBio commands\nclass FERegisterCmd\n{\npublic:\n\tFERegisterCmd(Command* pcmd, const char* szname, const char* szdesc) \n\t{ \n\t\tpcmd->SetName(szname);\n\t\tpcmd->SetDescription(szdesc);\n\t\tCommandManager* pCM = CommandManager::GetInstance();\n\t\tpCM->AddCommand(pcmd); \n\t}\n};\n\n#define DECLARE_COMMAND(theCmd) public:\tstatic FERegisterCmd m_##theCmd##_rc\n#define REGISTER_COMMAND(theClass, theName, theDesc) FERegisterCmd theClass::m_##theClass##_rc(new theClass(), theName, theDesc)\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Run : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Run);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Restart : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Restart);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_LoadPlugin : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_LoadPlugin);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_UnLoadPlugin : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_UnLoadPlugin);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Config : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Config);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Plugins : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Plugins);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Help : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Help);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Events : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Events);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Quit : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Quit);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Cont : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Cont);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Conv : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Conv);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Debug : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Debug);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Fail : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Fail);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Plot : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Plot);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Print : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Print);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Version : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Version);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_Time : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_Time);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_svg : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_svg);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_out : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_out);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_where : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_where);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_break : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_break);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_breaks : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_breaks);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_clear_breaks : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_clear_breaks);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_list : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_list);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_hist: public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_hist);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioCmd_set : public FEBioCommand\n{\npublic:\n\tint run(int nargs, char** argv);\n\tDECLARE_COMMAND(FEBioCmd_set);\n};\n"
  },
  {
    "path": "FEBio/Interrupt.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"Interrupt.h\"\n#include \"FEBioApp.h\"\n#include <signal.h>\n\nbool Interruption::m_bsig = false;\n\nInterruption::Interruption()\n{\n\tstatic bool binit = false;\n\n\tif (!binit) \n\t{\n\t\tsignal(SIGINT, Interruption::handler);\n\t\tbinit = true;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Destructor\n// TODO: Restore original intteruption handler\nInterruption::~Interruption()\n{\n\t\n}\n\nvoid Interruption::handler(int sig)\n{\n\tm_bsig = true;\n\tsignal(SIGINT, Interruption::handler);\n}\n"
  },
  {
    "path": "FEBio/Interrupt.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\nclass Interruption\n{\npublic:\n\tInterruption();\n\tvirtual ~Interruption();\n\n\tstatic void handler(int sig);\n\tstatic bool\tm_bsig;\n};\n"
  },
  {
    "path": "FEBio/breakpoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"breakpoint.h\"\n#include <FECore/Callback.h>\n#include <vector>\n#include <iostream>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n// list of current break points\nstd::vector<BREAK_POINT> break_points;\n\n//-----------------------------------------------------------------------------\nvoid clear_break_points()\n{\n\tbreak_points.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid add_time_break_point(double t)\n{\n\tBREAK_POINT bp;\n\tbp.nwhen = -1;\n\tbp.time = t;\n\tbp.flag = 1;\n\tbreak_points.push_back(bp);\n}\n\n//-----------------------------------------------------------------------------\nvoid add_event_break_point(int nwhen)\n{\n\tBREAK_POINT bp;\n\tbp.nwhen = nwhen;\n\tbp.time = 0;\n\tbp.flag = 1;\n\tbreak_points.push_back(bp);\n}\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\n#define szcmp\t_stricmp\n#else\n#define szcmp\tstrcasecmp\n#endif\n\n//-----------------------------------------------------------------------------\nvoid add_break_point(const char* szcond)\n{\n\tif      (szcmp(szcond, \"ALWAYS\"       ) == 0) add_event_break_point(CB_ALWAYS);\n\telse if (szcmp(szcond, \"INIT\"         ) == 0) add_event_break_point(CB_INIT);\n\telse if (szcmp(szcond, \"STEP_ACTIVE\"  ) == 0) add_event_break_point(CB_STEP_ACTIVE);\n\telse if (szcmp(szcond, \"MAJOR_ITERS\"  ) == 0) add_event_break_point(CB_MAJOR_ITERS);\n\telse if (szcmp(szcond, \"MINOR_ITERS\"  ) == 0) add_event_break_point(CB_MINOR_ITERS);\n\telse if (szcmp(szcond, \"SOLVED\"       ) == 0) add_event_break_point(CB_SOLVED);\n\telse if (szcmp(szcond, \"UPDATE_TIME\"  ) == 0) add_event_break_point(CB_UPDATE_TIME);\n\telse if (szcmp(szcond, \"AUGMENT\"      ) == 0) add_event_break_point(CB_AUGMENT);\n\telse if (szcmp(szcond, \"STEP_SOLVED\"  ) == 0) add_event_break_point(CB_STEP_SOLVED);\n\telse if (szcmp(szcond, \"REFORM\"       ) == 0) add_event_break_point(CB_MATRIX_REFORM);\n\telse if (szcmp(szcond, \"MATRIX_SOLVE\" ) == 0) add_event_break_point(CB_PRE_MATRIX_SOLVE);\n\telse\n\t{\n\t\tdouble f = atof(szcond);\n\t\tadd_time_break_point(f);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid clear_break_points(int n)\n{\n\tif (n == -1)\n\t{\n\t\tbreak_points.clear();\n\t}\n\telse\n\t{\n\t\tif ((n >= 0) && (n < break_points.size())) break_points.erase(break_points.begin() + n);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// break points cb\nvoid print_break_points()\n{\n\tint nbp = (int)break_points.size();\n\tif (nbp == 0)\n\t{\n\t\tcout << \"No breakpoints defined.\\n\";\n\t\treturn;\n\t}\n\n\tfor (int i = 0; i < nbp; ++i)\n\t{\n\t\tBREAK_POINT& bpi = break_points[i];\n\t\tcout << i + 1 << \": \";\n\t\tif (bpi.nwhen > 0)\n\t\t{\n\t\t\tcout << \"event = \";\n\t\t\tswitch (bpi.nwhen)\n\t\t\t{\n\t\t\tcase CB_ALWAYS: cout << \"ALWAYS\"; break;\n\t\t\tcase CB_INIT: cout << \"INIT\"; break;\n\t\t\tcase CB_STEP_ACTIVE: cout << \"STEP ACTIVE\"; break;\n\t\t\tcase CB_MAJOR_ITERS: cout << \"MAJOR_ITERS\"; break;\n\t\t\tcase CB_MINOR_ITERS: cout << \"MINOR_ITERS\"; break;\n\t\t\tcase CB_SOLVED: cout << \"SOLVED\"; break;\n\t\t\tcase CB_UPDATE_TIME: cout << \"UPDATE_TIME\"; break;\n\t\t\tcase CB_AUGMENT: cout << \"AUGMENT\"; break;\n\t\t\tcase CB_STEP_SOLVED: cout << \"STEP_SOLVED\"; break;\n\t\t\tcase CB_MATRIX_REFORM: cout << \"MATRIX_REFORM\"; break;\n\t\t\tdefault:\n\t\t\t\tcout << \"(unknown)\"; break;\n\t\t\t}\n\t\t\tcout << endl;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcout << \"time = \" << bpi.time << endl;\n\t\t}\n\t}\n}\n\n// see if a break point is reached\nint check_break(int nwhen, double t)\n{\n\tconst double eps = 1e-12;\n\n\tint nbp = (int)break_points.size();\n\tfor (int i = 0; i<nbp; ++i)\n\t{\n\t\tBREAK_POINT& bpi = break_points[i];\n\t\tif (bpi.nwhen > 0)\n\t\t{\n\t\t\tif ((int)nwhen & bpi.nwhen)\n\t\t\t{\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\telse if (nwhen == CB_MAJOR_ITERS)\n\t\t{\n\t\t\tif (bpi.flag && (bpi.time <= t + eps))\n\t\t\t{\n\t\t\t\tbpi.flag = 0;\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n    }\n\treturn -1;\n}\n"
  },
  {
    "path": "FEBio/breakpoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// break point class\nstruct BREAK_POINT\n{\n\tint\t\tnwhen;\t\t// break on callback\n\tdouble\ttime;\t\t// break at time (nwhen == -1)\n\tint\t\tflag;\t\t// flag to see if break point was hit\n};\n\n// clear all break points\nvoid clear_break_points();\n\n// clear a particular break point\nvoid clear_break_points(int n);\n\n// add a break point\nvoid add_break_point(const char* szcond);\n\n// add a break point that breaks at (sim) time t\nvoid add_time_break_point(double t);\n\n// add a break point that breats at an event\nvoid add_event_break_point(const char* szwhen);\n\n// break points cb\nvoid print_break_points();\n\n// see if a break point is reached (return -1 if break point was not reached)\nint check_break(int nwhen, double t);\n"
  },
  {
    "path": "FEBio/console.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"console.h\"\n#include <stdarg.h>\n#include <string.h>\n#include <ctype.h>\n\n#ifdef WIN32\n\t#include <shobjidl.h>\n\t#include <windows.h>\n\t#include <conio.h>\n#else\n\t//These are for the wait command\n\t#include <termios.h>\n\t#include <unistd.h>\n#endif\n\n#ifdef WIN32\nITaskbarList3*\ttaskBar = NULL;\n#endif\n\n//--------------------------------------------------------------------\n// pointer to the one and only console object. This pointer\n// will be initialized during the first call to GetHandle.\nConsole* Console::m_pShell = 0;\n\n//--------------------------------------------------------------------\n//! This class returns the pointer to the console object. On the first\n//! call, the pointer is allocated.\nConsole* Console::GetHandle()\n{\n\tif (m_pShell == 0)\n\t{\n\t\tm_pShell = new Console;\n\t}\n\n\treturn m_pShell;\n}\n\nConsole::~Console()\n{\n\tCleanUp();\n}\n\nvoid Console::CleanUp()\n{\n#ifdef WIN32\n\tif (taskBar != NULL)\n\t{\n\t\ttaskBar->Release();\n\t\tCoUninitialize();\n\t\ttaskBar = NULL;\n\t}\n#endif\n}\n\n//--------------------------------------------------------------------\n//! Sets the title of the console window.\nvoid Console::SetTitle(const char* sz, ...)\n{\n\tif (m_bActive)\n\t{\n\t\t// get a pointer to the argument list\n\t\tva_list\targs;\n\n\t\t// make the message\n\t\tchar sztitle[512];\n\t\tva_start(args, sz);\n\t\tvsnprintf(sztitle, sizeof(sztitle), sz, args);\n\t\tva_end(args);\n\n#ifdef WIN32\n\t\tSetConsoleTitleA(sztitle);\n#endif\n#ifdef LINUX\n\t\tprintf(\"%c]0;%s%c\", '\\033', sztitle, '\\007');\n#endif\n\t}\n}\n\n\n//--------------------------------------------------------------------\n//! gets a line from the user\nvoid Console::Wait()\n{\n\t// notify the user.\n\tfprintf(stderr, \"Press any key to continue...\\n\");\n\n\t// flush the standard stream\n\tfflush(stdin);\n\n\t// wait until user inputs a character with no return symbol.\n#ifdef WIN32\n\t_getch();\n#else\n\t// change mode of termios so echo is off.\n\tstruct termios oldt,\n\t\t\t\t\tnewt;\n\ttcgetattr( STDIN_FILENO, &oldt );\n\tnewt = oldt;\n\tnewt.c_lflag &= ~( ICANON | ECHO );\n\ttcsetattr( STDIN_FILENO, TCSANOW, &newt );\n\tgetchar();\n\t// reset stdin.\n\ttcsetattr( STDIN_FILENO, TCSANOW, &oldt );\n#endif\n\n}\n\n//--------------------------------------------------------------------\n//! get a command from the user\n\nvoid Console::GetCommand(int& nargs, char **argv)\n{\n\tstatic char szcmd[512] = { 0 };\n\tszcmd[0] = 0;\n\n\t// print the command prompt\n\tWrite(\"\\nfebio>\", 0x0E);\n\n\t// you must flush the input buffer before using gets\n\tfflush(stdin);\n\n\t// get the command\n\tfgets(szcmd, 255, stdin);\n\n\t// fgets does not remove '\\n' so we'll do it ourselves\n\tchar* ch = strrchr(szcmd, '\\n');\n\tif (ch) *ch = 0;\n\n\t// check for a percentage sign\n\tif (szcmd[0] == '%')\n\t{\n\t\tint n = atoi(szcmd + 1) - 1;\n\t\tif ((n >= 0) && (n < m_history.size()))\n\t\t{\n\t\t\tstrcpy(szcmd, m_history[n].c_str());\n\t\t}\n\t\telse { nargs = 0; return; }\n\t}\n\n\t// store a copy of the input to the history\n\t// (unless it's the history command)\n\tif (strcmp(szcmd, \"hist\") != 0)\n\t\tm_history.push_back(szcmd);\n\n\t// parse the arguments\n\tnargs = 0;\n\tint n = 0;\n\tint b = 0;\n\tch = szcmd;\n\twhile (*ch)\n\t{\n\t\tswitch (*ch)\n\t\t{\n\t\tcase ' ':\n\t\t\tif ((b == 0) && (n != 0))\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tn = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase '\"':\n\t\t\tif ((b == 0) && (n==0)) b = 1;\n\t\t\telse \n\t\t\t{\n\t\t\t\tb = 0;\n\t\t\t\t*ch = 0;\n\t\t\t\tn = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (n == 0) argv[nargs++] = ch;\n\t\t\tn++;\n\t\t}\n\t\tch++;\n\t}\n}\n\n//--------------------------------------------------------------------\nconst std::vector<std::string>& Console::GetHistory() const\n{\n\treturn m_history;\n}\n\n//--------------------------------------------------------------------\n//! this function draws an image to the console\n\nvoid Console::Draw(unsigned char *img, int nx, int ny)\n{\n#ifdef WIN32\n\tint i, j, n = 0;\n\tWORD att;\n\tprintf(\"\\n\");\n\tHANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);\n\tDWORD col[] = {0x00, 0x04, 0x02, 0x01, 0x0C, 0x0A, 0x09, 0x08, 0x07};\n\tfor (j=0; j<ny; ++j)\n\t{\n\t\tfor (i=0; i<nx; ++i)\n\t\t{\n\t\t\tatt = (WORD) ((col[img[n++]] << 4)%0xFF);\n\t\t\tSetConsoleTextAttribute(hout, att);\n\t\t\tprintf(\" \");\n\t\t}\n\t\tprintf(\"\\n\");\n\t}\n\tSetConsoleTextAttribute(hout, 0x0F);\n#endif\n}\n\n//--------------------------------------------------------------------\nvoid Console::Write(const char *sz, unsigned short att)\n{\n#ifdef WIN32\n\tprintf(\"\\n\");\n\tHANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);\n\tSetConsoleTextAttribute(hout, (WORD) att);\n\tprintf(\"%s\", sz);\n\tSetConsoleTextAttribute(hout, 0x0F);\n#else\n\tprintf(\"%s\", sz);\n#endif\n}\n\n//--------------------------------------------------------------------\nvoid Console::SetProgress(double pct)\n{\n#ifdef WIN32\n\t// Get the console window's handle\n\tHWND hwnd = GetConsoleWindow();\n\tif (hwnd == NULL) return;\n\n\t// initialize task bar\n\tif (taskBar == NULL)\n\t{\n\t\tCoInitialize(NULL);\n\t\tCoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void **)&taskBar);\n\t}\n\n\tif (taskBar)\n\t{\n\t\tif ((pct <= 0.0) || (pct >= 100.0))\n\t\t{\n\t\t\ttaskBar->SetProgressState(hwnd, TBPF_NOPROGRESS);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttaskBar->SetProgressState(hwnd, TBPF_NORMAL);\n\t\t\ttaskBar->SetProgressValue(hwnd, (ULONGLONG)pct, 100);\n\t\t}\n\t}\n\telse CoUninitialize();\n#endif\n}\n\nvoid ConsoleStream::print(const char* sz)\n{ \n\tfprintf(stdout, \"%s\", sz);\n\tfflush(stdout);\n}\n"
  },
  {
    "path": "FEBio/console.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioLib/LogStream.h>\n#include <vector>\n#include <string>\n\n//-----------------------------------------------------------------------------\n//! The Console class manages the shell window. This class is implemented as\n//! a singleton, i.e. there can only be one console class in the entire\n//! application. Users obtain a pointer to the Console by calling the GetHandle\n//! function. \nclass Console  \n{\npublic:\n\t//! return the pointer to the one and only console object\n\tstatic Console* GetHandle();\n\npublic:\n\tConsole() { m_bActive = true; }\n\t~Console();\n\n\tvoid CleanUp();\n\n\t//! set the title of the console\n\tvoid SetTitle(const char* sz, ...);\n\n\tvoid Activate() { m_bActive = true; } \n\tvoid Deactivate() { m_bActive = false; }\n\n\tvoid GetCommand(int& nargs, char** argv);\n\n\t//! waits for user input (similar to system(\"pause\"))\n\tvoid Wait();\n\n\tvoid Draw(unsigned char* img, int nx, int ny);\n\n\tvoid Write(const char* sz, unsigned short att);\n\n\tvoid SetProgress(double pct);\n\n\tconst std::vector<std::string>& GetHistory() const;\n\nprotected:\n\tbool\tm_bActive;\n\tstd::vector<std::string>\tm_history;\t//!< command history\n\nprotected:\n\tstatic\tConsole*\t\t\tm_pShell;\t//!< pointer to the one and only console class\n};\n\n//-----------------------------------------------------------------------------\n// we use the console to log output \nclass ConsoleStream : public LogStream\n{\npublic:\n\tvoid print(const char* sz);\n};\n"
  },
  {
    "path": "FEBio/febio_cb.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"febio_cb.h\"\n#include <FEBioLib/FEBioModel.h>\n#include <FEBioLib/version.h>\n#include \"console.h\"\n#include \"Interrupt.h\"\n#include \"breakpoint.h\"\n#include \"FEBioApp.h\"\n#include <iostream>\n\n//-----------------------------------------------------------------------------\n// callback to update window title\nbool update_console_cb(FEModel* pfem, unsigned int nwhen, void* pd)\n{\n\tFEBioModel& fem = static_cast<FEBioModel&>(*pfem);\n\n\t// get the number of steps\n\tint nsteps = fem.Steps();\n\n\t// calculate progress\n\tdouble starttime = fem.GetStartTime();\n\tdouble endtime = fem.GetEndTime();\n\tdouble f = 0.0;\n\tif (nwhen != CB_INIT)\n\t{\n\t\tdouble ftime = fem.GetCurrentTime();\n\t\tif (endtime != starttime) f = (ftime - starttime) / (endtime - starttime);\n\t\telse\n\t\t{\n\t\t\t// this only happens (I think) when the model is solved\n\t\t\tf = 1.0;\n\t\t}\n\t}\n\n\tdouble pct = 100.0*f;\n\n\t// check debug flag\n\tint ndebug = fem.GetDebugLevel();\n\n\t// obtain a pointer to the console object. We'll use this to\n\t// set the title of the console window.\n\tConsole* pShell = Console::GetHandle();\n\n\t\n\tchar* szver = febio::getVersionString();\n\n\tchar szvers[64] = {0};\n#ifndef NDEBUG\n\tsnprintf(szvers, sizeof(szvers), \"FEBio (DEBUG) %s\", szver);\n#else\n\tsnprintf(szvers, sizeof(szvers), \"FEBio %s\", szver);\n#endif\n\n\t// print progress in title bar\n\tconst std::string& sfile = fem.GetFileTitle();\n\tconst char* szfile = sfile.c_str();\n\n\tif (nsteps > 1)\n\t\tpShell->SetTitle(\"(step %d/%d: %.f%%) %s - %s %s\", fem.GetCurrentStepIndex() + 1, nsteps, pct, szfile, szvers, (ndebug?\"(debug mode)\": \"\"));\n\telse\n\t\tpShell->SetTitle(\"(%.f%%) %s - %s %s\", pct, szfile, szvers, (ndebug?\"(debug mode)\": \"\"));\n\n\tif (nsteps > 1)\n\t{\n\t\tint step = fem.GetCurrentStepIndex();\n\t\tdouble w = (step + f) / nsteps;\n\t\tpct = 100.0*w;\n\t}\n\n\t// set progress (will print progress on task bar)\n\tif (nwhen == CB_SOLVED) pShell->SetProgress(100.0);\n\telse pShell->SetProgress(pct);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// break points cb\nbool break_point_cb(FEModel* pfem, unsigned int nwhen, void* pd)\n{\n\t// get the current simulation time\n\tdouble t = pfem->GetTime().currentTime;\n\n\t// see if a break point was reached\n\tint bp = check_break(nwhen, t);\n\tif (bp >= 0)\n\t{\n\t\tstd::cout << \"breakpoint \" << bp + 1 << \" reached\\n\";\n\n\t\t// get a pointer to the app\n\t\tFEBioApp* app = FEBioApp::GetInstance();\n\n\t\t// process the commands\n\t\tif (app) app->ProcessCommands();\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// callback for ctrl+c interruptions\nbool interrupt_cb(FEModel* pfem, unsigned int nwhen, void* pd)\n{\n\t// Do not process this when we are writing or reading the dump file since\n\t// this may cause problems\n\tif ((nwhen == CB_SERIALIZE_LOAD) || (nwhen == CB_SERIALIZE_SAVE)) return true;\n\n\tInterruption itr;\n\tif (itr.m_bsig)\n\t{\n\t\titr.m_bsig = false;\n\n\t\tstd::cout << \"User interruption\\n\";\n\n\t\t// get a pointer to the app\n\t\tFEBioApp* app = FEBioApp::GetInstance();\n\n\t\t// process the commands\n\t\tif (app) app->ProcessCommands();\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FEBio/febio_cb.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\nclass FEModel;\n\n// callback to update window title\nbool update_console_cb(FEModel* pfem, unsigned int nwhen, void* pd);\n\n// callback that manages the break points\nbool break_point_cb(FEModel* pfem, unsigned int nwhen, void* pd);\n\n// callback for ctrl+c interruptions\nbool interrupt_cb(FEModel* pfem, unsigned int nwhen, void* pd);\n"
  },
  {
    "path": "FEBio/stdafx.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n\n// TODO: reference any additional headers you need in STDAFX.H\n// and not in this file\n"
  },
  {
    "path": "FEBio/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <string.h>\n\n// TODO: reference additional headers your program requires here\n"
  },
  {
    "path": "FEBioConfig.cmake",
    "content": "cmake_minimum_required(VERSION 3.15)\n\n# ------------------------------------------------------------------------------\n# FEBioConfig.cmake\n#\n# Exposes one imported target per FEBio library.\n#\n# Targets:\n#     FEBio::FEAMR\n#     FEBio::FEBioLib\n#     FEBio::FEBioMech\n#     FEBio::FEBioOpt\n#     FEBio::FEBioRVE\n#     FEBio::FECore\n#     FEBio::FEImgLib\n#     FEBio::FEBioPlot\n#     FEBio::FEBioTest\n#     FEBio::FEBioXML\n# ------------------------------------------------------------------------------\n\n# ------------------------------------------------------------------------------\n# Libraries\n# ------------------------------------------------------------------------------\n\nset(_FEBIO_LIBS\n    FECore\n    FEBioMech\n    FEBioMix\n    FEBioFluid\n    FEBioRVE\n    FEBioPlot\n    FEBioXML\n    FEBioLib\n    FEAMR\n    FEBioOpt\n    FEImgLib\n)\n\n# ------------------------------------------------------------------------------\n# Detect in-tree build\n# ------------------------------------------------------------------------------\n\nget_filename_component(_FEBIO_CONFIG_DIR\n    \"${CMAKE_CURRENT_LIST_FILE}\" PATH\n)\n\nset(_FEBIO_IS_SOURCE_TREE TRUE)\nif(EXISTS \"${_FEBIO_CONFIG_DIR}/../../../include\")\n        set(_FEBIO_IS_SOURCE_TREE FALSE)\nendif()\n\nif(_FEBIO_IS_SOURCE_TREE)\n    # Config file is at the root of the FEBio source tree\n    set(_FEBIO_PREFIX \"${_FEBIO_CONFIG_DIR}\")\n    set(_FEBIO_INCLUDE_DIR \"${_FEBIO_PREFIX}\")\n\n    # This is the default build output directory\n    set(_FEBIO_LIB_DIR \"${_FEBIO_PREFIX}/build\")\n\n    # Try to find the build directory by searching for fecore\n    file(GLOB _MATCHED_DIRS LIST_DIRECTORIES true \"${_FEBIO_PREFIX}/*build*\")\n\n    if(_MATCHED_DIRS)\n\n        if(WIN32)\n            set(_LIB_NAME \"fecore.lib\")\n        elseif(APPLE)\n            set(_LIB_NAME \"libfecore.dylib\")\n        else()\n            set(_LIB_NAME \"libfecore.so\")\n        endif()\n\n\n        foreach(dir IN LISTS _MATCHED_DIRS)\n            find_file(_test\n                NAMES ${_LIB_NAME}\n                PATHS ${dir}\n                PATH_SUFFIXES lib lib/Release Release/lib Debug/lib lib/Debug\n                NO_DEFAULT_PATH            \n            )\n\n            if(_test)\n                set(_FEBIO_LIB_DIR \"${dir}\")\n                break()\n            endif()\n        endforeach()\n\n        unset(_test CACHE)\n    endif()\nelse()\n    # Installed SDK: <prefix>/lib/cmake/FEBio/FEBioConfig.cmake\n    get_filename_component(_FEBIO_PREFIX\n        \"${_FEBIO_CONFIG_DIR}/../../..\" REALPATH\n    )\n    set(_FEBIO_INCLUDE_DIR \"${_FEBIO_PREFIX}/include\")\n    set(_FEBIO_LIB_DIR \"${_FEBIO_PREFIX}/lib\")\nendif()\n\n# ------------------------------------------------------------------------------\n# Helper to locate libraries\n# ------------------------------------------------------------------------------\n\nfunction(_febio_find_library out_release out_debug out_min_size out_rel_deb out_fallback libname)\n    string(TOLOWER ${libname} _lib_lc)\n\n    # Find release version\n    find_library(_TEMP\n        NAMES ${_lib_lc}\n        PATHS ${_FEBIO_LIB_DIR}\n        PATH_SUFFIXES lib lib/Release Release/lib Release\n        NO_DEFAULT_PATH)\n\n    string(REGEX MATCH \"Release\" _is_release ${_TEMP})\n\n    if(_is_release)\n        set(${out_release} ${_TEMP} PARENT_SCOPE)\n    endif()\n    unset(_TEMP CACHE)\n\n    # Find debug version\n    find_library(_TEMP \n        NAMES ${_lib_lc}\n        PATHS ${_FEBIO_LIB_DIR}\n        PATH_SUFFIXES lib lib/Debug Debug/lib Debug\n        NO_DEFAULT_PATH)\n\n    string(REGEX MATCH \"Debug\" _is_debug ${_TEMP})\n\n    if(_is_debug)\n        set(${out_debug} \"${_TEMP}\" PARENT_SCOPE)\n    endif()\n    unset(_TEMP CACHE)\n\n    # Find min size version\n    find_library(_TEMP \n        NAMES ${_lib_lc}\n        PATHS ${_FEBIO_LIB_DIR}\n        PATH_SUFFIXES lib lib/MinSizeRel MinSizeRel/lib MinSizeRel\n        NO_DEFAULT_PATH)\n\n    string(REGEX MATCH \"MinSizeRel\" _is_min_size ${_TEMP})\n\n    if(_is_min_size)\n        set(${out_min_size} \"${_TEMP}\" PARENT_SCOPE)\n    endif()\n    unset(_TEMP CACHE)\n\n    # Find rel with deb info version\n    find_library(_TEMP \n        NAMES ${_lib_lc}\n        PATHS ${_FEBIO_LIB_DIR}\n        PATH_SUFFIXES lib lib/RelWithDebInfo RelWithDebInfo/lib RelWithDebInfo\n        NO_DEFAULT_PATH)\n\n    string(REGEX MATCH \"RelWithDebInfo\" _is_rel_deb ${_TEMP})\n\n    if(_is_rel_deb)\n        set(${out_rel_deb} \"${_TEMP}\" PARENT_SCOPE)\n    endif()\n    unset(_TEMP CACHE)\n\n    # Fallback: find any version\n    find_library(_TEMP \n        NAMES ${_lib_lc}\n        PATHS ${_FEBIO_LIB_DIR}\n        PATH_SUFFIXES lib\n        NO_DEFAULT_PATH)\n\n    set(${out_fallback} \"${_TEMP}\" PARENT_SCOPE)\n    unset(_TEMP CACHE)\nendfunction()\n\n# ------------------------------------------------------------------------------\n# Create imported targets\n# ------------------------------------------------------------------------------\n\nforeach(lib IN LISTS _FEBIO_LIBS)\n    if(TARGET \"FEBio::${lib}\")\n        continue()\n    endif()\n\n    add_library(\"FEBio::${lib}\" SHARED IMPORTED)\n\n    set_target_properties(\"FEBio::${lib}\" PROPERTIES\n        INTERFACE_INCLUDE_DIRECTORIES \"${_FEBIO_INCLUDE_DIR}\"\n    )\n\n    _febio_find_library(_rel _dbg _min_size _rel_deb _fallback \"${lib}\")\n\n    if(_rel)\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_LOCATION_RELEASE \"${_rel}\"\n        )\n\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_IMPLIB_RELEASE \"${_rel}\"\n        )\n\n        # Use release configuration as default in case not all are found\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_LOCATION \"${_rel}\"\n        )\n\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_IMPLIB \"${_rel}\"\n        )        \n    endif()\n   \n    if(_dbg)\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_LOCATION_DEBUG \"${_dbg}\"\n        )\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_IMPLIB_DEBUG \"${_dbg}\"\n        )\n\n        # Use debug configuration as default if release not found\n        if(NOT _rel)\n            set_target_properties(\"FEBio::${lib}\" PROPERTIES\n                IMPORTED_LOCATION \"${_dbg}\"\n            )\n            set_target_properties(\"FEBio::${lib}\" PROPERTIES\n                IMPORTED_IMPLIB \"${_dbg}\"\n            )\n        endif()\n    endif()\n\n    if(_min_size)\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_LOCATION_MINSIZEREL \"${_min_size}\"\n        )\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_IMPLIB_MINSIZEREL \"${_min_size}\"\n        )\n    endif()\n\n    if(_rel_deb)\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_LOCATION_RELWITHDEBINFO \"${_rel_deb}\"\n        )\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_IMPLIB_RELWITHDEBINFO \"${_rel_deb}\"\n        )\n    endif()\n\n    if(_fallback)\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_LOCATION \"${_fallback}\"\n        )\n        set_target_properties(\"FEBio::${lib}\" PROPERTIES\n            IMPORTED_IMPLIB \"${_fallback}\"\n        )\n    endif()\n\n    if(NOT _rel AND NOT _dbg AND NOT _fallback)\n        message(\n            \"FEBio library '${lib}' could not be found.\\n\"\n            \"Expected under: ${_FEBIO_PREFIX}\"\n        )\n\n        set(FEBio_FOUND FALSE)\n        return()\n    endif()\n\n    unset(_rel)\n    unset(_dbg)\n    unset(_fallback)\n\nendforeach()\n\nunset(_FEBIO_LIBS)\nunset(_FEBIO_PREFIX)\nunset(_FEBIO_INCLUDE_DIR)\nunset(_FEBIO_LIB_DIR)\n\nset(FEBio_FOUND TRUE)"
  },
  {
    "path": "FEBioFluid/FEBackFlowFSIStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBackFlowFSIStabilization.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEBackFlowFSIStabilization, FESurfaceLoad)\n    ADD_PARAMETER(m_beta, \"beta\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEBackFlowFSIStabilization::FEBackFlowFSIStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofU(pfem), m_dofW(pfem)\n{\n    m_beta = 1.0;\n    \n    // get the degrees of freedom\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::DISPLACEMENT));\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n\n        m_dof.Clear();\n        m_dof.AddDofs(m_dofU);\n        m_dof.AddDofs(m_dofW);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEBackFlowFSIStabilization::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEBackFlowFSIStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofU & m_dofW;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBackFlowFSIStabilization::StiffnessMatrix(FELinearSystem& LS)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    m_psurf->LoadStiffness(LS, m_dof, m_dof, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n        FESurfaceElement& el = *mp.SurfaceElement();\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n        \n        // tangent vectors\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        vec3d dxr = el.eval_deriv1(rt, mp.m_index);\n        vec3d dxs = el.eval_deriv2(rt, mp.m_index);\n\n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n\n        // Fluid velocity\n        vec3d v = FluidVelocity(mp, tp.alphaf);\n\n        double vn = v*n;\n\n        Kab.zero();\n        if (m_beta*vn < 0) {\n\n            // shape functions and derivatives\n            double H_i  = dof_a.shape;\n\n            double H_j  = dof_b.shape;\n            double Gr_j = dof_b.shape_deriv_r;\n            double Gs_j = dof_b.shape_deriv_s;\n\n            mat3d K = dyad(n)*(m_beta*rho * 2 * vn*da);\n            double tnt = m_beta*rho*vn*vn;\n\n            // calculate stiffness component\n            mat3d Kww = K*(H_i * H_j)*tp.alphaf;\n            vec3d g = (dxr*Gs_j - dxs*Gr_j)*(H_i * tnt*tp.alphaf);\n            mat3d Kwu; Kwu.skew(g);\n\n            Kab.sub(3, 0, Kwu);\n            Kab.sub(3, 3, Kww);\n        }\n    });\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEBackFlowFSIStabilization::FluidVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n    vec3d vt[FEElement::MAX_NODES];\n    FESurfaceElement& el = *mp.SurfaceElement();\n    int neln = el.Nodes();\n    for (int j = 0; j<neln; ++j) {\n        FENode& node = m_psurf->Node(el.m_lnode[j]);\n        vt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1 - alpha);\n    }\n    return el.eval(vt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBackFlowFSIStabilization::LoadVector(FEGlobalVector& R)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    m_psurf->LoadVector(R, m_dofW, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n        FESurfaceElement& el = *mp.SurfaceElement();\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n        \n        // tangent vectors\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        vec3d dxr = el.eval_deriv1(rt, mp.m_index);\n        vec3d dxs = el.eval_deriv2(rt, mp.m_index);\n\n        // normal and area element\n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n\n        // fluid velocity\n        vec3d v = FluidVelocity(mp, tp.alphaf);\n        double vn = v*n;\n\n        if (m_beta*vn < 0) {\n\n            // force vector (change sign for inflow vs outflow)\n            vec3d f = n*(m_beta*rho*vn*vn*da);\n\n            double H = dof_a.shape;\n            fa[0] = H * f.x;\n            fa[1] = H * f.y;\n            fa[2] = H * f.z;\n        }\n        else\n        {\n            fa[0] = fa[1] = fa[2] = 0.0;\n        }\n    });\n}\n"
  },
  {
    "path": "FEBioFluid/FEBackFlowFSIStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Backflow stabilization prescribes a normal traction that opposes\n//! backflow on a boundary surface.\nclass FEBIOFLUID_API FEBackFlowFSIStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEBackFlowFSIStabilization(FEModel* pfem);\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate residual\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialization\n    bool Init() override;\n    \nprotected:\n    vec3d FluidVelocity(FESurfaceMaterialPoint& mp, double alpha);\n    \nprotected:\n    double          m_beta;     //!< backflow stabilization coefficient\n    \n    // degrees of freedom\n    FEDofList    m_dofU;\n    FEDofList    m_dofW;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEBackFlowStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBackFlowStabilization.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEBackFlowStabilization, FESurfaceLoad)\n\tADD_PARAMETER(m_beta, \"beta\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEBackFlowStabilization::FEBackFlowStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_beta = 1.0;\n    \n    // get the degrees of freedom\n\t// TODO: Can this be done in Init, since  there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofW.Clear();\n\t\tm_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n\n\t\tm_dof.Clear();\n\t\tm_dof.AddDofs(m_dofW);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEBackFlowStabilization::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEBackFlowStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n\tar & m_dofW;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBackFlowStabilization::StiffnessMatrix(FELinearSystem& LS)\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\tm_psurf->LoadStiffness(LS, m_dofW, m_dofW, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n\n\t\t// tangent vectors\n\t\tvec3d r0[FEElement::MAX_NODES];\n\t\tm_psurf->GetReferenceNodalCoordinates(el, r0);\n\t\tvec3d dxr = el.eval_deriv1(r0, mp.m_index);\n\t\tvec3d dxs = el.eval_deriv2(r0, mp.m_index);\n\n\t\tvec3d n = dxr ^ dxs;\n\t\tdouble da = n.unit();\n\n\t\t// Fluid velocity\n\t\tvec3d v = FluidVelocity(mp, tp.alphaf);\n\n\t\tdouble vn = v*n;\n\n\t\tKab.zero();\n\t\tif (m_beta*vn < 0) {\n\n\t\t\t// shape functions and derivatives\n\t\t\tdouble H_i  = dof_a.shape;\n\n\t\t\tdouble H_j  = dof_b.shape;\n\n\t\t\tmat3d K = dyad(n)*(m_beta*rho * 2 * vn*da);\n\n\t\t\t// calculate stiffness component\n\t\t\tmat3d Kww = K*(H_i * H_j)*tp.alphaf;\n\n\t\t\tKab.sub(0, 0, Kww);\n\t\t}\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEBackFlowStabilization::FluidVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n\tvec3d vt[FEElement::MAX_NODES];\n\tFESurfaceElement& el = *mp.SurfaceElement();\n\tint neln = el.Nodes();\n\tfor (int j = 0; j<neln; ++j) {\n\t\tFENode& node = m_psurf->Node(el.m_lnode[j]);\n\t\tvt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1 - alpha);\n\t}\n\treturn el.eval(vt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBackFlowStabilization::LoadVector(FEGlobalVector& R)\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\tm_psurf->LoadVector(R, m_dofW, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n\n\t\t// tangent vectors\n\t\tvec3d r0[FEElement::MAX_NODES];\n\t\tm_psurf->GetReferenceNodalCoordinates(el, r0);\n\t\tvec3d dxr = el.eval_deriv1(r0, mp.m_index);\n\t\tvec3d dxs = el.eval_deriv2(r0, mp.m_index);\n\n\t\t// normal and area element\n\t\tvec3d n = dxr ^ dxs;\n\t\tdouble da = n.unit();\n\n\t\t// fluid velocity\n\t\tvec3d v = FluidVelocity(mp, tp.alphaf);\n\t\tdouble vn = v*n;\n\n\t\tif (m_beta*vn < 0) {\n\n\t\t\t// force vector (change sign for inflow vs outflow)\n\t\t\tvec3d f = n*(m_beta*rho*vn*vn*da);\n\n\t\t\tdouble H = dof_a.shape;\n\t\t\tfa[0] = H * f.x;\n\t\t\tfa[1] = H * f.y;\n\t\t\tfa[2] = H * f.z;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfa[0] = fa[1] = fa[2] = 0.0;\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "FEBioFluid/FEBackFlowStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Backflow stabilization prescribes a normal traction that opposes\n//! backflow on a boundary surface.\nclass FEBIOFLUID_API FEBackFlowStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEBackFlowStabilization(FEModel* pfem);\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate residual\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialization\n    bool Init() override;\n    \nprotected:\n\tvec3d FluidVelocity(FESurfaceMaterialPoint& mp, double alpha);\n    \nprotected:\n    double\t\t\tm_beta;     //!< backflow stabilization coefficient\n    \n    // degrees of freedom\n\tFEDofList\tm_dofW;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEBinghamFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBinghamFluid.h\"\n#include \"FEFluid.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBinghamFluid, FEViscousFluid)\n    ADD_PARAMETER(m_mu  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"  )->setLongName(\"shear viscosity\");\n    ADD_PARAMETER(m_tauy, FE_RANGE_GREATER_OR_EQUAL(0.0), \"tauy\")->setLongName(\"yield stress\");\n    ADD_PARAMETER(m_n   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"n\"   )->setLongName(\"exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBinghamFluid::FEBinghamFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_mu = 0;\n    m_tauy = 0;\n    m_n = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FEBinghamFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double mu = ShearViscosity(pt);\n    \n    mat3ds s = D*(2*mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FEBinghamFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FEBinghamFluid::Tangent_RateOfDeformation(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    tens4ds c;\n    mat3dd I(1.0);\n\n    if (gdot > 0) {\n        double mu = m_mu + m_tauy/gdot*(1-exp(-m_n*gdot));\n        double dmu = m_tauy/pow(gdot,2)*((1+m_n*gdot)*exp(-m_n*gdot)-1);\n        c = dyad1s(D)*(4/gdot*dmu) + dyad4s(I)*(2*mu);\n    }\n    else {\n        double mu = m_mu + m_tauy*m_n;\n        c = dyad4s(I)*(2*mu);\n    }\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic viscosity\ndouble FEBinghamFluid::ShearViscosity(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double mu = (gdot > 0) ? m_mu + m_tauy/gdot*(1-exp(-m_n*gdot)) : m_mu + m_tauy*m_n;\n    return mu;\n}\n\ndouble FEBinghamFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\tFEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n\tmat3ds D = vt.RateOfDeformation();\n\tdouble dmu_dgdot = 0;\n\tdouble gdot = sqrt(2 * (D.sqr()).tr());\n\tif (gdot > 0) {\n\t\tdmu_dgdot = m_tauy / pow(gdot, 2) * ((1 + m_n * gdot) * exp(-m_n * gdot) - 1);\n\t}\n\treturn dmu_dgdot;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FEBinghamFluid::BulkViscosity(FEMaterialPoint& pt)\n{\n    return 2*ShearViscosity(pt)/3.;\n}\n"
  },
  {
    "path": "FEBioFluid/FEBinghamFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Bingham plastic fluid\n\nclass FEBIOFLUID_API FEBinghamFluid : public FEViscousFluid\n{\npublic:\n    //! constructor\n    FEBinghamFluid(FEModel* pfem);\n    \n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n\n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n    \n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double  m_mu;       //!< shear viscosity at infinite shear rate\n    double  m_tauy;     //!< yield stress\n    double  m_n;        //!< exponential-law coefficient\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEBioFSI.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioFSI.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEFluidFSISolver.h\"\n#include \"FEFluidFSI.h\"\n#include \"FEFluidFSIDomain3D.h\"\n#include \"FEFluidFSITraction.h\"\n#include \"FEFluidFSIDomainFactory.h\"\n#include \"FEBackFlowFSIStabilization.h\"\n#include \"FETangentialFlowFSIStabilization.h\"\n#include \"FEBiphasicFSITraction.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEBiphasicFSIDomain3D.h\"\n#include \"FEFluidModule.h\"\n#include \"FEFluidFSIAnalysis.h\"\n#include \"FEFluidSupplyStarling.h\"\n#include <FECore/FETimeStepController.h>\n\n//-----------------------------------------------------------------------------\nconst char* FEBioFSI::GetVariableName(FEBioFSI::FSI_VARIABLE var)\n{\n\tswitch (var)\n\t{\n\tcase DISPLACEMENT                : return \"displacement\"               ; break;\n\tcase VELOCITY                    : return \"velocity\"                   ; break;\n\tcase ROTATION                    : return \"rotation\"                   ; break;\n\tcase SHELL_DISPLACEMENT          : return \"shell displacement\"         ; break;\n\tcase SHELL_VELOCITY              : return \"shell velocity\"             ; break;\n\tcase SHELL_ACCELERATION          : return \"shell acceleration\"         ; break;\n\tcase RIGID_ROTATION              : return \"rigid rotation\"             ; break;\n\tcase RELATIVE_FLUID_VELOCITY     : return \"relative fluid velocity\"    ; break;\n\tcase RELATIVE_FLUID_ACCELERATION : return \"relative fluid acceleration\"; break;\n\tcase FLUID_VELOCITY              : return \"fluid velocity\"             ; break;\n\tcase FLUID_ACCELERATION          : return \"fluid acceleration\"         ; break;\n\tcase FLUID_DILATATION            : return \"fluid dilatation\"           ; break;\n\tcase FLUID_DILATATION_TDERIV     : return \"fluid dilatation tderiv\"    ; break;\n\t}\n\tassert(false);\n\treturn nullptr;\n}\n\nvoid FEBioFSI::InitModule()\n{\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// register domain\n\tfebio.RegisterDomain(new FEFluidFSIDomainFactory);\n\n\t// define the fsi module\n\tfebio.CreateModule(new FEFluidFSIModule, \"fluid-FSI\",\n\t\t\"{\"\n\t\t\"   \\\"title\\\" : \\\"Fluid-Structure Interaction\\\",\"\n\t\t\"   \\\"info\\\"  : \\\"FSI analysis where a fluid interacts with a rigid, solid or biphasic structure.\\\"\"\n\t\t\"}\");\n\n\tfebio.AddModuleDependency(\"fluid\");\n\tfebio.AddModuleDependency(\"biphasic\");\t// also pulls in \"solid\"\n\n\t//-----------------------------------------------------------------------------\n\t// analysis classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FEFluidFSIAnalysis, \"fluid-FSI\");\n\n\t//-----------------------------------------------------------------------------\n\tREGISTER_FECORE_CLASS(FEFluidFSISolver, \"fluid-FSI\");\n\n\tREGISTER_FECORE_CLASS(FEFluidFSI, \"fluid-FSI\");\n\n\tREGISTER_FECORE_CLASS(FEFluidFSIDomain3D, \"fluid-FSI-3D\");\n\n\tREGISTER_FECORE_CLASS(FEFluidFSITraction, \"fluid-FSI traction\");\n    \n    REGISTER_FECORE_CLASS(FEBiphasicFSITraction, \"biphasic-FSI traction\");\n    \n    REGISTER_FECORE_CLASS(FEBackFlowFSIStabilization, \"fluid backflow stabilization\");\n    \n    REGISTER_FECORE_CLASS(FETangentialFlowFSIStabilization, \"fluid tangential stabilization\");\n\n    REGISTER_FECORE_CLASS(FEBiphasicFSI, \"biphasic-FSI\");\n    \n    REGISTER_FECORE_CLASS(FEBiphasicFSIDomain3D, \"biphasic-FSI-3D\");\n\n    REGISTER_FECORE_CLASS(FEFluidSupplyStarling, \"Starling\");\n\n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioFSI.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\nnamespace FEBioFSI {\n\n\tFEBIOFLUID_API void InitModule();\n\n\tenum FSI_VARIABLE {\n\t\tDISPLACEMENT,\n\t\tVELOCITY,\n\t\tROTATION,\n\t\tSHELL_DISPLACEMENT,\n\t\tSHELL_VELOCITY,\n\t\tSHELL_ACCELERATION,\n\t\tRIGID_ROTATION,\n\t\tRELATIVE_FLUID_VELOCITY,\n\t\tRELATIVE_FLUID_ACCELERATION,\n\t\tFLUID_VELOCITY,\n\t\tFLUID_ACCELERATION,\n\t\tFLUID_DILATATION,\n\t\tFLUID_DILATATION_TDERIV\n\t};\n\n\tFEBIOFLUID_API const char* GetVariableName(FSI_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioFluid.h\"\n#include \"FEFluid.h\"\n#include \"FENewtonianFluid.h\"\n#include \"FEBinghamFluid.h\"\n#include \"FECarreauFluid.h\"\n#include \"FECarreauYasudaFluid.h\"\n#include \"FEPowellEyringFluid.h\"\n#include \"FECrossFluid.h\"\n#include \"FEQuemadaFluid.h\"\n#include \"FEFluidFSI.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEIdealGasIsentropic.h\"\n#include \"FEIdealGasIsothermal.h\"\n#include \"FELinearElasticFluid.h\"\n#include \"FENonlinearElasticFluid.h\"\n#include \"FELogNonlinearElasticFluid.h\"\n\n#include \"FEFluidSolver.h\"\n#include \"FEFluidDomain3D.h\"\n\n#include \"FEFluidPressureLoad.h\"\n#include \"FEFluidTractionLoad.h\"\n#include \"FEFluidMixtureTractionLoad.h\"\n#include \"FEFluidNormalTraction.h\"\n#include \"FEFluidNormalVelocity.h\"\n#include \"FEFluidVelocity.h\"\n#include \"FEFluidRotationalVelocity.h\"\n#include \"FEFluidResistanceBC.h\"\n#include \"FEFluidResistanceLoad.h\"\n#include \"FEFluidRCRBC.h\"\n#include \"FEFluidRCRLoad.h\"\n#include \"FEFluidCOBC.h\"\n#include \"FETangentialDamping.h\"\n#include \"FETangentialFlowStabilization.h\"\n#include \"FEBackFlowStabilization.h\"\n#include \"FEFluidRCBC.h\"\n#include \"FEFluidRCLoad.h\"\n#include \"FEPrescribedFluidPressure.h\"\n\n#include \"FETiedFluidInterface.h\"\n#include \"FEConstraintFrictionlessWall.h\"\n#include \"FEConstraintNormalFlow.h\"\n#include \"FEConstraintUniformFlow.h\"\n#include \"FEBioFluidPlot.h\"\n#include \"FEBioFluidData.h\"\n#include \"FEFluidDomainFactory.h\"\n#include \"FEFSIErosionVolumeRatio.h\"\n#include \"FEFluidStressCriterion.h\"\n#include \"FEFixedFluidVelocity.h\"\n#include \"FEPrescribedFluidVelocity.h\"\n#include \"FEFixedFluidDilatation.h\"\n#include \"FEPrescribedFluidDilatation.h\"\n#include \"FEInitialFluidDilatation.h\"\n#include \"FEInitialFluidVelocity.h\"\n#include \"FEInitialFluidPressure.h\"\n\n#include \"FEConstFluidBodyForce.h\"\n#include \"FECentrifugalFluidBodyForce.h\"\n#include \"FEFluidMovingFrameLoad.h\"\n\n#include \"FEFluidModule.h\"\n\n#include \"FEFluidAnalysis.h\"\n#include <FECore/FETimeStepController.h>\n\n//-----------------------------------------------------------------------------\nconst char* FEBioFluid::GetVariableName(FEBioFluid::FLUID_VARIABLE var)\n{\n\tswitch (var)\n\t{\n\tcase DISPLACEMENT                    : return \"displacement\"                        ; break;\n\tcase RELATIVE_FLUID_VELOCITY         : return \"relative fluid velocity\"             ; break;\n\tcase FLUID_DILATATION                : return \"fluid dilatation\"                    ; break;\n\tcase RELATIVE_FLUID_ACCELERATION     : return \"relative fluid acceleration\"         ; break;\n\tcase FLUID_DILATATION_TDERIV         : return \"fluid dilatation tderiv\"             ; break;\n\t}\n\tassert(false);\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization of the FEBioFluid module. This function registers all the classes\n//! in this module with the FEBio framework.\nvoid FEBioFluid::InitModule()\n{\n\t//-----------------------------------------------------------------------------\n\t// Domain factory\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tfebio.RegisterDomain(new FEFluidDomainFactory);\n\n\t// define the fluid module\n\tfebio.CreateModule(new FEFluidModule, \"fluid\", \n\t\t\"{\"\n\t\t\"   \\\"title\\\" : \\\"Fluid Mechanics\\\",\"\n\t\t\"   \\\"info\\\"  : \\\"Steady-state or transient fluid dynamics analysis.\\\"\"\n\t\t\"}\");\n\n\t//-----------------------------------------------------------------------------\n\t// analysis classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FEFluidAnalysis, \"fluid\");\n\n//-----------------------------------------------------------------------------\n// solver classes\nREGISTER_FECORE_CLASS(FEFluidSolver, \"fluid\");\n\n//-----------------------------------------------------------------------------\n// Materials\nREGISTER_FECORE_CLASS(FEFluid             , \"fluid\"         );\n// viscous fluids\nREGISTER_FECORE_CLASS(FENewtonianFluid    , \"Newtonian fluid\");\nREGISTER_FECORE_CLASS(FEBinghamFluid      , \"Bingham\"       )\nREGISTER_FECORE_CLASS(FECarreauFluid      , \"Carreau\"       );\nREGISTER_FECORE_CLASS(FECarreauYasudaFluid, \"Carreau-Yasuda\");\nREGISTER_FECORE_CLASS(FEPowellEyringFluid , \"Powell-Eyring\" );\nREGISTER_FECORE_CLASS(FECrossFluid        , \"Cross\"         );\nREGISTER_FECORE_CLASS(FEQuemadaFluid      , \"Quemada\"       );\n\n// elastic fluids\nREGISTER_FECORE_CLASS(FEIdealGasIsentropic, \"ideal gas isentropic\");\n//REGISTER_FECORE_CLASS(FEIdealGasIsothermal, \"ideal gas isothermal\");\nREGISTER_FECORE_CLASS(FELinearElasticFluid, \"linear\"        );\nREGISTER_FECORE_CLASS(FENonlinearElasticFluid, \"nonlinear\"  );\nREGISTER_FECORE_CLASS(FELogNonlinearElasticFluid, \"log-nonlinear\");\n\n//-----------------------------------------------------------------------------\n// Domain classes\nREGISTER_FECORE_CLASS(FEFluidDomain3D, \"fluid-3D\");\n\n//-----------------------------------------------------------------------------\n// Surface loads\nREGISTER_FECORE_CLASS(FEFluidPressureLoad          , \"fluid pressure\"                , 0x0300); // Deprecated, use the BC version.\nREGISTER_FECORE_CLASS(FEFluidTractionLoad          , \"fluid viscous traction\"        );\nREGISTER_FECORE_CLASS(FEFluidMixtureTractionLoad   , \"fluid mixture viscous traction\");\nREGISTER_FECORE_CLASS(FEFluidNormalTraction        , \"fluid normal traction\"         );\nREGISTER_FECORE_CLASS(FEFluidNormalVelocity        , \"fluid normal velocity\"         );\nREGISTER_FECORE_CLASS(FEFluidVelocity              , \"fluid velocity\"                );\nREGISTER_FECORE_CLASS(FEFluidResistanceLoad        , \"fluid resistance\"              , 0x0300);  // Deprecated, use the BC version.\nREGISTER_FECORE_CLASS(FEFluidRCRLoad               , \"fluid RCR\"                     , 0x0300);  // Deprecated, use the BC version.\nREGISTER_FECORE_CLASS(FETangentialDamping          , \"fluid tangential damping\"      );\nREGISTER_FECORE_CLASS(FETangentialFlowStabilization, \"fluid tangential stabilization\");\nREGISTER_FECORE_CLASS(FEBackFlowStabilization      , \"fluid backflow stabilization\"  );\nREGISTER_FECORE_CLASS(FEFluidRCLoad                , \"fluid RC\"                      , 0x0300);  // Deprecated, use the BC version.\n\n//-----------------------------------------------------------------------------\n// body loads\nREGISTER_FECORE_CLASS(FEConstFluidBodyForce      , \"fluid body force\");\nREGISTER_FECORE_CLASS(FECentrifugalFluidBodyForce, \"fluid centrifugal force\");\nREGISTER_FECORE_CLASS(FEFluidMovingFrameLoad     , \"fluid moving frame\");\n\n//-----------------------------------------------------------------------------\n// boundary conditions\nREGISTER_FECORE_CLASS(FEFixedFluidVelocity       , \"zero fluid velocity\");\nREGISTER_FECORE_CLASS(FEPrescribedFluidVelocity  , \"prescribed fluid velocity\");\nREGISTER_FECORE_CLASS(FEFixedFluidDilatation     , \"zero fluid dilatation\");\nREGISTER_FECORE_CLASS(FEPrescribedFluidDilatation, \"prescribed fluid dilatation\");\nREGISTER_FECORE_CLASS(FEFluidRotationalVelocity  , \"fluid rotational velocity\");\nREGISTER_FECORE_CLASS(FEPrescribedFluidPressure  , \"fluid pressure\");\nREGISTER_FECORE_CLASS(FEFluidRCBC                , \"fluid RC\");\nREGISTER_FECORE_CLASS(FEFluidRCRBC               , \"fluid RCR\");\nREGISTER_FECORE_CLASS(FEFluidResistanceBC        , \"fluid resistance\");\nREGISTER_FECORE_CLASS(FEFluidCOBC                , \"fluid coronary outflow\");\n\n//-----------------------------------------------------------------------------\n// initial conditions\nREGISTER_FECORE_CLASS(FEInitialFluidDilatation, \"initial fluid dilatation\");\nREGISTER_FECORE_CLASS(FEInitialFluidVelocity  , \"initial fluid velocity\");\nREGISTER_FECORE_CLASS(FEInitialFluidPressure  , \"initial fluid pressure\");\n\n//-----------------------------------------------------------------------------\n// Contact interfaces\nREGISTER_FECORE_CLASS(FETiedFluidInterface, \"tied-fluid\");\n   \n//-----------------------------------------------------------------------------\n// constraint classes\nREGISTER_FECORE_CLASS(FEConstraintFrictionlessWall, \"frictionless fluid wall\");\nREGISTER_FECORE_CLASS(FEConstraintNormalFlow      , \"normal fluid flow\"      );\nREGISTER_FECORE_CLASS(FEConstraintUniformFlow     , \"uniform fluid flow\"     );\n\n//-----------------------------------------------------------------------------\n// classes derived from FEPlotData\nREGISTER_FECORE_CLASS(FEPlotDisplacement               , \"displacement\"             );\nREGISTER_FECORE_CLASS(FEPlotNodalFluidVelocity         , \"nodal fluid velocity\"     );\nREGISTER_FECORE_CLASS(FEPlotNodalRelativeFluidVelocity , \"nodal fluid flux\"         );\nREGISTER_FECORE_CLASS(FEPlotFluidDilatation            , \"fluid dilatation\"         );\nREGISTER_FECORE_CLASS(FEPlotFluidEffectivePressure     , \"effective fluid pressure\" );\nREGISTER_FECORE_CLASS(FEPlotElasticFluidPressure\t   , \"elastic fluid pressure\"   );\nREGISTER_FECORE_CLASS(FEPlotFluidBodyForce             , \"fluid body force\"         );\nREGISTER_FECORE_CLASS(FEPlotFluidVolumeRatio\t\t   , \"fluid volume ratio\"       );\nREGISTER_FECORE_CLASS(FEPlotFluidDensity               , \"fluid density\"            );\nREGISTER_FECORE_CLASS(FEPlotFluidDensityRate           , \"fluid density rate\"       );\nREGISTER_FECORE_CLASS(FEPlotFluidVelocity              , \"fluid velocity\"           );\nREGISTER_FECORE_CLASS(FEPlotBFSISolidVolumeFraction    , \"solid volume fraction\"    );\nREGISTER_FECORE_CLASS(FEPlotRelativeFluidVelocity      , \"relative fluid velocity\"  );\nREGISTER_FECORE_CLASS(FEPlotFSIFluidFlux               , \"fluid flux\"               );\nREGISTER_FECORE_CLASS(FEPlotPermeability               , \"permeability\"             );\nREGISTER_FECORE_CLASS(FEPlotGradJ                      , \"dilatation gradient\"      );\nREGISTER_FECORE_CLASS(FEPlotGradPhiF                   , \"fluid volume ratio gradient\");\nREGISTER_FECORE_CLASS(FEPlotFluidAcceleration          , \"fluid acceleration\"       );\nREGISTER_FECORE_CLASS(FEPlotFluidVorticity             , \"fluid vorticity\"          );\nREGISTER_FECORE_CLASS(FEPlotFluidStress                , \"fluid stress\"             );\nREGISTER_FECORE_CLASS(FEPlotElementFluidRateOfDef      , \"fluid rate of deformation\");\nREGISTER_FECORE_CLASS(FEPlotFluidStressPowerDensity    , \"fluid stress power density\");\nREGISTER_FECORE_CLASS(FEPlotFluidHeatSupplyDensity     , \"fluid heat supply density\");\nREGISTER_FECORE_CLASS(FEPlotFluidSurfaceForce          , \"fluid surface force\"      );\nREGISTER_FECORE_CLASS(FEPlotFluidSurfacePressure       , \"fluid surface pressure\"   );\nREGISTER_FECORE_CLASS(FEPlotFluidSurfaceTractionPower  , \"fluid surface traction power\");\nREGISTER_FECORE_CLASS(FEPlotFluidSurfaceEnergyFlux     , \"fluid surface energy flux\");\nREGISTER_FECORE_CLASS(FEPlotFluidShearViscosity        , \"fluid shear viscosity\"    );\nREGISTER_FECORE_CLASS(FEPlotFluidMassFlowRate          , \"fluid mass flow rate\"     );\nREGISTER_FECORE_CLASS(FEPlotFluidStrainEnergyDensity   , \"fluid strain energy density\");\nREGISTER_FECORE_CLASS(FEPlotFluidKineticEnergyDensity  , \"fluid kinetic energy density\");\nREGISTER_FECORE_CLASS(FEPlotFluidEnergyDensity         , \"fluid energy density\"     );\nREGISTER_FECORE_CLASS(FEPlotFluidBulkModulus           , \"fluid bulk modulus\"       );\nREGISTER_FECORE_CLASS(FEPlotFluidElementStrainEnergy   , \"fluid element strain energy\");\nREGISTER_FECORE_CLASS(FEPlotFluidElementKineticEnergy  , \"fluid element kinetic energy\");\nREGISTER_FECORE_CLASS(FEPlotFluidElementLinearMomentum , \"fluid element linear momentum\");\nREGISTER_FECORE_CLASS(FEPlotFluidElementAngularMomentum, \"fluid element angular momentum\");\nREGISTER_FECORE_CLASS(FEPlotFluidElementCenterOfMass   , \"fluid element center of mass\");\nREGISTER_FECORE_CLASS(FEPlotFluidFlowRate              , \"fluid flow rate\"               );\nREGISTER_FECORE_CLASS(FEPlotFluidPressure              , \"fluid pressure\"                );\nREGISTER_FECORE_CLASS(FEPlotFluidPressureTangentStrain , \"fluid pressure tangent strain\" );\nREGISTER_FECORE_CLASS(FEPlotFluidRelativeReynoldsNumber, \"fluid relative Reynolds number\");\nREGISTER_FECORE_CLASS(FEPlotFluidSpecificFreeEnergy    , \"fluid specific free energy\"    );\nREGISTER_FECORE_CLASS(FEPlotFluidSpecificEntropy       , \"fluid specific entropy\"        );\nREGISTER_FECORE_CLASS(FEPlotFluidSpecificInternalEnergy, \"fluid specific internal energy\");\nREGISTER_FECORE_CLASS(FEPlotFluidSpecificGaugeEnthalpy , \"fluid specific gauge enthalpy\" );\nREGISTER_FECORE_CLASS(FEPlotFluidSpecificFreeEnthalpy  , \"fluid specific free enthalpy\"  );\nREGISTER_FECORE_CLASS(FEPlotFluidSpecificStrainEnergy  , \"fluid specific strain energy\"  );\nREGISTER_FECORE_CLASS(FEPlotBFSIPorosity               , \"porosity\"                 );\nREGISTER_FECORE_CLASS(FEPlotFSISolidStress             , \"solid stress\"             );\nREGISTER_FECORE_CLASS(FEPlotFluidShearStressError      , \"fluid shear stress error\");\n\n//-----------------------------------------------------------------------------\nREGISTER_FECORE_CLASS(FENodeFluidXVel          , \"nfvx\");\nREGISTER_FECORE_CLASS(FENodeFluidYVel          , \"nfvy\");\nREGISTER_FECORE_CLASS(FENodeFluidZVel          , \"nfvz\");\nREGISTER_FECORE_CLASS(FELogElemFluidPosX       , \"fx\");\nREGISTER_FECORE_CLASS(FELogElemFluidPosY       , \"fy\");\nREGISTER_FECORE_CLASS(FELogElemFluidPosZ       , \"fz\");\nREGISTER_FECORE_CLASS(FELogElasticFluidPressure, \"fp\");\nREGISTER_FECORE_CLASS(FELogFluidVolumeRatio    , \"fJ\");\nREGISTER_FECORE_CLASS(FELogFluidDensity        , \"fd\");\nREGISTER_FECORE_CLASS(FELogFluidStressPower    , \"fsp\");\nREGISTER_FECORE_CLASS(FELogFluidVelocityX      , \"fvx\");\nREGISTER_FECORE_CLASS(FELogFluidVelocityY      , \"fvy\");\nREGISTER_FECORE_CLASS(FELogFluidVelocityZ      , \"fvz\");\nREGISTER_FECORE_CLASS(FELogFluidAccelerationX  , \"fax\");\nREGISTER_FECORE_CLASS(FELogFluidAccelerationY  , \"fay\");\nREGISTER_FECORE_CLASS(FELogFluidAccelerationZ  , \"faz\");\nREGISTER_FECORE_CLASS(FELogFluidVorticityX     , \"fwx\");\nREGISTER_FECORE_CLASS(FELogFluidVorticityY     , \"fwy\");\nREGISTER_FECORE_CLASS(FELogFluidVorticityZ     , \"fwz\");\nREGISTER_FECORE_CLASS(FELogFluidStressXX       , \"fsxx\");\nREGISTER_FECORE_CLASS(FELogFluidStressYY       , \"fsyy\");\nREGISTER_FECORE_CLASS(FELogFluidStressZZ       , \"fszz\");\nREGISTER_FECORE_CLASS(FELogFluidStressXY       , \"fsxy\");\nREGISTER_FECORE_CLASS(FELogFluidStressYZ       , \"fsyz\");\nREGISTER_FECORE_CLASS(FELogFluidStressXZ       , \"fsxz\");\nREGISTER_FECORE_CLASS(FELogFluidStress1        , \"fs1\" );\nREGISTER_FECORE_CLASS(FELogFluidStress2        , \"fs2\" );\nREGISTER_FECORE_CLASS(FELogFluidStress3        , \"fs3\" );\nREGISTER_FECORE_CLASS(FELogFluidMaxShearStress , \"fmxs\");\nREGISTER_FECORE_CLASS(FELogFluidRateOfDefXX    , \"fdxx\");\nREGISTER_FECORE_CLASS(FELogFluidRateOfDefYY    , \"fdyy\");\nREGISTER_FECORE_CLASS(FELogFluidRateOfDefZZ    , \"fdzz\");\nREGISTER_FECORE_CLASS(FELogFluidRateOfDefXY    , \"fdxy\");\nREGISTER_FECORE_CLASS(FELogFluidRateOfDefYZ    , \"fdyz\");\nREGISTER_FECORE_CLASS(FELogFluidRateOfDefXZ    , \"fdxz\");\n\n//-----------------------------------------------------------------------------\n// Derived from FEMeshAdaptor\nREGISTER_FECORE_CLASS(FEFSIErosionVolumeRatio, \"fsi-volume-erosion\");\n\n//-----------------------------------------------------------------------------\n// Derived from FEMeshAdaptorCriterion\nREGISTER_FECORE_CLASS(FEFluidStressCriterion     , \"fluid shear stress\");\n\nfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEBioFluid module\n\n//! The FEBioFluid module adds fluid capabilities to FEBio.\n//!\nnamespace FEBioFluid {\n\n\tFEBIOFLUID_API void InitModule();\n\n\tenum FLUID_VARIABLE {\n\t\tDISPLACEMENT,\n\t\tRELATIVE_FLUID_VELOCITY,\n\t\tFLUID_DILATATION,\n\t\tRELATIVE_FLUID_ACCELERATION,\n\t\tFLUID_DILATATION_TDERIV,\n\t};\n\n\tFEBIOFLUID_API const char* GetVariableName(FLUID_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioFluidData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioFluidData.h\"\n#include \"FEFluid.h\"\n#include \"FEFluidFSI.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\ndouble FENodeFluidXVel::value(const FENode& node)\n{\n    const int dof_VFX = GetFEModel()->GetDOFIndex(\"wx\");\n    return node.get(dof_VFX);\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeFluidYVel::value(const FENode& node)\n{\n    const int dof_VFY = GetFEModel()->GetDOFIndex(\"wy\");\n    return node.get(dof_VFY);\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeFluidZVel::value(const FENode& node)\n{\n    const int dof_VFZ = GetFEModel()->GetDOFIndex(\"wz\");\n    return node.get(dof_VFZ);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidPosX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(i);\n        FEFluidMaterialPoint* pt = mp.ExtractData<FEFluidMaterialPoint>();\n        FEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt) val += mp.m_r0.x;\n        else if (ept) val += mp.m_rt.x;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidPosY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(i);\n\t\tFEFluidMaterialPoint* pt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\tFEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt) val += mp.m_r0.y;\n        else if (ept) val += mp.m_rt.y;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidPosZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(i);\n\t\tFEFluidMaterialPoint* pt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\tFEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt) val += mp.m_r0.z;\n        else if (ept) val += mp.m_rt.z;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElasticFluidPressure::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n\t\tif (ppt) val += ppt->m_pf;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVolumeRatio::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_ef + 1;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidDensity::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    FEMaterial* pmat = GetFEModel()->GetMaterial(el.GetMatID());\n    FEFluid* pfmat = dynamic_cast<FEFluid*>(pmat);\n    if (pfmat) {\n        for (int i=0; i<nint; ++i)\n        {\n            FEMaterialPoint* pt = el.GetMaterialPoint(i);\n            val += pfmat->Density(*pt);\n        }\n        return val / (double) nint;\n    }\n    else\n        return 0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressPower::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += (ppt->m_sf*ppt->m_Lf).trace();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVelocityX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_vft.x;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVelocityY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_vft.y;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVelocityZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_vft.z;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidAccelerationX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_aft.x;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidAccelerationY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_aft.y;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidAccelerationZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_aft.z;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVorticityX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->Vorticity().x;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVorticityY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->Vorticity().y;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidVorticityZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->Vorticity().z;\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressXX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_sf.xx();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressYY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_sf.yy();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressZZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_sf.zz();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_sf.xy();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_sf.yz();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStressXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) val += ppt->m_sf.xz();\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStress1::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            ppt->m_sf.exact_eigen(l);\n            val += l[0];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStress2::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            ppt->m_sf.exact_eigen(l);\n            val += l[1];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidStress3::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            ppt->m_sf.exact_eigen(l);\n            val += l[2];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidMaxShearStress::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            ppt->m_sf.exact_eigen(l);\n            double mxs = max(fabs(l[1]-l[0])/2,fabs(l[2]-l[1])/2);\n            mxs = max(mxs,fabs(l[0]-l[2])/2);\n            val += mxs;\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidRateOfDefXX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            mat3ds D = ppt->RateOfDeformation();\n            val += D.xx();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidRateOfDefYY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            mat3ds D = ppt->RateOfDeformation();\n            val += D.yy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidRateOfDefZZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            mat3ds D = ppt->RateOfDeformation();\n            val += D.zz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidRateOfDefXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            mat3ds D = ppt->RateOfDeformation();\n            val += D.xy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidRateOfDefYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            mat3ds D = ppt->RateOfDeformation();\n            val += D.yz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFluidRateOfDefXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEFluidMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEFluidMaterialPoint>();\n        if (ppt) {\n            mat3ds D = ppt->RateOfDeformation();\n            val += D.xz();\n        }\n    }\n    return val / (double) nint;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEBioFluidData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/NodeDataRecord.h\"\n#include \"FECore/ElementDataRecord.h\"\n\n//=============================================================================\n// N O D E  D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FENodeFluidXVel : public FELogNodeData\n{\npublic:\n    FENodeFluidXVel(FEModel* pfem) : FELogNodeData(pfem){}\n    double value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeFluidYVel : public FELogNodeData\n{\npublic:\n    FENodeFluidYVel(FEModel* pfem) : FELogNodeData(pfem){}\n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeFluidZVel : public FELogNodeData\n{\npublic:\n    FENodeFluidZVel(FEModel* pfem) : FELogNodeData(pfem){}\n\tdouble value(const FENode& node) override;\n};\n\n//=============================================================================\n// E L E M E N T   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidPosX : public FELogElemData\n{\npublic:\n    FELogElemFluidPosX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidPosY : public FELogElemData\n{\npublic:\n    FELogElemFluidPosY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidPosZ : public FELogElemData\n{\npublic:\n    FELogElemFluidPosZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElasticFluidPressure : public FELogElemData\n{\npublic:\n\tFELogElasticFluidPressure(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVolumeRatio : public FELogElemData\n{\npublic:\n\tFELogFluidVolumeRatio(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidDensity : public FELogElemData\n{\npublic:\n    FELogFluidDensity(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressPower : public FELogElemData\n{\npublic:\n    FELogFluidStressPower(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVelocityX : public FELogElemData\n{\npublic:\n    FELogFluidVelocityX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVelocityY : public FELogElemData\n{\npublic:\n    FELogFluidVelocityY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVelocityZ : public FELogElemData\n{\npublic:\n    FELogFluidVelocityZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidAccelerationX : public FELogElemData\n{\npublic:\n\tFELogFluidAccelerationX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidAccelerationY : public FELogElemData\n{\npublic:\n    FELogFluidAccelerationY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidAccelerationZ : public FELogElemData\n{\npublic:\n    FELogFluidAccelerationZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVorticityX : public FELogElemData\n{\npublic:\n    FELogFluidVorticityX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVorticityY : public FELogElemData\n{\npublic:\n    FELogFluidVorticityY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidVorticityZ : public FELogElemData\n{\npublic:\n    FELogFluidVorticityZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressXX : public FELogElemData\n{\npublic:\n    FELogFluidStressXX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressYY : public FELogElemData\n{\npublic:\n    FELogFluidStressYY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressZZ : public FELogElemData\n{\npublic:\n    FELogFluidStressZZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressXY : public FELogElemData\n{\npublic:\n    FELogFluidStressXY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressYZ : public FELogElemData\n{\npublic:\n    FELogFluidStressYZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStressXZ : public FELogElemData\n{\npublic:\n    FELogFluidStressXZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStress1 : public FELogElemData\n{\npublic:\n    FELogFluidStress1(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStress2 : public FELogElemData\n{\npublic:\n    FELogFluidStress2(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidStress3 : public FELogElemData\n{\npublic:\n    FELogFluidStress3(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidMaxShearStress : public FELogElemData\n{\npublic:\n    FELogFluidMaxShearStress(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidRateOfDefXX : public FELogElemData\n{\npublic:\n    FELogFluidRateOfDefXX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidRateOfDefYY : public FELogElemData\n{\npublic:\n    FELogFluidRateOfDefYY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidRateOfDefZZ : public FELogElemData\n{\npublic:\n    FELogFluidRateOfDefZZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidRateOfDefXY : public FELogElemData\n{\npublic:\n    FELogFluidRateOfDefXY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidRateOfDefYZ : public FELogElemData\n{\npublic:\n    FELogFluidRateOfDefYZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFluidRateOfDefXZ : public FELogElemData\n{\npublic:\n    FELogFluidRateOfDefXZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEBioFluidPlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioFluidPlot.h\"\n#include \"FEFluidDomain3D.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEPolarFluidMaterial.h\"\n#include \"FEFluid.h\"\n#include \"FEPolarFluid.h\"\n#include \"FEFluidDomain.h\"\n#include \"FEFluidFSIDomain.h\"\n#include \"FEFluidFSI.h\"\n#include \"FEBiphasicFSIDomain.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEMultiphasicFSIDomain.h\"\n#include \"FEMultiphasicFSI.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESurface.h>\n#include <FECore/writeplot.h>\n#include <FECore/FEDomainParameter.h>\n\n//=============================================================================\n//                            N O D E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Store the nodal displacements\nbool FEPlotDisplacement::Save(FEMesh& m, FEDataStream& a)\n{\n    // loop over all nodes\n\twriteNodalValues<vec3d>(m, a, [](const FENode& node) {\n\t\treturn node.m_rt - node.m_r0;\n\t});\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the nodal fluid velocity (only for CFD domains)\nbool FEPlotNodalFluidVelocity::Save(FEMesh& m, FEDataStream& a)\n{\n\tFEModel* fem = GetFEModel();\n    int dofWX = fem->GetDOFIndex(\"wx\");\n    int dofWY = fem->GetDOFIndex(\"wy\");\n    int dofWZ = fem->GetDOFIndex(\"wz\");\n    int dofVX = fem->GetDOFIndex(\"vfx\");\n    int dofVY = fem->GetDOFIndex(\"vfy\");\n    int dofVZ = fem->GetDOFIndex(\"vfz\");\n\n\tbool bcfd = false;\n\tif ((dofVX == -1) && (dofVY == -1) && (dofVZ == -1))\n\t{\n\t\tbcfd = true;\n\t}\n    \n    if (bcfd) {\n        writeNodalValues<vec3d>(m, a, [=](const FENode& node) {\n            return node.get_vec3d(dofWX, dofWY, dofWZ);\n        });\n        return true;\n    }\n    else {\n        writeNodalValues<vec3d>(m, a, [=](const FENode& node) {\n            return node.get_vec3d(dofVX, dofVY, dofVZ);\n        });\n        return true;\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Store the nodal relative fluid velocity\nbool FEPlotNodalRelativeFluidVelocity::Save(FEMesh& m, FEDataStream& a)\n{\n\tFEModel* fem = GetFEModel();\n\tint dofWX = fem->GetDOFIndex(\"wx\");\n    int dofWY = fem->GetDOFIndex(\"wy\");\n    int dofWZ = fem->GetDOFIndex(\"wz\");\n    \n\twriteNodalValues<vec3d>(m, a, [=](const FENode& node) {\n\t\treturn node.get_vec3d(dofWX, dofWY, dofWZ);\n\t});\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the nodal dilatations\nbool FEPlotFluidDilatation::Save(FEMesh& m, FEDataStream& a)\n{\n    // get the dilatation dof index\n    int dof_e = GetFEModel()->GetDOFIndex(\"ef\");\n\tif (dof_e < 0) return false;\n\n    // loop over all nodes\n\twriteNodalValues<double>(m, a, [=](const FENode& node) {\n\t\treturn node.get(dof_e);\n\t});\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the nodal effective fluid pressure\nbool FEPlotFluidEffectivePressure::Save(FEDomain& dom, FEDataStream& a)\n{\n    // get the dilatation dof index\n    int dof_e = GetFEModel()->GetDOFIndex(\"ef\");\n    if (dof_e < 0) return false;\n    \n    FEFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEFluid>();\n    if (pfluid == 0) return false;\n    \n    // loop over all nodes\n    writeNodalValues<double>(dom, a, [=, &dom](int i) {\n        FENode& node = dom.Node(i);\n        return pfluid->Pressure(node.get(dof_e));\n    });\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the nodal polar fluid angular velocity\nbool FEPlotNodalPolarFluidAngularVelocity::Save(FEMesh& m, FEDataStream& a)\n{\n    FEModel* fem = GetFEModel();\n    int dofGX = fem->GetDOFIndex(\"gx\");\n    int dofGY = fem->GetDOFIndex(\"gy\");\n    int dofGZ = fem->GetDOFIndex(\"gz\");\n    \n    writeNodalValues<vec3d>(m, a, [=](const FENode& node) {\n        return node.get_vec3d(dofGX, dofGY, dofGZ);\n    });\n    return true;\n}\n\n//=============================================================================\n//                       S U R F A C E    D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSurfaceForce::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n    int NF = pcs->Elements();\n    vec3d fn(0,0,0);    // initialize\n    \n    // calculate the vectorial area of each surface element and to identify solid element associated with this surface element\n    m_area.resize(NF);\n    for (int j=0; j<NF; ++j)\n    {\n        FESurfaceElement& el = pcs->Element(j);\n        m_area[j] = pcs->SurfaceNormal(el,0,0)*pcs->FaceArea(el);\n    }\n    \n    // calculate net fluid force\n    for (int j=0; j<NF; ++j)\n    {\n\t\tFESurfaceElement& el = pcs->Element(j);\n\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            FEFluidMaterial* pfluid = pm->ExtractProperty<FEFluidMaterial>();\n\n            if (!pfluid) {\n                pe = el.m_elem[1].pe;\n                if (pe) pfluid = GetFEModel()->GetMaterial(pe->GetMatID())->ExtractProperty<FEFluidMaterial>();\n            }\n\n            // see if this is a fluid element\n            if (pfluid) {\n                FEPolarFluidMaterial* polar = pfluid->ExtractProperty<FEPolarFluidMaterial>();\n                // evaluate the average stress in this element\n                int nint = pe->GaussPoints();\n                mat3d s(mat3dd(0));\n                for (int n=0; n<nint; ++n)\n                {\n                    FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n                    FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n                    s += pt.m_sf;\n                    if (polar)\n                        s += polar->GetViscousPolar()->SkewStress(mp);\n                }\n                s /= nint;\n                \n                // Evaluate contribution to net force on surface.\n                // Negate the fluid traction since we want the traction on the surface,\n                // which is the opposite of the traction on the fluid.\n                fn -= s*m_area[j];\n            }\n        }\n    }\n    \n    // save results\n\ta << fn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSurfaceMoment::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n    int NF = pcs->Elements();\n    vec3d mn(0,0,0);    // initialize\n    \n    // initialize on the first pass to calculate the vectorial area of each surface element and to identify solid element associated with this surface element\n    if (m_binit) {\n        m_area.resize(NF);\n        for (int j=0; j<NF; ++j)\n        {\n            FESurfaceElement& el = pcs->Element(j);\n            m_area[j] = pcs->SurfaceNormal(el,0,0)*pcs->FaceArea(el);\n        }\n        m_binit = false;\n    }\n    \n    // calculate net fluid moment\n    for (int j=0; j<NF; ++j)\n    {\n        FESurfaceElement& el = pcs->Element(j);\n        \n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            FEPolarFluidMaterial* pfluid = pm->ExtractProperty<FEPolarFluidMaterial>();\n            \n            if (!pfluid) {\n                pe = el.m_elem[1].pe;\n                if (pe) pfluid = GetFEModel()->GetMaterial(pe->GetMatID())->ExtractProperty<FEPolarFluidMaterial>();\n            }\n            \n            // see if this is a fluid element\n            if (pfluid) {\n                // evaluate the average stress in this element\n                int nint = pe->GaussPoints();\n                mat3d s(mat3dd(0));\n                for (int n=0; n<nint; ++n)\n                {\n                    FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n                    if (pfluid->GetViscousPolar())\n                        s += pfluid->GetViscousPolar()->CoupleStress(mp);\n                }\n                s /= nint;\n                \n                // Evaluate contribution to net moment on surface.\n                // Negate the fluid couple vector since we want the couple vector on the surface,\n                // which is the opposite of the traction on the fluid.\n                mn -= s*m_area[j];\n            }\n        }\n    }\n    \n    // save results\n    a << mn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Plot contact pressure\nbool FEPlotFluidSurfacePressure::Save(FESurface &surf, FEDataStream& a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n    const int dof_EF = GetFEModel()->GetDOFIndex(\"ef\");\n    const int dof_T = GetFEModel()->GetDOFIndex(\"T\");\n    \n    writeElementValue<double>(surf, a, [=](int nface) {\n        FESurfaceElement& el = pcs->Element(nface);\n        double ef = pcs->Evaluate(nface, dof_EF);\n        double T = pcs->Evaluate(nface, dof_T);\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe) {\n            // get the material\n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n            if (!fluid) {\n                pe = el.m_elem[1].pe;\n                if (pe) fluid = GetFEModel()->GetMaterial(pe->GetMatID())->ExtractProperty<FEFluidMaterial>();\n            }\n            if (fluid) return fluid->Pressure(ef, T);\n            else return 0.;\n        }\n        else return 0.;\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSurfaceTractionPower::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n\n    int NF = pcs->Elements();\n    double fn = 0;    // initialize\n    \n    // initialize on the first pass to calculate the vectorial area of each surface element and to identify solid element associated with this surface element\n    if (m_binit) {\n        m_area.resize(NF);\n        for (int j=0; j<NF; ++j)\n        {\n            FESurfaceElement& el = pcs->Element(j);\n            m_area[j] = pcs->SurfaceNormal(el,0,0)*pcs->FaceArea(el);\n        }\n        m_binit = false;\n    }\n    \n    // calculate net fluid force\n    for (int j=0; j<NF; ++j)\n    {\n\t\tFESurfaceElement& el = pcs->Element(j);\n\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            FEFluidMaterial* pfluid = pm->ExtractProperty<FEFluidMaterial>();\n\n            // see if this is a fluid element\n            if (pfluid) {\n                // evaluate the average stress in this element\n                int nint = pe->GaussPoints();\n                double s = 0;\n                for (int n=0; n<nint; ++n)\n                {\n                    FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n                    FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n                    s += pt.m_vft*(pt.m_sf*m_area[j]);\n                }\n                s /= nint;\n                \n                // Evaluate contribution to net traction power on surface.\n                fn += s;\n            }\n        }\n    }\n    \n    // save results\n    a << fn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSurfaceEnergyFlux::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n\n    int NF = pcs->Elements();\n    double fn = 0;    // initialize\n    \n    // initialize on the first pass to calculate the vectorial area of each surface element and to identify solid element associated with this surface element\n    if (m_binit) {\n        m_area.resize(NF);\n        for (int j=0; j<NF; ++j)\n        {\n            FESurfaceElement& el = pcs->Element(j);\n            m_area[j] = pcs->SurfaceNormal(el,0,0)*pcs->FaceArea(el);\n        }\n        m_binit = false;\n    }\n    \n    // calculate net fluid force\n    for (int j=0; j<NF; ++j)\n    {\n\t\tFESurfaceElement& el = pcs->Element(j);\n\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            FEFluidMaterial* pfluid = pm->ExtractProperty<FEFluidMaterial>();\n\n            // see if this is a fluid element\n            if (pfluid) {\n                // evaluate the average stress in this element\n                int nint = pe->GaussPoints();\n                double s = 0;\n                for (int n=0; n<nint; ++n)\n                {\n                    FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n                    FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n                    s += pfluid->EnergyDensity(mp)*(pt.m_vft*m_area[j]);\n                }\n                s /= nint;\n                \n                // Evaluate contribution to net energy flux on surface.\n                fn += s;\n            }\n        }\n    }\n    \n    // save results\n    a << fn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidMassFlowRate::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n\n\tFEModel* fem = GetFEModel();\n    int dofWX = fem->GetDOFIndex(\"wx\");\n    int dofWY = fem->GetDOFIndex(\"wy\");\n    int dofWZ = fem->GetDOFIndex(\"wz\");\n    int dofEF = fem->GetDOFIndex(\"ef\");\n    \n    int NF = pcs->Elements();\n    double fn = 0;    // initialize\n    \n    FEMesh* m_pMesh = pcs->GetMesh();\n    \n    // calculate net fluid mass flow rate\n    for (int j=0; j<NF; ++j)\n    {\n\t\tFESurfaceElement& el = pcs->Element(j);\n\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = fem->GetMaterial(pe->GetMatID());\n            \n            // see if this is a fluid element\n            FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n            if (fluid) {\n                double rhor = fluid->m_rhor;\n                // get the surface element\n                FESurfaceElement& el = pcs->Element(j);\n                // extract nodal velocities and dilatation\n                int neln = el.Nodes();\n                vec3d vt[FEElement::MAX_NODES];\n                double et[FEElement::MAX_NODES];\n                for (int j=0; j<neln; ++j) {\n                    vt[j] = m_pMesh->Node(el.m_node[j]).get_vec3d(dofWX, dofWY, dofWZ);\n                    et[j] = m_pMesh->Node(el.m_node[j]).get(dofEF);\n                }\n                \n                // evaluate mass flux across this surface element\n                int nint = el.GaussPoints();\n                double*\tgw = el.GaussWeights();\n                vec3d gcov[2];\n                for (int n=0; n<nint; ++n) {\n                    vec3d v = el.eval(vt, n);\n                    double J = 1 + el.eval(et, n);\n                    pcs->CoBaseVectors(el, n, gcov);\n                    fn += (v*(gcov[0] ^ gcov[1]))*rhor/J*gw[n];\n                }\n            }\n        }\n    }\n    \n    // save results\n    a << fn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidFlowRate::Save(FESurface &surf, FEDataStream &a)\n{\n\tFESurface* pcs = &surf;\n\tif (pcs == 0) return false;\n\n\tint NF = pcs->Elements();\n\tdouble fn = 0;    // initialize\n\n\t// initialize on the first pass to calculate the vectorial area of each surface element and to identify solid element associated with this surface element\n\tif (m_binit) {\n\t\tm_area.resize(NF);\n\t\tfor (int j = 0; j<NF; ++j)\n\t\t{\n\t\t\tFESurfaceElement& el = pcs->Element(j);\n\t\t\tm_area[j] = pcs->SurfaceNormal(el, 0, 0)*pcs->FaceArea(el);\n\t\t}\n\t\tm_binit = false;\n\t}\n\n\t// calculate net flow rate normal to this surface\n\tfor (int j = 0; j<NF; ++j)\n\t{\n\t\tFESurfaceElement& el = pcs->Element(j);\n\n\t\t// get the element this surface element belongs to\n\t\tFEElement* pe = el.m_elem[0].pe;\n\t\tif (pe)\n\t\t{\n\t\t\t// evaluate the average fluid flux in this element\n\t\t\tint nint = pe->GaussPoints();\n\t\t\tvec3d w(0, 0, 0);\n\t\t\tfor (int n = 0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n\t\t\t\tFEFluidMaterialPoint* ptf = mp.ExtractData<FEFluidMaterialPoint>();\n\t\t\t\tif (ptf) w += ptf->m_vft / (ptf->m_ef + 1);\n\t\t\t}\n\t\t\tw /= nint;\n\n\t\t\t// Evaluate contribution to net flow rate across surface.\n\t\t\tfn += w*m_area[j];\n\t\t}\n\t}\n\n\t// save results\n\ta << fn;\n\n\treturn true;\n}\n\n//=============================================================================\n//\t\t\t\t\t\t\tD O M A I N   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidPressure::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* pt = (mp.ExtractData<FEFluidMaterialPoint>());\n\t\treturn (pt ? pt->m_pf : 0.0);\n\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// TODO: This plots the exact same value as FEFluidPressure. Delete?\nbool FEPlotElasticFluidPressure::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* pt = (mp.ExtractData<FEFluidMaterialPoint>());\n\t\treturn (pt ? pt->m_pf : 0.0);\n\t});\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nclass FEFluidVolumeRatio\n{\npublic:\n\tFEFluidVolumeRatio(FEModel* fem, FESolidDomain& dom) : m_dom(dom)\n\t{\n\t\tm_dofEF = fem->GetDOFIndex(\"ef\");\n\t}\n\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tFESolidElement* pel = dynamic_cast<FESolidElement*>(mp.m_elem);\n\t\tif (pel == nullptr) return 0.0;\n\n\t\tFESolidElement& el = *pel;\n\t\tFEMesh& mesh = *m_dom.GetMesh();\n\t\tint neln = el.Nodes();\n\t\tdouble et[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t\tet[j] = mesh.Node(el.m_node[j]).get(m_dofEF);\n\t\t\n\t\tdouble  Jf = 1.0 + el.Evaluate(et, mp.m_index);\n\t\treturn Jf;\n\t}\n\nprivate:\n\tFESolidDomain&\tm_dom;\n\tint m_dofEF;\n};\n\nbool FEPlotFluidVolumeRatio::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\t\twriteAverageElementValue<double>(dom, a, FEFluidVolumeRatio(GetFEModel(), sd));\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEFluidDensity\n{\npublic:\n\tFEFluidDensity(FEModel* fem, FESolidDomain& dom, FEFluidMaterial* pm) : m_dom(dom), m_mat(pm)\n\t{\n\t\tm_dofEF = fem->GetDOFIndex(\"ef\");\n\t}\n\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tFESolidElement* pel = dynamic_cast<FESolidElement*>(mp.m_elem);\n\t\tif (pel == nullptr) return 0.0;\n\t\t\t\n\t\tFESolidElement& el = *pel;\n\t\tFEMesh& mesh = *m_dom.GetMesh();\n\t\tint neln = el.Nodes();\n\t\tdouble et[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j<neln; ++j)\n\t\t\tet[j] = mesh.Node(el.m_node[j]).get(m_dofEF);\n\n\t\tdouble rhor = m_mat->m_rhor;\n\t\tdouble Jf = 1 + el.Evaluate(et, mp.m_index);\n\t\treturn rhor / Jf;\n\t}\n\nprivate:\n\tFESolidDomain&\tm_dom;\n\tFEFluidMaterial*\tm_mat;\n\tint m_dofEF;\n};\n\nbool FEPlotFluidDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\t\twriteAverageElementValue<double>(dom, a, FEFluidDensity(GetFEModel(), sd, pfluid));\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEFluidDensityRate\n{\npublic:\n\tFEFluidDensityRate(FEModel* fem, FESolidDomain& dom, FEFluidMaterial* pm) : m_dom(dom), m_mat(pm)\n\t{\n\t\tm_dofVX = fem->GetDOFIndex(\"vx\");\n\t\tm_dofVY = fem->GetDOFIndex(\"vy\");\n\t\tm_dofVZ = fem->GetDOFIndex(\"vz\");\n\t\tm_dofEF = fem->GetDOFIndex(\"ef\");\n\t\tm_dofAEF = fem->GetDOFIndex(\"aef\");\n\t}\n\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tFESolidElement* pel = dynamic_cast<FESolidElement*>(mp.m_elem);\n\t\tif (pel == nullptr) return 0.0;\n\n\t\tFESolidElement& el = *pel;\n\t\tFEMesh& mesh = *m_dom.GetMesh();\n\n\t\tvec3d vt[FEElement::MAX_NODES];\n\t\tdouble et[FEElement::MAX_NODES];\n\t\tdouble aet[FEElement::MAX_NODES];\n\t\tint neln = el.Nodes();\n\t\tfor (int j = 0; j<neln; ++j) {\n\t\t\tvt[j] = mesh.Node(el.m_node[j]).get_vec3d(m_dofVX, m_dofVY, m_dofVZ);\n\t\t\tet[j] = mesh.Node(el.m_node[j]).get(m_dofEF);\n\t\t\taet[j] = mesh.Node(el.m_node[j]).get(m_dofAEF);\n\t\t}\n\n\t\tdouble rhor = m_mat->m_rhor;\n\t\tdouble Jf = 1.0 + el.Evaluate(et, mp.m_index);\n\t\tdouble Jfdot = el.Evaluate(aet, mp.m_index);\n\t\tdouble divvs = m_dom.gradient(el, vt, mp.m_index).trace();\n\n\t\treturn rhor / Jf*(divvs - Jfdot / Jf);\n\t}\n\nprivate:\n\tFESolidDomain&\tm_dom;\n\tFEFluidMaterial* m_mat;\n\n\tint m_dofVX, m_dofVY, m_dofVZ;\n\tint m_dofEF, m_dofAEF;\n};\n\nbool FEPlotFluidDensityRate::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\t\twriteAverageElementValue<double>(sd, a, FEFluidDensityRate(GetFEModel(), sd, pfluid));\n\t\treturn true;\n    }\n    \n    return false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEFluidBodyForce\n{\npublic:\n    FEFluidBodyForce(FEModel* fem, FESolidDomain& dom) : m_fem(fem), m_dom(dom) {}\n    \n    vec3d operator()(const FEMaterialPoint& mp)\n    {\n        int NBL = m_fem->ModelLoads();\n        vec3d bf(0,0,0);\n        for (int j = 0; j<NBL; ++j)\n        {\n            FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(m_fem->ModelLoad(j));\n\t\t\tFEMaterialPoint& pt = const_cast<FEMaterialPoint&>(mp);\n\t\t\tif (pbf && pbf->IsActive()) bf += pbf->force(pt);\n        }\n\t\t// FEBio actually applies the negative of the body force\n        return -bf;\n    }\n\nprivate:\n    FESolidDomain&    m_dom;\n    FEModel*          m_fem;\n};\n\nbool FEPlotFluidBodyForce::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n        writeAverageElementValue<vec3d>(dom, a, FEFluidBodyForce(GetFEModel(), sd));\n        return true;\n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidVelocity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\treturn (ppt ? ppt->m_vft : vec3d(0.));\n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRelativeFluidVelocity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n    writeAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n        const FEFluidMaterialPoint* fpt = mp.ExtractData<FEFluidMaterialPoint>();\n        const FEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n        return (fpt ? fpt->m_vft - ept->m_v : vec3d(0.0));\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotBFSIPorosity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasicFSI* bp = dom.GetMaterial()->ExtractProperty<FEBiphasicFSI>();\n    if (bp == 0) return false;\n\n    // write solid element data\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        return bp->Porosity(const_cast<FEMaterialPoint&>(mp));\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotBFSISolidVolumeFraction::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasicFSI* bp = dom.GetMaterial()->ExtractProperty<FEBiphasicFSI>();\n    if (bp == 0) return false;\n    \n    // write solid element data\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        return bp->SolidVolumeFrac(const_cast<FEMaterialPoint&>(mp));\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFSIFluidFlux::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFSIMaterialPoint* ppt = mp.ExtractData<FEFSIMaterialPoint>();\n        return (ppt ? ppt->m_w : vec3d(0.0));\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPermeability::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasicFSI* bp = dom.GetMaterial()->ExtractProperty<FEBiphasicFSI>();\n    if (bp == 0) return false;\n    \n    writeAverageElementValue<mat3ds>(dom, a, [=](const FEMaterialPoint& mp) {\n        return bp->Permeability(const_cast<FEMaterialPoint&>(mp));\n    });\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotGradJ::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasicFSI* pbfsi = dom.GetMaterial()->ExtractProperty<FEBiphasicFSI>();\n    if (pbfsi == 0) return false;\n    \n    writeAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n        const FEBiphasicFSIMaterialPoint* bpt = mp.ExtractData<FEBiphasicFSIMaterialPoint>();\n        return (bpt ? bpt->m_gradJ : vec3d(0.0));\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotGradPhiF::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasicFSI* bp = dom.GetMaterial()->ExtractProperty<FEBiphasicFSI>();\n    if (bp == 0) return false;\n    \n    // write solid element data\n    writeAverageElementValue<vec3d>(dom, a, [=](const FEMaterialPoint& mp) {\n        return bp->gradPorosity(const_cast<FEMaterialPoint&>(mp));\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidAcceleration::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\treturn (ppt ? ppt->m_aft : vec3d(0.));\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidVorticity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\treturn (ppt ? ppt->Vorticity() : vec3d(0.));\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPolarFluidAngularVelocity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n    // write solid element data\n    writeAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n        const FEPolarFluidMaterialPoint* ppt = mp.ExtractData<FEPolarFluidMaterialPoint>();\n        const FEFluidMaterialPoint* pt = mp.ExtractData<FEFluidMaterialPoint>();\n        vec3d g(0,0,0);\n        if (ppt) g = ppt->m_gf;\n        else if (pt) g = pt->Vorticity()/2;\n        return g;\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPolarFluidRelativeAngularVelocity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n    // write solid element data\n    writeAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n        const FEFluidMaterialPoint* pt = mp.ExtractData<FEFluidMaterialPoint>();\n        const FEPolarFluidMaterialPoint* ppt = mp.ExtractData<FEPolarFluidMaterialPoint>();\n        return (ppt ? ppt->m_gf - pt->Vorticity()/2 : vec3d(0.));\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPolarFluidRegionalAngularVelocity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n    // write solid element data\n    writeAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n        const FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n        return (ppt ? ppt->Vorticity()/2 : vec3d(0.));\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average stresses for each element.\nbool FEPlotFluidStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\treturn (ppt ? ppt->m_sf : mat3ds(0.));\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average stresses for each element.\nbool FEPlotElementFluidRateOfDef::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\treturn (ppt ? ppt->RateOfDeformation() : mat3ds(0.));\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidStressPowerDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* ppt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\treturn (ppt ? (ppt->m_sf*ppt->m_Lf).trace() : 0.0);\n\t});\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidHeatSupplyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\tFEFluidMaterialPoint* ppt = (mp_noconst.ExtractData<FEFluidMaterialPoint>());\n\t\treturn (ppt ? -(pfluid->GetViscous()->Stress(mp_noconst)*ppt->m_Lf).trace() : 0.0);\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidShearViscosity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->GetViscous()->ShearViscosity(mp_noconst);\n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidStrainEnergyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->StrainEnergyDensity(mp_noconst);\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidKineticEnergyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->KineticEnergyDensity(mp_noconst);\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidEnergyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    // write solid element data\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->EnergyDensity(mp_noconst);\n\t});\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidBulkModulus::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n\n    // write solid element data\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->BulkModulus(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidElementStrainEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<double>(sd, a, [=](const FEMaterialPoint& mp) {\n\t\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\t\treturn pfluid->StrainEnergyDensity(mp_noconst);\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidElementKineticEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<double>(sd, a, [=](const FEMaterialPoint& mp) {\n\t\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\t\treturn pfluid->KineticEnergyDensity(mp_noconst);\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Can't use helper functions due to division by m.\nbool FEPlotFluidElementCenterOfMass::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\tdouble dens = pfluid->m_rhor;\n        \n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\tfor (int i=0; i<bd.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = bd.Element(i);\n\t\t\tdouble* gw = el.GaussWeights();\n                \n\t\t\t// integrate zeroth and first mass moments\n\t\t\tvec3d ew = vec3d(0,0,0);\n\t\t\tdouble m = 0;\n\t\t\tfor (int j=0; j<el.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFEFluidMaterialPoint& pt = *(el.GetMaterialPoint(j)->ExtractData<FEFluidMaterialPoint>());\n\t\t\t\tdouble detJ = bd.detJ0(el, j)*gw[j];\n\t\t\t\tew += pt.m_r0*(dens*detJ);\n\t\t\t\tm += dens*detJ;\n\t\t\t}\n                \n\t\t\ta << ew/m;\n\t\t}\n\t\treturn true;\n    }\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidElementLinearMomentum::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<vec3d>(bd, a, [=](const FEMaterialPoint& mp) {\n\t\t\tdouble dens = pfluid->m_rhor;\n\t\t\tconst FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n\t\t\treturn pt.m_vft*dens;\n\t\t});\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidElementAngularMomentum::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<vec3d>(bd, a, [=](const FEMaterialPoint& mp) {\n\t\t\tdouble dens = pfluid->m_rhor;\n\t\t\tconst FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n\t\t\treturn pt.m_vft*dens;\n\t\t});\n\t\treturn true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSpecificFreeEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->SpecificFreeEnergy(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSpecificEntropy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->SpecificEntropy(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSpecificInternalEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->SpecificInternalEnergy(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSpecificGaugeEnthalpy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->SpecificGaugeEnthalpy(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSpecificFreeEnthalpy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->SpecificFreeEnthalpy(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidSpecificStrainEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n\n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->SpecificStrainEnergy(mp_noconst);\n    });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidPressureTangentStrain::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n    if (pfluid == 0) return false;\n    \n    writeAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->Tangent_Strain(mp_noconst);\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average stresses for each element.\nbool FEPlotFSISolidStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEFluid>();\n    if (pfluid == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n        writeAverageElementValue<mat3ds>(sd, a, [](const FEMaterialPoint& mp) {\n            const FEFSIMaterialPoint* pt = (mp.ExtractData<FEFSIMaterialPoint>());\n            return (pt ? pt->m_ss : mat3ds(0.0));\n        });\n        return true;\n    }\n    \n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidShearStressError::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEFluid>();\n\tif (pfluid == 0) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\twriteRelativeError(dom, a, [](FEMaterialPoint& mp) {\n\t\t\tFEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n\t\t\tmat3ds s = fp.m_sf;\n\t\t\tdouble v = s.max_shear();\n\t\t\treturn v;\n\t\t});\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average polar fluid stresses for each element.\nbool FEPlotPolarFluidStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEViscousPolarFluid* vpfluid = dom.GetMaterial()->ExtractProperty<FEViscousPolarFluid>();\n    if (vpfluid == 0) return false;\n    FEViscousFluid* vfluid = dom.GetMaterial()->ExtractProperty<FEViscousFluid>();\n\n    // write solid element data\n    writeAverageElementValue<mat3d>(dom, a, [&](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return (vpfluid->SkewStress(mp_noconst) + vfluid->Stress(mp_noconst));\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average polar fluid couple stresses for each element.\nbool FEPlotPolarFluidCoupleStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEViscousPolarFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEViscousPolarFluid>();\n    if (pfluid == 0) return false;\n\n    // write solid element data\n    writeAverageElementValue<mat3d>(dom, a, [&](const FEMaterialPoint& mp) {\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        return pfluid->CoupleStress(mp_noconst);\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidRelativeReynoldsNumber::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n    writeAverageElementValue<double>(dom, a, [&pfluid](const FEMaterialPoint& mp) {\n        const FEFluidMaterialPoint* fpt = mp.ExtractData<FEFluidMaterialPoint>();\n        const FEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n        FEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n        double nu = pfluid->KinematicViscosity(mp_noconst);\n        vec3d v(0,0,0);\n        if (ept) v = ept->m_v;\n        return (fpt->m_vft - v).Length()/nu;\n    });\n    \n    return true;\n}\n\n//=================================================================================================\n//-----------------------------------------------------------------------------\nFEPlotFluidRelativePecletNumber::FEPlotFluidRelativePecletNumber(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n    SetUnits(UNIT_RECIPROCAL_LENGTH);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidRelativePecletNumber::Save(FEDomain &dom, FEDataStream& a)\n{\n    FESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n    if (pm == 0) return false;\n    \n    FEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n    if (pfluid == 0) return false;\n    \n    // figure out the local solute IDs. This depends on the material\n    int nsols = (int)m_sol.size();\n    vector<int> lid(nsols, -1);\n    int negs = 0;\n    for (int i = 0; i<(int)m_sol.size(); ++i)\n    {\n        lid[i] = pm->FindLocalSoluteID(m_sol[i]);\n        if (lid[i] < 0) negs++;\n    }\n    if (negs == nsols) return false;\n    \n    // loop over all elements\n    int N = dom.Elements();\n    for (int i = 0; i<N; ++i)\n    {\n        FEElement& el = dom.ElementRef(i);\n        \n        for (int k=0; k<nsols; ++k)\n        {\n            int nsid = lid[k];\n            if (nsid == -1) a << 0.f;\n            else\n            {\n                // calculate average relative Peclet number\n                double ew = 0;\n                for (int j = 0; j<el.GaussPoints(); ++j)\n                {\n                    FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                    const FEFluidMaterialPoint* fpt = mp.ExtractData<FEFluidMaterialPoint>();\n                    const FEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n                    vec3d v(0,0,0);\n                    if (ept) v = ept->m_v;\n                    ew += (fpt->m_vft - v).Length()/pm->GetFreeDiffusivity(mp, nsid);\n                }\n                ew /= el.GaussPoints();\n                a << ew;\n            }\n        }\n        \n    }\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioFluidPlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEPlotData.h\"\n#include <FECore/units.h>\n\n//=============================================================================\n//                            N O D E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Nodal displacement\nclass FEPlotDisplacement : public FEPlotNodeData\n{\npublic:\n    FEPlotDisplacement(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_LENGTH); }\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal fluid velocity\nclass FEPlotNodalFluidVelocity : public FEPlotNodeData\n{\npublic:\n    FEPlotNodalFluidVelocity(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_VELOCITY); }\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal relative fluid velocity\nclass FEPlotNodalRelativeFluidVelocity : public FEPlotNodeData\n{\npublic:\n    FEPlotNodalRelativeFluidVelocity(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_VELOCITY); }\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal fluid dilatation\nclass FEPlotFluidDilatation : public FEPlotNodeData\n{\npublic:\n    FEPlotFluidDilatation(FEModel* pfem) : FEPlotNodeData(pfem, PLT_FLOAT, FMT_NODE){}\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal effective fluid pressures\nclass FEPlotFluidEffectivePressure : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidEffectivePressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_NODE) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal polar fluid angular velocity\nclass FEPlotNodalPolarFluidAngularVelocity : public FEPlotNodeData\n{\npublic:\n    FEPlotNodalPolarFluidAngularVelocity(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_ANGULAR_VELOCITY); }\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//=============================================================================\n//                         S U R F A C E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Fluid surface force\n//!\nclass FEPlotFluidSurfaceForce : public FEPlotSurfaceData\n{\nprivate:\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotFluidSurfaceForce(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION){ SetUnits(UNIT_FORCE); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid surface moment (polar fluids)\n//!\nclass FEPlotFluidSurfaceMoment : public FEPlotSurfaceData\n{\nprivate:\n    bool                m_binit;\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotFluidSurfaceMoment(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION){ m_binit = true; SetUnits(UNIT_MOMENT); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid surface pressure\n//!\nclass FEPlotFluidSurfacePressure : public FEPlotSurfaceData\n{\npublic:\n    FEPlotFluidSurfacePressure(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid surface traction power\n//!\nclass FEPlotFluidSurfaceTractionPower : public FEPlotSurfaceData\n{\nprivate:\n    bool                m_binit;\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotFluidSurfaceTractionPower(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ m_binit = true; SetUnits(UNIT_POWER); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid surface energy flux\n//!\nclass FEPlotFluidSurfaceEnergyFlux : public FEPlotSurfaceData\n{\nprivate:\n    bool                m_binit;\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotFluidSurfaceEnergyFlux(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ m_binit = true; SetUnits(UNIT_POWER); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid mass flow rate\n//!\nclass FEPlotFluidMassFlowRate : public FEPlotSurfaceData\n{\npublic:\n    FEPlotFluidMassFlowRate(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION) { SetUnits(UNIT_MASS_FLOW_RATE); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid flow rate\n//!\nclass FEPlotFluidFlowRate : public FEPlotSurfaceData\n{\nprivate:\n\tbool                m_binit;\n\tvector<vec3d>       m_area;\n\npublic:\n\tFEPlotFluidFlowRate(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ m_binit = true; SetUnits(UNIT_FLOW_RATE); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//=============================================================================\n//\t\t\t\t\t\t\tD O M A I N   D A T A\n//=============================================================================\n\n\n//-----------------------------------------------------------------------------\n//! Actual fluid pressure\nclass FEPlotFluidPressure : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidPressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element elastic fluid pressure\nclass FEPlotElasticFluidPressure : public FEPlotDomainData\n{\npublic:\n\tFEPlotElasticFluidPressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid volume ratio\nclass FEPlotFluidVolumeRatio : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidVolumeRatio(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid density\nclass FEPlotFluidDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid density rate\nclass FEPlotFluidDensityRate : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidDensityRate(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_DENSITY_RATE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid body force\nclass FEPlotFluidBodyForce : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidBodyForce(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_FORCE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid velocity\nclass FEPlotFluidVelocity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element relative fluid velocity\nclass FEPlotRelativeFluidVelocity : public FEPlotDomainData\n{\npublic:\n    FEPlotRelativeFluidVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element relative fluid flux\nclass FEPlotFSIFluidFlux : public FEPlotDomainData\n{\npublic:\n    FEPlotFSIFluidFlux(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Permeability\nclass FEPlotPermeability : public FEPlotDomainData\n{\npublic:\n    FEPlotPermeability(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PERMEABILITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element GradJ\nclass FEPlotGradJ : public FEPlotDomainData\n{\npublic:\n    FEPlotGradJ(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element gradphif\nclass FEPlotGradPhiF : public FEPlotDomainData\n{\npublic:\n    FEPlotGradPhiF(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element porosity\nclass FEPlotBFSIPorosity : public FEPlotDomainData\n{\npublic:\n    FEPlotBFSIPorosity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element solid volume fraction\nclass FEPlotBFSISolidVolumeFraction : public FEPlotDomainData\n{\npublic:\n    FEPlotBFSISolidVolumeFraction(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid acceleration\nclass FEPlotFluidAcceleration : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidAcceleration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ACCELERATION); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid vorticity\nclass FEPlotFluidVorticity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidVorticity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ANGULAR_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element polar fluid angular velocity\nclass FEPlotPolarFluidAngularVelocity : public FEPlotDomainData\n{\npublic:\n    FEPlotPolarFluidAngularVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ANGULAR_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element polar fluid relative angular velocity\nclass FEPlotPolarFluidRelativeAngularVelocity : public FEPlotDomainData\n{\npublic:\n    FEPlotPolarFluidRelativeAngularVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ANGULAR_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element polar fluid rregional angular velocity\nclass FEPlotPolarFluidRegionalAngularVelocity : public FEPlotDomainData\n{\npublic:\n    FEPlotPolarFluidRegionalAngularVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ANGULAR_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid stresses\nclass FEPlotFluidStress : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid rate of deformation\nclass FEPlotElementFluidRateOfDef : public FEPlotDomainData\n{\npublic:\n    FEPlotElementFluidRateOfDef(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_RECIPROCAL_TIME); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid stress power density\nclass FEPlotFluidStressPowerDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidStressPowerDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_POWER_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid heat supply density\nclass FEPlotFluidHeatSupplyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidHeatSupplyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_POWER_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid shear viscosity\nclass FEPlotFluidShearViscosity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidShearViscosity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_VISCOSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element strain energy density\nclass FEPlotFluidStrainEnergyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidStrainEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_ENERGY_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element kinetic energy density\nclass FEPlotFluidKineticEnergyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidKineticEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_ENERGY_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element energy density\nclass FEPlotFluidEnergyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_ENERGY_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element fluid bulk modulus\nclass FEPlotFluidBulkModulus : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidBulkModulus(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy\nclass FEPlotFluidElementStrainEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidElementStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Kinetic energy\nclass FEPlotFluidElementKineticEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidElementKineticEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Center of mass\nclass FEPlotFluidElementCenterOfMass : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidElementCenterOfMass(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_LENGTH); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Linear momentum\nclass FEPlotFluidElementLinearMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidElementLinearMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_LINEAR_MOMENTUM); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Angular momentum\nclass FEPlotFluidElementAngularMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidElementAngularMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ANGULAR_MOMENTUM); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific free energy\nclass FEPlotFluidSpecificFreeEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidSpecificFreeEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific entropy\nclass FEPlotFluidSpecificEntropy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidSpecificEntropy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENTROPY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific internal energy\nclass FEPlotFluidSpecificInternalEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidSpecificInternalEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific gauge enthalpy\nclass FEPlotFluidSpecificGaugeEnthalpy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidSpecificGaugeEnthalpy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific free enthalpy\nclass FEPlotFluidSpecificFreeEnthalpy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidSpecificFreeEnthalpy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific strain energy\nclass FEPlotFluidSpecificStrainEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidSpecificStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid pressure tangent strain\nclass FEPlotFluidPressureTangentStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidPressureTangentStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENTROPY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element solid stresses\nclass FEPlotFSISolidStress : public FEPlotDomainData\n{\npublic:\n    FEPlotFSISolidStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! fluid shear stress error\nclass FEPlotFluidShearStressError : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidShearStressError(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element polar fluid  stresses\nclass FEPlotPolarFluidStress : public FEPlotDomainData\n{\npublic:\n    FEPlotPolarFluidStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3F, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element polar fluid couple stresses\nclass FEPlotPolarFluidCoupleStress : public FEPlotDomainData\n{\npublic:\n    FEPlotPolarFluidCoupleStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3F, FMT_ITEM) { SetUnits(UNIT_ENERGY_AREAL_DENSITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element relative Reynolds number\nclass FEPlotFluidRelativeReynoldsNumber : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidRelativeReynoldsNumber(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_RECIPROCAL_LENGTH); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element relative Peclet number\nclass FEPlotFluidRelativePecletNumber : public FEPlotDomainData\n{\npublic:\n    FEPlotFluidRelativePecletNumber(FEModel* pfem);\n    bool Save(FEDomain& dom, FEDataStream& a);\nprotected:\n    vector<int>    m_sol;\n};\n"
  },
  {
    "path": "FEBioFluid/FEBioFluidSolutes.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidModule.h\"\n#include \"FEBioFluidSolutes.h\"\n#include \"FEFluidSolutesSolver.h\"\n#include \"FEFluidSolutes.h\"\n#include \"FEFluidSolutesDomain3D.h\"\n#include \"FEFluidSolutesDomainFactory.h\"\n#include \"FESoluteBackflowStabilization.h\"\n#include \"FEInitialFluidSolutesPressure.h\"\n#include \"FEFluidSolutesFlux.h\"\n#include \"FEFluidSolutesNaturalFlux.h\"\n#include \"FEFluidSolutesPressureBC.h\"\n#include \"FEFluidSolutesResistanceBC.h\"\n#include \"FEFluidSolutesRCRBC.h\"\n#include \"FESoluteConvectiveFlow.h\"\n#include \"FESolutesSolver.h\"\n#include \"FESolutesMaterial.h\"\n#include \"FESolutesDomain.h\"\n#include \"FESolutesDomainFactory.h\"\n#include \"FEBioFluidPlot.h\"\n#include <FEBioMix/FESoluteFlux.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FETimeStepController.h>\n#include \"FEFluidSolutesAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nconst char* FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_SOLUTES_VARIABLE var)\n{\n    switch (var)\n    {\n        case DISPLACEMENT                : return \"displacement\"               ; break;\n        case RELATIVE_FLUID_VELOCITY     : return \"relative fluid velocity\"    ; break;\n        case RELATIVE_FLUID_ACCELERATION : return \"relative fluid acceleration\"; break;\n        case FLUID_DILATATION            : return \"fluid dilatation\"           ; break;\n        case FLUID_DILATATION_TDERIV     : return \"fluid dilatation tderiv\"    ; break;\n        case FLUID_CONCENTRATION         : return \"concentration\"              ; break;\n        case FLUID_CONCENTRATION_TDERIV  : return \"concentration tderiv\"       ; break;\n    }\n    assert(false);\n    return nullptr;\n}\n\nvoid FEBioFluidSolutes::InitModule()\n{\n    FECoreKernel& febio = FECoreKernel::GetInstance();\n    \n    // register domain\n    febio.RegisterDomain(new FEFluidSolutesDomainFactory);\n    \n    // define the fsi module\n    febio.CreateModule(new FEFluidSolutesModule, \"fluid-solutes\",\n                       \"{\"\n                       \"   \\\"title\\\" : \\\"Fluid-Solutes\\\",\"\n                       \"   \\\"info\\\"  : \\\"Fluid analysis with solute transport and reactive processes.\\\"\"\n                       \"}\");\n\tfebio.AddModuleDependency(\"fluid\");\n    febio.AddModuleDependency(\"multiphasic\"); // also pulls in solid, biphasic, solutes\n    \n    //-----------------------------------------------------------------------------\n    // analysis classes (default type must match module name!)\n    REGISTER_FECORE_CLASS(FEFluidSolutesAnalysis, \"fluid-solutes\");\n\n\t// monolithic fluid-solutes solver\n    REGISTER_FECORE_CLASS(FEFluidSolutesSolver, \"fluid-solutes\");\n    REGISTER_FECORE_CLASS(FEFluidSolutes, \"fluid-solutes\");\n    REGISTER_FECORE_CLASS(FEFluidSolutesDomain3D, \"fluid-solutes-3D\");\n    \n    // loads\n    REGISTER_FECORE_CLASS(FEFluidSolutesFlux           , \"solute flux\"                  );\n    REGISTER_FECORE_CLASS(FESoluteBackflowStabilization, \"solute backflow stabilization\");\n    REGISTER_FECORE_CLASS(FEFluidSolutesNaturalFlux    , \"solute natural flux\"          , FECORE_EXPERIMENTAL);\n    REGISTER_FECORE_CLASS(FESoluteConvectiveFlow       , \"solute convective flow\"       , FECORE_EXPERIMENTAL);\n\n    // bcs\n    REGISTER_FECORE_CLASS(FEFluidSolutesPressureBC     , \"fluid pressure\"  );\n    REGISTER_FECORE_CLASS(FEFluidSolutesResistanceBC   , \"fluid resistance\");\n    REGISTER_FECORE_CLASS(FEFluidSolutesRCRBC          , \"fluid RCR\"       );\n\n    // ics\n//    REGISTER_FECORE_CLASS(FEInitialFluidSolutesPressure, \"initial fluid pressure\");\n\n    //-----------------------------------------------------------------------------\n    // classes derived from FEPlotData\n    REGISTER_FECORE_CLASS(FEPlotFluidRelativePecletNumber, \"solute relative Peclet number\");\n\n\t// solutes solver classes\n\tfebio.RegisterDomain(new FESolutesDomainFactory);\n\tREGISTER_FECORE_CLASS(FESolutesSolver, \"solutes\");\n\tREGISTER_FECORE_CLASS(FESolutesMaterial, \"solutes\");\n\tREGISTER_FECORE_CLASS(FESolutesDomain, \"solutes-3D\");\n\n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioFluidSolutes.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\nnamespace FEBioFluidSolutes {\n    \n    FEBIOFLUID_API void InitModule();\n    \n    enum FLUID_SOLUTES_VARIABLE {\n        DISPLACEMENT,\n        RELATIVE_FLUID_VELOCITY,\n        RELATIVE_FLUID_ACCELERATION,\n        FLUID_DILATATION,\n        FLUID_DILATATION_TDERIV,\n        FLUID_CONCENTRATION,\n        FLUID_CONCENTRATION_TDERIV\n    };\n    \n    FEBIOFLUID_API const char* GetVariableName(FLUID_SOLUTES_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioMultiphasicFSI.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEMultiphasicFSISolver.h\"\n#include \"FEMultiphasicFSI.h\"\n#include \"FEMultiphasicFSIDomain3D.h\"\n#include \"FEBiphasicFSITraction.h\"\n#include \"FEMultiphasicFSIDomainFactory.h\"\n#include \"FEBackFlowFSIStabilization.h\"\n#include \"FETangentialFlowFSIStabilization.h\"\n#include \"FEMultiphasicFSISoluteFlux.h\"\n#include \"FEMultiphasicFSIPressure.h\"\n#include \"FEMultiphasicFSIPressureBC.h\"\n#include \"FESoluteConvectiveFlow.h\"\n#include \"FEMultiphasicFSISoluteBackflowStabilization.h\"\n#include \"FEFluidModule.h\"\n#include \"FEMultiphasicFSIAnalysis.h\"\n#include <FECore/FETimeStepController.h>\n\n//-----------------------------------------------------------------------------\nconst char* FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::MULTIPHASIC_FSI_VARIABLE var)\n{\n    switch (var)\n    {\n        case DISPLACEMENT                : return \"displacement\"               ; break;\n        case VELOCITY                    : return \"velocity\"                   ; break;\n        case ROTATION                    : return \"rotation\"                   ; break;\n        case SHELL_DISPLACEMENT          : return \"shell displacement\"         ; break;\n        case SHELL_VELOCITY              : return \"shell velocity\"             ; break;\n        case SHELL_ACCELERATION          : return \"shell acceleration\"         ; break;\n        case RIGID_ROTATION              : return \"rigid rotation\"             ; break;\n        case RELATIVE_FLUID_VELOCITY     : return \"relative fluid velocity\"    ; break;\n        case RELATIVE_FLUID_ACCELERATION : return \"relative fluid acceleration\"; break;\n        case FLUID_VELOCITY              : return \"fluid velocity\"             ; break;\n        case FLUID_ACCELERATION          : return \"fluid acceleration\"         ; break;\n        case FLUID_DILATATION            : return \"fluid dilatation\"           ; break;\n        case FLUID_DILATATION_TDERIV     : return \"fluid dilatation tderiv\"    ; break;\n        case FLUID_CONCENTRATION         : return \"concentration\"              ; break;\n        case FLUID_CONCENTRATION_TDERIV  : return \"concentration tderiv\"       ; break;\n    }\n    assert(false);\n    return nullptr;\n}\n\nvoid FEBioMultiphasicFSI::InitModule()\n{\n    FECoreKernel& febio = FECoreKernel::GetInstance();\n    \n    // register domain\n    febio.RegisterDomain(new FEMultiphasicFSIDomainFactory);\n    \n    // define the fsi module\n    febio.CreateModule(new FEMultiphasicFSIModule, \"multiphasic-FSI\");\n    febio.AddModuleDependency(\"fluid\");\n    febio.AddModuleDependency(\"multiphasic\");    // also pulls in solid, biphasic, solutes\n    \n    //-----------------------------------------------------------------------------\n    // analysis classes (default type must match module name!)\n    REGISTER_FECORE_CLASS(FEMultiphasicFSIAnalysis, \"multiphasic-FSI\");\n\n    //-----------------------------------------------------------------------------\n    REGISTER_FECORE_CLASS(FEMultiphasicFSISolver, \"multiphasic-FSI\");\n    \n    REGISTER_FECORE_CLASS(FEMultiphasicFSI, \"multiphasic-FSI\");\n    \n    REGISTER_FECORE_CLASS(FEMultiphasicFSIDomain3D, \"multiphasic-FSI-3D\");\n    \n    REGISTER_FECORE_CLASS(FEBiphasicFSITraction, \"multiphasic-FSI traction\");\n    \n    REGISTER_FECORE_CLASS(FEBackFlowFSIStabilization, \"fluid backflow stabilization\");\n    \n    REGISTER_FECORE_CLASS(FETangentialFlowFSIStabilization, \"fluid tangential stabilization\");\n    \n    // loads\n    REGISTER_FECORE_CLASS(FEMultiphasicFSISoluteFlux, \"solute flux\");\n    REGISTER_FECORE_CLASS(FEMultiphasicFSISoluteBackflowStabilization, \"solute backflow stabilization\");\n    REGISTER_FECORE_CLASS(FEMultiphasicFSIPressure, \"fluid pressure\", 0x0300); // deprecated, use BC version\n\n    // bcs\n    REGISTER_FECORE_CLASS(FEMultiphasicFSIPressureBC, \"fluid pressure\");\n\n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioMultiphasicFSI.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\nnamespace FEBioMultiphasicFSI {\n    \n    FEBIOFLUID_API void InitModule();\n    \n    enum MULTIPHASIC_FSI_VARIABLE {\n        DISPLACEMENT,\n        VELOCITY,\n        ROTATION,\n        SHELL_DISPLACEMENT,\n        SHELL_VELOCITY,\n        SHELL_ACCELERATION,\n        RIGID_ROTATION,\n        RELATIVE_FLUID_VELOCITY,\n        RELATIVE_FLUID_ACCELERATION,\n        FLUID_VELOCITY,\n        FLUID_ACCELERATION,\n        FLUID_DILATATION,\n        FLUID_DILATATION_TDERIV,\n        FLUID_CONCENTRATION,\n        FLUID_CONCENTRATION_TDERIV\n    };\n    \n    FEBIOFLUID_API const char* GetVariableName(MULTIPHASIC_FSI_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioPolarFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioPolarFluid.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEPolarFluidSolver.h\"\n#include \"FEPolarFluid.h\"\n#include \"FEViscousPolarLinear.h\"\n#include \"FEPolarFluidDomain3D.h\"\n#include \"FEPolarFluidDomainFactory.h\"\n#include \"FEPolarFluidAnalysis.h\"\n#include \"FETangentialFlowPFStabilization.h\"\n#include \"FEFluidModule.h\"\n#include \"FEInitialFluidAngularVelocity.h\"\n#include \"FEFixedFluidAngularVelocity.h\"\n#include \"FEPrescribedFluidAngularVelocity.h\"\n#include \"FEBioFluidPlot.h\"\n#include \"FEConstFluidBodyMoment.h\"\n#include <FECore/FETimeStepController.h>\n\n//-----------------------------------------------------------------------------\nconst char* FEBioPolarFluid::GetVariableName(FEBioPolarFluid::POLAR_FLUID_VARIABLE var)\n{\n    switch (var)\n    {\n        case DISPLACEMENT                : return \"displacement\"               ; break;\n        case RELATIVE_FLUID_VELOCITY     : return \"relative fluid velocity\"    ; break;\n        case RELATIVE_FLUID_ACCELERATION : return \"relative fluid acceleration\"; break;\n        case FLUID_ANGULAR_VELOCITY      : return \"fluid angular velocity\"     ; break;\n        case FLUID_ANGULAR_ACCELERATION  : return \"fluid angular acceleration\" ; break;\n        case FLUID_DILATATION            : return \"fluid dilatation\"           ; break;\n        case FLUID_DILATATION_TDERIV     : return \"fluid dilatation tderiv\"    ; break;\n    }\n    assert(false);\n    return nullptr;\n}\n\nvoid FEBioPolarFluid::InitModule()\n{\n    FECoreKernel& febio = FECoreKernel::GetInstance();\n    \n    // register domain\n    febio.RegisterDomain(new FEPolarFluidDomainFactory);\n    \n    // define the polar fluid module\n    febio.CreateModule(new FEPolarFluidModule, \"polar fluid\",\n                       \"{\"\n                       \"   \\\"title\\\" : \\\"Polar Fluid\\\",\"\n                       \"   \\\"info\\\"  : \\\"Polar fluid analysis.\\\"\"\n                       \"}\");\n    \n    febio.AddModuleDependency(\"fluid\");\n    \n    //-----------------------------------------------------------------------------\n    // analysis classes (default type must match module name!)\n    REGISTER_FECORE_CLASS(FEPolarFluidAnalysis, \"polar fluid\");\n    \n    //-----------------------------------------------------------------------------\n    // solver classes\n    REGISTER_FECORE_CLASS(FEPolarFluidSolver  , \"polar fluid\");\n    \n    //-----------------------------------------------------------------------------\n    // Materials\n    REGISTER_FECORE_CLASS(FEPolarFluid        , \"polar fluid\");\n    REGISTER_FECORE_CLASS(FEViscousPolarLinear, \"polar linear\");\n\n    //-----------------------------------------------------------------------------\n    // Domain classes\n    REGISTER_FECORE_CLASS(FEPolarFluidDomain3D, \"polar-fluid-3D\");\n    \n    //-----------------------------------------------------------------------------\n    // initial conditions\n    REGISTER_FECORE_CLASS(FEInitialFluidAngularVelocity  , \"initial fluid angular velocity\");\n    \n    //-----------------------------------------------------------------------------\n    // boundary conditions\n    REGISTER_FECORE_CLASS(FEFixedFluidAngularVelocity       , \"zero fluid angular velocity\"      );\n    REGISTER_FECORE_CLASS(FEPrescribedFluidAngularVelocity  , \"prescribed fluid angular velocity\");\n    \n    //-----------------------------------------------------------------------------\n    // Surface loads\n    REGISTER_FECORE_CLASS(FETangentialFlowPFStabilization   , \"fluid tangential stabilization\"   );\n    \n    //-----------------------------------------------------------------------------\n    // Body loads\n    REGISTER_FECORE_CLASS(FEConstFluidBodyMoment   , \"polar fluid body moment\");\n    \n    //-----------------------------------------------------------------------------\n    // classes derived from FEPlotData\n    REGISTER_FECORE_CLASS(FEPlotNodalPolarFluidAngularVelocity   , \"nodal polar fluid angular velocity\"   );\n    REGISTER_FECORE_CLASS(FEPlotPolarFluidAngularVelocity        , \"polar fluid angular velocity\"         );\n    REGISTER_FECORE_CLASS(FEPlotPolarFluidRelativeAngularVelocity, \"polar fluid relative angular velocity\");\n    REGISTER_FECORE_CLASS(FEPlotPolarFluidRegionalAngularVelocity, \"polar fluid regional angular velocity\");\n    REGISTER_FECORE_CLASS(FEPlotPolarFluidStress                 , \"polar fluid stress\"                   );\n    REGISTER_FECORE_CLASS(FEPlotPolarFluidCoupleStress           , \"polar fluid couple stress\"            );\n    REGISTER_FECORE_CLASS(FEPlotFluidSurfaceMoment               , \"fluid surface moment\"                 );\n    \n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioPolarFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\nnamespace FEBioPolarFluid {\n\nFEBIOFLUID_API void InitModule();\n\nenum POLAR_FLUID_VARIABLE {\n    DISPLACEMENT,\n    RELATIVE_FLUID_VELOCITY,\n    RELATIVE_FLUID_ACCELERATION,\n    FLUID_ANGULAR_VELOCITY,\n    FLUID_ANGULAR_ACCELERATION,\n    FLUID_DILATATION,\n    FLUID_DILATATION_TDERIV\n};\n\nFEBIOFLUID_API const char* GetVariableName(POLAR_FLUID_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioThermoFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioThermoFluid.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEThermoFluidSolver.h\"\n#include \"FEThermoFluid.h\"\n#include \"FEThermoFluidDomain3D.h\"\n#include \"FEThermoFluidDomainFactory.h\"\n#include \"FEFixedFluidTemperature.h\"\n#include \"FEInitialFluidTemperature.h\"\n#include \"FEInitialFluidPressureTemperature.h\"\n#include \"FEPrescribedFluidTemperature.h\"\n#include \"FEFluidHeatSupplyConst.h\"\n#include \"FEFluidNormalHeatFlux.h\"\n#include \"FEFluidNaturalHeatFlux.h\"\n#include \"FENewtonianThermoFluid.h\"\n#include \"FENewtonianRealVapor.h\"\n#include \"FEIdealGas.h\"\n#include \"FERealGas.h\"\n#include \"FERealVapor.h\"\n#include \"FERealLiquid.h\"\n#include \"FEFluidConstantConductivity.h\"\n#include \"FETempDependentConductivity.h\"\n#include \"FEConductivityRealVapor.h\"\n#include \"FEThermoFluidPressureLoad.h\"\n#include \"FETemperatureBackFlowStabilization.h\"\n#include \"FEThermoFluidPressureBC.h\"\n#include \"FEThermoFluidTemperatureBC.h\"\n#include \"FEFluidModule.h\"\n#include \"FEThermoFluidAnalysis.h\"\n#include \"FEBioThermoFluidPlot.h\"\n\nconst char* FEBioThermoFluid::GetVariableName(FEBioThermoFluid::THERMOFLUID_VARIABLE var)\n{\n    switch (var)\n    {\n    case DISPLACEMENT                : return \"displacement\"               ; break;\n    case RELATIVE_FLUID_VELOCITY     : return \"relative fluid velocity\"    ; break;\n    case RELATIVE_FLUID_ACCELERATION : return \"relative fluid acceleration\"; break;\n    case FLUID_DILATATION            : return \"fluid dilatation\"           ; break;\n    case FLUID_DILATATION_TDERIV     : return \"fluid dilatation tderiv\"    ; break;\n    case TEMPERATURE                 : return \"temperature\"                ; break;\n    case TEMPERATURE_TDERIV          : return \"temperature tderiv\"         ; break;\n    }\n    assert(false);\n    return nullptr;\n}\n\nvoid FEBioThermoFluid::InitModule()\n{\n    FECoreKernel& febio = FECoreKernel::GetInstance();\n\n    // register domain\n    febio.RegisterDomain(new FEThermoFluidDomainFactory);\n\n    // define the thermo-fluid module\n    febio.CreateModule(new FEThermoFluidModule, \"thermo-fluid\",\n                       \"{\"\n                       \"   \\\"title\\\" : \\\"Thermofluid\\\",\"\n                       \"   \\\"info\\\"  : \\\"Fluid analysis with heat transfer and thermodynamics.\\\"\"\n                       \"}\");\n    febio.AddModuleDependency(\"fluid\");\n\n    //-----------------------------------------------------------------------------\n    // analysis classes (default type must match module name!)\n    REGISTER_FECORE_CLASS(FEThermoFluidAnalysis, \"thermo-fluid\");\n\n    //-----------------------------------------------------------------------------\n    REGISTER_FECORE_CLASS(FEThermoFluidSolver, \"thermo-fluid\");\n\n    REGISTER_FECORE_CLASS(FEThermoFluid, \"thermo-fluid\");\n\n    REGISTER_FECORE_CLASS(FEThermoFluidDomain3D, \"thermo-fluid-3D\");\n\n    //-----------------------------------------------------------------------------\n    // initial conditions\n    REGISTER_FECORE_CLASS(FEInitialFluidTemperature  , \"initial fluid temperature\");\n    REGISTER_FECORE_CLASS(FEInitialFluidPressureTemperature  , \"initial fluid pressure and temperature\");\n\n    //-----------------------------------------------------------------------------\n    // boundary conditions\n    REGISTER_FECORE_CLASS(FEFixedFluidTemperature       , \"zero fluid temperature\"      );\n    REGISTER_FECORE_CLASS(FEPrescribedFluidTemperature  , \"prescribed fluid temperature\");\n    REGISTER_FECORE_CLASS(FEThermoFluidPressureBC       , \"fluid pressure\");\n    REGISTER_FECORE_CLASS(FEThermoFluidTemperatureBC    , \"natural temperature\");\n\n    //-----------------------------------------------------------------------------\n    // Surface loads\n    REGISTER_FECORE_CLASS(FEFluidNormalHeatFlux, \"fluid heat flux\");\n    REGISTER_FECORE_CLASS(FEFluidNaturalHeatFlux, \"fluid natural heat flux\");\n    REGISTER_FECORE_CLASS(FETemperatureBackFlowStabilization, \"temperature backflow stabilization\");\n\n    //-----------------------------------------------------------------------------\n    // Body loads\n    REGISTER_FECORE_CLASS(FEFluidHeatSupplyConst   , \"constant fluid heat supply\");\n    \n    //-----------------------------------------------------------------------------\n    // Materials\n    \n    // viscous thermofluids\n    REGISTER_FECORE_CLASS(FENewtonianThermoFluid, \"Newtonian fluid\");\n    REGISTER_FECORE_CLASS(FENewtonianRealVapor, \"Newtonian real vapor\");\n\n    // elastic fluids\n    REGISTER_FECORE_CLASS(FEIdealGas   , \"ideal gas\"   );\n    REGISTER_FECORE_CLASS(FERealGas    , \"real gas\"    );\n    REGISTER_FECORE_CLASS(FERealVapor  , \"real vapor\"  );\n    REGISTER_FECORE_CLASS(FERealLiquid , \"real liquid\" );\n    \n    // thermal conductivity\n    REGISTER_FECORE_CLASS(FEFluidConstantConductivity, \"constant thermal conductivity\");\n    REGISTER_FECORE_CLASS(FETempDependentConductivity, \"temp-dependent thermal conductivity\");\n    REGISTER_FECORE_CLASS(FEConductivityRealVapor    , \"real vapor thermal conductivity\");\n    \n    //-----------------------------------------------------------------------------\n    // loads\n    REGISTER_FECORE_CLASS(FEThermoFluidPressureLoad, \"fluid pressure constraint\");\n\n    //-----------------------------------------------------------------------------\n    // classes derived from FEPlotData\n\tREGISTER_FECORE_CLASS(FEPlotFluidTemperature, \"fluid temperature\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalFluidTemperature, \"nodal fluid temperature\");\n\tREGISTER_FECORE_CLASS(FEPlotFluidPressureTangentTemperature, \"fluid pressure tangent temperature\");\n\tREGISTER_FECORE_CLASS(FEPlotFluidRelativeThermalPecletNumber, \"fluid relative thermal Peclet number\");\n\tREGISTER_FECORE_CLASS(FEPlotFluidIsochoricSpecificHeatCapacity, \"fluid isochoric specific heat capacity\");\n\tREGISTER_FECORE_CLASS(FEPlotFluidIsobaricSpecificHeatCapacity, \"fluid isobaric specific heat capacity\");\n\tREGISTER_FECORE_CLASS(FEPlotFluidThermalConductivity, \"fluid thermal conductivity\");\n\tREGISTER_FECORE_CLASS(FEPlotFluidHeatFlux, \"fluid heat flux\");\n\n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioThermoFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEBioThermoFluid module\n\n//! The FEBioThermoFluid module adds thermofluid capabilities to FEBio.\n//!\nnamespace FEBioThermoFluid {\n\n    FEBIOFLUID_API void InitModule();\n\n    enum THERMOFLUID_VARIABLE {\n        DISPLACEMENT,\n        RELATIVE_FLUID_VELOCITY,\n        FLUID_DILATATION,\n        RELATIVE_FLUID_ACCELERATION,\n        FLUID_DILATATION_TDERIV,\n        TEMPERATURE,\n        TEMPERATURE_TDERIV\n    };\n\n    FEBIOFLUID_API const char* GetVariableName(THERMOFLUID_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioThermoFluidPlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBioThermoFluidPlot.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEThermoFluid.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n#include <FECore/writeplot.h>\n#include <FECore/FEModel.h>\n\nbool FEPlotFluidTemperature::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\treturn pfluid->Temperature(const_cast<FEMaterialPoint&>(mp));\n\t\t});\n\treturn true;\n}\n\n//! Store the nodal temperatures\nbool FEPlotNodalFluidTemperature::Save(FEMesh& m, FEDataStream& a)\n{\n\t// get the dilatation dof index\n\tint dof_T = GetFEModel()->GetDOFIndex(\"T\");\n\tif (dof_T < 0) return false;\n\n\t// loop over all nodes\n\twriteNodalValues<double>(m, a, [=](const FENode& node) {\n\t\treturn node.get(dof_T);\n\t\t});\n\treturn true;\n}\n\nbool FEPlotFluidPressureTangentTemperature::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->Tangent_Temperature(mp_noconst);\n\t\t});\n\n\treturn true;\n}\n\nbool FEPlotFluidHeatFlux::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEFluidMaterial* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidMaterial>();\n\tif (pfluid == 0) return false;\n\n\t// write solid element data\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEThermoFluidMaterialPoint* ppt = mp.ExtractData<FEThermoFluidMaterialPoint>();\n\t\treturn (ppt ? ppt->m_q : vec3d(0.));\n\t\t});\n\n\treturn true;\n}\n\nbool FEPlotFluidRelativeThermalPecletNumber::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEThermoFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEThermoFluid>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [&pfluid](const FEMaterialPoint& mp) {\n\t\tconst FEFluidMaterialPoint* fpt = mp.ExtractData<FEFluidMaterialPoint>();\n\t\t//        const FEElasticMaterialPoint* ept = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\tdouble cp = pfluid->GetElastic()->IsobaricSpecificHeatCapacity(mp_noconst);\n\t\tdouble K = pfluid->GetConduct()->ThermalConductivity(mp_noconst);\n\t\tdouble rho = pfluid->Density(mp_noconst);\n\t\tvec3d v(0, 0, 0);\n\t\t//        if (ept) v = ept->m_v;\n\t\treturn (fpt->m_vft - v).Length() * rho * cp / K;\n\t\t});\n\n\treturn true;\n}\n\nbool FEPlotFluidThermalConductivity::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEFluidThermalConductivity* pfluid = dom.GetMaterial()->ExtractProperty<FEFluidThermalConductivity>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->ThermalConductivity(mp_noconst);\n\t\t});\n\n\treturn true;\n}\n\nbool FEPlotFluidIsochoricSpecificHeatCapacity::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->IsochoricSpecificHeatCapacity(mp_noconst);\n\t\t});\n\n\treturn true;\n}\n\nbool FEPlotFluidIsobaricSpecificHeatCapacity::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticFluid* pfluid = dom.GetMaterial()->ExtractProperty<FEElasticFluid>();\n\tif (pfluid == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\treturn pfluid->IsobaricSpecificHeatCapacity(mp_noconst);\n\t\t});\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioFluid/FEBioThermoFluidPlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPlotData.h>\n\n//! Element fluid temperature\nclass FEPlotFluidTemperature : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidTemperature(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_RELATIVE_TEMPERATURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! Nodal fluid temperature\nclass FEPlotNodalFluidTemperature : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodalFluidTemperature(FEModel* pfem) : FEPlotNodeData(pfem, PLT_FLOAT, FMT_NODE) { SetUnits(UNIT_RELATIVE_TEMPERATURE); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n};\n\n//! Fluid pressure tangent temperature\nclass FEPlotFluidPressureTangentTemperature : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidPressureTangentTemperature(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENTROPY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! Element fluid heat flux\nclass FEPlotFluidHeatFlux : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidHeatFlux(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_ENERGY_FLUX); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! Element relative thermal Peclet number\nclass FEPlotFluidRelativeThermalPecletNumber : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidRelativeThermalPecletNumber(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_RECIPROCAL_LENGTH); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! Thermal conductivity\nclass FEPlotFluidThermalConductivity : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidThermalConductivity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_THERMAL_CONDUCTIVITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! Specific isochoric heat capacity\nclass FEPlotFluidIsochoricSpecificHeatCapacity : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidIsochoricSpecificHeatCapacity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENTROPY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! Specific isobaric heat capacity\nclass FEPlotFluidIsobaricSpecificHeatCapacity : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidIsobaricSpecificHeatCapacity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENTROPY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSI.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEFluidFSI.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEBiphasicFSI, FEFluidFSI)\n\nADD_PARAMETER(m_phi0 , FE_RANGE_CLOSED(0.0, 1.0), \"phi0\");\n\n// material properties\nADD_PROPERTY(m_pPerm, \"permeability\");\nADD_PROPERTY(m_pSupp, \"solvent_supply\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFSIMaterialPoint\n//============================================================================\nFEBiphasicFSIMaterialPoint::FEBiphasicFSIMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEBiphasicFSIMaterialPoint::Copy()\n{\n    FEBiphasicFSIMaterialPoint* pt = new FEBiphasicFSIMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_phi0 & m_gradJ & m_Lw & m_ss;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIMaterialPoint::Init()\n{\n    m_gradJ = vec3d(0,0,0);\n    m_phi0 = 0;\n    m_Lw = mat3d(0.0);\n    m_ss.zero();\n    \n\tFEMaterialPointData::Init();\n}\n\n//============================================================================\n// FEFluidFSI\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluidFSI constructor\n\nFEBiphasicFSI::FEBiphasicFSI(FEModel* pfem) : FEFluidFSI(pfem)\n{\n    m_rhoTw = 0;\n    m_phi0 = 0;\n    \n    m_pPerm = nullptr;\n    m_pSupp = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEBiphasicFSI::CreateMaterialPointData()\n{\n    FEFluidMaterialPoint* fpt = new FEFluidMaterialPoint(m_pSolid->CreateMaterialPointData());\n    FEFSIMaterialPoint* fst = new FEFSIMaterialPoint(fpt);\n    FEBiphasicFSIMaterialPoint* bfpt = new FEBiphasicFSIMaterialPoint(fst);\n    \n    return bfpt;\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FEBiphasicFSI::Init()\n{\n    m_rhoTw = m_pFluid->m_rhor;\n    \n    m_pPerm->Init();\n    if (m_pSupp) m_pSupp->Init();\n    \n    return FEMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Porosity in current configuration\ndouble FEBiphasicFSI::Porosity(FEMaterialPoint& pt)\n{\n    double phiw = 1 - SolidVolumeFrac(pt);\n    // check for pore collapse\n    // TODO: throw an error if pores collapse\n    // phiw cant be 0\n    phiw = (phiw > 0) ? phiw : 0;\n    \n    return phiw;\n}\n\n//-----------------------------------------------------------------------------\n//! Solid Volume Frac (1 - porosity) in current configuration\ndouble FEBiphasicFSI::SolidVolumeFrac(FEMaterialPoint& pt)\n{\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    FEBiphasicFSIMaterialPoint& bt = *pt.ExtractData<FEBiphasicFSIMaterialPoint>();\n    \n    // relative volume\n    double J = et.m_J;\n    double phis = bt.m_phi0/J;\n    //double phis = m_phi0(pt)/J;\n    // check if phis is negative\n    // TODO: throw an error if pores collapse\n    phis = (phis >= 0) ? phis : 0;\n    \n    return phis;\n}\n\n//-----------------------------------------------------------------------------\n//! porosity gradient\nvec3d FEBiphasicFSI::gradPorosity(FEMaterialPoint& pt)\n{\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    FEBiphasicFSIMaterialPoint& bt = *pt.ExtractData<FEBiphasicFSIMaterialPoint>();\n\n    double J = et.m_J;\n    double phis = SolidVolumeFrac(pt);\n\n    return bt.m_gradJ*(phis/J);\n}\n\n//-----------------------------------------------------------------------------\n//! porosity gradient\nvec3d FEBiphasicFSI::gradPhifPhis(FEMaterialPoint& pt)\n{\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    FEBiphasicFSIMaterialPoint& bt = *pt.ExtractData<FEBiphasicFSIMaterialPoint>();\n    \n    double phisr = SolidReferentialVolumeFraction(pt);\n    \n    vec3d gradphifphis = vec3d(0.0);\n    \n    if (phisr != 0)\n        gradphifphis = bt.m_gradJ/phisr;\n    \n    return gradphifphis;\n}\n\n//-----------------------------------------------------------------------------\n//! Solid referential apparent density\ndouble FEBiphasicFSI::SolidReferentialApparentDensity(FEMaterialPoint& pt)\n{\n    FEBiphasicFSIMaterialPoint& pet = *pt.ExtractData<FEBiphasicFSIMaterialPoint>();\n    \n    // evaluate referential apparent density of base solid\n    double density = TrueSolidDensity(pt);\n    double rhosr = pet.m_phi0*density;\n    \n    return rhosr;\n}\n\n//-----------------------------------------------------------------------------\n//! Solid referential volume fraction\ndouble FEBiphasicFSI::SolidReferentialVolumeFraction(FEMaterialPoint& pt)\n{\n    // get referential apparent density of base solid (assumed constant)\n    double phisr = m_phi0(pt);\n    \n    return phisr;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a poro-elastic material is the sum of the fluid stress\n//! and the elastic stress. Note that this function is declared in the base class\n//! so you do not have to reimplement it in a derived class, unless additional\n//! pressure terms are required.\n\nmat3ds FEBiphasicFSI::Stress(FEMaterialPoint& mp)\n{\n    // calculate solid material stress\n    mat3ds s = m_pSolid->Stress(mp);\n    \n    // add fluid stress\n    s = s + m_pFluid->Stress(mp);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the permeability tensor as a double array\n\nvoid FEBiphasicFSI::Permeability(double k[3][3], FEMaterialPoint& pt)\n\n{\n    mat3ds kt = m_pPerm->Permeability(pt);\n    \n    k[0][0] = kt.xx();\n    k[1][1] = kt.yy();\n    k[2][2] = kt.zz();\n    k[0][1] = k[1][0] = kt.xy();\n    k[1][2] = k[2][1] = kt.yz();\n    k[2][0] = k[0][2] = kt.xz();\n    \n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEBiphasicFSI::Permeability(FEMaterialPoint& mp)\n{\n    return m_pPerm->Permeability(mp);\n}\n\n//-----------------------------------------------------------------------------\ntens4dmm FEBiphasicFSI::Permeability_Tangent(FEMaterialPoint& mp)\n{\n    //Return 0 if permeability is 0 to avoid NAN\n    if (m_pPerm->Permeability(mp).xx() == 0.0 && m_pPerm->Permeability(mp).xy() == 0.0 && m_pPerm->Permeability(mp).xz() == 0.0 && m_pPerm->Permeability(mp).yy() == 0.0 && m_pPerm->Permeability(mp).yz() == 0.0 && m_pPerm->Permeability(mp).zz() == 0.0)\n        return tens4dmm(0.0);\n    else\n        return m_pPerm->Tangent_Permeability_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEBiphasicFSI::InvPermeability(FEMaterialPoint& mp)\n{\n    //Return 0 for inverse permeability when permeability is set to 0.\n    //Acts as if permeability if infinite.\n    if (m_pPerm->Permeability(mp).xx() == 0.0 && m_pPerm->Permeability(mp).xy() == 0.0 && m_pPerm->Permeability(mp).xz() == 0.0 && m_pPerm->Permeability(mp).yy() == 0.0 && m_pPerm->Permeability(mp).yz() == 0.0 && m_pPerm->Permeability(mp).zz() == 0.0)\n        return mat3ds(0.0);\n    else\n        return m_pPerm->Permeability(mp).inverse();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEBiphasicFSI::FluidDensity(FEMaterialPoint& mp)\n{\n    double rhoTf = TrueFluidDensity(mp);\n    double phif = Porosity(mp);\n    \n    return rhoTf*phif;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEBiphasicFSI::SolidDensity(FEMaterialPoint& mp)\n{\n    double rhoTs = TrueSolidDensity(mp);\n    double phis = SolidVolumeFrac(mp);\n    \n    return rhoTs*phis;\n}\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSI.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include \"FEFluidFSI.h\"\n#include \"FEFluidSupply.h\"\n#include <FEBioMix/FEHydraulicPermeability.h>\n#include <FEBioMix/FEBiphasic.h>\n#include <FEBioMech/FEBodyForce.h>\n\n//-----------------------------------------------------------------------------\n//! FSI material point class.\n//\nclass FEBIOFLUID_API FEBiphasicFSIMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n    FEBiphasicFSIMaterialPoint(FEMaterialPointData* pt);\n    \n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n    \n    //! data serialization\n    void Serialize(DumpStream& ar);\n    \n    //! Data initialization\n    void Init();\n    \npublic:\n    // Biphasic FSI material data\n    mat3d       m_Lw;       //!< grad of m_wt\n    vec3d       m_gradJ;    //!< gradient of J\n    double      m_phi0;     //!< solid volume fraction in reference configuration\n    mat3ds      m_ss;       //!< solid stress\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for FluidFSI materials.\n\nclass FEBIOFLUID_API FEBiphasicFSI : public FEFluidFSI, public FEBiphasicInterface\n{\npublic:\n    FEBiphasicFSI(FEModel* pfem);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! performs initialization\n    bool Init() override;\n    \npublic:\n    //! calculate inner stress at material point\n    mat3ds Stress(FEMaterialPoint& pt);\n    \n    //! return the permeability tensor as a matrix\n    void Permeability(double k[3][3], FEMaterialPoint& pt);\n    \n    //! return the permeability as a tensor\n    mat3ds Permeability(FEMaterialPoint& pt);\n    \n    //! return the inverse permeability as a tensor\n    mat3ds InvPermeability(FEMaterialPoint& pt);\n    \n    //! return the tangent permeability tensor\n    tens4dmm Permeability_Tangent(FEMaterialPoint& pt);\n    \n    //! return the permeability property\n    FEHydraulicPermeability* GetPermeability() { return m_pPerm; }\n    \n    //! porosity\n    double Porosity(FEMaterialPoint& pt);\n    \n    //! Solid Volume\n    double SolidVolumeFrac(FEMaterialPoint& pt);\n    \n    //! porosity gradient\n    vec3d gradPorosity(FEMaterialPoint& pt);\n    \n    //! porosity gradient\n    vec3d gradPhifPhis(FEMaterialPoint& pt);\n    \n    //! solid density\n    double TrueSolidDensity(FEMaterialPoint& mp) { return Solid()->Density(mp); }\n    \n    //! true fluid density\n    double TrueFluidDensity(FEMaterialPoint& mp) { return Fluid()->Density(mp); }\n    \n    //! solid density\n    double SolidDensity(FEMaterialPoint& mp);\n    \n    //! fluid density\n    double FluidDensity(FEMaterialPoint& mp);\n    \npublic: // overridden from FEBiphasicInterface\n\n    double GetReferentialSolidVolumeFraction(const FEMaterialPoint& mp) override {\n        const FEBiphasicFSIMaterialPoint* pt = (mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        return pt->m_phi0;\n    }\n\n    //! solid referential apparent density\n    double SolidReferentialApparentDensity(FEMaterialPoint& pt) override;\n\n    //! solid referential volume fraction\n    double SolidReferentialVolumeFraction(FEMaterialPoint& pt) override;\n\n    FEFluidSupply* FluidSupply() { return m_pSupp; }\n    \npublic: // material parameters\n    double      m_rhoTw; //!< true fluid density\n    FEParamDouble      m_phi0;  //!< solid volume fraction in reference configuration\n\n    vector<FEBodyForce*>    m_bf;       //!< body forces acting on this biphasic material\n    \nprotected: // material properties\n    FEHydraulicPermeability*    m_pPerm;    //!< pointer to permeability material\n    FEFluidSupply*              m_pSupp;    //!< pointer to (optional) fluid supply material\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSIDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEBiphasicFSIDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEBiphasicFSIDomain::FEBiphasicFSIDomain(FEModel* pfem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSIDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include \"febiofluid_api.h\"\n#include <vector>\n\nclass FEModel;\nclass FELinearSystem;\nclass FEBodyForce;\nclass FEGlobalVector;\nclass FETimeInfo;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for fluid-FSI domains.\n\n//! A fluid-FSI domain is used by the fluid-FSI solver.\n//! This interface defines the functions that have to be implemented by a\n//! fluid-FSI domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOFLUID_API FEBiphasicFSIDomain\n{\npublic:\n    FEBiphasicFSIDomain(FEModel* pfem);\n    virtual ~FEBiphasicFSIDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! calculate the internal forces\n    virtual void InternalForces(FEGlobalVector& R) = 0;\n    \n    //! Calculate the body force vector\n    virtual void BodyForce(FEGlobalVector& R, FEBodyForce& bf) = 0;\n    \n    //! calculate the interial forces (for dynamic problems)\n    virtual void InertialForces(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! Calculate global stiffness matrix (only contribution from internal force derivative)\n    //! \\todo maybe I should rename this the InternalStiffness matrix?\n    virtual void StiffnessMatrix   (FELinearSystem& LS) = 0;\n    \n    //! Calculate stiffness contribution of body forces\n    virtual void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) = 0;\n    \n    //! calculate the mass matrix (for dynamic problems)\n    virtual void MassMatrix(FELinearSystem& LS) = 0;\n    \n    //! transient analysis\n    void SetTransientAnalysis() { m_btrans = true; }\n    void SetSteadyStateAnalysis() { m_btrans = false; }\n    \nprotected:\n    bool        m_btrans;   // flag for transient (true) or steady-state (false) analysis\n};\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSIDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEBiphasicFSIDomain3D.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include \"FEBioFSI.h\"\n#include \"FEFluidFSI.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEBiphasicFSIDomain3D::FEBiphasicFSIDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEBiphasicFSIDomain(pfem), m_dofU(pfem), m_dofV(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n    m_sseps = 0;\n    \n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::DISPLACEMENT));\n        m_dofV.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::VELOCITY));\n        m_dofW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_ACCELERATION));\n        m_dofSU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_DISPLACEMENT));\n        m_dofR.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RIGID_ROTATION));\n        m_dofEF = pfem->GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION), 0);\n        m_dofAEF = pfem->GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION_TDERIV), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEBiphasicFSIDomain3D& FEBiphasicFSIDomain3D::operator = (FEBiphasicFSIDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// get the total dof\nconst FEDofList& FEBiphasicFSIDomain3D::GetDOFList() const\n{\n    return m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEBiphasicFSIDomain3D::SetMaterial(FEMaterial* pmat)\n{\n    FEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEBiphasicFSI*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::Activate()\n{\n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n            }\n            node.set_active(m_dofW[0]);\n            node.set_active(m_dofW[1]);\n            node.set_active(m_dofW[2]);\n            node.set_active(m_dofEF);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::Reset()\n{\n    // reset base class data\n    FESolidDomain::Reset();\n    \n    // initialize all element data\n    ForEachMaterialPoint([=](FEMaterialPoint& mp) {\n        FEBiphasicFSIMaterialPoint& pt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        \n        // initialize referential solid volume fraction\n        pt.m_phi0 = m_pMat->m_phi0(mp);\n    });\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEBiphasicFSIDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        if (el.isActive())\n        {\n            int neln = el.Nodes();\n            for (int i=0; i<neln; ++i)\n            {\n                x0[i] = m.Node(el.m_node[i]).m_r0;\n                xt[i] = m.Node(el.m_node[i]).m_rt;\n            }\n            \n            int n = el.GaussPoints();\n            for (int j=0; j<n; ++j)\n            {\n                r0 = el.Evaluate(x0, j);\n                rt = el.Evaluate(xt, j);\n                \n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n                et.m_Wp = et.m_Wt;\n                \n                if ((pt.m_ef <= -1) || (et.m_J <= 0)) {\n                    throw NegativeJacobianDetected();\n                }\n                \n                mp.Update(timeInfo);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEBiphasicFSIDomain3D::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    lm.resize(N*10);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& node = m_pMesh->Node(el.m_node[i]);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[7*i  ] = id[m_dofU[0]];\n        lm[7*i+1] = id[m_dofU[1]];\n        lm[7*i+2] = id[m_dofU[2]];\n        lm[7*i+3] = id[m_dofW[0]];\n        lm[7*i+4] = id[m_dofW[1]];\n        lm[7*i+5] = id[m_dofW[2]];\n        lm[7*i+6] = id[m_dofEF];\n        \n        // rigid rotational dofs\n        lm[7*N + 3*i  ] = id[m_dofR[0]];\n        lm[7*N + 3*i+1] = id[m_dofR[1]];\n        lm[7*N + 3*i+2] = id[m_dofR[2]];\n    }\n    \n    // substitute interface dofs for solid-shell interfaces\n    FESolidElement& sel = static_cast<FESolidElement&>(el);\n    for (int i = 0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(el.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the displacement dofs\n            lm[7*i  ] = id[m_dofSU[0]];\n            lm[7*i+1] = id[m_dofSU[1]];\n            lm[7*i+2] = id[m_dofSU[2]];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            // get the element force vector and initialize it to zero\n            int ndof = 7*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // calculate internal force vector\n            ElementInternalForce(el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEBiphasicFSIDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    mat3ds sv, se, pi;\n    vec3d gradp, divTe;\n    mat3ds km1;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFSIMaterialPoint& ft = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        \n        // calculate the jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        se = m_pMat->Solid()->Stress(mp);\n        // get the gradient of the elastic pressure\n        gradp = pt.m_gradef*m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        // get inverse of permeability tensor\n        km1 = m_pMat->InvPermeability(mp);\n        //get pI\n        pi = mat3dd(m_pMat->Fluid()->Pressure(mp));\n        // get fluid supply (if present)\n        double phifhat = 0;\n        if (m_pMat->FluidSupply()) phifhat = m_pMat->FluidSupply()->Supply(mp);\n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jfdot wrt fluid\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        double dJfdotf = pt.m_efdot + pt.m_gradef*ft.m_w/phif;\n        double Jf = 1 + pt.m_ef;\n        // Jsdot/Js\n        double dJsoJ = ft.m_Jdot/et.m_J;\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fs = ((se-sv*phis)*gradN[i] + (sv*bt.m_gradJ/(phif*et.m_J)*phis - km1*ft.m_w)*H[i])*detJ;\n            vec3d ff = (sv*gradN[i] + (gradp + km1*ft.m_w - sv*bt.m_gradJ*phis/(phif*et.m_J))*H[i])*detJ;\n            double fJ = (H[i]*(dJfdotf*phif/Jf - dJsoJ + phifhat) + gradN[i]*ft.m_w)*detJ;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[7*i  ] -= fs.x;\n            fe[7*i+1] -= fs.y;\n            fe[7*i+2] -= fs.z;\n            fe[7*i+3] -= ff.x;\n            fe[7*i+4] -= ff.y;\n            fe[7*i+5] -= ff.z;\n            fe[7*i+6] -= fJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    for (int i=0; i<NE; ++i)\n    {\n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            vector<double> fe;\n            vector<int> lm;\n            \n            // get the element force vector and initialize it to zero\n            int ndof = 7*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // apply body forces\n            ElementBodyForce(BF, el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEBiphasicFSIDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d ff, f;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        double densTs = m_pMat->TrueSolidDensity(mp);\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        \n        detJ = detJt(el, n, tp.alphaf)*gw[n];\n        \n        // get the force\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        f = BF.force(mp)*((-densTf+densTs)*phis*detJ);\n        ff = BF.force(mp)*(densTf*detJ);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[7*i+0] -= H[i]*f.x;\n            fe[7*i+1] -= H[i]*f.y;\n            fe[7*i+2] -= H[i]*f.z;\n            fe[7*i+3] -= H[i]*ff.x;\n            fe[7*i+4] -= H[i]*ff.y;\n            fe[7*i+5] -= H[i]*ff.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\n//! For now, we assume that the body force is constant\nvoid FEBiphasicFSIDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    int ndof = ke.columns()/neln;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    double *H, *Gr, *Gs, *Gt;\n    double* gw = el.GaussWeights();\n    vec3d f, ff, fs, ffs, kwJ, kuJ;\n    mat3d Kwu, Kuu;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double Jf = 1 + pt.m_ef;\n        \n        // calculate the jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        double densf = m_pMat->FluidDensity(mp);\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        double denss = m_pMat->SolidDensity(mp);\n        double densTs = m_pMat->TrueSolidDensity(mp);\n        \n        // get the force\n        ff = BF.force(mp)*(densTf*detJ);\n        f = BF.force(mp)*(densf*detJ);\n        fs = BF.force(mp)*(densTs*detJ);\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        ffs = BF.force(mp)*((phis/phif*(densf+denss) - densTf*phis + denss)*detJ);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (int i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                kwJ = ff*(-H[i]*H[j]/Jf);\n                kuJ = ff*(H[i]*H[j]*phis/Jf);\n                Kwu = (ff & gradN[j])*H[i];\n                Kuu = mat3d(0.0);\n                \n                ke[ndof*i+0][ndof*j  ] += Kuu(0,0); ke[ndof*i+0][ndof*j+1] += Kuu(0,1); ke[ndof*i+0][ndof*j+2] += Kuu(0,2);\n                ke[ndof*i+1][ndof*j  ] += Kuu(1,0); ke[ndof*i+1][ndof*j+1] += Kuu(1,1); ke[ndof*i+1][ndof*j+2] += Kuu(1,2);\n                ke[ndof*i+2][ndof*j  ] += Kuu(2,0); ke[ndof*i+2][ndof*j+1] += Kuu(2,1); ke[ndof*i+2][ndof*j+2] += Kuu(2,2);\n                ke[ndof*i+3][ndof*j  ] += Kwu(0,0); ke[ndof*i+3][ndof*j+1] += Kwu(0,1); ke[ndof*i+3][ndof*j+2] += Kwu(0,2);\n                ke[ndof*i+4][ndof*j  ] += Kwu(1,0); ke[ndof*i+4][ndof*j+1] += Kwu(1,1); ke[ndof*i+4][ndof*j+2] += Kwu(1,2);\n                ke[ndof*i+5][ndof*j  ] += Kwu(2,0); ke[ndof*i+5][ndof*j+1] += Kwu(2,1); ke[ndof*i+5][ndof*j+2] += Kwu(2,2);\n                ke[ndof*i+0][ndof*j+6] += kuJ.x;\n                ke[ndof*i+1][ndof*j+6] += kuJ.y;\n                ke[ndof*i+2][ndof*j+6] += kuJ.z;\n                ke[ndof*i+3][ndof*j+6] += kwJ.x;\n                ke[ndof*i+4][ndof*j+6] += kwJ.y;\n                ke[ndof*i+5][ndof*j+6] += kwJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEBiphasicFSIDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    vector<mat3d> gradgradN(neln);\n    \n    double dt = tp.timeIncrement;\n    double a = tp.gamma/(tp.beta*dt);\n    double c = tp.alpham/(tp.alphaf*tp.gamma*dt);\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    double *H, *Gr, *Gs, *Gt;\n    vec3d g[3], dg[3][3];\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        ContraBaseVectors(el, n, g, tp.alphaf);\n        ContraBaseVectorDerivatives(el, n, dg, tp.alphaf);\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // get the shape function derivatives\n        double* Grr = el.Grr(n); double* Grs = el.Grs(n); double* Grt = el.Grt(n);\n        double* Gsr = el.Gsr(n); double* Gss = el.Gss(n); double* Gst = el.Gst(n);\n        double* Gtr = el.Gtr(n); double* Gts = el.Gts(n); double* Gtt = el.Gtt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFSIMaterialPoint& fpt = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n\n        // get the tangents\n        mat3ds se = m_pMat->Solid()->Stress(mp);\n        tens4ds cs = m_pMat->Solid()->Tangent(mp);\n        mat3ds sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        mat3ds svJ = m_pMat->Fluid()->GetViscous()->Tangent_Strain(mp);\n        tens4ds cv = m_pMat->Fluid()->Tangent_RateOfDeformation(mp);\n        double dp = m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        double d2p = m_pMat->Fluid()->Tangent_Pressure_Strain_Strain(mp);\n        vec3d gradp = pt.m_gradef*dp;\n        // Jsdot/Js = div(vs)\n        double dJsoJ = fpt.m_Jdot/et.m_J;\n        mat3ds km1 = m_pMat->InvPermeability(mp);\n        \n        //Include dependence of permeability on displacement\n        tens4dmm K = m_pMat->Permeability_Tangent(mp);\n        \n        // include contributions from fluid supply (if present)\n        double phifhat = 0;\n        mat3d dphifhatdE(mat3dd(0));\n        double dphifhatdef = 0;\n        mat3ds dphifhatdD(0);\n        if (m_pMat->FluidSupply()) {\n            phifhat = m_pMat->FluidSupply()->Supply(mp);\n            dphifhatdE = m_pMat->FluidSupply()->Tangent_Supply_Strain(mp);\n            dphifhatdef = m_pMat->FluidSupply()->Tangent_Supply_Dilatation(mp);\n            dphifhatdD = m_pMat->FluidSupply()->Tangent_Supply_RateOfDeformation(mp);\n        }\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate spatial gradgrad of shape functions\n        for (i=0; i<neln; ++i) {\n            gradgradN[i] = (((dg[0][0] & g[0]) + (dg[0][1] & g[1]) + (dg[0][2] & g[2]))*Gr[i]\n                            + ((dg[1][0] & g[0]) + (dg[1][1] & g[1]) + (dg[1][2] & g[2]))*Gs[i]\n                            + ((dg[2][0] & g[0]) + (dg[2][1] & g[1]) + (dg[2][2] & g[2]))*Gt[i]\n                            + (g[0] & g[0])*Grr[i] + (g[0] & g[1])*Gsr[i] + (g[0] & g[2])*Gtr[i]\n                            + (g[1] & g[0])*Grs[i] + (g[1] & g[1])*Gss[i] + (g[1] & g[2] )*Gts[i]\n                            + (g[2] & g[0])*Grt[i] + (g[2] & g[1])*Gst[i] + (g[2] & g[2])*Gtt[i]);\n        }\n\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n\n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += 7)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += 7)\n            {\n                mat3d M = mat3dd(a*dtrans) - et.m_L;\n                tens4d km1km1 = dyad2(km1,km1);\n                tens4d Kfull = tens4d(K);\n                \n                mat3d Kuu = (sv*((gradN[j]&gradN[i])*phis) - (-((sv*gradN[i])&gradN[j])*phis/phif + vdotTdotv(gradN[i], cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M))*phis - vdotTdotv(gradN[i], cv, fpt.m_w*phis/(phif*phif)) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif)) + vdotTdotv(gradN[i], cv, fpt.m_w*phis*phis/(phif*phif)) * ((-bpt.m_gradJ&gradN[j])/et.m_J + gradgradN[j]) + mat3dd((se*gradN[i])*gradN[j]) + vdotTdotv(gradN[i], cs, gradN[j]) - (((sv*bpt.m_gradJ)*phis/(et.m_J*phif*phif))&gradN[j])*H[i] + (-((sv*bpt.m_gradJ)&gradN[j])*phis/phif + vdotTdotv(bpt.m_gradJ, cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M))*phis/(phif*et.m_J)*H[i] + vdotTdotv(bpt.m_gradJ, cv, fpt.m_w*phis/(phif*phif*phif*et.m_J)*H[i]) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif)) - vdotTdotv(bpt.m_gradJ, cv, fpt.m_w*phis*phis/(phif*phif*phif*et.m_J)*H[i]) * ((-bpt.m_gradJ&gradN[j])/et.m_J + gradgradN[j]) + sv*((bpt.m_gradJ&gradN[j]) - (gradN[j]&bpt.m_gradJ) + gradgradN[j]*et.m_J)*H[i]*phis/(phif*et.m_J) + (-((km1*fpt.m_w)&gradN[j])*2.0 + (gradN[j]&(km1*fpt.m_w)) + km1*(gradN[j]*fpt.m_w) + ddot(ddot(km1km1,Kfull),mat3dd(gradN[j]*fpt.m_w)))*H[i])*detJ; //mixture 3 adjusted viscous stress\n                \n                mat3d Kuw = (vdotTdotv(gradN[i], cv, (gradphif*H[j]/phif-gradN[j]))*phis/phif + vdotTdotv((-gradphif*H[j]/phif + gradN[j]), cv, bpt.m_gradJ)*H[i]*phis/(phif*phif*et.m_J) - km1*H[i]*H[j])*detJ; //mixture 3 adjusted stress\n                \n                vec3d kuJ = ((-svJ*gradN[i])*H[j]*phis + svJ*bpt.m_gradJ*H[j]*H[i]*phis/(phif*et.m_J))*detJ; //mixture 3 adjusted stress\n                \n                mat3d Kwu = (((gradp&gradN[j])-(gradN[j]&gradp))*H[i] + sv*((bpt.m_gradJ&gradN[j])*(phis/phif)+(gradN[j]&bpt.m_gradJ) - gradgradN[j]*et.m_J)*(phis*H[i]/(et.m_J*phif)) - (-((sv*bpt.m_gradJ)&gradN[j])*phis/phif + vdotTdotv(bpt.m_gradJ, cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M))*H[i]*phis/(phif*et.m_J) - vdotTdotv(bpt.m_gradJ*H[i]*phis/(et.m_J*phif*phif*phif), cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif)) + vdotTdotv(bpt.m_gradJ*H[i]*phis*phis/(et.m_J*phif*phif*phif), cv, fpt.m_w) * (-(bpt.m_gradJ&gradN[j])/et.m_J + gradgradN[j]) + sv*((gradN[i]&gradN[j])-(gradN[j]&gradN[i])) + (-((sv*gradN[i])&gradN[j])*phis/phif + vdotTdotv(gradN[i], cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M)) + vdotTdotv(gradN[i], cv, fpt.m_w/(phif*phif)) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif)) + vdotTdotv(gradN[i], cv, fpt.m_w*phis/(phif*phif)) * ((bpt.m_gradJ&gradN[j])/et.m_J - gradgradN[j]) + (((km1*fpt.m_w)&gradN[j])*2.0 - (gradN[j]&(km1*fpt.m_w)) - km1*(gradN[j]*fpt.m_w) - ddot(ddot(km1km1,Kfull),mat3dd(gradN[j]*fpt.m_w)))*H[i])*detJ; //fluid adjusted visc stress\n                \n                mat3d Kww = ((vdotTdotv(bpt.m_gradJ, cv, (gradphif*H[j]/phif-gradN[j]))*phis/(phif*phif*et.m_J) + km1*H[j])*H[i] + vdotTdotv(gradN[i], cv, (-gradphif*H[j]/phif+gradN[j]))/phif)*detJ; //fluid adjusted visc stress\n                \n                vec3d kwJ = ((svJ*gradN[i])*H[j] +(gradN[j]*dp+(pt.m_gradef*d2p - svJ*bpt.m_gradJ*phis/(phif*et.m_J))*H[j])*H[i])*detJ; //fluid adjusted visc stress\n                vec3d kJu = (((gradN[j]&fpt.m_w) - mat3dd(gradN[j]*fpt.m_w)) * gradN[i] + ((gradN[j]*pt.m_efdot + ((gradN[j]&fpt.m_w) - mat3dd(gradN[j]*fpt.m_w))*pt.m_gradef)/Jf - gradN[j]*(dJsoJ + a*dtrans) + et.m_L.transpose()*gradN[j]*dtrans\n                                                                                           + (dphifhatdE+mat3dd(phifhat))*gradN[j])*H[i])*detJ;\n                vec3d kJw = ((pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j] + dphifhatdD*(gradN[j]-gradphif*(H[j]/phif))*(H[i]/phif))*detJ;\n                double kJJ = (((c*phif*dtrans - (pt.m_efdot*phif + pt.m_gradef*fpt.m_w)/Jf)*H[j] + gradN[j]*fpt.m_w)*H[i]/Jf\n                              +H[i]*H[j]*dphifhatdef)*detJ;\n\n                ke[i7  ][j7  ] += Kuu(0,0); ke[i7  ][j7+1] += Kuu(0,1); ke[i7  ][j7+2] += Kuu(0,2);\n                ke[i7+1][j7  ] += Kuu(1,0); ke[i7+1][j7+1] += Kuu(1,1); ke[i7+1][j7+2] += Kuu(1,2);\n                ke[i7+2][j7  ] += Kuu(2,0); ke[i7+2][j7+1] += Kuu(2,1); ke[i7+2][j7+2] += Kuu(2,2);\n                \n                ke[i7  ][j7+3] += Kuw(0,0); ke[i7  ][j7+4] += Kuw(0,1); ke[i7  ][j7+5] += Kuw(0,2);\n                ke[i7+1][j7+3] += Kuw(1,0); ke[i7+1][j7+4] += Kuw(1,1); ke[i7+1][j7+5] += Kuw(1,2);\n                ke[i7+2][j7+3] += Kuw(2,0); ke[i7+2][j7+4] += Kuw(2,1); ke[i7+2][j7+5] += Kuw(2,2);\n                \n                ke[i7+3][j7  ] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7  ] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7  ] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                \n                ke[i7+3][j7+3] += Kww(0,0); ke[i7+3][j7+4] += Kww(0,1); ke[i7+3][j7+5] += Kww(0,2);\n                ke[i7+4][j7+3] += Kww(1,0); ke[i7+4][j7+4] += Kww(1,1); ke[i7+4][j7+5] += Kww(1,2);\n                ke[i7+5][j7+3] += Kww(2,0); ke[i7+5][j7+4] += Kww(2,1); ke[i7+5][j7+5] += Kww(2,2);\n                \n                ke[i7+0][j7+6] += kuJ.x;\n                ke[i7+1][j7+6] += kuJ.y;\n                ke[i7+2][j7+6] += kuJ.z;\n                \n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n                \n                ke[i7+6][j7  ] += kJu.x;\n                ke[i7+6][j7+1] += kJu.y;\n                ke[i7+6][j7+2] += kJu.z;\n                \n                ke[i7+6][j7+3] += kJw.x;\n                ke[i7+6][j7+4] += kJw.y;\n                ke[i7+6][j7+5] += kJw.z;\n                \n                ke[i7+6][j7+6] += kJJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            // element stiffness matrix\n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = 7*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate material stiffness\n            ElementStiffness(el, ke);\n            \n            // get the element's LM vector\n            vector<int> lm;\n            UnpackLM(el, lm);\n            ke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            \n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = 7*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate inertial stiffness\n            ElementMassMatrix(el, ke);\n            \n            // get the element's LM vector\n            vector<int> lm;\n            UnpackLM(el, lm);\n            ke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    FEBiphasicFSI* pme = dynamic_cast<FEBiphasicFSI*>(GetMaterial()); assert(pme);\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            \n            // element stiffness matrix\n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = 7*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate inertial stiffness\n            ElementBodyForceStiffness(bf, el, ke);\n            \n            // get the element's LM vector\n            vector<int> lm;\n            UnpackLM(el, lm);\n            ke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEBiphasicFSIDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    vector<mat3d> gradgradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    vec3d g[3], dg[3][3];\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    double dt = tp.timeIncrement;\n    double a = tp.gamma/(tp.beta*dt);\n    double b = tp.alpham/(tp.alphaf*tp.beta*dt*dt);\n    double c = tp.alpham/(tp.alphaf*tp.gamma*dt);\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        ContraBaseVectors(el, n, g, tp.alphaf);\n        ContraBaseVectorDerivatives(el, n, dg, tp.alphaf);\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // get the shape function derivatives\n        double* Grr = el.Grr(n); double* Grs = el.Grs(n); double* Grt = el.Grt(n);\n        double* Gsr = el.Gsr(n); double* Gss = el.Gss(n); double* Gst = el.Gst(n);\n        double* Gtr = el.Gtr(n); double* Gts = el.Gts(n); double* Gtt = el.Gtt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFSIMaterialPoint& fpt = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n\n        double denss = m_pMat->SolidDensity(mp);\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        \n        // Jsdot/Js = div(vs)\n        double dJsoJ = fpt.m_Jdot/et.m_J;\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate spatial gradgrad of shape functions\n        for (i=0; i<neln; ++i) {\n            gradgradN[i] = (((dg[0][0] & g[0]) + (dg[0][1] & g[1]) + (dg[0][2] & g[2]))*Gr[i]\n                            + ((dg[1][0] & g[0]) + (dg[1][1] & g[1]) + (dg[1][2] & g[2]))*Gs[i]\n                            + ((dg[2][0] & g[0]) + (dg[2][1] & g[1]) + (dg[2][2] & g[2]))*Gt[i]\n                            + (g[0] & g[0])*Grr[i] + (g[0] & g[1])*Gsr[i] + (g[0] & g[2])*Gtr[i]\n                            + (g[1] & g[0])*Grs[i] + (g[1] & g[1])*Gss[i] + (g[1] & g[2] )*Gts[i]\n                            + (g[2] & g[0])*Grt[i] + (g[2] & g[1])*Gst[i] + (g[2] & g[2])*Gtt[i]);\n        }\n\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n\n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += 7)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += 7)\n            {\n                \n                mat3d Kuu = ((mat3dd(b*H[j]*dtrans))*denss*H[i] + (((et.m_a*phis)&gradN[j]) + mat3dd(b*phif*H[j]*dtrans) - ((fpt.m_w&gradN[j]) * (-1.0/phif*dJsoJ + a*dtrans) - (fpt.m_w&(et.m_L.transpose()*gradN[j]*dtrans)))*phis/phif + mat3dd(gradN[j]*fpt.m_w*a*dtrans) - ((bpt.m_Lw*fpt.m_w)&gradN[j])*phis/(phif*phif) + ((fpt.m_w&gradN[j])*((gradphif*2.0/phif + bpt.m_gradJ/et.m_J)*fpt.m_w) - (fpt.m_w&(gradgradN[j].transpose()*fpt.m_w)))*phis/(phif*phif) - pt.m_Lf*(mat3dd(gradN[j]*fpt.m_w)) - (pt.m_aft&gradN[j])*phis)*(-H[i]*densTf*phis/phif))*detJ; //mixture 2\n                \n                mat3d Kuw = ((mat3dd(c*dtrans-phis/phif*dJsoJ-(gradphif*fpt.m_w)/(phif*phif))+pt.m_Lf)*H[j] + mat3dd(gradN[j]*fpt.m_w)/phif)*(-H[i]*densTf/phif*phis*detJ); //mixture 2\n                \n                vec3d kuJ = pt.m_aft*(densTf/Jf*H[i]*H[j]*phis*detJ); //mixture 2\n                mat3d Kwu = (((et.m_a&gradN[j])*phis + mat3dd(b*phif*H[j]*dtrans) - ((fpt.m_w&gradN[j]) * (-1.0/phif*dJsoJ + a*dtrans) - (fpt.m_w&(et.m_L.transpose()*gradN[j]*dtrans)))*phis/phif + mat3dd(gradN[j]*fpt.m_w*a*dtrans) - ((bpt.m_Lw*fpt.m_w)&gradN[j])*phis/(phif*phif) + ((fpt.m_w&gradN[j])*((gradphif*2.0/phif + bpt.m_gradJ/et.m_J)*fpt.m_w) - (fpt.m_w&(gradgradN[j].transpose()*fpt.m_w)))*phis/(phif*phif) - pt.m_Lf*mat3dd(gradN[j]*fpt.m_w))*(H[i]*densTf/phif) + (pt.m_aft&gradN[j])*H[i]*densTf*(1.0-phis/phif))*detJ;\n                mat3d Kww = ((mat3dd(c*dtrans-phis/phif*dJsoJ-(gradphif*fpt.m_w)/(phif*phif))+pt.m_Lf)*H[j] + mat3dd(gradN[j]*fpt.m_w)/phif)*(H[i]*densTf/phif*detJ);\n                vec3d kwJ = pt.m_aft*(-densTf/Jf*H[i]*H[j]*detJ);\n\n                ke[i7+0][j7  ] += Kuu(0,0); ke[i7+0][j7+1] += Kuu(0,1); ke[i7+0][j7+2] += Kuu(0,2);\n                ke[i7+1][j7  ] += Kuu(1,0); ke[i7+1][j7+1] += Kuu(1,1); ke[i7+1][j7+2] += Kuu(1,2);\n                ke[i7+2][j7  ] += Kuu(2,0); ke[i7+2][j7+1] += Kuu(2,1); ke[i7+2][j7+2] += Kuu(2,2);\n                \n                ke[i7+0][j7+3] += Kuw(0,0); ke[i7+0][j7+4] += Kuw(0,1); ke[i7+0][j7+5] += Kuw(0,2);\n                ke[i7+1][j7+3] += Kuw(1,0); ke[i7+1][j7+4] += Kuw(1,1); ke[i7+1][j7+5] += Kuw(1,2);\n                ke[i7+2][j7+3] += Kuw(2,0); ke[i7+2][j7+4] += Kuw(2,1); ke[i7+2][j7+5] += Kuw(2,2);\n                \n                ke[i7+3][j7  ] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7  ] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7  ] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                \n                ke[i7+3][j7+3] += Kww(0,0); ke[i7+3][j7+4] += Kww(0,1); ke[i7+3][j7+5] += Kww(0,2);\n                ke[i7+4][j7+3] += Kww(1,0); ke[i7+4][j7+4] += Kww(1,1); ke[i7+4][j7+5] += Kww(1,2);\n                ke[i7+5][j7+3] += Kww(2,0); ke[i7+5][j7+4] += Kww(2,1); ke[i7+5][j7+5] += Kww(2,2);\n                \n                ke[i7+0][j7+6] += kuJ.x;\n                ke[i7+1][j7+6] += kuJ.y;\n                ke[i7+2][j7+6] += kuJ.z;\n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            FESolidElement& el = Element(i);\n            if (el.isActive())\n            {\n                UpdateElementStress(i, tp);\n            }\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEBiphasicFSIDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d r0[NELN], r[NELN];\n    vec3d vs[NELN];\n    vec3d a[NELN];\n    vec3d w[NELN];\n    vec3d aw[NELN];\n    double e[NELN];\n    double ae[NELN];\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        r0[j] = node.m_r0;\n        r[j]  = node.m_rt*alphaf + node.m_rp*(1-alphaf);\n        vs[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*alphaf + node.m_vp*(1-alphaf);\n        w[j]  = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-alphaf);\n        e[j]  = node.get(m_dofEF)*alphaf + node.get_prev(m_dofEF)*(1-alphaf);\n        a[j]  = node.m_at*alpham + node.m_ap*(1-alpham);\n        aw[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2])*alpham + node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2])*(1-alpham);\n        ae[j] = node.get(m_dofAEF)*alpham + node.get_prev(m_dofAEF)*(1-alpham);\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFSIMaterialPoint& ft = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n\n        // elastic material point data\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(r, n);\n        mat3d Ft, Fp;\n        double Jt, Jp;\n        Jt = defgrad(el, Ft, n);\n        Jp = defgradp(el, Fp, n);\n        ept.m_F = Ft*alphaf + Fp*(1 - alphaf);\n        ept.m_J = ept.m_F.det();\n        mat3d Fi = ept.m_F.inverse();\n        ept.m_L = (Ft - Fp)*Fi*(dtrans / dt);\n        ept.m_v = m_btrans ? el.Evaluate(vs, n) : vec3d(0, 0, 0);\n        ept.m_a = m_btrans ? el.Evaluate(a, n) : vec3d(0, 0, 0);\n        //NOTE: Made these new function. Converts from GradJ to gradJ\n        vec3d GradJ = FESolidDomain::GradJ(el,n);\n        vec3d GradJp = FESolidDomain::GradJp(el, n);\n        \n        //calculate gradJ gradphif\n        bt.m_gradJ = Fi.transpose()*(GradJ*alphaf + GradJp*(1-alphaf));\n        \n        // FSI material point data\n        ft.m_w = el.Evaluate(w, n);\n        ft.m_Jdot = (Jt - Jp) / dt*dtrans;\n        ft.m_aw = el.Evaluate(aw, n)*dtrans;\n        \n        // fluid material point data\n        bt.m_phi0 = m_pMat->SolidReferentialVolumeFraction(mp);\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n        pt.m_efdot = el.Evaluate(ae, n)*dtrans;\n        pt.m_vft = ept.m_v + ft.m_w/phif;\n        mat3d Gradw = Gradient(el, w, n);\n        bt.m_Lw = Gradw*Fi;\n        pt.m_Lf = ept.m_L + bt.m_Lw/phif - (ft.m_w & gradphif)/(phif*phif);\n        pt.m_ef = el.Evaluate(e, n);\n        vec3d GradJf = Gradient(el, e, n);\n        pt.m_gradef = Fi.transpose()*GradJf;\n        \n        // fluid acceleration\n        pt.m_aft = (ft.m_aw + ept.m_a*phif - ft.m_w*ft.m_Jdot*phis/phif/ept.m_J + pt.m_Lf*ft.m_w)/phif;\n        \n        // calculate the fluid stress at this material point\n        pt.m_sf = m_pMat->Fluid()->Stress(mp);\n        \n        // calculate the solid stress at this material point\n        ft.m_ss = m_pMat->Solid()->Stress(mp) - m_pMat->Fluid()->GetViscous()->Stress(mp)*phis;\n\n        // calculate the mixture stress at this material point\n        ept.m_s = ft.m_ss + pt.m_sf;\n\n        // calculate the fluid pressure\n        pt.m_pf = m_pMat->Fluid()->Pressure(mp);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            // element force vector\n            vector<double> fe;\n            vector<int> lm;\n            \n            // get the element force vector and initialize it to zero\n            int ndof = 7*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // calculate internal force vector\n            ElementInertialForce(el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        double densTs = m_pMat->TrueSolidDensity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        \n        // calculate the jacobian\n        detJ = detJt(el, n, tp.alphaf)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d f = pt.m_aft*(densTf*H[i]*detJ);\n            \n            vec3d fs = (-pt.m_aft*densTf + ept.m_a*densTs)*H[i]*phis*detJ; //mixture 2\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[7*i+0] -= fs.x;\n            fe[7*i+1] -= fs.y;\n            fe[7*i+2] -= fs.z;\n            fe[7*i+3] -= f.x;\n            fe[7*i+4] -= f.y;\n            fe[7*i+5] -= f.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSIDomain3D::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    ar & m_sseps & m_btrans;\n}\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSIDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEBiphasicFSIDomain.h\"\n#include \"FEBiphasicFSI.h\"\n\n//-----------------------------------------------------------------------------\n//! Biphasic-FSI domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEBiphasicFSIDomain3D : public FESolidDomain, public FEBiphasicFSIDomain\n{\npublic:\n    //! constructor\n    FEBiphasicFSIDomain3D(FEModel* pfem);\n    ~FEBiphasicFSIDomain3D() {}\n    \n    //! assignment operator\n    FEBiphasicFSIDomain3D& operator = (FEBiphasicFSIDomain3D& d);\n    \n    //! activate\n    void Activate() override;\n    \n    //! reset domain data\n    void Reset() override;\n    \n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! Unpack element data\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \n    // get the total dof\n    const FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n    \npublic: // overrides from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n    \n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n    \n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEBiphasicFSI*    m_pMat;\n    double      m_sseps;\n    \nprotected:\n    FEDofList    m_dofU, m_dofV, m_dofW, m_dofAW;\n    FEDofList    m_dofSU, m_dofR;\n    FEDofList    m_dof;\n    int    m_dofEF;\n    int m_dofAEF;\n};\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSITraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEBiphasicFSITraction.h\"\n#include \"FECore/FEModel.h\"\n#include \"FEFluid.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEBioFSI.h\"\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEBiphasicFSITraction, FESurfaceLoad)\nADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEBiphasicFSITraction::FEBiphasicFSITraction(FEModel* pfem) : FESurfaceLoad(pfem), m_dofU(pfem), m_dofSU(pfem), m_dofW(pfem)\n{\n    m_bshellb = false;\n\n    // get the degrees of freedom\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::DISPLACEMENT));\n        m_dofSU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_DISPLACEMENT));\n        m_dofW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_VELOCITY));\n        m_dofEF = pfem->GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION), 0);\n\n        m_dof.Clear();\n        m_dof.AddDofs(m_dofU);\n        m_dof.AddDofs(m_dofSU);\n        m_dof.AddDofs(m_dofW);\n        m_dof.AddDof(m_dofEF);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEBiphasicFSITraction::Init()\n{\n    FESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n    surf.SetInterfaceStatus(true);\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    // get the list of fluid-FSI elements connected to this interface\n    FEModel* fem = GetFEModel();\n    int NF = surf.Elements();\n    m_elem.resize(NF);\n    m_s.resize(NF, 1);\n    for (int j = 0; j<NF; ++j)\n    {\n        bool bself = false;\n        FESurfaceElement& el = surf.Element(j);\n        // extract the first of two elements on this interface\n        m_elem[j] = el.m_elem[0].pe;\n        if (el.m_elem[1].pe == nullptr) bself = true;\n        // get its material and check if FEBiphasicFSI\n        FEMaterial* pm = fem->GetMaterial(m_elem[j]->GetMatID());\n        FEBiphasicFSI* pfsi = dynamic_cast<FEBiphasicFSI*>(pm);\n        if (pfsi) {\n            double s = m_psurf->FacePointing(el, *m_elem[j]);\n            m_s[j] = bself ? -s : s;\n            if (m_s[j] == 0) return false;\n        }\n        else if (!bself) {\n            // extract the second of two elements on this interface\n            m_elem[j] = el.m_elem[1].pe;\n            pm = fem->GetMaterial(m_elem[j]->GetMatID());\n            pfsi = dynamic_cast<FEBiphasicFSI*>(pm);\n            if (pfsi == nullptr) return false;\n            m_s[j] = m_psurf->FacePointing(el, *m_elem[j]);\n            if (m_s[j] == 0) return false;\n        }\n        else\n            return false;\n    }\n    \n    // TODO: Deal with the case when the surface is a shell domain separating two FSI domains\n    // that use different fluid bulk moduli\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEBiphasicFSITraction::GetFluidDilatation(FESurfaceMaterialPoint& mp, double alpha)\n{\n    double ef = 0;\n    FESurfaceElement& el = *mp.SurfaceElement();\n    double* H = el.H(mp.m_index);\n    int neln = el.Nodes();\n    for (int j = 0; j < neln; ++j) {\n        FENode& node = m_psurf->Node(el.m_lnode[j]);\n        double ej = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1.0 - alpha);\n        ef += ej*H[j];\n    }\n    return ef;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEBiphasicFSITraction::GetFluidStress(FESurfaceMaterialPoint& pt)\n{\n    FEModel* fem = GetFEModel();\n    FESurfaceElement& face = *pt.SurfaceElement();\n    int iel = face.m_lid;\n    \n    // Get the fluid stress from the fluid-FSI element\n    mat3ds sv(mat3dd(0));\n    FEElement* pe = m_elem[iel];\n    int nint = pe->GaussPoints();\n    FEBiphasicFSI* pfsi = dynamic_cast<FEBiphasicFSI*>(fem->GetMaterial(pe->GetMatID()));\n    for (int n = 0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n        sv += pfsi->Fluid()->GetViscous()->Stress(mp);\n    }\n    sv /= nint;\n    return sv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSITraction::LoadVector(FEGlobalVector& R)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    // If surface is bottom of shell, we should take shell displacement dofs (i.e. m_dofSU).\n    FEDofList dof = m_bshellb ? m_dofSU : m_dofU;\n    m_psurf->LoadVector(R, dof, false, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n        \n        // get the surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        int iel = el.m_lid;\n        FEBiphasicFSI* pfsi = dynamic_cast<FEBiphasicFSI*>(GetFEModel()->GetMaterial(m_elem[iel]->GetMatID()));\n\n        // nodal coordinates\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        \n        // evaluate covariant basis vectors at integration point\n        vec3d gr = el.eval_deriv1(rt, mp.m_index)*m_s[iel];\n        vec3d gs = el.eval_deriv2(rt, mp.m_index);\n        vec3d gt = gr ^ gs;\n        \n        // Get the fluid stress at integration point\n        // necessarily using the attached solid element\n        mat3ds sv = GetFluidStress(mp);\n        \n        // fluid dilatation at integration point\n        // only from surface element\n        double ef = GetFluidDilatation(mp, tp.alphaf);\n        double p = pfsi->Fluid()->Pressure(ef);\n\n        // evaluate traction\n        vec3d f = gt*p - sv*gt;\n        \n        double H = dof_a.shape;\n        fa[0] = H * f.x;\n        fa[1] = H * f.y;\n        fa[2] = H * f.z;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSITraction::StiffnessMatrix(FELinearSystem& LS)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    FEModel* fem = GetFEModel();\n    FESurface* ps = &GetSurface();\n    \n    // build dof list\n    // TODO: If surface is bottom of shell, we should take shell displacement dofs (i.e. m_dofSU).\n    FEDofList dofs(fem);\n    if (!m_bshellb) dofs.AddDofs(m_dofU); else dofs.AddDofs(m_dofSU);\n    dofs.AddDofs(m_dofW);\n    dofs.AddDof(m_dofEF);\n    \n    // evaluate stiffness\n    m_psurf->LoadStiffness(LS, dofs, dofs, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n        \n        FESurfaceElement& el = *mp.SurfaceElement();\n        int iel = el.m_lid;\n        int neln = el.Nodes();\n        \n        double dt = tp.timeIncrement;\n        double alpha = tp.alphaf;\n        double a = tp.gamma / (tp.beta*dt);\n        \n        vector<vec3d> gradN(neln);\n        \n        // nodal coordinates\n        vec3d rt[FEElement::MAX_NODES];\n        ps->GetNodalCoordinates(el, tp.alphaf, rt);\n        \n        // Get the fluid stress and its tangents from the fluid-FSI element\n        mat3ds sv(mat3dd(0)), svJ(mat3dd(0));\n        tens4ds cv; cv.zero();\n        mat3d Ls; Ls.zero();\n        mat3d Dw; Dw.zero();\n        double phif=0.0;\n        double phis=0.0;\n        double J=0.0;\n        vec3d gradphif = vec3d(0.0);\n        vec3d gradJ = vec3d(0.0);\n        FEElement* pe = m_elem[iel];\n        int pint = pe->GaussPoints();\n        FEBiphasicFSI* pfsi = dynamic_cast<FEBiphasicFSI*>(fem->GetMaterial(pe->GetMatID()));\n        for (int n = 0; n<pint; ++n)\n        {\n            FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n            FEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n            FEBiphasicFSIMaterialPoint& ft = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n            sv += pfsi->Fluid()->GetViscous()->Stress(mp);\n            svJ += pfsi->Fluid()->GetViscous()->Tangent_Strain(mp);\n            cv += pfsi->Fluid()->Tangent_RateOfDeformation(mp);\n            Ls += ep.m_L;\n            phif += pfsi->Porosity(mp);\n            phis += pfsi->SolidVolumeFrac(mp);\n            gradphif += pfsi->gradPorosity(mp);\n            gradJ += ft.m_gradJ;\n            Dw += ft.m_Lw.sym();\n            J += ep.m_J;\n        }\n        sv /= pint;\n        svJ /= pint;\n        cv /= pint;\n        Ls /= pint;\n        phif /= pint;\n        phis /=pint;\n        gradphif /=pint;\n        gradJ /=pint;\n        Dw /=pint;\n        J /=pint;\n        mat3d M = mat3dd(a) - Ls;\n        \n        double* N  = el.H (mp.m_index);\n        double* Gr = el.Gr(mp.m_index);\n        double* Gs = el.Gs(mp.m_index);\n        \n        // evaluate fluid dilatation\n        double ef = GetFluidDilatation(mp, tp.alphaf);\n        \n        // covariant basis vectors\n        vec3d gr = el.eval_deriv1(rt, mp.m_index)*m_s[iel];\n        vec3d gs = el.eval_deriv2(rt, mp.m_index);\n        vec3d gt = gr ^ gs;\n        \n        // evaluate fluid pressure\n        double p = pfsi->Fluid()->Pressure(ef);\n\n        vec3d f = gt*pfsi->Fluid()->GetElastic()->Tangent_Strain(ef, 0);\n\n        //TODO include second order gradgrad term for surface\n        \n        vec3d gcnt[2], gcntp[2];\n        ps->ContraBaseVectors(el, mp.m_index, gcnt);\n        ps->ContraBaseVectorsP(el, mp.m_index, gcntp);\n        for (int i = 0; i<neln; ++i)\n            gradN[i] = ((gcnt[0] * alpha + gcntp[0] * (1 - alpha))*(Gr[i]*m_s[iel]) +\n            (gcnt[1] * alpha + gcntp[1] * (1 - alpha))*Gs[i]);\n        \n        // calculate stiffness component\n        int i = dof_a.index;\n        int j = dof_b.index;\n        vec3d v = gr*Gs[j] - gs*Gr[j];\n        mat3d A; A.skew(v);\n        mat3d Kv1 = vdotTdotv(gt, cv, gradN[j]);\n        mat3d Kv2 = vdotTdotv(gt, cv, gradN[j]);\n        mat3d Kw = vdotTdotv(gt, cv, (gradN[j]-gradphif*N[j]/phif)/phif);\n        \n        mat3d Kuu = (sv*A + Kv1*(-Dw*phis/(phif*phif)+M) + Kv2*((gradphif&gradN[j])*2.0*phis/(phif*phif*phif)+(gradN[j]&gradphif)/(phif*phif)) - Kv2*(-gradJ&gradN[j])/J*phis/(phif*phif) - ((sv*gt)&gradN[j])*phis/phif)*N[i] - A*(N[i] * p); Kuu *= -alpha;\n        mat3d Kuw = Kw*N[i]; Kuw *= -alpha;\n        vec3d kuJ = svJ*gt*(N[i] * N[j]) - f*(N[i] * N[j]); kuJ *= -alpha;\n        \n        Kab.zero();\n        Kab.sub(0, 0, Kuu);\n        Kab.sub(0, 3, Kuw);\n        \n        Kab[0][6] -= kuJ.x;\n        Kab[1][6] -= kuJ.y;\n        Kab[2][6] -= kuJ.z;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicFSITraction::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    \n    ar & m_s;\n    \n    if (ar.IsShallow() == false)\n    {\n        if (ar.IsSaving())\n        {\n            int NE = (int)m_elem.size();\n            ar << NE;\n            for (int i = 0; i < NE; ++i)\n            {\n                FEElement* pe = m_elem[i];\n                int nid = (pe ? pe->GetID() : -1);\n                ar << nid;\n            }\n        }\n        else\n        {\n            FEMesh& mesh = ar.GetFEModel().GetMesh();\n            int NE, nid;\n            ar >> NE;\n            m_elem.resize(NE, nullptr);\n            for (int i = 0; i < NE; ++i)\n            {\n                ar >> nid;\n                if (nid != -1)\n                {\n                    FEElement* pe = mesh.FindElementFromID(nid);\n                    assert(pe);\n                    m_elem[i] = pe;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEBiphasicFSITraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! This surface load represents the traction applied on the solid at the\n//! interface between a fluid and solid in an FSI analysis.\nclass FEBIOFLUID_API FEBiphasicFSITraction : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEBiphasicFSITraction(FEModel* pfem);\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialization\n    bool Init() override;\n    \nprivate:\n    double GetFluidDilatation(FESurfaceMaterialPoint& mp, double alpha);\n    mat3ds GetFluidStress(FESurfaceMaterialPoint& mp);\n    \nprotected:\n    vector<double>      m_s;        //!< scale factor\n    vector<FEElement*>  m_elem;     //!< list of fluid-FSI elements\n    \n    // degrees of freedom\n    FEDofList    m_dofU, m_dofSU, m_dofW;\n    int        m_dofEF;\n    \nprotected:\n    bool                m_bshellb;  //!< flag for prescribing traction on shell bottom\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEBodyMoment.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBodyMoment.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEPolarFluidDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEBodyMoment::FEBodyMoment(FEModel* pfem) : FEBodyLoad(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Work in progress! Working on integrating body loads as model loads\nvoid FEBodyMoment::LoadVector(FEGlobalVector& R)\n{\n\tfor (int i = 0; i<Domains(); ++i)\n\t{\n\t\tFEDomain* dom = Domain(i);\n        FEFluidMaterial* mat = dynamic_cast<FEFluidMaterial*>(dom->GetMaterial());\n\t\tif (mat == nullptr)\n\t\t{\n\t\t\tFEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(dom);\n\t\t\tif (pfdom) pfdom->BodyMoment(R, *this);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Work in progress! Working on integrating body loads as model loads\nvoid FEBodyMoment::StiffnessMatrix(FELinearSystem& LS)\n{\n\tfor (int i = 0; i<Domains(); ++i)\n\t{\n\t\tFEDomain* dom = Domain(i);\n        FEFluidMaterial* mat = dynamic_cast<FEFluidMaterial*>(dom->GetMaterial());\n\t\tif (mat==nullptr)\n\t\t{\n            FEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(dom);\n\t\t\tif (pfdom) pfdom->BodyMomentStiffness(LS, *this);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioFluid/FEBodyMoment.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FEBodyLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body moments\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOFLUID_API FEBodyMoment : public FEBodyLoad\n{\npublic:\n\t//! constructor\n    FEBodyMoment(FEModel* pfem);\n\npublic:\n\t//! calculate the specific body moment at a material point\n\tvirtual vec3d moment(FEMaterialPoint& pt) = 0;\n\n\t//! calculate constribution to stiffness matrix\n\tvirtual mat3ds stiffness(FEMaterialPoint& pt) = 0;\n\npublic:\n\tvoid LoadVector(FEGlobalVector& R) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n};\n"
  },
  {
    "path": "FEBioFluid/FECarreauFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECarreauFluid.h\"\n#include \"FEFluid.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FECarreauFluid, FEViscousFluid)\n\tADD_PARAMETER(m_mu0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\")->setUnits(\"P.t\")->setLongName(\"zero shear rate viscosity\");\n\tADD_PARAMETER(m_mui, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mui\")->setUnits(\"P.t\")->setLongName(\"infinite shear rate viscosity\");\n\tADD_PARAMETER(m_lam, FE_RANGE_GREATER_OR_EQUAL(0.0), \"lambda\")->setUnits(UNIT_TIME)->setLongName(\"relaxation time\");\n\tADD_PARAMETER(m_n  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"n\")->setLongName(\"power index\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFECarreauFluid::FECarreauFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_mu0 = 0;\n    m_mui = 0;\n    m_lam = 0;\n    m_n = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FECarreauFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double mu = ShearViscosity(pt);\n    \n    mat3ds s = D*(2*mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FECarreauFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FECarreauFluid::Tangent_RateOfDeformation(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamg2 = m_lam*m_lam*gdot*gdot;\n    \n    double mu = m_mui + (m_mu0 - m_mui)*pow(1+lamg2, (m_n-1)*0.5);\n    double dmu = 2*(m_mu0 - m_mui)*(m_n-1)*m_lam*m_lam*pow(1+lamg2, (m_n-3)*0.5);\n    mat3dd I(1.0);\n    tens4ds c = dyad1s(D)*(2*dmu) + dyad4s(I)*(2*mu);\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic viscosity\ndouble FECarreauFluid::ShearViscosity(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double mu = m_mui + (m_mu0 - m_mui)*pow(1+m_lam*m_lam*gdot*gdot, (m_n-1)*0.5);\n    return mu;\n}\n\n//! derivative of shear viscosity w.r.t. strain rate\ndouble FECarreauFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\tFEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n\tmat3ds D = vt.RateOfDeformation();\n\tdouble gdot = sqrt(2 * (D.sqr()).tr());\n\tdouble lamg2 = m_lam * m_lam * gdot * gdot;\n\n\tdouble dmu = 2 * (m_mu0 - m_mui) * (m_n - 1) * m_lam * m_lam * pow(1 + lamg2, (m_n - 3) * 0.5);\n\n   double dmu_dgdot = 0.5 * gdot * dmu;\n\n   return dmu_dgdot;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FECarreauFluid::BulkViscosity(FEMaterialPoint& pt)\n{\n    return 2*ShearViscosity(pt)/3.;\n}\n"
  },
  {
    "path": "FEBioFluid/FECarreauFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Carreau power-law fluid\n\nclass FEBIOFLUID_API FECarreauFluid :\tpublic FEViscousFluid\n{\npublic:\n    //! constructor\n    FECarreauFluid(FEModel* pfem);\n    \n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    \n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double\tm_mu0;\t\t//!< shear viscosity at zero shear rate\n    double\tm_mui;\t\t//!< shear viscosity at infinite shear rate\n    double  m_lam;      //!< time constant\n    double  m_n;        //!< power-law index\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FECarreauYasudaFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECarreauYasudaFluid.h\"\n#include \"FEFluid.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FECarreauYasudaFluid, FEViscousFluid)\n\tADD_PARAMETER(m_mu0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\")->setUnits(\"P.t\")->setLongName(\"zero shear rate viscosity\");\n\tADD_PARAMETER(m_mui, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mui\")->setUnits(\"P.t\")->setLongName(\"infinite shear rate viscosity\");\n\tADD_PARAMETER(m_lam, FE_RANGE_GREATER_OR_EQUAL(0.0), \"lambda\")->setUnits(UNIT_TIME)->setLongName(\"relaxation time\");\n\tADD_PARAMETER(m_n  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"n\")->setLongName(\"power index\");\n    ADD_PARAMETER(m_a  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"a\")->setLongName(\"power denominator\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFECarreauYasudaFluid::FECarreauYasudaFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_mu0 = 0;\n    m_mui = 0;\n    m_lam = 0;\n    m_n = 1;\n    m_a = 2;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FECarreauYasudaFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double mu = ShearViscosity(pt);\n    \n    mat3ds s = D*(2*mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FECarreauYasudaFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FECarreauYasudaFluid::Tangent_RateOfDeformation(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamga = pow(m_lam*gdot,m_a);\n    \n    double mu = m_mui + (m_mu0 - m_mui)*pow(1+lamga, (m_n-1)/m_a);\n    double dmu = (m_a >= 2) ? 2*(m_mu0 - m_mui)*(m_n-1)*pow(m_lam,m_a)*pow(gdot,m_a-2)\n    *pow(1+lamga, (m_n-m_a-1)/m_a) : 0;\n    mat3dd I(1.0);\n    tens4ds c = dyad1s(D)*(2*dmu) + dyad4s(I)*(2*mu);\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic viscosity\ndouble FECarreauYasudaFluid::ShearViscosity(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamga = pow(m_lam*gdot,m_a);\n    double mu = m_mui + (m_mu0 - m_mui)*pow(1+lamga, (m_n-1)/m_a);\n    return mu;\n}\n\n//! derivative of shear viscosity w.r.t. strain rate\ndouble FECarreauYasudaFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\tFEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n\tmat3ds D = vt.RateOfDeformation();\n\tdouble gdot = sqrt(2 * (D.sqr()).tr());\n\tdouble lamga = pow(m_lam * gdot, m_a);\n\n\tdouble dmu = (m_a >= 2) ? 2 * (m_mu0 - m_mui) * (m_n - 1) * pow(m_lam, m_a) * pow(gdot, m_a - 2)\n\t\t* pow(1 + lamga, (m_n - m_a - 1) / m_a) : 0;\n\n\tdouble dmu_dgdot = 0.5 * gdot * dmu;\n\n   return dmu_dgdot;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FECarreauYasudaFluid::BulkViscosity(FEMaterialPoint& pt)\n{\n    return 2*ShearViscosity(pt)/3.;\n}\n"
  },
  {
    "path": "FEBioFluid/FECarreauYasudaFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Carreau-Yasuda power-law fluid\n\nclass FEBIOFLUID_API FECarreauYasudaFluid :\tpublic FEViscousFluid\n{\npublic:\n    //! constructor\n    FECarreauYasudaFluid(FEModel* pfem);\n    \n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    \n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double\tm_mu0;\t\t//!< shear viscosity at zero shear rate\n    double\tm_mui;\t\t//!< shear viscosity at infinite shear rate\n    double  m_lam;      //!< time constant\n    double  m_n;        //!< power-law index\n    double  m_a;        //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FECentrifugalFluidBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECentrifugalFluidBodyForce.h\"\n\nBEGIN_FECORE_CLASS(FECentrifugalFluidBodyForce, FEBodyForce);\n\tADD_PARAMETER(w, \"angular_speed\")->setUnits(UNIT_ANGULAR_VELOCITY);\n\tADD_PARAMETER(n, \"rotation_axis\");\n\tADD_PARAMETER(c, \"rotation_center\")->setUnits(UNIT_LENGTH);\nEND_FECORE_CLASS();\n\nFECentrifugalFluidBodyForce::FECentrifugalFluidBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n\tw = 0.0;\n\tn = vec3d(0,0,1);\n\tc = vec3d(0,0,0);\n}\n\nvec3d FECentrifugalFluidBodyForce::force(FEMaterialPoint& mp)\n{\n\tmat3d K = stiffness(mp);\n\treturn K*(mp.m_rt - c);\n}\n\ndouble FECentrifugalFluidBodyForce::divforce(FEMaterialPoint& mp)\n{\n    return -2*w*w;\n}\n\nmat3d FECentrifugalFluidBodyForce::stiffness(FEMaterialPoint& mp)\n{ \n\treturn (mat3dd(1) - dyad(n))*(-w*w); \n}\n"
  },
  {
    "path": "FEBioFluid/FECentrifugalFluidBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include <FEBioMech/FEBodyForce.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class defines a centrigufal force\n\nclass FEBIOFLUID_API FECentrifugalFluidBodyForce : public FEBodyForce\n{\npublic:\n    FECentrifugalFluidBodyForce(FEModel* pfem);\n\n\tvec3d force(FEMaterialPoint& mp) override;\n\n\tdouble divforce(FEMaterialPoint& mp) override;\n\n\tmat3d stiffness(FEMaterialPoint& mp) override;\n\npublic:\n\tvec3d\tn;\t// rotation axis\n\tvec3d\tc;\t// point on axis of rotation (e.g., center of rotation)\n\tdouble\tw;\t// angular speed\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEConductivityRealVapor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n//\n//  FEFluidConstantConductivity.cpp\n//  FEBioFluid\n//\n//  Created by Gerard Ateshian on 2/28/20.\n//  Copyright © 2020 febio.org. All rights reserved.\n//\n\n#include \"FEConductivityRealVapor.h\"\n#include \"FEThermoFluid.h\"\n#include \"FERealVapor.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEConductivityRealVapor, FEFluidThermalConductivity)\n\n    // parameters\n    ADD_PARAMETER(m_Kr, \"Kr\")->setLongName(\"referential thermal conductivity\")->setUnits(UNIT_THERMAL_CONDUCTIVITY);\n    // properties\n    ADD_PROPERTY(m_esat , \"esat\", FEProperty::Optional)->SetLongName(\"saturation dilatation\");\n    ADD_PROPERTY(m_Ksat , \"Ksat\", FEProperty::Optional)->SetLongName(\"normalized saturation thermal conductivity\");\n    ADD_PROPERTY(m_C[0] , \"C0\"  , FEProperty::Optional)->SetLongName(\"1st K virial coeff\");\n    ADD_PROPERTY(m_C[1] , \"C1\"  , FEProperty::Optional)->SetLongName(\"2nd K virial coeff\");\n    ADD_PROPERTY(m_C[2] , \"C2\"  , FEProperty::Optional)->SetLongName(\"3rd K virial coeff\");\n    ADD_PROPERTY(m_C[3] , \"C3\"  , FEProperty::Optional)->SetLongName(\"4th K virial coeff\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConductivityRealVapor::FEConductivityRealVapor(FEModel* pfem) : FEFluidThermalConductivity(pfem)\n{\n    m_Ksat = nullptr;\n    m_esat = nullptr;\n    for (int k=0; k<MAX_NVC; ++k) m_C[k] = nullptr;\n    m_Kr = 0;\n    m_Tr = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEConductivityRealVapor::Init()\n{\n    m_Tr = GetGlobalConstant(\"T\");\n    \n    if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    \n    if (m_esat) m_esat->Init();\n    else {\n        FECoreBase* pMat = GetAncestor();\n        FEThermoFluid* pFluid = dynamic_cast<FEThermoFluid*>(pMat);\n        if (pFluid) {\n            FERealVapor* pRV = dynamic_cast<FERealVapor*>(pFluid->GetElastic());\n            if (pRV) {\n                m_esat = pRV->m_esat;\n                m_esat->Init();\n                m_Tc = pRV->m_Tc;\n                m_alpha = pRV->m_alpha;\n            }\n            else return false;\n        }\n        else return false;\n    }\n    if (m_Ksat) m_Ksat->Init();\n    m_nvc = 0;\n    for (int k=0; k<MAX_NVC; ++k) {\n        if (m_C[k]) {\n            m_C[k]->Init();\n            ++m_nvc;\n        }\n    }\n\n    return FEFluidThermalConductivity::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConductivityRealVapor::Serialize(DumpStream& ar)\n{\n    FEFluidThermalConductivity::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_Kr & m_Tr & m_nvc;\n    ar & m_Ksat;\n    for (int i=0; i<MAX_NVC; ++i)\n        ar & m_C[i];\n    \n    if (ar.IsLoading()) {\n        if (m_Ksat) m_Ksat->Init();\n        for (int i=0; i<MAX_NVC; ++i)\n            if (m_C[i]) m_C[i]->Init();\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculate thermal conductivity at material point\ndouble FEConductivityRealVapor::ThermalConductivity(FEMaterialPoint& mp)\n{\n    double K = 1.0;\n    if (m_Ksat) {\n        FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n        FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n        double T = tf.m_T + m_Tr;\n        double That = T/m_Tr;\n        double J = 1 + pf.m_ef;\n        double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n        double q = log(1+pow(y,m_alpha));\n        K = m_Ksat->value(q);\n        double Jsat = exp(m_esat->value(q));\n        double x = 1 - Jsat/J;\n        for (int k=0; k<m_nvc; ++k) K += m_C[k]->value(q)*pow(x,k+1);\n    }\n    return K*m_Kr;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of thermal conductivity with respect to strain J\ndouble FEConductivityRealVapor::Tangent_Strain(FEMaterialPoint& mp)\n{\n    double d = 1e-6;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef+d;\n    ft->m_T = tf.m_T;\n    FEMaterialPoint tmp(ft);\n    double Kp = ThermalConductivity(tmp);\n    fp->m_ef = pf.m_ef-d;\n    double Km = ThermalConductivity(tmp);\n    delete ft;\n    double dKJ = (Kp - Km)/(2*d);\n    return dKJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of thermal conductivity with respect to temperature T\ndouble FEConductivityRealVapor::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    double Tr = GetGlobalConstant(\"T\");\n    double d = 1e-6*Tr;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef;\n    ft->m_T = tf.m_T+d;\n    FEMaterialPoint tmp(ft);\n    double Kp = ThermalConductivity(tmp);\n    ft->m_T = tf.m_T-d;\n    double Km = ThermalConductivity(tmp);\n    delete ft;\n    double dKT = (Kp - Km)/(2*d);\n    return dKT;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEConductivityRealVapor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluidThermalConductivity.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid thermal conductivity materials.\n\nclass FEBIOFLUID_API FEConductivityRealVapor : public FEFluidThermalConductivity\n{\npublic:\n    enum { MAX_NVC = 4 };\n    \npublic:\n    FEConductivityRealVapor(FEModel* pfem);\n    virtual ~FEConductivityRealVapor() {}\n    \npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! calculate thermal conductivity at material point\n    double ThermalConductivity(FEMaterialPoint& pt) override;\n    \n    //! tangent of thermal conductivity with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of thermal conductivity with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \npublic:\n    double          m_Kr;   //!< thermal conductivity at reference temperature\n    double          m_Tr;   //!< reference temperature\n    double          m_Tc;           //!< normalized critical temperature (Tc/Tr)\n    double          m_alpha;        //!< exponent alpha used for calculating temperature map\n    int             m_nvc;          //!< number of virial coefficients for isochoric specific heat capacity\n    FEFunction1D*   m_esat;         //!< dilatation on saturation curve\n    FEFunction1D*   m_Ksat;        //!< normalized thermal conductivity vs normalized temperature on saturation curve\n    FEFunction1D*   m_C[MAX_NVC];   //!< non-dimensional conductivity virial coefficient\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n    \n};\n"
  },
  {
    "path": "FEBioFluid/FEConstFluidBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEConstFluidBodyForce.h\"\n\nBEGIN_FECORE_CLASS(FEConstFluidBodyForce, FEBodyForce);\n    ADD_PARAMETER(m_force, \"force\")->setUnits(UNIT_SPECIFIC_FORCE);\nEND_FECORE_CLASS();\n\nFEConstFluidBodyForce::FEConstFluidBodyForce(FEModel* pfem) : FEBodyForce(pfem) \n{ \n}\n\nvec3d FEConstFluidBodyForce::force(FEMaterialPoint& pt) \n{ \n    return m_force(pt);\n}\n\nmat3d FEConstFluidBodyForce::stiffness(FEMaterialPoint& pt) \n{ \n\treturn mat3ds(0, 0, 0, 0, 0, 0); \n}\n"
  },
  {
    "path": "FEBioFluid/FEConstFluidBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEModelParam.h>\n#include <FECore/FEMaterialPoint.h>\n#include <FEBioMech/FEBodyForce.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body forces\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOFLUID_API FEConstFluidBodyForce : public FEBodyForce\n{\npublic:\n\t//! constructor\n\tFEConstFluidBodyForce(FEModel* pfem);\n\npublic:\n\t//! calculate the body force at a material point\n\tvec3d force(FEMaterialPoint& pt) override;\n\n\t//! calculate the body force at a material point\n\tdouble divforce(FEMaterialPoint& pt) override { return 0; }\n\n\t//! calculate constribution to stiffness matrix\n\tmat3d stiffness(FEMaterialPoint& pt) override;\n\nprotected:\n    FEParamVec3 m_force;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEConstFluidBodyMoment.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEConstFluidBodyMoment.h\"\n\nBEGIN_FECORE_CLASS(FEConstFluidBodyMoment, FEBodyMoment);\n    ADD_PARAMETER(m_moment, \"moment\")->setUnits(UNIT_SPECIFIC_FORCE);\nEND_FECORE_CLASS();\n\nFEConstFluidBodyMoment::FEConstFluidBodyMoment(FEModel* pfem) : FEBodyMoment(pfem)\n{ \n}\n\nvec3d FEConstFluidBodyMoment::moment(FEMaterialPoint& pt)\n{ \n\treturn m_moment(pt);\n}\n\nmat3ds FEConstFluidBodyMoment::stiffness(FEMaterialPoint& pt)\n{ \n\treturn mat3ds(0, 0, 0, 0, 0, 0); \n}\n"
  },
  {
    "path": "FEBioFluid/FEConstFluidBodyMoment.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FEModelParam.h>\n#include \"FEBodyMoment.h\"\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body forces\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOFLUID_API FEConstFluidBodyMoment : public FEBodyMoment\n{\npublic:\n\t//! constructor\n    FEConstFluidBodyMoment(FEModel* pfem);\n\npublic:\n\t//! calculate the body force at a material point\n\tvec3d moment(FEMaterialPoint& pt) override;\n\n\t//! calculate constribution to stiffness matrix\n\tmat3ds stiffness(FEMaterialPoint& pt) override;\n\nprotected:\n    FEParamVec3 m_moment;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEConstraintFrictionlessWall.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEConstraintFrictionlessWall.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FESurface.h>\n\nBEGIN_FECORE_CLASS(FEConstraintFrictionlessWall, FESurfaceConstraint)\n    ADD_PARAMETER(m_lc.m_laugon, \"laugon\");\n    ADD_PARAMETER(m_lc.m_tol, \"tol\");\n    ADD_PARAMETER(m_lc.m_eps, \"penalty\");\n    ADD_PARAMETER(m_lc.m_rhs, \"rhs\");\n    ADD_PARAMETER(m_lc.m_naugmin, \"minaug\");\n    ADD_PARAMETER(m_lc.m_naugmax, \"maxaug\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConstraintFrictionlessWall::FEConstraintFrictionlessWall(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem), m_lc(pfem)\n{\n    m_binit = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures.\nvoid FEConstraintFrictionlessWall::Activate()\n{\n    // don't forget to call base class\n    FESurfaceConstraint::Activate();\n    \n    if (m_binit == false) {\n        \n        // evaluate the nodal normals\n        m_surf.UpdateNodeNormals();\n        \n        // get the dofs\n        int dof_wx = GetDOFIndex(\"wx\");\n        int dof_wy = GetDOFIndex(\"wy\");\n        int dof_wz = GetDOFIndex(\"wz\");\n\n        // create linear constraints\n        // for a frictionless wall the constraint on (vx, vy, vz) is\n        // nx*vx + ny*vy + nz*vz = 0\n        for (int i=0; i<m_surf.Nodes(); ++i) {\n            FEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n            FENode& node = m_surf.Node(i);\n            vec3d nn = m_surf.NodeNormal(i);\n            pLC->AddDOF(node.GetID(), dof_wx, nn.x);\n            pLC->AddDOF(node.GetID(), dof_wy, nn.y);\n            pLC->AddDOF(node.GetID(), dof_wz, nn.z);\n            // add the linear constraint to the system\n            m_lc.add(pLC);\n        }\n        m_lc.Init();\n        m_lc.Activate();\n        m_binit = true;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEConstraintFrictionlessWall::Init()\n{\n    // initialize surface\n    return m_surf.Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstraintFrictionlessWall::Serialize(DumpStream& ar) { m_lc.Serialize(ar); }\nvoid FEConstraintFrictionlessWall::LoadVector(FEGlobalVector& R, const FETimeInfo& tp) { m_lc.LoadVector(R, tp); }\nvoid FEConstraintFrictionlessWall::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) { m_lc.StiffnessMatrix(LS, tp); }\nbool FEConstraintFrictionlessWall::Augment(int naug, const FETimeInfo& tp) { return m_lc.Augment(naug, tp); }\nvoid FEConstraintFrictionlessWall::BuildMatrixProfile(FEGlobalMatrix& M) { m_lc.BuildMatrixProfile(M); }\nint FEConstraintFrictionlessWall::InitEquations(int neq) { return m_lc.InitEquations(neq); }\nvoid FEConstraintFrictionlessWall::Update(const std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.Update(Ui, ui); }\nvoid FEConstraintFrictionlessWall::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.UpdateIncrements(Ui, ui); }\nvoid FEConstraintFrictionlessWall::PrepStep() { m_lc.PrepStep(); }\n"
  },
  {
    "path": "FEBioFluid/FEConstraintFrictionlessWall.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FESurface.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEConstraintFrictionlessWall class implements a frictionless fluid wall\n//! as a linear constraint on the components of the fluid velocity.\n\nclass FEBIOFLUID_API FEConstraintFrictionlessWall : public FESurfaceConstraint\n{\npublic:\n    //! constructor\n    FEConstraintFrictionlessWall(FEModel* pfem);\n    \n    //! destructor\n    ~FEConstraintFrictionlessWall() {}\n    \n    //! Activation\n    void Activate() override;\n\n    //! initialization\n    bool Init() override;\n    \n    // allocate equations\n    int InitEquations(int neq) override;\n    \nprotected:\n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \n    //! Get the surface\n    FESurface* GetSurface() override { return &m_surf; }\n    \npublic:\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! add the linear constraint contributions to the residual\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n    //! add the linear constraint contributions to the stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n    //! do the augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n\n    //! build connectivity for matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n\nprotected:\n    FESurface\tm_surf;\n    FELinearConstraintSet   m_lc;\n    bool        m_binit;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEConstraintNormalFlow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEConstraintNormalFlow.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FEMesh.h>\n\nBEGIN_FECORE_CLASS(FEConstraintNormalFlow, FESurfaceConstraint)\n    ADD_PARAMETER(m_lc.m_laugon, \"laugon\");\n    ADD_PARAMETER(m_lc.m_tol, \"tol\");\n    ADD_PARAMETER(m_lc.m_eps, \"penalty\");\n    ADD_PARAMETER(m_lc.m_rhs, \"rhs\");\n    ADD_PARAMETER(m_lc.m_naugmin, \"minaug\");\n    ADD_PARAMETER(m_lc.m_naugmax, \"maxaug\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConstraintNormalFlow::FEConstraintNormalFlow(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem), m_lc(pfem)\n{\n    m_binit = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures.\nvoid FEConstraintNormalFlow::Activate()\n{\n    // don't forget to call base class\n    FESurfaceConstraint::Activate();\n    \n    if (m_binit == false)\n    {\n        m_surf.UpdateNodeNormals();\n        \n        // get the dof indices\n        int dof_wx = GetDOFIndex(\"wx\");\n        int dof_wy = GetDOFIndex(\"wy\");\n        int dof_wz = GetDOFIndex(\"wz\");\n        \n        // create linear constraints\n        // for a surface with zero tangential velocity the constraints\n        // on (vx, vy, vz) are\n        //  (1-nx^2)*vx - nx*ny*vy - nx*nz*vz = 0\n        // -nx*ny*vx + (1-ny^2)*vy - ny*nz*vz = 0\n        // -nx*nz*vx - ny*nz*vy + (1-nz^2)*vz = 0\n        for (int i=0; i<m_surf.Nodes(); ++i) {\n            FENode& node = m_surf.Node(i);\n            if ((node.HasFlags(FENode::EXCLUDE) == false) && (node.m_rid == -1)) {\n                vec3d nn = m_surf.NodeNormal(i);\n                \n                //  (1-nx^2)*vx - nx*ny*vy - nx*nz*vz = 0\n                FEAugLagLinearConstraint* pLC0 = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n                pLC0->AddDOF(node.GetID(), dof_wx, 1.0 - nn.x * nn.x);\n                pLC0->AddDOF(node.GetID(), dof_wy,     - nn.x * nn.y);\n                pLC0->AddDOF(node.GetID(), dof_wz,     - nn.x * nn.z);\n                // add the linear constraint to the system\n                if ((pLC0->m_dof[0]->m_val != 0) || (pLC0->m_dof[1]->m_val != 0) || (pLC0->m_dof[2]->m_val != 0))\n                    m_lc.add(pLC0);\n                \n                // -nx*ny*vx + (1-ny^2)*vy - ny*nz*vz = 0\n                FEAugLagLinearConstraint* pLC1 = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n                pLC1->AddDOF(node.GetID(), dof_wx,     -nn.y * nn.x);\n                pLC1->AddDOF(node.GetID(), dof_wy, 1.0 -nn.y * nn.y);\n                pLC1->AddDOF(node.GetID(), dof_wz,     -nn.y * nn.z);\n                // add the linear constraint to the system\n                if ((pLC1->m_dof[0]->m_val != 0) || (pLC1->m_dof[1]->m_val != 0) || (pLC1->m_dof[2]->m_val != 0))\n                    m_lc.add(pLC1);\n                \n                // -nx*nz*vx - ny*nz*vy + (1-nz^2)*vz = 0\n                FEAugLagLinearConstraint* pLC2 = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n                pLC2->AddDOF(node.GetID(), dof_wx,    -nn.z * nn.x);\n                pLC2->AddDOF(node.GetID(), dof_wy,    -nn.z * nn.y);\n                pLC2->AddDOF(node.GetID(), dof_wz, 1.0-nn.z * nn.z);\n                // add the linear constraint to the system\n                if ((pLC2->m_dof[0]->m_val != 0) || (pLC2->m_dof[1]->m_val != 0) || (pLC2->m_dof[2]->m_val != 0))\n                    m_lc.add(pLC2);\n            }\n        }\n        m_lc.Init();\n        m_lc.Activate();\n        m_binit = true;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEConstraintNormalFlow::Init()\n{\n    // initialize surface\n    return m_surf.Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstraintNormalFlow::Serialize(DumpStream& ar)\n{\n\tFESurfaceConstraint::Serialize(ar);\n    m_lc.Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_surf;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstraintNormalFlow::LoadVector(FEGlobalVector& R, const FETimeInfo& tp) { m_lc.LoadVector(R, tp); }\nvoid FEConstraintNormalFlow::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) { m_lc.StiffnessMatrix(LS, tp); }\nbool FEConstraintNormalFlow::Augment(int naug, const FETimeInfo& tp) { return m_lc.Augment(naug, tp); }\nvoid FEConstraintNormalFlow::BuildMatrixProfile(FEGlobalMatrix& M) { m_lc.BuildMatrixProfile(M); }\nint FEConstraintNormalFlow::InitEquations(int neq) { return m_lc.InitEquations(neq); }\nvoid FEConstraintNormalFlow::Update(const std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.Update(Ui, ui); }\nvoid FEConstraintNormalFlow::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.UpdateIncrements(Ui, ui); }\nvoid FEConstraintNormalFlow::PrepStep() { m_lc.PrepStep(); }\n"
  },
  {
    "path": "FEBioFluid/FEConstraintNormalFlow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FESurface.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEConstraintNormalFlow class implements a fluid surface with zero\n//! tangential velocity as a linear constraint.\n\nclass FEBIOFLUID_API FEConstraintNormalFlow : public FESurfaceConstraint\n{\npublic:\n    //! constructor\n    FEConstraintNormalFlow(FEModel* pfem);\n    \n    //! destructor\n    ~FEConstraintNormalFlow() {}\n    \n    //! Activation\n    void Activate() override;\n    \n    //! initialization\n    bool Init() override;\n    \n    // allocate equations\n    int InitEquations(int neq) override;\n    \nprotected:\n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \n    //! Get the surface\n    FESurface* GetSurface() override { return &m_surf; }\n    \npublic:\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! add the linear constraint contributions to the residual\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n    //! add the linear constraint contributions to the stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n    //! do the augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n\n    //! build connectivity for matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n\nprotected:\n    FESurface\tm_surf;\n    FELinearConstraintSet   m_lc;\n    bool        m_binit;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEConstraintUniformFlow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEConstraintUniformFlow.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FEMesh.h>\n\nBEGIN_FECORE_CLASS(FEConstraintUniformFlow, FESurfaceConstraint)\n    ADD_PARAMETER(m_lc.m_laugon, \"laugon\");\n    ADD_PARAMETER(m_lc.m_tol, \"tol\");\n    ADD_PARAMETER(m_lc.m_eps, \"penalty\");\n    ADD_PARAMETER(m_lc.m_naugmin, \"minaug\");\n    ADD_PARAMETER(m_lc.m_naugmax, \"maxaug\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConstraintUniformFlow::FEConstraintUniformFlow(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem), m_lc(pfem)\n{\n    m_vbar = 0;\n    m_binit = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures.\nvoid FEConstraintUniformFlow::Activate()\n{\n    // don't forget to call base class\n    FESurfaceConstraint::Activate();\n    \n    if (m_binit == false)\n    {\n        // evaluate the nodal normals\n        m_surf.UpdateNodeNormals();\n        \n        // get the dof indices\n        int dof_wx = GetDOFIndex(\"wx\");\n        int dof_wy = GetDOFIndex(\"wy\");\n        int dof_wz = GetDOFIndex(\"wz\");\n        \n        // create linear constraint\n        // for a surface with prescribed velocity the constraint\n        // on (vx, vy, vz) is\n        //  nx*vx + ny*vy + nz*vz = vbar\n        for (int i=0; i<m_surf.Nodes(); ++i) {\n\n            //  (1-nx^2)*vx - nx*ny*vy - nx*nz*vz = 0\n            FEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n            for (int j=0; j<3; ++j) {\n                FENode& node = m_surf.Node(i);\n                vec3d nn = m_surf.NodeNormal(i);\n                switch (j) {\n                case 0: pLC->AddDOF(node.GetID(), dof_wx, nn.x); break;\n                case 1: pLC->AddDOF(node.GetID(), dof_wy, nn.y); break;\n                case 2: pLC->AddDOF(node.GetID(), dof_wz, nn.z); break;\n                }\n            }\n            // add the linear constraint to the system\n            m_lc.add(pLC);\n        }\n        m_lc.Init();\n        m_lc.Activate();\n        m_binit = true;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEConstraintUniformFlow::Init()\n{\n    // initialize surface\n    return m_surf.Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstraintUniformFlow::Update()\n{\n    // evaluate the mean normal velocity on this surface\n\n    // get the dof indices\n    int dof_wx = GetDOFIndex(\"wx\");\n    int dof_wy = GetDOFIndex(\"wy\");\n    int dof_wz = GetDOFIndex(\"wz\");\n\n    /*\n    m_vbar = 0;\n    // loop over all elements\n    for (int i=0; i<m_surf.Nodes(); ++i)\n    {\n        FENode& node = m_surf.Node(i);\n        vec3d w = node.get_vec3d(dof_wx, dof_wy, dof_wz);\n        double wn = w*m_nn[i];\n        m_vbar += wn;\n    }\n    m_vbar /= m_surf.Nodes();    */\n\n    m_vbar = 0;\n    double area = 0;\n    // loop over all elements\n    for (int i=0; i<m_surf.Elements(); ++i)\n    {\n        FESurfaceElement& el = m_surf.Element(i);\n        int ne = el.Nodes();\n        vector<vec3d> w(ne,vec3d(0,0,0));\n        \n        // get the nodal fluid velocities\n        for (int j=0; j<ne; ++j) w[j] = m_surf.Node(el.m_lnode[j]).get_vec3d(dof_wx, dof_wy, dof_wz);\n        \n        int nint = el.GaussPoints();\n        double* gw = el.GaussWeights();\n        vec3d t[2];\n        for (int n=0; n<nint; ++n) {\n            double* H = el.H(n);\n            vec3d wn(0,0,0);\n            for (int ie=0; ie<ne; ++ie) wn += w[ie]*H[ie];\n            m_surf.CoBaseVectors(el, n, t);\n            vec3d nu = t[0] ^ t[1];\n            m_vbar += (wn*nu)*gw[n];\n            area += nu.norm()*gw[n];\n        }\n    }\n    \n    m_vbar /= area;\n\n    // assign mean velocity as rhs of constraint\n    m_lc.m_rhs = m_vbar;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstraintUniformFlow::Serialize(DumpStream& ar) { m_lc.Serialize(ar); }\nvoid FEConstraintUniformFlow::LoadVector(FEGlobalVector& R, const FETimeInfo& tp) { m_lc.LoadVector(R, tp); }\nvoid FEConstraintUniformFlow::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) { m_lc.StiffnessMatrix(LS, tp); }\nbool FEConstraintUniformFlow::Augment(int naug, const FETimeInfo& tp) { return m_lc.Augment(naug, tp); }\nvoid FEConstraintUniformFlow::BuildMatrixProfile(FEGlobalMatrix& M) { m_lc.BuildMatrixProfile(M); }\nint FEConstraintUniformFlow::InitEquations(int neq) { return m_lc.InitEquations(neq); }\nvoid FEConstraintUniformFlow::Update(const std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.Update(Ui, ui); }\nvoid FEConstraintUniformFlow::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.UpdateIncrements(Ui, ui); }\nvoid FEConstraintUniformFlow::PrepStep() { m_lc.PrepStep(); }\n"
  },
  {
    "path": "FEBioFluid/FEConstraintUniformFlow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FESurface.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEConstraintUniformFlow class implements a fluid surface with zero\n//! tangential velocity as a linear constraint.\n\nclass FEBIOFLUID_API FEConstraintUniformFlow : public FESurfaceConstraint\n{\npublic:\n    //! constructor\n    FEConstraintUniformFlow(FEModel* pfem);\n    \n    //! destructor\n    ~FEConstraintUniformFlow() {}\n    \n    //! Activation\n    void Activate() override;\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Get the surface\n    FESurface* GetSurface() override { return &m_surf; }\n    \n    //! update\n    void Update() override;\n    \n    // allocate equations\n    int InitEquations(int neq) override;\n    \nprotected:\n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \npublic:\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! add the linear constraint contributions to the residual\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n    //! add the linear constraint contributions to the stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n    //! do the augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n\n    //! build connectivity for matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n\nprotected:\n    FESurface\tm_surf;\n    FELinearConstraintSet   m_lc;\n    bool        m_binit;\n    \n    double          m_vbar;     //! mean normal velocity\n    vector<vec3d>   m_nn;       //! nodal normals at initialization\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FECrossFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECrossFluid.h\"\n#include \"FEFluid.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FECrossFluid, FEViscousFluid)\n\tADD_PARAMETER(m_mu0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\")->setUnits(\"P.t\")->setLongName(\"zero shear rate viscosity\");\n\tADD_PARAMETER(m_mui, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mui\")->setUnits(\"P.t\")->setLongName(\"infinite shear rate viscosity\");\n\tADD_PARAMETER(m_lam, FE_RANGE_GREATER_OR_EQUAL(0.0), \"lambda\")->setUnits(UNIT_TIME)->setLongName(\"relaxation time\");\n\tADD_PARAMETER(m_m  , FE_RANGE_GREATER_OR_EQUAL(2.0), \"m\")->setLongName(\"power\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFECrossFluid::FECrossFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_mu0 = 0;\n    m_mui = 0;\n    m_lam = 0;\n    m_m = 2;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FECrossFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double mu = ShearViscosity(pt);\n    \n    mat3ds s = D*(2*mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FECrossFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FECrossFluid::Tangent_RateOfDeformation(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamg = m_lam*gdot;\n    \n    double mu = ShearViscosity(pt);\n    double dmu = -2*(m_mu0 - m_mui)*m_m*pow(m_lam,m_m)*pow(gdot,m_m-2)/pow(1+pow(lamg,m_m),2);\n    mat3dd I(1.0);\n    tens4ds c = dyad1s(D)*(2*dmu) + dyad4s(I)*(2*mu);\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic viscosity\ndouble FECrossFluid::ShearViscosity(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamg = m_lam*gdot;\n    double mu = m_mui + (m_mu0 - m_mui)/(1+pow(lamg, m_m));\n    return mu;\n}\n\n//! derivative of shear viscosity w.r.t. strain rate\ndouble FECrossFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\tFEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n\tmat3ds D = vt.RateOfDeformation();\n\tdouble gdot = sqrt(2 * (D.sqr()).tr());\n\tdouble lamg = m_lam * gdot;\n\n\tdouble dmu = -2 * (m_mu0 - m_mui) * m_m * pow(m_lam, m_m) * pow(gdot, m_m - 2) / pow(1 + pow(lamg, m_m), 2);\n\n\tdouble dmu_dgdot = 0.5 * gdot * dmu;\n\n   return dmu_dgdot;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FECrossFluid::BulkViscosity(FEMaterialPoint& pt)\n{\n    return 2*ShearViscosity(pt)/3.;\n}\n"
  },
  {
    "path": "FEBioFluid/FECrossFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Cross fluid\n\nclass FEBIOFLUID_API FECrossFluid :\tpublic FEViscousFluid\n{\npublic:\n    //! constructor\n    FECrossFluid(FEModel* pfem);\n    \n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    \n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double\tm_mu0;\t\t//!< shear viscosity at zero shear rate\n    double\tm_mui;\t\t//!< shear viscosity at infinite shear rate\n    double  m_lam;      //!< time constant\n    double  m_m;        //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEElasticFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticFluid.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n#include \"FEThermoFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FEElasticFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    double d = 1e-6;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef+d;\n    ft->m_T = tf.m_T;\n    FEMaterialPoint tmp(ft);\n    double pp = Pressure(tmp);\n    fp->m_ef = pf.m_ef-d;\n    double pm = Pressure(tmp);\n    delete ft;\n    double dpJ = (pp - pm)/(2*d);\n    return dpJ;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FEElasticFluid::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    double d = 1e-6;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef+d;\n    ft->m_T = tf.m_T;\n    FEMaterialPoint tmp(ft);\n    double dpp = Tangent_Strain(tmp);\n    fp->m_ef = pf.m_ef-d;\n    double dpm = Tangent_Strain(tmp);\n    delete ft;\n    double dpJ2 = (dpp - dpm)/(2*d);\n    return dpJ2;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FEElasticFluid::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    double Tr = GetGlobalConstant(\"T\");\n    double d = 1e-6*Tr;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef;\n    ft->m_T = tf.m_T+d;\n    FEMaterialPoint tmp(ft);\n    double pp = Pressure(tmp);\n    ft->m_T = tf.m_T-d;\n    double pm = Pressure(tmp);\n    delete ft;\n    double dpT = (pp - pm)/(2*d);\n    return dpT;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FEElasticFluid::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    double Tr = GetGlobalConstant(\"T\");\n    double d = 1e-6*Tr;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef;\n    ft->m_T = tf.m_T+d;\n    FEMaterialPoint tmp(ft);\n    double dpp = Tangent_Temperature(tmp);\n    ft->m_T = tf.m_T-d;\n    double dpm = Tangent_Temperature(tmp);\n    delete ft;\n    double dpT2 = (dpp - dpm)/(2*d);\n    return dpT2;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FEElasticFluid::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    double Tr = GetGlobalConstant(\"T\");\n    double dJ = 1e-6;\n    double dT = 1e-6*Tr;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef+dJ;\n    ft->m_T = tf.m_T;\n    FEMaterialPoint tmp(ft);\n    double dpp = Tangent_Temperature(tmp);\n    fp->m_ef = pf.m_ef-dJ;\n    double dpm = Tangent_Temperature(tmp);\n    delete ft;\n    double dpTJ = (dpp - dpm)/(2*dJ);\n    return dpTJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FEElasticFluid::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    double d = 1e-6;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef+d;\n    ft->m_T = tf.m_T;\n    FEMaterialPoint tmp(ft);\n    double cvp = IsochoricSpecificHeatCapacity(tmp);\n    fp->m_ef = pf.m_ef-d;\n    double cvm = IsochoricSpecificHeatCapacity(tmp);\n    delete ft;\n    double dcvJ = (cvp - cvm)/(2*d);\n    return dcvJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FEElasticFluid::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    double Tr = GetGlobalConstant(\"T\");\n    double d = 1e-6*Tr;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef;\n    ft->m_T = tf.m_T+d;\n    FEMaterialPoint tmp(ft);\n    double cvp = IsochoricSpecificHeatCapacity(tmp);\n    ft->m_T = tf.m_T-d;\n    double cvm = IsochoricSpecificHeatCapacity(tmp);\n    delete ft;\n    double dcvT = (cvp - cvm)/(2*d);\n    return dcvT;\n}\n\n//-----------------------------------------------------------------------------\n//! specific internal energy\ndouble FEElasticFluid::SpecificInternalEnergy(FEMaterialPoint& mp)\n{\n    double Tr = GetGlobalConstant(\"T\");\n\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double T = tf.m_T + Tr;\n\n    double u = SpecificFreeEnergy(mp) + T*SpecificEntropy(mp);\n    \n    return u;\n}\n\n//-----------------------------------------------------------------------------\n//! specific gauge enthalpy\ndouble FEElasticFluid::SpecificGaugeEnthalpy(FEMaterialPoint& mp)\n{\n    FEThermoFluid* pMat = dynamic_cast<FEThermoFluid*>(GetParent());\n    \n    double h = SpecificInternalEnergy(mp) + Pressure(mp)/pMat->Density(mp);\n    \n    return h;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free enthalpy\ndouble FEElasticFluid::SpecificFreeEnthalpy(FEMaterialPoint& mp)\n{\n    FEThermoFluid* pMat = dynamic_cast<FEThermoFluid*>(GetParent());\n    \n    double g = SpecificFreeEnergy(mp) + Pressure(mp)/pMat->Density(mp);\n    \n    return g;\n}\n\n//-----------------------------------------------------------------------------\n//! pressure from state variables\ndouble FEElasticFluid::Pressure(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double p = Pressure(tmp);\n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! dp/dJ\ndouble FEElasticFluid::Tangent_Strain(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double dpJ = Tangent_Strain(tmp);\n    return dpJ;\n}\n\n//-----------------------------------------------------------------------------\n//! dp/dT\ndouble FEElasticFluid::Tangent_Temperature(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double dpT = Tangent_Temperature(tmp);\n    return dpT;\n}\n\n//-----------------------------------------------------------------------------\n//! d2p/dJ2\ndouble FEElasticFluid::Tangent_Strain_Strain(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double dpJ2 = Tangent_Strain_Strain(tmp);\n    return dpJ2;\n}\n\n//-----------------------------------------------------------------------------\n//! d2p/dJdT\ndouble FEElasticFluid::Tangent_Strain_Temperature(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double dpJT = Tangent_Strain_Temperature(tmp);\n    return dpJT;\n}\n\n//-----------------------------------------------------------------------------\n//! d2p/dT2\ndouble FEElasticFluid::Tangent_Temperature_Temperature(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double dpT2 = Tangent_Temperature_Temperature(tmp);\n    return dpT2;\n}\n"
  },
  {
    "path": "FEBioFluid/FEElasticFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for the viscous part of the fluid response.\n//! These materials provide the viscous stress and its tangents.\n//!\nclass FEBIOFLUID_API FEElasticFluid : public FEMaterialProperty\n{\npublic:\n    FEElasticFluid(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    virtual ~FEElasticFluid() {}\n    \n    //! gauge pressure\n    virtual double Pressure(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of pressure with respect to strain J\n    virtual double Tangent_Strain(FEMaterialPoint& mp);\n    \n    //! 2nd tangent of pressure with respect to strain J\n    virtual double Tangent_Strain_Strain(FEMaterialPoint& mp);\n    \n    //! tangent of pressure with respect to temperature T\n    virtual double Tangent_Temperature(FEMaterialPoint& mp);\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    virtual double Tangent_Temperature_Temperature(FEMaterialPoint& mp);\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    virtual double Tangent_Strain_Temperature(FEMaterialPoint& mp);\n    \n    //! specific free energy\n    virtual double SpecificFreeEnergy(FEMaterialPoint& mp) = 0;\n    \n    //! specific entropy\n    virtual double SpecificEntropy(FEMaterialPoint& mp) = 0;\n    \n    //! specific strain energy\n    virtual double SpecificStrainEnergy(FEMaterialPoint& mp) = 0;\n    \n    //! isochoric specific heat capacity\n    virtual double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) = 0;\n            \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    virtual double Tangent_cv_Strain(FEMaterialPoint& mp);\n            \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    virtual double Tangent_cv_Temperature(FEMaterialPoint& mp);\n\n    //! isobaric specific heat capacity\n    virtual double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) = 0;\n            \n    //! calculate dilatation for given (effective) pressure and temperature\n    virtual bool Dilatation(const double T, const double p, double& e) = 0;\n    \n    //! calculate fluid pressure and its derivatives from state variables\n    double Pressure(const double ef, const double T);\n    double Tangent_Strain(const double ef, const double T);\n    double Tangent_Temperature(const double ef, const double T);\n    double Tangent_Strain_Strain(const double ef, const double T);\n    double Tangent_Strain_Temperature(const double ef, const double T);\n    double Tangent_Temperature_Temperature(const double ef, const double T);\n\npublic:\n    //! specific internal energy\n    double SpecificInternalEnergy(FEMaterialPoint& mp);\n    \n    //! specific gauge enthalpy\n    double SpecificGaugeEnthalpy(FEMaterialPoint& mp);\n    \n    //! specific free enthalpy\n    double SpecificFreeEnthalpy(FEMaterialPoint& mp);\n\n    FECORE_BASE_CLASS(FEElasticFluid)\n};\n"
  },
  {
    "path": "FEBioFluid/FEFSIErosionVolumeRatio.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFSIErosionVolumeRatio.h\"\n#include \"FEFluidFSIDomain3D.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEMesh.h>\n#include <FECore/FEDomain.h>\n#include <FECore/log.h>\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include <FECore/FELinearConstraintManager.h>\n#include <algorithm>\n\nBEGIN_FECORE_CLASS(FEFSIErosionVolumeRatio, FEMeshAdaptor)\nADD_PARAMETER(m_minJ, FE_RANGE_GREATER(0.0), \"min_volume_ratio\");\nADD_PARAMETER(m_maxElems, \"max_elems\");\nADD_PARAMETER(m_maxIters, \"max_iters\");\nADD_PARAMETER(m_metric  , \"metric\");\nEND_FECORE_CLASS();\n\nFEFSIErosionVolumeRatio::FEFSIErosionVolumeRatio(FEModel* fem) : FEMeshAdaptor(fem)\n{\n    m_minJ = 0.0;\n    m_maxElems = 0;\n    m_maxIters = -1;\n    m_metric = 0;\n}\n\nbool FEFSIErosionVolumeRatio::Apply(int iteration)\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    if ((m_maxIters >= 0) && (iteration >= m_maxIters))\n    {\n        feLog(\"Max iterations reached.\");\n        return false;\n    }\n    \n    int deactiveElems = 0;\n    int activeElems = 0;\n    \n    map<int, bool> melem;\n    for (int i = 0; i < mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n        if (fsidom) {\n            int NE = dom.Elements();\n            for (int j = 0; j < NE; ++j)\n            {\n                FEElement& el = dom.ElementRef(j);\n                if (el.isActive())\n                {\n                    bool bdeactivate = false;\n                    int nint = el.GaussPoints();\n                    double elemVal = 0;\n                    for (int n = 0; n < nint; ++n)\n                    {\n                        FEMaterialPoint* mp = el.GetMaterialPoint(n);\n                        FEElasticMaterialPoint* ep = mp->ExtractData<FEElasticMaterialPoint>();\n                        if (ep)\n                        {\n                            mat3ds C = ep->RightCauchyGreen();\n                            \n                            switch (m_metric)\n                            {\n                                case 0: elemVal = ep->m_J; break;\n                                case 1:\n                                {\n                                    double lam[3];\n                                    C.eigen(lam);\n                                    elemVal = sqrt(lam[2]);\n                                }\n                                    break;\n                                default:\n                                    break;\n                            }\n\n                            if (elemVal <= m_minJ)\n                            {\n                                bdeactivate = true;\n                                break;\n                            }\n                        }\n                    }\n                    \n                    if (bdeactivate)\n                    {\n                        melem[el.GetID()] = true;\n                        deactiveElems++;\n                    }\n                }\n                // if an element is inactive, check whether we need to activate it again\n                else\n                {\n                    // nodal coordinates\n                    const int NELN = FEElement::MAX_NODES;\n                    vec3d r[NELN];\n                    FESolidElement* sel = dynamic_cast<FESolidElement*>(&el);\n                    if (sel) {\n                        FEFluidFSIDomain3D* fsi3D = dynamic_cast<FEFluidFSIDomain3D*>(fsidom);\n                        fsi3D->GetCurrentNodalCoordinates(*sel, r);\n                        int nint = el.GaussPoints();\n                        double minJ = 1;\n                        for (int n = 0; n < nint; ++n)\n                        {\n                            FEMaterialPoint* mp = el.GetMaterialPoint(n);\n                            FEElasticMaterialPoint* ep = mp->ExtractData<FEElasticMaterialPoint>();\n                            \n                            // get the deformation gradient and determinant at intermediate time\n                            double Jt;\n                            mat3d Ft, Fp;\n                            Jt = fsi3D->defgrad(*sel, Ft, n, r);\n                            ep->m_J = Jt;\n                            \n                            ep->m_F = Ft;\n\n                            mat3ds C = ep->RightCauchyGreen();\n                            \n                            switch (m_metric)\n                            {\n                                case 0: minJ = min(ep->m_J,minJ); break;\n                                case 1:\n                                {\n                                    double lam[3];\n                                    C.eigen(lam);\n                                    minJ = min(sqrt(lam[2]),minJ);\n                                }\n                                    break;\n                                default:\n                                    break;\n                            }\n                        }\n                        \n                        if (minJ > m_minJ) {\n                            melem[el.GetID()] = false;\n                            activeElems++;\n                        }\n                    }\n                }\n            }\n        }\n    }\n    if ((deactiveElems == 0) && (activeElems == 0))\n    {\n        feLog(\"Nothing to do.\");\n        return false;\n    }\n    \n    int melems = melem.size();\n    if (melems > m_maxElems) melems = m_maxElems;\n    int nel = 0;\n    for (std::map<int,bool>::iterator it=melem.begin(); it!=melem.end(); ++it)\n    {\n        FEElement* pe = mesh.FindElementFromID(it->first); assert(pe);\n        if (it->second) {\n            pe->setInactive();\n            if (++nel > m_maxElems) break;\n        }\n        else {\n            pe->setActive();\n        }\n    }\n    \n    // if any nodes were orphaned, we need to deactivate them as well\n    int NN = mesh.Nodes();\n    vector<int> tag(NN, 0);\n    for (int i = 0; i < mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        int NE = dom.Elements();\n        for (int j = 0; j < NE; ++j)\n        {\n            FEElement& el = dom.ElementRef(j);\n            if (el.isActive())\n            {\n                int neln = el.Nodes();\n                for (int n = 0; n < neln; ++n) tag[el.m_node[n]] = 1;\n            }\n        }\n    }\n    \n    for (int i = 0; i < NN; ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (tag[i] == 0)\n        {\n            node.SetFlags(FENode::EXCLUDE);\n            int ndofs = node.dofs();\n            for (int j = 0; j < ndofs; ++j)\n                node.set_inactive(j);\n        }\n    }\n    \n    // remove any linear constraints of exclude nodes\n    FELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    for (int j = 0; j < LCM.LinearConstraints();)\n    {\n        FELinearConstraint& lc = LCM.LinearConstraint(j);\n        if (mesh.Node(lc.GetParentNode()).HasFlags(FENode::EXCLUDE))\n        {\n            LCM.RemoveLinearConstraint(j);\n        }\n        else ++j;\n    }\n    \n    // also remove any linear constraints that have excluded child nodes\n    for (int j = 0; j < LCM.LinearConstraints();)\n    {\n        FELinearConstraint& lc = LCM.LinearConstraint(j);\n        \n        bool del = false;\n        int n = lc.Size();\n        for (int k = 0; k < n; ++k)\n        {\n            if (mesh.Node(lc.GetChildDof(k).node).HasFlags(FENode::EXCLUDE))\n            {\n                del = true;\n                break;\n            }\n        }\n        if (del) LCM.RemoveLinearConstraint(j); else ++j;\n    }\n    \n    // reactivate the linear constraints\n    LCM.Activate();\n\n//    feLog(\"Deactivate elements: %d\\n\", elems);\n//    return (elems == 0);\n    feLog(\"Deactivate elements: %d\\n\", melems);\n    feLog(\"Reactivate elements: %d\\n\", activeElems);\n    return (melems != 0);\n}\n"
  },
  {
    "path": "FEBioFluid/FEFSIErosionVolumeRatio.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMeshAdaptor.h>\n\nclass FEFSIErosionVolumeRatio : public FEMeshAdaptor\n{\npublic:\n    FEFSIErosionVolumeRatio(FEModel* fem);\n    \n    bool Apply(int iteration) override;\n    \nprivate:\n    double     m_minJ;\n    int        m_maxElems;\n    int        m_maxIters;\n    int        m_metric;\n    \n    DECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidAngularVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedFluidAngularVelocity.h\"\n\nBEGIN_FECORE_CLASS(FEFixedFluidAngularVelocity, FEFixedBC)\n    ADD_PARAMETER(m_dof[0], \"gx_dof\")->setLongName(\"x-angular-velocity\");\n    ADD_PARAMETER(m_dof[1], \"gy_dof\")->setLongName(\"y-angular-velocity\");\n    ADD_PARAMETER(m_dof[2], \"gz_dof\")->setLongName(\"z-angular-velocity\");\nEND_FECORE_CLASS();\n\nFEFixedFluidAngularVelocity::FEFixedFluidAngularVelocity(FEModel* fem) : FEFixedBC(fem)\n{\n    m_dof[0] = false;\n    m_dof[1] = false;\n    m_dof[2] = false;\n}\n\nbool FEFixedFluidAngularVelocity::Init()\n{\n    vector<int> dofs;\n    if (m_dof[0]) dofs.push_back(GetDOFIndex(\"gx\"));\n    if (m_dof[1]) dofs.push_back(GetDOFIndex(\"gy\"));\n    if (m_dof[2]) dofs.push_back(GetDOFIndex(\"gz\"));\n    SetDOFList(dofs);\n    \n    return FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidAngularVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedFluidAngularVelocity : public FEFixedBC\n{\npublic:\n    FEFixedFluidAngularVelocity(FEModel* fem);\n    \n    bool Init() override;\n    \nprivate:\n    bool    m_dof[3];\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidDilatation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedFluidDilatation.h\"\n\nBEGIN_FECORE_CLASS(FEFixedFluidDilatation, FEFixedBC)\nEND_FECORE_CLASS();\n\nFEFixedFluidDilatation::FEFixedFluidDilatation(FEModel* fem) : FEFixedBC(fem)\n{\n\n}\n\nbool FEFixedFluidDilatation::Init()\n{\n\tSetDOFList(GetDOFIndex(\"ef\"));\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidDilatation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedFluidDilatation : public FEFixedBC\n{\npublic:\n\tFEFixedFluidDilatation(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidTemperature.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedFluidTemperature.h\"\n\nBEGIN_FECORE_CLASS(FEFixedFluidTemperature, FEFixedBC)\nEND_FECORE_CLASS();\n\nFEFixedFluidTemperature::FEFixedFluidTemperature(FEModel* fem) : FEFixedBC(fem)\n{\n\n}\n\nbool FEFixedFluidTemperature::Init()\n{\n\tSetDOFList(GetDOFIndex(\"T\"));\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidTemperature.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedFluidTemperature : public FEFixedBC\n{\npublic:\n    FEFixedFluidTemperature(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedFluidVelocity.h\"\n\nBEGIN_FECORE_CLASS(FEFixedFluidVelocity, FEFixedBC)\n\tADD_PARAMETER(m_dof[0], \"wx_dof\")->setLongName(\"x-velocity\");\n\tADD_PARAMETER(m_dof[1], \"wy_dof\")->setLongName(\"y-velocity\");\n\tADD_PARAMETER(m_dof[2], \"wz_dof\")->setLongName(\"z-velocity\");\nEND_FECORE_CLASS();\n\nFEFixedFluidVelocity::FEFixedFluidVelocity(FEModel* fem) : FEFixedBC(fem)\n{\n\tm_dof[0] = false;\n\tm_dof[1] = false;\n\tm_dof[2] = false;\n}\n\nbool FEFixedFluidVelocity::Init()\n{\n\tvector<int> dofs;\n\tif (m_dof[0]) dofs.push_back(GetDOFIndex(\"wx\"));\n\tif (m_dof[1]) dofs.push_back(GetDOFIndex(\"wy\"));\n\tif (m_dof[2]) dofs.push_back(GetDOFIndex(\"wz\"));\n\tSetDOFList(dofs);\n\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEFixedFluidVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedFluidVelocity : public FEFixedBC\n{\npublic:\n\tFEFixedFluidVelocity(FEModel* fem);\n\n\tbool Init() override;\n\nprivate:\n\tbool\tm_dof[3];\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEFluid.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n#include \"FELinearElasticFluid.h\"\n#include \"FENonlinearElasticFluid.h\"\n#include \"FELogNonlinearElasticFluid.h\"\n#include \"FEIdealGasIsentropic.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFluid, FEFluidMaterial)\n\n\t// material parameters\n    ADD_PARAMETER(m_k   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\")->setUnits(UNIT_PRESSURE);\n    ADD_PROPERTY(m_pElastic, \"elastic\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFluid\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluid constructor\n\nFEFluid::FEFluid(FEModel* pfem) : FEFluidMaterial(pfem)\n{ \n    m_k = 0;\n    m_pElastic = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! FEFluid initialization\nbool FEFluid::Init()\n{\n    m_Tr = GetGlobalConstant(\"T\");\n    if (m_pElastic == nullptr) {\n        m_pElastic = fecore_alloc(FELinearElasticFluid, GetFEModel());\n    }\n    FELinearElasticFluid* pLN = dynamic_cast<FELinearElasticFluid*>(m_pElastic);\n    FENonlinearElasticFluid* pNL = dynamic_cast<FENonlinearElasticFluid*>(m_pElastic);\n    FELogNonlinearElasticFluid* pLNL = dynamic_cast<FELogNonlinearElasticFluid*>(m_pElastic);\n    FEIdealGasIsentropic* pIGH = dynamic_cast<FEIdealGasIsentropic*>(m_pElastic);\n    if (pLN) {\n        pLN->m_k = m_k;\n        pLN->m_rhor = m_rhor;\n        return pLN->Init();\n    }\n    else if (pNL) {\n        pNL->m_k = m_k;\n        pNL->m_rhor = m_rhor;\n        return pNL->Init();\n    }\n    else if (pLNL) {\n        pLNL->m_k = m_k;\n        pLNL->m_rhor = m_rhor;\n        return pLNL->Init();\n    }\n    else if (pIGH) {\n        pIGH->m_k = m_k;\n        pIGH->m_rhor = m_rhor;\n        return pIGH->Init();\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluid::Serialize(DumpStream& ar)\n{\n    FEFluidMaterial::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! returns a pointer to a new material point object\nFEMaterialPointData* FEFluid::CreateMaterialPointData()\n{\n\treturn new FEFluidMaterialPoint();\n}\n\n//-----------------------------------------------------------------------------\n//! bulk modulus\ndouble FEFluid::BulkModulus(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n    return -(vt.m_ef+1)*Tangent_Pressure_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure\ndouble FEFluid::Pressure(FEMaterialPoint& mp)\n{\n    return m_pElastic->Pressure(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure from dilatation\ndouble FEFluid::Pressure(const double e, const double T)\n{\n    return m_pElastic->Pressure(e, T);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFluid::Tangent_Pressure_Strain(FEMaterialPoint& mp)\n{\n    return m_pElastic->Tangent_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFluid::Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp)\n{\n    return m_pElastic->Tangent_Strain_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a fluid material is the sum of the fluid pressure\n//! and the viscous stress.\n\nmat3ds FEFluid::Stress(FEMaterialPoint& mp)\n{\n\t// calculate solid material stress\n\tmat3ds s = GetViscous()->Stress(mp);\n    \n    double p = Pressure(mp);\n\t\n\t// add fluid pressure\n\ts.xx() -= p;\n\ts.yy() -= p;\n\ts.zz() -= p;\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent of stress with respect to strain J of a fluid material is the\n//! sum of the tangent of the fluid pressure and that of the viscous stress.\n\nmat3ds FEFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    // get tangent of viscous stress\n    mat3ds sJ = GetViscous()->Tangent_Strain(mp);\n    \n    // add tangent of fluid pressure\n    double dp = Tangent_Pressure_Strain(mp);\n    sJ.xx() -= dp;\n    sJ.yy() -= dp;\n    sJ.zz() -= dp;\n    \n    return sJ;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density (per reference volume)\ndouble FEFluid::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double sed = m_k*(fp.m_ef-log(fp.m_ef+1));\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! invert pressure-dilatation relation\nbool FEFluid::Dilatation(const double T, const double p, double& e)\n{\n    e = -p/m_k;\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEBodyForce.h>\n#include \"FEFluidMaterial.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEViscousFluid.h\"\n#include <FEBioMix/FEBiphasic.h>\n#include \"FEElasticFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid materials.\n\n//! NOTE: This inherits from FEBiphasicInterface in order to override the GetActualFluidPressure, \n//!       which is used in FEReactionRateExpSED and FEReactionRateHuiskes. \n//!       Note sure yet if there is a better alternative.\n\nclass FEBIOFLUID_API FEFluid : public FEFluidMaterial, public FEBiphasicInterface\n{\npublic:\n\tFEFluid(FEModel* pfem);\n\t\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\t\npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! elastic pressure\n    double Pressure(FEMaterialPoint& mp) override;\n    double Pressure(const double e, const double T = 0) override;\n\n    //! tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! bulk modulus\n    double BulkModulus(FEMaterialPoint& mp) override;\n    \n    //! strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    //! evaluate temperature\n    double Temperature(FEMaterialPoint& mp) override { return m_Tr; }\n\n    //! evaluate dilatation from effective pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n    \n    //! return elastic fluid\n    FEElasticFluid* GetElastic() { return m_pElastic; }\n    \nprivate: // material properties\n    FEElasticFluid*             m_pElastic;     //!< pointer to elastic part of fluid material\n    \n\npublic: // from FEBiphasicInterface\n    double GetActualFluidPressure(const FEMaterialPoint& mp) override {\n        const FEFluidMaterialPoint* pt = (mp.ExtractData<FEFluidMaterialPoint>());\n        return pt->m_pf;\n    }\n    \npublic:\n    double      m_k;        //!< bulk modulus at J=1\n    double      m_Tr;       //!< ambient temperature\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEFluidAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEFluidAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFEFluidAnalysis::FEFluidAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEFluidAnalysis : public FEAnalysis\n{\npublic:\n\tenum FluidAnalysisType {\n\t\tSTEADY_STATE,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFEFluidAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidCOBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidCOBC.h\"\n#include \"FEFluid.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidCOBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_Ra, \"Ra\")->setLongName(\"arterial resistance\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_Ca, \"Ca\")->setLongName(\"arterial compliance\")->setUnits(\"L^5/F\");\n    ADD_PARAMETER(m_Ram,\"Ra-micro\")->setLongName(\"microvascular arterial resistance\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_Cim,\"Cim\")->setLongName(\"myocardial compliance\")->setUnits(\"L^5/F\");\n    ADD_PARAMETER(m_Rv, \"Rv\")->setLongName(\"ventricular resistance\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_Rvm,\"Rv-micro\")->setLongName(\"microvascular ventricular resistance\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_p0, \"initial_pressure\")->setUnits(UNIT_PRESSURE);\n//    ADD_PARAMETER(m_pd, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\n\n    ADD_PROPERTY(m_Pmi,\"Pmi\",FEProperty::Optional)->SetLongName(\"mycoardial pressure\");\n    ADD_PROPERTY(m_PRA,\"PRA\",FEProperty::Optional)->SetLongName(\"right atrium pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidCOBC::FEFluidCOBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_Ra = m_Ram = m_Rv = m_Rvm = 0.0;\n    m_Ca = m_Cim = 0.0;\n    m_pfluid = nullptr;\n    m_psurf = nullptr;\n    m_p0 = 0;\n    m_pd = 0.0;\n    m_e = 0.0;\n    m_Pmi = nullptr;\n    m_PRA = nullptr;\n }\n\n//-----------------------------------------------------------------------------\n//! initialize\n//! TODO: Generalize to include the initial conditions\nbool FEFluidCOBC::Init()\n{\n    m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    \n    m_psurf = GetSurface();\n    \n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n    if (m_pfluid == nullptr) return false;\n    \n    m_pn = m_pp = m_ppp = m_p0;\n    m_q = m_qp = m_qpp = 0;\n    m_tp = m_tpp = 0;\n\n    if (m_Pmi) { if (!m_Pmi->Init()) return false; }\n    if (m_PRA) { if (!m_PRA->Init()) return false; }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidCOBC::UpdateDilatation()\n{\n\t// Check if we started a new time, if so, update variables\n\tFETimeInfo& timeInfo = GetFEModel()->GetTime();\n\tdouble time = timeInfo.currentTime;\n\tint iter = timeInfo.currentIteration;\n\tdouble dt = timeInfo.timeIncrement;\n    if (dt == 0) return;\n\tif ((time > m_tp) && (iter == 0)) {\n        m_ppp = m_pp;\n\t\tm_pp = m_pn;\n        m_qpp = m_qp;\n\t\tm_qp = m_q;\n\t\tm_pdp = m_pdn;\n        m_tpp = m_tp;\n\t\tm_tp = time;\n\t}\n\n\t// evaluate the flow rate at the current time\n\tm_q = FlowRate();\n\tm_pdn = m_pd;\n    \n    double Rve   = m_Rv + m_Rvm;\n    double Rae   = m_Ram + Rve;\n    double Rt    = m_Ra + Rae;\n    double tauvi = Rve*m_Cim;\n    double taua  = m_Ram*m_Ca;\n    double tauae = Rae*m_Ca;\n    \n    double dtp = m_tp - m_tpp;\n    \n    double c0 = tauvi*taua/(dt*dt);\n    double d0 = (dtp > 0) ? tauvi*taua/(dt*dtp) : 0;\n    double c1 = m_Ra*c0;\n    double d1 = m_Ra*d0;\n    double d2 = (tauvi+tauae)/dt;\n    double c3 = tauvi/dt;\n    double c2 = m_Ra*d2 + m_Ram*c3;\n    double a = c1 + c2 + Rt;\n    double b = (c1+c2+d1)*m_qp;\n    b -= (dtp > 0) ?  d1*m_qpp : 0;\n    double c = c0 + d2 + 1;\n    double d = (c0 + d0 + d2)*m_pp;\n    d -= (dtp > 0) ?  d0*m_ppp : 0;\n    if (m_PRA) d += (m_PRA) ? m_PRA->value(time) : 0;\n    if (m_Pmi) d += tauvi*m_Pmi->derive(time);\n\n\t// calculate the RCR pressure\n    m_pn = (a*m_q + d - b)/c;\n\n\t// calculate the dilatation\n\tm_e = 0.0;\n\tbool good = m_pfluid->Dilatation(0, m_pn, m_e);\n\tassert(good);\n}\n\nvoid FEFluidCOBC::UpdateModel() { Update(); }\n\nvoid FEFluidCOBC::Update()\n{\n\tUpdateDilatation();\n\n    // the base class handles mapping the values to the nodal dofs\n    FEPrescribedSurface::Update();\n\n\t// TODO: Is this necessary?\n\tGetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at current time\ndouble FEFluidCOBC::FlowRate()\n{\n    double Q = 0;\n    \n    const FETimeInfo& tp = GetTimeInfo();\n\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->Node(el.m_lnode[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n    \n    return Q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidCOBC::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::PrepStep(ui, brel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidCOBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e;\n\tFENode& node = GetMesh().Node(m_nodeList[nodelid]);\n\tnode.set(m_dofEF, m_e);\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidCOBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidCOBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_pn & m_pp & m_pp & m_q & m_qp & m_qpp & m_tp & m_tpp & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n    ar & m_psurf;\n    ar & m_Pmi & m_PRA;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidCOBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/FEFunction1D.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidCOBC is a fluid surface load that implements a coronary outflow boundary condition\n//!\nclass FEBIOFLUID_API FEFluidCOBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidCOBC(FEModel* pfem);\n    \n    //! set the dilatation\n    void Update() override;\n    void UpdateModel() override;\n    \n    //! evaluate flow rate\n    double FlowRate();\n    \n    //! initialize\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n\tvoid PrepStep(std::vector<double>& ui, bool brel) override;\n\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n\t//! set the dilatation\n\tvoid UpdateDilatation();\n\nprivate:\n    double          m_Ra;       //!< arterial resistance\n    double          m_Ca;       //!< arterial compliance\n    double          m_Ram;      //!< microvascular arterial resistance\n    double          m_Cim;      //!< myocardial compliance\n    double          m_Rv;       //!< ventricular resistance\n    double          m_Rvm;      //!< microvascular ventricular resistance\n    double          m_p0;       //!< initial fluid pressure\n    FEFunction1D*   m_Pmi;      //!< myocardial pressure\n    FEFunction1D*   m_PRA;      //!< right atrium pressure\n    double          m_pd;       //!< downstream pressure\n    \nprivate:\n    double              m_pn;   //!< fluid pressure at current time point\n    double              m_pp;   //!< fluid pressure at previous time point\n    double              m_ppp;  //!< fluid pressure at 2nd-previous time point\n    double              m_q;    //!< flow rate at current time point\n    double              m_qp;   //!< flow rate at previous time point\n    double              m_qpp;  //!< flow rate at 2nd-previous time point\n    double              m_pdn;  //!< downstream fluid pressure at current time point\n    double              m_pdp;  //!< downstream fluid pressure at previous time point\n    double              m_tp;   //!< previous time\n    double              m_tpp;  //!< 2nd-previous time\n    double              m_e;\n    FEFluidMaterial*    m_pfluid;   //!< pointer to fluid\n    FESurface*          m_psurf;    //!< pointer to surface\n    \n    FEDofList   m_dofW;\n    int         m_dofEF;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidConstantConductivity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n//\n//  FEFluidConstantConductivity.cpp\n//  FEBioFluid\n//\n//  Created by Gerard Ateshian on 2/28/20.\n//  Copyright © 2020 febio.org. All rights reserved.\n//\n\n#include \"FEFluidConstantConductivity.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidConstantConductivity, FEFluidThermalConductivity)\n\n    // material parameters\n    ADD_PARAMETER(m_K   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"K\")->setUnits(\"W/L.T\");\n\nEND_FECORE_CLASS();\n\n"
  },
  {
    "path": "FEBioFluid/FEFluidConstantConductivity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluidThermalConductivity.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid thermal conductivity materials.\n\nclass FEBIOFLUID_API FEFluidConstantConductivity : public FEFluidThermalConductivity\n{\npublic:\n    FEFluidConstantConductivity(FEModel* pfem) : FEFluidThermalConductivity(pfem) {}\n    virtual ~FEFluidConstantConductivity() {}\n    \npublic:\n    //! calculate thermal conductivity at material point\n    double ThermalConductivity(FEMaterialPoint& pt) override { return m_K(pt); }\n    \n    //! tangent of thermal conductivity with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override { return 0; }\n    \n    //! tangent of thermal conductivity with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override { return 0; };\n\npublic:\n    FEParamDouble   m_K;    //!< thermal conductivity\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEFluidDomain::FEFluidDomain(FEModel* pfem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"febiofluid_api.h\"\n\nclass FEModel;\nclass FELinearSystem;\nclass FEBodyForce;\nclass FEGlobalVector;\nclass FETimeInfo;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for fluid domains.\n\n//! An fluid domain is used by the fluid mechanics solver.\n//! This interface defines the functions that have to be implemented by a\n//! fluid domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOFLUID_API FEFluidDomain\n{\npublic:\n    FEFluidDomain(FEModel* pfem);\n    virtual ~FEFluidDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! calculate the internal forces\n    virtual void InternalForces(FEGlobalVector& R) = 0;\n    \n    //! Calculate the body force vector\n    virtual void BodyForce(FEGlobalVector& R, FEBodyForce& bf) = 0;\n    \n    //! calculate the interial forces (for dynamic problems)\n    virtual void InertialForces(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! Calculate global stiffness matrix (only contribution from internal force derivative)\n    //! \\todo maybe I should rename this the InternalStiffness matrix?\n    virtual void StiffnessMatrix (FELinearSystem& LS) = 0;\n    \n    //! Calculate stiffness contribution of body forces\n    virtual void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) = 0;\n    \n    //! calculate the mass matrix (for dynamic problems)\n    virtual void MassMatrix(FELinearSystem& LS) = 0;\n    \n    //! transient analysis\n    void SetTransientAnalysis() { m_btrans = true; }\n    void SetSteadyStateAnalysis() { m_btrans = false; }\n    \nprotected:\n    bool        m_btrans;   // flag for transient (true) or steady-state (false) analysis\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidDomain3D.h\"\n#include \"FEFluidSolver.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include \"FEBioFluid.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEFluidDomain3D::FEFluidDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEFluidDomain(pfem), m_dofW(pfem), m_dofAW(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n\n\tif (pfem)\n\t{\n\t\tm_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n\t\tm_dofAW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_ACCELERATION));\n\n\t\tm_dofEF = pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n\t\tm_dofAEF = pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION_TDERIV), 0);\n\n\t\tFEDofList dofs(pfem);\n\t\tdofs.AddDofs(m_dofW);\n\t\tdofs.AddDof(m_dofEF);\n\t\tm_dof = dofs;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEFluidDomain3D& FEFluidDomain3D::operator = (FEFluidDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n//! serialize data to archive\nvoid FEFluidDomain3D::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofW & m_dofAW & m_dof;\n    ar & m_dofEF & m_dofAEF;\n    ar & m_pMat;\n}\n\n//-----------------------------------------------------------------------------\n// get total dof list\nconst FEDofList& FEFluidDomain3D::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEFluidDomain3D::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEFluid*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEFluidDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], r0, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n            pt.m_r0 = el.Evaluate(x0, j);\n            mp.m_rt = mp.m_r0;\n\n            if (pt.m_ef <= -1) {\n                throw NegativeJacobianDetected();\n            }\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 4*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEFluidDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    mat3ds sv;\n    vec3d gradp;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*\tgw = el.GaussWeights();\n\n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        \n        // calculate the jacobian\n        detJ = invjac0(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        sv = m_pMat->GetViscous()->Stress(mp);\n        // get the gradient of the elastic pressure\n        gradp = pt.m_gradef*m_pMat->Tangent_Pressure_Strain(mp);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jdot/J\n        double dJoJ = pt.m_efdot/(pt.m_ef+1);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fs = sv*gradN[i] + gradp*H[i];\n            double fJ = dJoJ*H[i] + gradN[i]*pt.m_vft;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[4*i  ] -= fs.x*detJ;\n            fe[4*i+1] -= fs.y*detJ;\n            fe[4*i+2] -= fs.z*detJ;\n            fe[4*i+3] -= fJ*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 4*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEFluidDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double dens = m_pMat->Density(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = detJ0(el, n)*gw[n];\n        \n        // get the force\n        f = BF.force(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[4*i  ] -= H[i]*dens*f.x*detJ;\n            fe[4*i+1] -= H[i]*dens*f.y*detJ;\n            fe[4*i+2] -= H[i]*dens*f.z*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEFluidDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    int ndof = ke.columns()/neln;\n    \n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f, k;\n    mat3d K, Kvv;\n    \n    // gradient of shape functions\n    vec3d gradN;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n\n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n]*tp.alphaf;\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Density(mp);\n        \n        // get the force\n        f = BF.force(mp);\n        K = BF.stiffness(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                k = f*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                Kvv = K*(H[i]*H[j]*dens*detJ);\n                ke[ndof*i  ][ndof*j  ] += Kvv(0,0); ke[ndof*i  ][ndof*j+1] += Kvv(0,1); ke[ndof*i  ][ndof*j+2] += Kvv(0,2);\n                ke[ndof*i+1][ndof*j  ] += Kvv(1,0); ke[ndof*i+1][ndof*j+1] += Kvv(1,1); ke[ndof*i+1][ndof*j+2] += Kvv(1,2);\n                ke[ndof*i+2][ndof*j  ] += Kvv(2,0); ke[ndof*i+2][ndof*j+1] += Kvv(2,1); ke[ndof*i+2][ndof*j+2] += Kvv(2,2);\n                ke[ndof*i  ][ndof*j+3] += k.x;\n                ke[ndof*i+1][ndof*j+3] += k.y;\n                ke[ndof*i+2][ndof*j+3] += k.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEFluidDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i4, j, j4, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n\n\tdouble dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf);\n    \n    double *H, *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        // get the tangents\n        mat3ds svJ = m_pMat->GetViscous()->Tangent_Strain(mp);\n        tens4ds cv = m_pMat->Tangent_RateOfDeformation(mp);\n        double dp = m_pMat->Tangent_Pressure_Strain(mp);\n        double d2p = m_pMat->Tangent_Pressure_Strain_Strain(mp);\n        // Jdot/J\n        double dJoJ = pt.m_efdot/Jf;\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i4=0; i<neln; ++i, i4 += 4)\n        {\n            for (j=0, j4 = 0; j<neln; ++j, j4 += 4)\n            {\n                mat3d Kvv = vdotTdotv(gradN[i], cv, gradN[j]);\n                vec3d kJv = (pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j];\n                vec3d kvJ = (svJ*gradN[i])*H[j] + (gradN[j]*dp+pt.m_gradef*(H[j]*d2p))*H[i];\n                double kJJ = (H[j]*(ksi/dt - dJoJ) + gradN[j]*pt.m_vft)*H[i]/Jf;\n                \n                ke[i4  ][j4  ] += Kvv(0,0)*detJ;\n                ke[i4  ][j4+1] += Kvv(0,1)*detJ;\n                ke[i4  ][j4+2] += Kvv(0,2)*detJ;\n                ke[i4  ][j4+3] += kvJ.x*detJ;\n                \n                ke[i4+1][j4  ] += Kvv(1,0)*detJ;\n                ke[i4+1][j4+1] += Kvv(1,1)*detJ;\n                ke[i4+1][j4+2] += Kvv(1,2)*detJ;\n                ke[i4+1][j4+3] += kvJ.y*detJ;\n                \n                ke[i4+2][j4  ] += Kvv(2,0)*detJ;\n                ke[i4+2][j4+1] += Kvv(2,1)*detJ;\n                ke[i4+2][j4+2] += Kvv(2,2)*detJ;\n                ke[i4+2][j4+3] += kvJ.z*detJ;\n                \n                ke[i4+3][j4  ] += kJv.x*detJ;\n                ke[i4+3][j4+1] += kJv.y*detJ;\n                ke[i4+3][j4+2] += kJv.z*detJ;\n                ke[i4+3][j4+3] += kJJ*detJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 4*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate material stiffness\n        ElementStiffness(el, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n\n#pragma omp parallel for shared(NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n\t\tFEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 4*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementMassMatrix(el, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 4*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEFluidDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i4, j, j4, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n\n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf)*m_btrans;\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        \n        double dens = m_pMat->Density(mp);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i4=0; i<neln; ++i, i4 += 4)\n        {\n            for (j=0, j4 = 0; j<neln; ++j, j4 += 4)\n            {\n                mat3d Mv = ((mat3dd(ksi/dt) + pt.m_Lf)*H[j] + mat3dd(gradN[j]*pt.m_vft))*(H[i]*dens*detJ);\n                vec3d mJ = pt.m_aft*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                \n                ke[i4  ][j4  ] += Mv(0,0);\n                ke[i4  ][j4+1] += Mv(0,1);\n                ke[i4  ][j4+2] += Mv(0,2);\n                ke[i4  ][j4+3] += mJ.x;\n                \n                ke[i4+1][j4  ] += Mv(1,0);\n                ke[i4+1][j4+1] += Mv(1,1);\n                ke[i4+1][j4+2] += Mv(1,2);\n                ke[i4+1][j4+3] += mJ.y;\n                \n                ke[i4+2][j4  ] += Mv(2,0);\n                ke[i4+2][j4+1] += Mv(2,1);\n                ke[i4+2][j4+2] += Mv(2,2);\n                ke[i4+2][j4+3] += mJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i, tp);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (NegativeJacobian::DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEFluidDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d vt[NELN], vp[NELN];\n    vec3d at[NELN], ap[NELN];\n    double et[NELN], ep[NELN];\n    double aet[NELN], aep[NELN];\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        vt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        vp[j] = node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n        at[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n        ap[j] = node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n        et[j] = node.get(m_dofEF);\n        ep[j] = node.get_prev(m_dofEF);\n        aet[j] = node.get(m_dofAEF);\n        aep[j] = node.get_prev(m_dofAEF);\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        \n        // material point data\n        pt.m_vft = el.Evaluate(vt, n)*alphaf + el.Evaluate(vp, n)*(1-alphaf);\n        pt.m_Lf = gradient(el, vt, n)*alphaf + gradient(el, vp, n)*(1-alphaf);\n        pt.m_aft = pt.m_Lf*pt.m_vft;\n        if (m_btrans) pt.m_aft += el.Evaluate(at, n)*alpham + el.Evaluate(ap, n)*(1-alpham);\n        pt.m_ef = el.Evaluate(et, n)*alphaf + el.Evaluate(ep, n)*(1-alphaf);\n        pt.m_gradef = gradient(el, et, n)*alphaf + gradient(el, ep, n)*(1-alphaf);\n        pt.m_efdot = pt.m_gradef*pt.m_vft;\n        if (m_btrans) pt.m_efdot += el.Evaluate(aet, n)*alpham + el.Evaluate(aep, n)*(1-alpham);\n\n        // calculate the stress at this material point\n        pt.m_sf = m_pMat->Stress(mp);\n        \n        // calculate the fluid pressure\n        pt.m_pf = m_pMat->Pressure(mp);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared(NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 4*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInertialForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        double dens = m_pMat->Density(mp);\n        \n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d f = pt.m_aft*(dens*H[i]);\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[4*i  ] -= f.x*detJ;\n            fe[4*i+1] -= f.y*detJ;\n            fe[4*i+2] -= f.z*detJ;\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEFluidDomain.h\"\n#include \"FEFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEFluidDomain3D : public virtual FESolidDomain, public FEFluidDomain\n{\npublic:\n    //! constructor\n    FEFluidDomain3D(FEModel* pfem);\n    ~FEFluidDomain3D() {}\n    \n    //! assignment operator\n    FEFluidDomain3D& operator = (FEFluidDomain3D& d);\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n\n\t// get total dof list\n\tconst FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n    \n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n    \n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEFluid*\tm_pMat;\n    \nprotected:\n\tFEDofList\tm_dofW;\n\tFEDofList\tm_dofAW;\n\tFEDofList\tm_dof;\n    int\tm_dofEF;\n    int m_dofAEF;\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidDomainFactory.h\"\n#include \"FEFluid.h\"\n#include \"FEFluidDomain.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEDomain2D.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEFluidDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tFEModel* pfem = pmat->GetFEModel();\n\tFE_Element_Class eclass = spec.eclass;\n\tFE_Element_Shape eshape = spec.eshape;\n\tconst char* sztype = 0;\n\n\tFEDomain* pd = nullptr;\n\tif (dynamic_cast<FEFluid*>(pmat))\n\t{\n\t\t// fluid elements\n\t\tif      (eclass==FE_ELEM_SOLID) pd = fecore_new<FESolidDomain>(\"fluid-3D\", pfem);\n        else if (eclass==FE_ELEM_2D   ) pd = fecore_new<FEDomain2D   >(\"fluid-2D\", pfem);\n\t\telse return 0;\n\t}\n\tif (pd) pd->SetMaterial(pmat);\n\treturn pd;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEFluidDomainFactory : public FEDomainFactory\n{\npublic:\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSI.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSI.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidFSI, FEMaterial)\n\t// material properties\n\tADD_PROPERTY(m_pSolid, \"solid\", FEProperty::Required | FEProperty::TopLevel);\n\tADD_PROPERTY(m_pFluid, \"fluid\");\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFSIMaterialPoint\n//============================================================================\nFEFSIMaterialPoint::FEFSIMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEFSIMaterialPoint::Copy()\n{\n    FEFSIMaterialPoint* pt = new FEFSIMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFSIMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_w & m_aw & m_Jdot & m_ss;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFSIMaterialPoint::Init()\n{\n    m_w = m_aw = vec3d(0,0,0);\n    m_Jdot = 0;\n    m_ss.zero();\n    \n\tFEMaterialPointData::Init();\n}\n\n//============================================================================\n// FEFluidFSI\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluidFSI constructor\n\nFEFluidFSI::FEFluidFSI(FEModel* pfem) : FEMaterial(pfem)\n{\n\tm_pSolid = 0;\n\tm_pFluid = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEFluidFSI::CreateMaterialPointData()\n{\n    FEFluidMaterialPoint* fpt = new FEFluidMaterialPoint(m_pSolid->CreateMaterialPointData());\n    return new FEFSIMaterialPoint(fpt);\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FEFluidFSI::Init()\n{\n    m_pFluid->Init();\n    m_pSolid->Init();\n    // set the solid density to zero (required for the solid of a FSI domain)\n    m_pSolid->SetDensity(0.0);\n    \n    return FEMaterial::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSI.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include \"FEFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! FSI material point class.\n//\nclass FEBIOFLUID_API FEFSIMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n    FEFSIMaterialPoint(FEMaterialPointData* pt);\n    \n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n    \n    //! data serialization\n    void Serialize(DumpStream& ar);\n    \n    //! Data initialization\n    void Init();\n    \npublic:\n    // FSI material data\n    vec3d       m_w;      //!< fluid flux relative to solid\n    vec3d       m_aw;     //!< material time derivative of m_wt\n    double      m_Jdot;   //!< time derivative of solid volume ratio\n    mat3ds      m_ss;     //!< solid stress\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for FluidFSI materials.\n\nclass FEBIOFLUID_API FEFluidFSI : public FEMaterial\n{\npublic:\n    FEFluidFSI(FEModel* pfem);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    // Get the elastic component (overridden from FEMaterial)\n    FEElasticMaterial* GetElasticMaterial() { return m_pSolid; }\n    \n    //! performs initialization\n    bool Init() override;\n    \npublic:\n    FEFluid* Fluid() { return m_pFluid; }\n    FEElasticMaterial* Solid() { return m_pSolid; }\n    \nprotected: // material properties\n    FEElasticMaterial*\t\t\tm_pSolid;\t//!< pointer to elastic solid material\n    FEFluid*                    m_pFluid;\t//!< pointer to fluid material\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEFluidFSIAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEFluidFSIAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFEFluidFSIAnalysis::FEFluidFSIAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEFluidFSIAnalysis : public FEAnalysis\n{\npublic:\n\tenum FluidFSIAnalysisType {\n\t\tSTEADY_STATE,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFEFluidFSIAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSIDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEFluidFSIDomain::FEFluidFSIDomain(FEModel* pfem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n\nclass FEModel;\nclass FELinearSystem;\nclass FEBodyForce;\nclass FEGlobalVector;\nclass FETimeInfo;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for fluid-FSI domains.\n\n//! A fluid-FSI domain is used by the fluid-FSI solver.\n//! This interface defines the functions that have to be implemented by a\n//! fluid-FSI domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOFLUID_API FEFluidFSIDomain\n{\npublic:\n    FEFluidFSIDomain(FEModel* pfem);\n    virtual ~FEFluidFSIDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! calculate the internal forces\n    virtual void InternalForces(FEGlobalVector& R) = 0;\n    \n    //! Calculate the body force vector\n    virtual void BodyForce(FEGlobalVector& R, FEBodyForce& bf) = 0;\n    \n    //! calculate the interial forces (for dynamic problems)\n    virtual void InertialForces(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! Calculate global stiffness matrix (only contribution from internal force derivative)\n    //! \\todo maybe I should rename this the InternalStiffness matrix?\n    virtual void StiffnessMatrix   (FELinearSystem& LS) = 0;\n    \n    //! Calculate stiffness contribution of body forces\n    virtual void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) = 0;\n    \n    //! calculate the mass matrix (for dynamic problems)\n    virtual void MassMatrix(FELinearSystem& LS) = 0;\n    \n    //! transient analysis\n    void SetTransientAnalysis() { m_btrans = true; }\n    void SetSteadyStateAnalysis() { m_btrans = false; }\n    \nprotected:\n    bool        m_btrans;   // flag for transient (true) or steady-state (false) analysis\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSIDomain3D.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include \"FEBioFSI.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEFluidFSIDomain3D::FEFluidFSIDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEFluidFSIDomain(pfem), m_dofU(pfem), m_dofV(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n    m_sseps = 0;\n\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::DISPLACEMENT));\n\t\tm_dofV.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::VELOCITY));\n\t\tm_dofW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_VELOCITY));\n\t\tm_dofAW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_ACCELERATION));\n\t\tm_dofSU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RIGID_ROTATION));\n\t\tm_dofEF = pfem->GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION), 0);\n\t\tm_dofAEF = pfem->GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION_TDERIV), 0);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEFluidFSIDomain3D& FEFluidFSIDomain3D::operator = (FEFluidFSIDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// get the total dof\nconst FEDofList& FEFluidFSIDomain3D::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEFluidFSIDomain3D::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEFluidFSI*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::Activate()\n{\n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n            }\n            node.set_active(m_dofW[0]);\n            node.set_active(m_dofW[1]);\n            node.set_active(m_dofW[2]);\n            node.set_active(m_dofEF);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEFluidFSIDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        if (el.isActive())\n        {\n            int neln = el.Nodes();\n            for (int i=0; i<neln; ++i)\n            {\n                x0[i] = m.Node(el.m_node[i]).m_r0;\n                xt[i] = m.Node(el.m_node[i]).m_rt;\n            }\n            \n            int n = el.GaussPoints();\n            for (int j=0; j<n; ++j)\n            {\n                r0 = el.Evaluate(x0, j);\n                rt = el.Evaluate(xt, j);\n                \n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n                FEFSIMaterialPoint& ft = *mp.ExtractData<FEFSIMaterialPoint>();\n                et.m_Wp = et.m_Wt;\n                \n                if ((pt.m_ef <= -1) || (et.m_J <= 0)) {\n                    throw NegativeJacobianDetected();\n                }\n                \n                mp.Update(timeInfo);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEFluidFSIDomain3D::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    lm.resize(N*10);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& node = m_pMesh->Node(el.m_node[i]);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[7*i  ] = id[m_dofU[0]];\n        lm[7*i+1] = id[m_dofU[1]];\n        lm[7*i+2] = id[m_dofU[2]];\n        lm[7*i+3] = id[m_dofW[0]];\n        lm[7*i+4] = id[m_dofW[1]];\n        lm[7*i+5] = id[m_dofW[2]];\n        lm[7*i+6] = id[m_dofEF];\n\n\t\t// rigid rotational dofs\n\t\tlm[7*N + 3*i  ] = id[m_dofR[0]];\n\t\tlm[7*N + 3*i+1] = id[m_dofR[1]];\n\t\tlm[7*N + 3*i+2] = id[m_dofR[2]];\n    }\n    \n    // substitute interface dofs for solid-shell interfaces\n    FESolidElement& sel = static_cast<FESolidElement&>(el);\n    for (int i = 0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(el.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the displacement dofs\n            lm[7*i  ] = id[m_dofSU[0]];\n            lm[7*i+1] = id[m_dofSU[1]];\n            lm[7*i+2] = id[m_dofSU[2]];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            // get the element force vector and initialize it to zero\n            int ndof = 7*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // calculate internal force vector\n            ElementInternalForce(el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEFluidFSIDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    mat3ds sv, ss;\n    vec3d gradp;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*\tgw = el.GaussWeights();\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFSIMaterialPoint& ft = *(mp.ExtractData<FEFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        // calculate the jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        // get the gradient of the elastic pressure\n        gradp = pt.m_gradef*m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        // get the solid stress tensor\n        ss = ft.m_ss;\n\n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jfdot/Jf\n        double dJfoJ = pt.m_efdot/Jf;\n        // Jsdot/Js\n        double dJsoJ = ft.m_Jdot/et.m_J;\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fs = (ss*gradN[i])*detJ;\n            vec3d ff = (sv*gradN[i] + gradp*H[i])*detJ;\n            double fJ = (H[i]*(((dJfoJ - dJsoJ)*dtrans + (pt.m_gradef*ft.m_w)/Jf)) + gradN[i]*ft.m_w)*detJ;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[7*i  ] -= fs.x;\n            fe[7*i+1] -= fs.y;\n            fe[7*i+2] -= fs.z;\n            fe[7*i+3] -= ff.x;\n            fe[7*i+4] -= ff.y;\n            fe[7*i+5] -= ff.z;\n            fe[7*i+6] -= fJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    for (int i=0; i<NE; ++i)\n    {\n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            vector<double> fe;\n            vector<int> lm;\n            \n            // get the element force vector and initialize it to zero\n            int ndof = 7*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // apply body forces\n            ElementBodyForce(BF, el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEFluidFSIDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        detJ = detJt(el, n, tp.alphaf)*gw[n];\n        \n        // get the force\n        f = BF.force(mp)*(dens*detJ);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[7*i+3] -= H[i]*f.x;\n            fe[7*i+4] -= H[i]*f.y;\n            fe[7*i+5] -= H[i]*f.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\n//! For now, we assume that the body force is constant\nvoid FEFluidFSIDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    int ndof = ke.columns()/neln;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    double *H, *Gr, *Gs, *Gt;\n    double* gw = el.GaussWeights();\n    vec3d f, kwJ;\n    mat3d Kwu, Kww, K;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        \n        // calculate the jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        // get the force\n        f = BF.force(mp);\n        K = BF.stiffness(mp);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (int i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                kwJ = f*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                Kwu = (f & gradN[j])*(dens*H[i]*detJ);\n                Kww = K*(H[i]*H[j]*dens*detJ);\n                ke[ndof*i+3][ndof*j  ] += Kwu(0,0); ke[ndof*i+3][ndof*j+1] += Kwu(0,1); ke[ndof*i+3][ndof*j+2] += Kwu(0,2);\n                ke[ndof*i+4][ndof*j  ] += Kwu(1,0); ke[ndof*i+4][ndof*j+1] += Kwu(1,1); ke[ndof*i+4][ndof*j+2] += Kwu(1,2);\n                ke[ndof*i+5][ndof*j  ] += Kwu(2,0); ke[ndof*i+5][ndof*j+1] += Kwu(2,1); ke[ndof*i+5][ndof*j+2] += Kwu(2,2);\n                ke[ndof*i+3][ndof*j+3] += Kww(0,0); ke[ndof*i+3][ndof*j+4] += Kwu(0,1); ke[ndof*i+3][ndof*j+5] += Kwu(0,2);\n                ke[ndof*i+4][ndof*j+3] += Kww(1,0); ke[ndof*i+4][ndof*j+4] += Kwu(1,1); ke[ndof*i+4][ndof*j+5] += Kwu(1,2);\n                ke[ndof*i+5][ndof*j+3] += Kww(2,0); ke[ndof*i+5][ndof*j+4] += Kwu(2,1); ke[ndof*i+5][ndof*j+5] += Kwu(2,2);\n                ke[ndof*i+3][ndof*j+6] += kwJ.x;\n                ke[ndof*i+4][ndof*j+6] += kwJ.y;\n                ke[ndof*i+5][ndof*j+6] += kwJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEFluidFSIDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double dt = tp.timeIncrement;\n    double a = tp.gamma/(tp.beta*dt);\n    double b = tp.alpham/(tp.alphaf*tp.beta*dt*dt);\n    double c = tp.alpham/(tp.alphaf*tp.gamma*dt);\n\n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    double *H, *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFSIMaterialPoint& fpt = *(mp.ExtractData<FEFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        // get the tangents\n        mat3ds ss = m_pMat->Solid()->Stress(mp);\n        tens4ds cs = m_pMat->Solid()->Tangent(mp);\n        mat3ds sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        mat3ds svJ = m_pMat->Fluid()->GetViscous()->Tangent_Strain(mp);\n        tens4ds cv = m_pMat->Fluid()->Tangent_RateOfDeformation(mp);\n        double dp = m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        double d2p = m_pMat->Fluid()->Tangent_Pressure_Strain_Strain(mp);\n        // Jfdot/Jf\n        double dJfoJ = pt.m_efdot/Jf;\n        vec3d gradp = pt.m_gradef*dp;\n        // Jsdot/Js = div(vs)\n        double dJsoJ = fpt.m_Jdot/et.m_J;\n\n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += 7)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += 7)\n            {\n                mat3d M = mat3dd(a*dtrans) - et.m_L;\n                mat3d Kuu = (vdotTdotv(gradN[i], cs, gradN[j])\n                             + mat3dd(gradN[i]*(ss*gradN[j])))*detJ;\n                mat3d Kwu = (vdotTdotv(gradN[i], cv, gradN[j])*M\n                             + sv*((gradN[i] & gradN[j]) - (gradN[j] & gradN[i]))\n                             + ((gradp & gradN[j]) - (gradN[j] & gradp)*H[i]))*detJ;\n                mat3d Kww = vdotTdotv(gradN[i], cv, gradN[j])*detJ;\n                vec3d kwJ = ((svJ*gradN[i])*H[j]\n                             + (gradN[j]*dp+pt.m_gradef*(H[j]*d2p))*H[i])*detJ;\n                vec3d kJu = ((gradN[j]*(dJfoJ - dJsoJ - a)*dtrans\n                              + ((gradN[j] & pt.m_gradef) - (pt.m_gradef & gradN[j]))*fpt.m_w/Jf + et.m_L.transpose()*gradN[j])*H[i]\n                             + ((gradN[j] & gradN[i]) - (gradN[i] & gradN[j]))*fpt.m_w\n                             )*detJ;\n                vec3d kJw = ((pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j])*detJ;\n                double kJJ = (((c - dJfoJ)*dtrans - (pt.m_gradef*fpt.m_w)/Jf)*H[j]\n                              + gradN[j]*fpt.m_w)*H[i]/Jf*detJ;\n                \n                ke[i7  ][j7  ] += Kuu(0,0); ke[i7  ][j7+1] += Kuu(0,1); ke[i7  ][j7+2] += Kuu(0,2);\n                ke[i7+1][j7  ] += Kuu(1,0); ke[i7+1][j7+1] += Kuu(1,1); ke[i7+1][j7+2] += Kuu(1,2);\n                ke[i7+2][j7  ] += Kuu(2,0); ke[i7+2][j7+1] += Kuu(2,1); ke[i7+2][j7+2] += Kuu(2,2);\n                \n                ke[i7+3][j7  ] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7  ] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7  ] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                \n                ke[i7+3][j7+3] += Kww(0,0); ke[i7+3][j7+4] += Kww(0,1); ke[i7+3][j7+5] += Kww(0,2);\n                ke[i7+4][j7+3] += Kww(1,0); ke[i7+4][j7+4] += Kww(1,1); ke[i7+4][j7+5] += Kww(1,2);\n                ke[i7+5][j7+3] += Kww(2,0); ke[i7+5][j7+4] += Kww(2,1); ke[i7+5][j7+5] += Kww(2,2);\n                \n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n                \n                ke[i7+6][j7  ] += kJu.x;\n                ke[i7+6][j7+1] += kJu.y;\n                ke[i7+6][j7+2] += kJu.z;\n\n                ke[i7+6][j7+3] += kJw.x;\n                ke[i7+6][j7+4] += kJw.y;\n                ke[i7+6][j7+5] += kJw.z;\n                \n                ke[i7+6][j7+6] += kJJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            // element stiffness matrix\n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = 7*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate material stiffness\n            ElementStiffness(el, ke);\n            \n            // get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\t\t\tke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n\n\t\t\tFEElementMatrix ke(el);\n\n            // create the element's stiffness matrix\n            int ndof = 7*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate inertial stiffness\n            ElementMassMatrix(el, ke);\n            \n            // get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\t\t\tke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    FEFluidFSI* pme = dynamic_cast<FEFluidFSI*>(GetMaterial()); assert(pme);\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n\n\t\t\t// element stiffness matrix\n\t\t\tFEElementMatrix ke(el);\n\n            // create the element's stiffness matrix\n            int ndof = 7*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate inertial stiffness\n            ElementBodyForceStiffness(bf, el, ke);\n            \n            // get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\t\t\tke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEFluidFSIDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n\n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    double dt = tp.timeIncrement;\n    double a = tp.gamma/(tp.beta*dt);\n    double b = tp.alpham/(tp.alphaf*tp.beta*dt*dt);\n    double c = tp.alpham/(tp.alphaf*tp.gamma*dt);\n\n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFSIMaterialPoint& fpt = *(mp.ExtractData<FEFSIMaterialPoint>());\n        \n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += 7)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += 7)\n            {\n                mat3d Kwu = ((pt.m_aft & gradN[j]) - pt.m_Lf*(fpt.m_w*gradN[j])\n                             + mat3dd((gradN[j]*fpt.m_w)*a*dtrans)\n                             + mat3dd(b*H[j]*dtrans)\n                             )*(H[i]*dens*detJ);\n                mat3d Kww = (mat3dd(c*dtrans*H[j] + gradN[j]*fpt.m_w) + pt.m_Lf*H[j])*(H[i]*dens*detJ);\n                vec3d kwJ = pt.m_aft*(-dens/(pt.m_ef+1)*H[i]*H[j]*detJ);\n                \n                ke[i7+3][j7  ] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7  ] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7  ] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                \n                ke[i7+3][j7+3] += Kww(0,0); ke[i7+3][j7+4] += Kww(0,1); ke[i7+3][j7+5] += Kww(0,2);\n                ke[i7+4][j7+3] += Kww(1,0); ke[i7+4][j7+4] += Kww(1,1); ke[i7+4][j7+5] += Kww(1,2);\n                ke[i7+5][j7+3] += Kww(2,0); ke[i7+5][j7+4] += Kww(2,1); ke[i7+5][j7+5] += Kww(2,2);\n                \n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            FESolidElement& el = Element(i);\n            if (el.isActive())\n            {\n                UpdateElementStress(i, tp);\n            }\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEFluidFSIDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d r0[NELN], r[NELN];\n    vec3d vs[NELN];\n    vec3d a[NELN];\n    vec3d w[NELN];\n    vec3d aw[NELN];\n    double e[NELN];\n    double ae[NELN];\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        r0[j] = node.m_r0;\n        r[j]  = node.m_rt*alphaf + node.m_rp*(1-alphaf);\n        vs[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*alphaf + node.m_vp*(1-alphaf);\n        w[j]  = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-alphaf);\n        e[j]  = node.get(m_dofEF)*alphaf + node.get_prev(m_dofEF)*(1-alphaf);\n        a[j]  = node.m_at*alpham + node.m_ap*(1-alpham);\n        aw[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2])*alpham + node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2])*(1-alpham);\n        ae[j] = node.get(m_dofAEF)*alpham + node.get_prev(m_dofAEF)*(1-alpham);\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n\t\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\tFEFSIMaterialPoint& ft = *(mp.ExtractData<FEFSIMaterialPoint>());\n\n\t\t// elastic material point data\n\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\tmp.m_rt = el.Evaluate(r, n);\n\t\tmat3d Ft, Fp;\n\t\tdouble Jt, Jp;\n\t\tJt = defgrad(el, Ft, n);\n\t\tJp = defgradp(el, Fp, n);\n\t\tept.m_F = Ft*alphaf + Fp*(1 - alphaf);\n\t\tept.m_J = ept.m_F.det();\n\t\tmat3d Fi = ept.m_F.inverse();\n\t\tept.m_L = (Ft - Fp)*Fi*(dtrans / dt);\n\t\tept.m_v = m_btrans ? el.Evaluate(vs, n) : vec3d(0, 0, 0);\n\t\tept.m_a = m_btrans ? el.Evaluate(a, n) : vec3d(0, 0, 0);\n\n\t\t// FSI material point data\n\t\tft.m_w = el.Evaluate(w, n);\n\t\tft.m_Jdot = (Jt - Jp) / dt*dtrans;\n\t\tft.m_aw = el.Evaluate(aw, n)*dtrans;\n\n\t\t// fluid material point data\n\t\tpt.m_efdot = el.Evaluate(ae, n)*dtrans;\n\t\tpt.m_vft = ept.m_v + ft.m_w;\n\t\tmat3d Gradw = Gradient(el, w, n);\n\t\tmat3d Lw = Gradw*Fi;\n\t\tpt.m_Lf = ept.m_L + Lw;\n        pt.m_ef = el.Evaluate(e, n);\n\t\tvec3d Gradef = Gradient(el, e, n);\n\t\tpt.m_gradef = Fi.transpose()*Gradef;\n\n\t\t// fluid acceleration\n\t\tpt.m_aft = ept.m_a + ft.m_aw + pt.m_Lf*ft.m_w;\n\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, tp);\n        \n\t\t// calculate the stresses at this material point\n\t\tpt.m_sf = m_pMat->Fluid()->Stress(mp);\n        ft.m_ss = m_pMat->Solid()->Stress(mp);\n        ept.m_s = pt.m_sf + ft.m_ss;\n\n\t\t// calculate the fluid pressure\n\t\tpt.m_pf = m_pMat->Fluid()->Pressure(pt.m_ef);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            // element force vector\n            vector<double> fe;\n            vector<int> lm;\n            \n            // get the element force vector and initialize it to zero\n            int ndof = 7*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // calculate internal force vector\n            ElementInertialForce(el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        // calculate the jacobian\n        detJ = detJt(el, n, tp.alphaf)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d f = pt.m_aft*(dens*H[i]*detJ);\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[7*i+3] -= f.x;\n            fe[7*i+4] -= f.y;\n            fe[7*i+5] -= f.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSIDomain3D::Serialize(DumpStream& ar)\n{\n\tFESolidDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n\tar & m_sseps;\n    ar & m_dofU & m_dofV & m_dofW & m_dofAW;\n    ar & m_dofSU & m_dofR;\n    ar & m_dof;\n    ar & m_dofEF & m_dofAEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEFluidFSIDomain.h\"\n#include \"FEFluidFSI.h\"\n\n//-----------------------------------------------------------------------------\n//! Fluid-FSI domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEFluidFSIDomain3D : public FESolidDomain, public FEFluidFSIDomain\n{\npublic:\n    //! constructor\n    FEFluidFSIDomain3D(FEModel* pfem);\n    ~FEFluidFSIDomain3D() {}\n    \n    //! assignment operator\n    FEFluidFSIDomain3D& operator = (FEFluidFSIDomain3D& d);\n    \n    //! activate\n    void Activate() override;\n    \n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! Unpack element data\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// get the total dof\n\tconst FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n    \npublic: // overrides from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n    \n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n    \n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEFluidFSI*\tm_pMat;\n    double      m_sseps;\n    \nprotected:\n\tFEDofList\tm_dofU, m_dofV, m_dofW, m_dofAW;\n\tFEDofList\tm_dofSU, m_dofR;\n\tFEDofList\tm_dof;\n    int\tm_dofEF;\n    int m_dofAEF;\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSIDomainFactory.h\"\n#include \"FEBiphasicFSI.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEFluidFSIDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tFEModel* pfem = pmat->GetFEModel();\n\tFE_Element_Class eclass = spec.eclass;\n\tFE_Element_Shape eshape = spec.eshape;\n\n\tFEDomain* pd = nullptr;\n\t\n\tif (dynamic_cast<FEBiphasicFSI*>(pmat))\n\t{\n\t\t// fluid elements\n\t\tif (eclass == FE_ELEM_SOLID) pd = fecore_new<FESolidDomain>(\"biphasic-FSI-3D\", pfem);\n\t\telse return 0;\n\t}\n    else if (dynamic_cast<FEFluidFSI*>(pmat))\n    {\n        // fluid elements\n        if (eclass == FE_ELEM_SOLID) pd = fecore_new<FESolidDomain>(\"fluid-FSI-3D\", pfem);\n        else return 0;\n    }\n\n\tif (pd) pd->SetMaterial(pmat);\n\treturn pd;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSIDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEFluidFSIDomainFactory : public FEDomainFactory\n{\npublic:\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSISolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <assert.h>\n#include \"FEBioFSI.h\"\n#include \"FEFluidFSISolver.h\"\n#include \"FEFluidFSIDomain.h\"\n#include \"FEFluidDomain.h\"\n#include \"FEBiphasicFSIDomain.h\"\n#include <FEBioMech/FEElasticDomain.h>\n#include <FEBioMech/FEPressureLoad.h>\n#include <FEBioMech/FERigidConnector.h>\n#include <FEBioMech/FESlidingElasticInterface.h>\n#include <FEBioMech/FESSIShellDomain.h>\n#include <FEBioMech/FEResidualVector.h>\n#include <FEBioMech/FE3FieldElasticSolidDomain.h>\n#include <FEBioMech/FE3FieldElasticShellDomain.h>\n#include <FEBioMech/FEUncoupledMaterial.h>\n#include <FEBioMech/FEBodyForce.h>\n#include <FEBioMech/FESolidLinearSystem.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/sys.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/DumpStream.h>\n#include \"FEFluidFSIAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEFluidFSISolver, FENewtonSolver)\n\tADD_PARAMETER(m_Dtol , \"dtol\"        );\n\tADD_PARAMETER(m_Vtol , \"vtol\"        );\n\tADD_PARAMETER(m_Ftol , \"ftol\"        );\n    ADD_PARAMETER(m_Etol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n    ADD_PARAMETER(m_Rtol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n    ADD_PARAMETER(m_rhoi , \"rhoi\"        );\n\tADD_PARAMETER(m_pred , \"predictor\"   );\n    ADD_PARAMETER(m_minJf, \"min_volume_ratio\");\n    ADD_PARAMETER(m_order, \"order\"      );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEFluidFSISolver Construction\n//\nFEFluidFSISolver::FEFluidFSISolver(FEModel* pfem) : FENewtonSolver(pfem), m_rigidSolver(pfem), \\\nm_dofU(pfem), m_dofV(pfem), m_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem),m_dofQ(pfem),m_dofRQ(pfem), m_dofVF(pfem),m_dofAF(pfem),m_dofW(pfem), m_dofAW(pfem), m_dofEF(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Dtol = 0.001;\n    m_Vtol = 0.001;\n    m_Ftol = 0.001;\n    m_Rmin = 1.0e-20;\n\tm_Rmax = 0;\t// not used if zero\n    m_minJf = 0;\n    \n    m_ndeq = 0;\n    m_nveq = 0;\n    m_nfeq = 0;\n    m_niter = 0;\n    m_nreq = 0;\n\n\t// assume non-symmetric\n    m_msymm = REAL_UNSYMMETRIC;\n    \n    // default Newmark parameters for rhoi = 0\n    m_rhoi = 0;\n    m_alphaf = 1;\n    m_alpham = 1.5;\n    m_beta = 0.5625;\n    m_gamma = 1;\n    m_pred = 0;\n    m_order = 2;\n    \n\t// Preferred strategy is Broyden's method\n\tSetDefaultStrategy(QN_BROYDEN);\n\n    // turn off checking for a zero diagonal\n    CheckZeroDiagonal(false);\n    \n    // get the dof indices\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::DISPLACEMENT));\n        m_dofV.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::VELOCITY));\n        m_dofSU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_DISPLACEMENT));\n        m_dofSV.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_VELOCITY));\n        m_dofSA.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_ACCELERATION));\n        m_dofQ.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::ROTATION));\n        m_dofRQ.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RIGID_ROTATION));\n        m_dofW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_ACCELERATION));\n        m_dofVF.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_VELOCITY));\n        m_dofAF.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_ACCELERATION));\n        m_dofEF.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION));\n        m_dofAEF = pfem->GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION_TDERIV), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEFluidFSISolver::~FEFluidFSISolver()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Generate warnings if needed\nvoid FEFluidFSISolver:: SolverWarnings()\n{\n\tFEModel& fem = *GetFEModel();\n\n    // Generate warning if rigid connectors are used with symmetric stiffness\n    if (m_msymm == REAL_SYMMETRIC) {\n        for (int i=0; i<fem.NonlinearConstraints(); ++i)\n        {\n            FENLConstraint* plc = fem.NonlinearConstraint(i);\n            FERigidConnector* prc = dynamic_cast<FERigidConnector*>(plc);\n            if (prc) {\n                feLogWarning(\"Rigid connectors require non-symmetric stiffness matrix.\\nSet symmetric_stiffness flag to 0 in Control section.\");\n                break;\n            }\n        }\n        \n        // Generate warning if sliding-elastic contact is used with symmetric stiffness\n        if (fem.SurfacePairConstraints() > 0)\n        {\n            // loop over all contact interfaces\n            for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n            {\n                FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n                FESlidingElasticInterface* pbw = dynamic_cast<FESlidingElasticInterface*>(pci);\n                if (pbw) {\n                    feLogWarning(\"The sliding-elastic contact algorithm runs better with a non-symmetric stiffness matrix.\\nYou may set symmetric_stiffness 0 to false in Control section.\");\n                    break;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEFluidFSISolver\n//\nbool FEFluidFSISolver::Init()\n{\n    // initialize base class\n    if (FENewtonSolver::Init() == false) return false;\n    \n    // check parameters\n    if (m_Dtol <  0.0) { feLogError(\"dtol must be nonnegative.\"); return false; }\n    if (m_Vtol <  0.0) { feLogError(\"vtol must be nonnegative.\"); return false; }\n    if (m_Ftol <  0.0) { feLogError(\"ftol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_beta = m_gamma = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        if (m_order == 1)\n            m_alpham = (3-m_rhoi)/(1+m_rhoi)/2; // 1st-order system\n        else\n            m_alpham = (2-m_rhoi)/(1+m_rhoi); // 2nd-order system\n        m_beta = pow(1 + m_alpham - m_alphaf,2)/4;\n        m_gamma = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\\n\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fn.assign(neq, 0);\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    m_di.assign(m_ndeq,0);\n    m_Di.assign(m_ndeq,0);\n    m_vi.assign(m_nveq,0);\n    m_Vi.assign(m_nveq,0);\n    m_fi.assign(m_nfeq,0);\n    m_Fi.assign(m_nfeq,0);\n    \n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n    gather(m_Ut, mesh, m_dofU[0]);\n    gather(m_Ut, mesh, m_dofU[1]);\n    gather(m_Ut, mesh, m_dofU[2]);\n    gather(m_Ut, mesh, m_dofSU[0]);\n    gather(m_Ut, mesh, m_dofSU[1]);\n    gather(m_Ut, mesh, m_dofSU[2]);\n    gather(m_Ut, mesh, m_dofW[0]);\n    gather(m_Ut, mesh, m_dofW[1]);\n    gather(m_Ut, mesh, m_dofW[2]);\n    gather(m_Ut, mesh, m_dofEF[0]);\n    \n    \n    // set flag for transient or steady-state analyses\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n            if (fdom) {\n                if (pstep->m_nanalysis == FEFluidFSIAnalysis::STEADY_STATE)\n                    fdom->SetSteadyStateAnalysis();\n                else\n                    fdom->SetTransientAnalysis();\n            }\n            else if (fsidom) {\n                if (pstep->m_nanalysis == FEFluidFSIAnalysis::STEADY_STATE)\n                    fsidom->SetSteadyStateAnalysis();\n                else\n                    fsidom->SetTransientAnalysis();\n            }\n            else if (bfsidom) {\n                if (pstep->m_nanalysis == FEFluidFSIAnalysis::STEADY_STATE)\n                    bfsidom->SetSteadyStateAnalysis();\n                else\n                    bfsidom->SetTransientAnalysis();\n            }\n        }\n    }\n    \n    SolverWarnings();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEFluidFSISolver::InitEquations()\n{\n    // base class initialization\n    if (FENewtonSolver::InitEquations() == false) return false;\n\n\tif (m_eq_scheme == EQUATION_SCHEME::BLOCK)\n\t{\n\t\t// merge the second and third partition\n\t\tif (m_part.size() == 3)\n\t\t{\n\t\t\tvector<int> newPart(2);\n\t\t\tnewPart[0] = m_part[0];\n\t\t\tnewPart[1] = m_part[1] + m_part[2];\n\n\t\t\tm_part = newPart;\n\t\t}\n\t}\n\n    // store the number of equations we currently have\n    m_nreq = m_neq;\n    \n    // Next, we assign equation numbers to the rigid body degrees of freedom\n    int neq = m_rigidSolver.InitEquations(m_neq);\n    if (neq == -1) return false;\n    else m_neq = neq;\n    \n    // determine the number of velocity and dilatation equations\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n    m_ndeq = m_nveq = m_nfeq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofU[0] ] != -1) m_ndeq++;\n        if (n.m_ID[m_dofU[1] ] != -1) m_ndeq++;\n        if (n.m_ID[m_dofU[2] ] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[0]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[1]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[2]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofW[0] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_nfeq++;\n    }\n\n    // Next, we add any Lagrange Multipliers\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n\n    // All initialization is done\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSISolver::GetDisplacementData(vector<double> &xi, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(xi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofU[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofU[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofU[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofSU[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofSU[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofSU[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSISolver::GetVelocityData(vector<double> &vi, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(vi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofW[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSISolver::GetDilatationData(vector<double> &ei, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ei);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofEF[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ei[m++] = ui[nid];\n            assert(m <= (int) ei.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEFluidFSISolver::Serialize(DumpStream& ar)\n{\n    // Serialize parameters\n    FENewtonSolver::Serialize(ar);\n    // serialize rigid solver\n    m_rigidSolver.Serialize(ar);\n    if (ar.IsShallow()) {\n        ar & m_Ui & m_Ut & m_Fr;\n        ar & m_Di & m_Vi & m_Fi;\n    }\n    else {\n        ar & m_nrhs;\n        ar & m_niter;\n        ar & m_nref & m_ntotref;\n\n        ar & m_nreq & m_ndeq & m_nfeq & m_nveq;\n\n        ar & m_rhoi & m_alphaf & m_alpham;\n        ar & m_beta & m_gamma;\n        ar & m_pred;\n\n        m_Fr.assign(m_neq, 0);\n        m_Di.assign(m_ndeq,0);\n        m_Vi.assign(m_nveq,0);\n        m_Fi.assign(m_nfeq,0);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEFluidFSISolver::UpdateKinematics(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update rigid bodies\n    m_rigidSolver.UpdateRigidBodies(m_Ui, ui);\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter3(U, mesh, m_dofU[0], m_dofU[1], m_dofU[2]);\n    scatter3(U, mesh, m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n    scatter3(U, mesh, m_dofW[0], m_dofW[1], m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // force dilatations to remain greater than -1\n    if (m_minJf > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofEF[0]) <= -1.0)\n                node.set(m_dofEF[0], m_minJf - 1.0);\n        }\n    }\n\n    // make sure the prescribed BCs are fulfilled\n\tint nvel = fem.BoundaryConditions();\n    for (int i=0; i<nvel; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.Update();\n    }\n    \n    // apply prescribed DOFs for specialized surface loads\n\tint nml = fem.ModelLoads();\n    for (int i=0; i<nml; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.Update();\n    }\n    \n    // enforce the linear constraints\n    // TODO: do we really have to do this? Shouldn't the algorithm\n    // already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    if (LCM.LinearConstraints() > 0)\n    {\n        LCM.Update();\n    }\n    \n    // Update the spatial nodal positions\n    // Don't update rigid nodes since they are already updated\n    for (int i = 0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.m_rid == -1) {\n            node.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n            node.m_dt = node.m_d0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2])\n            - node.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n        }\n    }\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEFluidFSIAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n\t\tdouble dt = fem.GetTime().timeIncrement;\n        double a = 1.0 / (m_beta*dt);\n        double b = a / dt;\n        double c = 1.0 - 0.5/m_beta;\n        double cgi = 1 - 1.0/m_gamma;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // solid acceleration\n            n.m_at = (n.m_rt - n.m_rp)*b - n.m_vp*a + n.m_ap*c;\n            // solid velocity\n            vec3d vt = n.m_vp + (n.m_ap*(1.0 - m_gamma) + n.m_at*m_gamma)*dt;\n            n.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n            \n            // shell kinematics\n            vec3d qt = n.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n            vec3d qp = n.get_vec3d_prev(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n            vec3d vqp = n.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2]);\n            vec3d aqp = n.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2]);\n            vec3d aqt = (qt - qp)*b - vqp*a + aqp*c;\n            vec3d vqt = vqp + (aqp*(1.0 - m_gamma) + aqt*m_gamma)*dt;\n            n.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n            n.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vqt);\n\n            // relative fluid velocity material time derivative (in solid frame)\n            vec3d wt = n.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d wp = n.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d awt = n.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            vec3d awp = n.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            awt = awp*cgi + (wt - wp)/(m_gamma*dt);\n            n.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awt);\n            \n            // fluid velocity\n            vec3d vft = vt + wt;\n            n.set_vec3d(m_dofVF[0], m_dofVF[1], m_dofVF[2], vft);\n            // material time derivative of fluid velocity (in solid frame)\n            vec3d aft = n.m_at + awt;\n            n.set_vec3d(m_dofAF[0], m_dofAF[1], m_dofAF[2], aft);\n            \n            // dilatation time derivative\n            double eft = n.get(m_dofEF[0]);\n            double efp = n.get_prev(m_dofEF[0]);\n            double aefp = n.get_prev(m_dofAEF);\n            double aeft = aefp*cgi + (eft - efp)/(m_gamma*dt);\n            n.set(m_dofAEF, aeft);\n        }\n\t\tint _a = 0;\n    }\n\n    // update nonlinear constraints (needed for updating Lagrange Multiplier)\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* nlc = fem.NonlinearConstraint(i);\n        if (nlc->IsActive()) nlc->Update(m_Ui, ui);\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive()) spc->Update(m_Ui, ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update DOF increments\nvoid FEFluidFSISolver::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n\tFEModel& fem = *GetFEModel();\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update rigid bodies\n    m_rigidSolver.UpdateIncrements(Ui, ui, emap);\n    \n    // extract the velocity and dilatation increments\n    GetDisplacementData(m_di, ui);\n    GetVelocityData(m_vi, ui);\n    GetDilatationData(m_fi, ui);\n        \n    // update displacements\n    for (int i = 0; i<m_ndeq; ++i) m_Di[i] += m_di[i];\n        \n    // update velocities\n    for (int i = 0; i<m_nveq; ++i) m_Vi[i] += m_vi[i];\n        \n    // update dilatations\n    for (int i = 0; i<m_nfeq; ++i) m_Fi[i] += m_fi[i];\n    \n   // update flexible nodes\n    int n;\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // displacement dofs\n        // current position = initial + total at prev conv step + total increment so far + current increment\n        if ((n = node.m_ID[m_dofU[0]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofU[1]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofU[2]]) >= 0) Ui[n] += ui[n];\n        \n        // rotational dofs\n        if ((n = node.m_ID[m_dofSU[0]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofSU[1]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofSU[2]]) >= 0) Ui[n] += ui[n];\n\n        // fluid relative velocity\n        if ((n = node.m_ID[m_dofW[0]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofW[1]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofW[2]]) >= 0) Ui[n] += ui[n];\n        \n        // fluid dilatation\n        if ((n = node.m_ID[m_dofEF[0]]) >= 0) Ui[n] += ui[n];\n    }\n\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->UpdateIncrements(Ui, ui);\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->UpdateIncrements(Ui, ui);\n\t}\n\n    // TODO: This is a hack!\n    // The problem is that I only want to call the domain's IncrementalUpdate during\n    // the quasi-Newtoon loop. However, this function is also called after the loop\n    // converges. The emap parameter is used here to detect wether we are inside the\n    // loop (emap == false), or not (emap == true).\n    if (emap == false)\n    {\n        for (int i = 0; i < mesh.Domains(); ++i)\n        {\n            FEDomain& dom = mesh.Domain(i);\n            dom.IncrementalUpdate(ui, true);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEFluidFSISolver::Update(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n\n    // update EAS\n    UpdateEAS(ui);\n    UpdateIncrementsEAS(ui, true);\n    \n    // update kinematics\n    UpdateKinematics(ui);\n    \n\t// update element stresses\n\tUpdateModel();\n}\n\n//-----------------------------------------------------------------------------\n//! Update EAS\nvoid FEFluidFSISolver::UpdateEAS(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update EAS on shell domains\n    for (int i=0; i<mesh.Domains(); ++i) {\n        FESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n        if (sdom && sdom->IsActive()) sdom->UpdateEAS(ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update EAS\nvoid FEFluidFSISolver::UpdateIncrementsEAS(vector<double>& ui, const bool binc)\n{\n\tFEModel& fem = *GetFEModel();\n\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update EAS on shell domains\n    for (int i=0; i<mesh.Domains(); ++i) {\n        FESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n        if (sdom && sdom->IsActive()) sdom->UpdateIncrementsEAS(ui, binc);\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidFSISolver::InitStep(double time)\n{\n\tFEModel& fem = *GetFEModel();\n\n    // get time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alpha = m_alphaf;\n    tp.beta  = m_beta;\n    tp.gamma = m_gamma;\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n//    double dt = tp.timeIncrement;\n//    double ta = (t > 0) ? t - (1-m_alpha)*dt : m_alpha*dt;\n//    return FESolver::InitStep(ta);\n    return FESolver::InitStep(t);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FEFluidFSISolver::PrepStep()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFETimeInfo& tp = fem.GetTime();\n\tdouble dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n\n    // zero total DOFs\n    zero(m_Ui);\n    zero(m_Vi);\n    zero(m_Di);\n    zero(m_Fi);\n    \n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt;\n        ni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n        ni.m_ap = ni.m_at;\n        ni.m_dp = ni.m_dt = ni.m_d0;\n\t\tni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n            {\n                // initial guess at start of new time step (default)\n                // solid\n                ni.m_at = ni.m_ap*(1-0.5/m_beta) - ni.m_vp/(m_beta*dt);\n                vec3d vs = ni.m_vp + (ni.m_at*m_gamma + ni.m_ap*(1-m_gamma))*dt;\n                ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vs);\n                \n                // solid shell\n                vec3d aqp = ni.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2]);\n                vec3d vqp = ni.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2]);\n                vec3d aqt = aqp*(1-0.5/m_beta) - vqp/(m_beta*dt);\n                ni.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n                vec3d vqt = vqp + (aqt*m_gamma + aqp*(1-m_gamma))*dt;\n                ni.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vqt);\n                \n                // fluid\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awp*(m_gamma-1)/m_gamma);\n                \n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF)*(m_gamma-1)/m_gamma);\n            }\n                break;\n                \n            case 1:\n            {\n                // initial guess at start of new time step (Zero Ydot)\n                ni.m_at = vec3d(0,0,0);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2],vec3d(0,0,0));\n                ni.set(m_dofAEF, 0);\n\n                ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], ni.m_vp + ni.m_ap*dt*(1-m_gamma)*m_alphaf);\n                vec3d wp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], wp + awp*dt*(1-m_gamma)*m_alphaf);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt*(1-m_gamma)*m_alphaf);\n            }\n                break;\n                \n            case 2:\n            {\n                // initial guess at start of new time step (Same Ydot)\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awp);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF));\n                \n                ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], ni.m_vp + ni.m_ap*dt);\n                vec3d wp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], wp + awp*dt);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt);\n            }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.PrepStep(ui);\n    }\n\n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.PrepStep();\n    }\n    \n    // do the linear constraints\n    fem.GetLinearConstraintManager().PrepStep();\n    \n    // initialize rigid bodies\n    m_rigidSolver.PrepStep(tp, ui);\n    \n   // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n    // update domain data\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) dom.PreSolveUpdate(tp);\n    }\n\n    // update model state\n\tUpdateModel();\n    \n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->PrepStep();\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n\n    // see if we need to do contact augmentations\n    m_baugment = false;\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n        if (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n    }\n    \n\t// see if we need to do incompressible augmentations\n\t// TODO: Should I do these augmentations in a nlconstraint class instead?\n\tint ndom = mesh.Domains();\n\tfor (int i = 0; i < ndom; ++i)\n\t{\n\t\tFEDomain* dom = &mesh.Domain(i);\n\t\tFE3FieldElasticSolidDomain* dom3f = dynamic_cast<FE3FieldElasticSolidDomain*>(dom);\n\t\tif (dom3f && dom3f->DoAugmentations()) m_baugment = true;\n\n\t\tFE3FieldElasticShellDomain* dom3fs = dynamic_cast<FE3FieldElasticShellDomain*>(dom);\n\t\tif (dom3fs && dom3fs->DoAugmentations()) m_baugment = true;\n\t}\n\n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidFSISolver::Quasin()\n{\n\tFEModel& fem = *GetFEModel();\n\n    vector<double> u0(m_neq);\n    vector<double> Rold(m_neq);\n    \n    // convergence norms\n    double\tnormR1;\t\t// residual norm\n    double\tnormE1;\t\t// energy norm\n    double\tnormD;\t\t// displacement norm\n    double\tnormd;\t\t// displacement increment norm\n    double\tnormV;\t\t// velocity norm\n    double\tnormv;\t\t// velocity increment norm\n    double\tnormRi = 0;\t// initial residual norm\n    double\tnormDi = 0;\t// initial displacement norm\n    double\tnormVi = 0;\t// initial velocity norm\n    double\tnormEi = 0; // initial energy norm\n    double\tnormEm = 0;\t// max energy norm\n    double\tnormFi = 0;\t// initial dilatation norm\n    double\tnormF;\t\t// current dilatation norm\n    double\tnormf;\t\t// incremement dilatation norm\n    \n    // prepare for the first iteration\n    const FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n    // init QN method\n\tif (QNInit() == false) return false;\n    \n    // loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n        \n        // solve the equations\n        SolveEquations(m_ui, m_R0);\n\n        // do the line search\n        double s = DoLineSearch();\n\n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normDi = fabs(m_di*m_di);\n            normVi = fabs(m_vi*m_vi);\n            normFi = fabs(m_fi*m_fi);\n            normEm = normEi;\n        }\n        \n        // calculate actual displacement increment\n        // NOTE: We don't apply the line search directly to m_ui since we need the unscaled search direction for the QN update below\n        int neq = (int)m_Ui.size();\n        vector<double> ui(m_ui);\n        for (int i = 0; i<neq; ++i) ui[i] *= s;\n\n        // update increments (including Lagrange multipliers)\n        UpdateIncrements(m_Ui, ui, false);\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n        normd  = m_di*m_di;\n        normD  = m_Di*m_Di;\n        normv  = m_vi*m_vi;\n        normV  = m_Vi*m_Vi;\n        normf  = m_fi*m_fi;\n        normF  = m_Fi*m_Fi;\n        normE1 = fabs(ui*m_R1);\n            \n        // check for nans\n        if (ISNAN(normR1)) throw NANInResidualDetected();\n        if (ISNAN(normv)) throw NANInResidualDetected();\n        if (ISNAN(normd)) throw NANInResidualDetected();\n        if (ISNAN(normf)) throw NANInResidualDetected();\n\n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check displacement norm\n        if ((m_Dtol > 0) && (normd  > (m_Dtol*m_Dtol)*normD )) bconv = false;\n        \n        // check velocity norm\n        if ((m_Vtol > 0) && (normv  > (m_Vtol*m_Vtol)*normV )) bconv = false;\n        \n        // check dilatation norm\n        if ((m_Ftol > 0) && (normf  > (m_Ftol*m_Ftol)*normF )) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        if (normE1 > normEm) bconv = false;\n        \n        // print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        feLog(\"\\t   displacement     %15le %15le %15le \\n\", normDi, normd ,(m_Dtol*m_Dtol)*normD );\n        feLog(\"\\t   velocity         %15le %15le %15le \\n\", normVi, normv ,(m_Vtol*m_Vtol)*normV );\n        feLog(\"\\t   dilatation       %15le %15le %15le \\n\", normFi, normf ,(m_Ftol*m_Ftol)*normF );\n        \n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n\t\t// see if we have exceeded the max residual\n\t\tif ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n\t\t{\n\t\t\t// doesn't look like we're getting anywhere, so let's retry the time step\n\t\t\tthrow MaxResidualError();\n\t\t}\n\n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n\t\t\tif (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                normDi = normd;\n                normVi = normv;\n                normFi = normf;\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n        }\n        else if (m_baugment)\n        {\n\t\t\t// Do augmentations\n\t\t\tbconv = DoAugmentations();\n        }\n        \n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        UpdateIncrementsEAS(m_Ui, false);\n        UpdateIncrements(m_Ut, m_Ui, true);\n        zero(m_Ui);\n        zero(m_Di); zero(m_Vi); zero(m_Fi);\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEFluidFSISolver::StiffnessMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tconst FETimeInfo& tp = fem.GetTime();\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n\n\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), m_alphaf, m_nreq);\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n            FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n            if (fdom) fdom->StiffnessMatrix(LS);\n            else if (fsidom) fsidom->StiffnessMatrix(LS);\n            else if (bfsidom) bfsidom->StiffnessMatrix(LS);\n            else if (edom) edom->StiffnessMatrix(LS);\n        }\n    }\n    \n    // calculate the body force stiffness matrix for each domain\n    // but not for solid domains (since they have no mass in FSI)\n\tint NML = fem.ModelLoads();\n\tfor (int j = 0; j<NML; ++j)\n\t{\n\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n\t\tif (pbf && pbf->IsActive())\n\t\t{\n\t\t\tfor (int i = 0; i<pbf->Domains(); ++i)\n\t\t\t{\n\t\t\t\tFEDomain* dom = pbf->Domain(i);\n\t\t\t\tif (dom->IsActive())\n\t\t\t\t{\n\t\t\t\t\tFEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(dom);\n\t\t\t\t\tFEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(dom);\n                    FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(dom);\n\t\t\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(dom);\n\t\t\t\t\tif (fdom) fdom->BodyForceStiffness(LS, *pbf);\n\t\t\t\t\telse if (fsidom) fsidom->BodyForceStiffness(LS, *pbf);\n                    else if (bfsidom) bfsidom->BodyForceStiffness(LS, *pbf);\n\t\t\t\t\telse if (edom)\n\t\t\t\t\t{\n\t\t\t\t\t\tFESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom->GetMaterial());\n\t\t\t\t\t\tif (mat && (mat->IsRigid()==false)) edom->BodyForceStiffness(LS, *pbf);\n\t\t\t\t\t}\n                }\n            }\n        }\n    }\n    \n    // TODO: add body force stiffness for rigid bodies\n    \n    // Add mass matrix\n//    FEAnalysis* pstep = fem.GetCurrentStep();\n//    if (pstep->m_nanalysis == FEFluidFSIAnalysis::DYNAMIC)\n    {\n        // scale factor\n        double dt = tp.timeIncrement;\n        double a = tp.alpham / (m_beta*dt*dt);\n        // loop over all domains (except rigid)\n\t\tfor (int i = 0; i<mesh.Domains(); ++i)\n        {\n            FEDomain& dom = mesh.Domain(i);\n            if (dom.IsActive())\n\t\t\t{\n\t\t\t\tFEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n\t\t\t\tFEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n                FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n\t\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n\t\t\t\tif (fdom) fdom->MassMatrix(LS);\n\t\t\t\telse if (fsidom) fsidom->MassMatrix(LS);\n                else if (bfsidom) bfsidom->MassMatrix(LS);\n\t\t\t\telse if (edom)\n\t\t\t\t{\n\t\t\t\t\tFESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n\t\t\t\t\tif (mat && (mat->IsRigid() == false)) edom->MassMatrix(LS, a);\n\t\t\t\t}\n            }\n        }\n        \n        m_rigidSolver.RigidMassMatrix(LS, tp);\n    }\n    \n    // calculate contact stiffness\n    ContactStiffness(LS);\n\n    // calculate the stiffness contributions for the loads\n    for (int i = 0; i < fem.ModelLoads(); ++i) fem.ModelLoad(i)->StiffnessMatrix(LS);\n\n    // calculate nonlinear constraint stiffness\n    // note that this is the contribution of the\n    // constraints enforced with augmented lagrangian\n    NonLinearConstraintStiffness(LS, tp);    \n   \n    // add contributions from rigid bodies\n    m_rigidSolver.StiffnessMatrix(*m_pK, tp);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEFluidFSISolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FEFluidFSISolver::ContactStiffness(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEFluidFSISolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tconst FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry\n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEFluidFSISolver::Residual(vector<double>& R)\n{\n\tFEModel& fem = *GetFEModel();\n\n    // get the time information\n\tconst FETimeInfo& tp = fem.GetTime();\n    \n    // initialize residual with concentrated nodal loads\n    R = m_Fn;\n    \n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n    FEResidualVector RHS(fem, R, m_Fr);\n\n    // zero rigid body reaction forces\n    m_rigidSolver.Residual();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n\t\t{\n\t\t\tFEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n\t\t\tFEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n\t\t\tif (fdom) fdom->InternalForces(RHS);\n\t\t\telse if (fsidom) fsidom->InternalForces(RHS);\n            else if (bfsidom) bfsidom->InternalForces(RHS);\n\t\t\telse if (edom)\n\t\t\t{\n\t\t\t\tFESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n\t\t\t\tif (mat && (mat->IsRigid() == false)) edom->InternalForces(RHS);\n\t\t\t}\n        }\n    }\n    \n    // calculate the body forces\n\tfor (int j = 0; j<fem.ModelLoads(); ++j)\n\t{\n\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n\t\tif (pbf && pbf->IsActive())\n\t\t{\n\t\t\tfor (int i = 0; i<pbf->Domains(); ++i)\n\t\t\t{\n\t\t\t\tFEDomain* dom = pbf->Domain(i);\n\t\t\t\tif (dom->IsActive())\n\t\t\t\t{\n\t\t\t\t\tFEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(dom);\n\t\t\t\t\tFEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(dom);\n                    FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(dom);\n\t\t\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(dom);\n\t\t\t\t\tif (fdom) fdom->BodyForce(RHS, *pbf);\n\t\t\t\t\telse if (fsidom) fsidom->BodyForce(RHS, *pbf);\n                    else if (bfsidom) bfsidom->BodyForce(RHS, *pbf);\n\t\t\t\t\telse if (edom)\n\t\t\t\t\t{\n\t\t\t\t\t\tFESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom->GetMaterial());\n\t\t\t\t\t\tif (mat && (mat->IsRigid()==false)) edom->BodyForce(RHS, *pbf);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n    // allocate F\n    vector<double> F;\n    \n    FEAnalysis* pstep = fem.GetCurrentStep();\n    \n    // calculate inertial forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n\t\t{\n\t\t\tFEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n\t\t\tFEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n\t\t\tif (fdom) fdom->InertialForces(RHS);\n\t\t\telse if (fsidom) fsidom->InertialForces(RHS);\n            else if (bfsidom) bfsidom->InertialForces(RHS);\n\t\t\telse if (edom && (pstep->m_nanalysis == FEFluidFSIAnalysis::DYNAMIC))\n\t\t\t{\n\t\t\t\tFESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n\t\t\t\tif (mat && (mat->IsRigid()==false)) edom->InertialForces(RHS, F);\n\t\t\t}\n        }\n    }\n    \n    // update rigid bodies\n    if (pstep->m_nanalysis == FEFluidFSIAnalysis::DYNAMIC) m_rigidSolver.InertialForces(RHS, tp);\n\n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i = 0; i < NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive()) mli.LoadVector(RHS);\n    }\n\n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\t}\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces\nvoid FEFluidFSISolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->LoadVector(R, tp);\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSISolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEGlobalVector.h>\n#include <FEBioMech/FERigidSolver.h>\n#include <FECore/FEDofList.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEFluidFSISolver class solves fluid-FSI problems\n//! It can deal with quasi-static and dynamic problems\n//!\nclass FEBIOFLUID_API FEFluidFSISolver : public FENewtonSolver\n{\npublic:\n    //! constructor\n    FEFluidFSISolver(FEModel* pfem);\n    \n    //! destructor\n    ~FEFluidFSISolver();\n    \n    //! serialize data to/from dump file\n    void Serialize(DumpStream& ar) override;\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n    \n    //! Generate warnings if needed\n    void SolverWarnings();\n\n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n\n\t//! update nodal positions, velocities, accelerations, etc.\n\tvoid UpdateKinematics(vector<double>& ui);\n\n\t//! Update EAS\n\tvoid UpdateEAS(vector<double>& ui);\n\tvoid UpdateIncrementsEAS(vector<double>& ui, const bool binc);\n\n\t//! update DOF increments\n\tvirtual void UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap);\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix() override;\n    \n    //! contact stiffness\n    void ContactStiffness(FELinearSystem& LS);\n    \n    //! calculates stiffness contributon of nonlinear constraints\n    void NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n    \n    //{ --- Residual routines ---\n    \n    //! Calculate the contact forces\n    void ContactForces(FEGlobalVector& R);\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n    \n    //! Calculate nonlinear constraint forces\n    void NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n    \nprotected:\n    void GetDisplacementData(vector<double>& xi, vector<double>& ui);\n    void GetVelocityData(vector<double>& vi, vector<double>& ui);\n    void GetDilatationData(vector<double>& ei, vector<double>& ui);\n    \npublic:\n    // convergence tolerances\n    double\tm_Dtol;\t\t\t//!< displacement tolerance\n    double\tm_Vtol;\t\t\t//!< velocity tolerance\n    double\tm_Ftol;\t\t\t//!< dilatation tolerance\n    double  m_minJf;        //!< minimum allowable compression ratio\n\npublic:\n    // equation numbers\n    int     m_nreq;         //!< start of rigid body equations\n    int\t\tm_ndeq;\t\t\t//!< number of equations related to displacement dofs\n    int\t\tm_nveq;\t\t\t//!< number of equations related to velocity dofs\n    int\t\tm_nfeq;\t\t\t//!< number of equations related to dilatation dofs\n    \npublic:\n    vector<double> m_Fn;\t//!< concentrated nodal force vector\n    vector<double> m_Fr;\t//!< nodal reaction forces\n    vector<double> m_di;\t//!< displacement increment vector\n    vector<double> m_Di;\t//!< Total displacement vector for iteration\n    vector<double> m_vi;\t//!< velocity increment vector\n    vector<double> m_Vi;\t//!< Total velocity vector for iteration\n    vector<double> m_fi;\t//!< dilatation increment vector\n    vector<double> m_Fi;\t//!< Total dilatation vector for iteration\n    \n    // generalized alpha method\n    double  m_rhoi;         //!< spectral radius (rho infinity)\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_beta;         //!< beta\n    double  m_gamma;        //!< gamma\n    int     m_pred;         //!< predictor method\n    int     m_order;        //!< generalized-alpha integration order\n    \nprotected:\n\tFEDofList\tm_dofU;\t\t// solid displacement\n\tFEDofList\tm_dofV;\t\t// solid velocity\n\tFEDofList\tm_dofSU;\t// shell displacement\n\tFEDofList\tm_dofSV;\t// shell velocity\n\tFEDofList\tm_dofSA;\t// shell acceleration\n    FEDofList   m_dofQ;     // rotation\n    FEDofList   m_dofRQ;    // rigid rotation\n\tFEDofList\tm_dofVF;\t// fluid velocity\n\tFEDofList\tm_dofAF;\t// material time derivative of fluid velocity\n\tFEDofList\tm_dofW;\t    // fluid velocity relative to solid\n\tFEDofList\tm_dofAW;\t// material time derivative of fluid velocity relative to solid\n    FEDofList\tm_dofEF;\t// fluid dilatation\n    int\t\t\tm_dofAEF;\t// material time derivative of fluid dilatation\n\nprotected:\n    FERigidSolverNew    m_rigidSolver;\n\n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSITraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSITraction.h\"\n#include \"FECore/FEModel.h\"\n#include \"FEFluid.h\"\n#include \"FEFluidFSI.h\"\n#include \"FEBioFSI.h\"\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEFluidFSITraction, FESurfaceLoad)\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidFSITraction::FEFluidFSITraction(FEModel* pfem) : FESurfaceLoad(pfem), m_dofU(pfem), m_dofSU(pfem), m_dofW(pfem)\n{\n\tm_bshellb = false;\n\n    // get the degrees of freedom\n\t// TODO: Can this be done in Init, since  there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::DISPLACEMENT));\n\t\tm_dofSU.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_DISPLACEMENT));\n\t\tm_dofW.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_VELOCITY));\n\t\tm_dofEF = GetDOFIndex(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION), 0);\n\n\t\tm_dof.Clear();\n\t\tm_dof.AddDofs(m_dofU);\n\t\tm_dof.AddDofs(m_dofSU);\n\t\tm_dof.AddDofs(m_dofW);\n\t\tm_dof.AddDof(m_dofEF);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidFSITraction::Init()\n{\n\tFESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n\tsurf.SetInterfaceStatus(true);\n\tif (FESurfaceLoad::Init() == false) return false;\n\n    // TODO: Deal with the case when the surface is a shell domain separating two FSI domains\n    // that use different fluid bulk moduli\n    // (for now, users have to define two FEFluidFSITraction loads, one on front shell\n    // face and the other on back shell face)\n    \n    return true;\n}\n\nvoid FEFluidFSITraction::Activate()\n{\n\tFESurface& surf = GetSurface();\n\n\t// get the list of fluid-FSI elements connected to this interface\n\tFEModel* fem = GetFEModel();\n\tint NF = surf.Elements();\n\tm_elem.resize(NF);\n\tm_s.resize(NF, 1);\n\tfor (int j = 0; j < NF; ++j)\n\t{\n\t\tbool bself = false;\n\t\tFESurfaceElement& el = surf.Element(j);\n\t\t// extract the first of two elements on this interface\n\t\tm_elem[j] = el.m_elem[0].pe;\n\t\tif (el.m_elem[1].pe == nullptr) bself = true;\n\t\t// get its material and check if FluidFSI\n\t\tFEMaterial* pm = fem->GetMaterial(m_elem[j]->GetMatID());\n\t\tFEFluidFSI* pfsi = dynamic_cast<FEFluidFSI*>(pm);\n\t\tif (pfsi) {\n\t\t\tdouble s = m_psurf->FacePointing(el, *m_elem[j]);\n\t\t\tm_s[j] = bself ? -s : s;\n\t\t\tassert(m_s[j]);\n\t\t}\n\t\telse if (!bself) {\n\t\t\t// extract the second of two elements on this interface\n\t\t\tm_elem[j] = el.m_elem[1].pe;\n\t\t\tpm = fem->GetMaterial(m_elem[j]->GetMatID());\n\t\t\tpfsi = dynamic_cast<FEFluidFSI*>(pm);\n\t\t\tassert(pfsi);\n\t\t\tm_s[j] = m_psurf->FacePointing(el, *m_elem[j]);\n\t\t\tassert(m_s[j]);\n\t\t}\n\t\telse\n\t\t\tassert(false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFluidFSITraction::GetFluidDilatation(FESurfaceMaterialPoint& mp, double alpha)\n{\n\tdouble ef = 0;\n\tFESurfaceElement& el = *mp.SurfaceElement();\n\tdouble* H = el.H(mp.m_index);\n\tint neln = el.Nodes();\n\tfor (int j = 0; j < neln; ++j) {\n\t\tFENode& node = m_psurf->Node(el.m_lnode[j]);\n\t\tdouble ej = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1.0 - alpha);\n\t\tef += ej*H[j];\n\t}\n\treturn ef;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFluidFSITraction::GetFluidStress(FESurfaceMaterialPoint& pt)\n{\n\tFEModel* fem = GetFEModel();\n\tFESurfaceElement& face = *pt.SurfaceElement();\n\tint iel = face.m_lid;\n\n\t// Get the fluid stress from the fluid-FSI element\n\tmat3ds sv(mat3dd(0));\n\tFEElement* pe = m_elem[iel];\n\tint nint = pe->GaussPoints();\n\tFEFluidFSI* pfsi = dynamic_cast<FEFluidFSI*>(fem->GetMaterial(pe->GetMatID()));\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n\t\tsv += pfsi->Fluid()->GetViscous()->Stress(mp);\n\t}\n\tsv /= nint;\n\treturn sv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSITraction::LoadVector(FEGlobalVector& R)\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\t// If surface is bottom of shell, we should take shell displacement dofs (i.e. m_dofSU).\n    FEDofList dof = m_bshellb ? m_dofSU : m_dofU;\n\tm_psurf->LoadVector(R, dof, false, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\t// get the surface element\n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\t\tint iel = el.m_lid;\n        FEFluidFSI* pfsi = dynamic_cast<FEFluidFSI*>(GetFEModel()->GetMaterial(m_elem[iel]->GetMatID()));\n\n\t\t// nodal coordinates\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tm_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n\n\t\t// evaluate covariant basis vectors at integration point\n\t\tvec3d gr = el.eval_deriv1(rt, mp.m_index)*m_s[iel];\n\t\tvec3d gs = el.eval_deriv2(rt, mp.m_index);\n\t\tvec3d gt = gr ^ gs;\n\n\t\t// Get the fluid viscous stress at integration point\n        // necessarily using the attached solid element\n\t\tmat3ds sv = GetFluidStress(mp);\n\n\t\t// fluid dilatation at integration point\n        // only from surface element\n\t\tdouble ef = GetFluidDilatation(mp, tp.alphaf);\n        double p = pfsi->Fluid()->Pressure(ef);\n\n\t\t// evaluate traction\n\t\tvec3d f = gt*p - sv*gt;\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * f.x;\n\t\tfa[1] = H * f.y;\n\t\tfa[2] = H * f.z;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSITraction::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEModel* fem = GetFEModel();\n\tFESurface* ps = &GetSurface();\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\t// build dof list\n\t// TODO: If surface is bottom of shell, we should take shell displacement dofs (i.e. m_dofSU).\n\tFEDofList dofs(fem);\n    if (!m_bshellb) dofs.AddDofs(m_dofU); else dofs.AddDofs(m_dofSU);\n\tdofs.AddDofs(m_dofW);\n\tdofs.AddDof(m_dofEF);\n\n\t// evaluate stiffness\n\tm_psurf->LoadStiffness(LS, dofs, dofs, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\t\tint iel = el.m_lid;\n\t\tint neln = el.Nodes();\n\n\t\tdouble dt = tp.timeIncrement;\n\t\tdouble alpha = tp.alphaf;\n\t\tdouble a = tp.gamma / (tp.beta*dt);\n\n\t\tvector<vec3d> gradN(neln);\n\n\t\t// nodal coordinates\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tps->GetNodalCoordinates(el, tp.alphaf, rt);\n\n\t\t// Get the fluid stress and its tangents from the fluid-FSI element\n\t\tmat3ds sv(mat3dd(0)), svJ(mat3dd(0));\n\t\ttens4ds cv; cv.zero();\n\t\tmat3d Ls; Ls.zero();\n\t\tFEElement* pe = m_elem[iel];\n\t\tint pint = pe->GaussPoints();\n\t\tFEFluidFSI* pfsi = dynamic_cast<FEFluidFSI*>(fem->GetMaterial(pe->GetMatID()));\n\t\tfor (int n = 0; n<pint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n\t\t\tFEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\tsv += pfsi->Fluid()->GetViscous()->Stress(mp);\n\t\t\tsvJ += pfsi->Fluid()->GetViscous()->Tangent_Strain(mp);\n\t\t\tcv += pfsi->Fluid()->Tangent_RateOfDeformation(mp);\n\t\t\tLs += ep.m_L;\n\t\t}\n\t\tsv /= pint;\n\t\tsvJ /= pint;\n\t\tcv /= pint;\n\t\tLs /= pint;\n\t\tmat3d M = mat3dd(a) - Ls;\n\n\t\tdouble* N  = el.H (mp.m_index);\n\t\tdouble* Gr = el.Gr(mp.m_index);\n\t\tdouble* Gs = el.Gs(mp.m_index);\n\n\t\t// evaluate fluid dilatation\n\t\tdouble ef = GetFluidDilatation(mp, tp.alphaf);\n\n\t\t// covariant basis vectors\n\t\tvec3d gr = el.eval_deriv1(rt, mp.m_index)*m_s[iel];\n\t\tvec3d gs = el.eval_deriv2(rt, mp.m_index);\n\t\tvec3d gt = gr ^ gs;\n\n\t\t// evaluate fluid pressure\n        double p = pfsi->Fluid()->Pressure(ef);\n\n        vec3d f = gt*pfsi->Fluid()->GetElastic()->Tangent_Strain(ef,0);\n\n\t\tvec3d gcnt[2], gcntp[2];\n\t\tps->ContraBaseVectors(el, mp.m_index, gcnt);\n\t\tps->ContraBaseVectorsP(el, mp.m_index, gcntp);\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t\tgradN[i] = (gcnt[0] * alpha + gcntp[0] * (1 - alpha))*(Gr[i]*m_s[iel]) +\n\t\t\t(gcnt[1] * alpha + gcntp[1] * (1 - alpha))*Gs[i];\n\n\t\t// calculate stiffness component\n\t\tint i = dof_a.index;\n\t\tint j = dof_b.index;\n\t\tvec3d v = gr*Gs[j] - gs*Gr[j];\n\t\tmat3d A; A.skew(v);\n\t\tmat3d Kv = vdotTdotv(gt, cv, gradN[j]);\n\n        mat3d Kuu = (sv*A + Kv*M)*N[i] - A*(N[i] * p); Kuu *= -alpha;\n        mat3d Kuw = Kv*N[i]; Kuw *= -alpha;\n        vec3d kuJ = svJ*gt*(N[i] * N[j]) - f*(N[i] * N[j]); kuJ *= -alpha;\n\n\t\tKab.zero();\n\t\tKab.sub(0, 0, Kuu);\n\t\tKab.sub(0, 3, Kuw);\n\n\t\tKab[0][6] -= kuJ.x;\n\t\tKab[1][6] -= kuJ.y;\n\t\tKab[2][6] -= kuJ.z;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFSITraction::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n\n\tif (ar.IsShallow() == false)\n\t{\n        ar & m_s;\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tint NE = (int)m_elem.size();\n\t\t\tar << NE;\n\t\t\tfor (int i = 0; i < NE; ++i)\n\t\t\t{\n\t\t\t\tFEElement* pe = m_elem[i];\n\t\t\t\tint nid = (pe ? pe->GetID() : -1);\n\t\t\t\tar << nid;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEMesh& mesh = ar.GetFEModel().GetMesh();\n\t\t\tint NE, nid;\n\t\t\tar >> NE;\n\t\t\tm_elem.resize(NE, nullptr);\n\t\t\tfor (int i = 0; i < NE; ++i)\n\t\t\t{\n\t\t\t\tar >> nid;\n\t\t\t\tif (nid != -1)\n\t\t\t\t{\n\t\t\t\t\tFEElement* pe = mesh.FindElementFromID(nid);\n\t\t\t\t\tassert(pe);\n\t\t\t\t\tm_elem[i] = pe;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidFSITraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! This surface load represents the traction applied on the solid at the\n//! interface between a fluid and solid in an FSI analysis.\nclass FEBIOFLUID_API FEFluidFSITraction : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidFSITraction(FEModel* pfem);\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialization\n    bool Init() override;\n\n\tvoid Activate() override;\n    \nprivate:\n\tdouble GetFluidDilatation(FESurfaceMaterialPoint& mp, double alpha);\n\tmat3ds GetFluidStress(FESurfaceMaterialPoint& mp);\n    \nprotected:\n\tvector<double>      m_s;        //!< scale factor\n\tvector<FEElement*>  m_elem;     //!< list of fluid-FSI elements\n\n    // degrees of freedom\n\tFEDofList\tm_dofU, m_dofSU, m_dofW;\n    int\t\tm_dofEF;\n    \nprotected:\n    bool                m_bshellb;  //!< flag for prescribing traction on shell bottom\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidHeatSupply.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidHeatSupply.h\"\n#include \"FEThermoFluid.h\"\n#include \"FEThermoFluidDomain3D.h\"\n\n//-----------------------------------------------------------------------------\nFEFluidHeatSupply::FEFluidHeatSupply(FEModel* pfem) : FEBodyLoad(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Work in progress! Working on integrating body loads as model loads\nvoid FEFluidHeatSupply::LoadVector(FEGlobalVector& R)\n{\n    for (int i = 0; i<Domains(); ++i)\n    {\n        FEDomain* dom = Domain(i);\n        FEThermoFluidDomain3D* fdom = dynamic_cast<FEThermoFluidDomain3D*>(dom);\n        if (fdom) fdom->HeatSupply(R, *this);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Work in progress! Working on integrating body loads as model loads\nvoid FEFluidHeatSupply::StiffnessMatrix(FELinearSystem& LS)\n{\n    for (int i = 0; i<Domains(); ++i)\n    {\n        FEDomain* dom = Domain(i);\n        FEThermoFluidDomain3D* fdom = dynamic_cast<FEThermoFluidDomain3D*>(dom);\n        if (fdom) fdom->HeatSupplyStiffness(LS, *this);\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidHeatSupply.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FEBodyLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body forces\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOFLUID_API FEFluidHeatSupply : public FEBodyLoad\n{\npublic:\n    //! constructor\n    FEFluidHeatSupply(FEModel* pfem);\n    virtual ~FEFluidHeatSupply() {}\n\npublic:\n    //! calculate the body force at a material point\n    virtual double heat(FEMaterialPoint& pt) = 0;\n\n    //! calculate constribution to stiffness matrix\n    virtual double stiffness(FEMaterialPoint& pt) = 0;\n\npublic:\n    void LoadVector(FEGlobalVector& R) override;\n    void StiffnessMatrix(FELinearSystem& LS) override;\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidHeatSupplyConst.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEFluidHeatSupplyConst.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFluidHeatSupplyConst, FEFluidHeatSupply)\n    ADD_PARAMETER(m_r, FE_RANGE_DONT_CARE(), \"r\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEFluidHeatSupplyConst::FEFluidHeatSupplyConst(FEModel* pfem) : FEFluidHeatSupply(pfem)\n{\n    m_r = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the body force at a material point\ndouble FEFluidHeatSupplyConst::heat(FEMaterialPoint& pt)\n{\n    return m_r;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidHeatSupplyConst.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluidHeatSupply.h\"\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body forces\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOFLUID_API FEFluidHeatSupplyConst : public FEFluidHeatSupply\n{\npublic:\n    //! constructor\n    FEFluidHeatSupplyConst(FEModel* pfem);\n\npublic:\n    //! calculate the body force at a material point\n    double heat(FEMaterialPoint& pt) override;\n\n    //! calculate constribution to stiffness matrix\n    double stiffness(FEMaterialPoint& pt) override { return 0; }\n\npublic:\n    double  m_r;    //!< specific heat supply\n        \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEFluidMaterial.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFluidMaterial, FEMaterial)\n\n    // material parameters\n    ADD_PARAMETER(m_rhor, FE_RANGE_GREATER_OR_EQUAL(0.0), \"density\")->setUnits(UNIT_DENSITY);\n\n    // material properties\n    ADD_PROPERTY(m_pViscous, \"viscous\");\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFluid\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluid constructor\n\nFEFluidMaterial::FEFluidMaterial(FEModel* pfem) : FEMaterial(pfem)\n{\n    m_rhor = 0;\n    m_pViscous = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate current fluid density\ndouble FEFluidMaterial::Density(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    return m_rhor/(vt.m_ef+1);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate current fluid kinematic viscosity\ndouble FEFluidMaterial::KinematicViscosity(FEMaterialPoint& mp)\n{\n    return m_pViscous->ShearViscosity(mp)/Density(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate current acoustic speed\ndouble FEFluidMaterial::AcousticSpeed(FEMaterialPoint& mp)\n{\n    double c = sqrt(BulkModulus(mp)/Density(mp));\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate kinetic energy density (per reference volume)\ndouble FEFluidMaterial::KineticEnergyDensity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double ked = m_rhor*(fp.m_vft*fp.m_vft)/2;\n    return ked;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain + kinetic energy density (per reference volume)\ndouble FEFluidMaterial::EnergyDensity(FEMaterialPoint& mp)\n{\n    return StrainEnergyDensity(mp) + KineticEnergyDensity(mp);\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEFluidMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FEBioMech/FEBodyForce.h>\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEViscousFluid.h\"\n#include \"FEViscousPolarFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid materials.\n\nclass FEBIOFLUID_API FEFluidMaterial : public FEMaterial\n{\npublic:\n    FEFluidMaterial(FEModel* pfem);\n    virtual ~FEFluidMaterial() {}\n    \npublic:\n    //! calculate stress at material point\n    virtual mat3ds Stress(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of stress with respect to strain J\n    virtual mat3ds Tangent_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! elastic fluid pressure\n    virtual double Pressure(FEMaterialPoint& mp) = 0;\n\n    //! tangent of elastic pressure with respect to strain J\n    virtual double Tangent_Pressure_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! 2nd tangent of elastic pressure with respect to strain J\n    virtual double Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! bulk modulus\n    virtual double BulkModulus(FEMaterialPoint& mp) = 0;\n    \n    //! strain energy density\n    virtual double StrainEnergyDensity(FEMaterialPoint& mp) = 0;\n    \n    //! invert effective pressure-dilatation relation\n    virtual bool Dilatation(const double T, const double p, double& e) = 0;\n    \n    //! evaluate temperature\n    virtual double Temperature(FEMaterialPoint& mp) = 0;\n    \n    //! return viscous part\n    FEViscousFluid* GetViscous() { return m_pViscous; }\n\n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp)  { return m_pViscous->Tangent_RateOfDeformation(mp); }\n    \n    //! referential fluid density\n    double ReferentialDensity() { return m_rhor; }\n\n    //! calculate current fluid density\n    double Density(FEMaterialPoint& pt);\n    \n    //! kinematic viscosity\n    double KinematicViscosity(FEMaterialPoint& mp);\n    \n    //! acoustic speed\n    double AcousticSpeed(FEMaterialPoint& mp);\n    \n    //! kinetic energy density\n    double KineticEnergyDensity(FEMaterialPoint& mp);\n    \n    //! strain + kinetic energy density\n    double EnergyDensity(FEMaterialPoint& mp);\n    \n    //! fluid pressure from state variables\n    virtual double Pressure(const double ef, const double T) = 0;\n    \nprivate: // material properties\n    FEViscousFluid*         m_pViscous; //!< pointer to viscous part of fluid material\n\npublic:\n    double      m_rhor;     //!< referential fluid density\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"FEFluidMaterialPoint.h\"\n\n//============================================================================\n// FEFluidMaterialPoint\n//============================================================================\n//-----------------------------------------------------------------------------\nFEFluidMaterialPoint::FEFluidMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt)\n{\n    m_pf = 0;\n    m_Lf.zero();\n    m_ef = 0;\n    m_efdot = 0;\n    m_vft = m_aft = m_gradef = vec3d(0,0,0);\n    m_sf.zero();\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEFluidMaterialPoint::Copy()\n{\n    FEFluidMaterialPoint* pt = new FEFluidMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_pf & m_Lf & m_ef & m_efdot & m_gradef & m_vft & m_aft & m_sf;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidMaterialPoint::Init()\n{\n    m_pf = 0;\n    m_Lf.zero();\n    m_ef = 0;\n    m_efdot = 0;\n    m_vft = m_aft = m_gradef = vec3d(0,0,0);\n    m_sf.zero();\n    \n    // don't forget to initialize the base class\n\tFEMaterialPointData::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Fluid material point class.\n//\nclass FEBIOFLUID_API FEFluidMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n    FEFluidMaterialPoint(FEMaterialPointData* pt = 0);\n\n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n\n    //! data serialization\n    void Serialize(DumpStream& ar);\n\n    //! Data initialization\n    void Init();\n\npublic:\n    mat3ds RateOfDeformation() const { return m_Lf.sym(); }\n    mat3da Spin() { return m_Lf.skew(); }\n    vec3d  Vorticity() const { return vec3d(m_Lf(2,1)-m_Lf(1,2), m_Lf(0,2)-m_Lf(2,0), m_Lf(1,0)-m_Lf(0,1)); }\n    \npublic:\n    // fluid data\n    vec3d       m_r0;       //!< material position\n    vec3d       m_vft;      //!< fluid velocity\n    vec3d       m_aft;      //!< fluid acceleration\n    mat3d       m_Lf;       //!< fluid velocity gradient\n    double      m_ef;       //!< fluid dilatation\n    double      m_efdot;    //!< material time derivative of ef\n    vec3d       m_gradef;   //!< gradient of ef\n    double      m_pf;       //!< elastic fluid pressure\n    mat3ds      m_sf;       //!< fluid stress\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidMixtureTractionLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidMixtureTractionLoad.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEMesh.h>\n#include \"FEBioFluid.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidMixtureTractionLoad, FESurfaceLoad)\nADD_PARAMETER(m_scale, \"scale\"   );\nADD_PARAMETER(m_TC   , \"traction\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidMixtureTractionLoad::FEFluidMixtureTractionLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_scale = 1.0;\n    m_TC = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEFluidMixtureTractionLoad::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n    m_TC.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidMixtureTractionLoad::Init()\n{\n    m_dof.Clear();\n    if (m_dof.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::DISPLACEMENT)) == false) return false;\n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the traction load\nvoid FEFluidMixtureTractionLoad::LoadVector(FEGlobalVector& R)\n{\n    m_psurf->LoadVector(R, m_dof, true, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n        \n        // fluid traction\n        vec3d t = m_TC(mp)*m_scale;\n        vec3d f = t*((mp.dxr ^ mp.dxs).norm());\n        \n        double H = dof_a.shape;\n        fa[0] = H * f.x;\n        fa[1] = H * f.y;\n        fa[2] = H * f.z;\n    });\n}\n\n//-----------------------------------------------------------------------------\n//! calculate traction stiffness (there is none)\nvoid FEFluidMixtureTractionLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n    \n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidMixtureTractionLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidTractionLoad is a fluid surface that has a prescribed\n//! viscous traction vector on it.\n//!\nclass FEBIOFLUID_API FEFluidMixtureTractionLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidMixtureTractionLoad(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \nprivate:\n    double            m_scale;    //!< magnitude of traction load\n    FEParamVec3     m_TC;        //!< traction boundary cards\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidModule.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"FEFluidModule.h\"\n#include <FECore/FEModel.h>\n#include \"FEBioFluid.h\"\n#include \"FEBioFSI.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include \"FEBioThermoFluid.h\"\n#include \"FEBioFluidSolutes.h\"\n#include \"FEBioPolarFluid.h\"\n\nFEFluidModule::FEFluidModule() {}\nvoid FEFluidModule::InitModel(FEModel* fem)\n{\n    // Allocate degrees of freedom\n    DOFS& dofs = fem->GetDOFS();\n    int varD = dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varD, 0, \"x\");\n    dofs.SetDOFName(varD, 1, \"y\");\n    dofs.SetDOFName(varD, 2, \"z\");\n\n    int nW = dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nW, 0, \"wx\");\n    dofs.SetDOFName(nW, 1, \"wy\");\n    dofs.SetDOFName(nW, 2, \"wz\");\n\n    int nE = dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), VAR_SCALAR);\n    dofs.SetDOFName(nE, 0, \"ef\");\n\n    int nAW = dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAW, 0, \"awx\");\n    dofs.SetDOFName(nAW, 1, \"awy\");\n    dofs.SetDOFName(nAW, 2, \"awz\");\n\n    int nAE = dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAE, 0, \"aef\");\n}\n\n//=============================================================================\nFEFluidFSIModule::FEFluidFSIModule() {}\nvoid FEFluidFSIModule::InitModel(FEModel* fem)\n{\n    // Allocate degrees of freedom\n    DOFS& dofs = fem->GetDOFS();\n\n    int varD = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varD, 0, \"x\");\n    dofs.SetDOFName(varD, 1, \"y\");\n    dofs.SetDOFName(varD, 2, \"z\");\n\n    int varV = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(varV, 0, \"vx\");\n    dofs.SetDOFName(varV, 1, \"vy\");\n    dofs.SetDOFName(varV, 2, \"vz\");\n\n    int varQ = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::ROTATION), VAR_VEC3);\n    dofs.SetDOFName(varQ, 0, \"u\");\n    dofs.SetDOFName(varQ, 1, \"v\");\n    dofs.SetDOFName(varQ, 2, \"w\");\n\n    int varSD = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varSD, 0, \"sx\");\n    dofs.SetDOFName(varSD, 1, \"sy\");\n    dofs.SetDOFName(varSD, 2, \"sz\");\n\n    int varQV = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(varQV, 0, \"svx\");\n    dofs.SetDOFName(varQV, 1, \"svy\");\n    dofs.SetDOFName(varQV, 2, \"svz\");\n\n    int varQA = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::SHELL_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(varQA, 0, \"sax\");\n    dofs.SetDOFName(varQA, 1, \"say\");\n    dofs.SetDOFName(varQA, 2, \"saz\");\n\n    int varQR = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RIGID_ROTATION), VAR_VEC3);\n    dofs.SetDOFName(varQR, 0, \"Ru\");\n    dofs.SetDOFName(varQR, 1, \"Rv\");\n    dofs.SetDOFName(varQR, 2, \"Rw\");\n\n    int nW = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nW, 0, \"wx\");\n    dofs.SetDOFName(nW, 1, \"wy\");\n    dofs.SetDOFName(nW, 2, \"wz\");\n\n    int nAW = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::RELATIVE_FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAW, 0, \"awx\");\n    dofs.SetDOFName(nAW, 1, \"awy\");\n    dofs.SetDOFName(nAW, 2, \"awz\");\n\n    int nVF = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nVF, 0, \"vfx\");\n    dofs.SetDOFName(nVF, 1, \"vfy\");\n    dofs.SetDOFName(nVF, 2, \"vfz\");\n\n    int nAF = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAF, 0, \"afx\");\n    dofs.SetDOFName(nAF, 1, \"afy\");\n    dofs.SetDOFName(nAF, 2, \"afz\");\n\n    int nE = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION), VAR_SCALAR);\n    dofs.SetDOFName(nE, 0, \"ef\");\n\n    int nAE = dofs.AddVariable(FEBioFSI::GetVariableName(FEBioFSI::FLUID_DILATATION_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAE, 0, \"aef\");\n}\n\n//=============================================================================\nFEMultiphasicFSIModule::FEMultiphasicFSIModule() { SetStatus(EXPERIMENTAL); }\nvoid FEMultiphasicFSIModule::InitModel(FEModel* fem)\n{\n    // Allocate degrees of freedom\n    DOFS& dofs = fem->GetDOFS();\n\n    int varD = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varD, 0, \"x\");\n    dofs.SetDOFName(varD, 1, \"y\");\n    dofs.SetDOFName(varD, 2, \"z\");\n\n    int varV = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(varV, 0, \"vx\");\n    dofs.SetDOFName(varV, 1, \"vy\");\n    dofs.SetDOFName(varV, 2, \"vz\");\n\n    int varQ = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::ROTATION), VAR_VEC3);\n    dofs.SetDOFName(varQ, 0, \"u\");\n    dofs.SetDOFName(varQ, 1, \"v\");\n    dofs.SetDOFName(varQ, 2, \"w\");\n\n    int varSD = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varSD, 0, \"sx\");\n    dofs.SetDOFName(varSD, 1, \"sy\");\n    dofs.SetDOFName(varSD, 2, \"sz\");\n\n    int varQV = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(varQV, 0, \"svx\");\n    dofs.SetDOFName(varQV, 1, \"svy\");\n    dofs.SetDOFName(varQV, 2, \"svz\");\n\n    int varQA = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(varQA, 0, \"sax\");\n    dofs.SetDOFName(varQA, 1, \"say\");\n    dofs.SetDOFName(varQA, 2, \"saz\");\n\n    int varQR = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RIGID_ROTATION), VAR_VEC3);\n    dofs.SetDOFName(varQR, 0, \"Ru\");\n    dofs.SetDOFName(varQR, 1, \"Rv\");\n    dofs.SetDOFName(varQR, 2, \"Rw\");\n\n    int nW = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nW, 0, \"wx\");\n    dofs.SetDOFName(nW, 1, \"wy\");\n    dofs.SetDOFName(nW, 2, \"wz\");\n\n    int nAW = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAW, 0, \"awx\");\n    dofs.SetDOFName(nAW, 1, \"awy\");\n    dofs.SetDOFName(nAW, 2, \"awz\");\n\n    int nVF = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nVF, 0, \"vfx\");\n    dofs.SetDOFName(nVF, 1, \"vfy\");\n    dofs.SetDOFName(nVF, 2, \"vfz\");\n\n    int nAF = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAF, 0, \"afx\");\n    dofs.SetDOFName(nAF, 1, \"afy\");\n    dofs.SetDOFName(nAF, 2, \"afz\");\n\n    int nE = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION), VAR_SCALAR);\n    dofs.SetDOFName(nE, 0, \"ef\");\n\n    int nAE = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAE, 0, \"aef\");\n\n    int varC = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), VAR_ARRAY);\n    int varAC = dofs.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION_TDERIV), VAR_ARRAY);\n}\n\n//=============================================================================\nFEThermoFluidModule::FEThermoFluidModule() { SetStatus(EXPERIMENTAL); }\nvoid FEThermoFluidModule::InitModel(FEModel* fem)\n{\n    // Allocate degrees of freedom\n    DOFS& dofs = fem->GetDOFS();\n    int varD = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varD, 0, \"x\");\n    dofs.SetDOFName(varD, 1, \"y\");\n    dofs.SetDOFName(varD, 2, \"z\");\n\n    int nW = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nW, 0, \"wx\");\n    dofs.SetDOFName(nW, 1, \"wy\");\n    dofs.SetDOFName(nW, 2, \"wz\");\n\n    int nE = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION), VAR_SCALAR);\n    dofs.SetDOFName(nE, 0, \"ef\");\n\n    int nT = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE), VAR_SCALAR);\n    dofs.SetDOFName(nT, 0, \"T\");\n\n    int nAW = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAW, 0, \"awx\");\n    dofs.SetDOFName(nAW, 1, \"awy\");\n    dofs.SetDOFName(nAW, 2, \"awz\");\n\n    int nAE = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAE, 0, \"aef\");\n\n    int nAT = dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAT, 0, \"aT\");\n}\n\n//=============================================================================\nFEPolarFluidModule::FEPolarFluidModule() { SetStatus(EXPERIMENTAL); }\nvoid FEPolarFluidModule::InitModel(FEModel* fem)\n{\n    // Allocate degrees of freedom\n    DOFS& dofs = fem->GetDOFS();\n    \n    int varD = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varD, 0, \"x\");\n    dofs.SetDOFName(varD, 1, \"y\");\n    dofs.SetDOFName(varD, 2, \"z\");\n    \n    int nW = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nW, 0, \"wx\");\n    dofs.SetDOFName(nW, 1, \"wy\");\n    dofs.SetDOFName(nW, 2, \"wz\");\n    \n    int nAW = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAW, 0, \"awx\");\n    dofs.SetDOFName(nAW, 1, \"awy\");\n    dofs.SetDOFName(nAW, 2, \"awz\");\n    \n    int nG = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nG, 0, \"gx\");\n    dofs.SetDOFName(nG, 1, \"gy\");\n    dofs.SetDOFName(nG, 2, \"gz\");\n    \n    int nAG = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAG, 0, \"agx\");\n    dofs.SetDOFName(nAG, 1, \"agy\");\n    dofs.SetDOFName(nAG, 2, \"agz\");\n    \n    int nE = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_DILATATION), VAR_SCALAR);\n    dofs.SetDOFName(nE, 0, \"ef\");\n    \n    int nAE = dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_DILATATION_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAE, 0, \"aef\");\n}\n\n//=============================================================================\nFEFluidSolutesModule::FEFluidSolutesModule() { SetStatus(RELEASED); }\nvoid FEFluidSolutesModule::InitModel(FEModel* fem)\n{\n    // Allocate degrees of freedom\n    DOFS& dofs = fem->GetDOFS();\n    int varD = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::DISPLACEMENT), VAR_VEC3);\n    dofs.SetDOFName(varD, 0, \"x\");\n    dofs.SetDOFName(varD, 1, \"y\");\n    dofs.SetDOFName(varD, 2, \"z\");\n\n    int nW = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY), VAR_VEC3);\n    dofs.SetDOFName(nW, 0, \"wx\");\n    dofs.SetDOFName(nW, 1, \"wy\");\n    dofs.SetDOFName(nW, 2, \"wz\");\n\n    int nE = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION), VAR_SCALAR);\n    dofs.SetDOFName(nE, 0, \"ef\");\n\n    int nAW = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_ACCELERATION), VAR_VEC3);\n    dofs.SetDOFName(nAW, 0, \"awx\");\n    dofs.SetDOFName(nAW, 1, \"awy\");\n    dofs.SetDOFName(nAW, 2, \"awz\");\n\n    int nAE = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION_TDERIV), VAR_SCALAR);\n    dofs.SetDOFName(nAE, 0, \"aef\");\n\n    int varC = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), VAR_ARRAY);\n    int varAC = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV), VAR_ARRAY);\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidModule.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEModule.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEFluidModule : public FEModule\n{\npublic:\n\tFEFluidModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n\nclass FEBIOFLUID_API FEFluidFSIModule : public FEModule\n{\npublic:\n\tFEFluidFSIModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n\nclass FEBIOFLUID_API FEMultiphasicFSIModule : public FEModule\n{\npublic:\n\tFEMultiphasicFSIModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n\nclass FEBIOFLUID_API FEThermoFluidModule : public FEModule\n{\npublic:\n\tFEThermoFluidModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n\nclass FEBIOFLUID_API FEPolarFluidModule : public FEModule\n{\npublic:\n    FEPolarFluidModule();\n    void InitModel(FEModel* fem) override;\n};\n\nclass FEBIOFLUID_API FEFluidSolutesModule : public FEModule\n{\npublic:\n\tFEFluidSolutesModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidMovingFrameLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFluidMovingFrameLoad.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEFluidMovingFrameLoad, FEBodyForce)\n    ADD_PARAMETER(m_at.x, \"ax\");\n    ADD_PARAMETER(m_at.y, \"ay\");\n    ADD_PARAMETER(m_at.z, \"az\");\n\tADD_PARAMETER(m_wi.x, \"wx\");\n\tADD_PARAMETER(m_wi.y, \"wy\");\n\tADD_PARAMETER(m_wi.z, \"wz\");\n\tADD_PARAMETER(m_frame, \"frame\")->setEnums(\"fixed\\0moving\\0\");\n\n\tADD_PARAMETER(m_wt, \"wt\")->setLongName(\"Angular velocity in fixed frame\")->setUnits(UNIT_ANGULAR_VELOCITY)->SetFlags(FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_Wt, \"Wt\")->setLongName(\"Angular velocity in moving frame\")->setUnits(UNIT_ANGULAR_VELOCITY)->SetFlags(FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_rt, \"rt\")->setLongName(\"Rotation vector in fixed frame\")->setUnits(UNIT_RADIAN)->SetFlags(FE_PARAM_HIDDEN);\nEND_FECORE_CLASS();\n\nFEFluidMovingFrameLoad::FEFluidMovingFrameLoad(FEModel* fem) : FEBodyForce(fem)\n{\n\tm_frame = 0; // assume angular velocity is input in fixed frame\n}\n\nvoid FEFluidMovingFrameLoad::Activate()\n{\n\tm_ap = m_at;\n\tm_wp = m_wt;\n\tm_Wp = m_Wt;\n\tm_alp = m_alt;\n\tm_Alp = m_Alt;\n\tm_qp = m_qt;\n\tFEBodyForce::Activate();\n}\n\nvoid FEFluidMovingFrameLoad::PrepStep()\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble dt = tp.timeIncrement;\n\tdouble alpha = tp.alpha;\n\n\tif (m_frame == 0)\n\t{\n\t\tm_wt = m_wi; // input is ang vel in fixed frame\n\n\t\t// current quantities\n\t\tm_alp = m_alt;\n\t\tm_alt = (m_wt - m_wp) / dt;\n\t\tquatd wt(m_wt.x, m_wt.y, m_wt.z, 0.0);\n\t\tm_qt = m_qp + (wt * m_qp) * (0.5 * dt); m_qt.MakeUnit();\n\n\t\t// intermediate quantities\n\t\tm_al = m_alt * alpha + m_alp * (1.0 - alpha);\n\t\tm_w = m_wt * alpha + m_wp * (1.0 - alpha);\n\t\tquatd wa(m_w.x, m_w.y, m_w.z, 0.0);\n\t\tm_q = m_qp + (wa * m_qp) * (0.5 * dt * alpha); m_q.MakeUnit();\n\n\t\tm_qp = m_qt;\n\t\tm_wp = m_wt;\n\n\t\t// quantities in body frame\n\t\tquatd qti = m_qt.Inverse();\n\t\tm_Wt = qti * m_wt;\n\t\tm_rt = m_qt.GetRotationVector();\n\n\t\tquatd qi = m_q.Inverse();\n\t\tm_Al = qi * m_al;\n\t\tm_W = qi * m_w;\n\t}\n\telse\n\t{\n\t\tm_Wt = m_wi; // input is ang vel in body frame\n\n\t\t// current quantities\n\t\tm_Alp = m_Alt;\n\t\tm_Alt = (m_Wt - m_Wp) / dt;\n\t\tquatd Wt(m_Wt.x, m_Wt.y, m_Wt.z, 0.0);\n\t\tm_qt = m_qp + (m_qp * Wt) * (0.5 * dt); m_qt.MakeUnit();\n\n\t\t// intermediate quantities\n\t\tm_Al = m_Alt * alpha + m_Alp * (1.0 - alpha);\n\t\tm_W = m_Wt * alpha + m_Wp * (1.0 - alpha);\n\t\tquatd Wa(m_W.x, m_W.y, m_W.z, 0.0);\n\t\tm_q = m_qp + (m_qp * Wa) * (0.5 * dt * alpha); m_q.MakeUnit();\n\n\t\tm_qp = m_qt;\n\t\tm_Wp = m_Wt;\n\n\t\t// quantities in fixed frame\n\t\tm_wt = m_qt * m_Wt;\n\t\tm_rt = m_qt.GetRotationVector();\n\t\tm_alp = m_alt;\n\t\tm_alt = m_qt * m_Alt;\n\n\t\tm_al = m_q * m_Al;\n\t\tm_w = m_q * m_W;\n\t}\n\n\t// linear acceleration (always assumed to be given in the fixed frame)\n\tm_a = m_at * alpha + m_ap * (1.0 - alpha);\n\tm_ap = m_at;\n\tquatd qi = m_q.Inverse();\n\tm_A = qi * m_a;\n\n\tFEBodyForce::PrepStep();\n}\n\nvec3d FEFluidMovingFrameLoad::force(FEMaterialPoint& pt)\n{\n\tFEFluidMaterialPoint& fp = *pt.ExtractData<FEFluidMaterialPoint>();\n\n\tvec3d r = pt.m_r0;\n\tvec3d V = fp.m_vft;\n\n\tvec3d f = m_A + (m_Al ^ r) + (m_W ^ (m_W ^ r)) + (m_W ^ V) * 2.0;\n\treturn f;\n}\n\nmat3d FEFluidMovingFrameLoad::stiffness(FEMaterialPoint& pt)\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\tmat3da Sw(m_W);\n\tmat3d K = Sw* (2.0 * tp.alphaf);\n\n\treturn K;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidMovingFrameLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FEBioMech/FEBodyForce.h>\n#include <FECore/quatd.h>\n#include <FECore/FEFunction1D.h>\n\n//! This body load emulates the effect of a moving frame\nclass FEFluidMovingFrameLoad : public FEBodyForce\n{\npublic:\n\tFEFluidMovingFrameLoad(FEModel* fem);\n\n\tvoid Activate() override;\n\n\tvoid PrepStep() override;\n\n\tvec3d force(FEMaterialPoint& pt) override;\n\n\tmat3d stiffness(FEMaterialPoint& pt) override;\n\nprivate:\n\tvec3d\tm_at;\t// linear acceleration of moving frame in fixed frame\n\tvec3d\tm_wi;\t// angular velocity input\n\tint\t\tm_frame;\t// fixed (=0), or moving (=1) frame for input angular velocity\n\nprivate:\n\tvec3d\tm_ap, m_a, m_A;\n\tvec3d\tm_wp, m_w, m_Wp, m_W;\n\tvec3d\tm_alt, m_alp, m_al, m_Alt, m_Alp, m_Al;\n\tquatd\tm_qt, m_qp, m_q;\n\n\t// output parameters\n\tvec3d m_wt; // angular velocity in fixed frame (output)\n\tvec3d m_Wt; // angular velocity in moving frame (output)\n\tvec3d m_rt; // rotational vector in fixed frame (output)\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidNaturalHeatFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidNaturalHeatFlux.h\"\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/log.h>\n#include <FECore/LinearSolver.h>\n#include <FECore/FEModel.h>\n#include \"FEBioThermoFluid.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidNaturalHeatFlux::FEFluidNaturalHeatFlux(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEFluidNaturalHeatFlux::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidNaturalHeatFlux::Init()\n{\n    // get the degrees of freedom\n    m_dof.Clear();\n    m_dof.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE));\n    if (m_dof.IsEmpty()) return false;\n\n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the prescribed normal velocity\nvoid FEFluidNaturalHeatFlux::LoadVector(FEGlobalVector& R)\n{\n    m_psurf->LoadVector(R, m_dof, true, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, std::vector<double>& fa) {\n        \n        const FETimeInfo& tp = GetTimeInfo();\n        \n        // get surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        // get underlying solid element\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        vec3d dxt = mp.dxr ^ mp.dxs;\n\n        // get element-averaged heat flux\n        vec3d q(0,0,0);\n        int nint = pe->GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEFluidMaterialPoint* pf = pt.ExtractData<FEFluidMaterialPoint>();\n            if (pf == nullptr) break;\n            FEThermoFluidMaterialPoint* ps = pt.ExtractData<FEThermoFluidMaterialPoint>();\n            if (ps == nullptr) break;\n//            if (ps->m_q*pf->m_vft > 0) q += ps->m_q;\n            q += ps->m_q;\n        }\n        q /= nint;\n        \n        double qn = dxt*q;\n        double H = dof_a.shape;\n        \n        fa[0] = qn*H;\n    });\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidNaturalHeatFlux::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidNaturalHeatFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidNormalHeatFlux is a thermo-fluid surface that has a normal\n//! heat flux prescribed on it, equal to what's coming underneath it\nclass FEBIOFLUID_API FEFluidNaturalHeatFlux : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidNaturalHeatFlux(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! calculate heat flux stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidNormalHeatFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidNormalHeatFlux.h\"\n#include <FECore/FEElemElemList.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/log.h>\n#include <FECore/LinearSolver.h>\n#include \"FEBioThermoFluid.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidNormalHeatFlux, FESurfaceLoad)\n    ADD_PARAMETER(m_flux    , \"flux\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidNormalHeatFlux::FEFluidNormalHeatFlux(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_flux = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidNormalHeatFlux::Init()\n{\n    // get the degrees of freedom\n    m_dof.Clear();\n    m_dof.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE));\n    if (m_dof.IsEmpty()) return false;\n\n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the prescribed normal velocity\nvoid FEFluidNormalHeatFlux::LoadVector(FEGlobalVector& R)\n{\n    FESurface& surf = GetSurface();\n\n    // evaluate the integral\n    surf.LoadVector(R, m_dof, false, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n        \n        // evaluate pressure at this material point\n        double q = m_flux(pt);\n\n        double J = (pt.dxr ^ pt.dxs).norm();\n\n        double H = dof_a.shape;\n\n        val[0] = H*q*J;\n    });\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidNormalHeatFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidNormalHeatFlux is a thermo-fluid surface that has a normal\n//! heat flux prescribed on it.\nclass FEBIOFLUID_API FEFluidNormalHeatFlux : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidNormalHeatFlux(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! calculate heat flux stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \nprotected:\n    FEParamDouble   m_flux;     //!< heat flux\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidNormalTraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidNormalTraction.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FEFacetSet.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidNormalTraction, FESurfaceLoad)\n\tADD_PARAMETER(m_traction, \"traction\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidNormalTraction::FEFluidNormalTraction(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n\tm_traction = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEFluidNormalTraction::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n\tm_traction.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEFluidNormalTraction::Init()\n{\n\tm_dofW.Clear();\n\tif (m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY))) return false;\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidNormalTraction::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofW;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the traction load\nvoid FEFluidNormalTraction::LoadVector(FEGlobalVector& R)\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\t// evaluate integral over surface\n\tm_psurf->LoadVector(R, m_dofW, false, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape &dof_a, vector<double>& fa) {\n\n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tm_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n\n\t\t// calculate the tangent vectors\n\t\tvec3d dxr = el.eval_deriv1(rt, mp.m_index);\n\t\tvec3d dxs = el.eval_deriv2(rt, mp.m_index);\n\t\tvec3d normal = dxr ^ dxs;\n\n\t\tdouble tn = m_traction(mp);\n\t\tvec3d f = normal*tn;\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * f.x;\n\t\tfa[1] = H * f.y;\n\t\tfa[2] = H * f.z;\n\t});\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidNormalTraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidNormalTraction is a fluid surface that has a normal\n//! viscous traction prescribed on it.\n//!\nclass FEBIOFLUID_API FEFluidNormalTraction : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidNormalTraction(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \nprivate:\n    FEParamDouble\tm_traction;\t//!< magnitude of traction load\n\nprivate:\n\tFEDofList\tm_dofW;\t// relative fluid velocity dofs\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidNormalVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidNormalVelocity.h\"\n#include \"FECore/FEElemElemList.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/FEGlobalVector.h\"\n#include \"FECore/log.h\"\n#include \"FECore/LinearSolver.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEParabolicMap.h>\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEMesh.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidNormalVelocity, FESurfaceLoad)\n\tADD_PARAMETER(m_velocity, \"velocity\"                  )->setUnits(UNIT_VELOCITY);\n    ADD_PARAMETER(m_bpv     , \"prescribe_nodal_velocities\");\n    ADD_PARAMETER(m_bpar    , \"parabolic\");\n    ADD_PARAMETER(m_brim    , \"prescribe_rim_pressure\"    );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidNormalVelocity::FEFluidNormalVelocity(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem), m_dofEF(pfem)\n{\n    m_velocity = 0.0;\n    m_bpv = true;\n    m_bpar = false;\n    m_brim = false;\n\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofEF.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFluidNormalVelocity::NormalVelocity(FESurfaceMaterialPoint& mp)\n{\n    FESurfaceElement& el = *mp.SurfaceElement();\n    double vn = 0;\n    double* N = mp.m_shape;\n    if (N == nullptr) return 0;\n    int neln = el.Nodes();\n    for (int i = 0; i < neln; ++i)\n    {\n        vn += N[i] * m_VN[el.m_lnode[i]];\n    }\n    return vn;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the prescribed normal velocity\nvoid FEFluidNormalVelocity::LoadVector(FEGlobalVector& R)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n\tm_psurf->LoadVector(R, m_dofEF, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\n\t\t// nodal coordinates\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tm_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n\n\t\t// calculate the tangent vectors\n\t\tvec3d dxr = el.eval_deriv1(rt, mp.m_index);\n\t\tvec3d dxs = el.eval_deriv2(rt, mp.m_index);\n\n\t\t// normal velocity\n\t\tdouble vn = NormalVelocity(mp);\n\t\tdouble da = (dxr ^ dxs).norm();\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * vn * da;\n\t});\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFluidNormalVelocity::ScalarLoad(FESurfaceMaterialPoint& mp)\n{\n    return m_velocity(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidNormalVelocity::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    m_dof.Clear();\n    m_dof.AddDofs(m_dofW);\n    m_dof.AddDofs(m_dofEF);\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEFluidNormalVelocity::Activate()\n{\n\tFESurface& surface = GetSurface();\n\n    // This is needed to reproduce the same behavior as feb3. \n    FESurfaceMap VC(FE_DOUBLE); \n    VC.Create(surface.GetFacetSet(), 1.0);\n\n\t// evaluate surface normals\n\tvector<vec3d> sn(surface.Elements(), vec3d(0, 0, 0));\n\tm_nu.resize(surface.Nodes(), vec3d(0, 0, 0));\n    m_VN.resize(surface.Nodes(), 0.0);\n    vector<int> nf(surface.Nodes(), 0);\n\tvec3d r0[FEElement::MAX_NODES];\n\tfor (int iel = 0; iel< surface.Elements(); ++iel)\n\t{\n\t\tFESurfaceElement& el = surface.Element(iel);\n\n\t\t// nr integration points\n\t\tint nint = el.GaussPoints();\n\n\t\t// nr of element nodes\n\t\tint neln = el.Nodes();\n\n\t\t// nodal coordinates\n\t\tfor (int i = 0; i<neln; ++i) {\n\t\t\tr0[i] = surface.Node(el.m_lnode[i]).m_r0;\n            m_VN[el.m_lnode[i]] += VC.value<double>(iel, i);\n            ++nf[el.m_lnode[i]];\n\t\t}\n\n\t\tdouble* Nr, *Ns;\n\n\t\tvec3d dxr, dxs;\n\n\t\t// repeat over integration points\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tNr = el.Gr(n);\n\t\t\tNs = el.Gs(n);\n\n\t\t\t// calculate the tangent vectors at integration point\n\t\t\tdxr = dxs = vec3d(0, 0, 0);\n\t\t\tfor (int i = 0; i<neln; ++i)\n\t\t\t{\n\t\t\t\tdxr += r0[i] * Nr[i];\n\t\t\t\tdxs += r0[i] * Ns[i];\n\t\t\t}\n\n\t\t\tsn[iel] = dxr ^ dxs;\n\t\t\tsn[iel].unit();\n\t\t}\n\n\t\t// evaluate nodal normals by averaging surface normals\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t\tm_nu[el.m_lnode[i]] += sn[iel];\n\t}\n\n\tfor (int i = 0; i< surface.Nodes(); ++i) {\n\t\tm_nu[i].unit();\n        m_VN[i] /= nf[i];\n    }\n\n    // Set up data structure for setting rim pressure\n    if (m_brim) {\n        // find rim nodes (rim)\n        FEElemElemList EEL;\n        EEL.Create(&surface);\n        \n        vector<bool> boundary(surface.Nodes(), false);\n        for (int i=0; i< surface.Elements(); ++i) {\n            FESurfaceElement& el = surface.Element(i);\n            for (int j=0; j<el.facet_edges(); ++j) {\n                FEElement* nel = EEL.Neighbor(i, j);\n                if (nel == nullptr) {\n                    int en[3] = {-1,-1,-1};\n                    el.facet_edge(j, en);\n                    boundary[en[0]] = true;\n                    boundary[en[1]] = true;\n                    if (en[2] > -1) boundary[en[2]] = true;\n                }\n            }\n        }\n        \n        // store boundary nodes for which the velocity DOFs are fixed\n        // which defines the rim on which the dilatation needs to be prescribed\n        m_rim.reserve(surface.Nodes());\n        for (int i=0; i<surface.Nodes(); ++i)\n            if (boundary[i]) {\n                FENode& node = surface.Node(i);\n                if ((node.get_bc(m_dofW[0]) == DOF_FIXED) &&\n                    (node.get_bc(m_dofW[1]) == DOF_FIXED) &&\n                    (node.get_bc(m_dofW[2]) == DOF_FIXED))\n                    m_rim.push_back(i);\n            }\n        \n        if (m_rim.size() == surface.Nodes())\n        {\n            feLogError(\"Unable to set rim pressure\\n\");\n        }\n    }\n    \n    // Set parabolic velocity profile if requested.\n    // Note that this may override any maps assigned to velocity\n    if (m_bpar)\n    {\n        if (SetParabolicVelocity() == false)\n        {\n            feLogError(\"Unable to set parabolic velocity\\n\");\n        }\n    }\n\n    if (m_bpv) {\n        for (int i = 0; i < surface.Nodes(); ++i)\n        {\n            FENode& node = surface.Node(i);\n            // mark node as having prescribed DOF\n            if (node.get_bc(m_dofW[0]) != DOF_FIXED) node.set_bc(m_dofW[0], DOF_PRESCRIBED);\n            if (node.get_bc(m_dofW[1]) != DOF_FIXED) node.set_bc(m_dofW[1], DOF_PRESCRIBED);\n            if (node.get_bc(m_dofW[2]) != DOF_FIXED) node.set_bc(m_dofW[2], DOF_PRESCRIBED);\n        }\n    }\n    \n    if (m_brim) {\n        for (int i=0; i<m_rim.size(); ++i) {\n            FENode& node = surface.Node(m_rim[i]);\n            // mark node as having prescribed DOF\n            if (node.get_bc(m_dofEF[0]) != DOF_FIXED) node.set_bc(m_dofEF[0], DOF_PRESCRIBED);\n        }\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the velocities\nvoid FEFluidNormalVelocity::Update()\n{\n    // prescribe this velocity at the nodes\n    FESurface* ps = &GetSurface();\n\n    // The velocity map is read in as surface data with MULT format. \n    // However, we need to have (unique) values at the nodes\n    // so we need to convert this to nodal data. \n    int N = ps->Nodes();\n    m_VN.assign(N, 0.0);\n    vector<int> tag(N, 0);\n    for (int i = 0; i < ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        for (int j = 0; j < el.Nodes(); ++j)\n        {\n            FEMaterialPoint mp;\n            mp.m_elem = &el;\n            mp.m_index = j + 0x10000;\n\n            double vnj = m_velocity(mp);\n\n            m_VN[el.m_lnode[j]] += vnj;\n            tag[el.m_lnode[j]]++;\n        }\n    }\n    for (int i = 0; i < N; ++i)\n    {\n        if (tag[i] != 0) m_VN[i] /= (double)tag[i];\n    }\n  \n    if (m_bpv) {\n\n        for (int i=0; i<ps->Nodes(); ++i)\n        {\n            // evaluate the velocity\n            vec3d v = m_nu[i]*m_VN[i];\n            FENode& node = ps->Node(i);\n            if (node.get_bc(m_dofW[0]) == DOF_PRESCRIBED) node.set(m_dofW[0], v.x);\n            if (node.get_bc(m_dofW[1]) == DOF_PRESCRIBED) node.set(m_dofW[1], v.y);\n            if (node.get_bc(m_dofW[2]) == DOF_PRESCRIBED) node.set(m_dofW[2], v.z);\n        }\n    }\n    \n    if (m_brim) SetRimPressure();\n    \n    ForceMeshUpdate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate normal velocities by solving Poiseuille flow across the surface\nbool FEFluidNormalVelocity::SetParabolicVelocity()\n{\n\tFEFacetSet* surf = GetSurface().GetFacetSet(); assert(surf);\n\tif (surf == nullptr) return false;\n\n\tFEParabolicMap gen(GetFEModel());\n\tgen.SetFacetSet(surf);\n\n\t// only consider fluid dofs\n\tgen.SetDOFConstraint(m_dofW);\n\n\tFEDataMap* map = gen.Generate();\n\tif (map == nullptr)\n\t{\n\t\tassert(false);\n\t\treturn false;\n\t}\n\tGetMesh().AddDataMap(map);\n\n    FEMappedValue* val = fecore_alloc(FEMappedValue, GetFEModel());\n    val->setDataMap(map);\n\n    if (m_velocity.isConst() == false) return false;\n    double vn = m_velocity.constValue();\n    val->setScaleFactor(vn);\n\n    m_velocity.setValuator(val);\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe rim pressure\nbool FEFluidNormalVelocity::SetRimPressure()\n{\n    FESurface* ps = &GetSurface();\n    \n    // evaluate dilatation everywhere but rim\n    double es = 0;\n    int neq = 0;\n    for (int i=0; i<ps->Nodes(); ++i) {\n        FENode& node = ps->Node(i);\n        if (node.get_bc(m_dofEF[0]) == DOF_OPEN) {\n            es += node.get(m_dofEF[0]);\n            ++neq;\n        }\n    }\n    es /= neq;\n\n    // set the rim pressure (dilatation)\n    for (int i=0; i<m_rim.size(); ++i) {\n        FENode& node = ps->Node(m_rim[i]);\n        node.set(m_dofEF[0],es);\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! serializatsion\nvoid FEFluidNormalVelocity::Serialize(DumpStream& ar)\n{\n\tFESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n\tar & m_nu & m_VN;\n    ar & m_rim;\n    ar & m_dofW & m_dofEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidNormalVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FESurfaceMap.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidNormalVelocity is a fluid surface that has a normal\n//! velocity prescribed on it.  This routine simultaneously prescribes a\n//! surface load and nodal prescribed velocities\nclass FEBIOFLUID_API FEFluidNormalVelocity : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidNormalVelocity(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n\t//! serializatsion\n\tvoid Serialize(DumpStream& ar) override;\n    \n    //! set the velocity\n    void Update() override;\n    \n    //! initialization\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! parabolic velocity profile\n    bool SetParabolicVelocity();\n\n    //! rim pressure\n    bool SetRimPressure();\n    \n    double ScalarLoad(FESurfaceMaterialPoint& mp) override;\n\nprivate:\n    double NormalVelocity(FESurfaceMaterialPoint& mp);\n\nprivate:\n    FEParamDouble\tm_velocity;\t//!< average velocity\n    bool            m_brim;     //!< flag for setting rim pressure\n\n    // obsolete parameters\n    bool            m_bpv;      //!< flag for prescribing nodal values\n    bool            m_bpar;\n\nprivate:\n    vector<vec3d>   m_nu;       //!< nodal normals\n    vector<double>  m_VN;       //!< nodal values for velocity\n    vector<int>     m_rim;      //!< list of nodes on the rim\n\n\tFEDofList\tm_dofW;\n    FEDofList   m_dofEF;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidPressureLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidPressureLoad.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidPressureLoad, FESurfaceLoad)\nADD_PARAMETER(m_p0    , \"pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFluidPressureLoad::FEFluidPressureLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_pfluid = nullptr;\n    m_p0 = 0;\n    \n    m_dofEF = (pfem ? pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0) : -1);\n    \n    m_dof.Clear();\n    m_dof.AddDof(m_dofEF);\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidPressureLoad::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n    if (m_pfluid == nullptr) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEFluidPressureLoad::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofEF, DOF_PRESCRIBED);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidPressureLoad::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofEF] < -1)\n        {\n            FENode& node = ps->Node(i);\n            // calculate the dilatation\n            double e = node.get(m_dofEF);\n            bool good = m_pfluid->Dilatation(0,m_p0,e);\n            assert(good);\n            // set node as having prescribed DOF\n            node.set(m_dofEF, e);\n        }\n    }\n    \n    GetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidPressureLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofEF;\n    ar & m_pfluid;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidPressureLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluid.h\"\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEConstraintNormalFlow class implements a fluid surface with zero\n//! tangential velocity as a linear constraint.\n\nclass FEBIOFLUID_API FEFluidPressureLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidPressureLoad(FEModel* pfem);\n    \n    //! destructor\n    ~FEFluidPressureLoad() {}\n    \n    //! calculate traction stiffness (there is none for this load)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector (there is none for this load)\n    void LoadVector(FEGlobalVector& R) override {}\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    int             m_dofEF;\n    FEFluidMaterial*    m_pfluid;   //!< pointer to fluid\n    \npublic:\n    double  m_p0;       // prescribed pressure\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidRCBC.h\"\n#include \"FEFluid.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidRCBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_p0, \"initial_pressure\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_C , \"capacitance\")->setUnits(\"L^5/F\");\n    ADD_PARAMETER(m_Bern, \"Bernoulli\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidRCBC::FEFluidRCBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_pfluid = nullptr;\n    m_psurf = nullptr;\n    m_p0 = 0;\n    m_C = 0.0;\n    m_Bern = false;\n    m_e = 0.0;\n    m_pt = m_dpt = 0;\n    m_qt = m_dqt = 0;\n    m_pp = m_dpp = 0;\n    m_qp = m_dqp = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\n//! TODO: Generalize to include the initial conditions\nbool FEFluidRCBC::Init()\n{\n    m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n\n    m_psurf = GetSurface();\n    \n    SetDOFList(m_dofEF);\n\n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n    if (m_pfluid == nullptr) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidRCBC::UpdateDilatation()\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    double dt = tp.timeIncrement;\n    double gamma = tp.gamma;\n    double omoog = 1.0 - 1.0/gamma;\n    int niter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n\n    // if this is the start of a new time step, update the flow/pressure parameters\n    if (niter == 0) {\n        m_qp = m_qt;\n        m_dqp = m_dqt*omoog;\n        m_pp = m_pt;\n        m_dpp = m_dpt*omoog;\n    }\n\n    // evaluate the flow rate\n    m_qt = FlowRate();\n    \n    // evaluate the outflow pressure\n    double qt = m_Bern ? m_qt*fabs(m_qt) : m_qt;\n    double qp = m_Bern ? m_qp*fabs(m_qp) : m_qp;\n    m_dqt = m_dqp*omoog + (qt-qp)/gamma/dt;\n    m_dpt = m_dpp*omoog + (m_pt-m_pp)/gamma/dt;\n    m_pt = m_pp + (m_R*m_dqt + m_qt/m_C - m_dpp*omoog)*gamma*dt;\n    \n    // calculate the dilatation\n    m_e = 0.0;\n    bool good = m_pfluid->Dilatation(0,m_pt + m_p0, m_e);\n    assert(good);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidRCBC::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::PrepStep(ui, brel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidRCBC::Update()\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::Update();\n\n\t// TODO: Is this necessary?\n\tGetFEModel()->SetMeshUpdateFlag(true);\n}\n\nvoid FEFluidRCBC::UpdateModel() { Update(); }\n\n//-----------------------------------------------------------------------------\n// return the value for node i, dof j\nvoid FEFluidRCBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e;\n\tFENode& node = GetMesh().Node(m_nodeList[nodelid]);\n\tnode.set(m_dofEF, m_e);\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidRCBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface\ndouble FEFluidRCBC::FlowRate()\n{\n    double Q = 0;\n    \n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n    \n    return Q;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidRCBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_pt & m_dpt & m_qt & m_dqt & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n    ar & m_psurf;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidRCBC is a fluid surface that has an RC-equivalent circuit for outflow conditions\n//!\nclass FEBIOFLUID_API FEFluidRCBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidRCBC(FEModel* pfem);\n    \n    //! evaluate flow rate\n    double FlowRate();\n    \n    //! initialize\n    bool Init() override;\n\n    //! Update \n    void Update() override;\n\tvoid UpdateModel() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n\tvoid PrepStep(std::vector<double>& ui, bool brel) override;\n\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n\t//! set the dilatation\n\tvoid UpdateDilatation();\n\nprivate:\n    double          m_R;        //!< flow resistance\n    double          m_p0;       //!< initial fluid pressure\n    double          m_C;        //!< capacitance\n    bool            m_Bern;     //!< Use Bernoulli's Relation (Q*|Q|)\n\n    double          m_qt;       //!< flow rate at current time step\n    double          m_dqt;      //!< flow rate time derivative at current time step\n    double          m_pt;       //!< pressure at current time step\n    double          m_dpt;      //!< pressure derivative at current time step\n    double          m_qp;       //!< flow rate at previous time step\n    double          m_dqp;      //!< flow rate time derivative at previous time step\n    double          m_pp;       //!< pressure at previoust time step\n    double          m_dpp;      //!< pressure derivative at previoust time step\n    double          m_e;\n    \nprivate:\n    FEFluidMaterial*    m_pfluid;   //!< pointer to fluid\n    FESurface*          m_psurf;    //!< pointer to surface\n\n    FEDofList   m_dofW;\n    int         m_dofEF;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidRCLoad.h\"\n#include \"FEFluid.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidRCLoad, FESurfaceLoad)\n    ADD_PARAMETER(m_R, \"R\");\n    ADD_PARAMETER(m_p0, \"initial_pressure\");\n    ADD_PARAMETER(m_C, \"capacitance\");\n    ADD_PARAMETER(m_Bern, \"Bernoulli\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidRCLoad::FEFluidRCLoad(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_pfluid = nullptr;\n    m_p0 = 0;\n    m_C = 0.0;\n    m_Bern = false;\n\n    m_pt = m_dpt = 0;\n    m_qt = m_dqt = 0;\n    m_pp = m_dpp = 0;\n    m_qp = m_dqp = 0;\n\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofEF = pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\n//! TODO: Generalize to include the initial conditions\nbool FEFluidRCLoad::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofW);\n    m_dof.AddDof(m_dofEF);\n\n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n\n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n    if (m_pfluid == nullptr) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEFluidRCLoad::Activate()\n{\n    FESurface* ps = &GetSurface();\n\n    for (int i = 0; i < ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofEF, DOF_PRESCRIBED);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidRCLoad::Update()\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    double dt = tp.timeIncrement;\n    double gamma = tp.gamma;\n    double omoog = 1 - 1. / gamma;\n    int niter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n\n    // if this is the start of a new time step, update the flow/pressure parameters\n    if (niter == 0) {\n        m_qp = m_qt;\n        m_dqp = m_dqt * omoog;\n        m_pp = m_pt;\n        m_dpp = m_dpt * omoog;\n    }\n\n    // evaluate the flow rate\n    m_qt = FlowRate();\n\n    // evaluate the outflow pressure\n    double qt = m_Bern ? m_qt * fabs(m_qt) : m_qt;\n    double qp = m_Bern ? m_qp * fabs(m_qp) : m_qp;\n    m_dqt = m_dqp * omoog + (qt - qp) / gamma / dt;\n    m_dpt = m_dpp * omoog + (m_pt - m_pp) / gamma / dt;\n    m_pt = m_pp + (m_R * m_dqt + m_qt / m_C - m_dpp * omoog) * gamma * dt;\n\n    // calculate the dilatation\n    double e = 0;\n    bool good = m_pfluid->Dilatation(0, m_pt + m_p0, e);\n    assert(good);\n\n    // prescribe this dilatation at the nodes\n    FESurface* ps = &GetSurface();\n\n    for (int i = 0; i < ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofEF] < -1)\n        {\n            FENode& node = ps->Node(i);\n            // set node as having prescribed DOF\n            node.set(m_dofEF, e);\n        }\n    }\n\n    GetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface\ndouble FEFluidRCLoad::FlowRate()\n{\n    double Q = 0;\n\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n\n    for (int iel = 0; iel < m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n\n        // nr integration points\n        int nint = el.GaussPoints();\n\n        // nr of element nodes\n        int neln = el.Nodes();\n\n        // nodal coordinates\n        for (int i = 0; i < neln; ++i) {\n            FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n\n        double* Nr, * Ns;\n        double* N;\n        double* w = el.GaussWeights();\n\n        vec3d dxr, dxs, v;\n\n        // repeat over integration points\n        for (int n = 0; n < nint; ++n)\n        {\n            N = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n\n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0, 0, 0);\n            for (int i = 0; i < neln; ++i)\n            {\n                v += vt[i] * N[i];\n                dxr += rt[i] * Nr[i];\n                dxs += rt[i] * Ns[i];\n            }\n\n            vec3d normal = dxr ^ dxs;\n            double q = normal * v;\n            Q += q * w[n];\n        }\n    }\n\n    return Q;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate residual\nvoid FEFluidRCLoad::LoadVector(FEGlobalVector& R)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidRCLoad::Serialize(DumpStream& ar)\n{\n\tFESurfaceLoad::Serialize(ar);\n    ar & m_pt & m_dpt & m_qt & m_dqt;\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidRCLoad is a fluid surface that has an RC-equivalent circuit for outflow conditions\n//!\nclass FEBIOFLUID_API FEFluidRCLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidRCLoad(FEModel* pfem);\n\n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n\n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n\n    //! set the dilatation\n    void Update() override;\n\n    //! evaluate flow rate\n    double FlowRate();\n\n    //! initialize\n    bool Init() override;\n\n    //! activate\n    void Activate() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\nprivate:\n    double          m_R;        //!< flow resistance\n    double          m_p0;       //!< initial fluid pressure\n    double          m_C;        //!< capacitance\n    bool            m_Bern;     //!< Use Bernoulli's Relation (Q*|Q|)\n\n    double          m_qt;       //!< flow rate at current time step\n    double          m_dqt;      //!< flow rate time derivative at current time step\n    double          m_pt;       //!< pressure at current time step\n    double          m_dpt;      //!< pressure derivative at current time step\n    double          m_qp;       //!< flow rate at previous time step\n    double          m_dqp;      //!< flow rate time derivative at previous time step\n    double          m_pp;       //!< pressure at previoust time step\n    double          m_dpp;      //!< pressure derivative at previoust time step\n\nprivate:\n    FEFluidMaterial* m_pfluid;   //!< pointer to fluid\n\n    FEDofList   m_dofW;\n    int         m_dofEF;\n\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCRBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidRCRBC.h\"\n#include \"FEFluid.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidRCRBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_Rd, \"Rd\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_p0, \"initial_pressure\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_pd, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_C , \"capacitance\")->setUnits(\"L^5/F\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidRCRBC::FEFluidRCRBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_pfluid = nullptr;\n    m_psurf = nullptr;\n    m_p0 = 0;\n    m_Rd = 0.0;\n    m_pd = 0.0;\n    m_C = 0.0;\n    m_e = 0.0;\n }\n\n//-----------------------------------------------------------------------------\n//! initialize\n//! TODO: Generalize to include the initial conditions\nbool FEFluidRCRBC::Init()\n{\n    m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    \n    m_psurf = GetSurface();\n    \n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n    if (m_pfluid == nullptr) return false;\n    \n    m_pn = m_pp = m_p0;\n    m_pdn = m_pdp = m_pd;\n    m_qn = m_qp = 0;\n    m_tp = 0;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidRCRBC::UpdateDilatation()\n{\n\t// Check if we started a new time, if so, update variables\n\tFETimeInfo& timeInfo = GetFEModel()->GetTime();\n\tdouble time = timeInfo.currentTime;\n\tint iter = timeInfo.currentIteration;\n\tdouble dt = timeInfo.timeIncrement;\n\tif ((time > m_tp) && (iter == 0)) {\n\t\tm_pp = m_pn;\n\t\tm_qp = m_qn;\n\t\tm_pdp = m_pdn;\n\t\tm_tp = time;\n\t}\n\n\t// evaluate the flow rate at the current time\n\tm_qn = FlowRate();\n\tm_pdn = m_pd;\n\n\tdouble tau = m_Rd * m_C;\n\n\t// calculate the RCR pressure\n\tm_pn = m_pdn + (m_Rd / (1 + tau / dt) + m_R) * m_qn + tau / (dt + tau) * (m_pp - m_pdp - m_R * m_qp);\n\n\t// calculate the dilatation\n\tm_e = 0.0;\n\tbool good = m_pfluid->Dilatation(0, m_pn, m_e);\n\tassert(good);\n}\n\nvoid FEFluidRCRBC::UpdateModel() { Update(); }\n\nvoid FEFluidRCRBC::Update()\n{\n\tUpdateDilatation();\n\n    // the base class handles mapping the values to the nodal dofs\n    FEPrescribedSurface::Update();\n\n\t// TODO: Is this necessary?\n\tGetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at current time\ndouble FEFluidRCRBC::FlowRate()\n{\n    double Q = 0;\n    \n    const FETimeInfo& tp = GetTimeInfo();\n\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->Node(el.m_lnode[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n    \n    return Q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidRCRBC::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::PrepStep(ui, brel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidRCRBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e;\n\tFENode& node = GetMesh().Node(m_nodeList[nodelid]);\n\tnode.set(m_dofEF, m_e);\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidRCRBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidRCRBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_pn & m_pp & m_qn & m_qp & m_pdn & m_pdp & m_tp & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n    ar & m_psurf;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCRBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidRCRBC is a fluid surface load that implements a 3-element Windkessel model\n//!\nclass FEBIOFLUID_API FEFluidRCRBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidRCRBC(FEModel* pfem);\n    \n    //! set the dilatation\n    void Update() override;\n    void UpdateModel() override;\n    \n    //! evaluate flow rate\n    double FlowRate();\n    \n    //! initialize\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n\tvoid PrepStep(std::vector<double>& ui, bool brel) override;\n\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n\t//! set the dilatation\n\tvoid UpdateDilatation();\n\nprivate:\n    double          m_R;        //!< flow resistance\n    double          m_Rd;       //!< distal resistance\n    double          m_p0;       //!< initial fluid pressure\n    double          m_C;        //!< capacitance\n    double          m_pd;       //!< downstream pressure\n    \nprivate:\n    double              m_pn;   //!< fluid pressure at current time point\n    double              m_pp;   //!< fluid pressure at previous time point\n    double              m_qn;   //!< flow rate at current time point\n    double              m_qp;   //!< flow rate at previous time point\n    double              m_pdn;  //!< downstream fluid pressure at current time point\n    double              m_pdp;  //!< downstream fluid pressure at previous time point\n    double              m_tp;   //!< previous time\n    double              m_e;\n    FEFluidMaterial*    m_pfluid;   //!< pointer to fluid\n    FESurface*          m_psurf;    //!< pointer to surface\n    \n    FEDofList   m_dofW;\n    int         m_dofEF;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCRLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidRCRLoad.h\"\n#include \"FEFluid.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidRCRLoad, FESurfaceLoad)\n    ADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_Rd, \"Rd\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_p0, \"initial_pressure\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_pd, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_C , \"capacitance\")->setUnits(\"L^5/F\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidRCRLoad::FEFluidRCRLoad(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_pfluid = nullptr;\n    m_p0 = 0;\n    m_Rd = 0.0;\n    m_pd = 0.0;\n    m_C = 0.0;\n\n    if (pfem) m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = (pfem ? pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0) : -1);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\n//! TODO: Generalize to include the initial conditions\nbool FEFluidRCRLoad::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofW);\n    m_dof.AddDof(m_dofEF);\n\n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n\n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n    if (m_pfluid == nullptr) return false;\n\n    m_pn = m_pp = m_p0;\n    m_pdn = m_pdp = m_pd;\n    m_qn = m_qp = 0;\n    m_tp = 0;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEFluidRCRLoad::Activate()\n{\n    FESurface* ps = &GetSurface();\n\n    for (int i = 0; i < ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofEF, DOF_PRESCRIBED);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidRCRLoad::Update()\n{\n    // Check if we started a new time, if so, update variables\n    FETimeInfo& timeInfo = GetFEModel()->GetTime();\n    double time = timeInfo.currentTime;\n    int iter = timeInfo.currentIteration;\n    double dt = timeInfo.timeIncrement;\n    if ((time > m_tp) && (iter == 0)) {\n        m_pp = m_pn;\n        m_qp = m_qn;\n        m_pdp = m_pdn;\n        m_tp = time;\n    }\n\n    // evaluate the flow rate at the current time\n    m_qn = FlowRate();\n    m_pdn = m_pd;\n\n    double tau = m_Rd * m_C;\n\n    // calculate the RCR pressure\n    m_pn = m_pdn + (m_Rd / (1 + tau / dt) + m_R) * m_qn + tau / (dt + tau) * (m_pp - m_pdp - m_R * m_qp);\n\n    // calculate the dilatation\n    double e = 0;\n    bool good = m_pfluid->Dilatation(0, m_pn, e);\n    assert(good);\n\n    // prescribe this dilatation at the nodes\n    FESurface* ps = &GetSurface();\n\n    for (int i = 0; i < ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofEF] < -1)\n        {\n            FENode& node = ps->Node(i);\n            // set node as having prescribed DOF\n            node.set(m_dofEF, e);\n        }\n    }\n\n    // Force a mesh update after loads have been updated\n    ForceMeshUpdate();\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at current time\ndouble FEFluidRCRLoad::FlowRate()\n{\n    double Q = 0;\n\n    const FETimeInfo& tp = GetTimeInfo();\n\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n\n    for (int iel = 0; iel < m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n\n        // nr integration points\n        int nint = el.GaussPoints();\n\n        // nr of element nodes\n        int neln = el.Nodes();\n\n        // nodal coordinates\n        for (int i = 0; i < neln; ++i) {\n            FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n\n        double* Nr, * Ns;\n        double* N;\n        double* w = el.GaussWeights();\n\n        vec3d dxr, dxs, v;\n\n        // repeat over integration points\n        for (int n = 0; n < nint; ++n)\n        {\n            N = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n\n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0, 0, 0);\n            for (int i = 0; i < neln; ++i)\n            {\n                v += vt[i] * N[i];\n                dxr += rt[i] * Nr[i];\n                dxs += rt[i] * Ns[i];\n            }\n\n            vec3d normal = dxr ^ dxs;\n            double q = normal * v;\n            Q += q * w[n];\n        }\n    }\n\n    return Q;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate residual\nvoid FEFluidRCRLoad::LoadVector(FEGlobalVector& R)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidRCRLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    ar & m_pn& m_pp& m_qn& m_qp& m_pdn& m_pdp& m_tp;\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidRCRLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidRCRLoad is a fluid surface load that implements a 3-element Windkessel model\n//!\nclass FEBIOFLUID_API FEFluidRCRLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidRCRLoad(FEModel* pfem);\n\n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n\n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n\n    //! set the dilatation\n    void Update() override;\n\n    //! evaluate flow rate\n    double FlowRate();\n\n    //! initialize\n    bool Init() override;\n\n    //! activate\n    void Activate() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\nprivate:\n    double          m_R;        //!< flow resistance\n    double          m_Rd;       //!< distal resistance\n    double          m_p0;       //!< initial fluid pressure\n    double          m_C;        //!< capacitance\n    double          m_pd;       //!< downstream pressure\n\nprivate:\n    double              m_pn;   //!< fluid pressure at current time point\n    double              m_pp;   //!< fluid pressure at previous time point\n    double              m_qn;   //!< flow rate at current time point\n    double              m_qp;   //!< flow rate at previous time point\n    double              m_pdn;  //!< downstream fluid pressure at current time point\n    double              m_pdp;  //!< downstream fluid pressure at previous time point\n    double              m_tp;   //!< previous time\n    FEFluidMaterial* m_pfluid;   //!< pointer to fluid\n\n    FEDofList   m_dofW;\n    int         m_dofEF;\n\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEFluidResidualVector.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidResidualVector.h\"\n#include \"FECore/DOFS.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FELinearConstraintManager.h>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nFEFluidResidualVector::FEFluidResidualVector(FEModel& fem, vector<double>& R, vector<double>& Fr) : FEGlobalVector(fem, R, Fr)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEFluidResidualVector::~FEFluidResidualVector()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidResidualVector::Assemble(vector<int>& en, vector<int>& elm, vector<double>& fe)\n{\n    \n    vector<double>& R = m_R;\n    \n    int i, I;\n    \n    vec3d a, d;\n    \n    //#pragma omp critical\n    {\n        // assemble the element residual into the global residual\n        int ndof = (int)fe.size();\n        for (i=0; i<ndof; ++i)\n        {\n            \n            I = elm[i];\n            \n            if ( I >= 0){\n#pragma omp atomic\n                R[I] += fe[i];\n            }\n            // TODO: Find another way to store reaction forces\n            \n            else if (-I-2 >= 0){\n#pragma omp atomic\n                m_Fr[-I-2] -= fe[i];\n            }\n        }\n        \n        \n        int ndn = ndof / (int)en.size();\n        // if there are linear constraints we need to apply them\n        \n\n\t\t// process linear constraints\n\t\tFELinearConstraintManager& LCM = m_fem.GetLinearConstraintManager();\n\t\tif (LCM.LinearConstraints())\n\t\t{\n\t\t\tLCM.AssembleResidual(R, en, elm, fe);\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidResidualVector.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEGlobalVector.h>\n#include <vector>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n//! The FEFluidResidualVector implements a global vector that stores the residual.\n\nclass FEBIOFLUID_API FEFluidResidualVector : public FEGlobalVector\n{\npublic:\n\t//! constructor\n\tFEFluidResidualVector(FEModel& fem, std::vector<double>& R, std::vector<double>& Fr);\n\n\t//! destructor\n\t~FEFluidResidualVector();\n\n\t//! Assemble the element vector into this global vector\n\tvoid Assemble(std::vector<int>& en, std::vector<int>& elm, std::vector<double>& fe);\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidResistanceBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidResistanceBC.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidResistanceBC, FEPrescribedSurface)\n\tADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n\tADD_PARAMETER(m_p0, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidResistanceBC::FEFluidResistanceBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_pfluid = nullptr;\n    m_p0 = 0;\n    m_e = 0.0;\n    m_psurf = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidResistanceBC::Init()\n{\n    m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n    SetDOFList(m_dofEF);\n    \n\tif (FEPrescribedSurface::Init() == false) return false;\n    \n    m_psurf = GetSurface();\n\n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n\n\t// get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\tm_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n\tif (m_pfluid == nullptr) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidResistanceBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n    ar & m_psurf;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidResistanceBC::Update()\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::Update();\n\n\t// TODO: Is this necessary?\n\tGetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidResistanceBC::UpdateModel() { Update(); }\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidResistanceBC::UpdateDilatation()\n{\n    // evaluate the flow rate\n    double Q = FlowRate();\n    \n    // calculate the resistance pressure\n    double p = m_R*Q;\n    \n    // calculate the dilatation\n    m_e = 0;\n    bool good = m_pfluid->Dilatation(0,p+m_p0, m_e);\n    assert(good);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at the current time\ndouble FEFluidResistanceBC::FlowRate()\n{\n    double Q = 0;\n    \n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    const FETimeInfo& tp = GetTimeInfo();\n    double alpha = tp.alpha;\n    double alphaf = tp.alphaf;\n\n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->Node(el.m_lnode[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n\n    return Q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidResistanceBC::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::PrepStep(ui, brel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidResistanceBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e;\n\n\tFENode& node = GetMesh().Node(m_nodeList[nodelid]);\n\tnode.set(m_dofEF, m_e);\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidResistanceBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidResistanceBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidResistanceBC is a fluid surface that has a normal\n//! pressure proportional to the flow rate (resistance).\n//!\nclass FEBIOFLUID_API FEFluidResistanceBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidResistanceBC(FEModel* pfem);\n    \n    //! evaluate flow rate\n    double FlowRate();\n    \n    //! initialize\n    bool Init() override;\n\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n\tvoid Update() override;\n\tvoid UpdateModel() override;\n    \npublic:\n\tvoid PrepStep(std::vector<double>& ui, bool brel) override;\n\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n\t//! set the dilatation\n\tvoid UpdateDilatation();\n\nprivate:\n    double\t\t\tm_R;        //!< flow resistance\n\tdouble          m_p0;       //!< fluid pressure offset\n\nprivate:\n    FEFluidMaterial*    m_pfluid;   //!< pointer to fluid\n    double              m_e;    //!< fluid dilatation\n    FESurface*          m_psurf;\n    \n\tFEDofList\tm_dofW;\n    int\t\tm_dofEF;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidResistanceLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidResistanceLoad.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidResistanceLoad, FESurfaceLoad)\n\tADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n\tADD_PARAMETER(m_p0, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidResistanceLoad::FEFluidResistanceLoad(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_pfluid = nullptr;\n    m_p0 = 0;\n    \n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofEF = pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n\n        m_dof.Clear();\n        m_dof.AddDofs(m_dofW);\n        m_dof.AddDof(m_dofEF);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidResistanceLoad::Init()\n{\n\tif (FESurfaceLoad::Init() == false) return false;\n\n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n\n\t// get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\tm_pfluid = pm->ExtractProperty<FEFluidMaterial>();\n\tif (m_pfluid == nullptr) return false;\n    \n    \n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidResistanceLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofW & m_dofEF;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEFluidResistanceLoad::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofEF, DOF_PRESCRIBED);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidResistanceLoad::Update()\n{\n    // evaluate the flow rate\n    double Q = FlowRate();\n    \n    // calculate the resistance pressure\n    double p = m_R*Q;\n    \n    // calculate the dilatation\n    double e = 0;\n    bool good = m_pfluid->Dilatation(0,p+m_p0, e);\n    assert(good);\n    \n    // prescribe this dilatation at the nodes\n    FESurface* ps = &GetSurface();\n\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofEF] < -1)\n        {\n            FENode& node = ps->Node(i);\n            // set node as having prescribed DOF\n            node.set(m_dofEF, e);\n        }\n    }\n    \n    GetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at the current time\ndouble FEFluidResistanceLoad::FlowRate()\n{\n    double Q = 0;\n    \n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    const FETimeInfo& tp = GetTimeInfo();\n    double alpha = tp.alpha;\n    double alphaf = tp.alphaf;\n\n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n\n    return Q;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidResistanceLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidResistanceBC is a fluid surface that has a normal\n//! pressure proportional to the flow rate (resistance).\n//!\nclass FEBIOFLUID_API FEFluidResistanceLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidResistanceLoad(FEModel* pfem);\n\n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n\n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override {}\n\n    //! set the dilatation\n    void Update() override;\n\n    //! evaluate flow rate\n    double FlowRate();\n\n    //! initialize\n    bool Init() override;\n\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! activate\n    void Activate() override;\n\nprivate:\n    double\t\t\tm_R;        //!< flow resistance\n    double          m_p0;       //!< fluid pressure offset\n\nprivate:\n    FEFluidMaterial* m_pfluid;   //!< pointer to fluid\n\n    FEDofList\tm_dofW;\n    int\t\tm_dofEF;\n\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEFluidRotationalVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidRotationalVelocity.h\"\n#include \"FECore/FEElemElemList.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/FEGlobalVector.h\"\n#include \"FECore/log.h\"\n#include \"FECore/LinearSolver.h\"\n#include <FECore/FENode.h>\n#include \"FEBioFluid.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidRotationalVelocity, FEPrescribedNodeSet)\n\tADD_PARAMETER(m_w, \"angular_speed\");\n\tADD_PARAMETER(m_n, \"axis\"         );\n\tADD_PARAMETER(m_p, \"origin\"       );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidRotationalVelocity::FEFluidRotationalVelocity(FEModel* pfem) : FEPrescribedNodeSet(pfem)\n{\n    m_w = 0.0;\n    m_n = vec3d(0,0,1);\n    m_p = vec3d(0,0,0);\n\n\t// Set the dof list\n\t// TODO: Can this be done in Init, since  there is no error checking\n\tif (pfem)\n\t{\n\t\tFEDofList dofs(pfem);\n\t\tdofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n\t\tSetDOFList(dofs);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidRotationalVelocity::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFEFluidRotationalVelocity* rv = dynamic_cast<FEFluidRotationalVelocity*>(pbc);\n\tm_w = rv->m_w;\n\tm_n = rv->m_n;\n\tm_p = rv->m_p;\n\tCopyParameterListState(rv->GetParameterList());\n}\n\n//-----------------------------------------------------------------------------\n// return nodal value\nvoid FEFluidRotationalVelocity::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n\tvec3d n(m_n); n.unit();\n\tvec3d v = (n ^ m_r[nodelid])*m_w;\n\tval[0] = v.x;\n\tval[1] = v.y;\n\tval[2] = v.z;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidRotationalVelocity::Init()\n{\n    // evaluate nodal radial positions\n\tvec3d n(m_n); n.unit();\n\tconst FENodeSet& nset = *GetNodeSet();\n    int N = nset.Size();\n    m_r.resize(N,vec3d(0,0,0));\n    for (int i=0; i<N; ++i) {\n        vec3d x = nset.Node(i)->m_r0 - m_p;\n        m_r[i] = x - n*(x*n);\n    }\n\n    return FEPrescribedNodeSet::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidRotationalVelocity::Serialize(DumpStream& ar)\n{\n\tFEPrescribedNodeSet::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_r;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidRotationalVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidRotationalVelocity is a fluid surface that has a rotational\n//! velocity prescribed on it.  This routine prescribes nodal velocities\n\nclass FEBIOFLUID_API FEFluidRotationalVelocity : public FEPrescribedNodeSet\n{\npublic:\n    //! constructor\n    FEFluidRotationalVelocity(FEModel* pfem);\n\n    //! initialization\n    bool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \n\t// copy data from another class\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\n\t// return nodal value\n\tvoid GetNodalValues(int nodelid, std::vector<double>& val) override;\n\nprivate:\n    double\t\t\tm_w;        //!< angular speed\n    vec3d           m_n;        //!< unit vector along axis of rotation\n    vec3d           m_p;        //!< point on axis of rotation\n    vector<vec3d>   m_r;        //!< nodal radial positions\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSoluteAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEFluidSolutesAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEFluidSolutesAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFEFluidSolutesAnalysis::FEFluidSolutesAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutes.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutes.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n#include <FECore/log.h>\n#include <FECore/tools.h>\n#include <complex>\nusing namespace std;\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidSolutes, FEMaterial)\n// material properties\n// ADD_PARAMETER(m_penalty, FE_RANGE_GREATER_OR_EQUAL(0.0), \"penalty\"      );\nADD_PARAMETER(m_diffMtmSupp , \"dms\")->setLongName(\"include osmosis\");\nADD_PROPERTY(m_pFluid, \"fluid\");\nADD_PROPERTY(m_pOsmC  , \"osmotic_coefficient\");\nADD_PROPERTY(m_pSolute, \"solute\"             , FEProperty::Optional);\nADD_PROPERTY(m_pReact , \"reaction\"           , FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFluidSolutesMaterialPoint\n//============================================================================\nFEFluidSolutesMaterialPoint::FEFluidSolutesMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEFluidSolutesMaterialPoint::Copy()\n{\n    FEFluidSolutesMaterialPoint* pt = new FEFluidSolutesMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_nsol & m_psi & m_Ie & m_pe;\n    ar & m_c & m_ca & m_gradc & m_j & m_cdot & m_k & m_dkdJ;\n    ar & m_dkdc;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesMaterialPoint::Init()\n{\n    m_nsol = 0;\n    m_psi = 0;\n    m_pe = 0;\n    m_Ie = vec3d(0,0,0);\n    m_c.clear();\n    m_ca.clear();\n    m_gradc.clear();\n    m_j.clear();\n    m_cdot.clear();\n    m_k.clear();\n    m_dkdJ.clear();\n    m_dkdJJ.clear();\n    m_dkdc.clear();\n    m_dkdJc.clear();\n    m_dkdcc.clear();\n\n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFluidSolutesMaterialPoint::Osmolarity() const\n{\n    double ew = 0.0;\n    for (int isol = 0; isol < (int)m_ca.size(); ++isol)\n    {\n        ew += m_ca[isol];\n    }\n    return ew;\n}\n\n//============================================================================\n// FEFluidSolutes\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluidSolutes constructor\n\nFEFluidSolutes::FEFluidSolutes(FEModel* pfem) : FEMaterial(pfem)\n{\n    m_pFluid = 0;\n    m_Rgas = 0; m_Tabs = 0; m_Fc = 0;\n    m_diffMtmSupp = false;\n    m_penalty = 1;\n    m_pOsmC = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEFluidSolutes::CreateMaterialPointData()\n{\n    FEFluidMaterialPoint* fpt = new FEFluidMaterialPoint();\n    return new FEFluidSolutesMaterialPoint(fpt);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutes::AddChemicalReaction(FEChemicalReaction* pcr)\n{\n    m_pReact.push_back(pcr);\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FEFluidSolutes::Init()\n{\n    // set the solute IDs first, since they are referenced in FESolute::Init()\n    for (int i = 0; i<Solutes(); ++i) {\n        m_pSolute[i]->SetSoluteLocalID(i);\n    }\n    \n    // call the base class.\n    // This also initializes all properties\n    if (FEMaterial::Init() == false) return false;\n    \n    // Determine how to solve for the electric potential psi\n    int isol;\n    int zmin = 0, zmax = 0, z;\n    for (isol=0; isol<(int)m_pSolute.size(); ++isol) {\n        z = m_pSolute[isol]->ChargeNumber();\n        if (z < zmin) zmin = z;\n        if (z > zmax) zmax = z;\n    }\n    m_zmin = zmin;\n    m_ndeg = zmax - zmin;    // polynomial degree\n    \n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n    m_Fc   = GetFEModel()->GetGlobalConstant(\"Fc\");\n    \n    if (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n    if (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");     return false; }\n    if ((zmin || zmax) && (m_Fc <= 0)) {\n        feLogError(\"A positive Faraday constant Fc must be defined in Globals section\");\n        return false;\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutes::Serialize(DumpStream& ar)\n{\n    FEMaterial::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_Rgas & m_Tabs & m_Fc;\n    ar & m_zmin & m_ndeg;\n}\n\n//-----------------------------------------------------------------------------\n//! Electric potential\ndouble FEFluidSolutes::ElectricPotential(const FEMaterialPoint& pt, const bool eform)\n{\n    // check if solution is neutral\n    if (m_ndeg == 0) {\n        if (eform) return 1.0;\n        else return 0.0;\n    }\n    \n    int i, j;\n    \n    // if not neutral, solve electroneutrality polynomial for zeta\n    const FEFluidSolutesMaterialPoint& set = *pt.ExtractData<FEFluidSolutesMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    double cF = 0.0;\n    \n\tFEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n\tvector<double> c(nsol);        // effective concentration\n    vector<double> khat(nsol);    // solubility\n    vector<int> z(nsol);        // charge number\n    for (i=0; i<nsol; ++i) {\n        c[i] = set.m_c[i];\n        khat[i] = m_pSolute[i]->m_pSolub->Solubility(mp);\n        z[i] = m_pSolute[i]->ChargeNumber();\n    }\n    \n    // evaluate polynomial coefficients\n    const int n = m_ndeg;\n    vector<double> a(n+1,0);\n    if (m_zmin < 0) {\n        for (i=0; i<nsol; ++i) {\n            j = z[i] - m_zmin;\n            a[j] += z[i]*khat[i]*c[i];\n        }\n        a[-m_zmin] = cF;\n    } else {\n        for (i=0; i<nsol; ++i) {\n            j = z[i];\n            a[j] += z[i]*khat[i]*c[i];\n        }\n        a[0] = cF;\n    }\n    \n    // solve polynomial\n    double psi = set.m_psi;        // use previous solution as initial guess\n    double zeta = exp(-m_Fc*psi/m_Rgas/m_Tabs);\n    if (!solvepoly(n, a, zeta)) {\n        zeta = 1.0;\n    }\n    \n    // Return exponential (non-dimensional) form if desired\n    if (eform) return zeta;\n    \n    // Otherwise return dimensional value of electric potential\n    psi = -m_Rgas*m_Tabs/m_Fc*log(zeta);\n    \n    return psi;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficient\ndouble FEFluidSolutes::PartitionCoefficient(const FEMaterialPoint& pt, const int sol)\n{\n    FEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n    // solubility\n    double khat = m_pSolute[sol]->m_pSolub->Solubility(mp);\n    // charge number\n    int z = m_pSolute[sol]->ChargeNumber();\n    // electric potential\n    double zeta = ElectricPotential(pt, true);\n    double zz = pow(zeta, z);\n    // partition coefficient\n    double kappa = zz*khat;\n    \n    return kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficients and their derivatives\nvoid FEFluidSolutes::PartitionCoefficientFunctions(const FEMaterialPoint& mp, vector<double>& kappa,\n                                                  vector<double>& dkdJ,\n                                                  vector< vector<double> >& dkdc)\n{\n    //TODO: Include dkdcc and dkdJc\n    \n    int isol, jsol, ksol;\n    \n    const FEFluidMaterialPoint& fpt = *(mp.ExtractData<FEFluidMaterialPoint>());\n    const FEFluidSolutesMaterialPoint& spt = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n\tFEMaterialPoint& pt = const_cast<FEMaterialPoint&>(mp);\n\n    const int nsol = (int)m_pSolute.size();\n    \n    vector<double> c(nsol);\n    vector<int> z(nsol);\n    vector<double> khat(nsol);\n    vector<double> dkhdJ(nsol);\n    vector<double> dkhdJJ(nsol);\n    vector< vector<double> > dkhdc(nsol, vector<double>(nsol));\n    vector< vector<double> > dkhdJc(nsol, vector<double>(nsol));\n    vector< vector< vector<double> > > dkhdcc(nsol, dkhdc);    // use dkhdc to initialize only\n    vector<double> zz(nsol);\n    kappa.resize(nsol);\n    \n    double den = 0;\n    double num = 0;\n    double zeta = ElectricPotential(mp, true);\n    \n    for (isol=0; isol<nsol; ++isol) {\n        // get the effective concentration, its gradient and its time derivative\n        c[isol] = spt.m_c[isol];\n        // get the charge number\n        z[isol] = m_pSolute[isol]->ChargeNumber();\n        // evaluate the solubility and its derivatives w.r.t. J and c\n        khat[isol] = m_pSolute[isol]->m_pSolub->Solubility(pt);\n        dkhdJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain(pt);\n        dkhdJJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Strain(pt);\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkhdc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration(pt,jsol);\n            dkhdJc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Concentration(pt,jsol);\n            for (ksol=0; ksol<nsol; ++ksol) {\n                dkhdcc[isol][jsol][ksol] =\n                m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration_Concentration(pt,jsol,ksol);\n            }\n        }\n        zz[isol] = pow(zeta, z[isol]);\n        kappa[isol] = zz[isol]*khat[isol];\n        den += SQR(z[isol])*kappa[isol]*c[isol];\n        num += pow((double)z[isol],3)*kappa[isol]*c[isol];\n    }\n\n    // evaluate electric potential (nondimensional exponential form) and its derivatives\n    // also evaluate partition coefficients and their derivatives\n    double zidzdJ = 0;\n    double zidzdJJ = 0, zidzdJJ1 = 0, zidzdJJ2 = 0;\n    vector<double> zidzdc(nsol,0);\n    vector<double> zidzdJc(nsol,0), zidzdJc1(nsol,0), zidzdJc2(nsol,0);\n    vector< vector<double> > zidzdcc(nsol, vector<double>(nsol,0));\n    vector< vector<double> > zidzdcc1(nsol, vector<double>(nsol,0));\n    vector<double> zidzdcc2(nsol,0);\n    double zidzdcc3 = 0;\n    \n    if (den > 0) {\n        \n        for (isol=0; isol<nsol; ++isol)\n            zidzdJ += z[isol]*zz[isol]*dkhdJ[isol]*c[isol];\n        zidzdJ = -(zidzdJ)/den;\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdJJ1 += SQR(z[jsol])*c[jsol]*(z[jsol]*zidzdJ*kappa[jsol]+zz[jsol]*dkhdJ[jsol]);\n                zidzdJJ2 += z[jsol]*zz[jsol]*c[jsol]*(zidzdJ*z[jsol]*dkhdJ[jsol]+dkhdJJ[jsol]);\n                zidzdc[isol] += z[jsol]*zz[jsol]*dkhdc[jsol][isol]*c[jsol];\n            }\n            zidzdc[isol] = -(z[isol]*kappa[isol]+zidzdc[isol])/den;\n            zidzdcc3 += pow(double(z[isol]),3)*kappa[isol]*c[isol];\n        }\n        zidzdJJ = zidzdJ*(zidzdJ-zidzdJJ1/den)-(zidzdJJ2)/den;\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdJc1[isol] += SQR(z[jsol])*c[jsol]*(zidzdc[isol]*z[jsol]*kappa[jsol]+zz[jsol]*dkhdc[jsol][isol]);\n                zidzdJc2[isol] += z[jsol]*zz[jsol]*c[jsol]*(zidzdc[isol]*z[jsol]*dkhdJ[jsol]+dkhdJc[jsol][isol]);\n                zidzdcc2[isol] += SQR(z[jsol])*zz[jsol]*c[jsol]*dkhdc[jsol][isol];\n                for (ksol=0; ksol<nsol; ++ksol)\n                    zidzdcc1[isol][jsol] += z[ksol]*zz[ksol]*c[ksol]*dkhdcc[ksol][isol][jsol];\n            }\n            zidzdJc[isol] = zidzdJ*(zidzdc[isol]-(SQR(z[isol])*kappa[isol] + zidzdJc1[isol])/den)\n            -(z[isol]*zz[isol]*dkhdJ[isol] + zidzdJc2[isol])/den;\n        }\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdcc[isol][jsol] = zidzdc[isol]*zidzdc[jsol]*(1 - zidzdcc3/den)\n                - zidzdcc1[isol][jsol]/den\n                - z[isol]*(z[isol]*kappa[isol]*zidzdc[jsol]+zz[isol]*dkhdc[isol][jsol])/den\n                - z[jsol]*(z[jsol]*kappa[jsol]*zidzdc[isol]+zz[jsol]*dkhdc[jsol][isol])/den\n                - zidzdc[jsol]*zidzdcc2[isol]/den\n                - zidzdc[isol]*zidzdcc2[jsol]/den;\n            }\n        }\n    }\n    \n    dkdJ.resize(nsol);\n    dkdc.resize(nsol, vector<double>(nsol,0));\n    \n    for (isol=0; isol<nsol; ++isol) {\n        dkdJ[isol] = zz[isol]*dkhdJ[isol]+z[isol]*kappa[isol]*zidzdJ;\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkdc[isol][jsol] = zz[isol]*dkhdc[isol][jsol]+z[isol]*kappa[isol]*zidzdc[jsol];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Current density\nvec3d FEFluidSolutes::CurrentDensity(const FEMaterialPoint& pt)\n{\n    int i;\n    const int nsol = (int)m_pSolute.size();\n    \n    vector<vec3d> j(nsol);\n    vector<int> z(nsol);\n    vec3d Ie(0,0,0);\n    for (i=0; i<nsol; ++i) {\n        j[i] = SoluteFlux(pt, i);\n        z[i] = m_pSolute[i]->ChargeNumber();\n        Ie += j[i]*z[i];\n    }\n    Ie *= m_Fc;\n    \n    return Ie;\n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FEFluidSolutes::ConcentrationActual(const FEMaterialPoint& pt, const int sol)\n{\n    const FEFluidSolutesMaterialPoint& spt = *pt.ExtractData<FEFluidSolutesMaterialPoint>();\n    \n    // effective concentration\n    double c = spt.m_c[sol];\n    \n    // partition coefficient\n    double kappa = PartitionCoefficient(pt, sol);\n    \n    double ca = kappa*c;\n    \n    return ca;\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure\ndouble FEFluidSolutes::PressureActual(const FEMaterialPoint& pt)\n{\n    int i;\n    \n    const FEFluidMaterialPoint& fpt = *pt.ExtractData<FEFluidMaterialPoint>();\n    const FEFluidSolutesMaterialPoint& spt = *(pt.ExtractData<FEFluidSolutesMaterialPoint>());\n    const int nsol = (int)m_pSolute.size();\n    \n    // effective pressure\n    double p = spt.m_pe;\n    \n    // actual concentration\n    vector<double> c(nsol);\n    for (i=0; i<nsol; ++i)\n        c[i] = ConcentrationActual(pt, i);\n    \n    // osmotic coefficient\n    FEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n    double osmc = m_pOsmC->OsmoticCoefficient(mp);\n    \n    // actual pressure\n    double pa = 0;\n    for (i=0; i<nsol; ++i) pa += c[i];\n    pa = p + m_Rgas*m_Tabs*osmc*pa;\n    \n    return pa;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate solute molar flux\n\nvec3d FEFluidSolutes::SoluteFlux(const FEMaterialPoint& pt, const int sol)\n{\n    const FEFluidSolutesMaterialPoint& spt = *pt.ExtractData<FEFluidSolutesMaterialPoint>();\n    const FEFluidMaterialPoint& fpt = *pt.ExtractData<FEFluidMaterialPoint>();\n    \n    // concentration gradient\n    vec3d gradc = spt.m_gradc[sol];\n    \n    // solute free diffusivity\n    FEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n    double D0 = m_pSolute[sol]->m_pDiff->Free_Diffusivity(mp);\n    double kappa = PartitionCoefficient(pt, sol);\n    \n    double c = spt.m_c[sol];\n    vec3d v = fpt.m_vft;\n    \n    // solute flux j\n    vec3d j = -gradc*D0*kappa + v*c*kappa;\n    \n    return j;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate diffusive solute molar flux\n\nvec3d FEFluidSolutes::SoluteDiffusiveFlux(const FEMaterialPoint& pt, const int sol)\n{\n    const FEFluidSolutesMaterialPoint& spt = *pt.ExtractData<FEFluidSolutesMaterialPoint>();\n    const FEFluidMaterialPoint& fpt = *pt.ExtractData<FEFluidMaterialPoint>();\n    \n    // concentration gradient\n    vec3d gradc = spt.m_gradc[sol];\n    \n    // solute free diffusivity\n\tFEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n\tdouble D0 = m_pSolute[sol]->m_pDiff->Free_Diffusivity(mp);\n    double kappa = PartitionCoefficient(pt, sol);\n    \n    // diffusive solute flux j\n    vec3d jd = -gradc*D0*kappa;\n    \n    return jd;\n}\n\n//-----------------------------------------------------------------------------\n// solute interface functions\n\ndouble FEFluidSolutes::GetEffectiveSoluteConcentration(FEMaterialPoint& mp, int soluteIndex)\n{\n    FEFluidSolutesMaterialPoint& spt = *mp.ExtractData<FEFluidSolutesMaterialPoint>();\n    return spt.m_c[soluteIndex];\n}\n\ndouble FEFluidSolutes::GetFreeDiffusivity(FEMaterialPoint& mp, int soluteIndex)\n{\n    return m_pSolute[soluteIndex]->m_pDiff->Free_Diffusivity(mp);\n}\n\ndouble FEFluidSolutes::GetPartitionCoefficient(FEMaterialPoint& mp, int soluteIndex)\n{\n    FEFluidSolutesMaterialPoint& spt = *mp.ExtractData<FEFluidSolutesMaterialPoint>();\n    return spt.m_k[soluteIndex];\n}\n\ndouble FEFluidSolutes::GetOsmolarity(const FEMaterialPoint& mp)\n{\n    double osm = 0;\n    const FEFluidSolutesMaterialPoint& spt = *mp.ExtractData<FEFluidSolutesMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    for (int i=0; i<nsol; ++i) osm += spt.m_ca[i];\n    return osm;\n}\n\ndouble FEFluidSolutes::dkdc(const FEMaterialPoint& mp, int i, int j)\n{\n    const FEFluidSolutesMaterialPoint& spt = *mp.ExtractData<FEFluidSolutesMaterialPoint>();\n    return spt.m_dkdc[i][j];\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutes.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include \"FEFluid.h\"\n#include <FEBioMix/FESolute.h>\n#include <FEBioMix/FESoluteInterface.h>\n#include <FEBioMix/FEOsmoticCoefficient.h>\n#include <FEBioMix/FEChemicalReaction.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! FSI material point class.\n//\nclass FEBIOFLUID_API FEFluidSolutesMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n    FEFluidSolutesMaterialPoint(FEMaterialPointData* pt);\n    \n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n    \n    //! data serialization\n    void Serialize(DumpStream& ar);\n    \n    //! Data initialization\n    void Init();\n\npublic:\n    double Osmolarity() const;\n    \npublic:\n    // solutes material data\n    int                 m_nsol;     //!< number of solutes\n    vector<double>      m_c;        //!< effective solute concentration\n    vector<double>      m_ca;       //!< actual solute concentration\n    vector<vec3d>       m_gradc;    //!< spatial gradient of solute concentration\n    vector<vec3d>       m_j;        //!< solute molar flux\n    vector<double>      m_cdot;     //!< material time derivative of solute concentration following fluid\n    double            m_psi;        //!< electric potential\n    vec3d            m_Ie;          //!< current density\n    double           m_pe;          //!< effective fluid pressure\n    vector<double>    m_k;          //!< solute partition coefficient\n    vector<double>    m_dkdJ;       //!< 1st deriv of m_k with strain (J)\n    vector<double>    m_dkdJJ;      //!< 2nd deriv of m_k with strain (J)\n    vector< vector<double> >    m_dkdc;            //!< 1st deriv of m_k with effective concentration\n    vector< vector<double> >    m_dkdJc;        //!< cross deriv of m_k with J and c\n    vector< vector< vector<double> > > m_dkdcc;    // 2nd deriv of m_k with c\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for FluidFSI materials.\n\nclass FEBIOFLUID_API FEFluidSolutes : public FEMaterial, public FESoluteInterface_T<FEFluidSolutesMaterialPoint>\n{\npublic:\n    FEFluidSolutes(FEModel* pfem);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! performs initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    FEFluid* Fluid() { return m_pFluid; }\n    \n    //! calculate solute molar flux\n    vec3d SoluteFlux(const FEMaterialPoint& pt, const int sol);\n    \n    //! calculate diffusive solute molar flux\n    vec3d SoluteDiffusiveFlux(const FEMaterialPoint& pt, const int sol);\n    \n    //! actual concentration (as opposed to effective concentration)\n    double ConcentrationActual(const FEMaterialPoint& pt, const int sol);\n    \n    //! actual fluid pressure (as opposed to effective pressure)\n    double PressureActual(const FEMaterialPoint& pt);\n    \n    //! partition coefficient\n    double PartitionCoefficient(const FEMaterialPoint& pt, const int sol);\n    \n    //! partition coefficients and their derivatives\n    void PartitionCoefficientFunctions(const FEMaterialPoint& mp, vector<double>& kappa,\n                                       vector<double>& dkdJ,\n                                       vector< vector<double> >& dkdc);\n    \n    //! electric potential\n    double ElectricPotential(const FEMaterialPoint& pt, const bool eform=false);\n    \n    //! current density\n    vec3d CurrentDensity(const FEMaterialPoint& pt);\n    \n    //! solute density\n    double SoluteDensity(const int sol) { return m_pSolute[sol]->Density(); }\n    \n    //! solute molar mass\n    double SoluteMolarMass(const int sol) { return m_pSolute[sol]->MolarMass(); }\n    \n    //! solute charge number\n    int SoluteChargeNumber(const int sol) { return m_pSolute[sol]->ChargeNumber(); }\n    \n    //! Add a chemical reaction\n    void AddChemicalReaction(FEChemicalReaction* pcr);\n    \n    // solute interface\npublic:\n\n    typedef FEFluidSolutesMaterialPoint SoluteMaterialPoint_t;\n\n    int Solutes() override { return (int)m_pSolute.size(); }\n    FESolute* GetSolute(int i) override { return m_pSolute[i]; }\n    FEOsmoticCoefficient* GetOsmoticCoefficient() override { return m_pOsmC;  }\n    double GetEffectiveSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) override;\n    double GetActualSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) override { return ConcentrationActual(mp, soluteIndex); }\n    double GetFreeDiffusivity(FEMaterialPoint& mp, int soluteIndex) override;\n    double GetPartitionCoefficient(FEMaterialPoint& mp, int soluteIndex) override;\n    vec3d GetSoluteFlux(FEMaterialPoint& mp, int soluteIndex) override { return SoluteFlux(mp, soluteIndex); }\n    double GetOsmolarity(const FEMaterialPoint& mp) override;\n    double GetElectricPotential(const FEMaterialPoint& mp) override { return ElectricPotential(mp); }\n    vec3d GetCurrentDensity(const FEMaterialPoint& mp) override { return CurrentDensity(mp); }\n    double dkdc(const FEMaterialPoint& mp, int i, int j) override;\n\npublic:\n    FEChemicalReaction*            GetReaction            (int i) { return m_pReact[i];  }\n    \n    int Reactions         () { return (int) m_pReact.size();    }\n    \npublic:\n    double    m_Rgas;            //!< universal gas constant\n    double    m_Tabs;            //!< absolute temperature\n    double    m_Fc;              //!< Faraday's constant\n    bool      m_diffMtmSupp;     //!< Toggle on or off diffusive mtm supply for fluid\n    int        m_zmin;            //!< minimum charge number in mixture\n    int        m_ndeg;            //!< polynomial degree of zeta in electroneutrality\n    double              m_penalty;  //!< penalty for enforcing electroneutrality\n    \nprivate: // material properties\n    FEFluid*                m_pFluid;       //!< pointer to fluid material\n    std::vector<FESolute*>  m_pSolute;      //!< pointer to solute materials\n    FEOsmoticCoefficient*        m_pOsmC;        //!< pointer to osmotic coefficient material\n    std::vector<FEChemicalReaction*>    m_pReact;        //!< pointer to chemical reactions\n    \n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEFluidSolutesAnalysis : public FEAnalysis\n{\npublic:\n\tenum FluidSolutesAnalysisType {\n\t\tSTEADY_STATE,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFEFluidSolutesAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutesDomain3D.h\"\n#include \"FEFluidSolver.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FELinearSystem.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEFluidSolutesDomain3D::FEFluidSolutesDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEFluidDomain(pfem), m_dofW(pfem), m_dofAW(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n    \n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_ACCELERATION));\n\n        m_dofEF = pfem->GetDOFIndex(\"ef\");\n        m_dofAEF = pfem->GetDOFIndex(\"aef\");\n        m_dofC = pfem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0);\n        m_dofAC = pfem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV), 0);\n\n        // list the degrees of freedom\n        // (This allows the FEDomain base class to handle several tasks such as UnpackLM)\n        m_dof.AddDof(m_dofW[0]);\n        m_dof.AddDof(m_dofW[1]);\n        m_dof.AddDof(m_dofW[2]);\n        m_dof.AddDof(m_dofEF);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEFluidSolutesDomain3D& FEFluidSolutesDomain3D::operator = (FEFluidSolutesDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dofs\nconst FEDofList& FEFluidSolutesDomain3D::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEFluidSolutesDomain3D::SetMaterial(FEMaterial* pmat)\n{\n    FEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEFluidSolutes*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidSolutesDomain3D::Init()\n{\n    // initialize base class\n    if (FESolidDomain::Init() == false) return false;\n    \n    const int nsol = m_pMat->Solutes();\n    \n    // set the active degrees of freedom list\n    FEDofList dofs = GetDOFList();\n    for (int i=0; i<nsol; ++i)\n    {\n        int m = m_pMat->GetSolute(i)->GetSoluteDOF();\n        dofs.AddDof(m_dofC + m);\n    }\n    m_dof = dofs;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n    ar & m_dofW & m_dofAW & m_dof;\n    ar & m_dofEF & m_dofAEF & m_dofC & m_dofAC;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::Reset()\n{\n    // reset base class\n    FESolidDomain::Reset();\n    \n    const int nsol = m_pMat->Solutes();\n    \n    for (int i=0; i<(int) m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n=0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEFluidSolutesMaterialPoint& ps = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n            \n            // initialize solutes\n            ps.m_nsol = nsol;\n            ps.m_c.assign(nsol,0);\n            ps.m_ca.assign(nsol,0);\n            ps.m_cdot.assign(nsol,0);\n            ps.m_gradc.assign(nsol,vec3d(0,0,0));\n            ps.m_j.assign(nsol,vec3d(0,0,0));\n            ps.m_k.assign(nsol, 0);\n            ps.m_dkdJ.assign(nsol, 0);\n            ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n            \n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->ResetElementData(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::Activate()\n{\n    const int nsol = m_pMat->Solutes();\n    \n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n/*          if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n            }\n*/            node.set_active(m_dofW[0]);\n            node.set_active(m_dofW[1]);\n            node.set_active(m_dofW[2]);\n            node.set_active(m_dofEF);\n            for (int isol=0; isol<nsol; ++isol)\n                node.set_active(m_dofC + m_pMat->GetSolute(isol)->GetSoluteDOF());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::InitMaterialPoints()\n{\n    const int nsol = m_pMat->Solutes();\n    FEMesh& m = *GetMesh();\n    \n    const int NE = FEElement::MAX_NODES;\n    vector< vector<double> > c0(nsol, vector<double>(NE));\n    vector<int> sid(nsol);\n    for (int j = 0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n    \n    for (int j = 0; j<(int)m_Elem.size(); ++j)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[j];\n        \n        // get the number of nodes\n        int neln = el.Nodes();\n        // get initial values of fluid pressure and solute concentrations\n        for (int i = 0; i<neln; ++i)\n        {\n            FENode& ni = m.Node(el.m_node[i]);\n            for (int isol = 0; isol<nsol; ++isol)\n                c0[isol][i] = ni.get(m_dofC + sid[isol]);\n        }\n\n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEFluidSolutesMaterialPoint& ps = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n            \n            // initialize solutes\n            ps.m_nsol = nsol;\n            \n            // initialize effective solute concentrations\n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_c[isol] = el.Evaluate(c0[isol], n);\n                ps.m_ca[isol] = m_pMat->ConcentrationActual(mp, isol);\n                ps.m_gradc[isol] = gradient(el, c0[isol], n);\n            }\n            \n            ps.m_psi = m_pMat->ElectricPotential(mp);\n            ps.m_Ie = m_pMat->CurrentDensity(mp);\n            \n            for (int isol = 0; isol<nsol; ++isol)\n                ps.m_j[isol] = m_pMat->SoluteDiffusiveFlux(mp, isol);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEFluidSolutesDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], r0, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n            pt.m_r0 = el.Evaluate(x0, j);\n            mp.m_rt = mp.m_r0;\n            \n            if (pt.m_ef <= -1) {\n                throw NegativeJacobianDetected();\n            }\n            \n            // reset chemical reaction element data\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->InitializeElementData(mp);\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n    \n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEFluidSolutesDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    mat3ds sv;\n    vec3d gradep;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    const int nreact = m_pMat->Reactions();\n\n    double dt = tp.timeIncrement;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFluidSolutesMaterialPoint& spt = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n\n        // calculate the jacobian\n        detJ = invjac0(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        // get the gradient of the elastic pressure\n        gradep = pt.m_gradef*m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double penalty = m_pMat->m_penalty;\n        \n        // evaluate the chat\n        vector<double> chat(nsol,0);\n        double phiwhat = 0;\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += pri->m_Vbar*zhat;\n            for (int isol=0; isol<nsol; ++isol)\n                chat[isol] += zhat*pri->m_v[isol];\n        }\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jdot/J\n        double dJoJ = pt.m_efdot/(pt.m_ef+1);\n        \n        double dms = m_pMat->m_diffMtmSupp;\n        \n        vector<int> z(nsol);\n        vector<vec3d> jd(nsol);\n        vec3d je(0,0,0);\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            jd[isol] = m_pMat->SoluteDiffusiveFlux(mp,isol);\n            je += jd[isol]*z[isol];\n        }\n        \n        vector<double> dkdt(nsol,0);\n        vector<vec3d> gradk(nsol,vec3d(0,0,0));\n        for (int isol=0; isol<nsol; ++isol)\n        {\n            dkdt[isol] = pt.m_efdot*spt.m_dkdJ[isol];\n            for (int jsol=0; jsol<nsol; ++jsol)\n                dkdt[isol] += spt.m_dkdc[isol][jsol]*spt.m_cdot[jsol];\n        }\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fs = sv*gradN[i] + gradep*H[i];\n            for (int isol=0; isol<nsol; ++isol)\n                fs += spt.m_gradc[isol]*(R*T*spt.m_k[isol]*H[i]*dms); //fluid mtm bal only\n            double fJ = (dJoJ+phiwhat)*H[i] + gradN[i]*pt.m_vft;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fs.x*detJ;\n            fe[ndpn*i+1] -= fs.y*detJ;\n            fe[ndpn*i+2] -= fs.z*detJ;\n            fe[ndpn*i+3] -= fJ*detJ;\n            for (int isol=0; isol<nsol; ++isol)\n            {\n                double fc = (jd[isol]+je*penalty)*gradN[i] - H[i]*(spt.m_ca[isol]*dJoJ + spt.m_k[isol]*spt.m_cdot[isol] + spt.m_c[isol]*dkdt[isol] - chat[isol]);\n                fe[ndpn*i+4+isol] -= fc*detJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    \n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEFluidSolutesDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    double R = m_pMat->m_Rgas;\n    double T = m_pMat->m_Tabs;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    const double *H, *Gr, *Gs, *Gt;\n    double* gw = el.GaussWeights();\n    vec3d f;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        FEFluidSolutesMaterialPoint& spt = *mp.ExtractData<FEFluidSolutesMaterialPoint>();\n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = invjac0(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the force\n        f = BF.force(mp);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        double dms = m_pMat->m_diffMtmSupp;\n        double penalty = m_pMat->m_penalty;\n        vector<vec3d> jb(nsol,vec3d(0,0,0));\n        vec3d je(0,0,0);\n        \n        // evaluate sedimentation fluxes and effective flux contribution\n        for (int isol=0; isol<nsol; ++isol) {\n            double M = m_pMat->GetSolute(isol)->MolarMass();\n            // get the sedimentation coefficient\n            double s = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp)*M/(R*T);\n            // get the sedimentation flux\n            jb[isol] = f*(s*spt.m_ca[isol]);\n            // get the charge number\n            double z = m_pMat->GetSolute(isol)->ChargeNumber();\n            je += jb[isol]*z;\n            dens += M*spt.m_ca[isol]*dms;\n        }\n        \n        // evaluate spatial gradient of shape functions\n        for (int i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fs = f*H[i]*dens;\n            \n            fe[ndpn*i  ] -= fs.x*detJ;\n            fe[ndpn*i+1] -= fs.y*detJ;\n            fe[ndpn*i+2] -= fs.z*detJ;\n            for (int isol=0; isol<nsol; ++isol)\n            {\n                double fc = -gradN[i]*(jb[isol] + je*penalty);\n                fe[ndpn*i+4+isol] -= fc*detJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEFluidSolutesDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    // jacobian\n    double Ji[3][3], detJ;\n    const double *H, *Gr, *Gs, *Gt;\n    double* gw = el.GaussWeights();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    vec3d f;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        FEFluidSolutesMaterialPoint& spt = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Fluid()->Density(mp);\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double dms = m_pMat->m_diffMtmSupp;\n        double penalty = m_pMat->m_penalty;\n        vector<double> M(nsol);\n        vector<int> z(nsol);\n        vector<double> d0(nsol);\n        vector<double> s(nsol);\n        vector<vector<double>> d0p(nsol, vector<double>(nsol));\n        vector<vector<double>> wkcc(nsol, vector<double>(nsol));\n        vector<double> wkce(nsol,0);\n        vector<vec3d> wkvc(nsol);\n        vector<double> wkcJ(nsol,0);\n\n        // get the force\n        f = BF.force(mp);\n        \n        vec3d wkvJ = f*(-dens/(1+pt.m_ef));\n        double wkcJe = 0;\n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            M[isol] = m_pMat->GetSolute(isol)->MolarMass();\n            d0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            s[isol] = d0[isol]*M[isol]/(R*T);\n            wkvJ += f*(M[isol]*spt.m_dkdJ[isol]*spt.m_c[isol]*dms);\n            wkvc[isol] = f*(M[isol]*spt.m_k[isol]*dms);\n            wkcJ[isol] = s[isol]*spt.m_dkdJ[isol]*spt.m_c[isol];\n            wkcJe += z[isol]*wkcJ[isol];\n            for (int jsol=0; jsol<nsol; ++jsol)\n            {\n                d0p[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp, jsol);\n                wkcc[isol][jsol] = s[isol]*spt.m_dkdc[isol][jsol]*spt.m_c[isol];\n                wkce[jsol] += z[isol]*wkcc[isol][jsol];\n                wkvc[isol] += f*(M[jsol]*spt.m_dkdc[jsol][isol]*spt.m_c[jsol]*dms);\n            }\n        }\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (int i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        for (int i=0, i4=0; i<neln; ++i, i4 += ndpn) {\n            for (int j=0, j4 = 0; j<neln; ++j, j4 += ndpn)\n            {\n                vec3d kvJ = wkvJ*(H[i]*H[j]*detJ);\n                ke[i4  ][j4+3] += kvJ.x;\n                ke[i4+1][j4+3] += kvJ.y;\n                ke[i4+2][j4+3] += kvJ.z;\n                \n                for (int isol = 0; isol<nsol; ++isol)\n                {\n                    vec3d kvc = wkvc[isol]*(H[i]*H[j]*detJ);\n                    ke[i4  ][j4+4+isol] += kvc.x;\n                    ke[i4+1][j4+4+isol] += kvc.y;\n                    ke[i4+2][j4+4+isol] += kvc.z;\n                    \n                    double kcJ = -(gradN[i]*f)*H[j]*(wkcJ[isol] + wkcJe)*detJ;\n                    ke[i4+4+isol][j4+3] += kcJ;\n                    \n                    for(int jsol=0; jsol<nsol; ++jsol)\n                    {\n                        double kd = (isol == jsol) ? 1 : 0;\n                        double kcc = -(gradN[i]*f)*H[j]*((kd + z[jsol])*s[jsol]*spt.m_k[jsol] + wkcc[isol][jsol] + wkce[jsol])*detJ;\n\n                        ke[i4+4+isol][j4+4+jsol] += kcc;\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEFluidSolutesDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i4, j, j4, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 4 + nsol;\n    \n    const int nreact = m_pMat->Reactions();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf);\n    \n    double *H, *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFluidSolutesMaterialPoint& spt = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n\n        // get the tangents\n        mat3ds svJ = m_pMat->Fluid()->GetViscous()->Tangent_Strain(mp);\n        tens4ds cv = m_pMat->Fluid()->Tangent_RateOfDeformation(mp);\n        double dep = m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        double d2ep = m_pMat->Fluid()->Tangent_Pressure_Strain_Strain(mp);\n        // Jdot/J\n        double dJoJ = pt.m_efdot/Jf;\n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double dms = m_pMat->m_diffMtmSupp;\n        double penalty = m_pMat->m_penalty;\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodJ = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Strain(mp);\n        vector<double> dodc(nsol);\n        vector<double> M(nsol);\n        vector<int> z(nsol);\n        vector<double> d0(nsol);\n        vector<vector<double>> d0c(nsol, vector<double>(nsol));\n        \n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            M[isol] = m_pMat->GetSolute(isol)->MolarMass();\n            d0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            for (int jsol=0; jsol<nsol; ++jsol)\n                d0c[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp, jsol);\n        }\n        \n        //Get dk/dt (partial differential wrt time)\n        vector<double> dkdt(nsol,0);\n        vector<vec3d> gradk(nsol,vec3d(0,0,0));\n        vector<double> sum1(nsol,0);\n        vector<vec3d> sum2(nsol,vec3d(0,0,0));\n        vector<vec3d> sum3(nsol,vec3d(0,0,0));\n        vec3d ovJ(0,0,0);\n        vector<vec3d> ovc(nsol,vec3d(0,0,0));\n        for (int isol=0; isol<nsol; ++isol)\n        {\n            dkdt[isol] = pt.m_efdot*spt.m_dkdJ[isol];\n            gradk[isol] = pt.m_gradef*spt.m_dkdJ[isol];\n            sum1[isol] = (spt.m_k[isol]/Jf + spt.m_dkdJ[isol])*spt.m_cdot[isol];\n            sum2[isol] = spt.m_gradc[isol]*(d0[isol]*spt.m_dkdJ[isol]);\n            ovJ += spt.m_gradc[isol]*spt.m_dkdJ[isol];\n            for (int jsol=0; jsol<nsol; ++jsol)\n            {\n                dkdt[isol] += spt.m_dkdc[isol][jsol]*spt.m_cdot[jsol];\n                gradk[isol] += spt.m_gradc[jsol]*spt.m_dkdc[isol][jsol];\n                sum1[isol] += spt.m_c[isol]*(spt.m_dkdc[isol][jsol]/Jf)*spt.m_cdot[jsol];\n                sum2[isol] += spt.m_gradc[jsol]*(z[jsol]*d0[jsol]*spt.m_dkdJ[jsol]);\n                sum3[isol] += spt.m_gradc[jsol]*(z[jsol]*(spt.m_dkdc[jsol][isol]*d0[jsol] + spt.m_k[jsol]*d0c[jsol][isol]));\n                ovc[isol] += spt.m_gradc[jsol]*spt.m_dkdc[jsol][isol];\n            }\n        }\n        \n        ovJ *= R*T*dms;\n        \n        // evaluate the chat\n        vector<double> vbardzdc(nsol, 0.0);\n        vector<vector<double>> dchatdc(nsol, vector<double>(nsol, 0.0));\n        \n        // chemical reactions\n        for (int isol = 0; isol < nsol; ++isol)\n        {\n            for (int jsol = 0; jsol < nsol; ++jsol) {\n                for (i=0; i<nreact; ++i) {\n                    double v = m_pMat->GetReaction(i)->m_v[isol];\n                    double dzdc = m_pMat->GetReaction(i)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                    dchatdc[isol][jsol] += v*dzdc;\n                }\n            }\n        }\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i4=0; i<neln; ++i, i4 += ndpn)\n        {\n            for (j=0, j4 = 0; j<neln; ++j, j4 += ndpn)\n            {\n                mat3d Kvv = vdotTdotv(gradN[i], cv, gradN[j]);\n                vec3d kJv = (pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j];\n                vec3d kvJ = ((pt.m_gradef*d2ep + ovJ)*H[j] + gradN[j]*dep)*H[i] + svJ*gradN[i]*H[j];\n                double kJJ = (H[j]*(ksi/dt - dJoJ) + gradN[j]*pt.m_vft)*H[i]/Jf;\n                \n                ke[i4  ][j4  ] += Kvv(0,0)*detJ;\n                ke[i4  ][j4+1] += Kvv(0,1)*detJ;\n                ke[i4  ][j4+2] += Kvv(0,2)*detJ;\n                ke[i4  ][j4+3] += kvJ.x*detJ;\n                \n                ke[i4+1][j4  ] += Kvv(1,0)*detJ;\n                ke[i4+1][j4+1] += Kvv(1,1)*detJ;\n                ke[i4+1][j4+2] += Kvv(1,2)*detJ;\n                ke[i4+1][j4+3] += kvJ.y*detJ;\n                \n                ke[i4+2][j4  ] += Kvv(2,0)*detJ;\n                ke[i4+2][j4+1] += Kvv(2,1)*detJ;\n                ke[i4+2][j4+2] += Kvv(2,2)*detJ;\n                ke[i4+2][j4+3] += kvJ.z*detJ;\n                \n                ke[i4+3][j4  ] += kJv.x*detJ;\n                ke[i4+3][j4+1] += kJv.y*detJ;\n                ke[i4+3][j4+2] += kJv.z*detJ;\n                ke[i4+3][j4+3] += kJJ*detJ;\n                \n                for (int isol=0; isol<nsol; ++isol) {\n                    vec3d kcv = (pt.m_gradef*spt.m_ca[isol]/Jf + spt.m_gradc[isol]*spt.m_k[isol] + gradk[isol]*spt.m_c[isol])*(-H[i]*H[j]);\n                    vec3d kvc = (gradN[j]*spt.m_k[isol] + ovc[isol]*H[j])*(H[i]*R*T*dms);\n                    double kJc = 0;\n                    double kcJ = (spt.m_ca[isol]*pt.m_efdot + spt.m_k[isol]*spt.m_cdot[isol] + spt.m_c[isol]*dkdt[isol])*H[i]*H[j]/Jf\n                    - spt.m_c[isol]*(spt.m_k[isol]/Jf + spt.m_dkdJ[isol])*H[i]*(ksi/dt*H[j] + gradN[j]*pt.m_vft)\n                    - spt.m_c[isol]*dJoJ*(2*spt.m_dkdJ[isol])*H[i]*H[j]\n                    - sum1[isol]*H[i]*H[j] - (gradN[i]*sum2[isol])*H[j];\n                    \n                    int irow = i4+4+isol;\n                    int jrow = j4+4+isol;\n                    \n                    ke[i4  ][jrow] += kvc.x*detJ;\n                    ke[i4+1][jrow] += kvc.y*detJ;\n                    ke[i4+2][jrow] += kvc.z*detJ;\n                    ke[i4+3][jrow] += kJc*detJ;\n                    ke[irow][j4  ] += kcv.x*detJ;\n                    ke[irow][j4+1] += kcv.y*detJ;\n                    ke[irow][j4+2] += kcv.z*detJ;\n                    ke[irow][j4+3] += kcJ*detJ;\n                    \n                    for(int jsol=0; jsol<nsol; ++jsol)\n                    {\n                        double kd = (jsol == isol) ? 1 : 0;\n                        double kcc = -H[i]*(kd*spt.m_k[jsol] + spt.m_c[isol]*spt.m_dkdc[isol][jsol])*(H[j]*(ksi/dt + dJoJ) + gradN[j]*pt.m_vft)\n                        + H[i]*H[j]*dchatdc[isol][jsol]\n                        - gradN[i]*(gradN[j]*(spt.m_k[jsol]*d0[jsol]*kd) + spt.m_gradc[isol]*(d0[isol]*spt.m_dkdc[isol][jsol] + spt.m_k[isol]*d0c[isol][jsol])*H[j])\n                        - gradN[i]*(gradN[j]*(z[jsol]*spt.m_k[jsol]*d0[jsol]) + sum3[jsol]*H[j]);\n                        ke[irow][j4+4+jsol] += kcc*detJ;\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int nsol = m_pMat->Solutes();\n        int ndpn = 4 + nsol;\n        int ndof = ndpn*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate material stiffness\n        ElementStiffness(el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        const int nsol = m_pMat->Solutes();\n        const int ndpn = 4 + nsol;\n        int ndof = ndpn*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementMassMatrix(el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        const int nsol = m_pMat->Solutes();\n        const int ndpn = 4 + nsol;\n        int ndof = ndpn*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate element body force stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEFluidSolutesDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i4, j, j4, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 4 + nsol;\n\n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf)*m_btrans;\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        \n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i4=0; i<neln; ++i, i4 += ndpn)\n        {\n            for (j=0, j4 = 0; j<neln; ++j, j4 += ndpn)\n            {\n                mat3d Mv = ((mat3dd(ksi/dt) + pt.m_Lf)*H[j] + mat3dd(gradN[j]*pt.m_vft))*(H[i]*dens*detJ);\n                vec3d mJ = pt.m_aft*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                \n                ke[i4  ][j4  ] += Mv(0,0);\n                ke[i4  ][j4+1] += Mv(0,1);\n                ke[i4  ][j4+2] += Mv(0,2);\n                ke[i4  ][j4+3] += mJ.x;\n                \n                ke[i4+1][j4  ] += Mv(1,0);\n                ke[i4+1][j4+1] += Mv(1,1);\n                ke[i4+1][j4+2] += Mv(1,2);\n                ke[i4+1][j4+3] += mJ.y;\n                \n                ke[i4+2][j4  ] += Mv(2,0);\n                ke[i4+2][j4+1] += Mv(2,1);\n                ke[i4+2][j4+2] += Mv(2,2);\n                ke[i4+2][j4+3] += mJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i, tp);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (NegativeJacobian::DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEFluidSolutesDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n\n    // number of solutes\n    const int nsol = m_pMat->Solutes();\n\n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d vt[NELN], vp[NELN];\n    vec3d at[NELN], ap[NELN];\n    double et[NELN], ep[NELN];\n    double aet[NELN], aep[NELN];\n    vector< vector<double> > ct(nsol, vector<double>(NELN));\n    vector< vector<double> > cp(nsol, vector<double>(NELN));\n    vector< vector<double> > act(nsol, vector<double>(NELN));\n    vector< vector<double> > acp(nsol, vector<double>(NELN));\n    vector<int> sid(nsol);\n    for (int j=0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        vt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        vp[j] = node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n        at[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n        ap[j] = node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n        et[j] = node.get(m_dofEF);\n        ep[j] = node.get_prev(m_dofEF);\n        aet[j] = node.get(m_dofAEF);\n        aep[j] = node.get_prev(m_dofAEF);\n        for (int k=0; k<nsol; ++k) {\n            ct[k][j] = node.get(m_dofC + sid[k]);\n            cp[k][j] = node.get_prev(m_dofC + sid[k]);\n            act[k][j] = node.get(m_dofAC + sid[k]);\n            acp[k][j] = node.get_prev(m_dofAC + sid[k]);\n        }\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFluidSolutesMaterialPoint& spt = *(mp.ExtractData<FEFluidSolutesMaterialPoint>());\n\n        // material point data\n        pt.m_vft = el.Evaluate(vt, n)*alphaf + el.Evaluate(vp, n)*(1-alphaf);\n        pt.m_Lf = gradient(el, vt, n)*alphaf + gradient(el, vp, n)*(1-alphaf);\n        pt.m_aft = pt.m_Lf*pt.m_vft;\n        if (m_btrans) pt.m_aft += el.Evaluate(at, n)*alpham + el.Evaluate(ap, n)*(1-alpham);\n        pt.m_ef = el.Evaluate(et, n)*alphaf + el.Evaluate(ep, n)*(1-alphaf);\n        pt.m_gradef = gradient(el, et, n)*alphaf + gradient(el, ep, n)*(1-alphaf);\n        pt.m_efdot = pt.m_gradef*pt.m_vft;\n        if (m_btrans) pt.m_efdot += el.Evaluate(aet, n)*alpham + el.Evaluate(aep, n)*(1-alpham);\n        for (int isol=0; isol < nsol; ++isol) {\n            spt.m_c[isol] = el.Evaluate(ct[isol], n)*alphaf + el.Evaluate(cp[isol], n)*(1-alphaf);\n            spt.m_gradc[isol] = gradient(el, ct[isol], n)*alphaf + gradient(el, cp[isol], n)*(1-alphaf);\n            spt.m_cdot[isol] = spt.m_gradc[isol]*pt.m_vft;\n            if (m_btrans) spt.m_cdot[isol] += el.Evaluate(act[isol], n)*alpham + el.Evaluate(acp[isol], n)*(1-alpham);\n        }\n        \n        m_pMat->PartitionCoefficientFunctions(mp, spt.m_k, spt.m_dkdJ, spt.m_dkdc);\n        \n        // calculate the stress at this material point\n        pt.m_sf = m_pMat->Fluid()->Stress(mp);\n        \n        spt.m_pe = m_pMat->Fluid()->Pressure(mp);\n        \n        // calculate the solute flux and actual concentration\n        for (int isol=0; isol < nsol; ++isol)\n        {\n            spt.m_j[isol] = m_pMat->SoluteDiffusiveFlux(mp, isol);\n            spt.m_ca[isol] = m_pMat->ConcentrationActual(mp, isol);\n        }\n        \n        // calculate the fluid pressure\n        pt.m_pf = m_pMat->PressureActual(mp);\n        \n        spt.m_psi = m_pMat->ElectricPotential(mp);\n        spt.m_Ie = m_pMat->CurrentDensity(mp);\n        \n        // update chemical reaction element data\n        for (int j=0; j<m_pMat->Reactions(); ++j)\n            m_pMat->GetReaction(j)->UpdateElementData(mp);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        const int nsol = m_pMat->Solutes();\n        const int ndpn = 4+nsol;\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInertialForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        double dens = m_pMat->Fluid()->Density(mp);\n        \n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d f = pt.m_aft*(dens*H[i]);\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= f.x*detJ;\n            fe[ndpn*i+1] -= f.y*detJ;\n            fe[ndpn*i+2] -= f.z*detJ;\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEFluidDomain.h\"\n#include \"FEFluidSolutes.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEFluidSolutesDomain3D : public FESolidDomain, public FEFluidDomain\n{\npublic:\n    //! constructor\n    FEFluidSolutesDomain3D(FEModel* pfem);\n    ~FEFluidSolutesDomain3D() {}\n    \n    //! assignment operator\n    FEFluidSolutesDomain3D& operator = (FEFluidSolutesDomain3D& d);\n    \n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n\n\t//! get the total dofs\n\tconst FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEElasticDomain\n    \n    //! initialize class\n    bool Init() override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! Reset data\n    void Reset() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! initialize material points in the domain\n    void InitMaterialPoints() override;\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n    \n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n\n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n\n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n\n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n\n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEFluidSolutes*     m_pMat;\n    \nprotected:\n    FEDofList           m_dofW;\n    FEDofList           m_dofAW;\n\tFEDofList           m_dof;\n\tint                 m_dofEF;\n    int                 m_dofAEF;\n    int                 m_dofC;\n    int                 m_dofAC;\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutesDomainFactory.h\"\n#include \"FEFluidSolutes.h\"\n#include \"FEFluidDomain.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEFluidSolutesDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n    FEModel* pfem = pmat->GetFEModel();\n    FE_Element_Class eclass = spec.eclass;\n    FE_Element_Shape eshape = spec.eshape;\n    const char* sztype = 0;\n    if (dynamic_cast<FEFluidSolutes*>(pmat))\n    {\n        // fluid elements\n        if      (eclass==FE_ELEM_SOLID) sztype = \"fluid-solutes-3D\";\n        else return 0;\n    }\n    \n    if (sztype)\n    {\n        FEDomain* pd = fecore_new<FESolidDomain>(sztype, pfem);\n        if (pd) pd->SetMaterial(pmat);\n        return pd;\n    }\n    else return 0;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEFluidSolutesDomainFactory : public FEDomainFactory\n{\npublic:\n    virtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutesFlux.h\"\n#include \"FEBioFluidSolutes.h\"\n#include <FEBioMix/FESoluteInterface.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/FEAnalysis.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidSolutesFlux, FESurfaceLoad)\nADD_PARAMETER(m_flux   , \"flux\")->setUnits(\"n/L^2.t\")->setLongName(\"effective solute molar flux\");\nADD_PARAMETER(m_isol   , \"solute_id\")->setEnums(\"$(solutes)\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidSolutesFlux::FEFluidSolutesFlux(FEModel* pfem) : FESurfaceLoad(pfem), m_dofC(pfem)\n{\n    m_flux = 1.0;\n    m_isol = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEFluidSolutesFlux::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n    m_flux.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the prescribed normal component of velocity\nvoid FEFluidSolutesFlux::LoadVector(FEGlobalVector& R)\n{\n    FEFluidSolutesFlux* flux = this;\n    m_psurf->LoadVector(R, m_dofC, true, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n        \n        double wr = flux->m_flux(mp);\n        \n        vec3d dxt = mp.dxr ^ mp.dxs;\n        \n        // volumetric flow rate\n        double f = dxt.norm()*wr;\n        \n        double H_i = dof_a.shape;\n        fa[0] = H_i * f;\n    });\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidSolutesFlux::Init()\n{\n    if (m_isol == -1) return false;\n    \n    // set up the dof lists\n    FEModel* fem = GetFEModel();\n    m_dofC.Clear();\n    \n    m_dofC.AddDof(fem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), m_isol-1));\n\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofC);\n    \n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidSolutesFlux::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    \n    if (ar.IsShallow() == false)\n    {\n        ar & m_dofC;\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The flux surface is a surface domain that sustains a solute flux boundary\n//! condition for FluidSolutesDomain\nclass FEBIOFLUID_API FEFluidSolutesFlux : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidSolutesFlux(FEModel* pfem);\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    void SetSolute(int isol) { m_isol = isol; }\n    \n    //! initialization\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    \nprivate:\n    FEParamDouble    m_flux;        //!< flux scale factor magnitude\n    int        m_isol;        //!< solute index\n    \nprivate:\n    FEDofList        m_dofC;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesNaturalFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n//For now only allows for one solute at a time, jn=-d0*gradc+c*vf\n//Stiffness does not include effect from different solutes\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutesNaturalFlux.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEFluidSolutes.h\"\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include \"FEFluidSolutesDomain3D.h\"\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEFluidSolutesNaturalFlux, FESurfaceLoad)\n    ADD_PARAMETER(m_isol   , \"solute_id\")->setEnums(\"$(solutes)\");\n    ADD_PARAMETER(m_bup    , \"update\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidSolutesNaturalFlux::FEFluidSolutesNaturalFlux(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem), m_dofEF(pfem), m_dofC(pfem)\n{\n    m_isol = -1;\n    m_bup = false;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEFluidSolutesNaturalFlux::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidSolutesNaturalFlux::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    \n    if (ar.IsShallow() == false)\n    {\n        ar & m_dofC & m_dofW & m_dofEF;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidSolutesNaturalFlux::Init()\n{\n    if (m_isol <= 0) return false;\n    \n    // set up the dof lists\n    FEModel* fem = GetFEModel();\n    m_dofC.Clear();\n    m_dofW.Clear();\n    m_dofEF.Clear();\n    m_dofC.AddDof(fem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), m_isol - 1));\n    m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n    m_dofEF.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION));\n\n    m_dof.AddDofs(m_dofW);\n    m_dof.AddDofs(m_dofC);\n\n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesNaturalFlux::Update()\n{\n    if (m_bup) {\n        for (int is=0; is<m_psurf->Elements(); ++is)\n        {\n            // get surface element\n            FESurfaceElement& el = m_psurf->Element(is);\n            // get underlying solid element\n            FESolidElement* pe = dynamic_cast<FESolidElement*>(el.m_elem[0].pe);\n            if (pe == nullptr) break;\n            // get element data\n            int neln = pe->Nodes();\n            int nint = pe->GaussPoints();\n            \n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            // get the local solute id\n            FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n            if (psi == nullptr) break;\n            int sid = psi->FindLocalSoluteID(m_isol);\n            if (sid == -1) break;\n            \n            // identify nodes on the surface\n            vector<bool> nsrf(neln,false);\n            for (int j=0; j<neln; ++j) {\n                for (int k=0; k < el.Nodes(); ++k) {\n                    if (el.m_node[k] == pe->m_node[j]) nsrf[j] = true;\n                }\n            }\n            \n            // get average effective concentration of nodes not on surface\n            double cavg = 0;\n            int m = 0;\n            for (int i=0; i<neln; ++i) {\n                if (!nsrf[i]) {\n                    int n = pe->m_node[i];\n                    FENode& node = GetMesh().Node(n);\n                    int dof = m_dofC[m_isol-1];\n                    if (dof != -1) {\n                        cavg += node.get(dof);\n                        ++m;\n                    }\n                }\n            }\n            // assign this average value to surface nodes as initial guess\n            if (m) {\n                cavg /= m;\n                for (int i=0; i<neln; ++i) {\n                    if (nsrf[i]) {\n                        int n = pe->m_node[i];\n                        FENode& node = GetMesh().Node(n);\n                        int dof = m_dofC[m_isol-1];\n                        if (dof != -1) node.set(dof, cavg);\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesNaturalFlux::LoadVector(FEGlobalVector& R)\n{\n    FEFluidSolutesNaturalFlux* flux = this;\n    m_psurf->LoadVector(R, m_dofC, true, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, std::vector<double>& fa) {\n        \n        const FETimeInfo& tp = GetTimeInfo();\n        \n        // get surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        // get underlying solid element\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) {\n            fa[0] = 0;\n            return;\n        }\n        FEFluidSolutes* pmat = dynamic_cast<FEFluidSolutes*>(pm);\n        int sid = psi->FindLocalSoluteID(flux->m_isol);\n        vec3d dxt = mp.dxr ^ mp.dxs;\n        \n        // get element-averaged diffusive flux\n        vec3d jd(0,0,0);\n        int nint = pe->GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            jd += pmat->SoluteDiffusiveFlux(pt, m_isol-1);\n        }\n        jd /= nint;\n        \n        // evaluate desired natural solute flux = normal convective flux * area\n        double jn = jd*dxt*tp.alphaf;\n        \n        \n        double H_i = dof_a.shape;\n        fa[0] = H_i * jn;\n    });\n}\n/*{\n    FEFluidSolutesNaturalFlux* flux = this;\n    m_psurf->LoadVector(R, m_dofC, true, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, std::vector<double>& fa) {\n        \n        const FETimeInfo& tp = GetTimeInfo();\n        \n        // get surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        // get underlying solid element\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) {\n            fa[0] = 0;\n            return;\n        }\n        int sid = psi->FindLocalSoluteID(flux->m_isol);\n        vec3d dxt = mp.dxr ^ mp.dxs;\n\n        // get element-averaged partition coefficient\n        double kappa = 0;\n        int nint = pe->GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEFluidSolutesMaterialPoint& ps = *(pt.ExtractData<FEFluidSolutesMaterialPoint>());\n            kappa += ps.m_k[sid];\n        }\n        kappa /= nint;\n\n        // evaluate average effective solute concentration and fluid velocity at nodes\n        vec3d w(0,0,0);\n        double ce = 0, cp = 0;\n        for (int i=0; i<el.Nodes(); ++i) {\n            FENode& node = m_psurf->Node(el.m_lnode[i]);\n            w += node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*tp.alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-tp.alphaf);\n            ce += node.get(m_dofC[m_isol-1])*tp.alphaf + node.get_prev(m_dofC[m_isol-1])*(1-tp.alphaf);\n            cp += node.get_prev(m_dofC[m_isol-1]);\n        }\n        w /= el.Nodes();\n        ce /= el.Nodes();\n        cp /= el.Nodes();\n\n        // evaluate desired natural solute flux = normal convective flux * area\n        double wn = w*dxt;\n        double jn = kappa*ce*wn;\n\n        // molar flow rate (if negative, use previous value of concentration)\n        double f = (wn >= 0) ? jn : kappa*cp*wn;\n//        double f = (jn > 0) ? jn : 0;\n\n        double H_i = dof_a.shape;\n        fa[0] = H_i * f;\n    });\n}*/\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesNaturalFlux::StiffnessMatrix(FELinearSystem& LS)\n{}\n/*{\n    FEFluidSolutesNaturalFlux* flux = this;\n    m_psurf->LoadStiffness(LS, m_dofC, m_dof, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n        \n        const FETimeInfo& tp = GetTimeInfo();\n        \n        // get surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        // get underlying solid element\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) return;\n        int sid = psi->FindLocalSoluteID(flux->m_isol);\n        \n        // get element-averaged solute partition coefficient\n        double kappa = 0, d0 = 0;\n        int nint = pe->GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEFluidSolutesMaterialPoint& ps = *(pt.ExtractData<FEFluidSolutesMaterialPoint>());\n            kappa += ps.m_k[sid];\n            d0 += psi->GetSolute(sid)->m_pDiff->Free_Diffusivity(pt);\n        }\n        kappa /= nint;\n        d0 /= nint;\n\n        // evaluate average effective solute concentration and fluid velocity\n        vec3d w(0,0,0);\n        double ce = 0;\n        for (int i=0; i<el.Nodes(); ++i) {\n            FENode& node = m_psurf->Node(el.m_lnode[i]);\n            w += node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*tp.alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-tp.alphaf);\n            ce += node.get(m_dofC[m_isol-1])*tp.alphaf + node.get_prev(m_dofC[m_isol-1])*(1-tp.alphaf);\n        }\n        w /= el.Nodes();\n        ce /= el.Nodes();\n\n        // shape functions and derivatives\n        double H_i  = dof_a.shape;\n        double H_j  = dof_b.shape;\n        \n        // calculate surface normal\n        vec3d dxt = mp.dxr ^ mp.dxs;\n        \n        // calculate stiffness component\n        vec3d kcv = dxt*(H_i*H_j*kappa*ce*tp.alphaf);\n        double kcc = (dxt*w)*(H_i*H_j*kappa)*tp.alphaf;\n        \n        Kab[0][0] = -kcv.x;\n        Kab[0][1] = -kcv.y;\n        Kab[0][2] = -kcv.z;\n        Kab[0][3] = -kcc;\n    });\n}*/\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesNaturalFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n#include \"FEFluidSolutes.h\"\n\n//-----------------------------------------------------------------------------\n//! Backflow stabilization prescribes a normal traction that opposes\n//! backflow on a boundary surface.\nclass FEBIOFLUID_API FEFluidSolutesNaturalFlux : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidSolutesNaturalFlux(FEModel* pfem);\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    void SetSolute(int isol) { m_isol = isol; }\n    \n    //! calculate flux stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate residual\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! update\n    void Update() override;\n    \nprotected:\n    int         m_isol;      //!< solute index\n    bool        m_bup;          //!< flag to call Update function\n    \n    // degrees of freedom\n    FEDofList    m_dofW;\n    FEDofList    m_dofEF;\n    FEDofList    m_dofC;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesPressureBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFluidSolutesPressureBC.h\"\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidSolutesPressureBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_p, \"pressure\")->setUnits(\"P\")->setLongName(\"fluid pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidSolutesPressureBC::FEFluidSolutesPressureBC(FEModel* pfem) : FEPrescribedSurface(pfem)\n{\n    m_p = 0;\n    m_dofEF = -1;\n    m_dofC = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidSolutesPressureBC::Init()\n{\n    m_dofEF = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION), 0);\n    m_dofC = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0);\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n    m_e.assign(GetSurface()->Nodes(), 0.0);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEFluidSolutesPressureBC::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n\n    int N = ps->Nodes();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    std::vector<vector<double>> caNodes(N, vector<double>());\n\n    //Project sum of all ca and osc values from int points to nodes on surface\n    //All values put into map, including duplicates\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        // evaluate average prescribed pressure on this face\n        double p = 0;\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMaterialPoint* pt = el.GetMaterialPoint(j);\n            p += m_p(*pt);\n        }\n        p /= el.GaussPoints();\n        FEElement* e = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        if (se) {\n            double efo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetPartitionCoefficient(*pt, k);\n                }\n                osc /= nint;\n                for (int k=0; k<nsol; ++k) kappa[k] /= nint;\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    // evaluate dilatation at this node\n                    bool good = pfl->Dilatation(0, p - m_Rgas*m_Tabs*osc*osm, efo[j]);\n                    assert(good);\n                }\n            }\n            else {\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // evaluate dilatation at this node\n                    bool good = pfl->Dilatation(0, p, efo[j]);\n                    assert(good);\n                }\n            }\n            // only keep the dilatations at the nodes of the surface face\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(efo[j]);\n        }\n        //If no solid element, insert all 0s\n        else {\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(0);\n        }\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n            \n        // store value for now\n        m_e[i] = ef;\n    }\n \n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesPressureBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidSolutesPressureBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidSolutesPressureBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofC & m_dofEF;\n    ar & m_Rgas & m_Tabs;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesPressureBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEFluidSolutes.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidResistanceBC is a fluid surface that has a normal\n//! pressure proportional to the flow rate (resistance).\n//!\nclass FEBIOFLUID_API FEFluidSolutesPressureBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidSolutesPressureBC(FEModel* pfem);\n\n    //! set the dilatation\n    void Update() override;\n\n    //! initialize\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    FEParamDouble   m_p;       //!< prescribed fluid pressure\n    vector<double>  m_e;\n\nprivate:\n    double      m_Rgas;\n    double      m_Tabs;\n\n    int        m_dofEF;\n    int        m_dofC;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesRCRBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutesRCRBC.h\"\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidSolutesRCRBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_Rd, \"Rd\")->setUnits(\"F.t/L^5\");\n    ADD_PARAMETER(m_p0, \"initial_pressure\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_pd, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_C , \"capacitance\")->setUnits(\"L^5/F\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidSolutesRCRBC::FEFluidSolutesRCRBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_p0 = 0;\n    m_Rd = 0.0;\n    m_pd = 0.0;\n    m_C = 0.0;\n    m_dofEF = -1;\n    m_dofC = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\n//! TODO: Generalize to include the initial conditions\nbool FEFluidSolutesRCRBC::Init()\n{\n    m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION), 0);\n    m_dofC = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0);\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    \n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n    m_e.assign(GetSurface()->Nodes(), 0.0);\n    \n    m_pn = m_pp = m_p0;\n    m_pdn = m_pdp = m_pd;\n    m_qn = m_qp = 0;\n    m_tp = 0;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesRCRBC::Update()\n{\n    // Check if we started a new time, if so, update variables\n    FETimeInfo& timeInfo = GetFEModel()->GetTime();\n    double time = timeInfo.currentTime;\n    int iter = timeInfo.currentIteration;\n    double dt = timeInfo.timeIncrement;\n    if ((time > m_tp) && (iter == 0)) {\n        m_pp = m_pn;\n        m_qp = m_qn;\n        m_pdp = m_pdn;\n        m_tp = time;\n    }\n    \n    // evaluate the flow rate at the current time\n    m_qn = FlowRate();\n    m_pdn = m_pd;\n    \n    double tau = m_Rd * m_C;\n    \n    // calculate the RCR pressure\n    m_pn = m_pdn + (m_Rd / (1 + tau / dt) + m_R) * m_qn + tau / (dt + tau) * (m_pp - m_pdp - m_R * m_qp);\n    \n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n    \n    int N = ps->Nodes();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    std::vector<vector<double>> caNodes(N, vector<double>());\n    \n    // Project mean of osmotic coefficient and partition coefficient from int points to nodes on surface\n    // Use these to evaluate osmolarity at each node on surface\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        FEElement* e = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        if (se) {\n            double efo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetPartitionCoefficient(*pt, k);\n                }\n                osc /= nint;\n                for (int k=0; k<nsol; ++k) kappa[k] /= nint;\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    // evaluate dilatation at this node\n                    double c = m_Rgas*osc*osm;\n                    bool good = pfl->Dilatation(0, m_pn - m_Rgas*m_Tabs*osc*osm, efo[j]);\n                    assert(good);\n                }\n            }\n            else {\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // evaluate dilatation at this node\n                    bool good = pfl->Dilatation(0, m_pn, efo[j]);\n                    assert(good);\n                }\n            }\n            // only keep the dilatations at the nodes of the surface face\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(efo[j]);\n        }\n        //If no solid element, insert all 0s\n        else {\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(0);\n        }\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n        \n        // store value for now\n        m_e[i] = ef;\n    }\n    \n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at current time\ndouble FEFluidSolutesRCRBC::FlowRate()\n{\n    double Q = 0;\n    \n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    const FETimeInfo& tp = GetTimeInfo();\n    double alpha = tp.alpha;\n    double alphaf = tp.alphaf;\n    \n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n    \n    for (int iel=0; iel<ps->Elements(); ++iel)\n    {\n        FESurfaceElement& el = ps->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = ps->Node(el.m_lnode[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n    \n    return Q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesRCRBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidSolutesRCRBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidSolutesRCRBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_e;\n    ar & m_pn & m_pp & m_qn & m_qp & m_pdn & m_pdp & m_tp & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofW & m_dofEF & m_dofC;\n    ar & m_Rgas & m_Tabs;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesRCRBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEFluidSolutes.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidRCRBC is a fluid surface load that implements a 3-element Windkessel model\n//!\nclass FEBIOFLUID_API FEFluidSolutesRCRBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidSolutesRCRBC(FEModel* pfem);\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! evaluate flow rate\n    double FlowRate();\n    \n    //! initialize\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    double          m_R;        //!< flow resistance\n    double          m_Rd;       //!< distal resistance\n    double          m_p0;       //!< initial fluid pressure\n    double          m_C;        //!< capacitance\n    double          m_pd;       //!< downstream pressure\n    \nprivate:\n    double              m_pn;   //!< fluid pressure at current time point\n    double              m_pp;   //!< fluid pressure at previous time point\n    double              m_qn;   //!< flow rate at current time point\n    double              m_qp;   //!< flow rate at previous time point\n    double              m_pdn;  //!< downstream fluid pressure at current time point\n    double              m_pdp;  //!< downstream fluid pressure at previous time point\n    double              m_tp;   //!< previous time\n    vector<double>      m_e;    //!< fluid dilatation\n\n    double          m_Rgas;\n    double          m_Tabs;\n    FEDofList       m_dofW;\n    int             m_dofEF;\n    int             m_dofC;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesResistanceBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolutesResistanceBC.h\"\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidSolutesResistanceBC, FEPrescribedSurface)\n\tADD_PARAMETER(m_R , \"R\")->setUnits(\"F.t/L^5\");\n\tADD_PARAMETER(m_p0, \"pressure_offset\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidSolutesResistanceBC::FEFluidSolutesResistanceBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_R = 0.0;\n    m_p0 = 0;\n    m_dofEF = -1;\n    m_dofC = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidSolutesResistanceBC::Init()\n{\n    m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION), 0);\n    m_dofC = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0);\n    SetDOFList(m_dofEF);\n\n\tif (FEPrescribedSurface::Init() == false) return false;\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n    m_e.assign(GetSurface()->Nodes(), 0.0);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidSolutesResistanceBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofW & m_dofEF & m_dofC;\n    ar & m_Rgas & m_Tabs;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesResistanceBC::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n    \n    int N = ps->Nodes();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    std::vector<vector<double>> caNodes(N, vector<double>());\n    \n    // evaluate the flow rate\n    double Q = FlowRate();\n    \n    // calculate the resistance pressure\n    double p = m_R*Q;\n    \n    // Project mean of osmotic coefficient and partition coefficient from int points to nodes on surface\n    // Use these to evaluate osmolarity at each node on surface\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        FEElement* e = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        if (se) {\n            double efo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetPartitionCoefficient(*pt, k);\n                }\n                osc /= nint;\n                for (int k=0; k<nsol; ++k) kappa[k] /= nint;\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    // evaluate dilatation at this node\n                    double c = m_Rgas*osc*osm;\n                    bool good = pfl->Dilatation(0, p+m_p0 - m_Rgas*m_Tabs*osc*osm, efo[j]);\n                    assert(good);\n                }\n            }\n            else {\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // evaluate dilatation at this node\n                    bool good = pfl->Dilatation(0, p+m_p0, efo[j]);\n                    assert(good);\n                }\n            }\n            // only keep the dilatations at the nodes of the surface face\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(efo[j]);\n        }\n        //If no solid element, insert all 0s\n        else {\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(0);\n        }\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n        \n        // store value for now\n        m_e[i] = ef;\n    }\n    \n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface at the current time\ndouble FEFluidSolutesResistanceBC::FlowRate()\n{\n    double Q = 0;\n    \n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    const FETimeInfo& tp = GetTimeInfo();\n    double alpha = tp.alpha;\n    double alphaf = tp.alphaf;\n\n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n    \n    for (int iel=0; iel<ps->Elements(); ++iel)\n    {\n        FESurfaceElement& el = ps->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = ps->Node(el.m_lnode[i]);\n            rt[i] = node.m_rt;\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            double q = normal*v;\n            Q += q*w[n];\n        }\n    }\n\n    return Q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesResistanceBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEFluidSolutesResistanceBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesResistanceBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEFluidSolutes.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidSolutesResistanceBC is a fluid-solutes surface that has a normal\n//! pressure proportional to the flow rate (resistance).\n//!\nclass FEBIOFLUID_API FEFluidSolutesResistanceBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEFluidSolutesResistanceBC(FEModel* pfem);\n    \n    //! evaluate flow rate\n    double FlowRate();\n    \n    //! initialize\n    bool Init() override;\n\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n\tvoid Update() override;\n    \npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    double\t\t\tm_R;        //!< flow resistance\n    double          m_p0;       //!< fluid pressure offset\n    vector<double>  m_e;        //!< fluid dilatation\n\nprivate:\n    double          m_Rgas;\n    double          m_Tabs;\n    \n\tFEDofList       m_dofW;\n    int             m_dofEF;\n    int             m_dofC;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioFluidSolutes.h\"\n#include \"FEFluidSolutesSolver.h\"\n#include \"FEFluidDomain.h\"\n#include \"FEFluidResidualVector.h\"\n#include \"FEFluidResistanceBC.h\"\n#include \"FEBackFlowStabilization.h\"\n#include \"FEFluidNormalVelocity.h\"\n#include \"FEFluidVelocity.h\"\n#include \"FEFluidRotationalVelocity.h\"\n#include \"FETiedFluidInterface.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <assert.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/sys.h>\n#include <FEBioMech/FEBodyForce.h>\n#include <FEBioMech/FEResidualVector.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEFluidSolutesAnalysis.h\"\n#include <limits>\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEFluidSolutesSolver, FENewtonSolver)\n    ADD_PARAMETER(m_Vtol , \"vtol\"        );\n    ADD_PARAMETER(m_Ftol , \"ftol\"        );\n    ADD_PARAMETER(m_Ctol , \"ctol\"        );\n    ADD_PARAMETER(m_Etol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n    ADD_PARAMETER(m_Rtol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n    ADD_PARAMETER(m_rhoi , \"rhoi\"        );\n    ADD_PARAMETER(m_pred , \"predictor\"   );\n    ADD_PARAMETER(m_minJf, \"min_volume_ratio\");\n    ADD_PARAMETER(m_forcePositive, \"force_positive_concentrations\");\n    ADD_PARAMETER(m_solve_strategy, \"solve_strategy\")->setEnums(\"coupled\\0sequential\\0\");\n    ADD_PARAMETER(m_cmin , \"min_C_drop\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n    ADD_PARAMETER(m_cmax , \"min_C_rise\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n    ADD_PARAMETER(m_cnum , \"min_C_num\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEFluidSolutesSolver Construction\n//\nFEFluidSolutesSolver::FEFluidSolutesSolver(FEModel* pfem) : FENewtonSolver(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofEF(pfem), m_dofC(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Vtol = 0.001;\n    m_Ftol = 0.001;\n    m_Ctol = 0.01;\n    m_Rmin = 1.0e-20;\n    m_Rmax = 0;     // not used if zero\n    m_minJf = 0;    // not used if zero\n    m_cmin = 0;     // not used if zero\n    m_cmax = 0;     // not used if zero\n    m_cnum = 1;\n    \n    m_nveq = 0;\n    m_ndeq = 0;\n    m_niter = 0;\n    \n    // assume non-symmetric stiffness\n    m_msymm = REAL_UNSYMMETRIC;\n    \n    m_forcePositive = true;    // force all concentrations to remain positive\n\n\tm_solve_strategy = SOLVE_COUPLED;\n    \n    m_rhoi = 0;\n    m_pred = 0;\n    \n    m_sudden_C_change = false;\n    \n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n    \n    // turn off checking for a zero diagonal\n    CheckZeroDiagonal(false);\n    \n}\n\n//-----------------------------------------------------------------------------\nFEFluidSolutesSolver::~FEFluidSolutesSolver()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEFluidSolutesSolver\n//\nbool FEFluidSolutesSolver::Init()\n{\n    // initialize base class\n    if (FENewtonSolver::Init() == false) return false;\n    \n    // check parameters\n    if (m_Vtol <  0.0) { feLogError(\"vtol must be nonnegative.\"); return false; }\n    if (m_Ftol <  0.0) { feLogError(\"dtol must be nonnegative.\"); return false; }\n    if (m_Ctol <  0.0) { feLogError(\"ctol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_gammaf = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        m_alpham = (3-m_rhoi)/(1+m_rhoi)/2;\n        m_gammaf = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    m_vi.assign(m_nveq,0);\n    m_Vi.assign(m_nveq,0);\n    m_di.assign(m_ndeq,0);\n    m_Di.assign(m_ndeq,0);\n    \n    // get number of DOFS\n    FEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    // allocate concentration-vectors\n    m_ci.assign(MAX_CDOFS,vector<double>(0,0));\n    m_Ci.assign(MAX_CDOFS,vector<double>(0,0));\n    for (int i=0; i<MAX_CDOFS; ++i) {\n        m_ci[i].assign(m_nceq[i], 0);\n        m_Ci[i].assign(m_nceq[i], 0);\n    }\n    vector<int> dofs;\n    for (int j=0; j<(int)m_nceq.size(); ++j) {\n        if (m_nceq[j]) {\n            dofs.push_back(m_dofC[j]);\n        }\n    }\n\n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n    FEMesh& mesh = fem.GetMesh();\n    gather(m_Ut, mesh, m_dofW[0]);\n    gather(m_Ut, mesh, m_dofW[1]);\n    gather(m_Ut, mesh, m_dofW[2]);\n    gather(m_Ut, mesh, m_dofEF[0]);\n    gather(m_Ut, mesh, dofs);\n\n    // set flag for transient or steady-state analyses\n    for (int i = 0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        if (fem.GetCurrentStep()->m_nanalysis == FEFluidSolutesAnalysis::STEADY_STATE)\n            dom.SetSteadyStateAnalysis();\n        else\n            dom.SetTransientAnalysis();\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEFluidSolutesSolver::InitEquations()\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n    m_dofAW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_ACCELERATION));\n    m_dofEF.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION));\n    m_dofAEF = fem.GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION_TDERIV), 0);\n    m_dofC.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    m_dofAC = fem.GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV), 0);\n\n    // Add the solution variables\n    AddSolutionVariable(&m_dofW, 1, \"velocity\", m_Vtol);\n    AddSolutionVariable(&m_dofEF, 1, \"dilatation\", m_Ftol);\n    AddSolutionVariable(&m_dofC, 1, \"concentration\", m_Ctol);\n\n    // base class initialization\n    if (FENewtonSolver::InitEquations() == false) return false;\n    \n    // determined the nr of velocity and dilatation equations\n    m_nveq = m_ndeq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofW[0]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2]] != -1) m_nveq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_ndeq++;\n    }\n    \n    // determine the nr of concentration equations\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    m_nceq.assign(MAX_CDOFS, 0);\n    \n    // get number of DOFS\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            if (n.m_ID[m_dofC[j]] != -1) m_nceq[j]++;\n        }\n    }\n\n\t// get the total concentration equations\n\tm_nseq = 0;\n\tfor (int i = 0; i < MAX_CDOFS; ++i) m_nseq += m_nceq[i];\n\n\t// check that we are using a block scheme for sequential solves\n\tif ((m_solve_strategy == SOLVE_SEQUENTIAL) && (m_eq_scheme != EQUATION_SCHEME::BLOCK))\n\t{\n\t\tfeLogWarning(\"You need a block solver when using the sequential solve strategy.\");\n\t\treturn false;\n\t}\n\n    // Next, we add any Lagrange Multipliers\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n\n\tif (m_eq_scheme == EQUATION_SCHEME::BLOCK)\n\t{\n\t\t// repartition the equations so that we only have two partitions,\n\t\t// one for the fluid-dilataion, and one for the solutes. \n\n\t\t// fluid equations is all the rest\n\t\tint nfeq = m_neq - m_nseq;\n\n\t\t// create the new partitions\n\t\t// Note that this assumes that the solute equations are always last!\n\t\tvector<int> p = { nfeq, m_nseq };\n\t\tSetPartitions(p);\n\t}\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesSolver::GetVelocityData(vector<double> &vi, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(vi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofW[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesSolver::GetDilatationData(vector<double> &ei, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ei);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofEF[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ei[m++] = ui[nid];\n            assert(m <= (int) ei.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesSolver::GetConcentrationData(vector<double> &ci, vector<double> &ui, const int sol)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ci);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofC[sol]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ci[m++] = ui[nid];\n            assert(m <= (int) ci.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEFluidSolutesSolver::UpdateKinematics(vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter(U, mesh, m_dofW[0]);\n    scatter(U, mesh, m_dofW[1]);\n    scatter(U, mesh, m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    \n    // update solute data\n    int nssd = 0, nssr = 0;\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n\n        // update nodal concentration\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            int n = node.m_ID[m_dofC[j]];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if ((ct < 0.0) && m_forcePositive) ct = 0.0;\n                double cp = node.get_prev(m_dofC[j]);\n                if ((m_cmin > 0) && (node.get_bc(m_dofC[j]) == DOF_OPEN) && (cp - ct >= m_cmin))\n                    nssd++;\n                if ((m_cmax > 0) && (node.get_bc(m_dofC[j]) == DOF_OPEN) && (ct - cp >= m_cmax))\n                    nssr++;\n                node.set(m_dofC[j], ct);\n            }\n        }\n    }\n    \n    if (nssd >= m_cnum) m_sudden_C_change = true;\n    if (nssr >= m_cnum) m_sudden_C_change = true;\n\n    // force dilatations to remain greater than -1\n    if (m_minJf > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofEF[0]) <= -1.0)\n                node.set(m_dofEF[0], m_minJf - 1.0);\n        }\n    }\n    \n    // make sure the prescribed velocities are fulfilled\n    int nvel = fem.BoundaryConditions();\n    for (int i=0; i<nvel; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.Update();\n    }\n    \n    // prescribe DOFs for specialized surface loads\n/*    int nsl = fem.ModelLoads();\n    for (int i=0; i<nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.Update();\n    }*/\n    \n    // enforce the linear constraints\n    // TODO: do we really have to do this? Shouldn't the algorithm\n    // already guarantee that the linear constraints are satisfied?\n    FELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    if (LCM.LinearConstraints() > 0)\n    {\n        LCM.Update();\n    }\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEFluidSolutesAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n        double dt = fem.GetTime().timeIncrement;\n        double cgi = 1 - 1.0/m_gammaf;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // velocity time derivative\n            vec3d vft = n.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d vfp = n.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d aft = n.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            vec3d afp = n.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            aft = afp*cgi + (vft - vfp)/(m_gammaf*dt);\n            n.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], aft);\n            \n            // dilatation time derivative\n            double eft = n.get(m_dofEF[0]);\n            double efp = n.get_prev(m_dofEF[0]);\n            double aefp = n.get_prev(m_dofAEF);\n            double aeft = aefp*cgi + (eft - efp)/(m_gammaf*dt);\n            n.set(m_dofAEF, aeft);\n            \n            // concentration time derivative\n            // update nodal concentration\n            for (int j=0; j<MAX_CDOFS; ++j) {\n                int k = n.m_ID[m_dofC[j]];\n                // Force the concentrations to remain positive\n                if (k >= 0) {\n                    double ct = n.get(m_dofC[j]);\n                    double cp = n.get_prev(m_dofC[j]);\n                    double acp = n.get_prev(m_dofAC+j);\n                    double act = acp*cgi + (ct - cp)/(m_gammaf*dt);\n                    n.set(m_dofC[j], ct);\n                    n.set(m_dofAC + j, act);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolutesSolver::Update2(const vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter(U, mesh, m_dofW[0]);\n    scatter(U, mesh, m_dofW[1]);\n    scatter(U, mesh, m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    \n    // update solute data\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // update nodal concentration\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            int n = node.m_ID[m_dofC[j]];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if ((ct < 0.0) && m_forcePositive) ct = 0.0;\n                node.set(m_dofC[j], ct);\n            }\n        }\n    }\n    \n    // Update the prescribed nodes\n    for (int i = 0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.m_rid == -1)\n        {\n            vec3d dv(0, 0, 0);\n            for (int j = 0; j < node.m_ID.size(); ++j)\n            {\n                int nj = -node.m_ID[j] - 2; if (nj >= 0) node.set(j, node.get(j) + ui[nj]);\n            }\n        }\n    }\n    \n    // update model state\n    GetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEFluidSolutesSolver::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update kinematics\n    UpdateKinematics(ui);\n    \n    // update model state\n    UpdateModel();\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidSolutesSolver::InitStep(double time)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // set time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    tp.gamma = m_gammaf;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n    double dt = tp.timeIncrement;\n    double ta = (t > 0) ? t - (1-m_alphaf)*dt : m_alphaf*dt;\n    \n    return FESolver::InitStep(ta);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FEFluidSolutesSolver::PrepStep()\n{\n    FEModel& fem = *GetFEModel();\n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n\n    FETimeInfo& tp = fem.GetTime();\n    double dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n    \n    // zero total DOFs\n    zero(m_Ui);\n    zero(m_Vi);\n    zero(m_Di);\n    for (int j=0; j<(int)m_nceq.size(); ++j) if (m_nceq[j]) zero(m_Ci[j]);\n    \n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt = ni.m_r0;\n        ni.m_dp = ni.m_dt = ni.m_d0;\n        ni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n            {\n                // initial guess at start of new time step (default)\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], afp*(m_gammaf-1)/m_gammaf);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF)*(m_gammaf-1)/m_gammaf);\n                // update nodal concentration\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofAC+j, ni.get_prev(m_dofAC+j)*(m_gammaf-1)/m_gammaf);\n            }\n                break;\n                \n            case 1:\n            {\n                // initial guess at start of new time step (Zero Ydot)\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], vec3d(0,0,0));\n                ni.set(m_dofAEF, 0);\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofAC+j, 0);\n\n                vec3d vfp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], vfp + afp*dt*(1-m_gammaf)*m_alphaf);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt*(1-m_gammaf)*m_alphaf);\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofC[j], ni.get_prev(m_dofC[j]) + ni.get_prev(m_dofAC+j)*dt*(1-m_gammaf)*m_alphaf);\n            }\n                break;\n                \n            case 2:\n            {\n                // initial guess at start of new time step (Same Ydot)\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], afp);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF));\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofAC+j, ni.get_prev(m_dofAC+j));\n\n                vec3d vfp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], vfp + afp*dt);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt);\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofC[j], ni.get_prev(m_dofC[j]) + ni.get_prev(m_dofAC+j)*dt);\n            }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.PrepStep(ui);\n    }\n    \n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.PrepStep();\n    }\n\n    // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n    // update domain data\n    for (int i=0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n    \n    // update model state\n    UpdateModel();\n\n    // see if we need to do contact augmentations\n    m_baugment = false;\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n        if (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n    }\n    \n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidSolutesSolver::Quasin()\n{\n    FEModel& fem = *GetFEModel();\n    // convergence norms\n    double    normR1;        // residual norm\n    double    normE1;        // energy norm\n    double    normV;        // velocity norm\n    double    normv;        // velocity increment norm\n    double    normRi = 0;    // initial residual norm\n    double    normVi = 0;    // initial velocity norm\n    double    normEi = 0; // initial energy norm\n    double    normEm = 0;    // max energy norm\n    double    normDi = 0;    // initial dilatation norm\n    double    normD;        // current dilatation norm\n    double    normd;        // incremement dilatation norm\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    \n    // solute convergence data\n    vector<double>    normCi(MAX_CDOFS);    // initial concentration norm\n    vector<double>    normC(MAX_CDOFS);    // current concentration norm\n    vector<double>    normc(MAX_CDOFS);    // incremement concentration norm\n    \n    // Get the current step\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    \n    // prepare for the first iteration\n    const FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n    // Init QN method\n    if (QNInit() == false) return false;\n\n\t// this flag indicates whether the velocity has converged for a sequential solve\n\t// (This is not used for a coupled solve.)\n\tbool vel_converged = false;\n    \n    // loop until converged or when max nr of reformations reached\n    bool bconv = false; // convergence flag\n    do\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n\n\t\t// for sequential solve, we set one of the residual components to zero\n\t\tif (m_solve_strategy == SOLVE_SEQUENTIAL)\n\t\t{\n\t\t\tint veq = m_neq - m_nseq;\n\t\t\tif (vel_converged == false)\n\t\t\t{\n\t\t\t\t// zero the solute residual\n\t\t\t\tfor (int i = veq; i < m_neq; ++i) m_R0[i] = 0.0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// zero the velocity residual\n\t\t\t\tfor (int i = 0; i < veq; ++i) m_R0[i] = 0.0;\n\t\t\t}\n\t\t}\n        \n        // solve the equations (returns line search; solution stored in m_ui)\n        double s = QNSolve();\n\n\t\t// for sequential solve, we set one of the residual components to zero\n\t\tif (m_solve_strategy == SOLVE_SEQUENTIAL)\n\t\t{\n\t\t\tint veq = m_neq - m_nseq;\n\t\t\tif (vel_converged == false)\n\t\t\t{\n\t\t\t\t// zero the solute residual\n\t\t\t\tfor (int i = veq; i < m_neq; ++i) m_R1[i] = 0.0;\n\n\t\t\t\t// zero the solute solution\n\t\t\t\tfor (int i = veq; i < m_neq; ++i) m_ui[i] = 0.0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// zero the velocity residual\n\t\t\t\tfor (int i = 0; i < veq; ++i) m_R1[i] = 0.0;\n                // if solving sequentially with ctol = 0, ignore solute residual\n                if (m_Ctol == 0) {\n                    // zero the solute residual\n                    for (int i = veq; i < m_neq; ++i) m_R1[i] = 0.0;\n                }\n\t\t\t}\n\t\t}\n\n        // extract the velocity and dilatation increments\n        GetVelocityData(m_vi, m_ui);\n        GetDilatationData(m_di, m_ui);\n        \n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normVi = fabs(m_vi*m_vi);\n            normDi = fabs(m_di*m_di);\n            normEm = normEi;\n        }\n        \n        // calculate norms\n        // update all degrees of freedom\n        for (int i=0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n        \n        // update velocities\n        for (int i = 0; i<m_nveq; ++i) m_Vi[i] += s*m_vi[i];\n        \n        // update dilatations\n        for (int i = 0; i<m_ndeq; ++i) m_Di[i] += s*m_di[i];\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n        normv  = (m_vi*m_vi)*(s*s);\n        normV  = m_Vi*m_Vi;\n        normd  = (m_di*m_di)*(s*s);\n        normD  = m_Di*m_Di;\n        normE1 = s*fabs(m_ui*m_R1);\n        \n        // check for nans\n        if (ISNAN(normR1)) throw NANInResidualDetected();\n        \n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check velocity norm\n        if ((m_Vtol > 0) && (normv  > (m_Vtol*m_Vtol)*normV )) bconv = false;\n        \n        // check dilatation norm\n        if ((m_Ftol > 0) && (normd  > (m_Ftol*m_Ftol)*normD )) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n        if ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        //Changed to be like solid solver\n        /*\n        if (m_bdivreform)\n        {\n            if (normE1 > normEm) bconv = false;\n        }*/\n        if (normE1 > normEm) bconv = false;\n        \n        // check solute convergence\n        // extract the concentration increments\n        for (int j = 0; j<(int)m_nceq.size(); ++j) {\n            if (m_nceq[j]) {\n                GetConcentrationData(m_ci[j], m_ui,j);\n                \n                // set initial norm\n                if (m_niter == 0)\n                    normCi[j] = fabs(m_ci[j]*m_ci[j]);\n                \n                // update total concentration\n                for (int i = 0; i<m_nceq[j]; ++i) m_Ci[j][i] += s*m_ci[j][i];\n                \n                // calculate norms\n                normC[j] = m_Ci[j]*m_Ci[j];\n                normc[j] = (m_ci[j]*m_ci[j])*(s*s);\n                \n            }\n        }\n        \n        // check convergence\n        if (m_Ctol > 0) {\n            for (int j = 0; j<(int)m_nceq.size(); ++j)\n                if (m_nceq[j]) bconv = bconv && (normc[j] <= (m_Ctol*m_Ctol)*normC[j]);\n        }\n        \n        // print convergence summary\n        feLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n        feLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n        if (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        feLog(\"\\t   velocity         %15le %15le %15le \\n\", normVi, normv ,(m_Vtol*m_Vtol)*normV );\n        feLog(\"\\t   dilatation       %15le %15le %15le \\n\", normDi, normd ,(m_Ftol*m_Ftol)*normD );\n        for (int j = 0; j<(int)m_nceq.size(); ++j) {\n            if (m_nceq[j])\n                feLog(\"\\t solute %d concentration %15le %15le %15le\\n\", j+1, normCi[j], normc[j] ,(m_Ctol*m_Ctol)*normC[j] );\n        }\n\n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n        // see if we have exceeded the max residual\n        if ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n        {\n            // doesn't look like we're getting anywhere, so let's retry the time step\n            throw MaxResidualError();\n        }\n        \n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n            if (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n                feLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n                QNForceReform(true);\n            }\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n                feLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                normVi = normv;\n                normDi = normd;\n                for (int j = 0; j<(int)m_nceq.size(); ++j)\n                    if (m_nceq[j]) normCi[j] = normc[j];\n                QNForceReform(true);\n            }\n            \n            // Do the QN update (This may also do a stiffness reformation if necessary)\n            bool bret = QNUpdate();\n            \n            // something went wrong with the update, so we'll need to break\n            if (bret == false) break;\n        }\n        else if (m_baugment)\n        {\n            // Do augmentations\n            bconv = DoAugmentations();\n        }\n\n\t\tif (bconv && (m_solve_strategy == SOLVE_SEQUENTIAL))\n\t\t{\n\t\t\tif (vel_converged == false)\n\t\t\t{\n\t\t\t\tvel_converged = true;\n\t\t\t\tbconv = false;\n\t\t\t\tm_qnstrategy->m_nups = 0;\n\t\t\t\tm_niter = -1;\n\t\t\t\tResidual(m_R0);\n\t\t\t\tfeLog(\"\\n*** Velocity converged. Now solving for solutes.\\n\");\n\t\t\t}\n\t\t}\n        \n        // check for sudden solute concentration change\n        if (bconv && m_sudden_C_change) {\n            m_sudden_C_change = false;\n            throw ConcentrationChangeDetected();\n        }\n        else m_sudden_C_change = false;\n\n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        m_Ut += m_Ui;\n        zero(m_Ui);\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEFluidSolutesSolver::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.StiffnessMatrix(LS);\n    }\n    \n    // calculate the body force stiffness matrix for each domain\n    int NBL = fem.ModelLoads();\n    for (int j = 0; j<NBL; ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n                dom.BodyForceStiffness(LS, *pbf);\n            }\n        }\n    }\n    \n    // calculate contact stiffness\n    ContactStiffness(LS);\n    \n    // calculate stiffness matrix due to model loads\n    int nsl = fem.ModelLoads();\n    for (int i=0; i<nsl; ++i)\n    {\n        FEModelLoad* pml = fem.ModelLoad(i);\n        if (pml->IsActive()) pml->StiffnessMatrix(LS);\n        //        if (pml->IsActive() && HasActiveDofs(pml->GetDofList())) pml->StiffnessMatrix(LS);\n    }\n\n    // Add mass matrix\n    // loop over all domains\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.MassMatrix(LS);\n    }\n    \n    // calculate nonlinear constraint stiffness\n    // note that this is the contribution of the\n    // constraints enforced with augmented lagrangian\n    NonLinearConstraintStiffness(LS, tp);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEFluidSolutesSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FEFluidSolutesSolver::ContactStiffness(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEFluidSolutesSolver::ContactForces(FEGlobalVector& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry\n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEFluidSolutesSolver::Residual(vector<double>& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the time information\n    const FETimeInfo& tp = fem.GetTime();\n    \n    // initialize residual\n    zero(R);\n    \n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n    FEResidualVector RHS(fem, R, m_Fr);\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.InternalForces(RHS);\n    }\n    \n    // calculate the body forces\n    for (int j = 0; j<fem.ModelLoads(); ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n                dom.BodyForce(RHS, *pbf);\n            }\n        }\n    }\n    \n    // calculate inertial forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.InertialForces(RHS);\n    }\n    \n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n \n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i=0; i<NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive())\n        {\n            mli.LoadVector(RHS);\n        }\n    }\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofW[0], 0);\n\t\tnode.set_load(m_dofW[1], 0);\n\t\tnode.set_load(m_dofW[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofW[0]] - 2) >= 0) node.set_load(m_dofW[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofW[1]] - 2) >= 0) node.set_load(m_dofW[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofW[2]] - 2) >= 0) node.set_load(m_dofW[2], -m_Fr[n]);\n    }\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces\nvoid FEFluidSolutesSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FEFluidSolutesSolver::Serialize(DumpStream& ar)\n{\n    FENewtonSolver::Serialize(ar);\n\n    ar & m_neq & m_nveq & m_ndeq & m_nseq & m_nceq;\n    ar & m_nrhs & m_niter & m_nref & m_ntotref;\n\n    ar & m_Fr & m_Ui & m_Ut;\n    ar & m_Vi & m_Di & m_Ci;\n\n    if (ar.IsLoading())\n    {\n        m_Fr.assign(m_neq, 0);\n        m_Vi.assign(m_nveq,0);\n        m_Di.assign(m_ndeq,0);\n        for (int i=0; i<m_nceq.size(); ++i) {\n            m_ci[i].assign(m_nceq[i], 0);\n            m_Ci[i].assign(m_nceq[i], 0);\n        }\n    }\n    \n    if (ar.IsShallow()) return;\n\n    ar & m_alphaf & m_alpham;\n    ar & m_gammaf;\n    ar & m_pred;\n    ar & m_dofW & m_dofAW & m_dofEF & m_dofAEF & m_dofC & m_dofAC;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolutesSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/FEDofList.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! The FEFluidSolutesSolver class solves fluid mechanics problems\n//! with mass transport (solutes)\n//! It can deal with quasi-static and dynamic problems\n//!\nclass FEBIOFLUID_API FEFluidSolutesSolver : public FENewtonSolver\n{\n\tenum SOLVE_STRATEGY {\n\t\tSOLVE_COUPLED,\t\t\t// monolithic solution approach\n\t\tSOLVE_SEQUENTIAL\t\t// first solve velocity, then solutes\n\t};\n\npublic:\n    //! constructor\n    FEFluidSolutesSolver(FEModel* pfem);\n    \n    //! destructor\n    ~FEFluidSolutesSolver();\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n\n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n    \npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n    \n    //! update nodal positions, velocities, accelerations, etc.\n    void UpdateKinematics(vector<double>& ui);\n    \n    //! used by JFNK\n    void Update2(const vector<double>& ui) override;\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! contact stiffness\n    void ContactStiffness(FELinearSystem& LS);\n    \n    //! calculates stiffness contributon of nonlinear constraints\n    void NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n    \n    //{ --- Residual routines ---\n    \n    //! Calculate the contact forces\n    void ContactForces(FEGlobalVector& R);\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n    \n    //! Calculate nonlinear constraint forces\n    void NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    void GetVelocityData(vector<double>& vi, vector<double>& ui);\n    void GetDilatationData(vector<double>& ei, vector<double>& ui);\n    void GetConcentrationData(vector<double>& ci, vector<double>& ui, const int sol);\n\npublic:\n    // convergence tolerances\n    double  m_Vtol;     //!< velocity tolerance\n    double  m_Ftol;     //!< dilatation tolerance\n    double  m_Ctol;     //!< concentration tolerance\n    double  m_minJf;    //!< minimum allowable compression ratio\n    bool    m_forcePositive;    //!< force conentrations to remain positive\n    double  m_cmin;     //!< threshold for detecting sudden drop in concentration\n    double  m_cmax;     //!< threshold for detecting sudden increase in concentration\n    int     m_cnum;     //!< minimum number of points at which C drops suddenly\n\npublic:\n    // equation numbers\n    int         m_nveq;     //!< number of equations related to velocity dofs\n    int         m_ndeq;     //!< number of equations related to dilatation dofs\n\tint\t\t\tm_nseq;\t\t//!< total number of concentration dofs\n    vector<int> m_nceq;     //!< number of equations related to concentration dofs\n\npublic:\n    vector<double> m_Fr;    //!< nodal reaction forces\n    vector<double> m_vi;    //!< velocity increment vector\n    vector<double> m_Vi;    //!< Total velocity vector for iteration\n    vector<double> m_di;    //!< dilatation increment vector\n    vector<double> m_Di;    //!< Total dilatation vector for iteration\n    \n    // solute data\n    vector< vector<double> >    m_ci;    //!< concentration increment vector\n    vector< vector<double> >    m_Ci;    //!< Total concentration vector for iteration\n\n    // generalized alpha method\n    double  m_rhoi;         //!< rho infinity\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_gammaf;       //!< gamma\n    int     m_pred;         //!< predictor method\n    \nprotected:\n    FEDofList       m_dofW;\n    FEDofList       m_dofAW;\n    FEDofList       m_dofEF;\n    int             m_dofAEF;\n    FEDofList       m_dofC;\n    int             m_dofAC;\n\n\tint\t\t\t\tm_solve_strategy;\n    \nprivate:\n    bool            m_sudden_C_change;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSolver.h\"\n#include \"FEFluidDomain.h\"\n#include \"FEFluidResidualVector.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <assert.h>\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/sys.h\"\n#include <FEBioMech/FEBodyForce.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluidResistanceBC.h\"\n#include \"FEBackFlowStabilization.h\"\n#include \"FEFluidNormalVelocity.h\"\n#include \"FEFluidVelocity.h\"\n#include \"FEFluidRotationalVelocity.h\"\n#include \"FETiedFluidInterface.h\"\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBioFluid.h\"\n#include \"FEFluidAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEFluidSolver, FENewtonSolver)\n\tADD_PARAMETER(m_Vtol , \"vtol\"        );\n    ADD_PARAMETER(m_Ftol , \"ftol\"        );\n    ADD_PARAMETER(m_rhoi , \"rhoi\"        );\n    ADD_PARAMETER(m_Etol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n    ADD_PARAMETER(m_Rtol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n    ADD_PARAMETER(m_pred , \"predictor\"   );\n    ADD_PARAMETER(m_minJf, \"min_volume_ratio\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEFluidSolver Construction\n//\nFEFluidSolver::FEFluidSolver(FEModel* pfem) : FENewtonSolver(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofEF(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Vtol = 0.001;\n    m_Ftol = 0.001;\n    m_Rmin = 1.0e-20;\n\tm_Rmax = 0;     // not used if zero\n    m_minJf = 0;    // not used if zero\n    \n    m_nveq = 0;\n    m_ndeq = 0;\n    m_niter = 0;\n    \n\t// assume non-symmetric stiffness\n    m_msymm = REAL_UNSYMMETRIC;\n\n    m_rhoi = 0;\n    m_pred = 0;\n    \n\t// Preferred strategy is Broyden's method\n\tSetDefaultStrategy(QN_BROYDEN);\n\n\t// turn off checking for a zero diagonal\n\tCheckZeroDiagonal(false);\n    \n\t// get the dof indices\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_ACCELERATION));\n        m_dofEF.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION));\n        m_dofAEF = pfem->GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION_TDERIV), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEFluidSolver::~FEFluidSolver()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEFluidSolver\n//\nbool FEFluidSolver::Init()\n{\n\t// initialize base class\n\tif (FENewtonSolver::Init() == false) return false;\n\n    // check parameters\n    if (m_Vtol <  0.0) { feLogError(\"vtol must be nonnegative.\"); return false; }\n    if (m_Ftol <  0.0) { feLogError(\"dtol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_gammaf = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        m_alpham = (3-m_rhoi)/(1+m_rhoi)/2;\n        m_gammaf = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    m_vi.assign(m_nveq,0);\n    m_Vi.assign(m_nveq,0);\n    m_di.assign(m_ndeq,0);\n    m_Di.assign(m_ndeq,0);\n    \n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, m_dofW[0]);\n    gather(m_Ut, mesh, m_dofW[1]);\n    gather(m_Ut, mesh, m_dofW[2]);\n    gather(m_Ut, mesh, m_dofEF[0]);\n    \n\t// set flag for transient or steady-state analyses\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n\t\tif (fem.GetCurrentStep()->m_nanalysis == FEFluidAnalysis::STEADY_STATE)\n\t\t\tdom.SetSteadyStateAnalysis();\n\t\telse\n\t\t\tdom.SetTransientAnalysis();\n\t}\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEFluidSolver::InitEquations()\n{\n\t// Add the solution variables\n\tAddSolutionVariable(&m_dofW, 1, \"velocity\", m_Vtol);\n\tAddSolutionVariable(&m_dofEF, 1, \"dilatation\", m_Ftol);\n\n    // base class initialization\n    if (FENewtonSolver::InitEquations() == false) return false;\n\n    // determined the nr of velocity and dilatation equations\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    m_nveq = m_ndeq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofW[0]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2]] != -1) m_nveq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_ndeq++;\n    }\n\n    // Next, we add any Lagrange Multipliers\n    FEModel& fem = *GetFEModel();\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEFluidSolver::InitEquations2()\n{\n\t// Add the solution variables\n\tAddSolutionVariable(&m_dofW, -1, \"velocity\", m_Vtol);\n\tAddSolutionVariable(&m_dofEF, -1, \"dilatation\", m_Ftol);\n\n\t// base class initialization\n\tFENewtonSolver::InitEquations2();\n\n\t// determined the nr of velocity and dilatation equations\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tm_nveq = m_ndeq = 0;\n\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& n = mesh.Node(i);\n\t\tif (n.m_ID[m_dofW[0]] != -1) m_nveq++;\n\t\tif (n.m_ID[m_dofW[1]] != -1) m_nveq++;\n\t\tif (n.m_ID[m_dofW[2]] != -1) m_nveq++;\n\t\tif (n.m_ID[m_dofEF[0]] != -1) m_ndeq++;\n\t}\n\n    // Next, we add any Lagrange Multipliers\n    FEModel& fem = *GetFEModel();\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolver::GetVelocityData(vector<double> &vi, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(vi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofW[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolver::GetDilatationData(vector<double> &ei, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ei);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofEF[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ei[m++] = ui[nid];\n            assert(m <= (int) ei.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEFluidSolver::UpdateKinematics(vector<double>& ui)\n{\n    // get the mesh\n\tFEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter(U, mesh, m_dofW[0]);\n    scatter(U, mesh, m_dofW[1]);\n    scatter(U, mesh, m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // force dilatations to remain greater than -1\n    if (m_minJf > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofEF[0]) <= -1.0)\n                node.set(m_dofEF[0], m_minJf - 1.0);\n        }\n    }\n\n    // make sure the prescribed velocities are fulfilled\n    int nvel = fem.BoundaryConditions();\n    for (int i=0; i<nvel; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.Update();\n    }\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEFluidAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n\t\tdouble dt = fem.GetTime().timeIncrement;\n        double cgi = 1 - 1.0/m_gammaf;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // velocity time derivative\n            vec3d vft = n.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d vfp = n.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d aft = n.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            vec3d afp = n.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            aft = afp*cgi + (vft - vfp)/(m_gammaf*dt);\n            n.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], aft);\n            \n            // dilatation time derivative\n            double eft = n.get(m_dofEF[0]);\n            double efp = n.get_prev(m_dofEF[0]);\n            double aefp = n.get_prev(m_dofAEF);\n            double aeft = aefp*cgi + (eft - efp)/(m_gammaf*dt);\n            n.set(m_dofAEF, aeft);\n        }\n    }\n\n    // update nonlinear constraints (needed for updating Lagrange Multiplier)\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* nlc = fem.NonlinearConstraint(i);\n        if (nlc->IsActive()) nlc->Update(m_Ui, ui);\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive()) spc->Update(m_Ui, ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update DOF increments\nvoid FEFluidSolver::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // extract the velocity and dilatation increments\n    GetVelocityData(m_vi, ui);\n    GetDilatationData(m_di, ui);\n        \n    // update all degrees of freedom\n    for (int i=0; i<m_neq; ++i) Ui[i] += ui[i];\n        \n    // update velocities\n    for (int i = 0; i<m_nveq; ++i) m_Vi[i] += m_vi[i];\n\n    // update dilatations\n    for (int i = 0; i<m_ndeq; ++i) m_Di[i] += m_di[i];\n\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->UpdateIncrements(Ui, ui);\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->UpdateIncrements(Ui, ui);\n\t}\n\n    // TODO: This is a hack!\n    // The problem is that I only want to call the domain's IncrementalUpdate during\n    // the quasi-Newtoon loop. However, this function is also called after the loop\n    // converges. The emap parameter is used here to detect wether we are inside the\n    // loop (emap == false), or not (emap == true).\n    if (emap == false)\n    {\n        for (int i = 0; i < mesh.Domains(); ++i)\n        {\n            FEDomain& dom = mesh.Domain(i);\n            dom.IncrementalUpdate(ui, true);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidSolver::Update2(const vector<double>& ui)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update nodes\n\tvector<double> U(m_Ut.size());\n\tfor (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n\tscatter(U, mesh, m_dofW[0]);\n\tscatter(U, mesh, m_dofW[1]);\n\tscatter(U, mesh, m_dofW[2]);\n\tscatter(U, mesh, m_dofEF[0]);\n\n\t// Update the prescribed nodes\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid == -1)\n\t\t{\n\t\t\tvec3d dv(0, 0, 0);\n\t\t\tfor (int j = 0; j < node.m_ID.size(); ++j)\n\t\t\t{\n\t\t\t\tint nj = -node.m_ID[j] - 2; if (nj >= 0) node.set(j, node.get(j) + ui[nj]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// update model state\n\tGetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEFluidSolver::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update kinematics\n    UpdateKinematics(ui);\n    \n    // update model state\n\tGetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Update nonlinear constraints\nvoid FEFluidSolver::UpdateConstraints()\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // Update all nonlinear constraints\n    for (int i = 0; i<fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* pci = fem.NonlinearConstraint(i);\n        if (pci->IsActive()) pci->Update();\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidSolver::InitStep(double time)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // set time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    tp.gamma = m_gammaf;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n    double dt = tp.timeIncrement;\n    double ta = (t > 0) ? t - (1-m_alphaf)*dt : m_alphaf*dt;\n    \n    return FESolver::InitStep(ta);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FEFluidSolver::PrepStep()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFETimeInfo& tp = fem.GetTime();\n\tdouble dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n\n    // zero total DOFs\n    zero(m_Ui);\n    zero(m_Vi);\n    zero(m_Di);\n    \n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt = ni.m_r0;\n        ni.m_dp = ni.m_dt = ni.m_d0;\n\t\tni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n            {\n                // initial guess at start of new time step (default)\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], afp*(m_gammaf-1)/m_gammaf);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF)*(m_gammaf-1)/m_gammaf);\n            }\n                break;\n                \n            case 1:\n            {\n                // initial guess at start of new time step (Zero Ydot)\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], vec3d(0,0,0));\n                ni.set(m_dofAEF, 0);\n                \n                vec3d vfp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], vfp + afp*dt*(1-m_gammaf)*m_alphaf);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt*(1-m_gammaf)*m_alphaf);\n            }\n                break;\n                \n            case 2:\n            {\n                // initial guess at start of new time step (Same Ydot)\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], afp);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF));\n                \n                vec3d vfp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], vfp + afp*dt);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt);\n            }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.PrepStep(ui);\n    }\n  \n    // do the linear constraints\n    fem.GetLinearConstraintManager().PrepStep();\n\n    // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n\t// update domain data\n    for (int i=0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n    \n    // update model state\n    UpdateModel();\n\n//    // update stresses\n//\tfem.Update();\n    \n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->PrepStep();\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n\n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.PrepStep();\n    }\n\n    // see if we need to do contact augmentations\n    m_baugment = false;\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n        if (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n    }\n    \n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidSolver::Quasin()\n{\n\tFEModel& fem = *GetFEModel();\n\n    // convergence norms\n    double\tnormR1;\t\t// residual norm\n    double\tnormE1;\t\t// energy norm\n    double\tnormV;\t\t// velocity norm\n    double\tnormv;\t\t// velocity increment norm\n    double\tnormRi = 0;\t// initial residual norm\n    double\tnormVi = 0;\t// initial velocity norm\n    double\tnormEi = 0; // initial energy norm\n    double\tnormEm = 0;\t// max energy norm\n    double\tnormDi = 0;\t// initial dilatation norm\n    double\tnormD;\t\t// current dilatation norm\n    double\tnormd;\t\t// incremement dilatation norm\n    \n    // prepare for the first iteration\n\tconst FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n\t// Init QN method\n\tif (QNInit() == false) return false;\n    \n    // loop until converged or when max nr of reformations reached\n\tbool bconv = false; // convergence flag\n\tdo\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n        \n        // solve the equations\n        SolveEquations(m_ui, m_R0);\n\n        // do the line search\n        double s = DoLineSearch();\n\n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normVi = fabs(m_vi*m_vi);\n            normDi = fabs(m_di*m_di);\n            normEm = normEi;\n        }\n        \n        // calculate actual displacement increment\n        // NOTE: We don't apply the line search directly to m_ui since we need the unscaled search direction for the QN update below\n        int neq = (int)m_Ui.size();\n        vector<double> ui(m_ui);\n        for (int i = 0; i<neq; ++i) ui[i] *= s;\n\n        // update increments (including Lagrange multipliers)\n        UpdateIncrements(m_Ui, ui, false);\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n\t\tnormv  = m_vi*m_vi;\n        normV  = m_Vi*m_Vi;\n        normd  = m_di*m_di;\n        normD  = m_Di*m_Di;\n        normE1 = fabs(ui*m_R1);\n        \n\t\t// check for nans\n\t\tif (ISNAN(normR1)) throw NANInResidualDetected();\n        if (ISNAN(normv)) throw NANInSolutionDetected();\n        if (ISNAN(normd)) throw NANInSolutionDetected();\n\n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check velocity norm\n        if ((m_Vtol > 0) && (normv  > (m_Vtol*m_Vtol)*normV )) bconv = false;\n        \n        // check dilatation norm\n        if ((m_Ftol > 0) && (normd  > (m_Ftol*m_Ftol)*normD )) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        if (normE1 > normEm) bconv = false;\n        \n        // print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        feLog(\"\\t   velocity         %15le %15le %15le \\n\", normVi, normv ,(m_Vtol*m_Vtol)*normV );\n        feLog(\"\\t   dilatation       %15le %15le %15le \\n\", normDi, normd ,(m_Ftol*m_Ftol)*normD );\n        \n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n\t\t// see if we have exceeded the max residual\n\t\tif ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n\t\t{\n\t\t\t// doesn't look like we're getting anywhere, so let's retry the time step\n\t\t\tthrow MaxResidualError();\n\t\t}\n\n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n\t\t\tif (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                normVi = normv;\n                normDi = normd;\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n\t\t}\n        else if (m_baugment)\n        {\n\t\t\t// Do augmentations\n\t\t\tbconv = DoAugmentations();\n        }\n        \n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        UpdateIncrements(m_Ut, m_Ui, true);\n\t\tzero(m_Ui);\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEFluidSolver::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tconst FETimeInfo& tp = fem.GetTime();\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.StiffnessMatrix(LS);\n    }\n    \n    // calculate the body force stiffness matrix for each domain\n\tint NBL = fem.ModelLoads();\n\tfor (int j = 0; j<NBL; ++j)\n\t{\n\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n\t\tif (pbf && pbf->IsActive())\n\t\t{\n\t\t\tfor (int i = 0; i<pbf->Domains(); ++i)\n\t\t\t{\n\t\t\t\tFEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n\t\t\t\tdom.BodyForceStiffness(LS, *pbf);\n\t\t\t}\n        }\n    }\n    \n    // calculate contact stiffness\n    ContactStiffness(LS);\n    \n    // calculate stiffness matrix due to model loads\n    int nsl = fem.ModelLoads();\n    for (int i=0; i<nsl; ++i)\n    {\n        FEModelLoad* pml = fem.ModelLoad(i);\n        if (pml->IsActive()) pml->StiffnessMatrix(LS);\n//        if (pml->IsActive() && HasActiveDofs(pml->GetDofList())) pml->StiffnessMatrix(LS);\n    }\n    \n    // Add mass matrix\n    // loop over all domains\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.MassMatrix(LS);\n    }\n    \n    // calculate nonlinear constraint stiffness\n    // note that this is the contribution of the\n    // constraints enforced with augmented lagrangian\n    NonLinearConstraintStiffness(LS, tp);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEFluidSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FEFluidSolver::ContactStiffness(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEFluidSolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEFluidSolver::Residual(vector<double>& R)\n{\n\tFEModel& fem = *GetFEModel();\n\n    // get the time information\n\tconst FETimeInfo& tp = fem.GetTime();\n\n    // initialize residual with concentrated nodal loads\n    zero(R);\n    \n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n    FEFluidResidualVector RHS(fem, R, m_Fr);\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.InternalForces(RHS);\n    }\n    \n    // calculate the body forces\n\tfor (int j = 0; j<fem.ModelLoads(); ++j)\n\t{\n\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n\t\tif (pbf && pbf->IsActive())\n\t\t{\n\t\t\tfor (int i = 0; i<pbf->Domains(); ++i)\n\t\t\t{\n\t\t\t\tFEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n\t\t\t\tdom.BodyForce(RHS, *pbf);\n\t\t\t}\n        }\n    }\n    \n    // calculate inertial forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.InertialForces(RHS);\n    }\n\n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n    \n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i=0; i<NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive())\n        {\n            mli.LoadVector(RHS);\n        }\n    }\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofW[0], 0);\n\t\tnode.set_load(m_dofW[1], 0);\n\t\tnode.set_load(m_dofW[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofW[0]] - 2) >= 0) node.set_load(m_dofW[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofW[1]] - 2) >= 0) node.set_load(m_dofW[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofW[2]] - 2) >= 0) node.set_load(m_dofW[2], -m_Fr[n]);\n    }\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces\nvoid FEFluidSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FEFluidSolver::Serialize(DumpStream& ar)\n{\n\tFENewtonSolver::Serialize(ar);\n    \n    ar & m_Ut & m_Ui;\n    ar & m_Vi & m_Di;\n    ar & m_nrhs & m_niter & m_nref & m_ntotref;\n    ar & m_neq & m_nveq & m_ndeq;\n    \n    if (ar.IsLoading())\n    {\n        m_Fr.assign(m_neq, 0);\n        m_Vi.assign(m_nveq,0);\n        m_Di.assign(m_ndeq,0);\n    }\n    \n\tif (ar.IsShallow()) return;\n    \n    ar & m_rhoi & m_alphaf & m_alpham;\n    ar & m_gammaf;\n    ar & m_pred;\n    \n    ar & m_dofW & m_dofAW & m_dofEF & m_dofAEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/FEDofList.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! The FEFluidSolver class solves fluid mechanics problems\n//! It can deal with quasi-static and dynamic problems\n//!\nclass FEBIOFLUID_API FEFluidSolver : public FENewtonSolver\n{\npublic:\n    //! constructor\n    FEFluidSolver(FEModel* pfem);\n    \n    //! destructor\n    ~FEFluidSolver();\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n\tbool InitEquations2() override;\n\n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n    \npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n\n\t//! update nodal positions, velocities, accelerations, etc.\n\tvoid UpdateKinematics(vector<double>& ui);\n\n\t//! used by JFNK\n\tvoid Update2(const vector<double>& ui) override;\n    \n    void UpdateConstraints();\n    \n    //! update DOF increments\n    void UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap);\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! contact stiffness\n    void ContactStiffness(FELinearSystem& LS);\n    \n    //! calculates stiffness contributon of nonlinear constraints\n    void NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n    \n    //{ --- Residual routines ---\n    \n    //! Calculate the contact forces\n    void ContactForces(FEGlobalVector& R);\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n    \n    //! Calculate nonlinear constraint forces\n    void NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \nprotected:\n    void GetVelocityData(vector<double>& vi, vector<double>& ui);\n    void GetDilatationData(vector<double>& ei, vector<double>& ui);\n    \npublic:\n    // convergence tolerances\n    double\tm_Vtol;\t\t\t//!< velocity tolerance\n    double\tm_Ftol;\t\t\t//!< dilatation tolerance\n    double  m_minJf;        //!< minimum allowable compression ratio\n\npublic:\n    // equation numbers\n    int\t\tm_nveq;\t\t\t\t//!< number of equations related to velocity dofs\n    int\t\tm_ndeq;\t\t\t\t//!< number of equations related to dilatation dofs\n    \npublic:\n    vector<double> m_Fr;\t//!< nodal reaction forces\n    vector<double> m_vi;\t//!< velocity increment vector\n    vector<double> m_Vi;\t//!< Total velocity vector for iteration\n    vector<double> m_di;\t//!< dilatation increment vector\n    vector<double> m_Di;\t//!< Total dilatation vector for iteration\n    \n    // generalized alpha method\n    double  m_rhoi;         //!< rho infinity\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_gammaf;       //!< gamma\n    int     m_pred;         //!< predictor method\n\nprotected:\n    FEDofList\tm_dofW;\n\tFEDofList\tm_dofAW;\n\tFEDofList\tm_dofEF;\n    int\t\t\tm_dofAEF;    \n   \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidStressCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFluidStressCriterion.h\"\n#include \"FEFluid.h\"\n#include <FECore/FEElementList.h>\n\nBEGIN_FECORE_CLASS(FEFluidStressCriterion, FEMeshAdaptorCriterion)\nEND_FECORE_CLASS();\n\nFEFluidStressCriterion::FEFluidStressCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n}\n\nbool FEFluidStressCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tFEFluidMaterialPoint* fp = mp.ExtractData<FEFluidMaterialPoint>();\n\tif (fp == nullptr) return false;\n\tmat3ds& s = fp->m_sf;\n\tvalue = s.max_shear();\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidStressCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n\nclass FEFluidStressCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEFluidStressCriterion(FEModel* fem);\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidSupply.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2023 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidSupply.h\"\n\n//-----------------------------------------------------------------------------\n// Derivative of supply w.r.t. solute concentration at material point\n// Set this to zero by default because biphasic problems do not require it\ndouble FEFluidSupply::Tangent_Supply_Concentration(FEMaterialPoint& pt, const int isol)\n{\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidSupply.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2023 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for solvent supply.\n//! These materials need to define the supply and tangent supply functions.\n//!\nclass FEBIOFLUID_API FEFluidSupply : public FEMaterialProperty\n{\npublic:\n    FEFluidSupply(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\tvirtual ~FEFluidSupply(){}\n\t\n\t//! fluid supply\n\tvirtual double Supply(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of fluid supply with respect to strain\n\tvirtual mat3d Tangent_Supply_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of fluid supply with respect to pressure\n\tvirtual double Tangent_Supply_Dilatation(FEMaterialPoint& mp) = 0;\n\t\n    //! tangent of fluid supply with respect to rate of deformation\n    virtual mat3ds Tangent_Supply_RateOfDeformation(FEMaterialPoint& mp) = 0;\n    \n\t//! tangent of fluid supply with respect to concentration\n\tvirtual double Tangent_Supply_Concentration(FEMaterialPoint& mp, const int isol);\n\n    //! initialization\n    bool Init() override { return FEMaterialProperty::Init(); }\n    \n\tFECORE_BASE_CLASS(FEFluidSupply)\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEFluidSupplyStarling.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <sstream>\n#include <iostream>\n#include <cstdlib>\n#include \"FEFluidSupplyStarling.h\"\n#include \"FEFluidFSI.h\"\n//#include \"FESolutesMaterialPoint.h\"\n#include <FECore/FEModel.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFluidSupplyStarling, FEFluidSupply)\n\tADD_PARAMETER(m_kp, \"kp\")->setLongName(\"filtration coefficient\");\n\tADD_PARAMETER(m_pv, \"pv\")->setLongName(\"external pressure\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEFluidSupplyStarling::FEFluidSupplyStarling(FEModel* pfem) : FEFluidSupply(pfem)\n{\n\tm_kp = 0;\n\tm_pv = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solvent supply\ndouble FEFluidSupplyStarling::Supply(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n\n\t// evaluate solvent supply from pressure drop\n    double kp = m_kp(mp);\n    double pv = m_pv(mp);\n\tdouble phiwhat = kp*(pv - pt.m_pf);\n/*\n\t// evaluate solvent supply from concentration drop\n\tif (mpt) {\n\t\tint nsol = mpt->m_nsol;\n\t\tfor (int isol=0; isol<nsol; ++isol) {\n\t\t\tphiwhat += m_qc[isol]*(m_cv[isol] - mpt->m_c[isol]);\n\t\t}\n\t}\n*/\n\treturn phiwhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solvent supply with respect to strain\nmat3d FEFluidSupplyStarling::Tangent_Supply_Strain(FEMaterialPoint &mp)\n{\n\treturn mat3d(mat3dd(0));\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solvent supply with respect to pressure\ndouble FEFluidSupplyStarling::Tangent_Supply_Dilatation(FEMaterialPoint &mp)\n{\n    double dpdJ = 0;\n    FEFluidFSI* m_pMat = dynamic_cast<FEFluidFSI*>(GetParent());\n    if (m_pMat) dpdJ = m_pMat->Fluid()->GetElastic()->Tangent_Strain(mp);\n    double kp = m_kp(mp);\n\treturn -kp*dpdJ;\n}\n/*\n//-----------------------------------------------------------------------------\n//! Tangent of solvent supply with respect to concentration\ndouble FEFluidSupplyStarling::Tangent_Supply_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\tFESolutesMaterialPoint& mpt = *mp.ExtractData<FESolutesMaterialPoint>();\n\tif (isol < mpt.m_nsol) {\n\t\treturn -m_qc[isol];\n\t}\n\t\n\treturn 0;\n}\n*/\n"
  },
  {
    "path": "FEBioFluid/FEFluidSupplyStarling.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEFluidSupply.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a fluid supply following\n// Starling's equation\nclass FEBIOFLUID_API FEFluidSupplyStarling :\tpublic FEFluidSupply\n{\npublic:\n\t//! constructor\n    FEFluidSupplyStarling(FEModel* pfem);\n\t\n\t//! Solute supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of supply with respect to solid strain\n\tmat3d Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to pressure\n\tdouble Tangent_Supply_Dilatation(FEMaterialPoint& mp) override;\n\t\n    //! tangent of fluid supply with respect to rate of deformation\n    mat3ds Tangent_Supply_RateOfDeformation(FEMaterialPoint& mp) override { return mat3ds(0); }\n\n    //! Initialization\n    bool Init() override { return FEFluidSupply::Init(); }\n\t\n   \npublic:\n\tFEParamDouble\t\tm_kp;\t\t\t\t//!< coefficient of pressure drop\n    FEParamDouble\t\tm_pv;\t\t\t\t//!< prescribed (e.g., vascular) pressure\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidThermalConductivity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEFluidThermalConductivity.h\"\n"
  },
  {
    "path": "FEBioFluid/FEFluidThermalConductivity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid thermal conductivity materials.\n\nclass FEBIOFLUID_API FEFluidThermalConductivity : public FEMaterialProperty\n{\npublic:\n    FEFluidThermalConductivity(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    virtual ~FEFluidThermalConductivity() {}\n    \npublic:\n    //! calculate thermal conductivity at material point\n    virtual double ThermalConductivity(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of thermal conductivity with respect to strain J\n    virtual double Tangent_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of thermal conductivity with respect to temperature T\n    virtual double Tangent_Temperature(FEMaterialPoint& mp) = 0;\n\n    FECORE_BASE_CLASS(FEFluidThermalConductivity)\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidTractionLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidTractionLoad.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEMesh.h>\n#include \"FEBioFluid.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidTractionLoad, FESurfaceLoad)\n\tADD_PARAMETER(m_scale, \"scale\"   );\n\tADD_PARAMETER(m_TC   , \"traction\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidTractionLoad::FEFluidTractionLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n\tm_scale = 1.0;\n\tm_TC = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEFluidTractionLoad::SetSurface(FESurface* ps)\n{\n\tFESurfaceLoad::SetSurface(ps);\n\tm_TC.SetItemList(ps->GetFacetSet()); \n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidTractionLoad::Init()\n{\n\tm_dof.Clear();\n\tif (m_dof.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY)) == false) return false;\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the traction load\nvoid FEFluidTractionLoad::LoadVector(FEGlobalVector& R)\n{\n\tm_psurf->LoadVector(R, m_dof, true, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\t// fluid traction\n\t\tvec3d t = m_TC(mp)*m_scale;\n\t\tvec3d f = t*((mp.dxr ^ mp.dxs).norm());\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * f.x;\n\t\tfa[1] = H * f.y;\n\t\tfa[2] = H * f.z;\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! calculate traction stiffness (there is none)\nvoid FEFluidTractionLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidTractionLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidTractionLoad is a fluid surface that has a prescribed\n//! viscous traction vector on it.\n//!\nclass FEBIOFLUID_API FEFluidTractionLoad : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n\tFEFluidTractionLoad(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! Set the surface to apply the load to\n\tvoid SetSurface(FESurface* ps) override;\n\n\t//! calculate traction stiffness (there is none)\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculate load vector\n\tvoid LoadVector(FEGlobalVector& R) override;\n\nprivate:\n\tdouble\t\t\tm_scale;\t//!< magnitude of traction load\n\tFEParamVec3 \tm_TC;\t\t//!< traction boundary cards\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEFluidVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidVelocity.h\"\n#include <FECore/log.h>\n#include \"FEBioFluid.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFluidVelocity, FESurfaceLoad)\n\tADD_PARAMETER(m_scale   , \"scale\");\n\tADD_PARAMETER(m_velocity, \"velocity\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFluidVelocity::FEFluidVelocity(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem), m_dofEF(pfem)\n{\n    m_scale = 1.0;\n\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofEF.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the prescribed normal component of velocity\nvoid FEFluidVelocity::LoadVector(FEGlobalVector& R)\n{\n\tm_psurf->LoadVector(R, m_dofEF, true, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\t// calculate the tangent vectors\n\t\tvec3d v = m_velocity(mp);\n\n\t\tvec3d nu = mp.dxr ^ mp.dxs;\n\t\tdouble da = nu.unit();\n\t\tdouble vn = (v*nu)*m_scale;\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * vn * da;\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEFluidVelocity::Init()\n{\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofW);\n    m_dof.AddDofs(m_dofEF);\n    \n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEFluidVelocity::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofW[0], DOF_PRESCRIBED);\n        node.set_bc(m_dofW[1], DOF_PRESCRIBED);\n        node.set_bc(m_dofW[2], DOF_PRESCRIBED);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the nodal velocities\nvoid FEFluidVelocity::Update()\n{\n    // evaluate nodal velocities from boundary cards\n    FESurface* ps = &GetSurface();\n    vector<vec3d> VN(ps->Nodes(), vec3d(0, 0, 0));\n    vector<int> nf(ps->Nodes(), 0);\n\n    for (int iel = 0; iel < ps->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n\n        // nr of element nodes\n        int neln = el.Nodes();\n\n        for (int i = 0; i < neln; ++i)\n        {\n            FESurfaceMaterialPoint mp;\n            mp.m_elem = &el;\n            mp.m_index = i; // this is a node index, but we really need a gauss-point index\n\n            VN[el.m_lnode[i]] += m_velocity(mp);\n            ++nf[el.m_lnode[i]];\n        }\n    }\n    for (int i = 0; i < ps->Nodes(); ++i) VN[i] /= nf[i];\n\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        // evaluate the nodal velocity\n        vec3d v = VN[i]*m_scale;\n        FENode& node = ps->Node(i);\n        if (node.m_ID[m_dofW[0]] < -1) node.set(m_dofW[0], v.x);\n        if (node.m_ID[m_dofW[1]] < -1) node.set(m_dofW[1], v.y);\n        if (node.m_ID[m_dofW[2]] < -1) node.set(m_dofW[2], v.z);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFluidVelocity::Serialize(DumpStream& ar)\n{\n\tFESurfaceLoad::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_bpv & m_dofW & m_dofEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEFluidVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FESurfaceMap.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidVelocity is a fluid surface that has a velocity\n//! prescribed on it.  This routine simultaneously prescribes a\n//! surface load and nodal prescribed velocities\n//! TODO: This is not a surface load!\nclass FEBIOFLUID_API FEFluidVelocity : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEFluidVelocity(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! set the velocity\n    void Update() override;\n    \n    //! initialization\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n    double\t\t\tm_scale;\t//!< average velocity\n    FEParamVec3 \tm_velocity;\t//!< velocity boundary cards\n    \nprivate:\n    bool            m_bpv;      //!< flag for prescribing nodal values\n\n\tFEDofList\t\tm_dofW;\n    FEDofList\t\tm_dofEF;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEIdealGas.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"FEIdealGas.h\"\n#include <FECore/log.h>\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEThermoFluid.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEIdealGas, FEElasticFluid)\n\n    // material parameters\n    ADD_PARAMETER(m_M   , FE_RANGE_GREATER(0.0), \"M\")->setUnits(UNIT_MOLAR_MASS)->setLongName(\"molar mass\");\n    ADD_PARAMETER(m_ar  , \"ar\")->setLongName(\"normalized referential specific free energy\");    // ar normalized by R.Tr/M\n    ADD_PARAMETER(m_sr  , \"sr\")->setLongName(\"normalized referential specific entropy\");        // sr normalized by R/M\n    ADD_PROPERTY (m_ao  , \"ao\")->SetLongName(\"normalized specific free energy circle\");         // a-circle normalized by R.Tr/M\n    ADD_PROPERTY (m_cp  , \"cp\")->SetLongName(\"normalized isobaric specific heat capacity\");     // cp normalized by R/M\n\nEND_FECORE_CLASS();\n\nFEIdealGas::FEIdealGas(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_R = m_Pr = m_Tr = m_ar = m_sr = 0;\n    m_ao = nullptr;\n    m_cp = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEIdealGas::Init()\n{\n    m_R  = GetGlobalConstant(\"R\");\n    m_Tr = GetGlobalConstant(\"T\");\n    m_Pr = GetGlobalConstant(\"P\");\n    \n    if (m_R  <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\");    return false; }\n    if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    if (m_Pr == 0) {\n        FEThermoFluid* pMat = dynamic_cast<FEThermoFluid*>(GetParent());\n        double rhor = pMat->ReferentialDensity();\n        m_Pr = m_R*m_Tr/m_M*rhor;\n        feLogWarning(\"The referential absolute pressure P is calculated internally as %g\\n\",m_Pr);\n    }\n    \n    m_ao->Init();\n    m_cp->Init();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEIdealGas::Serialize(DumpStream& ar)\n{\n    FEElasticFluid::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_R & m_Pr & m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FEIdealGas::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double T = m_Tr + tf.m_T;\n    double Jf = 1 + fp.m_ef;\n\n    double p = m_Pr*(T/(Jf*m_Tr) - 1);\n\n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FEIdealGas::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double T = m_Tr + tf.m_T;\n    double Jf = 1 + fp.m_ef;\n\n    double dp = -m_Pr*T/(Jf*Jf*m_Tr);\n\n    return dp;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FEIdealGas::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double T = m_Tr + tf.m_T;\n    double Jf = 1 + fp.m_ef;\n\n    double d2p = 2*m_Pr*T/(pow(Jf,3)*m_Tr);\n\n    return d2p;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FEIdealGas::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double Jf = 1 + fp.m_ef;\n\n    double dp = m_Pr/(Jf*m_Tr);\n\n    return dp;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FEIdealGas::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FEIdealGas::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double Jf = 1 + fp.m_ef;\n\n    double d2p = -m_Pr/(Jf*Jf*m_Tr);\n\n    return d2p;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FEIdealGas::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double scl = m_R*m_Tr/m_M;\n    \n    // referential free energy\n    double a = m_ar - m_sr*(That-1);\n    \n    // add a_circle\n    a += m_ao->value(That);\n    \n    // add strain-dependent contribution\n    a += J+That*(log(That/J)-1);\n\n    return a*scl;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FEIdealGas::SpecificEntropy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double scl = m_R/m_M;\n\n    // referential entropy\n    double s = m_sr;\n    \n    // add s_circle\n    s -= m_ao->derive(That);\n    \n    // add strain-dependent contribution\n    s += -log(That/J);\n\n    return s*scl;\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FEIdealGas::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T;\n    double That = T/m_Tr;\n    double scl = m_R*m_Tr/m_M;\n\n    // strain-dependent contribution\n    double a = J+That*(log(That/J)-1);\n\n    return a*scl;\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FEIdealGas::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double scl = m_R/m_M;\n\n    double cp = m_cp->value(That);\n    \n    return cp*scl;\n}\n                \n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FEIdealGas::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    double cv = IsobaricSpecificHeatCapacity(mp) - m_R/m_M;\n\n    return cv;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FEIdealGas::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FEIdealGas::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double scl = m_R/(m_M*m_Tr);\n\n    double dcv = m_cp->derive(That);\n    \n    return dcv*scl;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FEIdealGas::Dilatation(const double T, const double p, double& e)\n{\n    double J = (T+m_Tr)/m_Tr/(1+p/m_Pr);\n    e = J - 1;\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FEIdealGas.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for the viscous part of the fluid response.\n//! These materials provide the viscous stress and its tangents.\n//!\nclass FEBIOFLUID_API FEIdealGas : public FEElasticFluid\n{\npublic:\n    FEIdealGas(FEModel* pfem);\n    ~FEIdealGas() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n\npublic:\n    double      m_R;        //!< universal gas constant\n    double      m_Pr;       //!< referential absolute pressure\n    double      m_Tr;       //!< referential absolute temperature\n    double      m_M;        //!< molar mass\n    double      m_ar;       //!< referential specific free energy\n    double      m_sr;       //!< referential specific entropy\n    FEFunction1D*   m_cp;   //!< isobaric specific heat capacity\n    FEFunction1D*   m_ao;   //!< specific free energy a-circle\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n\n};\n"
  },
  {
    "path": "FEBioFluid/FEIdealGasIsentropic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEIdealGasIsentropic.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIdealGasIsentropic, FEElasticFluid)\n\tADD_PARAMETER(m_gamma, FE_RANGE_GREATER(0.0), \"gamma\")->setLongName(\"specific heat capacity ratio\");\n\tADD_PARAMETER(m_M    , FE_RANGE_GREATER(0.0), \"M\"    )->setLongName(\"molar mass\")->setUnits(UNIT_MOLAR_MASS);\n    ADD_PARAMETER(m_cv0  , FE_RANGE_GREATER(0.0), \"cv0\"  )->setLongName(\"isochoric specific heat capacity\")->setUnits(UNIT_SPECIFIC_ENTROPY);\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEIdealGasIsentropic\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEIdealGasIsentropic constructor\n\nFEIdealGasIsentropic::FEIdealGasIsentropic(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_rhor = 0;\n    m_k = 0;\n    m_gamma = 0;\n    m_M = 0;\n    m_cv0 = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEIdealGasIsentropic::Init() \n{\n    m_R  = GetGlobalConstant(\"R\");\n    m_Tr = GetGlobalConstant(\"T\");\n    m_Pr = GetGlobalConstant(\"P\");\n    \n\tif (m_R  <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tr <= 0) { feLogError(\"A positive ambient absolute temperature T must be defined in Globals section\"); return false; }\n\tif (m_Pr <= 0) { feLogError(\"A positive ambient absolute pressure P must be defined in Globals section\"); return false; }\n\n    if (m_rhor == 0) m_rhor = m_M*m_Pr/(m_R*m_Tr);\n    if (m_k == 0) m_k = m_Pr;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEIdealGasIsentropic::Serialize(DumpStream& ar)\n{\n    FEElasticFluid::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_R & m_Pr & m_Tr & m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure\ndouble FEIdealGasIsentropic::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double p =m_k*(pow(1+fp.m_ef,-m_gamma)-1);\n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FEIdealGasIsentropic::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double dpJ = -m_k*m_gamma*pow(1+fp.m_ef,-1-m_gamma);\n    return dpJ;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FEIdealGasIsentropic::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double d2pJ = m_k*m_gamma*(1+m_gamma)*pow(1+fp.m_ef,-2-m_gamma);\n    return d2pJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FEIdealGasIsentropic::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double dpT = m_k/(1+fp.m_ef)/m_Tr;\n    return dpT;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FEIdealGasIsentropic::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FEIdealGasIsentropic::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double dpTJ = -m_k/pow(1+fp.m_ef,2)/m_Tr;\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FEIdealGasIsentropic::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double a = m_Tr*(m_R/m_M*fp.m_ef + m_cv0*(pow(1+fp.m_ef,1-m_gamma)-1));\n    return a;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FEIdealGasIsentropic::SpecificEntropy(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FEIdealGasIsentropic::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    return SpecificFreeEnergy(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FEIdealGasIsentropic::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return m_cv0 + m_R/m_M;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FEIdealGasIsentropic::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return m_cv0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FEIdealGasIsentropic::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FEIdealGasIsentropic::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FEIdealGasIsentropic::Dilatation(const double T, const double p, double& e)\n{\n    e = pow(1+p/m_k,-1/m_gamma)-1.0;\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FEIdealGasIsentropic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! Ideal gas under isentropic conditions.\n\nclass FEBIOFLUID_API FEIdealGasIsentropic : public FEElasticFluid\n{\npublic:\n    FEIdealGasIsentropic(FEModel* pfem);\n    \npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! elastic pressure\n    double Pressure(FEMaterialPoint& mp) override;\n\n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n\npublic:\n    double      m_gamma;    //!< ratio of specific heats (constant pressure/constant volume)\n    double      m_M;        //!< molar mass\n    double      m_cv0;      //!< isochoric specific heat capacity\n    double      m_Pr;       //!< ambient pressure\n    double      m_Tr;       //!< ambient temperature\n    double      m_R;        //!< universal gas constant\n    double      m_k;        //!< bulk modulus\n    double      m_rhor;     //!< true density\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEIdealGasIsothermal.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEIdealGasIsothermal.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIdealGasIsothermal, FEFluid)\n\tADD_PARAMETER(m_M    , FE_RANGE_GREATER(0.0), \"M\"    )->setUnits(UNIT_MOLAR_MASS)->setLongName(\"molar mass\");\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEIdealGasIsothermal\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEIdealGasIsothermal constructor\n\nFEIdealGasIsothermal::FEIdealGasIsothermal(FEModel* pfem) : FEFluid(pfem)\n{\n    m_rhor = 0;\n    m_k = 0;\n    m_M = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEIdealGasIsothermal::Init()\n{\n    m_R  = GetGlobalConstant(\"R\");\n    m_Tr = GetGlobalConstant(\"T\");\n    m_Pr = GetGlobalConstant(\"P\");\n    \n    if (m_R  <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\");    return false; }\n    if (m_Pr <= 0) { feLogError(\"A positive ambient absolute pressure P must be defined in Globals section\"); return false; }\n    \n    m_rhor = m_M*m_Pr/(m_R*m_Tr);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEIdealGasIsothermal::Serialize(DumpStream& ar)\n{\n    FEFluid::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_R & m_Pr & m_Tr & m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure\ndouble FEIdealGasIsothermal::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    return Pressure(fp.m_ef,0);\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure from dilatation\ndouble FEIdealGasIsothermal::Pressure(const double e, const double T)\n{\n    double J = 1 + e;\n    return m_Pr*(1./J - 1);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of elastic pressure with respect to strain J\ndouble FEIdealGasIsothermal::Tangent_Pressure_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1 + fp.m_ef;\n    double dp = -m_Pr/J;\n    return dp;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of elastic pressure with respect to strain J\ndouble FEIdealGasIsothermal::Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1 + fp.m_ef;\n    double d2p = 2*m_Pr/(J*J);\n    return d2p;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate temperature\ndouble FEIdealGasIsothermal::Temperature(FEMaterialPoint& mp)\n{\n    return m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate free energy density (per reference volume)\ndouble FEIdealGasIsothermal::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1 + fp.m_ef;\n    double sed = m_Pr*(J-1-log(J));\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! invert effective pressure-dilatation relation\nbool FEIdealGasIsothermal::Dilatation(const double T, const double p, double& e)\n{\n    double J = m_Pr/(p+m_Pr);\n    e = J - 1;\n    return true;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEIdealGasIsothermal.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! Ideal gas under isothermal conditions.\n\nclass FEBIOFLUID_API FEIdealGasIsothermal : public FEFluid\n{\npublic:\n    FEIdealGasIsothermal(FEModel* pfem);\n    \npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! elastic pressure\n    double Pressure(FEMaterialPoint& mp) override;\n    double Pressure(const double e, const double T = 0) override;\n    \n    //! tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    //! invert effective pressure-dilatation relation\n    bool Dilatation(const double T, const double p, double& e) override;\n    \n    //! evaluate temperature\n    double Temperature(FEMaterialPoint& mp) override;\n    \npublic:\n    double      m_M;        //!< moral mass\n    double      m_Pr;       //!< ambient pressure\n    double      m_Tr;       //!< ambient temperature\n    double      m_R;        //!< universal gas constant\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidAngularVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEInitialFluidAngularVelocity.h\"\n#include \"FEBioPolarFluid.h\"\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FEInitialFluidAngularVelocity, FENodalIC)\n    ADD_PARAMETER(m_v0, \"value\")->setUnits(UNIT_ANGULAR_VELOCITY);\nEND_FECORE_CLASS();\n\nFEInitialFluidAngularVelocity::FEInitialFluidAngularVelocity(FEModel* fem) : FENodalIC(fem)\n{\n    m_v0 = vec3d(0, 0, 0);\n}\n\n// set the initial value\nvoid FEInitialFluidAngularVelocity::SetValue(const vec3d& v0)\n{\n    m_v0 = v0;\n}\n\n// initialization\nbool FEInitialFluidAngularVelocity::Init()\n{\n    FEDofList dofs(GetFEModel());\n    if (dofs.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_VELOCITY)) == false) return false;\n    SetDOFList(dofs);\n    return true;\n}\n\n// return the values for node i\nvoid FEInitialFluidAngularVelocity::GetNodalValues(int inode, std::vector<double>& values)\n{\n    assert(values.size() == 3);\n    \n    const FENodeSet& nset = *GetNodeSet();\n    const FENode& node = *nset.Node(inode);\n    \n    FEMaterialPoint mp;\n    mp.m_r0 = node.m_r0;\n    mp.m_index = inode;\n    \n    vec3d v0 = m_v0(mp);\n    \n    values[0] = v0.x;\n    values[1] = v0.y;\n    values[2] = v0.z;\n}\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidAngularVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include <FECore/FEInitialCondition.h>\n#include \"febiofluid_api.h\"\n#include <FECore/FEModelParam.h>\n\nclass FEBIOFLUID_API FEInitialFluidAngularVelocity : public FENodalIC\n{\npublic:\n    FEInitialFluidAngularVelocity(FEModel* fem);\n    \n    bool Init() override;\n    \n    // set the initial value\n    void SetValue(const vec3d& v0);\n    \n    // return the values for node i\n    void GetNodalValues(int inode, std::vector<double>& values) override;\n    \nprivate:\n    FEParamVec3        m_v0;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidDilatation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialFluidDilatation.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialFluidDilatation, FEInitialCondition)\n\tADD_PARAMETER(m_data, \"value\")->setUnits(UNIT_NONE);\nEND_FECORE_CLASS();\n\nFEInitialFluidDilatation::FEInitialFluidDilatation(FEModel* fem) : FEInitialDOF(fem)\n{\n}\n\nbool FEInitialFluidDilatation::Init()\n{\n\tif (SetDOF(\"ef\") == false) return false;\n\treturn FEInitialDOF::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidDilatation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialFluidDilatation : public FEInitialDOF\n{\npublic:\n\tFEInitialFluidDilatation(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialFluidPressure.h\"\n#include \"FEBioFluid.h\"\n#include \"FEFluid.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialFluidPressure, FEInitialCondition)\n// material properties\n    ADD_PARAMETER(m_Pdata, \"value\"   )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEInitialFluidPressure::FEInitialFluidPressure(FEModel* fem) : FENodalIC(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidPressure::Init()\n{\n    if (SetPDOF(\"ef\") == false) return false;\n    m_e.assign(m_nodeSet->Size(), 0.0);\n\n    FEDofList dofs(GetFEModel());\n    if (dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION)) == false) return false;\n    SetDOFList(dofs);\n    \n\treturn FENodalIC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressure::SetPDOF(int ndof) { m_dofEF = ndof; }\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidPressure::SetPDOF(const char* szdof)\n{\n    FEModel* fem = GetFEModel();\n    int ndof = fem->GetDOFIndex(szdof);\n    assert(ndof >= 0);\n    if (ndof < 0) return false;\n    SetPDOF(ndof);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressure::Activate()\n{\n    // prescribe this dilatation at the nodes\n    FEModel* fem = GetFEModel();\n    \n    FENodeList nodeList = m_nodeSet->GetNodeList();\n\n    int N = nodeList.Size();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    \n    FEMesh& mesh = *m_nodeSet->GetMesh();\n\n    // evaluate average prescribed pressure and temperature in each element\n    // project them from int points to nodes\n    for (int i=0; i<mesh.Elements(); ++i)\n    {\n        FEElement& el = *mesh.Element(i);\n        // get material\n        FEMaterial* pm = GetFEModel()->GetMaterial(el.GetMatID());\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        if (pfl) {\n            double efi[FEElement::MAX_INTPOINTS] = {0};\n            double efo[FEElement::MAX_NODES] = {0};\n            bool good = true;\n            for (int j=0; j<el.GaussPoints(); ++j) {\n                FEMaterialPoint* pt = el.GetMaterialPoint(j);\n                good = good && pfl->Dilatation(0, m_Pdata(*pt), efi[j]);\n            }\n            // project dilatations from integration points to nodes\n            el.project_to_nodes(efi, efo);\n            if (good) {\n                for (int j=0; j<el.Nodes(); ++j)\n                    efNodes[el.m_node[j]].push_back(efo[j]);\n            }\n            else {\n                for (int j=0; j<el.Nodes(); ++j) {\n                    efo[j] = 0;\n                    efNodes[el.m_node[j]].push_back(efo[j]);\n                }\n            }\n        }\n        else break;\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<nodeList.Size(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n            \n        // store value for now\n        m_e[i] = ef;\n    }\n    \n    FEStepComponent::Activate();\n    if (m_dofs.IsEmpty()) return;\n\n    int dofs = (int)m_dofs.Size();\n    std::vector<double> val(dofs, 0.0);\n\n    for (int i = 0; i<N; ++i)\n    {\n        FENode& node = *m_nodeSet->Node(i);\n\n        // get the nodal values\n        GetNodalValues(i, val);\n        \n        for (int j = 0; j < dofs; ++j)\n        {\n            node.set(m_dofs[j], val[j]);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressure::GetNodalValues(int inode, std::vector<double>& values)\n{\n    values[0] = m_e[inode];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressure::Serialize(DumpStream& ar)\n{\n    FENodalIC::Serialize(ar);\n    if (ar.IsLoading())\n        m_e.assign(m_nodeSet->Size(), 0.0);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofEF;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialFluidPressure : public FENodalIC\n{\npublic:\n    FEInitialFluidPressure(FEModel* fem);\n\tbool Init() override;\n    void Activate() override;\n    \n    void SetPDOF(int ndof);\n    bool SetPDOF(const char* szdof);\n\n    void GetNodalValues(int inode, std::vector<double>& values) override;\n\n    void Serialize(DumpStream& ar) override;\n\nprotected:\n    int     m_dofEF;\n    FEParamDouble   m_Pdata;\n    vector<double>  m_e;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidPressureTemperature.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialFluidPressureTemperature.h\"\n#include \"FEBioThermoFluid.h\"\n#include \"FEThermoFluid.h\"\n#include \"FEFluid.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialFluidPressureTemperature, FEInitialCondition)\n// material properties\n    ADD_PARAMETER(m_Pdata, \"pressure\"   )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_Tdata, \"temperature\")->setUnits(UNIT_RELATIVE_TEMPERATURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEInitialFluidPressureTemperature::FEInitialFluidPressureTemperature(FEModel* fem) : FENodalIC(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidPressureTemperature::Init()\n{\n    if (SetPDOF(\"ef\") == false) return false;\n\tif (SetTDOF(\"T\") == false) return false;\n    m_e.assign(m_nodeSet->Size(), 0.0);\n    m_T.assign(m_nodeSet->Size(), 0.0);\n\n    FEDofList dofs(GetFEModel());\n    if (dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION)) == false) return false;\n    if (dofs.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE)) == false) return false;\n    SetDOFList(dofs);\n    \n\treturn FENodalIC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressureTemperature::SetTDOF(int ndof) { m_dofT = ndof; }\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidPressureTemperature::SetTDOF(const char* szdof)\n{\n    FEModel* fem = GetFEModel();\n    int ndof = fem->GetDOFIndex(szdof);\n    assert(ndof >= 0);\n    if (ndof < 0) return false;\n    SetTDOF(ndof);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressureTemperature::SetPDOF(int ndof) { m_dofEF = ndof; }\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidPressureTemperature::SetPDOF(const char* szdof)\n{\n    FEModel* fem = GetFEModel();\n    int ndof = fem->GetDOFIndex(szdof);\n    assert(ndof >= 0);\n    if (ndof < 0) return false;\n    SetPDOF(ndof);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressureTemperature::Activate()\n{\n    // prescribe this dilatation at the nodes\n    FEModel* fem = GetFEModel();\n    \n    FENodeList nodeList = m_nodeSet->GetNodeList();\n\n    int N = nodeList.Size();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    \n    FEMesh& mesh = *m_nodeSet->GetMesh();\n\n    // evaluate average prescribed pressure and temperature in each element\n    // project them from int points to nodes\n    for (int i=0; i<mesh.Elements(); ++i)\n    {\n        FEElement& el = *mesh.Element(i);\n        // get material\n        FEMaterial* pm = GetFEModel()->GetMaterial(el.GetMatID());\n        FEThermoFluid* ptfl = pm->ExtractProperty<FEThermoFluid>();\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        if (ptfl) {\n            double efi[FEElement::MAX_INTPOINTS] = {0};\n            double efo[FEElement::MAX_NODES] = {0};\n            bool good = true;\n            for (int j=0; j<el.GaussPoints(); ++j) {\n                FEMaterialPoint* pt = el.GetMaterialPoint(j);\n                good = good && ptfl->Dilatation(m_Tdata(*pt), m_Pdata(*pt), efi[j]);\n            }\n            // project dilatations from integration points to nodes\n            el.project_to_nodes(efi, efo);\n            if (good) {\n                for (int j=0; j<el.Nodes(); ++j)\n                    efNodes[el.m_node[j]].push_back(efo[j]);\n            }\n            else {\n                for (int j=0; j<el.Nodes(); ++j) {\n                    efo[j] = 0;\n                    efNodes[el.m_node[j]].push_back(efo[j]);\n                }\n            }\n        }\n        else if (pfl) {\n            double efi[FEElement::MAX_INTPOINTS] = {0};\n            double efo[FEElement::MAX_NODES] = {0};\n            bool good = true;\n            for (int j=0; j<el.GaussPoints(); ++j) {\n                FEMaterialPoint* pt = el.GetMaterialPoint(j);\n                good = good && pfl->Dilatation(0, m_Pdata(*pt), efi[j]);\n            }\n            // project dilatations from integration points to nodes\n            el.project_to_nodes(efi, efo);\n            if (good) {\n                for (int j=0; j<el.Nodes(); ++j)\n                    efNodes[el.m_node[j]].push_back(efo[j]);\n            }\n            else {\n                for (int j=0; j<el.Nodes(); ++j) {\n                    efo[j] = 0;\n                    efNodes[el.m_node[j]].push_back(efo[j]);\n                }\n            }\n        }\n        else break;\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<nodeList.Size(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n            \n        // store value for now\n        m_e[i] = ef;\n        \n        // get the nodal temperature\n        FENode& node = *nodeList.Node(i);\n        FEMaterialPoint mp;\n        mp.m_r0 = node.m_r0;\n        mp.m_index = i;\n\n        m_T[i] = m_Tdata(mp);\n    }\n    \n    FEStepComponent::Activate();\n    if (m_dofs.IsEmpty()) return;\n\n    int dofs = (int)m_dofs.Size();\n    std::vector<double> val(dofs, 0.0);\n\n    for (int i = 0; i<N; ++i)\n    {\n        FENode& node = *m_nodeSet->Node(i);\n\n        // get the nodal values\n        GetNodalValues(i, val);\n        \n        for (int j = 0; j < dofs; ++j)\n        {\n            node.set(m_dofs[j], val[j]);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressureTemperature::GetNodalValues(int inode, std::vector<double>& values)\n{\n    values[0] = m_e[inode];\n    values[1] = m_T[inode];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidPressureTemperature::Serialize(DumpStream& ar)\n{\n    FENodalIC::Serialize(ar);\n    if (ar.IsLoading()) {\n        m_e.assign(m_nodeSet->Size(), 0.0);\n        m_T.assign(m_nodeSet->Size(), 0.0);\n    }\n    ar & m_e & m_T;\n    if (ar.IsShallow()) return;\n    ar & m_dofEF & m_dofT;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidPressureTemperature.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialFluidPressureTemperature : public FENodalIC\n{\npublic:\n    FEInitialFluidPressureTemperature(FEModel* fem);\n\tbool Init() override;\n    void Activate() override;\n    \n    void SetPDOF(int ndof);\n    bool SetPDOF(const char* szdof);\n\n    void SetTDOF(int ndof);\n    bool SetTDOF(const char* szdof);\n\n    void GetNodalValues(int inode, std::vector<double>& values) override;\n\n    void Serialize(DumpStream& ar) override;\n\nprotected:\n    int     m_dofEF;\n    int     m_dofT;\n    FEParamDouble   m_Pdata;\n    FEParamDouble   m_Tdata;\n    vector<double>  m_e;\n    vector<double>  m_T;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidSolutesPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialFluidSolutesPressure.h\"\n#include \"FEBioFluidSolutes.h\"\n#include \"FEFluidSolutes.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialFluidSolutesPressure, FEInitialCondition)\n// material properties\n    ADD_PARAMETER(m_Pdata, \"value\"   )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEInitialFluidSolutesPressure::FEInitialFluidSolutesPressure(FEModel* fem) : FENodalIC(fem)\n{\n    m_dofEF = -1;\n    m_dofC = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n    m_Fc = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidSolutesPressure::Init()\n{\n    if (SetPDOF(\"ef\") == false) return false;\n    m_e.assign(m_nodeSet->Size(), 0.0);\n\n    FEDofList dofs(GetFEModel());\n    if (dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION)) == false) return false;\n    SetDOFList(dofs);\n    m_dofC = GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0);\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n    m_Fc   = GetFEModel()->GetGlobalConstant(\"Fc\");\n\n\treturn FENodalIC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidSolutesPressure::SetPDOF(int ndof) { m_dofEF = ndof; }\n\n//-----------------------------------------------------------------------------\nbool FEInitialFluidSolutesPressure::SetPDOF(const char* szdof)\n{\n    FEModel* fem = GetFEModel();\n    int ndof = fem->GetDOFIndex(szdof);\n    assert(ndof >= 0);\n    if (ndof < 0) return false;\n    SetPDOF(ndof);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidSolutesPressure::Activate()\n{\n    // prescribe this dilatation at the nodes\n    FEModel* fem = GetFEModel();\n    \n    FENodeList nodeList = m_nodeSet->GetNodeList();\n\n    int N = nodeList.Size();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    \n    FEMesh& mesh = *m_nodeSet->GetMesh();\n\n    // evaluate average prescribed pressure and temperature in each element\n    // project them from int points to nodes\n    for (int i=0; i<mesh.Elements(); ++i)\n    {\n        FEElement& el = *mesh.Element(i);\n        FESolidElement* se = dynamic_cast<FESolidElement*>(&el);\n        // get material\n        FEMaterial* pm = GetFEModel()->GetMaterial(el.GetMatID());\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        if (pfl) {\n            double efi[FEElement::MAX_INTPOINTS] = {0};\n            double efo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0, p = 0, epot = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    p += m_Pdata(*pt);\n                    epot += psi->GetElectricPotential(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetSolute(k)->m_pSolub->Solubility(*pt);\n                }\n                osc /= nint;\n                p /= nint;\n                epot /= nint;\n                double ex = exp(-m_Fc*epot/m_Rgas/m_Tabs);\n                for (int k=0; k<nsol; ++k) kappa[k] *= pow(ex,psi->GetSolute(k)->ChargeNumber())/nint;\n                // loop over element nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = mesh.Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    bool good = pfl->Dilatation(0, p - m_Rgas*m_Tabs*osc*osm, efo[j]);\n                    assert(good);\n                }\n            }\n            else {\n                double p = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    p += m_Pdata(*pt);\n                }\n                p /= nint;\n                // loop over element nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = mesh.Node(el.m_lnode[j]);\n                    bool good = pfl->Dilatation(0, p, efo[j]);\n                    assert(good);\n                }\n            }\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(efo[j]);\n        }\n        //If no solid element, insert all 0s\n        else {\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(0);\n        }\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n        \n        // store value for now\n        m_e[i] = ef;\n    }\n\n    FEStepComponent::Activate();\n    if (m_dofs.IsEmpty()) return;\n\n    int dofs = (int)m_dofs.Size();\n    std::vector<double> val(dofs, 0.0);\n\n    for (int i = 0; i<N; ++i)\n    {\n        FENode& node = *m_nodeSet->Node(i);\n\n        // get the nodal values\n        GetNodalValues(i, val);\n        \n        for (int j = 0; j < dofs; ++j)\n        {\n            node.set(m_dofs[j], val[j]);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidSolutesPressure::GetNodalValues(int inode, std::vector<double>& values)\n{\n    values[0] = m_e[inode];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialFluidSolutesPressure::Serialize(DumpStream& ar)\n{\n    FENodalIC::Serialize(ar);\n    if (ar.IsLoading())\n        m_e.assign(m_nodeSet->Size(), 0.0);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofEF;\n    ar & m_dofC;\n    ar & m_Rgas & m_Tabs & m_Fc;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidSolutesPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialFluidSolutesPressure : public FENodalIC\n{\npublic:\n    FEInitialFluidSolutesPressure(FEModel* fem);\n\tbool Init() override;\n    void Activate() override;\n    \n    void SetPDOF(int ndof);\n    bool SetPDOF(const char* szdof);\n\n    void GetNodalValues(int inode, std::vector<double>& values) override;\n\n    void Serialize(DumpStream& ar) override;\n\nprotected:\n    int             m_dofEF;\n    int             m_dofC;\n    FEParamDouble   m_Pdata;\n    vector<double>  m_e;\n    double          m_Rgas;\n    double          m_Tabs;\n    double          m_Fc;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidTemperature.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialFluidTemperature.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialFluidTemperature, FEInitialCondition)\n\tADD_PARAMETER(m_data, \"value\")->setUnits(UNIT_RELATIVE_TEMPERATURE);\nEND_FECORE_CLASS();\n\nFEInitialFluidTemperature::FEInitialFluidTemperature(FEModel* fem) : FEInitialDOF(fem)\n{\n}\n\nbool FEInitialFluidTemperature::Init()\n{\n\tif (SetDOF(\"T\") == false) return false;\n\treturn FEInitialDOF::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidTemperature.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialFluidTemperature : public FEInitialDOF\n{\npublic:\n    FEInitialFluidTemperature(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEInitialFluidVelocity.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FEInitialFluidVelocity, FENodalIC)\n    ADD_PARAMETER(m_v0, \"value\")->setUnits(UNIT_VELOCITY);\nEND_FECORE_CLASS();\n\nFEInitialFluidVelocity::FEInitialFluidVelocity(FEModel* fem) : FENodalIC(fem)\n{\n    m_v0 = vec3d(0, 0, 0);\n}\n\n// set the initial value\nvoid FEInitialFluidVelocity::SetValue(const vec3d& v0)\n{\n    m_v0 = v0;\n}\n\n// initialization\nbool FEInitialFluidVelocity::Init()\n{\n    FEDofList dofs(GetFEModel());\n    if (dofs.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY)) == false) return false;\n    SetDOFList(dofs);\n    return true;\n}\n\n// return the values for node i\nvoid FEInitialFluidVelocity::GetNodalValues(int inode, std::vector<double>& values)\n{\n    assert(values.size() == 3);\n    \n    const FENodeSet& nset = *GetNodeSet();\n    const FENode& node = *nset.Node(inode);\n    \n    FEMaterialPoint mp;\n    mp.m_r0 = node.m_r0;\n    mp.m_index = inode;\n    \n    vec3d v0 = m_v0(mp);\n    \n    values[0] = v0.x;\n    values[1] = v0.y;\n    values[2] = v0.z;\n}\n"
  },
  {
    "path": "FEBioFluid/FEInitialFluidVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEInitialCondition.h>\n#include \"febiofluid_api.h\"\n#include <FECore/FEModelParam.h>\n\nclass FEBIOFLUID_API FEInitialFluidVelocity : public FENodalIC\n{\npublic:\n    FEInitialFluidVelocity(FEModel* fem);\n    \n    bool Init() override;\n    \n    // set the initial value\n    void SetValue(const vec3d& v0);\n    \n    // return the values for node i\n    void GetNodalValues(int inode, std::vector<double>& values) override;\n    \nprivate:\n    FEParamVec3        m_v0;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FELinearElasticFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FELinearElasticFluid.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include \"FEFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nFELinearElasticFluid::FELinearElasticFluid(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_k = 0;\n    m_rhor = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FELinearElasticFluid::Init()\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FELinearElasticFluid::Serialize(DumpStream& ar)\n{\n    if (ar.IsShallow()) return;\n    ar & m_k & m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FELinearElasticFluid::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double p = -m_k*fp.m_ef;\n    \n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FELinearElasticFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return -m_k;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FELinearElasticFluid::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FELinearElasticFluid::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FELinearElasticFluid::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FELinearElasticFluid::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FELinearElasticFluid::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double a = m_k/(2*m_rhor)*pow(fp.m_ef,2);\n    return a;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FELinearElasticFluid::SpecificEntropy(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FELinearElasticFluid::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    return SpecificFreeEnergy(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FELinearElasticFluid::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FELinearElasticFluid::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FELinearElasticFluid::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FELinearElasticFluid::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FELinearElasticFluid::Dilatation(const double T, const double p, double& e)\n{\n    e = -p/m_k;\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FELinearElasticFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Linear elastic fluid\n//!\nclass FEBIOFLUID_API FELinearElasticFluid : public FEElasticFluid\n{\npublic:\n    FELinearElasticFluid(FEModel* pfem);\n    ~FELinearElasticFluid() {}\n    \n    //! initialization\n    bool Init() override;\n\n    // serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n    \npublic:\n    double      m_k;        //!< bulk modulus\n    double      m_rhor;     //!< true density\n};\n"
  },
  {
    "path": "FEBioFluid/FELogNonlinearElasticFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FELogNonlinearElasticFluid.h\"\n#include \"FEFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nFELogNonlinearElasticFluid::FELogNonlinearElasticFluid(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_k = 0;\n    m_rhor = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FELogNonlinearElasticFluid::Init()\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FELogNonlinearElasticFluid::Serialize(DumpStream& ar)\n{\n    if (ar.IsLoading()) return;\n    ar & m_k & m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FELogNonlinearElasticFluid::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1+fp.m_ef;\n    return -m_k*log(J)/J;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FELogNonlinearElasticFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1+fp.m_ef;\n    return m_k*(log(J)-1)/pow(J,2);\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FELogNonlinearElasticFluid::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1+fp.m_ef;\n    return m_k*(3-2*log(J))/pow(J,3);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FELogNonlinearElasticFluid::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FELogNonlinearElasticFluid::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FELogNonlinearElasticFluid::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FELogNonlinearElasticFluid::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double J = 1+fp.m_ef;\n    return m_k/2*pow(log(J),2)/m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FELogNonlinearElasticFluid::SpecificEntropy(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FELogNonlinearElasticFluid::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    return SpecificFreeEnergy(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FELogNonlinearElasticFluid::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FELogNonlinearElasticFluid::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FELogNonlinearElasticFluid::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FELogNonlinearElasticFluid::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FELogNonlinearElasticFluid::Dilatation(const double T, const double p, double& e)\n{\n    double errabs = 1e-3;\n    double errrel = 1e-3;\n    int maxiter = 50;\n    bool cnvgd = false;\n    // initializations\n    e = -p/(m_k+p);                     // initial guess\n    double f = log(1+e)+p*(1+e)/m_k;    // function\n    double de = 0;\n    int iter = 0;\n    while (!cnvgd) {\n        double df = p/m_k+1.0/(1+e);    // derivative\n        double de = -f/df;              // solution increment\n        e += de;                        // update solution\n        f = log(1+e)+p*(1+e)/m_k;       // function\n        // check convergence\n        if ((fabs(f) < errabs) || (fabs(de) < errrel*fabs(e))) cnvgd = true;\n        else if (++iter > maxiter) return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FELogNonlinearElasticFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Linear elastic fluid\n//!\nclass FEBIOFLUID_API FELogNonlinearElasticFluid : public FEElasticFluid\n{\npublic:\n    FELogNonlinearElasticFluid(FEModel* pfem);\n    ~FELogNonlinearElasticFluid() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    // serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n    \npublic:\n    double      m_k;        //!< bulk modulus\n    double      m_rhor;     //!< true density\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSI.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEMultiphasicFSI.h\"\n#include \"FEFluidFSI.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n#include \"FECore/FEModel.h\"\n#include <FECore/log.h>\n#include <FECore/tools.h>\n#include <complex>\nusing namespace std;\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMultiphasicFSI, FEBiphasicFSI)\nADD_PARAMETER(m_penalty, FE_RANGE_GREATER_OR_EQUAL(0.0), \"penalty\"      );\nADD_PARAMETER(m_diffMtmSupp , \"dms\");\nADD_PARAMETER(m_cFr    , \"fixed_charge_density\");\n// material properties\nADD_PROPERTY(m_pOsmC  , \"osmotic_coefficient\");\nADD_PROPERTY(m_pSolute, \"solute\"             , FEProperty::Optional);\nADD_PROPERTY(m_pReact , \"reaction\"           , FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFSIMaterialPoint\n//============================================================================\nFEMultiphasicFSIMaterialPoint::FEMultiphasicFSIMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMultiphasicFSIMaterialPoint::Copy()\n{\n    FEMultiphasicFSIMaterialPoint* pt = new FEMultiphasicFSIMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_nsol & m_psi & m_Ie & m_cF & m_pe;\n    ar & m_c & m_ca & m_gradc & m_j & m_cdot & m_k & m_dkdJ;\n    ar & m_dkdc;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIMaterialPoint::Init()\n{\n    m_nsol = 0;\n    m_psi = m_cF = 0;\n    m_pe = 0;\n    m_Ie = vec3d(0,0,0);\n    m_c.clear();\n    m_ca.clear();\n    m_gradc.clear();\n    m_j.clear();\n    m_cdot.clear();\n    m_k.clear();\n    m_dkdJ.clear();\n    m_dkdJJ.clear();\n    m_dkdc.clear();\n    m_dkdJc.clear();\n    m_dkdcc.clear();\n    \n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMultiphasicFSIMaterialPoint::Osmolarity() const\n{\n    double ew = 0.0;\n    for (int isol = 0; isol < (int)m_ca.size(); ++isol)\n    {\n        ew += m_ca[isol];\n    }\n    return ew;\n}\n\n//============================================================================\n// FEFluidFSI\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluidFSI constructor\n\nFEMultiphasicFSI::FEMultiphasicFSI(FEModel* pfem) : FEBiphasicFSI(pfem)\n{\n    m_Rgas = 0; m_Tabs = 0; m_Fc = 0;\n    m_cFr = 0.0;\n    m_diffMtmSupp = 1.0;\n    m_penalty = 1;\n    m_pOsmC = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEMultiphasicFSI::CreateMaterialPointData()\n{\n    FEFluidMaterialPoint* fpt = new FEFluidMaterialPoint(m_pSolid->CreateMaterialPointData());\n    FEFSIMaterialPoint* fst = new FEFSIMaterialPoint(fpt);\n    FEBiphasicFSIMaterialPoint* bfpt = new FEBiphasicFSIMaterialPoint(fst);\n    FEMultiphasicFSIMaterialPoint* mfpt = new FEMultiphasicFSIMaterialPoint(bfpt);\n    \n    return mfpt;\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FEMultiphasicFSI::Init()\n{\n    // set the solute IDs first, since they are referenced in FESolute::Init()\n    for (int i = 0; i<Solutes(); ++i) {\n        m_pSolute[i]->SetSoluteLocalID(i);\n    }\n    \n    // call the base class.\n    // This also initializes all properties\n    if (FEBiphasicFSI::Init() == false) return false;\n    \n    // Determine how to solve for the electric potential psi\n    int isol;\n    int zmin = 0, zmax = 0, z;\n    for (isol=0; isol<(int)m_pSolute.size(); ++isol) {\n        z = m_pSolute[isol]->ChargeNumber();\n        if (z < zmin) zmin = z;\n        if (z > zmax) zmax = z;\n    }\n    m_zmin = zmin;\n    m_ndeg = zmax - zmin;    // polynomial degree\n    \n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n    m_Fc   = GetFEModel()->GetGlobalConstant(\"Fc\");\n    \n    if (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n    if (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");     return false; }\n    if ((zmin || zmax) && (m_Fc <= 0)) {\n        feLogError(\"A positive Faraday constant Fc must be defined in Globals section\");\n        return false;\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSI::Serialize(DumpStream& ar)\n{\n    FEMaterial::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_Rgas & m_Tabs & m_Fc;\n    ar & m_zmin & m_ndeg;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a poro-elastic material is the sum of the fluid stress\n//! and the elastic stress. Note that this function is declared in the base class\n//! so you do not have to reimplement it in a derived class, unless additional\n//! pressure terms are required.\n\nmat3ds FEMultiphasicFSI::Stress(FEMaterialPoint& mp)\n{\n    // calculate solid material stress\n    mat3ds s = m_pSolid->Stress(mp);\n    \n    // add fluid stress\n    s += m_pFluid->GetViscous()->Stress(mp);\n    \n    //add actual pressure\n    s += -mat3dd(1.0)*PressureActual(mp);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the permeability tensor as a double array\n\nvoid FEMultiphasicFSI::Diffusivity(double d[3][3], FEMaterialPoint& pt, int sol)\n\n{\n    mat3ds dt = m_pSolute[sol]->m_pDiff->Diffusivity(pt);\n    \n    d[0][0] = dt.xx();\n    d[1][1] = dt.yy();\n    d[2][2] = dt.zz();\n    d[0][1] = d[1][0] = dt.xy();\n    d[1][2] = d[2][1] = dt.yz();\n    d[2][0] = d[0][2] = dt.xz();\n    \n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEMultiphasicFSI::Diffusivity(FEMaterialPoint& mp, int sol)\n{\n    return m_pSolute[sol]->m_pDiff->Diffusivity(mp);\n}\n\n//-----------------------------------------------------------------------------\ntens4dmm FEMultiphasicFSI::Diffusivity_Tangent_Strain(FEMaterialPoint& mp, int isol)\n{\n    //Return 0 if diffusivity is 0 to avoid NAN\n    if (m_pSolute[isol]->m_pDiff->Diffusivity(mp).xx() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).xy() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).xz() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).yy() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).yz() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).zz() == 0.0)\n        return tens4dmm(0.0);\n    else\n        return m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEMultiphasicFSI::Diffusivity_Tangent_Concentration(FEMaterialPoint& mp, int isol, int jsol)\n{\n    //Return 0 if diffusivity is 0 to avoid NAN\n    if (m_pSolute[isol]->m_pDiff->Diffusivity(mp).xx() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).xy() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).xz() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).yy() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).yz() == 0.0 && m_pSolute[isol]->m_pDiff->Diffusivity(mp).zz() == 0.0)\n        return mat3ds(0.0);\n    else\n        return m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Concentration(mp, jsol);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEMultiphasicFSI::InvDiffusivity(FEMaterialPoint& mp, int sol)\n{\n    //Return 0 for inverse diffusivity when diffusivity is set to 0.\n    //Acts as if diffusivity if infinite.\n    if (m_pSolute[sol]->m_pDiff->Diffusivity(mp).xx() == 0.0 && m_pSolute[sol]->m_pDiff->Diffusivity(mp).xy() == 0.0 && m_pSolute[sol]->m_pDiff->Diffusivity(mp).xz() == 0.0 && m_pSolute[sol]->m_pDiff->Diffusivity(mp).yy() == 0.0 && m_pSolute[sol]->m_pDiff->Diffusivity(mp).yz() == 0.0 && m_pSolute[sol]->m_pDiff->Diffusivity(mp).zz() == 0.0)\n        return mat3ds(0.0);\n    else\n        return m_pSolute[sol]->m_pDiff->Diffusivity(mp).inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! Electric potential\ndouble FEMultiphasicFSI::ElectricPotential(FEMaterialPoint& pt, const bool eform)\n{\n    // check if solution is neutral\n    if (m_ndeg == 0) {\n        if (eform) return 1.0;\n        else return 0.0;\n    }\n    \n    int i, j;\n    \n    // if not neutral, solve electroneutrality polynomial for zeta\n    FEMultiphasicFSIMaterialPoint& set = *pt.ExtractData<FEMultiphasicFSIMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    double cF = FixedChargeDensity(pt);\n    \n    vector<double> c(nsol);        // effective concentration\n    vector<double> khat(nsol);    // solubility\n    vector<int> z(nsol);        // charge number\n    for (i=0; i<nsol; ++i) {\n        c[i] = set.m_c[i];\n        khat[i] = m_pSolute[i]->m_pSolub->Solubility(pt);\n        z[i] = m_pSolute[i]->ChargeNumber();\n    }\n    \n    // evaluate polynomial coefficients\n    const int n = m_ndeg;\n    vector<double> a(n+1,0);\n    if (m_zmin < 0) {\n        for (i=0; i<nsol; ++i) {\n            j = z[i] - m_zmin;\n            a[j] += z[i]*khat[i]*c[i];\n        }\n        a[-m_zmin] = cF;\n    } else {\n        for (i=0; i<nsol; ++i) {\n            j = z[i];\n            a[j] += z[i]*khat[i]*c[i];\n        }\n        a[0] = cF;\n    }\n    \n    // solve polynomial\n    double psi = set.m_psi;        // use previous solution as initial guess\n    double zeta = exp(-m_Fc*psi/m_Rgas/m_Tabs);\n    if (!solvepoly(n, a, zeta)) {\n        zeta = 1.0;\n    }\n    \n    // Return exponential (non-dimensional) form if desired\n    if (eform) return zeta;\n    \n    // Otherwise return dimensional value of electric potential\n    psi = -m_Rgas*m_Tabs/m_Fc*log(zeta);\n    \n    return psi;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficient\ndouble FEMultiphasicFSI::PartitionCoefficient(FEMaterialPoint& pt, const int sol)\n{\n    \n    // solubility\n    double khat = m_pSolute[sol]->m_pSolub->Solubility(pt);\n    // charge number\n    int z = m_pSolute[sol]->ChargeNumber();\n    // electric potential\n    double zeta = ElectricPotential(pt, true);\n    double zz = pow(zeta, z);\n    // partition coefficient\n    double kappa = zz*khat;\n    \n    return kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficients and their derivatives\nvoid FEMultiphasicFSI::PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                                   vector<double>& dkdJ,\n                                                   vector< vector<double> >& dkdc)\n{\n    //TODO: Include dkdcc and dkdJc\n    \n    int isol, jsol, ksol;\n    \n    FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n    FEFluidMaterialPoint& fpt = *(mp.ExtractData<FEFluidMaterialPoint>());\n    FEBiphasicFSIMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n    FEMultiphasicFSIMaterialPoint& spt = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n    \n    const int nsol = (int)m_pSolute.size();\n    \n    vector<double> c(nsol);\n    vector<int> z(nsol);\n    vector<double> khat(nsol);\n    vector<double> dkhdJ(nsol);\n    vector<double> dkhdJJ(nsol);\n    vector< vector<double> > dkhdc(nsol, vector<double>(nsol));\n    vector< vector<double> > dkhdJc(nsol, vector<double>(nsol));\n    vector< vector< vector<double> > > dkhdcc(nsol, dkhdc);    // use dkhdc to initialize only\n    vector<double> zz(nsol);\n    kappa.resize(nsol);\n    \n    double den = 0;\n    double num = 0;\n    double zeta = ElectricPotential(mp, true);\n    \n    for (isol=0; isol<nsol; ++isol) {\n        // get the effective concentration, its gradient and its time derivative\n        c[isol] = spt.m_c[isol];\n        // get the charge number\n        z[isol] = m_pSolute[isol]->ChargeNumber();\n        // evaluate the solubility and its derivatives w.r.t. J and c\n        khat[isol] = m_pSolute[isol]->m_pSolub->Solubility(mp);\n        dkhdJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain(mp);\n        dkhdJJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Strain(mp);\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkhdc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration(mp,jsol);\n            dkhdJc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Concentration(mp,jsol);\n            for (ksol=0; ksol<nsol; ++ksol) {\n                dkhdcc[isol][jsol][ksol] =\n                m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration_Concentration(mp,jsol,ksol);\n            }\n        }\n        zz[isol] = pow(zeta, z[isol]);\n        kappa[isol] = zz[isol]*khat[isol];\n        den += SQR(z[isol])*kappa[isol]*c[isol];\n        num += pow((double)z[isol],3)*kappa[isol]*c[isol];\n    }\n    \n    // get the charge density and its derivatives\n    double J = ept.m_J;\n    double phi0 = ppt.m_phi0;\n    double cF = FixedChargeDensity(mp);\n    double dcFdJ = -cF/(J - phi0);\n    double dcFdJJ = 2*cF/SQR(J-phi0);\n    \n    // evaluate electric potential (nondimensional exponential form) and its derivatives\n    // also evaluate partition coefficients and their derivatives\n    double zidzdJ = 0;\n    double zidzdJJ = 0, zidzdJJ1 = 0, zidzdJJ2 = 0;\n    vector<double> zidzdc(nsol,0);\n    vector<double> zidzdJc(nsol,0), zidzdJc1(nsol,0), zidzdJc2(nsol,0);\n    vector< vector<double> > zidzdcc(nsol, vector<double>(nsol,0));\n    vector< vector<double> > zidzdcc1(nsol, vector<double>(nsol,0));\n    vector<double> zidzdcc2(nsol,0);\n    double zidzdcc3 = 0;\n    \n    if (den > 0) {\n        \n        for (isol=0; isol<nsol; ++isol)\n            zidzdJ += z[isol]*zz[isol]*dkhdJ[isol]*c[isol];\n        zidzdJ = -(dcFdJ+zidzdJ)/den;\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdJJ1 += SQR(z[jsol])*c[jsol]*(z[jsol]*zidzdJ*kappa[jsol]+zz[jsol]*dkhdJ[jsol]);\n                zidzdJJ2 += z[jsol]*zz[jsol]*c[jsol]*(zidzdJ*z[jsol]*dkhdJ[jsol]+dkhdJJ[jsol]);\n                zidzdc[isol] += z[jsol]*zz[jsol]*dkhdc[jsol][isol]*c[jsol];\n            }\n            zidzdc[isol] = -(z[isol]*kappa[isol]+zidzdc[isol])/den;\n            zidzdcc3 += pow(double(z[isol]),3)*kappa[isol]*c[isol];\n        }\n        zidzdJJ = zidzdJ*(zidzdJ-zidzdJJ1/den)-(dcFdJJ+zidzdJJ2)/den;\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdJc1[isol] += SQR(z[jsol])*c[jsol]*(zidzdc[isol]*z[jsol]*kappa[jsol]+zz[jsol]*dkhdc[jsol][isol]);\n                zidzdJc2[isol] += z[jsol]*zz[jsol]*c[jsol]*(zidzdc[isol]*z[jsol]*dkhdJ[jsol]+dkhdJc[jsol][isol]);\n                zidzdcc2[isol] += SQR(z[jsol])*zz[jsol]*c[jsol]*dkhdc[jsol][isol];\n                for (ksol=0; ksol<nsol; ++ksol)\n                    zidzdcc1[isol][jsol] += z[ksol]*zz[ksol]*c[ksol]*dkhdcc[ksol][isol][jsol];\n            }\n            zidzdJc[isol] = zidzdJ*(zidzdc[isol]-(SQR(z[isol])*kappa[isol] + zidzdJc1[isol])/den)\n            -(z[isol]*zz[isol]*dkhdJ[isol] + zidzdJc2[isol])/den;\n        }\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdcc[isol][jsol] = zidzdc[isol]*zidzdc[jsol]*(1 - zidzdcc3/den)\n                - zidzdcc1[isol][jsol]/den\n                - z[isol]*(z[isol]*kappa[isol]*zidzdc[jsol]+zz[isol]*dkhdc[isol][jsol])/den\n                - z[jsol]*(z[jsol]*kappa[jsol]*zidzdc[isol]+zz[jsol]*dkhdc[jsol][isol])/den\n                - zidzdc[jsol]*zidzdcc2[isol]/den\n                - zidzdc[isol]*zidzdcc2[jsol]/den;\n            }\n        }\n    }\n    \n    dkdJ.resize(nsol);\n    dkdc.resize(nsol, vector<double>(nsol,0));\n    \n    for (isol=0; isol<nsol; ++isol) {\n        dkdJ[isol] = zz[isol]*dkhdJ[isol]+z[isol]*kappa[isol]*zidzdJ;\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkdc[isol][jsol] = zz[isol]*dkhdc[isol][jsol]+z[isol]*kappa[isol]*zidzdc[jsol];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Current density\nvec3d FEMultiphasicFSI::CurrentDensity(FEMaterialPoint& pt)\n{\n    int i;\n    const int nsol = (int)m_pSolute.size();\n    \n    vector<vec3d> j(nsol);\n    vector<int> z(nsol);\n    vec3d Ie(0,0,0);\n    for (i=0; i<nsol; ++i) {\n        j[i] = SoluteFlux(pt, i);\n        z[i] = m_pSolute[i]->ChargeNumber();\n        Ie += j[i]*z[i];\n    }\n    Ie *= m_Fc;\n    \n    return Ie;\n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FEMultiphasicFSI::ConcentrationActual(FEMaterialPoint& pt, const int sol)\n{\n    FEMultiphasicFSIMaterialPoint& spt = *pt.ExtractData<FEMultiphasicFSIMaterialPoint>();\n    \n    // effective concentration\n    double c = spt.m_c[sol];\n    \n    // partition coefficient\n    double kappa = PartitionCoefficient(pt, sol);\n    \n    double ca = kappa*c;\n    \n    return ca;\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure\ndouble FEMultiphasicFSI::PressureActual(FEMaterialPoint& pt)\n{\n    int i;\n    \n    FEFluidMaterialPoint& fpt = *pt.ExtractData<FEFluidMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    \n    // effective pressure\n    double p = Fluid()->Pressure(pt);\n    \n    // actual concentration\n    vector<double> c(nsol);\n    for (i=0; i<nsol; ++i)\n        c[i] = ConcentrationActual(pt, i);\n    \n    // osmotic coefficient\n    double osmc = m_pOsmC->OsmoticCoefficient(pt);\n    \n    // actual pressure\n    double ca = 0;\n    for (i=0; i<nsol; ++i) ca += c[i];\n    double pa = p + m_Rgas*m_Tabs*osmc*ca;\n    \n    return pa;\n}\n\n//-----------------------------------------------------------------------------\n//! Fixed charge density in current configuration\ndouble FEMultiphasicFSI::FixedChargeDensity(FEMaterialPoint& pt)\n{\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    FEBiphasicFSIMaterialPoint& bt = *pt.ExtractData<FEBiphasicFSIMaterialPoint>();\n    FEMultiphasicFSIMaterialPoint& spt = *pt.ExtractData<FEMultiphasicFSIMaterialPoint>();\n    \n    // relative volume\n    double J = et.m_J;\n    double phi0 = bt.m_phi0;\n    double phif = Porosity(pt);\n    \n    double cFr = m_cFr(pt);\n    double cF = cFr*(1-phi0)/(J - phi0);\n    \n    return cF;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate solute molar flux\n\nvec3d FEMultiphasicFSI::SoluteFlux(FEMaterialPoint& pt, const int sol)\n{\n    FEMultiphasicFSIMaterialPoint& spt = *pt.ExtractData<FEMultiphasicFSIMaterialPoint>();\n    FEFSIMaterialPoint& fpt = *pt.ExtractData<FEFSIMaterialPoint>();\n    \n    // concentration gradient\n    vec3d gradc = spt.m_gradc[sol];\n    \n    // solute properties\n    mat3ds D = Diffusivity(pt, sol);\n    double khat = m_pSolute[sol]->m_pSolub->Solubility(pt);\n    int z = m_pSolute[sol]->ChargeNumber();\n    double zeta = ElectricPotential(pt, true);\n    double zz = pow(zeta, z);\n    double kappa = zz*khat;\n    double d0 = GetSolute(sol)->m_pDiff->Free_Diffusivity(pt);\n    \n    double c = spt.m_c[sol];\n    vec3d w = fpt.m_w;\n    double phif = Porosity(pt);\n    \n    // solute flux j\n    vec3d j = D*(-gradc*phif + w*c/d0)*kappa;\n    \n    return j;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSI::AddChemicalReaction(FEChemicalReaction* pcr)\n{\n    m_pReact.push_back(pcr);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMultiphasicFSI::GetReferentialFixedChargeDensity(const FEMaterialPoint& mp)\n{\n    const FEElasticMaterialPoint* ept = (mp.ExtractData<FEElasticMaterialPoint >());\n    const FEMultiphasicFSIMaterialPoint* mfspt = mp.ExtractData<FEMultiphasicFSIMaterialPoint>();\n    const FEBiphasicFSIMaterialPoint* bfspt = mp.ExtractData<FEBiphasicFSIMaterialPoint>();\n    double cf = (ept->m_J - bfspt->m_phi0) * mfspt->m_cF / (1 - bfspt->m_phi0);\n    return cf;\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSI.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include \"FEFluidFSI.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEMultiphasicFSI.h\"\n#include <FEBioMix/FEHydraulicPermeability.h>\n#include <FEBioMech/FEBodyForce.h>\n#include \"FEFluid.h\"\n#include <FEBioMix/FESolute.h>\n#include <FEBioMix/FESoluteInterface.h>\n#include <FEBioMix/FEOsmoticCoefficient.h>\n#include <FEBioMix/FEChemicalReaction.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! FSI material point class.\n//\nclass FEBIOFLUID_API FEMultiphasicFSIMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n    FEMultiphasicFSIMaterialPoint(FEMaterialPointData* pt);\n    \n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n    \n    //! data serialization\n    void Serialize(DumpStream& ar);\n    \n    //! Data initialization\n    void Init();\n\npublic:\n    double Osmolarity() const;\n    \npublic:\n    // Multiphasic FSI material data\n    int                 m_nsol;     //!< number of solutes\n    vector<double>      m_c;        //!< effective solute concentration\n    vector<double>      m_ca;        //!< effective solute concentration\n    vector<vec3d>       m_gradc;    //!< spatial gradient of solute concentration\n    vector<vec3d>       m_j;        //!< solute molar flux\n    vector<double>      m_cdot;     //!< material time derivative of solute concentration following fluid\n    double            m_psi;        //!< electric potential\n    vec3d            m_Ie;        //!< current density\n    double            m_pe;        //!< effective fluid pressure\n    double            m_cF;        //!< fixed charge density in current configuration\n    vector<double>    m_k;        //!< solute partition coefficient\n    vector<double>    m_dkdJ;        //!< 1st deriv of m_k with strain (J)\n    vector<double>    m_dkdJJ;    //!< 2nd deriv of m_k with strain (J)\n    vector< vector<double> >    m_dkdc;            //!< 1st deriv of m_k with effective concentration\n    vector< vector<double> >    m_dkdJc;        //!< cross deriv of m_k with J and c\n    vector< vector< vector<double> > > m_dkdcc;    // 2nd deriv of m_k with c\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for FluidFSI materials.\n\nclass FEBIOFLUID_API FEMultiphasicFSI : public FEBiphasicFSI, public FESoluteInterface_T<FEMultiphasicFSIMaterialPoint>\n{\npublic:\n    FEMultiphasicFSI(FEModel* pfem);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! performs initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    //! calculate inner stress at material point\n    mat3ds Stress(FEMaterialPoint& pt);\n    \n    //! return the diffusivity tensor as a matrix\n    void Diffusivity(double k[3][3], FEMaterialPoint& pt, int sol);\n    \n    //! return the diffusivity as a tensor\n    mat3ds Diffusivity(FEMaterialPoint& pt, int sol);\n    \n    //! return the inverse diffusivity as a tensor\n    mat3ds InvDiffusivity(FEMaterialPoint& pt, int sol);\n    \n    //! return the tangent diffusivity tensor\n    tens4dmm Diffusivity_Tangent_Strain(FEMaterialPoint& pt, int isol);\n    \n    //! return the tangent diffusivity tensor\n    mat3ds Diffusivity_Tangent_Concentration(FEMaterialPoint& pt, int isol, int jsol);\n    \n    //! return the diffusivity property\n    FESoluteDiffusivity* GetDiffusivity(int sol) { return m_pSolute[sol]->m_pDiff; }\n    \n    //! calculate solute molar flux\n    vec3d SoluteFlux(FEMaterialPoint& pt, const int sol);\n    \n    //! actual concentration (as opposed to effective concentration)\n    double ConcentrationActual(FEMaterialPoint& pt, const int sol);\n    \n    //! actual fluid pressure (as opposed to effective pressure)\n    double PressureActual(FEMaterialPoint& pt);\n    \n    //! fixed charge density\n    virtual double FixedChargeDensity(FEMaterialPoint& pt);\n    \n    //! partition coefficient\n    double PartitionCoefficient(FEMaterialPoint& pt, const int sol);\n    \n    //! partition coefficients and their derivatives\n    void PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                       vector<double>& dkdJ,\n                                       vector< vector<double> >& dkdc);\n    \n    //! electric potential\n    double ElectricPotential(FEMaterialPoint& pt, const bool eform=false);\n    \n    //! current density\n    vec3d CurrentDensity(FEMaterialPoint& pt);\n    \n    //! solute density\n    double SoluteDensity(const int sol) { return m_pSolute[sol]->Density(); }\n    \n    //! solute molar mass\n    double SoluteMolarMass(const int sol) { return m_pSolute[sol]->MolarMass(); }\n    \n    //! solute charge number\n    int SoluteChargeNumber(const int sol) { return m_pSolute[sol]->ChargeNumber(); }\n    \n    //! Add a chemical reaction\n    void AddChemicalReaction(FEChemicalReaction* pcr);\n    \n    // solute interface\npublic:\n    int Solutes() override { return (int)m_pSolute.size(); }\n    FESolute* GetSolute(int i) override { return m_pSolute[i]; }\n    double GetReferentialFixedChargeDensity(const FEMaterialPoint& mp) override;\n    FEOsmoticCoefficient* GetOsmoticCoefficient() override { return m_pOsmC;  }\n    double GetFixedChargeDensity(const FEMaterialPoint& mp) override {\n        const FEMultiphasicFSIMaterialPoint* spt = (mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        return spt->m_cF;\n    }\n\npublic: // from FEBiphasicInterface\n    double GetActualFluidPressure(const FEMaterialPoint& mp) override {\n        const FEMultiphasicFSIMaterialPoint* pt = (mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        return pt->m_pe;\n    }\n\npublic:\n    FEChemicalReaction*            GetReaction            (int i) { return m_pReact[i];  }\n    \n    int Reactions         () { return (int) m_pReact.size();    }\n    \npublic: // material parameters\n    FEParamDouble       m_cFr;      //!< fixed charge density in reference configurations TODO: gradphisr\n    vector<FEBodyForce*>    m_mbf;       //!< body forces acting on this multiphasic material solutes\n    double    m_Rgas;            //!< universal gas constant\n    double    m_Tabs;            //!< absolute temperature\n    double    m_Fc;              //!< Faraday's constant\n    bool      m_diffMtmSupp;     //!< Toggle on or off diffusive mtm supply for fluid\n    int        m_zmin;            //!< minimum charge number in mixture\n    int        m_ndeg;            //!< polynomial degree of zeta in electroneutrality\n    double              m_penalty;  //!< penalty for enforcing electroneutrality\n    \nprotected: // material properties\n    std::vector<FESolute*>  m_pSolute;      //!< pointer to solute materials\n    FEOsmoticCoefficient*        m_pOsmC;        //!< pointer to osmotic coefficient material\n    std::vector<FEChemicalReaction*>    m_pReact;        //!< pointer to chemical reactions\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEMultiphasicFSIAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEMultiphasicFSIAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFEMultiphasicFSIAnalysis::FEMultiphasicFSIAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEMultiphasicFSIAnalysis : public FEAnalysis\n{\npublic:\n\tenum FluidSoluteAnalysisType {\n\t\tSTEADY_STATE,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFEMultiphasicFSIAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSIDomain.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFEMultiphasicFSIDomain::FEMultiphasicFSIDomain(FEModel* pfem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include \"febiofluid_api.h\"\n\nclass FEModel;\nclass FELinearSystem;\nclass FEBodyForce;\nclass FEGlobalVector;\nclass FETimeInfo;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for multiphasic-FSI domains.\n\n//! A multiphasic-FSI domain is used by the multiphasic-FSI solver.\n//! This interface defines the functions that have to be implemented by a\n//! multiphasic-FSI domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOFLUID_API FEMultiphasicFSIDomain\n{\npublic:\n    FEMultiphasicFSIDomain(FEModel* pfem);\n    virtual ~FEMultiphasicFSIDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! calculate the internal forces\n    virtual void InternalForces(FEGlobalVector& R) = 0;\n    \n    //! Calculate the body force vector\n    virtual void BodyForce(FEGlobalVector& R, FEBodyForce& bf) = 0;\n    \n    //! calculate the interial forces (for dynamic problems)\n    virtual void InertialForces(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! Calculate global stiffness matrix (only contribution from internal force derivative)\n    //! \\todo maybe I should rename this the InternalStiffness matrix?\n    virtual void StiffnessMatrix   (FELinearSystem& LS) = 0;\n    \n    //! Calculate stiffness contribution of body forces\n    virtual void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) = 0;\n    \n    //! calculate the mass matrix (for dynamic problems)\n    virtual void MassMatrix(FELinearSystem& LS) = 0;\n    \n    //! transient analysis\n    void SetTransientAnalysis() { m_btrans = true; }\n    void SetSteadyStateAnalysis() { m_btrans = false; }\n    \nprotected:\n    bool        m_btrans;   // flag for transient (true) or steady-state (false) analysis\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSIDomain3D.h\"\n#include <FECore/log.h>\n#include \"FECore/DOFS.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include \"FEBioMultiphasicFSI.h\"\n#include \"FEFluidFSI.h\"\n#include \"FEBiphasicFSI.h\"\n#include <FECore/FELinearSystem.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEMultiphasicFSIDomain3D::FEMultiphasicFSIDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEMultiphasicFSIDomain(pfem), m_dofU(pfem), m_dofV(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n    m_sseps = 0;\n    \n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::DISPLACEMENT));\n        m_dofV.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::VELOCITY));\n        m_dofW.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_ACCELERATION));\n        m_dofSU.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_DISPLACEMENT));\n        m_dofR.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RIGID_ROTATION));\n        m_dofEF = pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION), 0);\n        m_dofAEF = pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION_TDERIV), 0);\n        m_dofC = pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), 0);\n        m_dofAC = pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION_TDERIV), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEMultiphasicFSIDomain3D& FEMultiphasicFSIDomain3D::operator = (FEMultiphasicFSIDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// get the total dof\nconst FEDofList& FEMultiphasicFSIDomain3D::GetDOFList() const\n{\n    return m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEMultiphasicFSIDomain3D::SetMaterial(FEMaterial* pmat)\n{\n    FEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEMultiphasicFSI*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasicFSIDomain3D::Init()\n{\n    // initialize base class\n    if (FESolidDomain::Init() == false) return false;\n    \n    //TODO Initialize body force like biphasic solver? maybe not necessary.\n    const int nsol = m_pMat->Solutes();\n    \n    for (int i = 0; i<(int)m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEBiphasicFSIMaterialPoint& pb = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n            FEMultiphasicFSIMaterialPoint& ps = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n            \n            pb.m_phi0 = m_pMat->SolidReferentialVolumeFraction(mp);\n            ps.m_cF = m_pMat->FixedChargeDensity(mp);\n        }\n    }\n    \n    // set the active degrees of freedom list\n    FEDofList dofs(GetFEModel());\n    for (int i=0; i<nsol; ++i)\n    {\n        int m = m_pMat->GetSolute(i)->GetSoluteDOF();\n        dofs.AddDof(m_dofC + m);\n    }\n    m_dof = dofs;\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::Activate()\n{\n    const int nsol = m_pMat->Solutes();\n    \n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n            }\n            node.set_active(m_dofW[0]);\n            node.set_active(m_dofW[1]);\n            node.set_active(m_dofW[2]);\n            node.set_active(m_dofEF);\n            for (int isol=0; isol<nsol; ++isol)\n                node.set_active(m_dofC + m_pMat->GetSolute(isol)->GetSoluteDOF());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::InitMaterialPoints()\n{\n    const int nsol = m_pMat->Solutes();\n    FEMesh& m = *GetMesh();\n    \n    const int NE = FEElement::MAX_NODES;\n    double ef[NE];\n    vector< vector<double> > c0(nsol, vector<double>(NE));\n    vector<int> sid(nsol);\n    for (int j = 0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n    \n    for (int j = 0; j<(int)m_Elem.size(); ++j)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[j];\n        \n        // get the number of nodes\n        int neln = el.Nodes();\n        // get initial values of fluid pressure and solute concentrations\n        for (int i = 0; i<neln; ++i)\n        {\n            FENode& ni = m.Node(el.m_node[i]);\n            ef[i] = ni.get(m_dofEF);\n            for (int isol = 0; isol<nsol; ++isol)\n                c0[isol][i] = ni.get(m_dofC + sid[isol]);\n        }\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEFluidMaterialPoint& ft = *(mp.ExtractData<FEFluidMaterialPoint>());\n            FEFSIMaterialPoint& fs = *(mp.ExtractData<FEFSIMaterialPoint>());\n            FEBiphasicFSIMaterialPoint& pt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n            FEMultiphasicFSIMaterialPoint& ps = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n            \n            // initialize effective fluid pressure, its gradient, and fluid flux\n            ft.m_ef = el.Evaluate(ef, n);\n            ft.m_gradef = gradient(el, ef, n);\n            ps.m_pe = m_pMat->Fluid()->Pressure(mp);\n            \n            // initialize solutes\n            ps.m_nsol = nsol;\n            \n            // initialize effective solute concentrations\n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_c[isol] = el.Evaluate(c0[isol], n);\n                ps.m_gradc[isol] = gradient(el, c0[isol], n);\n            }\n            \n            ps.m_psi = m_pMat->ElectricPotential(mp);\n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_ca[isol] = m_pMat->ConcentrationActual(mp, isol);\n                ps.m_j[isol] = m_pMat->SoluteFlux(mp, isol);\n            }\n            ft.m_pf = m_pMat->PressureActual(mp);\n            \n            // initialize referential solid volume fraction\n            pt.m_phi0 = m_pMat->SolidReferentialVolumeFraction(mp);\n            \n            // calculate FCD, current and stress\n            ps.m_cF = m_pMat->FixedChargeDensity(mp);\n            ps.m_Ie = m_pMat->CurrentDensity(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::Reset()\n{\n    // reset base class data\n    FESolidDomain::Reset();\n    \n    const int nsol = m_pMat->Solutes();\n    \n    // initialize all element data\n    ForEachMaterialPoint([=](FEMaterialPoint& mp) {\n        FEBiphasicFSIMaterialPoint& pt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        FEMultiphasicFSIMaterialPoint& ps = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        \n        // initialize referential solid volume fraction\n        pt.m_phi0 = m_pMat->m_phi0(mp);\n        \n        // initialize solutes\n        ps.m_nsol = nsol;\n        ps.m_c.assign(nsol,0);\n        ps.m_ca.assign(nsol,0);\n        ps.m_cdot.assign(nsol,0);\n        ps.m_gradc.assign(nsol,vec3d(0,0,0));\n        ps.m_j.assign(nsol,vec3d(0,0,0));\n        ps.m_k.assign(nsol, 0);\n        ps.m_dkdJ.assign(nsol, 0);\n        ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n    });\n    \n    for (int i=0; i<(int) m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n=0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->ResetElementData(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEMultiphasicFSIDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        if (el.isActive())\n        {\n            int neln = el.Nodes();\n            for (int i=0; i<neln; ++i)\n            {\n                x0[i] = m.Node(el.m_node[i]).m_r0;\n                xt[i] = m.Node(el.m_node[i]).m_rt;\n            }\n            \n            int n = el.GaussPoints();\n            for (int j=0; j<n; ++j)\n            {\n                r0 = el.Evaluate(x0, j);\n                rt = el.Evaluate(xt, j);\n                \n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n                \n                mp.m_r0 = r0;\n                mp.m_rt = rt;\n                et.m_Wp = et.m_Wt;\n                \n                if ((pt.m_ef <= -1) || (et.m_J <= 0)) {\n                    throw NegativeJacobianDetected();\n                }\n                \n                // reset chemical reaction element data\n                for (int j=0; j<m_pMat->Reactions(); ++j)\n                    m_pMat->GetReaction(j)->InitializeElementData(mp);\n                \n                mp.Update(timeInfo);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEMultiphasicFSIDomain3D::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    int nsol = m_pMat->Solutes();\n    int ndpn = 7+nsol;\n    lm.resize(N*(3+ndpn));\n    for (int i=0; i<N; ++i)\n    {\n        FENode& node = m_pMesh->Node(el.m_node[i]);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[ndpn*i  ] = id[m_dofU[0]];\n        lm[ndpn*i+1] = id[m_dofU[1]];\n        lm[ndpn*i+2] = id[m_dofU[2]];\n        lm[ndpn*i+3] = id[m_dofW[0]];\n        lm[ndpn*i+4] = id[m_dofW[1]];\n        lm[ndpn*i+5] = id[m_dofW[2]];\n        lm[ndpn*i+6] = id[m_dofEF];\n        for (int isol = 0; isol < nsol; ++isol)\n        {\n            //int m = m_pMat->GetSolute(i)->GetSoluteDOF();\n            lm[ndpn*i+7+isol] = id[m_dofC + m_pMat->GetSolute(isol)->GetSoluteDOF()];\n        }\n        \n        // rigid rotational dofs\n        lm[ndpn*N + 3*i  ] = id[m_dofR[0]];\n        lm[ndpn*N + 3*i+1] = id[m_dofR[1]];\n        lm[ndpn*N + 3*i+2] = id[m_dofR[2]];\n    }\n    \n    // substitute interface dofs for solid-shell interfaces\n    FESolidElement& sel = static_cast<FESolidElement&>(el);\n    for (int i = 0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(el.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the displacement dofs\n            lm[ndpn*i  ] = id[m_dofSU[0]];\n            lm[ndpn*i+1] = id[m_dofSU[1]];\n            lm[ndpn*i+2] = id[m_dofSU[2]];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n    \n    int nsol = m_pMat->Solutes();\n    int ndpn = 7+nsol;\n    \n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            // get the element force vector and initialize it to zero\n            int ndof = ndpn*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // calculate internal force vector\n            ElementInternalForce(el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEMultiphasicFSIDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    mat3ds sv, se, pi;\n    vec3d gradp, divTe;\n    mat3ds km1;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    const int nsol = m_pMat->Solutes();\n    int ndpn = 7+nsol;\n    const int nreact = m_pMat->Reactions();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFSIMaterialPoint& ft = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        FEMultiphasicFSIMaterialPoint& mt = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        double Jf =  1 + pt.m_ef;\n        \n        // calculate the jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        se = m_pMat->Solid()->Stress(mp);\n        double pa = m_pMat->PressureActual(mp);\n        // get the gradient of the elastic pressure\n        gradp = pt.m_gradef*m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        // get inverse of permeability tensor\n        km1 = m_pMat->InvPermeability(mp);\n        //get pI\n        pi = mat3dd(m_pMat->Fluid()->Pressure(mp));\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double penalty = m_pMat->m_penalty;\n        double dms = m_pMat->m_diffMtmSupp;\n        \n        // evaluate the chat\n        vector<double> chat(nsol,0);\n        double phiwhat = 0;\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += pri->m_Vbar*zhat;\n            for (int isol=0; isol<nsol; ++isol)\n            {\n                chat[isol] += zhat*pri->m_v[isol];\n            }\n        }\n        \n        vector<double> M(nsol);\n        vector<int> z(nsol);\n        vec3d je(0,0,0);\n        vector<double> d0(nsol);\n        vector<mat3ds> dm1(nsol);\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            M[isol] = m_pMat->GetSolute(isol)->MolarMass();\n            d0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            dm1[isol] = m_pMat->InvDiffusivity(mp, isol);\n            je += mt.m_j[isol]*z[isol];\n        }\n        \n        vector<double> dkdt(nsol,0);\n        for (int isol=0; isol<nsol; ++isol)\n        {\n            dkdt[isol] = mt.m_dkdJ[isol]*ft.m_Jdot;\n            for (int jsol=0; jsol<nsol; ++jsol)\n            {\n                dkdt[isol] += mt.m_dkdc[isol][jsol]*mt.m_cdot[jsol];\n            }\n        }\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jfdot wrt fluid\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n        vec3d gradphifphis = m_pMat->gradPhifPhis(mp);\n        double dJfdotf = pt.m_efdot + pt.m_gradef*ft.m_w/phif;\n        // Jsdot/Js\n        double dJsoJ = ft.m_Jdot/et.m_J;\n        double phifdot = dJsoJ*phis;\n        mat3dd I = mat3dd(1.0);\n        \n         //Old Nat BC\n        mat3ds Tmix = se - sv*phis;\n        for (int isol = 0; isol < nsol; ++isol)\n        {\n            Tmix += -mat3dd(1.0)*R*T*osmc*mt.m_ca[isol];\n        }\n        \n        \n        //New Nat BC\n        //mat3ds Tmix = -mat3dd(1.0)*pa + se - sv*phis;\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fs = (Tmix*gradN[i] + (sv*gradphifphis*phis*phis/phif - km1*ft.m_w)*H[i])*detJ; //Old Nat BC\n            //vec3d fs = (Tmix*gradN[i] + (-gradp + sv*gradphifphis*phis*phis/phif - km1*ft.m_w)*H[i])*detJ; //New Nat BC\n            vec3d ff = (sv*gradN[i] + (gradp + km1*ft.m_w - sv*gradphif/phif)*H[i])*detJ;\n            double fJ = (H[i]*(dJfdotf*phif/Jf - dJsoJ + phif*phiwhat) + gradN[i]*ft.m_w)*detJ;\n            \n            for (int isol=0; isol<nsol; ++isol)\n            {\n                fs += (-mt.m_gradc[isol]*mt.m_k[isol]*phif*R*T - (dm1[isol] - I/phif/d0[isol])*mt.m_j[isol]*R*T - ft.m_w*R*T*phis/phif*mt.m_k[isol]*mt.m_c[isol]/d0[isol])*H[i]*detJ*dms;\n                ff += (ft.m_w*R*T/phif*mt.m_k[isol]*mt.m_c[isol]/d0[isol] - mt.m_j[isol]*R*T/phif/d0[isol])*H[i]*dms*detJ;\n            }\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fs.x;\n            fe[ndpn*i+1] -= fs.y;\n            fe[ndpn*i+2] -= fs.z;\n            fe[ndpn*i+3] -= ff.x;\n            fe[ndpn*i+4] -= ff.y;\n            fe[ndpn*i+5] -= ff.z;\n            fe[ndpn*i+6] -= fJ;\n            \n            for (int isol=0; isol<nsol; ++isol)\n            {\n                double fc = ((mt.m_j[isol]+je*penalty)*gradN[i] + H[i]*(phif*chat[isol] - (ft.m_Jdot*phif*mt.m_k[isol]*mt.m_c[isol] + phifdot*et.m_J*mt.m_k[isol]*mt.m_c[isol] + dkdt[isol]*et.m_J*phif*mt.m_c[isol] + mt.m_cdot[isol]*et.m_J*phif*mt.m_k[isol])/et.m_J))*detJ;\n                fe[ndpn*i+7+isol] -= fc;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    \n    int nsol = m_pMat->Solutes();\n    int ndpn = 7+nsol;\n    for (int i=0; i<NE; ++i)\n    {\n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            vector<double> fe;\n            vector<int> lm;\n            \n            // get the element force vector and initialize it to zero\n            int ndof = ndpn*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // apply body forces\n            ElementBodyForce(BF, el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEMultiphasicFSIDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    // jacobian\n    double Ji[3][3], detJ;\n    double *H, *Gr, *Gs, *Gt;\n    double* gw = el.GaussWeights();\n    vec3d ff, f;\n    \n    int neln = el.Nodes();\n    int nsol = m_pMat->Solutes();\n    int ndpn = 7+nsol;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        FEMultiphasicFSIMaterialPoint& mt = *mp.ExtractData<FEMultiphasicFSIMaterialPoint>();\n        \n        double densTs = m_pMat->TrueSolidDensity(mp);\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double dms = m_pMat->m_diffMtmSupp;\n        double penalty = m_pMat->m_penalty;\n        vector<double> M(nsol);\n        vector<int> z(nsol);\n        vector<double> d0(nsol);\n        vector<mat3ds> D(nsol);\n        \n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            M[isol] = m_pMat->GetSolute(isol)->MolarMass();\n            d0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            D[isol] = m_pMat->Diffusivity(mp, isol);\n        }\n        \n        // evaluate spatial gradient of shape functions\n        for (int i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // get the force\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        double phif = m_pMat->Porosity(mp);\n        mat3dd I = mat3dd(1.0);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            f = BF.force(mp)*((-densTf+densTs)*phis*detJ*H[i]);\n            ff = BF.force(mp)*(densTf*detJ*H[i]);\n            \n            for (int isol = 0; isol < nsol; ++isol)\n            {\n                f += (I*phif-D[isol]/d0[isol])*BF.force(mp)*M[isol]*mt.m_k[isol]*mt.m_c[isol]*H[i]*detJ*dms;\n                ff+= D[isol]*BF.force(mp)/d0[isol]*M[isol]*mt.m_k[isol]*mt.m_c[isol]*H[i]*detJ*dms;\n            }\n            \n            fe[ndpn*i+0] -= f.x;\n            fe[ndpn*i+1] -= f.y;\n            fe[ndpn*i+2] -= f.z;\n            fe[ndpn*i+3] -= ff.x;\n            fe[ndpn*i+4] -= ff.y;\n            fe[ndpn*i+5] -= ff.z;\n            \n            for (int isol=0; isol<nsol; ++isol)\n            {\n                double fc = -gradN[i]*(D[isol]*BF.force(mp))*mt.m_k[isol]*M[isol]*mt.m_c[isol]*phif/(R*T)*detJ;\n                for(int jsol = 0; jsol<nsol; ++jsol)\n                {\n                    fc += -gradN[i]*(D[jsol]*BF.force(mp))*z[jsol]*penalty*mt.m_k[jsol]*M[jsol]*mt.m_c[jsol]*phif/(R*T)*detJ;\n                }\n                fe[ndpn*i+7+isol] -= fc;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\n//! For now, we assume that the body force is constant\nvoid FEMultiphasicFSIDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    double *H, *Gr, *Gs, *Gt;\n    double* gw = el.GaussWeights();\n    vec3d f, ff, fs, ffs, kwJ, kuJ;\n    mat3d Kwu, Kuu;\n    int nsol = m_pMat->Solutes();\n    int ndpn = 7+nsol;\n    \n    const int nreact = m_pMat->Reactions();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        FEMultiphasicFSIMaterialPoint& mt = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        // calculate the jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        double densf = m_pMat->FluidDensity(mp);\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        double denss = m_pMat->SolidDensity(mp);\n        double densTs = m_pMat->TrueSolidDensity(mp);\n        \n        // get the force\n        ff = BF.force(mp)*(densTf*detJ);\n        f = BF.force(mp)*(densf*detJ);\n        fs = BF.force(mp)*(densTs*detJ);\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        ffs = BF.force(mp)*((phis/phif*(densf+denss) - densTf*phis + denss)*detJ);\n        \n        double dens = m_pMat->Fluid()->Density(mp);\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double dms = m_pMat->m_diffMtmSupp;\n        double penalty = m_pMat->m_penalty;\n        vector<double> M(nsol);\n        vector<int> z(nsol);\n        vector<double> d0(nsol);\n        vector<vector<double>> d0p(nsol, vector<double>(nsol));\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector<vector<mat3ds>> dDdc(nsol, vector<mat3ds>(nsol));\n        \n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            M[isol] = m_pMat->GetSolute(isol)->MolarMass();\n            d0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            D[isol] = m_pMat->Diffusivity(mp, isol);\n            dDdE[isol] = m_pMat->Diffusivity_Tangent_Strain(mp, isol);\n            for (int jsol=0; jsol<nsol; ++jsol)\n            {\n                d0p[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp, jsol);\n                dDdc[isol][jsol] = m_pMat->Diffusivity_Tangent_Concentration(mp, isol, jsol);\n            }\n        }\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (int i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        for (int i=0, i7=0; i<neln; ++i, i7 += ndpn) {\n            for (int j=0, j7 = 0; j<neln; ++j, j7 += ndpn)\n            {\n                kwJ = ff*(-H[i]*H[j]/Jf);\n                kuJ = ff*(H[i]*H[j]*phis/Jf);\n                Kwu = (ff & gradN[j])*H[i];\n                Kuu = mat3d(0.0);\n                \n                for (int isol = 0; isol < nsol; ++isol)\n                {\n                    Kwu += (((D[isol]*BF.force(mp))&gradN[j])*M[isol]*mt.m_c[isol]*mt.m_dkdJ[isol]/d0[isol]*H[i]*et.m_J + (vdotTdotv(BF.force(mp), dDdE[isol], gradN[j]) + mat3dd(1.0)*((D[isol]*BF.force(mp))*gradN[j]) + D[isol]*(gradN[j] & BF.force(mp)))*M[isol]*mt.m_k[isol]*mt.m_c[isol]/d0[isol]*H[i])*detJ*dms;\n                    Kuu += ((((mat3dd(1.0)*phif-D[isol]/d0[isol])*BF.force(mp))&gradN[j])*M[isol]*mt.m_c[isol]*mt.m_dkdJ[isol]*H[i]*et.m_J + ((BF.force(mp)&gradN[j]) - mat3dd(1.0)*((D[isol]*BF.force(mp))*gradN[j])/d0[isol])*M[isol]*mt.m_c[isol]*mt.m_k[isol]*H[i] - (vdotTdotv(BF.force(mp), dDdE[isol], gradN[j]) + D[isol]*(gradN[j] & BF.force(mp)))*M[isol]*mt.m_k[isol]*mt.m_c[isol]/d0[isol]*H[i])*detJ*dms;\n                }\n                \n                ke[i7+0][j7+0] += Kuu(0,0); ke[i7+0][j7+1] += Kuu(0,1); ke[i7+0][j7+2] += Kuu(0,2);\n                ke[i7+1][j7+0] += Kuu(1,0); ke[i7+1][j7+1] += Kuu(1,1); ke[i7+1][j7+2] += Kuu(1,2);\n                ke[i7+2][j7+0] += Kuu(2,0); ke[i7+2][j7+1] += Kuu(2,1); ke[i7+2][j7+2] += Kuu(2,2);\n                ke[i7+3][j7+0] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7+0] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7+0] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                ke[i7+0][j7+6] += kuJ.x;\n                ke[i7+1][j7+6] += kuJ.y;\n                ke[i7+2][j7+6] += kuJ.z;\n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n                \n                for (int isol = 0; isol<nsol; ++isol)\n                {\n                    vec3d kwc = vec3d(0.0);\n                    vec3d kuc = vec3d(0.0);\n                    vec3d kcu = ((gradN[i]&gradN[j])*D[isol]*BF.force(mp)*M[isol]*mt.m_k[isol]*mt.m_c[isol]/R/T*phif - gradN[j]*(gradN[i]*(D[isol]*BF.force(mp)))*M[isol]*mt.m_k[isol]*mt.m_c[isol]/R/T*phis -  ((gradN[j]&BF.force(mp))*D[isol]*et.m_J*mt.m_dkdJ[isol] + mat3dd(1.0)*(D[isol]*BF.force(mp)*gradN[j])*mt.m_k[isol] + vdotTdotv(BF.force(mp), dDdE[isol], gradN[j]).transpose()*mt.m_k[isol] + (BF.force(mp)&gradN[j])*D[isol]*mt.m_k[isol]) *gradN[i]*M[isol]*mt.m_c[isol]/R/T*phif)*detJ*dms;\n                    for(int jsol=0; jsol<nsol; ++jsol)\n                    {\n                        double kcc = 0;\n                        kcu += ((gradN[i]&gradN[j])*D[jsol]*BF.force(mp)*M[jsol]*z[jsol]*penalty*mt.m_k[jsol]*mt.m_c[jsol]/R/T*phif - gradN[j]*(gradN[i]*(D[jsol]*BF.force(mp)))*M[jsol]*z[jsol]*penalty*mt.m_k[jsol]*mt.m_c[jsol]/R/T*phis - ((gradN[j]&BF.force(mp))*D[jsol]*et.m_J*mt.m_dkdJ[jsol] + mat3dd(1.0)*(D[jsol]*BF.force(mp)*gradN[j])*mt.m_k[jsol] + vdotTdotv(BF.force(mp), dDdE[jsol], gradN[j]).transpose()*mt.m_k[jsol] + (BF.force(mp)&gradN[j])*D[jsol]*mt.m_k[jsol]) *gradN[i]*M[jsol]*z[jsol]*penalty*mt.m_c[jsol]/R/T*phif)*detJ*dms;\n                        if (isol == jsol)\n                        {\n                            kwc += (D[isol]*mt.m_dkdc[isol][isol]*mt.m_c[isol]/d0[isol] + D[isol]*mt.m_k[isol]/d0[isol] - D[isol]*mt.m_k[isol]*mt.m_c[isol]*d0p[isol][isol]/d0[isol]/d0[isol] + dDdc[isol][isol]*mt.m_k[isol]*mt.m_c[isol]/d0[isol])*BF.force(mp)*M[isol]*H[i]*H[j]*detJ*dms;\n                            kuc += ((mat3dd(1.0)*phif - D[isol]/d0[isol])*BF.force(mp)*(mt.m_dkdc[isol][isol]*mt.m_c[isol] + mt.m_k[isol])*M[isol]*H[i]*H[j] + (D[isol]*d0p[isol][isol]/d0[isol]/d0[isol] - dDdc[isol][isol]/d0[isol])*BF.force(mp)*M[isol]*mt.m_k[isol]*mt.m_c[isol]*H[i]*H[j])*dms*detJ;\n                            kcc = -(D[isol]*mt.m_dkdc[isol][isol]*mt.m_c[isol] + D[isol]*mt.m_k[isol] + dDdc[isol][isol]*mt.m_k[isol]*mt.m_c[isol])*BF.force(mp)*gradN[i]*M[isol]/R/T*H[j]*phif*detJ;\n                            for (int ksol=0; ksol<nsol; ++ksol)\n                            {\n                                if(isol==ksol)\n                                {\n                                    kcc += -(D[isol]*mt.m_dkdc[isol][isol]*mt.m_c[isol] + D[isol]*mt.m_k[isol] + dDdc[isol][isol]*mt.m_k[isol]*mt.m_c[isol])*BF.force(mp)*gradN[i]*M[isol]*z[isol]*penalty/R/T*H[j]*phif*detJ;\n                                }\n                                else\n                                {\n                                    kcc += -(D[ksol]*mt.m_dkdc[ksol][isol]*mt.m_c[ksol] + dDdc[ksol][isol]*mt.m_k[ksol]*mt.m_c[ksol])*BF.force(mp)*gradN[i]*M[ksol]*z[ksol]*penalty/R/T*H[j]*phif*detJ;\n                                }\n                            }\n                        }\n                        else\n                        {\n                            kwc += (D[jsol]*mt.m_dkdc[jsol][isol]*mt.m_c[jsol]/d0[jsol] - D[jsol]*mt.m_k[jsol]*mt.m_c[jsol]*d0p[jsol][isol]/d0[jsol]/d0[jsol] + dDdc[jsol][isol]*mt.m_k[jsol]*mt.m_c[jsol]/d0[jsol])*BF.force(mp)*M[jsol]*H[i]*H[j]*detJ*dms;\n                            kuc += ((mat3dd(1.0)*phif - D[jsol]/d0[jsol])*BF.force(mp)*mt.m_dkdc[jsol][isol]*mt.m_c[jsol]*M[jsol]*H[i]*H[j] + (D[jsol]*d0p[jsol][isol]/d0[jsol]/d0[jsol] - dDdc[jsol][isol]/d0[jsol])*BF.force(mp)*M[jsol]*mt.m_k[jsol]*mt.m_c[jsol]*H[i]*H[j])*dms*detJ;\n                            kcc = -(D[isol]*mt.m_dkdc[isol][jsol]*mt.m_c[isol] + dDdc[isol][jsol]*mt.m_k[isol]*mt.m_c[isol])*BF.force(mp)*gradN[i]*M[isol]/R/T*H[j]*phif*detJ;\n                            for (int ksol=0; ksol<nsol; ++ksol)\n                            {\n                                if(jsol==ksol)\n                                {\n                                    kcc += -(D[jsol]*mt.m_dkdc[jsol][jsol]*mt.m_c[jsol] + D[jsol]*mt.m_k[jsol] + dDdc[jsol][jsol]*mt.m_k[jsol]*mt.m_c[jsol])*BF.force(mp)*gradN[i]*M[jsol]*z[jsol]*penalty/R/T*H[j]*phif*detJ;\n                                }\n                                else\n                                {\n                                    kcc += -(D[ksol]*mt.m_dkdc[ksol][jsol]*mt.m_c[ksol] + dDdc[ksol][jsol]*mt.m_k[ksol]*mt.m_c[ksol])*BF.force(mp)*gradN[i]*M[ksol]*z[ksol]*penalty/R/T*H[j]*phif*detJ;\n                                }\n                            }\n                        }\n                        ke[i7+7+isol][j7+7+jsol] += kcc;\n                    }\n                    ke[i7+0][j7+7+isol] += kuc.x;\n                    ke[i7+1][j7+7+isol] += kuc.y;\n                    ke[i7+2][j7+7+isol] += kuc.z;\n                    ke[i7+3][j7+7+isol] += kwc.x;\n                    ke[i7+4][j7+7+isol] += kwc.y;\n                    ke[i7+5][j7+7+isol] += kwc.z;\n                    ke[i7+7+isol][j7+0] += kcu.x;\n                    ke[i7+7+isol][j7+1] += kcu.y;\n                    ke[i7+7+isol][j7+2] += kcu.z;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEMultiphasicFSIDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 7 + nsol;\n    \n    const int nreact = m_pMat->Reactions();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    vector<mat3d> gradgradN(neln);\n    \n    double dt = tp.timeIncrement;\n    double a = tp.gamma/(tp.beta*dt);\n    double c = tp.alpham/(tp.alphaf*tp.gamma*dt);\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    double *H, *Gr, *Gs, *Gt;\n    vec3d g[3], dg[3][3];\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        ContraBaseVectors(el, n, g, tp.alphaf);\n        ContraBaseVectorDerivatives(el, n, dg, tp.alphaf);\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // get the shape function derivatives\n        double* Grr = el.Grr(n); double* Grs = el.Grs(n); double* Grt = el.Grt(n);\n        double* Gsr = el.Gsr(n); double* Gss = el.Gss(n); double* Gst = el.Gst(n);\n        double* Gtr = el.Gtr(n); double* Gts = el.Gts(n); double* Gtt = el.Gtt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFSIMaterialPoint& fpt = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        FEMultiphasicFSIMaterialPoint& mt = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        // get the tangents\n        mat3ds se = m_pMat->Solid()->Stress(mp);\n        tens4ds cs = m_pMat->Solid()->Tangent(mp);\n        mat3ds sv = m_pMat->Fluid()->GetViscous()->Stress(mp);\n        mat3ds svJ = m_pMat->Fluid()->GetViscous()->Tangent_Strain(mp);\n        tens4ds cv = m_pMat->Fluid()->Tangent_RateOfDeformation(mp);\n        double pa = m_pMat->PressureActual(mp);\n        double dp = m_pMat->Fluid()->Tangent_Pressure_Strain(mp);\n        double d2p = m_pMat->Fluid()->Tangent_Pressure_Strain_Strain(mp);\n        vec3d gradp = pt.m_gradef*dp;\n        // Jsdot/Js = div(vs)\n        double dJsoJ = fpt.m_Jdot/et.m_J;\n        mat3ds km1 = m_pMat->InvPermeability(mp);\n        \n        //Include dependence of permeability on displacement\n        tens4dmm K = m_pMat->Permeability_Tangent(mp);\n        \n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n        vec3d gradphifphis = m_pMat->gradPhifPhis(mp);\n        \n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double dms = m_pMat->m_diffMtmSupp;\n        double penalty = m_pMat->m_penalty;\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodJ = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Strain(mp);\n        vector<double> MM(nsol);\n        vector<int> z(nsol);\n        vector<double> d0(nsol);\n        vector<vector<double>> d0p(nsol, vector<double>(nsol));\n        vector<mat3ds> D(nsol);\n        vector<mat3ds> Dm1(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector<vector<mat3ds>> dDdc(nsol, vector<mat3ds>(nsol));\n        vector<tens4d> Dm1Dm1(nsol);\n        vector<tens4d> dDdEfull(nsol);\n        vector<vec3d> flux(nsol);\n        vector<double> dodc(nsol);\n        \n        for (int isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            MM[isol] = m_pMat->GetSolute(isol)->MolarMass();\n            d0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            D[isol] = m_pMat->Diffusivity(mp, isol);\n            Dm1[isol] = m_pMat->InvDiffusivity(mp, isol);\n            Dm1Dm1[isol] = dyad2(Dm1[isol],Dm1[isol]);\n            dDdE[isol] = m_pMat->Diffusivity_Tangent_Strain(mp, isol);\n            dDdEfull[isol] = tens4d(dDdE[isol]);\n            flux[isol] = -mt.m_gradc[isol]*phif + fpt.m_w*mt.m_c[isol]/d0[isol];\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp, isol);\n            for (int jsol=0; jsol<nsol; ++jsol)\n            {\n                d0p[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp, jsol);\n                dDdc[isol][jsol] = m_pMat->Diffusivity_Tangent_Concentration(mp, isol, jsol);\n            }\n        }\n        \n        vector<double> dkdt(nsol,0);\n        for (int isol=0; isol<nsol; ++isol)\n        {\n            dkdt[isol] = mt.m_dkdJ[isol]*fpt.m_Jdot;\n            for (int jsol=0; jsol<nsol; ++jsol)\n            {\n                dkdt[isol] += mt.m_dkdc[isol][jsol]*mt.m_cdot[jsol];\n            }\n        }\n        \n        // evaluate the chat\n        double vbarzeta = 0.0;\n        vector<double> vzeta(nsol,0.0);\n        mat3ds vbardzdE = mat3ds(0.0);\n        vector<mat3ds> vdzdE(nsol,mat3ds(0.0));\n        vector<double> vbardzdc(nsol,0.0);\n        vector<vector<double>> vdzdc(nsol,vector<double>(nsol, 0.0));\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            double vbar = m_pMat->GetReaction(i)->m_Vbar;\n            mat3ds dzdE = m_pMat->GetReaction(i)->Tangent_ReactionSupply_Strain(mp);\n            double zeta = m_pMat->GetReaction(i)->ReactionSupply(mp);\n            vbarzeta += vbar*zeta;\n            vbardzdE += dzdE*vbar;\n            for (int isol = 0; isol < nsol; ++isol)\n            {\n                double v = m_pMat->GetReaction(i)->m_v[isol];\n                double dzdc1 = m_pMat->GetReaction(i)->Tangent_ReactionSupply_Concentration(mp,isol);\n                vzeta[isol] += v*zeta;\n                vdzdE[isol] += dzdE*v;\n                vbardzdc[isol] += vbar*dzdc1;\n                for (int jsol = 0; jsol<nsol; ++jsol)\n                {\n                    double dzdc2 = m_pMat->GetReaction(i)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                    vdzdc[isol][jsol] += v*dzdc2;\n                }\n            }\n        }\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate spatial gradgrad of shape functions\n        for (i=0; i<neln; ++i) {\n            gradgradN[i] = (((dg[0][0] & g[0]) + (dg[0][1] & g[1]) + (dg[0][2] & g[2]))*Gr[i]\n                            + ((dg[1][0] & g[0]) + (dg[1][1] & g[1]) + (dg[1][2] & g[2]))*Gs[i]\n                            + ((dg[2][0] & g[0]) + (dg[2][1] & g[1]) + (dg[2][2] & g[2]))*Gt[i]\n                            + (g[0] & g[0])*Grr[i] + (g[0] & g[1])*Gsr[i] + (g[0] & g[2])*Gtr[i]\n                            + (g[1] & g[0])*Grs[i] + (g[1] & g[1])*Gss[i] + (g[1] & g[2] )*Gts[i]\n                            + (g[2] & g[0])*Grt[i] + (g[2] & g[1])*Gst[i] + (g[2] & g[2])*Gtt[i]);\n        }\n        \n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += ndpn)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += ndpn)\n            {\n                mat3d M = mat3dd(a*dtrans) - et.m_L;\n                tens4d km1km1 = dyad2(km1,km1);\n                tens4d Kfull = tens4d(K);\n                \n                mat3d Kuu = (sv*((gradN[i]&gradN[j])*phis/phif + (gradN[j]&gradN[i]))*phis - vdotTdotv(gradN[i], cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M)*phis - vdotTdotv(gradN[i], cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif))*phis/(phif*phif) + vdotTdotv(gradN[i], cv, fpt.m_w) * ((-gradphif&gradN[j]) + gradgradN[j]*phis)*phis/(phif*phif) + mat3dd((se*gradN[i])*gradN[j]) + vdotTdotv(gradN[i], cs, gradN[j]) -  sv*((gradphifphis&gradN[j])*2.0*phis*phis/phif + (gradN[j]&gradphifphis)*phis - gradgradN[j])*H[i]*phis/phif + vdotTdotv(gradphifphis, cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M)*phis*phis/phif*H[i] + vdotTdotv(gradphifphis, cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif))*phis*phis/(phif*phif*phif)*H[i] - vdotTdotv(gradphifphis, cv, fpt.m_w) * ((-gradphif&gradN[j]) + gradgradN[j]*phis)*phis*phis/(phif*phif*phif)*H[i] + (-((km1*fpt.m_w)&gradN[j])*2.0 + (gradN[j]&(km1*fpt.m_w)) + km1*(gradN[j]*fpt.m_w) + ddot(ddot(km1km1,Kfull),mat3dd(gradN[j]*fpt.m_w)))*H[i])*detJ; //Old Nat BC\n                \n                //mat3d Kuu = (sv*((gradN[i]&gradN[j])*phis/phif + (gradN[j]&gradN[i]))*phis - vdotTdotv(gradN[i], cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M)*phis - vdotTdotv(gradN[i], cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif))*phis/(phif*phif) + vdotTdotv(gradN[i], cv, fpt.m_w) * ((-gradphif&gradN[j]) + gradgradN[j]*phis)*phis/(phif*phif) + mat3dd((se*gradN[i])*gradN[j]) + vdotTdotv(gradN[i], cs, gradN[j]) -  sv*((gradphifphis&gradN[j])*2.0*phis*phis/phif + (gradN[j]&gradphifphis)*phis - gradgradN[j])*H[i]*phis/phif + vdotTdotv(gradphifphis, cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M)*phis*phis/phif*H[i] + vdotTdotv(gradphifphis, cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif))*phis*phis/(phif*phif*phif)*H[i] - vdotTdotv(gradphifphis, cv, fpt.m_w) * ((-gradphif&gradN[j]) + gradgradN[j]*phis)*phis*phis/(phif*phif*phif)*H[i] + (-((km1*fpt.m_w)&gradN[j])*2.0 + (gradN[j]&(km1*fpt.m_w)) + km1*(gradN[j]*fpt.m_w) + ddot(ddot(km1km1,Kfull),mat3dd(gradN[j]*fpt.m_w)))*H[i] - ((gradp&gradN[j])-(gradN[j]&gradp))*H[i] - (gradN[i]&gradN[j])*pa + (gradN[j]&gradN[i])*pa)*detJ; //New Nat BC\n                \n                mat3d Kuw = (vdotTdotv(gradN[i], cv, (gradphif*H[j]/phif-gradN[j]))*phis/phif + vdotTdotv(gradphifphis, cv, (-gradphif*H[j]/phif + gradN[j]))*H[i]*phis*phis/(phif*phif) - km1*H[i]*H[j])*detJ;\n                \n                vec3d kuJ = ((-svJ*gradN[i])*H[j]*phis + svJ*gradphifphis*H[j]*H[i]*phis*phis/phif)*detJ; //Old Nat BC\n                //vec3d kuJ = (-(mat3dd(1.0)*dp+svJ*phis)*gradN[i]*H[j] + ((-pt.m_gradef*d2p + svJ*gradphifphis*phis*phis/phif)*H[j] - gradN[j]*dp)*H[i])*detJ; //New Nat BC\n                \n                mat3d Kwu = (((gradp&gradN[j])-(gradN[j]&gradp))*H[i] + sv*((gradphif&gradN[j])*(2*phis/phif)+(gradN[j]&gradphif) - gradgradN[j]*phis)*(H[i]/phif) - vdotTdotv(gradphif, cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M)*H[i]/phif - vdotTdotv(gradphif, cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif))*H[i]/(phif*phif*phif) + vdotTdotv(gradphif, cv, fpt.m_w) * (-(gradphif&gradN[j]) + gradgradN[j]*phis)*H[i]/(phif*phif*phif) + sv*((gradN[i]&gradN[j])*(1-phis/phif)-(gradN[j]&gradN[i])) + vdotTdotv(gradN[i], cv, gradN[j])*(-bpt.m_Lw.sym()*phis/(phif*phif) + M) + vdotTdotv(gradN[i], cv, fpt.m_w) * ((gradphif&gradN[j])*2.0*phis/phif + (gradN[j]&gradphif))/(phif*phif) + vdotTdotv(gradN[i], cv, fpt.m_w) * ((gradphif&gradN[j]) - gradgradN[j]*phis)/(phif*phif) + (((km1*fpt.m_w)&gradN[j])*2.0 - (gradN[j]&(km1*fpt.m_w)) - km1*(gradN[j]*fpt.m_w) - ddot(ddot(km1km1,Kfull),mat3dd(gradN[j]*fpt.m_w)))*H[i])*detJ;\n                \n                mat3d Kww = ((vdotTdotv(gradphif, cv, (gradphif*H[j]/phif-gradN[j]))/(phif*phif) + km1*H[j])*H[i] + vdotTdotv(gradN[i], cv, (-gradphif*H[j]/phif+gradN[j]))/phif)*detJ;\n                \n                vec3d kwJ = ((svJ*gradN[i])*H[j] +(gradN[j]*dp+(pt.m_gradef*d2p - svJ*gradphif/phif)*H[j])*H[i])*detJ;\n                \n                vec3d kJu = (((gradN[j]&fpt.m_w) - mat3dd(gradN[j]*fpt.m_w)) * gradN[i] + ((gradN[j]*pt.m_efdot + ((gradN[j]&fpt.m_w) - mat3dd(gradN[j]*fpt.m_w))*pt.m_gradef)/Jf - gradN[j]*(dJsoJ + a*dtrans) + et.m_L.transpose()*gradN[j]*dtrans)*H[i] + (mat3dd(1.0)*vbarzeta + et.m_F*vbardzdE*et.m_F.transpose()*phif)*gradN[j]*H[i])*detJ;\n                \n                vec3d kJw = ((pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j])*detJ;\n                \n                double kJJ = ((c*phif*dtrans - (pt.m_efdot*phif + pt.m_gradef*fpt.m_w)/Jf)*H[j] + gradN[j]*fpt.m_w)*H[i]/Jf*detJ;\n                \n                for (int isol = 0; isol < nsol; ++isol)\n                {\n                    Kuu += (-(gradN[i]&gradN[j])*(osmc + et.m_J*dodJ)*R*T*mt.m_k[isol]*mt.m_c[isol] + (-(gradN[i]&gradN[j])*et.m_J*mt.m_dkdJ[isol] + ((gradN[j]&gradN[i]))*mt.m_k[isol])*mt.m_c[isol]*R*T*osmc - (mt.m_gradc[isol]&gradN[j])*mt.m_k[isol]*R*T*H[i] + (-(mt.m_gradc[isol]&gradN[j])*et.m_J*mt.m_dkdJ[isol] + (gradN[j]&mt.m_gradc[isol])*mt.m_k[isol])*H[i]*phif*R*T + (-((Dm1[isol]*mt.m_j[isol])&gradN[j])*2.0 + (gradN[j]&(Dm1[isol]*mt.m_j[isol])) + Dm1[isol]*(gradN[j]*mt.m_j[isol]) + ddot(ddot(Dm1Dm1[isol],dDdEfull[isol]),mat3dd(gradN[j]*mt.m_j[isol])))*H[i]*R*T + Dm1[isol]*(mat3dd((D[isol]*flux[isol])*gradN[j]) - D[isol]*(flux[isol]&gradN[j]) - dDdEfull[isol].dot(mat3dd(gradN[j]*flux[isol])) - D[isol]*(gradN[j]&flux[isol]))*mt.m_k[isol]*H[i]*R*T - (flux[isol]&gradN[j])*et.m_J*mt.m_dkdJ[isol]*H[i]*R*T - (-(mt.m_gradc[isol]&gradN[j])*phis + (gradN[j]&mt.m_gradc[isol])*phif)*mt.m_k[isol]*H[i]*R*T + (mt.m_j[isol]&gradN[j])*H[i]*(1/phif - phis/(phif*phif))*R*T/d0[isol] + (mat3dd((D[isol]*flux[isol])*gradN[j]) - D[isol]*(flux[isol]&gradN[j]) + dDdEfull[isol].dot(mat3dd(gradN[j]*flux[isol])) + D[isol]*(gradN[j]&flux[isol]))*H[i]*R*T/phif/d0[isol]*mt.m_k[isol] + D[isol]*(flux[isol]&gradN[j])*H[i]*R*T/phif*et.m_J*mt.m_dkdJ[isol]/d0[isol] + D[isol]*(-(mt.m_gradc[isol]&gradN[j])*phis + (gradN[j]&mt.m_gradc[isol])*phif)*H[i]*R*T/phif*mt.m_k[isol]/d0[isol] + (fpt.m_w&gradN[j])*H[i]*(phis/phif*mt.m_k[isol] - et.m_J*mt.m_dkdJ[isol])*phis/phif*R*T*mt.m_c[isol]/d0[isol])*dms*detJ; //Old Nat BC\n                    //Kuu += (-(gradN[i]&gradN[j])*et.m_J*dodJ*R*T*mt.m_k[isol]*mt.m_c[isol] -(gradN[i]&gradN[j])*et.m_J*mt.m_dkdJ[isol]*mt.m_c[isol]*R*T*osmc - (mt.m_gradc[isol]&gradN[j])*mt.m_k[isol]*R*T*H[i] + (-(mt.m_gradc[isol]&gradN[j])*et.m_J*mt.m_dkdJ[isol] + (gradN[j]&mt.m_gradc[isol])*mt.m_k[isol])*H[i]*phif*R*T + (-((Dm1[isol]*mt.m_j[isol])&gradN[j])*2.0 + (gradN[j]&(Dm1[isol]*mt.m_j[isol])) + Dm1[isol]*(gradN[j]*mt.m_j[isol]) + ddot(ddot(Dm1Dm1[isol],dDdEfull[isol]),mat3dd(gradN[j]*mt.m_j[isol])))*H[i]*R*T + Dm1[isol]*(mat3dd((D[isol]*flux[isol])*gradN[j]) - D[isol]*(flux[isol]&gradN[j]) - dDdEfull[isol].dot(mat3dd(gradN[j]*flux[isol])) - D[isol]*(gradN[j]&flux[isol]))*mt.m_k[isol]*H[i]*R*T - (flux[isol]&gradN[j])*et.m_J*mt.m_dkdJ[isol]*H[i]*R*T - (-(mt.m_gradc[isol]&gradN[j])*phis + (gradN[j]&mt.m_gradc[isol])*phif)*mt.m_k[isol]*H[i]*R*T + (mt.m_j[isol]&gradN[j])*H[i]*(1/phif - phis/(phif*phif))*R*T/d0[isol] + (mat3dd((D[isol]*flux[isol])*gradN[j]) - D[isol]*(flux[isol]&gradN[j]) + dDdEfull[isol].dot(mat3dd(gradN[j]*flux[isol])) + D[isol]*(gradN[j]&flux[isol]))*H[i]*R*T/phif/d0[isol]*mt.m_k[isol] + D[isol]*(flux[isol]&gradN[j])*H[i]*R*T/phif*et.m_J*mt.m_dkdJ[isol]/d0[isol] + D[isol]*(-(mt.m_gradc[isol]&gradN[j])*phis + (gradN[j]&mt.m_gradc[isol])*phif)*H[i]*R*T/phif*mt.m_k[isol]/d0[isol] + (fpt.m_w&gradN[j])*H[i]*(phis/phif*mt.m_k[isol] - et.m_J*mt.m_dkdJ[isol])*phis/phif*R*T*mt.m_c[isol]/d0[isol])*dms*detJ; //New Nat BC\n                    \n                    Kwu += ((fpt.m_w&gradN[j])*H[i]*(1.0/phif-phis/(phif*phif))*R*T*mt.m_k[isol]*mt.m_c[isol]/d0[isol] + (fpt.m_w&gradN[j])*H[i]/phif*et.m_J*R*T*mt.m_dkdJ[isol]*mt.m_c[isol]/d0[isol] - (mt.m_j[isol]&gradN[j])*H[i]*(1.0/phif-phis/(phif*phif))*R*T/d0[isol] - (mat3dd((D[isol]*flux[isol])*gradN[j]) - D[isol]*(flux[isol]&gradN[j]) + dDdEfull[isol].dot(mat3dd(flux[isol]*gradN[j])) + D[isol]*(gradN[j]&flux[isol]) + D[isol]*(-(mt.m_gradc[isol]&gradN[j])*phis + (gradN[j]&mt.m_gradc[isol])*phif))*H[i]*R*T/phif/d0[isol]*mt.m_k[isol] - D[isol]*(flux[isol]&gradN[j])*et.m_J*mt.m_dkdJ[isol]*H[i]*R*T/phif/d0[isol])*detJ*dms;\n                    \n                    Kww += (mat3dd(1.0) - D[isol]/d0[isol])*mt.m_k[isol]*mt.m_c[isol]/d0[isol]*R*T/phif*H[i]*H[j]*dms*detJ;\n                    \n                    Kuw += -((mat3dd(1.0) - D[isol]/d0[isol]/phif)*mt.m_k[isol]*mt.m_c[isol]/d0[isol]*R*T*H[i]*H[j] + mat3dd(1.0)*mt.m_k[isol]*mt.m_c[isol]/d0[isol]*R*T*phis/phif*H[i]*H[j])*dms*detJ;\n                }\n                \n                ke[i7  ][j7  ] += Kuu(0,0); ke[i7  ][j7+1] += Kuu(0,1); ke[i7  ][j7+2] += Kuu(0,2);\n                ke[i7+1][j7  ] += Kuu(1,0); ke[i7+1][j7+1] += Kuu(1,1); ke[i7+1][j7+2] += Kuu(1,2);\n                ke[i7+2][j7  ] += Kuu(2,0); ke[i7+2][j7+1] += Kuu(2,1); ke[i7+2][j7+2] += Kuu(2,2);\n                \n                ke[i7  ][j7+3] += Kuw(0,0); ke[i7  ][j7+4] += Kuw(0,1); ke[i7  ][j7+5] += Kuw(0,2);\n                ke[i7+1][j7+3] += Kuw(1,0); ke[i7+1][j7+4] += Kuw(1,1); ke[i7+1][j7+5] += Kuw(1,2);\n                ke[i7+2][j7+3] += Kuw(2,0); ke[i7+2][j7+4] += Kuw(2,1); ke[i7+2][j7+5] += Kuw(2,2);\n                \n                ke[i7+3][j7  ] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7  ] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7  ] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                \n                ke[i7+3][j7+3] += Kww(0,0); ke[i7+3][j7+4] += Kww(0,1); ke[i7+3][j7+5] += Kww(0,2);\n                ke[i7+4][j7+3] += Kww(1,0); ke[i7+4][j7+4] += Kww(1,1); ke[i7+4][j7+5] += Kww(1,2);\n                ke[i7+5][j7+3] += Kww(2,0); ke[i7+5][j7+4] += Kww(2,1); ke[i7+5][j7+5] += Kww(2,2);\n                \n                ke[i7+0][j7+6] += kuJ.x;\n                ke[i7+1][j7+6] += kuJ.y;\n                ke[i7+2][j7+6] += kuJ.z;\n                \n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n                \n                ke[i7+6][j7  ] += kJu.x;\n                ke[i7+6][j7+1] += kJu.y;\n                ke[i7+6][j7+2] += kJu.z;\n                \n                ke[i7+6][j7+3] += kJw.x;\n                ke[i7+6][j7+4] += kJw.y;\n                ke[i7+6][j7+5] += kJw.z;\n                \n                ke[i7+6][j7+6] += kJJ;\n                \n                for (int isol=0; isol<nsol; ++isol)\n                {\n                    vec3d kcw = D[isol]*gradN[i]/d0[isol]*mt.m_k[isol]*mt.m_c[isol]*H[j]*detJ;\n                    \n                    vec3d kcu = (((gradN[j]&mt.m_j[isol]) - mat3dd(mt.m_j[isol]*gradN[j]))*gradN[i] + (mat3dd((D[isol]*flux[isol])*gradN[j]) - (gradN[j]&(D[isol]*flux[isol])) + (dDdEfull[isol].dot(mat3dd(gradN[j]*flux[isol]))).transpose() + (flux[isol]&gradN[j])*D[isol])*gradN[i]*mt.m_k[isol] + gradN[j]*((D[isol]*flux[isol])*gradN[i])*et.m_J*mt.m_dkdJ[isol] + (-(gradN[j]&mt.m_gradc[isol])*phis + (mt.m_gradc[isol]&gradN[j])*phif)*D[isol]*gradN[i]*mt.m_k[isol] + (mat3dd(vzeta[isol]) + et.m_F*vdzdE[isol]*et.m_F.transpose()*phif)*gradN[j]*H[i] - (mat3dd(2*mt.m_dkdJ[isol]*fpt.m_Jdot*mt.m_c[isol]) + (mat3dd(dJsoJ + a*dtrans) - et.m_L.transpose())*mt.m_k[isol]*mt.m_c[isol])*gradN[j]*H[i] - (mat3dd(dJsoJ + a*dtrans) - et.m_L.transpose())*gradN[j]*H[i]*et.m_J*phif*mt.m_c[isol]*mt.m_dkdJ[isol] - gradN[j]*H[i]*(mt.m_k[isol] + et.m_J*phif*mt.m_dkdJ[isol])*mt.m_cdot[isol])*detJ;\n                    \n                    vec3d kwc = vec3d(0);\n                    \n                    vec3d kuc = vec3d(0);\n                    \n                    double kJc = H[i]*H[j]*phif*vbardzdc[isol]*mt.m_k[isol]*detJ;\n                    \n                    for(int jsol=0; jsol<nsol; ++jsol)\n                    {\n                        kcw += D[jsol]*gradN[i]/d0[jsol]*mt.m_k[jsol]*mt.m_c[jsol]*z[jsol]*H[j]*detJ;\n                        \n                        kcu += (((gradN[j]&mt.m_j[jsol])*z[jsol]*penalty - mat3dd(mt.m_j[isol]*gradN[j]*z[jsol]*penalty))*gradN[i] + (mat3dd((D[jsol]*flux[jsol])*gradN[j]) - (gradN[j]&(D[jsol]*flux[jsol])) + (dDdEfull[jsol].dot(mat3dd(gradN[j]*flux[jsol]))).transpose() + (flux[jsol]&gradN[j])*D[jsol])*gradN[i]*mt.m_k[jsol]*z[jsol]*penalty + gradN[j]*((D[jsol]*flux[jsol])*gradN[i])*et.m_J*mt.m_dkdJ[jsol]*z[jsol]*penalty + (-(gradN[j]&mt.m_gradc[jsol])*phis + (mt.m_gradc[jsol]&gradN[j])*phif)*D[jsol]*gradN[i]*mt.m_k[jsol]*z[jsol]*penalty + mat3dd(vdzdc[isol][jsol]*mt.m_dkdJ[jsol]*mt.m_c[jsol])*gradN[j]*phif*et.m_J*H[i] - gradN[j]*H[i]*mt.m_dkdc[isol][jsol]*mt.m_c[isol]*mt.m_cdot[jsol])*detJ;\n                        \n                        kJc += H[i]*H[j]*phif*vbardzdc[isol]*mt.m_dkdc[jsol][isol]*mt.m_c[jsol]*detJ;\n                        \n                        double kcc = 0;\n                        \n                        if (isol == jsol)\n                        {\n                            kwc += (fpt.m_w*H[i]*H[j]*R*T/phif*(mt.m_dkdc[isol][isol]*mt.m_c[isol]/d0[isol] + mt.m_k[isol]/d0[isol] - mt.m_k[isol]*mt.m_c[isol]*d0p[isol][isol]/(d0[isol]*d0[isol])) + mt.m_j[isol]*H[i]*H[j]*R*T/phif*d0p[isol][isol]/(d0[isol]*d0[isol]) - (dDdc[isol][isol]*mt.m_k[isol] + D[isol]*mt.m_dkdc[isol][isol])*flux[isol]*H[i]*H[j]*R*T/phif/d0[isol] - D[isol]*(-gradN[j]*phif + fpt.m_w*(1.0/d0[isol] - mt.m_c[isol]*d0p[isol][isol]/(d0[isol]*d0[isol]))*H[j])*H[i]*R*T/phif/d0[isol]*mt.m_k[isol])*detJ*dms;\n                            \n                            kuc += (-gradN[i]*H[j]*R*T*(dodc[isol]*mt.m_k[isol]*mt.m_c[isol] + osmc*mt.m_dkdc[isol][isol]*mt.m_c[isol] + osmc*mt.m_k[isol]) - (mt.m_gradc[isol]*mt.m_dkdc[isol][isol]*H[j] + gradN[j]*mt.m_k[isol])*H[i]*R*T*phif + (Dm1Dm1[isol].dot(dDdc[isol][isol]) - mat3dd(d0p[isol][isol]/phif/d0[isol]/d0[isol]))*mt.m_j[isol]*H[i]*H[j]*R*T - (Dm1[isol] - mat3dd(1.0/phif/d0[isol]))*(dDdc[isol][isol]*mt.m_k[isol] + D[isol]*mt.m_dkdc[isol][isol])*flux[isol]*H[i]*H[j]*R*T - (mat3dd(1.0) - D[isol]/d0[isol]/phif)*(-gradN[j]*phif + fpt.m_w*H[j]*(1.0/d0[isol] - mt.m_c[isol]*d0p[isol][isol]/(d0[isol]*d0[isol])))*H[i]*R*T*mt.m_k[isol] - fpt.m_w*H[i]*H[j]*R*T*phis/phif*(mt.m_dkdc[isol][isol]*mt.m_c[isol]/d0[isol] + mt.m_k[isol]/d0[isol] - mt.m_k[isol]*mt.m_c[isol]*d0p[isol][isol]/(d0[isol]*d0[isol])))*detJ*dms;\n                            \n                            kcc += (((dDdc[isol][isol]*mt.m_k[isol] + D[isol]*mt.m_dkdc[isol][isol])*flux[isol])*gradN[i]*H[j] + (D[isol]*(-gradN[j]*phif + fpt.m_w*H[j]*(1.0/d0[isol] - mt.m_c[isol]*d0p[isol][isol]/(d0[isol]*d0[isol]))))*gradN[i]*mt.m_k[isol] + H[i]*H[j]*phif*vdzdc[isol][isol]*mt.m_k[isol] - H[i]*H[j]*dJsoJ*(mt.m_dkdc[isol][isol]*mt.m_c[isol] + mt.m_k[isol]) - H[i]*H[j]*phif*mt.m_dkdJ[isol]*fpt.m_Jdot - H[i]*H[j]*phif*mt.m_c[isol]*mt.m_dkdc[isol][isol]*c*dtrans - H[i]*H[j]*phif*(mt.m_dkdc[isol][isol]*mt.m_cdot[isol] + mt.m_k[isol]*c*dtrans))*detJ;\n                            \n                            for (int ksol=0; ksol<nsol; ++ksol)\n                            {\n                                if(isol==ksol)\n                                {\n                                    kcc += (((dDdc[isol][isol]*mt.m_k[isol] + D[isol]*mt.m_dkdc[isol][isol])*flux[isol])*gradN[i]*H[j]*z[isol]*penalty + (D[isol]*(-gradN[j]*phif + fpt.m_w*H[j]*(1.0/d0[isol] - mt.m_c[isol]*d0p[isol][isol]/(d0[isol]*d0[isol]))))*gradN[i]*mt.m_k[isol]*z[isol]*penalty + H[i]*H[j]*phif*vdzdc[isol][isol]*mt.m_dkdc[isol][isol]*mt.m_c[isol] - H[i]*H[j]*phif*mt.m_dkdc[isol][isol]*mt.m_cdot[isol])*detJ;\n                                }\n                                else\n                                {\n                                    kcc += (((dDdc[ksol][isol]*mt.m_k[ksol] + D[ksol]*mt.m_dkdc[ksol][isol])*flux[ksol])*gradN[i]*H[j]*z[ksol]*penalty - (D[ksol]*fpt.m_w*H[j]*mt.m_c[ksol]*d0p[ksol][isol]/(d0[ksol]*d0[ksol]))*gradN[i]*mt.m_k[ksol]*z[ksol]*penalty + H[i]*H[j]*phif*vdzdc[isol][isol]*mt.m_dkdc[ksol][isol]*mt.m_c[ksol] - H[i]*H[j]*phif*mt.m_dkdc[isol][ksol]*mt.m_cdot[ksol])*detJ;\n                                }\n                            }\n                        }\n                        else\n                        {\n                            kwc += (fpt.m_w*H[i]*H[j]*R*T/phif*(mt.m_dkdc[jsol][isol]*mt.m_c[jsol]/d0[jsol] - mt.m_k[jsol]*mt.m_c[jsol]*d0p[jsol][isol]/(d0[jsol]*d0[jsol])) + mt.m_j[jsol]*H[i]*H[j]*R*T/phif*d0p[jsol][isol]/(d0[jsol]*d0[jsol]) - (dDdc[jsol][isol]*mt.m_k[jsol] + D[jsol]*mt.m_dkdc[jsol][isol])*flux[jsol]*H[i]*H[j]*R*T/phif/d0[jsol] + D[jsol]*fpt.m_w*mt.m_k[jsol]*mt.m_c[jsol]*d0p[jsol][isol]/(d0[jsol]*d0[jsol]*d0[jsol])*H[j]*H[i]*R*T/phif)*detJ*dms;\n                            \n                            kuc += (-gradN[i]*H[j]*R*T*(dodc[isol]*mt.m_k[jsol]*mt.m_c[jsol] + osmc*mt.m_dkdc[jsol][isol]*mt.m_c[jsol]) - mt.m_gradc[jsol]*mt.m_dkdc[jsol][isol]*H[i]*H[j]*R*T*phif + (Dm1Dm1[jsol].dot(dDdc[jsol][isol]) - mat3dd(d0p[jsol][isol]/phif/d0[jsol]/d0[jsol]))*mt.m_j[jsol]*H[i]*H[j]*R*T - (Dm1[jsol] - mat3dd(1.0/phif/d0[jsol]))*(dDdc[jsol][isol]*mt.m_k[jsol] + D[jsol]*mt.m_dkdc[jsol][isol])*flux[jsol]*H[i]*H[j]*R*T + (mat3dd(1.0) - D[jsol]/d0[jsol]/phif)*fpt.m_w*H[j]*mt.m_c[jsol]*d0p[jsol][isol]/(d0[jsol]*d0[jsol])*H[i]*R*T*mt.m_k[jsol] - fpt.m_w*H[i]*H[j]*R*T*phis/phif*(mt.m_dkdc[jsol][isol]*mt.m_c[jsol]/d0[jsol] - mt.m_k[jsol]*mt.m_c[jsol]*d0p[jsol][isol]/(d0[jsol]*d0[jsol])))*detJ*dms;\n                            \n                            kcc += (((dDdc[isol][jsol]*mt.m_k[isol] + D[isol]*mt.m_dkdc[isol][jsol])*flux[isol])*gradN[i]*H[j] - (D[isol]*fpt.m_w*H[j]*mt.m_c[isol]*d0p[isol][jsol]/(d0[isol]*d0[isol]))*gradN[i]*mt.m_k[isol] + H[i]*H[j]*phif*vdzdc[isol][jsol]*mt.m_k[jsol] - H[i]*H[j]*dJsoJ*mt.m_dkdc[isol][jsol]*mt.m_c[isol] - H[i]*H[j]*phif*mt.m_c[isol]*mt.m_dkdc[isol][jsol]*c*dtrans - H[i]*H[j]*phif*mt.m_dkdc[isol][jsol]*mt.m_cdot[isol])*detJ;\n                            \n                            for (int ksol=0; ksol<nsol; ++ksol)\n                            {\n                                if(jsol==ksol)\n                                {\n                                    kcc += (((dDdc[jsol][jsol]*mt.m_k[jsol] + D[jsol]*mt.m_dkdc[jsol][jsol])*flux[jsol])*gradN[i]*H[j]*z[jsol]*penalty + (D[jsol]*(-gradN[j]*phif + fpt.m_w*H[j]*(1.0/d0[jsol] - mt.m_c[jsol]*d0p[jsol][jsol]/(d0[jsol]*d0[jsol]))))*gradN[i]*mt.m_k[jsol]*z[jsol]*penalty + H[i]*H[j]*phif*vdzdc[isol][jsol]*mt.m_dkdc[jsol][jsol]*mt.m_c[jsol])*detJ;\n                                }\n                                else\n                                {\n                                    kcc += (((dDdc[ksol][jsol]*mt.m_k[ksol] + D[ksol]*mt.m_dkdc[ksol][jsol])*flux[ksol])*gradN[i]*H[j]*z[ksol]*penalty - (D[ksol]*fpt.m_w*H[j]*mt.m_c[ksol]*d0p[ksol][jsol]/(d0[ksol]*d0[ksol]))*gradN[i]*mt.m_k[ksol]*z[ksol]*penalty + H[i]*H[j]*phif*vdzdc[isol][jsol]*mt.m_dkdc[ksol][jsol]*mt.m_c[ksol])*detJ;\n                                }\n                            }\n                        }\n                        ke[i7+7+isol][j7+7+jsol] += kcc;\n                    }\n                    ke[i7  ][j7+7+isol] += kuc.x;\n                    ke[i7+1][j7+7+isol] += kuc.y;\n                    ke[i7+2][j7+7+isol] += kuc.z;\n                    ke[i7+3][j7+7+isol] += kwc.x;\n                    ke[i7+4][j7+7+isol] += kwc.y;\n                    ke[i7+5][j7+7+isol] += kwc.z;\n                    ke[i7+6][j7+7+isol] += kJc;\n                    ke[i7+7+isol][j7  ] += kcu.x;\n                    ke[i7+7+isol][j7+1] += kcu.y;\n                    ke[i7+7+isol][j7+2] += kcu.z;\n                    ke[i7+7+isol][j7+3] += kcw.x;\n                    ke[i7+7+isol][j7+4] += kcw.y;\n                    ke[i7+7+isol][j7+5] += kcw.z;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    int nsol = m_pMat->Solutes();\n    int ndpn = 7 + nsol;\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            // element stiffness matrix\n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = ndpn*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate material stiffness\n            ElementStiffness(el, ke);\n            \n            // get the element's LM vector\n            vector<int> lm;\n            UnpackLM(el, lm);\n            ke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 7 + nsol;\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            \n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = ndpn*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate inertial stiffness\n            ElementMassMatrix(el, ke);\n            \n            // get the element's LM vector\n            vector<int> lm;\n            UnpackLM(el, lm);\n            ke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    FEBiphasicFSI* pme = dynamic_cast<FEBiphasicFSI*>(GetMaterial()); assert(pme);\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 7 + nsol;\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        if (el.isActive()) {\n            \n            // element stiffness matrix\n            FEElementMatrix ke(el);\n            \n            // create the element's stiffness matrix\n            int ndof = ndpn*el.Nodes();\n            ke.resize(ndof, ndof);\n            ke.zero();\n            \n            // calculate inertial stiffness\n            ElementBodyForceStiffness(bf, el, ke);\n            \n            // get the element's LM vector\n            vector<int> lm;\n            UnpackLM(el, lm);\n            ke.SetIndices(lm);\n            \n            // assemble element matrix in global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEMultiphasicFSIDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 7 + nsol;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    vector<mat3d> gradgradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    vec3d g[3], dg[3][3];\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    double dt = tp.timeIncrement;\n    double a = tp.gamma/(tp.beta*dt);\n    double b = tp.alpham/(tp.alphaf*tp.beta*dt*dt);\n    double c = tp.alpham/(tp.alphaf*tp.gamma*dt);\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjact(el, Ji, n, tp.alphaf)*gw[n]*tp.alphaf;\n        \n        ContraBaseVectors(el, n, g, tp.alphaf);\n        ContraBaseVectorDerivatives(el, n, dg, tp.alphaf);\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // get the shape function derivatives\n        double* Grr = el.Grr(n); double* Grs = el.Grs(n); double* Grt = el.Grt(n);\n        double* Gsr = el.Gsr(n); double* Gss = el.Gss(n); double* Gst = el.Gst(n);\n        double* Gtr = el.Gtr(n); double* Gts = el.Gts(n); double* Gtt = el.Gtt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& et = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEFSIMaterialPoint& fpt = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        double denss = m_pMat->SolidDensity(mp);\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        \n        // Jsdot/Js = div(vs)\n        double dJsoJ = fpt.m_Jdot/et.m_J;\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate spatial gradgrad of shape functions\n        for (i=0; i<neln; ++i) {\n            gradgradN[i] = (((dg[0][0] & g[0]) + (dg[0][1] & g[1]) + (dg[0][2] & g[2]))*Gr[i]\n                            + ((dg[1][0] & g[0]) + (dg[1][1] & g[1]) + (dg[1][2] & g[2]))*Gs[i]\n                            + ((dg[2][0] & g[0]) + (dg[2][1] & g[1]) + (dg[2][2] & g[2]))*Gt[i]\n                            + (g[0] & g[0])*Grr[i] + (g[0] & g[1])*Gsr[i] + (g[0] & g[2])*Gtr[i]\n                            + (g[1] & g[0])*Grs[i] + (g[1] & g[1])*Gss[i] + (g[1] & g[2] )*Gts[i]\n                            + (g[2] & g[0])*Grt[i] + (g[2] & g[1])*Gst[i] + (g[2] & g[2])*Gtt[i]);\n        }\n        \n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n        \n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += ndpn)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += ndpn)\n            {\n                \n                mat3d Kuu = ((mat3dd(b*H[j]*dtrans))*denss*H[i] + (((et.m_a*phis)&gradN[j]) + mat3dd(b*phif*H[j]*dtrans) - ((fpt.m_w&gradN[j]) * (-1.0/phif*dJsoJ + a*dtrans) - (fpt.m_w&(et.m_L.transpose()*gradN[j]*dtrans)))*phis/phif + mat3dd(gradN[j]*fpt.m_w*a*dtrans) - ((bpt.m_Lw*fpt.m_w)&gradN[j])*phis/(phif*phif) + ((fpt.m_w&gradN[j])*((gradphif*(phis+1.0)/phif)*fpt.m_w) - (fpt.m_w&(gradgradN[j].transpose()*fpt.m_w)))*phis/(phif*phif) - pt.m_Lf*(mat3dd(gradN[j]*fpt.m_w)) - (pt.m_aft&gradN[j])*phis)*(-H[i]*densTf*phis/phif))*detJ; //mixture 2\n                \n                mat3d Kuw = ((mat3dd(c*dtrans-phis/phif*dJsoJ-(gradphif*fpt.m_w)/(phif*phif))+pt.m_Lf)*H[j] + mat3dd(gradN[j]*fpt.m_w)/phif)*(-H[i]*densTf/phif*phis*detJ); //mixture 2\n                \n                vec3d kuJ = pt.m_aft*(densTf/Jf*H[i]*H[j]*phis*detJ); //mixture 2\n                mat3d Kwu = (((et.m_a&gradN[j])*phis + mat3dd(b*phif*H[j]*dtrans) - ((fpt.m_w&gradN[j]) * (-1.0/phif*dJsoJ + a*dtrans) - (fpt.m_w&(et.m_L.transpose()*gradN[j]*dtrans)))*phis/phif + mat3dd(gradN[j]*fpt.m_w*a*dtrans) - ((bpt.m_Lw*fpt.m_w)&gradN[j])*phis/(phif*phif) + ((fpt.m_w&gradN[j])*((gradphif*(phis+1.0)/phif)*fpt.m_w) - (fpt.m_w&(gradgradN[j].transpose()*fpt.m_w)))*phis/(phif*phif) - pt.m_Lf*mat3dd(gradN[j]*fpt.m_w))*(H[i]*densTf/phif) + (pt.m_aft&gradN[j])*H[i]*densTf*(1.0-phis/phif))*detJ;\n                mat3d Kww = ((mat3dd(c*dtrans-phis/phif*dJsoJ-(gradphif*fpt.m_w)/(phif*phif))+pt.m_Lf)*H[j] + mat3dd(gradN[j]*fpt.m_w)/phif)*(H[i]*densTf/phif*detJ);\n                vec3d kwJ = pt.m_aft*(-densTf/Jf*H[i]*H[j]*detJ);\n                \n                ke[i7+0][j7  ] += Kuu(0,0); ke[i7+0][j7+1] += Kuu(0,1); ke[i7+0][j7+2] += Kuu(0,2);\n                ke[i7+1][j7  ] += Kuu(1,0); ke[i7+1][j7+1] += Kuu(1,1); ke[i7+1][j7+2] += Kuu(1,2);\n                ke[i7+2][j7  ] += Kuu(2,0); ke[i7+2][j7+1] += Kuu(2,1); ke[i7+2][j7+2] += Kuu(2,2);\n                \n                ke[i7+0][j7+3] += Kuw(0,0); ke[i7+0][j7+4] += Kuw(0,1); ke[i7+0][j7+5] += Kuw(0,2);\n                ke[i7+1][j7+3] += Kuw(1,0); ke[i7+1][j7+4] += Kuw(1,1); ke[i7+1][j7+5] += Kuw(1,2);\n                ke[i7+2][j7+3] += Kuw(2,0); ke[i7+2][j7+4] += Kuw(2,1); ke[i7+2][j7+5] += Kuw(2,2);\n                \n                ke[i7+3][j7  ] += Kwu(0,0); ke[i7+3][j7+1] += Kwu(0,1); ke[i7+3][j7+2] += Kwu(0,2);\n                ke[i7+4][j7  ] += Kwu(1,0); ke[i7+4][j7+1] += Kwu(1,1); ke[i7+4][j7+2] += Kwu(1,2);\n                ke[i7+5][j7  ] += Kwu(2,0); ke[i7+5][j7+1] += Kwu(2,1); ke[i7+5][j7+2] += Kwu(2,2);\n                \n                ke[i7+3][j7+3] += Kww(0,0); ke[i7+3][j7+4] += Kww(0,1); ke[i7+3][j7+5] += Kww(0,2);\n                ke[i7+4][j7+3] += Kww(1,0); ke[i7+4][j7+4] += Kww(1,1); ke[i7+4][j7+5] += Kww(1,2);\n                ke[i7+5][j7+3] += Kww(2,0); ke[i7+5][j7+4] += Kww(2,1); ke[i7+5][j7+5] += Kww(2,2);\n                \n                ke[i7+0][j7+6] += kuJ.x;\n                ke[i7+1][j7+6] += kuJ.y;\n                ke[i7+2][j7+6] += kuJ.z;\n                ke[i7+3][j7+6] += kwJ.x;\n                ke[i7+4][j7+6] += kwJ.y;\n                ke[i7+5][j7+6] += kwJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            FESolidElement& el = Element(i);\n            if (el.isActive())\n            {\n                UpdateElementStress(i, tp);\n            }\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEMultiphasicFSIDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    double dtrans = m_btrans ? 1 : m_sseps;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // number of solutes\n    const int nsol = m_pMat->Solutes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d r0[NELN], r[NELN];\n    vec3d vs[NELN];\n    vec3d a[NELN];\n    vec3d w[NELN];\n    vec3d aw[NELN];\n    double e[NELN];\n    double ae[NELN];\n    vector< vector<double> > ct(nsol, vector<double>(NELN));\n    vector< vector<double> > act(nsol, vector<double>(NELN));\n    vector<int> sid(nsol);\n    for (int j=0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        r0[j] = node.m_r0;\n        r[j]  = node.m_rt*alphaf + node.m_rp*(1-alphaf);\n        vs[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*alphaf + node.m_vp*(1-alphaf);\n        w[j]  = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-alphaf);\n        e[j]  = node.get(m_dofEF)*alphaf + node.get_prev(m_dofEF)*(1-alphaf);\n        a[j]  = node.m_at*alpham + node.m_ap*(1-alpham);\n        aw[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2])*alpham + node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2])*(1-alpham);\n        ae[j] = node.get(m_dofAEF)*alpham + node.get_prev(m_dofAEF)*(1-alpham);\n        for (int k=0; k<nsol; ++k) {\n            ct[k][j] = node.get(m_dofC + sid[k])*alphaf + node.get_prev(m_dofC + sid[k])*(1-alphaf);\n            act[k][j] = node.get(m_dofAC + sid[k])*alpham + node.get_prev(m_dofAC + sid[k])*(1-alpham);\n        }\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEFSIMaterialPoint& ft = *(mp.ExtractData<FEFSIMaterialPoint>());\n        FEBiphasicFSIMaterialPoint& bt = *(mp.ExtractData<FEBiphasicFSIMaterialPoint>());\n        FEMultiphasicFSIMaterialPoint& spt = *(mp.ExtractData<FEMultiphasicFSIMaterialPoint>());\n        \n        // elastic material point data\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(r, n);\n        mat3d Ft, Fp;\n        double Jt, Jp;\n        Jt = defgrad(el, Ft, n);\n        Jp = defgradp(el, Fp, n);\n        ept.m_F = Ft*alphaf + Fp*(1 - alphaf);\n        ept.m_J = ept.m_F.det();\n        mat3d Fi = ept.m_F.inverse();\n        ept.m_L = (Ft - Fp)*Fi*(dtrans / dt);\n        ept.m_v = m_btrans ? el.Evaluate(vs, n) : vec3d(0, 0, 0);\n        ept.m_a = m_btrans ? el.Evaluate(a, n) : vec3d(0, 0, 0);\n        //NOTE: Made these new function. Converts from GradJ to gradJ\n        vec3d GradJ = FESolidDomain::GradJ(el,n);\n        vec3d GradJp = FESolidDomain::GradJp(el, n);\n        \n        //calculate gradJ gradphif\n        bt.m_gradJ = Fi.transpose()*(GradJ*alphaf + GradJp*(1-alphaf));\n        \n        // FSI material point data\n        ft.m_w = el.Evaluate(w, n);\n        ft.m_Jdot = (Jt - Jp) / dt*dtrans;\n        ft.m_aw = el.Evaluate(aw, n)*dtrans;\n        \n        for (int isol=0; isol < nsol; ++isol) {\n            spt.m_c[isol] = el.Evaluate(ct[isol], n);\n            vec3d Gradc = Gradient(el, ct[isol], n);\n            spt.m_gradc[isol] = Fi.transpose()*Gradc;\n            spt.m_cdot[isol] = el.Evaluate(act[isol], n)*dtrans;\n        }\n        \n        bt.m_phi0 = m_pMat->SolidReferentialVolumeFraction(mp);\n        m_pMat->PartitionCoefficientFunctions(mp, spt.m_k, spt.m_dkdJ, spt.m_dkdc);\n        \n        // fluid material point data\n        double phif = m_pMat->Porosity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        vec3d gradphif = m_pMat->gradPorosity(mp);\n        pt.m_efdot = el.Evaluate(ae, n)*dtrans;\n        pt.m_vft = ept.m_v + ft.m_w/phif;\n        mat3d Gradw = Gradient(el, w, n);\n        bt.m_Lw = Gradw*Fi;\n        pt.m_Lf = ept.m_L + bt.m_Lw/phif - (ft.m_w & gradphif)/(phif*phif);\n        pt.m_ef = el.Evaluate(e, n);\n        vec3d Gradef = Gradient(el, e, n);\n        pt.m_gradef = Fi.transpose()*Gradef;\n        \n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        \n        // fluid acceleration\n        pt.m_aft = (ft.m_aw + ept.m_a*phif - ft.m_w*ft.m_Jdot*phis/phif/ept.m_J + pt.m_Lf*ft.m_w)/phif;\n        \n        spt.m_pe = m_pMat->Fluid()->Pressure(mp);\n        \n        // calculate the solute flux and actual concentration\n        for (int isol=0; isol < nsol; ++isol)\n        {\n            spt.m_j[isol] = m_pMat->SoluteFlux(mp, isol);\n            spt.m_ca[isol] = m_pMat->ConcentrationActual(mp, isol);\n        }\n        \n        // calculate the fluid pressure\n        pt.m_pf = m_pMat->PressureActual(mp);\n        \n        spt.m_psi = m_pMat->ElectricPotential(mp);\n        spt.m_Ie = m_pMat->CurrentDensity(mp);\n        spt.m_cF = m_pMat->FixedChargeDensity(mp);\n        \n        // calculate the solid stress at this material point\n        pt.m_sf = m_pMat->Fluid()->GetViscous()->Stress(mp); //Old Nat BC\n        ft.m_ss = m_pMat->Solid()->Stress(mp) - m_pMat->Fluid()->GetViscous()->Stress(mp)*phis; //Old NatBC\n         //Old Nat BC\n        for (int isol=0; isol<nsol; ++isol)\n            ft.m_ss += -mat3dd(1.0)*R*T*osmc*spt.m_ca[isol];\n        \n        \n        //pt.m_sf = m_pMat->Fluid()->GetViscous()->Stress(mp); //New Nat BC\n        //ft.m_ss = -mat3dd(1.0)*pt.m_pf + m_pMat->Solid()->Stress(mp) - m_pMat->Fluid()->GetViscous()->Stress(mp)*phis; //New NatBC\n        \n        // calculate the mixture stress at this material point\n        ept.m_s =  -mat3dd(1.0)*spt.m_pe + ft.m_ss + pt.m_sf;\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n    \n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 7+nsol;\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        if (el.isActive()) {\n            // element force vector\n            vector<double> fe;\n            vector<int> lm;\n            \n            // get the element force vector and initialize it to zero\n            int ndof = ndpn*el.Nodes();\n            fe.assign(ndof, 0);\n            \n            // calculate internal force vector\n            ElementInertialForce(el, fe);\n            \n            // get the element's LM vector\n            UnpackLM(el, lm);\n            \n            // assemble element 'fe'-vector into global R vector\n            R.Assemble(el.m_node, lm, fe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    const int nsol = m_pMat->Solutes();\n    const int ndpn = 7 + nsol;\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n        double densTf = m_pMat->TrueFluidDensity(mp);\n        double densTs = m_pMat->TrueSolidDensity(mp);\n        double phis = m_pMat->SolidVolumeFrac(mp);\n        \n        // calculate the jacobian\n        detJ = detJt(el, n, tp.alphaf)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d f = pt.m_aft*(densTf*H[i]*detJ);\n            \n            vec3d fs = (-pt.m_aft*densTf + ept.m_a*densTs)*H[i]*phis*detJ; //mixture 2\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i+0] -= fs.x;\n            fe[ndpn*i+1] -= fs.y;\n            fe[ndpn*i+2] -= fs.z;\n            fe[ndpn*i+3] -= f.x;\n            fe[ndpn*i+4] -= f.y;\n            fe[ndpn*i+5] -= f.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSIDomain3D::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_sseps;\n    ar & m_pMat;\n    ar & m_dofU & m_dofV & m_dofW & m_dofAW;\n    ar & m_dofSU & m_dofR;\n    ar & m_dof;\n    ar & m_dofEF & m_dofAEF & m_dofC & m_dofAC;\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEMultiphasicFSIDomain.h\"\n#include \"FEMultiphasicFSI.h\"\n#include \"FEFluidDomain.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! Biphasic-FSI domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEMultiphasicFSIDomain3D : public FESolidDomain, public FEMultiphasicFSIDomain\n{\npublic:\n    //! constructor\n    FEMultiphasicFSIDomain3D(FEModel* pfem);\n    ~FEMultiphasicFSIDomain3D() {}\n    \n    //! assignment operator\n    FEMultiphasicFSIDomain3D& operator = (FEMultiphasicFSIDomain3D& d);\n    \n    //! initialize class\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! reset domain data\n    void Reset() override;\n    \n    //! initialize material points in the domain\n    void InitMaterialPoints() override;\n    \n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! Unpack element data\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \n    // get the total dof\n    const FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n    \npublic: // overrides from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n    \n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n    \n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEMultiphasicFSI*    m_pMat;\n    double      m_sseps;\n    \nprotected:\n    FEDofList    m_dofU, m_dofV, m_dofW, m_dofAW;\n    FEDofList    m_dofSU, m_dofR;\n    FEDofList    m_dof;\n    int          m_dofEF;\n    int          m_dofAEF;\n    int          m_dofC;\n    int          m_dofAC;\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSIDomainFactory.h\"\n#include \"FEMultiphasicFSI.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEFluidFSI.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEMultiphasicFSIDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n    FEModel* pfem = pmat->GetFEModel();\n    FE_Element_Class eclass = spec.eclass;\n    FE_Element_Shape eshape = spec.eshape;\n    const char* sztype = 0;\n    if (dynamic_cast<FEMultiphasicFSI*>(pmat))\n    {\n        // fluid elements\n        if (eclass == FE_ELEM_SOLID) sztype = \"multiphasic-FSI-3D\";\n        else return 0;\n    }\n    else if (dynamic_cast<FEBiphasicFSI*>(pmat))\n    {\n        // fluid elements\n        if (eclass == FE_ELEM_SOLID) sztype = \"biphasic-FSI-3D\";\n        else return 0;\n    }\n    else if (dynamic_cast<FEFluidFSI*>(pmat))\n    {\n        // fluid elements\n        if (eclass == FE_ELEM_SOLID) sztype = \"fluid-FSI-3D\";\n        else return 0;\n    }\n    \n    if (sztype)\n    {\n        FEDomain* pd = fecore_new<FESolidDomain>(sztype, pfem);\n        if (pd) pd->SetMaterial(pmat);\n        return pd;\n    }\n    else return 0;\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEMultiphasicFSIDomainFactory : public FEDomainFactory\n{\npublic:\n    virtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIPressure.cpp",
    "content": "//\n//  FEFluidSolutesPressure.cpp\n//  FEBioFluid\n//\n//  Created by Jay Shim on 12/10/20.\n//  Copyright © 2020 febio.org. All rights reserved.\n//\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSIPressure.h\"\n#include \"FEMultiphasicFSI.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMultiphasicFSIPressure, FESurfaceLoad)\nADD_PARAMETER(m_p, \"pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMultiphasicFSIPressure::FEMultiphasicFSIPressure(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_pfs = nullptr;\n    m_p = 0;\n    \n    \n    m_dofEF = (pfem ? pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION   ), 0) : -1);\n    m_dofC  = (pfem ? pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), 0) : -1);\n    \n    m_dof.Clear();\n    m_dof.AddDof(m_dofEF);\n    \n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEMultiphasicFSIPressure::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_psurf->Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfs = dynamic_cast<FEMultiphasicFSI*>(pm);\n    if (m_pfs == nullptr) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEMultiphasicFSIPressure::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofEF, DOF_PRESCRIBED);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEMultiphasicFSIPressure::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = &GetSurface();\n    int nsol = 0;\n    double T = 0;\n    double R = 0;\n\n    nsol = m_pfs->Solutes();\n    T = m_pfs->m_Tabs;\n    R = m_pfs->m_Rgas;\n    \n    std::map<int,vector<double>> oscNodes;\n    std::map<int,vector<double>> caNodes;\n    \n    //initialize maps based on surface nodal IDs\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        oscNodes.insert(pair<int,vector<double> >(ps->Node(i).GetID(), vector<double>()));\n        caNodes.insert(pair<int,vector<double> >(ps->Node(i).GetID(), vector<double>()));\n    }\n    \n    //Project sum of all ca and osc values from int points to nodes on surface\n    //All values put into map, including duplicates\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        FEElement* e = el.m_elem[0].pe;\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        if (se) {\n            double osci[FEElement::MAX_INTPOINTS];\n            double osco[FEElement::MAX_NODES];\n            double cai[FEElement::MAX_INTPOINTS];\n            double cao[FEElement::MAX_NODES];\n            for (int j=0; j<se->GaussPoints(); ++j) {\n                FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                FEMultiphasicFSIMaterialPoint* fsp = pt->ExtractData<FEMultiphasicFSIMaterialPoint>();\n                if (fsp)\n                {\n                    osci[j] = m_pfs->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    cai[j] = fsp->m_ca[0];\n                    for (int isol = 1; isol < nsol; ++isol)\n                        cai[j] += fsp->m_ca[isol];\n                }\n                else\n                {\n                    osci[j] = 0;\n                    cai[j] = 0;\n                }\n            }\n            // project stresses from integration points to nodes\n            se->project_to_nodes(osci, osco);\n            se->project_to_nodes(cai, cao);\n            // only keep the stresses at the nodes of the contact face\n            for (int j=0; j<el.Nodes(); ++j)\n            {\n                oscNodes[el.m_node[j]+1].push_back(osco[se->FindNode(el.m_node[j])]);\n                caNodes[el.m_node[j]+1].push_back(cao[se->FindNode(el.m_node[j])]);\n            }\n        }\n        //If no solid element, insert all 0s\n        else{\n            for (int j=0; j<el.Nodes(); ++j)\n            {\n                oscNodes[el.m_node[j]+1].push_back(0);\n                caNodes[el.m_node[j]+1].push_back(0);\n            }\n        }\n    }\n    //For each node average the nodal ca and osc and then calculate ef based on desired p\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofEF] < -1)\n        {\n            FENode& node = ps->Node(i);\n            \n            //get osmotic component of pressure\n            double ca = 0;\n            double osc = 0;\n            for (int j = 0; j < caNodes[node.GetID()].size(); ++j)\n            {\n                ca += caNodes[node.GetID()][j];\n                osc += oscNodes[node.GetID()][j];\n            }\n            ca /= caNodes[node.GetID()].size();\n            osc /= caNodes[node.GetID()].size();\n            \n            double pc = R*T*osc*ca;\n            \n            //get correct ef for desired pressure\n            double e = 0;\n            bool good = false;\n            good = m_pfs->Fluid()->Dilatation(0, m_p - pc, e);\n            assert(good);\n            \n            // set node as having prescribed DOF\n            node.set(m_dofEF, e);\n        }\n    }\n    \n    /*\n     for (int i=0; i<ps->Nodes(); ++i)\n     {\n     if (ps->Node(i).m_ID[m_dofEF] < -1)\n     {\n     FENode& node = ps->Node(i);\n     \n     //get osmotic component of pressure\n     double c = 0;\n     for (int isol = 0; isol < nsol; ++isol)\n     {\n     c += node.get(m_dofC + isol);\n     }\n     \n     //get correct ef for desired pressure\n     double e = -1/K*(m_p - R*T*m_osc*c);\n     \n     // set node as having prescribed DOF\n     node.set(m_dofEF, e);\n     }\n     }\n     */\n    GetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate residual\nvoid FEMultiphasicFSIPressure::LoadVector(FEGlobalVector& R)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMultiphasicFSIPressure::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pfs;\n    ar & m_dofC;\n    ar & m_dofEF;\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIPressure.h",
    "content": "//\n//  FEFluidSolutesPressure.hpp\n//  FEBioFluid\n//\n//  Created by Jay Shim on 12/10/20.\n//  Copyright © 2020 febio.org. All rights reserved.\n//\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEMultiphasicFSI.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidResistanceBC is a fluid surface that has a normal\n//! pressure proportional to the flow rate (resistance).\n//!\nclass FEBIOFLUID_API FEMultiphasicFSIPressure : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEMultiphasicFSIPressure(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! set the dilatation\n    void Update() override;\n    \n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \nprivate:\n    double          m_p;       //!< prescribed fluid pressure\n    \nprivate:\n    FEMultiphasicFSI*    m_pfs;   //!< pointer to fluid-solutes material\n    \n    int        m_dofEF;\n    int        m_dofC;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIPressureBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n\n See Copyright-FEBio.txt for details.\n\n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSIPressureBC.h\"\n#include \"FEMultiphasicFSI.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMultiphasicFSIPressureBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_p, \"pressure\")->setUnits(\"P\")->setLongName(\"fluid pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMultiphasicFSIPressureBC::FEMultiphasicFSIPressureBC(FEModel* pfem) : FEPrescribedSurface(pfem)\n{\n    m_p = 0;\n    m_dofEF = -1;\n    m_dofC = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEMultiphasicFSIPressureBC::Init()\n{\n    m_dofEF = GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION), 0);\n    m_dofC = GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), 0);\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n    \n    m_e.assign(GetSurface()->Nodes(), 0.0);\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEMultiphasicFSIPressureBC::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n    \n    int N = ps->Nodes();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    std::vector<vector<double>> caNodes(N, vector<double>());\n    \n    //Project sum of all ca and osc values from int points to nodes on surface\n    //All values put into map, including duplicates\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        // evaluate average prescribed pressure on this face\n        double p = 0;\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMaterialPoint* pt = el.GetMaterialPoint(j);\n            p += m_p(*pt);\n        }\n        p /= el.GaussPoints();\n        FEElement* e = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n        FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        if (se) {\n            double efo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetPartitionCoefficient(*pt, k);\n                }\n                osc /= nint;\n                for (int k=0; k<nsol; ++k) kappa[k] /= nint;\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    // evaluate dilatation at this node\n                    bool good = pfl->Dilatation(0, p - m_Rgas*m_Tabs*osc*osm, efo[j]);\n                    assert(good);\n                }\n            }\n            else {\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // evaluate dilatation at this node\n                    bool good = pfl->Dilatation(0, p, efo[j]);\n                    assert(good);\n                }\n            }\n            // only keep the dilatations at the nodes of the surface face\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(efo[j]);\n        }\n        //If no solid element, insert all 0s\n        else {\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(0);\n        }\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n        \n        // store value for now\n        m_e[i] = ef;\n    }\n    \n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\n// return the value for node i, dof j\nvoid FEMultiphasicFSIPressureBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEMultiphasicFSIPressureBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMultiphasicFSIPressureBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    if (ar.IsLoading()) {\n        m_e.assign(GetSurface()->Nodes(), 0.0);\n    }\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_Rgas & m_Tabs;\n    ar & m_dofEF & m_dofC;\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSIPressureBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEMultiphasicFSI.h\"\n\n//-----------------------------------------------------------------------------\n//! FEFluidResistanceBC is a fluid surface that has a normal\n//! pressure proportional to the flow rate (resistance).\n//!\nclass FEBIOFLUID_API FEMultiphasicFSIPressureBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEMultiphasicFSIPressureBC(FEModel* pfem);\n\n    //! set the dilatation\n    void Update() override;\n\n    //! initialize\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    FEParamDouble   m_p;       //!< prescribed fluid pressure\n\nprivate:\n    std::vector<double> m_e;\n    double      m_Rgas;\n    double      m_Tabs;\n    int        m_dofEF;\n    int        m_dofC;\n\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSISoluteBackflowStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSISoluteBackflowStabilization.h\"\n#include \"FEFluid.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include <FECore/FENodeNodeList.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMultiphasicFSISoluteBackflowStabilization, FESurfaceLoad)\nADD_PARAMETER(m_sol , \"sol\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMultiphasicFSISoluteBackflowStabilization::FEMultiphasicFSISoluteBackflowStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_sol = -1;\n    m_dofC = (pfem ? pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), 0) : -1);\n    m_nnlist = FENodeNodeList();\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEMultiphasicFSISoluteBackflowStabilization::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    // determine the nr of concentration equations\n    FEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n    m_dofW.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_VELOCITY));\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    if ((m_sol < 1) || (m_sol > MAX_CDOFS)) return false;\n    \n    m_dof.AddDofs(m_dofW);\n    m_dof.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    \n    FESurface* ps = &GetSurface();\n    m_backflow.assign(ps->Nodes(), false);\n    \n    m_nnlist.Create(fem.GetMesh());\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEMultiphasicFSISoluteBackflowStabilization::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    int dofc = m_dofC + m_sol - 1;\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(dofc, DOF_OPEN);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEMultiphasicFSISoluteBackflowStabilization::Update()\n{\n    // determine backflow conditions\n    MarkBackFlow();\n    \n    // prescribe solute backflow constraint at the nodes\n    FESurface* ps = &GetSurface();\n    \n    int dofc = m_dofC + m_sol - 1;\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // set node as having prescribed DOF (concentration at previous time)\n        //Otherwise set node as having concentration of adjacent node\n        if (node.m_ID[dofc] < -1)\n            node.set(dofc, node.get_prev(dofc));\n        else\n        {\n            node.set_bc(dofc, DOF_PRESCRIBED);\n            node.m_ID[dofc] = -node.m_ID[dofc] - 2;\n            int nid = node.GetID()-1; //0 based\n            int val = m_nnlist.Valence(nid);\n            int* nlist = m_nnlist.NodeList(nid);\n            \n            vector<int> connectnidarray;\n            int connectnid=0;\n            //check which connecting nodes are not on surface\n            for (int j = 0; j<val; ++j)\n            {\n                int cnid = *(nlist+j);\n                bool isSurf = false;\n                for (int k=0; k<ps->Nodes(); ++k)\n                {\n                    if (cnid==ps->Node(k).GetID()-1)\n                        isSurf = true;\n                }\n                if (!isSurf)\n                {\n                    connectnidarray.push_back(cnid);\n                }\n            }\n            //find closest connecting node\n            if (connectnidarray.size()>0)\n            {\n                int cnodeIndex = 0;\n                FENode* cnodeArray = GetFEModel()->GetMesh().FindNodeFromID(connectnidarray[cnodeIndex]+1);\n                double smallDist = sqrt(pow((node.m_rt.x-cnodeArray->m_rt.x),2)+pow((node.m_rt.y-cnodeArray->m_rt.y),2)+pow((node.m_rt.z-cnodeArray->m_rt.z),2));\n                for (int j = 1; j<connectnidarray.size(); ++j)\n                {\n                    FENode* tempNode = GetFEModel()->GetMesh().FindNodeFromID(connectnidarray[j]+1);\n                    double temp = sqrt(pow((node.m_rt.x-tempNode->m_rt.x),2)+pow((node.m_rt.y-tempNode->m_rt.y),2)+pow((node.m_rt.z-tempNode->m_rt.z),2));\n                    if(temp<smallDist)\n                    {\n                        cnodeIndex = j;\n                        cnodeArray = tempNode;\n                        smallDist = temp;\n                    }\n                }\n                connectnid = connectnidarray[cnodeIndex];\n                FENode* cnode = GetFEModel()->GetMesh().FindNodeFromID(connectnid+1);\n                node.set(dofc, cnode->get(dofc));\n            }\n            else\n                node.set(dofc, 0);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface\nvoid FEMultiphasicFSISoluteBackflowStabilization::MarkBackFlow()\n{\n    // Mark all nodes on this surface to have open concentration DOF\n    FESurface* ps = &GetSurface();\n    int dofc = m_dofC + m_sol - 1;\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having free DOF\n        if (node.m_ID[dofc] < -1) {\n            node.set_bc(dofc, DOF_OPEN);\n            node.m_ID[dofc] = -node.m_ID[dofc] - 2;\n        }\n    }\n    \n    const FETimeInfo& tp = GetTimeInfo();\n\n    // Calculate normal flow velocity on each face to determine\n    // backflow condition\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt*tp.alpha + node.m_rp*(1-tp.alpha);\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*tp.alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-tp.alphaf);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        double vn = 0;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            normal.unit();\n            vn += (normal*v)*w[n];\n        }\n        \n        if (vn < 0) {\n            for (int i=0; i<neln; ++i) {\n                FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n                if (node.m_ID[dofc] > -1) {\n                    node.set_bc(dofc, DOF_PRESCRIBED);\n                    node.m_ID[dofc] = -node.m_ID[dofc] - 2;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculate residual\nvoid FEMultiphasicFSISoluteBackflowStabilization::LoadVector(FEGlobalVector& R)\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMultiphasicFSISoluteBackflowStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsLoading()) {\n        m_backflow.assign(GetSurface().Nodes(), false);\n    }\n    ar & m_backflow;\n    if (ar.IsShallow()) return;\n    ar & m_dofW & m_dofC;\n    //ar & m_nnlist;\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSISoluteBackflowStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluidSolutes.h\"\n#include <FECore/FENodeNodeList.h>\n\n//-----------------------------------------------------------------------------\n//! FESoluteBackflowStabilization is a fluid surface where solute concentration\n//! is maintained constant when the fluid velocity normal to the surface is\n//! inward.\n//!\nclass FEBIOFLUID_API FEMultiphasicFSISoluteBackflowStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEMultiphasicFSISoluteBackflowStabilization(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! evaluate flow rate\n    void MarkBackFlow();\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \nprivate:\n    int         m_sol;          //!< solute id\n    FEDofList   m_dofW;\n    int         m_dofC;\n    vector<bool> m_backflow;    //!< flag for nodes that have backflow\n    FENodeNodeList m_nnlist;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSISoluteFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFSISoluteFlux.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include <FECore/log.h>\n#include <FECore/FEFacetSet.h>\n#include \"FECore/FEAnalysis.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMultiphasicFSISoluteFlux, FESurfaceLoad)\nADD_PARAMETER(m_flux   , \"flux\");\nADD_PARAMETER(m_isol   , \"sol\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMultiphasicFSISoluteFlux::FEMultiphasicFSISoluteFlux(FEModel* pfem) : FESurfaceLoad(pfem), m_dofC(pfem), m_dofU(pfem)\n{\n    m_flux = 1.0;\n    m_isol = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEMultiphasicFSISoluteFlux::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n    m_flux.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the residual for the prescribed normal component of velocity\nvoid FEMultiphasicFSISoluteFlux::LoadVector(FEGlobalVector& R)\n{\n    FEMultiphasicFSISoluteFlux* flux = this;\n    m_psurf->LoadVector(R, m_dofC, true, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n        \n        double wr = flux->m_flux(mp);\n        \n        vec3d dxt = mp.dxr ^ mp.dxs;\n        \n        // volumetric flow rate\n        double f = dxt.norm()*wr;\n        \n        double H_i = dof_a.shape;\n        fa[0] = H_i * f;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSISoluteFlux::StiffnessMatrix(FELinearSystem& LS)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    // evaluate the stiffness contribution\n    FEMultiphasicFSISoluteFlux* flux = this;\n    m_psurf->LoadStiffness(LS, m_dofC, m_dofU, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n        \n        // shape functions and derivatives\n        double H_i  = dof_a.shape;\n        double Gr_j = dof_b.shape_deriv_r;\n        double Gs_j = dof_b.shape_deriv_s;\n        \n        double wr = flux->m_flux(mp);\n        \n        // calculate surface normal\n        vec3d dxt = mp.dxr ^ mp.dxs;\n        \n        double alpha = tp.alphaf;\n        \n        // calculate stiffness component\n        vec3d t1 = dxt / dxt.norm()*wr;\n        vec3d t2 = - mp.dxs*Gr_j + mp.dxr*Gs_j;\n        mat3d A; A.skew(t2);\n        vec3d kab = -A*t1*(H_i)*alpha;\n        Kab.zero();\n        Kab[0][0] -= kab.x;\n        Kab[0][1] -= kab.y;\n        Kab[0][2] -= kab.z;\n    });\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEMultiphasicFSISoluteFlux::Init()\n{\n    if (m_isol == -1) return false;\n    \n    // set up the dof lists\n    FEModel* fem = GetFEModel();\n    m_dofC.Clear();\n    m_dofU.Clear();\n    m_dofC.AddDof(GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), m_isol-1));\n    m_dofU.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::DISPLACEMENT));\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofU);\n    m_dof.AddDofs(m_dofC);\n    \n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMultiphasicFSISoluteFlux::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    \n    if (ar.IsShallow() == false)\n    {\n        ar & m_dofC & m_dofU;\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSISoluteFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The flux surface is a surface domain that sustains a solute flux boundary\n//! condition for FluidSolutesDomain\nclass FEBIOFLUID_API FEMultiphasicFSISoluteFlux : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEMultiphasicFSISoluteFlux(FEModel* pfem);\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    void SetSolute(int isol) { m_isol = isol; }\n    \n    //! initialization\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    \nprivate:\n    FEParamDouble    m_flux;        //!< flux scale factor magnitude\n    int        m_isol;        //!< solute index\n    \nprivate:\n    FEDofList        m_dofC;\n    FEDofList        m_dofU;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSISolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSISolver.h\"\n#include \"FEMultiphasicFSISolver.h\"\n#include \"FEFluidResidualVector.h\"\n#include \"FEBioMech/FEElasticDomain.h\"\n#include \"FEBioMech/FEPressureLoad.h\"\n#include \"FEBioMech/FERigidConnector.h\"\n#include \"FEBioMech/FESlidingElasticInterface.h\"\n#include \"FEBioMech/FESSIShellDomain.h\"\n#include \"FEBioMech/FEResidualVector.h\"\n#include \"FEFluidFSIDomain.h\"\n#include \"FEFluidDomain.h\"\n#include \"FEBiphasicFSIDomain.h\"\n#include \"FEMultiphasicFSIDomain.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <assert.h>\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/sys.h\"\n#include \"FEBioMech/FE3FieldElasticSolidDomain.h\"\n#include \"FEBioMech/FE3FieldElasticShellDomain.h\"\n#include \"FEBioMech/FEUncoupledMaterial.h\"\n#include <FEBioMech/FEBodyForce.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/DumpStream.h>\n#include <FEBioMech/FESolidLinearSystem.h>\n#include \"FEBioFSI.h\"\n#include \"FEBioMultiphasicFSI.h\"\n#include \"FEMultiphasicFSIAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEMultiphasicFSISolver, FENewtonSolver)\n    ADD_PARAMETER(m_Dtol , \"dtol\"        );\n    ADD_PARAMETER(m_Vtol , \"vtol\"        );\n    ADD_PARAMETER(m_Ftol , \"ftol\"        );\n    ADD_PARAMETER(m_Ctol , \"ctol\"        );\n    ADD_PARAMETER(m_Etol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n    ADD_PARAMETER(m_Rtol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n    ADD_PARAMETER(m_rhoi , \"rhoi\"        );\n    ADD_PARAMETER(m_pred , \"predictor\"   );\n    ADD_PARAMETER(m_minJf, \"min_volume_ratio\");\n    ADD_PARAMETER(m_order, \"order\"      );\n    ADD_PARAMETER(m_forcePositive, \"force_positive_concentrations\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEFluidFSISolver Construction\n//\nFEMultiphasicFSISolver::FEMultiphasicFSISolver(FEModel* pfem) : FENewtonSolver(pfem), m_rigidSolver(pfem), \\\nm_dofU(pfem), m_dofV(pfem), m_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem),m_dofR(pfem), m_dofVF(pfem),m_dofAF(pfem),m_dofW(pfem), m_dofAW(pfem), m_dofEF(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Dtol = 0.001;\n    m_Vtol = 0.001;\n    m_Ftol = 0.001;\n    m_Ctol = 0.01;\n    m_Rmin = 1.0e-20;\n    m_Rmax = 0;    // not used if zero\n    m_minJf = 0;\n    \n    m_ndeq = 0;\n    m_nveq = 0;\n    m_nfeq = 0;\n    m_niter = 0;\n    m_nreq = 0;\n    \n    // assume non-symmetric\n    m_msymm = REAL_UNSYMMETRIC;\n    \n    // default Newmark parameters for rhoi = 0\n    m_rhoi = 0;\n    m_alphaf = 1;\n    m_alpham = 1.5;\n    m_beta = 0.5625;\n    m_gamma = 1;\n    m_pred = 0;\n    m_order = 2;\n    \n    m_forcePositive = true;    // force all concentrations to remain positive\n    \n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n    \n    // turn off checking for a zero diagonal\n    CheckZeroDiagonal(false);\n    \n    // get the dof indices\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::DISPLACEMENT));\n        m_dofV.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::VELOCITY));\n        m_dofSU.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_DISPLACEMENT));\n        m_dofSV.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_VELOCITY));\n        m_dofSA.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::SHELL_ACCELERATION));\n        m_dofR.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RIGID_ROTATION));\n        m_dofW.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::RELATIVE_FLUID_ACCELERATION));\n        m_dofVF.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_VELOCITY));\n        m_dofAF.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_ACCELERATION));\n        m_dofEF.AddVariable(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION));\n        m_dofAEF = pfem->GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_DILATATION_TDERIV), 0);\n        m_dofC = m_dofAC = -1;\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEMultiphasicFSISolver::~FEMultiphasicFSISolver()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Generate warnings if needed\nvoid FEMultiphasicFSISolver:: SolverWarnings()\n{\n    FEModel& fem = *GetFEModel();\n    \n    // Generate warning if rigid connectors are used with symmetric stiffness\n    if (m_msymm == REAL_SYMMETRIC) {\n        for (int i=0; i<fem.NonlinearConstraints(); ++i)\n        {\n            FENLConstraint* plc = fem.NonlinearConstraint(i);\n            FERigidConnector* prc = dynamic_cast<FERigidConnector*>(plc);\n            if (prc) {\n                feLogWarning(\"Rigid connectors require non-symmetric stiffness matrix.\\nSet symmetric_stiffness flag to 0 in Control section.\");\n                break;\n            }\n        }\n        \n        // Generate warning if sliding-elastic contact is used with symmetric stiffness\n        if (fem.SurfacePairConstraints() > 0)\n        {\n            // loop over all contact interfaces\n            for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n            {\n                FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n                FESlidingElasticInterface* pbw = dynamic_cast<FESlidingElasticInterface*>(pci);\n                if (pbw) {\n                    feLogWarning(\"The sliding-elastic contact algorithm runs better with a non-symmetric stiffness matrix.\\nYou may set symmetric_stiffness 0 to false in Control section.\");\n                    break;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEFluidFSISolver\n//\nbool FEMultiphasicFSISolver::Init()\n{\n    // initialize base class\n    if (FENewtonSolver::Init() == false) return false;\n    \n    // check parameters\n    if (m_Dtol <  0.0) { feLogError(\"dtol must be nonnegative.\"); return false; }\n    if (m_Vtol <  0.0) { feLogError(\"vtol must be nonnegative.\"); return false; }\n    if (m_Ftol <  0.0) { feLogError(\"ftol must be nonnegative.\"); return false; }\n    if (m_Ctol <  0.0) { feLogError(\"ctol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_beta = m_gamma = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        if (m_order == 1)\n            m_alpham = (3-m_rhoi)/(1+m_rhoi)/2; // 1st-order system\n        else\n            m_alpham = (2-m_rhoi)/(1+m_rhoi); // 2nd-order system\n        m_beta = pow(1 + m_alpham - m_alphaf,2)/4;\n        m_gamma = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\\n\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    m_di.assign(m_ndeq,0);\n    m_Di.assign(m_ndeq,0);\n    m_vi.assign(m_nveq,0);\n    m_Vi.assign(m_nveq,0);\n    m_fi.assign(m_nfeq,0);\n    m_Fi.assign(m_nfeq,0);\n    \n    // get number of DOFS\n    FEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    // allocate concentration-vectors\n    m_ci.assign(MAX_CDOFS,vector<double>(0,0));\n    m_Ci.assign(MAX_CDOFS,vector<double>(0,0));\n    for (int i=0; i<MAX_CDOFS; ++i) {\n        m_ci[i].assign(m_nceq[i], 0);\n        m_Ci[i].assign(m_nceq[i], 0);\n    }\n    vector<int> dofs;\n    for (int j=0; j<(int)m_nceq.size(); ++j) {\n        if (m_nceq[j]) {\n            dofs.push_back(m_dofC + j);\n        }\n    }\n    \n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n    FEMesh& mesh = fem.GetMesh();\n    gather(m_Ut, mesh, m_dofU[0]);\n    gather(m_Ut, mesh, m_dofU[1]);\n    gather(m_Ut, mesh, m_dofU[2]);\n    gather(m_Ut, mesh, m_dofSU[0]);\n    gather(m_Ut, mesh, m_dofSU[1]);\n    gather(m_Ut, mesh, m_dofSU[2]);\n    gather(m_Ut, mesh, m_dofW[0]);\n    gather(m_Ut, mesh, m_dofW[1]);\n    gather(m_Ut, mesh, m_dofW[2]);\n    gather(m_Ut, mesh, m_dofEF[0]);\n    gather(m_Ut, mesh, dofs);\n    \n    \n    // set flag for transient or steady-state analyses\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n            FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(&dom);\n            if (fdom) {\n                if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::STEADY_STATE)\n                    fdom->SetSteadyStateAnalysis();\n                else\n                    fdom->SetTransientAnalysis();\n            }\n            else if (fsidom) {\n                if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::STEADY_STATE)\n                    fsidom->SetSteadyStateAnalysis();\n                else\n                    fsidom->SetTransientAnalysis();\n            }\n            else if (bfsidom) {\n                if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::STEADY_STATE)\n                    bfsidom->SetSteadyStateAnalysis();\n                else\n                    bfsidom->SetTransientAnalysis();\n            }\n            else if (mfsidom) {\n                if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::STEADY_STATE)\n                    mfsidom->SetSteadyStateAnalysis();\n                else\n                    mfsidom->SetTransientAnalysis();\n            }\n        }\n    }\n    \n    SolverWarnings();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEMultiphasicFSISolver::InitEquations()\n{\n    // base class initialization\n    if (FENewtonSolver::InitEquations() == false) return false;\n    \n    if (m_eq_scheme == EQUATION_SCHEME::BLOCK)\n    {\n        // merge the second and third partition\n        if (m_part.size() == 3)\n        {\n            vector<int> newPart(2);\n            newPart[0] = m_part[0];\n            newPart[1] = m_part[1] + m_part[2];\n            \n            m_part = newPart;\n        }\n    }\n    \n    // store the number of equations we currently have\n    m_nreq = m_neq;\n    \n    // Next, we assign equation numbers to the rigid body degrees of freedom\n    int neq = m_rigidSolver.InitEquations(m_neq);\n    if (neq == -1) return false;\n    else m_neq = neq;\n    \n    // determine the number of velocity and dilatation equations\n    FEModel& fem = *GetFEModel();\n    m_dofC = fem.GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION), 0);\n    m_dofAC = fem.GetDOFIndex(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION_TDERIV), 0);\n    FEMesh& mesh = fem.GetMesh();\n    m_ndeq = m_nveq = m_nfeq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofU[0] ] != -1) m_ndeq++;\n        if (n.m_ID[m_dofU[1] ] != -1) m_ndeq++;\n        if (n.m_ID[m_dofU[2] ] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[0]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[1]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[2]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofW[0] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_nfeq++;\n    }\n    \n    // determine the nr of concentration equations\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    m_nceq.assign(MAX_CDOFS, 0);\n    \n    // get number of DOFS\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            if (n.m_ID[m_dofC+j] != -1) m_nceq[j]++;\n        }\n    }\n    \n    // get the total concentration equations\n    m_nseq = 0;\n    for (int i = 0; i < MAX_CDOFS; ++i) m_nseq += m_nceq[i];\n\n    // Next, we add any Lagrange Multipliers\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n\n    // All initialization is done\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSISolver::GetDisplacementData(vector<double> &xi, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(xi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofU[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofU[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofU[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofSU[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofSU[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofSU[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSISolver::GetVelocityData(vector<double> &vi, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(vi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofW[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSISolver::GetDilatationData(vector<double> &ei, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ei);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofEF[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ei[m++] = ui[nid];\n            assert(m <= (int) ei.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFSISolver::GetConcentrationData(vector<double> &ci, vector<double> &ui, const int sol)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ci);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofC+sol];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ci[m++] = ui[nid];\n            assert(m <= (int) ci.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEMultiphasicFSISolver::Serialize(DumpStream& ar)\n{\n    // Serialize parameters\n    FENewtonSolver::Serialize(ar);\n    // serialize rigid solver\n    m_rigidSolver.Serialize(ar);\n    \n    ar & m_nreq & m_ndeq & m_nfeq & m_nveq & m_nseq & m_nceq;\n    ar & m_nrhs & m_niter & m_nref & m_ntotref;\n\n    if (ar.IsLoading())\n    {\n        m_Fr.assign(m_neq, 0);\n        m_Vi.assign(m_nveq,0);\n        m_Di.assign(m_ndeq,0);\n        for (int i=0; i<m_nceq.size(); ++i) {\n            m_ci[i].assign(m_nceq[i], 0);\n            m_Ci[i].assign(m_nceq[i], 0);\n        }\n    }\n    \n    ar & m_Ui & m_Ut & m_Fr;\n    ar & m_Di & m_Vi & m_Fi & m_Ci;\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_rhoi & m_alphaf & m_alpham;\n    ar & m_beta & m_gamma;\n    ar & m_pred;\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEMultiphasicFSISolver::UpdateKinematics(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update rigid bodies\n    m_rigidSolver.UpdateRigidBodies(m_Ui, ui);\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter(U, mesh, m_dofU[0]);\n    scatter(U, mesh, m_dofU[1]);\n    scatter(U, mesh, m_dofU[2]);\n    scatter(U, mesh, m_dofSU[0]);\n    scatter(U, mesh, m_dofSU[1]);\n    scatter(U, mesh, m_dofSU[2]);\n    scatter(U, mesh, m_dofW[0]);\n    scatter(U, mesh, m_dofW[1]);\n    scatter(U, mesh, m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    \n    // update solute data\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // update nodal concentration\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            int n = node.m_ID[m_dofC+j];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if ((ct < 0.0) && m_forcePositive) ct = 0.0;\n                node.set(m_dofC + j, ct);\n            }\n        }\n    }\n    \n    // force dilatations to remain greater than -1\n    if (m_minJf > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofEF[0]) <= -1.0)\n                node.set(m_dofEF[0], m_minJf - 1.0);\n        }\n    }\n    \n    // make sure the prescribed BCs are fulfilled\n    int nvel = fem.BoundaryConditions();\n    for (int i=0; i<nvel; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.Update();\n    }\n    \n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i=0; i<nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.Update();\n    }\n    \n    // enforce the linear constraints\n    // TODO: do we really have to do this? Shouldn't the algorithm\n    // already guarantee that the linear constraints are satisfied?\n    FELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    if (LCM.LinearConstraints() > 0)\n    {\n        LCM.Update();\n    }\n    \n    // Update the spatial nodal positions\n    // Don't update rigid nodes since they are already updated\n    for (int i = 0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.m_rid == -1) {\n            node.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n            node.m_dt = node.m_d0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2])\n            - node.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n        }\n    }\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n        double dt = fem.GetTime().timeIncrement;\n        double a = 1.0 / (m_beta*dt);\n        double b = a / dt;\n        double c = 1.0 - 0.5/m_beta;\n        double cgi = 1 - 1.0/m_gamma;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // solid acceleration\n            n.m_at = (n.m_rt - n.m_rp)*b - n.m_vp*a + n.m_ap*c;\n            // solid velocity\n            vec3d vt = n.m_vp + (n.m_ap*(1.0 - m_gamma) + n.m_at*m_gamma)*dt;\n            n.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n            \n            // shell kinematics\n            vec3d qt = n.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n            vec3d qp = n.get_vec3d_prev(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n            vec3d vqp = n.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2]);\n            vec3d aqp = n.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2]);\n            vec3d aqt = (qt - qp)*b - vqp*a + aqp*c;\n            vec3d vqt = vqp + (aqp*(1.0 - m_gamma) + aqt*m_gamma)*dt;\n            n.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n            n.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vqt);\n            \n            // relative fluid velocity material time derivative (in solid frame)\n            vec3d wt = n.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d wp = n.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d awt = n.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            vec3d awp = n.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            awt = awp*cgi + (wt - wp)/(m_gamma*dt);\n            n.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awt);\n            \n            // fluid velocity\n            vec3d vft = vt + wt;\n            n.set_vec3d(m_dofVF[0], m_dofVF[1], m_dofVF[2], vft);\n            // material time derivative of fluid velocity (in solid frame)\n            vec3d aft = n.m_at + awt;\n            n.set_vec3d(m_dofAF[0], m_dofAF[1], m_dofAF[2], aft);\n            \n            // dilatation time derivative\n            double eft = n.get(m_dofEF[0]);\n            double efp = n.get_prev(m_dofEF[0]);\n            double aefp = n.get_prev(m_dofAEF);\n            double aeft = aefp*cgi + (eft - efp)/(m_gamma*dt);\n            n.set(m_dofAEF, aeft);\n            \n            // concentration time derivative\n            // update nodal concentration\n            for (int j=0; j<MAX_CDOFS; ++j) {\n                int k = n.m_ID[m_dofC+j];\n                // Force the concentrations to remain positive\n                if (k >= 0) {\n                    double ct = n.get(m_dofC+j);\n                    double cp = n.get_prev(m_dofC+j);\n                    double acp = n.get_prev(m_dofAC+j);\n                    double act = acp*cgi + (ct - cp)/(m_gamma*dt);\n                    n.set(m_dofAC + j, act);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update DOF increments\nvoid FEMultiphasicFSISolver::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update rigid bodies\n    m_rigidSolver.UpdateIncrements(Ui, ui, emap);\n    \n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    \n    // update flexible nodes\n    int n;\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // displacement dofs\n        // current position = initial + total at prev conv step + total increment so far + current increment\n        if ((n = node.m_ID[m_dofU[0]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofU[1]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofU[2]]) >= 0) Ui[n] += ui[n];\n        \n        // rotational dofs\n        if ((n = node.m_ID[m_dofSU[0]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofSU[1]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofSU[2]]) >= 0) Ui[n] += ui[n];\n        \n        // fluid relative velocity\n        if ((n = node.m_ID[m_dofW[0]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofW[1]]) >= 0) Ui[n] += ui[n];\n        if ((n = node.m_ID[m_dofW[2]]) >= 0) Ui[n] += ui[n];\n        \n        // fluid dilatation\n        if ((n = node.m_ID[m_dofEF[0]]) >= 0) Ui[n] += ui[n];\n        \n        // update nodal concentration\n        for (int j=0; j<MAX_CDOFS; ++j)\n        {\n            if((n = node.m_ID[m_dofC+j]) >= 0)\n            {\n                Ui[n] += ui[n];\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEMultiphasicFSISolver::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update EAS\n    UpdateEAS(ui);\n    UpdateIncrementsEAS(ui, true);\n    \n    // update kinematics\n    UpdateKinematics(ui);\n     \n    // update element stresses\n    UpdateModel();\n}\n\n//-----------------------------------------------------------------------------\n//! Update EAS\nvoid FEMultiphasicFSISolver::UpdateEAS(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    FEMesh& mesh = fem.GetMesh();\n    \n    // update EAS on shell domains\n    for (int i=0; i<mesh.Domains(); ++i) {\n        FESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n        if (sdom && sdom->IsActive()) sdom->UpdateEAS(ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update EAS\nvoid FEMultiphasicFSISolver::UpdateIncrementsEAS(vector<double>& ui, const bool binc)\n{\n    FEModel& fem = *GetFEModel();\n    \n    FEMesh& mesh = fem.GetMesh();\n    \n    // update EAS on shell domains\n    for (int i=0; i<mesh.Domains(); ++i) {\n        FESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n        if (sdom && sdom->IsActive()) sdom->UpdateIncrementsEAS(ui, binc);\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasicFSISolver::InitStep(double time)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alpha = m_alphaf;\n    tp.beta  = m_beta;\n    tp.gamma = m_gamma;\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n    //    double dt = tp.timeIncrement;\n    //    double ta = (t > 0) ? t - (1-m_alpha)*dt : m_alpha*dt;\n    //    return FESolver::InitStep(ta);\n    return FESolver::InitStep(t);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FEMultiphasicFSISolver::PrepStep()\n{\n    FEModel& fem = *GetFEModel();\n    \n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    \n    FETimeInfo& tp = fem.GetTime();\n    double dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n    \n    // zero total DOFs\n    zero(m_Ui);\n    zero(m_Vi);\n    zero(m_Di);\n    zero(m_Fi);\n    for (int j=0; j<(int)m_nceq.size(); ++j) if (m_nceq[j]) zero(m_Ci[j]);\n    \n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt;\n        ni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n        ni.m_ap = ni.m_at;\n        ni.m_dp = ni.m_dt = ni.m_d0;\n        ni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n            {\n                // initial guess at start of new time step (default)\n                // solid\n                ni.m_at = ni.m_ap*(1-0.5/m_beta) - ni.m_vp/(m_beta*dt);\n                vec3d vs = ni.m_vp + (ni.m_at*m_gamma + ni.m_ap*(1-m_gamma))*dt;\n                ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vs);\n                \n                // solid shell\n                vec3d aqp = ni.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2]);\n                vec3d vqp = ni.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2]);\n                vec3d aqt = aqp*(1-0.5/m_beta) - vqp/(m_beta*dt);\n                ni.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n                vec3d vqt = vqp + (aqt*m_gamma + aqp*(1-m_gamma))*dt;\n                ni.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vqt);\n                \n                // fluid\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awp*(m_gamma-1)/m_gamma);\n                \n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF)*(m_gamma-1)/m_gamma);\n                // update nodal concentration\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofAC+j, ni.get_prev(m_dofAC+j)*(m_gamma-1)/m_gamma);\n            }\n                break;\n                \n            case 1:\n            {\n                // initial guess at start of new time step (Zero Ydot)\n                ni.m_at = vec3d(0,0,0);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2],vec3d(0,0,0));\n                ni.set(m_dofAEF, 0);\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofAC+j, 0);\n                \n                ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], ni.m_vp + ni.m_ap*dt*(1-m_gamma)*m_alphaf);\n                vec3d wp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], wp + awp*dt*(1-m_gamma)*m_alphaf);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt*(1-m_gamma)*m_alphaf);\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofC+j, ni.get_prev(m_dofC+j) + ni.get_prev(m_dofAC+j)*dt*(1-m_gamma)*m_alphaf);\n            }\n                break;\n                \n            case 2:\n            {\n                // initial guess at start of new time step (Same Ydot)\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awp);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF));\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofAC+j, ni.get_prev(m_dofAC+j));\n                \n                ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], ni.m_vp + ni.m_ap*dt);\n                vec3d wp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], wp + awp*dt);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt);\n                for (int j=0; j<MAX_CDOFS; ++j)\n                    ni.set(m_dofC+j, ni.get_prev(m_dofC+j) + ni.get_prev(m_dofAC+j)*dt);\n            }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.PrepStep(ui);\n    }\n    \n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.PrepStep();\n    }\n\n    // do the linear constraints\n    fem.GetLinearConstraintManager().PrepStep();\n    \n    // initialize rigid bodies\n    m_rigidSolver.PrepStep(tp, ui);\n    \n    // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n    // update domain data\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) dom.PreSolveUpdate(tp);\n    }\n    \n    // update model state\n    UpdateModel();\n    \n    // see if we need to do contact augmentations\n    m_baugment = false;\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n        if (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n    }\n    \n    // see if we need to do incompressible augmentations\n    // TODO: Should I do these augmentations in a nlconstraint class instead?\n    int ndom = mesh.Domains();\n    for (int i = 0; i < ndom; ++i)\n    {\n        FEDomain* dom = &mesh.Domain(i);\n        FE3FieldElasticSolidDomain* dom3f = dynamic_cast<FE3FieldElasticSolidDomain*>(dom);\n        if (dom3f && dom3f->DoAugmentations()) m_baugment = true;\n        \n        FE3FieldElasticShellDomain* dom3fs = dynamic_cast<FE3FieldElasticShellDomain*>(dom);\n        if (dom3fs && dom3fs->DoAugmentations()) m_baugment = true;\n    }\n    \n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasicFSISolver::Quasin()\n{\n    FEModel& fem = *GetFEModel();\n    \n    vector<double> u0(m_neq);\n    vector<double> Rold(m_neq);\n    \n    // convergence norms\n    double    normR1;        // residual norm\n    double    normE1;        // energy norm\n    double    normD;        // displacement norm\n    double    normd;        // displacement increment norm\n    double    normV;        // velocity norm\n    double    normv;        // velocity increment norm\n    double    normRi = 0;    // initial residual norm\n    double    normDi = 0;    // initial displacement norm\n    double    normVi = 0;    // initial velocity norm\n    double    normEi = 0; // initial energy norm\n    double    normEm = 0;    // max energy norm\n    double    normFi = 0;    // initial dilatation norm\n    double    normF;        // current dilatation norm\n    double    normf;        // incremement dilatation norm\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioMultiphasicFSI::GetVariableName(FEBioMultiphasicFSI::FLUID_CONCENTRATION));\n    \n    // solute convergence data\n    vector<double>    normCi(MAX_CDOFS);    // initial concentration norm\n    vector<double>    normC(MAX_CDOFS);    // current concentration norm\n    vector<double>    normc(MAX_CDOFS);    // incremement concentration norm\n    \n    // prepare for the first iteration\n    const FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n    // init QN method\n    if (QNInit() == false) return false;\n    \n    // loop until converged or when max nr of reformations reached\n    bool bconv = false;        // convergence flag\n    do\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n        \n        // solve the equations (returns line search; solution stored in m_ui)\n        double s = QNSolve();\n        \n        // extract the velocity and dilatation increments\n        GetDisplacementData(m_di, m_ui);\n        GetVelocityData(m_vi, m_ui);\n        GetDilatationData(m_fi, m_ui);\n        \n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normDi = fabs(m_di*m_di);\n            normVi = fabs(m_vi*m_vi);\n            normFi = fabs(m_fi*m_fi);\n            normEm = normEi;\n        }\n        \n        // calculate norms\n        // update all degrees of freedom\n        for (int i=0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n        \n        // update displacements\n        for (int i = 0; i<m_ndeq; ++i) m_Di[i] += s*m_di[i];\n        \n        // update velocities\n        for (int i = 0; i<m_nveq; ++i) m_Vi[i] += s*m_vi[i];\n        \n        // update dilatations\n        for (int i = 0; i<m_nfeq; ++i) m_Fi[i] += s*m_fi[i];\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n        normd  = (m_di*m_di)*(s*s);\n        normD  = m_Di*m_Di;\n        normv  = (m_vi*m_vi)*(s*s);\n        normV  = m_Vi*m_Vi;\n        normf  = (m_fi*m_fi)*(s*s);\n        normF  = m_Fi*m_Fi;\n        normE1 = s*fabs(m_ui*m_R1);\n        \n        // check for nans\n        if (ISNAN(normR1)) throw NANInResidualDetected();\n        \n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check displacement norm\n        if ((m_Dtol > 0) && (normd  > (m_Dtol*m_Dtol)*normD )) bconv = false;\n        \n        // check velocity norm\n        if ((m_Vtol > 0) && (normv  > (m_Vtol*m_Vtol)*normV )) bconv = false;\n        \n        // check dilatation norm\n        if ((m_Ftol > 0) && (normf  > (m_Ftol*m_Ftol)*normF )) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n        if ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        if (normE1 > normEm) bconv = false;\n        \n        // check solute convergence\n        // extract the concentration increments\n        for (int j = 0; j<(int)m_nceq.size(); ++j) {\n            if (m_nceq[j]) {\n                GetConcentrationData(m_ci[j], m_ui,j);\n                \n                // set initial norm\n                if (m_niter == 0)\n                    normCi[j] = fabs(m_ci[j]*m_ci[j]);\n                \n                // update total concentration\n                for (int i = 0; i<m_nceq[j]; ++i) m_Ci[j][i] += s*m_ci[j][i];\n                \n                // calculate norms\n                normC[j] = m_Ci[j]*m_Ci[j];\n                normc[j] = (m_ci[j]*m_ci[j])*(s*s);\n                \n            }\n        }\n        \n        // check convergence\n        if (m_Ctol > 0) {\n            for (int j = 0; j<(int)m_nceq.size(); ++j)\n                if (m_nceq[j]) bconv = bconv && (normc[j] <= (m_Ctol*m_Ctol)*normC[j]);\n        }\n        \n        // print convergence summary\n        feLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n        feLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n        if (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        feLog(\"\\t   displacement     %15le %15le %15le \\n\", normDi, normd ,(m_Dtol*m_Dtol)*normD );\n        feLog(\"\\t   velocity         %15le %15le %15le \\n\", normVi, normv ,(m_Vtol*m_Vtol)*normV );\n        feLog(\"\\t   dilatation       %15le %15le %15le \\n\", normFi, normf ,(m_Ftol*m_Ftol)*normF );\n        for (int j = 0; j<(int)m_nceq.size(); ++j) {\n            if (m_nceq[j])\n                feLog(\"\\t solute %d concentration %15le %15le %15le\\n\", j+1, normCi[j], normc[j] ,(m_Ctol*m_Ctol)*normC[j] );\n        }\n        \n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n        // see if we have exceeded the max residual\n        if ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n        {\n            // doesn't look like we're getting anywhere, so let's retry the time step\n            throw MaxResidualError();\n        }\n        \n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n            if (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n                feLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n                QNForceReform(true);\n            }\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n                feLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                normDi = normd;\n                normVi = normv;\n                normFi = normf;\n                for (int j = 0; j<(int)m_nceq.size(); ++j)\n                    if (m_nceq[j]) normCi[j] = normc[j];\n                QNForceReform(true);\n            }\n            \n            // Do the QN update (This may also do a stiffness reformation if necessary)\n            bool bret = QNUpdate();\n            \n            // something went wrong with the update, so we'll need to break\n            if (bret == false) break;\n        }\n        else if (m_baugment)\n        {\n            // Do augmentations\n            bconv = DoAugmentations();\n        }\n        \n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        UpdateIncrementsEAS(m_Ui, false);\n        UpdateIncrements(m_Ut, m_Ui, true);\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEMultiphasicFSISolver::StiffnessMatrix()\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    FESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), m_alphaf, m_nreq);\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n            FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(&dom);\n            FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n            if (fdom) fdom->StiffnessMatrix(LS);\n            else if (fsidom) fsidom->StiffnessMatrix(LS);\n            else if (bfsidom) bfsidom->StiffnessMatrix(LS);\n            else if (mfsidom) mfsidom->StiffnessMatrix(LS);\n            else if (edom) edom->StiffnessMatrix(LS);\n        }\n    }\n    \n    // calculate the body force stiffness matrix for each domain\n    // but not for solid domains (since they have no mass in FSI)\n    int NBL = fem.ModelLoads();\n    for (int j = 0; j<NBL; ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEDomain* dom = pbf->Domain(i);\n                if (dom->IsActive())\n                {\n                    FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(dom);\n                    FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(dom);\n                    FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(dom);\n                    FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(dom);\n                    FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(dom);\n                    if (fdom) fdom->BodyForceStiffness(LS, *pbf);\n                    else if (fsidom) fsidom->BodyForceStiffness(LS, *pbf);\n                    else if (bfsidom) bfsidom->BodyForceStiffness(LS, *pbf);\n                    else if (mfsidom) mfsidom->BodyForceStiffness(LS, *pbf);\n                    else if (edom)\n                    {\n                        FESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom->GetMaterial());\n                        if (mat && (mat->IsRigid()==false)) edom->BodyForceStiffness(LS, *pbf);\n                    }\n                }\n            }\n        }\n    }\n    \n    // TODO: add body force stiffness for rigid bodies\n    \n    // Add mass matrix\n    //    FEAnalysis* pstep = fem.GetCurrentStep();\n    //    if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::DYNAMIC)\n    {\n        // scale factor\n        double dt = tp.timeIncrement;\n        double a = tp.alpham / (m_beta*dt*dt);\n        // loop over all domains (except rigid)\n        for (int i = 0; i<mesh.Domains(); ++i)\n        {\n            FEDomain& dom = mesh.Domain(i);\n            if (dom.IsActive())\n            {\n                FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n                FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n                FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n                FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(&dom);\n                FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n                if (fdom) fdom->MassMatrix(LS);\n                else if (fsidom) fsidom->MassMatrix(LS);\n                else if (bfsidom) bfsidom->MassMatrix(LS);\n                else if (mfsidom) mfsidom->MassMatrix(LS);\n                else if (edom)\n                {\n                    FESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n                    if (mat && (mat->IsRigid() == false)) edom->MassMatrix(LS, a);\n                }\n            }\n        }\n        \n        m_rigidSolver.RigidMassMatrix(LS, tp);\n    }\n    \n    // calculate contact stiffness\n    ContactStiffness(LS);\n    \n    // calculate nonlinear constraint stiffness\n    // note that this is the contribution of the\n    // constraints enforced with augmented lagrangian\n    NonLinearConstraintStiffness(LS, tp);\n    \n    // calculate the stiffness contributions for the rigid forces\n    for (int i = 0; i<fem.ModelLoads(); ++i) fem.ModelLoad(i)->StiffnessMatrix(LS);\n    \n    // add contributions from rigid bodies\n    m_rigidSolver.StiffnessMatrix(*m_pK, tp);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEMultiphasicFSISolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FEMultiphasicFSISolver::ContactStiffness(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEMultiphasicFSISolver::ContactForces(FEGlobalVector& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry\n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEMultiphasicFSISolver::Residual(vector<double>& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the time information\n    const FETimeInfo& tp = fem.GetTime();\n    \n    // initialize residual with concentrated nodal loads\n    zero(R);\n    \n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n    FEResidualVector RHS(fem, R, m_Fr);\n    \n    // zero rigid body reaction forces\n    m_rigidSolver.Residual();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n            FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(&dom);\n            FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n            if (fdom) fdom->InternalForces(RHS);\n            else if (fsidom) fsidom->InternalForces(RHS);\n            else if (bfsidom) bfsidom->InternalForces(RHS);\n            else if (mfsidom) mfsidom->InternalForces(RHS);\n            else if (edom)\n            {\n                FESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n                if (mat && (mat->IsRigid() == false)) edom->InternalForces(RHS);\n            }\n        }\n    }\n    \n    // calculate the body forces\n    for (int j = 0; j<fem.ModelLoads(); ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEDomain* dom = pbf->Domain(i);\n                if (dom->IsActive())\n                {\n                    FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(dom);\n                    FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(dom);\n                    FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(dom);\n                    FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(dom);\n                    FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(dom);\n                    if (fdom) fdom->BodyForce(RHS, *pbf);\n                    else if (fsidom) fsidom->BodyForce(RHS, *pbf);\n                    else if (bfsidom) bfsidom->BodyForce(RHS, *pbf);\n                    else if (mfsidom) mfsidom->BodyForce(RHS, *pbf);\n                    else if (edom)\n                    {\n                        FESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom->GetMaterial());\n                        if (mat && (mat->IsRigid()==false)) edom->BodyForce(RHS, *pbf);\n                    }\n                }\n            }\n        }\n    }\n    \n    // allocate F\n    vector<double> F;\n    \n    FEAnalysis* pstep = fem.GetCurrentStep();\n    \n    // calculate inertial forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEFluidFSIDomain* fsidom = dynamic_cast<FEFluidFSIDomain*>(&dom);\n            FEBiphasicFSIDomain* bfsidom = dynamic_cast<FEBiphasicFSIDomain*>(&dom);\n            FEMultiphasicFSIDomain* mfsidom = dynamic_cast<FEMultiphasicFSIDomain*>(&dom);\n            FEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&dom);\n            if (fdom) fdom->InertialForces(RHS);\n            else if (fsidom) fsidom->InertialForces(RHS);\n            else if (bfsidom) bfsidom->InertialForces(RHS);\n            else if (mfsidom) mfsidom->InertialForces(RHS);\n            else if (edom && (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::DYNAMIC))\n            {\n                FESolidMaterial* mat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n                if (mat && (mat->IsRigid()==false)) edom->InertialForces(RHS, F);\n            }\n        }\n    }\n    \n    // update rigid bodies\n    if (pstep->m_nanalysis == FEMultiphasicFSIAnalysis::DYNAMIC) m_rigidSolver.InertialForces(RHS, tp);\n    \n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n    \n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i=0; i<NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive()) mli.LoadVector(RHS);\n    }\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.set_load(m_dofU[0], 0);\n        node.set_load(m_dofU[1], 0);\n        node.set_load(m_dofU[2], 0);\n        \n        int n;\n        if ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n    }\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces\nvoid FEMultiphasicFSISolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->LoadVector(R, tp);\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEMultiphasicFSISolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEGlobalVector.h>\n#include <FEBioMech/FERigidSolver.h>\n#include <FECore/FEDofList.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEFluidFSISolver class solves fluid-FSI problems\n//! It can deal with quasi-static and dynamic problems\n//!\nclass FEBIOFLUID_API FEMultiphasicFSISolver : public FENewtonSolver\n{\npublic:\n    //! constructor\n    FEMultiphasicFSISolver(FEModel* pfem);\n    \n    //! destructor\n    ~FEMultiphasicFSISolver();\n    \n    //! serialize data to/from dump file\n    void Serialize(DumpStream& ar) override;\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n    \n    //! Generate warnings if needed\n    void SolverWarnings();\n    \n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n    \n    //! update nodal positions, velocities, accelerations, etc.\n    void UpdateKinematics(vector<double>& ui);\n    \n    //! Update EAS\n    void UpdateEAS(vector<double>& ui);\n    void UpdateIncrementsEAS(vector<double>& ui, const bool binc);\n    \n    //! update DOF increments\n    virtual void UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap);\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix() override;\n    \n    //! contact stiffness\n    void ContactStiffness(FELinearSystem& LS);\n    \n    //! calculates stiffness contributon of nonlinear constraints\n    void NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n    \n    //{ --- Residual routines ---\n    \n    //! Calculate the contact forces\n    void ContactForces(FEGlobalVector& R);\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n    \n    //! Calculate nonlinear constraint forces\n    void NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n    \nprotected:\n    void GetDisplacementData(vector<double>& xi, vector<double>& ui);\n    void GetVelocityData(vector<double>& vi, vector<double>& ui);\n    void GetDilatationData(vector<double>& ei, vector<double>& ui);\n    void GetConcentrationData(vector<double>& ci, vector<double>& ui, const int sol);\n    \npublic:\n    // convergence tolerances\n    double    m_Dtol;            //!< displacement tolerance\n    double    m_Vtol;            //!< velocity tolerance\n    double    m_Ftol;            //!< dilatation tolerance\n    double  m_Ctol;     //!< concentration tolerance\n    double  m_minJf;        //!< minimum allowable compression ratio\n    bool    m_forcePositive;    //!< force conentrations to remain positive\n    \npublic:\n    // equation numbers\n    int     m_nreq;         //!< start of rigid body equations\n    int        m_ndeq;            //!< number of equations related to displacement dofs\n    int        m_nveq;            //!< number of equations related to velocity dofs\n    int        m_nfeq;            //!< number of equations related to dilatation dofs\n    int            m_nseq;        //!< total number of concentration dofs\n    vector<int> m_nceq;     //!< number of equations related to concentration dofs\n    \npublic:\n    vector<double> m_Fr;    //!< nodal reaction forces\n    vector<double> m_di;    //!< displacement increment vector\n    vector<double> m_Di;    //!< Total displacement vector for iteration\n    vector<double> m_vi;    //!< velocity increment vector\n    vector<double> m_Vi;    //!< Total velocity vector for iteration\n    vector<double> m_fi;    //!< dilatation increment vector\n    vector<double> m_Fi;    //!< Total dilatation vector for iteration\n    \n    // solute data\n    vector< vector<double> >    m_ci;    //!< concentration increment vector\n    vector< vector<double> >    m_Ci;    //!< Total concentration vector for iteration\n    \n    // generalized alpha method\n    double  m_rhoi;         //!< spectral radius (rho infinity)\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_beta;         //!< beta\n    double  m_gamma;        //!< gamma\n    int     m_pred;         //!< predictor method\n    int     m_order;        //!< generalized-alpha integration order\n    \nprotected:\n    FEDofList    m_dofU;        // solid displacement\n    FEDofList    m_dofV;        // solid velocity\n    FEDofList    m_dofSU;    // shell displacement\n    FEDofList    m_dofSV;    // shell velocity\n    FEDofList    m_dofSA;    // shell acceleration\n    FEDofList    m_dofR;        // rigid body rotations\n    FEDofList    m_dofVF;    // fluid velocity\n    FEDofList    m_dofAF;    // material time derivative of fluid velocity\n    FEDofList    m_dofW;        // fluid velocity relative to solid\n    FEDofList    m_dofAW;    // material time derivative of fluid velocity relative to solid\n    FEDofList    m_dofEF;    // fluid dilatation\n    int          m_dofAEF;    // material time derivative of fluid dilatation\n    int          m_dofC;\n    int          m_dofAC;\n    \nprotected:\n    FERigidSolverNew    m_rigidSolver;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FENewtonianFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonianFluid.h\"\n#include \"FEFluid.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FENewtonianFluid, FEViscousFluid)\n    ADD_PARAMETER(m_kappa, FE_RANGE_GREATER_OR_EQUAL(0.0), \"kappa\")->setUnits(UNIT_VISCOSITY)->setLongName(\"bulk viscosity\");\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"   )->setUnits(UNIT_VISCOSITY)->setLongName(\"shear viscosity\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFENewtonianFluid::FENewtonianFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_kappa = 0;\n    m_mu = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FENewtonianFluid::Init()\n{\n    return FEViscousFluid::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonianFluid::Serialize(DumpStream& ar)\n{\n    FEViscousFluid::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FENewtonianFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    \n    mat3ds D = vt.RateOfDeformation();\n    \n    double mu = ShearViscosity(pt);\n    double kappa = BulkViscosity(pt);\n    \n    mat3ds s = mat3dd(1.0)*(D.tr()*(kappa - 2.*mu/3.)) + D*(2*mu);\n        \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FENewtonianFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FENewtonianFluid::Tangent_RateOfDeformation(FEMaterialPoint& mp)\n{\n    mat3dd I(1.0);\n    double mu = ShearViscosity(mp);\n    double kappa = BulkViscosity(mp);\n    \n    tens4ds c = dyad1s(I)*(kappa - 2.*mu/3.) + dyad4s(I)*(2*mu);\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic shear viscosity\ndouble FENewtonianFluid::ShearViscosity(FEMaterialPoint& mp)\n{\n    return m_mu;\n}\n\n//! derivative of shear viscosity w.r.t. strain rate\ndouble FENewtonianFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\treturn 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FENewtonianFluid::BulkViscosity(FEMaterialPoint& mp)\n{\n    return m_kappa;\n}\n"
  },
  {
    "path": "FEBioFluid/FENewtonianFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Newtonian fluid\n\nclass FEBIOFLUID_API FENewtonianFluid :\tpublic FEViscousFluid\n{\npublic:\n    //! constructor\n    FENewtonianFluid(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n\n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n\npublic:\n    double\tm_kappa;\t//!< bulk viscosity\n    double\tm_mu;\t\t//!< shear viscosity\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FENewtonianRealVapor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonianRealVapor.h\"\n#include \"FEFluid.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEThermoFluid.h\"\n#include \"FERealVapor.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FENewtonianRealVapor, FEThermoViscousFluid)\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"   )->setUnits(UNIT_VISCOSITY)->setLongName(\"referential shear viscosity\");\n// properties\n    ADD_PROPERTY(m_esat , \"esat\",FEProperty::Optional)->SetLongName(\"saturation dilatation\");\n    ADD_PROPERTY(m_musat, \"musat\",FEProperty::Optional)->SetLongName(\"normalized saturation shear viscosity\");\n    ADD_PROPERTY(m_C[0] , \"C0\", FEProperty::Optional)->SetLongName(\"1st mu virial coeff\");\n    ADD_PROPERTY(m_C[1] , \"C1\", FEProperty::Optional)->SetLongName(\"2nd mu virial coeff\");\n    ADD_PROPERTY(m_C[2] , \"C2\", FEProperty::Optional)->SetLongName(\"3rd mu virial coeff\");\n    ADD_PROPERTY(m_C[3] , \"C3\", FEProperty::Optional)->SetLongName(\"4th mu virial coeff\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFENewtonianRealVapor::FENewtonianRealVapor(FEModel* pfem) : FEThermoViscousFluid(pfem)\n{\n    m_kappa = 0;\n    m_mu = 0;\n    m_Tr = 0;\n    m_nvc = 0;\n    m_esat = nullptr;\n    m_musat = nullptr;\n    for (int k=0; k<MAX_NVC; ++k) m_C[k] = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FENewtonianRealVapor::Init()\n{\n    if (m_musat) {\n        m_Tr = GetGlobalConstant(\"T\");\n        \n        if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    }\n    \n    if (m_esat) m_esat->Init();\n    else {\n        FECoreBase* pMat = GetAncestor();\n        FEThermoFluid* pFluid = dynamic_cast<FEThermoFluid*>(pMat);\n        if (pFluid) {\n            FERealVapor* pRV = dynamic_cast<FERealVapor*>(pFluid->GetElastic());\n            if (pRV) {\n                m_esat = pRV->m_esat;\n                m_esat->Init();\n                m_Tc = pRV->m_Tc;\n                m_alpha = pRV->m_alpha;\n            }\n            else return false;\n        }\n        else return false;\n    }\n    if (m_musat) m_musat->Init();\n    m_nvc = 0;\n    for (int k=0; k<MAX_NVC; ++k) {\n        if (m_C[k]) {\n            m_C[k]->Init();\n            ++m_nvc;\n        }\n    }\n\n    return FEViscousFluid::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonianRealVapor::Serialize(DumpStream& ar)\n{\n    FEViscousFluid::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FENewtonianRealVapor::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    \n    mat3ds D = vt.RateOfDeformation();\n    \n    double mu = ShearViscosity(pt);\n    double kappa = BulkViscosity(pt);\n    \n    mat3ds s = mat3dd(1.0)*(D.tr()*(kappa - 2.*mu/3.)) + D*(2*mu);\n        \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FENewtonianRealVapor::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = pf.RateOfDeformation();\n    \n    // evaluate ∂µ/∂J\n    double dmuJ = TangentShearViscosityStrain(mp);\n    double dkpJ = TangentBulkViscosityStrain(mp);\n\n    mat3ds dsJ = mat3dd(1.0)*(D.tr()*(dkpJ - 2.*dmuJ/3.)) + D*(2*dmuJ);\n    \n    return dsJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FENewtonianRealVapor::Tangent_RateOfDeformation(FEMaterialPoint& mp)\n{\n    mat3dd I(1.0);\n    double mu = ShearViscosity(mp);\n    double kappa = BulkViscosity(mp);\n    \n    tens4ds c = dyad1s(I)*(kappa - 2.*mu/3.) + dyad4s(I)*(2*mu);\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to temperature T\nmat3ds FENewtonianRealVapor::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = pf.RateOfDeformation();\n\n    // evaluate ∂µ/∂T\n    double dmuT = TangentShearViscosityTemperature(mp);\n    double dkpT = TangentBulkViscosityTemperature(mp);\n    \n    mat3ds dsT = mat3dd(1.0)*(D.tr()*(dkpT - 2.*dmuT/3.)) + D*(2*dmuT);\n    \n    return dsT;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic shear viscosity\ndouble FENewtonianRealVapor::ShearViscosity(FEMaterialPoint& mp)\n{\n    double mu = 1.0;\n    if (m_musat) {\n        FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n        FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n        double T = tf.m_T + m_Tr;\n        double That = T/m_Tr;\n        double J = 1 + pf.m_ef;\n        double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n        double q = log(1+pow(y,m_alpha));\n        mu = m_musat->value(q);\n        double Jsat = exp(m_esat->value(q));\n        double x = 1 - Jsat/J;\n        for (int k=0; k<m_nvc; ++k) mu += m_C[k]->value(q)*pow(x,k+1);\n    }\n    return mu*m_mu;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic shear viscosity tangent w.r.t. temperature\ndouble FENewtonianRealVapor::TangentShearViscosityTemperature(FEMaterialPoint& mp)\n{\n    double d = 1e-6*m_Tr;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n    \n    // evaluate ∂µ/∂T\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef;\n    ft->m_T = tf.m_T+d;\n    FEMaterialPoint tmp(ft);\n    double mup = ShearViscosity(tmp);\n    ft->m_T = tf.m_T-d;\n    double mum = ShearViscosity(tmp);\n    delete ft;\n    double dmuT = (mup - mum)/(2*d);\n    \n    return dmuT;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic shear viscosity tangent w.r.t. J\ndouble FENewtonianRealVapor::TangentShearViscosityStrain(FEMaterialPoint& mp)\n{\n    double d = 1e-6;\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    FEFluidMaterialPoint& pf = *mp.ExtractData<FEFluidMaterialPoint>();\n    \n    // evaluate ∂µ/∂J\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = pf.m_ef+d;\n    ft->m_T = tf.m_T;\n    FEMaterialPoint tmp(ft);\n    double mup = ShearViscosity(tmp);\n    fp->m_ef = pf.m_ef-d;\n    double mum = ShearViscosity(tmp);\n    delete ft;\n    double dmuJ = (mup - mum)/(2*d);\n\n    return dmuJ;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FENewtonianRealVapor::BulkViscosity(FEMaterialPoint& mp)\n{\n    double kappa = 0;\n    return kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity tangent w.r.t. temperature\ndouble FENewtonianRealVapor::TangentBulkViscosityTemperature(FEMaterialPoint& mp)\n{\n    double dkpT = 0;\n    return dkpT;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity tangent w.r.t. J\ndouble FENewtonianRealVapor::TangentBulkViscosityStrain(FEMaterialPoint& mp)\n{\n    double dkpJ = 0;\n    return dkpJ;\n}\n"
  },
  {
    "path": "FEBioFluid/FENewtonianRealVapor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEThermoViscousFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Newtonian viscous real vapor\n\nclass FEBIOFLUID_API FENewtonianRealVapor :\tpublic FEThermoViscousFluid\n{\npublic:\n    enum { MAX_NVC = 4 };\n    \npublic:\n    //! constructor\n    FENewtonianRealVapor(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to temperature\n    mat3ds Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    double TangentShearViscosityTemperature(FEMaterialPoint& mp);\n    double TangentShearViscosityStrain(FEMaterialPoint& mp);\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    double TangentBulkViscosityTemperature(FEMaterialPoint& mp);\n    double TangentBulkViscosityStrain(FEMaterialPoint& mp);\n\npublic:\n    double\tm_kappa;\t//!< bulk viscosity\n    double\tm_mu;\t\t//!< shear viscosity\n    double  m_Tr;       //!< referential temperature\n    double          m_Tc;           //!< normalized critical temperature (Tc/Tr)\n    double          m_alpha;        //!< exponent alpha used for calculating temperature map\n    int             m_nvc;          //!< number of virial coefficients for isochoric specific heat capacity\n    FEFunction1D*   m_esat;         //!< dilatation on saturation curve\n    FEFunction1D*   m_musat;        //!< normalized shear viscosity vs normalized temperature on saturation curve\n    FEFunction1D*   m_C[MAX_NVC];   //!< non-dimensional pressure coefficient (multiply by m_Pr to get actual value)\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FENewtonianThermoFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonianThermoFluid.h\"\n#include \"FEFluid.h\"\n#include \"FEBiphasicFSI.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FENewtonianThermoFluid, FEThermoViscousFluid)\n    ADD_PARAMETER(m_kappa, FE_RANGE_GREATER_OR_EQUAL(0.0), \"kappa\")->setUnits(UNIT_VISCOSITY)->setLongName(\"bulk viscosity\");\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"   )->setUnits(UNIT_VISCOSITY)->setLongName(\"shear viscosity\");\n// properties\n    ADD_PROPERTY(m_kappahat, \"khat\" ,FEProperty::Optional)->SetLongName(\"normalized bulk viscosity\");\n    ADD_PROPERTY(m_muhat   , \"muhat\",FEProperty::Optional)->SetLongName(\"normalized shear viscosity\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFENewtonianThermoFluid::FENewtonianThermoFluid(FEModel* pfem) : FEThermoViscousFluid(pfem)\n{\n    m_kappa = 0;\n    m_mu = 0;\n    m_Tr = 0;\n    m_kappahat = nullptr;\n    m_muhat = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FENewtonianThermoFluid::Init()\n{\n    if (m_kappahat || m_muhat) {\n        m_Tr = GetGlobalConstant(\"T\");\n        \n        if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    }\n    \n    if (m_kappahat) m_kappahat->Init();\n    if (m_muhat) m_muhat->Init();\n    \n    return FEViscousFluid::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonianThermoFluid::Serialize(DumpStream& ar)\n{\n    FEViscousFluid::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FENewtonianThermoFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    \n    mat3ds D = vt.RateOfDeformation();\n    \n    double mu = ShearViscosity(pt);\n    double kappa = BulkViscosity(pt);\n    \n    mat3ds s = mat3dd(1.0)*(D.tr()*(kappa - 2.*mu/3.)) + D*(2*mu);\n        \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FENewtonianThermoFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FENewtonianThermoFluid::Tangent_RateOfDeformation(FEMaterialPoint& mp)\n{\n    mat3dd I(1.0);\n    double mu = ShearViscosity(mp);\n    double kappa = BulkViscosity(mp);\n    \n    tens4ds c = dyad1s(I)*(kappa - 2.*mu/3.) + dyad4s(I)*(2*mu);\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to temperature T\nmat3ds FENewtonianThermoFluid::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n    \n    mat3ds D = vt.RateOfDeformation();\n    \n    double dmu = TangentShearViscosityTemperature(mp);\n    double dkappa = TangentBulkViscosityTemperature(mp);\n    \n    mat3ds ds = mat3dd(1.0)*(D.tr()*(dkappa - 2.*dmu/3.)) + D*(2*dmu);\n        \n    return ds;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic shear viscosity\ndouble FENewtonianThermoFluid::ShearViscosity(FEMaterialPoint& mp)\n{\n    double mu = m_mu;\n    if (m_muhat) {\n        FEThermoFluidMaterialPoint* tf = mp.ExtractData<FEThermoFluidMaterialPoint>();\n        if (tf) {\n            double That = (tf->m_T+m_Tr)/m_Tr;\n            mu *= m_muhat->value(That);\n        }\n    }\n    return mu;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic shear viscosity tangent w.r.t. temperature\ndouble FENewtonianThermoFluid::TangentShearViscosityTemperature(FEMaterialPoint& mp)\n{\n    double dmu = 0;\n    if (m_muhat) {\n        FEThermoFluidMaterialPoint* tf = mp.ExtractData<FEThermoFluidMaterialPoint>();\n        if (tf) {\n            double That = (tf->m_T+m_Tr)/m_Tr;\n            dmu = m_muhat->derive(That)*m_mu/m_Tr;\n        }\n    }\n    return dmu;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FENewtonianThermoFluid::BulkViscosity(FEMaterialPoint& mp)\n{\n    double kappa = m_kappa;\n    if (m_kappa) {\n        FEThermoFluidMaterialPoint* tf = mp.ExtractData<FEThermoFluidMaterialPoint>();\n        if (tf) {\n            double That = (tf->m_T+m_Tr)/m_Tr;\n            kappa *= m_kappahat->value(That);\n        }\n    }\n    return kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity tangent w.r.t. temperature\ndouble FENewtonianThermoFluid::TangentBulkViscosityTemperature(FEMaterialPoint& mp)\n{\n    double dkappa = 0;\n    if (m_kappa) {\n        FEThermoFluidMaterialPoint* tf = mp.ExtractData<FEThermoFluidMaterialPoint>();\n        if (tf) {\n            double That = (tf->m_T+m_Tr)/m_Tr;\n            dkappa = m_kappahat->derive(That)*m_kappa/m_Tr;\n        }\n    }\n    return dkappa;\n}\n"
  },
  {
    "path": "FEBioFluid/FENewtonianThermoFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEThermoViscousFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Newtonian fluid\n\nclass FEBIOFLUID_API FENewtonianThermoFluid :\tpublic FEThermoViscousFluid\n{\npublic:\n    //! constructor\n    FENewtonianThermoFluid(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to temperature\n    mat3ds Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    double TangentShearViscosityTemperature(FEMaterialPoint& mp);\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    double TangentBulkViscosityTemperature(FEMaterialPoint& mp);\n\npublic:\n    double\tm_kappa;\t//!< bulk viscosity\n    double\tm_mu;\t\t//!< shear viscosity\n    double  m_Tr;       //!< referential temperature\n    FEFunction1D*   m_kappahat; //!< normalized bulk viscosity vs normalized temperature\n    FEFunction1D*   m_muhat;    //!< normalized shear viscosity vs normalized temperature\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FENonlinearElasticFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FENonlinearElasticFluid.h\"\n#include \"FEFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nFENonlinearElasticFluid::FENonlinearElasticFluid(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_k = 0;\n    m_rhor = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FENonlinearElasticFluid::Init()\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FENonlinearElasticFluid::Serialize(DumpStream& ar)\n{\n    ar& m_k& m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FENonlinearElasticFluid::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double p =m_k*(1/(1+fp.m_ef)-1);\n    \n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FENonlinearElasticFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    return -m_k/pow(1+fp.m_ef,2);\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FENonlinearElasticFluid::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    return 2*m_k/pow(1+fp.m_ef,3);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FENonlinearElasticFluid::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FENonlinearElasticFluid::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FENonlinearElasticFluid::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FENonlinearElasticFluid::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double a = m_k/m_rhor*(fp.m_ef-log(1+fp.m_ef));\n    return a;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FENonlinearElasticFluid::SpecificEntropy(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FENonlinearElasticFluid::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    return SpecificFreeEnergy(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FENonlinearElasticFluid::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FENonlinearElasticFluid::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FENonlinearElasticFluid::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FENonlinearElasticFluid::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FENonlinearElasticFluid::Dilatation(const double T, const double p, double& e)\n{\n    e = 1.0/(1+p/m_k)-1.0;\n    return true;\n}\n"
  },
  {
    "path": "FEBioFluid/FENonlinearElasticFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Linear elastic fluid\n//!\nclass FEBIOFLUID_API FENonlinearElasticFluid : public FEElasticFluid\n{\npublic:\n    FENonlinearElasticFluid(FEModel* pfem);\n    ~FENonlinearElasticFluid() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    // serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n    \npublic:\n    double      m_k;        //!< bulk modulus\n    double      m_rhor;     //!< true density\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPolarFluid.h\"\n#include \"FELinearElasticFluid.h\"\n#include \"FENonlinearElasticFluid.h\"\n#include \"FELogNonlinearElasticFluid.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPolarFluid, FEPolarFluidMaterial)\n// material properties\n    ADD_PARAMETER(m_k   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_kg   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"kg\");\n    ADD_PROPERTY(m_pElastic, \"elastic\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEPolarFluid\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluidFSI constructor\n\nFEPolarFluid::FEPolarFluid(FEModel* pfem) : FEPolarFluidMaterial(pfem)\n{\n    m_kg = 0;\n    m_k = 0;\n    m_pElastic = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEPolarFluid::CreateMaterialPointData()\n{\n    return new FEPolarFluidMaterialPoint(new FEFluidMaterialPoint());\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FEPolarFluid::Init()\n{\n    m_Tr = GetGlobalConstant(\"T\");\n    if (m_pElastic == nullptr) {\n        m_pElastic = fecore_alloc(FELinearElasticFluid, GetFEModel());\n    }\n    FELinearElasticFluid* pLN = dynamic_cast<FELinearElasticFluid*>(m_pElastic);\n    FENonlinearElasticFluid* pNL = dynamic_cast<FENonlinearElasticFluid*>(m_pElastic);\n    FELogNonlinearElasticFluid* pLNL = dynamic_cast<FELogNonlinearElasticFluid*>(m_pElastic);\n    if (pLN) {\n        pLN->m_k = m_k;\n        pLN->m_rhor = m_rhor;\n    }\n    else if (pNL) {\n        pNL->m_k = m_k;\n        pNL->m_rhor = m_rhor;\n    }\n    else if (pLNL) {\n        pLNL->m_k = m_k;\n        pLNL->m_rhor = m_rhor;\n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluid::Serialize(DumpStream& ar)\n{\n    FEPolarFluidMaterial::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    ar & m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk modulus\ndouble FEPolarFluid::BulkModulus(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n    return -(vt.m_ef+1)*Tangent_Pressure_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure\ndouble FEPolarFluid::Pressure(FEMaterialPoint& mp)\n{\n    return m_pElastic->Pressure(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! elastic pressure from dilatation\ndouble FEPolarFluid::Pressure(const double e, const double T)\n{\n    return m_pElastic->Pressure(e, T);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPolarFluid::Tangent_Pressure_Strain(FEMaterialPoint& mp)\n{\n    return m_pElastic->Tangent_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPolarFluid::Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp)\n{\n    return m_pElastic->Tangent_Strain_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a fluid material is the sum of the fluid pressure\n//! and the viscous stress.\n\nmat3ds FEPolarFluid::Stress(FEMaterialPoint& mp)\n{\n    // calculate solid material stress\n    mat3ds s = GetViscous()->Stress(mp);\n    \n    double p = Pressure(mp);\n    \n    // add fluid pressure\n    s.xx() -= p;\n    s.yy() -= p;\n    s.zz() -= p;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent of stress with respect to strain J of a fluid material is the\n//! sum of the tangent of the fluid pressure and that of the viscous stress.\n\nmat3ds FEPolarFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    // get tangent of viscous stress\n    mat3ds sJ = GetViscous()->Tangent_Strain(mp);\n    \n    // add tangent of fluid pressure\n    double dp = Tangent_Pressure_Strain(mp);\n    sJ.xx() -= dp;\n    sJ.yy() -= dp;\n    sJ.zz() -= dp;\n    \n    return sJ;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density (per reference volume)\ndouble FEPolarFluid::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    double sed = m_k*(fp.m_ef-log(fp.m_ef+1));\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! invert pressure-dilatation relation\nbool FEPolarFluid::Dilatation(const double T, const double p, double& e)\n{\n    e = -p/m_k;\n    return true;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#pragma once\n#include \"FEPolarFluidMaterial.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEElasticFluid.h\"\n#include \"FEPolarFluidMaterialPoint.h\"\n#include <FEBioMix/FEBiphasic.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for polar fluid materials.\n\n//! NOTE: This inherits from FEBiphasicInterface in order to override the GetActualFluidPressure,\n//!       which is used in FEReactionRateExpSED and FEReactionRateHuiskes.\n//!       Note sure yet if there is a better alternative.\n\nclass FEBIOFLUID_API FEPolarFluid : public FEPolarFluidMaterial, public FEBiphasicInterface\n{\npublic:\n    FEPolarFluid(FEModel* pfem);\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \npublic:\n    //! performs initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! elastic pressure\n    double Pressure(FEMaterialPoint& mp) override;\n    double Pressure(const double e, const double T = 0) override;\n    \n    //! tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! bulk modulus\n    double BulkModulus(FEMaterialPoint& mp) override;\n    \n    //! strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    //! evaluate temperature\n    double Temperature(FEMaterialPoint& mp) override { return m_Tr; }\n    \n    //! evaluate dilatation from pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n    \n    //! return elastic fluid\n    FEElasticFluid* GetElastic() { return m_pElastic; }\n    \nprivate: // material properties\n    FEElasticFluid*             m_pElastic;     //!< pointer to elastic part of fluid material\n    \n    \npublic: // from FEBiphasicInterface\n    double GetActualFluidPressure(const FEMaterialPoint& mp) override {\n        const FEFluidMaterialPoint* pt = (mp.ExtractData<FEFluidMaterialPoint>());\n        return pt->m_pf;\n    }\n    \npublic:\n    double      m_kg;       //!< radius of gyration\n    double      m_k;        //!< bulk modulus at J=1\n    double      m_Tr;       //!< ambient temperature\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEPolarFluidAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEPolarFluidAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFEPolarFluidAnalysis::FEPolarFluidAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEPolarFluidAnalysis : public FEAnalysis\n{\npublic:\n\tenum PolarFluidAnalysisType {\n\t\tSTEADY_STATE,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFEPolarFluidAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPolarFluidDomain.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFEPolarFluidDomain::FEPolarFluidDomain(FEModel* pfem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include \"febiofluid_api.h\"\n\nclass FEModel;\nclass FELinearSystem;\nclass FEBodyForce;\nclass FEBodyMoment;\nclass FEGlobalVector;\nclass FETimeInfo;\n\n//! Abstract interface class for polar fluid domains.\n\n//! A polar fluid domain is used by the polar fluid mechanics solver.\n//! This interface defines the functions that have to be implemented by a\n//! polar fluid domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOFLUID_API FEPolarFluidDomain\n{\npublic:\n    FEPolarFluidDomain(FEModel* pfem);\n    virtual ~FEPolarFluidDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! calculate the internal forces\n    virtual void InternalForces(FEGlobalVector& R) = 0;\n    \n    //! Calculate the body force vector\n    virtual void BodyForce(FEGlobalVector& R, FEBodyForce& bf) = 0;\n    \n    //! Calculate the body moment vector\n    virtual void BodyMoment(FEGlobalVector& R, FEBodyMoment& bm) = 0;\n    \n    //! calculate the interial forces (for dynamic problems)\n    virtual void InertialForces(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! Calculate global stiffness matrix (only contribution from internal force derivative)\n    //! \\todo maybe I should rename this the InternalStiffness matrix?\n    virtual void StiffnessMatrix (FELinearSystem& LS) = 0;\n    \n    //! Calculate stiffness contribution of body forces\n    virtual void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) = 0;\n    \n    //! Calculate stiffness contribution of body moments\n    virtual void BodyMomentStiffness(FELinearSystem& LS, FEBodyMoment& bm) = 0;\n    \n    //! calculate the mass matrix (for dynamic problems)\n    virtual void MassMatrix(FELinearSystem& LS) = 0;\n    \n    //! transient analysis\n    void SetTransientAnalysis() { m_btrans = true; }\n    void SetSteadyStateAnalysis() { m_btrans = false; }\n    \nprotected:\n    bool        m_btrans;   // flag for transient (true) or steady-state (false) analysis\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPolarFluidDomain3D.h\"\n#include \"FEPolarFluidMaterialPoint.h\"\n#include \"FEFluidSolver.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include \"FEBioPolarFluid.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FEBodyMoment.h\"\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEPolarFluidDomain3D::FEPolarFluidDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEPolarFluidDomain(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofG(pfem), m_dofAG(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n    \n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_ACCELERATION));\n        \n        m_dofG.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_VELOCITY));\n        m_dofAG.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_ACCELERATION));\n        \n        m_dofEF = pfem->GetDOFIndex(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_DILATATION), 0);\n        m_dofAEF = pfem->GetDOFIndex(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_DILATATION_TDERIV), 0);\n        \n        FEDofList dofs(pfem);\n        dofs.AddDofs(m_dofW);\n        dofs.AddDofs(m_dofG);\n        dofs.AddDof(m_dofEF);\n        m_dof = dofs;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEPolarFluidDomain3D& FEPolarFluidDomain3D::operator = (FEPolarFluidDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n    ar & m_dofW & m_dofAW;\n    ar & m_dofG & m_dofAG;\n    ar & m_dof;\n    ar & m_dofEF & m_dofAEF;\n}\n\n//-----------------------------------------------------------------------------\n// get total dof list\nconst FEDofList& FEPolarFluidDomain3D::GetDOFList() const\n{\n    return m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEPolarFluidDomain3D::SetMaterial(FEMaterial* pmat)\n{\n    FEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEPolarFluid*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEPolarFluidDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], r0, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n            pt.m_r0 = el.Evaluate(x0, j);\n            \n            if (pt.m_ef <= -1) {\n                throw NegativeJacobianDetected();\n            }\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 7*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEPolarFluidDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*    gw = el.GaussWeights();\n    \n    FEViscousFluid* Viscous = m_pMat->GetViscous();\n    FEViscousPolarFluid* ViscPol = m_pMat->GetViscousPolar();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        \n        // calculate the jacobian\n        detJ = invjac0(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        vec3d th = ViscPol->SkewStressDualVector(mp);\n        mat3d sva = mat3da(th);\n        mat3d sv = sva + Viscous->Stress(mp);\n        mat3d M = ViscPol->CoupleStress(mp);\n        // get the gradient of the elastic pressure\n        vec3d gradp = pt.m_gradef*m_pMat->Tangent_Pressure_Strain(mp);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jdot/J\n        double dJoJ = pt.m_efdot/(pt.m_ef+1);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fv = sv*gradN[i] + gradp*H[i];\n            vec3d fg = M*gradN[i] - th*(2*H[i]);\n            double fJ = dJoJ*H[i] + gradN[i]*pt.m_vft;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[7*i  ] -= fv.x*detJ;\n            fe[7*i+1] -= fv.y*detJ;\n            fe[7*i+2] -= fv.z*detJ;\n            fe[7*i+3] -= fg.x*detJ;\n            fe[7*i+4] -= fg.y*detJ;\n            fe[7*i+5] -= fg.z*detJ;\n            fe[7*i+6] -= fJ*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 7*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEPolarFluidDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double dens = m_pMat->Density(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = detJ0(el, n)*gw[n];\n        \n        // get the force\n        f = BF.force(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[7*i  ] -= H[i]*dens*f.x*detJ;\n            fe[7*i+1] -= H[i]*dens*f.y*detJ;\n            fe[7*i+2] -= H[i]*dens*f.z*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEPolarFluidDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    \n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f, k;\n    \n    // gradient of shape functions\n    vec3d gradN;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        \n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n]*tp.alphaf;\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Density(mp);\n        \n        // get the force\n        f = BF.force(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                k = f*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                ke[7*i  ][7*j+6] += k.x;\n                ke[7*i+1][7*j+6] += k.y;\n                ke[7*i+2][7*j+6] += k.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::BodyMoment(FEGlobalVector& R, FEBodyMoment& bm)\n{\n    int NE = (int)m_Elem.size();\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 7*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementBodyMoment(bm, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEPolarFluidDomain3D::ElementBodyMoment(FEBodyMoment& bm, FESolidElement& el, vector<double>& fe)\n{\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d m;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double dens = m_pMat->Density(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = detJ0(el, n)*gw[n];\n        \n        // get the moment\n        m = bm.moment(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[7*i+3] -= H[i]*dens*m.x*detJ;\n            fe[7*i+4] -= H[i]*dens*m.y*detJ;\n            fe[7*i+5] -= H[i]*dens*m.z*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body moments\nvoid FEPolarFluidDomain3D::ElementBodyMomentStiffness(FEBodyMoment& bm, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    \n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d m, k;\n    \n    // gradient of shape functions\n    vec3d gradN;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        \n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n]*tp.alphaf;\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Density(mp);\n        \n        // get the force\n        m = bm.moment(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                k = m*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                ke[7*i+3][7*j+6] += k.x;\n                ke[7*i+4][7*j+6] += k.y;\n                ke[7*i+5][7*j+6] += k.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEPolarFluidDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf);\n    \n    double *H, *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    FEViscousFluid* Viscous = m_pMat->GetViscous();\n    FEViscousPolarFluid* ViscPol = m_pMat->GetViscousPolar();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n        \n        // get the tangents\n        mat3d svJ = Viscous->Tangent_Strain(mp) + ViscPol->SkewTangent_Strain(mp);\n        vec3d thJ = ViscPol->SkewTangent_Strain(mp).vec();\n        tens4ds cvs = m_pMat->Tangent_RateOfDeformation(mp);\n        mat3d cva = ViscPol->SkewTangent_RateOfRotation(mp);\n        tens4d m = ViscPol->CoupleTangent_RateOfRotation(mp);\n        mat3d mJ = ViscPol->CoupleTangent_Strain(mp);\n        double dp = m_pMat->Tangent_Pressure_Strain(mp);\n        double d2p = m_pMat->Tangent_Pressure_Strain_Strain(mp);\n        // Jdot/J\n        double dJoJ = pt.m_efdot/Jf;\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += 7)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += 7)\n            {\n                mat3da gNa(gradN[i]), gNb(gradN[j]);\n                mat3d Kvv = vdotTdotv(gradN[i], cvs, gradN[j]) + gNa*cva*gNb/2;\n                mat3d Kgv = cva*gNb*H[i];\n                vec3d kJv = (pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j];\n                mat3d Kvg = -gNa*cva*H[j];\n                //! TODO: There may be a mistake in vdotTdotv when using tens4d object, evaluates as b.T.a instead of a.T.b\n//                mat3d Kgg = vdotTdotv(gradN[i], m, gradN[j])-cva*(2*H[i]*H[j]);\n                mat3d Kgg = vdotTdotv(gradN[j], m, gradN[i])-cva*(2*H[i]*H[j]);\n                vec3d kvJ = (svJ*gradN[i])*H[j] + (gradN[j]*dp+pt.m_gradef*(H[j]*d2p))*H[i];\n                vec3d kgJ = mJ*gradN[i]*H[j] - thJ*(2*H[i]*H[j]);\n                double kJJ = (H[j]*(ksi/dt - dJoJ) + gradN[j]*pt.m_vft)*H[i]/Jf;\n                \n                ke[i7  ][j7  ] += Kvv(0,0)*detJ;\n                ke[i7  ][j7+1] += Kvv(0,1)*detJ;\n                ke[i7  ][j7+2] += Kvv(0,2)*detJ;\n                ke[i7  ][j7+3] += Kvg(0,0)*detJ;\n                ke[i7  ][j7+4] += Kvg(0,1)*detJ;\n                ke[i7  ][j7+5] += Kvg(0,2)*detJ;\n                ke[i7  ][j7+6] += kvJ.x*detJ;\n                \n                ke[i7+1][j7  ] += Kvv(1,0)*detJ;\n                ke[i7+1][j7+1] += Kvv(1,1)*detJ;\n                ke[i7+1][j7+2] += Kvv(1,2)*detJ;\n                ke[i7+1][j7+3] += Kvg(1,0)*detJ;\n                ke[i7+1][j7+4] += Kvg(1,1)*detJ;\n                ke[i7+1][j7+5] += Kvg(1,2)*detJ;\n                ke[i7+1][j7+6] += kvJ.y*detJ;\n                \n                ke[i7+2][j7  ] += Kvv(2,0)*detJ;\n                ke[i7+2][j7+1] += Kvv(2,1)*detJ;\n                ke[i7+2][j7+2] += Kvv(2,2)*detJ;\n                ke[i7+2][j7+3] += Kvg(2,0)*detJ;\n                ke[i7+2][j7+4] += Kvg(2,1)*detJ;\n                ke[i7+2][j7+5] += Kvg(2,2)*detJ;\n                ke[i7+2][j7+6] += kvJ.z*detJ;\n                \n                ke[i7+3][j7  ] += Kgv(0,0)*detJ;\n                ke[i7+3][j7+1] += Kgv(0,1)*detJ;\n                ke[i7+3][j7+2] += Kgv(0,2)*detJ;\n                ke[i7+3][j7+3] += Kgg(0,0)*detJ;\n                ke[i7+3][j7+4] += Kgg(0,1)*detJ;\n                ke[i7+3][j7+5] += Kgg(0,2)*detJ;\n                ke[i7+3][j7+6] += kgJ.x*detJ;\n                \n                ke[i7+4][j7  ] += Kgv(1,0)*detJ;\n                ke[i7+4][j7+1] += Kgv(1,1)*detJ;\n                ke[i7+4][j7+2] += Kgv(1,2)*detJ;\n                ke[i7+4][j7+3] += Kgg(1,0)*detJ;\n                ke[i7+4][j7+4] += Kgg(1,1)*detJ;\n                ke[i7+4][j7+5] += Kgg(1,2)*detJ;\n                ke[i7+4][j7+6] += kgJ.y*detJ;\n                \n                ke[i7+5][j7  ] += Kgv(2,0)*detJ;\n                ke[i7+5][j7+1] += Kgv(2,1)*detJ;\n                ke[i7+5][j7+2] += Kgv(2,2)*detJ;\n                ke[i7+5][j7+3] += Kgg(2,0)*detJ;\n                ke[i7+5][j7+4] += Kgg(2,1)*detJ;\n                ke[i7+5][j7+5] += Kgg(2,2)*detJ;\n                ke[i7+5][j7+6] += kgJ.z*detJ;\n                \n                ke[i7+6][j7  ] += kJv.x*detJ;\n                ke[i7+6][j7+1] += kJv.y*detJ;\n                ke[i7+6][j7+2] += kJv.z*detJ;\n                ke[i7+6][j7+6] += kJJ*detJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 7*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate material stiffness\n        ElementStiffness(el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 7*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementMassMatrix(el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 7*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::BodyMomentStiffness(FELinearSystem& LS, FEBodyMoment& bm)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        \n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 7*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyMomentStiffness(bm, el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEPolarFluidDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i7, j, j7, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf)*m_btrans;\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEPolarFluidMaterialPoint& pf = *(mp.ExtractData<FEPolarFluidMaterialPoint>());\n        \n        double dens = m_pMat->Density(mp);\n        double kg = m_pMat->m_kg;\n        double moi = dens*kg*kg;\n        double J = 1+pt.m_ef;\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i7=0; i<neln; ++i, i7 += 7)\n        {\n            for (j=0, j7 = 0; j<neln; ++j, j7 += 7)\n            {\n                mat3d Mv = ((mat3dd(ksi/dt) + pt.m_Lf)*H[j] + mat3dd(gradN[j]*pt.m_vft))*(H[i]*dens*detJ);\n                mat3d Jgg = mat3dd(ksi/dt*H[j] + gradN[j]*pt.m_vft)*(H[i]*moi*detJ);\n                mat3d Jgv = pf.m_Psi*(H[i]*H[j]*moi*detJ);\n                vec3d mJ = pt.m_aft*(-H[i]*H[j]*dens/J*detJ);\n                vec3d jJ = pf.m_gfdot*(-H[i]*H[j]*moi/J*detJ);\n                \n                ke[i7  ][j7  ] += Mv(0,0);\n                ke[i7  ][j7+1] += Mv(0,1);\n                ke[i7  ][j7+2] += Mv(0,2);\n                ke[i7  ][j7+6] += mJ.x;\n                \n                ke[i7+1][j7  ] += Mv(1,0);\n                ke[i7+1][j7+1] += Mv(1,1);\n                ke[i7+1][j7+2] += Mv(1,2);\n                ke[i7+1][j7+6] += mJ.y;\n                \n                ke[i7+2][j7  ] += Mv(2,0);\n                ke[i7+2][j7+1] += Mv(2,1);\n                ke[i7+2][j7+2] += Mv(2,2);\n                ke[i7+2][j7+6] += mJ.z;\n\n                ke[i7+3][j7  ] += Jgv(0,0);\n                ke[i7+3][j7+1] += Jgv(0,1);\n                ke[i7+3][j7+2] += Jgv(0,2);\n                ke[i7+3][j7+3] += Jgg(0,0);\n                ke[i7+3][j7+4] += Jgg(0,1);\n                ke[i7+3][j7+5] += Jgg(0,2);\n                ke[i7+3][j7+6] += jJ.x;\n                \n                ke[i7+4][j7  ] += Jgv(1,0);\n                ke[i7+4][j7+1] += Jgv(1,1);\n                ke[i7+4][j7+2] += Jgv(1,2);\n                ke[i7+4][j7+3] += Jgg(1,0);\n                ke[i7+4][j7+4] += Jgg(1,1);\n                ke[i7+4][j7+5] += Jgg(1,2);\n                ke[i7+4][j7+6] += jJ.y;\n                \n                ke[i7+5][j7  ] += Jgv(2,0);\n                ke[i7+5][j7+1] += Jgv(2,1);\n                ke[i7+5][j7+2] += Jgv(2,2);\n                ke[i7+5][j7+3] += Jgg(2,0);\n                ke[i7+5][j7+4] += Jgg(2,1);\n                ke[i7+5][j7+5] += Jgg(2,2);\n                ke[i7+5][j7+6] += jJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i, tp);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (NegativeJacobian::DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEPolarFluidDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d vt[NELN], vp[NELN];\n    vec3d at[NELN], ap[NELN];\n    vec3d gt[NELN], gp[NELN];\n    vec3d agt[NELN], agp[NELN];\n    double et[NELN], ep[NELN];\n    double aet[NELN], aep[NELN];\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        vt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n        vp[j] = node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n        at[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n        ap[j] = node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n        gt[j] = node.get_vec3d(m_dofG[0], m_dofG[1], m_dofG[2]);\n        gp[j] = node.get_vec3d_prev(m_dofG[0], m_dofG[1], m_dofG[2]);\n        agt[j] = node.get_vec3d(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n        agp[j] = node.get_vec3d_prev(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n        et[j] = node.get(m_dofEF);\n        ep[j] = node.get_prev(m_dofEF);\n        aet[j] = node.get(m_dofAEF);\n        aep[j] = node.get_prev(m_dofAEF);\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEPolarFluidMaterialPoint& pf = *(mp.ExtractData<FEPolarFluidMaterialPoint>());\n\n        // material point data\n        pt.m_vft = el.Evaluate(vt, n)*alphaf + el.Evaluate(vp, n)*(1-alphaf);\n        pt.m_Lf = gradient(el, vt, n)*alphaf + gradient(el, vp, n)*(1-alphaf);\n        pt.m_aft = pt.m_Lf*pt.m_vft;\n        if (m_btrans) pt.m_aft += el.Evaluate(at, n)*alpham + el.Evaluate(ap, n)*(1-alpham);\n        pf.m_gf = el.Evaluate(gt, n)*alphaf + el.Evaluate(gp, n)*(1-alphaf);\n        pf.m_Psi = gradient(el, gt, n)*alphaf + gradient(el, gp, n)*(1-alphaf);\n        pf.m_gfdot = pf.m_Psi*pt.m_vft;\n        if (m_btrans) pf.m_gfdot += el.Evaluate(agt, n)*alpham + el.Evaluate(agp, n)*(1-alpham);\n        pt.m_ef = el.Evaluate(et, n)*alphaf + el.Evaluate(ep, n)*(1-alphaf);\n        pt.m_gradef = gradient(el, et, n)*alphaf + gradient(el, ep, n)*(1-alphaf);\n        pt.m_efdot = pt.m_gradef*pt.m_vft;\n        if (m_btrans) pt.m_efdot += el.Evaluate(aet, n)*alpham + el.Evaluate(aep, n)*(1-alpham);\n        \n        // calculate the stress at this material point\n        pt.m_sf = m_pMat->Stress(mp);\n        pf.m_sfa = m_pMat->GetViscousPolar()->SkewStress(mp);\n        \n        // calculate the fluid pressure\n        pt.m_pf = m_pMat->Pressure(mp);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 7*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInertialForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEPolarFluidMaterialPoint& pf = *(mp.ExtractData<FEPolarFluidMaterialPoint>());\n        double dens = m_pMat->Density(mp);\n        double kg = m_pMat->m_kg;\n        \n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fv = pt.m_aft*(dens*H[i]);\n            vec3d fg = pf.m_gfdot*(dens*kg*kg*H[i]);\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[7*i  ] -= fv.x*detJ;\n            fe[7*i+1] -= fv.y*detJ;\n            fe[7*i+2] -= fv.z*detJ;\n            fe[7*i+3] -= fg.x*detJ;\n            fe[7*i+4] -= fg.y*detJ;\n            fe[7*i+5] -= fg.z*detJ;\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEPolarFluidDomain.h\"\n#include \"FEPolarFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEPolarFluidDomain3D : public virtual FESolidDomain, public FEPolarFluidDomain\n{\npublic:\n    //! constructor\n    FEPolarFluidDomain3D(FEModel* pfem);\n    ~FEPolarFluidDomain3D() {}\n    \n    //! assignment operator\n    FEPolarFluidDomain3D& operator = (FEPolarFluidDomain3D& d);\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n    \n    // get total dof list\n    const FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n    \n    //! body moments\n    void BodyMoment(FEGlobalVector& R, FEBodyMoment& bm) override;\n    \n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \n    //! body momdent stiffness\n    void BodyMomentStiffness(FELinearSystem& LS, FEBodyMoment& bm) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n    \n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body moments\n    void ElementBodyMomentStiffness(FEBodyMoment& bm, FESolidElement& el, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates external body moments for solid elements\n    void ElementBodyMoment(FEBodyMoment& bm, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEPolarFluid*   m_pMat;\n    \nprotected:\n    FEDofList   m_dofW;\n    FEDofList   m_dofAW;\n    FEDofList   m_dofG;\n    FEDofList   m_dofAG;\n    FEDofList   m_dof;\n    int         m_dofEF;\n    int         m_dofAEF;\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPolarFluidDomainFactory.h\"\n#include \"FEPolarFluid.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEPolarFluidDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n    FEModel* pfem = pmat->GetFEModel();\n    FE_Element_Class eclass = spec.eclass;\n    const char* sztype = 0;\n    if (dynamic_cast<FEPolarFluid*>(pmat))\n    {\n        // fluid elements\n        if (eclass == FE_ELEM_SOLID) sztype = \"polar-fluid-3D\";\n        else return 0;\n    }\n    \n    if (sztype)\n    {\n        FEDomain* pd = fecore_new<FESolidDomain>(sztype, pfem);\n        if (pd) pd->SetMaterial(pmat);\n        return pd;\n    }\n    else return 0;\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEPolarFluidDomainFactory : public FEDomainFactory\n{\npublic:\n    virtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEPolarFluidMaterial.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPolarFluidMaterial, FEFluidMaterial)\n\n    // material properties\n    ADD_PROPERTY(m_pViscpol, \"polar\");\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEPolarFluidMaterial\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEPolarFluidMaterial constructor\n\nFEPolarFluidMaterial::FEPolarFluidMaterial(FEModel* pfem) : FEFluidMaterial(pfem)\n{\n    m_pViscpol = nullptr;\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluidMaterial.h\"\n#include <FEBioMech/FEBodyForce.h>\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEViscousFluid.h\"\n#include \"FEViscousPolarFluid.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid materials.\n\nclass FEBIOFLUID_API FEPolarFluidMaterial : public FEFluidMaterial\n{\npublic:\n    FEPolarFluidMaterial(FEModel* pfem);\n    virtual ~FEPolarFluidMaterial() {}\n    \npublic:\n    //! calculate stress at material point\n    virtual mat3ds Stress(FEMaterialPoint& pt) override = 0;\n    \n    //! tangent of stress with respect to strain J\n    virtual mat3ds Tangent_Strain(FEMaterialPoint& mp) override = 0;\n    \n    //! elastic fluid pressure\n    virtual double Pressure(FEMaterialPoint& mp) override = 0;\n    \n    //! tangent of elastic pressure with respect to strain J\n    virtual double Tangent_Pressure_Strain(FEMaterialPoint& mp) override = 0;\n    \n    //! 2nd tangent of elastic pressure with respect to strain J\n    virtual double Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp) override = 0;\n    \n    //! bulk modulus\n    virtual double BulkModulus(FEMaterialPoint& mp) override = 0;\n    \n    //! strain energy density\n    virtual double StrainEnergyDensity(FEMaterialPoint& mp) override = 0;\n    \n    //! invert effective pressure-dilatation relation\n    virtual bool Dilatation(const double T, const double p, double& e) override = 0;\n    \n    //! evaluate temperature\n    virtual double Temperature(FEMaterialPoint& mp) override = 0;\n    \n    //! return viscous part\n    FEViscousPolarFluid* GetViscousPolar() { return m_pViscpol; }\n    \n    //! fluid pressure from state variables\n    virtual double Pressure(const double ef, const double T) override = 0;\n    \nprivate: // material properties\n    FEViscousPolarFluid*    m_pViscpol; //!< pointer to viscous polar part of fluid material\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#include \"FEPolarFluidMaterialPoint.h\"\n\n//============================================================================\n// FEPolarFluidMaterialPoint\n//============================================================================\n//-----------------------------------------------------------------------------\nFEPolarFluidMaterialPoint::FEPolarFluidMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt)\n{\n    m_gf = m_gfdot = m_hf = vec3d(0,0,0);\n    m_sfa = mat3da(0,0,0);\n    m_Psi = mat3d(0,0,0,0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEPolarFluidMaterialPoint::Copy()\n{\n    FEPolarFluidMaterialPoint* pt = new FEPolarFluidMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_gf & m_gfdot & m_hf & m_sfa & m_Psi;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidMaterialPoint::Init()\n{\n    m_gf = m_gfdot = m_hf = vec3d(0,0,0);\n    m_sfa = mat3da(0,0,0);\n    m_Psi = mat3d(0,0,0,0,0,0,0,0,0);\n\n    // don't forget to initialize the base class\n\tFEMaterialPointData::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include <FECore/FEModel.h>\n#include <FECore/FEMaterial.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Polar fluid material point class.\n//\nclass FEBIOFLUID_API FEPolarFluidMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n    FEPolarFluidMaterialPoint(FEMaterialPointData* pt = 0);\n    \n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n    \n    //! data serialization\n    void Serialize(DumpStream& ar);\n    \n    //! Data initialization\n    void Init();\n    \npublic:\n    mat3da RelativeSpin() { return mat3da(m_hf); }\n    \npublic:\n    // polar fluid data\n    vec3d       m_gf;       //!< fluid angular velocity\n    vec3d       m_gfdot;    //!< fluid angular acceleration\n    vec3d       m_hf;       //!< fluid relative angular velocity\n    mat3da      m_sfa;      //!< antisymmetric part of fluid stress\n    mat3d       m_Psi;      //!< gradient of fluid angular velocity\n};\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <assert.h>\n#include \"FEBioPolarFluid.h\"\n#include \"FEPolarFluidSolver.h\"\n#include \"FEPolarFluidDomain.h\"\n#include \"FEFluidDomain.h\"\n#include \"FEFluidResistanceBC.h\"\n#include \"FEBackFlowStabilization.h\"\n#include \"FEFluidNormalVelocity.h\"\n#include \"FEFluidVelocity.h\"\n#include \"FEFluidRotationalVelocity.h\"\n#include \"FEPolarFluidAnalysis.h\"\n#include \"FEBodyMoment.h\"\n#include <FEBioMech/FEBodyForce.h>\n#include <FEBioMech/FEContactInterface.h>\n#include <FEBioMech/FEResidualVector.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/sys.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEPolarFluidSolver, FENewtonSolver)\nADD_PARAMETER(m_Vtol , \"vtol\"        );\nADD_PARAMETER(m_Gtol , \"gtol\"        );\nADD_PARAMETER(m_Ftol , \"ftol\"        );\nADD_PARAMETER(m_Etol , \"etol\"        );\nADD_PARAMETER(m_Rtol , \"rtol\"        );\nADD_PARAMETER(m_rhoi , \"rhoi\"        );\nADD_PARAMETER(m_pred , \"predictor\"   );\nADD_PARAMETER(m_minJf, \"min_volume_ratio\");\nADD_PARAMETER(m_order, \"order\"      );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEPolarFluidSolver Construction\n//\nFEPolarFluidSolver::FEPolarFluidSolver(FEModel* pfem) : FENewtonSolver(pfem), \\\nm_dofW(pfem), m_dofAW(pfem), m_dofG(pfem), m_dofAG(pfem), m_dofEF(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Vtol = 0.001;\n    m_Gtol = 0.001;\n    m_Ftol = 0.001;\n    m_Rmin = 1.0e-20;\n    m_Rmax = 0;         // not used if zero\n    m_minJf = 0;\n    \n    m_nveq = 0;\n    m_ngeq = 0;\n    m_nfeq = 0;\n    m_niter = 0;\n    \n    // assume non-symmetric\n    m_msymm = REAL_UNSYMMETRIC;\n    \n    // default Newmark parameters for rhoi = 0\n    m_rhoi = 0;\n    m_alphaf = 1;\n    m_alpham = 1.5;\n    m_beta = 0.5625;\n    m_gamma = 1;\n    m_pred = 0;\n    m_order = 1;\n    \n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n    \n    // turn off checking for a zero diagonal\n    CheckZeroDiagonal(false);\n    \n    // get the dof indices\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_ACCELERATION));\n        m_dofG.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_VELOCITY));\n        m_dofAG.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_ACCELERATION));\n        m_dofEF.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_DILATATION));\n        m_dofAEF = pfem->GetDOFIndex(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_DILATATION_TDERIV), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEPolarFluidSolver::~FEPolarFluidSolver()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Generate warnings if needed\nvoid FEPolarFluidSolver:: SolverWarnings()\n{\n    // Generate warning if rigid connectors are used with symmetric stiffness\n    if (m_msymm == REAL_SYMMETRIC) {\n        feLogWarning(\"Fluid analyses require non-symmetric stiffness matrix.\\nSet symmetric_stiffness flag to 0 in Control section.\");\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEPolarFluidSolver\n//\nbool FEPolarFluidSolver::Init()\n{\n    // initialize base class\n    if (FENewtonSolver::Init() == false) return false;\n    \n    // check parameters\n    if (m_Vtol <  0.0) { feLogError(\"vtol must be nonnegative.\"); return false; }\n    if (m_Gtol <  0.0) { feLogError(\"gtol must be nonnegative.\"); return false; }\n    if (m_Ftol <  0.0) { feLogError(\"ftol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_beta = m_gamma = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        if (m_order == 1)\n            m_alpham = (3-m_rhoi)/(1+m_rhoi)/2; // 1st-order system\n        else\n            m_alpham = (2-m_rhoi)/(1+m_rhoi); // 2nd-order system\n        m_beta = pow(1 + m_alpham - m_alphaf,2)/4;\n        m_gamma = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\\n\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    m_vi.assign(m_nveq,0);\n    m_Vi.assign(m_nveq,0);\n    m_gi.assign(m_ngeq,0);\n    m_Gi.assign(m_ngeq,0);\n    m_fi.assign(m_nfeq,0);\n    m_Fi.assign(m_nfeq,0);\n    \n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    gather(m_Ut, mesh, m_dofW[0]);\n    gather(m_Ut, mesh, m_dofW[1]);\n    gather(m_Ut, mesh, m_dofW[2]);\n    gather(m_Ut, mesh, m_dofG[0]);\n    gather(m_Ut, mesh, m_dofG[1]);\n    gather(m_Ut, mesh, m_dofG[2]);\n    gather(m_Ut, mesh, m_dofEF[0]);\n    \n    \n    // set flag for transient or steady-state analyses\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n        FEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(&dom);\n        if (fdom) {\n            if (pstep->m_nanalysis == FEPolarFluidAnalysis::STEADY_STATE)\n                fdom->SetSteadyStateAnalysis();\n            else\n                fdom->SetTransientAnalysis();\n        }\n        else if (pfdom) {\n            if (pstep->m_nanalysis == FEPolarFluidAnalysis::STEADY_STATE)\n                pfdom->SetSteadyStateAnalysis();\n            else\n                pfdom->SetTransientAnalysis();\n        }\n    }\n    \n    SolverWarnings();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEPolarFluidSolver::InitEquations()\n{\n    // Add the solution variables\n    AddSolutionVariable(&m_dofW , 1, \"velocity\"        , m_Vtol);\n    AddSolutionVariable(&m_dofG , 1, \"angular velocity\", m_Gtol);\n    AddSolutionVariable(&m_dofEF, 1, \"dilatation\"      , m_Ftol);\n    \n    // base class initialization\n    if (FENewtonSolver::InitEquations() == false) return false;\n\n    // determine the number of velocity and dilatation equations\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    m_nveq = m_ngeq = m_nfeq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofW[0] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofG[0] ] != -1) m_ngeq++;\n        if (n.m_ID[m_dofG[1] ] != -1) m_ngeq++;\n        if (n.m_ID[m_dofG[2] ] != -1) m_ngeq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_nfeq++;\n    }\n    \n    // Next, we add any Lagrange Multipliers\n    FEModel& fem = *GetFEModel();\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n    \n    // All initialization is done\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEPolarFluidSolver::InitEquations2()\n{\n    // Add the solution variables\n    AddSolutionVariable(&m_dofW , -1, \"velocity\", m_Vtol);\n    AddSolutionVariable(&m_dofG , -1, \"angular velocity\", m_Gtol);\n    AddSolutionVariable(&m_dofEF, -1, \"dilatation\", m_Ftol);\n    \n    // base class initialization\n    if (FENewtonSolver::InitEquations2() == false) return false;\n\n    // determined the nr of velocity and dilatation equations\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    m_nveq = m_ngeq = m_nfeq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofW[0] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2] ] != -1) m_nveq++;\n        if (n.m_ID[m_dofG[0] ] != -1) m_ngeq++;\n        if (n.m_ID[m_dofG[1] ] != -1) m_ngeq++;\n        if (n.m_ID[m_dofG[2] ] != -1) m_ngeq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_nfeq++;\n    }\n\n    // Next, we add any Lagrange Multipliers\n    FEModel& fem = *GetFEModel();\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidSolver::GetVelocityData(vector<double> &vi, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(vi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofW[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidSolver::GetAngularVelocityData(vector<double> &xi, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(xi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofG[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofG[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n        nid = n.m_ID[m_dofG[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            xi[m++] = ui[nid];\n            assert(m <= (int) xi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidSolver::GetDilatationData(vector<double> &ei, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ei);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofEF[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ei[m++] = ui[nid];\n            assert(m <= (int) ei.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEPolarFluidSolver::Serialize(DumpStream& ar)\n{\n    // Serialize parameters\n    FENewtonSolver::Serialize(ar);\n\n    ar & m_nrhs;\n    ar & m_niter;\n    ar & m_nref & m_ntotref;\n    \n    ar & m_neq & m_nveq & m_ngeq & m_nfeq;\n\n    ar & m_Fr & m_Ui & m_Ut;\n    ar & m_Vi & m_Gi & m_Fi;\n    \n    if (ar.IsLoading())\n    {\n        m_Fr.assign(m_neq, 0);\n        m_Vi.assign(m_nveq,0);\n        m_Gi.assign(m_ngeq,0);\n        m_Fi.assign(m_nfeq,0);\n    }\n    \n    if (ar.IsShallow()) return;\n    ar & m_rhoi & m_alphaf & m_alpham;\n    ar & m_beta & m_gamma;\n    ar & m_pred;\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEPolarFluidSolver::UpdateKinematics(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter3(U, mesh, m_dofW[0], m_dofW[1], m_dofW[2]);\n    scatter3(U, mesh, m_dofG[0], m_dofG[1], m_dofG[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // force dilatations to remain greater than -1\n    if (m_minJf > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofEF[0]) <= -1.0)\n                node.set(m_dofEF[0], m_minJf - 1.0);\n        }\n    }\n    \n    // make sure the prescribed BCs are fulfilled\n    int nvel = fem.BoundaryConditions();\n    for (int i=0; i<nvel; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.Update();\n    }\n\n    // enforce the linear constraints\n    // TODO: do we really have to do this? Shouldn't the algorithm\n    // already guarantee that the linear constraints are satisfied?\n    FELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    if (LCM.LinearConstraints() > 0)\n    {\n        LCM.Update();\n    }\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEPolarFluidAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n        double dt = fem.GetTime().timeIncrement;\n        double cgi = 1 - 1.0/m_gamma;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // relative fluid velocity material time derivative\n            vec3d wt = n.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d wp = n.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d awt = n.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            vec3d awp = n.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            awt = awp*cgi + (wt - wp)/(m_gamma*dt);\n            n.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awt);\n            \n            // fluid angular velocity material time derivative\n            vec3d gt = n.get_vec3d(m_dofG[0], m_dofG[1], m_dofG[2]);\n            vec3d gp = n.get_vec3d_prev(m_dofG[0], m_dofG[1], m_dofG[2]);\n            vec3d agt = n.get_vec3d(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n            vec3d agp = n.get_vec3d_prev(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n            agt = agp*cgi + (gt - gp)/(m_gamma*dt);\n            n.set_vec3d(m_dofAG[0], m_dofAG[1], m_dofAG[2], agt);\n            \n            // dilatation time derivative\n            double eft = n.get(m_dofEF[0]);\n            double efp = n.get_prev(m_dofEF[0]);\n            double aefp = n.get_prev(m_dofAEF);\n            double aeft = aefp*cgi + (eft - efp)/(m_gamma*dt);\n            n.set(m_dofAEF, aeft);\n        }\n    }\n\n    // update nonlinear constraints (needed for updating Lagrange Multiplier)\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* nlc = fem.NonlinearConstraint(i);\n        if (nlc->IsActive()) nlc->Update(m_Ui, ui);\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive()) spc->Update(m_Ui, ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update DOF increments\nvoid FEPolarFluidSolver::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->UpdateIncrements(Ui, ui);\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->UpdateIncrements(Ui, ui);\n\t}\n\n    // TODO: This is a hack!\n    // The problem is that I only want to call the domain's IncrementalUpdate during\n    // the quasi-Newtoon loop. However, this function is also called after the loop\n    // converges. The emap parameter is used here to detect wether we are inside the\n    // loop (emap == false), or not (emap == true).\n    if (emap == false)\n    {\n        for (int i = 0; i < mesh.Domains(); ++i)\n        {\n            FEDomain& dom = mesh.Domain(i);\n            dom.IncrementalUpdate(ui, true);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEPolarFluidSolver::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update kinematics\n    UpdateKinematics(ui);\n    \n    // update model state\n    UpdateModel();\n}\n\n//-----------------------------------------------------------------------------\n//! Update nonlinear constraints\nvoid FEPolarFluidSolver::UpdateConstraints()\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // Update all nonlinear constraints\n    for (int i = 0; i<fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* pci = fem.NonlinearConstraint(i);\n        if (pci->IsActive()) pci->Update();\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPolarFluidSolver::Update2(const vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter3(U, mesh, m_dofW[0], m_dofW[1], m_dofW[2]);\n    scatter3(U, mesh, m_dofG[0], m_dofG[1], m_dofG[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    \n    // Update the prescribed nodes\n    for (int i = 0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.m_rid == -1)\n        {\n            vec3d dv(0, 0, 0);\n            for (int j = 0; j < node.m_ID.size(); ++j)\n            {\n                int nj = -node.m_ID[j] - 2; if (nj >= 0) node.set(j, node.get(j) + ui[nj]);\n            }\n        }\n    }\n\n    // update model state\n    UpdateModel();\n}\n\n//-----------------------------------------------------------------------------\nbool FEPolarFluidSolver::InitStep(double time)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alpha = m_alphaf;\n    tp.beta  = m_beta;\n    tp.gamma = m_gamma;\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n    //    double dt = tp.timeIncrement;\n    //    double ta = (t > 0) ? t - (1-m_alpha)*dt : m_alpha*dt;\n    //    return FESolver::InitStep(ta);\n    return FESolver::InitStep(t);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FEPolarFluidSolver::PrepStep()\n{\n    FEModel& fem = *GetFEModel();\n    \n    FETimeInfo& tp = fem.GetTime();\n    double dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n    \n    // zero total DOFs\n    zero(m_Ui);\n    zero(m_Vi);\n    zero(m_Gi);\n    zero(m_Fi);\n    \n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt;\n        ni.m_dp = ni.m_dt = ni.m_d0;\n        ni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n            {\n                // initial guess at start of new time step (default)\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awp*(m_gamma-1)/m_gamma);\n                \n                vec3d agp = ni.get_vec3d_prev(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n                ni.set_vec3d(m_dofAG[0], m_dofAG[1], m_dofAG[2], agp*(m_gamma-1)/m_gamma);\n                \n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF)*(m_gamma-1)/m_gamma);\n            }\n                break;\n                \n            case 1:\n            {\n                // initial guess at start of new time step (Zero Ydot)\n                vec3d wp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                vec3d gp = ni.get_vec3d_prev(m_dofG[0], m_dofG[1], m_dofG[2]);\n                vec3d agp = ni.get_vec3d_prev(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], wp + awp*dt*(1-m_gamma)*m_alphaf);\n                ni.set_vec3d(m_dofG[0], m_dofG[1], m_dofG[2], gp + agp*dt*(1-m_gamma)*m_alphaf);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt*(1-m_gamma)*m_alphaf);\n            }\n                break;\n                \n            case 2:\n            {\n                // initial guess at start of new time step (Same Ydot)\n                vec3d awp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], awp);\n                vec3d agp = ni.get_vec3d_prev(m_dofAG[0], m_dofAG[1], m_dofAG[2]);\n                ni.set_vec3d(m_dofAG[0], m_dofAG[1], m_dofAG[2], agp);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF));\n                \n                vec3d wp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], wp + awp*dt);\n                vec3d gp = ni.get_vec3d_prev(m_dofG[0], m_dofG[1], m_dofG[2]);\n                ni.set_vec3d(m_dofG[0], m_dofG[1], m_dofG[2], gp + agp*dt);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt);\n            }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.PrepStep(ui);\n    }\n\n    // do the linear constraints\n    fem.GetLinearConstraintManager().PrepStep();\n\n    // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n    // update domain data\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) dom.PreSolveUpdate(tp);\n    }\n\n    // update stresses\n    UpdateModel();\n    \n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->PrepStep();\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n    \n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.PrepStep();\n    }\n\n    // see if we need to do contact augmentations\n    m_baugment = false;\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n        if (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n    }\n    \n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPolarFluidSolver::Quasin()\n{\n    // convergence norms\n    double    normR1;       // residual norm\n    double    normE1;       // energy norm\n    double    normV;        // velocity norm\n    double    normv;        // velocity increment norm\n    double    normG;        // angular velocity norm\n    double    normg;        // angular velocity increment norm\n    double    normRi = 0;   // initial residual norm\n    double    normVi = 0;   // initial velocity norm\n    double    normGi = 0;   // initial angular velocity norm\n    double    normEi = 0;   // initial energy norm\n    double    normEm = 0;   // max energy norm\n    double    normFi = 0;   // initial dilatation norm\n    double    normF;        // current dilatation norm\n    double    normf;        // incremement dilatation norm\n    \n    FEModel& fem = *GetFEModel();\n    \n    // prepare for the first iteration\n    const FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n    // init QN method\n    if (QNInit() == false) return false;\n    \n    // loop until converged or when max nr of reformations reached\n    bool bconv = false;        // convergence flag\n    do\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n        \n        // solve the equations (returns line search; solution stored in m_ui)\n        double s = QNSolve();\n        \n        // extract the velocity and dilatation increments\n        GetVelocityData(m_vi, m_ui);\n        GetAngularVelocityData(m_gi, m_ui);\n        GetDilatationData(m_fi, m_ui);\n        \n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normVi = fabs(m_vi*m_vi);\n            normGi = fabs(m_gi*m_gi);\n            normFi = fabs(m_fi*m_fi);\n            normEm = normEi;\n        }\n        \n        // update all degrees of freedom\n        for (int i=0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n        \n        // update velocities\n        for (int i = 0; i<m_nveq; ++i) m_Vi[i] += s*m_vi[i];\n        \n        // update angular velocities\n        for (int i = 0; i<m_ngeq; ++i) m_Gi[i] += s*m_gi[i];\n        \n        // update dilatations\n        for (int i = 0; i<m_nfeq; ++i) m_Fi[i] += s*m_fi[i];\n        \n        // update other increments (e.g., Lagrange multipliers)\n        UpdateIncrements(m_Ui, m_ui, false);\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n        normv  = (m_vi*m_vi)*(s*s);\n        normV  = m_Vi*m_Vi;\n        normg  = (m_gi*m_gi)*(s*s);\n        normG  = m_Gi*m_Gi;\n        normf  = (m_fi*m_fi)*(s*s);\n        normF  = m_Fi*m_Fi;\n        normE1 = s*fabs(m_ui*m_R1);\n        \n        // check for nans\n        if (ISNAN(normR1)) throw NANInResidualDetected();\n        \n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check velocity norm\n        if ((m_Vtol > 0) && (normv  > (m_Vtol*m_Vtol)*normV )) bconv = false;\n        \n        // check angular velocity norm\n        if ((m_Gtol > 0) && (normg  > (m_Gtol*m_Gtol)*normG )) bconv = false;\n        \n        // check dilatation norm\n        if ((m_Ftol > 0) && (normf  > (m_Ftol*m_Ftol)*normF )) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n        if ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        if (normE1 > normEm) bconv = false;\n        \n        // print convergence summary\n        feLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n        feLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n        if (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        feLog(\"\\t   velocity         %15le %15le %15le \\n\", normVi, normv ,(m_Vtol*m_Vtol)*normV );\n        feLog(\"\\t   angular velocity %15le %15le %15le \\n\", normGi, normg ,(m_Gtol*m_Gtol)*normG );\n        feLog(\"\\t   dilatation       %15le %15le %15le \\n\", normFi, normf ,(m_Ftol*m_Ftol)*normF );\n        \n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n        // see if we have exceeded the max residual\n        if ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n        {\n            // doesn't look like we're getting anywhere, so let's retry the time step\n            throw MaxResidualError();\n        }\n        \n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n            if (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n                feLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n                QNForceReform(true);\n            }\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n                feLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                normVi = normv;\n                normGi = normg;\n                normFi = normf;\n                QNForceReform(true);\n            }\n            \n            // Do the QN update (This may also do a stiffness reformation if necessary)\n            bool bret = QNUpdate();\n            \n            // something went wrong with the update, so we'll need to break\n            if (bret == false) break;\n        }\n        else if (m_baugment)\n        {\n            // Do augmentations\n            bconv = DoAugmentations();\n        }\n        \n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        m_Ut += m_Ui;\n        zero(m_Ui);\n    }\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEPolarFluidSolver::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(&dom);\n            if (fdom) fdom->StiffnessMatrix(LS);\n            else if (pfdom) pfdom->StiffnessMatrix(LS);\n        }\n    }\n    \n    // calculate the body force stiffness matrix for each domain\n    int NBL = fem.ModelLoads();\n    for (int j = 0; j<NBL; ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEFluidDomain& fdom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n                FEPolarFluidDomain& pfdom = dynamic_cast<FEPolarFluidDomain&>(*pbf->Domain(i));\n                if (&fdom) fdom.BodyForceStiffness(LS, *pbf);\n                else if (&pfdom) pfdom.BodyForceStiffness(LS, *pbf);\n            }\n        }\n        FEBodyMoment* pbm = dynamic_cast<FEBodyMoment*>(fem.ModelLoad(j));\n        if (pbm && pbm->IsActive())\n        {\n            for (int i = 0; i<pbm->Domains(); ++i)\n            {\n                FEPolarFluidDomain& pfdom = dynamic_cast<FEPolarFluidDomain&>(*pbm->Domain(i));\n                if (&pfdom) pfdom.BodyMomentStiffness(LS, *pbm);\n            }\n        }\n    }\n\n    // calculate the body force stiffness matrix for each domain\n    for (int j = 0; j<fem.ModelLoads(); ++j)\n    {\n        FEModelLoad* pml = fem.ModelLoad(j);\n        if (pml && pml->IsActive()) pml->StiffnessMatrix(LS);\n    }\n        \n    // Add mass matrix\n    // loop over all domains (except rigid)\n    for (int i = 0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(&dom);\n            if (fdom) fdom->MassMatrix(LS);\n            else if (pfdom) pfdom->MassMatrix(LS);\n        }\n    }\n    \n    // calculate contact stiffness\n    ContactStiffness(LS);\n    \n    // calculate nonlinear constraint stiffness\n    // note that this is the contribution of the\n    // constraints enforced with augmented lagrangian\n    NonLinearConstraintStiffness(LS, tp);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEPolarFluidSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    \n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FEPolarFluidSolver::ContactStiffness(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEPolarFluidSolver::ContactForces(FEGlobalVector& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry\n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEPolarFluidSolver::Residual(vector<double>& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the time information\n    const FETimeInfo& tp = fem.GetTime();\n    \n    // initialize residual with concentrated nodal loads\n    zero(R);\n\n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n    FEResidualVector RHS(fem, R, m_Fr);\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(&dom);\n            if (fdom) fdom->InternalForces(RHS);\n            else if (pfdom) pfdom->InternalForces(RHS);\n        }\n    }\n    \n    // calculate the body forces\n    for (int j = 0; j<fem.ModelLoads(); ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEFluidDomain& fdom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n                FEPolarFluidDomain& pfdom = dynamic_cast<FEPolarFluidDomain&>(*pbf->Domain(i));\n                if (&fdom) fdom.BodyForce(RHS, *pbf);\n                else if (&pfdom) pfdom.BodyForce(RHS, *pbf);\n            }\n        }\n        FEBodyMoment* pbm = dynamic_cast<FEBodyMoment*>(fem.ModelLoad(j));\n        if (pbm && pbm->IsActive())\n        {\n            for (int i = 0; i<pbm->Domains(); ++i)\n            {\n                FEPolarFluidDomain& pfdom = dynamic_cast<FEPolarFluidDomain&>(*pbm->Domain(i));\n                if (&pfdom) pfdom.BodyMoment(RHS, *pbm);\n            }\n        }\n    }\n    \n    // apply external loads\n    for (int j = 0; j<fem.ModelLoads(); ++j)\n    {\n        FEModelLoad* pml = fem.ModelLoad(j);\n        if (pml->IsActive()) pml->LoadVector(RHS);\n    }\n    \n    // calculate inertial forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive())\n        {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            FEPolarFluidDomain* pfdom = dynamic_cast<FEPolarFluidDomain*>(&dom);\n            if (fdom) fdom->InertialForces(RHS);\n            else if (pfdom) pfdom->InertialForces(RHS);\n        }\n    }\n    \n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.set_load(m_dofW[0], 0);\n        node.set_load(m_dofW[1], 0);\n        node.set_load(m_dofW[2], 0);\n        \n        int n;\n        if ((n = -node.m_ID[m_dofW[0]] - 2) >= 0) node.set_load(m_dofW[0], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofW[1]] - 2) >= 0) node.set_load(m_dofW[1], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofW[2]] - 2) >= 0) node.set_load(m_dofW[2], -m_Fr[n]);\n    }\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces\nvoid FEPolarFluidSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->LoadVector(R, tp);\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEPolarFluidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/FEDofList.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! The FEPolarFluidSolver class solves polar fluid problems\n//! It can deal with quasi-static and dynamic problems\n//!\nclass FEBIOFLUID_API FEPolarFluidSolver : public FENewtonSolver\n{\npublic:\n    //! constructor\n    FEPolarFluidSolver(FEModel* pfem);\n    \n    //! destructor\n    ~FEPolarFluidSolver();\n    \n    //! serialize data to/from dump file\n    void Serialize(DumpStream& ar) override;\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n    bool InitEquations2() override;\n\n    //! Generate warnings if needed\n    void SolverWarnings();\n    \n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n    void Update2(const vector<double>& ui) override;\n\n    //! update nodal positions, velocities, accelerations, etc.\n    void UpdateKinematics(vector<double>& ui);\n    \n    void UpdateConstraints();\n    \n    //! update DOF increments\n    virtual void UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap);\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix(FELinearSystem& LS) override;\n\n    //! contact stiffness\n    void ContactStiffness(FELinearSystem& LS);\n    \n    //! calculates stiffness contributon of nonlinear constraints\n    void NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n    \n    //{ --- Residual routines ---\n    \n    //! Calculate the contact forces\n    void ContactForces(FEGlobalVector& R);\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n    \n    //! Calculate nonlinear constraint forces\n    void NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n    \nprotected:\n    void GetVelocityData(vector<double>& vi, vector<double>& ui);\n    void GetAngularVelocityData(vector<double>& xi, vector<double>& ui);\n    void GetDilatationData(vector<double>& ei, vector<double>& ui);\n    \npublic:\n    // convergence tolerances\n    double  m_Vtol;     //!< velocity tolerance\n    double  m_Gtol;     //!< angular velocity tolerance\n    double  m_Ftol;     //!< dilatation tolerance\n    double  m_minJf;    //!< minimum allowable compression ratio\n    \npublic:\n    // equation numbers\n    int m_nveq;     //!< number of equations related to velocity dofs\n    int m_ngeq;     //!< number of equations related to angular velocity dofs\n    int m_nfeq;     //!< number of equations related to dilatation dofs\n    \npublic:\n    vector<double> m_Fn;    //!< concentrated nodal force vector\n    vector<double> m_Fr;    //!< nodal reaction forces\n    vector<double> m_vi;    //!< velocity increment vector\n    vector<double> m_Vi;    //!< Total velocity vector for iteration\n    vector<double> m_gi;    //!< angular velocity increment vector\n    vector<double> m_Gi;    //!< Total angular velocity vector for iteration\n    vector<double> m_fi;    //!< dilatation increment vector\n    vector<double> m_Fi;    //!< Total dilatation vector for iteration\n    \n    // generalized alpha method\n    double  m_rhoi;         //!< spectral radius (rho infinity)\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_beta;         //!< beta\n    double  m_gamma;        //!< gamma\n    int     m_pred;         //!< predictor method\n    int     m_order;        //!< generalized-alpha integration order\n    \nprotected:\n    FEDofList   m_dofW;     // fluid velocity\n    FEDofList   m_dofAW;    // material time derivative of fluid velocity\n    FEDofList   m_dofG;     // fluid angular velocity\n    FEDofList   m_dofAG;    // material time derivative of fluid angular velocity\n    FEDofList   m_dofEF;    // fluid dilatation\n    int         m_dofAEF;   // material time derivative of fluid dilatation\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPowellEyringFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPowellEyringFluid.h\"\n#include \"FEFluid.h\"\n#include <math.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPowellEyringFluid, FEViscousFluid)\n\tADD_PARAMETER(m_mu0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\")->setUnits(\"P.t\")->setLongName(\"zero shear rate viscosity\");\n\tADD_PARAMETER(m_mui, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mui\")->setUnits(\"P.t\")->setLongName(\"infinite shear rate viscosity\");\n\tADD_PARAMETER(m_lam, FE_RANGE_GREATER_OR_EQUAL(0.0), \"lambda\")->setUnits(UNIT_TIME)->setLongName(\"relaxation time\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEPowellEyringFluid::FEPowellEyringFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_mu0 = 0;\n    m_mui = 0;\n    m_lam = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FEPowellEyringFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double mu = ShearViscosity(pt);\n    \n    mat3ds s = D*(2*mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FEPowellEyringFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FEPowellEyringFluid::Tangent_RateOfDeformation(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamg = m_lam*gdot;\n    \n    double mu = ShearViscosity(pt);\n    double dmu = (lamg < 1e-3) ? -2*(m_mu0 - m_mui)*m_lam*m_lam/3. :\n    (2*(m_mu0 - m_mui)*(gdot/sqrt(1 + pow(lamg,2)) - asinh(lamg)/m_lam))/pow(gdot,3);\n    mat3dd I(1.0);\n    tens4ds c = dyad1s(D)*(2*dmu) + dyad4s(I)*(2*mu);\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic viscosity\ndouble FEPowellEyringFluid::ShearViscosity(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double lamg = m_lam*gdot;\n    double mu = (lamg < 1e-3) ? m_mu0 : m_mui + (m_mu0 - m_mui)*asinh(lamg)/lamg;\n    return mu;\n}\n\n//! derivative of shear viscosity w.r.t. strain rate\ndouble FEPowellEyringFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\tFEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n\tmat3ds D = vt.RateOfDeformation();\n\tdouble gdot = sqrt(2 * (D.sqr()).tr());\n\tdouble lamg = m_lam * gdot;\n\n\tdouble dmu = (lamg < 1e-3) ? -2 * (m_mu0 - m_mui) * m_lam * m_lam / 3. :\n\t\t(2 * (m_mu0 - m_mui) * (gdot / sqrt(1 + pow(lamg, 2)) - asinh(lamg) / m_lam)) / pow(gdot, 3);\n\n\tdouble dmu_dgdot = 0.5 * gdot * dmu;\n\n   return dmu_dgdot;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FEPowellEyringFluid::BulkViscosity(FEMaterialPoint& pt)\n{\n    return 2*ShearViscosity(pt)/3.;\n}\n"
  },
  {
    "path": "FEBioFluid/FEPowellEyringFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Powell-Eyring fluid\n\nclass FEBIOFLUID_API FEPowellEyringFluid :\tpublic FEViscousFluid\n{\npublic:\n    //! constructor\n    FEPowellEyringFluid(FEModel* pfem);\n    \n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    \n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double\tm_mu0;\t\t//!< shear viscosity at zero shear rate\n    double\tm_mui;\t\t//!< shear viscosity at infinite shear rate\n    double  m_lam;      //!< time constant\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidAngularVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedFluidAngularVelocity.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF.\nBEGIN_FECORE_CLASS(FEPrescribedFluidAngularVelocity, FEBoundaryCondition)\nADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:fluid angular velocity)\");\nADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_ANGULAR_VELOCITY)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\nADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedFluidAngularVelocity::FEPrescribedFluidAngularVelocity(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidAngularVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedFluidAngularVelocity : public FEPrescribedDOF\n{\npublic:\n    FEPrescribedFluidAngularVelocity(FEModel* fem);\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidDilatation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedFluidDilatation.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedFluidDilatation, FEBoundaryCondition)\n\tADD_PARAMETER(m_scale, \"value\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedFluidDilatation::FEPrescribedFluidDilatation(FEModel* fem) : FEPrescribedDOF(fem)\n{\n\n}\n\nbool FEPrescribedFluidDilatation::Init()\n{\n\tSetDOF(GetDOFIndex(\"ef\"));\n\treturn FEPrescribedDOF::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidDilatation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedFluidDilatation : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedFluidDilatation(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n\n See Copyright-FEBio.txt for details.\n\n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"FEPrescribedFluidPressure.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FEModel.h>\n#include \"FEFluid.h\"\n\n //-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPrescribedFluidPressure, FEPrescribedSurface)\n    ADD_PARAMETER(m_p, \"pressure\")->setUnits(UNIT_PRESSURE)->setLongName(\"fluid pressure\");\nEND_FECORE_CLASS();\n\n\n//-----------------------------------------------------------------------------\n //! constructor\nFEPrescribedFluidPressure::FEPrescribedFluidPressure(FEModel* fem) : FEPrescribedSurface(fem)\n{\n\tm_p = 0.0;\n    m_psurf = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEPrescribedFluidPressure::Init()\n{\n    // get the dof index\n    m_dofEF = GetDOFIndex(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION), 0);\n    if (m_dofEF < 0) return false;\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    \n    m_psurf = GetSurface();\n\n    m_e.assign(GetSurface()->Nodes(), 0.0);\n    \n    // do an initial Update so that the dilatations are set properly at the very first time step\n    Update();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedFluidPressure::UpdateDilatation()\n{\n\tint N = m_psurf->Nodes();\n\tstd::vector<vector<double>> efNodes(N, vector<double>());\n\n\t//Project sum of all ca and osc values from int points to nodes on surface\n\t//All values put into map, including duplicates\n\tfor (int i = 0; i < m_psurf->Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = m_psurf->Element(i);\n\t\t// evaluate average prescribed pressure on this face\n\t\tdouble p = 0;\n\t\tfor (int j = 0; j < el.GaussPoints(); ++j) {\n\t\t\tFEMaterialPoint* pt = el.GetMaterialPoint(j);\n\t\t\tp += m_p(*pt);\n\t\t}\n\t\tp /= el.GaussPoints();\n\t\tFEElement* e = el.m_elem[0].pe;\n\t\tFEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n\t\tFEFluid* pfl = pm->ExtractProperty<FEFluid>();\n\t\tif (pfl == nullptr) break;\n\t\tFESolidElement* se = dynamic_cast<FESolidElement*>(e);\n\t\tif (se) {\n\t\t\tdouble efi[FEElement::MAX_INTPOINTS] = { 0 };\n\t\t\tdouble efo[FEElement::MAX_NODES] = { 0 };\n\t\t\tfor (int j = 0; j < se->GaussPoints(); ++j) {\n\t\t\t\tFEMaterialPoint* pt = se->GetMaterialPoint(j);\n\t\t\t\tbool good = pfl->Dilatation(0, p, efi[j]);\n\t\t\t\tassert(good);\n\t\t\t}\n\t\t\t// project dilatations from integration points to nodes\n\t\t\tse->project_to_nodes(efi, efo);\n\t\t\t// only keep the dilatations at the nodes of the surface face\n\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\tefNodes[el.m_lnode[j]].push_back(efo[j]);\n\t\t}\n\t\t//If no solid element, insert all 0s\n\t\telse {\n\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\tefNodes[el.m_lnode[j]].push_back(0);\n\t\t}\n\t}\n\t//For each node, average the nodal ef\n\tfor (int i = 0; i < m_psurf->Nodes(); ++i)\n\t{\n\t\tdouble ef = 0;\n\t\tfor (int j = 0; j < efNodes[i].size(); ++j)\n\t\t\tef += efNodes[i][j];\n\t\tef /= efNodes[i].size();\n\n\t\t// store value for now\n\t\tm_e[i] = ef;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEPrescribedFluidPressure::UpdateModel() { Update(); }\nvoid FEPrescribedFluidPressure::Update()\n{\n\tUpdateDilatation();\n    FEPrescribedSurface::Update();\n\n\t// TODO: Is this necessary?\n\tGetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedFluidPressure::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tUpdateDilatation();\n\tFEPrescribedSurface::PrepStep(ui, brel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedFluidPressure::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e[nodelid];\n\n\tFENode& node = GetMesh().Node(m_nodeList[nodelid]);\n\tnode.set(m_dofEF, m_e[nodelid]);\n\n}\n\nvoid FEPrescribedFluidPressure::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEPrescribedFluidPressure::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofEF;\n    ar & m_psurf;\n}\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n\n See Copyright-FEBio.txt for details.\n\n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include <FECore/FEModelParam.h>\n#include <FECore/FEPrescribedBC.h>\n#include \"febiofluid_api.h\"\n\nclass FEFluidMaterial;\n\n //-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEPrescribedFluidPressure : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEPrescribedFluidPressure(FEModel* pfem);\n\n    //! set the dilatation\n    void Update() override;\n    void UpdateModel() override;\n    \n    //! initialize\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\n\tvoid PrepStep(std::vector<double>& ui, bool brel) override;\n    \n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n\tvoid UpdateDilatation();\n\nprotected:\n    int             m_dofEF;\n    FESurface*      m_psurf;\n\npublic:\n    FEParamDouble   m_p;       //!< prescribed fluid pressure\n    vector<double>  m_e;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidTemperature.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedFluidTemperature.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedFluidTemperature, FEBoundaryCondition)\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_RELATIVE_TEMPERATURE)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedFluidTemperature::FEPrescribedFluidTemperature(FEModel* fem) : FEPrescribedDOF(fem)\n{\n\n}\n\nbool FEPrescribedFluidTemperature::Init()\n{\n\tSetDOF(GetDOFIndex(\"T\"));\n\treturn FEPrescribedDOF::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidTemperature.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedFluidTemperature : public FEPrescribedDOF\n{\npublic:\n    FEPrescribedFluidTemperature(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedFluidVelocity.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedFluidVelocity, FEBoundaryCondition)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:relative fluid velocity)\");\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_VELOCITY)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedFluidVelocity::FEPrescribedFluidVelocity(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioFluid/FEPrescribedFluidVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedFluidVelocity : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedFluidVelocity(FEModel* fem);\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEQuemadaFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEQuemadaFluid.h\"\n#include \"FEFluid.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEQuemadaFluid, FEViscousFluid)\n\tADD_PARAMETER(m_mu0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\")->setUnits(\"P.t\")->setLongName(\"zero shear rate viscosity\");\n\tADD_PARAMETER(m_H  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"H\")->setLongName(\"suspension volume fraction\");\n    ADD_PARAMETER(m_k0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k0\");\n    ADD_PARAMETER(m_ki , FE_RANGE_GREATER_OR_EQUAL(0.0), \"kinf\");\n    ADD_PARAMETER(m_gc , FE_RANGE_GREATER(0.0)         , \"gc\")->setUnits(\"1/t\")->setLongName(\"criticial shear rate\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEQuemadaFluid::FEQuemadaFluid(FEModel* pfem) : FEViscousFluid(pfem)\n{\n    m_mu0 = 0;\n    m_H = 0;\n    m_k0 = 0;\n    m_ki = 0;\n    m_gc = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! viscous stress\nmat3ds FEQuemadaFluid::Stress(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double mu = ShearViscosity(pt);\n    \n    mat3ds s = D*(2*mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to strain J\nmat3ds FEQuemadaFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of stress with respect to rate of deformation tensor D\ntens4ds FEQuemadaFluid::Tangent_RateOfDeformation(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double grsqrt = sqrt(gdot/m_gc);\n    double k = (m_k0 + m_ki*grsqrt)/(1+grsqrt);\n    double mu = m_mu0*pow(1-0.5*k*m_H,-2);\n    double dmu = (gdot > 0) ? 4*m_mu0*m_H/m_gc*(m_k0 - m_ki)*(1+grsqrt)/grsqrt/pow(-2*(1+grsqrt)+m_H*(m_k0+m_ki*grsqrt),3) : 0.0;\n    mat3dd I(1.0);\n    tens4ds c = dyad1s(D)*(2*dmu) + dyad4s(I)*(2*mu);\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! dynamic viscosity\ndouble FEQuemadaFluid::ShearViscosity(FEMaterialPoint& pt)\n{\n    FEFluidMaterialPoint& vt = *pt.ExtractData<FEFluidMaterialPoint>();\n    mat3ds D = vt.RateOfDeformation();\n    double gdot = sqrt(2*(D.sqr()).tr());\n    double grsqrt = sqrt(gdot/m_gc);\n    double k = (m_k0 + m_ki*grsqrt)/(1+grsqrt);\n    double mu = m_mu0*pow(1-0.5*k*m_H,-2);\n    return mu;\n}\n\n//! derivative of shear viscosity w.r.t. strain rate\ndouble FEQuemadaFluid::Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp)\n{\n\tFEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n\tmat3ds D = vt.RateOfDeformation();\n\tdouble gdot = sqrt(2 * (D.sqr()).tr());\n\tdouble grsqrt = sqrt(gdot / m_gc);\n\tdouble dmu = (gdot > 0) ? 4 * m_mu0 * m_H / m_gc * (m_k0 - m_ki) * (1 + grsqrt) / grsqrt / pow(-2 * (1 + grsqrt) + m_H * (m_k0 + m_ki * grsqrt), 3) : 0.0;\n\t\n   double dmu_dgdot = 0.5 * gdot * dmu;\n\n   return dmu_dgdot;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk viscosity\ndouble FEQuemadaFluid::BulkViscosity(FEMaterialPoint& pt)\n{\n    return 2*ShearViscosity(pt)/3.;\n}\n"
  },
  {
    "path": "FEBioFluid/FEQuemadaFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a Quemada fluid\n// see https://en.wikipedia.org/wiki/Hemorheology\n\nclass FEBIOFLUID_API FEQuemadaFluid :\tpublic FEViscousFluid\n{\npublic:\n    //! constructor\n    FEQuemadaFluid(FEModel* pfem);\n    \n    //! viscous stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) override;\n    \n    //! dynamic viscosity\n    double ShearViscosity(FEMaterialPoint& mp) override;\n    \n\t//! derivative of shear viscosity w.r.t. strain rate\n\tdouble Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) override;\n\n    //! bulk viscosity\n    double BulkViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double\tm_mu0;\t\t//!< shear viscosity at zero shear rate\n    double  m_H;        //!< volume fraction occupied by particles\n    double  m_k0;\n    double  m_ki;\n    double  m_gc;\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FERealGas.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FERealGas.h\"\n#include <FECore/log.h>\n#include <FECore/FEFunction1D.h>\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERealGas, FEElasticFluid)\n\n// material parameters\nADD_PARAMETER(m_nva  , FE_RANGE_GREATER_OR_EQUAL(0), \"nva\")->setLongName(\"no. of p virial coeffs\");\nADD_PARAMETER(m_nvc  , FE_RANGE_GREATER_OR_EQUAL(0), \"nvc\")->setLongName(\"no. of cv virial coeffs\");\n\nADD_PROPERTY(m_a0  , \"a0\")->SetLongName(\"normalized specific free energy 0\");\nADD_PROPERTY(m_A[0], \"A1\", FEProperty::Optional)->SetLongName(\"1st p virial coeff\");\nADD_PROPERTY(m_A[1], \"A2\", FEProperty::Optional)->SetLongName(\"2nd p virial coeff\");\nADD_PROPERTY(m_A[2], \"A3\", FEProperty::Optional)->SetLongName(\"3rd p virial coeff\");\nADD_PROPERTY(m_A[3], \"A4\", FEProperty::Optional)->SetLongName(\"4th p virial coeff\");\nADD_PROPERTY(m_A[4], \"A5\", FEProperty::Optional)->SetLongName(\"5th p virial coeff\");\nADD_PROPERTY(m_A[5], \"A6\", FEProperty::Optional)->SetLongName(\"6th p virial coeff\");\nADD_PROPERTY(m_A[6], \"A7\", FEProperty::Optional)->SetLongName(\"7th p virial coeff\");\n\nADD_PROPERTY(m_C[0], \"C0\")->SetLongName(\"1st cv virial coeff\");\nADD_PROPERTY(m_C[1], \"C1\", FEProperty::Optional)->SetLongName(\"2nd cv virial coeff\");\nADD_PROPERTY(m_C[2], \"C2\", FEProperty::Optional)->SetLongName(\"3rd cv virial coeff\");\nADD_PROPERTY(m_C[3], \"C3\", FEProperty::Optional)->SetLongName(\"4th cv virial coeff\");\n\nEND_FECORE_CLASS();\n\nFERealGas::FERealGas(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_nva = 0;\n    m_nvc = 1;\n    m_R = m_Pr = m_Tr = 0;\n    m_a0 = nullptr;\n    m_A[0] = m_A[1] = m_A[2] = m_A[3] = m_A[4] = m_A[5] = m_A[6] = nullptr;\n    m_C[0] = m_C[1] = m_C[2] = m_C[3] = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FERealGas::Init()\n{\n    m_R  = GetGlobalConstant(\"R\");\n    m_Tr = GetGlobalConstant(\"T\");\n    m_Pr = GetGlobalConstant(\"P\");\n    \n    if (m_R  <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\");    return false; }\n    if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    if (m_Pr <= 0) { feLogError(\"A positive referential absolute pressure P must be defined in Globals section\"); return false; }\n\n    m_pMat = dynamic_cast<FEThermoFluid*>(GetParent());\n    m_rhor = m_pMat->ReferentialDensity();\n\n    // check if we should assume ideal gas\n    if (m_nva == 0) {\n\t\tm_A[0] = fecore_alloc(FEConstFunction, GetFEModel());\n\t\tm_A[0]->SetParameter(\"value\", 1.0);\n        m_nva = 1;\n    }\n    m_a0->Init();\n    for (int k=0; k<m_nva; ++k)\n        if (m_A[k]) m_A[k]->Init();\n    for (int k=0; k<m_nvc; ++k)\n        if (m_C[k]) m_C[k]->Init();\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERealGas::Serialize(DumpStream& ar)\n{\n    FEElasticFluid::Serialize(ar);\n\n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n    ar & m_R & m_Pr & m_Tr & m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FERealGas::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double p = 0;\n    for (int k=1; k<=m_nva; ++k)\n        p += m_A[k-1]->value(That)*pow(y,k);\n    \n    return p*m_Pr;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FERealGas::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double dpJ = m_A[0]->value(That);\n    for (int k=2; k<=m_nva; ++k)\n        dpJ += k*m_A[k-1]->value(That)*pow(y,k-1);\n\n    return -dpJ*m_Pr*That/pow(J,2);\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FERealGas::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double dpJ2 = 2*m_A[0]->value(That);\n    for (int k=2; k<=m_nva; ++k)\n        dpJ2 += k*m_A[k-1]->value(That)*(2*y+(k-1)*That/J)*pow(y,k-2);\n\n    return dpJ2*m_Pr*That/pow(J,3);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FERealGas::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double y = That/J - 1;\n    double dpT = 0;\n    for (int k=1; k<=m_nva; ++k)\n        dpT += (m_A[k-1]->derive(That)*y + k/J*m_A[k-1]->value(That))*pow(y,k-1);\n\n    return dpT*m_Pr/m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FERealGas::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double y = That/J - 1;\n    double dpT2 = m_A[0]->deriv2(That)*y + 2/J*m_A[0]->derive(That);\n    for (int k=2; k<=m_nva; ++k)\n        dpT2 += (m_A[k-1]->deriv2(That)*pow(y,2) + 2*k/J*m_A[k-1]->derive(That)*y\n                 + k*(k-1)/pow(J,2)*m_A[k-1]->value(That))*pow(y,k-2);\n\n    return dpT2*m_Pr/pow(m_Tr,2);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FERealGas::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double dpJT = m_A[0]->value(That) + That*m_A[0]->derive(That);\n    for (int k=2; k<=m_nva; ++k)\n        dpJT += k*(That*m_A[k-1]->derive(That)*y + m_A[k-1]->value(That)*(k*x-1))*pow(y,k-2);\n\n    return -dpJT*m_Pr/(m_Tr*pow(J, 2));\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FERealGas::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double x2 = x*x, x3 = x2*x, x4 = x3*x, x5 = x4*x, x6 = x5*x;\n    double lnx = log(x);\n    double A[MAX_NVA], f[MAX_NVA];\n    f[0] =  1 + x*(lnx-1);\n    f[1] = -1 + x*(x-2*lnx);\n    f[2] =  1 + x*(1.5+3*lnx) - 3*x2 + x3/2;\n    f[3] = -1 - x*(10./3.+4*lnx) + 6*x2 - 2*x3 + x4/3;\n    f[4] =  1 + x*(65./12.+5*lnx) - 10*x2 + 5*x3 - 5*x4/3 + x5/4;\n    f[5] = -1 - x*(77./10.+6*lnx) + 15*x2 - 10*x3 + 5*x4 - 1.5*x5 + x6/5;\n    f[6] =  1 + x*(203./20.+7*lnx) - 21*x2 + 35*x3/2 - 35*x4/3 + 21*x5/4 - 7*x6/5;\n    for (int k=0; k<m_nva; ++k)\n        A[k] = m_A[k]->value(That);\n    double a = m_a0->value(That);\n    for (int k=0; k<m_nva; ++k)\n        a += A[k]*J*f[k];\n\n    return a*m_Pr/m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FERealGas::SpecificEntropy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double x2 = x*x, x3 = x2*x, x4 = x3*x, x5 = x4*x, x6 = x5*x;\n    double lnx = log(x);\n    double A[MAX_NVA], dA[MAX_NVA], f[MAX_NVA], df[MAX_NVA];\n    f[0] =  1 + x*(lnx-1);\n    f[1] = -1 + x*(x-2*lnx);\n    f[2] =  1 + x*(1.5+3*lnx) - 3*x2 + x3/2;\n    f[3] = -1 - x*(10./3.+4*lnx) + 6*x2 - 2*x3 + x4/3;\n    f[4] =  1 + x*(65./12.+5*lnx) - 10*x2 + 5*x3 - 5*x4/3 + x5/4;\n    f[5] = -1 - x*(77./10.+6*lnx) + 15*x2 - 10*x3 + 5*x4 - 1.5*x5 + x6/5;\n    f[6] =  1 + x*(203./20.+7*lnx) - 21*x2 + 35*x3/2 - 35*x4/3 + 21*x5/4 - 7*x6/5;\n    df[0] = lnx;\n    df[1] = 2*(-1 + x - lnx);\n    df[2] = 1.5*(3 - 4*x + x2 + 2*lnx);\n    df[3] = 2./3.*(-11 + 18*x - 9*x2 + 2*x3 - 6*lnx);\n    df[4] = 5./12.*(25 - 48*x + 36*x2 - 16*x3 + 3*x4 + 12*lnx);\n    df[5] = -13.7 + 30*x - 30*x2 + 20*x3 - 7.5*x4 + 1.2*x5 - 6*lnx;\n    df[6] = 7./60.*(147 - 360*x + 450*x2 - 400*x3 + 225*x4 - 72*x5 + 10*x6 + 60*lnx);\n    for (int k=0; k<m_nva; ++k) {\n        A[k] = m_A[k]->value(That);\n        dA[k] = m_A[k]->derive(That);\n    }\n    double s = -m_a0->derive(That);\n    for (int k=0; k<m_nva; ++k)\n        s -= A[k]*df[k] + J*dA[k]*f[k];\n\n    return s*m_Pr/(m_Tr*m_rhor);\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FERealGas::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    // get the specific free energy\n    double a = SpecificFreeEnergy(mp);\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double a0 = m_a0->value(That)*m_Pr/m_rhor;\n    \n    // the specific strain energy is the difference between these two values\n    return a - a0;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FERealGas::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n\n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double y = That/J - 1;\n    double cv = m_C[0]->value(That);\n    for (int k=1; k<m_nvc; ++k)\n        cv += m_C[k]->value(That)*pow(y,k);\n\n    return cv*m_Pr/(m_Tr*m_rhor);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FERealGas::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double dcvJ = 0;\n    for (int k=1; k<m_nvc; ++k)\n        dcvJ -= m_C[k]->value(That)*pow(y,k-1);\n    return dcvJ*m_Pr/(m_Tr*m_rhor)*x/J;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FERealGas::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double x = That/J;\n    double y = x - 1;\n    double dcvT = m_C[0]->derive(That);\n    for (int k=1; k<m_nvc; ++k)\n        dcvT += (m_C[k]->derive(That)*y+k/J*m_C[k]->value(That))*pow(y,k-1);\n    return dcvT*m_Pr/(pow(m_Tr,2)*m_rhor);\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FERealGas::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double cv = IsochoricSpecificHeatCapacity(mp);\n    double p = Pressure(mp);    // current gauge pressure\n    double dpT = Tangent_Temperature(mp);\n    // evaluate dpT/dpJ in the reference configuration\n    double efsafe = fp.m_ef;\n    double Tsafe = tf.m_T;\n    fp.m_ef = 0;\n    tf.m_T = 0;\n    double r = Tangent_Temperature(mp)/Tangent_Strain(mp);\n    // restore values before continuing\n    fp.m_ef = efsafe;\n    tf.m_T = Tsafe;\n    // evaluate isobaric specific heat capacity\n    double cp = cv + r/m_rhor*(p - (m_Tr + tf.m_T)*dpT);\n    return cp;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FERealGas::Dilatation(const double T, const double p, double& e)\n{\n    double errrel = 1e-6;\n    double errabs = 1e-6;\n    int maxiter = 100;\n    switch (m_nva) {\n        case 0:\n            return false;\n            break;\n        case 1:\n        {\n            double q = T/m_Tr;\n            double x = p/m_Pr/m_A[0]->value(q);\n            e = (q+1)/(x+1) - 1;\n            return true;\n        }\n            break;\n        case 2:\n        {\n            double q = T/m_Tr;\n            double A1 = m_A[0]->value(q);\n            double A2 = m_A[1]->value(q);\n            double det = A1*A1 + 4*A2*p/m_Pr;\n            if (det < 0) return false;\n            det = sqrt(det);\n            // only one root of this quadratic equation is valid.\n            double x = (-A1+det)/(2*A2);\n            e = (q+1)/(x+1) - 1;\n            return true;\n        }\n            break;\n            \n        default:\n        {\n            FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n            FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n            ft->m_T = T;\n            bool convgd = false;\n            bool done = false;\n            int iter = 0;\n\t\t\tFEMaterialPoint mp(ft);\n            do {\n                ++iter;\n                fp->m_ef = e;\n                double f = Pressure(mp) - p;\n                double df = Tangent_Strain(mp);\n                double de = (df != 0) ? -f/df : 0;\n                e += de;\n                if ((fabs(de) < errrel*fabs(e)) ||\n                    (fabs(f/m_Pr) < errabs)) { convgd = true; done = true; }\n                if (iter > maxiter) done = true;\n            } while (!done);\n            \n            delete ft;\n            return convgd;\n        }\n        break;\n    }\n    return false;\n}\n"
  },
  {
    "path": "FEBioFluid/FERealGas.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for the viscous part of the fluid response.\n//! These materials provide the viscous stress and its tangents.\n//!\nclass FEBIOFLUID_API FERealGas : public FEElasticFluid\n{\npublic:\n    enum { MAX_NVA = 7, MAX_NVC = 4 };\n    \npublic:\n    FERealGas(FEModel* pfem);\n    ~FERealGas() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n\npublic:\n    double      m_R;        //!< universal gas constant\n    double      m_Pr;       //!< referential absolute pressure\n    double      m_Tr;       //!< referential absolute temperature\n    double      m_rhor;     //!< referential mass density\n    int             m_nva;          //!< number of virial coefficients for pressure constitutive relation\n    int             m_nvc;          //!< number of virial coefficients for isochoric specific heat capacity\n    FEFunction1D*   m_a0;           //!< specific free energy at zero pressure\n    FEFunction1D*   m_A[MAX_NVA];   //!< non-dimensional virial coefficients for pressure constitutive relation\n    FEFunction1D*   m_C[MAX_NVC];   //!< non-dimensional virial coefficients for isochoric specific heat capacity\n\n    FEThermoFluid*  m_pMat; //!< parent thermo-fluid material\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n    \n};\n"
  },
  {
    "path": "FEBioFluid/FERealLiquid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FERealLiquid.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERealLiquid, FEElasticFluid)\n\n// material parameters\nADD_PARAMETER(m_nvc  , FE_RANGE_GREATER(0), \"nvc\");\n\nADD_PROPERTY(m_psat, \"psat\")->SetLongName(\"saturation gauge pressure normalized\");\nADD_PROPERTY(m_asat, \"asat\")->SetLongName(\"saturation free energy normalized\");\nADD_PROPERTY(m_ssat, \"ssat\")->SetLongName(\"saturation entropy normalized\");\nADD_PROPERTY(m_esat, \"esat\")->SetLongName(\"saturation dilatation\");\nADD_PROPERTY(m_B[0], \"B1\"  )->SetLongName(\"1st pressure virial coefficient\");\nADD_PROPERTY(m_B[1], \"B2\", FEProperty::Optional)->SetLongName(\"2nd pressure virial coefficient\");\nADD_PROPERTY(m_B[2], \"B3\", FEProperty::Optional)->SetLongName(\"3rd pressure virial coefficient\");\nADD_PROPERTY(m_cvsat, \"cvsat\")->SetLongName(\"saturation isochoric heat capacity normalized\");\nADD_PROPERTY(m_C[0], \"C1\"  )->SetLongName(\"1st cv virial coefficient\");\nADD_PROPERTY(m_C[1], \"C2\", FEProperty::Optional)->SetLongName(\"2nd cv virial coefficient\");\nADD_PROPERTY(m_C[2], \"C3\", FEProperty::Optional)->SetLongName(\"3rd cv virial coefficient\");\n\nEND_FECORE_CLASS();\n\nFERealLiquid::FERealLiquid(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_nvc = 0;\n    m_R = m_Pr = m_Tr = 0;\n    m_psat = m_asat = m_ssat = m_esat = m_cvsat = nullptr;\n    m_B[0] = m_B[1] = m_B[2] = nullptr;\n    m_C[0] = m_C[1] = m_C[2] = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FERealLiquid::Init()\n{\n    m_R  = GetGlobalConstant(\"R\");\n    m_Tr = GetGlobalConstant(\"T\");\n    m_Pr = GetGlobalConstant(\"P\");\n    \n    if (m_R  <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\");    return false; }\n    if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    if (m_Pr <= 0) { feLogError(\"A positive referential absolute pressure P must be defined in Globals section\"); return false; }\n    if (m_nvc == 0){ feLogError(\"At least one virial coefficient must be specified in this real liquid\"); return false; }\n\n    m_pMat = dynamic_cast<FEThermoFluid*>(GetParent());\n    m_rhor = m_pMat->ReferentialDensity();\n    \n    m_psat->Init();\n    m_asat->Init();\n    m_ssat->Init();\n    m_esat->Init();\n    m_cvsat->Init();\n    for (int k=0; k<m_nvc; ++k) {\n        if (m_B[k]) m_B[k]->Init();\n        if (m_C[k]) m_C[k]->Init();\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERealLiquid::Serialize(DumpStream& ar)\n{\n    FEElasticFluid::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n    ar & m_R & m_Pr & m_Tr & m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FERealLiquid::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double p = m_psat->value(That);\n    double x = fp.m_ef - m_esat->value(That);\n    for (int k=1; k<=m_nvc; ++k)\n        p += m_B[k-1]->value(That)*pow(x,k);\n    \n    return p*m_Pr;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FERealLiquid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double dpJ = m_B[0]->value(That);\n    double x = fp.m_ef - m_esat->value(That);\n    for (int k=2; k<=m_nvc; ++k)\n        dpJ += k*m_B[k-1]->value(That)*pow(x,k-1);\n    \n    return dpJ*m_Pr;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to strain J\ndouble FERealLiquid::Tangent_Strain_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double dpJ2 = (m_nvc > 1) ? 2*m_B[1]->value(That) : 0;\n    double x = fp.m_ef - m_esat->value(That);\n    for (int k=3; k<=m_nvc; ++k)\n        dpJ2 += k*(k-1)*m_B[k-1]->value(That)*pow(x,k-2);\n    \n    return dpJ2*m_Pr;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FERealLiquid::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double dpT = m_psat->derive(That);\n    double dJsat = m_esat->derive(That);\n    double x = fp.m_ef - m_esat->value(That);\n    for (int k=1; k<=m_nvc; ++k)\n        dpT += m_B[k-1]->derive(That)*pow(x,k)\n        - k*dJsat*m_B[k-1]->value(That)*pow(x,k-1);\n    \n    return dpT*m_Pr/m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! 2nd tangent of pressure with respect to temperature T\ndouble FERealLiquid::Tangent_Temperature_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double dpT2 = m_psat->deriv2(That);\n    double dJsat = m_esat->derive(That);\n    double dJsa2 = m_esat->deriv2(That);\n    double x = fp.m_ef - m_esat->value(That);\n    vector<double> B(m_nvc,0);\n    for (int k=0; k<m_nvc; ++k) B[k] = m_B[k]->value(That);\n    for (int k=1; k<=m_nvc; ++k)\n        dpT2 += m_B[k-1]->deriv2(That)*pow(x,k)\n        - (2*dJsat*m_B[k-1]->derive(That) + dJsa2*B[k-1])*k*pow(x,k-1);\n    for (int k=2; k<=m_nvc; ++k)\n        dpT2 += k*(k-1)*pow(dJsat,2)*B[k-1]*pow(x,k-2);\n\n    return dpT2*m_Pr/pow(m_Tr,2);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J and temperature T\ndouble FERealLiquid::Tangent_Strain_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double dpJT = m_B[0]->derive(That);\n    double dJsat = m_esat->derive(That);\n    double x = fp.m_ef - m_esat->value(That);\n    for (int k=2; k<=m_nvc; ++k)\n        dpJT += k*m_B[k-1]->derive(That)*pow(x,k-1)\n        - k*(k-1)*dJsat*m_B[k-1]->value(That)*pow(x,k-2);\n    \n    return dpJT*m_Pr/m_Tr;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FERealLiquid::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double x = fp.m_ef - m_esat->value(That);\n    double a = m_asat->value(That) - x*m_psat->value(That);\n    for (int k=1; k<=m_nvc; ++k)\n        a -= m_B[k-1]->value(That)/(k+1)*pow(x,k+1);\n    \n    return a*m_Pr/m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FERealLiquid::SpecificEntropy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double x = fp.m_ef - m_esat->value(That);\n    double dJsat = m_esat->derive(That);\n    double ssat = m_ssat->value(That);\n    double s = ssat + x*m_psat->derive(That);\n    for (int k=1; k<=m_nvc; ++k)\n        s += (m_B[k-1]->derive(That)*x/(k+1) - dJsat*m_B[k-1]->value(That))*pow(x,k);\n\n    return s*m_Pr/(m_rhor*m_Tr);\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FERealLiquid::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n\n    // get the specific free energy\n    double a = SpecificFreeEnergy(mp);\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double asat = m_asat->value(That)*m_Pr/m_rhor;\n    \n    // the specific strain energy is the difference between these two values\n    return a - asat;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FERealLiquid::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double x = fp.m_ef - m_esat->value(That);\n    double cv = 0;\n    for (int k=1; k<=m_nvc; ++k) cv += m_C[k-1]->value(That)*pow(x,k);\n    cv *= m_cvsat->value(1.0);\n    cv += m_cvsat->value(That);\n    \n    return m_Pr/(m_rhor*m_Tr)*cv;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to strain J\ndouble FERealLiquid::Tangent_cv_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double x = fp.m_ef - m_esat->value(That);\n    double dcvJ = 0;\n    for (int k=1; k<=m_nvc; ++k) dcvJ += k*m_C[k-1]->value(That)*pow(x,k-1);\n    dcvJ *= m_cvsat->value(1.0);\n    \n    return m_Pr/(m_rhor*m_Tr)*dcvJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of isochoric specific heat capacity with respect to temperature T\ndouble FERealLiquid::Tangent_cv_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double x = fp.m_ef - m_esat->value(That);\n    double dcvT = 0;\n    for (int k=1; k<=m_nvc; ++k) dcvT += (m_C[k-1]->derive(That)*x - k*m_esat->derive(That)*m_C[k-1]->value(That))*pow(x,k-1);\n    dcvT *= m_cvsat->value(1.0);\n    dcvT += m_cvsat->derive(That);\n\n    return m_Pr/(m_rhor*pow(m_Tr,2))*dcvT;\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FERealLiquid::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double cv = IsochoricSpecificHeatCapacity(mp);\n    double p = Pressure(mp);    // current gauge pressure\n    double dpT = Tangent_Temperature(mp);\n    // evaluate dpT/dpJ in the reference configuration\n    double efsafe = fp.m_ef;\n    double Tsafe = tf.m_T;\n    fp.m_ef = 0;\n    tf.m_T = 0;\n    double r = Tangent_Temperature(mp)/Tangent_Strain(mp);\n    // restore values before continuing\n    fp.m_ef = efsafe;\n    tf.m_T = Tsafe;\n    // evaluate isobaric specific heat capacity\n    double cp = cv + r/m_rhor*(p - (m_Tr + tf.m_T)*dpT);\n    return cp;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FERealLiquid::Dilatation(const double T, const double p, double& e)\n{\n    double errrel = 1e-6;\n    double errabs = 1e-6;\n    int maxiter = 100;\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    double That = (T+m_Tr)/m_Tr;\n    double psat = m_psat->value(That);\n    double esat = m_esat->value(That);\n    double phat = p/m_Pr;\n    if (phat == psat) { e = esat; return true; }\n    switch (m_nvc) {\n        case 0:\n            delete ft;\n            return false;\n            break;\n        case 1:\n        {\n            double B0 = m_B[0]->value(That);\n            if (B0 == 0) { e = esat; return true; }\n            e = (phat - psat)/B0 + esat;\n            delete ft;\n            return true;\n        }\n            break;\n        case 2:\n        {\n            double B0 = m_B[0]->value(That);\n            double B1 = m_B[1]->value(That);\n            if ((B0 == 0) || (B1 == 0)) { e = esat; return true; }\n            double det = pow(B0,2) + 4*B1*(phat - psat);\n            if (det < 0) return false;\n            det = sqrt(det);\n            // only one root of this quadratic equation is valid.\n            e = esat - (B0+det)/(2*B1);\n            delete ft;\n            return true;\n        }\n            break;\n\n        default:\n        {\n            bool convgd = false;\n            bool done = false;\n            int iter = 0;\n\t\t\tFEMaterialPoint mp(ft);\n\t\t\tdo {\n                ++iter;\n                fp->m_ef = e;\n                double f = Pressure(mp) - p;\n                double df = Tangent_Strain(mp);\n                double de = (df != 0) ? -f/df : 0;\n                e += de;\n                if ((fabs(de) < errrel*fabs(e)) ||\n                    (fabs(f/m_Pr) < errabs)) { convgd = true; done = true; }\n                if (iter > maxiter) done = true;\n            } while (!done);\n            \n            delete ft;\n            return convgd;\n        }\n            break;\n    }\n    delete ft;\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n//! pressure from state variables\ndouble FERealLiquid::Pressure(const double ef, const double T)\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    FEThermoFluidMaterialPoint* ft = new FEThermoFluidMaterialPoint(fp);\n    fp->m_ef = ef;\n    ft->m_T = T;\n    FEMaterialPoint tmp(ft);\n    double p = Pressure(tmp);\n    delete ft;\n    return p;\n}\n"
  },
  {
    "path": "FEBioFluid/FERealLiquid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for the viscous part of the fluid response.\n//! These materials provide the viscous stress and its tangents.\n//!\nclass FEBIOFLUID_API FERealLiquid : public FEElasticFluid\n{\npublic:\n    enum { MAX_NVC = 3 };\n    \npublic:\n    FERealLiquid(FEModel* pfem);\n    ~FERealLiquid() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to strain J\n    double Tangent_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \n    //! 2nd tangent of pressure with respect to temperature T\n    double Tangent_Temperature_Temperature(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to strain J and temperature T\n    double Tangent_Strain_Temperature(FEMaterialPoint& mp) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to strain J\n    double Tangent_cv_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of isochoric specific heat capacity with respect to temperature T\n    double Tangent_cv_Temperature(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n    \n    //! fluid pressure from state variables\n    double Pressure(const double ef, const double T);\n    \npublic:\n    double      m_R;        //!< universal gas constant\n    double      m_Pr;       //!< referential absolute pressure\n    double      m_Tr;       //!< referential absolute temperature\n    double      m_rhor;     //!< referential mass density\n    FEFunction1D*   m_psat;         //!< normalized gauge pressure on saturation curve (multiply by m_Pr to get actual value)\n    FEFunction1D*   m_asat;         //!< normalized specific free energy on saturation curve (multiply by m_Pr/m_rhor to get actual value)\n    FEFunction1D*   m_ssat;         //!< normalized specific entropy on saturation curve (multiply by m_Pr/m_rhor*m_Tr to get actual value)\n    FEFunction1D*   m_esat;         //!< dilatation on saturation curve\n    int             m_nvc;          //!< number of virial coefficients for pressure and cv constitutive relation\n    FEFunction1D*   m_B[MAX_NVC];   //!< non-dimensional virial coefficients for pressure constitutive relation\n    FEFunction1D*   m_cvsat;        //!< normalized isochoric specific heat capacity on saturation curve (multiply by m_Pr/m_rhor*m_Tr to get actual value)\n    FEFunction1D*   m_C[MAX_NVC];   //!< non-dimensional virial coefficients for isochoric specific heat capacity\n\n    FEThermoFluid*  m_pMat; //!< parent thermo-fluid material\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n    \n};\n"
  },
  {
    "path": "FEBioFluid/FERealVapor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FERealVapor.h\"\n#include <FECore/log.h>\n#include <FECore/FEFunction1D.h>\n#include <FECore/sys.h>\n#include <FECore/tools.h>\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERealVapor, FEElasticFluid)\n\n// material parameters\nADD_PARAMETER(m_Tc  , FE_RANGE_GREATER(1.0), \"Tc\")->setLongName(\"normalized critical temperature\");\nADD_PROPERTY(m_esat , \"esat\" )->SetLongName(\"ln(Jsat)\");\nADD_PROPERTY(m_psat , \"psat\" )->SetLongName(\"ln(Psat/Pr)\");\nADD_PROPERTY(m_asat , \"asat\" )->SetLongName(\"normalized saturation free energy\");\nADD_PROPERTY(m_ssat , \"ssat\" )->SetLongName(\"ln(normalized saturation entropy\");\nADD_PROPERTY(m_cvsat, \"cvsat\")->SetLongName(\"ln(normalized saturation cv\");\nADD_PROPERTY(m_D[0] , \"D0\"   )->SetLongName(\"1st normalized pressure coefficient\");\nADD_PROPERTY(m_D[1] , \"D1\" , FEProperty::Optional)->SetLongName(\"2nd normalized pressure coefficient\");\nADD_PROPERTY(m_D[2] , \"D2\" , FEProperty::Optional)->SetLongName(\"3rd normalized pressure coefficient\");\nADD_PROPERTY(m_D[3] , \"D3\" , FEProperty::Optional)->SetLongName(\"4th normalized pressure coefficient\");\nADD_PROPERTY(m_D[4] , \"D4\" , FEProperty::Optional)->SetLongName(\"5th normalized pressure coefficient\");\nADD_PROPERTY(m_D[5] , \"D5\" , FEProperty::Optional)->SetLongName(\"6th normalized pressure coefficient\");\nADD_PROPERTY(m_D[6] , \"D6\" , FEProperty::Optional)->SetLongName(\"7th normalized pressure coefficient\");\n\nADD_PROPERTY(m_C[0], \"C0\")->SetLongName(\"1st cv virial coeff\");\nADD_PROPERTY(m_C[1], \"C1\", FEProperty::Optional)->SetLongName(\"2nd cv virial coeff\");\nADD_PROPERTY(m_C[2], \"C2\", FEProperty::Optional)->SetLongName(\"3rd cv virial coeff\");\nADD_PROPERTY(m_C[3], \"C3\", FEProperty::Optional)->SetLongName(\"4th cv virial coeff\");\n\nEND_FECORE_CLASS();\n\nFERealVapor::FERealVapor(FEModel* pfem) : FEElasticFluid(pfem)\n{\n    m_nvp = 0;\n    m_nvc = 0;\n    m_Pr = m_Tr = 0;\n    m_Tc = 1;\n    for (int k=0; k<MAX_NVP; ++k) m_D[k] = nullptr;\n    for (int k=0; k<MAX_NVC; ++k) m_C[k] = nullptr;\n    m_psat = m_asat = m_ssat = m_esat = m_cvsat = nullptr;\n    m_alpha = 0.35; // hard-coded for now\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FERealVapor::Init()\n{\n    m_Tr = GetGlobalConstant(\"T\");\n    m_Pr = GetGlobalConstant(\"P\");\n    \n    if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    if (m_Pr <= 0) { feLogError(\"A positive referential absolute pressure P must be defined in Globals section\"); return false; }\n\n    m_pMat = dynamic_cast<FEThermoFluid*>(GetParent());\n    m_rhor = m_pMat->ReferentialDensity();\n\n    m_esat->Init();\n    m_psat->Init();\n    m_asat->Init();\n    m_ssat->Init();\n    m_cvsat->Init();\n    m_nvp = 0;\n    for (int k=0; k<MAX_NVP; ++k) {\n        if (m_D[k]) {\n            m_D[k]->Init();\n            ++m_nvp;\n        }\n    }\n    m_nvc = 0;\n    for (int k=0; k<MAX_NVC; ++k) {\n        if (m_C[k]) {\n            m_C[k]->Init();\n            ++m_nvc;\n        }\n    }\n\n    if (m_nvp < 1) { feLogError(\"At least one virial coefficient should be provided for the pressure\"); return false; }\n    if (m_nvc < 1) { feLogError(\"At least one virial coefficient should be provided for cv\"); return false; }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERealVapor::Serialize(DumpStream& ar)\n{\n    FEElasticFluid::Serialize(ar);\n\n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n    ar & m_Pr & m_Tr & m_rhor;\n    ar & m_nvp & m_nvc;\n}\n\n//-----------------------------------------------------------------------------\n//! gauge pressure\ndouble FERealVapor::Pressure(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double J = 1 + fp.m_ef;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double Jsat = exp(m_esat->value(q));\n    double Psat = exp(m_psat->value(q));\n    double D[MAX_NVP];\n    for (int k=0; k<m_nvp; ++k) D[k] = m_D[k]->value(q);\n    double x = Jsat/J;\n    double sum = 0;\n    for (int k=0; k<m_nvp; ++k) sum += D[k]*pow(x,k+1);\n    double p = Psat*(x + (1-x)*sum) - 1;\n    \n    return p*m_Pr;\n}\n\n//-----------------------------------------------------------------------------\n//! specific free energy\ndouble FERealVapor::SpecificFreeEnergy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double asat = m_asat->value(q);\n\n    double J = 1 + fp.m_ef;\n    double Jsat = exp(m_esat->value(q));\n    double Psat = exp(m_psat->value(q));\n    double D[MAX_NVP];\n    for (int k=0; k<m_nvp; ++k) D[k] = m_D[k]->value(q);\n    double x = Jsat/J;\n    double sum = 0;\n    for (int k=1; k<m_nvp; ++k) {\n        double xk = pow(x,k);\n        double xkp = xk*x;\n        sum += D[k]*((1-xkp)/(k+1) - (1-xk)/k);\n    }\n    double a = asat - Jsat*(1-1/x) + Psat*Jsat*(log(x) + D[0]*(log(x)+1-x) + sum);\n\n    return a*m_Pr/m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! specific entropy\ndouble FERealVapor::SpecificEntropy(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double ssat = exp(m_ssat->value(q));\n    \n    double J = 1 + fp.m_ef;\n    double Jsat = exp(m_esat->value(q));\n    double Psat = exp(m_psat->value(q));\n    double coef = -m_alpha*(1-exp(-q))/(m_Tc-That);\n    double dJsat = coef*Jsat*m_esat->derive(q);\n    double dPsat = coef*Psat*m_psat->derive(q);\n    double D[MAX_NVP], dD[MAX_NVP];\n    for (int k=0; k<m_nvp; ++k) {\n        D[k] = m_D[k]->value(q);\n        dD[k] = coef*m_D[k]->derive(q);\n    }\n    double x = Jsat/J;\n    double dx = dJsat/J;\n    double sum1 = 0, sum2 = 0, sum3 = 0;\n    for (int k=1; k<m_nvp; ++k) {\n        double xk = pow(x,k);\n        double xkp = xk*x;\n        double y = (1-xkp)/(k+1) - (1-xk)/k;\n        sum1 += D[k]*y;\n        sum2 += D[k]*xk;\n        sum3 += dD[k]*y;\n    }\n    \n    double z = log(x)+1-x;\n    double s = ssat - (dPsat*Jsat + Psat*dJsat)*(log(x) + D[0]*z + sum1)\n    - Psat*(1-x)*dJsat*(D[0] + sum2) - Psat*Jsat*(dD[0]*z + sum3);\n\n    return s*m_Pr/(m_Tr*m_rhor);\n}\n\n//-----------------------------------------------------------------------------\n//! specific strain energy\ndouble FERealVapor::SpecificStrainEnergy(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    // get the specific free energy\n    double a = SpecificFreeEnergy(mp);\n    \n    double That = (m_Tr+tf.m_T)/m_Tr;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double Jsat = exp(m_esat->value(q));\n    double asat = m_asat->value(q)*m_Pr/m_rhor;\n    \n    // the specific strain energy is the difference between these two values\n    return a - asat;\n}\n\n//-----------------------------------------------------------------------------\n//! isochoric specific heat capacity\ndouble FERealVapor::IsochoricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n\n    double J = 1 + fp.m_ef;\n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double Jsat = exp(m_esat->value(q));\n    double x = 1 - Jsat/J;\n    double cv = exp(m_cvsat->value(q));\n    for (int k=0; k<m_nvc; ++k) cv += m_C[k]->value(q)*pow(x,k+1);\n\n    return cv*m_Pr/(m_Tr*m_rhor);\n}\n\n//-----------------------------------------------------------------------------\n//! isobaric specific heat capacity\ndouble FERealVapor::IsobaricSpecificHeatCapacity(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double cv = IsochoricSpecificHeatCapacity(mp);\n    double p = Pressure(mp);    // current gauge pressure\n    double dpT = Tangent_Temperature(mp);\n    // evaluate dpT/dpJ in the reference configuration\n    double efsafe = fp.m_ef;\n    double Tsafe = tf.m_T;\n    fp.m_ef = 0;\n    tf.m_T = 0;\n    double r = Tangent_Temperature(mp)/Tangent_Strain(mp);\n    // restore values before continuing\n    fp.m_ef = efsafe;\n    tf.m_T = Tsafe;\n    // evaluate isobaric specific heat capacity\n    double cp = cv + r/m_rhor*(p - (m_Tr + tf.m_T)*dpT);\n    return cp;\n}\n\n//-----------------------------------------------------------------------------\n//! dilatation from temperature and pressure\nbool FERealVapor::Dilatation(const double T, const double p, double& e)\n{\n    // check that the prescribed gauge pressure is valid (positive)\n    if (p < 0) return false;\n    // if valid, continue\n    double Phat = 1 + p/m_Pr;\n    double That = (T+m_Tr)/m_Tr;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double Psat = exp(m_psat->value(q));\n    // check to make sure that we are in the vapor phase\n    if (Phat > Psat) return false;\n    // then continue\n    double Jsat = exp(m_esat->value(q));\n    double D[MAX_NVP];\n    vector <double> B(m_nvp+2,0);\n    for (int k=0; k<m_nvp; ++k) D[k] = m_D[k]->value(q);\n    B[0] = -Phat/Psat;\n    B[1] = 1 + D[0];\n    B[m_nvp+1] = -D[m_nvp-1];\n    for (int k=0; k<m_nvp-1; ++k) B[k+2] = D[k+1] - D[k];\n    double x = (-B[1]+sqrt(B[1]*B[1]-4*B[0]*B[2]))/(2*B[2]);\n    // check to see if we are infinitesimally close to the saturation curve\n    if (Psat - Phat < 1e-3) {\n        // if nearly on saturation curve, use saturation curve dilatation\n        e = Jsat/x - 1;\n        return true;\n    }\n    // initial guess for J depends if e = 0 or not\n    double J = (e == 0) ? Jsat/x : 1 + e;\n    // if one virial coefficient only, we're done\n    if (m_nvp == 1) { e = J - 1; return true; }\n    // solve iteratively for J using Newton's method\n    bool convgd = solvepoly(m_nvp, B, x, true);\n    J = Jsat/x;\n    e = J - 1;\n    return convgd;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to strain J\ndouble FERealVapor::Tangent_Strain(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double J = 1 + fp.m_ef;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double Jsat = exp(m_esat->value(q));\n    double Psat = exp(m_psat->value(q));\n    double D[MAX_NVP];\n    for (int k=0; k<m_nvp; ++k) D[k] = m_D[k]->value(q);\n    double x = Jsat/J;\n    double sum = 1;\n    for (int k=0; k<m_nvp; ++k) sum += D[k]*(pow(x,k)*(k+1-x*(k+2)));\n    double dx = -Jsat/pow(J,2);\n    double dpJ = Psat*dx*sum;\n    \n    return dpJ*m_Pr;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of pressure with respect to temperature T\ndouble FERealVapor::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& fp = *mp.ExtractData<FEFluidMaterialPoint>();\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    \n    double T = tf.m_T + m_Tr;\n    double That = T/m_Tr;\n    double J = 1 + fp.m_ef;\n    double y = (That < m_Tc) ? (m_Tc-That)/(m_Tc-1) : 0;\n    double q = log(1+pow(y,m_alpha));\n    double Jsat = exp(m_esat->value(q));\n    double Psat = exp(m_psat->value(q));\n    double coef = -m_alpha*(1-exp(-q))/(m_Tc-That);\n    double dJsat = coef*Jsat*m_esat->derive(q);\n    double dPsat = coef*Psat*m_psat->derive(q);\n    double D[MAX_NVP], dD[MAX_NVP];\n    for (int k=0; k<m_nvp; ++k) {\n        D[k] = m_D[k]->value(q);\n        dD[k] = coef*m_D[k]->derive(q);\n    }\n    double x = Jsat/J;\n    double sum1 = 0;\n    double sum2 = 1;\n    for (int k=0; k<m_nvp; ++k) {\n        sum1 += (dPsat*D[k]+Psat*dD[k])*pow(x,k+1);\n        sum2 += D[k]*(pow(x,k)*(k+1-x*(k+2)));\n    }\n    double dx = dJsat/J;\n    double dpT = x*dPsat + (1-x)*sum1 + Psat*dx*sum2;\n    \n    return dpT*m_Pr/m_Tr;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FERealVapor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFluid.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Elastic fluid model for real vapor\n//!\nclass FEBIOFLUID_API FERealVapor : public FEElasticFluid\n{\npublic:\n    enum { MAX_NVP = 7, MAX_NVC = 4 };\n    \npublic:\n    FERealVapor(FEModel* pfem);\n    ~FERealVapor() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! gauge pressure\n    double Pressure(FEMaterialPoint& pt) override;\n    \n    //! specific free energy\n    double SpecificFreeEnergy(FEMaterialPoint& mp) override;\n    \n    //! specific entropy\n    double SpecificEntropy(FEMaterialPoint& mp) override;\n    \n    //! specific strain energy\n    double SpecificStrainEnergy(FEMaterialPoint& mp) override;\n    \n    //! isochoric specific heat capacity\n    double IsochoricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! isobaric specific heat capacity\n    double IsobaricSpecificHeatCapacity(FEMaterialPoint& mp) override;\n    \n    //! dilatation from temperature and pressure\n    bool Dilatation(const double T, const double p, double& e) override;\n\n    //! tangent of pressure with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of pressure with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \npublic:\n    double          m_Pr;           //!< referential absolute pressure\n    double          m_Tr;           //!< referential absolute temperature\n    double          m_Tc;           //!< normalized critical temperature (Tc/Tr)\n    double          m_alpha;        //!< exponent alpha used for calculating temperature map\n    double          m_rhor;         //!< referential mass density\n    int             m_nvp;          //!< number of virial coefficients for pressure\n    int             m_nvc;          //!< number of virial coefficients for isochoric specific heat capacity\n    FEFunction1D*   m_D[MAX_NVP];   //!< non-dimensional pressure coefficient (multiply by m_Pr to get actual value)\n    FEFunction1D*   m_psat;         //!< normalized gauge pressure on saturation curve (multiply by m_Pr to get actual value)\n    FEFunction1D*   m_asat;         //!< normalized specific free energy on saturation curve (multiply by m_Pr/m_rhor to get actual value)\n    FEFunction1D*   m_ssat;         //!< normalized specific entropy on saturation curve (multiply by m_Pr/m_rhor*m_Tr to get actual value)\n    FEFunction1D*   m_esat;         //!< dilatation on saturation curve\n    FEFunction1D*   m_cvsat;        //!< non-dimensional isochoric specific heat capacity on saturation curve (multiply by m_Pr/m_rhor*m_Tr to get actual value)\n    FEFunction1D*   m_C[MAX_NVC];   //!< non-dimensional virial coefficients for isochoric specific heat capacity\n\n    FEThermoFluid*  m_pMat; //!< parent thermo-fluid material\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n    \n};\n"
  },
  {
    "path": "FEBioFluid/FESoluteBackflowStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESoluteBackflowStabilization.h\"\n#include \"FEFluidSolutes.h\"\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FENodeNodeList.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FESoluteBackflowStabilization, FESurfaceLoad)\n    ADD_PARAMETER(m_isol   , \"solute_id\")->setEnums(\"$(solutes)\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESoluteBackflowStabilization::FESoluteBackflowStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_isol = -1;\n    m_dofC = (pfem ? pfem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0) : -1);\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FESoluteBackflowStabilization::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FESoluteBackflowStabilization::Init()\n{\n    // determine the nr of concentration equations\n    FEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n    m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    if ((m_isol < 1) || (m_isol > MAX_CDOFS)) return false;\n    \n    m_dof.AddDofs(m_dofW);\n\tm_dof.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n\n    FESurface* ps = &GetSurface();\n    ps->UpdateNodeNormals();\n    \n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FESoluteBackflowStabilization::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    int dofc = m_dofC + m_isol - 1;\n    \n    m_ndof.assign(ps->Nodes(),DOF_OPEN);\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        m_ndof[i] = node.get_bc(dofc);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FESoluteBackflowStabilization::Update()\n{\n    // determine backflow conditions\n    MarkBackFlow();\n    \n    // prescribe solute backflow constraint at the nodes\n    FESurface* ps = &GetSurface();\n    \n    int dofc = m_dofC + m_isol - 1;\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        if (m_ndof[i] == DOF_OPEN) {\n            FENode& node = ps->Node(i);\n            // set node as having prescribed DOF (concentration at previous time)\n            if (node.get_bc(dofc) == DOF_PRESCRIBED) node.set(dofc,node.get_prev(dofc));\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface\nvoid FESoluteBackflowStabilization::MarkBackFlow()\n{\n    const FETimeInfo& tp = GetTimeInfo();\n    \n    // Mark all nodes on this surface to have open concentration DOF\n    FESurface* ps = &GetSurface();\n    int dofc = m_dofC + m_isol - 1;\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        if (m_ndof[i] == DOF_OPEN) {\n            node.set_bc(dofc, DOF_OPEN);\n            if (node.m_ID[dofc] < -1) node.m_ID[dofc] = -node.m_ID[dofc] - 2;\n            // check velocity normal to surface at this node\n            vec3d v = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            double vn = v*ps->NodeNormal(i);\n            if (vn < 0) {\n                node.set_bc(dofc, DOF_PRESCRIBED);\n                node.m_ID[dofc] = -node.m_ID[dofc] - 2;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculate residual\nvoid FESoluteBackflowStabilization::LoadVector(FEGlobalVector& R)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FESoluteBackflowStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n\tar & m_dofW & m_dofC;\n}\n"
  },
  {
    "path": "FEBioFluid/FESoluteBackflowStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEFluidSolutes.h\"\n#include <FECore/FENodeNodeList.h>\n\n//-----------------------------------------------------------------------------\n//! FESoluteBackflowStabilization is a fluid surface where solute concentration\n//! is maintained constant when the fluid velocity normal to the surface is\n//! inward.\n//!\nclass FEBIOFLUID_API FESoluteBackflowStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FESoluteBackflowStabilization(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! evaluate flow rate\n    void MarkBackFlow();\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    void SetSolute(int isol) { m_isol = isol; }\n\nprotected:\n    int         m_isol;          //!< solute id\n\nprivate:\n    FEDofList   m_dofW;\n    int         m_dofC;\n    vector<int> m_ndof;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FESoluteConvectiveFlow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESoluteConvectiveFlow.h\"\n#include \"FECore/FEElemElemList.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/FEGlobalVector.h\"\n#include \"FECore/LinearSolver.h\"\n#include <FECore/FEModel.h>\n#include \"FEBioFluidSolutes.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FESoluteConvectiveFlow, FESurfaceLoad)\n    ADD_PARAMETER(m_sol   , \"solute_id\")->setEnums(\"$(solutes)\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESoluteConvectiveFlow::FESoluteConvectiveFlow(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_sol = -1;\n    \n    if (pfem) m_dofW.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::RELATIVE_FLUID_VELOCITY));\n    m_dofEF = (pfem ? GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_DILATATION), 0) : -1);\n    m_dofC  = (pfem ? GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0) : -1);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FESoluteConvectiveFlow::Init()\n{\n    // determine the nr of concentration equations\n    FEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    if ((m_sol < 1) || (m_sol > MAX_CDOFS)) return false;\n    \n    FEMesh& mesh = GetMesh();\n    m_octree = new FEOctreeSearch(&mesh);\n    m_octree->Init();\n    \n    FESurface* ps = &GetSurface();\n    m_np = new FENormalProjection(*ps);\n    m_np->SetTolerance(0.01);\n    m_np->SetSearchRadius(1.0);\n    m_np->Init();\n    \n    int NN = mesh.Nodes();\n    m_bexclude.assign(NN, false);\n    \n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FESoluteConvectiveFlow::Activate()\n{\n    int dofc = m_dofC + m_sol - 1;\n    \n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = GetMesh();\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.get_bc(dofc) == DOF_PRESCRIBED) {\n            m_bexclude[i] = true;\n        }\n        else if (node.get_bc(dofc) == DOF_OPEN) {\n            // mark node as having prescribed DOF\n            node.set_bc(dofc, DOF_PRESCRIBED);\n        }\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n// return nodal value\nvoid FESoluteConvectiveFlow::Update()\n{\n    FEMesh& mesh = GetMesh();\n\n    const FETimeInfo& tp = GetTimeInfo();\n    double gamma = tp.gamma;\n    double dt = tp.timeIncrement;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        if (!m_bexclude[i]) {\n            FENode& node = mesh.Node(i);\n            vec3d x = node.m_rt;\n            vec3d vt = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d vp = node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            \n            vec3d X = x - (vt*gamma + vp*(1-gamma))*dt;\n            \n            int dofc = m_dofC + m_sol - 1;\n            double r[3] = { 0 };\n            double c = 0;\n            \n            // search for the solid element in which X lies\n            FESolidElement* el = (FESolidElement*)m_octree->FindElement(X, r);\n            if (el) {\n                const int NELN = FESolidElement::MAX_NODES;\n                double ep[NELN], cp[NELN];\n                int neln = el->Nodes();\n                for (int j=0; j<neln; ++j) {\n                    FENode& node = mesh.Node(el->m_node[j]);\n                    ep[j] = node.get_prev(m_dofEF);\n                    cp[j] = node.get_prev(dofc);\n                }\n                double Jt = 1 + node.get(m_dofEF);\n                double Jp = 1 + el->evaluate(ep, r[0], r[1], r[2]);\n                c = Jp*el->evaluate(cp, r[0], r[1], r[2])/Jt;\n            }\n            // if solid element is not found, project x onto the solute inlet surface\n            else {\n                FESurfaceElement* pme;\n                vec3d n = x - X;\n                n.unit();\n                pme = m_np->Project(x, n, r);\n                if (pme) {\n                    const int NELN = FEShellElement::MAX_NODES;\n                    double ep[NELN], cp[NELN];\n                    int neln = pme->Nodes();\n                    for (int j=0; j<neln; ++j) {\n                        FENode& mode = mesh.Node(pme->m_node[j]);\n                        ep[j] = mode.get_prev(m_dofEF);\n                        cp[j] = mode.get_prev(dofc);\n                    }\n                    double Jt = 1 + node.get(m_dofEF);\n                    double Jp = 1 + pme->eval(ep, r[0], r[1]);\n                    c = Jp*pme->eval(cp, r[0], r[1])/Jt;\n                }\n                else\n                    c = node.get_prev(dofc);\n            }\n            \n            if (node.m_ID[dofc] < -1)\n                node.set(dofc, c);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FESoluteConvectiveFlow::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofW & m_dofEF & m_dofC;\n}\n"
  },
  {
    "path": "FEBioFluid/FESoluteConvectiveFlow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEOctreeSearch.h>\n#include \"FECore/FENormalProjection.h\"\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FESoluteConvectiveFlow is a fluid domain where solute is transported\n//! purely via convection.  The solute concentration can be obtained from\n//! the fluid velocity and dilatation results.\n// TODO: This is not a surface load!\nclass FEBIOFLUID_API FESoluteConvectiveFlow : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FESoluteConvectiveFlow(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override {};\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\nprivate:\n    int         m_sol;      //!< solute id\n    FEDofList   m_dofW;\n    int         m_dofEF;\n    int         m_dofC;\n    vector<bool>    m_bexclude;\n    FENormalProjection* m_np;\n    FEOctreeSearch* m_octree;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FESolutesDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESolutesDomain.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include \"FEBioFluidSolutes.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFESolutesDomain::FESolutesDomain(FEModel* pfem) : FESolidDomain(pfem), m_dof(pfem)\n{\n\tm_pMat = 0;\n\tm_btrans = true;\n\n\tm_dofC  = (pfem ? pfem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), 0) : -1);\n\tm_dofAC = (pfem ? pfem->GetDOFIndex(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV), 0) : -1);\n\n\t// list the degrees of freedom\n\t// (This allows the FEDomain base class to handle several tasks such as UnpackLM)\n//\tvector<int> dof;\n//\tSetDOFList(dof);\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dofs\nconst FEDofList& FESolutesDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FESolutesDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tif (pmat)\n\t{\n\t\tm_pMat = dynamic_cast<FESolutesMaterial*>(pmat);\n\t\tassert(m_pMat);\n\t}\n\telse m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FESolutesDomain::Init()\n{\n\t// initialize base class\n\tif (FESolidDomain::Init() == false) return false;\n\n\tconst int nsol = m_pMat->Solutes();\n\n\t// set the active degrees of freedom list\n\tm_dof.Clear();\n\tfor (int i = 0; i<nsol; ++i)\n\t{\n\t\tint m = m_pMat->GetSolute(i)->GetSoluteDOF();\n\t\tm_dof.AddDof(m_dofC + m);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::Reset()\n{\n\t// reset base class\n\tFESolidDomain::Reset();\n\n\tconst int nsol = m_pMat->Solutes();\n\n\tfor (int i = 0; i<(int)m_Elem.size(); ++i)\n\t{\n\t\t// get the solid element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the number of integration points\n\t\tint nint = el.GaussPoints();\n\n\t\t// loop over the integration points\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tFESolutesMaterial::Point& ps = *(mp.ExtractData<FESolutesMaterial::Point>());\n\n\t\t\t// initialize solutes\n            ps.m_nsol = nsol;\n            ps.m_c.assign(nsol,0);\n            ps.m_ca.assign(nsol,0);\n            ps.m_cdot.assign(nsol,0);\n            ps.m_gradc.assign(nsol,vec3d(0,0,0));\n            ps.m_j.assign(nsol,vec3d(0,0,0));\n            ps.m_k.assign(nsol, 0);\n            ps.m_dkdJ.assign(nsol, 0);\n            ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n            \n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->ResetElementData(mp);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pMat;\n    ar & m_dofC & m_dofAC;\n    ar & m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::Activate()\n{\n\tconst int nsol = m_pMat->Solutes();\n\tfor (int i = 0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tfor (int isol = 0; isol<nsol; ++isol)\n\t\t\t\tnode.set_active(m_dofC + m_pMat->GetSolute(isol)->GetSoluteDOF());\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::InitMaterialPoints()\n{\n\tconst int nsol = m_pMat->Solutes();\n\tFEMesh& m = *GetMesh();\n\n\tconst int NE = FEElement::MAX_NODES;\n\tvector< vector<double> > c0(nsol, vector<double>(NE));\n\tvector<int> sid(nsol);\n\tfor (int j = 0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n\n\tfor (int j = 0; j<(int)m_Elem.size(); ++j)\n\t{\n\t\t// get the solid element\n\t\tFESolidElement& el = m_Elem[j];\n\n\t\t// get the number of nodes\n\t\tint neln = el.Nodes();\n\t\t// get initial values of fluid pressure and solute concentrations\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tFENode& ni = m.Node(el.m_node[i]);\n\t\t\tfor (int isol = 0; isol<nsol; ++isol)\n\t\t\t\tc0[isol][i] = ni.get(m_dofC + sid[isol]);\n\t\t}\n\n\t\t// get the number of integration points\n\t\tint nint = el.GaussPoints();\n\n\t\t// loop over the integration points\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tFESolutesMaterial::Point& ps = *(mp.ExtractData<FESolutesMaterial::Point>());\n\n\t\t\t// initialize solutes\n\t\t\tps.m_nsol = nsol;\n\n\t\t\t// initialize effective solute concentrations\n\t\t\tfor (int isol = 0; isol<nsol; ++isol) {\n                ps.m_c[isol] = el.Evaluate(c0[isol], n);\n                ps.m_ca[isol] = m_pMat->ConcentrationActual(mp, isol);\n                ps.m_gradc[isol] = gradient(el, c0[isol], n);\n\t\t\t}\n\n\t\t\tfor (int isol = 0; isol<nsol; ++isol)\n\t\t\t\tps.m_j[isol] = m_pMat->SoluteFlux(mp, isol);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FESolutesDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tconst int NE = FEElement::MAX_NODES;\n\tvec3d x0[NE], r0, v;\n\tFEMesh& m = *GetMesh();\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\t\tint neln = el.Nodes();\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tx0[i] = m.Node(el.m_node[i]).m_r0;\n\t\t}\n\n\t\tint n = el.GaussPoints();\n\t\tfor (int j = 0; j<n; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tmp.m_r0 = el.Evaluate(x0, j);\n            // reset chemical reaction element data\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->InitializeElementData(mp);\n\t\t\tmp.Update(timeInfo);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::InternalForces(FEGlobalVector& R)\n{\n\tint NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\t// element force vector\n\t\tvector<double> fe;\n\t\tvector<int> lm;\n\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the element force vector and initialize it to zero\n\t\tint nsol = m_pMat->Solutes();\n\t\tint ndof = nsol*el.Nodes();\n\t\tfe.assign(ndof, 0);\n\n\t\t// calculate internal force vector\n\t\tElementInternalForce(el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble element 'fe'-vector into global R vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FESolutesDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n\t// jacobian matrix, inverse jacobian matrix and determinants\n\tdouble Ji[3][3];\n\n\t// get the time step size\n\tdouble dt = tp.timeIncrement;\n\n\t// number of solutes\n\tconst int nsol = m_pMat->Solutes();\n    \n    const int nreact = m_pMat->Reactions();\n\n\t// gradient of shape functions\n\tint neln = el.Nodes();\n\tvector<vec3d> gradN(neln);\n\n\t// repeat for all integration points\n\tint nint = el.GaussPoints();\n\tdouble* gw = el.GaussWeights();\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFESolutesMaterial::Point& spt = *(mp.ExtractData<FESolutesMaterial::Point>());\n\n\t\t// calculate the jacobian\n\t\tdouble detJ = invjac0(el, Ji, n)*gw[n];\n\n\t\tvec3d g1(Ji[0][0], Ji[0][1], Ji[0][2]);\n\t\tvec3d g2(Ji[1][0], Ji[1][1], Ji[1][2]);\n\t\tvec3d g3(Ji[2][0], Ji[2][1], Ji[2][2]);\n\n\t\tdouble* H = el.H(n);\n\t\tdouble* Gr = el.Gr(n);\n\t\tdouble* Gs = el.Gs(n);\n\t\tdouble* Gt = el.Gt(n);\n\n\t\t// evaluate spatial gradient of shape functions\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tgradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n\t\t}\n        \n        vector<double> kappa(spt.m_k);\n        double osmc;\n        osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        \n        // evaluate the chat\n        vector<double> chat(nsol,0);\n        double phiwhat = 0;\n        \n        // chemical reactions\n        for (int i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += pri->m_Vbar*zhat;\n            for (int isol=0; isol<nsol; ++isol)\n            {\n                chat[isol] += zhat*pri->m_v[isol];\n            }\n        }\n\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfor (int isol = 0; isol<nsol; ++isol)\n\t\t\t\tfe[nsol*i + isol] -= (spt.m_j[isol]*gradN[i] - H[i]*(spt.m_cdot[isol]*kappa[isol] - chat[isol]))*detJ;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FESolutesDomain::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n\t// Get the current element's data\n\tconst int nint = el.GaussPoints();\n\tconst int neln = el.Nodes();\n\tconst int nsol = m_pMat->Solutes();\n    const int nreact = m_pMat->Reactions();\n\n\t// gradient of shape functions\n\tvector<vec3d> gradN(neln);\n\n\tdouble dt = tp.timeIncrement;\n\tdouble ksi = tp.alpham / (tp.gamma*tp.alphaf);\n\n\t// jacobian\n\tdouble Ji[3][3];\n\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// calculate element stiffness matrix\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdouble detJ = invjac0(el, Ji, n)*gw[n];\n\n\t\tvec3d g1(Ji[0][0], Ji[0][1], Ji[0][2]);\n\t\tvec3d g2(Ji[1][0], Ji[1][1], Ji[1][2]);\n\t\tvec3d g3(Ji[2][0], Ji[2][1], Ji[2][2]);\n\n\t\tdouble* H = el.H(n);\n\t\tdouble* Gr = el.Gr(n);\n\t\tdouble* Gs = el.Gs(n);\n\t\tdouble* Gt = el.Gt(n);\n\n\t\t// setup the material point\n\t\t// NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFESolutesMaterial::Point& spt = *(mp.ExtractData<FESolutesMaterial::Point>());\n        \n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double osmc;\n        osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodJ = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Strain(mp);\n        vector<double> dodc(nsol);\n        for (int isol=0; isol<nsol; ++isol)\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n        \n        // evaluate the chat\n        vector<vector<double>> dchatdc(nsol,vector<double>(nsol,0));\n        vector<double> dphiwhatdc(nsol,0);\n        \n        // chemical reactions\n        for (int i=0; i<nreact; ++i) {\n            for (int isol = 0; isol < nsol; ++isol){\n                dphiwhatdc[isol] += m_pMat->GetReaction(i)->m_Vbar\n                *m_pMat->GetReaction(i)->Tangent_ReactionSupply_Concentration(mp,isol);\n                for (int jsol = 0; jsol < nsol; ++jsol){\n                    dchatdc[isol][jsol] += m_pMat->GetReaction(i)->m_v[isol]\n                    *m_pMat->GetReaction(i)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                }\n            }\n        }\n\n\t\t// evaluate spatial gradient of shape functions\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t\tgradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n\n\t\t// evaluate stiffness matrix\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t{\n\t\t\t\tfor (int isol = 0; isol<nsol; ++isol) {\n                    for(int jsol=0; jsol<nsol; ++jsol){\n                        double kcc = 0;\n                        double d0p = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp, jsol);\n                        double d0 = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n                        double kappa = spt.m_k[isol];\n                        if (isol == jsol)\n                            //TODO: add partition coeff terms\n                            kcc = -(ksi/dt*m_btrans*kappa-dchatdc[isol][jsol])*H[i]*H[j]-gradN[i]*spt.m_gradc[isol]*H[j]*d0p+gradN[i]*(-gradN[j]*d0+spt.m_vft*H[j]);\n                        else\n                            kcc = H[i]*H[j]*dchatdc[isol][jsol] + (-gradN[i]*spt.m_gradc[isol]*d0p)*H[j];\n                        ke[i*nsol + isol][j*nsol + jsol] += kcc*detJ;\n                    }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\n#pragma omp parallel for shared (NE)\n\tfor (int iel = 0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\n\t\t// create the element's stiffness matrix\n\t\tint nsol = m_pMat->Solutes();\n\t\tint ndof = nsol*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate material stiffness\n\t\tElementStiffness(el, ke);\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesDomain::Update(const FETimeInfo& tp)\n{\n\tbool berr = false;\n\tint NE = (int)m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\ttry\n\t\t{\n\t\t\tUpdateElementStress(i, tp);\n\t\t}\n\t\tcatch (NegativeJacobian e)\n\t\t{\n#pragma omp critical\n\t\t\t{\n\t\t\t\t// reset the logfile mode\n\t\t\t\tberr = true;\n\t\t\t\tif (NegativeJacobian::DoOutput()) feLogError(e.what());\n\t\t\t}\n\t\t}\n\t}\n\n\tif (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FESolutesDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n\t// time constants\n\tdouble alphaf = tp.alphaf;\n\tdouble alpham = tp.alpham;\n\n\t// get the solid element\n\tFESolidElement& el = m_Elem[iel];\n\n\t// number of nodes\n\tint neln = el.Nodes();\n\n\t// number of solutes\n\tconst int nsol = m_pMat->Solutes();\n\n\t// nodal coordinates\n\tconst int NELN = FEElement::MAX_NODES;\n\tvector< vector<double> > ct(nsol, vector<double>(NELN));\n\tvector< vector<double> > cp(nsol, vector<double>(NELN));\n\tvector< vector<double> > act(nsol, vector<double>(NELN));\n\tvector< vector<double> > acp(nsol, vector<double>(NELN));\n\tvector<int> sid(nsol);\n\tfor (int j = 0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n\tfor (int j = 0; j<neln; ++j) {\n\t\tFENode& node = m_pMesh->Node(el.m_node[j]);\n\t\tfor (int k = 0; k<nsol; ++k) {\n\t\t\tct[k][j] = node.get(m_dofC + sid[k]);\n\t\t\tcp[k][j] = node.get_prev(m_dofC + sid[k]);\n\t\t\tact[k][j] = node.get(m_dofAC + sid[k]);\n\t\t\tacp[k][j] = node.get_prev(m_dofAC + sid[k]);\n\t\t}\n\t}\n\n\t// loop over the integration points and update\n\t// velocity, velocity gradient, acceleration\n\t// stress and pressure at the integration point\n\tint nint = el.GaussPoints();\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFESolutesMaterial::Point& spt = *(mp.ExtractData<FESolutesMaterial::Point>());\n\n\t\t// material point data\n\t\tfor (int isol = 0; isol < nsol; ++isol) {\n\t\t\tspt.m_c[isol] = el.Evaluate(ct[isol], n)*alphaf + el.Evaluate(cp[isol], n)*(1 - alphaf);\n\t\t\tspt.m_gradc[isol] = gradient(el, ct[isol], n)*alphaf + gradient(el, cp[isol], n)*(1 - alphaf);\n            spt.m_cdot[isol] = 0.0;\n            if (m_btrans) spt.m_cdot[isol] += el.Evaluate(act[isol], n)*alpham + el.Evaluate(acp[isol], n)*(1-alpham);\n\t\t}\n        \n        m_pMat->PartitionCoefficientFunctions(mp, spt.m_k, spt.m_dkdJ, spt.m_dkdc);\n        \n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n\n\t\t// calculate the solute flux\n        for (int isol = 0; isol < nsol; ++isol){\n\t\t\tspt.m_j[isol] = m_pMat->SoluteFlux(mp, isol);\n            spt.m_ca[isol] = m_pMat->ConcentrationActual(mp, isol);\n        }\n        // update chemical reaction element data\n        for (int j=0; j<m_pMat->Reactions(); ++j)\n            m_pMat->GetReaction(j)->UpdateElementData(mp);\n\t}\n}\n"
  },
  {
    "path": "FEBioFluid/FESolutesDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FESolutesMaterial.h\"\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FESolutesDomain : public virtual FESolidDomain\n{\npublic:\n\t//! constructor\n\tFESolutesDomain(FEModel* pfem);\n\t~FESolutesDomain() {}\n\n\t//! initialize elements\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\tvoid SetSteadyStateAnalysis() { m_btrans = false; }\n\tvoid SetTransientAnalysis() { m_btrans = true; }\n\npublic: // overrides from FEDomain\n\n\t//! get the material\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pm) override;\n\n\t//! get the total dofs\n\tconst FEDofList& GetDOFList() const override;\n\npublic: // overrides from FEElasticDomain\n\n\t\t//! initialize class\n\tbool Init() override;\n\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n\t//! Reset data\n\tvoid Reset() override;\n\n\t//! activate\n\tvoid Activate() override;\n\n\t//! initialize material points in the domain\n\tvoid InitMaterialPoints() override;\n\n\t// update stresses\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t// update the element stress\n\tvoid UpdateElementStress(int iel, const FETimeInfo& tp);\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R);\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS);\n\npublic:\n\t// --- S T I F F N E S S ---\n\n\t//! calculates the solid element stiffness matrix\n\tvoid ElementStiffness(FESolidElement& el, matrix& ke);\n\n\t// --- R E S I D U A L ---\n\n\t//! Calculates the internal stress vector for solid elements\n\tvoid ElementInternalForce(FESolidElement& el, vector<double>& fe);\n\nprotected:\n\tbool\tm_btrans;\n\tint     m_dofC;\n\tint     m_dofAC;\n\tFEDofList\tm_dof;\n\n\tFESolutesMaterial*     m_pMat;\n};\n"
  },
  {
    "path": "FEBioFluid/FESolutesDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FESolutesDomainFactory.h\"\n#include \"FESolutesMaterial.h\"\n#include \"FESolutesDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEDomain* FESolutesDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tFEModel* pfem = pmat->GetFEModel();\n\tFE_Element_Class eclass = spec.eclass;\n\tFE_Element_Shape eshape = spec.eshape;\n\tconst char* sztype = 0;\n\tif (dynamic_cast<FESolutesMaterial*>(pmat))\n\t{\n\t\t// fluid elements\n\t\tif (eclass == FE_ELEM_SOLID) sztype = \"solutes-3D\";\n\t\telse return 0;\n\t}\n\n\tif (sztype)\n\t{\n\t\tFEDomain* pd = fecore_new<FESolidDomain>(sztype, pfem);\n\t\tif (pd) pd->SetMaterial(pmat);\n\t\treturn pd;\n\t}\n\telse return 0;\n}\n"
  },
  {
    "path": "FEBioFluid/FESolutesDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FESolutesDomainFactory : public FEDomainFactory\n{\npublic:\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FESolutesMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESolutesMaterial.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FESolutesMaterial, FEMaterial)\n\tADD_PROPERTY(m_pSolute, \"solute\", FEProperty::Optional);\n    ADD_PROPERTY(m_pOsmC  , \"osmotic_coefficient\");\n    ADD_PROPERTY(m_pReact , \"reaction\"           , FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEFluidSolutesMaterialPoint\n//============================================================================\nFESolutesMaterial::Point::Point(FEMaterialPointData* pt) : FEMaterialPointData(pt)\n{\n\tm_vft = vec3d(0.0, 0.0, 0.0);\n\tm_JfdotoJf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FESolutesMaterial::Point::Copy()\n{\n\tFESolutesMaterial::Point* pt = new FESolutesMaterial::Point(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesMaterial::Point::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_vft & m_JfdotoJf;\n    ar & m_nsol;\n    ar & m_c & m_ca & m_gradc & m_j & m_cdot & m_k & m_dkdJ;\n    ar & m_dkdc;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesMaterial::Point::Init()\n{\n    m_nsol = 0;\n    m_c.clear();\n    m_ca.clear();\n    m_gradc.clear();\n    m_j.clear();\n    m_cdot.clear();\n    m_k.clear();\n    m_dkdJ.clear();\n    m_dkdc.clear();\n\n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolutesMaterial::Point::Osmolarity() const\n{\n    double ew = 0.0;\n    for (int isol = 0; isol < (int)m_ca.size(); ++isol)\n    {\n        ew += m_ca[isol];\n    }\n    return ew;\n}\n\n\n//============================================================================\n// FESolutesMaterial\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEFluidSolutes constructor\n\nFESolutesMaterial::FESolutesMaterial(FEModel* pfem) : FEMaterial(pfem)\n{\n    m_Rgas = 0; m_Tabs = 0; m_Fc = 0;\n    m_pOsmC = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FESolutesMaterial::CreateMaterialPointData()\n{\n    FEFluidMaterialPoint* fpt = new FEFluidMaterialPoint();\n    return new FESolutesMaterial::Point(fpt);\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FESolutesMaterial::Init()\n{\n    // set the solute IDs first, since they are referenced in FESolute::Init()\n    for (int i = 0; i<Solutes(); ++i) {\n        m_pSolute[i]->SetSoluteLocalID(i);\n    }\n    \n    // call the base class.\n    // This also initializes all properties\n    if (FEMaterial::Init() == false) return false;\n    \n    int zmin = 0, zmax = 0;\n    \n    m_Rgas = GetGlobalConstant(\"R\");\n    m_Tabs = GetGlobalConstant(\"T\");\n    m_Fc   = GetGlobalConstant(\"Fc\");\n    \n    if (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n    if (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");     return false; }\n    if ((zmin || zmax) && (m_Fc <= 0)) {\n        feLogError(\"A positive Faraday constant Fc must be defined in Globals section\");\n        return false;\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesMaterial::Serialize(DumpStream& ar)\n{\n    FEMaterial::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_Rgas & m_Tabs & m_Fc;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficient\ndouble FESolutesMaterial::PartitionCoefficient(FEMaterialPoint& pt, const int sol)\n{\n    \n    // solubility\n    double khat = m_pSolute[sol]->m_pSolub->Solubility(pt);\n    double kappa = khat;\n    \n    return kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficients and their derivatives\nvoid FESolutesMaterial::PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                                   vector<double>& dkdJ,\n                                                   vector< vector<double> >& dkdc)\n{\n    int isol, jsol;\n    \n    FEFluidMaterialPoint& fpt = *(mp.ExtractData<FEFluidMaterialPoint>());\n    FESolutesMaterial::Point& spt = *mp.ExtractData<FESolutesMaterial::Point>();\n    \n    const int nsol = (int)m_pSolute.size();\n    \n    vector<double> c(nsol);\n    vector<int> z(nsol);\n    vector<double> khat(nsol);\n    vector<double> dkhdJ(nsol);\n    vector< vector<double> > dkhdc(nsol, vector<double>(nsol));\n    kappa.resize(nsol);\n    \n    for (isol=0; isol<nsol; ++isol) {\n        // get the effective concentration, its gradient and its time derivative\n        c[isol] = spt.m_c[isol];\n        // evaluate the solubility and its derivatives w.r.t. J and c\n        khat[isol] = m_pSolute[isol]->m_pSolub->Solubility(mp);\n        dkhdJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain(mp);\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkhdc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration(mp,jsol);\n            dkdc[isol][jsol]=dkhdc[isol][jsol];\n        }\n        kappa[isol] = khat[isol];\n        dkdJ[isol] = dkhdJ[isol];\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FESolutesMaterial::Concentration(FEMaterialPoint& pt, const int sol)\n{\n\tFESolutesMaterial::Point& spt = *pt.ExtractData<FESolutesMaterial::Point>();\n    \n    // effective concentration\n    double c = spt.m_c[sol];\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FESolutesMaterial::ConcentrationActual(FEMaterialPoint& pt, const int sol)\n{\n    FESolutesMaterial::Point& spt = *pt.ExtractData<FESolutesMaterial::Point>();\n    \n    // effective concentration\n    double ca = spt.m_c[sol];\n    \n    // partition coefficient\n    double kappa = PartitionCoefficient(pt, sol);\n    \n    ca = kappa*ca;\n    \n    return ca;\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure\ndouble FESolutesMaterial::PressureActual(FEMaterialPoint& pt)\n{\n    int i;\n    \n    FEFluidMaterialPoint& fpt = *pt.ExtractData<FEFluidMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    \n    // effective pressure\n    double p = fpt.m_pf;\n    \n    // effective concentration\n    vector<double> c(nsol);\n    for (i=0; i<nsol; ++i)\n        c[i] = Concentration(pt, i);\n    \n    // osmotic coefficient\n    double osmc = m_pOsmC->OsmoticCoefficient(pt);\n    \n    // actual pressure\n    double pa = 0;\n    for (i=0; i<nsol; ++i) pa += c[i];\n    pa = p + m_Rgas*m_Tabs*osmc*pa;\n    \n    return pa;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate solute molar flux\n\nvec3d FESolutesMaterial::SoluteFlux(FEMaterialPoint& pt, const int sol)\n{\n\tFESolutesMaterial::Point& spt = *pt.ExtractData<FESolutesMaterial::Point>();\n    \n    // concentration gradient\n    vec3d gradc = spt.m_gradc[sol];\n    \n    // solute free diffusivity\n    double D0 = m_pSolute[sol]->m_pDiff->Free_Diffusivity(pt);\n    \n    double c = spt.m_c[sol];\n    vec3d v = spt.m_vft;\n    \n    // solute flux j\n    vec3d j = -gradc*D0 + v*c;\n    \n    return j;\n}\n"
  },
  {
    "path": "FEBioFluid/FESolutesMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FEBioMix/FESolute.h>\n#include <FEBioMix/FESoluteInterface.h>\n#include <FEBioMix/FEOsmoticCoefficient.h>\n#include <FEBioMix/FEChemicalReaction.h>\n#include \"febiofluid_api.h\"\n#include \"FEFluid.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FESolutesMaterial : public FEMaterial, public FESoluteInterface\n{\npublic:\n\tclass Point : public FEMaterialPointData\n\t{\n\tpublic:\n\t\t//! constructor\n\t\tPoint(FEMaterialPointData* pt);\n\n\t\t//! create a shallow copy\n\t\tFEMaterialPointData* Copy();\n\n\t\t//! data serialization\n\t\tvoid Serialize(DumpStream& ar);\n\n\t\t//! Data initialization\n\t\tvoid Init();\n\n        //! Osmolarity        \n        double Osmolarity() const;\n\n\tpublic:\n\t\tvec3d\tm_vft;\t\t// fluid velocity at integration point\n\t\tdouble\tm_JfdotoJf;\t\t// divergence of fluid velocity\n\n\t\t// solutes material data\n\t\tint                 m_nsol;     //!< number of solutes\n\t\tvector<double>      m_c;        //!< solute concentration\n        vector<double>      m_ca;        //!< effective solute concentration\n\t\tvector<vec3d>       m_gradc;    //!< spatial gradient of solute concentration\n\t\tvector<vec3d>       m_j;        //!< solute molar flux\n\t\tvector<double>      m_cdot;     //!< material time derivative of solute concentration following fluid\n        vector<double>    m_k;        //!< solute partition coefficient\n        vector<double>    m_dkdJ;        //!< 1st deriv of m_k with strain (J)\n        vector< vector<double> >    m_dkdc;            //!< 1st deriv of m_k with effective concentration\n\t};\n\npublic:\n\tFESolutesMaterial(FEModel* pfem);\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t//! performs initialization\n\tbool Init() override;\n\n    //! data serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n\t//! calculate solute molar flux\n\tvec3d SoluteFlux(FEMaterialPoint& pt, const int sol);\n\n\t//! actual concentration (as opposed to effective concentration)\n\tdouble Concentration(FEMaterialPoint& pt, const int sol);\n    \n    //! actual concentration (as opposed to effective concentration)\n    double ConcentrationActual(FEMaterialPoint& pt, const int sol);\n    \n    //! actual fluid pressure (as opposed to effective pressure)\n    double PressureActual(FEMaterialPoint& pt);\n    \n    //! partition coefficient\n    double PartitionCoefficient(FEMaterialPoint& pt, const int sol);\n    \n    //! partition coefficients and their derivatives\n    void PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                       vector<double>& dkdJ,\n                                       vector< vector<double> >& dkdc);\n    \n    //! solute density\n    double SoluteDensity(const int sol) { return m_pSolute[sol]->Density(); }\n    \n    //! solute molar mass\n    double SoluteMolarMass(const int sol) { return m_pSolute[sol]->MolarMass(); }\n    \n    //! Add a chemical reaction\n    void AddChemicalReaction(FEChemicalReaction* pcr);\n\n\t// solute interface\npublic:\n    typedef FESolutesMaterial::Point SolutesMaterial_t;\n\n\tint Solutes() override { return (int)m_pSolute.size(); }\n\tFESolute* GetSolute(int i) override { return m_pSolute[i]; }\n    double GetEffectiveSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) override {\n        SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->m_c[soluteIndex];\n    };\n    double GetActualSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) override {\n        SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->m_ca[soluteIndex];\n    };\n    double GetPartitionCoefficient(FEMaterialPoint& mp, int soluteIndex) override {\n        SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->m_k[soluteIndex];\n    };\n    vec3d GetSoluteFlux(FEMaterialPoint& mp, int soluteIndex) override {\n        SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->m_j[soluteIndex];\n    };\n    double GetOsmolarity(const FEMaterialPoint& mp) override {\n        const SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->Osmolarity();\n    }\n    double dkdc(const FEMaterialPoint& mp, int i, int j) override {\n        const SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->m_dkdc[i][j];\n    }\n    double dkdJ(const FEMaterialPoint& mp, int soluteIndex) override {\n        const SolutesMaterial_t* spt = (mp.ExtractData<SolutesMaterial_t>());\n        return spt->m_dkdJ[soluteIndex];\n    }\n    FEOsmoticCoefficient* GetOsmoticCoefficient() override { return m_pOsmC; }\n\npublic:\n    FEChemicalReaction*            GetReaction            (int i) { return m_pReact[i];  }\n    \n    int Reactions         () { return (int) m_pReact.size();    }\npublic:\n    double    m_Rgas;            //!< universal gas constant\n    double    m_Tabs;            //!< absolute temperature\n    double    m_Fc;              //!< Faraday's constant\n\nprivate: // material properties\n\tstd::vector<FESolute*>  m_pSolute;      //!< pointer to solute materials\n    FEOsmoticCoefficient*        m_pOsmC;        //!< pointer to osmotic coefficient material\n    std::vector<FEChemicalReaction*>    m_pReact;        //!< pointer to chemical reactions\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FESolutesSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FESolutesSolver.h\"\n#include \"FESolutesDomain.h\"\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/sys.h>\n#include <FEBioMech/FEBodyForce.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n#include <FECore/FENLConstraint.h>\n#include \"FEBioFluidSolutes.h\"\n#include <assert.h>\n#include \"FEFluidSolutesAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FESolutesSolver, FENewtonSolver)\n\tADD_PARAMETER(m_Ctol , \"ctol\"        );\n    ADD_PARAMETER(m_Etol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n    ADD_PARAMETER(m_Rtol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n    ADD_PARAMETER(m_rhoi , \"rhoi\"        );\n\tADD_PARAMETER(m_pred , \"predictor\"   );\n\tADD_PARAMETER(m_forcePositive, \"force_positive_concentrations\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEFluidSolutesSolver Construction\n//\nFESolutesSolver::FESolutesSolver(FEModel* pfem) : FENewtonSolver(pfem), m_dofC(pfem), m_dofAC(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Ctol = 0.01;\n    m_Rmin = 1.0e-20;\n    m_Rmax = 0;     // not used if zero\n    \n    m_niter = 0;\n    \n    // assume non-symmetric stiffness\n    m_msymm = REAL_UNSYMMETRIC;\n    \n    m_forcePositive = true;    // force all concentrations to remain positive\n\n    m_rhoi = 0;\n    m_pred = 0;\n    \n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n    \n    // turn off checking for a zero diagonal\n    CheckZeroDiagonal(false);\n    \n    // Allocate degrees of freedom\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        DOFS& dofs = pfem->GetDOFS();\n        int varC = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION), VAR_ARRAY);\n        int varAC = dofs.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV), VAR_ARRAY);\n    }\n}\n\n//-----------------------------------------------------------------------------\nFESolutesSolver::~FESolutesSolver()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEFluidSolutesSolver\n//\nbool FESolutesSolver::Init()\n{\n    // initialize base class\n    if (FENewtonSolver::Init() == false) return false;\n    \n    // check parameters\n    if (m_Ctol <  0.0) { feLogError(\"ctol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_gammaf = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        m_alpham = (3-m_rhoi)/(1+m_rhoi)/2;\n        m_gammaf = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    \n    // get number of DOFS\n    FEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    // allocate concentration-vectors\n    m_ci.assign(MAX_CDOFS,vector<double>(0,0));\n    m_Ci.assign(MAX_CDOFS,vector<double>(0,0));\n    for (int i=0; i<MAX_CDOFS; ++i) {\n        m_ci[i].assign(m_nceq[i], 0);\n        m_Ci[i].assign(m_nceq[i], 0);\n    }\n    vector<int> dofs;\n    for (int j=0; j<(int)m_nceq.size(); ++j) {\n        if (m_nceq[j]) {\n            dofs.push_back(m_dofC[j]);\n        }\n    }\n\n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n    FEMesh& mesh = fem.GetMesh();\n    gather(m_Ut, mesh, dofs);\n\n    // set flag for transient or steady-state analyses\n    for (int i = 0; i<mesh.Domains(); ++i)\n    {\n        FESolutesDomain& dom = dynamic_cast<FESolutesDomain&>(mesh.Domain(i));\n        if (fem.GetCurrentStep()->m_nanalysis == FEFluidSolutesAnalysis::STEADY_STATE)\n            dom.SetSteadyStateAnalysis();\n        else\n            dom.SetTransientAnalysis();\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FESolutesSolver::InitEquations()\n{\n\tm_dofC.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n\tm_dofAC.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV));\n\n\tAddSolutionVariable(&m_dofC, 1, \"concentration\", m_Ctol);\n\n    // base class initialization\n    FENewtonSolver::InitEquations();\n    \n    // determined the nr of velocity and dilatation equations\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // determine the nr of concentration equations\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    m_nceq.assign(MAX_CDOFS, 0);\n    \n    // get number of DOFS\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            if (n.m_ID[m_dofC[j]] != -1) m_nceq[j]++;\n        }\n    }\n\n\t// add up all equation\n\tm_nCeq = 0;\n\tfor (int i = 0; i < m_nceq.size(); ++i) m_nCeq += m_nceq[i];\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FESolutesSolver::InitEquations2()\n{\n\tm_dofC.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n\tm_dofAC.AddVariable(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION_TDERIV));\n\n\tAddSolutionVariable(&m_dofC, -1, \"concentration\", m_Ctol);\n\n\t// base class initialization\n\tFENewtonSolver::InitEquations2();\n\n\t// determined the nr of velocity and dilatation equations\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// determine the nr of concentration equations\n\tDOFS& fedofs = fem.GetDOFS();\n\tint MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n\tm_nceq.assign(MAX_CDOFS, 0);\n\n\t// get number of DOFS\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& n = mesh.Node(i);\n\t\tfor (int j = 0; j<MAX_CDOFS; ++j) {\n\t\t\tif (n.m_ID[m_dofC[j]] != -1) m_nceq[j]++;\n\t\t}\n\t}\n\n\t// add up all equation\n\tm_nCeq = 0;\n\tfor (int i = 0; i < m_nceq.size(); ++i) m_nCeq += m_nceq[i];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesSolver::GetConcentrationData(vector<double> &ci, vector<double> &ui, const int sol)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ci);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofC[sol]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ci[m++] = ui[nid];\n            assert(m <= (int) ci.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FESolutesSolver::UpdateKinematics(vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    \n    // update solute data\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // update nodal concentration\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            int n = node.m_ID[m_dofC[j]];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if ((ct < 0.0) && m_forcePositive) ct = 0.0;\n                node.set(m_dofC[j], ct);\n            }\n        }\n    }\n    \n\n    // make sure the prescribed dofs are fulfilled\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.Update();\n    }\n    \n    // prescribe DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i=0; i<nsl; ++i)\n    {\n        FEModelLoad& psl = *fem.ModelLoad(i);\n//        if (psl.IsActive() && HasActiveDofs(psl.GetDofList())) psl.Update();\n        if (psl.IsActive()) psl.Update();\n    }\n    \n    // enforce the linear constraints\n    // TODO: do we really have to do this? Shouldn't the algorithm\n    // already guarantee that the linear constraints are satisfied?\n    FELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    if (LCM.LinearConstraints() > 0)\n    {\n        LCM.Update();\n    }\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEFluidSolutesAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n        double dt = fem.GetTime().timeIncrement;\n        double cgi = 1 - 1.0/m_gammaf;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // concentration time derivative\n            // update nodal concentration\n            for (int j=0; j<MAX_CDOFS; ++j) {\n                int k = n.m_ID[m_dofC[j]];\n                // Force the concentrations to remain positive\n                if (k >= 0) {\n                    double ct = n.get(m_dofC[j]);\n                    double cp = n.get_prev(m_dofC[j]);\n                    double acp = n.get_prev(m_dofAC[j]);\n                    double act = acp*cgi + (ct - cp)/(m_gammaf*dt);\n                    n.set(m_dofC[j], ct);\n                    n.set(m_dofAC[j], act);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolutesSolver::Update2(const vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    \n    // update solute data\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // update nodal concentration\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            int n = node.m_ID[m_dofC[j]];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if ((ct < 0.0) && m_forcePositive) ct = 0.0;\n                node.set(m_dofC[j], ct);\n            }\n        }\n    }\n    \n    // Update the prescribed nodes\n    for (int i = 0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.m_rid == -1)\n        {\n            vec3d dv(0, 0, 0);\n            for (int j = 0; j < node.m_ID.size(); ++j)\n            {\n                int nj = -node.m_ID[j] - 2; if (nj >= 0) node.set(j, node.get(j) + ui[nj]);\n            }\n        }\n    }\n    \n    // update model state\n    GetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FESolutesSolver::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update kinematics\n    UpdateKinematics(ui);\n    \n    // update model state\n    UpdateModel();\n}\n\n//-----------------------------------------------------------------------------\nbool FESolutesSolver::InitStep(double time)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // set time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    tp.gamma = m_gammaf;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n    double dt = tp.timeIncrement;\n    double ta = (t > 0) ? t - (1-m_alphaf)*dt : m_alphaf*dt;\n    \n    return FESolver::InitStep(ta);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FESolutesSolver::PrepStep()\n{\n    FEModel& fem = *GetFEModel();\n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n\n    FETimeInfo& tp = fem.GetTime();\n    double dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n\n    // zero total DOFs\n    zero(m_Ui);\n    for (int j=0; j<(int)m_nceq.size(); ++j) if (m_nceq[j]) zero(m_Ci[j]);\n    \n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt = ni.m_r0;\n        ni.m_dp = ni.m_dt = ni.m_d0;\n        ni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n\t\t\t\t{\n\t\t\t\t\t// update nodal concentration\n\t\t\t\t\tfor (int j=0; j<MAX_CDOFS; ++j)\n\t\t\t\t\t\tni.set(m_dofAC[j], ni.get_prev(m_dofAC[j])*(m_gammaf-1)/m_gammaf);\n\t\t\t\t}\n\t\t\t\tbreak;\n            case 1:\n\t\t\t\t{\n\t\t\t\t\tfor (int j=0; j<MAX_CDOFS; ++j)\n\t\t\t\t\t\tni.set(m_dofC[j], ni.get_prev(m_dofC[j]) + ni.get_prev(m_dofAC[j])*dt*(1-m_gammaf)*m_alphaf);\n\t\t\t\t}\n                break;\n            case 2:\n\t\t\t\t{\n\t\t\t\t\tfor (int j=0; j<MAX_CDOFS; ++j)\n\t\t\t\t\t\tni.set(m_dofC[j], ni.get_prev(m_dofC[j]) + ni.get_prev(m_dofAC[j])*dt);\n\t\t\t\t}\n                break;\n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.PrepStep(ui);\n    }\n    \n    // do the linear constraints\n    fem.GetLinearConstraintManager().PrepStep();\n\n    // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n    // update domain data\n    for (int i=0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n    \n    // update stresses\n    fem.Update();\n    \n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive() && HasActiveDofs(pml.GetDofList())) pml.PrepStep();\n    }\n\n    // see if we need to do contact augmentations\n    m_baugment = false;\n    \n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FESolutesSolver::Quasin()\n{\n    FEModel& fem = *GetFEModel();\n    // convergence norms\n    double    normR1;\t\t// residual norm\n    double    normE1;\t\t// energy norm\n    double    normRi = 0;\t// initial residual norm\n    double    normEi = 0;\t// initial energy norm\n    double    normEm = 0;\t// max energy norm\n    \n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(FEBioFluidSolutes::GetVariableName(FEBioFluidSolutes::FLUID_CONCENTRATION));\n    \n    // solute convergence data\n    vector<double>    normCi(MAX_CDOFS);    // initial concentration norm\n    vector<double>    normC(MAX_CDOFS);    // current concentration norm\n    vector<double>    normc(MAX_CDOFS);    // incremement concentration norm\n    \n    // Get the current step\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    \n    // prepare for the first iteration\n    const FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n    // Init QN method\n    if (QNInit() == false) return false;\n\n    // loop until converged or when max nr of reformations reached\n    bool bconv = false; // convergence flag\n    do\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n        \n        // solve the equations (returns line search; solution stored in m_ui)\n        double s = QNSolve();\n\n       \n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normEm = normEi;\n        }\n        \n        // calculate norms\n        // update all degrees of freedom\n        for (int i=0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n        normE1 = s*fabs(m_ui*m_R1);\n        \n        // check for nans\n        if (ISNAN(normR1)) throw NANInResidualDetected();\n        \n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n        if ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        if (normE1 > normEm) bconv = false;\n        \n        // check solute convergence\n        // extract the concentration increments\n        for (int j = 0; j<(int)m_nceq.size(); ++j) {\n            if (m_nceq[j]) {\n                GetConcentrationData(m_ci[j], m_ui,j);\n                \n                // set initial norm\n                if (m_niter == 0)\n                    normCi[j] = fabs(m_ci[j]*m_ci[j]);\n                \n                // update total concentration\n                for (int i = 0; i<m_nceq[j]; ++i) m_Ci[j][i] += s*m_ci[j][i];\n                \n                // calculate norms\n                normC[j] = m_Ci[j]*m_Ci[j];\n                normc[j] = (m_ci[j]*m_ci[j])*(s*s);\n                \n            }\n        }\n        \n        // check convergence\n        if (m_Ctol > 0) {\n            for (int j = 0; j<(int)m_nceq.size(); ++j)\n                if (m_nceq[j]) bconv = bconv && (normc[j] <= (m_Ctol*m_Ctol)*normC[j]);\n        }\n        \n        // print convergence summary\n        feLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n        feLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n        if (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        for (int j = 0; j<(int)m_nceq.size(); ++j) {\n            if (m_nceq[j])\n                feLog(\"\\t solute %d concentration %15le %15le %15le\\n\", j+1, normCi[j], normc[j] ,(m_Ctol*m_Ctol)*normC[j] );\n        }\n\n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n        // see if we have exceeded the max residual\n        if ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n        {\n            // doesn't look like we're getting anywhere, so let's retry the time step\n            throw MaxResidualError();\n        }\n        \n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n            if (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n                feLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n                QNForceReform(true);\n            }\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n                feLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                for (int j = 0; j<(int)m_nceq.size(); ++j)\n                    if (m_nceq[j]) normCi[j] = normc[j];\n                QNForceReform(true);\n            }\n            \n            // Do the QN update (This may also do a stiffness reformation if necessary)\n            bool bret = QNUpdate();\n            \n            // something went wrong with the update, so we'll need to break\n            if (bret == false) break;\n        }\n        else if (m_baugment)\n        {\n            // Do augmentations\n            bconv = DoAugmentations();\n        }\n\n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        m_Ut += m_Ui;\n        zero(m_Ui);\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FESolutesSolver::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    const FETimeInfo& tp = fem.GetTime();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n\t\tFESolutesDomain& dom = dynamic_cast<FESolutesDomain&>(mesh.Domain(i));\n        dom.StiffnessMatrix(LS);\n    }\n    \n    // calculate stiffness matrix due to model loads\n    const int nml = fem.ModelLoads();\n    for (int i=0; i<nml; ++i)\n    {\n        FEModelLoad* pml = fem.ModelLoad(i);\n//        if (pml->IsActive() && HasActiveDofs(pml->GetDofList())) pml->StiffnessMatrix(LS);\n        if (pml->IsActive()) pml->StiffnessMatrix(LS);\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry\n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FESolutesSolver::Residual(vector<double>& R)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the time information\n    const FETimeInfo& tp = fem.GetTime();\n    \n    // initialize residual with concentrated nodal loads\n    zero(R);\n    \n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n    FEGlobalVector RHS(fem, R, m_Fr);\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FESolutesDomain& dom = dynamic_cast<FESolutesDomain&>(mesh.Domain(i));\n        dom.InternalForces(RHS);\n    }\n    \n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i=0; i<NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive()) mli.LoadVector(RHS);\n    }\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FESolutesSolver::Serialize(DumpStream& ar)\n{\n    FENewtonSolver::Serialize(ar);\n\n    ar & m_neq & m_nceq;\n    ar & m_nrhs & m_niter & m_nref & m_ntotref;\n\n    ar & m_Fr & m_Ui & m_Ut;\n    ar & m_Ci;\n\n    if (ar.IsLoading())\n    {\n        m_Fr.assign(m_neq, 0);\n        for (int i=0; i<m_nceq.size(); ++i) {\n            m_ci[i].assign(m_nceq[i], 0);\n            m_Ci[i].assign(m_nceq[i], 0);\n        }\n    }\n    \n    if (ar.IsShallow()) return;\n\n    ar & m_alphaf & m_alpham;\n    ar & m_gammaf;\n    ar & m_pred;\n    ar & m_dofC & m_dofAC;\n}\n"
  },
  {
    "path": "FEBioFluid/FESolutesSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FENewtonSolver.h>\n\nclass FESolutesSolver : public FENewtonSolver\n{\npublic:\n    //! constructor\n\tFESolutesSolver(FEModel* pfem);\n    \n    //! destructor\n    ~FESolutesSolver();\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n\tbool InitEquations2() override;\n\n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n    \n    //! update nodal positions, velocities, accelerations, etc.\n    void UpdateKinematics(vector<double>& ui);\n    \n    //! used by JFNK\n    void Update2(const vector<double>& ui) override;\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //{ --- Residual routines ---\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n\npublic:\n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    void GetConcentrationData(vector<double>& ci, vector<double>& ui, const int sol);\n\npublic:\n    // convergence tolerances\n    double  m_Ctol;     //!< concentration tolerance\n    bool    m_forcePositive;    //!< force conentrations to remain positive\n\npublic:\n    // equation numbers\n    vector<int> m_nceq;     //!< number of equations related to concentration dofs\n\tint\t\t\tm_nCeq;\t\t//!< total number of concentration dofs\n\npublic:\n    vector<double> m_Fr;    //!< nodal reaction forces\n    \n    // solute data\n    vector< vector<double> >    m_ci;    //!< concentration increment vector\n    vector< vector<double> >    m_Ci;    //!< Total concentration vector for iteration\n\n    // generalized alpha method\n    double  m_rhoi;         //!< rho infinity\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_gammaf;       //!< gamma\n    int     m_pred;         //!< predictor method\n    \nprotected:\n    FEDofList\tm_dofC;\n    FEDofList\tm_dofAC;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FETangentialDamping.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETangentialDamping.h\"\n#include <FECore/FEMesh.h>\n#include \"FEBioFluid.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FETangentialDamping, FESurfaceLoad)\n\tADD_PARAMETER(m_eps, \"penalty\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETangentialDamping::FETangentialDamping(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_eps = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FETangentialDamping::Init()\n{\n    \n    m_dofW.Clear();\n    if (m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY)) == false) return false;\n\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofW);\n\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialDamping::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofW;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETangentialDamping::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETangentialDamping::FluidVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n\tvec3d vt[FEElement::MAX_NODES];\n\tFESurfaceElement& el = *mp.SurfaceElement();\n\tint neln = el.Nodes();\n\tfor (int j = 0; j<neln; ++j) {\n\t\tFENode& node = m_psurf->Node(el.m_lnode[j]);\n\t\tvt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1. - alpha);\n\t}\n\treturn el.eval(vt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialDamping::LoadVector(FEGlobalVector& R)\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\tm_psurf->LoadVector(R, m_dofW, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\t// fluid velocity\n\t\tvec3d v = FluidVelocity(mp, tp.alphaf);\n\n\t\tvec3d n = mp.dxr ^ mp.dxs;\n\t\tdouble da = n.unit();\n\n\t\t// force vector\n\t\tmat3dd I(1);\n\t\tvec3d f = (I - dyad(n))*v*(-m_eps*da);\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * f.x;\n\t\tfa[1] = H * f.y;\n\t\tfa[2] = H * f.z;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialDamping::StiffnessMatrix(FELinearSystem& LS)\n{\n\tm_psurf->LoadStiffness(LS, m_dofW, m_dofW, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n   \n        vec3d n = mp.dxr ^ mp.dxs;\n        double da = n.unit();\n        \n\t\tmat3dd I(1);\n\t\tmat3ds K = (I - dyad(n))*(-m_eps*da);\n\n\t\t// shape functions\n\t\tdouble H_a = dof_a.shape;\n\t\tdouble H_b = dof_b.shape;\n\n        // calculate stiffness component\n\t\tmat3ds kab = K*(H_a*H_b);\n\t\tKab.set(0, 0, kab);\n\t});\n}\n"
  },
  {
    "path": "FEBioFluid/FETangentialDamping.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Tangential damping prescribes a shear traction that opposes tangential\n//! fluid velocity on a boundary surface.  This can help stabilize inflow\n//! conditions.\nclass FEBIOFLUID_API FETangentialDamping : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FETangentialDamping(FEModel* pfem);\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! data serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \nprotected:\n\tvec3d FluidVelocity(FESurfaceMaterialPoint& mp, double alpha);\n\nprotected:\n    double\t\t\tm_eps;      //!< damping coefficient (penalty)\n    \n    // degrees of freedom\n\tFEDofList\tm_dofW;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FETangentialFlowFSIStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETangentialFlowFSIStabilization.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FETangentialFlowFSIStabilization, FESurfaceLoad)\n    ADD_PARAMETER(m_beta, \"beta\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETangentialFlowFSIStabilization::FETangentialFlowFSIStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofU(pfem), m_dofW(pfem)\n{\n    m_beta = 1.0;\n    \n    // get the degrees of freedom\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.Clear();\n        m_dofU.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::DISPLACEMENT));\n\n        m_dofW.Clear();\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n\n        m_dof.Clear();\n        m_dof.AddDofs(m_dofU);\n        m_dof.AddDofs(m_dofW);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETangentialFlowFSIStabilization::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FETangentialFlowFSIStabilization::Init()\n{\n\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowFSIStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofU & m_dofW;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETangentialFlowFSIStabilization::FluidVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n    FESurfaceElement& el = *mp.SurfaceElement();\n    vec3d vt[FEElement::MAX_NODES];\n    int neln = el.Nodes();\n    for (int j = 0; j<neln; ++j) {\n        FENode& node = m_psurf->Node(el.m_lnode[j]);\n        vt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1 - alpha);\n    }\n    return el.eval(vt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowFSIStabilization::LoadVector(FEGlobalVector& R)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    m_psurf->LoadVector(R, m_dofW, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n        FESurfaceElement& el = *mp.SurfaceElement();\n        \n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n        \n        // tangent vectors\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        vec3d dxr = el.eval_deriv1(rt, mp.m_index);\n        vec3d dxs = el.eval_deriv2(rt, mp.m_index);\n        \n        // normal and area element\n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n\n        // fluid velocity\n        vec3d v = FluidVelocity(mp, tp.alphaf);\n\n        // tangential traction = -beta*density*|tangential velocity|*(tangential velocity)\n        mat3dd I(1.0);\n        vec3d vtau = (I - dyad(n))*v;\n        double vmag = vtau.norm();\n\n        // force vector (change sign for inflow vs outflow)\n        vec3d f = vtau*(-m_beta*rho*vmag*da);\n\n        double H = dof_a.shape;\n        fa[0] = H * f.x;\n        fa[1] = H * f.y;\n        fa[2] = H * f.z;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowFSIStabilization::StiffnessMatrix(FELinearSystem& LS)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n    m_psurf->LoadStiffness(LS, m_dof, m_dof, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n    \n        FESurfaceElement& el = *mp.SurfaceElement();\n\n        // fluid velocity\n        vec3d v = FluidVelocity(mp, tp.alphaf);\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n        \n        // tangent vectors\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        vec3d dxr = el.eval_deriv1(rt, mp.m_index);\n        vec3d dxs = el.eval_deriv2(rt, mp.m_index);\n        \n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n        \n        mat3dd I(1.0);\n        vec3d vtau = (I - dyad(n))*v;\n        double vmag = vtau.unit();\n        mat3d K = (I - dyad(n) + dyad(vtau))*(-m_beta*rho*vmag*da);\n        // force vector (change sign for inflow vs outflow)\n        vec3d ttt = vtau*(-m_beta*rho*vmag);\n\n        // shape functions and derivatives\n        double H_i  = dof_a.shape;\n\n        double H_j  = dof_b.shape;\n        double Gr_j = dof_b.shape_deriv_r;\n        double Gs_j = dof_b.shape_deriv_s;\n\n        // calculate stiffness component\n        mat3d Kww = K*(H_i*H_j*tp.alphaf);\n        vec3d g = (dxr*Gs_j - dxs*Gr_j)*(H_i*tp.alphaf);\n        mat3d Kwu; Kwu.skew(g);\n        Kwu = (ttt & n)*Kwu;\n        Kab.zero();\n\n        // dw/du\n        Kab.sub(3, 0, Kwu);\n\n        // dw/dw\n        Kab.sub(3, 3, Kww);\n    });\n}\n"
  },
  {
    "path": "FEBioFluid/FETangentialFlowFSIStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Tangential flow stabilization prescribes a shear traction that opposes\n//! tangential fluid velocity on a boundary surface, in the presence of normal\n//! flow.  This can help stabilize inflow/outflow conditions.\nclass FEBIOFLUID_API FETangentialFlowFSIStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FETangentialFlowFSIStabilization(FEModel* pfem);\n\n    //! Initialization\n    bool Init() override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    vec3d FluidVelocity(FESurfaceMaterialPoint& mp, double alpha);\n    \nprotected:\n    double            m_beta;     //!< damping coefficient\n    \n    // degrees of freedom\n    FEDofList    m_dofU;\n    FEDofList    m_dofW;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FETangentialFlowPFStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETangentialFlowPFStabilization.h\"\n#include \"FEBioPolarFluid.h\"\n#include \"FEPolarFluid.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FETangentialFlowPFStabilization, FESurfaceLoad)\n    ADD_PARAMETER(m_beta, \"beta\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETangentialFlowPFStabilization::FETangentialFlowPFStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem), m_dofG(pfem)\n{\n    m_beta = 1.0;\n    \n    // get the degrees of freedom\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofW.Clear();\n        m_dofW.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::RELATIVE_FLUID_VELOCITY));\n\n        m_dofG.Clear();\n        m_dofG.AddVariable(FEBioPolarFluid::GetVariableName(FEBioPolarFluid::FLUID_ANGULAR_VELOCITY));\n        \n        m_dof.Clear();\n        m_dof.AddDofs(m_dofW);\n        m_dof.Clear();\n        m_dof.AddDofs(m_dofW);\n        m_dof.AddDofs(m_dofG);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETangentialFlowPFStabilization::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FETangentialFlowPFStabilization::Init()\n{\n    \n    if (FESurfaceLoad::Init() == false) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowPFStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_dofW & m_dofG;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETangentialFlowPFStabilization::FluidVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n    FESurfaceElement& el = *mp.SurfaceElement();\n    vec3d vt[FEElement::MAX_NODES];\n    int neln = el.Nodes();\n    for (int j = 0; j<neln; ++j) {\n        FENode& node = m_psurf->Node(el.m_lnode[j]);\n        vt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1 - alpha);\n    }\n    return el.eval(vt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETangentialFlowPFStabilization::FluidAngularVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n    FESurfaceElement& el = *mp.SurfaceElement();\n    vec3d gt[FEElement::MAX_NODES];\n    int neln = el.Nodes();\n    for (int j = 0; j<neln; ++j) {\n        FENode& node = m_psurf->Node(el.m_lnode[j]);\n        gt[j] = node.get_vec3d(m_dofG[0], m_dofG[1], m_dofG[2])*alpha + node.get_vec3d_prev(m_dofG[0], m_dofG[1], m_dofG[2])*(1 - alpha);\n    }\n    return el.eval(gt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowPFStabilization::LoadVector(FEGlobalVector& R)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    \n    m_psurf->LoadVector(R, m_dof, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n        \n        FESurfaceElement& el = *mp.SurfaceElement();\n        \n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEPolarFluid* pfluid = pm->ExtractProperty<FEPolarFluid>();\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n        double kg = (pfluid) ? pfluid->m_kg : 0;\n        \n        // tangent vectors\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        vec3d dxr = el.eval_deriv1(rt, mp.m_index);\n        vec3d dxs = el.eval_deriv2(rt, mp.m_index);\n        \n        // normal and area element\n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n        \n        // fluid velocity\n        vec3d v = FluidVelocity(mp, tp.alphaf);\n        \n        // fluid angular velocity\n        vec3d g = FluidAngularVelocity(mp, tp.alphaf);\n        \n        // tangential traction = -beta*density*|tangential velocity|*(tangential velocity)\n        mat3dd I(1.0);\n        vec3d vtau = (I - dyad(n))*v;\n        double vmag = vtau.norm();\n        vec3d gtau = (I - dyad(n))*g;\n        double gmag = gtau.norm();\n\n        // force vector (change sign for inflow vs outflow)\n        vec3d fv = vtau*(-m_beta*rho*vmag*da);\n        vec3d fg = gtau*(-m_beta*rho*pow(kg,3)*gmag*da);\n\n        double H = dof_a.shape;\n        fa[0] = H * fv.x;\n        fa[1] = H * fv.y;\n        fa[2] = H * fv.z;\n        fa[3] = H * fg.x;\n        fa[4] = H * fg.y;\n        fa[5] = H * fg.z;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowPFStabilization::StiffnessMatrix(FELinearSystem& LS)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    \n    m_psurf->LoadStiffness(LS, m_dof, m_dof, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n        \n        FESurfaceElement& el = *mp.SurfaceElement();\n        \n        // fluid velocity\n        vec3d v = FluidVelocity(mp, tp.alphaf);\n        \n        // fluid angular velocity\n        vec3d g = FluidAngularVelocity(mp, tp.alphaf);\n        \n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEPolarFluid* pfluid = pm->ExtractProperty<FEPolarFluid>();\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n        double kg = (pfluid) ? pfluid->m_kg : 0;\n\n        // tangent vectors\n        vec3d rt[FEElement::MAX_NODES];\n        m_psurf->GetNodalCoordinates(el, tp.alphaf, rt);\n        vec3d dxr = el.eval_deriv1(rt, mp.m_index);\n        vec3d dxs = el.eval_deriv2(rt, mp.m_index);\n        \n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n        \n        mat3dd I(1.0);\n        vec3d vtau = (I - dyad(n))*v;\n        double vmag = vtau.unit();\n        mat3d Kv = (I - dyad(n) + dyad(vtau))*(-m_beta*rho*vmag*da);\n        \n        vec3d gtau = (I - dyad(n))*g;\n        double gmag = gtau.unit();\n        mat3d Kg = (I - dyad(n) + dyad(gtau))*(-m_beta*rho*pow(kg,3)*gmag*da);\n        \n        // shape functions and derivatives\n        double H_i  = dof_a.shape;\n        \n        double H_j  = dof_b.shape;\n\n        // calculate stiffness component\n        mat3d Kww = Kv*(H_i*H_j*tp.alphaf);\n        mat3d Kgg = Kg*(H_i*H_j*tp.alphaf);\n        Kab.zero();\n        \n        // dw/dw\n        Kab.sub(0, 0, Kww);\n        // dg/dg\n        Kab.sub(3, 3, Kgg);\n    });\n}\n"
  },
  {
    "path": "FEBioFluid/FETangentialFlowPFStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Tangential flow stabilization prescribes a shear traction that opposes\n//! tangential fluid velocity on a boundary surface, in the presence of normal\n//! flow.  This can help stabilize inflow/outflow conditions.\nclass FEBIOFLUID_API FETangentialFlowPFStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FETangentialFlowPFStabilization(FEModel* pfem);\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    vec3d FluidVelocity(FESurfaceMaterialPoint& mp, double alpha);\n    vec3d FluidAngularVelocity(FESurfaceMaterialPoint& mp, double alpha);\n\nprotected:\n    double  m_beta;     //!< damping coefficient for viscous surface traction and coupld\n\n    // degrees of freedom\n    FEDofList    m_dofW;\n    FEDofList    m_dofG;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FETangentialFlowStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETangentialFlowStabilization.h\"\n#include \"FEFluidMaterial.h\"\n#include \"FEBioFluid.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FETangentialFlowStabilization, FESurfaceLoad)\n    ADD_PARAMETER(m_beta, \"beta\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETangentialFlowStabilization::FETangentialFlowStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n    m_beta = 1.0;\n    \n    // get the degrees of freedom\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofW.Clear();\n        m_dofW.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n\n        m_dof.Clear();\n        m_dof.AddDofs(m_dofW);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETangentialFlowStabilization::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FETangentialFlowStabilization::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n\tar & m_dofW;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETangentialFlowStabilization::FluidVelocity(FESurfaceMaterialPoint& mp, double alpha)\n{\n\tFESurfaceElement& el = *mp.SurfaceElement();\n\tvec3d vt[FEElement::MAX_NODES];\n\tint neln = el.Nodes();\n\tfor (int j = 0; j<neln; ++j) {\n\t\tFENode& node = m_psurf->Node(el.m_lnode[j]);\n\t\tvt[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1 - alpha);\n\t}\n\treturn el.eval(vt, mp.m_index);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowStabilization::LoadVector(FEGlobalVector& R)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n\tm_psurf->LoadVector(R, m_dofW, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n        FESurfaceElement& el = *mp.SurfaceElement();\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n\n        // tangent vectors\n        vec3d r0[FEElement::MAX_NODES];\n        m_psurf->GetReferenceNodalCoordinates(el, r0);\n        vec3d dxr = el.eval_deriv1(r0, mp.m_index);\n        vec3d dxs = el.eval_deriv2(r0, mp.m_index);\n        \n        // normal and area element\n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n\n\t\t// fluid velocity\n\t\tvec3d v = FluidVelocity(mp, tp.alphaf);\n\n\t\t// tangential traction = -beta*density*|tangential velocity|*(tangential velocity)\n\t\tmat3dd I(1.0);\n\t\tvec3d vtau = (I - dyad(n))*v;\n\t\tdouble vmag = vtau.norm();\n\n\t\t// force vector (change sign for inflow vs outflow)\n\t\tvec3d f = vtau*(-m_beta*rho*vmag*da);\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * f.x;\n\t\tfa[1] = H * f.y;\n\t\tfa[2] = H * f.z;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FETangentialFlowStabilization::StiffnessMatrix(FELinearSystem& LS)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n\n\tm_psurf->LoadStiffness(LS, m_dofW, m_dofW, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n    \n\t\tFESurfaceElement& el = *mp.SurfaceElement();\n\n        // get the density\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FEFluidMaterial* fluid = pm->ExtractProperty<FEFluidMaterial>();\n        double rho = fluid->ReferentialDensity();\n\n        // fluid velocity\n\t\tvec3d v = FluidVelocity(mp, tp.alphaf);\n\n\t\t// tangent vectors\n\t\tvec3d r0[FEElement::MAX_NODES];\n\t\tm_psurf->GetReferenceNodalCoordinates(el, r0);\n\t\tvec3d dxr = el.eval_deriv1(r0, mp.m_index);\n\t\tvec3d dxs = el.eval_deriv2(r0, mp.m_index);\n        \n        vec3d n = dxr ^ dxs;\n        double da = n.unit();\n        \n\t\tmat3dd I(1.0);\n\t\tvec3d vtau = (I - dyad(n))*v;\n        double vmag = vtau.unit();\n        mat3d K = (I - dyad(n) + dyad(vtau))*(-m_beta*rho*vmag*da);\n\n\t\t// shape functions and derivatives\n\t\tdouble H_i  = dof_a.shape;\n\n\t\tdouble H_j  = dof_b.shape;\n\n        // calculate stiffness component\n\t\tmat3d Kww = K*(H_i*H_j*tp.alphaf);\n\t\tKab.zero();\n\n\t\t// dw/dw\n\t\tKab.sub(0, 0, Kww);\n\t});\n}\n"
  },
  {
    "path": "FEBioFluid/FETangentialFlowStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Tangential flow stabilization prescribes a shear traction that opposes\n//! tangential fluid velocity on a boundary surface, in the presence of normal\n//! flow.  This can help stabilize inflow/outflow conditions.\nclass FEBIOFLUID_API FETangentialFlowStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FETangentialFlowStabilization(FEModel* pfem);\n\n\t//! Initialization\n\tbool Init() override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! serialize data\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n\tvec3d FluidVelocity(FESurfaceMaterialPoint& mp, double alpha);\n    \nprotected:\n    double\t\t\tm_beta;     //!< damping coefficient\n    \n    // degrees of freedom\n\tFEDofList\tm_dofW;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FETempDependentConductivity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n//\n//  FEFluidConstantConductivity.cpp\n//  FEBioFluid\n//\n//  Created by Gerard Ateshian on 2/28/20.\n//  Copyright © 2020 febio.org. All rights reserved.\n//\n\n#include \"FETempDependentConductivity.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETempDependentConductivity, FEFluidThermalConductivity)\n\n    // parameters\n    ADD_PARAMETER(m_Kr, \"Kr\")->setLongName(\"referential thermal conductivity\")->setUnits(UNIT_THERMAL_CONDUCTIVITY);\n    // properties\n    ADD_PROPERTY(m_Khat, \"Khat\")->SetLongName(\"normalized thermal conductivity\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETempDependentConductivity::FETempDependentConductivity(FEModel* pfem) : FEFluidThermalConductivity(pfem)\n{\n    m_Khat = nullptr;\n    m_Kr = 0;\n    m_Tr = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FETempDependentConductivity::Init()\n{\n    m_Tr = GetGlobalConstant(\"T\");\n    \n    if (m_Tr <= 0) { feLogError(\"A positive referential absolute temperature T must be defined in Globals section\"); return false; }\n    \n    m_Khat->Init();\n    \n    return FEFluidThermalConductivity::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETempDependentConductivity::Serialize(DumpStream& ar)\n{\n    FEFluidThermalConductivity::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_Kr & m_Tr;\n    ar & m_Khat;\n    \n    if (ar.IsLoading()) {\n        m_Khat->Init();\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculate thermal conductivity at material point\ndouble FETempDependentConductivity::ThermalConductivity(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double That = (tf.m_T+m_Tr)/m_Tr;\n    return m_Khat->value(That)*m_Kr;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of thermal conductivity with respect to strain J\ndouble FETempDependentConductivity::Tangent_Strain(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of thermal conductivity with respect to temperature T\ndouble FETempDependentConductivity::Tangent_Temperature(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tf = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double That = (tf.m_T+m_Tr)/m_Tr;\n    return m_Khat->derive(That)*m_Kr/m_Tr;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FETempDependentConductivity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFluidThermalConductivity.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid thermal conductivity materials.\n\nclass FEBIOFLUID_API FETempDependentConductivity : public FEFluidThermalConductivity\n{\npublic:\n    FETempDependentConductivity(FEModel* pfem);\n    virtual ~FETempDependentConductivity() {}\n    \npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! calculate thermal conductivity at material point\n    double ThermalConductivity(FEMaterialPoint& pt) override;\n    \n    //! tangent of thermal conductivity with respect to strain J\n    double Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of thermal conductivity with respect to temperature T\n    double Tangent_Temperature(FEMaterialPoint& mp) override;\n    \npublic:\n    FEFunction1D*   m_Khat; //!< normalized thermal conductivity\n    double          m_Kr;   //!< thermal conductivity at reference temperature\n    double          m_Tr;   //!< reference temperature\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n    \n};\n"
  },
  {
    "path": "FEBioFluid/FETemperatureBackFlowStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETemperatureBackFlowStabilization.h\"\n#include \"FEFluid.h\"\n#include \"FEBioThermoFluid.h\"\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETemperatureBackFlowStabilization::FETemperatureBackFlowStabilization(FEModel* pfem) : FESurfaceLoad(pfem), m_dofW(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETemperatureBackFlowStabilization::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FETemperatureBackFlowStabilization::Init()\n{\n    FEModel& fem = *GetFEModel();\n    \n    // determine the nr of concentration equations\n    m_dofW.Clear();\n    m_dofW.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofT = fem.GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE), 0);\n    m_dof.Clear();\n    m_dof.AddDofs(m_dofW);\n    m_dof.AddDof(m_dofT);\n\n    FESurface* ps = &GetSurface();\n\n    m_nnlist.Create(fem.GetMesh());\n\n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FETemperatureBackFlowStabilization::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having open DOF\n        node.set_bc(m_dofT, DOF_OPEN);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the temperature\nvoid FETemperatureBackFlowStabilization::Update()\n{\n    // determine backflow conditions\n    MarkBackFlow();\n    \n    // prescribe solute backflow constraint at the nodes\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // set node as having prescribed DOF (concentration at previous time)\n        if (node.m_ID[m_dofT] < -1)\n            node.set(m_dofT, node.get_prev(m_dofT));\n        else\n        {\n            node.set_bc(m_dofT, DOF_PRESCRIBED);\n            node.m_ID[m_dofT] = -node.m_ID[m_dofT] - 2;\n            int nid = node.GetID()-1; //0 based\n            int val = m_nnlist.Valence(nid);\n            int* nlist = m_nnlist.NodeList(nid);\n            \n            vector<int> connectnidarray;\n            int connectnid=0;\n            //check which connecting nodes are not on surface\n            for (int j = 0; j<val; ++j)\n            {\n                int cnid = *(nlist+j);\n                bool isSurf = false;\n                for (int k=0; k<ps->Nodes(); ++k)\n                {\n                    if (cnid==ps->Node(k).GetID()-1)\n                        isSurf = true;\n                }\n                if (!isSurf)\n                {\n                    connectnidarray.push_back(cnid);\n                }\n            }\n            //find closest connecting node\n            if (connectnidarray.size()>0)\n            {\n                int cnodeIndex = 0;\n                FENode* cnodeArray = GetFEModel()->GetMesh().FindNodeFromID(connectnidarray[cnodeIndex]+1);\n                double smallDist = sqrt(pow((node.m_rt.x-cnodeArray->m_rt.x),2)+pow((node.m_rt.y-cnodeArray->m_rt.y),2)+pow((node.m_rt.z-cnodeArray->m_rt.z),2));\n                for (int j = 1; j<connectnidarray.size(); ++j)\n                {\n                    FENode* tempNode = GetFEModel()->GetMesh().FindNodeFromID(connectnidarray[j]+1);\n                    double temp = sqrt(pow((node.m_rt.x-tempNode->m_rt.x),2)+pow((node.m_rt.y-tempNode->m_rt.y),2)+pow((node.m_rt.z-tempNode->m_rt.z),2));\n                    if(temp<smallDist)\n                    {\n                        cnodeIndex = j;\n                        cnodeArray = tempNode;\n                        smallDist = temp;\n                    }\n                }\n                connectnid = connectnidarray[cnodeIndex];\n                FENode* cnode = GetFEModel()->GetMesh().FindNodeFromID(connectnid+1);\n                node.set(m_dofT, cnode->get(m_dofT));\n            }\n            else\n                node.set(m_dofT, node.get_prev(m_dofT));\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface\nvoid FETemperatureBackFlowStabilization::MarkBackFlow()\n{\n    // Mark all nodes on this surface to have open concentration DOF\n    FESurface* ps = &GetSurface();\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having free DOF\n        if (node.m_ID[m_dofT] < -1) {\n            node.set_bc(m_dofT, DOF_OPEN);\n            node.m_ID[m_dofT] = -node.m_ID[m_dofT] - 2;\n        }\n    }\n\n    // Calculate normal flow velocity on each face to determine\n    // backflow condition\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    const FETimeInfo& tp = GetTimeInfo();\n\n    for (int iel=0; iel<m_psurf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_psurf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt*tp.alpha + node.m_rp*(1-tp.alpha);\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*tp.alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-tp.alpha);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        double vn = 0;\n\n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            normal.unit();\n            vn += (normal*v)*w[n];\n        }\n        \n        if (vn < 0) {\n            for (int i=0; i<neln; ++i) {\n                FENode& node = m_psurf->GetMesh()->Node(el.m_node[i]);\n                if (node.m_ID[m_dofT] > -1) {\n                    node.set_bc(m_dofT, DOF_PRESCRIBED);\n                    node.m_ID[m_dofT] = -node.m_ID[m_dofT] - 2;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculate residual\nvoid FETemperatureBackFlowStabilization::LoadVector(FEGlobalVector& R)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FETemperatureBackFlowStabilization::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow()) return;\n\tar & m_dofW;\n\tar & m_dofT;\n    m_nnlist.Create(GetFEModel()->GetMesh());\n}\n"
  },
  {
    "path": "FEBioFluid/FETemperatureBackFlowStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiofluid_api.h\"\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FENodeNodeList.h>\n\n//-----------------------------------------------------------------------------\n//! FETemperatureBackflowStabilization is a fluid surface where temperature\n//! is maintained constant when the fluid velocity normal to the surface is\n//! inward.\n//!\nclass FEBIOFLUID_API FETemperatureBackFlowStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FETemperatureBackFlowStabilization(FEModel* pfem);\n    \n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! evaluate flow rate\n    void MarkBackFlow();\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \nprivate:\n    FEDofList   m_dofW;\n    int         m_dofT;\n    FENodeNodeList m_nnlist;\n\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEThermoFluid.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEThermoFluid, FEFluidMaterial)\n\n    // material properties\n    ADD_PROPERTY(m_pElastic, \"elastic\");\n    ADD_PROPERTY(m_pConduct, \"conduct\");\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEThermoFluid\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEThermoFluid constructor\n\nFEThermoFluid::FEThermoFluid(FEModel* pfem) : FEFluidMaterial(pfem)\n{\n    m_pElastic = 0;\n    m_pConduct = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluid::Serialize(DumpStream& ar)\n{\n    FEFluidMaterial::Serialize(ar);\n    if (ar.IsShallow()) return;\n}\n\n//-----------------------------------------------------------------------------\n//! returns a pointer to a new material point object\nFEMaterialPointData* FEThermoFluid::CreateMaterialPointData()\n{\n    FEFluidMaterialPoint* fp = new FEFluidMaterialPoint();\n    return new FEThermoFluidMaterialPoint(fp);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate temperature\ndouble FEThermoFluid::Temperature(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tp = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    return tp.m_T;\n}\n\n//-----------------------------------------------------------------------------\n//! bulk modulus\ndouble FEThermoFluid::BulkModulus(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint& vt = *mp.ExtractData<FEFluidMaterialPoint>();\n    return -(vt.m_ef+1)*Tangent_Pressure_Strain(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! heat flux\nvec3d FEThermoFluid::HeatFlux(FEMaterialPoint& mp)\n{\n    FEThermoFluidMaterialPoint& tp = *mp.ExtractData<FEThermoFluidMaterialPoint>();\n    double k = m_pConduct->ThermalConductivity(mp);\n    vec3d q = -tp.m_gradT*k;\n    return q;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a fluid material is the sum of the fluid pressure\n//! and the viscous stress.\n\nmat3ds FEThermoFluid::Stress(FEMaterialPoint& mp)\n{\n    // calculate solid material stress\n    mat3ds s = GetViscous()->Stress(mp);\n    \n    double p = m_pElastic->Pressure(mp);\n    \n    // add fluid pressure\n    s.xx() -= p;\n    s.yy() -= p;\n    s.zz() -= p;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent of stress with respect to strain J of a fluid material is the\n//! sum of the tangent of the fluid pressure and that of the viscous stress.\n\nmat3ds FEThermoFluid::Tangent_Strain(FEMaterialPoint& mp)\n{\n    // get tangent of viscous stress\n    mat3ds sJ = GetViscous()->Tangent_Strain(mp);\n    \n    // add tangent of fluid pressure\n    double dp = m_pElastic->Tangent_Strain(mp);\n    sJ.xx() -= dp;\n    sJ.yy() -= dp;\n    sJ.zz() -= dp;\n    \n    return sJ;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density (per reference volume)\ndouble FEThermoFluid::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = m_rhor*m_pElastic->SpecificStrainEnergy(mp);\n    return sed;\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEBodyForce.h>\n#include \"FEFluidMaterial.h\"\n#include \"FEElasticFluid.h\"\n#include \"FEFluidThermalConductivity.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for fluid materials.\n\nclass FEBIOFLUID_API FEThermoFluid : public FEFluidMaterial\n{\npublic:\n    FEThermoFluid(FEModel* pfem);\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3ds Tangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to temperature T\n    mat3ds Tangent_Temperature(FEMaterialPoint& mp);\n    \n    //! Elastic pressure\n    double Pressure(FEMaterialPoint& mp) override { return m_pElastic->Pressure(mp); }\n\n    //! tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain(FEMaterialPoint& mp) override { return m_pElastic->Tangent_Strain(mp); }\n    \n    //! 2nd tangent of elastic pressure with respect to strain J\n    double Tangent_Pressure_Strain_Strain(FEMaterialPoint& mp) override { return m_pElastic->Tangent_Strain_Strain(mp); }\n    \n    //! tangent of elastic pressure with respect to temperature T\n    double Tangent_Pressure_Temperature(FEMaterialPoint& mp) { return m_pElastic->Tangent_Temperature(mp); }\n    \n    //! 2nd tangent of elastic pressure with respect to temperature T\n    double Tangent_Pressure_Temperature_Temperature(FEMaterialPoint& mp) { return m_pElastic->Tangent_Temperature_Temperature(mp); }\n    \n    //! tangent of elastic pressure with respect to strain J and temperature T\n    double Tangent_Pressure_Strain_Temperature(FEMaterialPoint& mp) { return m_pElastic->Tangent_Strain_Temperature(mp); }\n    \npublic:\n    //! bulk modulus\n    double BulkModulus(FEMaterialPoint& mp) override;\n\n    //! heat flux\n    vec3d HeatFlux(FEMaterialPoint& mp);\n    \n    //! strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    //! invert pressure-dilatation relation\n    bool Dilatation(const double T, const double p, double& e) override { return GetElastic()->Dilatation(T,p,e); }\n    \n    //! fluid pressure from state variables\n    double Pressure(const double ef, const double T) override { return GetElastic()->Pressure(ef, T); };\n\n    //! evaluate temperature\n    double Temperature(FEMaterialPoint& mp) override;\n\npublic:\n    //! return elastic part\n    FEElasticFluid* GetElastic() { return m_pElastic; }\n    \n    //! return thermal conductivity part\n    FEFluidThermalConductivity* GetConduct() { return m_pConduct; }\n    \nprivate: // material properties\n    FEElasticFluid*             m_pElastic;     //!< pointer to elastic part of fluid material\n    FEFluidThermalConductivity* m_pConduct;     //!< pointer to fluid thermal conductivity material\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEThermoFluidAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEThermoFluidAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFEThermoFluidAnalysis::FEThermoFluidAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEThermoFluidAnalysis : public FEAnalysis\n{\npublic:\n\tenum ThermoFluidAnalysisType {\n\t\tSTEADY_STATE,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFEThermoFluidAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidDomain3D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioThermoFluid.h\"\n#include \"FEThermoFluidDomain3D.h\"\n#include \"FEThermoFluidSolver.h\"\n#include \"FEFluidHeatSupply.h\"\n#include \"FEThermoViscousFluid.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEThermoFluidDomain3D::FEThermoFluidDomain3D(FEModel* pfem) : FESolidDomain(pfem), FEFluidDomain(pfem), m_dofW(pfem), m_dofAW(pfem), m_dof(pfem)\n{\n    m_pMat = 0;\n    m_btrans = true;\n    \n    if (pfem)\n    {\n        // set the active degrees of freedom list\n        m_dofW.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_ACCELERATION));\n\n        m_dofEF = pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION), 0);\n        m_dofAEF = pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION_TDERIV), 0);\n        m_dofT = pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE), 0);\n        m_dofAT = pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE_TDERIV), 0);\n\n        FEDofList dofs(pfem);\n        dofs.AddDofs(m_dofW);\n        dofs.AddDof(m_dofEF);\n        dofs.AddDof(m_dofT);\n        m_dof = dofs;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEThermoFluidDomain3D& FEThermoFluidDomain3D::operator = (FEThermoFluidDomain3D& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// get total dof list\nconst FEDofList& FEThermoFluidDomain3D::GetDOFList() const\n{\n    return m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEThermoFluidDomain3D::SetMaterial(FEMaterial* pmat)\n{\n    FEDomain::SetMaterial(pmat);\n    if (pmat)\n    {\n        m_pMat = dynamic_cast<FEThermoFluid*>(pmat);\n        assert(m_pMat);\n    }\n    else m_pMat = 0;\n    \n}\n\n//-----------------------------------------------------------------------------\nbool FEThermoFluidDomain3D::Init()\n{\n    // initialize base class\n    if (FESolidDomain::Init() == false) return false;\n    \n    FEModel* pfem = GetFEModel();\n    \n    m_Tr = GetFEModel()->GetGlobalConstant(\"T\");\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_pMat;\n    ar & m_Tr;\n    ar & m_dof;\n    ar & m_dofW & m_dofAW;\n    ar & m_dofEF & m_dofAEF;\n    ar & m_dofT & m_dofAT;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEThermoFluidDomain3D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], r0, v;\n    FEMesh& m = *GetMesh();\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n            pt.m_r0 = el.Evaluate(x0, j);\n            mp.m_rt = mp.m_r0;\n\n            if (pt.m_ef <= -1) {\n                throw NegativeJacobianDetected();\n            }\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n    int ndpn = 5;\n    \n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEThermoFluidDomain3D::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJ;\n    \n    mat3ds sv;\n    vec3d gradp, q;\n    double dpT, dpJ, rho, cv;\n    \n    const double *H, *Gr, *Gs, *Gt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    int ndpn = 5;\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double*    gw = el.GaussWeights();\n\n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEThermoFluidMaterialPoint& tf = *(mp.ExtractData<FEThermoFluidMaterialPoint>());\n\n        // calculate the jacobian\n        detJ = invjac0(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        // get the viscous stress tensor for this integration point\n        sv = m_pMat->GetViscous()->Stress(mp);\n        // get the derivative of the elastic pressure with respect to temperature\n        dpT = m_pMat->GetElastic()->Tangent_Temperature(mp);\n        // get the derivative of the elastic pressure with respect to strain\n        dpJ = m_pMat->GetElastic()->Tangent_Strain(mp);\n        // get the gradient of the elastic pressure\n        gradp = tf.m_gradT*dpT + pt.m_gradef*dpJ;\n        // get the heat flux\n        q = m_pMat->HeatFlux(mp);\n        // get fluid mass density\n        rho = m_pMat->Density(mp);\n        // get the isochoric specific heat capacity\n        cv = m_pMat->GetElastic()->IsochoricSpecificHeatCapacity(mp);\n        // get absolute temperature\n        double T = m_Tr + tf.m_T;\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        }\n        \n        // Jdot/J\n        double dJoJ = pt.m_efdot/(pt.m_ef+1);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d fs = sv*gradN[i] + gradp*H[i];\n            double fJ = dJoJ*H[i] + gradN[i]*pt.m_vft;\n            double fT = q*gradN[i] + H[i]*(((sv - mat3dd(T*dpT))*pt.m_Lf).trace() - rho*cv*tf.m_Tdot);\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fs.x*detJ;\n            fe[ndpn*i+1] -= fs.y*detJ;\n            fe[ndpn*i+2] -= fs.z*detJ;\n            fe[ndpn*i+3] -= fJ*detJ;\n            fe[ndpn*i+4] -= fT*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n    int ndpn = 5;\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEThermoFluidDomain3D::ElementBodyForce(FEBodyForce& BF, FESolidElement& el, vector<double>& fe)\n{\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    int ndpn = 5;\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double dens = m_pMat->Density(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = detJ0(el, n)*gw[n];\n        \n        // get the force\n        f = BF.force(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[ndpn*i  ] -= H[i]*dens*f.x*detJ;\n            fe[ndpn*i+1] -= H[i]*dens*f.y*detJ;\n            fe[ndpn*i+2] -= H[i]*dens*f.z*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::HeatSupply(FEGlobalVector& R, FEFluidHeatSupply& BF)\n{\n    int NE = (int)m_Elem.size();\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 5*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementHeatSupply(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEThermoFluidDomain3D::ElementHeatSupply(FEFluidHeatSupply& BF, FESolidElement& el, vector<double>& fe)\n{\n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    double r;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    int ndpn = 5;\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double dens = m_pMat->Density(mp);\n        \n        pt.m_r0 = el.Evaluate(r0, n);\n        \n        detJ = detJ0(el, n)*gw[n];\n        \n        // get the force\n        r = BF.heat(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fe[ndpn*i+4] += H[i]*dens*r*detJ;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEThermoFluidDomain3D::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    int ndof = ke.columns()/neln;\n    \n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    vec3d f, k;\n    \n    // gradient of shape functions\n    vec3d gradN;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n\n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n]*tp.alphaf;\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Density(mp);\n        \n        // get the force\n        f = BF.force(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                k = f*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                ke[ndof*i  ][ndof*j+3] += k.x;\n                ke[ndof*i+1][ndof*j+3] += k.y;\n                ke[ndof*i+2][ndof*j+3] += k.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEThermoFluidDomain3D::ElementHeatSupplyStiffness(FEFluidHeatSupply& BF, FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int neln = el.Nodes();\n    int ndof = ke.columns()/neln;\n    \n    // jacobian\n    double detJ;\n    double *H;\n    double* gw = el.GaussWeights();\n    double r, drT;\n    \n    // gradient of shape functions\n    vec3d gradN;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *mp.ExtractData<FEFluidMaterialPoint>();\n        double Jf = 1 + pt.m_ef;\n\n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n]*tp.alphaf;\n        \n        H = el.H(n);\n        \n        double dens = m_pMat->Density(mp);\n        \n        // get the heat supply and its temperature derivative\n        r = BF.heat(mp);\n        drT = BF.stiffness(mp);\n        \n        H = el.H(n);\n        \n        for (int i=0; i<neln; ++i) {\n            for (int j=0; j<neln; ++j)\n            {\n                ke[ndof*i+4][ndof*j+3] -= H[i]*H[j]*dens*r/Jf*detJ;\n                ke[ndof*i+4][ndof*j+4] += H[i]*H[j]*dens*drT/Jf*detJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEThermoFluidDomain3D::ElementStiffness(FESolidElement &el, matrix &ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i5, j, j5, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n\n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf)*m_btrans;\n\n    double *H, *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEThermoFluidMaterialPoint& tf = *(mp.ExtractData<FEThermoFluidMaterialPoint>());\n        double Jf = 1 + pt.m_ef;\n\n\t\tFEThermoViscousFluid* pVisc = dynamic_cast<FEThermoViscousFluid*>(m_pMat->GetViscous());\n\n        // get the tangents\n        mat3ds sv   = pVisc->Stress(mp);\n        mat3ds svJ  = pVisc->Tangent_Strain(mp);\n        mat3ds svT  = pVisc->Tangent_Temperature(mp);\n        tens4ds Cv  = pVisc->Tangent_RateOfDeformation(mp);\n        double dpJ  = m_pMat->GetElastic()->Tangent_Strain(mp);\n        double dpJJ = m_pMat->GetElastic()->Tangent_Strain_Strain(mp);\n        double dpT  = m_pMat->GetElastic()->Tangent_Temperature(mp);\n        double dpTT = m_pMat->GetElastic()->Tangent_Temperature_Temperature(mp);\n        double dpJT = m_pMat->GetElastic()->Tangent_Strain_Temperature(mp);\n        // Jdot/J\n        double dJoJ = pt.m_efdot/Jf;\n        double rho  = m_pMat->Density(mp);\n        double cv   = m_pMat->GetElastic()->IsochoricSpecificHeatCapacity(mp);\n        double dcvT = m_pMat->GetElastic()->Tangent_cv_Temperature(mp);\n        double dcvJ = m_pMat->GetElastic()->Tangent_cv_Strain(mp);\n        double k    = m_pMat->GetConduct()->ThermalConductivity(mp);\n        double dkJ  = m_pMat->GetConduct()->Tangent_Strain(mp);\n        double dkT  = m_pMat->GetConduct()->Tangent_Temperature(mp);\n        double T = m_Tr + tf.m_T;\n\n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i5=0; i<neln; ++i, i5 += 5)\n        {\n            for (j=0, j5 = 0; j<neln; ++j, j5 += 5)\n            {\n                mat3d Kvv = vdotTdotv(gradN[i], Cv, gradN[j]);\n                vec3d kvJ = (svJ*gradN[i])*H[j] + (gradN[j]*dpJ+(tf.m_gradT*dpJT+pt.m_gradef*dpJJ)*H[j])*H[i];\n                vec3d kvT = (svT*gradN[i])*H[j] + (gradN[j]*dpT+(tf.m_gradT*dpTT+pt.m_gradef*dpJT)*H[j])*H[i];\n                vec3d kJv = (pt.m_gradef*(H[i]/Jf) + gradN[i])*H[j];\n                double kJJ = (H[j]*(ksi/dt - dJoJ) + gradN[j]*pt.m_vft)*H[i]/Jf;\n                double kJT = 0;\n                vec3d kTv = ((Cv.dot(pt.m_Lf) + sv - mat3dd(T*dpT))*gradN[j] - tf.m_gradT*(rho*cv*H[j]))*H[i];\n                double kTJ = -dkJ*H[j]*(gradN[i]*tf.m_gradT)\n                + H[i]*H[j]*((svJ - mat3dd(T*dpJT))*pt.m_Lf).trace()\n                + H[i]*H[j]*rho*(cv/Jf - dcvJ)*tf.m_Tdot;\n                double kTT = -(tf.m_gradT*(dkT*H[j]) + gradN[j]*k)*gradN[i]\n                + H[i]*H[j]*((svT - mat3dd(dpT+T*dpTT))*pt.m_Lf).trace()\n                - H[i]*H[j]*rho*(dcvT*tf.m_Tdot+ksi*cv/dt)\n                - H[i]*rho*cv*(gradN[j]*pt.m_vft);\n                \n                ke[i5  ][j5  ] += Kvv(0,0)*detJ;\n                ke[i5  ][j5+1] += Kvv(0,1)*detJ;\n                ke[i5  ][j5+2] += Kvv(0,2)*detJ;\n                ke[i5  ][j5+3] += kvJ.x*detJ;\n                ke[i5  ][j5+4] += kvT.x*detJ;\n\n                ke[i5+1][j5  ] += Kvv(1,0)*detJ;\n                ke[i5+1][j5+1] += Kvv(1,1)*detJ;\n                ke[i5+1][j5+2] += Kvv(1,2)*detJ;\n                ke[i5+1][j5+3] += kvJ.y*detJ;\n                ke[i5+1][j5+4] += kvT.y*detJ;\n\n                ke[i5+2][j5  ] += Kvv(2,0)*detJ;\n                ke[i5+2][j5+1] += Kvv(2,1)*detJ;\n                ke[i5+2][j5+2] += Kvv(2,2)*detJ;\n                ke[i5+2][j5+3] += kvJ.z*detJ;\n                ke[i5+2][j5+4] += kvT.z*detJ;\n\n                ke[i5+3][j5  ] += kJv.x*detJ;\n                ke[i5+3][j5+1] += kJv.y*detJ;\n                ke[i5+3][j5+2] += kJv.z*detJ;\n                ke[i5+3][j5+3] += kJJ*detJ;\n                ke[i5+3][j5+4] += kJT*detJ;\n\n                ke[i5+4][j5  ] += kTv.x*detJ;\n                ke[i5+4][j5+1] += kTv.y*detJ;\n                ke[i5+4][j5+2] += kTv.z*detJ;\n                ke[i5+4][j5+3] += kTJ*detJ;\n                ke[i5+4][j5+4] += kTT*detJ;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 5*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate material stiffness\n        ElementStiffness(el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::MassMatrix(FELinearSystem& LS)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 5*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementMassMatrix(el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 5*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::HeatSupplyStiffness(FELinearSystem& LS, FEFluidHeatSupply& bf)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 5*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementHeatSupplyStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n        vector<int> lm;\n        UnpackLM(el, lm);\n        ke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n        LS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEThermoFluidDomain3D::ElementMassMatrix(FESolidElement& el, matrix& ke)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    int i, i5, j, j5, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    double *H;\n    double *Gr, *Gs, *Gt;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n\n    double dt = tp.timeIncrement;\n    double ksi = tp.alpham/(tp.gamma*tp.alphaf)*m_btrans;\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        detJ = invjac0(el, Ji, n)*gw[n]*tp.alphaf;\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        H = el.H(n);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        // setup the material point\n        // NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        \n        double dens = m_pMat->Density(mp);\n        \n        // evaluate spatial gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // evaluate stiffness matrix\n        for (i=0, i5=0; i<neln; ++i, i5 += 5)\n        {\n            for (j=0, j5 = 0; j<neln; ++j, j5 += 5)\n            {\n                mat3d Mv = ((mat3dd(ksi/dt) + pt.m_Lf)*H[j] + mat3dd(gradN[j]*pt.m_vft))*(H[i]*dens*detJ);\n                vec3d mJ = pt.m_aft*(-H[i]*H[j]*dens/(pt.m_ef+1)*detJ);\n                \n                ke[i5  ][j5  ] += Mv(0,0);\n                ke[i5  ][j5+1] += Mv(0,1);\n                ke[i5  ][j5+2] += Mv(0,2);\n                ke[i5  ][j5+3] += mJ.x;\n                \n                ke[i5+1][j5  ] += Mv(1,0);\n                ke[i5+1][j5+1] += Mv(1,1);\n                ke[i5+1][j5+2] += Mv(1,2);\n                ke[i5+1][j5+3] += mJ.y;\n                \n                ke[i5+2][j5  ] += Mv(2,0);\n                ke[i5+2][j5+1] += Mv(2,1);\n                ke[i5+2][j5+2] += Mv(2,2);\n                ke[i5+2][j5+3] += mJ.z;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i, tp);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (NegativeJacobian::DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\nvoid FEThermoFluidDomain3D::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double alphaf = tp.alphaf;\n    double alpham = tp.alpham;\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d v[NELN];\n    vec3d a[NELN];\n    double e[NELN];\n    double ae[NELN];\n    double T[NELN];\n    double aT[NELN];\n    for (int j=0; j<neln; ++j) {\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n        v[j] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*alphaf + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-alphaf);\n        a[j] = node.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2])*alpham + node.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2])*(1-alpham);\n        e[j] = node.get(m_dofEF)*alphaf + node.get_prev(m_dofEF)*(1-alphaf);\n        ae[j] = node.get(m_dofAEF)*alpham + node.get_prev(m_dofAEF)*(1-alpham);\n        T[j] = node.get(m_dofT)*alphaf + node.get_prev(m_dofT)*(1-alphaf);\n        aT[j] = node.get(m_dofAT)*alpham + node.get_prev(m_dofAT)*(1-alpham);\n    }\n    \n    // loop over the integration points and update\n    // velocity, velocity gradient, acceleration\n    // stress and pressure at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        FEThermoFluidMaterialPoint& tf = *(mp.ExtractData<FEThermoFluidMaterialPoint>());\n        \n        // material point data\n        pt.m_vft = el.Evaluate(v, n);\n        pt.m_Lf = gradient(el, v, n);\n        pt.m_aft = pt.m_Lf*pt.m_vft;\n        if (m_btrans) pt.m_aft += el.Evaluate(a, n);\n        pt.m_ef = el.Evaluate(e, n);\n        pt.m_gradef = gradient(el, e, n);\n        pt.m_efdot = pt.m_gradef*pt.m_vft;\n        if (m_btrans) pt.m_efdot += el.Evaluate(ae, n);\n        tf.m_T = el.Evaluate(T, n);\n        tf.m_gradT = gradient(el, T, n);\n        tf.m_Tdot = tf.m_gradT*pt.m_vft;\n        if (m_btrans) tf.m_Tdot += el.Evaluate(aT, n);\n\n        // calculate the stress at this material point\n        pt.m_sf = m_pMat->Stress(mp);\n        \n        // calculate the fluid pressure\n        pt.m_pf = m_pMat->Pressure(mp);\n        \n        // calculate the heat flux\n        tf.m_q = m_pMat->HeatFlux(mp);\n        \n        // calculate remaining entries of thermofluid material point\n        // TODO: Not sure that we need any of these (consider taking them out)\n        tf.m_k = m_pMat->BulkModulus(mp);\n        tf.m_K = m_pMat->GetConduct()->ThermalConductivity(mp);\n        tf.m_dKJ = m_pMat->GetConduct()->Tangent_Strain(mp);\n        tf.m_dKT = m_pMat->GetConduct()->Tangent_Temperature(mp);\n        tf.m_cv = m_pMat->GetElastic()->IsochoricSpecificHeatCapacity(mp);\n        tf.m_dcvJ = m_pMat->GetElastic()->Tangent_cv_Strain(mp);\n        tf.m_dcvT = m_pMat->GetElastic()->Tangent_cv_Temperature(mp);\n        tf.m_cp = m_pMat->GetElastic()->IsobaricSpecificHeatCapacity(mp);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::InertialForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 5*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInertialForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidDomain3D::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian determinant\n    double detJ;\n    \n    const double* H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*    gw = el.GaussWeights();\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEFluidMaterialPoint& pt = *(mp.ExtractData<FEFluidMaterialPoint>());\n        double dens = m_pMat->Density(mp);\n        \n        // calculate the jacobian\n        detJ = detJ0(el, n)*gw[n];\n        \n        H = el.H(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d f = pt.m_aft*(dens*H[i]);\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[5*i  ] -= f.x*detJ;\n            fe[5*i+1] -= f.y*detJ;\n            fe[5*i+2] -= f.z*detJ;\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidDomain3D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEFluidDomain.h\"\n#include \"FEThermoFluid.h\"\n#include \"FEFluidHeatSupply.h\"\n\n//-----------------------------------------------------------------------------\n//! domain described by 3D volumetric elements\n//!\nclass FEBIOFLUID_API FEThermoFluidDomain3D : public virtual FESolidDomain, public FEFluidDomain\n{\npublic:\n    //! constructor\n    FEThermoFluidDomain3D(FEModel* pfem);\n    ~FEThermoFluidDomain3D() {}\n    \n    //! assignment operator\n    FEThermoFluidDomain3D& operator = (FEThermoFluidDomain3D& d);\n    \n    //! initialize\n    bool Init() override;\n    \n    //! serialize\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \npublic: // overrides from FEDomain\n    \n    //! get the material\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pm) override;\n\n    // get total dof list\n    const FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! body forces\n    void BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n    \n    //! Calculate the heat supply\n    void HeatSupply(FEGlobalVector& R, FEFluidHeatSupply& r);\n\n    //! inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! Calculate stiffness contribution of heat supplies\n    void HeatSupplyStiffness(FELinearSystem& LS, FEFluidHeatSupply& bf);\n    \n    //! calculates inertial stiffness\n    void MassMatrix(FELinearSystem& LS) override;\n    \n    //! body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \npublic:\n    // --- S T I F F N E S S ---\n    \n    //! calculates the solid element stiffness matrix\n    void ElementStiffness(FESolidElement& el, matrix& ke);\n    \n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FESolidElement& el, matrix& ke);\n    \n    //! calculates the stiffness matrix due to heat supplies\n    void ElementHeatSupplyStiffness(FEFluidHeatSupply& bf, FESolidElement& el, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for solid elements\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! Calculates external body forces for solid elements\n    void ElementBodyForce(FEBodyForce& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates external supplies for solid elements\n    void ElementHeatSupply(FEFluidHeatSupply& BF, FESolidElement& elem, vector<double>& fe);\n    \n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    FEThermoFluid*  m_pMat;\n    double          m_Tr;       // referential absolute temperature\n\nprotected:\n    FEDofList   m_dofW;\n    FEDofList   m_dofAW;\n    FEDofList   m_dof;\n    int         m_dofEF;\n    int         m_dofAEF;\n    int         m_dofT;\n    int         m_dofAT;\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEThermoFluidDomainFactory.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEThermoFluidDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n    FEModel* pfem = pmat->GetFEModel();\n    FE_Element_Class eclass = spec.eclass;\n    FE_Element_Shape eshape = spec.eshape;\n    const char* sztype = 0;\n    if (dynamic_cast<FEThermoFluid*>(pmat))\n    {\n        // fluid elements\n        if (eclass == FE_ELEM_SOLID) sztype = \"thermo-fluid-3D\";\n        else return 0;\n    }\n\n    if (sztype)\n    {\n        FEDomain* pd = fecore_new<FESolidDomain>(sztype, pfem);\n        if (pd) pd->SetMaterial(pmat);\n        return pd;\n    }\n    else return 0;\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FEThermoFluidDomainFactory : public FEDomainFactory\n{\npublic:\n    virtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"FEThermoFluidMaterialPoint.h\"\n\n//============================================================================\nFEThermoFluidMaterialPoint::FEThermoFluidMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEThermoFluidMaterialPoint::Copy()\n{\n    FEThermoFluidMaterialPoint* pt = new FEThermoFluidMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_T & m_Tdot & m_gradT & m_k & m_K & m_dKJ & m_dKT & m_cv & m_dcvJ & m_dcvT & m_cp & m_q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidMaterialPoint::Init()\n{\n    m_T = m_Tdot = 0;\n    m_gradT = vec3d(0);\n    m_k = 0;\n    m_K = m_dKJ = m_dKT = 0;\n    m_cv = m_dcvJ = m_dcvT = 0;\n    m_cp = 0;\n    m_q = vec3d(0);\n    \n    // don't forget to initialize the base class\n\tFEMaterialPointData::Init();\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Fluid material point class.\n//\nclass FEBIOFLUID_API FEThermoFluidMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! constructor\n\tFEThermoFluidMaterialPoint(FEMaterialPointData* pt);\n\n    //! create a shallow copy\n\tFEMaterialPointData* Copy();\n\n    //! data serialization\n    void Serialize(DumpStream& ar);\n\n    //! Data initialization\n    void Init();\n\npublic:\n    // fluid data\n    double      m_T;        //!< temperature (relative to reference temperature)\n    double      m_Tdot;     //!< material time derivative of temperature\n    vec3d       m_gradT;    //!< temperature gradient\n    double      m_k;        //!< bulk modulus\n    double      m_K;        //!< thermal conductivity\n    double      m_dKJ;      //!< derivative of thermal conductivity with respect to fluid volume ratio\n    double      m_dKT;      //!< derivative of thermal conductivity with respect to temperature\n    double      m_cv;       //!< isochoric specific heat capacity\n    double      m_dcvJ;     //!< derivative of isochoric specific heat capacity w.r.t. fluid volume ratio\n    double      m_dcvT;     //!< derivative of isochoric specific heat capacity w.r.t. temperature\n    double      m_cp;       //!< isobaric specific heat capacity\n    vec3d       m_q;        //!< heat flux\n};\n\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidPressureBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEThermoFluidPressureBC.h\"\n#include \"FEBioThermoFluid.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEModel.h>\n#include <FECore/FESurface.h>\n#include \"FEThermoFluid.h\"\n#include \"FEFluid.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEThermoFluidPressureBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_p, \"pressure\")->setUnits(\"P\")->setLongName(\"fluid pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEThermoFluidPressureBC::FEThermoFluidPressureBC(FEModel* pfem) : FEPrescribedSurface(pfem)\n{\n    m_p = 0;\n    m_dofEF = -1;\n    m_dofT = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n    m_psurf = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEThermoFluidPressureBC::Init()\n{\n    m_dofEF = GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION), 0);\n    m_dofT = GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE), 0);\n    SetDOFList(m_dofEF);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n    \n    m_psurf = FESurfaceBC::GetSurface();\n\n    m_e.assign(GetSurface()->Nodes(), 0.0);\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEThermoFluidPressureBC::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n    FEModel* fem = GetFEModel();\n    FEMesh& mesh = fem->GetMesh();\n    FETimeInfo& tp = fem->GetTime();\n\n    int N = ps->Nodes();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n\n    //Project sum of all ca and osc values from int points to nodes on surface\n    //All values put into map, including duplicates\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        // evaluate average prescribed pressure on this face\n        double p = 0;\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMaterialPoint* pt = el.GetMaterialPoint(j);\n            p += m_p(*pt);\n        }\n        p /= el.GaussPoints();\n        // get surface underlying material\n        FEElement* e = el.m_elem[0].pe;\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        if (se) {\n            FEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n            FEThermoFluid* ptfl = pm->ExtractProperty<FEThermoFluid>();\n            FEFluid* pfl = pm->ExtractProperty<FEFluid>();\n            if (ptfl) {\n                // evaluate average temperature on this face\n                double T = 0;\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = mesh.Node(el.m_node[j]);\n                    T += node.get(m_dofT)*tp.alphaf + node.get_prev(m_dofT)*(1-tp.alphaf);\n                }\n                T /= el.Nodes();\n                double efi[FEElement::MAX_INTPOINTS] = {0};\n                double efo[FEElement::MAX_NODES] = {0};\n/*                bool good = true;\n                for (int j=0; j<se->GaussPoints(); ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    good = good && ptfl->Dilatation(T, p, 0, efi[j]);\n                    if (!good) break;\n                }\n                // only keep the dilatations at the nodes of the surface face\n                if (good) {\n                    // project dilatations from integration points to nodes\n                    se->project_to_nodes(efi, efo);\n                    for (int j=0; j<el.Nodes(); ++j)\n                        efNodes[el.m_lnode[j]].push_back(efo[j]);\n                }\n                else {*/\n                    for (int j=0; j<el.Nodes(); ++j) {\n                        FENode& node = mesh.Node(el.m_node[j]);\n                        efo[j] = node.get_prev(m_dofEF);\n                        ptfl->Dilatation(T, p, efo[j]);\n                        efNodes[el.m_lnode[j]].push_back(efo[j]);\n                    }\n//                }\n            }\n            else if (pfl) {\n                double efi[FEElement::MAX_INTPOINTS] = {0};\n                double efo[FEElement::MAX_NODES] = {0};\n                bool good = true;\n                for (int j=0; j<se->GaussPoints(); ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    good = good && pfl->Dilatation(0, p, efi[j]);\n                    if (!good) break;\n                }\n                // only keep the dilatations at the nodes of the surface face\n                if (good) {\n                    // project dilatations from integration points to nodes\n                    se->project_to_nodes(efi, efo);\n                    for (int j=0; j<el.Nodes(); ++j)\n                        efNodes[el.m_lnode[j]].push_back(efo[j]);\n                }\n                else {\n                    for (int j=0; j<el.Nodes(); ++j) {\n                        FENode& node = mesh.Node(el.m_node[j]);\n                        efo[j] = node.get_prev(m_dofEF);\n                        pfl->Dilatation(0, p, efo[j]);\n                        efNodes[el.m_lnode[j]].push_back(efo[j]);\n                    }\n                }\n            }\n            else break;\n        }\n        //If no solid element, insert all 0s\n        else {\n            for (int j=0; j<el.Nodes(); ++j)\n                efNodes[el.m_lnode[j]].push_back(0);\n        }\n    }\n    \n    //For each node, average the nodal ef\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        double ef = 0;\n        for (int j = 0; j < efNodes[i].size(); ++j)\n            ef += efNodes[i][j];\n        ef /= efNodes[i].size();\n            \n        // store value for now\n        m_e[i] = ef;\n    }\n \n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidPressureBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_e[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEThermoFluidPressureBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEThermoFluidPressureBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_e;\n    if (ar.IsShallow()) return;\n    ar & m_dofT & m_dofEF;\n    ar & m_Rgas & m_Tabs;\n    ar & m_psurf;\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidPressureBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/FEModelParam.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! FEThermoFluidPressure is a thermofluid surface that has prescribed fluid pressure\n//!\nclass FEBIOFLUID_API FEThermoFluidPressureBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEThermoFluidPressureBC(FEModel* pfem);\n\n    //! set the dilatation\n    void Update() override;\n\n    //! initialize\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n    \n    FESurface* GetSurface() { return m_psurf; }\n\nprivate:\n    FEParamDouble   m_p;       //!< prescribed fluid pressure\n    vector<double>  m_e;\n\nprivate:\n    double      m_Rgas;\n    double      m_Tabs;\n\n    int        m_dofEF;\n    int        m_dofT;\n    \n    FESurface* m_psurf;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidPressureLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEThermoFluidPressureLoad.h\"\n#include \"FEBioThermoFluid.h\"\n#include \"FEThermoFluidSolver.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEThermoFluidPressureLoad, FESurfaceConstraint)\n    ADD_PARAMETER(m_p, \"pressure\")->setUnits(\"P\")->setLongName(\"fluid pressure\");\n    ADD_PARAMETER(m_laugon, \"laugon\"        )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n    BEGIN_PARAM_GROUP(\"Augmentation\");\n        ADD_PARAMETER(m_tol, \"tol\")->setLongName(\"tolerance\");\n        ADD_PARAMETER(m_eps, \"penalty\");\n        ADD_PARAMETER(m_naugmin, \"minaug\");\n        ADD_PARAMETER(m_naugmax, \"maxaug\");\n    END_PARAM_GROUP();\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEThermoFluidPressureLoad::FEThermoFluidPressureLoad(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem)\n{\n    m_pfluid = nullptr;\n    m_p = 0;\n    m_tol = 0.1;\n    m_laugon = FECore::PENALTY_METHOD;\n    m_eps = 1.0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n    \n    m_dofEF = (pfem ? pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION), 0) : -1);\n    m_dofT  = (pfem ? pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE), 0) : -1);\n}\n\n//-----------------------------------------------------------------------------\nbool FEThermoFluidPressureLoad::Init()\n{\n    if (FESurfaceConstraint::Init() == false) return false;\n    \n    m_surf.Init();\n    \n    // get fluid from first surface element\n    // assuming the entire surface bounds the same fluid\n    FESurfaceElement& el = m_surf.Element(0);\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == nullptr) return false;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    m_pfluid = pm->ExtractProperty<FEThermoFluid>();\n    if (m_pfluid == nullptr) return false;\n    \n    switch (m_laugon) {\n        case 0:\n        case 1:\n            m_Lm.resize(m_surf.Nodes(), 0.0);\n            m_Lmp.resize(m_surf.Nodes(), 0.0);\n            break;\n        case 2:\n        {\n            m_EQ.resize(m_surf.Nodes(), -1);\n            m_Lm.resize(m_surf.Nodes(), 0.0);\n            m_Lmp.resize(m_surf.Nodes(), 0.0);\n        }\n            break;\n        default:\n            break;\n    }\n    \n    // project the prescribed pressure to the nodes\n    m_surf.ProjectToNodes(m_p, m_pn);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// allocate equations\nint FEThermoFluidPressureLoad::InitEquations(int neq)\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return 0;\n    int n = neq;\n    for (int i = 0; i < m_surf.Nodes(); ++i) m_EQ[i] = n++;\n\n    return n - neq;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidPressureLoad::UnpackLM(vector<int>& lm, int n)\n{\n    int ndof;\n    if (m_laugon < FECore::LAGMULT_METHOD) ndof = 2;\n    else ndof = 3;\n    \n    lm.reserve(ndof);\n    FENode& node = m_surf.Node(n);\n    lm.push_back(node.m_ID[m_dofEF]);\n    lm.push_back(node.m_ID[m_dofT]);\n    if (m_laugon == FECore::LAGMULT_METHOD) lm.push_back(m_EQ[n]);\n}\n\n//-----------------------------------------------------------------------------\n// Build the matrix profile\nvoid FEThermoFluidPressureLoad::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n    for (int i=0; i<m_surf.Nodes(); ++i) {\n        vector<int> lm;\n        UnpackLM(lm, i);\n        \n        // add it to the pile\n        M.build_add(lm);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidPressureLoad::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return;\n    for (int i = 0; i < m_surf.Nodes(); ++i)\n    {\n        if (m_EQ[i] != -1) m_Lm[i] = m_Lmp[i] + Ui[m_EQ[i]] + ui[m_EQ[i]];\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidPressureLoad::PrepStep()\n{\n    m_surf.ProjectToNodes(m_p, m_pn);\n    if (m_laugon < FECore::LAGMULT_METHOD) return;\n    for (int i = 0; i < m_surf.Nodes(); ++i)\n    {\n        m_Lmp[i] = m_Lm[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidPressureLoad::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return;\n    for (int i = 0; i < m_surf.Nodes(); ++i)\n    {\n        if (m_EQ[i] != -1) Ui[m_EQ[i]] += ui[m_EQ[i]];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEThermoFluidPressureLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceConstraint::Serialize(ar);\n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofT & m_dofEF;\n    if (m_laugon == FECore::AUGLAG_METHOD) ar & m_Lm;\n    else if (m_laugon > FECore::AUGLAG_METHOD)\n        ar & m_Lm & m_Lmp;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FEThermoFluidPressureLoad::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    double alpha = tp.alphaf;\n    \n    if (m_laugon == FECore::LAGMULT_METHOD) {\n        int ndof = 3;\n        vector<double> fe(ndof, 0.0);\n        \n        for (int i=0; i<m_surf.Nodes(); ++i) {\n            FENode& node = m_surf.Node(i);\n            double e = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1-alpha);\n            double T = node.get(m_dofT)*alpha + node.get_prev(m_dofT)*(1-alpha);\n            double p = m_pfluid->GetElastic()->Pressure(e, T);\n            double dpJ = m_pfluid->GetElastic()->Tangent_Strain(e, T);\n            double dpT = m_pfluid->GetElastic()->Tangent_Temperature(e, T);\n            double lam = m_Lm[i]*alpha + m_Lmp[i]*(1-alpha);\n            double f = p - m_pn[i];\n            fe[0] = -lam*dpJ;\n            fe[1] = -lam*dpT;\n            fe[2] = -f;\n            vector<int> lm;\n            UnpackLM(lm,i);\n            R.Assemble(lm, fe);\n        }\n    }\n    else {\n        int ndof = 2;\n        vector<double> fe(ndof, 0.0);\n        \n        for (int i=0; i<m_surf.Nodes(); ++i) {\n            FENode& node = m_surf.Node(i);\n            double e = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1-alpha);\n            double T = node.get(m_dofT)*alpha + node.get_prev(m_dofT)*(1-alpha);\n            double p = m_pfluid->GetElastic()->Pressure(e, T);\n            double dpJ = m_pfluid->GetElastic()->Tangent_Strain(e, T);\n            double dpT = m_pfluid->GetElastic()->Tangent_Temperature(e, T);\n            double p0 = m_pn[i];\n            double c = m_Lm[i] + m_eps*(p - p0);\n            fe[0] = -c*dpJ;\n            fe[1] = -c*dpT;\n            vector<int> lm;\n            UnpackLM(lm,i);\n            R.Assemble(lm, fe);\n        }\n    }\n\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FEThermoFluidPressureLoad::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    double alpha = tp.alphaf;\n    \n    if (m_laugon == FECore::LAGMULT_METHOD) {\n        int ndof = 3;\n        FEElementMatrix ke;\n        ke.resize(ndof, ndof);\n        for (int i=0; i<m_surf.Nodes(); ++i) {\n            ke.zero();\n            FENode& node = m_surf.Node(i);\n            double e = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1-alpha);\n            double T = node.get(m_dofT)*alpha + node.get_prev(m_dofT)*(1-alpha);\n            double p = m_pfluid->GetElastic()->Pressure(e, T);\n            double dpJ = m_pfluid->GetElastic()->Tangent_Strain(e, T);\n            double dpT = m_pfluid->GetElastic()->Tangent_Temperature(e, T);\n            double lam = m_Lm[i]*alpha + m_Lmp[i]*(1-alpha);\n            double f = p - m_pn[i];\n            double dpJ2 = m_pfluid->GetElastic()->Tangent_Strain_Strain(e, T);\n            double dpJT = m_pfluid->GetElastic()->Tangent_Strain_Temperature(e, T);\n            double dpT2 = m_pfluid->GetElastic()->Tangent_Temperature_Temperature(e, T);\n            \n            mat3d Kab(lam*dpJ2, lam*dpJT, dpJ,\n                      lam*dpJT, lam*dpT2, dpT,\n                      dpJ, dpT, 0);\n            ke.add(0, 0, Kab);\n            \n            // unpack LM\n            vector<int> lm;\n            UnpackLM(lm, i);\n            ke.SetIndices(lm);\n            \n            // assemle into global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n    else {\n        int ndof = 2;\n        FEElementMatrix ke;\n        ke.resize(ndof, ndof);\n        for (int i=0; i<m_surf.Nodes(); ++i) {\n            ke.zero();\n            FENode& node = m_surf.Node(i);\n            double e = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1-alpha);\n            double T = node.get(m_dofT)*alpha + node.get_prev(m_dofT)*(1-alpha);\n            double p = m_pfluid->GetElastic()->Pressure(e, T);\n            double p0 = m_pn[i];\n            double dpJ = m_pfluid->GetElastic()->Tangent_Strain(e, T);\n            double dpT = m_pfluid->GetElastic()->Tangent_Temperature(e, T);\n            double c = m_Lm[i] + m_eps*(p - p0);\n            double dpJ2 = m_pfluid->GetElastic()->Tangent_Strain_Strain(e, T);\n            double dpJT = m_pfluid->GetElastic()->Tangent_Strain_Temperature(e, T);\n            double dpT2 = m_pfluid->GetElastic()->Tangent_Temperature_Temperature(e, T);\n            \n            ke(0, 0) += m_eps*dpJ*dpJ + c*dpJ2;\n            ke(0, 1) += m_eps*dpJ*dpT + c*dpJT;\n            ke(1, 0) += m_eps*dpJ*dpT + c*dpJT;\n            ke(1, 1) += m_eps*dpT*dpT + c*dpT2;\n\n            // unpack LM\n            vector<int> lm;\n            UnpackLM(lm, i);\n            ke.SetIndices(lm);\n            \n            // assemle into global stiffness matrix\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEThermoFluidPressureLoad::Augment(int naug, const FETimeInfo& tp)\n{\n    if (m_laugon != FECore::AUGLAG_METHOD) return true;\n    \n    double alpha = tp.alphaf;\n    \n    // calculate lag multipliers\n    double L0 = 0, L1 = 0;\n    for (int i=0; i<m_surf.Nodes(); ++i) {\n        FENode& node = m_surf.Node(i);\n        double e = node.get(m_dofEF)*alpha + node.get_prev(m_dofEF)*(1-alpha);\n        double T = node.get(m_dofT)*alpha + node.get_prev(m_dofT)*(1-alpha);\n        double p = m_pfluid->GetElastic()->Pressure(e, T);\n        double lam = m_Lm[i] + m_eps*(p - m_pn[i]);\n        L0 += m_Lm[i]*m_Lm[i];\n        L1 += lam*lam;\n        m_Lmp[i] = lam;\n    }\n    \n    L0 = sqrt(L0);\n    L1 = sqrt(L1);\n    \n    double d;\n    if (L1 != 0)\n        d = fabs((L1 - L0)/L1);\n    else d = fabs(L1 - L0);\n    \n    const std::string name = GetName();\n    feLog(\"constraint %s: %15.7lg %15.7lg %15.7lg\\n\", name.c_str(), L0, fabs(L1 - L0), fabs(m_tol*L1));\n    \n    bool bconv = false;\n    if (d <= m_tol) bconv = true;\n    if ((m_naugmax >= 0) && (naug >= m_naugmax)) bconv = true;\n    if (naug < m_naugmin) bconv = false;\n    \n    if (bconv == false)\n    {\n        for (int i=0; i<m_surf.Nodes(); ++i) m_Lm[i] = m_Lmp[i];\n    }\n    \n    return bconv;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidPressureLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEThermoFluid.h\"\n#include <FECore/FESurfaceConstraint.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEConstraintNormalFlow class implements a fluid surface with zero\n//! tangential velocity as a linear constraint.\n\nclass FEBIOFLUID_API FEThermoFluidPressureLoad : public FESurfaceConstraint\n{\npublic:\n    //! constructor\n    FEThermoFluidPressureLoad(FEModel* pfem);\n    \n    //! destructor\n    ~FEThermoFluidPressureLoad() {}\n    \n    //! calculate traction stiffness (there is none for this load)\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate load vector (there is none for this load)\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! initialize\n    bool Init() override;\n    \n    // allocate equations\n    int InitEquations(int neq) override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    // return the surface\n    FESurface* GetSurface() override { return &m_surf; }\n\nprotected:\n    void UnpackLM(vector<int>& lm, int n);\n    \n    // Build the matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n    \n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \nprotected:\n    int             m_dofT;\n    int             m_dofEF;\n    vector<int>     m_EQ;\n    vector<double>  m_Lm, m_Lmp;\n    FESurface       m_surf;\n    vector<double>  m_pn;       //!< prescribed fluid pressure at nodes\n    \n    FEThermoFluid*    m_pfluid; //!< pointer to thermo-fluid material\n\npublic:\n    FEParamDouble   m_p;        // prescribed pressure\n    int             m_laugon;\n    double          m_tol;\n    double          m_eps;\n    int             m_naugmin;\n    int             m_naugmax;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioThermoFluid.h\"\n#include \"FEFluidHeatSupply.h\"\n#include \"FEFluidResistanceBC.h\"\n#include \"FEBackFlowStabilization.h\"\n#include \"FEFluidNormalVelocity.h\"\n#include \"FEFluidVelocity.h\"\n#include \"FEFluidRotationalVelocity.h\"\n#include \"FETiedFluidInterface.h\"\n#include \"FEThermoFluidSolver.h\"\n#include \"FEThermoFluidDomain3D.h\"\n#include \"FEFluidDomain.h\"\n#include <assert.h>\n#include \"FEFluidResidualVector.h\"\n#include <FEBioMech/FEResidualVector.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/sys.h>\n#include <FEBioMech/FEBodyForce.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FENLConstraint.h>\n#include \"FEThermoFluidAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEThermoFluidSolver, FENewtonSolver)\n    ADD_PARAMETER(m_Vtol , \"vtol\"        );\n    ADD_PARAMETER(m_Ftol , \"ftol\"        );\n    ADD_PARAMETER(m_Ttol , \"ttol\"        );\n    ADD_PARAMETER(m_Etol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n    ADD_PARAMETER(m_Rtol, FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n    ADD_PARAMETER(m_rhoi , \"rhoi\"        );\n    ADD_PARAMETER(m_pred , \"predictor\"   );\n    ADD_PARAMETER(m_minJf, \"min_volume_ratio\");\n    ADD_PARAMETER(m_minT , \"min_abs_temperature\");\n    ADD_PARAMETER(m_solve_strategy, \"solve_strategy\")->setEnums(\"coupled\\0sequential\\0\");\n    ADD_PARAMETER(m_Tmin , \"min_T_drop\");\n    ADD_PARAMETER(m_Tmax , \"min_T_rise\");\n    ADD_PARAMETER(m_Tnum , \"min_T_num\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEThermoFluidSolver Construction\n//\nFEThermoFluidSolver::FEThermoFluidSolver(FEModel* pfem) : FENewtonSolver(pfem), m_dofW(pfem), m_dofAW(pfem), m_dofEF(pfem), m_dofT(pfem)\n{\n    // default values\n    m_Rtol = 0.001;\n    m_Etol = 0.01;\n    m_Vtol = 0.001;\n    m_Ftol = 0.001;\n    m_Ttol = 0.001;\n    m_Rmin = 1.0e-20;\n    m_Rmax = 0;     // not used if zero\n    m_minJf = 0;    // not used if zero\n    m_minT = 0;     // not used if zero\n    m_Tmin = 0;     // not used if zero\n    m_Tmax = 0;     // not used if zero\n    m_Tnum = 1;\n\n    m_nveq = 0;\n    m_ndeq = 0;\n    m_nteq = 0;\n    m_niter = 0;\n    \n    // assume non-symmetric stiffness\n    m_msymm = REAL_UNSYMMETRIC;\n\n    m_solve_strategy = SOLVE_COUPLED;\n    \n    m_rhoi = 0;\n    m_pred = 0;\n    \n    m_sudden_T_change = false;\n    \n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n\n    // turn off checking for a zero diagonal\n    CheckZeroDiagonal(false);\n\n    // get the dof indices\n    // TODO: Can this be done in Init, since  there is no error checking\n    if (pfem)\n    {\n        m_dofW.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_VELOCITY));\n        m_dofAW.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_ACCELERATION));\n        m_dofEF.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION));\n        m_dofAEF = pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::FLUID_DILATATION_TDERIV), 0);\n        m_dofT.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE));\n        m_dofAT = pfem->GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE_TDERIV), 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEThermoFluidSolver::~FEThermoFluidSolver()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FEThermoFluidSolver\n//\nbool FEThermoFluidSolver::Init()\n{\n    // initialize base class\n    if (FENewtonSolver::Init() == false) return false;\n\n    // check parameters\n    if (m_Vtol <  0.0) { feLogError(\"vtol must be nonnegative.\"); return false; }\n    if (m_Ftol <  0.0) { feLogError(\"dtol must be nonnegative.\"); return false; }\n    if (m_Ttol <  0.0) { feLogError(\"ttol must be nonnegative.\"); return false; }\n    if (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n    if (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n    \n    if (m_rhoi == -1) {\n        m_alphaf = m_alpham = m_gammaf = 1.0;\n    }\n    else if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n        m_alphaf = 1.0/(1+m_rhoi);\n        m_alpham = (3-m_rhoi)/(1+m_rhoi)/2;\n        m_gammaf = 0.5 + m_alpham - m_alphaf;\n    }\n    else { feLogError(\"rhoi must be -1 or between 0 and 1.\"); return false; }\n    \n    // allocate vectors\n    int neq = m_neq;\n    m_Fr.assign(neq, 0);\n    m_Ui.assign(neq, 0);\n    m_Ut.assign(neq, 0);\n    m_vi.assign(m_nveq,0);\n    m_Vi.assign(m_nveq,0);\n    m_di.assign(m_ndeq,0);\n    m_Di.assign(m_ndeq,0);\n    m_ti.assign(m_nteq,0);\n    m_Ti.assign(m_nteq,0);\n\n    // we need to fill the total DOF vector m_Ut\n    // TODO: I need to find an easier way to do this\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    gather(m_Ut, mesh, m_dofW[0]);\n    gather(m_Ut, mesh, m_dofW[1]);\n    gather(m_Ut, mesh, m_dofW[2]);\n    gather(m_Ut, mesh, m_dofEF[0]);\n    gather(m_Ut, mesh, m_dofT[0]);\n\n    // set flag for transient or steady-state analyses\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    for (int i = 0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.IsActive()) {\n            FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(&dom);\n            if (pstep->m_nanalysis == FEThermoFluidAnalysis::STEADY_STATE)\n                fdom->SetSteadyStateAnalysis();\n            else\n                fdom->SetTransientAnalysis();\n        }\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEThermoFluidSolver::InitEquations()\n{\n    // Add the solution variables\n    AddSolutionVariable(&m_dofW , 1, \"velocity\"   , m_Vtol);\n    AddSolutionVariable(&m_dofEF, 1, \"dilatation\" , m_Ftol);\n    AddSolutionVariable(&m_dofT , 1, \"temperature\", m_Ttol);\n\n    // base class initialization\n    if (FENewtonSolver::InitEquations() == false) return false;\n    \n    // determined the nr of velocity and dilatation equations\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    m_nveq = m_ndeq = m_nteq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofW[0]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2]] != -1) m_nveq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofT[0]] != -1) m_nteq++;\n    }\n\n    // check that we are using a block scheme for sequential solves\n    if ((m_solve_strategy == SOLVE_SEQUENTIAL) && (m_eq_scheme != EQUATION_SCHEME::BLOCK))\n    {\n        feLogWarning(\"You need a block solver when using the sequential solve strategy.\");\n        return false;\n    }\n    \n    // Next, we add any Lagrange Multipliers\n    FEModel& fem = *GetFEModel();\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n    \n    if (m_eq_scheme == EQUATION_SCHEME::BLOCK)\n    {\n        // repartition the equations so that we only have two partitions,\n        // one for the fluid-dilatation, and one for the temperature.\n        \n        // fluid equations is all the rest\n        int nfeq = m_neq - m_nteq;\n        \n        // create the new partitions\n        // Note that this assumes that the temperature equations are always last!\n        vector<int> p = { nfeq, m_nteq };\n        SetPartitions(p);\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEThermoFluidSolver::InitEquations2()\n{\n    // Add the solution variables\n    AddSolutionVariable(&m_dofW , 1, \"velocity\"   , m_Vtol);\n    AddSolutionVariable(&m_dofEF, 1, \"dilatation\" , m_Ftol);\n    AddSolutionVariable(&m_dofT , 1, \"temperature\", m_Ttol);\n\n    // base class initialization\n    if (FENewtonSolver::InitEquations2() == false) return false;\n    \n    // determined the nr of velocity and dilatation equations\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    m_nveq = m_ndeq = m_nteq = 0;\n    \n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& n = mesh.Node(i);\n        if (n.m_ID[m_dofW[0]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[1]] != -1) m_nveq++;\n        if (n.m_ID[m_dofW[2]] != -1) m_nveq++;\n        if (n.m_ID[m_dofEF[0]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofT[0]] != -1) m_nteq++;\n    }\n\n    // Next, we add any Lagrange Multipliers\n    FEModel& fem = *GetFEModel();\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* lmc = fem.NonlinearConstraint(i);\n        if (lmc->IsActive())\n        {\n            m_neq += lmc->InitEquations(m_neq);\n        }\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive())\n        {\n            m_neq += spc->InitEquations(m_neq);\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidSolver::GetVelocityData(vector<double> &vi, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(vi);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofW[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n        nid = n.m_ID[m_dofW[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            vi[m++] = ui[nid];\n            assert(m <= (int) vi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidSolver::GetDilatationData(vector<double> &ei, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ei);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofEF[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ei[m++] = ui[nid];\n            assert(m <= (int) ei.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidSolver::GetTemperatureData(vector<double> &ti, vector<double> &ui)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GetMesh().Nodes(), nid, m = 0;\n    zero(ti);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& n = fem.GetMesh().Node(i);\n        nid = n.m_ID[m_dofT[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ti[m++] = ui[nid];\n            assert(m <= (int) ti.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEThermoFluidSolver::UpdateKinematics(vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n    \n    scatter(U, mesh, m_dofW[0]);\n    scatter(U, mesh, m_dofW[1]);\n    scatter(U, mesh, m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n//    scatter(U, mesh, m_dofT[0]);\n\n    // update temperature data\n    int nssd = 0, nssr = 0;\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        \n        // update nodal temperature\n        int n = node.m_ID[m_dofT[0]];\n        // Force the temperature to remain positive\n        if (n >= 0) {\n            double Tt = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n            double Tp = node.get_prev(m_dofT[0]);\n            if ((m_Tmin > 0) && (node.get_bc(m_dofT[0]) == DOF_OPEN) && (Tp - Tt >= m_Tmin))\n                nssd++;\n            if ((m_Tmax > 0) && (node.get_bc(m_dofT[0]) == DOF_OPEN) && (Tt - Tp >= m_Tmax))\n                nssr++;\n            node.set(m_dofT[0], Tt);\n        }\n    }\n    \n    if (nssd >= m_Tnum) m_sudden_T_change = true;\n    if (nssr >= m_Tnum) m_sudden_T_change = true;\n    \n    // force dilatations to remain greater than -1\n    if (m_minJf > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofEF[0]) <= -1.0)\n                node.set(m_dofEF[0], m_minJf - 1.0);\n        }\n    }\n\n    // force absolute temperature to remain greater than 0\n    double Tr = fem.GetGlobalConstant(\"T\");\n    if (m_minT > 0) {\n        const int NN = mesh.Nodes();\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = mesh.Node(i);\n            if (node.get(m_dofT[0]) <= -Tr)\n                node.set(m_dofT[0], m_minT - Tr);\n        }\n    }\n    \n    // make sure the prescribed velocities are fulfilled\n    int nvel = fem.BoundaryConditions();\n    for (int i=0; i<nvel; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.Update();\n    }\n\n    // enforce the linear constraints\n    // TODO: do we really have to do this? Shouldn't the algorithm\n    // already guarantee that the linear constraints are satisfied?\n    FELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n    if (LCM.LinearConstraints() > 0)\n    {\n        LCM.Update();\n    }\n    \n    // update time derivatives of velocity and dilatation\n    // for dynamic simulations\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    if (pstep->m_nanalysis == FEThermoFluidAnalysis::DYNAMIC)\n    {\n        int N = mesh.Nodes();\n        double dt = fem.GetTime().timeIncrement;\n        double cgi = 1 - 1.0/m_gammaf;\n        for (int i=0; i<N; ++i)\n        {\n            FENode& n = mesh.Node(i);\n            \n            // velocity time derivative\n            vec3d vft = n.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d vfp = n.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n            vec3d aft = n.get_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            vec3d afp = n.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n            aft = afp*cgi + (vft - vfp)/(m_gammaf*dt);\n            n.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], aft);\n            \n            // dilatation time derivative\n            double eft = n.get(m_dofEF[0]);\n            double efp = n.get_prev(m_dofEF[0]);\n            double aefp = n.get_prev(m_dofAEF);\n            double aeft = aefp*cgi + (eft - efp)/(m_gammaf*dt);\n            n.set(m_dofAEF, aeft);\n            \n            // temperature time derivative\n            double Tt = n.get(m_dofT[0]);\n            double Tp = n.get_prev(m_dofT[0]);\n            double aTp = n.get_prev(m_dofAT);\n            double aTt = aTp*cgi + (Tt - Tp)/(m_gammaf*dt);\n            n.set(m_dofAT, aTt);\n        }\n    }\n    // update nonlinear constraints (needed for updating Lagrange Multiplier)\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* nlc = fem.NonlinearConstraint(i);\n        if (nlc->IsActive()) nlc->Update(m_Ui, ui);\n    }\n    for (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n    {\n        FESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n        if (spc->IsActive()) spc->Update(m_Ui, ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidSolver::Update2(const vector<double>& ui)\n{\n    // get the mesh\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n\n    // update nodes\n    vector<double> U(m_Ut.size());\n    for (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n    scatter(U, mesh, m_dofW[0]);\n    scatter(U, mesh, m_dofW[1]);\n    scatter(U, mesh, m_dofW[2]);\n    scatter(U, mesh, m_dofEF[0]);\n    scatter(U, mesh, m_dofT[0]);\n\n    // Update the prescribed nodes\n    for (int i = 0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        if (node.m_rid == -1)\n        {\n            vec3d dv(0, 0, 0);\n            for (int j = 0; j < node.m_ID.size(); ++j)\n            {\n                int nj = -node.m_ID[j] - 2; if (nj >= 0) node.set(j, node.get(j) + ui[nj]);\n            }\n        }\n    }\n\n    // update model state\n    GetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEThermoFluidSolver::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update kinematics\n    UpdateKinematics(ui);\n    \n    // update model state\n//    GetFEModel()->Update();\n    UpdateModel();\n}\n\n//-----------------------------------------------------------------------------\n//! Update DOF increments\nvoid FEThermoFluidSolver::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // extract the velocity and dilatation increments\n    GetVelocityData(m_vi, ui);\n    GetDilatationData(m_di, ui);\n    GetTemperatureData(m_ti, ui);\n\n    // update all degrees of freedom\n    for (int i=0; i<m_neq; ++i) Ui[i] += ui[i];\n        \n    // update velocities\n    for (int i = 0; i<m_nveq; ++i) m_Vi[i] += m_vi[i];\n\n    // update dilatations\n    for (int i = 0; i<m_ndeq; ++i) m_Di[i] += m_di[i];\n        \n    // update temperatures\n    for (int i = 0; i<m_nteq; ++i) m_Ti[i] += m_ti[i];\n        \n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->UpdateIncrements(Ui, ui);\n    }\n    \n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->UpdateIncrements(Ui, ui);\n\t}\n\n    // TODO: This is a hack!\n    // The problem is that I only want to call the domain's IncrementalUpdate during\n    // the quasi-Newtoon loop. However, this function is also called after the loop\n    // converges. The emap parameter is used here to detect wether we are inside the\n    // loop (emap == false), or not (emap == true).\n    if (emap == false)\n    {\n        for (int i = 0; i < mesh.Domains(); ++i)\n        {\n            FEDomain& dom = mesh.Domain(i);\n            dom.IncrementalUpdate(ui, true);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update nonlinear constraints\nvoid FEThermoFluidSolver::UpdateConstraints()\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // Update all nonlinear constraints\n    for (int i = 0; i<fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* pci = fem.NonlinearConstraint(i);\n        if (pci->IsActive()) pci->Update();\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEThermoFluidSolver::InitStep(double time)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // set time integration parameters\n    FETimeInfo& tp = fem.GetTime();\n    tp.alphaf = m_alphaf;\n    tp.alpham = m_alpham;\n    tp.gamma = m_gammaf;\n    \n    // evaluate load curve values at current (or intermediate) time\n    double t = tp.currentTime;\n    double dt = tp.timeIncrement;\n    double ta = (t > 0) ? t - (1-m_alphaf)*dt : m_alphaf*dt;\n    \n    return FESolver::InitStep(ta);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration.\nvoid FEThermoFluidSolver::PrepStep()\n{\n    FEModel& fem = *GetFEModel();\n\n    FETimeInfo& tp = fem.GetTime();\n    double dt = tp.timeIncrement;\n    tp.currentIteration = m_niter;\n\n    // zero total DOFs\n    zero(m_Ui);\n    zero(m_Vi);\n    zero(m_Di);\n    zero(m_Ti);\n\n    // store previous mesh state\n    // we need them for strain and acceleration calculations\n    FEMesh& mesh = fem.GetMesh();\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& ni = mesh.Node(i);\n        ni.m_rp = ni.m_rt = ni.m_r0;\n        ni.UpdateValues();\n        \n        switch (m_pred) {\n            case 0:\n            {\n                // initial guess at start of new time step (default)\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], afp*(m_gammaf-1)/m_gammaf);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF)*(m_gammaf-1)/m_gammaf);\n                ni.set(m_dofAT, ni.get_prev(m_dofAT)*(m_gammaf-1)/m_gammaf);\n            }\n                break;\n                \n            case 1:\n            {\n                // initial guess at start of new time step (Zero Ydot)\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], vec3d(0,0,0));\n                ni.set(m_dofAEF, 0);\n                ni.set(m_dofAT, 0);\n\n                vec3d vfp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], vfp + afp*dt*(1-m_gammaf)*m_alphaf);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt*(1-m_gammaf)*m_alphaf);\n                ni.set(m_dofT[0] , ni.get_prev(m_dofT[0])  + ni.get_prev(m_dofAT )*dt*(1-m_gammaf)*m_alphaf);\n            }\n                break;\n                \n            case 2:\n            {\n                // initial guess at start of new time step (Same Ydot)\n                vec3d afp = ni.get_vec3d_prev(m_dofAW[0], m_dofAW[1], m_dofAW[2]);\n                ni.set_vec3d(m_dofAW[0], m_dofAW[1], m_dofAW[2], afp);\n                ni.set(m_dofAEF, ni.get_prev(m_dofAEF));\n                ni.set(m_dofAT, ni.get_prev(m_dofAT));\n\n                vec3d vfp = ni.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2]);\n                ni.set_vec3d(m_dofW[0], m_dofW[1], m_dofW[2], vfp + afp*dt);\n                ni.set(m_dofEF[0], ni.get_prev(m_dofEF[0]) + ni.get_prev(m_dofAEF)*dt);\n                ni.set(m_dofT[0] , ni.get_prev(m_dofT[0])  + ni.get_prev(m_dofAT)*dt);\n            }\n                break;\n                \n            default:\n                break;\n        }\n    }\n    \n    // apply prescribed velocities\n    // we save the prescribed velocity increments in the ui vector\n    vector<double>& ui = m_ui;\n    zero(ui);\n    int nbc = fem.BoundaryConditions();\n    for (int i=0; i<nbc; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive() && HasActiveDofs(bc.GetDofList())) bc.PrepStep(ui);\n    }\n    \n    // do the linear constraints\n    fem.GetLinearConstraintManager().PrepStep();\n\n    // initialize material point data\n    // NOTE: do this before the stresses are updated\n    // TODO: does it matter if the stresses are updated before\n    //       the material point data is initialized\n    // update domain data\n    for (int i=0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n\n    // update model state\n    UpdateModel();\n\n    for (int i = 0; i < fem.NonlinearConstraints(); ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc && plc->IsActive()) plc->PrepStep();\n    }\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n    \n    // apply prescribed DOFs for specialized surface loads\n    int nsl = fem.ModelLoads();\n    for (int i = 0; i < nsl; ++i)\n    {\n        FEModelLoad& pml = *fem.ModelLoad(i);\n        if (pml.IsActive()) pml.PrepStep();\n    }\n\n    // see if we need to do contact augmentations\n    m_baugment = false;\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n        if (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n    }\n    \n    // see if we have to do nonlinear constraint augmentations\n    if (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEThermoFluidSolver::Quasin()\n{\n    FEModel& fem = *GetFEModel();\n\n    // convergence norms\n    double    normR1;       // residual norm\n    double    normE1;       // energy norm\n    double    normV;        // velocity norm\n    double    normv;        // velocity increment norm\n    double    normRi = 0;   // initial residual norm\n    double    normVi = 0;   // initial velocity norm\n    double    normEi = 0;   // initial energy norm\n    double    normEm = 0;   // max energy norm\n    double    normDi = 0;   // initial dilatation norm\n    double    normD;        // current dilatation norm\n    double    normd;        // incremement dilatation norm\n    double    normTi = 0;   // initial temperature norm\n    double    normT;        // current temperature norm\n    double    normt;        // incremement temperature norm\n\n    // prepare for the first iteration\n    const FETimeInfo& tp = fem.GetTime();\n    PrepStep();\n    \n    // Init QN method\n    if (QNInit() == false) return false;\n    \n    // this flag indicates whether the velocity has converged for a sequential solve\n    // (This is not used for a coupled solve.)\n    bool vel_converged = false;\n    \n    // loop until converged or when max nr of reformations reached\n    bool bconv = false; // convergence flag\n    do\n    {\n        feLog(\" %d\\n\", m_niter+1);\n        \n        // assume we'll converge.\n        bconv = true;\n        \n        // for sequential solve, we set one of the residual components to zero\n        if (m_solve_strategy == SOLVE_SEQUENTIAL)\n        {\n            int veq = m_neq - m_nteq;\n            if (vel_converged == false)\n            {\n                // zero the solute residual\n                for (int i = veq; i < m_neq; ++i) m_R0[i] = 0.0;\n            }\n            else\n            {\n                // zero the velocity residual\n                for (int i = 0; i < veq; ++i) m_R0[i] = 0.0;\n            }\n        }\n        \n        // solve the equations\n        SolveEquations(m_ui, m_R0);\n\n        // do the line search\n        double s = DoLineSearch();\n\n        // for sequential solve, we set one of the residual components to zero\n        if (m_solve_strategy == SOLVE_SEQUENTIAL)\n        {\n            int veq = m_neq - m_nteq;\n            if (vel_converged == false)\n            {\n                // zero the solute residual\n                for (int i = veq; i < m_neq; ++i) m_R1[i] = 0.0;\n                \n                // zero the solute solution\n                for (int i = veq; i < m_neq; ++i) m_ui[i] = 0.0;\n            }\n            else\n            {\n                // zero the velocity residual\n                for (int i = 0; i < veq; ++i) m_R1[i] = 0.0;\n            }\n        }\n        \n        // set initial convergence norms\n        if (m_niter == 0)\n        {\n            normRi = fabs(m_R0*m_R0);\n            normEi = fabs(m_ui*m_R0);\n            normVi = fabs(m_vi*m_vi);\n            normDi = fabs(m_di*m_di);\n            normTi = fabs(m_ti*m_ti);\n            normEm = normEi;\n        }\n        \n        // calculate actual increment\n        // NOTE: We don't apply the line search directly to m_ui since we need the unscaled search direction for the QN update below\n        int neq = (int)m_Ui.size();\n        vector<double> ui(m_ui);\n        for (int i = 0; i<neq; ++i) ui[i] *= s;\n\n        // update other increments (e.g., Lagrange multipliers)\n        UpdateIncrements(m_Ui, ui, false);\n        \n        // calculate the norms\n        normR1 = m_R1*m_R1;\n        normv  = m_vi*m_vi;\n        normV  = m_Vi*m_Vi;\n        normd  = m_di*m_di;\n        normD  = m_Di*m_Di;\n        normt  = m_ti*m_ti;\n        normT  = m_Ti*m_Ti;\n        normE1 = fabs(m_ui*m_R1);\n        \n        // check for nans\n        if (ISNAN(normR1)) throw NANInResidualDetected();\n        \n        // check residual norm\n        if ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\n        \n        // check velocity norm\n        if ((m_Vtol > 0) && (normv  > (m_Vtol*m_Vtol)*normV )) bconv = false;\n        \n        // check dilatation norm\n        if ((m_Ftol > 0) && (normd  > (m_Ftol*m_Ftol)*normD )) bconv = false;\n        \n        // check temperature norm\n        if ((m_Ttol > 0) && (normt  > (m_Ttol*m_Ttol)*normT )) bconv = false;\n        \n        // check energy norm\n        if ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n        \n        // check linestep size\n        if ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n        \n        // check energy divergence\n        if (normE1 > normEm) bconv = false;\n        \n        // print convergence summary\n        feLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n        feLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n        feLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n        feLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n        if (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n        feLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n        feLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n        feLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n        feLog(\"\\t   velocity         %15le %15le %15le \\n\", normVi, normv ,(m_Vtol*m_Vtol)*normV );\n        feLog(\"\\t   dilatation       %15le %15le %15le \\n\", normDi, normd ,(m_Ftol*m_Ftol)*normD );\n        feLog(\"\\t   temperature      %15le %15le %15le \\n\", normTi, normt ,(m_Ttol*m_Ttol)*normT );\n\n        // see if we may have a small residual\n        if ((bconv == false) && (normR1 < m_Rmin))\n        {\n            // check for almost zero-residual on the first iteration\n            // this might be an indication that there is no force on the system\n            feLogWarning(\"No force acting on the system.\");\n            bconv = true;\n        }\n        \n        // see if we have exceeded the max residual\n        if ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n        {\n            // doesn't look like we're getting anywhere, so let's retry the time step\n            throw MaxResidualError();\n        }\n\n        // check if we have converged.\n        // If not, calculate the BFGS update vectors\n        if (bconv == false)\n        {\n            if (s < m_lineSearch->m_LSmin)\n            {\n                // check for zero linestep size\n                feLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n                QNForceReform(true);\n            }\n            else if ((normE1 > normEm) && m_bdivreform)\n            {\n                // check for diverging\n                feLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n                normEm = normE1;\n                normEi = normE1;\n                normRi = normR1;\n                normVi = normv;\n                normDi = normd;\n                normTi = normt;\n                QNForceReform(true);\n            }\n\n            // Do the QN update (This may also do a stiffness reformation if necessary)\n            bool bret = QNUpdate();\n\n            // something went wrong with the update, so we'll need to break\n            if (bret == false) break;\n        }\n        else if (m_baugment)\n        {\n            // Do augmentations\n            bconv = DoAugmentations();\n        }\n        \n        if (bconv && (m_solve_strategy == SOLVE_SEQUENTIAL))\n        {\n            if (vel_converged == false)\n            {\n                vel_converged = true;\n                bconv = false;\n                m_qnstrategy->m_nups = 0;\n                m_niter = -1;\n                Residual(m_R0);\n                feLog(\"\\n*** Velocity converged. Now solving for temperature.\\n\");\n            }\n        }\n        \n        // check for sudden temperature change\n        if (bconv && m_sudden_T_change) {\n            m_sudden_T_change = false;\n            throw ConcentrationChangeDetected();\n        }\n        else m_sudden_T_change = false;\n        \n        // increase iteration number\n        m_niter++;\n        \n        // do minor iterations callbacks\n        fem.DoCallback(CB_MINOR_ITERS);\n    }\n    while (bconv == false);\n    \n    // if converged we update the total velocities\n    if (bconv)\n    {\n        UpdateIncrements(m_Ut, m_Ui, true);\n        zero(m_Ui);\n        zero(m_Di); zero(m_Vi); zero(m_Ti);\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEThermoFluidSolver::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n\n    const FETimeInfo& tp = fem.GetTime();\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the stiffness matrix for each domain\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.StiffnessMatrix(LS);\n    }\n    \n    // calculate the body force stiffness matrix for each domain\n    int NML = fem.ModelLoads();\n    for (int j = 0; j<NML; ++j)\n    {\n        FEModelLoad* pml = fem.ModelLoad(j);\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(pml);\n        FEFluidHeatSupply* phs = dynamic_cast<FEFluidHeatSupply*>(pml);\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(*pbf->Domain(i));\n                dom.BodyForceStiffness(LS, *pbf);\n            }\n        }\n        else if (phs && phs->IsActive())\n        {\n            for (int i = 0; i<phs->Domains(); ++i)\n            {\n                FEThermoFluidDomain3D* tdom = dynamic_cast<FEThermoFluidDomain3D*>(phs->Domain(i));\n                if (tdom) tdom->HeatSupplyStiffness(LS, *phs);\n            }\n        }\n    }\n    \n    // calculate contact stiffness\n    ContactStiffness(LS);\n    \n    // calculate stiffness matrix due to model loads\n    int nsl = fem.ModelLoads();\n    for (int i=0; i<nsl; ++i)\n    {\n        FEModelLoad* pml = fem.ModelLoad(i);\n        if (pml->IsActive()) pml->StiffnessMatrix(LS);\n    }\n    // Add mass matrix\n    // loop over all domains\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.MassMatrix(LS);\n    }\n    \n    // calculate nonlinear constraint stiffness\n    // note that this is the contribution of the\n    // constraints enforced with augmented lagrangian\n    NonLinearConstraintStiffness(LS, tp);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEThermoFluidSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FEThermoFluidSolver::ContactStiffness(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n\n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEThermoFluidSolver::ContactForces(FEGlobalVector& R)\n{\n    FEModel& fem = *GetFEModel();\n\n    const FETimeInfo& tp = fem.GetTime();\n    for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n    {\n        FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n        if (pci->IsActive()) pci->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry\n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEThermoFluidSolver::Residual(vector<double>& R)\n{\n    FEModel& fem = *GetFEModel();\n\n    // get the time information\n    const FETimeInfo& tp = fem.GetTime();\n\n    // initialize residual with concentrated nodal loads\n    zero(R);\n    \n    // zero nodal reaction forces\n    zero(m_Fr);\n    \n    // setup the global vector\n//    FEFluidResidualVector RHS(fem, R, m_Fr);\n    FEResidualVector RHS(fem, R, m_Fr);\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate the internal (stress) forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& dom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        dom.InternalForces(RHS);\n    }\n    \n\n    // calculate the model loads\n    for (int j = 0; j<fem.ModelLoads(); ++j)\n    {\n        FEModelLoad* pml = fem.ModelLoad(j);\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(pml);\n        FEFluidHeatSupply* phs = dynamic_cast<FEFluidHeatSupply*>(pml);\n        if (pbf && pbf->IsActive())\n        {\n            for (int i = 0; i<pbf->Domains(); ++i)\n            {\n                FEFluidDomain* fdom = dynamic_cast<FEFluidDomain*>(pbf->Domain(i));\n                fdom->BodyForce(RHS, *pbf);\n            }\n        }\n        else if (phs && phs->IsActive())\n        {\n            for (int i = 0; i<phs->Domains(); ++i)\n            {\n                FEThermoFluidDomain3D* tdom = dynamic_cast<FEThermoFluidDomain3D*>(phs->Domain(i));\n                if (tdom) tdom->HeatSupply(RHS, *phs);\n            }\n        }\n    }\n    \n    // calculate inertial forces\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEFluidDomain& fdom = dynamic_cast<FEFluidDomain&>(mesh.Domain(i));\n        fdom.InertialForces(RHS);\n    }\n\n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n    \n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i=0; i<NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive())\n        {\n            mli.LoadVector(RHS);\n        }\n    }\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.set_load(m_dofW[0], 0);\n        node.set_load(m_dofW[1], 0);\n        node.set_load(m_dofW[2], 0);\n\n        int n;\n        if ((n = -node.m_ID[m_dofW[0]] - 2) >= 0) node.set_load(m_dofW[0], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofW[1]] - 2) >= 0) node.set_load(m_dofW[1], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofW[2]] - 2) >= 0) node.set_load(m_dofW[2], -m_Fr[n]);\n    }\n    \n    // increase RHS counter\n    m_nrhs++;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces\nvoid FEThermoFluidSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n\n    int N = fem.NonlinearConstraints();\n    for (int i=0; i<N; ++i)\n    {\n        FENLConstraint* plc = fem.NonlinearConstraint(i);\n        if (plc->IsActive()) plc->LoadVector(R, tp);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FEThermoFluidSolver::Serialize(DumpStream& ar)\n{\n    FENewtonSolver::Serialize(ar);\n\n    ar & m_nrhs;\n    ar & m_niter;\n    ar & m_nref & m_ntotref;\n\n    ar & m_nveq & m_ndeq & m_nteq;\n\n    ar & m_Fr & m_Ui &m_Ut;\n    ar & m_Vi & m_Di & m_Ti;\n    \n    if (ar.IsLoading())\n    {\n        m_Fr.assign(m_neq, 0);\n        m_Vi.assign(m_nveq,0);\n        m_Di.assign(m_ndeq,0);\n        m_Ti.assign(m_nteq,0);\n    }\n    \n    if (ar.IsShallow()) return;\n    \n    ar & m_rhoi & m_alphaf & m_alpham;\n    ar & m_gammaf;\n    ar & m_pred;\n\n    ar & m_dofW & m_dofEF & m_dofAEF & m_dofT & m_dofAT;    \n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/FEDofList.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! The FEThermoFluidSolver class solves thermo-fluid mechanics problems\n//! It can deal with quasi-static and dynamic problems\n//!\nclass FEBIOFLUID_API FEThermoFluidSolver : public FENewtonSolver\n{\npublic:\n    enum SOLVE_STRATEGY {\n        SOLVE_COUPLED,          // monolithic solution approach\n        SOLVE_SEQUENTIAL        // first solve velocity+dilatation, then temperature\n    };\n    \n    //! constructor\n    FEThermoFluidSolver(FEModel* pfem);\n    \n    //! destructor\n    ~FEThermoFluidSolver();\n    \n    //! Initializes data structures\n    bool Init() override;\n    \n    //! initialize the step\n    bool InitStep(double time) override;\n    \n    //! Initialize linear equation system\n    bool InitEquations() override;\n    bool InitEquations2() override;\n\n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n    //{ --- evaluation and update ---\n    //! Perform an update\n    void Update(vector<double>& ui) override;\n\n    //! update nodal positions, velocities, accelerations, etc.\n    void UpdateKinematics(vector<double>& ui);\n\n    //! used by JFNK\n    void Update2(const vector<double>& ui) override;\n    \n    void UpdateConstraints();\n    \n    //! update DOF increments\n    void UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap);\n    //}\n    \n    //{ --- Solution functions ---\n    \n    //! prepares the data for the first QN iteration\n    void PrepStep() override;\n    \n    //! Performs a Newton-Raphson iteration\n    bool Quasin() override;\n    \n    //{ --- Stiffness matrix routines ---\n    \n    //! calculates the global stiffness matrix\n    bool StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! contact stiffness\n    void ContactStiffness(FELinearSystem& LS);\n    \n    //! calculates stiffness contributon of nonlinear constraints\n    void NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n    \n    //{ --- Residual routines ---\n    \n    //! Calculate the contact forces\n    void ContactForces(FEGlobalVector& R);\n    \n    //! Calculates residual\n    bool Residual(vector<double>& R) override;\n    \n    //! Calculate nonlinear constraint forces\n    void NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    void GetVelocityData(vector<double>& vi, vector<double>& ui);\n    void GetDilatationData(vector<double>& ei, vector<double>& ui);\n    void GetTemperatureData(vector<double>& ti, vector<double>& ui);\n\npublic:\n    // convergence tolerances\n    double  m_Vtol;         //!< velocity tolerance\n    double  m_Ftol;         //!< dilatation tolerance\n    double  m_Ttol;         //!< temperature tolerance\n    double  m_minJf;        //!< minimum allowable compression ratio\n    double  m_minT;         //!< minimum allowable absolute temperature\n    double  m_Tmin;     //!< threshold for detecting sudden drop in concentration\n    double  m_Tmax;     //!< threshold for detecting sudden increase in concentration\n    int     m_Tnum;     //!< minimum number of points at which C drops suddenly\n\npublic:\n    // equation numbers\n    int        m_nveq;                //!< number of equations related to velocity dofs\n    int        m_ndeq;                //!< number of equations related to dilatation dofs\n    int        m_nteq;                //!< number of equations related to temperature dofs\n\npublic:\n    vector<double> m_Fr;    //!< nodal reaction forces\n    vector<double> m_vi;    //!< velocity increment vector\n    vector<double> m_Vi;    //!< Total velocity vector for iteration\n    vector<double> m_di;    //!< dilatation increment vector\n    vector<double> m_Di;    //!< Total dilatation vector for iteration\n    vector<double> m_ti;    //!< temperature increment vector\n    vector<double> m_Ti;    //!< Total temperature vector for iteration\n\n    // generalized alpha method\n    double  m_rhoi;         //!< rho infinity\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n    double  m_gammaf;       //!< gamma\n    int     m_pred;         //!< predictor method\n\nprotected:\n    FEDofList   m_dofW;\n    FEDofList   m_dofAW;\n    FEDofList   m_dofEF;\n    FEDofList   m_dofT;\n    int         m_dofAEF;\n    int         m_dofAT;\n\n    int                m_solve_strategy;\n    \nprivate:\n    bool            m_sudden_T_change;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidTemperatureBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEThermoFluidTemperatureBC.h\"\n#include \"FEBioThermoFluid.h\"\n#include \"FEThermoFluid.h\"\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEThermoFluidTemperatureBC::FEThermoFluidTemperatureBC(FEModel* pfem) : FEPrescribedSurface(pfem), m_dofW(pfem)\n{\n    m_dofT = -1;\n    m_surf = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEThermoFluidTemperatureBC::Init()\n{\n    FEModel& fem = *GetFEModel();\n    \n    m_dofW.Clear();\n    m_dofW.AddVariable(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofT = fem.GetDOFIndex(FEBioThermoFluid::GetVariableName(FEBioThermoFluid::TEMPERATURE), 0);\n    SetDOFList(m_dofW);\n    SetDOFList(m_dofT);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n\n    m_surf = GetSurface();\n    m_T.assign(m_surf->Nodes(), 0.0);\n    m_backflow.assign(m_surf->Nodes(), false);\n    \n    m_nnlist.Create(fem.GetMesh());\n    \n    return FEPrescribedSurface::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEThermoFluidTemperatureBC::Update()\n{\n    // determine backflow conditions\n    MarkBackFlow();\n    \n    int N = m_surf->Nodes();\n    std::vector<vector<double>> efNodes(N, vector<double>());\n    std::vector<vector<double>> vn(N, vector<double>());\n    \n    for (int i=0; i<m_surf->Nodes(); ++i)\n    {\n        FENode& node = m_surf->Node(i);\n        \n        if (m_backflow[i]) m_T[i] = node.get_prev(m_dofT);\n        else {\n            int nid = node.GetID()-1; //0 based\n            int val = m_nnlist.Valence(nid);\n            int* nlist = m_nnlist.NodeList(nid);\n            \n            vector<int> connectnidarray;\n            int connectnid=0;\n            //check which connecting nodes are not on surface\n            for (int j = 0; j<val; ++j)\n            {\n                int cnid = *(nlist+j);\n                bool isSurf = false;\n                for (int k=0; k<m_surf->Nodes(); ++k)\n                {\n                    if (cnid==m_surf->Node(k).GetID()-1)\n                        isSurf = true;\n                }\n                if (!isSurf)\n                {\n                    connectnidarray.push_back(cnid);\n                }\n            }\n            //find closest connecting node\n            if (connectnidarray.size()>0)\n            {\n                int cnodeIndex = 0;\n                FENode* cnodeArray = GetFEModel()->GetMesh().FindNodeFromID(connectnidarray[cnodeIndex]+1);\n                double smallDist = sqrt(pow((node.m_rt.x-cnodeArray->m_rt.x),2)+pow((node.m_rt.y-cnodeArray->m_rt.y),2)+pow((node.m_rt.z-cnodeArray->m_rt.z),2));\n                for (int j = 1; j<connectnidarray.size(); ++j)\n                {\n                    FENode* tempNode = GetFEModel()->GetMesh().FindNodeFromID(connectnidarray[j]+1);\n                    double temp = sqrt(pow((node.m_rt.x-tempNode->m_rt.x),2)+pow((node.m_rt.y-tempNode->m_rt.y),2)+pow((node.m_rt.z-tempNode->m_rt.z),2));\n                    if(temp<smallDist)\n                    {\n                        cnodeIndex = j;\n                        cnodeArray = tempNode;\n                        smallDist = temp;\n                    }\n                }\n                connectnid = connectnidarray[cnodeIndex];\n                FENode* cnode = GetFEModel()->GetMesh().FindNodeFromID(connectnid+1);\n                m_T[i] = cnode->get(m_dofT);\n            }\n            else\n                m_T[i] = node.get_prev(m_dofT);\n        }\n    }\n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate the flow rate across this surface\nvoid FEThermoFluidTemperatureBC::MarkBackFlow()\n{\n    // Calculate normal flow velocity on each face to determine\n    // backflow condition\n    vec3d rt[FEElement::MAX_NODES];\n    vec3d vt[FEElement::MAX_NODES];\n    \n    const FETimeInfo& tp = GetTimeInfo();\n    \n    for (int iel=0; iel<m_surf->Elements(); ++iel)\n    {\n        FESurfaceElement& el = m_surf->Element(iel);\n        \n        // nr integration points\n        int nint = el.GaussPoints();\n        \n        // nr of element nodes\n        int neln = el.Nodes();\n        \n        // nodal coordinates\n        for (int i=0; i<neln; ++i) {\n            FENode& node = m_surf->GetMesh()->Node(el.m_node[i]);\n            rt[i] = node.m_rt*tp.alpha + node.m_rp*(1-tp.alpha);\n            vt[i] = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2])*tp.alpha + node.get_vec3d_prev(m_dofW[0], m_dofW[1], m_dofW[2])*(1-tp.alpha);\n        }\n        \n        double* Nr, *Ns;\n        double* N;\n        double* w  = el.GaussWeights();\n        \n        vec3d dxr, dxs, v;\n        double vn = 0;\n        \n        // repeat over integration points\n        for (int n=0; n<nint; ++n)\n        {\n            N  = el.H(n);\n            Nr = el.Gr(n);\n            Ns = el.Gs(n);\n            \n            // calculate the velocity and tangent vectors at integration point\n            dxr = dxs = v = vec3d(0,0,0);\n            for (int i=0; i<neln; ++i)\n            {\n                v += vt[i]*N[i];\n                dxr += rt[i]*Nr[i];\n                dxs += rt[i]*Ns[i];\n            }\n            \n            vec3d normal = dxr ^ dxs;\n            normal.unit();\n            vn += (normal*v)*w[n];\n        }\n        \n        if (vn < 0)\n            for (int i=0; i<neln; ++i) m_backflow[el.m_lnode[i]] = true;\n        else\n            for (int i=0; i<neln; ++i) m_backflow[el.m_lnode[i]] = false;\n\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEThermoFluidTemperatureBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_T[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEThermoFluidTemperatureBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEThermoFluidTemperatureBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_T;\n    if (ar.IsShallow()) return;\n    ar & m_dofT;\n    ar & m_surf;\n}\n"
  },
  {
    "path": "FEBioFluid/FEThermoFluidTemperatureBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febiofluid_api.h\"\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/FESurface.h>\n#include <FECore/FENodeNodeList.h>\n\n//-----------------------------------------------------------------------------\n//! FEThermoFluidTemperatureBC prescribes a temperature BC on an inflow/outflow boundary\n//! by enforcing a simplified version of the energy balance as an essential BC\n//!\nclass FEBIOFLUID_API FEThermoFluidTemperatureBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEThermoFluidTemperatureBC(FEModel* pfem);\n\n    //! set the dilatation\n    void Update() override;\n\n    //! initialize\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! evaluate flow rate\n    void MarkBackFlow();\n    \npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    vector<double>  m_T;\n    FESurface*      m_surf;\n    FENodeNodeList  m_nnlist;\n    vector<bool>    m_backflow;\n\n    FEDofList       m_dofW;\n    int             m_dofT;\n};\n"
  },
  {
    "path": "FEBioFluid/FEThermoViscousFluid.h",
    "content": "#pragma once\n#include \"FEViscousFluid.h\"\n#include \"febiofluid_api.h\"\n\nclass FEBIOFLUID_API FEThermoViscousFluid : public FEViscousFluid\n{\npublic:\n\tFEThermoViscousFluid(FEModel* fem) : FEViscousFluid(fem) {}\n\n\t//! tangent of stress with respect to temperature\n\tvirtual mat3ds Tangent_Temperature(FEMaterialPoint& mp) = 0;\n};\n"
  },
  {
    "path": "FEBioFluid/FETiedFluidInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedFluidInterface.h\"\n#include <FECore/FENormalProjection.h>\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/log.h>\n#include <FECore/DumpStream.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n#include \"FEBioFluid.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FETiedFluidInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n\tADD_PARAMETER(m_gtol     , \"gaptol\"             );\n\tADD_PARAMETER(m_ptol     , \"ptol\"               );\n\tADD_PARAMETER(m_epst     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_epsn     , \"pressure_penalty\"   );\n\tADD_PARAMETER(m_srad     , \"search_radius\"      );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\n    ADD_PARAMETER(m_bfreedofs, \"free_dofs\"          );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETiedFluidSurface::Data::Data()\n{\n    m_Gap = vec3d(0,0,0);\n    m_vg  = vec3d(0,0,0);\n    m_nu  = vec3d(0,0,0);\n    m_rs  = vec2d(0,0);\n    m_Lmd = vec3d(0,0,0);\n    m_tv  = vec3d(0,0,0);\n    m_Lmp = 0.0;\n    m_epst= 1.0;\n    m_epsn= 1.0;\n    m_pg  = 0.0;\n    m_vn  = 0.0;\n    m_pme = (FESurfaceElement*)0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_Gap;\n\tar & m_vg;\n\tar & m_nu;\n\tar & m_rs;\n\tar & m_Lmd;\n    ar & m_tv;\n    ar & m_Lmp;\n\tar & m_epst;\n\tar & m_epsn;\n\tar & m_pg;\n\tar & m_vn;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedFluidSurface\n//-----------------------------------------------------------------------------\n\nFETiedFluidSurface::FETiedFluidSurface(FEModel* pfem) : FEContactSurface(pfem), m_dofWE(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedFluidSurface::Init()\n{\n    // initialize surface data first\n    if (FEContactSurface::Init() == false) return false;\n    \n\t// set the dof list\n\tif (m_dofWE.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY)) == false) return false;\n    if (m_dofWE.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION)) == false) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidSurface::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    lm.resize(N*4);\n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        FENode& node = m_pMesh->Node(n);\n        vector<int>& id = node.m_ID;\n        \n        lm[4*i  ] = id[m_dofWE[0]];\n        lm[4*i+1] = id[m_dofWE[1]];\n        lm[4*i+2] = id[m_dofWE[2]];\n        lm[4*i+3] = id[m_dofWE[3]];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FETiedFluidSurface::CreateMaterialPoint()\n{\n\treturn new FETiedFluidSurface::Data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidSurface::GetVelocityGap(int nface, vec3d& vg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vg = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tvg += d.m_vg;\n\t}\n    vg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidSurface::GetPressureGap(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tpg += d.m_pg;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidSurface::GetViscousTraction(int nface, vec3d& tv)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    tv = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\ttv += d.m_tv;\n\t}\n    tv /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidSurface::GetNormalVelocity(int nface, double& vn)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vn = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tvn += d.m_vn;\n\t}\n    vn /= ni;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedFluidInterface\n//-----------------------------------------------------------------------------\n\nFETiedFluidInterface::FETiedFluidInterface(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem), m_dofWE(pfem)\n{\n    static int count = 1;\n    SetID(count++);\n    \n    // initial values\n    m_atol = 0.1;\n    m_epst = 1;\n    m_epsn = 1;\n    m_btwo_pass = false;\n    m_stol = 0.01;\n    m_srad = 1.0;\n    m_gtol = -1;    // we use augmentation tolerance by default\n    m_ptol = -1;    // we use augmentation tolerance by default\n    m_bautopen = false;\n    m_bfreedofs = false;\n    \n    m_naugmin = 0;\n    m_naugmax = 10;\n    \n    // set parents\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n\n    m_ss.SetSibling(&m_ms);\n    m_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFETiedFluidInterface::~FETiedFluidInterface()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedFluidInterface::Init()\n{\n    // initialize surface data\n    if (m_ss.Init() == false) return false;\n    if (m_ms.Init() == false) return false;\n    \n    // get the DOFS\n    m_dofWE.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::RELATIVE_FLUID_VELOCITY));\n    m_dofWE.AddVariable(FEBioFluid::GetVariableName(FEBioFluid::FLUID_DILATATION));\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FETiedFluidInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n    FEMesh& mesh = GetMesh();\n    \n    vector<int> lm(4*FEElement::MAX_NODES*2);\n    \n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FETiedFluidSurface& ss = (np == 0? m_ss : m_ms);\n        \n        int ni = 0, k, l;\n        for (int j=0; j<ss.Elements(); ++j)\n        {\n            FESurfaceElement& se = ss.Element(j);\n            int nint = se.GaussPoints();\n            int* sn = &se.m_node[0];\n            for (k=0; k<nint; ++k, ++ni)\n            {\n                FETiedFluidSurface::Data& pt = static_cast<FETiedFluidSurface::Data&>(*se.GetMaterialPoint(k));\n                FESurfaceElement* pe = pt.m_pme;\n                if (pe != 0)\n                {\n                    FESurfaceElement& me = *pe;\n                    int* mn = &me.m_node[0];\n                    \n                    assign(lm, -1);\n                    \n                    int nseln = se.Nodes();\n                    int nmeln = me.Nodes();\n                    \n                    for (l=0; l<nseln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(sn[l]).m_ID;\n                        lm[4*l  ] = id[m_dofWE[0]];\n                        lm[4*l+1] = id[m_dofWE[1]];\n                        lm[4*l+2] = id[m_dofWE[2]];\n                        lm[4*l+3] = id[m_dofWE[3]];\n                    }\n                    \n                    for (l=0; l<nmeln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(mn[l]).m_ID;\n                        lm[4*(l+nseln)  ] = id[m_dofWE[0]];\n                        lm[4*(l+nseln)+1] = id[m_dofWE[1]];\n                        lm[4*(l+nseln)+2] = id[m_dofWE[2]];\n                        lm[4*(l+nseln)+3] = id[m_dofWE[3]];\n                    }\n                    \n                    K.build_add(lm);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidInterface::Activate()\n{\n    // don't forget to call the base class\n    FEContactInterface::Activate();\n    \n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPressurePenalty(m_ss);\n        if (m_btwo_pass) CalcAutoPressurePenalty(m_ms);\n    }\n    \n    // project the surfaces onto each other\n    // this will evaluate the gap functions in the reference configuration\n    InitialProjection(m_ss, m_ms);\n    if (m_btwo_pass) InitialProjection(m_ms, m_ss);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidInterface::CalcAutoPressurePenalty(FETiedFluidSurface& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPressurePenalty(el, s);\n        \n        // assign to integration points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n\t\t\tFETiedFluidSurface::Data& pt = static_cast<FETiedFluidSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epst = eps;\n            if (eps != 0) pt.m_epsn = 1./eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\ndouble FETiedFluidInterface::AutoPressurePenalty(FESurfaceElement& el, FETiedFluidSurface& s)\n{\n    // get the mesh\n    FEMesh& m = GetMesh();\n    \n    // evaluate element surface normal at parametric center\n    vec3d t[2];\n    s.CoBaseVectors0(el, 0, 0, t);\n    vec3d n = t[0] ^ t[1];\n    n.unit();\n    \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n    \n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n    \n    // check that it is a fluid material\n    FEFluidMaterial* fluid = dynamic_cast<FEFluidMaterial*> (pm);\n    if (fluid == 0) return 0.0;\n    m_pfluid = fluid;\n\n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n    // get the shear viscosity\n    double mu = fluid->GetViscous()->ShearViscosity(mp);\n\n    // get the area of the surface element\n    double A = s.FaceArea(el);\n    \n    // get the volume of the volume element\n    double V = m.ElementVolume(*pe);\n    \n    return mu*A/V;\n}\n\n//-----------------------------------------------------------------------------\n// Perform initial projection between tied surfaces in reference configuration\nvoid FETiedFluidInterface::InitialProjection(FETiedFluidSurface& ss, FETiedFluidSurface& ms)\n{\n    FEMesh& mesh = GetMesh();\n    FESurfaceElement* pme;\n    vec3d r, nu;\n    double rs[2];\n    \n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n    \n    FEClosestPointProjection cp(ss);\n    cp.SetTolerance(m_stol);\n    cp.SetSearchRadius(m_srad);\n    cp.Init();\n    vec3d sq;\n    vec2d srs;\n\n    // loop over all integration points\n    int n = 0;\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        \n        int nint = el.GaussPoints();\n        \n        for (int j=0; j<nint; ++j, ++n)\n        {\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // calculate the normal at this integration point\n            nu = ss.SurfaceNormal(el, j);\n            \n            // find the intersection point with the secondary surface\n            pme = np.Project2(r, nu, rs);\n            \n\t\t\tFETiedFluidSurface::Data& pt = static_cast<FETiedFluidSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_pme = pme;\n            pt.m_nu = nu;\n            pt.m_rs[0] = rs[0];\n            pt.m_rs[1] = rs[1];\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                pt.m_Gap = q - r;\n                \n                // free the nodal dofs of pme if requested\n                if (m_bfreedofs) {\n                    for (int k=0; k<pme->Nodes(); ++k) {\n                        FENode& node = mesh.Node(pme->m_node[k]);\n                        FESurfaceElement* sme = cp.Project(node.m_rt, sq, srs);\n                        if (sme) {\n                            for (int l=0; l<m_dofWE.Size(); ++l)\n                                if (node.get_bc(m_dofWE[l]) != DOF_OPEN) node.set_bc(m_dofWE[l], DOF_OPEN);\n                        }\n                    }\n                }\n            }\n            else\n            {\n                // the node is not in contact\n                pt.m_Gap = vec3d(0,0,0);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate gap functions for fluid velocity and fluid pressure\nvoid FETiedFluidInterface::ProjectSurface(FETiedFluidSurface& ss, FETiedFluidSurface& ms)\n{\n    FEMesh& mesh = GetMesh();\n    FESurfaceElement* pme;\n    vec3d r;\n    \n    vec3d  vt[FEElement::MAX_NODES], v1;\n    double ps[FEElement::MAX_NODES], p1;\n    \n    // loop over all integration points\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        \n        int ne = el.Nodes();\n        int nint = el.GaussPoints();\n        \n        // get the nodal velocities and pressures\n        for (int j=0; j<ne; ++j) {\n            vt[j] = mesh.Node(el.m_node[j]).get_vec3d(m_dofWE[0], m_dofWE[1], m_dofWE[2]);\n            ps[j] = m_pfluid->Pressure(mesh.Node(el.m_node[j]).get(m_dofWE[3]),0);\n        }\n\n        for (int j=0; j<nint; ++j)\n        {\n\t\t\tFETiedFluidSurface::Data& pt = static_cast<FETiedFluidSurface::Data&>(*el.GetMaterialPoint(j));\n\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // get the velocity and pressure at the integration point\n            v1 = el.eval(vt, j);\n            p1 = el.eval(ps, j);\n            \n            // if this node is tied, evaluate gap functions\n            pme = pt.m_pme;\n            if (pme)\n            {\n                // calculate the tangential velocity gap function\n                vec3d vm[FEElement::MAX_NODES];\n                for (int k=0; k<pme->Nodes(); ++k) vm[k] = mesh.Node(pme->m_node[k]).get_vec3d(m_dofWE[0], m_dofWE[1], m_dofWE[2]);\n                vec3d v2 = pme->eval(vm, pt.m_rs[0], pt.m_rs[1]);\n                pt.m_vg = v2 - v1;\n\n                // calculate the pressure gap function\n                double pm[FEElement::MAX_NODES];\n                for (int k=0; k<pme->Nodes(); ++k) pm[k] = m_pfluid->Pressure(mesh.Node(pme->m_node[k]).get(m_dofWE[3]),0);\n                double p2 = pme->eval(pm, pt.m_rs[0], pt.m_rs[1]);\n                pt.m_pg = p1 - p2;\n            }\n            else\n            {\n                // the node is not tied\n                pt.m_vg = vec3d(0,0,0);\n                pt.m_pg = 0;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FETiedFluidInterface::Update()\n{\n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    ProjectSurface(m_ss, m_ms);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss);\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    int i, j, k;\n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    double N[8*MN];\n    \n    // loop over the nr of passes\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        // get primary and secondary surfaces\n        FETiedFluidSurface& ss = (np == 0? m_ss : m_ms);\n        FETiedFluidSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all elements of primary surface\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get the surface element\n            FESurfaceElement& se = ss.Element(i);\n            \n            // get the nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // copy the LM vector; we'll need it later\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n            }\n            \n            // loop over all integration points\n            // note that we are integrating over the current surface\n            for (j=0; j<nint; ++j)\n            {\n\t\t\t\tFETiedFluidSurface::Data& pt = static_cast<FETiedFluidSurface::Data&>(*se.GetMaterialPoint(j));\n\n                // get the secondary surface element\n                FESurfaceElement* pme = pt.m_pme;\n                if (pme)\n                {\n                    // get the secondary surface element\n                    FESurfaceElement& me = *pme;\n                    \n                    // get the nr of secondary element nodes\n                    int nmeln = me.Nodes();\n                    \n                    // copy LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    // calculate degrees of freedom\n                    int ndof = 3*(nseln + nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    for (k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[4*k  ];\n                        LM[3*k+1] = sLM[4*k+1];\n                        LM[3*k+2] = sLM[4*k+2];\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[4*k  ];\n                        LM[3*(k+nseln)+1] = mLM[4*k+1];\n                        LM[3*(k+nseln)+2] = mLM[4*k+2];\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // get primary element shape functions\n                    Hs = se.H(j);\n                    \n                    // get secondary element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // gap function\n                    vec3d dg = pt.m_vg;\n                    \n                    // lagrange multiplier\n                    vec3d Lm = pt.m_Lmd;\n                    \n                    // penalty\n                    double eps = m_epst*pt.m_epst;\n                    \n                    // viscous traction\n                    vec3d tv = Lm + dg*eps;\n                    pt.m_tv = tv;\n                    \n                    // calculate the force vector\n                    fe.resize(ndof);\n                    zero(fe);\n                    \n                    for (k=0; k<nseln; ++k)\n                    {\n                        N[3*k  ] = Hs[k]*tv.x;\n                        N[3*k+1] = Hs[k]*tv.y;\n                        N[3*k+2] = Hs[k]*tv.z;\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        N[3*(k+nseln)  ] = -Hm[k]*tv.x;\n                        N[3*(k+nseln)+1] = -Hm[k]*tv.y;\n                        N[3*(k+nseln)+2] = -Hm[k]*tv.z;\n                    }\n                    \n                    for (k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n                    \n                    // assemble the global residual\n                    R.Assemble(en, LM, fe);\n                    \n                    // do the pressure stuff\n                    // calculate nr of pressure dofs\n                    ndof = nseln + nmeln;\n                    \n                    // calculate the flow rate\n                    double epsn = m_epsn*pt.m_epsn;\n                    \n                    double vn = pt.m_Lmp + epsn*pt.m_pg;\n                    pt.m_vn = vn;\n                    \n                    // fill the LM\n                    LM.resize(ndof);\n                    for (k=0; k<nseln; ++k) LM[k        ] = sLM[4*k+3];\n                    for (k=0; k<nmeln; ++k) LM[k + nseln] = mLM[4*k+3];\n                    \n                    // fill the force array\n                    fe.resize(ndof);\n                    zero(fe);\n                    for (k=0; k<nseln; ++k) N[k      ] = -Hs[k];\n                    for (k=0; k<nmeln; ++k) N[k+nseln] =  Hm[k];\n                    \n                    for (k=0; k<ndof; ++k) fe[k] += vn*N[k]*detJ[j]*w[j];\n\n                    // assemble residual\n                    R.Assemble(en, LM, fe);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    int i, j, k, l;\n    vector<int> sLM, mLM, LM, en;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN], pt[MN], dpr[MN], dps[MN];\n    FEElementMatrix ke;\n    \n    // do single- or two-pass\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np < npass; ++np)\n    {\n\t\t// get primary and secondary surfaces\n\t\tFETiedFluidSurface& ss = (np == 0? m_ss : m_ms);\n        FETiedFluidSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all elements of primary surface\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get the next element\n            FESurfaceElement& se = ss.Element(i);\n            FEElement* sse = se.m_elem[0].pe;\n\n            // get nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // nodal velocities and pressures\n            vec3d vt[FEElement::MAX_NODES];\n            double pn[FEElement::MAX_NODES];\n            for (j=0; j<nseln; ++j) {\n                vt[j] = ss.GetMesh()->Node(se.m_node[j]).get_vec3d(m_dofWE[0], m_dofWE[1], m_dofWE[2]);\n                pn[j] = m_pfluid->Pressure(ss.GetMesh()->Node(se.m_node[j]).get(m_dofWE[3]),0);\n            }\n            \n            // copy the LM vector\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n                \n                // pressure\n                pt[j] = se.eval(pn, j);\n                dpr[j] = se.eval_deriv1(pn, j);\n                dps[j] = se.eval_deriv2(pn, j);\n            }\n            \n            // loop over all integration points\n            for (j=0; j<nint; ++j)\n            {\n\t\t\t\tFETiedFluidSurface::Data& pt = static_cast<FETiedFluidSurface::Data&>(*se.GetMaterialPoint(j));\n\n                // get the secondary element\n                FESurfaceElement* pme = pt.m_pme;\n                if (pme)\n                {\n                    FESurfaceElement& me = *pme;\n                    \n                    // get the nr of secondary nodes\n                    int nmeln = me.Nodes();\n                    \n                    // nodal pressure\n                    vec3d vm[FEElement::MAX_NODES];\n                    double pm[FEElement::MAX_NODES];\n                    for (k=0; k<nmeln; ++k) {\n                        vm[k] = ms.GetMesh()->Node(me.m_node[k]).get_vec3d(m_dofWE[0], m_dofWE[1], m_dofWE[2]);\n                        pm[k] = m_pfluid->Pressure(ms.GetMesh()->Node(me.m_node[k]).get(m_dofWE[3]),0);\n                    }\n                    \n                    // copy the LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    int ndpn;    // number of dofs per node\n                    int ndof;    // number of dofs in stiffness matrix\n                    \n                    // calculate degrees of freedom for fluid-on-fluid contact\n                    ndpn = 4;\n                    ndof = ndpn*(nseln+nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    \n                    for (k=0; k<nseln; ++k)\n                    {\n                        LM[4*k  ] = sLM[4*k  ];             // wx-dof\n                        LM[4*k+1] = sLM[4*k+1];             // wy-dof\n                        LM[4*k+2] = sLM[4*k+2];             // wz-dof\n                        LM[4*k+3] = sLM[4*k+3];             // ef-dof\n                    }\n                    for (k=0; k<nmeln; ++k)\n                    {\n                        LM[4*(k+nseln)  ] = mLM[4*k  ];     // wx-dof\n                        LM[4*(k+nseln)+1] = mLM[4*k+1];     // wy-dof\n                        LM[4*(k+nseln)+2] = mLM[4*k+2];     // wz-dof\n                        LM[4*(k+nseln)+3] = mLM[4*k+3];     // ef-dof\n                    }\n\n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // primary element shape functions\n                    Hs = se.H(j);\n                    \n                    // secondary element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // penalty\n                    double eps = m_epst*pt.m_epst;\n                    \n                    // create the stiffness matrix\n                    ke.resize(ndof, ndof); ke.zero();\n                    \n                    // a. K-term\n                    //------------------------------------\n                    \n                    for (k=0; k<nseln; ++k) {\n                        for (l=0; l<nseln; ++l)\n                        {\n                            double K = eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n                            ke[ndpn*k    ][ndpn*l    ] += K;\n                            ke[ndpn*k + 1][ndpn*l + 1] += K;\n                            ke[ndpn*k + 2][ndpn*l + 2] += K;\n                            \n                        }\n                        for (l=0; l<nmeln; ++l)\n                        {\n                            double K = -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n                            ke[ndpn*k    ][ndpn*(nseln+l)    ] += K;\n                            ke[ndpn*k + 1][ndpn*(nseln+l) + 1] += K;\n                            ke[ndpn*k + 2][ndpn*(nseln+l) + 2] += K;\n                        }\n                    }\n                    \n                    for (k=0; k<nmeln; ++k) {\n                        for (l=0; l<nseln; ++l)\n                        {\n                            double K = -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n                            ke[ndpn*(nseln+k)    ][ndpn*l    ] += K;\n                            ke[ndpn*(nseln+k) + 1][ndpn*l + 1] += K;\n                            ke[ndpn*(nseln+k) + 2][ndpn*l + 2] += K;\n                        }\n                        for (l=0; l<nmeln; ++l)\n                        {\n                            double K = eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n                            ke[ndpn*(nseln+k)    ][ndpn*(nseln+l)    ] += K;\n                            ke[ndpn*(nseln+k) + 1][ndpn*(nseln+l) + 1] += K;\n                            ke[ndpn*(nseln+k) + 2][ndpn*(nseln+l) + 2] += K;\n                        }\n                    }\n                    \n                    // --- D I L A T A T I O N   S T I F F N E S S ---\n                    {\n                        double epsn = m_epsn*pt.m_epsn;\n                        double K = m_pfluid->BulkModulus(*sse->GetMaterialPoint(0));\n                        \n                        for (k=0; k<nseln; ++k) {\n                            for (l=0; l<nseln; ++l)\n                                ke[4*k + 3][4*l+3] += -K*epsn*w[j]*detJ[j]*Hs[k]*Hs[l];\n                            for (l=0; l<nmeln; ++l)\n                                ke[4*k + 3][4*(nseln+l)+3] += K*epsn*w[j]*detJ[j]*Hs[k]*Hm[l];\n                        }\n                        \n                        for (k=0; k<nmeln; ++k) {\n                            for (l=0; l<nseln; ++l)\n                                ke[4*(nseln+k)+3][4*l + 3] += K*epsn*w[j]*detJ[j]*Hm[k]*Hs[l];\n                            for (l=0; l<nmeln; ++l)\n                                ke[4*(nseln+k)+3][4*(nseln+l) + 3] += -K*epsn*w[j]*detJ[j]*Hm[k]*Hm[l];\n                        }\n                        \n                    }\n\n                    // assemble the global stiffness\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\tLS.Assemble(ke);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedFluidInterface::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    int i;\n    vec3d Ln;\n    bool bconv = true;\n    \n    int NS = m_ss.Elements();\n    int NM = m_ms.Elements();\n    \n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0, normP0 = 0;\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& se = m_ss.Element(i);\n        for (int j=0; j<se.GaussPoints(); ++j)\n        {\n\t\t\tFETiedFluidSurface::Data& ds = static_cast<FETiedFluidSurface::Data&>(*se.GetMaterialPoint(j));\n\t\t\tnormL0 += ds.m_Lmd*ds.m_Lmd;\n            normP0 += ds.m_Lmp*ds.m_Lmp;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& me = m_ms.Element(i);\n        for (int j=0; j<me.GaussPoints(); ++j)\n        {\n\t\t\tFETiedFluidSurface::Data& dm = static_cast<FETiedFluidSurface::Data&>(*me.GetMaterialPoint(j));\n\t\t\tnormL0 += dm.m_Lmd*dm.m_Lmd;\n            normP0 += dm.m_Lmp*dm.m_Lmp;\n        }\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0;\n    double maxpg = 0;\n    \n    // update Lagrange multipliers\n    double normL1 = 0, normP1 = 0, eps, epsn;\n    for (i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tfor (int j = 0; j<se.GaussPoints(); ++j)\n\t\t{\n\t\t\tFETiedFluidSurface::Data& ds = static_cast<FETiedFluidSurface::Data&>(*se.GetMaterialPoint(j));\n\n            if (ds.m_pme) {\n                // update Lagrange multipliers on primary surface\n                eps = m_epst*ds.m_epst;\n                ds.m_Lmd = ds.m_Lmd + ds.m_vg*eps;\n                maxgap = max(maxgap,sqrt(ds.m_vg*ds.m_vg));\n                normL1 += ds.m_Lmd*ds.m_Lmd;\n                \n                epsn = m_epsn*ds.m_epsn;\n                ds.m_Lmp = ds.m_Lmp + epsn*ds.m_pg;\n                maxpg = max(maxpg,fabs(ds.m_pg));\n                normP1 += ds.m_Lmp*ds.m_Lmp;\n            }\n        }\n    }\n    \n    for (i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& me = m_ms.Element(i);\n\t\tfor (int j = 0; j<me.GaussPoints(); ++j)\n\t\t{\n\t\t\tFETiedFluidSurface::Data& dm = static_cast<FETiedFluidSurface::Data&>(*me.GetMaterialPoint(j));\n\n            if (dm.m_pme) {\n                // update Lagrange multipliers on secondary surface\n                eps = m_epst*dm.m_epst;\n                dm.m_Lmd = dm.m_Lmd + dm.m_vg*eps;\n                maxgap = max(maxgap,sqrt(dm.m_vg*dm.m_vg));\n                normL1 += dm.m_Lmd*dm.m_Lmd;\n                \n                epsn = m_epsn*dm.m_epsn;\n                dm.m_Lmp = dm.m_Lmp + epsn*dm.m_pg;\n                maxpg = max(maxpg,fabs(dm.m_pg));\n                normP1 += dm.m_Lmp*dm.m_Lmp;\n            }\n        }\n    }\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0));\n    double pnorm = (normP1 != 0 ? fabs((normP1 - normP0) / normP1) : fabs(normP1 - normP0));\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    if ((m_ptol > 0) && (maxpg > m_ptol)) bconv = false;\n    \n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    if ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" tied fluid interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    V multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n    feLog(\"    P multiplier        : %15le\", pnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n    \n    feLog(\"    maximum velocity gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n    feLog(\"    maximum pressure gap : %15le\", maxpg);\n    if (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedFluidInterface::Serialize(DumpStream &ar)\n{\n    // store contact data\n    FEContactInterface::Serialize(ar);\n    \n    // store contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n    \n\t// serialize element pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n    \n    if (ar.IsShallow()) return;\n    ar & m_pfluid;\n    ar & m_dofWE;\n}\n"
  },
  {
    "path": "FEBioFluid/FETiedFluidInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEContactInterface.h>\n#include <FEBioMech/FEContactSurface.h>\n#include \"FEFluidMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FETiedFluidSurface : public FEContactSurface\n{\npublic:\n    //! Integration point data\n    class Data : public FEContactMaterialPoint\n    {\n    public:\n        Data();\n\n\t\tvoid Serialize(DumpStream& ar) override;\n        \n    public:\n        vec3d   m_Gap;      //!< initial gap in reference configuration\n        vec3d   m_vg;       //!< tangential velocity gap function at integration points\n        vec3d   m_nu;       //!< normal at integration points\n        vec2d   m_rs;       //!< natural coordinates of projection of integration point\n        vec3d   m_Lmd;      //!< lagrange multipliers for tangential velocity\n        vec3d   m_tv;       //!< viscous tangential traction\n        double  m_Lmp;      //!< lagrange multipliers for fluid pressures\n        double  m_epst;     //!< viscous traction penalty factor\n        double  m_epsn;     //!< normal velocity penalty factor\n        double  m_pg;       //!< pressure \"gap\"\n        double  m_vn;       //!< normal fluid velocity gap\n    };\n    \n    //! constructor\n    FETiedFluidSurface(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Unpack surface element data\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n    \npublic:\n    void GetVelocityGap     (int nface, vec3d& vg);\n    void GetPressureGap     (int nface, double& pg);\n    void GetViscousTraction (int nface, vec3d& tv);\n    void GetNormalVelocity  (int nface, double& vn);\n\n   \npublic:\n\tFEDofList\tm_dofWE;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOFLUID_API FETiedFluidInterface :    public FEContactInterface\n{\npublic:\n    //! constructor\n    FETiedFluidInterface(FEModel* pfem);\n    \n    //! destructor\n    ~FETiedFluidInterface();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! interface activation\n    void Activate() override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! return the primary and secondary surfaces\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n    \n    //! return integration rule class\n    bool UseNodalIntegration() override { return false; }\n    \n    //! build the matrix profile for use in the stiffness matrix\n    void BuildMatrixProfile(FEGlobalMatrix& K) override;\n    \npublic:\n    //! calculate contact forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculate contact stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentations\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! update\n    void Update() override;\n    \nprotected:\n    void InitialProjection(FETiedFluidSurface& ss, FETiedFluidSurface& ms);\n    void ProjectSurface(FETiedFluidSurface& ss, FETiedFluidSurface& ms);\n    \n    //! calculate penalty factor\n    void CalcAutoPressurePenalty(FETiedFluidSurface& s);\n    double AutoPressurePenalty(FESurfaceElement& el, FETiedFluidSurface& s);\n    \npublic:\n\tFETiedFluidSurface    m_ss;    //!< primary surface\n\tFETiedFluidSurface    m_ms;    //!< secondary surface\n    \n    bool            m_btwo_pass;    //!< two-pass flag\n    double          m_atol;         //!< augmentation tolerance\n    double          m_gtol;         //!< gap tolerance\n    double          m_ptol;         //!< pressure gap tolerance\n    double          m_stol;         //!< search tolerance\n    double          m_srad;         //!< contact search radius\n    int             m_naugmax;      //!< maximum nr of augmentations\n    int             m_naugmin;      //!< minimum nr of augmentations\n    \n    double          m_epst;         //!< tangential viscous traction penalty factor\n    double          m_epsn;         //!< normal fluid velocity penalty factor\n    bool            m_bautopen;     //!< use autopenalty factor\n    \n    bool            m_bfreedofs;    //!< flag to free constrained/fixed DOFS on secondary surface\n    \n    FEFluidMaterial* m_pfluid;       //!< fluid pointer\n\n\tFEDofList\t\tm_dofWE;\n   \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/FEViscousFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEViscousFluid.h\"\n"
  },
  {
    "path": "FEBioFluid/FEViscousFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for the viscous part of the fluid response.\n//! These materials provide the viscous stress and its tangents.\n//!\nclass FEBIOFLUID_API FEViscousFluid : public FEMaterialProperty\n{\npublic:\n    FEViscousFluid(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    virtual ~FEViscousFluid() {}\n    \n    //! viscous stress\n    virtual mat3ds Stress(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of stress with respect to strain J\n    virtual mat3ds Tangent_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of stress with respect to rate of deformation tensor D\n    virtual tens4ds Tangent_RateOfDeformation(FEMaterialPoint& mp) = 0;\n\n    //! dynamic viscosity\n    virtual double ShearViscosity(FEMaterialPoint& mp) = 0;\n\n\t//! derivative of shear viscosity w.r.t. strain rate\n\tvirtual double Tangent_ShearViscosity_StrainRate(FEMaterialPoint& mp) { return 0.0; }\n    \n    //! bulk viscosity\n    virtual double BulkViscosity(FEMaterialPoint& mp) = 0;\n\n    FECORE_BASE_CLASS(FEViscousFluid)\n};\n"
  },
  {
    "path": "FEBioFluid/FEViscousPolarFluid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEViscousPolarFluid.h\"\n"
  },
  {
    "path": "FEBioFluid/FEViscousPolarFluid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include \"febiofluid_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for the viscous part of the fluid response.\n//! These materials provide the viscous stress and its tangents.\n//!\nclass FEBIOFLUID_API FEViscousPolarFluid : public FEMaterialProperty\n{\npublic:\n    FEViscousPolarFluid(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    virtual ~FEViscousPolarFluid() {}\n    \n    //! dual vector non-symmetric part of viscous stress in polar fluid\n    virtual vec3d SkewStressDualVector(FEMaterialPoint& pt) = 0;\n    \n    //! non-symmetric part of viscous stress in polar fluid\n    virtual mat3da SkewStress(FEMaterialPoint& pt) { return mat3da(SkewStressDualVector(pt)); }\n    \n    //! tangent of stress with respect to strain J\n    virtual mat3da SkewTangent_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of stress with respect to relative rate of rotation tensor H\n    virtual mat3d SkewTangent_RateOfRotation(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of stress with respect to temperature\n    virtual mat3da SkewTangent_Temperature(FEMaterialPoint& mp) = 0;\n    \n    //! viscous couple stress in polar fluid\n    virtual mat3d CoupleStress(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of viscous couple stress with respect to strain J\n    virtual mat3d CoupleTangent_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of viscous couple stress with respect to rate of rotation tensor g\n    virtual tens4d CoupleTangent_RateOfRotation(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of viscous couple stress with respect to temperature\n    virtual mat3d CoupleTangent_Temperature(FEMaterialPoint& mp) = 0;\n    \n    //! dynamic viscosity\n    virtual double RelativeRotationalViscosity(FEMaterialPoint& mp) = 0;\n\n    FECORE_BASE_CLASS(FEViscousPolarFluid)\n};\n"
  },
  {
    "path": "FEBioFluid/FEViscousPolarLinear.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEViscousPolarLinear.h\"\n#include \"FEFluidMaterialPoint.h\"\n#include \"FEPolarFluidMaterialPoint.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEViscousPolarLinear, FEViscousPolarFluid)\n    ADD_PARAMETER(m_tau, FE_RANGE_GREATER_OR_EQUAL(0.0), \"tau\");\n    ADD_PARAMETER(m_alpha, \"alpha\");\n    ADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta\");\n    ADD_PARAMETER(m_gamma, FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma\");\nEND_FECORE_CLASS();\n\n\n//! constructor\nFEViscousPolarLinear::FEViscousPolarLinear(FEModel* pfem) : FEViscousPolarFluid(pfem)\n{\n    m_tau = m_alpha = m_beta = m_gamma = 0;\n}\n\n//! dual vector of non-symmetric part of viscous stress in polar fluid\nvec3d FEViscousPolarLinear::SkewStressDualVector(FEMaterialPoint& mp)\n{\n    FEFluidMaterialPoint* pt = (mp.ExtractData<FEFluidMaterialPoint>());\n    FEPolarFluidMaterialPoint* pf = (mp.ExtractData<FEPolarFluidMaterialPoint>());\n    \n    vec3d h = pf ? pf->m_gf - pt->Vorticity()/2 : - pt->Vorticity()/2;\n    \n    return h*(-2*m_tau);\n}\n\n//! non-symmetric part of viscous stress in polar fluid\nmat3da FEViscousPolarLinear::SkewStress(FEMaterialPoint& mp)\n{\n    return mat3da(SkewStressDualVector(mp));\n}\n\n//! tangent of stress with respect to strain J\nmat3da FEViscousPolarLinear::SkewTangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3da(0,0,0);\n}\n\n//! tangent of stress with respect to relative rate of rotation tensor H\nmat3d FEViscousPolarLinear::SkewTangent_RateOfRotation(FEMaterialPoint& mp)\n{\n    return mat3dd(-2*m_tau);\n}\n\n//! viscous couple stress in polar fluid\nmat3d FEViscousPolarLinear::CoupleStress(FEMaterialPoint& mp)\n{\n    FEPolarFluidMaterialPoint* pf = (mp.ExtractData<FEPolarFluidMaterialPoint>());\n    mat3d Psi = pf->m_Psi;\n    mat3d M = mat3dd(Psi.trace()*m_alpha) + Psi*(m_beta+m_gamma) + Psi.transpose()*(m_beta - m_gamma);\n    \n    return M;\n}\n\n//! tangent of viscous couple stress with respect to strain J\nmat3d FEViscousPolarLinear::CoupleTangent_Strain(FEMaterialPoint& mp)\n{\n    return mat3d(0.);\n}\n\n//! tangent of viscous couple stress with respect to rate of rotation tensor g (left-minor transpose of...)\ntens4d FEViscousPolarLinear::CoupleTangent_RateOfRotation(FEMaterialPoint& mp)\n{\n    mat3dd I(1);\n    tens4d m = dyad1(I,I)*m_alpha + dyad2(I,I)*(m_beta-m_gamma) + dyad3(I,I)*(m_beta+m_gamma);\n    \n    return m;\n}\n\n//! dynamic viscosity\ndouble FEViscousPolarLinear::RelativeRotationalViscosity(FEMaterialPoint& mp)\n{\n    return m_tau;\n}\n\n"
  },
  {
    "path": "FEBioFluid/FEViscousPolarLinear.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscousPolarFluid.h\"\n\n//-----------------------------------------------------------------------------\n// This class evaluates the viscous stress in a linear polar fluid\n\nclass FEBIOFLUID_API FEViscousPolarLinear : public FEViscousPolarFluid\n{\npublic:\n    //! constructor\n    FEViscousPolarLinear(FEModel* pfem);\n    \n    //! dual vector non-symmetric part of viscous stress in polar fluid\n    vec3d SkewStressDualVector(FEMaterialPoint& pt) override;\n    \n    //! non-symmetric part of viscous stress in polar fluid\n    mat3da SkewStress(FEMaterialPoint& pt) override;\n    \n    //! tangent of stress with respect to strain J\n    mat3da SkewTangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to relative rate of rotation tensor H\n    mat3d SkewTangent_RateOfRotation(FEMaterialPoint& mp) override;\n    \n    //! tangent of stress with respect to temperature\n    mat3da SkewTangent_Temperature(FEMaterialPoint& mp) override { return mat3da(0,0,0); }\n    \n    //! viscous couple stress in polar fluid\n    mat3d CoupleStress(FEMaterialPoint& pt) override;\n    \n    //! tangent of viscous couple stress with respect to strain J\n    mat3d CoupleTangent_Strain(FEMaterialPoint& mp) override;\n    \n    //! tangent of viscous couple stress with respect to rate of rotation tensor g\n    tens4d CoupleTangent_RateOfRotation(FEMaterialPoint& mp) override;\n    \n    //! tangent of viscous couple stress with respect to temperature\n    mat3d CoupleTangent_Temperature(FEMaterialPoint& mp) override { return mat3d(0.); }\n    \n    //! dynamic viscosity\n    double RelativeRotationalViscosity(FEMaterialPoint& mp) override;\n    \npublic:\n    double    m_tau;    //!< relative rotational viscosity\n    double    m_alpha;  //!< couple stress viscosity\n    double    m_beta;   //!< couple stress viscosity\n    double    m_gamma;  //!< couple stress viscosity\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioFluid/febiofluid_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febiofluid_EXPORTS\n\t\t\t#define FEBIOFLUID_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOFLUID_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIOFLUID_API\n\t#endif\n#else\n\t#define FEBIOFLUID_API\n#endif\n"
  },
  {
    "path": "FEBioFluid/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <stdlib.h>\n"
  },
  {
    "path": "FEBioLib/FEBioConfig.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBioConfig.h\"\n\nFEBioConfig::FEBioConfig()\n{\n\tm_noutput = 1;\n\tDefaults();\n}\n\nvoid FEBioConfig::SetOutputLevel(int n)\n{\n\tm_noutput = n;\n}\n\nvoid FEBioConfig::Defaults()\n{\n\tm_printParams = -1;\n\tm_bshowErrors = true;\n\treadPlugins = true;\n}\n"
  },
  {
    "path": "FEBioLib/FEBioConfig.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febiolib_api.h\"\n\nclass FEBIOLIB_API FEBioConfig\n{\npublic:\n\tFEBioConfig();\n\n\tvoid Defaults();\n\n\tvoid SetOutputLevel(int n);\n\npublic: // input parameters\n\tbool readPlugins;\n\npublic: // output parameters (set by reading the config file)\n\tint\t\tm_printParams;\n\tint\t\tm_noutput;\n\tbool\tm_bshowErrors;\n};\n"
  },
  {
    "path": "FEBioLib/FEBioModel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioModel.h\"\n#include \"FEBioPlot/FEBioPlotFile.h\"\n#include \"FEBioPlot/VTKPlotFile.h\"\n#include \"FEBioXML/FEBioImport.h\"\n#include \"FEBioXML/FERestartImport.h\"\n#include <FECore/NodeDataRecord.h>\n#include <FECore/FaceDataRecord.h>\n#include <FECore/ElementDataRecord.h>\n#include <FECore/SurfaceDataRecord.h>\n#include <FECore/DomainDataRecord.h>\n#include <FECore/FEModelDataRecord.h>\n#include <FEBioMech/ObjectDataRecord.h>\n#include <FECore/NLConstraintDataRecord.h>\n#include <FEBioMech/FERigidConnector.h>\n#include <FEBioMech/FEGenericRigidJoint.h>\n#include <FEBioMech/FERigidSphericalJoint.h>\n#include <FEBioMech/FERigidPrismaticJoint.h>\n#include <FEBioMech/FERigidRevoluteJoint.h>\n#include <FEBioMech/FERigidCylindricalJoint.h>\n#include <FEBioMech/FERigidPlanarJoint.h>\n#include <FEBioMech/FERigidDamper.h>\n#include <FEBioMech/FERigidSpring.h>\n#include <FEBioMech/FERigidAngularDamper.h>\n#include <FEBioMech/FERigidContractileForce.h>\n#include \"FEBioModelBuilder.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FECore/DumpFile.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEAnalysis.h>\n#include <NumCore/MatrixTools.h>\n#include <FECore/LinearSolver.h>\n#include <FECore/FEDomain.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEPlotDataStore.h>\n#include <FECore/FETimeStepController.h>\n#include \"febio.h\"\n#include \"version.h\"\n#include <iostream>\n#include <sstream>\n#include <fstream>\n#include <functional>\n\n#ifdef WIN32\nsize_t FEBIOLIB_API GetPeakMemory();\t// in memory.cpp\n#endif\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEBioModel, FEMechModel)\n\tADD_PARAMETER(m_title   , \"title\"    );\n\tADD_PARAMETER(m_logLevel, \"log_level\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// echo the input data to the log file\nextern void echo_input(FEBioModel& fem);\n\nbool FEBioModel::handleCB(FEModel* fem, int unsigned nwhen, void* pd)\n{\n\tFEBioModel* febioModel = (FEBioModel*)pd;\n\treturn febioModel->processEvent(nwhen);\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioModel::processEvent(int nevent)\n{\n\t// write output files (but not while serializing)\n\tif ((nevent == CB_SERIALIZE_LOAD) || (nevent == CB_SERIALIZE_SAVE)) return true;\n\tWrite(nevent);\n\n\t// process event handlers\n\tswitch (nevent)\n\t{\n\tcase CB_STEP_SOLVED: on_cb_stepSolved(); break;\n\tcase CB_SOLVED     : on_cb_solved(); break;\n\tcase CB_MAJOR_ITERS:\n\tcase CB_TIMESTEP_FAILED:\n\t{\n\t\tFEAnalysis* step = GetCurrentStep();\n\t\tFESolver* solver = step->GetFESolver();\n\t\tTimeStepStats stats = {solver->m_niter, solver->m_nrhs, solver->m_nref, (nevent== CB_MAJOR_ITERS?1:0)};\n\t\tm_timestepStats.push_back(stats);\n\t}\n\tbreak;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Constructor of FEBioModel class.\nFEBioModel::FEBioModel()\n{\n\tm_logLevel = 1;\n\n\tm_dumpLevel = FE_DUMP_NEVER;\n\tm_dumpStride = 1;\n\n\t// --- I/O-Data ---\n\tm_ndebug = 0;\n\tm_becho = true;\n\tm_plot = nullptr;\n\tm_writeMesh = false;\n\tm_createReport = false;\n\n\tm_pltAppendOnRestart = true;\n\n\tm_lastUpdate = -1;\n\n\tm_bshowErrors = true;\n\n\t// Add the output callback\n\t// We call this function always since we want to flush the logfile for each event.\n\tAddCallback(handleCB, CB_ALWAYS, this);\n}\n\n//-----------------------------------------------------------------------------\nFEBioModel::~FEBioModel()\n{\n\t// close the plot file\n\tif (m_plot) { delete m_plot; m_plot = 0; }\n\tm_log.close();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::ShowWarningsAndErrors(bool b)\n{\n\tm_bshowErrors = b;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioModel::ShowWarningsAndErrors() const\n{\n\treturn m_bshowErrors;\n}\n\n//-----------------------------------------------------------------------------\nTimer& FEBioModel::GetSolveTimer()\n{\n\treturn *GetTimer(Timer_ModelSolve);\n}\n\n//-----------------------------------------------------------------------------\n//! set the debug level\nvoid FEBioModel::SetDebugLevel(int debugLvl) { m_ndebug = debugLvl; }\n\n//! get the debug level\nint FEBioModel::GetDebugLevel() { return m_ndebug; }\n\n//! set the dump level (for cold restarts)\nvoid FEBioModel::SetDumpLevel(int dumpLevel) { m_dumpLevel = dumpLevel; }\n\n//! get the dump level\nint FEBioModel::GetDumpLevel() const { return m_dumpLevel; }\n\n//! Set the dump stride\nvoid FEBioModel::SetDumpStride(int n) { m_dumpStride = n; }\n\n//! get the dump stride\nint FEBioModel::GetDumpStride() const { return m_dumpStride; }\n\n//! Set the log level\nvoid FEBioModel::SetLogLevel(int logLevel) { m_logLevel = logLevel; }\n\n//! Get the stats \nModelStats FEBioModel::GetModelStats() const\n{\n\treturn m_modelStats;\n}\n\nModelStats FEBioModel::GetStepStats(size_t n) const\n{\n\treturn m_stepStats[n];\n}\n\nstd::vector<ModelStats> FEBioModel::GetStepStats() const\n{\n\treturn m_stepStats;\n}\n\nstd::vector<TimeStepStats> FEBioModel::GetTimeStepStats() const\n{\n\treturn m_timestepStats;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the title of the model\nvoid FEBioModel::SetTitle(const char* sz)\n{\n\tm_title = sz;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the title of the model\nconst std::string& FEBioModel::GetTitle() const\n{\n\treturn m_title;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEBioModel::GetEndTime() const\n{\n\treturn GetCurrentStep()->m_tend;\n}\n\n//=============================================================================\n//\n//\t\tFEBioModel: I-O Functions\n//\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Add a data record to the data store\nvoid FEBioModel::AddDataRecord(DataRecord* pd)\n{\n\tDataStore& dataStore = GetDataStore();\n\tdataStore.AddRecord(pd); \n}\n\n//-----------------------------------------------------------------------------\n//! Get the plot file\nPlotFile* FEBioModel::GetPlotFile()\n{\n\treturn m_plot;\n}\n\n//-----------------------------------------------------------------------------\n//! Sets the name of the FEBio input file\nvoid FEBioModel::SetInputFilename(const std::string& sfile)\n{ \n\tm_sfile = sfile;\n\tsize_t npos = sfile.rfind('/');\n\tif (npos != string::npos) m_sfile_title = sfile.substr(npos+1, std::string::npos);\n\tif (m_sfile_title.empty()) \n\t{\n\t\tnpos = sfile.rfind('\\\\');\n\t\tif (npos != string::npos) m_sfile_title = sfile.substr(npos, string::npos);\n\t\tif (m_sfile_title.empty()) m_sfile_title = m_sfile;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Set the name of the log file\nvoid FEBioModel::SetLogFilename(const std::string& sfile)\n{ \n\tm_slog = sfile; \n}\n\n//-----------------------------------------------------------------------------\n//! Set the name of the plot file\nvoid FEBioModel::SetPlotFilename(const std::string& sfile)\n{ \n\tm_splot = sfile;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the name of the restart archive (i.e. the dump file)\nvoid FEBioModel::SetDumpFilename(const std::string& sfile)\n{ \n\tm_sdump = sfile;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the name of the input file\nconst std::string& FEBioModel::GetInputFileName()\n{ \n\treturn m_sfile; \n}\n\n//-----------------------------------------------------------------------------\n//! Return the name of the log file\nconst std::string& FEBioModel::GetLogfileName()\n{ \n\treturn m_slog;  \n}\n\n//-----------------------------------------------------------------------------\n//! Return the name of the plot file\nconst std::string& FEBioModel::GetPlotFileName()\n{ \n\treturn m_splot; \n}\n\n//-----------------------------------------------------------------------------\n//! Return the dump file name.\nconst std::string& FEBioModel::GetDumpFileName()\n{\n\treturn\tm_sdump;\n}\n\n//-----------------------------------------------------------------------------\n//! get the file title (i.e. name of input file without the path)\nconst std::string& FEBioModel::GetFileTitle()\n{ \n\treturn m_sfile_title; \n}\n\n//-----------------------------------------------------------------------------\n// set append-on-restart flag\nvoid FEBioModel::SetAppendOnRestart(bool b)\n{\n\tm_pltAppendOnRestart = b;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioModel::AppendOnRestart() const\n{\n\treturn m_pltAppendOnRestart;\n}\n\n//=============================================================================\n//    I N P U T\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! This routine reads in an input file and performs some initialization stuff.\n//! The rest of the initialization is done in Init\n\nbool FEBioModel::Input(const char* szfile)\n{\n\t// start the total timer (assumes that this is the first function we'll hit)\n\tm_TotalTime.start();\n\n\t// start the timer\n\tTimerTracker t(&m_InputTime);\n\n\t// create file reader\n\tFEBioImport fim;\n\n\t// override the default model builder\n\tfim.SetModelBuilder(new FEBioModelBuilder(*this));\n\n\tfeLog(\"Reading file %s ...\", szfile);\n\n\t// Load the file\n\tif (fim.Load(*this, szfile) == false)\n\t{\n\t\tfeLog(\"FAILED!\\n\");\n\t\tchar szerr[256];\n\t\tfim.GetErrorMessage(szerr);\n\t\tfeLogError(szerr);\n\n\t\treturn false;\n\t}\n\telse feLog(\"SUCCESS!\\n\");\n\n\t// set the input file name\n\tSetInputFilename(szfile);\n\n\t// see if user redefined output filenames\n\tif (fim.m_szdmp[0]) SetDumpFilename(fim.m_szdmp);\n\tif (fim.m_szlog[0]) SetLogFilename (fim.m_szlog);\n\tif (fim.m_szplt[0]) SetPlotFilename(fim.m_szplt);\n\n\t// add the data records\n\tint ND = (int)fim.m_data.size();\n\tfor (int i=0; i<ND; ++i) AddDataRecord(fim.m_data[i]);\n\n\t// we're done reading\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function finds all the domains that have a certain material\nvoid FEBioModel::DomainListFromMaterial(vector<int>& lmat, vector<int>& ldom)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// make sure the list is empty\n\tif (ldom.empty() == false) ldom.clear();\n\n\t// loop over all domains\n\tint ND = mesh.Domains();\n\tint NM = (int)lmat.size();\n\tfor (int i = 0; i<ND; ++i)\n\t{\n\t\tFEDomain& di = mesh.Domain(i);\n\t\tint dmat = di.GetMaterial()->GetID();\n\t\tfor (int j = 0; j<NM; ++j)\n\t\t{\n\t\t\tif (dmat == lmat[j])\n\t\t\t{\n\t\t\t\tldom.push_back(i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//=============================================================================\n//    O U T P U T\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Export state to plot file.\nvoid FEBioModel::Write(unsigned int nevent)\n{\n\tTimerTracker t(&m_IOTimer);\n\n\t// get the current step\n\tFEAnalysis* pstep = GetCurrentStep();\n\n\t// echo fem data to the logfile\n\t// we do this here (and not e.g. directly after input)\n\t// since the data can be changed after input, which is the case,\n\t// for instance, in the parameter optimization module\n\tif ((nevent == CB_INIT) && m_becho)\n\t{\n\t\tLogfile::MODE old_mode = m_log.GetMode();\n\n\t\t// don't output when no output is requested\n\t\tif (old_mode != Logfile::LOG_NEVER)\n\t\t{\n\t\t\t// we only output this data to the log file and not the screen\n\t\t\tm_log.SetMode(Logfile::LOG_FILE);\n\n\t\t\t// write output\n\t\t\techo_input();\n\n\t\t\t// reset log mode\n\t\t\tm_log.SetMode(old_mode);\n\t\t}\n\t}\n\n\t// update plot file\n\tWritePlot(nevent);\n\n\t// Dump converged state to the archive\n\tDumpData(nevent);\n\n\t// write the output data\n\tWriteData(nevent);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::WritePlot(unsigned int nevent)\n{\n\t// get the current step\n\tFEAnalysis* pstep = GetCurrentStep();\n\n\t// get the plot level\n\tint nplt = pstep->GetPlotLevel();\n\n\t// if we don't want to plot anything we return\n\tif (nplt != FE_PLOT_NEVER)\n\t{\n\t\t// try to open the plot file\n\t\tif ((nevent == CB_INIT) || (nevent == CB_STEP_ACTIVE))\n\t\t{\n\t\t\t// If the first step did not request output, m_plot can still be null\n\t\t\tif (m_plot == nullptr)\n\t\t\t{\n\t\t\t\tif (InitPlotFile() == false)\n\t\t\t\t{\n\t\t\t\t\tfeLogError(\"Failed to initialize plot file.\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (m_plot->IsValid() == false)\n\t\t\t{\n\t\t\t\t// Add the plot objects\n\t\t\t\tUpdatePlotObjects();\n\n\t\t\t\tif (m_plot->Open(m_splot.c_str()) == false)\n\t\t\t\t{\n\t\t\t\t\tfeLog(\"ERROR : Failed creating PLOT database\\n\");\n\t\t\t\t\tdelete m_plot;\n\t\t\t\t\tm_plot = 0;\n\t\t\t\t}\n\n\t\t\t\t// Since it is assumed that for the first timestep\n\t\t\t\t// there are no loads or initial displacements, the case n=0 is skipped.\n\t\t\t\t// Therefor we can output those results here.\n\t\t\t\t// TODO: Offcourse we should actually check if this is indeed\n\t\t\t\t//       the case, otherwise we should also solve for t=0\n\t\t\t\t// Only output the initial state if requested\n\t\t\t\tif (m_plot)\n\t\t\t\t{\n\t\t\t\t\tbool bout = true;\n\n\t\t\t\t\t// if we're using the fixed time stepper, we check the plot range and zero state flag\n\t\t\t\t\tif (pstep->m_timeController == nullptr) bout = (pstep->m_nplotRange[0] == 0) || (pstep->m_bplotZero);\n\n\t\t\t\t\t// for multi-step analyses, some plot variables can't be plot until the first step\n\t\t\t\t\t// is activated. So, in that case, we'll wait. \n\t\t\t\t\tif ((nevent == CB_INIT) && (Steps() > 1)) bout = false;\n\n\t\t\t\t\t// store initial time step (i.e. time step zero)\n\t\t\t\t\tif (bout)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble time = GetTime().currentTime;\n\t\t\t\t\t\tm_plot->Write((float)time);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// for multi-step analyses, we did not write the initial time step during CB_INIT\n\t\t\t\t// so we'll do it during the activation of the first step. \n\t\t\t\tif ((nevent == CB_STEP_ACTIVE) && (Steps() > 1) && (GetCurrentStepIndex() == 0))\n\t\t\t\t{\n\t\t\t\t\tdouble time = GetTime().currentTime;\n\t\t\t\t\tm_plot->Write((float)time);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// assume we won't be writing anything\n\t\t\tbool bout = false;\n\n\t\t\t// see if we need to output something\n\t\t\tint ndebug = GetDebugLevel();\n\n\t\t\t// write a new mesh section if needed\n\t\t\tif (nevent == CB_REMESH)\n\t\t\t{\n\t\t\t\tm_writeMesh = true;\n\t\t\t\tm_lastUpdate = -1;\n\t\t\t}\n\n\t\t\tif (ndebug == 1)\n\t\t\t{\n\t\t\t\tif ((nevent == CB_INIT) || (nevent == CB_MODEL_UPDATE) || (nevent == CB_MINOR_ITERS) || (nevent == CB_SOLVED) || (nevent == CB_REMESH) || (nevent == CB_TIMESTEP_FAILED))\n\t\t\t\t{\n\t\t\t\t\tbout = true;\n\t\t\t\t}\n\n\t\t\t\tif (nevent == CB_MAJOR_ITERS)\n\t\t\t\t{\n\t\t\t\t\tbout = true;\n\t\t\t\t\tm_lastUpdate = -1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint currentStep = pstep->m_ntimesteps;\n\t\t\t\tint lastStep = pstep->m_ntime;\n\t\t\t\tint nmin = pstep->m_nplotRange[0]; if (nmin < 0) nmin = lastStep + nmin + 1;\n\t\t\t\tint nmax = pstep->m_nplotRange[1]; if (nmax < -1) nmax = lastStep + nmax + 1;\n\n\t\t\t\tbool inRange = true;\n\t\t\t\tbool isStride = true;\n\t\t\t\tif (pstep->m_timeController == nullptr)\n\t\t\t\t{\n\t\t\t\t\tinRange = false;\n\t\t\t\t\tif ((currentStep >= nmin) && ((currentStep <= nmax) || (nmax == -1))) inRange = true;\n\n\t\t\t\t}\n\t\t\t\tisStride = ((pstep->m_ntimesteps - nmin) % pstep->m_nplot_stride) == 0;\n\n\t\t\t\tbool isMustPoint = (pstep->m_timeController && (pstep->m_timeController->m_nmust >= 0));\n\n\t\t\t\tswitch (nevent)\n\t\t\t\t{\n\t\t\t\tcase CB_MINOR_ITERS:\n\t\t\t\t{\n\t\t\t\t\tif (nplt == FE_PLOT_MINOR_ITRS) bout = true;\n\t\t\t\t\tif ((ndebug == 2) && (NegativeJacobian::IsThrown()))\n\t\t\t\t\t{\n\t\t\t\t\t\tbout = true;\n\t\t\t\t\t\tNegativeJacobian::clearFlag();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t\tcase CB_MAJOR_ITERS:\n\t\t\t\t\tif ((nplt == FE_PLOT_MAJOR_ITRS) && inRange && (isStride || isMustPoint)) bout = true;\n\t\t\t\t\tif ((nplt == FE_PLOT_MUST_POINTS) && isMustPoint) bout = true;\n\t\t\t\t\tif (nplt == FE_PLOT_AUGMENTATIONS) bout = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase CB_AUGMENT:\n\t\t\t\t\tif (nplt == FE_PLOT_AUGMENTATIONS)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Note that this is called before the augmentations.\n\t\t\t\t\t\t// The reason we store the state prior to the augmentations\n\t\t\t\t\t\t// is because the augmentations are going to change things such that\n\t\t\t\t\t\t// the system no longer in equilibrium. Since the model has to be converged\n\t\t\t\t\t\t// before we do augmentations, storing the model now will store an actual converged state.\n\t\t\t\t\t\tbout = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CB_SOLVED:\n\t\t\t\t\tif (nplt == FE_PLOT_FINAL) bout = true;\n\t\t\t\t\tif (nplt == FE_PLOT_MAJOR_ITRS)\n\t\t\t\t\t{\n\t\t\t\t\t\t// we want to force storing the final time step, but we have to make sure \n\t\t\t\t\t\t// it hasn't been stored already during the CB_MAJOR_ITERS callback.\n\t\t\t\t\t\tif ((inRange == false) || (isStride == false)) bout = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase CB_STEP_SOLVED: if (nplt == FE_PLOT_STEP_FINAL) bout = true;  break;\n\t\t\t\tcase CB_USER1: if ((nplt == FE_PLOT_USER1) && inRange && isStride) bout = true; break;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// output the state if requested\n\t\t\tif (bout && (m_lastUpdate != UpdateCounter()))\n\t\t\t{\n\t\t\t\tm_lastUpdate = UpdateCounter();\n\n\t\t\t\t// update the plot objects\n\t\t\t\tUpdatePlotObjects();\n\n\t\t\t\t// see if we need to write a new mesh section\n\t\t\t\tif (m_writeMesh) {\n\t\t\t\t\tFEBioPlotFile* plt = dynamic_cast<FEBioPlotFile*>(m_plot);\n\t\t\t\t\tfeLogDebug(\"writing mesh section to plot file\");\n\t\t\t\t\tplt->WriteMeshSection(*this);\n\t\t\t\t}\n\n\t\t\t\t// set the status flag\n\t\t\t\tint statusFlag = 0;\n\t\t\t\tif (m_writeMesh) statusFlag = 1;\n\t\t\t\telse if (nevent != CB_MAJOR_ITERS)\n\t\t\t\t{\n\t\t\t\t\tstatusFlag = 2;\n\t\t\t\t}\n\n\t\t\t\t// write the state section\n\t\t\t\tdouble time = GetTime().currentTime;\n\t\t\t\tif (m_plot)\n\t\t\t\t{\n\t\t\t\t\tm_plot->Write((float)time, statusFlag);\n\t\t\t\t}\n\n\t\t\t\t// make sure to reset write mesh flag\n\t\t\t\tm_writeMesh = false;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Write user data to the logfile\nvoid FEBioModel::WriteData(unsigned int nevent)\n{\n\t// get the current step\n\tFEAnalysis* pstep = GetCurrentStep();\n\tint nout = pstep->GetOutputLevel();\n\tif (nout == FE_OUTPUT_NEVER) return;\n\n\t// see if we need to output\n\tbool bout = false;\n\tswitch (nevent)\n\t{\n\tcase CB_INIT: if (nout == FE_OUTPUT_MAJOR_ITRS) bout = true; break;\n\tcase CB_MINOR_ITERS: if (nout == FE_OUTPUT_MINOR_ITRS) bout = true; break;\n\tcase CB_MAJOR_ITERS:\n\t{\n\t\tif (nout == FE_OUTPUT_MAJOR_ITRS)\n\t\t{\n\t\t\tbout = ((pstep->m_ntimesteps % pstep->m_noutput_stride) == 0);\n\t\t}\n\t\tif ((nout == FE_OUTPUT_MUST_POINTS) && (pstep->m_timeController) && (pstep->m_timeController->m_nmust >= 0)) bout = true;\n\t}\n\tbreak;\n\tcase CB_SOLVED:\n\t\tif (nout == FE_OUTPUT_FINAL) bout = true;\n\n\t\t// make sure that the final solve data is output\n\t\tif (nout == FE_OUTPUT_MAJOR_ITRS)\n\t\t{\n\t\t\tbout = !((pstep->m_ntimesteps % pstep->m_noutput_stride) == 0);\n\t\t}\n\t\tbreak;\n\t}\n\n\t// output data\n\tif (bout)\n\t{\n\t\tDataStore& dataStore = GetDataStore();\n\t\tdataStore.Write();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Dump state to archive for restarts\nvoid FEBioModel::DumpData(int nevent)\n{\n\t// get the current step\n\tFEAnalysis* pstep = GetCurrentStep();\n\tint ndump = GetDumpLevel();\n\tint stride = GetDumpStride();\n\tif (ndump == FE_DUMP_NEVER) return;\n\n\tbool bdump = false;\n\tswitch (nevent)\n\t{\n\tcase CB_MAJOR_ITERS:\n\t\tif (ndump == FE_DUMP_MAJOR_ITRS)\n\t\t{\n\t\t\tif (stride <= 1) bdump = true;\n\t\t\telse\n\t\t\t{\n\t\t\t\tint niter = pstep->m_ntimesteps;\n\t\t\t\tbdump = ((niter % stride) == 0);\n\t\t\t}\n\t\t}\n\t\tif ((ndump == FE_DUMP_MUST_POINTS) && (pstep->m_timeController) && (pstep->m_timeController->m_nmust >= 0)) bdump = true;\n\t\tbreak;\n\tcase CB_STEP_SOLVED: if (ndump == FE_DUMP_STEP) bdump = true; break;\n\t}\n\t\n\tif (bdump)\n\t{\n\t\tDumpFile ar(*this);\n\t\tif (ar.Create(m_sdump.c_str()) == false)\n\t\t{\n\t\t\tfeLogWarning(\"Failed creating restart file (%s).\\n\", m_sdump.c_str());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSerialize(ar);\n\t\t\tfeLogInfo(\"\\nRestart point created. Archive name is %s.\", m_sdump.c_str());\n\t\t}\n\t}\n}\n\nstring removeNewLines(const char* sz)\n{\n\tstring tmp; tmp.reserve(128);\n\tconst char* c = sz;\n\twhile ((c != 0) && (*c != 0))\n\t{\n\t\tif ((*c != '\\n') && (*c != '\\r')) tmp.push_back(*c);\n\t\telse tmp.push_back(' ');\n\t\tc++;\n\t}\n\treturn tmp;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::Log(int ntag, const char* szmsg)\n{\n\tTimerTracker t(&m_IOTimer);\n\n\tif      (ntag == 0) m_log.printf(szmsg);\n\telse if ((ntag == 1) && m_bshowErrors) m_log.printbox(\"WARNING\", szmsg);\n\telse if ((ntag == 2) && m_bshowErrors) m_log.printbox(\"ERROR\", szmsg);\n\telse if (ntag == 3) m_log.printbox(nullptr, szmsg);\n\telse if (ntag == 4)\n\t{\n\t\tif (GetDebugLevel() > 0)\n\t\t\tm_log.printf(\"debug>%s\\n\", szmsg);\n\t}\n\n\tif (m_createReport)\n\t{\n\t\tstring msg = removeNewLines(szmsg);\n\n\t\tdouble t = GetCurrentTime();\n\t\tstringstream ss;\n\t\tss << msg << \" (t = \" << t << \")\\n\";\n\t\tmsg = ss.str();\n\n\t\tif (ntag == 1) m_report += \"Warning: \" + msg;\n\t\tif (ntag == 2) m_report += \"Error: \" + msg;\n\t}\n\n\t// Flushing the logfile each time we get here might be a bit overkill.\n\t// For now, I'm flushing the log file in the output_cb method.\n//\tm_log.flush();\n}\n\n//-----------------------------------------------------------------------------\nclass FEPlotRigidBodyData : public FEPlotObjectData\n{\npublic:\n\tFEPlotRigidBodyData(FEModel* fem, FERigidBody* rb, std::function<vec3d (const FERigidBody& rb)> f) : FEPlotObjectData(fem), m_rb(rb), m_f(f) {}\n\n\tbool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n\t{\n\t\tassert(m_rb);\n\t\tar << m_f(*m_rb);\n\t\treturn true;\n\t}\n\nprivate:\n\tFERigidBody* m_rb;\n\tstd::function<vec3d(const FERigidBody& rb)> m_f;\n};\n\nclass FEPlotRigidBodyPosition : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyPosition(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_rt; }) {}\n};\n\nclass FEPlotRigidBodyVelocity : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyVelocity(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_vt; }) {}\n};\n\nclass FEPlotRigidBodyAcceleration : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyAcceleration(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_at; }) {}\n};\n\nclass FEPlotRigidBodyAngularVelocity : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyAngularVelocity(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_wt; }) {}\n};\n\nclass FEPlotRigidBodyAngularAcceleration : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyAngularAcceleration(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_alt; }) {}\n};\n\nclass FEPlotRigidBodyEuler : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyEuler(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) {\n\t\tquatd q = rb.GetRotation();\n\t\tvec3d e;\n\t\tq.GetEuler(e.x, e.y, e.z);\n\t\te *= RAD2DEG;\n\t\treturn e;\n\t\t}) {}\n};\n\nclass FEPlotRigidBodyForce : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyForce(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_Fr; }) {}\n};\n\nclass FEPlotRigidBodyMoment : public FEPlotRigidBodyData\n{\npublic:\n\tFEPlotRigidBodyMoment(FEModel* fem, FERigidBody* prb) : FEPlotRigidBodyData(fem, prb, [](const FERigidBody& rb) { return rb.m_Mr; }) {}\n};\n\n\n//-----------------------------------------------------------------------------\nclass FEPlotRigidConnectorTranslationLCS : public FEPlotObjectData\n{\npublic:\n    FEPlotRigidConnectorTranslationLCS(FEModel* fem, FERigidConnector* prb) : FEPlotObjectData(fem), m_rc(prb) {}\n\n    bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n    {\n        assert(m_rc);\n        ar << m_rc->RelativeTranslation(false);\n        return true;\n    }\n\nprivate:\n    FERigidConnector* m_rc;\n};\n\nclass FEPlotRigidConnectorRotationLCS : public FEPlotObjectData\n{\npublic:\n    FEPlotRigidConnectorRotationLCS(FEModel* fem, FERigidConnector* prb) : FEPlotObjectData(fem), m_rc(prb) {}\n\n    bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n    {\n        assert(m_rc);\n        ar << m_rc->RelativeRotation(false);\n        return true;\n    }\n\nprivate:\n    FERigidConnector* m_rc;\n};\n\nclass FEPlotRigidConnectorTranslationGCS : public FEPlotObjectData\n{\npublic:\n    FEPlotRigidConnectorTranslationGCS(FEModel* fem, FERigidConnector* prb) : FEPlotObjectData(fem), m_rc(prb) {}\n\n    bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n    {\n        assert(m_rc);\n        ar << m_rc->RelativeTranslation(true);\n        return true;\n    }\n\nprivate:\n    FERigidConnector* m_rc;\n};\n\nclass FEPlotRigidConnectorRotationGCS : public FEPlotObjectData\n{\npublic:\n    FEPlotRigidConnectorRotationGCS(FEModel* fem, FERigidConnector* prb) : FEPlotObjectData(fem), m_rc(prb) {}\n\n    bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n    {\n        assert(m_rc);\n        ar << m_rc->RelativeRotation(true);\n        return true;\n    }\n\nprivate:\n    FERigidConnector* m_rc;\n};\n\nclass FEPlotRigidConnectorForce : public FEPlotObjectData\n{\npublic:\n    FEPlotRigidConnectorForce(FEModel* fem, FERigidConnector* prb) : FEPlotObjectData(fem), m_rc(prb) {}\n\n    bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n    {\n        assert(m_rc);\n        ar << m_rc->m_F;\n        return true;\n    }\n\nprivate:\n    FERigidConnector* m_rc;\n};\n\nclass FEPlotRigidConnectorMoment : public FEPlotObjectData\n{\npublic:\n    FEPlotRigidConnectorMoment(FEModel* fem, FERigidConnector* prb) : FEPlotObjectData(fem), m_rc(prb) {}\n\n    bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar)\n    {\n        assert(m_rc);\n        ar << m_rc->m_M;\n        return true;\n    }\n\nprivate:\n    FERigidConnector* m_rc;\n};\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::UpdatePlotObjects()\n{\n\tFEBioPlotFile* plt = dynamic_cast<FEBioPlotFile*>(m_plot);\n\tif (plt == nullptr) return;\n\n\tint nrb = RigidBodies();\n\tif (nrb == 0) return;\n\n\tFEModel& fem = *GetFEModel();\n\n\tint nid = 1;\n\tif (plt->PointObjects() == 0)\n\t{\n\t\tfor (int i = 0; i < nrb; ++i)\n\t\t{\n\t\t\tFERigidBody* rb = GetRigidBody(i);\n\t\t\tstring name = rb->GetName();\n\t\t\tif (name.empty())\n\t\t\t{\n\t\t\t\tstringstream ss;\n\t\t\t\tss << \"Object\" << nid;\n\t\t\t\tname = ss.str();\n\t\t\t}\n\n\t\t\tFEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n\t\t\tpo->m_tag = OBJ_RIGID_BODY;\n\t\t\tpo->m_pos = rb->m_r0;\n\t\t\tpo->m_rot = quatd(0, vec3d(1,0,0));\n\n\t\t\tpo->AddData(\"Position\"            , PLT_VEC3F, new FEPlotRigidBodyPosition(this, rb));\n\t\t\tpo->AddData(\"Velocity\"            , PLT_VEC3F, new FEPlotRigidBodyVelocity(this, rb));\n\t\t\tpo->AddData(\"Acceleration\"        , PLT_VEC3F, new FEPlotRigidBodyAcceleration(this, rb));\n\t\t\tpo->AddData(\"Euler angles (deg)\"  , PLT_VEC3F, new FEPlotRigidBodyEuler(this, rb));\n\t\t\tpo->AddData(\"Angular velocity\"    , PLT_VEC3F, new FEPlotRigidBodyAngularVelocity(this, rb));\n\t\t\tpo->AddData(\"Angular acceleration\", PLT_VEC3F, new FEPlotRigidBodyAngularAcceleration(this, rb));\n\t\t\tpo->AddData(\"Force\"               , PLT_VEC3F, new FEPlotRigidBodyForce(this, rb));\n\t\t\tpo->AddData(\"Moment\"              , PLT_VEC3F, new FEPlotRigidBodyMoment(this, rb));\n\n\t\t\tnid++;\n\t\t}\n\n\t\t// check rigid connectors\n\t\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFENLConstraint* pc = fem.NonlinearConstraint(i);\n\n\t\t\tstring name = pc->GetName();\n\t\t\tif (name.empty())\n\t\t\t{\n\t\t\t\tstringstream ss;\n\t\t\t\tss << \"Object\" << nid;\n\t\t\t\tname = ss.str();\n\t\t\t}\n\n\t\t\tFEGenericRigidJoint* rj = dynamic_cast<FEGenericRigidJoint*>(pc);\n\t\t\tif (rj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n\t\t\t\tpo->m_tag = OBJ_GENERIC_JOINT;\n\t\t\t\tpo->m_pos = rj->InitialPosition();\n\t\t\t\tpo->m_rot = quatd(0, vec3d(1, 0, 0));\n                po->AddData(\"Relative translation (LCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationLCS(this, rj));\n                po->AddData(\"Relative rotation (LCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationLCS(this, rj));\n                po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rj));\n                po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rj));\n                po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rj));\n                po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rj));\n\t\t\t}\n\n            FERigidSphericalJoint* rsj = dynamic_cast<FERigidSphericalJoint*>(pc);\n            if (rsj)\n            {\n                FEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n                po->m_tag = OBJ_SPHERICAL_JOINT;\n                po->m_pos = rsj->InitialPosition();\n                po->m_rot = quatd(0, vec3d(1, 0, 0));\n                po->AddData(\"Relative translation (LCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationLCS(this, rsj));\n                po->AddData(\"Relative rotation (LCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationLCS(this, rsj));\n                po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rsj));\n                po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rsj));\n                po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rsj));\n                po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rsj));\n            }\n\n\t\t\tFERigidPrismaticJoint* rpj = dynamic_cast<FERigidPrismaticJoint*>(pc);\n\t\t\tif (rpj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n\t\t\t\tpo->m_tag = OBJ_PRISMATIC_JOINT;\n\t\t\t\tpo->m_pos = rpj->InitialPosition();\n\t\t\t\tpo->m_rot = rpj->Orientation();\n                po->AddData(\"Relative translation (LCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationLCS(this, rpj));\n                po->AddData(\"Relative rotation (LCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationLCS(this, rpj));\n                po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rpj));\n                po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rpj));\n                po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rpj));\n                po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rpj));\n\t\t\t}\n\n\t\t\tFERigidRevoluteJoint* rrj = dynamic_cast<FERigidRevoluteJoint*>(pc);\n\t\t\tif (rrj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n\t\t\t\tpo->m_tag = OBJ_REVOLUTE_JOINT;\n\t\t\t\tpo->m_pos = rrj->InitialPosition();\n\t\t\t\tpo->m_rot = rrj->Orientation();\n                po->AddData(\"Relative translation (LCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationLCS(this, rrj));\n                po->AddData(\"Relative rotation (LCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationLCS(this, rrj));\n                po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rrj));\n                po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rrj));\n                po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rrj));\n                po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rrj));\n\t\t\t}\n\n\t\t\tFERigidCylindricalJoint* rcj = dynamic_cast<FERigidCylindricalJoint*>(pc);\n\t\t\tif (rcj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n\t\t\t\tpo->m_tag = OBJ_CYLINDRICAL_JOINT;\n\t\t\t\tpo->m_pos = rcj->InitialPosition();\n\t\t\t\tpo->m_rot = rcj->Orientation();\n                po->AddData(\"Relative translation (LCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationLCS(this, rcj));\n                po->AddData(\"Relative rotation (LCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationLCS(this, rcj));\n                po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rcj));\n                po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rcj));\n                po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rcj));\n                po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rcj));\n\t\t\t}\n            \n            FERigidPlanarJoint* rlj = dynamic_cast<FERigidPlanarJoint*>(pc);\n            if (rlj)\n            {\n                FEBioPlotFile::PointObject* po = plt->AddPointObject(name);\n                po->m_tag = OBJ_PLANAR_JOINT;\n                po->m_pos = rlj->InitialPosition();\n                po->m_rot = rlj->Orientation();\n                po->AddData(\"Relative translation (LCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationLCS(this, rlj));\n                po->AddData(\"Relative rotation (LCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationLCS(this, rlj));\n                po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rlj));\n                po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rlj));\n                po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rlj));\n                po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rlj));\n            }\n\t\t\tnid++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < nrb; ++i)\n\t\t{\n\t\t\tFERigidBody* rb = GetRigidBody(i);\n\t\t\tFEBioPlotFile::PointObject* po = plt->GetPointObject(i);\n\t\t\tpo->m_pos = rb->m_rt;\n\t\t\tpo->m_rot = rb->GetRotation();\n\t\t}\n\n\t\t// check rigid connectors\n\t\tint n = nrb;\n\t\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFENLConstraint* pc = fem.NonlinearConstraint(i);\n\n\t\t\tFEGenericRigidJoint* rj = dynamic_cast<FEGenericRigidJoint*>(pc);\n\t\t\tif (rj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->GetPointObject(n++);\n\t\t\t\tpo->m_pos = rj->Position();\n\t\t\t\tpo->m_rot = quatd(0, vec3d(1, 0, 0));\n\t\t\t}\n\n            FERigidSphericalJoint* rsj = dynamic_cast<FERigidSphericalJoint*>(pc);\n            if (rsj)\n            {\n                FEBioPlotFile::PointObject* po = plt->GetPointObject(n++);\n                po->m_pos = rsj->Position();\n                po->m_rot = quatd(0, vec3d(1, 0, 0));\n            }\n\n\t\t\tFERigidPrismaticJoint* rpj = dynamic_cast<FERigidPrismaticJoint*>(pc);\n\t\t\tif (rpj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->GetPointObject(n++);\n\t\t\t\tpo->m_pos = rpj->Position();\n\t\t\t\tpo->m_rot = rpj->Orientation();\n\t\t\t}\n\n\t\t\tFERigidRevoluteJoint* rrj = dynamic_cast<FERigidRevoluteJoint*>(pc);\n\t\t\tif (rrj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->GetPointObject(n++);\n\t\t\t\tpo->m_pos = rrj->Position();\n\t\t\t\tpo->m_rot = rrj->Orientation();\n\t\t\t}\n\n\t\t\tFERigidCylindricalJoint* rcj = dynamic_cast<FERigidCylindricalJoint*>(pc);\n\t\t\tif (rcj)\n\t\t\t{\n\t\t\t\tFEBioPlotFile::PointObject* po = plt->GetPointObject(n++);\n\t\t\t\tpo->m_pos = rcj->Position();\n\t\t\t\tpo->m_rot = rcj->Orientation();\n\t\t\t}\n            \n            FERigidPlanarJoint* rlj = dynamic_cast<FERigidPlanarJoint*>(pc);\n            if (rlj)\n            {\n                FEBioPlotFile::PointObject* po = plt->GetPointObject(n++);\n                po->m_pos = rlj->Position();\n                po->m_rot = rlj->Orientation();\n            }\n\t\t}\n\t}\n\n\tif (plt->LineObjects() == 0)\n\t{\n\t\t// check rigid connectors\n\t\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(fem.NonlinearConstraint(i));\n\t\t\tif (prc)\n\t\t\t{\n\t\t\t\tstring name = prc->GetName();\n\t\t\t\tif (name.empty())\n\t\t\t\t{\n\t\t\t\t\tstringstream ss;\n\t\t\t\t\tss << \"LineObject\" << nid;\n\t\t\t\t\tname = ss.str();\n\t\t\t\t}\n\n\t\t\t\tvec3d ra = GetRigidBody(prc->m_nRBa)->m_r0;\n\t\t\t\tvec3d rb = GetRigidBody(prc->m_nRBb)->m_r0;\n\n\t\t\t\tFERigidSpring* rs = dynamic_cast<FERigidSpring*>(prc);\n\t\t\t\tif (rs)\n\t\t\t\t{\n\t\t\t\t\tFEBioPlotFile::LineObject* po = plt->AddLineObject(name);\n\t\t\t\t\tpo->m_tag = 1;\n\t\t\t\t\tpo->m_rot = quatd(0, vec3d(1, 0, 0));\n\t\t\t\t\tpo->m_r1 = rs->m_at;\n\t\t\t\t\tpo->m_r2 = rs->m_bt;\n                    po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rs));\n                    po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rs));\n                    po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rs));\n                    po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rs));\n\t\t\t\t}\n\n\t\t\t\tFERigidDamper* rd = dynamic_cast<FERigidDamper*>(prc);\n\t\t\t\tif (rd)\n\t\t\t\t{\n\t\t\t\t\tFEBioPlotFile::LineObject* po = plt->AddLineObject(name);\n\t\t\t\t\tpo->m_tag = 2;\n                    po->m_rot = quatd(0, vec3d(1, 0, 0));\n\t\t\t\t\tpo->m_r1 = rd->m_at;\n\t\t\t\t\tpo->m_r2 = rd->m_bt;\n                    po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rd));\n                    po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rd));\n                    po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rd));\n                    po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rd));\n\t\t\t\t}\n                \n                FERigidAngularDamper* rad = dynamic_cast<FERigidAngularDamper*>(prc);\n                if (rad)\n                {\n                    FEBioPlotFile::LineObject* po = plt->AddLineObject(name);\n                    po->m_tag = 3;\n                    po->m_r1 = ra;\n                    po->m_r2 = rb;\n                    po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rad));\n                    po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rad));\n                    po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rad));\n                    po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rad));\n                }\n                \n                FERigidContractileForce* rcf = dynamic_cast<FERigidContractileForce*>(prc);\n                if (rcf)\n                {\n                    FEBioPlotFile::LineObject* po = plt->AddLineObject(name);\n                    po->m_tag = 4;\n                    po->m_rot = quatd(0, vec3d(1, 0, 0));\n                    po->m_r1 = rcf->m_at;\n                    po->m_r2 = rcf->m_bt;\n                    po->AddData(\"Relative translation (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorTranslationGCS(this, rcf));\n                    po->AddData(\"Relative rotation (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorRotationGCS(this, rcf));\n                    po->AddData(\"Reaction force (GCS)\" , PLT_VEC3F, new FEPlotRigidConnectorForce(this, rcf));\n                    po->AddData(\"Reaction moment (GCS)\", PLT_VEC3F, new FEPlotRigidConnectorMoment(this, rcf));\n                }\n\t\t\t}\n\t\t\tnid++;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// check rigid connectors\n\t\tint n = 0;\n\t\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(fem.NonlinearConstraint(i));\n\t\t\tif (prc)\n\t\t\t{\n\t\t\t\tvec3d ra = GetRigidBody(prc->m_nRBa)->m_rt;\n\t\t\t\tvec3d rb = GetRigidBody(prc->m_nRBb)->m_rt;\n\n\t\t\t\tFERigidSpring* rs = dynamic_cast<FERigidSpring*>(prc);\n\t\t\t\tif (rs)\n\t\t\t\t{\n\t\t\t\t\tFEBioPlotFile::LineObject* po = plt->GetLineObject(n++);\n\t\t\t\t\tpo->m_r1 = rs->m_at;\n\t\t\t\t\tpo->m_r2 = rs->m_bt;\n\t\t\t\t}\n\n\t\t\t\tFERigidDamper* rd = dynamic_cast<FERigidDamper*>(prc);\n\t\t\t\tif (rd)\n\t\t\t\t{\n\t\t\t\t\tFEBioPlotFile::LineObject* po = plt->GetLineObject(n++);\n\t\t\t\t\tpo->m_r1 = ra;\n\t\t\t\t\tpo->m_r2 = rb;\n\t\t\t\t}\n                \n                FERigidAngularDamper* rad = dynamic_cast<FERigidAngularDamper*>(prc);\n                if (rad)\n                {\n                    FEBioPlotFile::LineObject* po = plt->GetLineObject(n++);\n                    po->m_r1 = ra;\n                    po->m_r2 = rb;\n                }\n                \n                FERigidContractileForce* rcf = dynamic_cast<FERigidContractileForce*>(prc);\n                if (rcf)\n                {\n                    FEBioPlotFile::LineObject* po = plt->GetLineObject(n++);\n                    po->m_r1 = rcf->m_at;\n                    po->m_r2 = rcf->m_bt;\n                }\n\t\t\t}\n\t\t}\n\t}\n}\n\n//=============================================================================\n//    R E S T A R T\n//=============================================================================\n\nclass restart_exception : public std::runtime_error\n{\npublic:\n\trestart_exception() : std::runtime_error(\"restart error\") {}\n\trestart_exception(const char* msg) : std::runtime_error(msg) {}\n};\n\n//-----------------------------------------------------------------------------\n//!  Reads or writes the current state to/from a binary file\n//!  This is used to restart the solution from a saved position\n//!  or to create a restart point.\n//!  A version number is written to file to make sure the same\n//!  format is used for reading and writing.\n//! \\param[in] ar the archive to which the data is serialized\n//! \\sa DumpFile\nvoid FEBioModel::Serialize(DumpStream& ar)\n{\n\t// don't need to do anything for running restarts\n\tif (ar.IsShallow())\n\t{\n\t\t// serialize model data\n\t\tFEMechModel::Serialize(ar);\n\t}\n\telse\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\t// --- version number ---\n\t\t\tar << (int) RSTRTVERSION;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// --- version ---\n\t\t\tint nversion;\n\t\t\tar >> nversion;\n\n\t\t\t// make sure it is the right version\n\t\t\tif (nversion != RSTRTVERSION) throw restart_exception(\"incorrect version number\");\n\t\t}\n\n\t\t// serialize model data\n\t\tFEMechModel::Serialize(ar);\n\n\t\t// --- Save IO Data\n\t\tSerializeIOData(ar);\n\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tint n = (int)m_stepStats.size();\n\t\t\tar << n;\n\t\t\tfor (ModelStats& s : m_stepStats)\n\t\t\t{\n\t\t\t\tar << s.ntimeSteps << s.ntotalIters << s.ntotalReforms << s.ntotalRHS;\n\t\t\t}\n\n\t\t\tn = (int)m_timestepStats.size();\n\t\t\tar << n;\n\t\t\tfor (TimeStepStats& s : m_timestepStats)\n\t\t\t{\n\t\t\t\tar << s.iters << s.nrhs << s.refs << s.status;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_stepStats.clear();\n\t\t\tint n = 0;\n\t\t\tar >> n;\n\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t{\n\t\t\t\tModelStats s;\n\t\t\t\tar >> s.ntimeSteps >> s.ntotalIters >> s.ntotalReforms >> s.ntotalRHS;\n\t\t\t\tm_stepStats.push_back(s);\n\t\t\t}\n\n\t\t\tm_timestepStats.clear();\n\t\t\tn = 0;\n\t\t\tar >> n;\n\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t{\n\t\t\t\tTimeStepStats s;\n\t\t\t\tar >> s.iters >> s.nrhs >> s.refs >> s.status;\n\t\t\t\tm_timestepStats.push_back(s);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization of FEBioModel data\nvoid FEBioModel::SerializeIOData(DumpStream &ar)\n{\n\tif (ar.IsSaving())\n\t{\n\t\t// file names\n\t\tar << m_sfile << m_splot << m_slog << m_sdump;\n\n\t\t// plot file\n\t\tint npltfmt = 2;\n\t\tar << npltfmt;\n\n\t\tSerializePlotData(ar);\n\n\t\tif (m_plot) m_plot->Serialize(ar);\n\n\t\t// data records\n\t\tSerializeDataStore(ar);\n\t}\n\telse\n\t{\n\t\t// file names\n\t\tstring splot, slog, sdmp;\n\t\tar >> m_sfile >> splot >> slog >> sdmp;\n\n\t\t// don't forget to call store the input file name so\n\t\t// that m_szfile_title gets initialized\n\t\tSetInputFilename(m_sfile);\n\n\t\t// If we append, use the original names\n\t\t// otherwise we use the names as was initialized by the command line parser\n\t\tif (m_pltAppendOnRestart)\n\t\t{\n\t\t\tm_splot = splot;\n\t\t\tm_slog = slog;\n\t\t\tm_sdump = sdmp;\n\t\t}\n\n\t\t// get the plot file format (should be 2)\n\t\tint npltfmt = 0;\n\t\tar >> npltfmt;\n\t\tassert(npltfmt == 2);\n\n\t\tSerializePlotData(ar);\n\n\t\t// remove the plot file (if any)\n\t\tif (m_plot) { delete m_plot; m_plot = 0; }\n\n\t\t// create the plot file\n\t\tFEPlotDataStore& data = GetPlotDataStore();\n\t\tif (data.GetPlotFileType() == \"febio\")\n\t\t{\n\t\t\tFEBioPlotFile* xplt = new FEBioPlotFile(this);\n\n\t\t\t// set the software string\n\t\t\tconst char* szver = febio::getVersionString();\n\t\t\tchar szbuf[256] = { 0 };\n\t\t\tsnprintf(szbuf, sizeof(szbuf), \"FEBio %s\", szver);\n\t\t\txplt->SetSoftwareString(szbuf);\n\n\t\t\tm_plot = xplt;\n\t\t}\n\t\telse if (data.GetPlotFileType() == \"vtk\") m_plot = new VTKPlotFile(this);\n\n\t\tif (m_plot) m_plot->Serialize(ar);\n\n\t\tif (m_pltAppendOnRestart)\n\t\t{\n\t\t\t// Open for appending\n\t\t\tif (m_plot->Append(m_splot.c_str()) == false)\n\t\t\t{\n\t\t\t\tprintf(\"FATAL ERROR: Failed reopening plot database %s\\n\", m_splot.c_str());\n\t\t\t\tthrow \"FATAL ERROR\";\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (m_plot->Open(m_splot.c_str()) == false)\n\t\t\t{\n\t\t\t\tprintf(\"FATAL ERROR: Failed creating plot database %s\\n\", m_splot.c_str());\n\t\t\t\tthrow \"FATAL ERROR\";\n\t\t\t}\n\t\t}\n\n\t\t// data records\n\t\tSerializeDataStore(ar);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::SerializePlotData(DumpStream& ar)\n{\n\tGetPlotDataStore().Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::SerializeDataStore(DumpStream& ar)\n{\n\tDataStore& dataStore = GetDataStore();\n\tif (ar.IsSaving())\n\t{\n\t\tint N = dataStore.Size();\n\t\tar << N;\n\t\tfor (int i=0; i<N; ++i)\n\t\t{\n\t\t\tDataRecord* pd = dataStore.GetDataRecord(i);\n\n\t\t\tint ntype = pd->m_type;\n\t\t\tar << ntype;\n\t\t\tpd->Serialize(ar);\n\t\t}\n\t}\n\telse\n\t{\n\t\tint N;\n\t\tdataStore.Clear();\n\t\tar >> N;\n\t\tfor (int i=0; i<N; ++i)\n\t\t{\n\t\t\tint ntype;\n\t\t\tar >> ntype;\n\n\t\t\tDataRecord* pd = 0;\n\t\t\tswitch(ntype)\n\t\t\t{\n\t\t\tcase FE_DATA_NODE   : pd = new NodeDataRecord        (this); break;\n\t\t\tcase FE_DATA_FACE   : pd = new FaceDataRecord        (this); break;\n\t\t\tcase FE_DATA_ELEM   : pd = new ElementDataRecord     (this); break;\n\t\t\tcase FE_DATA_RB     : pd = new ObjectDataRecord      (this); break;\n\t\t\tcase FE_DATA_NLC    : pd = new NLConstraintDataRecord(this); break;\n\t\t\tcase FE_DATA_SURFACE: pd = new FESurfaceDataRecord   (this); break;\n\t\t\tcase FE_DATA_DOMAIN : pd = new FEDomainDataRecord    (this); break;\n\t\t\tcase FE_DATA_MODEL  : pd = new FEModelDataRecord     (this); break;\n\t\t\t}\n\t\t\tassert(pd);\n\t\t\tpd->Serialize(ar);\n\t\t\tdataStore.AddRecord(pd);\n\t\t}\n\t}\n}\n\n//=============================================================================\n//    I N I T I A L I Z A T I O N\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Initialize plot file\nbool FEBioModel::InitPlotFile()\n{\n\tFEPlotDataStore& data = GetPlotDataStore();\n\n\tif (data.GetPlotFileType() == \"febio\")\n\t{\n\t\tFEBioPlotFile* xplt = new FEBioPlotFile(this);\n\t\t// set the software string\n\t\tconst char* szver = febio::getVersionString();\n\t\tchar szbuf[256] = { 0 };\n\t\tsnprintf(szbuf, sizeof(szbuf), \"FEBio %s\", szver);\n\t\txplt->SetSoftwareString(szbuf);\n\n\t\tm_plot = xplt;\n\n\t\t// see if a valid plot file name is defined.\n\t\tconst std::string& splt = GetPlotFileName();\n\t\tif (splt.empty())\n\t\t{\n\t\t\t// if not, we take the input file name and set the extension to .xplt\n\t\t\tchar sz[1024] = { 0 };\n\t\t\tstrcpy(sz, GetInputFileName().c_str());\n\t\t\tchar* ch = strrchr(sz, '.');\n\t\t\tif (ch) *ch = 0;\n\t\t\tstrcat(sz, \".xplt\");\n\t\t\tSetPlotFilename(sz);\n\t\t}\n\t}\n\telse if (data.GetPlotFileType() == \"vtk\")\n\t{\n\t\tVTKPlotFile* vtk = new VTKPlotFile(this);\n\t\tm_plot = vtk;\n\n\t\t// see if a valid plot file name is defined.\n\t\tconst std::string& splt = GetPlotFileName();\n\t\tif (splt.empty())\n\t\t{\n\t\t\t// if not, we take the input file name and set the extension to .vtk\n\t\t\tchar sz[1024] = { 0 };\n\t\t\tstrcpy(sz, GetInputFileName().c_str());\n\t\t\tchar* ch = strrchr(sz, '.');\n\t\t\tif (ch) *ch = 0;\n\t\t\tstrcat(sz, \".vtk\");\n\t\t\tSetPlotFilename(sz);\n\t\t}\n\t}\n\telse return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function performs one-time-initialization stuff. All the different \n//! modules are initialized here as well. This routine also performs some\n//! data checks\n\nbool FEBioModel::Init()\n{\n\tTRACK_TIME(TimerID::Timer_Init);\n\n\t// Open the logfile\n\tif (m_logLevel != 0)\n\t{\n\t\tif (InitLogFile() == false) return false;\n\t}\n\n\tm_report.clear();\n\tm_stepStats.clear();\n\tm_timestepStats.clear();\n\n\tFEBioPlotFile* pplt = nullptr;\n\tm_lastUpdate = -1;\n\n\t// see if a valid dump file name is defined.\n\tconst std::string& sdmp = GetDumpFileName();\n\tif (sdmp.empty())\n\t{\n\t\t// if not, we take the input file name and set the extension to .dmp\n\t\tchar sz[1024] = { 0 };\n\t\tstrcpy(sz, GetInputFileName().c_str());\n\t\tchar* ch = strrchr(sz, '.');\n\t\tif (ch) *ch = 0;\n\t\tstrcat(sz, \".dmp\");\n\t\tSetDumpFilename(sz);\n\t}\n\n\t// initialize data records\n\tDataStore& dataStore = GetDataStore();\n\tfor (int i = 0; i < dataStore.Size(); ++i)\n\t{\n\t\tif (dataStore.GetDataRecord(i)->Initialize() == false) return false;\n\t}\n\n\t// initialize model data\n\tif (FEMechModel::Init() == false) \n\t{\n\t\tfeLogError(\"Model initialization failed\");\n\t\treturn false;\n\t}\n\n\t// open plot database file\n\tFEAnalysis* step = GetCurrentStep();\n\tif (step->GetPlotLevel() != FE_PLOT_NEVER)\n\t{\n\t\tif (m_plot == nullptr)\n\t\t{\n\t\t\tif (InitPlotFile() == false) { feLogError(\"Failed to initialize plot file.\"); return false; }\n\t\t}\n\t}\n\n\t// Alright, all initialization is done, so let's get busy !\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Opens the log file\nbool FEBioModel::InitLogFile()\n{\n\t// Only do this if the log file is not valid\n\tif (!m_log.is_valid())\n\t{\n\t\t// see if a valid log file name is defined.\n\t\tconst std::string& slog = GetLogfileName();\n\t\tif (slog.empty())\n\t\t{\n\t\t\t// if not, we take the input file name and set the extension to .log\n\t\t\tchar sz[1024] = {0};\n\t\t\tstrcpy(sz, GetInputFileName().c_str());\n\t\t\tchar *ch = strrchr(sz, '.');\n\t\t\tif (ch) *ch = 0;\n\t\t\tstrcat(sz, \".log\");\n\t\t\tSetLogFilename(sz);\n\t\t}\n\n\t\t// create a log stream\n\t\tLogFileStream* fp = new LogFileStream;\n\t\tm_log.SetFileStream(fp);\n\t\tif (fp->open(m_slog.c_str()) == false)\n\t\t{\n\t\t\tfeLogError(\"Failed creating log file\");\n\t\t\treturn false;\n\t\t}\n\n\t\t// make sure we have a step\n\t\tFEAnalysis* step = GetCurrentStep();\n\t\tif (step == 0)\n\t\t{\n\t\t\tfeLogError(\"No step defined.\");\n\t\t\treturn false;\n\t\t}\n\n\t\t// print welcome message to file\n\t\tLogfile::MODE m = m_log.SetMode(Logfile::LOG_FILE);\n\t\tfebio::Hello(*fp);\n\t\tm_log.SetMode(m);\n\t}\n\n\treturn true;\n}\n\nbool FEBioModel::Solve()\n{\n\t// The total time is usually started on calling Input,\n\t// however, in a restart Input is not called, so we start it here.\n\tif (!m_TotalTime.isRunning()) m_TotalTime.start();\n\tbool b = FEModel::Solve();\n\tm_TotalTime.stop();\n\treturn b;\n}\n\n//! This function resets the FEM data so that a new run can be done.\n//! This routine is called from the optimization routine.\nbool FEBioModel::Reset()\n{\n\t// Reset model data\n\tFEMechModel::Reset();\n\tm_TotalTime.reset();\n\n\t// re-initialize the log file\n\tif (m_logLevel != 0)\n\t{\n\t\t// TODO: I added this so that log files can be compared using the reset_test\n\t\t// but this messes up the output for optimization problems.\n\t\t// I want the optimization create its own log file, so it is decoupled from the model's\n\t\t// log file. But since all the logging stuff lives in FEBioLib, I can't do this yet.\n//\t\tif (m_log.is_valid()) m_log.close();\n\n\t\tif (InitLogFile() == false) return false;\n\t}\n\n\t// open plot database file\n\tFEAnalysis* step =  GetCurrentStep();\n\tif (step->GetPlotLevel() != FE_PLOT_NEVER)\n\t{\n\t\tint hint = step->GetPlotHint();\n\t\tif (m_plot == 0) \n\t\t{\n\t\t\tFEPlotDataStore& data = GetPlotDataStore();\n\t\t\tif      (data.GetPlotFileType() == \"febio\") m_plot = new FEBioPlotFile(this);\n\t\t\telse if (data.GetPlotFileType() == \"vtk\"  ) m_plot = new VTKPlotFile(this);\n\t\t\thint = 0;\n\t\t}\n\n\t\tif (hint != FE_PLOT_APPEND)\n\t\t{\n\t\t\tif (m_plot->Open(m_splot.c_str()) == false)\n\t\t\t{\n\t\t\t\tfeLogError(\"Failed creating PLOT database.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// reset stats\n\tm_modelStats.ntimeSteps = 0;\n\tm_modelStats.ntotalIters = 0;\n\tm_modelStats.ntotalRHS = 0;\n\tm_modelStats.ntotalReforms = 0;\n\tm_stepStats.clear();\n\tm_timestepStats.clear();\n\n\t// do the callback\n\tDoCallback(CB_INIT);\n\n\t// All data is reset successfully\n\treturn true;\n}\n\nTimingInfo FEBioModel::GetTimingInfo()\n{\n\tdouble tot = m_TotalTime.GetTime();\n\n\tTimingInfo ti;\n\tti.total_time = m_TotalTime.GetTime();\n\tti.solve_time = GetSolveTimer().GetTime();\n\n\tdouble total = 0;\n\tti.input_time         = m_InputTime.GetExclusiveTime(); total += ti.input_time;\n\tti.init_time          = GetTimer(TimerID::Timer_Init)->GetExclusiveTime(); total += ti.init_time;\n\tti.io_time            = m_IOTimer.GetExclusiveTime(); total += ti.io_time;\n\tti.total_ls_factor    = GetTimer(TimerID::Timer_LinSol_Factor   )->GetExclusiveTime(); total += ti.total_ls_factor;\n\tti.total_ls_backsolve = GetTimer(TimerID::Timer_LinSol_Backsolve)->GetExclusiveTime(); total += ti.total_ls_backsolve;\n\tti.total_reform       = GetTimer(TimerID::Timer_Reform          )->GetExclusiveTime(); total += ti.total_reform;\n\tti.total_stiff        = GetTimer(TimerID::Timer_Stiffness       )->GetExclusiveTime(); total += ti.total_stiff;\n\tti.total_rhs          = GetTimer(TimerID::Timer_Residual        )->GetExclusiveTime(); total += ti.total_rhs;\n\tti.total_update       = GetTimer(TimerID::Timer_Update          )->GetExclusiveTime(); total += ti.total_update;\n\tti.total_qn           = GetTimer(TimerID::Timer_QNUpdate        )->GetExclusiveTime(); total += ti.total_qn;\n\tti.total_serialize    = GetTimer(TimerID::Timer_Serialize       )->GetExclusiveTime(); total += ti.total_serialize;\n\tti.total_callback     = GetTimer(TimerID::Timer_Callback        )->GetExclusiveTime(); total += ti.total_callback;\n\n\tti.total_other  = ti.total_time - total;\n//\tassert(ti.total_other >= 0);\n\n\treturn ti;\n}\n\n//=============================================================================\n//                               S O L V E\n//=============================================================================\n\nvoid FEBioModel::on_cb_solved()\n{\n\tFEAnalysis* step = GetCurrentStep();\n\tif (step == nullptr) return;\n\n\t// for multistep analysis we'll print a grand total\n\tif (Steps() > 1)\n\t{\n\t\tfeLog(\"\\n\\n N O N L I N E A R   I T E R A T I O N   S U M M A R Y\\n\\n\");\n\t\tfeLog(\"\\tNumber of time steps completed .................... : %d\\n\\n\", m_modelStats.ntimeSteps);\n\t\tfeLog(\"\\tTotal number of equilibrium iterations ............ : %d\\n\\n\", m_modelStats.ntotalIters);\n\t\tfeLog(\"\\tTotal number of right hand evaluations ............ : %d\\n\\n\", m_modelStats.ntotalRHS);\n\t\tfeLog(\"\\tTotal number of stiffness reformations ............ : %d\\n\\n\", m_modelStats.ntotalReforms);\n\t}\n\n\t// get timing info\n\tTimingInfo ti = GetTimingInfo();\n\n\t// get and print elapsed time\n\tdouble linsol_time = ti.total_ls_factor + ti.total_ls_backsolve;\n\tchar sztime[64];\n\tTimer::time_str(linsol_time, sztime);\n\tfeLog(\"\\tTime in linear solver: %s\\n\\n\", sztime);\n\n\t// always flush the log\n\tm_log.flush();\n\n\t// get peak memory usage\n#ifdef WIN32\n\tsize_t memsize = GetPeakMemory();\n\tif (memsize != 0)\n\t{\n\t\tdouble mb = (double)memsize / 1048576.0;\n\t\tfeLog(\" Peak memory  : %.1lf MB\\n\", mb);\n\t}\n#endif\n\n\t// print the elapsed time\n\tGetSolveTimer().time_str(sztime);\n\tfeLog(\"\\n Elapsed time : %s\\n\\n\", sztime);\n\n\t// print additional stats to the log file only\n\tif (m_log.GetMode() & Logfile::LOG_FILE)\n\t{\n\t\t// print more detailed timing info to the log file\n\t\tLogfile::MODE old_mode = m_log.SetMode(Logfile::LOG_FILE);\n\n\t\t// sum up all the times spend in the linear solvers\n\t\tfeLog(\" T I M I N G   I N F O R M A T I O N\\n\\n\");\n\t\tTimer::time_str(ti.input_time  , sztime); feLog(\"\\tInput time ...................... : %s (%lg sec)\\n\\n\", sztime, ti.input_time);\n\t\tTimer::time_str(ti.init_time   , sztime); feLog(\"\\tInitialization time ............. : %s (%lg sec)\\n\\n\", sztime, ti.init_time);\n\t\tTimer::time_str(ti.solve_time  , sztime); feLog(\"\\tSolve time ...................... : %s (%lg sec)\\n\\n\", sztime, ti.solve_time);\n\t\tTimer::time_str(ti.io_time     , sztime); feLog(\"\\t   IO-time (plot, dmp, data) .... : %s (%lg sec)\\n\\n\", sztime, ti.io_time);\n\t\tTimer::time_str(ti.total_reform, sztime); feLog(\"\\t   reforming stiffness .......... : %s (%lg sec)\\n\\n\", sztime, ti.total_reform);\n\t\tTimer::time_str(ti.total_stiff , sztime); feLog(\"\\t   evaluating stiffness ......... : %s (%lg sec)\\n\\n\", sztime, ti.total_stiff);\n\t\tTimer::time_str(ti.total_rhs   , sztime); feLog(\"\\t   evaluating residual .......... : %s (%lg sec)\\n\\n\", sztime, ti.total_rhs);\n\t\tTimer::time_str(ti.total_update, sztime); feLog(\"\\t   model update ................. : %s (%lg sec)\\n\\n\", sztime, ti.total_update);\n\t\tTimer::time_str(ti.total_qn    , sztime); feLog(\"\\t   QN updates ................... : %s (%lg sec)\\n\\n\", sztime, ti.total_qn);\n\t\tTimer::time_str(linsol_time    , sztime); feLog(\"\\t   time in linear solver ........ : %s (%lg sec)\\n\\n\", sztime, linsol_time);\n\t\tTimer::time_str(ti.total_time  , sztime); feLog(\"\\tTotal elapsed time .............. : %s (%lg sec)\\n\\n\", sztime, ti.total_time);\n\n\t\tm_log.SetMode(old_mode);\n\n\t\tbool bconv = IsSolved();\n\t\tif (bconv)\n\t\t{\n\t\t\tfeLog(\"\\n N O R M A L   T E R M I N A T I O N\\n\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfeLog(\"\\n E R R O R   T E R M I N A T I O N\\n\\n\");\n\t\t}\n\n\t\t// flush the log file\n\t\tm_log.flush();\n\t}\n\n\t// close the plot file\n\tint hint = GetStep(Steps() - 1)->GetPlotHint();\n\tif (hint != FE_PLOT_APPEND)\n\t\tif (m_plot) m_plot->Close();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModel::on_cb_stepSolved()\n{\n\tFEAnalysis* step = GetCurrentStep();\n\tif (step == nullptr) return;\n\n\t// output report\n\tfeLog(\"\\n\\n N O N L I N E A R   I T E R A T I O N   I N F O R M A T I O N\\n\\n\");\n\tfeLog(\"\\tNumber of time steps completed .................... : %d\\n\\n\", step->m_ntimesteps);\n\tfeLog(\"\\tTotal number of equilibrium iterations ............ : %d\\n\\n\", step->m_ntotiter);\n\tfeLog(\"\\tAverage number of equilibrium iterations .......... : %lg\\n\\n\", (step->m_ntimesteps != 0 ? (double)step->m_ntotiter / (double)step->m_ntimesteps : 0));\n\tfeLog(\"\\tTotal number of right hand evaluations ............ : %d\\n\\n\", step->m_ntotrhs);\n\tfeLog(\"\\tTotal number of stiffness reformations ............ : %d\\n\\n\", step->m_ntotref);\n\n\t// print linear solver stats\n\tFESolver* ps = step->GetFESolver();\n\tif (ps)\n\t{\n\t\tLinearSolver* ls = step->GetFESolver()->GetLinearSolver();\n\t\tif (ls)\n\t\t{\n\t\t\tLinearSolverStats stats = ls->GetStats();\n\t\t\tint nsolves = stats.backsolves;\n\t\t\tint niters = stats.iterations;\n\t\t\tdouble avgiters = (nsolves != 0 ? (double)niters / (double)nsolves : (double)niters);\n\t\t\tfeLog(\"\\n L I N E A R   S O L V E R   S T A T S\\n\\n\");\n\t\t\tfeLog(\"\\tTotal calls to linear solver ........ : %d\\n\\n\", nsolves);\n\t\t\tfeLog(\"\\tAvg iterations per solve ............ : %lg\\n\\n\", avgiters);\n\t\t}\n\t}\n\n\t// add to stats\n\tModelStats stats;\n\tstats.ntimeSteps    = step->m_ntimesteps;\n\tstats.ntotalIters   = step->m_ntotiter;\n\tstats.ntotalRHS     = step->m_ntotrhs;\n\tstats.ntotalReforms = step->m_ntotref;\n\tm_stepStats.push_back(stats);\n\tm_modelStats.ntimeSteps    += stats.ntimeSteps;\n\tm_modelStats.ntotalIters   += stats.ntotalIters;\n\tm_modelStats.ntotalRHS     += stats.ntotalRHS;\n\tm_modelStats.ntotalReforms += stats.ntotalReforms;\n}\n\nbool FEBioModel::Restart(const char* szfile)\n{\n\t// check the extension of the file\n\tconst char* szext = strrchr(szfile, '.');\n\tif (strcmp(szext, \".feb\") == 0)\n\t{\n\t\t// process restart input file\n\t\tFERestartImport file;\n\t\tif (file.Load(*this, szfile) == false)\n\t\t{\n\t\t\tchar szerr[256];\n\t\t\tfile.GetErrorMessage(szerr);\n\t\t\tfprintf(stderr, \"%s\", szerr);\n\t\t\treturn false;\n\t\t}\n\n\t\t// get the number of new steps added\n\t\tint newSteps = file.StepsAdded();\n\t\tint step = Steps() - newSteps;\n\n\t\t// Any additional steps that were created must be initialized\n\t\tfor (int i = step; i < Steps(); ++i)\n\t\t{\n\t\t\tFEAnalysis* step = GetStep(i);\n\t\t\tif (step->Init() == false) return false;\n\n\t\t\t// also initialize all the step components\n\t\t\tfor (int j = 0; j < step->StepComponents(); ++j)\n\t\t\t{\n\t\t\t\tFEStepComponent* pc = step->GetStepComponent(j);\n\t\t\t\tif (pc->Init() == false) return false;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Open the dump file\n\t\tDumpFile ar(*this);\n\t\tif (ar.Open(szfile) == false)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t// try reading the file\n\t\tSerialize(ar);\n\t}\n\n\n\t// Open the log file for appending\n\tconst std::string& slog = GetLogfileName();\n\tLogfile& felog = GetLogFile();\n\tif (felog.append(slog.c_str()) == false)\n\t{\n\t\tprintf(\"WARNING: Could not reopen log file. A new log file is created\\n\");\n\t\tfelog.open(slog.c_str());\n\t\treturn false;\n\t}\n\n\t// inform the user from where the problem is restarted\n\tfelog.printbox(\" - R E S T A R T -\", \"Restarting from time %lg.\\n\", GetCurrentTime());\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioLib/FEBioModel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEMechModel.h>\n#include <FECore/Timer.h>\n#include <FECore/DataStore.h>\n#include <FEBioPlot/PlotFile.h>\n#include <FECore/FECoreKernel.h>\n#include <FEBioLib/Logfile.h>\n#include \"febiolib_api.h\"\n#include \"febiolib_types.h\"\n\n//-----------------------------------------------------------------------------\n// Dump level determines the times the restart file is written\nenum FE_Dump_Level {\n\tFE_DUMP_NEVER,\t\t\t// never write a dump file\n\tFE_DUMP_MAJOR_ITRS,\t\t// create a dump file at the end of each converged time step\n\tFE_DUMP_STEP,\t\t\t// create a dump file at the end of an analysis step\n\tFE_DUMP_MUST_POINTS     // create a dump file only on must-points\n};\n\nenum PlotObjectType {\n\tOBJ_UNKNOWN           = 0,\n\tOBJ_RIGID_BODY        = 1,\n\tOBJ_GENERIC_JOINT     = 2,\n\tOBJ_SPHERICAL_JOINT   = 3,\n\tOBJ_PRISMATIC_JOINT   = 4,\n\tOBJ_REVOLUTE_JOINT    = 5,\n\tOBJ_CYLINDRICAL_JOINT = 6,\n\tOBJ_PLANAR_JOINT      = 7\n};\n\n//-----------------------------------------------------------------------------\n//! The FEBio model specializes the FEModel class to implement FEBio specific\n//! functionality.\n//!\n//! In addition it adds support for all I/O capabilities. \n//!\nclass FEBIOLIB_API FEBioModel : public FEMechModel\n{\npublic:\n\t//! constructor\n\tFEBioModel();\n\n\t//! destructor\n\t~FEBioModel();\n\n\t//! Initializes data structures\n\tbool Init() override;\n\n\t//! Resets data structures\n\tbool Reset() override;\n\n\t//! solve the model\n\tbool Solve() override;\n\n\tTimingInfo GetTimingInfo();\n\npublic: // --- I/O functions ---\n\n\t//! input data from file\n\tbool Input(const char* szfile);\n\n\t//! handle output\n\tvoid Write(unsigned int nwhen);\n\n\t// write to plot file\n\tvoid WritePlot(unsigned int nevent);\n\n\t//! write data to log file\n\tvoid WriteData(unsigned int nevent);\n\n\t//! dump data to archive for restart\n\tvoid DumpData(int nevent);\n\n\t//! add to log \n\tvoid Log(int ntag, const char* szmsg) override;\n\n\t// get the log file\n\tLogfile& GetLogFile() { return m_log; }\n\n\t// get the report\n\tstd::string GetReport() const { return m_report; }\n\n\tvoid CreateReport(bool b) { m_createReport = b; }\n\npublic:\n\t//! set the problem title\n\tvoid SetTitle(const char* sz);\n\n\t//! get the problem title\n\tconst std::string& GetTitle() const;\n\npublic: //! --- serialization for restarts ---\n\t\n\t//! Write or read data from archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! restart from dump file or restart input file\n\tbool Restart(const char* szfile);\n\nprivate:\n\tstatic bool handleCB(FEModel* fem, unsigned int nwhen, void* pd);\n\tbool processEvent(int nevent);\n\n\tvoid on_cb_solved();\n\tvoid on_cb_stepSolved();\n\nprotected:\n\t// helper functions for serialization\n\tvoid SerializeIOData   (DumpStream& ar);\n\tvoid SerializeDataStore(DumpStream& ar);\n\tvoid SerializePlotData (DumpStream& ar);\n\n\tbool InitLogFile();\n\tbool InitPlotFile();\n\n\t//! get a list of domains that belong to a specific material\n\tvoid DomainListFromMaterial(vector<int>& lmat, vector<int>& ldom);\n\npublic: // --- I/O functions ---\n\t//! Add data record\n\tvoid AddDataRecord(DataRecord* pd);\n\n\t//! Get the plot file\n\tPlotFile* GetPlotFile();\n\n\t// set the i/o files\n\tvoid SetInputFilename(const std::string& sfile);\n\tvoid SetLogFilename  (const std::string& sfile);\n\tvoid SetPlotFilename (const std::string& sfile);\n\tvoid SetDumpFilename (const std::string& sfile);\n\n\t//! Get the I/O file names\n\tconst std::string& GetInputFileName();\n\tconst std::string& GetLogfileName  ();\n\tconst std::string& GetPlotFileName ();\n\tconst std::string& GetDumpFileName ();\n\n\t//! get the file title\n\tconst std::string& GetFileTitle();\n\n\t// set append-on-restart flag\n\tvoid SetAppendOnRestart(bool b);\n\tbool AppendOnRestart() const;\n\npublic:\n\tdouble GetEndTime() const;\n\npublic: // Timers\n\n\t//! Return the total timer\n\tTimer& GetSolveTimer();\n\npublic:\n\t//! set the debug level\n\tvoid SetDebugLevel(int debugLvl);\n\n\t//! get the debug level\n\tint GetDebugLevel();\n\n\t//! set the dump level (for cold restarts)\n\tvoid SetDumpLevel(int dumpLevel);\n\n\t//! get the dump level\n\tint GetDumpLevel() const;\n\n\t//! Set the dump stride\n\tvoid SetDumpStride(int n);\n\n\t//! get the dump stride\n\tint GetDumpStride() const;\n\n\t//! Set the log level\n\tvoid SetLogLevel(int logLevel);\n\n\t//! Get the stats \n\tModelStats GetModelStats() const;\n\tModelStats GetStepStats(size_t n) const;\n\tstd::vector<ModelStats> GetStepStats() const;\n\tstd::vector<TimeStepStats> GetTimeStepStats() const;\n\n\t// flag to show warnings and errors\n\tvoid ShowWarningsAndErrors(bool b);\n\tbool ShowWarningsAndErrors() const;\n\nprivate:\n\tvoid print_parameter(FEParam& p, int level = 0);\n\tvoid print_parameter_list(FEParameterList& pl, int level = 0);\n\tvoid print_parameter_list(FECoreBase* pc, int level = 0);\n\tvoid echo_input();\n\nprivate:\n\tvoid UpdatePlotObjects();\n\nprivate:\n\tTimer\t\tm_TotalTime;\t//!< timer to track total time\n\tTimer\t\tm_InputTime;\t//!< timer to track time to read model\n\tTimer\t\tm_IOTimer;\t\t//!< timer to track output (include plot, dump, and data)\n\n\tPlotFile*\tm_plot;\t\t\t//!< the plot file\n\tbool\t\tm_becho;\t\t//!< echo input to logfile\n\tint\t\t\tm_ndebug;\t\t//!< debug level flag\n\tbool\t\tm_writeMesh;\t//!< write a new mesh section\n\n\tbool\t\tm_bshowErrors;\t//!< print warnings and errors\n\n\tint\t\t\tm_logLevel;\t\t//!< output level for log file\n\n\tint\t\t\tm_dumpLevel;\t//!< level or writing restart file\n\tint\t\t\tm_dumpStride;\t//!< write dump file every nth iterations\n\nprivate:\n\t// accumulative statistics\n\tModelStats\tm_modelStats;\n\tstd::vector<ModelStats> m_stepStats;\n\tstd::vector<TimeStepStats> m_timestepStats;\n\nprotected: // file names\n\tstd::string\t\tm_sfile_title;\t\t//!< input file title \n\tstd::string\t\tm_sfile;\t\t\t//!< input file name (= path + title)\n\tstd::string\t\tm_splot;\t\t\t//!< plot output file name\n\tstd::string\t\tm_slog ;\t\t\t//!< log output file name\n\tstd::string\t\tm_sdump;\t\t\t//!< dump file name\n\n\tstd::string\tm_title;\t//!< model title\n\nprotected:\n\tbool\t\t\t\t\tm_pltAppendOnRestart;\n\tint\t\t\t\t\t\tm_lastUpdate;\n\nprivate:\n\tLogfile\tm_log;\n\tstd::string\tm_report;\n\tbool\tm_createReport;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioLib/FEBioModelBuilder.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioModelBuilder.h\"\n#include \"FEBioModel.h\"\n#include <FEBioMech/FEUncoupledMaterial.h>\n#include <FEBioMech/FEUDGHexDomain.h>\n#include <FEBioMech/FEUT4Domain.h>\n#include <FEBioMech/FESSIShellDomain.h>\n#include <FEBioMech/RigidBC.h>\n#include <FEBioMech/FERigidForce.h>\n#include <FEBioMech/FEMechModel.h>\n\n// In FEBio 3, the bulk modulus k must be defined at the top - level.\n// However, this could break backward compatibility, so for older file version\n// we apply this hack that collects the child moduli and assigns it to the top-level\nvoid FixUncoupledMaterial(FEUncoupledMaterial* mat)\n{\n\tdouble K = mat->m_K;\n\tfor (int i = 0; i < mat->Properties(); ++i)\n\t{\n\t\tFEUncoupledMaterial* mati = dynamic_cast<FEUncoupledMaterial*>(mat->GetProperty(i));\n\t\tif (mati)\n\t\t{\n\t\t\tFixUncoupledMaterial(mati);\n\t\t\tK += mati->m_K;\n\t\t\tmati->m_K = 0.0;\n\t\t}\n\t}\n\tmat->m_K = K;\n}\n\nFEBioModelBuilder::FEBioModelBuilder(FEBioModel& fem) : FEModelBuilder(fem)\n{\n\n}\n\nvoid FEBioModelBuilder::AddMaterial(FEMaterial* mat)\n{\n\tFEModel& fem = GetFEModel();\n\tfem.AddMaterial(mat);\n\n\t// For uncoupled materials, we collect the bulk moduli of child materials\n\t// and assign it to the top-level material (this one)\n\tFEUncoupledMaterial* pucm = dynamic_cast<FEUncoupledMaterial*>(mat);\n\tif (pucm) FixUncoupledMaterial(pucm);\n}\n\nFEDomain* FEBioModelBuilder::CreateDomain(FE_Element_Spec espec, FEMaterial* mat)\n{\n\tFEModel& fem = GetFEModel();\n\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tFEDomain* pdom = febio.CreateDomain(espec, &fem.GetMesh(), mat);\n\n\t// Handle dome special cases\n\t// TODO: Find a better way of dealing with these special cases\n\tFEUDGHexDomain* udg = dynamic_cast<FEUDGHexDomain*>(pdom);\n\tif (udg)\n\t{\n\t\tudg->SetHourGlassParameter(m_udghex_hg);\n\t}\n\n\tFEUT4Domain* ut4 = dynamic_cast<FEUT4Domain*>(pdom);\n\tif (ut4)\n\t{\n\t\tut4->SetUT4Parameters(m_ut4_alpha, m_ut4_bdev);\n\t}\n\n\tFESSIShellDomain* ssi = dynamic_cast<FESSIShellDomain*>(pdom);\n\tif (ssi) {\n\t\tssi->m_bnodalnormals = espec.m_shell_norm_nodal;\n\t}\n\n\treturn pdom;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioModelBuilder::AddRigidComponent(FEStepComponent* pmc)\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(GetFEModel());\n\n\tAddComponent(pmc);\n\n\tFERigidBC* prc = dynamic_cast<FERigidBC*>(pmc);\n\tif (prc) { fem.AddRigidBC(prc); return; }\n\n\tFERigidIC* ric = dynamic_cast<FERigidIC*>(pmc);\n\tif (ric) { fem.AddRigidInitialCondition(ric); return; }\n\n\tFEModelLoad* pml = dynamic_cast<FEModelLoad*>(pmc);\n\tif (pml) { AddModelLoad(pml); return; }\n\n\tassert(false);\n}\n"
  },
  {
    "path": "FEBioLib/FEBioModelBuilder.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FEBioXML/FEModelBuilder.h>\n\nclass FEBioModel;\n\nclass FEBioModelBuilder : public FEModelBuilder\n{\npublic:\n\tFEBioModelBuilder(FEBioModel& fem);\n\npublic:\n\tFEDomain* CreateDomain(FE_Element_Spec espec, FEMaterial* mat) override;\n\tvoid AddMaterial(FEMaterial* mat) override;\n\tvoid AddRigidComponent(FEStepComponent* prc) override;\n};\n\n"
  },
  {
    "path": "FEBioLib/FEBioRestart.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioRestart.h\"\n#include <FEBioLib/FEBioModel.h>\n#include <FECore/log.h>\n#include <FEBioXML/FERestartImport.h>\n#include <FECore/DumpFile.h>\n#include <FECore/FEAnalysis.h>\n#include \"FEBioModelBuilder.h\"\n\n//-----------------------------------------------------------------------------\nFEBioRestart::FEBioRestart(FEModel* pfem) : FECoreTask(pfem) {}\n\n//-----------------------------------------------------------------------------\nbool FEBioRestart::Init(const char* szfile)\n{\n\tFEBioModel& fem = static_cast<FEBioModel&>(*GetFEModel());\n\n\t// check the extension of the file\n\t// if the extension is .dmp or not given it is assumed the file\n\t// is a bindary archive (dump file). Otherwise it is assumed the\n\t// file is a restart input file.\n\tconst char* ch = strrchr(szfile, '.');\n\tif ((ch == 0) || (strcmp(ch, \".dmp\") == 0) || (strcmp(ch, \".DMP\") == 0))\n\t{\n\t\t// the file is binary so just read the dump file and return\n\n\t\t// open the archive\n\t\tDumpFile ar(fem);\n\t\tif (ar.Open(szfile) == false) { fprintf(stderr, \"FATAL ERROR: failed opening restart archive\\n\"); return false; }\n\n\t\t// read the archive\n\t\ttry\n\t\t{\n\t\t\tfem.Serialize(ar);\n\t\t}\n\t\tcatch (std::exception e)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: Exception occured while reading restart archive %s\\n%s\\n\", szfile, e.what());\n\t\t\treturn false;\n\t\t}\n\t\tcatch (...)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: failed reading restart data from archive %s\\n\", szfile);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// the file is assumed to be a xml-text input file\n\t\tFERestartImport file;\n\t\tfile.SetModelBuilder(new FEBioModelBuilder(fem));\n\t\tif (file.Load(fem, szfile) == false)\n\t\t{\n\t\t\tchar szerr[256];\n\t\t\tfile.GetErrorMessage(szerr);\n\t\t\tfprintf(stderr, \"%s\", szerr);\n\t\t\treturn false;\n\t\t}\n\n\t\t// get the number of new steps added\n\t\tint newSteps = file.StepsAdded();\n\t\tint step = fem.Steps() - newSteps;\n\n\t\t// Any additional steps that were created must be initialized\n\t\tfor (int i = step; i < fem.Steps(); ++i)\n\t\t{\n\t\t\tFEAnalysis* step = fem.GetStep(i);\n\t\t\tif (step->Init() == false) return false;\n\n\t\t\t// also initialize all the model components\n\t\t\tfor (int j = 0; j < step->StepComponents(); ++j)\n\t\t\t{\n\t\t\t\tFEStepComponent* pc = step->GetStepComponent(j);\n\t\t\t\tif (pc->Init() == false) return false;\n\t\t\t}\n\t\t}\n\n\t\t// see if user redefined restart file name\n\t\tif (file.m_szdmp[0]) fem.SetDumpFilename(file.m_szdmp);\n\t}\n\n\t// Open the log file for appending\n\tconst std::string& slog = fem.GetLogfileName();\n\tLogfile& felog = fem.GetLogFile();\n\tif (felog.append(slog.c_str()) == false)\n\t{\n\t\tprintf(\"WARNING: Could not reopen log file. A new log file is created\\n\");\n\t\tfelog.open(slog.c_str());\n\t\treturn false;\n\t}\n\n\t// inform the user from where the problem is restarted\n\tfelog.printbox(\" - R E S T A R T -\", \"Restarting from time %lg.\\n\", fem.GetCurrentTime());\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioRestart::Run()\n{\n\t// continue the analysis\n\treturn (GetFEModel() ? GetFEModel()->Solve() : false);\n}\n"
  },
  {
    "path": "FEBioLib/FEBioRestart.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FECoreTask.h>\n#include <FECore/FECoreKernel.h>\n\n//-----------------------------------------------------------------------------\n// Task that does a cold restart. \nclass FEBioRestart : public FECoreTask\n{\npublic:\n\tFEBioRestart(FEModel* pfem);\n\n\t//! initialization\n\tbool Init(const char* szfile) override;\n\n\t//! Run the FE model\n\tbool Run() override;\n};\n"
  },
  {
    "path": "FEBioLib/FEBioStdSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioStdSolver.h\"\n#include <FEBioLib/FEBioModel.h>\n#include <FECore/log.h>\n#include <FEBioXML/FERestartImport.h>\n#include <FECore/DumpFile.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEModelDataRecord.h>\n\n//-----------------------------------------------------------------------------\nFEBioStdSolver::FEBioStdSolver(FEModel* pfem) : FECoreTask(pfem) {}\n\n//-----------------------------------------------------------------------------\n// This simply calls the FEModel::Init\nbool FEBioStdSolver::Init(const char* szfile)\n{\n\treturn (GetFEModel() ? GetFEModel()->Init() : false);\n}\n\n//-----------------------------------------------------------------------------\n// This simply calls the FEM::Solve function which will solve the FE problem.\nbool FEBioStdSolver::Run()\n{\n\t// Solve the problem and return error code\n\treturn (GetFEModel() ? GetFEModel()->Solve() : false);\n}\n\n//=============================================================================\n\nFEBioRCISolver::FEBioRCISolver(FEModel* fem) : FECoreTask(fem) {}\n\n//! initialization\nbool FEBioRCISolver::Init(const char* szfile)\n{\n\treturn (GetFEModel() ? GetFEModel()->Init() : false);\n}\n\n//! Run the FE model\nbool FEBioRCISolver::Run()\n{\n\t// get the model\n\tFEModel* fem = GetFEModel();\n\tif (fem == nullptr) return false;\n\n\t// initialize RCI solver\n\tif (fem->RCI_Init() == false) return false;\n\n\t// loop until solved\n\twhile (fem->IsSolved() == false)\n\t{\n\t\t// try to advance the solution\n\t\tif (fem->RCI_Advance() == false)\n\t\t{\n\t\t\t// if we were unable to advance the solution, we do a rewind and try again\n\t\t\tif (fem->RCI_Rewind() == false)\n\t\t\t{\n\t\t\t\t// couldn't rewind, so we're done\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// finalize the solver\n\tif (fem->RCI_Finish() == false) return false;\n\n\treturn true;\n}\n\n//==========================================================================\nFEBioTestSuiteTask::FEBioTestSuiteTask(FEModel* fem) : FECoreTask(fem) {}\n\n//! initialization\nbool FEBioTestSuiteTask::Init(const char* szfile)\n{\n\tFEModel* fem = GetFEModel(); assert(fem);\n\tif (fem == nullptr) return false;\n\n\t// See if the model defines any data records\n\tDataStore& data = fem->GetDataStore();\n\tif (data.Size() == 0)\n\t{\n\t\tFEModelDataRecord* rec = new FEModelDataRecord(fem);\n\t\trec->SetData(\"solution_norm\");\n\t\trec->SetName(\"solution_norm\");\n\t\tdata.AddRecord(rec);\n\t}\n\n\treturn (GetFEModel() ? GetFEModel()->Init() : false);\n}\n\n//! Run the FE model\nbool FEBioTestSuiteTask::Run()\n{\n\treturn (GetFEModel() ? GetFEModel()->Solve() : false);\n}\n"
  },
  {
    "path": "FEBioLib/FEBioStdSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreTask.h>\n#include <FECore/FECoreKernel.h>\n\n//-----------------------------------------------------------------------------\n// This is the most commenly used task which will run a user-specified input \n// file. The results are stored in the logfile and the plotfile.\nclass FEBioStdSolver : public FECoreTask\n{\npublic:\n\tFEBioStdSolver(FEModel* pfem);\n\n\t//! initialization\n\tbool Init(const char* szfile) override;\n\n\t//! Run the FE model\n\tbool Run() override;\n};\n\n//-----------------------------------------------------------------------------\n// class for testing reverse communication interface of FEModel\nclass FEBioRCISolver : public FECoreTask\n{\npublic:\n\tFEBioRCISolver(FEModel* fem);\n\n\t//! initialization\n\tbool Init(const char* szfile) override;\n\n\t//! Run the FE model\n\tbool Run() override;\n};\n\n//-----------------------------------------------------------------------------\n// Configures the model for running in the nightly test suite. \nclass FEBioTestSuiteTask : public FECoreTask\n{\npublic:\n\tFEBioTestSuiteTask(FEModel* fem);\n\n\t//! initialization\n\tbool Init(const char* szfile) override;\n\n\t//! Run the FE model\n\tbool Run() override;\n};\n"
  },
  {
    "path": "FEBioLib/FEBox.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBox.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include <FECore/FEModel.h>\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEBoxMesh::FEBoxMesh(FEModel* fem) : FEMesh(fem)\n{\n\n}\n\nFEBoxMesh::~FEBoxMesh()\n{\n\n}\n\nvoid FEBoxMesh::Create(int nx, int ny, int nz, vec3d r0, vec3d r1, FE_Element_Type nhex)\n{\n\tint i, j, k, n;\n\n\t// make sure the parameters make sense\n\tassert((nx > 0) && (ny > 0) && (nz > 0));\n\n\t// count items\n\tint nodes = (nx+1)*(ny+1)*(nz+1);\n\tint elems = nx*ny*nz;\n\n\t// allocate data\n\tFEMesh::CreateNodes(nodes);\n\n\tFEModel* fem = GetFEModel();\n\tint MAX_DOFS = fem->GetDOFS().GetTotalDOFS();\n\tFEMesh::SetDOFS(MAX_DOFS);\n\n\t// create the nodes\n\tdouble x, y, z;\n\tn = 0;\n\tfor (i=0; i<=nx; ++i)\n\t{\n\t\tx = r0.x + ((r1.x - r0.x)*i)/nx;\n\t\tfor (j=0; j<=ny; ++j)\n\t\t{\n\t\t\ty = r0.y + ((r1.y - r0.y)*j)/ny;\n\t\t\tfor (k=0; k<=nz; ++k, ++n)\n\t\t\t{\n\t\t\t\tz = r0.z + ((r1.z - r0.z)*k)/nz;\n\n\t\t\t\tFENode& node = Node(n);\n\n\t\t\t\tnode.m_r0 = vec3d(x, y, z);\n\n\t\t\t\tnode.m_rt = node.m_r0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// create the elements\n\tint *en;\n\tn = 0;\n\tFEElasticSolidDomain* pbd = new FEElasticSolidDomain(fem);\n\tpbd->Create(elems, FEElementLibrary::GetElementSpecFromType(nhex));\n\tpbd->SetMatID(-1);\n\tAddDomain(pbd);\n\tfor (i=0; i<nx; ++i)\n\t{\n\t\tfor (j=0; j<ny; ++j)\n\t\t{\n\t\t\tfor (k=0; k<nz; ++k, ++n)\n\t\t\t{\n\t\t\t\tFESolidElement& el = pbd->Element(n);\n\n\t\t\t\tel.SetID(n+1);\n\n\t\t\t\ten = &el.m_node[0];\n\n\t\t\t\ten[0] = (i  )*(ny+1)*(nz+1) + (j  )*(nz+1) + (k  );\n\t\t\t\ten[1] = (i+1)*(ny+1)*(nz+1) + (j  )*(nz+1) + (k  );\n\t\t\t\ten[2] = (i+1)*(ny+1)*(nz+1) + (j+1)*(nz+1) + (k  );\n\t\t\t\ten[3] = (i  )*(ny+1)*(nz+1) + (j+1)*(nz+1) + (k  );\n\t\t\t\ten[4] = (i  )*(ny+1)*(nz+1) + (j  )*(nz+1) + (k+1);\n\t\t\t\ten[5] = (i+1)*(ny+1)*(nz+1) + (j  )*(nz+1) + (k+1);\n\t\t\t\ten[6] = (i+1)*(ny+1)*(nz+1) + (j+1)*(nz+1) + (k+1);\n\t\t\t\ten[7] = (i  )*(ny+1)*(nz+1) + (j+1)*(nz+1) + (k+1);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioLib/FEBox.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMesh.h\"\n\nclass FEBoxMesh : public FEMesh  \n{\npublic:\n\tFEBoxMesh(FEModel* fem);\n\tvirtual ~FEBoxMesh();\n\n\tvoid Create(int nx, int ny, int nz, vec3d r0, vec3d r1, FE_Element_Type nhex = FE_HEX8G8);\n};\n"
  },
  {
    "path": "FEBioLib/LogFileStream.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"LogFileStream.h\"\n\n//-----------------------------------------------------------------------------\nLogFileStream::LogFileStream()\n{\n\tm_fp = 0;\n}\n\n//-----------------------------------------------------------------------------\nLogFileStream::~LogFileStream()\n{\n\tclose();\n}\n\n//-----------------------------------------------------------------------------\nvoid LogFileStream::close()\n{\n\tif (m_fp) fclose(m_fp);\n\tm_fp = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid LogFileStream::flush()\n{\n\tif (m_fp) fflush(m_fp);\n}\n\n//-----------------------------------------------------------------------------\nbool LogFileStream::open(const char* szfile)\n{\n\tm_fileName = szfile;\n\tif (m_fp) close();\n\tm_fp = fopen(szfile, \"wt\");\n\treturn (m_fp != NULL);\n}\n\n//-----------------------------------------------------------------------------\nbool LogFileStream::append(const char* szfile)\n{\n\t// make sure we don't have a log file already open\n\tif (m_fp)\n\t{\n\t\tfseek(m_fp, SEEK_END, 0);\n\t\treturn true;\n\t}\n\n\t// create the log file\n\tm_fileName = szfile;\n\tm_fp = fopen(szfile, \"a+t\");\n\n\treturn (m_fp != NULL);\n}\n\n//-----------------------------------------------------------------------------\nvoid LogFileStream::print(const char* sztxt)\n{\n\tif (m_fp) fprintf(m_fp, \"%s\", sztxt);\n}\n"
  },
  {
    "path": "FEBioLib/LogFileStream.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"LogStream.h\"\n#include \"stdio.h\"\n#include <string>\n\n//-----------------------------------------------------------------------------\n// A stream that outputs to a file\nclass FEBIOLIB_API LogFileStream : public LogStream\n{\npublic:\n\t// constructor\n\tLogFileStream();\n\n\t// destructor\n\t~LogFileStream();\n\n\t// open the file\n\tbool open(const char* szfile);\n\n\t// open for appending\n\tbool append(const char* szfile);\n\n\t// close the file stream\n\tvoid close();\n\n\t// get the file handle\n\tFILE* GetFileHandle() { return m_fp; }\n\n\t// get the file name\n\tconst std::string& GetFileName() const { return m_fileName; }\n\npublic:\n\t// print text to the file\n\tvoid print(const char* sz);\n\n\t// flush the stream\n\tvoid flush();\n\nprivate:\n\tFILE*\t\tm_fp;\n\tstd::string\tm_fileName;\n};\n"
  },
  {
    "path": "FEBioLib/LogStream.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"LogStream.h\"\n#include <stdarg.h>\n#include <stdio.h>\n\nvoid LogStream::printf(const char* sz, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar sztxt[1024] = { 0 };\n\tva_start(args, sz);\n\tvsnprintf(sztxt, sizeof(sztxt), sz, args);\n\tva_end(args);\n\n\tprint(sztxt);\n}\n"
  },
  {
    "path": "FEBioLib/LogStream.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiolib_api.h\"\n\n//-----------------------------------------------------------------------------\n// class used to create an abstract interface to a screen\nclass FEBIOLIB_API LogStream\n{\npublic:\n\tLogStream() {}\n\tvirtual ~LogStream() {}\n\n\t// override function to print\n\tvirtual void print(const char* sz) = 0;\n\n\t// function to print variable output\n\tvoid printf(const char* sz, ...);\n\n\t// flush the stream\n\tvirtual void flush() {}\n};\n"
  },
  {
    "path": "FEBioLib/Logfile.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"Logfile.h\"\n#include <stdarg.h>\n#include <cstring>\n\n//-----------------------------------------------------------------------------\n// constructor for the Logfile class\nLogfile::Logfile()\n{\n\tm_fp = 0;\n\tm_ps = 0;\n\n\tm_mode = LOG_FILE_AND_SCREEN;\n}\n\n//-----------------------------------------------------------------------------\n// destructor for the Logfile class\n//\nLogfile::~Logfile()\n{\n\tclose();\n}\n\n//-----------------------------------------------------------------------------\n// open a file\n//\nbool Logfile::open(const char* szfile)\n{\n\tif (m_fp == 0) m_fp = new LogFileStream;\n\treturn m_fp->open(szfile);\n}\n\n//-----------------------------------------------------------------------------\n//  opens a file and prepares for appending\n//\nbool Logfile::append(const char* szfile)\n{\n\t// store a copy of the filename\n\tif (m_fp == 0) m_fp = new LogFileStream;\n\treturn m_fp->append(szfile);\n}\n\n//-----------------------------------------------------------------------------\n//! flush the logfile\nvoid Logfile::flush()\n{\n\tif (m_fp) m_fp->flush(); \n\tif (m_ps) m_ps->flush();\n}\n\n//-----------------------------------------------------------------------------\n//! close the logfile\nvoid Logfile::close()\n{ \n\tif (m_fp)\n\t{\n\t\tm_fp->close(); \n\t\tdelete m_fp;\n\t\tm_fp = 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function works like all other printf functions\n// with the exception that everything that is output to the file\n// is (optionally) also output to the screen.\n//\nvoid Logfile::printf(const char* sz, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar sztxt[2048] = {0};\n\tva_start(args, sz);\n\tvsnprintf(sztxt, sizeof(sztxt), sz, args);\n\tva_end(args);\n\t\n\t// print to file\n\tif (m_fp && (m_mode & LOG_FILE)) m_fp->print(sztxt);\n\n\t// print to screen\n\tif (m_ps && (m_mode & LOG_SCREEN)) m_ps->print(sztxt);\n}\n\n//-----------------------------------------------------------------------------\n// FUNCTION: Logfile::printbox\n// This function prints a message insided a box\n//\nvoid Logfile::printbox(const char* sztitle, const char* sz, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar sztxt[1024] = {0};\n\tva_start(args, sz);\n\tvsnprintf(sztxt, 1024, sz, args);\n\tva_end(args);\n\n\t// print the box\n\tchar szmsg[1024] = {0};\n\tchar* ch = szmsg;\n\tsnprintf(szmsg,sizeof(szmsg), \"\\n *************************************************************************\\n\"); ch += strlen(ch);\n\t// print the title\n\tif (sztitle)\n\t{\n\t\tint l = (int)strlen(sztitle);\n\t\tchar left[60] = {0};\n\t\tchar right[60] = {0};\n\t\tstrncpy(left, sztitle, l/2);\n\t\tstrncpy(right, sztitle+l/2, l - l/2);\n\n\t\tsize_t remaining = sizeof(szmsg) - (ch - szmsg);\n\t\tsnprintf(ch,remaining, \" * %33s\", left); ch += strlen(ch);\n\t\tremaining = sizeof(szmsg) - (ch - szmsg);\n\t\tsnprintf(ch,remaining, \"%-36s *\\n\", right); ch += strlen(ch);\n//\t\tsnprintf(ch,1024, \" *%71s*\\n\", \"\"); ch += strlen(ch);\n\t}\n\n\t// print the message\n\tchar* ct = sztxt, *cn;\n\tchar tmp;\n\tdo\n\t{\n\t\tcn = strchr(ct,'\\n');\n\t\tif (cn) *cn = 0;\n\t\tint l = (int)strlen(ct);\n\t\tbool wrap = false;\n\t\tint n = 69;\n\t\tif (l > 69) {\n\t\t\twhile ((n > 0) && (ct[n] != ' ')) n--;\n\t\t\tif (n == 0) n = 69;\n\t\t\ttmp = ct[n];\n\t\t\tct[n] = 0;\n\t\t\twrap = true;\n\t\t\tif (cn) *cn = '\\n';\n\t\t\tcn = ct + n; \n\t\t}\n\t\tsize_t remaining = sizeof(szmsg) - (ch - szmsg);\n\t\tsnprintf(ch,remaining,\" * %-69s *\\n\", ct); ch += strlen(ch);\n\t\tif (wrap) { ct[n] = tmp; }\n\t\tif (cn) ct = cn+1;\n\t}\n\twhile (cn);\n//\tsprintf(ch,\" *                                                                       *\\n\"); ch += strlen(ch);\n\tsize_t remaining = sizeof(szmsg) - (ch - szmsg);\n\tsnprintf(ch,remaining,\" *************************************************************************\\n\");\n\n\t// print the message\n\tprintf(szmsg);\n}\n\n\n//-----------------------------------------------------------------------------\n// Sets the Logfile mode. \n//\nLogfile::MODE Logfile::SetMode(Logfile::MODE mode)\n{\n\tMODE old = m_mode;\n\tm_mode = mode;\n\treturn old;\n}\n\n//! get the loggin mode\nLogfile::MODE Logfile::GetMode() \n{ \n\treturn m_mode; \n}\n"
  },
  {
    "path": "FEBioLib/Logfile.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <stdio.h>\n#include <string>\n#include \"LogFileStream.h\"\n\n//-----------------------------------------------------------------------------\n//! Class that is used for logging purposes\n\n//! This class can output to different \n//! files at the same time.\n//! At this time it outputs data to the screen (stdout) and to an external text file.\n//! Note that this class is implemented as a singleton, in other words, only one\n//! instance can be created.\n\nclass FEBIOLIB_API Logfile\n{\npublic:\n\tenum MODE { LOG_NEVER = 0, LOG_FILE = 1, LOG_SCREEN, LOG_FILE_AND_SCREEN };\n\npublic:\n\t//! constructor\n\tLogfile();\n\n\t//! destructor\n\t~Logfile();\n\n\t//! open a new logfile\n\tbool open(const char* szfile);\n\n\t//! append to existing file\n\tbool append(const  char* szfile);\n\n\t//! formatted printing\n\tvoid printf(const char* sz, ...);\n\n\t//! print a nice box\n\tvoid printbox(const char* sztitle, const char* sz, ...);\n\n\t//! set the loggin mode\n\tMODE SetMode(MODE mode);\n\n\t//! get the loggin mode\n\tMODE GetMode();\n\n\t//! flush the logfile\n\tvoid flush();\n\n\t//! close the logfile\n\tvoid close();\n\n\t//! return the file name\n\tconst std::string& FileName() { return m_fp->GetFileName(); }\n\n\t//! returns if the logfile is ready to be written to\n\tbool is_valid() { return (m_fp != 0); }\n\n\t// set the log stream\n\tvoid SetLogStream(LogStream* ps) { m_ps = ps; }\n\n\t// set the file log stream\n\tvoid SetFileStream(LogFileStream* fp) { m_fp = fp; }\n\n\t// return the file handle\n\toperator FILE* () { return (m_fp ? m_fp->GetFileHandle() : 0); }\n\nprivate:\n\t//! copy constructor is private so that you cannot create it directly\n\tLogfile(const Logfile& log){}\n\nprotected:\n\tLogFileStream*\tm_fp;\t//!< the actual log file\n\n\tLogStream*\tm_ps;\t//!< This stream is used to output to the screen\n\n\tMODE\tm_mode;\t//!< mode of log file\n};\n"
  },
  {
    "path": "FEBioLib/cmdoptions.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"cmdoptions.h\"\n#include \"febio.h\"\n#include <stdlib.h>\n\nstd::vector< std::string > split_string(const std::string& s)\n{\n\tstd::vector< std::string > args;\n\tstd::string t;\n\tbool inquote = false;\n\tfor (int n = 0; n < s.size(); ++n)\n\t{\n\t\tchar c = s[n];\n\t\tif (isspace(c) && (inquote== false))\n\t\t{\n\t\t\tif (t.empty() == false)\n\t\t\t{\n\t\t\t\targs.push_back(t);\n\t\t\t\tt.clear();\n\t\t\t}\n\t\t}\n\t\telse if (c == '\\\"')\n\t\t{\n\t\t\tif (inquote == false) inquote = true;\n\t\t\telse inquote = false;\n\t\t}\n\t\telse t.push_back(c);\n\t}\n\tif (t.empty() == false)\n\t{\n\t\targs.push_back(t);\n\t}\n\treturn args;\n}\n\nbool febio::ProcessOptionsString(const std::string& s, CMDOPTIONS& ops)\n{\n\t// split the string\n\tstd::vector< std::string > args = split_string(s);\n\n\t// set default options\n\tops.ndebug = 0;\n\tops.bsplash = true;\n\tops.bsilent = false;\n\tops.binteractive = true;\n\n\t// these flags indicate whether the corresponding file name\n\t// was defined on the command line. Otherwise, a default name will be generated.\n\tbool blog = false;\n\tbool bplt = false;\n\tbool bdmp = false;\n\tbool brun = true;\n\n\t// initialize file names\n\tops.szfile[0] = 0;\n\tops.szplt[0] = 0;\n\tops.szlog[0] = 0;\n\tops.szdmp[0] = 0;\n\tops.sztask[0] = 0;\n\tops.szctrl[0] = 0;\n\tops.szimp[0] = 0;\n\n\t// set initial configuration file name\n\tif (ops.szcnf[0] == 0)\n\t{\n\t\tchar szpath[1024] = { 0 };\n\t\tfebio::get_app_path(szpath, 1023);\n\t\tsnprintf(ops.szcnf, sizeof(ops.szcnf), \"%sfebio.xml\", szpath);\n\t}\n\n\t// loop over the arguments\n\tint nargs = args.size();\n\tfor (int i = 1; i < nargs; ++i)\n\t{\n\t\tconst char* sz = args[i].c_str();\n\n\t\tif (strcmp(sz, \"-r\") == 0)\n\t\t{\n\t\t\tif (ops.sztask[0] != 0) { fprintf(stderr, \"-r is incompatible with other command line option.\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, \"restart\");\n\t\t\tstrcpy(ops.szctrl, args[++i].c_str());\n\t\t\tops.binteractive = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-noappend\") == 0)\n\t\t{\n\t\t\tops.bappendFiles = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-d\") == 0)\n\t\t{\n\t\t\tif (ops.sztask[0] != 0) { fprintf(stderr, \"-d is incompatible with other command line option.\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, \"diagnose\");\n\t\t\tstrcpy(ops.szctrl, args[++i].c_str());\n\t\t\tops.binteractive = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-p\") == 0)\n\t\t{\n\t\t\tbplt = true;\n\t\t\tstrcpy(ops.szplt, args[++i].c_str());\n\t\t}\n\t\telse if (strncmp(sz, \"-dump\", 5) == 0)\n\t\t{\n\t\t\tops.dumpLevel = FE_DUMP_MAJOR_ITRS;\n\t\t\tif (sz[5] == '=') ops.dumpLevel = atoi(sz + 6);\n\t\t\tif ((ops.dumpLevel < 0) || (ops.dumpLevel > 3))\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: invalid restart level.\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (i < nargs - 1)\n\t\t\t{\n\t\t\t\tconst char* szi = args[i + 1].c_str();\n\t\t\t\tif (szi[0] != '-')\n\t\t\t\t{\n\t\t\t\t\t// assume this is the name of the dump file\n\t\t\t\t\tstrcpy(ops.szdmp, args[++i].c_str());\n\t\t\t\t\tbdmp = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(sz, \"-o\") == 0)\n\t\t{\n\t\t\tblog = true;\n\t\t\tstrcpy(ops.szlog, args[++i].c_str());\n\t\t}\n\t\telse if (strcmp(sz, \"-i\") == 0)\n\t\t{\n\t\t\t++i;\n\t\t\tconst char* szext = strrchr(sz, '.');\n\t\t\tif (szext == 0)\n\t\t\t{\n\t\t\t\t// we assume a default extension of .feb if none is provided\n\t\t\t\tsnprintf(ops.szfile, sizeof(ops.szfile), \"%s.feb\", sz);\n\t\t\t}\n\t\t\telse strcpy(ops.szfile, sz);\n\t\t\tops.binteractive = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-s\") == 0)\n\t\t{\n\t\t\tif (ops.sztask[0] != 0) { fprintf(stderr, \"-s is incompatible with other command line option.\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, \"optimize\");\n\t\t\tstrcpy(ops.szctrl, args[++i].c_str());\n\t\t}\n\t\telse if ((strcmp(sz, \"-g\") == 0) || (strcmp(sz, \"-g1\") == 0))\n\t\t{\n\t\t\tops.ndebug = 1;\n\t\t}\n\t\telse if (strcmp(sz, \"-g2\") == 0)\n\t\t{\n\t\t\tops.ndebug = 2;\n\t\t}\n\t\telse if (strcmp(sz, \"-nosplash\") == 0)\n\t\t{\n\t\t\t// don't show the welcome message\n\t\t\tops.bsplash = false;\n\t\t}\n\t\telse if (strcmp(sz, \"-silent\") == 0)\n\t\t{\n\t\t\t// no output to screen\n\t\t\tops.bsilent = true;\n\t\t}\n\t\telse if (strcmp(sz, \"-cnf\") == 0)\t// obsolete: use -config instead\n\t\t{\n\t\t\tstrcpy(ops.szcnf, args[++i].c_str());\n\t\t}\n\t\telse if (strcmp(sz, \"-config\") == 0)\n\t\t{\n\t\t\tstrcpy(ops.szcnf, args[++i].c_str());\n\t\t}\n\t\telse if (strcmp(sz, \"-noconfig\") == 0)\n\t\t{\n\t\t\tops.szcnf[0] = 0;\n\t\t}\n\t\telse if (strncmp(sz, \"-task\", 5) == 0)\n\t\t{\n\t\t\tif (sz[5] != '=') { fprintf(stderr, \"command line error when parsing task\\n\"); return false; }\n\t\t\tstrcpy(ops.sztask, sz + 6);\n\n\t\t\tif (i < nargs - 1)\n\t\t\t{\n\t\t\t\tconst char* szi = args[i + 1].c_str();\n\t\t\t\tif (szi[0] != '-')\n\t\t\t\t{\n\t\t\t\t\t// assume this is a control file for the specified task\n\t\t\t\t\tstrcpy(ops.szctrl, args[++i].c_str());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(sz, \"-import\") == 0)\n\t\t{\n\t\t\tstrcpy(ops.szimp, args[++i].c_str());\n\t\t}\n\t\telse if (sz[0] == '-')\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: Invalid command line option '%s'.\\n\", sz);\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// if no input file is given yet, we'll assume this is the input file\n\t\t\tif (ops.szfile[0] == 0)\n\t\t\t{\n\t\t\t\tconst char* szext = strrchr(sz, '.');\n\t\t\t\tif (szext == 0)\n\t\t\t\t{\n\t\t\t\t\t// we assume a default extension of .feb if none is provided\n\t\t\t\t\tsnprintf(ops.szfile, sizeof(ops.szfile), \"%s.feb\", sz);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstrcpy(ops.szfile, sz);\n\t\t\t\t}\n\t\t\t\tops.binteractive = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: Invalid command line option '%s'\\n\", sz);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// do some sanity checks\n\tif (strcmp(ops.sztask, \"optimize\") == 0)\n\t{\n\t\t// make sure we have an input file\n\t\tif (ops.szfile[0] == 0)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: no model input file was defined (use -i to define the model input file)\\n\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// if no task is defined, we assume a std solve is wanted\n\tif (ops.sztask[0] == 0) strcpy(ops.sztask, \"solve\");\n\n\t// derive the other filenames\n\tif (ops.szfile[0])\n\t{\n\t\tchar szbase[256]; strcpy(szbase, ops.szfile);\n\t\tchar* ch = strrchr(szbase, '.');\n\t\tif (ch) *ch = 0;\n\n\t\tchar szlogbase[256];\n\t\tif (ops.szctrl[0])\n\t\t{\n\t\t\tstrcpy(szlogbase, ops.szctrl);\n\t\t\tch = strrchr(szlogbase, '.');\n\t\t\tif (ch) *ch = 0;\n\t\t}\n\t\telse strcpy(szlogbase, szbase);\n\n\t\tif (!blog) snprintf(ops.szlog, sizeof(ops.szlog), \"%s.log\", szlogbase);\n\t\tif (!bplt) snprintf(ops.szplt, sizeof(ops.szplt), \"%s.xplt\", szbase);\n\t\tif (!bdmp) snprintf(ops.szdmp, sizeof(ops.szdmp), \"%s.dmp\", szbase);\n\t}\n\telse if (ops.szctrl[0])\n\t{\n\t\tchar szbase[256]; strcpy(szbase, ops.szfile);\n\t\tstrcpy(szbase, ops.szctrl);\n\t\tchar* ch = strrchr(szbase, '.');\n\t\tif (ch) *ch = 0;\n\n\t\tif (!blog) snprintf(ops.szlog, sizeof(ops.szlog), \"%s.log\", szbase);\n\t\tif (!bplt) snprintf(ops.szplt, sizeof(ops.szplt), \"%s.xplt\", szbase);\n\t\tif (!bdmp) snprintf(ops.szdmp, sizeof(ops.szdmp), \"%s.dmp\", szbase);\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioLib/cmdoptions.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febiolib_api.h\"\n#include <string>\nnamespace febio {\n\n//! This structures stores the command line options that were input by the user\nstruct FEBIOLIB_API CMDOPTIONS\n{\n\tenum { MAXFILE = 512 };\n\n\tint\t\tndebug;\t\t\t//!< debug flag\n\n\tbool\tbsplash;\t\t\t//!< show splash screen or not\n\tbool\tbsilent;\t\t\t//!< run FEBio in silent mode (no output to screen)\n\tbool\tbinteractive;\t\t//!< start FEBio interactively\n\tbool\tbappendFiles;\t\t//!< append plot and log files on restart?\n\tbool\tbupdateTitle;\t\t//!< update the console title with progress info\n\tbool\tboutputLog;\t\t\t//!< write a log file\n\n\tint\t\tdumpLevel;\t\t//!< requested restart level\n\tint\t\tdumpStride;\t\t//!< (cold) restart file stride\n\n\tchar\tszfile[MAXFILE];\t//!< model input file name\n\tchar\tszlog[MAXFILE];\t//!< log file name\n\tchar\tszplt[MAXFILE];\t//!< plot file name\n\tchar\tszdmp[MAXFILE];\t//!< dump file name\n\tchar\tszcnf[MAXFILE];\t//!< configuration file\n\tchar\tsztask[MAXFILE];\t//!< task name\n\tchar\tszctrl[MAXFILE];\t//!< control file for tasks\n\tchar\tszimp[MAXFILE];\t\t//!< import file\n\n\tCMDOPTIONS()\n\t{\n\t\tdefaults();\n\t}\n\n\tvoid defaults()\n\t{\n\t\tndebug = 0;\n\t\tbsplash = true;\n\t\tbsilent = false;\n\t\tbinteractive = false;\n\t\tdumpLevel = 0;\n\t\tdumpStride = 1;\n\t\tbappendFiles = true;\n\t\tbupdateTitle = true;\n\t\tboutputLog = true;\n\n\t\tszfile[0] = 0;\n\t\tszlog[0] = 0;\n\t\tszplt[0] = 0;\n\t\tszdmp[0] = 0;\n\t\tszcnf[0] = 0;\n\t\tsztask[0] = 0;\n\t\tszctrl[0] = 0;\n\t\tszimp[0] = 0;\n\t}\n};\n\n\t// process string for command line options\n\tFEBIOLIB_API bool ProcessOptionsString(const std::string& s, CMDOPTIONS& ops);\n}\n"
  },
  {
    "path": "FEBioLib/config.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"febio.h\"\n#include <FEBioXML/XMLReader.h>\n#include <FEBioXML/xmltool.h>\n#include <FECore/FEModel.h>\n#include <FECore/FECoreTask.h>\n#include <FECore/FEMaterial.h>\n#include <NumCore/MatrixTools.h>\n#include <FECore/LinearSolver.h>\n#include <FEBioTest/FEMaterialTest.h>\n#include \"plugin.h\"\n#include <map>\n#include <iostream>\n\n#ifndef WIN32\n#include <dlfcn.h>\n#endif\n\n#ifdef HAS_STD_FILESYSTEM\n    #include <filesystem>\n    namespace fs = std::filesystem;\n#endif\n\nnamespace febio {\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_tags(XMLTag& tag, bool readPlugins);\n\tbool parse_default_linear_solver(XMLTag& tag);\n\tbool parse_import(XMLTag& tag);\n\tbool parse_import_folder(XMLTag& tag);\n    bool parse_repo_plugins(XMLTag& tag);\n\tbool parse_set(XMLTag& tag);\n\tbool parse_output_negative_jacobians(XMLTag& tag);\n\n\t// create a map for the variables (defined with set)\n\tstatic std::map<string, string> vars;\n\tstatic bool boutput = true;\n\n\t//-----------------------------------------------------------------------------\n\t// configure FEBio\n\tbool Configure(const char* szfile, FEBioConfig& config)\n\t{\n\t\tvars.clear();\n\n\t\tboutput = (config.m_noutput != 0);\n\n\t\t// open the configuration file\n\t\tXMLReader xml;\n\t\tif (xml.Open(szfile) == false)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: Failed reading FEBio configuration file %s.\", szfile);\n\t\t\treturn false;\n\t\t}\n\n\t\tbool readPlugins = config.readPlugins;\n\n\t\tif (readPlugins)\n\t\t{\n\t\t\t// unload all plugins before reading new ones\n\t\t\tFEBioPluginManager& pm = *FEBioPluginManager::GetInstance();\n\t\t\tpm.UnloadAllPlugins();\n\t\t}\n\n\t\t// loop over all child tags\n\t\ttry\n\t\t{\n\t\t\t// Find the root element\n\t\t\tXMLTag tag;\n\t\t\tif (xml.FindTag(\"febio_config\", tag) == false) return false;\n\t\t\tconst char* szversion = tag.AttributeValue(\"version\");\n\t\t\tif (strcmp(szversion, \"3.0\") == 0)\n\t\t\t{\n\t\t\t\tif (!tag.isleaf())\n\t\t\t\t{\n\t\t\t\t\t// Read version 1.0\n\t\t\t\t\t++tag;\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\t// parse the tags\n\t\t\t\t\t\tif (tag == \"if_debug\")\n\t\t\t\t\t\t{\n#ifndef NDEBUG\n\t\t\t\t\t\t\t++tag;\n\t\t\t\t\t\t\tif (parse_tags(tag, readPlugins) == false) return false;\n\t\t\t\t\t\t\t++tag;\n#else\n\t\t\t\t\t\t\ttag.skip();\n#endif // NDEBUG\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (tag == \"if_release\")\n\t\t\t\t\t\t{\n#ifdef NDEBUG\n\t\t\t\t\t\t\t++tag;\n\t\t\t\t\t\t\tif (parse_tags(tag, readPlugins) == false) return false;\n\t\t\t\t\t\t\t++tag;\n#else\n\t\t\t\t\t\t\ttag.skip();\n#endif // NDEBUG\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (tag == \"print_model_params\")\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttag.value(config.m_printParams);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (tag == \"show_warnings_and_errors\")\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttag.value(config.m_bshowErrors);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (parse_tags(tag, readPlugins) == false) return false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t++tag;\n\t\t\t\t\t} \n\t\t\t\t\twhile (!tag.isend());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"FATAL ERROR: Invalid version for FEBio configuration file.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tcatch (XMLReader::Error& e)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: %s\\n\", e.what());\n\t\t\treturn false;\n\t\t}\n\t\tcatch (...)\n\t\t{\n\t\t\tfprintf(stderr, \"FATAL ERROR: unrecoverable error (line %d)\", xml.GetCurrentLine());\n\t\t\treturn false;\n\t\t}\n\n\t\txml.Close();\n\n\t\treturn true;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_tags(XMLTag& tag, bool readPlugins)\n\t{\n\t\tif (tag == \"set\")\n\t\t{\n\t\t\tif (parse_set(tag) == false) return false;\n\t\t}\n\t\telse if (tag == \"default_linear_solver\")\n\t\t{\n\t\t\tif (parse_default_linear_solver(tag) == false) return false;\n\t\t}\n\t\telse if (tag == \"import\")\n\t\t{\n\t\t\tif (readPlugins)\n\t\t\t{\n\t\t\t\tif (parse_import(tag) == false) return false;\n\t\t\t}\n\t\t\telse\n\t\t\t\ttag.skip();\n\t\t}\n\t\telse if (tag == \"import_folder\")\n\t\t{\n\t\t\tif (readPlugins)\n\t\t\t{\n\t\t\t\tif (parse_import_folder(tag) == false) return false;\n\t\t\t}\n\t\t\telse tag.skip();\n\t\t}\n        else if (tag == \"repo_plugin_xml\")\n        {\n\t\t\tif (readPlugins)\n\t\t\t{\n\t\t\t\tif (parse_repo_plugins(tag) == false) return false;\n\t\t\t}\n\t\t\telse\n\t\t\t\ttag.skip();\n        }\n\t\telse if (tag == \"output_negative_jacobians\")\n\t\t{\n\t\t\tif (parse_output_negative_jacobians(tag) == false) return false;\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\treturn true;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_set(XMLTag& tag)\n\t{\n\t\tchar szname[256] = { 0 };\n\t\tstrcpy(szname, tag.AttributeValue(\"name\"));\n\t\tstring key(szname);\n\t\tstring val(tag.szvalue());\n\t\tvars[key] = val;\n\t\treturn true;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_output_negative_jacobians(XMLTag& tag)\n\t{\n\t\tint n;\n\t\ttag.value(n);\n\t\tNegativeJacobian::m_maxout = n;\n\t\treturn true;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_default_linear_solver(XMLTag& tag)\n\t{\n\t\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t\t// read the solver parameters\n\t\tFEClassDescriptor* cd = fexml::readParameterList(tag);\n\t\tif (cd == nullptr)\n\t\t{\n\t\t\tdelete cd;\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// set this as the default solver\n\t\t\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\t\t\tfecore.SetDefaultSolver(cd);\n\t\t}\n\n\t\treturn true;\n\t}\n\t\n\t//-----------------------------------------------------------------------------\n\tbool process_aliases(char* szout, const char* szbuf)\n\t{\n\t\tbool bok = true;\n\t\tchar sztmp[64] = { 0 };\n\t\tconst char* ch = szbuf;\n\t\tchar* s = szout;\n\t\twhile (*ch)\n\t\t{\n\t\t\tif (*ch == '$')\n\t\t\t{\n\t\t\t\t++ch;\n\t\t\t\tif (*ch++ == '(')\n\t\t\t\t{\n\t\t\t\t\tconst char* ch2 = strchr(ch, ')');\n\t\t\t\t\tif (ch2)\n\t\t\t\t\t{\n\t\t\t\t\t\tint l = (int)(ch2 - ch);\n\t\t\t\t\t\tstrncpy(sztmp, ch, l);\n\t\t\t\t\t\tsztmp[l] = 0;\n\t\t\t\t\t\tstring key(sztmp);\n\t\t\t\t\t\tch = ch2 + 1;\n\t\t\t\t\t\tstd::map<string, string>::iterator it = vars.find(key);\n\t\t\t\t\t\tif (it != vars.end())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstring v = it->second;\n\t\t\t\t\t\t\tconst char* sz = v.c_str();\n\t\t\t\t\t\t\twhile (*sz) *s++ = *sz++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse { bok = false; break; }\n\t\t\t\t\t}\n\t\t\t\t\telse { bok = false; break; }\n\n\t\t\t\t}\n\t\t\t\telse { bok = false; break; }\n\t\t\t}\n\t\t\telse *s++ = *ch++;\n\t\t}\n\t\treturn bok;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_import(XMLTag& tag)\n\t{\n\t\t// get the file name\n\t\tconst char* szfile = tag.szvalue();\n\n\t\t// process any aliases\n\t\tchar szbuf[1024] = { 0 };\n\t\tbool bok = process_aliases(szbuf, szfile);\n\n\t\t// load the plugin\n\t\tif (bok) febio::ImportPlugin(szbuf);\n\n\t\treturn bok;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool parse_import_folder(XMLTag& tag)\n\t{\n\t\t// get the folder name\n\t\tconst char* szfolder = tag.szvalue();\n\n\t\t// process any aliases\n\t\tchar szbuf[1024] = { 0 };\n\t\tbool bok = process_aliases(szbuf, szfolder);\n\n\t\t// load the plugin\n\t\tif (bok) bok = febio::ImportPluginFolder(szbuf);\n\n\t\treturn bok;\n\t}\n\n    //-----------------------------------------------------------------------------\n    bool parse_repo_plugins(XMLTag& tag)\n    {\n        // get the file name\n\t\tconst char* szfile = tag.szvalue();\n\n\t\t// process any aliases\n\t\tchar szbuf[1024] = { 0 };\n\t\tbool bok = process_aliases(szbuf, szfile);\n\n\t\t// load the plugin\n\t\tif (bok) febio::ImportRepoPlugins(szbuf);\n\n\t\treturn bok;\n    }\n\n\t//-----------------------------------------------------------------------------\n\tconst char* GetFileTitle(const char* szfile)\n\t{\n\t\tconst char* ch = strrchr(szfile, '\\\\');\n\t\tif (ch == 0) {\n\t\t\tch = strrchr(szfile, '/');\n\t\t\tif (ch == 0) ch = szfile; else ch++;\n\t\t}\n\t\telse ch++;\n\t\treturn ch;\n\t}\n\n\t//-----------------------------------------------------------------------------\n\tbool ImportPlugin(const char* szfile)\n\t{\n\t\tconst char* sztitle = GetFileTitle(szfile);\n\t\tFEBioPluginManager* pPM = FEBioPluginManager::GetInstance();\n\t\tPLUGIN_INFO info;\n\t\tint nerr = pPM->LoadPlugin(szfile, info);\n\t\tswitch (nerr)\n\t\t{\n\t\tcase 0: if (boutput) fprintf(stderr, \"Success loading plugin %s (version %d.%d.%d)\\n\", sztitle, info.major, info.minor, info.patch); return true; break;\n\t\tcase 1:\n\t\t\tfprintf(stderr, \"Failed loading plugin %s\\n Reason: Failed to load the file.\\n\\n\", szfile);\n#ifndef WIN32\n\t\t\tfprintf(stderr, \"dlopen failed: %s\\n\\n\", dlerror());\n#endif\n\t\t\tbreak;\n\t\tcase 2: fprintf(stderr, \"Failed loading plugin %s\\n Reason: Required plugin function PluginNumClasses not found.\\n\\n\", szfile); break;\n\t\tcase 3: fprintf(stderr, \"Failed loading plugin %s\\n Reason: Required plugin function PluginGetFactory not found.\\n\\n\", szfile); break;\n\t\tcase 4: fprintf(stderr, \"Failed loading plugin %s\\n Reason: Invalid number of classes returned by PluginNumClasses.\\n\\n\", szfile); break;\n\t\tcase 5: fprintf(stderr, \"Failed loading plugin %s\\n Reason: Required plugin function GetSDKVersion not found.\\n\\n\", szfile); break;\n\t\tcase 6: fprintf(stderr, \"Failed loading plugin %s\\n Reason: Invalid SDK version.\\n\\n\", szfile); break;\n\t\tcase 7: fprintf(stderr, \"Failed loading plugin %s\\n Reason: Plugin is already loaded.\\n\\n\", szfile); break;\n\t\tdefault:\n\t\t\tfprintf(stderr, \"Failed loading plugin %s\\n Reason: unspecified.\\n\\n\", szfile); break;\n\t\t}\n\n\t\treturn false;\n\t}\n\n//-----------------------------------------------------------------------------\n\nbool ImportPluginFolder(const char* szfolder)\n{\n#ifdef HAS_STD_FILESYSTEM\n    // get the default (system-dependant) extension\n    #ifdef WIN32\n        std::string extension = \".dll\";\n    #elif __APPLE__\n        std::string extension = \".dylib\";\n    #else\n        std::string extension = \".so\";\n    #endif\n    \n    for (const auto& entry : fs::directory_iterator(szfolder)) \n    {\n        if (entry.is_regular_file() && entry.path().extension() == extension)\n        {\n            // try to load the plugin\n            bool ok = ImportPlugin(entry.path().string().c_str());\n\n            if(!ok) return false;\n        }\n    }\n\n    return true;\n#else\n    fprintf(stderr, \"This version of FEBio does not support the import_folder tag.\\n\");\n    return false;\n#endif\n}\n\nvoid ImportRepoPlugins(const char* szxmlFile)\n{\n    XMLReader xml;\n    if (xml.Open(szxmlFile))\n    {\n        XMLTag tag;\n\n        if(!xml.FindTag(\"plugins\", tag)) return;\n\n        if(!tag.isleaf())\n        {\n            ++tag;\n            do\n            {\n                if(tag == \"plugin\")\n                {\n                    int ID = tag.AttributeValue(\"ID\", 0);\n\n                    if(!tag.isleaf())\n                    {\n                        ++tag;\n                        do\n                        {\n                            if (tag == \"file\")\n                            {\n                                int main = tag.AttributeValue(\"main\", 1);\n\n                                std::string filePath;\n                                tag.value(filePath);\n\n                                if(main == 1)\n                                {\n                                    ImportPlugin(filePath.c_str());\n                                }\n                            }\n                            ++tag;\n                        } while(!tag.isend());\n                    }\n                }\n                ++tag;\n            } while(!tag.isend());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEBIOLIB_API const char* GetPluginName(int allocId)\n{\n\tFEBioPluginManager& pm = *FEBioPluginManager::GetInstance();\n\n\tfor (int i = 0; i < pm.Plugins(); ++i)\n\t{\n\t\tconst FEBioPlugin& pi = pm.GetPlugin(i);\n\t\tif (pi.GetAllocatorID() == allocId)\n\t\t{\n\t\t\treturn pi.GetName();\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// run an FEBioModel\nFEBIOLIB_API bool SolveModel(FEBioModel& fem, const char* sztask, const char* szctrl)\n{\n\t// Make sure we have a task\n\tif (sztask == nullptr) sztask = \"solve\";\n\n\t// find a task\n\tFECoreTask* ptask = fecore_new<FECoreTask>(sztask, &fem);\n\tif (ptask == 0)\n\t{\n\t\tfprintf(stderr, \"Don't know how to do task: %s\\n\", sztask);\n\t\treturn false;\n\t}\n\n\t// initialize the task\n\tif (ptask->Init(szctrl) == false)\n\t{\n\t\tfprintf(stderr, \"Failed initializing the task: %s\\n\", sztask);\n\t\treturn false;\n\t}\n\n\t// run the task\n\tbool bret = true;\n\ttry {\n\t\tbret = ptask->Run();\n\t}\n\tcatch (std::exception e)\n\t{\n\t\tfprintf(stderr, \"\\nException detected: %s\\n\\n\", e.what());\n\t\tbret = false;\n\t}\n\n\treturn bret;\n}\n\n//-----------------------------------------------------------------------------\n// run an FEBioModel\nFEBIOLIB_API int RunModel(FEBioModel& fem, CMDOPTIONS* ops)\n{\n\t// set options that were passed on the command line\n\tif (ops)\n\t{\n\t\tfem.SetDebugLevel(ops->ndebug);\n\t\tfem.SetDumpLevel(ops->dumpLevel);\n\n\t\t// set the output filenames\n\t\tfem.SetLogFilename(ops->szlog);\n\t\tfem.SetPlotFilename(ops->szplt);\n\t\tfem.SetDumpFilename(ops->szdmp);\n\t}\n\n\t// read the input file if specified\n\tint nret = 0;\n\tif (ops && ops->szfile[0])\n\t{\n\t\t// read the input file\n\t\tif (fem.Input(ops->szfile) == false) nret = 1;\n\t}\n\n\t// solve the model with the task and control file\n\tif (nret == 0)\n\t{\n\t\tconst char* sztask = (ops && ops->sztask[0] ? ops->sztask : nullptr);\n\t\tconst char* szctrl = (ops && ops->szctrl[0] ? ops->szctrl : nullptr);\n\t\tbool b = febio::SolveModel(fem, sztask, szctrl);\n\t\tnret = (b ? 0 : 1);\n\t}\n\n\treturn nret;\n}\n\n// write a matrix to file\nbool write_hb(CompactMatrix& K, const char* szfile, int mode)\n{\n\treturn NumCore::write_hb(K, szfile, mode);\n}\n\n// print matrix sparsity pattern to svn file\nvoid print_svg(CompactMatrix* m, std::ostream &out, int i0, int j0, int i1, int j1)\n{\n\tNumCore::print_svg(m, out, i0, j0, i1, j1);\n}\n\n// write a vector to file\nbool write_vector(const vector<double>& a, const char* szfile, int mode)\n{\n\treturn NumCore::write_vector(a, szfile, mode);\n}\n\nbool RunMaterialTest(FEMaterial* mat, double simtime, int steps, double strain, const char* sztest, std::vector<pair<double, double> >& out)\n{\n\tFEModel fem;\n\n\tFEMaterial* matcopy = dynamic_cast<FEMaterial*>(CopyFEBioClass(mat, &fem));\n\tif (matcopy == nullptr) return false;\n\n\tfem.AddMaterial(matcopy);\n\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\tFEMaterialTest diag(&fem);\n\tdiag.SetOutputFileName(nullptr);\n\n\tFEDiagnosticScenario* s = diag.CreateScenario(sztest);\n\ts->GetParameterList();\n\ts->SetParameter<double>(\"strain\", strain);\n\n\tFEAnalysis* step = fem.GetStep(0);\n\tstep->m_ntime = steps;\n\tstep->m_dt0 = simtime / steps;\n\tfem.SetCurrentStepIndex(0);\n\n\tif (diag.Init() == false) return false;\n\n\tif (fem.Init() == false) return false;\n\n\tbool b = diag.Run();\n\n\tif (b)\n\t{\n\t\tout = diag.GetOutputData();\n\t}\n\n\treturn b;\n}\n\n} // namespace febio\n"
  },
  {
    "path": "FEBioLib/febio.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <cstddef>\n#include \"febiolib_api.h\"\n#include \"FEBioModel.h\"\n#include \"FEBioConfig.h\"\n#include \"cmdoptions.h\"\n#include <ostream>\n\nclass CompactMatrix;\nclass LogStream;\n\n//-----------------------------------------------------------------------------\n// Defines the FEBio namespace\nnamespace febio \n{\n\t// get the kernel\n\tFEBIOLIB_API FECoreKernel* GetFECoreKernel();\n\n\t// Initialize all the FEBio modules\n\tFEBIOLIB_API void InitLibrary();\n\n\t// read the configuration file\n\tFEBIOLIB_API bool Configure(const char* szfile, FEBioConfig& config);\n\n\t// load a plugin\n\tFEBIOLIB_API bool ImportPlugin(const char* szfile);\n\n\t// load all the plugins in a folder\n\tFEBIOLIB_API bool ImportPluginFolder(const char* szfolder);\n\n    // load the plugins as defined in the repo's XML file\n\tFEBIOLIB_API void ImportRepoPlugins(const char* szxmlFile);\n\n\t// get the name of the plugin from its allocator Id\n\tFEBIOLIB_API const char* GetPluginName(int allocId);\n\n\t// call this to clean up all FEBio data\n\tFEBIOLIB_API void FinishLibrary();\n\n\t// helper function for retrieving the executable's path\n\tFEBIOLIB_API int get_app_path(char *pname, size_t pathsize);\n\n\t// print hello message\n\tFEBIOLIB_API int Hello(LogStream& log);\n\n\t// set the number of OMP threads\n\tFEBIOLIB_API void SetOMPThreads(int n);\n\n\t// run an FEBioModel\n\tFEBIOLIB_API bool SolveModel(FEBioModel& fem, const char* sztask = nullptr, const char* szctrl = nullptr);\n\n\t// run an FEBioModel\n\tFEBIOLIB_API int RunModel(FEBioModel& fem, CMDOPTIONS* ops);\n\n\t// write a matrix to file\n\tFEBIOLIB_API bool write_hb(CompactMatrix& K, const char* szfile, int mode = 0);\n\n\t// print matrix sparsity pattern to svn file\n\tFEBIOLIB_API void print_svg(CompactMatrix* m, std::ostream &out, int i0 = 0, int j0 = 0, int i1 = -1, int j1 = -1);\n\n\t// write a vector to file\n\tFEBIOLIB_API bool write_vector(const vector<double>& a, const char* szfile, int mode = 0);\n\n\t// run a material test\n\tFEBIOLIB_API bool RunMaterialTest(FEMaterial* mat, double simtime, int steps, double strain, const char* sztest, std::vector<pair<double, double> >& out);\n}\n"
  },
  {
    "path": "FEBioLib/febiolib.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECore/FECore.h\"\n#include \"NumCore/NumCore.h\"\n#include \"FEAMR/FEAMR.h\"\n#include \"FEBioMech/FEBioMechModule.h\"\n#ifndef MECH_ONLY\n#include \"FEBioMix/FEBioMix.h\"\n#include \"FEBioOpt/FEBioOpt.h\"\n#include \"FEBioFluid/FEBioFluid.h\"\n#include <FEBioFluid/FEBioFSI.h>\n#include <FEBioFluid/FEBioMultiphasicFSI.h>\n#include <FEBioFluid/FEBioFluidSolutes.h>\n#include <FEBioFluid/FEBioThermoFluid.h>\n#include <FEBioFluid/FEBioPolarFluid.h>\n#include <FEBioTest/FEBioTest.h>\n#include <FEBioRVE/FEBioRVE.h>\n#include <FEImgLib/FEImgLib.h>\n#endif\n#include \"febio.h\"\n#include \"plugin.h\"\n#include \"FEBioStdSolver.h\"\n#include \"FEBioRestart.h\"\n\nnamespace febio {\n\n//-----------------------------------------------------------------------------\nFECoreKernel* GetFECoreKernel()\n{\n\treturn &FECoreKernel::GetInstance();\n}\n\n//-----------------------------------------------------------------------------\n// import all modules\nvoid InitLibrary()\n{\n\tREGISTER_FECORE_CLASS(FEBioStdSolver, \"solve\");\n\tREGISTER_FECORE_CLASS(FEBioRestart  , \"restart\");\n\tREGISTER_FECORE_CLASS(FEBioRCISolver, \"rci_solve\");\n\tREGISTER_FECORE_CLASS(FEBioTestSuiteTask, \"test\");\n\n\tFECore::InitModule();\n\tFEAMR::InitModule();\n\tNumCore::InitModule();\n\tFEBioMech::InitModule();\n\n#ifndef MECH_ONLY\n\tFEBioMix::InitModule();\n\tFEBioOpt::InitModule();\n\tFEBioFluid::InitModule();\n\tFEBioFSI::InitModule();\n    FEBioMultiphasicFSI::InitModule();\n    FEBioFluidSolutes::InitModule();\n    FEBioThermoFluid::InitModule();\n    FEBioPolarFluid::InitModule();\n\tFEBioTest::InitModule();\n\tFEBioRVE::InitModule();\n\tFEImgLib::InitModule();\n#endif\n}\n\n//-----------------------------------------------------------------------------\nvoid FinishLibrary()\n{\n\tFEBioPluginManager* pPM = FEBioPluginManager::GetInstance();\n\tpPM->DeleteThis();\n}\n\n} // namespace febio\n"
  },
  {
    "path": "FEBioLib/febiolib_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febiolib_EXPORTS\n\t\t\t#define FEBIOLIB_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOLIB_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIOLIB_API\n\t#endif\n#else\n\t#define FEBIOLIB_API\n#endif\n"
  },
  {
    "path": "FEBioLib/febiolib_types.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\nstruct ModelStats {\n\tint\t\tntimeSteps = 0;\t\t//!< total nr of time steps\n\tint\t\tntotalIters = 0;\t//!< total nr of equilibrium iterations\n\tint\t\tntotalRHS = 0;\t\t//!< total nr of right hand side evaluations\n\tint\t\tntotalReforms = 0;\t//!< total nr of stiffness reformations\n\tdouble  solutionNorm = 0.0; //!< norm of solution\n};\n\nstruct TimingInfo {\n\tdouble total_time = 0;\n\tdouble input_time = 0;\n\tdouble init_time = 0;\n\tdouble solve_time = 0;\n\tdouble io_time = 0;\n\tdouble total_ls_factor = 0;\n\tdouble total_ls_backsolve = 0;\n\tdouble total_reform = 0;\n\tdouble total_stiff = 0;\n\tdouble total_rhs = 0;\n\tdouble total_update = 0;\n\tdouble total_qn = 0;\n\tdouble total_serialize = 0;\n\tdouble total_callback = 0;\n\tdouble total_other = 0;\n};\n\nstruct TimeStepStats {\n\tint iters = 0;\n\tint nrhs = 0;\n\tint refs = 0;\n\tint status = 0; // 0 = failed, 1 = converged\n};\n"
  },
  {
    "path": "FEBioLib/getapppath.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"febio.h\"\n\n#ifdef WIN32\n#include \"windows.h\"\n#endif\n\n#ifdef LINUX\n#include <unistd.h>\n#include <string.h>\n#endif // LINUX\n\n#ifdef __APPLE__\n#include <sys/param.h>\n#include <mach-o/dyld.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#endif\n\n//-----------------------------------------------------------------------------\n// This function determines *where* the exe actually lives\n// This code is from Ian MacArthur from the FLTK forum: fltk.general \n//\n\nint febio::get_app_path (char *pname, size_t pathsize)\n{\n\tlong result;\n\tint status = -1;\n#ifdef LINUX\n\t/* Oddly, the readlink(2) man page says no NULL is appended. */\n\t/* So you have to do it yourself, based on the return value: */\n\tpathsize --; /* Preserve a space to add the trailing NULL */\n\tresult = readlink(\"/proc/self/exe\", pname, pathsize);\n\tif (result > 0)\n\t{\n\t\tpname[result] = 0; /* add the #@!%ing NULL */\n\n\t\tif ((access(pname, 0) == 0))\n\t\tstatus = 0; /* file exists, return OK */\n\t\t          /*else name doesn't seem to exist, return FAIL (falls through) */\n\t}\n#endif /* LINUX */\n\n#ifdef WIN32\n\tresult = GetModuleFileNameA(GetModuleHandle(NULL), (LPCH) pname, pathsize);\n\tif (result > 0)\n\t{\n\t\t/* fix up the dir slashes... */\n\t\tint len = strlen(pname);\n\t\tint idx;\n\t\tfor (idx = 0; idx < len; idx++)\n\t\t{\n\t\t\tif (pname[idx] == '\\\\') pname[idx] = '/';\n\t\t}\n\t\tstatus = 0; /* file exists, return OK */\n\t\t          /*else name doesn't seem to exist, return FAIL (falls through) */\n\t}\n#endif /* WIN32 */\n\n\n#ifdef __APPLE__ /* assume this is OSX */\n/*\n        from http://www.hmug.org/man/3/NSModule.html\n\n        extern int _NSGetExecutablePath(char *buf, unsigned long *bufsize);\n\n        _NSGetExecutablePath  copies  the  path  of the executable\n        into the buffer and returns 0 if the path was successfully\n        copied  in the provided buffer. If the buffer is not large\n        enough, -1 is returned and the  expected  buffer  size  is\n        copied  in  *bufsize.  Note that _NSGetExecutablePath will\n        return \"a path\" to the executable not a \"real path\" to the\n        executable.  That  is  the path may be a symbolic link and\n        not the real file. And with  deep  directories  the  total\n        bufsize needed could be more than MAXPATHLEN.\n*/\n\tchar given_path[MAXPATHLEN * 2];\n\n\tpathsize = MAXPATHLEN * 2;\n\tuint32_t nsize = (uint32_t) (pathsize);\n\tresult = _NSGetExecutablePath(given_path, &nsize);\n\tif (result == 0)\n\t{ /* OK, we got something - now try and resolve the real path...*/\n\t\tif (realpath(given_path, pname) != NULL)\n\t\t{\n\t\t\tif ((access(pname, 0) == 0))\n\t\t\tstatus = 0; /* file exists, return OK */\n\t\t}\n\t}\n#endif /* APPLE */\n\n\tif (status == 0)\n\t{\n\t\t// remove the application's name\n\t\tchar* ch = strrchr(pname, '\\\\');\n\t\tif (ch == 0) ch = strrchr(pname, '/');\n\t\tif (ch) ch[1] = 0;\n\t}\n\n\treturn status;\n}\n"
  },
  {
    "path": "FEBioLib/hello.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"version.h\"\n#include \"LogStream.h\"\n#include \"febio.h\"\n#include <stdio.h>\n\nint febio::Hello(LogStream& log)\n{\n\tchar szversion[128] = { 0 };\n\tchar* szvernum = getVersionString();\n\tsnprintf(szversion, sizeof(szversion), \"  version %s\\n\", szvernum);\n\t\n\tlog.print(\"===========================================================================\\n\");\n\tlog.print(\"         ________    _________   _______       __     _________            \\n\");\n\tlog.print(\"        |        |\\\\ |        |\\\\ |       \\\\\\\\    |  |\\\\  /         \\\\\\\\          \\n\");\n\tlog.print(\"        |    ____|| |    ____|| |    __  ||   |__|| |    ___    ||         \\n\");\n\tlog.print(\"        |   |\\\\___\\\\| |   |\\\\___\\\\| |   |\\\\_| ||    \\\\_\\\\| |   //  \\\\   ||         \\n\");\n\tlog.print(\"        |   ||__    |   ||__    |   ||_| ||   |  |\\\\ |  ||    |  ||         \\n\");\n\tlog.print(\"        |       |\\\\  |       |\\\\  |         \\\\\\\\  |  || |  ||    |  ||         \\n\");\n\tlog.print(\"        |    ___||  |    ___||  |    ___   || |  || |  ||    |  ||         \\n\");\n\tlog.print(\"        |   |\\\\__\\\\|  |   |\\\\__\\\\|  |   |\\\\__|  || |  || |  ||    |  ||         \\n\");\n\tlog.print(\"        |   ||      |   ||___   |   ||__|  || |  || |   \\\\\\\\__/   ||         \\n\");\n\tlog.print(\"        |   ||      |        |\\\\ |          || |  || |           ||         \\n\");\n\tlog.print(\"        |___||      |________|| |_________//  |__||  \\\\_________//          \\n\");\n\tlog.print(\"                                                                           \\n\");\n\tlog.print(\"      F I N I T E   E L E M E N T S   F O R   B I O M E C H A N I C S      \\n\");\n\tlog.print(\"                                                                           \\n\");\n\tlog.print(szversion);\n\tlog.print(\"  FEBio is a registered trademark.                                         \\n\");\n\tlog.print(\"  copyright (c) 2006-2026 - All rights reserved                            \\n\");\n\tlog.print(\"                                                                           \\n\");\n\tlog.print(\"===========================================================================\\n\");\n\tlog.print(\"\\n\");\n\n\treturn 0;\n}\n \n"
  },
  {
    "path": "FEBioLib/input.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioModel.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/tens3d.h>\n#include <FEBioMech/FERigidJoint.h>\n#include <FEBioMech/FERigidSphericalJoint.h>\n#include <FEBioPlot/FEBioPlotFile.h>\n#include <FECore/FEMaterial.h>\n#include <FEBioMech/FERigidBody.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FEInitialCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEBodyLoad.h>\n#include <FECore/FEModelParam.h>\n#include <FECore/FELoadCurve.h>\n#include <FECore/FEMeshAdaptor.h>\n#include <FECore/FESurfacePairConstraint.h>\n#include <FECore/FETimeStepController.h>\n#include <FECore/log.h>\n#include <string.h>\n\n//-----------------------------------------------------------------------------\n// helper function to print a parameter to the logfile\nvoid FEBioModel::print_parameter(FEParam& p, int level)\n{\n\tchar sz[512] = {0};\n\tint l = (int)strlen(p.name()) + 2*level;\n\tsnprintf(sz, sizeof(sz), \"\\t%*s %.*s\", l, p.name(), 50-l, \"..................................................\");\n\tif (p.dim() == 1)\n\t{\n\t\tswitch (p.type())\n\t\t{\n\t\tcase FE_PARAM_DOUBLE : feLog(\"%s : %lg\\n\", sz, p.value<double>()); break;\n\t\tcase FE_PARAM_INT    : \n\t\t{\n\t\t\tif (p.enums())\n\t\t\t{\n\t\t\t\tint n = p.value<int>();\n\t\t\t\tconst char* szkey = p.enumKey();\n\t\t\t\tif (szkey)\n\t\t\t\t{\n\t\t\t\t\tfeLog(\"%s : %s (%d)\\n\", sz, szkey, n);\n\t\t\t\t}\n\t\t\t\telse feLog(\"%s : %d\\n\", sz, n);\n\t\t\t}\n\t\t\telse feLog(\"%s : %d\\n\", sz, p.value<int   >());\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_BOOL   : feLog(\"%s : %s\\n\" , sz, (p.value<bool>() ? \"yes (1)\" : \"no (0)\")); break;\n\t\tcase FE_PARAM_STRING : feLog(\"%s : %s\\n\" , sz, p.cvalue()); break;\n\t\tcase FE_PARAM_STD_STRING: feLog(\"%s : %s\\n\", sz, p.value<string>().c_str()); break;\n\t\tcase FE_PARAM_VEC3D  :\n\t\t\t{\n\t\t\t\tvec3d v = p.value<vec3d>();\n\t\t\t\tfeLog(\"%s : %lg,%lg,%lg\\n\", sz, v.x, v.y, v.z);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_MAT3DS :\n\t\t\t{\n\t\t\t\tmat3ds m = p.value<mat3ds>();\n\t\t\t\tfeLog(\"%s : %lg,%lg,%lg,%lg,%lg,%lg\\n\", sz, m.xx(), m.yy(), m.zz(), m.xy(), m.yz(), m.xz());\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_MAT3D:\n\t\t\t{\n\t\t\t\tmat3d m = p.value<mat3d>();\n\t\t\t\tfeLog(\"%s : %lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg\\n\", sz, m(0,0), m(0,1), m(0,2), m(1,0), m(1,1), m(1,2), m(2,0), m(2,1), m(2,2));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_TENS3DRS:\n\t\t\t{\n\t\t\t\ttens3drs m = p.value<tens3drs>();\n\t\t\t\tdouble* d = m.d;\n\t\t\t\tfeLog(\"%s : %lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg\\n\", sz,d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8],d[9],d[10],d[11],d[12],d[13],d[14],d[15],d[16],d[17]);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamDouble& v = p.value<FEParamDouble>();\n\t\t\t\tif (v.isConst())\n\t\t\t\t{\n\t\t\t\t\tdouble a = v.constValue();\n\t\t\t\t\tfeLog(\"%s : %lg\\n\", sz, a);\n\t\t\t\t}\n\t\t\t\telse feLog(\"%s : (not constant)\\n\", sz);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_VEC3D_MAPPED:\n\t\t{\n\t\t\tFEParamVec3& v = p.value<FEParamVec3>();\n\t\t\tif (v.isConst())\n\t\t\t{\n\t\t\t\tvec3d a = v.constValue();\n\t\t\t\tfeLog(\"%s : %lg, %lg, %lg\\n\", sz, a.x, a.y, a.z);\n\t\t\t}\n\t\t\telse feLog(\"%s : (not constant)\\n\", sz);\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_STD_VECTOR_INT:\n\t\t{\n\t\t\tstd::vector<int>& v = p.value<std::vector<int> >();\n\t\t\tint nsize = (int)v.size();\n\t\t\tint n = (nsize <= 5 ? nsize : 5);\n\t\t\tfeLog(\"%s : \", sz);\n\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t{\n\t\t\t\tfeLog(\"%d\", v[i]);\n\t\t\t\tif (i != n - 1) feLog(\", \");\n\t\t\t}\n\t\t\tif (nsize > n) feLog(\", ...\\n\");\n\t\t\telse feLog(\"\\n\");\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\tfeLog(\"%s : (can't display value)\\n\", sz);\n\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\tswitch (p.type())\n\t\t{\n\t\tcase FE_PARAM_INT:\n\t\tcase FE_PARAM_DOUBLE:\n\t\t\t{\n\t\t\t\tint n = p.dim();\n\t\t\t\tfeLog(\"%s : \", sz);\n\t\t\t\tfor (int k=0; k<n; ++k)\n\t\t\t\t{\n\t\t\t\t\tswitch (p.type())\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_PARAM_INT   : feLog(\"%d\" , p.pvalue<int   >()[k]); break;\n\t\t\t\t\tcase FE_PARAM_DOUBLE: feLog(\"%lg\", p.pvalue<double>()[k]); break;\n                    default: break;\n\t\t\t\t\t}\n\t\t\t\t\tif (k!=n-1) feLog(\",\"); else feLog(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t{\n\t\t\tint n = p.dim();\n\t\t\tfeLog(\"%s : \", sz);\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tFEParamDouble& v = p.value<FEParamDouble>(k);\n\t\t\t\tif (v.isConst())\n\t\t\t\t\tfeLog(\"%lg\", v.constValue());\n\t\t\t\telse\n\t\t\t\t\tfeLog(\"???\");\n\t\t\t\tif (k != n - 1) feLog(\",\"); else feLog(\"\\n\");\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// print the parameter list to the log file\nvoid FEBioModel::print_parameter_list(FEParameterList& pl, int level)\n{\n\tint n = pl.Parameters();\n\tif (n > 0)\n\t{\n\t\tFEParamIterator it = pl.first();\n\t\tfor (int j = 0; j < n; ++j, ++it)\n\t\t{\n\t\t\tif (it->IsHidden() == false)\n\t\t\t{\n\t\t\t\tprint_parameter(*it, level);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//------------------------------------------------------------------------------\nvoid FEBioModel::print_parameter_list(FECoreBase* pc, int level)\n{\n\tprint_parameter_list(pc->GetParameterList(), level);\n\tfor (int i=0; i<pc->PropertyClasses(); ++i)\n\t{\n\t\tFEProperty* prop = pc->PropertyClass(i);\n\t\tint n = prop->size();\n\t\tfor (int j=0; j<n; ++j)\n\t\t{\n\t\t\tfeLog(\"\\t%s: \", prop->GetName());\n\t\t\tFECoreBase* pcj = prop->get(j);\n\t\t\tif (pcj)\n\t\t\t{\n\t\t\t\tfeLog(\"(type: %s)\\n\", pcj->GetTypeStr());\n\t\t\t\tprint_parameter_list(pcj, level + 1);\n\t\t\t}\n\t\t\telse feLog(\"(unspecified)\\n\");\n\t\t}\n\t}\n}\n\n//------------------------------------------------------------------------------\n//! This function outputs the input data to the felog file.\nvoid FEBioModel::echo_input()\n{\n\t// get the FE mesh\n\tFEBioModel& fem = *this;\n\tFEMesh& mesh = GetMesh();\n\n\t// print title\n\tfeLog(\"%s\\n\\n\", fem.GetTitle().c_str());\n\n\t// print file info\n\tfeLog(\" FILES USED\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\tfeLog(\"\\tInput file : %s\\n\", fem.GetInputFileName().c_str());\n\tfeLog(\"\\tPlot file  : %s\\n\", fem.GetPlotFileName().c_str());\n\tfeLog(\"\\tLog file   : %s\\n\", fem.GetLogfileName().c_str());\n\tfeLog(\"\\n\\n\");\n\n\t// print mesh info\n\tfeLog(\" MESH INFO\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\tfeLog(\"\\tNumber of materials ............................ : %d\\n\", fem.Materials());\n\tfeLog(\"\\tNumber of domains .............................. : %d\\n\", mesh.Domains());\n\tfeLog(\"\\tNumber of nodes ................................ : %d\\n\", mesh.Nodes());\n\tint nsolid = mesh.Elements(FE_DOMAIN_SOLID    ); if (nsolid > 0) feLog(\"\\tNumber of solid elements ....................... : %d\\n\", nsolid);\n\tint nshell = mesh.Elements(FE_DOMAIN_SHELL    ); if (nshell > 0) feLog(\"\\tNumber of shell elements ....................... : %d\\n\", nshell);\n\tint nbeam  = mesh.Elements(FE_DOMAIN_BEAM     ); if (nbeam  > 0) feLog(\"\\tNumber of beam elements ........................ : %d\\n\", nbeam );\n\tint nelm2d = mesh.Elements(FE_DOMAIN_2D       ); if (nelm2d > 0) feLog(\"\\tNumber of 2D elements .......................... : %d\\n\", nelm2d);\n\tfeLog(\"\\n\\n\");\n\n\tfeLog(\" MODULE\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\tstring modName = fem.GetModuleName();\n\tconst char* szmod = modName.c_str();\n\tif (szmod == 0) { szmod = \"unknown\"; assert(false); }\n\tfeLog(\"\\tModule type ....................................... : %s\\n\", szmod);\n\tfeLog(\"\\n\\n\");\n\n\t// print control info\n\tif (fem.Steps() == 1)\n\t{\n\t\t// get the analysis step\n\t\tFEAnalysis& step = *fem.GetStep(0);\n\n\t\tfeLog(\" CONTROL DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tprint_parameter_list(step.GetParameterList());\n\n\t\tfeLog(\"\\tAuto time stepper activated ....................... : %s\\n\", (step.m_timeController ? \"yes\" : \"no\"));\n\t\tif (step.m_timeController)\n\t\t{\n\t\t\tFETimeStepController& tc = *step.m_timeController;\n\t\t\tprint_parameter_list(tc.GetParameterList(), 1);\n\t\t}\n\n\t\t// output solver data\n\t\tfeLog(\" SOLVER PARAMETERS\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\n\t\tFESolver* psolver = step.GetFESolver();\n\t\tif (psolver)\n\t\t{\n\t\t\tprint_parameter_list(psolver->GetParameterList());\n\t\t\tfeLog(\"\\n\\n\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tfeLog(\" STEP DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tfor (int i = 0; i < fem.Steps(); ++i)\n\t\t{\n\t\t\tif (i > 0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\t\tfeLog(\"step %3d - \", i + 1);\n\n\t\t\t// get the step\n\t\t\tFEAnalysis* pstep = fem.GetStep(i);\n\n\t\t\t// get the name and type string\n\t\t\tconst char* szname = pstep->GetName().c_str();\n\t\t\tconst char* sztype = pstep->GetTypeStr();\n\t\t\tif (szname[0] == 0) szname = 0;\n\n\t\t\t// print type and name\n\t\t\tfeLog(\"%s\", (szname ? szname : \"(noname)\"));\n\t\t\tfeLog(\" (type: %s)\", sztype);\n\t\t\tfeLog(\"\\n\");\n\n\t\t\t// print the parameter list\n\t\t\tprint_parameter_list(pstep);\n\t\t}\n\t}\n\tfeLog(\"\\n\\n\");\n\n\t// material data\n\tfeLog(\"\\n\\n\");\n\tfeLog(\" MATERIAL DATA\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\tfor (int i=0; i<fem.Materials(); ++i)\n\t{\n\t\tif (i>0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\tfeLog(\"%3d - \", i+1);\n\n\t\t// get the material\n\t\tFEMaterial* pmat = fem.GetMaterial(i);\n\n\t\t// get the material name and type string\n\t\tconst char* szname = pmat->GetName().c_str();\n\t\tconst char* sztype = pmat->GetTypeStr();\n\t\tif (szname[0] == 0) szname = 0;\n\n\t\t// print type and name\n\t\tfeLog(\"%s\", (szname?szname:\"unknown\"));\n\t\tfeLog(\" (type: %s)\", sztype);\n\t\tfeLog(\"\\n\");\n\n\t\t// print the parameter list\n\t\tprint_parameter_list(pmat);\n\t}\n\tfeLog(\"\\n\\n\");\n\n\tif (fem.RigidBodies())\n\t{\n\t\tfeLog(\" RIGID BODY DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tfor (int i=0; i<fem.RigidBodies(); ++i)\n\t\t{\n\t\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\t\tif (i>0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\t\tfeLog(\"Rigid Body %d:\\n\", rb.m_nID+1);\n\t\t\tfeLog(\"\\tmaterial id    : %d\\n\", rb.m_mat+1);\n\t\t\tfeLog(\"\\tname           : %s\\n\", rb.GetName().c_str());\n\t\t\tfeLog(\"\\tcenter of mass : %lg, %lg, %lg\\n\", rb.m_r0.x, rb.m_r0.y, rb.m_r0.z);\n            feLog(\"\\tmass           : %lg\\n\", rb.m_mass);\n            feLog(\"\\tIxx Ixy Ixz    : %lg, %lg, %lg\\n\", rb.m_moi.xx(), rb.m_moi.xy(), rb.m_moi.xz());\n            feLog(\"\\tIxy Iyy Iyz    : %lg, %lg, %lg\\n\", rb.m_moi.xy(), rb.m_moi.yy(), rb.m_moi.yz());\n            feLog(\"\\tIxz Iyz Izz    : %lg, %lg, %lg\\n\", rb.m_moi.xz(), rb.m_moi.yz(), rb.m_moi.zz());\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tif (fem.InitialConditions())\n\t{\n\t\tfeLog(\" INITIAL CONDITION DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tfor (int i = 0; i < fem.InitialConditions(); ++i)\n\t\t{\n\t\t\tif (i > 0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\t\tfeLog(\"%3d - \", i + 1);\n\n\t\t\t// get the initial condition\n\t\t\tFEInitialCondition* pic = fem.InitialCondition(i);\n\n\t\t\t// get the type string\n\t\t\tconst char* sztype = pic->GetTypeStr();\n\t\t\tif (sztype == 0) sztype = \"unknown\";\n\t\t\tfeLog(\" Type: %s\\n\", sztype);\n\n\t\t\t// print the parameter list\n\t\t\tFEParameterList& pl = pic->GetParameterList();\n\t\t\tprint_parameter_list(pl);\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tif (fem.BoundaryConditions())\n\t{\n\t\tfeLog(\" BOUNDARY CONDITION DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tfor (int i = 0; i < fem.BoundaryConditions(); ++i)\n\t\t{\n\t\t\tif (i > 0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\t\tfeLog(\"%3d - \", i + 1);\n\n\t\t\t// get the bc\n\t\t\tFEBoundaryCondition* pbc = fem.BoundaryCondition(i);\n\n\t\t\t// get the type string\n\t\t\tconst char* sztype = pbc->GetTypeStr();\n\t\t\tif (sztype == 0) sztype = \"unknown\";\n\t\t\tfeLog(\" Type: %s\\n\", sztype);\n\n\t\t\t// print the parameter list\n\t\t\tFEParameterList& pl = pbc->GetParameterList();\n\t\t\tprint_parameter_list(pl);\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tif (fem.ModelLoads())\n\t{\n\t\tfeLog(\" MODEL LOAD DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tfor (int i = 0; i < fem.ModelLoads(); ++i)\n\t\t{\n\t\t\tif (i > 0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\t\tfeLog(\"%3d - \", i + 1);\n\n\t\t\t// get the model load\n\t\t\tFEModelLoad* pml = fem.ModelLoad(i);\n\n\t\t\t// get the type string\n\t\t\tconst char* sztype = pml->GetTypeStr();\n\t\t\tif (sztype == 0) sztype = \"unknown\";\n\t\t\tfeLog(\" Type: %s\\n\", sztype);\n\n\t\t\t// print the parameter list\n\t\t\tFEParameterList& pl = pml->GetParameterList();\n\t\t\tprint_parameter_list(pl);\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tif (fem.SurfacePairConstraints() > 0)\n\t{\n\t\tfeLog(\" CONTACT INTERFACE DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t\t{\n\t\t\tif (i>0) feLog(\"---------------------------------------------------------------------------\\n\");\n\n\t\t\tFESurfacePairConstraint* pi = fem.SurfacePairConstraint(i);\n\t\t\tconst char* sztype = pi->GetTypeStr();\n\t\t\tif (sztype == 0) sztype = \"unknown\";\n\t\t\tfeLog(\"contact interface %d - Type: %s\\n\", i+1, sztype);\n\t\t\tFEParameterList& pl = pi->GetParameterList();\n\t\t\tprint_parameter_list(pl);\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tif (fem.NonlinearConstraints() != 0)\n\t{\n\t\tfeLog(\" NONLINEAR CONSTRAINT DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tint NC = fem.NonlinearConstraints();\n\t\tfor (int i = 0; i<NC; ++i)\n\t\t{\n\t\t\tFENLConstraint* plc = fem.NonlinearConstraint(i); assert(plc);\n\t\t\tconst char* sztype = plc->GetTypeStr();\n\t\t\tif (sztype == 0) sztype = \"unknown\";\n\t\t\tfeLog(\"\\nnonlinear constraint %d - Type: %s\\n\", i + 1, sztype);\n\t\t\tFEParameterList& pl = plc->GetParameterList();\n\t\t\tprint_parameter_list(pl);\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tif (fem.MeshAdaptors())\n\t{\n\t\tfeLog(\" MESH ADAPTOR DATA\\n\");\n\t\tfeLog(\"===========================================================================\\n\");\n\t\tint NMA = fem.MeshAdaptors();\n\t\tfor (int i = 0; i < NMA; ++i)\n\t\t{\n\t\t\tif (i > 0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\t\tFEMeshAdaptor* pma = fem.MeshAdaptor(i); assert(pma);\n\t\t\tconst char* sztype = pma->GetTypeStr();\n\t\t\tif (sztype == 0) sztype = \"unknown\";\n\t\t\tfeLog(\"\\nmesh adaptor %d - Type: %s\\n\", i + 1, sztype);\n\t\t\tFEParameterList& pl = pma->GetParameterList();\n\t\t\tprint_parameter_list(pl);\n\t\t}\n\t\tfeLog(\"\\n\\n\");\n\t}\n\n\tfeLog(\" LOAD CONTROLLER DATA\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\tfor (int i = 0; i < fem.LoadControllers(); ++i)\n\t{\n\t\tif (i > 0) feLog(\"---------------------------------------------------------------------------\\n\");\n\t\tfeLog(\"load controller %3d - \", i + 1);\n\n\t\t// get the load controller\n\t\tFELoadController* plc = fem.GetLoadController(i);\n\n\t\t// get the name and type string\n\t\tconst char* szname = plc->GetName().c_str();\n\t\tconst char* sztype = plc->GetTypeStr();\n\t\tif (szname[0] == 0) szname = 0;\n\n\t\t// print type and name\n\t\tfeLog(\"%s\", (szname ? szname : \"(noname)\"));\n\t\tfeLog(\" (type: %s)\", sztype);\n\t\tfeLog(\"\\n\");\n\n\t\t// print the parameter list\n\t\tprint_parameter_list(plc);\n\t}\n\tfeLog(\"\\n\\n\");\n\n\t// print output data\n\tfeLog(\" OUTPUT DATA\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\n\tPlotFile* pplt = fem.GetPlotFile();\n\tif (dynamic_cast<FEBioPlotFile*>(pplt))\n\t{\n\t\tFEBioPlotFile* pf = dynamic_cast<FEBioPlotFile*>(pplt);\n\t\tfeLog(\"\\tplotfile format ........................... : FEBIO\\n\");\n\n\t\tFEBioPlotFile::Dictionary& dic = pf->GetDictionary();\n\n\t\tfor (int i = 0; i < 5; ++i)\n\t\t{\n\t\t\tlist<FEBioPlotFile::DICTIONARY_ITEM>* pl = 0;\n\t\t\tconst char* szn = 0;\n\t\t\tswitch (i)\n\t\t\t{\n\t\t\tcase 0: pl = &dic.GlobalVariableList(); szn = \"Global Variables\"; break;\n\t\t\tcase 1: pl = &dic.MaterialVariableList(); szn = \"Material Variables\"; break;\n\t\t\tcase 2: pl = &dic.NodalVariableList(); szn = \"Nodal Variables\"; break;\n\t\t\tcase 3: pl = &dic.DomainVariableList(); szn = \"Domain Variables\"; break;\n\t\t\tcase 4: pl = &dic.SurfaceVariableList(); szn = \"Surface Variables\"; break;\n\t\t\t}\n\n\t\t\tif (!pl->empty())\n\t\t\t{\n\t\t\t\tfeLog(\"\\t\\t%s:\\n\", szn);\n\t\t\t\tlist<FEBioPlotFile::DICTIONARY_ITEM>::const_iterator it;\n\t\t\t\tfor (it = pl->begin(); it != pl->end(); ++it)\n\t\t\t\t{\n\t\t\t\t\tconst char* szt = 0;\n\t\t\t\t\tswitch (it->m_ntype)\n\t\t\t\t\t{\n\t\t\t\t\tcase PLT_FLOAT: szt = \"float\"; break;\n\t\t\t\t\tcase PLT_VEC3F: szt = \"vec3f\"; break;\n\t\t\t\t\tcase PLT_MAT3FS: szt = \"mat3fs\"; break;\n\t\t\t\t\tcase PLT_MAT3FD: szt = \"mat3fd\"; break;\n\t\t\t\t\tcase PLT_TENS4FS: szt = \"tens4fs\"; break;\n\t\t\t\t\tcase PLT_MAT3F: szt = \"mat3f\"; break;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst char* szf = 0;\n\t\t\t\t\tswitch (it->m_nfmt)\n\t\t\t\t\t{\n\t\t\t\t\tcase FMT_NODE: szf = \"NODE\"; break;\n\t\t\t\t\tcase FMT_ITEM: szf = \"ITEM\"; break;\n\t\t\t\t\tcase FMT_MULT: szf = \"COMP\"; break;\n\t\t\t\t\tcase FMT_REGION: szf = \"REGION\"; break;\n\t\t\t\t\t}\n\n\t\t\t\t\tfeLog(\"\\t\\t\\t%-20s (type = %5s, format = %4s)\\n\", it->m_szname, szt, szf);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tfeLog(\" LINEAR SOLVER DATA\\n\");\n\tfeLog(\"===========================================================================\\n\");\n\tfeLog(\"\\tDefault linear solver ............................. : %s\\n\", fecore.GetLinearSolverType());\n\tFECoreFactory* fac = fecore.FindFactoryClass(FELINEARSOLVER_ID, fecore.GetLinearSolverType());\n\tif (fac) print_parameter_list(fac->GetParameterList());\n\tfeLog(\"\\n\");\n}\n"
  },
  {
    "path": "FEBioLib/memory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"febiolib_api.h\"\n#include <stddef.h>\n#ifdef WIN32\n#include <windows.h>\n#include <psapi.h>\n#endif\n\nsize_t FEBIOLIB_API GetPeakMemory()\n{\n#ifdef WIN32\n\tPROCESS_MEMORY_COUNTERS memCounters;\n\tGetProcessMemoryInfo(GetCurrentProcess(), &memCounters, sizeof(memCounters));\n\treturn (size_t)memCounters.PeakWorkingSetSize;\n#else\n\treturn 0;\n#endif\n}\n"
  },
  {
    "path": "FEBioLib/plugin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"plugin.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FECore/log.h\"\n\n#ifdef WIN32\n#include <direct.h>\n#endif\n#ifdef LINUX\n#include <dlfcn.h>\n#endif\n#ifdef __APPLE__\n#include <dlfcn.h>\n#endif\n\n//=============================================================================\n// Typedefs of the plugin functions.\n// These are the functions that the plugin must implement\nextern \"C\" {\n\ttypedef unsigned int (*PLUGIN_GETSDKVERSION)();\n\ttypedef void (*PLUGIN_INIT_FNC)(FECoreKernel&);\n\ttypedef void (*PLUGIN_CLEANUP_FNC)();\n\ttypedef int  (*PLUGIN_NUMCLASSES_FNC)();\n\ttypedef FECoreFactory* (*PLUGIN_GETFACTORY_FNC)(int i);\n\ttypedef void (*PLUGIN_VERSION_FNC)(int&,int&,int&);\n}\n\n//=============================================================================\n// Wrappers for system calls\n#ifdef WIN32\nFEBIO_PLUGIN_HANDLE LoadPlugin(const char* szfile) { return LoadLibraryA(szfile); }\nvoid* FindPluginFunc(FEBIO_PLUGIN_HANDLE ph, const char* szfunc) { return GetProcAddress(ph, szfunc); }\nbool UnloadPlugin(FEBIO_PLUGIN_HANDLE ph) { return (FreeLibrary(ph) == TRUE); }\n#endif\n#ifdef LINUX\nFEBIO_PLUGIN_HANDLE LoadPlugin(const char* szfile) { return dlopen(szfile, RTLD_NOW); }\nvoid* FindPluginFunc(FEBIO_PLUGIN_HANDLE ph, const char* szfunc) { return dlsym(ph, szfunc); }\nbool UnloadPlugin(FEBIO_PLUGIN_HANDLE ph) { return dlclose(ph) == 0; }\n#endif\n#ifdef __APPLE__\nFEBIO_PLUGIN_HANDLE LoadPlugin(const char* szfile) { return dlopen(szfile, RTLD_NOW); }\nvoid* FindPluginFunc(FEBIO_PLUGIN_HANDLE ph, const char* szfunc) { return dlsym(ph, szfunc); }\nbool UnloadPlugin(FEBIO_PLUGIN_HANDLE ph) { return dlclose(ph) == 0; }\n#endif\n\n//=============================================================================\n// FEBioPlugin\n//=============================================================================\nFEBioPlugin::FEBioPlugin()\n{\n\tm_ph = 0;\n\tm_szname[0] = 0;\n\n\tm_version.major = 0;\n\tm_version.minor = 0;\n\tm_version.patch = 0;\n\n\tm_allocater_id = FECoreKernel::GetInstance().GenerateAllocatorID();\n}\n\n//-----------------------------------------------------------------------------\nFEBioPlugin::~FEBioPlugin()\n{\n\tif (m_ph) UnLoad();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlugin::SetNameFromFilePath(const char* szfile)\n{\n\tconst char* ch = strrchr(szfile, '\\\\');\n\tif (ch==0) \n\t{\n\t\tch = strrchr(szfile, '/'); \n\t\tif (ch==0) ch = szfile; else ch++;\n\t} else ch++;\n\tif (ch) strcpy(m_szname, ch);\n}\n\n//-----------------------------------------------------------------------------\n//! This function tries to load a plugin from file.\n//! \\return Return values are: \n//! (0) success, \n//! (1) Failed to load the file, \n//! (2) Required plugin function PluginNumClasses not found (NOTE: as of 2.5 this error is not returned anymore since PluginNumClasses is obsolete)\n//! (3) Required plugin function PluginGetFactory not found,\n//! (4) Invalid number of classes returned by PluginNumClasses.\n//! (5) Required plugin function GetSDKVersion not found.\n//! (6) Invalid SDK version.\nint FEBioPlugin::Load(const char* szfile)\n{\n\t// Make sure the plugin is not loaded already\n\tassert(m_ph == 0);\n\tif (m_ph) return 0;\n\n\t// set the file name as the plugin name\n\tSetNameFromFilePath(szfile);\n\n\t// load the library\n\tFEBIO_PLUGIN_HANDLE ph = LoadPlugin(szfile);\n\tif (ph == NULL) return 1;\n\n\t// get the GetSDKVersion function\n\tPLUGIN_GETSDKVERSION pf_sdk = (PLUGIN_GETSDKVERSION) FindPluginFunc(ph, \"GetSDKVersion\");\n\tif (pf_sdk == 0)\n\t{\n\t\tUnloadPlugin(ph);\n\t\tm_ph = 0;\n\t\treturn 5;\n\t}\n\n\t// get the SDK version of the plugin\n\tunsigned int n = FE_SDK_VERSION;\n\tunsigned int version = pf_sdk();\n\tif (version != FE_SDK_VERSION)\n\t{\n\t\tUnloadPlugin(ph);\n\t\tm_ph = 0;\n\t\treturn 6;\n\t}\n\n\t// find the numclasses function\n\tPLUGIN_NUMCLASSES_FNC pfnc_cnt = (PLUGIN_NUMCLASSES_FNC) FindPluginFunc(ph, \"PluginNumClasses\");\n\t\n\t// NOTE: As of 2.5, the PluginNumClasses is no longer required.\n\t//       If it is not defined, the PluginGetFactory will be called until null is returned.\n\t//       If it is defined, the behavior is as usual.\n//\tif (pfnc_cnt == 0) return 2;\n\n\t// find the GetFactory function\n\tPLUGIN_GETFACTORY_FNC pfnc_get = (PLUGIN_GETFACTORY_FNC) FindPluginFunc(ph, \"PluginGetFactory\");\n\n\t// NOTE: As of 2.6, the PluginGetFactory function is optional. This is because the \n\t// REGISTER_FECORE_CLASS can be used to register a new plugin class in PluginInitialize.\n//\tif (pfnc_get == 0) return 3;\n\n\n\t// find the plugin's initialization function\n\tPLUGIN_INIT_FNC pfnc_init = (PLUGIN_INIT_FNC) FindPluginFunc(ph, \"PluginInitialize\");\n\n\t// find the optional plugin version function\n\tPLUGIN_VERSION_FNC pfnc_version = (PLUGIN_VERSION_FNC) FindPluginFunc(ph, \"GetPluginVersion\");\n\tif (pfnc_version) pfnc_version(m_version.major, m_version.minor, m_version.patch);\n\n\t// get the kernel\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// set the current allocater id\n\tfebio.SetAllocatorID(m_allocater_id);\n\n\t// call the (optional) initialization function\n\tif (pfnc_init)\n\t{\n\t\tpfnc_init(febio);\n\t\tfebio.SetActiveModule(0);\n\t}\n\n\t// find out how many classes there are in this plugin\n\t// This is only called when the PluginGetFactory function was found. \n\t// (As of 2.6, this is no longer a required function)\n\tif (pfnc_get)\n\t{\n\t\tif (pfnc_cnt)\n\t\t{\n\t\t\tint NC = pfnc_cnt();\n\t\t\t// call the get factory functions\n\t\t\tfor (int i=0; i<NC; ++i)\n\t\t\t{\n\t\t\t\tFECoreFactory* pfac = pfnc_get(i);\n\t\t\t\tif (pfac) \n\t\t\t\t{\t\n\t\t\t\t\tfebio.RegisterFactory(pfac);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// As of 2.5, the PluginNumClasses is no longer required. \n\t\t\t// In this case, the PluginGetFactory is called until null is returned.\n\t\t\tFECoreFactory* pfac = 0;\n\t\t\tint i = 0;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tpfac = pfnc_get(i);\n\t\t\t\tif (pfac)\n\t\t\t\t{\n\t\t\t\t\tfebio.RegisterFactory(pfac);\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile (pfac);\n\t\t}\n\t}\n\n\tfebio.SetAllocatorID(0);\n\n\t// If we get here everything seems okay so let's store the handle\n\tm_ph = ph;\n\tm_filepath = szfile;\n\n\t// a-ok!\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlugin::UnLoad()\n{\n\tif (m_ph)\n\t{\n\t\t// find the plugin's cleanup function\n\t\tPLUGIN_CLEANUP_FNC pfnc = (PLUGIN_CLEANUP_FNC) FindPluginFunc(m_ph, \"PluginCleanup\");\n\t\tif (pfnc) pfnc();\n\n\t\t// remove all features from the kernel that were added by the plugin\n\t\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\t\tfebio.UnregisterFactories(m_allocater_id);\n\n        // unregister the modules from the kernel that were added by the plugin\n        febio.UnregisterModules(m_allocater_id);\n\n\t\t// unload the plugin from memory\n\t\tbool b = UnloadPlugin(m_ph);\n\t\tif (b == false) fprintf(stderr, \"ERROR: Failed unloading plugin %s\\n\", m_szname);\n\t\tm_ph = 0;\n\t}\n}\n\n//=============================================================================\n// FEBioPluginManager\n//=============================================================================\n\nFEBioPluginManager* FEBioPluginManager::m_pThis = 0;\n\nFEBioPluginManager* FEBioPluginManager::GetInstance()\n{\n\tif (m_pThis == 0) m_pThis = new FEBioPluginManager;\n\treturn m_pThis;\n}\n\n//-----------------------------------------------------------------------------\nFEBioPluginManager::~FEBioPluginManager()\n{\n\t// NOTE: I (S.M.) commented this out since for very small problems that run\n\t//       very quickly, FEBio can crash when it unloads the plugins on Windows. \n\t//       It looks like this is caused by some kind of race condition with Windows\n\t//       dll management. \n//\tUnloadAllPlugins();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPluginManager::DeleteThis()\n{\n\tdelete m_pThis;\n\tm_pThis = 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioPluginManager::Plugins()\n{ \n\treturn (int) m_Plugin.size(); \n}\n\n//-----------------------------------------------------------------------------\nconst FEBioPlugin& FEBioPluginManager::GetPlugin(int i)\n{\n\treturn *(m_Plugin[i]);\n}\n\n//-----------------------------------------------------------------------------\n//! This function tries to load a plugin and returns the error code from the\n//! FEBioPlugin::Load function. \n//! \\return Returns zero on success, nonzero on failure.\n//! \\sa FEBioPlugin::Load\nint FEBioPluginManager::LoadPlugin(const char* szfile, PLUGIN_INFO& info)\n{\n\tstd::string sfile = szfile;\n\n\t// First, make sure this plugin does not exist yet\n\tfor (int i=0; i<Plugins(); ++i)\n\t{\n\t\tconst FEBioPlugin& pi = GetPlugin(i);\n\t\tif (pi.GetFilePath() == sfile) return 7;\t\t\n\t}\n\n\t// create a new plugin object\n\tFEBioPlugin* pdll = new FEBioPlugin;\n\n\tinfo.bloaded = false;\n\tinfo.major = 0;\n\tinfo.minor = 0;\n\tinfo.patch = 0;\n\n\t// try to load the plugin\n\tint nerr = pdll->Load(szfile);\n\n\t// add it to the list or delete it if error\n\tif (nerr == 0) \n\t{\n\t\tFEBioPlugin::Version version = pdll->GetVersion();\n\n\t\tinfo.bloaded = true;\n\t\tinfo.major = version.major;\n\t\tinfo.minor = version.minor;\n\t\tinfo.patch = version.patch;\n\n\t\tm_Plugin.push_back(pdll);\n\t}\n\telse delete pdll;\n\n\t// pass error code to caller\n\treturn nerr;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPluginManager::UnloadPlugin(int n)\n{\n\tif ((n<0) || (n >= Plugins())) return false;\n\n\tstd::vector<FEBioPlugin*>::iterator it = m_Plugin.begin() + n;\n\t(*it)->UnLoad();\n\n\tm_Plugin.erase(it);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPluginManager::UnloadPlugin(const std::string& name)\n{\n\tconst char* szname = name.c_str();\n\tfor (std::vector<FEBioPlugin*>::iterator it = m_Plugin.begin(); it != m_Plugin.end(); ++it)\n\t{\n\t\tif (strcmp((*it)->GetName(), szname) == 0)\n\t\t{\n\t\t\t(*it)->UnLoad();\n\t\t\tdelete *it;\n\t\t\tm_Plugin.erase(it);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPluginManager::UnloadAllPlugins()\n{\n\tfor (size_t i = 0; i < m_Plugin.size(); ++i) delete m_Plugin[i];\n\tm_Plugin.clear();\n}\n\n/*\n//-----------------------------------------------------------------------------\n// Import all the plugins from a folder. The szdir is actually a folder name\n// plus a wildcard file reference (e.g. C:\\folder\\*.dll)\nbool LoadPluginFolder(const char* szdir)\n{\n\tWIN32_FIND_DATAA FileData;\n\tHANDLE hFind = FindFirstFileA(szdir, &FileData);\n\n\tLoadPlugin(FileData.cFileName);\n\tprintf(\"Plugin \\\"%s\\\" loaded successfully\\n\", FileData.cFileName);\n\n\twhile (FindNextFileA(hFind, &FileData)) \n\t{\n\t\tLoadPlugin(FileData.cFileName);\n\t\tprintf(\"Plugin \\\"%s\\\" loaded successfully\\n\", FileData.cFileName);\n\t}\n\n\treturn true;\n}\n*/\n"
  },
  {
    "path": "FEBioLib/plugin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <string>\n\n#ifdef WIN32\n#include <Windows.h>\n#undef RegisterClass\n#undef GetFileTitle\n#endif\n\n#include \"febiolib_api.h\"\n\n#ifdef WIN32\ntypedef HMODULE FEBIO_PLUGIN_HANDLE;\n#endif\n\n#ifdef LINUX\ntypedef void* FEBIO_PLUGIN_HANDLE;\n#endif\n\n#ifdef __APPLE__\ntypedef void* FEBIO_PLUGIN_HANDLE;\n#endif\n\nclass FECoreFactory;\n\n//-----------------------------------------------------------------------------\nstruct PLUGIN_INFO\n{\n\tbool\tbloaded;\t// loaded successfully\n\tint\t\tmajor;\t// major version number\n\tint\t\tminor;\t// minor version number\n\tint\t\tpatch;\t// patch versin number\n};\n\n//-----------------------------------------------------------------------------\n//! This class defines a FEBio plugin\nclass FEBIOLIB_API FEBioPlugin\n{\npublic:\n\tstruct Version\n\t{\n\t\tint major;\t// major version number\n\t\tint\tminor;\t// minor version number\n\t\tint\tpatch;\t// patch versin number\n\t};\n\npublic:\n\tFEBioPlugin();\n\t~FEBioPlugin();\n\n\t//! Try to load the library\n\tint Load(const char* szfile);\n\n\t//! Unload the library\n\tvoid UnLoad();\n\n\t//! get the plugin name\n\tconst char* GetName() const { return m_szname; }\n\n\t//! return the version info\n\tVersion GetVersion() const { return m_version; }\n\n\t//! get the plugin's path\n\tstd::string GetFilePath() const { return m_filepath; }\n\n\t//! Get the allocator ID\n\tint GetAllocatorID() const { return m_allocater_id; }\n\nprotected:\n\tvoid SetNameFromFilePath(const char* szfile);\n\nprivate:\n\tstd::string\t\t\t\t\tm_filepath;\n\tchar\t\t\t\t\t\tm_szname[1024];\n\tint\t\t\t\t\t\t\tm_allocater_id;\n\tVersion\t\t\t\t\t\tm_version;\n\tFEBIO_PLUGIN_HANDLE\t\t\tm_ph;\n};\n\n//-----------------------------------------------------------------------------\n//! This class manages all the plugins\nclass FEBIOLIB_API FEBioPluginManager\n{\npublic:\n\t//! Get the plugin manager\n\tstatic FEBioPluginManager* GetInstance();\n\n\t//! Load a plugin into memory\n\tint LoadPlugin(const char* szfile, PLUGIN_INFO& info);\n\n\t//! unload a plugin from memory\n\tbool UnloadPlugin(int n);\n\tbool UnloadPlugin(const std::string& name);\n\tvoid UnloadAllPlugins();\n\n\t//! Clean up\n\tvoid DeleteThis();\n\n\t//! return the number of plugins loaded\n\tint Plugins();\n\n\t//! return an instance of a plugin\n\tconst FEBioPlugin& GetPlugin(int i);\n\nprivate:\n\tstd::vector<FEBioPlugin*>\tm_Plugin;\n\nprivate:\n\tFEBioPluginManager(){}\n\t~FEBioPluginManager();\n\tFEBioPluginManager(const FEBioPluginManager&){}\n\tstatic FEBioPluginManager*\tm_pThis;\n};\n"
  },
  {
    "path": "FEBioLib/stdafx.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n\n// TODO: reference any additional headers you need in STDAFX.H\n// and not in this file\n"
  },
  {
    "path": "FEBioLib/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n#include \"targetver.h\"\n\n#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\n\n#endif\n\n// TODO: reference additional headers your program requires here\n"
  },
  {
    "path": "FEBioLib/targetver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// The following macros define the minimum required platform.  The minimum required platform\n// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run \n// your application.  The macros work by enabling all features available on platform versions up to and \n// including the version specified.\n\n// Modify the following defines if you have to target a platform prior to the ones specified below.\n// Refer to MSDN for the latest info on corresponding values for different platforms.\n#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows Vista.\n#define _WIN32_WINNT 0x0600     // Change this to the appropriate value to target other versions of Windows.\n#endif\n\n"
  },
  {
    "path": "FEBioLib/version.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include <cstdio>\n#include \"version.h\"\n\nchar* febio::getVersionString()\n{\n    static char version[32];\n\n#ifndef DEVCOMMIT\n    snprintf(version, sizeof(version), \"%d.%d.%d\", VERSION, SUBVERSION, SUBSUBVERSION);\n#else\n    snprintf(version, sizeof(version), \"%d.%d.%d.%s\", VERSION, SUBVERSION, SUBSUBVERSION, DEVCOMMIT);\n#endif\n\n    return version;\n}\n"
  },
  {
    "path": "FEBioLib/version.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febiolib_api.h\"\n\n///////////////////////////////////////////////////////////////////////////////\n// FEBio version numbers\n// VERSION is the main version number. This number is only incremented when\n// major modifications or additions where added to the code\n// SUBVERSION is only incremented when minor modifications or \n// additions where added to the code.\n// SUBSUBVERSION is incremented when bugs are fixed.\n\n#define VERSION\t\t\t4\n#define SUBVERSION\t\t12\n#define SUBSUBVERSION\t0\n\n///////////////////////////////////////////////////////////////////////////////\n// Restart file version\n// This is the version number of the restart dump file format.\n// It is incremented when the structure of this file is modified.\n//\n\n#define RSTRTVERSION\t\t0x06\n\nnamespace febio\n{\n\nFEBIOLIB_API char* getVersionString();\n\n}\n"
  },
  {
    "path": "FEBioMech/FE2DFiberNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FE2DFiberNeoHookean.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FE2DFiberNeoHookean, FEElasticMaterial)\n\tADD_PARAMETER(m_E, \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\n\tADD_PARAMETER(m_a, 2, \"a\");\n\tADD_PARAMETER(m_ac, \"active_contraction\")->setUnits(UNIT_PRESSURE);\n\t\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\ndouble FE2DFiberNeoHookean::m_cth[FE2DFiberNeoHookean::NSTEPS];\ndouble FE2DFiberNeoHookean::m_sth[FE2DFiberNeoHookean::NSTEPS];\n\n//////////////////////////////////////////////////////////////////////\n// FE2DFiberNeoHookean\n//////////////////////////////////////////////////////////////////////\n\nFE2DFiberNeoHookean::FE2DFiberNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tstatic bool bfirst = true;\n\n\tif (bfirst)\n\t{\n\t\tdouble ph;\n\t\tfor (int n=0; n<NSTEPS; ++n)\n\t\t{\n\t\t\tph = 2.0*PI*n / (double) NSTEPS;\n\t\t\tm_cth[n] = cos(ph);\n\t\t\tm_sth[n] = sin(ph);\n\t\t}\n\t\tbfirst = false;\n\t}\n\n\tm_ac = 0;\n\tm_a[0] = m_a[1] = 0;\n}\n\nmat3ds FE2DFiberNeoHookean::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\tdouble lndetF = log(detF);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// calculate left Cauchy-Green tensor\n\t// (we commented out the matrix components we do not need)\n\tdouble b[3][3];\n\n\tb[0][0] = F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2];\n\tb[0][1] = F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2];\n\tb[0][2] = F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2];\n\n//\tb[1][0] = F[1][0]*F[0][0]+F[1][1]*F[0][1]+F[1][2]*F[0][2];\n\tb[1][1] = F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2];\n\tb[1][2] = F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2];\n\n//\tb[2][0] = F[2][0]*F[0][0]+F[2][1]*F[0][1]+F[2][2]*F[0][2];\n//\tb[2][1] = F[2][0]*F[1][0]+F[2][1]*F[1][1]+F[2][2]*F[1][2];\n\tb[2][2] = F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2];\n\n\t// material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\n\t// calculate stress\n\tmat3ds s;\n\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\ts.xx() = (mu*(b[0][0] - 1) + lam*lndetF)*detFi;\n\ts.yy() = (mu*(b[1][1] - 1) + lam*lndetF)*detFi;\n\ts.zz() = (mu*(b[2][2] - 1) + lam*lndetF)*detFi;\n\ts.xy() = mu*b[0][1]*detFi;\n\ts.yz() = mu*b[1][2]*detFi;\n\ts.xz() = mu*b[0][2]*detFi;\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t// NOTE: we have only implemented the active contraction model for this material\n\t// There is no passive fiber stress.\n\tif (m_ac > 0)\n\t{\n\t\tdouble wa = 1.0 / (double) NSTEPS;\n\t\tvec3d a0, a, v;\n\t\tdouble at;\n\t\tfor (int n=0; n<NSTEPS; ++n)\n\t\t{\n\t\t\t// calculate the local material fiber vector\n\t\t\tv.x = m_cth[n];\n\t\t\tv.y = m_sth[n];\n\t\t\tv.z = 0;\n\n\t\t\t// calculate the global material fiber vector\n\t\t\ta0 = Q*v;\n\n\t\t\t// calculate the global spatial fiber vector\n\t\t\ta.x = F[0][0]*a0.x + F[0][1]*a0.y + F[0][2]*a0.z;\n\t\t\ta.y = F[1][0]*a0.x + F[1][1]*a0.y + F[1][2]*a0.z;\n\t\t\ta.z = F[2][0]*a0.x + F[2][1]*a0.y + F[2][2]*a0.z;\n\n\t\t\t// normalize material axis and store fiber stretch\n\t\t\ta.unit();\n\n\t\t\t// add active contraction stuff\n\t\t\tat = wa*m_ac *sqrt((m_a[0]*v.x)*(m_a[0]*v.x) + (m_a[1]*v.y)*(m_a[1]*v.y));\n\n\t\t\ts.xx() += at*a.x*a.x;\n\t\t\ts.yy() += at*a.y*a.y;\n\t\t\ts.zz() += at*a.z*a.z;\n\t\t\ts.xy() += at*a.x*a.y;\n\t\t\ts.yz() += at*a.y*a.z;\n\t\t\ts.xz() += at*a.x*a.z;\n\t\t}\n\t}\n\n\treturn s;\n}\n\ntens4ds FE2DFiberNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\n\t// material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\n\tdouble lam1 = lam / detF;\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\n\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tdouble D[6][6] = {0};\n\tD[0][0] = lam1+2.*mu1; D[0][1] = lam1       ; D[0][2] = lam1       ;\n\tD[1][0] = lam1       ; D[1][1] = lam1+2.*mu1; D[1][2] = lam1       ;\n\tD[2][0] = lam1       ; D[2][1] = lam1       ; D[2][2] = lam1+2.*mu1;\n\tD[3][3] = mu1;\n\tD[4][4] = mu1;\n\tD[5][5] = mu1;\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t// NOTE: I commented the fiber stiffness out since I think it will lead to\n\t// a nonsymmetric D matrix and I can't deal with that yet. Besides, most\n\t// problems seem to be running just fine without this contribution.\n/*\n\tif (m_ac)\n\t{\n\t\t// Next, we add the fiber contribution. Since the fibers lie\n\t\t// randomly perpendicular to the transverse axis, we need\n\t\t// to integrate over that plane\n\t\tconst double PI = 4.0*atan(1.0);\n\t\tdouble lam, at, In;\n\t\tvec3d a0, a, v;\n\t\tdouble wa = 1.0 / (double) NSTEPS;\n\t\tfor (int n=0; n<NSTEPS; ++n)\n\t\t{\n\t\t\t// calculate the local material fiber vector\n\t\t\tv.x = m_cth[n];\n\t\t\tv.y = m_sth[n];\n\t\t\tv.z = 0;\n\n\t\t\t// calculate the global material fiber vector\n\t\t\ta0 = pt.Q*v;\n\n\t\t\t// calculate the global spatial fiber vector\n\t\t\ta.x = F[0][0]*a0.x + F[0][1]*a0.y + F[0][2]*a0.z;\n\t\t\ta.y = F[1][0]*a0.x + F[1][1]*a0.y + F[1][2]*a0.z;\n\t\t\ta.z = F[2][0]*a0.x + F[2][1]*a0.y + F[2][2]*a0.z;\n\n\t\t\t// normalize material axis and store fiber stretch\n\t\t\tlam = a.unit();\n\n\t\t\t// add active contraction stuff\n\t\t\tat = wa*m_ac *sqrt((m_a[0]*v.x)*(m_a[0]*v.x) + (m_a[1]*v.y)*(m_a[1]*v.y));\n\t\t\tIn = lam*lam;\n\n\t\t\tD[0][0] += at*(a.x*a.x - 2.0*a.x*a.x*a.x*a.x);\t// c(0,0,0,0)\n\t\t\tD[0][1] += at*(a.x*a.x - 2.0*a.x*a.x*a.y*a.y);\t// c(0,0,1,1)\n\t\t\tD[0][2] += at*(a.x*a.x - 2.0*a.x*a.x*a.z*a.z); // c(0,0,2,2)\n\t\t\tD[0][3] -= at*(2.0*a.x*a.x*a.x*a.y); // c(0,0,0,1)\n\t\t\tD[0][4] -= at*(2.0*a.x*a.x*a.y*a.z); // c(0,0,1,2)\n\t\t\tD[0][5] -= at*(2.0*a.x*a.x*a.x*a.z); // c(0,0,0,2)\n\n\t\t\tD[1][1] += at*(a.y*a.y - 2.0*a.y*a.y*a.y*a.y); // c(1,1,1,1)\n\t\t\tD[1][2] += at*(a.y*a.y - 2.0*a.y*a.y*a.z*a.z); // c(1,1,2,2)\n\t\t\tD[1][3] -= at*(2.0*a.y*a.y*a.x*a.y); // c(1,1,0,1)\n\t\t\tD[1][4] -= at*(2.0*a.y*a.y*a.y*a.z); // c(1,1,1,2)\n\t\t\tD[1][5] -= at*(2.0*a.y*a.y*a.x*a.z); // c(1,1,0,2)\n\n\t\t\tD[2][2] += at*(a.z*a.z - 2.0*a.z*a.z*a.z*a.z); // c(2,2,2,2)\n\t\t\tD[2][3] -= at*(2.0*a.z*a.z*a.x*a.y); // c(2,2,0,1)\n\t\t\tD[2][4] -= at*(2.0*a.z*a.z*a.y*a.z); // c(2,2,1,2)\n\t\t\tD[2][5] -= at*(2.0*a.z*a.z*a.x*a.z); // c(2,2,0,2)\n\n\t\t\tD[3][3] -= at*(2.0*a.x*a.y*a.x*a.y); // c(0,1,0,1)\n\t\t\tD[3][4] -= at*(2.0*a.x*a.y*a.y*a.z); // c(0,1,1,2)\n\t\t\tD[3][5] -= at*(2.0*a.x*a.y*a.x*a.z); // c(0,1,0,2)\n\n\t\t\tD[4][4] -= at*(2.0*a.y*a.z*a.y*a.z); // c(1,2,1,2)\n\t\t\tD[4][5] -= at*(2.0*a.y*a.z*a.x*a.z); // c(1,2,0,2)\n\n\t\t\tD[5][5] -= at*(2.0*a.x*a.z*a.x*a.z); // c(0,2,0,2)\n\t\t}\n\t}\n*/\n\treturn tens4ds(D);\n}\n"
  },
  {
    "path": "FEBioMech/FE2DFiberNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FE2DFiberNeoHookean :\tpublic FEElasticMaterial\n{\n\tenum { NSTEPS = 12 };\t// nr of integration steps\n\npublic:\n\tFE2DFiberNeoHookean(FEModel* pfem);\n\npublic:\n\tFEParamDouble\tm_E;\t//!< Young's modulus\n\tFEParamDouble\tm_v;\t//!< Poisson's ratio\n\n\t//--- active contraction stuff ---\n\tdouble\tm_a[2];\n\tdouble\tm_ac;\n\t// -------------------------------\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\nprotected:\n\tstatic double\tm_cth[NSTEPS];\n\tstatic double\tm_sth[NSTEPS];\n};\n"
  },
  {
    "path": "FEBioMech/FE2DTransIsoMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FE2DTransIsoMooneyRivlin.h\"\n#include <FECore/FEConstValueVec3.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FE2DTransIsoMooneyRivlin, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_w, 2, \"w\");\n\tADD_PARAMETER(m_c3, \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c4, \"c4\");\n\tADD_PARAMETER(m_c5, \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_lam1, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam_max\");\n\tADD_PARAMETER(m_a, 2, \"a\");\n\tADD_PARAMETER(m_ac, \"active_contraction\")->setUnits(UNIT_PRESSURE);;\n\n\tADD_PROPERTY(m_fiber, \"fiber\");\n\nEND_FECORE_CLASS();\n\ndouble FE2DTransIsoMooneyRivlin::m_cth[FE2DTransIsoMooneyRivlin::NSTEPS];\ndouble FE2DTransIsoMooneyRivlin::m_sth[FE2DTransIsoMooneyRivlin::NSTEPS];\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//////////////////////////////////////////////////////////////////////\n// FE2DTransIsoMooneyRivlin\n//////////////////////////////////////////////////////////////////////\n\nFE2DTransIsoMooneyRivlin::FE2DTransIsoMooneyRivlin(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tstatic bool bfirst = true;\n\n\tif (bfirst)\n\t{\n\t\tdouble ph;\n\t\tfor (int n=0; n<NSTEPS; ++n)\n\t\t{\n\t\t\tph = 2.0*PI*n / (double) NSTEPS;\n\t\t\tm_cth[n] = cos(ph);\n\t\t\tm_sth[n] = sin(ph);\n\t\t}\n\t\tbfirst = false;\n\t}\n\n\tm_c1 = 0;\n\tm_c2 = 0;\n\n\tm_a[0] = m_a[1] = 1;\n\tm_ac = 0;\n\n\tm_w[0] = m_w[1] = 1;\n\n\tm_fiber = nullptr;\n\n\tm_epsf = 0.;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress for this material.\n//! \\param pt material point at which to evaluate the stress\nmat3ds FE2DTransIsoMooneyRivlin::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tconst double third = 1.0/3.0;\n\n\t// get the \"fiber\" direction\n\tvec3d r = m_fiber->unitVector(mp);\n\n\t// setup a rotation\n\tquatd q(vec3d(1, 0, 0), r);\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of deviatoric B, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// strain energy derivatives\n\tdouble W1 = m_c1;\n\tdouble W2 = m_c2;\n\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\n\t// Next, we calculate the fiber contribution. For this material\n\t// the fibers lie randomly in a plane that is perpendicular to the transverse\n\t// axis. We therefor need to integrate over this plane.\n\tdouble w, wtot = 0;\n\tvec3d v;\n\tdouble lam, lamd, I4, W4;\n\tmat3ds Tf; Tf.zero();\n\tmat3ds N;\n\tfor (int n=0; n<NSTEPS; ++n)\n\t{\n\t\t// calculate the local material fiber vector\n\t\tv.y = m_cth[n];\n\t\tv.z = m_sth[n];\n\t\tv.x = 0;\n\n\t\t// calculate the global material fiber vector\n\t\tvec3d a0 = q*v;\n\n\t\t// calculate the global spatial fiber vector\n\t\tvec3d a = F*a0;\n\n\t\t// normalize material axis and store fiber stretch\n\t\tlam = a.unit();\n\t\tlamd = lam*Jm13; // i.e. lambda tilde\n\t\n\t\t// fourth invariant of C\n\t\tI4 = lamd*lamd;\n\n\t\tif (lamd > 1)\n\t\t{\n\t\t\tdouble lamdi = 1.0/lamd;\n\t\t\tdouble Wl;\n\t\t\tif (lamd < m_lam1)\n\t\t\t{\n\t\t\t\tWl = lamdi*m_c3*(exp(m_c4*(lamd - 1)) - 1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_lam1-1))-1) - m_c5*m_lam1;\n\t\t\t\tWl = lamdi*(m_c5*lamd + c6);\n\t\t\t}\n\t\t\tW4  = 0.5*lamdi*Wl;\n\t\t}\n\t\telse \n\t\t{\n\t\t\tW4 = 0;\n\t\t}\n\n\t\tw = 1.0/sqrt((v.y/m_w[0])*(v.y/m_w[0]) + (v.z/m_w[1])*(v.z/m_w[1]));\n\t\twtot += w;\n\n\t\t// calculate the stress\n\t\tN = dyad(a);\n\t\tTf += N*(W4*I4*w);\n\n\t\t// add active contraction stuff\n\t\tif (m_ac > 0)\n\t\t{\n\t\t\t// The .5 is to compensate for the 2 multiplier later.\n\t\t\tdouble at = 0.5*w*m_ac /sqrt(SQR(v.y/m_a[0]) + SQR(v.z / m_a[1]));\n\t\t\tTf += N*at;\n\t\t}\n\t}\n\n\t// add fiber to total\n\tT += Tf/wtot;\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric elasticity tensor for this material.\n//! \\param D elasticity tensor\n//! \\param pt material point at which to evaulate the elasticity tensor\ntens4ds FE2DTransIsoMooneyRivlin::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the \"fiber\" direction\n\tvec3d r = m_fiber->unitVector(mp);\n\n\t// setup a rotation\n\tquatd q(vec3d(1, 0, 0), r);\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\tdouble Jm23 = Jm13*Jm13;\n\tdouble Ji = 1.0/J;\n\n\t// deviatoric right Cauchy-Green tensor: C = Ft*F\n\tmat3ds C = pt.DevRightCauchyGreen();\n\n\t// square of C\n\tmat3ds C2 = C.sqr();\n\n\t// Invariants of C\n\tdouble I1 = C.tr();\n\tdouble I2 = 0.5*(I1*I1 - C2.tr());\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// strain energy derivatives\n\tdouble W1 = m_c1;\n\tdouble W2 = m_c2;\n\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2;\n\n\tmat3dd I(1);\t// Identity\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W2*I1) - B2*W2;\n\n\ttens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\tdouble eps = m_epsf * std::numeric_limits<double>::epsilon();\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\n\t// Next, we add the fiber contribution. Since the fibers lie\n\t// randomly perpendicular to the transverse axis, we need\n\t// to integrate over that plane\n\tdouble lam, lamd;\n\tdouble In, Wl, Wll;\n\tvec3d v;\n\tdouble w, wtot = 0;\n\ttens4ds cf, cfw; cf.zero();\n\tmat3ds N2;\n\ttens4ds N4;\n\ttens4ds I4mIxId3 = I4 - IxI/3.0;\n\tfor (int n=0; n<NSTEPS; ++n)\n\t{\n\t\t// calculate the local material fiber vector\n\t\tv.y = m_cth[n];\n\t\tv.z = m_sth[n];\n\t\tv.x = 0;\n\n\t\t// calculate the global material fiber vector\n\t\tvec3d a0 = q*v;\n\n\t\t// calculate the global spatial fiber vector\n\t\tvec3d a = F*a0;\n\n\t\t// normalize material axis and store fiber stretch\n\t\tlam = a.unit();\n\t\tlamd = lam*Jm13; // i.e. lambda tilde\n\t\n\t\t// fourth invariant of C\n\t\tIn = lamd*lamd;\n\n\t\t// Wi = dW/dIi\n\t\tif (lamd >= 1 + eps)\n\t\t{\n\t\t\tdouble lamdi = 1.0/lamd;\n\t\t\tdouble W4, W44;\n\t\t\tif (lamd < m_lam1)\n\t\t\t{\n\t\t\t\tW4  = lamdi*m_c3*(exp(m_c4*(lamd - 1)) - 1);\n\t\t\t\tW44 = m_c3*lamdi*(m_c4*exp(m_c4*(lamd - 1)) - lamdi*(exp(m_c4*(lamd-1))-1));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_lam1-1))-1) - m_c5*m_lam1;\n\t\t\t\tW4  = lamdi*(m_c5*lamd + c6);\n\t\t\t\tW44 = -c6*lamdi*lamdi;\n\t\t\t}\n\t\t\tWl  = 0.5*lamdi*W4;\n\t\t\tWll = 0.25*lamdi*lamdi*(W44 - lamdi*W4);\n\t\t}\n\t\telse \n\t\t{\n\t\t\tWl = 0;\n\t\t\tWll = 0;\n\t\t}\n\n\t\tw = 1.0/sqrt((v.y/m_w[0])*(v.y/m_w[0]) + (v.z/m_w[1])*(v.z/m_w[1]));\n\t\twtot += w;\n\n\t\t// calculate dWdC:C\n\t\tdouble WC = Wl*In;\n\n\t\t// calculate C:d2WdCdC:C\n\t\tdouble CWWC = Wll*In*In;\n\n\t\tN2 = dyad(a);\n\t\tN4 = dyad1s(N2);\n\n\t\tWCCxC = N2*(Wll*In*In);\n\n\t\tcfw = N4*(4.0*Wll*In*In) - dyad1s(WCCxC, I)*(4.0/3.0) + IxI*(4.0/9.0*CWWC);\n\n\t\tcf += (I4mIxId3)*(4.0/3.0*Ji*WC*w) + cfw*(Ji*w);\n\t}\n\n\t// add fiber to total\n\tc += cf/wtot;\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FE2DTransIsoMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! 2D transversely isotropic Mooney-Rivlin\n\n//! This class describes a transversely isotropic matrix where the base material\n//! is Mooney-Rivlin. The difference between this material and the FETransIsoMooneyRivlin\n//! material is that in this material the fibers lie in the plane that is perpendicular\n//! to the transverse axis. \n\nclass FE2DTransIsoMooneyRivlin : public FEUncoupledMaterial\n{\n\tenum { NSTEPS = 12 };\t// nr of integration steps\n\npublic:\n\t// material parameters\n\tdouble\tm_c1;\t//!< Mooney-Rivlin parameter c1\n\tdouble\tm_c2;\t//!< Mooney-Rivlin parameter c2\n\n\t// fiber parameters\n\tdouble\tm_c3;\n\tdouble\tm_c4;\n\tdouble\tm_c5;\n\tdouble\tm_lam1;\n\tdouble\tm_w[2];\n\tdouble\tm_epsf;\n\tFEVec3dValuator*\tm_fiber;\n\n\t//--- active contraction stuff ---\n\tdouble\tm_a[2];\n\tdouble\tm_ac;\n\t// -------------------------------\n\npublic:\n\t//! constructor\n\tFE2DTransIsoMooneyRivlin(FEModel* pfem);\n\t\n\t//! calculate deviatoric stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n\nprotected:\n\tstatic double\tm_cth[NSTEPS];\n\tstatic double\tm_sth[NSTEPS];\n};\n"
  },
  {
    "path": "FEBioMech/FE2DTransIsoVerondaWestmann.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FE2DTransIsoVerondaWestmann.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FE2DTransIsoVerondaWestmann, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, FE_RANGE_GREATER(0.0), \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_w, 2, \"w\");\n\tADD_PARAMETER(m_c3, \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c4, \"c4\");\n\tADD_PARAMETER(m_c5, \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_lam1, \"lam_max\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\ndouble FE2DTransIsoVerondaWestmann::m_cth[FE2DTransIsoVerondaWestmann::NSTEPS];\ndouble FE2DTransIsoVerondaWestmann::m_sth[FE2DTransIsoVerondaWestmann::NSTEPS];\n\n//////////////////////////////////////////////////////////////////////\n// FE2DTransIsoVerondaWestmann\n//////////////////////////////////////////////////////////////////////\n\nFE2DTransIsoVerondaWestmann::FE2DTransIsoVerondaWestmann(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tstatic bool bfirst = true;\n\n\tif (bfirst)\n\t{\n\t\tdouble ph;\n\t\tfor (int n=0; n<NSTEPS; ++n)\n\t\t{\n\t\t\tph = 2.0*PI*n / (double) NSTEPS;\n\t\t\tm_cth[n] = cos(ph);\n\t\t\tm_sth[n] = sin(ph);\n\t\t}\n\t\tbfirst = false;\n\t}\n\n\tm_w[0] = m_w[1] = 1;\n\tm_epsf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress for this material.\n//! \\param pt material point at which to evaluate the stress\nmat3ds FE2DTransIsoVerondaWestmann::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0 / J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\tdouble Jm23 = Jm13*Jm13;\n\tdouble twoJi = 2.0*Ji;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1, I2;\n\tI1 = B.tr();\n\tI2 = 0.5*(I1*I1 - B2.tr() );\n\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\n\t// strain energy derivatives\n\tdouble W1, W2;\n\tW1 = m_c1*m_c2*exp(m_c2*(I1-3));\n\tW2 = -0.5*m_c1*m_c2;\n\n\t// T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\tmat3ds Tf; Tf.zero();\n\n\t// Next, we calculate the fiber contribution. For this material\n\t// the fibers lie randomly in a plane that is perpendicular to the transverse\n\t// axis. We therefor need to integrate over this plane.\n\tdouble w, wtot = 0;\n\tvec3d a0, a, v;\n\tmat3ds A;\n\tdouble lam, lamd, I4, W4;\n\tfor (int n=0; n<NSTEPS; ++n)\n\t{\n\t\t// calculate the local material fiber vector\n\t\tv.y = m_cth[n];\n\t\tv.z = m_sth[n];\n\t\tv.x = 0;\n\n\t\t// calculate the global material fiber vector\n\t\ta0 = Q*v;\n\n\t\t// calculate the global spatial fiber vector\n\t\ta = F*a0;\n\n\t\t// normalize material axis and store fiber stretch\n\t\tlam = a.unit();\n\t\tlamd = lam*Jm13; // i.e. lambda tilde\n\t\n\t\t// fourth invariant of C\n\t\tI4 = lamd*lamd;\n\n\t\tif (lamd > 1)\n\t\t{\n\t\t\tdouble lamdi = 1.0/lamd;\n\t\t\tdouble Wl;\n\t\t\tif (lamd < m_lam1)\n\t\t\t{\n\t\t\t\tWl = lamdi*m_c3*(exp(m_c4*(lamd - 1)) - 1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_lam1-1))-1) - m_c5*m_lam1;\n\t\t\t\tWl = lamdi*(m_c5*lamd + c6);\n\t\t\t}\n\t\t\tW4  = 0.5*lamdi*Wl;\n\t\t}\n\t\telse \n\t\t{\n\t\t\tW4 = 0;\n\t\t}\n\n\t\t// calculate the weight\n\t\tw = 1.0/sqrt((v.y/m_w[0])*(v.y/m_w[0]) + (v.z/m_w[1])*(v.z/m_w[1]));\n\t\twtot += w;\n\n\t\t// Add fiber contribution to T\n\t\tA = dyad(a);\n\t\tTf += A*(W4*I4*w);\n\t}\n\n\t// normalize fiber stress and add to total\n\tT += Tf/wtot;\n\n\treturn T.dev()*twoJi;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric elasticity tensor for this material.\n//! \\param D elasticity tensor\n//! \\param pt material point at which to evaulate the elasticity tensor\ntens4ds FE2DTransIsoVerondaWestmann::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble eps = m_epsf * std::numeric_limits<double>::epsilon();\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\tdouble Jm23 = Jm13*Jm13;\n\tdouble Ji = 1.0/J;\n\n\t// deviatoric right Cauchy-Green tensor: C = Ft*F\n\tmat3ds C = pt.DevRightCauchyGreen();\n\n\t// square of C\n\tmat3ds C2 = C.sqr();\n\n\t// Invariants of C\n\tdouble I1 = C.tr();\n\tdouble I2 = 0.5*(I1*I1 - C2.tr());\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\n\t// strain energy derivatives\n\tdouble W1, W2, W11;\n\tW1 = m_c1*m_c2*exp(m_c2*(I1-3));\n\tW2 = -0.5*m_c1*m_c2;\n\tW11 = m_c2*W1;\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = W11*I1*I1+2*I2*W2;\n\n\tmat3dd I(1);\t// Identity\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W11*I1 + W2*I1) - B2*W2;\n\n\ttens4ds cw = BxB*((W11+W2)*4.0*Ji) - B4*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\tdouble D[6][6];\n\tc.extract(D);\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t// Next, we add the fiber contribution. Since the fibers lie\n\t// randomly perpendicular to the transverse axis, we need\n\t// to integrate over that plane\n\tdouble lam, lamd;\n\tdouble In, Wl, Wll;\n\tvec3d a0, a, v;\n\tdouble w, wtot = 0;\n\tmat3ds N2;\n\ttens4ds N4, cf, cfw;\n\tcf.zero();\n\ttens4ds I4mIxId3 = I4 - IxI/3.0;\n\tfor (int n=0; n<NSTEPS; ++n)\n\t{\n\t\t// calculate the local material fiber vector\n\t\tv.y = m_cth[n];\n\t\tv.z = m_sth[n];\n\t\tv.x = 0;\n\n\t\t// calculate the global material fiber vector\n\t\ta0 = Q*v;\n\n\t\t// calculate the global spatial fiber vector\n\t\ta.x = F[0][0]*a0.x + F[0][1]*a0.y + F[0][2]*a0.z;\n\t\ta.y = F[1][0]*a0.x + F[1][1]*a0.y + F[1][2]*a0.z;\n\t\ta.z = F[2][0]*a0.x + F[2][1]*a0.y + F[2][2]*a0.z;\n\n\t\t// normalize material axis and store fiber stretch\n\t\tlam = a.unit();\n\t\tlamd = lam*Jm13; // i.e. lambda tilde\n\t\n\t\t// fourth invariant of C\n\t\tIn = lamd*lamd;\n\n\t\t// Wi = dW/dIi\n\t\tif (lamd >= 1 + eps)\n\t\t{\n\t\t\tdouble lamdi = 1.0/lamd;\n\t\t\tdouble W4, W44;\n\t\t\tif (lamd < m_lam1)\n\t\t\t{\n\t\t\t\tW4  = lamdi*m_c3*(exp(m_c4*(lamd - 1)) - 1);\n\t\t\t\tW44 = m_c3*lamdi*(m_c4*exp(m_c4*(lamd - 1)) - lamdi*(exp(m_c4*(lamd-1))-1));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_lam1-1))-1) - m_c5*m_lam1;\n\t\t\t\tW4  = lamdi*(m_c5*lamd + c6);\n\t\t\t\tW44 = -c6*lamdi*lamdi;\n\t\t\t}\n\t\t\tWl  = 0.5*lamdi*W4;\n\t\t\tWll = 0.25*lamdi*lamdi*(W44 - lamdi*W4);\n\t\t}\n\t\telse \n\t\t{\n\t\t\tWl = 0;\n\t\t\tWll = 0;\n\t\t}\n\n\t\t// calculate dWdC:C\n\t\tdouble WC = Wl*In;\n\n\t\t// calculate C:d2WdCdC:C\n\t\tdouble CWWC = Wll*In*In;\n\n\t\tw = 1.0/sqrt((v.y/m_w[0])*(v.y/m_w[0]) + (v.z/m_w[1])*(v.z/m_w[1]));\n\t\twtot += w;\n\n\t\tN2 = dyad(a);\n\t\tN4 = dyad1s(N2);\n\n\t\tWCCxC = N2*(Wll*In*In);\n\n\t\tcfw = N4*(4.0*Wll*In*In) - dyad1s(WCCxC, I)*(4.0/3.0) + IxI*(4.0/9.0*CWWC);\n\n\t\tcf += (I4mIxId3)*(4.0/3.0*Ji*WC*w) + cfw*(Ji*w);\n\t}\n\n\t// normalize fiber tangent and add to total tangent\n\tc += cf/wtot;\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FE2DTransIsoVerondaWestmann.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! 2D transversely isotropic Veronda-Westmann\n\n//! This class describes a transversely isotropic matrix where the base material\n//! is Veronda-Westmann. The difference between this material and the FETransIsoVerondaWestmann\n//! material is that in this material the fibers lie in the plane that is perpendicular\n//! to the transverse axis. \n\nclass FE2DTransIsoVerondaWestmann :\tpublic FEUncoupledMaterial\n{\n\tenum { NSTEPS = 12 };\t// nr of integration steps\n\npublic:\n\t// material parameters\n\tdouble\tm_c1;\t//!< Veronda-Westmann parameter c1\n\tdouble\tm_c2;\t//!< Veronda-Westmann parameter c2\n\n\t// fiber parameters\n\tdouble\tm_c3;\n\tdouble\tm_c4;\n\tdouble\tm_c5;\n\tdouble\tm_lam1;\n\tdouble\tm_epsf;\n\npublic:\n\t//! constructor\n\tFE2DTransIsoVerondaWestmann(FEModel* pfem);\n\t\n\t//! calculate deviatoric stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n\nprotected:\n\tstatic double\tm_cth[NSTEPS];\n\tstatic double\tm_sth[NSTEPS];\n\n\tdouble\tm_w[2];\n};\n"
  },
  {
    "path": "FEBioMech/FE3FieldElasticShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FE3FieldElasticShellDomain.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FE3FieldElasticShellDomain, FEElasticShellDomain)\n\tADD_PARAMETER(m_blaugon, \"laugon\");\n\tADD_PARAMETER(m_augtol , \"atol\");\n\tADD_PARAMETER(m_naugmin, \"minaug\");\n\tADD_PARAMETER(m_naugmax, \"maxaug\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FE3FieldElasticShellDomain::ELEM_DATA::Serialize(DumpStream& ar)\n{\n\tar & eJ;\n\tar & ep;\n\tar & Lk;\n}\n\n//-----------------------------------------------------------------------------\nFE3FieldElasticShellDomain::FE3FieldElasticShellDomain(FEModel* pfem) : FEElasticShellDomain(pfem)\n{\n\tm_blaugon = false;\n\tm_augtol = 0.01;\n\tm_naugmin = 0;\n\tm_naugmax = 0;\n}\n\n//-----------------------------------------------------------------------------\nFE3FieldElasticShellDomain& FE3FieldElasticShellDomain::operator = (FE3FieldElasticShellDomain& d) \n{ \n\tm_Elem = d.m_Elem; \n\tm_pMesh = d.m_pMesh; \n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\nbool FE3FieldElasticShellDomain::DoAugmentations() const\n{\n\treturn m_blaugon;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize the 3-field domain data\nbool FE3FieldElasticShellDomain::Init()\n{\n    // make sure the domain material uses an uncoupled formulation\n    if (dynamic_cast<FEUncoupledMaterial*>(m_pMat) == 0) return false;\n\tif (FEElasticShellDomain::Init() == false) return false;\n    \n    // allocate element data\n    int NE = Elements();\n    m_Data.resize(NE);\n    \n    // initialize element data\n    for (int i=0; i<NE; ++i)\n    {\n        ELEM_DATA& d = m_Data[i];\n        d.eJ = 1.0;\n        d.ep = 0.0;\n        d.Lk = 0.0;\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE3FieldElasticShellDomain::Reset()\n{\n    FEElasticShellDomain::Reset();\n    // initialize element data\n    int NE = (int)m_Data.size();\n    for (int i=0; i<NE; ++i)\n    {\n        ELEM_DATA& d = m_Data[i];\n        d.eJ = 1.0;\n        d.ep = 0.0;\n        d.Lk = 0.0;\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix for three-field domain\nvoid FE3FieldElasticShellDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // create the element's stiffness matrix\n        int ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate material and geometrical stiffness (i.e. constitutive component)\n        ElementStiffness(iel, ke);\n\n        // Calculate dilatational stiffness\n        ElementDilatationalStiffness(fem, iel, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates dilatational element stiffness component for element iel\n\nvoid FE3FieldElasticShellDomain::ElementDilatationalStiffness(FEModel& fem, int iel, matrix& ke)\n{\n    int i, j, i6, j6, n;\n    \n    FEShellElement& elem = Element(iel);\n    ELEM_DATA& ed = m_Data[iel];\n    \n    const int nint = elem.GaussPoints();\n    const int neln = elem.Nodes();\n    \n    // get the material\n    FEUncoupledMaterial* pmi = dynamic_cast<FEUncoupledMaterial*>(m_pMat);\n    assert(pmi);\n    \n    // average global derivatives\n    vector<vec3d> gradMu(neln,vec3d(0,0,0));\n    vector<vec3d> gradMd(neln,vec3d(0,0,0));\n    \n    // initial element volume\n    double Ve = 0;\n    \n    // global derivatives of shape functions\n    const double *gw = elem.GaussWeights();\n    double eta;\n    vec3d gcnt[3];\n\n    // jacobian\n    double Jt, J0;\n    \n    const double* Mr, *Ms, *M;\n\n    // repeat over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        J0 = detJ0(elem, n);\n        Jt = detJ(elem, n);\n        \n        Jt *= gw[n];\n        \n        Ve += J0*gw[n];\n        \n        eta = elem.gt(n);\n        Mr = elem.Hr(n);\n        Ms = elem.Hs(n);\n        M  = elem.H(n);\n\n        ContraBaseVectors(elem, n, gcnt);\n\n        for (i=0; i<neln; ++i)\n        {\n            vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] += ((gradM*(1+eta) + gcnt[2]*M[i])/2)*Jt;\n            gradMd[i] += ((gradM*(1-eta) - gcnt[2]*M[i])/2)*Jt;\n        }\n    }\n    \n    // get effective modulus\n    double k = pmi->UJJ(ed.eJ);\n    \n    // next, we add the Lagrangian contribution\n    // note that this term will always be zero if the material does not\n    // use the augmented lagrangian\n    k += ed.Lk*pmi->hpp(ed.eJ);\n    \n    // divide by initial volume\n    k /= Ve;\n    \n    // calculate dilatational stiffness component\n    for (i=0, i6=0; i<neln; ++i, i6 += 6)\n    {\n        for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n        {\n            mat3d Kuu = (gradMu[i] & gradMu[j])*k;\n            mat3d Kud = (gradMu[i] & gradMd[j])*k;\n            mat3d Kdu = (gradMd[i] & gradMu[j])*k;\n            mat3d Kdd = (gradMd[i] & gradMd[j])*k;\n            \n            ke[i6  ][j6  ] += Kuu(0,0); ke[i6  ][j6+1] += Kuu(0,1); ke[i6  ][j6+2] += Kuu(0,2);\n            ke[i6+1][j6  ] += Kuu(1,0); ke[i6+1][j6+1] += Kuu(1,1); ke[i6+1][j6+2] += Kuu(1,2);\n            ke[i6+2][j6  ] += Kuu(2,0); ke[i6+2][j6+1] += Kuu(2,1); ke[i6+2][j6+2] += Kuu(2,2);\n            \n            ke[i6  ][j6+3] += Kud(0,0); ke[i6  ][j6+4] += Kud(0,1); ke[i6  ][j6+5] += Kud(0,2);\n            ke[i6+1][j6+3] += Kud(1,0); ke[i6+1][j6+4] += Kud(1,1); ke[i6+1][j6+5] += Kud(1,2);\n            ke[i6+2][j6+3] += Kud(2,0); ke[i6+2][j6+4] += Kud(2,1); ke[i6+2][j6+5] += Kud(2,2);\n            \n            ke[i6+3][j6  ] += Kdu(0,0); ke[i6+3][j6+1] += Kdu(0,1); ke[i6+3][j6+2] += Kdu(0,2);\n            ke[i6+4][j6  ] += Kdu(1,0); ke[i6+4][j6+1] += Kdu(1,1); ke[i6+4][j6+2] += Kdu(1,2);\n            ke[i6+5][j6  ] += Kdu(2,0); ke[i6+5][j6+1] += Kdu(2,1); ke[i6+5][j6+2] += Kdu(2,2);\n            \n            ke[i6+3][j6+3] += Kdd(0,0); ke[i6+3][j6+4] += Kdd(0,1); ke[i6+3][j6+5] += Kdd(0,2);\n            ke[i6+4][j6+3] += Kdd(1,0); ke[i6+4][j6+4] += Kdd(1,1); ke[i6+4][j6+5] += Kdd(1,2);\n            ke[i6+5][j6+3] += Kdd(2,0); ke[i6+5][j6+4] += Kdd(2,1); ke[i6+5][j6+5] += Kdd(2,2);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the shell element material and geometrical stiffness matrix\n\nvoid FE3FieldElasticShellDomain::ElementStiffness(int iel, matrix& ke)\n{\n    FEShellElement& el = Element(iel);\n    ELEM_DATA& ed = m_Data[iel];\n\n    // get the material\n    FEUncoupledMaterial* pmi = dynamic_cast<FEUncoupledMaterial*>(m_pMat);\n    assert(pmi);\n    \n    int i, i6, j, j6, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    const double* Mr, *Ms, *M;\n    vec3d gradMu[FEElement::MAX_NODES], gradMd[FEElement::MAX_NODES];\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // get the material\n    FEUncoupledMaterial& mat = dynamic_cast<FEUncoupledMaterial&>(*m_pMat);\n    \n    // we need the following tensors for the dilational stiffness\n    mat3dd I(1);\n    tens4ds IxI = dyad1s(I);\n    tens4ds I4  = dyad4s(I);\n    tens4ds Cp = IxI - I4*2;\n    \n    // calculate element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        // get the material's tangent\n        // Note that we are only grabbing the deviatoric tangent.\n        // The other tangent terms depend on the pressure p\n        // which we seperately\n        tens4ds C = Cp*ed.ep + mat.DevTangent(mp);\n        \n        // get the stress\n        mat3ds s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // ------------ constitutive component --------------\n        \n        // setup the material point\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n        }\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                mat3d Kuu = vdotTdotv(gradMu[i], C, gradMu[j])*detJt;\n                mat3d Kud = vdotTdotv(gradMu[i], C, gradMd[j])*detJt;\n                mat3d Kdu = vdotTdotv(gradMd[i], C, gradMu[j])*detJt;\n                mat3d Kdd = vdotTdotv(gradMd[i], C, gradMd[j])*detJt;\n                \n                ke[i6  ][j6  ] += Kuu(0,0); ke[i6  ][j6+1] += Kuu(0,1); ke[i6  ][j6+2] += Kuu(0,2);\n                ke[i6+1][j6  ] += Kuu(1,0); ke[i6+1][j6+1] += Kuu(1,1); ke[i6+1][j6+2] += Kuu(1,2);\n                ke[i6+2][j6  ] += Kuu(2,0); ke[i6+2][j6+1] += Kuu(2,1); ke[i6+2][j6+2] += Kuu(2,2);\n                \n                ke[i6  ][j6+3] += Kud(0,0); ke[i6  ][j6+4] += Kud(0,1); ke[i6  ][j6+5] += Kud(0,2);\n                ke[i6+1][j6+3] += Kud(1,0); ke[i6+1][j6+4] += Kud(1,1); ke[i6+1][j6+5] += Kud(1,2);\n                ke[i6+2][j6+3] += Kud(2,0); ke[i6+2][j6+4] += Kud(2,1); ke[i6+2][j6+5] += Kud(2,2);\n                \n                ke[i6+3][j6  ] += Kdu(0,0); ke[i6+3][j6+1] += Kdu(0,1); ke[i6+3][j6+2] += Kdu(0,2);\n                ke[i6+4][j6  ] += Kdu(1,0); ke[i6+4][j6+1] += Kdu(1,1); ke[i6+4][j6+2] += Kdu(1,2);\n                ke[i6+5][j6  ] += Kdu(2,0); ke[i6+5][j6+1] += Kdu(2,1); ke[i6+5][j6+2] += Kdu(2,2);\n                \n                ke[i6+3][j6+3] += Kdd(0,0); ke[i6+3][j6+4] += Kdd(0,1); ke[i6+3][j6+5] += Kdd(0,2);\n                ke[i6+4][j6+3] += Kdd(1,0); ke[i6+4][j6+4] += Kdd(1,1); ke[i6+4][j6+5] += Kdd(1,2);\n                ke[i6+5][j6+3] += Kdd(2,0); ke[i6+5][j6+4] += Kdd(2,1); ke[i6+5][j6+5] += Kdd(2,2);\n            }\n        }\n        \n        // ------------ initial stress component --------------\n        \n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                double Kuu = gradMu[i]*(s*gradMu[j])*detJt;\n                double Kud = gradMu[i]*(s*gradMd[j])*detJt;\n                double Kdu = gradMd[i]*(s*gradMu[j])*detJt;\n                double Kdd = gradMd[i]*(s*gradMd[j])*detJt;\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-d component\n                ke[6*i  ][6*j+3] += Kud;\n                ke[6*i+1][6*j+4] += Kud;\n                ke[6*i+2][6*j+5] += Kud;\n                \n                // the d-u component\n                ke[6*i+3][6*j  ] += Kdu;\n                ke[6*i+4][6*j+1] += Kdu;\n                ke[6*i+5][6*j+2] += Kdu;\n                \n                // the d-d component\n                ke[6*i+3][6*j+3] += Kdd;\n                ke[6*i+4][6*j+4] += Kdd;\n                ke[6*i+5][6*j+5] += Kdd;\n            }\n        \n    } // end loop over gauss-points\n    \n}\n\n//-----------------------------------------------------------------------------\n//! This function loops over all elements and updates the stress\nvoid FE3FieldElasticShellDomain::Update(const FETimeInfo& tp)\n{\n\tFESSIShellDomain::Update(tp);\n\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! This function updates the stresses for elements using the three-field formulation.\n//! For such elements, the stress is a sum of a deviatoric stress, calculate by the\n//! material and a dilatational term.\nvoid FE3FieldElasticShellDomain::UpdateElementStress(int iel)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // get the material\n    FEUncoupledMaterial& mat = *(dynamic_cast<FEUncoupledMaterial*>(m_pMat));\n    \n    // get the solid element\n    FEShellElement& el = m_Elem[iel];\n    ELEM_DATA& ed = m_Data[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // get the integration weights\n    double* gw = el.GaussWeights();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d r0[NELN], s0[NELN], r[NELN], s[NELN];\n    vec3d v[NELN], w[NELN];\n    vec3d a[NELN], b[NELN];\n    // nodal coordinates\n    GetCurrentNodalCoordinates(el, r, m_alphaf, false);\n    GetCurrentNodalCoordinates(el, s, m_alphaf, true);\n    GetReferenceNodalCoordinates(el, r0, false);\n    GetReferenceNodalCoordinates(el, s0, true);\n\n    // update dynamic quantities\n    if (m_update_dynamic)\n    {\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m_pMesh->Node(el.m_node[j]);\n            v[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*m_alphaf + node.m_vp*(1-m_alphaf);\n            w[j] = node.get_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2])*m_alphaf + node.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2])*(1-m_alphaf);\n            a[j] = node.m_at*m_alpham + node.m_ap*(1-m_alpham);\n            b[j] = node.get_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2])*m_alpham + node.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2])*(1-m_alpham);\n        }\n    }\n\n    // calculate the average dilatation and pressure\n    double vt = 0, V = 0;\n    for (int n=0; n<nint; ++n)\n    {\n        vt += detJ(el, n)*gw[n];\n        V += detJ0(el, n)*gw[n];\n    }\n    \n    // calculate volume ratio\n    ed.eJ = vt / V;\n    \n    // Calculate pressure. This is a sum of a Lagrangian term and a penalty term\n    //      <--- Lag. mult. -->  <-- penalty -->\n    ed.ep = ed.Lk*mat.hp(ed.eJ) + mat.UJ(ed.eJ);\n    //    ed.ep = mat.UJ(ed.eJ);\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        pt.m_p = ed.ep;\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //         since the material point coordinates are not used by most materials.\n        mp.m_r0 = evaluate(el, r0, s0, n);\n        mp.m_rt = evaluate(el, r, s, n);\n\n        // get the deformation gradient and determinant at intermediate time\n        double Jt, Jp;\n        mat3d Ft, Fp;\n        Jt = defgrad(el, Ft, n);\n        Jp = defgradp(el, Fp, n);\n        pt.m_F = Ft*m_alphaf + Fp*(1-m_alphaf);\n        pt.m_J = pt.m_F.det();\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (Ft - Fp)*Fi/dt;\n        if (m_update_dynamic)\n        {\n            pt.m_v = evaluate(el, v, w, n);\n            pt.m_a = evaluate(el, a, b, n);\n        }\n        \n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the stress at this material point\n        // Note that we don't call the material's Stress member function.\n        // The reason is that we need to use the averaged pressure for the element\n        // and the Stress function uses the pointwise pressure.\n        // Therefore we call the DevStress function and add the pressure term\n        // seperately.\n        pt.m_s = mat3dd(ed.ep) + mat.DevStress(mp);\n        \n        \n        // adjust stress for strain energy conservation\n        if (m_alphaf == 0.5)\n        {\n            // evaluate strain energy at current time\n\t\t\tmat3d Ftmp = pt.m_F;\n\t\t\tdouble Jtmp = pt.m_J;\n\t\t\tpt.m_F = Ft;\n            pt.m_J = Jt;\n            FEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(m_pMat);\n            pt.m_Wt = pme->StrainEnergyDensity(mp);\n\t\t\tpt.m_F = Ftmp;\n\t\t\tpt.m_J = Jtmp;\n\n            mat3ds D = pt.m_L.sym();\n            double D2 = D.dotdot(D);\n            if (D2 > std::numeric_limits<double>::epsilon())\n                pt.m_s += D*(((pt.m_Wt-pt.m_Wp)/(dt*pt.m_J) - pt.m_s.dotdot(D))/D2);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Do augmentation\nbool FE3FieldElasticShellDomain::Augment(int naug)\n{\n    FEUncoupledMaterial* pmi = dynamic_cast<FEUncoupledMaterial*>(m_pMat);\n    assert(pmi);\n    \n    // make sure Augmented Lagrangian flag is on\n    if (m_blaugon == false) return true;\n    \n    // do the augmentation\n    int n;\n    double normL0 = 0, normL1 = 0, L0, L1;\n    double k = pmi->m_K;\n    int NE = Elements();\n    \n    for (n=0; n<NE; ++n)\n    {\n        ELEM_DATA& ed = m_Data[n];\n        \n        L0 = ed.Lk;\n        normL0 += L0*L0;\n        \n        L1 = L0 + k*pmi->h(ed.eJ);\n        normL1 += L1*L1;\n    }\n    \n    normL0 = sqrt(normL0);\n    normL1 = sqrt(normL1);\n    \n    // check convergence\n    double pctn = 0;\n    if (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n    \n    feLog(\" material %d\\n\", pmi->GetID());\n    feLog(\"                        CURRENT         CHANGE        REQUIRED\\n\");\n    feLog(\"   pressure norm : %15le%15le%15le\\n\", normL1, pctn, m_augtol);\n    \n    // check convergence\n    bool bconv = true;\n    if (pctn >= m_augtol) bconv = false;\n    if (m_naugmin > naug) bconv = false;\n    if ((m_naugmax > 0) && (m_naugmax <= naug)) bconv = true;\n    \n    // do the augmentation only if we have not yet converged\n    if (bconv == false)\n    {\n        for (n=0; n<NE; ++n)\n        {\n            ELEM_DATA& ed = m_Data[n];\n            \n            ed.Lk += k*pmi->h(ed.eJ);\n            ed.ep = ed.Lk*pmi->hp(ed.eJ) + k*log(ed.eJ)/ed.eJ;\n        }\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Data serialization\nvoid FE3FieldElasticShellDomain::Serialize(DumpStream &ar)\n{\n    FEElasticShellDomain::Serialize(ar);\n\tar & m_Data;\n}\n"
  },
  {
    "path": "FEBioMech/FE3FieldElasticShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticShellDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! The following domain implements the finite element formulation for a three-field\n//! shell element. Results indicate that using this class produces poorer convergence\n//! with shells than the standard FEElasticShellDomain.  This class is included\n//! only for development purposes.\nclass FEBIOMECH_API FE3FieldElasticShellDomain : public FEElasticShellDomain\n{\nprotected:\n    struct ELEM_DATA\n    {\n        double    eJ;        // average element jacobian\n        double    ep;        // average pressure\n        double    Lk;        // Lagrangian multiplier\n\n\t\tvoid Serialize(DumpStream& ar);\n    };\n    \npublic:\n    //! constructor\n\tFE3FieldElasticShellDomain(FEModel* pfem);\n    \n    //! \\todo Is this really used?\n\tFE3FieldElasticShellDomain& operator = (FE3FieldElasticShellDomain& d);\n    \n    //! initialize class\n\tbool Init() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! augmentation\n    bool Augment(int naug) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \npublic: // overridden from FEElasticDomain\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // calculate stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \nprotected:\n    //! Dilatational stiffness component for nearly-incompressible materials\n    void ElementDilatationalStiffness(FEModel& fem, int iel, matrix& ke);\n    \n    //! material and geometrical stiffness components\n    void ElementStiffness(int iel, matrix& ke);\n    \n    //! update the stress of an element\n    void UpdateElementStress(int iel);\n\npublic:\n\tbool DoAugmentations() const;\n    \nprotected:\n    vector<ELEM_DATA>    m_Data;\n\n\tbool\tm_blaugon;\t\t//!< augmented lagrangian flag\n\tdouble\tm_augtol;\t\t//!< augmented lagrangian tolerance\n\tint\t\tm_naugmin;\t\t//!< minimum number of augmentations\n\tint\t\tm_naugmax;\t\t//!< max number of augmentations\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FE3FieldElasticSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FE3FieldElasticSolidDomain.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModel.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FE3FieldElasticSolidDomain, FEElasticSolidDomain)\n\tADD_PARAMETER(m_blaugon, \"laugon\");\n\tADD_PARAMETER(m_augtol , \"atol\");\n\tADD_PARAMETER(m_naugmin, \"minaug\");\n\tADD_PARAMETER(m_naugmax, \"maxaug\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FE3FieldElasticSolidDomain::ELEM_DATA::Serialize(DumpStream& ar)\n{\n\tar & eJ;\n\tar & ep;\n\tar & Lk;\n\tar & eJt;\n\tar & eJp;\n}\n\n//-----------------------------------------------------------------------------\n//! constructor\nFE3FieldElasticSolidDomain::FE3FieldElasticSolidDomain(FEModel* pfem) : FEElasticSolidDomain(pfem) \n{\n\tm_blaugon = false;\n\tm_augtol = 0.01;\n\tm_naugmin = 0;\n\tm_naugmax = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Do I really use this?\nFE3FieldElasticSolidDomain& FE3FieldElasticSolidDomain::operator = (FE3FieldElasticSolidDomain& d) \n{ \n\tm_Elem = d.m_Elem; \n\tm_pMesh = d.m_pMesh; \n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\nbool FE3FieldElasticSolidDomain::DoAugmentations() const\n{\n\treturn m_blaugon;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize the 3-field domain data\nbool FE3FieldElasticSolidDomain::Init()\n{\n\t// make sure the domain material uses an uncoupled formulation\n\tif (dynamic_cast<FEUncoupledMaterial*>(m_pMat) == 0) return false;\n\tif (FEElasticSolidDomain::Init() == false) return false;\n\n\t// allocate element data\n\tint NE = Elements();\n\tm_Data.resize(NE);\n\n\t// initialize element data\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\tELEM_DATA& d = m_Data[i];\n\t\td.eJ = d.eJt = d.eJp = 1.0;\n\t\td.ep = 0.0;\n\t\td.Lk = 0.0;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE3FieldElasticSolidDomain::Reset()\n{\n\tFEElasticSolidDomain::Reset();\n\t// initialize element data\n\tsize_t NE = m_Data.size();\n\tfor (size_t i=0; i<NE; ++i)\n\t{\n\t\tELEM_DATA& d = m_Data[i];\n        d.eJ = d.eJt = d.eJp = 1.0;\n        d.ep = 0.0;\n        d.Lk = 0.0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FE3FieldElasticSolidDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    FEElasticSolidDomain::PreSolveUpdate(timeInfo);\n    int NE = (int)m_Data.size();\n#pragma omp parallel for\n\tfor (int i=0; i<NE; ++i)\n    {\n        ELEM_DATA& d = m_Data[i];\n        d.eJp = d.eJt;\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix for three-field domain\nvoid FE3FieldElasticSolidDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\t#pragma omp parallel for\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\n\t\t// create the element's stiffness matrix\n\t\tint ndof = 3*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate material stiffness (i.e. constitutive component)\n\t\tElementMaterialStiffness(iel, ke);\n\n\t\t// calculate geometrical stiffness\n\t\tElementGeometricalStiffness(iel, ke);\n\n\t\t// Calculate dilatational stiffness\n\t\tElementDilatationalStiffness(fem, iel, ke);\n\n\t\t// assign symmetic parts\n\t\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t\t// grabs elements from the upper diagonal matrix?\n\t\tfor (int i=0; i<ndof; ++i)\n\t\t\tfor (int j=i+1; j<ndof; ++j)\n\t\t\t\tke[j][i] = ke[i][j];\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates dilatational element stiffness component for element iel\n\nvoid FE3FieldElasticSolidDomain::ElementDilatationalStiffness(FEModel& fem, int iel, matrix& ke)\n{\n\tint i, j, n;\n\n\tFESolidElement& elem = Element(iel);\n\tELEM_DATA& ed = m_Data[iel];\n\n\tconst int nint = elem.GaussPoints();\n\tconst int neln = elem.Nodes();\n\tconst int ndof = 3*neln;\n\n\t// get the elements material\n\tFEUncoupledMaterial* pmi = dynamic_cast<FEUncoupledMaterial*>(m_pMat);\n\tassert(pmi);\n\n\t// average global derivatives\n\tvector<double> gradN(3*neln);\n\tzero(gradN);\n\n\t// initial element volume\n\tdouble Ve = 0;\n\n\t// global derivatives of shape functions\n\tdouble Gx, Gy, Gz;\n\tconst double *gw = elem.GaussWeights();\n\n\t// jacobian\n\tdouble Ji[3][3], Jt, J0;\n\n\tdouble *Gr, *Gs, *Gt;\n\n\t// repeat over gauss-points\n\tfor (n=0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tJ0 = detJ0(elem, n);\n\t\tJt = invjact(elem, Ji, n, m_alphaf)*m_alphaf;\n\n\t\tJt *= gw[n];\n\n\t\tVe += J0*gw[n];\n\n\t\tGr = elem.Gr(n);\n\t\tGs = elem.Gs(n);\n\t\tGt = elem.Gt(n);\n\n\t\t// calculate global gradient of shape functions\n\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\tfor (i=0; i<neln; ++i)\n\t\t{\n\t\t\tGx = Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i];\n\t\t\tGy = Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i];\n\t\t\tGz = Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i];\n\n\t\t\tgradN[3*i  ] += Gx*Jt;\n\t\t\tgradN[3*i+1] += Gy*Jt;\n\t\t\tgradN[3*i+2] += Gz*Jt;\n\t\t}\n\t}\n\n\t// get effective modulus\n\tdouble k = pmi->UJJ(ed.eJ);\n\n\t// next, we add the Lagrangian contribution\n\t// note that this term will always be zero if the material does not\n\t// use the augmented lagrangian\n\tk += ed.Lk*pmi->hpp(ed.eJ);\n\n\t// divide by initial volume\n\tk /= Ve;\n\n\t// calculate dilatational stiffness component\n\t// we only calculate the upper triangular part\n\t// since ke is symmetric.\n\tfor (i=0; i<ndof; ++i)\n\t\tfor (j=i; j<ndof; ++j)\n\t\t\tke[i][j] += k*gradN[i]*gradN[j];\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FE3FieldElasticSolidDomain::ElementMaterialStiffness(int iel, matrix &ke)\n{\n\tFESolidElement &el = Element(iel);\n\tELEM_DATA& ed = m_Data[iel];\n\n\t// Get the current element's data\n\tconst int nint = el.GaussPoints();\n\tconst int neln = el.Nodes();\n\tconst int ndof = 3*neln;\n\n\t// global derivatives of shape functions\n\tconst int NME = FEElement::MAX_NODES;\n\tvec3d G[NME];\n\n\tdouble Gxi, Gyi, Gzi;\n\tdouble Gxj, Gyj, Gzj;\n\n\t// The 'D' matrix\n\tdouble D[6][6] = {0};\t// The 'D' matrix\n\n\t// The 'D*BL' matrix\n\tdouble DBL[6][3];\n\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// get the material\n\tFEUncoupledMaterial& mat = dynamic_cast<FEUncoupledMaterial&>(*m_pMat);\n\n\t// we need the following tensors for the dilational stiffness\n\tmat3dd I(1);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds Cp = IxI - I4*2;\n\n\t// calculate element stiffness matrix\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdouble detJt = ShapeGradient(el, n, G, m_alphaf)*gw[n]*m_alphaf;\n\n\t\t// setup the material point\n\t\t// NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// get the material's tangent\n\t\t// Note that we are only grabbing the deviatoric tangent. \n\t\t// The other tangent terms depend on the pressure p\n\t\t// which we seperately\n\t\ttens4ds C = Cp*ed.ep + mat.DevTangent(mp);\n\n\t\t// get the 'D' matrix\n\t\tC.extract(D);\n\n\t\t// we only calculate the upper triangular part\n\t\t// since ke is symmetric. The other part is\n\t\t// determined below using this symmetry.\n\t\tfor (int i = 0, i3 = 0; i<neln; ++i, i3 += 3)\n\t\t{\n\t\t\tGxi = G[i].x;\n\t\t\tGyi = G[i].y;\n\t\t\tGzi = G[i].z;\n\n\t\t\tfor (int j = i, j3 = i3; j<neln; ++j, j3 += 3)\n\t\t\t{\n\t\t\t\tGxj = G[j].x;\n\t\t\t\tGyj = G[j].y;\n\t\t\t\tGzj = G[j].z;\n\n\t\t\t\t// calculate D*BL matrices\n\t\t\t\tDBL[0][0] = (D[0][0]*Gxj+D[0][3]*Gyj+D[0][5]*Gzj);\n\t\t\t\tDBL[0][1] = (D[0][1]*Gyj+D[0][3]*Gxj+D[0][4]*Gzj);\n\t\t\t\tDBL[0][2] = (D[0][2]*Gzj+D[0][4]*Gyj+D[0][5]*Gxj);\n\n\t\t\t\tDBL[1][0] = (D[1][0]*Gxj+D[1][3]*Gyj+D[1][5]*Gzj);\n\t\t\t\tDBL[1][1] = (D[1][1]*Gyj+D[1][3]*Gxj+D[1][4]*Gzj);\n\t\t\t\tDBL[1][2] = (D[1][2]*Gzj+D[1][4]*Gyj+D[1][5]*Gxj);\n\n\t\t\t\tDBL[2][0] = (D[2][0]*Gxj+D[2][3]*Gyj+D[2][5]*Gzj);\n\t\t\t\tDBL[2][1] = (D[2][1]*Gyj+D[2][3]*Gxj+D[2][4]*Gzj);\n\t\t\t\tDBL[2][2] = (D[2][2]*Gzj+D[2][4]*Gyj+D[2][5]*Gxj);\n\n\t\t\t\tDBL[3][0] = (D[3][0]*Gxj+D[3][3]*Gyj+D[3][5]*Gzj);\n\t\t\t\tDBL[3][1] = (D[3][1]*Gyj+D[3][3]*Gxj+D[3][4]*Gzj);\n\t\t\t\tDBL[3][2] = (D[3][2]*Gzj+D[3][4]*Gyj+D[3][5]*Gxj);\n\n\t\t\t\tDBL[4][0] = (D[4][0]*Gxj+D[4][3]*Gyj+D[4][5]*Gzj);\n\t\t\t\tDBL[4][1] = (D[4][1]*Gyj+D[4][3]*Gxj+D[4][4]*Gzj);\n\t\t\t\tDBL[4][2] = (D[4][2]*Gzj+D[4][4]*Gyj+D[4][5]*Gxj);\n\n\t\t\t\tDBL[5][0] = (D[5][0]*Gxj+D[5][3]*Gyj+D[5][5]*Gzj);\n\t\t\t\tDBL[5][1] = (D[5][1]*Gyj+D[5][3]*Gxj+D[5][4]*Gzj);\n\t\t\t\tDBL[5][2] = (D[5][2]*Gzj+D[5][4]*Gyj+D[5][5]*Gxj);\n\n\t\t\t\tke[i3  ][j3  ] += (Gxi*DBL[0][0] + Gyi*DBL[3][0] + Gzi*DBL[5][0] )*detJt;\n\t\t\t\tke[i3  ][j3+1] += (Gxi*DBL[0][1] + Gyi*DBL[3][1] + Gzi*DBL[5][1] )*detJt;\n\t\t\t\tke[i3  ][j3+2] += (Gxi*DBL[0][2] + Gyi*DBL[3][2] + Gzi*DBL[5][2] )*detJt;\n\n\t\t\t\tke[i3+1][j3  ] += (Gyi*DBL[1][0] + Gxi*DBL[3][0] + Gzi*DBL[4][0] )*detJt;\n\t\t\t\tke[i3+1][j3+1] += (Gyi*DBL[1][1] + Gxi*DBL[3][1] + Gzi*DBL[4][1] )*detJt;\n\t\t\t\tke[i3+1][j3+2] += (Gyi*DBL[1][2] + Gxi*DBL[3][2] + Gzi*DBL[4][2] )*detJt;\n\n\t\t\t\tke[i3+2][j3  ] += (Gzi*DBL[2][0] + Gyi*DBL[4][0] + Gxi*DBL[5][0] )*detJt;\n\t\t\t\tke[i3+2][j3+1] += (Gzi*DBL[2][1] + Gyi*DBL[4][1] + Gxi*DBL[5][1] )*detJt;\n\t\t\t\tke[i3+2][j3+2] += (Gzi*DBL[2][2] + Gyi*DBL[4][2] + Gxi*DBL[5][2] )*detJt;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element's geometrical stiffness component for each integration point\n\nvoid FE3FieldElasticSolidDomain::ElementGeometricalStiffness(int iel, matrix &ke)\n{\n\tFESolidElement &el = Element(iel);\n\n\tconst int NME = FEElement::MAX_NODES;\n\tdouble Gx[NME], Gy[NME], Gz[NME];\n\tdouble *Grn, *Gsn, *Gtn;\n\tdouble Gr, Gs, Gt;\n\n\t// nr of nodes\n\tint neln = el.Nodes();\n\n\t// nr of integration points\n\tint nint = el.GaussPoints();\n\n\t// jacobian\n\tdouble Ji[3][3], detJt;\n\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// stiffness component for the initial stress component of stiffness matrix\n\tdouble kab;\n\n\t// calculate geometrical element stiffness matrix\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdetJt = invjact(el, Ji, n, m_alphaf)*gw[n]*m_alphaf;\n\n\t\tGrn = el.Gr(n);\n\t\tGsn = el.Gs(n);\n\t\tGtn = el.Gt(n);\n\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tGr = Grn[i];\n\t\t\tGs = Gsn[i];\n\t\t\tGt = Gtn[i];\n\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tGx[i] = Ji[0][0]*Gr+Ji[1][0]*Gs+Ji[2][0]*Gt;\n\t\t\tGy[i] = Ji[0][1]*Gr+Ji[1][1]*Gs+Ji[2][1]*Gt;\n\t\t\tGz[i] = Ji[0][2]*Gr+Ji[1][2]*Gs+Ji[2][2]*Gt;\n\t\t}\n\n\t\t// get the material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// element's Cauchy-stress tensor at gauss point n\n\t\t// s is the voight vector\n\t\tmat3ds& s = pt.m_s;\n\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t\tfor (int j = i; j<neln; ++j)\n\t\t\t{\n\t\t\t\tkab = (Gx[i]*(s.xx()*Gx[j]+s.xy()*Gy[j]+s.xz()*Gz[j]) +\n\t\t\t\t\t   Gy[i]*(s.xy()*Gx[j]+s.yy()*Gy[j]+s.yz()*Gz[j]) + \n\t\t\t\t\t   Gz[i]*(s.xz()*Gx[j]+s.yz()*Gy[j]+s.zz()*Gz[j]))*detJt;\n\n\t\t\t\tke[3*i  ][3*j  ] += kab;\n\t\t\t\tke[3*i+1][3*j+1] += kab;\n\t\t\t\tke[3*i+2][3*j+2] += kab;\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function loops over all elements and updates the stress\nvoid FE3FieldElasticSolidDomain::Update(const FETimeInfo& tp)\n{\n\tbool berr = false;\n\tint NE = (int) m_Elem.size();\n\t#pragma omp parallel for shared(NE, berr)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\ttry\n\t\t{\n\t\t\tUpdateElementStress(i, tp);\n\t\t}\n\t\tcatch (NegativeJacobian e)\n\t\t{\n\t\t\t#pragma omp critical\n\t\t\t{\n\t\t\t\tberr = true;\n\t\t\t\tif (e.DoOutput()) feLogError(e.what());\n\t\t\t}\n\t\t}\n\t}\n\n\tif (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! This function updates the stresses for elements using the three-field formulation.\n//! For such elements, the stress is a sum of a deviatoric stress, calculate by the\n//! material and a dilatational term.\nvoid FE3FieldElasticSolidDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n\t// get the material\n\tFEUncoupledMaterial& mat = *(dynamic_cast<FEUncoupledMaterial*>(m_pMat));\n\n\t// get the solid element\n\tFESolidElement& el = m_Elem[iel];\n\tELEM_DATA& ed = m_Data[iel];\n\n\t// get the number of integration points\n\tint nint = el.GaussPoints();\n\n\t// get the integration weights\n\tdouble* gw = el.GaussWeights();\n\n\t// number of nodes\n\tint neln = el.Nodes();\n\n\t// nodal coordinates\n\tconst int NME = FEElement::MAX_NODES;\n\tvec3d r0[NME], r[NME], vel[NME], acc[NME];\n\tfor (int j=0; j<neln; ++j)\n\t{\n        FENode& node = m_pMesh->Node(el.m_node[j]);\n\t\tr0[j] = node.m_r0;\n        r[j] = node.m_rt*m_alphaf + node.m_rp*(1-m_alphaf);\n        vel[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*m_alphaf + node.m_vp*(1-m_alphaf);\n        acc[j] = node.m_at*m_alpham + node.m_ap*(1-m_alpham);\n\t}\n\n\t// calculate the average dilatation and pressure\n\tdouble v = 0, vt = 0, V = 0;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tv += detJt(el, n, m_alphaf)*gw[n];\n        vt+= detJt(el, n)*gw[n];\n\t\tV += detJ0(el, n)*gw[n];\n\t}\n\n\t// calculate volume ratio\n\ted.eJ = v / V;\n    ed.eJt = vt / V;\n    double eUt = mat.U(ed.eJt);\n    double eUp = mat.U(ed.eJp);\n\n\t// Calculate pressure. This is a sum of a Lagrangian term and a penalty term\n\t//      <--- Lag. mult. -->  <-- penalty -->\n\ted.ep = ed.Lk*mat.hp(ed.eJ) + mat.UJ(ed.eJ);\n//\ted.ep = mat.UJ(ed.eJ);\n\n\t// loop over the integration points and calculate\n\t// the stress at the integration point\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        pt.m_p = ed.ep;\n\n\t\t// material point coordinates\n\t\t// TODO: I'm not entirly happy with this solution\n\t\t//\t\t since the material point coordinates are not used by most materials.\n\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\tmp.m_rt = el.Evaluate(r, n);\n\n\t\t// get the deformation gradient and determinant\n        double Jt, Jp;\n        mat3d Ft, Fp;\n        Jt = defgrad(el, Ft, n);\n        Jp = defgradp(el, Fp, n);\n        pt.m_F = (m_alphaf==1.0? Ft : Ft*m_alphaf + Fp*(1-m_alphaf));\n        pt.m_J = pt.m_F.det();\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (Ft - Fp)*Fi/dt;\n        pt.m_v = el.Evaluate(vel, n);\n        pt.m_a = el.Evaluate(acc, n);\n\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, tp);\n        \n\t\t// calculate the stress at this material point\n\t\t// Note that we don't call the material's Stress member function.\n\t\t// The reason is that we need to use the averaged pressure for the element\n\t\t// and the Stress function uses the pointwise pressure. \n\t\t// Therefore we call the DevStress function and add the pressure term\n\t\t// seperately. \n\t\tpt.m_s = mat.DevStress(mp);\n        \n        // adjust stress for strain energy conservation\n        if (m_alphaf == 0.5) \n\t\t{\n\t\t\t// evaluate deviatoric strain energy at current and previous time\n\t\t\tmat3d Ftmp = pt.m_F;\n\t\t\tdouble Jtmp = pt.m_J;\n\t\t\tpt.m_F = Ft;\n\t\t\tpt.m_J = Jt;\n\t\t\tdouble Wt = mat.DevStrainEnergyDensity(mp);\n\t\t\tpt.m_F = Ftmp;\n\t\t\tpt.m_J = Jtmp;\n\n\t\t\t// store total strain energy density at current time\n\t\t\tpt.m_Wt = Wt + eUt;\n\n\t\t\tdouble Wp = pt.m_Wp;\n            mat3ds D = pt.RateOfDeformation();\n            double D2 = D.dotdot(D);\n            if (D2 > std::numeric_limits<double>::epsilon())\n                pt.m_s += D*(((Wt-Wp)/(dt*pt.m_J) - pt.m_s.dotdot(D))/D2);\n            if (fabs(ed.eJt - ed.eJp) > std::numeric_limits<double>::epsilon())\n                pt.m_s += mat3dd((eUt-eUp)/(ed.eJ*(ed.eJt-ed.eJp)));\n        }\n        else\n            pt.m_s += mat3dd(ed.ep);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Do augmentation\nbool FE3FieldElasticSolidDomain::Augment(int naug)\n{\n\tFEUncoupledMaterial* pmi = dynamic_cast<FEUncoupledMaterial*>(m_pMat);\n\tassert(pmi);\n\n\t// make sure Augmented Lagrangian flag is on\n\tif (m_blaugon == false) return true;\n\n\t// do the augmentation\n\tint n;\n\tdouble normL0 = 0, normL1 = 0, L0, L1;\n\tdouble k = pmi->m_K;\n\tFEMesh& mesh = *m_pMesh;\n\tint NE = Elements();\n\n\tfor (n=0; n<NE; ++n)\n\t{\n\t\tELEM_DATA& ed = m_Data[n];\n\n\t\tL0 = ed.Lk;\n\t\tnormL0 += L0*L0;\n\n\t\tL1 = L0 + k*pmi->h(ed.eJ);\n\t\tnormL1 += L1*L1;\n\t}\n\n\tnormL0 = sqrt(normL0);\n\tnormL1 = sqrt(normL1);\n\n\t// check convergence\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\n\tfeLog(\" material %d\\n\", pmi->GetID());\n\tfeLog(\"                        CURRENT         CHANGE        REQUIRED\\n\");\n\tfeLog(\"   pressure norm : %15le%15le%15le\\n\", normL1, pctn, m_augtol);\n\n\t// check convergence\n\tbool bconv = true;\n\tif (pctn >= m_augtol) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif ((m_naugmax > 0) && (m_naugmax <= naug)) bconv = true;\n\n\t// do the augmentation only if we have not yet converged\n\tif (bconv == false)\n\t{\n\t\tfor (n=0; n<NE; ++n)\n\t\t{\n\t\t\tELEM_DATA& ed = m_Data[n];\n\n\t\t\tdouble hi = pmi->h(ed.eJ);\n\t\t\ted.Lk += k*pmi->h(ed.eJ);\n\t\t\ted.ep = ed.Lk*pmi->hp(ed.eJ) + k*log(ed.eJ)/ed.eJ;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Data serialization\nvoid FE3FieldElasticSolidDomain::Serialize(DumpStream &ar)\n{\n\tFEElasticSolidDomain::Serialize(ar);\n\tar & m_Data;\n}\n"
  },
  {
    "path": "FEBioMech/FE3FieldElasticSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! The following domain implements the finite element formulation for a three-field\n//! volume element. \nclass FEBIOMECH_API FE3FieldElasticSolidDomain : public FEElasticSolidDomain\n{\nprotected:\n\tstruct ELEM_DATA\n\t{\n\t\tdouble\teJ;\t\t// average element jacobian at intermediate time\n\t\tdouble\tep;\t\t// average pressure\n\t\tdouble\tLk;\t\t// Lagrangian multiplier\n        double  eJt;    // average element jacobian at current time\n        double  eJp;    // average element jacobian at previous time\n\n\t\tvoid Serialize(DumpStream& ar);\n\t};\n\npublic:\n\t//! constructor\n\tFE3FieldElasticSolidDomain(FEModel* pfem);\n\n\t//! \\todo Do I really use this?\n\tFE3FieldElasticSolidDomain& operator = (FE3FieldElasticSolidDomain& d);\n\n\t//! initialize class\n\tbool Init() override;\n\n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n\t//! Reset data\n\tvoid Reset() override;\n\n\t//! augmentation\n\tbool Augment(int naug) override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n    \npublic: // overridden from FEElasticDomain\n\n\t// update stresses\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t// calculate stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\t//! Dilatational stiffness component for nearly-incompressible materials\n\tvoid ElementDilatationalStiffness(FEModel& fem, int iel, matrix& ke);\n\n\t//! material stiffness component\n\tvoid ElementMaterialStiffness(int iel, matrix& ke);\n\n\t//! geometrical stiffness (i.e. initial stress)\n\tvoid ElementGeometricalStiffness(int iel, matrix& ke);\n\n\t//! update the stress of an element\n\tvoid UpdateElementStress(int iel, const FETimeInfo& tp) override;\n\npublic:\n\tbool DoAugmentations() const;\n\nprotected:\n\tvector<ELEM_DATA>\tm_Data;\n\n\tbool\tm_blaugon;\t\t//!< augmented lagrangian flag\n\tdouble\tm_augtol;\t\t//!< augmented lagrangian tolerance\n\tint\t\tm_naugmin;\t\t//!< minimum number of augmentations\n\tint\t\tm_naugmax;\t\t//!< max number of augmentations\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEABUnconstrained.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\r\nlisted below.\r\n\r\nSee Copyright-FEBio.txt for details.\r\n\r\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in \r\nthe City of New York, and others.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.*/\r\n\r\n\r\n#include \"FECore/stdafx.h\"\r\n#include \"FEABUnconstrained.h\"\r\n#include <iostream>\r\n#include <typeinfo>\r\nusing namespace std;\r\n\r\nBEGIN_FECORE_CLASS(FEABUnconstrained, FEElasticMaterial);\r\n\tADD_PARAMETER(m_ksi, FE_RANGE_GREATER(0.0), \"ksi\");\r\n\tADD_PARAMETER(m_N, FE_RANGE_GREATER(0.0), \"N\");\r\n\tADD_PARAMETER(m_term, FE_RANGE_CLOSED(3,30), \"n_term\");\r\n\tADD_PARAMETER(m_kappa, FE_RANGE_GREATER(0.0), \"kappa\");\r\nEND_FECORE_CLASS();\r\n\r\n//-----------------------------------------------------------------------------\r\n//! constructor\r\nFEABUnconstrained::FEABUnconstrained(FEModel* pfem) : FEElasticMaterial(pfem)\r\n{\r\n\tm_N = 100.0; \r\n\tm_ksi = 0.00001;\r\n\tm_kappa = 0.0;\r\n\tm_term = 30;\r\n\tm_eps = 1e-12;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\nvoid FEABUnconstrained::EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps)\r\n{\r\n\tA.eigen(l, r);\r\n\t\r\n\t// correct for numerical inaccuracy\r\n\tdouble d01 = fabs(l[0] - l[1]);\r\n\tdouble d12 = fabs(l[1] - l[2]);\r\n\tdouble d02 = fabs(l[0] - l[2]);\r\n\t\r\n\tif (d01 < eps) l[1] = l[0]; //= 0.5*(l[0]+l[1]);\r\n\tif (d02 < eps) l[2] = l[0]; //= 0.5*(l[0]+l[2]);\r\n\tif (d12 < eps) l[2] = l[1]; //= 0.5*(l[1]+l[2]);\r\n\t\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\n//! Calculates the Cauchy stress\r\nmat3ds FEABUnconstrained::Stress(FEMaterialPoint& mp)\r\n{\r\n\tdouble ksi = m_ksi(mp);\r\n\tdouble kappa = m_kappa(mp);\r\n\t// extract elastic material data\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\r\n\t// jacobian\r\n\tdouble J = pt.m_J;\r\n\r\n\t// get the left Cauchy-Green tensor\r\n\tmat3ds b = pt.LeftCauchyGreen();\r\n\r\n\t// get the eigenvalues and eigenvectors of b\r\n\tdouble lam2[3];\t// these are the squares of the eigenvalues of V\r\n\tvec3d ev[3];\r\n\tEigenValues(b, lam2, ev, m_eps);\r\n\r\n\t// get the eigenvalues of V\r\n\tdouble lam[3];\r\n\tmat3ds N[3];\r\n\tfor (int i = 0; i < 3; ++i) {\r\n\t\tlam[i] = sqrt(lam2[i]);\r\n\t\tN[i] = dyad(ev[i]);\r\n\t}\r\n\r\n\t// Invariants of b\r\n\tdouble I1 = b.tr();\r\n\r\n\t// stress\r\n\tmat3ds s;\r\n\ts.zero();\r\n\r\n\tconst double f_a[] = { 3.0, 9.0 / 5.0, 297.0 / 175.0, 1539.0 / 875.0, 126117.0 / 67373.0, 43733439.0 / 21896875.0, \\\r\n\t\t231321177.0 / 109484375.0, 20495009043.0 / 9306171875.0, 1073585186448381.0 / 476522530859375.0, 4387445039583.0 / 1944989921875.0, \\\r\n\t\t\t1000263375846831627.0 / 453346207767578125.0, 280865021365240713.0 / 133337119931640625.0, 148014872740758343473.0 / 75350125192138671875.0, 137372931237386537808993.0 / 76480377070020751953125.0, \\\r\n\t\t\t41722474198742657618857192737.0 / 25674386102028896409912109375.0, 12348948373636682700768301125723.0 / 8344175483159391333221435546875.0, 5001286000741585238340074032091091.0 / 3590449627018291035442047119140625.0, \\\r\n\t\t\t185364329915163811141785118512534489.0 / 132846636199676768311355743408203125.0, 6292216384025878939310787532157558451.0 / 4160197291516193533960877227783203125.0, 299869254759556271677902570230858640837.0 / 170568088952163934892395966339111328125.0, \\\r\n\t\t\t316689568216860631885141475537178451746044283.0 / 148810301691076651811854156590061187744140625.0, 670194310437429598283653289122392937145371137.0 / 258140319260030926612400067554187774658203125.0, 19697015384373759058671314622426656486031717197919.0 / 6332687088594936951180328352888571262359619140625.0, \\\r\n\t\t\t178793788985653424246012689916144867915861856840849.0 / 49756827124674504616416865629838774204254150390625.0, 323844166067349737493036492206152479344269351967043143667.0 / 82039106319990447886052744467343800746748447418212890625.0, 200808116689754604893460969866238617668631975356485302537199.0 / 49409916306357883385918130190559334540655314922332763671875.0, \\\r\n\t\t\t27506481209689719715275759452624078040221544551995885750037973.0 / 7164437864421893090958128877631103508395020663738250732421875.0, 16356939619211770477227805130221533318985185730316048281126247721.0 / 5122573073061653560035062147506239008502439774572849273681640625.0, 30976199222209837888906735596203249520053107250807132769871859115868101.0 / 14801698741072505317694781203956860833766632412399612367153167724609375.0, \\\r\n\t\t\t519588001407316958447129785511020819131555326399179970047767492196701159.0 / 902903623205422824379381653441368510859764577156376354396343231201171875.0 };\r\n\tdouble alpha = sqrt(I1) / sqrt(3.0 * m_N);\r\n\tdouble alpha0 = 1.0 / sqrt(m_N);\r\n\r\n\t///////////////////////////////\r\n\tdouble beta = 0, beta0 = 0, alpha2 = alpha * alpha, alpha02 = alpha0 * alpha0;\r\n\tint num_fa = sizeof(f_a) / sizeof(f_a[0]);\r\n\tif (m_term > num_fa) {\r\n\t\tm_term = num_fa;\r\n\t}\r\n\tbeta = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2;\r\n\tbeta0 = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha02;\r\n\tfor (int i = 2; i < m_term; i++) {\r\n\t\tbeta = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta;\r\n\t\tbeta0 = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha02 * beta0;\r\n\t};\r\n\tbeta = f_a[0] * alpha * beta;\r\n\tbeta0 = f_a[0] * alpha0 * beta0;\r\n\t////////////////////////////////\r\n\r\n\tdouble WI = ksi * m_N * (beta / sqrt(I1) / 2.0 / sqrt(3.0 * m_N));\r\n\tdouble W1[3];\r\n\tdouble T[3], T_NH[3];\r\n\tdouble I1_1[3];\r\n\tdouble W10 = ksi * m_N * (beta0 / sqrt(3.0) / 2.0 / sqrt(3.0 * m_N)) * 2.0;\r\n\r\n\t/////////////////////////////////////////////////////////////////////\r\n\tdouble aaaI = exp((I1) / 3 - 1); //Simple\r\n\r\n\t//double TCom = kappa * log(J) / J; //NH Term\r\n\tdouble TCom = kappa * (J * J - 1) / J; //NH Term\r\n\r\n\tfor (int i = 0; i < 3; ++i) {\r\n\t\tI1_1[i] = 2 * lam[i];\r\n\t\tW1[i] = WI * I1_1[i] - W10 / lam[i];\r\n\t\tT[i] = lam[i] * W1[i] / J;\r\n\t\tT[i] += TCom; //NH Term \r\n\t\ts += N[i] * T[i];\r\n\t}\r\n\r\n\r\n\treturn s;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\n//! Calculates the spatial tangent\r\ntens4ds FEABUnconstrained::Tangent(FEMaterialPoint& mp)\r\n{\r\n\tdouble ksi = m_ksi(mp);\r\n\tdouble kappa = m_kappa(mp);\r\n\tint i, j;\r\n\r\n\t// extract elastic material data\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\r\n\t// jacobian\r\n\tdouble J = pt.m_J;\r\n\r\n\t// get the left Cauchy-Green tensor\r\n\tmat3ds b = pt.LeftCauchyGreen();\r\n\r\n\t// Invariants of b\r\n\tdouble I1 = b.tr();\r\n\r\n\t// get the eigenvalues and eigenvectors of b\r\n\tdouble lam2[3];\t// these are the squares of the eigenvalues of V\r\n\tvec3d ev[3];\r\n\tEigenValues(b, lam2, ev, m_eps);\r\n\r\n\t// get the eigenvalues of V\r\n\tdouble lam[3];\r\n\tmat3ds N[3];\r\n\tfor (i = 0; i < 3; ++i) {\r\n\t\tlam[i] = sqrt(lam2[i]);\r\n\t\tN[i] = dyad(ev[i]);\r\n\t}\r\n\r\n\t// principal stresses\r\n\tmat3ds s;\r\n\ts.zero();\r\n\r\n\tconst double f_a[] = { 3.0, 9.0 / 5.0, 297.0 / 175.0, 1539.0 / 875.0, 126117.0 / 67373.0, 43733439.0 / 21896875.0, \\\r\n\t\t231321177.0 / 109484375.0, 20495009043.0 / 9306171875.0, 1073585186448381.0 / 476522530859375.0, 4387445039583.0 / 1944989921875.0, \\\r\n\t\t\t1000263375846831627.0 / 453346207767578125.0, 280865021365240713.0 / 133337119931640625.0, 148014872740758343473.0 / 75350125192138671875.0, 137372931237386537808993.0 / 76480377070020751953125.0, \\\r\n\t\t\t41722474198742657618857192737.0 / 25674386102028896409912109375.0, 12348948373636682700768301125723.0 / 8344175483159391333221435546875.0, 5001286000741585238340074032091091.0 / 3590449627018291035442047119140625.0, \\\r\n\t\t\t185364329915163811141785118512534489.0 / 132846636199676768311355743408203125.0, 6292216384025878939310787532157558451.0 / 4160197291516193533960877227783203125.0, 299869254759556271677902570230858640837.0 / 170568088952163934892395966339111328125.0, \\\r\n\t\t\t316689568216860631885141475537178451746044283.0 / 148810301691076651811854156590061187744140625.0, 670194310437429598283653289122392937145371137.0 / 258140319260030926612400067554187774658203125.0, 19697015384373759058671314622426656486031717197919.0 / 6332687088594936951180328352888571262359619140625.0, \\\r\n\t\t\t178793788985653424246012689916144867915861856840849.0 / 49756827124674504616416865629838774204254150390625.0, 323844166067349737493036492206152479344269351967043143667.0 / 82039106319990447886052744467343800746748447418212890625.0, 200808116689754604893460969866238617668631975356485302537199.0 / 49409916306357883385918130190559334540655314922332763671875.0, \\\r\n\t\t\t27506481209689719715275759452624078040221544551995885750037973.0 / 7164437864421893090958128877631103508395020663738250732421875.0, 16356939619211770477227805130221533318985185730316048281126247721.0 / 5122573073061653560035062147506239008502439774572849273681640625.0, 30976199222209837888906735596203249520053107250807132769871859115868101.0 / 14801698741072505317694781203956860833766632412399612367153167724609375.0, \\\r\n\t\t\t519588001407316958447129785511020819131555326399179970047767492196701159.0 / 902903623205422824379381653441368510859764577156376354396343231201171875.0 };\r\n\tdouble alpha = sqrt(I1) / sqrt(3.0 * m_N);\r\n\tdouble alpha0 = 1.0 / sqrt(m_N);\r\n\r\n\t\t//////////////////////////////////////\r\n\tdouble beta = 0, beta0 = 0, beta_alpha = 0, alpha2 = alpha * alpha, alpha02 = alpha0 * alpha0;\r\n\tint num_fa = sizeof(f_a) / sizeof(f_a[0]);\r\n\tif (m_term > num_fa) {\r\n\t\tm_term = num_fa;\r\n\t}\r\n\tbeta = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2;\r\n\tbeta0 = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha02;\r\n\tbeta_alpha = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2 * (2 * m_term - 1) / (2 * m_term - 3);\r\n\tfor (int i = 2; i < m_term; i++) {\r\n\t\tbeta = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta;\r\n\t\tbeta0 = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha02 * beta0;\r\n\t\tbeta_alpha = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta_alpha * (2 * m_term - 2 * i + 1) / (2 * m_term - 2 * i - 1);\r\n\t};\r\n\tbeta = f_a[0] * alpha * beta;\r\n\tbeta0 = f_a[0] * alpha0 * beta0;\r\n\tbeta_alpha = f_a[0] * beta_alpha;\r\n\t////////////////////////////////////////\r\n\r\n\tdouble WI = ksi * m_N * (beta / sqrt(I1) / 2.0 / sqrt(3.0 * m_N));\r\n\tdouble WII = ksi * m_N * (beta_alpha / I1 / sqrt(3.0 * m_N) - beta * pow(I1, (-3.0 / 2.0))) / 4.0 / sqrt(3.0 * m_N);\r\n\tdouble W1[3];\r\n\tdouble W11[3][3];\r\n\tdouble T[3], T_NH[3];\r\n\tdouble I1_1[3];\r\n\tdouble I1_11 = 2;\r\n\tdouble W10 = ksi * m_N * (beta0 / sqrt(3.0) / 2.0 / sqrt(3 * m_N)) * 2.0;\r\n\r\n\t/////////////////////////////////////////////////////////////////////\r\n\r\n\tdouble TCom = kappa * (J * J - 1) / J; //NH Term\r\n\r\n\tfor (int i = 0; i < 3; ++i) {\r\n\t\tI1_1[i] = 2 * lam[i];\r\n\t\tW1[i] = WI * I1_1[i] - W10 / lam[i];\r\n\t\tT[i] = lam[i] * W1[i] / J;\r\n\t\tT[i] += TCom;\r\n\t\ts += N[i] * T[i];\r\n\t}\r\n\r\n\t// coefficients appearing in elasticity tensor\r\n\tdouble D[3][3], E[3][3];\r\n\r\n\tdouble D_NH[3][3], E_NH[3][3]; //NH Term     \r\n\r\n\tdouble DComii = 2 * kappa / J + TCom; //NH Term\r\n\tdouble DComij = 2 * kappa * J; //NH Term\r\n\tdouble EComij = 2 * (-kappa * J * J + kappa) / J; //NH Term\r\n\r\n\tfor (i = 0; i < 3; ++i) {\r\n\r\n\t\tI1_1[i] = 2 * lam[i];\r\n\t\tW11[i][i] = WII * I1_1[i] * I1_1[i] + WI * I1_11 + W10 / lam2[i];\r\n\t\tD[i][i] = lam2[i] * W11[i][i] / J - T[i];\r\n\t\tD[i][i] += DComii;\r\n\t\tfor (j = i + 1; j < 3; ++j) {\r\n\r\n\t\t\tW11[i][j] = WII * I1_1[i] * I1_1[j];\r\n\t\t\tD[i][j] = lam[i] * lam[j] * W11[i][j] / J;\r\n\t\t\tD[i][j] += DComij;\r\n\r\n\t\t\tif (lam2[j] != lam2[i]) {\r\n\r\n\t\t\t\tE[i][j] = 2 * (lam2[j] * T[i] - lam2[i] * T[j]) / (lam2[i] - lam2[j]);\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tE[i][j] = 2 * W10 / J;\r\n\t\t\t\tE[i][j] += EComij;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// spatial elasticity tensor\r\n\ttens4ds c(0.0);\r\n\tmat3dd I(1.0);\r\n\tfor (i = 0; i < 3; ++i) {\r\n\t\tc += dyad1s(N[i]) * D[i][i];\r\n\t\tfor (j = i + 1; j < 3; ++j) {\r\n\t\t\tc += dyad1s(N[i], N[j]) * D[i][j];\r\n\t\t\tc += dyad4s(N[i], N[j]) * E[i][j];\r\n\t\t}\r\n\t}\r\n\r\n\treturn c;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\ndouble FEABUnconstrained::StrainEnergyDensity(FEMaterialPoint& mp)\r\n{\r\n\tdouble ksi = m_ksi(mp);\r\n\tdouble kappa = m_kappa(mp);\r\n\t// extract elastic material data\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\t\r\n\t// jacobian\r\n\tdouble J = pt.m_J;\r\n    double lnJ = log(J);\r\n\t\r\n\t// get the left Cauchy-Green tensor\r\n\tmat3ds b = pt.LeftCauchyGreen();\r\n\t\r\n\t// get the eigenvalues and eigenvectors of b\r\n\tdouble lam2[3];\t// these are the squares of the eigenvalues of V\r\n\tvec3d ev[3];\r\n\tEigenValues(b, lam2, ev, m_eps);\r\n\t\r\n\t// get the eigenvalues of V\r\n\tdouble lam[3];\r\n\tlam[0] = sqrt(lam2[0]);\r\n\tlam[1] = sqrt(lam2[1]);\r\n\tlam[2] = sqrt(lam2[2]);\r\n\t\r\n\t// Invariants of b\r\n\tdouble I1 = b.tr();\r\n\r\n\t// strain energy density\r\n\r\n\tdouble alpha = sqrt(I1) / sqrt(3*m_N);\r\n\tdouble beta = alpha * (3.0 - alpha * alpha) / (1.0 - alpha * alpha) -0.5 * pow(alpha, (10.0 / 3.0)) + 3.0 * pow(alpha, 5.0)*(alpha - 0.76)*(alpha - 1.0);\r\n\tdouble alpha0 = 1 / sqrt(m_N);\r\n\tdouble beta0 = alpha0 * (3.0 - alpha0 * alpha0) / (1.0 - alpha0 * alpha0) -0.5 * pow(alpha0, (10.0 / 3.0)) + 3.0 * pow(alpha0, 5.0)*(alpha0 - 0.76)*(alpha0 - 1.0);\r\n\t\r\n\tdouble W10 = ksi * m_N * (beta0 / sqrt(3) / 2 / sqrt(3 * m_N)) * 2;\r\n\tdouble sedCom = kappa / kappa / kappa * cosh(kappa*(J - 1) - 1);\r\n\r\n\tdouble sed = ksi * m_N * (sqrt(I1)*beta / sqrt(3 * m_N) + log(beta / sinh(beta))) - W10 * lnJ +sedCom;\r\n\t\r\n\treturn sed;\r\n}\r\n"
  },
  {
    "path": "FEBioMech/FEABUnconstrained.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEBioMech/FEUncoupledMaterial.h\"\n\nclass FEABUnconstrained : public FEElasticMaterial\n{\n//public:\n\t//enum { MAX_TERMS = 6 };\npublic:\n\tFEABUnconstrained(FEModel* pfem);\n\t\n\t//! calculate the stress\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate the tangent\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\t\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \nprotected:\n\tvoid EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps = 0);\n\tdouble\tm_eps;\n\t\npublic:\n\t//FEParamDouble m_ksi;\n\tdouble m_N; \n\tint m_term;\n\tFEParamDouble m_ksi;\n\tFEParamDouble m_kappa;\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEActiveContractionMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEActiveContractionMaterial.h\"\n"
  },
  {
    "path": "FEBioMech/FEActiveContractionMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! A material class describing the active fiber contraction model of Estrada et al. in doi: 10.1115/1.4044030\nclass FEBIOMECH_API FEActiveContractionMaterial : public FEMaterialProperty\n{\npublic:\n    FEActiveContractionMaterial(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    virtual ~FEActiveContractionMaterial(){}\n    \n    //! calculate the active contractile stress\n    virtual mat3ds ActiveStress(FEMaterialPoint& mp, const vec3d& a0) = 0;\n    \n    //! active contraction stiffness contribution\n    virtual tens4ds ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0) = 0;\n    \n    //! update force-velocity material point\n    virtual void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp, const vec3d& a0) {}\n\n    FECORE_BASE_CLASS(FEActiveContractionMaterial)\n};\n"
  },
  {
    "path": "FEBioMech/FEActiveFiberContraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEActiveFiberContraction.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEActiveFiberContraction, FEActiveContractionMaterial);\n\tADD_PARAMETER(m_ascl , \"ascl\");\n\tADD_PARAMETER(m_Tmax , \"Tmax\");\n\tADD_PARAMETER(m_ca0  , \"ca0\");\n\tADD_PARAMETER(m_camax, \"camax\");\n\tADD_PARAMETER(m_beta , \"beta\");\n\tADD_PARAMETER(m_l0   , \"l0\");\n\tADD_PARAMETER(m_refl , \"refl\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEActiveFiberContraction::FEActiveFiberContraction(FEModel* pfem) : FEActiveContractionMaterial(pfem)\n{\n\tm_ascl = 0;\n\tm_Tmax = 1.0;\n\tm_ca0 = 1.0;\n\tm_camax = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEActiveFiberContraction::Init()\n{\n\tif (FEActiveContractionMaterial::Init() == false) return false;\n\n\t// for backward compatibility we set m_camax to m_ca0 if it is not defined\n\tif (m_camax == 0.0) m_camax = m_ca0;\n\tif (m_camax <= 0.0) { feLogError(\"camax must be larger than zero\"); return false; }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEActiveFiberContraction::ActiveStress(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam*Jm13; // i.e. lambda tilde\n\n\t// calculate dyad of a: AxA = (a x a)\n\tmat3ds AxA = dyad(a);\n\n\t// get the activation\n\tdouble saf = 0.0;\n\tif (m_ascl > 0)\n\t{\n\t\t// current sarcomere length\n\t\tdouble strl = m_refl*lamd;\n\n\t\t// sarcomere length change\n\t\tdouble dl = strl - m_l0;\n\n\t\tif (dl >= 0)\n\t\t{\n\t\t\t// calcium sensitivity\n\t\t\tdouble eca50i = (exp(m_beta*dl) - 1);\n\n\t\t\t// ratio of Camax/Ca0\n\t\t\tdouble rca = m_camax / m_ca0;\n\n\t\t\t// active fiber stress\n\t\t\tsaf = m_Tmax*(eca50i / (eca50i + rca*rca))*m_ascl;\n\t\t}\n\t}\n\treturn AxA*saf;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEActiveFiberContraction::ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam*Jm13; // i.e. lambda tilde\n\n\t// calculate dyad of a: AxA = (a x a)\n\tmat3ds AxA = dyad(a);\n\ttens4ds AxAxAxA = dyad1s(AxA);\n\n\tdouble c = 0;\n\tif (m_ascl > 0)\n\t{\n\t\t// current sarcomere length\n\t\tdouble strl = m_refl*lamd;\n\n\t\t// sarcomere length change\n\t\tdouble dl = strl - m_l0;\n\n\t\tif (dl >= 0)\n\t\t{\n\t\t\t// calcium sensitivity\n\t\t\tdouble eca50i = (exp(m_beta*dl) - 1);\n\n\t\t\tdouble decl = m_beta*m_refl*exp(m_beta*dl);\n\n\t\t\t// ratio of Camax/Ca0\n\t\t\tdouble rca = m_camax / m_ca0;\n\n\t\t\tdouble d = eca50i + rca*rca;\n\n\t\t\t// active fiber stress\n\t\t\tdouble saf = m_Tmax*(eca50i / d)*m_ascl;\n\n\t\t\tdouble dsf = m_Tmax*m_ascl*decl*(1.0/ d - eca50i / (d*d));\n\n\t\t\tc = (lamd*dsf - 2.0*saf);\n\t\t}\n\t}\n\n\treturn AxAxAxA*c;\n}\n"
  },
  {
    "path": "FEBioMech/FEActiveFiberContraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEActiveContractionMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! A material class describing the active fiber contraction\nclass FEActiveFiberContraction : public FEActiveContractionMaterial\n{\npublic:\n\tFEActiveFiberContraction(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! calculate the fiber stress\n\tmat3ds ActiveStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! active contraction stiffness contribution\n\ttens4ds ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0) override;\n\nprotected:\n\tdouble\tm_ascl;\t\t//!< activation scale factor\n\tdouble\tm_Tmax;\t\t//!< activation scale factor\n\tdouble\tm_ca0;\t\t//!< intracellular calcium concentration\n\tdouble\tm_camax;\t//!< peak calcium concentration\n\tdouble\tm_beta;\t\t//!< shape of peak isometric tension-sarcomere length relation\n\tdouble\tm_l0;\t\t//!< unloaded length\n\tdouble\tm_refl;\t\t//!< sarcomere length\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEActiveFiberStress.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEActiveFiberStress.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/log.h>\n\nvoid FEActiveFiberStress::Data::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_lamp;\n}\n\n//=====================================================================================\n\nBEGIN_FECORE_CLASS(FEActiveFiberStress, FEElasticMaterial);\n\tADD_PARAMETER(m_smax, \"smax\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_ac, \"activation\");\n\n\tADD_PROPERTY(m_stl, \"stl\", FEProperty::Optional);\n\tADD_PROPERTY(m_stv, \"stv\", FEProperty::Optional);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\nFEActiveFiberStress::FEActiveFiberStress(FEModel* fem) : FEElasticMaterial(fem)\n{\n\tm_smax = 0.0;\n\tm_ac = 0.0;\n\n\tm_stl = nullptr;\n\tm_stv = nullptr;\n}\n\nmat3ds FEActiveFiberStress::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble dt = 0.0;\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3d Q = GetLocalCS(mp);\n\n\tvec3d a0 = Q.col(0);\n\n\tvec3d a = F*a0;\n\tdouble lam = a.unit();\n\n\tdouble stl = (m_stl ? m_stl->value(lam) : 1.0);\n\tdouble v = 0;// (lam - lamp) / dt;\n\tdouble stv = (m_stv ? m_stv->value(v) : 1.0);\n\n\tmat3ds A = dyad(a);\n\n\tdouble sf = m_ac*m_smax*stl*stv;\n\n\treturn A*(sf / J);\n}\n\ntens4ds FEActiveFiberStress::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble dt = 1.0;\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3d Q = GetLocalCS(mp);\n\n\tvec3d a0 = Q.col(0);\n\n\tvec3d a = F*a0;\n\tdouble lam = a.unit();\n\n\tmat3ds A = dyad(a);\n\n\tmat3dd I(1.0);\n\n\ttens4ds AA = dyad1s(A);\n\n\tdouble stl = (m_stl ? m_stl->value(lam) : 1.0);\n\tdouble v = 0;// (lam - lamp) / dt;\n\tdouble stv = (m_stv ? m_stv->value(v) : 1.0);\n\n\tdouble dstl = (m_stl ? m_stl->derive(lam) : 0.0);\n\tdouble dstv = (m_stv ? m_stv->derive(v) / dt : 0.0);\n\n\tdouble sf = m_ac*m_smax*stl*stv;\n\tdouble sf_l = m_ac*m_smax*(dstl*stv + stl*dstv);\n\n\ttens4ds c = AA*((lam*sf_l - 2.0*sf) / J);\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEActiveFiberStress.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"FEElasticMaterial.h\"\n#include <FECore/tens4d.h>\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\nclass FEActiveFiberStress : public FEElasticMaterial\n{\npublic:\n\tclass Data : public FEMaterialPointData\n\t{\n\tpublic:\n\t\tdouble\tm_lamp;\n\n\tpublic:\n\t\tvoid Update()\n\t\t{\n\t\t\t// TODO:\n\t\t}\n\n\t\tvoid Serialize(DumpStream& ar) override;\n\t};\n\npublic:\n\tFEActiveFiberStress(FEModel* fem);\n\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\nprivate:\n\tdouble\tm_smax;\t\t//!< peak stress\n\tdouble\tm_ac;\t\t//!< activation level\n\tFEFunction1D*\tm_stl;\t//!< stress from tension-length \n\tFEFunction1D*\tm_stv;\t//!< stress from tension-velocity\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEActiveFiberStressUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEActiveFiberStressUC.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/log.h>\n\nvoid FEActiveFiberStressUC::Data::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_lamp;\n}\n\n//=====================================================================================\n\nBEGIN_FECORE_CLASS(FEActiveFiberStressUC, FEUncoupledMaterial);\n\tADD_PARAMETER(m_smax, \"smax\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_ac, \"activation\");\n\n\tADD_PROPERTY(m_stl, \"stl\", FEProperty::Optional);\n\tADD_PROPERTY(m_stv, \"stv\", FEProperty::Optional);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\nFEActiveFiberStressUC::FEActiveFiberStressUC(FEModel* fem) : FEUncoupledMaterial(fem)\n{\n\tm_smax = 0.0;\n\tm_ac = 0.0;\n\n\tm_stl = nullptr;\n\tm_stv = nullptr;\n}\n\nmat3ds FEActiveFiberStressUC::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble dt = 0.0;\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble J13 = pow(J, 1.0 / 3.0);\n\n\tmat3d Q = GetLocalCS(mp);\n\n\tvec3d a0 = Q.col(0);\n\n\tvec3d a = F*a0;\n\tdouble lam = a.unit() / J13;\n\n\tdouble stl = (m_stl ? m_stl->value(lam) : 1.0);\n\tdouble v = 0;// (lam - lamp) / dt;\n\tdouble stv = (m_stv ? m_stv->value(v) : 1.0);\n\n\tmat3ds A = dyad(a);\n\n\tdouble sf = m_ac*m_smax*stl*stv;\n\n\treturn A.dev()*(sf / J);\n}\n\ntens4ds FEActiveFiberStressUC::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble dt = 1.0;\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble J13 = pow(J, 1.0 / 3.0);\n\n\tmat3d Q = GetLocalCS(mp);\n\n\tvec3d a0 = Q.col(0);\n\n\tvec3d a = F*a0;\n\tdouble lam = a.unit() / J13;\n\n\tmat3ds A = dyad(a);\n\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4 = dyad4s(I);\n\n\ttens4ds AA = dyad1s(A.dev());\n\ttens4ds AI = dyad1s(A, I);\n\n\tdouble stl = (m_stl ? m_stl->value(lam) : 1.0);\n\tdouble v = 0;// (lam - lamp) / dt;\n\tdouble stv = (m_stv ? m_stv->value(v) : 1.0);\n\n\tdouble dstl = (m_stl ? m_stl->derive(lam) : 0.0);\n\tdouble dstv = (m_stv ? m_stv->derive(v) / dt : 0.0);\n\n\tdouble sf = m_ac*m_smax*stl*stv;\n\tdouble sf_l = m_ac*m_smax*(dstl*stv + stl*dstv);\n\n\ttens4ds cff = AA*(lam*sf_l - 2.0*sf);\n\ttens4ds cf = (AI - I4 - IxI/3.0)*(2.0*sf/3.0);\n\n\ttens4ds c = (cff - cf) / J;\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEActiveFiberStressUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include <FECore/FEFunction1D.h>\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEActiveFiberStressUC : public FEUncoupledMaterial\n{\npublic:\n\tclass Data : public FEMaterialPointData\n\t{\n\tpublic:\n\t\tdouble\tm_lamp;\n\n\tpublic:\n\t\tvoid Update()\n\t\t{\n\t\t\t// TODO:\n\t\t}\n\n\t\tvoid Serialize(DumpStream& ar) override;\n\t};\n\npublic:\n\tFEActiveFiberStressUC(FEModel* fem);\n\n\tmat3ds DevStress(FEMaterialPoint& mp) override;\n\n\ttens4ds DevTangent(FEMaterialPoint& mp) override;\n\nprivate:\n\tdouble\tm_smax;\t\t//!< peak stress\n\tdouble\tm_ac;\t\t//!< activation level\n\tFEFunction1D*\tm_stl;\t//!< stress from tension-length \n\tFEFunction1D*\tm_stv;\t//!< stress from tension-velocity\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEArrudaBoyce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEArrudaBoyce.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEArrudaBoyce, FEUncoupledMaterial)\n\tADD_PARAMETER(m_mu, FE_RANGE_GREATER(0.0), \"mu\")->setUnits(UNIT_PRESSURE)->setLongName(\"initial modulus\");\n\tADD_PARAMETER(m_N , FE_RANGE_GREATER(0.0), \"N\" )->setLongName(\"links\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEArrudaBoyce::FEArrudaBoyce(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_N = 1;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEArrudaBoyce::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tconst double a[] = {0.5, 0.1, 11.0/350.0, 19.0/1750.0, 519.0/134750.0};\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\n\t// left Cauchy-Green tensor and its square\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// Invariants of B_tilde\n\tdouble I1 = B.tr();\n\n\t// strain energy derivative\n\tdouble mu = m_mu(mp);\n\tdouble f = I1/m_N;\n\tdouble W1 = mu*(a[0] + (2.0*a[1] + (3*a[2] + (4*a[3] + 5*a[4]*f)*f)*f)*f);\n\n\t// T = FdW/dCFt\n\tmat3ds T = B*W1;\n\n\t// deviatoric Cauchy stress is 2/J*dev(T)\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEArrudaBoyce::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tconst double a[] = {0.5, 0.1, 11.0/350.0, 19.0/1750.0, 519.0/134750.0};\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// W1 = dW/dI1\n\t// W11 = d2W/dI1^2\n\tdouble mu = m_mu(mp);\n\n\tconst double f = I1/m_N;\n\tdouble W1  = mu*(a[0] + (2*a[1] + (3*a[2] + (4*a[3] + 5*a[4]*f)*f)*f)*f);\n\tdouble W11 = 2.0*mu*(a[1] + (3*a[2] + (6*a[3] + 10*a[4]*f)*f)*f)/m_N;\n\t// ---\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = W11*I1*I1;\n\n\t// deviatoric cauchy-stress\n\tmat3ds T = B * W1;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// Identity tensor\n\tmat3ds I(1,1,1,0,0,0);\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W11*I1);\n\n\ttens4ds cw = BxB*(W11*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEArrudaBoyce::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\tconst double a[] = {0.5, 0.1, 11.0/350.0, 19.0/1750.0, 519.0/134750.0};\n    \n\t// left Cauchy-Green tensor and its square\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// Invariants of B_tilde\n\tdouble I1 = B.tr();\n    double I1i = I1, ti = 3, Ni = 1;\n    \n\tdouble mu = m_mu(mp);\n\n    double sed = a[0]*(I1-3);\n    for (int i=1; i<5; ++i) {\n        Ni *= m_N;\n        ti *= 3;\n        I1i *= I1;\n        sed += a[i]*(I1i - ti)/Ni;\n    }\n    sed *= mu;\n    \n    return sed;\n}"
  },
  {
    "path": "FEBioMech/FEArrudaBoyce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Arruda-Boyce material\n\nclass FEArrudaBoyce : public FEUncoupledMaterial\n{\npublic:\n\tFEArrudaBoyce(FEModel* pfem);\n\npublic:\n\tFEParamDouble\tm_mu;\t//!< shear modulus\n\tdouble\tm_N;\t//!< Nr of links in chain\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tvirtual double DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEAxialBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEAxialBodyForce.h\"\n#include \"FEElasticMaterial.h\"\n\nBEGIN_FECORE_CLASS(FEAxialBodyForce, FEBodyForce);\n\tADD_PARAMETER(s, \"scale\");\n\tADD_PARAMETER(p, \"p\");\n\tADD_PARAMETER(c, \"c\");\n\tADD_PARAMETER(n, \"axis\");\nEND_FECORE_CLASS();\n\nFEAxialBodyForce::FEAxialBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n\tp = 0;\n\tc = vec3d(0, 0, 0);\n\tn = vec3d(0, 0, 1);\n\ts = 1;\n}\n\nvec3d FEAxialBodyForce::force(FEMaterialPoint& mp)\n{\n\tvec3d x = mp.m_rt;\n\tvec3d q = x - c;\n\tvec3d r = q - n*((q*n)/(n*n));\n\n\tif (p == 0)\n\t{\n\t\tvec3d f = r * s;\n\t\treturn f;\n\t}\n\telse\n\t{\n\t\tdouble R = r.norm();\n\t\tdouble Rp = pow(R, p);\n\t\tvec3d f = r *(s*Rp);\n\t\treturn f;\n\t}\n}\n\nmat3d FEAxialBodyForce::stiffness(FEMaterialPoint& mp)\n{ \n\tmat3d K; K.zero();\n\tif (p == 0)\n\t{\n\t\tK[0][0] = -s;\n\t\tK[1][1] = -s;\n\t\tK[2][2] = -s;\n\t}\n\telse\n\t{\n\t\tvec3d x = mp.m_rt;\n\t\tvec3d q = x - c;\n\t\tvec3d r = q - n * ((q * n) / (n * n));\n\n\t\tdouble R = x.norm();\n\t\tdouble Rp = pow(R, p);\n\t\tdouble Rpm2 = pow(R, p-2 );\n\n\t\tK[0][0] = -s*(p * Rpm2 * r.x*r.x + Rp);\n\t\tK[0][1] = -s*(p * Rpm2 * r.x*r.y);\n\t\tK[0][2] = -s*(p * Rpm2 * r.x*r.z);\n\n\t\tK[1][0] = -s*(p * Rpm2 * r.y*r.x);\n\t\tK[1][1] = -s*(p * Rpm2 * r.y*r.y + Rp);\n\t\tK[1][2] = -s*(p * Rpm2 * r.y*r.z);\n\n\t\tK[2][0] = -s*(p * Rpm2 * r.z*r.x);\n\t\tK[2][1] = -s*(p * Rpm2 * r.z*r.y);\n\t\tK[2][2] = -s*(p * Rpm2 * r.z*r.z + Rp);\n\t}\n\treturn K; \n}\n\ndouble FEAxialBodyForce::divforce(FEMaterialPoint& mp)\n{\n\t// TODO: implement this\n\tassert(false);\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioMech/FEAxialBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBodyForce.h\"\n\n//! This class defines a radial body force.\n//! The body force depends on the current nodal coordinates\nclass FEAxialBodyForce : public FEBodyForce\n{\npublic:\n\tFEAxialBodyForce(FEModel* pfem);\n\n\tvec3d force(FEMaterialPoint& mp) override;\n\n\tdouble divforce(FEMaterialPoint& mp) override;\n\n\tmat3d stiffness(FEMaterialPoint& mp) override;\n\npublic:\n\tvec3d\tc; //!< force center\n\tvec3d\tn; //!< axis \n\tdouble\tp; //!< power\n\tdouble\ts; //!< scale factor\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEAzimuthConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEAzimuthConstraint.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FENode.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEAzimuthConstraint, FENodeSetConstraint)\n\tADD_PARAMETER(m_laugon, \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_tol, \"augtol\");\n\tADD_PARAMETER(m_eps, \"penalty\");\n\tADD_PARAMETER(m_minaug, \"minaug\");\n\tADD_PARAMETER(m_maxaug, \"maxaug\");\n\tEND_FECORE_CLASS();\n\nFEAzimuthConstraint::FEAzimuthConstraint(FEModel* fem) : FENodeSetConstraint(fem), m_dofU(fem), m_nodeSet(fem)\n{\n\tm_laugon = FECore::PENALTY_METHOD;\n\tm_tol = 0.1;\n\tm_eps = 0.0;\n\n\tm_minaug = 0;\n\tm_maxaug = 0;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (fem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEAzimuthConstraint::Init()\n{\n\tif (FENLConstraint::Init() == false) return false;\n\n\t// make sure there is a node set\n\tif (m_nodeSet.Size() == 0) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEAzimuthConstraint::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\tar & m_Lm & m_Lp;\n}\n\n//-----------------------------------------------------------------------------\n//! return node set\nFENodeSet* FEAzimuthConstraint::GetNodeSet()\n{\n\treturn &m_nodeSet;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEAzimuthConstraint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tint N = m_nodeSet.Size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tvector<int> lm(2);\n\t\tFENode& n0 = *m_nodeSet.Node(i);\n\t\tlm[0] = n0.m_ID[m_dofU[0]];\n\t\tlm[1] = n0.m_ID[m_dofU[1]];\n\n\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t{\n\t\t\tlm.push_back(m_eq[2*i  ]);\n\t\t\tlm.push_back(m_eq[2*i+1]);\n\t\t}\n\n\t\tM.build_add(lm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! return number of equations to be allocated for Lagrange multipliers\nint FEAzimuthConstraint::InitEquations(int neq)\n{\n\tint N = m_nodeSet.Size();\n\tm_Lm.assign(N, vec3d(0, 0, 0));\n\tm_Lp = m_Lm;\n\n\t// make sure we want to use Lagrange Multiplier method\n\tif (m_laugon != FECore::LAGMULT_METHOD) return 0;\n\n\t// allocate two equations per node\n\tm_eq.resize(2 * N);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tm_eq[2*i  ] = neq++;\n\t\tm_eq[2*i+1] = neq++;\n\t}\n\treturn 2*N;\n}\n\n//! Calculate the constraint force\nvoid FEAzimuthConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint N = m_nodeSet.Size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENode& node = *m_nodeSet.Node(i);\n\n\t\tvec3d e0 = node.m_r0; e0.z = 0.0; e0.unit();\n\t\tvec3d et = node.m_rt; et.z = 0.0; double l = et.unit();\n\n\t\t// projection mat3ds\n\t\tmat3dd I(1.0);\n\t\tmat3ds P = (I - dyad(et)) / l;\n\n\t\t// evaluate constraint \n\t\tvec3d c = et - e0;\n\n\t\t// calculate force\n\t\tvector<double> F;\n\t\tvector<int> lm;\n\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t{\n\t\t\t// Lagrange multiplier formulation\n\t\t\tvec3d Pl = P * m_Lm[i];\n\t\t\tF.resize(4);\n\t\t\tF[0] = -Pl.x;\n\t\t\tF[1] = -Pl.y;\n\t\t\tF[2] = -c.x;\n\t\t\tF[3] = -c.y;\n\n\t\t\tlm.resize(4);\n\t\t\tlm[0] = node.m_ID[m_dofU[0]];\n\t\t\tlm[1] = node.m_ID[m_dofU[1]];\n\t\t\tlm[2] = m_eq[2*i  ];\n\t\t\tlm[3] = m_eq[2*i+1];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Augmented Lagrangian formulation\n\t\t\tvec3d lam = m_Lm[i] + c * m_eps;\n\t\t\tvec3d Pl = P * lam;\n\n\t\t\tF.resize(2);\n\t\t\tF[0] = -Pl.x;\n\t\t\tF[1] = -Pl.y;\n\n\t\t\tlm.resize(2);\n\t\t\tlm[0] = node.m_ID[m_dofU[0]];\n\t\t\tlm[1] = node.m_ID[m_dofU[1]];\n\t\t}\n\n\t\tR.Assemble(lm, F);\n\t}\n}\n\n//! calculate the constraint stiffness\nvoid FEAzimuthConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint N = m_nodeSet.Size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENode* node = m_nodeSet.Node(i); assert(node);\n\n\t\tvec3d e0 = node->m_r0; e0.z = 0.0; e0.unit();\n\t\tvec3d et = node->m_rt; et.z = 0.0; double l = et.unit();\n\n\t\t// projection mat3ds\n\t\tmat3dd I(1.0);\n\t\tmat3ds P = (I - dyad(et)) / l;\n\n\t\t// evaluate constraint \n\t\tvec3d c = et - e0;\n\n\t\tmat3ds Ploe = dyads(P*m_Lm[i], et);\n\t\tmat3ds Q = (Ploe + P * (et*m_Lm[i])) / (-l);\n\n\t\t// stiffness\n\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t{\n\t\t\tFEElementMatrix ke(4, 4); ke.zero();\n\t\t\tke[0][0] = Q(0, 0); ke[0][1] = Q(0, 1);\n\t\t\tke[1][0] = Q(1, 0); ke[1][1] = Q(1, 1);\n\n\t\t\tke[0][2] = P(0, 0); ke[0][3] = P(0, 1);\n\t\t\tke[1][2] = P(1, 0); ke[1][3] = P(1, 1);\n\n\t\t\tke[2][0] = P(0, 0); ke[3][0] = P(0, 1);\n\t\t\tke[2][1] = P(1, 0); ke[3][1] = P(1, 1);\n\n\t\t\tvector<int> lm(4);\n\t\t\tlm[0] = node->m_ID[m_dofU[0]];\n\t\t\tlm[1] = node->m_ID[m_dofU[1]];\n\t\t\tlm[2] = m_eq[2*i  ];\n\t\t\tlm[3] = m_eq[2*i+1];\n\t\t\tke.SetIndices(lm);\n\n\t\t\tvector<int> en(1);\n\t\t\ten[0] = m_nodeSet[i];\n\t\t\tke.SetNodes(en);\n\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3d lam = m_Lm[i] + c * m_eps;\n\n\t\t\tmat3ds K = Q + (P.sqr())*m_eps;\n\n\t\t\tFEElementMatrix ke(2, 2); ke.zero();\n\t\t\tke[0][0] = K(0, 0); ke[0][1] = K(0, 1);\n\t\t\tke[1][0] = K(1, 0); ke[1][1] = K(1, 1);\n\n\t\t\tvector<int> lm(2);\n\t\t\tlm[0] = node->m_ID[m_dofU[0]];\n\t\t\tlm[1] = node->m_ID[m_dofU[1]];\n\n\t\t\tvector<int> en(1);\n\t\t\ten[0] = m_nodeSet[i];\n\t\t\tke.SetIndices(lm);\n\t\t\tke.SetNodes(en);\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n}\n\nvoid FEAzimuthConstraint::PrepStep()\n{\n\tm_Lp = m_Lm;\n}\n\nvoid FEAzimuthConstraint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tint N = m_nodeSet.Size();\n\t\tfor (int i = 0; i < N; ++i)\n\t\t{\n\t\t\tm_Lm[i].x = m_Lp[i].x + Ui[m_eq[2*i  ]] + ui[m_eq[2*i  ]];\n\t\t\tm_Lm[i].y = m_Lp[i].y + Ui[m_eq[2*i+1]] + ui[m_eq[2*i+1]];\n\t\t}\n\t}\n}\n\nvoid FEAzimuthConstraint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tint N = m_nodeSet.Size();\n\t\tfor (int i = 0; i < N; ++i)\n\t\t{\n\t\t\tUi[m_eq[2*i  ]] += ui[m_eq[2*i  ]];\n\t\t\tUi[m_eq[2*i+1]] += ui[m_eq[2*i+1]];\n\t\t}\n\t}\n}\n\n\n//! augmentations\nbool FEAzimuthConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\tbool bconv = true;\n\tint N = m_nodeSet.Size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENode& node = *m_nodeSet.Node(i);\n\t\tint nodeId = node.GetID();\n\n\t\tvec3d e0 = node.m_r0; e0.z = 0.0; e0.unit();\n\t\tvec3d et = node.m_rt; et.z = 0.0; double l = et.unit();\n\n\t\t// projection mat3ds\n\t\tmat3dd I(1.0);\n\t\tmat3ds P = (I - dyad(et)) / l;\n\n\t\t// Lagrange multiplier\n\t\tvec3d c = et - e0;\n\t\tvec3d lam1 = m_Lm[i] + c * m_eps;\n\n\t\tdouble g = c.norm();\n\t\tdouble L0 = m_Lm[i].norm();\n\t\tdouble L1 = lam1.norm();\n\n\t\tif (m_laugon == FECore::PENALTY_METHOD)\n\t\t{\n\t\t\t// penalty-formulation\n\t\t\tfeLog(\"\\t%d: %lg, %lg\\n\", nodeId, g, L1);\n\t\t}\n\t\telse if (m_laugon == FECore::LAGMULT_METHOD)\n\t\t{\n\t\t\t// Lagrange multiplier\n\t\t\tfeLog(\"\\t%d: %lg, %lg\\n\", nodeId, g, L0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// aug-lag constraint\n\t\t\tbool bconvi = true;\n\t\t\tif (m_Lm[i].norm() == 0.0)\n\t\t\t{\n\t\t\t\tfeLog(\"\\t%d: %lg, %lg\\n\", nodeId, g, L1);\n\t\t\t\tbconvi = (L1 == 0.0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble dl = fabs((L1 - L0) / L1);\n\t\t\t\tfeLog(\"\\t%d: %lg, %lg (%lg)\\n\", nodeId, g, L1, dl);\n\t\t\t\tif (dl >= m_tol)\n\t\t\t\t{\n\t\t\t\t\tbconvi = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check augmentation range\n\t\t\tnaug++;\n\t\t\tif ((m_minaug > 0) && (naug < m_minaug)) bconvi = false;\n\t\t\tif ((m_maxaug > 0) && (naug >= m_maxaug)) bconvi = true;\n\n\t\t\t// update LM if not converged\n\t\t\tif (bconvi == false)\n\t\t\t{\n\t\t\t\tm_Lm[i] = lam1;\n\t\t\t}\n\n\t\t\tbconv = bconv & bconvi;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n"
  },
  {
    "path": "FEBioMech/FEAzimuthConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENodeSetConstraint.h>\n#include <FECore/FENodeSet.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FEAzimuthConstraint : public FENodeSetConstraint\n{\npublic:\n\tFEAzimuthConstraint(FEModel* fem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! Calculate the constraint force\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate the constraint stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t//! return number of equations to be allocated for Lagrange multipliers\n\tint InitEquations(int neq) override;\n\n\t//! Update Lagrange multipliers\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return node set\n\tFENodeSet* GetNodeSet() override;\n\nprivate:\n\tint\t\tm_laugon;\t\t//!< Augmentation flag\n\tdouble\tm_tol;\t\t\t//!< augmentation tolerance\n\tdouble\tm_eps;\t\t\t//!< penalty factor\n\tint\t\tm_minaug;\t\t//!< minimum nr of augmentations\n\tint\t\tm_maxaug;\t\t//!< max nr of augmentations\n\nprivate:\n\tvector<vec3d>\tm_Lm, m_Lp;\t//!< Lagrange multipliers\n\nprivate:\n\tvector<int>\t\tm_eq;\t\t//!< equation nr of LM\n\tFEDofList\tm_dofU;\t\t\t//!< dof list\n\tFENodeSet\tm_nodeSet;\t\t//!< the node set to apply this constraint to\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEBCPrescribedDeformation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBCPrescribedDeformation.h\"\n#include <FECore/FEMesh.h>\n#include \"FEBioMech.h\"\n\nBEGIN_FECORE_CLASS(FEBCPrescribedDeformation, FEPrescribedNodeSet)\n\tADD_PARAMETER(m_scale, \"scale\");\n\tADD_PARAMETER(m_F    , \"F\");\nEND_FECORE_CLASS();\n\nFEBCPrescribedDeformation::FEBCPrescribedDeformation(FEModel* pfem) : FEPrescribedNodeSet(pfem)\n{\n\tm_scale = 1.0;\n\tm_F.unit();\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tFEDofList dof(pfem);\n\t\tdof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tSetDOFList(dof);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFEBCPrescribedDeformation* ps = dynamic_cast<FEBCPrescribedDeformation*>(pbc); assert(ps);\n\tm_scale = ps->m_scale;\n\tm_F = ps->m_F;\n\n\t// copy the node set\n\tconst FENodeSet* src = ps->GetNodeSet();\n\tFENodeList nodeList = src->GetNodeList();\n\t\n\tvector<int> nodes;\n\tfor (int i = 0; i < nodeList.Size(); ++i) nodes.push_back(nodeList[i]);\n\n\tFENodeSet* ns = new FENodeSet(GetFEModel());\n\tns->Add(nodes);\n\n\tSetNodeSet(ns);\n\t\n\t// copy parameter list\n\tCopyParameterListState(ps->GetParameterList());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation::SetDeformationGradient(const mat3d& F)\n{\n\tm_F = F;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n\tvec3d X = GetNodeSet()->Node(nodelid)->m_r0;\n\tmat3ds XX = dyad(X);\n\tvec3d x = m_F*X;\n\tvec3d u = (x - X)*m_scale;\n\t\n\tval[0] = u.x;\n\tval[1] = u.y;\n\tval[2] = u.z;\n}\n"
  },
  {
    "path": "FEBioMech/FEBCPrescribedDeformation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FEBCPrescribedDeformation : public FEPrescribedNodeSet\n{\npublic:\n\tFEBCPrescribedDeformation(FEModel* pfem);\n\n\tvoid SetDeformationGradient(const mat3d& F);\n\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\nprotected:\n\tvoid GetNodalValues(int nodelid, std::vector<double>& val) override;\n\nprotected:\n\tdouble\tm_scale;\n\tmat3d\tm_F;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEBCRigidDeformation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBCRigidDeformation.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FENodeSet.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FEBCRigidDeformation, FEPrescribedNodeSet)\n\tADD_PARAMETER(m_rt, \"pos\");\n\tADD_PARAMETER(m_qt, \"rot\");\nEND_FECORE_CLASS();\n\nFEBCRigidDeformation::FEBCRigidDeformation(FEModel* fem) : FEPrescribedNodeSet(fem)\n{\n\tm_rt = vec3d(0, 0, 0);\n\tm_qt = vec3d(0, 0, 0);\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (fem)\n\t{\n\t\tFEDofList dofs(fem);\n\t\tdofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tSetDOFList(dofs);\n\t}\n}\n\nvoid FEBCRigidDeformation::Activate()\n{\n\tm_r0 = m_rt;\n\tFEPrescribedNodeSet::Activate();\n}\n\nvoid FEBCRigidDeformation::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFEBCRigidDeformation* ps = dynamic_cast<FEBCRigidDeformation*>(pbc); assert(ps);\n\tm_rt = ps->m_rt;\n\tm_qt = ps->m_qt;\n\tCopyParameterListState(ps->GetParameterList());\n}\n\nvoid FEBCRigidDeformation::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n\tvec3d X = GetNodeSet()->Node(nodelid)->m_r0;\n\n\tquatd Q(m_qt);\n\n\tvec3d x = Q*(X - m_r0) + m_rt;\n\tvec3d u = x - X;\n\n\tval[0] = u.x;\n\tval[1] = u.y;\n\tval[2] = u.z;\n}\n"
  },
  {
    "path": "FEBioMech/FEBCRigidDeformation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n\nclass FEBCRigidDeformation : public FEPrescribedNodeSet\n{\npublic:\n\tFEBCRigidDeformation(FEModel* fem);\n\n\tvoid Activate() override;\n\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\nprotected:\n\tvoid GetNodalValues(int nodelid, std::vector<double>& val) override;\n\nprivate:\n\tvec3d\tm_r0;\n\n\tvec3d\tm_rt;\t// rigid body position\n\tvec3d\tm_qt;\t// rigid body rotation vector\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEBearingLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBearingLoad.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEMesh.h>\n#include <FECore/QuadricFit.h>\n#include <FECore/log.h>\n#include <string>\n\n//-----------------------------------------------------------------------------\n// Parameter block for bearing loads\nBEGIN_FECORE_CLASS(FEBearingLoad, FESurfaceLoad)\n    ADD_PARAMETER(m_scale   , \"scale\");\n    ADD_PARAMETER(m_force   , \"force\");\n    ADD_PARAMETER(m_bsymm   , \"symmetric_stiffness\");\n    ADD_PARAMETER(m_blinear , \"linear\");\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\n    ADD_PARAMETER(m_profile , \"profile\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEBearingLoad::FEBearingLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_scale = 1.0;\n    m_force = vec3d(0,0,0);\n    m_bsymm = true;\n    m_bshellb = false;\n    m_blinear = false;\n    m_profile = P_SINE;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBearingLoad::Init()\n{\n    // get the degrees of freedom\n    m_dof.Clear();\n    if (m_bshellb == false)\n    {\n        m_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n    }\n    else\n    {\n        m_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n    }\n    if (m_dof.IsEmpty()) return false;\n    \n    if (FESurfaceLoad::Init() == false) return false;\n    \n    std::vector<vec3d> pc;\n    \n    FESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n    FENodeList nl = surf.GetNodeList();\n    FEMesh* mesh = surf.GetMesh();\n    \n    // figure out the pressure load distribution on the bearing surface.\n    // 1. Fit a quadric to the bearing surface\n    // 2. Confirm that the quadric is a cylinder\n    // 3. Set the cylinder axis ez and create local coordinate system\n    //    based on orientation of bearing force (er)\n    // 4. For each face of bearing surface, get cosine of angle theta of its normal relative to er (cq)\n    // 5. Use cq to evaluate the pressure profile and projected area, scale to match prescribed force\n    \n    // collect all the surface nodal positions into pc\n    if (m_bshellb == false) {\n        for (int i=0; i<nl.Size(); ++i)\n            pc.push_back(mesh->Node(nl[i]).m_r0);\n    }\n    else {\n        for (int i=0; i<nl.Size(); ++i)\n            pc.push_back(mesh->Node(nl[i]).s0());\n    }\n    \n    // find the best fit quadric\n    QuadricFit* fit = new QuadricFit();\n    fit->Fit(pc);\n    \n    QuadricFit::Q_TYPE qtype = fit->GetType();\n\n    bool cylindrical = qtype & (QuadricFit::Q_CIRCULAR_CYLINDER |\n                                QuadricFit::Q_ELLIPTIC_CYLINDER |\n                                QuadricFit::Q_PARABOLIC_CYLINDER|\n                                QuadricFit::Q_HYPERBOLIC_CYLINDER);\n    \n    if (cylindrical == false) {\n        std::string quadric = fit->GetStringType(qtype);\n        feLogError(\"Bearing load surface is %s, not cylindrical!\\n\",quadric.c_str());\n        return false;\n    }\n    \n    // extract cylinder parameters\n//    vec3d c = fit->m_rc;                // cylinder origin\n//    double R = sqrt(1./fit->m_c2.x);    // radius\n    vec3d ez = fit->m_ax[2];            // cylinder axis\n    vec3d et = (ez ^ m_force).normalized();\n    m_er = (et ^ ez).normalized();  // radial force direction\n    \n    // evaluate dot product of bearing force and axis\n    double dp = m_force*ez;\n    double eps = 1e-3;\n    if (fabs(dp) > m_force.norm()*eps) {\n        feLogWarning(\"Axial component of bearing force (%g) is ignored!\\n\",dp);\n    }\n    \n    // create surface map for prescribed pressure\n    m_pc = new FESurfaceMap(FE_DOUBLE);\n    m_pc->Create(surf.GetFacetSet(), 0.0, FMT_MULT);\n    int m = m_pc->MaxNodes();\n\n    // evaluate projected area, which is needed to evaluate peak pressure\n    double A = 0;       // effective projected area\n    for (int i=0; i<surf.Elements(); ++i) {\n        // get the surface element\n        FESurfaceElement& el = surf.Element(i);\n        // get (and negate inward) normal at face centroid\n        vec3d n = -surf.SurfaceNormal(el, el.cr(), el.cs());\n        // get face area\n        double area = surf.FaceArea(el);\n        // cosine of angle between force and surface normal\n        double cq = n*m_er;\n        // if cq is positive\n        if (cq > 0) {\n            // face projected area\n            double parea = area*cq;\n            // add to effective projected area\n            switch (m_profile) {\n                case P_SINE: A += cq*parea; break;\n                case P_PARA: A += pow(cq,2)*parea; break;\n                default: break;\n            }\n        }\n    }\n    // evaluate peak pressure p0 for a unit force magnitude\n    double p0 = 1.0/A;\n    \n    // prescribe bearing pressure on each face\n    for (int i=0; i<surf.Elements(); ++i) {\n        // get the surface element\n        FESurfaceElement& el = surf.Element(i);\n        // get (and negate inward) normal at face centroid\n        vec3d n = -surf.SurfaceNormal(el, el.cr(), el.cs());\n        // cosine of angle between force and surface normal\n        double cq = n*m_er;\n        // if cq is positive\n        if (cq > 0) {\n            double p = p0;\n            switch (m_profile) {\n                case P_SINE: p *= cq; break;\n                case P_PARA: p *= pow(cq,2); break;\n                default: p = 0; break;\n            }\n            for (int j = 0; j < m; ++j)\n                m_pc->setValue(el.m_lid, j, p);\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! update projected area of bearing surface when nonlinear flag is set\nvoid FEBearingLoad::Update()\n{\n    if (m_blinear == false) {\n        FESurface& surf = GetSurface();\n        surf.SetShellBottom(m_bshellb);\n        int m = m_pc->MaxNodes();\n\n        // evaluate projected area, which is needed to evaluate peak pressure\n        double A = 0;       // effective projected area\n        for (int i=0; i<surf.Elements(); ++i) {\n            // get the surface element\n            FESurfaceElement& el = surf.Element(i);\n            // get (and negate inward) normal at face centroid\n            vec3d n = -surf.SurfaceNormal(el, el.cr(), el.cs());\n            // get face area\n            double area = surf.FaceArea(el);\n            // cosine of angle between force and surface normal\n            double cq = n*m_er;\n            // if cq is positive\n            if (cq > 0) {\n                // face projected area\n                double parea = area*cq;\n                // add to effective projected area\n                switch (m_profile) {\n                    case P_SINE: A += cq*parea; break;\n                    case P_PARA: A += pow(cq,2)*parea; break;\n                    default: break;\n                }\n            }\n        }\n        // evaluate peak pressure p0 for a unit force magnitude\n        double p0 = 1.0/A;\n        \n        // prescribe bearing pressure on each face\n        for (int i=0; i<surf.Elements(); ++i) {\n            // get the surface element\n            FESurfaceElement& el = surf.Element(i);\n            // get (and negate inward) normal at face centroid\n            vec3d n = -surf.SurfaceNormal(el, el.cr(), el.cs());\n            // cosine of angle between force and surface normal\n            double cq = n*m_er;\n            // if cq is positive\n            if (cq > 0) {\n                double p = p0;\n                switch (m_profile) {\n                    case P_SINE: p *= cq; break;\n                    case P_PARA: p *= pow(cq,2); break;\n                    default: p = 0; break;\n                }\n                for (int j = 0; j < m; ++j)\n                    m_pc->setValue(el.m_lid, j, p);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBearingLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n    if (ar.IsShallow() == false)\n    {\n        ar & m_er;\n\n        if (ar.IsSaving())\n            m_pc->Serialize(ar);\n        else\n        {\n            m_pc = new FESurfaceMap(FE_DOUBLE);\n            m_pc->Serialize(ar);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bearing pressure\ndouble FEBearingLoad::ScalarLoad(FESurfaceMaterialPoint& mp)\n{\n    // evaluate pressure at this material point\n    double P = m_pc->value(mp)*m_scale*m_force.norm();\n    return P;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBearingLoad::LoadVector(FEGlobalVector& R)\n{\n    FESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n    \n    // evaluate the integral\n    surf.LoadVector(R, m_dof, m_blinear, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n        \n        // evaluate pressure at this material point\n        double P = -ScalarLoad(pt);\n        if (m_bshellb) P = -P;\n\n        double J = (pt.dxr ^ pt.dxs).norm();\n        \n        // force vector\n        vec3d N = (pt.dxr ^ pt.dxs); N.unit();\n        vec3d t = N*P;\n        \n        double H_u = dof_a.shape;\n        \n        val[0] = H_u*t.x*J;\n        val[1] = H_u*t.y*J;\n        val[2] = H_u*t.z*J;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBearingLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n    // Don't calculate stiffness for a linear load\n    if (m_blinear) return;\n    \n    FESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n    \n    // evaluate the integral\n    surf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& kab) {\n        \n        // evaluate pressure at this material point\n        double P = -ScalarLoad(mp);\n        if (m_bshellb) P = -P;\n\n        double H_i  = dof_a.shape;\n        double Gr_i = dof_a.shape_deriv_r;\n        double Gs_i = dof_a.shape_deriv_s;\n        \n        double H_j  = dof_b.shape;\n        double Gr_j = dof_b.shape_deriv_r;\n        double Gs_j = dof_b.shape_deriv_s;\n        \n        vec3d vab(0,0,0);\n        if (m_bsymm)\n            vab = (mp.dxr*(H_j * Gs_i - H_i * Gs_j) - mp.dxs*(H_j * Gr_i - H_i * Gr_j)) * 0.5*P;\n        else\n            vab = (mp.dxs*Gr_j - mp.dxr*Gs_j)*(P*H_i);\n        \n        mat3da K(vab);\n        kab.set(0, 0, K);\n    });\n}\n"
  },
  {
    "path": "FEBioMech/FEBearingLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include <FECore/FESurfaceMap.h>\n\n//-----------------------------------------------------------------------------\n//! The bearing load is a surface domain that sustains a parabolic or sinusoidal pressure boundary\n//! condition that simulates the contact pressure in a bearing.\n//!\nclass FEBearingLoad : public FESurfaceLoad\n{\npublic:\n    enum P_PROFILE {\n        P_SINE,         // sinusoidal pressure distribution\n        P_PARA          // parabolic pressure distribution\n    };\n\npublic:\n    //! constructor\n    FEBearingLoad(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! update\n    void Update() override;\n    \n    //! evaluate bearing pressure\n    double ScalarLoad(FESurfaceMaterialPoint& mp) override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    //! calculate residual\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! calculate stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \nprotected:\n    double          m_scale;        //!< scale factor for bearing load\n    vec3d           m_force;        //!< bearing load\n    bool            m_bsymm;        //!< use symmetric formulation\n    bool            m_blinear;      //!< is the load linear (i.e. it will be calculated in the reference frame and assummed deformation independent)\n    bool            m_bshellb;      //!< flag for prescribing pressure on shell bottom\n    int             m_profile;      //!< profile of pressure distribution\n\nprivate:\n    vec3d           m_er;           //!< unit vector along radial direction of bearing surface\n    FESurfaceMap*   m_pc;            //!< pressure value\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEBioMech.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioMech.h\"\n#include <assert.h>\n\n//-----------------------------------------------------------------------------\nconst char* FEBioMech::GetVariableName(FEBioMech::MECH_VARIABLE var)\n{\n\tswitch (var)\n\t{\n\tcase DISPLACEMENT             : return \"displacement\"        ; break;\n\tcase ROTATION                 : return \"rotation\"            ; break;\n\tcase RIGID_ROTATION           : return \"rigid rotation\"      ; break;\n\tcase SHELL_DISPLACEMENT       : return \"shell displacement\"  ; break;\n\tcase VELOCITY                 : return \"velocity\"            ; break;\n\tcase SHELL_VELOCITY           : return \"shell velocity\"      ; break;\n\tcase SHELL_ACCELERATION       : return \"shell acceleration\"  ; break;\n\tcase BEAM_ANGULAR_VELOCITY    : return \"vorticity\"           ; break;\n\tcase BEAM_ANGULAR_ACCELERATION: return \"angular acceleration\"; break;\n\t}\n\n\tassert(false);\n\treturn nullptr;\n}\n"
  },
  {
    "path": "FEBioMech/FEBioMech.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiomech_api.h\"\n//-----------------------------------------------------------------------------\n//! The FEBioMech module\n\n//! This module defines classes for dealing with large deformation structural\n//! mechanics problems. \n\nnamespace FEBioMech\n{\n\tenum MECH_VARIABLE {\n\t\tDISPLACEMENT,\n\t\tROTATION,\n\t\tRIGID_ROTATION,\n\t\tSHELL_DISPLACEMENT,\n\t\tVELOCITY,\n\t\tSHELL_VELOCITY,\n\t\tSHELL_ACCELERATION,\n\t\tBEAM_ANGULAR_VELOCITY,\n\t\tBEAM_ANGULAR_ACCELERATION,\n\t};\n\n\tFEBIOMECH_API const char* GetVariableName(MECH_VARIABLE var);\n}\n"
  },
  {
    "path": "FEBioMech/FEBioMechData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMechData.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FEReactiveMaterialPoint.h\"\n#include \"FEDamageMaterialPoint.h\"\n#include \"FEReactivePlasticityMaterialPoint.h\"\n#include \"FEReactivePlasticDamageMaterialPoint.h\"\n#include \"FEElasticMixture.h\"\n#include \"FEElasticMultigeneration.h\"\n#include \"FERigidMaterial.h\"\n#include \"FESolidSolver.h\"\n#include \"FESolidSolver2.h\"\n#include \"FEExplicitSolidSolver.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FERigidConnector.h\"\n#include \"FEVolumeConstraint.h\"\n#include \"FEContactSurface.h\"\n#include \"FEDiscreteElasticMaterial.h\"\n#include \"FESlidingInterface.h\"\n#include \"FEPreStrainElastic.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\ndouble FENodeXPos::value(const FENode& node)\n{\n\treturn node.m_rt.x; \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeYPos::value(const FENode& node)\n{\n\treturn node.m_rt.y; \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeZPos::value(const FENode& node)\n{\n\treturn node.m_rt.z; \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeXDisp::value(const FENode& node)\n{\n\tconst int dof_X = GetFEModel()->GetDOFIndex(\"x\");\n\treturn node.get(dof_X); \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeYDisp::value(const FENode& node)\n{\n\tconst int dof_Y = GetFEModel()->GetDOFIndex(\"y\");\n\treturn node.get(dof_Y); \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeZDisp::value(const FENode& node)\n{\n\tconst int dof_Z = GetFEModel()->GetDOFIndex(\"z\");\n\treturn node.get(dof_Z); \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeXVel::value(const FENode& node)\n{\n\tconst int dof_VX = GetFEModel()->GetDOFIndex(\"vx\");\n\treturn node.get(dof_VX);\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeYVel::value(const FENode& node)\n{\n\tconst int dof_VY = GetFEModel()->GetDOFIndex(\"vy\");\n\treturn node.get(dof_VY); \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeZVel::value(const FENode& node)\n{\n\tconst int dof_VZ = GetFEModel()->GetDOFIndex(\"vz\");\n\treturn node.get(dof_VZ);\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeXAcc::value(const FENode& node)\n{\n\treturn node.m_at.x; \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeYAcc::value(const FENode& node)\n{\n\treturn node.m_at.y; \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeZAcc::value(const FENode& node)\n{\n\treturn node.m_at.z; \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeForceX::value(const FENode& node)\n{\n\tFESolidSolver2* psolid_solver = dynamic_cast<FESolidSolver2*>(GetFEModel()->GetCurrentStep()->GetFESolver());\n\tif (psolid_solver)\n\t{\n\t\tvector<double>& Fr = psolid_solver->m_Fr;\n\t\tconst vector<int>& id = node.m_ID;\n\t\treturn (-id[0] - 2 >= 0 ? Fr[-id[0] - 2] : 0);\n\t}\n\n\t// This code is from the \"reaction forces\" plot variable\n\t// TODO: Need to check if code above produces the same answer as below. If so, \n\t//       just use code below.\n\tFEModel& fem = *GetFEModel();\n\tint dofX = fem.GetDOFIndex(\"x\");\n\tif (dofX >= 0)\n\t{\n\t\tdouble Rx = node.get_load(dofX);\n\t\treturn Rx;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeForceY::value(const FENode& node)\n{\n\tFESolidSolver2* psolid_solver = dynamic_cast<FESolidSolver2*>(GetFEModel()->GetCurrentStep()->GetFESolver());\n\tif (psolid_solver)\n\t{\n\t\tvector<double>& Fr = psolid_solver->m_Fr;\n\t\tconst vector<int>& id = node.m_ID;\n\t\treturn (-id[1] - 2 >= 0 ? Fr[-id[1]-2] : 0);\n\t}\n\n\t// This code is from the \"reaction forces\" plot variable\n\t// TODO: Need to check if code above produces the same answer as below. If so, \n\t//       just use code below.\n\tFEModel& fem = *GetFEModel();\n\tint dofY = fem.GetDOFIndex(\"y\");\n\tif (dofY >= 0)\n\t{\n\t\tdouble Ry = node.get_load(dofY);\n\t\treturn Ry;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeForceZ::value(const FENode& node)\n{\n\tFESolver* solver = GetFEModel()->GetCurrentStep()->GetFESolver();\n\tFESolidSolver2* psolid_solver = dynamic_cast<FESolidSolver2*>(solver);\n\tif (psolid_solver)\n\t{\n\t\tvector<double>& Fr = psolid_solver->m_Fr;\n\t\tconst vector<int>& id = node.m_ID;\n\t\treturn (-id[2] - 2 >= 0 ? Fr[-id[2]-2] : 0);\n\t}\n\tFEExplicitSolidSolver* explicitSolver = dynamic_cast<FEExplicitSolidSolver*>(solver);\n\tif (explicitSolver)\n\t{\n\t\tvector<double>& Fr = explicitSolver->m_Fr;\n\t\tconst vector<int>& id = node.m_ID;\n\t\treturn (-id[2] - 2 >= 0 ? Fr[-id[2] - 2] : 0);\n\t}\n\n\t// This code is from the \"reaction forces\" plot variable\n\t// TODO: Need to check if code above produces the same answer as below. If so, \n\t//       just use code below.\n\tFEModel& fem = *GetFEModel();\n\tint dofZ = fem.GetDOFIndex(\"z\");\n\tif (dofZ >= 0)\n\t{\n\t\tdouble Rz = node.get_load(dofZ);\n\t\treturn Rz;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogContactGap::value(FESurfaceElement& el)\n{\n\tFEContactSurface* ps = dynamic_cast<FEContactSurface*>(el.GetMeshPartition());\n\tif (ps == nullptr) return 0.0;\n\n\t// returned contact gap\n\tdouble g = 0.0;\n\n\t// NOTE: the sliding surface does not use material points, so we need this little hack. \n\tFESlidingSurface* ss = dynamic_cast<FESlidingSurface*>(ps);\n\tif (ss)\n\t{\n\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t{\n\t\t\tdouble gj = ss->m_data[el.m_lnode[j]].m_gap;\n\t\t\tg += gj;\n\t\t}\n\t\tg /= el.Nodes();\n\t\treturn g;\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < el.GaussPoints(); ++i)\n\t\t{\n\t\t\tFEMaterialPoint* mp = el.GetMaterialPoint(i);\n\t\t\tFEContactMaterialPoint* cp = dynamic_cast<FEContactMaterialPoint*>(mp);\n\t\t\tif (cp)\n\t\t\t{\n\t\t\t\tg += cp->m_gap;\n\t\t\t}\n\t\t}\n\t\tg /= (double)el.GaussPoints();\n\t}\n\n\treturn g;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogContactPressure::value(FESurfaceElement& el)\n{\n\tFEContactSurface* ps = dynamic_cast<FEContactSurface*>(el.GetMeshPartition());\n\tif (ps == nullptr) return 0.0;\n\n\tdouble Lm = 0.0;\n\n\t// NOTE: the sliding surface does not use material points, so we need this little hack. \n\tFESlidingSurface* ss = dynamic_cast<FESlidingSurface*>(ps);\n\tif (ss)\n\t{\n\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t{\n\t\t\tdouble Lmj = ss->m_data[el.m_lnode[j]].m_Ln;\n\t\t\tLm += Lmj;\n\t\t}\n\t\tLm /= el.Nodes();\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < el.GaussPoints(); ++i)\n\t\t{\n\t\t\tFEMaterialPoint* mp = el.GetMaterialPoint(i);\n\t\t\tFEContactMaterialPoint* cp = dynamic_cast<FEContactMaterialPoint*>(mp);\n\t\t\tif (cp)\n\t\t\t{\n\t\t\t\tLm += cp->m_Ln;\n\t\t\t}\n\t\t}\n\t\tLm /= (double)el.GaussPoints();\n\t}\n\n\treturn Lm;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogContactTractionX::value(FESurfaceElement& el)\n{\n\tFEContactSurface* ps = dynamic_cast<FEContactSurface*>(el.GetMeshPartition());\n\tif (ps == nullptr) return 0.0;\n\n\tFEContactInterface* pci = ps->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tvec3d tn;\n\t\tps->GetSurfaceTraction(el.m_lid, tn);\n\t\treturn tn.x;\n\t}\n\treturn 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogContactTractionY::value(FESurfaceElement& el)\n{\n\tFEContactSurface* ps = dynamic_cast<FEContactSurface*>(el.GetMeshPartition());\n\tif (ps == nullptr) return 0.0;\n\n\tFEContactInterface* pci = ps->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tvec3d tn;\n\t\tps->GetSurfaceTraction(el.m_lid, tn);\n\t\treturn tn.y;\n\t}\n\treturn 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogContactTractionZ::value(FESurfaceElement& el)\n{\n\tFEContactSurface* ps = dynamic_cast<FEContactSurface*>(el.GetMeshPartition());\n\tif (ps == nullptr) return 0.0;\n\n\tFEContactInterface* pci = ps->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tvec3d tn;\n\t\tps->GetSurfaceTraction(el.m_lid, tn);\n\t\treturn tn.z;\n\t}\n\treturn 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPosX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(i);\n\t\tval += pt.m_rt.x;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPosY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(i);\n\t\tval += pt.m_rt.y;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPosZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(i);\n\t\tval += pt.m_rt.z;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemJacobian::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t\tval += pt->m_J;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tval += E.xx();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tval += E.yy();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tval += E.zz();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainXY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tval += E.xy();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainYZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tval += E.yz();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainXZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tval += E.xz();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrain1::value(FEElement& el)\n{\n\tdouble l[3];\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tE.exact_eigen(l);\n\t\t\tval += l[0];\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainEffective::value(FEElement& el)\n{\n\tint nint = el.GaussPoints();\n\tmat3ds Eavg; Eavg.zero();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (ep)\n\t\t{\n\t\t\tmat3ds C = ep->LeftCauchyGreen();\n\t\t\tmat3dd I(1.0);\n\t\t\tmat3ds E = (C - I) * 0.5;\n\n\t\t\tEavg += E;\n\t\t}\n\t}\n\tEavg /= (double)nint;\n\tdouble val = Eavg.effective_norm();\n\n\treturn val;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrain2::value(FEElement& el)\n{\n\tdouble l[3];\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tE.exact_eigen(l);\n\t\t\tval += l[1];\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrain3::value(FEElement& el)\n{\n\tdouble l[3];\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds E = pt->Strain();\n\t\t\tE.exact_eigen(l);\n\t\t\tval += l[2];\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemInfStrainX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds e = pt->SmallStrain();\n\t\t\tval += e.xx();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemInfStrainY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds e = pt->SmallStrain();\n\t\t\tval += e.yy();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemInfStrainZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds e = pt->SmallStrain();\n\t\t\tval += e.zz();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemInfStrainXY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds e = pt->SmallStrain();\n\t\t\tval += e.xy();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemInfStrainYZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds e = pt->SmallStrain();\n\t\t\tval += e.yz();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemInfStrainXZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds e = pt->SmallStrain();\n\t\t\tval += e.xz();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            val += U.xx();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            val += U.yy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            val += U.zz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            val += U.xy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            val += U.yz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            val += U.xz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretch1::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            U.exact_eigen(l);\n            val += l[0];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretch2::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            U.exact_eigen(l);\n            val += l[1];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretch3::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds U = pt->RightStretch();\n            U.exact_eigen(l);\n            val += l[2];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightStretchEffective::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    mat3ds Uavg; Uavg.zero();\n    for (int n = 0; n < nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (ep)\n\t\t{\n\t\t\tmat3ds U = ep->RightStretch();\n\t\t\tUavg += U;\n\t\t}\n    }\n    Uavg /= (double)nint;\n    double val = Uavg.effective_norm();\n    \n    return val;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            val += V.xx();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            val += V.yy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            val += V.zz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            val += V.xy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            val += V.yz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            val += V.xz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretch1::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            V.exact_eigen(l);\n            val += l[0];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretch2::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            V.exact_eigen(l);\n            val += l[1];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretch3::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds V = pt->LeftStretch();\n            V.exact_eigen(l);\n            val += l[2];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftStretchEffective::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    mat3ds Vavg; Vavg.zero();\n    for (int n = 0; n < nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\n        if (ep)\n        {\n            mat3ds V = ep->LeftStretch();\n            Vavg += V;\n        }\n    }\n    Vavg /= (double)nint;\n    double val = Vavg.effective_norm();\n    \n    return val;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            val += H.xx();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            val += H.yy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            val += H.zz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            val += H.xy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            val += H.yz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            val += H.xz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHencky1::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            H.exact_eigen(l);\n            val += l[0];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHencky2::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            H.exact_eigen(l);\n            val += l[1];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHencky3::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds H = pt->RightHencky();\n            H.exact_eigen(l);\n            val += l[2];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemRightHenckyEffective::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    mat3ds Havg; Havg.zero();\n    for (int n = 0; n < nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\n        if (ep)\n        {\n            mat3ds H = ep->RightHencky();\n            Havg += H;\n        }\n    }\n    Havg /= (double)nint;\n    double val = Havg.effective_norm();\n    \n    return val;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            val += h.xx();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            val += h.yy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            val += h.zz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            val += h.xy();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            val += h.yz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            val += h.xz();\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHencky1::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            h.exact_eigen(l);\n            val += l[0];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHencky2::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            h.exact_eigen(l);\n            val += l[1];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHencky3::value(FEElement& el)\n{\n    double l[3];\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i=0; i<nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3ds h = pt->LeftHencky();\n            h.exact_eigen(l);\n            val += l[2];\n        }\n    }\n    return val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemLeftHenckyEffective::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    mat3ds havg; havg.zero();\n    for (int n = 0; n < nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\n        if (ep)\n        {\n            mat3ds h = ep->LeftHencky();\n            havg += h;\n        }\n    }\n    havg /= (double)nint;\n    double val = havg.effective_norm();\n    \n    return val;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_s.xx();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_s.yy();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_s.zz();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressXY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_s.xy();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressYZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_s.yz();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressXZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_s.xz();\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressEffective::value(FEElement& el)\n{\n\tint nint = el.GaussPoints();\n\tmat3ds savg; savg.zero();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\tif (ep)\n\t\t{\n\t\t\tsavg += ep->m_s;\n\t\t}\n\t}\n\tsavg /= (double)nint;\n\tdouble val = savg.effective_norm();\n\n\treturn val;\n}\n\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStress1::value(FEElement& el)\n{\n\tdouble l[3];\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tpt->m_s.exact_eigen(l);\n\t\t\tval += l[0];\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStress2::value(FEElement& el)\n{\n\tdouble l[3];\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tpt->m_s.exact_eigen(l);\n\t\t\tval += l[1];\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStress3::value(FEElement& el)\n{\n\tdouble l[3];\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tpt->m_s.exact_eigen(l);\n\t\t\tval += l[2];\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK2StressX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds S = pt->pull_back(pt->m_s);\n\t\t\tval += S.xx();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK2StressY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds S = pt->pull_back(pt->m_s);\n\t\t\tval += S.yy();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK2StressZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds S = pt->pull_back(pt->m_s);\n\t\t\tval += S.zz();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK2StressXY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds S = pt->pull_back(pt->m_s);\n\t\t\tval += S.xy();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK2StressYZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds S = pt->pull_back(pt->m_s);\n\t\t\tval += S.yz();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK2StressXZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds S = pt->pull_back(pt->m_s);\n\t\t\tval += S.xz();\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressXX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(0,0);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressYY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(1,1);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressZZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(2,2);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressXY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(0,1);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressYZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(1,2);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressXZ::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(0,2);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressYX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(1,0);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressZY::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(2,1);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPK1StressZX::value(FEElement& el)\n{\n    double val = 0.0;\n    int nint = el.GaussPoints();\n    for (int i = 0; i < nint; ++i)\n    {\n        FEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n        if (pt)\n        {\n            mat3d P = pt->m_J*pt->m_s*pt->m_F.transinv();\n            val += P(2,0);\n        }\n    }\n    return val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStressEigenVector::value(FEElement& el)\n{\n\tassert(m_eigenVector >= 0);\n\tassert(m_component >= 0);\n\n\t// calculate average stress\n\tmat3ds s(0.0);\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\ts += pt->m_s;\n\t\t}\n\t}\n\ts /= (double)nint;\n\n\t// get the eigen vectors\n\tvec3d e[3];\n\tdouble l[3];\n\ts.eigen(l, e);\n\n\t// extract component\n\tdouble v = 0.0;\n\tswitch (m_component)\n\t{\n\tcase 0: v = e[m_eigenVector].x; break;\n\tcase 1: v = e[m_eigenVector].y; break;\n\tcase 2: v = e[m_eigenVector].z; break;\n\t}\n\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientXX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(0,0);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientXY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(0,1);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientXZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(0,2);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientYX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(1,0);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientYY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(1,1);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientYZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(1,2);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientZX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(2,0);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientZY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(2,1);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDeformationGradientZZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEElasticMaterialPoint* pt = el.GetMaterialPoint(i)->ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tval += pt->m_F(2,2);\n\t\t}\n\t}\n\treturn val / (double) nint;\n}\n\ndouble FELogTotalDeformationGradient::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(i);\n\t\tFEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tFEPrestrainMaterialPoint* pp = mp.ExtractData<FEPrestrainMaterialPoint>();\n\t\t\tmat3d F = pt->m_F;\n\t\t\tif (pp)\n\t\t\t{\n\t\t\t\tmat3d Fp = pp->prestrain();\n\t\t\t\tF = F * Fp;\n\t\t\t}\n\n\t\t\tval += F(m_r, m_c);\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemElasticity_::value(FEElement& el, int n)\n{\n    FEElasticMaterial* pme = GetFEModel()->GetMaterial(el.GetMatID())->ExtractProperty<FEElasticMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return 0;\n\n    tens4dmm c;\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(i);\n        c = pme->SolidTangent(pt);\n\t\tval += c.d[n];\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemStrainEnergyDensity::value(FEElement& el)\n{\n\tFEElasticMaterial* pme = GetFEModel()->GetMaterial(el.GetMatID())->ExtractProperty<FEElasticMaterial>();\n\tif ((pme == 0) || pme->IsRigid()) return 0;\n    \n    double sed;\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(i);\n        sed = pme->StrainEnergyDensity(pt);\n\t\tval += sed;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemDevStrainEnergyDensity::value(FEElement& el)\n{\n\tFEElasticMaterial* pme = GetFEModel()->GetMaterial(el.GetMatID())->ExtractProperty<FEElasticMaterial>();\n\tFEUncoupledMaterial* pmu = dynamic_cast<FEUncoupledMaterial*>(pme);\n    if ((pme == 0) || pme->IsRigid() || (pmu == 0)) return 0;\n    \n    double sed;\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(i);\n        sed = pmu->DevStrainEnergyDensity(pt);\n\t\tval += sed;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFiberStretch::value(FEElement& el)\n{\n\tint matID = el.GetMatID();\n\tFEMaterial* mat = GetFEModel()->GetMaterial(matID);\n\n\tint n = el.GaussPoints();\n\tdouble l = 0.0;\n\tfor (int j=0; j<n; ++j)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\tFEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3d Q = mat->GetLocalCS(mp);\n\t\t\tvec3d ri = Q.col(0);\n\t\t\tvec3d r = pt->m_F * ri;\n\t\t\tl += r.norm();\n\t\t}\n\t}\n\tl /= (double) n;\n\treturn l;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFiberVector_::value(FEElement& el)\n{\n\tint matID = el.GetMatID();\n\tFEMaterial* mat = GetFEModel()->GetMaterial(matID);\n\n\tFEElasticMaterial* pme = mat->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return 0.0;\n\n\tFEVec3dValuator* vec = dynamic_cast<FEVec3dValuator*>(pme->GetProperty(\"fiber\"));\n\tif (vec == nullptr) return 0.0;\n\n\tint n = el.GaussPoints();\n\tdouble l = 0.0;\n\tfor (int j = 0; j<n; ++j)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\n\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<const FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3d Q = pme->GetLocalCS(mp);\n\t\t\tmat3d F = pt->m_F;\n\t\t\tvec3d a0 = vec->unitVector(mp);\n\t\t\tvec3d ar = Q * a0;\n\t\t\tvec3d a = F * ar; a.unit();\n\n\t\t\tswitch (m_comp)\n\t\t\t{\n\t\t\tcase 0: l += a.x; break;\n\t\t\tcase 1: l += a.y; break;\n\t\t\tcase 2: l += a.z; break;\n\t\t\t}\n\t\t}\n\t}\n\tl /= (double)n;\n\treturn l;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDamage_::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    double D = 0;\n\tif (m_comp == -1)\n\t{\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(j);\n\t\t\tFEReactiveMaterialPoint* ppd = pt.ExtractData<FEReactiveMaterialPoint>();\n\t\t\tFEElasticMixtureMaterialPoint* pem = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n\t\t\tFEMultigenerationMaterialPoint* pmg = pt.ExtractData<FEMultigenerationMaterialPoint>();\n\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\telse if (pem) {\n\t\t\t\tfor (int k = 0; k < pem->Components(); ++k)\n\t\t\t\t{\n\t\t\t\t\tFEReactiveMaterialPoint* ppd = pem->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pmg) {\n\t\t\t\tfor (int k = 0; k < pmg->Components(); ++k)\n\t\t\t\t{\n\t\t\t\t\tFEReactiveMaterialPoint* ppd = pmg->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\t\tFEElasticMixtureMaterialPoint* pem = pmg->GetPointData(k)->ExtractData<FEElasticMixtureMaterialPoint>();\n\t\t\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\t\t\telse if (pem)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int l = 0; l < pem->Components(); ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEReactiveMaterialPoint* ppd = pem->GetPointData(l)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\t\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tFEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n\t\t\tif (mmp && (m_comp < mmp->Components()))\n\t\t\t{\n\t\t\t\tFEReactiveMaterialPoint* dp = mmp->GetPointData(m_comp)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\tif (dp) D += dp->BrokenBonds();\n\t\t\t}\n\t\t}\n\t}\n    D /= (double) nint;\n    return D;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogIntactBonds::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    double D = 0;\n    for (int j=0; j<nint; ++j)\n    {\n        FEMaterialPoint& pt = *el.GetMaterialPoint(j);\n        FEReactiveMaterialPoint* ppd = pt.ExtractData<FEReactiveMaterialPoint>();\n        FEElasticMixtureMaterialPoint* pem = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        FEMultigenerationMaterialPoint* pmg = pt.ExtractData<FEMultigenerationMaterialPoint>();\n        if (ppd) D += (float) ppd->IntactBonds();\n        else if (pem) {\n            for (int k=0; k<pem->Components(); ++k)\n            {\n                FEReactiveMaterialPoint* ppd = pem->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n                if (ppd) D += (float) ppd->IntactBonds();\n            }\n        }\n        else if (pmg) {\n            for (int k=0; k<pmg->Components(); ++k)\n            {\n                FEReactiveMaterialPoint* ppd = pmg->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n                FEElasticMixtureMaterialPoint* pem = pmg->GetPointData(k)->ExtractData<FEElasticMixtureMaterialPoint>();\n                if (ppd) D += (float) ppd->IntactBonds();\n                else if (pem)\n                {\n                    for (int l=0; l<pem->Components(); ++l)\n                    {\n                        FEReactiveMaterialPoint* ppd = pem->GetPointData(l)->ExtractData<FEReactiveMaterialPoint>();\n                        if (ppd) D += (float) ppd->IntactBonds();\n                    }\n                }\n            }\n        }\n    }\n    D /= (double) nint;\n    return D;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogYieldedBonds::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    double D = 0;\n    for (int j=0; j<nint; ++j)\n    {\n        FEMaterialPoint& pt = *el.GetMaterialPoint(j);\n        FEReactiveMaterialPoint* ppd = pt.ExtractData<FEReactiveMaterialPoint>();\n        FEElasticMixtureMaterialPoint* pem = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        FEMultigenerationMaterialPoint* pmg = pt.ExtractData<FEMultigenerationMaterialPoint>();\n        if (ppd) D += (float) ppd->YieldedBonds();\n        else if (pem) {\n            for (int k=0; k<pem->Components(); ++k)\n            {\n                FEReactiveMaterialPoint* ppd = pem->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n                if (ppd) D += (float) ppd->YieldedBonds();\n            }\n        }\n        else if (pmg) {\n            for (int k=0; k<pmg->Components(); ++k)\n            {\n                FEReactiveMaterialPoint* ppd = pmg->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n                FEElasticMixtureMaterialPoint* pem = pmg->GetPointData(k)->ExtractData<FEElasticMixtureMaterialPoint>();\n                if (ppd) D += (float) ppd->YieldedBonds();\n                else if (pem)\n                {\n                    for (int l=0; l<pem->Components(); ++l)\n                    {\n                        FEReactiveMaterialPoint* ppd = pem->GetPointData(l)->ExtractData<FEReactiveMaterialPoint>();\n                        if (ppd) D += (float) ppd->YieldedBonds();\n                    }\n                }\n            }\n        }\n    }\n    D /= (double) nint;\n    return D;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFatigueBonds::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    double D = 0;\n    for (int j=0; j<nint; ++j)\n    {\n        FEMaterialPoint& pt = *el.GetMaterialPoint(j);\n        FEReactiveMaterialPoint* ppd = pt.ExtractData<FEReactiveMaterialPoint>();\n        FEElasticMixtureMaterialPoint* pem = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        FEMultigenerationMaterialPoint* pmg = pt.ExtractData<FEMultigenerationMaterialPoint>();\n        if (ppd) D += (float) ppd->FatigueBonds();\n        else if (pem) {\n            for (int k=0; k<pem->Components(); ++k)\n            {\n                FEReactiveMaterialPoint* ppd = pem->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n                if (ppd) D += (float) ppd->FatigueBonds();\n            }\n        }\n        else if (pmg) {\n            for (int k=0; k<pmg->Components(); ++k)\n            {\n                FEReactiveMaterialPoint* ppd = pmg->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n                FEElasticMixtureMaterialPoint* pem = pmg->GetPointData(k)->ExtractData<FEElasticMixtureMaterialPoint>();\n                if (ppd) D += (float) ppd->FatigueBonds();\n                else if (pem)\n                {\n                    for (int l=0; l<pem->Components(); ++l)\n                    {\n                        FEReactiveMaterialPoint* ppd = pem->GetPointData(l)->ExtractData<FEReactiveMaterialPoint>();\n                        if (ppd) D += (float) ppd->FatigueBonds();\n                    }\n                }\n            }\n        }\n    }\n    D /= (double) nint;\n    return D;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogOctahedralPlasticStrain::value(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    double D = 0;\n    for (int j=0; j<nint; ++j)\n    {\n        FEMaterialPoint& pt = *el.GetMaterialPoint(j);\n        FEReactivePlasticityMaterialPoint* prp = pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n        FEReactivePlasticDamageMaterialPoint* prd = pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n        FEElasticMixtureMaterialPoint* pem = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        FEMultigenerationMaterialPoint* pmg = pt.ExtractData<FEMultigenerationMaterialPoint>();\n        if (prp) D += (float) prp->m_gp[0];\n        else if (prd) D += (float) prd->m_gp[0];\n        else if (pem) {\n            for (int k=0; k<pem->Components(); ++k)\n            {\n                FEReactivePlasticityMaterialPoint* prp = pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n                FEReactivePlasticDamageMaterialPoint* prd = pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n                if (prp) D += (float) prp->m_gp[0];\n                else if (prd) D += (float) prd->m_gp[0];\n            }\n        }\n        else if (pmg) {\n            for (int k=0; k<pmg->Components(); ++k)\n            {\n                FEReactivePlasticityMaterialPoint* prp = pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n                FEReactivePlasticDamageMaterialPoint* prd = pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n                FEElasticMixtureMaterialPoint* pem = pmg->GetPointData(k)->ExtractData<FEElasticMixtureMaterialPoint>();\n                if (prp) D += (float) prp->m_gp[0];\n                else if (prd) D += (float) prd->m_gp[0];\n                else if (pem)\n                {\n                    for (int l=0; l<pem->Components(); ++l)\n                    {\n                        FEReactivePlasticityMaterialPoint* prp = pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n                        FEReactivePlasticDamageMaterialPoint* prd = pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n                        if (prp) D += (float) prp->m_gp[0];\n                        else if (prd) D += (float) prd->m_gp[0];\n                    }\n                }\n            }\n        }\n    }\n    D /= (double) nint;\n    return D;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDiscreteElementStretch::value(FEElement& el)\n{\n\tif (dynamic_cast<FEDiscreteElement*>(&el) == nullptr) return 0.0;\n\n\tFEDiscreteElement& del = dynamic_cast<FEDiscreteElement&>(el);\n\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tvec3d ra0 = mesh.Node(del.m_node[0]).m_r0;\n\tvec3d ra1 = mesh.Node(del.m_node[0]).m_rt;\n\tvec3d rb0 = mesh.Node(del.m_node[1]).m_r0;\n\tvec3d rb1 = mesh.Node(del.m_node[1]).m_rt;\n\n\tdouble L0 = (rb0 - ra0).norm();\n\tdouble Lt = (rb1 - ra1).norm();\n\n\tdouble l = Lt / L0;\n\n\treturn l;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDiscreteElementElongation::value(FEElement& el)\n{\n\tif (dynamic_cast<FEDiscreteElement*>(&el) == nullptr) return 0.0;\n\n\tFEDiscreteElement& del = dynamic_cast<FEDiscreteElement&>(el);\n\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tvec3d ra0 = mesh.Node(del.m_node[0]).m_r0;\n\tvec3d ra1 = mesh.Node(del.m_node[0]).m_rt;\n\tvec3d rb0 = mesh.Node(del.m_node[1]).m_r0;\n\tvec3d rb1 = mesh.Node(del.m_node[1]).m_rt;\n\n\tdouble L0 = (rb0 - ra0).norm();\n\tdouble Lt = (rb1 - ra1).norm();\n\n\tdouble Dl = Lt - L0;\n\n\treturn Dl;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDiscreteElementForce::value(FEElement& el)\n{\n\tif (dynamic_cast<FEDiscreteElement*>(&el) == nullptr) return 0.0;\n\n\tFEDiscreteElement& del = dynamic_cast<FEDiscreteElement&>(el);\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tvec3d ra1 = mesh.Node(del.m_node[0]).m_rt;\n\tvec3d rb1 = mesh.Node(del.m_node[1]).m_rt;\n\tvec3d e = rb1 - ra1; e.unit();\n\n\tFEDiscreteElasticMaterialPoint* mp = el.GetMaterialPoint(0)->ExtractData<FEDiscreteElasticMaterialPoint>();\n\tassert(mp);\n\tif (mp)\n\t{\n\t\tvec3d F = mp->m_Ft;\n\t\tdouble Fm = F * e;\n\t\treturn Fm;\n\t}\n\telse return 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDiscreteElementForceX::value(FEElement& el)\n{\n\tFEDiscreteElasticMaterialPoint* mp = el.GetMaterialPoint(0)->ExtractData<FEDiscreteElasticMaterialPoint>();\n\tif (mp) return mp->m_Ft.x;\n\telse return 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDiscreteElementForceY::value(FEElement& el)\n{\n\tFEDiscreteElasticMaterialPoint* mp = dynamic_cast<FEDiscreteElasticMaterialPoint*>(el.GetMaterialPoint(0));\n\tif (mp) return mp->m_Ft.y;\n\telse return 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogDiscreteElementForceZ::value(FEElement& el)\n{\n\tFEDiscreteElasticMaterialPoint* mp = dynamic_cast<FEDiscreteElasticMaterialPoint*>(el.GetMaterialPoint(0));\n\tif (mp) return mp->m_Ft.z;\n\telse return 0.0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElementMixtureStress::value(FEElement& el)\n{\n\tif (m_comp < 0) return 0.0;\n\n\tdouble s = 0.0;\n\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n\t\tif (mmp)\n\t\t{\n\t\t\tif (m_comp < mmp->Components())\n\t\t\t{\n\t\t\t\tFEElasticMaterialPoint* ep = mmp->GetPointData(m_comp)->ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tif (ep)\n\t\t\t\t{\n\t\t\t\t\tswitch (m_metric)\n\t\t\t\t\t{\n\t\t\t\t\tcase 0: s += ep->m_s.xx(); break;\n\t\t\t\t\tcase 1: s += ep->m_s.xy(); break;\n\t\t\t\t\tcase 2: s += ep->m_s.yy(); break;\n\t\t\t\t\tcase 3: s += ep->m_s.xz(); break;\n\t\t\t\t\tcase 4: s += ep->m_s.yz(); break;\n\t\t\t\t\tcase 5: s += ep->m_s.zz(); break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\ts /= (double)el.GaussPoints();\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyR11::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(0,0)); }\ndouble FELogRigidBodyR12::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(0, 1)); }\ndouble FELogRigidBodyR13::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(0, 2)); }\ndouble FELogRigidBodyR21::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(1, 0)); }\ndouble FELogRigidBodyR22::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(1, 1)); }\ndouble FELogRigidBodyR23::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(1, 2)); }\ndouble FELogRigidBodyR31::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(2, 0)); }\ndouble FELogRigidBodyR32::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(2, 1)); }\ndouble FELogRigidBodyR33::value(FERigidBody& rb) { return (rb.GetRotation().RotationMatrix()(2, 2)); }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyEulerX::value(FERigidBody& rb) { double x, y, z; rb.GetRotation().GetEuler(x, y, z); return x; }\ndouble FELogRigidBodyEulerY::value(FERigidBody& rb) { double x, y, z; rb.GetRotation().GetEuler(x, y, z); return y; }\ndouble FELogRigidBodyEulerZ::value(FERigidBody& rb) { double x, y, z; rb.GetRotation().GetEuler(x, y, z); return z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyPosX::value(FERigidBody& rb) { return rb.m_rt.x; }\ndouble FELogRigidBodyPosY::value(FERigidBody& rb) { return rb.m_rt.y; }\ndouble FELogRigidBodyPosZ::value(FERigidBody& rb) { return rb.m_rt.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyVelX::value(FERigidBody& rb) { return rb.m_vt.x; }\ndouble FELogRigidBodyVelY::value(FERigidBody& rb) { return rb.m_vt.y; }\ndouble FELogRigidBodyVelZ::value(FERigidBody& rb) { return rb.m_vt.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyAccX::value(FERigidBody& rb) { return rb.m_at.x; }\ndouble FELogRigidBodyAccY::value(FERigidBody& rb) { return rb.m_at.y; }\ndouble FELogRigidBodyAccZ::value(FERigidBody& rb) { return rb.m_at.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyAngPosX::value(FERigidBody& rb) { return ((rb.GetRotation().GetVector()).x*rb.GetRotation().GetAngle()); }\ndouble FELogRigidBodyAngPosY::value(FERigidBody& rb) { return ((rb.GetRotation().GetVector()).y*rb.GetRotation().GetAngle()); }\ndouble FELogRigidBodyAngPosZ::value(FERigidBody& rb) { return ((rb.GetRotation().GetVector()).z*rb.GetRotation().GetAngle()); }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyAngVelX::value(FERigidBody& rb) { return rb.m_wt.x; }\ndouble FELogRigidBodyAngVelY::value(FERigidBody& rb) { return rb.m_wt.y; }\ndouble FELogRigidBodyAngVelZ::value(FERigidBody& rb) { return rb.m_wt.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyAngAccX::value(FERigidBody& rb) { return rb.m_alt.x; }\ndouble FELogRigidBodyAngAccY::value(FERigidBody& rb) { return rb.m_alt.y; }\ndouble FELogRigidBodyAngAccZ::value(FERigidBody& rb) { return rb.m_alt.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyQuatX::value(FERigidBody& rb) { return rb.GetRotation().x; }\ndouble FELogRigidBodyQuatY::value(FERigidBody& rb) { return rb.GetRotation().y; }\ndouble FELogRigidBodyQuatZ::value(FERigidBody& rb) { return rb.GetRotation().z; }\ndouble FELogRigidBodyQuatW::value(FERigidBody& rb) { return rb.GetRotation().w; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyForceX::value(FERigidBody& rb) { return rb.m_Fr.x; }\ndouble FELogRigidBodyForceY::value(FERigidBody& rb) { return rb.m_Fr.y; }\ndouble FELogRigidBodyForceZ::value(FERigidBody& rb) { return rb.m_Fr.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyTorqueX::value(FERigidBody& rb) { return rb.m_Mr.x; }\ndouble FELogRigidBodyTorqueY::value(FERigidBody& rb) { return rb.m_Mr.y; }\ndouble FELogRigidBodyTorqueZ::value(FERigidBody& rb) { return rb.m_Mr.z; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyKineticEnergy::value(FERigidBody& rb) {\n    FERigidBody&rbl = static_cast<FERigidBody&>(rb);\n    return (rbl.m_mass*(rbl.m_vt*rbl.m_vt) + rbl.m_wt*(rbl.m_moi*rbl.m_wt))/2;\n}\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyIHAwx::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return omega.x; }\ndouble FELogRigidBodyIHAwy::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return omega.y; }\ndouble FELogRigidBodyIHAwz::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return omega.z; }\ndouble FELogRigidBodyIHAwm::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return omega.norm() * 180 / PI; }\ndouble FELogRigidBodyIHAsx::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return s.x; }\ndouble FELogRigidBodyIHAsy::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return s.y; }\ndouble FELogRigidBodyIHAsz::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return s.z; }\ndouble FELogRigidBodyIHAtd::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.InstantaneousHelicalAxis(omega, s, tdot); return tdot; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidBodyFHAwx::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return omega.x; }\ndouble FELogRigidBodyFHAwy::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return omega.y; }\ndouble FELogRigidBodyFHAwz::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return omega.z; }\ndouble FELogRigidBodyFHAwm::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return omega.norm()*180/PI; }\ndouble FELogRigidBodyFHAsx::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return s.x; }\ndouble FELogRigidBodyFHAsy::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return s.y; }\ndouble FELogRigidBodyFHAsz::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return s.z; }\ndouble FELogRigidBodyFHAtd::value(FERigidBody& rb) { vec3d omega; vec3d s; double tdot; rb.FiniteHelicalAxis(omega, s, tdot); return tdot; }\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidConnectorIHAwx::value(FENLConstraint& rc) {\n    vec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return omega.x;\n}\ndouble FELogRigidConnectorIHAwy::value(FENLConstraint& rc) {\n    vec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return omega.y;\n}\ndouble FELogRigidConnectorIHAwz::value(FENLConstraint& rc) {\n    vec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return omega.z;\n}\ndouble FELogRigidConnectorIHAwm::value(FENLConstraint& rc) {\n    vec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return omega.norm()*180/PI;\n}\ndouble FELogRigidConnectorIHAsx::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return s.x;\n}\ndouble FELogRigidConnectorIHAsy::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return s.y;\n}\ndouble FELogRigidConnectorIHAsz::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return s.z;\n}\ndouble FELogRigidConnectorIHAtd::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->InstantaneousHelicalAxis(omega, s, tdot);\n    return tdot;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidConnectorFHAwx::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return omega.x;\n}\ndouble FELogRigidConnectorFHAwy::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return omega.y;\n}\ndouble FELogRigidConnectorFHAwz::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return omega.z;\n}\ndouble FELogRigidConnectorFHAwm::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return omega.norm()*180/PI;\n}\ndouble FELogRigidConnectorFHAsx::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return s.x;\n}\ndouble FELogRigidConnectorFHAsy::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return s.y;\n}\ndouble FELogRigidConnectorFHAsz::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return s.z;\n}\ndouble FELogRigidConnectorFHAtd::value(FENLConstraint& rc) {\n\tvec3d omega;\n    vec3d s;\n    double tdot;\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    prc->FiniteHelicalAxis(omega, s, tdot);\n    return tdot;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidConnectorForceX::value(FENLConstraint& rc) \n{ \n\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n\treturn (prc ? prc->m_F.x : 0); \n}\n\ndouble FELogRigidConnectorForceY::value(FENLConstraint& rc)\n{\n\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n\treturn (prc ? prc->m_F.y : 0);\n}\n\ndouble FELogRigidConnectorForceZ::value(FENLConstraint& rc)\n{\n\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n\treturn (prc ? prc->m_F.z : 0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidConnectorMomentX::value(FENLConstraint& rc)\n{ \n\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n\treturn (prc ? prc->m_M.x : 0);\n}\n\ndouble FELogRigidConnectorMomentY::value(FENLConstraint& rc)\n{ \n\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n\treturn (prc ? prc->m_M.y : 0);\n}\n\ndouble FELogRigidConnectorMomentZ::value(FENLConstraint& rc)\n{\n\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n\treturn (prc ? prc->m_M.z : 0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidConnectorTranslationX::value(FENLConstraint& rc)\n{\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    return (prc ? prc->RelativeTranslation().x : 0);\n}\n\ndouble FELogRigidConnectorTranslationY::value(FENLConstraint& rc)\n{\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    return (prc ? prc->RelativeTranslation().y : 0);\n}\n\ndouble FELogRigidConnectorTranslationZ::value(FENLConstraint& rc)\n{\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    return (prc ? prc->RelativeTranslation().z : 0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogRigidConnectorRotationX::value(FENLConstraint& rc)\n{\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    return (prc ? prc->RelativeRotation().x : 0);\n}\n\ndouble FELogRigidConnectorRotationY::value(FENLConstraint& rc)\n{\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    return (prc ? prc->RelativeRotation().y : 0);\n}\n\ndouble FELogRigidConnectorRotationZ::value(FENLConstraint& rc)\n{\n    FERigidConnector* prc = dynamic_cast<FERigidConnector*>(&rc);\n    return (prc ? prc->RelativeRotation().z : 0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogVolumeConstraint::value(FENLConstraint& rc)\n{\n    FEVolumeConstraint* prc = dynamic_cast<FEVolumeConstraint*>(&rc);\n    return (prc ? prc->EnclosedVolume() : 0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogVolumePressure::value(FENLConstraint& rc)\n{\n    FEVolumeConstraint* prc = dynamic_cast<FEVolumeConstraint*>(&rc);\n    return (prc ? prc->Pressure() : 0);\n}\n\n//=============================================================================\ndouble FELogContactArea::value(FESurface& surface)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surface);\n\tif (pcs == 0) return 0.0;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tdouble area = pcs->GetContactArea();\n\t\treturn area;\n\t}\n\treturn 0.0;\n}\n\ndouble FELogMaxContactGap::value(FESurface& surface)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surface);\n\tif (pcs == 0) return 0.0;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tdouble maxGap = 0;\n\t\tfor (int i = 0; i < pcs->Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = pcs->Element(i);\n\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t{\n\t\t\t\tFEContactMaterialPoint* pt = dynamic_cast<FEContactMaterialPoint*>(el.GetMaterialPoint(n));\n\t\t\t\tif (pt)\n\t\t\t\t{\n\t\t\t\t\tif (pt->m_gap > maxGap) maxGap = pt->m_gap;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn maxGap;\n\t}\n\treturn 0.0;\n}\n\ndouble FENormalizedInternalEnergy::value(FEDomain& dom)\n{\n\tdouble sum = 0.0;\n\tdouble vol = 0.0;\n\tFESolidDomain* solidDomain = dynamic_cast<FESolidDomain*>(&dom);\n\tif (solidDomain == nullptr) return 0.0;\n\n\tFEModel* fem = GetFEModel();\n\tconst FETimeInfo& ti = fem->GetTime();\n\tdouble dt = ti.timeIncrement;\n\n\tdouble P0 = fem->GetGlobalConstant(\"P\");\n\tif (P0 == 0.0) P0 = 1.0;\n\n\tint NE = solidDomain->Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = solidDomain->Element(i);\n\t\tdouble Ve = solidDomain->Volume(el);\n\t\tvol += Ve;\n\n\t\tmat3ds s(0), D(0);\n\t\tint nint = el.GaussPoints();\n\t\tdouble* gw = el.GaussWeights();\n\t\tdouble w = 0.0;\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tFEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\tif (ep)\n\t\t\t{\n\t\t\t\ts += ep->m_s * gw[n];\n\t\t\t\tD += ep->RateOfDeformation();\n\t\t\t}\n\t\t\tw += gw[n];\n\n\t\t}\n\t\ts /= w;\n\t\tD /= w;\n\n\t\tdouble We = s.dotdot(D);\n\n\t\tsum += We * dt * Ve;\n\t}\n\tm_sum += sum;\n\tdouble NTSIE = m_sum / (P0 * vol);\n\treturn NTSIE;\n}\n\ndouble FELogTotalEnergy::value(FEDomain& dom)\n{\n\tm_sum = 0.0;\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\t\tif (pme == 0) return false;\n\n\t\tdouble E = 0.0;\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i < solidDomain.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\tif (pt)\n\t\t\t\t{\n\t\t\t\t\t// strain energy\n\t\t\t\t\tdouble W = pme->StrainEnergyDensity(mp);\n\n\t\t\t\t\t// kinetic energy\n\t\t\t\t\tdouble D = pme->Density(mp);\n\t\t\t\t\tvec3d& v = pt->m_v;\n\t\t\t\t\tdouble K = 0.5 * (v * v) * D;\n\n\t\t\t\t\tdouble J0 = solidDomain.detJ0(el, n);\n\n\t\t\t\t\tE += (K + W) * J0 * w[n];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tm_sum = E;\n\t}\n\n\treturn m_sum;\n}\n"
  },
  {
    "path": "FEBioMech/FEBioMechData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/NodeDataRecord.h>\n#include <FECore/ElementDataRecord.h>\n#include <FECore/FaceDataRecord.h>\n#include \"ObjectDataRecord.h\"\n#include <FECore/NLConstraintDataRecord.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/SurfaceDataRecord.h>\n#include <FECore/DomainDataRecord.h>\n\n//=============================================================================\n// N O D E  D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FENodeXPos : public FELogNodeData\n{ \npublic: \n\tFENodeXPos(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeYPos : public FELogNodeData \n{ \npublic: \n\tFENodeYPos(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeZPos : public FELogNodeData\n{ \npublic: \n\tFENodeZPos(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeXDisp : public FELogNodeData\n{ \npublic: \n\tFENodeXDisp(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeYDisp : public FELogNodeData\n{ \npublic: \n\tFENodeYDisp(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeZDisp : public FELogNodeData\n{ \npublic: \n\tFENodeZDisp(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeXVel : public FELogNodeData\n{ \npublic: \n\tFENodeXVel(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeYVel : public FELogNodeData\n{ \npublic: \n\tFENodeYVel(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeZVel : public FELogNodeData\n{ \npublic: \n\tFENodeZVel(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeXAcc : public FELogNodeData\n{ \npublic: \n\tFENodeXAcc(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeYAcc : public FELogNodeData\n{ \npublic: \n\tFENodeYAcc(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeZAcc : public FELogNodeData\n{ \npublic: \n\tFENodeZAcc(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeForceX: public FELogNodeData\n{ \npublic: \n\tFENodeForceX(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeForceY: public FELogNodeData\n{ \npublic: \n\tFENodeForceY(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeForceZ: public FELogNodeData\n{ \npublic: \n\tFENodeForceZ(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node) override;\n};\n\n//=============================================================================\n// F A C E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FELogContactGap : public FELogFaceData\n{\npublic:\n\tFELogContactGap(FEModel* fem) : FELogFaceData(fem) {}\n\tdouble value(FESurfaceElement& el) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogContactPressure : public FELogFaceData\n{\npublic:\n\tFELogContactPressure(FEModel* fem) : FELogFaceData(fem) {}\n\tdouble value(FESurfaceElement& el) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogContactTractionX : public FELogFaceData\n{\npublic:\n\tFELogContactTractionX(FEModel* fem) : FELogFaceData(fem) {}\n\tdouble value(FESurfaceElement& el) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogContactTractionY : public FELogFaceData\n{\npublic:\n\tFELogContactTractionY(FEModel* fem) : FELogFaceData(fem) {}\n\tdouble value(FESurfaceElement& el) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogContactTractionZ : public FELogFaceData\n{\npublic:\n\tFELogContactTractionZ(FEModel* fem) : FELogFaceData(fem) {}\n\tdouble value(FESurfaceElement& el) override;\n};\n\n//=============================================================================\n// E L E M E N T   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FELogElemPosX : public FELogElemData\n{\npublic:\n\tFELogElemPosX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPosY : public FELogElemData\n{\npublic:\n\tFELogElemPosY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPosZ : public FELogElemData\n{\npublic:\n\tFELogElemPosZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemJacobian : public FELogElemData\n{\npublic:\n\tFELogElemJacobian(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainX : public FELogElemData\n{\npublic:\n\tFELogElemStrainX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainY : public FELogElemData\n{\npublic:\n\tFELogElemStrainY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainZ : public FELogElemData\n{\npublic:\n\tFELogElemStrainZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainXY : public FELogElemData\n{\npublic:\n\tFELogElemStrainXY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainYZ : public FELogElemData\n{\npublic:\n\tFELogElemStrainYZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainXZ : public FELogElemData\n{\npublic:\n\tFELogElemStrainXZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrain1 : public FELogElemData\n{\npublic:\n\tFELogElemStrain1(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainEffective : public FELogElemData\n{\npublic:\n\tFELogElemStrainEffective(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrain2 : public FELogElemData\n{\npublic:\n\tFELogElemStrain2(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrain3 : public FELogElemData\n{\npublic:\n\tFELogElemStrain3(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemInfStrainX : public FELogElemData\n{\npublic:\n\tFELogElemInfStrainX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemInfStrainY : public FELogElemData\n{\npublic:\n\tFELogElemInfStrainY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemInfStrainZ : public FELogElemData\n{\npublic:\n\tFELogElemInfStrainZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemInfStrainXY : public FELogElemData\n{\npublic:\n\tFELogElemInfStrainXY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemInfStrainYZ : public FELogElemData\n{\npublic:\n\tFELogElemInfStrainYZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemInfStrainXZ : public FELogElemData\n{\npublic:\n\tFELogElemInfStrainXZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchX : public FELogElemData\n{\npublic:\n    FELogElemRightStretchX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchY : public FELogElemData\n{\npublic:\n    FELogElemRightStretchY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchZ : public FELogElemData\n{\npublic:\n    FELogElemRightStretchZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchXY : public FELogElemData\n{\npublic:\n    FELogElemRightStretchXY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchYZ : public FELogElemData\n{\npublic:\n    FELogElemRightStretchYZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchXZ : public FELogElemData\n{\npublic:\n    FELogElemRightStretchXZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretch1 : public FELogElemData\n{\npublic:\n    FELogElemRightStretch1(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretch2 : public FELogElemData\n{\npublic:\n    FELogElemRightStretch2(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretch3 : public FELogElemData\n{\npublic:\n    FELogElemRightStretch3(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightStretchEffective : public FELogElemData\n{\npublic:\n    FELogElemRightStretchEffective(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchX : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchY : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchZ : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchXY : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchXY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchYZ : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchYZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchXZ : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchXZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretch1 : public FELogElemData\n{\npublic:\n    FELogElemLeftStretch1(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretch2 : public FELogElemData\n{\npublic:\n    FELogElemLeftStretch2(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretch3 : public FELogElemData\n{\npublic:\n    FELogElemLeftStretch3(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftStretchEffective : public FELogElemData\n{\npublic:\n    FELogElemLeftStretchEffective(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyX : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyY : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyZ : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyXY : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyXY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyYZ : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyYZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyXZ : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyXZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHencky1 : public FELogElemData\n{\npublic:\n    FELogElemRightHencky1(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHencky2 : public FELogElemData\n{\npublic:\n    FELogElemRightHencky2(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHencky3 : public FELogElemData\n{\npublic:\n    FELogElemRightHencky3(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemRightHenckyEffective : public FELogElemData\n{\npublic:\n    FELogElemRightHenckyEffective(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyX : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyX(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyY : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyZ : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyXY : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyXY(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyYZ : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyYZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyXZ : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyXZ(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHencky1 : public FELogElemData\n{\npublic:\n    FELogElemLeftHencky1(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHencky2 : public FELogElemData\n{\npublic:\n    FELogElemLeftHencky2(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHencky3 : public FELogElemData\n{\npublic:\n    FELogElemLeftHencky3(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemLeftHenckyEffective : public FELogElemData\n{\npublic:\n    FELogElemLeftHenckyEffective(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressX : public FELogElemData\n{\npublic:\n\tFELogElemStressX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressY : public FELogElemData\n{\npublic:\n\tFELogElemStressY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressZ : public FELogElemData\n{\npublic:\n\tFELogElemStressZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressXY : public FELogElemData\n{\npublic:\n\tFELogElemStressXY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressYZ : public FELogElemData\n{\npublic:\n\tFELogElemStressYZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressXZ : public FELogElemData\n{\npublic:\n\tFELogElemStressXZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressEffective : public FELogElemData\n{\npublic:\n\tFELogElemStressEffective(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStress1 : public FELogElemData\n{\npublic:\n\tFELogElemStress1(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStress2 : public FELogElemData\n{\npublic:\n\tFELogElemStress2(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStress3 : public FELogElemData\n{\npublic:\n\tFELogElemStress3(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK2StressX : public FELogElemData\n{\npublic:\n\tFELogElemPK2StressX(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK2StressY : public FELogElemData\n{\npublic:\n\tFELogElemPK2StressY(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK2StressZ : public FELogElemData\n{\npublic:\n\tFELogElemPK2StressZ(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK2StressXY : public FELogElemData\n{\npublic:\n\tFELogElemPK2StressXY(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK2StressYZ : public FELogElemData\n{\npublic:\n\tFELogElemPK2StressYZ(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK2StressXZ : public FELogElemData\n{\npublic:\n\tFELogElemPK2StressXZ(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressXX : public FELogElemData\n{\npublic:\n    FELogElemPK1StressXX(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressYY : public FELogElemData\n{\npublic:\n    FELogElemPK1StressYY(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressZZ : public FELogElemData\n{\npublic:\n    FELogElemPK1StressZZ(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressXY : public FELogElemData\n{\npublic:\n    FELogElemPK1StressXY(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressYZ : public FELogElemData\n{\npublic:\n    FELogElemPK1StressYZ(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressXZ : public FELogElemData\n{\npublic:\n    FELogElemPK1StressXZ(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressYX : public FELogElemData\n{\npublic:\n    FELogElemPK1StressYX(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressZY : public FELogElemData\n{\npublic:\n    FELogElemPK1StressZY(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPK1StressZX : public FELogElemData\n{\npublic:\n    FELogElemPK1StressZX(FEModel* pfem) : FELogElemData(pfem) {}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStressEigenVector : public FELogElemData\n{\npublic:\n\tFELogElemStressEigenVector(FEModel* pfem) : FELogElemData(pfem), m_eigenVector(-1), m_component(-1) {}\n\tdouble value(FEElement& el);\n\nprotected:\n\tint\tm_eigenVector;\n\tint\tm_component;\n};\n\ntemplate <int eigenVector, int component> class FELogElemStressEigenVector_T : public FELogElemStressEigenVector\n{\npublic:\n\tFELogElemStressEigenVector_T(FEModel* pfem) : FELogElemStressEigenVector(pfem)\n\t{\n\t\tm_eigenVector = eigenVector;\n\t\tm_component = component;\n\t}\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientXX : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientXX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientXY : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientXY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientXZ : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientXZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientYX : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientYX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientYY : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientYY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientYZ : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientYZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientZX : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientZX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientZY : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientZY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDeformationGradientZZ : public FELogElemData\n{\npublic:\n\tFELogElemDeformationGradientZZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n// This log variable outputs the total deformation gradient\n// For prestrain materials this will include the effect of the prestrain gradient\n// For other materials, this will output the same as the regular deformation gradient\nclass FELogTotalDeformationGradient : public FELogElemData\n{\npublic:\n\tFELogTotalDeformationGradient(int r, int c, FEModel* fem) : FELogElemData(fem), m_r(r), m_c(c) {}\n\tdouble value(FEElement& el) override;\nprivate:\n\tint m_r, m_c;\n};\n\ntemplate <int row, int col> class FELogTotalDeformationGradient_T : public FELogTotalDeformationGradient\n{\npublic:\n\tFELogTotalDeformationGradient_T(FEModel* fem) : FELogTotalDeformationGradient(row, col, fem){}\n};\n\n//-----------------------------------------------------------------------------\n// Base class for elasticity tensor output\nclass FELogElemElasticity_ : public FELogElemData\n{\nprotected:\n\tFELogElemElasticity_(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el, int n);\n};\n\n//-----------------------------------------------------------------------------\n// template class for generating the different tensor components\ntemplate <int n> class FELogElemElasticity : public FELogElemElasticity_\n{\npublic:\n\tFELogElemElasticity(FEModel* pfem) : FELogElemElasticity_(pfem){}\n\tdouble value(FEElement& el) { return FELogElemElasticity_::value(el, n); }\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemStrainEnergyDensity : public FELogElemData\n{\npublic:\n\tFELogElemStrainEnergyDensity(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemDevStrainEnergyDensity : public FELogElemData\n{\npublic:\n\tFELogElemDevStrainEnergyDensity(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFiberStretch : public FELogElemData\n{\npublic:\n\tFELogElemFiberStretch(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFiberVector_ : public FELogElemData\n{\npublic:\n\tFELogElemFiberVector_(FEModel* pfem, int comp) : FELogElemData(pfem), m_comp(comp) {}\n\tdouble value(FEElement& el);\n\nprivate:\n\tint m_comp;\n};\n\ntemplate <int N> class FELogElemFiberVector_N : public FELogElemFiberVector_\n{\npublic:\n\tFELogElemFiberVector_N(FEModel* fem) : FELogElemFiberVector_(fem, N) {}\n};\n\n//-----------------------------------------------------------------------------\n//! Damage (fraction of broken bonds))\nclass FELogDamage_ : public FELogElemData\n{\npublic:\n\tFELogDamage_(FEModel* pfem, int comp = -1) : FELogElemData(pfem), m_comp(comp) {}\n\tdouble value(FEElement& el);\nprivate:\n\tint m_comp;\n};\n\nclass FELogDamage : public FELogDamage_\n{\npublic:\n\tFELogDamage(FEModel* pfem) : FELogDamage_(pfem, -1) {}\n};\n\ntemplate <int n> class FELogDamage_n : public FELogDamage_\n{\npublic:\n\tFELogDamage_n(FEModel* fem) : FELogDamage_(fem, n) {}\n};\n\n//-----------------------------------------------------------------------------\n//! Fraction of intact bonds\nclass FELogIntactBonds : public FELogElemData\n{\npublic:\n    FELogIntactBonds(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Fraction of fatigued bonds\nclass FELogFatigueBonds : public FELogElemData\n{\npublic:\n    FELogFatigueBonds(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Fraction of yielded bonds\nclass FELogYieldedBonds : public FELogElemData\n{\npublic:\n    FELogYieldedBonds(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Damage reduction factor\nclass FELogOctahedralPlasticStrain : public FELogElemData\n{\npublic:\n    FELogOctahedralPlasticStrain(FEModel* pfem) : FELogElemData(pfem){}\n    double value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element stretch\nclass FELogDiscreteElementStretch : public FELogElemData\n{\npublic:\n\tFELogDiscreteElementStretch(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element elongation\nclass FELogDiscreteElementElongation : public FELogElemData\n{\npublic:\n\tFELogDiscreteElementElongation(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element force\nclass FELogDiscreteElementForce : public FELogElemData\n{\npublic:\n\tFELogDiscreteElementForce(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element force\nclass FELogDiscreteElementForceX : public FELogElemData\n{\npublic:\n\tFELogDiscreteElementForceX(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element force\nclass FELogDiscreteElementForceY : public FELogElemData\n{\npublic:\n\tFELogDiscreteElementForceY(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element force\nclass FELogDiscreteElementForceZ : public FELogElemData\n{\npublic:\n\tFELogDiscreteElementForceZ(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElementMixtureStress : public FELogElemData\n{\npublic:\n\tFELogElementMixtureStress(FEModel* fem, int n, int m) : FELogElemData(fem), m_comp(n), m_metric(m) {}\n\tdouble value(FEElement& el);\n\nprivate:\n\tint\tm_comp;\n\tint\tm_metric;\n};\n\ntemplate <int N, int M> class FELogElementMixtureStress_T : public FELogElementMixtureStress\n{\npublic:\n\tFELogElementMixtureStress_T(FEModel* fem) : FELogElementMixtureStress(fem, N, M) {}\n};\n\n//=============================================================================\n// R I G I D   B O D Y    D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyPosX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyPosX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyPosY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyPosY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyPosZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyPosZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyVelX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyVelX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyVelY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyVelY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyVelZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyVelZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAccX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAccX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAccY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAccY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAccZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAccZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngPosX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngPosX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngPosY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngPosY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngPosZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngPosZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngVelX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngVelX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngVelY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngVelY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngVelZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngVelZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngAccX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngAccX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngAccY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngAccY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyAngAccZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyAngAccZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyQuatX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyQuatX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyQuatY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyQuatY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyQuatZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyQuatZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyQuatW : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyQuatW(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR11 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR11(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR12 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR12(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR13 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR13(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR21 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR21(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR22 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR22(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR23 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR23(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR31 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR31(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR32 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR32(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyR33 : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyR33(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyEulerX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyEulerX(FEModel* pfem) : FELogObjectData(pfem) {}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyEulerY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyEulerY(FEModel* pfem) : FELogObjectData(pfem) {}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyEulerZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyEulerZ(FEModel* pfem) : FELogObjectData(pfem) {}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyForceX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyForceX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyForceY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyForceY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyForceZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyForceZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyTorqueX : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyTorqueX(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyTorqueY : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyTorqueY(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyTorqueZ : public FELogObjectData\n{\npublic:\n\tFELogRigidBodyTorqueZ(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyKineticEnergy : public FELogObjectData\n{\npublic:\n    FELogRigidBodyKineticEnergy(FEModel* pfem) : FELogObjectData(pfem){}\n\tdouble value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorForceX : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorForceX(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorForceY : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorForceY(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorForceZ : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorForceZ(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorMomentX : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorMomentX(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorMomentY : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorMomentY(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorMomentZ : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorMomentZ(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorTranslationX : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorTranslationX(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorTranslationY : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorTranslationY(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorTranslationZ : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorTranslationZ(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorRotationX : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorRotationX(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorRotationY : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorRotationY(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorRotationZ : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorRotationZ(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogVolumeConstraint : public FELogNLConstraintData\n{\npublic:\n    FELogVolumeConstraint(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogVolumePressure : public FELogNLConstraintData\n{\npublic:\n    FELogVolumePressure(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAwx : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAwx(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAwy : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAwy(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAwz : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAwz(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAwm : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAwm(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAsx : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAsx(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAsy : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAsy(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAsz : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAsz(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyIHAtd : public FELogObjectData\n{\npublic:\n    FELogRigidBodyIHAtd(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAwx : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAwx(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAwy : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAwy(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAwz : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAwz(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAwm : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAwm(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAsx : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAsx(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAsy : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAsy(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAsz : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAsz(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidBodyFHAtd : public FELogObjectData\n{\npublic:\n    FELogRigidBodyFHAtd(FEModel* pfem) : FELogObjectData(pfem){}\n    double value(FERigidBody& rb) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAwx : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAwx(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAwy : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAwy(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAwz : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAwz(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAwm : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAwm(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAsx : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAsx(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAsy : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAsy(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAsz : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAsz(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorIHAtd : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorIHAtd(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAwx : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAwx(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAwy : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAwy(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAwz : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAwz(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAwm : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAwm(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAsx : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAsx(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAsy : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAsy(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAsz : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAsz(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogRigidConnectorFHAtd : public FELogNLConstraintData\n{\npublic:\n    FELogRigidConnectorFHAtd(FEModel* pfem) : FELogNLConstraintData(pfem){}\n    double value(FENLConstraint& rc);\n};\n\n//=============================================================================\n// S U R F A C E   D A T A\n//=============================================================================\n\nclass FELogContactArea : public FELogSurfaceData\n{\npublic:\n\tFELogContactArea(FEModel* fem) : FELogSurfaceData(fem) {}\n\tdouble value(FESurface& surface) override;\n};\n\nclass FELogMaxContactGap : public FELogSurfaceData\n{\npublic:\n\tFELogMaxContactGap(FEModel* fem) : FELogSurfaceData(fem) {}\n\tdouble value(FESurface& surface) override;\n};\n\n//=============================================================================\n// D O M A I N   D A T A\n//=============================================================================\n// from Gibbons, \"Finite Element Modeling of Blast Lung Injury in Sheep\", JBME 2015\n// this calculates the normalized time-summed internal energy\nclass FENormalizedInternalEnergy : public FELogDomainData\n{\npublic:\n\tFENormalizedInternalEnergy(FEModel* fem) : FELogDomainData(fem), m_sum(0) {}\n\tdouble value(FEDomain& dom) override;\n\nprivate:\n\tdouble m_sum;\n};\n\nclass FELogTotalEnergy : public FELogDomainData\n{\npublic:\n\tFELogTotalEnergy(FEModel* fem) : FELogDomainData(fem), m_sum(0) {}\n\tdouble value(FEDomain& dom) override;\n\nprivate:\n\tdouble m_sum;\n};\n"
  },
  {
    "path": "FEBioMech/FEBioMechModule.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n\n#include \"FEBioMech.h\"\n#include \"FEBioMechModule.h\"\n#include \"FE2DFiberNeoHookean.h\"\n#include \"FE2DTransIsoMooneyRivlin.h\"\n#include \"FE2DTransIsoVerondaWestmann.h\"\n#include \"FEABUnconstrained.h\"\n#include \"FEActiveFiberContraction.h\"\n#include \"FEUncoupledActiveFiberContraction.h\"\n#include \"FEArrudaBoyce.h\"\n#include \"FECarreauYasudaViscousSolid.h\"\n#include \"FECarterHayesOld.h\"\n#include \"FECellGrowth.h\"\n#include \"FECubicCLE.h\"\n#include \"FEDamageMooneyRivlin.h\"\n#include \"FEDamageNeoHookean.h\"\n#include \"FEDamageTransIsoMooneyRivlin.h\"\n#include \"FESpringMaterial.h\"\n#include \"FETorsionalSpring.h\"\n#include \"FEDonnanEquilibrium.h\"\n#include \"FEEFDDonnanEquilibrium.h\"\n#include \"FEEFDMooneyRivlin.h\"\n#include \"FEEFDNeoHookean.h\"\n#include \"FEEFDUncoupled.h\"\n#include \"FEEFDVerondaWestmann.h\"\n#include \"FEElasticMixture.h\"\n#include \"FEElasticMultigeneration.h\"\n#include \"FEEllipsoidalFiberDistribution.h\"\n#include \"FEFiberCDF.h\"\n#include \"FEFiberCDFUncoupled.h\"\n#include \"FEFiberEntropyChain.h\"\n#include \"FEFiberEntropyChainUC.h\"\n#include \"FEFiberExpPow.h\"\n#include \"FEFiberExpPowUncoupled.h\"\n#include \"FEFiberNaturalNeoHookean.h\"\n#include \"FEFiberNeoHookean.h\"\n#include \"FEFiberPow.h\"\n#include \"FEFiberPowLinear.h\"\n#include \"FEFiberPowLinearUncoupled.h\"\n#include \"FEFiberEFDNeoHookean.h\"\n#include \"FEFiberExponentialPowerUC.h\"\n#include \"FEFiberNHUC.h\"\n#include \"FEFiberKiousisUncoupled.h\"\n#include \"FEForceVelocityContraction.h\"\n#include \"FEFungOrthoCompressible.h\"\n#include \"FEFungOrthotropic.h\"\n#include \"FEHolmesMow.h\"\n#include \"FEHolmesMowUC.h\"\n#include \"FEHolzapfelGasserOgden.h\"\n#include \"FEHolzapfelUnconstrained.h\"\n#include \"FEHuiskesSupply.h\"\n#include \"FEIncompNeoHookean.h\"\n#include \"FEIsotropicElastic.h\"\n#include \"FEIsoHencky.h\"\n#include \"FEMooneyRivlin.h\"\n#include \"FEMooneyRivlinAD.h\"\n#include \"FEMRVonMisesFibers.h\"\n#include \"FEMuscleMaterial.h\"\n#include \"FENaturalNeoHookean.h\"\n#include \"FENeoHookean.h\"\n#include \"FENeoHookeanAD.h\"\n#include \"FENeoHookeanTransIso.h\"\n#include \"FENewtonianViscousSolid.h\"\n#include \"FENewtonianViscousSolidUC.h\"\n#include \"FEOgdenMaterial.h\"\n#include \"FEOgdenUnconstrained.h\"\n#include \"FEOrthoElastic.h\"\n#include \"FEOrthotropicCLE.h\"\n#include \"FEOsmoticVirialExpansion.h\"\n#include \"FEPerfectOsmometer.h\"\n#include \"FEReactiveFatigue.h\"\n#include \"FEUncoupledReactiveFatigue.h\"\n#include \"FERemodelingElasticMaterial.h\"\n#include \"FERigidMaterial.h\"\n#include \"FESphericalFiberDistribution.h\"\n#include \"FEStVenantKirchhoff.h\"\n#include \"FETCNonlinearOrthotropic.h\"\n#include \"FETendonMaterial.h\"\n#include \"FETraceFreeNeoHookean.h\"\n#include \"FETransIsoMooneyRivlin.h\"\n#include \"FETransIsoMREstrada.h\"\n#include \"FETransIsoVerondaWestmann.h\"\n#include \"FETrussMaterial.h\"\n#include \"FEUncoupledActiveContraction.h\"\n#include \"FEUncoupledElasticMixture.h\"\n#include \"FEUncoupledViscoElasticMaterial.h\"\n#include \"FEUncoupledViscoElasticDamage.h\"\n#include \"FEVerondaWestmann.h\"\n#include \"FEViscoElasticDamage.h\"\n#include \"FEViscoElasticMaterial.h\"\n#include \"FEVonMisesPlasticity.h\"\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberDensityDistribution.h\"\n#include \"FEContinuousFiberDistribution.h\"\n#include \"FEContinuousFiberDistributionUC.h\"\n#include \"FEODFFiberDistribution.h\"\n#include \"FEFiberIntegrationGauss.h\"\n#include \"FEFiberIntegrationTrapezoidal.h\"\n#include \"FEFiberIntegrationGeodesic.h\"\n#include \"FEFiberIntegrationGaussKronrod.h\"\n#include \"FEFiberIntegrationTriangle.h\"\n#include \"FECoupledTransIsoMooneyRivlin.h\"\n#include \"FECoupledTransIsoVerondaWestmann.h\"\n#include \"FEHGOCoronary.h\"\n#include \"FENonlinearSpring.h\"\n#include \"FEDiscreteElementMaterial.h\"\n#include \"FEPRLig.h\"\n#include \"FECoupledMooneyRivlin.h\"\n#include \"FECoupledVerondaWestmann.h\"\n#include \"FEReactivePlasticity.h\"\n#include \"FEReactivePlasticDamage.h\"\n#include \"FEReactiveViscoelastic.h\"\n#include \"FEUncoupledReactiveViscoelastic.h\"\n#include \"FEBondRelaxation.h\"\n#include \"FEBondRecruitment.h\"\n#include \"FEDamageMaterial.h\"\n#include \"FEDamageMaterialUC.h\"\n#include \"FERVEDamageMaterial.h\"\n#include \"FERVEFatigueMaterial.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FERelativeVolumeCriterion.h\"\n#include \"FEPlasticFlowCurve.h\"\n#include \"FEFiberExpLinear.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n#include \"FEPrescribedActiveContractionUniaxial.h\"\n#include \"FEPrescribedActiveContractionUniaxialUC.h\"\n#include \"FEPrescribedActiveContractionTransIso.h\"\n#include \"FEPrescribedActiveContractionTransIsoUC.h\"\n#include \"FEPrescribedActiveContractionIsotropic.h\"\n#include \"FEPrescribedActiveContractionIsotropicUC.h\"\n#include \"FEGentMaterial.h\"\n#include \"FEWrinkleOgdenMaterial.h\"\n#include \"FEGenericHyperelastic.h\"\n#include \"FEGenericHyperelasticUC.h\"\n#include \"FEGenericTransIsoHyperelastic.h\"\n#include \"FEGenericTransIsoHyperelasticUC.h\"\n#include \"FEActiveFiberStress.h\"\n#include \"FEActiveFiberStressUC.h\"\n#include \"FEContinuousElasticDamage.h\"\n#include \"FEIsotropicLeeSacks.h\"\n#include \"FEIsotropicLeeSacksUncoupled.h\"\n#include \"FEPolynomialHyperElastic.h\"\n#include \"FEShenoyMaterial.h\"\n#include \"FELungMaterial.h\"\n#include \"FEGrowthTensor.h\"\n#include \"FEKinematicGrowth.h\"\n#include \"FEYeoh.h\"\n#include \"FEScaledElasticMaterial.h\"\n#include \"FEScaledUncoupledMaterial.h\"\n#include \"FEPressureLoad.h\"\n#include \"FEScriptedPressureLoad.h\"\n#include \"FEScriptedTractionLoad.h\"\n#include \"FEScriptedBodyForce.h\"\n#include \"FEScriptedDisplacementBC.h\"\n#include \"FEPressureRobinBC.h\"\n#include \"FETractionLoad.h\"\n#include \"FETractionRobinBC.h\"\n#include \"FESurfaceForceUniform.h\"\n#include \"FEBearingLoad.h\"\n#include \"FEIdealGasPressure.h\"\n#include \"FEGenericBodyForce.h\"\n#include \"FECentrifugalBodyForce.h\"\n#include \"FEPointBodyForce.h\"\n#include \"FESurfaceAttractionBodyForce.h\"\n#include \"FEMassDamping.h\"\n#include \"FEMovingFrameLoad.h\"\n#include \"FERadialBodyForce.h\"\n#include \"FEAxialBodyForce.h\"\n#include \"FEFacet2FacetSliding.h\"\n#include \"FEPeriodicBoundary.h\"\n#include \"FERigidWallInterface.h\"\n#include \"FESlidingInterface.h\"\n#include \"FESlidingElasticInterface.h\"\n#include \"FEPeriodicSurfaceConstraint.h\"\n#include \"FETiedInterface.h\"\n#include \"FETiedElasticInterface.h\"\n#include \"FEStickyInterface.h\"\n#include \"FEPointConstraint.h\"\n#include \"FEAzimuthConstraint.h\"\n#include \"FEFacet2FacetTied.h\"\n#include \"FEVolumeConstraint.h\"\n#include \"FEDistanceConstraint.h\"\n#include \"FEMortarSlidingContact.h\"\n#include \"FEMortarTiedContact.h\"\n#include \"FEContactPotential.h\"\n#include \"FEEdgeToSurfaceContactPotential.h\"\n#include \"FEEdgeToSurfaceSlidingContact.h\"\n#include \"FEPipetteAspiration.h\"\n\n#include \"FESymmetryPlane.h\"\n#include \"FERigidJoint.h\"\n#include \"FEGenericRigidJoint.h\"\n#include \"FERigidSphericalJoint.h\"\n#include \"FERigidRevoluteJoint.h\"\n#include \"FERigidPrismaticJoint.h\"\n#include \"FERigidCylindricalJoint.h\"\n#include \"FERigidPlanarJoint.h\"\n#include \"FERigidLock.h\"\n#include \"FERigidSpring.h\"\n#include \"FERigidDamper.h\"\n#include \"FERigidAngularDamper.h\"\n#include \"FERigidContractileForce.h\"\n#include \"FERigidForce.h\"\n#include \"FERigidCable.h\"\n#include \"FEDiscreteContact.h\"\n#include \"FERigidFollowerForce.h\"\n#include \"FERigidFollowerMoment.h\"\n#include \"FEFixedNormalDisplacement.h\"\n\n#include \"FESolidSolver.h\"\n#include \"FESolidSolver2.h\"\n#include \"FEExplicitSolidSolver.h\"\n#include \"FECGSolidSolver.h\"\n\n#include \"FEBioMechPlot.h\"\n#include \"FEBioMechData.h\"\n\n#include \"FESolidDomainFactory.h\"\n#include \"FEElasticSolidDomain.h\"\n#include \"FEElasticShellDomain.h\"\n#include \"FEElasticShellDomainOld.h\"\n#include \"FEElasticEASShellDomain.h\"\n#include \"FEElasticANSShellDomain.h\"\n#include \"FEElasticTrussDomain.h\"\n#include \"FELinearTrussDomain.h\"\n#include \"FERigidSolidDomain.h\"\n#include \"FERigidShellDomain.h\"\n#include \"FEElasticBeamDomain.h\"\n#include \"FERemodelingElasticDomain.h\"\n#include \"FEUDGHexDomain.h\"\n#include \"FEUT4Domain.h\"\n#include \"FESRIElasticSolidDomain.h\"\n#include \"FE3FieldElasticSolidDomain.h\"\n#include \"FE3FieldElasticShellDomain.h\"\n#include \"FEDiscreteElasticDomain.h\"\n#include \"FEDeformableSpringDomain.h\"\n#include \"RigidBC.h\"\n#include \"FERigidNodeSet.h\"\n#include \"FERigidRotationVector.h\"\n#include \"FERigidEulerAngles.h\"\n#include \"FEFixedDisplacement.h\"\n#include \"FEFixedShellDisplacement.h\"\n#include \"FEFixedRotation.h\"\n#include \"FEPrescribedDisplacement.h\"\n#include \"FEPrescribedShellDisplacement.h\"\n#include \"FEPrescribedRotation.h\"\n#include \"FEBCPrescribedDeformation.h\"\n#include \"FEBCRigidDeformation.h\"\n#include \"FEPrescribedNormalDisplacement.h\"\n#include \"FEMaxStressCriterion.h\"\n#include \"FEMaxDamageCriterion.h\"\n#include \"FESpringRuptureCriterion.h\"\n#include \"FEContactGapCriterion.h\"\n\n#include \"FEInitialDisplacement.h\"\n#include \"FEInitialVelocity.h\"\n#include \"FENodalForce.h\"\n#include \"FENodalTargetForce.h\"\n\n#include \"FEPreStrainElastic.h\"\n#include \"FEPreStrainUncoupledElastic.h\"\n#include \"FEConstPrestrain.h\"\n#include \"FEInSituStretchGradient.h\"\n#include \"FEPreStrainConstraint.h\"\n#include \"FEInitialPreStrain.h\"\n#include \"FEInitialRigidKinematics.h\"\n\n#include \"FENodeToNodeConstraint.h\"\n\n#include \"FEDeformationMapGenerator.h\"\n\n#include \"FESolidModule.h\"\n\n#include \"FESolidAnalysis.h\"\n\n#include \"FEElasticBeamMaterial.h\"\n#include \"FETiedLineConstraint.h\"\n#include \"FESlideLineConstraint.h\"\n\n//-----------------------------------------------------------------------------\n//! Register all the classes of the FEBioMech module with the FEBio framework.\nvoid FEBioMech::InitModule()\n{\n\t//-----------------------------------------------------------------------------\n\t// Domain factory\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tfebio.RegisterDomain(new FESolidDomainFactory);\n\n\t//-----------------------------------------------------------------------------\n\t// create module\n\tfebio.CreateModule(new FESolidModule, \"solid\",\n\t\t\"{\"\n\t\t\"   \\\"title\\\" : \\\"Structural Mechanics\\\",\"\n\t\t\"   \\\"info\\\"  : \\\"Quasi-static or dynamical structural mechanics analysis.\\\"\"\n\t\t\"}\");\n\n\t//-----------------------------------------------------------------------------\n\t// analyis classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FESolidAnalysis, \"solid\");\n\n\t//-----------------------------------------------------------------------------\n\t// Solver classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FESolidSolver2, \"solid\");\n\tREGISTER_FECORE_CLASS(FEExplicitSolidSolver, \"explicit-solid\");\n\tREGISTER_FECORE_CLASS(FESolidSolver, \"solid_old\", FECORE_DEPRECATED);\n\tREGISTER_FECORE_CLASS(FECGSolidSolver, \"CG-solid\");\n\n\t//-----------------------------------------------------------------------------\n\t// material classes\n\n\t// elastic materials (derived from FEElasticMaterial)\n\tREGISTER_FECORE_CLASS(FE2DFiberNeoHookean, \"2D fiber neo-Hookean\");\n    REGISTER_FECORE_CLASS(FEABUnconstrained, \"Arruda-Boyce unconstrained\");\n\tREGISTER_FECORE_CLASS(FECarreauYasudaViscousSolid, \"Carreau-Yasuda viscous solid\");\n\tREGISTER_FECORE_CLASS(FECellGrowth, \"cell growth\");\n\tREGISTER_FECORE_CLASS(FECubicCLE, \"cubic CLE\");\n\tREGISTER_FECORE_CLASS(FEDamageNeoHookean, \"damage neo-Hookean\");\n\tREGISTER_FECORE_CLASS(FEDonnanEquilibrium, \"Donnan equilibrium\");\n\tREGISTER_FECORE_CLASS(FEEFDDonnanEquilibrium, \"EFD Donnan equilibrium\");\n\tREGISTER_FECORE_CLASS(FEEFDNeoHookean, \"EFD neo-Hookean (new)\");\n\tREGISTER_FECORE_CLASS(FEEFDNeoHookeanOld, \"EFD neo-Hookean\");\n\tREGISTER_FECORE_CLASS(FEEllipsoidalFiberDistribution, \"ellipsoidal fiber distribution\");\n\tREGISTER_FECORE_CLASS(FEEllipsoidalFiberDistributionOld, \"ellipsoidal fiber distribution (old)\");\n\tREGISTER_FECORE_CLASS(FEFungOrthoCompressible, \"Fung-ortho-compressible\");\n\tREGISTER_FECORE_CLASS(FECompressibleGentMaterial, \"compressible Gent\");\n\tREGISTER_FECORE_CLASS(FEHolmesMow, \"Holmes-Mow\");\n    REGISTER_FECORE_CLASS(FEHolmesMowUC, \"uncoupled Holmes-Mow\");\n    REGISTER_FECORE_CLASS(FEHolzapfelUnconstrained, \"HGO unconstrained\");\n\tREGISTER_FECORE_CLASS(FEIsotropicElastic, \"isotropic elastic\");\n    REGISTER_FECORE_CLASS(FEIsoHencky, \"isotropic Hencky\");\n\tREGISTER_FECORE_CLASS(FECoupledMooneyRivlin, \"coupled Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FECoupledVerondaWestmann, \"coupled Veronda-Westmann\");\n\tREGISTER_FECORE_CLASS(FENaturalNeoHookean, \"natural neo-Hookean\");\n\tREGISTER_FECORE_CLASS(FENeoHookean, \"neo-Hookean\");\n\tREGISTER_FECORE_CLASS(FENeoHookeanAD, \"neo-Hookean AD\");\n\tREGISTER_FECORE_CLASS(FENeoHookeanTransIso, \"neo-Hookean transiso\");\n    REGISTER_FECORE_CLASS(FETraceFreeNeoHookean, \"trace-free neo-Hookean\");\n\tREGISTER_FECORE_CLASS(FENewtonianViscousSolid, \"Newtonian viscous solid\");\n\tREGISTER_FECORE_CLASS(FEOgdenUnconstrained, \"Ogden unconstrained\");\n\tREGISTER_FECORE_CLASS(FEOrthoElastic, \"orthotropic elastic\");\n\tREGISTER_FECORE_CLASS(FEOrthotropicCLE, \"orthotropic CLE\");\n\tREGISTER_FECORE_CLASS(FEOsmoticVirialExpansion, \"osmotic virial expansion\");\n\tREGISTER_FECORE_CLASS(FEPerfectOsmometer, \"perfect osmometer\");\n\tREGISTER_FECORE_CLASS(FESphericalFiberDistribution, \"spherical fiber distribution\");\n\tREGISTER_FECORE_CLASS(FEStVenantKirchhoff, \"St.Venant-Kirchhoff\");\n    REGISTER_FECORE_CLASS(FEViscoElasticDamage, \"viscoelastic damage\");\n\tREGISTER_FECORE_CLASS(FEViscoElasticMaterial, \"viscoelastic\");\n\tREGISTER_FECORE_CLASS(FEElasticMultigeneration, \"multigeneration\");\n\tREGISTER_FECORE_CLASS(FERemodelingElasticMaterial, \"remodeling solid\");\n\tREGISTER_FECORE_CLASS(FECarterHayesOld, \"Carter-Hayes (old)\");\n\tREGISTER_FECORE_CLASS(FEContinuousFiberDistribution, \"continuous fiber distribution\");\n    REGISTER_FECORE_CLASS(FEODFFiberDistribution, \"fiberODF\");\n\tREGISTER_FECORE_CLASS(FECoupledTransIsoVerondaWestmann, \"coupled trans-iso Veronda-Westmann\");\n\tREGISTER_FECORE_CLASS(FECoupledTransIsoMooneyRivlin, \"coupled trans-iso Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FEGenericHyperelastic, \"hyperelastic\");\n\tREGISTER_FECORE_CLASS(FEGenericTransIsoHyperelastic, \"trans-iso hyperelastic\");\n\tREGISTER_FECORE_CLASS(FEDamageFiberPower, \"damage fiber power\");\n\tREGISTER_FECORE_CLASS(FEDamageFiberExponential, \"damage fiber exponential\");\n\tREGISTER_FECORE_CLASS(FEDamageFiberExpLinear, \"damage fiber exp-linear\");\n\tREGISTER_FECORE_CLASS(FEGenerationMaterial, \"generation\");\n\tREGISTER_FECORE_CLASS(FEHGOCoronary, \"HGO-coronary\");\n    REGISTER_FECORE_CLASS(FELungMaterial, \"lung\");\n    REGISTER_FECORE_CLASS(FEKinematicGrowth, \"kinematic growth\");\n    REGISTER_FECORE_CLASS(FEScaledElasticMaterial, \"scaled elastic\");\n\n\t// These materials are derived from FEElasticMaterial and use FEElasticMaterials\n\tREGISTER_FECORE_CLASS(FEElasticMixture, \"solid mixture\");\n\tREGISTER_FECORE_CLASS(FEReactiveViscoelasticMaterial, \"reactive viscoelastic\");\n\tREGISTER_FECORE_CLASS(FEDamageMaterial, \"elastic damage\");\n\tREGISTER_FECORE_CLASS(FERVEDamageMaterial, \"reactive viscoelastic damage\");\n\tREGISTER_FECORE_CLASS(FEReactiveFatigue, \"reactive fatigue\");\n    REGISTER_FECORE_CLASS(FERVEFatigueMaterial, \"reactive viscoelastic fatigue\");\n\tREGISTER_FECORE_CLASS(FEReactivePlasticity, \"reactive plasticity\");\n\tREGISTER_FECORE_CLASS(FEReactivePlasticDamage, \"reactive plastic damage\");\n\n\t// Uncoupled elastic materials (derived from FEUncoupledMaterial)\n\tREGISTER_FECORE_CLASS(FEArrudaBoyce, \"Arruda-Boyce\");\n\tREGISTER_FECORE_CLASS(FE2DTransIsoMooneyRivlin, \"2D trans iso Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FE2DTransIsoVerondaWestmann, \"2D trans iso Veronda-Westmann\");\n\tREGISTER_FECORE_CLASS(FEDamageMooneyRivlin, \"damage Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FEDamageTransIsoMooneyRivlin, \"damage trans iso Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FEEFDMooneyRivlin, \"EFD Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FEEFDUncoupled, \"EFD uncoupled\");\n\tREGISTER_FECORE_CLASS(FEEFDVerondaWestmann, \"EFD Veronda-Westmann\");\n\tREGISTER_FECORE_CLASS(FEFungOrthotropic, \"Fung orthotropic\");\n\tREGISTER_FECORE_CLASS(FEHolzapfelGasserOgden, \"Holzapfel-Gasser-Ogden\");\n\tREGISTER_FECORE_CLASS(FEGentMaterial, \"Gent\");\n\tREGISTER_FECORE_CLASS(FEIncompNeoHookean, \"incomp neo-Hookean\");\n\tREGISTER_FECORE_CLASS(FEMooneyRivlin, \"Mooney-Rivlin\");\n\tREGISTER_FECORE_CLASS(FEMooneyRivlinAD, \"Mooney-Rivlin AD\");\n\tREGISTER_FECORE_CLASS(FEMuscleMaterial, \"muscle material\");\n\tREGISTER_FECORE_CLASS(FENewtonianViscousSolidUC, \"Newtonian viscous solid uncoupled\");\n\tREGISTER_FECORE_CLASS(FEOgdenMaterial, \"Ogden\");\n\tREGISTER_FECORE_CLASS(FETCNonlinearOrthotropic, \"TC nonlinear orthotropic\");\n\tREGISTER_FECORE_CLASS(FETendonMaterial, \"tendon material\");\n\tREGISTER_FECORE_CLASS(FETransIsoMooneyRivlin, \"trans iso Mooney-Rivlin\");\n    REGISTER_FECORE_CLASS(FETransIsoMREstrada, \"trans iso MR-Estrada\");\n\tREGISTER_FECORE_CLASS(FETransIsoVerondaWestmann, \"trans iso Veronda-Westmann\");\n\tREGISTER_FECORE_CLASS(FEUncoupledElasticMixture, \"uncoupled solid mixture\");\n\tREGISTER_FECORE_CLASS(FEVerondaWestmann, \"Veronda-Westmann\");\n    REGISTER_FECORE_CLASS(FEUncoupledViscoElasticDamage, \"uncoupled viscoelastic damage\");\n\tREGISTER_FECORE_CLASS(FEUncoupledViscoElasticMaterial, \"uncoupled viscoelastic\");\n\tREGISTER_FECORE_CLASS(FEMRVonMisesFibers, \"Mooney-Rivlin von Mises Fibers\");\n\tREGISTER_FECORE_CLASS(FEUncoupledActiveContraction, \"uncoupled active contraction\");\n\tREGISTER_FECORE_CLASS(FEContinuousFiberDistributionUC, \"continuous fiber distribution uncoupled\");\n\tREGISTER_FECORE_CLASS(FEPRLig, \"PRLig\");\n\tREGISTER_FECORE_CLASS(FEUncoupledReactiveViscoelasticMaterial, \"uncoupled reactive viscoelastic\");\n\tREGISTER_FECORE_CLASS(FEDamageMaterialUC, \"uncoupled elastic damage\");\n    REGISTER_FECORE_CLASS(FEUncoupledReactiveFatigue, \"uncoupled reactive fatigue\");\n\tREGISTER_FECORE_CLASS(FEGenericHyperelasticUC, \"uncoupled hyperelastic\");\n\tREGISTER_FECORE_CLASS(FEGenericTransIsoHyperelasticUC, \"uncoupled trans-iso hyperelastic\");\n\tREGISTER_FECORE_CLASS(FEIsotropicLeeSacks, \"isotropic Lee-Sacks\");\n\tREGISTER_FECORE_CLASS(FEIsotropicLeeSacksUncoupled, \"uncoupled isotropic Lee-Sacks\");\n\tREGISTER_FECORE_CLASS(FEPolynomialHyperElastic, \"polynomial\");\n\tREGISTER_FECORE_CLASS(FEShenoyMaterial, \"Shenoy\");\n\tREGISTER_FECORE_CLASS(FEFiberEFDNeoHookean, \"fiber neo-Hookean\");\n    REGISTER_FECORE_CLASS(FEScaledUncoupledMaterial, \"scaled uncoupled\");\n    REGISTER_FECORE_CLASS(FEYeoh, \"Yeoh\");\n\n\t// fiber materials (derived from FEFiberMaterial)\n    REGISTER_FECORE_CLASS(FEFiberCDF         , \"fiber-CDF\"           );\n\tREGISTER_FECORE_CLASS(FEFiberNH          , \"fiber-NH\"            );\n\tREGISTER_FECORE_CLASS(FEFiberExpPow      , \"fiber-exp-pow\"       );\n\tREGISTER_FECORE_CLASS(FEFiberExpLinear   , \"fiber-exp-linear\"    );\n\tREGISTER_FECORE_CLASS(FEFiberPow         , \"fiber-pow\"           );\n\tREGISTER_FECORE_CLASS(FEFiberPowLinear   , \"fiber-pow-linear\"    );\n\tREGISTER_FECORE_CLASS(FEFiberExpPowLinear, \"fiber-exp-pow-linear\");\n\tREGISTER_FECORE_CLASS(FEFiberNaturalNH   , \"fiber-natural-NH\"    );\n    REGISTER_FECORE_CLASS(FEFiberEntropyChain, \"fiber-entropy-chain\" );\n\n\t// growth materials (derived from FEGrowthTensor) \n    REGISTER_FECORE_CLASS(FEVolumeGrowth     , \"volume growth\"       );\n    REGISTER_FECORE_CLASS(FEAreaGrowth       , \"area growth\"         );\n    REGISTER_FECORE_CLASS(FEFiberGrowth      , \"fiber growth\"        );\n    REGISTER_FECORE_CLASS(FEGeneralGrowth    , \"general growth\"      );\n\n\t// Elastic Fiber materials (derived from FEElasticFiberMaterial)\n    REGISTER_FECORE_CLASS(FEElasticFiberCDF         , \"fiber-CDF\"           );\n\tREGISTER_FECORE_CLASS(FEElasticFiberNH          , \"fiber-NH\"            );\n\tREGISTER_FECORE_CLASS(FEElasticFiberExpPow      , \"fiber-exp-pow\"       );\n\tREGISTER_FECORE_CLASS(FEElasticFiberExpLinear   , \"fiber-exp-linear\"    );\n\tREGISTER_FECORE_CLASS(FEElasticFiberPow         , \"fiber-pow\"           );\n\tREGISTER_FECORE_CLASS(FEElasticFiberPowLinear   , \"fiber-pow-linear\"    );\n\tREGISTER_FECORE_CLASS(FEElasticFiberExpPowLinear, \"fiber-exp-pow-linear\");\n\tREGISTER_FECORE_CLASS(FEElasticFiberNaturalNH   , \"fiber-natural-NH\"    );\n    REGISTER_FECORE_CLASS(FEElasticFiberEntropyChain, \"fiber-entropy-chain\" );\n\n\t// fiber materials for uncoupled formulation (derived from FEFiberMaterialUC)\n    REGISTER_FECORE_CLASS(FEFiberCDFUncoupled  , \"fiber-CDF-uncoupled\"       );\n\tREGISTER_FECORE_CLASS(FEFiberExpLinearUC   , \"uncoupled fiber-exp-linear\");\n\tREGISTER_FECORE_CLASS(FEFiberNHUC          , \"fiber-NH-uncoupled\");\n\tREGISTER_FECORE_CLASS(FEFiberExpPowUC      , \"fiber-exp-pow-uncoupled\");\n\tREGISTER_FECORE_CLASS(FEFiberPowLinearUC   , \"fiber-pow-linear-uncoupled\");\n    REGISTER_FECORE_CLASS(FEFiberEntropyChainUC, \"uncoupled fiber-entropy-chain\");\n\n\t// Uncoupled elastic fiber materials (derived from FEUncoupledFiberMaterial)\n    REGISTER_FECORE_CLASS(FEElasticFiberCDFUncoupled    , \"fiber-CDF-uncoupled\"       );\n\tREGISTER_FECORE_CLASS(FEUncoupledFiberExpLinear     , \"uncoupled fiber-exp-linear\");\n\tREGISTER_FECORE_CLASS(FEUncoupledFiberNH            , \"fiber-NH-uncoupled\");\n\tREGISTER_FECORE_CLASS(FEUncoupledFiberExpPow        , \"fiber-exp-pow-uncoupled\");\n\tREGISTER_FECORE_CLASS(FEUncoupledFiberPowLinear     , \"fiber-pow-linear-uncoupled\");\n    REGISTER_FECORE_CLASS(FEUncoupledFiberKiousis       , \"fiber-Kiousis-uncoupled\");\n    REGISTER_FECORE_CLASS(FEUncoupledFiberEntropyChainUC, \"uncoupled fiber-entropy-chain\");\n\n\t// obsolete fiber materials\n\tREGISTER_FECORE_CLASS(FEFiberExponentialPower, \"fiber-exponential-power-law\");\n\tREGISTER_FECORE_CLASS(FEFiberExponentialPowerUC, \"fiber-exponential-power-law-uncoupled\");\n\n\t// solid materials (derived from FESolidMaterial)\n\tREGISTER_FECORE_CLASS(FERigidMaterial, \"rigid body\");\n\tREGISTER_FECORE_CLASS(FEVonMisesPlasticity, \"von-Mises plasticity\");\n\n\t// Fiber density distributions for CFD materials\n\tREGISTER_FECORE_CLASS(FESphericalFiberDensityDistribution, \"spherical\");\n\tREGISTER_FECORE_CLASS(FEEllipsoidalFiberDensityDistribution, \"ellipsoidal\");\n\tREGISTER_FECORE_CLASS(FEVonMises3DFiberDensityDistribution, \"von-Mises-3d\");\n\tREGISTER_FECORE_CLASS(FEVonMises3DTwoFDDAxisymmetric, \"von-Mises-3d-two-axisym\");\n\tREGISTER_FECORE_CLASS(FECircularFiberDensityDistribution, \"circular\");\n\tREGISTER_FECORE_CLASS(FEEllipticalFiberDensityDistribution, \"elliptical\");\n\tREGISTER_FECORE_CLASS(FEVonMises2DFiberDensityDistribution, \"von-Mises-2d\");\n\tREGISTER_FECORE_CLASS(FEStructureTensorDistribution, \"structure-tensor\");\n\n\t// Fiber distribution integration schemes for CFD materials\n\tREGISTER_FECORE_CLASS(FEFiberIntegrationGauss, \"fibers-3d-gauss\");\n\tREGISTER_FECORE_CLASS(FEFiberIntegrationGeodesic, \"fibers-3d-geodesic\");\n\tREGISTER_FECORE_CLASS(FEFiberIntegrationGaussKronrod, \"fibers-3d-gkt\");\n\tREGISTER_FECORE_CLASS(FEFiberIntegrationTriangle, \"fibers-3d-fei\");\n\tREGISTER_FECORE_CLASS(FEFiberIntegrationTrapezoidal, \"fibers-2d-trapezoidal\");\n\n    // Fiber ODF classes\n    REGISTER_FECORE_CLASS(FEFiberODF, \"fiber-odf\");\n\n\t// Other materials \n\tREGISTER_FECORE_CLASS(FELinearTrussMaterial, \"linear truss\");\n\tREGISTER_FECORE_CLASS(FEHuiskesSupply, \"Huiskes-supply\");\n\tREGISTER_FECORE_CLASS(FEUncoupledActiveFiberContraction, \"uncoupled active_contraction\");\n\tREGISTER_FECORE_CLASS(FEActiveFiberContraction, \"active_contraction\");\n    REGISTER_FECORE_CLASS(FEForceVelocityContraction, \"force-velocity-Estrada\");\n\tREGISTER_FECORE_CLASS(FEWrinkleOgdenMaterial, \"wrinkle Ogden\");\n\tREGISTER_FECORE_CLASS(FEElasticMembrane, \"elastic membrane\");\n\n\t// active contraction materials\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionUniaxial, \"prescribed uniaxial active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionUniaxialUC, \"uncoupled prescribed uniaxial active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionTransIso, \"prescribed trans iso active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionTransIsoUC, \"uncoupled prescribed trans iso active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionIsotropic, \"prescribed isotropic active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionIsotropicUC, \"uncoupled prescribed isotropic active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionFiber, \"prescribed fiber active contraction\");\n\tREGISTER_FECORE_CLASS(FEPrescribedActiveContractionFiberUC, \"uncoupled prescribed fiber active contraction\");\n\n\tREGISTER_FECORE_CLASS(FEActiveFiberStress, \"active fiber stress\");\n\tREGISTER_FECORE_CLASS(FEActiveFiberStressUC, \"uncoupled active fiber stress\");\n\n\t// discrete materials\n\tREGISTER_FECORE_CLASS(FECompositeDiscreteMaterial, \"discrete composite\");\n\tREGISTER_FECORE_CLASS(FELinearSpring, \"linear spring\");\n\tREGISTER_FECORE_CLASS(FETensionOnlyLinearSpring, \"tension-only linear spring\");\n\tREGISTER_FECORE_CLASS(FENonlinearSpringMaterial, \"nonlinear spring\");\n\tREGISTER_FECORE_CLASS(FEExperimentalSpring, \"experimental spring\");\n\tREGISTER_FECORE_CLASS(FEDiscreteContractileMaterial, \"Hill\");\n\tREGISTER_FECORE_CLASS(FETorsionalSpring, \"torsion spring\");\n\n\t// bond relaxation materials (used by reactive visco-elastic materials)\n\tREGISTER_FECORE_CLASS(FEBondRelaxationExponential, \"relaxation-exponential\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationExpDistortion, \"relaxation-exp-distortion\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationExpDistUser, \"relaxation-exp-dist-user\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationFung, \"relaxation-Fung\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationPark, \"relaxation-Park\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationParkDistortion, \"relaxation-Park-distortion\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationParkDistUser, \"relaxation-Park-dist-user\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationPower, \"relaxation-power\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationPowerDistortion, \"relaxation-power-distortion\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationPowerDistUser, \"relaxation-power-dist-user\");\n\tREGISTER_FECORE_CLASS(FEBondRelaxationCarreau, \"relaxation-Carreau\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationProny, \"relaxation-Prony\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationMalkin, \"relaxation-Malkin\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationMalkinDist, \"relaxation-Malkin-distortion\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationMalkinDistUser, \"relaxation-Malkin-dist-user\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationCSexp, \"relaxation-CSexp\");\n    REGISTER_FECORE_CLASS(FEBondRelaxationCSexpDistUser, \"relaxation-CSexp-dist-user\");\n\n    // bond recruitment materials (used by reactive visco-elastic materials)\n    REGISTER_FECORE_CLASS(FEBondRecruitmentUser, \"recruitment user\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentPower, \"recruitment power\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentExp, \"recruitment exponential\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentPoly, \"recruitment polynomial\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentLogNormal, \"recruitment log-normal\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentWeibull, \"recruitment Weibull\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentPQP, \"recruitment quintic\");\n    REGISTER_FECORE_CLASS(FEBondRecruitmentGamma, \"recruitment gamma\");\n\n\t// damage cumulative distribution functions (used by damage materials)\n\tREGISTER_FECORE_CLASS(FEDamageCDFSimo, \"CDF Simo\");\n\tREGISTER_FECORE_CLASS(FEDamageCDFLogNormal, \"CDF log-normal\");\n\tREGISTER_FECORE_CLASS(FEDamageCDFWeibull, \"CDF Weibull\");\n\tREGISTER_FECORE_CLASS(FEDamageCDFStep, \"CDF step\");\n\tREGISTER_FECORE_CLASS(FEDamageCDFPQP, \"CDF quintic\");\n\tREGISTER_FECORE_CLASS(FEDamageCDFGamma, \"CDF gamma\");\n\tREGISTER_FECORE_CLASS(FEDamageCDFUser, \"CDF user\");\n\n\t// damage criterion (used by damage and plastic materials)\n\tREGISTER_FECORE_CLASS(FEDamageCriterionSimo, \"DC Simo\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionSED, \"DC strain energy density\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionSSE, \"DC specific strain energy\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionVMS, \"DC von Mises stress\");\n    REGISTER_FECORE_CLASS(FEDamageCriterionDrucker, \"DC Drucker shear stress\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionMSS, \"DC max shear stress\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionMNS, \"DC max normal stress\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionMNLS, \"DC max normal Lagrange strain\");\n\tREGISTER_FECORE_CLASS(FEDamageCriterionOSS, \"DC octahedral shear strain\");\n    REGISTER_FECORE_CLASS(FEDamageCriterionONS, \"DC octahedral natural strain\");\n    REGISTER_FECORE_CLASS(FEDamageCriterionDruckerPrager, \"DC Drucker-Prager\");\n    REGISTER_FECORE_CLASS(FEDamageCriterionDeshpandeFleck, \"DC Deshpande-Fleck\");\n\n    // plastic flow curve (used by plastic materials)\n    REGISTER_FECORE_CLASS(FEPlasticFlowCurvePaper, \"PFC paper\");\n    REGISTER_FECORE_CLASS(FEPlasticFlowCurveUser , \"PFC user\");\n    REGISTER_FECORE_CLASS(FEPlasticFlowCurveMath , \"PFC math\");\n\n\t// prestrain materials\n\tREGISTER_FECORE_CLASS(FEPrestrainElastic, \"prestrain elastic\");\n\tREGISTER_FECORE_CLASS(FEPreStrainUncoupledElastic, \"uncoupled prestrain elastic\");\n\tREGISTER_FECORE_CLASS(FEConstPrestrainGradient, \"prestrain gradient\");\n\tREGISTER_FECORE_CLASS(FEInSituStretchGradient, \"in-situ stretch\");\n\n\t// beam materials\n\tREGISTER_FECORE_CLASS(FEElasticBeamMaterial, \"linear-beam\");\n\n\t//-----------------------------------------------------------------------------\n\t// domain classes\n\tREGISTER_FECORE_CLASS(FERigidSolidDomain, \"rigid-solid\");\n\tREGISTER_FECORE_CLASS(FERigidShellDomain, \"rigid-shell\");\n\tREGISTER_FECORE_CLASS(FERigidShellDomainOld, \"rigid-shell-old\");\n\tREGISTER_FECORE_CLASS(FERemodelingElasticDomain, \"remodeling-solid\");\n\tREGISTER_FECORE_CLASS(FE3FieldElasticSolidDomain, \"three-field-solid\");\n\tREGISTER_FECORE_CLASS(FE3FieldElasticShellDomain, \"three-field-shell\");\n\tREGISTER_FECORE_CLASS(FEUDGHexDomain, \"udg-hex\");\n\tREGISTER_FECORE_CLASS(FESRIElasticSolidDomain, \"sri-solid\");\n\tREGISTER_FECORE_CLASS(FEUT4Domain, \"ut4-solid\");\n\tREGISTER_FECORE_CLASS(FEStandardElasticSolidDomain, \"elastic-solid\");\n\tREGISTER_FECORE_CLASS(FEElasticShellDomain, \"elastic-shell\");\n\tREGISTER_FECORE_CLASS(FEElasticShellDomainOld, \"elastic-shell-old\");\n\tREGISTER_FECORE_CLASS(FEElasticEASShellDomain, \"elastic-shell-eas\");\n\tREGISTER_FECORE_CLASS(FEElasticANSShellDomain, \"elastic-shell-ans\");\n\tREGISTER_FECORE_CLASS(FELinearTrussDomain, \"linear-truss\");\n\tREGISTER_FECORE_CLASS(FEElasticTrussDomain, \"elastic-truss\");\n\tREGISTER_FECORE_CLASS(FEElasticBeamDomain, \"linear-beam\");\n\tREGISTER_FECORE_CLASS(FEDiscreteElasticDomain, \"discrete\");\n\tREGISTER_FECORE_CLASS(FEDeformableSpringDomain, \"deformable-spring\");\n\tREGISTER_FECORE_CLASS(FEDeformableSpringDomain2, \"deformable-spring2\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEBoundaryCondition\n\tREGISTER_FECORE_CLASS(FEFixedDisplacement           , \"zero displacement\");\n\tREGISTER_FECORE_CLASS(FEFixedRotation               , \"zero rotation\");\n\tREGISTER_FECORE_CLASS(FEFixedShellDisplacement      , \"zero shell displacement\");\n\tREGISTER_FECORE_CLASS(FEPrescribedDisplacement      , \"prescribed displacement\");\n\tREGISTER_FECORE_CLASS(FEPrescribedRotation          , \"prescribed rotation\");\n\tREGISTER_FECORE_CLASS(FEPrescribedShellDisplacement , \"prescribed shell displacement\");\n\tREGISTER_FECORE_CLASS(FEBCPrescribedDeformation     , \"prescribed deformation\");\n\tREGISTER_FECORE_CLASS(FEPrescribedNormalDisplacement, \"normal displacement\");\n\tREGISTER_FECORE_CLASS(FEBCRigidDeformation          , \"rigid deformation\");\n\tREGISTER_FECORE_CLASS(FERigidNodeSet                , \"rigid\");\n\tREGISTER_FECORE_CLASS(FEScriptedDisplacementBC      , \"displacement script\", FECORE_EXPERIMENTAL);\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEInitialCondition\n    REGISTER_FECORE_CLASS(FEInitialDisplacement, \"displacement\");\n\tREGISTER_FECORE_CLASS(FEInitialVelocity, \"velocity\");\n\tREGISTER_FECORE_CLASS(FEInitialShellVelocity, \"shell velocity\");\n\tREGISTER_FECORE_CLASS(FEInitialPreStrain, \"prestrain\");\n\tREGISTER_FECORE_CLASS(FEInitialRigidKinematics, \"rigid kinematics\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FENodalLoad\n\tREGISTER_FECORE_CLASS(FENodalForce, \"nodal_force\");\n\tREGISTER_FECORE_CLASS(FENodalTargetForce, \"nodal_target_force\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FESurfaceLoad\n\tREGISTER_FECORE_CLASS(FEPressureLoad, \"pressure\");\n\tREGISTER_FECORE_CLASS(FETractionLoad, \"traction\");\n\tREGISTER_FECORE_CLASS(FESurfaceForceUniform, \"force\");\n\tREGISTER_FECORE_CLASS(FEBearingLoad, \"bearing load\");\n\tREGISTER_FECORE_CLASS(FEIdealGasPressure, \"ideal gas pressure\");\n    REGISTER_FECORE_CLASS(FEPressureRobinBC, \"pressure Robin BC\", FECORE_EXPERIMENTAL);\n    REGISTER_FECORE_CLASS(FETractionRobinBC, \"traction Robin BC\", FECORE_EXPERIMENTAL);\n    REGISTER_FECORE_CLASS(FEPipetteAspiration, \"pipette aspiration\");\n\n\tREGISTER_FECORE_CLASS(FEScriptedPressureLoad, \"pressure script\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEScriptedTractionLoad, \"traction script\", FECORE_EXPERIMENTAL);\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEBodyForce\n\tREGISTER_FECORE_CLASS(FEConstBodyForceOld, \"const\");\t// obsolete in 3.0\n\tREGISTER_FECORE_CLASS(FENonConstBodyForceOld, \"non-const\");\t// obsolete in 3.0\n\n\tREGISTER_FECORE_CLASS(FEGenericBodyForce, \"body force\");\n\tREGISTER_FECORE_CLASS(FECentrifugalBodyForce, \"centrifugal\");\n\tREGISTER_FECORE_CLASS(FEPointBodyForce, \"point\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FESurfaceAttractionBodyForce, \"surface attraction\");\n\tREGISTER_FECORE_CLASS(FEMassDamping, \"mass damping\");\n\tREGISTER_FECORE_CLASS(FEMovingFrameLoad, \"moving frame\");\n\tREGISTER_FECORE_CLASS(FERadialBodyForce, \"radial force\");\n\tREGISTER_FECORE_CLASS(FEAxialBodyForce, \"axial force\");\n\n\tREGISTER_FECORE_CLASS(FEScriptedBodyForce, \"body force script\", FECORE_EXPERIMENTAL);\n\n\t//-----------------------------------------------------------------------------\n\t// constraint classes\n\tREGISTER_FECORE_CLASS(FEPointConstraint, \"point\");\n\tREGISTER_FECORE_CLASS(FESymmetryPlane, \"symmetry plane\");\n\tREGISTER_FECORE_CLASS(FERigidJoint, \"rigid joint\");\n\tREGISTER_FECORE_CLASS(FEGenericRigidJoint, \"generic rigid joint\");\n\tREGISTER_FECORE_CLASS(FERigidSphericalJoint, \"rigid spherical joint\");\n\tREGISTER_FECORE_CLASS(FERigidRevoluteJoint, \"rigid revolute joint\");\n\tREGISTER_FECORE_CLASS(FERigidPrismaticJoint, \"rigid prismatic joint\");\n\tREGISTER_FECORE_CLASS(FERigidCylindricalJoint, \"rigid cylindrical joint\");\n\tREGISTER_FECORE_CLASS(FERigidPlanarJoint, \"rigid planar joint\");\n\tREGISTER_FECORE_CLASS(FERigidLock, \"rigid lock\");\n\tREGISTER_FECORE_CLASS(FERigidSpring, \"rigid spring\");\n\tREGISTER_FECORE_CLASS(FERigidDamper, \"rigid damper\");\n\tREGISTER_FECORE_CLASS(FERigidAngularDamper, \"rigid angular damper\");\n\tREGISTER_FECORE_CLASS(FERigidContractileForce, \"rigid contractile force\");\n\tREGISTER_FECORE_CLASS(FEVolumeConstraint, \"volume\");\n\tREGISTER_FECORE_CLASS(FEDiscreteContact, \"discrete contact\");\n\tREGISTER_FECORE_CLASS(FEDiscreteContact2, \"discrete contact2\");\n\tREGISTER_FECORE_CLASS(FEDistanceConstraint, \"node distance\");\n\tREGISTER_FECORE_CLASS(FEGPAConstraint, \"prestrain\");\n\tREGISTER_FECORE_CLASS(FEInSituStretchConstraint, \"in-situ stretch\");\n\tREGISTER_FECORE_CLASS(FEAzimuthConstraint, \"azimuth constraint\");\n    REGISTER_FECORE_CLASS(FEFixedNormalDisplacement, \"fixed normal displacement\");\n\n\t// Lagrange multiplier constraints\n\tREGISTER_FECORE_CLASS(FENodeToNodeConstraint, \"node-on-node\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEContactInterface\n\n\tREGISTER_FECORE_CLASS(FESlidingInterface, \"sliding-node-on-facet\");\n\tREGISTER_FECORE_CLASS(FEFacet2FacetSliding, \"sliding-facet-on-facet\");\n\tREGISTER_FECORE_CLASS(FESlidingElasticInterface, \"sliding-elastic\");\n\tREGISTER_FECORE_CLASS(FETiedInterface, \"tied-node-on-facet\");\n\tREGISTER_FECORE_CLASS(FEFacet2FacetTied, \"tied-facet-on-facet\");\n\tREGISTER_FECORE_CLASS(FETiedElasticInterface, \"tied-elastic\");\n\tREGISTER_FECORE_CLASS(FEPeriodicBoundary, \"periodic boundary\");\n\tREGISTER_FECORE_CLASS(FERigidWallInterface, \"rigid_wall\");\n\tREGISTER_FECORE_CLASS(FEPeriodicSurfaceConstraint, \"surface constraint\");\n\tREGISTER_FECORE_CLASS(FEStickyInterface, \"sticky\");\n\tREGISTER_FECORE_CLASS(FEMortarSlidingContact, \"mortar-sliding\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEMortarTiedContact, \"mortar-tied\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEContactPotential, \"contact potential\");\n\t\n\tREGISTER_FECORE_CLASS(FEEdgeToSurfaceContactPotential, \"edge-to-surface contact potential\");\n\tREGISTER_FECORE_CLASS(FEEdgeToSurfaceSlidingContact, \"edge-to-surface sliding contact\");\n\n\tREGISTER_FECORE_CLASS(FETiedLineConstraint, \"tied-line\");\n\tREGISTER_FECORE_CLASS(FESlideLineConstraint, \"slide-line\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived directly from FERigidBC\n\tREGISTER_FECORE_CLASS(FERigidFixedBCNew     , \"rigid_fixed\"           );\n\tREGISTER_FECORE_CLASS(FERigidDisplacement   , \"rigid_displacement\"    );\n\tREGISTER_FECORE_CLASS(FERigidRotation       , \"rigid_rotation\"        );\n\tREGISTER_FECORE_CLASS(FERigidRotationVector , \"rigid_rotation_vector\" );\n\tREGISTER_FECORE_CLASS(FERigidEulerAngles    , \"rigid_euler_angles\"    );\n\n\tREGISTER_FECORE_CLASS(FERigidFixedBCOld     , \"rigid_fixed_old\"     , FECORE_DEPRECATED);\t// obsolete in 4.0\n\tREGISTER_FECORE_CLASS(FERigidPrescribedOld  , \"rigid_prescribed_old\", FECORE_DEPRECATED);\t// obsolete in 4.0\n\t\n\t// classes derived directly from FERigidIC\n\tREGISTER_FECORE_CLASS(FERigidBodyVelocity       , \"initial_rigid_velocity\"        );\n\tREGISTER_FECORE_CLASS(FERigidBodyAngularVelocity, \"initial_rigid_angular_velocity\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived directly from FERigidLoad\n\tREGISTER_FECORE_CLASS(FERigidAxialForce    , \"rigid_axial_force\"    );\n\tREGISTER_FECORE_CLASS(FERigidBodyForce     , \"rigid_force\"          );\n\tREGISTER_FECORE_CLASS(FERigidBodyMoment    , \"rigid_moment\"         );\n    REGISTER_FECORE_CLASS(FERigidFollowerForce , \"rigid_follower_force\" );\n    REGISTER_FECORE_CLASS(FERigidFollowerMoment, \"rigid_follower_moment\");\n\tREGISTER_FECORE_CLASS(FERigidCable         , \"rigid_cable\");\n\tREGISTER_FECORE_CLASS(FERigidCablePoint\t   , \"rigid_cable_point\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEPlotData\n\tREGISTER_FECORE_CLASS(FEPlotElementVelocity, \"velocity\");\n\tREGISTER_FECORE_CLASS(FEPlotElementAcceleration, \"acceleration\");\n\tREGISTER_FECORE_CLASS(FEPlotDensity, \"density\");\n\tREGISTER_FECORE_CLASS(FEPlotElementStress, \"stress\");\n\tREGISTER_FECORE_CLASS(FEPlotElementPK2Stress, \"PK2 stress\");\n\tREGISTER_FECORE_CLASS(FEPlotElementPK1Stress, \"PK1 stress\");\n\tREGISTER_FECORE_CLASS(FEPlotElementMixtureStress, \"mixture stress\");\n    REGISTER_FECORE_CLASS(FEPlotElementPlasticYieldStress, \"plastic yield stress\");\n    REGISTER_FECORE_CLASS(FEPlotElementDruckerShear, \"Drucker shear stress\");\n    REGISTER_FECORE_CLASS(FEPlotElementPragerDruckerStress, \"Drucker-Prager stress\");\n    REGISTER_FECORE_CLASS(FEPlotElementDeshpandeFleckStress, \"Deshpande-Fleck stress\");\n    REGISTER_FECORE_CLASS(FEPlotMixtureStrainEnergyDensity, \"mixture strain energy density\");\n    REGISTER_FECORE_CLASS(FEPlotMixtureDevStrainEnergyDensity, \"mixture deviatoric strain energy density\");\n    REGISTER_FECORE_CLASS(FEPlotMixtureSpecificStrainEnergy, \"mixture specific strain energy\");\n\tREGISTER_FECORE_CLASS(FEPlotElementUncoupledPressure, \"uncoupled pressure\");\n\tREGISTER_FECORE_CLASS(FEPlotElementElasticity, \"elasticity\");\n    REGISTER_FECORE_CLASS(FEPlotElementDevElasticity, \"deviatoric elasticity\");\n\tREGISTER_FECORE_CLASS(FEPlotRelativeVolume, \"relative volume\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRRelativeVolume, \"SPR relative volume\");\n\tREGISTER_FECORE_CLASS(FEPlotShellRelativeVolume, \"shell relative volume\");// , FECORE_SPEC(3, 0)); // NOTE: deprecated\n\tREGISTER_FECORE_CLASS(FEPlotFiberVector, \"fiber vector\");\n\tREGISTER_FECORE_CLASS(FEPlotFiberStretch, \"fiber stretch\");\n\tREGISTER_FECORE_CLASS(FEPlotDevFiberStretch, \"deviatoric fiber stretch\");\n\tREGISTER_FECORE_CLASS(FEPlotMaterialAxes, \"material axes\");\n\tREGISTER_FECORE_CLASS(FEPlotShellThickness, \"shell thickness\");\n\tREGISTER_FECORE_CLASS(FEPlotShellDirector, \"shell director\");\n    REGISTER_FECORE_CLASS(FEPlotNodalShellDirector, \"nodal shell director\");\n\tREGISTER_FECORE_CLASS(FEPlotDamage, \"damage\");\n\tREGISTER_FECORE_CLASS(FEPlotIntactBondFraction, \"intact bond fraction\");\n    REGISTER_FECORE_CLASS(FEPlotFatigueBondFraction, \"fatigue bond fraction\");\n\tREGISTER_FECORE_CLASS(FEPlotYieldedBondFraction, \"yielded bond fraction\");\n\tREGISTER_FECORE_CLASS(FEPlotOctahedralPlasticStrain, \"octahedral plastic strain\");\n\tREGISTER_FECORE_CLASS(FEPlotReactivePlasticityHeatSupply, \"plasticity heat supply density\");\n\tREGISTER_FECORE_CLASS(FEPlotMixtureVolumeFraction, \"volume fraction\");\n\tREGISTER_FECORE_CLASS(FEPlotUT4NodalStresses, \"ut4 nodal stress\");\n\tREGISTER_FECORE_CLASS(FEPlotContactGap, \"contact gap\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalContactGap, \"nodal contact gap\");\n\tREGISTER_FECORE_CLASS(FEPlotVectorGap, \"vector gap\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalVectorGap, \"nodal vector gap\");\n\tREGISTER_FECORE_CLASS(FEPlotContactPressure, \"contact pressure\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalContactPressure, \"nodal contact pressure\");\n\tREGISTER_FECORE_CLASS(FEPlotContactTraction, \"contact traction\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalContactTraction, \"nodal contact traction\");\n\tREGISTER_FECORE_CLASS(FEPlotStickStatus, \"contact stick\");\n\tREGISTER_FECORE_CLASS(FEPlotContactForce, \"contact force\");\n\tREGISTER_FECORE_CLASS(FEPlotContactArea, \"contact area\");\n\tREGISTER_FECORE_CLASS(FEPlotContactPenalty, \"contact penalty\");\n\tREGISTER_FECORE_CLASS(FEPlotContactStatus, \"contact status\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRStresses, \"SPR stress\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRLinearStresses, \"SPR-P1 stress\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRPrincStresses, \"SPR principal stress\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalStresses, \"nodal stress\");\n    REGISTER_FECORE_CLASS(FEPlotShellTopStress, \"shell top stress\");\n    REGISTER_FECORE_CLASS(FEPlotShellBottomStress, \"shell bottom stress\");\n    REGISTER_FECORE_CLASS(FEPlotShellTopNodalStresses, \"shell top nodal stress\");\n    REGISTER_FECORE_CLASS(FEPlotShellBottomNodalStresses, \"shell bottom nodal stress\");\n    REGISTER_FECORE_CLASS(FEPlotNodalStrains, \"nodal strain\");\n    REGISTER_FECORE_CLASS(FEPlotShellTopNodalStrains, \"shell top nodal strain\");\n    REGISTER_FECORE_CLASS(FEPlotShellBottomNodalStrains, \"shell bottom nodal strain\");\n    REGISTER_FECORE_CLASS(FEPlotShellTopStrain, \"shell top strain\");\n    REGISTER_FECORE_CLASS(FEPlotShellBottomStrain, \"shell bottom strain\");\n\tREGISTER_FECORE_CLASS(FEPlotShellStrain, \"shell strain\");\n\tREGISTER_FECORE_CLASS(FEPlotDeformationGradient, \"deformation gradient\");\n\tREGISTER_FECORE_CLASS(FEPlotLagrangeStrain, \"Lagrange strain\");\n\tREGISTER_FECORE_CLASS(FEPlotInfStrain, \"infinitesimal strain\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRLagrangeStrain, \"SPR Lagrange strain\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRInfStrain, \"SPR infinitesimal strain\");\n    REGISTER_FECORE_CLASS(FEPlotAlmansiStrain, \"Almansi strain\");\n    REGISTER_FECORE_CLASS(FEPlotRightCauchyGreen, \"right Cauchy-Green\");\n    REGISTER_FECORE_CLASS(FEPlotLeftCauchyGreen, \"left Cauchy-Green\");\n    REGISTER_FECORE_CLASS(FEPlotRightStretch, \"right stretch\");\n    REGISTER_FECORE_CLASS(FEPlotLeftStretch, \"left stretch\");\n    REGISTER_FECORE_CLASS(FEPlotRightHencky, \"right Hencky\");\n    REGISTER_FECORE_CLASS(FEPlotLeftHencky, \"left Hencky\");\n    REGISTER_FECORE_CLASS(FEPlotRateOfDeformation, \"rate of deformation\");\n\tREGISTER_FECORE_CLASS(FEPlotMortarContactGap, \"mortar-gap\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEPlotSurfaceTraction, \"surface traction\");\n\tREGISTER_FECORE_CLASS(FEPlotNodalSurfaceTraction, \"nodal surface traction\");\n\tREGISTER_FECORE_CLASS(FEPlotEnclosedVolume, \"enclosed volume\");\n    REGISTER_FECORE_CLASS(FEPlotEnclosedVolumeChange, \"enclosed volume change\");\n\tREGISTER_FECORE_CLASS(FEPlotSurfaceArea, \"surface area\");\n\tREGISTER_FECORE_CLASS(FEPlotFacetArea, \"facet area\");\n\tREGISTER_FECORE_CLASS(FEPlotStrainEnergyDensity, \"strain energy density\");\n\tREGISTER_FECORE_CLASS(FEPlotDevStrainEnergyDensity, \"deviatoric strain energy density\");\n\tREGISTER_FECORE_CLASS(FEPlotSpecificStrainEnergy, \"specific strain energy\");\n\tREGISTER_FECORE_CLASS(FEPlotKineticEnergyDensity, \"kinetic energy density\");\n\tREGISTER_FECORE_CLASS(FEPlotElementStrainEnergy, \"element strain energy\");\n\tREGISTER_FECORE_CLASS(FEPlotElementKineticEnergy, \"element kinetic energy\");\n\tREGISTER_FECORE_CLASS(FEPlotElementCenterOfMass, \"element center of mass\");\n\tREGISTER_FECORE_CLASS(FEPlotElementLinearMomentum, \"element linear momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotElementAngularMomentum, \"element angular momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotElementStressPower, \"element stress power\");\n\tREGISTER_FECORE_CLASS(FEPlotCurrentElementStrainEnergy, \"current element strain energy\");\n\tREGISTER_FECORE_CLASS(FEPlotCurrentElementKineticEnergy, \"current element kinetic energy\");\n\tREGISTER_FECORE_CLASS(FEPlotCurrentElementCenterOfMass, \"current element center of mass\");\n\tREGISTER_FECORE_CLASS(FEPlotCurrentElementLinearMomentum, \"current element linear momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotCurrentElementAngularMomentum, \"current element angular momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotNodeDisplacement, \"displacement\");\n\tREGISTER_FECORE_CLASS(FEPlotNodeIncrementalDisplacement, \"incremental displacement\");\n\tREGISTER_FECORE_CLASS(FEPlotNodeRotation, \"rotation\");\n    REGISTER_FECORE_CLASS(FEPlotNodeShellDisplacement, \"shell displacement\");\n\tREGISTER_FECORE_CLASS(FEPlotNodeVelocity, \"nodal velocity\");\n\tREGISTER_FECORE_CLASS(FEPlotNodeAcceleration, \"nodal acceleration\");\n\tREGISTER_FECORE_CLASS(FEPlotNodeReactionForces, \"reaction forces\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidReactionForce, \"rigid force\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidReactionTorque, \"rigid torque\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidDisplacement, \"rigid position\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidVelocity, \"rigid velocity\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidAcceleration, \"rigid acceleration\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidRotation, \"rigid angular position\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidAngularVelocity, \"rigid angular velocity\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidAngularAcceleration, \"rigid angular acceleration\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidLinearMomentum, \"rigid linear momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidAngularMomentum, \"rigid angular momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidKineticEnergy, \"rigid kinetic energy\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidEuler, \"Euler angle\");\n\tREGISTER_FECORE_CLASS(FEPlotRigidRotationVector, \"rigid rotation vector\");\n\tREGISTER_FECORE_CLASS(FEPlotScalarSurfaceLoad, \"scalar surface load\");\n\tREGISTER_FECORE_CLASS(FEPlotNetSurfaceReactionForce, \"surface reaction force\");\n\tREGISTER_FECORE_CLASS(FEPlotNetSurfaceReactionMoment, \"surface reaction moment\");\n\tREGISTER_FECORE_CLASS(FEPlotStressError, \"stress error\");\n\tREGISTER_FECORE_CLASS(FEPlotFiberTargetStretch, \"in-situ target stretch\");\n\tREGISTER_FECORE_CLASS(FEPlotPreStrainStretch, \"prestrain stretch\");\n\tREGISTER_FECORE_CLASS(FEPlotPreStrainStretchError, \"prestrain stretch error\");\n\tREGISTER_FECORE_CLASS(FEPlotPreStrainCorrection, \"prestrain correction\");\n\tREGISTER_FECORE_CLASS(FEPlotSPRPreStrainCorrection, \"SPR prestrain correction\");\n\tREGISTER_FECORE_CLASS(FEPlotPreStrainCompatibility, \"prestrain compatibility\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementStretch, \"discrete element stretch\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementElongation, \"discrete element elongation\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementPercentElongation, \"discrete element percent elongation\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementDirection, \"discrete element direction\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementLength, \"discrete element length\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementForce, \"discrete element force\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementSignedForce, \"discrete element signed force\");\n\tREGISTER_FECORE_CLASS(FEPlotDiscreteElementStrainEnergy, \"discrete element strain energy\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_D    , \"continuous damage\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_D1   , \"continuous damage D1\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_Ds   , \"continuous damage Ds\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_D2   , \"continuous damage D2\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_D3   , \"continuous damage D3\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_P    , \"continuous damage P\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_Psi0 , \"continuous damage Psi0\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_beta , \"continuous damage beta\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_gamma, \"continuous damage gamma\");\n\tREGISTER_FECORE_CLASS(FEPlotContinuousDamage_D2beta, \"continuous damage D2beta\");\n    REGISTER_FECORE_CLASS(FEPlotRVEgenerations, \"RVE generations\");\n    REGISTER_FECORE_CLASS(FEPlotRVEbonds, \"RVE reforming bonds\");\n    REGISTER_FECORE_CLASS(FEPlotRVErecruitment, \"RVE recruitment\");\n    REGISTER_FECORE_CLASS(FEPlotRVEstrain, \"RVE strain\");\n    REGISTER_FECORE_CLASS(FEPlotStrongBondSED, \"strong bond SED\");\n    REGISTER_FECORE_CLASS(FEPlotWeakBondSED, \"weak bond SED\");\n    REGISTER_FECORE_CLASS(FEPlotStrongBondDevSED, \"deviatoric strong bond SED\");\n    REGISTER_FECORE_CLASS(FEPlotWeakBondDevSED, \"deviatoric weak bond SED\");\n    REGISTER_FECORE_CLASS(FEPlotTrussStretch  , \"truss stretch\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthLagrangeStrain, \"growth Lagrange strain\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthInfStrain, \"growth infinitesimal strain\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthRightStretch, \"growth right stretch\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthLeftStretch, \"growth left stretch\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthRightHencky, \"growth right Hencky\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthLeftHencky, \"growth left Hencky\");\n    REGISTER_FECORE_CLASS(FEPlotGrowthRelativeVolume, \"growth relative volume\");\n    REGISTER_FECORE_CLASS(FEPlotIdealGasPressure, \"ideal gas pressure\");\n\tREGISTER_FECORE_CLASS(FEPlotBodyForce, \"body force\");\n\n\tREGISTER_FECORE_CLASS(FEPlotTotalLinearMomentum, \"total linear momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotTotalAngularMomentum, \"total angular momentum\");\n\tREGISTER_FECORE_CLASS(FEPlotTotalEnergy, \"total energy\");\n\n\t// beam variables\n\tREGISTER_FECORE_CLASS(FEPlotBeamStress      , \"beam stress\");\n\tREGISTER_FECORE_CLASS(FEPlotBeamStressCouple, \"beam stress couple\");\n\tREGISTER_FECORE_CLASS(FEPlotBeamStrain      , \"beam strain\");\n\tREGISTER_FECORE_CLASS(FEPlotBeamCurvature   , \"beam curvature\");\n\n\tREGISTER_FECORE_CLASS(FEPlotBeamReferenceStress      , \"beam reference stress\");\n\tREGISTER_FECORE_CLASS(FEPlotBeamReferenceStressCouple, \"beam reference stress couple\");\n\n\tREGISTER_FECORE_CLASS(FEPlotEdgeContactGap, \"edge contact gap\");\n\n\t// 2O continuum fields\n\tREGISTER_FECORE_CLASS(FEPlotElementsnorm, \"s norm\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FELogNodeData\n\tREGISTER_FECORE_CLASS(FENodeXPos, \"x\");\n\tREGISTER_FECORE_CLASS(FENodeYPos, \"y\");\n\tREGISTER_FECORE_CLASS(FENodeZPos, \"z\");\n\tREGISTER_FECORE_CLASS(FENodeXDisp, \"ux\");\n\tREGISTER_FECORE_CLASS(FENodeYDisp, \"uy\");\n\tREGISTER_FECORE_CLASS(FENodeZDisp, \"uz\");\n\tREGISTER_FECORE_CLASS(FENodeXVel, \"vx\");\n\tREGISTER_FECORE_CLASS(FENodeYVel, \"vy\");\n\tREGISTER_FECORE_CLASS(FENodeZVel, \"vz\");\n\tREGISTER_FECORE_CLASS(FENodeXAcc, \"ax\");\n\tREGISTER_FECORE_CLASS(FENodeYAcc, \"ay\");\n\tREGISTER_FECORE_CLASS(FENodeZAcc, \"az\");\n\tREGISTER_FECORE_CLASS(FENodeForceX, \"Rx\");\n\tREGISTER_FECORE_CLASS(FENodeForceY, \"Ry\");\n\tREGISTER_FECORE_CLASS(FENodeForceZ, \"Rz\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FELogFaceData\n\tREGISTER_FECORE_CLASS(FELogContactGap     , \"contact gap\");\n\tREGISTER_FECORE_CLASS(FELogContactPressure, \"contact pressure\");\n\tREGISTER_FECORE_CLASS(FELogContactTractionX, \"contact_traction.x\");\n\tREGISTER_FECORE_CLASS(FELogContactTractionY, \"contact_traction.y\");\n\tREGISTER_FECORE_CLASS(FELogContactTractionZ, \"contact_traction.z\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FELogElemData\n\tREGISTER_FECORE_CLASS(FELogElemPosX, \"x\");\n\tREGISTER_FECORE_CLASS(FELogElemPosY, \"y\");\n\tREGISTER_FECORE_CLASS(FELogElemPosZ, \"z\");\n\tREGISTER_FECORE_CLASS(FELogElemJacobian, \"J\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainX, \"Ex\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainY, \"Ey\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainZ, \"Ez\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainXY, \"Exy\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainYZ, \"Eyz\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainXZ, \"Exz\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainEffective, \"effective strain\");\n\tREGISTER_FECORE_CLASS(FELogElemStrain1, \"E1\");\n\tREGISTER_FECORE_CLASS(FELogElemStrain2, \"E2\");\n\tREGISTER_FECORE_CLASS(FELogElemStrain3, \"E3\");\n\tREGISTER_FECORE_CLASS(FELogElemInfStrainX, \"ex\");\n\tREGISTER_FECORE_CLASS(FELogElemInfStrainY, \"ey\");\n\tREGISTER_FECORE_CLASS(FELogElemInfStrainZ, \"ez\");\n\tREGISTER_FECORE_CLASS(FELogElemInfStrainXY, \"exy\");\n\tREGISTER_FECORE_CLASS(FELogElemInfStrainYZ, \"eyz\");\n\tREGISTER_FECORE_CLASS(FELogElemInfStrainXZ, \"exz\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchX, \"Ux\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchY, \"Uy\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchZ, \"Uz\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchXY, \"Uxy\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchYZ, \"Uyz\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchXZ, \"Uxz\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretchEffective, \"effective right stretch\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretch1, \"U1\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretch2, \"U2\");\n    REGISTER_FECORE_CLASS(FELogElemRightStretch3, \"U3\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchX, \"Vx\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchY, \"Vy\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchZ, \"Vz\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchXY, \"Vxy\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchYZ, \"Vyz\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchXZ, \"Vxz\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretchEffective, \"effective left stretch\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretch1, \"V1\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretch2, \"V2\");\n    REGISTER_FECORE_CLASS(FELogElemLeftStretch3, \"V3\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyX, \"Hx\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyY, \"Hy\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyZ, \"Hz\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyXY, \"Hxy\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyYZ, \"Hyz\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyXZ, \"Hxz\");\n    REGISTER_FECORE_CLASS(FELogElemRightHenckyEffective, \"effective right Hencky\");\n    REGISTER_FECORE_CLASS(FELogElemRightHencky1, \"H1\");\n    REGISTER_FECORE_CLASS(FELogElemRightHencky2, \"H2\");\n    REGISTER_FECORE_CLASS(FELogElemRightHencky3, \"H3\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyX, \"hx\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyY, \"hy\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyZ, \"hz\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyXY, \"hxy\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyYZ, \"hyz\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyXZ, \"hxz\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHenckyEffective, \"effective left Hencky\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHencky1, \"h1\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHencky2, \"h2\");\n    REGISTER_FECORE_CLASS(FELogElemLeftHencky3, \"h3\");\n\tREGISTER_FECORE_CLASS(FELogElemStressX, \"sx\");\n\tREGISTER_FECORE_CLASS(FELogElemStressY, \"sy\");\n\tREGISTER_FECORE_CLASS(FELogElemStressZ, \"sz\");\n\tREGISTER_FECORE_CLASS(FELogElemStressXY, \"sxy\");\n\tREGISTER_FECORE_CLASS(FELogElemStressYZ, \"syz\");\n\tREGISTER_FECORE_CLASS(FELogElemStressXZ, \"sxz\");\n\tREGISTER_FECORE_CLASS(FELogElemStressEffective, \"effective stress\");\n\tREGISTER_FECORE_CLASS(FELogElemStress1, \"s1\");\n\tREGISTER_FECORE_CLASS(FELogElemStress2, \"s2\");\n\tREGISTER_FECORE_CLASS(FELogElemStress3, \"s3\");\n\tREGISTER_FECORE_CLASS(FELogElemPK2StressX , \"Sx\");\n\tREGISTER_FECORE_CLASS(FELogElemPK2StressY , \"Sy\");\n\tREGISTER_FECORE_CLASS(FELogElemPK2StressZ , \"Sz\");\n\tREGISTER_FECORE_CLASS(FELogElemPK2StressXY, \"Sxy\");\n\tREGISTER_FECORE_CLASS(FELogElemPK2StressYZ, \"Syz\");\n\tREGISTER_FECORE_CLASS(FELogElemPK2StressXZ, \"Sxz\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressXX, \"Pxx\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressYY, \"Pyy\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressZZ, \"Pzz\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressXY, \"Pxy\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressYZ, \"Pyz\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressZX, \"Pzx\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressZY, \"Pzy\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressXZ, \"Pxz\");\n    REGISTER_FECORE_CLASS(FELogElemPK1StressYX, \"Pyx\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 0, 0, \"s1x\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 0, 1, \"s1y\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 0, 2, \"s1z\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 1, 0, \"s2x\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 1, 1, \"s2y\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 1, 2, \"s2z\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 2, 0, \"s3x\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 2, 1, \"s3y\");\n\tREGISTER_FECORE_CLASS_T2(FELogElemStressEigenVector_T, 2, 2, \"s3z\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientXX, \"Fxx\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientXY, \"Fxy\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientXZ, \"Fxz\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientYX, \"Fyx\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientYY, \"Fyy\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientYZ, \"Fyz\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientZX, \"Fzx\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientZY, \"Fzy\");\n\tREGISTER_FECORE_CLASS(FELogElemDeformationGradientZZ, \"Fzz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 0, \"cxxxx\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 1, \"cxxyy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 2, \"cyyyy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 3, \"cxxzz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 4, \"cyyzz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 5, \"czzzz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 6, \"cxxxy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 7, \"cyyxy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 8, \"czzxy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 9, \"cxyxy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 10, \"cxxyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 11, \"cyyyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 12, \"czzyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 13, \"cxyyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 14, \"cyzyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 15, \"cxxxz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 16, \"cyyxz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 17, \"czzxz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 18, \"cxyxz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 19, \"cyzxz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemElasticity, 20, \"cxzxz\");\n\tREGISTER_FECORE_CLASS(FELogElemStrainEnergyDensity, \"sed\");\n\tREGISTER_FECORE_CLASS(FELogElemDevStrainEnergyDensity, \"devsed\");\n\tREGISTER_FECORE_CLASS(FELogElemFiberStretch, \"fiber_stretch\");\n\tREGISTER_FECORE_CLASS_T(FELogElemFiberVector_N, 0, \"fiber_x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemFiberVector_N, 1, \"fiber_y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemFiberVector_N, 2, \"fiber_z\");\n\tREGISTER_FECORE_CLASS(FELogDamage, \"D\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 0, \"damage_1\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 1, \"damage_2\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 2, \"damage_3\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 3, \"damage_4\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 4, \"damage_5\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 5, \"damage_6\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 6, \"damage_7\");\n\tREGISTER_FECORE_CLASS_T(FELogDamage_n, 7, \"damage_8\");\n    REGISTER_FECORE_CLASS(FELogIntactBonds, \"wi\");\n    REGISTER_FECORE_CLASS(FELogYieldedBonds, \"wy\");\n    REGISTER_FECORE_CLASS(FELogFatigueBonds, \"wf\");\n    REGISTER_FECORE_CLASS(FELogOctahedralPlasticStrain, \"ops\");\n    REGISTER_FECORE_CLASS(FELogDiscreteElementStretch   , \"discrete element stretch\");\n    REGISTER_FECORE_CLASS(FELogDiscreteElementElongation, \"discrete element elongation\");\n    REGISTER_FECORE_CLASS(FELogDiscreteElementForce     , \"discrete element force\"  );\n    REGISTER_FECORE_CLASS(FELogDiscreteElementForceX    , \"Fde.x\");\n    REGISTER_FECORE_CLASS(FELogDiscreteElementForceY    , \"Fde.y\");\n    REGISTER_FECORE_CLASS(FELogDiscreteElementForceZ    , \"Fde.z\");\n\tREGISTER_FECORE_CLASS(FELogContactArea, \"contact area\");\n\tREGISTER_FECORE_CLASS(FELogMaxContactGap, \"max contact gap\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 0, 0, \"mixture_stress[0].xx\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 0, 1, \"mixture_stress[0].xy\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 0, 2, \"mixture_stress[0].yy\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 0, 3, \"mixture_stress[0].xz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 0, 4, \"mixture_stress[0].yz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 0, 5, \"mixture_stress[0].zz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 1, 0, \"mixture_stress[1].xx\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 1, 1, \"mixture_stress[1].xy\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 1, 2, \"mixture_stress[1].yy\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 1, 3, \"mixture_stress[1].xz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 1, 4, \"mixture_stress[1].yz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 1, 5, \"mixture_stress[1].zz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 2, 0, \"mixture_stress[2].xx\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 2, 1, \"mixture_stress[2].xy\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 2, 2, \"mixture_stress[2].yy\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 2, 3, \"mixture_stress[2].xz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 2, 4, \"mixture_stress[2].yz\");\n\tREGISTER_FECORE_CLASS_T2(FELogElementMixtureStress_T, 2, 5, \"mixture_stress[2].zz\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 0, 0, \"Ft_xx\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 0, 1, \"Ft_xy\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 0, 2, \"Ft_xz\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 1, 0, \"Ft_yx\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 1, 1, \"Ft_yy\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 1, 2, \"Ft_yz\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 2, 0, \"Ft_zx\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 2, 1, \"Ft_zy\");\n\tREGISTER_FECORE_CLASS_T2(FELogTotalDeformationGradient_T, 2, 2, \"Ft_zz\");\n\n\t// derived from FELogDomainData\n\tREGISTER_FECORE_CLASS(FENormalizedInternalEnergy, \"normalized internal energy\");\n\tREGISTER_FECORE_CLASS(FELogTotalEnergy, \"total energy\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FELogObjectData\n\tREGISTER_FECORE_CLASS(FELogRigidBodyPosX, \"x\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyPosY, \"y\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyPosZ, \"z\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyVelX, \"vx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyVelY, \"vy\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyVelZ, \"vz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAccX, \"ax\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAccY, \"ay\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAccZ, \"az\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngPosX, \"thx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngPosY, \"thy\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngPosZ, \"thz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngVelX, \"omx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngVelY, \"omy\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngVelZ, \"omz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngAccX, \"alx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngAccY, \"aly\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyAngAccZ, \"alz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyQuatX, \"qx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyQuatY, \"qy\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyQuatZ, \"qz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyQuatW, \"qw\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR11, \"R11\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR12, \"R12\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR13, \"R13\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR21, \"R21\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR22, \"R22\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR23, \"R23\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR31, \"R31\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR32, \"R32\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyR33, \"R33\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyEulerX, \"EulerX\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyEulerY, \"EulerY\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyEulerZ, \"EulerZ\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyForceX, \"Fx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyForceY, \"Fy\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyForceZ, \"Fz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyTorqueX, \"Mx\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyTorqueY, \"My\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyTorqueZ, \"Mz\");\n\tREGISTER_FECORE_CLASS(FELogRigidBodyKineticEnergy, \"KE\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAwx, \"IHAwx\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAwy, \"IHAwy\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAwz, \"IHAwz\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAwm, \"IHAwm\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAsx, \"IHAsx\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAsy, \"IHAsy\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAsz, \"IHAsz\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyIHAtd, \"IHAtd\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAwx, \"FHAwx\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAwy, \"FHAwy\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAwz, \"FHAwz\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAwm, \"FHAwm\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAsx, \"FHAsx\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAsy, \"FHAsy\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAsz, \"FHAsz\");\n    REGISTER_FECORE_CLASS(FELogRigidBodyFHAtd, \"FHAtd\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAwx, \"rcIHAwx\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAwy, \"rcIHAwy\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAwz, \"rcIHAwz\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAwm, \"rcIHAwm\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAsx, \"rcIHAsx\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAsy, \"rcIHAsy\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAsz, \"rcIHAsz\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorIHAtd, \"rcIHAtd\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAwx, \"rcFHAwx\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAwy, \"rcFHAwy\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAwz, \"rcFHAwz\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAwm, \"rcFHAwm\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAsx, \"rcFHAsx\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAsy, \"rcFHAsy\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAsz, \"rcFHAsz\");\n    REGISTER_FECORE_CLASS(FELogRigidConnectorFHAtd, \"rcFHAtd\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FELogConnectorData\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorForceX, \"RCFx\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorForceY, \"RCFy\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorForceZ, \"RCFz\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorMomentX, \"RCMx\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorMomentY, \"RCMy\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorMomentZ, \"RCMz\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorTranslationX, \"RCx\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorTranslationY, \"RCy\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorTranslationZ, \"RCz\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorRotationX, \"RCthx\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorRotationY, \"RCthy\");\n\tREGISTER_FECORE_CLASS(FELogRigidConnectorRotationZ, \"RCthz\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FELogNLConstraintData\n\tREGISTER_FECORE_CLASS(FELogVolumeConstraint, \"constrained volume\");\n\tREGISTER_FECORE_CLASS(FELogVolumePressure, \"volume pressure\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from DataRecord\n\tREGISTER_FECORE_CLASS(ObjectDataRecord, \"rigid_body_data\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FEMeshAdaptorCriterion\n\tREGISTER_FECORE_CLASS(FEStressCriterion, \"stress\");\n\tREGISTER_FECORE_CLASS(FEDamageAdaptorCriterion, \"damage\");\n    REGISTER_FECORE_CLASS(FERelativeVolumeCriterion, \"relative volume\");\n\tREGISTER_FECORE_CLASS(FESpringForceCriterion, \"spring force\");\n\tREGISTER_FECORE_CLASS(FESpringStretchCriterion, \"spring stretch\");\n\tREGISTER_FECORE_CLASS(FEContactGapCriterion, \"contact gap\");\n\n\t//-----------------------------------------------------------------------------\n\t// Derived from FEElemDataGenerator\n\tREGISTER_FECORE_CLASS(FEDeformationMapGenerator, \"defgrad\");\n\n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioMech/FEBioMechModule.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEBioMech module\n//! This module defines classes for dealing with large deformation structural\n//! mechanics problems. \n\nnamespace FEBioMech\n{\n\t//! Initialize the FEBioMech module\n\tFEBIOMECH_API void InitModule();\n}\n"
  },
  {
    "path": "FEBioMech/FEBioMechPlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMechPlot.h\"\n#include \"FEDamageNeoHookean.h\"\n#include \"FEDamageTransIsoMooneyRivlin.h\"\n#include \"FEKinematicGrowth.h\"\n#include \"FEReactiveMaterialPoint.h\"\n#include \"FEReactiveFatigue.h\"\n#include \"FEReactivePlasticity.h\"\n#include \"FEReactivePlasticDamage.h\"\n#include \"FERemodelingElasticMaterial.h\"\n#include \"FERigidSolidDomain.h\"\n#include \"FERigidShellDomain.h\"\n#include \"FEElasticShellDomainOld.h\"\n#include \"FEElasticEASShellDomain.h\"\n#include \"FEElasticANSShellDomain.h\"\n#include \"FEElasticMixture.h\"\n#include \"FEElasticMultigeneration.h\"\n#include \"FEUT4Domain.h\"\n#include \"FEContactSurface.h\"\n#include \"FERigidBody.h\"\n#include <FECore/FESPRProjection.h>\n#include \"FEUncoupledElasticMixture.h\"\n#include \"FERigidMaterial.h\"\n#include \"FEVolumeConstraint.h\"\n#include \"FEFacet2FacetSliding.h\"\n#include \"FEMortarSlidingContact.h\"\n#include \"FEMechModel.h\"\n#include \"FEPreStrainElastic.h\"\n#include <FECore/writeplot.h>\n#include <FECore/FEDomainParameter.h>\n#include <FECore/FEModel.h>\n#include \"FEDiscreteElasticMaterial.h\"\n#include \"FEDiscreteElasticDomain.h\"\n#include \"FEContinuousElasticDamage.h\"\n#include <FECore/FEMeshAdaptor.h> // for projectToNodes\n#include \"FESlidingInterface.h\"\n#include \"FESlidingElasticInterface.h\"\n#include \"FETiedContactSurface.h\"\n#include \"FEStickyInterface.h\"\n#include \"FEReactiveVEMaterialPoint.h\"\n#include \"FELinearTrussDomain.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FETrussDomain.h>\n#include <FECore/FEElement.h>\n#include <FEBioMech/FEElasticBeamDomain.h>\n#include <FEBioMech/FEElasticBeamMaterial.h>\n#include <FEBioMech/FEEdgeToSurfaceSlidingContact.h>\n#include \"FEIdealGasPressure.h\"\n#include \"FEBodyForce.h\"\n\n//=============================================================================\n//                            N O D E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nbool FEPlotNodeDisplacement::Save(FEMesh& m, FEDataStream& a)\n{\n\tFEModel* fem = GetFEModel();\n\tconst int dof_X = fem->GetDOFIndex(\"x\");\n\tconst int dof_Y = fem->GetDOFIndex(\"y\");\n\tconst int dof_Z = fem->GetDOFIndex(\"z\");\n\n\twriteNodalValues<vec3d>(m, a, [=](const FENode& node) {\n\t\treturn node.get_vec3d(dof_X, dof_Y, dof_Z);\n\t\t});\n\treturn true;\n}\n\nbool FEPlotNodeRotation::Save(FEMesh& m, FEDataStream& a)\n{\n\tFEModel* fem = GetFEModel();\n\tconst int dof_U = fem->GetDOFIndex(\"u\");\n\tconst int dof_V = fem->GetDOFIndex(\"v\");\n\tconst int dof_W = fem->GetDOFIndex(\"w\");\n\n\twriteNodalValues<vec3d>(m, a, [=](const FENode& node) {\n\t\treturn node.get_vec3d(dof_U, dof_V, dof_W);\n\t\t});\n\treturn true;\n}\n\nbool FEPlotNodeShellDisplacement::Save(FEMesh& m, FEDataStream& a)\n{\n    FEModel* fem = GetFEModel();\n    const int dof_SX = fem->GetDOFIndex(\"sx\");\n    const int dof_SY = fem->GetDOFIndex(\"sy\");\n    const int dof_SZ = fem->GetDOFIndex(\"sz\");\n    \n    writeNodalValues<vec3d>(m, a, [=](const FENode& node) {\n        return node.get_vec3d(dof_SX, dof_SY, dof_SZ);\n    });\n    return true;\n}\n\nbool FEPlotNodalShellDirector::Save(FEMesh& m, FEDataStream& a)\n{\n    writeNodalValues<vec3d>(m, a, [=](const FENode& node) {\n        return (node.HasFlags(FENode::SHELL)) ? node.m_dt : vec3d(0,0,0);\n    });\n    return true;\n}\n\n//! Store the nodal incremental displacements\nbool FEPlotNodeIncrementalDisplacement::Save(FEMesh& m, FEDataStream& a)\n{\n\tint N = m.Nodes();\n\tif (m_rp.size() != N)\n\t{\n\t\tm_rp.resize(N);\n\t\tfor (int i = 0; i < N; ++i) m_rp[i] = m.Node(i).m_rt;\n\t}\n\n\t// loop over all nodes\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tvec3d& rt = m.Node(i).m_rt;\n\t\ta << rt - m_rp[i];\n\t\tm_rp[i] = rt;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNodeVelocity::Save(FEMesh& m, FEDataStream& a)\n{\n\tFEModel* fem = GetFEModel();\n\tconst int dof_VX = fem->GetDOFIndex(\"vx\");\n\tconst int dof_VY = fem->GetDOFIndex(\"vy\");\n\tconst int dof_VZ = fem->GetDOFIndex(\"vz\");\n\n\twriteNodalValues<vec3d>(m, a, [=](const FENode& node) {\n\t\treturn node.get_vec3d(dof_VX, dof_VY, dof_VZ);\n\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNodeAcceleration::Save(FEMesh& m, FEDataStream& a)\n{\n\twriteNodalValues<vec3d>(m, a, [](const FENode& node) {\n\t\treturn node.m_at;\n\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store nodal reaction forces\nbool FEPlotNodeReactionForces::Save(FEMesh& m, FEDataStream& a)\n{\n\t// NOTE: Currently, for nodes attached to rigid bodies the reaction forces \n\t//       will actually be the rigid reaction forces. \n\tFEModel& fem = *GetFEModel();\n\tint dofX = fem.GetDOFIndex(\"x\");\n\tint dofY = fem.GetDOFIndex(\"y\");\n\tint dofZ = fem.GetDOFIndex(\"z\");\n\tif ((dofX >= 0) && (dofY >= 0) && (dofZ >= 0))\n\t{\n\t\twriteNodalValues<vec3d>(m, a, [=](const FENode& node) {\n\t\t\treturn node.get_load3(dofX, dofY, dofZ);\n\t\t\t});\n\t\treturn true;\n\t}\n\telse\n\t\treturn false;\n}\n\n//=============================================================================\n//                       S U R F A C E    D A T A\n//=============================================================================\n\nbool FEPlotContactGap::SetFilter(const char* szfilter)\n{\n\tif (szfilter) m_interfaceName = szfilter;\n\treturn (szfilter != nullptr);\n}\n\n// Plot contact gap\nbool FEPlotContactGap::Save(FESurface& surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface();\n\tbool write = false;\n\tif (pci == nullptr) write = true;\n\telse if (pci->IsActive())\n\t{\n\t\tif (m_interfaceName.empty() || (m_interfaceName == pci->GetName())) write = true;\n\t}\n\n\tif (write)\n\t{\n\t\t// NOTE: the sliding surface does not use material points, so we need this little hack. \n\t\tFESlidingSurface* slidingSurface = dynamic_cast<FESlidingSurface*>(pcs);\n\t\tif (slidingSurface)\n\t\t{\n\t\t\tfor (int i = 0; i < slidingSurface->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = slidingSurface->Element(i);\n\t\t\t\tdouble g = 0.0;\n\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble gj = slidingSurface->m_data[el.m_lnode[j]].m_gap;\n\t\t\t\t\tg += gj;\n\t\t\t\t}\n\t\t\t\tg /= el.Nodes();\n\t\t\t\ta << g;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tFETiedContactSurface* tiedSurface = dynamic_cast<FETiedContactSurface*>(pcs);\n\t\tif (tiedSurface)\n\t\t{\n\t\t\tfor (int i = 0; i < tiedSurface->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = tiedSurface->Element(i);\n\t\t\t\tdouble g = 0.0;\n\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble gj = tiedSurface->m_data[el.m_lnode[j]].m_gap;\n\t\t\t\t\tg += gj;\n\t\t\t\t}\n\t\t\t\tg /= el.Nodes();\n\t\t\t\ta << g;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tFEStickySurface* stickySurface = dynamic_cast<FEStickySurface*>(pcs);\n\t\tif (stickySurface)\n\t\t{\n\t\t\tfor (int i = 0; i < stickySurface->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = stickySurface->Element(i);\n\t\t\t\tdouble g = 0.0;\n\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble gj = stickySurface->m_data[el.m_lnode[j]].scalar_gap;\n\t\t\t\t\tg += gj;\n\t\t\t\t}\n\t\t\t\tg /= el.Nodes();\n\t\t\t\ta << g;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\twriteAverageElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n\t\t\tconst FEContactMaterialPoint* pt = dynamic_cast<const FEContactMaterialPoint*>(&mp);\n\t\t\treturn (pt ? pt->m_gap : 0.0);\n\t\t\t});\n\n\t\treturn true;\n\t}\n\telse return false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot vector gap\nbool FEPlotVectorGap::Save(FESurface& surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\twriteElementValue<vec3d>(surf, a, [=](int nface) {\n\t\t\tvec3d gn;\n\t\t\tpcs->GetVectorGap(nface, gn);\n\t\t\treturn gn;\n\t\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool FEPlotContactPressure::SetFilter(const char* szfilter)\n{\n\tif (szfilter) m_interfaceName = szfilter;\n\treturn (szfilter != nullptr);\n}\n\n// Plot contact pressure\nbool FEPlotContactPressure::Save(FESurface &surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface();\n\tbool write = false;\n\tif (pci == nullptr) write = true;\n\telse if (pci->IsActive())\n\t{\n\t\tif (m_interfaceName.empty() || (m_interfaceName == pci->GetName())) write = true;\n\t}\n\n\tif (write)\n\t{\n\t\t// NOTE: the sliding surface does not use material points, so we need this little hack. \n\t\tFESlidingSurface* slidingSurface = dynamic_cast<FESlidingSurface*>(pcs);\n\t\tif (slidingSurface)\n\t\t{\n\t\t\tfor (int i = 0; i < slidingSurface->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = slidingSurface->Element(i);\n\t\t\t\tdouble Lm = 0.0;\n\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble Lmj = slidingSurface->m_data[el.m_lnode[j]].m_Ln;\n\t\t\t\t\tLm += Lmj;\n\t\t\t\t}\n\t\t\t\tLm /= el.Nodes();\n\t\t\t\ta << Lm;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\twriteAverageElementValue<double>(surf, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEContactMaterialPoint* pt = dynamic_cast<const FEContactMaterialPoint*>(&mp);\n\t\t\treturn (pt ? pt->m_Ln : 0.0);\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool FEPlotContactTraction::SetFilter(const char* szfilter)\n{\n\tif (szfilter) m_interfaceName = szfilter;\n\treturn (szfilter != nullptr);\n}\n\n// Plot contact traction\nbool FEPlotContactTraction::Save(FESurface &surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface();\n\tbool write = false;\n\tif (pci == nullptr) write = true;\n\telse if (pci->IsActive())\n\t{\n\t\tif (m_interfaceName.empty() || (m_interfaceName == pci->GetName())) write = true;\n\t}\n\n\tif (write)\n\t{\n\t\twriteElementValue<vec3d>(surf, a, [=](int nface) {\n\t\t\tvec3d tn;\n\t\t\tpcs->GetContactTraction(nface, tn);\n\t\t\treturn tn;\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot nodal contact gap\nbool FEPlotNodalContactGap::Save(FESurface& surf, FEDataStream& a)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\t// NOTE: the sliding surface does not use material points, so we need this little hack. \n\t\tFESlidingSurface* ss = dynamic_cast<FESlidingSurface*>(pcs);\n\t\tif (ss)\n\t\t{\n\t\t\tfor (int i = 0; i < ss->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = ss->Element(i);\n\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble gap = ss->m_data[el.m_lnode[j]].m_gap;\n\t\t\t\t\ta << gap;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\twriteNodalProjectedElementValues<double>(surf, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEContactMaterialPoint* pt = dynamic_cast<const FEContactMaterialPoint*>(&mp);\n\t\t\treturn (pt ? pt->m_gap : 0.0);\n\t\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot nodal vector gap\nbool FEPlotNodalVectorGap::Save(FESurface &surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tint NF = pcs->Elements();\n\t\tvec3d gn[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j < NF; ++j)\n\t\t{\n\t\t\tFESurfaceElement& el = pcs->Element(j);\n\t\t\tpcs->GetNodalVectorGap(j, gn);\n\n\t\t\t// store in archive\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int k = 0; k < ne; ++k) a << gn[k];\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot nodal contact pressure\nbool FEPlotNodalContactPressure::Save(FESurface &surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\twriteNodalProjectedElementValues<double>(surf, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEContactMaterialPoint* pt = dynamic_cast<const FEContactMaterialPoint*>(&mp);\n\t\t\treturn (pt ? pt->m_Ln : 0.0);\n\t\t\t});\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot nodal contact traction\nbool FEPlotNodalContactTraction::Save(FESurface &surf, FEDataStream& a)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tint NF = pcs->Elements();\n\t\tvec3d tn[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j < NF; ++j)\n\t\t{\n\t\t\tFESurfaceElement& el = pcs->Element(j);\n\t\t\tpcs->GetNodalContactTraction(j, tn);\n\n\t\t\t// store in archive\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int k = 0; k < ne; ++k) a << tn[k];\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot surface traction\nbool FEPlotSurfaceTraction::Save(FESurface &surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\n\t\twriteElementValue<vec3d>(surf, a, [=](int nface) {\n\t\t\tvec3d tn;\n\t\t\tpcs->GetSurfaceTraction(nface, tn);\n\t\t\treturn tn;\n\t\t\t});\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot nodal contact traction\nbool FEPlotNodalSurfaceTraction::Save(FESurface &surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tint NF = pcs->Elements();\n\t\tvec3d tn[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j < NF; ++j)\n\t\t{\n\t\t\tFESurfaceElement& el = pcs->Element(j);\n\t\t\tpcs->GetNodalSurfaceTraction(j, tn);\n\n\t\t\t// store in archive\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int k = 0; k < ne; ++k) a << tn[k];\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot stick status\nbool FEPlotStickStatus::Save(FESurface& surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\twriteElementValue<double>(surf, a, [=](int nface) {\n\t\t\tdouble gn;\n\t\t\tpcs->GetStickStatus(nface, gn);\n\t\t\treturn gn;\n\t\t\t});\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotContactForce::Save(FESurface &surf, FEDataStream &a)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n    \n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tvec3d fn = pcs->GetContactForce();\n\t\ta << fn;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot contact area\nbool FEPlotContactArea::Save(FESurface &surf, FEDataStream& a)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tdouble area = pcs->GetContactArea();\n\t\ta << area;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Plot contact penalty parameter\nbool FEPlotContactPenalty::Save(FESurface& surf, FEDataStream& a)\n{\n\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = pcs->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tFEFacetSlidingSurface* ps = dynamic_cast<FEFacetSlidingSurface*>(&surf);\n\t\tif (ps)\n\t\t{\n\t\t\tFEFacet2FacetSliding* pf2f = dynamic_cast<FEFacet2FacetSliding*>(pci);\n\t\t\tdouble eps = (pf2f ? pf2f->m_epsn : 1);\n\n\t\t\twriteAverageElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n\t\t\t\tconst FEFacetSlidingSurface::Data* pt = dynamic_cast<const FEFacetSlidingSurface::Data*>(&mp);\n\t\t\t\treturn (pt ? eps*pt->m_eps : 0);\n\t\t\t\t});\n\t\t\treturn true;\n\t\t}\n\n\t\tFESlidingElasticSurface* pse = dynamic_cast<FESlidingElasticSurface*>(&surf);\n\t\tif (pse)\n\t\t{\n\t\t\tFESlidingElasticInterface* psei = dynamic_cast<FESlidingElasticInterface*>(pci);\n\t\t\tdouble epsn = (psei ? psei->m_epsn : 1);\n\t\t\twriteAverageElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n\t\t\t\tconst FESlidingElasticSurface::Data* pt = dynamic_cast<const FESlidingElasticSurface::Data*>(&mp);\n\t\t\t\treturn (pt ? epsn*pt->m_epsn : 0);\n\t\t\t\t});\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotContactStatus::Save(FESurface& surf, FEDataStream& a)\n{\n\tFEContactSurface* ps = dynamic_cast<FEContactSurface*>(&surf);\n\tif (ps == nullptr) return false;\n\n\t// make sure the corresponding contact interface is active\n\t// (in case the parent was not set, we'll proceed regardless)\n\tFEContactInterface* pci = ps->GetContactInterface(); assert(pci);\n\tif ((pci == 0) || pci->IsActive())\n\t{\n\t\tint NF = ps->Elements();\n\t\tfor (int i = 0; i < NF; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = ps->Element(i);\n\t\t\tdouble nc = 0.0;\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t{\n\t\t\t\tFEContactMaterialPoint* mp = dynamic_cast<FEContactMaterialPoint*>(el.GetMaterialPoint(j));\n\t\t\t\tif (mp && mp->m_pme) nc++;\n\t\t\t}\n\n\t\t\ta << nc;\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotMortarContactGap::Save(FESurface& S, FEDataStream& a)\n{\n\tFEMortarSlidingSurface* ps = dynamic_cast<FEMortarSlidingSurface*>(&S);\n\tif (ps)\n\t{\n\t\twriteNodalValues<double>(S, a, [=](int i) {\n\t\t\tvec3d vA = ps->m_nu[i];\n\t\t\tvec3d gA = ps->m_gap[i];\n\t\t\treturn gA*vA;\n\t\t});\n\t\treturn true;\n\t}\n\telse return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEnclosedVolume::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n\twriteIntegratedElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n\t\tFESurfaceElement& el = static_cast<FESurfaceElement&>(*mp.m_elem);\n\t\tint n = mp.m_index;\n\t\tvec3d xi = pcs->Local2Global(el, n);\n\t\tvec3d g[2];\n\t\tpcs->CoBaseVectors(el, n, g);\n\t\treturn xi*(g[0] ^ g[1]) / 3;\n\t});\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEnclosedVolumeChange::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n    writeIntegratedElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n        FESurfaceElement& el = static_cast<FESurfaceElement&>(*mp.m_elem);\n        int n = mp.m_index;\n        vec3d xi = pcs->Local2Global(el, n);\n        vec3d g[2];\n        pcs->CoBaseVectors(el, n, g);\n        vec3d Xi = pcs->Local2Global0(el, n);\n        vec3d G[2];\n        pcs->CoBaseVectors0(el, n, G);\n        return (xi*(g[0] ^ g[1]) - Xi*(G[0] ^ G[1])) / 3;\n    });\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSurfaceArea::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n    writeIntegratedElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n        FESurfaceElement& el = static_cast<FESurfaceElement&>(*mp.m_elem);\n        int n = mp.m_index;\n        vec3d g[2];\n        pcs->CoBaseVectors(el, n, g);\n        return (g[0] ^ g[1]).norm();\n    });\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFacetArea::Save(FESurface& surf, FEDataStream& a)\n{\n\tFESurface* pcs = &surf;\n\tif (pcs == 0) return false;\n\n\twriteElementValue<double>(surf, a, [=](int nface) {\n\t\tdouble A = pcs->CurrentFaceArea(pcs->Element(nface));\n\t\treturn A;\n\t\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Plot scalar surface load\nbool FEPlotScalarSurfaceLoad::Save(FESurface &surf, FEDataStream& a)\n{\n    FEModel* fem = GetFEModel();\n    int nsl = fem->ModelLoads();\n    FESurfaceLoad* psl = nullptr;\n    for (int i = 0; i<nsl; ++i)\n    {\n        psl = dynamic_cast<FESurfaceLoad*>(fem->ModelLoad(i));\n        if (psl && (&psl->GetSurface() == &surf)) break;\n    }\n\n    if (psl == nullptr) return false;\n    \n    if (psl->IsActive()) {\n        writeAverageElementValue<double>(surf, a, [=](const FEMaterialPoint& mp) {\n\t\t\tconst FESurfaceMaterialPoint* pt = dynamic_cast<const FESurfaceMaterialPoint*>(&mp);\n\t\t\treturn (pt ? psl->ScalarLoad(*const_cast<FESurfaceMaterialPoint*>(pt)) : 0.0);\n        });\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNetSurfaceReactionForce::Save(FESurface& surf, FEDataStream& a)\n{\n\t// NOTE: Currently, for nodes attached to rigid bodies the reaction forces \n\t//       will actually be the rigid reaction forces. \n\tFEModel& fem = *GetFEModel();\n\tint dofX = fem.GetDOFIndex(\"x\");\n\tint dofY = fem.GetDOFIndex(\"y\");\n\tint dofZ = fem.GetDOFIndex(\"z\");\n\tif ((dofX < 0) || (dofY < 0) || (dofZ < 0)) return false;\n\n\tvec3d F(0, 0, 0);\n\tfor (int i = 0; i < surf.Nodes(); ++i)\n\t{\n\t\tFENode& n = surf.Node(i);\n\t\tvec3d Fi = n.get_load3(dofX, dofY, dofZ);\n\t\tF += Fi;\n\t}\n\n\ta << F;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNetSurfaceReactionMoment::Save(FESurface& surf, FEDataStream& a)\n{\n\t// NOTE: Currently, for nodes attached to rigid bodies the reaction forces \n\t//       will actually be the rigid reaction forces. \n\tFEModel& fem = *GetFEModel();\n\tint dofX = fem.GetDOFIndex(\"x\");\n\tint dofY = fem.GetDOFIndex(\"y\");\n\tint dofZ = fem.GetDOFIndex(\"z\");\n\tif ((dofX < 0) || (dofY < 0) || (dofZ < 0)) return false;\n\n\tvec3d M(0, 0, 0);\n\tfor (int i = 0; i < surf.Nodes(); ++i)\n\t{\n\t\tFENode& n = surf.Node(i);\n\t\tvec3d Fi = n.get_load3(dofX, dofY, dofZ);\n\t\tvec3d r = n.m_rt;\n\t\tvec3d Mi = r ^ Fi;\n\t\tM += Mi;\n\t}\n\n\ta << M;\n\n\treturn true;\n}\n\n\n//=============================================================================\n//\t\t\t\t\t\t\tD O M A I N   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nbool FEPlotElementVelocity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n\tif ((pme == 0) || pme->IsRigid()) return false;\n\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\treturn pt.m_v;\n\t});\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotElementAcceleration::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n\n\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\treturn pt.m_a;\n\t});\n\n\treturn true;\n}\n\n//=============================================================================\nclass FEStress\n{\npublic:\n\tmat3ds operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\treturn (pt ? pt->m_s : mat3ds(0));\n\t}\n};\n\nclass FEStrain\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        FEElement* el = mp.m_elem;\n        FEShellElementNew* se = dynamic_cast<FEShellElementNew*>(el);\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (se) return (se->m_E[mp.m_index].norm() > 0) ? se->m_E[mp.m_index] : pt->Strain();\n        else return (pt ? pt->Strain() : mat3ds(0));\n    }\n};\n\n//-----------------------------------------------------------------------------\n//! Store the average stresses for each element. \nbool FEPlotElementStress::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDomainParameter* var = nullptr;\n\tFESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n\tif (pme)\n\t{\n\t\tif (!pme->IsRigid())\n\t\t\tvar = pme->FindDomainParameter(\"stress\");\n\t}\n\telse var = dom.GetMaterial()->FindDomainParameter(\"stress\");\n\n\tif (var == nullptr) return false;\n\n\twriteAverageElementValue<mat3ds>(dom, a, var);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average stresses for each element. \nbool FEPlotElementPK2Stress::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n\tif ((pme == 0) || pme->IsRigid()) return false;\n\n\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData< FEElasticMaterialPoint>();\n\t\tmat3ds s = ep.m_s;\n\t\tmat3ds S = ep.pull_back(s);\n\t\treturn S;\n\t\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average PK1 stress for each element. \nbool FEPlotElementPK1Stress::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n\tif ((pme == 0) || pme->IsRigid()) return false;\n\n\twriteAverageElementValue<mat3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData< FEElasticMaterialPoint>();\n\t\tmat3d  F = ep.m_F;\n\t\tdouble J = F.det();\n\t\tmat3ds s = ep.m_s;\t// Cauchy stress\n\n\t\tmat3d P = (s * F.transinv()) * J;\n\t\treturn P;\n\t\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average element plastic yield stress\nbool FEPlotElementPlasticYieldStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n    FEReactivePlasticity* pmp = pme->ExtractProperty<FEReactivePlasticity>();\n    if (pmp == nullptr) return false;\n\n    writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n        const FEReactivePlasticityMaterialPoint& pp = *mp.ExtractData<FEReactivePlasticityMaterialPoint>();\n        if (pp.m_Kv.size() > 0) return pp.m_Kv[0];\n        return 0.0;\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average element yield stress based on Drucker shear stress criterion\nbool FEPlotElementDruckerShear::Save(FEDomain& dom, FEDataStream& a)\n{\n    FESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n    FEDamageCriterionDrucker* pmd = pme->ExtractProperty<FEDamageCriterionDrucker>();\n    if (pmd == nullptr) return false;\n\n    writeAverageElementValue<double>(dom, a, [&pmd](const FEMaterialPoint& mp) {\n        double c = pmd->m_c(mp);\n        const FEElasticMaterialPoint& ep = *mp.ExtractData< FEElasticMaterialPoint>();\n        mat3ds s = ep.m_s;    // Cauchy stress\n        mat3ds sdev = s.dev();\n        double J2 = 0.5*(sdev*sdev).trace();\n        double J3 = sdev.det();\n        double k = pow(pow(J2,3) - c*pow(J3,2),1./6.);\n        return k;\n        });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average element yield stress based on Prager-Drucker criterion\nbool FEPlotElementPragerDruckerStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n    FEDamageCriterionDruckerPrager* pmd = pme->ExtractProperty<FEDamageCriterionDruckerPrager>();\n    if (pmd == nullptr) return false;\n\n    writeAverageElementValue<double>(dom, a, [&pmd](const FEMaterialPoint& mp) {\n        double b = pmd->m_b(mp);\n        const FEElasticMaterialPoint& ep = *mp.ExtractData< FEElasticMaterialPoint>();\n        mat3ds s = ep.m_s;    // Cauchy stress\n        double se = sqrt((pow(s.xx()-s.yy(),2) + pow(s.yy()-s.zz(),2) + pow(s.zz()-s.xx(),2)\n                           + 6*(pow(s.xy(),2) + pow(s.yz(),2) + pow(s.xz(),2)))/2);\n        double sm = s.tr()/3;\n        double Phi = se - b*sm;\n        return Phi;\n        });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average element yield stress based on Deshpande-Fleck criterion\nbool FEPlotElementDeshpandeFleckStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    FESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n    FEDamageCriterionDeshpandeFleck* pmd = pme->ExtractProperty<FEDamageCriterionDeshpandeFleck>();\n    if (pmd == nullptr) return false;\n\n    writeAverageElementValue<double>(dom, a, [&pmd](const FEMaterialPoint& mp) {\n        double beta = pmd->m_beta(mp);\n        const FEElasticMaterialPoint& ep = *mp.ExtractData< FEElasticMaterialPoint>();\n        mat3ds s = ep.m_s;  // Cauchy stress\n        double se = sqrt((pow(s.xx()-s.yy(),2) + pow(s.yy()-s.zz(),2) + pow(s.zz()-s.xx(),2)\n                           + 6*(pow(s.xy(),2) + pow(s.yz(),2) + pow(s.xz(),2)))/2);\n        double sm = s.tr()/3;\n        double Phi = sqrt((se*se+pow(3*beta*sm,2))/(1+beta*beta));\n        return Phi;\n        });\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSPRStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\n\t// get the domain\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\twriteSPRElementValueMat3ds(sd, a, FEStress());\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSPRLinearStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\n\t// get the domain\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\twriteSPRElementValueMat3ds(sd, a, FEStress(), 1);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNodalStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // new shell elements should be excluded\n\twriteNodalProjectedElementValues<mat3ds>(dom, a, FEStress());\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellTopStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellElementValues<mat3ds>(dom, a, FEStress(), false);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellBottomStress::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellElementValues<mat3ds>(dom, a, FEStress(), true);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellTopNodalStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellNodalProjectedElementValues<mat3ds>(dom, a, FEStress(), false);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellBottomNodalStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellNodalProjectedElementValues<mat3ds>(dom, a, FEStress(), true);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNodalStrains::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // new shell elements should be excluded\n    writeNodalProjectedElementValues<mat3ds>(dom, a, FEStrain());\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellTopStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellElementValues<mat3ds>(dom, a, FEStrain(), false);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellBottomStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellElementValues<mat3ds>(dom, a, FEStrain(), true);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellTopNodalStrains::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellNodalProjectedElementValues<mat3ds>(dom, a, FEStrain(), false);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotShellBottomNodalStrains::Save(FEDomain& dom, FEDataStream& a)\n{\n    if (!dynamic_cast<FESSIShellDomain*>(&dom)) return false;  // only use with new shell elements\n    writeShellNodalProjectedElementValues<mat3ds>(dom, a, FEStrain(), true);\n    return true;\n}\n\n//=============================================================================\nFEPlotElementMixtureStress::FEPlotElementMixtureStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) \n{\n\tm_mat = -1;\n\tm_comp = -1;\n\tSetUnits(UNIT_PRESSURE);\n}\n\nbool FEPlotElementMixtureStress::SetFilter(const char* szfilter)\n{\n\tif (strncmp(szfilter, \"material\", 8) == 0)\n\t{\n\t\tif (sscanf(szfilter, \"material[%d].solid[%d]\", &m_mat, &m_comp) != 2) return false;\n\t}\n\telse\n\t{\n\t\tif (sscanf(szfilter, \"solid[%d]\", &m_comp) != 1) return false;\n\t}\n\treturn true;\n}\n\nbool FEPlotElementMixtureStress::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMaterial* pm = dom.GetMaterial();\n\tif (m_mat != -1)\n\t{\n\t\tif (pm != GetFEModel()->GetMaterial(m_mat)) return false;\n\t}\n\n\t// make sure we start from the elastic component\n\tFEElasticMaterial* pmat = pm->ExtractProperty<FEElasticMaterial>();\n\tif (pmat == nullptr) return false;\n\n\t// make sure this is a mixture\n\tFEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n\tFEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n\tif ((pmm == nullptr) && (pum == nullptr)) return false;\n\n\t// get the mixture component\n\tif (m_comp < 0) return false;\n\n\tfor (int i = 0; i < dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tmat3ds savg; savg.zero();\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tFEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n\t\t\tif (mmp && (m_comp < mmp->Components()))\n\t\t\t{\n\t\t\t\tFEElasticMaterialPoint& ep = *mmp->GetPointData(m_comp)->ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tsavg += ep.m_s;\n\t\t\t}\n\t\t}\n\t\tsavg /= (double)el.GaussPoints();\n\n\t\ta << savg;\n\t}\n\n\treturn true;\n}\n\n//=============================================================================\n//! Store the uncoupled pressure for each element.\nbool FEPlotElementUncoupledPressure::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEUncoupledMaterial* pmu = dom.GetMaterial()->ExtractProperty<FEUncoupledMaterial>();\n    if (pmu == 0) return false;\n   \n    // write element data\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt == 0) return 0.0;\n\t\treturn -pt->m_p;   // use negative sign to get positive pressure in compression\n\t});\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the norm of the average Cauchy stress for each element. \nbool FEPlotElementsnorm::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n\tif ((pme == 0) || pme->IsRigid()) return false;\n\n\twriteAverageElementValue<mat3ds, double>(dom, a, FEStress(), [](const mat3ds& s) { return sqrt(s.dotdot(s)); });\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average elasticity for each element.\n\nclass FEElementElasticity\n{\npublic:\n\tFEElementElasticity(FESolidMaterial* pm) : m_mat(pm) {}\n\ttens4ds operator()(const FEMaterialPoint& mp)\n\t{\n\t\treturn m_mat->Tangent(const_cast<FEMaterialPoint&>(mp));\n\t}\nprivate:\n\tFESolidMaterial*\tm_mat;\n};\n\nbool FEPlotElementElasticity::Save(FEDomain& dom, FEDataStream& a)\n{\n    FESolidMaterial* pme = dom.GetMaterial()->ExtractProperty<FESolidMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n\n\twriteAverageElementValue<tens4ds>(dom, a, FEElementElasticity(pme));\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the average deviatoric elasticity for each element.\n\nclass FEElementDevElasticity\n{\npublic:\n    FEElementDevElasticity(FEUncoupledMaterial* pm) : m_mat(pm) {}\n    tens4ds operator()(const FEMaterialPoint& mp)\n    {\n        return m_mat->DevTangent(const_cast<FEMaterialPoint&>(mp));\n    }\nprivate:\n    FEUncoupledMaterial*    m_mat;\n};\n\nbool FEPlotElementDevElasticity::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEUncoupledMaterial* pme = dom.GetMaterial()->ExtractProperty<FEUncoupledMaterial>();\n    if ((pme == 0) || pme->IsRigid()) return false;\n    \n    writeAverageElementValue<tens4ds>(dom, a, FEElementDevElasticity(pme));\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nclass FEStrainEnergy\n{\npublic:\n\tFEStrainEnergy(FEElasticMaterial* pm) : m_mat(pm) {}\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\treturn m_mat->StrainEnergyDensity(const_cast<FEMaterialPoint&>(mp));\n\t}\nprivate:\n\tFEElasticMaterial*\tm_mat;\n};\n\nbool FEPlotStrainEnergyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == 0) return false;\n    \n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEStrainEnergy W(pme);\n\t\twriteAverageElementValue<double>(dom, a, W);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEDevStrainEnergy\n{\npublic:\n\tFEDevStrainEnergy(FEUncoupledMaterial* pm) : m_mat(pm) {}\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\treturn m_mat->DevStrainEnergyDensity(const_cast<FEMaterialPoint&>(mp));\n\t}\nprivate:\n\tFEUncoupledMaterial*\tm_mat;\n};\n\nbool FEPlotDevStrainEnergyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEUncoupledMaterial* pmu = dom.GetMaterial()->ExtractProperty<FEUncoupledMaterial>();\n    if (pmu == 0) return false;\n    \n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEDevStrainEnergy devW(pmu);\n\t\twriteAverageElementValue<double>(dom, a, devW);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nclass FESpecificStrainEnergy\n{\npublic:\n    FESpecificStrainEnergy(FEElasticMaterial* pm, int comp) : m_mat(pm), m_comp(comp) {}\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n        if (dynamic_cast<FERemodelingInterface*>(m_mat) == 0) return 0;\n\t\tFEMaterialPoint& lmp = const_cast<FEMaterialPoint&>(mp);\n\t\tFERemodelingMaterialPoint* rpt = lmp.ExtractData<FERemodelingMaterialPoint>();\n        if (rpt == nullptr) {\n            FEMaterialPoint* pt = lmp.ExtractData<FEElasticMixtureMaterialPoint>()->GetPointData(m_comp);\n            rpt = pt->ExtractData<FERemodelingMaterialPoint>();\n        }\n\t\treturn (((rpt != nullptr) && (rpt->m_rhor > 0)) ? rpt->m_sed / rpt->m_rhor : 0.0);\n\t}\nprivate:\n    FEElasticMaterial*    m_mat;\n    int                   m_comp;\n};\n\nbool FEPlotSpecificStrainEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESpecificStrainEnergy w(pme, -1);\n        writeAverageElementValue<double>(dom, a, w);\n        return true;\n    }\n    return false;\n}\n\n//=============================================================================\nFEPlotMixtureStrainEnergyDensity::FEPlotMixtureStrainEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_mat = -1;\n    m_comp = -1;\n    SetUnits(UNIT_PRESSURE);\n}\n\nbool FEPlotMixtureStrainEnergyDensity::SetFilter(const char* szfilter)\n{\n    if (strncmp(szfilter, \"material\", 8) == 0)\n    {\n        if (sscanf(szfilter, \"material[%d].solid[%d]\", &m_mat, &m_comp) != 2) return false;\n    }\n    else\n    {\n        if (sscanf(szfilter, \"solid[%d]\", &m_comp) != 1) return false;\n    }\n    return true;\n}\n\nbool FEPlotMixtureStrainEnergyDensity::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEMaterial* pm = dom.GetMaterial();\n    if (m_mat != -1)\n    {\n        if (pm != GetFEModel()->GetMaterial(m_mat)) return false;\n    }\n\n    // make sure we start from the elastic component\n    FEElasticMaterial* pmat = pm->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n\n    // make sure this is a mixture\n    FEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n    FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n    if ((pmm == nullptr) && (pum == nullptr)) return false;\n\n    // get the mixture component\n    if (m_comp < 0) return false;\n    FEElasticMaterial* pme = nullptr;\n    if (pmm) pme = pmm->GetMaterial(m_comp);\n    else if (pum) pme = pum->GetMaterial(m_comp);\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEStrainEnergy W(pme);\n        writeAverageElementValue<double>(dom, a, W);\n        return true;\n    }\n    return false;\n}\n\n//=============================================================================\nFEPlotMixtureDevStrainEnergyDensity::FEPlotMixtureDevStrainEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_mat = -1;\n    m_comp = -1;\n    SetUnits(UNIT_PRESSURE);\n}\n\nbool FEPlotMixtureDevStrainEnergyDensity::SetFilter(const char* szfilter)\n{\n    if (strncmp(szfilter, \"material\", 8) == 0)\n    {\n        if (sscanf(szfilter, \"material[%d].solid[%d]\", &m_mat, &m_comp) != 2) return false;\n    }\n    else\n    {\n        if (sscanf(szfilter, \"solid[%d]\", &m_comp) != 1) return false;\n    }\n    return true;\n}\n\nbool FEPlotMixtureDevStrainEnergyDensity::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEMaterial* pm = dom.GetMaterial();\n    if (m_mat != -1)\n    {\n        if (pm != GetFEModel()->GetMaterial(m_mat)) return false;\n    }\n\n    // make sure we start from the elastic component\n    FEElasticMaterial* pmat = pm->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n\n    // make sure this is a mixture\n    FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n    if (pum == nullptr) return false;\n\n    // get the mixture component\n    if (m_comp < 0) return false;\n    FEElasticMaterial* pme = pum->GetMaterial(m_comp);\n    FEUncoupledMaterial* pmu = pme->ExtractProperty<FEUncoupledMaterial>();\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEDevStrainEnergy devW(pmu);\n        writeAverageElementValue<double>(dom, a, devW);\n        return true;\n    }\n    return false;\n}\n\n//=============================================================================\nFEPlotMixtureSpecificStrainEnergy::FEPlotMixtureSpecificStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_mat = -1;\n    m_comp = -1;\n    SetUnits(UNIT_SPECIFIC_ENERGY);\n}\n\nbool FEPlotMixtureSpecificStrainEnergy::SetFilter(const char* szfilter)\n{\n    if (strncmp(szfilter, \"material\", 8) == 0)\n    {\n        if (sscanf(szfilter, \"material[%d].solid[%d]\", &m_mat, &m_comp) != 2) return false;\n    }\n    else\n    {\n        if (sscanf(szfilter, \"solid[%d]\", &m_comp) != 1) return false;\n    }\n    return true;\n}\n\nbool FEPlotMixtureSpecificStrainEnergy::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEMaterial* pm = dom.GetMaterial();\n    if (m_mat != -1)\n    {\n        if (pm != GetFEModel()->GetMaterial(m_mat)) return false;\n    }\n\n    // make sure we start from the elastic component\n    FEElasticMaterial* pmat = pm->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n\n    // make sure this is a mixture\n    FEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n    FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n    if ((pmm == nullptr) && (pum == nullptr)) return false;\n\n    // get the mixture component\n    if (m_comp < 0) return false;\n    FEElasticMaterial* pme = nullptr;\n    if (pmm) pme = pmm->GetMaterial(m_comp);\n    else if (pum) pme = pum->GetMaterial(m_comp);\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESpecificStrainEnergy w(pme, m_comp);\n        writeAverageElementValue<double>(dom, a, w);\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotKineticEnergyDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n    const int dof_VX = GetFEModel()->GetDOFIndex(\"vx\");\n    const int dof_VY = GetFEModel()->GetDOFIndex(\"vy\");\n    const int dof_VZ = GetFEModel()->GetDOFIndex(\"vz\");\n    const int dof_VU = GetFEModel()->GetDOFIndex(\"vu\");\n    const int dof_VV = GetFEModel()->GetDOFIndex(\"vv\");\n    const int dof_VW = GetFEModel()->GetDOFIndex(\"vw\");\n    \n    FEMesh& mesh = *dom.GetMesh();\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n        for (int i=0; i<bd.Elements(); ++i)\n        {\n            FESolidElement& el = bd.Element(i);\n            double *H;\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d vt[FEElement::MAX_NODES];\n            vec3d vn[FEElement::MAX_NODES];\n            for (int j=0; j<el.Nodes(); ++j) {\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n                H = el.H(j);\n                vn[j] = vec3d(0, 0, 0);\n                for (int k=0; k<el.Nodes(); ++k)\n                    vn[j] += vt[k]*H[k];\n            }\n            \n            // integrate kinetic energy\n            double ew = 0;\n            double V = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                double detJ = bd.detJ0(el, j)*gw[j];\n                V += detJ;\n                ew += vn[j]*vn[j]*(pme->Density(mp)/2*detJ);\n            }\n            \n            a << ew/V;\n        }\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n        for (int i=0; i<bd->Elements(); ++i)\n        {\n            FEShellElement& el = bd->Element(i);\n            double* gw = el.GaussWeights();\n\n\t\t\tdouble ew = 0.0;\n\t\t\tif ((dof_VU >= 0) && (dof_VV >= 0) && (dof_VW >= 0))\n\t\t\t{\n\t\t\t\t// get nodal velocities\n\t\t\t\tvec3d vt[FEElement::MAX_NODES];\n\t\t\t\tvec3d wt[FEElement::MAX_NODES];\n\t\t\t\tvec3d vn[FEElement::MAX_NODES];\n\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j) {\n\t\t\t\t\tvt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n\t\t\t\t\twt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VU, dof_VV, dof_VW);\n\t\t\t\t}\n\n\t\t\t\t// evaluate velocities at integration points\n\t\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t\t\t\tvn[j] = bd->evaluate(el, vt, wt, j);\n\n\t\t\t\t// integrate kinetic energy\n\t\t\t\tdouble ew = 0;\n\t\t\t\tdouble V = 0;\n\t\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\n\t\t\t\t\tdouble detJ = bd->detJ0(el, j) * gw[j];\n\t\t\t\t\tV += detJ;\n\t\t\t\t\tew += vn[j] * vn[j] * (pme->Density(mp) / 2 * detJ);\n\t\t\t\t}\n\n\t\t\t\t// normalize by volume\n\t\t\t\tew /= V;\n\t\t\t}\n            a << ew;\n        }\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n// TODO: Should I call the density for remodeling materials something else? \n//       Or maybe the FEElasticMaterialPoint should define a density parameter\n//       that will be updated by the materials to define the current density?\n\nclass FEDensity\n{\npublic:\n\tFEDensity(FEElasticMaterial* pm) : m_mat(pm) {}\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\tdouble J = ep.m_F.det();\n\t\treturn m_mat->Density(const_cast<FEMaterialPoint&>(mp)) / J;\n\t}\nprivate:\n\tFEElasticMaterial*\tm_mat;\n};\n\nclass FERemodelingDensity\n{\npublic:\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FERemodelingMaterialPoint* pt = (mp.ExtractData<FERemodelingMaterialPoint>());\n\t\treturn (pt ? pt->m_rhor : 0.0);\n\t}\n};\n\nbool FEPlotDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\tFEElasticMaterial* em = bd.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n        if (em == 0) return false;\n\n\t\tFERemodelingElasticMaterial* rm = dynamic_cast<FERemodelingElasticMaterial*>(em);\n\t\tif (rm)\n\t\t{\n\t\t\tFERemodelingDensity dens;\n\t\t\twriteAverageElementValue<double>(dom, a, dens);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEDensity dens(em);\n\t\t\twriteAverageElementValue<double>(dom, a, dens);\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (dom.Class() == FE_DOMAIN_SHELL)\n\t{\n        FEElasticMaterial* em = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\t\tif (em == 0) return false;\n\t\tFEDensity dens(em);\n\t\twriteAverageElementValue<double>(dom, a, dens);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotElementStrainEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<double>(bd, a, FEStrainEnergy(pme));\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n\t\twriteIntegratedElementValue(*bd, a, FEStrainEnergy(pme));\n\t\treturn true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n// integrated element kinetic energy\nclass FEKineticEnergyDensity\n{\npublic:\n\tFEKineticEnergyDensity(FEElasticMaterial* pm) : m_mat(pm) {}\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\treturn 0.5*(ep.m_v*ep.m_v)*m_mat->Density(const_cast<FEMaterialPoint&>(mp));\n\t}\nprivate:\n\tFEElasticMaterial*\tm_mat;\n};\n\nbool FEPlotElementKineticEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<double>(bd, a, FEKineticEnergyDensity(pme));\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n\t\twriteIntegratedElementValue(*bd, a, FEKineticEnergyDensity(pme));\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotElementCenterOfMass::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i<bd.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = bd.Element(i);\n\t\t\tdouble* gw = el.GaussWeights();\n\n\t\t\t// integrate zeroth and first mass moments\n\t\t\tvec3d ew = vec3d(0, 0, 0);\n\t\t\tdouble m = 0;\n\t\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\tdouble detJ = bd.detJ0(el, j)*gw[j];\n\t\t\t\tew += mp.m_rt*(pme->Density(mp)*detJ);\n\t\t\t\tm += pme->Density(mp)*detJ;\n\t\t\t}\n\n\t\t\ta << ew / m;\n\t\t}\n\t\treturn true;\n\t}\n\telse if (dom.Class() == FE_DOMAIN_SHELL)\n\t{\n\t\tFESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n\t\tif (bd == 0) return false;\n\t\tfor (int i = 0; i<bd->Elements(); ++i)\n\t\t{\n\t\t\tFEShellElement& el = bd->Element(i);\n\t\t\tdouble* gw = el.GaussWeights();\n\n\t\t\t// integrate zeroth and first mass moments\n\t\t\tvec3d ew = vec3d(0, 0, 0);\n\t\t\tdouble m = 0;\n\t\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(j);\n\t\t\t\tdouble detJ = bd->detJ0(el, j)*gw[j];\n\t\t\t\tew += pt.m_rt*(pme->Density(pt)*detJ);\n\t\t\t\tm += pme->Density(pt)*detJ;\n\t\t\t}\n\n\t\t\ta << ew / m;\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEElementLinearMomentum\n{\npublic:\n\tFEElementLinearMomentum(FEElasticMaterial* pm) : m_mat(pm) {}\n\tvec3d operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\treturn pt.m_v*m_mat->Density(const_cast<FEMaterialPoint&>(mp));\n\t}\n\nprivate:\n\tFEElasticMaterial*\tm_mat;\n};\n\nbool FEPlotElementLinearMomentum::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<vec3d>(bd, a, FEElementLinearMomentum(pme));\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n\t\twriteIntegratedElementValue(*bd, a, FEElementLinearMomentum(pme));\n\t\treturn true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n// Integrated element angular momentum\nclass FEElementAngularMomentum\n{\npublic:\n\tFEElementAngularMomentum(FEElasticMaterial* pm) : m_mat(pm) {}\n\tvec3d operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\treturn (mp.m_rt ^ pt.m_v)*m_mat->Density(const_cast<FEMaterialPoint&>(mp));\n\t}\n\nprivate:\n\tFEElasticMaterial*\tm_mat;\n};\n\nbool FEPlotElementAngularMomentum::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<vec3d>(bd, a, FEElementAngularMomentum(pme));\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n\t\twriteIntegratedElementValue(*bd, a, FEElementAngularMomentum(pme));\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEElementStressPower\n{\npublic:\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\treturn  ep.m_s.dotdot(ep.m_L.sym())*ep.m_J;\n\t}\n};\n\nbool FEPlotElementStressPower::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<double>(bd, a, FEElementStressPower());\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n\t\twriteIntegratedElementValue(*bd, a, FEElementStressPower());\n\t\treturn true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nclass FECurrentElementStrainEnergy\n{\npublic:\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\treturn ep.m_Wt;\n\t}\n};\n\nbool FEPlotCurrentElementStrainEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\t\twriteIntegratedElementValue<double>(bd, a, FECurrentElementStrainEnergy());\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n\t\twriteIntegratedElementValue(*bd, a, FECurrentElementStrainEnergy());\n\t\treturn true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotCurrentElementKineticEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    const int dof_VX = GetFEModel()->GetDOFIndex(\"vx\");\n    const int dof_VY = GetFEModel()->GetDOFIndex(\"vy\");\n    const int dof_VZ = GetFEModel()->GetDOFIndex(\"vz\");\n    const int dof_VU = GetFEModel()->GetDOFIndex(\"vu\");\n    const int dof_VV = GetFEModel()->GetDOFIndex(\"vv\");\n    const int dof_VW = GetFEModel()->GetDOFIndex(\"vw\");\n    \n    FEMesh& mesh = *dom.GetMesh();\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    const int NELN = FEElement::MAX_NODES;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n        for (int i=0; i<bd.Elements(); ++i)\n        {\n            FESolidElement& el = bd.Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d vt[NELN], vn[NELN];\n            for (int j=0; j<el.Nodes(); ++j)\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n                vn[j] = el.Evaluate(vt, j);\n            \n            // integrate kinetic energy\n            double ew = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\n                double detJ = bd.detJ0(el, j)*gw[j]* pme->Density(mp)/2;\n                ew += vn[j]*vn[j]*detJ;\n            }\n            \n            a << ew;\n        }\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n        for (int i=0; i<bd->Elements(); ++i)\n        {\n            FEShellElement& el = bd->Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d vt[NELN], wt[NELN], vn[NELN];\n            for (int j=0; j<el.Nodes(); ++j) {\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n                wt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VU, dof_VV, dof_VW);\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n                vn[j] = bd->evaluate(el, vt, wt, j);\n            \n            // integrate kinetic energy\n            double ew = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\n                double detJ = bd->detJ0(el, j)*gw[j]* pme->Density(mp)/2;\n                ew += vn[j]*vn[j]*detJ;\n            }\n            \n            a << ew;\n        }\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotCurrentElementCenterOfMass::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEMesh& mesh = *dom.GetMesh();\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    const int NELN = FEElement::MAX_NODES;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n        for (int i=0; i<bd.Elements(); ++i)\n        {\n            FESolidElement& el = bd.Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal positions and velocities\n            vec3d rt[NELN], rn[NELN];\n            for (int j=0; j<el.Nodes(); ++j)\n                rt[j] = mesh.Node(el.m_node[j]).m_rt;\n            \n            // evaluate positions at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n                rn[j] = el.Evaluate(rt, j);\n            \n            // integrate zeroth and first mass moment\n            double ez = 0;\n            vec3d ef = vec3d(0,0,0);\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\n                double detJ = bd.detJ0(el, j)*gw[j]* pme->Density(mp);\n                ez += detJ;\n                ef += rn[j]*detJ;\n            }\n            \n            a << ef/ez;\n        }\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n        for (int i=0; i<bd->Elements(); ++i)\n        {\n            FEShellElement& el = bd->Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d rt[NELN], st[NELN], rn[NELN];\n            for (int j=0; j<el.Nodes(); ++j) {\n                rt[j] = mesh.Node(el.m_node[j]).m_rt;\n                st[j] = mesh.Node(el.m_node[j]).st();\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n                rn[j] = bd->evaluate(el, rt, st, j);\n            \n            // integrate zeroth and first mass moment\n            double ez = 0;\n            vec3d ef = vec3d(0,0,0);\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\n                double detJ = bd->detJ0(el, j)*gw[j]* pme->Density(mp);\n                ez += detJ;\n                ef += rn[j]*detJ;\n            }\n            \n            a << ef/ez;\n        }\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotCurrentElementLinearMomentum::Save(FEDomain &dom, FEDataStream& a)\n{\n    const int dof_VX = GetFEModel()->GetDOFIndex(\"vx\");\n    const int dof_VY = GetFEModel()->GetDOFIndex(\"vy\");\n    const int dof_VZ = GetFEModel()->GetDOFIndex(\"vz\");\n    const int dof_VU = GetFEModel()->GetDOFIndex(\"vu\");\n    const int dof_VV = GetFEModel()->GetDOFIndex(\"vv\");\n    const int dof_VW = GetFEModel()->GetDOFIndex(\"vw\");\n    \n    FEMesh& mesh = *dom.GetMesh();\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    const int NELN = FEElement::MAX_NODES;\n\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n        for (int i=0; i<bd.Elements(); ++i)\n        {\n            FESolidElement& el = bd.Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d vt[NELN], vn[NELN];\n            for (int j=0; j<el.Nodes(); ++j) {\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n                vn[j] = el.Evaluate(vt, j);\n            \n            // integrate linear momentum\n            vec3d ew = vec3d(0,0,0);\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                double detJ = bd.detJ0(el, j)*gw[j];\n                ew += vn[j]*(pme->Density(mp)*detJ);\n            }\n            \n            a << ew;\n        }\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n        for (int i=0; i<bd->Elements(); ++i)\n        {\n            FEShellElement& el = bd->Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d vt[NELN], wt[NELN], vn[NELN];\n            for (int j=0; j<el.Nodes(); ++j) {\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n                wt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VU, dof_VV, dof_VW);\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j)\n                vn[j] = bd->evaluate(el, vt, wt, j);\n            \n            // integrate linear momentum\n            vec3d ew = vec3d(0,0,0);\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                double detJ = bd->detJ0(el, j)*gw[j];\n                ew += vn[j]*(pme->Density(mp)*detJ);\n            }\n            \n            a << ew;\n        }\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotCurrentElementAngularMomentum::Save(FEDomain &dom, FEDataStream& a)\n{\n    const int dof_VX = GetFEModel()->GetDOFIndex(\"vx\");\n    const int dof_VY = GetFEModel()->GetDOFIndex(\"vy\");\n    const int dof_VZ = GetFEModel()->GetDOFIndex(\"vz\");\n    const int dof_SVX = GetFEModel()->GetDOFIndex(\"svx\");\n    const int dof_SVY = GetFEModel()->GetDOFIndex(\"svy\");\n    const int dof_SVZ = GetFEModel()->GetDOFIndex(\"svz\");\n    \n    FEMesh& mesh = *dom.GetMesh();\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n    const int NELN = FEElement::MAX_NODES;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n        for (int i=0; i<bd.Elements(); ++i)\n        {\n            FESolidElement& el = bd.Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal positions and velocities\n            vec3d rt[NELN], rn[NELN];\n            vec3d vt[NELN], vn[NELN];\n            for (int j=0; j<el.Nodes(); ++j) {\n                rt[j] = mesh.Node(el.m_node[j]).m_rt;\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j) {\n                rn[j] = el.Evaluate(rt, j);\n                vn[j] = el.Evaluate(vt, j);\n            }\n            \n            // integrate angular momentum\n            vec3d ew = vec3d(0,0,0);\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                double detJ = bd.detJ0(el, j)*gw[j];\n                ew += (rn[j] ^ vn[j])*(pme->Density(mp)*detJ);\n            }\n            \n            a << ew;\n        }\n        return true;\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n        if (bd == 0) return false;\n        for (int i=0; i<bd->Elements(); ++i)\n        {\n            FEShellElement& el = bd->Element(i);\n            double* gw = el.GaussWeights();\n            \n            // get nodal velocities\n            vec3d rt[NELN], st[NELN], rn[NELN];\n            vec3d vt[NELN], wt[NELN], vn[NELN];\n            for (int j=0; j<el.Nodes(); ++j) {\n                rt[j] = mesh.Node(el.m_node[j]).m_rt;\n                st[j] = mesh.Node(el.m_node[j]).st();\n                vt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_VX, dof_VY, dof_VZ);\n                wt[j] = mesh.Node(el.m_node[j]).get_vec3d(dof_SVX, dof_SVY, dof_SVZ);\n            }\n            \n            // evaluate velocities at integration points\n            for (int j=0; j<el.GaussPoints(); ++j) {\n                rn[j] = bd->evaluate(el, rt, st, j);\n                vn[j] = bd->evaluate(el, vt, wt, j);\n            }\n            \n            // integrate angular momentum\n            vec3d ew = vec3d(0,0,0);\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                double detJ = bd->detJ0(el, j)*gw[j];\n                ew += (rn[j] ^ vn[j])*(pme->Density(mp)*detJ);\n            }\n            \n            a << ew;\n        }\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRelativeVolume::Save(FEDomain &dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\treturn (pt ? pt->m_J : 0.0);\n\t\t\t});\n\t}\n\telse if (dom.Class() == FE_DOMAIN_SHELL)\n\t{\n\t\tFEShellDomain* sd = dynamic_cast<FEShellDomain*>(&dom); assert(sd);\n\n\t\t// a filter to get J from a strain tensor\n\t\tauto getJfromE = [](const mat3ds& E) {\n\t\t\tmat3ds C = mat3dd(1) + E * 2;\n\t\t\treturn sqrt(C.det());\n\t\t};\n\n\t\tFEShellDomainNew* newsd = dynamic_cast<FEShellDomainNew*>(sd);\n\t\tFEElasticEASShellDomain* easd = dynamic_cast<FEElasticEASShellDomain*>(newsd);\n\t\tFEElasticANSShellDomain* ansd = dynamic_cast<FEElasticANSShellDomain*>(newsd);\n\t\tif (easd || ansd) {\n\t\t\twriteAverageElementValue<mat3ds, double>(dom, a, [](FEElement& el, int ip) {\n\t\t\t\tFEShellElementNew& se = static_cast<FEShellElementNew&>(el);\n\t\t\t\treturn se.m_E[ip];\n\t\t\t\t}, getJfromE);\n\t\t}\n\t\telse {\n\t\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\treturn (pt ? pt->m_J : 0.0);\n\t\t\t\t});\n\t\t}\n\t\treturn true;\n\t}\n\telse return false;\n\n\treturn true;\n}\n\nbool FEPlotSPRRelativeVolume::Save(FEDomain& dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\twriteSPRElementValue(solidDomain, a, [](const FEMaterialPoint& mp) {\n\t\t\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\treturn (pt ? pt->m_J : 0.0);\n\t\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool FEPlotShellRelativeVolume::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEShellDomain* sd = dynamic_cast<FEShellDomain*>(&dom);\n\tif (sd == nullptr) return false;\n\n\t// a filter to get J from a strain tensor\n\tauto getJfromE = [](const mat3ds& E) {\n\t\tmat3ds C = mat3dd(1) + E * 2;\n\t\treturn sqrt(C.det());\n\t};\n\n\tFEShellDomainNew* newsd = dynamic_cast<FEShellDomainNew*>(sd);\n\tFEElasticEASShellDomain* easd = dynamic_cast<FEElasticEASShellDomain*>(newsd);\n\tFEElasticANSShellDomain* ansd = dynamic_cast<FEElasticANSShellDomain*>(newsd);\n\tif (easd || ansd) {\n\t\twriteAverageElementValue<mat3ds, double>(dom, a, [](FEElement& el, int ip) {\n\t\t\tFEShellElementNew& se = static_cast<FEShellElementNew&>(el);\n\t\t\treturn se.m_E[ip];\n\t\t\t}, getJfromE);\n\t}\n\telse {\n\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\treturn (pt ? pt->m_J : 0.0);\n\t\t\t});\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFiberStretch::SetFilter(const char* szfilter)\n{\n\tm_matComp = szfilter;\n\treturn true;\n}\n\nbool FEPlotFiberStretch::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (m_matComp.empty() == false)\n\t{\n\t\tpme = dynamic_cast<FEElasticMaterial*>(pme->GetProperty(m_matComp.c_str()));\n\t\tif (pme == nullptr) return false;\n\t}\n\n\t// get the fiber property\n\tFEVec3dValuator* vec = dynamic_cast<FEVec3dValuator*>(pme->GetProperty(\"fiber\"));\n\tif (vec == 0) return false;\n\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\twriteAverageElementValue<double>(dom, a, [&](const FEMaterialPoint& mp) -> double { \n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\tmat3d Q = pme->GetLocalCS(mp);\n\t\tvec3d a0 = vec->unitVector(mp);\n\t\tvec3d ar = Q * a0;\n\t\tmat3d F = ep.m_F;\n\t\tvec3d a = F*ar;\n\t\treturn a.norm(); \n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nclass FEFiberVector\n{\npublic:\n\tFEFiberVector(FEMaterial* pm, FEVec3dValuator& vec) : m_pm(pm), m_vec(vec) {}\n\tvec3d operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<const FEElasticMaterialPoint>();\n\t\tif (pt)\n\t\t{\n\t\t\tmat3d Q = m_pm->GetLocalCS(mp);\n\t\t\tmat3d F = pt->m_F;\n\t\t\tvec3d a0 = m_vec.unitVector(mp);\n\t\t\tvec3d ar = Q * a0;\n\t\t\tvec3d a = F * ar; a.unit();\n\t\t\treturn a;\n\t\t}\n\t\telse\n\t\t\treturn m_vec(mp);\n\t}\nprivate:\n\tFEMaterial*\t\tm_pm;\n\tFEVec3dValuator&\tm_vec;\n};\n\n\nFEPlotFiberVector::FEPlotFiberVector(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) \n{\n}\n\nbool FEPlotFiberVector::SetFilter(const char* szfilter)\n{\n\tm_matComp = szfilter;\n\treturn true;\n}\n\nbool FEPlotFiberVector::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (m_matComp.empty() == false)\n\t{\n\t\tpme = dynamic_cast<FEElasticMaterial*>(pme->GetProperty(m_matComp.c_str()));\n\t\tif (pme == nullptr) return false;\n\t}\n\n\t// get the fiber property\n\tFEVec3dValuator* vec = dynamic_cast<FEVec3dValuator*>(pme->GetProperty(\"fiber\"));\n\tif (vec == 0) return false;\n\n\twriteAverageElementValue<vec3d, vec3d>(dom, a, FEFiberVector(pme, *vec), [](const vec3d& r) -> vec3d { vec3d n(r); n.unit(); return n; });\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n\nbool FEPlotMaterialAxes::SetFilter(const char* szfilter)\n{\n\tm_matComp = szfilter;\n\treturn true;\n}\n\nbool FEPlotMaterialAxes::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (m_matComp.empty() == false)\n\t{\n\t\tpme = dynamic_cast<FEElasticMaterial*>(pme->GetProperty(m_matComp.c_str()));\n\t\tif (pme == nullptr) return false;\n\t}\n\n\tint BE = dom.Elements();\n\tfor (int i = 0; i<BE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\t// I cannot average the material axes since the average may not be orthogonal\n\t\t// Until I find a better option, I'll just export the first integration point.\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\tmat3d Q = pme->GetLocalCS(mp);\n\t\ta << Q;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// TODO: The factor Jm13 is not used. This doesn't look correct\nclass FEDevFiberStretch\n{\npublic:\n\tFEDevFiberStretch(FEElasticMaterial* mat) : m_mat(mat) {}\npublic:\n\tdouble operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\tmat3d Q = m_mat->GetLocalCS(mp);\n\n\t\t// get the material fiber axis\n\t\tvec3d r0 = Q.col(0);\n\n\t\t// apply deformation\n\t\tvec3d r = pt.m_F*r0;\n\n\t\t// calculate the deviatoric fiber stretch\n\t\tdouble lam = r.norm();\n\t\treturn lam;\n\t}\n\nprivate:\n\tFEElasticMaterial*\tm_mat;\n};\n\nbool FEPlotDevFiberStretch::SetFilter(const char* szfilter)\n{\n\tm_matComp = szfilter;\n\treturn true;\n}\n\nbool FEPlotDevFiberStretch::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (m_matComp.empty() == false)\n\t{\n\t\tpme = dynamic_cast<FEElasticMaterial*>(pme->GetProperty(m_matComp.c_str()));\n\t\tif (pme == nullptr) return false;\n\t}\n\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\tFEDevFiberStretch lam(pme);\n\twriteAverageElementValue<double>(dom, a, lam);\n\treturn true;\n}\n\n\n//=============================================================================\n// Principal components of stress\n\nclass FEPrincStresses\n{\npublic:\n\tmat3dd operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\tconst mat3ds& s = ep.m_s;\n\t\tdouble l[3];\n\t\ts.exact_eigen(l);\n\t\treturn mat3dd(l[0], l[1], l[2]);\n\t}\n};\n\nbool FEPlotSPRPrincStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\n\t// get the domain\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\twriteSPRElementValueMat3dd(sd, a, FEPrincStresses());\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotDeformationGradient::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\twriteAverageElementValue<mat3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\treturn pt.m_F;\n\t});\n\n\treturn true;\n}\n\n//=============================================================================\n//! Store the average Lagrangian strain\nclass FELagrangeStrain\n{\npublic:\n\tmat3ds operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tif (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n\n\t\tmat3d C = pt->RightCauchyGreen();\n\t\tmat3ds E = ((C - mat3dd(1.0))*0.5).sym();\n\t\treturn E;\n\t}\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotLagrangeStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\treturn pt.Strain();\n\t\t\t});\n\t}\n\telse if (dom.Class() == FE_DOMAIN_SHELL)\n\t{\n\t\tFEShellDomain* sd = dynamic_cast<FEShellDomain*>(&dom); assert(sd);\n\n\t\tFEElasticEASShellDomain* easd = dynamic_cast<FEElasticEASShellDomain*>(&dom);\n\t\tFEElasticANSShellDomain* ansd = dynamic_cast<FEElasticANSShellDomain*>(&dom);\n\t\tif (easd || ansd)\n\t\t{\n\t\t\twriteAverageElementValue<mat3ds>(dom, a, [](FEElement& el, int ip) {\n\t\t\t\tFEShellElementNew& se = static_cast<FEShellElementNew&>(el);\n\t\t\t\treturn se.m_E[ip];\n\t\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\treturn pt.Strain();\n\t\t\t\t});\n\t\t}\n\t}\n\telse return false;\n\n\treturn true;\n}\n\nbool FEPlotShellStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tFEShellDomain* sd = dynamic_cast<FEShellDomain*>(&dom);\n\tif (sd == nullptr) return false;\n\n\tFEElasticEASShellDomain* easd = dynamic_cast<FEElasticEASShellDomain*>(&dom);\n\tFEElasticANSShellDomain* ansd = dynamic_cast<FEElasticANSShellDomain*>(&dom);\n\tif (easd || ansd)\n\t{\n\t\twriteAverageElementValue<mat3ds>(dom, a, [](FEElement& el, int ip) {\n\t\t\tFEShellElementNew& se = static_cast<FEShellElementNew&>(el);\n\t\t\treturn se.m_E[ip];\n\t\t\t});\n\t}\n\telse\n\t{\n\t\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\treturn pt.Strain();\n\t\t\t});\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotInfStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\tif (pme == nullptr) return false;\n\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\twriteAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t// displacement tensor\n\t\t\tmat3d U = pt.m_F - mat3dd(1.0);\n\n\t\t\t// evaluate small strain tensor eij = 0.5*(Uij + Uji)\n\t\t\tmat3ds e = U.sym();\n\n\t\t\treturn e;\n\t\t\t});\n\t}\n\telse return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSPRLagrangeStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\twriteSPRElementValueMat3ds(sd, a, FELagrangeStrain());\n\treturn true;\n}\n\nbool FEPlotSPRInfStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\twriteSPRElementValueMat3ds(sd, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t// displacement tensor\n\t\tmat3d U = pt.m_F - mat3dd(1.0);\n\n\t\t// evaluate small strain tensor eij = 0.5*(Uij + Uji)\n\t\tmat3ds e = U.sym();\n\n\t\treturn e;\n\t});\n\treturn true;\n}\n\n//=============================================================================\n//! Store the average Almansi tensor\nclass FEAlmansiStrain\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        \n        return pt->AlmansiStrain();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotAlmansiStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FEAlmansiStrain());\n    return true;\n}\n\n//=============================================================================\n//! Store the average right Cauchy Green tensor\nclass FERightCauchyGreen\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        \n        return pt->RightCauchyGreen();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotRightCauchyGreen::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FERightCauchyGreen());\n    return true;\n}\n\n//=============================================================================\n//! Store the average left Cauchy Green tensor\nclass FELeftCauchyGreen\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        \n        return pt->LeftCauchyGreen();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotLeftCauchyGreen::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FELeftCauchyGreen());\n    return true;\n}\n\n//=============================================================================\n//! Store the average right stretch\nclass FERightStretch\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            \n        return pt->RightStretch();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotRightStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FERightStretch());\n    return true;\n}\n\n//=============================================================================\n//! Store the average right stretch\nclass FELeftStretch\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            \n            return pt->LeftStretch();\n            }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotLeftStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FELeftStretch());\n    return true;\n}\n\n//=============================================================================\n//! Store the average right Hencky\nclass FERightHencky\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            \n        return pt->RightHencky();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotRightHencky::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FERightHencky());\n    return true;\n}\n\n//=============================================================================\n//! Store the average left Hencky\nclass FELeftHencky\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            \n        return pt->LeftHencky();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotLeftHencky::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FELeftHencky());\n    return true;\n}\n\n//=============================================================================\n//! Store the average rate of deformation\nclass FERateOfDeformation\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            \n            return pt->RateOfDeformation();\n            }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotRateOfDeformation::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FERateOfDeformation());\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store shell thicknesses\nbool FEPlotShellThickness::Save(FEDomain &dom, FEDataStream &a)\n{\n\tif (dom.Class() == FE_DOMAIN_SHELL)\n\t{\n\t\tFEShellDomain& sd = static_cast<FEShellDomain&>(dom);\n\t\tint NS = sd.Elements();\n\t\tfor (int i=0; i<NS; ++i)\n\t\t{\t\n\t\t\tFEShellElement& e = sd.Element(i);\n\t\t\tint n = e.Nodes();\n\t\t\tfor (int j=0; j<n; ++j) a << e.m_ht[j];\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! Store shell directors\nbool FEPlotShellDirector::Save(FEDomain &dom, FEDataStream &a)\n{\n    const int dof_X = GetFEModel()->GetDOFIndex(\"x\");\n    const int dof_Y = GetFEModel()->GetDOFIndex(\"y\");\n    const int dof_Z = GetFEModel()->GetDOFIndex(\"z\");\n    const int dof_SX = GetFEModel()->GetDOFIndex(\"sx\");\n    const int dof_SY = GetFEModel()->GetDOFIndex(\"sy\");\n    const int dof_SZ = GetFEModel()->GetDOFIndex(\"sz\");\n\tif (dom.Class() == FE_DOMAIN_SHELL)\n\t{\n\t\tif (dynamic_cast<FEElasticShellDomainOld*>(&dom))\n\t\t{\n\t\t\tFEShellDomainOld& sd = static_cast<FEShellDomainOld&>(dom);\n\t\t\tint NS = sd.Elements();\n\t\t\tFEMesh& mesh = *sd.GetMesh();\n\t\t\tfor (int i = 0; i<NS; ++i)\n\t\t\t{\n\t\t\t\tFEShellElementOld& e = sd.ShellElement(i);\n\t\t\t\tint n = e.Nodes();\n\t\t\t\tfor (int j = 0; j<n; ++j)\n\t\t\t\t{\n\t\t\t\t\tFENode& nj = mesh.Node(e.m_node[j]);\n\t\t\t\t\tvec3d D = e.m_D0[j] + nj.get_vec3d(dof_SX, dof_SY, dof_SZ);\n\t\t\t\t\ta << D;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n        else if (dynamic_cast<FESSIShellDomain*>(&dom))\n        {\n            FESSIShellDomain* bd = dynamic_cast<FESSIShellDomain*>(&dom);\n            int NS = bd->Elements();\n            FEMesh& mesh = *bd->GetMesh();\n            for (int i=0; i<NS; ++i)\n            {\n                FEShellElement& e = bd->Element(i);\n                int n = e.Nodes();\n                for (int j=0; j<n; ++j)\n                {\n                    FENode& nj = mesh.Node(e.m_node[j]);\n                    vec3d D;\n                    if (bd->m_bnodalnormals) {\n                        D = nj.m_d0;\n                    }\n                    else {\n                        D = e.m_d0[j];\n                    }\n                    D += nj.get_vec3d(dof_X, dof_Y, dof_Z) - nj.get_vec3d(dof_SX, dof_SY, dof_SZ);\n                    a << D;\n                }\n            }\n            return true;\n        }\n\t}\n\treturn false;\n}\n\n//=============================================================================\nFEPlotDamage::FEPlotDamage(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_comp = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotDamage::SetFilter(const char* szfilter)\n{\n    int nread = sscanf(szfilter, \"solid[%d]\", &m_comp);\n\treturn (nread == 1);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotDamage::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (m_comp == -1) {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& pt) {\n\t\t\tconst FEReactiveMaterialPoint* ppd = pt.ExtractData<FEReactiveMaterialPoint>();\n\t\t\tFEElasticMixtureMaterialPoint* pem = const_cast<FEElasticMixtureMaterialPoint*>(pt.ExtractData<FEElasticMixtureMaterialPoint>());\n\t\t\tFEMultigenerationMaterialPoint* pmg = const_cast<FEMultigenerationMaterialPoint*>(pt.ExtractData<FEMultigenerationMaterialPoint>());\n\t\t\tdouble D = 0.0;\n\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\telse if (pem) {\n\t\t\t\tfor (int k = 0; k < pem->Components(); ++k)\n\t\t\t\t{\n\t\t\t\t\tconst FEReactiveMaterialPoint* ppd = pem->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pmg) {\n\t\t\t\tfor (int k = 0; k < pmg->Components(); ++k)\n\t\t\t\t{\n\t\t\t\t\tFEReactiveMaterialPoint* ppd = pmg->GetPointData(k)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\t\tFEElasticMixtureMaterialPoint* pem = pmg->GetPointData(k)->ExtractData<FEElasticMixtureMaterialPoint>();\n\t\t\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\t\t\telse if (pem)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int l = 0; l < pem->Components(); ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEReactiveMaterialPoint* ppd = pem->GetPointData(l)->ExtractData<FEReactiveMaterialPoint>();\n\t\t\t\t\t\t\tif (ppd) D += (float)ppd->BrokenBonds();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n            return D;\n        });\n        return true;\n    }\n    else {\n        for (int i = 0; i < dom.Elements(); ++i)\n        {\n            FEElement& el = dom.ElementRef(i);\n\n            double D = 0.0;\n            for (int n = 0; n < el.GaussPoints(); ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n                if (mmp && (m_comp < mmp->Components()))\n                {\n                    FEReactiveMaterialPoint* dp = mmp->GetPointData(m_comp)->ExtractData<FEReactiveMaterialPoint>();\n                    if (dp) D += dp->BrokenBonds();\n                }\n            }\n            D /= (float)el.GaussPoints();\n\n            a << D;\n        }\n        return true;\n    }\n}\n\n//=============================================================================\nFEPlotIntactBondFraction::FEPlotIntactBondFraction(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_comp = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotIntactBondFraction::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotIntactBondFraction::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (m_comp == -1) {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& pt) {\n\t\t\tFEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n            double D = 0.0;\n            FEReactiveMaterialPoint* prm = mp.ExtractData<FEReactiveMaterialPoint>();\n            FEReactiveViscoelasticMaterialPoint* pve = mp.ExtractData<FEReactiveViscoelasticMaterialPoint>();\n            if (prm) D = (float) prm->IntactBonds();\n            else if (pve) {\n                const FEReactiveMaterialPoint* pr = pve->GetPointData(0)->ExtractData< FEReactiveMaterialPoint>();\n                if (pr) D = (float) pr->IntactBonds();\n            }\n            return D;\n        });\n        return true;\n    }\n    else {\n        for (int i = 0; i < dom.Elements(); ++i)\n        {\n            FEElement& el = dom.ElementRef(i);\n\n            float D = 0.0;\n            for (int n = 0; n < el.GaussPoints(); ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n                if (mmp && (m_comp < mmp->Components()))\n                {\n                    FEReactiveMaterialPoint* prm = mmp->GetPointData(m_comp)->ExtractData<FEReactiveMaterialPoint>();\n                    FEReactiveViscoelasticMaterialPoint* pve = mmp->GetPointData(m_comp)->ExtractData<FEReactiveViscoelasticMaterialPoint>();\n                    if (prm) D += (float) prm->IntactBonds();\n                    else if (pve) {\n                        FEReactiveMaterialPoint* pr = pve->GetPointData(0)->ExtractData<FEReactiveMaterialPoint>();\n                        if (pr) D += (float) pr->IntactBonds();\n                    }\n                }\n            }\n            D /= (float)el.GaussPoints();\n\n            a << D;\n        }\n        return true;\n    }\n}\n\n//=============================================================================\nFEPlotFatigueBondFraction::FEPlotFatigueBondFraction(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_comp = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFatigueBondFraction::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFatigueBondFraction::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (m_comp == -1) {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& pt) {\n\t\t\tFEMaterialPoint& mp = const_cast<FEMaterialPoint&>(pt);\n            float wf = 0.0;\n            FEReactiveMaterialPoint* prm = mp.ExtractData<FEReactiveMaterialPoint>();\n            FEReactiveViscoelasticMaterialPoint* pve = mp.ExtractData<FEReactiveViscoelasticMaterialPoint>();\n            if (prm) wf = (float) prm->FatigueBonds();\n            else if (pve) {\n                FEReactiveMaterialPoint* pr = pve->GetPointData(0)->ExtractData<FEReactiveMaterialPoint>();\n                if (pr) wf = (float) pr->FatigueBonds();\n            }\n            return wf;\n        });\n        return true;\n    }\n    else {\n        for (int i = 0; i < dom.Elements(); ++i)\n        {\n            FEElement& el = dom.ElementRef(i);\n\n            float wf = 0.0;\n            for (int n = 0; n < el.GaussPoints(); ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n                if (mmp && (m_comp < mmp->Components()))\n                {\n                    FEReactiveMaterialPoint* ppr = mmp->GetPointData(m_comp)->ExtractData<FEReactiveMaterialPoint>();\n                    FEReactiveViscoelasticMaterialPoint* pve = mmp->GetPointData(m_comp)->ExtractData<FEReactiveViscoelasticMaterialPoint>();\n                    if (ppr) wf += (float) ppr->FatigueBonds();\n                    else if (pve) {\n                        FEReactiveMaterialPoint* pr = pve->GetPointData(0)->ExtractData<FEReactiveMaterialPoint>();\n                        if (pr) wf += (float) pr->FatigueBonds();\n                    }\n                }\n            }\n            wf /= (float)el.GaussPoints();\n\n            a << wf;\n        }\n        return true;\n    }\n}\n\n//=============================================================================\nFEPlotYieldedBondFraction::FEPlotYieldedBondFraction(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_comp = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotYieldedBondFraction::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotYieldedBondFraction::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (m_comp == -1) {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n            float wy = 0.0;\n            const FEReactiveMaterialPoint* prp = mp.ExtractData<FEReactiveMaterialPoint>();\n            if (prp) wy = (float) prp->YieldedBonds();\n            return wy;\n        });\n        return true;\n    }\n    else {\n        for (int i = 0; i < dom.Elements(); ++i)\n        {\n            FEElement& el = dom.ElementRef(i);\n\n            float wy = 0.0;\n            for (int n = 0; n < el.GaussPoints(); ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n                if (mmp && (m_comp < mmp->Components()))\n                {\n                    const FEReactiveMaterialPoint* prp = mmp->GetPointData(m_comp)->ExtractData<FEReactiveMaterialPoint>();\n                    if (prp) wy += (float) prp->YieldedBonds();\n                }\n            }\n            wy /= (float)el.GaussPoints();\n\n            a << wy;\n        }\n        return true;\n    }\n}\n\n//=============================================================================\nFEPlotReactivePlasticityHeatSupply::FEPlotReactivePlasticityHeatSupply(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_comp = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotReactivePlasticityHeatSupply::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotReactivePlasticityHeatSupply::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (m_comp == -1) {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n            float Rhat = 0.0;\n            const FEReactivePlasticityMaterialPoint* prp = mp.ExtractData<FEReactivePlasticityMaterialPoint>();\n            const FEReactivePlasticDamageMaterialPoint* ppp = mp.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n            if (prp) Rhat = (float) prp->m_Rhat;\n            else if (ppp) Rhat = (float) ppp->m_Rhat;\n            return Rhat;\n        });\n        return true;\n    }\n    else {\n        for (int i = 0; i < dom.Elements(); ++i)\n        {\n            FEElement& el = dom.ElementRef(i);\n\n            float Rhat = 0.0;\n            for (int n = 0; n < el.GaussPoints(); ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n                if (mmp && (m_comp < mmp->Components()))\n                {\n                    const FEReactivePlasticityMaterialPoint* prp = mmp->GetPointData(m_comp)->ExtractData<FEReactivePlasticityMaterialPoint>();\n                    const FEReactivePlasticDamageMaterialPoint* ppp = mmp->GetPointData(m_comp)->ExtractData<FEReactivePlasticDamageMaterialPoint>();\n                    if (prp) Rhat += (float) prp->m_Rhat;\n                    else if (ppp) Rhat += (float) ppp->m_Rhat;\n                }\n            }\n            Rhat /= (float)el.GaussPoints();\n\n            a << Rhat;\n        }\n        return true;\n    }\n}\n\n\n//=============================================================================\nFEPlotOctahedralPlasticStrain::FEPlotOctahedralPlasticStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM)\n{\n    m_comp = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotOctahedralPlasticStrain::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotOctahedralPlasticStrain::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (m_comp == -1) {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n            float gp = 0.0;\n            const FEReactivePlasticityMaterialPoint* prp = mp.ExtractData<FEReactivePlasticityMaterialPoint>();\n            const FEReactivePlasticDamageMaterialPoint* ppp = mp.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n            if (prp && prp->m_gp.size()) gp = (float) prp->m_gp[0];\n            else if (ppp && ppp->m_gp.size()) gp = (float) ppp->m_gp[0];\n            return gp;\n        });\n        return true;\n    }\n    else {\n        for (int i = 0; i < dom.Elements(); ++i)\n        {\n            FEElement& el = dom.ElementRef(i);\n\n            float gp = 0.0;\n            for (int n = 0; n < el.GaussPoints(); ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMixtureMaterialPoint* mmp = mp.ExtractData< FEElasticMixtureMaterialPoint>();\n                if (mmp && (m_comp < mmp->Components()))\n                {\n                    const FEReactivePlasticityMaterialPoint* prp = mmp->GetPointData(m_comp)->ExtractData<FEReactivePlasticityMaterialPoint>();\n                    const FEReactivePlasticDamageMaterialPoint* ppp = mmp->GetPointData(m_comp)->ExtractData<FEReactivePlasticDamageMaterialPoint>();\n                    if (prp && prp->m_gp.size()) gp += (float) prp->m_gp[0];\n                    else if (ppp && ppp->m_gp.size()) gp += (float) ppp->m_gp[0];\n                }\n            }\n            gp /= (float)el.GaussPoints();\n\n            a << gp;\n        }\n        return true;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotMixtureVolumeFraction::Save(FEDomain &dom, FEDataStream &a)\n{\n\t// extract the mixture material\n\tFEMaterial* pmat = dom.GetMaterial();\n\tFEElasticMixture* pm = dynamic_cast<FEElasticMixture*>(pmat);\n\tif (pm == 0) return false;\n\n\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\t\treturn pt.m_w[0];\n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotUT4NodalStresses::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// make sure this is a UT4 domain\n\tFEUT4Domain* pd = dynamic_cast<FEUT4Domain*>(&dom);\n\tif (pd == 0) return false;\n\n\t// write the nodal values\n\twriteNodalValues<mat3ds>(dom, a, [=](int i) {\n\t\tFEUT4Domain::UT4NODE& n = pd->UT4Node(i);\n\t\treturn n.si;\n\t});\n\n\treturn true;\n}\n\n//==============================================================================\n//                  R I G I D   B O D Y   D A T A\n//==============================================================================\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidDisplacement::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n    \n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n\n\t// store the rigid body position\n\t// TODO: why do we not store displacement?\n\ta << rb.m_rt;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidVelocity::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    \n\t// store the rigid velocity\n\ta << rb.m_vt;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidAcceleration::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    \n\t// store rigid body acceleration\n\ta << rb.m_at;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidRotation::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n\tvec3d q = rb.GetRotation().GetRotationVector();\n    \n\t// store rotation vector\n\ta << q;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidAngularVelocity::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    \n\t// store rigid angular velocity\n\ta << rb.m_wt;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidAngularAcceleration::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    \n\t// store angular acceleration\n\ta << rb.m_alt;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidKineticEnergy::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    vec3d v = rb.m_vt;\n    double m = rb.m_mass;\n    vec3d w = rb.m_wt;\n\tmat3d Rt = rb.GetRotation().RotationMatrix();\n    mat3ds Jt = (Rt*rb.m_moi*Rt.transpose()).sym();\n    double ke = ((v*v)*m + w*(Jt*w))/2;\n    \n\t// store kinetic energy\n\ta << ke;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidLinearMomentum::Save(FEDomain& dom, FEDataStream& a)\n{\n    // get the rigid material\n    FEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n    // get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    \n    // store linear momentum (mass x velocity)\n    a << rb.m_vt*rb.m_mass;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidAngularMomentum::Save(FEDomain& dom, FEDataStream& a)\n{\n    // get the rigid material\n    FEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n    // get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n    \n    // store angular momentum (mass moment of inertia x angular velocity)\n\tmat3d Rt = rb.GetRotation().RotationMatrix();\n    mat3ds Jt = (Rt*rb.m_moi*Rt.transpose()).sym();\n    \n    a << Jt*rb.m_wt;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidEuler::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n\n\t// get the Euler angles\n\tdouble E[3];\n\tquat2euler(rb.GetRotation(), E);\n    \n\t// store Euler\n\ta << E[0] << E[1] << E[2];\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// TODO: I think this already gets stored somewhere\nbool FEPlotRigidRotationVector::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the rigid material\n\tFEMaterial* pm = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pm);\n\tif (prm == 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(prm->GetRigidBodyID());\n\n\t// get the rotation vector and angle\n\tvec3d r = rb.GetRotation().GetRotationVector();\n    \n\t// store rotation vector\n\ta << r;\n    \n\treturn true;\n}\n\n//=============================================================================\nbool FEPlotRigidReactionForce::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the material\n\tFEMaterial* pmat = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pmat);\n\tif (prm == 0) return false;\n\n\t// get the rigid body ID\n\tint nrid = prm->GetRigidBodyID();\n\tif (nrid < 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& rb = *fem.GetRigidBody(nrid);\n\n\ta << rb.m_Fr;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRigidReactionTorque::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the material\n\tFEMaterial* pmat = dom.GetMaterial();\n\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(pmat);\n\tif (prm == 0) return false;\n\n\t// get the rigid body ID\n\tint nrid = prm->GetRigidBodyID();\n\tif (nrid < 0) return false;\n\n\t// get the rigid body\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& rb = *fem.GetRigidBody(nrid);\n\n\ta << rb.m_Mr;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotStressError::Save(FEDomain& dom, FEDataStream& a)\n{\n\twriteRelativeError(dom, a, [](FEMaterialPoint& mp) {\n\t\tFEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\t\tmat3ds& s = ep->m_s;\n\t\tdouble v = s.effective_norm();\n\t\treturn v;\n\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFiberTargetStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMaterial* mat = dom.GetMaterial();\n\tFEPrestrainMaterial * pmat = dynamic_cast<FEPrestrainMaterial*>(mat);\n\tif (pmat == nullptr) return false;\n\n\t// get the elastic component\n\tFEProperty* prop = mat->FindProperty(\"elastic\");\n\tif (prop == nullptr) return false;\n\n\tFEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(prop->get(0));\n\tif (pme == 0) return false;\n\n\t// get the fiber property\n\tFEVec3dValuator* vec = dynamic_cast<FEVec3dValuator*>(pme->GetProperty(\"fiber\"));\n\tif (vec == 0) return false;\n\n\t// we're good so store the in-situ stretch\n\tint NE = dom.Elements();\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint nint = e.GaussPoints();\n\t\tdouble lam = 0.0;\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(j)->GetPointData(0);\n\t\t\tFEPrestrainMaterialPoint& pp = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\tmat3d Fp = pp.initialPrestrain();\n\t\t\tmat3d Q = mat->GetLocalCS(mp);\n\t\t\tvec3d a0 = vec->unitVector(mp);\n\t\t\tvec3d ar = Q * a0;\n\t\t\tvec3d a = Fp*ar;\n\t\t\tdouble lamp = a.norm();\n\n\t\t\tlam += lamp;\n\t\t}\n\t\tlam /= (double)nint;\n\n\t\ta << lam;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPreStrainStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMaterial* mat = dom.GetMaterial();\n\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(mat);\n\tif (pmat == 0) return false;\n\n\t// get the elastic component\n\tFEProperty* prop = mat->FindProperty(\"elastic\");\n\tif (prop == nullptr) return false;\n\n\tFEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(prop->get(0));\n\tif (pme== 0) return false;\n\n\t// get the fiber property\n\tFEVec3dValuator* vec = dynamic_cast<FEVec3dValuator*>(pme->GetProperty(\"fiber\"));\n\tif (vec == 0) return false;\n\n\tint NE = dom.Elements();\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint nint = e.GaussPoints();\n\t\tdouble lam = 0.0;\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(j)->GetPointData(0);\n\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\tFEPrestrainMaterialPoint& pp = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\tmat3d& F = pt.m_F;\n\t\t\tmat3d Fp = pp.prestrain();\n\t\t\tmat3d Ft = F*Fp;\n\n\t\t\tmat3d Q = mat->GetLocalCS(mp);\n\t\t\tvec3d a0 = vec->unitVector(mp);\n\t\t\tvec3d ar = Q * a0;\n\t\t\tvec3d a = Ft*ar;\n\n\t\t\tdouble lambda = a.norm();\n\t\t\tlam += lambda;\n\t\t}\n\t\tlam /= (double)nint;\n\n\t\ta << lam;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPreStrainStretchError::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMaterial* mat = dom.GetMaterial();\n\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(mat);\n\tif (pmat == 0) return false;\n\n\t// get the elastic component\n\tFEProperty* prop = mat->FindProperty(\"elastic\");\n\tif (prop == nullptr) return false;\n\n\tFEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(prop->get(0));\n\tif (pme == 0) return false;\n\n\t// get the fiber property\n\tFEVec3dValuator* vec = dynamic_cast<FEVec3dValuator*>(pme->GetProperty(\"fiber\"));\n\tif (vec == 0) return false;\n\n\tint NE = dom.Elements();\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint nint = e.GaussPoints();\n\t\tdouble err = 0.0;\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(j)->GetPointData(0);\n\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\tFEPrestrainMaterialPoint& pp = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\t// initial fiber vector\n\t\t\tmat3d Q = mat->GetLocalCS(mp);\n\t\t\tvec3d a0 = vec->unitVector(mp);\n\t\t\tvec3d ar = Q * a0;\n\n\t\t\t// target stretch\n\t\t\tmat3d Fp = pp.initialPrestrain();\n\t\t\tvec3d a = Fp*ar;\n\t\t\tdouble lam_trg = a.norm();\n\n\t\t\t// current stretch\n\t\t\tmat3d& F = pt.m_F;\n\t\t\tFp = pp.prestrain();\n\t\t\tmat3d Ft = F*Fp;\n\t\t\ta = Ft*ar;\n\n\t\t\tdouble lam_cur = a.norm();\n\n\t\t\terr += fabs(lam_cur / lam_trg - 1.0);\n\t\t}\n\t\terr /= (double)nint;\n\n\t\ta << err;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPreStrainCorrection::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMaterial* mat = dom.GetMaterial();\n\tFEElasticMaterial* solidMat = mat->ExtractProperty<FEElasticMaterial>();\n\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(solidMat);\n\tif (pmat == 0) return false;\n\n\tint NE = dom.Elements();\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint nint = e.GaussPoints();\n\t\tmat3d Fc; Fc.zero();\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(j)->GetPointData(0);\n\t\t\tFEPrestrainMaterialPoint& pt = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\tFc += pt.PrestrainCorrection();\n\t\t}\n\t\tFc *= 1.0 / (double)nint;\n\n\t\ta << Fc;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSPRPreStrainCorrection::Save(FEDomain& dom, FEDataStream& a)\n{\n\tconst int LUT[9][2] = { { 0,0 },{ 0,1 },{ 0,2 },{ 1,0 },{ 1,1 },{ 1,2 },{ 2,0 },{ 2,1 },{ 2,2 } };\n\n\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(dom.GetMaterial());\n\tif (pmat == 0) return false;\n\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\n\t// get the domain\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\tint NN = sd.Nodes();\n\tint NE = sd.Elements();\n\n\t// build the element data array\n\tvector< vector<double> > ED;\n\tED.resize(NE);\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& e = sd.Element(i);\n\t\tint nint = e.GaussPoints();\n\t\tED[i].assign(nint, 0.0);\n\t}\n\n\t// this array will store the results\n\tFESPRProjection map(sd);\n\tvector<double> val[9];\n\n\t// loop over stress components\n\tfor (int n = 0; n<9; ++n)\n\t{\n\t\t// fill the ED array\n\t\tfor (int i = 0; i<NE; ++i)\n\t\t{\n\t\t\tFESolidElement& el = sd.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j)->GetPointData(0);\n\t\t\t\tFEPrestrainMaterialPoint& pt = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\t\t\t\tconst mat3d& F = pt.PrestrainCorrection();\n\t\t\t\tED[i][j] = F(LUT[n][0], LUT[n][1]);\n\t\t\t}\n\t\t}\n\n\t\t// project to nodes\n\t\tmap.Project(ED, val[n]);\n\t}\n\n\t// copy results to archive\n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\ta << val[0][i];\n\t\ta << val[1][i];\n\t\ta << val[2][i];\n\t\ta << val[3][i];\n\t\ta << val[4][i];\n\t\ta << val[5][i];\n\t\ta << val[6][i];\n\t\ta << val[7][i];\n\t\ta << val[8][i];\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPreStrainCompatibility::Save(FEDomain& dom, FEDataStream& a)\n{\n\tconst int LUT[9][2] = { { 0,0 },{ 0,1 },{ 0,2 },{ 1,0 },{ 1,1 },{ 1,2 },{ 2,0 },{ 2,1 },{ 2,2 } };\n\n\t// make sure this is a pre-strain material\n\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(dom.GetMaterial());\n\tif (pmat == 0) return false;\n\n\t// For now, this is only available for solid domains\n\tif (dom.Class() != FE_DOMAIN_SOLID) return false;\n\n\t// get the domain\n\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\tint NE = sd.Elements();\n\n\t// STEP 1 - first we do an SPR recovery of the pre-strain gradient\n\n\t// build the element data array\n\tvector< vector<double> > ED;\n\tED.resize(NE);\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& e = sd.Element(i);\n\t\tint nint = e.GaussPoints();\n\t\tED[i].assign(nint, 0.0);\n\t}\n\n\t// this array will store the results\n\tFESPRProjection map(sd);\n\tvector<double> val[9];\n\n\t// create a global-to-local node list\n\tFEMesh& mesh = *dom.GetMesh();\n\tvector<int> g2l; g2l.assign(mesh.Nodes(), -1);\n\tint nn = 0;\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = sd.Element(i);\n\t\tint neln = el.Nodes();\n\t\tfor (int j = 0; j<neln; ++j)\n\t\t{\n\t\t\tif (g2l[el.m_node[j]] == -1) g2l[el.m_node[j]] = nn++;\n\t\t}\n\t}\n\n\t// loop over tensor components\n\tfor (int n = 0; n<9; ++n)\n\t{\n\t\t// fill the ED array\n\t\tfor (int i = 0; i<NE; ++i)\n\t\t{\n\t\t\tFESolidElement& el = sd.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j)->GetPointData(0);\n\t\t\t\tFEPrestrainMaterialPoint& pt = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\t\t\t\tmat3d Fp = pt.prestrain();\n\t\t\t\tED[i][j] = Fp(LUT[n][0], LUT[n][1]);\n\t\t\t}\n\t\t}\n\n\t\t// project to nodes\n\t\tmap.Project(ED, val[n]);\n\t}\n\n\t// STEP 2 - now we calculate the gradient of the nodal values at the integration points\n\tvector<double> vn(FEElement::MAX_NODES);\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = sd.Element(i);\n\t\tint neln = el.Nodes();\n\t\tint nint = el.GaussPoints();\n\n\t\tvector<mat3d> gF[3];\n\t\tgF[0].assign(nint, mat3dd(0));\n\t\tgF[1].assign(nint, mat3dd(0));\n\t\tgF[2].assign(nint, mat3dd(0));\n\t\tfor (int n = 0; n<9; ++n)\n\t\t{\n\t\t\t// get the nodal values\n\t\t\tfor (int m = 0; m<neln; ++m) vn[m] = val[n][g2l[el.m_node[m]]];\n\n\t\t\t// calculate the gradient at the integration points\n\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t{\n\t\t\t\tvec3d g = sd.gradient(el, vn, j);\n\n\t\t\t\tgF[0][j](LUT[n][0], LUT[n][1]) = g.x;\n\t\t\t\tgF[1][j](LUT[n][0], LUT[n][1]) = g.y;\n\t\t\t\tgF[2][j](LUT[n][0], LUT[n][1]) = g.z;\n\t\t\t}\n\t\t}\n\n\t\tdouble c = 0.0;\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tmat3d C;\n\t\t\tC(0, 0) = gF[1][j](0, 2) - gF[2][j](0, 1);\n\t\t\tC(0, 1) = gF[1][j](1, 2) - gF[2][j](1, 1);\n\t\t\tC(0, 2) = gF[1][j](2, 2) - gF[2][j](2, 1);\n\n\t\t\tC(1, 0) = gF[2][j](0, 0) - gF[0][j](0, 2);\n\t\t\tC(1, 1) = gF[2][j](1, 0) - gF[0][j](1, 2);\n\t\t\tC(1, 2) = gF[2][j](2, 0) - gF[0][j](2, 2);\n\n\t\t\tC(2, 0) = gF[0][j](0, 1) - gF[1][j](0, 0);\n\t\t\tC(2, 1) = gF[0][j](1, 1) - gF[1][j](1, 0);\n\t\t\tC(2, 2) = gF[0][j](2, 1) - gF[1][j](2, 0);\n\n\t\t\tc += sqrt(C.dotdot(C));\n\t\t}\n\t\tc /= (double)nint;\n\n\t\t// store the compatibility\n\t\ta << c;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteDomain* pdiscreteDomain = dynamic_cast<FEDiscreteDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteDomain& discreteDomain = *pdiscreteDomain;\n\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\t\t\n\t\tvec3d ra0 = mesh.Node(el.m_node[0]).m_r0;\n\t\tvec3d ra1 = mesh.Node(el.m_node[0]).m_rt;\n\t\tvec3d rb0 = mesh.Node(el.m_node[1]).m_r0;\n\t\tvec3d rb1 = mesh.Node(el.m_node[1]).m_rt;\n\n\t\tdouble L0 = (rb0 - ra0).norm();\n\t\tdouble Lt = (rb1 - ra1).norm();\n\n\t\tdouble l = Lt / L0;\n\t\ta << l;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementElongation::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteDomain* pdiscreteDomain = dynamic_cast<FEDiscreteDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteDomain& discreteDomain = *pdiscreteDomain;\n\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\t\t\n\t\tvec3d ra0 = mesh.Node(el.m_node[0]).m_r0;\n\t\tvec3d ra1 = mesh.Node(el.m_node[0]).m_rt;\n\t\tvec3d rb0 = mesh.Node(el.m_node[1]).m_r0;\n\t\tvec3d rb1 = mesh.Node(el.m_node[1]).m_rt;\n\n\t\tdouble L0 = (rb0 - ra0).norm();\n\t\tdouble Lt = (rb1 - ra1).norm();\n\n\t\tdouble l = Lt - L0;\n\t\ta << l;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementPercentElongation::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteDomain* pdiscreteDomain = dynamic_cast<FEDiscreteDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteDomain& discreteDomain = *pdiscreteDomain;\n\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\t\t\n\t\tvec3d ra0 = mesh.Node(el.m_node[0]).m_r0;\n\t\tvec3d ra1 = mesh.Node(el.m_node[0]).m_rt;\n\t\tvec3d rb0 = mesh.Node(el.m_node[1]).m_r0;\n\t\tvec3d rb1 = mesh.Node(el.m_node[1]).m_rt;\n\n\t\tdouble L0 = (rb0 - ra0).norm();\n\t\tdouble Lt = (rb1 - ra1).norm();\n\n\t\tdouble l = (Lt - L0)/L0;\n\t\ta << l;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementDirection::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteDomain* pdiscreteDomain = dynamic_cast<FEDiscreteDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteDomain& discreteDomain = *pdiscreteDomain;\n\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\t\tvec3d ra = mesh.Node(el.m_node[0]).m_rt;\n\t\tvec3d rb = mesh.Node(el.m_node[1]).m_rt;\n\t\tvec3d e = (rb - ra); e.unit();\n\t\ta << e;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementLength::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteDomain* pdiscreteDomain = dynamic_cast<FEDiscreteDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteDomain& discreteDomain = *pdiscreteDomain;\n\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\t\tvec3d ra = mesh.Node(el.m_node[0]).m_rt;\n\t\tvec3d rb = mesh.Node(el.m_node[1]).m_rt;\n\t\tdouble L = (rb - ra).Length();\n\t\ta << L;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementForce::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteElasticDomain* pdiscreteDomain = dynamic_cast<FEDiscreteElasticDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteElasticDomain& discreteDomain = *pdiscreteDomain;\n\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\n\t\t// get the (one) material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\tFEDiscreteElasticMaterialPoint& dmp = *mp.ExtractData<FEDiscreteElasticMaterialPoint>();\n\n\t\t// write the force\n\t\ta << dmp.m_Ft;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementSignedForce::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteElasticDomain* pdiscreteDomain = dynamic_cast<FEDiscreteElasticDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteElasticDomain& discreteDomain = *pdiscreteDomain;\n\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\n\t\t// get the (one) material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\tFEDiscreteElasticMaterialPoint& dmp = *mp.ExtractData<FEDiscreteElasticMaterialPoint>();\n\n\t\tvec3d ra1 = dom.Node(el.m_lnode[0]).m_rt;\n\t\tvec3d rb1 = dom.Node(el.m_lnode[1]).m_rt;\n\t\tvec3d e = rb1 - ra1; e.unit();\n\n\t\tvec3d F = dmp.m_Ft;\n\n\t\tdouble Fm = F * e;\n\n\t\t// write the force\n\t\ta << Fm;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotDiscreteElementStrainEnergy::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDiscreteElasticDomain* pdiscreteDomain = dynamic_cast<FEDiscreteElasticDomain*>(&dom);\n\tif (pdiscreteDomain == nullptr) return false;\n\tFEDiscreteElasticDomain& discreteDomain = *pdiscreteDomain;\n\n\tFEDiscreteElasticMaterial* discreteMaterial = dynamic_cast<FEDiscreteElasticMaterial*>(discreteDomain.GetMaterial());\n\n\tint NE = discreteDomain.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEDiscreteElement& el = discreteDomain.Element(i);\n\n\t\t// get the (one) material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\tFEDiscreteElasticMaterialPoint& dmp = *mp.ExtractData<FEDiscreteElasticMaterialPoint>();\n\n\t\t// write the strain energy\n\t\ta << discreteMaterial->StrainEnergy(dmp);\n\t}\n\n\treturn true;\n\n}\n\n//=================================================================================================\nFEPlotContinuousDamage_::FEPlotContinuousDamage_(FEModel* fem, int n) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM)\n{\n\tm_propIndex = 0;\n\tm_comp = n;\n}\n\nbool FEPlotContinuousDamage_::SetFilter(const char* sz)\n{\n\tm_prop = sz;\n\treturn true;\n}\n\nbool FEPlotContinuousDamage_::Save(FEDomain& dom, FEDataStream& a)\n{\n\t// get the material\n\tFEMaterial* domMat = dom.GetMaterial();\n\tif (domMat == nullptr) return false;\n\n\t// get the fiber damage component\n\tFEDamageElasticFiber* mat = nullptr;\n\tif (m_prop.empty()) mat = dynamic_cast<FEDamageElasticFiber*>(domMat);\n\telse\n\t{\n\t\tParamString ps(m_prop.c_str());\n\t\tm_propIndex = ps.Index();\n\t\tmat = dynamic_cast<FEDamageElasticFiber*>(domMat->GetProperty(ps));\n\t}\n\tif (mat == nullptr) return false;\n\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tdouble D = 0.0;\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tFEMaterialPoint* pt = mp.GetPointData(m_propIndex);\n\t\t\tdouble Dj = mat->Damage(*pt, m_comp);\n\n\t\t\tD += Dj;\n\t\t}\n\t\tD /= (double)nint;\n\n\t\ta << D;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRVEgenerations::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\nbool FEPlotRVEgenerations::Save(FEDomain& dom, FEDataStream& a)\n{\n    int N = dom.Elements();\n    FEElasticMaterial* pmat = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n    FEReactiveViscoelasticMaterial* rvmat = nullptr;\n    FEUncoupledReactiveViscoelasticMaterial* rumat = nullptr;\n    if (m_comp > -1) {\n        FEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n        FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n        if (pmm) rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmm->GetMaterial(m_comp));\n        if (pum) rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pum->GetMaterial(m_comp));\n    }\n    else {\n        rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmat);\n        rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pmat);\n    }\n    if (rvmat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j)\n                bmf += rvmat->RVEGenerations(*el.GetMaterialPoint(j));\n            a << bmf/nint;\n        }\n    }\n    else if (rumat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j)\n                bmf += rumat->RVEGenerations(*el.GetMaterialPoint(j));\n            a << bmf/nint;\n        }\n    }\n    else {\n        return false;\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRVEbonds::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\nbool FEPlotRVEbonds::Save(FEDomain& dom, FEDataStream& a)\n{\n    int N = dom.Elements();\n    FEElasticMaterial* pmat = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n    FEReactiveViscoelasticMaterial* rvmat = nullptr;\n    FEUncoupledReactiveViscoelasticMaterial* rumat = nullptr;\n    if (m_comp > -1) {\n        FEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n        FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n        if (pmm) rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmm->GetMaterial(m_comp));\n        if (pum) rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pum->GetMaterial(m_comp));\n    }\n    else {\n        rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmat);\n        rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pmat);\n    }\n    if (rvmat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j)\n                bmf += rvmat->ReformingBondMassFraction(*rvmat->GetBondMaterialPoint(*el.GetMaterialPoint(j)));\n            a << bmf/nint;\n        }\n    }\n    else if (rumat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j)\n                bmf += rumat->ReformingBondMassFraction(*rumat->GetBondMaterialPoint(*el.GetMaterialPoint(j)));\n            a << bmf/nint;\n        }\n    }\n    else {\n        return false;\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRVErecruitment::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\nbool FEPlotRVErecruitment::Save(FEDomain& dom, FEDataStream& a)\n{\n    int N = dom.Elements();\n    FEElasticMaterial* pmat = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n    FEReactiveViscoelasticMaterial* rvmat = nullptr;\n    FEUncoupledReactiveViscoelasticMaterial* rumat = nullptr;\n    if (m_comp > -1) {\n        FEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n        FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n        if (pmm) rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmm->GetMaterial(m_comp));\n        if (pum) rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pum->GetMaterial(m_comp));\n    }\n    else {\n        rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmat);\n        rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pmat);\n    }\n    if (rvmat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j) {\n                FEMaterialPoint& mp = *rvmat->GetBondMaterialPoint(*el.GetMaterialPoint(j));\n                FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n                if (!pt.m_wv.empty()) bmf += pt.m_wv.back();\n                else bmf += 1.0;\n            }\n            a << bmf/nint;\n        }\n    }\n    else if (rumat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j) {\n                FEMaterialPoint& mp = *rvmat->GetBondMaterialPoint(*el.GetMaterialPoint(j));\n                FEReactiveVEMaterialPoint & pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n                if (!pt.m_wv.empty()) bmf += pt.m_wv.back();\n                else bmf += 1.0;\n            }\n            a << bmf/nint;\n        }\n    }\n    else {\n        return false;\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotRVEstrain::SetFilter(const char* szfilter)\n{\n    sscanf(szfilter, \"solid[%d]\", &m_comp);\n    return true;\n}\n\nbool FEPlotRVEstrain::Save(FEDomain& dom, FEDataStream& a)\n{\n    int N = dom.Elements();\n    FEElasticMaterial* pmat = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pmat == nullptr) return false;\n    FEReactiveViscoelasticMaterial* rvmat = nullptr;\n    FEUncoupledReactiveViscoelasticMaterial* rumat = nullptr;\n    if (m_comp > -1) {\n        FEElasticMixture* pmm = dynamic_cast<FEElasticMixture*>(pmat);\n        FEUncoupledElasticMixture* pum = dynamic_cast<FEUncoupledElasticMixture*>(pmat);\n        if (pmm) rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmm->GetMaterial(m_comp));\n        if (pum) rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pum->GetMaterial(m_comp));\n    }\n    else {\n        rvmat = dynamic_cast<FEReactiveViscoelasticMaterial*>(pmat);\n        rumat = dynamic_cast<FEUncoupledReactiveViscoelasticMaterial*>(pmat);\n    }\n    if (rvmat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j)\n                bmf += rvmat->ScalarStrain(*rvmat->GetBondMaterialPoint(*el.GetMaterialPoint(j)));\n            a << bmf/nint;\n        }\n    }\n    else if (rumat) {\n        for (int iel=0; iel<N; ++iel)\n        {\n            FEElement& el = dom.ElementRef(iel);\n            \n            int nint = el.GaussPoints();\n            double bmf = 0;\n            for (int j=0; j<nint; ++j)\n                bmf += rumat->ScalarStrain(*rumat->GetBondMaterialPoint(*el.GetMaterialPoint(j)));\n            a << bmf/nint;\n        }\n    }\n    else {\n        return false;\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nclass FEStrongBondSED\n{\npublic:\n    FEStrongBondSED(FEElasticMaterial* pm) : m_mat(pm) {}\n    double operator()(const FEMaterialPoint& mp)\n    {\n        return m_mat->StrongBondSED(const_cast<FEMaterialPoint&>(mp));\n    }\nprivate:\n    FEElasticMaterial*    m_mat;\n};\n\nbool FEPlotStrongBondSED::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEStrongBondSED W(pme);\n        writeAverageElementValue<double>(dom, a, W);\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEWeakBondSED\n{\npublic:\n    FEWeakBondSED(FEElasticMaterial* pm) : m_mat(pm) {}\n    double operator()(const FEMaterialPoint& mp)\n    {\n        return m_mat->WeakBondSED(const_cast<FEMaterialPoint&>(mp));\n    }\nprivate:\n    FEElasticMaterial*    m_mat;\n};\n\nbool FEPlotWeakBondSED::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEWeakBondSED W(pme);\n        writeAverageElementValue<double>(dom, a, W);\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEStrongBondDevSED\n{\npublic:\n    FEStrongBondDevSED(FEUncoupledMaterial* pm) : m_mat(pm) {}\n    double operator()(const FEMaterialPoint& mp)\n    {\n        return m_mat->StrongBondDevSED(const_cast<FEMaterialPoint&>(mp));\n    }\nprivate:\n    FEUncoupledMaterial*    m_mat;\n};\n\nbool FEPlotStrongBondDevSED::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEUncoupledMaterial* pme = dom.GetMaterial()->ExtractProperty<FEUncoupledMaterial>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEStrongBondDevSED W(pme);\n        writeAverageElementValue<double>(dom, a, W);\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nclass FEWeakBondDevSED\n{\npublic:\n    FEWeakBondDevSED(FEUncoupledMaterial* pm) : m_mat(pm) {}\n    double operator()(const FEMaterialPoint& mp)\n    {\n        return m_mat->WeakBondDevSED(const_cast<FEMaterialPoint&>(mp));\n    }\nprivate:\n    FEUncoupledMaterial*    m_mat;\n};\n\nbool FEPlotWeakBondDevSED::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEUncoupledMaterial* pme = dom.GetMaterial()->ExtractProperty<FEUncoupledMaterial>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEWeakBondDevSED W(pme);\n        writeAverageElementValue<double>(dom, a, W);\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotTrussStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFETrussDomain* td = dynamic_cast<FETrussDomain*>(&dom);\n\tif (td == nullptr) return false;\n\n\tfor (int i = 0; i < td->Elements(); ++i)\n\t{\n\t\tFETrussElement& el = td->Element(i);\n\t\ta << el.m_lam;\n\t}\n\treturn true;\n}\n\n//=============================================================================\n//! Store the average growth  Lagrangian strain\nclass FEGrowthLagrangeStrain\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n        const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n        if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        mat3d Fg = kp->m_Fg;\n        \n        mat3d C = Fg.transpose()*Fg;\n        mat3ds E = ((C - mat3dd(1.0))*0.5).sym();\n        return E;\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthLagrangeStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEKinematicGrowth* pme = dom.GetMaterial()->ExtractProperty<FEKinematicGrowth>();\n    if (pme == nullptr) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        writeAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n            const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n            if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n            const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n            if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            mat3d Fg = kp->m_Fg;\n            FEElasticMaterialPoint pe2;\n            pe2.m_F = Fg;\n            \n            return pe2.Strain();\n        });\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        FEShellDomain* sd = dynamic_cast<FEShellDomain*>(&dom); assert(sd);\n        \n        FEElasticEASShellDomain* easd = dynamic_cast<FEElasticEASShellDomain*>(&dom);\n        FEElasticANSShellDomain* ansd = dynamic_cast<FEElasticANSShellDomain*>(&dom);\n        if (easd || ansd)\n        {\n            return false;\n        }\n        else\n        {\n            writeAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n                const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n                if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n                const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n                if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n                mat3d Fg = kp->m_Fg;\n                FEElasticMaterialPoint pe2;\n                pe2.m_F = Fg;\n                \n                return pe2.Strain();\n            });\n        }\n    }\n    else return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthInfStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEKinematicGrowth* pme = dom.GetMaterial()->ExtractProperty<FEKinematicGrowth>();\n    if (pme == nullptr) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        writeAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n            const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n            if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n            const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n            if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n            mat3d Fg = kp->m_Fg;\n\n            // displacement tensor\n            mat3d U = Fg - mat3dd(1.0);\n            \n            // evaluate small strain tensor eij = 0.5*(Uij + Uji)\n            mat3ds e = U.sym();\n            \n            return e;\n        });\n    }\n    else return false;\n\n\treturn true;\n}\n\n//-------------------------------------------------------------------------------\nbool FEPlotBeamStress::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticBeamDomain* beam = dynamic_cast<FEElasticBeamDomain*>(&dom);\n\tif (beam == nullptr) return false;\n\n\tfor (int i = 0; i < beam->Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = beam->Element(i);\n\n\t\tvec3d t(0, 0, 0);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t\tt += mp.m_t;\n\t\t}\n\t\tt /= (double)nint;\n\t\ta << t;\n\t}\n\n\treturn true;\n}\n\n//-------------------------------------------------------------------------------\nbool FEPlotBeamReferenceStress::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticBeamDomain* beam = dynamic_cast<FEElasticBeamDomain*>(&dom);\n\tif (beam == nullptr) return false;\n\n\tfor (int i = 0; i < beam->Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = beam->Element(i);\n\n\t\tvec3d T(0, 0, 0);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\t\t\tquatd Ri = mp.m_Rt.Conjugate();\n\t\t\tT += Ri*mp.m_t;\n\t\t}\n\t\tT /= (double)nint;\n\t\ta << T;\n\t}\n\n\treturn true;\n}\n\n//-------------------------------------------------------------------------------\nbool FEPlotBeamStressCouple::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticBeamDomain* beam = dynamic_cast<FEElasticBeamDomain*>(&dom);\n\tif (beam == nullptr) return false;\n\n\tfor (int i = 0; i < beam->Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = beam->Element(i);\n\n\t\tvec3d m(0, 0, 0);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t\tm += mp.m_m;\n\t\t}\n\t\tm /= (double)nint;\n\t\ta << m;\n\t}\n\n\treturn true;\n}\n\n//-------------------------------------------------------------------------------\nbool FEPlotBeamReferenceStressCouple::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticBeamDomain* beam = dynamic_cast<FEElasticBeamDomain*>(&dom);\n\tif (beam == nullptr) return false;\n\n\tfor (int i = 0; i < beam->Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = beam->Element(i);\n\n\t\tvec3d M(0, 0, 0);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\t\t\tquatd Ri = mp.m_Rt.Conjugate();\n\t\t\tM += Ri*mp.m_m;\n\t\t}\n\t\tM /= (double)nint;\n\t\ta << M;\n\t}\n\n\treturn true;\n}\n\n//-------------------------------------------------------------------------------\nbool FEPlotBeamStrain::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticBeamDomain* beam = dynamic_cast<FEElasticBeamDomain*>(&dom);\n\tif (beam == nullptr) return false;\n\n\tfor (int i = 0; i < beam->Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = beam->Element(i);\n\n\t\tvec3d t(0, 0, 0);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t\tt += mp.m_Rt * mp.m_Gamma;\n\t\t}\n\t\tt /= (double)nint;\n\t\ta << t;\n\t}\n\n\treturn true;\n}\n\n//-------------------------------------------------------------------------------\nbool FEPlotBeamCurvature::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticBeamDomain* beam = dynamic_cast<FEElasticBeamDomain*>(&dom);\n\tif (beam == nullptr) return false;\n\n\tfor (int i = 0; i < beam->Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = beam->Element(i);\n\n\t\tvec3d t(0, 0, 0);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t\tt += mp.m_Rt*mp.m_Kappa;\n\t\t}\n\t\tt /= (double)nint;\n\t\ta << t;\n\t}\n\n\treturn true;\n}\n\n//=============================================================================\n//! Store the average right stretch\nclass FEGrowthRightStretch\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n        const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n        if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        mat3d Fg = kp->m_Fg;\n        FEElasticMaterialPoint pe2;\n        pe2.m_F = Fg;\n        \n        return pe2.RightStretch();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthRightStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEKinematicGrowth* pme = dom.GetMaterial()->ExtractProperty<FEKinematicGrowth>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FEGrowthRightStretch());\n    return true;\n}\n\n//=============================================================================\n//! Store the average right stretch\nclass FEGrowthLeftStretch\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n        const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n        if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        mat3d Fg = kp->m_Fg;\n        FEElasticMaterialPoint pe2;\n        pe2.m_F = Fg;\n        \n        return pe2.LeftStretch();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthLeftStretch::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEKinematicGrowth* pme = dom.GetMaterial()->ExtractProperty<FEKinematicGrowth>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FEGrowthLeftStretch());\n    return true;\n}\n\n//=============================================================================\n//! Store the average growth right Hencky\nclass FEGrowthRightHencky\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n        const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n        if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        mat3d Fg = kp->m_Fg;\n        FEElasticMaterialPoint pe2;\n        pe2.m_F = Fg;\n        \n        return pe2.RightHencky();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthRightHencky::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEKinematicGrowth* pme = dom.GetMaterial()->ExtractProperty<FEKinematicGrowth>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FEGrowthRightHencky());\n    return true;\n}\n\n//=============================================================================\n//! Store the average left Hencky\nclass FEGrowthLeftHencky\n{\npublic:\n    mat3ds operator()(const FEMaterialPoint& mp)\n    {\n        const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n        if (pe == nullptr) return mat3ds(0, 0, 0, 0, 0, 0);\n        const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n        if (kp == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n        mat3d Fg = kp->m_Fg;\n        FEElasticMaterialPoint pe2;\n        pe2.m_F = Fg;\n\n        return pe2.LeftHencky();\n    }\n};\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthLeftHencky::Save(FEDomain& dom, FEDataStream& a)\n{\n    FEKinematicGrowth* pme = dom.GetMaterial()->ExtractProperty<FEKinematicGrowth>();\n    if (pme == nullptr) return false;\n    writeAverageElementValue<mat3ds>(dom, a, FEGrowthLeftHencky());\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotGrowthRelativeVolume::Save(FEDomain &dom, FEDataStream& a)\n{\n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n            const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n            if (pe == nullptr) return 0.0;\n            const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n            return (kp ? kp->m_Fg.det() : 0.0);\n        });\n    }\n    else if (dom.Class() == FE_DOMAIN_SHELL)\n    {\n        // TODO: implement relative volume calculation for kinematic growth material in EAS and ANS shells\n        FEShellDomain* sd = dynamic_cast<FEShellDomain*>(&dom); assert(sd);\n        \n        FEShellDomainNew* newsd = dynamic_cast<FEShellDomainNew*>(sd);\n        FEElasticEASShellDomain* easd = dynamic_cast<FEElasticEASShellDomain*>(newsd);\n        FEElasticANSShellDomain* ansd = dynamic_cast<FEElasticANSShellDomain*>(newsd);\n        if (easd || ansd) {\n            return false;\n        }\n        else {\n            writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n                const FEElasticMaterialPoint* pe = mp.ExtractData<FEElasticMaterialPoint>();\n                if (pe == nullptr) return 0.0;\n                const FEKinematicMaterialPoint* kp = pe->ExtractData<FEKinematicMaterialPoint>();\n                return (kp ? kp->m_Fg.det() : 0.0);\n            });\n        }\n        return true;\n    }\n    else return false;\n    \n    return true;\n}\n\n\nbool FEPlotIdealGasPressure::Init()\n{\n\tFEModel* fem = GetFEModel();\n\tif (fem == nullptr) return false;\n\n\tfor (int i = 0; i < fem->ModelLoads(); ++i)\n\t{\n\t\tm_load = dynamic_cast<FEIdealGasPressure*>(fem->ModelLoad(i));\n\t\tif (m_load) return true;\n\t}\n\treturn (m_load != nullptr);\n}\n\nbool FEPlotIdealGasPressure::Save(FESurface& surf, FEDataStream& a)\n{\n\tif (m_binit == false)\n\t{\n\t\tif (!Init()) return false;\n\t\tm_binit = true;\n\t}\n\tif (m_load == nullptr) return false;\n\n\tif (m_load->GetSurface().GetFacetSet() == surf.GetFacetSet())\n\t{\n\t\ta << m_load->GetCurrentPressure();\n\t\treturn true;\n\t}\n\telse return false;\n}\n\nbool FEPlotBodyForce::Save(FEDomain& dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEModel* fem = GetFEModel();\n\t\tFESolidDomain& sd = static_cast<FESolidDomain&>(dom);\n\t\twriteAverageElementValue<vec3d>(dom, a, [&](const FEMaterialPoint& mp) {\n\t\t\tint NBL = fem->ModelLoads();\n\t\t\tvec3d bf(0, 0, 0);\n\t\t\tfor (int j = 0; j < NBL; ++j)\n\t\t\t{\n\t\t\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem->ModelLoad(j));\n\t\t\t\tFEMaterialPoint& pt = const_cast<FEMaterialPoint&>(mp);\n\t\t\t\tif (pbf && pbf->IsActive()) bf += pbf->force(pt);\n\t\t\t}\n\n\t\t\t// NOTE: We flip the sign because FEBio applies the negative of the body force.\n\t\t\t// see FEElasticSolidDomain::BodyForce\n\t\t\treturn -bf;\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool FEPlotEdgeContactGap::Save(FEEdge& edge, FEDataStream& a)\n{\n\tFEEdgeToSurfaceSlidingContactEdge* pe = dynamic_cast<FEEdgeToSurfaceSlidingContactEdge*>(&edge);\n\tif (pe == nullptr) return false;\n\n\tfor (int i = 0; i < pe->Nodes(); ++i)\n\t{\n\t\tFEE2SSlidingContactPoint& pt = pe->m_points[i];\n\t\ta << pt.m_gap;\n\t}\n\n\treturn true;\n}\n\nbool FEPlotTotalLinearMomentum::Save(FEDomain& dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\t\tif (pme == 0) return false;\n\n\t\tvec3d L(0,0,0);\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i < solidDomain.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\tvec3d& v = pt.m_v;\n\n\t\t\t\tdouble J0 = solidDomain.detJ0(el, n);\n\n\t\t\t\tL.x += v.x * J0 * w[n];\n\t\t\t\tL.y += v.y * J0 * w[n];\n\t\t\t\tL.z += v.z * J0 * w[n];\n\t\t\t}\n\t\t}\n\t\ta << L;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool FEPlotTotalAngularMomentum::Save(FEDomain& dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\t\tif (pme == 0) return false;\n\n\t\tvec3d J(0, 0, 0);\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i < solidDomain.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\tvec3d& r = mp.m_rt;\n\t\t\t\tvec3d& v = pt.m_v;\n\n\t\t\t\tvec3d j = r ^ v;\n\n\t\t\t\tdouble J0 = solidDomain.detJ0(el, n);\n\n\t\t\t\tJ.x += j.x * J0 * w[n];\n\t\t\t\tJ.y += j.y * J0 * w[n];\n\t\t\t\tJ.z += j.z * J0 * w[n];\n\t\t\t}\n\t\t}\n\t\ta << J;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\nbool FEPlotTotalEnergy::Save(FEDomain& dom, FEDataStream& a)\n{\n\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t{\n\t\tFEElasticMaterial* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial>();\n\t\tif (pme == 0) return false;\n\n\t\tdouble E = 0.0;\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i < solidDomain.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\t// strain energy\n\t\t\t\tdouble W = pme->StrainEnergyDensity(mp);\n\n\t\t\t\t// kinetic energy\n\t\t\t\tdouble D = pme->Density(mp);\n\t\t\t\tvec3d& v = pt.m_v;\n\t\t\t\tdouble K = 0.5 * (v * v) * D;\n\n\t\t\t\tdouble J0 = solidDomain.detJ0(el, n);\n\n\t\t\t\tE += (K + W) * J0 * w[n];\n\t\t\t}\n\t\t}\n\t\ta << E;\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "FEBioMech/FEBioMechPlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPlotData.h>\n#include <FECore/FEElement.h>\n#include <FECore/units.h>\n\n//=============================================================================\n//                            N O D E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Nodal displacements\n//!\nclass FEPlotNodeDisplacement : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodeDisplacement(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_LENGTH); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n};\n\n//! Nodal rotations\nclass FEPlotNodeRotation : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodeRotation(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_RADIAN); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n};\n\n\n//! Nodal shell displacements\nclass FEPlotNodeShellDisplacement : public FEPlotNodeData\n{\npublic:\n    FEPlotNodeShellDisplacement(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_LENGTH); }\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//! Nodal incremental displacement\nclass FEPlotNodeIncrementalDisplacement : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodeIncrementalDisplacement(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_LENGTH); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n\nprivate:\n\tstd::vector<vec3d> m_rp;\n};\n\n//-----------------------------------------------------------------------------\n//! Shell directors\nclass FEPlotNodalShellDirector : public FEPlotNodeData\n{\npublic:\n    FEPlotNodalShellDirector(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_LENGTH); }\n    bool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal velocities\n//!\nclass FEPlotNodeVelocity : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodeVelocity(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_VELOCITY); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal accelerations\n//!\nclass FEPlotNodeAcceleration : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodeAcceleration(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_ACCELERATION); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal reaction forces\nclass FEPlotNodeReactionForces : public FEPlotNodeData\n{\npublic:\n\tFEPlotNodeReactionForces(FEModel* pfem) : FEPlotNodeData(pfem, PLT_VEC3F, FMT_NODE) { SetUnits(UNIT_FORCE); }\n\tbool Save(FEMesh& m, FEDataStream& a);\n};\n\n//=============================================================================\n//                         S U R F A C E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Contact gap\n//!\nclass FEPlotContactGap : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotContactGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_LENGTH); }\n\tbool SetFilter(const char* sz) override;\n\tbool Save(FESurface& surf, FEDataStream& a) override;\nprivate:\n\tstd::string m_interfaceName;\n};\n\n//-----------------------------------------------------------------------------\n//! Vector gap\n//!\nclass FEPlotVectorGap : public FEPlotSurfaceData\n{\npublic:\n    FEPlotVectorGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_LENGTH); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Contact pressure\n//!\nclass FEPlotContactPressure : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotContactPressure(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n\tbool SetFilter(const char* sz) override;\n\tbool Save(FESurface& surf, FEDataStream& a) override;\nprivate:\n\tstd::string m_interfaceName;\n};\n\n//-----------------------------------------------------------------------------\n//! Contact traction\n//!\nclass FEPlotContactTraction : public FEPlotSurfaceData\n{\npublic:\n    FEPlotContactTraction(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n\tbool SetFilter(const char* sz) override;\n\tbool Save(FESurface& surf, FEDataStream& a) override;\nprivate:\n\tstd::string m_interfaceName;\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal contact gap\n//!\nclass FEPlotNodalContactGap : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotNodalContactGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_MULT){ SetUnits(UNIT_LENGTH); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal vector gap\n//!\nclass FEPlotNodalVectorGap : public FEPlotSurfaceData\n{\npublic:\n    FEPlotNodalVectorGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_MULT){ SetUnits(UNIT_LENGTH); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal contact pressure\n//!\nclass FEPlotNodalContactPressure : public FEPlotSurfaceData\n{\npublic:\n    FEPlotNodalContactPressure(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_MULT){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal contact traction\n//!\nclass FEPlotNodalContactTraction : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotNodalContactTraction(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_MULT){ SetUnits(UNIT_PRESSURE); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Surface traction\n//!\nclass FEPlotSurfaceTraction : public FEPlotSurfaceData\n{\npublic:\n    FEPlotSurfaceTraction(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal surface traction\n//!\nclass FEPlotNodalSurfaceTraction : public FEPlotSurfaceData\n{\npublic:\n    FEPlotNodalSurfaceTraction(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_MULT){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Stick status\n//!\nclass FEPlotStickStatus : public FEPlotSurfaceData\n{\npublic:\n    FEPlotStickStatus(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Contact force\n//!\nclass FEPlotContactForce : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotContactForce(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION){ SetUnits(UNIT_FORCE); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Contact area\n//!\nclass FEPlotContactArea : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotContactArea(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ SetUnits(UNIT_AREA); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Contact penalty parameter\nclass FEPlotContactPenalty : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotContactPenalty(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Contact status \nclass FEPlotContactStatus : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotContactStatus(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Mortar gap\nclass FEPlotMortarContactGap : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotMortarContactGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_NODE){ SetUnits(UNIT_LENGTH); }\n\tbool Save(FESurface& S, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Enclosed volume\n//!\nclass FEPlotEnclosedVolume : public FEPlotSurfaceData\n{\nprivate:\n    bool                m_binit;\n    vector<FEElement*>  m_elem;\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotEnclosedVolume(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ m_binit = true; SetUnits(UNIT_VOLUME);\n\t}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Enclosed volume change\n//!\nclass FEPlotEnclosedVolumeChange : public FEPlotSurfaceData\n{\nprivate:\n    bool                m_binit;\n    vector<FEElement*>  m_elem;\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotEnclosedVolumeChange(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ m_binit = true; SetUnits(UNIT_VOLUME);\n    }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Surface area\n//!\nclass FEPlotSurfaceArea : public FEPlotSurfaceData\n{\nprivate:\n    FEModel*            m_pfem;\n    bool                m_binit;\n    vector<FEElement*>  m_elem;\n    vector<vec3d>       m_area;\n    \npublic:\n    FEPlotSurfaceArea(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){ m_binit = true; SetUnits(UNIT_AREA);\t}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Surface facet area\n//!\nclass FEPlotFacetArea : public FEPlotSurfaceData\n{\nprivate:\n\tFEModel* m_pfem;\n\tbool                m_binit;\n\tvector<FEElement*>  m_elem;\n\tvector<vec3d>       m_area;\n\npublic:\n\tFEPlotFacetArea(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM) { m_binit = true; SetUnits(UNIT_AREA);\t}\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Scalar surface load\n//!\nclass FEPlotScalarSurfaceLoad : public FEPlotSurfaceData\n{\npublic:\n    FEPlotScalarSurfaceLoad(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Total reaction force on surface\nclass FEPlotNetSurfaceReactionForce : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotNetSurfaceReactionForce(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION) {}\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Total reaction moment on surface\nclass FEPlotNetSurfaceReactionMoment : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotNetSurfaceReactionMoment(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION) {}\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n\n//=============================================================================\n//\t\t\t\t\t\t\tD O M A I N   D A T A\n//=============================================================================\n//-----------------------------------------------------------------------------\n//! Velocity\nclass FEPlotElementVelocity : public FEPlotDomainData\n{\npublic:\n    FEPlotElementVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_VELOCITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Acceleration\nclass FEPlotElementAcceleration : public FEPlotDomainData\n{\npublic:\n    FEPlotElementAcceleration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_ACCELERATION); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element stresses\nclass FEPlotElementStress : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element stresses\nclass FEPlotElementPK2Stress : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementPK2Stress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element stresses\nclass FEPlotElementPK1Stress : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementPK1Stress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3F, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element stresses for mixture components\nclass FEPlotElementMixtureStress : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementMixtureStress(FEModel* pfem);\n\tbool SetFilter(const char* szfilter) override;\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n\tint\t\tm_comp;\n\tint\t\tm_mat;\n};\n\n//-----------------------------------------------------------------------------\n//! Element plastic yield stresses\nclass FEPlotElementPlasticYieldStress : public FEPlotDomainData\n{\npublic:\n    FEPlotElementPlasticYieldStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element yield stress based on Drucker shear stress criterion\nclass FEPlotElementDruckerShear : public FEPlotDomainData\n{\npublic:\n    FEPlotElementDruckerShear(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element yield stress based on Prager-Drucker criterion\nclass FEPlotElementPragerDruckerStress : public FEPlotDomainData\n{\npublic:\n    FEPlotElementPragerDruckerStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element yield stress based on Deshpande-Fleck criterion\nclass FEPlotElementDeshpandeFleckStress : public FEPlotDomainData\n{\npublic:\n    FEPlotElementDeshpandeFleckStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element uncoupled pressure\nclass FEPlotElementUncoupledPressure : public FEPlotDomainData\n{\npublic:\n    FEPlotElementUncoupledPressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element norm for Cauchy stress\nclass FEPlotElementsnorm : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementsnorm(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\nclass FEPlotStrainEnergyDensity : public FEPlotDomainData\n{\npublic:\n\tFEPlotStrainEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Deviatoric strain energy density\nclass FEPlotDevStrainEnergyDensity : public FEPlotDomainData\n{\npublic:\n\tFEPlotDevStrainEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Specific strain energy\nclass FEPlotSpecificStrainEnergy : public FEPlotDomainData\n{\npublic:\n\tFEPlotSpecificStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy density in mixture components\nclass FEPlotMixtureStrainEnergyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotMixtureStrainEnergyDensity(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n    int        m_mat;\n};\n\n//-----------------------------------------------------------------------------\n//! Deviatoric strain energy density in mixture components\nclass FEPlotMixtureDevStrainEnergyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotMixtureDevStrainEnergyDensity(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n    int        m_mat;\n};\n\n//-----------------------------------------------------------------------------\n//! Specific strain energy in mixture components\nclass FEPlotMixtureSpecificStrainEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotMixtureSpecificStrainEnergy(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n    int        m_mat;\n};\n\n//-----------------------------------------------------------------------------\n//! Kinetic energy density\nclass FEPlotKineticEnergyDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotKineticEnergyDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Mass density\nclass FEPlotDensity : public FEPlotDomainData\n{\npublic:\n\tFEPlotDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){ SetUnits(UNIT_DENSITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy\nclass FEPlotElementStrainEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotElementStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){ SetUnits(UNIT_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Kinetic energy\nclass FEPlotElementKineticEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotElementKineticEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){ SetUnits(UNIT_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Center of mass\nclass FEPlotElementCenterOfMass : public FEPlotDomainData\n{\npublic:\n    FEPlotElementCenterOfMass(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_LENGTH); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Linear momentum\nclass FEPlotElementLinearMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotElementLinearMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Angular momentum\nclass FEPlotElementAngularMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotElementAngularMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Stress power\nclass FEPlotElementStressPower : public FEPlotDomainData\n{\npublic:\n    FEPlotElementStressPower(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy at current time\nclass FEPlotCurrentElementStrainEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotCurrentElementStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Kinetic energy at current time\nclass FEPlotCurrentElementKineticEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotCurrentElementKineticEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Center of mass at current time\nclass FEPlotCurrentElementCenterOfMass : public FEPlotDomainData\n{\npublic:\n    FEPlotCurrentElementCenterOfMass(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){ SetUnits(UNIT_LENGTH); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Linear momentum at current time\nclass FEPlotCurrentElementLinearMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotCurrentElementLinearMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Angular momentum at current time\nclass FEPlotCurrentElementAngularMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotCurrentElementAngularMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Relative volume\nclass FEPlotRelativeVolume : public FEPlotDomainData\n{\npublic:\n\tFEPlotRelativeVolume(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotSPRRelativeVolume : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRRelativeVolume(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_NODE) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n// NOTE: Deprecated, but maintained for backward compatibility\nclass FEPlotShellRelativeVolume : public FEPlotDomainData\n{\npublic:\n\tFEPlotShellRelativeVolume(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Material fibers\nclass FEPlotFiberVector : public FEPlotDomainData\n{\npublic:\n\tFEPlotFiberVector(FEModel* pfem);\n\tbool SetFilter(const char* szfilter) override;\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\nprivate:\n\tstd::string\t\tm_matComp;\n};\n\n//-----------------------------------------------------------------------------\n//! Material axes\nclass FEPlotMaterialAxes : public FEPlotDomainData\n{\npublic:\n\tFEPlotMaterialAxes(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3F, FMT_ITEM){}\n\tbool SetFilter(const char* szfilter) override;\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\nprivate:\n\tstd::string\t\tm_matComp;\n};\n\n//-----------------------------------------------------------------------------\n//! fiber stretch\nclass FEPlotFiberStretch : public FEPlotDomainData\n{\npublic:\n\tFEPlotFiberStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool SetFilter(const char* szfilter) override;\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\nprivate:\n\tstd::string\t\tm_matComp;\n};\n\n//-----------------------------------------------------------------------------\n//! deviatoric fiber stretch\nclass FEPlotDevFiberStretch : public FEPlotDomainData\n{\npublic:\n\tFEPlotDevFiberStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool SetFilter(const char* szfilter) override;\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\nprivate:\n\tstd::string\t\tm_matComp;\n};\n\n//-----------------------------------------------------------------------------\n//! Shell thicknesses\nclass FEPlotShellThickness : public FEPlotDomainData\n{\npublic:\n\tFEPlotShellThickness(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_MULT){ SetUnits(UNIT_LENGTH); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Shell directors\nclass FEPlotShellDirector : public FEPlotDomainData\n{\npublic:\n\tFEPlotShellDirector(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_MULT){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element elasticity tensor\nclass FEPlotElementElasticity : public FEPlotDomainData\n{\npublic:\n    FEPlotElementElasticity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_TENS4FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element elasticity tensor\nclass FEPlotElementDevElasticity : public FEPlotDomainData\n{\npublic:\n    FEPlotElementDevElasticity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_TENS4FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Damage reduction factor\nclass FEPlotDamage : public FEPlotDomainData\n{\npublic:\n    FEPlotDamage(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n\tbool Save(FEDomain& m, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Intact bond fraction (fatigue)\nclass FEPlotIntactBondFraction : public FEPlotDomainData\n{\npublic:\n    FEPlotIntactBondFraction(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& m, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Fatigue bond fraction (fatigue)\nclass FEPlotFatigueBondFraction : public FEPlotDomainData\n{\npublic:\n    FEPlotFatigueBondFraction(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& m, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Yielded bond fraction (fatigue)\nclass FEPlotYieldedBondFraction : public FEPlotDomainData\n{\npublic:\n    FEPlotYieldedBondFraction(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& m, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Octahedral Plastic Strain\nclass FEPlotOctahedralPlasticStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotOctahedralPlasticStrain(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& m, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Reactive plasticity heat supply\nclass FEPlotReactivePlasticityHeatSupply : public FEPlotDomainData\n{\npublic:\n    FEPlotReactivePlasticityHeatSupply(FEModel* pfem);\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& m, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Mixture volume fraction\nclass FEPlotMixtureVolumeFraction : public FEPlotDomainData\n{\npublic:\n\tFEPlotMixtureVolumeFraction(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n\tbool Save(FEDomain& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that outputs the element nodal stresses for UT4 domains\nclass FEPlotUT4NodalStresses : public FEPlotDomainData\n{\npublic:\n\tFEPlotUT4NodalStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_NODE) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! class the projects stresses from integration points to nodes using\n//! SPR (superconvergergent patch recovery)\nclass FEPlotSPRStresses : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_NODE){ SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! class the projects stresses from integration points to nodes using\n//! SPR (superconvergergent patch recovery)\nclass FEPlotSPRLinearStresses : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRLinearStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_NODE){ SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! class that projects the principal stresses from integration points to nodes\n//! using the SPR projection method\nclass FEPlotSPRPrincStresses : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRPrincStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FD, FMT_NODE){ SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body displacement\nclass FEPlotRigidDisplacement : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidDisplacement(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_LENGTH); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body velocity\nclass FEPlotRigidVelocity : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_VELOCITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body acceleration\nclass FEPlotRigidAcceleration : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidAcceleration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_ACCELERATION); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body rotation\nclass FEPlotRigidRotation : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidRotation(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body angular velocity\nclass FEPlotRigidAngularVelocity : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidAngularVelocity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body angular acceleration\nclass FEPlotRigidAngularAcceleration : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidAngularAcceleration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body kinetic energy\nclass FEPlotRigidKineticEnergy : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidKineticEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_REGION) { SetUnits(UNIT_ENERGY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body linear momentum\nclass FEPlotRigidLinearMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotRigidLinearMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body angular momentum\nclass FEPlotRigidAngularMomentum : public FEPlotDomainData\n{\npublic:\n    FEPlotRigidAngularMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid Euler angles\nclass FEPlotRigidEuler : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidEuler(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_RADIAN); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rotation vector\nclass FEPlotRigidRotationVector : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidRotationVector(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the nodes\n//! TODO: This only works with tet10 and hex8 -domains\nclass FEPlotNodalStresses : public FEPlotDomainData\n{\npublic:\n\tFEPlotNodalStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_MULT){ SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the nodes\nclass FEPlotShellTopStress : public FEPlotDomainData\n{\npublic:\n    FEPlotShellTopStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the bottom shell nodes\nclass FEPlotShellBottomStress : public FEPlotDomainData\n{\npublic:\n    FEPlotShellBottomStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the nodes\nclass FEPlotShellTopNodalStresses : public FEPlotDomainData\n{\npublic:\n    FEPlotShellTopNodalStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_MULT){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the bottom shell nodes\nclass FEPlotShellBottomNodalStresses : public FEPlotDomainData\n{\npublic:\n    FEPlotShellBottomNodalStresses(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_MULT){ SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects Lagrange strains from integration points to the nodes\nclass FEPlotNodalStrains : public FEPlotDomainData\n{\npublic:\n    FEPlotNodalStrains(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_MULT){ SetUnits(UNIT_NONE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the nodes\nclass FEPlotShellTopStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotShellTopStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){ SetUnits(UNIT_NONE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects stresses from integration points to the bottom shell nodes\nclass FEPlotShellBottomStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotShellBottomStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){ SetUnits(UNIT_NONE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects Lagrange strains from integration points to the shell nodes\nclass FEPlotShellTopNodalStrains : public FEPlotDomainData\n{\npublic:\n    FEPlotShellTopNodalStrains(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_MULT){ SetUnits(UNIT_NONE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that projects Lagrange strains from integration points to the shell nodes\nclass FEPlotShellBottomNodalStrains : public FEPlotDomainData\n{\npublic:\n    FEPlotShellBottomNodalStrains(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_MULT){ SetUnits(UNIT_NONE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Deformation gradient\nclass FEPlotDeformationGradient : public FEPlotDomainData\n{\npublic:\n\tFEPlotDeformationGradient(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Lagrange strains\nclass FEPlotLagrangeStrain : public FEPlotDomainData\n{\npublic:\n\tFEPlotLagrangeStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! NOTE: Deprecated, but maintained for backward compatibility\nclass FEPlotShellStrain : public FEPlotDomainData\n{\npublic:\n\tFEPlotShellStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Almansi strain\nclass FEPlotAlmansiStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotAlmansiStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n// Infinitesimal strain\nclass FEPlotInfStrain : public FEPlotDomainData\n{\npublic:\n\tFEPlotInfStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Lagrange strains\nclass FEPlotSPRLagrangeStrain : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRLagrangeStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_NODE){}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//! SPR infinitesimal strains\nclass FEPlotSPRInfStrain : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRInfStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_NODE) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Right Cauchy-Green tensor\nclass FEPlotRightCauchyGreen : public FEPlotDomainData\n{\npublic:\n    FEPlotRightCauchyGreen(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Left Cauchy-Green tensor\nclass FEPlotLeftCauchyGreen : public FEPlotDomainData\n{\npublic:\n    FEPlotLeftCauchyGreen(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Right stretch\nclass FEPlotRightStretch : public FEPlotDomainData\n{\npublic:\n    FEPlotRightStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Left stretch\nclass FEPlotLeftStretch : public FEPlotDomainData\n{\npublic:\n    FEPlotLeftStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Right Hencky\nclass FEPlotRightHencky : public FEPlotDomainData\n{\npublic:\n    FEPlotRightHencky(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Left Hencky\nclass FEPlotLeftHencky : public FEPlotDomainData\n{\npublic:\n    FEPlotLeftHencky(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rate of deformation\nclass FEPlotRateOfDeformation : public FEPlotDomainData\n{\npublic:\n    FEPlotRateOfDeformation(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body reaction force\nclass FEPlotRigidReactionForce : public FEPlotDomainData\n{\npublic:\n\tFEPlotRigidReactionForce(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_FORCE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Rigid body reaction torque\nclass FEPlotRigidReactionTorque : public FEPlotDomainData\n{\npublic:\n    FEPlotRigidReactionTorque(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotStressError : public FEPlotDomainData\n{\npublic:\n\tFEPlotStressError(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that outputs the in-situ fiber stretch\nclass FEPlotFiberTargetStretch : public FEPlotDomainData\n{\npublic:\n\tFEPlotFiberTargetStretch(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that outputs the fiber stretch\nclass FEPlotPreStrainStretch : public FEPlotDomainData\n{\npublic:\n\tFEPlotPreStrainStretch(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that outputs the error in the fiber stretch\nclass FEPlotPreStrainStretchError : public FEPlotDomainData\n{\npublic:\n\tFEPlotPreStrainStretchError(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that outputs the pre-strain correction deformation gradient\nclass FEPlotPreStrainCorrection : public FEPlotDomainData\n{\npublic:\n\tFEPlotPreStrainCorrection(FEModel* fem) : FEPlotDomainData(fem, PLT_MAT3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Class that outputs the pre-strain correction deformation gradient\nclass FEPlotSPRPreStrainCorrection : public FEPlotDomainData\n{\npublic:\n\tFEPlotSPRPreStrainCorrection(FEModel* fem) : FEPlotDomainData(fem, PLT_MAT3F, FMT_NODE) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotPreStrainCompatibility : public FEPlotDomainData\n{\npublic:\n\tFEPlotPreStrainCompatibility(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotDiscreteElementStretch : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementStretch(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotDiscreteElementElongation : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementElongation(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotDiscreteElementPercentElongation : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementPercentElongation(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotDiscreteElementDirection : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementDirection(FEModel* fem) : FEPlotDomainData(fem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotDiscreteElementLength : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementLength(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotDiscreteElementForce : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementForce(FEModel* fem) : FEPlotDomainData(fem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_FORCE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotDiscreteElementSignedForce : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementSignedForce(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n\n//-----------------------------------------------------------------------------\nclass FEPlotDiscreteElementStrainEnergy : public FEPlotDomainData\n{\npublic:\n\tFEPlotDiscreteElementStrainEnergy(FEModel* fem) : FEPlotDomainData(fem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotContinuousDamage_ : public FEPlotDomainData\n{\npublic:\n\tFEPlotContinuousDamage_(FEModel* fem, int n);\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\n\tbool SetFilter(const char* sz) override;\nprivate:\n\tstd::string\tm_prop;\n\tint\t\t\tm_propIndex;\n\tint\t\t\tm_comp;\n};\n\nclass FEPlotContinuousDamage_D : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_D(FEModel* fem) : FEPlotContinuousDamage_(fem, 0) {}\n};\n\nclass FEPlotContinuousDamage_D1 : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_D1(FEModel* fem) : FEPlotContinuousDamage_(fem, 1) {}\n};\n\nclass FEPlotContinuousDamage_Ds : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_Ds(FEModel* fem) : FEPlotContinuousDamage_(fem, 2) {}\n};\n\nclass FEPlotContinuousDamage_D2 : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_D2(FEModel* fem) : FEPlotContinuousDamage_(fem, 3) {}\n};\n\nclass FEPlotContinuousDamage_D3 : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_D3(FEModel* fem) : FEPlotContinuousDamage_(fem, 4) {}\n};\n\nclass FEPlotContinuousDamage_P : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_P(FEModel* fem) : FEPlotContinuousDamage_(fem, 5) {}\n};\n\nclass FEPlotContinuousDamage_Psi0 : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_Psi0(FEModel* fem) : FEPlotContinuousDamage_(fem, 6) {}\n};\n\nclass FEPlotContinuousDamage_beta : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_beta(FEModel* fem) : FEPlotContinuousDamage_(fem, 7) {}\n};\n\nclass FEPlotContinuousDamage_gamma : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_gamma(FEModel* fem) : FEPlotContinuousDamage_(fem, 8) {}\n};\n\nclass FEPlotContinuousDamage_D2beta : public FEPlotContinuousDamage_ {\npublic: FEPlotContinuousDamage_D2beta(FEModel* fem) : FEPlotContinuousDamage_(fem, 9) {}\n};\n\n//-----------------------------------------------------------------------------\n//! Number of generations in reactive viscoelastic material point\nclass FEPlotRVEgenerations : public FEPlotDomainData\n{\npublic:\n    FEPlotRVEgenerations(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { m_comp = -1; }\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Reforming bond mass fraction in reactive viscoelastic material point\nclass FEPlotRVEbonds : public FEPlotDomainData\n{\npublic:\n    FEPlotRVEbonds(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { m_comp = -1; }\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Weak bond recruitment ratio in reactive viscoelastic material point\nclass FEPlotRVErecruitment : public FEPlotDomainData\n{\npublic:\n    FEPlotRVErecruitment(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { m_comp = -1; }\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Reactive viscoelastic strain measure for bond-breaking trigger and bond recruitment\nclass FEPlotRVEstrain : public FEPlotDomainData\n{\npublic:\n    FEPlotRVEstrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { m_comp = -1; }\n    bool SetFilter(const char* szfilter) override;\n    bool Save(FEDomain& dom, FEDataStream& a) override;\nprotected:\n    int        m_comp;\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy density of strong bonds in reactive viscoelastic material point\nclass FEPlotStrongBondSED : public FEPlotDomainData\n{\npublic:\n    FEPlotStrongBondSED(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy density of weak bonds in reactive viscoelastic material point\nclass FEPlotWeakBondSED : public FEPlotDomainData\n{\npublic:\n    FEPlotWeakBondSED(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy density of strong bonds in reactive viscoelastic material point\nclass FEPlotStrongBondDevSED : public FEPlotDomainData\n{\npublic:\n    FEPlotStrongBondDevSED(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Strain energy density of weak bonds in reactive viscoelastic material point\nclass FEPlotWeakBondDevSED : public FEPlotDomainData\n{\npublic:\n    FEPlotWeakBondDevSED(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! truss element stretch\nclass FEPlotTrussStretch : public FEPlotDomainData\n{\npublic:\n\tFEPlotTrussStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth deformation gradient (Kinematic Growth)\nclass FEPlotGrowthDeformationGradient : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthDeformationGradient(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3F, FMT_ITEM) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth  Lagrange strains\nclass FEPlotGrowthLagrangeStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthLagrangeStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n// Growth infinitesimal strain\nclass FEPlotGrowthInfStrain : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthInfStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) {}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth right stretch\nclass FEPlotGrowthRightStretch : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthRightStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth left stretch\nclass FEPlotGrowthLeftStretch : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthLeftStretch(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth right Hencky\nclass FEPlotGrowthRightHencky : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthRightHencky(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth left Hencky\nclass FEPlotGrowthLeftHencky : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthLeftHencky(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Growth relative volume\nclass FEPlotGrowthRelativeVolume : public FEPlotDomainData\n{\npublic:\n    FEPlotGrowthRelativeVolume(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n\n//-----------------------------------------------------------------------------\n//! beam element stress\nclass FEPlotBeamStress : public FEPlotDomainData\n{\npublic:\n\tFEPlotBeamStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! beam element stress in reference frame\nclass FEPlotBeamReferenceStress : public FEPlotDomainData\n{\npublic:\n\tFEPlotBeamReferenceStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! beam element stress couple\nclass FEPlotBeamStressCouple : public FEPlotDomainData\n{\npublic:\n\tFEPlotBeamStressCouple(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! beam element stress couple in reference frame\nclass FEPlotBeamReferenceStressCouple : public FEPlotDomainData\n{\npublic:\n\tFEPlotBeamReferenceStressCouple(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! beam element strain\nclass FEPlotBeamStrain : public FEPlotDomainData\n{\npublic:\n\tFEPlotBeamStrain(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! beam element curvature\nclass FEPlotBeamCurvature : public FEPlotDomainData\n{\npublic:\n\tFEPlotBeamCurvature(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n// plot the current pressure value of the FEIdealGasPressure load\nclass FEIdealGasPressure;\nclass FEPlotIdealGasPressure : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotIdealGasPressure(FEModel* fem) : FEPlotSurfaceData(fem, PLT_FLOAT, FMT_REGION) {}\n\tbool Save(FESurface& surf, FEDataStream& a) override;\n\nprivate:\n\tbool Init() override;\n\tbool m_binit = false;\n\tFEIdealGasPressure* m_load = nullptr;\n};\n\nclass FEPlotBodyForce : public FEPlotDomainData\n{\npublic:\n\tFEPlotBodyForce(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_FORCE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotTotalLinearMomentum : public FEPlotDomainData\n{\npublic:\n\tFEPlotTotalLinearMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_ENERGY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotTotalAngularMomentum : public FEPlotDomainData\n{\npublic:\n\tFEPlotTotalAngularMomentum(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_ENERGY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\nclass FEPlotTotalEnergy : public FEPlotDomainData\n{\npublic:\n\tFEPlotTotalEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_REGION) { SetUnits(UNIT_ENERGY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//=============================================================================\n//                            E D G E   D A T A\n//=============================================================================\n\nclass FEPlotEdgeContactGap : public FEPlotEdgeData\n{\npublic:\n\tFEPlotEdgeContactGap(FEModel* fem) : FEPlotEdgeData(fem, PLT_FLOAT, FMT_NODE) { SetUnits(UNIT_LENGTH); }\n\tbool Save(FEEdge& edge, FEDataStream& a);\n};\n"
  },
  {
    "path": "FEBioMech/FEBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBodyForce.h\"\n#include \"FESolidMaterial.h\"\n#include \"FEElasticDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEBodyForce::FEBodyForce(FEModel* pfem) : FEBodyLoad(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Work in progress! Working on integrating body loads as model loads\nvoid FEBodyForce::LoadVector(FEGlobalVector& R)\n{\n\tfor (int i = 0; i<Domains(); ++i)\n\t{\n\t\tFEDomain* dom = Domain(i);\n\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(dom);\n\t\tif (edom) edom->BodyForce(R, *this);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// NOTE: Work in progress! Working on integrating body loads as model loads\nvoid FEBodyForce::StiffnessMatrix(FELinearSystem& LS)\n{\n\tfor (int i = 0; i<Domains(); ++i)\n\t{\n\t\tFEDomain* dom = Domain(i);\n\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(dom);\n\t\tif (edom) edom->BodyForceStiffness(LS, *this);\n\t}\n}\n\ndouble FEBodyForce::divforce(FEMaterialPoint& pt)\n{ \n\treturn (stiffness(pt)).trace(); \n}\n"
  },
  {
    "path": "FEBioMech/FEBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FEBodyLoad.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body forces\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOMECH_API FEBodyForce : public FEBodyLoad\n{\npublic:\n\t//! constructor\n\tFEBodyForce(FEModel* pfem);\n\npublic:\n\t//! calculate the body force at a material point\n\tvirtual vec3d force(FEMaterialPoint& pt) = 0;\n\n\t//! calculate the divergence of the body force at a material point\n\t//! TODO: Is this used anywhere?\n\tvirtual double divforce(FEMaterialPoint& pt);\n\n\t//! calculate constribution to stiffness matrix\n\tvirtual mat3d stiffness(FEMaterialPoint& pt) = 0;\n\npublic:\n\tvoid LoadVector(FEGlobalVector& R) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEBondRecruitment.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBondRecruitment.h\"\n#include <FECore/log.h>\n#include <FECore/gamma.h>\n#include <math.h>\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentUser, FEBondRecruitment)\n    ADD_PROPERTY(m_brf, \"function\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentUser::FEBondRecruitmentUser(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_brf = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// User-defined loadcurve for bond recruitment function\ndouble FEBondRecruitmentUser::brf(FEMaterialPoint& mp, const double X)\n{\n    return m_brf->value(X);\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentPower, FEBondRecruitment)\n    ADD_PARAMETER(m_alpha, \"alpha\" )->setLongName(\"power exponent\");\n    ADD_PARAMETER(m_mu0  , \"mu0\"   )->setLongName(\"constant mu0\");\n    ADD_PARAMETER(m_mu1  , \"mu1\"   )->setLongName(\"power coefficient\");\n    ADD_PARAMETER(m_s    , FE_RANGE_GREATER(0.0)         , \"scale\" );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentPower::FEBondRecruitmentPower(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_alpha = 2;\n    m_mu0 = 1;\n    m_mu1 = 0;\n    m_s = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Power bond recruitment function\ndouble FEBondRecruitmentPower::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 0;\n    double alpha = m_alpha(mp);\n    double mu0 = m_mu0(mp);\n    double mu1 = m_mu1(mp);\n    double s = m_s(mp);\n\n    // this brf only admits positive values\n    if (X > 0)\n        brf = mu0 + mu1*pow(X/s,alpha);\n    \n    return brf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentExp, FEBondRecruitment)\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\" );\n    ADD_PARAMETER(m_mu0  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\"   );\n    ADD_PARAMETER(m_mu1  , \"mu1\"   );\n    ADD_PARAMETER(m_s    , FE_RANGE_GREATER(0.0)         , \"scale\" );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentExp::FEBondRecruitmentExp(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_alpha = 1;\n    m_mu0 = 1;\n    m_mu1 = 0;\n    m_s = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Exponential bond recruitment function\ndouble FEBondRecruitmentExp::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 0;\n    double alpha = m_alpha(mp);\n    double mu0 = m_mu0(mp);\n    double mu1 = m_mu1(mp);\n    double s = m_s(mp);\n\n    // this brf only admits positive values\n    if (X > 0)\n        brf = mu0*exp(mu1*pow(X/s,alpha));\n    \n    return brf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentPoly, FEBondRecruitment)\n    ADD_PARAMETER(m_mu0  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\"   );\n    ADD_PARAMETER(m_mu1  , \"mu1\"   );\n    ADD_PARAMETER(m_mu2  , \"mu2\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentPoly::FEBondRecruitmentPoly(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_mu0 = 1;\n    m_mu1 = 0;\n    m_mu2 = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Poly bond recruitment function\ndouble FEBondRecruitmentPoly::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 0;\n    double mu0 = m_mu0(mp);\n    double mu1 = m_mu1(mp);\n    double mu2 = m_mu2(mp);\n\n    // this brf only admits positive arguments\n    if (X > 0)\n        brf = mu0 + mu1*X + mu2*X*X;\n    \n    return brf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentLogNormal, FEBondRecruitment)\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER(0.0), \"mu\");\n    ADD_PARAMETER(m_sigma, FE_RANGE_GREATER(0.0), \"sigma\");\n    ADD_PARAMETER(m_max  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"max\")->setLongName(\"max recruitment increase\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentLogNormal::FEBondRecruitmentLogNormal(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_mu = 1;\n    m_sigma = 1;\n    m_max = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Lognormal damage cumulative distribution function\ndouble FEBondRecruitmentLogNormal::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 0;\n    double mu = m_mu(mp);\n    double sigma = m_sigma(mp);\n    double maxr = m_max(mp);\n    // this brf only admits positive values\n    if (X >= 0)\n        brf = 1.0 + maxr*(0.5*erfc(-log(X/mu)/sigma/sqrt(2.)));\n    \n    return brf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentWeibull, FEBondRecruitment)\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\");\n    ADD_PARAMETER(m_ploc, \"ploc\");\n    ADD_PARAMETER(m_max  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"max\")->setLongName(\"max recruitment increase\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentWeibull::FEBondRecruitmentWeibull(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_alpha = 1;\n    m_mu = 1;\n    m_ploc = 0;\n    m_max = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Weibull damage cumulative distribution function\ndouble FEBondRecruitmentWeibull::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 0;\n    double alpha = m_alpha(mp);\n    double mu = m_mu(mp);\n    double ploc = m_ploc(mp);\n    double maxr = m_max(mp);\n\n    // this brf only admits positive values\n    if (X > ploc)\n        brf = 1 + maxr*(1 - exp(-pow((X-ploc)/mu,alpha)));\n    \n    return brf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentPQP, FEBondRecruitment)\n    ADD_PARAMETER(m_mumin, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mumin\");\n    ADD_PARAMETER(m_mumax, \"mumax\");\n    ADD_PARAMETER(m_max  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"max\")->setLongName(\"max recruitment increase\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentPQP::FEBondRecruitmentPQP(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_mumin = 0;\n    m_mumax = 1;\n    m_max = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEBondRecruitmentPQP::Validate()\n{\n    return FEBondRecruitment::Validate();\n}\n\n//-----------------------------------------------------------------------------\n// Piecewise S-shaped quintic polynomial damage bond recruitment function\ndouble FEBondRecruitmentPQP::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 0;\n    double mumin = m_mumin(mp);\n    double mumax = m_mumax(mp);\n    double maxr = m_max(mp);\n\n    if (X <= mumin) brf = 1;\n    else if (X >= mumax) brf = 1+maxr;\n    else\n    {\n        double x = (X - mumin)/(mumax - mumin);\n        brf = 1 + maxr*(pow(x,3)*(10 - 15*x + 6*x*x));\n    }\n    \n    return brf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRecruitmentGamma, FEBondRecruitment)\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER(0), \"alpha\");\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"   );\n    ADD_PARAMETER(m_max  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"max\")->setLongName(\"max recruitment increase\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRecruitmentGamma::FEBondRecruitmentGamma(FEModel* pfem) : FEBondRecruitment(pfem)\n{\n    m_alpha = 2;\n    m_mu = 4;\n    m_max = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Gamma damage cumulative distribution function\ndouble FEBondRecruitmentGamma::brf(FEMaterialPoint& mp, const double X)\n{\n    double brf = 1;\n    double alpha = m_alpha(mp);\n    double mu = m_mu(mp);\n    double maxr = m_max(mp);\n\n    // this CDF only admits positive values\n    double scale = gamma_inc_Q(alpha,0);\n    if (X > 0)\n        brf = 1 + maxr*gamma_inc_P(alpha,X/mu)/scale;\n    \n    return brf;\n}\n"
  },
  {
    "path": "FEBioMech/FEBondRecruitment.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/FEFunction1D.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Virtual base class for damage bond recruitment functions\n\nclass FEBIOMECH_API FEBondRecruitment : public FEMaterialProperty\n{\npublic:\n    FEBondRecruitment(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    \n    //! bond recruitment function\n    virtual double brf(FEMaterialPoint& mp, const double X) = 0;\n    \npublic:\n    \n    FECORE_BASE_CLASS(FEBondRecruitment)\n};\n\n//-----------------------------------------------------------------------------\n// User-specified load curve for damage bond recruitment function\n\nclass FEBondRecruitmentUser : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentUser(FEModel* pfem);\n    ~FEBondRecruitmentUser() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEFunction1D*    m_brf;           //!< user-defined BRF\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Power law bond recruitment function\n\nclass FEBondRecruitmentPower : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentPower(FEModel* pfem);\n    ~FEBondRecruitmentPower() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble    m_alpha;            //!< power exponent alpha\n    FEParamDouble    m_mu0;              //!< constant coeff\n    FEParamDouble    m_mu1;              //!< coeff of power\n    FEParamDouble    m_s;                //!< scale factor for argument\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Exponential law bond recruitment function\n\nclass FEBondRecruitmentExp : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentExp(FEModel* pfem);\n    ~FEBondRecruitmentExp() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble    m_alpha;            //!< power exponent alpha\n    FEParamDouble    m_mu0;              //!< constant coeff\n    FEParamDouble    m_mu1;              //!< coeff of power\n    FEParamDouble    m_s;                //!< scale factor for argument\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Quadratic polynomial bond recruitment function\n\nclass FEBondRecruitmentPoly : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentPoly(FEModel* pfem);\n    ~FEBondRecruitmentPoly() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble    m_mu0;              //!< constant coeff\n    FEParamDouble    m_mu1;              //!< coeff of linear term\n    FEParamDouble    m_mu2;              //!< coeff of quadratic term\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Log-normal damage bond recruitment function\n\nclass FEBondRecruitmentLogNormal : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentLogNormal(FEModel* pfem);\n    ~FEBondRecruitmentLogNormal() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble   m_mu;       //!< mean on log scale\n    FEParamDouble   m_sigma;    //!< standard deviation on log scale\n    FEParamDouble   m_max;      //!< maximum increase in recruitment\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Weibull damage bond recruitment function\n\nclass FEBondRecruitmentWeibull : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentWeibull(FEModel* pfem);\n    ~FEBondRecruitmentWeibull() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble   m_alpha;    //!< exponent alpha\n    FEParamDouble   m_mu;       //!< mean mu\n    FEParamDouble   m_ploc;     //!< location parameter\n    FEParamDouble   m_max;      //!< maximum increase in recruitment\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Piecewise S-shaped quintic polynomial bond recruitment function\n\nclass FEBondRecruitmentPQP : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentPQP(FEModel* pfem);\n    ~FEBondRecruitmentPQP() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \n    bool Validate() override;\n    \npublic:\n    FEParamDouble   m_mumin;    //!< mu threshold\n    FEParamDouble   m_mumax;    //!< mu cap\n    FEParamDouble   m_max;      //!< maximum increase in recruitment\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Gamma damage bond recruitment function\n\nclass FEBondRecruitmentGamma : public FEBondRecruitment\n{\npublic:\n    FEBondRecruitmentGamma(FEModel* pfem);\n    ~FEBondRecruitmentGamma() {}\n    \n    //! bond recruitment function\n    double brf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble   m_alpha;    //!< exponent alpha\n    FEParamDouble   m_mu;       //!< pdf expected mean mu\n    FEParamDouble   m_max;      //!< maximum increase in recruitment\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEBondRelaxation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBondRelaxation.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/log.h>\n#include <FECore/expint_Ei.h>\n#include <FECore/gamma.h>\n#include <FECore/besselIK.h>\n#include <math.h>\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationExponential\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationExponential, FEBondRelaxation)\n    ADD_PARAMETER(m_tau, FE_RANGE_GREATER(0.0), \"tau\")->setLongName(\"time constant\")->setUnits(UNIT_TIME);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationExponential::FEBondRelaxationExponential(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationExponential::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double tau = m_tau(mp);\n\t// --- constant relaxation times ---\n    double g = exp(-t/tau);\n\t\n\treturn g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationExpDistortion\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationExpDistortion, FEBondRelaxation)\n    ADD_PARAMETER(m_tau0 , FE_RANGE_GREATER         (0.0), \"tau0\" )->setLongName(\"constant coefficient\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_tau1 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"tau1\" )->setLongName(\"power coefficient\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\")->setLongName(\"power exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationExpDistortion::FEBondRelaxationExpDistortion(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau0 = 0;\n    m_tau1 = 0;\n    m_alpha = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationExpDistortion::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double alpha = m_alpha(mp);\n    double tau0 = m_tau0(mp);\n    double tau1 = m_tau1(mp);\n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n\n    double K2a = pow(K2,alpha);\n    double tau = tau0 + tau1*K2a;\n\n    double g = exp(-t/tau);\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationExpDistUser\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationExpDistUser, FEBondRelaxation)\nADD_PROPERTY(m_tau  , \"tau\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationExpDistUser::FEBondRelaxationExpDistUser(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! performs initialization\nbool FEBondRelaxationExpDistUser::Init()\n{\n    if (!m_tau->Init()) return false;\n    return FEBondRelaxation::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationExpDistUser::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double tau = m_tau->value(K2);\n    \n    double g = exp(-t/tau);\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationFung\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationFung, FEBondRelaxation)\n    ADD_PARAMETER(m_tau1, FE_RANGE_GREATER(0.0), \"tau1\")->setLongName(\"min. relaxation time\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_tau2, FE_RANGE_GREATER(0.0), \"tau2\")->setLongName(\"max. relaxation time\")->setUnits(UNIT_TIME);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationFung::FEBondRelaxationFung(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEBondRelaxationFung::Validate()\n{\n    if (FEBondRelaxation::Validate() == false) return false;\n//\tif (m_tau2 <= m_tau1) { feLogError(\"tau2 must be > tau1\"); return false; }\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationFung::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double tau1 = m_tau1(mp);\n    double tau2 = m_tau2(mp);\n    double g = 0;\n    \n    if (t > 0) {\n        g = (tau2*exp(-t/tau2) - tau1*exp(-t/tau1)\n        + t*(expint_Ei(-t/tau2) - expint_Ei(-t/tau1)))\n        /(tau2 - tau1);\n    }\n    else\n        g = 1;\n    \n    if (g < 0) g = 0;\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationPark\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationPark, FEBondRelaxation)\n    ADD_PARAMETER(m_tau , FE_RANGE_GREATER(0.0), \"tau\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_beta, FE_RANGE_GREATER(0.0), \"beta\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationPark::FEBondRelaxationPark(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationPark::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double tau = m_tau(mp);\n    double beta = m_beta(mp);\n    double g = 1./(1+pow(t/tau,beta));\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationParkDistortion\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationParkDistortion, FEBondRelaxation)\n    ADD_PARAMETER(m_tau0 , FE_RANGE_GREATER         (0.0), \"tau0\" )->setLongName(\"constant coefficient tau0\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_tau1 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"tau1\" )->setLongName(\"power coefficient tau1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_beta0, FE_RANGE_GREATER         (0.0), \"beta0\")->setLongName(\"constant coefficient beta0\");\n    ADD_PARAMETER(m_beta1, FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta1\")->setLongName(\"power coefficient beta1\");\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\")->setLongName(\"power exponent alpha\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationParkDistortion::FEBondRelaxationParkDistortion(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau0 = 0;\n    m_beta0 = 0;\n    m_tau1 = 0;\n    m_beta1 = 0;\n    m_alpha = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationParkDistortion::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g;\n    double alpha = m_alpha(mp);\n    double tau0 = m_tau0(mp);\n    double tau1 = m_tau1(mp);\n    double beta0 = m_beta0(mp);\n    double beta1 = m_beta1(mp);\n\n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n\n    double K2a = pow(K2,alpha);\n    double tau = tau0 + tau1*K2a;\n    double beta = beta0 + beta1*K2a;\n    g = 1./(1+pow(t/tau,beta));\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationParkDistUser\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationParkDistUser, FEBondRelaxation)\nADD_PROPERTY(m_tau  , \"tau\");\nADD_PROPERTY(m_beta , \"beta\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationParkDistUser::FEBondRelaxationParkDistUser(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau = nullptr;\n    m_beta = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! performs initialization\nbool FEBondRelaxationParkDistUser::Init()\n{\n    if (!m_tau->Init()) return false;\n    if (!m_beta->Init()) return false;\n    return FEBondRelaxation::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationParkDistUser::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g;\n    \n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double tau = m_tau->value(K2);\n    double beta = m_beta->value(K2);\n    g = 1./(1+pow(t/tau,beta));\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationPower\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationPower, FEBondRelaxation)\n    ADD_PARAMETER(m_tau , FE_RANGE_GREATER(0.0), \"tau\" )->setLongName(\"time constant\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_beta, FE_RANGE_GREATER(0.0), \"beta\")->setLongName(\"power exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationPower::FEBondRelaxationPower(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationPower::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double tau = m_tau(mp);\n    double beta = m_beta(mp);\n    double g = pow(1+t/tau,-beta);\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationPowerDistortion\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationPowerDistortion, FEBondRelaxation)\n    ADD_PARAMETER(m_tau0 , FE_RANGE_GREATER         (0.0), \"tau0\" )->setLongName(\"constant coefficient tau0\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_tau1 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"tau1\" )->setLongName(\"power coefficient tau1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_beta0, FE_RANGE_GREATER         (0.0), \"beta0\")->setLongName(\"constant coefficient beta0\");\n    ADD_PARAMETER(m_beta1, FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta1\")->setLongName(\"power coefficient beta1\");\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\")->setLongName(\"power exponent alpha\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationPowerDistortion::FEBondRelaxationPowerDistortion(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau1 = 0;\n    m_alpha = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationPowerDistortion::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g;\n    double tau0 = m_tau0(mp);\n    double tau1 = m_tau1(mp);\n    double beta0 = m_beta0(mp);\n    double beta1 = m_beta1(mp);\n    double alpha = m_alpha(mp);\n\n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double K2a = pow(K2,alpha);\n    double tau = tau0 + tau1*K2a;\n    double beta = beta0 + beta1*K2a;\n\n    g = pow(1+t/tau,-beta);\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationPowerDistUser\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationPowerDistUser, FEBondRelaxation)\nADD_PROPERTY(m_tau  , \"tau\");\nADD_PROPERTY(m_beta , \"beta\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationPowerDistUser::FEBondRelaxationPowerDistUser(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau = nullptr;\n    m_beta = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! performs initialization\nbool FEBondRelaxationPowerDistUser::Init()\n{\n    if (!m_tau->Init()) return false;\n    if (!m_beta->Init()) return false;\n    return FEBondRelaxation::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationPowerDistUser::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g;\n    \n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double tau = m_tau->value(K2);\n    double beta = m_beta->value(K2);\n    \n    g = pow(1+t/tau,-beta);\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationCarreau\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationCarreau, FEBondRelaxation)\n    ADD_PARAMETER(m_tau0 , FE_RANGE_GREATER         (0.0), \"tau0\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_lam  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"lambda\");\n    ADD_PARAMETER(m_n    , FE_RANGE_GREATER         (0.0), \"n\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationCarreau::FEBondRelaxationCarreau(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau0 = 0;\n    m_lam = 0;\n    m_n = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationCarreau::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g;\n    double tau0 = m_tau0(mp);\n    double lam = m_lam(mp);\n    double n = m_n(mp);\n    \n    // evaluate the engineering shear rate\n    double gdot = sqrt(2.)*D.norm();\n    \n    // evaluate the relaxation time\n    double tau = tau0*pow(1+pow(lam*gdot,2),(n-1)/2.);\n    \n    g = exp(-t/tau);\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationProny\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationProny, FEBondRelaxation)\n    // material parameters\n    ADD_PARAMETER(m_t[0], FE_RANGE_GREATER_OR_EQUAL(0.0), \"t1\")->setLongName(\"relaxation time t1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[1], FE_RANGE_GREATER_OR_EQUAL(0.0), \"t2\")->setLongName(\"relaxation time t2\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[2], FE_RANGE_GREATER_OR_EQUAL(0.0), \"t3\")->setLongName(\"relaxation time t3\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[3], FE_RANGE_GREATER_OR_EQUAL(0.0), \"t4\")->setLongName(\"relaxation time t4\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[4], FE_RANGE_GREATER_OR_EQUAL(0.0), \"t5\")->setLongName(\"relaxation time t5\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[5], FE_RANGE_GREATER_OR_EQUAL(0.0), \"t6\")->setLongName(\"relaxation time t6\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_g[0], FE_RANGE_CLOSED(0.0, 1.0)     , \"g1\")->setLongName(\"coefficient g1\");\n    ADD_PARAMETER(m_g[1], FE_RANGE_CLOSED(0.0, 1.0)     , \"g2\")->setLongName(\"coefficient g2\");\n    ADD_PARAMETER(m_g[2], FE_RANGE_CLOSED(0.0, 1.0)     , \"g3\")->setLongName(\"coefficient g3\");\n    ADD_PARAMETER(m_g[3], FE_RANGE_CLOSED(0.0, 1.0)     , \"g4\")->setLongName(\"coefficient g4\");\n    ADD_PARAMETER(m_g[4], FE_RANGE_CLOSED(0.0, 1.0)     , \"g5\")->setLongName(\"coefficient g5\");\n    ADD_PARAMETER(m_g[5], FE_RANGE_CLOSED(0.0, 1.0)     , \"g6\")->setLongName(\"coefficient g6\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationProny::FEBondRelaxationProny(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        m_t[i] = 1;\n        m_g[i] = 0;\n    }\n    m_sg = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEBondRelaxationProny::Validate()\n{\n    if (FEBondRelaxation::Validate() == false) return false;\n    m_sg = 0;\n    for (int i=0; i<MAX_TERMS; ++i) m_sg += m_g[i];\n    if (m_sg <= 0) return false;\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationProny::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    // --- Prony series ---\n    double g = 0;\n    for (int i=0; i<MAX_TERMS; ++i) g += m_g[i]*exp(-t/m_t[i]);\n    g /= m_sg;\n    \n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationMalkin\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationMalkin, FEBondRelaxation)\nADD_PARAMETER(m_tau1 , FE_RANGE_GREATER(0.0), \"tau1\")->setLongName(\"min. relaxation time\")->setUnits(UNIT_TIME);\nADD_PARAMETER(m_tau2 , FE_RANGE_GREATER(0.0), \"tau2\")->setLongName(\"max. relaxation time\")->setUnits(UNIT_TIME);\nADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(1.0), \"beta\")->setLongName(\"power exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationMalkin::FEBondRelaxationMalkin(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationMalkin::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g = 1.0;\n    if (t == 0) return g;\n    \n    double tau1 = m_tau1(mp);\n    double tau2 = m_tau2(mp);\n    double beta = m_beta(mp);\n    \n    if (beta != 1) {\n        double bm1 = beta - 1;\n        double Q1 = gamma_inc_Q(bm1, t/tau1);\n        double den = pow(tau1,-bm1) - pow(tau2,-bm1);\n        double Q2 = gamma_inc_Q(bm1, t/tau2);\n        g = bm1*pow(t,-bm1)*(Q2-Q1)/den;\n    }\n    else {\n        g = (expint_Ei(-t/tau2) - expint_Ei(-t/tau1))/(log(tau1/tau2));\n    }\n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationMalkinDist\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationMalkinDist, FEBondRelaxation)\n    ADD_PARAMETER(m_t1c0 , FE_RANGE_GREATER(0.0), \"t1c0\")->setLongName(\"constant for tau1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t1c1 , \"t1c1\")->setLongName(\"coefficient for tau1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t1s0 , FE_RANGE_GREATER(0.0), \"t1s0\")->setLongName(\"strain for tau1\");\n    ADD_PARAMETER(m_t2c0 , FE_RANGE_GREATER(0.0), \"t2c0\")->setLongName(\"constant for tau2\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t2c1 , \"t2c1\")->setLongName(\"coefficient for tau2\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t2s0 , FE_RANGE_GREATER(0.0), \"t2s0\")->setLongName(\"strain for tau2\");\n    ADD_PARAMETER(m_beta , FE_RANGE_GREATER(0.0), \"beta\")->setLongName(\"power exponent beta\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationMalkinDist::FEBondRelaxationMalkinDist(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_t1c0 = 0;\n    m_t1c1 = 0;\n    m_t1s0 = 1;\n    m_t2c0 = 0;\n    m_t2c1 = 0;\n    m_t2s0 = 1;\n    m_beta = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationMalkinDist::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g = 1.0;\n    if (t == 0) return g;\n    \n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double tau1 = m_t1c0(mp) + m_t1c1(mp)*exp(-K2/m_t1s0(mp));\n    double tau2 = m_t2c0(mp) + m_t2c1(mp)*exp(-K2/m_t2s0(mp));\n    double beta = m_beta(mp);\n    \n    if (beta != 1) {\n        double bm1 = beta - 1;\n        double Q1 = gamma_inc_Q(bm1, t/tau1);\n        double den = pow(tau1,-bm1) - pow(tau2,-bm1);\n        double Q2 = gamma_inc_Q(bm1, t/tau2);\n        g = bm1*pow(t,-bm1)*(Q2-Q1)/den;\n    }\n    else {\n        g = (expint_Ei(-t/tau2) - expint_Ei(-t/tau1))/(log(tau1/tau2));\n    }\n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationMalkinDistUser\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationMalkinDistUser, FEBondRelaxation)\nADD_PROPERTY(m_tau1  , \"tau1\");\nADD_PROPERTY(m_tau2  , \"tau2\");\nADD_PROPERTY(m_beta  , \"beta\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationMalkinDistUser::FEBondRelaxationMalkinDistUser(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau1 = nullptr;\n    m_tau2 = nullptr;\n    m_beta = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! performs initialization\nbool FEBondRelaxationMalkinDistUser::Init()\n{\n    if (!m_tau1->Init()) return false;\n    if (!m_tau2->Init()) return false;\n    if (!m_beta->Init()) return false;\n    return FEBondRelaxation::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationMalkinDistUser::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g = 1.0;\n    if (t == 0) return g;\n    \n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double tau1 = m_tau1->value(K2);\n    double tau2 = m_tau2->value(K2);\n    double beta = m_beta->value(K2);\n    \n    if (beta != 1) {\n        double bm1 = beta - 1;\n        double Q1 = gamma_inc_Q(bm1, t/tau1);\n        double den = pow(tau1,-bm1) - pow(tau2,-bm1);\n        double Q2 = gamma_inc_Q(bm1, t/tau2);\n        g = bm1*pow(t,-bm1)*(Q2-Q1)/den;\n    }\n    else {\n        g = (expint_Ei(-t/tau2) - expint_Ei(-t/tau1))/(log(tau1/tau2));\n    }\n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationCSexp\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationCSexp, FEBondRelaxation)\nADD_PARAMETER(m_tau , FE_RANGE_GREATER(0.0), \"tau\")->setLongName(\"exponential spectrum constant\")->setUnits(UNIT_TIME);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationCSexp::FEBondRelaxationCSexp(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationCSexp::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g = 1;\n    if (t == 0) return g;\n    double tau = m_tau(mp);\n    double ts = 2*sqrt(t/tau);\n    g = ts*k1(ts);\n    return g;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEBondRelaxationCSexpDistUser\n//\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEBondRelaxationCSexpDistUser, FEBondRelaxation)\nADD_PROPERTY(m_tau  , \"tau\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEBondRelaxationCSexpDistUser::FEBondRelaxationCSexpDistUser(FEModel* pfem) : FEBondRelaxation(pfem)\n{\n    m_tau = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! performs initialization\nbool FEBondRelaxationCSexpDistUser::Init()\n{\n    if (!m_tau->Init()) return false;\n    return FEBondRelaxation::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Relaxation function\ndouble FEBondRelaxationCSexpDistUser::Relaxation(FEMaterialPoint& mp, const double t, const mat3ds D)\n{\n    double g = 1;\n    if (t == 0) return g;\n\n    // get the elastic material point data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate distortion magnitude (always positive)\n    double K2 = (h.dev()).norm();\n    \n    double tau = m_tau->value(K2);\n\n    // evaluate relaxation function\n    double ts = 2*sqrt(t/tau);\n    // k1 is the modified bessel function of the second kind\n    g = ts*k1(ts);\n    return g;\n}\n"
  },
  {
    "path": "FEBioMech/FEBondRelaxation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/FEFunction1D.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for bond relaxation of reactive viscoelastic materials.\n//! These materials need to define a relaxation function.\n//!\nclass FEBIOMECH_API FEBondRelaxation : public FEMaterialProperty\n{\npublic:\n\tFEBondRelaxation(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\tvirtual ~FEBondRelaxation() {}\n    \n\t//! relaxation\n\tvirtual double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) = 0;\n\n    FECORE_BASE_CLASS(FEBondRelaxation)\n};\n\n//-----------------------------------------------------------------------------\n// This class implements exponential relaxation with constant relaxation time\n\nclass FEBondRelaxationExponential : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationExponential(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n\tFEParamDouble   m_tau;      //!< relaxation time\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements exponential relaxation with a relaxation\n// time that is a function of the distortional strain\n\nclass FEBondRelaxationExpDistortion : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationExpDistortion(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble  m_tau0;     //!< relaxation time\n    FEParamDouble  m_tau1;     //!< relaxation time coeff. of 2nd term\n    FEParamDouble  m_alpha;    //!< exponent of 2nd term for tau\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements exponential relaxation with a relaxation\n// time that is a function of the distortional strain\n\nclass FEBondRelaxationExpDistUser : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationExpDistUser(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \n    //! performs initialization\n    bool Init() override;\n\npublic:\n    FEFunction1D*   m_tau;      //!< relaxation time\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements Fung's relaxation\n\nclass FEBondRelaxationFung : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationFung(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \n    //! data initialization and checking\n    bool Validate() override;\n    \npublic:\n    FEParamDouble   m_tau1;     //!< lower relaxation time\n    FEParamDouble   m_tau2;     //!< upper relaxation time\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements Park's relaxation with constant relaxation time\n\nclass FEBondRelaxationPark : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationPark(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble   m_tau;      //!< relaxation time\n    FEParamDouble   m_beta;     //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements Park's relaxation with a relaxation\n// time that is a function of the distortional strain\n\nclass FEBondRelaxationParkDistortion : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationParkDistortion(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble   m_tau0;     //!< relaxation time\n    FEParamDouble   m_tau1;     //!< relaxation time coeff. of 2nd term\n    FEParamDouble   m_beta0;    //!< exponent\n    FEParamDouble   m_beta1;    //!< coefficient of 2nd for beta\n    FEParamDouble   m_alpha;    //!< exponent of 2nd term for tau and beta\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements Park's relaxation with a relaxation\n// time that is a function of the distortional strain\n\nclass FEBondRelaxationParkDistUser : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationParkDistUser(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \n    //! performs initialization\n    bool Init() override;\n    \npublic:\n    FEFunction1D*   m_tau;      //!< relaxation time\n    FEFunction1D*   m_beta;     //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a power-law relaxation with constant relaxation time\n\nclass FEBondRelaxationPower : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationPower(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble   m_tau;      //!< relaxation time\n    FEParamDouble   m_beta;     //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a power-law relaxation with a relaxation\n// time that is a function of the distortional strain\n\nclass FEBondRelaxationPowerDistortion : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationPowerDistortion(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble   m_tau0;     //!< relaxation time at zero strain\n    FEParamDouble   m_beta0;    //!< exponent of relaxation power law\n    FEParamDouble   m_tau1;     //!< relaxation time coeff. of 2nd term\n    FEParamDouble   m_beta1;    //!< coefficient of 2nd for beta\n    FEParamDouble   m_alpha;    //!< exponent of 2nd term\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a power-law relaxation with a relaxation\n// time that is a function of the distortional strain\n\nclass FEBondRelaxationPowerDistUser : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationPowerDistUser(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \n    //! performs initialization\n    bool Init() override;\n    \npublic:\n    FEFunction1D*   m_tau;      //!< relaxation time\n    FEFunction1D*   m_beta;     //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a relaxation that produces Carreau fluid response\n// under stead-state conditions\n\nclass FEBondRelaxationCarreau : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationCarreau(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble   m_tau0;     //!< characteristic time constant\n    FEParamDouble   m_lam;      //!< time constant\n    FEParamDouble   m_n;        //!< power-law index\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements Prony series exponential relaxation with constant relaxation time\n\nclass FEBondRelaxationProny : public FEBondRelaxation\n{\npublic:\n    enum { MAX_TERMS = 6 };\n\npublic:\n    //! constructor\n    FEBondRelaxationProny(FEModel* pfem);\n    \n    //! data initialization and checking\n    bool Validate() override;\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    double  m_g[MAX_TERMS];     //!< viscoelastic coefficients\n    double  m_t[MAX_TERMS];     //!< relaxation times\n    double  m_sg;               //!< sum of viscoelastic coefficients\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a Malkin relaxation with constant relaxation time\n\nclass FEBondRelaxationMalkin : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationMalkin(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble  m_tau1;     //!< lower relaxation time\n    FEParamDouble  m_tau2;     //!< upper relaxation time\n    FEParamDouble  m_beta;     //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a Malkin relaxation with adjustable relaxation time\n\nclass FEBondRelaxationMalkinDist : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationMalkinDist(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble  m_t1c0;     //!< constant coefficient of lower relaxation time\n    FEParamDouble  m_t1c1;     //!< coefficient of exponential for lower relaxation time\n    FEParamDouble  m_t1s0;     //!< time constant of exponential for lower relaxation time\n    FEParamDouble  m_t2c0;     //!< constant coefficient of upper relaxation time\n    FEParamDouble  m_t2c1;     //!< coefficient of exponential for upper relaxation time\n    FEParamDouble  m_t2s0;     //!< time constant of exponential for upper relaxation time\n    FEParamDouble  m_beta;     //!< exponent\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a Malkin relaxation with user-adjustable relaxation time\n\nclass FEBondRelaxationMalkinDistUser : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationMalkinDistUser(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \n    //! performs initialization\n    bool Init() override;\n    \npublic:\n    FEFunction1D*   m_tau1;     //!< lower relaxation time\n    FEFunction1D*   m_tau2;     //!< upper relaxation time\n    FEFunction1D*   m_beta;     //!< exponent\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a continuous spectrum exponential relaxation with constant relaxation time\n\nclass FEBondRelaxationCSexp : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationCSexp(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \npublic:\n    FEParamDouble   m_tau;      //!< relaxation time\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n// This class implements a continuous spectrum exponential relaxation with adjustable relaxation time\n\nclass FEBondRelaxationCSexpDistUser : public FEBondRelaxation\n{\npublic:\n    //! constructor\n    FEBondRelaxationCSexpDistUser(FEModel* pfem);\n    \n    //! relaxation\n    double Relaxation(FEMaterialPoint& pt, const double t, const mat3ds D) override;\n    \n    //! performs initialization\n    bool Init() override;\n    \npublic:\n    FEFunction1D*   m_tau;      //!< relaxation time\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FECGSolidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECGSolidSolver.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEMesh.h\"\n#include \"FECore/log.h\"\n#include \"FEContactInterface.h\"\n#include \"FESSIShellDomain.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FEResidualVector.h\"\n#include \"FEElasticDomain.h\"\n#include \"FE3FieldElasticSolidDomain.h\"\n#include \"FE3FieldElasticShellDomain.h\"\n#include \"FERigidBody.h\"\n#include \"RigidBC.h\"\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FELinearConstraintManager.h>\n#include \"FEBodyForce.h\"\n#include \"FECore/sys.h\"\n#include \"FECore/vector.h\"\n#include \"FEMechModel.h\"\n#include \"FEBioMech.h\"\n#include \"FESolidAnalysis.h\"\n#include <FECore/FENLConstraint.h>\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FECGSolidSolver, FESolver)\n\tADD_PARAMETER(m_Dtol  , \"dtol\");\n\tADD_PARAMETER(m_Etol  , \"etol\");\n\tADD_PARAMETER(m_Rtol  , \"rtol\");\n\tADD_PARAMETER(m_Rmin  , \"min_residual\");\n\tADD_PARAMETER(m_beta  , \"beta\");\n\tADD_PARAMETER(m_gamma , \"gamma\");\n\tADD_PARAMETER(m_LStol , \"lstol\");\n\tADD_PARAMETER(m_LSmin , \"lsmin\");\n\tADD_PARAMETER(m_LSiter, \"lsiter\");\n\tADD_PARAMETER(m_CGmethod, \"cgmethod\");\n\tADD_PARAMETER(m_precon, \"preconditioner\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFECGSolidSolver::FECGSolidSolver(FEModel* pfem) : FESolver(pfem), m_rigidSolver(pfem), \\\nm_dofU(pfem), m_dofV(pfem), m_dofQ(pfem), m_dofRQ(pfem), m_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem)\n{\n\t// default values\n\tm_Rtol = 0;\t// deactivate residual convergence \n\tm_Dtol = 1e-6;\n\tm_Etol = 0.01;\n\tm_Rmin = 1.0e-20;\n\n\tm_LStol = 0.9;\n\tm_LSmin = 1e-15;\n\tm_LSiter = 10;\n\n\tm_niter = 0;\n\tm_nreq = 0;\n\n\tm_CGmethod = 0; // 0 = Hager-Zhang, 1 = steepest descent\n\tm_precon = 0; // 0 = no preconditioner, 1 = diagonal stiffness\n\n\t// default Newmark parameters for unconditionally stable time integration\n\tm_beta = 0.25;\n\tm_gamma = 0.5;\n\n\t// Allocate degrees of freedom\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint varD = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT), VAR_VEC3);\n\t\tdofs.SetDOFName(varD, 0, \"x\");\n\t\tdofs.SetDOFName(varD, 1, \"y\");\n\t\tdofs.SetDOFName(varD, 2, \"z\");\n\t\tint varQ = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION), VAR_VEC3);\n\t\tdofs.SetDOFName(varQ, 0, \"u\");\n\t\tdofs.SetDOFName(varQ, 1, \"v\");\n\t\tdofs.SetDOFName(varQ, 2, \"w\");\n\t\tint varQR = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION), VAR_VEC3);\n\t\tdofs.SetDOFName(varQR, 0, \"Ru\");\n\t\tdofs.SetDOFName(varQR, 1, \"Rv\");\n\t\tdofs.SetDOFName(varQR, 2, \"Rw\");\n\t\tint varV = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY), VAR_VEC3);\n\t\tdofs.SetDOFName(varV, 0, \"vx\");\n\t\tdofs.SetDOFName(varV, 1, \"vy\");\n\t\tdofs.SetDOFName(varV, 2, \"vz\");\n\t\tint varSU = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT), VAR_VEC3);\n\t\tdofs.SetDOFName(varSU, 0, \"sx\");\n\t\tdofs.SetDOFName(varSU, 1, \"sy\");\n\t\tdofs.SetDOFName(varSU, 2, \"sz\");\n\t\tint varSV = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY), VAR_VEC3);\n\t\tdofs.SetDOFName(varSV, 0, \"svx\");\n\t\tdofs.SetDOFName(varSV, 1, \"svy\");\n\t\tdofs.SetDOFName(varSV, 2, \"svz\");\n\t\tint varSA = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION), VAR_VEC3);\n\t\tdofs.SetDOFName(varSA, 0, \"sax\");\n\t\tdofs.SetDOFName(varSA, 1, \"say\");\n\t\tdofs.SetDOFName(varSA, 2, \"saz\");\n\n\t\t// get the DOF indices\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t\tm_dofQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION));\n\t\tm_dofRQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n\t\tm_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FECGSolidSolver::Clean()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FECGSolidSolver::CalculatePreconditioner()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tvector<double> dummy(m_Mi);\n\tFEGlobalVector Mi(fem, m_Mi, dummy);\n\tmatrix me;\n\tvector <int> lm;\n\n\tFETimeInfo& tp = fem.GetTime();\n\tmatrix ke;\n\tvector <double> el_diagonal_stiffness;\n\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\t// check whether it is a solid domain\n\t\tFEElasticSolidDomain* pbd = dynamic_cast<FEElasticSolidDomain*>(&mesh.Domain(nd));\n\t\tif (pbd)  // it is an elastic solid domain\n\t\t{\n\t\t\tFESolidMaterial* pme = dynamic_cast<FESolidMaterial*>(pbd->GetMaterial());\n\n\t\t\t// loop over all the elements\n\t\t\tfor (int iel = 0; iel < pbd->Elements(); ++iel)\n\t\t\t{\n\t\t\t\tFESolidElement& el = pbd->Element(iel);\n\t\t\t\tpbd->UnpackLM(el, lm);\n\n\t\t\t\t//int nint = el.GaussPoints();\n\t\t\t\tlong int neln = el.Nodes();\n\n\t\t\t\t// set up, zero and calculate the element stiffness matrix\n\t\t\t\tke.resize(neln * 3, neln * 3);\n\t\t\t\tke.zero();\n\t\t\t\tpbd->ElementStiffness(tp, iel, ke);\n\n\t\t\t\t// set up the diagonal stiffness vector and copy in the diagonal values from ke\n\t\t\t\tel_diagonal_stiffness.assign(3 * neln, 0.0);\n\t\t\t\tfor (int i = 0; i < neln * 3; ++i)\n\t\t\t\t{\n\t\t\t\t\tel_diagonal_stiffness[i] = ke[i][i];\n\t\t\t\t}\n\n\t\t\t\t// assemble element stiffness vector into the global stiffness vector \n\t\t\t\tMi.Assemble(el.m_node, lm, el_diagonal_stiffness);\n\n\t\t\t} // loop over elements\n\t\t}\n\t\telse if (dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(nd)))\n\t\t{\n\t\t\tFEElasticShellDomain* psd = dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(nd));\n\t\t\tFESolidMaterial* pme = dynamic_cast<FESolidMaterial*>(psd->GetMaterial());\n\t\t\t// loop over all the elements\n\t\t\tfor (int iel = 0; iel < psd->Elements(); ++iel)\n\t\t\t{\n\t\t\t\tFEShellElement& el = psd->Element(iel);\n\t\t\t\tpsd->UnpackLM(el, lm);\n\n\t\t\t\t// create the element's stiffness matrix\n\t\t\t\tFEElementMatrix ke(el);\n\t\t\t\tint neln = el.Nodes();\n\t\t\t\tint ndof = 6 * el.Nodes();\n\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\tke.zero();\n\n\t\t\t\t// calculate element stiffness matrix\n\t\t\t\tpsd->ElementStiffness(iel, ke);\n\n\t\t\t\t// pick out the diagonal stiffness values\n\t\t\t\tel_diagonal_stiffness.assign(ndof, 0.0);\n\t\t\t\tfor (int i = 0; i < ndof; ++i)\n\t\t\t\t{\n\t\t\t\t\tel_diagonal_stiffness[i] = ke[i][i];\n\t\t\t\t}\n\n\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\tMi.Assemble(el.m_node, lm, el_diagonal_stiffness);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// TODO: we can only do solid domains right now.\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// we need the inverse of the nodal stiffnesses later\n\t// Also, make sure everything is positive.\n\tfor (int i = 0; i < m_Mi.size(); ++i)\n\t{\n\t\tif (m_Mi[i] < 0.0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (m_Mi[i] != 0.0) m_Mi[i] = 1.0 / m_Mi[i]; // note prescribed dofs will have zero mass so we need to skip them\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FESolidSolver\n//\nbool FECGSolidSolver::Init()\n{\n\tif (FESolver::Init() == false) return false;\n\n\t// check parameters\n\tif (m_Dtol <  0.0) { feLogError(\"dtol must be nonnegative.\"); return false; }\n\tif (m_Etol <  0.0) { feLogError(\"etol must be nonnegative.\"); return false; }\n\tif (m_Rtol <  0.0) { feLogError(\"rtol must be nonnegative.\"); return false; }\n\tif (m_Rmin <  0.0) { feLogError(\"min_residual must be nonnegative.\"); return false; }\n\tif (m_LStol  < 0.) { feLogError(\"lstol must be nonnegative.\" ); return false; }\n\tif (m_LSmin  < 0.) { feLogError(\"lsmin must be nonnegative.\" ); return false; }\n\tif (m_LSiter < 0 ) { feLogError(\"lsiter must be nonnegative.\"); return false; }\n\n\t// get nr of equations\n\tint neq = m_neq;\n\n\t// allocate vectors\n\tm_Fn.assign(neq, 0);\n\tm_Fr.assign(neq, 0);\n\tm_Ui.assign(neq, 0);\n\tm_Ut.assign(neq, 0);\n\tm_R0.assign(neq, 0);\n\tm_R1.assign(neq, 0);\n\tm_Mi.assign(neq, 0.0);\n\n\t// we need to fill the total displacement vector m_Ut\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, m_dofU[0]);\n\tgather(m_Ut, mesh, m_dofU[1]);\n\tgather(m_Ut, mesh, m_dofU[2]);\n\tgather(m_Ut, mesh, m_dofQ[0]);\n\tgather(m_Ut, mesh, m_dofQ[1]);\n\tgather(m_Ut, mesh, m_dofQ[2]);\n\tgather(m_Ut, mesh, m_dofSU[0]);\n\tgather(m_Ut, mesh, m_dofSU[1]);\n\tgather(m_Ut, mesh, m_dofSU[2]);\n\n\t// calculate the inverse mass vector to use as a preconditioner\n\tif (m_precon == 1)\n\t{\n\t\tif (CalculatePreconditioner() == false)\n\t\t{\n\t\t\tfeLogError(\"Failed building preconditioner.\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//!\tThis function initializes the equation system.\n//! It is assumed that all free dofs up until now have been given an ID >= 0\n//! and the fixed or rigid dofs an ID < 0.\n//! After this operation the nodal ID array will contain the equation\n//! number assigned to the corresponding degree of freedom. To distinguish\n//! between free or unconstrained dofs and constrained ones the following rules\n//! apply to the ID array:\n//!\n//!           /\n//!          |  >=  0 --> dof j of node i is a free dof\n//! ID[i][j] <  == -1 --> dof j of node i is a fixed (no equation assigned too)\n//!          |  <  -1 --> dof j of node i is constrained and has equation nr = -ID[i][j]-2\n//!           \\\n//!\nbool FECGSolidSolver::InitEquations()\n{\n\t// get the mesh\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// initialize nr of equations\n\tint neq = 0;\n\n\t// give all free dofs an equation number\n\tm_dofMap.clear();\n\tDOFS& dofs = fem.GetDOFS();\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false) {\n\t\t\tfor (int nv = 0; nv < dofs.Variables(); ++nv)\n\t\t\t{\n\t\t\t\tint n = dofs.GetVariableSize(nv);\n\t\t\t\tfor (int l = 0; l < n; ++l)\n\t\t\t\t{\n\t\t\t\t\tint nl = dofs.GetDOF(nv, l);\n\t\t\t\t\tif (node.is_active(nl))\n\t\t\t\t\t{\n\t\t\t\t\t\tint bcj = node.get_bc(nl);\n\t\t\t\t\t\tif      (bcj == DOF_OPEN      ) { node.m_ID[nl] = neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\telse if (bcj == DOF_FIXED     ) { node.m_ID[nl] = -1; }\n\t\t\t\t\t\telse if (bcj == DOF_PRESCRIBED) { node.m_ID[nl] = -neq - 2; neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\telse { assert(false); return false; }\n\t\t\t\t\t}\n\t\t\t\t\telse node.m_ID[nl] = -1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tm_nreq = neq;\n\tint nrb = fem.RigidBodies();\n\tfor (int i = 0; i<nrb; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j<6; ++j)\n\t\t{\n\t\t\tint bcj = RB.m_BC[j];\n\t\t\tint lmj = RB.m_LM[j];\n\t\t\tif (bcj == DOF_OPEN) { RB.m_LM[j] = neq; neq++; }\n\t\t\telse if (bcj == DOF_PRESCRIBED) { RB.m_LM[j] = -neq - 2; neq++; }\n\t\t\telse if (bcj == DOF_FIXED) { RB.m_LM[j] = -1; }\n\t\t\telse { assert(false); return false; }\n\t\t}\n\t}\n\n\t// store the number of equations\n\tm_neq = neq;\n\n\t// we assign the rigid body equation number to\n\t// Also make sure that the nodes are NOT constrained!\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tFERigidBody& RB = *fem.GetRigidBody(node.m_rid);\n\t\t\tnode.m_ID[m_dofU[0] ] = (RB.m_LM[0] >= 0 ? -RB.m_LM[0] - 2 : RB.m_LM[0]);\n\t\t\tnode.m_ID[m_dofU[1] ] = (RB.m_LM[1] >= 0 ? -RB.m_LM[1] - 2 : RB.m_LM[1]);\n\t\t\tnode.m_ID[m_dofU[2] ] = (RB.m_LM[2] >= 0 ? -RB.m_LM[2] - 2 : RB.m_LM[2]);\n\t\t\tnode.m_ID[m_dofRQ[0]] = (RB.m_LM[3] >= 0 ? -RB.m_LM[3] - 2 : RB.m_LM[3]);\n\t\t\tnode.m_ID[m_dofRQ[1]] = (RB.m_LM[4] >= 0 ? -RB.m_LM[4] - 2 : RB.m_LM[4]);\n\t\t\tnode.m_ID[m_dofRQ[2]] = (RB.m_LM[5] >= 0 ? -RB.m_LM[5] - 2 : RB.m_LM[5]);\n\t\t}\n\t}\n\n\t// All initialization is done\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration. \nvoid FECGSolidSolver::PrepStep()\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// initialize counters\n\tm_niter = 0;\t// nr of iterations\n\tm_nrhs = 0;\t// nr of RHS evaluations\n\tm_nref = 0;\t// nr of stiffness reformations\n\tm_ntotref = 0;\n\tm_naug = 0;\t// nr of augmentations\n\n\t// allocate data vectors\n\tm_R0.assign(m_neq, 0);\n\tm_R1.assign(m_neq, 0);\n\tm_ui.assign(m_neq, 0);\n\tm_Ui.assign(m_neq, 0);\n\n\t// zero total displacements\n\tzero(m_Ui);\n\n\t// store previous mesh state\n\t// we need them for velocity and acceleration calculations\n\tFEMesh& mesh = fem.GetMesh();\n\t//for (int i = 0; i<mesh.Nodes(); ++i)\n\t//{\n\t//\tFENode& ni = mesh.Node(i);\n\t//\tni.m_rp = ni.m_rt;\n\t//\tni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t//\tni.m_ap = ni.m_at;\n\t//}\n\n\t// apply concentrated nodal forces\n\t// since these forces do not depend on the geometry\n\t// we can do this once outside the NR loop.\n\tvector<double> dummy(m_neq, 0.0);\n\tzero(m_Fn);\n\tFEResidualVector Fn(*GetFEModel(), m_Fn, dummy);\n\n\t// TODO: This function does not exist\n//\tNodalLoads(Fn, tp);\n\n\t// apply boundary conditions\n\t// we save the prescribed displacements increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint neq = m_neq;\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i = 0; i<nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.PrepStep(ui);\n\t}\n\n\t// initialize rigid bodies\n\tint NO = fem.RigidBodies();\n\tfor (int i = 0; i<NO; ++i) fem.GetRigidBody(i)->Init();\n\n\t// calculate local rigid displacements\n\tfor (int i = 0; i < NO; ++i)\n\t{\n\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tFERigidPrescribedBC* dc = rb.m_pDC[j];\n\t\t\tif (dc) dc->InitTimeStep();\n\t\t}\n\t}\n\n\t// calculate global rigid displacements\n\tfor (int i = 0; i<NO; ++i)\n\t{\n\t\tFERigidBody* prb = fem.GetRigidBody(i);\n\t\tif (prb)\n\t\t{\n\t\t\tFERigidBody& RB = *prb;\n\t\t\tif (RB.m_prb == 0)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j<6; ++j) RB.m_du[j] = RB.m_dul[j];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble* dul = RB.m_dul;\n\t\t\t\tvec3d dr = vec3d(dul[0], dul[1], dul[2]);\n\n\t\t\t\tvec3d v = vec3d(dul[3], dul[4], dul[5]);\n\t\t\t\tdouble w = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);\n\t\t\t\tquatd dq = quatd(w, v);\n\n\t\t\t\tFERigidBody* pprb = RB.m_prb;\n\n\t\t\t\tvec3d r0 = RB.m_rt;\n\t\t\t\tquatd Q0 = RB.GetRotation();\n\n\t\t\t\tdr = Q0*dr;\n\t\t\t\tdq = Q0*dq*Q0.Inverse();\n\n\t\t\t\twhile (pprb)\n\t\t\t\t{\n\t\t\t\t\tvec3d r1 = pprb->m_rt;\n\t\t\t\t\tdul = pprb->m_dul;\n\n\t\t\t\t\tquatd Q1 = pprb->GetRotation();\n\n\t\t\t\t\tdr = r0 + dr - r1;\n\n\t\t\t\t\t// grab the parent's local displacements\n\t\t\t\t\tvec3d dR = vec3d(dul[0], dul[1], dul[2]);\n\t\t\t\t\tv = vec3d(dul[3], dul[4], dul[5]);\n\t\t\t\t\tw = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);\n\t\t\t\t\tquatd dQ = quatd(w, v);\n\n\t\t\t\t\tdQ = Q1*dQ*Q1.Inverse();\n\n\t\t\t\t\t// update global displacements\n\t\t\t\t\tquatd Qi = Q1.Inverse();\n\t\t\t\t\tdr = dR + r1 + dQ*dr - r0;\n\t\t\t\t\tdq = dQ*dq;\n\n\t\t\t\t\t// move up in the chain\n\t\t\t\t\tpprb = pprb->m_prb;\n\t\t\t\t\tQ0 = Q1;\n\t\t\t\t}\n\n\t\t\t\t// set global displacements\n\t\t\t\tdouble* du = RB.m_du;\n\n\t\t\t\tdu[0] = dr.x;\n\t\t\t\tdu[1] = dr.y;\n\t\t\t\tdu[2] = dr.z;\n\n\t\t\t\tv = dq.GetVector();\n\t\t\t\tw = dq.GetAngle();\n\t\t\t\tdu[3] = w*v.x;\n\t\t\t\tdu[4] = w*v.y;\n\t\t\t\tdu[5] = w*v.z;\n\t\t\t}\n\t\t}\n\t}\n\n\t// store rigid displacements in Ui vector\n\tfor (int i = 0; i<NO; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j<6; ++j)\n\t\t{\n\t\t\tint I = -RB.m_LM[j] - 2;\n\t\t\tif (I >= 0) ui[I] = RB.m_du[j];\n\t\t}\n\t}\n\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tif (pstep->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t{\n\t\tfeLogError(\"The CG-Solid solver cannot be used for dynamic analysis\");\n\t\tthrow FatalError();\n\t}\n\n\t//{\n\t//\tFEMesh& mesh = fem.GetMesh();\n\n\t//\t// set the initial velocities of all rigid nodes\n\t//\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t//\t{\n\t//\t\tFENode& n = mesh.Node(i);\n\t//\t\tif (n.m_rid >= 0)\n\t//\t\t{\n\t//\t\t\tFERigidBody& rb = *fem.GetRigidBody(n.m_rid);\n\t//\t\t\tvec3d V = rb.m_vt;\n\t//\t\t\tvec3d W = rb.m_wt;\n\t//\t\t\tvec3d r = n.m_rt - rb.m_rt;\n\n\t//\t\t\tvec3d v = V + (W ^ r);\n\t//\t\t\tn.m_vp = v;\n\t//\t\t\tn.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], v);\n\n\t//\t\t\tvec3d a = (W ^ V)*2.0 + (W ^ (W ^ r));\n\t//\t\t\tn.m_ap = n.m_at = a;\n\t//\t\t}\n\t//\t}\n\t//}\n\n\t// store the current rigid body reaction forces\n\tfor (int i = 0; i<fem.RigidBodies(); ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tRB.m_Fp = RB.m_Fr;\n\t\tRB.m_Mp = RB.m_Mr;\n\t}\n\n\t// intialize material point data\n\tfor (int i = 0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n\n\t// update model state\n\tfem.Update();\n\n\t// see if we need to do contact augmentations\n\tm_baugment = false;\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n\t\tif (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n\t}\n\n\t// see if we need to do incompressible augmentations\n\t// TODO: Should I do these augmentations in a nlconstraint class instead?\n\tint ndom = mesh.Domains();\n\tfor (int i = 0; i < ndom; ++i)\n\t{\n\t\tFEDomain* dom = &mesh.Domain(i);\n\t\tFE3FieldElasticSolidDomain* dom3f = dynamic_cast<FE3FieldElasticSolidDomain*>(dom);\n\t\tif (dom3f && dom3f->DoAugmentations()) m_baugment = true;\n\n\t\tFE3FieldElasticShellDomain* dom3fs = dynamic_cast<FE3FieldElasticShellDomain*>(dom);\n\t\tif (dom3fs && dom3fs->DoAugmentations()) m_baugment = true;\n\t}\n\n\t// see if we have to do nonlinear constraint augmentations\n\tfor (int i = 0; i<fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint& ci = *fem.NonlinearConstraint(i);\n\t\tif (ci.IsActive()) m_baugment = true;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FECGSolidSolver::SolveStep()\n{\n\tint i;\n\n\tvector<double> u0(m_neq);\n\tvector<double> Rold(m_neq);\n\n\t// convergence norms\n\tdouble\tnormR1;\t\t// residual norm\n\tdouble\tnormE1;\t\t// energy norm\n\tdouble\tnormU;\t\t// displacement norm\n\tdouble\tnormu;\t\t// displacement increment norm\n\tdouble\tnormRi;\t\t// initial residual norm\n\tdouble\tnormEi;\t\t// initial energy norm\n\tdouble\tnormEm;\t\t// max energy norm\n\tdouble\tnormUi;\t\t// initial displacement norm\n\n\t// initialize flags\n\tbool breform = false;\t// reformation flag\n\tbool sdstep = true; // set to true on a steepest descent iteration - if this fails we will give up\n\tint cgits = 0; // count CG iterations to trigger periodic restart\n\tint maxits = 100; // how many to do before restarting\n\n\t// Get the current step\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t// prepare for the first iteration\n\tconst FETimeInfo& tp = fem.GetTime();\n\tPrepStep();\n\n\t// We need to try the prescribed displacements and make sure they don't cause a -ve Jacobian\n\t// before we have moved any of the flexible nodes\n\t// We also calculate the initial residual\n\t// TODO: I think some of this update is duplicated in PrepStep and could just be done here\n\t//if (Residual(m_R0) == false) return false;\n\ttry\n\t{\n\t\tUpdate(m_ui); // m_ui contains the prescribed displacements calculated in PrepStep\n\t\tResidual(m_R0);\n\t}\n\tcatch (...) // negative Jacobian if prescribed disps are too big\n\t{\n\t\t\tfeLogError(\"Time step too big, prescribed displacements caused negative Jacobian\");\n\t\t\treturn false;\n\t}\n\n\t// set the initial step length estimates to 1.0e-6\n\tdouble s = 1e-6;\n\tdouble olds = 1e-6;\n\tdouble oldolds = 1e-6;  // line search step lengths from the current iteration and the two previous ones\n\t\n\t// loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter+1);\n\n\t\t// assume we'll converge. \n\t\tbconv = true;\n\t\tif ((m_niter>0)&&(breform==false)&&(m_CGmethod == 0))  // no need to restart CG\n\t\t{ \n\t\t\t// calculate Hager- Zhang direction\n        \tdouble moddU=sqrt(u0*u0);  // needed later for the step length calculation\n\n\t\t\t// calculate yk\n\t\t\tvector<double> RR(m_neq);\n\t\t\tRR=m_R0-Rold;\n\n\t\t\t// calculate dk.yk\n\t\t\tdouble bdiv=u0*RR;\n\t\t\tdouble betapcg;\n\t\t\tif (bdiv==0.0) // use steepest descent method if necessary\n\t\t\t{\n\t\t\t\tbetapcg=0.0;\n\t\t\t\tsdstep = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdouble RR2 = RR * RR;\t// yk^2\n\t\t\t\t// use m_ui as a temporary vector\n\t\t\t\tif (m_precon == 1)\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < m_neq; ++i) {\n\t\t\t\t\t\t// improved formula from L-CG DESCENT (*2 removed):\n\t\t\t\t\t\tm_ui[i] = m_Mi[i] * (RR[i] - u0[i] * RR2 / bdiv);\t// yk-2*dk*yk^2/(dk.yk)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < m_neq; ++i) {\n\t\t\t\t\t\t// formula from CG DESCENT (without *2 removed):\n\t\t\t\t\t\tm_ui[i] = RR[i] - 2.0 * u0[i] * RR2 / bdiv;\t// yk-2*dk*yk^2/(dk.yk)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbetapcg = m_ui * m_R0;\t// m_ui*gk+1\n\t\t\t\tbetapcg = -betapcg / bdiv;\n\t\t\t\tif (m_precon == 1)\n\t\t\t\t{\n\t\t\t\t\t// improved truncation from L-CG DESCENT:\n\t\t\t\t\tdouble dPd = 0.0;\n\t\t\t\t\tfor (i = 0; i < m_neq; ++i) {\n\t\t\t\t\t\tif (m_Mi[i] != 0) {  // m_Mi=0 for prescribed displacements, so skip those dofs\n\t\t\t\t\t\t\tdPd += u0[i] * u0[i] / m_Mi[i];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdouble etak = 0.4 * (u0 * Rold) / dPd;\n\t\t\t\t\tbetapcg = max(etak, betapcg);\n\t\t\t\t\tif (ISNAN(betapcg)) throw; // NANDetected(); TODO: update this error\n\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Original H-Z truncation formula\n\t\t\t\t\tdouble modR = sqrt(m_R0 * m_R0);\n\t\t\t\t\tdouble etak = -1.0 / (moddU * min(0.01, modR));\n\t\t\t\t\tbetapcg = max(etak, betapcg);\n\t\t\t\t}\n\n\n\n\t\t\t\tsdstep = false;\n\t\t\t}\n\n\t\t\tif (m_precon == 1) // use LMM preconditioner\n\t\t\t{\n\t\t\t\tfor (i = 0; i < m_neq; ++i)  // calculate new search direction m_ui\n\t\t\t\t{\n\t\t\t\t\tm_ui[i] = m_Mi[i] * m_R0[i] + betapcg * u0[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (i = 0; i < m_neq; ++i)  // calculate new search direction m_ui\n\t\t\t\t{\n\t\t\t\t\tm_ui[i] = m_R0[i] + betapcg * u0[i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse \n\t\t{\n\t\t\t// use steepest descent for first iteration or when a restart is needed\n\t\t\tif (m_precon == 1) // use LMM preconditioner\n\t\t\t{\n\t\t\t\tfor (i = 0; i < m_neq; ++i)  // calculate new search direction m_ui\n\t\t\t\t{\n\t\t\t\t\tm_ui[i] = m_Mi[i] * m_R0[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse m_ui = m_R0;\n\n\t\t\tbreform=false;\n\t\t\tif (m_niter > 0) m_nref += 1;\n\t\t\tsdstep = true;\n       \t}\n\t\tRold=m_R0;\t\t// store residual for use next time\n\t\tu0=m_ui;\t\t// store direction for use on the next iteration\n\n\t\t// check for nans\n\t\tdouble du = m_ui*m_ui;\n\t\tif (ISNAN(du)) throw NANInSolutionDetected();\n\n\t\t// perform a linesearch\n\t\t// the geometry is also updated in the line search\n\t\t// use the step length from two steps previously as the initial guess\n\t\t// note that it has its own linesearch, different from the BFGS one\n\t\ts = LineSearchCG(oldolds);\n\t\tif (s != -1) {// update the old step lengths for use as an initial guess in two iterations' time\n\t\t\tif (m_niter < 1) oldolds = s;\t// if this is the first iteration, use current step length\n\t\t\telse oldolds = olds;\t// otherwise use the previous one\n\t\t\tif (s > 0) olds = s;  // and store the current step to be used for the iteration after next\n\t\t\t// update total incremental displacements\n\t\t\tint neq = (int)m_Ui.size();\n\t\t\tfor (i = 0; i < neq; ++i) m_Ui[i] += s * m_ui[i];\n\t\t}\n\t\telse { // the line search has failed and we need to restart\n\t\t\tbreform = true;\n\t\t\tfeLogWarning(\"Line search failed. Restarting conjugate gradient algorithm\");\n\t\t\toldolds = 1e-6;\n\t\t\tolds = 1e-6;\n\t\t}\n\t\t// set initial convergence norms if on the first iteration\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tnormRi = fabs(m_R0 * m_R0);\n\t\t\tnormEi = fabs(m_ui * m_R0)*s;\n\t\t\tnormUi = fabs(m_ui * m_ui)*s*s;\n\t\t\tnormEm = normEi;\n\t\t}\n\n\t\t// calculate norms (using actual step from line search=s*m_ui)\n\t\tnormR1 = m_R1*m_R1;\n\t\tnormu  = (m_ui*m_ui)*(s*s);\n\t\tnormU  = m_Ui*m_Ui;\n\t\tnormE1 = s*fabs(m_ui*m_R1);\n\n\t\t// check residual norm\n\t\tif ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\t\n\n\t\t// check displacement norm\n\t\tif ((m_Dtol > 0) && (normu  > (m_Dtol*m_Dtol)*normU )) bconv = false;\n\n\t\t// check energy norm\n\t\tif ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n\n\t\t// check linestep size\n\t\tif ((m_LStol > 0) && (s < m_LSmin)) bconv = false;\n\n\t\t// check energy divergence\n\t\tif (normE1 > normEm) bconv = false;\n\n\t\t// print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tconjugate gradient restarts = %d\\n\", m_nref);\n\t\tif (m_LStol > 0) feLog(\"\\tstep from line search         = %15le\\n\", s);\n\t\tfeLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n\t\tfeLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n\t\tfeLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n\t\tfeLog(\"\\t   displacement     %15le %15le %15le \\n\", normUi, normu ,(m_Dtol*m_Dtol)*normU );\n\n\t\t// see if we may have a small residual\n\t\tif ((bconv == false) && (normR1 < m_Rmin))\n\t\t{\n\t\t\t// check for almost zero-residual on the first iteration\n\t\t\t// this might be an indication that there is no force on the system\n\t\t\tfeLogWarning(\"No force acting on the system.\");\n\t\t\tbconv = true;\n\t\t}\n\n\t\t// check if we have converged. \n\t\tif (bconv == false)\n\t\t{\n\t\t\tif (fabs(s) < m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Restarting conjugate gradient algorithm\");\n\t\t\t\tfeLogWarning(\"\\tstep from line search         = %15le\\n\", s);\n\t\t\t\tbreform = true;\n\t\t\t\toldolds = 1e-6; // reset step lengths for restart\n\t\t\t\tolds = 1e-6;\n\t\t\t}\n\t\t\t// check for diverging\n\t\t\telse if (normE1 > 1000*normEm) // less strict divergence check than for BFGS\n\t\t\t\t// as norms tend to increase at first as deformation propagates\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Solution is diverging. Restarting conjugate gradient algorithm\");\n\t\t\t\tnormEm = normE1;\n\t\t\t\tnormEi = normE1;\n\t\t\t\tnormRi = normR1;\n\t\t\t\tbreform = true;\n\t\t\t\toldolds = 1e-6; // reset step lengths for restart\n\t\t\t\tolds = 1e-6;\n\t\t\t}\n\n\t\t\t// zero displacement increments\n\t\t\t// we must set this to zero before the next iteration\n\t\t\t// because we assume that the prescribed displacements are stored \n\t\t\t// in the m_ui vector.\n\t\t\tzero(m_ui);\n\n\t\t\t// copy last calculated residual\n\t\t\tm_R0 = m_R1;\n\t\t\t//Rold = m_R1;\t\t// store residual for use next time\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// we have converged, so let's see if the augmentations have converged as well\n\n\t\t\tfeLog(\"\\n........................ augmentation # %d\\n\", m_naug+1);\n\n\t\t\t// do the augmentations\n\t\t\tbconv = Augment();\n\n\t\t\t// update counter\n\t\t\t++m_naug;\n\n\t\t\t// we reset the reformations counter\n\t\t\tm_nref = 0;\n\t\n\t\t\t// If we havn't converged we prepare for the next iteration\n\t\t\tif (!bconv) \n\t\t\t{\n\t\t\t\t// Since the Lagrange multipliers have changed, we can't just copy \n\t\t\t\t// the last residual but have to recalculate the residual\n\t\t\t\t// we also recalculate the stresses in case we are doing augmentations\n\t\t\t\t// for incompressible materials\n\t\t\t\tfem.Update();\n\t\t\t\tResidual(m_R0);\n\t\t\t}\n\t\t}\n\t\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile ((bconv == false)&&((s!=-1)||(sdstep==false))); // give up if a steepest descent iteration fails\n\n\t// if converged we update the total displacements\n\tif (bconv)\n\t{\n\t\tm_Ut += m_Ui;\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECGSolidSolver::Update(std::vector<double>& u)\n{\n\tUpdateKinematics(u);\n\tGetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FECGSolidSolver::UpdateKinematics(vector<double>& ui)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update rigid bodies\n\tUpdateRigidBodies(ui);\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tfor (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n\t// update flexible nodes\n\t// translational dofs\n\tscatter(U, mesh, m_dofU[0]);\n\tscatter(U, mesh, m_dofU[1]);\n\tscatter(U, mesh, m_dofU[2]);\n\t// rotational dofs\n\tscatter(U, mesh, m_dofQ[0]);\n\tscatter(U, mesh, m_dofQ[1]);\n\tscatter(U, mesh, m_dofQ[2]);\n\t// shell dofs\n\tscatter(U, mesh, m_dofSU[0]);\n\tscatter(U, mesh, m_dofSU[1]);\n\tscatter(U, mesh, m_dofSU[2]);\n\n\t// make sure the prescribed displacements are fullfilled\n\tint ndis = fem.BoundaryConditions();\n\tfor (int i=0; i<ndis; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Update();\n\t}\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// Update the spatial nodal positions\n\t// Don't update rigid nodes since they are already updated\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid == -1)\n\t\t\tnode.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FECGSolidSolver::LineSearchCG(double s)\n{\n\t//  s is now passed from the solver routine instead of defaulting to 1.0\n\tdouble smin = s;\n\n\tdouble FA, FB, FC, AA, AB, r, r0;\n\tbool failed = false;\n\tdouble A[12] = {}; // vectors to store line search results\n\tdouble F[12] = {};\n\tdouble lspow[5]; // used for quadratic fitting calculation\n\tdouble B[3][4];\n\tdouble Y[3]; // RHS vector for curve fitting\n\tdouble coeffs[3];\n\tdouble temp, term;\n\n\n\t// max nr of line search iterations\n\tint nmax = m_LSiter;\n\tint n = 0;\n\tint i, j,k;\n\n\t// initial energy\n\tFA = m_ui*m_R0;\n\tAA = 0.0;\n\tr0 = FA;\n\tF[0] = FA;\n\tA[0] = 0.0;\n\t\n\tdouble rmin = fabs(FA);\n\n\tvector<double> ul(m_ui.size());  // temporary vector for trial displacements\n\t\n\t// so we can set AA = 0 and FA= r0\n\t// AB=s and we need to evaluate FB (called r1)\n\t// n is a count of the number of linesearch attempts\n\n\t// calculate residual at this point, reducing s if necessary\n\tdo\n\t{\n\t\t// Update geometry using the initial guess s\n\t\tvcopys(ul, m_ui, s);\n\t\tfailed = false;\n\t\ttry\n\t\t{\n\t\t\tUpdate(ul);\n\t\t\tResidual(m_R1);\n\t\t\t\t}\n\t\tcatch (...) // negative Jacobian if s is much too big\n\t\t{\n\t\t\t\t\t\t//feLog(\"reducing s at initial evaluation\");\n\t\t\t\t\t\t//feLog(\"\\tstep from line search         = %15le\\n\", s); \n\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\tif (s > 10 * m_LSmin) s = 0.1 * s; // make s smaller and try again until we don't get a -ve J\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tfeLogError(\"Direction invalid, line search failed\");\n\t\t\t\t\t\t\ts= -1;\n\t\t\t\t\t\t\tfailed = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t}\n\t} while (failed == true);\n\n\tif (s != -1) {\n\t\t// calculate energies\n\t\tFB = m_ui * m_R1;\n\t\tAB = s;\n\t\tF[1] = FB;\n\t\tA[1] = AB;\n\n\t\tif (fabs(FB) < rmin) { // remember best values in case we need them later\n\t\t\trmin = FB;\n\t\t\tsmin = s;\n\t\t}\n\n\t\tdo\n\t\t{\n\t\t\t// make sure that r1 does not happen to be really close to zero,\n\t\t\t// since in that case we won't find any better solution.\n\t\t\tif (fabs(FB) < 1.e-20) r = 0;  // we've hit the converged solution and don't need to do any more\n\t\t\telse r = fabs(FB / r0);\n\n\t\t\tif (r > m_LStol)\t// we need to search and find a better value of s\n\t\t\t{\n\t\t\t\tif (n < 4) { // use linear fitting algorithm\n\t\t\t\t\tif (fabs(FB - FA) < fabs(FB * 0.01)) { // if FB=FA (or nearly) the next step won't work, so make s bigger\n\t\t\t\t\t\tif (AB != 0) s = max(AA, AB) * 200; // try a much bigger value\n\t\t\t\t\t\telse if (AA != 0) s = AA * 200;\n\t\t\t\t\t\telse s = 1e-6; // should never happen!\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\ts = (AA * FB - AB * FA) / (FB - FA);  // use linear interpolation for first few attempts\n\t\t\t\t\t\t//s = min(s, 1e-3); // limit how much s can grow to avoid over-extrapolating\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse { // use quadratic curve fit to try to find a minimum if multiple linear attempts have failed\n\t\t\t\t\t// calculate powers to fill matrix\n\t\t\t\t\tfor (i = 0; i <= 4; i++) {\n\t\t\t\t\t\tlspow[i] = 0;\n\t\t\t\t\t\tfor (j = 0; j < n; j++) {\n\t\t\t\t\t\t\tlspow[i] = lspow[i] + pow(A[j], i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// calculate rhs\n\t\t\t\t\tfor (i = 0; i <= 2; i++) {\n\t\t\t\t\t\tY[i] = 0;\n\t\t\t\t\t\tfor (j = 0; j < n; j++) {\n\t\t\t\t\t\t\tY[i] = Y[i] + pow(A[j], i) * F[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// fill matrix\n\t\t\t\t\tfor (i = 0; i <= 2; i++) {\n\t\t\t\t\t\tfor (j = 0; j <= 2; j++) {\n\t\t\t\t\t\t\tB[i][j] = lspow[i + j];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (i = 0; i <= 2; i++) {\n\t\t\t\t\t\tB[i][3] = Y[i];\n\t\t\t\t\t}\n\t\t\t\t\t// solve by Gaussian elimination\n\t\t\t\t\tfor (i = 0; i < 3; i++) {\n\t\t\t\t\t\tfor (k = i + 1; k < 3; k++) {\n\t\t\t\t\t\t\tif (fabs(B[i][i]) < fabs(B[k][i])) {\n\t\t\t\t\t\t\t\t//Swap\n\t\t\t\t\t\t\t\tfor (j = 0; j < 4; j++) {\n\t\t\t\t\t\t\t\t\ttemp = B[i][j];\n\t\t\t\t\t\t\t\t\tB[i][j] = B[k][j];\n\t\t\t\t\t\t\t\t\tB[k][j] = temp;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// eliminate\n\t\t\t\t\t\tfor (k = i + 1; k < 3; k++) {\n\t\t\t\t\t\t\tterm = B[k][i] / B[i][i];\n\t\t\t\t\t\t\tfor (j = 0; j < 4; j++) {\n\t\t\t\t\t\t\t\tB[k][j] = B[k][j] - term * B[i][j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//back substitute\n\t\t\t\t\tfor (i = 2; i >= 0; i--) {\n\t\t\t\t\t\tcoeffs[i] = B[i][3];\n\t\t\t\t\t\tfor (j = i + 1; j < 3; j++) {\n\t\t\t\t\t\t\tcoeffs[i] = coeffs[i] - B[i][j] * coeffs[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcoeffs[i] = coeffs[i] / B[i][i];\n\t\t\t\t\t}\n\t\t\t\t\ts = -coeffs[1]*0.5/coeffs[2]; // quadratic estimate\n\t\t\t\t\t//feLog(\"\\tQuadratic curve fit coeffs s %15le %15le %15le %15le\\n\", coeffs[0], coeffs[1], coeffs[2], s);\n\t\t\t\t}\n\t\t\t\tif (s == 0) { // check just in case\n\t\t\t\t\tfeLog(\"\\tZero step length FA FB AA AB %15le %15le %15le %15le\\n\", FA, FB, AA, AB);\n\t\t\t\t}\n\n\t\t\t\t// calculate residual at this point, reducing s if necessary\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\t// Update geometry using the initial guess s\n\t\t\t\t\t//feLog(\"\\tFA FB AA AB s %15le %15le %15le %15le %15le\\n\", FA, FB, AA, AB, s);\n\t\t\t\t\tvcopys(ul, m_ui, s);\n\t\t\t\t\tfailed = false;\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tUpdate(ul);\n\t\t\t\t\t\tResidual(m_R1);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (...)\n\t\t\t\t\t{\n\t\t\t\t\t\tfeLog(\"reducing s at FC\");\n\t\t\t\t\t\tfeLog(\"\\tstep from line search         = %15le\\n\", s);\n\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\ts = 0.1 * s;\n\t\t\t\t\t}\n\t\t\t\t} while ((failed == true) && (s > m_LSmin));\n\n\t\t\t\t// calculate energies\n\t\t\t\tFC = m_ui * m_R1;\n\t\t\t\tr = fabs(FC / r0);\n\n\t\t\t\tif (fabs(FC) > 100 * min(fabs(FA), fabs(FB)))  //  it was a bad guess and we need to go back a bit\n\t\t\t\t{\n\t\t\t\t\ts = 0.1 * s;\n\n\t\t\t\t\t// calculate residual at this point, reducing s more if necessary\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\t// Update geometry using the initial guess s\n\t\t\t\t\t\tvcopys(ul, m_ui, s);\n\t\t\t\t\t\tfailed = false;\n\t\t\t\t\t\ttry\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUpdate(ul);\n\t\t\t\t\t\t\tResidual(m_R1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (...)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfeLog(\"reducing s after bad guess\");\n\t\t\t\t\t\t\tfeLog(\"\\tstep from line search         = %15le\\n\", s);\n\t\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\t\ts = 0.1 * s;\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (failed == true);\n\n\t\t\t\t\t// calculate energies\n\t\t\t\t\tFC = m_ui * m_R1;\n\t\t\t\t\tr = fabs(FC / r0);\n\t\t\t\t}\n\n\t\t\t\tif (fabs(FA) < fabs(FB)) // use the new value and the closest of the previous ones\n\t\t\t\t{\n\t\t\t\t\tFB = FC;\n\t\t\t\t\tAB = s;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFA = FC;\n\t\t\t\t\tAA = s;\n\t\t\t\t}\n\t\t\t\tF[n + 2] = FC;\n\t\t\t\tA[n + 2] = s;\n\t\t\t\t++n;\n\t\t\t\tfeLog(\"\\tF %15le %15le %15le %15le %15le\\n\", F[0], F[1], F[2], F[3], F[4]);\n\t\t\t\tfeLog(\"\\tA %15le %15le %15le %15le %15le\\n\", A[0], A[1], A[2], A[3], A[4]);\n\t\t\t\tif (n > 3) {\n\t\t\t\t\tfeLog(\"\\tF %15le %15le %15le %15le %15le\\n\", F[5], F[6], F[7], F[8], F[9]);\n\t\t\t\t\tfeLog(\"\\tA %15le %15le %15le %15le %15le\\n\", A[5], A[6], A[7], A[8], A[9]);\n\t\t\t\t}\n\t\t\t}\n\t\t} while ((((r > m_LStol) && (n <= 5)) || ((r >= 1) && (n > 3))) && (n < nmax));\n\t\t// try to find a better solution within m_LStol, but if we haven't after five tries, accept any improvement\n\n\t\tif (n >= nmax)\n\t\t{\n\t\t\t// max nr of iterations reached.\n\t\t\ts = -1;// this signals to the main algorithm that the line search has failed and a restart is needed\n\t\t}\n\t}\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FECGSolidSolver::Residual(vector<double>& R)\n{\n\t// get the time information\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// initialize residual with concentrated nodal loads\n\tR = m_Fn;\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup the global vector\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tint NRB = fem.RigidBodies();\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tRB.m_Fr = RB.m_Mr = vec3d(0, 0, 0);\n\t}\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// calculate the internal (stress) forces\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n\t\tdom.InternalForces(RHS);\n\t}\n\n\t// calculate inertial forces for dynamic problems\n\tif (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC) InertialForces(RHS);\n\n\t// calculate contact forces\n\tif (fem.SurfacePairConstraints() > 0)\n\t{\n\t\tContactForces(RHS);\n\t}\n\n\t// calculate nonlinear constraint forces\n\t// note that these are the linear constraints\n\t// enforced using the augmented lagrangian\n\tNonLinearConstraintForces(RHS, tp);\n\n\t// forces due to point constraints\n\t//\tfor (i=0; i<(int) fem.m_PC.size(); ++i) fem.m_PC[i]->Residual(this, R);\n\n\t// add model loads\n\tint NML = fem.ModelLoads();\n\tfor (int i = 0; i<NML; ++i)\n\t{\n\t\tFEModelLoad& mli = *fem.ModelLoad(i);\n\t\tif (mli.IsActive())\n\t\t{\n\t\t\tmli.LoadVector(RHS);\n\t\t}\n\t}\n\n\t// set the nodal reaction forces\n\t// TODO: Is this a good place to do this?\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FECGSolidSolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->LoadVector(R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces \nvoid FECGSolidSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->LoadVector(R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the inertial forces for dynamic problems\n\nvoid FECGSolidSolver::InertialForces(FEGlobalVector& R)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// allocate F\n\tvector<double> F(3 * mesh.Nodes());\n\tzero(F);\n\n\t// calculate F\n    double dt = fem.GetTime().timeIncrement;\n\tdouble a = 1.0 / (m_beta*dt);\n\tdouble b = a / dt;\n\tdouble c = 1.0 - 0.5 / m_beta;\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tvec3d& rt = node.m_rt;\n\t\tvec3d& rp = node.m_rp;\n\t\tvec3d& vp = node.m_vp;\n\t\tvec3d& ap = node.m_ap;\n\n\t\tF[3 * i] = b*(rt.x - rp.x) - a*vp.x + c * ap.x;\n\t\tF[3 * i + 1] = b*(rt.y - rp.y) - a*vp.y + c * ap.y;\n\t\tF[3 * i + 2] = b*(rt.z - rp.z) - a*vp.z + c * ap.z;\n\t}\n\n\t// now multiply F with the mass matrix\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(nd));\n\t\tdom.InertialForces(R, F);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I'd like to do something different with this. Right now, if a nodal load\n//       it applied to a rigid body, the load has to be translated to a force and \n//       torque applied to the rigid body. Perhaps we should really define two types\n//       of nodal loads, one for the deformable body and for the rigid body. This can\n//       be done in a pre-processor phase. That way, standard assembly routines can be\n//       used to assemble to loads into the global vector.\nvoid FECGSolidSolver::AssembleResidual(int node_id, int dof, double f, vector<double>& R)\n{\n\t// get the mesh\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the equation number\n\tFENode& node = mesh.Node(node_id);\n\tint n = node.m_ID[dof];\n\n\t// assemble into global vector\n\tif (n >= 0) R[n] += f;\n\telse if (node.m_rid >= 0)\n\t{\n\t\t// this is a rigid body node\n\t\tFERigidBody& RB = *fem.GetRigidBody(node.m_rid);\n\n\t\t// get the relative position\n\t\tvec3d a = node.m_rt - RB.m_rt;\n\n\t\tint* lm = RB.m_LM;\n\t\tif (dof == m_dofU[0])\n\t\t{\n\t\t\tif (lm[0] >= 0) R[lm[0]] += f;\n\t\t\tif (lm[4] >= 0) R[lm[4]] += a.z*f;\n\t\t\tif (lm[5] >= 0) R[lm[5]] += -a.y*f;\n\t\t}\n\t\telse if (dof == m_dofU[1])\n\t\t{\n\t\t\tif (lm[1] >= 0) R[lm[1]] += f;\n\t\t\tif (lm[3] >= 0) R[lm[3]] += -a.z*f;\n\t\t\tif (lm[5] >= 0) R[lm[5]] += a.x*f;\n\t\t}\n\t\telse if (dof == m_dofU[2])\n\t\t{\n\t\t\tif (lm[2] >= 0) R[lm[2]] += f;\n\t\t\tif (lm[3] >= 0) R[lm[3]] += a.y*f;\n\t\t\tif (lm[4] >= 0) R[lm[4]] += -a.x*f;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the rigid body data\nvoid FECGSolidSolver::UpdateRigidBodies(vector<double>& ui)\n{\n\t// get the number of rigid bodies\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tconst int NRB = fem.RigidBodies();\n\n\t// first calculate the rigid body displacement increments\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tint *lm = RB.m_LM;\n\t\tdouble* du = RB.m_du;\n\n\t\tif (RB.m_prb == 0)\n\t\t{\n\t\t\tfor (int j = 0; j<6; ++j)\n\t\t\t{\n\t\t\t\tdu[j] = (lm[j] >= 0 ? m_Ui[lm[j]] + ui[lm[j]] : 0);\n\t\t\t}\n\t\t}\n\t}\n\n\t// for prescribed displacements, the displacement increments are evaluated differently\n\t// TODO: Is this really necessary? Why can't the ui vector contain the correct values?\n\tfor (int i = 0; i < NRB; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tif (RB.m_prb == nullptr)\n\t\t{\n\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t{\n\t\t\t\tFERigidPrescribedBC* dc = RB.m_pDC[j];\n\t\t\t\tif (dc)\n\t\t\t\t{\n\t\t\t\t\tint I = dc->GetBC();\n\t\t\t\t\tRB.m_du[I] = dc->Value() - RB.m_Up[I];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// update the rigid bodies\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tdouble* du = RB.m_du;\n\n\t\t// This is the \"new\" update algorithm which addressesses a couple issues\n\t\t// with the old method, namely that prescribed rotational dofs aren't update correctly.\n\t\t// Unfortunately, it seems to produce worse convergence in some cases, especially with line search\n\t\t// and it doesn't work when rigid bodies are used in a hierarchy\n\t\tif (RB.m_prb) du = RB.m_dul;\n\t\tRB.m_Ut[0] = RB.m_Up[0] + du[0];\n\t\tRB.m_Ut[1] = RB.m_Up[1] + du[1];\n\t\tRB.m_Ut[2] = RB.m_Up[2] + du[2];\n\t\tRB.m_Ut[3] = RB.m_Up[3] + du[3];\n\t\tRB.m_Ut[4] = RB.m_Up[4] + du[4];\n\t\tRB.m_Ut[5] = RB.m_Up[5] + du[5];\n\n\t\tRB.m_rt = RB.m_r0 + vec3d(RB.m_Ut[0], RB.m_Ut[1], RB.m_Ut[2]);\n\n\t\tvec3d Rt(RB.m_Ut[3], RB.m_Ut[4], RB.m_Ut[5]);\n\t\tRB.SetRotation(quatd(Rt));\n\t}\n\n\t// we need to update the position of rigid nodes\n\tfem.UpdateRigidMesh();\n\n\t// Since the rigid nodes are repositioned we need to update the displacement DOFS\n\tFEMesh& mesh = fem.GetMesh();\n\tint N = mesh.Nodes();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tvec3d ut = node.m_rt - node.m_r0;\n\t\t\tnode.set_vec3d(m_dofU[0], m_dofU[1], m_dofU[2], ut);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FECGSolidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolver.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a solver for solid mechanics problems that uses\n//! the conjugate gradient method to solve the nonlinear finite element equations\nclass FECGSolidSolver : public FESolver\n{\npublic:\n\t//! constructor\n\tFECGSolidSolver(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! clean up\n\tvoid Clean() override;\n\n\t//! Performs a CG step\n\tbool SolveStep() override;\n\nprivate:\n\tbool CalculatePreconditioner();\n\npublic:\n\t//! update model\n\tvoid Update(std::vector<double>& u) override;\n\n\t//! update nodal positions, velocities, accelerations, etc.\n\tvoid UpdateKinematics(vector<double>& ui);\n\n\t// Initialize linear equation system (TODO: Is this the right place to do this?)\n\t// \\todo Can I make this part of the Init function?\n\tvirtual bool InitEquations() override;\n\nprotected:\n\t//! update rigid bodies\n\tvoid UpdateRigidBodies(vector<double>& ui);\n\n\t//! Evaluate the residual\n\tbool Residual(vector<double>& R);\n\n\t//! assemble into the residual\n\tvoid AssembleResidual(int node_id, int dof, double f, vector<double>& R);\n\n\t//! contact forces\n\tvoid ContactForces(FEGlobalVector& R);\n\n\t//! the non-linear constraint forces\n\tvoid NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n\t//! Inertial forces\n\tvoid InertialForces(FEGlobalVector& R);\n\n\t//! helper function for setting up the solution phase\n\tvoid PrepStep();\n\n\t//! modified linesearch for Hager-Zhang solver\n\tdouble LineSearchCG(double s);\n\npublic:\n\tdouble\tm_Dtol;\n\tdouble\tm_Etol;\n\tdouble\tm_Rtol;\n\tdouble\tm_Rmin;\n\tdouble\tm_LStol;\n\tdouble\tm_LSmin;\n\tint\t\tm_LSiter;\n\tint\t\tm_CGmethod;\n\tint\t\tm_precon;\n\tvector<double> m_Mi;\t//!< inverse mass vector for explicit analysis\n\n\t// Newmark parameters (for dynamic analyses)\n\tdouble\tm_beta;\t\t\t//!< Newmark parameter beta (displacement integration)\n\tdouble\tm_gamma;\t\t//!< Newmark parameter gamme (velocity integration)\n\nprivate:\n\tvector<double>\tm_R0;\n\tvector<double>\tm_R1;\n\tvector<double>\tm_Ui;\n\tvector<double>\tm_ui;\n\tvector<double>\tm_Ut;\n\tvector<double>\tm_Fn;\n\tvector<double>\tm_Fd;\n\tvector<double>\tm_Fr;\n\n\tint\t\t\t\tm_neq;\n\tint\t\t\t\tm_nreq;\n\t//int m_CGmethod; // 0 =  Hager-Zhang (default), 1 = steepest descent\n\nprotected:\n\tFEDofList\tm_dofU, m_dofV, m_dofQ, m_dofRQ,m_dofSU, m_dofSV, m_dofSA, m_rigidSolver;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECarreauYasudaViscousSolid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECarreauYasudaViscousSolid.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECarreauYasudaViscousSolid, FEElasticMaterial)\n    ADD_PARAMETER(m_mu0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu0\");\n    ADD_PARAMETER(m_mui, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mui\");\n    ADD_PARAMETER(m_lam, FE_RANGE_GREATER_OR_EQUAL(0.0), \"lambda\");\n    ADD_PARAMETER(m_n  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"n\");\n    ADD_PARAMETER(m_a  , FE_RANGE_GREATER_OR_EQUAL(2.0), \"a\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nmat3ds FECarreauYasudaViscousSolid::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    mat3ds D = pt.RateOfDeformation();\n//    double d[3];\n//    D.eigen(d);\n    \n    double mu0 = m_mu0(mp);\n    double mui = m_mui(mp);\n    double lam = m_lam(mp);\n    double n = m_n(mp);\n    double a = m_a(mp);\n    double gdot = sqrt(2*(D.sqr()).tr());\n//    double gdot = max(fabs(d[2]-d[1]),max(fabs(d[0]-d[2]),fabs(d[1]-d[0])));\n    double lamga = pow(lam*gdot,a);\n    double mu = mui + (mu0 - mui)*pow(1+lamga, (n-1)/a);\n    mat3ds s = D*(2*mu);\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FECarreauYasudaViscousSolid::Tangent(FEMaterialPoint& mp)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    tens4ds Cv;\n\n    if (tp.timeIncrement > 0) {\n        mat3ds D = pt.RateOfDeformation();\n\n        double mu0 = m_mu0(mp);\n        double mui = m_mui(mp);\n        double lam = m_lam(mp);\n        double n = m_n(mp);\n        double a = m_a(mp);\n        double gdot = sqrt(2*(D.sqr()).tr());\n        double lamga = pow(lam*gdot,a);\n        double mu = mui + (mu0 - mui)*pow(1+lamga, (n-1)/a);\n        double dmu = 2*(mu0 - mui)*(n-1)*pow(lam,a)*pow(gdot,a-2)*pow(1+lamga, (n-a-1)/a);\n\n        mat3dd I(1);\n        double tmp = tp.alphaf*tp.gamma/(tp.beta*tp.timeIncrement);\n        Cv = (dyad1s(D)*(2*dmu) + dyad4s(I)*(2*mu))*tmp;\n    }\n    else Cv.zero();\n\n    return Cv;\n}\n\n//-----------------------------------------------------------------------------\ndouble FECarreauYasudaViscousSolid::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n"
  },
  {
    "path": "FEBioMech/FECarreauYasudaViscousSolid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\nclass FECarreauYasudaViscousSolid : public FEElasticMaterial\n{\npublic:\n    FECarreauYasudaViscousSolid(FEModel* pfem) : FEElasticMaterial(pfem) {}\n    \npublic:\n    FEParamDouble   m_mu0;  //!< shear viscosity at zero shear rate\n    FEParamDouble   m_mui;  //!< shear viscosity at infinite shear rate\n    FEParamDouble   m_lam;  //!< time constant\n    FEParamDouble   m_n;    //!< power-law index\n    FEParamDouble   m_a;    //!< exponent\n    \npublic:\n    //! calculate stress at material point\n    virtual mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    virtual tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    virtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECarterHayesOld.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECarterHayesOld.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FECarterHayesOld, FEElasticMaterial)\n\tADD_PARAMETER(m_c, FE_RANGE_GREATER         (0.0), \"c\");\n\tADD_PARAMETER(m_g, FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FECarterHayes\n//////////////////////////////////////////////////////////////////////\n\ndouble FECarterHayesOld::StrainEnergy(FEMaterialPoint& mp)\n{\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble detF = pt.m_J;\n\tdouble lndetF = log(detF);\n\t\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble I1 = b.tr();\n\t\n\t// lame parameters\n\tdouble rhor = rpt.m_rhor;\n\tdouble m_E = YoungModulus(rhor);\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\t\n\tdouble sed = mu*((I1-3)/2 - lndetF)+lam*lndetF*lndetF/2;\n\t\n\treturn sed;\n}\n\nmat3ds FECarterHayesOld::Stress(FEMaterialPoint& mp)\n{\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\tdouble lndetF = log(detF);\n\t\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\t\n\t// lame parameters\n\tdouble rhor = rpt.m_rhor;\n\tdouble m_E = YoungModulus(rhor);\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// calculate stress\n\tmat3ds s = (b - I)*(mu*detFi) + I*(lam*lndetF*detFi);\n\t\n\treturn s;\n}\n\ntens4ds FECarterHayesOld::Tangent(FEMaterialPoint& mp)\n{\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tdouble detF = pt.m_J;\n\t\n\t// lame parameters\n\tdouble rhor = rpt.m_rhor;\n\tdouble m_E = YoungModulus(rhor);\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\t\n\tdouble lam1 = lam / detF;\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\n\t\n\tdouble D[6][6] = {0};\n\tD[0][0] = lam1+2.*mu1; D[0][1] = lam1       ; D[0][2] = lam1       ;\n\tD[1][0] = lam1       ; D[1][1] = lam1+2.*mu1; D[1][2] = lam1       ;\n\tD[2][0] = lam1       ; D[2][1] = lam1       ; D[2][2] = lam1+2.*mu1;\n\tD[3][3] = mu1;\n\tD[4][4] = mu1;\n\tD[5][5] = mu1;\n\t\n\treturn tens4ds(D);\n}\n\n//! calculate tangent of strain energy density with mass density\ndouble FECarterHayesOld::Tangent_SE_Density(FEMaterialPoint& mp)\n{\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n    return StrainEnergy(mp)*m_g/rpt.m_rhor;\n}\n\n//! calculate tangent of stress with mass density\nmat3ds FECarterHayesOld::Tangent_Stress_Density(FEMaterialPoint& mp)\n{\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n    return Stress(mp)*m_g/rpt.m_rhor;\n}\n\n"
  },
  {
    "path": "FEBioMech/FECarterHayesOld.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FERemodelingElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This is a neo-Hookean material whose Young's modulus is evaluated from the density\n//! according to the power-law relation proposed by Carter and Hayes for trabecular bone\n\nclass FECarterHayesOld : public FEElasticMaterial, public FERemodelingInterface\n{\npublic:\n\tFECarterHayesOld(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\t\npublic:\n\tdouble\tm_c;\t//!< c coefficient for calculation of Young's modulus\n\tdouble\tm_g;\t//!< gamma exponent for calculation of Young's modulus\n\tdouble\tm_v;\t//!< prescribed Poisson's ratio\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\t\npublic: // --- remodeling interface ---\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergy(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent of strain energy density with mass density\n\tdouble Tangent_SE_Density(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent of stress with mass density\n\tmat3ds Tangent_Stress_Density(FEMaterialPoint& pt) override;\n\n\t//! return Young's modulus\n\tdouble YoungModulus(double rhor) { return m_c*pow(rhor, m_g);}\n\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECellGrowth.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECellGrowth.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECellGrowth, FEElasticMaterial)\n\tADD_PARAMETER(m_phir, FE_RANGE_GREATER(0.0), \"phir\");\n\tADD_PARAMETER(m_cr  , FE_RANGE_GREATER(0.0), \"cr\")->setUnits(UNIT_CONCENTRATION);\n\tADD_PARAMETER(m_ce  , FE_RANGE_GREATER(0.0), \"ce\")->setUnits(UNIT_CONCENTRATION);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFECellGrowth::FECellGrowth(FEModel* pfem) : FEElasticMaterial(pfem) \n{ \n\tm_Rgas = 0; \n\tm_Tabs = 0; \n\n\tm_phir = 0;\n\tm_cr = 0;\n\tm_ce = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FECellGrowth::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\tm_Rgas = GetGlobalConstant(\"R\");\n\tm_Tabs = GetGlobalConstant(\"T\");\n\t\n\tif (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");\t return false; }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FECellGrowth::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\t\n\t// calculate intracellular osmolarity relative to mixture volume in reference configuration\n\tdouble c = m_cr/(J-m_phir);\n\t\n\t// calculate osmotic pressure\n\tdouble p = m_Rgas*m_Tabs*(c - m_ce);\n\t\n\t// calculate T = -p*I\n\tmat3dd I(1.0);\t// identity tensor\n\tmat3ds s = -p*I;\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FECellGrowth::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\t\n\t// calculate intracellular osmolarity relative to mixture volume in reference configuration\n\tdouble c = m_cr/(J-m_phir);\n\t\t\n\t// calculate osmotic pressure\n\tdouble p = m_Rgas*m_Tabs*(c - m_ce);\n\t\n\tmat3dd I(1.0);\t// Identity\n\t\n\ttens4ds I1 = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\t\n\t// calculate tangent osmotic modulus\n\ttens4ds C = I4*(2*p) - I1*(p-m_Rgas*m_Tabs*c*J/(J-m_phir));\n\treturn C;\n}\n\n"
  },
  {
    "path": "FEBioMech/FECellGrowth.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class that implements the equilibrium of a perfect osmometer.\n//\nclass FECellGrowth : public FEElasticMaterial\n{\npublic:\n\t//! When used on its own (not in a solid mixture), this materials\n\t//! is intrinsically unstable\n\tFECellGrowth(FEModel* pfem);\n\t\n\t//! Initialization routine\n\tbool Init() override;\n\t\n\t//! Returns the Cauchy stress\n\tvirtual mat3ds Stress(FEMaterialPoint& mp) override;\n\t\n\t//! Returs the spatial tangent\n\tvirtual tens4ds Tangent(FEMaterialPoint& mp) override;\n\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\t\npublic:\n\tdouble\tm_phir;\t\t//!< intracellular solid volume fraction normalized to reference configuration\n\tdouble\tm_cr;\t\t//!< intracellular osmolarity normalized to reference configuration\n\tdouble\tm_ce;\t\t//!< extracellular osmolarity\n\tdouble\tm_Rgas;\t\t//!< universal gas constant\n\tdouble\tm_Tabs;\t\t//!< absolute temperature\n};\n"
  },
  {
    "path": "FEBioMech/FECentrifugalBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECentrifugalBodyForce.h\"\n#include \"FEElasticMaterial.h\"\n\nBEGIN_FECORE_CLASS(FECentrifugalBodyForce, FEBodyForce);\n\tADD_PARAMETER(w, \"angular_speed\")->setUnits(UNIT_ANGULAR_VELOCITY);\n\tADD_PARAMETER(n, \"rotation_axis\");\n\tADD_PARAMETER(c, \"rotation_center\")->setUnits(UNIT_LENGTH);\nEND_FECORE_CLASS();\n\nFECentrifugalBodyForce::FECentrifugalBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n\tw = 0.0;\n\tn = vec3d(0,0,1);\n\tc = vec3d(0,0,0);\n}\n\nvec3d FECentrifugalBodyForce::force(FEMaterialPoint& mp)\n{\n\tmat3d K = stiffness(mp);\n\treturn K*(mp.m_rt - c);\n}\n\nmat3d FECentrifugalBodyForce::stiffness(FEMaterialPoint& mp) \n{ \n\treturn (mat3dd(1) - dyad(n))*(-w*w); \n}\n\ndouble FECentrifugalBodyForce::divforce(FEMaterialPoint& mp)\n{\n    return -2*w*w;\n}\n"
  },
  {
    "path": "FEBioMech/FECentrifugalBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBodyForce.h\"\n\n//-----------------------------------------------------------------------------\n//! This class defines a centrigufal force\n\nclass FECentrifugalBodyForce : public FEBodyForce\n{\npublic:\n\tFECentrifugalBodyForce(FEModel* pfem);\n\n\tvec3d force(FEMaterialPoint& mp) override;\n\n    double divforce(FEMaterialPoint& mp) override;\n    \n\tmat3d stiffness(FEMaterialPoint& mp) override;\n\npublic:\n\tvec3d\tn;\t// rotation axis\n\tvec3d\tc;\t// point on axis of rotation (e.g., center of rotation)\n\tdouble\tw;\t// angular speed\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEConstPrestrain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEConstPrestrain.h\"\n\n//-----------------------------------------------------------------------------\nFEConstPrestrainGradient::MaterialPointData::MaterialPointData() \n{\n\tFp.unit();\n}\n\t\t\n//-----------------------------------------------------------------------------\nvoid FEConstPrestrainGradient::MaterialPointData::Init(bool bflag) \n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEConstPrestrainGradient::MaterialPointData::Copy()\n{ \n\tMaterialPointData* pm = new MaterialPointData(*this); \n\treturn pm;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstPrestrainGradient::MaterialPointData::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & Fp;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEConstPrestrainGradient, FEPrestrainGradient)\n\tADD_PARAMETER(m_ramp, \"ramp\");\n\tADD_PARAMETER(m_Fp, \"F0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConstPrestrainGradient::FEConstPrestrainGradient(FEModel* pfem) : FEPrestrainGradient(pfem)\n{\n\tm_ramp = 1.0;\n\tm_Fp = mat3dd(1.0);\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEConstPrestrainGradient::CreateMaterialPointData()\n{\n\treturn new MaterialPointData;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEConstPrestrainGradient::Prestrain(FEMaterialPoint& mp)\n{\n\tMaterialPointData& ep = *mp.ExtractData<MaterialPointData>();\n\n\tmat3d Fp = mat3dd(1.0) * (1.0 - m_ramp) + m_Fp(mp) * m_ramp;\n\n\treturn Fp*ep.Fp;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstPrestrainGradient::Initialize(const mat3d& F, FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\tFEConstPrestrainGradient::MaterialPointData* ps = mp.ExtractData<FEConstPrestrainGradient::MaterialPointData>();\n/*\n\t// calculate left polar decomposition\n\tmat3d R;\n\tmat3ds V;\n\tF.left_polar(V, R);\n\n\t// assign it to the pre-strain gradient\n\tps->Fp = V;\n\n\t// adjust fiber vector\n\tvec3d a0 = pt->m_Q.col(0);\n\tvec3d a1 = R*a0;\n\n\t// setup orthogonal system\n\tvec3d b(0,0,1);\n\tif (b*a1 > 0.9) b = vec3d(0,1,0);\n\tvec3d a3 = a1^b;\n\tvec3d a2 = a3^a1;\n\n\tmat3d Q;\n\tQ(0,0) = a1.x; Q(0,1) = a2.x; Q(0,2) = a3.x;\n\tQ(1,0) = a1.y; Q(1,1) = a2.y; Q(1,2) = a3.y;\n\tQ(2,0) = a1.z; Q(2,1) = a2.z; Q(2,2) = a3.z;\n\n\tpt->m_Q = Q;\n*/\n\tps->Fp = F;\n}\n"
  },
  {
    "path": "FEBioMech/FEConstPrestrain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEPreStrainElastic.h\"\n\n//-----------------------------------------------------------------------------\n// A constant, full-tensor pre-strain gradient\nclass FEConstPrestrainGradient : public FEPrestrainGradient\n{\npublic:\n\tclass MaterialPointData : public FEMaterialPointData\n\t{\n\tpublic:\n\t\tMaterialPointData();\n\t\tvoid Init(bool bflag);\n\t\tFEMaterialPointData* Copy();\n\t\tvoid Serialize(DumpStream& ar);\n\n\tpublic:\n\t\tmat3d\tFp;\n\t};\n\npublic:\n\tFEConstPrestrainGradient(FEModel* pfem);\n\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\tmat3d Prestrain(FEMaterialPoint& mp) override;\n\n\tvoid Initialize(const mat3d& F, FEMaterialPoint& mp) override;\n\nprotected:\n\tdouble\t\t\tm_ramp;\n\tFEParamMat3d\tm_Fp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEContactGapCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEContactGapCriterion.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\nBEGIN_FECORE_CLASS(FEContactGapCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_gap, \"gap\");\nEND_FECORE_CLASS();\n\nFEContactGapCriterion::FEContactGapCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_gap = 0;\n}\n\nFEMeshAdaptorSelection FEContactGapCriterion::GetElementSelection(FEElementSet* elset)\n{\n\tFEMeshAdaptorSelection sel;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci && pci->IsActive())\n\t\t{\n\t\t\tFEContactSurface* pcs = dynamic_cast<FEContactSurface*>(pci->GetPrimarySurface());\n\t\t\tif (pcs)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j < pcs->Elements(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& face = pcs->Element(j);\n\t\t\t\t\tif (face.isActive())\n\t\t\t\t\t{\n\t\t\t\t\t\tint nint = face.GaussPoints();\n\t\t\t\t\t\tdouble max_gap = m_gap;\n\t\t\t\t\t\tfor (int k = 0; k < nint; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEMaterialPoint* mp = face.GetMaterialPoint(k);\n\t\t\t\t\t\t\tFEContactMaterialPoint* cp = dynamic_cast<FEContactMaterialPoint*>(mp);\n\t\t\t\t\t\t\tif (cp)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (cp->m_gap > max_gap) max_gap = cp->m_gap;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (max_gap > m_gap)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEElement* pe = face.m_elem[0].pe;\n\t\t\t\t\t\t\tif (pe)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// TODO: Add check for duplicate element addition \n\t\t\t\t\t\t\t\tsel.push_back({ pe->GetID(), max_gap});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sel;\n}\n"
  },
  {
    "path": "FEBioMech/FEContactGapCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n\nclass FEContactGapCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEContactGapCriterion(FEModel* fem);\n\n\tFEMeshAdaptorSelection GetElementSelection(FEElementSet* elset) override;\n\nprivate:\n\tdouble m_gap;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEBioMech/FEContactInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEContactInterface.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEContactSurface.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolver.h>\n#include <FECore/FEAnalysis.h>\n#include \"FEElasticDomain.h\"\n\nBEGIN_FECORE_CLASS(FEContactInterface, FESurfacePairConstraint)\n\tADD_PARAMETER(m_psf   , \"penalty_sf\"    )->setLongName(\"penalty scale factor\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_psfmax, \"max_penalty_sf\")->setLongName(\"Max penalty scale factor\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEContactInterface::FEContactInterface(FEModel* pfem) : FESurfacePairConstraint(pfem)\n{\n\tm_laugon = FECore::PENALTY_METHOD;\t// penalty method by default\n    m_psf = 1.0;    // default scale factor is 1\n    m_psfmax = 0;   // default max scale factor is not set\n}\n\nFEContactInterface::~FEContactInterface()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates a contact penalty parameter based on the \n//! material and geometrical properties of the primary and secondary surfaces\n//!\ndouble FEContactInterface::AutoPenalty(FESurfaceElement& el, FESurface &s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == nullptr) return 0.0;\n\n\t// make sure the parent domain is an elastic domain\n\tFEElasticDomain* ped = dynamic_cast<FEElasticDomain*>(pe->GetMeshPartition());\n\tif (ped == nullptr)\n\t{\n\t\tif ((pe = el.m_elem[1].pe) == nullptr) return 0.0;\n\t\tped = dynamic_cast<FEElasticDomain*>(pe->GetMeshPartition());\n\t\tif (ped == nullptr) return 0.0;\n\t}\n\n\tdouble eps = 0;\n\n\t// extract the elastic material\n\tFEElasticMaterial* pme = GetFEModel()->GetMaterial(pe->GetMatID())->ExtractProperty<FEElasticMaterial>();\n    if (pme) {\n        // get a material point\n        FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// backup the material point\n\t\tmat3d F0 = pt.m_F;\n\t\tdouble J0 = pt.m_J;\n\t\tmat3ds s0 = pt.m_s;\n\n        // override the material point\n        pt.m_F = mat3dd(1.0);\n        pt.m_J = 1;\n        pt.m_s.zero();\n        \n        // get the tangent (stiffness) and it inverse (compliance) at this point\n        tens4ds S = pme->Tangent(mp);\n        tens4ds C = S.inverse();\n        \n\t\t// restore the material point\n\t\tpt.m_F = F0;\n\t\tpt.m_J = J0;\n\t\tpt.m_s = s0;\n\n        // evaluate element surface normal at parametric center\n        vec3d t[2];\n        s.CoBaseVectors0(el, 0, 0, t);\n        vec3d n = t[0] ^ t[1];\n        n.unit();\n        \n        // evaluate normal component of the compliance matrix\n        // (equivalent to inverse of Young's modulus along n)\n        eps = 1./(n*(vdotTdotv(n, C, n)*n));\n    }\n    else {\n        FERigidMaterial* prm = GetFEModel()->GetMaterial(pe->GetMatID())->ExtractProperty<FERigidMaterial>();\n        if (prm == nullptr) return 0.0;\n        eps = prm->m_E/(1 - pow(prm->m_v, 2));\n        if (eps == 0) return 0.0;\n    }\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n\t// If the surface is a rigid shell with no thickness (which is allowed),\n\t// the volume can be zero. In that case, we return 0. (which is also backward compatible)\n\tif (V == 0.0) return 0.0;\n\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEContactInterface::Serialize(DumpStream& ar)\n{\n\t// store base class\n\tFESurfacePairConstraint::Serialize(ar);\n\n\t// save parameters\n\tar & m_laugon;\n\n\tif ((ar.IsShallow() == false) && (ar.IsSaving() == false))\n\t{\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tFESurface* ss = GetPrimarySurface();\n\t\tFESurface* ms = GetSecondarySurface();\n\t\tif (ss) mesh.AddSurface(ss);\n\t\tif (ms) mesh.AddSurface(ms);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// serialize the pointers\nvoid FEContactInterface::SerializeElementPointers(FEContactSurface& ss, FEContactSurface& ms, DumpStream& ar)\n{\n\tif (ar.IsSaving())\n\t{\n\t\tint NE = ss.Elements();\n\t\tfor (int i = 0; i<NE; ++i)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\tfor (int j = 0; j<se.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFEContactMaterialPoint& ds = static_cast<FEContactMaterialPoint&>(*se.GetMaterialPoint(j));\n\t\t\t\tint eid0 = (ds.m_pme  ? ds.m_pme ->m_lid : -1);\n\t\t\t\tint eid1 = (ds.m_pmep ? ds.m_pmep->m_lid : -1);\n\t\t\t\tar << eid0 << eid1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tint lid = -1;\n\t\tint NE = ss.Elements();\n\t\tfor (int i = 0; i<NE; ++i)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\tfor (int j = 0; j<se.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFEContactMaterialPoint& ds = static_cast<FEContactMaterialPoint&>(*se.GetMaterialPoint(j));\n\t\t\t\tint eid0 = -1, eid1 = -1;\n\t\t\t\tar >> eid0 >> eid1;\n\n\t\t\t\tif (eid0 >= 0) ds.m_pme  = &ms.Element(eid0); else ds.m_pme  = nullptr;\n\t\t\t\tif (eid1 >= 0) ds.m_pmep = &ms.Element(eid1); else ds.m_pmep = nullptr;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FEContactInterface::GetPenaltyScaleFactor()\n{\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolver* psolver = pstep->GetFESolver();\n    int naug = psolver->m_naug;\n    double psf = pow(m_psf,naug);\n    if ((m_psfmax > 0) && (psf > m_psfmax)) psf = m_psfmax;\n    return psf;\n}\n"
  },
  {
    "path": "FEBioMech/FEContactInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfacePairConstraint.h>\n#include \"febiomech_api.h\"\n\nclass FEModel;\nclass FESolver;\nclass FEGlobalMatrix;\nclass FEContactSurface;\n\n// Macauley bracket\n#define MBRACKET(x) ((x)>=0? (x): 0)\n\n// Heavyside function\n#define HEAVYSIDE(x) ((x)>=0?1:0)\n\n\n//-----------------------------------------------------------------------------\n//! This is the base class for contact interfaces\nclass FEBIOMECH_API FEContactInterface : public FESurfacePairConstraint\n{\npublic:\n\t//! constructor\n\tFEContactInterface(FEModel* pfem);\n\n\t//! destructor\n\t~FEContactInterface();\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n    //! cale the penalty factor during Lagrange augmentation\n    double GetPenaltyScaleFactor();\n    \npublic:\n\t// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\n\tvirtual void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) = 0;\n\n\t// Evaluates the contriubtion to the stiffness matrix\n\tvirtual void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) = 0;\n\nprotected:\n\t//! don't call the default constructor\n\tFEContactInterface() : FESurfacePairConstraint(0){}\n\n\t//! auto-penalty calculation\n\tdouble AutoPenalty(FESurfaceElement& el, FESurface& s);\n\n\t// serialize the element pointers\n\tvoid SerializeElementPointers(FEContactSurface& ss, FEContactSurface& ms, DumpStream& ar);\n\npublic:\n\tint\t\tm_laugon;\t//!< contact enforcement method\n    double  m_psf;      //!< penalty scale factor during Lagrange augmentation\n    double  m_psfmax;   //!< max allowable penalty scale factor during laugon\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEContactPotential.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEContactPotential.h\"\n#include <FECore/FENode.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEBox.h>\n#include <stdexcept>\n#include <FECore/FENodeNodeList.h>\n#include <FECore/FEMesh.h>\n#include <FECore/log.h>\n#include <omp.h>\n\nvoid FEContactPotential::UpdateSurface(FESurface& surface)\n{\n// This assumes we are inside a omp parallel region!\n#pragma omp for\n\tfor (int i = 0; i < surface.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = surface.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tvec3d re[FEElement::MAX_NODES];\n\t\t\tsurface.GetNodalCoordinates(el, re);\n\n\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t{\n\t\t\t\tFECPContactPoint& mp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\tmp.m_rt = surface.Position(el, n);\n\n\t\t\t\t// kinematics at integration points\n\t\t\t\tmp.dxr = el.eval_deriv1(re, n);\n\t\t\t\tmp.dxs = el.eval_deriv2(re, n);\n\n\t\t\t\tmp.m_Jt = (mp.dxr ^ mp.dxs).norm();\n\t\t\t}\n\t\t}\n\t}\n}\n\nFEContactPotentialSurface::FEContactPotentialSurface(FEModel* fem) : FEContactSurface(fem)\n{\n\n}\n\nvoid FEContactPotentialSurface::InitSurface()\n{\n\tFEContactPotential* pci = dynamic_cast<FEContactPotential*>(GetContactInterface());\n\n\tif (pci && (pci->IntegrationRule() == 1))\n\t{\n\t\tFEFacetSet& fs = *m_surf;\n\t\tint elems = Elements();\n\t\tfor (int i = 0; i < elems; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = Element(i);\n\t\t\tFEFacetSet::FACET& fi = fs.Face(i);\n\n\t\t\tif      (fi.ntype == 4) el.SetType(FE_QUAD4G16);\n\t\t\telse if (fi.ntype == 3) el.SetType(FE_TRI3G7);\n\t\t\telse assert(false);\n\n\t\t\tint N = el.Nodes(); assert(N == fi.ntype);\n\t\t\tfor (int j = 0; j < N; ++j) el.m_node[j] = fi.node[j];\n\t\t}\n\n\t\tCreateMaterialPointData();\n\t}\n\n\tFEContactSurface::InitSurface();\n}\n\nFEMaterialPoint* FEContactPotentialSurface::CreateMaterialPoint()\n{\n\treturn new FECPContactPoint;\n}\n\nvoid FEContactPotentialSurface::GetContactTraction(int nelem, vec3d& tc)\n{\n\tFESurfaceElement& el = Element(nelem);\n\ttc = vec3d(0, 0, 0);\n\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t{\n\t\tFECPContactPoint& mp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\ttc += mp.m_tc;\n\t}\n\ttc /= (double)el.GaussPoints();\n}\n\nvec3d FEContactPotentialSurface::GetContactForce()\n{\n\tvec3d F(0, 0, 0);\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tdouble* gw = el.GaussWeights();\n\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t{\n\t\t\t\tFECPContactPoint& mp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\tvec3d dA = mp.dxr ^ mp.dxs;\n\t\t\t\tdouble da = dA.norm();\n\t\t\t\tF += mp.m_tc * (da * gw[n]);\n\t\t\t}\n\t\t}\n\t}\n\treturn F;\n}\n\ndouble FEContactPotentialSurface::GetContactArea()\n{\n\tdouble area = 0.0;\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tdouble* gw = el.GaussWeights();\n\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t{\n\t\t\t\tFECPContactPoint& mp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\tif (mp.m_tc.norm() != 0.0)\n\t\t\t\t{\n\t\t\t\t\tvec3d dA = mp.dxr ^ mp.dxs;\n\t\t\t\t\tdouble da = dA.norm();\n\t\t\t\t\tarea += da * gw[n];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn area;\n}\n\n\nBEGIN_FECORE_CLASS(FEContactPotential, FEContactInterface)\n\t// just adding this parameter for backward compatibility. \n\tADD_PARAMETER(m_laugon, \"laugon\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n\n\tADD_PARAMETER(m_kc, \"kc\");\n\tADD_PARAMETER(m_p, \"p\");\n\tADD_PARAMETER(m_Rin, \"R_in\");\n\tADD_PARAMETER(m_Rout, \"R_out\");\n\tADD_PARAMETER(m_Rmin, \"R0_min\");\n\tADD_PARAMETER(m_wtol, \"w_tol\");\n\tADD_PARAMETER(m_checkIntersections, \"check_intersections\");\n\tADD_PARAMETER(m_integrationRule, \"integration_rule\")->setEnums(\"default\\0higher-order\\0\");\n\tADD_PARAMETER(m_excludeNeighbors, \"exclude_neighbors\");\nEND_FECORE_CLASS();\n\nFEContactPotential::FEContactPotential(FEModel* fem) : FEContactInterface(fem), m_surf1(fem), m_surf2(fem)\n{\n\tm_surf1.SetContactInterface(this);\n\tm_surf2.SetContactInterface(this);\n\n\tm_kc = 0.0;\n\tm_p = 4;\n\tm_Rin = 1.0;\n\tm_Rout = 2.0;\n\tm_Rmin = 0.0;\n\tm_wtol = 0.0;\n\tm_checkIntersections = false;\n\tm_integrationRule = 0;\n}\n\n//! return the primary surface\nFESurface* FEContactPotential::GetPrimarySurface()\n{\n\treturn &m_surf1;\n}\n\n//! return the secondary surface\nFESurface* FEContactPotential::GetSecondarySurface()\n{\n\treturn &m_surf2;\n}\n\n//! temporary construct to determine if contact interface uses nodal integration rule (or facet)\nbool FEContactPotential::UseNodalIntegration()\n{\n\treturn false;\n}\n\nstatic bool is_neighbor(FESurfaceElement& e1, FESurfaceElement& e2)\n{\n\tint n1 = e1.Nodes();\n\tint n2 = e2.Nodes();\n\tfor (int i = 0; i < n1; ++i)\n\tfor (int j = 0; j < n2; ++j)\n\t{\n\t\tif (e1.m_node[i] == e2.m_node[j]) return true;\n\t}\n\treturn false;\n}\n\nstatic bool is_same(FESurfaceElement& e1, FESurfaceElement& e2)\n{\n\tint n1 = e1.Nodes();\n\tint n2 = e2.Nodes();\n\tfor (int i = 0; i < n1; ++i)\n\t{\n\t\tif (e1.m_node[i] != e2.m_node[i]) return false;\n\t}\n\treturn true;\n}\n\n\nstruct BOX\n{\npublic:\n\tvec3d r0, r1;\n\npublic:\n\tBOX() {}\n\tBOX(const vec3d& p0, const vec3d& p1) : r0(p0), r1(p1) {}\n\n\tvoid add(const vec3d& r)\n\t{\n\t\tif (r.x < r0.x) r0.x = r.x;\n\t\tif (r.y < r0.y) r0.y = r.y;\n\t\tif (r.z < r0.z) r0.z = r.z;\n\n\t\tif (r.x > r1.x) r1.x = r.x;\n\t\tif (r.y > r1.y) r1.y = r.y;\n\t\tif (r.z > r1.z) r1.z = r.z;\n\t}\n\n\tvoid inflate(double l)\n\t{\n\t\tr0.x -= l; r0.y -= l; r0.z -= l;\n\t\tr1.x += l; r1.y += l; r1.z += l;\n\t}\n\n\tbool isInside(const vec3d& r) const\n\t{\n\t\treturn (\n\t\t\t(r.x >= r0.x) && (r.x <= r1.x) &&\n\t\t\t(r.y >= r0.y) && (r.y <= r1.y) &&\n\t\t\t(r.z >= r0.z) && (r.z <= r1.z));\n\t}\n\n\tdouble MaxExtent() const\n\t{\n\t\tdouble sx = r1.x - r0.x;\n\t\tdouble sy = r1.y - r0.y;\n\t\tdouble sz = r1.z - r0.z;\n\n\t\tif ((sx >= sy) && (sx >= sz)) return sx;\n\t\tif ((sy >= sx) && (sy >= sz)) return sy;\n\t\tif ((sz >= sx) && (sz >= sy)) return sz;\n\t\t// we should never reach here\n\t\treturn 0;\n\t}\n\n\tdouble width () const { return r1.x - r0.x; }\n\tdouble height() const { return r1.y - r0.y; }\n\tdouble depth () const { return r1.z - r0.z; }\n};\n\nclass FEContactPotential::Grid\n{\npublic:\n\tclass Cell\n\t{\n\tpublic:\n\t\tCell() {}\n\t\tCell(const Cell& c) : m_box(c.m_box), m_elemList(c.m_elemList), m_nbr(c.m_nbr) {}\n\t\tvoid operator = (const Cell& c) { \n\t\t\tm_box = c.m_box; \n\t\t\tm_elemList = c.m_elemList; \n\t\t\tm_nbr = c.m_nbr;\n\t\t}\n\n\t\tvoid add(FESurfaceElement* el)\n\t\t{\n\t\t\tm_elemList.insert(el);\n\t\t}\n\n\t\tbool empty() const { return m_elemList.empty(); }\n\n\tpublic:\n\t\tBOX m_box;\n\t\tset<FESurfaceElement*>\tm_elemList;\n\t\tvector<Cell*>\tm_nbr;\n\t\tint id = -1;\n\t};\n\n\tCell* FindCell(const vec3d& r)\n\t{\n\t\tif (box.isInside(r) == false) return nullptr;\n\n\t\tint ix = (int)(m_nx * (r.x - box.r0.x) / box.width());\n\t\tint iy = (int)(m_ny * (r.y - box.r0.y) / box.height());\n\t\tint iz = (int)(m_nz * (r.z - box.r0.z) / box.depth());\n\t\tif (ix == m_nx) ix--;\n\t\tif (iy == m_ny) iy--;\n\t\tif (iz == m_nz) iz--;\n\n\t\tint icell = iz * (m_nx * m_ny) + iy * m_nx + ix;\n\t\tCell* c = m_cell + icell;\n\t\tassert(c->m_box.isInside(r));\n\t\treturn m_cell + icell;\n\t}\n\n\tint GetCellNeighborHood(const vec3d& r, Cell** cellList)\n\t{\n\t\tCell* c = FindCell(r);\n\t\tif (c == nullptr) return 0;\n\n\t\tint n = 0;\n\t\tfor (int i = 0; i < 27; ++i)\n\t\t{\n\t\t\tCell* ci = c->m_nbr[i];\n\t\t\tif ((ci != nullptr) && (ci->empty() == false)) cellList[n++] = ci;\n\t\t}\n\t\treturn n;\n\t}\n\npublic:\n\tenum { MAX_OMP_LOCKS = 256 };\n\tGrid() { \n\t\tm_nx = m_ny = m_nz = 0; m_cell = nullptr; \n\t\tif (lock == nullptr) init_locks();\n\t}\n\n\tbool Build(FESurface& s, int boxDivs, double minBoxSize)\n\t{\n\t\t// update the bounding box\n\t\tfor (int i = 0; i < s.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& node = s.Node(i);\n\t\t\tvec3d ri = node.m_rt;\n\t\t\tif (i == 0) box.r0 = box.r1 = ri;\n\t\t\telse box.add(ri);\n\t\t}\n\n\t\t// inflate a little, just to be sure\n\t\tdouble R = box.MaxExtent();\n\t\tbox.inflate(minBoxSize);\n\n\t\t// determine the sizes\n\t\tdouble W = box.width();\n\t\tdouble H = box.height();\n\t\tdouble D = box.depth();\n\n\t\tdouble boxSize = box.MaxExtent() / boxDivs;\n\t\tif (boxSize < minBoxSize) boxSize = minBoxSize;\n\n\t\tm_nx = (int)(W / boxSize); if (m_nx < 1) m_nx = 1;\n\t\tm_ny = (int)(H / boxSize); if (m_ny < 1) m_ny = 1;\n\t\tm_nz = (int)(D / boxSize); if (m_nz < 1) m_nz = 1;\n\n\t\t// allocate the grid\n\t\tint ncells = m_nx * m_ny * m_nz;\n\t\tm_cell = new Cell[ncells];\n\n\t\t// helper lookup-table for cell neighbors\n\t\tconst int LUT[27][3] = {\n\t\t\t{-1,-1,-1},{ 0,-1,-1},{1,-1,-1},\n\t\t\t{-1, 0,-1},{ 0, 0,-1},{1, 0,-1},\n\t\t\t{-1, 1,-1},{ 0, 1,-1},{1, 1,-1},\n\t\t\t{-1,-1, 0},{ 0,-1, 0},{1,-1, 0},\n\t\t\t{-1, 0, 0},{ 0, 0, 0},{1, 0, 0},\n\t\t\t{-1, 1, 0},{ 0, 1, 0},{1, 1, 0},\n\t\t\t{-1,-1, 1},{ 0,-1, 1},{1,-1, 1},\n\t\t\t{-1, 0, 1},{ 0, 0, 1},{1, 0, 1},\n\t\t\t{-1, 1, 1},{ 0, 1, 1},{1, 1, 1} };\n\n\t\t// construct cells\n\t\tCell* c = m_cell;\n\t\tdouble X0 = box.r0.x;\n\t\tdouble Y0 = box.r0.y;\n\t\tdouble Z0 = box.r0.z;\n\t\tfor (int k = 0; k < m_nz; ++k)\n\t\t{\n\t\t\tdouble z0 = Z0 + k * D / m_nz;\n\t\t\tdouble z1 = Z0 + (k+1) * D / m_nz;\n\t\t\tfor (int j = 0; j < m_ny; ++j)\n\t\t\t{\n\t\t\t\tdouble y0 = Y0 + j * H / m_ny;\n\t\t\t\tdouble y1 = Y0 + (j + 1) * H / m_ny;\n\t\t\t\tfor (int i = 0; i < m_nx; ++i, ++c)\n\t\t\t\t{\n\t\t\t\t\tdouble x0 = X0 + i * W / m_nx;\n\t\t\t\t\tdouble x1 = X0 + (i + 1) * W / m_nx;\n\n\t\t\t\t\tc->m_box = BOX(vec3d(x0, y0, z0), vec3d(x1, y1, z1));\n\t\t\t\t\tdouble R = c->m_box.MaxExtent();\n\t\t\t\t\tc->m_box.inflate(R * 1e-4);\n\n\t\t\t\t\t// assign neighbors\n\t\t\t\t\tc->m_nbr.assign(27, nullptr);\n\t\t\t\t\tfor (int n = 0; n < 27; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst int* l = LUT[n];\n\t\t\t\t\t\tc->m_nbr[n] = GetCell(i + l[0], j + l[1], k + l[2]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tfor (int i = 0; i < ncells; ++i) m_cell[i].id = (i% MAX_OMP_LOCKS);\n\n\t\t// assign elements to grid cells\n#pragma omp parallel for\n\t\tfor (int i = 0; i < s.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = s.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t{\n\t\t\t\t\tFECPContactPoint& mp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\t\tCell* c = FindCell(mp.m_rt); assert(c);\n\t\t\t\t\tif (c == nullptr) continue;\n\t\t\t\t\tomp_set_lock(lock + c->id);\n\t\t\t\t\tc->add(&el);\n\t\t\t\t\tomp_unset_lock(lock + c->id);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tCell* GetCell(int i, int j, int k) const\n\t{\n\t\tif ((i < 0) || (i >= m_nx)) return nullptr;\n\t\tif ((j < 0) || (j >= m_ny)) return nullptr;\n\t\tif ((k < 0) || (k >= m_nz)) return nullptr;\n\t\treturn m_cell + (k * (m_nx * m_ny) + j * m_nx + i);\n\t}\n\n\t~Grid() { delete [] m_cell; }\n\nprotected:\n\tBOX\t\tbox;\n\tint\t\tm_nx, m_ny, m_nz;\n\tCell*\tm_cell;\n\npublic:\n\tstatic omp_lock_t* lock;\n\tstatic void init_locks()\n\t{\n\t\tlock = new omp_lock_t[MAX_OMP_LOCKS];\n\t\tfor (int i = 0; i < MAX_OMP_LOCKS; ++i) omp_init_lock(lock + i);\n\t}\n\n\tstatic void delete_locks()\n\t{\n\t\tif (lock)\n\t\t{\n\t\t\tfor (int i = 0; i < MAX_OMP_LOCKS; ++i) omp_destroy_lock(lock + i);\n\t\t\tdelete[] lock;\n\t\t\tlock = nullptr;\n\t\t}\n\t}\n};\n\nomp_lock_t* FEContactPotential::Grid::lock = nullptr;\n\n// initialization\nbool FEContactPotential::Init()\n{\n\tif (FEContactInterface::Init() == false) return false;\n\tBuildNeighborTable();\n\tm_activeElements.resize(m_surf1.Elements());\n\tm_NNL.Create(m_surf1);\n\treturn true;\n}\n\nFEContactPotential::~FEContactPotential()\n{\n\tGrid::delete_locks();\n}\n\nvoid FEContactPotential::BuildNeighborTable()\n{\n\tm_elemNeighbors.resize(m_surf1.Elements());\n\n\tif (m_excludeNeighbors)\n\t{\n\t\t// build the full neighbor element list.\n\t\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el1 = m_surf1.Element(i);\n\t\t\tif (el1.isActive())\n\t\t\t{\n\t\t\t\tset<FESurfaceElement*>& nbrList = m_elemNeighbors[i];\n\t\t\t\tnbrList.clear();\n\n\t\t\t\tfor (int j = 0; j < m_surf2.Elements(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& el2 = m_surf2.Element(j);\n\t\t\t\t\tif (el2.isActive() && is_neighbor(el1, el2))\n\t\t\t\t\t{\n\t\t\t\t\t\tnbrList.insert(&el2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// If we don't exclude neighbors, we just build a list of each element itself.\n\t\t// Just to ensure that the element is not included in the case of self-contact.\n\t\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el1 = m_surf1.Element(i);\n\t\t\tif (el1.isActive())\n\t\t\t{\n\t\t\t\tset<FESurfaceElement*>& nbrList = m_elemNeighbors[i];\n\t\t\t\tnbrList.clear();\n\n\t\t\t\tfor (int j = 0; j < m_surf2.Elements(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& el2 = m_surf2.Element(j);\n\t\t\t\t\tif (el2.isActive() && ::is_same(el1, el2))\n\t\t\t\t\t{\n\t\t\t\t\t\tnbrList.insert(&el2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// update\nvoid FEContactPotential::Update()\n{\n\tFEContactInterface::Update();\n\n\t// update the constants\n\tm_c1 = 0.5 * m_p * m_kc / ((m_Rout - m_Rin) * pow(m_Rin, m_p + 1.0));\n\tm_c2 = m_kc / pow(m_Rin, m_p) - m_c1 * (m_Rin - m_Rout) * (m_Rin - m_Rout);\n\n\t// Update the surfaces\n#pragma omp parallel \n\t{\n\t\tUpdateSurface(m_surf1);\n\t\tUpdateSurface(m_surf2);\n\t}\n\n\t// build the grid\n\tint ndivs = (int)pow(m_surf2.Elements(), 0.33333);\n\tif (ndivs < 2) ndivs = 2;\n\tGrid g;\n\tif (g.Build(m_surf2, ndivs, m_Rout) == false)\n\t{\n\t\tthrow std::runtime_error(\"Failed to build grid in FEContactPotential::Update\");\n\t}\n\n\tif (m_checkIntersections)\n\t{\n\t\tfeLog(\"Checking intersections ...\");\n\t\tif (CheckIntersections(g))\n\t\t{\n\t\t\tfeLog(\"FOUND!\\n\");\n\t\t\tthrow NegativeJacobianDetected();\n\t\t}\n\t\telse feLog(\"all good.\\n\");\n\t}\n\n\t// build the list of active elements\n#pragma omp parallel for shared(g) schedule(dynamic, 5)\n\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el1 = m_surf1.Element(i);\n\t\tif (el1.isActive())\n\t\t{\n\t\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\t\tactiveElems.clear();\n\n\t\t\t// list of elements to exclude. This will be neighbors and elements\n\t\t\t// already processed\n\t\t\tset<FESurfaceElement*> excludeList = m_elemNeighbors[i];\n\n\t\t\tfor (int n = 0; n < el1.GaussPoints(); ++n)\n\t\t\t{\n\t\t\t\tFECPContactPoint& mp1 = static_cast<FECPContactPoint&>(*el1.GetMaterialPoint(n));\n\t\t\t\tmp1.m_gap = 0.0;\n\t\t\t\tvec3d r1 = mp1.m_rt;\n\t\t\t\tvec3d R1 = mp1.m_r0;\n\t\t\t\tvec3d n1 = mp1.dxr ^ mp1.dxs; n1.unit();\n\n\t\t\t\t// find the grid cell this point is in and loop over the cell's neighborhood\n\t\t\t\tGrid::Cell* c[27] = { nullptr };\n\t\t\t\tint nc = g.GetCellNeighborHood(r1, &c[0]);\n\t\t\t\tfor (int l = 0; l < nc; ++l)\n\t\t\t\t{\n\t\t\t\t\tGrid::Cell* cl = c[l];\n\t\t\t\t\tif (cl == nullptr) continue;\n\t\t\t\t\tfor (FESurfaceElement* el2 : cl->m_elemList)\n\t\t\t\t\t{\n\t\t\t\t\t\t// make sure we did not process this element yet\n\t\t\t\t\t\t// and the element is not a neighbor (which can be the case for self-contact)\n\t\t\t\t\t\tif (el2->isActive() && (excludeList.find(el2) == excludeList.end()))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Next, we see if any integration point of el2 is close to the current \n\t\t\t\t\t\t\t// integration point of el1. \n\t\t\t\t\t\t\tvec3d r12;\n\t\t\t\t\t\t\tfor (int m = 0; m < el2->GaussPoints(); ++m)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFEMaterialPoint* mp2 = el2->GetMaterialPoint(m);\n\t\t\t\t\t\t\t\tvec3d r2 = mp2->m_rt;\n\t\t\t\t\t\t\t\tvec3d R2 = mp2->m_r0;\n\n\t\t\t\t\t\t\t\tr12.x = r1.x - r2.x;\n\t\t\t\t\t\t\t\tr12.y = r1.y - r2.y;\n\t\t\t\t\t\t\t\tr12.z = r1.z - r2.z;\n\t\t\t\t\t\t\t\t//\t\t\t\t\t\t\tvec3d r12 = r1 - mp2.m_rt;\n\t\t\t\t\t\t\t\tif ((r12.x < m_Rout) && (r12.x > -m_Rout) &&\n\t\t\t\t\t\t\t\t\t(r12.y < m_Rout) && (r12.y > -m_Rout) &&\n\t\t\t\t\t\t\t\t\t(r12.z < m_Rout) && (r12.z > -m_Rout) &&\n\t\t\t\t\t\t\t\t\t(r12.norm2() < m_Rout * m_Rout))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdouble L12 = (R2 - R1).norm();\n\t\t\t\t\t\t\t\t\tdouble l12 = r12.unit();\n\t\t\t\t\t\t\t\t\tif ((fabs(r12 * n1) >= m_wtol) && (L12 >= m_Rmin))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t// we found one, so insert it to the list of active elements\n\t\t\t\t\t\t\t\t\t\tactiveElems.insert(el2);\n\n\t\t\t\t\t\t\t\t\t\t// also insert it to the exclude list\n\t\t\t\t\t\t\t\t\t\texcludeList.insert(el2);\n\n\t\t\t\t\t\t\t\t\t\t//\t\t\t\t\t\t\t\t\tfprintf(stderr, \"r12: %lg, %lg, %lg\\n\", r12.x, r12.y, r12.z);\n\t\t\t\t\t\t\t\t\t\t//\t\t\t\t\t\t\t\t\tfprintf(stderr, \"n1 : %lg, %lg, %lg\\n\", n1.x, n1.y, n1.z);\n\t\t\t\t\t\t\t\t\t\t//\t\t\t\t\t\t\t\t\tfprintf(stderr, \"l12 = %lg\\n\", l12);\n\n\t\t\t\t\t\t\t\t\t\tif ((mp1.m_gap == 0.0) || (l12 < mp1.m_gap))\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tmp1.m_gap = l12;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Build the matrix profile\nvoid FEContactPotential::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\t// connect every element of surface 1 to surface 2\n\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el1 = m_surf1.Element(i);\n\t\tif (el1.isActive())\n\t\t{\n\t\t\t// add the dofs of element 1\n\t\t\tvector<int> lm;\n\t\t\tfor (int j = 0; j < el1.Nodes(); ++j)\n\t\t\t{\n\t\t\t\tFENode& node = m_surf1.Node(el1.m_lnode[j]);\n\t\t\t\tlm.push_back(node.m_ID[dof_X]);\n\t\t\t\tlm.push_back(node.m_ID[dof_Y]);\n\t\t\t\tlm.push_back(node.m_ID[dof_Z]);\n\t\t\t\tlm.push_back(node.m_ID[dof_RU]);\n\t\t\t\tlm.push_back(node.m_ID[dof_RV]);\n\t\t\t\tlm.push_back(node.m_ID[dof_RW]);\n\t\t\t}\n\n\t\t\t// add all active dofs of surface 2\n\t\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\t\tfor (FESurfaceElement* el2 : activeElems)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j < el2->Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = m_surf2.Node(el2->m_lnode[j]);\n\t\t\t\t\tlm.push_back(node.m_ID[dof_X]);\n\t\t\t\t\tlm.push_back(node.m_ID[dof_Y]);\n\t\t\t\t\tlm.push_back(node.m_ID[dof_Z]);\n\t\t\t\t\tlm.push_back(node.m_ID[dof_RU]);\n\t\t\t\t\tlm.push_back(node.m_ID[dof_RV]);\n\t\t\t\t\tlm.push_back(node.m_ID[dof_RW]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tM.build_add(lm);\n\t\t}\n\t}\n}\n\ndouble FEContactPotential::PotentialDerive(double r)\n{\n\tdouble f = 0.0;\n\tif (r < m_Rin)\n\t{\n\t\tf = (m_kc / pow(r, m_p)) - m_c2;\n\t}\n\telse if (r < m_Rout)\n\t{\n\t\tf = m_c1 * (r - m_Rout) * (r - m_Rout);\n\t}\n\treturn f;\n}\n\ndouble FEContactPotential::PotentialDerive2(double r)\n{\n\tdouble f = 0.0;\n\tif (r < m_Rin)\n\t{\n\t\tf = -m_p*m_kc / pow(r, m_p + 1.0);\n\t}\n\telse if (r < m_Rout)\n\t{\n\t\tf = 2.0*m_c1 * (r - m_Rout);\n\t}\n\treturn f;\n}\n\n// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\nvoid FEContactPotential::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tconst int ndof = 3;\n\n\t// don't bother if kc is zero\n\tif (m_kc <= 0) return;\n\n\t// clear all contact tractions\n#pragma omp parallel\n\t{\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = m_surf1.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t\t{\n\t\t\t\t\tFECPContactPoint& cp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\t\tcp.m_Ln = 0.0;\n\t\t\t\t\tcp.m_tc = vec3d(0, 0, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_surf2.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = m_surf2.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t\t{\n\t\t\t\t\tFECPContactPoint& cp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\t\tcp.m_Ln = 0.0;\n\t\t\t\t\tcp.m_tc = vec3d(0, 0, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// loop over all elements of surf 1\n#pragma omp parallel for shared(R)\n\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& eli = m_surf1.Element(i);\n\t\tif (eli.isActive())\n\t\t{\n\t\t\tint na = eli.Nodes();\n\n\t\t\tvector<double> fe;\n\t\t\tvector<int> lm;\n\n\t\t\t// loop over all elements of surf 2\n\t\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\t\tfor (FESurfaceElement* elj : activeElems)\n\t\t\t{\n\t\t\t\tint nb = elj->Nodes();\n\n\t\t\t\t// evaluate contribution to force vector\n\t\t\t\tfe.assign((na + nb) * ndof, 0.0);\n\t\t\t\tElementForce(eli, *elj, fe);\n\n\t\t\t\t// setup the lm vector\n\t\t\t\tlm.resize(3 * (na + nb));\n\t\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = m_surf1.Node(eli.m_lnode[a]);\n\t\t\t\t\tlm[3 * a] = node.m_ID[0];\n\t\t\t\t\tlm[3 * a + 1] = node.m_ID[1];\n\t\t\t\t\tlm[3 * a + 2] = node.m_ID[2];\n\t\t\t\t}\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = m_surf2.Node(elj->m_lnode[b]);\n\t\t\t\t\tlm[3 * na + 3 * b] = node.m_ID[0];\n\t\t\t\t\tlm[3 * na + 3 * b + 1] = node.m_ID[1];\n\t\t\t\t\tlm[3 * na + 3 * b + 2] = node.m_ID[2];\n\t\t\t\t}\n\n\t\t\t\t// assemble\n\t\t\t\tR.Assemble(lm, fe);\n\t\t\t}\n\t\t}\n\t}\n\n\t// update contact pressures (only needed for plot output)\n#pragma omp parallel\n\t{\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = m_surf1.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t\t{\n\t\t\t\t\tFECPContactPoint& cp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\t\tcp.m_Ln = cp.m_tc.norm();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_surf2.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = m_surf2.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t\t{\n\t\t\t\t\tFECPContactPoint& cp = static_cast<FECPContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\t\tcp.m_Ln = cp.m_tc.norm();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEContactPotential::ElementForce(FESurfaceElement& el1, FESurfaceElement& el2, vector<double>& fe)\n{\n\tint na = el1.Nodes();\n\tint nb = el2.Nodes();\n\tdouble* w1 = el1.GaussWeights();\n\tdouble* w2 = el2.GaussWeights();\n\n\tfor (int n = 0; n < el1.GaussPoints(); ++n)\n\t{\n\t\tconst double* H1 = el1.H(n);\n\t\tFECPContactPoint& mp1 = static_cast<FECPContactPoint&>(*el1.GetMaterialPoint(n));\n\t\tdouble Jw1 = mp1.m_Jt*w1[n];\n\t\tvec3d r1 = mp1.m_rt;\n\n\t\tfor (int m = 0; m < el2.GaussPoints(); ++m)\n\t\t{\n\t\t\tconst double* H2 = el2.H(m);\n\t\t\tFECPContactPoint& mp2 = static_cast<FECPContactPoint&>(*el2.GetMaterialPoint(m));\n\t\t\tdouble Jw2 = mp2.m_Jt*w2[m];\n\n\t\t\tdouble Jw12 = Jw1 * Jw2;\n\n\t\t\t// get the position of the integration point\n\t\t\tvec3d r2 = mp2.m_rt;\n\n\t\t\t// get the normalized direction vector\n\t\t\tvec3d e12 = r1 - r2;\n\t\t\tdouble r12 = e12.unit();\n\n\t\t\t// calculate the potential derivative\n\t\t\tdouble df = PotentialDerive(r12);\n\n\t\t\t// add to the force\n\t\t\tif (df != 0.0)\n\t\t\t{\n\t\t\t\tvec3d F = e12 * df;\n\t\t\t\tmp1.m_tc += F * Jw2;\n\t\t\t\tmp2.m_tc -= F * Jw1;\n\n\t\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * a    ] += F.x * (H1[a] * Jw12);\n\t\t\t\t\tfe[3 * a + 1] += F.y * (H1[a] * Jw12);\n\t\t\t\t\tfe[3 * a + 2] += F.z * (H1[a] * Jw12);\n\t\t\t\t}\n\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * (na + b)    ] -= F.x * (H2[b] * Jw12);\n\t\t\t\t\tfe[3 * (na + b) + 1] -= F.y * (H2[b] * Jw12);\n\t\t\t\t\tfe[3 * (na + b) + 2] -= F.z * (H2[b] * Jw12);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Evaluates the contriubtion to the stiffness matrix\nvoid FEContactPotential::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tconst int ndof = 3;\n\n\t// don't bother if kc is zero\n\tif (m_kc <= 0) return;\n\n\t// loop over all elements of surf 1\n#pragma omp parallel for shared(LS)\n\tfor (int i = 0; i < m_surf1.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& eli = m_surf1.Element(i);\n\t\tif (eli.isActive())\n\t\t{\n\t\t\tint na = eli.Nodes();\n\n\t\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\t\tfor (FESurfaceElement* elj : activeElems)\n\t\t\t{\n\t\t\t\tint nb = elj->Nodes();\n\n\t\t\t\tFEElementMatrix ke((na + nb) * ndof, (na + nb) * ndof);\n\t\t\t\tke.zero();\n\n\t\t\t\tElementStiffness(eli, *elj, ke);\n\n\t\t\t\tvector<int> lm(3 * (na + nb));\n\t\t\t\tvector<int> en(na + nb);\n\t\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = m_surf1.Node(eli.m_lnode[a]);\n\t\t\t\t\tlm[3 * a] = node.m_ID[0];\n\t\t\t\t\tlm[3 * a + 1] = node.m_ID[1];\n\t\t\t\t\tlm[3 * a + 2] = node.m_ID[2];\n\t\t\t\t\ten[a] = eli.m_node[a];\n\t\t\t\t}\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = m_surf2.Node(elj->m_lnode[b]);\n\t\t\t\t\tlm[3 * na + 3 * b] = node.m_ID[0];\n\t\t\t\t\tlm[3 * na + 3 * b + 1] = node.m_ID[1];\n\t\t\t\t\tlm[3 * na + 3 * b + 2] = node.m_ID[2];\n\t\t\t\t\ten[na + b] = elj->m_node[b];\n\t\t\t\t}\n\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tke.SetNodes(en);\n\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEContactPotential::ElementStiffness(FESurfaceElement& el1, FESurfaceElement& el2, matrix& ke)\n{\n\tdouble* w1 = el1.GaussWeights();\n\tdouble* w2 = el2.GaussWeights();\n\n\tint na = el1.Nodes();\n\tint nb = el2.Nodes();\n\n\tfor (int n = 0; n < el1.GaussPoints(); ++n)\n\t{\n\t\tconst double* H1 = el1.H(n);\n\t\tFECPContactPoint& mp1 = static_cast<FECPContactPoint&>(*el1.GetMaterialPoint(n));\n\t\tdouble Jw1 = mp1.m_Jt*w1[n];\n\n\t\tvec3d r1 = mp1.m_rt;\n\n\t\tfor (int m = 0; m < el2.GaussPoints(); ++m)\n\t\t{\n\t\t\tconst double* H2 = el2.H(m);\n\t\t\tFECPContactPoint& mp2 = static_cast<FECPContactPoint&>(*el2.GetMaterialPoint(m));\n\t\t\tdouble Jw2 = mp2.m_Jt*w2[m];\n\n\t\t\tvec3d r2 = mp2.m_rt;\n\n\t\t\t// get the normalized direction vector\n\t\t\tvec3d e12 = r1 - r2;\n\t\t\tdouble r12 = e12.unit();\n\n\t\t\t// calculate the potential's derivatives\n\t\t\tdouble df  = PotentialDerive(r12);\n\t\t\tdouble d2f = PotentialDerive2(r12);\n\n\t\t\t// add to the stiffness\n\t\t\tmat3dd I(1.0);\n\t\t\tmat3d ExE = dyad(e12);\n\t\t\tmat3d P = (I - ExE) / r12;\n\t\t\tmat3d M = ExE * d2f + P * df;\n\n\t\t\t// 1-1 contribution\n\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b < na; ++b)\n\t\t\t\t{\n\t\t\t\t\tmat3d K2 = M * Jw2;\n\t\t\t\t\tke.add(3 * a, 3 * b, K2 * (H1[a] * H1[b])*Jw1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 1-2 contribution\n\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tmat3d K2b = M * (H2[b] * Jw2);\n\t\t\t\t\tke.sub(3 * a, 3 * na + 3 * b, K2b * (H1[a] * Jw1));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2-1 contribution\n\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t{\n\t\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t\t{\n\t\t\t\t\tmat3d K1a = M * (H1[a] * Jw1);\n\t\t\t\t\tke.sub(3 * na + 3*b, 3 * a, K1a * (H2[b] * Jw2));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2-2 contribution\n\t\t\tfor (int a = 0; a < nb; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tmat3d K1 = M * Jw1;\n\t\t\t\t\tke.add(3*na + 3 * a, 3*na + 3 * b, K1 * (H2[a] * H2[b]) * Jw2);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tke *= -1.0;\n}\n\nvoid FEContactPotential::Serialize(DumpStream& ar)\n{\n\tFEContactInterface::Serialize(ar);\n\n\tm_surf1.Serialize(ar);\n\tm_surf2.Serialize(ar);\n\n\tif (!ar.IsShallow() && ar.IsLoading())\n\t{\n\t\tBuildNeighborTable();\n\t\tm_activeElements.resize(m_surf1.Elements());\n\t\tm_NNL.Create(m_surf1);\n\t}\n}\n\nbool FEContactPotential::CheckIntersections(FEContactPotential::Grid& g)\n{\n\tint intersectionCount = 0;\n#pragma omp parallel for shared(g, intersectionCount) schedule(dynamic)\n\tfor (int i = 0; i < m_surf1.Nodes(); ++i)\n\t{\n\t\tint n0 = m_surf1.NodeIndex(i);\n\t\tint nval = m_NNL.Valence(i);\n\t\tint* nnl = m_NNL.NodeList(i);\n\t\tvec3d r0 = m_surf1.Node(i).m_rt;\n\n\t\tGrid::Cell* c[27] = { nullptr };\n\t\tint nc = g.GetCellNeighborHood(r0, &c[0]);\n\n\t\tfor (int j = 0; j < nval; ++j)\n\t\t{\n\t\t\tif (nnl[j] > i)\n\t\t\t{\n\t\t\t\tint n1 = m_surf1.NodeIndex(nnl[j]);\n\t\t\t\tvec3d r1 = m_surf1.Node(nnl[j]).m_rt;\n\t\t\t\tvec3d e = r1 - r0; \n\t\t\t\tdouble l = e.unit();\n\n\t\t\t\tfor (int k = 0; k < nc; ++k)\n\t\t\t\t{\n\t\t\t\t\tGrid::Cell* cl = c[k]; assert(cl);\n\t\t\t\t\tif (cl == nullptr) continue;\n\t\t\t\t\tfor (FESurfaceElement* el2 : cl->m_elemList)\n\t\t\t\t\t{\n\t\t\t\t\t\tFESurfaceElement& el = *el2;\n\t\t\t\t\t\tif (!el.HasNode(n0) && !el.HasNode(n1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble rs[2] = { 0 }, g(0);\n\t\t\t\t\t\t\tif (m_surf2.Intersect(el, r0, e, rs, g, 1e-7, false))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif ((g >= 0) && (g <= l))\n\t\t\t\t\t\t\t\t{\n/*\t\t\t\t\t\t\t\t\tint nid0 = m_surf1.Node(i).GetID();\n\t\t\t\t\t\t\t\t\tint nid1 = m_surf1.Node(nnl[j]).GetID();\n\t\t\t\t\t\t\t\t\tfeLog(\"\\nNode : %d, %d\\n\", nid0, nid1);\n\t\t\t\t\t\t\t\t\tint eid = el.m_elem[0].pe->GetID();\n\t\t\t\t\t\t\t\t\tfeLog(\"\\nElement : %d\\n\", eid);\n*/\n\t\t\t\t\t\t\t\t\t# pragma omp atomic\n\t\t\t\t\t\t\t\t\tintersectionCount++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn (intersectionCount != 0);\n}\n"
  },
  {
    "path": "FEBioMech/FEContactPotential.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n#include <FECore/FENodeNodeList.h>\n#include <set>\n\nclass FEContactPotentialSurface : public FEContactSurface\n{\npublic:\n\tclass Data : public FEContactMaterialPoint\n\t{\n\tpublic:\n\t\tvec3d\tm_tc;\n\n\t\tvoid Serialize(DumpStream& ar) override\n\t\t{\n\t\t\tFEContactMaterialPoint::Serialize(ar);\n\t\t\tar & m_tc;\n\t\t}\n\t};\n\npublic:\n\tFEContactPotentialSurface(FEModel* fem);\n\n\tvoid InitSurface() override;\n\n\tvoid GetContactTraction(int nelem, vec3d& tc) override;\n\n\tdouble GetContactArea() override;\n\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\tvec3d GetContactForce() override;\n};\n\ntypedef FEContactPotentialSurface::Data FECPContactPoint;\n\nclass FEContactPotential : public FEContactInterface\n{\n\tclass Grid;\n\npublic:\n\tFEContactPotential(FEModel* fem);\n\t~FEContactPotential();\n\n\t// -- From FESurfacePairConstraint\npublic:\n\t//! return the primary surface\n\tFESurface* GetPrimarySurface() override;\n\n\t//! return the secondary surface\n\tFESurface* GetSecondarySurface() override;\n\n\t//! temporary construct to determine if contact interface uses nodal integration rule (or facet)\n\tbool UseNodalIntegration() override;\n\n\t// Build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t// update\n\tvoid Update() override;\n\n\t// init\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\tint IntegrationRule() const { return m_integrationRule; }\n\n\t// -- from FEContactInterface\npublic:\n\t// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t// Evaluates the contriubtion to the stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\nprotected:\n\tvoid ElementForce(FESurfaceElement& el1, FESurfaceElement& el2, std::vector<double>& fe);\n\tvoid ElementStiffness(FESurfaceElement& el1, FESurfaceElement& el2, matrix& ke);\n\n\tdouble PotentialDerive(double r);\n\tdouble PotentialDerive2(double r);\n\n\tvoid BuildNeighborTable();\n\n\tvoid UpdateSurface(FESurface& surface);\n\n\t//! checks for edge-face intersections\n\tbool CheckIntersections(Grid& grid);\n\nprotected:\n\tFEContactPotentialSurface\tm_surf1;\n\tFEContactPotentialSurface\tm_surf2;\n\nprotected:\n\tdouble\tm_kc;\n\tdouble\tm_p;\n\tdouble\tm_Rin;\n\tdouble\tm_Rout;\n\tdouble\tm_Rmin;\n\tdouble\tm_wtol;\n\tbool\tm_checkIntersections;\t//!< check for edge/face intersections\n\n\tint\t\tm_integrationRule = 0;\n\tbool\tm_excludeNeighbors = true;\n\n\tdouble\tm_c1, m_c2;\n\n\tstd::vector< std::set<FESurfaceElement*> >\tm_activeElements;\n\tstd::vector< std::set<FESurfaceElement*> >\tm_elemNeighbors;\n\tFENodeNodeList m_NNL;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEContactSurface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEContactSurface.h\"\n#include \"FECore/FEModel.h\"\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include <assert.h>\n\n//-----------------------------------------------------------------------------\nFEContactSurface::FEContactSurface(FEModel* pfem) : FESurface(pfem), m_pfem(pfem)\n{\n\tm_pContactInterface = nullptr;\n\tm_pSibling = nullptr; \n\tm_dofX = -1;\n\tm_dofY = -1;\n\tm_dofZ = -1;\n}\n\n//-----------------------------------------------------------------------------\nFEContactSurface::~FEContactSurface()\n{\n\tm_pSibling = nullptr; \n\tm_pContactInterface = nullptr; \n}\n\n//-----------------------------------------------------------------------------\nbool FEContactSurface::Init()\n{\n\t// I want to use the FEModel class for this, but don't know how\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tm_dofX = dofs.GetDOF(\"x\");\n\tm_dofY = dofs.GetDOF(\"y\");\n\tm_dofZ = dofs.GetDOF(\"z\");\n\n\tSetInterfaceStatus(true);\n\treturn FESurface::Init();\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FEContactSurface::Serialize(DumpStream& ar)\n{\n\tFESurface::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_dofX & m_dofY & m_dofZ;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::SetSibling(FEContactSurface* ps) { m_pSibling = ps; }\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::SetContactInterface(FEContactInterface* ps) { m_pContactInterface = ps; }\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::GetVectorGap(int nface, vec3d& pg) {}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::GetContactTraction(int nface, vec3d& pt) {}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::GetNodalVectorGap(int nface, vec3d* pg) {}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::GetNodalContactPressure(int nface, double* pg) {}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::GetNodalContactTraction(int nface, vec3d* pt) {}\n\n//-----------------------------------------------------------------------------\nvec3d FEContactSurface::GetContactForce() { return vec3d(0,0,0); }\n\n//-----------------------------------------------------------------------------\ndouble FEContactSurface::GetContactArea() { return 0; }\n\n//-----------------------------------------------------------------------------\n// Evaluate surface traction from the stress tensor of the attached solid element\nvoid FEContactSurface::GetSurfaceTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    FEElement* e = el.m_elem[0].pe;\n    FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n    if (se) {\n        mat3ds si[FEElement::MAX_INTPOINTS];\n        mat3ds so[FEElement::MAX_NODES];\n        for (int i=0; i<se->GaussPoints(); ++i) {\n            FEMaterialPoint* pt = se->GetMaterialPoint(i);\n            FEElasticMaterialPoint* ep = pt->ExtractData<FEElasticMaterialPoint>();\n            if (ep)\n                si[i] = ep->m_s;\n            else\n                si[i].zero();\n        }\n        // project stresses from integration points to nodes\n        se->project_to_nodes(si, so);\n        // only keep the stresses at the nodes of the contact face\n        mat3ds sn[FEElement::MAX_NODES];\n        for (int i=0; i<el.Nodes(); ++i)\n            sn[i] = so[se->FindNode(el.m_node[i])];\n        // evaluate tractions at integration points of that face\n        vec3d t[FEElement::MAX_INTPOINTS];\n        for (int i=0; i<el.GaussPoints(); ++i) {\n            double *H = el.H(i);\n            t[i] = vec3d(0,0,0);\n            for (int j=0; j<el.Nodes(); ++j) {\n                vec3d n = SurfaceNormal(el, j);\n                t[i] += sn[j]*n*H[j];\n            }\n        }\n        // now save the average traction on that face\n        int ni = el.GaussPoints();\n        pt = vec3d(0,0,0);\n        for (int k=0; k<ni; ++k) pt += t[k];\n        pt /= ni;\n    }\n    else\n        pt = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate surface traction from the stress tensor of the attached solid element\nvoid FEContactSurface::GetNodalSurfaceTraction(int nface, vec3d* pt)\n{\n    FESurfaceElement& el = Element(nface);\n\tFEElement* e = el.m_elem[0].pe;\n\tFESolidElement* se = dynamic_cast<FESolidElement*>(e);\n    if (se) {\n        mat3ds si[FEElement::MAX_INTPOINTS];\n        mat3ds so[FEElement::MAX_NODES];\n        for (int i=0; i<se->GaussPoints(); ++i) {\n            FEMaterialPoint* pt = se->GetMaterialPoint(i);\n            FEElasticMaterialPoint* ep = pt->ExtractData<FEElasticMaterialPoint>();\n            if (ep)\n                si[i] = ep->m_s;\n            else\n                si[i].zero();\n        }\n        // project stresses from integration points to nodes\n        se->project_to_nodes(si, so);\n        // only keep the stresses at the nodes of the contact face\n        mat3ds sn[FEElement::MAX_NODES];\n        for (int i=0; i<el.Nodes(); ++i)\n            sn[i] = so[se->FindNode(el.m_node[i])];\n        // evaluate tractions at integration points of that face\n        vec3d t[FEElement::MAX_INTPOINTS];\n        for (int i=0; i<el.GaussPoints(); ++i) {\n            double *H = el.H(i);\n            t[i] = vec3d(0,0,0);\n            for (int j=0; j<el.Nodes(); ++j) {\n                vec3d n = SurfaceNormal(el, j);\n                t[i] += sn[j]*n*H[j];\n            }\n        }\n        // now project the traction from integration points back to nodes\n        el.project_to_nodes(t, pt);\n    }\n    else\n        for (int i=0; i<el.Nodes(); ++i) pt[i] = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate surface traction from the stress tensor of the attached solid element\nvoid FEContactSurface::GetGPSurfaceTraction(int nface, vec3d* pt)\n{\n    FESurfaceElement& el = Element(nface);\n\tFEElement* e = el.m_elem[0].pe;\n\tFESolidElement* se = dynamic_cast<FESolidElement*>(e);\n    if (se) {\n        mat3ds si[FEElement::MAX_INTPOINTS];\n        mat3ds so[FEElement::MAX_NODES];\n        for (int i=0; i<se->GaussPoints(); ++i) {\n            FEMaterialPoint* pt = se->GetMaterialPoint(i);\n            FEElasticMaterialPoint* ep = pt->ExtractData<FEElasticMaterialPoint>();\n            if (ep)\n                si[i] = ep->m_s;\n            else\n                si[i].zero();\n        }\n        // project stresses from integration points to nodes\n        se->project_to_nodes(si, so);\n        // only keep the stresses at the nodes of the contact face\n        mat3ds sn[FEElement::MAX_NODES];\n        for (int i=0; i<el.Nodes(); ++i)\n            sn[i] = so[se->FindNode(el.m_node[i])];\n        // evaluate tractions at integration points of that face\n        for (int i=0; i<el.GaussPoints(); ++i) {\n            double *H = el.H(i);\n            pt[i] = vec3d(0,0,0);\n            for (int j=0; j<el.Nodes(); ++j) {\n                vec3d n = SurfaceNormal(el, j);\n                pt[i] += sn[j]*n*H[j];\n            }\n        }\n    }\n    else\n        for (int i=0; i<el.Nodes(); ++i) pt[i] = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::GetStickStatus(int nface, double& pt) {}\n\n//-----------------------------------------------------------------------------\nvoid FEContactSurface::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N*3);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\tlm[3*i  ] = id[m_dofX];\n\t\tlm[3*i+1] = id[m_dofY];\n\t\tlm[3*i+2] = id[m_dofZ];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEContactSurface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <FECore/FESurface.h>\n#include <FECore/vec2d.h>\n#include \"FEContactInterface.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Stores material point data for contact interfaces\nclass FEBIOMECH_API FEContactMaterialPoint : public FESurfaceMaterialPoint\n{\npublic:\n\tFEContactMaterialPoint()\n\t{\n\t\tm_gap = 0.0;\n\t\tm_Ln  = 0.0;\n\t\tm_pme = nullptr;\n\t\tm_pmep = nullptr;\n\t}\n\n\tvoid Init() override\n\t{\n\t\tFESurfaceMaterialPoint::Init();\n\t\tm_gap = 0.0;\n\t\tm_Ln = 0.0;\n\t\tm_pme = nullptr;\n\t\tm_pmep = nullptr;\n\t}\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tFESurfaceMaterialPoint::Serialize(ar);\n\t\tar & m_gap & m_Ln;\n\t}\n\npublic:\n\tdouble\tm_gap;\t//!< gap function at integration points\n\tdouble\tm_Ln;\t//!< net contact pressure\n\n\tFESurfaceElement*\tm_pme;\t//!< target element\n\tFESurfaceElement*\tm_pmep;\t//!< previous target element\n};\n\n//-----------------------------------------------------------------------------\n//! This class describes a contact surface\n\n//!\tthis class is used in contact analyses to describe a contacting\n//! surface in a contact interface.\n\nclass FEBIOMECH_API FEContactSurface : public FESurface\n{\npublic:\n\t//! constructor\n\tFEContactSurface(FEModel* pfem);\n\n\t//! destructor\n\t~FEContactSurface();\n\n\t// initialization\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Set the sibling of this contact surface\n\tvoid SetSibling(FEContactSurface* ps);\n\n    //! Set the parent of this contact surface\n    void SetContactInterface(FEContactInterface* ps);\n    \n    //! Get the parent of this contact surface\n    FEContactInterface* GetContactInterface() { return m_pContactInterface; }\n    \n\t//! Unpack surface element data\n\tvirtual void UnpackLM(FEElement& el, vector<int>& lm);\n\npublic:\n    virtual void GetVectorGap      (int nface, vec3d& pg);\n    virtual void GetContactTraction(int nface, vec3d& pt);\n    \n    virtual void GetNodalVectorGap      (int nface, vec3d* pg);\n\tvirtual void GetNodalContactPressure(int nface, double* pg);\n\tvirtual void GetNodalContactTraction(int nface, vec3d* pt);\n\n    virtual void GetStickStatus(int nface, double& pt);\n    \n    void GetSurfaceTraction(int nface, vec3d& pt);\n    void GetNodalSurfaceTraction(int nface, vec3d* pt);\n    void GetGPSurfaceTraction(int nface, vec3d* pt);\n\n\tvirtual vec3d GetContactForce();\n    virtual double GetContactArea();\n\n\tFEModel* GetFEModel() { return m_pfem; }\n\nprotected:\n\tFEContactSurface* m_pSibling;\n    FEContactInterface* m_pContactInterface;\n\tFEModel*\tm_pfem;\n\n\tint\tm_dofX;\n\tint\tm_dofY;\n\tint\tm_dofZ;\n};\n"
  },
  {
    "path": "FEBioMech/FEContinuousElasticDamage.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEContinuousElasticDamage.h\"\n#include <FECore/log.h>\n#include <FECore/expint_Ei.h>\n\n// Macauley Bracket\n#define MB(a) ((a)>0.0?(a):0.0)\n#define SIGN(a) ((a)>0.0?1.0:0.0)\n\n//=========================================================================================\n\nclass FEFiberDamagePoint : public FEMaterialPointData\n{\npublic:\n\tFEFiberDamagePoint(FEMaterialPointData* pm) : FEMaterialPointData(pm)\n\t{\n\t\tm_D = 0.0;\n\t\tm_D1 = m_D2 = m_D3 = 0.0;\n\t\tm_P = 0.0;\n\t\tm_Ds = 0.0;\n\t\tm_psi_f0_ini = 0.0;\n\t\tm_psi_f0 = 0.0;\n\t\tm_psi_f0_prev = 0.0;\n\t\tm_bt_ini = 0.0;\n\t\tm_bt = 0.0;\n\t\tm_bt_prev = 0.0;\n\t\tm_gamma = 0.0;\n\t\tm_gamma_prev = 0.0;\n\t\tm_psf_c = 0.0;\n\t\tm_init = false;\n\t}\n\n\tvoid Init() override\n\t{\n\t\tm_D = 0.0;\n\t\tm_D1 = m_D2 = m_D2f = m_D3 = 0.0;\n\t\tm_P = 0.0;\n\t\tm_Ds = 0.0;\n\t\tm_psi_f0_ini = m_psf_c;// we set this to the initial value\n\t\tm_psi_f0 = m_psf_c;\t\t// we set this to the initial value\n\t\tm_psi_f0_prev = m_psf_c; // we set this to the initial value\n\t\tm_bt_ini = 0.0;\n\t\tm_bt = 0.0;\n\t\tm_bt_prev = 0.0;\n\t\tm_beta = 0.0;\n\t\tm_gamma = 0.0;\n\t\tm_gamma_prev = 0.0;\n\t\tm_init = false;\n\n\t\tFEMaterialPointData::Init();\n\t}\n\n\tvoid Update(const FETimeInfo& timeInfo) override\n\t{\n\t\tm_gamma_prev = m_gamma;\n\t\tm_bt_prev = m_bt;\n\t\tm_psi_f0_prev = m_psi_f0;\n\t\tFEMaterialPointData::Update(timeInfo);\n\t}\n\npublic:\n\tbool\tm_init;\t// initialization flag\n\tdouble\tm_D;\t\t// accumulated damage\n\tdouble\tm_D1, m_D2, m_D2f, m_D3;\t// damage components\n\tdouble\tm_P, m_Ds;\n\n\tdouble\tm_psi_f0_ini, m_psf_c;\n\tdouble\tm_psi_f0, m_psi_f0_prev;\n\n\tdouble\tm_bt_ini;\n\tdouble\tm_bt, m_bt_prev, m_beta;\n\n\tdouble\tm_gamma, m_gamma_prev;\n};\n\n//=========================================================================================\n\nBEGIN_FECORE_CLASS(FEDamageElasticFiber, FEElasticFiberMaterial)\n\tADD_PARAMETER(m_tinit, FE_RANGE_GREATER_OR_EQUAL(0.0), \"t0\");\n\tADD_PARAMETER(m_Dmax, FE_RANGE_CLOSED(0.0, 1.0), \"Dmax\");\n\tADD_PARAMETER(m_beta_s, FE_RANGE_GREATER(0.0), \"beta_s\");\n\tADD_PARAMETER(m_gamma_max, FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma_max\");\n\n\tADD_PARAMETER(m_D2_a, \"D2_a\");\n\tADD_PARAMETER(m_D2_b, \"D2_b\");\n\tADD_PARAMETER(m_D2_c, \"D2_c\");\n\tADD_PARAMETER(m_D2_d, \"D2_d\");\n\tADD_PARAMETER(m_D3_inf, \"D3_inf\");\n\tADD_PARAMETER(m_D3_g0, \"D3_g0\");\n\tADD_PARAMETER(m_D3_rg, \"D3_rg\");\nEND_FECORE_CLASS();\n\nFEDamageElasticFiber::FEDamageElasticFiber(FEModel* fem) : FEElasticFiberMaterial(fem)\n{\n\tm_tinit = 1e9;\t// large value so, no damage accumulation by default\n\tm_Dmax = 1.0;\n\n\tm_beta_s = 0.0;\n\tm_gamma_max = 0.0;\n\n\t// Looks like these are hard-coded\n\tm_r_s = 0.99;\n\tm_r_inf = 0.99;\n\n\t// initial values for D2 term\n\tm_D2_a = 0.0;\n\tm_D2_b = 0.0;\n\tm_D2_c = 0.0;\n\tm_D2_d = 0.0;\n\tm_D3_inf = 0.0;\n\tm_D3_g0 = 0.0;\n\tm_D3_rg = 1.0;\n}\n\ndouble FEDamageElasticFiber::Damage(FEMaterialPoint& mp)\n{\n\tFEFiberDamagePoint& damagePoint = *mp.ExtractData<FEFiberDamagePoint>();\n\treturn damagePoint.m_D;\n}\n\ndouble FEDamageElasticFiber::Damage(FEMaterialPoint& mp, int n)\n{\n\tFEFiberDamagePoint& damagePoint = *mp.ExtractData<FEFiberDamagePoint>();\n\tswitch (n)\n\t{\n\tcase 0: return damagePoint.m_D; break;\n\tcase 1: return damagePoint.m_D1; break;\n\tcase 2: return damagePoint.m_Ds; break;\n\tcase 3: return damagePoint.m_D2; break;\n\tcase 4: return damagePoint.m_D3; break;\n\tcase 5: return damagePoint.m_P; break;\n\tcase 6: return damagePoint.m_psi_f0; break;\n\tcase 7: return damagePoint.m_beta; break;\n\tcase 8: return damagePoint.m_gamma; break;\n\tcase 9: return damagePoint.m_D2f; break;\n\t}\n\treturn 0.0;\n}\n\n//! Strain energy density\ndouble FEDamageElasticFiber::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEFiberDamagePoint& damagePoint = *mp.ExtractData<FEFiberDamagePoint>();\n\n\tdouble D = damagePoint.m_D;\n\tdouble psi0 = Psi0(mp, a0);\n\tdouble P = (1.0 - D)*psi0 - damagePoint.m_psf_c;\n\tif (P < 0) P = 0;\n\tdouble psi = m(P);\n\n\treturn psi;\n}\n\n// calculate stress in fiber direction a0\nmat3ds FEDamageElasticFiber::FiberStress(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEFiberDamagePoint& damagePoint = *mp.ExtractData<FEFiberDamagePoint>();\n\n\tmat3d F = pt.m_F;\n\n\t// get internal variables\n\tdouble D = damagePoint.m_D;\n\tdouble bt_prev = damagePoint.m_bt_prev;\n\tdouble psi_f0_prev = damagePoint.m_psi_f0_prev;\n\tdouble gamma_prev = damagePoint.m_gamma_prev;\n\n\tdamagePoint.m_D1 = 0.0;\n\tdamagePoint.m_D2 = 0.0;\n\tdamagePoint.m_D2f = 0.0;\n\tdamagePoint.m_D3 = 0.0;\n\tdamagePoint.m_P = 0.0;\n\tdamagePoint.m_Ds = 0.0;\n\n\t// get current simulation time.\n\tdouble t = CurrentTime();\n\n\t// (i) compute trans-iso strain energy\n\tdouble psi_f0 = Psi0(mp, a0);\n\n\t// (ii) check initial damage state\n\tdouble eps = 1e-9; // NOTE: should be machine eps\n\tif (t >= (m_tinit - eps))\n\t{\n\t\t// (b) compute bt\n\t\tdouble bt = bt_prev + MB(psi_f0 - psi_f0_prev);\n\n\t\t// init damage \n\t\tif (damagePoint.m_init == false)\n\t\t{\n\t\t\tdamagePoint.m_psi_f0_ini = psi_f0;\n\t\t\tdamagePoint.m_bt_ini = bt;\n\t\t\tdamagePoint.m_init = true;\n\t\t}\n\n\t\t// (iii) calculate max damage saturation value \n\t\t// trial criterion\n\t\tdouble phi_trial = MB(psi_f0 - damagePoint.m_psi_f0_ini) - gamma_prev;\n\n\t\t// check algorithmic saturation criterion\n\t\tdouble gamma = 0;\n\t\tif (phi_trial > eps) gamma = MB(psi_f0 - damagePoint.m_psi_f0_ini);\n\t\telse gamma = gamma_prev;\n\t\tassert(gamma >= gamma_prev);\n\n\t\t// compute damage saturation value\n\t\tdouble Ds = (m_gamma_max == 0.0 ? m_Dmax : m_Dmax * (1.0 - exp(log(1.0 - m_r_inf)*gamma / m_gamma_max)));\n\n\t\t// (iv) compute internal variable\n\t\tdouble beta = MB(bt - damagePoint.m_bt_ini);\n\n\t\t// (v) evaluate damage function\n\t\tdouble D1 = Ds * (1.0 - exp(log(1.0 - m_r_s)*beta / m_beta_s));\n\n\t\t// D2 term\n\t\tdouble D3 = (m_D3_rg != 0 ? m_D3_inf / (1.0 + exp(-(gamma - m_D3_g0) / m_D3_rg)) : m_D3_inf);\n\t\tdouble D2f = (m_D2_a*(exp(m_D2_b*beta) - 1) + m_D2_c*(exp(m_D2_d*beta) - 1));\n\t\tdouble D2 = D3*D2f;\n\n\t\t// Add the damage\n\t\tD = D1 + D2;\n\n\t\t// update internal variables\n\t\tdamagePoint.m_D = D;\n\t\tdamagePoint.m_D1 = D1;\n\t\tdamagePoint.m_Ds = Ds;\n\t\tdamagePoint.m_D2 = D2;\n\t\tdamagePoint.m_D2f = D2f;\n\t\tdamagePoint.m_D3 = D3;\n\n\t\tdamagePoint.m_bt = bt;\n\t\tdamagePoint.m_psi_f0 = psi_f0;\n\t\tdamagePoint.m_beta = beta;\n\t\tdamagePoint.m_gamma = gamma;\n\t}\n\n\tdouble P = (1.0 - D)*(psi_f0) - damagePoint.m_psf_c;\n\tdamagePoint.m_P = P;\n\tif (P < 0.0) P = 0.0;\n\tdouble dm = dm_dP(P);\n\n\tmat3ds S0 = dPsi0_dC(mp, a0)*2.0;\n\tmat3ds s0 = pt.push_forward(S0);\n\tmat3ds s = s0*(dm*(1.0 - D));\n\n\treturn s;\n}\n\n// Spatial tangent\ntens4ds FEDamageElasticFiber::FiberTangent(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEFiberDamagePoint& damagePoint = *mp.ExtractData<FEFiberDamagePoint>();\n\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tdouble D = damagePoint.m_D;\n\n\tdouble psi0 = Psi0(mp, a0);\n\tdouble P = (1.0 - D)*(psi0)-damagePoint.m_psf_c;\n\tif (P < 0) P = 0.0;\n\n\tdouble dm = dm_dP(P);\n\tdouble d2m = d2m_dP(P);\n\n\tmat3ds S0 = dPsi0_dC(mp, a0)*2.0;\n\tmat3ds s0 = pt.push_forward(S0);\n\ttens4ds sxs = dyad1s(s0);\n\n\t// elastic stiffness \n\ttens4ds C0 = d2Psi0_dC(mp, a0)*2.0;\n\ttens4ds c0 = pt.push_forward(C0);\n\ttens4ds ceD = sxs * (J*d2m*(1.0 - D)) + c0 * ((1.0 - D)*dm);\n\n\ttens4ds c = ceD;\n\n\t// damage stiffness\n\tdouble bt = damagePoint.m_bt;\n\tdouble gamma = damagePoint.m_gamma;\n\tdouble Ds = (m_gamma_max == 0.0 ? m_Dmax : m_Dmax * (1.0 - exp(log(1.0 - m_r_inf)*gamma / m_gamma_max)));\n\tdouble beta = MB(bt - damagePoint.m_bt_ini);\n\n\tdouble psi0_prev = damagePoint.m_psi_f0_prev;\n\tdouble psi0_ini = damagePoint.m_psi_f0_ini;\n\tdouble gamma_prev = damagePoint.m_gamma_prev;\n\n\tdouble dD1_dbeta = -Ds * (log(1 - m_r_s) / m_beta_s)*exp(log(1 - m_r_s)*beta / m_beta_s);\n\n\tdouble D3 = (m_D3_rg != 0 ? m_D3_inf / (1.0 + exp(-(gamma - m_D3_g0) / m_D3_rg)) : m_D3_inf);\n\tdouble dD2_dD3 = m_D2_a * (exp(m_D2_b * beta) - 1) + m_D2_c * (exp(m_D2_d * beta) - 1);\n\tdouble dD2_dbeta = D3 * (m_D2_a*m_D2_b*exp(m_D2_b*beta) + m_D2_c * m_D2_d * exp(m_D2_d * beta));\n\tdouble dD3_dgamma = 0.0;\n\tif (m_D3_rg != 0)\n\t{\n\t\tdouble D3_exp = exp(-(gamma - m_D3_g0) / m_D3_rg);\n\t\tdD3_dgamma = m_D3_inf * (D3_exp / m_D3_rg) / ((1.0 + D3_exp) * (1.0 + D3_exp));\n\t}\n\t\n\tdouble dD_dbeta = dD1_dbeta + dD2_dbeta;\n\n\tdouble dDs_dgamma = (m_gamma_max ==0.0 ? 0.0 : -m_Dmax * (log(1 - m_r_inf) / m_gamma_max)*exp(log(1 - m_r_inf)*gamma / m_gamma_max));\n\n\tdouble dD_dDs = 1.0 - exp(log(1 - m_r_s)*beta / m_beta_s);\n\tdouble dbeta_dpsi0 = 0.25*(SIGN(bt - damagePoint.m_bt_ini) + 1)*(SIGN(psi0 - psi0_prev) + 1);\n\tdouble dgamma_dpsi0 = 0.5*(SIGN(psi0 - psi0_ini) + 1);\n\n\tdouble meps = 1e-9; // NOTE: should be machine eps\n\tif (psi0 - psi0_prev > meps)\n\t{\n\t\ttens4ds Cd = sxs * ((dm + d2m * (1 - D)*psi0)*dD_dbeta*dbeta_dpsi0);\n\t\tc -= Cd;\n\t}\n\n\tdouble phi_trial = MB(psi0 - damagePoint.m_psi_f0_ini) - gamma_prev;\n\tif (phi_trial > meps)\n\t{\n\t\ttens4ds Cd = sxs * ((dm + d2m * (1 - D)*psi0)*(dD_dDs*dDs_dgamma + dD2_dD3 * dD3_dgamma)*dgamma_dpsi0);\n\t\tc -= Cd;\n\t}\n\n\treturn c;\n}\n\ndouble FEDamageElasticFiber::Psi0(FEMaterialPoint& mp, const vec3d& a0) { return 0.0; }\nmat3ds FEDamageElasticFiber::dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0) { return mat3ds(0.0); }\ntens4ds FEDamageElasticFiber::d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0) { return tens4ds(0.0); }\n\ndouble FEDamageElasticFiber::m(double P) { return 0.0; }\ndouble FEDamageElasticFiber::dm_dP(double P) { return 0.0; }\ndouble FEDamageElasticFiber::d2m_dP(double P) { return 0.0; }\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FEDamageFiberPower, FEDamageElasticFiber)\n\tADD_PARAMETER(m_a1, FE_RANGE_GREATER_OR_EQUAL(0.0), \"a1\");\n\tADD_PARAMETER(m_a2, FE_RANGE_GREATER(1.0), \"a2\");\n\tADD_PARAMETER(m_kappa, FE_RANGE_CLOSED(0.0, 2.0/3.0), \"kappa\");\nEND_FECORE_CLASS();\n\nFEDamageFiberPower::FEDamageFiberPower(FEModel* fem) : FEDamageElasticFiber(fem)\n{\n\tm_a1 = 0.0;\n\tm_a2 = 0.0;\n\tm_kappa = 0.0;\n}\n\nFEMaterialPointData* FEDamageFiberPower::CreateMaterialPointData()\n{\n\tFEFiberDamagePoint* mp = new FEFiberDamagePoint(new FEElasticMaterialPoint);\n\tmp->m_psf_c = 2.0;\n\treturn mp;\n}\n\ndouble FEDamageFiberPower::Psi0(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n\tdouble I1 = C.tr();\n\n\tdouble I4 = a0 * (C*a0);\n\tdouble I5 = a0 * (C2*a0);\n\tdouble K3 = I1 * I4 - I5;\n\n\tdouble Psi0 = m_kappa * I1 + (1.0 - 3.0*m_kappa / 2.0)*K3;\n\n\treturn Psi0;\n}\n\nmat3ds FEDamageFiberPower::dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d F = pt.m_F;\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble I1 = C.tr();\n\n\tdouble I4 = a0 * (C *a0);\n\tdouble I5 = a0 * (C2*a0);\n\tdouble K3 = I1 * I4 - I5;\n\n\tdouble k = 1.0 - 3.0*m_kappa / 2.0;\n\n\tmat3dd I(1.0);\n\tmat3ds M = dyad(a0);\n\tmat3ds ACA = dyads(a0, C*a0);\n\tmat3ds T = I * I4 + M * I1 - ACA;\n\tmat3ds S0 = I*m_kappa + T * k;\n\n\treturn S0;\n}\n\ntens4ds FEDamageFiberPower::d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEFiberDamagePoint& damagePoint = *mp.ExtractData<FEFiberDamagePoint>();\n\n\tdouble k = 1.0 - 3.0*m_kappa / 2.0;\n\n\tmat3ds M = dyad(a0);\n\tmat3dd I(1.0);\n\ttens4ds AIA = dyad4s(a0, I, a0)*(2.0);\n\ttens4ds IoM = dyad1s(I, M);\n\n\ttens4ds c0 = (IoM - AIA)*k;\n\n\treturn c0;\n}\n\ndouble FEDamageFiberPower::m(double P)\n{\n\treturn m_a1 * pow(P, m_a2);\n}\n\ndouble FEDamageFiberPower::dm_dP(double P)\n{\n\treturn m_a1 * m_a2*pow(P, m_a2 - 1.0);\n}\n\ndouble FEDamageFiberPower::d2m_dP(double P)\n{\n\treturn m_a1 * m_a2*(m_a2 - 1.0)*pow(P, m_a2 - 2.0);\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FEDamageFiberExponential, FEDamageElasticFiber)\n\tADD_PARAMETER(m_k1, FE_RANGE_GREATER_OR_EQUAL(0.0), \"k1\");\n\tADD_PARAMETER(m_k2, FE_RANGE_GREATER(0.0), \"k2\");\n\tADD_PARAMETER(m_kappa, FE_RANGE_CLOSED(0.0, 1.0/3.0), \"kappa\");\nEND_FECORE_CLASS();\n\nFEDamageFiberExponential::FEDamageFiberExponential(FEModel* fem) : FEDamageElasticFiber(fem)\n{\n\tm_k1 = 0.0;\n\tm_k2 = 0.0;\n\tm_kappa = 0.0;\n}\n\nFEMaterialPointData* FEDamageFiberExponential::CreateMaterialPointData()\n{\n\tFEFiberDamagePoint* mp = new FEFiberDamagePoint(new FEElasticMaterialPoint);\n\tmp->m_psf_c = 1.0;\n\treturn mp;\n}\n\n//! Strain energy density\ndouble FEDamageFiberExponential::Psi0(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3ds C = pt.RightCauchyGreen();\n\tdouble I1 = C.tr();\n\tdouble I4 = a0 * (C*a0);\n\n\tdouble psi0 = m_kappa * I1 + (1.0 - 3.0*m_kappa)*I4;\n\n\treturn psi0;\n}\n\n// calculate stress in fiber direction a0\nmat3ds FEDamageFiberExponential::dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tdouble k = 1.0 - 3.0*m_kappa;\n\n\tmat3dd I(1.0);\n\tmat3ds M = dyad(a0);\n\tmat3ds S0 = (I*m_kappa + M*k);\n\n\treturn S0;\n}\n\n// Spatial tangent\ntens4ds FEDamageFiberExponential::d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0)\n{\n\ttens4ds c0(0.0);\n\treturn c0;\n}\n\ndouble FEDamageFiberExponential::m(double P)\n{\n\tif (P <= 0) return 0;\n\treturn (0.5*m_k1 / m_k2)*(exp(m_k2*P*P) - 1);\n}\n\ndouble FEDamageFiberExponential::dm_dP(double P)\n{\n\tif (P <= 0) return 0;\n\treturn (m_k1 * P)*exp(m_k2*P*P);\n}\n\ndouble FEDamageFiberExponential::d2m_dP(double P)\n{\n\tif (P <= 0) return 0;\n\treturn m_k1*(1.0 + (2.0 * m_k2 * P * P))*exp(m_k2*P*P);\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FEDamageFiberExpLinear, FEDamageElasticFiber)\n\tADD_PARAMETER(m_c3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c3\");\n\tADD_PARAMETER(m_c4, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c4\");\n\tADD_PARAMETER(m_c5, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c5\");\n\tADD_PARAMETER(m_lamax, FE_RANGE_GREATER(0.0), \"lambda\");\nEND_FECORE_CLASS();\n\nFEDamageFiberExpLinear::FEDamageFiberExpLinear(FEModel* fem) : FEDamageElasticFiber(fem)\n{\n\tm_c3 = 0.0;\n\tm_c4 = 0.0;\n\tm_c5 = 0.0;\n\tm_lamax = 0.0;\n}\n\nFEMaterialPointData* FEDamageFiberExpLinear::CreateMaterialPointData()\n{\n\tFEFiberDamagePoint* mp = new FEFiberDamagePoint(new FEElasticMaterialPoint);\n\tmp->m_psf_c = 1.0;\n\treturn mp;\n}\n\ndouble FEDamageFiberExpLinear::Psi0(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3ds C = pt.RightCauchyGreen();\n\tdouble I4 = a0 * (C*a0);\n\n\tdouble Psi0 = sqrt(I4);\n\treturn Psi0;\n}\n\nmat3ds FEDamageFiberExpLinear::dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3ds C = pt.RightCauchyGreen();\n\tdouble I4 = a0 * (C*a0);\n\tdouble l = sqrt(I4);\n\n\tmat3ds M = dyad(a0);\n\tmat3ds S0 = M * (0.5 / l);\n\treturn S0;\n}\n\ntens4ds FEDamageFiberExpLinear::d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3ds C = pt.RightCauchyGreen();\n\tdouble I4 = a0 * (C*a0);\n\tdouble l = sqrt(I4);\n\n\tmat3ds M = dyad(a0);\n\ttens4ds c0 = dyad1s(M)*( - 1.0 / (4.0*l*l*l));\n\n\treturn c0;\n}\n\ninline double Ei(double x) { return expint_Ei(x); }\n\ndouble FEDamageFiberExpLinear::m(double P)\n{\n\tdouble m = 0.0;\n\n\tdouble Pmax = m_lamax - 1.0;\n\tif (P <= Pmax)\n\t{\n\t\tm = m_c3 * (exp(-m_c4)*(Ei(m_c4*(P+1.0)) - Ei(m_c4)) - log(P + 1));\n\t}\n\telse\n\t{\n\t\tdouble c6 = m_c3 * (exp(m_c4*Pmax) - 1) - (Pmax + 1.0) * m_c5;\n\n\t\t// constant offset for ensuring continuity of strain-energy\n\t\tdouble d0 = m_c3* (exp(-m_c4) * (Ei(m_c4 * (Pmax + 1.0)) - Ei(m_c4)) - log(Pmax + 1)) - m_c5 * Pmax - c6 * log(Pmax + 1.0);\n\n\t\tm = m_c5 * P + c6 * log(P + 1.0) + d0;\n\t}\n\n\treturn m;\n}\n\ndouble FEDamageFiberExpLinear::dm_dP(double P)\n{\n\tdouble dm = 0.0;\n\tdouble Pmax = m_lamax - 1.0;\n\tif (P <= Pmax)\n\t{\n\t\tdm = m_c3 * (exp(m_c4*P) - 1) / (P+1.0);\n\t}\n\telse\n\t{\n\t\tdouble c6 = m_c3 * (exp(m_c4*Pmax) - 1) - (Pmax + 1.0) * m_c5;\n\t\tdm = m_c5 + c6 / (P + 1.0);\n\t}\n\n\treturn dm;\n}\n\ndouble FEDamageFiberExpLinear::d2m_dP(double P)\n{\n\n\tdouble d2m = 0.0;\n\tdouble Pmax = m_lamax - 1.0;\n\tif (P <= Pmax)\n\t{\n\t\tdouble expP = exp(m_c4*P);\n\t\td2m = m_c3 * m_c4*expP / (P+1) - m_c3*(expP - 1) / ((P+1)*(P+1));\n\t}\n\telse\n\t{\n\t\tdouble c6 = m_c3 * (exp(m_c4*Pmax) - 1) - (Pmax + 1.0) * m_c5;\n\t\td2m = -c6 / ((P+1)*(P+1));\n\t}\n\n\treturn d2m;\n}\n"
  },
  {
    "path": "FEBioMech/FEContinuousElasticDamage.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEElasticFiberMaterial.h\"\n\n// These classes implement the elastic damage framework from \n// Balzani, Brinkhues, Holzapfel, Comput. Methods Appl. Mech. Engrg. 213216 (2012) 139151\n\nclass FEFiberDamagePoint;\n\n//=================================================================================================\n// Base class for continuous damage elastic fiber materials. \n//\nclass FEDamageElasticFiber : public FEElasticFiberMaterial\n{\npublic:\n\tFEDamageElasticFiber(FEModel* fem);\n\n\tdouble Damage(FEMaterialPoint& mp);\n\tdouble Damage(FEMaterialPoint& mp, int n);\n\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// calculate stress in fiber direction a0\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\nprotected:\n\t// strain-energy and its derivatives\n\tvirtual double Psi0(FEMaterialPoint& mp, const vec3d& a0);\n\tvirtual mat3ds dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0);\n\tvirtual tens4ds d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0);\n\n\tvirtual double m(double P);\n\tvirtual double dm_dP(double P);\n\tvirtual double d2m_dP(double P);\n\nprotected:\n\t// damage model parameters\n\tdouble\tm_tinit;\t\t// start time of damage\n\tdouble\tm_Dmax;\t\t\t// max damage\n\tdouble\tm_beta_s;\t\t// saturation parameter\n\tdouble\tm_gamma_max;\t// saturation parameter\n\tdouble\tm_r_s, m_r_inf;\n\n\t// D2 parameters\n\tdouble m_D2_a;\n\tdouble m_D2_b;\n\tdouble m_D2_c;\n\tdouble m_D2_d;\n\n\tdouble m_D3_g0;\n\tdouble m_D3_rg;\n\tdouble m_D3_inf;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//=================================================================================================\nclass FEDamageFiberPower : public FEDamageElasticFiber\n{\npublic:\n\tFEDamageFiberPower(FEModel* fem);\n\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\nprotected:\n\tdouble Psi0(FEMaterialPoint& mp, const vec3d& a0) override;\n\tmat3ds dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0) override;\n\ttens4ds d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\tdouble m(double P) override;\n\tdouble dm_dP(double P) override;\n\tdouble d2m_dP(double P) override;\n\npublic:\n\t// fiber parameters\n\tdouble\tm_a1, m_a2, m_kappa;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//=================================================================================================\nclass FEDamageFiberExponential : public FEDamageElasticFiber\n{\npublic:\n\tFEDamageFiberExponential(FEModel* fem);\n\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\nprotected:\n\tdouble Psi0(FEMaterialPoint& mp, const vec3d& a0) override;\n\tmat3ds dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0) override;\n\ttens4ds d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\tdouble m(double P) override;\n\tdouble dm_dP(double P) override;\n\tdouble d2m_dP(double P) override;\n\npublic:\n\t// fiber parameters\n\tdouble\tm_k1, m_k2, m_kappa;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//=================================================================================================\nclass FEDamageFiberExpLinear: public FEDamageElasticFiber\n{\npublic:\n\tFEDamageFiberExpLinear(FEModel* fem);\n\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\nprotected:\n\tdouble Psi0(FEMaterialPoint& mp, const vec3d& a0) override;\n\tmat3ds dPsi0_dC(FEMaterialPoint& mp, const vec3d& a0) override;\n\ttens4ds d2Psi0_dC(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\tdouble m(double P) override;\n\tdouble dm_dP(double P) override;\n\tdouble d2m_dP(double P) override;\n\npublic:\n\tdouble\tm_c3;\n\tdouble\tm_c4;\n\tdouble\tm_c5;\n\tdouble\tm_lamax;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEContinuousFiberDistribution.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEContinuousFiberDistribution.h\"\n\nBEGIN_FECORE_CLASS(FEContinuousFiberDistribution, FEElasticMaterial)\n\n\t// material properties\n\tADD_PROPERTY(m_pFmat, \"fibers\");\n\tADD_PROPERTY(m_pFDD, \"distribution\");\n\tADD_PROPERTY(m_pFint, \"scheme\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEContinuousFiberDistribution::FEContinuousFiberDistribution(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_pFmat = 0;\n\tm_pFDD = 0;\n\tm_pFint = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEContinuousFiberDistribution::~FEContinuousFiberDistribution() {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEContinuousFiberDistribution::CreateMaterialPointData()\n{\n\tFEMaterialPointData* mp = FEElasticMaterial::CreateMaterialPointData();\n\tmp->SetNext(m_pFmat->CreateMaterialPointData());\n    return mp;\n}\n\n//-----------------------------------------------------------------------------\nbool FEContinuousFiberDistribution::Init()\n{\n    // initialize base class\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FEContinuousFiberDistribution::Serialize(DumpStream& ar)\n{\t\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEContinuousFiberDistribution::Stress(FEMaterialPoint& mp)\n{ \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n\t// calculate stress\n\tmat3ds s; s.zero();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n    \n    double IFD = IntegratedFiberDensity(mp);\n\n\tfp.m_index = 0;\n\n\t// obtain an integration point iterator\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(&mp);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n\t\t\t// get the fiber direction for that fiber distribution\n\t\t\tvec3d& N = it->m_fiber;\n\n\t\t\t// evaluate ellipsoidally distributed material coefficients\n\t\t\tdouble R = m_pFDD->FiberDensity(mp, N);\n            \n            // convert fiber to global coordinates\n            vec3d n0 = Q*N;\n            \n\t\t\t// calculate the stress\n\t\t\tdouble wn = it->m_weight;\n\t\t\ts += m_pFmat->FiberStress(mp, fp.FiberPreStretch(n0))*(R*wn);\n\n\t\t\tfp.m_index++;\n\t\t}\n\t\twhile (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// divide by IFD\n\treturn s / IFD;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEContinuousFiberDistribution::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n    \n    double IFD = IntegratedFiberDensity(mp);\n\n\t// initialize stress tensor\n\ttens4ds c;\n\tc.zero();\n\n\tfp.m_index = 0;\n\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(&mp);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n            // get the fiber direction for that fiber distribution\n            vec3d& N = it->m_fiber;\n            \n            // evaluate ellipsoidally distributed material coefficients\n            double R = m_pFDD->FiberDensity(mp, N);\n            \n            // convert fiber to global coordinates\n            vec3d n0 = Q*N;\n\n\t\t\t// calculate the tangent\n\t\t\tc += m_pFmat->FiberTangent(mp, fp.FiberPreStretch(n0))*(R*it->m_weight);\n\n\t\t\tfp.m_index++;\n\t\t}\n\t\twhile (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n    \n\t// divide by IFD\n\treturn c / IFD;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\n//double FEContinuousFiberDistribution::StrainEnergyDensity(FEMaterialPoint& pt) { return m_pFint->StrainEnergyDensity(pt); }\ndouble FEContinuousFiberDistribution::StrainEnergyDensity(FEMaterialPoint& mp)\n{ \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n    \n    double IFD = IntegratedFiberDensity(mp);\n\n\tfp.m_index = 0;\n\n\tdouble sed = 0.0;\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(&mp);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n            // get the fiber direction for that fiber distribution\n            vec3d& N = it->m_fiber;\n            \n            // evaluate ellipsoidally distributed material coefficients\n            double R = m_pFDD->FiberDensity(mp, N);\n            \n            // convert fiber to global coordinates\n            vec3d n0 = Q*N;\n\n\t\t\t// calculate the stress\n\t\t\tsed += m_pFmat->FiberStrainEnergyDensity(mp, fp.FiberPreStretch(n0))*(R*it->m_weight);\n\n\t\t\tfp.m_index++;\n\t\t}\n\t\twhile (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// divide by IFD\n\treturn sed / IFD;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEContinuousFiberDistribution::IntegratedFiberDensity(FEMaterialPoint& mp)\n{\n\tdouble IFD = 0;\n\t// NOTE: Pass nullptr to GetIterator to avoid issues with GK rule!\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(nullptr);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n            // get the fiber direction for that fiber distribution\n            vec3d& N = it->m_fiber;\n\n\t\t\tdouble R = m_pFDD->FiberDensity(mp, N);\n\n\t\t\t// integrate the fiber distribution\n\t\t\tIFD += R * it->m_weight;\n\n\t\t} while (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// just in case\n\tif (IFD == 0.0) IFD = 1.0;\n\n\treturn IFD;\n}\n"
  },
  {
    "path": "FEBioMech/FEContinuousFiberDistribution.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEFiberMaterial.h\"\n#include \"FEFiberDensityDistribution.h\"\n#include \"FEFiberIntegrationScheme.h\"\n#include \"FEFiberMaterialPoint.h\"\n\n//  This material is a container for a fiber material, a fiber density\n//  distribution, and an integration scheme.\n//\nclass FEContinuousFiberDistribution : public FEElasticMaterial\n{\npublic:\n    FEContinuousFiberDistribution(FEModel* pfem);\n    ~FEContinuousFiberDistribution();\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // Initialization\n    bool Init() override;\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n    \n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n    \n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tdouble IntegratedFiberDensity(FEMaterialPoint& pt);\n\nprotected:\n\tFEFiberMaterial*\t\t\tm_pFmat;    // pointer to fiber material\n\tFEFiberDensityDistribution* m_pFDD;     // pointer to fiber density distribution\n\tFEFiberIntegrationScheme*   m_pFint;    // pointer to fiber integration scheme\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEContinuousFiberDistributionUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEContinuousFiberDistributionUC.h\"\n\nBEGIN_FECORE_CLASS(FEContinuousFiberDistributionUC, FEUncoupledMaterial)\n\t// set material properties\n\tADD_PROPERTY(m_pFmat, \"fibers\");\n\tADD_PROPERTY(m_pFDD , \"distribution\");\n\tADD_PROPERTY(m_pFint, \"scheme\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEContinuousFiberDistributionUC::FEContinuousFiberDistributionUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_pFmat = 0;\n\tm_pFDD = 0;\n\tm_pFint = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEContinuousFiberDistributionUC::~FEContinuousFiberDistributionUC() {}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEContinuousFiberDistributionUC::CreateMaterialPointData() \n{\n\tFEMaterialPointData* mp = FEUncoupledMaterial::CreateMaterialPointData();\n\tmp->SetNext(m_pFmat->CreateMaterialPointData());\n\treturn mp;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEContinuousFiberDistributionUC::DevStress(FEMaterialPoint& mp)\n{ \n\tFEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n\t// calculate stress\n\tmat3ds s; s.zero();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n\tdouble IFD = IntegratedFiberDensity(mp);\n\n\t// obtain an integration point iterator\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(&mp);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n            // get the fiber direction for that fiber distribution\n            vec3d& N = it->m_fiber;\n            \n            // evaluate ellipsoidally distributed material coefficients\n            double R = m_pFDD->FiberDensity(mp, N);\n            \n            // convert fiber to global coordinates\n            vec3d n0 = Q*N;\n\n\t\t\t// calculate the stress\n\t\t\tdouble wn = it->m_weight;\n\t\t\ts += m_pFmat->DevFiberStress(mp, fp.FiberPreStretch(n0))*(R*wn);\n\t\t}\n\t\twhile (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// divide by IFD\n\treturn s / IFD;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEContinuousFiberDistributionUC::DevTangent(FEMaterialPoint& mp)\n{ \n\tFEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n\t// initialize stress tensor\n\ttens4ds c;\n\tc.zero();\n\n\tdouble IFD = IntegratedFiberDensity(mp);\n\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(&mp);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n            // get the fiber direction for that fiber distribution\n            vec3d& N = it->m_fiber;\n            \n            // evaluate ellipsoidally distributed material coefficients\n            double R = m_pFDD->FiberDensity(mp, N);\n            \n            // convert fiber to global coordinates\n            vec3d n0 = Q*N;\n\n\t\t\t// calculate the tangent\n\t\t\tc += m_pFmat->DevFiberTangent(mp, fp.FiberPreStretch(n0))*(R*it->m_weight);\n\t\t}\n\t\twhile (it->Next());\n\t}\n\t\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// divide by IFD\n\treturn c / IFD;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEContinuousFiberDistributionUC::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{ \n\tFEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n\tdouble IFD = IntegratedFiberDensity(mp);\n\tdouble sed = 0.0;\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(&mp);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n            // get the fiber direction for that fiber distribution\n            vec3d& N = it->m_fiber;\n            \n            // evaluate ellipsoidally distributed material coefficients\n            double R = m_pFDD->FiberDensity(mp, N);\n            \n            // convert fiber to global coordinates\n            vec3d n0 = Q*N;\n\n\t\t\t// calculate the stress\n\t\t\tsed += m_pFmat->DevFiberStrainEnergyDensity(mp, fp.FiberPreStretch(n0))*(R*it->m_weight);\n\t\t}\n\t\twhile (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// divide by IFD\n\treturn sed / IFD;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEContinuousFiberDistributionUC::IntegratedFiberDensity(FEMaterialPoint& mp)\n{\n\tdouble IFD = 0;\n\t// NOTE: Pass nullptr to GetIterator to avoid issues with GK rule!\n\tFEFiberIntegrationSchemeIterator* it = m_pFint->GetIterator(nullptr);\n\tif (it->IsValid())\n\t{\n\t\tdo\n\t\t{\n\t\t\t// set fiber direction in global coordinate system\n\t\t\tvec3d& N = it->m_fiber;\n\n\t\t\tdouble R = m_pFDD->FiberDensity(mp, N);\n\n\t\t\t// integrate the fiber distribution\n\t\t\tIFD += R * it->m_weight;\n\n\t\t} while (it->Next());\n\t}\n\n\t// don't forget to delete the iterator\n\tdelete it;\n\n\t// just in case\n\tif (IFD == 0.0) IFD = 1.0;\n\n\treturn IFD;\n}\n"
  },
  {
    "path": "FEBioMech/FEContinuousFiberDistributionUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberDensityDistribution.h\"\n#include \"FEFiberIntegrationScheme.h\"\n#include \"FEFiberMaterialPoint.h\"\n#include \"FEFiberMaterial.h\"\n\n//  This material is a container for a fiber material, a fiber density\n//  distribution, and an integration scheme.\n//\nclass FEContinuousFiberDistributionUC : public FEUncoupledMaterial\n{\npublic:\n    FEContinuousFiberDistributionUC(FEModel* pfem);\n    ~FEContinuousFiberDistributionUC();\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \npublic:\n\t//! calculate stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n    \n\t//! calculate tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \nprivate:\n\tdouble IntegratedFiberDensity(FEMaterialPoint& pt);\n\nprotected:\n\tFEFiberMaterialUncoupled*\tm_pFmat;    // pointer to fiber material\n\tFEFiberDensityDistribution* m_pFDD;     // pointer to fiber density distribution\n\tFEFiberIntegrationScheme*\tm_pFint;    // pointer to fiber integration scheme\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECoupledMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoupledMooneyRivlin.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECoupledMooneyRivlin, FEElasticMaterial)\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_K , FE_RANGE_GREATER(0.0), \"k\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FECoupledMooneyRivlin::Stress(FEMaterialPoint& mp)\n{\n\t// get the elastic material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\n\t// identity tensor\n\tmat3dd I(1.0);\n\n\t// calculate stress\n\treturn (B*(m_c1+I1*m_c2) - B2*m_c2 - I*(m_c1+2.0*m_c2))*(2.0/J) + I*(m_K*log(J)/J);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent at material point\ntens4ds FECoupledMooneyRivlin::Tangent(FEMaterialPoint& mp)\n{\n\t// get material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// Invariants of B (= invariants of C)\n\tdouble J = pt.m_J;\n\n\t// some useful tensors\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds IoI = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds BoB = dyad4s(B);\n\n\t// strain energy derivates\n\tdouble W2 = m_c2;\n\n\t// spatial tangent\n\ttens4ds c = BxB*(4.0*W2/J) - BoB*(4.0*W2/J) + IoI*(4.0*(m_c1+2.0*m_c2)/J) + IxI*(m_K/J) - IoI*(2.0*m_K*log(J)/J);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FECoupledMooneyRivlin::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the elastic material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n    double I2 = (I1*I1 - B2.tr())/2.;\n    \n    double sed = m_c1*(I1-3) + m_c2*(I2-3)\n    - 2*(m_c1+2*m_c2)*lnJ + m_K*lnJ*lnJ/2.;\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FECoupledMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This is a coupled formulation for the Mooney-Rivlin material.\nclass FECoupledMooneyRivlin : public FEElasticMaterial\n{\npublic:\n\tFECoupledMooneyRivlin(FEModel* pfem) : FEElasticMaterial(pfem){}\n\nprotected:\n\tdouble\tm_c1;\t//!< Mooney-Rivlin parameter c1\n\tdouble\tm_c2;\t//!< Mooney-Rivlin parameter c2\n\tdouble\tm_K;\t//!< bulk modulus\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECoupledTransIsoMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoupledTransIsoMooneyRivlin.h\"\n#include <FECore/log.h>\n#include <FECore/expint_Ei.h>\n#include <FECore/FEConstValueVec3.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECoupledTransIsoMooneyRivlin, FEElasticMaterial)\n\tADD_PARAMETER(m_c1  , FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2  , \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c3  , \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c4  , \"c4\");\n\tADD_PARAMETER(m_c5  , \"c5\");\n\n\t// NOTE: This was changed from \"lambda\" to make it consistent with febio3, but in febiostudio1 it \n\t// was defined as \"lambda\". This means that old fsm files that use this material might not read in correctly. \n\tADD_PARAMETER(m_flam, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam_max\");\n\n\tADD_PARAMETER(m_K   , FE_RANGE_GREATER(0.0), \"k\");\n\t\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFECoupledTransIsoMooneyRivlin::FECoupledTransIsoMooneyRivlin(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\tm_c3 = 0.0;\n\tm_c4 = 0.0;\n\tm_c5 = 0.0;\n\tm_flam = 1.0;\n\tm_K = 0.0;\n\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the Cauchy stress\nmat3ds FECoupledTransIsoMooneyRivlin::Stress(FEMaterialPoint& mp)\n{\n\t// get the material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble l = a.unit();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble J = pt.m_J;\n\n\t// some useful tensors\n\tmat3dd I(1.0);\n\tmat3ds A = dyad(a);\n\n\t// a. define the matrix stress\n\t//-----------------------------\n\t// W = c1*(I1 - 3) + c2*(I2 - 3)\n\t// Wi = dW/dIi\n\tdouble W1 = m_c1(mp);\n\tdouble W2 = m_c2(mp);\n    \n    double flam = m_flam(mp);\n    double c3 = m_c3(mp);\n    double c4 = m_c4(mp);\n    double c5 = m_c5(mp);\n    double K = m_K(mp);\n\n\tmat3ds s = (B*(W1 + I1*W2) - B2*W2 - I*(W1 + 2.0*W2))*(2.0/J);\n\n\t// b. define fiber stress\n\t//-------------------------\n\tif (l > 1.0)\n\t{\n\t\tdouble Wl = 0.0;\n\t\tif (l < flam)\n\t\t{\n\t\t\tWl = c3*(exp(c4*(l - 1.0)) - 1.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = c3*(exp(c4*(flam - 1.0)) - 1.0) - c5*flam;\n\t\t\tWl = c5*l + c6;\n\t\t}\n\t\ts += A*(Wl/J);\n\t}\n\n\t// c. define dilational stress\n\t//------------------------------\n\t// U(J) = 1/2*k*(lnJ)^2\n\tdouble UJ = K*log(J)/J;\n\ts += I*UJ;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the spatial elasticity tangent\ntens4ds FECoupledTransIsoMooneyRivlin::Tangent(FEMaterialPoint& mp)\n{\n\t// get material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble l = a.unit();\n\n\t// Invariants of B (= invariants of C)\n\tdouble J = pt.m_J;\n\tdouble I4 = l*l;\n\n\t// some useful tensors\n\tmat3dd I(1.0);\n\tmat3ds A = dyad(a);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds IoI = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds BoB = dyad4s(B);\n\ttens4ds AxA = dyad1s(A);\n\n\t// a. matrix tangent\n\t//----------------------------------\n    double W1 = m_c1(mp);\n    double W2 = m_c2(mp);\n    \n    double flam = m_flam(mp);\n    double c3 = m_c3(mp);\n    double c4 = m_c4(mp);\n    double c5 = m_c5(mp);\n    double K = m_K(mp);\n\n\ttens4ds c = BxB*(4.0*W2/J) - BoB*(4.0*W2/J)  + IoI*(4.0*(W1+2.0*W2)/J);\n\n\t// b. fiber tangent\n\t// ---------------------------------\n\tif (l > 1.0)\n\t{\n\t\tdouble Fl = 0.0, Fll = 0.0;\n\t\tif (l < flam)\n\t\t{\n\t\t\tFl = c3*(exp(c4*(l - 1.0)) - 1.0)/l;\n\t\t\tFll = -c3*(exp(c4*(l-1.0)) - 1.0)/(l*l) + c3*c4*exp(c4*(l-1.0))/l;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = c3*(exp(c4*(flam - 1.0)) - 1.0) - c5*flam;\n\t\t\tFl = c5 + c6 / l;\n\t\t\tFll = -c6/(l*l);\n\t\t}\n\n\t\tdouble W44 = (Fll - Fl/l)/(4*l*l);\n\n\t\tc += AxA*(4.0*W44*I4*I4/J);\n\t}\n\n\t// c. dilational tangent\n\t// ---------------------------------\n\tdouble UJ = K*log(J)/J;\n\tdouble UJJ = K*(1 - log(J))/(J*J);\n\tc += IxI*(UJ + J*UJJ) - IoI*(2.0*UJ);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FECoupledTransIsoMooneyRivlin::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n    \n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble l = a.unit();\n    \n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n    double I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n    \n    double c1 = m_c1(mp);\n    double c2 = m_c2(mp);\n    double flam = m_flam(mp);\n    double c3 = m_c3(mp);\n    double c4 = m_c4(mp);\n    double c5 = m_c5(mp);\n    double K = m_K(mp);\n    \n\t// a. define the matrix sed\n\t//-----------------------------\n\t// W = c1*(I1 - 3) + c2*(I2 - 3)\n    double sed = c1*(I1 - 3) + c2*(I2 - 3) - 2*(c1 + 2*c2)*lnJ;\n    \n\t// b. define fiber strain energy density\n\t//-------------------------\n\tif (l > 1.0)\n\t{\n\t\tif (l < flam)\n\t\t{\n            sed += c3*(exp(-c4)*(expint_Ei(c4*l) - expint_Ei(c4))-log(l));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = c3*(exp(c4*(flam - 1.0)) - 1.0) - c5*flam;\n\t\t\tsed += c5*(l-1) +c6*log(l);\n\t\t}\n\t}\n    \n\t// c. define dilational stress\n\t//------------------------------\n\t// U(J) = 1/2*k*(lnJ)^2\n\tsed += K*lnJ*lnJ/2;\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FECoupledTransIsoMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Coupled transversely-isotropic Mooney-Rivlin material\n//!\nclass FECoupledTransIsoMooneyRivlin : public FEElasticMaterial\n{\npublic:\n\tFECoupledTransIsoMooneyRivlin(FEModel* pfem);\n\npublic:\n\tFEParamDouble\tm_c1;\t//!< Mooney-Rivlin coefficient C1\n    FEParamDouble\tm_c2;\t//!< Mooney-Rivlin coefficient C2\n    FEParamDouble\tm_c3;\t//!< fiber stress scale factor\n    FEParamDouble\tm_c4;\t//!< exponential scale factor\n    FEParamDouble\tm_c5;\t//!< slope of linear stress region\n    FEParamDouble\tm_flam;\t//!< fiber stretch at which fibers are straight\n    FEParamDouble\tm_K;\t//!< \"bulk\"-modulus\n\n\tFEVec3dValuator*\tm_fiber;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECoupledTransIsoVerondaWestmann.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoupledTransIsoVerondaWestmann.h\"\n#include <FECore/log.h>\n#include <FECore/expint_Ei.h>\n#include <FECore/FEConstValueVec3.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECoupledTransIsoVerondaWestmann, FEElasticMaterial)\n\tADD_PARAMETER(m_c1  , FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2  , \"c2\");\n\tADD_PARAMETER(m_c3  , \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c4  , \"c4\");\n\tADD_PARAMETER(m_c5  , \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_flam, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lambda\");\n\tADD_PARAMETER(m_K   , FE_RANGE_GREATER(0.0), \"k\")->setUnits(UNIT_PRESSURE);\n\t\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFECoupledTransIsoVerondaWestmann::FECoupledTransIsoVerondaWestmann(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\tm_c3 = 0.0;\n\tm_c4 = 0.0;\n\tm_c5 = 0.0;\n\tm_flam = 1.0;\n\tm_K = 0.0;\n\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the Cauchy stress\nmat3ds FECoupledTransIsoVerondaWestmann::Stress(FEMaterialPoint& mp)\n{\n\t// get the material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble l = a.unit();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble J = pt.m_J;\n\n\t// some useful tensors\n\tmat3dd I(1.0);\n\tmat3ds A = dyad(a);\n\n\t// a. define the matrix stress\n\t//-----------------------------\n\t// W = C1*(exp(C2*(I1-3)-1)-0.5*C1*C2*(I2 - 3)\n\t// Wi = dW/dIi\n\tdouble W1 = m_c1*m_c2*exp(m_c2*(I1-3));\n\tdouble W2 = -0.5*m_c1*m_c2;\n\n\tmat3ds s = (B*(W1 + I1*W2) - B2*W2)*(2.0/J);\n\n\t// b. define fiber stress\n\t//-------------------------\n\tif (l > 1.0)\n\t{\n\t\tdouble Wl = 0.0;\n\t\tif (l < m_flam)\n\t\t{\n\t\t\tWl = m_c3*(exp(m_c4*(l - 1.0)) - 1.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_flam - 1.0)) - 1.0) - m_c5*m_flam;\n\t\t\tWl = m_c5*l + c6;\n\t\t}\n\t\ts += A*(Wl/J);\n\t}\n\n\t// c. define dilational stress\n\t//------------------------------\n\t// U(J) = 1/2*k*(lnJ)^2\n\tdouble UJ = m_K*log(J)/J;\n\ts += I*UJ;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the spatial elasticity tangent\ntens4ds FECoupledTransIsoVerondaWestmann::Tangent(FEMaterialPoint& mp)\n{\n\t// get material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble l = a.unit();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble J = pt.m_J;\n\tdouble I4 = l*l;\n\n\t// some useful tensors\n\tmat3dd I(1.0);\n\tmat3ds A = dyad(a);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds IoI = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds BoB = dyad4s(B);\n\ttens4ds AxA = dyad1s(A);\n\n\t// a. matrix tangent\n\t//----------------------------------\n\tdouble W1 = m_c1*m_c2*exp(m_c2*(I1-3));\n\tdouble W11 = m_c2*W1;\n\tdouble W2 = -0.5*m_c1*m_c2;\n\ttens4ds c = BxB*(4.0*(W11+W2)/J) - BoB*(4.0*W2/J);\n\n\t// b. fiber tangent\n\t// ---------------------------------\n\tif (l > 1.0)\n\t{\n\t\tdouble Fl = 0.0, Fll = 0.0;\n\t\tif (l < m_flam)\n\t\t{\n\t\t\tFl = m_c3*(exp(m_c4*(l - 1.0)) - 1.0)/l;\n\t\t\tFll = -m_c3*(exp(m_c4*(l-1.0)) - 1.0)/(l*l) + m_c3*m_c4*exp(m_c4*(l-1.0))/l;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_flam - 1.0)) - 1.0) - m_c5*m_flam;\n\t\t\tFl = m_c5 + c6 / l;\n\t\t\tFll = -c6/(l*l);\n\t\t}\n\n\t\tdouble W44 = (Fll - Fl/l)/(4*l*l);\n\n\t\tc += AxA*(4.0*W44*I4*I4/J);\n\t}\n\n\t// c. dilational tangent\n\t// ---------------------------------\n\tdouble UJ = m_K*log(J)/J;\n\tdouble UJJ = m_K*(1 - log(J))/(J*J);\n\tc += IxI*(UJ + J*UJJ) - IoI*(2.0*UJ);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FECoupledTransIsoVerondaWestmann::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble l = a.unit();\n    \n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n    double I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n    \n\t// a. define the matrix strain energy density\n\t//-----------------------------\n\t// W = C1*(exp(C2*(I1-3)-1)-0.5*C1*C2*(I2 - 3)\n    double sed = m_c1*(exp(m_c2*(I1-3))-1) - m_c1*m_c2*(I2-3)/2;\n    \n\t// b. define fiber strain energy density\n\t//-------------------------\n\tif (l > 1.0)\n\t{\n\t\tif (l < m_flam)\n\t\t{\n            sed += m_c3*(exp(-m_c4)*(expint_Ei(m_c4*l) - expint_Ei(m_c4))-log(l));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = m_c3*(exp(m_c4*(m_flam - 1.0)) - 1.0) - m_c5*m_flam;\n\t\t\tsed += m_c5*(l-1) +c6*log(l);\n\t\t}\n\t}\n    \n\t// c. add dilational strain energy density\n\t//------------------------------\n\t// U(J) = 1/2*k*(lnJ)^2\n    sed += m_K*lnJ*lnJ/2;\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FECoupledTransIsoVerondaWestmann.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Coupled transversely-isotropic Veronda-Westmann material\n//!\nclass FECoupledTransIsoVerondaWestmann: public FEElasticMaterial\n{\npublic:\n\tFECoupledTransIsoVerondaWestmann(FEModel* pfem);\n\npublic:\n\tdouble\tm_c1;\t//!< Veronda-Westmann coefficient C1\n\tdouble\tm_c2;\t//!< Veronda-Westmann coefficient C2\n\tdouble\tm_c3;\t//!< fiber stress scale factor\n\tdouble\tm_c4;\t//!< exponential scale factor\n\tdouble\tm_c5;\t//!< slope of linear stress region\n\tdouble\tm_flam;\t//!< fiber stretch at which fibers are straight\n\tdouble\tm_K;\t//!< \"bulk\"-modulus\n\n\tFEVec3dValuator*\tm_fiber;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECoupledVerondaWestmann.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoupledVerondaWestmann.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECoupledVerondaWestmann, FEElasticMaterial)\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\");\n\tADD_PARAMETER(m_k , FE_RANGE_GREATER(0.0), \"k\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FECoupledVerondaWestmann::Stress(FEMaterialPoint& mp)\n{\n\t// get the elastic material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\n\t// identity tensor\n\tmat3dd I(1.0);\n\n\t// calculate stress\n\treturn B*(m_c1*m_c2*(2.0*exp(m_c2*(I1-3)) - I1)/J) + B2*(m_c1*m_c2/J) + I*(m_k*log(J)/J);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent at material point\ntens4ds FECoupledVerondaWestmann::Tangent(FEMaterialPoint& mp)\n{\n\t// get material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble J = pt.m_J;\n\n\t// some useful tensors\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds IoI = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds BoB = dyad4s(B);\n\n\t// strain energy derivates\n\tdouble W1 = m_c1;\n\tdouble W2 = m_c2;\n\n\t// spatial tangent\n\ttens4ds c = BxB*(2.0*W1*W2*(2.0*W2*exp(W2*(I1-3))-1)/J) + BoB*(2.0*W1*W2/J) + IxI*(m_k/J) - IoI*(2.0*m_k*log(J)/J);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FECoupledVerondaWestmann::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the elastic material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n    double I2 = (I1*I1 - B2.tr())/2.0;\n    \n    double sed = m_c1*(exp(m_c2*(I1-3))-1) - m_c1*m_c2*(I2-3)/2 + m_k*lnJ*lnJ/2;\n    \n    return sed;\n}\n\n"
  },
  {
    "path": "FEBioMech/FECoupledVerondaWestmann.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This material describes a coupled Veronda-Westmann formulation\nclass FECoupledVerondaWestmann : public FEElasticMaterial\n{\npublic:\n\t//! constructor\n\tFECoupledVerondaWestmann(FEModel* pfem) : FEElasticMaterial(pfem){}\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \nprotected:\n\tdouble\tm_c1;\t//!< Veronda-Westmann material parameter c1\n\tdouble\tm_c2;\t//!< Veronda-Westmann material parameter c2\n\tdouble\tm_k;\t//!< bulk-modulus\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FECubicCLE.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECubicCLE.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECubicCLE, FEElasticMaterial)\n\tADD_PARAMETER(m_lp1, \"lp1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_lm1, \"lm1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_l2 , \"l2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_mu , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Check material parameters.\nbool FECubicCLE::Validate()\n{\n    if (FEElasticMaterial::Validate() == false) return false;\n\n\t// Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = m_mu;\n    mu[1] = m_mu;\n    mu[2] = m_mu;\n    lam[0][0] = m_lm1; lam[0][1] = m_l2 ; lam[0][2] = m_l2;\n    lam[1][0] = m_l2 ; lam[1][1] = m_lm1; lam[1][2] = m_l2;\n    lam[2][0] = m_l2 ; lam[2][1] = m_l2 ; lam[2][2] = m_lm1;\n    \n    // check that stiffness matrix is positive definite\n    mat3ds c(lam[0][0]+2*mu[0],lam[1][1]+2*mu[1],lam[2][2]+2*mu[2],lam[0][1],lam[1][2],lam[0][2]);\n    double l[3];\n    c.exact_eigen(l);\n    \n\tif ((l[0] < 0) || (l[1] < 0) || (l[2] < 0))\n\t{\n\t\tfeLogError(\"Stiffness matrix is not positive definite.\");\n\t\treturn false;\n\t}\n    \n    // repeat check with all tensile diagonal first lamé constants\n    lam[0][0] = m_lp1; lam[1][1] = m_lp1; lam[2][2] = m_lp1;\n    // check that compliance matrix is positive definite\n    c = mat3ds(lam[0][0]+2*mu[0],lam[1][1]+2*mu[1],lam[2][2]+2*mu[2],lam[0][1],lam[1][2],lam[0][2]);\n    c.exact_eigen(l);\n    \n\tif ((l[0] < 0) || (l[1] < 0) || (l[2] < 0))\n\t{\n\t\tfeLogError(\"Stiffness matrix is not positive definite.\");\n\t\treturn false;\n\t}\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the stress\nmat3ds FECubicCLE::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = m_mu;\n    mu[1] = m_mu;\n    mu[2] = m_mu;\n    lam[0][0] = m_lm1; lam[0][1] = m_l2 ; lam[0][2] = m_l2;\n    lam[1][0] = m_l2 ; lam[1][1] = m_lm1; lam[1][2] = m_l2;\n    lam[2][0] = m_l2 ; lam[2][1] = m_l2 ; lam[2][2] = m_lm1;\n    \n    int i,j;\n    vec3d a0;           // texture direction in reference configuration\n    mat3ds A0[3];\t\t// texture tensor in reference configuration\n    double AE[3];\t\t// E:A\n    mat3ds E = pt.Strain();\n    \n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    for (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n        // Copy the texture direction in the reference configuration to a0\n        a0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n        A0[i] = dyad(a0);\n        AE[i] = a0*(E*a0);\n    }\n    \n    lam[0][0] = (AE[0] >= 0) ? m_lp1 : m_lm1;\n    lam[1][1] = (AE[1] >= 0) ? m_lp1 : m_lm1;\n    lam[2][2] = (AE[2] >= 0) ? m_lp1 : m_lm1;\n    \n\tmat3ds s; s.zero();\n    \n    for (i=0; i<3; ++i) {\n        s += (A0[i]*E).sym()*(mu[i]);\n        for (j=0; j<3; ++j) {\n            s += A0[j]*(lam[i][j]*AE[i]);\n        }\n    }\n    s = (F*s*F.transpose()).sym()/J;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the elasticity tensor\ntens4ds FECubicCLE::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = m_mu;\n    mu[1] = m_mu;\n    mu[2] = m_mu;\n    lam[0][0] = m_lm1; lam[0][1] = m_l2 ; lam[0][2] = m_l2;\n    lam[1][0] = m_l2 ; lam[1][1] = m_lm1; lam[1][2] = m_l2;\n    lam[2][0] = m_l2 ; lam[2][1] = m_l2 ; lam[2][2] = m_lm1;\n    \n    int i,j;\n    vec3d a0;           // texture direction in reference configuration\n    mat3ds A[3];        // texture tensor in current configuration\n    double AE[3];       // E:A\n    mat3ds E = pt.Strain();\n\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    for (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n        // Copy the texture direction in the reference configuration to a0\n        a0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n        A[i] = dyad(F*a0);\n        AE[i] = a0*(E*a0);\n    }\n    \n    lam[0][0] = (AE[0] >= 0) ? m_lp1 : m_lm1;\n    lam[1][1] = (AE[1] >= 0) ? m_lp1 : m_lm1;\n    lam[2][2] = (AE[2] >= 0) ? m_lp1 : m_lm1;\n\n    tens4ds c;\n    c.zero();\n\n    mat3ds B = pt.LeftCauchyGreen();\n    for (i=0; i<3; ++i) {\n        c += dyad4s(A[i], B)*mu[i];\n        for (j=0; j<3; ++j) {\n            c += dyad1s(A[i],A[j])*lam[i][j]/2;\n        }\n    }\n    c /= J;\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FECubicCLE::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = m_mu;\n    mu[1] = m_mu;\n    mu[2] = m_mu;\n    lam[0][0] = m_lm1; lam[0][1] = m_l2 ; lam[0][2] = m_l2;\n    lam[1][0] = m_l2 ; lam[1][1] = m_lm1; lam[1][2] = m_l2;\n    lam[2][0] = m_l2 ; lam[2][1] = m_l2 ; lam[2][2] = m_lm1;\n    \n    int i,j;\n    vec3d a0;           // texture direction in reference configuration\n    mat3ds A0;          // texture tensor in reference configuration\n    double AE[3], AE2[3];\t// Ka\n    mat3ds E = pt.Strain();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    for (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n        // Copy the texture direction in the reference configuration to a0\n        a0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n        A0 = dyad(a0);\n        AE[i] = a0*(E*a0);\n        AE2[i] = a0*(E*E*a0);\n    }\n    \n    lam[0][0] = (AE[0] >= 0) ? m_lp1 : m_lm1;\n    lam[1][1] = (AE[1] >= 0) ? m_lp1 : m_lm1;\n    lam[2][2] = (AE[2] >= 0) ? m_lp1 : m_lm1;\n    \n    double W = 0;\n    \n    for (i=0; i<3; ++i) {\n        W += AE2[i]*mu[i];\n        for (j=0; j<3; ++j) {\n            W += AE[i]*AE[j]*lam[i][j]/2;\n        }\n    }\n    \n    return W;\n}\n"
  },
  {
    "path": "FEBioMech/FECubicCLE.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a cubic conewise linear elastic (CLE) material (Curnier et al. 1995 J Elasticity).\nclass FECubicCLE :\tpublic FEElasticMaterial\n{\npublic:\n    double\tm_lp1;      // diagonal first lamé constants (tension)\n    double\tm_lm1;      // diagonal first lamé constants (compression)\n    double\tm_l2;       // off-diagonal first lamé constants\n    double\tm_mu;       // shear moduli\n    \npublic:\n    FECubicCLE(FEModel* pfem) : FEElasticMaterial(pfem) {}\n    \n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! data initialization\n    bool Validate() override;\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageCDF.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageMaterialPoint.h\"\n#include <FECore/log.h>\n#include <FECore/gamma.h>\n#include <math.h>\n\n#ifndef M_PI\n#define M_PI 3.141592653589793238462643\n#endif\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDF, FEMaterialProperty)\n\tADD_PARAMETER(m_Dmax , FE_RANGE_CLOSED(0.0, 1.0), \"Dmax\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\ndouble FEDamageCDF::Damage(FEMaterialPoint& mp) {\n    \n    // get the damage material point data\n    FEDamageMaterialPoint& dp = *mp.ExtractData<FEDamageMaterialPoint>();\n    \n    // get the damage criterion (assuming dp.m_Etrial is up-to-date)\n    double Es = max(dp.m_Etrial, dp.m_Emax);\n\n    dp.m_D = cdf(mp,Es)*m_Dmax;\n    \n    return dp.m_D;\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFSimo, FEDamageCDF)\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER(0.0), \"a\");\n    ADD_PARAMETER(m_beta , FE_RANGE_CLOSED(0.0, 1.0), \"b\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFSimo::FEDamageCDFSimo(FEModel* pfem) : FEDamageCDF(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// Simo damage cumulative distribution function\n// Simo, CMAME 60 (1987), 153-173\n// Simo damage cumulative distribution function\ndouble FEDamageCDFSimo::cdf(FEMaterialPoint& mp, const double X)\n{\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n    if (alpha == 0) {\n        return 0;\n    }\n    \n    double cdf = 0;\n    \n    // this CDF only admits positive values\n    if (X >= 0) {\n        if (X > 1e-12) cdf = 1 - beta - (1.0 - beta)*(1.0 - exp(-X/alpha))*alpha/X;\n        else cdf = 0.5*(1.0 - beta)/alpha*X;\n    }\n    \n    return cdf;\n}\n\n// Simo damage probability density function\ndouble FEDamageCDFSimo::pdf(FEMaterialPoint& mp, const double X)\n{\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n    if (alpha == 0) {\n        return 0;\n    }\n    \n    double pdf = 0;\n    \n    // this CDF only admits positive values\n    if (X >= 0) {\n        if (X > 1e-12) pdf = (1.0 - beta)*(alpha - (alpha + X)*exp(-X/alpha))/(X*X);\n        else pdf = (1.0 - beta)/alpha*(0.5 - X/3/alpha);\n    }\n    \n    return pdf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFLogNormal, FEDamageCDF)\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER(0.0), \"mu\");\n    ADD_PARAMETER(m_sigma, FE_RANGE_GREATER(0.0), \"sigma\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFLogNormal::FEDamageCDFLogNormal(FEModel* pfem) : FEDamageCDF(pfem)\n{\n    m_mu = 1;\n    m_sigma = 1;\n    m_Dmax = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Lognormal damage cumulative distribution function\ndouble FEDamageCDFLogNormal::cdf(FEMaterialPoint& mp, const double X)\n{\n    double cdf = 0;\n    double mu = m_mu(mp);\n    double sigma = m_sigma(mp);\n    // this CDF only admits positive values\n    if (X >= 0)\n        cdf = 0.5*erfc(-log(X/mu)/sigma/sqrt(2.));\n    \n    return cdf;\n}\n\n// Lognormal damage probability density function\ndouble FEDamageCDFLogNormal::pdf(FEMaterialPoint& mp, const double X)\n{\n    double pdf = 0;\n    double mu = m_mu(mp);\n    double sigma = m_sigma(mp);\n\n    // this CDF only admits positive values\n    if (X > 1e-12) pdf = exp(-pow(log(X/mu)/sigma,2)/2)/(sqrt(2*M_PI)*X*sigma);\n    \n    return pdf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFWeibull, FEDamageCDF)\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\");\n    ADD_PARAMETER(m_ploc, \"ploc\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFWeibull::FEDamageCDFWeibull(FEModel* pfem) : FEDamageCDF(pfem)\n{\n    m_alpha = 1;\n    m_mu = 1;\n    m_ploc = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Weibull damage cumulative distribution function\ndouble FEDamageCDFWeibull::cdf(FEMaterialPoint& mp, const double X)\n{\n    double cdf = 0;\n    double alpha = m_alpha(mp);\n    double mu = m_mu(mp);\n    double ploc = m_ploc(mp);\n    \n    // this CDF only admits positive values\n    if (X > ploc)\n        cdf = 1 - exp(-pow((X-ploc)/mu,alpha));\n    \n    return cdf;\n}\n\n// Weibull damage probability density function\ndouble FEDamageCDFWeibull::pdf(FEMaterialPoint& mp, const double X)\n{\n    double pdf = 0;\n    double alpha = m_alpha(mp);\n    double mu = m_mu(mp);\n    double ploc = m_ploc(mp);\n\n    // this CDF only admits positive values\n    if ((alpha > 1) && (X > ploc))\n        pdf = exp(-pow((X-ploc)/mu,alpha))*alpha*pow(X-ploc, alpha-1)/pow(mu, alpha);\n    else if ((alpha == 1) && (X > ploc))\n        pdf = exp(-(X-ploc)/mu)/mu;\n    \n    return pdf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFStep, FEDamageCDF)\n    ADD_PARAMETER(m_mu  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFStep::FEDamageCDFStep(FEModel* pfem) : FEDamageCDF(pfem)\n{\n    m_mu = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Step cumulative distribution function (sudden fracture)\n// Step damage cumulative distribution function\ndouble FEDamageCDFStep::cdf(FEMaterialPoint& mp, const double X)\n{\n    double cdf = 0;\n    double mu = m_mu(mp);\n    \n    // this CDF only admits positive values\n    if (X > mu)\n        cdf = 1.0;\n    \n    return cdf;\n}\n\n// Step damage probability density function\ndouble FEDamageCDFStep::pdf(FEMaterialPoint& mp, const double X)\n{\n    double pdf = 0;\n    double mu = m_mu(mp);\n\n    // this PDF only admits positive values\n    if (X == mu) pdf = 1.0;\n    \n    return pdf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFPQP, FEDamageCDF)\n    ADD_PARAMETER(m_mumin, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mumin\");\n    ADD_PARAMETER(m_mumax, \"mumax\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFPQP::FEDamageCDFPQP(FEModel* pfem) : FEDamageCDF(pfem)\n{\n    m_mumin = 0;\n    m_mumax = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEDamageCDFPQP::Validate()\n{\n\treturn FEDamageCDF::Validate();\n}\n\n//-----------------------------------------------------------------------------\n// Piecewise S-shaped quintic polynomial damage cumulative distribution function\ndouble FEDamageCDFPQP::cdf(FEMaterialPoint& mp, const double X)\n{\n    double cdf = 0;\n    double mumin = m_mumin(mp);\n    double mumax = m_mumax(mp);\n\n    if (X <= mumin) cdf = 0;\n    else if (X >= mumax) cdf = 1;\n    else\n    {\n        double x = (X - mumin)/(mumax - mumin);\n        cdf = pow(x,3)*(10 - 15*x + 6*x*x);\n    }\n\n    return cdf;\n}\n\n// Piecewise S-shaped quintic polynomial damage probability density function\ndouble FEDamageCDFPQP::pdf(FEMaterialPoint& mp, const double X)\n{\n    double pdf = 0;\n    double mumin = m_mumin(mp);\n    double mumax = m_mumax(mp);\n\n    if (X <= mumin) pdf = 0;\n    else if (X >= mumax) pdf = 0;\n    else\n    {\n        double x = (X - mumin)/(mumax - mumin);\n        pdf = pow(x*(x-1),2)*30;\n    }\n\n    return pdf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFGamma, FEDamageCDF)\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER(0)           , \"alpha\");\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFGamma::FEDamageCDFGamma(FEModel* pfem) : FEDamageCDF(pfem)\n{\n    m_alpha = 2;\n    m_mu = 4;\n}\n\n//-----------------------------------------------------------------------------\n// Gamma damage cumulative distribution function\ndouble FEDamageCDFGamma::cdf(FEMaterialPoint& mp, const double X)\n{\n    double cdf = 0;\n    double alpha = m_alpha(mp);\n    double mu = m_mu(mp);\n    \n    // this CDF only admits positive values\n    double scale = gamma_inc_Q(alpha,0);\n    if (X > 0)\n        cdf = gamma_inc_P(alpha,X/mu)/scale;\n    \n    return cdf;\n}\n\n// Gamma damage probability density function\ndouble FEDamageCDFGamma::pdf(FEMaterialPoint& mp, const double X)\n{\n    double pdf = 0;\n    double alpha = m_alpha(mp);\n    double mu = m_mu(mp);\n\n    // this CDF only admits positive values\n    if (X > 0)\n        pdf = pow(X/mu, alpha-1)*exp(-X/mu)/mu*gammainv(alpha);\n    \n    return pdf;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageCDFUser, FEDamageCDF)\n    ADD_PROPERTY(m_cdf, \"cdf\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageCDFUser::FEDamageCDFUser(FEModel* pfem) : FEDamageCDF(pfem)\n{\n    m_cdf = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// User-defined loadcurve for damage cumulative distribution function\ndouble FEDamageCDFUser::cdf(FEMaterialPoint& mp, const double X)\n{\n    return m_cdf->value(X);\n}\n\n// Derivative of user-defined loadcurve damage probability density function\ndouble FEDamageCDFUser::pdf(FEMaterialPoint& mp, const double X)\n{\n    return m_cdf->derive(X);\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageCDF.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/FEFunction1D.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Virtual base class for damage cumulative distribution functions\n\nclass FEBIOMECH_API FEDamageCDF : public FEMaterialProperty\n{\npublic:\n    FEDamageCDF(FEModel* pfem) : FEMaterialProperty(pfem) { m_Dmax = 1; }\n    \n\t//! damage\n\tdouble Damage(FEMaterialPoint& pt);\n    \n    //! cumulative distribution function\n    virtual double cdf(FEMaterialPoint& mp, const double X) = 0;\n    \n    //! probability density function\n    virtual double pdf(FEMaterialPoint& mp, const double X) = 0;\n\npublic:\n    double  m_Dmax;              //!< maximum allowable damage\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n    FECORE_BASE_CLASS(FEDamageCDF)\n};\n\n//-----------------------------------------------------------------------------\n// Simo damage cumulative distribution function\n// Simo, CMAME 60 (1987), 153-173\n\nclass FEDamageCDFSimo : public FEDamageCDF\n{\npublic:\n\tFEDamageCDFSimo(FEModel* pfem);\n\t~FEDamageCDFSimo() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n\npublic:\n    FEParamDouble\tm_alpha;\t\t\t//!< parameter alpha\n    FEParamDouble\tm_beta;             //!< parameter beta\n    \n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Log-normal damage cumulative distribution function\n\nclass FEDamageCDFLogNormal : public FEDamageCDF\n{\npublic:\n\tFEDamageCDFLogNormal(FEModel* pfem);\n\t~FEDamageCDFLogNormal() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n\npublic:\n    FEParamDouble\tm_mu;               //!< mean on log scale\n    FEParamDouble\tm_sigma;            //!< standard deviation on log scale\n    \n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Weibull damage cumulative distribution function\n\nclass FEDamageCDFWeibull : public FEDamageCDF\n{\npublic:\n\tFEDamageCDFWeibull(FEModel* pfem);\n\t~FEDamageCDFWeibull() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n\npublic:\n    FEParamDouble\tm_alpha;            //!< exponent alpha\n    FEParamDouble\tm_mu;               //!< mean mu\n    FEParamDouble  m_ploc;             //!< location parameter\n    \n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Step cumulative distribution function (sudden fracture)\n\nclass FEDamageCDFStep : public FEDamageCDF\n{\npublic:\n\tFEDamageCDFStep(FEModel* pfem);\n\t~FEDamageCDFStep() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n\npublic:\n    FEParamDouble\tm_mu;               //!< threshold mu\n    \n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Piecewise S-shaped quintic polynomial cumulative distribution function\n\nclass FEDamageCDFPQP : public FEDamageCDF\n{\npublic:\n\tFEDamageCDFPQP(FEModel* pfem);\n\t~FEDamageCDFPQP() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n\n\tbool Validate() override;\n    \npublic:\n    FEParamDouble\tm_mumin;            //!< mu threshold\n    FEParamDouble\tm_mumax;            //!< mu cap\n    \n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Gamma damage cumulative distribution function\n\nclass FEDamageCDFGamma : public FEDamageCDF\n{\npublic:\n    FEDamageCDFGamma(FEModel* pfem);\n    ~FEDamageCDFGamma() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEParamDouble    m_alpha;            //!< exponent alpha\n    FEParamDouble    m_mu;               //!< pdf expected mean mu\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// User-specified load curve for damage cumulative distribution function\n\nclass FEDamageCDFUser : public FEDamageCDF\n{\npublic:\n    FEDamageCDFUser(FEModel* pfem);\n    ~FEDamageCDFUser() {}\n    \n    //! cumulative distribution function\n    double cdf(FEMaterialPoint& mp, const double X) override;\n    \n    //! probability density function\n    double pdf(FEMaterialPoint& mp, const double X) override;\n    \npublic:\n    FEFunction1D*    m_cdf;           //!< user-defined CDF\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageMaterial.h\"\n#include <algorithm>\n#include <limits>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n// Simo's damage criterion uses sqrt(2*strain energy density)\ndouble FEDamageCriterionSimo::DamageCriterion(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n\tFEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    double sed = m_pBase->StrainEnergyDensity(pt);\n    \n    // clean up round-off errors\n    if (sed < 0) sed = 0;\n    \n    return sqrt(2*sed);\n}\n\n//-----------------------------------------------------------------------------\n// Strain energy density damage criterion\ndouble FEDamageCriterionSED::DamageCriterion(FEMaterialPoint& pt)\n{\n\tFEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n\tFEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n\tdouble sed = m_pBase->StrainEnergyDensity(pt);\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n// Specific strain energy damage criterion\ndouble FEDamageCriterionSSE::DamageCriterion(FEMaterialPoint& pt)\n{\n\tFEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n\tFEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n\tdouble sed = m_pBase->StrainEnergyDensity(pt);\n    \n    return sed/ m_pBase->Density(pt);\n}\n\n//-----------------------------------------------------------------------------\n// von Mises stress damage criterion\ndouble FEDamageCriterionVMS::DamageCriterion(FEMaterialPoint& pt)\n{\n\tFEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n\tFEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n\tmat3ds s = m_pBase->Stress(pt);\n    \n    double vms = sqrt((SQR(s.xx()-s.yy()) + SQR(s.yy()-s.zz()) + SQR(s.zz()-s.xx())\n                       + 6*(SQR(s.xy()) + SQR(s.yz()) + SQR(s.xz())))/2);\n    \n    return vms;\n}\n\n\n//-----------------------------------------------------------------------------\n//! criterion tangent with respect to stress\nmat3ds FEDamageCriterionVMS::CriterionStressTangent(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    double vms = DamageCriterion(pt);\n    mat3ds dPhi = (vms > std::numeric_limits<double>::epsilon()) ? s.dev()*(1.5/vms) : mat3dd(1);\n    return dPhi;\n}\n\n//-----------------------------------------------------------------------------\n// Drucker yield criterion\nBEGIN_FECORE_CLASS(FEDamageCriterionDrucker, FEDamageCriterion)\n    ADD_PARAMETER(m_c, FE_RANGE_CLOSED(-27.0/8.0,9.0/4.0), \"c\");\nEND_FECORE_CLASS();\n\ndouble FEDamageCriterionDrucker::DamageCriterion(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    mat3ds sdev = s.dev();\n    double J2 = (sdev*sdev).trace()/2;\n    double J3 = sdev.det();\n    double c = m_c(pt);\n    double tau = pow(pow(J2,3) - c*pow(J3,2),1./6.);\n    \n    return tau;\n}\n\n//-----------------------------------------------------------------------------\n//! criterion tangent with respect to stress\nmat3ds FEDamageCriterionDrucker::CriterionStressTangent(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    mat3ds sdev = s.dev();\n    double J2 = (sdev*sdev).trace()/2;\n    double J3 = sdev.det();\n    double c = m_c(pt);\n    double tau = pow(pow(J2,3) - c*pow(J3,2),1./6.);\n    return (s.dev()*(pow(J2,2)/2) - (sdev.inverse()).dev()*(pow(J3, 2)*c/3))/pow(tau, 5);\n}\n\n//-----------------------------------------------------------------------------\n// max shear stress damage criterion\ndouble FEDamageCriterionMSS::DamageCriterion(FEMaterialPoint& pt)\n{\n    // evaluate stress tensor\n\tFEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n\tFEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n\tmat3ds s = m_pBase->Stress(pt);\n    \n    // evaluate principal normal stresses\n    double ps[3];\n    s.eigen2(ps);   // sorted in ascending order\n    \n    // evaluate max shear stresses\n    double mss = (ps[2] - ps[0])/2;\n    \n    return mss;\n}\n\n//-----------------------------------------------------------------------------\n//! criterion tangent with respect to stress\nmat3ds FEDamageCriterionMSS::CriterionStressTangent(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    // evaluate principal normal stresses\n    double ps[3];\n    vec3d v[3];\n    s.eigen2(ps,v); // sorted in ascending order\n    // clean up small differences\n    const double eps = 1e-6;\n    if (fabs(ps[2] - ps[0]) <= eps*fabs(ps[2])) ps[0] = ps[2];\n    if (fabs(ps[2] - ps[1]) <= eps*fabs(ps[2])) ps[1] = ps[2];\n    \n    // return tangent\n    mat3ds N;\n    if (ps[2] > ps[1]) {\n        if (ps[1] > ps[0])\n            N = (dyad(v[2])-dyad(v[0]))/2;\n        else\n            N = (dyad(v[2])-(dyad(v[1]) + dyad(v[0]))/2)/2;\n    }\n    else if (ps[1] > ps[0])\n        N = ((dyad(v[2]) + dyad(v[1]))/2 - dyad(v[0]))/2;\n    else N.zero();\n    \n    return N;\n}\n\n//-----------------------------------------------------------------------------\n// max normal stress damage criterion\ndouble FEDamageCriterionMNS::DamageCriterion(FEMaterialPoint& pt)\n{\n    // evaluate stress tensor\n\tFEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n\tFEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n\tmat3ds s = m_pBase->Stress(pt);\n    \n    // evaluate principal normal stresses\n    double ps[3];\n    s.eigen2(ps);   // sorted in ascending order\n    \n    return ps[2];\n}\n\n//-----------------------------------------------------------------------------\n//! criterion tangent with respect to stress\nmat3ds FEDamageCriterionMNS::CriterionStressTangent(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    // evaluate principal normal stresses\n    double ps[3];\n    vec3d v[3];\n    s.eigen2(ps,v); // sorted in ascending order\n    \n    // clean up small differences\n    const double eps = 1e-6;\n    if (fabs(ps[2] - ps[0]) <= eps*fabs(ps[2])) ps[0] = ps[2];\n    if (fabs(ps[2] - ps[1]) <= eps*fabs(ps[2])) ps[1] = ps[2];\n    \n    // return tangent\n    mat3ds N;\n    if (ps[2] > ps[1]) N = dyad(v[2]);\n    else if (ps[2] > ps[0]) N = dyad(v[2]) + dyad(v[1]);\n    else N = mat3dd(1);\n    \n    return N;\n}\n\n//-----------------------------------------------------------------------------\n// max normal Lagrange strain damage criterion\ndouble FEDamageCriterionMNLS::DamageCriterion(FEMaterialPoint& mp)\n{\n    // evaluate strain tensor\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3ds E = pt.Strain();\n    \n    // evaluate principal normal strains\n    double ps[3];\n    E.eigen2(ps);\n    \n    // evaluate max normal Lagrange strain\n    double mnls = max(max(ps[0],ps[1]),ps[2]);\n    \n    return mnls;\n}\n\n//-----------------------------------------------------------------------------\n// octahedral shear strain damage criterion\ndouble FEDamageCriterionOSS::DamageCriterion(FEMaterialPoint& mp)\n{\n    // evaluate strain tensor\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3ds E = pt.Strain();\n    \n    mat3ds devE = E.dev();\n    double oss = sqrt(devE.dotdot(devE)*(2./3.));\n\n    return oss;\n}\n\n//-----------------------------------------------------------------------------\n// octahedral natural shear strain damage criterion\ndouble FEDamageCriterionONS::DamageCriterion(FEMaterialPoint& mp)\n{\n    // evaluate strain tensor\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3ds h = pt.LeftHencky();\n    \n    mat3ds devh = h.dev();\n    double ons = sqrt(devh.dotdot(devh)*(2./3.));\n    \n    return ons;\n}\n\n//-----------------------------------------------------------------------------\n// Drucker-Prager yield criterion\nBEGIN_FECORE_CLASS(FEDamageCriterionDruckerPrager, FEDamageCriterion)\n    ADD_PARAMETER(m_b, \"b\");\nEND_FECORE_CLASS();\n\ndouble FEDamageCriterionDruckerPrager::DamageCriterion(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    double se = sqrt((SQR(s.xx()-s.yy()) + SQR(s.yy()-s.zz()) + SQR(s.zz()-s.xx())\n                       + 6*(SQR(s.xy()) + SQR(s.yz()) + SQR(s.xz())))/2);\n    double sm = s.tr()/3;\n    double b = m_b(pt);\n    double Phi = se - b*sm;\n    \n    return Phi;\n}\n\n//-----------------------------------------------------------------------------\n//! criterion tangent with respect to stress\nmat3ds FEDamageCriterionDruckerPrager::CriterionStressTangent(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    double se = sqrt((SQR(s.xx()-s.yy()) + SQR(s.yy()-s.zz()) + SQR(s.zz()-s.xx())\n                       + 6*(SQR(s.xy()) + SQR(s.yz()) + SQR(s.xz())))/2);\n    double sm = s.tr()/3;\n    double b = m_b(pt);\n    mat3dd I(1);\n    return s.dev()*(1.5/se) - I*b/(3*sqrt(3));\n}\n\n//-----------------------------------------------------------------------------\n// Deshpande-Fleck yield criterion\nBEGIN_FECORE_CLASS(FEDamageCriterionDeshpandeFleck, FEDamageCriterion)\n    ADD_PARAMETER(m_beta, \"beta\");\nEND_FECORE_CLASS();\n\ndouble FEDamageCriterionDeshpandeFleck::DamageCriterion(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    double se = sqrt((SQR(s.xx()-s.yy()) + SQR(s.yy()-s.zz()) + SQR(s.zz()-s.xx())\n                      + 6*(SQR(s.xy()) + SQR(s.yz()) + SQR(s.xz())))/2);\n    double sm = s.tr()/3;\n    double beta = m_beta(pt);\n    double Phi = sqrt((se*se+pow(3*beta*sm,2))/(1+beta*beta));\n\n    return Phi;\n}\n\n//-----------------------------------------------------------------------------\n//! Deshpande-Fleck criterion tangent with respect to stress\nmat3ds FEDamageCriterionDeshpandeFleck::CriterionStressTangent(FEMaterialPoint& pt)\n{\n    FEElasticMaterial* m_pMat = dynamic_cast<FEElasticMaterial*>(GetParent());\n    FEElasticMaterial* m_pBase = m_pMat->GetElasticMaterial();\n    mat3ds s = m_pBase->Stress(pt);\n    double se = sqrt((SQR(s.xx()-s.yy()) + SQR(s.yy()-s.zz()) + SQR(s.zz()-s.xx())\n                      + 6*(SQR(s.xy()) + SQR(s.yz()) + SQR(s.xz())))/2);\n    double sm = s.tr()/3;\n    double beta = m_beta(pt);\n    mat3dd I(1);\n    return (s.dev()/2+I*(beta*beta*sm))*(3/(sqrt(se*se+pow(3*beta*sm,2))*sqrt(1+beta*beta)));\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Virtual base class for damage criterion\n\nclass FEBIOMECH_API FEDamageCriterion : public FEMaterialProperty\n{\npublic:\n\tFEDamageCriterion(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    \n\t//! damage\n\tvirtual double DamageCriterion(FEMaterialPoint& pt) = 0;\n    \n    //! criterion tangent with respect to stress\n    virtual mat3ds CriterionStressTangent(FEMaterialPoint& pt) { return mat3ds(0); }\n\n    FECORE_BASE_CLASS(FEDamageCriterion);\n};\n\n//-----------------------------------------------------------------------------\n// Simo's damage criterion\n\nclass FEDamageCriterionSimo : public FEDamageCriterion\n{\npublic:\n\tFEDamageCriterionSimo(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n\t//! damage\n\tdouble DamageCriterion(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// Strain energy density as damage criterion\n\nclass FEDamageCriterionSED : public FEDamageCriterion\n{\npublic:\n\tFEDamageCriterionSED(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n\t//! damage\n\tdouble DamageCriterion(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// Specific strain energy as damage criterion\n\nclass FEDamageCriterionSSE : public FEDamageCriterion\n{\npublic:\n    FEDamageCriterionSSE(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n    //! damage\n    double DamageCriterion(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// von Mises stress as damage criterion\n\nclass FEDamageCriterionVMS : public FEDamageCriterion\n{\npublic:\n\tFEDamageCriterionVMS(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n\t//! damage\n\tdouble DamageCriterion(FEMaterialPoint& pt);\n    \n    //! criterion tangent with respect to stress\n    mat3ds CriterionStressTangent(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// Drucker yield criterion\n\nclass FEDamageCriterionDrucker : public FEDamageCriterion\n{\npublic:\n    FEDamageCriterionDrucker(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n    //! damage\n    double DamageCriterion(FEMaterialPoint& pt) override;\n    \n    //! criterion tangent with respect to stress\n    mat3ds CriterionStressTangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble   m_c;    //!< Drucker material parameter\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// max shear stress as damage criterion\n\nclass FEDamageCriterionMSS : public FEDamageCriterion\n{\npublic:\n\tFEDamageCriterionMSS(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n\t//! damage\n\tdouble DamageCriterion(FEMaterialPoint& pt);\n    \n    //! criterion tangent with respect to stress\n    mat3ds CriterionStressTangent(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// max normal stress as damage criterion\n\nclass FEDamageCriterionMNS : public FEDamageCriterion\n{\npublic:\n\tFEDamageCriterionMNS(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n\t//! damage\n\tdouble DamageCriterion(FEMaterialPoint& pt);\n    \n    //! criterion tangent with respect to stress\n    mat3ds CriterionStressTangent(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// max normal Lagrange strain as damage criterion\n\nclass FEDamageCriterionMNLS : public FEDamageCriterion\n{\npublic:\n\tFEDamageCriterionMNLS(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n\t//! damage\n\tdouble DamageCriterion(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// max octahedral strain as damage criterion\n\nclass FEDamageCriterionOSS : public FEDamageCriterion\n{\npublic:\n    FEDamageCriterionOSS(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n    //! damage\n    double DamageCriterion(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// max octahedral natural strain as damage criterion\n\nclass FEDamageCriterionONS : public FEDamageCriterion\n{\npublic:\n    FEDamageCriterionONS(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n    //! damage\n    double DamageCriterion(FEMaterialPoint& pt);\n};\n\n//-----------------------------------------------------------------------------\n// Drucker-Prager yield criterion\n\nclass FEDamageCriterionDruckerPrager : public FEDamageCriterion\n{\npublic:\n    FEDamageCriterionDruckerPrager(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n    //! damage\n    double DamageCriterion(FEMaterialPoint& pt) override;\n    \n    //! criterion tangent with respect to stress\n    mat3ds CriterionStressTangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble   m_b;    //!< Drucker-Prager material parameter\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Deshpande-Fleck yield criterion\n\nclass FEDamageCriterionDeshpandeFleck : public FEDamageCriterion\n{\npublic:\n    FEDamageCriterionDeshpandeFleck(FEModel* pfem) : FEDamageCriterion(pfem) {}\n    \n    //! damage\n    double DamageCriterion(FEMaterialPoint& pt) override;\n    \n    //! criterion tangent with respect to stress\n    mat3ds CriterionStressTangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble   m_beta;    //!< Deshpande-Fleck material parameter\n    \n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEDamageMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageMaterial.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEDamageMaterial, FEElasticMaterial)\n\t// set material properties\n\tADD_PROPERTY(m_pBase, \"elastic\");\n\tADD_PROPERTY(m_pDamg, \"damage\");\n\tADD_PROPERTY(m_pCrit, \"criterion\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageMaterial::FEDamageMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_pBase = 0;\n\tm_pDamg = 0;\n\tm_pCrit = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEDamageMaterial::CreateMaterialPointData()\n{\n\treturn new FEDamageMaterialPoint(m_pBase->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEDamageMaterial::Init()\n{\n    FEUncoupledMaterial* m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pBase);\n\tif (m_pMat != nullptr)\n\t{\n\t\tfeLogError(\"Elastic material should not be of type uncoupled\");\n\t\treturn false;\n\t}\n    \n\treturn FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEDamageMaterial::Stress(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the stress\n    mat3ds s = m_pBase->Stress(pt);\n    \n    // return damaged stress\n    return s*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEDamageMaterial::Tangent(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the tangent\n    tens4ds c = m_pBase->Tangent(pt);\n    \n    // return damaged tangent\n    return c*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEDamageMaterial::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pBase->StrainEnergyDensity(pt);\n\n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FEDamageMaterial::Damage(FEMaterialPoint& pt)\n{\n    // get the damage material point data\n    FEDamageMaterialPoint& pd = *pt.ExtractData<FEDamageMaterialPoint>();\n\n    // evaluate the trial value of the damage criterion\n    // this must be done before evaluating the damage\n    pd.m_Etrial = m_pCrit->DamageCriterion(pt);\n    \n    // evaluate and set the damage\n    double d = m_pDamg->Damage(pt);\n    pd.m_D = d;\n    \n    return d;\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEDamageMaterialPoint.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n\n//-----------------------------------------------------------------------------\n// This material models damage in any hyper-elastic materials.\n\nclass FEDamageMaterial : public FEElasticMaterial\n{\npublic:\n\tFEDamageMaterial(FEModel* pfem);\n    \npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n    \n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n    \n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n    \n\t//! data initialization and checking\n\tbool Init() override;\n    \n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n    \npublic:\n    FEElasticMaterial*  m_pBase;    // base elastic material\n\tFEDamageCDF*        m_pDamg;    // damage model\n\tFEDamageCriterion*  m_pCrit;    // damage criterion\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageMaterialPoint.h\"\n#include <FECore/DumpStream.h>\n\nFEMaterialPointData* FEDamageMaterialPoint::Copy()\n{\n    FEDamageMaterialPoint* pt = new FEDamageMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\nvoid FEDamageMaterialPoint::Init()\n{\n\tFEMaterialPointData::Init();\n\n\t// intialize data to zero\n\tm_Emax = 0;\n\tm_Etrial = 0;\n\tm_D = 0;\n}\n\nvoid FEDamageMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointData::Update(timeInfo);\n\n\tm_Emax = max(m_Emax, m_Etrial);\n}\n\nvoid FEDamageMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_Etrial & m_Emax & m_D;\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEReactiveMaterialPoint.h\"\n#include \"febiomech_api.h\"\n\n#ifdef WIN32\n#define max(a,b) ((a)>(b)?(a):(b))\n#endif\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores the damage variable.\nclass FEBIOMECH_API FEDamageMaterialPoint : public FEReactiveMaterialPoint\n{\npublic:\n    FEDamageMaterialPoint(FEMaterialPointData*pt) : FEReactiveMaterialPoint(pt) {}\n    \n\tFEMaterialPointData* Copy() override;\n    \n    void Init() override;\n    void Update(const FETimeInfo& timeInfo) override;\n    \n    void Serialize(DumpStream& ar) override;\n    \n    double BrokenBonds() const override { return m_D; }\n    double IntactBonds() const override { return 1 - m_D; }\n    \npublic:\n\tdouble\tm_Etrial;\t\t//!< trial damage criterion at time t\n\tdouble\tm_Emax;\t\t\t//!< max damage criterion up to time t\n\tdouble\tm_D;\t\t\t//!< damage (0 = no damage, 1 = complete damage)\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageMaterialUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageMaterialUC.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FECore/FECoreKernel.h\"\n\nBEGIN_FECORE_CLASS(FEDamageMaterialUC, FEUncoupledMaterial)\n\t// set material properties\n\tADD_PROPERTY(m_pBase, \"elastic\");\n\tADD_PROPERTY(m_pDamg, \"damage\");\n\tADD_PROPERTY(m_pCrit, \"criterion\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDamageMaterialUC::FEDamageMaterialUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_pBase = 0;\n\tm_pDamg = 0;\n\tm_pCrit = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEDamageMaterialUC::CreateMaterialPointData()\n{\n\treturn new FEDamageMaterialPoint(m_pBase->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEDamageMaterialUC::Init()\n{\n    return FEUncoupledMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEDamageMaterialUC::DevStress(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the stress\n    mat3ds s = m_pBase->DevStress(pt);\n    \n    // return the damaged stress\n    return s*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEDamageMaterialUC::DevTangent(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the tangent\n    tens4ds c = m_pBase->DevTangent(pt);\n    \n    // return the damaged tangent\n    return c*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEDamageMaterialUC::DevStrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pBase->DevStrainEnergyDensity(pt);\n    \n    // return the damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FEDamageMaterialUC::Damage(FEMaterialPoint& pt)\n{\n    // get the damage material point data\n    FEDamageMaterialPoint& pd = *pt.ExtractData<FEDamageMaterialPoint>();\n    \n    // evaluate the trial value of the damage criterion\n    // this must be done before evaluating the damage\n    pd.m_Etrial = m_pCrit->DamageCriterion(pt);\n    \n    // evaluate the damage\n    double d = m_pDamg->Damage(pt);\n    pd.m_D = d;\n\n    return d;\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageMaterialUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEDamageMaterialPoint.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n\n//-----------------------------------------------------------------------------\n// This material models damage in any hyper-elastic materials.\n\nclass FEDamageMaterialUC : public FEUncoupledMaterial\n{\npublic:\n\tFEDamageMaterialUC(FEModel* pfem);\n    \npublic:\n\t//! calculate stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n    \n\t//! calculate tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n\t//! calculate strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n    \n    //! data initialization and checking\n    bool Init() override;\n    \n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEUncoupledMaterial* GetElasticMaterial() override { return m_pBase; }\n    \npublic:\n    FEUncoupledMaterial*    m_pBase;    // base elastic material\n\tFEDamageCDF*            m_pDamg;    // damage model\n\tFEDamageCriterion*      m_pCrit;    // damage criterion\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageMooneyRivlin.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageMooneyRivlin, FEUncoupledMaterial)\n\tADD_PARAMETER(c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(c2, \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_beta, \"beta\");\n\tADD_PARAMETER(m_smin, \"smin\");\n\tADD_PARAMETER(m_smax, \"smax\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEDamageMooneyRivlin::FEDamageMooneyRivlin(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tc1 = 0;\n\tc2 = 0;\n\tm_beta = 0.1;\n\tm_smin = 0.0;\n\tm_smax = 0.5;\n}\n\n//-----------------------------------------------------------------------------\nbool FEDamageMooneyRivlin::Validate()\n{\n\tif (c1 + c2 <= 0) { feLogError(\"c1 + c2 must be a positive number.\"); return false; }\n\treturn FEUncoupledMaterial::Validate();\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEDamageMooneyRivlin::CreateMaterialPointData() \n{ \n\treturn new FEDamageMaterialPoint(new FEElasticMaterialPoint); \n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric stress\nmat3ds FEDamageMooneyRivlin::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n\t// Wi = dW/dIi\n\tdouble W1 = c1;\n\tdouble W2 = c2;\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\t// T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\t// calculate damage reduction factor\n\tdouble g = Damage(mp);\n\n\treturn T.dev()*(2.0*g/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FEDamageMooneyRivlin::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2;\n\tW1 = c1;\n\tW2 = c2;\n\t// ---\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2;\n\n\t// deviatoric cauchy-stress\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// Identity tensor\n\tmat3ds I(1,1,1,0,0,0);\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W2*I1) - B2*W2;\n\n\ttens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\t// calculate reduction factor at this point\n\tdouble g = Damage(mp);\n\n\treturn c*g;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEDamageMooneyRivlin::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n    \n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n    \n    double sed = c1*(I1-3) + c2*(I2-3);\n    \n\t// calculate damage reduction factor\n\tdouble g = Damage(mp);\n    \n    return sed*g;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate damage reduction factor \ndouble FEDamageMooneyRivlin::Damage(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate right Cauchy-Green tensor\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n\n\t// Invariants\n\tdouble I1 = C.tr();\n\tdouble I2 = 0.5*(I1*I1 - C2.tr());\n\n\t// strain-energy value\n\tdouble SEF = c1*(I1 - 3) + c2*(I2 - 3);\n\n\t// get the damage material point data\n\tFEDamageMaterialPoint& dp = *mp.ExtractData<FEDamageMaterialPoint>();\n\n\t// calculate trial-damage parameter\n\tdp.m_Etrial = sqrt(2.0*fabs(SEF));\n\n\t// calculate damage parameter\n\tdouble Es = max(dp.m_Etrial, dp.m_Emax);\n\n\t// calculate reduction parameter\n\tdouble g = 1.0;\n\tif (Es < m_smin) g = 1.0;\n\telse if (Es > m_smax) g = 0.0;\n\telse \n\t{\n\t\tdouble F = (Es - m_smin)/(m_smin - m_smax);\n\t\tg = 1.0 - (1.0 - m_beta + m_beta*F*F)*(F*F);\n\t}\n\n\tdp.m_D = 1-g;\n\treturn g;\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEDamageMaterialPoint.h\"\n\nclass FEDamageMooneyRivlin : public FEUncoupledMaterial\n{\npublic:\n\tFEDamageMooneyRivlin(FEModel* pfem);\n\npublic:\n\tdouble\tc1;\t//!< Mooney-Rivlin coefficient C1\n\tdouble\tc2;\t//!< Mooney-Rivlin coefficient C2\n\n\tdouble\tm_beta;\t\t//!< damage parameter beta\n\tdouble\tm_smin;\t\t//!< damage parameter psi-min\n\tdouble\tm_smax;\t\t//!< damage parameter psi-max\n\npublic:\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n\t//! data initialization\n\tbool Validate() override;\n\n\t// calculate damage reduction factor\n\tdouble Damage(FEMaterialPoint& pt);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageNeoHookean.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageNeoHookean, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"a\");\n\tADD_PARAMETER(m_beta , FE_RANGE_CLOSED(0.0, 1.0), \"b\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEDamageNeoHookean::FEDamageNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_E = 0;\n\tm_v = 0;\n\n\tm_alpha = 0.014;\n\tm_beta = 0.34;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEDamageNeoHookean::CreateMaterialPointData()\n{\n\treturn new FEDamageMaterialPoint(new FEElasticMaterialPoint);\n}\n\n//-----------------------------------------------------------------------------\n// Initialization routine and parameter checking\nbool FEDamageNeoHookean::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\t// calculate Lame parameters\n\tm_lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tm_mu  = 0.5*m_E/(1+m_v);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stress. This happens in two phases. First, we calculate \n//! the stress for the undamaged material. Second, we update the damage\n//! parameter and correct the stress accordingly.\nmat3ds FEDamageNeoHookean::Stress(FEMaterialPoint& mp)\n{\n\t// --- A. Calculate neo-Hookean stress ----\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\tdouble lndetF = log(detF);\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\n\t// Identity\n\tmat3dd I(1);\n\n\t// calculate stress\n\tmat3ds s = (b - I)*(m_mu*detFi) + I*(m_lam*lndetF*detFi);\n\n\t// --- B. Calculate the damage reduction factor ---\n\tdouble g = Damage(mp);\n\n\treturn s*g;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate damage reduction factor \ndouble FEDamageNeoHookean::Damage(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate right Cauchy-Green tensor\n\tmat3ds C = pt.RightCauchyGreen();\n\n\t// Invariants\n\tdouble I1 = C.tr();\n\tdouble J = pt.m_J;\n\n\t// strain-energy value\n\tdouble lnJ = log(J);\n\tdouble SEF = 0.5*m_mu*(I1 - 3) - m_mu*lnJ + 0.5*m_lam*(lnJ*lnJ);\n\n\t// get the damage material point data\n\tFEDamageMaterialPoint& dp = *mp.ExtractData<FEDamageMaterialPoint>();\n\n\t// calculate trial-damage parameter\n\tdp.m_Etrial = sqrt(2.0*fabs(SEF));\n\n\t// calculate damage parameter\n\tdouble Es = max(dp.m_Etrial, dp.m_Emax);\n\n\t// calculate reduction parameter\n\tdouble g = 1.0;\n\tif (fabs(Es) > 1e-12) g = m_beta + (1.0 - m_beta)*(1.0 - exp(-Es/m_alpha))/(Es/m_alpha);\n\telse g = 1.0 - 0.5*(1.0 - m_beta)/m_alpha*Es;\n\n\tdp.m_D = 1-g;\n\treturn g;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate tangent. I'm not sure if the tangent needs to be modified for the \n// damage model For now, I don't modify it.\ntens4ds FEDamageNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tdouble detF = pt.m_J;\n\n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\n\tdouble lam1 = lam / detF;\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\n\t\n\tdouble D[6][6] = {0};\n\tD[0][0] = lam1+2.*mu1; D[0][1] = lam1       ; D[0][2] = lam1       ;\n\tD[1][0] = lam1       ; D[1][1] = lam1+2.*mu1; D[1][2] = lam1       ;\n\tD[2][0] = lam1       ; D[2][1] = lam1       ; D[2][2] = lam1+2.*mu1;\n\tD[3][3] = mu1;\n\tD[4][4] = mu1;\n\tD[5][5] = mu1;\n\n\tdouble g = Damage(mp);\n\n\treturn tens4ds(D)*g;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEDamageNeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// --- A. Calculate neo-Hookean strain energy density ----\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n    \n    double I1 = b.tr();\n    \n    double sed = m_mu*(0.5*(I1-3) - lnJ) + 0.5*m_lam*lnJ*lnJ;\n    \n\t// --- B. Calculate the damage reduction factor ---\n\tdouble g = Damage(mp);\n    \n    return sed*g;\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEDamageMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n// This material is a first attempt to include damage in hyper-elastic materials.\n// It assumes the simple damage model as defined in Simo, CMAME 60 (1987), 153-173\n\n//-----------------------------------------------------------------------------\nclass FEDamageNeoHookean : public FEElasticMaterial\n{\npublic:\n\tFEDamageNeoHookean(FEModel* pfem);\n\npublic:\n\tdouble\tm_E;\t//!< Young's modulus\n\tdouble\tm_v;\t//!< Poisson's ratio\n\n\tdouble\tm_alpha;\t//!< damage parameter alpha\n\tdouble\tm_beta;\t\t//!< damage parameter beta\n\nprotected:\n\tdouble\tm_lam;\n\tdouble\tm_mu;\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization and checking\n\tbool Init() override;\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// calculate damage reduction factor\n\tdouble Damage(FEMaterialPoint& pt);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDamageTransIsoMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageTransIsoMooneyRivlin.h\"\n\nFETIMRDamageMaterialPoint::FETIMRDamageMaterialPoint(FEMaterialPointData*pt) : FEMaterialPointData(pt) {}\n\nFEMaterialPointData* FETIMRDamageMaterialPoint::Copy()\n{\n\tFETIMRDamageMaterialPoint* pt = new FETIMRDamageMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\nvoid FETIMRDamageMaterialPoint::Init()\n{\n\tFEMaterialPointData::Init();\n\n\t// intialize data to zero\n\tm_MEmax = 0;\n\tm_MEtrial = 0;\n\tm_Dm = 0;\n\n\tm_FEmax = 0;\n\tm_FEtrial = 0;\n\tm_Df = 0;\n}\n\nvoid FETIMRDamageMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointData::Update(timeInfo);\n\n\tm_MEmax = max(m_MEmax, m_MEtrial);\n\tm_FEmax = max(m_FEmax, m_FEtrial);\n}\n\nvoid FETIMRDamageMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_MEtrial & m_MEmax & m_Dm;\n\tar & m_FEtrial & m_FEmax & m_Df;\n}\n\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDamageTransIsoMooneyRivlin, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c3, \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c4, FE_RANGE_GREATER(0.0), \"c4\");\n\tADD_PARAMETER(m_Mbeta, \"Mbeta\");\n\tADD_PARAMETER(m_Msmin, \"Msmin\");\n\tADD_PARAMETER(m_Msmax, \"Msmax\");\n\tADD_PARAMETER(m_Fbeta, \"Fbeta\");\n\tADD_PARAMETER(m_Fsmin, \"Fsmin\");\n\tADD_PARAMETER(m_Fsmax, \"Fsmax\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEDamageTransIsoMooneyRivlin::FEDamageTransIsoMooneyRivlin(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEDamageTransIsoMooneyRivlin::CreateMaterialPointData() \n{ \n\treturn new FETIMRDamageMaterialPoint(new FEElasticMaterialPoint); \n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEDamageTransIsoMooneyRivlin::DevStress(FEMaterialPoint& mp)\n{\n\t// matrix stress\n\tmat3ds sm = MatrixStress(mp);\n\n\t// matrix reduction factor\n\tdouble gm = MatrixDamage(mp);\n\n\t// fiber stress\n\tmat3ds sf = FiberStress(mp);\n\n\t// fiber reduction factor\n\tdouble gf = FiberDamage(mp);\n\n\treturn sm*gm + sf*gf;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric stress\nmat3ds FEDamageTransIsoMooneyRivlin::MatrixStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\n\t// --- TODO: put strain energy derivatives here ---\n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n\t// Wi = dW/dIi\n\tdouble W1 = m_c1;\n\tdouble W2 = m_c2;\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\t// T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the fiber stress\nmat3ds FEDamageTransIsoMooneyRivlin::FiberStress(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam*Jm13; // i.e. lambda tilde\n\n\t// invariant I4\n\tdouble I4 = lamd*lamd;\n\n\t// strain energy derivative\n\tdouble W4 = (I4 - 1)*m_c3*exp(m_c4*(I4-1)*(I4-1));\n\n\t// calculate dyad of a: AxA = (a x a)\n\tmat3ds AxA = dyad(a);\n\n\t// calculate FdWf/dCFt = I4*W4*(a x a)\n\tmat3ds T = AxA*(W4*I4);\n\n\t// return stress\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEDamageTransIsoMooneyRivlin::DevTangent(FEMaterialPoint& mp)\n{\n\t// matrix tangent\n\ttens4ds Cm = MatrixTangent(mp);\n\n\t// matrix damage\n\tdouble gm = MatrixDamage(mp);\n\n\t// fiber tangent\n\ttens4ds Cf = FiberTangent(mp);\n\n\t// fiber damage\n\tdouble gf = FiberDamage(mp);\n\n\treturn Cm*gm + Cf*gf;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FEDamageTransIsoMooneyRivlin::MatrixTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2;\n\tW1 = m_c1;\n\tW2 = m_c2;\n\t// ---\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2;\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// Identity tensor\n\tmat3ds I(1,1,1,0,0,0);\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W2*I1) - B2*W2;\n\n\ttens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\treturn c;\n}\n\n\n//-----------------------------------------------------------------------------\n// Fiber material tangent\n//\ntens4ds FEDamageTransIsoMooneyRivlin::FiberTangent(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\tdouble Ji = 1.0/J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get initial local material axis\n\tvec3d a0 = Q.col(0);\n\n\t// calculate current local material axis\n\tvec3d a = F*a0;\n\n\tdouble lam = a.unit();\n\n\t// deviatoric stretch\n\tdouble lamd = lam*Jm13;\n\n\tdouble I4 = lamd*lamd;\n\n\t// strain energy derivative\n\tdouble W4 = (I4 - 1)*m_c3*exp(m_c4*(I4-1)*(I4-1));\n\tdouble W44 = m_c3*exp(m_c4*(I4 - 1)*(I4 - 1)) + 2*m_c3*m_c4*(I4 - 1)*(I4 - 1)*exp(m_c4*(I4 - 1)*(I4 - 1));\n\n\t// calculate dWdC:C\n\tdouble WC = W4*I4;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = W44*I4*I4;\n\n\tmat3dd I(1);\t// Identity\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds Id4  = dyad4s(I);\n\n\tmat3ds AxA = dyad(a);\n\ttens4ds AxAxAxA = dyad1s(AxA);\n\n\ttens4ds cw = AxAxAxA*(4.0*Ji*W44*I4*I4) - dyad1s(I, AxA)*(4.0/3.0*Ji*W44*I4*I4);\n\n\ttens4ds c = (Id4 - IxI/3.0)*(4.0/3.0*Ji*WC) + IxI*(4.0/9.0*Ji*CWWC) + cw;\n\n\t// see if we need to add the stress\n\tFETIMRDamageMaterialPoint& dp = *mp.ExtractData<FETIMRDamageMaterialPoint>();\n\tif (dp.m_FEtrial > dp.m_FEmax)\n\t{\n\t\tmat3ds devs = pt.m_s.dev();\n\t\tdouble dg = FiberDamageDerive(mp);\n\t\tc += dyad1s(devs)*(J*dg/dp.m_FEtrial);\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDamageTransIsoMooneyRivlin::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// matrix sed\n\tdouble sedm = MatrixStrainEnergyDensity(mp);\n    \n\t// matrix reduction factor\n\tdouble gm = MatrixDamage(mp);\n    \n\t// fiber sed\n\tdouble sedf = FiberStrainEnergyDensity(mp);\n    \n\t// fiber reduction factor\n\tdouble gf = FiberDamage(mp);\n    \n\treturn sedm*gm + sedf*gf;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the matrix strain energy density\ndouble FEDamageTransIsoMooneyRivlin::MatrixStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n    \n\t// --- TODO: put strain energy derivatives here ---\n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n    double sed = m_c1*(I1 - 3) + m_c2*(I2 - 3);\n    \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the fiber strain energy density\ndouble FEDamageTransIsoMooneyRivlin::FiberStrainEnergyDensity(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n    \n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n    \n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam*Jm13; // i.e. lambda tilde\n    \n\t// invariant I4\n\tdouble I4 = lamd*lamd;\n    \n\t// strain energy derivative\n\tdouble sed = 0.5*m_c3/m_c4*(exp(m_c4*(I4-1)*(I4-1))-1);\n    \n\t// return sed\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate damage reduction factor for matrix\ndouble FEDamageTransIsoMooneyRivlin::MatrixDamage(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate right Cauchy-Green tensor\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n\n\t// Invariants\n\tdouble I1 = C.tr();\n\tdouble I2 = 0.5*(I1*I1 - C2.tr());\n\n\t// strain-energy value\n\tdouble SEF = m_c1*(I1 - 3) + m_c2*(I2 - 3);\n\n\t// get the damage material point data\n\tFETIMRDamageMaterialPoint& dp = *mp.ExtractData<FETIMRDamageMaterialPoint>();\n\n\t// calculate trial-damage parameter\n\tdp.m_MEtrial = sqrt(2.0*fabs(SEF));\n\n\t// calculate damage parameter\n\tdouble Es = max(dp.m_MEtrial, dp.m_MEmax);\n\n\t// calculate reduction parameter\n\tdouble g = 1.0;\n\tif (Es < m_Msmin) g = 1.0;\n\telse if (Es > m_Msmax) g = 0.0;\n\telse \n\t{\n\t\tdouble F = (Es - m_Msmin)/(m_Msmin - m_Msmax);\n\t\tg = 1.0 - (1.0 - m_Mbeta + m_Mbeta*F*F)*(F*F);\n\t}\n\n\tdp.m_Dm = 1-g;\n\treturn g;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate damage reduction factor for matrix\ndouble FEDamageTransIsoMooneyRivlin::MatrixDamageDerive(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate right Cauchy-Green tensor\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n\n\t// Invariants\n\tdouble I1 = C.tr();\n\tdouble I2 = 0.5*(I1*I1 - C2.tr());\n\n\t// strain-energy value\n\tdouble SEF = m_c1*(I1 - 3) + m_c2*(I2 - 3);\n\n\t// get the damage material point data\n\tFETIMRDamageMaterialPoint& dp = *mp.ExtractData<FETIMRDamageMaterialPoint>();\n\n\t// calculate trial-damage parameter\n\tdp.m_MEtrial = sqrt(2.0*fabs(SEF));\n\n\t// calculate damage parameter\n\tdouble Es = max(dp.m_MEtrial, dp.m_MEmax);\n\n\t// calculate reduction parameter\n\tdouble dg = 0.0;\n\tif (Es < m_Msmin) dg = 0.0;\n\telse if (Es > m_Msmax) dg = 0.0;\n\telse \n\t{\n\t\tdouble h = m_Msmax - m_Msmin;\n\t\tdouble F = (Es - m_Msmin)/h;\n\t\tdg = -2.0*F/h*(1 - m_Mbeta + m_Mbeta*F*F)-(F*F)*(2.0*m_Mbeta*F/h);\n\t}\n\n\treturn dg;\n}\n\n\n//-----------------------------------------------------------------------------\n// Calculate damage reduction factor for fibers\ndouble FEDamageTransIsoMooneyRivlin::FiberDamage(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam*Jm13; // i.e. lambda tilde\n\n\t// invariant I4\n\tdouble I4 = lamd*lamd;\n\n\t// strain energy value\n\tdouble SEF = 0.5*m_c3/m_c4*(exp(m_c4*(I4-1)*(I4-1))-1);\n\n\t// get the damage material point data\n\tFETIMRDamageMaterialPoint& dp = *mp.ExtractData<FETIMRDamageMaterialPoint>();\n\n\t// calculate trial-damage parameter\n\tdp.m_FEtrial = sqrt(2.0*fabs(SEF));\n\n\t// calculate damage parameter\n\tdouble Es = max(dp.m_FEtrial, dp.m_FEmax);\n\n\t// calculate reduction parameter\n\tdouble g = 1.0;\n\tif (Es < m_Fsmin) g = 1.0;\n\telse if (Es > m_Fsmax) g = 0.0;\n\telse \n\t{\n\t\tdouble F = (Es - m_Fsmin)/(m_Fsmin - m_Fsmax);\n\t\tg = 1.0 - (1.0 - m_Fbeta + m_Fbeta*F*F)*(F*F);\n\t}\n\n\tdp.m_Df = 1-g;\n\treturn g;\n}\n\n\n//-----------------------------------------------------------------------------\n// Calculate damage reduction factor for fibers\ndouble FEDamageTransIsoMooneyRivlin::FiberDamageDerive(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam*Jm13; // i.e. lambda tilde\n\n\t// invariant I4\n\tdouble I4 = lamd*lamd;\n\n\t// strain energy value\n\tdouble SEF = 0.5*m_c3/m_c4*(exp(m_c4*(I4-1)*(I4-1))-1);\n\n\t// get the damage material point data\n\tFETIMRDamageMaterialPoint& dp = *mp.ExtractData<FETIMRDamageMaterialPoint>();\n\n\t// calculate trial-damage parameter\n\tdp.m_FEtrial = sqrt(2.0*fabs(SEF));\n\n\t// calculate damage parameter\n\tdouble Es = max(dp.m_FEtrial, dp.m_FEmax);\n\n\t// calculate reduction parameter\n\tdouble dg = 0.0;\n\tif (Es < m_Fsmin) dg = 0.0;\n\telse if (Es > m_Fsmax) dg = 0.0;\n\telse \n\t{\n\t\tdouble h = m_Fsmin - m_Fsmax;\n\t\tdouble F = (Es - m_Fsmin)/h;\n\t\tdg = -2.0*F/h*(1 - m_Fbeta + m_Fbeta*F*F)-(F*F)*(2.0*m_Fbeta*F/h);\n\t}\n\n\treturn dg;\n}\n\n//-----------------------------------------------------------------------------\n//! damage\ndouble FEDamageTransIsoMooneyRivlin::Damage(FEMaterialPoint& pt)\n{\n    return MatrixDamage(pt) + FiberDamage(pt);\n}\n"
  },
  {
    "path": "FEBioMech/FEDamageTransIsoMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n#ifdef WIN32\n#define max(a,b) ((a)>(b)?(a):(b))\n#endif\n\n//-----------------------------------------------------------------------------\n// We first define a material point that stores the damage variable.\nclass FETIMRDamageMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFETIMRDamageMaterialPoint(FEMaterialPointData*pt);\n\n\tFEMaterialPointData* Copy();\n\n\tvoid Init();\n\tvoid Update(const FETimeInfo& timeInfo);\n\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\t// matrix\n\tdouble\tm_MEtrial;\t\t\t//!< trial strain at time t\n\tdouble\tm_MEmax;\t\t\t//!< max strain variable up to time t\n\tdouble\tm_Dm;\t\t\t\t//!< damage\n\n\t// fiber\n\tdouble\tm_FEtrial;\t\t\t//!< trial strain at time t\n\tdouble\tm_FEmax;\t\t\t//!< max strain variable up to time t\n\tdouble\tm_Df;\t\t\t\t//!< damage\n};\n\n//-----------------------------------------------------------------------------\nclass FEDamageTransIsoMooneyRivlin : public FEUncoupledMaterial\n{\npublic:\n\tFEDamageTransIsoMooneyRivlin(FEModel* pfem);\n\npublic:\n\t// Mooney-Rivlin parameters\n\tdouble\tm_c1;\t//!< Mooney-Rivlin coefficient C1\n\tdouble\tm_c2;\t//!< Mooney-Rivlin coefficient C2\n\n\t// fiber parameters\n\tdouble\tm_c3;\n\tdouble\tm_c4;\n\n\t// Matrix damage parameters\n\tdouble\tm_Mbeta;\t\t//!< damage parameter beta\n\tdouble\tm_Msmin;\t\t//!< damage parameter psi-min\n\tdouble\tm_Msmax;\t\t//!< damage parameter psi-max\n\n\t// Fiber damage parameters\n\tdouble\tm_Fbeta;\n\tdouble\tm_Fsmin;\n\tdouble\tm_Fsmax;\n\npublic:\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n    \nprotected:\n\tmat3ds MatrixStress(FEMaterialPoint& mp);\n\tmat3ds FiberStress (FEMaterialPoint& mp);\n\ttens4ds MatrixTangent(FEMaterialPoint& pt);\n\ttens4ds FiberTangent (FEMaterialPoint& pt);\n\tdouble MatrixStrainEnergyDensity(FEMaterialPoint& pt);\n\tdouble FiberStrainEnergyDensity (FEMaterialPoint& pt);\n\nprotected:\n\t// calculate damage reduction factor for matrix\n\tdouble MatrixDamage(FEMaterialPoint& pt);\n\n\t// calculate damage reduction factor for fibers\n\tdouble FiberDamage(FEMaterialPoint& pt);\n\n\tdouble MatrixDamageDerive(FEMaterialPoint& pt);\n\tdouble FiberDamageDerive(FEMaterialPoint& pt);\n\npublic:\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDeformableSpringDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDeformableSpringDomain.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBioMech.h\"\n\nBEGIN_FECORE_CLASS(FEDeformableSpringDomain, FEDiscreteDomain)\n\tADD_PARAMETER(m_kbend, \"k_bend\");\n\tADD_PARAMETER(m_kstab, \"k_stab\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEDeformableSpringDomain::FEDeformableSpringDomain(FEModel* pfem) : FEDiscreteDomain(pfem), FEElasticDomain(pfem), m_dofU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n\tm_pMat  =   0;\n\tm_kbend = 0.0;\n\tm_kstab = 0.0;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dofs\nconst FEDofList& FEDeformableSpringDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FESpringMaterial*>(pmat);\n\tassert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\t// we connect each node to its two neighbors\n\tint NN = Nodes();\n\tfor (int i=1; i<NN-1; ++i)\n\t{\n\t\tvector<int> lm(3*6, -1);\n\t\tfor (int j=0; j<3; ++j)\n\t\t{\n\t\t\tint n = i-1+j;\n\t\t\tvector<int>& id = Node(n).m_ID;\n\n\t\t\t// first the displacement dofs\n\t\t\tlm[6 * j    ] = id[m_dofU[0]];\n\t\t\tlm[6 * j + 1] = id[m_dofU[1]];\n\t\t\tlm[6 * j + 2] = id[m_dofU[2]];\n\n\t\t\t// rigid rotational dofs\n\t\t\tlm[6 * j + 3] = id[m_dofR[0]];\n\t\t\tlm[6 * j + 4] = id[m_dofR[1]];\n\t\t\tlm[6 * j + 5] = id[m_dofR[2]];\n\t\t}\n\n\t\tK.build_add(lm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain::UnpackLM(FEElement &el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N * 6);\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = m_pMesh->Node(el.m_node[i]);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3 * i    ] = id[m_dofU[0]];\n\t\tlm[3 * i + 1] = id[m_dofU[1]];\n\t\tlm[3 * i + 2] = id[m_dofU[2]];\n\n\t\t// rigid rotational dofs\n\t\tlm[3 * N + 3 * i    ] = id[m_dofR[0]];\n\t\tlm[3 * N + 3 * i + 1] = id[m_dofR[1]];\n\t\tlm[3 * N + 3 * i + 2] = id[m_dofR[2]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain::Activate()\n{\n\tfor (int i = 0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// calculate the intitial spring length\n\tm_L0 = InitialLength();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDeformableSpringDomain::InitialLength()\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tdouble L = 0.0;\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// get the nodes\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r1 = n1.m_r0;\n\t\tvec3d& r2 = n2.m_r0;\n\n\t\tL += (r2 - r1).norm();\n\t}\n\treturn L;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDeformableSpringDomain::CurrentLength()\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tdouble L = 0.0;\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// get the nodes\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r1 = n1.m_rt;\n\t\tvec3d& r2 = n2.m_rt;\n\n\t\tL += (r2 - r1).norm();\n\t}\n\treturn L;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the forces due to discrete elements (i.e. springs)\n\nvoid FEDeformableSpringDomain::InternalForces(FEGlobalVector& R)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tvector<double> fe(6);\n\tvec3d u1, u2;\n\n\tvector<int> en(2), lm(6);\n\n\t// calculate current length\n\tdouble L = CurrentLength();\n\tdouble DL = L - m_L0;\n\n\t// calculate force\n\tdouble F = m_pMat->force(DL);\n\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// get the nodes\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r01 = n1.m_r0;\n\t\tvec3d& r02 = n2.m_r0;\n\t\tvec3d& rt1 = n1.m_rt;\n\t\tvec3d& rt2 = n2.m_rt;\n\n\t\tvec3d e = rt2 - rt1; e.unit();\n\n\t\t// calculate spring lengths\n\t\tdouble L0 = (r02 - r01).norm();\n\t\tdouble Lt = (rt2 - rt1).norm();\n\t\tdouble DL = Lt - L0;\n\n\t\t// set up the force vector\n\t\tfe[0] = F*e.x;\n\t\tfe[1] = F*e.y;\n\t\tfe[2] = F*e.z;\n\t\tfe[3] = -F*e.x;\n\t\tfe[4] = -F*e.y;\n\t\tfe[5] = -F*e.z;\n\n\t\t// setup the node vector\n\t\ten[0] = el.m_node[0];\n\t\ten[1] = el.m_node[1];\n\n\t\t// set up the LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble element\n\t\tR.Assemble(en, lm, fe);\n\t}\n\n\tif (m_kbend > 0)\n\t{\n/*\t\tdouble eps = m_kbend;\n\t\tlm.resize(3);\n\t\ten.resize(1);\n\t\tfe.resize(3);\n\t\tint NN = Nodes();\n\t\tfor (int i = 1; i<NN - 1; ++i)\n\t\t{\n\t\t\tint i0 = i - 1;\n\t\t\tint i1 = i + 1;\n\n\t\t\tvec3d xi = Node(i).m_rt;\n\t\t\tvec3d x0 = Node(i0).m_rt;\n\t\t\tvec3d x1 = Node(i1).m_rt;\n\n\t\t\tvec3d r = xi - x0;\n\t\t\tvec3d s = x1 - x0; s.unit();\n\t\t\tvec3d d = r - s*(r*s);\n\n\t\t\tfe[0] = -eps*d.x;\n\t\t\tfe[1] = -eps*d.y;\n\t\t\tfe[2] = -eps*d.z;\n\n\t\t\ten[0] = m_Node[i];\n\t\t\tlm[0] = Node(i).m_ID[m_dofX];\n\t\t\tlm[1] = Node(i).m_ID[m_dofY];\n\t\t\tlm[2] = Node(i).m_ID[m_dofZ];\n\t\t\tR.Assemble(en, lm, fe);\n\t\t}\n*/\n\t\tdouble eps = m_kbend;\n\t\tlm.resize(3);\n\t\ten.resize(1);\n\t\tfe.resize(3);\n\t\tint NN = Nodes();\n\t\tfor (int i = 1; i<NN - 1; ++i)\n\t\t{\n\t\t\tint i0 = i - 1;\n\t\t\tint i1 = i + 1;\n\n\t\t\tvec3d xi = Node(i).m_rt;\n\t\t\tvec3d x0 = Node(i0).m_rt;\n\t\t\tvec3d x1 = Node(i1).m_rt;\n\n\t\t\tvec3d d = xi - (x0 + x1)*0.5;\n\n\t\t\tfe[0] = -eps*d.x;\n\t\t\tfe[1] = -eps*d.y;\n\t\t\tfe[2] = -eps*d.z;\n\n\t\t\ten[0] = m_Node[i];\n\t\t\tlm[0] = Node(i).m_ID[m_dofU[0]];\n\t\t\tlm[1] = Node(i).m_ID[m_dofU[1]];\n\t\t\tlm[2] = Node(i).m_ID[m_dofU[2]];\n\t\t\tR.Assemble(en, lm, fe);\n\t\t}\n\n\t}\n\n\tif (m_kstab > 0)\n\t{\n\t\tdouble eps = m_kstab;\n\t\tlm.resize(6);\n\t\ten.resize(2);\n\t\tfe.resize(6);\n\t\tint NE = Elements();\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFEDiscreteElement& el = Element(i);\n\t\t\ten[0] = el.m_node[0];\n\t\t\ten[1] = el.m_node[1];\n\n\t\t\t// get the nodes\n\t\t\tFENode& n0 = mesh.Node(en[0]);\n\t\t\tFENode& n1 = mesh.Node(en[1]);\n\n\t\t\tlm[0] = n0.m_ID[m_dofU[0]];\n\t\t\tlm[1] = n0.m_ID[m_dofU[1]];\n\t\t\tlm[2] = n0.m_ID[m_dofU[2]];\n\n\t\t\tlm[3] = n1.m_ID[m_dofU[0]];\n\t\t\tlm[4] = n1.m_ID[m_dofU[1]];\n\t\t\tlm[5] = n1.m_ID[m_dofU[2]];\n\n\t\t\tvec3d ei = n1.m_rt - n0.m_rt;\n\n\t\t\tfe[0] =  eps*ei.x;\n\t\t\tfe[1] =  eps*ei.y;\n\t\t\tfe[2] =  eps*ei.z;\n\t\t\tfe[3] = -eps*ei.x;\n\t\t\tfe[4] = -eps*ei.y;\n\t\t\tfe[5] = -eps*ei.z;\n\n\t\t\tR.Assemble(en, lm, fe);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the discrete element stiffness\n\nvoid FEDeformableSpringDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\t// calculate current length\n\tdouble L = CurrentLength();\n\tdouble DL = L - m_L0;\n\n\t// evaluate the stiffness\n\tdouble F = m_pMat->force(DL);\n\tdouble E = m_pMat->stiffness(DL);\n\n\tFEElementMatrix ke;\n\tke.resize(6, 6);\n\tke.zero();\n\tvector<int> en(2), lm(6);\n\n\t// loop over all discrete elements\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// get the nodes of the element\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r01 = n1.m_r0;\n\t\tvec3d& r02 = n2.m_r0;\n\t\tvec3d& rt1 = n1.m_rt;\n\t\tvec3d& rt2 = n2.m_rt;\n\n\t\tvec3d e = rt2 - rt1; e.unit();\n\n\t\t// calculate nodal displacements\n\t\tvec3d u1 = rt1 - r01;\n\t\tvec3d u2 = rt2 - r02;\n\n\t\t// calculate spring lengths\n\t\tdouble L0 = (r02 - r01).norm();\n\t\tdouble Lt = (rt2 - rt1).norm();\n\t\tdouble DL = Lt - L0;\n\n\n\t\tif (Lt == 0) { F = 0; Lt = 1; e = vec3d(1, 1, 1); }\n\n\t\tdouble A[3][3] = { 0 };\n\t\tA[0][0] = ((E - F / Lt)*e.x*e.x + F / Lt);\n\t\tA[1][1] = ((E - F / Lt)*e.y*e.y + F / Lt);\n\t\tA[2][2] = ((E - F / Lt)*e.z*e.z + F / Lt);\n\n\t\tA[0][1] = A[1][0] = (E - F / Lt)*e.x*e.y;\n\t\tA[1][2] = A[2][1] = (E - F / Lt)*e.y*e.z;\n\t\tA[0][2] = A[2][0] = (E - F / Lt)*e.x*e.z;\n\n\t\tke[0][0] = A[0][0]; ke[0][1] = A[0][1]; ke[0][2] = A[0][2];\n\t\tke[1][0] = A[1][0]; ke[1][1] = A[1][1]; ke[1][2] = A[1][2];\n\t\tke[2][0] = A[2][0]; ke[2][1] = A[2][1]; ke[2][2] = A[2][2];\n\n\t\tke[0][3] = -A[0][0]; ke[0][4] = -A[0][1]; ke[0][5] = -A[0][2];\n\t\tke[1][3] = -A[1][0]; ke[1][4] = -A[1][1]; ke[1][5] = -A[1][2];\n\t\tke[2][3] = -A[2][0]; ke[2][4] = -A[2][1]; ke[2][5] = -A[2][2];\n\n\t\tke[3][0] = -A[0][0]; ke[3][1] = -A[0][1]; ke[3][2] = -A[0][2];\n\t\tke[4][0] = -A[1][0]; ke[4][1] = -A[1][1]; ke[4][2] = -A[1][2];\n\t\tke[5][0] = -A[2][0]; ke[5][1] = -A[2][1]; ke[5][2] = -A[2][2];\n\n\t\tke[3][3] = A[0][0]; ke[3][4] = A[0][1]; ke[3][5] = A[0][2];\n\t\tke[4][3] = A[1][0]; ke[4][4] = A[1][1]; ke[4][5] = A[1][2];\n\t\tke[5][3] = A[2][0]; ke[5][4] = A[2][1]; ke[5][5] = A[2][2];\n\n\t\t// setup the node vector\n\t\ten[0] = el.m_node[0];\n\t\ten[1] = el.m_node[1];\n\n\t\t// set up the LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble the element into the global system\n\t\tke.SetNodes(en);\n\t\tke.SetIndices(lm);\n\t\tLS.Assemble(ke);\n\t}\n\n\t// Add Bending stiffness\n\tif (m_kbend > 0)\n\t{\n\t\tdouble eps = m_kbend;\n\t\tvector<int> lmi(3);\n\t\tvector<int>\tlmj(9);\n\t\ten.resize(3);\n\t\tint NN = Nodes();\n\t\tfor (int i = 1; i<NN - 1; ++i)\n\t\t{\n\t\t\tint i0 = i - 1;\n\t\t\tint i1 = i + 1;\n\n\t\t\tke.resize(3, 9);\n\t\t\tke.zero();\n\t\t\tke[0][0] = -eps; ke[0][3] = 0.5*eps; ke[0][6] = 0.5*eps;\n\t\t\tke[1][1] = -eps; ke[1][4] = 0.5*eps; ke[1][7] = 0.5*eps;\n\t\t\tke[2][2] = -eps; ke[2][5] = 0.5*eps; ke[2][8] = 0.5*eps;\n\n\t\t\tvector<int>& IDi = Node(i).m_ID;\n\t\t\tvector<int>& ID0 = Node(i0).m_ID;\n\t\t\tvector<int>& ID1 = Node(i1).m_ID;\n\n\t\t\tlmi[0] = IDi[m_dofU[0]];\n\t\t\tlmi[1] = IDi[m_dofU[1]];\n\t\t\tlmi[2] = IDi[m_dofU[2]];\n\n\t\t\tlmj[0] = IDi[m_dofU[0]];\n\t\t\tlmj[1] = IDi[m_dofU[1]];\n\t\t\tlmj[2] = IDi[m_dofU[2]];\n\t\t\tlmj[3] = ID0[m_dofU[0]];\n\t\t\tlmj[4] = ID0[m_dofU[1]];\n\t\t\tlmj[5] = ID0[m_dofU[2]];\n\t\t\tlmj[6] = ID1[m_dofU[0]];\n\t\t\tlmj[7] = ID1[m_dofU[1]];\n\t\t\tlmj[8] = ID1[m_dofU[2]];\n\n\t\t\tke.SetNodes(en);\n\t\t\tke.SetIndices(lmi, lmj);\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n\n\tif (m_kstab > 0)\n\t{\n\t\tdouble eps = m_kstab;\n\t\tlm.resize(6);\n\t\ten.resize(2);\n\t\tke.resize(6,6); ke.zero();\n\t\tke[0][0] = ke[1][1] = ke[2][2] =  eps;\n\t\tke[3][0] = ke[4][1] = ke[5][2] = -eps;\n\t\tke[0][3] = ke[1][4] = ke[2][5] = -eps;\n\t\tke[3][3] = ke[4][4] = ke[5][5] =  eps;\n\n\t\tint NE = Elements();\n\t\tfor (int i = 0; i<NE; ++i)\n\t\t{\n\t\t\tFEDiscreteElement& el = Element(i);\n\t\t\ten[0] = el.m_node[0];\n\t\t\ten[1] = el.m_node[1];\n\n\t\t\t// get the nodes\n\t\t\tFENode& n0 = mesh.Node(en[0]);\n\t\t\tFENode& n1 = mesh.Node(en[1]);\n\n\t\t\tlm[0] = n0.m_ID[m_dofU[0]];\n\t\t\tlm[1] = n0.m_ID[m_dofU[1]];\n\t\t\tlm[2] = n0.m_ID[m_dofU[2]];\n\t\t\tlm[3] = n1.m_ID[m_dofU[0]];\n\t\t\tlm[4] = n1.m_ID[m_dofU[1]];\n\t\t\tlm[5] = n1.m_ID[m_dofU[2]];\n\n\t\t\t// assemble the element into the global system\n\t\t\tke.SetNodes(en);\n\t\t\tke.SetIndices(lm);\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n}\n\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEDeformableSpringDomain2::FEDeformableSpringDomain2(FEModel* pfem) : FEDiscreteDomain(pfem), FEElasticDomain(pfem), m_dofU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n\tm_pMat = nullptr;\n\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dofs\nconst FEDofList& FEDeformableSpringDomain2::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain2::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FESpringMaterial*>(pmat);\n\tassert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n// Only two nodes contribute to this spring\nvoid FEDeformableSpringDomain2::UnpackLM(FEElement &el, vector<int>& lm)\n{\n\tint N = Nodes();\n\tlm.resize(2 * 6);\n\tfor (int i = 0; i<2; ++i)\n\t{\n\t\tint n = (i==0? 0 : N-1);\n\t\tFENode& node = Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3 * i    ] = id[m_dofU[0]];\n\t\tlm[3 * i + 1] = id[m_dofU[1]];\n\t\tlm[3 * i + 2] = id[m_dofU[2]];\n\n\t\t// rigid rotational dofs\n\t\tlm[3  + 3 * i    ] = id[m_dofR[0]];\n\t\tlm[3  + 3 * i + 1] = id[m_dofR[1]];\n\t\tlm[3  + 3 * i + 2] = id[m_dofR[2]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEDeformableSpringDomain2::Init()\n{\n\tif (FEDiscreteDomain::Init() == false) return false;\n\n\t// initialize node data\n\tint NN = Nodes();\n\tm_nodeData.resize(NN);\n\tfor (int i=0; i<NN; ++i) m_nodeData[i].banchor = false;\n\n\t// anchor first and last\n\tm_nodeData[0].banchor = true;\n\tm_nodeData[NN-1].banchor = true;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain2::Activate()\n{\n\tint N = Nodes();\n\tfor (int i = 0; i<2; ++i)\n\t{\n\t\tint n = (i == 0 ? 0 : N - 1);\n\t\tFENode& node = Node(n);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Not sure if this necessary, but let's just do it to be sure\n\tUpdateNodes();\n\n\t// calculate the spring lengths\n\tm_L0 = InitialLength();\n\tm_Lt = CurrentLength();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDeformableSpringDomain2::InitialLength()\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tdouble L = 0.0;\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// get the nodes\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r1 = n1.m_r0;\n\t\tvec3d& r2 = n2.m_r0;\n\n\t\tdouble DL = (r2 - r1).norm();\n\t\tL += DL;\n\t}\n\treturn L;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDeformableSpringDomain2::CurrentLength()\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tdouble L = 0.0;\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// get the nodes\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r1 = n1.m_rt;\n\t\tvec3d& r2 = n2.m_rt;\n\n\t\tdouble DL = (r2 - r1).norm();\n\t\tL += DL;\n\t}\n\treturn L;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the forces due to discrete elements (i.e. springs)\n\nvoid FEDeformableSpringDomain2::InternalForces(FEGlobalVector& R)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tvector<double> fe(6);\n\tvec3d u1, u2;\n\n\tvector<int> en(2), lm(6);\n\n\t// calculate length increment\n\tdouble DL = m_Lt - m_L0;\n\n\t// calculate force\n\tdouble F = m_pMat->force(DL);\n\n\tint N = Elements();\n\tfor (size_t i = 0; i<2; ++i)\n\t{\n\t\tint n = (i==0? 0 : N-1);\n\t\tdouble sign = (i == 0 ? 1 : -1);\n\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = Element(n);\n\n\t\t// get the nodes\n\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t// get the nodal positions\n\t\tvec3d& r01 = n1.m_r0;\n\t\tvec3d& r02 = n2.m_r0;\n\t\tvec3d& rt1 = n1.m_rt;\n\t\tvec3d& rt2 = n2.m_rt;\n\n\t\tvec3d e = rt2 - rt1; e.unit();\n\n\t\t// set up the force vector\n\t\tfe[i*3    ] = sign*F*e.x;\n\t\tfe[i*3 + 1] = sign*F*e.y;\n\t\tfe[i*3 + 2] = sign*F*e.z;\n\n\t\t// setup the node vector\n\t\ten[i] = el.m_node[i];\n\n\t\t// set up the LM vector\n\t\tif (i==0) UnpackLM(el, lm);\n\t}\n\n\t// assemble element\n\tR.Assemble(en, lm, fe);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the discrete element stiffness\n\nvoid FEDeformableSpringDomain2::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tFEElementMatrix ke;\n\tke.resize(6, 6);\n\tke.zero();\n\n\tvector<int> en(2), lm(6);\n\n\t// get the nodes of the element\n\tint NN = Nodes();\n\tFENode& n1 = Node(0);\n\tFENode& n2 = Node(NN-1);\n\n\t// get the nodal positions\n\tvec3d& r01 = n1.m_r0;\n\tvec3d& r02 = n2.m_r0;\n\tvec3d& rt1 = n1.m_rt;\n\tvec3d& rt2 = n2.m_rt;\n\n\tvec3d e = rt2 - rt1; e.unit();\n\n\t// calculate spring lengths\n\tdouble L0 = (r02 - r01).norm();\n\tdouble Lt = (rt2 - rt1).norm();\n\tdouble DL = Lt - L0;\n\n\t// evaluate the stiffness\n\tdouble F = m_pMat->force(DL);\n\tdouble E = m_pMat->stiffness(DL);\n\n\tif (Lt == 0) { F = 0; Lt = 1; e = vec3d(1, 1, 1); }\n\n\tdouble A[3][3] = { 0 };\n\tA[0][0] = ((E - F / Lt)*e.x*e.x + F / Lt);\n\tA[1][1] = ((E - F / Lt)*e.y*e.y + F / Lt);\n\tA[2][2] = ((E - F / Lt)*e.z*e.z + F / Lt);\n\n\tA[0][1] = A[1][0] = (E - F / Lt)*e.x*e.y;\n\tA[1][2] = A[2][1] = (E - F / Lt)*e.y*e.z;\n\tA[0][2] = A[2][0] = (E - F / Lt)*e.x*e.z;\n\n\tke[0][0] = A[0][0]; ke[0][1] = A[0][1]; ke[0][2] = A[0][2];\n\tke[1][0] = A[1][0]; ke[1][1] = A[1][1]; ke[1][2] = A[1][2];\n\tke[2][0] = A[2][0]; ke[2][1] = A[2][1]; ke[2][2] = A[2][2];\n\n\tke[0][3] = -A[0][0]; ke[0][4] = -A[0][1]; ke[0][5] = -A[0][2];\n\tke[1][3] = -A[1][0]; ke[1][4] = -A[1][1]; ke[1][5] = -A[1][2];\n\tke[2][3] = -A[2][0]; ke[2][4] = -A[2][1]; ke[2][5] = -A[2][2];\n\n\tke[3][0] = -A[0][0]; ke[3][1] = -A[0][1]; ke[3][2] = -A[0][2];\n\tke[4][0] = -A[1][0]; ke[4][1] = -A[1][1]; ke[4][2] = -A[1][2];\n\tke[5][0] = -A[2][0]; ke[5][1] = -A[2][1]; ke[5][2] = -A[2][2];\n\n\tke[3][3] = A[0][0]; ke[3][4] = A[0][1]; ke[3][5] = A[0][2];\n\tke[4][3] = A[1][0]; ke[4][4] = A[1][1]; ke[4][5] = A[1][2];\n\tke[5][3] = A[2][0]; ke[5][4] = A[2][1]; ke[5][5] = A[2][2];\n\n\t// setup the node vector\n\ten[0] = m_Node[0];\n\ten[1] = m_Node[NN-1];\n\n\t// set up the LM vector\n\tUnpackLM(Element(0), lm);\n\n\t// assemble the element into the global system\n\tke.SetNodes(en);\n\tke.SetIndices(lm);\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain2::Update(const FETimeInfo& tp)\n{\n\t// update wire partition and nodal positions\n\tUpdateNodes();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDeformableSpringDomain2::SetNodePosition(int node, const vec3d& r)\n{\n\tFENode& nd = Node(node);\n\tnd.m_rt = r;\n\tvec3d u = nd.m_rt - nd.m_r0;\n\tnd.set_vec3d(m_dofU[0], m_dofU[1], m_dofU[2], u);\n}\n\n//-----------------------------------------------------------------------------\n// This functions distributes the nodes that are not anchored evenly between anchor\n// points.\nvoid FEDeformableSpringDomain2::UpdateNodes()\n{\n\t// make sure we have enough nodes\n\t// We need more than 2 nodes since the outer nodes are always anchored.\n\tint NN = Nodes();\n\tif (NN <= 2) return;\n\n\t// find wire segments\n\tint n0 = 0, n1 = 1;\n\twhile (n0 < NN-1)\n\t{\n\t\tif (m_nodeData[n1].banchor)\n\t\t{\n\t\t\tvec3d r0 = Node(n0).m_rt;\n\t\t\tvec3d r1 = Node(n1).m_rt;\n\n\t\t\tfor (int n = n0+1; n<=n1-1; ++n)\n\t\t\t{\n\t\t\t\tassert(m_nodeData[n].banchor == false);\n\n\t\t\t\tdouble w = (double) (n - n0) / (double)(n1 - n0);\n\n\t\t\t\tFENode& nd = Node(n);\n\t\t\t\tnd.m_rt = r0 + (r1 - r0)*w;\n\t\t\t\tvec3d u = nd.m_rt - nd.m_r0;\n\t\t\t\tnd.set_vec3d(m_dofU[0], m_dofU[1], m_dofU[2], u);\n\t\t\t}\n\n\t\t\tn0 = n1;\n\t\t}\n\t\tn1++;\n\t}\n\n\t// re-calculate current length\n\tm_Lt = CurrentLength();\n}\n\n//-----------------------------------------------------------------------------\n//! Anchor (or release) a node\nvoid FEDeformableSpringDomain2::AnchorNode(int node, bool banchor)\n{\n\tm_nodeData[node].banchor = banchor;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEDeformableSpringDomain2::NodalForce(int node)\n{\n\tint NN = Nodes();\n\tif ((node <= 0) || (node >= NN -1)) return vec3d(0,0,0);\n\n\tvec3d r = Node(node).m_rt;\n\tvec3d rm = Node(node-1).m_rt;\n\tvec3d rp = Node(node+1).m_rt;\n\n\tdouble F = SpringForce();\n\n\tvec3d A = rm - r; A.unit(); A *= F;\n\tvec3d B = rp - r; B.unit(); B *= F;\n\n\tvec3d D = A + B;\n\n\treturn D;\n}\n\n//-----------------------------------------------------------------------------\n//! get net spring force\ndouble FEDeformableSpringDomain2::SpringForce()\n{\n\treturn m_pMat->force(m_Lt - m_L0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent\nvec3d FEDeformableSpringDomain2::Tangent(int node)\n{\n\tint NN = Nodes();\n\tif (NN < 2) return vec3d(0,0,0);\n\tif (node == 0)\n\t{\n\t\tvec3d t = Node(node+1).m_rt - Node(node).m_rt;\n\t\tt.unit();\n\t\treturn t;\n\t}\n\telse if (node == NN-1)\n\t{\n\t\tvec3d t = Node(node).m_rt - Node(node - 1).m_rt;\n\t\tt.unit();\n\t\treturn t;\n\t}\n\telse\n\t{\n\t\tvec3d t = Node(node + 1).m_rt - Node(node - 1).m_rt;\n\t\tt.unit();\n\t\treturn t;\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEDeformableSpringDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEDiscreteDomain.h>\n#include <FECore/FEDofList.h>\n#include \"FEElasticDomain.h\"\n#include \"FESpringMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! domain for deformable springs\nclass FEDeformableSpringDomain : public FEDiscreteDomain, public FEElasticDomain\n{\npublic:\n\t//! constructor\n\tFEDeformableSpringDomain(FEModel* pfem);\n\n\t//! Unpack LM data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\tvoid Activate() override;\n\n\t//! get the total dofs\n\tconst FEDofList& GetDOFList() const override;\n\npublic: // overridden from FEElasticDomain\n\t//! build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! calculate stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override {}\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n\n\t//! Calculates inertial forces for dynamic problems | todo implement (removed assert DSR)\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override { }\n\n\t//! update domain data\n\tvoid Update(const FETimeInfo& tp) override {}\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculate bodyforces (not used since springs are considered mass-less)\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n\nprotected:\n\tdouble InitialLength();\n\tdouble CurrentLength();\n\nprotected:\n\tFESpringMaterial*\tm_pMat;\n\tdouble\t\t\t\tm_kbend;\t// bending stiffness\n\tdouble\t\t\t\tm_kstab;\t// stabilization penalty\n\tdouble\t\t\t\tm_L0;\t//!< initial spring length\n\nprotected:\n\tFEDofList\tm_dofU, m_dofR, m_dof;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! domain for deformable springs\n//! This approach assumes that the nodes are distributed evenly between anchor \n//! points. An anchor is a point that is constrained (e.g. prescribed, or in contact).\nclass FEDeformableSpringDomain2 : public FEDiscreteDomain, public FEElasticDomain\n{\n\tstruct NodeData\n\t{\n\t\tbool\tbanchor;\n\t};\n\npublic:\n\t//! constructor\n\tFEDeformableSpringDomain2(FEModel* pfem);\n\n\t//! Unpack LM data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! activation\n\tvoid Activate() override;\n\n\t//! get the total dofs\n\tconst FEDofList& GetDOFList() const override;\n\npublic:\n\t//! Set the position of a node\n\tvoid SetNodePosition(int node, const vec3d& r);\n\n\t//! Anchor (or release) a node\n\tvoid AnchorNode(int node, bool banchor);\n\n\t//! see if a node is anchored\n\tbool IsAnchored(int node) { return m_nodeData[node].banchor; }\n\n\t//! Update the position of all the nodes\n\tvoid UpdateNodes();\n\n\t//! Get the net force on this node\n\tvec3d NodalForce(int node);\n\n\t//! get net spring force\n\tdouble SpringForce();\n\n\t//! tangent\n\tvec3d Tangent(int node);\n\npublic: // overridden from FEElasticDomain\n\n\t//! calculate stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override {}\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n\n\t//! Calculates inertial forces for dynamic problems | todo implement (removed assert DSR)\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override { }\n\n\t//! update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculate bodyforces (not used since springs are considered mass-less)\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n\npublic:\n\tdouble InitialLength();\n\tdouble CurrentLength();\n\nprotected:\n\tFESpringMaterial*\tm_pMat;\n\tdouble\t\t\t\tm_L0;\t//!< initial wire length\n\tdouble\t\t\t\tm_Lt;\t//!< current wire length\n\tvector<NodeData>\tm_nodeData;\n\nprotected:\n\tFEDofList\tm_dofU, m_dofR, m_dof;\n};\n"
  },
  {
    "path": "FEBioMech/FEDeformationMapGenerator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDeformationMapGenerator.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FENodeDataMap.h>\n#include <FECore/FEDomainMap.h>\n\ndouble defgrad(FESolidElement &el, std::vector<vec3d>& X, std::vector<vec3d>& u, mat3d &F, int n);\n\nBEGIN_FECORE_CLASS(FEDeformationMapGenerator, FEElemDataGenerator)\n\tADD_PARAMETER(m_nodeDisplacementMap, \"node_displacement_map\");\nEND_FECORE_CLASS();\n\n\nFEDeformationMapGenerator::FEDeformationMapGenerator(FEModel* fem) : FEElemDataGenerator(fem)\n{\n\tm_nodeMap = nullptr;\n}\n\nFEDeformationMapGenerator::~FEDeformationMapGenerator()\n{\n\n}\n\nbool FEDeformationMapGenerator::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEDataMap* map = mesh.FindDataMap(m_nodeDisplacementMap);\n\tif (map == nullptr) return false;\n\n\tm_nodeMap = dynamic_cast<FENodeDataMap*>(map);\n\tif (m_nodeMap == nullptr) return false;\n\n\tif (m_nodeMap->DataType() != FE_VEC3D) return false;\n\n\treturn FEElemDataGenerator::Init();\n}\n\n// generate the data array for the given element set\nFEDataMap* FEDeformationMapGenerator::Generate()\n{\n\tFEElementSet& set = *GetElementSet();\n\n\tFEDomainMap* map = new FEDomainMap(FE_MAT3D, FMT_MATPOINTS);\n\tif (map->Create(&set) == false)\n\t{\n\t\tassert(false);\n\t\treturn nullptr;\n\t}\n\n\tFEMesh& mesh = *set.GetMesh();\n\n\tint N = set.Elements();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFESolidElement* pel = dynamic_cast<FESolidElement*>(mesh.FindElementFromID(set[i]));\n\t\tif (pel == nullptr) return nullptr;\n\t\tFESolidElement& el = *pel;\n\n\t\tint ne = el.Nodes();\n\t\tvector<vec3d> u(ne), X(ne);\n\t\tfor (int j = 0; j < ne; j++)\n\t\t{\n\t\t\tu[j] = m_nodeMap->get<vec3d>(el.m_node[j]);\n\t\t\tX[j] = mesh.Node(el.m_node[j]).m_r0 - u[j];\n\t\t}\n\n\t\tint ni = el.GaussPoints();\n\t\tfor (int j = 0; j < ni; ++j)\n\t\t{\n\t\t\tmat3d F;\n\t\t\tdefgrad(el, X, u, F, j);\n\t\t\tmap->setValue(i, j, F);\n\t\t}\n\t}\n\treturn map;\n}\n\ndouble invjac0(FESolidElement &el, std::vector<vec3d>& r0, double Ji[3][3], int n)\n{\n\t// calculate Jacobian\n\tdouble J[3][3] = { 0 };\n\tint neln = el.Nodes();\n\tfor (int i = 0; i < neln; ++i)\n\t{\n\t\tconst double& Gri = el.Gr(n)[i];\n\t\tconst double& Gsi = el.Gs(n)[i];\n\t\tconst double& Gti = el.Gt(n)[i];\n\n\t\tconst double& x = r0[i].x;\n\t\tconst double& y = r0[i].y;\n\t\tconst double& z = r0[i].z;\n\n\t\tJ[0][0] += Gri * x; J[0][1] += Gsi * x; J[0][2] += Gti * x;\n\t\tJ[1][0] += Gri * y; J[1][1] += Gsi * y; J[1][2] += Gti * y;\n\t\tJ[2][0] += Gri * z; J[2][1] += Gsi * z; J[2][2] += Gti * z;\n\t}\n\n\t// calculate the determinant\n\tdouble det = J[0][0] * (J[1][1] * J[2][2] - J[1][2] * J[2][1])\n\t\t+ J[0][1] * (J[1][2] * J[2][0] - J[2][2] * J[1][0])\n\t\t+ J[0][2] * (J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\tif (det != 0.0)\n\t{\n\t\t// calculate the inverse jacobian\n\t\tdouble deti = 1.0 / det;\n\n\t\tJi[0][0] = deti * (J[1][1] * J[2][2] - J[1][2] * J[2][1]);\n\t\tJi[1][0] = deti * (J[1][2] * J[2][0] - J[1][0] * J[2][2]);\n\t\tJi[2][0] = deti * (J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\t\tJi[0][1] = deti * (J[0][2] * J[2][1] - J[0][1] * J[2][2]);\n\t\tJi[1][1] = deti * (J[0][0] * J[2][2] - J[0][2] * J[2][0]);\n\t\tJi[2][1] = deti * (J[0][1] * J[2][0] - J[0][0] * J[2][1]);\n\n\t\tJi[0][2] = deti * (J[0][1] * J[1][2] - J[1][1] * J[0][2]);\n\t\tJi[1][2] = deti * (J[0][2] * J[1][0] - J[0][0] * J[1][2]);\n\t\tJi[2][2] = deti * (J[0][0] * J[1][1] - J[0][1] * J[1][0]);\n\t}\n\n\treturn det;\n}\n\ndouble defgrad(FESolidElement &el, std::vector<vec3d>& X, std::vector<vec3d>& u, mat3d &F, int n)\n{\n\t// calculate inverse jacobian\n    double Ji[3][3];\n    invjac0(el, X, Ji, n);\n\n\t// shape function derivatives\n\tdouble *Grn = el.Gr(n);\n\tdouble *Gsn = el.Gs(n);\n\tdouble *Gtn = el.Gt(n);\n\n\t// calculate deformation gradient\n\tF[0][0] = F[0][1] = F[0][2] = 0;\n\tF[1][0] = F[1][1] = F[1][2] = 0;\n\tF[2][0] = F[2][1] = F[2][2] = 0;\n\tint neln = el.Nodes();\n\tfor (int i = 0; i < neln; ++i)\n\t{\n\t\tdouble Gri = Grn[i];\n\t\tdouble Gsi = Gsn[i];\n\t\tdouble Gti = Gtn[i];\n\n\t\tdouble x = u[i].x;\n\t\tdouble y = u[i].y;\n\t\tdouble z = u[i].z;\n\n\t\t// calculate global gradient of shape functions\n\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\tdouble GX = Ji[0][0] * Gri + Ji[1][0] * Gsi + Ji[2][0] * Gti;\n\t\tdouble GY = Ji[0][1] * Gri + Ji[1][1] * Gsi + Ji[2][1] * Gti;\n\t\tdouble GZ = Ji[0][2] * Gri + Ji[1][2] * Gsi + Ji[2][2] * Gti;\n\n\t\t// calculate deformation gradient F\n\t\tF[0][0] += GX * x; F[0][1] += GY * x; F[0][2] += GZ * x;\n\t\tF[1][0] += GX * y; F[1][1] += GY * y; F[1][2] += GZ * y;\n\t\tF[2][0] += GX * z; F[2][1] += GY * z; F[2][2] += GZ * z;\n\t}\n\n\tF[0][0] += 1.0;\n\tF[1][1] += 1.0;\n\tF[2][2] += 1.0;\n\n\tdouble D = F.det();\n\n\treturn D;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEDeformationMapGenerator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEDataGenerator.h>\n\nclass FENodeDataMap;\n\nclass FEDeformationMapGenerator : public FEElemDataGenerator\n{\npublic:\n\tFEDeformationMapGenerator(FEModel* fem);\n\t~FEDeformationMapGenerator();\n\n\tbool Init() override;\n\n\t// generate the data array for the given element set\n\tFEDataMap* Generate() override;\n\nprivate:\n\tstd::string\t\tm_nodeDisplacementMap;\n\nprivate:\n\tFENodeDataMap*\tm_nodeMap;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDiscreteContact.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiscreteContact.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEGlobalMatrix.h>\n#include \"FEContactInterface.h\"\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\nFEDiscreteContactSurface::FEDiscreteContactSurface(FEModel* fem) : FEContactSurface(fem)\n{\n\n}\n\nbool FEDiscreteContactSurface::Init()\n{\n\treturn FEContactSurface::Init();\n}\n\nBEGIN_FECORE_CLASS(FEDiscreteContact, FESurfaceConstraint)\n\tADD_PARAMETER(m_blaugon, \"laugon\");\n\tADD_PARAMETER(m_altol  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"altol\");\n\tADD_PARAMETER(m_gaptol , FE_RANGE_GREATER_OR_EQUAL(0.0), \"gaptol\");\n\tADD_PARAMETER(m_penalty, FE_RANGE_GREATER_OR_EQUAL(0.0), \"penalty\");\n\tADD_PARAMETER(m_naugmin, \"minaug\");\n\tADD_PARAMETER(m_naugmax, \"maxaug\");\n\tADD_PARAMETER(m_nsegup , \"segup\");\nEND_FECORE_CLASS();\n\nFEDiscreteContact::FEDiscreteContact(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem)\n{\n\tm_blaugon = false;\n\tm_altol = 0.01;\n\tm_penalty = 1.0;\n\tm_gaptol = 0.0;\n\tm_naugmin = 0;\n\tm_naugmax = 100;\n\tm_bfirst = true;\n\tm_nsegup = 0;\n}\n\nbool FEDiscreteContact::Init()\n{\n\treturn m_surf.Init();\n}\n\nvoid FEDiscreteContact::Activate()\n{\n\tFENLConstraint::Activate();\n\tProjectSurface(true);\n}\n\nvoid FEDiscreteContact::Update(const FETimeInfo& tp)\n{\n\tbool bupdate = (m_bfirst || (m_nsegup == 0)? true : (tp.currentIteration <= m_nsegup));\n\tProjectSurface(true);\n\tm_bfirst = false;\n}\n\nvoid FEDiscreteContact::SetDiscreteSet(FEDiscreteSet* pset)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tvector<int> tag(mesh.Nodes(), 0);\n\tm_Node.clear();\n\tint nsize = pset->size();\n\tfor (int i=0; i<nsize; ++i)\n\t{\n\t\tconst FEDiscreteSet::NodePair& delem = pset->Element(i);\n\t\ttag[delem.n0] = 1;\n\t\ttag[delem.n1] = 1;\n\t}\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tif (tag[i])\n\t\t{\n\t\t\tNODE node;\n\t\t\tnode.nid = i;\n\t\t\tnode.pe = 0;\n\t\t\tnode.Lm = 0;\n\t\t\tnode.gap = 0;\n\t\t\tnode.nu = vec3d(0,0,0);\n\t\t\tnode.q = vec3d(0,0,0);\n\t\t\tnode.proj[0] = node.proj[1] = 0.0;\n\n\t\t\tm_Node.push_back(node);\n\t\t}\n\t}\n}\n\nvoid FEDiscreteContact::ProjectSurface(bool bsegup)\n{\n\tFEClosestPointProjection cpp(m_surf);\n\tcpp.SetTolerance(0.01);\n\tcpp.SetSearchRadius(0.0);\n\tcpp.HandleSpecialCases(true);\n\tcpp.Init();\n\n\t// loop over all primary nodes\n\tFEMesh& mesh = *m_surf.GetMesh();\n\tfor (int i=0; i<(int)m_Node.size(); ++i)\n\t{\n\t\tNODE& nodeData = m_Node[i];\n\n\t\t// get the node\n\t\tFENode& node = mesh.Node(nodeData.nid);\n\n\t\t// get the nodal position\n\t\tvec3d x = node.m_rt;\n\n\t\t// If the node is in contact, let's see if the node still is \n\t\t// on the same secondary element\n\t\tif (nodeData.pe != 0)\n\t\t{\n\t\t\tFESurfaceElement& mel = *nodeData.pe;\n\n\t\t\tdouble r = nodeData.proj[0];\n\t\t\tdouble s = nodeData.proj[1];\n\n\t\t\tvec3d q = m_surf.ProjectToSurface(mel, x, r, s);\n\t\t\tnodeData.proj[0] = r;\n\t\t\tnodeData.proj[1] = s;\n\t\t\tnodeData.q = q;\n\n\t\t\tif (bsegup && (!m_surf.IsInsideElement(mel, r, s, 0.01)))\n\t\t\t{\n\t\t\t\t// see if the node might have moved to another secondary element\n\t\t\t\tvec2d rs(0,0);\n\t\t\t\tnodeData.pe = cpp.Project(x, q, rs);\n\t\t\t\tnodeData.proj[0] = rs.x();\n\t\t\t\tnodeData.proj[1] = rs.y();\n\t\t\t\tnodeData.q = q;\n\t\t\t}\n\t\t}\n\t\telse if (bsegup)\n\t\t{\n\t\t\tvec2d rs(0,0); vec3d q;\n\t\t\tnodeData.pe = cpp.Project(x, q, rs);\n\t\t\tnodeData.proj[0] = rs.x();\n\t\t\tnodeData.proj[1] = rs.y();\n\t\t\tnodeData.q = q;\n\t\t}\n\n\t\t// if we found a secondary element, update the gap and normal data\n\t\tif (nodeData.pe != 0)\n\t\t{\n\t\t\tFESurfaceElement& mel =  *nodeData.pe;\n\n\t\t\tdouble r = nodeData.proj[0];\n\t\t\tdouble s = nodeData.proj[1];\n\n\t\t\t// the primary normal is set to the secondary element normal\n\t\t\tnodeData.nu = m_surf.SurfaceNormal(mel, r, s);\n\n\t\t\t// calculate gap\n\t\t\tnodeData.gap = -(nodeData.nu*(x - nodeData.q));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// TODO: Is this a good criteria for out-of-contact?\n\t\t\t//\t\t perhaps this is not even necessary.\n\t\t\t// since the node is not in contact, we set the gap function \n\t\t\t// and Lagrangian multiplier to zero\n\t\t\tnodeData.gap = 0;\n\t\t\tnodeData.Lm = 0;\n\t\t}\n\t}\n}\n\nvoid FEDiscreteContact::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\t// the elements LM vectors\n\tvector<int> mLM;\n\n\t// loop over all primary nodes\n\tFEMesh& mesh = *m_surf.GetMesh();\n\tint nodes = (int) m_Node.size();\n\tfor (int i=0; i<nodes; ++i)\n\t{\n\t\tNODE& nodeData = m_Node[i];\n\n\t\tFENode& node = mesh.Node(nodeData.nid);\n\t\tvector<int>& sLM = node.m_ID;\n\n\t\tFESurfaceElement* pe = nodeData.pe;\n\n\t\t// see if this node's constraint is active\n\t\t// that is, if it has a secondary element associated with it\n\t\t// TODO: is this a good way to test for an active constraint\n\t\t// The rigid wall criteria seems to work much better.\n\t\tif (pe != 0)\n\t\t{\n\t\t\t// This node is active and could lead to a non-zero\n\t\t\t// contact force.\n\t\t\t// get the secondary element\n\t\t\tFESurfaceElement& mel = *pe;\n\t\t\tm_surf.UnpackLM(mel, mLM);\n\n\t\t\t// calculate the degrees of freedom\n\t\t\tint nmeln = mel.Nodes();\n\t\t\tint ndof = 3*(nmeln+1);\n\t\t\tfe.resize(ndof);\n\n\t\t\t// calculate the nodal force\n\t\t\tContactNodalForce(nodeData, mel, fe);\n\n\t\t\t// fill the lm array\n\t\t\tlm.resize(3*(nmeln+1));\n\t\t\tlm[0] = sLM[0];\n\t\t\tlm[1] = sLM[1];\n\t\t\tlm[2] = sLM[2];\n\n\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t{\n\t\t\t\tlm[3*(l+1)  ] = mLM[l*3  ];\n\t\t\t\tlm[3*(l+1)+1] = mLM[l*3+1];\n\t\t\t\tlm[3*(l+1)+2] = mLM[l*3+2];\n\t\t\t}\n\n\t\t\t// fill the en array\n\t\t\ten.resize(nmeln+1);\n\t\t\ten[0] = nodeData.nid;\n\t\t\tfor (int l=0; l<nmeln; ++l) en[l+1] = mel.m_node[l];\n\n\t\t\t// assemble into global force vector\n\t\t\tR.Assemble(en, lm, fe);\n\t\t}\n\t}\n}\n\nvoid FEDiscreteContact::ContactNodalForce(FEDiscreteContact::NODE& nodeData, FESurfaceElement& mel, vector<double>& fe)\n{\n\t// max nr of secondary element nodes\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\t// secondary element nodes\n\tvec3d rtm[MAXMN];\n\n\t// secondary shape function values at projection point\n\tdouble H[MAXMN];\n\n\t// contact forces\n\tdouble N[3*(MAXMN+1)];\n\n\t// get the mesh\n\tFEMesh& mesh = *m_surf.GetMesh();\n\n\t// gap function\n\tdouble gap = nodeData.gap;\n\n\t// penalty\n\tdouble eps = m_penalty;\n\n\t// get primary node normal force\n\tdouble Ln = nodeData.Lm;\n\tdouble tn = Ln + eps*gap;\n\ttn = MBRACKET(tn);\n\n\t// get the primary node normal\n\tvec3d nu = nodeData.nu;\n\n\tint nmeln = mel.Nodes();\n\tint ndof = 3*(1 + nmeln);\n\n\t// get the secondary element node positions\n\tfor (int k=0; k<nmeln; ++k) rtm[k] = mesh.Node(mel.m_node[k]).m_rt;\n\n\t// isoparametric coordinates of the projected primary node\n\t// onto the secondary element\n\tdouble r = nodeData.proj[0];\n\tdouble s = nodeData.proj[1];\n\n\t// get the secondary shape function values at this primary node\n\tmel.shape_fnc(H, r, s);\n\n\t// calculate contact vectors for normal traction\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\tfor (int l=0; l<nmeln; ++l)\n\t{\n\t\tN[3*(l+1)  ] = -H[l]*nu.x;\n\t\tN[3*(l+1)+1] = -H[l]*nu.y;\n\t\tN[3*(l+1)+2] = -H[l]*nu.z;\n\t}\n\n\t// calculate force vector\n\tfor (int l=0; l<ndof; ++l) fe[l] = tn*N[l];\n}\n\nvoid FEDiscreteContact::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEElementMatrix ke;\n\n\tconst int MAXMN = FEElement::MAX_NODES;\n\tvector<int> lm(3*(MAXMN + 1));\n\tvector<int> en(MAXMN+1);\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\t// loop over all integration points (that is nodes)\n\tFEMesh& mesh = *m_surf.GetMesh();\n\tint nodes = (int) m_Node.size();\n\tfor (int i=0; i<nodes; ++i)\n\t{\n\t\tNODE& nodeData = m_Node[i];\n\n\t\tvector<int>& sLM = mesh.Node(nodeData.nid).m_ID;\n\n\t\t// see if this node's constraint is active\n\t\t// that is, if it has a secondary element associated with it\n\t\tif (nodeData.pe != 0)\n\t\t{\n\t\t\t// get the secondary element\n\t\t\tFESurfaceElement& me = *nodeData.pe;\n\n\t\t\t// get the secondary surface element's LM array\n\t\t\tm_surf.UnpackLM(me, mLM);\n\n\t\t\tint nmeln = me.Nodes();\n\t\t\tint ndof = 3*(nmeln+1);\n\n\t\t\t// calculate the stiffness matrix\n\t\t\tke.resize(ndof, ndof);\n\t\t\tContactNodalStiffness(nodeData, me, ke);\n\n\t\t\t// fill the lm array\n\t\t\tlm[0] = sLM[0];\n\t\t\tlm[1] = sLM[1];\n\t\t\tlm[2] = sLM[2];\n\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t{\n\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t}\n\n\t\t\t// create the en array\n\t\t\ten.resize(nmeln+1);\n\t\t\ten[0] = nodeData.nid;\n\t\t\tfor (int k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\t\t\t\t\t\t\n\t\t\t// assemble stiffness matrix\n\t\t\tke.SetNodes(en);\n\t\t\tke.SetIndices(lm);\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n}\n\nvoid FEDiscreteContact::ContactNodalStiffness(FEDiscreteContact::NODE& nodeData, FESurfaceElement& mel, matrix& ke)\n{\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\tvector<int> lm(3*(MAXMN+1));\n\tvector<int> en(MAXMN + 1);\n\n\tdouble H[MAXMN], Hr[MAXMN], Hs[MAXMN];\n\tdouble N[3*(MAXMN+1)], T1[3*(MAXMN+1)], T2[3*(MAXMN+1)];\n\tdouble N1[3*(MAXMN+1)], N2[3*(MAXMN+1)], D1[3*(MAXMN+1)], D2[3*(MAXMN+1)];\n\tdouble Nb1[3*(MAXMN+1)], Nb2[3*(MAXMN+1)];\n\n\t// get the mesh\n\tFEMesh& mesh = *m_surf.GetMesh();\n\n\t// nr of element nodes and degrees of freedom \n\tint nmeln = mel.Nodes();\n\tint ndof = 3*(1 + nmeln);\n\n\t// penalty factor\n\tdouble eps = m_penalty;\n\n\t// nodal coordinates\n\tvec3d rt[MAXMN];\n\tfor (int j=0; j<nmeln; ++j) rt[j] = mesh.Node(mel.m_node[j]).m_rt;\n\n\t// primary node natural coordinates in secondary element\n\tdouble r = nodeData.proj[0];\n\tdouble s = nodeData.proj[1];\n\n\t// primary gap\n\tdouble gap = nodeData.gap;\n\n\t// lagrange multiplier\n\tdouble Lm = nodeData.Lm;\n\n\t// get primary node normal force\n\tdouble tn = Lm + eps*gap;\n\ttn = MBRACKET(tn);\n\n\t// get the primary node normal\n\tvec3d nu = nodeData.nu;\n\n\t// get the secondary shape function values and the derivatives at this primary node\n\tmel.shape_fnc(H, r, s);\n\tmel.shape_deriv(Hr, Hs, r, s);\n\n\t// get the tangent vectors\n\tvec3d tau[2];\n\tm_surf.CoBaseVectors(mel, r, s, tau);\n\n\t// set up the N vector\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\n\tfor (int k=0; k<nmeln; ++k) \n\t{\n\t\tN[(k+1)*3  ] = -H[k]*nu.x;\n\t\tN[(k+1)*3+1] = -H[k]*nu.y;\n\t\tN[(k+1)*3+2] = -H[k]*nu.z;\n\t}\n\t\n\t// set up the Ti vectors\n\tT1[0] = tau[0].x; T2[0] = tau[1].x;\n\tT1[1] = tau[0].y; T2[1] = tau[1].y;\n\tT1[2] = tau[0].z; T2[2] = tau[1].z;\n\n\tfor (int k=0; k<nmeln; ++k) \n\t{\n\t\tT1[(k+1)*3  ] = -H[k]*tau[0].x;\n\t\tT1[(k+1)*3+1] = -H[k]*tau[0].y;\n\t\tT1[(k+1)*3+2] = -H[k]*tau[0].z;\n\n\t\tT2[(k+1)*3  ] = -H[k]*tau[1].x;\n\t\tT2[(k+1)*3+1] = -H[k]*tau[1].y;\n\t\tT2[(k+1)*3+2] = -H[k]*tau[1].z;\n\t}\n\n\t// set up the Ni vectors\n\tN1[0] = N2[0] = 0;\n\tN1[1] = N2[1] = 0;\n\tN1[2] = N2[2] = 0;\n\n\tfor (int k=0; k<nmeln; ++k) \n\t{\n\t\tN1[(k+1)*3  ] = -Hr[k]*nu.x;\n\t\tN1[(k+1)*3+1] = -Hr[k]*nu.y;\n\t\tN1[(k+1)*3+2] = -Hr[k]*nu.z;\n\n\t\tN2[(k+1)*3  ] = -Hs[k]*nu.x;\n\t\tN2[(k+1)*3+1] = -Hs[k]*nu.y;\n\t\tN2[(k+1)*3+2] = -Hs[k]*nu.z;\n\t}\n\n\t// calculate metric tensor\n\tmat2d M;\n\tM[0][0] = tau[0]*tau[0]; M[0][1] = tau[0]*tau[1]; \n\tM[1][0] = tau[1]*tau[0]; M[1][1] = tau[1]*tau[1]; \n\n\t// calculate reciprocal metric tensor\n\tmat2d Mi = M.inverse();\n\n\t// calculate curvature tensor\n\tdouble K[2][2] = {0};\n\tdouble Grr[FEElement::MAX_NODES];\n\tdouble Grs[FEElement::MAX_NODES];\n\tdouble Gss[FEElement::MAX_NODES];\n\tmel.shape_deriv2(Grr, Grs, Gss, r, s);\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tK[0][0] += (nu*rt[k])*Grr[k];\n\t\tK[0][1] += (nu*rt[k])*Grs[k];\n\t\tK[1][0] += (nu*rt[k])*Grs[k];\n\t\tK[1][1] += (nu*rt[k])*Gss[k];\n\t}\n\n\t// setup A matrix A = M + gK\n\tdouble A[2][2];\n\tA[0][0] = M[0][0] + gap*K[0][0];\n\tA[0][1] = M[0][1] + gap*K[0][1];\n\tA[1][0] = M[1][0] + gap*K[1][0];\n\tA[1][1] = M[1][1] + gap*K[1][1];\n\n\t// calculate determinant of A\n\tdouble detA = A[0][0]*A[1][1] - A[0][1]*A[1][0];\n\n\t// setup Di vectors\n\tfor (int k=0; k<ndof; ++k)\n\t{\n\t\tD1[k] = (1/detA)*(A[1][1]*(T1[k]+gap*N1[k]) - A[0][1]*(T2[k] + gap*N2[k]));\n\t\tD2[k] = (1/detA)*(A[0][0]*(T2[k]+gap*N2[k]) - A[0][1]*(T1[k] + gap*N1[k]));\n\t}\n\n\t// setup Nbi vectors\n\tfor (int k=0; k<ndof; ++k)\n\t{\n\t\tNb1[k] = N1[k] - K[0][1]*D2[k];\n\t\tNb2[k] = N2[k] - K[0][1]*D1[k];\n\t}\n\n\t// --- N O R M A L   S T I F F N E S S ---\n\tdouble sum;\n\tfor (int k=0; k<ndof; ++k)\n\t\tfor (int l=0; l<ndof; ++l)\n\t\t\t{\n\t\t\t\tsum = 0;\n\n\t\t\t\tsum = Mi[0][0]*Nb1[k]*Nb1[l]+Mi[0][1]*(Nb1[k]*Nb2[l]+Nb2[k]*Nb1[l])+Mi[1][1]*Nb2[k]*Nb2[l];\n\t\t\t\tsum *= gap;\n\t\t\t\tsum -= D1[k]*N1[l]+D2[k]*N2[l]+N1[k]*D1[l]+N2[k]*D2[l];\n\t\t\t\tsum += K[0][1]*(D1[k]*D2[l]+D2[k]*D1[l]);\n\t\t\t\tsum *= tn;\n\n\t\t\t\tsum += eps*HEAVYSIDE(Lm+eps*gap)*N[k]*N[l];\n\t\n\t\t\t\tke[k][l] = sum;\n\t\t\t}\t\t\n}\n\n\nbool FEDiscreteContact::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (!m_blaugon) return true;\n\n\tbool bconv = true;\n\tmat2d Mi;\n\n\t// penalty factor\n\tdouble eps = m_penalty;\n\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\tdouble normL0 = 0;\n\tfor (int i=0; i<(int) m_Node.size(); ++i)\tnormL0 += m_Node[i].Lm * m_Node[i].Lm;\n\tnormL0 = sqrt(normL0);\n\n\t// --- c a l c u l a t e   c u r r e n t   n o r m s ---\n\tdouble normL1 = 0;\t// force norm\n\tdouble normg1 = 0;\t// gap norm\n\tint N = 0;\n\tfor (int i=0; i<(int)m_Node.size(); ++i)\n\t{\n\t\t// update Lagrange multipliers\n\t\tdouble Ln = m_Node[i].Lm + eps*m_Node[i].gap;\n\t\tLn = MBRACKET(Ln);\n\n\t\tnormL1 += Ln*Ln;\n\n\t\tif (m_Node[i].gap > 0)\n\t\t{\n\t\t\tnormg1 += m_Node[i].gap*m_Node[i].gap;\n\t\t\t++N;\n\t\t}\n\t}\t\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormg1 = sqrt(normg1 / N);\n\n\tif (naug == 0) m_normg0 = 0;\n\n\t// calculate and print convergence norms\n\tdouble lnorm = 0, gnorm = 0;\n\tif (normL1 != 0) lnorm = fabs(normL1 - normL0)/normL1; else lnorm = fabs(normL1 - normL0);\n\tif (normg1 != 0) gnorm = fabs(normg1 - m_normg0)/normg1; else gnorm = fabs(normg1 - m_normg0);\n\n\tfeLog(\" discrete contact # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    normal force : %15le\", lnorm);\n\tif (m_altol > 0) feLog(\"%15le\\n\", m_altol); else feLog(\"       ***\\n\");\n\tfeLog(\"    gap function : %15le\", gnorm);\n\tif (m_gaptol > 0) feLog(\"%15le\\n\", m_gaptol); else feLog(\"       ***\\n\");\n\n\t// check convergence\n\tbconv = true;\n\tif ((m_altol > 0) && (lnorm > m_altol)) bconv = false;\n\tif ((m_gaptol > 0) && (gnorm > m_gaptol)) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif (m_naugmax <= naug) bconv = true;\n\t\t\n\tif (bconv == false)\n\t{\n\t\t// we did not converge so update multipliers\n\t\tfor (int i=0; i<(int) m_Node.size(); ++i)\n\t\t{\n\t\t\tNODE& node = m_Node[i];\n\t\t\t// update Lagrange multipliers\n\t\t\tdouble Ln = node.Lm + eps*node.gap;\n\t\t\tnode.Lm = MBRACKET(Ln);\n\t\t}\t\n\t}\n\n\t// store the last gap norm\n\tm_normg0 = normg1;\n\n\treturn bconv;\n}\n\nvoid FEDiscreteContact::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\t// TODO: this is currently for max 6 nodes (hence 7=6+1)\n\tvector<int> lm(6*7);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tconst int nodes = (int) m_Node.size();\n\tfor (int i=0; i<nodes; ++i)\n\t{\n\t\tNODE& nodeData = m_Node[i];\n\n\t\t// get the FE node\n\t\tFENode& node = mesh.Node(nodeData.nid);\n\n\t\t// get the secondary surface element\n\t\tFESurfaceElement* pe = nodeData.pe;\n\n\t\tif (pe != 0)\n\t\t{\n\t\t\tFESurfaceElement& me = *pe;\n\t\t\tint* en = &me.m_node[0];\n\n\t\t\t// Note that we need to grab the rigid degrees of freedom as well\n\t\t\t// this is in case one of the nodes belongs to a rigid body.\n\t\t\tint n = me.Nodes();\n\t\t\tif (n == 3)\n\t\t\t{\n\t\t\t\tlm[6*(3+1)  ] = -1;lm[6*(3+2)  ] = -1;lm[6*(3+3)  ] = -1;\n\t\t\t\tlm[6*(3+1)+1] = -1;lm[6*(3+2)+1] = -1;lm[6*(3+3)+1] = -1;\n\t\t\t\tlm[6*(3+1)+2] = -1;lm[6*(3+2)+2] = -1;lm[6*(3+3)+2] = -1;\n\t\t\t\tlm[6*(3+1)+3] = -1;lm[6*(3+2)+3] = -1;lm[6*(3+3)+3] = -1;\n\t\t\t\tlm[6*(3+1)+4] = -1;lm[6*(3+2)+4] = -1;lm[6*(3+3)+4] = -1;\n\t\t\t\tlm[6*(3+1)+5] = -1;lm[6*(3+2)+5] = -1;lm[6*(3+3)+5] = -1;\n\t\t\t}\n\t\t\tif (n == 4)\n\t\t\t{\n\t\t\t\tlm[6*(4+1)  ] = -1;lm[6*(4+2)  ] = -1;\n\t\t\t\tlm[6*(4+1)+1] = -1;lm[6*(4+2)+1] = -1;\n\t\t\t\tlm[6*(4+1)+2] = -1;lm[6*(4+2)+2] = -1;\n\t\t\t\tlm[6*(4+1)+3] = -1;lm[6*(4+2)+3] = -1;\n\t\t\t\tlm[6*(4+1)+4] = -1;lm[6*(4+2)+4] = -1;\n\t\t\t\tlm[6*(4+1)+5] = -1;lm[6*(4+2)+5] = -1;\n\t\t\t}\n\n\t\t\tlm[0] = node.m_ID[dof_X];\n\t\t\tlm[1] = node.m_ID[dof_Y];\n\t\t\tlm[2] = node.m_ID[dof_Z];\n\t\t\tlm[3] = node.m_ID[dof_RU];\n\t\t\tlm[4] = node.m_ID[dof_RV];\n\t\t\tlm[5] = node.m_ID[dof_RW];\n\n\t\t\tfor (int k=0; k<n; ++k)\n\t\t\t{\n\t\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\t\tlm[6*(k+1)  ] = id[dof_X];\n\t\t\t\tlm[6*(k+1)+1] = id[dof_Y];\n\t\t\t\tlm[6*(k+1)+2] = id[dof_Z];\n\t\t\t\tlm[6*(k+1)+3] = id[dof_RU];\n\t\t\t\tlm[6*(k+1)+4] = id[dof_RV];\n\t\t\t\tlm[6*(k+1)+5] = id[dof_RW];\n\t\t\t}\n\n\t\t\tK.build_add(lm);\n\t\t}\n\t}\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEDiscreteContact2, FESurfaceConstraint);\nEND_FECORE_CLASS();\n\nFEDiscreteContact2::FEDiscreteContact2(FEModel* fem) : FESurfaceConstraint(fem), m_surf(fem)\n{\n\tm_dom = 0;\t\n}\n\nbool FEDiscreteContact2::Init()\n{\n\t// let's make sure we have a discrete domain set\n\tif (m_dom == 0) return false;\n\n\t// initialize node data\n\tint NN = m_dom->Nodes();\n\tm_nodeData.resize(NN);\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tNODE& nd = m_nodeData[i];\n\t\tnd.node = i;\n\t\tnd.pe = 0;\n\t}\n\n\t// initialize surface\n\tif (m_surf.Init() == false) return false;\n\n\t// let's do base class initiialization\n\treturn FENLConstraint::Init();\n}\n\nvoid FEDiscreteContact2::Activate()\n{\n\tFENLConstraint::Activate();\n\tProjectNodes();\n}\n\nvoid FEDiscreteContact2::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint NN = m_dom->Nodes();\n\n\tFEMesh& mesh = *m_surf.GetMesh();\n\n\t// skip first and last\n\tfor (int n=1; n<NN-1; ++n)\n\t{\n\t\tif (m_dom->IsAnchored(n))\n\t\t{\n\t\t\tNODE& nodeData = m_nodeData[n];\n\t\t\tassert(nodeData.pe);\n\t\t\tFESurfaceElement& el = *nodeData.pe;\n\n\t\t\tdouble r = nodeData.proj[0];\n\t\t\tdouble s = nodeData.proj[1];\n\n\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\tint neln = el.Nodes();\n\n\t\t\tel.shape_fnc(H, r, s);\n\n\t\t\tvec3d F = m_dom->NodalForce(n);\n\t\t\tvec3d nu = nodeData.nu;\n\t\t\tif (F*nu < 0.0)\n\t\t\t{\n\t\t\t\tvector<double> fe(3*neln, 0.0);\n\t\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t\t{\n\t\t\t\t\tfe[3*i  ] = H[i]*F.x;\n\t\t\t\t\tfe[3*i+1] = H[i]*F.y;\n\t\t\t\t\tfe[3*i+2] = H[i]*F.z;\n\t\t\t\t}\n\n\t\t\t\tvector<int> lm(3*neln, -1);\n\t\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodei = mesh.Node(el.m_node[i]);\n\t\t\t\t\tlm[3*i  ] = nodei.m_ID[0];\n\t\t\t\t\tlm[3*i+1] = nodei.m_ID[1];\n\t\t\t\t\tlm[3*i+2] = nodei.m_ID[2];\n\t\t\t\t}\n\n\t\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEDiscreteContact2::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n}\n\nvoid FEDiscreteContact2::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n}\n\nvoid FEDiscreteContact2::Update(const FETimeInfo& tp)\n{\n\tProjectNodes();\n}\n\nvoid FEDiscreteContact2::ProjectNodes()\n{\n\t// setup closest point projection\n\tFEClosestPointProjection cpp(m_surf);\n\tcpp.SetTolerance(0.01);\n\tcpp.SetSearchRadius(0.0);\n\tcpp.HandleSpecialCases(true);\n\tcpp.Init();\n\n\t// number of nodes\n\tint NN = m_dom->Nodes();\n\n\tdouble L = m_dom->InitialLength();\n\n\tbool bdone = false;\n\n\tm_dom->UpdateNodes();\n\n\tconst double snap_tol = 1e-6;\n\tconst int maxIter = 1000;\n\tint iter = 0;\n\twhile ((bdone == false) && (iter < maxIter))\n\t{\n\t\tbdone = true;\n\t\titer++;\n\n\t\t// skip first and last\n\t\tfor (int i = 1; i<NN - 1; ++i)\n\t\t{\n\t\t\tFENode& nd = m_dom->Node(i);\n\t\t\tNODE& nodeData = m_nodeData[i];\n\t\t\t\t\n\t\t\t// get nodal position\n\t\t\tvec3d x = nd.m_rt;\n\n\t\t\t// If the node is in contact, let's see if it remains in contact\n\t\t\tif (nodeData.pe != 0)\n\t\t\t{\n\t\t\t\t// TODO: project the node on the surface to make sure we are using the updated projection position\n\t\t\t\tFESurfaceElement& mel = *nodeData.pe;\n\t\t\t\tdouble r = nodeData.proj[0];\n\t\t\t\tdouble s = nodeData.proj[1];\n\n\t\t\t\t// find the position of neighbors\n\t\t\t\tvec3d ra = m_dom->Node(i - 1).m_rt;\n\t\t\t\tvec3d rb = m_dom->Node(i + 1).m_rt;\n\n\t\t\t\t// evaluate center\n\t\t\t\tvec3d c = (ra + rb)*0.5;\n\n\t\t\t\t// try the same element first\n\t\t\t\tFESurfaceElement* pe = nodeData.pe;\n\t\t\t\tvec3d q = m_surf.ProjectToSurface(mel, c, r, s);\n\t\t\t\tif (m_surf.IsInsideElement(mel, r, s, 0.05) == false)\n\t\t\t\t{\n\t\t\t\t\t// project onto surface\n\t\t\t\t\tvec2d rs(r,s);\n\t\t\t\t\tpe = cpp.Project(c, q, rs);\n\t\t\t\t\tr = rs.x();\n\t\t\t\t\ts = rs.y();\n\t\t\t\t}\n\n\t\t\t\tif (pe)\n\t\t\t\t{\n\t\t\t\t\tvec3d nu = m_surf.SurfaceNormal(*pe, r, s);\n\t\t\t\t\tdouble gap = nu*(c - q);\n\t\t\t\t\tif (gap < 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnodeData.pe = pe;\n\t\t\t\t\t\tnodeData.proj[0] = r;\n\t\t\t\t\t\tnodeData.proj[1] = s;\n\t\t\t\t\t\tnodeData.nu = nu;\n\t\t\t\t\t\tnodeData.q = q;\n\n\t\t\t\t\t\t// see if the node moved significantly\n\t\t\t\t\t\tdouble d = (q - x).norm();\n\t\t\t\t\t\tif (d / L > 1e-2) bdone = false;\n\t\t\t\t\t}\n\t\t\t\t\telse if (gap > snap_tol)\n\t\t\t\t\t{\n\t\t\t\t\t\t// projection failed, release node and redo\n\t\t\t\t\t\tm_dom->AnchorNode(i, false);\n\t\t\t\t\t\tnodeData.pe = 0;\n\t\t\t\t\t\tbdone = false;\n\t\t\t\t\t}\n\t\t\t\t\telse if ((x - c).norm2() > snap_tol)\n\t\t\t\t\t{\n\t\t\t\t\t\t// projection failed, release node and redo\n\t\t\t\t\t\tm_dom->AnchorNode(i, false);\n\t\t\t\t\t\tnodeData.pe = 0;\n\t\t\t\t\t\tbdone = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// projection faild, release node and redo\n\t\t\t\t\tm_dom->AnchorNode(i, false);\n\t\t\t\t\tnodeData.pe = 0;\n\t\t\t\t\tbdone = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// see it the node establishes contact\n\t\t\t\tvec2d rs(0, 0); vec3d q;\n\t\t\t\tFESurfaceElement* pe = cpp.Project(x, q, rs);\n\n\t\t\t\t// if it does, update nodal data\n\t\t\t\t// and position the node\n\t\t\t\tif (pe)\n\t\t\t\t{\n\t\t\t\t\tvec3d nu = m_surf.SurfaceNormal(*pe, rs.x(), rs.y());\n\t\t\t\t\tif (nu*(x - q) < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnodeData.pe = pe;\n\t\t\t\t\t\tnodeData.proj[0] = rs.x();\n\t\t\t\t\t\tnodeData.proj[1] = rs.y();\n\t\t\t\t\t\tnodeData.nu = nu;\n\t\t\t\t\t\tnodeData.q = q;\n\n\t\t\t\t\t\tm_dom->AnchorNode(i, true);\n\t\t\t\t\t\tbdone = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// update anchor positions\n\t\tfor (int i=1; i<NN-1; ++i)\n\t\t{\n\t\t\tNODE& nodeData = m_nodeData[i];\n\t\t\tif (nodeData.pe != 0) m_dom->SetNodePosition(i, nodeData.q);\n\t\t}\n\n\t\t// tell the domain to update the positions of the free nodes\n\t\tm_dom->UpdateNodes();\n\t}\n\n//\tassert(iter < maxIter);\n\tfeLog(\"iterations = %d\\n\", iter);\n}\n"
  },
  {
    "path": "FEBioMech/FEDiscreteContact.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceConstraint.h>\n#include \"FEContactSurface.h\"\n#include \"FEDeformableSpringDomain.h\"\n\n//-----------------------------------------------------------------------------\nclass FEDiscreteSet;\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEDiscreteContactSurface : public FEContactSurface\n{\npublic:\n\t//! constructor\n\tFEDiscreteContactSurface(FEModel* fem);\n\n\t//! Initialization\n\tbool Init();\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEDiscreteContact : public FESurfaceConstraint\n{\n\tstruct NODE\n\t{\n\t\tint\t\t\t\t\tnid;\t//!< (local) node ID\n\t\tFESurfaceElement*\tpe;\t\t//!< secondary surface element\n\t\tdouble\t\t\t\tgap;\t//!< gap distance\n\t\tdouble\t\t\t\tLm;\t\t//!< Lagrange multiplier\n\t\tvec3d\t\t\t\tnu;\t\t//!< normal at secondary surface projection\n\t\tvec3d\t\t\t\tq;\t\t//!< projection point\n\t\tdouble\t\t\t\tproj[2];\t//!< iso-parametric coordinates of projection point\n\t};\n\npublic:\n\tFEDiscreteContact(FEModel* pfem);\n\npublic:\n\tbool Init() override;\n\tvoid Activate() override;\n\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid Update(const FETimeInfo& tp);\n\n\tvoid SetDiscreteSet(FEDiscreteSet* pset);\n\n\tFESurface* GetSurface() override { return &m_surf; }\n\nprotected:\n\tvoid ProjectSurface(bool bupseg);\n\tvoid ContactNodalForce    (NODE& nodeData, FESurfaceElement& mel, vector<double>& fe);\n\tvoid ContactNodalStiffness(NODE& nodeData, FESurfaceElement& mel, matrix& ke);\n\nprotected:\n\tFEDiscreteContactSurface\tm_surf;\n\tvector<NODE>\tm_Node;\n\tdouble\tm_normg0;\n\tbool\tm_bfirst;\n\nprotected:\n\tbool\tm_blaugon;\t//!< augmentation flag\n\tdouble\tm_altol;\t//!< augmentation tolerance\n\tdouble\tm_penalty;\t//!< penalty parameter\n\tdouble\tm_gaptol;\t//!< gap tolerance\n\tint\t\tm_naugmin;\t//!< minimum number of augmentations\n\tint\t\tm_naugmax;\t//!< maximum number of augmentations\n\tint\t\tm_nsegup;\t//!< number of segment updates (or zero)\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEDiscreteContact2 : public FESurfaceConstraint\n{\n\tstruct NODE\n\t{\n\t\tint\t\tnode;\t\t\t\t// node index (local ID into discrete domain)\n\t\tFESurfaceElement*\tpe;\t\t// secondary surface element\n\t\tdouble\tproj[2];\t\t\t// natural coordinates of projection\n\t\tvec3d\tnu;\t\t\t\t\t// normal on secondary surface\n\t\tvec3d\tq;\t\t\t\t\t// new position\n\t};\n\npublic:\n\tFEDiscreteContact2(FEModel* fem);\n\n\tbool Init() override;\n\tvoid Activate() override;\n\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid Update(const FETimeInfo& tp);\n\tbool Augment(int naug, const FETimeInfo& tp) override { return true; }\n\n\tvoid SetDiscreteDomain(FEDeformableSpringDomain2* dom) { m_dom = dom; }\n\tFESurface* GetSurface() override { return &m_surf; }\n\nprotected:\n\tvoid ProjectNodes();\n\nprotected:\n\tFEDiscreteContactSurface\tm_surf;\n\tFEDeformableSpringDomain2*\tm_dom;\n\tvector<NODE>\tm_nodeData;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDiscreteElasticDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDiscreteElasticDomain.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n\nFEDiscreteElasticDomain::FEDiscreteElasticDomain(FEModel* fem) : FEDiscreteDomain(fem), FEElasticDomain(fem), m_dofU(fem), m_dofR(fem), m_dof(fem)\n{\n\tm_pMat = nullptr;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (fem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the material (overridden from FEDomain)\nFEMaterial* FEDiscreteElasticDomain::GetMaterial()\n{\n\treturn m_pMat;\n}\n\n//-----------------------------------------------------------------------------\n//! set the material\nvoid FEDiscreteElasticDomain::SetMaterial(FEMaterial* pmat)\n{\n\tm_pMat = dynamic_cast<FEDiscreteElasticMaterial*>(pmat);\n\tassert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n// get the total dofs\nconst FEDofList& FEDiscreteElasticDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteElasticDomain::UnpackLM(FEElement &el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N * 6);\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = m_pMesh->Node(el.m_node[i]);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3 * i] = id[m_dofU[0]];\n\t\tlm[3 * i + 1] = id[m_dofU[1]];\n\t\tlm[3 * i + 2] = id[m_dofU[2]];\n\n\t\t// rigid rotational dofs\n\t\tlm[3 * N + 3 * i] = id[m_dofR[0]];\n\t\tlm[3 * N + 3 * i + 1] = id[m_dofR[1]];\n\t\tlm[3 * N + 3 * i + 2] = id[m_dofR[2]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteElasticDomain::Activate()\n{\n\tfor (int i = 0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteElasticDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tFEDiscreteDomain::PreSolveUpdate(timeInfo);\n\tUpdate(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the forces due to discrete elements (i.e. springs)\n\nvoid FEDiscreteElasticDomain::InternalForces(FEGlobalVector& R)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tvector<double> fe(6);\n\tvec3d u1, u2;\n\n\tvector<int> en(2), lm(6);\n\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// get the material point data\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\t\tFEDiscreteElasticMaterialPoint& ep = *mp.ExtractData<FEDiscreteElasticMaterialPoint>();\n\n\t\t\t// evaluate the force\n\t\t\tvec3d F = ep.m_Ft;\n\n\t\t\t// set up the force vector\n\t\t\tfe[0] = F.x;\n\t\t\tfe[1] = F.y;\n\t\t\tfe[2] = F.z;\n\t\t\tfe[3] = -F.x;\n\t\t\tfe[4] = -F.y;\n\t\t\tfe[5] = -F.z;\n\n\t\t\t// setup the node vector\n\t\t\ten[0] = el.m_node[0];\n\t\t\ten[1] = el.m_node[1];\n\n\t\t\t// set up the LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble element\n\t\t\tR.Assemble(en, lm, fe);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the discrete element stiffness\n\nvoid FEDiscreteElasticDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tFEElementMatrix ke;\n\tke.resize(6, 6);\n\tke.zero();\n\n\tvector<int> en(2), lm(6);\n\n\t// loop over all discrete elements\n\tfor (size_t i = 0; i<m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// get the material point data\n\t\t\tFEDiscreteMaterialPoint& mp = *el.GetMaterialPoint(0)->ExtractData<FEDiscreteMaterialPoint>();\n\n\t\t\t// evaluate the stiffness\n\t\t\tmat3d A = m_pMat->Stiffness(mp);\n\n\t\t\tke.zero();\n\t\t\tke.add(0, 0, A); ke.add(0, 3, -A);\n\t\t\tke.add(3, 0, -A); ke.add(3, 3, A);\n\n\t\t\t// setup the node vector\n\t\t\ten[0] = el.m_node[0];\n\t\t\ten[1] = el.m_node[1];\n\n\t\t\t// set up the LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble the element into the global system\n\t\t\tke.SetNodes(en);\n\t\t\tke.SetIndices(lm);\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n}\n\nbool FEDiscreteElasticDomain::Init()\n{\n\tif (FEDiscreteDomain::Init() == false) return false;\n\tFETimeInfo& tp = GetFEModel()->GetTime();\n\tUpdate(tp);\n\treturn true;\n}\n\n//! update domain data\nvoid FEDiscreteElasticDomain::Update(const FETimeInfo& tp)\n{\n\tFEMesh& mesh = *m_pMesh;\n\n\tdouble dt = tp.timeIncrement;\n\n\t// loop over all discrete elements\n\tfor (size_t i = 0; i < m_Elem.size(); ++i)\n\t{\n\t\t// get the discrete element\n\t\tFEDiscreteElement& el = m_Elem[i];\n\n\t\t// only process active springs\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// get the material point data\n\t\t\tFEDiscreteElasticMaterialPoint& mp = *el.GetMaterialPoint(0)->ExtractData<FEDiscreteElasticMaterialPoint>();\n\n\t\t\t// get the nodes of the element\n\t\t\tFENode& n1 = mesh.Node(el.m_node[0]);\n\t\t\tFENode& n2 = mesh.Node(el.m_node[1]);\n\n\t\t\t// get the nodal positions\n\t\t\tvec3d& rt1 = n1.m_rt;\n\t\t\tvec3d& rt2 = n2.m_rt;\n\n\t\t\t// get the previous nodal positions\n\t\t\tvec3d rp1 = n1.m_rp;\n\t\t\tvec3d rp2 = n2.m_rp;\n\n\t\t\t// get the initial nodal positions\n\t\t\tvec3d ri1 = n1.m_r0;\n\t\t\tvec3d ri2 = n2.m_r0;\n\n\t\t\tmp.m_drt = rt2 - rt1;\n\t\t\tmp.m_drp = rp2 - rp1;\n\t\t\tmp.m_dr0 = ri2 - ri1;\n\n\t\t\tmp.m_dvt = (mp.m_drt - mp.m_drp) / dt;\n\n\t\t\t// evaluate the force\n\t\t\tmp.m_Ft = m_pMat->Force(mp);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEDiscreteElasticDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDiscreteElasticMaterial.h\"\n#include <FECore/FEDiscreteDomain.h>\n#include \"FEElasticDomain.h\"\n\nclass FEDiscreteElasticDomain : public FEDiscreteDomain, public FEElasticDomain\n{\npublic:\n\tFEDiscreteElasticDomain(FEModel* fem);\n\n\t//! Unpack LM data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\tvoid Activate() override;\n\n\t// get the total dofs\n\tconst FEDofList& GetDOFList() const override;\n\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\npublic: // overridden from FEElasticDomain\n\n\t\t//! calculate stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override {}\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n\n\t//! Calculates inertial forces for dynamic problems | todo implement (removed assert DSR)\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override { }\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculate bodyforces (not used since springs are considered mass-less)\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n\nprotected:\n\tFEDiscreteElasticMaterial*\tm_pMat;\n\tFEDofList\tm_dofU, m_dofR, m_dof;\n};\n"
  },
  {
    "path": "FEBioMech/FEDiscreteElasticMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDiscreteElasticMaterial.h\"\n\nFEMaterialPointData* FEDiscreteElasticMaterialPoint::Copy()\n{\n\tFEDiscreteElasticMaterialPoint* pt = new FEDiscreteElasticMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//=============================================================================\nFEDiscreteElasticMaterial::FEDiscreteElasticMaterial(FEModel* pfem) : FEDiscreteMaterial(pfem)\n{\n\n}\n\nFEMaterialPointData* FEDiscreteElasticMaterial::CreateMaterialPointData()\n{\n\treturn new FEDiscreteElasticMaterialPoint;\n}\n\n//=============================================================================\n//\t\t\t\t\tFECompositeDiscreteElasticMaterial\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FECompositeDiscreteMaterial, FEDiscreteElasticMaterial)\n\tADD_PROPERTY(m_mats, \"mat\");\nEND_FECORE_CLASS();\n\n\nFECompositeDiscreteMaterial::FECompositeDiscreteMaterial(FEModel* pfem) : FEDiscreteElasticMaterial(pfem)\n{\n\n}\n\nvec3d FECompositeDiscreteMaterial::Force(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d force; \n\n\tfor(auto mat : m_mats)\n\t{\n\t\tforce += mat->Force(mp);\n\t}\n\n\treturn force;\n}\n\nmat3d FECompositeDiscreteMaterial::Stiffness(FEDiscreteMaterialPoint& mp)\n{\n\tmat3d stiffness; stiffness.zero();\n\n\tfor(auto mat : m_mats)\n\t{\n\t\tstiffness += mat->Stiffness(mp);\n\t}\n\n\treturn stiffness;\n}\n\ndouble FECompositeDiscreteMaterial::StrainEnergy(FEDiscreteMaterialPoint& mp)\n{\n\tdouble energy = 0; \n\n\tfor(auto mat : m_mats)\n\t{\n\t\tenergy += mat->StrainEnergy(mp);\n\t}\n\n\treturn energy;\n}"
  },
  {
    "path": "FEBioMech/FEDiscreteElasticMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEDiscreteMaterial.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FEDiscreteElasticMaterialPoint : public FEDiscreteMaterialPoint\n{\npublic:\n\tFEMaterialPointData* Copy() override;\n\npublic:\n\tvec3d\tm_Ft;\t// current force\t\n};\n\n//-----------------------------------------------------------------------------\n//! material class for discrete elements\nclass FEBIOMECH_API FEDiscreteElasticMaterial : public FEDiscreteMaterial\n{\npublic:\n\tFEDiscreteElasticMaterial(FEModel* pfem);\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// evaluate the force at a discrete element\n\tvirtual vec3d Force(FEDiscreteMaterialPoint& mp) = 0;\n\n\t// evaluate the stiffness at a discrete element (= dF / dr)\n\tvirtual mat3d Stiffness(FEDiscreteMaterialPoint& mp) = 0;\n\n\t// evaluate the strain energy at a discrete element\n\tvirtual double StrainEnergy(FEDiscreteMaterialPoint& mp) {return 0.0;}\n};\n\n//-----------------------------------------------------------------------------\n//! composite material class for discrete elements\nclass FEBIOMECH_API FECompositeDiscreteMaterial : public FEDiscreteElasticMaterial\n{\npublic:\n\tFECompositeDiscreteMaterial(FEModel* pfem);\n\n\t// evaluate the force at a discrete element\n\tvec3d Force(FEDiscreteMaterialPoint& mp) override;\n\n\t// evaluate the stiffness at a discrete element (= dF / dr)\n\tmat3d Stiffness(FEDiscreteMaterialPoint& mp) override;\n\n\t// evaluate the strain energy at a discrete element\n\tdouble StrainEnergy(FEDiscreteMaterialPoint& mp) override;\n\nprivate:\n\tstd::vector<FEDiscreteElasticMaterial*> m_mats;\n\nDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDiscreteElementMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDiscreteElementMaterial.h\"\n#include <FECore/FEModel.h>\n\nBEGIN_FECORE_CLASS(FEDiscreteContractileMaterial, FEDiscreteElasticMaterial)\n\tADD_PARAMETER(m_Vmax, \"Vmax\"  );\n\tADD_PARAMETER(m_ac  , \"ac\"  );\n\tADD_PARAMETER(m_Fmax, \"Fmax\");\n\tADD_PARAMETER(m_Ksh , \"Ksh\" );\n\tADD_PARAMETER(m_Lmax, \"Lmax\");\n\tADD_PARAMETER(m_L0  , \"L0\" );\n\n\tADD_PROPERTY(m_Sv , \"Sv\" , FEProperty::Optional);\n\tADD_PROPERTY(m_Ftl, \"Ftl\", FEProperty::Optional);\n\tADD_PROPERTY(m_Ftv, \"Fvl\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\nFEDiscreteContractileMaterial::FEDiscreteContractileMaterial(FEModel* fem) : FEDiscreteElasticMaterial(fem)\n{\n\tm_Vmax = 1.0;\n\tm_ac   = 0.0;\n\tm_Fmax = 1.0;\n\tm_L0   = 0.0;\n\tm_Ksh  = 1.0;\n\tm_Lmax = 1.0;\n\n\tm_Sv = nullptr;\n\tm_Ftl = nullptr;\n\tm_Ftv = nullptr;\n}\n\ndouble FEDiscreteContractileMaterial::passive_force(double L, double V)\n{\n\treturn (L > 1.0 ? m_Fmax * ((exp(m_Ksh*(L - 1.0) / m_Lmax) - 1.0) / (exp(m_Ksh) - 1.0)) : 0.0);\n}\n\ndouble FEDiscreteContractileMaterial::passive_force_deriv_L(double L, double V)\n{\n\tif (L < 1.0) return 0.0;\n\tdouble dF = (m_Fmax / (exp(m_Ksh) - 1.0)) * ((m_Ksh / m_Lmax)*(exp(m_Ksh*(L - 1.0) / m_Lmax)));\n\treturn dF;\n}\n\ndouble FEDiscreteContractileMaterial::passive_force_deriv_V(double L, double V)\n{\n\treturn 0.0;\n}\n\ndouble FEDiscreteContractileMaterial::active_force(double L, double V)\n{\n\tdouble Ftl = (m_Ftl ? m_Ftl->value(L) : 1.0);\n\tdouble Ftv = (m_Ftv ? m_Ftv->value(V) : 1.0);\n\n\treturn m_ac*m_Fmax*Ftl*Ftv;\n}\n\ndouble FEDiscreteContractileMaterial::active_force_deriv_L(double L, double V)\n{\n\tdouble dFtl = (m_Ftl ? m_Ftl->derive(L) : 0.0);\n\tdouble Ftv = (m_Ftv ? m_Ftv->value(V) : 1.0);\n\treturn m_ac*m_Fmax*dFtl*Ftv;\n}\n\ndouble FEDiscreteContractileMaterial::active_force_deriv_V(double L, double V)\n{\n\tdouble Ftl = (m_Ftl ? m_Ftl->value(L) : 1.0);\n\tdouble dFtv = (m_Ftv ? m_Ftv->derive(V) : 0.0);\n\treturn m_ac*m_Fmax*Ftl*dFtv;\n}\n\ndouble FEDiscreteContractileMaterial::force_deriv_L(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e0 = mp.m_dr0; double L0 = e0.unit();\n\tvec3d et = mp.m_drt; double Lm = et.unit();\n\tif (m_L0 != 0.0) L0 = m_L0;\n\n\t// stretch\n\tdouble L = Lm / L0;\n\n\t// normalized velocity\n\tdouble Vm = mp.m_dvt*et;\n\tdouble Sv = (m_Sv ? m_Sv->value(m_ac) : 1.0);\n\tdouble V = Vm / (m_Vmax * Sv);\n\n\treturn passive_force_deriv_L(L, V)/L0 + active_force_deriv_L(L, V)/L0;\n}\n\ndouble FEDiscreteContractileMaterial::force_deriv_V(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e0 = mp.m_dr0; double L0 = e0.unit();\n\tvec3d et = mp.m_drt; double Lm = et.unit();\n\tif (m_L0 != 0.0) L0 = m_L0;\n\n\t// stretch\n\tdouble L = Lm / L0;\n\n\t// normalized velocity\n\tdouble Vm = mp.m_dvt*et;\n\tdouble Sv = (m_Sv ? m_Sv->value(m_ac) : 1.0);\n\tdouble V = Vm / (m_Vmax * Sv);\n\n\treturn passive_force_deriv_V(L, V) / (m_Vmax * Sv) + active_force_deriv_V(L, V) / (m_Vmax * Sv);\n}\n\ndouble FEDiscreteContractileMaterial::force(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e0 = mp.m_dr0; double L0 = e0.unit();\n\tvec3d et = mp.m_drt; double Lm = et.unit();\n\tif (m_L0 != 0.0) L0 = m_L0;\n\n\t// stretch\n\tdouble L = Lm / L0;\n\n\t// normalized velocity\n\tdouble Vm = mp.m_dvt*et;\n\tdouble Sv = (m_Sv ? m_Sv->value(m_ac) : 1.0);\n\tdouble V = Vm / (m_Vmax * Sv);\n\n\t// passive element\n\tdouble Fp = passive_force(L, V);\n\n\t// active element\n\tdouble Fc = active_force(L, V);\n\n\t// add them up together\n\tdouble F = Fp + Fc;\n\n\t// return\n\treturn F;\n}\n\n// evaluate the force at a discrete element\nvec3d FEDiscreteContractileMaterial::Force(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e = mp.m_drt; e.unit();\n\tdouble F = force(mp);\n\treturn e*F;\n}\n\n// evaluate the stiffness at a discrete element (= dF / dr)\nmat3d FEDiscreteContractileMaterial::Stiffness(FEDiscreteMaterialPoint& mp)\n{\n\tdouble F = force(mp);\n\n\tvec3d e = mp.m_drt;\n\tdouble L = e.unit();\n\n\tmat3ds exe = dyad(e);\n\tmat3dd I(1.0);\n\n\tmat3ds E = (I - exe)/L;\n\n\tvec3d v = mp.m_dvt;\n\tdouble V = mp.m_dvt * e;\n\n\tdouble Fl = force_deriv_L(mp);\n\tdouble Fv = force_deriv_V(mp);\n\n\tdouble dt = GetFEModel()->GetTime().timeIncrement;\n\n\tvec3d f = e*(-Fl) - (e/dt + v/L + e*(L*V))*Fv;\n\n\treturn E*F - (e & f);\n}\n"
  },
  {
    "path": "FEBioMech/FEDiscreteElementMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include \"FEDiscreteElasticMaterial.h\"\n#include <FECore/FEFunction1D.h>\n\nclass FEDiscreteContractileMaterial : public FEDiscreteElasticMaterial\n{\npublic:\n\tFEDiscreteContractileMaterial(FEModel* fem);\n\n\t// evaluate the force at a discrete element\n\tvec3d Force(FEDiscreteMaterialPoint& mp) override;\n\n\t// evaluate the stiffness at a discrete element (= dF / dr)\n\tmat3d Stiffness(FEDiscreteMaterialPoint& mp) override;\n\nprivate:\n\tdouble force(FEDiscreteMaterialPoint& mp);\n\tdouble force_deriv_L(FEDiscreteMaterialPoint& mp);\n\tdouble force_deriv_V(FEDiscreteMaterialPoint& mp);\n\tdouble passive_force(double L, double V);\n\tdouble active_force(double L, double V);\n\tdouble passive_force_deriv_L(double L, double V);\n\tdouble passive_force_deriv_V(double L, double V);\n\tdouble active_force_deriv_L(double L, double V);\n\tdouble active_force_deriv_V(double L, double V);\n\nprivate:\n\tdouble\t\t\tm_Vmax;\t\t// maximum shortening velocity\n\tdouble\t\t\tm_ac;\t\t// activation level\n\tdouble\t\t\tm_Fmax;\t\t// max force\n\tdouble\t\t\tm_Ksh;\t\t// shape parameter that determines the rise of exponential in passive element\n\tdouble\t\t\tm_Lmax;\t\t// relative length at which Fmax occurs\n\tdouble\t\t\tm_L0;\t\t// initial reference length\n\n\tFEFunction1D*\tm_Sv;\t\t// max velocity scale\n\tFEFunction1D*\tm_Ftl;\t\t// normalized tension-length curve for active element\n\tFEFunction1D*\tm_Ftv;\t\t// normalized tension-velocity curve for active element\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDistanceConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDistanceConstraint.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FEBioMech.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEDistanceConstraint, FENLConstraint);\n\tADD_PARAMETER(m_blaugon, \"laugon\" ); \n\tADD_PARAMETER(m_atol   , \"augtol\" );\n\tADD_PARAMETER(m_eps    , \"penalty\");\n\tADD_PARAMETER(m_nodeID , 2, \"node\");\n\tADD_PARAMETER(m_nminaug, \"minaug\");\n\tADD_PARAMETER(m_nmaxaug, \"maxaug\");\n\tADD_PARAMETER(m_target, \"target\");\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEDistanceConstraint::FEDistanceConstraint(FEModel* pfem) : FENLConstraint(pfem), m_dofU(pfem)\n{\n\tm_eps = 0.0;\n\tm_atol = 0.01;\n\tm_blaugon = false;\n\tm_node[0] = -1;\n\tm_node[1] = -1;\n\tm_nodeID[0] = -1;\n\tm_nodeID[1] = -1;\n\tm_l0 = 0.0;\n\tm_Lm = 0.0;\n\tm_nminaug = 0;\n\tm_nmaxaug = 10;\n\tm_target = 0;\n\tm_brelative = true;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures. \nbool FEDistanceConstraint::Init()\n{\n\t// get the FE mesh\n\tFEMesh& mesh = GetMesh();\n\tint NN = mesh.Nodes();\n\n\t// make sure the nodes are valid\n\tm_node[0] = mesh.FindNodeIndexFromID(m_nodeID[0]);\n\tm_node[1] = mesh.FindNodeIndexFromID(m_nodeID[1]);\n\tif ((m_node[0] < 0)||(m_node[0] >= NN)) return false;\n\tif ((m_node[1] < 0)||(m_node[1] >= NN)) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDistanceConstraint::Activate()\n{\n\t// don't forget to call base class\n\tFENLConstraint::Activate();\n\n\t// get the FE mesh\n\tFEMesh& mesh = GetMesh();\n\tint NN = mesh.Nodes();\n\n\t// get the initial position of the two nodes\n\tvec3d ra = mesh.Node(m_node[0]).m_rt;\n\tvec3d rb = mesh.Node(m_node[1]).m_rt;\n\n\t// set the initial length\n\tm_l0 = (ra - rb).norm();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDistanceConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// get the FE mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// get the two nodes\n\tFENode& nodea = mesh.Node(m_node[0]);\n\tFENode& nodeb = mesh.Node(m_node[1]);\n\n\t// get the current position of the two nodes\n\tvec3d ra = nodea.m_rt;\n\tvec3d rb = nodeb.m_rt;\n\n\t// calculate the force\n\tdouble lt = (ra - rb).norm();\n\n\tdouble l0 = (m_brelative ? m_l0 + m_target : m_target);\n\tif (l0 < 0) l0 = 0;\n\n\tdouble Lm = m_Lm + m_eps*(lt - l0);\n\tvec3d Fc = (ra - rb)*(Lm/lt);\n\n\t// setup the \"element\" force vector\n\tvector<double> fe(6);\n\tfe[0] = -Fc.x;\n\tfe[1] = -Fc.y;\n\tfe[2] = -Fc.z;\n\tfe[3] =  Fc.x;\n\tfe[4] =  Fc.y;\n\tfe[5] =  Fc.z;\n\n\t// setup the LM vector\n\tvector<int> lm(6);\n\tlm[0] = nodea.m_ID[m_dofU[0]];\n\tlm[1] = nodea.m_ID[m_dofU[1]];\n\tlm[2] = nodea.m_ID[m_dofU[2]];\n\tlm[3] = nodeb.m_ID[m_dofU[0]];\n\tlm[4] = nodeb.m_ID[m_dofU[1]];\n\tlm[5] = nodeb.m_ID[m_dofU[2]];\n\n\t// setup element vector\n\tvector<int> en(2);\n\ten[0] = m_node[0];\n\ten[1] = m_node[1];\n\n\t// add element force vector to global force vector\n\tR.Assemble(en, lm, fe);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDistanceConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\t// get the FE mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// get the two nodes\n\tFENode& nodea = mesh.Node(m_node[0]);\n\tFENode& nodeb = mesh.Node(m_node[1]);\n\n\t// get the current position of the two nodes\n\tvec3d ra = nodea.m_rt;\n\tvec3d rb = nodeb.m_rt;\n\tvec3d rab = ra - rb;\n\n\t// calculate the Lagrange mulitplier\n\tdouble lt = rab.norm();\n\tdouble l3 = lt*lt*lt;\n\n\tdouble l0 = (m_brelative ? m_l0 + m_target : m_target);\n\tif (l0 < 0) l0 = 0;\n\n\tdouble Lm = m_Lm + m_eps*(lt - l0);\n\n\t// calculate the stiffness\n\tmat3d kab;\n\tkab[0][0] = (m_eps*l0*rab.x*rab.x/l3 + Lm/lt);\n\tkab[1][1] = (m_eps*l0*rab.y*rab.y/l3 + Lm/lt);\n\tkab[2][2] = (m_eps*l0*rab.z*rab.z/l3 + Lm/lt);\n\tkab[0][1] = kab[1][0] = (m_eps*l0*rab.x*rab.y/l3);\n\tkab[0][2] = kab[2][0] = (m_eps*l0*rab.x*rab.z/l3);\n\tkab[1][2] = kab[2][1] = (m_eps*l0*rab.y*rab.z/l3);\n\n\t// element stiffness matrix\n\tFEElementMatrix ke;\n\tke.resize(6, 6);\n\tke.zero();\n\tke[0][0] = kab[0][0]; ke[0][1] = kab[0][1]; ke[0][2] = kab[0][2]; ke[0][3] = -kab[0][0]; ke[0][4] = -kab[0][1]; ke[0][5] = -kab[0][2];\n\tke[1][0] = kab[1][0]; ke[1][1] = kab[1][1]; ke[1][2] = kab[1][2]; ke[1][3] = -kab[1][0]; ke[1][4] = -kab[1][1]; ke[1][5] = -kab[1][2];\n\tke[2][0] = kab[2][0]; ke[2][1] = kab[2][1]; ke[2][2] = kab[2][2]; ke[2][3] = -kab[2][0]; ke[2][4] = -kab[2][1]; ke[2][5] = -kab[2][2];\n\n\tke[3][0] = -kab[0][0]; ke[3][1] = -kab[0][1]; ke[3][2] = -kab[0][2]; ke[3][3] = kab[0][0]; ke[3][4] = kab[0][1]; ke[3][5] = kab[0][2];\n\tke[4][0] = -kab[1][0]; ke[4][1] = -kab[1][1]; ke[4][2] = -kab[1][2]; ke[4][3] = kab[1][0]; ke[4][4] = kab[1][1]; ke[4][5] = kab[1][2];\n\tke[5][0] = -kab[2][0]; ke[5][1] = -kab[2][1]; ke[5][2] = -kab[2][2]; ke[5][3] = kab[2][0]; ke[5][4] = kab[2][1]; ke[5][5] = kab[2][2];\n\n\t// setup the LM vector\n\tvector<int> lm(6);\n\tlm[0] = nodea.m_ID[m_dofU[0]];\n\tlm[1] = nodea.m_ID[m_dofU[1]];\n\tlm[2] = nodea.m_ID[m_dofU[2]];\n\tlm[3] = nodeb.m_ID[m_dofU[0]];\n\tlm[4] = nodeb.m_ID[m_dofU[1]];\n\tlm[5] = nodeb.m_ID[m_dofU[2]];\n\n\t// setup element vector\n\tvector<int> en(2);\n\ten[0] = m_node[0];\n\ten[1] = m_node[1];\n\n\t// assemble element matrix in global stiffness matrix\n\tke.SetNodes(en);\n\tke.SetIndices(lm);\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FEDistanceConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we are augmenting\n\tif ((m_blaugon == false) || (m_atol <= 0.0)) return true;\n\n\t// get the FE mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// get the two nodes\n\tFENode& nodea = mesh.Node(m_node[0]);\n\tFENode& nodeb = mesh.Node(m_node[1]);\n\n\t// get the current position of the two nodes\n\tvec3d ra = nodea.m_rt;\n\tvec3d rb = nodeb.m_rt;\n\n\t// calculate the Lagrange multipler\n\tdouble l = (ra - rb).norm();\n\n\tdouble l0 = (m_brelative ? m_l0 + m_target : m_target);\n\tif (l0 < 0) l0 = 0;\n\n\tdouble Lm = m_Lm + m_eps*(l - l0);\n\n\t// calculate force\n\tvec3d Fc = (ra - rb)*(Lm/l);\n\n\t// calculate relative error\n\tbool bconv = false;\n\tdouble err = fabs((Lm - m_Lm)/Lm);\n\tif (err < m_atol) bconv = true;\n\n\tfeLog(\"\\ndistance constraint:\\n\");\n\tfeLog(\"\\tmultiplier= %lg (error = %lg / %lg)\\n\", Lm, err, m_atol);\n\tfeLog(\"\\tforce     = %lg, %lg, %lg\\n\", Fc.x, Fc.y, Fc.z);\n\tfeLog(\"\\tdistance  = %lg (L0 = %lg)\\n\", l, m_l0);\n\n\t// check convergence\n\tif (m_nminaug > naug) bconv = false;\n\tif (m_nmaxaug <= naug) bconv = true;\n\n\t// update Lagrange multiplier\n\t// (only when we did not converge)\n\tif (bconv == false) m_Lm = Lm;\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDistanceConstraint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tFEMesh& mesh = GetMesh();\n\tvector<int> lm(6);\n\tFENode& n0 = mesh.Node(m_node[0]);\n\tlm[0] = n0.m_ID[m_dofU[0]];\n\tlm[1] = n0.m_ID[m_dofU[1]];\n\tlm[2] = n0.m_ID[m_dofU[2]];\n\tFENode& n1 = mesh.Node(m_node[1]);\n\tlm[3] = n1.m_ID[m_dofU[0]];\n\tlm[4] = n1.m_ID[m_dofU[1]];\n\tlm[5] = n1.m_ID[m_dofU[2]];\n    M.build_add(lm);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDistanceConstraint::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\tar & m_Lm; \n}\n\n//-----------------------------------------------------------------------------\nvoid FEDistanceConstraint::Reset()\n{\n\tm_Lm = 0.0;\n}\n"
  },
  {
    "path": "FEBioMech/FEDistanceConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENLConstraint.h>\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n// This class implements a constraint that enforces the distance between two nodes\nclass FEDistanceConstraint : public FENLConstraint\n{\npublic:\n\t//! constructor\n\tFEDistanceConstraint(FEModel* pfem);\n\n\tbool Init() override;\n\tvoid Activate() override;\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t// update state\n\tvoid Reset() override;\n\npublic:\n\tdouble\tm_eps;\t\t//!< penalty parameter\n\tdouble\tm_atol;\t\t//!< augmented Lagrangian tolerance\n\tbool\tm_blaugon;\t//!< augmentation flag\n\tint\t\tm_nodeID[2];\t//!< the IDs of the two nodes that are connected\n\tint\t\tm_nminaug;\t//!< min number of augmentations\n\tint\t\tm_nmaxaug;\t//!< max number of augmentations\n\tdouble\tm_target; //!< target distance\n\tbool\tm_brelative; //!< relative to initial distance?\n\n\tdouble\tm_l0;\t\t//!< reference length\n\tdouble\tm_Lm;\t\t//!< Lagrange multiplier\n\tint\t\tm_node[2];\t//!< nodal indices into mesh\n\n\tFEDofList\tm_dofU;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEDonnanEquilibrium.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDonnanEquilibrium.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\nFEMaterialPointData* FEDonnanEquilibriumMaterialPoint::Copy()\n{\n    FEDonnanEquilibriumMaterialPoint* pt = new FEDonnanEquilibriumMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\nvoid FEDonnanEquilibriumMaterialPoint::Init()\n{\n\tFEMaterialPointData::Init();\n    \n    // intialize data to zero\n    m_cF = 0;\n    m_osm = 0;\n    m_p = 0;\n    m_bpi = 0;\n}\n\nvoid FEDonnanEquilibriumMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_cFr & m_cF & m_osm & m_p & m_bpi;\n}\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDonnanEquilibrium, FEElasticMaterial)\n\tADD_PARAMETER(m_phiwr, FE_RANGE_LEFT_OPEN(0.0, 1.0), \"phiw0\");\n    ADD_PARAMETER(m_phisr, \"phis0\");\n\tADD_PARAMETER(m_cFr  , \"cF0\")->setUnits(UNIT_CONCENTRATION);\n\tADD_PARAMETER(m_bosm , FE_RANGE_GREATER_OR_EQUAL(0.0), \"bosm\")->setUnits(UNIT_CONCENTRATION);\n    ADD_PARAMETER(m_Phi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"Phi\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEDonnanEquilibrium::FEDonnanEquilibrium(FEModel* pfem) : FEElasticMaterial(pfem) \n{\n\tm_Rgas = 0; m_Tabs = 0; m_cFr = 0; m_phiwr = -1; m_phisr = -1;\n\tm_bnew = false; m_binit = false; m_Phi = 1;\n}\n\n//-----------------------------------------------------------------------------\n// FEDonnanEquilibrium\nbool FEDonnanEquilibrium::Init()\n{\n    if (!m_binit) {\n        if (m_phisr >= 0) {\n            m_bnew = true;\n            m_phiwr = 1 - m_phisr;  // use value at t=0 to initialize\n        }\n        m_binit = true;\n    }\n\n\tm_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n\tm_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\t\n\tif (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");   return false; }\n\t\n\treturn FEElasticMaterial::Init();\n}\n\nvoid FEDonnanEquilibrium::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tar & m_Rgas & m_Tabs & m_binit & m_bnew & m_phiwr;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEDonnanEquilibrium::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\t\n\t// calculate fixed charge density in current configuration\n    double cF;\n    if (m_bnew)\n        cF = m_phiwr*m_cFr(mp)/(J-m_phisr);\n    else\n        cF = m_phiwr*m_cFr(mp)/(J-1+m_phiwr);\n\t\n\t// calculate osmotic pressure\n\tdouble p = m_Rgas*m_Tabs*m_Phi*(sqrt(cF*cF+m_bosm*m_bosm) - m_bosm);\n\t\n\t// calculate T = -p*I\n\tmat3dd I(1.0);\t// identity tensor\n\tmat3ds s = -p*I;\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEDonnanEquilibrium::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\n\t// calculate fixed charge density in current configuration\n    double cF;\n    if (m_bnew)\n        cF = m_phiwr*m_cFr(mp)/(J-m_phisr);\n    else\n        cF = m_phiwr*m_cFr(mp)/(J-1+m_phiwr);\n\t\n\t// calculate osmotic pressure\n\tdouble tosm = sqrt(cF*cF+m_bosm*m_bosm);\t// tissue osmolarity\n\tdouble p = m_Rgas*m_Tabs*m_Phi*(tosm - m_bosm);\t// osmotic pressure\n\t\n\t// calculate derivative of osmotic pressure w.r.t. J\n    double bpi;\n    if (m_bnew)\n        bpi = m_Rgas*m_Tabs*m_Phi*J*cF*cF/(J-m_phisr)/tosm;\n    else\n        bpi = m_Rgas*m_Tabs*m_Phi*J*cF*cF/(J-1+m_phiwr)/tosm;\n\t\n\tmat3dd I(1.0);\t// Identity\n\t\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\t\n\t// calculate tangent osmotic modulus\n\ttens4ds c = bpi*IxI + p*(2.0*I4 - IxI);\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDonnanEquilibrium::FixedChargeDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // jacobian\n    double J = pt.m_J;\n    \n    // calculate fixed charge density in current configuration\n    double cF;\n    if (m_bnew)\n        cF = m_phiwr*m_cFr(mp)/(J-m_phisr);\n    else\n        cF = m_phiwr*m_cFr(mp)/(J-1+m_phiwr);\n    \n    return cF;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDonnanEquilibrium::OsmoticPressure(FEMaterialPoint& mp)\n{\n    double cF = FixedChargeDensity(mp);\n    \n    // calculate osmotic pressure\n    double p = m_Rgas*m_Tabs*m_Phi*(sqrt(cF*cF+m_bosm*m_bosm) - m_bosm);\n    \n    return p;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDonnanEquilibrium::OsmoticPressureTangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // jacobian\n    double J = pt.m_J;\n    \n    double cF = FixedChargeDensity(mp);\n    double tosm = Osmolarity(mp);\n    \n    // calculate derivative of osmotic pressure w.r.t. J\n    double bpi;\n    if (m_bnew)\n        bpi = m_Rgas*m_Tabs*m_Phi*J*cF*cF/(J-m_phisr)/tosm;\n    else\n        bpi = m_Rgas*m_Tabs*m_Phi*J*cF*cF/(J-1+m_phiwr)/tosm;\n    \n    return bpi;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDonnanEquilibrium::Osmolarity(FEMaterialPoint& mp)\n{\n    double cF = FixedChargeDensity(mp);\n    double tosm = sqrt(cF*cF+m_bosm*m_bosm);    // tissue osmolarity\n    \n    return tosm;\n}\n\n//-----------------------------------------------------------------------------\n// update Donnan equilibrium material point at each iteration\nvoid FEDonnanEquilibrium::UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp)\n{\n    // get the Donnan equilibrium material point data\n    FEDonnanEquilibriumMaterialPoint& pd = *pt.ExtractData<FEDonnanEquilibriumMaterialPoint>();\n    \n    pd.m_cFr = m_cFr(pt);\n    pd.m_cF = FixedChargeDensity(pt);\n    pd.m_osm = Osmolarity(pt);\n    pd.m_p = OsmoticPressure(pt);\n    pd.m_bpi = OsmoticPressureTangent(pt);\n}\n"
  },
  {
    "path": "FEBioMech/FEDonnanEquilibrium.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEMaterialPoint.h>\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores the Donnan equilibrium variables.\nclass FEDonnanEquilibriumMaterialPoint : public FEMaterialPointData\n{\npublic:\n    FEDonnanEquilibriumMaterialPoint(FEMaterialPointData*pt) : FEMaterialPointData(pt) {}\n    \n\tFEMaterialPointData* Copy();\n    \n    void Init();\n    \n    void Serialize(DumpStream& ar);\n    \npublic:\n    double      m_cFr;      //!< referential fixed-charge density\n    double      m_cF;       //!< fixed-charge density\n    double      m_osm;      //!< osmolarity\n    double      m_p;        //!< osmotic pressure\n    double      m_bpi;      //!< osmotic pressure tangent\n};\n\n//-----------------------------------------------------------------------------\n//! Material class that implements Donnan equilibrium. \n//! When used on its own (not in a solid mixture), this materials\n//! is intrinsically unstable\nclass FEDonnanEquilibrium : public FEElasticMaterial\n{\npublic:\n\t// constructor\n\tFEDonnanEquilibrium(FEModel* pfem);\n\t\n\t//! Initialization routine\n\tbool Init() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Returns the Cauchy stress\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\t//! Returs the spatial tangent\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\n    //! Return the fixed-charge density\n    double FixedChargeDensity(FEMaterialPoint& mp);\n    \n    //! Return the osmotic pressure\n    double OsmoticPressure(FEMaterialPoint& mp);\n    \n    //! Return the osmotic pressure tangent\n    double OsmoticPressureTangent(FEMaterialPoint& mp);\n    \n    //! Return the osmolarity\n    double Osmolarity(FEMaterialPoint& mp);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override\n    {\n        return new FEDonnanEquilibriumMaterialPoint(new FEElasticMaterialPoint);\n    }\n    \n    // update fatigue material point at each iteration\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\t\npublic:\n\tdouble\tm_phiwr;\t//!< fluid volume fraction in reference configuration\n    double\tm_phisr;\t//!< referential solid volume fraction (may evolve with time)\n\tFEParamDouble\tm_cFr;\t\t//!< fixed charge density in reference configuration\n\tdouble\tm_Rgas;\t\t//!< universal gas constant\n\tdouble\tm_Tabs;\t\t//!< absolute temperature\n\tdouble\tm_bosm;\t\t//!< bath osmolarity\n    double  m_Phi;      //!< osmotic coefficient\n    bool    m_bnew;     //!< flag for using old or new method\n    bool    m_binit;    //!< initialization flag\n};\n"
  },
  {
    "path": "FEBioMech/FEEFDDonnanEquilibrium.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEFDDonnanEquilibrium.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEFDDonnanEquilibrium, FEElasticMaterial)\n\tADD_PARAMETER(m_DEQ.m_phiwr, \"phiw0\");\n\tADD_PARAMETER(m_DEQ.m_cFr, \"cF0\")->setUnits(UNIT_CONCENTRATION);\n\tADD_PARAMETER(m_DEQ.m_bosm, \"bosm\")->setUnits(UNIT_CONCENTRATION);\n    ADD_PARAMETER(m_DEQ.m_Phi, \"Phi\");\n\tADD_PARAMETER(m_Fib.m_beta, 3, \"beta\");\n\tADD_PARAMETER(m_Fib.m_ksi , 3, \"ksi\" )->setUnits(UNIT_PRESSURE);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEEFDDonnanEquilibrium\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\nbool FEEFDDonnanEquilibrium::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\tif (m_DEQ.Init() == false) return false;\n\tif (m_Fib.Init() == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEFDDonnanEquilibrium::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tm_Fib.Serialize(ar);\n\tm_DEQ.Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEEFDDonnanEquilibrium::Stress(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tmat3ds s = m_DEQ.Stress(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\t// evaluate stress and add it to matrix contribution\n\ts += m_Fib.Stress(mp);\n\t\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEEFDDonnanEquilibrium::Tangent(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\ttens4ds c = m_DEQ.Tangent(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\t// evaluate stress and add it to matrix contribution\n\tc += m_Fib.Tangent(mp);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEEFDDonnanEquilibrium::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tdouble sed = m_DEQ.StrainEnergyDensity(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\tsed += m_Fib.StrainEnergyDensity(mp);\n    \n    return sed;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEEFDDonnanEquilibrium.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMaterial.h\"\n#include \"FEDonnanEquilibrium.h\"\n#include \"FEEllipsoidalFiberDistribution.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a material that consists of a continuous ellipsoidal fiber distribution\n//! superposed on a charged (swelling) gel described by the equations of Donnan equilibrium\n\nclass FEEFDDonnanEquilibrium : public FEElasticMaterial\n{\npublic:\n\tFEEFDDonnanEquilibrium(FEModel* pfem) : FEElasticMaterial(pfem), m_Fib(pfem), m_DEQ(pfem) {}\n\t\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! material parameter intialization and checking\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\t\t\npublic:\n\t\t\n//\tFEEllipsoidalFiberDistribution\t\tm_Fib;\n\tFEEllipsoidalFiberDistributionOld\tm_Fib;\n\tFEDonnanEquilibrium\t\t\t\t\tm_DEQ;\n\t\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEFDMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEFDMooneyRivlin.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEFDMooneyRivlin, FEUncoupledMaterial)\n\tADD_PARAMETER(m_MR.m_c1, \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_MR.m_c2, \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_EFD.m_beta, 3, \"beta\");\n\tADD_PARAMETER(m_EFD.m_ksi , 3, \"ksi\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEEFDMooneyRivlin::FEEFDMooneyRivlin(FEModel* pfem) : FEUncoupledMaterial(pfem), m_EFD(pfem), m_MR(pfem)\n{\n\tm_EFD.SetParent(this);\n\tm_MR.SetParent(this);\n}\n\n//-----------------------------------------------------------------------------\nbool FEEFDMooneyRivlin::Init()\n{\n\tif (FEUncoupledMaterial::Init() == false) return false;\n\tif (m_MR.Init() == false) return false;\n\tif (m_EFD.Init() == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEEFDMooneyRivlin::DevStress(FEMaterialPoint& pt)\n{\n\treturn m_MR.DevStress(pt) + m_EFD.DevStress(pt);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEEFDMooneyRivlin::DevTangent(FEMaterialPoint& pt)\n{\n\treturn m_MR.DevTangent(pt) + m_EFD.DevTangent(pt);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEEFDMooneyRivlin::DevStrainEnergyDensity(FEMaterialPoint& pt)\n{\n    return m_MR.DevStrainEnergyDensity(pt) + m_EFD.DevStrainEnergyDensity(pt);\n}\n"
  },
  {
    "path": "FEBioMech/FEEFDMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEMooneyRivlin.h\"\n#include \"FEEFDUncoupled.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a material that consists of a Mooney-Rivlin matrix and\n//! a continuous EFD fiber distribution.\nclass FEEFDMooneyRivlin : public FEUncoupledMaterial\n{\npublic:\n\t// constructor\n\tFEEFDMooneyRivlin(FEModel* pfem);\n\t\n\t//! Data initialization\n\tbool Init() override;\n\npublic:\n\t//! Calculate the deviatoric stress\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! Calculate deviatoric tangent\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \npublic:\n\tFEMooneyRivlin\tm_MR;\n\tFEEFDUncoupled\tm_EFD;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEFDNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEFDNeoHookean.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEFDNeoHookean, FEElasticMaterial)\n\tADD_PARAMETER(m_NH.m_E, \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_NH.m_v, \"v\");\n\tADD_PARAMETER(m_EFD.m_beta, 3, \"beta\");\n\tADD_PARAMETER(m_EFD.m_ksi , 3, \"ksi\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FEEFDNeoHookean\n//////////////////////////////////////////////////////////////////////\n\nFEEFDNeoHookean::FEEFDNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem), m_EFD(pfem), m_NH(pfem) \n{\n\tint a = 0;\n}\n\nbool FEEFDNeoHookean::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\tif (m_NH.Init()  == false) return false;\n\tif (m_EFD.Init() == false) return false;\n\treturn true;\n}\n\nvoid FEEFDNeoHookean::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tm_NH.Serialize(ar);\n\tm_EFD.Serialize(ar);\n}\n\nmat3ds FEEFDNeoHookean::Stress(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tmat3ds s = m_NH.Stress(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\t// evaluate stress and add it to matrix contribution\n\ts += m_EFD.Stress(mp);\n\t\n\treturn s;\n}\n\ntens4ds FEEFDNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\ttens4ds c = m_NH.Tangent(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\t// evaluate stress and add it to matrix contribution\n\tc += m_EFD.Tangent(mp);\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEEFDNeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tdouble sed = m_NH.StrainEnergyDensity(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\tsed += m_EFD.StrainEnergyDensity(mp);\n    \n    return sed;\n}\n\n//////////////////////////////////////////////////////////////////////\n// FEEFDNeoHookeanOld\n//////////////////////////////////////////////////////////////////////\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEFDNeoHookeanOld, FEElasticMaterial)\n\tADD_PARAMETER(m_NH.m_E, \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_NH.m_v, \"v\");\n\tADD_PARAMETER(m_EFD.m_beta, 3, \"beta\");\n\tADD_PARAMETER(m_EFD.m_ksi , 3, \"ksi\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\nbool FEEFDNeoHookeanOld::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\tif (m_NH.Init() == false) return false;\n\tif (m_EFD.Init() == false) return false;\n\treturn true;\n}\n\nvoid FEEFDNeoHookeanOld::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tm_NH.Serialize(ar);\n\tm_EFD.Serialize(ar);\n}\n\nmat3ds FEEFDNeoHookeanOld::Stress(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tmat3ds s = m_NH.Stress(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\t// evaluate stress and add it to matrix contribution\n\ts += m_EFD.Stress(mp);\n\t\n\treturn s;\n}\n\ntens4ds FEEFDNeoHookeanOld::Tangent(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\ttens4ds c = m_NH.Tangent(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\t// evaluate stress and add it to matrix contribution\n\tc += m_EFD.Tangent(mp);\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEEFDNeoHookeanOld::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// --- M A T R I X   C O N T R I B U T I O N ---\n\tdouble sed = m_NH.StrainEnergyDensity(mp);\n\t\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\t\n\tsed += m_EFD.StrainEnergyDensity(mp);\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEEFDNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMaterial.h\"\n#include \"FENeoHookean.h\"\n#include \"FEEllipsoidalFiberDistribution.h\"\n\nclass FEEFDNeoHookean :\tpublic FEElasticMaterial\n{\npublic:\n\tFEEFDNeoHookean(FEModel* pfem);\n\npublic:\n\tdouble\tm_E;\t//!< Young's modulus\n\tdouble\tm_v;\t//!< Poisson's ratio\n\tdouble\tm_ksi[3];\t//!< ksi\n\tdouble\tm_beta[3];\t//!< beta\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\n\tFEEllipsoidalFiberDistribution\tm_EFD;\n\tFENeoHookean\t\t\t\t\tm_NH;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//----------------------------------------------------------------\n// \"old\" version using full-sphere integration\nclass FEEFDNeoHookeanOld :\tpublic FEElasticMaterial\n{\npublic:\n\tFEEFDNeoHookeanOld(FEModel* pfem) : FEElasticMaterial(pfem), m_EFD(pfem), m_NH(pfem) {}\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization and checking\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\n\tFEEllipsoidalFiberDistributionOld\tm_EFD;\n\tFENeoHookean\t\t\t\t\t\tm_NH;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEFDUncoupled.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEFDUncoupled.h\"\n\n// The following file contains the integration points and weights\n// for the integration over a unit sphere in spherical coordinates\n#include \"geodesic.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n// FEEFDUncoupled\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEFDUncoupled, FEUncoupledMaterial)\n\tADD_PARAMETER(m_beta, 3, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi , 3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEEFDUncoupled::FEEFDUncoupled(FEModel* pfem) : FEUncoupledMaterial(pfem) {}\n\n//-----------------------------------------------------------------------------\nmat3ds FEEFDUncoupled::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble J = pt.m_J;\n\t// deviatoric deformation gradient\n\tmat3d F = pt.m_F*pow(J,-1.0/3.0);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wl;\n\tconst double eps = 0;\n\tmat3ds s;\n\ts.zero();\n\n\tconst int li[4][3] = {\n\t\t{ 1, 1, 1},\n\t\t{-1, 1, 1},\n\t\t{-1,-1, 1},\n\t\t{ 1,-1, 1}\n\t};\n\n\tdouble ksi[3] = { m_ksi[0](mp), m_ksi[1](mp), m_ksi[2](mp) };\n\tdouble beta[3] = { m_beta[0](mp), m_beta[1](mp), m_beta[2](mp) };\n\n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\n\t\t// calculate material coefficients\n\t\t// TODO: There is an obvious optimization opportunity here, since the values of ksi\n\t\t//       and beta can be precalculated and reused. I have not done this yet since I\n\t\t//       need to figure out how to initialize the material parameters for each time\n\t\t//       step (instead of once at the start) in case the values depend on load curves.\n\t\tdouble ksi_n  = 1.0 / sqrt(SQR(n0a.x / ksi [0]) + SQR(n0a.y / ksi [1]) + SQR(n0a.z / ksi [2]));\n\t\tdouble beta_n = 1.0 / sqrt(SQR(n0a.x / beta[0]) + SQR(n0a.y / beta[1]) + SQR(n0a.z / beta[2]));\n\n\t\t// loop over the four quadrants\n\t\tfor (int l=0; l<4; ++l)\n\t\t{\n\t\t\tn0q = vec3d(li[l][0]*n0a.x, li[l][1]*n0a.y, li[l][2]*n0a.z);\n\n\t\t\t// rotate to reference configuration\n\t\t\tn0e = Q*n0q;\n\n\t\t\t// get the global spatial fiber direction in current configuration\n\t\t\tnt = F*n0e;\n\n\t\t\t// Calculate In = n0e*C*n0e\n\t\t\tIn = nt*nt;\n\t\t\n\t\t\t// only take fibers in tension into consideration\n\t\t\tif (In > 1. + eps)\n\t\t\t{\n\t\t\t\t// calculate the outer product of nt\n\t\t\t\tmat3ds N = dyad(nt);\n\t\t\t\n\t\t\t\t// calculate strain energy derivative\n\t\t\t\tWl = beta_n *ksi_n *pow(In - 1.0, beta_n -1.0);\n\t\t\t\n\t\t\t\t// calculate the stress\n\t\t\t\ts += N*(Wl*wn);\n\t\t\t}\n\t\t}\n\t}\n\t// don't forget to multiply by two to include the other half-sphere\n\treturn s.dev()*(4.0/J);\n}\n\n\n//-----------------------------------------------------------------------------\ntens4ds FEEFDUncoupled::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble J = pt.m_J;\n\t// deviatoric deformation gradient\n\tmat3d F = pt.m_F*pow(J,-1.0/3.0);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, nt;\n\tdouble In, Wl, Wll;\n\tconst double eps = 0;\n\tmat3ds s;\n\ttens4ds cf, cfw; cf.zero();\n\tmat3ds N2;\n\tmat3dd I(1);\n\ttens4ds N4;\n\ttens4ds c;\n\n\ts.zero();\n\tc.zero();\n\n\tconst int li[4][3] = {\n\t\t{ 1, 1, 1},\n\t\t{-1, 1, 1},\n\t\t{-1,-1, 1},\n\t\t{ 1,-1, 1}\n\t};\n\t\n\tdouble ksi[3] = { m_ksi[0](mp), m_ksi[1](mp), m_ksi[2](mp) };\n\tdouble beta[3] = { m_beta[0](mp), m_beta[1](mp), m_beta[2](mp) };\n\n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\n\t\t// calculate material coefficients\n\t\tdouble ksi_n  = 1.0 / sqrt(SQR(n0a.x / ksi [0]) + SQR(n0a.y / ksi [1]) + SQR(n0a.z / ksi [2]));\n\t\tdouble beta_n = 1.0 / sqrt(SQR(n0a.x / beta[0]) + SQR(n0a.y / beta[1]) + SQR(n0a.z / beta[2]));\n\n\t\tfor (int l=0; l<4; ++l)\n\t\t{\n\t\t\tvec3d n0q = vec3d(li[l][0]*n0a.x, li[l][1]*n0a.y, li[l][2]*n0a.z);\n\n\t\t\t// rotate to reference configuration\n\t\t\tn0e = Q*n0q;\n\n\t\t\t// get the global spatial fiber direction in current configuration\n\t\t\tnt = F*n0e;\n\n\t\t\t// Calculate In = n0e*C*n0e\n\t\t\tIn = nt*nt;\n\t\n\t\t\t// only take fibers in tension into consideration\n\t\t\tif (In > 1. + eps)\n\t\t\t{\n\t\t\t\n\t\t\t\t// calculate strain energy derivative\n\t\t\t\tWl = beta_n *ksi_n *pow(In - 1.0, beta_n -1.0);\n\t\t\t\tWll = beta_n *(beta_n -1.0)*ksi_n *pow(In - 1.0, beta_n -2.0);\n\t\t\t\n\t\t\t\t// calculate the outer product of nt\n\t\t\t\tN2 = dyad(nt);\n\t\t\t\tN4 = dyad1s(N2);\n\n\t\t\t\t// calculate the stress\n\t\t\t\ts += N2*(2.0/J*Wl*wn);\n\n\t\t\t\t// calculate tangent\n\t\t\t\tc += N4*(4.0/J*Wll*wn);\n\t\t\t}\n\t\t}\n\t}\n\n\t// don't forget to multiply by two to include the other half-sphere\n\ts *= 2.0;\n\tc *= 2.0;\n\t\n\t// This is the final value of the elasticity tensor\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\tc += ((I4+IxI/3.0)*s.tr() - dyad1s(I,s))*(2./3.)\n\t- (ddots(IxI, c)-IxI*(c.tr()/3.))/3.;\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEEFDUncoupled::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = 0.0;\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble J = pt.m_J;\n\t// deviatoric deformation gradient\n\tmat3d F = pt.m_F*pow(J,-1.0/3.0);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, W;\n\tconst double eps = 0;\n    \n\tconst int li[4][3] = {\n\t\t{ 1, 1, 1},\n\t\t{-1, 1, 1},\n\t\t{-1,-1, 1},\n\t\t{ 1,-1, 1}\n\t};\n    \n\tdouble ksi[3] = { m_ksi[0](mp), m_ksi[1](mp), m_ksi[2](mp) };\n\tdouble beta[3] = { m_beta[0](mp), m_beta[1](mp), m_beta[2](mp) };\n\n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n        \n\t\t// calculate material coefficients\n\t\t// TODO: There is an obvious optimization opportunity here, since the values of ksi\n\t\t//       and beta can be precalculated and reused. I have not done this yet since I\n\t\t//       need to figure out how to initialize the material parameters for each time\n\t\t//       step (instead of once at the start) in case the values depend on load curves.\n\t\tdouble ksi_n  = 1.0 / sqrt(SQR(n0a.x / ksi [0]) + SQR(n0a.y / ksi [1]) + SQR(n0a.z / ksi [2]));\n\t\tdouble beta_n = 1.0 / sqrt(SQR(n0a.x / beta[0]) + SQR(n0a.y / beta[1]) + SQR(n0a.z / beta[2]));\n        \n\t\t// loop over the four quadrants\n\t\tfor (int l=0; l<4; ++l)\n\t\t{\n\t\t\tn0q = vec3d(li[l][0]*n0a.x, li[l][1]*n0a.y, li[l][2]*n0a.z);\n            \n\t\t\t// rotate to reference configuration\n\t\t\tn0e = Q*n0q;\n            \n\t\t\t// get the global spatial fiber direction in current configuration\n\t\t\tnt = F*n0e;\n            \n\t\t\t// Calculate In = n0e*C*n0e\n\t\t\tIn = nt*nt;\n            \n\t\t\t// only take fibers in tension into consideration\n\t\t\tif (In > 1. + eps)\n\t\t\t{\n\t\t\t\t// calculate strain energy density\n\t\t\t\tW = ksi_n *pow(In - 1.0, beta_n);\n\t\t\t\tsed += W*wn;\n\t\t\t}\n\t\t}\n\t}\n    \n\t// don't forget to multiply by two to include the other half-sphere\n    return sed*2.0;\n}\n"
  },
  {
    "path": "FEBioMech/FEEFDUncoupled.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for the uncoupled ellipsoidal fiber distribution\nclass FEEFDUncoupled : public FEUncoupledMaterial\n{\npublic:\n\tFEEFDUncoupled(FEModel* pfem);\n\n\t//! deviatoric Cauchy stress\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! deviatoric spatial tangent\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \npublic:\n\tFEParamDouble\tm_beta[3];\t// power in power-law relation\n\tFEParamDouble\tm_ksi[3];\t// coefficient in power-law relation\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEFDVerondaWestmann.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEFDVerondaWestmann.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEFDVerondaWestmann, FEUncoupledMaterial)\n\tADD_PARAMETER(m_VW.m_c1, \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_VW.m_c2, \"c2\");\n\tADD_PARAMETER(m_EFD.m_beta, 3, \"beta\");\n\tADD_PARAMETER(m_EFD.m_ksi , 3, \"ksi\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEEFDVerondaWestmann::FEEFDVerondaWestmann(FEModel* pfem) : FEUncoupledMaterial(pfem), m_VW(pfem), m_EFD(pfem) \n{\n\tm_VW.SetParent(this);\n\tm_EFD.SetParent(this);\n}\n\n//-----------------------------------------------------------------------------\nbool FEEFDVerondaWestmann::Init()\n{\n\tif (FEUncoupledMaterial::Init() == false) return false;\n\tif (m_VW.Init() == false) return false;\n\tif (m_EFD.Init() == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEFDVerondaWestmann::Serialize(DumpStream& ar)\n{\n\tFEUncoupledMaterial::Serialize(ar);\n\tm_VW.Serialize(ar);\n\tm_EFD.Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEEFDVerondaWestmann::DevStress(FEMaterialPoint& pt)\n{\n\treturn m_VW.DevStress(pt) + m_EFD.DevStress(pt);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEEFDVerondaWestmann::DevTangent(FEMaterialPoint& pt)\n{\n\treturn m_VW.DevTangent(pt) + m_EFD.DevTangent(pt);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEEFDVerondaWestmann::DevStrainEnergyDensity(FEMaterialPoint& pt)\n{\n    return m_VW.DevStrainEnergyDensity(pt) + m_EFD.DevStrainEnergyDensity(pt);\n}\n\n"
  },
  {
    "path": "FEBioMech/FEEFDVerondaWestmann.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEVerondaWestmann.h\"\n#include \"FEEFDUncoupled.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a material that consists of a Veronda-Westmann matrix and\n//! a continuous EFD fiber distribution.\nclass FEEFDVerondaWestmann : public FEUncoupledMaterial\n{\npublic:\n\t// constructor\n\tFEEFDVerondaWestmann(FEModel* pfem);\n\n\t//! material initialization\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! deviatoric stress\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! deviatoric tangent\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \npublic:\n\tFEVerondaWestmann\tm_VW;\n\tFEEFDUncoupled\t\tm_EFD;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEdgeToSurfaceContactPotential.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEEdgeToSurfaceContactPotential.h\"\n#include <FECore/FENode.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEBox.h>\n#include <stdexcept>\n\nvoid FEE2SCPSurface::Update()\n{\n// This assumes we are inside a omp parallel region!\n#pragma omp for\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\n\t\tvec3d re[FEElement::MAX_NODES];\n\t\tGetNodalCoordinates(el, re);\n\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint& mp = static_cast<FESurfaceMaterialPoint&>(*el.GetMaterialPoint(n));\n\t\t\tmp.m_rt = el.eval(re, n);\n\t\t\t\n\t\t\t// kinematics at integration points\n\t\t\tmp.dxr = el.eval_deriv1(re, n);\n\t\t\tmp.dxs = el.eval_deriv2(re, n);\n\n\t\t\tmp.m_Jt = (mp.dxr ^ mp.dxs).norm();\n\t\t}\n\t}\n}\n\nFEE2SCPSurface::FEE2SCPSurface(FEModel* fem) : FESurface(fem)\n{\n\n}\n\n//=================================================================================================\nFEE2SCPEdge::FEE2SCPEdge(FEModel* fem) : FEEdge(fem)\n{\n\n}\n\nFEMaterialPoint* FEE2SCPEdge::CreateMaterialPoint()\n{\n\treturn new FEE2SCPPoint();\n}\n\nbool FEE2SCPEdge::Create(FESegmentSet& eset)\n{\n\treturn FEEdge::Create(eset, FE_LINE2NI);\n}\n\nvoid FEE2SCPEdge::Update()\n{\n\t// This assumes we are inside a omp parallel region!\n#pragma omp for\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tGetNodalCoordinates(el, rt);\n\n\t\t// Jacobian is length in spatial coordinates divided by length in parametric coordinates (=2)\n\t\tdouble J = (rt[1] - rt[0]).Length() / 2.0;\n\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEE2SCPPoint& mp = static_cast<FEE2SCPPoint&>(*el.GetMaterialPoint(n));\n\t\t\tmp.m_rt = el.eval(rt, n);\n\t\t\tmp.m_Jt = J;\n\t\t\tmp.m_gap = 0.0;\n\t\t\tmp.m_tc = vec3d(0, 0, 0);\n\t\t}\n\t}\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FEEdgeToSurfaceContactPotential, FESurfaceConstraint)\n\tADD_PARAMETER(m_kc, \"kc\");\n\tADD_PARAMETER(m_p, \"p\");\n\tADD_PARAMETER(m_Rin, \"R_in\");\n\tADD_PARAMETER(m_Rout, \"R_out\");\n\tADD_PARAMETER(m_Rmin, \"R0_min\");\n\tADD_PARAMETER(m_wtol, \"w_tol\");\n\n\tADD_PROPERTY(m_edge, \"edgelist\")->AddFlag(FEProperty::Reference);\n\nEND_FECORE_CLASS();\n\nFEEdgeToSurfaceContactPotential::FEEdgeToSurfaceContactPotential(FEModel* fem) : FESurfaceConstraint(fem), m_edge(fem), m_surf(fem)\n{\n\tm_kc = 0.0;\n\tm_p = 4;\n\tm_Rin = 1.0;\n\tm_Rout = 2.0;\n\tm_Rmin = 0.0;\n\tm_wtol = 0.0;\n}\n\nFESurface* FEEdgeToSurfaceContactPotential::GetSurface()\n{\n\treturn &m_surf;\n}\n\nstatic bool is_neighbor(FESurfaceElement& e1, FESurfaceElement& e2)\n{\n\tint n1 = e1.Nodes();\n\tint n2 = e2.Nodes();\n\tfor (int i = 0; i < n1; ++i)\n\tfor (int j = 0; j < n2; ++j)\n\t{\n\t\tif (e1.m_node[i] == e2.m_node[j]) return true;\n\t}\n\treturn false;\n}\n\nstruct BOX\n{\npublic:\n\tvec3d r0, r1;\n\npublic:\n\tBOX() {}\n\tBOX(const vec3d& p0, const vec3d& p1) : r0(p0), r1(p1) {}\n\n\tvoid add(const vec3d& r)\n\t{\n\t\tif (r.x < r0.x) r0.x = r.x;\n\t\tif (r.y < r0.y) r0.y = r.y;\n\t\tif (r.z < r0.z) r0.z = r.z;\n\n\t\tif (r.x > r1.x) r1.x = r.x;\n\t\tif (r.y > r1.y) r1.y = r.y;\n\t\tif (r.z > r1.z) r1.z = r.z;\n\t}\n\n\tvoid inflate(double l)\n\t{\n\t\tr0.x -= l; r0.y -= l; r0.z -= l;\n\t\tr1.x += l; r1.y += l; r1.z += l;\n\t}\n\n\tbool isInside(const vec3d& r) const\n\t{\n\t\treturn (\n\t\t\t(r.x >= r0.x) && (r.x <= r1.x) &&\n\t\t\t(r.y >= r0.y) && (r.y <= r1.y) &&\n\t\t\t(r.z >= r0.z) && (r.z <= r1.z));\n\t}\n\n\tdouble MaxExtent() const\n\t{\n\t\tdouble sx = r1.x - r0.x;\n\t\tdouble sy = r1.y - r0.y;\n\t\tdouble sz = r1.z - r0.z;\n\n\t\tif ((sx >= sy) && (sx >= sz)) return sx;\n\t\tif ((sy >= sx) && (sy >= sz)) return sy;\n\t\tif ((sz >= sx) && (sz >= sy)) return sz;\n\t\t// we should never reach here\n\t\treturn 0;\n\t}\n\n\tdouble width () const { return r1.x - r0.x; }\n\tdouble height() const { return r1.y - r0.y; }\n\tdouble depth () const { return r1.z - r0.z; }\n};\n\nclass Grid\n{\npublic:\n\tclass Cell\n\t{\n\tpublic:\n\t\tCell() {}\n\t\tCell(const Cell& c) : m_box(c.m_box), m_elemList(c.m_elemList), m_nbr(c.m_nbr) {}\n\t\tvoid operator = (const Cell& c) { \n\t\t\tm_box = c.m_box; \n\t\t\tm_elemList = c.m_elemList; \n\t\t\tm_nbr = c.m_nbr;\n\t\t}\n\n\t\tvoid add(FESurfaceElement* el)\n\t\t{\n\t\t\tm_elemList.insert(el);\n\t\t}\n\n\t\tbool empty() const { return m_elemList.empty(); }\n\n\tpublic:\n\t\tBOX m_box;\n\t\tset<FESurfaceElement*>\tm_elemList;\n\t\tvector<Cell*>\tm_nbr;\n\t};\n\n\tCell* FindCell(const vec3d& r)\n\t{\n\t\tif (box.isInside(r) == false) return nullptr;\n\n\t\tint ix = (int)(m_nx * (r.x - box.r0.x) / box.width());\n\t\tint iy = (int)(m_ny * (r.y - box.r0.y) / box.height());\n\t\tint iz = (int)(m_nz * (r.z - box.r0.z) / box.depth());\n\t\tif (ix == m_nx) ix--;\n\t\tif (iy == m_ny) iy--;\n\t\tif (iz == m_nz) iz--;\n\n\t\tint icell = iz * (m_nx * m_ny) + iy * m_nx + ix;\n\t\tCell* c = m_cell + icell;\n\t\tassert(c->m_box.isInside(r));\n\t\treturn m_cell + icell;\n\t}\n\n\tint GetCellNeighborHood(const vec3d& r, Cell** cellList)\n\t{\n\t\tCell* c = FindCell(r);\n\t\tif (c == nullptr) return 0;\n\n\t\tint n = 0;\n\t\tfor (int i = 0; i < 27; ++i)\n\t\t{\n\t\t\tCell* ci = c->m_nbr[i];\n\t\t\tif ((ci != nullptr) && (ci->empty() == false)) cellList[n++] = ci;\n\t\t}\n\t\treturn n;\n\t}\n\npublic:\n\tGrid() { m_nx = m_ny = m_nz = 0; m_cell = nullptr; }\n\n\tbool Build(FESurface& s, int boxDivs, double minBoxSize)\n\t{\n\t\t// update the bounding box\n\t\tfor (int i = 0; i < s.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& node = s.Node(i);\n\t\t\tvec3d ri = node.m_rt;\n\t\t\tif (i == 0) box.r0 = box.r1 = ri;\n\t\t\telse box.add(ri);\n\t\t}\n\n\t\t// inflate a little, just to be sure\n\t\tdouble R = box.MaxExtent();\n\t\tbox.inflate(minBoxSize);\n\n\t\t// determine the sizes\n\t\tdouble W = box.width();\n\t\tdouble H = box.height();\n\t\tdouble D = box.depth();\n\n\t\tdouble boxSize = box.MaxExtent() / boxDivs;\n\t\tif (boxSize < minBoxSize) boxSize = minBoxSize;\n\n\t\tm_nx = (int)(W / boxSize); if (m_nx < 1) m_nx = 1;\n\t\tm_ny = (int)(H / boxSize); if (m_ny < 1) m_ny = 1;\n\t\tm_nz = (int)(D / boxSize); if (m_nz < 1) m_nz = 1;\n\n\t\t// allocate the grid\n\t\tint ncells = m_nx * m_ny * m_nz;\n\t\tm_cell = new Cell[ncells];\n\n\t\t// helper lookup-table for cell neighbors\n\t\tconst int LUT[27][3] = {\n\t\t\t{-1,-1,-1},{ 0,-1,-1},{1,-1,-1},\n\t\t\t{-1, 0,-1},{ 0, 0,-1},{1, 0,-1},\n\t\t\t{-1, 1,-1},{ 0, 1,-1},{1, 1,-1},\n\t\t\t{-1,-1, 0},{ 0,-1, 0},{1,-1, 0},\n\t\t\t{-1, 0, 0},{ 0, 0, 0},{1, 0, 0},\n\t\t\t{-1, 1, 0},{ 0, 1, 0},{1, 1, 0},\n\t\t\t{-1,-1, 1},{ 0,-1, 1},{1,-1, 1},\n\t\t\t{-1, 0, 1},{ 0, 0, 1},{1, 0, 1},\n\t\t\t{-1, 1, 1},{ 0, 1, 1},{1, 1, 1} };\n\n\t\t// construct cells\n\t\tCell* c = m_cell;\n\t\tdouble X0 = box.r0.x;\n\t\tdouble Y0 = box.r0.y;\n\t\tdouble Z0 = box.r0.z;\n\t\tfor (int k = 0; k < m_nz; ++k)\n\t\t{\n\t\t\tdouble z0 = Z0 + k * D / m_nz;\n\t\t\tdouble z1 = Z0 + (k+1) * D / m_nz;\n\t\t\tfor (int j = 0; j < m_ny; ++j)\n\t\t\t{\n\t\t\t\tdouble y0 = Y0 + j * H / m_ny;\n\t\t\t\tdouble y1 = Y0 + (j + 1) * H / m_ny;\n\t\t\t\tfor (int i = 0; i < m_nx; ++i, ++c)\n\t\t\t\t{\n\t\t\t\t\tdouble x0 = X0 + i * W / m_nx;\n\t\t\t\t\tdouble x1 = X0 + (i + 1) * W / m_nx;\n\n\t\t\t\t\tc->m_box = BOX(vec3d(x0, y0, z0), vec3d(x1, y1, z1));\n\t\t\t\t\tdouble R = c->m_box.MaxExtent();\n\t\t\t\t\tc->m_box.inflate(R * 1e-4);\n\n\t\t\t\t\t// assign neighbors\n\t\t\t\t\tc->m_nbr.assign(27, nullptr);\n\t\t\t\t\tfor (int n = 0; n < 27; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst int* l = LUT[n];\n\t\t\t\t\t\tc->m_nbr[n] = GetCell(i + l[0], j + l[1], k + l[2]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// assign elements to grid cells\n\t\tfor (int i = 0; i < s.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = s.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFESurfaceMaterialPoint& mp = static_cast<FESurfaceMaterialPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\tCell* c = FindCell(mp.m_rt); assert(c);\n\t\t\t\tif (c == nullptr) return false;\n\t\t\t\tc->add(&el);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tCell* GetCell(int i, int j, int k) const\n\t{\n\t\tif ((i < 0) || (i >= m_nx)) return nullptr;\n\t\tif ((j < 0) || (j >= m_ny)) return nullptr;\n\t\tif ((k < 0) || (k >= m_nz)) return nullptr;\n\t\treturn m_cell + (k * (m_nx * m_ny) + j * m_nx + i);\n\t}\n\n\t~Grid() { delete [] m_cell; }\n\nprotected:\n\tBOX\t\tbox;\n\tint\t\tm_nx, m_ny, m_nz;\n\tCell*\tm_cell;\n};\n\n// initialization\nbool FEEdgeToSurfaceContactPotential::Init()\n{\n\tif (FESurfaceConstraint::Init() == false) return false;\n\n\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t{\n\t\tFELineElement& el = m_edge.Element(i);\n\n\t\tvec3d r0[FEElement::MAX_NODES];\n\t\tm_edge.GetReferenceNodalCoordinates(el, r0);\n\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\t}\n\t}\n\n\tm_activeElements.resize(m_edge.Elements());\n\n\treturn true;\n}\n\n// update\nvoid FEEdgeToSurfaceContactPotential::Update()\n{\n\tFESurfaceConstraint::Update();\n\n\t// update the constants\n\tm_c1 = 0.5 * m_p * m_kc / ((m_Rout - m_Rin) * pow(m_Rin, m_p + 1.0));\n\tm_c2 = m_kc / pow(m_Rin, m_p) - m_c1 * (m_Rin - m_Rout) * (m_Rin - m_Rout);\n\n\t// Update the surfaces\n#pragma omp parallel \n\t{\n\t\tm_edge.Update();\n\t\tm_surf.Update();\n\t}\n\n\t// build the grid\n\tint ndivs = (int)pow(m_surf.Elements(), 0.33333);\n\tif (ndivs < 2) ndivs = 2;\n\tGrid g;\n\tif (g.Build(m_surf, ndivs, m_Rout) == false)\n\t{\n\t\tthrow std::runtime_error(\"Failed to build grid in FEEdgeToSurfaceContact::Update\");\n\t}\n\n\t// build the list of active elements\n//#pragma omp parallel for shared(g) schedule(dynamic)\n\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t{\n\t\tFELineElement& el1 = m_edge.Element(i);\n\n\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\tactiveElems.clear();\n\n\t\t// list of elements to exclude. This will be elements\n\t\t// already processed\n\t\tset<FESurfaceElement*> excludeList;\n\n\t\tfor (int n = 0; n < el1.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEE2SCPPoint& mp1 = static_cast<FEE2SCPPoint&>(*el1.GetMaterialPoint(n));\n\t\t\tmp1.m_gap = 0.0;\n\t\t\tvec3d r1 = mp1.m_rt;\n\t\t\tvec3d R1 = mp1.m_r0;\n\n\t\t\t// find the grid cell this point is in and loop over the cell's neighborhood\n\t\t\tGrid::Cell* c[27] = { nullptr };\n\t\t\tint nc = g.GetCellNeighborHood(r1, &c[0]);\n\t\t\tfor (int l = 0; l < nc; ++l)\n\t\t\t{\n\t\t\t\tGrid::Cell* cl = c[l];\n\t\t\t\tfor (FESurfaceElement* el2 : cl->m_elemList)\n\t\t\t\t{\n\t\t\t\t\t// make sure we did not process this element yet\n\t\t\t\t\t// and the element is not a neighbor (which can be the case for self-contact)\n\t\t\t\t\tif (excludeList.find(el2) == excludeList.end())\n\t\t\t\t\t{\n\t\t\t\t\t\t// Next, we see if any integration point of el2 is close to the current \n\t\t\t\t\t\t// integration point of el1. \n\t\t\t\t\t\tvec3d r12;\n\t\t\t\t\t\tfor (int m = 0; m < el2->GaussPoints(); ++m)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEMaterialPoint* mp2 = el2->GetMaterialPoint(m);\n\t\t\t\t\t\t\tvec3d r2 = mp2->m_rt;\n\t\t\t\t\t\t\tvec3d R2 = mp2->m_r0;\n\n\t\t\t\t\t\t\tr12.x = r1.x - r2.x;\n\t\t\t\t\t\t\tr12.y = r1.y - r2.y;\n\t\t\t\t\t\t\tr12.z = r1.z - r2.z;\n//\t\t\t\t\t\t\tvec3d r12 = r1 - mp2.m_rt;\n\t\t\t\t\t\t\tif ((r12.x < m_Rout) && (r12.x > -m_Rout) &&\n\t\t\t\t\t\t\t\t(r12.y < m_Rout) && (r12.y > -m_Rout) &&\n\t\t\t\t\t\t\t\t(r12.z < m_Rout) && (r12.z > -m_Rout) &&\n\t\t\t\t\t\t\t\t(r12.norm2() < m_Rout * m_Rout))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble L12 = (R2 - R1).norm2();\n\t\t\t\t\t\t\t\tdouble l12 = r12.unit();\n\t\t\t\t\t\t\t\tif (L12 >= m_Rmin)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t// we found one, so insert it to the list of active elements\n\t\t\t\t\t\t\t\t\tactiveElems.insert(el2);\n\n\t\t\t\t\t\t\t\t\t// also insert it to the exclude list\n\t\t\t\t\t\t\t\t\texcludeList.insert(el2);\n\n//\t\t\t\t\t\t\t\t\tfprintf(stderr, \"r12: %lg, %lg, %lg\\n\", r12.x, r12.y, r12.z);\n//\t\t\t\t\t\t\t\t\tfprintf(stderr, \"n1 : %lg, %lg, %lg\\n\", n1.x, n1.y, n1.z);\n//\t\t\t\t\t\t\t\t\tfprintf(stderr, \"l12 = %lg\\n\", l12);\n\n\t\t\t\t\t\t\t\t\tif ((mp1.m_gap == 0.0) || (l12 < mp1.m_gap))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tmp1.m_gap = l12;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Build the matrix profile\nvoid FEEdgeToSurfaceContactPotential::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\t// connect every element of the edge list to the surface\n\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t{\n\t\tFELineElement& el1 = m_edge.Element(i);\n\n\t\t// add the dofs of element 1\n\t\tvector<int> lm;\n\t\tfor (int j = 0; j < el1.Nodes(); ++j)\n\t\t{\n\t\t\tFENode& node = m_edge.Node(el1.m_lnode[j]);\n\t\t\tlm.push_back(node.m_ID[0]);\n\t\t\tlm.push_back(node.m_ID[1]);\n\t\t\tlm.push_back(node.m_ID[2]);\n\t\t}\n\n\t\t// add all active dofs of surface 2\n\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\tfor (FESurfaceElement* el2 : activeElems)\n\t\t{\n\t\t\tfor (int j = 0; j < el2->Nodes(); ++j)\n\t\t\t{\n\t\t\t\tFENode& node = m_surf.Node(el2->m_lnode[j]);\n\t\t\t\tlm.push_back(node.m_ID[0]);\n\t\t\t\tlm.push_back(node.m_ID[1]);\n\t\t\t\tlm.push_back(node.m_ID[2]);\n\t\t\t}\n\t\t}\n\t\tM.build_add(lm);\n\t}\n}\n\ndouble FEEdgeToSurfaceContactPotential::PotentialDerive(double r)\n{\n\tdouble f = 0.0;\n\tif (r < m_Rin)\n\t{\n\t\tf = (m_kc / pow(r, m_p)) - m_c2;\n\t}\n\telse if (r < m_Rout)\n\t{\n\t\tf = m_c1 * (r - m_Rout) * (r - m_Rout);\n\t}\n\treturn f;\n}\n\ndouble FEEdgeToSurfaceContactPotential::PotentialDerive2(double r)\n{\n\tdouble f = 0.0;\n\tif (r < m_Rin)\n\t{\n\t\tf = -m_p*m_kc / pow(r, m_p + 1.0);\n\t}\n\telse if (r < m_Rout)\n\t{\n\t\tf = 2.0*m_c1 * (r - m_Rout);\n\t}\n\treturn f;\n}\n\n// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\nvoid FEEdgeToSurfaceContactPotential::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tconst int ndof = 3;\n\n\t// clear all contact tractions\n#pragma omp parallel for\n\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t{\n\t\tFELineElement& el = m_edge.Element(i);\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEE2SCPPoint& cp = static_cast<FEE2SCPPoint&>(*el.GetMaterialPoint(n));\n\t\t\tcp.m_Ln = 0.0;\n\t\t\tcp.m_tc = vec3d(0, 0, 0);\n\t\t}\n\t}\n\n\t// loop over all elements of the edge list\n#pragma omp parallel for shared(R)\n\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t{\n\t\tFELineElement& eli = m_edge.Element(i);\n\t\tint na = eli.Nodes();\n\n\t\tvector<double> fe;\n\t\tvector<int> lm;\n\n\t\t// loop over all elements of surf 2\n\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\tfor (FESurfaceElement* elj : activeElems)\n\t\t{\n\t\t\tint nb = elj->Nodes();\n\n\t\t\t// evaluate contribution to force vector\n\t\t\tfe.assign((na + nb) * ndof, 0.0);\n\t\t\tElementForce(eli, *elj, fe);\n\n\t\t\t// setup the lm vector\n\t\t\tlm.resize(3 * (na + nb));\n\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t{\n\t\t\t\tFENode& node = m_edge.Node(eli.m_lnode[a]);\n\t\t\t\tlm[3*a    ] = node.m_ID[0];\n\t\t\t\tlm[3*a + 1] = node.m_ID[1];\n\t\t\t\tlm[3*a + 2] = node.m_ID[2];\n\t\t\t}\n\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t{\n\t\t\t\tFENode& node = m_surf.Node(elj->m_lnode[b]);\n\t\t\t\tlm[3*na + 3*b    ] = node.m_ID[0];\n\t\t\t\tlm[3*na + 3*b + 1] = node.m_ID[1];\n\t\t\t\tlm[3*na + 3*b + 2] = node.m_ID[2];\n\t\t\t}\n\n\t\t\t// assemble\n\t\t\tR.Assemble(lm, fe);\n\t\t}\n\t}\n\n\t// update contact pressures (only needed for plot output)\n#pragma omp parallel\n\t{\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t\t{\n\t\t\tFELineElement& el = m_edge.Element(i);\n\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t{\n\t\t\t\tFEE2SCPPoint& cp = static_cast<FEE2SCPPoint&>(*el.GetMaterialPoint(n));\n\t\t\t\tcp.m_Ln = cp.m_tc.norm();\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEEdgeToSurfaceContactPotential::ElementForce(FELineElement& el1, FESurfaceElement& el2, vector<double>& fe)\n{\n\tint na = el1.Nodes();\n\tint nb = el2.Nodes();\n\tdouble* w1 = el1.GaussWeights();\n\tdouble* w2 = el2.GaussWeights();\n\n\tfor (int n = 0; n < el1.GaussPoints(); ++n)\n\t{\n\t\tconst double* H1 = el1.H(n);\n\t\tFEE2SCPPoint& mp1 = static_cast<FEE2SCPPoint&>(*el1.GetMaterialPoint(n));\n\t\tdouble Jw1 = mp1.m_Jt*w1[n];\n\t\tvec3d r1 = mp1.m_rt;\n\n\t\tfor (int m = 0; m < el2.GaussPoints(); ++m)\n\t\t{\n\t\t\tconst double* H2 = el2.H(m);\n\t\t\tFESurfaceMaterialPoint& mp2 = static_cast<FESurfaceMaterialPoint&>(*el2.GetMaterialPoint(m));\n\t\t\tdouble Jw2 = mp2.m_Jt*w2[m];\n\n\t\t\tdouble Jw12 = Jw1 * Jw2;\n\n\t\t\t// get the position of the integration point\n\t\t\tvec3d r2 = mp2.m_rt;\n\n\t\t\t// get the normalized direction vector\n\t\t\tvec3d e12 = r1 - r2;\n\t\t\tdouble r12 = e12.unit();\n\n\t\t\t// calculate the potential derivative\n\t\t\tdouble df = PotentialDerive(r12);\n\n\t\t\t// add to the force\n\t\t\tif (df != 0.0)\n\t\t\t{\n\t\t\t\tvec3d F = e12 * df;\n\t\t\t\tmp1.m_tc += F * Jw2;\n\n\t\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * a    ] += F.x * (H1[a] * Jw12);\n\t\t\t\t\tfe[3 * a + 1] += F.y * (H1[a] * Jw12);\n\t\t\t\t\tfe[3 * a + 2] += F.z * (H1[a] * Jw12);\n\t\t\t\t}\n\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * (na + b)    ] -= F.x * (H2[b] * Jw12);\n\t\t\t\t\tfe[3 * (na + b) + 1] -= F.y * (H2[b] * Jw12);\n\t\t\t\t\tfe[3 * (na + b) + 2] -= F.z * (H2[b] * Jw12);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Evaluates the contriubtion to the stiffness matrix\nvoid FEEdgeToSurfaceContactPotential::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tconst int ndof = 3;\n\n\t// loop over all elements of surf 1\n#pragma omp parallel for shared(LS)\n\tfor (int i = 0; i < m_edge.Elements(); ++i)\n\t{\n\t\tFELineElement& eli = m_edge.Element(i);\n\t\tint na = eli.Nodes();\n\n\t\tset<FESurfaceElement*>& activeElems = m_activeElements[i];\n\t\tfor (FESurfaceElement* elj : activeElems)\n\t\t{\n\t\t\tint nb = elj->Nodes();\n\n\t\t\tFEElementMatrix ke((na + nb) * ndof, (na + nb) * ndof);\n\t\t\tke.zero();\n\n\t\t\tElementStiffness(eli, *elj, ke);\n\t\t\t\n\t\t\tvector<int> lm(3 * (na+nb));\n\t\t\tvector<int> en(na + nb);\n\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t{\n\t\t\t\tFENode& node = m_edge.Node(eli.m_lnode[a]);\n\t\t\t\tlm[3 * a    ] = node.m_ID[0];\n\t\t\t\tlm[3 * a + 1] = node.m_ID[1];\n\t\t\t\tlm[3 * a + 2] = node.m_ID[2];\n\t\t\t\ten[a] = eli.m_node[a];\n\t\t\t}\n\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t{\n\t\t\t\tFENode& node = m_surf.Node(elj->m_lnode[b]);\n\t\t\t\tlm[3 * na + 3 * b    ] = node.m_ID[0];\n\t\t\t\tlm[3 * na + 3 * b + 1] = node.m_ID[1];\n\t\t\t\tlm[3 * na + 3 * b + 2] = node.m_ID[2];\n\t\t\t\ten[na + b] = elj->m_node[b];\n\t\t\t}\n\n\t\t\tke.SetIndices(lm);\n\t\t\tke.SetNodes(en);\n\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n}\n\nvoid FEEdgeToSurfaceContactPotential::ElementStiffness(FELineElement& el1, FESurfaceElement& el2, matrix& ke)\n{\n\tdouble* w1 = el1.GaussWeights();\n\tdouble* w2 = el2.GaussWeights();\n\n\tint na = el1.Nodes();\n\tint nb = el2.Nodes();\n\n\tfor (int n = 0; n < el1.GaussPoints(); ++n)\n\t{\n\t\tconst double* H1 = el1.H(n);\n\t\tFEE2SCPPoint& mp1 = static_cast<FEE2SCPPoint&>(*el1.GetMaterialPoint(n));\n\t\tdouble Jw1 = mp1.m_Jt*w1[n];\n\n\t\tvec3d r1 = mp1.m_rt;\n\n\t\tfor (int m = 0; m < el2.GaussPoints(); ++m)\n\t\t{\n\t\t\tconst double* H2 = el2.H(m);\n\t\t\tFESurfaceMaterialPoint& mp2 = static_cast<FESurfaceMaterialPoint&>(*el2.GetMaterialPoint(m));\n\t\t\tdouble Jw2 = mp2.m_Jt*w2[m];\n\n\t\t\tvec3d r2 = mp2.m_rt;\n\n\t\t\t// get the normalized direction vector\n\t\t\tvec3d e12 = r1 - r2;\n\t\t\tdouble r12 = e12.unit();\n\n\t\t\t// calculate the potential's derivatives\n\t\t\tdouble df  = PotentialDerive(r12);\n\t\t\tdouble d2f = PotentialDerive2(r12);\n\n\t\t\t// add to the stiffness\n\t\t\tmat3dd I(1.0);\n\t\t\tmat3d ExE = dyad(e12);\n\t\t\tmat3d P = (I - ExE) / r12;\n\t\t\tmat3d M = ExE * d2f + P * df;\n\n\t\t\t// 1-1 contribution\n\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b < na; ++b)\n\t\t\t\t{\n\t\t\t\t\tmat3d K2 = M * Jw2;\n\t\t\t\t\tke.add(3 * a, 3 * b, K2 * (H1[a] * H1[b])*Jw1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 1-2 contribution\n\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tmat3d K2b = M * (H2[b] * Jw2);\n\t\t\t\t\tke.sub(3 * a, 3 * na + 3 * b, K2b * (H1[a] * Jw1));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2-1 contribution\n\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t{\n\t\t\t\tfor (int a = 0; a < na; ++a)\n\t\t\t\t{\n\t\t\t\t\tmat3d K1a = M * (H1[a] * Jw1);\n\t\t\t\t\tke.sub(3 * na + 3*b, 3 * a, K1a * (H2[b] * Jw2));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2-2 contribution\n\t\t\tfor (int a = 0; a < nb; ++a)\n\t\t\t{\n\t\t\t\tfor (int b = 0; b < nb; ++b)\n\t\t\t\t{\n\t\t\t\t\tmat3d K1 = M * Jw1;\n\t\t\t\t\tke.add(3*na + 3 * a, 3*na + 3 * b, K1 * (H2[a] * H2[b]) * Jw2);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tke *= -1.0;\n}\n\nvoid FEEdgeToSurfaceContactPotential::Serialize(DumpStream& ar)\n{\n\tFESurfaceConstraint::Serialize(ar);\n\tm_edge.Serialize(ar);\n\tm_surf.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMech/FEEdgeToSurfaceContactPotential.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FEEdge.h>\n#include \"FEContactSurface.h\"\n#include <set>\n\nclass FEE2SCPPoint : public FELineMaterialPoint\n{\npublic:\n\tdouble\tm_gap = 0.0;\n\tdouble\tm_Ln = 0.0;\n\tvec3d\tm_tc;\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tFELineMaterialPoint::Serialize(ar);\n\t\tar& m_tc & m_gap;\n\t}\n};\n\nclass FEE2SCPSurface : public FESurface\n{\npublic:\n\tFEE2SCPSurface(FEModel* fem);\n\n\tvoid Update();\n};\n\nclass FEE2SCPEdge : public FEEdge\n{\npublic:\n\tFEE2SCPEdge(FEModel* fem);\n\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\tvoid Update();\n\n\tbool Create(FESegmentSet& eset) override;\n};\n\nclass FEEdgeToSurfaceContactPotential : public FESurfaceConstraint\n{\npublic:\n\tFEEdgeToSurfaceContactPotential(FEModel* fem);\n\npublic:\n\t//! return the surface\n\tFESurface* GetSurface() override;\n\n\t// Build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t// update\n\tvoid Update() override;\n\n\t// init\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t// Evaluates the contriubtion to the stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\nprotected:\n\tvoid ElementForce(FELineElement& el1, FESurfaceElement& el2, std::vector<double>& fe);\n\tvoid ElementStiffness(FELineElement& el1, FESurfaceElement& el2, matrix& ke);\n\n\tdouble PotentialDerive(double r);\n\tdouble PotentialDerive2(double r);\n\nprotected:\n\tFEE2SCPEdge\tm_edge;\n\tFEE2SCPSurface\tm_surf;\n\nprotected:\n\tdouble\tm_kc;\n\tdouble\tm_p;\n\tdouble\tm_Rin;\n\tdouble\tm_Rout;\n\tdouble\tm_Rmin;\n\tdouble\tm_wtol;\n\n\tdouble\tm_c1, m_c2;\n\n\tstd::vector< std::set<FESurfaceElement*> >\tm_activeElements;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEdgeToSurfaceSlidingContact.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEEdgeToSurfaceSlidingContact.h\"\n#include <FECore/FENode.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEBox.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <stdexcept>\n\nvoid FEEdgeToSurfaceSlidingContactSurface::Update()\n{\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\n\t\tvec3d re[FEElement::MAX_NODES];\n\t\tGetNodalCoordinates(el, re);\n\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint& mp = static_cast<FESurfaceMaterialPoint&>(*el.GetMaterialPoint(n));\n\t\t\tmp.m_rt = el.eval(re, n);\n\t\t\t\n\t\t\t// kinematics at integration points\n\t\t\tmp.dxr = el.eval_deriv1(re, n);\n\t\t\tmp.dxs = el.eval_deriv2(re, n);\n\n\t\t\tmp.m_Jt = (mp.dxr ^ mp.dxs).norm();\n\t\t}\n\t}\n}\n\nFEEdgeToSurfaceSlidingContactSurface::FEEdgeToSurfaceSlidingContactSurface(FEModel* fem) : FEContactSurface(fem)\n{\n\n}\n\n//=================================================================================================\nFEEdgeToSurfaceSlidingContactEdge::FEEdgeToSurfaceSlidingContactEdge(FEModel* fem) : FEEdge(fem)\n{\n\n}\n\nbool FEEdgeToSurfaceSlidingContactEdge::Init()\n{\n\tif (!FEEdge::Init()) return false;\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tm_dofX = dofs.GetDOF(\"x\");\n\tm_dofY = dofs.GetDOF(\"y\");\n\tm_dofZ = dofs.GetDOF(\"z\");\n\tm_points.resize(Nodes());\n\treturn true;\n}\n\nvoid FEEdgeToSurfaceSlidingContactEdge::UnpackLM(FELineElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N * 3);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\tlm[3 * i    ] = id[m_dofX];\n\t\tlm[3 * i + 1] = id[m_dofY];\n\t\tlm[3 * i + 2] = id[m_dofZ];\n\t}\n}\n\nFEMaterialPoint* FEEdgeToSurfaceSlidingContactEdge::CreateMaterialPoint()\n{\n\treturn new FEE2SSlidingContactPoint();\n}\n\nbool FEEdgeToSurfaceSlidingContactEdge::Create(FESegmentSet& eset)\n{\n\treturn FEEdge::Create(eset, FE_LINE2NI);\n}\n\nvoid FEEdgeToSurfaceSlidingContactEdge::Update()\n{\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tGetNodalCoordinates(el, rt);\n\n\t\t// Jacobian is length in spatial coordinates divided by length in parametric coordinates (=2)\n\t\tdouble J = (rt[1] - rt[0]).Length() / 2.0;\n\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEE2SSlidingContactPoint& mp = static_cast<FEE2SSlidingContactPoint&>(*el.GetMaterialPoint(n));\n\t\t\tmp.m_rt = el.eval(rt, n);\n\t\t\tmp.m_Jt = J;\n\t\t}\n\t}\n}\n\nvoid FEEdgeToSurfaceSlidingContactEdge::Serialize(DumpStream& ar)\n{\n\tFEEdge::Serialize(ar);\n\tar.LockPointerTable();\n\tfor (int i = 0; i < m_points.size(); ++i)\n\t{\n\t\tar & m_points[i];\n\t}\n\tar.UnlockPointerTable();\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FEEdgeToSurfaceSlidingContact, FESurfaceConstraint)\n\tADD_PARAMETER(m_atol         , \"tolerance\"    );\n\tADD_PARAMETER(m_eps          , \"penalty\"      );\n\tADD_PARAMETER(m_naugmin      , \"minaug\"       );\n\tADD_PARAMETER(m_naugmax      , \"maxaug\"       );\n\tADD_PARAMETER(m_stol         , \"search_tol\"   );\n\tADD_PARAMETER(m_nsegup       , \"seg_up\"       );\n\tADD_PARAMETER(m_sradius      , \"search_radius\");\n\n\tADD_PROPERTY(m_edge, \"edgelist\")->AddFlag(FEProperty::Reference);\n\nEND_FECORE_CLASS();\n\nFEEdgeToSurfaceSlidingContact::FEEdgeToSurfaceSlidingContact(FEModel* fem) : FESurfaceConstraint(fem), m_edge(fem), m_surf(fem)\n{\n\tm_atol = 0.1;\n\tm_eps = 1.0;\n\tm_naugmin = 0;\n\tm_naugmax = 0;\n\tm_stol = 0.01;\n\tm_nsegup = 0;\n\tm_sradius = 0;\n\n\tm_bfirst = true;\n}\n\nFESurface* FEEdgeToSurfaceSlidingContact::GetSurface()\n{\n\treturn &m_surf;\n}\n\n// initialization\nbool FEEdgeToSurfaceSlidingContact::Init()\n{\n\tif (FESurfaceConstraint::Init() == false) return false;\n\n\tm_bfirst = true;\n\n\tif (m_edge.Init() == false) return false;\n\tif (m_surf.Init() == false) return false;\n\n\treturn true;\n}\n\n//!  Projects the primary surface onto the secondary surface.\n//!  That is, for each primary surface node we determine the closest\n//!  secondary surface element and the projection of that node onto\n//!  this element.\n\n//! \\todo this function needs to identify the different types of contact:\n//!   1/ first contact\n//!   2/ crossing of element boundary\n//!\t  3/ contact termination \n//!\t\t\teither by failure to find projection or when g < tolerance\n\nvoid FEEdgeToSurfaceSlidingContact::ProjectSurface(bool bupseg, bool bmove)\n{\n\tFEClosestPointProjection cpp(m_surf);\n\tcpp.SetTolerance(m_stol);\n\tcpp.SetSearchRadius(m_sradius);\n\tcpp.HandleSpecialCases(true);\n\tcpp.Init();\n\n\tfor (int i = 0; i < m_edge.Nodes(); ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = m_edge.Node(i);\n\n\t\t// get the nodal position\n\t\tvec3d x = node.m_rt;\n\n\t\t// get the global node number\n\t\tint m = m_edge.NodeIndex(i);\n\n\t\tFEE2SSlidingContactPoint& cp = m_edge.m_points[i];\n\n\t\t// get the previous secondary surface element (if any)\n\t\tFESurfaceElement* pme = cp.m_pme;\n\n\t\t// If the node is in contact, let's see if the node still is \n\t\t// on the same element\n\t\tvec3d q;\n\t\tif (pme != 0)\n\t\t{\n\t\t\tFESurfaceElement& mel = *pme;\n\n\t\t\tdouble r = cp.m_rs[0];\n\t\t\tdouble s = cp.m_rs[1];\n\n\t\t\tq = m_surf.ProjectToSurface(mel, x, r, s);\n\t\t\tcp.m_rs[1] = s;\n\t\t\tcp.m_rs[0] = r;\n\n\t\t\t// we only check when we can update the segments\n\t\t\t// otherwise, we just stick with this element, even\n\t\t\t// if the node is no longer inside it.\n\t\t\tif (bupseg)\n\t\t\t{\n\t\t\t\tif (!m_surf.IsInsideElement(mel, r, s, m_stol))\n\t\t\t\t{\n\t\t\t\t\t// see if the node might have moved to another element\n\t\t\t\t\tFESurfaceElement* pold = pme;\n\t\t\t\t\tcp.m_rs = vec2d(0, 0);\n\n\t\t\t\t\tpme = cpp.Project(m, q, cp.m_rs);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (bupseg)\n\t\t{\n\t\t\t// get the secondary surface element\n\t\t\t// don't forget to initialize the search for the first node!\n\t\t\tcp.m_rs = vec2d(0, 0);\n\t\t\tpme = cpp.Project(m, q, cp.m_rs);\n\t\t}\n\n\t\t// if we found a secondary surface element, update the gap and normal data\n\t\tcp.m_pme = pme;\n\t\tif (pme != 0)\n\t\t{\n\t\t\tFESurfaceElement& mel = *cp.m_pme;\n\n\t\t\tdouble r = cp.m_rs[0];\n\t\t\tdouble s = cp.m_rs[1];\n\n\t\t\t// the normal is set to the secondary surface element normal\n\t\t\tcp.m_nu = m_surf.SurfaceNormal(mel, r, s);\n\n\t\t\t// calculate gap\n\t\t\tcp.m_gap = -(cp.m_nu * (x - q));\n\t\t\tif (bmove && (cp.m_gap > 0))\n\t\t\t{\n\t\t\t\tnode.m_r0 = node.m_rt = q;\n\t\t\t\tcp.m_gap = 0;\n\t\t\t}\n\n\t\t\t// TODO: what should we do if the gap function becomes\n\t\t\t// negative? setting the Lagrange multipliers to zero\n\t\t\t// might make the system unstable.\n/*\t\t\tif (ss.gap[i] < 0)\n\t\t\t{\n\t\t\t\tss.Lm[i] = 0;\n\t\t\t\tss.pme[i] = 0;\n\t\t\t}\n*/\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// TODO: Is this a good criteria for out-of-contact?\n\t\t\t//\t\t perhaps this is not even necessary.\n\t\t\t// since the node is not in contact, we set the gap function \n\t\t\t// and Lagrangian multiplier to zero\n\t\t\tcp.m_gap = 0;\n\t\t\tcp.m_Lm = 0;\n\t\t}\n\t}\n}\n\n// update\nvoid FEEdgeToSurfaceSlidingContact::Update()\n{\n\tFESurfaceConstraint::Update();\n\n\tint niter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n\n\t// should we do a segment update or not?\n\t// TODO: check what happens when m_nsegup == -1 and m_npass = 2;\n\t// We have to make sure that in this case, both surfaces get at least\n\t// one pass!\n\tbool bupdate = (m_bfirst || (m_nsegup == 0) ? true : (niter <= m_nsegup));\n\n\tm_edge.Update();\n\tm_surf.Update();\n\n\t// project primary surface onto secondary surface\n\t// this also calculates the nodal gap functions\n\tProjectSurface(bupdate);\n\n\t// set the first-entry-flag to false\n\tm_bfirst = false;\n}\n\n// Build the matrix profile\nvoid FEEdgeToSurfaceSlidingContact::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\t// TODO: this is currently for max 6 nodes (hence 7=6+1)\n\tvector<int> lm(6 * 7);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tfor (int j = 0; j < m_edge.Nodes(); ++j)\n\t{\n\t\tFESurfaceElement* pe = m_edge.m_points[j].m_pme;\n\t\tif (pe != 0)\n\t\t{\n\t\t\tFESurfaceElement& me = *pe;\n\t\t\tint* en = &me.m_node[0];\n\n\t\t\t// Note that we need to grab the rigid degrees of freedom as well\n\t\t\t// this is in case one of the nodes belongs to a rigid body.\n\t\t\tint n = me.Nodes();\n\t\t\tif (n == 3)\n\t\t\t{\n\t\t\t\tlm[6 * (3 + 1)    ] = -1; lm[6 * (3 + 2)    ] = -1; lm[6 * (3 + 3)    ] = -1;\n\t\t\t\tlm[6 * (3 + 1) + 1] = -1; lm[6 * (3 + 2) + 1] = -1; lm[6 * (3 + 3) + 1] = -1;\n\t\t\t\tlm[6 * (3 + 1) + 2] = -1; lm[6 * (3 + 2) + 2] = -1; lm[6 * (3 + 3) + 2] = -1;\n\t\t\t\tlm[6 * (3 + 1) + 3] = -1; lm[6 * (3 + 2) + 3] = -1; lm[6 * (3 + 3) + 3] = -1;\n\t\t\t\tlm[6 * (3 + 1) + 4] = -1; lm[6 * (3 + 2) + 4] = -1; lm[6 * (3 + 3) + 4] = -1;\n\t\t\t\tlm[6 * (3 + 1) + 5] = -1; lm[6 * (3 + 2) + 5] = -1; lm[6 * (3 + 3) + 5] = -1;\n\t\t\t}\n\t\t\tif (n == 4)\n\t\t\t{\n\t\t\t\tlm[6 * (4 + 1)    ] = -1; lm[6 * (4 + 2)    ] = -1;\n\t\t\t\tlm[6 * (4 + 1) + 1] = -1; lm[6 * (4 + 2) + 1] = -1;\n\t\t\t\tlm[6 * (4 + 1) + 2] = -1; lm[6 * (4 + 2) + 2] = -1;\n\t\t\t\tlm[6 * (4 + 1) + 3] = -1; lm[6 * (4 + 2) + 3] = -1;\n\t\t\t\tlm[6 * (4 + 1) + 4] = -1; lm[6 * (4 + 2) + 4] = -1;\n\t\t\t\tlm[6 * (4 + 1) + 5] = -1; lm[6 * (4 + 2) + 5] = -1;\n\t\t\t}\n\n\t\t\tlm[0] = m_edge.Node(j).m_ID[dof_X];\n\t\t\tlm[1] = m_edge.Node(j).m_ID[dof_Y];\n\t\t\tlm[2] = m_edge.Node(j).m_ID[dof_Z];\n\t\t\tlm[3] = m_edge.Node(j).m_ID[dof_RU];\n\t\t\tlm[4] = m_edge.Node(j).m_ID[dof_RV];\n\t\t\tlm[5] = m_edge.Node(j).m_ID[dof_RW];\n\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\t\tlm[6 * (k + 1)    ] = id[dof_X];\n\t\t\t\tlm[6 * (k + 1) + 1] = id[dof_Y];\n\t\t\t\tlm[6 * (k + 1) + 2] = id[dof_Z];\n\t\t\t\tlm[6 * (k + 1) + 3] = id[dof_RU];\n\t\t\t\tlm[6 * (k + 1) + 4] = id[dof_RV];\n\t\t\t\tlm[6 * (k + 1) + 5] = id[dof_RW];\n\t\t\t}\n\n\t\t\tK.build_add(lm);\n\t\t}\n\t}\n}\n\n// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\nvoid FEEdgeToSurfaceSlidingContact::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\t// the elements LM vectors\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d r0[MN];\n\tdouble w[MN];\n\tdouble detJ[MN];\n\tvec3d dxr, dxs;\n\n\tint ne = m_edge.Elements();\n\tfor (int j = 0; j < ne; ++j)\n\t{\n\t\t// get the next element\n\t\tFELineElement& sel = m_edge.Element(j);\n\t\tint nseln = sel.Nodes();\n\n\t\t// get the element's LM array\n\t\tm_edge.UnpackLM(sel, sLM);\n\n\t\t// nodal coordinates\n\t\tfor (int i = 0; i < nseln; ++i) r0[i] = m_edge.GetMesh()->Node(sel.m_node[i]).m_r0;\n\n\t\t// we calculate all the metrics we need before we\n\t\t// calculate the nodal forces\n\t\tdouble* w1 = sel.GaussWeights();\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tFEE2SSlidingContactPoint& mp1 = static_cast<FEE2SSlidingContactPoint&>(*sel.GetMaterialPoint(n));\n\t\t\tdetJ[n] = mp1.m_Jt;\n\t\t\tw[n] = sel.GaussWeights()[n];\n\t\t}\n\n\t\t// loop over primary surface element nodes (which are the integration points as well)\n\t\t// and calculate the contact nodal force\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\t// get the local node number\n\t\t\tint m = sel.m_lnode[n];\n\n\t\t\tFEE2SSlidingContactPoint& cp = m_edge.m_points[m];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has an element associated with it\n\t\t\t// TODO: is this a good way to test for an active constraint\n\t\t\t// The rigid wall criteria seems to work much better.\n\t\t\tif (cp.m_pme != 0)\n\t\t\t{\n\t\t\t\t// This node is active and could lead to a non-zero\n\t\t\t\t// contact force.\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement& mel = *cp.m_pme;\n\t\t\t\tm_surf.UnpackLM(mel, mLM);\n\n\t\t\t\t// calculate the degrees of freedom\n\t\t\t\tint nmeln = mel.Nodes();\n\t\t\t\tint ndof = 3 * (nmeln + 1);\n\t\t\t\tfe.resize(ndof);\n\n\t\t\t\t// calculate the nodal force\n\t\t\t\tContactNodalForce(m, mel, fe);\n\n\t\t\t\t// multiply force with weights\n\t\t\t\tfor (int l = 0; l < ndof; ++l) fe[l] *= detJ[n] * w[n];\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3 * (nmeln + 1));\n\t\t\t\tlm[0] = sLM[n * 3    ];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\n\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (l + 1)    ] = mLM[l * 3];\n\t\t\t\t\tlm[3 * (l + 1) + 1] = mLM[l * 3 + 1];\n\t\t\t\t\tlm[3 * (l + 1) + 2] = mLM[l * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln + 1);\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (int l = 0; l < nmeln; ++l) en[l + 1] = mel.m_node[l];\n\t\t\t\t\t\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEEdgeToSurfaceSlidingContact::ContactNodalForce(int m, FESurfaceElement& mel, vector<double>& fe)\n{\n\t// max nr of element nodes\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\t// secondary surface element nodes\n\tvec3d rtm[MAXMN];\n\n\t// shape function values\n\tdouble H[MAXMN];\n\n\t// contact vectors\n\tdouble N[3 * (MAXMN + 1)];\n\n\tdouble eps = m_eps;\n\n\t// get the mesh\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tint nmeln, ndof;\n\n\tFEE2SSlidingContactPoint& cp = m_edge.m_points[m];\n\n\t// gap function\n\tdouble gap = cp.m_gap;\n\n\t// get primary surface node normal force\n\tdouble Ln = cp.m_Lm;\n\tdouble tn = Ln + eps * gap;\n\ttn = MBRACKET(tn);\n\n\t// get the primary surface  node normal\n\tvec3d& nu = cp.m_nu;\n\n\tnmeln = mel.Nodes();\n\tndof = 3 * (1 + nmeln);\n\n\t// get the secondary surface element node positions\n\tfor (int k = 0; k < nmeln; ++k) rtm[k] = mesh.Node(mel.m_node[k]).m_rt;\n\n\t// isoparametric coordinates of the projected node\n\t// onto the secondary surface element\n\tdouble r = cp.m_rs[0];\n\tdouble s = cp.m_rs[1];\n\n\t// get the secondary surface element shape function values at this node\n\tmel.shape_fnc(H, r, s);\n\n\t// --- N O R M A L   T R A C T I O N ---\n\n\t// calculate contact vectors for normal traction\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\tfor (int l = 0; l < nmeln; ++l)\n\t{\n\t\tN[3 * (l + 1)] = -H[l] * nu.x;\n\t\tN[3 * (l + 1) + 1] = -H[l] * nu.y;\n\t\tN[3 * (l + 1) + 2] = -H[l] * nu.z;\n\t}\n\n\t// calculate force vector\n\tfor (int l = 0; l < ndof; ++l) fe[l] = tn * N[l];\n}\n\n// Evaluates the contriubtion to the stiffness matrix\nvoid FEEdgeToSurfaceSlidingContact::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEElementMatrix ke;\n\n\tconst int MAXMN = FEElement::MAX_NODES;\n\tvector<int> lm(3 * (MAXMN + 1));\n\tvector<int> en(MAXMN + 1);\n\n\tdouble w[MAXMN];\n\tvec3d r0[MAXMN];\n\n\tdouble detJ[MAXMN];\n\tvec3d dxr, dxs;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\t// loop over all primary surface elements\n\tint ne = m_edge.Elements();\n\tfor (int j = 0; j < ne; ++j)\n\t{\n\t\t// unpack the next element\n\t\tFELineElement& se = m_edge.Element(j);\n\t\tint nseln = se.Nodes();\n\n\t\t// get the element's LM array\n\t\tm_edge.UnpackLM(se, sLM);\n\n\t\t// get the nodal coordinates\n\t\tfor (int i = 0; i < nseln; ++i) r0[i] = m_edge.GetMesh()->Node(se.m_node[i]).m_r0;\n\n\t\t// get all the metrics we need \n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tFEE2SSlidingContactPoint& mp1 = static_cast<FEE2SSlidingContactPoint&>(*se.GetMaterialPoint(n));\n\t\t\tdetJ[n] = mp1.m_Jt;\n\t\t\tw[n] = se.GaussWeights()[n];\n\t\t}\n\n\t\t// loop over all integration points (that is nodes)\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tint m = se.m_lnode[n];\n\n\t\t\tFEE2SSlidingContactPoint& cp = m_edge.m_points[m];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has an element associated with it\n\t\t\tif (cp.m_pme != 0)\n\t\t\t{\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement& me = *cp.m_pme;\n\n\t\t\t\t// get the secondary surface element's LM array\n\t\t\t\tm_surf.UnpackLM(me, mLM);\n\n\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\tint ndof = 3 * (nmeln + 1);\n\n\t\t\t\t// calculate the stiffness matrix\n\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\tContactNodalStiffness(m, me, ke);\n\n\t\t\t\t// muliply with weights\n\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\tfor (int l = 0; l < ndof; ++l) ke[k][l] *= detJ[n] * w[n];\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm[0] = sLM[n * 3    ];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\n\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (k + 1)    ] = mLM[k * 3];\n\t\t\t\t\tlm[3 * (k + 1) + 1] = mLM[k * 3 + 1];\n\t\t\t\t\tlm[3 * (k + 1) + 2] = mLM[k * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln + 1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + 1] = me.m_node[k];\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEEdgeToSurfaceSlidingContact::ContactNodalStiffness(int m, FESurfaceElement& mel, matrix& ke)\n{\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\tvector<int> lm(3 * (MAXMN + 1));\n\tvector<int> en(MAXMN + 1);\n\n\tvec3d dxr, dxs;\n\tdouble H[MAXMN], Hr[MAXMN], Hs[MAXMN];\n\n\tdouble N[3 * (MAXMN + 1)], T1[3 * (MAXMN + 1)], T2[3 * (MAXMN + 1)];\n\tdouble N1[3 * (MAXMN + 1)], N2[3 * (MAXMN + 1)], D1[3 * (MAXMN + 1)], D2[3 * (MAXMN + 1)];\n\tdouble Nb1[3 * (MAXMN + 1)], Nb2[3 * (MAXMN + 1)];\n\n\t// get the mesh\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\t// nr of element nodes and degrees of freedom \n\tint nmeln = mel.Nodes();\n\tint ndof = 3 * (1 + nmeln);\n\n\t// penalty factor\n\tdouble eps = m_eps;\n\n\t// nodal coordinates\n\tvec3d rt[MAXMN];\n\tfor (int j = 0; j < nmeln; ++j) rt[j] = mesh.Node(mel.m_node[j]).m_rt;\n\n\t// node natural coordinates in secondary surface element\n\tFEE2SSlidingContactPoint& cp = m_edge.m_points[m];\n\tdouble r = cp.m_rs[0];\n\tdouble s = cp.m_rs[1];\n\n\t// gap\n\tdouble gap = cp.m_gap;\n\n\t// lagrange multiplier\n\tdouble Lm = cp.m_Lm;\n\n\t// get node normal force\n\tdouble tn = Lm + eps * gap;\n\ttn = MBRACKET(tn);\n\n\t// get the node normal\n\tvec3d& nu = cp.m_nu;\n\n\t// get the secondary surface element shape function values and the derivatives at this node\n\tmel.shape_fnc(H, r, s);\n\tmel.shape_deriv(Hr, Hs, r, s);\n\n\t// get the tangent vectors\n\tvec3d tau[2];\n\tm_surf.CoBaseVectors(mel, r, s, tau);\n\n\t// set up the N vector\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\n\tfor (int k = 0; k < nmeln; ++k)\n\t{\n\t\tN[(k + 1) * 3] = -H[k] * nu.x;\n\t\tN[(k + 1) * 3 + 1] = -H[k] * nu.y;\n\t\tN[(k + 1) * 3 + 2] = -H[k] * nu.z;\n\t}\n\n\t// set up the Ti vectors\n\tT1[0] = tau[0].x; T2[0] = tau[1].x;\n\tT1[1] = tau[0].y; T2[1] = tau[1].y;\n\tT1[2] = tau[0].z; T2[2] = tau[1].z;\n\n\tfor (int k = 0; k < nmeln; ++k)\n\t{\n\t\tT1[(k + 1) * 3    ] = -H[k] * tau[0].x;\n\t\tT1[(k + 1) * 3 + 1] = -H[k] * tau[0].y;\n\t\tT1[(k + 1) * 3 + 2] = -H[k] * tau[0].z;\n\n\t\tT2[(k + 1) * 3    ] = -H[k] * tau[1].x;\n\t\tT2[(k + 1) * 3 + 1] = -H[k] * tau[1].y;\n\t\tT2[(k + 1) * 3 + 2] = -H[k] * tau[1].z;\n\t}\n\n\t// set up the Ni vectors\n\tN1[0] = N2[0] = 0;\n\tN1[1] = N2[1] = 0;\n\tN1[2] = N2[2] = 0;\n\n\tfor (int k = 0; k < nmeln; ++k)\n\t{\n\t\tN1[(k + 1) * 3    ] = -Hr[k] * nu.x;\n\t\tN1[(k + 1) * 3 + 1] = -Hr[k] * nu.y;\n\t\tN1[(k + 1) * 3 + 2] = -Hr[k] * nu.z;\n\n\t\tN2[(k + 1) * 3    ] = -Hs[k] * nu.x;\n\t\tN2[(k + 1) * 3 + 1] = -Hs[k] * nu.y;\n\t\tN2[(k + 1) * 3 + 2] = -Hs[k] * nu.z;\n\t}\n\n\t// calculate metric tensor\n\tmat2d M;\n\tM[0][0] = tau[0] * tau[0]; M[0][1] = tau[0] * tau[1];\n\tM[1][0] = tau[1] * tau[0]; M[1][1] = tau[1] * tau[1];\n\n\t// calculate reciprocal metric tensor\n\tmat2d Mi = M.inverse();\n\n\t// calculate curvature tensor\n\tdouble K[2][2] = { 0 };\n\tdouble Grr[FEElement::MAX_NODES];\n\tdouble Grs[FEElement::MAX_NODES];\n\tdouble Gss[FEElement::MAX_NODES];\n\tmel.shape_deriv2(Grr, Grs, Gss, r, s);\n\tfor (int k = 0; k < nmeln; ++k)\n\t{\n\t\tK[0][0] += (nu * rt[k]) * Grr[k];\n\t\tK[0][1] += (nu * rt[k]) * Grs[k];\n\t\tK[1][0] += (nu * rt[k]) * Grs[k];\n\t\tK[1][1] += (nu * rt[k]) * Gss[k];\n\t}\n\n\t// setup A matrix A = M + gK\n\tdouble A[2][2];\n\tA[0][0] = M[0][0] + gap * K[0][0];\n\tA[0][1] = M[0][1] + gap * K[0][1];\n\tA[1][0] = M[1][0] + gap * K[1][0];\n\tA[1][1] = M[1][1] + gap * K[1][1];\n\n\t// calculate determinant of A\n\tdouble detA = A[0][0] * A[1][1] - A[0][1] * A[1][0];\n\n\t// setup Di vectors\n\tfor (int k = 0; k < ndof; ++k)\n\t{\n\t\tD1[k] = (1 / detA) * (A[1][1] * (T1[k] + gap * N1[k]) - A[0][1] * (T2[k] + gap * N2[k]));\n\t\tD2[k] = (1 / detA) * (A[0][0] * (T2[k] + gap * N2[k]) - A[0][1] * (T1[k] + gap * N1[k]));\n\t}\n\n\t// setup Nbi vectors\n\tfor (int k = 0; k < ndof; ++k)\n\t{\n\t\tNb1[k] = N1[k] - K[0][1] * D2[k];\n\t\tNb2[k] = N2[k] - K[0][1] * D1[k];\n\t}\n\n\t// --- N O R M A L   S T I F F N E S S ---\n\tdouble sum;\n\tfor (int k = 0; k < ndof; ++k)\n\t\tfor (int l = 0; l < ndof; ++l)\n\t\t{\n\t\t\tsum = 0;\n\n\t\t\tsum = Mi[0][0] * Nb1[k] * Nb1[l] + Mi[0][1] * (Nb1[k] * Nb2[l] + Nb2[k] * Nb1[l]) + Mi[1][1] * Nb2[k] * Nb2[l];\n\t\t\tsum *= gap;\n\t\t\tsum -= D1[k] * N1[l] + D2[k] * N2[l] + N1[k] * D1[l] + N2[k] * D2[l];\n\t\t\tsum += K[0][1] * (D1[k] * D2[l] + D2[k] * D1[l]);\n\t\t\tsum *= tn;\n\n\t\t\tsum += eps * HEAVYSIDE(Lm + eps * gap) * N[k] * N[l];\n\n\t\t\tke[k][l] = sum;\n\t\t}\n}\n\nvoid FEEdgeToSurfaceSlidingContact::Serialize(DumpStream& ar)\n{\n\tFESurfaceConstraint::Serialize(ar);\n\tm_edge.Serialize(ar);\n\tm_surf.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMech/FEEdgeToSurfaceSlidingContact.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FEEdge.h>\n#include \"FEContactSurface.h\"\n#include <set>\n\n//=======================================================================================\nclass FEE2SSlidingContactPoint : public FELineMaterialPoint\n{\npublic:\n\tdouble\tm_gap = 0.0;\n\tdouble\tm_Ln = 0.0;\t  //!< Lagrange multipliers for contact pressure\n\tdouble\tm_Lm = 0.0;\t  //!< Lagrange multipliers for contact pressure\n\tvec3d\tm_nu;\t  //!< secondary surface normal at primary surface node\n\tvec2d\tm_rs;\t  //!< natural coordinates of primary surface projection on secondary surface element\n\tFESurfaceElement* m_pme = nullptr;\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tFELineMaterialPoint::Serialize(ar);\n\t\tar & m_gap & m_Ln & m_Lm & m_nu & m_rs;\n\t}\n\n\tvoid Init() override\n\t{\n\t\tFELineMaterialPoint::Init();\n\t\tm_gap = 0.0;\n\t\tm_Ln = 0.0;\n\t\tm_Lm = 0.0;\n\t\tm_nu = vec3d(0, 0, 0);\n\t\tm_rs = vec2d(0, 0);\n\t\tm_pme = nullptr;\n\t}\n};\n\n//=======================================================================================\nclass FEEdgeToSurfaceSlidingContactSurface : public FEContactSurface\n{\npublic:\n\tFEEdgeToSurfaceSlidingContactSurface(FEModel* fem);\n\n\tvoid Update();\n};\n\n//=======================================================================================\nclass FEEdgeToSurfaceSlidingContactEdge : public FEEdge\n{\npublic:\n\tFEEdgeToSurfaceSlidingContactEdge(FEModel* fem);\n\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\tbool Init() override;\n\n\tvoid Update();\n\n\tbool Create(FESegmentSet& eset) override;\n\n\tvoid UnpackLM(FELineElement& el, vector<int>& lm);\n\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tint m_dofX = -1;\n\tint m_dofY = -1;\n\tint m_dofZ = -1;\n\n\tvector<FEE2SSlidingContactPoint> m_points;\t//!< sliding contact surface data\n};\n\n//=======================================================================================\nclass FEEdgeToSurfaceSlidingContact : public FESurfaceConstraint\n{\npublic:\n\tFEEdgeToSurfaceSlidingContact(FEModel* fem);\n\npublic:\n\t//! return the surface\n\tFESurface* GetSurface() override;\n\n\t// Build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t// update\n\tvoid Update() override;\n\n\t// init\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t// Evaluates the contriubtion to the stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\nprotected:\n\n\tvoid ProjectSurface(bool bupseg, bool bmove = false);\n\n\tvoid ContactNodalForce(int m, FESurfaceElement& mel, vector<double>& fe);\n\tvoid ContactNodalStiffness(int m, FESurfaceElement& mel, matrix& ke);\n\nprotected:\n\tFEEdgeToSurfaceSlidingContactEdge\tm_edge;\n\tFEEdgeToSurfaceSlidingContactSurface\tm_surf;\n\nprotected:\n\tdouble\tm_atol;\n\tdouble\tm_eps;\n\tint\t\tm_naugmin;\n\tint\t\tm_naugmax;\n\tdouble\tm_stol;\n\tint\t\tm_nsegup;\n\tdouble\tm_sradius;\n\n\tbool m_bfirst;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticANSShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticANSShellDomain.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEBodyForce.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <math.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBioMech.h\"\n\nBEGIN_FECORE_CLASS(FEElasticANSShellDomain, FESSIShellDomain)\n\tADD_PARAMETER(m_secant_stress, \"secant_stress\");\n\tADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticANSShellDomain::FEElasticANSShellDomain(FEModel* pfem) : FESSIShellDomain(pfem), FEElasticDomain(pfem), m_dofV(pfem), m_dofSV(pfem), m_dofSA(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    m_pMat = nullptr;\n\n    m_update_dynamic = true; // default for backward compatibility\n    \n    m_secant_stress = false;\n    m_secant_tangent = false;\n    \n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n        m_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n        m_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEElasticANSShellDomain& FEElasticANSShellDomain::operator = (FEElasticANSShellDomain& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n//! Set flag for update for dynamic quantities\nvoid FEElasticANSShellDomain::SetDynamicUpdateFlag(bool b)\n{\n    m_update_dynamic = b;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEElasticANSShellDomain::Serialize(DumpStream& ar)\n{\n    //erialize the base class, which instantiates the elements\n    FESSIShellDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    // serialize class variables\n    ar & m_update_dynamic;\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof list\nconst FEDofList& FEElasticANSShellDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FESolidMaterial*>(pmat);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::Activate()\n{\n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n                \n                if (node.HasFlags(FENode::SHELL))\n                {\n                    node.set_active(m_dofSU[0]);\n                    node.set_active(m_dofSU[1]);\n                    node.set_active(m_dofSU[2]);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEElasticANSShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    FESSIShellDomain::PreSolveUpdate(timeInfo);\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FEShellElementNew& el = m_Elem[i];\n        el.m_alphai.zero();\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n            pt.m_Wp = pt.m_Wt;\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculates the forces due to the stress\nvoid FEElasticANSShellDomain::InternalForces(FEGlobalVector& R)\n{\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for shared (NS)\n    for (int i=0; i<NS; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n\t\tFEShellElementNew& el = m_Elem[i];\n        \n        // create the element force vector and initialize to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate element's internal force\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble the residual\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for shell elements\n//! Note that we use a one-point gauss integration rule for the thickness\n//! integration. This will integrate linear functions exactly.\n\nvoid FEElasticANSShellDomain::ElementInternalForce(FEShellElementNew& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*    gw = el.GaussWeights();\n    \n    vec3d Gcnt[3];\n    \n    // allocate arrays\n    vector<mat3ds> S(nint);\n    vector<tens4ds> C(nint);\n    vector<double> EE;\n    vector< vector<vec3d>> HU;\n    vector< vector<vec3d>> HW;\n    matrix NS(neln,16);\n    matrix NN(neln,8);\n    \n    // ANS method: Evaluate collocation strains\n    CollocationStrainsANS(el, EE, HU, HW, NS, NN);\n    \n    vector<matrix> hu(neln, matrix(3,6));\n    vector<matrix> hw(neln, matrix(3,6));\n    vector<vec3d> Nu(neln);\n    vector<vec3d> Nw(neln);\n    \n    matrix Fu(3,1), Fw(3,1);\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        \n        ContraBaseVectors0(el, n, Gcnt);\n        \n        EvaluateEh(el, n, Gcnt, el.m_E[n], hu, hw, Nu, Nw);\n        EvaluateANS(el, n, Gcnt, el.m_E[n], hu, hw, EE, HU, HW);\n        \n        // evaluate 2nd P-K stress\n        matrix SC(6,1);\n        mat3ds S = m_pMat->PK2Stress(mp, el.m_E[n]);\n        mat3dsCntMat61(S, Gcnt, SC);\n        \n        // calculate the jacobian and multiply by Gauss weight\n        detJt = detJ0(el, n)*gw[n];\n        \n        for (i=0; i<neln; ++i)\n        {\n            Fu = hu[i]*SC;\n            Fw = hw[i]*SC;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[6*i  ] -= Fu(0,0)*detJt;\n            fe[6*i+1] -= Fu(1,0)*detJt;\n            fe[6*i+2] -= Fu(2,0)*detJt;\n            \n            fe[6*i+3] -= Fw(0,0)*detJt;\n            fe[6*i+4] -= Fw(1,0)*detJt;\n            fe[6*i+5] -= Fw(2,0)*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for\n    for (int i=0; i<NS; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n\t\tFEShellElementNew& el = m_Elem[i];\n        \n        // create the element force vector and initialize to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces to shells\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble the residual\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element body forces for shells\n\nvoid FEElasticANSShellDomain::ElementBodyForce(FEBodyForce& BF, FEShellElementNew& el, vector<double>& fe)\n{\n    // integration weights\n    double* gw = el.GaussWeights();\n    double eta;\n    double *M, detJt;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tdouble dens = m_pMat->Density(mp);\n\n        // calculate the jacobian\n        detJt = detJ0(el, n)*gw[n];\n        \n        M  = el.H(n);\n        eta = el.gt(n);\n        \n        // get the force\n        vec3d f = BF.force(mp);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fu = f*(dens*M[i]*(1+eta)/2*detJt);\n            vec3d fd = f*(dens*M[i]*(1-eta)/2*detJt);\n            \n            fe[6*i  ] -= fu.x;\n            fe[6*i+1] -= fu.y;\n            fe[6*i+2] -= fu.z;\n            \n            fe[6*i+3] -= fd.x;\n            fe[6*i+4] -= fd.y;\n            fe[6*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculate inertial forces\nvoid FEElasticANSShellDomain::InertialForces(FEGlobalVector& R, vector<double>& F)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElementNew& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInertialForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::ElementInertialForce(FEShellElementNew& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    double alpham = tp.alpham;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    // evaluate the element inertial force vector\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        double J0 = detJ0(el, n)*el.GaussWeights()[n];\n        double d = m_pMat->Density(mp);\n        \n        double* M = el.H(n);\n        double eta = el.gt(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fu = pt.m_a*(d*M[i]*(1+eta)/2*J0);\n            vec3d fd = pt.m_a*(d*M[i]*(1-eta)/2*J0);\n            \n            fe[6*i  ] -= fu.x;\n            fe[6*i+1] -= fu.y;\n            fe[6*i+2] -= fu.z;\n            \n            fe[6*i+3] -= fd.x;\n            fe[6*i+4] -= fd.y;\n            fe[6*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEElasticANSShellDomain::ElementBodyForceStiffness(FEBodyForce& BF, FEShellElementNew &el, matrix &ke)\n{\n    int i, j, i6, j6;\n    int neln = el.Nodes();\n    \n    // jacobian\n    double detJ;\n    double *M;\n    double* gw = el.GaussWeights();\n    mat3d K;\n    \n    double Mu[FEElement::MAX_NODES], Md[FEElement::MAX_NODES];\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        detJ = detJ0(el, n)*gw[n];\n\t\tdouble dens = m_pMat->Density(mp);\n\n        // get the stiffness\n        K = BF.stiffness(mp)*dens*detJ;\n        \n        M = el.H(n);\n        \n        double eta = el.gt(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            Mu[i] = M[i]*(1+eta)/2;\n            Md[i] = M[i]*(1-eta)/2;\n        }\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                mat3d Kuu = K*(Mu[i]*Mu[j]);\n                mat3d Kud = K*(Mu[i]*Md[j]);\n                mat3d Kdu = K*(Md[i]*Mu[j]);\n                mat3d Kdd = K*(Md[i]*Md[j]);\n                \n                ke[i6  ][j6  ] += Kuu(0,0); ke[i6  ][j6+1] += Kuu(0,1); ke[i6  ][j6+2] += Kuu(0,2);\n                ke[i6+1][j6  ] += Kuu(1,0); ke[i6+1][j6+1] += Kuu(1,1); ke[i6+1][j6+2] += Kuu(1,2);\n                ke[i6+2][j6  ] += Kuu(2,0); ke[i6+2][j6+1] += Kuu(2,1); ke[i6+2][j6+2] += Kuu(2,2);\n                \n                ke[i6  ][j6+3] += Kud(0,0); ke[i6  ][j6+4] += Kud(0,1); ke[i6  ][j6+5] += Kud(0,2);\n                ke[i6+1][j6+3] += Kud(1,0); ke[i6+1][j6+4] += Kud(1,1); ke[i6+1][j6+5] += Kud(1,2);\n                ke[i6+2][j6+3] += Kud(2,0); ke[i6+2][j6+4] += Kud(2,1); ke[i6+2][j6+5] += Kud(2,2);\n                \n                ke[i6+3][j6  ] += Kdu(0,0); ke[i6+3][j6+1] += Kdu(0,1); ke[i6+3][j6+2] += Kdu(0,2);\n                ke[i6+4][j6  ] += Kdu(1,0); ke[i6+4][j6+1] += Kdu(1,1); ke[i6+4][j6+2] += Kdu(1,2);\n                ke[i6+5][j6  ] += Kdu(2,0); ke[i6+5][j6+1] += Kdu(2,1); ke[i6+5][j6+2] += Kdu(2,2);\n                \n                ke[i6+3][j6+3] += Kdd(0,0); ke[i6+3][j6+4] += Kdd(0,1); ke[i6+3][j6+5] += Kdd(0,2);\n                ke[i6+4][j6+3] += Kdd(1,0); ke[i6+4][j6+4] += Kdd(1,1); ke[i6+4][j6+5] += Kdd(1,2);\n                ke[i6+5][j6+3] += Kdd(2,0); ke[i6+5][j6+4] += Kdd(2,1); ke[i6+5][j6+5] += Kdd(2,2);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEElasticANSShellDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all shell elements\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for shared (NS)\n    for (int iel=0; iel<NS; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementStiffness(iel, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElementNew& el = m_Elem[iel];\n\n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementMassMatrix(el, ke, scale);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all shell elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElementNew& el = m_Elem[iel];\n        \n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the shell element stiffness matrix\n\nvoid FEElasticANSShellDomain::ElementStiffness(int iel, matrix& ke)\n{\n\tFEShellElementNew& el = ShellElement(iel);\n    \n    int i, i6, j, j6, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    vec3d Gcnt[3];\n    \n    // allocate arrays\n    vector<double> EE;\n    vector< vector<vec3d>> HU;\n    vector< vector<vec3d>> HW;\n    matrix NS(neln,16);\n    matrix NN(neln,8);\n    \n    bool ANS = true;\n    \n    if (ANS) CollocationStrainsANS(el, EE, HU, HW, NS, NN);\n    \n    // calculate element stiffness matrix\n    vector<matrix> hu(neln, matrix(3,6));\n    vector<matrix> hw(neln, matrix(3,6));\n    vector<vec3d> Nu(neln);\n    vector<vec3d> Nw(neln);\n    \n    ke.zero();\n    \n    matrix KUU(3,3), KUW(3,3), KWU(3,3), KWW(3,3);\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n        \n        ContraBaseVectors0(el, n, Gcnt);\n        \n        EvaluateEh(el, n, Gcnt, el.m_E[n], hu, hw, Nu, Nw);\n        if (ANS) EvaluateANS(el, n, Gcnt, el.m_E[n], hu, hw, EE, HU, HW);\n        \n        // calculate the jacobian\n        detJt = detJ0(el, n)*gw[n];\n        \n        // evaluate 2nd P-K stress\n        matrix SC(6,1);\n        mat3ds S = m_pMat->PK2Stress(mp, el.m_E[n]);\n        mat3dsCntMat61(S, Gcnt, SC);\n        \n        // evaluate the material tangent\n        matrix CC(6,6);\n        tens4dmm c = m_pMat->MaterialTangent(mp, el.m_E[n]);\n        tens4dmmCntMat66(c, Gcnt, CC);\n//        tens4dsCntMat66(c, Gcnt, CC);\n        \n        // ------------ constitutive component --------------\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                matrix KUU(3,3), KUW(3,3), KWU(3,3), KWW(3,3);\n                KUU = hu[i]*CC*hu[j].transpose();\n                KUW = hu[i]*CC*hw[j].transpose();\n                KWU = hw[i]*CC*hu[j].transpose();\n                KWW = hw[i]*CC*hw[j].transpose();\n                KUU *= detJt; KUW *= detJt; KWU *= detJt; KWW *= detJt;\n                \n                ke[i6  ][j6  ] += KUU(0,0); ke[i6  ][j6+1] += KUU(0,1); ke[i6  ][j6+2] += KUU(0,2);\n                ke[i6+1][j6  ] += KUU(1,0); ke[i6+1][j6+1] += KUU(1,1); ke[i6+1][j6+2] += KUU(1,2);\n                ke[i6+2][j6  ] += KUU(2,0); ke[i6+2][j6+1] += KUU(2,1); ke[i6+2][j6+2] += KUU(2,2);\n                \n                ke[i6  ][j6+3] += KUW(0,0); ke[i6  ][j6+4] += KUW(0,1); ke[i6  ][j6+5] += KUW(0,2);\n                ke[i6+1][j6+3] += KUW(1,0); ke[i6+1][j6+4] += KUW(1,1); ke[i6+1][j6+5] += KUW(1,2);\n                ke[i6+2][j6+3] += KUW(2,0); ke[i6+2][j6+4] += KUW(2,1); ke[i6+2][j6+5] += KUW(2,2);\n                \n                ke[i6+3][j6  ] += KWU(0,0); ke[i6+3][j6+1] += KWU(0,1); ke[i6+3][j6+2] += KWU(0,2);\n                ke[i6+4][j6  ] += KWU(1,0); ke[i6+4][j6+1] += KWU(1,1); ke[i6+4][j6+2] += KWU(1,2);\n                ke[i6+5][j6  ] += KWU(2,0); ke[i6+5][j6+1] += KWU(2,1); ke[i6+5][j6+2] += KWU(2,2);\n                \n                ke[i6+3][j6+3] += KWW(0,0); ke[i6+3][j6+4] += KWW(0,1); ke[i6+3][j6+5] += KWW(0,2);\n                ke[i6+4][j6+3] += KWW(1,0); ke[i6+4][j6+4] += KWW(1,1); ke[i6+4][j6+5] += KWW(1,2);\n                ke[i6+5][j6+3] += KWW(2,0); ke[i6+5][j6+4] += KWW(2,1); ke[i6+5][j6+5] += KWW(2,2);\n            }\n        }\n        \n        // ------------ initial stress component --------------\n        \n        for (i=0; i<neln; ++i) {\n            for (j=0; j<neln; ++j)\n            {\n                double Kuu, Kuw, Kwu, Kww;\n                if (ANS) {\n                    double r = el.gr(n);\n                    double s = el.gs(n);\n                    double N13uu = ((NS(i,0)*NS(j,1) + NS(j,0)*NS(i,1))*(1-s)+\n                                    (NS(i,8)*NS(j,9) + NS(j,8)*NS(i,9))*(1+s))/2;\n                    double N23uu = ((NS(i,12)*NS(j,13) + NS(j,12)*NS(i,13))*(1-r)+\n                                    (NS(i,4)*NS(j,5) + NS(j,4)*NS(i,5))*(1+r))/2;\n                    double N33uu = ((1-r)*(1-s)*NN(i,0)*NN(j,0) +\n                                    (1+r)*(1-s)*NN(i,2)*NN(j,2) +\n                                    (1+r)*(1+s)*NN(i,4)*NN(j,4) +\n                                    (1-r)*(1+s)*NN(i,6)*NN(j,6))/4;\n                    Kuu = (SC(0,0)*Nu[i].x*Nu[j].x+\n                           SC(1,0)*Nu[i].y*Nu[j].y+\n                           SC(2,0)*N33uu+\n                           SC(3,0)*(Nu[i].x*Nu[j].y+Nu[j].x*Nu[i].y)+\n                           SC(4,0)*N23uu+\n                           SC(5,0)*N13uu)*detJt;\n                    double N13uw = ((NS(i,0)*NS(j,3) + NS(j,2)*NS(i,1))*(1-s)+\n                                    (NS(i,8)*NS(j,11) + NS(j,10)*NS(i,9))*(1+s))/2;\n                    double N23uw = ((NS(i,12)*NS(j,15) + NS(j,14)*NS(i,13))*(1-r)+\n                                    (NS(i,4)*NS(j,7) + NS(j,6)*NS(i,5))*(1+r))/2;\n                    double N33uw = ((1-r)*(1-s)*NN(i,0)*NN(j,1) +\n                                    (1+r)*(1-s)*NN(i,2)*NN(j,3) +\n                                    (1+r)*(1+s)*NN(i,4)*NN(j,5) +\n                                    (1-r)*(1+s)*NN(i,6)*NN(j,7))/4;\n                    Kuw = (SC(0,0)*Nu[i].x*Nw[j].x+\n                           SC(1,0)*Nu[i].y*Nw[j].y+\n                           SC(2,0)*N33uw+\n                           SC(3,0)*(Nu[i].x*Nw[j].y+Nu[j].x*Nw[i].y)+\n                           SC(4,0)*N23uw+\n                           SC(5,0)*N13uw)*detJt;\n                    double N13wu = ((NS(i,2)*NS(j,1) + NS(j,0)*NS(i,3))*(1-s)+\n                                    (NS(i,10)*NS(j,9) + NS(j,8)*NS(i,11))*(1+s))/2;\n                    double N23wu = ((NS(i,14)*NS(j,13) + NS(j,12)*NS(i,15))*(1-r)+\n                                    (NS(i,6)*NS(j,5) + NS(j,4)*NS(i,7))*(1+r))/2;\n                    double N33wu = ((1-r)*(1-s)*NN(i,1)*NN(j,0) +\n                                    (1+r)*(1-s)*NN(i,3)*NN(j,2) +\n                                    (1+r)*(1+s)*NN(i,5)*NN(j,4) +\n                                    (1-r)*(1+s)*NN(i,7)*NN(j,6))/4;\n                    Kwu = (SC(0,0)*Nw[i].x*Nu[j].x+\n                           SC(1,0)*Nw[i].y*Nu[j].y+\n                           SC(2,0)*N33wu+\n                           SC(3,0)*(Nw[i].x*Nu[j].y+Nw[j].x*Nu[i].y)+\n                           SC(4,0)*N23wu+\n                           SC(5,0)*N13wu)*detJt;\n                    double N13ww = ((NS(i,2)*NS(j,3) + NS(j,2)*NS(i,3))*(1-s)+\n                                    (NS(i,10)*NS(j,11) + NS(j,10)*NS(i,11))*(1+s))/2;\n                    double N23ww = ((NS(i,14)*NS(j,15) + NS(j,14)*NS(i,15))*(1-r)+\n                                    (NS(i,6)*NS(j,7) + NS(j,6)*NS(i,7))*(1+r))/2;\n                    double N33ww = ((1-r)*(1-s)*NN(i,1)*NN(j,1) +\n                                    (1+r)*(1-s)*NN(i,3)*NN(j,3) +\n                                    (1+r)*(1+s)*NN(i,5)*NN(j,5) +\n                                    (1-r)*(1+s)*NN(i,7)*NN(j,7))/4;\n                    Kww = (SC(0,0)*Nw[i].x*Nw[j].x+\n                           SC(1,0)*Nw[i].y*Nw[j].y+\n                           SC(2,0)*N33ww+\n                           SC(3,0)*(Nw[i].x*Nw[j].y+Nw[j].x*Nw[i].y)+\n                           SC(4,0)*N23ww+\n                           SC(5,0)*N13ww)*detJt;\n                }\n                else {\n                    Kuu = (SC(0,0)*Nu[i].x*Nu[j].x+\n                           SC(1,0)*Nu[i].y*Nu[j].y+\n                           SC(2,0)*Nu[i].z*Nu[j].z+\n                           SC(3,0)*(Nu[i].x*Nu[j].y+Nu[j].x*Nu[i].y)+\n                           SC(4,0)*(Nu[i].y*Nu[j].z+Nu[j].y*Nu[i].z)+\n                           SC(5,0)*(Nu[i].z*Nu[j].x+Nu[j].z*Nu[i].x))*detJt;\n                    Kuw = (SC(0,0)*Nu[i].x*Nw[j].x+\n                           SC(1,0)*Nu[i].y*Nw[j].y+\n                           SC(2,0)*Nu[i].z*Nw[j].z+\n                           SC(3,0)*(Nu[i].x*Nw[j].y+Nu[j].x*Nw[i].y)+\n                           SC(4,0)*(Nu[i].y*Nw[j].z+Nu[j].y*Nw[i].z)+\n                           SC(5,0)*(Nu[i].z*Nw[j].x+Nu[j].z*Nw[i].x))*detJt;\n                    Kwu = (SC(0,0)*Nw[i].x*Nu[j].x+\n                           SC(1,0)*Nw[i].y*Nu[j].y+\n                           SC(2,0)*Nw[i].z*Nu[j].z+\n                           SC(3,0)*(Nw[i].x*Nu[j].y+Nw[j].x*Nu[i].y)+\n                           SC(4,0)*(Nw[i].y*Nu[j].z+Nw[j].y*Nu[i].z)+\n                           SC(5,0)*(Nw[i].z*Nu[j].x+Nw[j].z*Nu[i].x))*detJt;\n                    Kww = (SC(0,0)*Nw[i].x*Nw[j].x+\n                           SC(1,0)*Nw[i].y*Nw[j].y+\n                           SC(2,0)*Nw[i].z*Nw[j].z+\n                           SC(3,0)*(Nw[i].x*Nw[j].y+Nw[j].x*Nw[i].y)+\n                           SC(4,0)*(Nw[i].y*Nw[j].z+Nw[j].y*Nw[i].z)+\n                           SC(5,0)*(Nw[i].z*Nw[j].x+Nw[j].z*Nw[i].x))*detJt;\n                }\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-w component\n                ke[6*i  ][6*j+3] += Kuw;\n                ke[6*i+1][6*j+4] += Kuw;\n                ke[6*i+2][6*j+5] += Kuw;\n                \n                // the w-u component\n                ke[6*i+3][6*j  ] += Kwu;\n                ke[6*i+4][6*j+1] += Kwu;\n                ke[6*i+5][6*j+2] += Kwu;\n                \n                // the w-w component\n                ke[6*i+3][6*j+3] += Kww;\n                ke[6*i+4][6*j+4] += Kww;\n                ke[6*i+5][6*j+5] += Kww;\n            }\n        }\n        \n    } // end loop over gauss-points\n    \n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEElasticANSShellDomain::ElementMassMatrix(FEShellElementNew& el, matrix& ke, double a)\n{\n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (int n=0; n<nint; ++n)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tdouble D = m_pMat->Density(mp);\n\n        // shape functions\n        double* M = el.H(n);\n        \n        // Jacobian\n        double J0 = detJ0(el, n)*gw[n];\n        \n        // parametric coordinate through thickness\n        double eta = el.gt(n);\n        \n        for (int i=0; i<neln; ++i)\n            for (int j=0; j<neln; ++j)\n            {\n                double Kuu = (1+eta)/2*M[i]*(1+eta)/2*M[j]*a*D*J0;\n                double Kud = (1+eta)/2*M[i]*(1-eta)/2*M[j]*a*D*J0;\n                double Kdu = (1-eta)/2*M[i]*(1+eta)/2*M[j]*a*D*J0;\n                double Kdd = (1-eta)/2*M[i]*(1-eta)/2*M[j]*a*D*J0;\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-d component\n                ke[6*i  ][6*j+3] += Kud;\n                ke[6*i+1][6*j+4] += Kud;\n                ke[6*i+2][6*j+5] += Kud;\n                \n                // the d-u component\n                ke[6*i+3][6*j  ] += Kdu;\n                ke[6*i+4][6*j+1] += Kdu;\n                ke[6*i+5][6*j+2] += Kdu;\n                \n                // the d-d component\n                ke[6*i+3][6*j+3] += Kdd;\n                ke[6*i+4][6*j+4] += Kdd;\n                ke[6*i+5][6*j+5] += Kdd;\n            }\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Calculates body forces for shells\n\nvoid FEElasticANSShellDomain::ElementBodyForce(FEModel& fem, FEShellElementNew& el, vector<double>& fe)\n{\n    int NF = fem.ModelLoads();\n    for (int nf = 0; nf < NF; ++nf)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(nf));\n        if (pbf)\n        {\n            // integration weights\n            double* gw = el.GaussWeights();\n            double eta;\n            double *M, detJt;\n            \n            // loop over integration points\n            int nint = el.GaussPoints();\n            int neln = el.Nodes();\n            \n            for (int n=0; n<nint; ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\tdouble dens0 = m_pMat->Density(mp);\n                \n                // calculate density in current configuration\n                double dens = dens0/pt.m_J;\n                \n                // calculate the jacobian\n                detJt = detJ(el, n)*gw[n];\n                \n                M  = el.H(n);\n                eta = el.gt(n);\n                \n                // get the force\n                vec3d f = pbf->force(mp);\n                \n                for (int i=0; i<neln; ++i)\n                {\n                    vec3d fu = f*(dens*M[i]*(1+eta)/2);\n                    vec3d fd = f*(dens*M[i]*(1-eta)/2);\n                    \n                    fe[6*i  ] -= fu.x*detJt;\n                    fe[6*i+1] -= fu.y*detJt;\n                    fe[6*i+2] -= fu.z*detJt;\n                    \n                    fe[6*i+3] -= fd.x*detJt;\n                    fe[6*i+4] -= fd.y*detJt;\n                    fe[6*i+5] -= fd.z*detJt;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::Update(const FETimeInfo& tp)\n{\n    FESSIShellDomain::Update(tp);\n    \n    bool berr = false;\n    int NE = Elements();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            FEShellElement& el = Element(i);\n            if (el.isActive())\n            {\n                UpdateElementStress(i, tp);\n            }\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticANSShellDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double dt = tp.timeIncrement;\n    \n    // get the solid element\n    FEShellElementNew& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    const int NELN = FEElement::MAX_NODES;\n    vec3d r0[NELN], s0[NELN], r[NELN], s[NELN];\n    vec3d v[NELN], w[NELN];\n    vec3d a[NELN], b[NELN];\n    // nodal coordinates\n    GetCurrentNodalCoordinates(el, r, tp.alphaf, false);\n    GetCurrentNodalCoordinates(el, s, tp.alphaf, true);\n    GetReferenceNodalCoordinates(el, r0, false);\n    GetReferenceNodalCoordinates(el, s0, true);\n    \n    // update dynamic quantities\n    if (m_update_dynamic)\n    {\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m_pMesh->Node(el.m_node[j]);\n            v[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*tp.alphaf + node.m_vp*(1-tp.alphaf);\n            w[j] = node.get_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2])*tp.alphaf + node.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2])*(1-tp.alphaf);\n            a[j] = node.m_at*tp.alpham + node.m_ap*(1-tp.alpham);\n            b[j] = node.get_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2])*tp.alpham + node.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2])*(1-tp.alpham);\n        }\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //         since the material point coordinates are used by most materials.\n        mp.m_r0 = evaluate(el, r0, s0, n);\n        mp.m_rt = evaluate(el, r, s, n);\n        \n        // get the deformation gradient and determinant at intermediate time\n        mat3d Ft, Fp;\n        double Jt = defgrad(el, Ft, n);\n        double Jp = defgradp(el, Fp, n);\n        if (tp.alphaf == 1.0)\n        {\n            pt.m_F = Ft;\n            pt.m_J = Jt;\n        }\n        else\n        {\n            pt.m_F = Ft * tp.alphaf + Fp * (1 - tp.alphaf);\n            pt.m_J = pt.m_F.det();\n        }\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (Ft - Fp)*Fi/dt;\n        if (m_update_dynamic)\n        {\n            pt.m_v = evaluate(el, v, w, n);\n            pt.m_a = evaluate(el, a, b, n);\n        }\n        \n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, tp);\n        \n        // calculate the stress at this material point\n        mat3ds S = m_secant_stress ? m_pMat->SecantStress(mp, true) : m_pMat->PK2Stress(mp, el.m_E[n]);\n        pt.m_s = (pt.m_F*S*pt.m_F.transpose()).sym()/pt.m_J;\n        \n        // adjust stress for strain energy conservation\n        if (tp.alphaf == 0.5)\n        {\n            // evaluate strain energy at current time\n            mat3d Ftmp = pt.m_F;\n            double Jtmp = pt.m_J;\n            pt.m_F = Ft;\n            pt.m_J = Jt;\n            FEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(m_pMat);\n            pt.m_Wt = pme->StrainEnergyDensity(mp);\n            pt.m_F = Ftmp;\n            pt.m_J = Jtmp;\n            \n            mat3ds D = pt.m_L.sym();\n            double D2 = D.dotdot(D);\n            if (D2 > 0)\n                pt.m_s += D*(((pt.m_Wt-pt.m_Wp)/(dt*pt.m_J) - pt.m_s.dotdot(D))/D2);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element. That is, copy element data in traits structure\n//! Note that for the shell elements the lm order is different compared\n//! to the solid element ordering. This is because for shell elements the\n//! nodes have six degrees of freedom each, where for solids they only\n//! have 3 dofs.\nvoid FEElasticANSShellDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    lm.resize(N*9);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& node = m_pMesh->Node(el.m_node[i]);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[6*i  ] = id[m_dofU[0]];\n        lm[6*i+1] = id[m_dofU[1]];\n        lm[6*i+2] = id[m_dofU[2]];\n        \n        // next the shell displacement dofs\n        lm[6*i+3] = id[m_dofSU[0]];\n        lm[6*i+4] = id[m_dofSU[1]];\n        lm[6*i+5] = id[m_dofSU[2]];\n        \n        // rigid rotational dofs\n        lm[6*N + 3*i  ] = id[m_dofR[0]];\n        lm[6*N + 3*i+1] = id[m_dofR[1]];\n        lm[6*N + 3*i+2] = id[m_dofR[2]];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate contravariant components of mat3ds tensor\nvoid FEElasticANSShellDomain::mat3dsCntMat61(const mat3ds s, const vec3d* Gcnt, matrix& S)\n{\n    S.resize(6, 1);\n    S(0,0) = Gcnt[0]*(s*Gcnt[0]);\n    S(1,0) = Gcnt[1]*(s*Gcnt[1]);\n    S(2,0) = Gcnt[2]*(s*Gcnt[2]);\n    S(3,0) = Gcnt[0]*(s*Gcnt[1]);\n    S(4,0) = Gcnt[1]*(s*Gcnt[2]);\n    S(5,0) = Gcnt[0]*(s*Gcnt[2]);\n}\n\n\n//-----------------------------------------------------------------------------\n//! Evaluate contravariant components of tens4ds tensor\n//! Cijkl = Gj.(Gi.c.Gl).Gk\nvoid FEElasticANSShellDomain::tens4dsCntMat66(const tens4ds c, const vec3d* Gcnt, matrix& C)\n{\n    C.resize(6, 6);\n    C(0,0) =          Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[0])*Gcnt[0]);  // i=0, j=0, k=0, l=0\n    C(0,1) = C(1,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[1]);  // i=0, j=0, k=1, l=1\n    C(0,2) = C(2,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[2]);  // i=0, j=0, k=2, l=2\n    C(0,3) = C(3,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=0, k=0, l=1\n    C(0,4) = C(4,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=0, k=1, l=2\n    C(0,5) = C(5,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=0, k=0, l=2\n    C(1,1) =          Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[1]);  // i=1, j=1, k=1, l=1\n    C(1,2) = C(2,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[2]);  // i=1, j=1, k=2, l=2\n    C(1,3) = C(3,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[0]);  // i=1, j=1, k=0, l=1\n    C(1,4) = C(4,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=1, k=1, l=2\n    C(1,5) = C(5,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=1, k=0, l=2\n    C(2,2) =          Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[2]);  // i=2, j=2, k=2, l=2\n    C(2,3) = C(3,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[1])*Gcnt[0]);  // i=2, j=2, k=0, l=1\n    C(2,4) = C(4,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[1]);  // i=2, j=2, k=1, l=2\n    C(2,5) = C(5,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[0]);  // i=2, j=2, k=0, l=2\n    C(3,3) =          Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=1, k=0, l=1\n    C(3,4) = C(4,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=1, k=1, l=2\n    C(3,5) = C(5,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=1, k=0, l=2\n    C(4,4) =          Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=2, k=1, l=2\n    C(4,5) = C(5,4) = Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=2, k=0, l=2\n    C(5,5) =          Gcnt[2]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=2, k=0, l=2\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate contravariant components of tens4dm tensor\n//! Cijkl = Gj.(Gi.c.Gl).Gk\nvoid FEElasticANSShellDomain::tens4dmmCntMat66(const tens4dmm c, const vec3d* Gcnt, matrix& C)\n{\n    C.resize(6, 6);\n    C(0,0) =          Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[0])*Gcnt[0]);  // i=0, j=0, k=0, l=0\n    C(0,1) = C(1,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[1]);  // i=0, j=0, k=1, l=1\n    C(0,2) = C(2,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[2]);  // i=0, j=0, k=2, l=2\n    C(0,3) = C(3,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=0, k=0, l=1\n    C(0,4) = C(4,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=0, k=1, l=2\n    C(0,5) = C(5,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=0, k=0, l=2\n    C(1,1) =          Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[1]);  // i=1, j=1, k=1, l=1\n    C(1,2) = C(2,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[2]);  // i=1, j=1, k=2, l=2\n    C(1,3) = C(3,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[0]);  // i=1, j=1, k=0, l=1\n    C(1,4) = C(4,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=1, k=1, l=2\n    C(1,5) = C(5,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=1, k=0, l=2\n    C(2,2) =          Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[2]);  // i=2, j=2, k=2, l=2\n    C(2,3) = C(3,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[1])*Gcnt[0]);  // i=2, j=2, k=0, l=1\n    C(2,4) = C(4,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[1]);  // i=2, j=2, k=1, l=2\n    C(2,5) = C(5,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[0]);  // i=2, j=2, k=0, l=2\n    C(3,3) =          Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=1, k=0, l=1\n    C(3,4) = C(4,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=1, k=1, l=2\n    C(3,5) = C(5,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=1, k=0, l=2\n    C(4,4) =          Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=2, k=1, l=2\n    C(4,5) = C(5,4) = Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=2, k=0, l=2\n    C(5,5) =          Gcnt[2]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=2, k=0, l=2\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate collocation strains for assumed natural strain (ANS) method\nvoid FEElasticANSShellDomain::CollocationStrainsANS(FEShellElementNew& el, vector<double>& E,\n                                                    vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW,\n                                                    matrix& NS, matrix& NN)\n{\n    FETimeInfo& tp = GetFEModel()->GetTime();\n    \n    // ANS method for 4-node quadrilaterials\n    if (el.Nodes() == 4) {\n        vec3d gcov[3], Gcov[3];\n        \n        double Mr[FEElement::MAX_NODES], Ms[FEElement::MAX_NODES], M[FEElement::MAX_NODES];\n        double r, s, t;\n        int neln = el.Nodes();\n        double Nur, Nus, Nut;\n        double Nwr, Nws, Nwt;\n        \n        // Shear strains E13, E23\n        // point A\n        r = 0; s = -1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E13A = (gcov[0]*gcov[2] - Gcov[0]*Gcov[2])/2;\n        vector<vec3d> hu13A(neln);\n        vector<vec3d> hw13A(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,0) = Nur = (1+t)/2*Mr[i];\n            NS(i,1) = Nut = M[i]/2;\n            NS(i,2) = Nwr = (1-t)/2*Mr[i];\n            NS(i,3) = Nwt = -M[i]/2;\n            hu13A[i] = gcov[0]*Nut + gcov[2]*Nur;\n            hw13A[i] = gcov[0]*Nwt + gcov[2]*Nwr;\n        }\n        \n        // point B\n        r = 1; s = 0; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E23B = (gcov[1]*gcov[2] - Gcov[1]*Gcov[2])/2;\n        vector<vec3d> hu23B(neln);\n        vector<vec3d> hw23B(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,4) = Nus = (1+t)/2*Ms[i];\n            NS(i,5) = Nut = M[i]/2;\n            NS(i,6) = Nws = (1-t)/2*Ms[i];\n            NS(i,7) = Nwt = -M[i]/2;\n            hu23B[i] = gcov[2]*Nus + gcov[1]*Nut;\n            hw23B[i] = gcov[2]*Nws + gcov[1]*Nwt;\n        }\n        \n        // point C\n        r = 0; s = 1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E13C = (gcov[0]*gcov[2] - Gcov[0]*Gcov[2])/2;\n        vector<vec3d> hu13C(neln);\n        vector<vec3d> hw13C(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,8) = Nur = (1+t)/2*Mr[i];\n            NS(i,9) = Nut = M[i]/2;\n            NS(i,10) = Nwr = (1-t)/2*Mr[i];\n            NS(i,11) = Nwt = -M[i]/2;\n            hu13C[i] = gcov[0]*Nut + gcov[2]*Nur;\n            hw13C[i] = gcov[0]*Nwt + gcov[2]*Nwr;\n        }\n        \n        // point D\n        r = -1; s = 0; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E23D = (gcov[1]*gcov[2] - Gcov[1]*Gcov[2])/2;\n        vector<vec3d> hu23D(neln);\n        vector<vec3d> hw23D(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,12) = Nus = (1+t)/2*Ms[i];\n            NS(i,13) = Nut = M[i]/2;\n            NS(i,14) = Nws = (1-t)/2*Ms[i];\n            NS(i,15) = Nwt = -M[i]/2;\n            hu23D[i] = gcov[2]*Nus + gcov[1]*Nut;\n            hw23D[i] = gcov[2]*Nws + gcov[1]*Nwt;\n        }\n        \n        // normal strain E33\n        // point E\n        r = -1; s = -1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33E = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33E(neln);\n        vector<vec3d> hw33E(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,0) = Nut = M[i]/2;\n            NN(i,1) = Nwt = -M[i]/2;\n            hu33E[i] = gcov[2]*Nut;\n            hw33E[i] = gcov[2]*Nwt;\n        }\n        \n        // point F\n        r = 1; s = -1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33F = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33F(neln);\n        vector<vec3d> hw33F(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,2) = Nut = M[i]/2;\n            NN(i,3) = Nwt = -M[i]/2;\n            hu33F[i] = gcov[2]*Nut;\n            hw33F[i] = gcov[2]*Nwt;\n        }\n        \n        // point G\n        r = 1; s = 1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33G = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33G(neln);\n        vector<vec3d> hw33G(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,4) = Nut = M[i]/2;\n            NN(i,5) = Nwt = -M[i]/2;\n            hu33G[i] = gcov[2]*Nut;\n            hw33G[i] = gcov[2]*Nwt;\n        }\n        \n        // point H\n        r = -1; s = 1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33H = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33H(neln);\n        vector<vec3d> hw33H(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,6) = Nut = M[i]/2;\n            NN(i,7) = Nwt = -M[i]/2;\n            hu33H[i] = gcov[2]*Nut;\n            hw33H[i] = gcov[2]*Nwt;\n        }\n        // return the results in aggregated format\n        E.resize(8);\n        E[0] = E13A; E[1] = E23B; E[2] = E13C; E[3] = E23D;\n        E[4] = E33E; E[5] = E33F; E[6] = E33G; E[7] = E33H;\n        HU.resize(8,vector<vec3d>(neln)); HW.resize(8,vector<vec3d>(neln));\n        for (int i=0; i<neln; ++i) {\n            HU[0] = hu13A; HU[1] = hu23B; HU[2] = hu13C; HU[3] = hu23D;\n            HU[4] = hu33E; HU[5] = hu33F; HU[6] = hu33G; HU[7] = hu33H;\n            HW[0] = hw13A; HW[1] = hw23B; HW[2] = hw13C; HW[3] = hw23D;\n            HW[4] = hw33E; HW[5] = hw33F; HW[6] = hw33G; HW[7] = hw33H;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate assumed natural strain (ANS)\nvoid FEElasticANSShellDomain::EvaluateANS(FEShellElementNew& el, const int n, const vec3d* Gcnt,\n                                          mat3ds& Ec, vector<matrix>& hu, vector<matrix>& hw,\n                                          vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW)\n{\n    // ANS method for 4-node quadrilaterials\n    if (el.Nodes() == 4) {\n        vec3d Gcov[3];\n        int neln = el.Nodes();\n        \n        double E13A = E[0]; double E23B = E[1];\n        double E13C = E[2]; double E23D = E[3];\n        double E33E = E[4]; double E33F = E[5];\n        double E33G = E[6]; double E33H = E[7];\n        vector<vec3d> hu13A(HU[0]); vector<vec3d> hu23B(HU[1]);\n        vector<vec3d> hu13C(HU[2]); vector<vec3d> hu23D(HU[3]);\n        vector<vec3d> hu33E(HU[4]); vector<vec3d> hu33F(HU[5]);\n        vector<vec3d> hu33G(HU[6]); vector<vec3d> hu33H(HU[7]);\n        vector<vec3d> hw13A(HW[0]); vector<vec3d> hw23B(HW[1]);\n        vector<vec3d> hw13C(HW[2]); vector<vec3d> hw23D(HW[3]);\n        vector<vec3d> hw33E(HW[4]); vector<vec3d> hw33F(HW[5]);\n        vector<vec3d> hw33G(HW[6]); vector<vec3d> hw33H(HW[7]);\n        \n        // Evaluate ANS strains\n        double r = el.gr(n);\n        double s = el.gs(n);\n        double E13ANS = ((1-s)*E13A + (1+s)*E13C)/2;\n        double E23ANS = ((1-r)*E23D + (1+r)*E23B)/2;\n        double E33ANS = ((1-r)*(1-s)*E33E + (1+r)*(1-s)*E33F +\n                         (1+r)*(1+s)*E33G + (1-r)*(1+s)*E33H)/4;\n        vector<vec3d> hu13ANS(neln), hu23ANS(neln), hu33ANS(neln);\n        vector<vec3d> hw13ANS(neln), hw23ANS(neln), hw33ANS(neln);\n        for (int i=0; i<neln; ++i) {\n            hu13ANS[i] = (hu13A[i]*(1-s) + hu13C[i]*(1+s))/2;\n            hw13ANS[i] = (hw13A[i]*(1-s) + hw13C[i]*(1+s))/2;\n            hu23ANS[i] = (hu23D[i]*(1-r) + hu23B[i]*(1+r))/2;\n            hw23ANS[i] = (hw23D[i]*(1-r) + hw23B[i]*(1+r))/2;\n            hu33ANS[i] = (hu33E[i]*(1-r)*(1-s) + hu33F[i]*(1+r)*(1-s) +\n                          hu33G[i]*(1+r)*(1+s) + hu33H[i]*(1-r)*(1+s))/4;\n            hw33ANS[i] = (hw33E[i]*(1-r)*(1-s) + hw33F[i]*(1+r)*(1-s) +\n                          hw33G[i]*(1+r)*(1+s) + hw33H[i]*(1-r)*(1+s))/4;\n        }\n        \n        // Substitute these strain components into Ec\n        CoBaseVectors0(el, n, Gcov);\n        double E11c = Gcov[0]*(Ec*Gcov[0]);\n        double E22c = Gcov[1]*(Ec*Gcov[1]);\n        double E12c = Gcov[0]*(Ec*Gcov[1]);\n        Ec = ((Gcnt[0] & Gcnt[0])*E11c + (Gcnt[1] & Gcnt[1])*E22c + (Gcnt[2] & Gcnt[2])*E33ANS +\n              ((Gcnt[0] & Gcnt[1]) + (Gcnt[1] & Gcnt[0]))*E12c +\n              ((Gcnt[1] & Gcnt[2]) + (Gcnt[2] & Gcnt[1]))*E23ANS +\n              ((Gcnt[2] & Gcnt[0]) + (Gcnt[0] & Gcnt[2]))*E13ANS).sym();\n        for (int i=0; i<neln; ++i) {\n            hu[i](0,5) = hu13ANS[i].x; hu[i](1,5) = hu13ANS[i].y; hu[i](2,5) = hu13ANS[i].z;\n            hw[i](0,5) = hw13ANS[i].x; hw[i](1,5) = hw13ANS[i].y; hw[i](2,5) = hw13ANS[i].z;\n            hu[i](0,4) = hu23ANS[i].x; hu[i](1,4) = hu23ANS[i].y; hu[i](2,4) = hu23ANS[i].z;\n            hw[i](0,4) = hw23ANS[i].x; hw[i](1,4) = hw23ANS[i].y; hw[i](2,4) = hw23ANS[i].z;\n            hu[i](0,2) = hu33ANS[i].x; hu[i](1,2) = hu33ANS[i].y; hu[i](2,2) = hu33ANS[i].z;\n            hw[i](0,2) = hw33ANS[i].x; hw[i](1,2) = hw33ANS[i].y; hw[i](2,2) = hw33ANS[i].z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate strain E and matrix hu and hw\nvoid FEElasticANSShellDomain::EvaluateEh(FEShellElementNew& el, const int n, const vec3d* Gcnt, mat3ds& E,\n                                         vector<matrix>& hu, vector<matrix>& hw, vector<vec3d>& Nu, vector<vec3d>& Nw)\n{\n    FETimeInfo& tp = GetFEModel()->GetTime();\n    \n    const double* Mr, *Ms, *M;\n    vec3d gcov[3];\n    int neln = el.Nodes();\n    \n    FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n    FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n    \n    E = pt.Strain();\n    \n    CoBaseVectors(el, n, gcov, tp.alphaf);\n    double eta = el.gt(n);\n    \n    Mr = el.Hr(n);\n    Ms = el.Hs(n);\n    M  = el.H(n);\n    \n    for (int i=0; i<neln; ++i)\n    {\n        double Nur = Nu[i].x = (1+eta)/2*Mr[i];\n        double Nus = Nu[i].y = (1+eta)/2*Ms[i];\n        double Nut = Nu[i].z = M[i]/2;\n        double Nwr = Nw[i].x = (1-eta)/2*Mr[i];\n        double Nws = Nw[i].y = (1-eta)/2*Ms[i];\n        double Nwt = Nw[i].z = -M[i]/2;\n        hu[i](0,0) = Nur*gcov[0].x;                 hu[i](1,0) = Nur*gcov[0].y;                 hu[i](2,0) = Nur*gcov[0].z;\n        hu[i](0,1) = Nus*gcov[1].x;                 hu[i](1,1) = Nus*gcov[1].y;                 hu[i](2,1) = Nus*gcov[1].z;\n        hu[i](0,2) = Nut*gcov[2].x;                 hu[i](1,2) = Nut*gcov[2].y;                 hu[i](2,2) = Nut*gcov[2].z;\n        hu[i](0,3) = Nur*gcov[1].x + Nus*gcov[0].x; hu[i](1,3) = Nur*gcov[1].y + Nus*gcov[0].y; hu[i](2,3) = Nur*gcov[1].z + Nus*gcov[0].z;\n        hu[i](0,4) = Nus*gcov[2].x + Nut*gcov[1].x; hu[i](1,4) = Nus*gcov[2].y + Nut*gcov[1].y; hu[i](2,4) = Nus*gcov[2].z + Nut*gcov[1].z;\n        hu[i](0,5) = Nut*gcov[0].x + Nur*gcov[2].x; hu[i](1,5) = Nut*gcov[0].y + Nur*gcov[2].y; hu[i](2,5) = Nut*gcov[0].z + Nur*gcov[2].z;\n        hw[i](0,0) = Nwr*gcov[0].x;                 hw[i](1,0) = Nwr*gcov[0].y;                 hw[i](2,0) = Nwr*gcov[0].z;\n        hw[i](0,1) = Nws*gcov[1].x;                 hw[i](1,1) = Nws*gcov[1].y;                 hw[i](2,1) = Nws*gcov[1].z;\n        hw[i](0,2) = Nwt*gcov[2].x;                 hw[i](1,2) = Nwt*gcov[2].y;                 hw[i](2,2) = Nwt*gcov[2].z;\n        hw[i](0,3) = Nwr*gcov[1].x + Nws*gcov[0].x; hw[i](1,3) = Nwr*gcov[1].y + Nws*gcov[0].y; hw[i](2,3) = Nwr*gcov[1].z + Nws*gcov[0].z;\n        hw[i](0,4) = Nws*gcov[2].x + Nwt*gcov[1].x; hw[i](1,4) = Nws*gcov[2].y + Nwt*gcov[1].y; hw[i](2,4) = Nws*gcov[2].z + Nwt*gcov[1].z;\n        hw[i](0,5) = Nwt*gcov[0].x + Nwr*gcov[2].x; hw[i](1,5) = Nwt*gcov[0].y + Nwr*gcov[2].y; hw[i](2,5) = Nwt*gcov[0].z + Nwr*gcov[2].z;\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticANSShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESSIShellDomain.h\"\n#include \"FEElasticDomain.h\"\n#include \"FESolidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain described by 3D shell elements\nclass FEElasticANSShellDomain : public FESSIShellDomain, public FEElasticDomain\n{\npublic:\n    FEElasticANSShellDomain(FEModel* pfem);\n    \n    //! \\todo do I really need this?\n    FEElasticANSShellDomain& operator = (FEElasticANSShellDomain& d);\n    \n    //! Activate the domain\n    void Activate() override;\n    \n    //! Unpack shell element data\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! Set flag for update for dynamic quantities\n    void SetDynamicUpdateFlag(bool b);\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! get the material (overridden from FEDomain)\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n\n\t//! get the total dof list\n\tconst FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEElasticDomain\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! Calculates inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override;\n    \n    //! calculate body force\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! initialize elements for this domain\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    // inertial stiffness\n    void MassMatrix(FELinearSystem& LS, double scale) override;\n    \n    // body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \n    // evaluate strain E and matrix hu and hw\n\tvoid EvaluateEh(FEShellElementNew& el, const int n, const vec3d* Gcnt, mat3ds& E, vector<matrix>& hu, vector<matrix>& hw, vector<vec3d>& Nu, vector<vec3d>& Nw);\n    \npublic:\n    \n    // --- S T I F F N E S S ---\n    \n    //! calculates the shell element stiffness matrix\n    void ElementStiffness(int iel, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for shell elements\n\tvoid ElementInternalForce(FEShellElementNew& el, vector<double>& fe);\n    \n    //! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEModel& fem, FEShellElementNew& el, vector<double>& fe);\n    \n    //! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEBodyForce& BF, FEShellElementNew& el, vector<double>& fe);\n    \n    //! Calculates the inertial force for shell elements\n    void ElementInertialForce(FEShellElementNew& el, vector<double>& fe);\n    \n    //! calculates the solid element mass matrix\n\tvoid ElementMassMatrix(FEShellElementNew& el, matrix& ke, double a);\n    \n    //! calculates the stiffness matrix due to body forces\n\tvoid ElementBodyForceStiffness(FEBodyForce& bf, FEShellElementNew& el, matrix& ke);\n    \npublic:\n    \n    // --- A N S  M E T H O D ---\n    \n    // Evaluate contravariant components of mat3ds tensor\n    void mat3dsCntMat61(const mat3ds s, const vec3d* Gcnt, matrix& S);\n    \n    // Evaluate contravariant components of tens4ds tensor\n    void tens4dsCntMat66(const tens4ds c, const vec3d* Gcnt, matrix& C);\n    void tens4dmmCntMat66(const tens4dmm c, const vec3d* Gcnt, matrix& C);\n\n    // Evaluate the strain using the ANS method\n\tvoid CollocationStrainsANS(FEShellElementNew& el, vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW, matrix& NS, matrix& NN);\n    \n\tvoid EvaluateANS(FEShellElementNew& el, const int n, const vec3d* Gcnt, mat3ds& Ec, vector<matrix>& hu, vector<matrix>& hw, vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW);\n    \nprotected:\n    FESolidMaterial*    m_pMat;\n    int                 m_nEAS;\n    bool                m_update_dynamic;    //!< flag for updating quantities only used in dynamic analysis\n    \n    bool    m_secant_stress;    //!< use secant approximation to stress\n    bool    m_secant_tangent;   //!< flag for using secant tangent\n    \n    FEDofList   m_dofV;\n    FEDofList   m_dofSV;\n    FEDofList    m_dofSA;\n    FEDofList    m_dofR;\n    FEDofList    m_dof;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticBeamDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEElasticBeamDomain.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FEElasticBeamMaterial.h\"\n#include \"FEBioMech.h\"\n#include \"FEBodyForce.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FESolver.h>\n#include <FECore/fecore_debug.h>\n\n\nFEElasticBeamDomain::FEElasticBeamDomain(FEModel* fem) : FEBeamDomain(fem), FEElasticDomain(fem), \n\tm_dofs(fem), m_dofQ(fem), m_dofV(fem), m_dofW(fem), m_dofA(fem)\n{\n\tm_mat = nullptr;\n\n\tif (fem)\n\t{\n\t\tm_dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION));\n\n\t\tm_dofQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t\tm_dofW.AddVariable(FEBioMech::GetVariableName(FEBioMech::BEAM_ANGULAR_VELOCITY));\n\t\tm_dofA.AddVariable(FEBioMech::GetVariableName(FEBioMech::BEAM_ANGULAR_ACCELERATION));\n\t}\n}\n\n// return number of beam elements\nint FEElasticBeamDomain::Elements() const\n{\n\treturn (int)m_Elem.size();\n}\n\n//! return a reference to an element\nFEElement& FEElasticBeamDomain::ElementRef(int i) { return m_Elem[i]; }\nconst FEElement& FEElasticBeamDomain::ElementRef(int i) const { return m_Elem[i]; }\n\nFEBeamElement& FEElasticBeamDomain::Element(int i) { return m_Elem[i]; }\n\n// create function\nbool FEElasticBeamDomain::Create(int elements, FE_Element_Spec espec)\n{\n\tm_Elem.resize(elements);\n\tfor (int i = 0; i < elements; ++i)\n\t{\n\t\tFEBeamElement& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\t// set element type\n\tint etype = (espec.eshape == ET_LINE3 ? FE_BEAM3G2 : FE_BEAM2G2);\n\tForEachElement([=](FEElement& el) { el.SetType(etype); });\n\n\treturn true;\n}\n\nbool FEElasticBeamDomain::Init()\n{\n\tif (FEBeamDomain::Init() == false) return false;\n\n\t// initialize elements\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = m_Elem[i];\n\t\tvec3d r0[FEElement::MAX_NODES];\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j < ne; ++j) r0[j] = Node(el.m_lnode[j]).m_r0;\n\n\t\t// NOTE: This assumes the beam is initially straight!\n\t\t//       This also assumes that nodes 0 and 1 define the boundary nodes. \n\t\tel.m_L0 = (r0[1] - r0[0]).Length();\n\n\t\t// construct beam coordinate system\n\t\tvec3d E3 = r0[1] - r0[0]; E3.Normalize();\n\t\tvec3d E1, E2;\n\t\tif (fabs(E3 * vec3d(1, 0, 0)) > 0.5) E2 = E3 ^ vec3d(0, 1, 0);\n\t\telse E2 = E3 ^ vec3d(1,0,0);\n\t\tE2.Normalize();\n\t\tE1 = E2 ^ E3;\n\t\tel.m_E = mat3d(E1, E2, E3);\n\t}\n\n\treturn true;\n}\n\n//! Get the list of dofs on this domain\nconst FEDofList& FEElasticBeamDomain::GetDOFList() const\n{\n\treturn m_dofs;\n}\n\nvoid FEElasticBeamDomain::SetMaterial(FEMaterial* pm)\n{\n\tFEDomain::SetMaterial(pm);\n\tm_mat = dynamic_cast<FEElasticBeamMaterial*>(pm);\n\tassert(m_mat);\n}\n\nFEMaterial* FEElasticBeamDomain::GetMaterial()\n{\n\treturn m_mat;\n}\n\nvoid FEElasticBeamDomain::PreSolveUpdate(const FETimeInfo& tp)\n{\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEBeamElement& el = Element(iel);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int n = 0; n < nint; ++n) el.GetMaterialPoint(n)->Update(tp);\n\t\t}\n\t}\n}\n\n//! calculate the internal forces\nvoid FEElasticBeamDomain::InternalForces(FEGlobalVector& R)\n{\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEBeamElement& el = Element(iel);\n\n\t\tint ne = el.Nodes();\n\t\tint ndof = ne * 6;\n\t\tvector<double> fe(ndof, 0.0);\n\n\t\tElementInternalForces(el, fe);\n\n\t\tvector<int> lm(6*ne);\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\nvoid FEElasticBeamDomain::ElementInternalForces(FEBeamElement& el, std::vector<double>& fe)\n{\n\t// reference length of beam\n\tdouble L0 = el.m_L0;\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tint ne = el.Nodes();\n\tdouble* w = el.GaussWeights();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t// shape functions\n\t\tdouble* H = el.H(n);\n\t\tdouble* Hr = el.Hr(n);\n\n\t\t// get stress from beam element\n\t\tvec3d t = mp.m_t;\t// stress\n\t\tvec3d m = mp.m_m;\t// moment \n\n\t\tvec3d G0 = mp.m_G0; // = dphi_0/dS\n\n\t\tmat3da S(G0); // skew-symmetric matrix from Grad (TODO: check sign!)\n\n\t\t// J = dS / dr\n\t\tdouble J = L0 / 2.0;\n\n\t\t// shape function derivative (at integration point)\n\t\tdouble G[FEElement::MAX_NODES];\n\t\tfor (int i = 0; i < ne; ++i) G[i] = Hr[i] / J;\n\n\t\tdouble wJ = w[n] * J;\n\n\t\tfor (int i = 0; i < ne; ++i)\n\t\t{\n\t\t\t// build ksi matrix\n\t\t\tmat3dd I(1.0);\n\t\t\tmatrix ksi(6, 6); ksi.zero();\n\t\t\tksi.add(0, 0, (I * G[i]));\n\t\t\tksi.add(3, 3, (I * G[i]));\n\t\t\tksi.add(3, 0, (-S * H[i]));\n\n\t\t\tvector<double> R{ t.x, t.y, t.z, m.x, m.y, m.z };\n\t\t\tvector<double> P(6);\n\t\t\tP = ksi * R;\n\n\t\t\t// assemble\n\t\t\t// note the negative sign: this is because we need to subtract\n\t\t\t// the internal forces from the external forces.\n\t\t\tfor (int j = 0; j < 6; ++j) fe[i * 6 + j] -= P[j] * wJ;\n\t\t}\n\t}\n}\n\n//! Calculate global stiffness matrix\nvoid FEElasticBeamDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEBeamElement& el = Element(iel);\n\n\t\tint ne = el.Nodes();\n\t\tint ndof = ne * 6;\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\n\t\tFEElementMatrix ke(el, lm); ke.zero();\n\t\tElementStiffnessMatrix(el, ke);\n\n\t\tLS.Assemble(ke);\n\t}\n}\n\nvoid FEElasticBeamDomain::ElementStiffnessMatrix(FEBeamElement& el, FEElementMatrix& ke)\n{\n\t// reference length of beam\n\tdouble L0 = el.m_L0;\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tint ne = el.Nodes();\n\tdouble* w = el.GaussWeights();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t// copy local coordinate system (Probably don't need to do this every time)\n\t\tmp.m_Q = el.m_E;\n\n\t\t// get stress from beam element\n\t\tvec3d t = mp.m_t;\t// stress traction\n\t\tvec3d m = mp.m_m;\t// moment \n\n\t\tmat3da St(t);\n\t\tmat3da Sm(m);\n\n\t\t// shape functions\n\t\tdouble* H = el.H(n);\n\t\tdouble* Hr = el.Hr(n);\n\n\t\t// J = dS / dr\n\t\tdouble J = L0 / 2.0;\n\n\t\t// shape function derivative (at integration point)\n\t\tdouble G[FEElement::MAX_NODES];\n\t\tfor (int i = 0; i < ne; ++i) G[i] = Hr[i] / J;\n\n\t\tdouble wJ = w[n] * J;\n\n\t\tvec3d G0 = mp.m_G0; // = dphi_0/dS\n\n\t\tmat3da S(G0); // skew-symmetric matrix from Grad (TODO: check sign!)\n\n\t\tfor (int a = 0; a < ne; ++a)\n\t\t\tfor (int b = 0; b < ne; ++b)\n\t\t\t{\n\t\t\t\t// build ksi matrix\n\t\t\t\tmat3dd I(1.0);\n\t\t\t\tmatrix ksi_a(6, 6); ksi_a.zero();\n\t\t\t\tmatrix ksi_bT(6, 6); ksi_bT.zero();\n\t\t\t\tksi_a.add(0, 0, (I * G[a]));\n\t\t\t\tksi_a.add(3, 3, (I * G[a]));\n\t\t\t\tksi_a.add(3, 0, (-S * H[a]));\n\n\t\t\t\t// we assemble the transpose for b\n\t\t\t\tksi_bT.add(0, 0, (I * G[b]));\n\t\t\t\tksi_bT.add(3, 3, (I * G[b]));\n\t\t\t\tksi_bT.add(0, 3, (S * H[b]));\n\n\t\t\t\t// spatial tangent\n\t\t\t\tmatrix c(6, 6);\n\t\t\t\tm_mat->Tangent(mp, c);\n\n\t\t\t\t// material stiffness\n\t\t\t\tmatrix Se(6, 6);\n\t\t\t\tSe = ksi_a * c * ksi_bT;\n\n\t\t\t\t// geometrical stiffness\n\t\t\t\tmat3d P = (t & G0) - I * (t * G0);\n\t\t\t\tmatrix Te(6, 6); Te.zero();\n\t\t\t\tTe.add(0, 3, (-St * (G[a] * H[b])));\n\t\t\t\tTe.add(3, 0, (St * (H[a] * G[b])));\n\t\t\t\tTe.add(3, 3, P * (H[a] * H[b]) - Sm*(G[a]*H[b]) );\n\n\t\t\t\t// assemble into ke\n\t\t\t\tfor (int i = 0; i < 6; ++i)\n\t\t\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[6 * a + i][6 * b + j] += (Se(i, j) + Te(i, j)) * wJ;\n\t\t\t\t\t}\n\t\t\t}\n\t}\n}\n\nvoid FEElasticBeamDomain::IncrementalUpdate(std::vector<double>& ui, bool finalFlag)\n{\n\t// We need this for the incremental update of prescribed rotational dofs\n\tint niter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n\n\t// update the rotations at the material points\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFEBeamElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\t\tint ni = el.GaussPoints();\n\n\t\t// initial length\n\t\tdouble L0 = el.m_L0;\n\t\tdouble J = L0 / 2.0;\n\n\t\t// get the nodal values of the incremental rotations\n\t\t// NOTE: The ui vector does not contain the prescribed displacement updates!!\n\t\tvector<vec3d> dri(ne);\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tFENode& node = Node(el.m_lnode[j]);\n\t\t\tstd::vector<int>& id = node.m_ID;\n\t\t\tint eq[3] = { id[m_dofs[3]], id[m_dofs[4]], id[m_dofs[5]] };\n\t\t\tvec3d d(0, 0, 0);\n\t\t\tif (eq[0] >= 0) d.x = ui[eq[0]];\n\t\t\tif (eq[1] >= 0) d.y = ui[eq[1]];\n\t\t\tif (eq[2] >= 0) d.z = ui[eq[2]];\n\n\t\t\t// for prescribed nodes, determine the rotation increment\n\t\t\tif ((eq[0] < 0) && (eq[1] < 0) && (eq[2] < 0))\n\t\t\t{\n\t\t\t\tif (niter == 0)\n\t\t\t\t{\n\t\t\t\t\tvec3d Rp, Rt;\n\t\t\t\t\tint n;\n\t\t\t\t\tn = -eq[0] - 1; if (n >= 0) { Rt.x = node.get(m_dofs[3]); Rp.x = node.get_prev(m_dofs[3]); }\n\t\t\t\t\tn = -eq[1] - 1; if (n >= 0) { Rt.y = node.get(m_dofs[4]); Rp.y = node.get_prev(m_dofs[4]); }\n\t\t\t\t\tn = -eq[2] - 1; if (n >= 0) { Rt.z = node.get(m_dofs[5]); Rp.z = node.get_prev(m_dofs[5]); }\n\t\t\t\t\tquatd Qp(Rp), Qt(Rt);\n\t\t\t\t\tquatd dQ = Qp.Conjugate() * Qt;\n\t\t\t\t\td = dQ.GetRotationVector();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdri[j] = d;\n\t\t}\n\n\t\t// evaluate at integration points\n\t\tfor (int n = 0; n < ni; ++n)\n\t\t{\n\t\t\t// get the material point\n\t\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t\t// evaluate at integration point\n\t\t\tvec3d dr = el.Evaluate(&dri[0], n);\n\n\t\t\t// use shape function derivatives\n\t\t\tdouble* Hr = el.Hr(n);\n\t\t\tvec3d drdS(0, 0, 0);\n\t\t\tfor (int a = 0; a < ne; ++a) drdS += dri[a] * (Hr[a] / J);\n\n\t\t\t// calculate exponential map\n\t\t\tmat3d dR; dR.exp(dr);\n\n\t\t\t// extract quaternion\n\t\t\tquatd dq(dR);\n\n\t\t\t// update rotations\n\t\t\tmp.m_Rt = (dq*mp.m_Ri) * mp.m_Rp;\n\n\t\t\t// update spatial curvature\n\t\t\tmat3da Wn(mp.m_kn);\n\t\t\tmat3d W = dR * Wn * dR.transpose(); // this should be a skew-symmetric matrix!\n\n\t\t\tvec3d w2(-W[1][2], W[0][2], -W[0][1]);\n\n\t\t\tdouble g1 = 1, g2 = 1;\n\t\t\tdouble a = dr.norm();\n\t\t\tif (a != 0)\n\t\t\t{\n\t\t\t\tg1 = sin(a) / a;\n\t\t\t\tg2 = sin(0.5 * a) / (0.5 * a);\n\t\t\t}\n\t\t\tvec3d e(dr); e.unit();\n\n\t\t\tvec3d w1 = drdS*g1 + e*((1.0 - g1)*(e*drdS)) + (dr ^ drdS)*(0.5*g2*g2);\n\n\t\t\t// update curvature\n\t\t\tmp.m_k = w1 + w2;\n\n\t\t\tif (finalFlag)\n\t\t\t{\n\t\t\t\tmp.m_Ri = dq * mp.m_Ri;\n\t\t\t\tmp.m_kn = mp.m_k;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEElasticBeamDomain::Update(const FETimeInfo& tp)\n{\n\tint NE = Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEBeamElement& el = Element(i);\n\t\tUpdateElement(el);\n\t}\n}\n\nvoid FEElasticBeamDomain::UpdateElement(FEBeamElement& el)\n{\n\t// get the nodal positions\n\tconstexpr int NMAX = FEElement::MAX_NODES;\n\tvec3d rt[NMAX], vt[NMAX], at[NMAX], wt[NMAX], alt[NMAX];\n\tint ne = el.Nodes();\n\tfor (int i = 0; i < ne; ++i)\n\t{\n\t\tFENode& node = Node(el.m_lnode[i]);\n\t\trt[i] = node.m_rt;\n\t\tvt[i] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tat[i] = node.m_at;\n\n\t\twt[i]  = node.get_vec3d(m_dofW[0], m_dofW[1], m_dofW[2]);\n\t\talt[i] = node.get_vec3d(m_dofA[0], m_dofA[1], m_dofA[2]);\n\t}\n\n\t// initial length\n\tdouble L0 = el.m_L0;\n\tdouble J = L0 / 2;\n\n\tFEElasticBeamMaterial& mat = *m_mat;\n\tdouble rho = mat.m_density;\n\tdouble A_rho = mat.m_A * rho;\n\n\tdouble I1 = rho * mat.m_I1;\n\tdouble I2 = rho * mat.m_I2;\n\tdouble I3 = I1 + I2;\n\n\tvec3d E1 = el.m_E.col(0);\n\tvec3d E2 = el.m_E.col(1);\n\tvec3d E3 = el.m_E.col(2);\n\n\tmat3d I0 = (E1 & E1)*I1 + (E2 & E2)*I2 + (E3 & E3) * I3; // material inertia tensor\n\n\t// loop over all integration points\n\tint nint = el.GaussPoints();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\t// get the material point\n\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t// update quantities for dynamics\n\t\tmp.m_vt = el.Evaluate(vt, n);\n\t\tmp.m_at = el.Evaluate(at, n);\n\t\tmp.m_dpt = mp.m_at * A_rho;\n\n\t\t// (spatial) rotational quantities\n\t\tvec3d w = el.Evaluate(wt, n);\n\t\tvec3d al = el.Evaluate(alt, n);\n\t\tmp.m_wt = w;\n\t\tmp.m_alt = al;\n\n\t\t// calculate the spatial inertia tensor\n\t\tmat3d R = mp.m_Rt.RotationMatrix();\n\t\tmat3d I = R * I0 * R.transpose();\n\n\t\t// calculate rate of angular momentum\n\t\tmp.m_dht = I * al + (w ^ (I * w));\n\n\t\t// evaluate G0 = dphi0/dS\n\t\tdouble* Hr = el.Hr(n);\n\t\tvec3d G0(0, 0, 0);\n\t\tfor (int a = 0; a < ne; ++a)\n\t\t{\n\t\t\tG0 += rt[a] * (Hr[a] / J);\n\t\t}\n\n\t\t// update G0 = dphi0/dS\n\t\tmp.m_G0 = G0;\n\t\tquatd q = mp.m_Rt;\n\t\tquatd qi = q.Conjugate();\n\n\t\t// calculate material strain measures\n\t\tmp.m_Gamma = qi * G0 - E3;\n\t\tmp.m_Kappa = qi * mp.m_k; // m_k is updated in IncrementalUpdate(std::vector<double>& ui)\n\n\t\t// evaluate the (spatial) stress\n\t\tmp.m_Q = el.m_E;\n\t\tm_mat->Stress(mp);\n\t}\n}\n\n//! Calculates the inertial forces (for dynamic problems)\nvoid FEElasticBeamDomain::InertialForces(FEGlobalVector& R, std::vector<double>& F)\n{\n\tfor (auto& el : m_Elem)\n\t{\n\t\tint neln = el.Nodes();\n\t\tvector<double> fe(6 * neln, 0.0);\n\t\tElementInertialForce(el, fe);\n\t\tvector<int> lm(6 * neln);\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\nvoid FEElasticBeamDomain::ElementInertialForce(FEBeamElement& el, std::vector<double>& fe)\n{\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\tdouble* gw = el.GaussWeights();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\tdouble* H = el.H(n);\n\n\t\tdouble J = el.m_L0 / 2.0;\n\t\tdouble Jw = J * gw[n];\n\n\t\tvec3d dp = mp.m_dpt;\n\t\tvec3d dh = mp.m_dht;\n\n\t\tfor (int i = 0; i < neln; ++i)\n\t\t{\n\t\t\tfe[6*i    ] -= H[i]*dp.x*Jw;\n\t\t\tfe[6*i + 1] -= H[i]*dp.y*Jw;\n\t\t\tfe[6*i + 2] -= H[i]*dp.z*Jw;\n\t\t\tfe[6*i + 3] -= H[i]*dh.x*Jw;\n\t\t\tfe[6*i + 4] -= H[i]*dh.y*Jw;\n\t\t\tfe[6*i + 5] -= H[i]*dh.z*Jw;\n\t\t}\n\t}\n}\n\n//! Calculates the mass matrix (for dynamic problems)\nvoid FEElasticBeamDomain::MassMatrix(FELinearSystem& LS, double scale) \n{\n\tfor (auto& el : m_Elem)\n\t{\n\t\tint neln = el.Nodes();\n\t\tFEElementMatrix ke(6 * neln, 6 * neln); ke.zero();\n\t\tElementMassMatrix(el, ke);\n\t\tvector<int> lm(6 * neln);\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\t\tke.SetNodes(el.m_node);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n// tanx = tan(x)/x\ndouble tanx(double x)\n{\n\tdouble r = (fabs(x) < 1e-9 ? 1.0 : tan(x) / x);\n\treturn r;\n}\n\nvoid FEElasticBeamDomain::ElementMassMatrix(FEBeamElement& el, FEElementMatrix& ke)\n{\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\tdouble* gw = el.GaussWeights();\n\n\tFEElasticBeamMaterial& mat = *m_mat;\n\tdouble rho = mat.m_density;\n\tdouble A_rho = mat.m_A * rho;\n\n\tdouble I1 = rho * mat.m_I1;\n\tdouble I2 = rho * mat.m_I2;\n\tdouble I3 = I1 + I2;\n\n\tvec3d E1 = el.m_E.col(0);\n\tvec3d E2 = el.m_E.col(1);\n\tvec3d E3 = el.m_E.col(2);\n\n\tmat3d I0 = (E1 & E1) * I1 + (E2 & E2) * I2 + (E3 & E3) * I3; // material inertia tensor\n\n\tFETimeInfo& ti = GetFEModel()->GetTime();\n\tdouble h = ti.timeIncrement;\n\tdouble b = ti.beta;\n\tdouble g = ti.gamma;\n\tdouble h2bi = 1.0 / (h * h * b);\n\tdouble hg = h * g;\n\n\t// When evaluating at time 0 (to determine initial accelerations), we need \n\t// to make a minor change. \n\tif (ti.currentTime == 0.0) { h2bi = hg = 1.0; }\n\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEElasticBeamMaterialPoint& mp = *(el.GetMaterialPoint(n)->ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\tvec3d ri = mp.m_Ri.GetRotationVector();\n\t\tvec3d e = ri.Normalized();\n\t\tdouble th = ri.norm();\n\t\tmat3da ts(ri);\n\t\tmat3dd I(1.0);\n\t\tmat3ds exe = dyad(e);\n\t\tmat3d T = exe + (I - exe)*(1.0/tanx(th*0.5)) - ts*0.5;\n\n\t\tdouble* H = el.H(n);\n\n\t\tdouble J = el.m_L0 / 2.0;\n\t\tdouble Jw = J * gw[n];\n\n\t\tmat3d Rt = mp.m_Rt.RotationMatrix();\n\t\tmat3d Rp = mp.m_Rp.RotationMatrix();\n\n\t\tvec3d Wt = Rt.transpose() * mp.m_wt;\n\t\tvec3d At = Rt.transpose() * mp.m_alt;\n\t\tmat3da W_hat(Wt);\n\t\tmat3da Hs(I0 * At + (Wt ^ (I0 * Wt)));\n\t\tmat3da IW(I0 * Wt);\n\n\t\tdouble M11 = A_rho * Jw * h2bi;\n\t\tmat3d M22 = (-Rt * Hs + Rt * (I0 - IW * hg + (W_hat * I0) * hg)*h2bi)* Rp.transpose()* T;\n\n\t\tfor (int i=0; i<neln; ++i)\n\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t{\n\t\t\t\tmat3d m1; m1.zero();\n\t\t\t\tm1[0][0] = M11 * H[i] * H[j];\n\t\t\t\tm1[1][1] = M11 * H[i] * H[j];\n\t\t\t\tm1[2][2] = M11 * H[i] * H[j];\n\n\t\t\t\tmat3d m2;\n\t\t\t\tm2 = M22 * (H[i] * H[j] * Jw);\n\n\t\t\t\tint I = 6*i, J = 6*j;\n\t\t\t\tke.add(I, J, m1);\n\t\t\t\tke.add(I + 3, J + 3, m2);\n\t\t\t}\n\t}\n}\n\nvoid FEElasticBeamDomain::BodyForce(FEGlobalVector& R, FEBodyForce& bf) \n{\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEBeamElement& el = Element(iel);\n\n\t\tint ne = el.Nodes();\n\t\tint ndof = ne * 6;\n\t\tvector<double> fe(ndof, 0.0);\n\n\t\tElementBodyForce(el, fe, bf);\n\n\t\tvector<int> lm(6 * ne);\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\nvoid FEElasticBeamDomain::ElementBodyForce(FEBeamElement& el, std::vector<double>& fe, FEBodyForce& bf)\n{\n\t// reference length of beam\n\tdouble L0 = el.m_L0;\n\n\tFEElasticBeamMaterial& mat = *m_mat;\n\tdouble rho = mat.m_density;\n\tdouble A_rho = mat.m_A * rho;\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tint ne = el.Nodes();\n\tdouble* w = el.GaussWeights();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticBeamMaterialPoint& ebm = *(mp.ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t// shape functions\n\t\tdouble* H = el.H(n);\n\n\t\t// J = dS / dr\n\t\tdouble J = L0 / 2.0;\n\n\t\tdouble wJA = w[n] * J * A_rho;\n\n\t\tvec3d fn = bf.force(mp);\n\n\t\tfor (int i = 0; i < ne; ++i)\n\t\t{\n\t\t\tfe[i * 6    ] += H[i] * fn.x * wJA;\n\t\t\tfe[i * 6 + 1] += H[i] * fn.y * wJA;\n\t\t\tfe[i * 6 + 2] += H[i] * fn.z * wJA;\n\t\t}\n\t}\n}\n\nvoid FEElasticBeamDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) \n{\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEBeamElement& el = Element(iel);\n\n\t\tint ne = el.Nodes();\n\t\tint ndof = ne * 6;\n\n\t\tFEElementMatrix ke(ndof, ndof);\n\t\tke.zero();\n\t\tElementBodyForceStiffness(el, ke, bf);\n\n\t\tvector<int> lm(ndof);\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\t\tLS.Assemble(ke);\n\t}\n}\n\nvoid FEElasticBeamDomain::ElementBodyForceStiffness(FEBeamElement& el, FEElementMatrix& ke, FEBodyForce& bf)\n{\n\t// reference length of beam\n\tdouble L0 = el.m_L0;\n\n\tFEElasticBeamMaterial& mat = *m_mat;\n\tdouble rho = mat.m_density;\n\tdouble A_rho = mat.m_A * rho;\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tint ne = el.Nodes();\n\tdouble* w = el.GaussWeights();\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticBeamMaterialPoint& ebm = *(mp.ExtractData<FEElasticBeamMaterialPoint>());\n\n\t\t// shape functions\n\t\tdouble* H = el.H(n);\n\n\t\t// J = dS / dr\n\t\tdouble J = L0 / 2.0;\n\n\t\tdouble wJA = w[n] * J * A_rho;\n\n\t\tmat3d k = bf.stiffness(mp);\n\n\t\tfor (int a = 0; a < ne; ++a)\n\t\t\tfor (int b = 0; b < ne; ++b)\n\t\t\t{\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int j = 0; j < 3; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tke(a * 6 + i, b * 6 + j) -= H[a] * k[i][j] * H[b] * wJA;\n\t\t\t\t\t}\n\t\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticBeamDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEBeamDomain.h>\n#include \"FEElasticDomain.h\"\n#include \"febiomech_api.h\"\n\nclass FEElementMatrix;\nclass FEElasticBeamMaterial;\n\nclass FEBIOMECH_API FEElasticBeamDomain : public FEBeamDomain, public FEElasticDomain\n{\npublic:\n\tFEElasticBeamDomain(FEModel* fem);\n\npublic: // from FEDomain\n\t// create function\n\tbool Create(int elements, FE_Element_Spec espec) override;\n\n\t// initialize\n\tbool Init() override;\n\n\t//! Get the list of dofs on this domain\n\tconst FEDofList& GetDOFList() const override;\n\n\tvoid SetMaterial(FEMaterial* pm) override;\n\n\tFEMaterial* GetMaterial() override;\n\npublic: // from FEMeshPartition\n\n\t// return number of beam elements\n\tint Elements() const override;\n\n\t//! return a reference to an element\n\tFEElement& ElementRef(int i) override;\n\tconst FEElement& ElementRef(int i) const override;\n\n\t// update internal domain variables\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t// update data that depends on the incremental solution update\n\tvoid IncrementalUpdate(std::vector<double>& ui, bool finalFlag) override;\n\n\t// update material point data\n\tvoid PreSolveUpdate(const FETimeInfo& tp) override;\n\npublic:\n\t//! return a beam element\n\tFEBeamElement& Element(int i);\n\npublic: // from FEElasticDomain\n\t//! calculate the internal forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! Calculate the body force vector\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\n\t//! calculate the interial forces (for dynamic problems)\n\tvoid InertialForces(FEGlobalVector& R, std::vector<double>& F) override;\n\n\t//! Calculate global stiffness matrix (only contribution from internal force derivative)\n\t//! \\todo maybe I should rename this the InternalStiffness matrix?\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! Calculate stiffness contribution of body forces\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n\n\t//! calculate the mass matrix (for dynamic problems)\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\nprivate:\n\tvoid ElementInternalForces(FEBeamElement& el, std::vector<double>& fe);\n\n\tvoid ElementStiffnessMatrix(FEBeamElement& el, FEElementMatrix& ke);\n\n\tvoid ElementInertialForce(FEBeamElement& el, std::vector<double>& fe);\n\n\tvoid ElementBodyForce(FEBeamElement& el, std::vector<double>& fe, FEBodyForce& f);\n\n\tvoid ElementBodyForceStiffness(FEBeamElement& el, FEElementMatrix& ke, FEBodyForce& f);\n\n\tvoid ElementMassMatrix(FEBeamElement& el, FEElementMatrix& ke);\n\n\tvoid UpdateElement(FEBeamElement& el);\n\nprivate:\n\tFEDofList\tm_dofs;\n\tFEDofList\tm_dofQ;\n\tFEDofList\tm_dofV;\n\tFEDofList\tm_dofW;\n\tFEDofList\tm_dofA;\n\tFEElasticBeamMaterial* m_mat;\n\tstd::vector<FEBeamElement>\tm_Elem;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticBeamMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEElasticBeamMaterial.h\"\n\nFEElasticBeamMaterialPoint::FEElasticBeamMaterialPoint()\n{\n\n}\n\nvoid FEElasticBeamMaterialPoint::Init()\n{\n\n}\n\nvoid FEElasticBeamMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tm_Rp = m_Rt;\n\tm_Ri = quatd(0, 0, 0);\n\n\tm_vp = m_vt;\n\tm_ap = m_at;\n\tm_wp = m_wt;\n\tm_alp = m_alt;\n}\n\n//=======================================================================================\nBEGIN_FECORE_CLASS(FEElasticBeamMaterial, FEMaterial)\n\tADD_PARAMETER(m_density, FE_RANGE_GREATER(0.0), \"density\")->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_E , \"E\" )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G , \"G\" )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_A , \"A\" )->setUnits(UNIT_AREA);\n\tADD_PARAMETER(m_A1, \"A1\")->setUnits(UNIT_AREA);\n\tADD_PARAMETER(m_A2, \"A2\")->setUnits(UNIT_AREA);\n\tADD_PARAMETER(m_I1, \"I1\");\n\tADD_PARAMETER(m_I2, \"I2\");\nEND_FECORE_CLASS();\n\nFEElasticBeamMaterial::FEElasticBeamMaterial(FEModel* fem) : FEMaterial(fem)\n{\n\tm_density = 1.0;\n\tm_A = m_A1 = m_A2 = 0.0;\n\tm_G = m_E = 0.0;\n\tm_I1 = m_I2 = 0;\n\n\tAddDomainParameter(new FEBeamStress());\n}\n\nvoid FEElasticBeamMaterial::Stress(FEElasticBeamMaterialPoint& mp)\n{\n\tmat3d Q = mp.m_Q;\n\tmat3d Qt = Q.transpose();\n\n\tvec3d gamma = Qt * mp.m_Gamma;\n\tvec3d kappa = Qt * mp.m_Kappa;\n\tdouble J = m_I1 + m_I2;\n\n\t// material vectors\n\tvec3d N = Q*vec3d(m_G * m_A1 * gamma.x, m_G*m_A2*gamma.y, m_E*m_A*gamma.z);\n\tvec3d M = Q*vec3d(m_E * m_I1 * kappa.x, m_E*m_I2*kappa.y, m_G*  J*kappa.z);\n\n\t// spatial vectors\n\tquatd R = mp.m_Rt;\n\tmp.m_t = R * N;\n\tmp.m_m = R * M;\n\n\t// Cauchy stress\n\tvec3d E1 = Q.col(0);\n\tvec3d E2 = Q.col(1);\n\tvec3d E3 = Q.col(2);\n\tvec3d t1 = R * E1;\n\tvec3d t2 = R * E2;\n\tvec3d t3 = R * E3;\n\tvec3d t = mp.m_t;\n\tmp.m_s = (dyad(t1) * (t * t1) + dyad(t2) * (t * t2) + dyad(t3) * (t * t3));\n}\n\nvoid FEElasticBeamMaterial::Tangent(FEElasticBeamMaterialPoint& mp, matrix& C)\n{\n\tmat3d E = mp.m_Q;\n\tmat3d Et = E.transpose();\n\n\tdouble J = m_I1 + m_I2;\n\n\tmat3dd D1(m_G * m_A1, m_G * m_A2, m_E * m_A);\n\tmat3dd D2(m_E * m_I1, m_E * m_I2, m_G *   J);\n\n\tmat3d C1 = E * D1 * Et;\n\tmat3d C2 = E * D2 * Et;\n\n\tquatd R = mp.m_Rt;\n\tmat3d Q = R.RotationMatrix();\n\tmat3d Qt = Q.transpose();\n\n\tmat3d c1 = Q * C1 * Qt;\n\tmat3d c2 = Q * C2 * Qt;\n\n\tC.resize(6, 6);\n\tC.zero();\n\tC.add(0, 0, c1);\n\tC.add(3, 3, c2);\n}\n\nFEMaterialPointData* FEElasticBeamMaterial::CreateMaterialPointData()\n{\n\treturn new FEElasticBeamMaterialPoint();\n}\n\nFEBeamStress::FEBeamStress() : FEDomainParameter(\"stress\") {}\n\nFEParamValue FEBeamStress::value(FEMaterialPoint& mp)\n{\n\tFEElasticBeamMaterialPoint& pt = *mp.ExtractData<FEElasticBeamMaterialPoint>();\n\treturn pt.m_s;\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticBeamMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiomech_api.h\"\n\nclass FEElasticBeamMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFEElasticBeamMaterialPoint();\n\n\tvoid Init() override;\n\n\t//! The Update function is used to update material point data\n\t//! Note that this gets called at the start of the time step during PreSolveUpdate\n\tvoid Update(const FETimeInfo& timeInfo) override;\n\npublic:\n\tvec3d\tm_t;\t// stress vector\n\tvec3d\tm_m;\t// moment vector\n\n\t// local coordinate system\n\tmat3d\tm_Q;\n\n\t// strain measures\n\tvec3d\tm_G0;\n\tvec3d\tm_Gamma;\n\tvec3d\tm_Kappa;\n\n\t// rotation information\n\tquatd\tm_Rp;\t// rotation at previous time step\n\tquatd\tm_Ri;\t// increment at current time step\n\tquatd\tm_Rt;\t// current rotation\n\n\tvec3d\tm_k;\t// spatial curvature\n\tvec3d\tm_kn;\t// spatial curvature at current increment (temp storage)\n\n\t// dynamics\n\tvec3d\tm_vt, m_vp;\t// linear velocity at current and previous time\n\tvec3d\tm_at, m_ap;\t// linear acceleration at current and previous time\n\tvec3d\tm_wt, m_wp;\t// (spatial) angular velocity at current and previous time\n\tvec3d\tm_alt, m_alp;\t// (spatial) angular acceleration at current and previous time\n\n\tvec3d\tm_dpt;\t// rate of linear momentum (current)\n\tvec3d\tm_dht;\t// rate of angular momentum (current)\n\n\t// for output\n\tmat3ds m_s; // Cauchy stress tensor in global coordinates\n};\n\nclass FEElasticBeamMaterial : public FEMaterial\n{\npublic:\n\tFEElasticBeamMaterial(FEModel* fem);\n\n\tvoid Stress(FEElasticBeamMaterialPoint& mp);\n\n\tvoid Tangent(FEElasticBeamMaterialPoint& mp, matrix& C);\n\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic:\n\tdouble\tm_density;\n\tdouble\tm_A, m_A1, m_A2;\n\tdouble\tm_E, m_G;\n\tdouble\tm_I1, m_I2;\n\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEBIOMECH_API FEBeamStress : public FEDomainParameter\n{\npublic:\n\tFEBeamStress();\n\tFEParamValue value(FEMaterialPoint& mp) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEElasticDomain::FEElasticDomain(FEModel* pfem)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"febiomech_api.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalVector;\nclass FEBodyForce;\nclass FESolver;\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for elastic domains.\n\n//! An elastic domain is used by the structural mechanics solver.\n//! This interface defines the functions that have to be implemented by an\n//! elastic domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix \n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOMECH_API FEElasticDomain\n{\npublic:\n\tFEElasticDomain(FEModel* pfem);\n\tvirtual ~FEElasticDomain(){}\n\n\t// --- R E S I D U A L ---\n\n\t//! calculate the internal forces\n\tvirtual void InternalForces(FEGlobalVector& R) = 0;\n\n\t//! Calculate the body force vector\n\tvirtual void BodyForce(FEGlobalVector& R, FEBodyForce& bf) = 0;\n\n\t//! calculate the interial forces (for dynamic problems)\n\tvirtual void InertialForces(FEGlobalVector& R, std::vector<double>& F) = 0;\n\n\t// --- S T I F F N E S S   M A T R I X ---\n\n\t//! Calculate global stiffness matrix (only contribution from internal force derivative)\n\t//! \\todo maybe I should rename this the InternalStiffness matrix?\n\tvirtual void StiffnessMatrix   (FELinearSystem& LS) = 0;\n\n\t//! Calculate stiffness contribution of body forces\n\tvirtual void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) = 0;\n\n\t//! calculate the mass matrix (for dynamic problems)\n\tvirtual void MassMatrix(FELinearSystem& LS, double scale) = 0;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticEASShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticEASShellDomain.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEBodyForce.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <math.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBioMech.h\"\n\nBEGIN_FECORE_CLASS(FEElasticEASShellDomain, FESSIShellDomain)\n\tADD_PARAMETER(m_secant_stress, \"secant_stress\");\n\tADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticEASShellDomain::FEElasticEASShellDomain(FEModel* pfem) : FESSIShellDomain(pfem), FEElasticDomain(pfem), m_dofV(pfem), m_dofSV(pfem), m_dofSA(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    m_pMat = nullptr;\n\n    m_update_dynamic = true; // default for backward compatibility\n    \n    m_secant_stress = false;\n    m_secant_tangent = false;\n    \n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n        m_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n        m_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEElasticEASShellDomain& FEElasticEASShellDomain::operator = (FEElasticEASShellDomain& d)\n{\n    m_Elem = d.m_Elem;\n    m_pMesh = d.m_pMesh;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n//! Set flag for update for dynamic quantities\nvoid FEElasticEASShellDomain::SetDynamicUpdateFlag(bool b)\n{\n    m_update_dynamic = b;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEElasticEASShellDomain::Serialize(DumpStream& ar)\n{\n    //erialize the base class, which instantiates the elements\n    FESSIShellDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n    \n    // serialize class variables\n    ar & m_update_dynamic;\n}\n\n//-----------------------------------------------------------------------------\n// get the total dof list\nconst FEDofList& FEElasticEASShellDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FESolidMaterial*>(pmat);\n}\n\n//-----------------------------------------------------------------------------\nbool FEElasticEASShellDomain::Init()\n{\n    // initialize base class\n\tFESSIShellDomain::Init();\n    \n    // set up EAS arrays\n\tm_nEAS = 7;\n\tfor (int i=0; i<Elements(); ++i)\n    {\n        FEShellElementNew& el = ShellElement(i);\n        int neln = el.Nodes();\n        int nint = el.GaussPoints();\n        el.m_Kaai.resize(m_nEAS, m_nEAS);\n        el.m_fa.resize(m_nEAS, 1);\n        el.m_alpha.resize(m_nEAS, 1); el.m_alpha.zero();\n        el.m_alphat.resize(m_nEAS, 1); el.m_alphat.zero();\n        el.m_alphai.resize(m_nEAS, 1); el.m_alphai.zero();\n        el.m_Kua.resize(neln,matrix(3, m_nEAS));\n        el.m_Kwa.resize(neln,matrix(3, m_nEAS));\n        el.m_E.resize(nint, mat3ds(0, 0, 0, 0, 0, 0));\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::Activate()\n{\n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n                \n                if (node.HasFlags(FENode::SHELL))\n                {\n                    node.set_active(m_dofSU[0]);\n                    node.set_active(m_dofSU[1]);\n                    node.set_active(m_dofSU[2]);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEElasticEASShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    FESSIShellDomain::PreSolveUpdate(timeInfo);\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FEShellElementNew& el = m_Elem[i];\n        el.m_alphai.zero();\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n            pt.m_Wp = pt.m_Wt;\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculates the forces due to the stress\nvoid FEElasticEASShellDomain::InternalForces(FEGlobalVector& R)\n{\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for shared (NS)\n    for (int i=0; i<NS; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n\t\tFEShellElementNew& el = m_Elem[i];\n        \n        // create the element force vector and initialize to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate element's internal force\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble the residual\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for shell elements\n//! Note that we use a one-point gauss integration rule for the thickness\n//! integration. This will integrate linear functions exactly.\n\nvoid FEElasticEASShellDomain::ElementInternalForce(FEShellElementNew& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*    gw = el.GaussWeights();\n    \n    vec3d Gcnt[3];\n    \n    // allocate arrays\n    vector<mat3ds> S(nint);\n    vector<tens4dmm> C(nint);\n    vector<double> EE;\n    vector< vector<vec3d>> HU;\n    vector< vector<vec3d>> HW;\n    matrix NS(neln,16);\n    matrix NN(neln,8);\n    \n    // ANS method: Evaluate collocation strains\n    CollocationStrainsANS(el, EE, HU, HW, NS, NN);\n    \n    // EAS method: Evaluate Kua, Kwa, and Kaa\n    // Also evaluate PK2 stress and material tangent using enhanced strain\n    EvaluateEAS(el, EE, HU, HW, S, C);\n    matrix Kif(7,1);\n    Kif = el.m_Kaai*el.m_fa;\n    \n    vector<matrix> hu(neln, matrix(3,6));\n    vector<matrix> hw(neln, matrix(3,6));\n    vector<vec3d> Nu(neln);\n    vector<vec3d> Nw(neln);\n    \n    // EAS contribution\n    matrix Fu(3,1), Fw(3,1);\n    for (i=0; i<neln; ++i)\n    {\n        Fu = el.m_Kua[i]*Kif;\n        Fw = el.m_Kwa[i]*Kif;\n        \n        // calculate internal force\n        // the '-' sign is so that the internal forces get subtracted\n        // from the global residual vector\n        fe[6*i  ] += Fu(0,0);\n        fe[6*i+1] += Fu(1,0);\n        fe[6*i+2] += Fu(2,0);\n        \n        fe[6*i+3] += Fw(0,0);\n        fe[6*i+4] += Fw(1,0);\n        fe[6*i+5] += Fw(2,0);\n    }\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        ContraBaseVectors0(el, n, Gcnt);\n        \n        mat3ds E;\n        EvaluateEh(el, n, Gcnt, E, hu, hw, Nu, Nw);\n        EvaluateANS(el, n, Gcnt, E, hu, hw, EE, HU, HW);\n        \n        // evaluate 2nd P-K stress\n        matrix SC(6,1);\n        mat3dsCntMat61(S[n], Gcnt, SC);\n        //        mat3ds S = m_pMat->PK2Stress(E);\n        //        mat3dsCntMat61(S, Gcnt, SC);\n        \n        // calculate the jacobian and multiply by Gauss weight\n        detJt = detJ0(el, n)*gw[n];\n        \n        for (i=0; i<neln; ++i)\n        {\n            Fu = hu[i]*SC;\n            Fw = hw[i]*SC;\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[6*i  ] -= Fu(0,0)*detJt;\n            fe[6*i+1] -= Fu(1,0)*detJt;\n            fe[6*i+2] -= Fu(2,0)*detJt;\n            \n            fe[6*i+3] -= Fw(0,0)*detJt;\n            fe[6*i+4] -= Fw(1,0)*detJt;\n            fe[6*i+5] -= Fw(2,0)*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for\n    for (int i=0; i<NS; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n\t\tFEShellElementNew& el = m_Elem[i];\n        \n        // create the element force vector and initialize to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces to shells\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble the residual\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element body forces for shells\n\nvoid FEElasticEASShellDomain::ElementBodyForce(FEBodyForce& BF, FEShellElementNew& el, vector<double>& fe)\n{\n    // integration weights\n    double* gw = el.GaussWeights();\n    double eta;\n    double *M, detJt;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tdouble dens = m_pMat->Density(mp);\n\n        // calculate the jacobian\n        detJt = detJ0(el, n)*gw[n];\n        \n        M  = el.H(n);\n        eta = el.gt(n);\n        \n        // get the force\n        vec3d f = BF.force(mp);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fu = f*(dens*M[i]*(1+eta)/2*detJt);\n            vec3d fd = f*(dens*M[i]*(1-eta)/2*detJt);\n            \n            fe[6*i  ] -= fu.x;\n            fe[6*i+1] -= fu.y;\n            fe[6*i+2] -= fu.z;\n            \n            fe[6*i+3] -= fd.x;\n            fe[6*i+4] -= fd.y;\n            fe[6*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculate inertial forces \\todo Why is F no longer needed?\nvoid FEElasticEASShellDomain::InertialForces(FEGlobalVector& R, vector<double>& F)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElementNew& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInertialForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::ElementInertialForce(FEShellElementNew& el, vector<double>& fe)\n{\n    const FETimeInfo& tp = GetFEModel()->GetTime();\n    double alpham = tp.alpham;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    // evaluate the element inertial force vector\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n        double J0 = detJ0(el, n)*el.GaussWeights()[n];\n        double d = m_pMat->Density(mp);\n        \n        double* M = el.H(n);\n        double eta = el.gt(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fu = pt.m_a*(d*M[i]*(1+eta)/2*J0);\n            vec3d fd = pt.m_a*(d*M[i]*(1-eta)/2*J0);\n            \n            fe[6*i  ] -= fu.x;\n            fe[6*i+1] -= fu.y;\n            fe[6*i+2] -= fu.z;\n            \n            fe[6*i+3] -= fd.x;\n            fe[6*i+4] -= fd.y;\n            fe[6*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEElasticEASShellDomain::ElementBodyForceStiffness(FEBodyForce& BF, FEShellElementNew &el, matrix &ke)\n{\n    int i, j, i6, j6;\n    int neln = el.Nodes();\n    \n    // jacobian\n    double detJ;\n    double *M;\n    double* gw = el.GaussWeights();\n    mat3d K;\n    \n    double Mu[FEElement::MAX_NODES], Md[FEElement::MAX_NODES];\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        detJ = detJ0(el, n)*gw[n];\n\t\tdouble dens = m_pMat->Density(mp);\n        // get the stiffness\n        K = BF.stiffness(mp)*dens*detJ;\n        \n        M = el.H(n);\n        \n        double eta = el.gt(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            Mu[i] = M[i]*(1+eta)/2;\n            Md[i] = M[i]*(1-eta)/2;\n        }\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                mat3d Kuu = K*(Mu[i]*Mu[j]);\n                mat3d Kud = K*(Mu[i]*Md[j]);\n                mat3d Kdu = K*(Md[i]*Mu[j]);\n                mat3d Kdd = K*(Md[i]*Md[j]);\n                \n                ke[i6  ][j6  ] += Kuu(0,0); ke[i6  ][j6+1] += Kuu(0,1); ke[i6  ][j6+2] += Kuu(0,2);\n                ke[i6+1][j6  ] += Kuu(1,0); ke[i6+1][j6+1] += Kuu(1,1); ke[i6+1][j6+2] += Kuu(1,2);\n                ke[i6+2][j6  ] += Kuu(2,0); ke[i6+2][j6+1] += Kuu(2,1); ke[i6+2][j6+2] += Kuu(2,2);\n                \n                ke[i6  ][j6+3] += Kud(0,0); ke[i6  ][j6+4] += Kud(0,1); ke[i6  ][j6+5] += Kud(0,2);\n                ke[i6+1][j6+3] += Kud(1,0); ke[i6+1][j6+4] += Kud(1,1); ke[i6+1][j6+5] += Kud(1,2);\n                ke[i6+2][j6+3] += Kud(2,0); ke[i6+2][j6+4] += Kud(2,1); ke[i6+2][j6+5] += Kud(2,2);\n                \n                ke[i6+3][j6  ] += Kdu(0,0); ke[i6+3][j6+1] += Kdu(0,1); ke[i6+3][j6+2] += Kdu(0,2);\n                ke[i6+4][j6  ] += Kdu(1,0); ke[i6+4][j6+1] += Kdu(1,1); ke[i6+4][j6+2] += Kdu(1,2);\n                ke[i6+5][j6  ] += Kdu(2,0); ke[i6+5][j6+1] += Kdu(2,1); ke[i6+5][j6+2] += Kdu(2,2);\n                \n                ke[i6+3][j6+3] += Kdd(0,0); ke[i6+3][j6+4] += Kdd(0,1); ke[i6+3][j6+5] += Kdd(0,2);\n                ke[i6+4][j6+3] += Kdd(1,0); ke[i6+4][j6+4] += Kdd(1,1); ke[i6+4][j6+5] += Kdd(1,2);\n                ke[i6+5][j6+3] += Kdd(2,0); ke[i6+5][j6+4] += Kdd(2,1); ke[i6+5][j6+5] += Kdd(2,2);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEElasticEASShellDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all shell elements\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for shared (NS)\n    for (int iel=0; iel<NS; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementStiffness(iel, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElementNew& el = m_Elem[iel];\n        \n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementMassMatrix(el, ke, scale);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all shell elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElementNew& el = m_Elem[iel];\n        \n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the shell element stiffness matrix\n\nvoid FEElasticEASShellDomain::ElementStiffness(int iel, matrix& ke)\n{\n\tFEShellElementNew& el = ShellElement(iel);\n    \n    int i, i6, j, j6, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    vec3d Gcnt[3];\n    \n    // allocate arrays\n    vector<mat3ds> S(nint);\n    vector<tens4dmm> C(nint);\n    vector<double> EE;\n    vector< vector<vec3d>> HU;\n    vector< vector<vec3d>> HW;\n    matrix NS(neln,16);\n    matrix NN(neln,8);\n    \n    bool ANS = true;\n    //    bool ANS = false;\n    \n    if (ANS) CollocationStrainsANS(el, EE, HU, HW, NS, NN);\n    \n    // EAS method: Evaluate Kua, Kwa, and Kaa\n    // Also evaluate PK2 stress and material tangent using enhanced strain\n    EvaluateEAS(el, EE, HU, HW, S, C);\n    \n    // calculate element stiffness matrix\n    vector<matrix> hu(neln, matrix(3,6));\n    vector<matrix> hw(neln, matrix(3,6));\n    vector<vec3d> Nu(neln);\n    vector<vec3d> Nw(neln);\n    \n    ke.zero();\n    \n    matrix KUU(3,3), KUW(3,3), KWU(3,3), KWW(3,3);\n    for (i=0, i6=0; i<neln; ++i, i6 += 6)\n    {\n        for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n        {\n            KUU = (el.m_Kua[i]*el.m_Kaai*el.m_Kua[j].transpose());\n            KUW = (el.m_Kua[i]*el.m_Kaai*el.m_Kwa[j].transpose());\n            KWU = (el.m_Kwa[i]*el.m_Kaai*el.m_Kua[j].transpose());\n            KWW = (el.m_Kwa[i]*el.m_Kaai*el.m_Kwa[j].transpose());\n            \n            ke[i6  ][j6  ] -= KUU(0,0); ke[i6  ][j6+1] -= KUU(0,1); ke[i6  ][j6+2] -= KUU(0,2);\n            ke[i6+1][j6  ] -= KUU(1,0); ke[i6+1][j6+1] -= KUU(1,1); ke[i6+1][j6+2] -= KUU(1,2);\n            ke[i6+2][j6  ] -= KUU(2,0); ke[i6+2][j6+1] -= KUU(2,1); ke[i6+2][j6+2] -= KUU(2,2);\n            \n            ke[i6  ][j6+3] -= KUW(0,0); ke[i6  ][j6+4] -= KUW(0,1); ke[i6  ][j6+5] -= KUW(0,2);\n            ke[i6+1][j6+3] -= KUW(1,0); ke[i6+1][j6+4] -= KUW(1,1); ke[i6+1][j6+5] -= KUW(1,2);\n            ke[i6+2][j6+3] -= KUW(2,0); ke[i6+2][j6+4] -= KUW(2,1); ke[i6+2][j6+5] -= KUW(2,2);\n            \n            ke[i6+3][j6  ] -= KWU(0,0); ke[i6+3][j6+1] -= KWU(0,1); ke[i6+3][j6+2] -= KWU(0,2);\n            ke[i6+4][j6  ] -= KWU(1,0); ke[i6+4][j6+1] -= KWU(1,1); ke[i6+4][j6+2] -= KWU(1,2);\n            ke[i6+5][j6  ] -= KWU(2,0); ke[i6+5][j6+1] -= KWU(2,1); ke[i6+5][j6+2] -= KWU(2,2);\n            \n            ke[i6+3][j6+3] -= KWW(0,0); ke[i6+3][j6+4] -= KWW(0,1); ke[i6+3][j6+5] -= KWW(0,2);\n            ke[i6+4][j6+3] -= KWW(1,0); ke[i6+4][j6+4] -= KWW(1,1); ke[i6+4][j6+5] -= KWW(1,2);\n            ke[i6+5][j6+3] -= KWW(2,0); ke[i6+5][j6+4] -= KWW(2,1); ke[i6+5][j6+5] -= KWW(2,2);\n        }\n    }\n    \n    for (n=0; n<nint; ++n)\n    {\n        ContraBaseVectors0(el, n, Gcnt);\n        \n        mat3ds E;\n        EvaluateEh(el, n, Gcnt, E, hu, hw, Nu, Nw);\n        if (ANS) EvaluateANS(el, n, Gcnt, E, hu, hw, EE, HU, HW);\n        \n        // calculate the jacobian\n        detJt = detJ0(el, n)*gw[n];\n        \n        // evaluate 2nd P-K stress\n        matrix SC(6,1);\n        mat3dsCntMat61(S[n], Gcnt, SC);\n        //        mat3ds S = m_pMat->PK2Stress(E);\n        //        mat3dsCntMat61(S, Gcnt, SC);\n        \n        // evaluate the material tangent\n        matrix CC(6,6);\n        tens4dmmCntMat66(C[n], Gcnt, CC);\n//        tens4dsCntMat66(C[n], Gcnt, CC);\n        //        tens4ds c = m_pMat->MaterialTangent(E);\n        //        tens4dsCntMat66(c, Gcnt, CC);\n        \n        // ------------ constitutive component --------------\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                matrix KUU(3,3), KUW(3,3), KWU(3,3), KWW(3,3);\n                KUU = hu[i]*CC*hu[j].transpose();\n                KUW = hu[i]*CC*hw[j].transpose();\n                KWU = hw[i]*CC*hu[j].transpose();\n                KWW = hw[i]*CC*hw[j].transpose();\n                KUU *= detJt; KUW *= detJt; KWU *= detJt; KWW *= detJt;\n                \n                ke[i6  ][j6  ] += KUU(0,0); ke[i6  ][j6+1] += KUU(0,1); ke[i6  ][j6+2] += KUU(0,2);\n                ke[i6+1][j6  ] += KUU(1,0); ke[i6+1][j6+1] += KUU(1,1); ke[i6+1][j6+2] += KUU(1,2);\n                ke[i6+2][j6  ] += KUU(2,0); ke[i6+2][j6+1] += KUU(2,1); ke[i6+2][j6+2] += KUU(2,2);\n                \n                ke[i6  ][j6+3] += KUW(0,0); ke[i6  ][j6+4] += KUW(0,1); ke[i6  ][j6+5] += KUW(0,2);\n                ke[i6+1][j6+3] += KUW(1,0); ke[i6+1][j6+4] += KUW(1,1); ke[i6+1][j6+5] += KUW(1,2);\n                ke[i6+2][j6+3] += KUW(2,0); ke[i6+2][j6+4] += KUW(2,1); ke[i6+2][j6+5] += KUW(2,2);\n                \n                ke[i6+3][j6  ] += KWU(0,0); ke[i6+3][j6+1] += KWU(0,1); ke[i6+3][j6+2] += KWU(0,2);\n                ke[i6+4][j6  ] += KWU(1,0); ke[i6+4][j6+1] += KWU(1,1); ke[i6+4][j6+2] += KWU(1,2);\n                ke[i6+5][j6  ] += KWU(2,0); ke[i6+5][j6+1] += KWU(2,1); ke[i6+5][j6+2] += KWU(2,2);\n                \n                ke[i6+3][j6+3] += KWW(0,0); ke[i6+3][j6+4] += KWW(0,1); ke[i6+3][j6+5] += KWW(0,2);\n                ke[i6+4][j6+3] += KWW(1,0); ke[i6+4][j6+4] += KWW(1,1); ke[i6+4][j6+5] += KWW(1,2);\n                ke[i6+5][j6+3] += KWW(2,0); ke[i6+5][j6+4] += KWW(2,1); ke[i6+5][j6+5] += KWW(2,2);\n            }\n        }\n        \n        // ------------ initial stress component --------------\n        \n        for (i=0; i<neln; ++i) {\n            for (j=0; j<neln; ++j)\n            {\n                double Kuu, Kuw, Kwu, Kww;\n                if (ANS) {\n                    double r = el.gr(n);\n                    double s = el.gs(n);\n                    double N13uu = ((NS(i,0)*NS(j,1) + NS(j,0)*NS(i,1))*(1-s)+\n                                    (NS(i,8)*NS(j,9) + NS(j,8)*NS(i,9))*(1+s))/2;\n                    double N23uu = ((NS(i,12)*NS(j,13) + NS(j,12)*NS(i,13))*(1-r)+\n                                    (NS(i,4)*NS(j,5) + NS(j,4)*NS(i,5))*(1+r))/2;\n                    double N33uu = ((1-r)*(1-s)*NN(i,0)*NN(j,0) +\n                                    (1+r)*(1-s)*NN(i,2)*NN(j,2) +\n                                    (1+r)*(1+s)*NN(i,4)*NN(j,4) +\n                                    (1-r)*(1+s)*NN(i,6)*NN(j,6))/4;\n                    Kuu = (SC(0,0)*Nu[i].x*Nu[j].x+\n                           SC(1,0)*Nu[i].y*Nu[j].y+\n                           SC(2,0)*N33uu+\n                           SC(3,0)*(Nu[i].x*Nu[j].y+Nu[j].x*Nu[i].y)+\n                           SC(4,0)*N23uu+\n                           SC(5,0)*N13uu)*detJt;\n                    double N13uw = ((NS(i,0)*NS(j,3) + NS(j,2)*NS(i,1))*(1-s)+\n                                    (NS(i,8)*NS(j,11) + NS(j,10)*NS(i,9))*(1+s))/2;\n                    double N23uw = ((NS(i,12)*NS(j,15) + NS(j,14)*NS(i,13))*(1-r)+\n                                    (NS(i,4)*NS(j,7) + NS(j,6)*NS(i,5))*(1+r))/2;\n                    double N33uw = ((1-r)*(1-s)*NN(i,0)*NN(j,1) +\n                                    (1+r)*(1-s)*NN(i,2)*NN(j,3) +\n                                    (1+r)*(1+s)*NN(i,4)*NN(j,5) +\n                                    (1-r)*(1+s)*NN(i,6)*NN(j,7))/4;\n                    Kuw = (SC(0,0)*Nu[i].x*Nw[j].x+\n                           SC(1,0)*Nu[i].y*Nw[j].y+\n                           SC(2,0)*N33uw+\n                           SC(3,0)*(Nu[i].x*Nw[j].y+Nu[j].x*Nw[i].y)+\n                           SC(4,0)*N23uw+\n                           SC(5,0)*N13uw)*detJt;\n                    double N13wu = ((NS(i,2)*NS(j,1) + NS(j,0)*NS(i,3))*(1-s)+\n                                    (NS(i,10)*NS(j,9) + NS(j,8)*NS(i,11))*(1+s))/2;\n                    double N23wu = ((NS(i,14)*NS(j,13) + NS(j,12)*NS(i,15))*(1-r)+\n                                    (NS(i,6)*NS(j,5) + NS(j,4)*NS(i,7))*(1+r))/2;\n                    double N33wu = ((1-r)*(1-s)*NN(i,1)*NN(j,0) +\n                                    (1+r)*(1-s)*NN(i,3)*NN(j,2) +\n                                    (1+r)*(1+s)*NN(i,5)*NN(j,4) +\n                                    (1-r)*(1+s)*NN(i,7)*NN(j,6))/4;\n                    Kwu = (SC(0,0)*Nw[i].x*Nu[j].x+\n                           SC(1,0)*Nw[i].y*Nu[j].y+\n                           SC(2,0)*N33wu+\n                           SC(3,0)*(Nw[i].x*Nu[j].y+Nw[j].x*Nu[i].y)+\n                           SC(4,0)*N23wu+\n                           SC(5,0)*N13wu)*detJt;\n                    double N13ww = ((NS(i,2)*NS(j,3) + NS(j,2)*NS(i,3))*(1-s)+\n                                    (NS(i,10)*NS(j,11) + NS(j,10)*NS(i,11))*(1+s))/2;\n                    double N23ww = ((NS(i,14)*NS(j,15) + NS(j,14)*NS(i,15))*(1-r)+\n                                    (NS(i,6)*NS(j,7) + NS(j,6)*NS(i,7))*(1+r))/2;\n                    double N33ww = ((1-r)*(1-s)*NN(i,1)*NN(j,1) +\n                                    (1+r)*(1-s)*NN(i,3)*NN(j,3) +\n                                    (1+r)*(1+s)*NN(i,5)*NN(j,5) +\n                                    (1-r)*(1+s)*NN(i,7)*NN(j,7))/4;\n                    Kww = (SC(0,0)*Nw[i].x*Nw[j].x+\n                           SC(1,0)*Nw[i].y*Nw[j].y+\n                           SC(2,0)*N33ww+\n                           SC(3,0)*(Nw[i].x*Nw[j].y+Nw[j].x*Nw[i].y)+\n                           SC(4,0)*N23ww+\n                           SC(5,0)*N13ww)*detJt;\n                }\n                else {\n                    Kuu = (SC(0,0)*Nu[i].x*Nu[j].x+\n                           SC(1,0)*Nu[i].y*Nu[j].y+\n                           SC(2,0)*Nu[i].z*Nu[j].z+\n                           SC(3,0)*(Nu[i].x*Nu[j].y+Nu[j].x*Nu[i].y)+\n                           SC(4,0)*(Nu[i].y*Nu[j].z+Nu[j].y*Nu[i].z)+\n                           SC(5,0)*(Nu[i].z*Nu[j].x+Nu[j].z*Nu[i].x))*detJt;\n                    Kuw = (SC(0,0)*Nu[i].x*Nw[j].x+\n                           SC(1,0)*Nu[i].y*Nw[j].y+\n                           SC(2,0)*Nu[i].z*Nw[j].z+\n                           SC(3,0)*(Nu[i].x*Nw[j].y+Nu[j].x*Nw[i].y)+\n                           SC(4,0)*(Nu[i].y*Nw[j].z+Nu[j].y*Nw[i].z)+\n                           SC(5,0)*(Nu[i].z*Nw[j].x+Nu[j].z*Nw[i].x))*detJt;\n                    Kwu = (SC(0,0)*Nw[i].x*Nu[j].x+\n                           SC(1,0)*Nw[i].y*Nu[j].y+\n                           SC(2,0)*Nw[i].z*Nu[j].z+\n                           SC(3,0)*(Nw[i].x*Nu[j].y+Nw[j].x*Nu[i].y)+\n                           SC(4,0)*(Nw[i].y*Nu[j].z+Nw[j].y*Nu[i].z)+\n                           SC(5,0)*(Nw[i].z*Nu[j].x+Nw[j].z*Nu[i].x))*detJt;\n                    Kww = (SC(0,0)*Nw[i].x*Nw[j].x+\n                           SC(1,0)*Nw[i].y*Nw[j].y+\n                           SC(2,0)*Nw[i].z*Nw[j].z+\n                           SC(3,0)*(Nw[i].x*Nw[j].y+Nw[j].x*Nw[i].y)+\n                           SC(4,0)*(Nw[i].y*Nw[j].z+Nw[j].y*Nw[i].z)+\n                           SC(5,0)*(Nw[i].z*Nw[j].x+Nw[j].z*Nw[i].x))*detJt;\n                }\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-w component\n                ke[6*i  ][6*j+3] += Kuw;\n                ke[6*i+1][6*j+4] += Kuw;\n                ke[6*i+2][6*j+5] += Kuw;\n                \n                // the w-u component\n                ke[6*i+3][6*j  ] += Kwu;\n                ke[6*i+4][6*j+1] += Kwu;\n                ke[6*i+5][6*j+2] += Kwu;\n                \n                // the w-w component\n                ke[6*i+3][6*j+3] += Kww;\n                ke[6*i+4][6*j+4] += Kww;\n                ke[6*i+5][6*j+5] += Kww;\n            }\n        }\n        \n    } // end loop over gauss-points\n    \n}\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEElasticEASShellDomain::ElementMassMatrix(FEShellElementNew& el, matrix& ke, double a)\n{\n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (int n=0; n<nint; ++n)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\tdouble D = m_pMat->Density(mp);\n\n        // shape functions\n        double* M = el.H(n);\n        \n        // Jacobian\n        double J0 = detJ0(el, n)*gw[n];\n        \n        // parametric coordinate through thickness\n        double eta = el.gt(n);\n        \n        for (int i=0; i<neln; ++i)\n            for (int j=0; j<neln; ++j)\n            {\n                double Kuu = (1+eta)/2*M[i]*(1+eta)/2*M[j]*a*D*J0;\n                double Kud = (1+eta)/2*M[i]*(1-eta)/2*M[j]*a*D*J0;\n                double Kdu = (1-eta)/2*M[i]*(1+eta)/2*M[j]*a*D*J0;\n                double Kdd = (1-eta)/2*M[i]*(1-eta)/2*M[j]*a*D*J0;\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-d component\n                ke[6*i  ][6*j+3] += Kud;\n                ke[6*i+1][6*j+4] += Kud;\n                ke[6*i+2][6*j+5] += Kud;\n                \n                // the d-u component\n                ke[6*i+3][6*j  ] += Kdu;\n                ke[6*i+4][6*j+1] += Kdu;\n                ke[6*i+5][6*j+2] += Kdu;\n                \n                // the d-d component\n                ke[6*i+3][6*j+3] += Kdd;\n                ke[6*i+4][6*j+4] += Kdd;\n                ke[6*i+5][6*j+5] += Kdd;\n            }\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Calculates body forces for shells\n\nvoid FEElasticEASShellDomain::ElementBodyForce(FEModel& fem, FEShellElementNew& el, vector<double>& fe)\n{\n    int NF = fem.ModelLoads();\n    for (int nf = 0; nf < NF; ++nf)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(nf));\n        if (pbf)\n        {\n            // integration weights\n            double* gw = el.GaussWeights();\n            double eta;\n            double *M, detJt;\n            \n            // loop over integration points\n            int nint = el.GaussPoints();\n            int neln = el.Nodes();\n            \n            for (int n=0; n<nint; ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n                \n                // calculate density in current configuration\n\t\t\t\tdouble dens0 = m_pMat->Density(mp);\n                double dens = dens0/pt.m_J;\n                \n                // calculate the jacobian\n                detJt = detJ(el, n)*gw[n];\n                \n                M  = el.H(n);\n                eta = el.gt(n);\n                \n                // get the force\n                vec3d f = pbf->force(mp);\n                \n                for (int i=0; i<neln; ++i)\n                {\n                    vec3d fu = f*(dens*M[i]*(1+eta)/2);\n                    vec3d fd = f*(dens*M[i]*(1-eta)/2);\n                    \n                    fe[6*i  ] -= fu.x*detJt;\n                    fe[6*i+1] -= fu.y*detJt;\n                    fe[6*i+2] -= fu.z*detJt;\n                    \n                    fe[6*i+3] -= fd.x*detJt;\n                    fe[6*i+4] -= fd.y*detJt;\n                    fe[6*i+5] -= fd.z*detJt;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Update alpha in EAS method\nvoid FEElasticEASShellDomain::UpdateEAS(vector<double>& ui)\n{\n    FEMesh& mesh = *GetMesh();\n    \n    for (int i=0; i<(int) m_Elem.size(); ++i)\n    {\n        // get the solid element\n\t\tFEShellElementNew& el = m_Elem[i];\n        \n        // number of nodes\n        int neln = el.Nodes();\n        \n        // allocate arrays\n        matrix dalpha(m_nEAS,1);\n        matrix Du(3,1), Dw(3,1);\n        \n        // nodal coordinates and EAS vector alpha update\n        dalpha = el.m_fa;\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& nj = mesh.Node(el.m_node[j]);\n            Du(0,0) = (nj.m_ID[m_dofU[0]] >=0) ? ui[nj.m_ID[m_dofU[0]]] : 0;\n            Du(1,0) = (nj.m_ID[m_dofU[1]] >=0) ? ui[nj.m_ID[m_dofU[1]]] : 0;\n            Du(2,0) = (nj.m_ID[m_dofU[2]] >=0) ? ui[nj.m_ID[m_dofU[2]]] : 0;\n            Dw(0,0) = (nj.m_ID[m_dofSU[0]] >=0) ? ui[nj.m_ID[m_dofSU[0]]] : 0;\n            Dw(1,0) = (nj.m_ID[m_dofSU[1]] >=0) ? ui[nj.m_ID[m_dofSU[1]]] : 0;\n            Dw(2,0) = (nj.m_ID[m_dofSU[2]] >=0) ? ui[nj.m_ID[m_dofSU[2]]] : 0;\n            dalpha += el.m_Kua[j].transpose()*Du + el.m_Kwa[j].transpose()*Dw;\n        }\n        dalpha = el.m_Kaai*dalpha;\n        el.m_alpha = el.m_alphat + el.m_alphai - dalpha;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Update alpha in EAS method\nvoid FEElasticEASShellDomain::UpdateIncrementsEAS(vector<double>& ui, const bool binc)\n{\n    FEMesh& mesh = *GetMesh();\n    \n    for (int i=0; i<(int) m_Elem.size(); ++i)\n    {\n        // get the solid element\n\t\tFEShellElementNew& el = m_Elem[i];\n        \n        if (binc) {\n            // number of nodes\n            int neln = el.Nodes();\n            \n            // allocate arrays\n            matrix dalpha(m_nEAS,1);\n            matrix Du(3,1), Dw(3,1);\n            \n            // nodal coordinates and EAS vector alpha update\n            dalpha = el.m_fa;\n            for (int j=0; j<neln; ++j)\n            {\n                FENode& nj = mesh.Node(el.m_node[j]);\n                Du(0,0) = (nj.m_ID[m_dofU[0]] >=0) ? ui[nj.m_ID[m_dofU[0]]] : 0;\n                Du(1,0) = (nj.m_ID[m_dofU[1]] >=0) ? ui[nj.m_ID[m_dofU[1]]] : 0;\n                Du(2,0) = (nj.m_ID[m_dofU[2]] >=0) ? ui[nj.m_ID[m_dofU[2]]] : 0;\n                Dw(0,0) = (nj.m_ID[m_dofSU[0]] >=0) ? ui[nj.m_ID[m_dofSU[0]]] : 0;\n                Dw(1,0) = (nj.m_ID[m_dofSU[1]] >=0) ? ui[nj.m_ID[m_dofSU[1]]] : 0;\n                Dw(2,0) = (nj.m_ID[m_dofSU[2]] >=0) ? ui[nj.m_ID[m_dofSU[2]]] : 0;\n                dalpha += el.m_Kua[j].transpose()*Du + el.m_Kwa[j].transpose()*Dw;\n            }\n            dalpha = el.m_Kaai*dalpha;\n            el.m_alphai -= dalpha;\n        }\n        else el.m_alphat += el.m_alphai;\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::Update(const FETimeInfo& tp)\n{\n    FESSIShellDomain::Update(tp);\n    \n    bool berr = false;\n    int NE = Elements();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            FEShellElement& el = Element(i);\n            if (el.isActive())\n            {\n                UpdateElementStress(i, tp);\n            }\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticEASShellDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double dt = tp.timeIncrement;\n    \n    // get the solid element\n    FEShellElementNew& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    const int NELN = FEElement::MAX_NODES;\n    vec3d r0[NELN], s0[NELN], r[NELN], s[NELN];\n    vec3d v[NELN], w[NELN];\n    vec3d a[NELN], b[NELN];\n    // nodal coordinates\n    GetCurrentNodalCoordinates(el, r, tp.alphaf, false);\n    GetCurrentNodalCoordinates(el, s, tp.alphaf, true);\n    GetReferenceNodalCoordinates(el, r0, false);\n    GetReferenceNodalCoordinates(el, s0, true);\n    \n    // update dynamic quantities\n    if (m_update_dynamic)\n    {\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m_pMesh->Node(el.m_node[j]);\n            v[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*tp.alphaf + node.m_vp*(1-tp.alphaf);\n            w[j] = node.get_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2])*tp.alphaf + node.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2])*(1-tp.alphaf);\n            a[j] = node.m_at*tp.alpham + node.m_ap*(1-tp.alpham);\n            b[j] = node.get_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2])*tp.alpham + node.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2])*(1-tp.alpham);\n        }\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //         since the material point coordinates are used by most materials.\n        mp.m_r0 = evaluate(el, r0, s0, n);\n        mp.m_rt = evaluate(el, r, s, n);\n        \n        // get the deformation gradient and determinant at intermediate time\n        mat3d Ft, Fp;\n        double Jt = defgrad(el, Ft, n);\n        double Jp = defgradp(el, Fp, n);\n        if (tp.alphaf == 1.0)\n        {\n            pt.m_F = Ft;\n            pt.m_J = Jt;\n        }\n        else\n        {\n            pt.m_F = Ft * tp.alphaf + Fp * (1 - tp.alphaf);\n            pt.m_J = pt.m_F.det();\n        }\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (Ft - Fp)*Fi/dt;\n        if (m_update_dynamic)\n        {\n            pt.m_v = evaluate(el, v, w, n);\n            pt.m_a = evaluate(el, a, b, n);\n        }\n        \n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, tp);\n        \n        // calculate the stress at this material point\n        mat3ds S = m_secant_stress ? m_pMat->SecantStress(mp, true) : m_pMat->PK2Stress(mp, el.m_E[n]);\n        pt.m_s = (pt.m_F*S*pt.m_F.transpose()).sym()/pt.m_J;\n\n        // adjust stress for strain energy conservation\n        if (tp.alphaf == 0.5)\n        {\n            // evaluate strain energy at current time\n            mat3d Ftmp = pt.m_F;\n            double Jtmp = pt.m_J;\n            pt.m_F = Ft;\n            pt.m_J = Jt;\n            FEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(m_pMat);\n            pt.m_Wt = pme->StrainEnergyDensity(mp);\n            pt.m_F = Ftmp;\n            pt.m_J = Jtmp;\n            \n            mat3ds D = pt.m_L.sym();\n            double D2 = D.dotdot(D);\n            if (D2 > 0)\n                pt.m_s += D*(((pt.m_Wt-pt.m_Wp)/(dt*pt.m_J) - pt.m_s.dotdot(D))/D2);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element. That is, copy element data in traits structure\n//! Note that for the shell elements the lm order is different compared\n//! to the solid element ordering. This is because for shell elements the\n//! nodes have six degrees of freedom each, where for solids they only\n//! have 3 dofs.\nvoid FEElasticEASShellDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    lm.resize(N*9);\n    for (int i=0; i<N; ++i)\n    {\n        FENode& node = m_pMesh->Node(el.m_node[i]);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[6*i  ] = id[m_dofU[0]];\n        lm[6*i+1] = id[m_dofU[1]];\n        lm[6*i+2] = id[m_dofU[2]];\n        \n        // next the shell displacement dofs\n        lm[6*i+3] = id[m_dofSU[0]];\n        lm[6*i+4] = id[m_dofSU[1]];\n        lm[6*i+5] = id[m_dofSU[2]];\n        \n        // rigid rotational dofs\n        lm[6*N + 3*i  ] = id[m_dofR[0]];\n        lm[6*N + 3*i+1] = id[m_dofR[1]];\n        lm[6*N + 3*i+2] = id[m_dofR[2]];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Generate the G matrix for EAS method\nvoid FEElasticEASShellDomain::GenerateGMatrix(FEShellElementNew& el, const int n, const double Jeta, matrix& G)\n{\n    vec3d Gcnt[3], Gcov[3];\n    CoBaseVectors0(el, n, Gcov);\n    ContraBaseVectors0(el, 0, 0, 0, Gcnt);\n    double J0 = detJ0(el, 0, 0, 0);\n    double Jr = J0/Jeta;\n    \n    double G00 = Gcov[0]*Gcnt[0];\n    double G01 = Gcov[0]*Gcnt[1];\n    double G02 = Gcov[0]*Gcnt[2];\n    double G10 = Gcov[1]*Gcnt[0];\n    double G11 = Gcov[1]*Gcnt[1];\n    double G12 = Gcov[1]*Gcnt[2];\n    double G20 = Gcov[2]*Gcnt[0];\n    double G21 = Gcov[2]*Gcnt[1];\n    double G22 = Gcov[2]*Gcnt[2];\n    \n    matrix T0(6,6);\n    T0(0,0) = G00*G00; T0(0,1) = G01*G01; T0(0,2) = G02*G02; T0(0,3) = G00*G01; T0(0,4) = G01*G02; T0(0,5) = G00*G02;\n    T0(1,0) = G10*G10; T0(1,1) = G11*G11; T0(1,2) = G12*G12; T0(1,3) = G10*G11; T0(1,4) = G11*G12; T0(1,5) = G10*G12;\n    T0(2,0) = G20*G20; T0(2,1) = G21*G21; T0(2,2) = G22*G22; T0(2,3) = G20*G21; T0(2,4) = G21*G22; T0(2,5) = G20*G22;\n    T0(3,0) = 2*G00*G10; T0(3,1) = 2*G01*G11; T0(3,2) = 2*G02*G12; T0(3,3) = G00*G11+G01*G10; T0(3,4) = G01*G12+G02*G11; T0(3,5) = G00*G12+G02*G10;\n    T0(4,0) = 2*G10*G20; T0(4,1) = 2*G11*G21; T0(4,2) = 2*G12*G22; T0(4,3) = G10*G21+G11*G20; T0(4,4) = G11*G22+G12*G21; T0(4,5) = G10*G22+G12*G20;\n    T0(5,0) = 2*G00*G20; T0(5,1) = 2*G01*G21; T0(5,2) = 2*G02*G22; T0(5,3) = G00*G21+G01*G20; T0(5,4) = G01*G22+G02*G21; T0(5,5) = G00*G22+G02*G20;\n    \n    G.resize(6, m_nEAS);\n    double r = el.gr(n);\n    double s = el.gs(n);\n    double t = el.gt(n);\n    \n    G(0,0) = r*T0(0,0)*Jr;\n    G(1,0) = r*T0(1,0)*Jr;\n    G(2,0) = r*T0(2,0)*Jr;\n    G(3,0) = r*T0(3,0)*Jr;\n    G(4,0) = r*T0(4,0)*Jr;\n    G(5,0) = r*T0(5,0)*Jr;\n    \n    G(0,1) = s*T0(0,1)*Jr;\n    G(1,1) = s*T0(1,1)*Jr;\n    G(2,1) = s*T0(2,1)*Jr;\n    G(3,1) = s*T0(3,1)*Jr;\n    G(4,1) = s*T0(4,1)*Jr;\n    G(5,1) = s*T0(5,1)*Jr;\n    \n    G(0,2) = t*T0(0,2)*Jr;\n    G(1,2) = t*T0(1,2)*Jr;\n    G(2,2) = t*T0(2,2)*Jr;\n    G(3,2) = t*T0(3,2)*Jr;\n    G(4,2) = t*T0(4,2)*Jr;\n    G(5,2) = t*T0(5,2)*Jr;\n    \n    G(0,3) = r*t*T0(0,2)*Jr;\n    G(1,3) = r*t*T0(1,2)*Jr;\n    G(2,3) = r*t*T0(2,2)*Jr;\n    G(3,3) = r*t*T0(3,2)*Jr;\n    G(4,3) = r*t*T0(4,2)*Jr;\n    G(5,3) = r*t*T0(5,2)*Jr;\n    \n    G(0,4) = s*t*T0(0,2)*Jr;\n    G(1,4) = s*t*T0(1,2)*Jr;\n    G(2,4) = s*t*T0(2,2)*Jr;\n    G(3,4) = s*t*T0(3,2)*Jr;\n    G(4,4) = s*t*T0(4,2)*Jr;\n    G(5,4) = s*t*T0(5,2)*Jr;\n    \n    G(0,5) = r*T0(0,3)*Jr;\n    G(1,5) = r*T0(1,3)*Jr;\n    G(2,5) = r*T0(2,3)*Jr;\n    G(3,5) = r*T0(3,3)*Jr;\n    G(4,5) = r*T0(4,3)*Jr;\n    G(5,5) = r*T0(5,3)*Jr;\n    \n    G(0,6) = s*T0(0,3)*Jr;\n    G(1,6) = s*T0(1,3)*Jr;\n    G(2,6) = s*T0(2,3)*Jr;\n    G(3,6) = s*T0(3,3)*Jr;\n    G(4,6) = s*T0(4,3)*Jr;\n    G(5,6) = s*T0(5,3)*Jr;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate contravariant components of mat3ds tensor\nvoid FEElasticEASShellDomain::mat3dsCntMat61(const mat3ds s, const vec3d* Gcnt, matrix& S)\n{\n    S.resize(6, 1);\n    S(0,0) = Gcnt[0]*(s*Gcnt[0]);\n    S(1,0) = Gcnt[1]*(s*Gcnt[1]);\n    S(2,0) = Gcnt[2]*(s*Gcnt[2]);\n    S(3,0) = Gcnt[0]*(s*Gcnt[1]);\n    S(4,0) = Gcnt[1]*(s*Gcnt[2]);\n    S(5,0) = Gcnt[0]*(s*Gcnt[2]);\n}\n\n\n//-----------------------------------------------------------------------------\n//! Evaluate contravariant components of tens4ds tensor\n//! Cijkl = Gj.(Gi.c.Gl).Gk\nvoid FEElasticEASShellDomain::tens4dsCntMat66(const tens4ds c, const vec3d* Gcnt, matrix& C)\n{\n    C.resize(6, 6);\n    C(0,0) =          Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[0])*Gcnt[0]);  // i=0, j=0, k=0, l=0\n    C(0,1) = C(1,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[1]);  // i=0, j=0, k=1, l=1\n    C(0,2) = C(2,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[2]);  // i=0, j=0, k=2, l=2\n    C(0,3) = C(3,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=0, k=0, l=1\n    C(0,4) = C(4,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=0, k=1, l=2\n    C(0,5) = C(5,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=0, k=0, l=2\n    C(1,1) =          Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[1]);  // i=1, j=1, k=1, l=1\n    C(1,2) = C(2,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[2]);  // i=1, j=1, k=2, l=2\n    C(1,3) = C(3,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[0]);  // i=1, j=1, k=0, l=1\n    C(1,4) = C(4,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=1, k=1, l=2\n    C(1,5) = C(5,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=1, k=0, l=2\n    C(2,2) =          Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[2]);  // i=2, j=2, k=2, l=2\n    C(2,3) = C(3,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[1])*Gcnt[0]);  // i=2, j=2, k=0, l=1\n    C(2,4) = C(4,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[1]);  // i=2, j=2, k=1, l=2\n    C(2,5) = C(5,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[0]);  // i=2, j=2, k=0, l=2\n    C(3,3) =          Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=1, k=0, l=1\n    C(3,4) = C(4,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=1, k=1, l=2\n    C(3,5) = C(5,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=1, k=0, l=2\n    C(4,4) =          Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=2, k=1, l=2\n    C(4,5) = C(5,4) = Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=2, k=0, l=2\n    C(5,5) =          Gcnt[2]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=2, k=0, l=2\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate contravariant components of tens4dmm tensor\n//! Cijkl = Gj.(Gi.c.Gl).Gk\nvoid FEElasticEASShellDomain::tens4dmmCntMat66(const tens4dmm c, const vec3d* Gcnt, matrix& C)\n{\n    C.resize(6, 6);\n    C(0,0) =          Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[0])*Gcnt[0]);  // i=0, j=0, k=0, l=0\n    C(0,1) = C(1,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[1]);  // i=0, j=0, k=1, l=1\n    C(0,2) = C(2,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[2]);  // i=0, j=0, k=2, l=2\n    C(0,3) = C(3,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=0, k=0, l=1\n    C(0,4) = C(4,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=0, k=1, l=2\n    C(0,5) = C(5,0) = Gcnt[0]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=0, k=0, l=2\n    C(1,1) =          Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[1]);  // i=1, j=1, k=1, l=1\n    C(1,2) = C(2,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[2]);  // i=1, j=1, k=2, l=2\n    C(1,3) = C(3,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[1])*Gcnt[0]);  // i=1, j=1, k=0, l=1\n    C(1,4) = C(4,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=1, k=1, l=2\n    C(1,5) = C(5,1) = Gcnt[1]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=1, k=0, l=2\n    C(2,2) =          Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[2]);  // i=2, j=2, k=2, l=2\n    C(2,3) = C(3,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[1])*Gcnt[0]);  // i=2, j=2, k=0, l=1\n    C(2,4) = C(4,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[1]);  // i=2, j=2, k=1, l=2\n    C(2,5) = C(5,2) = Gcnt[2]*(vdotTdotv(Gcnt[2], c, Gcnt[2])*Gcnt[0]);  // i=2, j=2, k=0, l=2\n    C(3,3) =          Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[1])*Gcnt[0]);  // i=0, j=1, k=0, l=1\n    C(3,4) = C(4,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[1]);  // i=0, j=1, k=1, l=2\n    C(3,5) = C(5,3) = Gcnt[1]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=1, k=0, l=2\n    C(4,4) =          Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[1]);  // i=1, j=2, k=1, l=2\n    C(4,5) = C(5,4) = Gcnt[2]*(vdotTdotv(Gcnt[1], c, Gcnt[2])*Gcnt[0]);  // i=1, j=2, k=0, l=2\n    C(5,5) =          Gcnt[2]*(vdotTdotv(Gcnt[0], c, Gcnt[2])*Gcnt[0]);  // i=0, j=2, k=0, l=2\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the matrices and vectors relevant to the EAS method\nvoid FEElasticEASShellDomain::EvaluateEAS(FEShellElementNew& el, vector<double>& EE,\n                                       vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW,\n                                       vector<mat3ds>& S, vector<tens4dmm>& c)\n{\n    int i, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    vector<matrix> hu(neln, matrix(3,6));\n    vector<matrix> hw(neln, matrix(3,6));\n    vector<vec3d> Nu(neln);\n    vector<vec3d> Nw(neln);\n    matrix NS(neln,16);\n    matrix NN(neln,8);\n    \n    double*    gw = el.GaussWeights();\n    double eta;\n    vec3d Gcnt[3];\n    \n    // Evaluate fa, Kua, Kwa, and Kaa by integrating over the element\n    el.m_fa.zero();\n    el.m_Kaai.zero();\n    for (i=0; i< neln; ++i) {\n        el.m_Kua[i].zero();\n        el.m_Kwa[i].zero();\n    }\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        \n        ContraBaseVectors0(el, n, Gcnt);\n        mat3ds Ec;\n        // Evaluate compatible strain tensor\n        EvaluateEh(el, n, Gcnt, Ec, hu, hw, Nu, Nw);\n        // Evaluate assumed natural strain (ANS) and substitute it into Ec\n        EvaluateANS(el, n, Gcnt, Ec, hu, hw, EE, HU, HW);\n        \n        // calculate the jacobian\n        detJt = detJ0(el, n);\n        \n        // generate G matrix for EAS method\n        matrix G;\n        GenerateGMatrix(el, n, detJt, G);\n        \n        detJt *= gw[n];\n        \n        // Evaluate enhancing strain ES (covariant components)\n        matrix ES(6,1);\n        ES = G*el.m_alpha;\n        // Evaluate the tensor form of ES\n        mat3ds Es = ((Gcnt[0] & Gcnt[0])*ES(0,0) + (Gcnt[1] & Gcnt[1])*ES(1,0) + (Gcnt[2] & Gcnt[2])*ES(2,0) +\n                     ((Gcnt[0] & Gcnt[1]) + (Gcnt[1] & Gcnt[0]))*(ES(3,0)/2) +\n                     ((Gcnt[1] & Gcnt[2]) + (Gcnt[2] & Gcnt[1]))*(ES(4,0)/2) +\n                     ((Gcnt[2] & Gcnt[0]) + (Gcnt[0] & Gcnt[2]))*(ES(5,0)/2)).sym();\n        // Evaluate enhanced strain\n        el.m_E[n] = Ec + Es;\n        \n        // get the stress tensor for this integration point and evaluate its contravariant components\n        S[n] = m_pMat->PK2Stress(mp, el.m_E[n]);\n        matrix SM;\n        mat3dsCntMat61(S[n], Gcnt, SM);\n        \n        // get the material tangent\n        c[n] = m_pMat->MaterialTangent(mp, el.m_E[n]);\n        // get contravariant components of material tangent\n        matrix CC;\n        tens4dmmCntMat66(c[n], Gcnt, CC);\n//        tens4dsCntMat66(c[n], Gcnt, CC);\n        \n        // Evaluate fa\n        matrix tmp(m_nEAS,1);\n        tmp = G.transpose()*SM;\n        tmp *= detJt;\n        el.m_fa += tmp;\n        \n        // Evaluate Kaa\n        matrix Tmpa(m_nEAS,m_nEAS);\n        Tmpa = G.transpose()*CC*G;\n        Tmpa *= detJt;\n        el.m_Kaai += Tmpa;\n        \n        eta = el.gt(n);\n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        // Evaluate Kua and Kwa\n        matrix Tmp(3,m_nEAS);\n        for (i=0; i<neln; ++i)\n        {\n            Tmp = hu[i]*CC*G;\n            Tmp *= detJt;\n            el.m_Kua[i] += Tmp;\n            Tmp = hw[i]*CC*G;\n            Tmp *= detJt;\n            el.m_Kwa[i] += Tmp;\n        }\n    }\n    // invert Kaa\n    el.m_Kaai = el.m_Kaai.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate collocation strains for assumed natural strain (ANS) method\nvoid FEElasticEASShellDomain::CollocationStrainsANS(FEShellElementNew& el, vector<double>& E,\n                                                 vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW,\n                                                 matrix& NS, matrix& NN)\n{\n    FETimeInfo& tp = GetFEModel()->GetTime();\n    \n    // ANS method for 4-node quadrilaterials\n    if (el.Nodes() == 4) {\n        vec3d gcov[3], Gcov[3];\n        \n        double Mr[FEElement::MAX_NODES], Ms[FEElement::MAX_NODES], M[FEElement::MAX_NODES];\n        double r, s, t;\n        int neln = el.Nodes();\n        double Nur, Nus, Nut;\n        double Nwr, Nws, Nwt;\n        \n        // Shear strains E13, E23\n        // point A\n        r = 0; s = -1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E13A = (gcov[0]*gcov[2] - Gcov[0]*Gcov[2])/2;\n        vector<vec3d> hu13A(neln);\n        vector<vec3d> hw13A(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,0) = Nur = (1+t)/2*Mr[i];\n            NS(i,1) = Nut = M[i]/2;\n            NS(i,2) = Nwr = (1-t)/2*Mr[i];\n            NS(i,3) = Nwt = -M[i]/2;\n            hu13A[i] = gcov[0]*Nut + gcov[2]*Nur;\n            hw13A[i] = gcov[0]*Nwt + gcov[2]*Nwr;\n        }\n        \n        // point B\n        r = 1; s = 0; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E23B = (gcov[1]*gcov[2] - Gcov[1]*Gcov[2])/2;\n        vector<vec3d> hu23B(neln);\n        vector<vec3d> hw23B(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,4) = Nus = (1+t)/2*Ms[i];\n            NS(i,5) = Nut = M[i]/2;\n            NS(i,6) = Nws = (1-t)/2*Ms[i];\n            NS(i,7) = Nwt = -M[i]/2;\n            hu23B[i] = gcov[2]*Nus + gcov[1]*Nut;\n            hw23B[i] = gcov[2]*Nws + gcov[1]*Nwt;\n        }\n        \n        // point C\n        r = 0; s = 1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E13C = (gcov[0]*gcov[2] - Gcov[0]*Gcov[2])/2;\n        vector<vec3d> hu13C(neln);\n        vector<vec3d> hw13C(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,8) = Nur = (1+t)/2*Mr[i];\n            NS(i,9) = Nut = M[i]/2;\n            NS(i,10) = Nwr = (1-t)/2*Mr[i];\n            NS(i,11) = Nwt = -M[i]/2;\n            hu13C[i] = gcov[0]*Nut + gcov[2]*Nur;\n            hw13C[i] = gcov[0]*Nwt + gcov[2]*Nwr;\n        }\n        \n        // point D\n        r = -1; s = 0; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E23D = (gcov[1]*gcov[2] - Gcov[1]*Gcov[2])/2;\n        vector<vec3d> hu23D(neln);\n        vector<vec3d> hw23D(neln);\n        el.shape_fnc(M, r, s);\n        el.shape_deriv(Mr, Ms, r, s);\n        for (int i=0; i<neln; ++i) {\n            NS(i,12) = Nus = (1+t)/2*Ms[i];\n            NS(i,13) = Nut = M[i]/2;\n            NS(i,14) = Nws = (1-t)/2*Ms[i];\n            NS(i,15) = Nwt = -M[i]/2;\n            hu23D[i] = gcov[2]*Nus + gcov[1]*Nut;\n            hw23D[i] = gcov[2]*Nws + gcov[1]*Nwt;\n        }\n        \n        // normal strain E33\n        // point E\n        r = -1; s = -1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33E = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33E(neln);\n        vector<vec3d> hw33E(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,0) = Nut = M[i]/2;\n            NN(i,1) = Nwt = -M[i]/2;\n            hu33E[i] = gcov[2]*Nut;\n            hw33E[i] = gcov[2]*Nwt;\n        }\n        \n        // point F\n        r = 1; s = -1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33F = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33F(neln);\n        vector<vec3d> hw33F(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,2) = Nut = M[i]/2;\n            NN(i,3) = Nwt = -M[i]/2;\n            hu33F[i] = gcov[2]*Nut;\n            hw33F[i] = gcov[2]*Nwt;\n        }\n        \n        // point G\n        r = 1; s = 1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33G = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33G(neln);\n        vector<vec3d> hw33G(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,4) = Nut = M[i]/2;\n            NN(i,5) = Nwt = -M[i]/2;\n            hu33G[i] = gcov[2]*Nut;\n            hw33G[i] = gcov[2]*Nwt;\n        }\n        \n        // point H\n        r = -1; s = 1; t = 0;\n        CoBaseVectors(el, r, s, t, gcov, tp.alphaf);\n        CoBaseVectors0(el, r, s, t, Gcov);\n        double E33H = (gcov[2]*gcov[2] - Gcov[2]*Gcov[2])/2;\n        vector<vec3d> hu33H(neln);\n        vector<vec3d> hw33H(neln);\n        el.shape_fnc(M, r, s);\n        for (int i=0; i<neln; ++i) {\n            NN(i,6) = Nut = M[i]/2;\n            NN(i,7) = Nwt = -M[i]/2;\n            hu33H[i] = gcov[2]*Nut;\n            hw33H[i] = gcov[2]*Nwt;\n        }\n        // return the results in aggregated format\n        E.resize(8);\n        E[0] = E13A; E[1] = E23B; E[2] = E13C; E[3] = E23D;\n        E[4] = E33E; E[5] = E33F; E[6] = E33G; E[7] = E33H;\n        HU.resize(8,vector<vec3d>(neln)); HW.resize(8,vector<vec3d>(neln));\n        for (int i=0; i<neln; ++i) {\n            HU[0] = hu13A; HU[1] = hu23B; HU[2] = hu13C; HU[3] = hu23D;\n            HU[4] = hu33E; HU[5] = hu33F; HU[6] = hu33G; HU[7] = hu33H;\n            HW[0] = hw13A; HW[1] = hw23B; HW[2] = hw13C; HW[3] = hw23D;\n            HW[4] = hw33E; HW[5] = hw33F; HW[6] = hw33G; HW[7] = hw33H;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate assumed natural strain (ANS)\nvoid FEElasticEASShellDomain::EvaluateANS(FEShellElementNew& el, const int n, const vec3d* Gcnt,\n                                       mat3ds& Ec, vector<matrix>& hu, vector<matrix>& hw,\n                                       vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW)\n{\n    // ANS method for 4-node quadrilaterials\n    if (el.Nodes() == 4) {\n        vec3d Gcov[3];\n        int neln = el.Nodes();\n        \n        double E13A = E[0]; double E23B = E[1];\n        double E13C = E[2]; double E23D = E[3];\n        double E33E = E[4]; double E33F = E[5];\n        double E33G = E[6]; double E33H = E[7];\n        vector<vec3d> hu13A(HU[0]); vector<vec3d> hu23B(HU[1]);\n        vector<vec3d> hu13C(HU[2]); vector<vec3d> hu23D(HU[3]);\n        vector<vec3d> hu33E(HU[4]); vector<vec3d> hu33F(HU[5]);\n        vector<vec3d> hu33G(HU[6]); vector<vec3d> hu33H(HU[7]);\n        vector<vec3d> hw13A(HW[0]); vector<vec3d> hw23B(HW[1]);\n        vector<vec3d> hw13C(HW[2]); vector<vec3d> hw23D(HW[3]);\n        vector<vec3d> hw33E(HW[4]); vector<vec3d> hw33F(HW[5]);\n        vector<vec3d> hw33G(HW[6]); vector<vec3d> hw33H(HW[7]);\n        \n        // Evaluate ANS strains\n        double r = el.gr(n);\n        double s = el.gs(n);\n        double E13ANS = ((1-s)*E13A + (1+s)*E13C)/2;\n        double E23ANS = ((1-r)*E23D + (1+r)*E23B)/2;\n        double E33ANS = ((1-r)*(1-s)*E33E + (1+r)*(1-s)*E33F +\n                         (1+r)*(1+s)*E33G + (1-r)*(1+s)*E33H)/4;\n        vector<vec3d> hu13ANS(neln), hu23ANS(neln), hu33ANS(neln);\n        vector<vec3d> hw13ANS(neln), hw23ANS(neln), hw33ANS(neln);\n        for (int i=0; i<neln; ++i) {\n            hu13ANS[i] = (hu13A[i]*(1-s) + hu13C[i]*(1+s))/2;\n            hw13ANS[i] = (hw13A[i]*(1-s) + hw13C[i]*(1+s))/2;\n            hu23ANS[i] = (hu23D[i]*(1-r) + hu23B[i]*(1+r))/2;\n            hw23ANS[i] = (hw23D[i]*(1-r) + hw23B[i]*(1+r))/2;\n            hu33ANS[i] = (hu33E[i]*(1-r)*(1-s) + hu33F[i]*(1+r)*(1-s) +\n                          hu33G[i]*(1+r)*(1+s) + hu33H[i]*(1-r)*(1+s))/4;\n            hw33ANS[i] = (hw33E[i]*(1-r)*(1-s) + hw33F[i]*(1+r)*(1-s) +\n                          hw33G[i]*(1+r)*(1+s) + hw33H[i]*(1-r)*(1+s))/4;\n        }\n        \n        // Substitute these strain components into Ec\n        CoBaseVectors0(el, n, Gcov);\n        double E11c = Gcov[0]*(Ec*Gcov[0]);\n        double E22c = Gcov[1]*(Ec*Gcov[1]);\n        double E12c = Gcov[0]*(Ec*Gcov[1]);\n        Ec = ((Gcnt[0] & Gcnt[0])*E11c + (Gcnt[1] & Gcnt[1])*E22c + (Gcnt[2] & Gcnt[2])*E33ANS +\n              ((Gcnt[0] & Gcnt[1]) + (Gcnt[1] & Gcnt[0]))*E12c +\n              ((Gcnt[1] & Gcnt[2]) + (Gcnt[2] & Gcnt[1]))*E23ANS +\n              ((Gcnt[2] & Gcnt[0]) + (Gcnt[0] & Gcnt[2]))*E13ANS).sym();\n        for (int i=0; i<neln; ++i) {\n            hu[i](0,5) = hu13ANS[i].x; hu[i](1,5) = hu13ANS[i].y; hu[i](2,5) = hu13ANS[i].z;\n            hw[i](0,5) = hw13ANS[i].x; hw[i](1,5) = hw13ANS[i].y; hw[i](2,5) = hw13ANS[i].z;\n            hu[i](0,4) = hu23ANS[i].x; hu[i](1,4) = hu23ANS[i].y; hu[i](2,4) = hu23ANS[i].z;\n            hw[i](0,4) = hw23ANS[i].x; hw[i](1,4) = hw23ANS[i].y; hw[i](2,4) = hw23ANS[i].z;\n            hu[i](0,2) = hu33ANS[i].x; hu[i](1,2) = hu33ANS[i].y; hu[i](2,2) = hu33ANS[i].z;\n            hw[i](0,2) = hw33ANS[i].x; hw[i](1,2) = hw33ANS[i].y; hw[i](2,2) = hw33ANS[i].z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate strain E and matrix hu and hw\nvoid FEElasticEASShellDomain::EvaluateEh(FEShellElementNew& el, const int n, const vec3d* Gcnt, mat3ds& E,\n                                      vector<matrix>& hu, vector<matrix>& hw, vector<vec3d>& Nu, vector<vec3d>& Nw)\n{\n    FETimeInfo& tp = GetFEModel()->GetTime();\n    \n    const double* Mr, *Ms, *M;\n    vec3d gcov[3];\n    int neln = el.Nodes();\n    \n    FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n    FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n    \n    E = pt.Strain();\n    \n    CoBaseVectors(el, n, gcov, tp.alphaf);\n    double eta = el.gt(n);\n    \n    Mr = el.Hr(n);\n    Ms = el.Hs(n);\n    M  = el.H(n);\n    \n    for (int i=0; i<neln; ++i)\n    {\n        double Nur = Nu[i].x = (1+eta)/2*Mr[i];\n        double Nus = Nu[i].y = (1+eta)/2*Ms[i];\n        double Nut = Nu[i].z = M[i]/2;\n        double Nwr = Nw[i].x = (1-eta)/2*Mr[i];\n        double Nws = Nw[i].y = (1-eta)/2*Ms[i];\n        double Nwt = Nw[i].z = -M[i]/2;\n        hu[i](0,0) = Nur*gcov[0].x;                 hu[i](1,0) = Nur*gcov[0].y;                 hu[i](2,0) = Nur*gcov[0].z;\n        hu[i](0,1) = Nus*gcov[1].x;                 hu[i](1,1) = Nus*gcov[1].y;                 hu[i](2,1) = Nus*gcov[1].z;\n        hu[i](0,2) = Nut*gcov[2].x;                 hu[i](1,2) = Nut*gcov[2].y;                 hu[i](2,2) = Nut*gcov[2].z;\n        hu[i](0,3) = Nur*gcov[1].x + Nus*gcov[0].x; hu[i](1,3) = Nur*gcov[1].y + Nus*gcov[0].y; hu[i](2,3) = Nur*gcov[1].z + Nus*gcov[0].z;\n        hu[i](0,4) = Nus*gcov[2].x + Nut*gcov[1].x; hu[i](1,4) = Nus*gcov[2].y + Nut*gcov[1].y; hu[i](2,4) = Nus*gcov[2].z + Nut*gcov[1].z;\n        hu[i](0,5) = Nut*gcov[0].x + Nur*gcov[2].x; hu[i](1,5) = Nut*gcov[0].y + Nur*gcov[2].y; hu[i](2,5) = Nut*gcov[0].z + Nur*gcov[2].z;\n        hw[i](0,0) = Nwr*gcov[0].x;                 hw[i](1,0) = Nwr*gcov[0].y;                 hw[i](2,0) = Nwr*gcov[0].z;\n        hw[i](0,1) = Nws*gcov[1].x;                 hw[i](1,1) = Nws*gcov[1].y;                 hw[i](2,1) = Nws*gcov[1].z;\n        hw[i](0,2) = Nwt*gcov[2].x;                 hw[i](1,2) = Nwt*gcov[2].y;                 hw[i](2,2) = Nwt*gcov[2].z;\n        hw[i](0,3) = Nwr*gcov[1].x + Nws*gcov[0].x; hw[i](1,3) = Nwr*gcov[1].y + Nws*gcov[0].y; hw[i](2,3) = Nwr*gcov[1].z + Nws*gcov[0].z;\n        hw[i](0,4) = Nws*gcov[2].x + Nwt*gcov[1].x; hw[i](1,4) = Nws*gcov[2].y + Nwt*gcov[1].y; hw[i](2,4) = Nws*gcov[2].z + Nwt*gcov[1].z;\n        hw[i](0,5) = Nwt*gcov[0].x + Nwr*gcov[2].x; hw[i](1,5) = Nwt*gcov[0].y + Nwr*gcov[2].y; hw[i](2,5) = Nwt*gcov[0].z + Nwr*gcov[2].z;\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticEASShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESSIShellDomain.h\"\n#include \"FEElasticDomain.h\"\n#include \"FESolidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain described by 3D shell elements\nclass FEBIOMECH_API FEElasticEASShellDomain : public FESSIShellDomain, public FEElasticDomain\n{\npublic:\n    FEElasticEASShellDomain(FEModel* pfem);\n    \n    //! \\todo do I really need this?\n    FEElasticEASShellDomain& operator = (FEElasticEASShellDomain& d);\n    \n    //! Initialize domain\n\tbool Init() override;\n    \n    //! Activate the domain\n    void Activate() override;\n    \n    //! Unpack shell element data\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! Set flag for update for dynamic quantities\n    void SetDynamicUpdateFlag(bool b);\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! get the material (overridden from FEDomain)\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n\n\t// get the total dof list\n\tconst FEDofList& GetDOFList() const override;\n    \npublic: // overrides from FEElasticDomain\n    \n    //! calculates the residual\n    //    void Residual(FESolver* psolver, vector<double>& R);\n    \n    //! internal stress forces\n    void InternalForces(FEGlobalVector& R) override;\n    \n    //! Calculates inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override;\n    \n    //! calculate body force\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n    \n    // update stresses\n    void Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    //! initialize elements for this domain\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    // inertial stiffness\n    void MassMatrix(FELinearSystem& LS, double scale) override;\n    \n    // body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    \n    // evaluate strain E and matrix hu and hw\n\tvoid EvaluateEh(FEShellElementNew& el, const int n, const vec3d* Gcnt, mat3ds& E, vector<matrix>& hu, vector<matrix>& hw, vector<vec3d>& Nu, vector<vec3d>& Nw);\n    \npublic:\n    \n    // --- S T I F F N E S S ---\n    \n    //! calculates the shell element stiffness matrix\n    void ElementStiffness(int iel, matrix& ke);\n    \n    // --- R E S I D U A L ---\n    \n    //! Calculates the internal stress vector for shell elements\n    void ElementInternalForce(FEShellElementNew& el, vector<double>& fe);\n    \n    //! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEModel& fem, FEShellElementNew& el, vector<double>& fe);\n    \n    //! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEBodyForce& BF, FEShellElementNew& el, vector<double>& fe);\n    \n    //! Calculates the inertial force for shell elements\n    void ElementInertialForce(FEShellElementNew& el, vector<double>& fe);\n\n    //! calculates the solid element mass matrix\n\tvoid ElementMassMatrix(FEShellElementNew& el, matrix& ke, double a);\n    \n    //! calculates the stiffness matrix due to body forces\n\tvoid ElementBodyForceStiffness(FEBodyForce& bf, FEShellElementNew& el, matrix& ke);\n    \npublic:\n    \n    // --- E A S  M E T H O D ---\n    \n    // Generate the G matrix for the EAS method\n\tvoid GenerateGMatrix(FEShellElementNew& el, const int n, const double Jeta, matrix& G);\n    \n    // Evaluate contravariant components of mat3ds tensor\n    void mat3dsCntMat61(const mat3ds s, const vec3d* Gcnt, matrix& S);\n    \n    // Evaluate contravariant components of tens4ds tensor\n    void tens4dsCntMat66(const tens4ds c, const vec3d* Gcnt, matrix& C);\n    void tens4dmmCntMat66(const tens4dmm c, const vec3d* Gcnt, matrix& C);\n\n    // Evaluate the matrices and vectors relevant to the EAS method\n\tvoid EvaluateEAS(FEShellElementNew& el, vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW, vector<mat3ds>& S, vector<tens4dmm>& c);\n    \n    // Evaluate the strain using the ANS method\n\tvoid CollocationStrainsANS(FEShellElementNew& el, vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW, matrix& NS, matrix& NN);\n    \n\tvoid EvaluateANS(FEShellElementNew& el, const int n, const vec3d* Gcnt, mat3ds& Ec, vector<matrix>& hu, vector<matrix>& hw, vector<double>& E, vector< vector<vec3d>>& HU, vector< vector<vec3d>>& HW);\n    \n    // Update alpha in EAS method\n    void UpdateEAS(vector<double>& ui) override;\n    void UpdateIncrementsEAS(vector<double>& ui, const bool binc) override;\n    \nprotected:\n    FESolidMaterial*    m_pMat;\n    int                 m_nEAS;\n    bool                m_update_dynamic;    //!< flag for updating quantities only used in dynamic analysis\n    \n    bool    m_secant_stress;    //!< use secant approximation to stress\n    bool    m_secant_tangent;   //!< flag for using secant tangent\n\n    FEDofList   m_dofV;\n    FEDofList   m_dofSV;\n\tFEDofList\tm_dofSA;\n\tFEDofList\tm_dofR;\n\tFEDofList\tm_dof;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticFiberMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterialPoint.h\"\n#include <FECore/FEConstValueVec3.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEElasticFiberMaterial, FEElasticMaterial)\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticFiberMaterial::CreateMaterialPointData()\n{\n    FEFiberMaterialPoint* mp = new FEFiberMaterialPoint(new FEElasticMaterialPoint);\n    return mp;\n}\n\n//-----------------------------------------------------------------------------\nFEElasticFiberMaterial::FEElasticFiberMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_fiber = nullptr;\n\n    m_Us = mat3dd(1);\n    m_bUs = false;\n}\n\n//-----------------------------------------------------------------------------\n// get the fiber vector (in global coordinates)\nvec3d FEElasticFiberMaterial::FiberVector(FEMaterialPoint& mp)\n{\n\t// get the material coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get local fiber direction\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q*fiber;\n    \n    // account for prior deformation in multigenerational formulation\n    vec3d a = FiberPreStretch(a0);\n    \n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEElasticFiberMaterial::FiberPreStretch(const vec3d a0)\n{\n    // account for prior deformation in multigenerational formulation\n    if (m_bUs) {\n        vec3d a = (m_Us*a0);\n        a.unit();\n        return a;\n    }\n    else\n        return a0;\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticFiberMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for single fiber response\n\nclass FEElasticFiberMaterial : public FEElasticMaterial\n{\npublic:\n    FEElasticFiberMaterial(FEModel* pfem);\n\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n\t// get the fiber vector (in global coordinates)\n\tvec3d FiberVector(FEMaterialPoint& mp);\n\n\t// calculate stress in fiber direction a0\n\tvirtual mat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) = 0;\n\n\t// Spatial tangent\n\tvirtual tens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) = 0;\n\n\t//! Strain energy density\n\tvirtual double FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) = 0;\n\n    // Set or clear pre-stretch, as needed in multigenerational materials (e.g., reactive viscoelasticity)\n    void SetPreStretch(const mat3ds Us) { m_Us = Us; m_bUs = true; }\n    void ResetPreStretch() { m_bUs = false; }\n    vec3d FiberPreStretch(const vec3d a0);\n    \nprivate:\n\t// These are made private since fiber materials should implement the functions above instead. \n\t// The functions can still be reached when a fiber material is used in an elastic mixture. \n\t// In those cases the fiber vector is taken from the first column of Q. \n\tmat3ds Stress(FEMaterialPoint& mp) final { return FiberStress(mp, FiberVector(mp)); }\n\ttens4ds Tangent(FEMaterialPoint& mp) final { return FiberTangent(mp, FiberVector(mp)); }\n\tdouble StrainEnergyDensity(FEMaterialPoint& mp) final { return FiberStrainEnergyDensity(mp, FiberVector(mp)); }\n\nprivate:\n    mat3ds  m_Us;   //!< pre-stretch tensor for fiber\n    bool    m_bUs;  //!< flag for pre-stretch\n    \npublic:\n\tFEVec3dValuator*\tm_fiber;\t//!< fiber orientation\n\n\tDECLARE_FECORE_CLASS();\n};\n\n// helper class for constructing elastic fiber materials from classes derived from FEFiberMaterial. \ntemplate <class fiberMat> \nclass FEElasticFiberMaterial_T : public FEElasticFiberMaterial\n{\npublic:\n\tFEElasticFiberMaterial_T(FEModel* fem) : FEElasticFiberMaterial(fem), m_fib(fem) {}\n\n\tbool Init() override \n\t{ \n\t\tif (FEElasticFiberMaterial::Init() == false) return false;\n\t\treturn m_fib.Init(); \n\t}\n\tbool Validate() override \n\t{ \n\t\tif (FEElasticFiberMaterial::Validate() == false) return false;\n\t\treturn m_fib.Validate();\n\t}\n\tFEMaterialPointData* CreateMaterialPointData() override \n\t{ \n\t\treturn new FEElasticMaterialPoint(m_fib.CreateMaterialPointData());\n\t}\n\tvoid UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override \n\t{\n\t\tFEElasticFiberMaterial::UpdateSpecializedMaterialPoints(mp, tp);\n\t\tm_fib.UpdateSpecializedMaterialPoints(mp, tp);\n\t}\n\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override { return m_fib.FiberStress(mp, a0); }\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override { return m_fib.FiberTangent(mp, a0); }\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override { return m_fib.FiberStrainEnergyDensity(mp, a0); }\n\nprotected:\n\tfiberMat\tm_fib;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticFiberMaterialUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticFiberMaterialUC.h\"\n#include <FECore/FEConstValueVec3.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEElasticFiberMaterialUC, FEUncoupledMaterial)\n    // NOTE: We need to make this optional since it should not\n    // be defined when used in a CFD material.\n\tADD_PROPERTY(m_fiber, \"fiber\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticFiberMaterialUC::FEElasticFiberMaterialUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n    m_fiber = nullptr;\n    m_Us = mat3dd(1);\n    m_bUs = false;\n}\n\n// Get the fiber direction (in global coordinates) at a material point\nvec3d FEElasticFiberMaterialUC::FiberVector(FEMaterialPoint& mp)\n{\n\t// get the material coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q*fiber;\n    \n    // account for prior deformation in multigenerational formulation\n    vec3d a = FiberPreStretch(a0);\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEElasticFiberMaterialUC::FiberPreStretch(const vec3d& a0)\n{\n    // account for prior deformation in multigenerational formulation\n    if (m_bUs) {\n        vec3d a = (m_Us*a0);\n        a.unit();\n        return a;\n    }\n    else\n        return a0;\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticFiberMaterialUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for single fiber response\n\nclass FEElasticFiberMaterialUC : public FEUncoupledMaterial\n{\npublic:\n    FEElasticFiberMaterialUC(FEModel* pfem);\n\n\t// Get the fiber direction (in global coordinates) at a material point\n\tvec3d FiberVector(FEMaterialPoint& mp);\n\n\t// calculate stress in fiber direction a0\n\tvirtual mat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) = 0;\n\n\t// Spatial tangent\n\tvirtual tens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) = 0;\n\n\t//! Strain energy density\n\tvirtual double DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) = 0;\n    \n    // Set or clear pre-stretch, as needed in multigenerational materials (e.g., reactive viscoelasticity)\n    void SetPreStretch(const mat3ds& Us) { m_Us = Us; m_bUs = true; }\n    void ResetPreStretch() { m_bUs = false; }\n    vec3d FiberPreStretch(const vec3d& a0);\n\nprivate:\n\t// These are made private since fiber materials should implement the functions above instead. \n\t// The functions can still be reached when a fiber material is used in an elastic mixture. \n\t// In those cases the fiber vector is taken from the first column of Q. \n\tmat3ds DevStress(FEMaterialPoint& mp) final { return DevFiberStress(mp, FiberVector(mp)); }\n\ttens4ds DevTangent(FEMaterialPoint& mp) final { return DevFiberTangent(mp, FiberVector(mp)); }\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) final { return DevFiberStrainEnergyDensity(mp, FiberVector(mp)); }\n    \nprivate:\n    mat3ds  m_Us;   //!< pre-stretch tensor for fiber\n    bool    m_bUs;  //!< flag for pre-stretch\n\npublic:\n\t\n\tFEVec3dValuator*\tm_fiber;\t//!< fiber orientation\n\n\tdouble\tm_epsf;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\ntemplate <class FiberMatUC>\nclass FEElasticFiberMaterialUC_T : public FEElasticFiberMaterialUC\n{\npublic: \n\tFEElasticFiberMaterialUC_T(FEModel* fem) : FEElasticFiberMaterialUC(fem), m_fib(fem) {}\n\n\tbool Init() override {\n\t\tif (m_fib.Init() == false) return false;\n\t\treturn FEElasticFiberMaterialUC::Init();\n\t}\n\tbool Validate() override { return m_fib.Validate(); }\n\tFEMaterialPointData* CreateMaterialPointData() override\n\t{\n\t\tFEMaterialPointData* mp = FEUncoupledMaterial::CreateMaterialPointData();\n\t\tmp->SetNext(m_fib.CreateMaterialPointData());\n\t\treturn mp;\n\t}\t\n\tvoid UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override \n\t{ \n\t\tFEElasticFiberMaterialUC::UpdateSpecializedMaterialPoints(mp, tp);\n\t\tm_fib.UpdateSpecializedMaterialPoints(mp, tp); \n\t}\n\n\tmat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override { return m_fib.DevFiberStress(mp, a0); }\n\ttens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override { return m_fib.DevFiberTangent(mp, a0); }\n\tdouble DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override { return m_fib.DevFiberStrainEnergyDensity(mp, a0); }\n\nprotected:\n\tFiberMatUC\tm_fib;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FECore/FEModel.h\"\n\n//BEGIN_FECORE_CLASS(FEElasticMaterial, FESolidMaterial)\n//END_FECORE_CLASS();\n\nFEElasticMaterial::FEElasticMaterial(FEModel* pfem) : FESolidMaterial(pfem)\n{ \n\tm_density = 1;\n\tAddDomainParameter(new FEElasticStress());\n}\n\n//-----------------------------------------------------------------------------\nFEElasticMaterial::~FEElasticMaterial()\n{ \n\t\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticMaterial::CreateMaterialPointData()\n{ \n\treturn new FEElasticMaterialPoint;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial tangent stiffness at material point, using secant method\nmat3ds FEElasticMaterial::SecantStress(FEMaterialPoint& mp, bool PK2)\n{\n\t// extract the deformation gradient\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tmat3ds E = pt.Strain();\n\tmat3dd I(1);\n\tmat3d FiT = F.transinv();\n\n\t// calculate the 2nd P-K stress at the current deformation gradient\n\tdouble W = StrainEnergyDensity(mp);\n\n\t// create deformation gradient increment\n\tdouble eps = 1e-9;\n\tvec3d e[3];\n\te[0] = vec3d(1, 0, 0); e[1] = vec3d(0, 1, 0); e[2] = vec3d(0, 0, 1);\n\tmat3ds S(0.0);\n\tfor (int k = 0; k < 3; ++k) {\n\t\tfor (int l = k; l < 3; ++l) {\n\t\t\t// evaluate incremental stress\n\t\t\tmat3d dF = FiT * ((e[k] & e[l]))*(eps*0.5);\n\t\t\tmat3d F1 = F + dF;\n\t\t\tpt.m_F = F1;\n\t\t\tpt.m_J = pt.m_F.det();\n\n\t\t\tdouble dW = StrainEnergyDensity(mp) - W;\n\n\t\t\t// evaluate the secant modulus\n\t\t\tS(k, l) = 2.0 * dW / eps;\n\t\t}\n\t}\n\n    // restore values\n    pt.m_F = F;\n    pt.m_J = J;\n    \n    if (PK2) return S;\n    else {\n        // push from material to spatial frame\n        mat3ds s = pt.push_forward(S);\n        \n        // return secant stress\n        return s;\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! return the strain energy density\ndouble FEElasticMaterial::StrainEnergyDensity(FEMaterialPoint& pt) { return 0; }\n\n//-----------------------------------------------------------------------------\nFEElasticStress::FEElasticStress() : FEDomainParameter(\"stress\")\n{\n\n}\n\nFEParamValue FEElasticStress::value(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\treturn ep.m_s;\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESolidMaterial.h\"\n#include \"FEElasticMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for (hyper-)elastic materials\n\nclass FEBIOMECH_API FEElasticMaterial : public FESolidMaterial\n{\npublic:\n\t//! constructor \n\tFEElasticMaterial(FEModel* pfem);\n\n\t//! destructor\n\t~FEElasticMaterial();\n\n\t//! create material point data for this material\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt);\n    \n    // get the elastic material\n    virtual FEElasticMaterial* GetElasticMaterial() { return this; }\n\npublic:\n\t//! evaluates approximation to Cauchy stress using forward difference\n\tmat3ds SecantStress(FEMaterialPoint& pt, bool PK2 = false) override;\n\npublic:\n    virtual double StrongBondSED(FEMaterialPoint& pt) { return StrainEnergyDensity(pt); }\n    virtual double WeakBondSED(FEMaterialPoint& pt) { return 0; }\n\nprotected:\n//\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FEElasticMaterial);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEElasticStress : public FEDomainParameter\n{\npublic:\n\tFEElasticStress();\n\tFEParamValue value(FEMaterialPoint& mp) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nFEElasticMaterialPoint::FEElasticMaterialPoint(FEMaterialPointData* mp) : FEMaterialPointData(mp)\n{\n\tm_F.unit();\n\tm_J = 1;\n\tm_s.zero();\n    m_v = m_a = vec3d(0, 0, 0);\n    m_buncoupled = false;\n    m_Wt = m_Wp = 0;\n    m_p = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticMaterialPoint::Copy()\n{\n\tFEElasticMaterialPoint* pt = new FEElasticMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMaterialPoint::Init()\n{\n\tm_F.unit();\n\n\tm_J = 1;\n\n\tm_s.zero();\n\n    m_v = m_a = vec3d(0, 0, 0);\n    m_L.zero();\n    \n    m_Wt = m_Wp = 0;\n\n    m_p = 0;\n    \n\t// don't forget to initialize the base class\n    FEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_F & m_J & m_s & m_v & m_a & m_L & m_Wt & m_Wp & m_p;\n\tar & m_buncoupled;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the right Cauchy-Green tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::RightCauchyGreen() const\n{\n\t// get the right Cauchy-Green tensor\n\t// C = Ft*F\n\tconst mat3d& F = m_F;\n\tmat3ds C;\n\tC.xx() = F[0][0]*F[0][0]+F[1][0]*F[1][0]+F[2][0]*F[2][0]; // = C[0][0]\n\tC.yy() = F[0][1]*F[0][1]+F[1][1]*F[1][1]+F[2][1]*F[2][1]; // = C[1][1]\n\tC.zz() = F[0][2]*F[0][2]+F[1][2]*F[1][2]+F[2][2]*F[2][2]; // = C[2][2]\n\tC.xy() = F[0][0]*F[0][1]+F[1][0]*F[1][1]+F[2][0]*F[2][1]; // = C[0][1]\n\tC.yz() = F[0][1]*F[0][2]+F[1][1]*F[1][2]+F[2][1]*F[2][2]; // = C[1][2]\n\tC.xz() = F[0][0]*F[0][2]+F[1][0]*F[1][2]+F[2][0]*F[2][2]; // = C[0][2]\n\n\treturn C;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the left Cauchy-Green tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::LeftCauchyGreen() const\n{\n\t// get the left Cauchy-Green tensor\n\t// b = F*Ft\n\tconst mat3d& F = m_F;\n\tmat3ds b;\n\tb.xx() = F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2]; // = b[0][0]\n\tb.yy() = F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2]; // = b[1][1]\n\tb.zz() = F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2]; // = b[2][2]\n\tb.xy() = F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2]; // = b[0][1]\n\tb.yz() = F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2]; // = b[1][2]\n\tb.xz() = F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2]; // = b[0][2]\n\n\treturn b;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the right Cauchy-Green tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::DevRightCauchyGreen() const\n{\n\tdouble Jm23 = pow(m_J, -2.0/3.0);\n\n\t// get the deviatoric right Cauchy-Green tensor\n\t// C = Ft*F\n\tconst mat3d& F = m_F;\n\tmat3ds C;\n\tC.xx() = Jm23*(F[0][0]*F[0][0]+F[1][0]*F[1][0]+F[2][0]*F[2][0]); // = C[0][0]\n\tC.yy() = Jm23*(F[0][1]*F[0][1]+F[1][1]*F[1][1]+F[2][1]*F[2][1]); // = C[1][1]\n\tC.zz() = Jm23*(F[0][2]*F[0][2]+F[1][2]*F[1][2]+F[2][2]*F[2][2]); // = C[2][2]\n\tC.xy() = Jm23*(F[0][0]*F[0][1]+F[1][0]*F[1][1]+F[2][0]*F[2][1]); // = C[0][1]\n\tC.yz() = Jm23*(F[0][1]*F[0][2]+F[1][1]*F[1][2]+F[2][1]*F[2][2]); // = C[1][2]\n\tC.xz() = Jm23*(F[0][0]*F[0][2]+F[1][0]*F[1][2]+F[2][0]*F[2][2]); // = C[0][2]\n\n\treturn C;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the left Cauchy-Green tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::DevLeftCauchyGreen() const\n{\n\tdouble Jm23 = pow(m_J, -2.0/3.0);\n\n\t// get the left Cauchy-Green tensor\n\t// b = F*Ft\n\tconst mat3d& F = m_F;\n\tmat3ds b;\n\tb.xx() = Jm23*(F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2]); // = b[0][0]\n\tb.yy() = Jm23*(F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2]); // = b[1][1]\n\tb.zz() = Jm23*(F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2]); // = b[2][2]\n\tb.xy() = Jm23*(F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2]); // = b[0][1]\n\tb.yz() = Jm23*(F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2]); // = b[1][2]\n\tb.xz() = Jm23*(F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2]); // = b[0][2]\n\n\treturn b;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the right stretch tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::RightStretch() const\n{\n    // get the right stretch tensor\n    mat3ds C = RightCauchyGreen();\n    double l2[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    mat3ds U = dyad(v[0])*sqrt(l2[0]) + dyad(v[1])*sqrt(l2[1]) + dyad(v[2])*sqrt(l2[2]);\n    \n    return U;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the left stretch tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::LeftStretch() const\n{\n    // get the left stretch tensor\n    mat3ds B = LeftCauchyGreen();\n    double l2[3];\n    vec3d v[3];\n    B.eigen2(l2, v);\n    mat3ds V = dyad(v[0])*sqrt(l2[0]) + dyad(v[1])*sqrt(l2[1]) + dyad(v[2])*sqrt(l2[2]);\n    \n    return V;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the right stretch tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::RightStretchInverse() const\n{\n    // get the right stretch tensor\n    mat3ds C = RightCauchyGreen();\n    double l2[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    mat3ds U = dyad(v[0])/sqrt(l2[0]) + dyad(v[1])/sqrt(l2[1]) + dyad(v[2])/sqrt(l2[2]);\n    \n    return U;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the left stretch tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::LeftStretchInverse() const\n{\n    // get the left stretch tensor\n    mat3ds B = LeftCauchyGreen();\n    double l2[3];\n    vec3d v[3];\n    B.eigen2(l2, v);\n    mat3ds V = dyad(v[0])/sqrt(l2[0]) + dyad(v[1])/sqrt(l2[1]) + dyad(v[2])/sqrt(l2[2]);\n    \n    return V;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the right stretch tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::RightHencky() const\n{\n    // get the right stretch tensor\n    mat3ds C = RightCauchyGreen();\n    double l2[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    mat3ds H = dyad(v[0])*log(l2[0])/2 + dyad(v[1])*log(l2[1])/2 + dyad(v[2])*log(l2[2])/2;\n    \n    return H;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the left stretch tensor at the current material point\n\nmat3ds FEElasticMaterialPoint::LeftHencky() const\n{\n    // get the left stretch tensor\n    mat3ds B = LeftCauchyGreen();\n    double l2[3];\n    vec3d v[3];\n    B.eigen2(l2, v);\n    mat3ds h = dyad(v[0])*log(l2[0])/2 + dyad(v[1])*log(l2[1])/2 + dyad(v[2])*log(l2[2])/2;\n    \n    return h;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the rotation tensor at the current material point\n\nmat3d FEElasticMaterialPoint::Rotation() const\n{\n    return m_F*RightStretchInverse();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the Lagrangian strain at the current material point\n\nmat3ds FEElasticMaterialPoint::Strain() const\n{\n\t// get the right Cauchy-Green tensor\n\t// C = Ft*F\n\tconst mat3d& F = m_F;\n\tmat3ds C;\n\tC.xx() = F[0][0]*F[0][0]+F[1][0]*F[1][0]+F[2][0]*F[2][0]; // = C[0][0]\n\tC.yy() = F[0][1]*F[0][1]+F[1][1]*F[1][1]+F[2][1]*F[2][1]; // = C[1][1]\n\tC.zz() = F[0][2]*F[0][2]+F[1][2]*F[1][2]+F[2][2]*F[2][2]; // = C[2][2]\n\tC.xy() = F[0][0]*F[0][1]+F[1][0]*F[1][1]+F[2][0]*F[2][1]; // = C[0][1]\n\tC.yz() = F[0][1]*F[0][2]+F[1][1]*F[1][2]+F[2][1]*F[2][2]; // = C[1][2]\n\tC.xz() = F[0][0]*F[0][2]+F[1][0]*F[1][2]+F[2][0]*F[2][2]; // = C[0][2]\n\n\t// calculate the Lagrangian strain\n\t// E = 1/2*(C - 1)\n\tmat3ds E = (C - mat3dd(1))*0.5;\n\n\treturn E;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the small-strain tensor from the deformation gradient\nmat3ds FEElasticMaterialPoint::SmallStrain() const\n{\n\t// caculate small strain tensor\n\tconst mat3d& F = m_F;\n\treturn mat3ds(F[0][0] - 1.0, F[1][1] - 1.0, F[2][2] - 1.0, 0.5*(F[0][1] + F[1][0]), 0.5*(F[0][2] + F[2][0]), 0.5*(F[1][2] + F[2][1]));\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the Almansi strain tensor\nmat3ds FEElasticMaterialPoint::AlmansiStrain() const\n{\n    // caculate small strain tensor\n    mat3ds B = LeftCauchyGreen();\n    return (mat3dd(1)-B.inverse())/2.;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the 2nd PK stress from the Cauchy stress stored in the point\n\nmat3ds FEElasticMaterialPoint::pull_back(const mat3ds& A) const\n{\n\tconst mat3d& F = m_F;\n\tdouble J = m_J;\n\tmat3d Fi = F.inverse();\n\tmat3d P = Fi*A;\n\n\treturn mat3ds(J*(P[0][0]*Fi[0][0]+P[0][1]*Fi[0][1]+P[0][2]*Fi[0][2]),\n\t\t\t\t  J*(P[1][0]*Fi[1][0]+P[1][1]*Fi[1][1]+P[1][2]*Fi[1][2]),\n\t\t\t\t  J*(P[2][0]*Fi[2][0]+P[2][1]*Fi[2][1]+P[2][2]*Fi[2][2]),\n\t\t\t\t  J*(P[0][0]*Fi[1][0]+P[0][1]*Fi[1][1]+P[0][2]*Fi[1][2]),\n\t\t\t\t  J*(P[1][0]*Fi[2][0]+P[1][1]*Fi[2][1]+P[1][2]*Fi[2][2]),\n\t\t\t\t  J*(P[0][0]*Fi[2][0]+P[0][1]*Fi[2][1]+P[0][2]*Fi[2][2]));\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEElasticMaterialPoint::push_forward(const mat3ds& A) const\n{\n\tconst mat3d& F = m_F;\n\tmat3d P = F*A;\n\tdouble Ji = 1 / m_J;\n\n\treturn mat3ds(Ji*(P[0][0]*F[0][0]+P[0][1]*F[0][1]+P[0][2]*F[0][2]),\n\t\t\t\t  Ji*(P[1][0]*F[1][0]+P[1][1]*F[1][1]+P[1][2]*F[1][2]),\n\t\t\t\t  Ji*(P[2][0]*F[2][0]+P[2][1]*F[2][1]+P[2][2]*F[2][2]),\n\t\t\t\t  Ji*(P[0][0]*F[1][0]+P[0][1]*F[1][1]+P[0][2]*F[1][2]),\n\t\t\t\t  Ji*(P[1][0]*F[2][0]+P[1][1]*F[2][1]+P[1][2]*F[2][2]),\n\t\t\t\t  Ji*(P[0][0]*F[2][0]+P[0][1]*F[2][1]+P[0][2]*F[2][2]));\n}\n\n//-----------------------------------------------------------------------------\n// This function converts the spatial tangent to the material tangent\ntens4ds FEElasticMaterialPoint::pull_back(const tens4ds& c) const\n{\n\tconst mat3d& F = m_F;\n\tmat3d Fi = F.inverse();\n\tdouble J = F.det();\n\n\tdouble C[6][6] = {0};\n\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t{\n\t\t\t\t\tC[0][0] += J*Fi[0][i]*Fi[0][j]*Fi[0][k]*Fi[0][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][1] += J*Fi[0][i]*Fi[0][j]*Fi[1][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][2] += J*Fi[0][i]*Fi[0][j]*Fi[2][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][3] += J*Fi[0][i]*Fi[0][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][4] += J*Fi[0][i]*Fi[0][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][5] += J*Fi[0][i]*Fi[0][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[1][1] += J*Fi[1][i]*Fi[1][j]*Fi[1][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][2] += J*Fi[1][i]*Fi[1][j]*Fi[2][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][3] += J*Fi[1][i]*Fi[1][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][4] += J*Fi[1][i]*Fi[1][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][5] += J*Fi[1][i]*Fi[1][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[2][2] += J*Fi[2][i]*Fi[2][j]*Fi[2][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[2][3] += J*Fi[2][i]*Fi[2][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[2][4] += J*Fi[2][i]*Fi[2][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[2][5] += J*Fi[2][i]*Fi[2][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[3][3] += J*Fi[0][i]*Fi[1][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[3][4] += J*Fi[0][i]*Fi[1][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[3][5] += J*Fi[0][i]*Fi[1][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[4][4] += J*Fi[1][i]*Fi[2][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[4][5] += J*Fi[1][i]*Fi[2][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[5][5] += J*Fi[0][i]*Fi[2][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t}\n\n\treturn tens4ds(C);\n}\n\n//-----------------------------------------------------------------------------\n// This function converts the material tangent to the spatial tangent\ntens4ds FEElasticMaterialPoint::push_forward(const tens4ds& C) const\n{\n\tconst mat3d& F = m_F;\n\tdouble Ji = 1/F.det();\n\tdouble c[6][6] = {0};\n\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t{\n\t\t\t\t\tc[0][0] += Ji*F[0][i]*F[0][j]*F[0][k]*F[0][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][1] += Ji*F[0][i]*F[0][j]*F[1][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][2] += Ji*F[0][i]*F[0][j]*F[2][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][3] += Ji*F[0][i]*F[0][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][4] += Ji*F[0][i]*F[0][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][5] += Ji*F[0][i]*F[0][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[1][1] += Ji*F[1][i]*F[1][j]*F[1][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][2] += Ji*F[1][i]*F[1][j]*F[2][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][3] += Ji*F[1][i]*F[1][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][4] += Ji*F[1][i]*F[1][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][5] += Ji*F[1][i]*F[1][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[2][2] += Ji*F[2][i]*F[2][j]*F[2][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[2][3] += Ji*F[2][i]*F[2][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[2][4] += Ji*F[2][i]*F[2][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[2][5] += Ji*F[2][i]*F[2][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[3][3] += Ji*F[0][i]*F[1][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[3][4] += Ji*F[0][i]*F[1][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[3][5] += Ji*F[0][i]*F[1][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[4][4] += Ji*F[1][i]*F[2][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[4][5] += Ji*F[1][i]*F[2][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[5][5] += Ji*F[0][i]*F[2][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t}\n\n\treturn tens4ds(c);\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/tens4d.h>\n#include <FECore/FEMaterialPointProperty.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class defines material point data for elastic materials.\nclass FEBIOMECH_API FEElasticMaterialPoint : public FEMaterialPointData\n{\npublic:\n\t//! constructor\n\tFEElasticMaterialPoint(FEMaterialPointData* mp = nullptr);\n\n\t//! Initialize material point data\n\tvoid Init() override;\n\n\t//! create a shallow copy\n\tFEMaterialPointData* Copy() override;\n\n\t//! serialize material point data\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tmat3ds Strain() const;\n\tmat3ds SmallStrain() const;\n    mat3ds AlmansiStrain() const;\n\n\tmat3ds RightCauchyGreen() const;\n\tmat3ds LeftCauchyGreen () const;\n\n\tmat3ds DevRightCauchyGreen() const;\n\tmat3ds DevLeftCauchyGreen () const;\n    \n    mat3ds RightStretch() const;\n    mat3ds LeftStretch () const;\n    \n    mat3ds RightStretchInverse() const;\n    mat3ds LeftStretchInverse () const;\n    \n    mat3ds RightHencky() const;\n    mat3ds LeftHencky () const;\n    \n    mat3d Rotation() const;\n    \n    mat3ds RateOfDeformation() const { return m_L.sym(); }\n\n\tmat3ds pull_back(const mat3ds& A) const;\n\tmat3ds push_forward(const mat3ds& A) const;\n\n\ttens4ds pull_back(const tens4ds& C) const;\n\ttens4ds push_forward(const tens4ds& C) const;\n\npublic:\n    bool    m_buncoupled;   //!< set to true if this material point was created by an uncoupled material\n    \n\t// deformation data at intermediate time\n\tmat3d\tm_F;\t//!< deformation gradient\n\tdouble\tm_J;\t//!< determinant of F\n    vec3d   m_v;    //!< velocity\n    vec3d   m_a;    //!< acceleration\n    mat3d   m_L;    //!< spatial velocity gradient\n\n\t// solid material data\n\tmat3ds\t\tm_s;\t\t//!< Cauchy stress\n    \n    // uncoupled pressure\n    double      m_p;        //!< only for uncoupled materials\n    \n    // current time data\n    double      m_Wt;       //!< strain energy density at current time\n    \n    // previous time data\n    double      m_Wp;       //!< strain energy density\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticMixture.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticMixture.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nFEElasticMixtureMaterialPoint::FEElasticMixtureMaterialPoint() : FEMaterialPointArray(new FEElasticMaterialPoint)\n{ \n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticMixtureMaterialPoint::Copy()\n{\n\tFEElasticMixtureMaterialPoint* pt = new FEElasticMixtureMaterialPoint;\n\tpt->m_w = m_w;\n\tpt->m_mp = m_mp;\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMixtureMaterialPoint::Init()\n{\n\t// allocate weight array\n\tm_w.resize(Components(), 1.0);\n\n\t// don't forget to initialize the base class\n    FEMaterialPointArray::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMixtureMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointArray::Serialize(ar);\n\tar & m_w;\n}\n\n//=============================================================================\n//\t\t\t\t\t\t\t\tFEElasticMixture\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FEElasticMixture, FEElasticMaterial)\n\tADD_PROPERTY(m_pMat, \"solid\");\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticMixture::FEElasticMixture(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticMixture::CreateMaterialPointData() \n{ \n\tFEElasticMixtureMaterialPoint* pt = new FEElasticMixtureMaterialPoint();\n\tint NMAT = Materials();\n\tfor (int i=0; i<NMAT; ++i) \n\t{\n\t\tFEMaterialPointData* mpi = m_pMat[i]->CreateMaterialPointData();\n\t\tmpi->SetPrev(pt);\n\t\tFEMaterialPoint* pi = new FEMaterialPoint(mpi);\n\t\tpt->AddMaterialPoint(pi);\n\t}\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMixture::AddMaterial(FEElasticMaterial* pm) \n{ \n\tm_pMat.push_back(pm); \n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEElasticMixture::Init()\n{\n    // check if any of the solid materials are elastic mixtures -- none allowed,\n    // otherwise FEBio does not know which FEElasticMixtureMaterialPoint to access\n    int nmix = 0;\n    for (int i=0; i<Materials(); ++i)\n        if (dynamic_cast<FEElasticMixture*>(m_pMat[i])) nmix++;\n    \n    if (nmix > 0) {\n        feLogError(\"Solids in solid mixture material cannot be solid mixtures\");\n        return false;\n    }\n    \n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! This function evaluates the stress at the material point by evaluating the\n//! individual stress components. \nmat3ds FEElasticMixture::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\tvector<double>& w = pt.m_w;\n\tassert(w.size() == m_pMat.size());\n\n\t// get the elastic material point\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate stress\n\tmat3ds s; s.zero();\n\tfor (int i=0; i < (int) m_pMat.size(); ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n\t\t// copy the elastic material point data to the components\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\t\tepi.m_F = ep.m_F;\n\t\tepi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n\n\t\ts += epi.m_s = m_pMat[i]->Stress(mpi)*w[i];\n\t}\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEElasticMixture::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\tvector<double>& w = pt.m_w;\n\tassert(w.size() == m_pMat.size());\n\n\t// get the elastic material point\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate elasticity tensor\n\ttens4ds c(0.);\n\tfor (int i=0; i < (int) m_pMat.size(); ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n\t\t// copy the elastic material point data to the components\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\t\tepi.m_F = ep.m_F;\n\t\tepi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n\n\t\tc += m_pMat[i]->Tangent(mpi)*w[i];\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! This function evaluates the stress at the material point by evaluating the\n//! individual stress components.\ndouble FEElasticMixture::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\tvector<double>& w = pt.m_w;\n\tassert(w.size() == m_pMat.size());\n    \n\t// get the elastic material point\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate strain energy density\n\tdouble sed = 0.0;\n\tfor (int i=0; i < (int) m_pMat.size(); ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n\t\t// copy the elastic material point data to the components\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\t\tepi.m_F = ep.m_F;\n\t\tepi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n\n\t\tsed += m_pMat[i]->StrainEnergyDensity(mpi)*w[i];\n\t}\n    \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n//! This function evaluates the stress at the material point by evaluating the\n//! individual stress components.\ndouble FEElasticMixture::StrongBondSED(FEMaterialPoint& mp)\n{\n    FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n    vector<double>& w = pt.m_w;\n    assert(w.size() == m_pMat.size());\n    \n    // get the elastic material point\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate strain energy density\n    double sed = 0.0;\n    for (int i=0; i < (int) m_pMat.size(); ++i)\n    {\n        FEMaterialPoint& mpi = *pt.GetPointData(i);\n        mpi.m_elem = mp.m_elem;\n        mpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n        // copy the elastic material point data to the components\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        epi.m_F = ep.m_F;\n        epi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n        \n        sed += m_pMat[i]->StrongBondSED(mpi)*w[i];\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! This function evaluates the stress at the material point by evaluating the\n//! individual stress components.\ndouble FEElasticMixture::WeakBondSED(FEMaterialPoint& mp)\n{\n    FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n    vector<double>& w = pt.m_w;\n    assert(w.size() == m_pMat.size());\n    \n    // get the elastic material point\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate strain energy density\n    double sed = 0.0;\n    for (int i=0; i < (int) m_pMat.size(); ++i)\n    {\n        FEMaterialPoint& mpi = *pt.GetPointData(i);\n        mpi.m_elem = mp.m_elem;\n        mpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n        // copy the elastic material point data to the components\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        epi.m_F = ep.m_F;\n        epi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n        \n        sed += m_pMat[i]->WeakBondSED(mpi)*w[i];\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! specialized material points\nvoid FEElasticMixture::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n    \n    for (int i=0; i < (int) m_pMat.size(); ++i)\n    {\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        FEMaterial* pmj = GetMaterial(i);\n        pmj->UpdateSpecializedMaterialPoints(mpi, tp);\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticMixture.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material point data for mixtures\n//!\nclass FEElasticMixtureMaterialPoint : public FEMaterialPointArray\n{\npublic:\n\t//! constructor\n\tFEElasticMixtureMaterialPoint();\n\n\t//! Copy material point data\n\tFEMaterialPointData* Copy() override;\n\n\t//! material point initialization\n\tvoid Init() override;\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tvector<double>\t\t\t\tm_w;\t//!< material weights\n};\n\n//-----------------------------------------------------------------------------\n//! Elastic mixtures\n\n//! This class describes a mixture of elastic solids.  The user must declare\n//! elastic solids that can be combined within this class.  The stress and\n//! tangent tensors evaluated in this class represent the sum of the respective\n//! tensors of all the solids forming the mixture.\n\n//! \\todo This class defines two accessor interfaces. Modify to use the FEMaterial interface only.\nclass FEElasticMixture : public FEElasticMaterial\n{\npublic:\n\tFEElasticMixture(FEModel* pfem);\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// return number of materials\n\tint Materials() { return (int)m_pMat.size(); }\n\n\t// return a material component\n\tFEElasticMaterial* GetMaterial(int i) { return m_pMat[i]; }\n\n\t// Add a material component\n\tvoid AddMaterial(FEElasticMaterial* pm);\n\n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic:\n    //! data initialization\n    bool Init() override;\n    \n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n\npublic:\n    double StrongBondSED(FEMaterialPoint& pt) override;\n    double WeakBondSED(FEMaterialPoint& pt) override;\n    \nprivate:\n\tstd::vector<FEElasticMaterial*>\tm_pMat;\t//!< pointers to elastic materials\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticMultigeneration.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECore/tens4d.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FEElasticMultigeneration.h\"\n\n//=============================================================================\nFEGenerationBase::FEGenerationBase(FEModel* fem) : FEMaterialProperty(fem) \n{\n\tbtime = 0;\n\tm_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEGenerationBase::CreateMaterialPointData() \n{\n\treturn m_pMat->CreateMaterialPointData();\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEGenerationBase::Stress(FEMaterialPoint& pt)\n{\n\treturn m_pMat->Stress(pt);\n}\n\t\t\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEGenerationBase::Tangent(FEMaterialPoint& pt)\n{\n\treturn m_pMat->Tangent(pt);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEGenerationBase::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n\treturn m_pMat->StrainEnergyDensity(pt);\n}\n\n//=============================================================================\n// define the material parameters\nBEGIN_FECORE_CLASS(FEGenerationMaterial, FEGenerationBase)\n\n\t// material parameters\n\tADD_PARAMETER(btime, \"start_time\");\n\n\t// the solid property\n\tADD_PROPERTY(m_pMat, \"solid\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEGenerationMaterial::FEGenerationMaterial(FEModel* pfem) : FEGenerationBase(pfem)\n{\n}\n\n//=============================================================================\nFEMultigenerationMaterialPoint::FEMultigenerationMaterialPoint() : FEMaterialPointArray(new FEElasticMaterialPoint)\n{\n    m_tgen = 0.0;\n    m_ngen = 1;     // the first generation is always active\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMultigenerationMaterialPoint::Copy()\n{\n\tFEMultigenerationMaterialPoint* pt = new FEMultigenerationMaterialPoint(*this);\n    pt->m_mp = m_mp;\n\tpt->m_pmat = m_pmat;\n    pt->m_tgen = m_tgen;\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultigenerationMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointArray::Serialize(ar);\n\tif (ar.IsShallow())\n\t{\n\t\tar & m_tgen & m_ngen;\n\t\tfor (int i=0; i < (int)m_mp.size(); i++) m_mp[i]->Serialize(ar);\n\t\t// TODO: shallow copy m_pmat\n\t}\n\telse\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tar << m_tgen << m_ngen;\n\t\t\tar << (int)m_mp.size();\n\t\t\tfor (int i=0; i < (int)m_mp.size(); i++) m_mp[i]->Serialize(ar);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar >> m_tgen >> m_ngen;\n\t\t\tint mp_size;\n\t\t\tar >> mp_size;\n\t\t\tm_mp.resize(mp_size);\n\t\t\tfor (int i=0; i < mp_size; i++)\n\t\t\t{\n\t\t\t\tm_mp[i] = new FEMaterialPoint(new FEElasticMaterialPoint);\n\t\t\t\tm_mp[i]->Serialize(ar);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultigenerationMaterialPoint::Init()\n{\n\tFEMaterialPointArray::Init();\n    for (int i=0; i<(int)m_mp.size(); ++i) m_mp[i]->Init();\n\n\tm_tgen = 0.0;\n\tm_ngen = 1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultigenerationMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointArray::Update(timeInfo);\n\n\t// get the time\n\tdouble t = timeInfo.currentTime;\n\n\t// Check if this constitutes a new generation\n\tint igen = m_pmat->CheckGeneration(t);\n\tt = m_pmat->m_MG[igen]->btime;\n\tif (t>m_tgen)\n\t{\n\t\tFEElasticMaterialPoint& pt = *((*this).ExtractData<FEElasticMaterialPoint>());\n\t\t\t\t\t\n\t\t// push back F and J to define relative deformation gradient of this generation\n\t\tmat3d F = pt.m_F;\n\t\tdouble J = pt.m_J;\n        FEElasticMaterialPoint& pe = *(m_mp[m_ngen]->ExtractData<FEElasticMaterialPoint>());\n        pe.m_F = F.inverse();\n        pe.m_J = 1.0/J;\n\t\tm_tgen = t;\n        ++m_ngen;\n\t}\n}\n\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FEElasticMultigeneration, FEElasticMaterial)\n\tADD_PROPERTY(m_MG, \"generation\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticMultigeneration::FEElasticMultigeneration(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEElasticMultigeneration::CreateMaterialPointData()\n{\n    // use the zero-th generation material point as the base elastic material point\n    FEMultigenerationMaterialPoint* pt = new FEMultigenerationMaterialPoint();\n    pt->m_pmat = this;\n    int NMAT = Materials();\n    for (int i=0; i<NMAT; ++i) pt->AddMaterialPoint(new FEMaterialPoint(m_MG[i]->CreateMaterialPointData()));\n    return pt;\n}\n\n//--------------------------------------------------------------------------------\n// Check if time t constitutes a new generation and return that generation\nint FEElasticMultigeneration::CheckGeneration(const double t)\n{\n\tint ngen = (int)m_MG.size();\n\tfor (int igen=1; igen<ngen; ++igen)\n\t{\n\t\tif (t < m_MG[igen]->btime) return igen - 1;\n\t}\n\treturn ngen - 1;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEElasticMultigeneration::Stress(FEMaterialPoint& mp)\n{\n\tFEMultigenerationMaterialPoint& pt = *mp.ExtractData<FEMultigenerationMaterialPoint>();\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3ds s;\n\t\n\t// calculate stress\n\ts.zero();\n\t\n\t// extract deformation gradient\n\tmat3d Fs = ep.m_F;\n\tdouble Js = ep.m_J;\n\t\n\tfor (int i=0; i < pt.m_ngen; ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\n        // evaluate deformation gradient for this generation\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\n\t\t// copy the material point data to the components\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n\t\tmpi.m_r0 = mp.m_r0;\n\n        // store safe copies of Fi and Ji for this generation\n        mat3d Fi = epi.m_F;\n        double Ji = epi.m_J;\n        \n        // evaluate relative deformation gradient\n       \tepi.m_F = Fs*Fi;\n        epi.m_J = Js*Ji;\n        \n        // evaluate stress for this generation\n        s += epi.m_s = m_MG[i]->Stress(mpi);\n        \n        // restore the material point deformation gradient\n        epi.m_F = Fi;\n        epi.m_J = Ji;\n\t}\n    \n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEElasticMultigeneration::Tangent(FEMaterialPoint& mp)\n{\n\tFEMultigenerationMaterialPoint& pt = *mp.ExtractData<FEMultigenerationMaterialPoint>();\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\ttens4ds c(0.);\n\t\n\t// extract deformation gradient\n\tmat3d Fs = ep.m_F;\n\tdouble Js = ep.m_J;\n\t\n\tfor (int i=0; i < pt.m_ngen; ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\n\t\t// evaluate deformation gradient for this generation\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        \n        // store safe copies of Fi and Ji for this generation\n        mat3d Fi = epi.m_F;\n        double Ji = epi.m_J;\n        \n        // copy the elastic material point data to the components\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n        mpi.m_r0 = mp.m_r0;\n        \n        // evaluate relative deformation gradient\n       \tepi.m_F = Fs*Fi;\n        epi.m_J = Js*Ji;\n        \n\t\t// evaluate tangent for this generation\n\t\tc += m_MG[i]->Tangent(mpi);\n\n        // restore the material point deformation gradient\n        epi.m_F = Fi;\n        epi.m_J = Ji;\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElasticMultigeneration::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEMultigenerationMaterialPoint& pt = *mp.ExtractData<FEMultigenerationMaterialPoint>();\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\tdouble sed = 0.0;\n\t\n\t// extract deformation gradient\n\tmat3d Fs = ep.m_F;\n\tdouble Js = ep.m_J;\n\t\n\tfor (int i=0; i < pt.m_ngen; ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\n        // evaluate deformation gradient for this generation\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        \n        // store safe copies of Fi and Ji for this generation\n        mat3d Fi = epi.m_F;\n        double Ji = epi.m_J;\n        \n        // copy the elastic material point data to the components\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n        mpi.m_r0 = mp.m_r0;\n        \n        // evaluate relative deformation gradient\n       \tepi.m_F = Fs*Fi;\n        epi.m_J = Js*Ji;\n        \n        // evaluate strain energy density for this generation\n        double dsed = m_MG[i]->StrainEnergyDensity(mpi)/Ji;\n        sed += dsed;\n        \n        // restore the material point deformation gradient\n        epi.m_F = Fi;\n        epi.m_J = Ji;\n\t}\n     \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElasticMultigeneration::StrongBondSED(FEMaterialPoint& mp)\n{\n    FEMultigenerationMaterialPoint& pt = *mp.ExtractData<FEMultigenerationMaterialPoint>();\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double sed = 0.0;\n    \n    // extract deformation gradient\n    mat3d Fs = ep.m_F;\n    double Js = ep.m_J;\n    \n    for (int i=0; i < pt.m_ngen; ++i)\n    {\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\n        // evaluate deformation gradient for this generation\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        \n        // store safe copies of Fi and Ji for this generation\n        mat3d Fi = epi.m_F;\n        double Ji = epi.m_J;\n        \n        // copy the elastic material point data to the components\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n        mpi.m_r0 = mp.m_r0;\n        \n        // evaluate relative deformation gradient\n        epi.m_F = Fs*Fi;\n        epi.m_J = Js*Ji;\n        \n        // evaluate strain energy density for this generation\n        double dsed = m_MG[i]->m_pMat->StrongBondSED(mpi)/Ji;\n        sed += dsed;\n        \n        // restore the material point deformation gradient\n        epi.m_F = Fi;\n        epi.m_J = Ji;\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElasticMultigeneration::WeakBondSED(FEMaterialPoint& mp)\n{\n    FEMultigenerationMaterialPoint& pt = *mp.ExtractData<FEMultigenerationMaterialPoint>();\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double sed = 0.0;\n    \n    // extract deformation gradient\n    mat3d Fs = ep.m_F;\n    double Js = ep.m_J;\n    \n    for (int i=0; i < pt.m_ngen; ++i)\n    {\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\n        // evaluate deformation gradient for this generation\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        \n        // store safe copies of Fi and Ji for this generation\n        mat3d Fi = epi.m_F;\n        double Ji = epi.m_J;\n        \n        // copy the elastic material point data to the components\n\t\tmpi.m_elem = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt = mp.m_rt;\n        mpi.m_r0 = mp.m_r0;\n        \n        // evaluate relative deformation gradient\n        epi.m_F = Fs*Fi;\n        epi.m_J = Js*Ji;\n        \n        // evaluate strain energy density for this generation\n        double dsed = m_MG[i]->m_pMat->WeakBondSED(mpi)/Ji;\n        sed += dsed;\n        \n        // restore the material point deformation gradient\n        epi.m_F = Fi;\n        epi.m_J = Ji;\n    }\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticMultigeneration.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// TODO: This was introduced so that the FEGenerationMaterial fits in the current\n//       framework, which assumes that all features classes are derived from base-classes.\nclass FEBIOMECH_API FEGenerationBase : public FEMaterialProperty\n{\npublic:\n\tFEGenerationBase(FEModel* fem);\n\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt);\n\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt);\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt);\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic:\n\tdouble\tbtime;\t//!< generation birth time\n\n\tFEElasticMaterial* m_pMat;\t//!< pointer to elastic material\n\n\tFECORE_BASE_CLASS(FEGenerationBase)\n};\n\n//-----------------------------------------------------------------------------\n//! Material defining a single generation of a multi-generation material\nclass FEGenerationMaterial : public FEGenerationBase\n{\npublic:\n\tFEGenerationMaterial(FEModel* pfem);\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// forward declaration of material class\nclass FEElasticMultigeneration;\n\n//-----------------------------------------------------------------------------\n//! Multigenerational material point.\n//! First generation exists at t=0. Second, third, etc. generations appear at t>0.\n//! This material point stores the inverse of the relative deformation gradient of\n//! second, third, etc. generations.  These relate the reference configuration of \n//! each generation relative to the first generation.\n\nclass FEMultigenerationMaterialPoint : public FEMaterialPointArray\n{\npublic:\n    FEMultigenerationMaterialPoint();\n\t\t\n\tFEMaterialPointData* Copy();\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar);\n\n\tvoid Init();\n\n\tvoid Update(const FETimeInfo& timeInfo);\n\npublic:\n\t// multigenerational material data\n\tdouble\tm_tgen;\t\t//!< last generation time\n    int     m_ngen;     //!< number of active generations\n\tFEElasticMultigeneration*\tm_pmat;\n};\n\n//-----------------------------------------------------------------------------\n//! Multigenerational solid\n\nclass FEElasticMultigeneration : public FEElasticMaterial\n{\npublic:\n\tFEElasticMultigeneration(FEModel* pfem);\n\t\t\n\t// returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n\n    // return number of materials\n    int Materials() { return (int)m_MG.size(); }\n    \n    // return a generation material component\n\tFEGenerationBase* GetMaterial(int i) { return m_MG[i]; }\n    \npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\tint CheckGeneration(const double t);\n\npublic:\n    double StrongBondSED(FEMaterialPoint& pt) override;\n    double WeakBondSED(FEMaterialPoint& pt) override;\n    \npublic:\n\tstd::vector<FEGenerationBase*>\tm_MG;\t\t//!< multigeneration data\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEElasticShellDomain.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEBodyForce.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <math.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBioMech.h\"\n\nBEGIN_FECORE_CLASS(FEElasticShellDomain, FESSIShellDomain)\n\tADD_PARAMETER(m_secant_stress, \"secant_stress\");\n\tADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticShellDomain::FEElasticShellDomain(FEModel* pfem) : FESSIShellDomain(pfem), FEElasticDomain(pfem), m_dofV(pfem), m_dofSV(pfem), m_dofSA(pfem), m_dofR(pfem), m_dof(pfem)\n{\n\tm_pMat = 0;\n    m_alphaf = m_beta = 1;\n    m_alpham = 2;\n    m_update_dynamic = true; // default for backward compatibility\n\n    m_secant_stress = false;\n    m_secant_tangent = false;\n\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n        m_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n        m_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEElasticShellDomain& FEElasticShellDomain::operator = (FEElasticShellDomain& d)\n{ \n\tm_Elem = d.m_Elem; \n\tm_pMesh = d.m_pMesh; \n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\n//! Set flag for update for dynamic quantities\nvoid FEElasticShellDomain::SetDynamicUpdateFlag(bool b)\n{\n    m_update_dynamic = b;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEElasticShellDomain::Serialize(DumpStream& ar)\n{\n    //erialize the base class, which instantiates the elements\n    FESSIShellDomain::Serialize(ar);\n    if (ar.IsShallow()) return;\n\n    // serialize class variables\n    ar & m_alphaf;\n    ar & m_alpham;\n    ar & m_beta;\n    ar & m_update_dynamic;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FESolidMaterial*>(pmat);\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dofs\nconst FEDofList& FEElasticShellDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::Activate()\n{\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\n\t\t\t\tif (node.HasFlags(FENode::SHELL))\n\t\t\t\t{\n\t\t\t\t\tnode.set_active(m_dofSU[0]);\n\t\t\t\t\tnode.set_active(m_dofSU[1]);\n\t\t\t\t\tnode.set_active(m_dofSU[2]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEElasticShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    m_alphaf = timeInfo.alphaf;\n    m_alpham = timeInfo.alpham;\n    m_beta = timeInfo.beta;\n    \n    vec3d r0, rt;\n    for (size_t i=0; i<m_Elem.size(); ++i)\n    {\n        FEShellElement& el = m_Elem[i];\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint n = el.GaussPoints();\n\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tpt.m_Wp = pt.m_Wt;\n\n\t\t\t\tmp.Update(timeInfo);\n\t\t\t}\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculates the forces due to the stress\nvoid FEElasticShellDomain::InternalForces(FEGlobalVector& R)\n{\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for shared (NS)\n    for (int i=0; i<NS; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// create the element force vector and initialize to zero\n\t\t\tint ndof = 6 * el.Nodes();\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// calculate element's internal force\n\t\t\tElementInternalForce(el, fe);\n\n\t\t\t// get the element's LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble the residual\n\t\t\tR.Assemble(el.m_node, lm, fe, true);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for shell elements\n//! Note that we use a one-point gauss integration rule for the thickness\n//! integration. This will integrate linear functions exactly.\n\nvoid FEElasticShellDomain::ElementInternalForce(FEShellElement& el, vector<double>& fe)\n{\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\t// repeat for all integration points\n\tdouble* gw = el.GaussWeights();\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEElasticMaterialPoint& pt = *(el.GetMaterialPoint(n)->ExtractData<FEElasticMaterialPoint>());\n\n\t\t// calculate the jacobian\n\t\tdouble detJt = (m_alphaf == 1.0 ? detJ(el, n) : detJ(el, n, m_alphaf))*gw[n];\n\n\t\t// get base vectors\n\t\tvec3d gcnt[3];\n\t\tif (m_alphaf == 1.0)\n\t\t\tContraBaseVectors(el, n, gcnt);\n\t\telse\n\t\t\tContraBaseVectors(el, n, gcnt, m_alphaf);\n\n\t\t// get the stress vector for this integration point\n\t\tmat3ds& s = pt.m_s;\n\n\t\tdouble eta = el.gt(n);\n\n\t\tconst double* Mr = el.Hr(n);\n\t\tconst double* Ms = el.Hs(n);\n\t\tconst double* M  = el.H(n);\n        \n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n            vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            vec3d gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            vec3d gradMd = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            vec3d fu = s*gradMu;\n            vec3d fd = s*gradMd;\n            \n            // calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[6*i  ] -= fu.x*detJt;\n\t\t\tfe[6*i+1] -= fu.y*detJt;\n\t\t\tfe[6*i+2] -= fu.z*detJt;\n\n\t\t\tfe[6*i+3] -= fd.x*detJt;\n\t\t\tfe[6*i+4] -= fd.y*detJt;\n\t\t\tfe[6*i+5] -= fd.z*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for\n    for (int i=0; i<NS; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// create the element force vector and initialize to zero\n\t\t\tint ndof = 6 * el.Nodes();\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// apply body forces to shells\n\t\t\tElementBodyForce(BF, el, fe);\n\n\t\t\t// get the element's LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble the residual\n\t\t\tR.Assemble(el.m_node, lm, fe, true);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element body forces for shells\n\nvoid FEElasticShellDomain::ElementBodyForce(FEBodyForce& BF, FEShellElement& el, vector<double>& fe)\n{\n    // integration weights\n    double* gw = el.GaussWeights();\n    double eta;\n    double *M, detJt;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tdouble dens = m_pMat->Density(mp);\n        \n        // calculate the jacobian\n        detJt = detJ0(el, n)*gw[n];\n        \n        M  = el.H(n);\n        eta = el.gt(n);\n        \n        // get the force\n        vec3d f = BF.force(mp);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fu = f*(dens*M[i]*(1+eta)/2*detJt);\n            vec3d fd = f*(dens*M[i]*(1-eta)/2*detJt);\n            \n            fe[6*i  ] -= fu.x;\n            fe[6*i+1] -= fu.y;\n            fe[6*i+2] -= fu.z;\n            \n            fe[6*i+3] -= fd.x;\n            fe[6*i+4] -= fd.y;\n            fe[6*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculate inertial forces \\todo Why is F no longer needed?\nvoid FEElasticShellDomain::InertialForces(FEGlobalVector& R, vector<double>& F)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n\t\tif (el.isActive())\n\t\t{\n\n\t\t\t// get the element force vector and initialize it to zero\n\t\t\tint ndof = 6 * el.Nodes();\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// calculate internal force vector\n\t\t\tElementInertialForce(el, fe);\n\n\t\t\t// get the element's LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble element 'fe'-vector into global R vector\n\t\t\tR.Assemble(el.m_node, lm, fe, true);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::ElementInertialForce(FEShellElement& el, vector<double>& fe)\n{\n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n\n    // evaluate the element inertial force vector\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        double dens = m_pMat->Density(mp);\n        double J0 = detJ0(el, n)*el.GaussWeights()[n];\n        \n        double* M = el.H(n);\n        double eta = el.gt(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            vec3d fu = pt.m_a*(dens*M[i]*(1+eta)/2*J0);\n            vec3d fd = pt.m_a*(dens*M[i]*(1-eta)/2*J0);\n            \n            fe[6*i  ] -= fu.x;\n            fe[6*i+1] -= fu.y;\n            fe[6*i+2] -= fu.z;\n            \n            fe[6*i+3] -= fd.x;\n            fe[6*i+4] -= fd.y;\n            fe[6*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEElasticShellDomain::ElementBodyForceStiffness(FEBodyForce& BF, FEShellElement &el, matrix &ke)\n{\n    int i, j, i6, j6;\n    int neln = el.Nodes();\n    \n    // jacobian\n    double detJ;\n    double *M;\n    double* gw = el.GaussWeights();\n    mat3d K;\n    \n    double Mu[FEElement::MAX_NODES], Md[FEElement::MAX_NODES];\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        detJ = detJ0(el, n)*gw[n]*m_alphaf;\n        \n        // get the stiffness\n        K = BF.stiffness(mp)*m_pMat->Density(mp)*detJ;\n        \n        M = el.H(n);\n        \n        double eta = el.gt(n);\n        \n        for (i=0; i<neln; ++i)\n        {\n            Mu[i] = M[i]*(1+eta)/2;\n            Md[i] = M[i]*(1-eta)/2;\n        }\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                mat3d Kuu = K*(Mu[i]*Mu[j]);\n                mat3d Kud = K*(Mu[i]*Md[j]);\n                mat3d Kdu = K*(Md[i]*Mu[j]);\n                mat3d Kdd = K*(Md[i]*Md[j]);\n                \n                ke[i6  ][j6  ] += Kuu(0,0); ke[i6  ][j6+1] += Kuu(0,1); ke[i6  ][j6+2] += Kuu(0,2);\n                ke[i6+1][j6  ] += Kuu(1,0); ke[i6+1][j6+1] += Kuu(1,1); ke[i6+1][j6+2] += Kuu(1,2);\n                ke[i6+2][j6  ] += Kuu(2,0); ke[i6+2][j6+1] += Kuu(2,1); ke[i6+2][j6+2] += Kuu(2,2);\n                \n                ke[i6  ][j6+3] += Kud(0,0); ke[i6  ][j6+4] += Kud(0,1); ke[i6  ][j6+5] += Kud(0,2);\n                ke[i6+1][j6+3] += Kud(1,0); ke[i6+1][j6+4] += Kud(1,1); ke[i6+1][j6+5] += Kud(1,2);\n                ke[i6+2][j6+3] += Kud(2,0); ke[i6+2][j6+4] += Kud(2,1); ke[i6+2][j6+5] += Kud(2,2);\n                \n                ke[i6+3][j6  ] += Kdu(0,0); ke[i6+3][j6+1] += Kdu(0,1); ke[i6+3][j6+2] += Kdu(0,2);\n                ke[i6+4][j6  ] += Kdu(1,0); ke[i6+4][j6+1] += Kdu(1,1); ke[i6+4][j6+2] += Kdu(1,2);\n                ke[i6+5][j6  ] += Kdu(2,0); ke[i6+5][j6+1] += Kdu(2,1); ke[i6+5][j6+2] += Kdu(2,2);\n                \n                ke[i6+3][j6+3] += Kdd(0,0); ke[i6+3][j6+4] += Kdd(0,1); ke[i6+3][j6+5] += Kdd(0,2);\n                ke[i6+4][j6+3] += Kdd(1,0); ke[i6+4][j6+4] += Kdd(1,1); ke[i6+4][j6+5] += Kdd(1,2);\n                ke[i6+5][j6+3] += Kdd(2,0); ke[i6+5][j6+4] += Kdd(2,1); ke[i6+5][j6+5] += Kdd(2,2);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEElasticShellDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n    // repeat over all shell elements\n    int NS = (int)m_Elem.size();\n#pragma omp parallel for shared (NS)\n    for (int iel=0; iel<NS; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// create the element's stiffness matrix\n\t\t\tFEElementMatrix ke(el);\n\t\t\tint ndof = 6 * el.Nodes();\n\t\t\tke.resize(ndof, ndof);\n\n\t\t\t// calculate the element stiffness matrix\n\t\t\tElementStiffness(iel, ke);\n\n\t\t\t// get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\t\t\tke.SetIndices(lm);\n\n\t\t\t// assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// create the element's stiffness matrix\n\t\t\tFEElementMatrix ke(el);\n\t\t\tint ndof = 6 * el.Nodes();\n\t\t\tke.resize(ndof, ndof);\n\t\t\tke.zero();\n\n\t\t\t// calculate inertial stiffness\n\t\t\tElementMassMatrix(el, ke, scale);\n\n\t\t\t// get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\t\t\tke.SetIndices(lm);\n\n\t\t\t// assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    // repeat over all shell elements\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// create the element's stiffness matrix\n\t\t\tFEElementMatrix ke(el);\n\t\t\tint ndof = 6 * el.Nodes();\n\t\t\tke.resize(ndof, ndof);\n\t\t\tke.zero();\n\n\t\t\t// calculate inertial stiffness\n\t\t\tElementBodyForceStiffness(bf, el, ke);\n\n\t\t\t// get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\t\t\tke.SetIndices(lm);\n\n\t\t\t// assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the shell element stiffness matrix\n\nvoid FEElasticShellDomain::ElementStiffness(int iel, matrix& ke)\n{\n    FEShellElement& el = Element(iel);\n    \n    int i, i6, j, j6, n;\n    \n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    const double* Mr, *Ms, *M;\n    vec3d gradMu[FEElement::MAX_NODES], gradMd[FEElement::MAX_NODES];\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // calculate element stiffness matrix\n    ke.zero();\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = detJ(el, n, m_alphaf)*gw[n]*m_alphaf;\n        \n        // get the stress and elasticity for this integration point\n        mat3ds s = pt.m_s;\n//        tens4ds C = m_pMat->Tangent(mp);\n        tens4dmm C = (m_secant_tangent ? m_pMat->SecantTangent(mp) : m_pMat->SolidTangent(mp));\n\n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // ------------ constitutive component --------------\n        \n        // setup the material point\n        \n        for (i=0; i<neln; ++i)\n        {\n            vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n        }\n        \n        for (i=0, i6=0; i<neln; ++i, i6 += 6)\n        {\n            for (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n            {\n                mat3d Kuu = vdotTdotv(gradMu[i], C, gradMu[j])*detJt;\n                mat3d Kud = vdotTdotv(gradMu[i], C, gradMd[j])*detJt;\n                mat3d Kdu = vdotTdotv(gradMd[i], C, gradMu[j])*detJt;\n                mat3d Kdd = vdotTdotv(gradMd[i], C, gradMd[j])*detJt;\n                \n                ke[i6  ][j6  ] += Kuu(0,0); ke[i6  ][j6+1] += Kuu(0,1); ke[i6  ][j6+2] += Kuu(0,2);\n                ke[i6+1][j6  ] += Kuu(1,0); ke[i6+1][j6+1] += Kuu(1,1); ke[i6+1][j6+2] += Kuu(1,2);\n                ke[i6+2][j6  ] += Kuu(2,0); ke[i6+2][j6+1] += Kuu(2,1); ke[i6+2][j6+2] += Kuu(2,2);\n                \n                ke[i6  ][j6+3] += Kud(0,0); ke[i6  ][j6+4] += Kud(0,1); ke[i6  ][j6+5] += Kud(0,2);\n                ke[i6+1][j6+3] += Kud(1,0); ke[i6+1][j6+4] += Kud(1,1); ke[i6+1][j6+5] += Kud(1,2);\n                ke[i6+2][j6+3] += Kud(2,0); ke[i6+2][j6+4] += Kud(2,1); ke[i6+2][j6+5] += Kud(2,2);\n                \n                ke[i6+3][j6  ] += Kdu(0,0); ke[i6+3][j6+1] += Kdu(0,1); ke[i6+3][j6+2] += Kdu(0,2);\n                ke[i6+4][j6  ] += Kdu(1,0); ke[i6+4][j6+1] += Kdu(1,1); ke[i6+4][j6+2] += Kdu(1,2);\n                ke[i6+5][j6  ] += Kdu(2,0); ke[i6+5][j6+1] += Kdu(2,1); ke[i6+5][j6+2] += Kdu(2,2);\n                \n                ke[i6+3][j6+3] += Kdd(0,0); ke[i6+3][j6+4] += Kdd(0,1); ke[i6+3][j6+5] += Kdd(0,2);\n                ke[i6+4][j6+3] += Kdd(1,0); ke[i6+4][j6+4] += Kdd(1,1); ke[i6+4][j6+5] += Kdd(1,2);\n                ke[i6+5][j6+3] += Kdd(2,0); ke[i6+5][j6+4] += Kdd(2,1); ke[i6+5][j6+5] += Kdd(2,2);\n            }\n        }\n        \n        // ------------ initial stress component --------------\n        \n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                double Kuu = gradMu[i]*(s*gradMu[j])*detJt;\n                double Kud = gradMu[i]*(s*gradMd[j])*detJt;\n                double Kdu = gradMd[i]*(s*gradMu[j])*detJt;\n                double Kdd = gradMd[i]*(s*gradMd[j])*detJt;\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-d component\n                ke[6*i  ][6*j+3] += Kud;\n                ke[6*i+1][6*j+4] += Kud;\n                ke[6*i+2][6*j+5] += Kud;\n                \n                // the d-u component\n                ke[6*i+3][6*j  ] += Kdu;\n                ke[6*i+4][6*j+1] += Kdu;\n                ke[6*i+5][6*j+2] += Kdu;\n                \n                // the d-d component\n                ke[6*i+3][6*j+3] += Kdd;\n                ke[6*i+4][6*j+4] += Kdd;\n                ke[6*i+5][6*j+5] += Kdd;\n            }\n        \n    } // end loop over gauss-points\n    \n}\n\n\n//-----------------------------------------------------------------------------\n//! calculates element inertial stiffness matrix\nvoid FEElasticShellDomain::ElementMassMatrix(FEShellElement& el, matrix& ke, double a)\n{\n    // Get the current element's data\n    const int nint = el.GaussPoints();\n    const int neln = el.Nodes();\n    \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n    \n    // calculate element stiffness matrix\n    for (int n=0; n<nint; ++n)\n    {\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\tdouble D = m_pMat->Density(mp);\n\n        // shape functions\n        double* M = el.H(n);\n        \n        // Jacobian\n        double J0 = detJ0(el, n)*gw[n];\n        \n        // parametric coordinate through thickness\n        double eta = el.gt(n);\n        \n        for (int i=0; i<neln; ++i)\n            for (int j=0; j<neln; ++j)\n            {\n                double Kuu = (1+eta)/2*M[i]*(1+eta)/2*M[j]*a*D*J0;\n                double Kud = (1+eta)/2*M[i]*(1-eta)/2*M[j]*a*D*J0;\n                double Kdu = (1-eta)/2*M[i]*(1+eta)/2*M[j]*a*D*J0;\n                double Kdd = (1-eta)/2*M[i]*(1-eta)/2*M[j]*a*D*J0;\n                \n                // the u-u component\n                ke[6*i  ][6*j  ] += Kuu;\n                ke[6*i+1][6*j+1] += Kuu;\n                ke[6*i+2][6*j+2] += Kuu;\n                \n                // the u-d component\n                ke[6*i  ][6*j+3] += Kud;\n                ke[6*i+1][6*j+4] += Kud;\n                ke[6*i+2][6*j+5] += Kud;\n                \n                // the d-u component\n                ke[6*i+3][6*j  ] += Kdu;\n                ke[6*i+4][6*j+1] += Kdu;\n                ke[6*i+5][6*j+2] += Kdu;\n                \n                // the d-d component\n                ke[6*i+3][6*j+3] += Kdd;\n                ke[6*i+4][6*j+4] += Kdd;\n                ke[6*i+5][6*j+5] += Kdd;\n            }\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Calculates body forces for shells\n\nvoid FEElasticShellDomain::ElementBodyForce(FEModel& fem, FEShellElement& el, vector<double>& fe)\n{\n    int NF = fem.ModelLoads();\n    for (int nf = 0; nf < NF; ++nf)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(nf));\n        if (pbf)\n        {\n            // integration weights\n            double* gw = el.GaussWeights();\n            double eta;\n            double *M, detJt;\n            \n            // loop over integration points\n            int nint = el.GaussPoints();\n            int neln = el.Nodes();\n            \n            for (int n=0; n<nint; ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t\tdouble dens0 = m_pMat->Density(mp);\n                \n                // calculate the jacobian\n                detJt = detJ0(el, n)*gw[n];\n                \n                M  = el.H(n);\n                eta = el.gt(n);\n                \n                // get the force\n                vec3d f = pbf->force(mp);\n                \n                for (int i=0; i<neln; ++i)\n                {\n                    vec3d fu = f*(dens0*M[i]*(1+eta)/2);\n                    vec3d fd = f*(dens0*M[i]*(1-eta)/2);\n                    \n                    fe[6*i  ] -= fu.x*detJt;\n                    fe[6*i+1] -= fu.y*detJt;\n                    fe[6*i+2] -= fu.z*detJt;\n                    \n                    fe[6*i+3] -= fd.x*detJt;\n                    fe[6*i+4] -= fd.y*detJt;\n                    fe[6*i+5] -= fd.z*detJt;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::Update(const FETimeInfo& tp)\n{\n    FESSIShellDomain::Update(tp);\n\n    bool berr = false;\n    int NE = Elements();\n    #pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            FEShellElement& el = Element(i);\n            if (el.isActive())\n            {\n                UpdateElementStress(i, tp);\n            }\n        }\n        catch (NegativeJacobian e)\n        {\n            #pragma omp critical\n            {\n                // reset the logfile mode\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n\n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double dt = tp.timeIncrement;\n    \n    // get the shell element\n    FEShellElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    const int NELN = FEElement::MAX_NODES;\n\tvec3d r0[NELN], s0[NELN], r[NELN], s[NELN];\n    vec3d v[NELN], w[NELN];\n    vec3d a[NELN], b[NELN];\n    // nodal coordinates\n    GetCurrentNodalCoordinates(el, r, m_alphaf, false);\n    GetCurrentNodalCoordinates(el, s, m_alphaf, true);\n    GetReferenceNodalCoordinates(el, r0, false);\n    GetReferenceNodalCoordinates(el, s0, true);\n\n    // update dynamic quantities\n    if (m_update_dynamic)\n    {\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m_pMesh->Node(el.m_node[j]);\n            v[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*m_alphaf + node.m_vp*(1-m_alphaf);\n            w[j] = node.get_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2])*m_alphaf + node.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2])*(1-m_alphaf);\n            a[j] = node.m_at*m_alpham + node.m_ap*(1-m_alpham);\n            b[j] = node.get_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2])*m_alpham + node.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2])*(1-m_alpham);\n        }\n    }\n\n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //\t\t since the material point coordinates are used by most materials.\n        mp.m_r0 = evaluate(el, r0, s0, n);\n        mp.m_rt = evaluate(el, r, s, n);\n        \n        // get the deformation gradient and determinant at intermediate time\n        mat3d Ft, Fp;\n        double Jt = defgrad(el, Ft, n);\n        double Jp = defgradp(el, Fp, n);\n\t\tif (m_alphaf == 1.0)\n\t\t{\n\t\t\tpt.m_F = Ft;\n\t\t\tpt.m_J = Jt;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpt.m_F = Ft * m_alphaf + Fp * (1 - m_alphaf);\n\t\t\tpt.m_J = pt.m_F.det();\n\t\t}\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (Ft - Fp)*Fi/dt;\n        if (m_update_dynamic)\n        {\n            pt.m_v = evaluate(el, v, w, n);\n            pt.m_a = evaluate(el, a, b, n);\n        }\n        \n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, tp);\n\n        // calculate the stress at this material point\n//        pt.m_s = m_pMat->Stress(mp);\n        pt.m_s = (m_secant_stress ? m_pMat->SecantStress(mp) : m_pMat->Stress(mp));\n\n        // adjust stress for strain energy conservation\n        if (m_alphaf == 0.5)\n        {\n            // evaluate strain energy at current time\n\t\t\tmat3d Ftmp = pt.m_F;\n\t\t\tdouble Jtmp = pt.m_J;\n\t\t\tpt.m_F = Ft;\n            pt.m_J = Jt;\n            FEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(m_pMat);\n            pt.m_Wt = pme->StrainEnergyDensity(mp);\n\t\t\tpt.m_F = Ftmp;\n\t\t\tpt.m_J = Jtmp;\n\n            mat3ds D = pt.m_L.sym();\n            double D2 = D.dotdot(D);\n            if (D2 > std::numeric_limits<double>::epsilon())\n                pt.m_s += D*(((pt.m_Wt-pt.m_Wp)/(dt*pt.m_J) - pt.m_s.dotdot(D))/D2);\n        }\n    }\n}\n\n\n//-----------------------------------------------------------------------------\n//! Unpack the element. That is, copy element data in traits structure\n//! Note that for the shell elements the lm order is different compared\n//! to the solid element ordering. This is because for shell elements the\n//! nodes have six degrees of freedom each, where for solids they only\n//! have 3 dofs.\nvoid FEElasticShellDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N*9);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& node = m_pMesh->Node(el.m_node[i]);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[6*i  ] = id[m_dofU[0]];\n\t\tlm[6*i+1] = id[m_dofU[1]];\n\t\tlm[6*i+2] = id[m_dofU[2]];\n\n\t\t// next the shell displacement dofs\n\t\tlm[6*i+3] = id[m_dofSU[0]];\n\t\tlm[6*i+4] = id[m_dofSU[1]];\n\t\tlm[6*i+5] = id[m_dofSU[2]];\n\n\t\t// rigid rotational dofs\n\t\tlm[6*N + 3*i  ] = id[m_dofR[0]];\n\t\tlm[6*N + 3*i+1] = id[m_dofR[1]];\n\t\tlm[6*N + 3*i+2] = id[m_dofR[2]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESSIShellDomain.h\"\n#include \"FEElasticDomain.h\"\n#include \"FESolidMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain described by 3D shell elements\nclass FEBIOMECH_API FEElasticShellDomain : public FESSIShellDomain, public FEElasticDomain\n{\npublic:\n\tFEElasticShellDomain(FEModel* pfem);\n\n\t//! \\todo do I really need this?\n\tFEElasticShellDomain& operator = (FEElasticShellDomain& d);\n\n\t//! Activate the domain\n\tvoid Activate() override;\n\n    //! initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n\t//! Unpack shell element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n    //! Set flag for update for dynamic quantities\n    void SetDynamicUpdateFlag(bool b);\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! get the total dofs\n\tconst FEDofList& GetDOFList() const override;\n\npublic: // overrides from FEElasticDomain\n\n\t//! calculates the residual\n//\tvoid Residual(FESolver* psolver, vector<double>& R);\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! Calculates inertial forces for dynamic problems\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override;\n\n\t//! calculate body force\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\n\t// update stresses\n\tvoid Update(const FETimeInfo& tp) override;\n    \n    // update the element stress\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t// inertial stiffness\n    void MassMatrix(FELinearSystem& LS, double scale) override;\n\n\t// body force stiffness\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n\npublic:\n\n\t// --- S T I F F N E S S --- \n\n\t//! calculates the shell element stiffness matrix\n\tvoid ElementStiffness(int iel, matrix& ke);\n\n    //! calculates the solid element mass matrix\n    void ElementMassMatrix(FEShellElement& el, matrix& ke, double a);\n    \n    //! calculates the stiffness matrix due to body forces\n    void ElementBodyForceStiffness(FEBodyForce& bf, FEShellElement& el, matrix& ke);\n    \n\t// --- R E S I D U A L ---\n\n\t//! Calculates the internal stress vector for shell elements\n\tvoid ElementInternalForce(FEShellElement& el, vector<double>& fe);\n\n\t//! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEModel& fem, FEShellElement& el, vector<double>& fe);\n\n\t//! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEBodyForce& BF, FEShellElement& el, vector<double>& fe);\n    \n    //! Calculates the inertial force for shell elements\n    void ElementInertialForce(FEShellElement& el, vector<double>& fe);\n    \nprotected:\n\tFESolidMaterial*\tm_pMat;\n    double              m_alphaf;\n    double              m_alpham;\n    double              m_beta;\n    bool                m_update_dynamic;    //!< flag for updating quantities only used in dynamic analysis\n\n\tbool\tm_secant_stress;\t//!< use secant approximation to stress\n\tbool\tm_secant_tangent;   //!< flag for using secant tangent\n\nprotected:\n\tFEDofList\tm_dofV;\n\tFEDofList\tm_dofSV;\n\tFEDofList\tm_dofSA;\n\tFEDofList\tm_dofR;\n\tFEDofList\tm_dof;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticShellDomainOld.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticShellDomainOld.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEBodyForce.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearSystem.h>\n#include <math.h>\n#include \"FEBioMech.h\"\n\n//-----------------------------------------------------------------------------\nFEElasticShellDomainOld::FEElasticShellDomainOld(FEModel* pfem) : FEShellDomainOld(pfem), FEElasticDomain(pfem), m_dofSU(pfem), m_dofSR(pfem), m_dofR(pfem), m_dof(pfem)\n{\n\tm_pMat = nullptr;\n\t\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofSR.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo do I really need this?\nFEElasticShellDomainOld& FEElasticShellDomainOld::operator = (FEElasticShellDomainOld& d)\n{ \n\tm_Elem = d.m_Elem; \n\tm_pMesh = d.m_pMesh; \n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\n//! get total dof list\nconst FEDofList& FEElasticShellDomainOld::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomainOld::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FESolidMaterial*>(pmat);\n}\n\n//-----------------------------------------------------------------------------\nbool FEElasticShellDomainOld::Init()\n{\n\t// initialize base class\n\tFEShellDomain::Init();\n\n\t// error flag (set true on error)\n\tbool bmerr = false;\n\n\t// check for initially inverted shells\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\tFEShellElementOld& el = ShellElement(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\tdouble J0 = detJ0(el, n);\n\t\t\tif (J0 <= 0)\n\t\t\t{\n\t\t\t\tfeLog(\"**************************** E R R O R ****************************\\n\");\n\t\t\t\tfeLog(\"Negative jacobian detected at integration point %d of element %d\\n\", n+1, el.GetID());\n\t\t\t\tfeLog(\"Jacobian = %lg\\n\", J0);\n\t\t\t\tfeLog(\"Did you use the right node numbering?\\n\");\n\t\t\t\tfeLog(\"Nodes:\");\n\t\t\t\tfor (int l=0; l<el.Nodes(); ++l)\n\t\t\t\t{\n\t\t\t\t\tfeLog(\"%d\", el.m_node[l]+1);\n\t\t\t\t\tif (l+1 != el.Nodes()) feLog(\",\"); else feLog(\"\\n\");\n\t\t\t\t}\n\t\t\t\tfeLog(\"*******************************************************************\\n\\n\");\n\t\t\t\tbmerr = true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn (bmerr == false);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomainOld::Activate()\n{\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofSU[0]);\n\t\t\t\tnode.set_active(m_dofSU[1]);\n\t\t\t\tnode.set_active(m_dofSU[2]);\n\t\t\t}\n\n\t\t\tif (node.HasFlags(FENode::SHELL))\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofSR[0]);\n\t\t\t\tnode.set_active(m_dofSR[1]);\n\t\t\t\tnode.set_active(m_dofSR[2]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FEElasticShellDomainOld::CoBaseVectors0(FEShellElementOld& el, int n, vec3d g[3])\n{\n\tint i;\n\n\tint neln = el.Nodes();\n\n\t// current nodal coordinates and directors\n\tvec3d r[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n\tfor (i = 0; i<neln; ++i)\n\t{\n\t\tFENode& ni = m_pMesh->Node(el.m_node[i]);\n\t\tr[i] = ni.m_r0;\n\t\tD[i] = el.m_D0[i];\n\t}\n\n\tdouble eta = el.gt(n);\n\n\tdouble* Mr = el.Hr(n);\n\tdouble* Ms = el.Hs(n);\n\tdouble* M = el.H(n);\n\n\t// initialize covariant basis vectors\n\tg[0] = g[1] = g[2] = vec3d(0, 0, 0);\n\n\tfor (i = 0; i<neln; ++i)\n\t{\n\t\tg[0] += (r[i] + D[i] * eta / 2)*Mr[i];\n\t\tg[1] += (r[i] + D[i] * eta / 2)*Ms[i];\n\t\tg[2] += D[i] * (M[i] / 2);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FEElasticShellDomainOld::ContraBaseVectors0(FEShellElementOld& el, int n, vec3d gcnt[3])\n{\n\tvec3d gcov[3];\n\tCoBaseVectors0(el, n, gcov);\n\n\tmat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n\t\tgcov[0].y, gcov[1].y, gcov[2].y,\n\t\tgcov[0].z, gcov[1].z, gcov[2].z);\n\tmat3d Ji = J.inverse();\n\n\tgcnt[0] = vec3d(Ji(0, 0), Ji(0, 1), Ji(0, 2));\n\tgcnt[1] = vec3d(Ji(1, 0), Ji(1, 1), Ji(1, 2));\n\tgcnt[2] = vec3d(Ji(2, 0), Ji(2, 1), Ji(2, 2));\n\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElasticShellDomainOld::invjac0(FEShellElementOld& el, double Ji[3][3], int n)\n{\n\tvec3d gcov[3];\n\tCoBaseVectors0(el, n, gcov);\n\n\tmat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n\t\tgcov[0].y, gcov[1].y, gcov[2].y,\n\t\tgcov[0].z, gcov[1].z, gcov[2].z);\n\n\tdouble det = J.det();\n\n\t// make sure the determinant is positive\n\tif (det <= 0) throw NegativeJacobian(el.GetID(), n + 1, det);\n\n\t// calculate the inverse of the jacobian\n\tdouble deti = 1.0 / det;\n\n\tJi[0][0] = deti*(J[1][1] * J[2][2] - J[1][2] * J[2][1]);\n\tJi[1][0] = deti*(J[1][2] * J[2][0] - J[1][0] * J[2][2]);\n\tJi[2][0] = deti*(J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\tJi[0][1] = deti*(J[0][2] * J[2][1] - J[0][1] * J[2][2]);\n\tJi[1][1] = deti*(J[0][0] * J[2][2] - J[0][2] * J[2][0]);\n\tJi[2][1] = deti*(J[0][1] * J[2][0] - J[0][0] * J[2][1]);\n\n\tJi[0][2] = deti*(J[0][1] * J[1][2] - J[1][1] * J[0][2]);\n\tJi[1][2] = deti*(J[0][2] * J[1][0] - J[0][0] * J[1][2]);\n\tJi[2][2] = deti*(J[0][0] * J[1][1] - J[0][1] * J[1][0]);\n\n\treturn det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to reference frame\ndouble FEElasticShellDomainOld::detJ0(FEShellElementOld &el, int n)\n{\n\tvec3d gcov[3];\n\tCoBaseVectors0(el, n, gcov);\n\n\tmat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n\t\tgcov[0].y, gcov[1].y, gcov[2].y,\n\t\tgcov[0].z, gcov[1].z, gcov[2].z);\n\treturn J.det();\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FEElasticShellDomainOld::CoBaseVectors(FEShellElementOld& el, int n, vec3d g[3])\n{\n    int i;\n    \n    int neln = el.Nodes();\n\n    // current nodal coordinates and directors\n    vec3d r[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n    for (i=0; i<neln; ++i)\n    {\n        FENode& ni = m_pMesh->Node(el.m_node[i]);\n        r[i] = ni.m_rt;\n        D[i] = el.m_D0[i] + ni.get_vec3d(m_dofSR[0], m_dofSR[1], m_dofSR[2]);\n    }\n    \n    double eta = el.gt(n);\n    \n    double* Mr = el.Hr(n);\n    double* Ms = el.Hs(n);\n    double* M  = el.H(n);\n    \n    // initialize covariant basis vectors\n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n    \n    for (i=0; i<neln; ++i)\n    {\n        g[0] += (r[i] + D[i]*eta/2)*Mr[i];\n        g[1] += (r[i] + D[i]*eta/2)*Ms[i];\n        g[2] += D[i]*(M[i]/2);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FEElasticShellDomainOld::ContraBaseVectors(FEShellElementOld& el, int n, vec3d gcnt[3])\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, n, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n\n}\n\n//-----------------------------------------------------------------------------\n// jacobian with respect to current frame\ndouble FEElasticShellDomainOld::detJ(FEShellElementOld& el, int n)\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, n, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    return J.det();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElasticShellDomainOld::defgrad(FEShellElementOld& el, mat3d& F, int n)\n{\n    vec3d gcov[3], Gcnt[3];\n    CoBaseVectors(el, n, gcov);\n    ContraBaseVectors0(el, n, Gcnt);\n    \n    F = (gcov[0] & Gcnt[0]) + (gcov[1] & Gcnt[1]) + (gcov[2] & Gcnt[2]);\n\tdouble J = F.det();\n\tif (J <= 0) throw NegativeJacobian(el.GetID(), n, J, &el);\n\n\treturn J;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the current frame at \n//! integration point n. The inverse jacobian is return in Ji. The return value\n//! is the determinant of the jacobian (not the inverse!)\ndouble FEElasticShellDomainOld::invjact(FEShellElementOld& el, double Ji[3][3], int n)\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, n, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    \n    double det = J.det();\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n    \n    // calculate the inverse of the jacobian\n    double deti = 1.0 / det;\n    \n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n// Calculates the forces due to the stress\nvoid FEElasticShellDomainOld::InternalForces(FEGlobalVector& R)\n{\n\t// element force vector\n\tvector<double> fe;\n\n\tvector<int> lm;\n\n\tint NS = (int)m_Elem.size();\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\t// get the element\n\t\tFEShellElementOld& el = m_Elem[i];\n\n\t\t// create the element force vector and initialize to zero\n\t\tint ndof = 6*el.Nodes();\n\t\tfe.assign(ndof, 0);\n\n\t\t// calculate element's internal force\n\t\tElementInternalForce(el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble the residual\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for shell elements\n//! Note that we use a one-point gauss integration rule for the thickness\n//! integration. This will integrate linear functions exactly.\n\nvoid FEElasticShellDomainOld::ElementInternalForce(FEShellElementOld& el, vector<double>& fe)\n{\n\tint i, n;\n\n\t// jacobian matrix determinant\n\tdouble detJt;\n\n\tconst double* Mr, *Ms, *M;\n\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tdouble*\tgw = el.GaussWeights();\n\tdouble eta;\n    \n    vec3d gcnt[3];\n\n\t// repeat for all integration points\n\tfor (n=0; n<nint; ++n)\n\t{\n\t\tFEElasticMaterialPoint& pt = *(el.GetMaterialPoint(n)->ExtractData<FEElasticMaterialPoint>());\n\n\t\t// calculate the jacobian\n        detJt = detJ(el, n);\n\n\t\tdetJt *= gw[n];\n\n\t\t// get the stress vector for this integration point\n\t\tmat3ds& s = pt.m_s;\n\n\t\teta = el.gt(n);\n\n\t\tMr = el.Hr(n);\n\t\tMs = el.Hs(n);\n\t\tM  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n\n\t\tfor (i=0; i<neln; ++i)\n\t\t{\n            vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            vec3d gradP = (gradM*eta + gcnt[2]*M[i])/2;\n            vec3d fu = s*gradM;\n            vec3d fd = s*gradP;\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[6*i  ] -= fu.x*detJt;\n\t\t\tfe[6*i+1] -= fu.y*detJt;\n\t\t\tfe[6*i+2] -= fu.z*detJt;\n\n\t\t\tfe[6*i+3] -= fd.x*detJt;\n\t\t\tfe[6*i+4] -= fd.y*detJt;\n\t\t\tfe[6*i+5] -= fd.z*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomainOld::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n\t// element force vector\n\tvector<double> fe;\n\n\tvector<int> lm;\n\n\tint NS = (int)m_Elem.size();\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\t// get the element\n\t\tFEShellElementOld& el = m_Elem[i];\n\n\t\t// create the element force vector and initialize to zero\n\t\tint ndof = 6*el.Nodes();\n\t\tfe.assign(ndof, 0);\n\n\t\t// apply body forces to shells\n\t\tElementBodyForce(BF, el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble the residual\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element body forces for shells\n\nvoid FEElasticShellDomainOld::ElementBodyForce(FEBodyForce& BF, FEShellElementOld& el, vector<double>& fe)\n{\n\t// integration weights\n\tdouble* gw = el.GaussWeights();\n    double eta;\n\tdouble *M, detJt;\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n        \n        // calculate density in current configuration\n        double dens = m_pMat->Density(mp)/pt.m_J;\n        \n        // calculate the jacobian\n        detJt = detJ(el, n)*gw[n];\n        \n\t\tM  = el.H(n);\n\t\teta = el.gt(n);\n\n\t\t// get the force\n\t\tvec3d f = BF.force(mp);\n\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n            vec3d fu = f*(dens*M[i]);\n            vec3d fd = fu*(eta/2);\n            \n\t\t\tfe[6*i  ] -= fu.x*detJt;\n\t\t\tfe[6*i+1] -= fu.y*detJt;\n\t\t\tfe[6*i+2] -= fu.z*detJt;\n\n\t\t\tfe[6*i+3] -= fd.x*detJt;\n\t\t\tfe[6*i+4] -= fd.y*detJt;\n\t\t\tfe[6*i+5] -= fd.z*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEElasticShellDomainOld::StiffnessMatrix(FELinearSystem& LS)\n{\n\tint NS = (int)m_Elem.size();\n\tfor (int iel=0; iel<NS; ++iel)\n\t{\n\t\tFEShellElement& el = m_Elem[iel];\n\n\t\t// get the elements material\n\t\tFEMaterial* pmat = m_pMat;\n\n\t\t// create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 6*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\n\t\t// calculate the element stiffness matrix\n\t\tElementStiffness(iel, ke);\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the shell element stiffness matrix\n\nvoid FEElasticShellDomainOld::ElementStiffness(int iel, matrix& ke)\n{\n\tFEShellElementOld& el = ShellElement(iel);\n\n\tint i, i6, j, j6, n;\n\n\t// Get the current element's data\n\tconst int nint = el.GaussPoints();\n\tconst int neln = el.Nodes();\n\n    const double* Mr, *Ms, *M;\n    vec3d gradM[FEElement::MAX_NODES], gradP[FEElement::MAX_NODES];\n\n    // jacobian matrix determinant\n    double detJt;\n\t\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n\n\t// calculate element stiffness matrix\n\tke.zero();\n\tfor (n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress and elasticity for this integration point\n        mat3ds s = pt.m_s;\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n\t\t// ------------ constitutive component --------------\n\n\t\t// setup the material point\n\n\t\tfor (i=0; i<neln; ++i)\n\t\t{\n            gradM[i] = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradP[i] = (gradM[i]*eta + gcnt[2]*M[i])/2;\n\t\t}\n\n\t\tfor (i=0, i6=0; i<neln; ++i, i6 += 6)\n\t\t{\n\t\t\tfor (j=0, j6 = 0; j<neln; ++j, j6 += 6)\n\t\t\t{\n                mat3d Kuu = vdotTdotv(gradM[i], C, gradM[j]);\n                mat3d Kud = vdotTdotv(gradM[i], C, gradP[j]);\n                mat3d Kdu = vdotTdotv(gradP[i], C, gradM[j]);\n                mat3d Kdd = vdotTdotv(gradP[i], C, gradP[j]);\n                \n\t\t\t\tke[i6  ][j6  ] += Kuu(0,0)*detJt; ke[i6  ][j6+1] += Kuu(0,1)*detJt; ke[i6  ][j6+2] += Kuu(0,2)*detJt;\n                ke[i6+1][j6  ] += Kuu(1,0)*detJt; ke[i6+1][j6+1] += Kuu(1,1)*detJt; ke[i6+1][j6+2] += Kuu(1,2)*detJt;\n                ke[i6+2][j6  ] += Kuu(2,0)*detJt; ke[i6+2][j6+1] += Kuu(2,1)*detJt; ke[i6+2][j6+2] += Kuu(2,2)*detJt;\n\n                ke[i6  ][j6+3] += Kud(0,0)*detJt; ke[i6  ][j6+4] += Kud(0,1)*detJt; ke[i6  ][j6+5] += Kud(0,2)*detJt;\n\t\t\t\tke[i6+1][j6+3] += Kud(1,0)*detJt; ke[i6+1][j6+4] += Kud(1,1)*detJt; ke[i6+1][j6+5] += Kud(1,2)*detJt;\n\t\t\t\tke[i6+2][j6+3] += Kud(2,0)*detJt; ke[i6+2][j6+4] += Kud(2,1)*detJt; ke[i6+2][j6+5] += Kud(2,2)*detJt;\n\n\t\t\t\tke[i6+3][j6  ] += Kdu(0,0)*detJt; ke[i6+3][j6+1] += Kdu(0,1)*detJt; ke[i6+3][j6+2] += Kdu(0,2)*detJt;\n                ke[i6+4][j6  ] += Kdu(1,0)*detJt; ke[i6+4][j6+1] += Kdu(1,1)*detJt; ke[i6+4][j6+2] += Kdu(1,2)*detJt;\n                ke[i6+5][j6  ] += Kdu(2,0)*detJt; ke[i6+5][j6+1] += Kdu(2,1)*detJt; ke[i6+5][j6+2] += Kdu(2,2)*detJt;\n                \n\t\t\t\tke[i6+3][j6+3] += Kdd(0,0)*detJt; ke[i6+3][j6+4] += Kdd(0,1)*detJt; ke[i6+3][j6+5] += Kdd(0,2)*detJt;\n\t\t\t\tke[i6+4][j6+3] += Kdd(1,0)*detJt; ke[i6+4][j6+4] += Kdd(1,1)*detJt; ke[i6+4][j6+5] += Kdd(1,2)*detJt;\n\t\t\t\tke[i6+5][j6+3] += Kdd(2,0)*detJt; ke[i6+5][j6+4] += Kdd(2,1)*detJt; ke[i6+5][j6+5] += Kdd(2,2)*detJt;\n\t\t\t}\n\t\t}\n\n\t\t// ------------ initial stress component --------------\n\t\n\t\tfor (i=0; i<neln; ++i)\n\t\t\tfor (j=0; j<neln; ++j)\n\t\t\t{\n                double Kuu = gradM[i]*(s*gradM[j]);\n                double Kud = gradM[i]*(s*gradP[j]);\n                double Kdu = gradP[i]*(s*gradM[j]);\n                double Kdd = gradP[i]*(s*gradP[j]);\n                \n\t\t\t\t// the u-u component\n\t\t\t\tke[6*i  ][6*j  ] += Kuu*detJt;\n\t\t\t\tke[6*i+1][6*j+1] += Kuu*detJt;\n\t\t\t\tke[6*i+2][6*j+2] += Kuu*detJt;\n\n\t\t\t\t// the u-d component\n\t\t\t\tke[6*i  ][6*j+3] += Kud*detJt;\n\t\t\t\tke[6*i+1][6*j+4] += Kud*detJt;\n\t\t\t\tke[6*i+2][6*j+5] += Kud*detJt;\n\n\t\t\t\t// the d-u component\n\t\t\t\tke[6*i+3][6*j  ] += Kdu*detJt;\n\t\t\t\tke[6*i+4][6*j+1] += Kdu*detJt;\n\t\t\t\tke[6*i+5][6*j+2] += Kdu*detJt;\n\n\t\t\t\t// the d-d component\n\t\t\t\tke[6*i+3][6*j+3] += Kdd*detJt;\n\t\t\t\tke[6*i+4][6*j+4] += Kdd*detJt;\n\t\t\t\tke[6*i+5][6*j+5] += Kdd*detJt;\n\t\t\t}\n\n\t} // end loop over gauss-points\n\n}\n\n\n//-----------------------------------------------------------------------------\n//! Calculates body forces for shells\n\nvoid FEElasticShellDomainOld::ElementBodyForce(FEModel& fem, FEShellElementOld& el, vector<double>& fe)\n{\n\tint NF = fem.ModelLoads();\n\tfor (int nf = 0; nf < NF; ++nf)\n\t{\n\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(nf));\n\t\tif (pbf)\n\t\t{\n            // integration weights\n            double* gw = el.GaussWeights();\n            double eta;\n            double *M, detJt;\n            \n            // loop over integration points\n            int nint = el.GaussPoints();\n            int neln = el.Nodes();\n            \n            for (int n=0; n<nint; ++n)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n                FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n                \n                // calculate density in current configuration\n                double dens = m_pMat->Density(mp)/pt.m_J;\n                \n                // calculate the jacobian\n                detJt = detJ(el, n)*gw[n];\n                \n                M  = el.H(n);\n                eta = el.gt(n);\n                \n                // get the force\n                vec3d f = pbf->force(mp);\n                \n                for (int i=0; i<neln; ++i)\n                {\n                    vec3d fu = f*(dens*M[i]);\n                    vec3d fd = fu*(eta/2);\n                    \n                    fe[6*i  ] -= fu.x*detJt;\n                    fe[6*i+1] -= fu.y*detJt;\n                    fe[6*i+2] -= fu.z*detJt;\n                    \n                    fe[6*i+3] -= fd.x*detJt;\n                    fe[6*i+4] -= fd.y*detJt;\n                    fe[6*i+5] -= fd.z*detJt;\n                }\n            }\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticShellDomainOld::Update(const FETimeInfo& tp)\n{\n\tFEMesh& mesh = *GetMesh();\n\tvec3d r0[FEElement::MAX_NODES], rt[FEElement::MAX_NODES];\n\n\tint n;\n\tfor (int i=0; i<(int) m_Elem.size(); ++i)\n\t{\n\t\t// get the solid element\n\t\tFEShellElementOld& el = m_Elem[i];\n\n\t\t// get the number of integration points\n\t\tint nint = el.GaussPoints();\n\n\t\t// number of nodes\n\t\tint neln = el.Nodes();\n\n\t\t// nodal coordinates\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tr0[j] = mesh.Node(el.m_node[j]).m_r0;\n\t\t\trt[j] = mesh.Node(el.m_node[j]).m_rt;\n\t\t}\n\n\t\t// update shell thickness\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tFENode& nj = mesh.Node(el.m_node[j]);\n\t\t\tvec3d D = el.m_D0[j] + nj.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\t\t\tdouble h = D.norm();\n\n\t\t\tel.m_ht[j] = h;\n\t\t}\n\n\t\t// loop over the integration points and calculate\n\t\t// the stress at the integration point\n\t\tfor (n=0; n<nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n\t\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t\t// material point coordinates\n\t\t\t// TODO: I'm not entirly happy with this solution\n\t\t\t//\t\t since the material point coordinates are used by most materials.\n\t\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\t\tmp.m_rt = el.Evaluate(rt, n);\n\n\t\t\t// get the deformation gradient and determinant\n\t\t\tpt.m_J = defgrad(el, pt.m_F, n);\n\n\t\t\t// calculate the stress at this material point\n\t\t\tpt.m_s = m_pMat->Stress(mp);\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n//! Unpack the element. That is, copy element data in traits structure\n//! Note that for the shell elements the lm order is different compared\n//! to the solid element ordering. This is because for shell elements the\n//! nodes have six degrees of freedom each, where for solids they only\n//! have 3 dofs.\nvoid FEElasticShellDomainOld::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N*9);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& node = m_pMesh->Node(el.m_node[i]);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[6*i  ] = id[m_dofSU[0]];\n\t\tlm[6*i+1] = id[m_dofSU[1]];\n\t\tlm[6*i+2] = id[m_dofSU[2]];\n\n\t\t// next the rotational dofs\n\t\tlm[6*i+3] = id[m_dofSR[0]];\n\t\tlm[6*i+4] = id[m_dofSR[1]];\n\t\tlm[6*i+5] = id[m_dofSR[2]];\n\n\t\t// rigid rotational dofs\n\t\tlm[6*N + 3*i  ] = id[m_dofR[0]];\n\t\tlm[6*N + 3*i+1] = id[m_dofR[1]];\n\t\tlm[6*N + 3*i+2] = id[m_dofR[2]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticShellDomainOld.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEShellDomain.h>\n#include <FECore/FEDofList.h>\n#include \"FEElasticDomain.h\"\n#include \"FESolidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain described by 3D shell elements\nclass FEElasticShellDomainOld : public FEShellDomainOld, public FEElasticDomain\n{\npublic:\n\tFEElasticShellDomainOld(FEModel* pfem);\n\n\t//! \\todo do I really need this?\n\tFEElasticShellDomainOld& operator = (FEElasticShellDomainOld& d);\n\n\t//! Initialize domain\n\tbool Init() override;\n\n\t//! Activate the domain\n\tvoid Activate() override;\n\n\t//! Unpack shell element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! get total dof list\n\tconst FEDofList& GetDOFList() const override;\n\npublic: // overrides from FEElasticDomain\n\n\t//! calculates the residual\n//\tvoid Residual(FESolver* psolver, vector<double>& R);\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! Calculates inertial forces for dynamic problems | todo implement this (removed assert DSR)\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override { }\n\n\t//! calculate body force\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\n\t// update stresses\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t// inertial stiffness \\todo implement this (removed assert DSR)\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override { }\n\n\t// body force stiffness \\todo implement this (removed assert DSR)\n\tvoid BodyForceStiffness  (FELinearSystem& LS, FEBodyForce& bf) override { }\n\npublic:\n\t//! calculates covariant basis vectors at an integration point\n\tvoid CoBaseVectors0(FEShellElementOld& el, int n, vec3d g[3]);\n\n\t//! calculates contravariant basis vectors at an integration point\n\tvoid ContraBaseVectors0(FEShellElementOld& el, int n, vec3d g[3]);\n\n\t// inverse jacobian with respect to reference frame\n\tdouble invjac0(FEShellElementOld& el, double J[3][3], int n);\n\n\t// jacobian with respect to reference frame\n\tdouble detJ0(FEShellElementOld& el, int n);\n\n    //! calculates covariant basis vectors at an integration point\n\tvoid CoBaseVectors(FEShellElementOld& el, int n, vec3d g[3]);\n    \n    //! calculates contravariant basis vectors at an integration point\n\tvoid ContraBaseVectors(FEShellElementOld& el, int n, vec3d g[3]);\n    \n    // jacobian with respect to current frame\n\tdouble detJ(FEShellElementOld& el, int n);\n    \n\t// calculate deformation gradient\n\tdouble defgrad(FEShellElementOld& el, mat3d& F, int n);\n\n\t// inverse jacobian with respect to current frame\n\tdouble invjact(FEShellElementOld& el, double J[3][3], int n);\n \npublic:\n\n\t// --- S T I F F N E S S --- \n\n\t//! calculates the shell element stiffness matrix\n\tvoid ElementStiffness(int iel, matrix& ke);\n\n\t// --- R E S I D U A L ---\n\n\t//! Calculates the internal stress vector for shell elements\n\tvoid ElementInternalForce(FEShellElementOld& el, vector<double>& fe);\n\n\t//! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEModel& fem, FEShellElementOld& el, vector<double>& fe);\n\n\t//! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEBodyForce& BF, FEShellElementOld& el, vector<double>& fe);\n\nprotected:\n\tFESolidMaterial*\tm_pMat;\n\tFEDofList\t\t\tm_dofSU;\t// shell displacement dofs\n\tFEDofList\t\t\tm_dofSR;\t// shell rotation dofs\n\tFEDofList\t\t\tm_dofR;\t\t// rigid rotation dofs\n\tFEDofList\t\t\tm_dof;\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEElasticSolidDomain.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEBodyForce.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/sys.h>\n#include \"FEBioMech.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FEResidualVector.h\"\n\n//-----------------------------------------------------------------------------\n//! constructor\n//! Some derived classes will pass 0 to the pmat, since the pmat variable will be\n//! to initialize another material. These derived classes will set the m_pMat variable as well.\nFEElasticSolidDomain::FEElasticSolidDomain(FEModel* pfem) : FESolidDomain(pfem), FEElasticDomain(pfem), m_dofU(pfem), m_dofR(pfem), m_dofSU(pfem), m_dofV(pfem), m_dofSV(pfem), m_dofSA(pfem), m_dof(pfem)\n{\n\tm_pMat = 0;\n    m_alphaf = m_beta = 1;\n    m_alpham = 2;\n\tm_update_dynamic = true; // default for backward compatibility\n\n\tm_secant_stress = false;\n\tm_secant_tangent = false;\n\n\t// TODO: Can this be done in Init, since  there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t\tm_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n\t\tm_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// get the total dof list\nconst FEDofList& FEElasticSolidDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\n// \\todo I don't think this is being used\nFEElasticSolidDomain& FEElasticSolidDomain::operator = (FEElasticSolidDomain& d)\n{ \n\tm_Elem = d.m_Elem; \n\tm_pMesh = d.m_pMesh; \n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\n//! Set flag for update for dynamic quantities\nvoid FEElasticSolidDomain::SetDynamicUpdateFlag(bool b)\n{\n\tm_update_dynamic = b;\n}\n\n//-----------------------------------------------------------------------------\n//! Assign material\nvoid FEElasticSolidDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tif (pmat)\n\t{\n\t\tm_pMat = dynamic_cast<FESolidMaterial*>(pmat);\n\t\tassert(m_pMat);\n\t}\n\telse m_pMat = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::Activate()\n{\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEElasticSolidDomain::Serialize(DumpStream& ar)\n{\n\t//erialize the base class, which instantiates the elements\n\tFESolidDomain::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\t// serialize class variables\n\tar & m_alphaf;\n\tar & m_alpham;\n\tar & m_beta;\n\tar & m_update_dynamic;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEElasticSolidDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    m_alphaf = timeInfo.alphaf;\n    m_alpham = timeInfo.alpham;\n    m_beta = timeInfo.beta;\n\n#pragma omp parallel for\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint n = el.GaussPoints();\n\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tpt.m_Wp = pt.m_Wt;\n\n\t\t\t\tmp.Update(timeInfo);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::InternalForces(FEGlobalVector& R)\n{\n\tint NE = Elements();\n\t#pragma omp parallel for shared (NE)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\tif (el.isActive()) {\n\t\t\t// element force vector\n\t\t\tvector<double> fe;\n\t\t\tvector<int> lm;\n\n\t\t\t// get the element force vector and initialize it to zero\n\t\t\tint ndof = 3 * el.Nodes();\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// calculate internal force vector\n\t\t\tElementInternalForce(el, fe);\n\n\t\t\t// get the element's LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble element 'fe'-vector into global R vector\n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEElasticSolidDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n\t// jacobian matrix, inverse jacobian matrix and determinants\n\tdouble Ji[3][3];\n\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tdouble*\tgw = el.GaussWeights();\n\n\t// repeat for all integration points\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// calculate the jacobian\n\t\tdouble detJt = (m_update_dynamic ? invjact(el, Ji, n, m_alphaf) : invjact(el, Ji, n));\n\n\t\tdetJt *= gw[n];\n\n\t\t// get the stress vector for this integration point\n        const mat3ds& s = pt.m_s;\n\n\t\tconst double* Gr = el.Gr(n);\n\t\tconst double* Gs = el.Gs(n);\n\t\tconst double* Gt = el.Gt(n);\n\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tdouble Gx = Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i];\n\t\t\tdouble Gy = Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i];\n\t\t\tdouble Gz = Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i];\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[3*i  ] -= ( Gx*s.xx() +\n\t\t\t\t           Gy*s.xy() +\n\t\t\t\t\t       Gz*s.xz() )*detJt;\n\n\t\t\tfe[3*i+1] -= ( Gy*s.yy() +\n\t\t\t\t           Gx*s.xy() +\n\t\t\t\t\t       Gz*s.yz() )*detJt;\n\n\t\t\tfe[3*i+2] -= ( Gz*s.zz() +\n\t\t\t\t           Gy*s.yz() +\n\t\t\t\t\t       Gx*s.xz() )*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n\t// define some parameters that will be passed to lambda\n\tFEBodyForce* bodyForce = &BF;\n\n\t// TODO: a remaining issue here is that dofU does not consider the shell displacement\n\t// dofs for interface nodes (see UnpackLM). Is that an issue?\n\n\t// evaluate the residual contribution\n\tLoadVector(R, m_dofU, [=](FEMaterialPoint& mp, int node_a, std::vector<double>& fa) {\n\n\t\t// evaluate density\n\t\tdouble density = m_pMat->Density(mp);\n\n\t\t// get the force\n\t\tvec3d f = bodyForce->force(mp);\n\n\t\t// get element shape functions\n\t\tdouble* H = mp.m_shape;\n\n\t\t// get the initial Jacobian\n\t\tdouble J0 = mp.m_J0;\n\n\t\t// set integrand\n\t\tfa[0] = -H[node_a] * density* f.x * J0;\n\t\tfa[1] = -H[node_a] * density* f.y * J0;\n\t\tfa[2] = -H[node_a] * density* f.z * J0;\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element's geometrical stiffness component for integration point n\nvoid FEElasticSolidDomain::ElementGeometricalStiffness(FESolidElement &el, matrix &ke)\n{\n\t// spatial derivatives of shape functions\n\tvec3d G[FEElement::MAX_NODES];\n\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// calculate geometrical element stiffness matrix\n\tint neln = el.Nodes();\n\tint nint = el.GaussPoints();\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\t// calculate shape function gradients and jacobian\n\t\tdouble w = ShapeGradient(el, n, G, m_alphaf)*gw[n]*m_alphaf;\n\n\t\t// get the material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// element's Cauchy-stress tensor at gauss point n\n\t\tmat3ds& s = pt.m_s;\n\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t{\n\t\t\t\tdouble kab = (G[i]*(s * G[j]))*w;\n\n\t\t\t\tke[3*i  ][3*j  ] += kab;\n\t\t\t\tke[3*i+1][3*j+1] += kab;\n\t\t\t\tke[3*i+2][3*j+2] += kab;\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEElasticSolidDomain::ElementMaterialStiffness(FESolidElement &el, matrix &ke)\n{\n\t// Get the current element's data\n\tconst int nint = el.GaussPoints();\n\tconst int neln = el.Nodes();\n\n\t// global derivatives of shape functions\n\tvec3d G[FEElement::MAX_NODES];\n\n\tdouble Gxi, Gyi, Gzi;\n\tdouble Gxj, Gyj, Gzj;\n\n\t// The 'D' matrix\n\tdouble D[6][6] = {0};\t// The 'D' matrix\n\n\t// The 'D*BL' matrix\n\tdouble DBL[6][3];\n\n\t// jacobian\n\tdouble detJt;\n\t\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// calculate element stiffness matrix\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian and shape function gradients\n\t\tdetJt = ShapeGradient(el, n, G, m_alphaf)*gw[n]*m_alphaf;\n\n\t\t// setup the material point\n\t\t// NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t// get the 'D' matrix\n//\t\ttens4ds C = m_pMat->Tangent(mp);\n        tens4dmm C = (m_secant_tangent ? m_pMat->SecantTangent(mp) : m_pMat->SolidTangent(mp));\n\t\tC.extract(D);\n\n\t\t// we only calculate the upper triangular part\n\t\t// since ke is symmetric. The other part is\n\t\t// determined below using this symmetry.\n\t\tfor (int i=0, i3=0; i<neln; ++i, i3 += 3)\n\t\t{\n\t\t\tGxi = G[i].x;\n\t\t\tGyi = G[i].y;\n\t\t\tGzi = G[i].z;\n\n\t\t\tfor (int j=0, j3 = 0; j<neln; ++j, j3 += 3)\n\t\t\t{\n\t\t\t\tGxj = G[j].x;\n\t\t\t\tGyj = G[j].y;\n\t\t\t\tGzj = G[j].z;\n\n\t\t\t\t// calculate D*BL matrices\n\t\t\t\tDBL[0][0] = (D[0][0]*Gxj+D[0][3]*Gyj+D[0][5]*Gzj);\n\t\t\t\tDBL[0][1] = (D[0][1]*Gyj+D[0][3]*Gxj+D[0][4]*Gzj);\n\t\t\t\tDBL[0][2] = (D[0][2]*Gzj+D[0][4]*Gyj+D[0][5]*Gxj);\n\n\t\t\t\tDBL[1][0] = (D[1][0]*Gxj+D[1][3]*Gyj+D[1][5]*Gzj);\n\t\t\t\tDBL[1][1] = (D[1][1]*Gyj+D[1][3]*Gxj+D[1][4]*Gzj);\n\t\t\t\tDBL[1][2] = (D[1][2]*Gzj+D[1][4]*Gyj+D[1][5]*Gxj);\n\n\t\t\t\tDBL[2][0] = (D[2][0]*Gxj+D[2][3]*Gyj+D[2][5]*Gzj);\n\t\t\t\tDBL[2][1] = (D[2][1]*Gyj+D[2][3]*Gxj+D[2][4]*Gzj);\n\t\t\t\tDBL[2][2] = (D[2][2]*Gzj+D[2][4]*Gyj+D[2][5]*Gxj);\n\n\t\t\t\tDBL[3][0] = (D[3][0]*Gxj+D[3][3]*Gyj+D[3][5]*Gzj);\n\t\t\t\tDBL[3][1] = (D[3][1]*Gyj+D[3][3]*Gxj+D[3][4]*Gzj);\n\t\t\t\tDBL[3][2] = (D[3][2]*Gzj+D[3][4]*Gyj+D[3][5]*Gxj);\n\n\t\t\t\tDBL[4][0] = (D[4][0]*Gxj+D[4][3]*Gyj+D[4][5]*Gzj);\n\t\t\t\tDBL[4][1] = (D[4][1]*Gyj+D[4][3]*Gxj+D[4][4]*Gzj);\n\t\t\t\tDBL[4][2] = (D[4][2]*Gzj+D[4][4]*Gyj+D[4][5]*Gxj);\n\n\t\t\t\tDBL[5][0] = (D[5][0]*Gxj+D[5][3]*Gyj+D[5][5]*Gzj);\n\t\t\t\tDBL[5][1] = (D[5][1]*Gyj+D[5][3]*Gxj+D[5][4]*Gzj);\n\t\t\t\tDBL[5][2] = (D[5][2]*Gzj+D[5][4]*Gyj+D[5][5]*Gxj);\n\n\t\t\t\tke[i3  ][j3  ] += (Gxi*DBL[0][0] + Gyi*DBL[3][0] + Gzi*DBL[5][0] )*detJt;\n\t\t\t\tke[i3  ][j3+1] += (Gxi*DBL[0][1] + Gyi*DBL[3][1] + Gzi*DBL[5][1] )*detJt;\n\t\t\t\tke[i3  ][j3+2] += (Gxi*DBL[0][2] + Gyi*DBL[3][2] + Gzi*DBL[5][2] )*detJt;\n\n\t\t\t\tke[i3+1][j3  ] += (Gyi*DBL[1][0] + Gxi*DBL[3][0] + Gzi*DBL[4][0] )*detJt;\n\t\t\t\tke[i3+1][j3+1] += (Gyi*DBL[1][1] + Gxi*DBL[3][1] + Gzi*DBL[4][1] )*detJt;\n\t\t\t\tke[i3+1][j3+2] += (Gyi*DBL[1][2] + Gxi*DBL[3][2] + Gzi*DBL[4][2] )*detJt;\n\n\t\t\t\tke[i3+2][j3  ] += (Gzi*DBL[2][0] + Gyi*DBL[4][0] + Gxi*DBL[5][0] )*detJt;\n\t\t\t\tke[i3+2][j3+1] += (Gzi*DBL[2][1] + Gyi*DBL[4][1] + Gxi*DBL[5][1] )*detJt;\n\t\t\t\tke[i3+2][j3+2] += (Gzi*DBL[2][2] + Gyi*DBL[4][2] + Gxi*DBL[5][2] )*detJt;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// repeat over all solid elements\n\tint NE = Elements();\n\t\n\t#pragma omp parallel for shared (NE)\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\tif (el.isActive()) {\n\n\t\t\t// get the element's LM vector\n\t\t\tvector<int> lm;\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// element stiffness matrix\n\t\t\tFEElementMatrix ke(el, lm);\n\n\t\t\t// create the element's stiffness matrix\n\t\t\tint ndof = 3 * el.Nodes();\n\t\t\tke.resize(ndof, ndof);\n\t\t\tke.zero();\n\n\t\t\t// calculate geometrical stiffness\n\t\t\tElementGeometricalStiffness(el, ke);\n\n\t\t\t// calculate material stiffness\n\t\t\tElementMaterialStiffness(el, ke);\n\n/*\t\t\t// assign symmetic parts\n\t\t\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t\t\t// grabs elements from the upper diagonal matrix?\n\t\t\tfor (int i = 0; i < ndof; ++i)\n\t\t\t\tfor (int j = i + 1; j < ndof; ++j)\n\t\t\t\t\tke[j][i] = ke[i][j];\n*/\n\t\t\t// assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n\t// TODO: a remaining issue here is that dofU does not consider the shell displacement\n\t// dofs for interface nodes (see UnpackLM). Is that an issue?\n\n\t// evaluate body force stiffness\n\tLoadStiffness(LS, m_dofU, m_dofU, [=](FEMaterialPoint& mp, int node_a, int node_b, matrix& Kab) {\n\n\t\t// density\n\t\tdouble density = m_pMat->Density(mp);\n\n\t\t// shape functions\n\t\tdouble* H = mp.m_shape;\n\n\t\t// Jacobian\n\t\tdouble J0 = mp.m_J0;\n\n\t\t// mass\n\t\tdouble kab = scale *density*H[node_a] * H[node_b] * J0;\n\t\tKab.zero();\n\t\tKab[0][0] = kab;\n\t\tKab[1][1] = kab;\n\t\tKab[2][2] = kab;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n\t// define some parameters that will be passed to lambda\n\tFESolidMaterial* mat = m_pMat;\n\tFEBodyForce* bodyForce = &bf;\n\n\t// TODO: a remaining issue here is that dofU does not consider the shell displacement\n\t// dofs for interface nodes (see UnpackLM). Is that an issue?\n\n\t// evaluate body force stiffness\n\tLoadStiffness(LS, m_dofU, m_dofU, [=](FEMaterialPoint& mp, int node_a, int node_b, matrix& Kab) {\n\n\t\t// loop over integration points\n\t\tdouble detJ = mp.m_J0 * m_alphaf;\n\n\t\t// density\n\t\tdouble dens_n = mat->Density(mp);\n\t\t\t\n\t\t// get the stiffness\n\t\tmat3d K = bodyForce->stiffness(mp);\n\n\t\t// shape functions\n\t\tdouble* H = mp.m_shape;\n\n\t\t// put it together\n\t\tKab = K*(-H[node_a] * H[node_b] * dens_n*detJ);\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the element stiffness matrix. It calls the material\n//! stiffness function, the geometrical stiffness function and, if necessary, the\n//! dilatational stiffness function. Note that these three functions only calculate\n//! the upper diagonal matrix due to the symmetry of the element stiffness matrix\n//! The last section of this function fills the rest of the element stiffness matrix.\n\nvoid FEElasticSolidDomain::ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke)\n{\n\tFESolidElement& el = Element(iel);\n\n\t// calculate material stiffness (i.e. constitutive component)\n\tElementMaterialStiffness(el, ke);\n\n\t// calculate geometrical stiffness\n\tElementGeometricalStiffness(el, ke);\n\n\t// assign symmetic parts\n\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t// grabs elements from the upper diagonal matrix?\n\tint ndof = 3*el.Nodes();\n\tint i, j;\n\tfor (i=0; i<ndof; ++i)\n\t\tfor (j=i+1; j<ndof; ++j)\n\t\t\tke[j][i] = ke[i][j];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::Update(const FETimeInfo& tp)\n{\n\tbool berr = false;\n\tint NE = Elements();\n\t#pragma omp parallel for shared(NE, berr)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\ttry\n\t\t{\n\t\t\tFESolidElement& el = Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tUpdateElementStress(i, tp);\n\t\t\t}\n\t\t}\n\t\tcatch (NegativeJacobian e)\n\t\t{\n\t\t\t#pragma omp critical\n\t\t\t{\n\t\t\t\t// reset the logfile mode\n\t\t\t\tberr = true;\n\t\t\t\tif (e.DoOutput()) feLogError(e.what());\n\t\t\t}\n\t\t}\n\t}\n\n\tif (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\n//! \\todo Remove the remodeling solid stuff\nvoid FEElasticSolidDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double dt =tp.timeIncrement;\n    \n\t// get the solid element\n\tFESolidElement& el = m_Elem[iel];\n\n\t// get the number of integration points\n\tint nint = el.GaussPoints();\n\n\t// number of nodes\n\tint neln = el.Nodes();\n\n\t// nodal coordinates\n    const int NELN = FEElement::MAX_NODES;\n    vec3d r[NELN], v[NELN], a[NELN];\n\tGetCurrentNodalCoordinates(el, r, m_alphaf);\n\n\t// update dynamic quantities\n\tif (m_update_dynamic)\n\t{\n\t\tfor (int j = 0; j<neln; ++j)\n\t\t{\n\t\t\tFENode& node = m_pMesh->Node(el.m_node[j]);\n\t\t\tv[j] = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2])*m_alphaf + node.m_vp*(1 - m_alphaf);\n\t\t\ta[j] = node.m_at*m_alpham + node.m_ap*(1 - m_alpham);\n\t\t}\n\t}\n\n\t// loop over the integration points and calculate\n\t// the stress at the integration point\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// material point coordinates\n\t\tmp.m_rt = el.Evaluate(r, n);\n\n\t\t// get the deformation gradient and determinant at intermediate time\n        double Jt;\n        mat3d Ft, Fp;\n        Jt = defgrad(el, Ft, n);\n        defgradp(el, Fp, n);\n\n\t\tif (m_alphaf == 1.0)\n\t\t{\n\t\t\tpt.m_F = Ft;\n            pt.m_J = Jt;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpt.m_F = Ft*m_alphaf + Fp*(1-m_alphaf);\n            pt.m_J = pt.m_F.det();\n\t\t}\n\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (Ft - Fp)*Fi / dt;\n\t\tif (m_update_dynamic)\n\t\t{\n\t\t\tpt.m_v = el.Evaluate(v, n);\n\t\t\tpt.m_a = el.Evaluate(a, n);\n\t\t}\n\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, tp);\n        \n\t\t// calculate the stress at this material point\n//\t\tpt.m_s = m_pMat->Stress(mp);\n\t\tpt.m_s = (m_secant_stress ? m_pMat->SecantStress(mp) : m_pMat->Stress(mp));\n        \n        // adjust stress for strain energy conservation\n\t\t// (Apply only for mid-point rule)\n\t\tif (m_alphaf == 0.5)\n\t\t{\n\t\t\tFEElasticMaterial* pme = dynamic_cast<FEElasticMaterial*>(m_pMat);\n\n\t\t\t// evaluate strain energy at current time\n\t\t\tmat3d Ftmp = pt.m_F;\n\t\t\tdouble Jtmp = pt.m_J;\n\t\t\tpt.m_F = Ft;\n\t\t\tpt.m_J = Jt;\n\t\t\tpt.m_Wt = pme->StrainEnergyDensity(mp);\n\t\t\tpt.m_F = Ftmp;\n\t\t\tpt.m_J = Jtmp;\n\n            mat3ds D = pt.RateOfDeformation();\n            double D2 = D.dotdot(D);\n\t\t\tif (D2 > std::numeric_limits<double>::epsilon())\n\t\t\t{\n\t\t\t\tpt.m_s += D * (((pt.m_Wt - pt.m_Wp) / (dt * pt.m_J) - pt.m_s.dotdot(D)) / D2);\n\t\t\t}\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data. \nvoid FEElasticSolidDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N*6);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& node = m_pMesh->Node(el.m_node[i]);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3*i  ] = id[m_dofU[0]];\n\t\tlm[3*i+1] = id[m_dofU[1]];\n\t\tlm[3*i+2] = id[m_dofU[2]];\n\n\t\t// rigid rotational dofs\n\t\tlm[3*N + 3*i  ] = id[m_dofR[0]];\n\t\tlm[3*N + 3*i+1] = id[m_dofR[1]];\n\t\tlm[3*N + 3*i+2] = id[m_dofR[2]];\n\t}\n    \n    // substitute interface dofs for solid-shell interfaces\n\tFESolidElement& sel = static_cast<FESolidElement&>(el);\n\tfor (int i = 0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(el.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the displacement dofs\n            lm[3*i  ] = id[m_dofSU[0]];\n            lm[3*i+1] = id[m_dofSU[1]];\n            lm[3*i+2] = id[m_dofSU[2]];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Calculate inertial forces \\todo Why is F no longer needed?\nvoid FEElasticSolidDomain::InertialForces(FEGlobalVector& R, vector<double>& F)\n{\n    int NE = Elements();\n#pragma omp parallel for shared(R, F)\n\tfor (int i=0; i<NE; ++i)\n    {\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\tif (el.isActive()) {\n\t\t\t// element force vector\n\t\t\tvector<double> fe;\n\t\t\tvector<int> lm;\n\n\t\t\t// get the element force vector and initialize it to zero\n\t\t\tint ndof = 3 * el.Nodes();\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// calculate internal force vector\n\t\t\tElementInertialForce(el, fe);\n\n\t\t\t// get the element's LM vector\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble element 'fe'-vector into global R vector\n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain::ElementInertialForce(FESolidElement& el, vector<double>& fe)\n{\n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*    gw = el.GaussWeights();\n\n    // repeat for all integration points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        double dens = m_pMat->Density(mp);\n        double J0 = detJ0(el, n)*gw[n];\n        \n        double* H = el.H(n);\n        for (int i=0; i<neln; ++i)\n        {\n            double tmp = H[i]*J0*dens;\n            fe[3*i  ] -= tmp*pt.m_a.x;\n            fe[3*i+1] -= tmp*pt.m_a.y;\n            fe[3*i+2] -= tmp*pt.m_a.z;\n        }\n    }\n}\n\n\n//=================================================================================================\n\nBEGIN_FECORE_CLASS(FEStandardElasticSolidDomain, FEElasticSolidDomain)\n\tADD_PARAMETER(m_elemType, \"elem_type\", FE_PARAM_ATTRIBUTE, \"$(solid_element)\\0\");\n\tADD_PARAMETER(m_secant_stress, \"secant_stress\");\n\tADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\nEND_FECORE_CLASS();\n\nFEStandardElasticSolidDomain::FEStandardElasticSolidDomain(FEModel* fem) : FEElasticSolidDomain(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include \"FEElasticDomain.h\"\n#include \"FESolidMaterial.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! domain described by Lagrange-type 3D volumetric elements\n//!\nclass FEBIOMECH_API FEElasticSolidDomain : public FESolidDomain, public FEElasticDomain\n{\npublic:\n\t//! constructor\n\tFEElasticSolidDomain(FEModel* pfem);\n\n\t//! assignment operator\n\tFEElasticSolidDomain& operator = (FEElasticSolidDomain& d);\n\n\t//! activate\n\tvoid Activate() override;\n\n\t//! initialize elements\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\t//! Unpack solid element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! Set flag for update for dynamic quantities\n\tvoid SetDynamicUpdateFlag(bool b);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// get the total dof list\n\tconst FEDofList& GetDOFList() const override;\n\npublic: // overrides from FEDomain\n\n\t//! get the material\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pm) override;\n\npublic: // overrides from FEElasticDomain\n\n\t// update stresses\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t// update the element stress\n\tvirtual void UpdateElementStress(int iel, const FETimeInfo& tp);\n\n\t//! intertial forces for dynamic problems\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override;\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! body forces\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculates inertial stiffness\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\n\t//! body force stiffness\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n\npublic:\n\t// --- S T I F F N E S S ---\n\n\t//! calculates the solid element stiffness matrix\n\tvirtual void ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke);\n\n\t//! geometrical stiffness (i.e. initial stress)\n\tvirtual void ElementGeometricalStiffness(FESolidElement& el, matrix& ke);\n\n\t//! material stiffness component\n\tvirtual void ElementMaterialStiffness(FESolidElement& el, matrix& ke);\n\n\t// --- R E S I D U A L ---\n\n\t//! Calculates the internal stress vector for solid elements\n\tvoid ElementInternalForce(FESolidElement& el, vector<double>& fe);\n\n    //! Calculates the inertial force vector for solid elements\n    void ElementInertialForce(FESolidElement& el, vector<double>& fe);\n    \nprotected:\n    double              m_alphaf;\n    double              m_alpham;\n    double              m_beta;\n\tbool\t\t\t\tm_update_dynamic;\t//!< flag for updating quantities only used in dynamic analysis\n\n\tbool\tm_secant_stress;\t//!< use secant approximation to stress\n\tbool\tm_secant_tangent;   //!< flag for using secant tangent\n\nprotected:\n\tFEDofList\tm_dofU;\t\t// displacement dofs\n\tFEDofList\tm_dofR;\t\t// rigid rotation rofs\n\tFEDofList\tm_dofSU;\t// shell displacement dofs\n\tFEDofList\tm_dofV;\t\t// velocity dofs\n\tFEDofList\tm_dofSV;\t// shell velocity dofs\n\tFEDofList\tm_dofSA;\t// shell acceleration dofs\n\tFEDofList\tm_dof;\t\t// total dof list\n\n\tFESolidMaterial*\tm_pMat;\n};\n\nclass FEStandardElasticSolidDomain : public FEElasticSolidDomain\n{\npublic:\n\tFEStandardElasticSolidDomain(FEModel* fem);\n\nprivate:\n\tstd::string\t\tm_elemType;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEElasticTrussDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticTrussDomain.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEUncoupledMaterial.h\"\n#include \"FEElasticMaterialPoint.h\"\n#include \"FEBioMech.h\"\n#include \"FEBodyForce.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEElasticTrussDomain, FETrussDomain)\n\tADD_PARAMETER(m_a0, \"cross_sectional_area\");\n\tADD_PARAMETER(m_v, \"v\")->setLongName(\"Poisson's ratio\");\nEND_FECORE_CLASS();\n//-----------------------------------------------------------------------------\n//! Constructor\nFEElasticTrussDomain::FEElasticTrussDomain(FEModel* pfem) : FETrussDomain(pfem), FEElasticDomain(pfem), m_dofU(pfem)\n{\n\tm_a0 = 0.0;\n\tm_v = 0.5;\n\n\tm_pMat = 0;\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! copy operator\nFEElasticTrussDomain& FEElasticTrussDomain::operator = (FEElasticTrussDomain& d)\n{ \n\tm_Elem = d.m_Elem; \n\tm_pMesh = d.m_pMesh; \n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\n//! get the dof list\nconst FEDofList& FEElasticTrussDomain::GetDOFList() const\n{\n\treturn m_dofU;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElasticTrussDomain::detJt(FETrussElement& el) const\n{\n\tconst FENode& n0 = Node(el.m_lnode[0]);\n\tconst FENode& n1 = Node(el.m_lnode[1]);\n\n\tvec3d d = n1.m_rt - n0.m_rt;\n\treturn d.Length() * 0.5;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFETrussDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FESolidMaterial*>(pmat);\n\tassert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize the domain\nbool FEElasticTrussDomain::Init()\n{\n\tif (m_a0 != 0.0)\n\t{\n\t\tfor (int i = 0; i < Elements(); ++i)\n\t\t{\n\t\t\tFETrussElement& el = Element(i);\n\t\t\tel.m_a0 = m_a0;\n\t\t}\n\t}\n\n\n\tfor (int i = 0; i < (int)m_Elem.size(); ++i)\n\t{\n\t\t// unpack the element\n\t\tFETrussElement& el = m_Elem[i];\n\n\t\t// nodal coordinates\n\t\tvec3d r0[2];\n\t\tfor (int j = 0; j < 2; ++j) r0[j] = m_pMesh->Node(el.m_node[j]).m_r0;\n\n\t\t// initial length\n\t\tel.m_L0 = (r0[1] - r0[0]).norm();\n\t}\n\n\treturn FETrussDomain::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::Reset()\n{\n\tForEachMaterialPoint([](FEMaterialPoint& mp) {\n\t\tmp.Init();\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::UnpackLM(FEElement &el, vector<int>& lm)\n{\n\tlm.resize(6);\n\tFENode& n1 = m_pMesh->Node(el.m_node[0]);\n\tFENode& n2 = m_pMesh->Node(el.m_node[1]);\n\tlm[0] = n1.m_ID[m_dofU[0]];\n\tlm[1] = n1.m_ID[m_dofU[1]];\n\tlm[2] = n1.m_ID[m_dofU[2]];\n\tlm[3] = n2.m_ID[m_dofU[0]];\n\tlm[4] = n2.m_ID[m_dofU[1]];\n\tlm[5] = n2.m_ID[m_dofU[2]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::Activate()\n{\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tForEachMaterialPoint([&](FEMaterialPoint& mp) {\n\t\tmp.Update(timeInfo);\n\t});\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEElasticTrussDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tint NT = (int)m_Elem.size();\n\tvector<int> lm;\n\tfor (int iel =0; iel<NT; ++iel)\n\t{\n\t\tFETrussElement& el = m_Elem[iel];\n\t\tFEElementMatrix ke(el);\n\t\tElementStiffness(iel, ke);\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! intertial stiffness matrix\nvoid FEElasticTrussDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n\tfor (int n = 0; n < Elements(); ++n)\n\t{\n\t\tFETrussElement& el = Element(n);\n\n\t\tmatrix me(2, 2); me.zero();\n\t\tElementMassMatrix(el, me);\n\n\t\tFEElementMatrix Me(6, 6); Me.zero();\n\t\tfor (int i=0; i<2; ++i)\n\t\t{ \n\t\t\tfor (int j = 0; j < 2; ++j)\n\t\t\t{\n\t\t\t\tMe[3 * i    ][3 * j    ] = me[i][j];\n\t\t\t\tMe[3 * i + 1][3 * j + 1] = me[i][j];\n\t\t\t\tMe[3 * i + 2][3 * j + 2] = me[i][j];\n\t\t\t}\n\t\t}\n\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\n\t\tMe.SetIndices(lm);\n\n\t\tLS.Assemble(Me);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::ElementStiffness(int iel, matrix& ke)\n{\n\tFETrussElement& el = Element(iel);\n\n\t// get the elastic tangent\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\ttens4ds c = m_pMat->Tangent(mp);\n\tdouble E = c(0, 0);\n\n\t// element initial volume\n\tdouble V = el.m_L0*el.m_a0;\n\tdouble l = el.m_L0 / el.m_lam;\n\n\t// Kirchhoff Stress\n\tdouble tau = el.m_tau;\n\n\t// scalar stiffness\n\tdouble k = E;// V / (l * l) * (E - 2 * tau);\n\n\t// axial force T = s*a = t*V/l\n\tdouble T = tau*V/l;\n\n\tvec3d n = GetTrussAxisVector(el);\n\n\t// calculate the tangent matrix\n\tke.resize(6, 6);\n\n\tke[0][0] = ke[3][3] = k*n.x*n.x + T/l;\n\tke[1][1] = ke[4][4] = k*n.y*n.y + T/l;\n\tke[2][2] = ke[5][5] = k*n.z*n.z + T/l;\n\n\tke[0][1] = ke[1][0] = ke[3][4] = ke[4][3] = k*n.x*n.y;\n\tke[1][2] = ke[2][1] = ke[4][5] = ke[5][4] = k*n.y*n.z;\n\tke[0][2] = ke[2][0] = ke[3][5] = ke[5][3] = k*n.x*n.z;\n\n\tke[0][3] = ke[3][0] = -ke[0][0]; ke[0][4] = ke[4][0] = -ke[0][1]; ke[0][5] = ke[5][0] = -ke[0][2];\n\tke[1][3] = ke[3][1] = -ke[1][0]; ke[1][4] = ke[4][1] = -ke[1][1]; ke[1][5] = ke[5][1] = -ke[1][2];\n\tke[2][3] = ke[3][2] = -ke[2][0]; ke[2][4] = ke[4][2] = -ke[2][1]; ke[2][5] = ke[5][2] = -ke[2][2];\n}\n\n//----------------------------------------------------------------------------\n//! elemental mass matrix\nvoid FEElasticTrussDomain::ElementMassMatrix(FETrussElement& el, matrix& me)\n{\n\t// nodal coordinates\n\tvec3d r0[2];\n\tfor (int i = 0; i < 2; ++i)\n\t{\n\t\tr0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\t}\n\n\tdouble L = (r0[1] - r0[0]).norm();\n\tdouble A = el.m_a0;\n\tdouble rho = m_pMat->Density(*el.GetMaterialPoint(0));\n\n\tme[0][0] = rho * A * L / 3.0; me[0][1] = rho * A * L / 6.0;\n\tme[1][0] = rho * A * L / 6.0; me[1][1] = rho * A * L / 3.0;\n}\n\n//----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::InternalForces(FEGlobalVector& R)\n{\n\t// element force vector\n\tint NT = (int)m_Elem.size();\n#pragma omp parallel for\n\tfor (int i=0; i<NT; ++i)\n\t{\n\t\tFETrussElement& el = m_Elem[i];\n\n\t\tvector<double> fe;\n\t\tElementInternalForces(el, fe);\n\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticTrussDomain::ElementInternalForces(FETrussElement& el, vector<double>& fe)\n{\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\tvec3d n = GetTrussAxisVector(el);\n\n\t// nodal coordinates\n\tvec3d r0[2] = {\n\t\tm_pMesh->Node(el.m_node[0]).m_r0,\n\t\tm_pMesh->Node(el.m_node[1]).m_r0\n\t};\n\n\tvec3d rt[2] = {\n\t\tm_pMesh->Node(el.m_node[0]).m_rt,\n\t\tm_pMesh->Node(el.m_node[1]).m_rt\n\t};\n\n\t// initial length\n\tdouble L = (r0[1] - r0[0]).norm();\n\n\t// current length\n\tdouble l = (rt[1] - rt[0]).norm();\n\n\t// stress in element\n\tdouble tau = pt.m_s.xx();\n\n\t// elements initial volume\n\tdouble V = L*el.m_a0;\n\n\t// current volume\n\tdouble v = V * pt.m_J;\n\n\t// current area\n\tdouble a = v / l;\n\n\t// calculate nodal forces\n\tfe.resize(6);\n\tfe[0] = tau*a*n.x;\n\tfe[1] = tau*a*n.y;\n\tfe[2] = tau*a*n.z;\n\tfe[3] = -fe[0];\n\tfe[4] = -fe[1];\n\tfe[5] = -fe[2];\n}\n\n//! calculate body force \\todo implement this\nvoid FEElasticTrussDomain::BodyForce(FEGlobalVector& R, FEBodyForce& bf)\n{\n\tFEMesh& mesh = *GetMesh();\n\n\t// loop over all the elements\n\tint NE = Elements();\n#pragma omp parallel for \n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFETrussElement& el = Element(i);\n\t\tint neln = el.Nodes();\n\n\t\t// only consider active elements\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// total size of the element vector\n\t\t\tint ndof = 3* el.Nodes();\n\n\t\t\t// setup the element vector\n\t\t\tvector<double> fe;\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// jacobian\n\t\t\tdouble Jt = detJt(el);\n\n\t\t\t// loop over integration points\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t\tdouble* H = el.H(n);\n\t\t\t\tmp.m_Jt = Jt;\n\t\t\t\tmp.m_shape = el.H(n);\n\n\t\t\t\t// get the value of the integrand for this node\n\t\t\t\tvec3d f = bf.force(mp);\n\n\t\t\t\t// loop over all nodes\n\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t{\n\t\t\t\t\t// add it all up\n\t\t\t\t\tfe[3 * j   ] += H[j] * f.x * w[n];\n\t\t\t\t\tfe[3 * j +1] += H[j] * f.y * w[n];\n\t\t\t\t\tfe[3 * j +2] += H[j] * f.z * w[n];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get the element's LM vector\n\t\t\tvector<int> lm(ndof, -1);\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// Assemble into global vector\n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Update the truss' stresses\nvoid FEElasticTrussDomain::Update(const FETimeInfo& tp)\n{\n\tFEUncoupledMaterial* um = dynamic_cast<FEUncoupledMaterial*>(GetMaterial());\n\n\t// loop over all elements\n#pragma omp parallel for\n\tfor (int i=0; i<(int) m_Elem.size(); ++i)\n\t{\n\t\t// unpack the element\n\t\tFETrussElement& el = m_Elem[i];\n\n\t\t// nodal coordinates\n\t\tvec3d r0[2], rt[2];\n\t\tfor (int j=0; j<2; ++j)\n\t\t{\n\t\t\tr0[j] = m_pMesh->Node(el.m_node[j]).m_r0;\n\t\t\trt[j] = m_pMesh->Node(el.m_node[j]).m_rt;\n\t\t}\n\n\t\tdouble l = (rt[1] - rt[0]).norm();\n\t\tdouble L = (r0[1] - r0[0]).norm();\n\n\t\tdouble lam = l / L;\n\t\tdouble linv2 = 1.0 / sqrt(pow(lam, 2.0*m_v));\n\n\t\t// calculate strain\n\t\tel.m_lam = l / L;\n\n\t\t// setup deformation gradient (assuming incompressibility)\n\t\tmat3d F(lam, 0.0, 0.0, 0.0, linv2, 0.0, 0.0, 0.0, linv2);\n\n\t\t// calculate stress\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\tep.m_F = F;\n\t\tep.m_J = F.det();\n\n\t\tif (um)\n\t\t{\n\t\t\tmat3ds devs = um->DevStress(mp);\n\t\t\tdouble p = -devs.yy();\n\t\t\tep.m_s = devs + mat3dd(p);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tep.m_s = m_pMat->Stress(mp);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEElasticTrussDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FETrussDomain.h>\n#include \"FEElasticDomain.h\"\n#include \"FESolidMaterial.h\"\n#include <FECore/FEDofList.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain described by 3D truss elements\nclass FEBIOMECH_API FEElasticTrussDomain : public FETrussDomain, public FEElasticDomain\n{\npublic:\n\t//! Constructor\n\tFEElasticTrussDomain(FEModel* pfem);\n\n\t//! copy operator\n\tFEElasticTrussDomain& operator = (FEElasticTrussDomain& d);\n\n\t//! initialize the domain\n\tbool Init() override;\n\n\t//! Reset data\n\tvoid Reset() override;\n\n\t//! Initialize elements\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\t//! Unpack truss element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! Activate domain\n\tvoid Activate() override;\n\n\t//! get the dof list\n\tconst FEDofList& GetDOFList() const override;\n\n\tdouble detJt(FETrussElement& el) const;\n\npublic: // overloads from FEElasticDomain\n\n\t//! update the truss stresses\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculate body force\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\n\t//! Calculates inertial forces for dynamic problems\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override { assert(false); }\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! intertial stiffness matrix\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\n\t//! body force stiffness matrix \\todo implement this\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override { assert(false); }\n\n\t//! elemental mass matrix\n\tvoid ElementMassMatrix(FETrussElement& el, matrix& ke);\n\nprotected:\n\t//! calculates the truss element stiffness matrix\n\tvoid ElementStiffness(int iel, matrix& ke);\n\n\t//! Calculates the internal stress vector for solid elements\n\tvoid ElementInternalForces(FETrussElement& el, vector<double>& fe);\n\nprotected:\n\tFESolidMaterial*\tm_pMat;\n\tdouble\tm_a0;\n\tdouble\tm_v;\n\n\tFEDofList\tm_dofU;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEEllipsoidalFiberDistribution.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEllipsoidalFiberDistribution.h\"\n#include \"FEContinuousFiberDistribution.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n// FEEllipsoidalFiberDistribution\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEllipsoidalFiberDistribution, FEElasticMaterial)\n\tADD_PARAMETER(m_beta, 3, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi , 3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nmat3ds FEEllipsoidalFiberDistribution::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wl;\n\tconst double eps = 0;\n\tmat3ds s;\n\ts.zero();\n\n\t// calculate material coefficients\n\tdouble ksi0 = m_ksi[0](mp);\n\tdouble ksi1 = m_ksi[1](mp);\n\tdouble ksi2 = m_ksi[2](mp);\n\n\tdouble beta0 = m_beta[0](mp);\n\tdouble beta1 = m_beta[1](mp);\n\tdouble beta2 = m_beta[2](mp);\n\n\n\tfor (int n=0; n<MAX_INT; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\n\t\t// calculate material coefficients\n\t\tdouble ksi  = 1.0 / sqrt(SQR(n0a.x /  ksi0) + SQR(n0a.y /  ksi1) + SQR(n0a.z /  ksi2));\n\t\tdouble beta = 1.0 / sqrt(SQR(n0a.x / beta0) + SQR(n0a.y / beta1) + SQR(n0a.z / beta2));\n\n\t\t// --- quadrant 1,1,1 ---\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0);\n\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\n\t\t// --- quadrant -1,1,1 ---\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0);\n\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\n\t\t// --- quadrant -1,-1,1 ---\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0);\n\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\n\t\t// --- quadrant 1,-1,1 ---\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0);\n\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t}\n\n\t// we multiply by two to add contribution from other half-sphere\n\treturn s*(4.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEEllipsoidalFiberDistribution::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wll;\n\tconst double eps = 0;\n\ttens4ds cf, cfw; cf.zero();\n\tmat3ds N2;\n\ttens4ds N4;\n\ttens4ds c;\n\tc.zero();\n\t\n\t// calculate material coefficients\n\tdouble ksi0 = m_ksi[0](mp);\n\tdouble ksi1 = m_ksi[1](mp);\n\tdouble ksi2 = m_ksi[2](mp);\n\n\tdouble beta0 = m_beta[0](mp);\n\tdouble beta1 = m_beta[1](mp);\n\tdouble beta2 = m_beta[2](mp);\n\n\tfor (int n=0; n<MAX_INT; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\n\t\t// calculate material coefficients\n\t\tdouble ksi  = 1.0 / sqrt(SQR(n0a.x /  ksi0) + SQR(n0a.y /  ksi1) + SQR(n0a.z /  ksi2));\n\t\tdouble beta = 1.0 / sqrt(SQR(n0a.x / beta0) + SQR(n0a.y / beta1) + SQR(n0a.z / beta2));\n\n\t\t// --- quadrant 1,1,1 ---\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWll = beta*(beta-1.0)*ksi*pow(In - 1.0, beta-2.0);\n\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\n\t\t// --- quadrant -1,1,1 ---\n\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWll = beta*(beta-1.0)*ksi*pow(In - 1.0, beta-2.0);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\n\t\t// --- quadrant -1,-1,1 ---\n\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWll = beta*(beta-1.0)*ksi*pow(In - 1.0, beta-2.0);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\n\t\t// --- quadrant 1,-1,1 ---\n\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWll = beta*(beta-1.0)*ksi*pow(In - 1.0, beta-2.0);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t}\n\n\t// multiply by two to integrate over other half of sphere\n\treturn c*(2.0*4.0/J);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEEllipsoidalFiberDistribution::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = 0.0;\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, W;\n\tconst double eps = 0;\n\n\t// calculate material coefficients\n\tdouble ksi0 = m_ksi[0](mp);\n\tdouble ksi1 = m_ksi[1](mp);\n\tdouble ksi2 = m_ksi[2](mp);\n\n\tdouble beta0 = m_beta[0](mp);\n\tdouble beta1 = m_beta[1](mp);\n\tdouble beta2 = m_beta[2](mp);\n    \n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n        \n\t\t// calculate material coefficients\n\t\tdouble ksi = 1.0 / sqrt(SQR(n0a.x / ksi0) + SQR(n0a.y / ksi1) + SQR(n0a.z / ksi2));\n\t\tdouble beta = 1.0 / sqrt(SQR(n0a.x / beta0) + SQR(n0a.y / beta1) + SQR(n0a.z / beta2));\n\n\t\t// --- quadrant 1,1,1 ---\n        \n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n        \n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n        \n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy\n\t\t\tW = ksi*pow(In - 1.0, beta);\n\t\t\tsed += W*wn;\n\t\t}\n        \n\t\t// --- quadrant -1,1,1 ---\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n        \n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n        \n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n        \n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy\n\t\t\tW = ksi*pow(In - 1.0, beta);\n\t\t\tsed += W*wn;\n\t\t}\n        \n\t\t// --- quadrant -1,-1,1 ---\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n        \n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n        \n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n        \n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy\n\t\t\tW = ksi*pow(In - 1.0, beta);\n\t\t\tsed += W*wn;\n\t\t}\n        \n\t\t// --- quadrant 1,-1,1 ---\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n        \n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n        \n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n        \n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy\n\t\t\tW = ksi*pow(In - 1.0, beta);\n\t\t\tsed += W*wn;\n\t\t}\n\t}\n    \n\t// multiply by two to integrate over other half of sphere\n    return sed*2.0;\n}\n\n//-----------------------------------------------------------------------------\n// FEEllipsoidalFiberDistributionOld\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEEllipsoidalFiberDistributionOld, FEElasticMaterial)\n\tADD_PARAMETER(m_beta, 3, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi , 3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEEllipsoidalFiberDistributionOld\n//-----------------------------------------------------------------------------\n\nFEEllipsoidalFiberDistributionOld::FEEllipsoidalFiberDistributionOld(FEModel* pfem) : FEElasticMaterial(pfem)\n{ \n\tm_nres = 0; \n}\n\n//-----------------------------------------------------------------------------\nbool FEEllipsoidalFiberDistributionOld::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\tInitIntegrationRule();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEllipsoidalFiberDistributionOld::InitIntegrationRule()\n{\n\t// select the integration rule\n\tconst int nint = (m_nres == 0? NSTL  : NSTH  );\n\tconst double* phi = (m_nres == 0? PHIL  : PHIH  );\n\tconst double* the = (m_nres == 0? THETAL: THETAH);\n\tconst double* w   = (m_nres == 0? AREAL : AREAH );\n\t\t\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tm_cth[n] = cos(the[n]);\n\t\tm_sth[n] = sin(the[n]);\n\t\tm_cph[n] = cos(phi[n]);\n\t\tm_sph[n] = sin(phi[n]);\n\t\tm_w[n] = w[n];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEllipsoidalFiberDistributionOld::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tar << m_nres;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar >> m_nres;\n\t\t\tInitIntegrationRule();\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEEllipsoidalFiberDistributionOld::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tmat3d QT = Q.transpose();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// loop over all integration points\n\tdouble ksi, beta;\n\tvec3d n0e, n0a, nt;\n\tdouble In, Wl;\n\tconst double eps = 0;\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds s;\n\ts.zero();\n\n\t// material coefficients\n\tdouble ksi0 = m_ksi[0](mp);\n\tdouble ksi1 = m_ksi[1](mp);\n\tdouble ksi2 = m_ksi[2](mp);\n\n\tdouble beta0 = m_beta[0](mp);\n\tdouble beta1 = m_beta[1](mp);\n\tdouble beta2 = m_beta[2](mp);\n\n\tconst int nint = (m_nres == 0? NSTL  : NSTH  );\n\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in reference configuration\n\t\tn0e.x = m_cth[n]*m_sph[n];\n\t\tn0e.y = m_sth[n]*m_sph[n];\n\t\tn0e.z = m_cph[n];\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = n0e*(C*n0e);\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// get the global spatial fiber direction in current configuration\n\t\t\tnt = (F*n0e)/sqrt(In);\n\t\t\t\n\t\t\t// calculate the outer product of nt\n\t\t\tmat3ds N = dyad(nt);\n\t\t\t\n\t\t\t// get the local material fiber direction in reference configuration\n\t\t\tn0a = QT*n0e;\n\t\t\t\n\t\t\t// calculate material coefficients\n\t\t\tksi  = 1.0 / sqrt(SQR(n0a.x /  ksi0) + SQR(n0a.y / ksi1 ) + SQR(n0a.z / ksi2 ));\n\t\t\tbeta = 1.0 / sqrt(SQR(n0a.x / beta0) + SQR(n0a.y / beta1) + SQR(n0a.z / beta2));\n\t\t\t\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0);\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += N*(2.0/J*In*Wl*m_w[n]);\n\t\t}\n\t\t\n\t}\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEEllipsoidalFiberDistributionOld::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tmat3d QT = Q.transpose();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// loop over all integration points\n\tdouble ksi, beta;\n\tvec3d n0e, n0a, nt;\n\tdouble In, Wll;\n\tconst double eps = 0;\n\ttens4ds cf, cfw; cf.zero();\n\tmat3ds N2;\n\ttens4ds N4;\n\ttens4ds c;\n\tc.zero();\n\t\n\t// material coefficients\n\tdouble ksi0 = m_ksi[0](mp);\n\tdouble ksi1 = m_ksi[1](mp);\n\tdouble ksi2 = m_ksi[2](mp);\n\n\tdouble beta0 = m_beta[0](mp);\n\tdouble beta1 = m_beta[1](mp);\n\tdouble beta2 = m_beta[2](mp);\n\n\t// right Cauchy-Green tensor: C = Ft*F\n\tmat3ds C = (F.transpose()*F).sym();\n\n\tconst int nint = (m_nres == 0? NSTL  : NSTH  );\n\t\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in reference configuration\n\t\tn0e.x = m_cth[n]*m_sph[n];\n\t\tn0e.y = m_sth[n]*m_sph[n];\n\t\tn0e.z = m_cph[n];\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = n0e*(C*n0e);\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// get the global spatial fiber direction in current configuration\n\t\t\tnt = (F*n0e)/sqrt(In);\n\t\t\t\n\t\t\t// calculate the outer product of nt\n\t\t\tN2 = dyad(nt);\n\t\t\t\n\t\t\t// get the local material fiber direction in reference configuration\n\t\t\tn0a = QT*n0e;\n\t\t\t\n\t\t\t// calculate material coefficients in local fiber direction\n\t\t\tksi  = 1.0 / sqrt(SQR(n0a.x /  ksi0) + SQR(n0a.y / ksi1 ) + SQR(n0a.z / ksi2 ));\n\t\t\tbeta = 1.0 / sqrt(SQR(n0a.x / beta0) + SQR(n0a.y / beta1) + SQR(n0a.z / beta2));\n\t\t\t\n\t\t\t// calculate strain energy derivative\n\t\t\tWll = beta*(beta-1.0)*ksi*pow(In - 1.0, beta-2.0);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(4.0/J*In*In*Wll*m_w[n]);\n\t\t}\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEEllipsoidalFiberDistributionOld::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = 0.0;\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tmat3d QT = Q.transpose();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n    \n\t// loop over all integration points\n\tdouble ksi, beta;\n\tvec3d n0e, n0a, nt;\n\tdouble In, W;\n\tconst double eps = 0;\n\tmat3ds C = pt.RightCauchyGreen();\n    \n\tconst int nint = (m_nres == 0? NSTL  : NSTH  );\n    \n\t// material coefficients\n\tdouble ksi0 = m_ksi[0](mp);\n\tdouble ksi1 = m_ksi[1](mp);\n\tdouble ksi2 = m_ksi[2](mp);\n\n\tdouble beta0 = m_beta[0](mp);\n\tdouble beta1 = m_beta[1](mp);\n\tdouble beta2 = m_beta[2](mp);\n\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in reference configuration\n\t\tn0e.x = m_cth[n]*m_sph[n];\n\t\tn0e.y = m_sth[n]*m_sph[n];\n\t\tn0e.z = m_cph[n];\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = n0e*(C*n0e);\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// get the global spatial fiber direction in current configuration\n\t\t\tnt = (F*n0e)/sqrt(In);\n\t\t\t\n\t\t\t// get the local material fiber direction in reference configuration\n\t\t\tn0a = QT*n0e;\n\t\t\t\n\t\t\t// calculate material coefficients\n\t\t\tksi  = 1.0 / sqrt(SQR(n0a.x /  ksi0) + SQR(n0a.y / ksi1 ) + SQR(n0a.z / ksi2 ));\n\t\t\tbeta = 1.0 / sqrt(SQR(n0a.x / beta0) + SQR(n0a.y / beta1) + SQR(n0a.z / beta2));\n\t\t\t\n\t\t\t// calculate strain energy density\n\t\t\tW = ksi*pow(In - 1.0, beta);\n\t\t\tsed += W*m_w[n];\n\t\t}\n\t\t\n\t}\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEEllipsoidalFiberDistribution.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n// The following file contains the integration points and weights\n// for the integration over a unit sphere in spherical coordinates\n#include \"geodesic.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for the ellipsoidal fiber distribution\n//!\n\nclass FEEllipsoidalFiberDistribution : public FEElasticMaterial\n{\n\tenum { MAX_INT = 45 };\n\npublic:\n\tFEEllipsoidalFiberDistribution(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\n\t//! Cauchy stress\n\tvirtual mat3ds Stress(FEMaterialPoint& mp) override;\n\n\t// Spatial tangent\n\tvirtual tens4ds Tangent(FEMaterialPoint& mp) override;\n\t\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \npublic:\n\tFEParamDouble\tm_beta[3];\t// power in power-law relation\n\tFEParamDouble\tm_ksi[3];\t// coefficient in power-law relation\n\n\t\t\t\t\t\t\t\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Material class for the ellipsoidal fiber distribution\n//! (this is the old, obsolete implementation)\n\nclass FEEllipsoidalFiberDistributionOld : public FEElasticMaterial\n{\npublic:\n\tFEEllipsoidalFiberDistributionOld(FEModel* pfem);\n\t\n\t//! Initialization\n\tbool Init() override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Cauchy stress\n\tvirtual mat3ds Stress(FEMaterialPoint& mp) override;\n\n\t// Spatial tangent\n\tvirtual tens4ds Tangent(FEMaterialPoint& mp) override;\n\t\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n\nprotected:\n\tvoid InitIntegrationRule();\n    \npublic: // parameters\n\tFEParamDouble\tm_beta[3];\t// power in power-law relation\n\tFEParamDouble\tm_ksi[3];\t// coefficient in power-law relation\n\nprivate:\n\tint\t\tm_nres;\t// integration rule\n\tdouble\tm_cth[NSTH];\n\tdouble\tm_sth[NSTH];\n\tdouble\tm_cph[NSTH];\n\tdouble\tm_sph[NSTH];\n\tdouble\tm_w[NSTH];\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEExplicitSolidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEExplicitSolidSolver.h\"\n#include \"FEElasticSolidDomain.h\"\n#include \"FEElasticShellDomain.h\"\n#include \"FELinearTrussDomain.h\"\n#include \"FEElasticTrussDomain.h\"\n#include \"FERigidMaterial.h\"\n#include \"FEBodyForce.h\"\n#include \"FEContactInterface.h\"\n#include \"FERigidBody.h\"\n#include \"RigidBC.h\"\n#include \"FEMechModel.h\"\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include \"FECore/log.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FENLConstraint.h>\n#include \"FEResidualVector.h\"\n#include \"FEBioMech.h\"\n#include \"FESolidAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEExplicitSolidSolver, FESolver)\n\tADD_PARAMETER(m_mass_lumping, \"mass_lumping\");\n\tADD_PARAMETER(m_dyn_damping, \"dyn_damping\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEExplicitSolidSolver::FEExplicitSolidSolver(FEModel* pfem) : \n\tFESolver(pfem), \n\tm_dofU(pfem), m_dofV(pfem), m_dofQ(pfem), m_dofRQ(pfem),\n\tm_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem),\n\tm_rigidSolver(pfem)\n{\n\tm_dyn_damping = 1;\n\tm_niter = 0;\n\tm_nreq = 0;\n\n\tm_mass_lumping = HRZ_LUMPING;\n\n\t// Allocate degrees of freedom\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint varD = dofs.AddVariable(\"displacement\", VAR_VEC3);\n\t\tdofs.SetDOFName(varD, 0, \"x\");\n\t\tdofs.SetDOFName(varD, 1, \"y\");\n\t\tdofs.SetDOFName(varD, 2, \"z\");\n\t\tint varQ = dofs.AddVariable(\"rotation\", VAR_VEC3);\n\t\tdofs.SetDOFName(varQ, 0, \"u\");\n\t\tdofs.SetDOFName(varQ, 1, \"v\");\n\t\tdofs.SetDOFName(varQ, 2, \"w\");\n\t\tint varQR = dofs.AddVariable(\"rigid rotation\", VAR_VEC3);\n\t\tdofs.SetDOFName(varQR, 0, \"Ru\");\n\t\tdofs.SetDOFName(varQR, 1, \"Rv\");\n\t\tdofs.SetDOFName(varQR, 2, \"Rw\");\n\t\tint varV = dofs.AddVariable(\"velocity\", VAR_VEC3);\n\t\tdofs.SetDOFName(varV, 0, \"vx\");\n\t\tdofs.SetDOFName(varV, 1, \"vy\");\n\t\tdofs.SetDOFName(varV, 2, \"vz\");\n\t\tint varSU = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT), VAR_VEC3);\n\t\tdofs.SetDOFName(varSU, 0, \"sx\");\n\t\tdofs.SetDOFName(varSU, 1, \"sy\");\n\t\tdofs.SetDOFName(varSU, 2, \"sz\");\n\t\tint varSV = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY), VAR_VEC3);\n\t\tdofs.SetDOFName(varSV, 0, \"svx\");\n\t\tdofs.SetDOFName(varSV, 1, \"svy\");\n\t\tdofs.SetDOFName(varSV, 2, \"svz\");\n\t\tint varSA = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION), VAR_VEC3);\n\t\tdofs.SetDOFName(varSA, 0, \"sax\");\n\t\tdofs.SetDOFName(varSA, 1, \"say\");\n\t\tdofs.SetDOFName(varSA, 2, \"saz\");\n\n\t\t// get the DOF indices\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t\tm_dofQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION));\n\t\tm_dofRQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n\t\tm_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEExplicitSolidSolver::Clean()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEExplicitSolidSolver::CalculateMassMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tvector<double> massVector(m_neq), dummy(m_neq);\n\tFEGlobalVector M(fem, massVector, dummy);\n\tmatrix me;\n\tvector <int> lm;\n\tvector <double> el_lumped_mass;\n\n\t// loop over all domains\n\tif (m_mass_lumping == NO_MASS_LUMPING)\n\t{\n\t\t// use consistent mass matrix.\n\t\t// TODO: implement this\n\t\tassert(false);\n\t\treturn false;\n\t}\n\telse if (m_mass_lumping == ROW_SUM_LUMPING)\n\t{\n\t\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t\t{\n\t\t\t// check whether it is a solid domain\n\t\t\tFEElasticSolidDomain* pbd = dynamic_cast<FEElasticSolidDomain*>(&mesh.Domain(nd));\n\t\t\tif (pbd)  // it is an elastic solid domain\n\t\t\t{\n\t\t\t\tFESolidMaterial* pme = dynamic_cast<FESolidMaterial*>(pbd->GetMaterial());\n\n\t\t\t\t// loop over all the elements\n\t\t\t\tfor (int iel = 0; iel < pbd->Elements(); ++iel)\n\t\t\t\t{\n\t\t\t\t\tFESolidElement& el = pbd->Element(iel);\n\t\t\t\t\tpbd->UnpackLM(el, lm);\n\n\t\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\t\tint neln = el.Nodes();\n\n\t\t\t\t\tme.resize(neln, neln);\n\t\t\t\t\tme.zero();\n\n\t\t\t\t\t// create the element mass matrix\n\t\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\t\tdouble d = pme->Density(mp);\n\t\t\t\t\t\tdouble detJ0 = pbd->detJ0(el, n)*el.GaussWeights()[n];\n\n\t\t\t\t\t\tdouble* H = el.H(n);\n\t\t\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble kab = H[i] * H[j] * detJ0*d;\n\t\t\t\t\t\t\t\tme[i][j] += kab;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// reduce to a lumped mass vector and add up the total\n\t\t\t\t\tel_lumped_mass.assign(3 * neln, 0.0);\n\t\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble kab = me[i][j];\n\t\t\t\t\t\t\tel_lumped_mass[3 * i] += kab;\n\t\t\t\t\t\t\tel_lumped_mass[3 * i + 1] += kab;\n\t\t\t\t\t\t\tel_lumped_mass[3 * i + 2] += kab;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\t\tM.Assemble(el.m_node, lm, el_lumped_mass);\n\t\t\t\t} // loop over elements\n\t\t\t}\n\t\t\telse if (dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(nd)))\n\t\t\t{\n\t\t\t\tFEElasticShellDomain* psd = dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(nd));\n\t\t\t\tFESolidMaterial* pme = dynamic_cast<FESolidMaterial*>(psd->GetMaterial());\n\t\t\t\t// loop over all the elements\n\t\t\t\tfor (int iel = 0; iel < psd->Elements(); ++iel)\n\t\t\t\t{\n\t\t\t\t\tFEShellElement& el = psd->Element(iel);\n\t\t\t\t\tpsd->UnpackLM(el, lm);\n\n\t\t\t\t\t// create the element's stiffness matrix\n\t\t\t\t\tFEElementMatrix ke(el);\n\t\t\t\t\tint neln = el.Nodes();\n\t\t\t\t\tint ndof = 6 * el.Nodes();\n\t\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\t\tke.zero();\n\n\t\t\t\t\t// calculate inertial stiffness\n\t\t\t\t\tpsd->ElementMassMatrix(el, ke, 1.0);\n\n\t\t\t\t\t// reduce to a lumped mass vector and add up the total\n\t\t\t\t\tel_lumped_mass.assign(ndof, 0.0);\n\t\t\t\t\tfor (int i = 0; i < ndof; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int j = 0; j < ndof; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble kab = ke[i][j];\n\t\t\t\t\t\t\tel_lumped_mass[i] += kab;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\t\tM.Assemble(el.m_node, lm, el_lumped_mass);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (dynamic_cast<FELinearTrussDomain*>(&mesh.Domain(nd)))\n\t\t\t{\n\t\t\t\tFELinearTrussDomain* ptd = dynamic_cast<FELinearTrussDomain*>(&mesh.Domain(nd));\n\n\t\t\t\t// loop over all the elements\n\t\t\t\tfor (int iel = 0; iel < ptd->Elements(); ++iel)\n\t\t\t\t{\n\t\t\t\t\tFETrussElement& el = ptd->Element(iel);\n\t\t\t\t\tptd->UnpackLM(el, lm);\n\n\t\t\t\t\t// create the element's stiffness matrix\n\t\t\t\t\tFEElementMatrix ke(el);\n\t\t\t\t\tint neln = el.Nodes();\n\t\t\t\t\tke.resize(neln, neln);\n\t\t\t\t\tke.zero();\n\n\t\t\t\t\t// calculate inertial stiffness\n\t\t\t\t\tptd->ElementMassMatrix(el, ke);\n\n\t\t\t\t\t// reduce to a lumped mass vector and add up the total\n\t\t\t\t\tel_lumped_mass.assign(3 * neln, 0.0);\n\t\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble kab = ke[i][j];\n\t\t\t\t\t\t\tel_lumped_mass[3 * i    ] += kab;\n\t\t\t\t\t\t\tel_lumped_mass[3 * i + 1] += kab;\n\t\t\t\t\t\t\tel_lumped_mass[3 * i + 2] += kab;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\t\tM.Assemble(el.m_node, lm, el_lumped_mass);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (dynamic_cast<FEElasticTrussDomain*>(&mesh.Domain(nd)))\n\t\t\t{\n\t\t\t\tFEElasticTrussDomain* ptd = dynamic_cast<FEElasticTrussDomain*>(&mesh.Domain(nd));\n\n\t\t\t\t// loop over all the elements\n\t\t\t\tfor (int iel = 0; iel < ptd->Elements(); ++iel)\n\t\t\t\t{\n\t\t\t\t\tFETrussElement& el = ptd->Element(iel);\n\t\t\t\t\tptd->UnpackLM(el, lm);\n\n\t\t\t\t\t// create the element's stiffness matrix\n\t\t\t\t\tFEElementMatrix ke(el);\n\t\t\t\t\tint neln = el.Nodes();\n\t\t\t\t\tke.resize(neln, neln);\n\t\t\t\t\tke.zero();\n\n\t\t\t\t\t// calculate inertial stiffness\n\t\t\t\t\tptd->ElementMassMatrix(el, ke);\n\n\t\t\t\t\t// reduce to a lumped mass vector and add up the total\n\t\t\t\t\tel_lumped_mass.assign(3 * neln, 0.0);\n\t\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble kab = ke[i][j];\n\t\t\t\t\t\t\tel_lumped_mass[3 * i    ] += kab;\n\t\t\t\t\t\t\tel_lumped_mass[3 * i + 1] += kab;\n\t\t\t\t\t\t\tel_lumped_mass[3 * i + 2] += kab;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\t\tM.Assemble(el.m_node, lm, el_lumped_mass);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n//\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\telse if (m_mass_lumping == HRZ_LUMPING)\n\t{\n\t\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t\t{\n\t\t\t// check whether it is a solid domain\n\t\t\tFEElasticSolidDomain* pbd = dynamic_cast<FEElasticSolidDomain*>(&mesh.Domain(nd));\n\t\t\tif (pbd)  // it is an elastic solid domain\n\t\t\t{\n\t\t\t\tFESolidMaterial* pme = dynamic_cast<FESolidMaterial*>(pbd->GetMaterial());\n\n\t\t\t\t// loop over all the elements\n\t\t\t\tfor (int iel = 0; iel < pbd->Elements(); ++iel)\n\t\t\t\t{\n\t\t\t\t\tFESolidElement& el = pbd->Element(iel);\n\t\t\t\t\tpbd->UnpackLM(el, lm);\n\n\t\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\t\tint neln = el.Nodes();\n\n\t\t\t\t\tme.resize(neln, neln);\n\t\t\t\t\tme.zero();\n\n\t\t\t\t\t// calculate the element mass matrix (and element mass).\n\t\t\t\t\tdouble Me = 0.0;\n\t\t\t\t\tdouble* w = el.GaussWeights();\n\t\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\t\tdouble d = pme->Density(mp);\n\t\t\t\t\t\tdouble detJ0 = pbd->detJ0(el, n)*el.GaussWeights()[n];\n\t\t\t\t\t\tMe += d * detJ0 * w[n];\n\n\t\t\t\t\t\tdouble* H = el.H(n);\n\t\t\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble kab = H[i] * H[j] * detJ0*d;\n\t\t\t\t\t\t\t\tme[i][j] += kab;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// calculate sum of diagonals\n\t\t\t\t\tdouble S = 0.0;\n\t\t\t\t\tfor (int i = 0; i < neln; ++i) S += me[i][i];\n\n\t\t\t\t\t// reduce to a lumped mass vector and add up the total\n\t\t\t\t\tel_lumped_mass.assign(3 * neln, 0.0);\n\t\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble mab = me[i][i] * Me / S;\n\t\t\t\t\t\tel_lumped_mass[3 * i    ] = mab;\n\t\t\t\t\t\tel_lumped_mass[3 * i + 1] = mab;\n\t\t\t\t\t\tel_lumped_mass[3 * i + 2] = mab;\n\t\t\t\t\t}\n\n\t\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\t\tM.Assemble(el.m_node, lm, el_lumped_mass);\n\t\t\t\t} // loop over elements\n\t\t\t}\n\t\t\telse if(dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(nd)))\n\t\t\t{\n\t\t\t\tFEElasticShellDomain* psd = dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(nd));\n\t\t\t\tFESolidMaterial* pme = dynamic_cast<FESolidMaterial*>(psd->GetMaterial());\n\t\t\t\t// loop over all the elements\n\t\t\t\tfor (int iel = 0; iel < psd->Elements(); ++iel)\n\t\t\t\t{\n\t\t\t\t\tFEShellElement& el = psd->Element(iel);\n\t\t\t\t\tpsd->UnpackLM(el, lm);\n\n\t\t\t\t\t// create the element's stiffness matrix\n\t\t\t\t\tFEElementMatrix ke(el);\n\t\t\t\t\tint neln = el.Nodes();\n\t\t\t\t\tint ndof = 6 * el.Nodes();\n\t\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\t\tke.zero();\n\n\t\t\t\t\t// calculate inertial stiffness\n\t\t\t\t\tpsd->ElementMassMatrix(el, ke, 1.0);\n\n\t\t\t\t\t// calculate the element mass\n\t\t\t\t\tdouble Me = 0.0;\n\t\t\t\t\tdouble* w = el.GaussWeights();\n\t\t\t\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\t\tdouble d = pme->Density(mp);\n\t\t\t\t\t\tdouble detJ0 = psd->detJ0(el, n) * el.GaussWeights()[n];\n\t\t\t\t\t\tMe += d * detJ0 * w[n];\n\t\t\t\t\t}\n\n\t\t\t\t\t// calculate sum of diagonals\n\t\t\t\t\tdouble S = 0.0;\n\t\t\t\t\tfor (int i = 0; i < ndof; ++i) S += ke[i][i] / 3.0;\n\n\t\t\t\t\t// reduce to a lumped mass vector and add up the total\n\t\t\t\t\tel_lumped_mass.assign(ndof, 0.0);\n\t\t\t\t\tfor (int i = 0; i < ndof; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble mab = ke[i][i] * Me / S;\n\t\t\t\t\t\tel_lumped_mass[i] = mab;\n\t\t\t\t\t}\n\n\t\t\t\t\t// assemble element matrix into inv_mass vector \n\t\t\t\t\tM.Assemble(el.m_node, lm, el_lumped_mass);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// TODO: we can only do solid domains right now.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\t// we need the inverse of the lumped masses later\n\t// Also, make sure the lumped masses are positive.\n\tfor (int i = 0; i < massVector.size(); ++i)\n\t{\n\t\tm_data[i].mi = 0;\n\t\tif (massVector[i] != 0.0) m_data[i].mi = 1.0 / massVector[i];\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize equations\nbool FEExplicitSolidSolver::InitEquations()\n{\n\tif (FESolver::InitEquations() == false) return false;\n\n\t// allocate rigid body equation numbers\n\tm_neq = m_rigidSolver.InitEquations(m_neq);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEExplicitSolidSolver::Init()\n{\n\tif (FESolver::Init() == false) return false;\n\n\tFEMechModel& fem = dynamic_cast<FEMechModel&>(*GetFEModel());\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// set the dynamic update flag only if we are running a dynamic analysis\n\t// NOTE: I don't think we need to set the dynamic update flag, in fact, we should \n\t//\t\t turn it off, since it's on by default, and it incurs a significant performance overhead\n//\tbool b = (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC ? true : false);\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEElasticSolidDomain* d = dynamic_cast<FEElasticSolidDomain*>(&mesh.Domain(i));\n\t\tFEElasticShellDomain* s = dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(i));\n\t\tif (d) d->SetDynamicUpdateFlag(false);\n\t\tif (s) s->SetDynamicUpdateFlag(false);\n\t}\n\n\t// get nr of equations\n\tint neq = m_neq;\n\n\t// allocate vectors\n\tm_data.resize(neq);\n\tm_Rt.assign(neq, 0);\n\tm_Fr.assign(neq, 0);\n\tm_Ut.assign(neq, 0);\n\tm_ui.assign(neq, 0);\n\n\tfem.Update();\n\n\t// we need to fill the total displacement vector m_Ut\n\tgather(m_Ut, mesh, m_dofU[0]);\n\tgather(m_Ut, mesh, m_dofU[1]);\n\tgather(m_Ut, mesh, m_dofU[2]);\n\tgather(m_Ut, mesh, m_dofQ[0]);\n\tgather(m_Ut, mesh, m_dofQ[1]);\n\tgather(m_Ut, mesh, m_dofQ[2]);\n\tgather(m_Ut, mesh, m_dofSU[0]);\n\tgather(m_Ut, mesh, m_dofSU[1]);\n\tgather(m_Ut, mesh, m_dofSU[2]);\n\n\t// calculate the inverse mass vector for the explicit analysis\n\tif (CalculateMassMatrix() == false)\n\t{\n\t\tfeLogError(\"Failed building mass matrix.\");\n\t\treturn false;\n\t}\n\n\t// calculate the initial acceleration\n\t// (Only when the totiter == 0, in case of a restart)\n\tif (fem.GetCurrentStep()->m_ntotiter == 0)\n\t{\n\t\t// Calculate initial residual to be used on the first time step\n\t\tif (Residual(m_Rt) == false) return false;\n\n\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tint n;\n\t\t\tif ((n = node.m_ID[m_dofU[0]]) >= 0) { m_data[n].a = node.m_at.x = m_Rt[n] * m_data[n].mi; }\n\t\t\tif ((n = node.m_ID[m_dofU[1]]) >= 0) { m_data[n].a = node.m_at.y = m_Rt[n] * m_data[n].mi; }\n\t\t\tif ((n = node.m_ID[m_dofU[2]]) >= 0) { m_data[n].a = node.m_at.z = m_Rt[n] * m_data[n].mi; }\n\n\t\t\tif ((n = node.m_ID[m_dofSU[0]]) >= 0) { m_data[n].a = m_Rt[n] * m_data[n].mi; node.set(m_dofSA[0], m_data[n].a);}\n\t\t\tif ((n = node.m_ID[m_dofSU[1]]) >= 0) { m_data[n].a = m_Rt[n] * m_data[n].mi; node.set(m_dofSA[1], m_data[n].a);}\n\t\t\tif ((n = node.m_ID[m_dofSU[2]]) >= 0) { m_data[n].a = m_Rt[n] * m_data[n].mi; node.set(m_dofSA[2], m_data[n].a);}\n\t\t}\n\n\t\t// do rigid bodies\n\t\tfor (int i = 0; i < fem.RigidBodies(); ++i)\n\t\t{\n\t\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\t\tvec3d Fn, Mn;\n\t\t\tint n;\n\t\t\tif ((n = rb.m_LM[0]) >= 0) { Fn.x = m_Rt[n]; }\n\t\t\tif ((n = rb.m_LM[1]) >= 0) { Fn.y = m_Rt[n]; }\n\t\t\tif ((n = rb.m_LM[2]) >= 0) { Fn.z = m_Rt[n]; }\n\t\t\tif ((n = rb.m_LM[3]) >= 0) { Mn.x = m_Rt[n]; }\n\t\t\tif ((n = rb.m_LM[4]) >= 0) { Mn.y = m_Rt[n]; }\n\t\t\tif ((n = rb.m_LM[5]) >= 0) { Mn.z = m_Rt[n]; }\n\n\t\t\trb.m_at = Fn / rb.m_mass;\n\n\t\t\t// TODO: angular acceleration\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FEExplicitSolidSolver::Update(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// update kinematics\n\tUpdateKinematics(ui);\n\n\t// update element stresses\n\tfem.Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FEExplicitSolidSolver::UpdateKinematics(vector<double>& ui)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update rigid bodies\n\tUpdateRigidBodies(ui);\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n#pragma omp parallel for\n\tfor (int i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ut[i];\n\n\t// update flexible nodes\n\t// translational dofs\n\tscatter3(U, mesh, m_dofU[0], m_dofU[1], m_dofU[2]);\n\n\t// rotational dofs\n\t// TODO: Commenting this out, since this is only needed for the old shells, which I'm not sure\n\t//       if they would work with the explicit solver anyways.\n//\tscatter(U, mesh, m_dofQ[0]);\n//\tscatter(U, mesh, m_dofQ[1]);\n//\tscatter(U, mesh, m_dofQ[2]);\n\n\t// shell displacement\n\tscatter3(U, mesh, m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\n\t// make sure the prescribed displacements are fullfilled\n\tint ndis = fem.BoundaryConditions();\n\tfor (int i=0; i<ndis; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Update();\n\t}\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// Update the spatial nodal positions\n\t// Don't update rigid nodes since they are already updated\n#pragma omp parallel for\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid == -1)\n\t\t\tnode.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the rigid body data\nvoid FEExplicitSolidSolver::UpdateRigidBodies(vector<double>& ui)\n{\n\t// get the number of rigid bodies\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tconst int NRB = fem.RigidBodies();\n\tif (NRB == 0) return;\n\n\t// first calculate the rigid body displacement increments\n\tfor (int i=0; i<NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tint *lm = RB.m_LM;\n\t\tdouble* du = RB.m_du;\n\n\t\tif (RB.m_prb == 0)\n\t\t{\n\t\t\tfor (int j=0; j<6; ++j)\n\t\t\t{\n\t\t\t\tdu[j] = (lm[j] >=0 ? ui[lm[j]] : 0);\n\t\t\t}\n\t\t}\n\t}\n\n\t// for prescribed displacements, the displacement increments are evaluated differently\n\t// TODO: Is this really necessary? Why can't the ui vector contain the correct values?\n\tfor (int i = 0; i < NRB; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tFERigidPrescribedBC* dc = RB.m_pDC[j];\n\t\t\tif (dc)\n\t\t\t{\n\t\t\t\tint I = dc->GetBC();\n\t\t\t\tRB.m_du[I] = dc->Value() - RB.m_Up[I];\n\t\t\t}\n\t\t}\n\t}\n\n\t// update the rigid bodies\n\tfor (int i=0; i<NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tdouble* du = RB.m_du;\n\n\t\t// This is the \"old\" update algorithm which has some issues. It does not produce the correct\n\t\t// rigid body orientation when the rotational degrees of freedom are prescribed.\n\t\tRB.m_rt.x = RB.m_rp.x + du[0];\n\t\tRB.m_rt.y = RB.m_rp.y + du[1];\n\t\tRB.m_rt.z = RB.m_rp.z + du[2];\n\n\t\tvec3d r = vec3d(du[3], du[4], du[5]);\n\t\tdouble w = sqrt(r.x*r.x + r.y*r.y + r.z*r.z);\n\t\tquatd dq = quatd(w, r);\n\n\t\tquatd Q = dq*RB.m_qp;\n\t\tQ.MakeUnit();\n\t\tRB.SetRotation(Q);\n\n\t\tif (RB.m_prb) du = RB.m_dul;\n\t\tRB.m_Ut[0] = RB.m_Up[0] + du[0];\n\t\tRB.m_Ut[1] = RB.m_Up[1] + du[1];\n\t\tRB.m_Ut[2] = RB.m_Up[2] + du[2];\n\t\tRB.m_Ut[3] = RB.m_Up[3] + du[3];\n\t\tRB.m_Ut[4] = RB.m_Up[4] + du[4];\n\t\tRB.m_Ut[5] = RB.m_Up[5] + du[5];\n\t}\n\n\t// we need to update the position of rigid nodes\n\tfem.UpdateRigidMesh();\n\n\t// Since the rigid nodes are repositioned we need to update the displacement DOFS\n\tFEMesh& mesh = fem.GetMesh();\n\tint N = mesh.Nodes();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tvec3d ut = node.m_rt - node.m_r0;\n\t\t\tnode.set_vec3d(m_dofU[0], m_dofU[1], m_dofU[2], ut);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEExplicitSolidSolver::Serialize(DumpStream& ar)\n{\n\tFESolver::Serialize(ar);\n\tar & m_nrhs & m_niter & m_nref & m_ntotref & m_naug & m_neq & m_nreq;\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_Ut & m_Rt;\n\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tint N = (int)m_data.size();\n\t\t\tar << N;\n\t\t\tfor (int i = 0; i < N; ++i)\n\t\t\t{\n\t\t\t\tData& d = m_data[i];\n\t\t\t\tar << d.v << d.a << d.mi;\n\t\t\t}\n\t\t}\n\t\telse if (ar.IsLoading())\n\t\t{\n\t\t\tint N = 0;\n\t\t\tar >> N;\n\t\t\tm_data.resize(N);\n\t\t\tfor (int i = 0; i < N; ++i)\n\t\t\t{\n\t\t\t\tData& d = m_data[i];\n\t\t\t\tar >> d.v >> d.a >> d.mi;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//!  This function mainly calls the DoSolve routine \n//!  and deals with exceptions that require the immediate termination of\n//!\t the solution eg negative Jacobians.\nbool FEExplicitSolidSolver::SolveStep()\n{\n\tbool bret;\n\n\ttry\n\t{\n\t\t// let's try to solve the step\n\t\tbret = DoSolve();\n\t}\n\tcatch (NegativeJacobian e)\n\t{\n\t\t// A negative jacobian was detected\n\t\tfeLogError(\"Negative jacobian was detected at element %d at gauss point %d\\njacobian = %lg\\n\", e.m_iel, e.m_ng+1, e.m_vol);\n\t\treturn false;\n\t}\n\tcatch (MaxStiffnessReformations) // shouldn't happen for an explicit analysis!\n\t{\n\t\t// max nr of reformations is reached\n\t\tfeLogError(\"Max nr of reformations reached.\");\n\t\treturn false;\n\t}\n\tcatch (ForceConversion)\n\t{\n\t\t// user forced conversion of problem\n\t\tfeLogWarning(\"User forced conversion.\\nSolution might not be stable.\");\n\t\treturn true;\n\t}\n\tcatch (IterationFailure)\n\t{\n\t\t// user caused a forced iteration failure\n\t\tfeLogWarning(\"User forced iteration failure.\");\n\t\treturn false;\n\t}\n\tcatch (ZeroLinestepSize) // shouldn't happen for an explicit analysis!\n\t{\n\t\t// a zero line step size was detected\n\t\tfeLogError(\"Zero line step size.\");\n\t\treturn false;\n\t}\n\tcatch (EnergyDiverging) // shouldn't happen for an explicit analysis!\n\t{\n\t\t// problem was diverging after stiffness reformation\n\t\tfeLogError(\"Problem diverging uncontrollably.\");\n\t\treturn false;\n\t}\n\tcatch (FEMultiScaleException)\n\t{\n\t\t// the RVE problem didn't solve\n\t\tfeLogError(\"The RVE problem has failed. Aborting macro run.\");\n\t\treturn false;\n\t}\n\n\treturn bret;\n}\n\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the time step. \nvoid FEExplicitSolidSolver::PrepStep()\n{\n\tint i, j;\n\n\t// initialize counters\n\tm_niter = 0;\t// nr of iterations\n\tm_nrhs  = 0;\t// nr of RHS evaluations\n\tm_nref  = 0;\t// nr of stiffness reformations\n\tm_ntotref = 0;\n\tm_naug  = 0;\t// nr of augmentations\n\n\t// store previous mesh state\n\t// we need them for velocity and acceleration calculations\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFEMesh& mesh = fem.GetMesh();\n#pragma omp parallel for\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& ni = mesh.Node(i);\n\t\tni.m_rp = ni.m_rt;\n\t\tni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tni.m_ap = ni.m_at;\n\t\tni.UpdateValues();\n\t}\n\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// apply prescribed displacements\n\t// we save the prescribed displacements increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint neq = m_neq;\n\tint nbc = fem.BoundaryConditions();\n\tfor (i=0; i<nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.PrepStep(ui);\n\t}\n\n\t// initialize rigid bodies\n\tint NO = fem.RigidBodies();\n\tfor (i=0; i<NO; ++i) fem.GetRigidBody(i)->Init();\n\n\t// calculate local rigid displacements\n\tfor (i = 0; i < NO; ++i)\n\t{\n\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tFERigidPrescribedBC* dc = rb.m_pDC[j];\n\t\t\tif (dc)\n\t\t\t{\n\t\t\t\tint I = dc->GetBC();\n\t\t\t\trb.m_dul[I] = dc->Value() - rb.m_Ut[I];\n\t\t\t}\n\t\t}\n\t}\n\n\t// calculate global rigid displacements\n\tfor (i=0; i<NO; ++i)\n\t{\n\t\tFERigidBody* prb = fem.GetRigidBody(i);\n\t\tif (prb)\n\t\t{\n\t\t\tFERigidBody& RB = *prb;\n\t\t\tif (RB.m_prb == 0)\n\t\t\t{\n\t\t\t\tfor (j=0; j<6; ++j) RB.m_du[j] = RB.m_dul[j];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble* dul = RB.m_dul;\n\t\t\t\tvec3d dr = vec3d(dul[0], dul[1], dul[2]);\n\t\t\t\t\n\t\t\t\tvec3d v = vec3d(dul[3], dul[4], dul[5]);\n\t\t\t\tdouble w = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);\n\t\t\t\tquatd dq = quatd(w, v);\n\n\t\t\t\tFERigidBody* pprb = RB.m_prb;\n\n\t\t\t\tvec3d r0 = RB.m_rt;\n\t\t\t\tquatd Q0 = RB.GetRotation();\n\n\t\t\t\tdr = Q0*dr;\n\t\t\t\tdq = Q0*dq*Q0.Inverse();\n\n\t\t\t\twhile (pprb)\n\t\t\t\t{\n\t\t\t\t\tvec3d r1 = pprb->m_rt;\n\t\t\t\t\tdul = pprb->m_dul;\n\n\t\t\t\t\tquatd Q1 = pprb->GetRotation();\n\t\t\t\t\t\n\t\t\t\t\tdr = r0 + dr - r1;\n\n\t\t\t\t\t// grab the parent's local displacements\n\t\t\t\t\tvec3d dR = vec3d(dul[0], dul[1], dul[2]);\n\t\t\t\t\tv = vec3d(dul[3], dul[4], dul[5]);\n\t\t\t\t\tw = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);\n\t\t\t\t\tquatd dQ = quatd(w, v);\n\n\t\t\t\t\tdQ = Q1*dQ*Q1.Inverse();\n\n\t\t\t\t\t// update global displacements\n\t\t\t\t\tquatd Qi = Q1.Inverse();\n\t\t\t\t\tdr = dR + r1 + dQ*dr - r0;\n\t\t\t\t\tdq = dQ*dq;\n\n\t\t\t\t\t// move up in the chain\n\t\t\t\t\tpprb = pprb->m_prb;\n\t\t\t\t\tQ0 = Q1;\n\t\t\t\t}\n\n\t\t\t\t// set global displacements\n\t\t\t\tdouble* du = RB.m_du;\n\n\t\t\t\tdu[0] = dr.x;\n\t\t\t\tdu[1] = dr.y;\n\t\t\t\tdu[2] = dr.z;\n\n\t\t\t\tv = dq.GetVector();\n\t\t\t\tw = dq.GetAngle();\n\t\t\t\tdu[3] = w*v.x;\n\t\t\t\tdu[4] = w*v.y;\n\t\t\t\tdu[5] = w*v.z;\n\t\t\t}\n\t\t}\n\t}\n\n\t// store rigid displacements in Ui vector\n\tfor (i=0; i<NO; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tfor (j=0; j<6; ++j)\n\t\t{\n\t\t\tint I = -RB.m_LM[j]-2;\n\t\t\tif (I >= 0) ui[I] = RB.m_du[j];\n\t\t}\n\t}\n\n\t// intialize material point data\n\tfor (i=0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n\n\t// NOTE: Commenting this out since I don't think anything needs to be updated here. \n\t//       This also halves the number of update calls, so gives a performance boost. \n//\tfem.Update();\n}\n\n//-----------------------------------------------------------------------------\nbool FEExplicitSolidSolver::DoSolve()\n{\n\t// Get the current step\n\tFEMechModel& fem = dynamic_cast<FEMechModel&>(*GetFEModel());\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t// prepare for solve\n\tPrepStep();\n\n//\tfeLog(\" %d\\n\", m_niter+1);\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\tint N = mesh.Nodes(); // this is the total number of nodes in the mesh\n\tdouble dt = fem.GetTime().timeIncrement;\n\n\t// collect accelerations, velocities, displacements\n\t// NOTE: I don't think this is necessary\n/*\n#pragma omp parallel for shared(mesh)\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tvec3d vt = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tint n;\n\t\tif ((n = node.m_ID[m_dofU[0]]) >= 0) { m_vn[n] = vt.x; m_an[n] = node.m_at.x; }\n\t\tif ((n = node.m_ID[m_dofU[1]]) >= 0) { m_vn[n] = vt.y; m_an[n] = node.m_at.y; }\n\t\tif ((n = node.m_ID[m_dofU[2]]) >= 0) { m_vn[n] = vt.z; m_an[n] = node.m_at.z; }\n\n\t\tif ((n = node.m_ID[m_dofSU[0]]) >= 0) { m_vn[n] = node.get(m_dofSV[0]); m_an[n] = node.get(m_dofSA[0]); }\n\t\tif ((n = node.m_ID[m_dofSU[1]]) >= 0) { m_vn[n] = node.get(m_dofSV[1]); m_an[n] = node.get(m_dofSA[1]); }\n\t\tif ((n = node.m_ID[m_dofSU[2]]) >= 0) { m_vn[n] = node.get(m_dofSV[2]); m_an[n] = node.get(m_dofSA[2]); }\n\t}\n*/\n\n\t// do rigid bodies\n\tfor (int i = 0; i < fem.RigidBodies(); ++i)\n\t{\n\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\tint n;\n\t\tif ((n = rb.m_LM[0]) >= 0) { m_data[n].v = rb.m_vt.x; m_data[n].a = rb.m_at.x; }\n\t\tif ((n = rb.m_LM[1]) >= 0) { m_data[n].v = rb.m_vt.y; m_data[n].a = rb.m_at.y; }\n\t\tif ((n = rb.m_LM[2]) >= 0) { m_data[n].v = rb.m_vt.z; m_data[n].a = rb.m_at.z; }\n\t\t\n\t\t// convert to rigid frame\n\t\tquatd Q = rb.GetRotation();\n\t\tquatd Qi = Q.Inverse();\n\t\tvec3d Wn = Qi * rb.m_wt;\n\t\tvec3d An = Qi * rb.m_alt;\n\t\tif ((n = rb.m_LM[3]) >= 0) { m_data[n].v = Wn.x; m_data[n].a = An.x; }\n\t\tif ((n = rb.m_LM[4]) >= 0) { m_data[n].v = Wn.y; m_data[n].a = An.y; }\n\t\tif ((n = rb.m_LM[5]) >= 0) { m_data[n].v = Wn.z; m_data[n].a = An.z; }\n\t}\n\n\tdouble Dnorm = 0.0;\n#pragma omp parallel for reduction(+: Dnorm)\n\tfor (int i = 0; i < m_neq; ++i)\n\t{\n\t\t// velocity predictor\n\t\tm_data[i].v += m_data[i].a * dt*0.5;\n\n\t\t// update displacements\n\t\tm_ui[i] = dt * m_data[i].v;\n\n\t\t// update norm\n\t\tDnorm += m_ui[i] * m_ui[i];\n\t}\n\tDnorm = sqrt(Dnorm);\n\tfeLog(\"\\t displacement norm : %lg\\n\", Dnorm);\n\n\t// the update is done in the spatial frame, so we need to update\n\t// rigid body rotation increment\n\tfor (int i = 0; i < fem.RigidBodies(); ++i)\n\t{\n\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\tquatd Q = rb.GetRotation();\n\t\tvec3d dq(0, 0, 0);\n\t\tint n;\n\t\tif ((n = rb.m_LM[3]) >= 0) { dq.x = m_ui[n]; }\n\t\tif ((n = rb.m_LM[4]) >= 0) { dq.y = m_ui[n]; }\n\t\tif ((n = rb.m_LM[5]) >= 0) { dq.z = m_ui[n]; }\n\t\tvec3d qt = Q * dq;\n\t\tif ((n = rb.m_LM[3]) >= 0) { m_ui[n] = qt.x; }\n\t\tif ((n = rb.m_LM[4]) >= 0) { m_ui[n] = qt.y; }\n\t\tif ((n = rb.m_LM[5]) >= 0) { m_ui[n] = qt.z; }\n\t}\n\n\tUpdate(m_ui);\n\n\t// evaluate acceleration\n\tResidual(m_Rt);\n\n\tdouble Rnorm = 0.0;\n#pragma omp parallel shared(Rnorm)\n\t{\n#pragma omp for reduction(+: Rnorm) nowait\n\t\tfor (int i = 0; i < m_neq; ++i)\n\t\t{\n\t\t\tRnorm += m_Rt[i] * m_Rt[i];\n\t\t}\n\n#pragma omp for nowait\n\t\tfor (int i = 0; i < m_neq; ++i)\n\t\t{\n\t\t\t// update total displacement\n\t\t\tm_Ut[i] += m_ui[i];\n\t\t}\n\n#pragma omp for\n\t\tfor (int i = 0; i < m_neq; ++i)\n\t\t{\n\t\t\tm_data[i].a = m_Rt[i] * m_data[i].mi;\n\n\t\t\t// update velocity\n\t\t\tm_data[i].v = m_dyn_damping * (m_data[i].v + m_data[i].a * dt * 0.5);\n\t\t}\n\n\t\t// scatter velocity and accelerations\n#pragma omp for nowait\n\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tint n;\n\t\t\tif ((n = node.m_ID[m_dofU[0]]) >= 0) { node.set(m_dofV[0], m_data[n].v); node.m_at.x = m_data[n].a; }\n\t\t\tif ((n = node.m_ID[m_dofU[1]]) >= 0) { node.set(m_dofV[1], m_data[n].v); node.m_at.y = m_data[n].a; }\n\t\t\tif ((n = node.m_ID[m_dofU[2]]) >= 0) { node.set(m_dofV[2], m_data[n].v); node.m_at.z = m_data[n].a; }\n\n\t\t\tif ((n = node.m_ID[m_dofSU[0]]) >= 0) { node.set(m_dofSV[0], m_data[n].v); node.set(m_dofSA[0], m_data[n].a); }\n\t\t\tif ((n = node.m_ID[m_dofSU[1]]) >= 0) { node.set(m_dofSV[1], m_data[n].v); node.set(m_dofSA[1], m_data[n].a); }\n\t\t\tif ((n = node.m_ID[m_dofSU[2]]) >= 0) { node.set(m_dofSV[2], m_data[n].v); node.set(m_dofSA[2], m_data[n].a); }\n\t\t}\n\t}\n\n\tRnorm = sqrt(Rnorm);\n\tfeLog(\"\\t force vector norm : %lg\\n\", Rnorm);\n\n\t// do rigid bodies\n\tfor (int i = 0; i < fem.RigidBodies(); ++i)\n\t{\n\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\tquatd Q = rb.GetRotation();\n\t\tquatd Qi = Q.Inverse();\n\n\t\t// get the force and moment\n\t\tvec3d Fn(0, 0, 0), Mn;\n\t\tint n;\n\t\tif ((n = rb.m_LM[0]) >= 0) Fn.x = m_Rt[n];\n\t\tif ((n = rb.m_LM[1]) >= 0) Fn.y = m_Rt[n];\n\t\tif ((n = rb.m_LM[2]) >= 0) Fn.z = m_Rt[n];\n\t\tif ((n = rb.m_LM[3]) >= 0) Mn.x = m_Rt[n];\n\t\tif ((n = rb.m_LM[4]) >= 0) Mn.y = m_Rt[n];\n\t\tif ((n = rb.m_LM[5]) >= 0) Mn.z = m_Rt[n];\n\n\t\t// convert to rigid frame\n\t\tMn = Qi * Mn;\n\n\t\t// linear momentum update\n\t\tvec3d An = Fn / rb.m_mass;\n\t\trb.m_at = Q*An;\n\n\t\tvec3d Vn(0,0,0);\n\t\tif ((n = rb.m_LM[0]) >= 0) Vn.x = m_dyn_damping * (m_data[n].v + An.x * dt * 0.5);\n\t\tif ((n = rb.m_LM[1]) >= 0) Vn.y = m_dyn_damping * (m_data[n].v + An.y * dt * 0.5);\n\t\tif ((n = rb.m_LM[2]) >= 0) Vn.z = m_dyn_damping * (m_data[n].v + An.z * dt * 0.5);\n\t\trb.m_vt = Q * Vn;\n\n\t\t// angular momentum update\n\t\t// NOTE: This currently adds a_{n}, not a_{n+1}, because in order\n\t\t//       to evaluate a_{n+1}, I need W_{n+1}. This looks like a nonlinear problem\n\t\t//       so probably need to do something else here. \n\t\tvec3d Wn(0,0,0);\n\t\tif ((n = rb.m_LM[3]) >= 0) Wn.x = m_dyn_damping * (m_data[n].v + m_data[n].a * dt*0.5);\n\t\tif ((n = rb.m_LM[4]) >= 0) Wn.y = m_dyn_damping * (m_data[n].v + m_data[n].a * dt*0.5);\n\t\tif ((n = rb.m_LM[5]) >= 0) Wn.z = m_dyn_damping * (m_data[n].v + m_data[n].a * dt*0.5);\n\t\trb.m_wt = Q * Wn;\n\n\t\tmat3ds I0 = rb.m_moi;\n\t\tmat3ds I0inv = I0.inverse();\n\t\tvec3d Pn = I0inv * (Mn - (Wn ^ (I0 * Wn)));\n\t\trb.m_alt = Q * Pn;\n\n\t\t// update some other stuff\n\t\tmat3d R = Q.RotationMatrix();\n\t\tmat3d It = R * I0 * R.transpose();\n\t\trb.m_ht = It * rb.m_wt;\n\t}\n\n\t// increase iteration number\n\tm_niter++;\n\n\t// do minor iterations callbacks\n\tfem.DoCallback(CB_MINOR_ITERS);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEExplicitSolidSolver::Residual(vector<double>& R)\n{\n\tTRACK_TIME(Timer_Residual);\n\n\t// get the time information\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// initialize residual with concentrated nodal loads\n\tzero(R);\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup the global vector\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tint NRB = fem.RigidBodies();\n\tfor (int i=0; i<NRB; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tRB.m_Fr = RB.m_Mr = vec3d(0,0,0);\n\t}\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// calculate the internal (stress) forces\n\tfor (int i=0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n\t\tdom.InternalForces(RHS);\n\t}\n\n\t// calculate forces due to model loads\n\tint nml = fem.ModelLoads();\n\tfor (int i=0; i<nml; ++i)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(i);\n\t\tif (pml->IsActive()) pml->LoadVector(RHS);\n\t}\n\n\t// calculate contact forces\n\tif (fem.SurfacePairConstraints() > 0)\n\t{\n\t\tContactForces(RHS);\n\t}\n\n\t// calculate nonlinear constraint forces\n\t// note that these are the linear constraints\n\t// enforced using the augmented lagrangian\n\tNonLinearConstraintForces(RHS, tp);\n\n\t// set the nodal reaction forces\n\t// TODO: Is this a good place to do this?\n#pragma omp parallel for\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\t\tnode.set_load(m_dofSU[0], 0);\n\t\tnode.set_load(m_dofSU[1], 0);\n\t\tnode.set_load(m_dofSU[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\n\t\tif ((n = -node.m_ID[m_dofSU[0]] - 2) >= 0) node.set_load(m_dofSU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofSU[1]] - 2) >= 0) node.set_load(m_dofSU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofSU[2]] - 2) >= 0) node.set_load(m_dofSU[2], -m_Fr[n]);\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FEExplicitSolidSolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->LoadVector(R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces \nvoid FEExplicitSolidSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i=0; i<N; ++i) \n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->LoadVector(R, tp);\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEExplicitSolidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FESolver.h\"\n#include \"FECore/FEGlobalVector.h\"\n#include <FECore/FETimeInfo.h>\n#include <FECore/FEDofList.h>\n#include \"FERigidSolver.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a nonlinear explicit solver for solid mechanics\n//! problems.\nclass FEExplicitSolidSolver : public FESolver\n{\n\tenum MassLumpingMethod\n\t{\n\t\tNO_MASS_LUMPING,\t// use consistent mass matrix\n\t\tROW_SUM_LUMPING,\t// use simple row-sum lumping\n\t\tHRZ_LUMPING\t\t\t// use Hinton-Rock-Zienkiewicz lumping\n\t};\n\n\tstruct Data {\n\t\tdouble v = 0; // velocity\n\t\tdouble a = 0; // acceleration\n\t\tdouble mi = 0; // inverted mass\n\t};\n\npublic:\n\t//! constructor\n\tFEExplicitSolidSolver(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FEExplicitSolidSolver() {}\n\npublic:\n\t//! Data initialization\n\tbool Init() override;\n\n\t//! clean up\n\tvoid Clean() override;\n\n\t//! Solve an analysis step\n\tbool SolveStep() override;\n\n\t//! Update data\n\tvoid Update(vector<double>& ui) override;\n\n\t//! Serialize data\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! initialize equations\n\tbool InitEquations() override;\n\npublic:\n\t//! update kinematics\n\tvoid UpdateKinematics(vector<double>& ui);\n\n\t//! Update rigid bodies \n\tvoid UpdateRigidBodies(vector<double>& ui);\n\n\t//! solve the step\n\tbool DoSolve();\n\n\tvoid PrepStep();\n\n\tbool Residual(vector<double>& R);\n\n\tvoid NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n\tvoid ContactForces(FEGlobalVector& R);\n\nprivate:\n\tbool CalculateMassMatrix();\n\npublic:\n\tint\t\t\tm_mass_lumping;\t//!< specify mass lumping method\n\tdouble\t\tm_dyn_damping;\t//!< velocity damping for the explicit solver\n\npublic:\n\t// equation numbers\n\tint\t\tm_nreq;\t\t\t//!< start of rigid body equations\n\n\tvector<Data> m_data;\n\tvector<double> m_ui;\t//!< residual loads\n\tvector<double> m_Ut;\t//!< Total dispalcement vector at time t (incl all previous timesteps)\n\tvector<double> m_Rt;\t//!< residual loads\n\tvector<double> m_Fr;\t//!< nodal reaction forces\n\nprotected:\n\tFEDofList\tm_dofU, m_dofV, m_dofQ, m_dofRQ;\n\tFEDofList\tm_dofSU, m_dofSV, m_dofSA;\n\n\tFERigidSolverNew m_rigidSolver;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFacet2FacetSliding.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFacet2FacetSliding.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEClosestPointProjection.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/FEDataExport.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEAnalysis.h>\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEFacet2FacetSliding, FEContactInterface)\n\tBEGIN_PARAM_GROUP(\"Enforcement\");\n\t\tADD_PARAMETER(m_laugon   , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\t\tADD_PARAMETER(m_epsn     , \"penalty\"      );\n\t\tADD_PARAMETER(m_bautopen , \"auto_penalty\" );\n\t\tADD_PARAMETER(m_bupdtpen , \"update_penalty\");\n\t\tADD_PARAMETER(m_atol     , \"tolerance\"    );\n\t\tADD_PARAMETER(m_btwo_pass, \"two_pass\"     );\n        ADD_PARAMETER(m_gtol     , \"gaptol\"       )->setLongName(\"gap tolerance\")->setUnits(UNIT_LENGTH);;\n\t\tADD_PARAMETER(m_naugmin  , \"minaug\"       )->setLongName(\"min. augmentations\");\n\t\tADD_PARAMETER(m_naugmax  , \"maxaug\"       )->setLongName(\"max. augmentations\");\n\t\tADD_PARAMETER(m_bsmaug   , \"smooth_aug\"   )->setLongName(\"Smooth augmentations\");\n\t\tEND_PARAM_GROUP();\n\n\tBEGIN_PARAM_GROUP(\"Projection\");\n\t\tADD_PARAMETER(m_stol     , \"search_tol\"   );\n        ADD_PARAMETER(m_srad     , \"search_radius\")->setUnits(UNIT_LENGTH);;\n\t\tADD_PARAMETER(m_nsegup   , \"seg_up\"       )->setLongName(\"max. segment updates\");\n\t\tADD_PARAMETER(m_breloc   , \"node_reloc\")->setLongName(\"node relocation\");\n\tEND_PARAM_GROUP();\n\n\tBEGIN_PARAM_GROUP(\"Miscellaneous\");\n\t\tADD_PARAMETER(m_dxtol    , \"dxtol\"        )->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n\t\tADD_PARAMETER(m_mu       , \"fric_coeff\"   )->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n\t\tADD_PARAMETER(m_epsf     , \"fric_penalty\" )->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\n\t\tADD_PARAMETER(m_knmult   , \"knmult\")->setLongName(\"Stiffness scale factor\");\n\tEND_PARAM_GROUP();\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFacetSlidingSurface::Data::Data()\n{\n\tm_Lm  = 0.0;\n\tm_eps = 1.0;\n\tm_nu = vec3d(0,0,0);\n\tm_rs = vec2d(0,0);\n}\n\nvoid FEFacetSlidingSurface::Data::Init()\n{\n\tFEContactMaterialPoint::Init();\n\tm_Lm = 0.0;\n\tm_eps = 1.0;\n\tm_nu = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSlidingSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_Lm;\n\tar & m_eps;\n\tar & m_nu;\n\tar & m_rs;\n}\n\n//-----------------------------------------------------------------------------\n// FEFacetSlidingSurface\n//-----------------------------------------------------------------------------\n\nFEFacetSlidingSurface::FEFacetSlidingSurface(FEModel* pfem) : FEContactSurface(pfem)\n{\n\t// define class exports\n\tEXPORT_DATA(PLT_VEC3F, FMT_NODE, &m_Fn, \"contact nodal forces\");\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPoint* FEFacetSlidingSurface::CreateMaterialPoint()\n{\n\treturn new FEFacetSlidingSurface::Data;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFacetSlidingSurface::Init()\n{\n\t// initialize surface data first\n\tif (FEContactSurface::Init() == false) return false;\n\n\tint nn = Nodes();\n\tm_Fn.assign(nn, vec3d(0,0,0));\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEFacetSlidingSurface::Serialize(DumpStream& ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tar & m_Fn;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEFacetSlidingSurface::GetContactForce()\n{\n\t// initialize contact force\n\tvec3d f(0,0,0);\n\t\n\t// loop over all elements of the primary surface\n\tfor (int i=0; i<m_Fn.size(); ++i) f += m_Fn[i];\n\n\treturn f;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFacetSlidingSurface::GetContactArea()\n{\n\t// initialize contact area\n\tdouble a = 0;\n\t\n\t// loop over all elements of the primary surface\n\tfor (int n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint nint = el.GaussPoints();\n\n\t\t\t// evaluate the contact force for that element\n\t\t\tfor (int i = 0; i < nint; ++i)\n\t\t\t{\n\t\t\t\t// get data for this integration point\n\t\t\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(i));\n\t\t\t\tdouble s = (data.m_Ln > 0) ? 1 : 0;\n\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tCoBaseVectors(el, i, g);\n\n\t\t\t\t// normal (magnitude = area)\n\t\t\t\tvec3d n = g[0] ^ g[1];\n\n\t\t\t\t// gauss weight\n\t\t\t\tdouble w = el.GaussWeights()[i];\n\n\t\t\t\t// contact force\n\t\t\t\ta += n.norm() * (w * s);\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSlidingSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tpt -= d.m_nu*d.m_Ln;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSlidingSurface::GetNodalContactPressure(int nface, double* pn)\n{\n\tFESurfaceElement& el = Element(nface);\n\tint ne = el.Nodes();\n\tint ni = el.GaussPoints();\n\tdouble ti[FEElement::MAX_INTPOINTS];\n\tfor (int k=0; k<ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tdouble L = d.m_Ln;\n\t\tti[k] = L;// + pf->m_epsn*gi[k];\n\t\tti[k] = (ti[k]>=0?ti[k] : 0);\t\t\n\t}\n\n\tel.FEElement::project_to_nodes(ti, pn);\n\tfor (int k=0; k<ni; ++k)\n\t\tpn[k] = (pn[k]>=0?pn[k] : 0);\t\t\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSlidingSurface::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& el = Element(nface);\n\tint ne = el.Nodes();\n\tint ni = el.GaussPoints();\n\n\tvec3d t;\n\tconst int MFI = FEElement::MAX_INTPOINTS;\n\tdouble tix[MFI], tiy[MFI], tiz[MFI];\n\tfor (int k=0; k<ni; ++k)\n\t{\n\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tdouble gi = d.m_gap;\n\t\tdouble Li = d.m_Ln;\n\t\tvec3d  ti = d.m_nu;\n\t\tif (gi > 0) t = ti*(Li); else t = vec3d(0,0,0);\n\t\ttix[k] = t.x; tiy[k] = t.y; tiz[k] = t.z;\n\t}\n\n\t// project traction to nodes\n\tconst int MFN = FEElement::MAX_NODES;\n\tdouble tnx[MFN], tny[MFN], tnz[MFN];\n\tel.FEElement::project_to_nodes(tix, tnx);\n\tel.FEElement::project_to_nodes(tiy, tny);\n\tel.FEElement::project_to_nodes(tiz, tnz);\n\n\t// store data\n\tfor (int k=0; k<ne; ++k)\n\t{\n\t\ttn[k].x = tnx[k];\n\t\ttn[k].y = tny[k];\n\t\ttn[k].z = tnz[k];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// FEFacet2FacetSliding\n//-----------------------------------------------------------------------------\n\nFEFacet2FacetSliding::FEFacet2FacetSliding(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int ncount = 1;\n\tSetID(ncount++);\n\n\t// default parameters\n\tm_epsn = 1.0;\n\tm_knmult = 1.0;\n\tm_stol = 0.01;\n\tm_btwo_pass = false;\n\tm_bautopen = false;\n    m_bupdtpen = false;\n\tm_nsegup = 0;\t// always do segment updates\n\tm_breloc = false;\n    m_bsmaug = false;\n\tm_srad = 0.0;\n\n\tm_atol = 0.01;\n\tm_gtol = 0;\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n\tm_dxtol = 0;\n\n\t// Note that friction has not been implemented yet\n\tm_mu = 0;\n\tm_epsf = 0;\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FEFacet2FacetSliding::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6*FEElement::MAX_NODES*2);\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEFacetSlidingSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEFacetSlidingSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\tfor (int j=0; j<ss.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\t\t\tint nint = se.GaussPoints();\n\t\t\tint* sn = &se.m_node[0];\n\t\t\tfor (int k=0; k<nint; ++k)\n\t\t\t{\n\t\t\t\tFEFacetSlidingSurface::Data& pt = static_cast<FEFacetSlidingSurface::Data&>(*se.GetMaterialPoint(k));\n\t\t\t\tFESurfaceElement* pe = pt.m_pme;\n\t\t\t\tif (pe != 0)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\t\tint* mn = &me.m_node[0];\n\n\t\t\t\t\tassign(lm, -1);\n\n\t\t\t\t\tint nseln = se.Nodes();\n\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\tfor (int l=0; l<nseln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(sn[l]).m_ID;\n\t\t\t\t\t\tlm[6*l  ] = id[dof_X];\n\t\t\t\t\t\tlm[6*l+1] = id[dof_Y];\n\t\t\t\t\t\tlm[6*l+2] = id[dof_Z];\n\t\t\t\t\t\tlm[6*l+3] = id[dof_RU];\n\t\t\t\t\t\tlm[6*l+4] = id[dof_RV];\n\t\t\t\t\t\tlm[6*l+5] = id[dof_RW];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(mn[l]).m_ID;\n\t\t\t\t\t\tlm[6*(l+nseln)  ] = id[dof_X];\n\t\t\t\t\t\tlm[6*(l+nseln)+1] = id[dof_Y];\n\t\t\t\t\t\tlm[6*(l+nseln)+2] = id[dof_Z];\n\t\t\t\t\t\tlm[6*(l+nseln)+3] = id[dof_RU];\n\t\t\t\t\t\tlm[6*(l+nseln)+4] = id[dof_RV];\n\t\t\t\t\t\tlm[6*(l+nseln)+5] = id[dof_RW];\n\t\t\t\t\t}\n\n\t\t\t\t\tK.build_add(lm);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization routine\nbool FEFacet2FacetSliding::Init()\n{\n\tm_bfirst = true;\n\tm_normg0 = 0.0;\n\n\t// initialize surface data\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::UpdateAutoPenalty()\n{\n    // calculate penalty factors\n    if (m_bautopen) CalcAutoPenalty(m_ss);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::Activate()\n{\n\t// don't forget the base class\n\tFEContactInterface::Activate();\n\n    UpdateAutoPenalty();\n    \n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms, true, m_breloc);\n\n\tif (m_btwo_pass) \n\t{\n\t\tProjectSurface(m_ms, m_ss, true);\n\t\tif (m_bautopen) CalcAutoPenalty(m_ms);\n\t}\n\n\t// check friction parameters\n\t// since friction has not been implemented yet\n\tif ((m_mu != 0) || (m_epsf != 0))\n\t{\n\t\tfeLogWarning(\"Friction has NOT been implemented yet for facet-to-facet contact interfaces.\\nFriction parameters are ignored.\");\n\t\tm_mu = 0;\n\t\tm_epsf = 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::CalcAutoPenalty(FEFacetSlidingSurface& s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPenalty(el, s);\n\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j) \n\t\t{\n\t\t\tFEFacetSlidingSurface::Data& pt = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_eps = eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! In this function we project the integration points to the secondary surface,\n//! calculate the projection's natural coordinates and normal vector\n//\nvoid FEFacet2FacetSliding::ProjectSurface(FEFacetSlidingSurface &ss, FEFacetSlidingSurface &ms, bool bsegup, bool bmove)\n{\n\tFEClosestPointProjection cpp(ms);\n\tcpp.HandleSpecialCases(true);\n\tcpp.SetSearchRadius(m_srad);\n\tcpp.SetTolerance(m_stol);\n\tcpp.Init();\n\n\t// if we need to project the nodes onto the secondary surface,\n\t// let's do this first\n\tif (bmove)\n\t{\n\t\tint NN = ss.Nodes();\n\t\tint NE = ss.Elements();\n\t\t// first we need to calculate the node normals\n\t\tvector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = ss.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint ne = el.Nodes();\n\t\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t\t{\n\t\t\t\t\tvec3d r0 = ss.Node(el.m_lnode[j]).m_rt;\n\t\t\t\t\tvec3d rp = ss.Node(el.m_lnode[(j + 1) % ne]).m_rt;\n\t\t\t\t\tvec3d rm = ss.Node(el.m_lnode[(j + ne - 1) % ne]).m_rt;\n\t\t\t\t\tvec3d n = (rp - r0) ^ (rm - r0);\n\t\t\t\t\tnormal[el.m_lnode[j]] += n;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (int i=0; i<NN; ++i) normal[i].unit();\n\n\t\t// loop over all nodes\n\t\tfor (int i=0; i<NN; ++i)\n\t\t{\n\t\t\tFENode& node = ss.Node(i);\n\n\t\t\t// get the spatial nodal coordinates\n\t\t\tvec3d rt = node.m_rt;\n\t\t\tvec3d nu = normal[i];\n\n\t\t\t// project onto the secondary surface\n\t\t\tvec3d q;\n\t\t\tvec2d rs(0,0);\n\t\t\tFESurfaceElement* pme = cpp.Project(ss.NodeIndex(i), q, rs);\n\t\t\tif (pme) \n\t\t\t{\n\t\t\t\tdouble gap = (nu*(rt - q));\n\t\t\t\tif (gap>0) node.m_r0 = node.m_rt = q;\n\t\t\t}\n\t\t}\n\t}\n\n\t// loop over all primary surface elements\n\tint NE = ss.Elements();\n\n#pragma omp parallel for shared(cpp) schedule(dynamic)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& se = ss.Element(i);\n\t\tif (se.isActive())\n\t\t{\n\t\t\tint nn = se.Nodes();\n\n\t\t\t// get nodal coordinates\n\t\t\tvec3d re[FEElement::MAX_NODES];\n\t\t\tfor (int l = 0; l < nn; ++l) re[l] = ss.GetMesh()->Node(se.m_node[l]).m_rt;\n\n\t\t\t// loop over all its integration points\n\t\t\tint nint = se.GaussPoints();\n\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t{\n\t\t\t\t// get the integration point data\n\t\t\t\tFEFacetSlidingSurface::Data& pt = static_cast<FEFacetSlidingSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// calculate the global coordinates of this integration point\n\t\t\t\tdouble* H = se.H(j);\n\n\t\t\t\tvec3d x(0, 0, 0), q;\n\t\t\t\tfor (int k = 0; k < nn; ++k) x += re[k] * H[k];\n\n\t\t\t\tFESurfaceElement* pme_prev = pt.m_pme;\n\n\t\t\t\tif (bsegup)\n\t\t\t\t{\n\t\t\t\t\tif (pt.m_pme)\n\t\t\t\t\t{\n\t\t\t\t\t\t// see if it still projects to the same facet\n\t\t\t\t\t\tq = ms.ProjectToSurface(*pt.m_pme, x, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t\t\tif (ms.IsInsideElement(*pt.m_pme, pt.m_rs[0], pt.m_rs[1], m_stol) == false)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpt.m_pme = nullptr;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (pt.m_pme == nullptr)\n\t\t\t\t\t{\n\t\t\t\t\t\t// find the secondary surface segment this element belongs to\n\t\t\t\t\t\tpt.m_rs = vec2d(0, 0);\n\t\t\t\t\t\tFESurfaceElement* pme = 0;\n\t\t\t\t\t\tpme = cpp.Project(&se, j, q, pt.m_rs);\n\t\t\t\t\t\tpt.m_pme = pme;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (pt.m_pme)\n\t\t\t\t{\n\t\t\t\t\t// update projection to secondary surface element\n\t\t\t\t\tFESurfaceElement& mel = *pt.m_pme;\n\t\t\t\t\tq = ms.ProjectToSurface(mel, x, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t}\n\n\t\t\t\t// update normal and gap at integration point\n\t\t\t\tif (pt.m_pme)\n\t\t\t\t{\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\n\t\t\t\t\t// the normal is set to the secondary surface element normal\n\t\t\t\t\tpt.m_nu = ms.SurfaceNormal(*pt.m_pme, r, s);\n\n\t\t\t\t\t// calculate gap\n\t\t\t\t\tpt.m_gap = -pt.m_nu * (x - q);\n\n\t\t\t\t\t// if gap is negative reset contact\n\t\t\t\t\tif (bsegup && (pt.m_gap < 0.0))\n\t\t\t\t\t{\n\t\t\t\t\t\t//\t\t\t\t\tpt.m_gap = 0.0;\n\t\t\t\t\t\t//\t\t\t\t\tpt.m_pme = nullptr;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (pt.m_pme == nullptr)\n\t\t\t\t{\n\t\t\t\t\t// since the node is not in contact, we set the gap and Lagrange multiplier to zero\n\t\t\t\t\tpt.m_gap = 0;\n\t\t\t\t\t//\t\t\tpt.m_Lm = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::Update()\n{\n\tFEModel& fem = *GetFEModel();\n\tint niter = fem.GetCurrentStep()->GetFESolver()->m_niter;\n\n\t// should we do a segment update or not?\n\t// TODO: check what happens when m_nsegup == -1 and m_npass = 2;\n\t// We have to make sure that in this case, both surfaces get at least\n\t// one pass!\n\tbool bupdate = (m_bfirst || (m_nsegup == 0)? true : (niter <= m_nsegup));\n    \n    if ((niter == 0) && m_bupdtpen) UpdateAutoPenalty();\n\n\t// project primary surface to secondary surface\n\tProjectSurface(m_ss, m_ms, bupdate);\n\tif (m_btwo_pass) ProjectSurface(m_ms, m_ss, bupdate);\n\n\t// Update the net contact pressures\n\tUpdateContactPressures();\n\n\tm_bfirst = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, LM, en;\n\tvector<double> fe;\n\n\tconst int MELN = FEElement::MAX_NODES;\n\tdouble detJ[MELN], w[MELN], *Hs, Hm[MELN];\n\tvec3d r0[MELN];\n\n\tm_ss.m_Fn.assign(m_ss.Nodes(), vec3d(0,0,0));\n\tm_ms.m_Fn.assign(m_ms.Nodes(), vec3d(0,0,0));\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEFacetSlidingSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEFacetSlidingSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary surface elements\n\t\tfor (int i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\tif (se.isActive())\n\t\t\t{\n\t\t\t\tint nseln = se.Nodes();\n\t\t\t\tint nint = se.GaussPoints();\n\n\t\t\t\t// get the element's LM vector\n\t\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t\t// nodal coordinates\n\t\t\t\tfor (int j = 0; j < nseln; ++j) r0[j] = ss.GetMesh()->Node(se.m_node[j]).m_r0;\n\n\t\t\t\t// we calculate all the metrics we need before we\n\t\t\t\t// calculate the nodal forces\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble* Gr = se.Gr(j);\n\t\t\t\t\tdouble* Gs = se.Gs(j);\n\n\t\t\t\t\t// calculate jacobian\n\t\t\t\t\t// note that we are integrating over the reference surface\n\t\t\t\t\tvec3d dxr, dxs;\n\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdxr.x += Gr[k] * r0[k].x;\n\t\t\t\t\t\tdxr.y += Gr[k] * r0[k].y;\n\t\t\t\t\t\tdxr.z += Gr[k] * r0[k].z;\n\n\t\t\t\t\t\tdxs.x += Gs[k] * r0[k].x;\n\t\t\t\t\t\tdxs.y += Gs[k] * r0[k].y;\n\t\t\t\t\t\tdxs.z += Gs[k] * r0[k].z;\n\t\t\t\t\t}\n\n\t\t\t\t\t// jacobians\n\t\t\t\t\tdetJ[j] = (dxr ^ dxs).norm();\n\n\t\t\t\t\t// integration weights\n\t\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t\t}\n\n\t\t\t\t// loop over all integration points\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get integration point data\n\t\t\t\t\tFEFacetSlidingSurface::Data& pt = static_cast<FEFacetSlidingSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\t\tif (pme)\n\t\t\t\t\t{\n\t\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\t\tint ndof = 3 * (nseln + nmeln);\n\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * k] = sLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * k + 1] = sLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * k + 2] = sLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * (k + nseln)] = mLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 1] = mLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 2] = mLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// build the en vector\n\t\t\t\t\t\ten.resize(nseln + nmeln);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) en[k] = se.m_node[k];\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + nseln] = me.m_node[k];\n\n\t\t\t\t\t\t// calculate shape functions\n\t\t\t\t\t\tHs = se.H(j);\n\n\t\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t\t\t// get normal vector\n\t\t\t\t\t\tvec3d nu = pt.m_nu;\n\n\t\t\t\t\t\t// gap function\n\t\t\t\t\t\tdouble g = pt.m_gap;\n\n\t\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\t\tdouble Lm = pt.m_Lm;\n\n\t\t\t\t\t\t// penalty value\n\t\t\t\t\t\tdouble eps = m_epsn * pt.m_eps;\n\n\t\t\t\t\t\t// contact traction\n\t\t\t\t\t\tdouble tn = Lm + eps * g;\n\t\t\t\t\t\ttn = MBRACKET(tn);\n\n\t\t\t\t\t\t// calculate the force vector\n\t\t\t\t\t\tfe.resize(ndof);\n\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfe[3 * k] = Hs[k] * nu.x;\n\t\t\t\t\t\t\tfe[3 * k + 1] = Hs[k] * nu.y;\n\t\t\t\t\t\t\tfe[3 * k + 2] = Hs[k] * nu.z;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfe[3 * (k + nseln)] = -Hm[k] * nu.x;\n\t\t\t\t\t\t\tfe[3 * (k + nseln) + 1] = -Hm[k] * nu.y;\n\t\t\t\t\t\t\tfe[3 * (k + nseln) + 2] = -Hm[k] * nu.z;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k) fe[k] *= tn * detJ[j] * w[j];\n\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) ss.m_Fn[se.m_lnode[k]] += vec3d(fe[3 * k], fe[3 * k + 1], fe[3 * k + 2]);\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) ms.m_Fn[me.m_lnode[k]] += vec3d(fe[3 * nseln + 3 * k], fe[3 * nseln + 3 * k + 1], fe[3 * nseln + 3 * k + 2]);\n\n\t\t\t\t\t\t// assemble the global residual\n\t\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEFacet2FacetSliding::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, LM, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tconst int ME = 3*MN*2;\n\tdouble N[ME], T1[ME], T2[ME], N1[ME] = {0}, N2[ME] = {0}, D1[ME], D2[ME], Nb1[ME], Nb2[ME];\n\tFEElementMatrix ke;\n\n\t// get the mesh\n\tFEMesh* pm = m_ss.GetMesh();\n\n\t// see how many reformations we've had to do so far\n\tint nref = GetSolver()->m_nref;\n\n\t// get the \"size\" of the model\n\t// We need this to scale the insertion distance\n\tdouble R = GetFEModel()->GetMesh().GetBoundingBox().radius();\n\tdouble dxtol = R*m_dxtol;\n\n\t// set higher order stiffness mutliplier\n\tdouble knmult = m_knmult;\n\tif (m_knmult < 0)\n\t{\n\t\tint ni = int(-m_knmult);\n\t\tif (nref >= ni)\n\t\t{\n\t\t\tknmult = 1; \n\t\t\tfeLog(\"Higher order stiffness terms included.\\n\");\n\t\t}\n\t\telse knmult = 0;\n\t}\n\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN], Hmr[MN], Hms[MN];\n\tvec3d r0[MN];\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np < npass; ++np)\n\t{\n\t\tFEFacetSlidingSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEFacetSlidingSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary surface elements\n\t\tfor (int i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\tif (se.isActive())\n\t\t\t{\n\t\t\t\tint nseln = se.Nodes();\n\t\t\t\tint nint = se.GaussPoints();\n\n\t\t\t\t// get the element's LM vector\n\t\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t\t// nodal coordinates\n\t\t\t\tfor (int j = 0; j < nseln; ++j) r0[j] = ss.GetMesh()->Node(se.m_node[j]).m_r0;\n\n\t\t\t\t// we calculate all the metrics we need before we\n\t\t\t\t// calculate the nodal forces\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble* Gr = se.Gr(j);\n\t\t\t\t\tdouble* Gs = se.Gs(j);\n\n\t\t\t\t\t// calculate jacobian\n\t\t\t\t\t// note that we are integrating over the reference surface\n\t\t\t\t\tvec3d dxr, dxs;\n\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdxr.x += Gr[k] * r0[k].x;\n\t\t\t\t\t\tdxr.y += Gr[k] * r0[k].y;\n\t\t\t\t\t\tdxr.z += Gr[k] * r0[k].z;\n\n\t\t\t\t\t\tdxs.x += Gs[k] * r0[k].x;\n\t\t\t\t\t\tdxs.y += Gs[k] * r0[k].y;\n\t\t\t\t\t\tdxs.z += Gs[k] * r0[k].z;\n\t\t\t\t\t}\n\n\t\t\t\t\t// jacobians\n\t\t\t\t\tdetJ[j] = (dxr ^ dxs).norm();\n\n\t\t\t\t\t// integration weights\n\t\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t\t}\n\n\t\t\t\t// loop over all integration points\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get integration point data\n\t\t\t\t\tFEFacetSlidingSurface::Data& pt = static_cast<FEFacetSlidingSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\t\tif (pme)\n\t\t\t\t\t{\n\t\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\t\tint ndof = 3 * (nseln + nmeln);\n\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * k] = sLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * k + 1] = sLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * k + 2] = sLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * (k + nseln)] = mLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 1] = mLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 2] = mLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// build the en vector\n\t\t\t\t\t\ten.resize(nseln + nmeln);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) en[k] = se.m_node[k];\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + nseln] = me.m_node[k];\n\n\t\t\t\t\t\t// calculate shape functions\n\t\t\t\t\t\tHs = se.H(j);\n\t\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t\t\t// get normal vector\n\t\t\t\t\t\tvec3d nu = pt.m_nu;\n\n\t\t\t\t\t\t// gap function\n\t\t\t\t\t\tdouble g = pt.m_gap;\n\n\t\t\t\t\t\t// when the node is on the surface, the gap value\n\t\t\t\t\t\t// can flip-flop between positive and negative.\n\t\t\t\t\t\tif (fabs(g) < 1e-20) g = 0;\n\n\t\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\t\tdouble Lm = pt.m_Lm;\n\n\t\t\t\t\t\t// penalty value\n\t\t\t\t\t\tdouble eps = m_epsn * pt.m_eps;\n\n\t\t\t\t\t\t// contact traction\n\t\t\t\t\t\tdouble tn = Lm + eps * g;\n\t\t\t\t\t\ttn = MBRACKET(tn);\n\n\t\t\t\t\t\tdouble dtn = eps * HEAVYSIDE(Lm + eps * g);\n\n\t\t\t\t\t\t// define buffer layer for penalty insertion\n\t\t\t\t\t\t// TODO: I don't think this does anything since dtn cannot < 0\n\t\t\t\t\t\tif ((dtn < 1e-7) && (g < 0) && (dxtol != 0))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (dxtol < 0) dtn = eps * exp(-g / dxtol);\n\t\t\t\t\t\t\telse if (-g <= dxtol) dtn = eps * (1 + g / dxtol);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// calculate the N-vector\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[3 * k] = Hs[k] * nu.x;\n\t\t\t\t\t\t\tN[3 * k + 1] = Hs[k] * nu.y;\n\t\t\t\t\t\t\tN[3 * k + 2] = Hs[k] * nu.z;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[3 * (k + nseln)] = -Hm[k] * nu.x;\n\t\t\t\t\t\t\tN[3 * (k + nseln) + 1] = -Hm[k] * nu.y;\n\t\t\t\t\t\t\tN[3 * (k + nseln) + 2] = -Hm[k] * nu.z;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// --- N O R M A L   S T I F F N E S S ---\n\n\t\t\t\t\t\t// create the stiffness matrix\n\t\t\t\t\t\tke.resize(ndof, ndof);\n\n\t\t\t\t\t\t// add the first order term (= D(tn)*dg )\n\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\t\t\tfor (int l = 0; l < ndof; ++l) ke[k][l] = dtn * N[k] * N[l] * detJ[j] * w[j];\n\n\t\t\t\t\t\t// add the higher order terms (= tn*D(dg) )\n\t\t\t\t\t\tif (knmult > 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// calculate the secondary surface shape fncs derivatives\n\t\t\t\t\t\t\tme.shape_deriv(Hmr, Hms, r, s);\n\n\t\t\t\t\t\t\t// get the secondary surface nodes\n\t\t\t\t\t\t\tvec3d rt[MN];\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) rt[k] = ms.GetMesh()->Node(me.m_node[k]).m_rt;\n\n\t\t\t\t\t\t\t// get the tangent vectors\n\t\t\t\t\t\t\tvec3d tau1(0, 0, 0), tau2(0, 0, 0);\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttau1.x += Hmr[k] * rt[k].x;\n\t\t\t\t\t\t\t\ttau1.y += Hmr[k] * rt[k].y;\n\t\t\t\t\t\t\t\ttau1.z += Hmr[k] * rt[k].z;\n\n\t\t\t\t\t\t\t\ttau2.x += Hms[k] * rt[k].x;\n\t\t\t\t\t\t\t\ttau2.y += Hms[k] * rt[k].y;\n\t\t\t\t\t\t\t\ttau2.z += Hms[k] * rt[k].z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// set up the Ti vectors\n\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tT1[k * 3] = Hs[k] * tau1.x; T2[k * 3] = Hs[k] * tau2.x;\n\t\t\t\t\t\t\t\tT1[k * 3 + 1] = Hs[k] * tau1.y; T2[k * 3 + 1] = Hs[k] * tau2.y;\n\t\t\t\t\t\t\t\tT1[k * 3 + 2] = Hs[k] * tau1.z; T2[k * 3 + 2] = Hs[k] * tau2.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tT1[(k + nseln) * 3] = -Hm[k] * tau1.x;\n\t\t\t\t\t\t\t\tT1[(k + nseln) * 3 + 1] = -Hm[k] * tau1.y;\n\t\t\t\t\t\t\t\tT1[(k + nseln) * 3 + 2] = -Hm[k] * tau1.z;\n\n\t\t\t\t\t\t\t\tT2[(k + nseln) * 3] = -Hm[k] * tau2.x;\n\t\t\t\t\t\t\t\tT2[(k + nseln) * 3 + 1] = -Hm[k] * tau2.y;\n\t\t\t\t\t\t\t\tT2[(k + nseln) * 3 + 2] = -Hm[k] * tau2.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// set up the Ni vectors\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tN1[(k + nseln) * 3] = -Hmr[k] * nu.x;\n\t\t\t\t\t\t\t\tN1[(k + nseln) * 3 + 1] = -Hmr[k] * nu.y;\n\t\t\t\t\t\t\t\tN1[(k + nseln) * 3 + 2] = -Hmr[k] * nu.z;\n\n\t\t\t\t\t\t\t\tN2[(k + nseln) * 3] = -Hms[k] * nu.x;\n\t\t\t\t\t\t\t\tN2[(k + nseln) * 3 + 1] = -Hms[k] * nu.y;\n\t\t\t\t\t\t\t\tN2[(k + nseln) * 3 + 2] = -Hms[k] * nu.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// calculate metric tensor\n\t\t\t\t\t\t\tmat2d M;\n\t\t\t\t\t\t\tM[0][0] = tau1 * tau1; M[0][1] = tau1 * tau2;\n\t\t\t\t\t\t\tM[1][0] = tau2 * tau1; M[1][1] = tau2 * tau2;\n\n\t\t\t\t\t\t\t// calculate reciprocal metric tensor\n\t\t\t\t\t\t\tmat2d Mi = M.inverse();\n\n\t\t\t\t\t\t\t// calculate curvature tensor\n\t\t\t\t\t\t\tdouble K[2][2] = { 0 };\n\t\t\t\t\t\t\tdouble Grr[MN];\n\t\t\t\t\t\t\tdouble Gss[MN];\n\t\t\t\t\t\t\tdouble Grs[MN];\n\t\t\t\t\t\t\tme.shape_deriv2(Grr, Grs, Gss, r, s);\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tK[0][0] += (nu * rt[k]) * Grr[k];\n\t\t\t\t\t\t\t\tK[0][1] += (nu * rt[k]) * Grs[k];\n\t\t\t\t\t\t\t\tK[1][0] += (nu * rt[k]) * Grs[k];\n\t\t\t\t\t\t\t\tK[1][1] += (nu * rt[k]) * Gss[k];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// setup A matrix A = M + gK\n\t\t\t\t\t\t\tdouble A[2][2];\n\t\t\t\t\t\t\tA[0][0] = M[0][0] + g * K[0][0];\n\t\t\t\t\t\t\tA[0][1] = M[0][1] + g * K[0][1];\n\t\t\t\t\t\t\tA[1][0] = M[1][0] + g * K[1][0];\n\t\t\t\t\t\t\tA[1][1] = M[1][1] + g * K[1][1];\n\n\t\t\t\t\t\t\t// calculate determinant of A\n\t\t\t\t\t\t\tdouble detA = A[0][0] * A[1][1] - A[0][1] * A[1][0];\n\n\t\t\t\t\t\t\t// setup Di vectors\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tD1[k] = (1 / detA) * (A[1][1] * (T1[k] + g * N1[k]) - A[0][1] * (T2[k] + g * N2[k]));\n\t\t\t\t\t\t\t\tD2[k] = (1 / detA) * (A[0][0] * (T2[k] + g * N2[k]) - A[0][1] * (T1[k] + g * N1[k]));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// setup Nbi vectors\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tNb1[k] = N1[k] - K[0][1] * D2[k];\n\t\t\t\t\t\t\t\tNb2[k] = N2[k] - K[0][1] * D1[k];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// add it to the stiffness\n\t\t\t\t\t\t\tdouble sum;\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 0; l < ndof; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsum = Mi[0][0] * Nb1[k] * Nb1[l] + Mi[0][1] * (Nb1[k] * Nb2[l] + Nb2[k] * Nb1[l]) + Mi[1][1] * Nb2[k] * Nb2[l];\n\t\t\t\t\t\t\t\t\tsum *= g;\n\t\t\t\t\t\t\t\t\tsum -= D1[k] * N1[l] + D2[k] * N2[l] + N1[k] * D1[l] + N2[k] * D2[l];\n\t\t\t\t\t\t\t\t\tsum += K[0][1] * (D1[k] * D2[l] + D2[k] * D1[l]);\n\t\t\t\t\t\t\t\t\tsum *= tn * knmult;\n\n\t\t\t\t\t\t\t\t\tke[k][l] += sum * detJ[j] * w[j];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// assemble the global residual\n\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::UpdateContactPressures()\n{\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEFacetSlidingSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEFacetSlidingSurface& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all elements of the primary surface\n\t\tfor (int n=0; n<ss.Elements(); ++n)\n\t\t{\n\t\t\tFESurfaceElement& el = ss.Element(n);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint nint = el.GaussPoints();\n\n\t\t\t\t// get the normal tractions at the integration points\n\t\t\t\tdouble gap, eps;\n\t\t\t\tfor (int i = 0; i < nint; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEFacetSlidingSurface::Data& pt = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(i));\n\n\t\t\t\t\tgap = pt.m_gap;\n\t\t\t\t\teps = m_epsn * pt.m_eps;\n\t\t\t\t\tpt.m_Ln = MBRACKET(pt.m_Lm + eps * gap);\n\t\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\t\tif (m_btwo_pass && pme)\n\t\t\t\t\t{\n\t\t\t\t\t\t// get secondary surface element data\n\t\t\t\t\t\tint mint = pme->GaussPoints();\n\t\t\t\t\t\tdouble ti[FEElement::MAX_NODES];\n\t\t\t\t\t\tfor (int j = 0; j < mint; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEFacetSlidingSurface::Data& md = static_cast<FEFacetSlidingSurface::Data&>(*pme->GetMaterialPoint(j));\n\n\t\t\t\t\t\t\tgap = md.m_gap;\n\t\t\t\t\t\t\teps = m_epsn * md.m_eps;\n\t\t\t\t\t\t\tti[j] = MBRACKET(md.m_Lm + m_epsn * md.m_eps * md.m_gap);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// project the data to the nodes\n\t\t\t\t\t\tdouble tn[FEElement::MAX_NODES];\n\t\t\t\t\t\tpme->FEElement::project_to_nodes(ti, tn);\n\t\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\t\tdouble Ln = pme->eval(tn, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t\t\tpt.m_Ln += MBRACKET(Ln);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEFacet2FacetSliding::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tbool bconv = true;\n\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\t// a. normal component\n\tint NS = (int) m_ss.Elements();\n\tint NM = (int) m_ms.Elements();\n\n\tdouble normL0 = 0;\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t{\n\t\t\t\tFEFacetSlidingSurface::Data& ds = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\t\tnormL0 += ds.m_Lm * ds.m_Lm;\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i=0; i<NM; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t{\n\t\t\t\tFEFacetSlidingSurface::Data& dm = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\t\tnormL0 += dm.m_Lm * dm.m_Lm;\n\t\t\t}\n\t\t}\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// --- c a l c u l a t e   c u r r e n t   n o r m s ---\n\t// b. normal component\n\tdouble normL1 = 0;\t// force norm\n\tdouble normg1 = 0;\t// gap norm\n\tint N = 0;\n    double Ln;\n    for (int i=0; i<m_ss.Elements(); ++i) {\n        FESurfaceElement& el = m_ss.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tvec3d tn[FEElement::MAX_INTPOINTS];\n\t\t\tif (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j) {\n\t\t\t\tFEFacetSlidingSurface::Data& data = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t// update Lagrange multipliers on primary surface\n\t\t\t\tif (m_bsmaug) {\n\t\t\t\t\t// replace this multiplier with a smoother version\n\t\t\t\t\tLn = -(tn[j] * data.m_nu);\n\t\t\t\t\tdata.m_Lm = MBRACKET(Ln);\n\t\t\t\t\tif (m_btwo_pass) data.m_Lm /= 2;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tdouble eps = m_epsn * data.m_eps;\n\t\t\t\t\tLn = data.m_Lm + eps * data.m_gap;\n\t\t\t\t\tdouble Lm = MBRACKET(Ln);\n\t\t\t\t\tnormL1 += Lm * Lm;\n\t\t\t\t}\n\n\t\t\t\tif (data.m_gap > 0)\n\t\t\t\t{\n\t\t\t\t\tnormg1 += data.m_gap * data.m_gap;\n\t\t\t\t\t++N;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n    for (int i=0; i<m_ms.Elements(); ++i) {\n        FESurfaceElement& el = m_ms.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tvec3d tn[FEElement::MAX_INTPOINTS];\n\t\t\tif (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j) {\n\t\t\t\tFEFacetSlidingSurface::Data& data = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t// update Lagrange multipliers on secondary surface\n\t\t\t\tif (m_bsmaug) {\n\t\t\t\t\t// replace this multiplier with a smoother version\n\t\t\t\t\tLn = -(tn[j] * data.m_nu);\n\t\t\t\t\tdata.m_Lm = MBRACKET(Ln);\n\t\t\t\t\tif (m_btwo_pass) data.m_Lm /= 2;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tdouble eps = m_epsn * data.m_eps;\n\t\t\t\t\tLn = data.m_Lm + eps * data.m_gap;\n\t\t\t\t\tdouble Lm = MBRACKET(Ln);\n\t\t\t\t\tnormL1 += Lm * Lm;\n\t\t\t\t}\n\n\t\t\t\tif (data.m_gap > 0)\n\t\t\t\t{\n\t\t\t\t\tnormg1 += data.m_gap * data.m_gap;\n\t\t\t\t\t++N;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormg1 = sqrt(normg1 / N);\n\n\tif (naug == 0) m_normg0 = 0;\n\n\t// calculate and print convergence norms\n\tdouble lnorm = 0, gnorm = 0;\n\tif (normL1 != 0) lnorm = fabs(normL1 - normL0)/normL1; else lnorm = fabs(normL1 - normL0);\n//\tif (normg1 != 0) gnorm = fabs(normg1 - m_normg0)/normg1; else gnorm = fabs(normg1 - m_normg0);\n\tgnorm = normg1;\n\n\tfeLog(\" sliding interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    normal force : %15le\", lnorm);\n\tif (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\tfeLog(\"    gap function : %15le\", gnorm);\n\tif (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n\n\t// check convergence\n\tbconv = true;\n\tif ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n\tif ((m_gtol > 0) && (gnorm > m_gtol)) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif (m_naugmax <= naug) bconv = true;\n\t\t\n\tif (bconv == false)\n\t{\n\t\t// we did not converge so update multipliers\n\t\tfor (int i=0; i<NS; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEFacetSlidingSurface::Data& ds = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\n\t\t\t\t\t// penalty value\n\t\t\t\t\tdouble eps = m_epsn * ds.m_eps;\n\n\t\t\t\t\t// update Lagrange multipliers\n\t\t\t\t\tdouble Ln = ds.m_Lm + eps * ds.m_gap;\n\t\t\t\t\tds.m_Lm = MBRACKET(Ln);\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\n\n\t\tfor (int i=0; i<NM; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEFacetSlidingSurface::Data& dm = static_cast<FEFacetSlidingSurface::Data&>(*el.GetMaterialPoint(j));\n\n\t\t\t\t\t// penalty value\n\t\t\t\t\tdouble eps = m_epsn * dm.m_eps;\n\n\t\t\t\t\t// update Lagrange multipliers\n\t\t\t\t\tdouble Ln = dm.m_Lm + eps * dm.m_gap;\n\t\t\t\t\tdm.m_Lm = MBRACKET(Ln);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// store the last gap norm\n\tm_normg0 = normg1;\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetSliding::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n\n\t// serialize element pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n"
  },
  {
    "path": "FEBioMech/FEFacet2FacetSliding.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! Contact surface for facet-to-facet sliding interfaces\nclass FEFacetSlidingSurface : public FEContactSurface\n{\npublic:\n\t//! Integration point data\n\tclass Data : public FEContactMaterialPoint\n\t{\n\tpublic:\n\t\tData();\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\t\tvoid Init() override;\n\n\tpublic:\n\t\tdouble\tm_Lm;\t//!< Lagrange multipliers\n\t\tdouble\tm_eps;\t//!< penalty value at integration point\n\t\tvec3d\tm_nu;\t//!< secondary surface normal at integration points\n\t\tvec2d\tm_rs;\t//!< natural coordinates of projection of integration point\n\t};\n\npublic:\n\t//! constructor\n\tFEFacetSlidingSurface(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! evaluate net contact force\n\tvec3d GetContactForce() override;\n\n\t//! evaluate net contact area\n\tdouble GetContactArea() override;\n    \n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt) override;\n\tvoid GetNodalContactPressure(int nface, double* pn) override;\n\tvoid GetNodalContactTraction(int nface, vec3d* tn) override;\n\npublic:\n\tvector<vec3d>\tm_Fn;\t//!< equivalent nodal forces\n};\n\n//-----------------------------------------------------------------------------\n//! Sliding interface with facet to facet integration\n\n//! This class is similar to the sliding interface except that it uses\n//! a Gaussian quadrature rule in stead of a nodal integration rule\n//! as its base class does.\n//\nclass FEFacet2FacetSliding : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEFacet2FacetSliding(FEModel* pfem);\n\n\t//! initialization routine\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! calculate contact pressures for file output\n\tvoid UpdateContactPressures();\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! get primary and secondary surfaces\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update \n\tvoid Update() override;\n\nprotected:\n\t//! project primary surface onto secondary\n\tvoid ProjectSurface(FEFacetSlidingSurface& ss, FEFacetSlidingSurface& ms, bool bsegup, bool bmove = false);\n\n\t//! calculate auto-penalty\n    void UpdateAutoPenalty();\n    \n\tvoid CalcAutoPenalty(FEFacetSlidingSurface& s);\n\npublic:\n\tdouble\tm_epsn;\t\t\t//!< normal penalty factor\n\tdouble\tm_knmult;\t\t//!< normal stiffness multiplier\n\tdouble\tm_stol;\t\t\t//!< search tolerance\n\tbool\tm_btwo_pass;\t//!< two-pass flag\n\tbool\tm_bautopen;\t\t//!< auto-penalty flag\n    bool    m_bupdtpen;     //!< update penalty at each time step\n\tdouble\tm_srad;\t\t\t//!< search radius (% of model size)\n\tint\t\tm_nsegup;\t\t//!< segment update parameter\n\tbool\tm_breloc;       //!< node relocation on initialization\n    bool    m_bsmaug;       //!< smooth augmentation\n\n\tdouble\tm_atol;\t\t\t//!< aug lag tolerance\n\tdouble\tm_gtol;\t\t\t//!< gap tolerance\n\tint\t\tm_naugmin;\t\t//!< min nr of augmentations\n\tint\t\tm_naugmax;\t\t//!< max nr of augmentations\n\n\tdouble\t\t\tm_mu;\t\t//!< friction coefficient (not implemented yet)\n\tdouble\t\t\tm_epsf;\t\t//!< penalty scale factor for friction (not implementer yet)\n\n\tdouble\tm_dxtol;\t\t//!< penalty insertion distance\n\n\tFEFacetSlidingSurface\tm_ss;\t//!< primary surface\n\tFEFacetSlidingSurface\tm_ms;\t//!< secondary surface\n\nprivate:\n\tbool\tm_bfirst;\n\tdouble\tm_normg0;\n\npublic:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFacet2FacetTied.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFacet2FacetTied.h\"\n#include \"FECore/FEMesh.h\"\n#include \"FECore/FEClosestPointProjection.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define tied interface parameters\nBEGIN_FECORE_CLASS(FEFacet2FacetTied, FEContactInterface)\n\tADD_PARAMETER(m_laugon , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol   , \"tolerance\"       );\n\tADD_PARAMETER(m_eps    , \"penalty\"         );\n\tADD_PARAMETER(m_naugmin, \"minaug\"          );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"          );\n\tADD_PARAMETER(m_stol   , \"search_tolerance\");\n\tADD_PARAMETER(m_gapoff , \"gap_offset\"      );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFacetTiedSurface::Data::Data()\n{\n\tm_vgap  = vec3d(0,0,0);\n\tm_vgap0 = vec3d(0,0,0);\n\tm_Lm = vec3d(0,0,0);\n\tm_rs = vec2d(0,0);\n\tm_pme = (FESurfaceElement*) 0;\n}\n\nvoid FEFacetTiedSurface::Data::Init()\n{\n\tFEContactMaterialPoint::Init();\n\tm_vgap = vec3d(0, 0, 0);\n\tm_vgap0 = vec3d(0, 0, 0);\n\tm_Lm = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n\tm_pme = (FESurfaceElement*)0;\n}\n\nvoid FEFacetTiedSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_vgap & m_vgap0 & m_Lm & m_rs;\n}\n\n//-----------------------------------------------------------------------------\nFEFacetTiedSurface::FEFacetTiedSurface(FEModel* pfem) : FEContactSurface(pfem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FEFacetTiedSurface::Init()\n{\n\t// initialize surface data first\n\tif (FEContactSurface::Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FEFacetTiedSurface::CreateMaterialPoint()\n{\n\treturn new FEFacetTiedSurface::Data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetTiedSurface::Serialize(DumpStream &ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tif (ar.IsShallow())\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tfor (int n=0; n<Elements(); ++n)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el = Element(n);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int j=0; j<nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t\tar << d.m_vgap;\n\t\t\t\t\tar << d.m_vgap0;\n\t\t\t\t\tar << d.m_rs;\n\t\t\t\t\tar << d.m_Lm;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (int n = 0; n<Elements(); ++n)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el = Element(n);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t\tar >> d.m_vgap;\n\t\t\t\t\tar >> d.m_vgap0;\n\t\t\t\t\tar >> d.m_rs;\n\t\t\t\t\tar >> d.m_Lm;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tfor (int n = 0; n<Elements(); ++n)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el = Element(n);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t\tar << d.m_vgap;\n\t\t\t\t\tar << d.m_vgap0;\n\t\t\t\t\tar << d.m_rs;\n\t\t\t\t\tar << d.m_Lm;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (int n = 0; n<Elements(); ++n)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el = Element(n);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tData& d = static_cast<Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t\tar >> d.m_vgap;\n\t\t\t\t\tar >> d.m_vgap0;\n\t\t\t\t\tar >> d.m_rs;\n\t\t\t\t\tar >> d.m_Lm;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//=============================================================================\nFEFacet2FacetTied::FEFacet2FacetTied(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\t// give this interface an ID\n\tstatic int count = 1;\n\tSetID(count++);\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\t// define sibling relationships\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n\n\t// initial parameter values\n\tm_atol    = 0.01;\n\tm_eps     = 1.0;\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\tm_stol    = 0.0001;\n\tm_gapoff  = false;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FEFacet2FacetTied::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6*FEElement::MAX_NODES*2);\n\n\tFEFacetTiedSurface& ss = m_ss;\n\tFEFacetTiedSurface& ms = m_ms;\n\n\tfor (int j=0; j<ss.Elements(); ++j)\n\t{\n\t\tFESurfaceElement& se = ss.Element(j);\n\t\tint nint = se.GaussPoints();\n\t\tint* sn = &se.m_node[0];\n\t\tfor (int k=0; k<nint; ++k)\n\t\t{\n\t\t\tFEFacetTiedSurface::Data& d = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(k));\n\t\t\tFESurfaceElement* pe = d.m_pme;\n\t\t\tif (pe != 0)\n\t\t\t{\n\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\tint* mn = &me.m_node[0];\n\n\t\t\t\tlm.assign(lm.size(), -1);\n\n\t\t\t\tint nseln = se.Nodes();\n\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\tfor (int l=0; l<nseln; ++l)\n\t\t\t\t{\n\t\t\t\t\tvector<int>& id = mesh.Node(sn[l]).m_ID;\n\t\t\t\t\tlm[6*l  ] = id[dof_X];\n\t\t\t\t\tlm[6*l+1] = id[dof_Y];\n\t\t\t\t\tlm[6*l+2] = id[dof_Z];\n\t\t\t\t\tlm[6*l+3] = id[dof_RU];\n\t\t\t\t\tlm[6*l+4] = id[dof_RV];\n\t\t\t\t\tlm[6*l+5] = id[dof_RW];\n\t\t\t\t}\n\n\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tvector<int>& id = mesh.Node(mn[l]).m_ID;\n\t\t\t\t\tlm[6*(l+nseln)  ] = id[dof_X];\n\t\t\t\t\tlm[6*(l+nseln)+1] = id[dof_Y];\n\t\t\t\t\tlm[6*(l+nseln)+2] = id[dof_Z];\n\t\t\t\t\tlm[6*(l+nseln)+3] = id[dof_RU];\n\t\t\t\t\tlm[6*(l+nseln)+4] = id[dof_RV];\n\t\t\t\t\tlm[6*(l+nseln)+5] = id[dof_RW];\n\t\t\t\t}\n\n\t\t\t\tK.build_add(lm);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization. This function intializes the surfaces data\nbool FEFacet2FacetTied::Init()\n{\n\t// create the surfaces\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Interface activation. Also projects primary surface onto secondary surface\nvoid FEFacet2FacetTied::Activate()\n{\n\t// Don't forget to call base member!\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms);\n\n\tif (m_gapoff)\n\t{\n\t\t// loop over all primary elements\n\t\tfor (int i = 0; i < m_ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the primary element\n\t\t\tFESurfaceElement& se = m_ss.Element(i);\n\n\t\t\t// loop over all its integration points\n\t\t\tint nint = se.GaussPoints();\n\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t{\n\t\t\t\t// get integration point data\n\t\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\tpt.m_vgap0 = pt.m_vgap;\n\t\t\t\tpt.m_vgap = vec3d(0, 0, 0);\n\t\t\t\tpt.m_gap = 0.0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacet2FacetTied::ProjectSurface(FEFacetTiedSurface& ss, FEFacetTiedSurface& ms)\n{\n\t// get the mesh\n\tFEMesh& mesh = *ss.GetMesh();\n\n\t// closest point projection method\n\tFEClosestPointProjection cpp(ms);\n\tcpp.HandleSpecialCases(true);\n\tcpp.SetTolerance(m_stol);\n\tcpp.Init();\n\n\t// let's count contact pairs\n\tint contacts = 0;\n\n\t// loop over all primary elements\n\tfor (int i=0; i<ss.Elements(); ++i)\n\t{\n\t\t// get the primary element\n\t\tFESurfaceElement& se = ss.Element(i);\n\n\t\t// get nodal coordinates\n\t\tint nn = se.Nodes();\n\t\tvec3d re[FEElement::MAX_NODES];\n\t\tfor (int j=0; j<nn; ++j) re[j] = mesh.Node(se.m_node[j]).m_rt;\n\n\t\t// loop over all its integration points\n\t\tint nint = se.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n\t\t\t// get integration point data\n\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t// calculate the global coordinates of this integration point\n\t\t\tvec3d x = se.eval(re, j);\n\n\t\t\t// find the secondary element\n\t\t\tvec3d q; vec2d rs;\n\t\t\tFESurfaceElement* pme = cpp.Project(x, q, rs);\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// store the secondary element\n\t\t\t\tpt.m_pme = pme;\n\t\t\t\tpt.m_rs[0] = rs[0];\n\t\t\t\tpt.m_rs[1] = rs[1];\n\n\t\t\t\t// calculate gap\n\t\t\t\tpt.m_vgap = x - q;\n\n\t\t\t\tpt.m_gap = pt.m_vgap.norm();\n\n\t\t\t\tcontacts++;\n\t\t\t}\n\t\t\telse pt.m_pme = nullptr;\n\t\t}\n\t}\n\n\t// if we found no contact pairs, let's report this since this is probably not the user's intention\n\tif (contacts == 0)\n\t{\n\t\tstd::string name = GetName();\n\t\tfeLogWarning(\"No contact pairs found for tied interface \\\"%s\\\".\\nThis contact interface may not have any effect.\", name.c_str());\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Update tied interface data. This function re-evaluates the gaps between\n//! the primary node and their projections onto the secondary surface.\n//!\nvoid FEFacet2FacetTied::Update()\n{\n\t// get the mesh\n\tFEMesh& mesh = *m_ss.GetMesh();\n\n\t// loop over all primary elements\n\tconst int NE = m_ss.Elements();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// next element\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tint nseln = se.Nodes();\n\n\t\t// get the nodal coordinates\n\t\tvec3d rs[FEElement::MAX_NODES];\n\t\tfor (int j=0; j<nseln; ++j) rs[j] = mesh.Node(se.m_node[j]).m_rt;\n\n\t\t// loop over all integration points\n\t\tconst int nint = se.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// get integration point data\n\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(n));\n\n\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\tFESurfaceElement& me = static_cast<FESurfaceElement&>(*pme);\n\n\t\t\t\t// get the current primary nodal position\n\t\t\t\tvec3d rn = se.eval(rs, n);\n\n\t\t\t\t// get the natural coordinates of the primary projection\n\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\tdouble s = pt.m_rs[1];\n\n\t\t\t\t// get the secondary nodal coordinates\n\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\tvec3d y[FEElement::MAX_NODES];\n\t\t\t\tfor (int l=0; l<nmeln; ++l) y[l] = mesh.Node( me.m_node[l] ).m_rt;\n\n\t\t\t\t// calculate the primary node projection\n\t\t\t\tvec3d q = me.eval(y, r, s);\n\n\t\t\t\t// calculate the gap function\n\t\t\t\tpt.m_vgap = (rn - q) - pt.m_vgap0;\n\n\t\t\t\tpt.m_gap = pt.m_vgap.norm();\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact forces for a tied interface.\nvoid FEFacet2FacetTied::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, LM, en;\n\tvector<double> fe;\n\n\t// shape functions\n\tdouble Hm[FEElement::MAX_NODES];\n\n\t// get the mesh\n\tFEMesh& mesh = *m_ss.GetMesh();\n\n\t// loop over all elements\n\tconst int NE = m_ss.Elements();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tint nseln = se.Nodes();\n\n\t\t// integration weights\n\t\tdouble* w = se.GaussWeights();\n\n\t\t// get the element's LM vector\n\t\tm_ss.UnpackLM(se, sLM);\n\n\t\t// loop over integration points\n\t\tconst int nint = se.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// get integration point data\n\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(n));\n\n\t\t\t// get the secondary element\n\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *pme;\n\t\t\t\tm_ms.UnpackLM(me, mLM);\n\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t// get primary contact force\n\t\t\t\tvec3d tc = pt.m_Lm + pt.m_vgap*m_eps;\n\n\t\t\t\t// calculate jacobian\n\t\t\t\t// note that we are integrating over the reference surface\n\t\t\t\tdouble detJ = m_ss.jac0(se, n);\n\n\t\t\t\t// primary shape functions\n\t\t\t\tdouble* Hs = se.H(n);\n\n\t\t\t\t// secondary shape functions\n\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t// calculate degrees of freedom\n\t\t\t\tint ndof = 3*(nseln + nmeln);\n\n\t\t\t\t// calculate the force vector\n\t\t\t\tfe.resize(ndof);\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tfe[3*k  ] = -detJ*w[n]*tc.x*Hs[k];\n\t\t\t\t\tfe[3*k+1] = -detJ*w[n]*tc.y*Hs[k];\n\t\t\t\t\tfe[3*k+2] = -detJ*w[n]*tc.z*Hs[k];\n\t\t\t\t}\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tfe[3*(k+nseln)  ] = detJ*w[n]*tc.x*Hm[k];\n\t\t\t\t\tfe[3*(k+nseln)+1] = detJ*w[n]*tc.y*Hm[k];\n\t\t\t\t\tfe[3*(k+nseln)+2] = detJ*w[n]*tc.z*Hm[k];\n\t\t\t\t}\n\n\t\t\t\t// build the LM vector\n\t\t\t\tLM.resize(ndof);\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t}\n\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t}\n\n\t\t\t\t// build the en vector\n\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\tfor (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\tfor (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\n\t\t\t\t// assemble the global residual\n\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness matrix contribution.\nvoid FEFacet2FacetTied::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, LM, en;\n\tFEElementMatrix ke;\n\n\t// shape functions\n\tdouble Hm[FEElement::MAX_NODES];\n\n\t// loop over all primary elements\n\tconst int NE = m_ss.Elements();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tint nseln = se.Nodes();\n\n\t\t// get the element's LM vector\n\t\tm_ss.UnpackLM(se, sLM);\n\n\t\t// integration weights\n\t\tdouble* w = se.GaussWeights();\n\n\t\t// loop over all integration points\n\t\tconst int nint = se.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// get intgration point data\n\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(n));\n\n\t\t\t// get the secondary element\n\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *pme;\n\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\tm_ms.UnpackLM(me, mLM);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdouble detJ = m_ss.jac0(se, n);\n\n\t\t\t\t// primary shape functions\n\t\t\t\tdouble* Hs = se.H(n);\n\n\t\t\t\t// secondary shape functions\n\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t// calculate degrees of freedom\n\t\t\t\tint ndof = 3*(nseln + nmeln);\n\n\t\t\t\t// create the stiffness matrix\n\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\tke.zero();\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tfor (int l=0; l<nseln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3*k  ][3*l  ] = Hs[k]*Hs[l];\n\t\t\t\t\t\tke[3*k+1][3*l+1] = Hs[k]*Hs[l];\n\t\t\t\t\t\tke[3*k+2][3*l+2] = Hs[k]*Hs[l];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3*k  ][3*(l+nseln)  ] = -Hs[k]*Hm[l];\n\t\t\t\t\t\tke[3*k+1][3*(l+nseln)+1] = -Hs[k]*Hm[l];\n\t\t\t\t\t\tke[3*k+2][3*(l+nseln)+2] = -Hs[k]*Hm[l];\n\n\t\t\t\t\t\tke[3*(l+nseln)  ][3*k  ] = -Hs[k]*Hm[l];\n\t\t\t\t\t\tke[3*(l+nseln)+1][3*k+1] = -Hs[k]*Hm[l];\n\t\t\t\t\t\tke[3*(l+nseln)+2][3*k+2] = -Hs[k]*Hm[l];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3*(k+nseln)  ][3*(l+nseln)  ] = Hm[k]*Hm[l];\n\t\t\t\t\t\tke[3*(k+nseln)+1][3*(l+nseln)+1] = Hm[k]*Hm[l];\n\t\t\t\t\t\tke[3*(k+nseln)+2][3*(l+nseln)+2] = Hm[k]*Hm[l];\n\t\t\t\t\t}\n\n\t\t\t\tfor (int k=0; k<ndof; ++k)\n\t\t\t\t\tfor (int l=0; l<ndof; ++l) ke[k][l] *= m_eps*detJ*w[n];\n\n\t\t\t\t// build the LM vector\n\t\t\t\tLM.resize(ndof);\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t}\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t}\n\n\t\t\t\t// build the en vector\n\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\tfor (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\tfor (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\n\t\t\t\t// assemble the global residual\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(LM);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Do an augmentation.\nbool FEFacet2FacetTied::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (int i=0; i<m_ss.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tfor (int j=0; j<se.GaussPoints(); ++j)\n\t\t{\n\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(j));\n\t\t\tvec3d& lm = pt.m_Lm;\n\t\t\tnormL0 += lm*lm;\n\t\t}\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (int i=0; i<m_ss.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tfor (int j=0; j<se.GaussPoints(); ++j)\n\t\t{\n\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\tvec3d lm = pt.m_Lm + pt.m_vgap*m_eps;\n\n\t\t\tnormL1 += lm*lm;\n\t\t\tif (pt.m_pme != 0)\n\t\t\t{\n\t\t\t\tdouble g = pt.m_vgap.norm();\n\t\t\t\tnormgc += g*g;\n\t\t\t\t++N;\n\t\t\t}\n\t\t}\n\t}\t\n\tif (N == 0) N=1;\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\t// check convergence of constraints\n\tfeLog(\" tied interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\t\t\n\t// check convergence\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tif (bconv == false) \n\t{\n\t\tfor (int i=0; i<m_ss.Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\t\tfor (int j=0; j<se.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFEFacetTiedSurface::Data& pt = static_cast<FEFacetTiedSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// update Lagrange multipliers\n\t\t\t\tpt.m_Lm = pt.m_Lm + pt.m_vgap*m_eps;\n\t\t\t}\n\t\t}\t\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize the data to the archive.\nvoid FEFacet2FacetTied::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ss.Serialize(ar);\n\tm_ms.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMech/FEFacet2FacetTied.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! Surface definition for the facet-to-facet tied interface\nclass FEFacetTiedSurface : public FEContactSurface\n{\npublic:\n\t//! integration point data\n\tclass Data : public FEContactMaterialPoint\n\t{\n\tpublic:\n\t\tData();\n\n\t\tvoid Init() override;\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\tpublic:\n\t\tvec3d\tm_vgap;\t\t//!< gap function\n\t\tvec3d\tm_vgap0;\t//!< initial gap function\n\t\tvec3d\tm_Lm;\t//!< Lagrange multiplier\n\t\tvec2d\tm_rs;\t//!< natural coordinates on secondary surface element\n\t};\n\npublic:\n\t//! constructor\n\tFEFacetTiedSurface(FEModel* pfem);\n\n\t//! Initialization\n\tbool Init() override;\n\n\t//! serialization for cold restarts\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n};\n\n//-----------------------------------------------------------------------------\n//! Tied contact interface with facet-to-facet integration\nclass FEFacet2FacetTied : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEFacet2FacetTied(FEModel* pfem);\n\n\t//! Initialization\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update contact data\n\tvoid Update() override;\n\nprotected:\n\n\t//! projects primary nodes onto secondary nodes\n\tvoid ProjectSurface(FEFacetTiedSurface& ss, FEFacetTiedSurface& ms);\n\nprivate:\n\tFEFacetTiedSurface\tm_ms;\t//!< secondary surface\n\tFEFacetTiedSurface\tm_ss;\t//!< primary surface\n\npublic:\n\tdouble\t\tm_atol;\t\t//!< augmentation tolerance\n\tdouble\t\tm_eps;\t\t//!< penalty scale factor\n\tdouble\t\tm_stol;\t\t//!< search tolerance\n\tint\t\t\tm_naugmax;\t//!< maximum nr of augmentations\n\tint\t\t\tm_naugmin;\t//!< minimum nr of augmentations\n\tbool\t\tm_gapoff;\t//!< retain initial gap as offset\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberCDF.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberCDF.h\"\n#include \"FEFiberCDFMaterialPoint.h\"\n#include <limits>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// FEFiberCDF\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberCDF, FEFiberMaterial)\n\tADD_PARAMETER(m_E  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\" )->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus\");\n    ADD_PROPERTY (m_CDF,  \"cdf\")->SetLongName(\"cumulative distribution function\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberCDF::FEFiberCDF(FEModel* pfem) : FEFiberMaterial(pfem)\n{ \n\tm_E = 0;\n\tm_epsf = 1.0;\n    m_CDF = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEFiberCDF::CreateMaterialPointData()\n{\n    return new FEFiberCDFMaterialPoint(FEFiberMaterial::CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberCDF::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds s;\n\t\n\t// Calculate In - 1 = n0*C*n0 - 1\n\tdouble In_1 = n0*(C*n0) - 1;\n    \n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    fp.SetFiberStrain(In_1);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = m_epsf* std::numeric_limits<double>::epsilon();\n\tif (In_1 >= eps)\n\t{\n        double ksi = m_E(mp)/4;\n        \n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\t\n        // perform integration\n        Integrate(mp, In_1);\n\n\t\t// calculate strain energy first derivative\n        double Wl = ksi*fp.m_ds_t*In_1;\n\n\t\t// calculate the fiber stress\n\t\ts = N*(2.0*Wl/J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberCDF::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\ttens4ds c;\n\t\n    // Calculate In - 1 = n0*C*n0 - 1\n    double In_1 = n0*(C*n0) - 1;\n\n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    fp.SetFiberStrain(In_1);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = m_epsf*std::numeric_limits<double>::epsilon();\n\tif (In_1 >= eps)\n\t{\n        double ksi = m_E(mp)/4;\n        \n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n        \n        // perform integration\n        Integrate(mp, In_1);\n\n\t\t// calculate strain energy 2nd derivative\n        double Wll = ksi*fp.m_d2s_t;\n\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(4.0*Wll/J);\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberCDF::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\t\n    // Calculate In - 1 = n0*C*n0 - 1\n    double In = n0*(C*n0);\n    double In_1 = In - 1.0;\n    \n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    fp.SetFiberStrain(In_1);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = 0;\n\tif (In_1 >= eps)\n\t{\n        double ksi = m_E(mp)/4;\n\n        // perform integration\n        Integrate(mp, In_1);\n\n\t\t// calculate strain energy\n        sed = fp.m_sed_t*ksi;\n\t}\n\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberCDF::Integrate(FEMaterialPoint& mp, const double In_1)\n{\n    double dImax = 1e-2;\n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    \n    // check increment in strain\n    double dIn = In_1 - fp.m_In_1_p;\n    \n    // if small enough, integrate in a single step\n    if (fabs(dIn) <= dImax) {\n        fp.Integrate(m_CDF->cdf(mp,In_1));\n        return;\n    }\n    // otherwise, integrate over multiple steps\n    double In_1_p = fp.m_In_1_p;\n    double d2s_p = fp.m_d2s_p;\n    double ds_p = fp.m_ds_p;\n    double sed_p = fp.m_sed_p;\n    double In_1_t, d2s_t, ds_t, sed_t;\n    do {\n        In_1_t = In_1_p + dImax;\n        if (In_1_t > In_1) In_1_t = In_1;\n        dIn = In_1_t - In_1_p;\n        d2s_t = m_CDF->cdf(mp,In_1_t);\n        sed_t = sed_p + ds_p*dIn + 0.25*dIn*dIn*(d2s_t + d2s_p);\n        ds_t = ds_p + 0.5*dIn*(d2s_t + d2s_p);\n        fp.m_d2s_t = d2s_t;\n        fp.m_ds_t = ds_t;\n        fp.m_sed_t = sed_t;\n        In_1_p = In_1_t;\n        d2s_p = d2s_t;\n        ds_p = ds_t;\n        sed_p = sed_t;\n    } while (In_1_t < In_1);\n    return;\n}\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberCDF, FEElasticFiberMaterial)\n    ADD_PARAMETER(m_fib.m_E  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\" )->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus\");\n    ADD_PROPERTY (m_fib.m_CDF  ,  \"cdf\")->SetLongName(\"cumulative distribution function\");\nEND_FECORE_CLASS();\n\n"
  },
  {
    "path": "FEBioMech/FEFiberCDF.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n#include \"FEDamageCDF.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Cumulative distribution\n\nclass FEFiberCDF : public FEFiberMaterial\n{\npublic:\n    FEFiberCDF(FEModel* pfem);\n\t\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\t\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\t\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! Perform integration\n    void Integrate(FEMaterialPoint& mp, const double In_1);\n    \nprotected:\n\tFEParamDouble   m_E;\t\t// Young's modulus when all fibers are engaged\n\tdouble\tm_epsf;\n\n    FEDamageCDF*    m_CDF;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\n\tfriend class FEElasticFiberCDF;\n};\n\n//-----------------------------------------------------------------------------\nclass FEElasticFiberCDF : public FEElasticFiberMaterial_T<FEFiberCDF>\n{\npublic:\n    FEElasticFiberCDF(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberCDF>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEFiberCDFMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"FEFiberCDFMaterialPoint.h\"\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\n// FEFiberCDFMaterialPoint\n//-----------------------------------------------------------------------------\n\nFEFiberCDFMaterialPoint::FEFiberCDFMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt)\n{\n    m_In_1_p = m_In_1_t = 0;\n    m_sed_t = m_sed_p = 0;\n    m_ds_t = m_ds_p = 0;\n    m_d2s_p = m_d2s_t = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEFiberCDFMaterialPoint::Copy()\n{\n    FEFiberCDFMaterialPoint* pt = new FEFiberCDFMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberCDFMaterialPoint::Init()\n{\n    // initialize data\n    m_In_1_p = m_In_1_t = 0;\n    m_sed_t = m_sed_p = 0;\n    m_ds_t = m_ds_p = 0;\n    m_d2s_p = m_d2s_t = 0;\n    \n    // don't forget to intialize the nested data\n    FEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberCDFMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n    m_In_1_p = m_In_1_t;\n    m_sed_p = m_sed_t;\n    m_ds_p = m_ds_t;\n    m_d2s_p = m_d2s_t;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberCDFMaterialPoint::Serialize(DumpStream& ar)\n{\n    FEMaterialPointData::Serialize(ar);\n    ar & m_In_1_t & m_In_1_p;\n    ar & m_sed_t & m_sed_p;\n    ar & m_ds_t & m_ds_p;\n    ar & m_d2s_t & m_d2s_p;\n}\n\n//-----------------------------------------------------------------------------\n// Perform integration in one step\nvoid FEFiberCDFMaterialPoint::Integrate(const double cdf)\n{\n    m_d2s_t = (cdf > 0) ? cdf : 0;\n\n    double dIn = m_In_1_t - m_In_1_p;\n    m_sed_t = m_sed_p + m_ds_p*dIn + 0.25*dIn*dIn*(m_d2s_t + m_d2s_p);\n    m_ds_t = m_ds_p + 0.5*dIn*(m_d2s_t + m_d2s_p);\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberCDFMaterialPoint::SetFiberStrain(const double In_1) {\n    m_In_1_t = In_1;\n    if (m_In_1_t <= 0) {\n        m_d2s_t = 0;\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberCDFMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores fiber CDF data and integrates it\nclass FEFiberCDFMaterialPoint : public FEMaterialPointData\n{\npublic:\n    FEFiberCDFMaterialPoint(FEMaterialPointData* pt);\n    \n    FEMaterialPointData* Copy() override;\n    \n    void Init() override;\n    \n    void Serialize(DumpStream& ar) override;\n    \n    void Update(const FETimeInfo& timeInfo) override;\n    \npublic:\n    void SetFiberStrain(const double In_1);\n    \n    void Integrate(const double cdf);\n    \npublic:\n    double  m_In_1_t, m_In_1_p; //!< In - 1 (square of stretch ratio along fiber - 1) at current and previous time\n    double  m_sed_t, m_sed_p;   //!< normalized sed at current and previous time\n    double  m_ds_t, m_ds_p;     //!< first derivative of normalized sed w.r.t. In at current and previous time\n    double  m_d2s_t, m_d2s_p;   //!< second derivative of normalized sed w.r.t. In at current and previous time\n};\n\n"
  },
  {
    "path": "FEBioMech/FEFiberCDFUncoupled.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberCDFUncoupled.h\"\n#include \"FEFiberCDFMaterialPoint.h\"\n#include <limits>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// FEFiberCDFUncoupled\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberCDFUncoupled, FEFiberMaterialUncoupled)\n    ADD_PARAMETER(m_E  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\" )->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus\");\n    ADD_PROPERTY (m_CDF,  \"cdf\")->SetLongName(\"cumulative distribution function\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberCDFUncoupled::FEFiberCDFUncoupled(FEModel* pfem) : FEFiberMaterialUncoupled(pfem)\n{\n    m_E = 0;\n    m_epsf = 1.0;\n    m_CDF = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEFiberCDFUncoupled::CreateMaterialPointData()\n{\n    return new FEFiberCDFMaterialPoint(FEFiberMaterialUncoupled::CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberCDFUncoupled::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F*pow(J, -1./3.);\n    \n    // loop over all integration points\n    mat3ds C = pt.DevRightCauchyGreen();\n    mat3ds s;\n    \n    // Calculate In - 1 = n0*C*n0 - 1\n    double In_1 = n0*(C*n0) - 1;\n    \n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    fp.SetFiberStrain(In_1);\n    \n    // only take fibers in tension into consideration\n    const double eps = m_epsf* std::numeric_limits<double>::epsilon();\n    if (In_1 >= eps)\n    {\n        double ksi = m_E(mp)/4;\n        \n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // perform integration\n        Integrate(mp, In_1);\n\n        // calculate strain energy first derivative\n        double Wl = ksi*fp.m_ds_t*In_1;\n        \n        // calculate the fiber stress\n        s = N*(2.0*Wl/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberCDFUncoupled::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F*pow(J, -1./3.);\n    \n    // loop over all integration points\n    mat3ds C = pt.DevRightCauchyGreen();\n    tens4ds c;\n    \n    // Calculate In - 1 = n0*C*n0 - 1\n    double In_1 = n0*(C*n0) - 1;\n    \n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    fp.SetFiberStrain(In_1);\n    \n    // only take fibers in tension into consideration\n    const double eps = m_epsf*std::numeric_limits<double>::epsilon();\n    if (In_1 >= eps)\n    {\n        double ksi = m_E(mp)/4;\n        \n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // perform integration\n        Integrate(mp, In_1);\n\n        // calculate strain energy 2nd derivative\n        double Wll = ksi*fp.m_d2s_t;\n        \n        // calculate the fiber tangent\n        c = NxN*(4.0*Wll/J);\n        \n        // calculate strain energy first derivative\n        double Wl = ksi*fp.m_ds_t*In_1;\n        \n        // calculate the fiber stress\n        mat3ds s = N*(2.0*Wl/J);\n        \n        // This is the final value of the elasticity tensor\n        mat3dd I(1);\n        tens4ds IxI = dyad1s(I);\n        tens4ds I4  = dyad4s(I);\n        c += ((I4+IxI/3.0)*s.tr() - dyad1s(I,s))*(2./3.)\n        - (ddots(IxI, c)-IxI*(c.tr()/3.))/3.;\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberCDFUncoupled::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d F = pt.m_F*pow(pt.m_J, -1./3.);\n\n    // loop over all integration points\n    mat3ds C = pt.DevRightCauchyGreen();\n    \n    // Calculate In - 1 = n0*C*n0 - 1\n    double In = n0*(C*n0);\n    double In_1 = In - 1.0;\n    \n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    fp.SetFiberStrain(In_1);\n    \n    // only take fibers in tension into consideration\n    const double eps = 0;\n    if (In_1 >= eps)\n    {\n        double ksi = m_E(mp)/4;\n        \n        // perform integration\n        Integrate(mp, In_1);\n\n        // calculate strain energy\n        sed = fp.m_sed_t*ksi;\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberCDFUncoupled::Integrate(FEMaterialPoint& mp, const double In_1)\n{\n    double dImax = 1e-2;\n    FEFiberCDFMaterialPoint& fp = *mp.ExtractData<FEFiberCDFMaterialPoint>();\n    \n    // check increment in strain\n    double dIn = In_1 - fp.m_In_1_p;\n    \n    // if small enough, integrate in a single step\n    if (fabs(dIn) <= dImax) {\n        fp.Integrate(m_CDF->cdf(mp,In_1));\n        return;\n    }\n    // otherwise, integrate over multiple steps\n    double In_1_p = fp.m_In_1_p;\n    double d2s_p = fp.m_d2s_p;\n    double ds_p = fp.m_ds_p;\n    double sed_p = fp.m_sed_p;\n    double In_1_t, d2s_t, ds_t, sed_t;\n    do {\n        In_1_t = In_1_p + dImax;\n        if (In_1_t > In_1) In_1_t = In_1;\n        dIn = In_1_t - In_1_p;\n        d2s_t = m_CDF->cdf(mp,In_1_t);\n        sed_t = sed_p + ds_p*dIn + 0.25*dIn*dIn*(d2s_t + d2s_p);\n        ds_t = ds_p + 0.5*dIn*(d2s_t + d2s_p);\n        fp.m_d2s_t = d2s_t;\n        fp.m_ds_t = ds_t;\n        fp.m_sed_t = sed_t;\n        In_1_p = In_1_t;\n        d2s_p = d2s_t;\n        ds_p = ds_t;\n        sed_p = sed_t;\n    } while (In_1_t < In_1);\n    return;\n}\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberCDFUncoupled, FEElasticFiberMaterialUC)\n    ADD_PARAMETER(m_fib.m_E  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\" )->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus\");\n    ADD_PROPERTY (m_fib.m_CDF  ,  \"cdf\")->SetLongName(\"cumulative distribution function\");\nEND_FECORE_CLASS();\n\n"
  },
  {
    "path": "FEBioMech/FEFiberCDFUncoupled.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberMaterial.h\"\n#include \"FEDamageCDF.h\"\n\nclass FEElasticFiberCDFUncoupled;\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Cumulative distribution\n\nclass FEFiberCDFUncoupled : public FEFiberMaterialUncoupled\n{\npublic:\n    FEFiberCDFUncoupled(FEModel* pfem);\n    \n    //! Cauchy stress\n    mat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // Spatial tangent\n    tens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    //! Strain energy density\n    double DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! Perform integration\n    void Integrate(FEMaterialPoint& mp, const double In_1);\n    \nprotected:\n    FEParamDouble   m_E;        // fiber modulus\n    double          m_epsf;\n    \n    FEDamageCDF*    m_CDF;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n    \n    friend class FEElasticFiberCDFUncoupled;\n};\n\n//-----------------------------------------------------------------------------\nclass FEElasticFiberCDFUncoupled : public FEElasticFiberMaterialUC_T<FEFiberCDFUncoupled>\n{\npublic:\n    FEElasticFiberCDFUncoupled(FEModel* fem) : FEElasticFiberMaterialUC_T<FEFiberCDFUncoupled>(fem) {}\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEFiberDensityDistribution.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberDensityDistribution.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n// define the ellipsoidal fiber density distributionmaterial parameters\nBEGIN_FECORE_CLASS(FEEllipsoidalFiberDensityDistribution, FEFiberDensityDistribution)\n\tADD_PARAMETER(m_spa, 3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"spa\");\nEND_FECORE_CLASS();\n\nFEEllipsoidalFiberDensityDistribution::FEEllipsoidalFiberDensityDistribution(FEModel* pfem) : FEFiberDensityDistribution(pfem)\n{ \n\tm_spa[0] = 1;\n\tm_spa[1] = 1;\n\tm_spa[2] = 1;\n}\n\ndouble FEEllipsoidalFiberDensityDistribution::FiberDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tdouble a0 = m_spa[0](mp);\n\tdouble a1 = m_spa[1](mp);\n\tdouble a2 = m_spa[2](mp);\n\n    double R = 1.0/sqrt(SQR(n0.x/a0)+SQR(n0.y/a1)+SQR(n0.z/a2));\n    return R;\n}\n\n//-----------------------------------------------------------------------------\n// define the 3d von Mises fiber density distribution material parameters\nBEGIN_FECORE_CLASS(FEVonMises3DFiberDensityDistribution, FEFiberDensityDistribution)\n\tADD_PARAMETER(m_b, FE_RANGE_GREATER_OR_EQUAL(0.0), \"b\" )->setLongName(\"concentration\");\nEND_FECORE_CLASS();\n\ndouble FEVonMises3DFiberDensityDistribution::FiberDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    // The local x-direction is the principal fiber bundle direction\n    // The x-component of n0 is cos(phi)\n    double b = m_b(mp);\n    double R = exp(b*(2*SQR(n0.x)-1));\n    return R;\n}\n\n//-----------------------------------------------------------------------------\n// define the 3d 2-fiber family axisymmetric von Mises fiber density distribution material parameters\nBEGIN_FECORE_CLASS(FEVonMises3DTwoFDDAxisymmetric, FEFiberDensityDistribution)\n\tADD_PARAMETER(m_b, FE_RANGE_GREATER_OR_EQUAL(0.0), \"b\" );\n\tADD_PARAMETER(m_c, FE_RANGE_CLOSED(0, 1), \"cosg\" );\nEND_FECORE_CLASS();\n\nFEVonMises3DTwoFDDAxisymmetric::FEVonMises3DTwoFDDAxisymmetric(FEModel* pfem) : FEFiberDensityDistribution(pfem) \n{ \n\tm_b = 0; \n\tm_c = 1; \n}\n\ndouble FEVonMises3DTwoFDDAxisymmetric::FiberDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tdouble b = m_b(mp);\n\tdouble c = m_c(mp);\n\n    // The local x-direction is the principal fiber bundle direction\n    // The x-component of n0 is cos(phi)\n    double cphi = n0.x; double sphi = sqrt(1-cphi*cphi);\n    double sing = sqrt(1-c*c);\n    double cp = cphi*c - sphi*sing;\n    double cm = cphi*c + sphi*sing;\n    double R = exp(b*(2*SQR(cp)-1)) + exp(b*(2*SQR(cm)-1));\n    return R;\n}\n\n//-----------------------------------------------------------------------------\n// define the ellipsoidal fiber density distributionmaterial parameters\nBEGIN_FECORE_CLASS(FEEllipticalFiberDensityDistribution, FEFiberDensityDistribution)\n\tADD_PARAMETER(m_spa[0], FE_RANGE_GREATER_OR_EQUAL(0.0), \"spa1\" );\n\tADD_PARAMETER(m_spa[1], FE_RANGE_GREATER_OR_EQUAL(0.0), \"spa2\" );\nEND_FECORE_CLASS();\n\ndouble FEEllipticalFiberDensityDistribution::FiberDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    // 2d fibers lie in the local x-y plane\n    // n0.x = cos(theta) and n0.y = sin(theta)\n    double a0 = m_spa[0](mp);\n    double a1 = m_spa[1](mp);\n    double R = 1.0/sqrt(SQR(n0.x/a0)+SQR(n0.y/a1));\n    return R;\n}\n\n//-----------------------------------------------------------------------------\n// define the 2d von Mises fiber density distribution material parameters\nBEGIN_FECORE_CLASS(FEVonMises2DFiberDensityDistribution, FEFiberDensityDistribution)\n\tADD_PARAMETER(m_b, FE_RANGE_GREATER_OR_EQUAL(0.0), \"b\" )->setLongName(\"concentration\");\nEND_FECORE_CLASS();\n\ndouble FEVonMises2DFiberDensityDistribution::FiberDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    // The fiber bundle is in the x-y plane and\n    // the local x-direction is the principal fiber bundle direction\n    // The x-component of n0 is cos(theta)\n    double b = m_b(mp);\n    double R = exp(b*(2*SQR(n0.x)-1));\n    return R;\n}\n\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEStructureTensorDistribution, FEFiberDensityDistribution)\n\tADD_PARAMETER(m_SPD, \"spd\");\nEND_FECORE_CLASS();\n\nFEStructureTensorDistribution::FEStructureTensorDistribution(FEModel* fem) : FEFiberDensityDistribution(fem)\n{\n\tm_SPD = mat3dd(1.0);\n}\n\ndouble FEStructureTensorDistribution::FiberDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\treturn n0 * (m_SPD(mp)*n0);\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberDensityDistribution.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FECore/FEMaterial.h\"\n#include \"febiomech_api.h\"\n\n//---------------------------------------------------------------------------\n// Base class for fiber density distribution functions\n//\nclass FEBIOMECH_API FEFiberDensityDistribution : public FEMaterialProperty\n{\npublic:\n    FEFiberDensityDistribution(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    \n    // Evaluation of fiber density along n0\n    virtual double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) = 0;\n\n    FECORE_BASE_CLASS(FEFiberDensityDistribution)\n};\n\n//---------------------------------------------------------------------------\n// Spherical fiber density distribution\n//\nclass FESphericalFiberDensityDistribution : public FEFiberDensityDistribution\n{\npublic:\n    FESphericalFiberDensityDistribution(FEModel* pfem) : FEFiberDensityDistribution(pfem) {}\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override { return 1.0; }\n};\n\n//---------------------------------------------------------------------------\n// Ellipsoidal fiber density distribution\n//\nclass FEEllipsoidalFiberDensityDistribution : public FEFiberDensityDistribution\n{\npublic:\n\tFEEllipsoidalFiberDensityDistribution(FEModel* pfem);\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n    \npublic:\n\tFEParamDouble m_spa[3]; // semi-principal axes of ellipsoid\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------\n// 3D axisymmetric von Mises fiber density distribution\n//\nclass FEVonMises3DFiberDensityDistribution : public FEFiberDensityDistribution\n{\npublic:\n    FEVonMises3DFiberDensityDistribution(FEModel* pfem) : FEFiberDensityDistribution(pfem) { m_b = 0; }\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n    \npublic:\n    FEParamDouble m_b;         // concentration parameter\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------\n// 3D 2-fiber families axisymmetric von Mises fiber density distribution\n//\nclass FEVonMises3DTwoFDDAxisymmetric : public FEFiberDensityDistribution\n{\npublic:\n\tFEVonMises3DTwoFDDAxisymmetric(FEModel* pfem);\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n    \npublic:\n    FEParamDouble\tm_b;\t\t// concentration parameter\n    FEParamDouble\tm_c;         // cosine of ±angle offset of fiber families\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------\n// Circular fiber density distribution (2d)\n//\nclass FECircularFiberDensityDistribution : public FEFiberDensityDistribution\n{\npublic:\n    FECircularFiberDensityDistribution(FEModel* pfem) : FEFiberDensityDistribution(pfem) {}\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override { return 1.0; }\n};\n\n//---------------------------------------------------------------------------\n// Elliptical fiber density distribution (2d)\n//\nclass FEEllipticalFiberDensityDistribution : public FEFiberDensityDistribution\n{\npublic:\n    FEEllipticalFiberDensityDistribution(FEModel* pfem) : FEFiberDensityDistribution(pfem) { m_spa[0] = 1; m_spa[1] = 1; }\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n    \npublic:\n    FEParamDouble m_spa[2];    // semi-principal axes of ellipse\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------\n// 2D planar von Mises fiber density distribution\n//\nclass FEVonMises2DFiberDensityDistribution : public FEFiberDensityDistribution\n{\npublic:\n    FEVonMises2DFiberDensityDistribution(FEModel* pfem) : FEFiberDensityDistribution(pfem) { m_b = 0; }\n    \n    double FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n    \npublic:\n    FEParamDouble m_b;         // concentration parameter\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------\nclass FEStructureTensorDistribution : public FEFiberDensityDistribution\n{\npublic:\n\tFEStructureTensorDistribution(FEModel* fem);\n\n\tdouble FiberDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n\npublic:\n\tFEParamMat3ds\tm_SPD;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberEFDNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberNeoHookean.h\"\n#include \"FEFiberEFDNeoHookean.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberEFDNeoHookean, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\n\tADD_PARAMETER(m_a, 3, \"a\");\n\tADD_PARAMETER(m_ac, \"active_contraction\")->setUnits(UNIT_PRESSURE);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n#ifndef SQR\n\t#define SQR(x) ((x)*(x))\n#endif\n\n//////////////////////////////////////////////////////////////////////\n// FEFiberNeoHookean\n//////////////////////////////////////////////////////////////////////\n\n\nFEFiberEFDNeoHookean::FEFiberEFDNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_nres = 0;\n\tm_a[0] = m_a[1] = m_a[2] = 0;\n\tm_ac = 0;\n}\n\nbool FEFiberEFDNeoHookean::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n    m_bfirst = true;\n    \n\tif (m_bfirst)\n\t{\n\t\t// select the integration rule\n\t\tconst int nint    = (m_nres == 0? NSTL  : NSTH  );\n\t\tconst double* phi = (m_nres == 0? PHIL  : PHIH  );\n\t\tconst double* the = (m_nres == 0? THETAL: THETAH);\n\t\tconst double* w   = (m_nres == 0? AREAL : AREAH );\n        \n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\tm_cth[n] = cos(the[n]);\n\t\t\tm_sth[n] = sin(the[n]);\n\t\t\tm_cph[n] = cos(phi[n]);\n\t\t\tm_sph[n] = sin(phi[n]);\n\t\t\tm_w[n] = w[n];\n\t\t}\n        \n\t\tm_bfirst = false;\n\t}\n\n\treturn true;\n}\n\nmat3ds FEFiberEFDNeoHookean::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\tdouble lndetF = log(detF);\n\n\t// calculate left Cauchy-Green tensor\n\t// (we commented out the matrix components we do not need)\n\tdouble b[3][3];\n\n\tb[0][0] = F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2];\n\tb[0][1] = F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2];\n\tb[0][2] = F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2];\n\n//\tb[1][0] = F[1][0]*F[0][0]+F[1][1]*F[0][1]+F[1][2]*F[0][2];\n\tb[1][1] = F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2];\n\tb[1][2] = F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2];\n\n//\tb[2][0] = F[2][0]*F[0][0]+F[2][1]*F[0][1]+F[2][2]*F[0][2];\n//\tb[2][1] = F[2][0]*F[1][0]+F[2][1]*F[1][1]+F[2][2]*F[1][2];\n\tb[2][2] = F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2];\n\n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\n\t// calculate stress\n\tmat3ds s;\n\n\ts.xx() = (mu*(b[0][0] - 1) + lam*lndetF)*detFi;\n\ts.yy() = (mu*(b[1][1] - 1) + lam*lndetF)*detFi;\n\ts.zz() = (mu*(b[2][2] - 1) + lam*lndetF)*detFi;\n\ts.xy() = mu*b[0][1]*detFi;\n\ts.yz() = mu*b[1][2]*detFi;\n\ts.xz() = mu*b[0][2]*detFi;\n\n\t// --- F I B E R   C O N T R I B U T I O N ---\n\tif (m_ac)\n\t{\n\t\t// select the integration rule\n\t\tconst int nint    = (m_nres == 0? NSTL  : NSTH  );\n\t\tconst double* phi = (m_nres == 0? PHIL  : PHIH  );\n\t\tconst double* the = (m_nres == 0? THETAL: THETAH);\n\t\tconst double* w   = (m_nres == 0? AREAL : AREAH );\n\n\t\t// get the local coordinate systems\n\t\tmat3d Q = GetLocalCS(mp);\n\n\t\t// loop over all integration points\n\t\tdouble nr[3], n0[3], nt[3];\n\t\tdouble at;\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// set the local fiber direction\n\t\t\tnr[0] = m_cth[n]*m_sph[n];\n\t\t\tnr[1] = m_sth[n]*m_sph[n];\n\t\t\tnr[2] = m_cph[n];\n\n\t\t\t// get the global material fiber direction\n\t\t\tn0[0] = Q[0][0]*nr[0] + Q[0][1]*nr[1] + Q[0][2]*nr[2];\n\t\t\tn0[1] = Q[1][0]*nr[0] + Q[1][1]*nr[1] + Q[1][2]*nr[2];\n\t\t\tn0[2] = Q[2][0]*nr[0] + Q[2][1]*nr[1] + Q[2][2]*nr[2];\n\n\t\t\t// get the global spatial fiber direction\n\t\t\tnt[0] = F[0][0]*n0[0] + F[0][1]*n0[1] + F[0][2]*n0[2];\n\t\t\tnt[1] = F[1][0]*n0[0] + F[1][1]*n0[1] + F[1][2]*n0[2];\n\t\t\tnt[2] = F[2][0]*n0[0] + F[2][1]*n0[1] + F[2][2]*n0[2];\n\n\t\t\t// add active contraction stuff\n\t\t\tat = m_ac *sqrt(SQR(m_a[0]*nr[0]) + SQR(m_a[1]*nr[1]) + SQR(m_a[2]*nr[2]));\n\n\t\t\ts.xx() += at*nt[0]*nt[0];\n\t\t\ts.yy() += at*nt[1]*nt[1];\n\t\t\ts.zz() += at*nt[2]*nt[2];\n\t\t\ts.xy() += at*nt[0]*nt[1];\n\t\t\ts.yz() += at*nt[1]*nt[2];\n\t\t\ts.xz() += at*nt[0]*nt[2];\n\t\t}\n\t}\n\n\treturn s;\n}\n\ntens4ds FEFiberEFDNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\n\tdouble lam1 = lam / detF;\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\n\t\n\tdouble D[6][6] = {0};\n\tD[0][0] = lam1+2.*mu1; D[0][1] = lam1       ; D[0][2] = lam1       ;\n\tD[1][0] = lam1       ; D[1][1] = lam1+2.*mu1; D[1][2] = lam1       ;\n\tD[2][0] = lam1       ; D[2][1] = lam1       ; D[2][2] = lam1+2.*mu1;\n\tD[3][3] = mu1;\n\tD[4][4] = mu1;\n\tD[5][5] = mu1;\n\n\treturn tens4ds(D);\n}\n\ndouble FEFiberEFDNeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\tdouble J = pt.m_J;\n\tdouble lnJ = log(J);\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n    \n    double I1 = b.tr();\n    \n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n    \n\t// calculate strain energy density\n\tdouble sed = mu*((I1-3)/2.0 - lnJ) + lam*lnJ*lnJ/2.0;\n    \n\t// --- F I B E R   C O N T R I B U T I O N ---\n    // active contraction does not contribute to the strain energy density\n    \n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberEFDNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n// The following file contains the integration points and weights\n// for the integration over a unit sphere in spherical coordinates\n#include \"geodesic.h\"\n\nclass FEFiberEFDNeoHookean : public FEElasticMaterial\n{\npublic:\n\tFEFiberEFDNeoHookean(FEModel* pfem);\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& mp) override;\n\n\t//! material parameter intialization and checking\n\tbool Init() override;\n\npublic:\n\tdouble\tm_E;\t//!< Young's modulus\n\tdouble\tm_v;\t//!< Poisson's ratio\n\n\t//--- active contraction stuff ---\n\tdouble\tm_a[3];\n\tdouble\tm_ac;\n\t// -------------------------------\n\n\t// numerical quadrature stuff\n\tint     m_nres;\t// integration rule\n\n\tdouble\tm_cth[NSTH];\n\tdouble\tm_sth[NSTH];\n\tdouble\tm_cph[NSTH];\n\tdouble\tm_sph[NSTH];\n\tdouble  m_w[NSTH];\n\tbool    m_bfirst;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberEntropyChain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\r\nlisted below.\r\n\r\nSee Copyright-FEBio.txt for details.\r\n\r\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in \r\nthe City of New York, and others.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.*/\r\n\r\n\r\n#include \"stdafx.h\"\r\n#include \"FEFiberEntropyChain.h\"\r\n#include <iostream>\r\n#include \"triangle_sphere.h\"\r\n#include <limits>\r\n#include <FECore/log.h>\r\n\r\n//-----------------------------------------------------------------------------\r\n// FEFiberEntropyChain\r\n//-----------------------------------------------------------------------------\r\n\r\n// define the material parameters\r\nBEGIN_FECORE_CLASS(FEFiberEntropyChain, FEFiberMaterial)\r\n\tADD_PARAMETER(m_N   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"N\");\r\n\tADD_PARAMETER(m_ksi, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\")->setUnits(UNIT_PRESSURE);\r\n    ADD_PARAMETER(m_mu , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\" )->setUnits(UNIT_PRESSURE);\r\n\tADD_PARAMETER(m_term, FE_RANGE_CLOSED(3,30), \"n_term\");\r\nEND_FECORE_CLASS();\r\n\r\n//-----------------------------------------------------------------------------\r\nFEFiberEntropyChain::FEFiberEntropyChain(FEModel* pfem) : FEFiberMaterial(pfem)\r\n{\r\n\tm_N = 2;\r\n\tm_mu = 0;\r\n\tm_term = 30;\r\n\r\n\tm_epsf = 1.0;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\nbool FEFiberEntropyChain::Validate()\r\n{\r\n    return FEFiberMaterial::Validate();\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\nmat3ds FEFiberEntropyChain::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\r\n{\r\n\tdouble ksi = m_ksi(mp);\r\n\tdouble mu = m_mu(mp);\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n    \t\r\n\t// deformation gradient\r\n\tmat3d &F = pt.m_F;\r\n\tdouble J = pt.m_J;\r\n\t\r\n\t// loop over all integration points\r\n\tmat3ds C = pt.RightCauchyGreen();\r\n\tmat3ds s;\r\n\t\r\n\tconst double f_a[] = { 3.0, 9.0 / 5.0, 297.0 / 175.0, 1539.0 / 875.0, 126117.0 / 67373.0, 43733439.0 / 21896875.0, \\\r\n\t\t231321177.0 / 109484375.0, 20495009043.0 / 9306171875.0, 1073585186448381.0 / 476522530859375.0, 4387445039583.0 / 1944989921875.0, \\\r\n\t\t1000263375846831627.0 / 453346207767578125.0, 280865021365240713.0 / 133337119931640625.0, 148014872740758343473.0 / 75350125192138671875.0, 137372931237386537808993.0 / 76480377070020751953125.0, \\\r\n\t\t41722474198742657618857192737.0 / 25674386102028896409912109375.0, 12348948373636682700768301125723.0 / 8344175483159391333221435546875.0, 5001286000741585238340074032091091.0 / 3590449627018291035442047119140625.0, \\\r\n\t\t185364329915163811141785118512534489.0 / 132846636199676768311355743408203125.0, 6292216384025878939310787532157558451.0 / 4160197291516193533960877227783203125.0, 299869254759556271677902570230858640837.0 / 170568088952163934892395966339111328125.0, \\\r\n\t\t316689568216860631885141475537178451746044283.0 / 148810301691076651811854156590061187744140625.0, 670194310437429598283653289122392937145371137.0 / 258140319260030926612400067554187774658203125.0, 19697015384373759058671314622426656486031717197919.0 / 6332687088594936951180328352888571262359619140625.0, \\\r\n\t\t178793788985653424246012689916144867915861856840849.0 / 49756827124674504616416865629838774204254150390625.0, 323844166067349737493036492206152479344269351967043143667.0 / 82039106319990447886052744467343800746748447418212890625.0, 200808116689754604893460969866238617668631975356485302537199.0 / 49409916306357883385918130190559334540655314922332763671875.0, \\\r\n\t\t27506481209689719715275759452624078040221544551995885750037973.0 / 7164437864421893090958128877631103508395020663738250732421875.0, 16356939619211770477227805130221533318985185730316048281126247721.0 / 5122573073061653560035062147506239008502439774572849273681640625.0, 30976199222209837888906735596203249520053107250807132769871859115868101.0 / 14801698741072505317694781203956860833766632412399612367153167724609375.0, \\\r\n\t\t519588001407316958447129785511020819131555326399179970047767492196701159.0 / 902903623205422824379381653441368510859764577156376354396343231201171875.0 };\r\n\r\n\t// fiber direction in global coordinate system\r\n\t//vec3d n0 = GetFiberVector(mp);\r\n\t\r\n\t// Calculate In = n0*C*n0\r\n\tdouble In = n0*(C*n0);\r\n\t\r\n\t// only take fibers in tension into consideration\r\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\r\n\r\n\tif ((In - 1.0) > eps)\r\n\t{\r\n\t\t// define the distribution\r\n\t\tdouble R, Ra = 0.0;\r\n\t\t// get the global spatial fiber direction in current configuration\r\n\t\tvec3d nt = F*n0;\r\n\t\t\r\n\t\t// calculate the outer product of nt\r\n\t\tmat3ds N = dyad(nt);\r\n\t\t\r\n\t\t// calculate strain energy derivative\r\n\t\t//double alpha = sqrt(In) / sqrt(m_N);\r\n\t\tdouble alpha = sqrt(In) / sqrt(m_N);\r\n\t\tdouble alpha0 = 1 / sqrt(m_N);\r\n\t\tdouble alphaI = 1 / sqrt(In) / (2.0 * sqrt(m_N));\r\n\r\n\t\t///////////////////////////////////////////////////////////////////////////////////////\r\n\t\tdouble beta = 0, beta0 = 0, alpha2 = alpha * alpha, alpha02 = alpha0 * alpha0; \r\n\t\tint num_fa = sizeof(f_a) / sizeof(f_a[0]);\r\n\t\tif (m_term > num_fa) {\r\n\t\t\tm_term = num_fa;\r\n\t\t}\r\n\t\tbeta = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2;\r\n\t\tbeta0 = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha02;\r\n\t\tfor (int i = 2; i < m_term; i++) {\r\n\t\t\tbeta = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta;\r\n\t\t\tbeta0 = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha02 * beta0;\r\n\t\t\t//cout << num_fa - i << endl;\r\n\t\t};\r\n\t\tbeta = f_a[0] * alpha * beta;\r\n\t\tbeta0 = f_a[0] * alpha0 * beta0;\r\n\t\t////////////////////////////////////////////////////////////////////////////////////////////\r\n\t\t\r\n\t\tdouble Wl = ksi * m_N * alphaI * beta - ksi*sqrt(m_N) / 2 * beta0;\r\n\t\t\t\t\r\n\t\t// calculate the fiber stress\r\n\t\ts = N*(2.0*Wl / J);\r\n\r\n\t\t//cout << n0.x << \" \" << n0.y << \" \" << n0.z << endl;\r\n\r\n\t\t// add the contribution from shear\r\n\t\tif (mu != 0.0)\r\n\t\t{\r\n\t\t\tmat3ds BmI = pt.LeftCauchyGreen() - mat3dd(1);\r\n\t\t\ts += (N*BmI).sym()*(mu / J);\r\n\t\t}\r\n\r\n\t}\r\n\telse\r\n\t{\r\n\t\ts.zero();\r\n\t}\r\n\t\r\n\treturn s;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\ntens4ds FEFiberEntropyChain::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\r\n{\r\n\tdouble ksi = m_ksi(mp);\r\n\tdouble mu = m_mu(mp);\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\t\r\n\t// deformation gradient\r\n\tmat3d &F = pt.m_F;\r\n\tdouble J = pt.m_J;\r\n\t\r\n\tmat3ds C = pt.RightCauchyGreen();\r\n\ttens4ds c;\r\n\t\r\n\t// fiber direction in global coordinate system\r\n\t//vec3d n0 = GetFiberVector(mp);\r\n\t\r\n\t// Calculate In = n0*C*n0\r\n\tdouble In = n0*(C*n0);\r\n\r\n\t// only take fibers in tension into consideration\r\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\r\n\r\n\tif ((In - 1.0) > eps)\r\n\t{\r\n\t\t// get the global spatial fiber direction in current configuration\r\n\t\tvec3d nt = F*n0;\r\n\t\t\r\n\t\t// calculate the outer product of nt\r\n\t\tmat3ds N = dyad(nt);\r\n\t\ttens4ds NxN = dyad1s(N);\r\n\t\t\r\n\t\t// calculate strain energy 2nd derivative\r\n\t\tconst double f_a[] = { 3.0, 9.0 / 5.0, 297.0 / 175.0, 1539.0 / 875.0, 126117.0 / 67373.0, 43733439.0 / 21896875.0, \\\r\n\t\t\t231321177.0 / 109484375.0, 20495009043.0 / 9306171875.0, 1073585186448381.0 / 476522530859375.0, 4387445039583.0 / 1944989921875.0, \\\r\n\t\t\t1000263375846831627.0 / 453346207767578125.0, 280865021365240713.0 / 133337119931640625.0, 148014872740758343473.0 / 75350125192138671875.0, 137372931237386537808993.0 / 76480377070020751953125.0, \\\r\n\t\t\t41722474198742657618857192737.0 / 25674386102028896409912109375.0, 12348948373636682700768301125723.0 / 8344175483159391333221435546875.0, 5001286000741585238340074032091091.0 / 3590449627018291035442047119140625.0, \\\r\n\t\t\t185364329915163811141785118512534489.0 / 132846636199676768311355743408203125.0, 6292216384025878939310787532157558451.0 / 4160197291516193533960877227783203125.0, 299869254759556271677902570230858640837.0 / 170568088952163934892395966339111328125.0, \\\r\n\t\t\t316689568216860631885141475537178451746044283.0 / 148810301691076651811854156590061187744140625.0, 670194310437429598283653289122392937145371137.0 / 258140319260030926612400067554187774658203125.0, 19697015384373759058671314622426656486031717197919.0 / 6332687088594936951180328352888571262359619140625.0, \\\r\n\t\t\t178793788985653424246012689916144867915861856840849.0 / 49756827124674504616416865629838774204254150390625.0, 323844166067349737493036492206152479344269351967043143667.0 / 82039106319990447886052744467343800746748447418212890625.0, 200808116689754604893460969866238617668631975356485302537199.0 / 49409916306357883385918130190559334540655314922332763671875.0, \\\r\n\t\t\t27506481209689719715275759452624078040221544551995885750037973.0 / 7164437864421893090958128877631103508395020663738250732421875.0, 16356939619211770477227805130221533318985185730316048281126247721.0 / 5122573073061653560035062147506239008502439774572849273681640625.0, 30976199222209837888906735596203249520053107250807132769871859115868101.0 / 14801698741072505317694781203956860833766632412399612367153167724609375.0, \\\r\n\t\t\t519588001407316958447129785511020819131555326399179970047767492196701159.0 / 902903623205422824379381653441368510859764577156376354396343231201171875.0 };\r\n\t\tdouble alpha = sqrt(In) / sqrt(m_N);\r\n\t\tdouble alpha0 = 1 / sqrt(m_N);\r\n\t\tdouble alphaI = 1.0 / sqrt(In) / (2.0 * sqrt(m_N)); \r\n\t\tdouble alphaII = -pow(In, (-3.0 / 2.0)) / (4.0 * sqrt(m_N));\r\n\r\n\t\t/////////////////////////////////////////////////////////////////////////////////\r\n\t\tdouble beta = 0, beta0 = 0, beta_alpha = 0, alpha2 = alpha * alpha, alpha02 = alpha0 * alpha0;\r\n\t\tint num_fa = sizeof(f_a) / sizeof(f_a[0]);\r\n\t\tif (m_term > num_fa) {\r\n\t\t\tm_term = num_fa;\r\n\t\t}\r\n\t\tbeta = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2;\r\n\t\tbeta0 = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha02;\r\n\t\tbeta_alpha = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2 * (2 * m_term - 1) / (2 * m_term - 3);\r\n\t\tfor (int i = 2; i < m_term; i++) {\r\n\t\t\tbeta = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta;\r\n\t\t\tbeta0 = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha02 * beta0;\r\n\t\t\tbeta_alpha = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta_alpha * (2 * m_term - 2 * i + 1) / (2 * m_term - 2 * i - 1);\r\n\t\t};\r\n\t\tbeta = f_a[0] * alpha * beta;\r\n\t\tbeta0 = f_a[0] * alpha0 * beta0;\r\n\t\tbeta_alpha = f_a[0] * beta_alpha;\r\n\t\t//////////////////////////////////////////////////////////////////////////////////////\r\n\r\n\r\n\t\tdouble Wll = ksi * m_N * (beta_alpha*alphaI*alphaI + beta*alphaII);;\r\n\t\t\r\n\t\t// calculate the fiber tangent\r\n\t\tc = NxN*(4.0*Wll / J);\r\n        \r\n        // add the contribution from shear\r\n\t\tif (mu != 0.0)\r\n\t\t{\r\n\t\t\tmat3ds B = pt.LeftCauchyGreen();\r\n\t\t\tc += dyad4s(N,B)*(mu/J);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tc.zero();\r\n\t}\r\n\t\r\n\treturn c;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\n//! Strain energy density\r\ndouble FEFiberEntropyChain::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\r\n{\r\n\tdouble ksi = m_ksi(mp);\r\n\tdouble mu = m_mu(mp);\r\n    double sed = 0.0;\r\n\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\t\r\n\t// loop over all integration points\r\n\tmat3ds C = pt.RightCauchyGreen();\r\n    mat3ds C2 = C.sqr();\r\n\t\r\n\t// fiber direction in global coordinate system\r\n\t//vec3d n0 = GetFiberVector(mp);\r\n\t\r\n\t// Calculate In = n0*C*n0\r\n\tdouble In = n0*(C*n0);\r\n\r\n\t// only take fibers in tension into consideration\r\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\r\n\tif ((In - 1.0) > eps)\r\n\t{\r\n\t\t// calculate strain energy density\r\n\t\tdouble alpha = sqrt(In) / sqrt(m_N);\r\n\t\tdouble beta = alpha * (3.0 - alpha * alpha) / (1.0 - alpha * alpha) - 0.5 * pow(alpha, (10.0 / 3.0)) + 3.0 * pow(alpha, 5.0)*(alpha - 0.76)*(alpha - 1.0);\r\n\t\tdouble alpha0 = 1 / sqrt(m_N);\r\n\t\tdouble beta0 = alpha0 * (3.0 - alpha0 * alpha0) / (1.0 - alpha0 * alpha0) - 0.5 * pow(alpha0, (10.0 / 3.0)) + 3.0 * pow(alpha0, 5.0)*(alpha0 - 0.76)*(alpha0 - 1.0);\r\n\t\tdouble alpha00 = ksi* sqrt(m_N) / 2 * beta0 + ksi*m_N*log(beta0 / sinh(beta0));\r\n\t\tsed = ksi*m_N * (alpha*beta + log(beta / (sinh(beta)))) - ksi*sqrt(m_N)/2 * beta0 * In - alpha00;\r\n\t\t\r\n\t\t// add the contribution from shear\r\n\t\tsed += mu*(n0*(C2*n0)-2.0*(In-1.0)-1.0)/4.0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsed = 0; \r\n\t}\r\n\r\n    return sed;\r\n}\r\n//-----------------------------------------------------------------------------\r\n// FEElasticFiberEntropyChain\r\n//-----------------------------------------------------------------------------\r\n\r\n// define the material parameters\r\nBEGIN_FECORE_CLASS(FEElasticFiberEntropyChain, FEElasticFiberMaterial)\r\n    ADD_PARAMETER(m_fib.m_N   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"N\");\r\n    ADD_PARAMETER(m_fib.m_ksi, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\")->setUnits(UNIT_PRESSURE);\r\n    ADD_PARAMETER(m_fib.m_mu , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\" )->setUnits(UNIT_PRESSURE);\r\n    ADD_PARAMETER(m_fib.m_term, FE_RANGE_CLOSED(3, 30), \"n_term\");\r\nEND_FECORE_CLASS();\r\n\r\n"
  },
  {
    "path": "FEBioMech/FEFiberEntropyChain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Exponential-power law\n//! (Variation that includes a shear term)\nclass FEFiberEntropyChain : public FEFiberMaterial\n{\npublic:\n\tFEFiberEntropyChain(FEModel* pfem);\n\n\t//! Initialization\n\tbool Validate() override;\n\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tdouble          m_N;        // coefficient of micro-combination number\n\tFEParamDouble\tm_ksi;\t\t// measure of fiber modulus which equals to nkT\n\tint             m_term;     // how many Tayler approximation terms will be used\n\tFEParamDouble   m_mu;       // shear modulus\n\n\tdouble\tm_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEElasticFiberEntropyChain : public FEElasticFiberMaterial_T<FEFiberEntropyChain>\n{\npublic:\n    FEElasticFiberEntropyChain(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberEntropyChain>(fem) {}\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberEntropyChainUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\r\nlisted below.\r\n\r\nSee Copyright-FEBio.txt for details.\r\n\r\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in \r\nthe City of New York, and others.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.*/\r\n\r\n\r\n#include \"FECore/stdafx.h\"\r\n#include \"FEFiberEntropyChainUC.h\"\r\n#include <iostream>\r\n#include \"triangle_sphere.h\"\r\n#include <limits>\r\n#include <FECore/log.h>\r\n\r\n//-----------------------------------------------------------------------------\r\n// FEFiberEntropyChainUC\r\n//-----------------------------------------------------------------------------\r\n\r\n// define the material parameters\r\nBEGIN_FECORE_CLASS(FEFiberEntropyChainUC, FEFiberMaterialUncoupled)\r\n\tADD_PARAMETER(m_N, FE_RANGE_GREATER_OR_EQUAL(0.0), \"N\");\r\n\tADD_PARAMETER(mm_ksi, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\");\r\n\tADD_PARAMETER(m_term, FE_RANGE_CLOSED(3, 30), \"n_term\");\r\nEND_FECORE_CLASS();\r\n\r\n//-----------------------------------------------------------------------------\r\nFEFiberEntropyChainUC::FEFiberEntropyChainUC(FEModel* pfem) : FEFiberMaterialUncoupled(pfem)\r\n{\r\n\tm_N = 2;\r\n\tm_term = 30;\r\n\tm_epsf = 1.0;\r\n}\r\n//-----------------------------------------------------------------------------\r\nmat3ds FEFiberEntropyChainUC::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\r\n{\r\n\tdouble m_ksi = mm_ksi(mp);\r\n\t//double m_mu = mm_mu(mp);\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n    \t\r\n\t// deformation gradient\r\n\tdouble J = pt.m_J;\r\n\tmat3d F = pt.m_F * pow(J, -1.0 / 3.0);\r\n\t\r\n\t// loop over all integration points\r\n\tmat3ds C = pt.DevRightCauchyGreen();\r\n\tmat3ds s;\r\n\t\r\n\tconst double f_a[] = { 3.0, 9.0 / 5.0, 297.0 / 175.0, 1539.0 / 875.0, 126117.0 / 67373.0, 43733439.0 / 21896875.0, \\\r\n\t\t231321177.0 / 109484375.0, 20495009043.0 / 9306171875.0, 1073585186448381.0 / 476522530859375.0, 4387445039583.0 / 1944989921875.0, \\\r\n\t\t1000263375846831627.0 / 453346207767578125.0, 280865021365240713.0 / 133337119931640625.0, 148014872740758343473.0 / 75350125192138671875.0, 137372931237386537808993.0 / 76480377070020751953125.0, \\\r\n\t\t41722474198742657618857192737.0 / 25674386102028896409912109375.0, 12348948373636682700768301125723.0 / 8344175483159391333221435546875.0, 5001286000741585238340074032091091.0 / 3590449627018291035442047119140625.0, \\\r\n\t\t185364329915163811141785118512534489.0 / 132846636199676768311355743408203125.0, 6292216384025878939310787532157558451.0 / 4160197291516193533960877227783203125.0, 299869254759556271677902570230858640837.0 / 170568088952163934892395966339111328125.0, \\\r\n\t\t316689568216860631885141475537178451746044283.0 / 148810301691076651811854156590061187744140625.0, 670194310437429598283653289122392937145371137.0 / 258140319260030926612400067554187774658203125.0, 19697015384373759058671314622426656486031717197919.0 / 6332687088594936951180328352888571262359619140625.0, \\\r\n\t\t178793788985653424246012689916144867915861856840849.0 / 49756827124674504616416865629838774204254150390625.0, 323844166067349737493036492206152479344269351967043143667.0 / 82039106319990447886052744467343800746748447418212890625.0, 200808116689754604893460969866238617668631975356485302537199.0 / 49409916306357883385918130190559334540655314922332763671875.0, \\\r\n\t\t27506481209689719715275759452624078040221544551995885750037973.0 / 7164437864421893090958128877631103508395020663738250732421875.0, 16356939619211770477227805130221533318985185730316048281126247721.0 / 5122573073061653560035062147506239008502439774572849273681640625.0, 30976199222209837888906735596203249520053107250807132769871859115868101.0 / 14801698741072505317694781203956860833766632412399612367153167724609375.0, \\\r\n\t\t519588001407316958447129785511020819131555326399179970047767492196701159.0 / 902903623205422824379381653441368510859764577156376354396343231201171875.0 };\r\n\r\n\t// fiber direction in global coordinate system\r\n\t//vec3d n0 = GetFiberVector(mp);\r\n\t\r\n\t// Calculate In = n0*C*n0\r\n\tdouble In = n0*(C*n0);\r\n\t\r\n\t// only take fibers in tension into consideration\r\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\r\n\r\n\tif ((In - 1.0) > eps)\r\n\t{\r\n\t\t// define the distribution\r\n\t\tdouble R, Ra = 0.0;\r\n\t\t// get the global spatial fiber direction in current configuration\r\n\t\tvec3d nt = F*n0;\r\n\t\t\r\n\t\t// calculate the outer product of nt\r\n\t\tmat3ds N = dyad(nt);\r\n\t\t\r\n\t\t// calculate strain energy derivative\r\n\t\tdouble alpha = sqrt(In) / sqrt(m_N);\r\n\t\tdouble alpha0 = 1 / sqrt(m_N);\r\n\t\tdouble alphaI = 1 / sqrt(In) / (2.0 * sqrt(m_N));\r\n\r\n\t\t///////////////////////////////////////////////////////////////////////////////////////\r\n\t\tdouble beta = 0, beta0 = 0, alpha2 = alpha * alpha, alpha02 = alpha0 * alpha0; \r\n\t\tint num_fa = sizeof(f_a) / sizeof(f_a[0]);\r\n\t\tif (m_term > num_fa) {\r\n\t\t\tm_term = num_fa;\r\n\t\t}\r\n\t\tbeta = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2;\r\n\t\tbeta0 = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha02;\r\n\t\tfor (int i = 2; i < m_term; i++) {\r\n\t\t\tbeta = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta;\r\n\t\t\tbeta0 = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha02 * beta0;\r\n\t\t\t//cout << num_fa - i << endl;\r\n\t\t};\r\n\t\tbeta = f_a[0] * alpha * beta;\r\n\t\tbeta0 = f_a[0] * alpha0 * beta0;\r\n\t\t////////////////////////////////////////////////////////////////////////////////////////////\r\n\t\t\r\n\t\tdouble Wl = m_ksi * m_N * alphaI * beta - m_ksi*sqrt(m_N) / 2 * beta0;\r\n\t\t\t\t\r\n\t\t// calculate the fiber stress\r\n\t\ts = N*(2.0*Wl / J);\r\n\t}\r\n\telse\r\n\t{\r\n\t\ts.zero();\r\n\t}\r\n\t\r\n\treturn s.dev();\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\ntens4ds FEFiberEntropyChainUC::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\r\n{\r\n\tdouble m_ksi = mm_ksi(mp);\r\n\t//double m_mu = mm_mu(mp);\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\t\r\n\t// deformation gradient\r\n\tdouble J = pt.m_J;\r\n\tmat3d F = pt.m_F * pow(J, -1.0 / 3.0);\r\n\r\n\t// loop over all integration points\r\n\tmat3ds C = pt.DevRightCauchyGreen();\r\n\tmat3ds s;\r\n\ttens4ds c;\r\n\t\r\n\t// fiber direction in global coordinate system\r\n\t//vec3d n0 = GetFiberVector(mp);\r\n\t\r\n\t// Calculate In = n0*C*n0\r\n\tdouble In = n0*(C*n0);\r\n\r\n\t// only take fibers in tension into consideration\r\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\r\n\r\n\tif ((In - 1.0) > eps)\r\n\t{\r\n\t\t// get the global spatial fiber direction in current configuration\r\n\t\tvec3d nt = F*n0;\r\n\t\t\r\n\t\t// calculate the outer product of nt\r\n\t\tmat3ds N = dyad(nt);\r\n\t\ttens4ds NxN = dyad1s(N);\r\n\t\t\r\n\t\t// calculate strain energy 2nd derivative\r\n\t\tconst double f_a[] = { 3.0, 9.0 / 5.0, 297.0 / 175.0, 1539.0 / 875.0, 126117.0 / 67373.0, 43733439.0 / 21896875.0, \\\r\n\t\t\t231321177.0 / 109484375.0, 20495009043.0 / 9306171875.0, 1073585186448381.0 / 476522530859375.0, 4387445039583.0 / 1944989921875.0, \\\r\n\t\t\t1000263375846831627.0 / 453346207767578125.0, 280865021365240713.0 / 133337119931640625.0, 148014872740758343473.0 / 75350125192138671875.0, 137372931237386537808993.0 / 76480377070020751953125.0, \\\r\n\t\t\t41722474198742657618857192737.0 / 25674386102028896409912109375.0, 12348948373636682700768301125723.0 / 8344175483159391333221435546875.0, 5001286000741585238340074032091091.0 / 3590449627018291035442047119140625.0, \\\r\n\t\t\t185364329915163811141785118512534489.0 / 132846636199676768311355743408203125.0, 6292216384025878939310787532157558451.0 / 4160197291516193533960877227783203125.0, 299869254759556271677902570230858640837.0 / 170568088952163934892395966339111328125.0, \\\r\n\t\t\t316689568216860631885141475537178451746044283.0 / 148810301691076651811854156590061187744140625.0, 670194310437429598283653289122392937145371137.0 / 258140319260030926612400067554187774658203125.0, 19697015384373759058671314622426656486031717197919.0 / 6332687088594936951180328352888571262359619140625.0, \\\r\n\t\t\t178793788985653424246012689916144867915861856840849.0 / 49756827124674504616416865629838774204254150390625.0, 323844166067349737493036492206152479344269351967043143667.0 / 82039106319990447886052744467343800746748447418212890625.0, 200808116689754604893460969866238617668631975356485302537199.0 / 49409916306357883385918130190559334540655314922332763671875.0, \\\r\n\t\t\t27506481209689719715275759452624078040221544551995885750037973.0 / 7164437864421893090958128877631103508395020663738250732421875.0, 16356939619211770477227805130221533318985185730316048281126247721.0 / 5122573073061653560035062147506239008502439774572849273681640625.0, 30976199222209837888906735596203249520053107250807132769871859115868101.0 / 14801698741072505317694781203956860833766632412399612367153167724609375.0, \\\r\n\t\t\t519588001407316958447129785511020819131555326399179970047767492196701159.0 / 902903623205422824379381653441368510859764577156376354396343231201171875.0 };\r\n\t\tdouble alpha = sqrt(In) / sqrt(m_N);\r\n\t\tdouble alpha0 = 1 / sqrt(m_N);\r\n\t\tdouble alphaI = 1.0 / sqrt(In) / (2.0 * sqrt(m_N)); \r\n\t\tdouble alphaII = -pow(In, (-3.0 / 2.0)) / (4.0 * sqrt(m_N));\r\n\r\n\t\t/////////////////////////////////////////////////////////////////////////////////\r\n\t\tdouble beta = 0, beta0 = 0, beta_alpha = 0, alpha2 = alpha * alpha, alpha02 = alpha0 * alpha0;\r\n\t\tint num_fa = sizeof(f_a) / sizeof(f_a[0]);\r\n\t\tif (m_term > num_fa) {\r\n\t\t\tm_term = num_fa;\r\n\t\t}\r\n\t\tbeta = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2;\r\n\t\tbeta0 = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha02;\r\n\t\tbeta_alpha = 1 + f_a[m_term - 1] / f_a[m_term - 2] * alpha2 * (2 * m_term - 1) / (2 * m_term - 3);\r\n\t\tfor (int i = 2; i < m_term; i++) {\r\n\t\t\tbeta = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta;\r\n\t\t\tbeta0 = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha02 * beta0;\r\n\t\t\tbeta_alpha = 1 + f_a[m_term - i] / f_a[m_term - i - 1] * alpha2 * beta_alpha * (2 * m_term - 2 * i + 1) / (2 * m_term - 2 * i - 1);\r\n\t\t};\r\n\t\tbeta = f_a[0] * alpha * beta;\r\n\t\tbeta0 = f_a[0] * alpha0 * beta0;\r\n\t\tbeta_alpha = f_a[0] * beta_alpha;\r\n\r\n\t\t//////////////////////////////////////////////////////////////////////////////////////\r\n\r\n\t\tdouble Wl = m_ksi * m_N * alphaI * beta - m_ksi * sqrt(m_N) / 2 * beta0;\r\n\r\n\t\tdouble Wll = m_ksi * m_N * (beta_alpha*alphaI*alphaI + beta*alphaII);\r\n\t\t\r\n\t\t// calculate the fiber stress\r\n\t\ts = N * (2.0 * Wl / J);\r\n\r\n\t\t// calculate the fiber tangent\r\n\t\tc = NxN*(4.0*Wll / J);\r\n\r\n\t\t// This is the final value of the elasticity tensor\r\n\t\tmat3dd I(1);\r\n\t\ttens4ds IxI = dyad1s(I);\r\n\t\ttens4ds I4 = dyad4s(I);\r\n\t\tc += ((I4 + IxI / 3.0) * s.tr() - dyad1s(I, s)) * (2. / 3.)\r\n\t\t\t- (ddots(IxI, c) - IxI * (c.tr() / 3.)) / 3.;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tc.zero();\r\n\t}\r\n\t\r\n\treturn c;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\n//! Strain energy density\r\ndouble FEFiberEntropyChainUC::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\r\n{\r\n\tdouble m_ksi = mm_ksi(mp);\r\n\t//double m_mu = mm_mu(mp);\r\n    double sed = 0.0;\r\n\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\t\r\n\t// loop over all integration points\r\n\tmat3ds C = pt.RightCauchyGreen();\r\n    mat3ds C2 = C.sqr();\r\n\t\r\n\t// fiber direction in global coordinate system\r\n\t//vec3d n0 = GetFiberVector(mp);\r\n\t\r\n\t// Calculate In = n0*C*n0\r\n\tdouble In = n0*(C*n0);\r\n\r\n\t// only take fibers in tension into consideration\r\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\r\n\tif ((In - 1.0) > eps)\r\n\t{\r\n\t// calculate strain energy density\r\n\tdouble alpha = sqrt(In) / sqrt(m_N);\r\n\tdouble beta = alpha * (3.0 - alpha * alpha) / (1.0 - alpha * alpha) - 0.5 * pow(alpha, (10.0 / 3.0)) + 3.0 * pow(alpha, 5.0)*(alpha - 0.76)*(alpha - 1.0);\r\n\tdouble alpha0 = 1 / sqrt(m_N);\r\n\tdouble beta0 = alpha0 * (3.0 - alpha0 * alpha0) / (1.0 - alpha0 * alpha0) - 0.5 * pow(alpha0, (10.0 / 3.0)) + 3.0 * pow(alpha0, 5.0)*(alpha0 - 0.76)*(alpha0 - 1.0);\r\n\tdouble alpha00 = m_ksi* sqrt(m_N) / 2 * beta0 + m_ksi*m_N*log(beta0 / sinh(beta0));\r\n\tsed = m_ksi*m_N * (alpha*beta + log(beta / (sinh(beta)))) - m_ksi*sqrt(m_N)/2 * beta0 * In - alpha00;\r\n\t\t\r\n    // add the contribution from shear\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsed = 0; \r\n\t}\r\n\r\n    return sed;\r\n}\r\n\r\n//-----------------------------------------------------------------------------\r\n// FEUncoupledFiberEntropyChainUC\r\n//-----------------------------------------------------------------------------\r\n\r\n// define the material parameters\r\nBEGIN_FECORE_CLASS(FEUncoupledFiberEntropyChainUC, FEElasticFiberMaterialUC)\r\n    ADD_PARAMETER(m_fib.m_N, FE_RANGE_GREATER_OR_EQUAL(0.0), \"N\");\r\n    ADD_PARAMETER(m_fib.mm_ksi, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\");\r\n    ADD_PARAMETER(m_fib.m_term, FE_RANGE_CLOSED(3, 30), \"n_term\");\r\nEND_FECORE_CLASS();\r\n\r\n"
  },
  {
    "path": "FEBioMech/FEFiberEntropyChainUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Exponential-power law\n//! (Variation that includes a shear term)\nclass FEFiberEntropyChainUC : public FEFiberMaterialUncoupled\n{\npublic:\n\tFEFiberEntropyChainUC(FEModel* pfem);\n\n\t/*//! Initialization\n\tbool Validate() override;\n\t*/\n\n\t//! Cauchy stress\n\tmat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tdouble          m_N;        // coefficient of micro-combination number\n\tFEParamDouble\tmm_ksi;\t\t// measure of fiber modulus which equals to nkT \n\tint             m_term;     //\n\n\tdouble\tm_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEUncoupledFiberEntropyChainUC : public FEElasticFiberMaterialUC_T<FEFiberEntropyChainUC>\n{\npublic:\n    FEUncoupledFiberEntropyChainUC(FEModel* fem) : FEElasticFiberMaterialUC_T<FEFiberEntropyChainUC>(fem) {}\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberExpLinear.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEFiberExpLinear.h\"\n#include <FECore/expint_Ei.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExpLinear, FEFiberMaterial)\nADD_PARAMETER(m_c3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c3\")->setUnits(UNIT_PRESSURE);\nADD_PARAMETER(m_c4, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c4\");\nADD_PARAMETER(m_c5, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c5\")->setUnits(UNIT_PRESSURE);\nADD_PARAMETER(m_lam1, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lambda\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEFiberExpLinear::FEFiberExpLinear(FEModel* pfem) : FEFiberMaterial(pfem)\n{\n\tm_c3 = 0;\n\tm_c4 = 0;\n\tm_c5 = 0;\n\tm_lam1 = 1.0;\n\tm_epsf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the fiber stress\nmat3ds FEFiberExpLinear::FiberStress(FEMaterialPoint& mp, const vec3d& a0)\n{\n\t// get the material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the spatial fiber vector and stretch\n\tvec3d a = pt.m_F * a0;\n\tdouble l = a.unit();\n\n\t// fiber stress\n\tmat3ds s; s.zero();\n\n\tif (l >= 1.0)\n\t{\n\t\t// other stuff we need\n\t\tmat3ds A = dyad(a);\n\t\tdouble J = pt.m_J;\n\n\t\tdouble c3 = m_c3(mp);\n\t\tdouble c4 = m_c4(mp);\n\t\tdouble c5 = m_c5(mp);\n\t\tdouble lam1 = m_lam1(mp);\n\n\t\t// if c3==0 we enforce continuity of the slope\n\t\tif (c3 == 0)\n\t\t{\n\t\t\tc3 = c5 / c4 * exp(-c4 * (lam1 - 1));\n\t\t}\n\n\t\t// calculate fiber stress\n\t\tdouble Wl = 0.0;\n\t\tif (l < lam1)\n\t\t{\n\t\t\tWl = c3 * (exp(c4 * (l - 1.0)) - 1.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// c6 is chosen so that continuity of the stress is always enforced\n\t\t\tdouble c6 = c3 * (exp(c4 * (lam1 - 1.0)) - 1.0) - c5 * lam1;\n\t\t\tWl = c5 * l + c6;\n\t\t}\n\t\ts += A * (Wl / J);\n\t}\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the fiber tangent\ntens4ds FEFiberExpLinear::FiberTangent(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tdouble eps = m_epsf * std::numeric_limits<double>::epsilon();\n\n\t// get material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F * a0;\n\tdouble l = a.unit();\n\n\t// fiber tangent\n\ttens4ds c(0.0);\n\n\tif (l >= 1.0 + eps)\n\t{\n\t\t// Invariants of B (= invariants of C)\n\t\tdouble J = pt.m_J;\n\t\tdouble I4 = l * l;\n\n\t\t// some useful tensors\n\t\tmat3dd I(1.0);\n\t\tmat3ds A = dyad(a);\n\t\ttens4ds AxA = dyad1s(A);\n\t\tdouble c3 = m_c3(mp);\n\t\tdouble c4 = m_c4(mp);\n\t\tdouble c5 = m_c5(mp);\n\t\tdouble lam1 = m_lam1(mp);\n\n\t\t// if c3==0 we enforce continuity of the slope\n\t\tif (c3 == 0) {\n\t\t\tc3 = c5 / c4 * exp(-c4 * (lam1 - 1));\n\t\t}\n\n\t\tdouble Fl = 0.0, Fll = 0.0;\n\t\tif (l < lam1)\n\t\t{\n\t\t\tFl = c3 * (exp(c4 * (l - 1.0)) - 1.0) / l;\n\t\t\tFll = -c3 * (exp(c4 * (l - 1.0)) - 1.0) / (l * l) + c3 * c4 * exp(c4 * (l - 1.0)) / l;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// c6 is chosen so that continuity of the stress is always enforced\n\t\t\tdouble c6 = c3 * (exp(c4 * (lam1 - 1.0)) - 1.0) - c5 * lam1;\n\t\t\tFl = c5 + c6 / l;\n\t\t\tFll = -c6 / (l * l);\n\t\t}\n\n\t\tdouble W44 = (Fll - Fl / l) / (4 * l * l);\n\n\t\tc += AxA * (4.0 * W44 * I4 * I4 / J);\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the fiber strain energy density\ndouble FEFiberExpLinear::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F * a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam = a.unit();\n\n\t// strain energy density\n\tdouble sed = 0.0;\n\tif (lam >= 1)\n\t{\n\t\tdouble c3 = m_c3(mp);\n\t\tdouble c4 = m_c4(mp);\n\t\tdouble c5 = m_c5(mp);\n\t\tdouble lam1 = m_lam1(mp);\n\n\t\t// if c3==0 we enforce continuity of the slope\n\t\tif (c3 == 0) c3 = c5 / c4 * exp(-c4 * (lam1 - 1));\n\n\t\tif (lam < lam1)\n\t\t{\n\t\t\tsed = c3 * exp(-c4) * (expint_Ei(c4 * lam) - expint_Ei(c4)) - c3 * log(lam);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// c6 is chosen so that continuity of the stress is always enforced\n\t\t\tdouble c6 = c3 * (exp(c4 * (lam1 - 1.0)) - 1.0) - c5 * lam1;\n\n\t\t\tsed = c5 * (lam - lam1) + c6 * log(lam / lam1)\n\t\t\t\t+ c3 * exp(-c4) * (expint_Ei(c4 * lam1) - expint_Ei(c4)) - c3 * log(lam1);\n\t\t}\n\t}\n\n\treturn sed;\n}\n\n\n//============================================================================================\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberExpLinear, FEElasticFiberMaterial)\nADD_PARAMETER(m_fib.m_c3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c3\")->setUnits(UNIT_PRESSURE);\nADD_PARAMETER(m_fib.m_c4, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c4\");\nADD_PARAMETER(m_fib.m_c5, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c5\")->setUnits(UNIT_PRESSURE);\nADD_PARAMETER(m_fib.m_lam1, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lambda\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberExpLinear.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! This class represents a fiber material with an exponential toe-region\n//! and a linear region.\nclass FEFiberExpLinear : public FEFiberMaterial\n{\npublic:\n\t//! constructor\n\tFEFiberExpLinear(FEModel* pfem);\n\t\n\t//! Calculate the fiber stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Calculate the fiber tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Calculate the fiber strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tFEParamDouble   m_c3;\t\t//!< Exponential stress coefficient\n    FEParamDouble   m_c4;       //!< fiber uncrimping coefficient\n    FEParamDouble   m_c5;       //!< modulus of straightened fibers\n    FEParamDouble   m_lam1;\t\t//!< fiber stretch for straightened fibers\n\tdouble\tm_epsf;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEElasticFiberExpLinear : public FEElasticFiberMaterial_T<FEFiberExpLinear>\n{\npublic:\n\tFEElasticFiberExpLinear(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberExpLinear>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberExpPow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberExpPow.h\"\n#include <limits>\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExpPow, FEFiberMaterial)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"  )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_lam0 , FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEFiberExpPow\n//-----------------------------------------------------------------------------\n\nFEFiberExpPow::FEFiberExpPow(FEModel* pfem) : FEFiberMaterial(pfem)\n{ \n\tm_alpha = 0;\n\tm_beta = 2;\n\tm_ksi = 0;\n\tm_mu = 0;\n    m_lam0 = 1;\n\n\tm_epsf = 1.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberExpPow::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds s;\n\t\n\t// Calculate In - I0 = n0*C*n0 - I0\n    double lam0 = m_lam0(mp);\n\tdouble In_I0 = n0*(C*n0) - lam0*lam0;\n\n\tdouble ksi = m_ksi(mp);\n\tdouble mu = m_mu(mp);\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n\t\n\t// only take fibers in tension into consideration\n\tconst double eps = m_epsf* std::numeric_limits<double>::epsilon();\n\tif (In_I0 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\t\n\t\t// calculate strain energy derivative\n\t\tdouble Wl = ksi*pow(In_I0, beta-1.0)*exp(alpha*pow(In_I0, beta));\n\t\t\n\t\t// calculate the fiber stress\n\t\ts = N*(2.0*Wl/J);\n\n\t\t// add the contribution from shear\n\t\tif (mu != 0.0)\n\t\t{\n\t\t\tmat3ds BmI = pt.LeftCauchyGreen() - mat3dd(1);\n\t\t\ts += (N*BmI).sym()*(m_mu(mp) / J);\n\t\t}\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberExpPow::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\ttens4ds c;\n\t\n    // Calculate In - I0 = n0*C*n0 - I0\n    double lam0 = m_lam0(mp);\n    double In_I0 = n0*(C*n0) - lam0*lam0;\n\n    double ksi = m_ksi(mp);\n    double mu = m_mu(mp);\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = m_epsf*std::numeric_limits<double>::epsilon();\n\tif (In_I0 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n\t\t\n\t\t// calculate strain energy 2nd derivative\n\t\tdouble tmp = alpha*pow(In_I0, beta);\n\t\tdouble Wll = ksi*pow(In_I0, beta-2.0)*((tmp+1)*beta-1.0)*exp(tmp);\n\t\t\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(4.0*Wll/J);\n\n\t\t// add the contribution from shear\n\t\tif (mu != 0.0)\n\t\t{\n\t\t\tmat3ds B = pt.LeftCauchyGreen();\n\t\t\tc += dyad4s(N, B)*(mu / J);\n\t\t}\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberExpPow::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\t\n    // Calculate In - I0 = n0*C*n0 - I0\n    double In = n0*(C*n0);\n    double lam0 = m_lam0(mp);\n    double In_I0 = In - lam0*lam0;\n    \n    double ksi = m_ksi(mp);\n    double mu = m_mu(mp);\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = 0;\n\tif (In_I0 >= eps)\n\t{\n\t\t// calculate strain energy derivative\n        if (alpha > 0) {\n            sed = ksi/(alpha*beta)*(exp(alpha*pow(In_I0, beta))-1);\n        }\n        else\n            sed = ksi/beta*pow(In_I0, beta);\n\n\t\t// add the contribution from shear\n\t\tif (mu != 0.0)\n\t\t{\n\t\t\tmat3ds C2 = C.sqr();\n\t\t\tsed += mu * (n0*(C2*n0) - 2 * (In - 1) - 1) / 4.0;\n\t\t}\n\n\t}\n\n    return sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberExpPow, FEElasticFiberMaterial)\n\tADD_PARAMETER(m_fib.m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_fib.m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_fib.m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"  )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_fib.m_lam0 , FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam0\");\nEND_FECORE_CLASS();\n\n\n//-----------------------------------------------------------------------------\n// FEFiberExponentialPower\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExponentialPower, FEElasticFiberMaterial)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" );\n\tADD_PARAMETER(m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\"  )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\"   )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_lam0 , FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberExponentialPower::FEFiberExponentialPower(FEModel* pfem) : FEElasticFiberMaterial(pfem) \n{\n\tm_alpha = 0; \n\tm_beta = 2; \n\tm_ksi = 0; \n\tm_mu = 0;\n    m_lam0 = 1;\n\n\tm_epsf = 1.0;\t// set to 1 for compatibility with febio 2.10\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberExponentialPower::Validate()\n{\n\t// TODO: how validate model parameters?\n//\tif ((4 * m_ksi + 2 * m_mu) < 0) {\n//\t\tfeLogError(\"4*ksi+2*mu must be positive.\"); return false;\n//\t}\n    return FEElasticFiberMaterial::Validate();\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberExponentialPower::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    mat3ds C = pt.RightCauchyGreen();\n    mat3ds s;\n    \n    // Calculate In - I0 = n0*C*n0 - I0\n    double lam0 = m_lam0(mp);\n    double In_I0 = n0*(C*n0) - lam0*lam0;\n    \n    double ksi = m_ksi(mp);\n    double mu = m_mu(mp);\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n    \n    // only take fibers in tension into consideration\n    const double eps = m_epsf* std::numeric_limits<double>::epsilon();\n    if (In_I0 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate strain energy derivative\n        double Wl = ksi*pow(In_I0, beta-1.0)*exp(alpha*pow(In_I0, beta));\n        \n        // calculate the fiber stress\n        s = N*(2.0*Wl/J);\n        \n        // add the contribution from shear\n        if (mu != 0.0)\n        {\n            mat3ds BmI = pt.LeftCauchyGreen() - mat3dd(1);\n            s += (N*BmI).sym()*(m_mu(mp) / J);\n        }\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberExponentialPower::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    mat3ds C = pt.RightCauchyGreen();\n    tens4ds c;\n    \n    // Calculate In - I0 = n0*C*n0 - I0\n    double lam0 = m_lam0(mp);\n    double In_I0 = n0*(C*n0) - lam0*lam0;\n\n    double ksi = m_ksi(mp);\n    double mu = m_mu(mp);\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n    \n    // only take fibers in tension into consideration\n    const double eps = m_epsf*std::numeric_limits<double>::epsilon();\n    if (In_I0 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // calculate strain energy 2nd derivative\n        double tmp = alpha*pow(In_I0, beta);\n        double Wll = ksi*pow(In_I0, beta-2.0)*((tmp+1)*beta-1.0)*exp(tmp);\n        \n        // calculate the fiber tangent\n        c = NxN*(4.0*Wll/J);\n        \n        // add the contribution from shear\n        if (mu != 0.0)\n        {\n            mat3ds B = pt.LeftCauchyGreen();\n            c += dyad4s(N, B)*(mu / J);\n        }\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\ndouble FEFiberExponentialPower::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // loop over all integration points\n    mat3ds C = pt.RightCauchyGreen();\n    \n    // Calculate In - I0 = n0*C*n0 - I0\n    double In = n0*(C*n0);\n    double lam0 = m_lam0(mp);\n    double In_I0 = In - lam0*lam0;\n    \n    double ksi = m_ksi(mp);\n    double mu = m_mu(mp);\n    double alpha = m_alpha(mp);\n    double beta = m_beta(mp);\n    \n    // only take fibers in tension into consideration\n    const double eps = 0;\n    if (In_I0 >= eps)\n    {\n        // calculate strain energy derivative\n        if (alpha > 0) {\n            sed = ksi/(alpha*beta)*(exp(alpha*pow(In_I0, beta))-1);\n        }\n        else\n            sed = ksi/beta*pow(In_I0, beta);\n        \n        // add the contribution from shear\n        if (mu != 0.0)\n        {\n            mat3ds C2 = C.sqr();\n            sed += mu * (n0*(C2*n0) - 2 * (In - 1) - 1) / 4.0;\n        }\n        \n    }\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberExpPow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n\nclass FEElasticFiberExpPow;\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Exponential-power law\n\nclass FEFiberExpPow : public FEFiberMaterial\n{\npublic:\n\tFEFiberExpPow(FEModel* pfem);\n\t\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\t\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\t\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \nprotected:\n\tFEParamDouble       m_alpha;\t// coefficient of (In-I0) in exponential\n\tFEParamDouble       m_beta;\t\t// power of (In-I0) in exponential\n\tFEParamDouble\t\tm_ksi;\t\t// fiber modulus\n\tFEParamDouble\t\tm_mu;       // shear modulus\n    FEParamDouble       m_lam0;     // stretch threshold for tensile response\n\tdouble\tm_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\n\tfriend class FEElasticFiberExpPow;\n};\n\n//-----------------------------------------------------------------------------\nclass FEElasticFiberExpPow : public FEElasticFiberMaterial_T<FEFiberExpPow>\n{\npublic:\n\tFEElasticFiberExpPow(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberExpPow>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Exponential-power law\n//! (Variation that includes a shear term)\n//! TODO: I want to delete one of these two formulations.\nclass FEFiberExponentialPower : public FEElasticFiberMaterial\n{\npublic:\n\tFEFiberExponentialPower(FEModel* pfem);\n\n\t//! Initialization\n\tbool Validate() override;\n\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tFEParamDouble\tm_alpha;\t// coefficient of (In-I0) in exponential\n\tFEParamDouble\tm_beta;\t\t// power of (In-I0) in exponential\n\tFEParamDouble\tm_ksi;\t\t// measure of fiber modulus\n\tFEParamDouble   m_mu;       // shear modulus\n    FEParamDouble   m_lam0;     // stretch threshold for tensile response\n\n\tdouble\tm_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEFiberExpPowUncoupled.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberExpPowUncoupled.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExpPowUC, FEFiberMaterialUncoupled)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEFiberExpPowUC\n//-----------------------------------------------------------------------------\n\nFEFiberExpPowUC::FEFiberExpPowUC(FEModel* pfem) : FEFiberMaterialUncoupled(pfem)\n{ \n\tm_ksi = 0.0;\n\tm_alpha = 0.0;\n\tm_beta = 2.0;\n    m_mu = 0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberExpPowUC::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\tmat3d F = pt.m_F*pow(J,-1.0/3.0);\n\t\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds s;\n\t\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\tdouble ksi = m_ksi(mp);\n\t\n\t// only take fibers in tension into consideration\n\tif (In_1 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\t\n\t\t// calculate strain energy derivative\n\t\tdouble Wl = ksi*pow(In_1, m_beta-1.0)*exp(m_alpha*pow(In_1, m_beta));\n\t\t\n\t\t// calculate the fiber stress\n\t\ts = N*(2.0*Wl/J);\n\n        // add the contribution from shear\n        mat3ds BmI = pt.DevLeftCauchyGreen() - mat3dd(1);\n        s += (N*BmI).sym()*(m_mu / J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\t\n\treturn s.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberExpPowUC::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\tmat3d F = pt.m_F*pow(J,-1.0/3.0);\n\t\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds s;\n\ttens4ds c;\n\t\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\t\n\tdouble ksi = m_ksi(mp);\n\n\t// only take fibers in tension into consideration\n\tif (In_1 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n        mat3dd I(1);\n        tens4ds IxI = dyad1s(I);\n        tens4ds I4  = dyad4s(I);\n\n\t\t// calculate strain energy derivatives\n\t\tdouble tmp = m_alpha*pow(In_1, m_beta);\n\t\tdouble Wl = ksi*pow(In_1, m_beta-1.0)*exp(m_alpha*pow(In_1, m_beta));\n\t\tdouble Wll = ksi*pow(In_1, m_beta-2.0)*((tmp+1)*m_beta-1.0)*exp(tmp);\n\t\t\n\t\t// calculate the fiber stress\n\t\ts = N*(2.0*Wl/J);\n\n        // add the contribution from shear\n        mat3ds B = pt.DevLeftCauchyGreen();\n        mat3ds BmI = B - I;\n        s += (N*BmI).sym()*(m_mu / J);\n\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(4.0*Wll/J);\n\n        // add the contribution from shear\n        c += dyad4s(N, B)*(m_mu / J);\n\n\t\t// This is the final value of the elasticity tensor\n\t\tc += ((I4+IxI/3.0)*s.tr() - dyad1s(I,s))*(2./3.)\n\t\t- (ddots(IxI, c)-IxI*(c.tr()/3.))/3.;\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberExpPowUC::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.DevRightCauchyGreen();\n    mat3ds C2 = C.sqr();\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\t\n\tdouble ksi = m_ksi(mp);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = 0;\n\tif (In_1 >= eps)\n\t{\n\t\t// calculate strain energy derivative\n        if (m_alpha > 0) {\n            sed = ksi/(m_alpha*m_beta)*(exp(m_alpha*pow(In_1, m_beta))-1);\n        }\n        else\n            sed = ksi/m_beta*pow(In_1, m_beta);\n\n        // add the contribution from shear\n        sed += m_mu*(n0*(C2*n0) - 2 * In_1 - 1) / 4.0;\n\t}\n    \n    return sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledFiberExpPow, FEElasticFiberMaterialUC)\n\tADD_PARAMETER(m_fib.m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_fib.m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_fib.m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_fib.m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberExpPowUncoupled.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberMaterial.h\"\n\nclass FEUncoupledFiberExpPow;\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Exponential-power law\n\nclass FEFiberExpPowUC : public FEFiberMaterialUncoupled\n{\npublic:\n\tFEFiberExpPowUC(FEModel* pfem);\n\t\n\t//! Cauchy stress\n\tvirtual mat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\t\n\t// Spatial tangent\n\tvirtual tens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\t\n\t//! Strain energy density\n\tvirtual double DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \nprotected:\n\tdouble\t\t\tm_alpha;\t// coefficient of (In-1) in exponential\n\tdouble\t\t\tm_beta;\t\t// power of (In-1) in exponential\n\tFEParamDouble\tm_ksi;\t\t// fiber modulus\n    double          m_mu;       // shear modulus\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\n\tfriend class FEUncoupledFiberExpPow;\n};\n\nclass FEUncoupledFiberExpPow : public FEElasticFiberMaterialUC_T<FEFiberExpPowUC>\n{\npublic:\n\tFEUncoupledFiberExpPow(FEModel* fem) : FEElasticFiberMaterialUC_T<FEFiberExpPowUC>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberExponentialPowerUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberExponentialPowerUC.h\"\n#include <limits>\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExponentialPowerUC, FEElasticFiberMaterialUC)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi, \"ksi\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberExponentialPowerUC::FEFiberExponentialPowerUC(FEModel* pfem) : FEElasticFiberMaterialUC(pfem)\n{\n\tm_alpha = 0;\n\tm_beta = 2;\n\tm_ksi = 0;\n\tm_mu = 0;\n    m_epsf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberExponentialPowerUC::Validate()\n{\n\tif ((4 * m_ksi + 2 * m_mu) < 0) { feLogError(\"4*ksi+2*mu must be positive.\"); return false;\t}\n\treturn FEElasticFiberMaterialUC::Validate();\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberExponentialPowerUC::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = pt.m_J;\n\t// distortional part of deformation gradient\n\tmat3d F = pt.m_F*pow(J, -1.0 / 3.0);\n\n\t// loop over all integration points\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds s;\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\t// only take fibers in tension into consideration\n    const double eps = m_epsf* std::numeric_limits<double>::epsilon();\n\tif (In_1 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\n\t\t// calculate strain energy derivative\n\t\tdouble Wl = m_ksi*pow(In_1, m_beta - 1.0)*exp(m_alpha*pow(In_1, m_beta));\n\n\t\t// calculate the fiber stress\n\t\ts = N*(2.0*Wl / J);\n\n\t\t// add the contribution from shear\n\t\tmat3ds BmI = pt.DevLeftCauchyGreen() - mat3dd(1);\n\t\ts += (N*BmI).sym()*(m_mu / J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\n\treturn s.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberExponentialPowerUC::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = pt.m_J;\n\t// distortional part of deformation gradient\n\tmat3d F = pt.m_F*pow(J, -1.0 / 3.0);\n\n    const double eps = m_epsf*std::numeric_limits<double>::epsilon();\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds s;\n\ttens4ds c;\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\t// only take fibers in tension into consideration\n\tif (In_1 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n        mat3dd I(1);\n        tens4ds IxI = dyad1s(I);\n        tens4ds I4 = dyad4s(I);\n\n\t\t// calculate strain energy derivative\n\t\tdouble Wl = m_ksi*pow(In_1, m_beta - 1.0)*exp(m_alpha*pow(In_1, m_beta));\n\n\t\t// calculate the fiber stress\n\t\ts = N*(2.0*Wl / J);\n\n        // add the contribution from shear\n        mat3ds B = pt.DevLeftCauchyGreen();\n        mat3ds BmI = B - I;\n        s += (N*BmI).sym()*(m_mu / J);\n\n\t\t// calculate strain energy 2nd derivative\n\t\tdouble tmp = m_alpha*pow(In_1, m_beta);\n\t\tdouble Wll = m_ksi*pow(In_1, m_beta - 2.0)*((tmp + 1)*m_beta - 1.0)*exp(tmp);\n\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(4.0*Wll / J);\n\n\t\t// add the contribution from shear\n\t\tc += dyad4s(N, B)*(m_mu / J);\n\n        // This is the final value of the elasticity tensor\n        c += ((I4 + IxI / 3.0)*s.tr() - dyad1s(I, s))*(2. / 3.) - (ddots(IxI, c) - IxI*(c.tr() / 3.)) / 3.;\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\ndouble FEFiberExponentialPowerUC::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// loop over all integration points\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\t// only take fibers in tension into consideration\n    const double eps = m_epsf*std::numeric_limits<double>::epsilon();\n\tdouble sed = 0.0;\n\tif (In_1 >= eps)\n\t{\n\t\t// calculate strain energy density\n\t\tif (m_alpha > 0)\n\t\t\tsed = m_ksi / (m_alpha*m_beta)*(exp(m_alpha*pow(In_1, m_beta)) - 1);\n\t\telse\n\t\t\tsed = m_ksi / m_beta*pow(In_1, m_beta);\n\n\t\t// add the contribution from shear\n\t\tsed += m_mu*(n0*(C2*n0) - 2 * In_1 - 1) / 4.0;\n\t}\n\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberExponentialPowerUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n\n//-----------------------------------------------------------------------------\n//! Exponential-power law\n\nclass FEFiberExponentialPowerUC : public FEElasticFiberMaterialUC\n{\npublic:\n\tFEFiberExponentialPowerUC(FEModel* pfem);\n\n\t//! Validation\n\tbool Validate() override;\n\n\t//! Cauchy stress\n\tmat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tdouble\tm_alpha;\t// coefficient of (In-1) in exponential\n\tdouble\tm_beta;\t\t// power of (In-1) in exponential\n\tdouble\tm_ksi;\t\t// fiber modulus\n\tdouble  m_mu;       // shear modulus\n\n\t\t\t\t\t\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationGauss.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberIntegrationGauss.h\"\n#include \"gauss.h\"\n#include <FECore/log.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\nclass FEFiberIntegrationGauss::Iterator : public FEFiberIntegrationSchemeIterator\n{\npublic:\n\tIterator(FEMaterialPoint* mp, FEFiberIntegrationGauss::GRULE& rule)\n\t{\n\t\tm_ncase = -1;\n\t\tm_nth = rule.m_nth;\n\t\tm_nph = rule.m_nph;\n\t\tm_gp = rule.m_gp;\n\t\tm_gw = rule.m_gw;\n\n\t\tconst double eps = 1e-9;\n\t\tif (mp)\n\t\t{\n\t\t\tFEElasticMaterialPoint& pt = *mp->ExtractData<FEElasticMaterialPoint>();\n\t\t\t// right Cauchy-Green tensor and its eigenvalues & eigenvectors\n\t\t\tmat3ds C = (pt.m_buncoupled ? pt.DevRightCauchyGreen() : pt.RightCauchyGreen());\n\t\t\tC.eigen(lC, vC);\n\n\t\t\t// check if there is no tension\n\t\t\tif ((lC[0] <= 1 + eps) && (lC[1] <= 1 + eps) && (lC[2] <= 1 + eps)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// bubble sort eigenvalues & eigenvectors from smallest to largest\n\t\t\tdouble ltmp;\n\t\t\tvec3d vtmp;\n\t\t\tbool swp = true;\n\t\t\twhile (swp) {\n\t\t\t\tswp = false;\n\t\t\t\tfor (int i = 1; i<3; ++i) {\n\t\t\t\t\tint j = i - 1;\n\t\t\t\t\tif (lC[i] < lC[j]) {\n\t\t\t\t\t\tltmp = lC[i]; vtmp = vC[i];\n\t\t\t\t\t\tlC[i] = lC[j]; vC[i] = vC[j];\n\t\t\t\t\t\tlC[j] = ltmp; vC[j] = vtmp;\n\t\t\t\t\t\tswp = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check the other case\n\t\t\tif      (lC[0]  > 1 + eps) m_ncase = 0;\n\t\t\telse if (lC[1] <= 1 + eps) m_ncase = 1;\n\t\t\telse m_ncase = 2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_ncase = 0;\n\t\t\tvC[0] = vec3d(1,0,0);\n\t\t\tvC[1] = vec3d(0,1,0);\n\t\t\tvC[2] = vec3d(0,0,1);\n\t\t}\n\n\t\t// check remaining stretch states\n\t\tdouble pi = 4 * atan(1.0);\n\t\tdth = 2 * pi / m_nth;\n\n\t\tvec3d n0e, n0a;\n\n\t\t// tension along all three eigenvectors (all directions)\n\t\tif (m_ncase == 0) \n\t\t{\n\t\t\tksia = 0;\n\t\t\tksib = 1;\n\t\t\tdksi = (ksib - ksia) / 2;\n\t\t\tsksi = (ksib + ksia) / 2;\n\t\t}\n\t\t// tension along one eigenvector and compression along other two\n\t\telse if (m_ncase == 1)\n\t\t{\n\t\t\t// nothing to do\n\t\t\t// ksia and ksib are update in FiberVector\n\t\t}\n\t\t// tension along two eigenvectors and compression along third\n\t\telse\n\t\t{\n\t\t\t// swap first and last eigenvalues/eigenvectors to maintain consistency in formulas\n\t\t\tdouble ltmp = lC[2]; vec3d vtmp = vC[2];\n\t\t\tlC[2] = lC[0]; vC[2] = vC[0];\n\t\t\tlC[0] = ltmp; vC[0] = vtmp;\n\t\t}\n\n\t\ti = 0; j = -1;\n\t\ti_old = -1;\n\t\tcth = 1.0; sth = 0.0;\n\t\tNext();\n\t}\n\n\tbool IsValid()\n\t{\n\t\treturn (m_ncase != -1);\n\t}\n\n\t// move to the next integration point\n\tbool Next()\n\t{\n\t\t// make sure the iterator is valid\n\t\tif (m_ncase == -1) return false;\n\n\t\t// update loop counters\n\t\tj++;\n\t\tif (j >= m_nph)\n\t\t{\n\t\t\tj = 0;\n\t\t\ti++;\n\t\t\tif (i >= m_nth)\n\t\t\t{\n\t\t\t\t// all done\n\t\t\t\tm_ncase = -1;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// update vector\n\t\tif (i_old != i)\n\t\t{\n\t\t\tdouble theta = i*dth;\n\t\t\tcth = cos(theta);\n\t\t\tsth = sin(theta);\n\n\t\t\tif (m_ncase == 1)\n\t\t\t{\n\t\t\t\tksia = sqrt(1 - lC[0] * SQR(cth) - lC[1] * SQR(sth)) / sqrt(lC[2] - lC[0] * SQR(cth) - lC[1] * SQR(sth));\n\t\t\t\tksib = 1;\n\t\t\t\tdksi = (ksib - ksia) / 2;\n\t\t\t\tsksi = (ksib + ksia) / 2;\n\t\t\t}\n\t\t\telse if (m_ncase == 2)\n\t\t\t{\n\t\t\t\tksia = 0;\n\t\t\t\tksib = sqrt(lC[0] * SQR(cth) + lC[1] * SQR(sth) - 1) / sqrt(lC[0] * SQR(cth) + lC[1] * SQR(sth) - lC[2]);\n\t\t\t\tdksi = (ksib - ksia) / 2;\n\t\t\t\tsksi = (ksib + ksia) / 2;\n\t\t\t}\n\t\t}\n\n\t\tdouble ksi = sksi + dksi*m_gp[j];\n\t\tdouble sph = sqrt(1.0 - ksi*ksi); // = sin(acos(ksi));\n\n\t\tm_fiber = vC[0] * (cth*sph) + vC[1] * (sth*sph) + vC[2] * ksi;\n\n\t\t// we multiply by two to add contribution from other half-sphere\n\t\tm_weight = (m_gw[j] * dth*dksi)*2.0;\n\n\t\ti_old = i;\n\n\t\treturn true;\n\t}\n\npublic:\n\tint m_ncase;\n\tdouble lC[3];\n\tvec3d vC[3];\n\tint\tm_nth, m_nph;\n\tconst double* m_gp;\n\tconst double* m_gw;\n\n\tint i, j, i_old;\n\tdouble ksia, ksib, dksi, sksi;\n\tdouble dth;\n\tdouble cth, sth;\n};\n\n//-----------------------------------------------------------------------------\n// FEFiberIntegrationGauss\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberIntegrationGauss, FEFiberIntegrationScheme)\n\tADD_PARAMETER(m_rule.m_nph, FE_RANGE_GREATER(0), \"nph\");\n\tADD_PARAMETER(m_rule.m_nth, FE_RANGE_GREATER(0), \"nth\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FEFiberIntegrationGauss::Serialize(DumpStream& ar)\n{\n\tFEFiberIntegrationScheme::Serialize(ar);\n\tif ((ar.IsSaving() == false) && (ar.IsShallow() == false))\n\t{\n\t\tInitRule();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberIntegrationGauss::InitRule()\n{\n\tswitch (m_rule.m_nph) {\n        case 1:\n\t\t\tm_rule.m_gp = gp1;\n\t\t\tm_rule.m_gw = gw1;\n            break;\n        case 2:\n\t\t\tm_rule.m_gp = gp2;\n\t\t\tm_rule.m_gw = gw2;\n            break;\n        case 3:\n\t\t\tm_rule.m_gp = gp3;\n\t\t\tm_rule.m_gw = gw3;\n            break;\n        case 4:\n\t\t\tm_rule.m_gp = gp4;\n\t\t\tm_rule.m_gw = gw4;\n            break;\n        case 5:\n\t\t\tm_rule.m_gp = gp5;\n\t\t\tm_rule.m_gw = gw5;\n            break;\n        case 6:\n\t\t\tm_rule.m_gp = gp6;\n\t\t\tm_rule.m_gw = gw6;\n            break;\n        case 7:\n\t\t\tm_rule.m_gp = gp7;\n\t\t\tm_rule.m_gw = gw7;\n            break;\n        case 8:\n\t\t\tm_rule.m_gp = gp8;\n\t\t\tm_rule.m_gw = gw8;\n            break;\n        case 9:\n\t\t\tm_rule.m_gp = gp9;\n\t\t\tm_rule.m_gw = gw9;\n            break;\n        case 10:\n\t\t\tm_rule.m_gp = gp10;\n\t\t\tm_rule.m_gw = gw10;\n            break;\n        default:\n            return false;\n            break;\n    }\n\n\treturn true;\n}\n\nFEFiberIntegrationGauss::FEFiberIntegrationGauss(FEModel* pfem) : FEFiberIntegrationScheme(pfem)\n{ \n\tm_rule.m_nph = 5; \n\tm_rule.m_nth = 2 * m_rule.m_nph;\n}\n\nFEFiberIntegrationGauss::~FEFiberIntegrationGauss()\n{\n}\n\nbool FEFiberIntegrationGauss::Init()\n{\n\tif (InitRule() == false) {\n\t\tfeLogError(\"nint must not exceed 10.\"); return false;\n\t}\n\n    // also initialize the parent class\n    return FEFiberIntegrationScheme::Init();\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationSchemeIterator* FEFiberIntegrationGauss::GetIterator(FEMaterialPoint* mp)\n{\n\treturn new Iterator(mp, m_rule);\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationGauss.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFiberIntegrationScheme.h\"\n\n//----------------------------------------------------------------------------------\n// Gauss integration scheme for continuous fiber distributions\n//\nclass FEFiberIntegrationGauss : public FEFiberIntegrationScheme\n{\n\tclass Iterator;\n\n\tstruct GRULE\n\t{\n\t\tint\tm_nph;\t// number of gauss integration points along phi\n\t\tint\tm_nth;  // number of trapezoidal integration points along theta\n\t\tconst double* m_gp;   // gauss points\n\t\tconst double* m_gw;   // gauss weights\n\t};\n\npublic:\n    FEFiberIntegrationGauss(FEModel* pfem);\n    ~FEFiberIntegrationGauss();\n\t\n\t//! Initialization\n\tbool Init() override;\n    \n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// get iterator\n\tvirtual FEFiberIntegrationSchemeIterator* GetIterator(FEMaterialPoint* mp) override;\n\n\t// get number of integration points\n\tint IntegrationPoints() const override { return -1; };\n\nprotected:\n\tbool InitRule();\n    \nprotected:\t// parameters\n\tGRULE\tm_rule;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationGaussKronrod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEFiberIntegrationGaussKronrod.h\"\n#include \"gausskronrod.h\"\n#include <FECore/log.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\nclass FEFiberIntegrationGaussKronrod::Iterator : public FEFiberIntegrationSchemeIterator\n{\npublic:\n\tIterator(FEMaterialPoint* mp, FEFiberIntegrationGaussKronrod::GKRULE& rule)\n\t{\n\t\tm_ncase = -1;\n\t\tm_nth = rule.m_nth;\n\t\tm_nph = rule.m_nph;\n\t\tm_gp = rule.m_gp;\n\t\tm_gw = rule.m_gw;\n\n\t\tconst double eps = std::numeric_limits<double>::epsilon();\n\t\tif (mp)\n\t\t{\n\t\t\tFEElasticMaterialPoint& pt = *mp->ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t// right Cauchy-Green tensor and its eigenvalues & eigenvectors\n\t\t\t// TODO: for uncoupled formulations we need to use the deviatoric strain-energy\n\t\t\tmat3ds C = (pt.m_buncoupled? pt.DevRightCauchyGreen() : pt.RightCauchyGreen());\n\t\t\tmat3ds E = (C - mat3dd(1)) * 0.5;\n\t\t\tE.eigen2(lE, vE);//lE[2]>lE[1]>lE[0]\n\n\t\t\t// check if there is no tension\n\t\t\tif (lE[2] <= 0) return;\n\n\t\t\t// check the other case\n\t\t\tif      (lE[0] >=   0) m_ncase = 0;\n\t\t\telse if (lE[1] <= eps) m_ncase = 1;\n\t\t\telse m_ncase = 2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_ncase = 0;\n\t\t\tvE[0] = vec3d(1,0,0);\n\t\t\tvE[1] = vec3d(0,1,0);\n\t\t\tvE[2] = vec3d(0,0,1);\n\t\t}\n\n\t\t// initialize theta increment\n\t\tdouble pi = 4 * atan(1.0);\n\t\tdth = 2 * pi / m_nth;\n\n\t\t// tension along all three eigenvectors (all directions)\n\t\tif (m_ncase == 0)\n\t\t{\n\t\t\tksia = 0;\n\t\t\tksib = 1;\n\t\t\tdksi = (ksib - ksia) / 2;\n\t\t\tsksi = (ksib + ksia) / 2;\n\t\t}\n\t\t// tension along one eigenvector and compression along other two// TC case\n\t\telse if (m_ncase == 1)\n\t\t{\n\t\t\t// nothing to do\n\t\t\t// ksia, ksib are calculated in FiberVector\n\t\t}\n\t\t// tension along two eigenvectors and compression along third\n\t\telse\n\t\t{\n\t\t\t// swap first and last eigenvalues/eigenvectors to maintain consistency in formulas\n\t\t\tdouble ltmp = lE[2]; vec3d vtmp = vE[2];\n\t\t\tlE[2] = lE[0]; vE[2] = vE[0];\n\t\t\tlE[0] = ltmp; vE[0] = vtmp;\n\t\t}\n\n\t\ti = 0; j = -1;\n\t\ti_old = -1;\n\t\tcth = 1.0; sth = 0.0;\n\t\tNext();\n\t}\n\n\tbool IsValid()\n\t{\n\t\treturn (m_ncase != -1);\n\t}\n\n\t// move to the next integration point\n\tbool Next()\n\t{\n\t\t// check if the iterator is valid\n\t\tif (m_ncase == -1) return false;\n\n\t\t// update loop counters\n\t\tj++;\n\t\tif (j>=m_nph)\n\t\t{\n\t\t\tj = 0;\n\t\t\ti++;\n\t\t\tif (i >= m_nth)\n\t\t\t{\n\t\t\t\t// all done\n\t\t\t\tm_ncase = -1;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// update vector\n\t\tif (i_old != i)\n\t\t{\n\t\t\tdouble theta = i*dth;\n\t\t\tcth = cos(theta);\n\t\t\tsth = sin(theta);\n\n\t\t\tif (m_ncase == 1)\n\t\t\t{\n\t\t\t\tdouble nu = -lE[0] * SQR(cth) - lE[1] * SQR(sth);\n\t\t\t\tif (nu<0) nu = 0;\n\t\t\t\tdouble de = lE[2] - lE[0] * SQR(cth) - lE[1] * SQR(sth);\n\t\t\t\tif (de <= 0) ksia = 1;\n\t\t\t\telse ksia = sqrt(nu) / sqrt(de);\n\t\t\t\tksib = 1;\n\t\t\t\tdksi = (ksib - ksia) / 2;\n\t\t\t\tsksi = (ksib + ksia) / 2;\n\t\t\t}\n\t\t\telse if (m_ncase == 2)\n\t\t\t{\n\t\t\t\tksia = 0;\n\t\t\t\tdouble nu = lE[0] * SQR(cth) + lE[1] * SQR(sth);\n\t\t\t\tif (nu<0) nu = 0;\n\t\t\t\tdouble de = lE[0] * SQR(cth) + lE[1] * SQR(sth) - lE[2];\n\t\t\t\tif (de <= 0) ksib = 0;\n\t\t\t\telse ksib = sqrt(nu) / sqrt(de);\n\t\t\t\tdksi = (ksib - ksia) / 2;\n\t\t\t\tsksi = (ksib + ksia) / 2;\n\t\t\t}\n\t\t}\n\n\t\tdouble ksi = sksi + dksi*m_gp[j];\n\t\tdouble sph = sqrt(1.0 - ksi*ksi); // = sin(acos(ksi));\n\t\tm_fiber = vE[0] * (cth*sph) + vE[1] * (sth*sph) + vE[2] * ksi;\n\n\t\t// we multiply by two to add contribution from other half-sphere\n\t\tm_weight = (m_gw[j] * dth*dksi)*2.0;\n\n\t\ti_old = i;\n\t\treturn true;\n\t}\n\npublic:\n\tint\tm_ncase;\n\tint\tm_nth, m_nph;\n\tdouble lE[3];\n\tvec3d vE[3];\n\n\tint i, j, i_old;\t// loop iterators\n\tdouble dth;\n\tdouble cth, sth;\n\tdouble ksia, ksib, dksi, sksi;\n\tconst double*\tm_gp;\n\tconst double*\tm_gw;\n};\n\n//-----------------------------------------------------------------------------\n// FEFiberIntegrationGaussKronrod\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberIntegrationGaussKronrod, FEFiberIntegrationScheme)\n\tADD_PARAMETER(m_rule.m_nph, \"nph\");\n\tADD_PARAMETER(m_rule.m_nth, \"nth\");\nEND_FECORE_CLASS();\n\nFEFiberIntegrationGaussKronrod::FEFiberIntegrationGaussKronrod(FEModel* pfem) : FEFiberIntegrationScheme(pfem) \n{ \n\tm_rule.m_nph = 7;\n\tm_rule.m_nth = 31;\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationGaussKronrod::~FEFiberIntegrationGaussKronrod()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberIntegrationGaussKronrod::Serialize(DumpStream& ar)\n{\n\tFEFiberIntegrationScheme::Serialize(ar);\n\tif ((ar.IsShallow() == false) && (ar.IsSaving() == false))\n\t{\n\t\tInitRule();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberIntegrationGaussKronrod::Init()\n{\n\tif (m_rule.m_nth < 1) {\n\t\tfeLogError(\"nth must be strictly greater than zero.\"); return false;\n\t}\n\n\t// initialize the rule\n\tif (InitRule() == false) {\n\t\tfeLogError(\"nph must be 7, 11, 15, 19, 23, or 27.\"); return false;\n\t}    \n    \n    // also initialize the parent class\n    return FEFiberIntegrationScheme::Init();\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberIntegrationGaussKronrod::InitRule()\n{\n\tswitch (m_rule.m_nph) {\n\tcase 7:\n\t\tm_rule.m_gp = gp7;\n\t\tm_rule.m_gw = gw7;\n\t\tbreak;\n\tcase 11:\n\t\tm_rule.m_gp = gp11;\n\t\tm_rule.m_gw = gw11;\n\t\tbreak;\n\tcase 15:\n\t\tm_rule.m_gp = gp15;\n\t\tm_rule.m_gw = gw15;\n\t\tbreak;\n\tcase 19:\n\t\tm_rule.m_gp = gp19;\n\t\tm_rule.m_gw = gw19;\n\t\tbreak;\n\tcase 23:\n\t\tm_rule.m_gp = gp23;\n\t\tm_rule.m_gw = gw23;\n\t\tbreak;\n\tcase 27:\n\t\tm_rule.m_gp = gp27;\n\t\tm_rule.m_gw = gw27;\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationSchemeIterator* FEFiberIntegrationGaussKronrod::GetIterator(FEMaterialPoint* mp)\n{\n\t// create a new iterator\n\treturn new Iterator(mp, m_rule);\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationGaussKronrod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFiberIntegrationScheme.h\"\n\n//----------------------------------------------------------------------------------\n// Gauss integration scheme for continuous fiber distributions\n//\nclass FEFiberIntegrationGaussKronrod : public FEFiberIntegrationScheme\n{\npublic:\n\tclass Iterator;\n\n\tstruct GKRULE\n\t{\n\t\tint\tm_nph; // number of gauss integration points along phi\n\t\tint\tm_nth; // number of trapezoidal integration points along theta\n\t\tconst double*\tm_gp; // gauss points\n\t\tconst double*\tm_gw; // gauss weights\n\t};\n\npublic:\n    FEFiberIntegrationGaussKronrod(FEModel* pfem);\n    ~FEFiberIntegrationGaussKronrod();\n\t\n\t//! Initialization\n\tbool Init() override;\n    \n\t// Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// get the iterator\n\tFEFiberIntegrationSchemeIterator* GetIterator(FEMaterialPoint* mp) override;\n\n\t// get number of integration points\n\tint IntegrationPoints() const override { return -1; };\n\nprotected:\n\tbool InitRule();\n    \nprotected: // parameters\n\tGKRULE\tm_rule;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationGeodesic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberIntegrationGeodesic.h\"\n#include <FECore/log.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\nclass FEFiberIntegrationGeodesic::Iterator : public FEFiberIntegrationSchemeIterator\n{\npublic:\n\tIterator(int nint, const double* cth, const double* cph, const double* sth, const double* sph, const double* wn)\n\t{\n\t\tm_nint = nint;\n\t\tm_cth = cth;\n\t\tm_cph = cph;\n\t\tm_sth = sth;\n\t\tm_sph = sph;\n\t\tm_wn = wn;\n\n\t\tn = -1;\n\t\tNext();\n\t}\n\n\tbool IsValid()\n\t{\n\t\treturn (n < m_nint);\n\t}\n\n\t// move to the next integration point\n\tbool Next()\n\t{\n\t\tn++;\n\t\tif (n < m_nint)\n\t\t{\n\t\t\tm_fiber.x = m_cth[n] * m_sph[n];\n\t\t\tm_fiber.y = m_sth[n] * m_sph[n];\n\t\t\tm_fiber.z = m_cph[n];\n\n\t\t\tm_weight = m_wn[n];\n\n\t\t\treturn true;\n\t\t}\n\t\telse return false;\n\t}\n\npublic:\n\tint\t\tn;\n\tint\t\tm_nint;\n\tconst double* m_cth;\n\tconst double* m_cph;\n\tconst double* m_sth;\n\tconst double* m_sph;\n\tconst double* m_wn;\n};\n\n//-----------------------------------------------------------------------------\n// FEFiberIntegrationGeodesic\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberIntegrationGeodesic, FEFiberIntegrationScheme)\n\tADD_PARAMETER(m_nres, \"resolution\")->setEnums(\"low\\0high\\0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FEFiberIntegrationGeodesic::Serialize(DumpStream& ar)\n{\n\tFEFiberIntegrationScheme::Serialize(ar);\n\tif (ar.IsSaving() == false)\n\t{\n\t\tInitIntegrationRule();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationGeodesic::FEFiberIntegrationGeodesic(FEModel* pfem) : FEFiberIntegrationScheme(pfem)\n{ \n\tm_nres = 0; \n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationGeodesic::~FEFiberIntegrationGeodesic()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberIntegrationGeodesic::Init()\n{\n\tif ((m_nres != 0) && (m_nres != 1)) {\n\t\tfeLogError(\"resolution must be 0 (low) or 1 (high).\"); return false;\n\t}\n    \n\t// initialize integration rule data\n\tInitIntegrationRule();\n\t\t\n    // also initialize the parent class\n    return FEFiberIntegrationScheme::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberIntegrationGeodesic::InitIntegrationRule()\n{\n\t// select the integration rule\n\tm_nint = (m_nres == 0? NSTL  : NSTH  );\n\tconst double* phi = (m_nres == 0? PHIL  : PHIH  );\n\tconst double* the = (m_nres == 0? THETAL: THETAH);\n\tconst double* w   = (m_nres == 0? AREAL : AREAH );\n\t\t\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tm_cth[n] = cos(the[n]);\n\t\tm_sth[n] = sin(the[n]);\n\t\tm_cph[n] = cos(phi[n]);\n\t\tm_sph[n] = sin(phi[n]);\n\t\tm_w[n] = w[n];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationSchemeIterator* FEFiberIntegrationGeodesic::GetIterator(FEMaterialPoint* mp)\n{\n\treturn new Iterator(m_nint, &m_cth[0], &m_cph[0], &m_sth[0], &m_sph[0], &m_w[0]);\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationGeodesic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFiberIntegrationScheme.h\"\n#include \"geodesic.h\"\n\n//----------------------------------------------------------------------------------\n// Geodesic dome integration scheme for continuous fiber distributions\n//\nclass FEFiberIntegrationGeodesic : public FEFiberIntegrationScheme\n{\n\tclass Iterator;\n\npublic:\n    FEFiberIntegrationGeodesic(FEModel* pfem);\n    ~FEFiberIntegrationGeodesic();\n\t\n\t//! Initialization\n\tbool Init() override;\n    \n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// get iterator\n\tFEFiberIntegrationSchemeIterator* GetIterator(FEMaterialPoint* mp) override;\n\n\t// get number of integration points\n\tint IntegrationPoints() const override { return m_nint; };\n\nprotected:\n\tvoid InitIntegrationRule();  \n\nprivate: // parameters\n\tint             m_nres;\t// resolution\n\nprotected:\n    int             m_nint; // number of integration points\n\tdouble          m_cth[NSTH];\n\tdouble          m_sth[NSTH];\n\tdouble          m_cph[NSTH];\n\tdouble          m_sph[NSTH];\n\tdouble          m_w[NSTH];\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationScheme.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberIntegrationScheme.h\"\n\nFEFiberIntegrationScheme::FEFiberIntegrationScheme(FEModel* pfem) : FEMaterialProperty(pfem)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationScheme.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberDensityDistribution.h\"\n#include \"febiomech_api.h\"\n\n//----------------------------------------------------------------------------------\n// This is an iterator class that can be used to loop over all integration points of\n// a fiber integration scheme. It must implement the Next functions.\n// During the Next call, it should also update the fiber vector and weight.\n// Next should return false if there is no more integration point\nclass FEFiberIntegrationSchemeIterator\n{\npublic:\n\tFEFiberIntegrationSchemeIterator() {}\n\tvirtual ~FEFiberIntegrationSchemeIterator() {}\n\n\t// Move to the next integration point\n\t// This also updates the m_fiber and m_weight members\n\tvirtual bool Next() = 0;\n\n\t// check if the iterator is valid\n\tvirtual bool IsValid() = 0;\n\npublic:\n\tvec3d\tm_fiber;\t\t// current fiber vector at integration point\n\tdouble\tm_weight;\t\t// current integration weight\n};\n\n//----------------------------------------------------------------------------------\n// Base clase for integration schemes for continuous fiber distributions.\n// The purpose of this class is mainly to provide an interface to the integration schemes\n// for the FEBio input file. The code will use the GetIterator function to create an\n// iterator that can be used to loop over all the integration points of the scheme and to\n// evaluate the fiber vector and weights at each point.\nclass FEBIOMECH_API FEFiberIntegrationScheme : public FEMaterialProperty\n{\npublic:\n    FEFiberIntegrationScheme(FEModel* pfem);\n    \n\t// Creates an iterator for the scheme. \n\t// In general, the integration scheme may depend on the material point.\n\t// The passed material point pointer will be zero when evaluating the integrated fiber density\n\tvirtual FEFiberIntegrationSchemeIterator* GetIterator(FEMaterialPoint* mp = 0) = 0;\n\n\t// returns the number of integration point (returns -1 if scheme does not use a fixed number of integration points.)\n\tvirtual int IntegrationPoints() const = 0;\n\n\tFECORE_BASE_CLASS(FEFiberIntegrationScheme)\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationTrapezoidal.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberIntegrationTrapezoidal.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\nclass FEFiberIntegrationTrapezoidal::Iterator : public FEFiberIntegrationSchemeIterator\n{\npublic:\n\tIterator(FEMaterialPoint* mp, int nth)\n\t{\n\t\tm_nth = nth;\n\n\t\ta0 = vec3d(1,0,0);\n\t\ta1 = vec3d(0,1,0);\n\n\t\tdouble pi = 4 * atan(1.0);\n\t\tdth = pi / m_nth;  // integrate from 0 to pi\n\n\t\ti = -1;\n\t\tNext();\n\t}\n\n\tbool IsValid()\n\t{\n\t\treturn (i < m_nth);\n\t}\n\n\t// move to the next integration point\n\tbool Next()\n\t{\n\t\t++i;\n\t\tif (i < m_nth)\n\t\t{\n\t\t\tdouble theta = i*dth;\n\t\t\tm_fiber = a0*cos(theta) + a1*sin(theta);\n\n\t\t\t// Multiply by 2 since fibers along theta+pi have same stress as along theta\n\t\t\tm_weight = dth*2.0;\n\n\t\t\treturn true;\n\t\t}\n\t\telse return false;\n\t}\n\npublic:\n\tint\tm_nth;\n\tdouble dth;\n\tvec3d a0, a1;\n\n\tint i;\n};\n\n//-----------------------------------------------------------------------------\n// FEFiberIntegrationTrapezoidal\n//-----------------------------------------------------------------------------\n\n// register the material with the framework\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberIntegrationTrapezoidal, FEFiberIntegrationScheme)\n\tADD_PARAMETER(m_nth, FE_RANGE_GREATER(0), \"nth\");\nEND_FECORE_CLASS();\n\nFEFiberIntegrationTrapezoidal::FEFiberIntegrationTrapezoidal(FEModel* pfem) : FEFiberIntegrationScheme(pfem)\n{ \n\tm_nth = 12; \n}\n\nFEFiberIntegrationTrapezoidal::~FEFiberIntegrationTrapezoidal()\n{\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationSchemeIterator* FEFiberIntegrationTrapezoidal::GetIterator(FEMaterialPoint* mp)\n{\n\treturn new Iterator(mp, m_nth);\n}\n\n/*\n//-----------------------------------------------------------------------------\nmat3ds FEFiberIntegrationTrapezoidal::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n    // initialize stress tensor\n\tmat3ds s;\n\ts.zero();\n    \n    double theta;\n    double pi = 4*atan(1.0);\n    double dth = pi/m_nth;  // integrate from 0 to pi\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0(Q(0,0),Q(1,0),Q(2,0)); // local x-direction unit vector\n    vec3d a1(Q(0,1),Q(1,1),Q(2,1)); // local y-direction unit vector\n    \n    vec3d n0e, n0a;\n    \n    // loop over all integration points\n    for (int i=0; i<m_nth; ++i) {\n        theta = i*dth;\n        \n        // set fiber direction in x-y plane of local coordinate system\n        n0a = a0*cos(theta) + a1*sin(theta);\n        // evaluate local fiber distribution\n        double R = m_pFDD->FiberDensity(n0a);\n        \n        // rotate to global configuration to set fiber direction\n        n0e = Q*n0a;\n        m_pFmat->SetFiberDirection(mp, n0e);\n        \n        // calculate the stress\n        s += m_pFmat->Stress(mp)*(R*dth);\n    }\n    \n    // Multiply by 2 since fibers along theta+pi have same stress as along theta\n\treturn s*2;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberIntegrationTrapezoidal::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n    // initialize stress tensor\n\ttens4ds c;\n\tc.zero();\n    \n    double theta;\n    double pi = 4*atan(1.0);\n    double dth = pi/m_nth;  // integrate from 0 to pi\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0(Q(0,0),Q(1,0),Q(2,0)); // local x-direction unit vector\n    vec3d a1(Q(0,1),Q(1,1),Q(2,1)); // local y-direction unit vector\n    \n    vec3d n0e, n0a;\n    \n    // loop over all integration points\n    for (int i=0; i<m_nth; ++i) {\n        theta = i*dth;\n        \n        // set fiber direction in x-y plane of local coordinate system\n        n0a = a0*cos(theta) + a1*sin(theta);\n        // evaluate local fiber distribution\n        double R = m_pFDD->FiberDensity(n0a);\n        \n        // rotate to global configuration to set fiber direction\n        n0e = Q*n0a;\n        m_pFmat->SetFiberDirection(mp, n0e);\n        \n        // calculate the stress\n        c += m_pFmat->Tangent(mp)*(R*dth);\n    }\n\n    // Multiply by 2 since fibers along theta+pi have same stress as along theta\n    return c*2;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberIntegrationTrapezoidal::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n    // initialize strain energy density\n\tdouble sed = 0.0;\n    \n    double theta;\n    double pi = 4*atan(1.0);\n    double dth = pi/m_nth;  // integrate from 0 to pi\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0(Q(0,0),Q(1,0),Q(2,0)); // local x-direction unit vector\n    vec3d a1(Q(0,1),Q(1,1),Q(2,1)); // local y-direction unit vector\n    \n    vec3d n0e, n0a;\n    \n    // loop over all integration points\n    for (int i=0; i<m_nth; ++i) {\n        theta = i*dth;\n        \n        // set fiber direction in x-y plane of local coordinate system\n        n0a = a0*cos(theta) + a1*sin(theta);\n        // evaluate local fiber distribution\n        double R = m_pFDD->FiberDensity(n0a);\n        \n        // rotate to global configuration to set fiber direction\n        n0e = Q*n0a;\n        m_pFmat->SetFiberDirection(mp, n0e);\n        \n        // calculate the stress\n        sed += m_pFmat->StrainEnergyDensity(mp)*(R*dth);\n    }\n    \n    // Multiply by 2 since fibers along theta+pi have same sed as along theta\n\treturn sed*2;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberIntegrationTrapezoidal::IntegratedFiberDensity()\n{\n    // initialize integrated fiber density distribution\n    double IFD = 1;\n    \n\tdouble C = 0;\n    \n    double theta;\n    double pi = 4*atan(1.0);\n    double dth = pi/m_nth;  // integrate from 0 to pi\n    \n    vec3d a0(1,0,0); // local x-direction unit vector\n    vec3d a1(0,1,0); // local y-direction unit vector\n    \n    vec3d n0a;\n    \n    // loop over all integration points\n    for (int i=0; i<m_nth; ++i) {\n        theta = i*dth;\n        \n        // set fiber direction in x-y plane of local coordinate system\n        n0a = a0*cos(theta) + a1*sin(theta);\n        // evaluate local fiber distribution\n        double R = m_pFDD->FiberDensity(n0a);\n        \n        // integrate the fiber distribution\n        C += R*dth;\n    }\n    \n    // Multiply by 2 to take advantange of symmetry\n\tIFD = C*2;\n    return IFD;\n}\n*/\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationTrapezoidal.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFiberIntegrationScheme.h\"\n\n//----------------------------------------------------------------------------------\n// Trapezoidal integration scheme for 2D continuous fiber distributions\n//\nclass FEFiberIntegrationTrapezoidal : public FEFiberIntegrationScheme\n{\n\tclass Iterator;\n\npublic:\n    FEFiberIntegrationTrapezoidal(FEModel* pfem);\n    ~FEFiberIntegrationTrapezoidal();\n\n\t// get iterator\t\n\tFEFiberIntegrationSchemeIterator* GetIterator(FEMaterialPoint* mp) override;\n\n\t// get number of integration points\n\tint IntegrationPoints() const override { return -1; };\n    \nprivate:\n    int             m_nth;  // number of trapezoidal integration points along theta\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationTriangle.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberIntegrationTriangle.h\"\n#include \"triangle_sphere.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\nclass FEFiberIntegrationTriangle::Iterator : public FEFiberIntegrationSchemeIterator\n{\npublic:\n\tIterator(int nint, const double* cth, const double* cph, const double* sth, const double* sph, const double* wn)\n\t{\n\t\tm_nint = nint;\n\t\tm_cth = cth;\n\t\tm_cph = cph;\n\t\tm_sth = sth;\n\t\tm_sph = sph;\n\t\tm_wn = wn;\n\n\t\tn = -1;\n\t\tNext();\n\t}\n\n\tbool IsValid()\n\t{\n\t\treturn (n < m_nint);\n\t}\n\n\t// move to the next integration point\n\tbool Next()\n\t{\n\t\tn++;\n\t\tif (n < m_nint)\n\t\t{\n\t\t\tm_fiber.x = m_cth[n] * m_sph[n];\n\t\t\tm_fiber.y = m_sth[n] * m_sph[n];\n\t\t\tm_fiber.z = m_cph[n];\n\n\t\t\tm_weight = m_wn[n];\n\t\t\treturn true;\n\t\t}\n\t\telse return false;\n\t}\n\nprivate:\n\tint\t\tn;\n\tint\t\tm_nint;\n\tconst double* m_cth;\n\tconst double* m_cph;\n\tconst double* m_sth;\n\tconst double* m_sph;\n\tconst double* m_wn;\n};\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberIntegrationTriangle, FEFiberIntegrationScheme)\n\tADD_PARAMETER(m_nre, \"resolution\")->setEnums(\"  20\\0  34\\0  60\\0  74\\0 196\\0 210\\0 396\\0 410\\0 596\\0 610\\0 796\\0 810\\0 996\\0 1010\\0 1196\\0 1210\\0 1396\\0 1410\\0 1596\\0 1610\\0 1796\\0\");\nEND_FECORE_CLASS();\n\nFEFiberIntegrationTriangle::FEFiberIntegrationTriangle(FEModel* pfem) : FEFiberIntegrationScheme(pfem)\n{ \n\tm_nres = 0;\n    m_nre = -1;\n}\n\nFEFiberIntegrationTriangle::~FEFiberIntegrationTriangle()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberIntegrationTriangle::Init()\n{\n    int list[21] = {NST20, NST34, NST60, NST74, NST196, NST210, NST396, NST410, NST596, NST610, NST796, NST810, NST996, NST1010, NST1196, NST1210, NST1396, NST1410, NST1596, NST1610, NST1796};\n    \n    // this is needed to maintain backward compatibility\n    if (m_nre > 21) m_nres = m_nre;\n    else m_nres = list[m_nre];\n    \n\t// initialize integration rule data\n\tInitIntegrationRule();\n    \n    // also initialize the parent class\n    return FEFiberIntegrationScheme::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberIntegrationTriangle::Serialize(DumpStream& ar)\n{\n\tFEFiberIntegrationScheme::Serialize(ar);\n\tif (ar.IsSaving() == false)\n\t{\n\t\tInitIntegrationRule();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberIntegrationTriangle::InitIntegrationRule()\n{\n    switch (m_nres) {\n        case 20:\n            m_nint=NST20;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA20[n]);\n                m_sth[n] = sin(THETA20[n]);\n                m_cph[n] = cos(PHI20[n]);\n                m_sph[n] = sin(PHI20[n]);\n                m_w[n] = AREA20[n];\n            }\n            break;\n        case 34:\n            m_nint=NST34;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA34[n]);\n                m_sth[n] = sin(THETA34[n]);\n                m_cph[n] = cos(PHI34[n]);\n                m_sph[n] = sin(PHI34[n]);\n                m_w[n] = AREA34[n];\n            }\n            break;\n        case 60:\n            m_nint=NST60;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA60[n]);\n                m_sth[n] = sin(THETA60[n]);\n                m_cph[n] = cos(PHI60[n]);\n                m_sph[n] = sin(PHI60[n]);\n                m_w[n] = AREA60[n];\n            }\n            break;\n        case 74:\n            m_nint=NST74;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA74[n]);\n                m_sth[n] = sin(THETA74[n]);\n                m_cph[n] = cos(PHI74[n]);\n                m_sph[n] = sin(PHI74[n]);\n                m_w[n] = AREA74[n];\n            }\n            break;\n        case 196:\n            m_nint=NST196;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA196[n]);\n                m_sth[n] = sin(THETA196[n]);\n                m_cph[n] = cos(PHI196[n]);\n                m_sph[n] = sin(PHI196[n]);\n                m_w[n] = AREA196[n];\n            }\n            break;\n        case 210:\n            m_nint=NST210;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA210[n]);\n                m_sth[n] = sin(THETA210[n]);\n                m_cph[n] = cos(PHI210[n]);\n                m_sph[n] = sin(PHI210[n]);\n                m_w[n] = AREA210[n];\n            }\n            break;\n                \n        case 396:\n            m_nint=NST396;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA396[n]);\n                m_sth[n] = sin(THETA396[n]);\n                m_cph[n] = cos(PHI396[n]);\n                m_sph[n] = sin(PHI396[n]);\n                m_w[n] = AREA396[n];\n            }\n            break;\n                \n        case 410:\n            m_nint=NST410;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA410[n]);\n                m_sth[n] = sin(THETA410[n]);\n                m_cph[n] = cos(PHI410[n]);\n                m_sph[n] = sin(PHI410[n]);\n                m_w[n] = AREA410[n];\n            }\n            break;\n        case 596:\n            m_nint=NST596;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA596[n]);\n                m_sth[n] = sin(THETA596[n]);\n                m_cph[n] = cos(PHI596[n]);\n                m_sph[n] = sin(PHI596[n]);\n                m_w[n] = AREA596[n];\n            }\n            break;\n        case 610:\n            m_nint=NST610;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA610[n]);\n                m_sth[n] = sin(THETA610[n]);\n                m_cph[n] = cos(PHI610[n]);\n                m_sph[n] = sin(PHI610[n]);\n                m_w[n] = AREA610[n];\n            }\n            break;\n        case 796:\n            m_nint=NST796;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA796[n]);\n                m_sth[n] = sin(THETA796[n]);\n                m_cph[n] = cos(PHI796[n]);\n                m_sph[n] = sin(PHI796[n]);\n                m_w[n] = AREA796[n];\n            }\n            break;\n        case 810:\n            m_nint=NST810;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA810[n]);\n                m_sth[n] = sin(THETA810[n]);\n                m_cph[n] = cos(PHI810[n]);\n                m_sph[n] = sin(PHI810[n]);\n                m_w[n] = AREA810[n];\n            }\n            break;\n                \n        case 996:\n            m_nint=NST996;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA996[n]);\n                m_sth[n] = sin(THETA996[n]);\n                m_cph[n] = cos(PHI996[n]);\n                m_sph[n] = sin(PHI996[n]);\n                m_w[n] = AREA996[n];\n            }\n            break;\n                \n        case 1010:\n            m_nint=NST1010;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1010[n]);\n                m_sth[n] = sin(THETA1010[n]);\n                m_cph[n] = cos(PHI1010[n]);\n                m_sph[n] = sin(PHI1010[n]);\n                m_w[n] = AREA1010[n];\n            }\n            break;\n                \n        case 1196:\n            m_nint=NST1196;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1196[n]);\n                m_sth[n] = sin(THETA1196[n]);\n                m_cph[n] = cos(PHI1196[n]);\n                m_sph[n] = sin(PHI1196[n]);\n                m_w[n] = AREA1196[n];\n            }\n            break;\n                \n        case 1210:\n            m_nint=NST1210;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1210[n]);\n                m_sth[n] = sin(THETA1210[n]);\n                m_cph[n] = cos(PHI1210[n]);\n                m_sph[n] = sin(PHI1210[n]);\n                m_w[n] = AREA1210[n];\n            }\n            break;\n        case 1396:\n            m_nint=NST1396;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1396[n]);\n                m_sth[n] = sin(THETA1396[n]);\n                m_cph[n] = cos(PHI1396[n]);\n                m_sph[n] = sin(PHI1396[n]);\n                m_w[n] = AREA1396[n];\n            }\n            break;\n        case 1410:\n            m_nint=NST1410;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1410[n]);\n                m_sth[n] = sin(THETA1410[n]);\n                m_cph[n] = cos(PHI1410[n]);\n                m_sph[n] = sin(PHI1410[n]);\n                m_w[n] = AREA1410[n];\n            }\n            break;\n        case 1596:\n            m_nint=NST1596;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1596[n]);\n                m_sth[n] = sin(THETA1596[n]);\n                m_cph[n] = cos(PHI1596[n]);\n                m_sph[n] = sin(PHI1596[n]);\n                m_w[n] = AREA1596[n];\n            }\n            break;\n        case 1610:\n            m_nint=NST1610;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1610[n]);\n                m_sth[n] = sin(THETA1610[n]);\n                m_cph[n] = cos(PHI1610[n]);\n                m_sph[n] = sin(PHI1610[n]);\n                m_w[n] = AREA1610[n];\n            }\n            break;\n        case 1796:\n            m_nint=NST1796;\n            for (int n=0; n<m_nint; ++n)\n            {\n                m_cth[n] = cos(THETA1796[n]);\n                m_sth[n] = sin(THETA1796[n]);\n                m_cph[n] = cos(PHI1796[n]);\n                m_sph[n] = sin(PHI1796[n]);\n                m_w[n] = AREA1796[n];\n            }\n            break;\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEFiberIntegrationSchemeIterator* FEFiberIntegrationTriangle::GetIterator(FEMaterialPoint* mp)\n{\n\treturn new Iterator(m_nint, &m_cth[0], &m_cph[0], &m_sth[0], &m_sph[0], &m_w[0]);\n}\n\n/*\n//-----------------------------------------------------------------------------\nmat3ds FEFiberIntegrationTriangle::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tmat3d QT = Q.transpose();\n    \n    // loop over all integration points\n    double R;\n    vec3d n0e, n0a;\n    mat3ds s;\n    s.zero();\n    \n    for (int n=0; n<m_nint; ++n)\n    {\n        // set the global fiber direction in reference configuration\n        n0e.x = m_cth[n]*m_sph[n];\n        n0e.y = m_sth[n]*m_sph[n];\n        n0e.z = m_cph[n];\n        m_pFmat->SetFiberDirection(mp, n0e);\n        \n        // get the local material fiber direction in reference configuration\n        n0a = QT*n0e;\n        // evaluate the fiber density\n        R = m_pFDD->FiberDensity(n0a);\n        \n        // evaluate this fiber's contribution to the stress\n        s += m_pFmat->Stress(mp)*(R*m_w[n]);\n    }\n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberIntegrationTriangle::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tmat3d QT = Q.transpose();\n    \n    // loop over all integration points\n    double R;\n    vec3d n0e, n0a;\n    tens4ds c;\n    c.zero();\n    \n    for (int n=0; n<m_nint; ++n)\n    {\n        // set the global fiber direction in reference configuration\n        n0e.x = m_cth[n]*m_sph[n];\n        n0e.y = m_sth[n]*m_sph[n];\n        n0e.z = m_cph[n];\n        m_pFmat->SetFiberDirection(mp, n0e);\n        \n        // get the local material fiber direction in reference configuration\n        n0a = QT*n0e;\n        // evaluate the fiber density\n        R = m_pFDD->FiberDensity(n0a);\n        \n        // evaluate this fiber's contribution to the tangent\n        c += m_pFmat->Tangent(mp)*(R*m_w[n]);\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberIntegrationTriangle::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\tmat3d QT = Q.transpose();\n    \n    // loop over all integration points\n    double R;\n    vec3d n0e, n0a;\n    double sed = 0.0;\n    \n    for (int n=0; n<m_nint; ++n)\n    {\n        // set the global fiber direction in reference configuration\n        n0e.x = m_cth[n]*m_sph[n];\n        n0e.y = m_sth[n]*m_sph[n];\n        n0e.z = m_cph[n];\n        m_pFmat->SetFiberDirection(mp, n0e);\n        \n        // get the local material fiber direction in reference configuration\n        n0a = QT*n0e;\n        // evaluate the fiber density\n        R = m_pFDD->FiberDensity(n0a);\n        \n        // evaluate this fiber's contribution to the stress\n        sed += m_pFmat->StrainEnergyDensity(mp)*(R*m_w[n]);\n    }\n    return sed;\n}\n\n\n//-----------------------------------------------------------------------------\ndouble FEFiberIntegrationTriangle::IntegratedFiberDensity()\n{\n    // initialize integrated fiber density distribution\n    double IFD = 1;\n    \n    // loop over all integration points\n    double R;\n    vec3d n0a;\n    double C = 0;\n    \n    for (int n=0; n<m_nint; ++n)\n    {\n        // set the global fiber direction in reference configuration\n        n0a.x = m_cth[n]*m_sph[n];\n        n0a.y = m_sth[n]*m_sph[n];\n        n0a.z = m_cph[n];\n        \n        // evaluate the fiber density\n        R = m_pFDD->FiberDensity(n0a);\n        \n        // integrate the fiber density\n        C += R*m_w[n];\n    }\n    IFD = C;\n    return IFD;\n}\n*/\n"
  },
  {
    "path": "FEBioMech/FEFiberIntegrationTriangle.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEFiberIntegrationScheme.h\"\n\n//----------------------------------------------------------------------------------\n// Geodesic dome integration scheme for continuous fiber distributions\n//\nclass FEFiberIntegrationTriangle : public FEFiberIntegrationScheme\n{\n\tclass Iterator;\n\npublic:\n    FEFiberIntegrationTriangle(FEModel* pfem);\n    ~FEFiberIntegrationTriangle();\n\t\n\t//! Initialization\n\tbool Init() override;\n    \n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// create iterator\n\tFEFiberIntegrationSchemeIterator* GetIterator(FEMaterialPoint* mp) override;\n\n\t// get number of integration points\n\tint IntegrationPoints() const override { return m_nint; };\n\nprotected:\n\tvoid InitIntegrationRule();\n    \npublic: // parameters\n\tint             m_nres;\t// resolution\n    int             m_nre;  // resolution entry\n\npublic:\n    int             m_nint; // number of integration points\n\tdouble          m_cth[2000];\n\tdouble          m_sth[2000];\n\tdouble          m_cph[2000];\n\tdouble          m_sph[2000];\n\tdouble          m_w[2000];\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberKiousisUncoupled.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberKiousisUncoupled.h\"\n#include <limits>\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledFiberKiousis, FEElasticFiberMaterialUC)\n    ADD_PARAMETER(m_d1, \"d1\");\n    ADD_PARAMETER(m_d2, \"d2\");\n    ADD_PARAMETER(m_n , \"n\" );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEUncoupledFiberKiousis::FEUncoupledFiberKiousis(FEModel* pfem) : FEElasticFiberMaterialUC(pfem)\n{\n    m_d1 = 0;\n    m_d2 = 1;\n    m_n = 2;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEUncoupledFiberKiousis::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F*pow(J,-1.0/3.0);\n    \n    // loop over all integration points\n    const double eps = 0;\n    mat3ds C = pt.DevRightCauchyGreen();\n    mat3ds s;\n    \n    double d1 = m_d1(mp);\n    double d2 = m_d2(mp);\n    double n = m_n(mp);\n    \n    // Calculate In = n0*C*n0\n    double In_d2 = n0*(C*n0) - d2;\n\n    // only take fibers in tension into consideration\n    if (In_d2 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate the fiber stress\n        s = N*(2.0*d1*pow(In_d2, n-1)/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEUncoupledFiberKiousis::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F*pow(J,-1.0/3.0);\n    \n    // loop over all integration points\n    const double eps = 0;\n    mat3ds C = pt.DevRightCauchyGreen();\n    mat3ds s;\n    tens4ds c;\n    \n    // Calculate In = n0*C*n0\n    double In_d2 = n0*(C*n0) - m_d2(mp);\n    double n = m_n(mp);\n\n    // only take fibers in tension into consideration\n    if (In_d2 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // calculate the fiber stress\n        s = N*(2.0*m_d1(mp)*pow(In_d2, n-1)/J);\n        \n        // calculate the fiber tangent\n        c = NxN*(4.0*(n-1)*m_d1(mp)*pow(In_d2, n-2)/J);\n        \n        // This is the final value of the elasticity tensor\n        mat3dd I(1);\n        tens4ds IxI = dyad1s(I);\n        tens4ds I4  = dyad4s(I);\n        c += ((I4+IxI/3.0)*s.tr() - dyad1s(I,s))*(2./3.)\n        - (ddots(IxI, c)-IxI*(c.tr()/3.))/3.;\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEUncoupledFiberKiousis::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // loop over all integration points\n    mat3ds C = pt.DevRightCauchyGreen();\n    \n    // Calculate In = n0*C*n0\n    double In_d2 = n0*(C*n0) - m_d2(mp);\n    double n = m_n(mp);\n    \n    // only take fibers in tension into consideration\n    const double eps = 0;\n    if (In_d2 >= eps)\n    {\n        // calculate strain energy derivative\n        sed = m_d1(mp)/n*pow(In_d2, n);\n    }\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberKiousisUncoupled.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! D. E. Kiousis, T. C. Gasser and G. A. Holzapfel\n//! Smooth contact strategies with emphasis on the modeling of balloon angioplasty with stenting\n//! Int. J. Numer. Meth. Engng 2008; 75:826–855, equation (21)\n\nclass FEUncoupledFiberKiousis : public FEElasticFiberMaterialUC\n{\npublic:\n    FEUncoupledFiberKiousis(FEModel* pfem);\n    \n    //! Cauchy stress\n    virtual mat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // Spatial tangent\n    virtual tens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    //! Strain energy density\n    virtual double DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \nprotected:\n    FEParamDouble   m_d1;   // material coefficient d1 (proportional to initial fiber modulus)\n    FEParamDouble   m_d2;   // material coefficient d2 (square of stretch when fiber engages)\n    FEParamDouble   m_n;    // material coefficient n (power exponent)\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFiberMaterial.h\"\n#include \"FEFiberMaterialPoint.h\"\n\nFEFiberMaterial::FEFiberMaterial(FEModel* fem) : FEMaterialProperty(fem)\n{\n}\n\nFEMaterialPointData* FEFiberMaterial::CreateMaterialPointData()\n{\n\treturn new FEFiberMaterialPoint(nullptr);\n}\n\n//===========================================================================================\nFEFiberMaterialUncoupled::FEFiberMaterialUncoupled(FEModel* fem) : FEMaterialProperty(fem)\n{\n}\n\nFEMaterialPointData* FEFiberMaterialUncoupled::CreateMaterialPointData()\n{\n\treturn new FEFiberMaterialPoint(nullptr);\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiomech_api.h\"\n#include <FECore/tens4d.h>\n\nclass FEBIOMECH_API FEFiberMaterial : public FEMaterialProperty\n{\n\tFECORE_BASE_CLASS(FEFiberMaterial);\n\npublic:\n\tFEFiberMaterial(FEModel* fem);\n\n\tFEMaterialPointData* CreateMaterialPointData();\n\n\tvirtual mat3ds FiberStress(FEMaterialPoint& mp, const vec3d& fiber) = 0;\n\n\tvirtual tens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& fiber) = 0;\n\n\tvirtual double FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& fiber) = 0;\n};\n\n// fiber materials for use in uncoupled materials\nclass FEBIOMECH_API FEFiberMaterialUncoupled : public FEMaterialProperty\n{\n\tFECORE_BASE_CLASS(FEFiberMaterialUncoupled);\n\npublic:\n\tFEFiberMaterialUncoupled(FEModel* fem);\n\n\tFEMaterialPointData* CreateMaterialPointData();\n\n\tvirtual mat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& fiber) = 0;\n\n\tvirtual tens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& fiber) = 0;\n\n\tvirtual double DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& fiber) = 0;\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEFiberMaterialPoint.h\"\n#include \"stdafx.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nFEFiberMaterialPoint::FEFiberMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEFiberMaterialPoint::Copy()\n{\n    FEFiberMaterialPoint* pt = new FEFiberMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberMaterialPoint::Init()\n{\n    // initialize data to identity\n    m_Us = mat3dd(1);\n    m_bUs = false;\n    \n    // don't forget to intialize the nested data\n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFiberMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_Us & m_bUs;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEFiberMaterialPoint::FiberPreStretch(const vec3d a0)\n{\n    // account for prior deformation in multigenerational formulation\n    if (m_bUs) {\n        vec3d a = (m_Us*a0);\n        a.unit();\n        return a;\n    }\n    else\n        return a0;\n}\n"
  },
  {
    "path": "FEBioMech/FEFiberMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores the fiber pre-stretch\nclass FEBIOMECH_API FEFiberMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFEFiberMaterialPoint(FEMaterialPointData* pt);\n    \n\tFEMaterialPointData* Copy();\n    \n    void Init();\n    \n    void Serialize(DumpStream& ar);\n    \npublic:\n    // Set or clear pre-stretch, as needed in multigenerational materials (e.g., reactive viscoelasticity)\n    void SetPreStretch(const mat3ds Us) { m_Us = Us; m_bUs = true; }\n    void ResetPreStretch() { m_bUs = false; }\n    vec3d FiberPreStretch(const vec3d a0);\n\npublic:\n    mat3ds  m_Us;   //!< pre-stretch tensor for fiber\n    bool    m_bUs;  //!< flag for pre-stretch\n\tint\t\tm_index = -1;\t//!< used as index in CDF material\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberNHUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberNHUC.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberNHUC, FEFiberMaterialUncoupled)\n\tADD_PARAMETER(m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberNHUC::FEFiberNHUC(FEModel* pfem) : FEFiberMaterialUncoupled(pfem) \n{ \n\tm_mu = 0; \n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberNHUC::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = pt.m_J;\n\t// distortional part of deformation gradient\n\tmat3d F = pt.m_F*pow(J, -1.0 / 3.0);\n\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds s;\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\t// only take fibers in tension into consideration\n\tif (In_1 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\n\t\t// calculate the fiber stress\n\t\ts = N*(m_mu*In_1 / J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\n\treturn s.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberNHUC::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = pt.m_J;\n\t// distortional part of deformation gradient\n\tmat3d F = pt.m_F*pow(J, -1.0 / 3.0);\n\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3ds s;\n\ttens4ds c;\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\t// only take fibers in tension into consideration\n\tif (In_1 >= eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n\n\t\t// calculate the fiber stress\n\t\ts = N*(m_mu*In_1 / J);\n\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(2 * m_mu / J);\n\n        // This is the final value of the elasticity tensor\n        mat3dd I(1);\n        tens4ds IxI = dyad1s(I);\n        tens4ds I4 = dyad4s(I);\n        c += ((I4 + IxI / 3.0)*s.tr() - dyad1s(I, s))*(2. / 3.)\n        - (ddots(IxI, c) - IxI*(c.tr() / 3.)) / 3.;\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\ndouble FEFiberNHUC::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tdouble sed = 0.0;\n\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.DevRightCauchyGreen();\n\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\n\t// only take fibers in tension into consideration\n\tif (In_1 >= eps)\n\t\tsed = 0.25*m_mu*In_1*In_1;\n\n\treturn sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledFiberNH, FEElasticFiberMaterialUC)\n\tADD_PARAMETER(m_fib.m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberNHUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Neo-Hookean law\n\nclass FEFiberNHUC : public FEFiberMaterialUncoupled\n{\npublic:\n\tFEFiberNHUC(FEModel* pfem);\n\n\t//! Cauchy stress\n\tmat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tdouble\tm_mu;       // shear modulus\n\n\tDECLARE_FECORE_CLASS(); // declare the parameter list\n};\n\n\nclass FEUncoupledFiberNH : public FEElasticFiberMaterialUC_T<FEFiberNHUC>\n{\npublic:\n\tFEUncoupledFiberNH(FEModel* fem) : FEElasticFiberMaterialUC_T<FEFiberNHUC>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberNaturalNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEFiberNaturalNeoHookean.h\"\n\n//-----------------------------------------------------------------------------\n// FEFiberNH\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberNaturalNH, FEFiberMaterial)\n    ADD_PARAMETER(m_ksi, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\");\n    ADD_PARAMETER(m_lam0 , FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberNaturalNH::FEFiberNaturalNH(FEModel* pfem) : FEFiberMaterial(pfem)\n{\n    m_ksi = 0;\n    m_lam0 = 1;\n    m_epsf = 1;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberNaturalNH::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    double ksi = m_ksi(mp);\n    double lam0 = m_lam0(mp);\n    \n    // get stretch ratio lamn along fiber\n    vec3d nt = F*n0;\n    double lamn = nt.unit();\n    double epsn = log(lamn/lam0);\n    const double epsf = m_epsf* std::numeric_limits<double>::epsilon();\n\n    // only take fibers in tension into consideration\n    mat3ds s;\n    if (epsn > epsf)\n    {\n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate the fiber stress\n        s = N*(ksi*epsn/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberNaturalNH::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    double ksi = m_ksi(mp);\n    double lam0 = m_lam0(mp);\n    \n    // get stretch ratio lamn along fiber\n    vec3d nt = F*n0;\n    double lamn = nt.unit();\n    double epsn = log(lamn/lam0);\n    const double epsf = m_epsf* std::numeric_limits<double>::epsilon();\n\n    // only take fibers in tension into consideration\n    tens4ds c;\n    if (epsn > epsf)\n    {\n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // calculate the fiber tangent\n        c = NxN*(ksi*(1-2*epsn)/J);\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\ndouble FEFiberNaturalNH::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    \n    double ksi = m_ksi(mp);\n    double lam0 = m_lam0(mp);\n    \n    // get stretch ratio lamn along fiber\n    vec3d nt = F*n0;\n    double lamn = nt.unit();\n    double epsn = log(lamn/lam0);\n    const double epsf = m_epsf* std::numeric_limits<double>::epsilon();\n\n    // only take fibers in tension into consideration\n    double sed = 0.0;\n    if (epsn > epsf)\n        sed = 0.5*ksi*epsn*epsn;\n    \n    return sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberNaturalNH, FEElasticFiberMaterial)\n    ADD_PARAMETER(m_fib.m_ksi, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\");\n    ADD_PARAMETER(m_fib.m_lam0 , FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam0\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberNaturalNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Neo-Hookean law\n\nclass FEFiberNaturalNH : public FEFiberMaterial\n{\npublic:\n    FEFiberNaturalNH(FEModel* pfem);\n    \n    //! Cauchy stress\n    mat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // Spatial tangent\n    tens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    //! Strain energy density\n    double FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \npublic:\n    FEParamDouble   m_ksi;      // fiber modulus\n    FEParamDouble   m_lam0;     // stretch threshold for tensile response\n    double          m_epsf;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n\nclass FEElasticFiberNaturalNH : public FEElasticFiberMaterial_T<FEFiberNaturalNH>\n{\npublic:\n    FEElasticFiberNaturalNH(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberNaturalNH>(fem) {}\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEFiberNeoHookean.h\"\n\n//-----------------------------------------------------------------------------\n// FEFiberNH\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberNH, FEFiberMaterial)\n\tADD_PARAMETER(m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberNH::FEFiberNH(FEModel* pfem) : FEFiberMaterial(pfem)\n{ \n\tm_mu = 0; \n\tm_epsf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberNH::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\t\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\t\n\t// only take fibers in tension into consideration\n\tmat3ds s;\n\tif (In_1 > 0.0)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\t\n\t\t// calculate the fiber stress\n\t\ts = N*(m_mu*In_1/J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberNH::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\t\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\t\n\t// only take fibers in tension into consideration\n\ttens4ds c;\n\tconst double eps = m_epsf*std::numeric_limits<double>::epsilon();\n\tif (In_1 > eps)\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0;\n\t\t\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n\t\t\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(2*m_mu/J);\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\ndouble FEFiberNH::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\t\n\t// Calculate In = n0*C*n0\n\tdouble In_1 = n0*(C*n0) - 1.0;\n\t\n\t// only take fibers in tension into consideration\n\tdouble sed = 0.0;\n\tif (In_1 > 0.0)\n        sed = 0.25*m_mu*In_1*In_1;\n    \n    return sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberNH, FEElasticFiberMaterial)\n\tADD_PARAMETER(m_fib.m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Neo-Hookean law\nclass FEFiberNH : public FEFiberMaterial\n{\npublic:\n\tFEFiberNH(FEModel* pfem);\n\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n\tdouble\tm_mu;       // shear modulus\n\tdouble\tm_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Neo-Hookean law\nclass FEElasticFiberNH : public FEElasticFiberMaterial_T<FEFiberNH>\n{\npublic:\n\tFEElasticFiberNH(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberNH>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberPow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEFiberPow.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberPow, FEFiberMaterial)\n\tADD_PARAMETER(m_ksi, FE_RANGE_GREATER(0.0), \"ksi\")->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus\");\n\tADD_PARAMETER(m_beta, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\")->setLongName(\"power exponent\");\n\tADD_PARAMETER(m_tension_only, \"tension_only\");\nEND_FECORE_CLASS();\n\nFEFiberPow::FEFiberPow(FEModel* pfem) : FEFiberMaterial(pfem)\n{\n\tm_ksi = 0.0;\n\tm_beta = 2.0;\n\tm_epsf = 0.0;\n\tm_tension_only = true;\n}\n\nmat3ds FEFiberPow::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// initialize material constants\n\tdouble ksi = m_ksi(mp);\n\tdouble beta = m_beta(mp);\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds s;\n\n\t// Calculate In\n\tdouble In = n0 * (C * n0);\n\n\t// only take fibers in tension into consideration\n\tif (!m_tension_only || (In - 1 >= eps))\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F * n0 / sqrt(In);\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\n\t\t// calculate the fiber stress magnitude\n\t\tdouble sn = 2 * In * ksi * beta * pow(In - 1, beta - 1);\n\n\t\t// calculate the fiber stress\n\t\ts = N * (sn / J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\n\treturn s;\n}\n\ntens4ds FEFiberPow::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// initialize material constants\n\tdouble ksi = m_ksi(mp);\n\tdouble beta = m_beta(mp);\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\ttens4ds c;\n\n\t// Calculate In\n\tdouble In = n0 * (C * n0);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\n\tif (!m_tension_only || (In >= 1 + eps))\n\t{\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F * n0 / sqrt(In);\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n\n\t\t// calculate modulus\n\t\tdouble cn = 4 * In * In * ksi * beta * (beta - 1) * pow(In - 1, beta - 2);\n\n\t\t// calculate the fiber tangent\n\t\tc = NxN * (cn / J);\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\n\treturn c;\n}\n\ndouble FEFiberPow::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tdouble sed = 0.0;\n\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// initialize material constants\n\tdouble ksi = m_ksi(mp);\n\tdouble beta = m_beta(mp);\n\n\t// loop over all integration points\n\tconst double eps = 0;\n\tmat3ds C = pt.RightCauchyGreen();\n\n\t// Calculate In = n0*C*n0\n\tdouble In = n0 * (C * n0);\n\n\t// only take fibers in tension into consideration\n\tif (!m_tension_only || (In - 1 >= eps))\n\t{\n\t\t// calculate strain energy density\n\t\tsed = ksi * pow(In - 1, beta);\n\t}\n\n\treturn sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberPow, FEElasticFiberMaterial)\n\tADD_PARAMETER(m_fib.m_ksi, FE_RANGE_GREATER(0.0), \"ksi\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_beta, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_fib.m_tension_only, \"tension_only\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberPow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n\n//! Material class for single fiber, tension only\n//! Power law\n\nclass FEFiberPow : public FEFiberMaterial\n{\npublic:\n\tFEFiberPow(FEModel* pfem);\n\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\npublic:\n\tFEParamDouble   m_ksi;\t\t// fiber modulus\n\tFEParamDouble   m_beta;     // power law exponent in toe region\n\tdouble\tm_epsf;\n\tbool\tm_tension_only;\n};\n\nclass FEElasticFiberPow : public FEElasticFiberMaterial_T<FEFiberPow>\n{\npublic:\n\tFEElasticFiberPow(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberPow>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberPowLinear.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FEFiberPowLinear.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberPowLinear, FEFiberMaterial)\n    ADD_PARAMETER(m_E    , FE_RANGE_GREATER(0.0), \"E\"    )->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus E\");\n    ADD_PARAMETER(m_lam0 , FE_RANGE_GREATER(1.0), \"lam0\" )->setLongName(\"toe stretch ratio\");\n    ADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" )->setLongName(\"toe power exponent\");\n\tADD_PARAMETER(m_tension_only, \"tension_only\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEFiberPowLinear\n//-----------------------------------------------------------------------------\n\nFEFiberPowLinear::FEFiberPowLinear(FEModel* pfem) : FEFiberMaterial(pfem)\n{\n    m_E = 0.0;\n    m_lam0 = 1.0;\n    m_beta = 2.0;\n\tm_epsf = 0.0;\n\tm_tension_only = true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberPowLinear::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // initialize material constants\n    double E = m_E(mp);\n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n    double I0 = lam0*lam0;\n    double ksi = E/4/(beta-1)*pow(I0, -3./2.)*pow(I0-1, 2-beta);\n    double b = ksi*pow(I0-1, beta-1) + E/2/sqrt(I0);\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    mat3ds s;\n    \n    // Calculate In\n    double In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n    if (!m_tension_only || (In - 1 >= eps))\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0/sqrt(In);\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate the fiber stress magnitude\n        double sn = (In < I0) ?\n        2*In*ksi*pow(In-1, beta-1) :\n        2*b*In - E*sqrt(In);\n        \n        // calculate the fiber stress\n        s = N*(sn/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberPowLinear::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // initialize material constants\n    double E = m_E(mp);\n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n    double I0 = lam0*lam0;\n    double ksi = E/4/(beta-1)*pow(I0, -3./2.)*pow(I0-1, 2-beta);\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    mat3ds C = pt.RightCauchyGreen();\n    tens4ds c;\n    \n    // Calculate In\n    double In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\n    if (!m_tension_only || (In >= 1 + eps))\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0/sqrt(In);\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // calculate modulus\n        double cn = (In < I0) ?\n        4*In*In*ksi*(beta-1)*pow(In-1, beta-2) :\n        E*sqrt(In);\n        \n        // calculate the fiber tangent\n        c = NxN*(cn/J);\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberPowLinear::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    double sed = 0.0;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // initialize material constants\n    double E = m_E(mp);\n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n    double I0 = lam0*lam0;\n    double ksi = E/4/(beta-1)*pow(I0, -3./2.)*pow(I0-1, 2-beta);\n    double b = ksi*pow(I0-1, beta-1) + E/2/sqrt(I0);\n    \n    // loop over all integration points\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    \n    // Calculate In = n0*C*n0\n    double In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n    if (!m_tension_only || (In - 1 >= eps))\n    {\n        // calculate strain energy density\n        sed = (In < I0) ?\n        ksi/beta*pow(In-1, beta) :\n        b*(In-I0) - E*(sqrt(In)-sqrt(I0)) + ksi/beta*pow(I0-1, beta);\n    }\n    \n    return sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberPowLinear, FEElasticFiberMaterial)\n    ADD_PARAMETER(m_fib.m_E    , FE_RANGE_GREATER(0.0), \"E\"    )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_fib.m_lam0 , FE_RANGE_GREATER(1.0), \"lam0\" );\n    ADD_PARAMETER(m_fib.m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" );\n\tADD_PARAMETER(m_fib.m_tension_only, \"tension_only\");\nEND_FECORE_CLASS();\n\n\n//-----------------------------------------------------------------------------\n// FEFiberExpPowLinear\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExpPowLinear, FEFiberMaterial)\n\tADD_PARAMETER(m_E   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\");\n    ADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_lam0, FE_RANGE_GREATER(1.0), \"lam0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberExpPowLinear::FEFiberExpPowLinear(FEModel* pfem) : FEFiberMaterial(pfem)\n{\n\tm_E = 0;\n\tm_lam0 = 1;\n    m_alpha = 0;\n\tm_beta = 3;\n\tm_epsf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFiberExpPowLinear::Validate()\n{\n\tif (FEFiberMaterial::Validate() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberExpPowLinear::FiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds s;\n\n\t// Calculate In\n\tdouble In = n0*(C*n0);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = 0;\n\tif (In - 1 >= eps)\n\t{\n        // initialize material constants\n        double E = m_E(mp);\n        double lam0 = m_lam0(mp);\n        double alpha = m_alpha(mp);\n        double beta = m_beta(mp);\n        double I0 = lam0*lam0;\n        double ksi = E*pow(I0-1,2-beta)*exp(-alpha*pow(I0-1,beta))\n        /(4*pow(I0,1.5)*(beta-1+alpha*beta*pow(I0-1,beta)));\n        double b = E*(2*I0*(beta-0.5+alpha*beta*pow(I0-1,beta))-1)\n        /(4*pow(I0,1.5)*(beta-1+alpha*beta*pow(I0-1,beta)));\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0 / sqrt(In);\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\n\t\t// calculate the fiber stress magnitude\n\t\tdouble sn = (In < I0) ?\n\t\t\t2 * In*exp(alpha*pow(In-1,beta))*ksi*pow(In - 1, beta - 1) :\n\t\t\t2 * b*In - E*sqrt(In);\n\n\t\t// calculate the fiber stress\n\t\ts = N*(sn / J);\n\t}\n\telse\n\t{\n\t\ts.zero();\n\t}\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberExpPowLinear::FiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds C = pt.RightCauchyGreen();\n\ttens4ds c;\n\n\t// Calculate In\n\tdouble In = n0*(C*n0);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = m_epsf * std::numeric_limits<double>::epsilon();\n\tif (In >= 1 + eps)\n\t{\n        // initialize material constants\n        double E = m_E(mp);\n        double lam0 = m_lam0(mp);\n        double alpha = m_alpha(mp);\n        double beta = m_beta(mp);\n        double I0 = lam0*lam0;\n        double ksi = E*pow(I0-1,2-beta)*exp(-alpha*pow(I0-1,beta))\n        /(4*pow(I0,1.5)*(beta-1+alpha*beta*pow(I0-1,beta)));\n        double b = E*(2*I0*(beta-0.5+alpha*beta*pow(I0-1,beta))-1)\n        /(4*pow(I0,1.5)*(beta-1+alpha*beta*pow(I0-1,beta)));\n\n\t\t// get the global spatial fiber direction in current configuration\n\t\tvec3d nt = F*n0 / sqrt(In);\n\n\t\t// calculate the outer product of nt\n\t\tmat3ds N = dyad(nt);\n\t\ttens4ds NxN = dyad1s(N);\n\n\t\t// calculate modulus\n\t\tdouble cn = (In < I0) ?\n\t\t\t4*ksi*In*In*exp(alpha*pow(In-1,beta))*pow(In-1,beta-2)*(beta-1+alpha*beta*pow(In-1,beta)) :\n\t\t\tE*sqrt(In);\n\n\t\t// calculate the fiber tangent\n\t\tc = NxN*(cn / J);\n\t}\n\telse\n\t{\n\t\tc.zero();\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density\ndouble FEFiberExpPowLinear::FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n\tdouble sed = 0.0;\n\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// loop over all integration points\n\tmat3ds C = pt.RightCauchyGreen();\n\n\t// Calculate In = n0*C*n0\n\tdouble In = n0*(C*n0);\n\n\t// only take fibers in tension into consideration\n\tconst double eps = 0;\n\tif (In - 1 >= eps)\n\t{\n        // initialize material constants\n        double E = m_E(mp);\n        double lam0 = m_lam0(mp);\n        double alpha = m_alpha(mp);\n        double beta = m_beta(mp);\n        double I0 = lam0*lam0;\n        double ksi = E*pow(I0-1,2-beta)*exp(-alpha*pow(I0-1,beta))\n        /(4*pow(I0,1.5)*(beta-1+alpha*beta*pow(I0-1,beta)));\n        double b = E*(2*I0*(beta-0.5+alpha*beta*pow(I0-1,beta))-1)\n        /(4*pow(I0,1.5)*(beta-1+alpha*beta*pow(I0-1,beta)));\n\n\t\t// calculate strain energy density\n        if (alpha == 0) {\n\t\tsed = (In < I0) ?\n\t\t\tksi / beta*pow(In - 1, beta) :\n\t\t\tb*(In - I0) - E*(sqrt(In) - sqrt(I0)) + ksi / beta*pow(I0 - 1, beta);\n        }\n        else {\n            sed = (In < I0) ?\n            ksi / (alpha*beta)*(exp(alpha*pow(In - 1, beta))-1) :\n            b*(In - I0) - E*(sqrt(In) - sqrt(I0)) + ksi / (alpha*beta)*(exp(alpha*pow(I0 - 1, beta))-1);\n        }\n\t}\n\n\treturn sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticFiberExpPowLinear, FEElasticFiberMaterial)\n\tADD_PARAMETER(m_fib.m_E   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\");\n    ADD_PARAMETER(m_fib.m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_fib.m_beta, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_fib.m_lam0, FE_RANGE_GREATER(1.0), \"lam0\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberPowLinear.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Power law - linear\n\nclass FEFiberPowLinear : public FEFiberMaterial\n{\npublic:\n    FEFiberPowLinear(FEModel* pfem);\n    \n    //! Cauchy stress\n    mat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // Spatial tangent\n    tens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    //! Strain energy density\n    double FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n    \npublic:\n    FEParamDouble   m_E;\t\t// fiber modulus\n    FEParamDouble   m_lam0;     // stretch ratio at end of toe region\n    FEParamDouble   m_beta;     // power law exponent in toe region\n\tdouble\tm_epsf;\n\tbool\tm_tension_only;\n};\n\nclass FEElasticFiberPowLinear : public FEElasticFiberMaterial_T<FEFiberPowLinear>\n{\npublic:\n\tFEElasticFiberPowLinear(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberPowLinear>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Exponential-Power law toe region - linear\nclass FEFiberExpPowLinear : public FEFiberMaterial\n{\npublic:\n\tFEFiberExpPowLinear(FEModel* pfem);\n\n\t//! Initialization\n\tbool Validate() override;\n\n\t//! Cauchy stress\n\tmat3ds FiberStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t// Spatial tangent\n\ttens4ds FiberTangent(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! Strain energy density\n\tdouble FiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0) override;\n\npublic:\n    FEParamDouble   m_E;\t\t// fiber modulus\n    FEParamDouble   m_lam0;     // stretch ratio at end of toe region\n    FEParamDouble   m_alpha;    // exponential coefficient\n    FEParamDouble   m_beta;     // power law exponent in toe region\n\tdouble\tm_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEElasticFiberExpPowLinear : public FEElasticFiberMaterial_T<FEFiberExpPowLinear>\n{\npublic:\n\tFEElasticFiberExpPowLinear(FEModel* fem) : FEElasticFiberMaterial_T<FEFiberExpPowLinear>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFiberPowLinearUncoupled.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberPowLinearUncoupled.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberPowLinearUC, FEFiberMaterialUncoupled)\n\tADD_PARAMETER(m_E    , FE_RANGE_GREATER(0.0), \"E\"    )->setUnits(UNIT_PRESSURE)->setLongName(\"fiber modulus\");\n\tADD_PARAMETER(m_lam0 , FE_RANGE_GREATER(1.0), \"lam0\" )->setLongName(\"toe stretch ratio\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" )->setLongName(\"toe power exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberPowLinearUC::FEFiberPowLinearUC(FEModel* pfem) : FEFiberMaterialUncoupled(pfem)\n{ \n    m_E = 0.0;\n    m_lam0 = 1.0;\n    m_beta = 2.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberPowLinearUC::DevFiberStress(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F*pow(J, -1./3.);\n\n    // loop over all integration points\n    mat3ds C = pt.DevRightCauchyGreen();\n    mat3ds s;\n    \n    // Calculate In\n    double In = n0*(C*n0);\n    \n    // initialize material constants\n    double E = m_E(mp);\n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n\tdouble I0 = lam0*lam0;\n\tdouble ksi = E / 4.0 / (beta - 1)*pow(I0, -3.0 / 2.0)*pow(I0 - 1.0, 2.0 - beta);\n\tdouble b = ksi*pow(I0 - 1.0, beta - 1.0) + E / 2.0 / sqrt(I0);\n\n    // only take fibers in tension into consideration\n\tconst double eps = 0;\n\tif (In - 1 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0/sqrt(In);\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate the fiber stress magnitude\n        double sn = (In < I0) ?\n        2*In*ksi*pow(In-1, beta-1) :\n        2*b*In - E*sqrt(In);\n        \n        // calculate the fiber stress\n        s = N*(sn/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberPowLinearUC::DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F*pow(J, -1./3.);\n    \n    // loop over all integration points\n    const double eps = 0;\n    mat3ds C = pt.DevRightCauchyGreen();\n    mat3ds s;\n    tens4ds c;\n    \n    // Calculate In\n    double In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n    if (In - 1 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        vec3d nt = F*n0/sqrt(In);\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // initialize material constants\n        double E = m_E(mp);\n        double lam0 = m_lam0(mp);\n        double beta = m_beta(mp);\n        double I0 = lam0*lam0;\n        double ksi = E / 4.0 / (beta - 1)*pow(I0, -3.0 / 2.0)*pow(I0 - 1.0, 2.0 - beta);\n        double b = ksi*pow(I0 - 1.0, beta - 1.0) + E / 2.0 / sqrt(I0);\n\n        // calculate the fiber stress magnitude\n        double sn = (In < I0) ?\n        2*In*ksi*pow(In-1, beta-1) :\n        2*b*In - E*sqrt(In);\n        \n        // calculate the fiber stress\n        s = N*(sn/J);\n        \n        // calculate modulus\n        double cn = (In < I0) ? 4*In*In*ksi*(beta-1)*pow(In-1, beta-2) : E*sqrt(In);\n        \n        // calculate the fiber tangent\n        c = NxN*(cn/J);\n\n        // This is the final value of the elasticity tensor\n        mat3dd I(1);\n        tens4ds IxI = dyad1s(I);\n        tens4ds I4  = dyad4s(I);\n        c += ((I4+IxI/3.0)*s.tr() - dyad1s(I,s))*(2./3.)\n        - (ddots(IxI, c)-IxI*(c.tr()/3.))/3.;\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberPowLinearUC::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // loop over all integration points\n    const double eps = 0;\n    mat3ds C = pt.DevRightCauchyGreen();\n    \n    // Calculate In = n0*C*n0\n    double In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n\tdouble sed = 0.0;\n\tif (In - 1 >= eps)\n    {\n        // initialize material constants\n        double E = m_E(mp);\n        double lam0 = m_lam0(mp);\n        double beta = m_beta(mp);\n        double I0 = lam0*lam0;\n        double ksi = E / 4.0 / (beta - 1)*pow(I0, -3.0 / 2.0)*pow(I0 - 1.0, 2.0 - beta);\n        double b = ksi*pow(I0 - 1.0, beta - 1.0) + E / 2.0 / sqrt(I0);\n\n        // calculate strain energy density\n        sed = (In < I0) ?\n        ksi/beta*pow(In-1, beta) :\n        b*(In-I0) - E*(sqrt(In) - sqrt(I0)) + ksi/beta*pow(I0-1, beta);\n    }\n    \n    return sed;\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledFiberPowLinear, FEElasticFiberMaterialUC)\n\tADD_PARAMETER(m_fib.m_E    , FE_RANGE_GREATER(0.0), \"E\"    )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_lam0 , FE_RANGE_GREATER(1.0), \"lam0\" );\n\tADD_PARAMETER(m_fib.m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" );\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEFiberPowLinearUncoupled.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Power-law linear response (uncoupled)\n\nclass FEFiberPowLinearUC : public FEFiberMaterialUncoupled\n{\npublic:\n    FEFiberPowLinearUC(FEModel* pfem);\n   \n    //! Cauchy stress\n    mat3ds DevFiberStress(FEMaterialPoint& mp, const vec3d& n0) override;\n    \n    // Spatial tangent\n    tens4ds DevFiberTangent(FEMaterialPoint& mp, const vec3d& n0) override;\n    \n    //! Strain energy density\n    double DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& n0) override;\n    \npublic:\n    FEParamDouble   m_E;\t\t// fiber modulus\n    FEParamDouble   m_lam0;     // stretch ratio at end of toe region\n    FEParamDouble   m_beta;     // power law exponent in toe region\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEUncoupledFiberPowLinear : public FEElasticFiberMaterialUC_T<FEFiberPowLinearUC>\n{\npublic:\n    FEUncoupledFiberPowLinear(FEModel* fem) : FEElasticFiberMaterialUC_T<FEFiberPowLinearUC>(fem) {}\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFixedDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedDisplacement.h\"\n\nBEGIN_FECORE_CLASS(FEFixedDisplacement, FEFixedBC)\n\tADD_PARAMETER(m_dofx, \"x_dof\")->setLongName(\"x-displacement\");\n\tADD_PARAMETER(m_dofy, \"y_dof\")->setLongName(\"y-displacement\");\n\tADD_PARAMETER(m_dofz, \"z_dof\")->setLongName(\"z-displacement\");\nEND_FECORE_CLASS();\n\nFEFixedDisplacement::FEFixedDisplacement(FEModel* fem) : FEFixedBC(fem)\n{\n\tm_dofx = false;\n\tm_dofy = false;\n\tm_dofz = false;\n}\n\nbool FEFixedDisplacement::Init()\n{\n\tvector<int> dofs;\n\tif (m_dofx) dofs.push_back(GetDOFIndex(\"x\"));\n\tif (m_dofy) dofs.push_back(GetDOFIndex(\"y\"));\n\tif (m_dofz) dofs.push_back(GetDOFIndex(\"z\"));\n\n\tSetDOFList(dofs);\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioMech/FEFixedDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedDisplacement : public FEFixedBC\n{\npublic:\n\tFEFixedDisplacement(FEModel* fem);\n\n\tbool Init() override;\n\nprivate:\n\tbool\tm_dofx, m_dofy, m_dofz;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFixedNormalDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFixedNormalDisplacement.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFixedNormalDisplacement, FESurfaceConstraint)\n    ADD_PARAMETER(m_lc.m_laugon, \"laugon\");\n    ADD_PARAMETER(m_lc.m_tol, \"tol\");\n    ADD_PARAMETER(m_lc.m_eps, \"penalty\");\n    ADD_PARAMETER(m_lc.m_rhs, \"rhs\");\n    ADD_PARAMETER(m_lc.m_naugmin, \"minaug\");\n    ADD_PARAMETER(m_lc.m_naugmax, \"maxaug\");\n\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFixedNormalDisplacement::FEFixedNormalDisplacement(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem), m_lc(pfem)\n{\n    m_binit = false;\n    m_bshellb = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures.\nvoid FEFixedNormalDisplacement::Activate()\n{\n    // don't forget to call base class\n    FESurfaceConstraint::Activate();\n\n    if (m_binit == false)\n    {\n        FEModel& fem = *GetFEModel();\n        DOFS& dofs = fem.GetDOFS();\n\n        // evaluate the nodal normals\n        m_surf.UpdateNodeNormals();\n\n        // create linear constraints\n        // for a symmetry plane the constraint on (ux, uy, uz) is\n        // nx*ux + ny*uy + nz*uz = 0\n        if (m_bshellb == false) {\n            for (int i = 0; i < m_surf.Nodes(); ++i) {\n                FENode& node = m_surf.Node(i);\n                if ((node.HasFlags(FENode::EXCLUDE) == false) && (node.m_rid == -1)) {\n\t\t\t\t\tint nid = m_surf.NodeIndex(i) + 1; // NOTE: the linear constraint set still assumes 1-based indexing!\n                    vec3d nn = m_surf.NodeNormal(i);\n                    FEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, &fem);\n                    pLC->AddDOF(nid, dofs.GetDOF(\"x\"), nn.x);\n                    pLC->AddDOF(nid, dofs.GetDOF(\"y\"), nn.y);\n                    pLC->AddDOF(nid, dofs.GetDOF(\"z\"), nn.z);\n                    // add the linear constraint to the system\n                    m_lc.add(pLC);\n                }\n            }\n        }\n        else {\n            for (int i = 0; i < m_surf.Nodes(); ++i) {\n                FENode& node = m_surf.Node(i);\n                if ((node.HasFlags(FENode::EXCLUDE) == false) && (node.m_rid == -1)) {\n                    vec3d nn = m_surf.NodeNormal(i);\n\t\t\t\t\tint nid = m_surf.NodeIndex(i) + 1; // NOTE: the linear constraint set still assumes 1-based indexing!\n\t\t\t\t\tFEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, &fem);\n                    pLC->AddDOF(nid, dofs.GetDOF(\"sx\"), nn.x);\n                    pLC->AddDOF(nid, dofs.GetDOF(\"sy\"), nn.y);\n                    pLC->AddDOF(nid, dofs.GetDOF(\"sz\"), nn.z);\n                    // add the linear constraint to the system\n                    m_lc.add(pLC);\n                }\n            }\n        }\n\n        m_lc.Init();\n        m_lc.Activate();\n        m_binit = true;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEFixedNormalDisplacement::Init()\n{\n    // initialize surface\n    return m_surf.Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFixedNormalDisplacement::Serialize(DumpStream& ar) { m_lc.Serialize(ar); }\nvoid FEFixedNormalDisplacement::LoadVector(FEGlobalVector& R, const FETimeInfo& tp) { m_lc.LoadVector(R, tp); }\nvoid FEFixedNormalDisplacement::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) { m_lc.StiffnessMatrix(LS, tp); }\nbool FEFixedNormalDisplacement::Augment(int naug, const FETimeInfo& tp) { return m_lc.Augment(naug, tp); }\nvoid FEFixedNormalDisplacement::BuildMatrixProfile(FEGlobalMatrix& M) { m_lc.BuildMatrixProfile(M); }\nint FEFixedNormalDisplacement::InitEquations(int neq) { return m_lc.InitEquations(neq); }\nvoid FEFixedNormalDisplacement::Update(const std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.Update(Ui, ui); }\nvoid FEFixedNormalDisplacement::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.UpdateIncrements(Ui, ui); }\nvoid FEFixedNormalDisplacement::PrepStep() { m_lc.PrepStep(); }\n"
  },
  {
    "path": "FEBioMech/FEFixedNormalDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FESurface.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! The FEFixedNormalDisplacement class implements a linear constraint for fixing the normal component\n//! of the solid displacement.\n\nclass FEFixedNormalDisplacement : public FESurfaceConstraint\n{\npublic:\n    //! constructor\n    FEFixedNormalDisplacement(FEModel* pfem);\n    \n    //! destructor\n    ~FEFixedNormalDisplacement() {}\n    \n    //! Activation\n    void Activate() override;\n    \n    //! initialization\n    bool Init() override;\n\n    // allocate equations\n    int InitEquations(int neq) override;\n\n\t// we don't use nodal integration for this constraint\n\tbool UseNodalIntegration() { return false; };\n    \nprotected:\n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \npublic:\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! add the linear constraint contributions to the residual\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n    //! add the linear constraint contributions to the stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n    //! do the augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n\n    //! build connectivity for matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n    \n    //! Get the surface\n    FESurface* GetSurface() override { return &m_surf; }\n    \nprotected:\n    FESurface    m_surf;\n    bool        m_binit;\n    \npublic:\n    bool            m_bshellb;\n\nprivate:\n    FELinearConstraintSet   m_lc;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFixedRotation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedRotation.h\"\n\nBEGIN_FECORE_CLASS(FEFixedRotation, FEFixedBC)\n\tADD_PARAMETER(m_dof[0], \"u_dof\")->setLongName(\"x-rotation\");\n\tADD_PARAMETER(m_dof[1], \"v_dof\")->setLongName(\"y-rotation\");\n\tADD_PARAMETER(m_dof[2], \"w_dof\")->setLongName(\"z-rotation\");\nEND_FECORE_CLASS();\n\nFEFixedRotation::FEFixedRotation(FEModel* fem) : FEFixedBC(fem)\n{\n\tm_dof[0] = false;\n\tm_dof[1] = false;\n\tm_dof[2] = false;\n}\n\nbool FEFixedRotation::Init()\n{\n\tvector<int> dofs;\n\tif (m_dof[0]) dofs.push_back(GetDOFIndex(\"u\"));\n\tif (m_dof[1]) dofs.push_back(GetDOFIndex(\"v\"));\n\tif (m_dof[2]) dofs.push_back(GetDOFIndex(\"w\"));\n\tSetDOFList(dofs);\n\n\treturn FEFixedBC::Init();\n}\n\n"
  },
  {
    "path": "FEBioMech/FEFixedRotation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedRotation : public FEFixedBC\n{\npublic:\n\tFEFixedRotation(FEModel* fem);\n\n\tbool Init() override;\n\nprivate:\n\tbool\tm_dof[3];\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFixedShellDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedShellDisplacement.h\"\n\nBEGIN_FECORE_CLASS(FEFixedShellDisplacement, FEFixedBC)\n\tADD_PARAMETER(m_dof_sx, \"sx_dof\")->setLongName(\"sx-displacement\");\n\tADD_PARAMETER(m_dof_sy, \"sy_dof\")->setLongName(\"sy-displacement\");\n\tADD_PARAMETER(m_dof_sz, \"sz_dof\")->setLongName(\"sz-displacement\");\nEND_FECORE_CLASS();\n\nFEFixedShellDisplacement::FEFixedShellDisplacement(FEModel* fem) : FEFixedBC(fem)\n{\n\tm_dof_sx = false;\n\tm_dof_sy = false;\n\tm_dof_sz = false;\n}\n\nbool FEFixedShellDisplacement::Init()\n{\n\tvector<int> dofs;\n\tif (m_dof_sx) dofs.push_back(GetDOFIndex(\"sx\"));\n\tif (m_dof_sy) dofs.push_back(GetDOFIndex(\"sy\"));\n\tif (m_dof_sz) dofs.push_back(GetDOFIndex(\"sz\"));\n\n\tSetDOFList(dofs);\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioMech/FEFixedShellDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n\nclass FEFixedShellDisplacement : public FEFixedBC\n{\npublic:\n\tFEFixedShellDisplacement(FEModel* fem);\n\n\tbool Init() override;\n\nprivate:\n\tbool\tm_dof_sx, m_dof_sy, m_dof_sz;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEForceVelocityContraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEForceVelocityContraction.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nFEForceVelocityMaterialPoint::FEForceVelocityMaterialPoint()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEForceVelocityMaterialPoint::Copy()\n{\n    FEForceVelocityMaterialPoint* pt = new FEForceVelocityMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEForceVelocityMaterialPoint::Init()\n{\n\tFEMaterialPointData::Init();\n    \n    m_lambdap = 1;\n    \n    for (int i=0; i<MAX_TERMS; ++i) {\n        m_H[i] = 0;\n        m_Hp[i] = 0;\n    };\n}\n\n//-----------------------------------------------------------------------------\nvoid FEForceVelocityMaterialPoint::Serialize(DumpStream& ar)\n{\n    \n    if (ar.IsSaving())\n    {\n        ar << m_lambdap;\n        for (int i=0; i<MAX_TERMS; ++i) ar << m_H[i] << m_Hp[i];\n    }\n    else\n    {\n        ar >> m_lambdap;\n        for (int i=0; i<MAX_TERMS; ++i) ar >> m_H[i] >> m_Hp[i];\n    }\n    \n\tFEMaterialPointData::Serialize(ar);\n}\n\n//////////////////////////////////////////////////////////////////////\n// FEForceVelocityContraction\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEForceVelocityContraction, FEActiveContractionMaterial);\n    ADD_PARAMETER(m_ascl , \"ascl\");\n    ADD_PARAMETER(m_Tmax , \"Tmax\");\n    ADD_PARAMETER(m_ca0  , \"ca0\");\n    ADD_PARAMETER(m_camax, \"camax\");\n    ADD_PARAMETER(m_beta , \"beta\");\n    ADD_PARAMETER(m_l0   , \"l0\");\n    ADD_PARAMETER(m_refl , \"refl\");\n    ADD_PARAMETER(m_alpha[0] , \"alpha1\");\n    ADD_PARAMETER(m_alpha[1] , \"alpha2\");\n    ADD_PARAMETER(m_alpha[2] , \"alpha3\");\n    ADD_PARAMETER(m_A[0] , \"A1\");\n    ADD_PARAMETER(m_A[1] , \"A2\");\n    ADD_PARAMETER(m_A[2] , \"A3\");\n    ADD_PARAMETER(m_at   , \"a_t\");\n    ADD_PARAMETER(m_bfvel, \"force_velocity\" );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEForceVelocityContraction::FEForceVelocityContraction(FEModel* pfem) : FEActiveContractionMaterial(pfem)\n{\n    m_ascl = 0;\n    m_Tmax = 1.0;\n    m_ca0 = 1.0;\n    m_camax = 0.0;\n    m_l0 = m_refl = 0;\n    m_beta = 0;\n    m_A[0] = m_A[1] = m_A[2] = 0;\n    m_alpha[0] = m_alpha[1] = m_alpha[2] = 0;\n    m_at = 0;\n    m_bfvel = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEForceVelocityContraction::Init()\n{\n    if (FEActiveContractionMaterial::Init() == false) return false;\n    \n    // for backward compatibility we set m_camax to m_ca0 if it is not defined\n    if (m_camax == 0.0) m_camax = m_ca0;\n    if (m_camax <= 0.0) { feLogError(\"camax must be larger than zero\"); return false; }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEForceVelocityContraction::ActiveStress(FEMaterialPoint& mp, const vec3d& a0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the deformation gradient\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    double Jm13 = pow(J, -1.0 / 3.0);\n    \n    // calculate the current material axis lam*a = F*a0;\n    vec3d a = F*a0;\n    \n    double lam, lamd;\n    lam = a.unit();\n    lamd = lam*Jm13; // i.e. lambda tilde\n    \n    mat3ds AxA = dyad(a);\n    \n    // get the activation\n    double saf = 0.0;\n    double FVstress = 0.0;\n    double dt = GetTimeInfo().timeIncrement;\n    if (m_ascl > 0)\n    {\n        double ctenslm = m_ascl;\n        \n        // current sarcomere length\n        double strl = m_refl*lamd;\n        \n        // sarcomere length change\n        double dl = strl - m_l0;\n        \n        if (dl >= 0)\n        {\n            // calcium sensitivity\n            double eca50i = (exp(m_beta*dl) - 1);\n            \n            // ratio of Camax/Ca0\n            double rca = m_camax/m_ca0;\n            \n            // active fiber stress\n            saf = m_Tmax*(eca50i / ( eca50i + rca*rca ))*ctenslm;\n            \n            if (m_bfvel)\n            {\n                FEForceVelocityMaterialPoint& vpt = *mp.ExtractData<FEForceVelocityMaterialPoint>();\n                \n                double lambdap = vpt.m_lambdap;\n                \n                double Qac = 0;\n                double Qac2 = 0;\n                \n                // double dt = GetFEModel()->GetTime().timeIncrement;\n                // double dt = 1;\n                double g;\n                double h;\n                // double Hnew;\n                // double Hnew = 10;\n                \n                for (int i=0; i<MAX_TERMS; ++i)\n                {\n                    g = exp(-dt/m_alpha[i]);\n                    h = (1 - g)/(dt/m_alpha[i]);\n                    \n                    if ((lamd - lambdap) <= 0)\n                    {\n                        vpt.m_H[i] = vpt.m_Hp[i]*g + (lamd - lambdap)*h;\n                    }\n                    \n                    if ((lamd - lambdap) == 0)\n                    {\n                        vpt.m_H[i] = vpt.m_Hp[i]*g + (lamd - lambdap)*h;\n                    }\n                    \n                    if ((lamd - lambdap) > 0)\n                    {\n                        vpt.m_H[i] = vpt.m_Hp[i]*g; // POSITIVE VELOCITY SET TO 0\n                    }\n\n                    Qac += vpt.m_H[i]*m_A[i];\n                    Qac2 = abs(Qac);\n                }\n                \n                // return the total Cauchy stress,\n                // which is the push-forward of sarcomere\n                \n                // FVstress = saf*(1 - m_at*Qac2)/(1+Qac2); //NEGATIVE ABSOLUTE VALUE OF Q\n                FVstress = saf*(1 + m_at*Qac)/(1-Qac);\n            }\n            else\n            {\n                FVstress = saf;\n            }\n        }\n    }\n    return AxA*FVstress;\n\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEForceVelocityContraction::ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the deformation gradient\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    double Jm13 = pow(J, -1.0 / 3.0);\n    \n    // calculate the current material axis lam*a = F*a0;\n    vec3d a = F*a0;\n    \n    // normalize material axis and store fiber stretch\n    double lam, lamd;\n    lam = a.unit();\n    lamd = lam*Jm13; // i.e. lambda tilde\n    \n    // calculate dyad of a: AxA = (a x a)\n    mat3ds AxA = dyad(a);\n    tens4ds AxAxAxA = dyad1s(AxA);\n    \n    double c = 0;\n    if (m_ascl > 0)\n    {\n        // current sarcomere length\n        double strl = m_refl*lamd;\n        \n        // sarcomere length change\n        double dl = strl - m_l0;\n        \n        if (dl >= 0)\n        {\n            // calcium sensitivity\n            double eca50i = (exp(m_beta*dl) - 1);\n            \n            double decl = m_beta*m_refl*exp(m_beta*dl);\n            \n            // ratio of Camax/Ca0\n            double rca = m_camax / m_ca0;\n            \n            double d = eca50i + rca*rca;\n            \n            // active fiber stress\n            double saf = m_Tmax*(eca50i / d)*m_ascl;\n            \n            double dsf = m_Tmax*m_ascl*decl*(1.0/ d - eca50i / (d*d));\n            \n            c = (lamd*dsf - 2.0*saf);\n        }\n    }\n    \n    return AxAxAxA*c;\n}\n\n//-----------------------------------------------------------------------------\n// update force-velocity material point\nvoid FEForceVelocityContraction::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp, const vec3d& a0)\n{\n    FEForceVelocityMaterialPoint& pt = *mp.ExtractData<FEForceVelocityMaterialPoint>();\n    FEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n    double Jm13 = pow(pe.m_J, -1.0/3.0);\n    \n    vec3d a = pe.m_F*a0;\n    double lambda1 = a.unit();\n    pt.m_lambdap = lambda1*Jm13;\n    \n    for (int i=0; i<FEForceVelocityMaterialPoint::MAX_TERMS; ++i) {\n        pt.m_Hp[i] = pt.m_H[i];\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEForceVelocityContraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEActiveContractionMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEForceVelocityMaterialPoint : public FEMaterialPointData\n{\npublic:\n    enum { MAX_TERMS = 3 };\n    \npublic:\n    \n\tFEForceVelocityMaterialPoint();\n    \n\tFEMaterialPointData* Copy();\n    \n    void Init();\n    \n    void Serialize(DumpStream& ar);\n    \npublic:\n    \n    double m_lambdap;\n    \n    double m_H[MAX_TERMS];\n    double m_Hp[MAX_TERMS];\n    \n};\n\n//-----------------------------------------------------------------------------\n//! A material class describing the active fiber contraction model of Estrada et al. in doi: 10.1115/1.4044030\nclass FEForceVelocityContraction : public FEActiveContractionMaterial\n{\npublic:\n    enum { MAX_TERMS = 3 };\n    \npublic:\n    FEForceVelocityContraction(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculate the active stress\n    mat3ds ActiveStress(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    //! active contraction stiffness contribution\n    tens4ds ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0) override;\n    \n    //! create material point data\n    FEMaterialPointData* CreateMaterialPointData() override { return new FEForceVelocityMaterialPoint; }\n    \npublic:\n    //! update force-velocity material point\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp, const vec3d& a0) override;\n    \nprotected:\n    double  m_ascl;             //!< activation scale factor\n    double  m_Tmax;             //!< activation scale factor\n    double  m_ca0;              //!< intracellular calcium concentration\n    double  m_camax;            //!< peak calcium concentration\n    double  m_beta;             //!< shape of peak isometric tension-sarcomere length relation\n    double  m_l0;               //!< unloaded length\n    double  m_refl;             //!< sarcomere length\n    double  m_alpha[MAX_TERMS];\n    double  m_A[MAX_TERMS];\n    double  m_at;\n    bool    m_bfvel;            //!< flag for calculating force-velocity contraction response\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFungOrthoCompressible.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFungOrthoCompressible.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFungOrthoCompressible, FEElasticMaterial)\n\tADD_PARAMETER(E1, FE_RANGE_GREATER(0.0), \"E1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(E2, FE_RANGE_GREATER(0.0), \"E2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(E3, FE_RANGE_GREATER(0.0), \"E3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(G12, \"G12\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(G23, \"G23\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(G31, \"G31\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(v12, \"v12\");\n\tADD_PARAMETER(v23, \"v23\");\n\tADD_PARAMETER(v31, \"v31\");\n\tADD_PARAMETER(m_c, FE_RANGE_GREATER(0.0),\"c\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_k, FE_RANGE_GREATER(0.0), \"k\")->setUnits(UNIT_PRESSURE);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Data initialization\nbool FEFungOrthoCompressible::Validate()\n{\n\tif (FEElasticMaterial::Validate() == false) return false;\n\t\n\tif (v12 > sqrt(E1/E2)) { feLogError(\"Invalid value for v12. Let v12 > sqrt(E1/E2)\"); return false; }\n\tif (v23 > sqrt(E2/E3)) { feLogError(\"Invalid value for v23. Let v23 > sqrt(E2/E3)\"); return false; }\n\tif (v31 > sqrt(E3/E1)) { feLogError(\"Invalid value for v31. Let v31 > sqrt(E3/E1)\"); return false; }\n\n\t// Evaluate Lame coefficients\n\tmu[0] = G12 + G31 - G23;\n\tmu[1] = G12 - G31 + G23;\n\tmu[2] =-G12 + G31 + G23;\n\tlam[0][0] = 1.0/E1; lam[0][1] = -v12/E1; lam[0][2] = -v31/E3;\n\tlam[1][0] = -v12/E1; lam[1][1] = 1.0/E2; lam[1][2] = -v23/E2;\n\tlam[2][0] = -v31/E3; lam[2][1] = -v23/E2; lam[2][2] = 1.0/E3;\n\tmat3d c(lam);\n\tc = c.inverse();\n\tlam[0][0] = c[0][0] - 2*mu[0];\n\tlam[1][1] = c[1][1] - 2*mu[1];\n\tlam[2][2] = c[2][2] - 2*mu[2];\n\tlam[1][2] = c[1][2]; lam[2][1] = c[2][1];\n\tlam[2][0] = c[2][0]; lam[0][2] = c[0][2];\n\tlam[0][1] = c[0][1]; lam[1][0] = c[1][0];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the stress\nmat3ds FEFungOrthoCompressible::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tvec3d a[3];\t\t\t// texture direction in current configuration\n\tmat3ds A[3];\t\t// texture tensor in current configuration\n\tdouble K[3];\t\t// Ka\n\tdouble L[3];\t\t// La\n\tdouble eQ;\t\t\t// exp(Q)\n\tmat3ds bmi;\t\t\t// B - I\n\t// Evaluate the deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// calculate left and right Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tmat3ds c = pt.RightCauchyGreen();\n\tmat3ds c2 = c.sqr();\n\tmat3dd I(1.);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tK[i] = a0[i]*(c*a0[i]);\n\t\tL[i] = a0[i]*(c2*a0[i]);\n\t\ta[i] = F*a0[i]/sqrt(K[i]);\t// Evaluate the texture direction in the current configuration\n\t\tA[i] = dyad(a[i]);\t\t\t// Evaluate the texture tensor in the current configuration\n\t}\n\t\n\t// Evaluate exp(Q)\n\teQ = 0;\n\tfor (i=0; i<3; i++) {\n\t\teQ += 2*mu[i]*(L[i]-2*K[i]+1);\n\t\tfor (j=0; j<3; j++)\n\t\t\teQ += lam[i][j]*(K[i]-1)*(K[j]-1);\n\t}\n\teQ = exp(eQ/(4.*m_c));\n\t\n\t// Evaluate the stress\n\tmat3ds s;\n\ts.zero();\t\t// Initialize for summation\n\tbmi = b - I;\n\tfor (i=0; i<3; i++) {\n\t\ts += (A[i]*bmi).sym()*(2.0*mu[i] * K[i]);\n\t\tfor (j=0; j<3; j++)\n\t\t\ts += lam[i][j]*((K[i]-1)*K[j]*A[j]+(K[j]-1)*K[i]*A[i])/2.;\n\t}\n\ts *= eQ/(2.0*J);\n\t\n\ts += I*(m_k*log(J)/J);\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the tangent\ntens4ds FEFungOrthoCompressible::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tvec3d a[3];\t\t\t// texture direction in current configuration\n\tmat3ds A[3];\t\t// texture tensor in current configuration\n\tdouble K[3];\t\t// Ka\n\tdouble L[3];\t\t// La\n\tdouble eQ;\t\t\t// exp(Q)\n\tmat3ds bmi;\t\t\t// B - I\n\t// Evaluate the strain and texture\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// calculate left and right Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tmat3ds c = pt.RightCauchyGreen();\n\tmat3ds c2 = c.sqr();\n\tmat3dd I(1.);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tK[i] = a0[i]*(c*a0[i]);\n\t\tL[i] = a0[i]*(c2*a0[i]);\n\t\ta[i] = F*a0[i]/sqrt(K[i]);\t// Evaluate the texture direction in the current configuration\n\t\tA[i] = dyad(a[i]);\t\t\t// Evaluate the texture tensor in the current configuration\n\t}\n\t\n\t// Evaluate exp(Q)\n\teQ = 0;\n\tfor (i=0; i<3; i++) {\n\t\teQ += 2*mu[i]*(L[i]-2*K[i]+1);\n\t\tfor (j=0; j<3; j++)\n\t\t\teQ += lam[i][j]*(K[i]-1)*(K[j]-1);\n\t}\n\teQ = exp(eQ/(4.*m_c));\n\t\n\t// Evaluate the Cauchy stress\n\tmat3ds s;\n\ts.zero();\t\t// Initialize for summation\n\t//\ttens4ds C;\n\t//\tC.zero();\n\ttens4ds C(0.0);\n\tbmi = b - I;\n\tfor (i=0; i<3; i++) {\n\t\ts += (A[i]*bmi).sym()*(2.0*mu[i] * K[i]);\n\t\tfor (j=0; j<3; j++)\n\t\t\ts += lam[i][j]*((K[i]-1)*K[j]*A[j]+(K[j]-1)*K[i]*A[i])/2.;\n\t\tC += mu[i]*K[i]*dyad4s(A[i],b);\n\t\tfor (j=0; j<3; j++)\n\t\t\tC += lam[i][j]*K[i]*K[j]*dyad1s(A[i],A[j])/2.;\n\t}\n\ts *= eQ/(2.0*J);\n\t\n\t// Elasticity tensor\n\tC = (eQ/J)*C + (2*J/m_c/eQ)*dyad1s(s);\n\tC += (dyad1s(I) - dyad4s(I)*(2*log(J)))*(m_k/J);\n\t\n\treturn C;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FEFungOrthoCompressible::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tmat3ds A0[3];\t\t// texture tensor in current configuration\n\tdouble eQ = 0.0;\t// exp(Q)\n    double AE[3], AE2[3];\n\t\n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n    \n\t// calculate right Cauchy-Green tensor and Lagrange strain tensor\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3dd I(1.);\n    mat3ds E = (C - I)*0.5;\n    mat3ds E2 = E.sqr();\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tA0[i] = dyad(a0[i]);\t\t\t// Evaluate the texture tensor in the reference configuration\n        AE[i] = A0[i].dotdot(E);\n        AE2[i] = A0[i].dotdot(E2);\n\t}\n\t\n\t// Evaluate exp(Q)\n\tfor (i=0; i<3; i++) {\n\t\teQ += 2*mu[i]*AE2[i];\n\t\tfor (j=0; j<3; j++)\n\t\t\teQ += lam[i][j]*AE[i]*AE[j];\n\t}\n\teQ = exp(eQ/m_c);\n\t\n\t// Evaluate the strain energy density\n\tdouble sed = 0.5*(m_c*(eQ-1) + m_k*lnJ*lnJ);\n\t\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEFungOrthoCompressible.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\nclass FEFungOrthoCompressible : public FEElasticMaterial\n{\npublic:\n\tdouble\tE1, E2, E3;\t\t// Young's moduli\n\tdouble\tv12, v23, v31;\t// Poisson's ratio\n\tdouble\tG12, G23, G31;\t// Shear moduli\n\tdouble\tlam[3][3];\t\t// first Lame coefficients\n\tdouble\tmu[3];\t\t\t// second Lame coefficients\n\tdouble\tm_c;\t\t\t// c coefficient\n\tdouble\tm_k;\t\t\t// bulk modulus\n\t\npublic:\n\tFEFungOrthoCompressible(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\t\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization\n\tbool Validate() override;\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEFungOrthotropic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFungOrthotropic.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFungOrthotropic, FEUncoupledMaterial)\n\tADD_PARAMETER(E1, FE_RANGE_GREATER(0.0), \"E1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(E2, FE_RANGE_GREATER(0.0), \"E2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(E3, FE_RANGE_GREATER(0.0), \"E3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(G12, FE_RANGE_GREATER_OR_EQUAL(0.0), \"G12\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(G23, FE_RANGE_GREATER_OR_EQUAL(0.0), \"G23\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(G31, FE_RANGE_GREATER_OR_EQUAL(0.0), \"G31\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(v12, \"v12\");\n\tADD_PARAMETER(v23, \"v23\");\n\tADD_PARAMETER(v31, \"v31\");\n\tADD_PARAMETER(m_c, FE_RANGE_GREATER(0.0), \"c\")->setUnits(UNIT_PRESSURE);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFungOrthotropic::FEFungOrthotropic(FEModel* pfem) : FEUncoupledMaterial(pfem) \n{\n}\n\n//-----------------------------------------------------------------------------\n//! Data initialization\nbool FEFungOrthotropic::Validate()\n{\n\tif (FEUncoupledMaterial::Validate() == false) return false;\n\n\tif (v12 > sqrt(E1/E2)) { feLogError(\"Invalid value for v12. Let v12 <= sqrt(E1/E2)\"); return false; }\n\tif (v23 > sqrt(E2/E3)) { feLogError(\"Invalid value for v23. Let v23 <= sqrt(E2/E3)\"); return false; }\n\tif (v31 > sqrt(E3/E1)) { feLogError(\"Invalid value for v31. Let v31 <= sqrt(E3/E1)\"); return false; }\n\t\n\t// Evaluate Lame coefficients\n\tmu[0] = G12 + G31 - G23;\n\tmu[1] = G12 - G31 + G23;\n\tmu[2] =-G12 + G31 + G23;\n\tlam[0][0] = 1.0/E1; lam[0][1] = -v12/E1; lam[0][2] = -v31/E3;\n\tlam[1][0] = -v12/E1; lam[1][1] = 1.0/E2; lam[1][2] = -v23/E2;\n\tlam[2][0] = -v31/E3; lam[2][1] = -v23/E2; lam[2][2] = 1.0/E3;\n\tmat3d c(lam);\n\tc = c.inverse();\n\tlam[0][0] = c[0][0] - 2*mu[0];\n\tlam[1][1] = c[1][1] - 2*mu[1];\n\tlam[2][2] = c[2][2] - 2*mu[2];\n\tlam[1][2] = c[1][2]; lam[2][1] = c[2][1];\n\tlam[2][0] = c[2][0]; lam[0][2] = c[0][2];\n\tlam[0][1] = c[0][1]; lam[1][0] = c[1][0];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress\nmat3ds FEFungOrthotropic::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tvec3d a[3];\t\t\t// texture direction in current configuration\n\tmat3ds A[3];\t\t// texture tensor in current configuration\n\tdouble K[3];\t\t// Ka\n\tdouble L[3];\t\t// La\n\tdouble eQ;\t\t\t// exp(Q)\n\tmat3ds bmi;\t\t\t// B - I\n\t// Evaluate the deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\tmat3d Fd = F*pow(detF,-1./3.);\n\t\n\t// calculate deviatoric left and right Cauchy-Green tensor\n\tmat3ds b = pt.DevLeftCauchyGreen();\n\tmat3ds c = pt.DevRightCauchyGreen();\n\tmat3ds c2 = c.sqr();\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tK[i] = a0[i]*(c*a0[i]);\n\t\tL[i] = a0[i]*(c2*a0[i]);\n\t\ta[i] = Fd*a0[i]/sqrt(K[i]);\t// Evaluate the texture direction in the current configuration\n\t\tA[i] = dyad(a[i]);\t\t\t// Evaluate the texture tensor in the current configuration\n\t}\n\t\n\t// Evaluate exp(Q)\n\teQ = 0;\n\tfor (i=0; i<3; i++) {\n\t\teQ += 2*mu[i]*(L[i]-2*K[i]+1);\n\t\tfor (j=0; j<3; j++)\n\t\t\teQ += lam[i][j]*(K[i]-1)*(K[j]-1);\n\t}\n\teQ = exp(eQ/(4.*m_c));\n\t\n\t// Evaluate the stress\n\tmat3ds s;\n\ts.zero();\t\t// Initialize for summation\n\tbmi = b - mat3dd(1.);\n\tfor (i=0; i<3; i++) {\n\t\ts += (A[i]*bmi).sym()*(2.0*mu[i] * K[i]);\n\t\tfor (j=0; j<3; j++)\n\t\t\ts += lam[i][j]*((K[i]-1)*K[j]*A[j]+(K[j]-1)*K[i]*A[i])/2.;\n\t}\n\ts *= eQ/(2.0*detF);\n\n\treturn s.dev();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric tangent\ntens4ds FEFungOrthotropic::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tvec3d a[3];\t\t\t// texture direction in current configuration\n\tmat3ds A[3];\t\t// texture tensor in current configuration\n\tdouble K[3];\t\t// Ka\n\tdouble L[3];\t\t// La\n\tdouble eQ;\t\t\t// exp(Q)\n\tmat3ds bmi;\t\t\t// B - I\n\t// Evaluate the strain and texture\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\tmat3d Fd = F*pow(detF,-1./3.);\n\t\n\t// calculate left and right Cauchy-Green tensor\n\tmat3ds b = pt.DevLeftCauchyGreen();\n\tmat3ds c = pt.DevRightCauchyGreen();\n\tmat3ds c2 = c.sqr();\n\tmat3dd I(1.);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tK[i] = a0[i]*(c*a0[i]);\n\t\tL[i] = a0[i]*(c2*a0[i]);\n\t\ta[i] = Fd*a0[i]/sqrt(K[i]);\t// Evaluate the texture direction in the current configuration\n\t\tA[i] = dyad(a[i]);\t\t\t// Evaluate the texture tensor in the current configuration\n\t}\n\t\n\t// Evaluate exp(Q)\n\teQ = 0;\n\tfor (i=0; i<3; i++) {\n\t\teQ += 2*mu[i]*(L[i]-2*K[i]+1);\n\t\tfor (j=0; j<3; j++)\n\t\t\teQ += lam[i][j]*(K[i]-1)*(K[j]-1);\n\t}\n\teQ = exp(eQ/(4.*m_c));\n\t\n\t// Evaluate the distortional part of the Cauchy stress\n\tmat3ds sd;\n\tsd.zero();\t\t// Initialize for summation\n\ttens4ds C(0.0);\n\tbmi = b - I;\n\tfor (i=0; i<3; i++) {\n\t\tsd += ((A[i]*bmi).sym())*(2.0*mu[i] * K[i]);\n\t\tfor (j=0; j<3; j++)\n\t\t\tsd += lam[i][j]*((K[i]-1)*K[j]*A[j]+(K[j]-1)*K[i]*A[i])/2.;\n\t\tC += mu[i]*K[i]*dyad4s(A[i],b);\n\t\tfor (j=0; j<3; j++)\n\t\t\tC += lam[i][j]*K[i]*K[j]*dyad1s(A[i],A[j])/2.;\n\t}\n\tsd *= eQ/(2.0*detF);\n\t// This is the distortional part of the elasticity tensor\n\tC = (eQ/detF)*C + (2*detF/m_c/eQ)*dyad1s(sd);\n\t// This is the final value of the elasticity tensor\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\n\tC += - 1./3.*(ddots(C,IxI) - IxI*(C.tr()/3.))\n\t+ 2./3.*((I4-IxI/3.)*sd.tr()-dyad1s(sd.dev(),I));\n\t\n\treturn C;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FEFungOrthotropic::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tmat3ds A0[3];\t\t// texture tensor in current configuration\n\tdouble eQ = 0.0;\t// exp(Q)\n    double AE[3], AE2[3];\n\t\n\t// calculate right Cauchy-Green tensor and Lagrange strain tensor\n\tmat3ds C = pt.DevRightCauchyGreen();\n\tmat3dd I(1.);\n    mat3ds E = (C - I)*0.5;\n    mat3ds E2 = E.sqr();\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tA0[i] = dyad(a0[i]);\t\t\t// Evaluate the texture tensor in the reference configuration\n        AE[i] = A0[i].dotdot(E);\n        AE2[i] = A0[i].dotdot(E2);\n\t}\n\t\n\t// Evaluate exp(Q)\n\tfor (i=0; i<3; i++) {\n\t\teQ += 2*mu[i]*AE2[i];\n\t\tfor (j=0; j<3; j++)\n\t\t\teQ += lam[i][j]*AE[i]*AE[j];\n\t}\n\teQ = exp(eQ/m_c);\n\t\n\t// Evaluate the strain energy density\n\tdouble sed = 0.5*m_c*(eQ-1);\n\t\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEFungOrthotropic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\nclass FEFungOrthotropic : public FEUncoupledMaterial\n{\npublic:\n\tdouble\tE1, E2, E3;\t\t// Young's moduli\n\tdouble\tv12, v23, v31;\t// Poisson's ratio\n\tdouble\tG12, G23, G31;\t// Shear moduli\n\tdouble\tlam[3][3];\t\t// first Lame coefficients\n\tdouble\tmu[3];\t\t\t// second Lame coefficients\n\tdouble\tm_c;\t\t\t// c coefficient\n\t\t\npublic:\n\tFEFungOrthotropic(FEModel* pfem);\n\t\t\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization\n\tbool Validate() override;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGenericBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEGenericBodyForce.h\"\n#include \"FEElasticMaterial.h\"\n\nBEGIN_FECORE_CLASS(FEGenericBodyForce, FEBodyForce);\n\tADD_PARAMETER(m_force, \"force\")->setUnits(UNIT_SPECIFIC_FORCE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEGenericBodyForce::FEGenericBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEGenericBodyForce::force(FEMaterialPoint &mp)\n{\n\treturn m_force(mp);\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEGenericBodyForce::stiffness(FEMaterialPoint& pt)\n{\n\treturn mat3ds(0, 0, 0, 0, 0, 0);\n}\n\nvoid FEGenericBodyForce::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Nothing to do here.\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEConstBodyForceOld, FEBodyForce);\n\tADD_PARAMETER(m_f.x, \"x\");\n\tADD_PARAMETER(m_f.y, \"y\");\n\tADD_PARAMETER(m_f.z, \"z\");\nEND_FECORE_CLASS();\n\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FENonConstBodyForceOld, FEBodyForce);\n\tADD_PARAMETER(m_f[0], \"x\");\n\tADD_PARAMETER(m_f[1], \"y\");\n\tADD_PARAMETER(m_f[2], \"z\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENonConstBodyForceOld::FENonConstBodyForceOld(FEModel* pfem) : FEBodyForce(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nvec3d FENonConstBodyForceOld::force(FEMaterialPoint& pt)\n{\n\tvec3d F;\n\tF.x = m_f[0](pt);\n\tF.y = m_f[1](pt);\n\tF.z = m_f[2](pt);\n\treturn F;\n}\n"
  },
  {
    "path": "FEBioMech/FEGenericBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBodyForce.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! This class defines a non-homogeneous body force, i.e. the force can depend\n//! on the reference position\nclass FEGenericBodyForce : public FEBodyForce\n{\npublic:\n\t//! constructor\n\tFEGenericBodyForce(FEModel* pfem);\n\n\t//! evaluate the body force\n\tvec3d force(FEMaterialPoint& pt) override;\n\n\t//! evaluate the divergence of the body force\n\tdouble divforce(FEMaterialPoint& pt) override { return 0; }\n\n\t//! stiffness\n\tmat3d stiffness(FEMaterialPoint& pt) override;\n\nprivate:\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\npublic:\n\tFEParamVec3 m_force;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! This class defines a deformation-independent constant force (e.g. gravity)\n// NOTE: This class is obsolete!\nclass FEConstBodyForceOld : public FEBodyForce\n{\npublic:\n\tFEConstBodyForceOld(FEModel* pfem) : FEBodyForce(pfem) { m_f = vec3d(0, 0, 0); }\n\tvec3d force(FEMaterialPoint& pt) override { return m_f; }\n    double divforce(FEMaterialPoint& pt) override { return 0; }\n\tmat3d stiffness(FEMaterialPoint& pt) override { return mat3ds(0, 0, 0, 0, 0, 0); }\n\nprotected:\n\tvec3d\tm_f;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Class that implements old body force\n// NOTE: This class is obsolete!\nclass FENonConstBodyForceOld : public FEBodyForce\n{\npublic:\n\tFENonConstBodyForceOld(FEModel* fem);\n\tvec3d force(FEMaterialPoint& pt) override;\n    double divforce(FEMaterialPoint& pt) override { return 0; }\n\tmat3d stiffness(FEMaterialPoint& pt) override { return mat3ds(0, 0, 0, 0, 0, 0); }\n\nprivate:\n\tFEParamDouble\tm_f[3];\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGenericHyperelastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEGenericHyperelastic.h\"\n#include <FECore/MMath.h>\n#include <FECore/MObj2String.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEGenericHyperelastic, FEElasticMaterial)\n\tADD_PARAMETER(m_exp, \"W\")->setUnits(UNIT_ENERGY);\nEND_FECORE_CLASS();\n\nFEGenericHyperelastic::FEGenericHyperelastic(FEModel* fem) : FEElasticMaterial(fem)\n{\n}\n\nbool FEGenericHyperelastic::Init()\n{\n\tif (BuildMathExpressions() == false) return false;\n\treturn FEElasticMaterial::Init();\n}\n\nbool FEGenericHyperelastic::BuildMathExpressions()\n{\n\tvector<string> vars = { \"I1\", \"I2\", \"J\", \"X\", \"Y\", \"Z\"};\n\n\t// add all user parameters\n\tFEParameterList& pl = GetParameterList();\n\tFEParamIterator pi = pl.first();\n\tm_param.clear();\n\tfor (int i = 0; i < pl.Parameters(); ++i, ++pi)\n\t{\n\t\tFEParam& p = *pi;\n\t\tif (p.GetFlags() & FEParamFlag::FE_PARAM_USER)\n\t\t{\n\t\t\tvars.push_back(p.name());\n\t\t\tm_param.push_back((double*)p.data_ptr());\n\t\t}\n\t}\n\n\t// create math object\n\tm_W.AddVariables(vars);\n\tif (m_W.Create(m_exp) == false) return false;\n\n\t// calculate all derivatives\n    MITEM W1 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(0), 1));\n    MITEM W2 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(1), 1));\n    MITEM WJ = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(2), 1));\n\tm_W1.AddVariables(vars); m_W1.SetExpression(W1);\n\tm_W2.AddVariables(vars); m_W2.SetExpression(W2);\n\tm_WJ.AddVariables(vars); m_WJ.SetExpression(WJ);\n\n    MITEM W11 = MDerive(m_W1.GetExpression(), *m_W1.Variable(0), 1);\n    MITEM W12 = MDerive(m_W1.GetExpression(), *m_W1.Variable(1), 1);\n    MITEM W22 = MDerive(m_W2.GetExpression(), *m_W2.Variable(1), 1);\n\tm_W11.AddVariables(vars); m_W11.SetExpression(W11);\n\tm_W12.AddVariables(vars); m_W12.SetExpression(W12);\n\tm_W22.AddVariables(vars); m_W22.SetExpression(W22);\n\n    MITEM WJJ = MDerive(m_WJ.GetExpression(), *m_WJ.Variable(2), 1);\n\tm_WJJ.AddVariables(vars); m_WJJ.SetExpression(WJJ);\n\n#ifndef NDEBUG\n\tMObj2String o2s;\n\tstring sW1 = o2s.Convert(m_W1); feLog(\"W1  = %s\\n\", sW1.c_str());\n\tstring sW2 = o2s.Convert(m_W2); feLog(\"W2  = %s\\n\", sW2.c_str());\n\tstring sWJ = o2s.Convert(m_WJ); feLog(\"WJ  = %s\\n\", sWJ.c_str());\n\tstring sW11 = o2s.Convert(m_W11); feLog(\"W11 = %s\\n\", sW11.c_str());\n\tstring sW12 = o2s.Convert(m_W12); feLog(\"W12 = %s\\n\", sW12.c_str());\n\tstring sW22 = o2s.Convert(m_W22); feLog(\"W22 = %s\\n\", sW22.c_str());\n\tstring sWJJ = o2s.Convert(m_WJJ); feLog(\"WJJ = %s\\n\", sWJJ.c_str());\n#endif\n\n\treturn true;\n}\n\n\n// serialization\nvoid FEGenericHyperelastic::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tif ((ar.IsShallow() == false) && ar.IsLoading())\n\t{\n\t\tbool b = BuildMathExpressions();\n\t\tassert(b);\n\t}\n}\n\nmat3ds FEGenericHyperelastic::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\tvector<double> v = { I1, I2, J, mp.m_r0.x, mp.m_r0.y, mp.m_r0.z };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\tdouble WJ = m_WJ.value_s(v);\n\n\tmat3dd I(1.0);\n\n\tmat3ds s = (B*(W1 + W2*I1) - B2*W2)*(2.0/J) + WJ*I;\n\n\treturn s;\n}\n\ntens4ds FEGenericHyperelastic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\tvector<double> v = { I1, I2, J, mp.m_r0.x, mp.m_r0.y, mp.m_r0.z };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\tdouble WJ = m_WJ.value_s(v);\n\n\tdouble W11 = m_W11.value_s(v);\n\tdouble W22 = m_W22.value_s(v);\n\tdouble W12 = m_W12.value_s(v);\n\n\tdouble WJJ = m_WJJ.value_s(v);\n\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B2xB2 = dyad1s(B2);\n\n\ttens4ds BoB2 = dyad1s(B, B2);\n\n\ttens4ds Ib = dyad4s(B);\n\ttens4ds I4 = dyad4s(I);\n\n\ttens4ds cw = BxB*(W11 + 2*W12*I1 + W22*I1*I1 + W2) - BoB2*(W12 + W22*I1) + B2xB2*W22 - Ib*W2;\n\n\ttens4ds cp = IxI*(WJJ*J + WJ) - I4*(2 * WJ);\n\n\ttens4ds c = cp + cw*(4.0 / J);\n\n\treturn c;\n}\n\ndouble FEGenericHyperelastic::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\tvector<double> v = { I1, I2, J, mp.m_r0.x, mp.m_r0.y, mp.m_r0.z };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\tdouble W = m_W.value_s(v);\n\n\treturn W;\n}\n"
  },
  {
    "path": "FEBioMech/FEGenericHyperelastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/MathObject.h>\n\n//! Hyperelastic material, defined by strain energy function. \n//! This case only considers the strain energy function to be a function of\n//! the invariants: I1, I2, J. Furthermore, it assumes that the \n//! strain energy function is defined by W(C) = W1(I1,I2) + WJ(J), where\n//! W1 only depends on I1 and I2, and WJ only depends on J. \nclass FEGenericHyperelastic : public FEElasticMaterial\n{\npublic:\n\tFEGenericHyperelastic(FEModel* fem);\n\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\n\tdouble StrainEnergyDensity(FEMaterialPoint& mp) override;\n\nprivate:\n\tbool BuildMathExpressions();\n\nprivate:\n\tstd::string\t\t\tm_exp;\n\nprivate:\n\tMSimpleExpression\tm_W;\t// strain-energy function\n\tvector<double*>\t\tm_param;\t// user parameters\n\n\t// strain energy derivatives\n\tMSimpleExpression\tm_W1;\n\tMSimpleExpression\tm_W2;\n\tMSimpleExpression\tm_WJ;\n\n\tMSimpleExpression\tm_W11;\n\tMSimpleExpression\tm_W12;\n\tMSimpleExpression\tm_W22;\n\tMSimpleExpression\tm_WJJ;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGenericHyperelasticUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEGenericHyperelasticUC.h\"\n#include <FECore/MMath.h>\n#include <FECore/MObj2String.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEGenericHyperelasticUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_exp, \"W\")->setUnits(UNIT_ENERGY);\n\tADD_PARAMETER(m_printDerivs, \"print_derivs\");\nEND_FECORE_CLASS();\n\nFEGenericHyperelasticUC::FEGenericHyperelasticUC(FEModel* fem) : FEUncoupledMaterial(fem)\n{\n\tm_printDerivs = false;\n}\n\n// initialization\nbool FEGenericHyperelasticUC::Init()\n{\n\tif (BuildMathExpressions() == false) return false;\n\treturn FEUncoupledMaterial::Init();\n}\n\nbool FEGenericHyperelasticUC::BuildMathExpressions()\n{\n\tvector<string> vars = { \"I1\", \"I2\", \"X\", \"Y\", \"Z\" };\n\n\t// add all user parameters\n\tFEParameterList& pl = GetParameterList();\n\tFEParamIterator pi = pl.first();\n\tm_param.clear();\n\tfor (int i = 0; i < pl.Parameters(); ++i, ++pi)\n\t{\n\t\tFEParam& p = *pi;\n\t\tif (p.GetFlags() & FEParamFlag::FE_PARAM_USER)\n\t\t{\n\t\t\tvars.push_back(p.name());\n\t\t\tm_param.push_back((double*)p.data_ptr());\n\t\t}\n\t}\n\n\tm_W.AddVariables(vars);\n\tif (m_W.Create(m_exp) == false) return false;\n\n    MITEM W1 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(0), 1));\n    MITEM W2 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(1), 1));\n\tm_W1.AddVariables(vars); m_W1.SetExpression(W1);\n\tm_W2.AddVariables(vars); m_W2.SetExpression(W2);\n\n    MITEM W11 = MDerive(m_W1.GetExpression(), *m_W1.Variable(0), 1);\n    MITEM W12 = MDerive(m_W1.GetExpression(), *m_W1.Variable(1), 1);\n    MITEM W22 = MDerive(m_W2.GetExpression(), *m_W2.Variable(1), 1);\n\tm_W11.AddVariables(vars); m_W11.SetExpression(W11);\n\tm_W12.AddVariables(vars); m_W12.SetExpression(W12);\n\tm_W22.AddVariables(vars); m_W22.SetExpression(W22);\n\n\tif (m_printDerivs)\n\t{\n\t\tfeLog(\"\\nStrain energy and derivatives for material %d (%s):\\n\", GetID(), GetName().c_str());\n\t\tMObj2String o2s;\n\t\tstring sW = o2s.Convert(m_W); feLog(\"W = %s\\n\", sW.c_str());\n\t\tstring sW1 = o2s.Convert(m_W1); feLog(\"W1  = %s\\n\", sW1.c_str());\n\t\tstring sW2 = o2s.Convert(m_W2); feLog(\"W2  = %s\\n\", sW2.c_str());\n\t\tstring sW11 = o2s.Convert(m_W11); feLog(\"W11 = %s\\n\", sW11.c_str());\n\t\tstring sW12 = o2s.Convert(m_W12); feLog(\"W12 = %s\\n\", sW12.c_str());\n\t\tstring sW22 = o2s.Convert(m_W22); feLog(\"W22 = %s\\n\", sW22.c_str());\n\t}\n\n\treturn true;\n}\n\n// serialization\nvoid FEGenericHyperelasticUC::Serialize(DumpStream& ar)\n{\n\tFEUncoupledMaterial::Serialize(ar);\n\tif ((ar.IsShallow() == false) && ar.IsLoading())\n\t{\n\t\tbool b = BuildMathExpressions();\n\t\tassert(b);\n\t}\n}\n\n//! Deviatoric Cauchy stress\nmat3ds FEGenericHyperelasticUC::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// get strain energy derivatives\n\tvector<double> v = { I1, I2, mp.m_r0.x, mp.m_r0.y, mp.m_r0.z };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\t// return deviatoric Cauchy stress \n\treturn T.dev()*(2.0 / J);\n}\n\n//! Deviatoric spatial Tangent\ntens4ds FEGenericHyperelasticUC::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0 / J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// get strain energy derivatives\n\tvector<double> v = { I1, I2, mp.m_r0.x, mp.m_r0.y, mp.m_r0.z };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\n\tdouble W11 = m_W11.value_s(v);\n\tdouble W22 = m_W22.value_s(v);\n\tdouble W12 = m_W12.value_s(v);\n\n\t// define fourth-order tensors\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4 = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B2xB2 = dyad1s(B2);\n\ttens4ds BoB2 = dyad1s(B, B2);\n\ttens4ds Ib = dyad4s(B);\n\n\t// deviatoric stress\n\tmat3ds T = (B*(W1 + W2*I1) - B2*W2)*(2.0/J);\n\tmat3ds devT = T.dev();\n\n\t// isochoric stiffness contribution\n\ttens4ds dw = BxB*(W11 + 2.0*W12*I1 + W22*I1*I1 + W2) - BoB2*(W12 + W22*I1) + B2xB2*W22 - Ib*W2;\n\tdouble trDw = dw.tr();\n\tmat3ds dwoI = dw.contract();\n\ttens4ds cw = dw - dyad1s(dwoI, I) / 3.0 + IxI*(trDw / 9.0);\n\n\t// put it all together\n\ttens4ds c = (I4 - IxI/3.0)*(2.0*T.tr()/3.0) - dyad1s(devT, I)*(2.0/3.0) + cw*(4.0/J);\n\n\t// all done\n\treturn c;\n}\n\n//! Deviatoric strain energy density\ndouble FEGenericHyperelasticUC::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// evaluate (deviatoric) strain energy\n\tvector<double> v = { I1, I2, mp.m_r0.x, mp.m_r0.y, mp.m_r0.z };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\tdouble W = m_W.value_s(v);\n\n\treturn W;\n}\n"
  },
  {
    "path": "FEBioMech/FEGenericHyperelasticUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\nclass FEGenericHyperelasticUC : public FEUncoupledMaterial\n{\npublic:\n\tFEGenericHyperelasticUC(FEModel* fem);\n\n\t// initialization\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Deviatoric Cauchy stress\n\tmat3ds DevStress(FEMaterialPoint& mp) override;\n\n\t//! Deviatoric spatial Tangent\n\ttens4ds DevTangent(FEMaterialPoint& mp) override;\n\n\t//! Deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n\nprivate:\n\tbool BuildMathExpressions();\n\nprivate:\n\tstd::string\t\t\tm_exp;\t\t\t// the string with the strain energy expression\n\tbool\t\t\t\tm_printDerivs;\t// option to print out derivatives\n\nprivate:\n\tMSimpleExpression\tm_W;\t// deviatoric strain-energy function\n\tvector<double*>\t\tm_param;\t// user parameters\n\n\t// strain energy derivatives\n\tMSimpleExpression\tm_W1;\n\tMSimpleExpression\tm_W2;\n\tMSimpleExpression\tm_W11;\n\tMSimpleExpression\tm_W12;\n\tMSimpleExpression\tm_W22;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGenericRigidJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEGenericRigidJoint.h\"\n#include \"FERigidBody.h\"\n#include <FECore/log.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/fecore_debug.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEGenericRigidJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_eps    , \"penalty\");\n\tADD_PARAMETER(m_tol    , \"tolerance\");\n\tADD_PARAMETER(m_q0     , \"joint\"  );\n\tADD_PARAMETER(m_dc[0]  , \"prescribe_x\" , m_bc[0]);\n\tADD_PARAMETER(m_dc[1]  , \"prescribe_y\" , m_bc[1]);\n\tADD_PARAMETER(m_dc[2]  , \"prescribe_z\" , m_bc[2]);\n\tADD_PARAMETER(m_dc[3]  , \"prescribe_Rx\", m_bc[3]);\n\tADD_PARAMETER(m_dc[4]  , \"prescribe_Ry\", m_bc[4]);\n\tADD_PARAMETER(m_dc[5]  , \"prescribe_Rz\", m_bc[5]);\n\tADD_PARAMETER(m_bsymm  , \"symmetric_stiffness\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEGenericRigidJoint::FEGenericRigidJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n\tstatic int count = 1;\n\tm_nID = count++;\n\n\tm_rbA = m_rbB = nullptr;\n\n\tm_bc[0] = false;\n\tm_bc[1] = false;\n\tm_bc[2] = false;\n\tm_bc[3] = false;\n\tm_bc[4] = false;\n\tm_bc[5] = false;\n\n\tm_dc[0] = 0.0;\n\tm_dc[1] = 0.0;\n\tm_dc[2] = 0.0;\n\tm_dc[3] = 0.0;\n\tm_dc[4] = 0.0;\n\tm_dc[5] = 0.0;\n\n\tm_bsymm = false;\n\n\tm_laugon = FECore::PENALTY_METHOD;\t// default to penalty method\n\tm_eps = 1.0;\n\tm_tol = 0.0;\n\n\tvec3d a(1, 0, 0);\n\tvec3d d(0, 1, 0);\n\tvec3d e1 = a.normalized();\n\tvec3d e3 = a ^ d; e3.unit();\n\tvec3d e2 = e3 ^ e1;\n\n\tm_v0[0] = e1;\n\tm_v0[1] = e2;\n\tm_v0[2] = e3;\n\n\tfor (int i = 0; i < 6; ++i)\n\t{\n\t\tm_EQ[i] = -1;\n\t\tm_Lm[i] = 0.0;\n\t\tm_Lmp[i] = 0.0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEGenericRigidJoint::Init()\n{\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n\t// initialize relative joint positions\n\tm_qa0 = m_q0 - m_rbA->m_r0;\n\tm_qb0 = m_q0 - m_rbB->m_r0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! initial position \nvec3d FEGenericRigidJoint::InitialPosition() const\n{\n\treturn m_q0;\n}\n\n//-----------------------------------------------------------------------------\n//! current position\nvec3d FEGenericRigidJoint::Position() const\n{\n\tFERigidBody& RBa = *m_rbA;\n\tquatd Qa = RBa.GetRotation();\n\tvec3d qa = Qa * m_qa0;\n\tvec3d ra = RBa.m_rt;\n\t\n\treturn ra + qa;\n}\n\n//-----------------------------------------------------------------------------\n// allocate equations\nint FEGenericRigidJoint::InitEquations(int neq)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tint n = neq;\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tif (m_bc[i]) m_EQ[i] = n++; else m_EQ[i] = -1;\n\t\t}\n\t\treturn n - neq;\n\t}\n\telse return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Build the matrix profile\nvoid FEGenericRigidJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tquatd Qa = RBa.GetRotation();\n\tvec3d qa = Qa*m_qa0;\n\n\tquatd Qb = RBb.GetRotation();\n\tvec3d qb = Qb*m_qb0;\n\n\tvec3d ra = RBa.m_rt;\n\tvec3d rb = RBb.m_rt;\n\tvec3d g = ra + qa - rb - qb;\n\n\tmat3da ya(-qa); mat3da yaT = -ya;\n\tmat3da yb(-qb); mat3da ybT = -yb;\n\n\tint ndof = (m_laugon == FECore::LAGMULT_METHOD ? 18 : 12);\n\n\t// add translational constraints\n\tvector<double> fe(ndof, 0.0);\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tvec3d v = Qa*m_v0[i];\n\t\t\tmat3da VT(v);\n\n\t\t\tdouble c = v*g + m_dc[i];\n\n\t\t\tdouble L = m_Lm[i];\n\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) L += m_eps*c;\n\n\t\t\tvec3d F = v*L;\n\n\t\t\tvec3d Fa = F;\n\t\t\tvec3d Fb = -F;\n\t\t\tvec3d Ma = yaT*F + VT*(g*L);\n\t\t\tvec3d Mb = -ybT*F;\n\n\t\t\t// body A\n\t\t\tfe[0] -= Fa.x;\n\t\t\tfe[1] -= Fa.y;\n\t\t\tfe[2] -= Fa.z;\n\t\t\tfe[3] -= Ma.x;\n\t\t\tfe[4] -= Ma.y;\n\t\t\tfe[5] -= Ma.z;\n\n\t\t\t// body B\n\t\t\tfe[6] -= Fb.x;\n\t\t\tfe[7] -= Fb.y;\n\t\t\tfe[8] -= Fb.z;\n\t\t\tfe[9] -= Mb.x;\n\t\t\tfe[10] -= Mb.y;\n\t\t\tfe[11] -= Mb.z;\n\n\t\t\t// add constraint for Lagrange multiplier\n\t\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t\t{\n\t\t\t\tfe[12 + i] -= c;\n\t\t\t}\n\t\t}\n\t}\n\n\t// add rotational constraints\n\tfor (int i = 3; i < 6; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tint K = i - 3;\n\t\t\tint I = (K + 1) % 3;\n\t\t\tint J = (K + 2) % 3;\n\t\t\tvec3d va = Qa*m_v0[I];\n\t\t\tvec3d vb = Qb*m_v0[J];\n\n\t\t\tdouble c = va*vb - cos(0.5*PI + m_dc[i]);\n\n\t\t\tdouble L = m_Lm[i];\n\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) L += m_eps*c;\n\n\t\t\tmat3da VaT(va);\n\t\t\tmat3da VbT(vb);\n\n\t\t\tvec3d Ma = VaT*vb*L;\n\t\t\tvec3d Mb = VbT*va*L;\n\n\t\t\t// body A\n\t\t\tfe[ 3] -= Ma.x;\n\t\t\tfe[ 4] -= Ma.y;\n\t\t\tfe[ 5] -= Ma.z;\n\n\t\t\t// body B\n\t\t\tfe[ 9] -= Mb.x;\n\t\t\tfe[10] -= Mb.y;\n\t\t\tfe[11] -= Mb.z;\n\n\t\t\t// add constraint for Lagrange multiplier\n\t\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t\t{\n\t\t\t\tfe[12 + i] -= c;\n\t\t\t}\n\t\t}\n\t}\n\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\tR.Assemble(lm, fe);\n\n\tRBa.m_Fr -= vec3d(fe[0], fe[1], fe[2]);\n\tRBa.m_Mr -= vec3d(fe[3], fe[4], fe[5]);\n\tRBb.m_Fr -= vec3d(fe[6], fe[7], fe[8]);\n\tRBb.m_Mr -= vec3d(fe[9], fe[10], fe[11]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) StiffnessMatrixLM(LS, tp);\n\telse StiffnessMatrixAL(LS, tp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::StiffnessMatrixLM(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tquatd Qa = RBa.GetRotation();\n\tvec3d qa = Qa*m_qa0;\n\n\tquatd Qb = m_rbB->GetRotation();\n\tvec3d qb = Qb*m_qb0;\n\n\tvec3d ra = RBa.m_rt;\n\tvec3d rb = RBb.m_rt;\n\tvec3d g = ra + qa - rb - qb;\n\n\tmat3da ya(-qa);\n\tmat3da yaT = -ya;\n\tmat3da yb(-qb); \n\tmat3da ybT = -yb;\n\n\tmat3da G(-g);\n\tmat3da GT = -G;\n\n\tFEElementMatrix ke;\n\tke.resize(18, 18);\n\tke.zero();\n\n\t// translational constraints\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tdouble L = m_Lm[i];\n\t\t\tvec3d v = Qa*m_v0[i];\n\t\t\tmat3d V = mat3da(-v);\n\t\t\tmat3d VT = -V;\n\n\t\t\tke.add_symm(0, 3, V*L);\n\t\t\tke.add_symm(0, 12 + i, v);\n\n\t\t\tif (m_bsymm) ke.add(3, 3, (yaT*V - GT*V).sym()*L);\n\t\t\telse ke.add(3, 3, (yaT*V - GT*V)*L);\n\n\t\t\tke.add_symm(3, 6, -VT*L);\n\t\t\tke.add_symm(3, 9, -(VT*yb)*L);\n\t\t\tke.add_symm(3, 12 + i, yaT*v + VT*g);\n\n\t\t\tke.add_symm(6, 12 + i, -v);\n\n\t\t\tif (m_bsymm) ke.add(9, 9     , (VT*yb).sym()*L);\n\t\t\telse ke.add(9, 9, (VT*yb)*L);\n\n\t\t\tke.add_symm(9, 12 + i, -ybT*v);\n\t\t}\n\t}\n\n\t// rotational constraints\n\tfor (int i = 3; i < 6; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tdouble L = m_Lm[i];\n\n\t\t\tint K = i - 3;\n\t\t\tint I = (K + 1) % 3;\n\t\t\tint J = (K + 2) % 3;\n\t\t\tvec3d va = Qa*m_v0[I];\n\t\t\tvec3d vb = Qb*m_v0[J];\n\n\t\t\tmat3da Va(-va), VaT(va);\n\t\t\tmat3da Vb(-vb), VbT(vb);\n\n\t\t\tif (m_bsymm) ke.add(3, 3, -(VbT*Va).sym()*L);\n\t\t\telse ke.add(3, 3, -(VbT*Va)*L);\n\n\t\t\tke.add_symm(3, 9, VaT*Vb*L);\n\t\t\tke.add_symm(3, 12 + i, VaT*vb);\n\n\t\t\tif (m_bsymm) ke.add(9, 9, -(VaT*Vb).sym()*L);\n\t\t\telse ke.add(9, 9, -(VaT*Vb)*L);\n\n\t\t\tke.add_symm(9, 12 + i, VbT*va);\n\t\t}\n\t}\n\n\t// unpack LM\n\tvector<int> lm;\n\tUnpackLM(lm);\n\tke.SetIndices(lm);\n\n\t// assemle into global stiffness matrix\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::StiffnessMatrixAL(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tquatd Qa = RBa.GetRotation();\n\tvec3d qa = Qa*m_qa0;\n\n\tquatd Qb = m_rbB->GetRotation();\n\tvec3d qb = Qb*m_qb0;\n\n\tvec3d ra = RBa.m_rt;\n\tvec3d rb = RBb.m_rt;\n\tvec3d g = ra + qa - rb - qb;\n\n\tmat3da ya(-qa);\n\tmat3da yaT = -ya;\n\tmat3da yb(-qb);\n\tmat3da ybT = -yb;\n\n\tmat3da G(-g);\n\tmat3da GT = -G;\n\n\tFEElementMatrix ke;\n\tke.resize(12, 12);\n\tke.zero();\n\n\t// translational constraints\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tvec3d w = Qa*m_v0[i];\n\t\t\tmat3d W = mat3da(-w);\n\t\t\tmat3d WT = -W;\n\n\t\t\tdouble c = w*g + m_dc[i];\n\t\t\tdouble lam = m_Lm[i] + m_eps*c;\n\n\t\t\tmat3dd I(1.0);\n\t\t\tmatrix Q(12, 3);\n\t\t\tQ.set(0, 0,    I);\n\t\t\tQ.set(3, 0,  yaT);\n\t\t\tQ.set(6, 0,   -I);\n\t\t\tQ.set(9, 0, -ybT);\n\t\t\tmatrix Qw = Q*w;\n\n\t\t\tmatrix V(12, 3); V.zero();\n\t\t\tV.set(3, 0, WT);\n\t\t\tmatrix Vg = V*g;\n\n\t\t\tmatrix C = Vg + Qw;\n\n\t\t\tmatrix CCT = C*C.transpose();\n\n\t\t\tmatrix QVT = Q*V.transpose();\n\t\t\tmatrix VQT = V*Q.transpose();\n\n\t\t\tke += CCT*m_eps + (QVT + VQT)*lam;\n\n\t\t\tif (m_bsymm == false)\n\t\t\t{\n\t\t\t\tke.add(3, 3, (GT*W + WT*ya)*(-lam));\n\t\t\t\tke.add(9, 9, (WT*yb)*(lam));\n\t\t\t}\n\t\t}\n\t}\n\n\t// rotational constraints\n\tfor (int i = 3; i < 6; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tint K = i - 3;\n\t\t\tint I = (K + 1) % 3;\n\t\t\tint J = (K + 2) % 3;\n\t\t\tvec3d va = Qa*m_v0[I];\n\t\t\tvec3d vb = Qb*m_v0[J];\n\n\t\t\tdouble c = va*vb - cos(0.5*PI + m_dc[i]);\n\n\t\t\tdouble lam = m_Lm[i] + m_eps*c;\n\n\t\t\tmat3da Va(-va), VaT(va);\n\t\t\tmat3da Vb(-vb), VbT(vb);\n\n\t\t\tmat3dd Id(1.0);\n\t\t\tmatrix N(12, 3); N.zero();\n\t\t\tN.set(3, 0,  Id);\n\t\t\tN.set(9, 0, -Id);\n\t\t\n\t\t\tvec3d vv = VaT*vb;\n\t\t\tmatrix C = N*vv;\n\n\t\t\tmatrix CCT = C*C.transpose();\n\t\t\tke += CCT*m_eps;\n\n\t\t\tif (m_bsymm == false)\n\t\t\t{\n\t\t\t\tmat3d VV = VaT*Vb;\n\t\t\t\tmatrix WT(3, 12); WT.zero();\n\t\t\t\tWT.add(0, 3, -VV.transpose());\n\t\t\t\tWT.add(0, 9, VV);\n\n\t\t\t\tmatrix JWT = N*WT;\n\t\t\t\tke += JWT*lam;\n\t\t\t}\n\t\t}\n\t}\n\n\t// unpack LM\n\tvector<int> lm;\n\tUnpackLM(lm);\n\tke.SetIndices(lm);\n\n\t// assemle into global stiffness matrix\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FEGenericRigidJoint::Augment(int naug, const FETimeInfo& tp)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n\n\tquatd Qa = RBa.GetRotation();\n\tquatd Qb = RBb.GetRotation();\n\n\tvec3d ra = RBa.m_rt;\n\tvec3d rb = RBb.m_rt;\n\n\tvec3d qa = Qa*m_qa0;\n\tvec3d qb = Qb*m_qb0;\n\n\t// NOTE: This is NOT the gap that was used in the last residual\n\t//       evaluation. Is that a problem?\n\tvec3d pa = ra + qa;\n\tvec3d pb = rb + qb;\n\tvec3d g = (ra - rb) + (qa - qb);\n\n\tdouble c[6] = { 0 };\n\n\t// translational constraints\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tvec3d v = Qa*m_v0[i];\n\t\t\tc[i] = v*g + m_dc[i];\n\t\t}\n\t}\n\n\t// rotational constraints\n\tfor (int i = 3; i < 6; ++i)\n\t{\n\t\tif (m_bc[i])\n\t\t{\n\t\t\tint K = i - 3;\n\t\t\tint I = (K + 1) % 3;\n\t\t\tint J = (K + 2) % 3;\n\t\t\tvec3d va = Qa*m_v0[I];\n\t\t\tvec3d vb = Qb*m_v0[J];\n\n\t\t\tc[i] = va*vb - cos(0.5*PI + m_dc[i]);\n\t\t}\n\t}\n\n\tfeLog(\"\\n=== rigid connector # %d:\\n\", m_nID);\n\n\tbool bconv = true;\n\tdouble e[6] = { 0 };\n\tdouble Lm[6] = { 0 };\n\tif (m_laugon == FECore::PENALTY_METHOD)\n\t{\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tLm[i] = m_eps*c[i];\n\t\t}\n\t}\n\telse if (m_laugon == FECore::AUGLAG_METHOD)\n\t{\n//\t\tif (m_tol > 0.0)\n\t\t{\n\t\t\tfor (int i = 0; i < 6; ++i)\n\t\t\t{\n\t\t\t\tif (m_bc[i])\n\t\t\t\t{\n\t\t\t\t\tLm[i] = m_Lm[i] + m_eps*c[i];\n\n\t\t\t\t\tdouble F0 = fabs(m_Lm[i]);\n\t\t\t\t\tdouble F1 = fabs(Lm[i]);\n\n\t\t\t\t\tdouble dl = fabs((F1 - F0) / F1);\n\t\t\t\t\tif (m_tol && (dl > m_tol)) bconv = false;\n\n\t\t\t\t\te[i] = dl;\n\t\t\t\t}\n\t\t\t}\n\n//\t\t\tif (bconv == false)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < 6; ++i)\n\t\t\t\t\tm_Lm[i] += m_eps*c[i];\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < 6; ++i) Lm[i] = m_Lm[i];\n\t}\n\n\tfeLog(\" Lagrange m. : %13.7lg,%13.7lg,%13.7lg\\n\", Lm[0], Lm[1], Lm[2]);\n\tfeLog(\"               %13.7lg,%13.7lg,%13.7lg\\n\", Lm[3], Lm[4], Lm[5]);\n\tfeLog(\" constraint  : %13.7lg,%13.7lg,%13.7lg\\n\", c[0], c[1], c[2]);\n\tfeLog(\"               %13.7lg,%13.7lg,%13.7lg\\n\", c[3], c[4], c[5]);\n\tif (m_laugon == FECore::AUGLAG_METHOD)\n\t{\n\t\tfeLog(\" error       : %13.7lg,%13.7lg,%13.7lg\\n\", e[0], e[1], e[2]);\n\t\tfeLog(\"               %13.7lg,%13.7lg,%13.7lg\\n\", e[3], e[4], e[5]);\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n\tar & m_nID;\n\tar & m_q0 & m_qa0 & m_qb0;\n\tar & m_Lm & m_Lmp;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::Update()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::Reset()\n{\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::UnpackLM(vector<int>& lm)\n{\n\tint ndof = (m_laugon == FECore::LAGMULT_METHOD ? 18 : 12);\n\n\t// add the dofs of rigid body A\n\tlm.reserve(ndof);\n\tlm.push_back(m_rbA->m_LM[0]);\n\tlm.push_back(m_rbA->m_LM[1]);\n\tlm.push_back(m_rbA->m_LM[2]);\n\tlm.push_back(m_rbA->m_LM[3]);\n\tlm.push_back(m_rbA->m_LM[4]);\n\tlm.push_back(m_rbA->m_LM[5]);\n\n\t// add the dofs of rigid body B\n\tlm.push_back(m_rbB->m_LM[0]);\n\tlm.push_back(m_rbB->m_LM[1]);\n\tlm.push_back(m_rbB->m_LM[2]);\n\tlm.push_back(m_rbB->m_LM[3]);\n\tlm.push_back(m_rbB->m_LM[4]);\n\tlm.push_back(m_rbB->m_LM[5]);\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < 6; ++i) lm.push_back(m_EQ[i]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGenericRigidJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tif (m_EQ[i] != -1) m_Lm[i] = m_Lmp[i] + Ui[m_EQ[i]] + ui[m_EQ[i]];\n\t\t}\n\t}\n}\n\nvoid FEGenericRigidJoint::PrepStep()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tm_Lmp[i] = m_Lm[i];\n\t\t}\n\t}\n}\n\nvoid FEGenericRigidJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tif (m_EQ[i] != -1) Ui[m_EQ[i]] += ui[m_EQ[i]];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEGenericRigidJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/vec3d.h>\n#include \"FERigidConnector.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEGenericRigidJoint : public FERigidConnector\n{\npublic:\n\tFEGenericRigidJoint(FEModel* fem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t// allocate equations\n\tint InitEquations(int neq) override;\n\n\t//! calculates the joint forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculates the joint stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentation\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! update state\n\tvoid Update() override;\n\n\t//! Reset data\n\tvoid Reset() override;\n\n\t//! initial position \n\tvec3d InitialPosition() const;\n\n\t//! current position\n\tvec3d Position() const;\n\nprotected:\n\tvoid UnpackLM(vector<int>& lm);\n\n\t// Build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\n\tvoid PrepStep() override;\n\nprotected:\n\t//! stiffness matrix for penalty and augmented Lagrangian\n\tvoid StiffnessMatrixAL(FELinearSystem& LS, const FETimeInfo& tp);\n\n\t//! stiffness matrix for Lagrange Multipliers\n\tvoid StiffnessMatrixLM(FELinearSystem& LS, const FETimeInfo& tp);\n\nprivate:\n\tint\t\tm_laugon;\n\tdouble\tm_eps;\n\tdouble\tm_tol;\n\tvec3d\tm_q0;\n\tbool\tm_bsymm;\n\nprivate:\n\tvec3d\tm_qa0;\n\tvec3d\tm_qb0;\n\n\tbool\tm_bc[6];\n\tvec3d\tm_v0[3];\n\tdouble\tm_dc[6];\n\n\tint\t\tm_EQ[6];\n\tdouble\tm_Lm[6], m_Lmp[6];\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGenericTransIsoHyperelastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEGenericTransIsoHyperelastic.h\"\n#include <FECore/MMath.h>\n#include <FECore/MObj2String.h>\n#include <FECore/log.h>\n#include <FECore/FEConstValueVec3.h>\n\nBEGIN_FECORE_CLASS(FEGenericTransIsoHyperelastic, FEElasticMaterial)\n\tADD_PARAMETER(m_exp, \"W\");\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\nFEGenericTransIsoHyperelastic::FEGenericTransIsoHyperelastic(FEModel* fem) : FEElasticMaterial(fem)\n{\n\tm_fiber = nullptr;\n}\n\nbool FEGenericTransIsoHyperelastic::Init()\n{\n\tvector<string> vars = { \"I1\", \"I2\", \"I4\", \"I5\", \"J\" };\n\n\t// add all user parameters\n\tFEParameterList& pl = GetParameterList();\n\tFEParamIterator pi = pl.first();\n\tm_param.clear();\n\tfor (int i = 0; i < pl.Parameters(); ++i, ++pi)\n\t{\n\t\tFEParam& p = *pi;\n\t\tif (p.GetFlags() & FEParamFlag::FE_PARAM_USER)\n\t\t{\n\t\t\tvars.push_back(p.name());\n\t\t\tm_param.push_back((double*)p.data_ptr());\n\t\t}\n\t}\n\n\t// create math object\n\tm_W.AddVariables(vars);\n\tif (m_W.Create(m_exp) == false) return false;\n\n\t// calculate all derivatives\n\tMITEM W1 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(0), 1));\n\tMITEM W2 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(1), 1));\n\tMITEM W4 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(2), 1));\n\tMITEM W5 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(3), 1));\n\tMITEM WJ = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(4), 1));\n\tm_W1.AddVariables(vars); m_W1.SetExpression(W1);\n\tm_W2.AddVariables(vars); m_W2.SetExpression(W2);\n\tm_W4.AddVariables(vars); m_W4.SetExpression(W4);\n\tm_W5.AddVariables(vars); m_W5.SetExpression(W5);\n\tm_WJ.AddVariables(vars); m_WJ.SetExpression(WJ);\n\n\tMITEM W11 = MDerive(m_W1.GetExpression(), *m_W1.Variable(0), 1);\n\tMITEM W12 = MDerive(m_W1.GetExpression(), *m_W1.Variable(1), 1);\n\tMITEM W14 = MDerive(m_W1.GetExpression(), *m_W1.Variable(2), 1);\n\tMITEM W15 = MDerive(m_W1.GetExpression(), *m_W1.Variable(3), 1);\n\tMITEM W22 = MDerive(m_W2.GetExpression(), *m_W2.Variable(1), 1);\n\tMITEM W24 = MDerive(m_W2.GetExpression(), *m_W2.Variable(2), 1);\n\tMITEM W25 = MDerive(m_W2.GetExpression(), *m_W2.Variable(3), 1);\n\tMITEM W44 = MDerive(m_W4.GetExpression(), *m_W4.Variable(2), 1);\n\tMITEM W45 = MDerive(m_W4.GetExpression(), *m_W4.Variable(3), 1);\n\tMITEM W55 = MDerive(m_W5.GetExpression(), *m_W5.Variable(3), 1);\n\tm_W11.AddVariables(vars); m_W11.SetExpression(W11);\n\tm_W12.AddVariables(vars); m_W12.SetExpression(W12);\n\tm_W14.AddVariables(vars); m_W14.SetExpression(W14);\n\tm_W15.AddVariables(vars); m_W15.SetExpression(W15);\n\tm_W22.AddVariables(vars); m_W22.SetExpression(W22);\n\tm_W24.AddVariables(vars); m_W24.SetExpression(W24);\n\tm_W25.AddVariables(vars); m_W25.SetExpression(W25);\n\tm_W44.AddVariables(vars); m_W44.SetExpression(W44);\n\tm_W45.AddVariables(vars); m_W45.SetExpression(W45);\n\tm_W55.AddVariables(vars); m_W55.SetExpression(W55);\n\n\tMITEM WJJ = MDerive(m_WJ.GetExpression(), *m_WJ.Variable(4), 1);\n\tm_WJJ.AddVariables(vars); m_WJJ.SetExpression(WJJ);\n\n#ifndef NDEBUG\n\tMObj2String o2s;\n\tstring sW1 = o2s.Convert(m_W1); feLog(\"W1  = %s\\n\", sW1.c_str());\n\tstring sW2 = o2s.Convert(m_W2); feLog(\"W2  = %s\\n\", sW2.c_str());\n\tstring sW4 = o2s.Convert(m_W4); feLog(\"W4  = %s\\n\", sW4.c_str());\n\tstring sW5 = o2s.Convert(m_W5); feLog(\"W5  = %s\\n\", sW5.c_str());\n\tstring sWJ = o2s.Convert(m_WJ); feLog(\"WJ  = %s\\n\", sWJ.c_str());\n\tstring sW11 = o2s.Convert(m_W11); feLog(\"W11 = %s\\n\", sW11.c_str());\n\tstring sW12 = o2s.Convert(m_W12); feLog(\"W12 = %s\\n\", sW12.c_str());\n\tstring sW14 = o2s.Convert(m_W14); feLog(\"W14 = %s\\n\", sW14.c_str());\n\tstring sW15 = o2s.Convert(m_W15); feLog(\"W15 = %s\\n\", sW15.c_str());\n\tstring sW22 = o2s.Convert(m_W22); feLog(\"W22 = %s\\n\", sW22.c_str());\n\tstring sW24 = o2s.Convert(m_W24); feLog(\"W24 = %s\\n\", sW24.c_str());\n\tstring sW25 = o2s.Convert(m_W25); feLog(\"W25 = %s\\n\", sW25.c_str());\n\tstring sW44 = o2s.Convert(m_W44); feLog(\"W44 = %s\\n\", sW44.c_str());\n\tstring sW45 = o2s.Convert(m_W45); feLog(\"W45 = %s\\n\", sW45.c_str());\n\tstring sW55 = o2s.Convert(m_W55); feLog(\"W55 = %s\\n\", sW55.c_str());\n\tstring sWJJ = o2s.Convert(m_WJJ); feLog(\"WJJ = %s\\n\", sWJJ.c_str());\n#endif\n\n\treturn FEElasticMaterial::Init();\n}\n\nmat3ds FEGenericTransIsoHyperelastic::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble lam = a.unit();\n\n\t// evaluate the invariants\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble I4 = lam*lam;\n\tdouble I5 = I4*(a*(B*a));\n\n\t// create the parameter list\n\tvector<double> v = { I1, I2, I4, I5, J };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\t// evaluate the strain energy derivatives\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\tdouble W4 = m_W4.value_s(v);\n\tdouble W5 = m_W5.value_s(v);\n\tdouble WJ = m_WJ.value_s(v);\n\n\tmat3dd I(1.0);\n\tmat3ds AxA = dyad(a);\n\tmat3ds aBa = dyads(a, B*a);\n\n\tmat3ds s = (B*(W1 + W2*I1) - B2*W2 + AxA*(W4*I4) + aBa*(W5*I4))*(2.0 / J) + WJ*I;\n\n\t// all done!\n\treturn s;\n}\n\ntens4ds FEGenericTransIsoHyperelastic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble lam = a.unit();\n\n\t// evaluate the invariants\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble I4 = lam*lam;\n\tdouble I5 = I4*(a*(B*a));\n\n\t// evaluate parameters\n\tvector<double> v = { I1, I2, I4, I5, J };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\t// evaluate strain energy derivatives\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\tdouble W4 = m_W4.value_s(v);\n\tdouble W5 = m_W5.value_s(v);\n\tdouble WJ = m_WJ.value_s(v);\n\n\tdouble W11 = m_W11.value_s(v);\n\tdouble W12 = m_W12.value_s(v);\n\tdouble W14 = m_W14.value_s(v);\n\tdouble W15 = m_W15.value_s(v);\n\tdouble W22 = m_W22.value_s(v);\n\tdouble W24 = m_W24.value_s(v);\n\tdouble W25 = m_W25.value_s(v);\n\tdouble W44 = m_W44.value_s(v);\n\tdouble W45 = m_W45.value_s(v);\n\tdouble W55 = m_W55.value_s(v);\n\n\tdouble WJJ = m_WJJ.value_s(v);\n\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B2xB2 = dyad1s(B2);\n\n\ttens4ds BoB2 = dyad1s(B, B2);\n\n\ttens4ds Ib = dyad4s(B);\n\ttens4ds II = dyad4s(I);\n\n\tmat3ds A = dyad(a);\n\tmat3ds aBa = dyads(a, B*a);\n\n\ttens4ds Baa = dyad1s(B, A);\n\ttens4ds BaBa = dyad1s(B, aBa);\n\ttens4ds B2A = dyad1s(B2, A);\n\ttens4ds B2aBa = dyad1s(B2, aBa);\n\ttens4ds AxA = dyad1s(A);\n\ttens4ds AaBa = dyad1s(A, aBa);\n\ttens4ds aBaaBa = dyad1s(aBa, aBa);\n\ttens4ds AB = dyad5s(A, B);\n\n\t// stiffness contribution from isotropic terms\n\ttens4ds cw_iso = BxB*(W11 + 2 * W12*I1 + W22*I1*I1 + W2) - BoB2*(W12 + W22*I1) + B2xB2*W22 - Ib*W2;\n\n\t// stiffness constribution from anisotropic terms \n\ttens4ds cw_ani = Baa*(I4*(W14 + W24*I1)) \\\n\t\t+ BaBa*(I4*(W15 + W25*I1)) \\\n\t\t- B2A*(I4*W24) \\\n\t\t- B2aBa*(I4*W25) \\\n\t\t+ AxA*(I4*I4*W44) \\\n\t\t+ AaBa*(I4*I4*W45) \\\n\t\t+ aBaaBa*(I4*I4*W55) \\\n\t\t+ AB*(I4*W5);\n\n\t// let's sum them up\n\ttens4ds cw = cw_iso + cw_ani;\n\n\t// the \"pressure\" term\n\ttens4ds cp = IxI*(WJJ*J + WJ) - II*(2 * WJ);\n\n\t// add it up\n\ttens4ds c = cp + cw*(4.0 / J);\n\n\t// all done\n\treturn c;\n}\n\ndouble FEGenericTransIsoHyperelastic::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble lam = a.unit();\n\n\t// evaluate the invariants\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble I4 = lam*lam;\n\tdouble I5 = I4*(a*(B*a));\n\n\t// evaluate parameters\n\tvector<double> v = { I1, I2, I4, I5, J };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\tdouble W = m_W.value_s(v);\n\n\treturn W;\n}\n"
  },
  {
    "path": "FEBioMech/FEGenericTransIsoHyperelastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/MathObject.h>\n\n//! Transversely isotropic Hyperelastic material, defined by strain energy function. \n//! This case assumes the strain energy function to be a function of\n//! the invariants: I1, I2, I4, I5, and J. Furthermore, it assumes that the \n//! strain energy function is defined by W(C) = W1(I1,I2, I4, I5) + WJ(J), where\n//! W1 only depends on I1, I2, I4, and I5, and WJ only depends on J. \nclass FEGenericTransIsoHyperelastic : public FEElasticMaterial\n{\npublic:\n\tFEGenericTransIsoHyperelastic(FEModel* fem);\n\n\tbool Init() override;\n\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\n\tdouble StrainEnergyDensity(FEMaterialPoint& mp) override;\n\nprivate:\n\tstd::string\t\t\tm_exp;\n\tFEVec3dValuator*\tm_fiber;\n\nprivate:\n\tMSimpleExpression\tm_W;\t\t// strain-energy function\n\tvector<double*>\t\tm_param;\t// user parameters\n\n\t// strain energy derivatives\n\tMSimpleExpression\tm_W1;\n\tMSimpleExpression\tm_W2;\n\tMSimpleExpression\tm_W4;\n\tMSimpleExpression\tm_W5;\n\tMSimpleExpression\tm_WJ;\n\n\tMSimpleExpression\tm_W11;\n\tMSimpleExpression\tm_W12;\n\tMSimpleExpression\tm_W14;\n\tMSimpleExpression\tm_W15;\n\tMSimpleExpression\tm_W22;\n\tMSimpleExpression\tm_W24;\n\tMSimpleExpression\tm_W25;\n\tMSimpleExpression\tm_W44;\n\tMSimpleExpression\tm_W45;\n\tMSimpleExpression\tm_W55;\n\tMSimpleExpression\tm_WJJ;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGenericTransIsoHyperelasticUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEGenericTransIsoHyperelasticUC.h\"\n#include <FECore/MMath.h>\n#include <FECore/MObj2String.h>\n#include <FECore/log.h>\n#include <FECore/FEConstValueVec3.h>\n\nBEGIN_FECORE_CLASS(FEGenericTransIsoHyperelasticUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_exp, \"W\");\n\tADD_PARAMETER(m_printDerivs, \"print_derivs\");\n\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\nFEGenericTransIsoHyperelasticUC::FEGenericTransIsoHyperelasticUC(FEModel* fem) : FEUncoupledMaterial(fem)\n{\n\tm_fiber = nullptr;\n\tm_printDerivs = false;\n}\n\nbool FEGenericTransIsoHyperelasticUC::Init()\n{\n\tvector<string> vars = { \"I1\", \"I2\", \"I4\", \"I5\" };\n\n\t// add all user parameters\n\tFEParameterList& pl = GetParameterList();\n\tFEParamIterator pi = pl.first();\n\tm_param.clear();\n\tfor (int i = 0; i < pl.Parameters(); ++i, ++pi)\n\t{\n\t\tFEParam& p = *pi;\n\t\tif (p.GetFlags() & FEParamFlag::FE_PARAM_USER)\n\t\t{\n\t\t\tvars.push_back(p.name());\n\t\t\tm_param.push_back((double*)p.data_ptr());\n\t\t}\n\t}\n\n\t// create math object\n\tm_W.AddVariables(vars);\n\tif (m_W.Create(m_exp) == false) return false;\n\n\t// calculate all derivatives\n\tMITEM W1 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(0), 1));\n\tMITEM W2 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(1), 1));\n\tMITEM W4 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(2), 1));\n\tMITEM W5 = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(3), 1));\n\tMITEM WJ = MSimplify(MDerive(m_W.GetExpression(), *m_W.Variable(4), 1));\n\tm_W1.AddVariables(vars); m_W1.SetExpression(W1);\n\tm_W2.AddVariables(vars); m_W2.SetExpression(W2);\n\tm_W4.AddVariables(vars); m_W4.SetExpression(W4);\n\tm_W5.AddVariables(vars); m_W5.SetExpression(W5);\n\n\tMITEM W11 = MDerive(m_W1.GetExpression(), *m_W1.Variable(0), 1);\n\tMITEM W12 = MDerive(m_W1.GetExpression(), *m_W1.Variable(1), 1);\n\tMITEM W14 = MDerive(m_W1.GetExpression(), *m_W1.Variable(2), 1);\n\tMITEM W15 = MDerive(m_W1.GetExpression(), *m_W1.Variable(3), 1);\n\tMITEM W22 = MDerive(m_W2.GetExpression(), *m_W2.Variable(1), 1);\n\tMITEM W24 = MDerive(m_W2.GetExpression(), *m_W2.Variable(2), 1);\n\tMITEM W25 = MDerive(m_W2.GetExpression(), *m_W2.Variable(3), 1);\n\tMITEM W44 = MDerive(m_W4.GetExpression(), *m_W4.Variable(2), 1);\n\tMITEM W45 = MDerive(m_W4.GetExpression(), *m_W4.Variable(3), 1);\n\tMITEM W55 = MDerive(m_W5.GetExpression(), *m_W5.Variable(3), 1);\n\tm_W11.AddVariables(vars); m_W11.SetExpression(W11);\n\tm_W12.AddVariables(vars); m_W12.SetExpression(W12);\n\tm_W14.AddVariables(vars); m_W14.SetExpression(W14);\n\tm_W15.AddVariables(vars); m_W15.SetExpression(W15);\n\tm_W22.AddVariables(vars); m_W22.SetExpression(W22);\n\tm_W24.AddVariables(vars); m_W24.SetExpression(W24);\n\tm_W25.AddVariables(vars); m_W25.SetExpression(W25);\n\tm_W44.AddVariables(vars); m_W44.SetExpression(W44);\n\tm_W45.AddVariables(vars); m_W45.SetExpression(W45);\n\tm_W55.AddVariables(vars); m_W55.SetExpression(W55);\n\n\tif (m_printDerivs)\n\t{\n\t\tfeLog(\"\\nStrain energy and derivatives for material %d (%s):\\n\", GetID(), GetName().c_str());\n\t\tMObj2String o2s;\n\t\tstring sW = o2s.Convert(m_W); feLog(\"W = %s\\n\", sW.c_str());\n\t\tstring sW1 = o2s.Convert(m_W1); feLog(\"W1  = %s\\n\", sW1.c_str());\n\t\tstring sW2 = o2s.Convert(m_W2); feLog(\"W2  = %s\\n\", sW2.c_str());\n\t\tstring sW4 = o2s.Convert(m_W4); feLog(\"W4  = %s\\n\", sW4.c_str());\n\t\tstring sW5 = o2s.Convert(m_W5); feLog(\"W5  = %s\\n\", sW5.c_str());\n\t\tstring sW11 = o2s.Convert(m_W11); feLog(\"W11 = %s\\n\", sW11.c_str());\n\t\tstring sW12 = o2s.Convert(m_W12); feLog(\"W12 = %s\\n\", sW12.c_str());\n\t\tstring sW14 = o2s.Convert(m_W14); feLog(\"W14 = %s\\n\", sW14.c_str());\n\t\tstring sW15 = o2s.Convert(m_W15); feLog(\"W15 = %s\\n\", sW15.c_str());\n\t\tstring sW22 = o2s.Convert(m_W22); feLog(\"W22 = %s\\n\", sW22.c_str());\n\t\tstring sW24 = o2s.Convert(m_W24); feLog(\"W24 = %s\\n\", sW24.c_str());\n\t\tstring sW25 = o2s.Convert(m_W25); feLog(\"W25 = %s\\n\", sW25.c_str());\n\t\tstring sW44 = o2s.Convert(m_W44); feLog(\"W44 = %s\\n\", sW44.c_str());\n\t\tstring sW45 = o2s.Convert(m_W45); feLog(\"W45 = %s\\n\", sW45.c_str());\n\t\tstring sW55 = o2s.Convert(m_W55); feLog(\"W55 = %s\\n\", sW55.c_str());\n\t}\n\n\treturn FEElasticMaterial::Init();\n}\n\nmat3ds FEGenericTransIsoHyperelasticUC::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble lam = a.unit()*pow(J, -1./3.);\n\n\t// evaluate the invariants\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble I4 = lam*lam;\n\tdouble I5 = I4*(a*(B*a));\n\n\t// create the parameter list\n\tvector<double> v = { I1, I2, I4, I5 };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\t// evaluate the strain energy derivatives\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\tdouble W4 = m_W4.value_s(v);\n\tdouble W5 = m_W5.value_s(v);\n\n\tmat3dd I(1.0);\n\tmat3ds AxA = dyad(a);\n\tmat3ds aBa = dyads(a, B*a);\n\n\tmat3ds T = (B*(W1 + W2*I1) - B2*W2 + AxA*(W4*I4) + aBa*(W5*I4));\n\n\tmat3ds s = T.dev()*(2.0 / J);\n\n\t// all done!\n\treturn s;\n}\n\ntens4ds FEGenericTransIsoHyperelasticUC::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble lam = a.unit()*pow(J, -1./3.);\n\n\t// evaluate the invariants\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble I4 = lam*lam;\n\tdouble I5 = I4*(a*(B*a));\n\n\t// evaluate parameters\n\tvector<double> v = { I1, I2, I4, I5 };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\t// evaluate strain energy derivatives\n\tdouble W1 = m_W1.value_s(v);\n\tdouble W2 = m_W2.value_s(v);\n\tdouble W4 = m_W4.value_s(v);\n\tdouble W5 = m_W5.value_s(v);\n\n\tdouble W11 = m_W11.value_s(v);\n\tdouble W12 = m_W12.value_s(v);\n\tdouble W14 = m_W14.value_s(v);\n\tdouble W15 = m_W15.value_s(v);\n\tdouble W22 = m_W22.value_s(v);\n\tdouble W24 = m_W24.value_s(v);\n\tdouble W25 = m_W25.value_s(v);\n\tdouble W44 = m_W44.value_s(v);\n\tdouble W45 = m_W45.value_s(v);\n\tdouble W55 = m_W55.value_s(v);\n\n\t// a few tensors we'll need\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B2xB2 = dyad1s(B2);\n\ttens4ds BoB2 = dyad1s(B, B2);\n\ttens4ds Ib = dyad4s(B);\n\ttens4ds II = dyad4s(I);\n\tmat3ds A = dyad(a);\n\tmat3ds aBa = dyads(a, B*a);\n\ttens4ds Baa = dyad1s(B, A);\n\ttens4ds BaBa = dyad1s(B, aBa);\n\ttens4ds B2A = dyad1s(B2, A);\n\ttens4ds B2aBa = dyad1s(B2, aBa);\n\ttens4ds AxA = dyad1s(A);\n\ttens4ds AaBa = dyad1s(A, aBa);\n\ttens4ds aBaaBa = dyad1s(aBa, aBa);\n\ttens4ds AB = dyad5s(A, B);\n\n\t// evaluate deviatoric stress\n\tmat3ds T = (B*(W1 + W2*I1) - B2*W2 + A*(W4*I4) + aBa*(W5*I4))*(2./J);\n\tmat3ds devT = T.dev();\n\n\t// stiffness contribution from isotropic terms\n\ttens4ds dw_iso = BxB*(W11 + 2.0*W12*I1 + W22*I1*I1 + W2) - BoB2*(W12 + W22*I1) + B2xB2*W22 - Ib*W2;\n\n\t// stiffness constribution from anisotropic terms \n\ttens4ds dw_ani = Baa*(I4*(W14 + W24*I1)) \\\n\t\t+ BaBa*(I4*(W15 + W25*I1)) \\\n\t\t- B2A*(I4*W24) \\\n\t\t- B2aBa*(I4*W25) \\\n\t\t+ AxA*(I4*I4*W44) \\\n\t\t+ AaBa*(I4*I4*W45) \\\n\t\t+ aBaaBa*(I4*I4*W55) \\\n\t\t+ AB*(I4*W5);\n\n\t// isochoric contribution\n\ttens4ds dw = dw_iso + dw_ani;\n\tmat3ds dwoI = dw.contract();\n\tdouble trDw = dw.tr();\n\ttens4ds cw = dw - dyad1s(dwoI, I) / 3.0 + IxI*(trDw / 9.0);\n\n\t// put it all together\n\ttens4ds c = (II - IxI/3.0)*(2.0/3.0*T.tr()) - dyad1s(devT, I)*(2.0/3.0) + cw*(4. / J);\n\n\t// all done\n\treturn c;\n}\n\ndouble FEGenericTransIsoHyperelasticUC::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// get the material fiber axis\n\tvec3d a0 = m_fiber->unitVector(mp);\n\n\t// get the spatial fiber axis\n\tvec3d a = pt.m_F*a0;\n\tdouble lam = a.unit()*pow(J, -1./3.);\n\n\t// evaluate the invariants\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\tdouble I4 = lam*lam;\n\tdouble I5 = I4*(a*(B*a));\n\n\t// evaluate parameters\n\tvector<double> v = { I1, I2, I4, I5 };\n\tfor (int i = 0; i < m_param.size(); ++i) v.push_back(*m_param[i]);\n\n\tdouble W = m_W.value_s(v);\n\n\treturn W;\n}\n"
  },
  {
    "path": "FEBioMech/FEGenericTransIsoHyperelasticUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/MathObject.h>\n\n//! Uncoupled transversely isotropic Hyperelastic material, defined by strain energy function. \n//! This case assumes the strain energy function to be a function of\n//! the (deviatoric) invariants: I1, I2, I4, I5, and J. Furthermore, it assumes that the \n//! strain energy function is defined by W(C) = W1(I1,I2, I4, I5) + WJ(J), where\n//! W1 only depends on I1, I2, I4, and I5, and WJ only depends on J. \n//! Since FEBio handles WJ automatically, only W1 needs to be implemented\nclass FEGenericTransIsoHyperelasticUC : public FEUncoupledMaterial\n{\npublic:\n\tFEGenericTransIsoHyperelasticUC(FEModel* fem);\n\n\tbool Init() override;\n\n\tmat3ds DevStress(FEMaterialPoint& mp) override;\n\n\ttens4ds DevTangent(FEMaterialPoint& mp) override;\n\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n\nprivate:\n\tstd::string\t\t\tm_exp;\t\t// the string with the strain energy expression\n\tFEVec3dValuator*\tm_fiber;\t// fiber direction\n\tbool\t\t\t\tm_printDerivs;\t// option to print out derivatives\n\nprivate:\n\tMSimpleExpression\tm_W;\t\t// strain-energy function\n\tvector<double*>\t\tm_param;\t// user parameters\n\n\t\t\t\t\t\t\t\t\t// strain energy derivatives\n\tMSimpleExpression\tm_W1;\n\tMSimpleExpression\tm_W2;\n\tMSimpleExpression\tm_W4;\n\tMSimpleExpression\tm_W5;\n\n\tMSimpleExpression\tm_W11;\n\tMSimpleExpression\tm_W12;\n\tMSimpleExpression\tm_W14;\n\tMSimpleExpression\tm_W15;\n\tMSimpleExpression\tm_W22;\n\tMSimpleExpression\tm_W24;\n\tMSimpleExpression\tm_W25;\n\tMSimpleExpression\tm_W44;\n\tMSimpleExpression\tm_W45;\n\tMSimpleExpression\tm_W55;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGentMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEGentMaterial.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEGentMaterial, FEUncoupledMaterial)\n\tADD_PARAMETER(m_G , FE_RANGE_GREATER(0.0), \"G\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_Jm, FE_RANGE_GREATER(0.0), \"Jm\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEGentMaterial::FEGentMaterial(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_G  = 0.0;\n\tm_Jm = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEGentMaterial::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J  = ep.m_J;\n\tmat3ds b  = ep.DevLeftCauchyGreen();\n\tdouble I1 = b.tr();\n\n\tdouble mu = m_G;\n\tdouble Jm = m_Jm;\n\n\tdouble W1 = 0.5*mu*Jm / (Jm - I1 + 3.0);\n\n\tmat3ds T = b*W1;\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEGentMaterial::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1 = 0.5*m_G*m_Jm / (m_Jm - I1 + 3.0);\n\t// ---\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1;\n\n\t// deviatoric cauchy-stress\n\tmat3ds T = B*W1;\n\tT = T.dev()*(2.0/J);\n\n\t// Identity tensor\n\tmat3ds I(1,1,1,0,0,0);\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\ttens4ds c = dyad1s(T, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC);\n\n\treturn c;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FECompressibleGentMaterial, FEElasticMaterial)\n\tADD_PARAMETER(m_G , FE_RANGE_GREATER(0.0), \"G\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_K , FE_RANGE_GREATER(0.0), \"K\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_Jm, FE_RANGE_GREATER(0.0), \"Jm\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFECompressibleGentMaterial::FECompressibleGentMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_G = 0.0;\n\tm_K = 0.0;\n\tm_Jm = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FECompressibleGentMaterial::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = ep.m_J;\n\tmat3ds b = ep.LeftCauchyGreen();\n\tdouble I1 = b.tr();\n\n\tdouble mu = m_G;\n\tdouble k  = m_K;\n\tdouble Jm = m_Jm;\n\n\tdouble W1 = 0.5*mu*Jm / (Jm - I1 + 3.0);\n\n\tdouble h = 0.5*(J*J - 1.0) - log(J);\n\tdouble WJ = 2.0*k*(h*h*h)*(J - 1.0/J);\n\n\tmat3dd I(1.0);\n\tmat3ds s = b*(2.0*W1/J) + I*WJ;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FECompressibleGentMaterial::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = ep.m_J;\n\tmat3ds b = ep.LeftCauchyGreen();\n\tdouble I1 = b.tr();\n\n\tdouble mu = m_G;\n\tdouble k  = m_K;\n\tdouble Jm = m_Jm;\n\n\tdouble W11 = 0.5*mu*Jm/((Jm-I1+3)*(Jm-I1+3));\n\n\tdouble h = 0.5*(J*J - 1.0) - log(J);\n\tdouble WJ = 2.0*k*(h*h*h)*(J - 1.0/J);\n\tdouble WJJ = 6*k*h*h*(J-1.0/J)*(J-1.0/J) + 2*k*h*h*h*(1.0 + 1.0/(J*J));\n\n\tmat3dd I(1.0);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(b);\n\t\n\ttens4ds c = BxB*(4.0*W11/J) + IxI*(WJ + J*WJJ) - I4*(2*WJ);\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEGentMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include <FEBioMech/FEUncoupledMaterial.h>\n\n//-----------------------------------------------------------------------------\n// uncoupled Gent material\nclass FEGentMaterial : public FEUncoupledMaterial\n{\npublic:\n\t//! constructor\n\tFEGentMaterial(FEModel* pfem);\n\n\t//! deviatoric Cauchy stress\n\tmat3ds DevStress(FEMaterialPoint& mp) override;\n\n\t//! Deviatoric spatial Tangent\n\ttens4ds DevTangent(FEMaterialPoint& mp) override;\n\nprivate: // material parameters\n\tdouble\tm_G;\t//!< shear modulus\n\tdouble\tm_Jm;\t//!< Jm = Im - 3, where Im is max first invariant\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// compressible Gent material\nclass FECompressibleGentMaterial : public FEElasticMaterial\n{\npublic:\n\t// constructor\n\tFECompressibleGentMaterial(FEModel* pfem);\n\n\t// Cauchy stress\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\t// spatial elasticity tangent\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\npublic: // material parameter\n\tdouble\tm_G;\t//!< shear modulus\n\tdouble\tm_K;\t//!< bulk modulus\n\tdouble\tm_Jm;\t//!< Jm = Im - 3, where Im is max first invariant\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEGrowthTensor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"FEGrowthTensor.h\"\n#include <FECore/FEConstValueVec3.h>\n\n//-----------------------------------------------------------------------------\n//! Growth tensor\n//!\n// define the material parameters\nBEGIN_FECORE_CLASS(FEGrowthTensor, FEMaterialProperty)\n    ADD_PROPERTY(m_fiber, \"fiber\", FEProperty::Optional)->SetDefaultType(\"vector\");\nEND_FECORE_CLASS();\n\nFEGrowthTensor::FEGrowthTensor(FEModel* pfem) : FEMaterialProperty(pfem) \n{ \n\tm_fiber = nullptr; \n}\n\nFEGrowthTensor::~FEGrowthTensor() {}\n\nbool FEGrowthTensor::Init()\n{\n    if (m_fiber == nullptr) {\n        FEConstValueVec3* val = fecore_new<FEConstValueVec3>(\"vector\", nullptr);\n        val->value() = vec3d(1,0,0);\n        m_fiber = val;\n    }\n    \n    return FEMaterialProperty::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Volume growth\n//!\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEVolumeGrowth, FEGrowthTensor)\n    ADD_PARAMETER(m_gm, \"multiplier\")->setLongName(\"volumetric growth multiplier\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! growth tensor\nmat3d FEVolumeGrowth::GrowthTensor(FEMaterialPoint& pt, const vec3d& n0)\n{\n    return mat3dd(m_gm(pt));\n}\n\n//-----------------------------------------------------------------------------\n//! inverse of growth tensor\nmat3d FEVolumeGrowth::GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& n0)\n{\n    return mat3dd(1./m_gm(pt));\n}\n\n//-----------------------------------------------------------------------------\n//! referential solid density\ndouble FEVolumeGrowth::GrowthDensity(FEMaterialPoint& pt, const vec3d& n0)\n{\n    return pow(m_gm(pt), 3);\n}\n\n//-----------------------------------------------------------------------------\n//! Area growth\n//!\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEAreaGrowth, FEGrowthTensor)\n    ADD_PARAMETER(m_gm , \"multiplier\")->setLongName(\"areal growth multiplier\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! growth tensor\nmat3d FEAreaGrowth::GrowthTensor(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = sqrt(m_gm(pt));\n    double gmani = 2 - gmiso;\n    mat3d Fg = mat3dd(gmiso) + (n0 & n0)*(gmani - 1);\n    return Fg;\n}\n\n//-----------------------------------------------------------------------------\n//! inverse of growth tensor\nmat3d FEAreaGrowth::GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = sqrt(m_gm(pt));\n    double gmani = 2 - gmiso;\n    mat3d Fgi = mat3dd(1./gmiso) - (n0 & n0)*((gmani - 1)/gmiso/(gmiso+gmani-1));\n    return Fgi;\n}\n\n//-----------------------------------------------------------------------------\n//! referential solid density\ndouble FEAreaGrowth::GrowthDensity(FEMaterialPoint& pt, const vec3d& n0)\n{\n    return m_gm(pt);\n}\n\n//-----------------------------------------------------------------------------\n//! Fiber growth\n//!\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberGrowth, FEGrowthTensor)\n    ADD_PARAMETER(m_gm , \"multiplier\")->setLongName(\"uniaxial growth multiplier\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! growth tensor\nmat3d FEFiberGrowth::GrowthTensor(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = 1;\n    double gmani = m_gm(pt);\n    mat3d Fg = mat3dd(gmiso) + (n0 & n0)*(gmani - 1);\n    return Fg;\n}\n\n//-----------------------------------------------------------------------------\n//! inverse of growth tensor\nmat3d FEFiberGrowth::GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = 1;\n    double gmani = m_gm(pt);\n    mat3d Fgi = mat3dd(1./gmiso) - (n0 & n0)*((gmani - 1)/gmiso/(gmiso+gmani-1));\n    return Fgi;\n}\n\n//-----------------------------------------------------------------------------\n//! referential solid density\ndouble FEFiberGrowth::GrowthDensity(FEMaterialPoint& pt, const vec3d& n0)\n{\n    return m_gm(pt);\n}\n\n//-----------------------------------------------------------------------------\n//! General growth\n//!\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEGeneralGrowth, FEGrowthTensor)\n    ADD_PARAMETER(m_gi , \"iso\")->setLongName(\"isotropic growth multiplier\");\n    ADD_PARAMETER(m_ga , \"ani\")->setLongName(\"anisotropic growth multiplier\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! growth tensor\nmat3d FEGeneralGrowth::GrowthTensor(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = m_gi(pt);\n    double gmani = m_ga(pt);\n    mat3d Fg = mat3dd(gmiso) + (n0 & n0)*(gmani - 1);\n    return Fg;\n}\n\n//-----------------------------------------------------------------------------\n//! inverse of growth tensor\nmat3d FEGeneralGrowth::GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = m_gi(pt);\n    double gmani = m_ga(pt);\n    double den = gmiso + gmani - 1;\n    double da = (gmani-1)/gmiso;\n    double di = 1 + da;\n    mat3d Fgi = mat3dd(di/den) - (n0 & n0)*(da/den);\n    return Fgi;\n}\n\n//-----------------------------------------------------------------------------\n//! referential solid density\ndouble FEGeneralGrowth::GrowthDensity(FEMaterialPoint& pt, const vec3d& n0)\n{\n    double gmiso = m_gi(pt);\n    double gmani = m_ga(pt);\n    double den = gmiso + gmani - 1;\n    return pow(gmiso,2)*(gmani+gmiso-1);\n}\n\n"
  },
  {
    "path": "FEBioMech/FEGrowthTensor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for growth tensors.\n//!\nclass FEBIOMECH_API FEGrowthTensor : public FEMaterialProperty\n{\npublic:\n\tFECORE_BASE_CLASS(FEGrowthTensor);\n\npublic:\n\tFEGrowthTensor(FEModel* pfem);\n\tvirtual ~FEGrowthTensor();\n    \n    //! growth tensor\n    virtual mat3d GrowthTensor(FEMaterialPoint& pt, const vec3d& a0) = 0;\n    \n    //! inverse of growth tensor\n    virtual mat3d GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& a0) = 0;\n    \n    //! referential solid density\n    virtual double GrowthDensity(FEMaterialPoint& pt, const vec3d& a0) = 0;\n    \n    //! initialize\n    bool Init() override;\n    \npublic:\n    FEVec3dValuator* m_fiber;\n\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Volume growth\n//!\nclass FEVolumeGrowth : public FEGrowthTensor\n{\npublic:\n    FEVolumeGrowth(FEModel* pfem) : FEGrowthTensor(pfem) { m_gm = 1; }\n    virtual ~FEVolumeGrowth(){}\n    \n    //! growth tensor\n    mat3d GrowthTensor(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! inverse of growth tensor\n    mat3d GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! referential solid density\n    double GrowthDensity(FEMaterialPoint& pt, const vec3d& a0) override;\n\npublic:\n    FEParamDouble   m_gm;       //! isotropic growth multiplier\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n\n};\n\n//-----------------------------------------------------------------------------\n//! Area growth\n//!\nclass FEAreaGrowth : public FEGrowthTensor\n{\npublic:\n    FEAreaGrowth(FEModel* pfem) : FEGrowthTensor(pfem) { m_gm = 1; }\n    virtual ~FEAreaGrowth(){}\n    \n    //! growth tensor\n    mat3d GrowthTensor(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! inverse of growth tensor\n    mat3d GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! referential solid density\n    double GrowthDensity(FEMaterialPoint& pt, const vec3d& a0) override;\n    \npublic:\n    FEParamDouble   m_gm;       //! growth multiplier\n\n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n\n};\n\n//-----------------------------------------------------------------------------\n//! Fiber growth\n//!\nclass FEFiberGrowth : public FEGrowthTensor\n{\npublic:\n    FEFiberGrowth(FEModel* pfem) : FEGrowthTensor(pfem) { m_gm = 1; }\n    virtual ~FEFiberGrowth(){}\n    \n    //! growth tensor\n    mat3d GrowthTensor(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! inverse of growth tensor\n    mat3d GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! referential solid density\n    double GrowthDensity(FEMaterialPoint& pt, const vec3d& a0) override;\n    \npublic:\n    FEParamDouble   m_gm;       //! growth multiplier\n\n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n\n};\n\n//-----------------------------------------------------------------------------\n//! General growth\n//!\nclass FEGeneralGrowth : public FEGrowthTensor\n{\npublic:\n    FEGeneralGrowth(FEModel* pfem) : FEGrowthTensor(pfem) { m_gi = 1;  m_ga = 1; }\n    virtual ~FEGeneralGrowth(){}\n    \n    //! growth tensor\n    mat3d GrowthTensor(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! inverse of growth tensor\n    mat3d GrowthTensorInverse(FEMaterialPoint& pt, const vec3d& a0) override;\n    \n    //! referential solid density\n    double GrowthDensity(FEMaterialPoint& pt, const vec3d& a0) override;\n    \npublic:\n    FEParamDouble   m_gi;       //! growth multiplier for isotropic growth\n    FEParamDouble   m_ga;       //! growth multiplier for anisotropic growth\n\n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n    \n};\n\n"
  },
  {
    "path": "FEBioMech/FEHGOCoronary.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEHGOCoronary.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEHGOCoronary, FEUncoupledMaterial)\n\tADD_PARAMETER(m_rho, \"rho\");\n\tADD_PARAMETER(m_k1 , \"k1\");\n\tADD_PARAMETER(m_k2 , \"k2\");\n\n\tADD_PROPERTY(m_fiber, \"fiber\");\n\t\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FETransIsoMooneyRivlin\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nFEHGOCoronary::FEHGOCoronary(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_rho = 0.0;\n\tm_k1 = 0.0;\n\tm_k2 = 1.0;\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEHGOCoronary::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// fiber vector\n\tvec3d fiber = m_fiber->unitVector(mp);\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0 = Q*fiber; a0.unit();\n\tvec3d a = F * a0;\n\tdouble lam = Jm13 * a.unit();\n\tmat3ds m = dyad(a);\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I4 = lam * lam;\n\n\t// material parameters\n\tdouble rho = m_rho;\n\tdouble k1 = m_k1;\n\tdouble k2 = m_k2;\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble w = k2 * ((1.0 - rho) * (I1 - 3.0) * (I1 - 3.0) + rho*(I4 - 1.0) * (I4 - 1.0));\n\tdouble w1  = 2.0 * k2 * (1.0 - rho) * (I1 - 3.0);\n\tdouble w4  = 2.0 * k2 * rho * (I4 - 1.0);\n\tdouble kew = k1*exp(w) / k2;\n\n\tdouble W1 = kew * w1;\n\tdouble W4 = kew * w4;\n\t// ------------------------------------------------\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B * W1 + m*(W4*I4);\n\n\t// calculate stress s = pI + 2/J * dev(T) \n\tmat3ds s = T.dev() * (2.0 / J);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric tangent\ntens4ds FEHGOCoronary::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0 / pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// fiber vector\n\tvec3d fiber = m_fiber->unitVector(mp);\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0 = Q*fiber; a0.unit();\n\tvec3d a = F * a0;\n\tdouble lam = Jm13 * a.unit();\n\tmat3ds m = dyad(a);\n\n\t// Invariants of B (= invariants of C)\n\tdouble J1 = B.tr();\n\tdouble J4 = lam * lam;\n\n\t// material parameters\n\tdouble rho = m_rho;\n\tdouble k1 = m_k1;\n\tdouble k2 = m_k2;\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble w = k2 * ((1.0 - rho) * (J1 - 3.0) * (J1 - 3.0) + rho * (J4 - 1.0) * (J4 - 1.0));\n\tdouble w1 = 2.0 * k2 * (1.0 - rho) * (J1 - 3.0);\n\tdouble w11 = 2.0 * k2 * (1.0 - rho);\n\tdouble w4 = 2.0 * k2 * rho * (J4 - 1.0);\n\tdouble w44 = 2.0 * k2 * rho;\n\tdouble kew = k1 * exp(w) / k2;\n\n\tdouble W1 = kew * w1;\n\tdouble W4 = kew * w4;\n\tdouble W11 = kew * (w1*w1 + w11);\n\tdouble W44 = kew * (w4*w4 + w44);\n\tdouble W14 = kew * (w1*w4);\n\t// ------------------------------------\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B * W1 + m * (W4 * J4);\n\n\t// calculate stress s = pI + 2/J * dev(T) \n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// calculate dWdC:C\n\tdouble WC = W1 * J1 + W4 * J4;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = W11 * J1 * J1 + 2.0 * W14 * J1 * J4 + W44 * J4 * J4;\n\n\tmat3dd I(1);\t// Identity\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4 = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4 = dyad4s(B);\n\ttens4ds Bxm = dyad1s(B, m);\n\ttens4ds mxm = dyad1s(m);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B * (W11 * J1) + B*(W14*J4) + m*(W14*J4*J1) + m*(W44*J4*J4);\n\n\ttens4ds Cw = BxB * W11 + Bxm * (W14 * J4) + mxm * (W44 * J4 * J4);\n\n\ttens4ds cw = Cw*(4.0 *Ji) - dyad1s(WCCxC, I) * (4.0 / 3.0 * Ji) + IxI * (4.0 / 9.0 * Ji * CWWC);\n\ttens4ds c = dyad1s(devs, I) * (-2.0 / 3.0) + (I4 - IxI / 3.0) * (4.0 / 3.0 * Ji * WC) + cw;\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEHGOCoronary::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// fiber vector\n\tvec3d fiber = m_fiber->unitVector(mp);\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0 = Q*fiber; a0.unit();\n\tvec3d a = F * a0;\n\tdouble lam = Jm13 * a.unit();\n\n\t// Invariants of B (= invariants of C)\n\tdouble J1 = B.tr();\n\tdouble J4 = lam * lam;\n\n\t// material parameters\n\tdouble rho = m_rho;\n\tdouble k1 = m_k1;\n\tdouble k2 = m_k2;\n\n\t// calculate sed\n\tdouble w = k2 * ((1.0 - rho) * (J1 - 3.0) * (J1 - 3.0) + rho * (J4 - 1.0) * (J4 - 1.0));\n\tdouble sed = (k1/k2)*(exp(w) - 1.0);\n\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEHGOCoronary.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n\n//-----------------------------------------------------------------------------\n// Constitutive formulation from:\n// Holzapfel, et.a., \"Determination of layer-specific mechanical properties of human\n// coronary arteries with nonatherosclerotic intimal thickening\n// and related constitutive modeling\", Am J Physiol Heart Circ Physiol 289\nclass FEHGOCoronary : public FEUncoupledMaterial\n{\npublic:\n\tFEHGOCoronary(FEModel* pfem);\n\npublic:\n\tdouble\t\tm_rho;\n\tdouble\t\tm_k1;\n\tdouble\t\tm_k2;\n\n\tFEVec3dValuator* m_fiber;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n\nprotected:\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEHolmesMow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEHolmesMow.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEHolmesMow, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\n\tADD_PARAMETER(m_b, FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta\")->setLongName(\"power exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FEHolmesMow::Validate()\n{\n\tif (FEElasticMaterial::Validate() == false) return false;\n\t\n\t// Lame coefficients\n\tlam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tmu  = 0.5*m_E/(1+m_v);\n\tHa = lam + 2*mu;\t\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEHolmesMow::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\t\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen(); //(F*F.transpose()).sym();\n\tmat3ds b2 = b.sqr();\n\tmat3ds identity(1.,1.,1.,0.,0.,0.);\n\n\t// calculate invariants of B\n\tdouble I1 = b.tr();\n\tdouble I2 = (I1*I1 - b2.tr())/2.;\n\tdouble I3 = b.det();\n\n\t// Exponential term\n\tdouble eQ = exp(m_b*((2*mu-lam)*(I1-3) + lam*(I2-3))/Ha)/pow(I3,m_b);\n\t\n\t// calculate stress\n\tmat3ds s = 0.5*detFi*eQ*((2*mu+lam*(I1-1))*b - lam*b2 - Ha*identity);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEHolmesMow::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\t\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen(); //(F*F.transpose()).sym();\n\tmat3ds b2 = b.sqr();\n\tmat3ds identity(1.,1.,1.,0.,0.,0.);\n\t\n\t// calculate invariants of B\n\tdouble I1 = b.tr();\n\tdouble I2 = (I1*I1 - b2.tr())*0.5;\n\tdouble I3 = b.det();\n\t\n\t// Exponential term\n\tdouble eQ = exp(m_b*((2*mu-lam)*(I1-3) + lam*(I2-3))/Ha)/pow(I3,m_b);\n\t\n\t// calculate stress\n\tmat3ds s = 0.5*detFi*eQ*((2*mu+lam*(I1-1))*b - lam*b2 - Ha*identity);\n\t\n\t// calculate elasticity tensor\n\ttens4ds c = 4.*m_b/Ha*detF/eQ*dyad1s(s) \n\t+ detFi*eQ*(lam*(dyad1s(b) - dyad4s(b)) + Ha*dyad4s(identity));\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEHolmesMow::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen(); //(F*F.transpose()).sym();\n\tmat3ds b2 = b.sqr();\n    \n\t// calculate invariants of B\n\tdouble I1 = b.tr();\n\tdouble I2 = (I1*I1 - b2.tr())/2.;\n\tdouble I3 = b.det();\n    \n\t// Exponential term\n\tdouble eQ = exp(m_b*((2*mu-lam)*(I1-3) + lam*(I2-3))/Ha)/pow(I3,m_b);\n\t\n\t// calculate strain energy density\n\tdouble sed = Ha/(4*m_b)*(eQ - 1);\n\t\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEHolmesMow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEHolmesMow : public FEElasticMaterial\n{\npublic:\n\tFEHolmesMow(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\t\t\npublic:\n\tdouble\tm_E;\t//!< Young's modulus\n\tdouble\tm_v;\t//!< Poisson's ratio\n\tdouble\tm_b;\t//!< Exponential stiffening coefficient\n\tdouble\tlam;\t//!< first Lame coefficient\n\tdouble\tmu;\t\t//!< second Lame coefficient\n\tdouble\tHa;\t\t//!< aggregate modulus\n\t\t\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\t\t\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization and checking\n\tbool Validate() override;\n\t\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEHolmesMowUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEHolmesMowUC.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEHolmesMowUC, FEUncoupledMaterial)\n    ADD_PARAMETER(m_mu, FE_RANGE_GREATER(0.0), \"mu\")->setLongName(\"shear modulus\");\n    ADD_PARAMETER(m_b, FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta\")->setLongName(\"power exponent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nmat3ds FEHolmesMowUC::DevStress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate left Cauchy-Green tensor\n    mat3ds bt = pt.DevLeftCauchyGreen();\n    \n    // calculate invariants of B\n    double I1 = bt.tr();\n    \n    // Exponential term\n    double eQ = exp(m_b*(I1-3));\n    \n    // calculate stress\n    mat3ds st = bt*(m_mu*eQ/pt.m_J);\n    \n    return st.dev();\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEHolmesMowUC::DevTangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate left Cauchy-Green tensor\n    mat3ds bt = pt.DevLeftCauchyGreen();\n    \n    // calculate invariants of B\n    double I1 = bt.tr();\n    \n    // Exponential term\n    double eQ = exp(m_b*(I1-3));\n    \n    // calculate stress\n    mat3ds st = bt*(m_mu*eQ/pt.m_J);\n    \n    // calculate identity tensor\n    mat3dd I(1);\n\n    // calculate elasticity tensor\n    tens4ds ct = dyad1s(bt)*(2*m_b*m_mu*eQ/pt.m_J);\n    \n    // This is the final value of the elasticity tensor\n    tens4ds IxI = dyad1s(I);\n    tens4ds I4  = dyad4s(I);\n    \n    ct += - 1./3.*(ddots(ct,IxI) - IxI*(ct.tr()/3.))\n    + 2./3.*((I4-IxI/3.)*st.tr()-dyad1s(st.dev(),I));\n    \n    return ct;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEHolmesMowUC::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate left Cauchy-Green tensor\n    mat3ds bt = pt.DevLeftCauchyGreen();\n    \n    // calculate invariants of B\n    double I1 = bt.tr();\n    \n    // Exponential term\n    double eQ = exp(m_b*(I1-3));\n    \n    // calculate strain energy density\n    double sed = m_mu/(2*m_b)*(eQ-1);\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEHolmesMowUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEHolmesMowUC : public FEUncoupledMaterial\n{\npublic:\n    FEHolmesMowUC(FEModel* pfem) : FEUncoupledMaterial(pfem) {}\n    \npublic:\n    double    m_mu;   //!< shear modulus\n    double    m_b;    //!< Exponential stiffening coefficient\n    \npublic:\n    //! calculate stress at material point\n    virtual mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    virtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    virtual double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEHolzapfelGasserOgden.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEHolzapfelGasserOgden.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEHolzapfelGasserOgden, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"c\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_k1   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_k2   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k2\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_kappa, FE_RANGE_CLOSED(0.0, 1.0/3.0), \"kappa\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_gdeg , \"gamma\")->setUnits(UNIT_DEGREE);\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEHolzapfelGasserOgden::FEHolzapfelGasserOgden(FEModel* pfem) : FEUncoupledMaterial(pfem) \n{ \n    m_npmodel = 3; \n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress\nmat3ds FEHolzapfelGasserOgden::DevStress(FEMaterialPoint& mp)\n{\n    double c = m_c(mp);\n    double k1 = m_k1(mp);\n    double k2 = m_k2(mp);\n    double kappa = m_kappa(mp);\n    double g = m_gdeg(mp)*PI/180;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n\n   \n    // Evaluate the distortional deformation gradient\n\tdouble Jm13 = pow(J, -1. / 3.);\n\tmat3d F = pt.m_F*Jm13;\n    \n    // calculate deviatoric left Cauchy-Green tensor: b = F*Ft\n    mat3ds b = pt.DevLeftCauchyGreen();\n    mat3ds C = pt.DevRightCauchyGreen();\n    double I1 = C.tr();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // Copy the local element basis directions to n\n\tvec3d n[2];\n    n[0].x = Q[0][0]; n[0].y = Q[1][0]; n[0].z = Q[2][0];\n    n[1].x = Q[0][1]; n[1].y = Q[1][1]; n[1].z = Q[2][1];\n    \n    // Evaluate the structural direction in the current configuration\n    double cg = cos(g); double sg = sin(g);\n\tvec3d ar[2],a[2];\n    ar[0] = n[0]*cg + n[1]*sg; a[0] = F*ar[0];\n    ar[1] = n[0]*cg - n[1]*sg; a[1] = F*ar[1];\n    \n    // Evaluate the ground matrix stress\n    mat3ds s = c*b;\n    \n    // Evaluate the structural tensors in the current configuration\n    // and the fiber strains and stress contributions\n    double I40 = ar[0]*(C*ar[0]);\n    double E0 = kappa*(I1-3) + (1-3*kappa)*(I40-1);\n    if (E0 >= 0) {\n        mat3ds h0 = kappa*b + (1-3*kappa)*dyad(a[0]);\n        s += h0*(2.*k1*E0*exp(k2*E0*E0));\n    }\n\n    double I41 = ar[1]*(C*ar[1]);\n    double E1 = kappa*(I1-3) + (1-3*kappa)*(I41-1);\n    if (E1 >= 0) {\n        mat3ds h1 = kappa*b + (1-3*kappa)*dyad(a[1]);\n        s += h1*(2.*k1*E1*exp(k2*E1*E1));\n    }\n\n    return s.dev() / J;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric tangent\ntens4ds FEHolzapfelGasserOgden::DevTangent(FEMaterialPoint& mp)\n{\n    double c = m_c(mp);\n    double k1 = m_k1(mp);\n    double k2 = m_k2(mp);\n    double kappa = m_kappa(mp);\n    double g = m_gdeg(mp)*PI/180;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n    \n    // Evaluate the distortional deformation gradient\n\tdouble Jm13 = pow(J, -1. / 3.);\n    mat3d F = pt.m_F*Jm13;\n    \n    // calculate deviatoric left Cauchy-Green tensor: b = F*Ft\n    mat3ds b = pt.DevLeftCauchyGreen();\n    mat3ds C = pt.DevRightCauchyGreen();\n    double I1 = C.tr();\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // Copy the local element basis directions to n\n\tvec3d n[2];\n    n[0].x = Q[0][0]; n[0].y = Q[1][0]; n[0].z = Q[2][0];\n    n[1].x = Q[0][1]; n[1].y = Q[1][1]; n[1].z = Q[2][1];\n    \n    // Evaluate the structural direction in the current configuration\n    double cg = cos(g); double sg = sin(g);\n    vec3d ar[2],a[2];\n    ar[0] = n[0]*cg + n[1]*sg; a[0] = F*ar[0];\n    ar[1] = n[0]*cg - n[1]*sg; a[1] = F*ar[1];\n\n    // Evaluate the ground matrix stress\n    mat3ds s = c*b;\n    \n    // Evaluate the structural tensors in the current configuration\n    // and the fiber strains and stress contributions\n    double I40 = ar[0]*(C*ar[0]);\n    double E0 = kappa*(I1-3) + (1-3*kappa)*(I40-1);\n    mat3ds h0;\n    if (E0 >= 0) {\n        h0 = kappa*b + (1-3*kappa)*dyad(a[0]);\n        s += h0*(2.*k1*E0*exp(k2*E0*E0));\n    }\n\n    double I41 = ar[1]*(C*ar[1]);\n    double E1 = kappa*(I1-3) + (1-3*kappa)*(I41-1);\n    mat3ds h1;\n    if (E1 >= 0) {\n        h1 = kappa*b + (1-3*kappa)*dyad(a[1]);\n        s += h1*(2.*k1*E1*exp(k2*E1*E1));\n    }\n\n\tmat3ds sbar = s.dev();\n    \n    // Evaluate the elasticity tensor\n    mat3dd I(1);\n    tens4ds IxI = dyad1s(I);\n    tens4ds I4  = dyad4s(I);\n    tens4ds ce = ((I4 - IxI/3.)*s.tr()-dyad1s(sbar,I))*(2./3.);\n\tif (E0 >= 0) ce += dyad1s(h0.dev())*(4.*k1*(1 + 2 * k2*E0*E0)*exp(k2*E0*E0));\n\tif (E1 >= 0) ce += dyad1s(h1.dev())*(4.*k1*(1 + 2 * k2*E1*E1)*exp(k2*E1*E1));\n    \n    return ce / J;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress\ndouble FEHolzapfelGasserOgden::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double c = m_c(mp);\n    double k1 = m_k1(mp);\n    double k2 = m_k2(mp);\n    double kappa = m_kappa(mp);\n    double g = m_gdeg(mp)*PI/180;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    vec3d n[3];\t\t\t// local element basis directions\n    mat3ds h[2];\t\t// structural tensor in current configuration\n    double E[2];\t\t// fiber strain\n    \n    // calculate deviatoric right Cauchy-Green tensor\n    mat3ds C = pt.DevRightCauchyGreen();\n    double I1 = C.tr();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // Copy the local element basis directions to n\n    n[0].x = Q[0][0]; n[0].y = Q[1][0]; n[0].z = Q[2][0];\n    n[1].x = Q[0][1]; n[1].y = Q[1][1]; n[1].z = Q[2][1];\n    \n    // Evaluate the structural direction in the current configuration\n    double cg = cos(g); double sg = sin(g);\n    vec3d ar[2];\n    ar[0] = n[0]*cg + n[1]*sg;\n    ar[1] = n[0]*cg - n[1]*sg;\n\n    // Evaluate the ground matrix strain energy density\n    double sed = 0.5*c*(I1 - 3);\n    \n    // Evaluate the structural tensors in the current configuration\n    // and the fiber strains and stress contributions\n    double I40 = ar[0]*(C*ar[0]);\n    E[0] = kappa*(I1-3) + (1-3*kappa)*(I40-1);\n    if (E[0] >= 0)\n        sed += 0.5*k1/k2*(exp(k2*E[0]*E[0])-1);\n    double I41 = ar[1]*(C*ar[1]);\n    E[1] = kappa*(I1-3) + (1-3*kappa)*(I41-1);\n    if (E[1] >= 0)\n        sed += 0.5*k1/k2*(exp(k2*E[1]*E[1])-1);\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEHolzapfelGasserOgden.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModelParam.h>\n\nclass FEHolzapfelGasserOgden : public FEUncoupledMaterial\n{\npublic:\n    FEParamDouble  m_c;         // neo-Hookean c coefficient\n    FEParamDouble  m_k1,m_k2;   // fiber material constants\n    FEParamDouble  m_kappa;     // structure coefficient\n    FEParamDouble  m_gdeg;      // fiber angle in degrees\n\npublic:\n    FEHolzapfelGasserOgden(FEModel* pfem);\n    \n    //! calculate deviatoric stress at material point\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! calculate deviatoric tangent stiffness at material point\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! calculate deviatoric strain energy density at material point\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEHolzapfelUnconstrained.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEHolzapfelUnconstrained.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEHolzapfelUnconstrained, FEElasticMaterial)\n    ADD_PARAMETER(m_c    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"c\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_k1   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k1\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_k2   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k2\")->setUnits(UNIT_NONE);\n    ADD_PARAMETER(m_kappa, FE_RANGE_CLOSED(0.0, 1.0/3.0), \"kappa\")->setUnits(UNIT_NONE);\n    ADD_PARAMETER(m_gdeg , \"gamma\")->setUnits(UNIT_DEGREE);\n    ADD_PARAMETER(m_k    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\")->setUnits(UNIT_PRESSURE);\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Calculates the stress\nmat3ds FEHolzapfelUnconstrained::Stress(FEMaterialPoint& mp)\n{\n    double c = m_c(mp);\n    double k1 = m_k1(mp);\n    double k2 = m_k2(mp);\n    double kappa = m_kappa(mp);\n    double g = m_gdeg(mp)*PI/180;\n    double k = m_k(mp);\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n    \n    \n    // calculate left Cauchy-Green tensor: b = F*Ft\n    mat3ds b = pt.LeftCauchyGreen();\n    mat3ds C = pt.RightCauchyGreen();\n    double I1 = C.tr();\n    \n    // get the local coordinate systems\n    mat3d Q = GetLocalCS(mp);\n    \n    // Copy the local element basis directions to n\n    vec3d n[2];\n    n[0].x = Q[0][0]; n[0].y = Q[1][0]; n[0].z = Q[2][0];\n    n[1].x = Q[0][1]; n[1].y = Q[1][1]; n[1].z = Q[2][1];\n    \n    // Evaluate the structural direction in the current configuration\n    double cg = cos(g); double sg = sin(g);\n    vec3d ar[2],a[2];\n    ar[0] = n[0]*cg + n[1]*sg; a[0] = pt.m_F*ar[0];\n    ar[1] = n[0]*cg - n[1]*sg; a[1] = pt.m_F*ar[1];\n    \n    // Evaluate the ground matrix stress\n\tmat3dd I(1.0);\n    mat3ds s = c*(b-I) + I*((J*J-1.0)*(k*0.5));\n    \n    // Evaluate the structural tensors in the current configuration\n    // and the fiber strains and stress contributions\n    double I40 = ar[0]*(C*ar[0]);\n    double E0 = kappa*(I1-3) + (1-3*kappa)*(I40-1);\n    if (E0 >= 0) {\n        mat3ds h0 = kappa*b + (1-3*kappa)*dyad(a[0]);\n        s += h0*(2.*k1*E0*exp(k2*E0*E0));\n    }\n    \n    double I41 = ar[1]*(C*ar[1]);\n    double E1 = kappa*(I1-3) + (1-3*kappa)*(I41-1);\n    if (E1 >= 0) {\n        mat3ds h1 = kappa*b + (1-3*kappa)*dyad(a[1]);\n        s += h1*(2.*k1*E1*exp(k2*E1*E1));\n    }\n    \n    return s / J;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the tangent\ntens4ds FEHolzapfelUnconstrained::Tangent(FEMaterialPoint& mp)\n{\n    double c = m_c(mp);\n    double k1 = m_k1(mp);\n    double k2 = m_k2(mp);\n    double kappa = m_kappa(mp);\n    double g = m_gdeg(mp)*PI/180;\n    double k = m_k(mp);\n\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n    \n    // calculate left Cauchy-Green tensor: b = F*Ft\n    mat3ds b = pt.LeftCauchyGreen();\n    mat3ds C = pt.RightCauchyGreen();\n    double I1 = C.tr();\n    \n    // get the local coordinate systems\n    mat3d Q = GetLocalCS(mp);\n    \n    // Copy the local element basis directions to n\n    vec3d n[2];\n    n[0].x = Q[0][0]; n[0].y = Q[1][0]; n[0].z = Q[2][0];\n    n[1].x = Q[0][1]; n[1].y = Q[1][1]; n[1].z = Q[2][1];\n    \n    // Evaluate the structural direction in the current configuration\n    double cg = cos(g); double sg = sin(g);\n    vec3d ar[2],a[2];\n    ar[0] = n[0]*cg + n[1]*sg; a[0] = pt.m_F*ar[0];\n    ar[1] = n[0]*cg - n[1]*sg; a[1] = pt.m_F*ar[1];\n    \n    // Evaluate the structural tensors in the current configuration\n    // and the fiber strains and stress contributions\n    double I40 = ar[0]*(C*ar[0]);\n    double E0 = kappa*(I1-3) + (1-3*kappa)*(I40-1);\n    mat3ds h0;\n    if (E0 >= 0) {\n        h0 = kappa*b + (1-3*kappa)*dyad(a[0]);\n    }\n    \n    double I41 = ar[1]*(C*ar[1]);\n    double E1 = kappa*(I1-3) + (1-3*kappa)*(I41-1);\n    mat3ds h1;\n    if (E1 >= 0) {\n        h1 = kappa*b + (1-3*kappa)*dyad(a[1]);\n    }\n    \n    // Evaluate the elasticity tensor\n    mat3dd I(1);\n    tens4ds IoI = dyad4s(I);\n    tens4ds IxI = dyad1s(I);\n    tens4ds ce = IoI*(2*c-(J*J-1.0)*k) + IxI*(J*J*k);\n    if (E0 >= 0) ce += dyad1s(h0)*(4.*k1*(1 + 2 * k2*E0*E0)*exp(k2*E0*E0));\n    if (E1 >= 0) ce += dyad1s(h1)*(4.*k1*(1 + 2 * k2*E1*E1)*exp(k2*E1*E1));\n    \n    return ce / J;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FEHolzapfelUnconstrained::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double c = m_c(mp);\n    double k1 = m_k1(mp);\n    double k2 = m_k2(mp);\n    double kappa = m_kappa(mp);\n    double g = m_gdeg(mp)*PI/180;\n    double k = m_k(mp);\n\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n    \n    vec3d n[3];            // local element basis directions\n    mat3ds h[2];        // structural tensor in current configuration\n    double E[2];        // fiber strain\n    \n    // calculate right Cauchy-Green tensor\n    mat3ds C = pt.RightCauchyGreen();\n    double I1 = C.tr();\n    \n    // get the local coordinate systems\n    mat3d Q = GetLocalCS(mp);\n    \n    // Copy the local element basis directions to n\n    n[0].x = Q[0][0]; n[0].y = Q[1][0]; n[0].z = Q[2][0];\n    n[1].x = Q[0][1]; n[1].y = Q[1][1]; n[1].z = Q[2][1];\n    \n    // Evaluate the structural direction in the current configuration\n    double cg = cos(g); double sg = sin(g);\n    vec3d ar[2];\n    ar[0] = n[0]*cg + n[1]*sg;\n    ar[1] = n[0]*cg - n[1]*sg;\n    \n    // Evaluate the ground matrix strain energy density\n    double sed = 0.5*c*(I1 - 3) - c*log(J) + (k/2)*((J*J-1)/2 - log(J));\n    \n    // Evaluate the structural tensors in the current configuration\n    // and the fiber strains and stress contributions\n    double I40 = ar[0]*(C*ar[0]);\n    E[0] = kappa*(I1-3) + (1-3*kappa)*(I40-1);\n    if (E[0] >= 0)\n        sed += 0.5*k1/k2*(exp(k2*E[0]*E[0])-1);\n    double I41 = ar[1]*(C*ar[1]);\n    E[1] = kappa*(I1-3) + (1-3*kappa)*(I41-1);\n    if (E[1] >= 0)\n        sed += 0.5*k1/k2*(exp(k2*E[1]*E[1])-1);\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEHolzapfelUnconstrained.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\nclass FEHolzapfelUnconstrained: public FEElasticMaterial\n{\npublic:\n    FEParamDouble  m_c;         // neo-Hookean c coefficient\n    FEParamDouble  m_k1,m_k2;   // fiber material constants\n    FEParamDouble  m_kappa;     // structure coefficient\n    FEParamDouble  m_gdeg;      // fiber angle in degrees\n    FEParamDouble  m_k;         // buklk modulus\n    \npublic:\n    FEHolzapfelUnconstrained(FEModel* pfem) : FEElasticMaterial(pfem) {}\n    \n    //! calculate deviatoric stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate deviatoric tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate deviatoric strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEHuiskesSupply.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEHuiskesSupply.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEHuiskesSupply, FESolidSupply)\n\tADD_PARAMETER(m_B, \"B\");\n\tADD_PARAMETER(m_k, \"k\")->setUnits(UNIT_SPECIFIC_ENERGY);\n    ADD_PARAMETER(m_D, FE_RANGE_GREATER_OR_EQUAL(0.0), \"D\")->setUnits(UNIT_LENGTH)->setLongName(\"sensor distance\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEHuiskesSupply::FEHuiskesSupply(FEModel* pfem) : FESolidSupply(pfem)\n{\n\tm_B = m_k = m_D = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization\nbool FEHuiskesSupply::Init()\n{\n    // get neighboring elements for given proximity\n    if (m_D > 0) {\n        double mult = 4;    //! multiplier of characteristic distance, such that exp(-mult) << 1\n        FEMesh& mesh = GetFEModel()->GetMesh();\n        if (m_topo.Create(&mesh) == false)\n        {\n            feLogError(\"Failed building mesh topo.\");\n            return false;\n        }\n        feLogInfo(\"Evaluating element proximity...\");\n        m_EPL.assign(mesh.Elements(), std::vector<int>());\n        for (int i=0; i< mesh.Elements(); ++i) {\n            std::vector<int> epl = m_topo.ElementProximityList(i, m_D*mult);\n            m_EPL[i] = epl;\n        }\n        feLogInfo(\"Done.\");\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Solid supply\ndouble FEHuiskesSupply::Supply(FEMaterialPoint& pt)\n{\n\tFERemodelingMaterialPoint* rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n\tdouble rhor = rpt->m_rhor;\n\tdouble sed = rpt->m_sed;\n\tdouble rhorhat = m_B*(sed/rhor - m_k);\n    \n    if (m_D > 0) {\n        FEMesh& mesh = GetFEModel()->GetMesh();\n        int ie = pt.m_elem->GetLocalID();\n        int NEPL = (int)m_EPL[ie].size();\n#pragma omp parallel for shared (NEPL)\n        for (int i=0; i<NEPL; ++i) {\n            int je = m_EPL[ie][i];\n            if (je > -1) {\n                FEElement* el = mesh.Element(je);\n                for (int k=0; k<el->GaussPoints(); ++k) {\n                    FEMaterialPoint& mp = *(el->GetMaterialPoint(k));\n                    double d = (pt.m_rt - mp.m_rt).unit();\n                    rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n                    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                    rhorhat += exp(-d/m_D)*m_B*(rpt->m_sed/rpt->m_rhor - m_k);\n                }\n            }\n        }\n    }\n\n    return rhorhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solid supply with respect to strain\nmat3ds FEHuiskesSupply::Tangent_Supply_Strain(FEMaterialPoint &pt)\n{\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFERemodelingMaterialPoint* rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n    mat3ds ruhat = et.m_s*(m_B/rpt->m_rhor);\n\n    if (m_D > 0) {\n        FEMesh& mesh = GetFEModel()->GetMesh();\n        int ie = pt.m_elem->GetLocalID();\n        int NEPL = (int)m_EPL[ie].size();\n#pragma omp parallel for shared (NEPL)\n        for (int i=0; i<NEPL; ++i) {\n            int je = m_EPL[ie][i];\n            if (je > -1) {\n                FEElement* el = mesh.Element(je);\n                for (int k=0; k<el->GaussPoints(); ++k) {\n                    FEMaterialPoint& mp = *(el->GetMaterialPoint(k));\n                    double d = (pt.m_rt - mp.m_rt).unit();\n                    rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n                    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                    ruhat += et.m_s*(exp(-d/m_D)*m_B/rpt->m_rhor);\n                }\n            }\n        }\n    }\n\n    return ruhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solid supply with respect to referential density\ndouble FEHuiskesSupply::Tangent_Supply_Density(FEMaterialPoint &mp)\n{\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n    double rhor = rpt.m_rhor;\n    double sed = rpt.m_sed;\n    double dsed = rpt.m_dsed;\n\treturn (dsed - sed/rhor)*m_B/rhor;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEHuiskesSupply.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FERemodelingElasticMaterial.h\"\n#include <FECore/FEMeshTopo.h>\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a constant solute supply\n\nclass FEHuiskesSupply :\tpublic FESolidSupply\n{\npublic:\n\t//! constructor\n\tFEHuiskesSupply(FEModel* pfem);\n\t\n    //! initialization\n    bool Init() override;\n    \n\t//! solid supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of solute supply with respect to strain\n\tmat3ds Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! tangent of solute supply with respect to referential density\n\tdouble Tangent_Supply_Density(FEMaterialPoint& mp) override;\n\t\npublic:\n\tdouble\tm_B;\t\t\t//!< mass supply coefficient\n\tdouble\tm_k;\t\t\t//!< specific strain energy at homeostasis\n    double  m_D;            //!< characteristic sensor distance\n\nprivate:\n    std::vector<std::vector<int>>    m_EPL; //!< list of element proximity lists\n    FEMeshTopo      m_topo;                 //!< mesh topology;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEIdealGasPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEIdealGasPressure.h\"\n#include \"FEBioMech.h\"\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEIdealGasPressure, FESurfaceLoad)\n\tADD_PARAMETER(m_initialPressure, \"P0\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_bsymm, \"symmetric_stiffness\");\n\tADD_PARAMETER(m_bshellb, \"shell_bottom\");\nEND_FECORE_CLASS()\n\nFEIdealGasPressure::FEIdealGasPressure(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n\tm_initialPressure = 0.0;\n\tm_bsymm = true;\n\tm_bshellb = false;\n\tm_currentPressure = 0;\n\tm_currentVolume = 0;\n}\n\nbool FEIdealGasPressure::Init()\n{\n\tFESurface& surf = GetSurface();\n\tsurf.SetShellBottom(m_bshellb);\n\n\t// get the degrees of freedom\n\tm_dof.Clear();\n\tif (m_bshellb == false)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\telse\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t}\n\tif (m_dof.IsEmpty()) return false;\n\n\treturn FESurfaceLoad::Init();\n}\n\nvoid FEIdealGasPressure::Activate()\n{\n\tm_initialVolume = CalculateSurfaceVolume(GetSurface());\n\tm_currentVolume = m_initialVolume;\n\t\n\tm_currentPressure = m_initialPressure;\n\n\tfeLogDebug(\"initial volume: %lg\\n\", m_initialVolume);\n\t\n\tFESurfaceLoad::Activate();\n}\n\nvoid FEIdealGasPressure::Update()\n{\n\tm_currentVolume = CalculateSurfaceVolume(GetSurface());\n\tm_currentPressure = m_initialPressure * (m_initialVolume / m_currentVolume);\n\n\tfeLogDebug(\"volume: %lg, pressure: %lg\\n\", m_currentVolume, m_currentPressure);\n\n\tFESurfaceLoad::Update();\n}\n\nvoid FEIdealGasPressure::LoadVector(FEGlobalVector& R)\n{\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadVector(R, m_dof, false, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\n\t\t// evaluate pressure at this material point\n\t\tdouble P = m_currentPressure;\n\t\tif (m_bshellb) P = -P;\n\n\t\tdouble J = (pt.dxr ^ pt.dxs).norm();\n\n\t\t// force vector\n\t\tvec3d N = (pt.dxr ^ pt.dxs); N.unit();\n\t\tvec3d t = N * P;\n\n\t\tdouble H_u = dof_a.shape;\n\n\t\tval[0] = H_u * t.x * J;\n\t\tval[1] = H_u * t.y * J;\n\t\tval[2] = H_u * t.z * J;\n\t\t});\n}\n\nvoid FEIdealGasPressure::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& kab) {\n\n\t\t// evaluate pressure at this material point\n\t\tdouble P = m_currentPressure;\n\t\tif (m_bshellb) P = -P;\n\n\t\tdouble H_i = dof_a.shape;\n\t\tdouble Gr_i = dof_a.shape_deriv_r;\n\t\tdouble Gs_i = dof_a.shape_deriv_s;\n\n\t\tdouble H_j = dof_b.shape;\n\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\tvec3d vab(0, 0, 0);\n\t\tif (m_bsymm)\n\t\t\tvab = (mp.dxr * (H_j * Gs_i - H_i * Gs_j) - mp.dxs * (H_j * Gr_i - H_i * Gr_j)) * 0.5 * P;\n\t\telse\n\t\t\tvab = (mp.dxs * Gr_j - mp.dxr * Gs_j) * (P * H_i);\n\n\t\tmat3da K(vab);\n\t\tkab.set(0, 0, K);\n\t\t});\n}\n"
  },
  {
    "path": "FEBioMech/FEIdealGasPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! The pressure surface is a surface domain that sustains pressure boundary\n//! conditions\n//!\nclass FEIdealGasPressure : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n\tFEIdealGasPressure(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\tvoid Activate() override;\n\n\tvoid Update() override;\n\npublic:\n\tdouble GetCurrentPressure() const { return m_currentPressure; }\n\npublic:\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\tdouble\tm_initialPressure;\t//!< initial pressure value\n\tbool\tm_bsymm;\t\t\t//!< use symmetric formulation\n\tbool\tm_bshellb;\t\t\t//!< flag for prescribing pressure on shell bottom\n\nprivate:\n\tdouble\tm_initialVolume;\n\tdouble\tm_currentVolume;\n\tdouble\tm_currentPressure;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEInSituStretchGradient.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEInSituStretchGradient.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEInSituStretchGradient, FEPrestrainGradient)\n\tADD_PARAMETER(m_lam , \"stretch\"  );\n\tADD_PARAMETER(m_biso, \"isochoric\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEInSituStretchGradient::FEInSituStretchGradient(FEModel* pfem) : FEPrestrainGradient(pfem) \n{\n\tm_lam = 1.0;\n\tm_biso = true;\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FEInSituStretchGradient::Init()\n{\n\tm_fiber = GetFiberProperty();\n\tif (m_fiber == nullptr) return false;\n\treturn FEPrestrainGradient::Init();\n}\n\n//-----------------------------------------------------------------------------\nFEVec3dValuator* FEInSituStretchGradient::GetFiberProperty()\n{\n\t// make sure the parent material is a prestrain material\n\tFEPrestrainMaterial* prestrainMat = dynamic_cast<FEPrestrainMaterial*>(GetParent());\n\tif (prestrainMat == nullptr) return nullptr;\n\n\t// get the elastic property\n\tFEElasticMaterial* elasticMat = prestrainMat->GetElasticMaterial();\n\n\t// make sure it has a fiber property\n\tFEVec3dValuator* fiberProp = dynamic_cast<FEVec3dValuator*>(elasticMat->GetProperty(\"fiber\"));\n\tif (fiberProp == nullptr) return nullptr;\n\n\t// make sure it's a vector map\n\treturn fiberProp;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInSituStretchGradient::Serialize(DumpStream& ar)\n{\n\tFEPrestrainGradient::Serialize(ar);\n\tif ((ar.IsShallow() == false) && ar.IsLoading())\n\t{\n\t\tm_fiber = GetFiberProperty();\n\t\tassert(m_fiber);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEInSituStretchGradient::Prestrain(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the in-situ stretch\n\tdouble lam = m_lam(mp);\n\n\t// get the fiber vector\n\tvec3d a0 = m_fiber->unitVector(mp);\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d at = Q * a0;\n\n\t// set-up local uni-axial stretch tensor\n\tdouble l = lam;\n\tdouble li = (m_biso ? 1.0/sqrt(l) : 1.0);\n\t\n\t// setup prestrain tensor: Fp = lam*A + li*(I - A);\n\tmat3dd I(1.0);\n\tmat3ds A = dyad(at);\n\tmat3d F_bar = A * lam + (I - A)*li;\n\n\treturn F_bar;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInSituStretchGradient::Initialize(const mat3d& F, FEMaterialPoint& mp)\n{\n/*\tFEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left polar decomposition\n\tmat3d R;\n\tmat3ds V;\n\tF.left_polar(V, R);\n\n\t// get the fiber vector\n\tmat3d Q = GetLocalCS(mp);\n\tvec3d a0 = Q*vec3d(1,0,0);\n\tvec3d a1 = F*a0;\n\n\t// calculate the fiber stretch\n\tdouble lam = a1.unit();\n\n\t// assign it to the pre_stretch\n\tptis->m_lam = lam;\n\n\t// setup orthogonal system\n\tvec3d b(0,0,1);\n\tif (b*a1 > 0.9) b = vec3d(0,1,0);\n\tvec3d a3 = a1^b;\n\tvec3d a2 = a3^a1;\n\n\tQ(0,0) = a1.x; Q(0,1) = a2.x; Q(0,2) = a3.x;\n\tQ(1,0) = a1.y; Q(1,1) = a2.y; Q(1,2) = a3.y;\n\tQ(2,0) = a1.z; Q(2,1) = a2.z; Q(2,2) = a3.z;\n//\tpt->m_Q = Q;\n*/\n}\n"
  },
  {
    "path": "FEBioMech/FEInSituStretchGradient.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEPreStrainElastic.h\"\n\n//-----------------------------------------------------------------------------\n// A pre-strain gradient constructed from fiber stretches\nclass FEInSituStretchGradient : public FEPrestrainGradient\n{\npublic:\n\tFEInSituStretchGradient(FEModel* pfem);\n\n\tbool Init() override;\n\n\tmat3d Prestrain(FEMaterialPoint& mp) override;\n\n\tvoid Initialize(const mat3d& F, FEMaterialPoint& mp) override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFEVec3dValuator* GetFiberProperty();\n\npublic:\n\tFEParamDouble\tm_lam;\t//!< in-situ stretch\n\tbool\t\t\tm_biso;\t//!< isochoric generator option\n\n\tFEVec3dValuator*\tm_fiber;\t// fiber property of the elastic material.\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEIncompNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEIncompNeoHookean.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIncompNeoHookean, FEUncoupledMaterial)\n\tADD_PARAMETER(m_G, FE_RANGE_GREATER(0.0), \"G\")->setUnits(UNIT_PRESSURE)->setLongName(\"shear modulus\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEIncompNeoHookean::FEIncompNeoHookean(FEModel* pfem) : FEUncoupledMaterial(pfem) \n{\n\tm_G = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric stress\nmat3ds FEIncompNeoHookean::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// shear modulus\n\tdouble G = m_G(mp);\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// calculate deviatoric stress\n\treturn (B - mat3dd(B.tr()/3.))*(G/pow(J, 5.0/3.0));\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric tangent\ntens4ds FEIncompNeoHookean::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// shear modulus\n\tdouble G = m_G(mp);\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\n\t// left cauchy-green matrix (i.e. the 'b' matrix)\n\tmat3ds B = pt.LeftCauchyGreen();\n\n\t// trace of b\n\tdouble Ib = B.tr();\n\n\tdouble muJ = G/pow(J, 5.0/3.0);\n\n\tmat3ds I(1,1,1,0,0,0);\t// Identity\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxI = dyad1s(B, I); // = BxI + IxB\n\n\treturn (I4*Ib -BxI + IxI*(Ib/3))*(2.0*muJ/3.0);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric strain energy density\ndouble FEIncompNeoHookean::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // shear modulus\n    double G = m_G(mp);\n    \n    mat3ds C = pt.DevRightCauchyGreen();\n    double I1 = C.tr();\n    \n    // calculate deviatoric stress\n    return (I1-3)*G/2;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEIncompNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Incompressible Neo-Hookean material\n\nclass FEBIOMECH_API FEIncompNeoHookean : public FEUncoupledMaterial\n{\npublic:\n\tFEIncompNeoHookean(FEModel* pfem);\n\npublic:\n\tFEParamDouble\tm_G;\t//!< Shear modulus\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n    //! calculate deviatoric strain energy density at material point\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEInitialDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEInitialDisplacement.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FEInitialDisplacement, FENodalIC)\n    ADD_PARAMETER(m_u0, \"value\")->setUnits(UNIT_LENGTH);\nEND_FECORE_CLASS();\n\nFEInitialDisplacement::FEInitialDisplacement(FEModel* fem) : FENodalIC(fem)\n{\n    m_u0 = vec3d(0, 0, 0);\n}\n\n// set the initial value\nvoid FEInitialDisplacement::SetValue(const vec3d& u0)\n{\n    m_u0 = u0;\n}\n\n// initialization\nbool FEInitialDisplacement::Init()\n{\n    FEDofList dofs(GetFEModel());\n    if (dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT)) == false) return false;\n    SetDOFList(dofs);\n    return true;\n}\n\n// return the values for node i\nvoid FEInitialDisplacement::GetNodalValues(int inode, std::vector<double>& values)\n{\n    assert(values.size() == 3);\n    \n    const FENodeSet& nset = *GetNodeSet();\n    const FENode& node = *nset.Node(inode);\n    \n    FEMaterialPoint mp;\n    mp.m_r0 = node.m_r0;\n    mp.m_index = inode;\n    \n    vec3d u0 = m_u0(mp);\n    \n    values[0] = u0.x;\n    values[1] = u0.y;\n    values[2] = u0.z;\n}\n"
  },
  {
    "path": "FEBioMech/FEInitialDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FEInitialCondition.h>\n#include \"febiomech_api.h\"\n#include <FECore/FEModelParam.h>\n\nclass FEBIOMECH_API FEInitialDisplacement : public FENodalIC\n{\npublic:\n    FEInitialDisplacement(FEModel* fem);\n    \n    bool Init() override;\n    \n    // set the initial value\n    void SetValue(const vec3d& v0);\n    \n    // return the values for node i\n    void GetNodalValues(int inode, std::vector<double>& values) override;\n    \nprivate:\n    FEParamVec3        m_u0;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEInitialPreStrain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEInitialPreStrain.h\"\n#include <FECore/FEDomain.h>\n#include <FECore/FEMesh.h>\n#include \"FEConstPrestrain.h\"\n\n\nBEGIN_FECORE_CLASS(FEInitialPreStrain, FEInitialCondition)\n\tADD_PARAMETER(m_binit , \"init\" );\n\tADD_PARAMETER(m_breset, \"reset\");\nEND_FECORE_CLASS();\n\nFEInitialPreStrain::FEInitialPreStrain(FEModel* pfem) : FEInitialCondition(pfem)\n{\n\tm_binit = true;\n\tm_breset = true;\n}\n\nvoid FEInitialPreStrain::Activate()\n{\n\tFEInitialCondition::Activate();\n\n\tFEMesh& mesh = GetMesh();\n\n\t// loop over all the domains\n\tint ND = mesh.Domains();\n\tfor (int i=0; i<ND; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\n\t\t// see if this material is the right type\n\t\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(dom.GetMaterial());\n\t\tif (pmat)\n\t\t{\n\t\t\t// get the prestrain gradient\n\t\t\tFEPrestrainGradient* Fp = pmat->PrestrainGradientProperty();\n\t\t\tif (Fp)\n\t\t\t{\n\t\t\t\t// loop over the elements\n\t\t\t\tint NE = dom.Elements();\n\t\t\t\tfor (int i=0; i<NE; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\n\t\t\t\t\t// loop over the integration points\n\t\t\t\t\tconst int nint = el.GaussPoints();\n\t\t\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\t\tFEElasticMaterialPoint* pt = mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\t\t\tFEPrestrainMaterialPoint& pp = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\t\t\t\t// get the deformation gradient\n\t\t\t\t\t\tmat3d& F = pt->m_F;\n\n\t\t\t\t\t\t// initialize prestrain material point data\n\t\t\t\t\t\tif (m_binit) Fp->Initialize(F, mp);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmat3d Fc = F*pp.PrestrainCorrection();\n\t\t\t\t\t\t\tpp.setPrestrainCorrection(Fc);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// reset deformation gradient\n\t\t\t\t\t\tF.unit();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// reset the nodal coordinates\n\tif (m_breset)\n\t{\n\t\tint dofX = GetDOFIndex(\"x\");\n\t\tint dofY = GetDOFIndex(\"y\");\n\t\tint dofZ = GetDOFIndex(\"z\");\n\t\tint NN = mesh.Nodes();\n\t\tfor (int i=0; i<NN; ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tnode.m_r0 = node.m_rt;\n\t\t\tnode.set(dofX, 0.0);\n\t\t\tnode.set(dofY, 0.0);\n\t\t\tnode.set(dofZ, 0.0);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEInitialPreStrain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\n//-----------------------------------------------------------------------------\n// This initial condition grabs the fiber stretch and initializes the pre_stretch\n// variable with that value. \nclass FEInitialPreStrain : public FEInitialCondition\n{\npublic:\n\tFEInitialPreStrain(FEModel* pfem);\n\n\tvoid Activate() override;\n\nprivate:\n\tbool\tm_binit;\n\tbool\tm_breset;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEInitialRigidKinematics.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEInitialRigidKinematics.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FEInitialRigidKinematics, FENodalIC)\n\tADD_PARAMETER(m_v, \"velocity\")->setUnits(UNIT_VELOCITY);\n\tADD_PARAMETER(m_w, \"angular_velocity\")->setUnits(UNIT_ANGULAR_VELOCITY);\n\tADD_PARAMETER(m_c, \"center_of_rotation\")->setUnits(UNIT_LENGTH);\nEND_FECORE_CLASS();\n\nFEInitialRigidKinematics::FEInitialRigidKinematics(FEModel* fem) : FENodalIC(fem)\n{\n\tm_v = vec3d(0, 0, 0);\n\tm_w = vec3d(0, 0, 0);\n\tm_c = vec3d(0, 0, 0);\n}\n\n// initialization\nbool FEInitialRigidKinematics::Init()\n{\n\tFEDofList dofs(GetFEModel());\n\tif (dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY)) == false) return false;\n\tSetDOFList(dofs);\n\treturn FENodalIC::Init();\n}\n\n// return the values for node i\nvoid FEInitialRigidKinematics::GetNodalValues(int inode, std::vector<double>& values)\n{\n\tassert(values.size() == 3);\n\n\tconst FENodeSet& nset = *GetNodeSet();\n\tconst FENode& node = *nset.Node(inode);\n\n\tvec3d r = node.m_rt;\n\tvec3d v = m_v + (m_w ^ (r - m_c));\n\n\tvalues[0] = v.x;\n\tvalues[1] = v.y;\n\tvalues[2] = v.z;\n}\n"
  },
  {
    "path": "FEBioMech/FEInitialRigidKinematics.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n#include \"febiomech_api.h\"\n\n//! Component that assigns initial velocity based on rigid body kinematics\nclass FEBIOMECH_API FEInitialRigidKinematics : public FENodalIC\n{\npublic:\n\tFEInitialRigidKinematics(FEModel* fem);\n\n\tbool Init() override;\n\n\t// return the values for node i\n\tvoid GetNodalValues(int inode, std::vector<double>& values) override;\n\nprivate:\n\tvec3d\tm_v;\t// initial linear velocity\n\tvec3d\tm_w;\t// initial angular velocity\t\n\tvec3d\tm_c;\t// center of rotation\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEInitialVelocity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEInitialVelocity.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FEInitialVelocity, FENodalIC)\n\tADD_PARAMETER(m_v0, \"value\")->setUnits(UNIT_VELOCITY);\nEND_FECORE_CLASS();\n\nFEInitialVelocity::FEInitialVelocity(FEModel* fem) : FENodalIC(fem)\n{\n\tm_v0 = vec3d(0, 0, 0);\n}\n\n// set the initial value\nvoid FEInitialVelocity::SetValue(const vec3d& v0)\n{\n\tm_v0 = v0;\n}\n\n// initialization\nbool FEInitialVelocity::Init()\n{\n\tFEDofList dofs(GetFEModel());\n\tif (dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY)) == false) return false;\n\tSetDOFList(dofs);\n\treturn FENodalIC::Init();\n}\n\n// return the values for node i\nvoid FEInitialVelocity::GetNodalValues(int inode, std::vector<double>& values)\n{\n\tassert(values.size() == 3);\n\n\tconst FENodeSet& nset = *GetNodeSet();\n\tconst FENode& node = *nset.Node(inode);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = inode;\n\n\tvec3d v0 = m_v0(mp);\n\n\tvalues[0] = v0.x;\n\tvalues[1] = v0.y;\n\tvalues[2] = v0.z;\n}\n\n//=========================================================================\nBEGIN_FECORE_CLASS(FEInitialShellVelocity, FENodalIC)\n\tADD_PARAMETER(m_v0, \"value\");\nEND_FECORE_CLASS();\n\nFEInitialShellVelocity::FEInitialShellVelocity(FEModel* fem) : FENodalIC(fem)\n{\n\tm_v0 = vec3d(0, 0, 0);\n}\n\n// set the initial value\nvoid FEInitialShellVelocity::SetValue(const vec3d& v0)\n{\n\tm_v0 = v0;\n}\n\n// initialization\nbool FEInitialShellVelocity::Init()\n{\n\tFEDofList dofs(GetFEModel());\n\tif (dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY)) == false) return false;\n\tSetDOFList(dofs);\n\treturn FENodalIC::Init();\n}\n\n// return the values for node i\nvoid FEInitialShellVelocity::GetNodalValues(int inode, std::vector<double>& values)\n{\n\tassert(values.size() == 3);\n\n\tconst FENodeSet& nset = *GetNodeSet();\n\tconst FENode& node = *nset.Node(inode);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = inode;\n\n\tvec3d v0 = m_v0(mp);\n\n\tvalues[0] = v0.x;\n\tvalues[1] = v0.y;\n\tvalues[2] = v0.z;\n}\n"
  },
  {
    "path": "FEBioMech/FEInitialVelocity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include <FECore/FEInitialCondition.h>\n#include \"febiomech_api.h\"\n#include <FECore/FEModelParam.h>\n\nclass FEBIOMECH_API FEInitialVelocity : public FENodalIC\n{\npublic:\n\tFEInitialVelocity(FEModel* fem);\n\n\tbool Init() override;\n\n\t// set the initial value\n\tvoid SetValue(const vec3d& v0);\n\n\t// return the values for node i\n\tvoid GetNodalValues(int inode, std::vector<double>& values) override;\n\nprivate:\n\tFEParamVec3\t\tm_v0;\n\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEBIOMECH_API FEInitialShellVelocity : public FENodalIC\n{\npublic:\n\tFEInitialShellVelocity(FEModel* fem);\n\n\tbool Init() override;\n\n\t// set the initial value\n\tvoid SetValue(const vec3d& v0);\n\n\t// return the values for node i\n\tvoid GetNodalValues(int inode, std::vector<double>& values) override;\n\nprivate:\n\tFEParamVec3\t\tm_v0;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEIsoHencky.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEIsoHencky.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIsoHencky, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEIsoHencky::FEIsoHencky(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\n//-----------------------------------------------------------------------------\nmat3ds FEIsoHencky::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = pt.m_J;\n\tdouble lnJ = log(J);\n\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// calculate left Hencky tensor\n\tmat3ds h = pt.LeftHencky();\n\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\n\t// Identity\n\tmat3dd I(1);\n\n\t// calculate stress\n\tmat3ds s = h*(2*mu/J) + I*(lam*lnJ/J);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEIsoHencky::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\n\tdouble lam1 = lam / J;\n\tdouble mu1  = mu / J;\n\t\n    mat3dd I(1);\n\n\treturn dyad1s(I)*lam1 + dyad4s(I)*(2*mu1);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEIsoHencky::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble J = pt.m_J;\n\tdouble lnJ = log(J);\n\t\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// calculate right Hencky tensor\n\tmat3ds H = pt.RightHencky();\n\tdouble I1 = H.tr();\n    double I2 = H.dotdot(H);\n\t\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\t\n\tdouble sed = (lam/2)*pow(I1,2) + mu*I2;\n\t\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEIsoHencky.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Neo Hookean material\n\n//! Implementation of an isotropic Hencky hyperelastic material.\nclass FEBIOMECH_API FEIsoHencky : public FEElasticMaterial\n{\npublic:\n    FEIsoHencky(FEModel* pfem);\n\npublic:\n\tFEParamDouble\t\tm_E;\t//!< Young's modulus\n\tFEParamDouble\t\tm_v;\t//!< Poisson's ratio\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEIsotropicElastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEIsotropicElastic.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIsotropicElastic, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nmat3ds FEIsotropicElastic::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d &F = pt.m_F;\n\tdouble Ji = 1.0 / pt.m_J;\n\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// lame parameters\n\tdouble lam = Ji*(v*E/((1+v)*(1-2*v)));\n\tdouble mu  = Ji*(0.5*E/(1+v));\n\n\t// calculate left Cauchy-Green tensor (ie. b-matrix)\n\tmat3ds b = pt.LeftCauchyGreen();\n\n\t// calculate trace of Green-Lagrance strain tensor\n\tdouble trE = 0.5*(b.tr()-3);\n\n\t// calculate square of b-matrix\n\t// (we commented out the matrix components we do not need)\n\tmat3ds b2 = b.sqr();\n\n\t// calculate stress\n\tmat3ds s = b*(lam*trE - mu) + b2*mu;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEIsotropicElastic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble Ji = 1.0 / pt.m_J;\n\n\t// lame parameters\n\tdouble lam = Ji*(v*E/((1+v)*(1-2*v)));\n\tdouble mu  = Ji*(0.5*E/(1+v));\n\n\t// left cauchy-green matrix (i.e. the 'b' matrix)\n\tmat3ds b = pt.LeftCauchyGreen();\n\n\treturn dyad1s(b)*lam + dyad4s(b)*(2.0*mu);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEIsotropicElastic::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble mE = m_E(mp);\n\tdouble mv = m_v(mp);\n\n    mat3ds E = (pt.RightCauchyGreen() - mat3dd(1))/2;\n    \n    double lam = mv*mE/((1+mv)*(1-2*mv));\n\tdouble mu  = 0.5*mE/(1+mv);\n\n    double trE = E.tr();\n    double Enorm = E.norm();\n    \n    double sed = lam*trE*trE/2 + mu*Enorm*Enorm;\n    \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEIsotropicElastic::PK2Stress(FEMaterialPoint& pt, const mat3ds E)\n{\n\tdouble mE = m_E(pt);\n\tdouble mv = m_v(pt);\n\n    // lame parameters\n    double lam = (mv*mE/((1+mv)*(1-2*mv)));\n    double mu  = (0.5*mE/(1+mv));\n    \n    // Identity\n    mat3dd I(1);\n    \n    // calculate stress\n    mat3ds S = I*(E.tr()*lam) + E*(2*mu);\n    \n    return S;\n}\n\n//-----------------------------------------------------------------------------\ntens4dmm FEIsotropicElastic::MaterialTangent(FEMaterialPoint& pt, const mat3ds E)\n{\n\tdouble mE = m_E(pt);\n\tdouble mv = m_v(pt);\n\n    // lame parameters\n    double lam = (mv*mE/((1+mv)*(1-2*mv)));\n    double mu  = (0.5*mE/(1+mv));\n    \n    // Identity\n    mat3dd I(1);\n    \n    tens4dmm c = dyad1s(I)*lam + dyad4s(I)*(2*mu);\n    \n    return c;\n}\n"
  },
  {
    "path": "FEBioMech/FEIsotropicElastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEIsotropicElastic : public FEElasticMaterial\n{\npublic:\n\tFEIsotropicElastic(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\npublic:\n\tFEParamDouble\tm_E;\t//!< Young's modulus\n\tFEParamDouble\tm_v;\t//!< Poisson's ratio\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! calculate the 2nd Piola-Kirchhoff stress at material point\n    mat3ds PK2Stress(FEMaterialPoint& pt, const mat3ds E) override;\n    \n    //! calculate material tangent stiffness at material point\n    tens4dmm MaterialTangent(FEMaterialPoint& pt, const mat3ds E) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEIsotropicLeeSacks.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEIsotropicLeeSacks.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIsotropicLeeSacks, FEElasticMaterial)\n\tADD_PARAMETER(m_c0, \"c0\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c1, \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_k , \"k\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_tangent_scale , \"tangent_scale\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEIsotropicLeeSacks::FEIsotropicLeeSacks(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_c0 = 0.0;\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\tm_k = 0.0;\n\tm_tangent_scale = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FEIsotropicLeeSacks::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate right Cauchy-Green tensor and Lagrange strain tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble J = pt.m_J;\n\tdouble I1 = b.tr();\n\n\t// evaluate volumetric strain energy\n\tdouble lnJ = log(J);\n\tdouble U = 0.5 * m_k * pow(lnJ, 2);\n\n\t// Evaluate exp(Q)\n\tdouble Q = m_c2 * (I1 - 3.0) * (I1 - 3.0);\n\tdouble eQ = exp(Q);\n\n\t// Evaluate the strain energy density\n\tdouble sed = m_c0*(0.5*(I1 - 3) - lnJ) + 0.5*m_c1*(eQ - 1.0) + U;\n\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress\nmat3ds FEIsotropicLeeSacks::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate deviatoric\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble J = pt.m_J;\n\tdouble I1 = b.tr();\n\n\t// evaluate volumetric strain energy\n\tdouble p = m_k * log(J)/J;\n\tmat3dd I(1.0);\n\n\t// Evaluate exp(Q)\n\tdouble Q = m_c2 * (I1 - 3.0) * (I1 - 3.0);\n\tdouble eQ = exp(Q);\n\n\tmat3ds s = I * p + (b - I)*(m_c0/J) + b*(2.0*m_c1*m_c2*(I1 - 3)*eQ/J);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric tangent\ntens4ds FEIsotropicLeeSacks::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// apply tangent scale\n\tdouble c0 = m_c0 * m_tangent_scale;\n\n\t// calculate deviatoric\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble J = pt.m_J;\n\tdouble I1 = b.tr();\n\n\t// we'll need these tensors\n\tmat3dd I(1.0);\n\ttens4ds IoI = dyad4s(I);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds bxb = dyad1s(b);\n\n\t// evaluate volumetric tangent\n\tdouble lnJ = log(J);\n\tdouble p = m_k * lnJ / J;\n\tdouble pJ = m_k * (1.0 - lnJ) / (J*J);\n\n\ttens4ds cp = IxI * (p + J * pJ) + IoI * (2.0 * c0/J -2.0 * p);\n\n\t// evaluate strain tangent\n\tdouble Q = m_c2 * (I1 - 3.0) * (I1 - 3.0);\n\tdouble eQ = exp(Q);\n\n\ttens4ds cw = bxb*(4.0*m_c1*m_c2*eQ*(1.0+2.0*m_c2*(I1 - 3.0)*(I1 - 3.0)) / J);\n\n\t// put it together\n\ttens4ds c = cw + cp;\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEIsotropicLeeSacks.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElasticMaterial.h\"\n\nclass FEIsotropicLeeSacks : public FEElasticMaterial\n{\npublic:\n\tFEIsotropicLeeSacks(FEModel* pfem);\n\n\t//! calculate deviatoric stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n\npublic:\n\tdouble\tm_c0;\n\tdouble\tm_c1;\n\tdouble\tm_c2;\n\tdouble\tm_k;\n\tdouble\tm_tangent_scale;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEIsotropicLeeSacksUncoupled.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEIsotropicLeeSacksUncoupled.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEIsotropicLeeSacksUncoupled, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c0, \"c0\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c1, \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_tangent_scale, \"tangent_scale\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEIsotropicLeeSacksUncoupled::FEIsotropicLeeSacksUncoupled(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_c0 = 0.0;\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\tm_tangent_scale = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FEIsotropicLeeSacksUncoupled::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor and invariants\n\tmat3ds b = pt.DevLeftCauchyGreen();\n\tdouble I1 = b.tr();\n\n\t// Evaluate exp(Q)\n\tdouble Q = m_c2 * (I1 - 3.0) * (I1 - 3.0);\n\tdouble eQ = exp(Q);\n\n\t// Evaluate the strain energy density\n\tdouble sed = 0.5*m_c0*(I1 - 3.0) + 0.5*m_c1*(eQ - 1.0);\n\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress\nmat3ds FEIsotropicLeeSacksUncoupled::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate left Cauchy-Green tensor and invariants\n\tmat3ds b = pt.DevLeftCauchyGreen();\n\tdouble I1 = b.tr();\n\tdouble J = pt.m_J;\n\n\t// Evaluate exp(Q)\n\tdouble Q = m_c2 * (I1 - 3.0) * (I1 - 3.0);\n\tdouble eQ = exp(Q);\n\n\t// evaluate deviatoric stress\n\tmat3ds T = b * (m_c0 + 2.0 * m_c1 * m_c2 * (I1 - 3) * eQ);\n\tmat3ds s = T.dev() / J;\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric tangent\ntens4ds FEIsotropicLeeSacksUncoupled::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// apply tangent scale\n\tdouble c0 = m_c0 * m_tangent_scale;\n\n\t// calculate left Cauchy-Green tensor and invariants\n\tmat3ds b = pt.DevLeftCauchyGreen();\n\tdouble I1 = b.tr();\n\tdouble J = pt.m_J;\n\n\t// Evaluate exp(Q)\n\tdouble Q = m_c2 * (I1 - 3.0) * (I1 - 3.0);\n\tdouble eQ = exp(Q);\n\tdouble g = 4.0 * m_c1 * m_c2 * eQ * (1.0 + 2.0 * m_c2 * (I1 - 3.0) * (I1 - 3.0));\n\n\t// we'll need these tensors\n\tmat3dd I(1.0);\n\ttens4ds IoI = dyad4s(I);\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds bxb = dyad1s(b);\n\n\t// evaluate stress tensor T = F*S*F^t\n\tmat3ds T = b * (c0 + 2.0 * m_c1 * m_c2 * (I1 - 3) * eQ);\n\n\t// evaluate tangents\n\ttens4ds cT = (IoI - IxI / 3.0) * (2.0 * T.tr() / 3.0) - dyad1s(T.dev(), I)*(2.0/3.0);\n\n\ttens4ds cw = bxb * g;\n\tmat3ds chat = b * (g * I1);\n\tdouble cbar = g * I1 * I1;\n\n\ttens4ds c = cT + cw - dyad1s(chat, I) / 3.0 + IxI*(cbar / 9.0);\n\n\treturn c / J;\n}\n"
  },
  {
    "path": "FEBioMech/FEIsotropicLeeSacksUncoupled.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\nclass FEIsotropicLeeSacksUncoupled : public FEUncoupledMaterial\n{\npublic:\n\tFEIsotropicLeeSacksUncoupled(FEModel* pfem);\n\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n\npublic:\n\tdouble\tm_c0;\n\tdouble\tm_c1;\n\tdouble\tm_c2;\n\tdouble\tm_tangent_scale;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEKinematicGrowth.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"FEKinematicGrowth.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material point\n//\nFEMaterialPointData* FEKinematicMaterialPoint::Copy()\n{\n    FEKinematicMaterialPoint* pt = new FEKinematicMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEKinematicMaterialPoint::Init()\n{\n    FEMaterialPointData::Init();\n    \n    // intialize data\n    m_rhor = 0;\n    m_Fe = mat3dd(1);\n    m_Je = 1;\n    m_Fg = mat3dd(1);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEKinematicMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n    FEMaterialPointData::Update(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEKinematicMaterialPoint::Serialize(DumpStream& ar)\n{\n    if (ar.IsSaving())\n    {\n        ar << m_Fe << m_Fg << m_rhor;\n    }\n    else\n    {\n        ar >> m_Fe >> m_Fg >> m_rhor;\n    }\n    FEMaterialPointData::Serialize(ar);\n}\n\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEKinematicGrowth, FEElasticMaterial)\n    ADD_PROPERTY(m_pBase, \"elastic\");\n    ADD_PROPERTY(m_pGrowth, \"growth\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEKinematicGrowth::FEKinematicGrowth(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_pBase = nullptr;\n    m_pGrowth = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point\nFEMaterialPointData* FEKinematicGrowth::CreateMaterialPointData()\n{\n    FEElasticMaterial* pme = GetBaseMaterial();\n    FEMaterialPointData* ep = pme->CreateMaterialPointData();\n    return new FEKinematicMaterialPoint(ep);\n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEKinematicGrowth::Init()\n{\n    FEUncoupledMaterial* m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pBase);\n    if (m_pMat != nullptr) {\n        feLogError(\"Elastic material should not be of type uncoupled\");\n        return false;\n    }\n    \n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Returns the Cauchy stress\nmat3ds FEKinematicGrowth::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEKinematicMaterialPoint& pt = *mp.ExtractData<FEKinematicMaterialPoint>();\n\n\t// overwrite the elastic material point deformation gradient information\n\t// to match elastic deformation gradient of kinematic growth material\n\tmat3d F = pe.m_F;\n\tdouble J = pe.m_J;\n\tpe.m_F = pt.m_Fe;\n\tpe.m_J = pt.m_Je;\n\n    // evaluate stress\n    FEElasticMaterial* emat = GetBaseMaterial();\n    mat3ds s = emat->Stress(mp);\n\n\t// restore deformation gradient\n\tpe.m_F = F;\n\tpe.m_J = J;\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Returns the spatial tangent\ntens4ds FEKinematicGrowth::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEKinematicMaterialPoint& pt = *mp.ExtractData<FEKinematicMaterialPoint>();\n\n\t// overwrite the elastic material point deformation gradient information\n\t// to match elastic deformation gradient of kinematic growth material\n\tmat3d F = pe.m_F;\n\tdouble J = pe.m_J;\n\tpe.m_F = pt.m_Fe;\n\tpe.m_J = pt.m_Je;\n\n    // evaluate tangent\n    FEElasticMaterial* emat = GetBaseMaterial();\n    tens4ds c = emat->Tangent(mp);\n\n\t// restore deformation gradient\n\tpe.m_F = F;\n\tpe.m_J = J;\n\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Returns the strain energy density\ndouble FEKinematicGrowth::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEKinematicMaterialPoint& pt = *mp.ExtractData<FEKinematicMaterialPoint>();\n\n\t// overwrite the elastic material point deformation gradient information\n\t// to match elastic deformation gradient of kinematic growth material\n\tmat3d F = pe.m_F;\n\tdouble J = pe.m_J;\n\tpe.m_F = pt.m_Fe;\n\tpe.m_J = pt.m_Je;\n\n    // evaluate sed\n    FEElasticMaterial* emat = GetBaseMaterial();\n    double sed = emat->StrainEnergyDensity(mp);\n\n\t// restore deformation gradient\n\tpe.m_F = F;\n\tpe.m_J = J;\n\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! update material point at each iteration\nvoid FEKinematicGrowth::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    // Get the growth tensor inverse\n    FEGrowthTensor* gmat = GetGrowthMaterial();\n    // material axes\n    mat3d Q = GetLocalCS(mp);\n    // get the fiber vector in local coordinates\n    vec3d fiber = gmat->m_fiber->unitVector(mp);\n    // convert to global coordinates\n    vec3d a0 = Q * fiber;\n    \n    FEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Get the deformation gradient and evaluate elastic deformation\n    mat3d Fg = gmat->GrowthTensor(mp, a0);\n    mat3d Fe = pe.m_F*gmat->GrowthTensorInverse(mp, a0);\n\n    // extract Kinematic growth material point\n    FEKinematicMaterialPoint& pt = *mp.ExtractData<FEKinematicMaterialPoint>();\n    \n    pt.m_Fg = Fg;\n    pt.m_Fe = Fe;\n    pt.m_Je = Fe.det();\n    pt.m_rhor = GetBaseMaterial()->Density(mp)*GetGrowthMaterial()->GrowthDensity(mp, a0);\n}\n"
  },
  {
    "path": "FEBioMech/FEKinematicGrowth.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2019 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include \"FEGrowthTensor.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FEModelComponent.h>\n\n//-----------------------------------------------------------------------------\n//! Define a material point that stores the multiplicative decomposition\n//! of the deformation gradient and the solid referential density\n//\nclass FEKinematicMaterialPoint : public FEMaterialPointData\n{\npublic:\n    FEKinematicMaterialPoint(FEMaterialPointData *pt) : FEMaterialPointData(pt) {}\n    \n    FEMaterialPointData* Copy() override;\n    \n    void Init() override;\n    void Update(const FETimeInfo& timeInfo) override;\n    \n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    mat3d   m_Fe;       //!< elastic deformation\n    double  m_Je;       //!< elastic relative volume\n    mat3d   m_Fg;       //!< growth deformation\n    double  m_rhor;     //!< solid referential density\n};\n\n//-----------------------------------------------------------------------------\n//! Material class that implements Kinematic growth model.\n//\nclass FEKinematicGrowth : public FEElasticMaterial\n{\npublic:\n    FEKinematicGrowth(FEModel* pfem);\n    \n    //! Initialization routine\n    bool Init() override;\n    \n    //! get the elastic base material\n    FEElasticMaterial* GetBaseMaterial() { return m_pBase; }\n    \n    //! get the elastic base material\n    FEGrowthTensor* GetGrowthMaterial() { return m_pGrowth; }\n    \n    //! Returns the Cauchy stress\n    mat3ds Stress(FEMaterialPoint& mp) override;\n    \n    //! Returns the spatial tangent\n    tens4ds Tangent(FEMaterialPoint& mp) override;\n    \n    //! Returns the strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // update material point at each iteration\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \nprivate:\n    FEElasticMaterial*  m_pBase;        //!< pointer to elastic solid material\n    FEGrowthTensor*     m_pGrowth;      //!< pointer to growth tensor\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FELinearTrussDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELinearTrussDomain.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBodyForce.h\"\n#include \"FEBioMech.h\"\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FELinearTrussDomain, FETrussDomain)\n\tADD_PARAMETER(m_a0, \"cross_sectional_area\");\nEND_FECORE_CLASS();\n\nFELinearTrussDomain::FELinearTrussDomain(FEModel* pfem) : FETrussDomain(pfem), FEElasticDomain(pfem), m_dofU(pfem), m_dofV(pfem), m_dofR(pfem)\n{\n\tm_pMat = nullptr;\n\tm_a0 = 0.0;\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t}\n}\n\nconst FEDofList& FELinearTrussDomain::GetDOFList() const\n{\n\treturn m_dofU;\n}\n\nvoid FELinearTrussDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFETrussDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FETrussMaterial*>(pmat);\n\tassert(m_pMat);\n}\n\nbool FELinearTrussDomain::Init()\n{\n\tif (m_a0 <= 0.0)\n\t{\n\t\tfeLogError(\"Cross sectional area of \\\"%s\\\" must be positive.\", GetName().c_str());\n\t\treturn false;\n\t}\n\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tel.m_a0 = m_a0;\n\t}\n\t\n\treturn FETrussDomain::Init();\n}\n\nvoid FELinearTrussDomain::Reset()\n{\n\tForEachMaterialPoint([](FEMaterialPoint& mp) {\n\t\tmp.Init();\n\t});\n}\n\nvoid FELinearTrussDomain::UnpackLM(FEElement &el, vector<int>& lm)\n{\n\tlm.resize(12);\n\tFENode& n1 = m_pMesh->Node(el.m_node[0]);\n\tFENode& n2 = m_pMesh->Node(el.m_node[1]);\n\tlm[0] = n1.m_ID[m_dofU[0]];\n\tlm[1] = n1.m_ID[m_dofU[1]];\n\tlm[2] = n1.m_ID[m_dofU[2]];\n\tlm[3] = n2.m_ID[m_dofU[0]];\n\tlm[4] = n2.m_ID[m_dofU[1]];\n\tlm[5] = n2.m_ID[m_dofU[2]];\n\n\t// rigid rotational dofs\n\tlm[6 ] = n1.m_ID[m_dofR[0]];\n\tlm[7 ] = n1.m_ID[m_dofR[1]];\n\tlm[8 ] = n1.m_ID[m_dofR[2]];\n\tlm[9 ] = n2.m_ID[m_dofR[0]];\n\tlm[10] = n2.m_ID[m_dofR[1]];\n\tlm[11] = n2.m_ID[m_dofR[2]];\n}\n\nvoid FELinearTrussDomain::Activate()\n{\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FELinearTrussDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tForEachMaterialPoint([&](FEMaterialPoint& mp) {\n\t\tmp.Update(timeInfo);\n\t});\n}\n\nvoid FELinearTrussDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tmatrix me(2, 2); me.zero();\n\t\tElementMassMatrix(el, me);\n\n\t\t// The element mass matrix calculated above only evaluates the 2x2 mass matrix\n\t\t// Thus, we need to inlate it to a 6x6 mass matrix.\n\t\tFEElementMatrix Me(6, 6); Me.zero();\n\t\tfor (int i=0; i<2; ++i)\n\t\t{ \n\t\t\tfor (int j = 0; j < 2; ++j)\n\t\t\t{\n\t\t\t\tMe[3 * i    ][3 * j    ] = scale*me[i][j];\n\t\t\t\tMe[3 * i + 1][3 * j + 1] = scale*me[i][j];\n\t\t\t\tMe[3 * i + 2][3 * j + 2] = scale*me[i][j];\n\t\t\t}\n\t\t}\n\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tMe.SetNodes(el.m_node);\n\t\tMe.SetIndices(lm);\n\t\tLS.Assemble(Me);\n\t}\n}\n\nvoid FELinearTrussDomain::ElementMassMatrix(FETrussElement& el, matrix& me)\n{\n\tdouble L = el.m_L0;\n\tdouble A = el.m_a0;\n\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tdouble rho = m_pMat->Density(mp);\n\n\tme[0][0] = rho * A * L / 3.0; me[0][1] = rho * A * L / 6.0;\n\tme[1][0] = rho * A * L / 6.0; me[1][1] = rho * A * L / 3.0;\n}\n\nvoid FELinearTrussDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tvector<int> lm;\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tFEElementMatrix ke(el);\n\t\tElementStiffness(el, ke);\n\t\tUnpackLM(el, lm);\n\t\tke.SetNodes(el.m_node);\n\t\tke.SetIndices(lm);\n\t\tLS.Assemble(ke);\n\t}\n}\n\nvoid FELinearTrussDomain::ElementStiffness(FETrussElement& el, matrix& ke)\n{\n\t// intial length\n\tdouble L = el.m_L0;\n\n\t// current length\n\tdouble l = el.m_Lt;\n\n\t// get the elastic tangent\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFETrussMaterialPoint& pt = *mp.ExtractData<FETrussMaterialPoint>();\n\tdouble E = m_pMat->Tangent(mp);\n\n\t// element initial volume\n\tdouble V = L*el.m_a0;\n\n\t// Kirchhoff Stress\n\tdouble tau = pt.m_tau;\n\n\t// scalar stiffness\n\tdouble k = V / (l*l)*( E - 2*tau);\n\n\t// axial force T = s*a = t*V/l\n\tdouble T = tau*V/l;\n\n\tvec3d n = GetTrussAxisVector(el);\n\n\t// calculate the tangent matrix\n\tke.resize(6, 6);\n\n\tFETimeInfo& ti = GetFEModel()->GetTime();\n\tdouble alphaf = ti.alphaf;\n\n\tke[0][0] = ke[3][3] = (k*n.x*n.x + T/l)*alphaf;\n\tke[1][1] = ke[4][4] = (k*n.y*n.y + T/l)*alphaf;\n\tke[2][2] = ke[5][5] = (k*n.z*n.z + T/l)*alphaf;\n\n\tke[0][1] = ke[1][0] = ke[3][4] = ke[4][3] = k*n.x*n.y*alphaf;\n\tke[1][2] = ke[2][1] = ke[4][5] = ke[5][4] = k*n.y*n.z*alphaf;\n\tke[0][2] = ke[2][0] = ke[3][5] = ke[5][3] = k*n.x*n.z*alphaf;\n\n\tke[0][3] = ke[3][0] = -ke[0][0]; ke[0][4] = ke[4][0] = -ke[0][1]; ke[0][5] = ke[5][0] = -ke[0][2];\n\tke[1][3] = ke[3][1] = -ke[1][0]; ke[1][4] = ke[4][1] = -ke[1][1]; ke[1][5] = ke[5][1] = -ke[1][2];\n\tke[2][3] = ke[3][2] = -ke[2][0]; ke[2][4] = ke[4][2] = -ke[2][1]; ke[2][5] = ke[5][2] = -ke[2][2];\n}\n\nvoid FELinearTrussDomain::InternalForces(FEGlobalVector& R)\n{\n\t// element force vector\n\tvector<double> fe;\n\tvector<int> lm;\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tElementInternalForces(el, fe);\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\nvoid FELinearTrussDomain::ElementInternalForces(FETrussElement& el, vector<double>& fe)\n{\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFETrussMaterialPoint& pt = *(mp.ExtractData<FETrussMaterialPoint>());\n\n\tFETimeInfo& ti = GetFEModel()->GetTime();\n\tdouble alphaf = ti.alphaf;\n\n\tvec3d rt[2];\n\tfor (int i = 0; i < 2; ++i)\n\t{\n\t\tFENode& nd = m_pMesh->Node(el.m_node[i]);\n\t\trt[i] = nd.m_rt * alphaf + nd.m_rp * (1 - alphaf);\n\t}\n\tvec3d n = rt[1] - rt[0];\n\tn.unit();\n\n\t// get the element's Kirchhoff stress\n\tdouble tau = pt.m_tau;\n\n\t// initial length\n\tdouble L = el.m_L0;\n\n\t// current length\n\tdouble l = el.m_Lt;\n\n\t// initial volume\n\tdouble V = L*el.m_a0;\n\n\t// calculate nodal forces\n\tfe.resize(6);\n\tfe[0] = tau*V/l*n.x;\n\tfe[1] = tau*V/l*n.y;\n\tfe[2] = tau*V/l*n.z;\n\tfe[3] = -fe[0];\n\tfe[4] = -fe[1];\n\tfe[5] = -fe[2];\n}\n\n//! Calculates inertial forces for dynamic analyses\nvoid FELinearTrussDomain::InertialForces(FEGlobalVector& R, vector<double>& F)\n{\n\tvector<double> fe;\n\tvector<int> lm;\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tElementInertialForces(el, fe);\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\nvoid FELinearTrussDomain::ElementInertialForces(FETrussElement& el, vector<double>& fe)\n{\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFETrussMaterialPoint& pt = *(mp.ExtractData<FETrussMaterialPoint>());\n\n\tFETimeInfo& ti = GetFEModel()->GetTime();\n\tdouble alpham = ti.alpham;\n\n\t// get the nodal accelerations\n\tvec3d a[2];\n\tint neln = el.Nodes();\n\tfor (int i = 0; i < neln; ++i)\n\t{\n\t\tFENode& node = m_pMesh->Node(el.m_node[i]);\n\t\ta[i] = node.m_ap* (1 - alpham) + node.m_at* alpham;\n\t}\n\n\t// calculate element contribution to inertial force\n\tmatrix me(2, 2);\n\tElementMassMatrix(el, me);\n\n\tfe.resize(6, 0.0);\n\tfe[0] = -(me[0][0] * a[0].x + me[0][1] * a[1].x);\n\tfe[1] = -(me[0][0] * a[0].y + me[0][1] * a[1].y);\n\tfe[2] = -(me[0][0] * a[0].z + me[0][1] * a[1].z);\n\tfe[3] = -(me[1][0] * a[0].x + me[1][1] * a[1].x);\n\tfe[4] = -(me[1][0] * a[0].y + me[1][1] * a[1].y);\n\tfe[5] = -(me[1][0] * a[0].z + me[1][1] * a[1].z);\n}\n\n//! calculate body force on a truss domain\nvoid FELinearTrussDomain::BodyForce(FEGlobalVector& R, FEBodyForce& bf)\n{\n\tvector<double> fe(6);\n\tvector<int> lm;\n\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tzero(fe);\n\t\tint nint = el.GaussPoints();\n\t\tdouble* w = el.GaussWeights();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\tdouble density = m_pMat->Density(mp);\n\n\t\t\t// get the force\n\t\t\tvec3d f = bf.force(mp);\n\n\t\t\t// get element shape functions\n\t\t\tdouble* H = el.H(n);\n\n\t\t\t// get the initial Jacobian\n\t\t\tdouble J0 = el.m_a0 * el.m_L0 / 2.0;\n\n\t\t\t// set integrand\n\t\t\tfe[0] -= H[0] * density * f.x * J0 * w[n];\n\t\t\tfe[1] -= H[0] * density * f.y * J0 * w[n];\n\t\t\tfe[2] -= H[0] * density * f.z * J0 * w[n];\n\t\t\tfe[3] -= H[1] * density * f.x * J0 * w[n];\n\t\t\tfe[4] -= H[1] * density * f.y * J0 * w[n];\n\t\t\tfe[5] -= H[1] * density * f.z * J0 * w[n];\n\t\t}\n\n\t\tUnpackLM(el, lm);\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//! body force stiffness matrix\nvoid FELinearTrussDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n\tvector<int> lm;\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tFEElementMatrix ke(6, 6); ke.zero();\n\t\tint nint = el.GaussPoints();\n\t\tint neln = el.Nodes();\n\t\tdouble* gw = el.GaussWeights();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\tdouble density = m_pMat->Density(mp);\n\n\t\t\tdouble* H = el.H(n);\n\n\t\t\tdouble J0 = el.m_a0 * el.m_L0 / 2.0;\n\n\t\t\tdouble Jdw = J0 * density * gw[n];\n\n\t\t\tmat3d K = bf.stiffness(mp);\n\n\t\t\t// put it together\n\t\t\tfor (int a=0; a<neln; ++a)\n\t\t\t\tfor (int b = 0; b < neln; ++b)\n\t\t\t\t{\n\t\t\t\t\tke.sub(3 * a, 3 * b, K * (H[a] * H[b] * Jdw));\n\t\t\t\t}\n\t\t}\n\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\t\tke.SetNodes(el.m_node);\n\t\tLS.Assemble(ke);\n\t}\n}\n\nvoid FELinearTrussDomain::Update(const FETimeInfo& tp)\n{\n\t// get the poisson's ratio. \n\tFELinearTrussMaterial* mat = dynamic_cast<FELinearTrussMaterial*>(m_pMat);\n\tdouble nu = (mat ? mat->m_v : 0.5);\n\n\tdouble alpham = tp.alpham;\n\tdouble alphaf = tp.alphaf;\n\n\tFEMesh& mesh = *m_pMesh;\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\t// calculate the current length\n\t\tvec3d rt[2], vt[2], at[2];\n\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t{\n\t\t\tFENode& node = m_pMesh->Node(el.m_node[j]);\n\t\t\trt[j] = node.m_rt * alphaf + node.m_rp*(1 - alphaf);\n\t\t\tvec3d vtj = node.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\t\tvt[j] = vtj * alphaf + node.m_vp * (1 - alphaf);\n\t\t\tat[j] = node.m_at * alpham + node.m_ap * (1 - alpham);\n\t\t}\n\n\t\tel.m_Lt = (rt[1] - rt[0]).norm();\n\t\tvec3d e = (rt[1] - rt[0]).normalized();\n\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *(el.GetMaterialPoint(n));\n\t\t\tFETrussMaterialPoint& pt = *(mp.ExtractData<FETrussMaterialPoint>());\n\n\t\t\tmp.m_rt = el.Evaluate(rt, n);\n\t\t\tpt.m_v = el.Evaluate(vt, n);\n\t\t\tpt.m_a = el.Evaluate(at, n);\n\n\t\t\t// calculate stretch\n\t\t\tpt.m_lam = el.m_Lt / el.m_L0;\n\n\t\t\t// volume ratio (this assume linear truss material!)\n\t\t\tdouble J = pow(pt.m_lam, 1.0 - 2.0 * nu);\n\n\t\t\t// calculate stress\n\t\t\tpt.m_tau = m_pMat->Stress(mp);\n\t\t\tpt.m_s = dyad(e) * (pt.m_tau / J);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FELinearTrussDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FETrussDomain.h>\n#include \"FEElasticDomain.h\"\n#include \"FETrussMaterial.h\"\n#include <FECore/FEDofList.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain described by 3D truss elements\nclass FEBIOMECH_API FELinearTrussDomain : public FETrussDomain, public FEElasticDomain\n{\npublic:\n\t//! Constructor\n\tFELinearTrussDomain(FEModel* pfem);\n\n\t//! Initialize data\n\tbool Init() override;\n\n\t//! Reset data\n\tvoid Reset() override;\n\n\t//! Initialize elements\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\t//! Unpack truss element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! Activate domain\n\tvoid Activate() override;\n\n\t//! get the dof list\n\tconst FEDofList& GetDOFList() const override;\n\npublic: // overloads from FEElasticDomain\n\n\t//! update the truss stresses\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculate body force\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\n\t//! Calculates inertial forces for dynamic problems\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override;\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! intertial stiffness matrix\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\n\t//! body force stiffness matrix\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n\n\t//! elemental mass matrix\n\tvoid ElementMassMatrix(FETrussElement& el, matrix& ke);\n\npublic:\n\t//! calculates the truss element stiffness matrix\n\tvoid ElementStiffness(FETrussElement&, matrix& ke);\n\n\t//! Calculates the internal stress vector for truss elements\n\tvoid ElementInternalForces(FETrussElement& el, vector<double>& fe);\n\n\t//! Calculates the inertial contribution for truss elements\n\tvoid ElementInertialForces(FETrussElement& el, vector<double>& fe);\n\nprotected:\n\tFETrussMaterial*\tm_pMat;\n\tdouble\tm_a0;\n\n\tFEDofList\tm_dofU;\n\tFEDofList\tm_dofV;\n\tFEDofList\tm_dofR;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FELungMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\r\nlisted below.\r\n\r\nSee Copyright-FEBio.txt for details.\r\n\r\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\r\nthe City of New York, and others.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.*/\r\n\r\n#include \"FELungMaterial.h\"\r\n#include <math.h>\r\n\r\nBEGIN_FECORE_CLASS(FELungMaterial, FEElasticMaterial)\r\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\");\r\n\tADD_PARAMETER(m_v, FE_RANGE_GREATER(0.0), \"v\");\r\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\");\r\n\tADD_PARAMETER(m_c3, FE_RANGE_GREATER(0.0), \"c3\");\r\n\tADD_PARAMETER(m_d1, FE_RANGE_GREATER(0.0), \"d1\");\r\n\tADD_PARAMETER(m_d3, FE_RANGE_GREATER(0.0), \"d3\");\r\nEND_FECORE_CLASS();\r\n\r\n//-----------------------------------------------------------------------------\r\nFELungMaterial::FELungMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\r\n{\r\n}\r\n\r\nbool FELungMaterial::Init()\r\n{\r\n\tif (FEElasticMaterial::Init() == false) return false;\r\n\r\n\tm_c = m_E/(4*(1+m_v));\r\n\tm_b = m_v/(1-2*m_v);\r\n\r\n\treturn true;\r\n}\r\n\r\ndouble FELungMaterial::StrainEnergyDensity(FEMaterialPoint& mp)\r\n{\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\tmat3ds B = pt.LeftCauchyGreen();\r\n\r\n\tdouble I1 = B.tr();\r\n\tdouble I3 = B.det();\r\n\r\n\treturn m_c*(I1-3)\r\n\t\t+m_c/m_b*(pow(I3, -m_b)-1)\r\n\t\t+m_c1*pow((pow(I3, -1.0/3.0)*I1-3), m_d1)\r\n\t\t+m_c3*pow((pow(I3, 1.0/3.0)-1), m_d3);\r\n}\r\n\r\nmat3ds FELungMaterial::Stress(FEMaterialPoint& mp)\r\n{\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\r\n\tmat3ds B = pt.LeftCauchyGreen();\r\n\r\n\tdouble I1 = B.tr();\r\n\tdouble I3 = B.det();\r\n\r\n\tdouble J = pt.m_J;\r\n\r\n\tmat3dd I(1.0);\r\n\r\n\tmat3ds s = (2*m_c*B \r\n\t\t- 2*m_c*pow(I3, -m_b)*I +\r\n\t\t+2*m_c1*m_d1*pow(I3, -1.0/3.0)*pow(pow(I3, -1.0/3.0)*I1-3, m_d1-1)*(B-I1/3.0*I)\r\n\t\t+2.0/3.0*m_c3*m_d3*pow(pow(I3, 1.0/3.0)-1, m_d3-1)*pow(I3,1.0/3.0)*I)/J;\r\n\r\n\treturn s;\r\n}\r\n\r\ntens4ds FELungMaterial::Tangent(FEMaterialPoint& mp)\r\n{\r\n\t// As in the Stress function, we need the data from the FEElasticMaterialPoint\r\n\t// class to calculate the tangent.\r\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\r\n\r\n\t// Get the deformation gradient and its determinant\r\n\tdouble J = pt.m_J;\r\n\r\n\tmat3ds B = pt.LeftCauchyGreen();\r\n\r\n\tdouble I1 = B.tr();\r\n\tdouble I3 = B.det();\r\n\r\n\t// define identity tensor and some useful\r\n\t// dyadic products of the identity tensor.\r\n\tmat3dd I(1.0);\r\n\ttens4ds IxI = dyad1s(I);\r\n\ttens4ds I4 = dyad4s(I);\r\n\ttens4ds bxI = dyad1s(B,I);\r\n\t\r\n\r\n\tdouble term = m_c1*m_d1*pow(I3,-1.0/3.0)*pow(pow(I3,-1.0/3.0)*I1-3, m_d1-1);\r\n\r\n\ttens4ds c = (4*m_c*pow(I3, -m_b)*(m_b*IxI+I4)\r\n\t\t-4.0/3.0*term*(bxI-I1/3.0*IxI)\r\n\t\t+4*m_c1*m_d1*pow(I3,-2.0/3.0)*(m_d1-1)*pow(pow(I3,-1.0/3.0)*I1-3, m_d1-2)*(dyad1s(B)-I1/3.0*bxI+1.0/9.0*I1*I1*IxI)\r\n\t\t+4.0/3.0*term*I1*I4\r\n\t\t+4.0/9.0*m_c3*m_d3*((m_d3-1)*pow(I3, 2.0/3.0)*pow(pow(I3,1.0/3.0)-1, m_d3-2)+pow(pow(I3,1.0/3.0)-1, m_d3-1)*pow(I3,1.0/3.0))*IxI\r\n\t\t-4.0/3.0*m_c3*m_d3*pow(pow(I3,1.0/3.0)-1,m_d3-1)*pow(I3,1.0/3.0)*I4)/J;\r\n\r\n\treturn c;\r\n}\r\n"
  },
  {
    "path": "FEBioMech/FELungMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\r\nlisted below.\r\n\r\nSee Copyright-FEBio.txt for details.\r\n\r\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\r\nthe City of New York, and others.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\nSOFTWARE.*/\r\n\r\n#include <FEBioMech/FEElasticMaterial.h>\r\n\r\n//----------------------------------------------------------------------------\r\nclass FELungMaterial : public FEElasticMaterial\r\n{\r\npublic:\r\n\tFELungMaterial(FEModel* pfem);\r\n\r\nprivate:\r\n\tdouble\tm_c, m_c1, m_c3;\r\n\tdouble\tm_d1, m_d3;\r\n\tdouble\tm_b;\r\n\r\n\tdouble m_E, m_v;\r\n\r\npublic:\r\n\tvirtual bool Init() override;\r\n\t\r\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\r\n\r\n\tmat3ds Stress(FEMaterialPoint& pt) override;\r\n\r\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\r\n\r\n\tDECLARE_FECORE_CLASS();\r\n};\r\n"
  },
  {
    "path": "FEBioMech/FEMRVonMisesFibers.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMRVonMisesFibers.h\"\n#include <fstream>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMRVonMisesFibers, FEUncoupledMaterial)\n\tADD_PARAMETER(c1, \"c1\");\n\tADD_PARAMETER(c2, \"c2\");\n\tADD_PARAMETER(m_fib.m_c3, \"c3\");\n\tADD_PARAMETER(m_fib.m_c4, \"c4\");\n\tADD_PARAMETER(m_fib.m_c5, \"c5\");\n\tADD_PARAMETER(m_fib.m_lam1, \"lam_max\");\n\t// Fiber Concentration Factor\n\tADD_PARAMETER(kf, \"kf\");\n\t// Preferred fiber orientation IN RADIANS\n\tADD_PARAMETER(tp, \"tp\");\n\t// Number of Gauss Integration Points \n\tADD_PARAMETER(gipt, \"gipt\");\n\t// Choice of von Mises distribution; 1: semi-circular von Mises distribution; = 2: constrained von Mises distribution\n\tADD_PARAMETER(vmc, \"vmc\"); \n\t// Exponent for the constrained von Mises distribution\n\tADD_PARAMETER(var_n, \"var_n\"); \n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//=============================================================================\nFEMRVonMisesMaterialPoint::FEMRVonMisesMaterialPoint(FEMaterialPointData* mp) : FEMaterialPointData(mp)\n{\n\n}\n\nFEMaterialPointData* FEMRVonMisesMaterialPoint::Copy()\n{\n\tFEMRVonMisesMaterialPoint* pt = new FEMRVonMisesMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMRVonMisesMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_kf & m_tp;\n}\n\n//////////////////////////////////////////////////////////////////////\n// FEVonMisesFibers\n/////////////////////////////////////////////////////////////////////\n//\n// This material model has been implemented by Cecile Gouget and Michael Girard\n// \n// This is a fiber-based model intended to describe the behavior of the sclera - the white coat of the eye - and other thin soft tissues. \n// Instead of having a single fiber orientation (Transverse Isotropy), we consider that collagen fiber alignment is \n// multi-directional at local material points, confined within the plane tangent to the tissue surface, and described by either: \n// \n// 1) A semi-circular von Mises distribution function. Such model was originally implemented within Nike3d [1].\n// 2) A constrained von Mises distribution function [2]\n//\n// This latter function is formed as a weighted mixture of the semi-circular uniform distribution and the semi-circular von Mises distribution. \n// Such function has been shown to better describe the planar anisotropy of thin soft tissues and was validated using small angle light scattering measurements \n// tissue anisotropy. It is therefore the preferred function to use. \n//\n// REFERENCES\n// ------------\n// [1] Girard MJA, Downs JC, Burgoyne CF, and Suh JKF, 2009, \"Peripapillary and posterior scleral mechanics - Part I: \n// Development of an anisotropic hyperelastic constitutive model,\" ASME J. Biomech. Eng., 131, p. 051011.  \n// ------------\n// [2] Gouget CLM, Girard MJA, Ethier CR, 2012, \"A constrained von Mises distribution to describe fiber organization \n// in thin soft tissues,\" Biomechanics And Modeling in Mechanobiolgy, 11(3-4), p. 475-482. \n//\n//\n/////////////////////////////////////////////////////////////////////\n\n// This is the modified Bessel function of the first kind (order zero)\n// It is used to compute the probability distribution function \n// of the fibers - the semi-circular von-Mises distribution \ndouble bessi0(double X)\n{\n\tdouble Y,P1,P2,P3,P4,P5,P6,P7,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,AX,BX;\n\tP1=1.0; P2=3.5156229; P3=3.0899424; P4=1.2067429;\n\tP5=0.2659732; P6=0.360768e-1; P7=0.45813e-2;\n\tQ1=0.39894228; Q2=0.1328592e-1; Q3=0.225319e-2;\n\tQ4=-0.157565e-2; Q5=0.916281e-2; Q6=-0.2057706e-1;\n\tQ7=0.2635537e-1; Q8=-0.1647633e-1; Q9=0.392377e-2;\n\tif (fabs(X) < 3.75)\n\t{\n\t\tY=(X/3.75)*(X/3.75);\n\t\treturn (P1+Y*(P2+Y*(P3+Y*(P4+Y*(P5+Y*(P6+Y*P7))))));\n\t}\n\telse\n\t{\n\t\tAX=fabs(X);\n\t\tY=3.75/AX;\n\t\tBX=exp(AX)/sqrt(AX);\n\t\tAX=Q1+Y*(Q2+Y*(Q3+Y*(Q4+Y*(Q5+Y*(Q6+Y*(Q7+Y*(Q8+Y*Q9)))))));\n\t\treturn (AX*BX);\n\t}\n}\n\n// This is the modified Bessel function of the first kind (order one)\n// It is used to compute the probability distribution function \n// of the fibers - the constrained von-Mises distribution \ndouble bessi1(double X) \n{\n\tdouble Y,P1,P2,P3,P4,P5,P6,P7,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,AX,BX;\n\tP1=0.5; P2=0.87890594; P3=0.51498869; P4=0.15084934;\n\tP5=0.2658733e-1; P6=0.301532e-2; P7=0.32411e-3;\n\tQ1=0.39894228; Q2=-0.3988024e-1; Q3=-0.362018e-2;\n\tQ4=0.163801e-2; Q5=-0.1031555e-1; Q6=0.2282967e-1;\n\tQ7=-0.2895312e-1; Q8=0.1787654e-1; Q9=-0.420059e-2;\n\tif (fabs(X) < 3.75) \n\t{\n        Y=(X/3.75)*(X/3.75);\n        return(X*(P1+Y*(P2+Y*(P3+Y*(P4+Y*(P5+Y*(P6+Y*P7)))))));\n\t}\n\telse \n\t{\n        AX=fabs(X);\n        Y=3.75/AX;\n        BX=exp(AX)/sqrt(AX);\n        AX=Q1+Y*(Q2+Y*(Q3+Y*(Q4+Y*(Q5+Y*(Q6+Y*(Q7+Y*(Q8+Y*Q9)))))));\n        return (AX*BX);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEMRVonMisesFibers::FEMRVonMisesFibers(FEModel* pfem) : FEUncoupledMaterial(pfem), m_fib(pfem) \n{\n\tm_fib.SetParent(this);\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMRVonMisesFibers::CreateMaterialPointData()\n{\n\tFEMRVonMisesMaterialPoint* pt = new FEMRVonMisesMaterialPoint(FEUncoupledMaterial::CreateMaterialPointData());\n\tpt->m_kf = kf;\n\tpt->m_tp = tp;\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEMRVonMisesFibers::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEMRVonMisesMaterialPoint& vmpt = *mp.ExtractData<FEMRVonMisesMaterialPoint>();\n\n\t// get some of the material parmaters from the material point\n\tdouble kf = vmpt.m_kf;\n\tdouble tp = vmpt.m_tp;\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t//// FIRST, WE CALCULATE THE MOONEY RIVLIN PART OF THE STRESS\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\t\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1 = c1;\n\tdouble W2 = c2;\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\t\n\t// calculate stress s = 2/J * dev(T) \n\tmat3ds s =  T.dev()*(2.0/J);\n\n\t//NOW, WE CALCULATE THE FIBER COMPONENT OF THE STRESS by the mean of a Gauss integration\n\tconst double pi = 3.14159265358979323846;\n\tvec3d a0, b0, c0;\t\t\t// current local material axis\n\tvector<double> pp(gipt), wmg(gipt);\t\t//gipt Gauss points and weights\n\tdouble aa, bb, pas;\n\tint i;\n\tdouble distribution;\t\t\t//experimental distribution function of fiber orientations\n//\tdouble sigma;\n\tdouble beta;\n \n        //std::cout << gipt << \"\\n\"; \n\t\n\t//-=-=- Gauss Points -=-=- \n\t// Compute angles for Gaussian Quadrature  \n\t// for a function f, a Gaussian quadrature rule follows \n\t//\n\t//                         n\n\t//    /a                 -----\n\t//   |           b - a   \\             b - a      a + b\n\t//   | f(t)dt = -------   \\    wi * f( ----- xi + ----- ) \n\t//   |             2      /              2          2\n\t//  /  b                 /\n\t//                       -----    \n\t//                       i = 1 \n\t//\n\t// where xi are the points and wi are the weights \n\t//\n\t// For n = 2, we have \n\t// w1 = w2 = 1.0\n\t// x1 = - x2 = 1/sqrt(3.0)\n\t//\n\t// In the following we used \"gipt\" Gauss points for the numerical integration\n\t//-=-=-=-=-=-=-=-=-=-=-=-=\n       \n\t// step\n\tpas = pi / (gipt/10);\n\n\t//Gauss Points\n\tfor(int j=0; j<gipt; j+=10)\n\t{\n         aa = tp - pi/2.0 +  j/10    * pas;\n         bb = tp - pi/2.0 + (j/10+1) * pas; \n         pp[j+0] = pas/2.0 * ( + 0.14887434 ) + (aa+bb)/2.0;\n         pp[j+1] = pas/2.0 * ( - 0.14887434 ) + (aa+bb)/2.0;\n         pp[j+2] = pas/2.0 * ( + 0.43339539 ) + (aa+bb)/2.0;\n         pp[j+3] = pas/2.0 * ( - 0.43339539 ) + (aa+bb)/2.0;\n         pp[j+4] = pas/2.0 * ( + 0.67940957 ) + (aa+bb)/2.0;\n         pp[j+5] = pas/2.0 * ( - 0.67940957 ) + (aa+bb)/2.0;\n         pp[j+6] = pas/2.0 * ( + 0.86506337 ) + (aa+bb)/2.0;\n         pp[j+7] = pas/2.0 * ( - 0.86506337 ) + (aa+bb)/2.0;\n         pp[j+8] = pas/2.0 * ( + 0.97390653 ) + (aa+bb)/2.0;\n         pp[j+9] = pas/2.0 * ( - 0.97390653 ) + (aa+bb)/2.0;\n \t}\n\n\t//Weights for Gaussian Quadrature  \n\tfor(int j=0; j<gipt; j+=10)\n\t{\n\t\twmg[j+0] = 0.29552422 * pas/2.0;        \n         wmg[j+1] = 0.29552422 * pas/2.0;       \n         wmg[j+2] = 0.26926672 * pas/2.0;\n         wmg[j+3] = 0.26926672 * pas/2.0;\n         wmg[j+4] = 0.21908636 * pas/2.0;\n         wmg[j+5] = 0.21908636 * pas/2.0;\n         wmg[j+6] = 0.14945135 * pas/2.0;\n         wmg[j+7] = 0.14945135 * pas/2.0;\n         wmg[j+8] = 0.06667134 * pas/2.0;\n         wmg[j+9] = 0.06667134 * pas/2.0;\n \t}\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction : a0 is the fiber direction,  \n\t// b0 a vector of the plane of the fibers perpendicular to a0\n\t// (specified in mat_axis : a0 = unit(a) ; b0 = unit((a0^d0)^a0)\n\ta0.x = Q[0][0]; b0.x = Q[0][1]; c0.x = Q[0][2];\n\ta0.y = Q[1][0]; b0.y = Q[1][1]; c0.y = Q[1][2];\n\ta0.z = Q[2][0]; b0.z = Q[2][1]; c0.z = Q[2][2];\n\n\t// Think of it as #gipt different fiber orientations that are distributed within a plane\n\t// of coordinate system (a0,b0)\n\tfor(i=0;i<gipt;++i)\n\t{\n\t\t// vector describing the fibers which make an angle pp[i] with vector a0 in plane (a0,b0)\n\t\tvec3d n0;\n\t\tn0.x = cos(pp[i])*a0.x+sin(pp[i])*b0.x;\n\t\tn0.y = cos(pp[i])*a0.y+sin(pp[i])*b0.y;\n\t\tn0.z = cos(pp[i])*a0.z+sin(pp[i])*b0.z;\n\n\t\t// probability of having a fiber along this vector \n\t\tif (vmc==1) // Semi-circular von Mises distribution\n\t\t{\n\t\t\tdistribution = 1. / ( pi * bessi0(kf) ) * exp( kf * cos( 2.* (pp[i]-tp) ) );\n\t\t}\n\t\telse if (vmc==2) // Constrained von Mises distribution\n\t\t{\n\t\t\tbeta = pow(bessi1(kf)/bessi0(kf),var_n);  // Weighting factor (isotropic vs anisotropic)\n\t\t\tdistribution = (1.-beta)/pi + beta / ( pi * bessi0(kf) ) * exp( kf * cos( 2.* (pp[i]-tp) ) );\n\t\t}\t\t\n\t\t\n\t\t// add the fiber stress by Gauss integration : m_fib.Stress(mp) is the deviatoric stress due to the fibers in direction pp[i], \n\t\t// distribution is the probability of having a fiber in this direction, and wmg[i] is the Gauss Weight for integration\n\t\ts += wmg[i]*distribution*m_fib.DevFiberStress(mp, n0);\n\t}\n\n\treturn s;\n}\n\ntens4ds FEMRVonMisesFibers::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEMRVonMisesMaterialPoint& vmpt = *mp.ExtractData<FEMRVonMisesMaterialPoint>();\n\n\t// get some of the material parmaters from the material point\n\tdouble kf = vmpt.m_kf;\n\tdouble tp = vmpt.m_tp;\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t//// FIRST, WE CALCULATE THE MOONEY RIVLIN PART OF THE TANGENT\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2;\n\tW1 = c1;\n\tW2 = c2;\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2;\n\n\tmat3dd I(1);\t// Identity\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W2*I1) - B2*W2;\n\n\ttens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\ttens4ds c = dyad1s(devs, I)*(2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\t//NOW, WE CALCULATE THE FIBERS CONTRIBUTION TO THE TANGENT by the mean of a Gauss integration\n\t// current local material axis\n\tconst double pi = 3.14159265358979323846;\t\n\tvec3d a0, b0, c0;\n\tvector<double> pp(gipt), wmg(gipt);\n\tdouble aa, bb, pas;\n\tint i;\n\tdouble distribution;\n//\tdouble sigma;\n\tdouble beta;\n\t\n\t//-=-=- Gauss Points -=-=- \n\t// Compute angles for Gaussian Quadrature  \n\t// for a function f, a Gaussian quadrature rule follows \n\t//\n\t//                         n\n\t//    /a                 -----\n\t//   |           b - a   \\             b - a      a + b\n\t//   | f(t)dt = -------   \\    wi * f( ----- xi + ----- ) \n\t//   |             2      /              2          2\n\t//  /  b                 /\n\t//                       -----    \n\t//                       i = 1 \n\t//\n\t// where xi are the points and wi are the weights \n\t//\n\t// For n = 2, we have \n\t// w1 = w2 = 1.0\n\t// x1 = - x2 = 1/sqrt(3.0)\n\t//\n\t// In the following we used \"gipt\" Gauss points for the numerical integration\n\t//-=-=-=-=-=-=-=-=-=-=-=-=\n       \n\t// step\n\tpas = pi / (gipt/10);\n\n\t//Gauss Points\n\tfor(int j=0; j<gipt; j+=10)\n\t{\n         aa = tp - pi/2.0 +  j/10    * pas;\n         bb = tp - pi/2.0 + (j/10+1) * pas; \n         pp[j+0] = pas/2.0 * ( + 0.14887434 ) + (aa+bb)/2.0;\n         pp[j+1] = pas/2.0 * ( - 0.14887434 ) + (aa+bb)/2.0;\n         pp[j+2] = pas/2.0 * ( + 0.43339539 ) + (aa+bb)/2.0;\n         pp[j+3] = pas/2.0 * ( - 0.43339539 ) + (aa+bb)/2.0;\n         pp[j+4] = pas/2.0 * ( + 0.67940957 ) + (aa+bb)/2.0;\n         pp[j+5] = pas/2.0 * ( - 0.67940957 ) + (aa+bb)/2.0;\n         pp[j+6] = pas/2.0 * ( + 0.86506337 ) + (aa+bb)/2.0;\n         pp[j+7] = pas/2.0 * ( - 0.86506337 ) + (aa+bb)/2.0;\n         pp[j+8] = pas/2.0 * ( + 0.97390653 ) + (aa+bb)/2.0;\n         pp[j+9] = pas/2.0 * ( - 0.97390653 ) + (aa+bb)/2.0;\n \t}\n\n\t//Weights for Gaussian Quadrature  \n\tfor(int j=0; j<gipt; j+=10)\n\t{\n\t\twmg[j+0] = 0.29552422 * pas/2.0;        \n\t\twmg[j+1] = 0.29552422 * pas/2.0;       \n\t\twmg[j+2] = 0.26926672 * pas/2.0;\n\t\twmg[j+3] = 0.26926672 * pas/2.0;\n\t\twmg[j+4] = 0.21908636 * pas/2.0;\n\t\twmg[j+5] = 0.21908636 * pas/2.0;\n\t\twmg[j+6] = 0.14945135 * pas/2.0;\n\t\twmg[j+7] = 0.14945135 * pas/2.0;\n\t\twmg[j+8] = 0.06667134 * pas/2.0;\n\t\twmg[j+9] = 0.06667134 * pas/2.0;\n \t}\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\ta0.x = Q[0][0]; b0.x = Q[0][1]; c0.x = Q[0][2];\n\ta0.y = Q[1][0]; b0.y = Q[1][1]; c0.y = Q[1][2];\n\ta0.z = Q[2][0]; b0.z = Q[2][1]; c0.z = Q[2][2];\n\n\t// Think of it as #gipt different fiber orientations that are distributed within a plane of coordinate system (a0,b0)\n\t//  and that all contribute to the tangent in proportion with the probability of having a fiber in this orientation\n\tfor(i=0;i<gipt;++i)\n\t{\n\t    // vector describing the fibers which make an angle pp[i] with vector a0 in plane (a0,b0)\n\t\tvec3d n0;\n\t\tn0.x = cos(pp[i])*a0.x+sin(pp[i])*b0.x;\n\t\tn0.y = cos(pp[i])*a0.y+sin(pp[i])*b0.y;\n\t\tn0.z = cos(pp[i])*a0.z+sin(pp[i])*b0.z;\n\t\t\n\t\t// probability of having a fiber along this vector \n\t\tif (vmc==1) // Semi-circular von Mises distribution\n\t\t{\n\t\t\tdistribution = 1. / ( pi * bessi0(kf) ) * exp( kf * cos( 2.* (pp[i]-tp) ) );\n\t\t}\n\t\telse if (vmc==2) // Constrained von Mises distribution\n\t\t{\n\t\t\tbeta = pow(bessi1(kf)/bessi0(kf),var_n);  // Weighting factor (isotropic vs anisotropic)\n\t\t\tdistribution = (1.-beta)/pi + beta / ( pi * bessi0(kf) ) * exp( kf * cos( 2.* (pp[i]-tp) ) );\n\t\t}\t\t\n\t\t\n\t// add the fiber stress by Gauss integration : m_fib.Tangent(mp) is the contribution to the tangent of the fibers in direction pp[i], \n\t// distribution is the probability of having a fiber in this direction, and wmg[i] is the Gauss Weight for integration\n\t\tc += wmg[i]*distribution*m_fib.DevFiberTangent(mp, n0);\n\t}\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEMRVonMisesFibers.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMRVonMisesMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFEMRVonMisesMaterialPoint(FEMaterialPointData* mp = nullptr);\n\n\tFEMaterialPointData* Copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tdouble\tm_kf;\n\tdouble\tm_tp;\n};\n\n//-----------------------------------------------------------------------------\n//! Transversely Isotropic Multiple material\n\n//! This material has an isotopric Multiple basis and single preferred\n//! fiber direction.\n\nclass FEMRVonMisesFibers: public FEUncoupledMaterial\n{\npublic:\n\tFEMRVonMisesFibers (FEModel* pfem);\n\npublic:\n\tdouble\tc1;\t//!< Mooney-Rivlin coefficient C1\n\tdouble\tc2;\t//!< Mooney-Rivlin coefficient C2\n\tdouble\tkf;\t//!< Fiber Concentration Factor\n\tdouble\ttp;\t//!< Preferred Fiber Orientation IN RADIANS\n\tint\tgipt;\t//!< Gauss Integration Points\n\tint\tvmc;\t//!< Choice of von Mises distribution \n\tdouble var_n;\t//!< Exponent for the constrained von Mises distribution\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n\nprotected:\n\tFEFiberExpLinearUC\tm_fib;\n};\n"
  },
  {
    "path": "FEBioMech/FEMassDamping.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMassDamping.h\"\n#include <FEBioMech/FEElasticMaterialPoint.h>\n\nBEGIN_FECORE_CLASS(FEMassDamping, FEBodyForce)\n\tADD_PARAMETER(m_C, \"C\");\nEND_FECORE_CLASS();\n\nFEMassDamping::FEMassDamping(FEModel* fem) : FEBodyForce(fem)\n{\n\tm_C = 0.0;\n}\n\n//! calculate the body force at a material point\nvec3d FEMassDamping::force(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\treturn ep.m_v*m_C;\n}\n\n//! calculate the divergence of the body force at a material point\ndouble FEMassDamping::divforce(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    return ep.m_L.trace()*m_C;\n}\n\n//! calculate constribution to stiffness matrix\nmat3d FEMassDamping::stiffness(FEMaterialPoint& pt)\n{\n\tdouble dt = CurrentTimeIncrement();\n\treturn mat3dd(-m_C / dt);\n}\n"
  },
  {
    "path": "FEBioMech/FEMassDamping.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FEBioMech/FEBodyForce.h>\n\nclass FEMassDamping : public FEBodyForce\n{\npublic:\n\tFEMassDamping(FEModel* fem);\n\n\t//! calculate the body force at a material point\n\tvec3d force(FEMaterialPoint& pt) override;\n\n    //! calculate the divergence of the body force at a material point\n    double divforce(FEMaterialPoint& pt) override;\n    \n\t//! calculate constribution to stiffness matrix\n\tmat3d stiffness(FEMaterialPoint& pt) override;\n\nprivate:\n\tdouble\tm_C;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMaxDamageCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMaxDamageCriterion.h\"\n#include \"FEElasticMaterial.h\"\n\nBEGIN_FECORE_CLASS(FEDamageAdaptorCriterion, FEMeshAdaptorCriterion)\nEND_FECORE_CLASS();\n\nFEDamageAdaptorCriterion::FEDamageAdaptorCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n}\n\nbool FEDamageAdaptorCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\t// TODO: What about mixtures? \n\tFEDamageMaterialPoint* dp = mp.ExtractData<FEDamageMaterialPoint>();\n\tif (dp == nullptr) return false;\n\n\t// evaluate the damage at this point\n\tvalue = dp->m_D;\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioMech/FEMaxDamageCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include \"FEDamageMaterial.h\"\n\nclass FEDamageAdaptorCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEDamageAdaptorCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEBioMech/FEMaxStressCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMaxStressCriterion.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEElement.h>\n\nBEGIN_FECORE_CLASS(FEStressCriterion, FEMeshAdaptorCriterion)\n\tADD_PARAMETER(m_metric, \"metric\");\nEND_FECORE_CLASS();\n\nFEStressCriterion::FEStressCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n\tm_metric = 0;\n}\n\nbool FEStressCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tFEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\tif (ep == nullptr) return false;\n\n\tmat3ds s = ep->m_s;\n\n\t// get the metric\n\tvalue = 0.0;\n\tswitch (m_metric)\n\t{\n\tcase 0: value = s.effective_norm(); break;\n\tcase 1:\n\t{\n\t\tmat3ds devs = s.dev();\n\t\tdouble l[3], lmax;\n\t\tdevs.exact_eigen(l);\n\t\tlmax = l[0];\n\t\tif (l[1] > lmax) lmax = l[1];\n\t\tif (l[2] > lmax) lmax = l[2];\n\t\tvalue = lmax;\n\t}\n\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioMech/FEMaxStressCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n\nclass FEStressCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFEStressCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\nprivate:\n\tint\t\tm_metric;\n\n\tDECLARE_FECORE_CLASS()\n};\n\n"
  },
  {
    "path": "FEBioMech/FEMechModel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidSystem.h\"\n#include <FECore/FEMaterial.h>\n#include <FECore/FEDomain.h>\n#include \"FERigidBody.h\"\n#include \"FESolidMaterial.h\"\n#include \"FERigidMaterial.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMechModel, FEModel)\n\tADD_PROPERTY(m_prs->RigidBodyList(), \"rigid_body\");\nEND_FECORE_CLASS();\n\n\n//-----------------------------------------------------------------------------\nFEMechModel::FEMechModel()\n{\n\t// create a rigid system\n\tm_prs = new FERigidSystem(this);\n}\n\n//-----------------------------------------------------------------------------\n// get the rigid system\nFERigidSystem* FEMechModel::GetRigidSystem() \n{ \n\treturn m_prs; \n}\n\n//-----------------------------------------------------------------------------\n// clear all model data\nvoid FEMechModel::Clear()\n{\n\tm_prs->Clear();\n\n\tFEModel::Clear();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMechModel::Init()\n{\n\t// create and initialize the rigid bodies\n\tif (InitRigidSystem() == false) return false;\n\n\t// initialize the rest of the model\n\treturn FEModel::Init();\n}\n\n//-----------------------------------------------------------------------------\n// number of rigid bodies\nint FEMechModel::RigidBodies() const\n{\n\treturn m_prs->Objects();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMechModel::InitRigidSystem()\n{\n\treturn m_prs->Init();\n}\n\n//-----------------------------------------------------------------------------\n// get a rigid body\nFERigidBody* FEMechModel::GetRigidBody(int n)\n{\n\treturn m_prs->Object(n);\n}\n\n//-----------------------------------------------------------------------------\n// find a rigid body from a material ID\nint FEMechModel::FindRigidbodyFromMaterialID(int matId)\n{\n\tint NRB = RigidBodies();\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\tFERigidBody& rb = *GetRigidBody(i);\n\t\tif (rb.GetMaterialID() == matId) return i;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// return number or rigid BCs\nint FEMechModel::RigidBCs() const\n{\n\treturn m_prs->RigidBCs();\n}\n\n//-----------------------------------------------------------------------------\n// return the rigid displacement\nFERigidBC* FEMechModel::GetRigidBC(int i)\n{\n\treturn m_prs->RigidBC(i);\n}\n\n//-----------------------------------------------------------------------------\n// add a rigid presribed BC\nvoid FEMechModel::AddRigidBC(FERigidBC* pDC)\n{\n\tm_prs->AddRigidBC(pDC);\n}\n\n//-----------------------------------------------------------------------------\n// add a rigid initial condition\nvoid FEMechModel::AddRigidInitialCondition(FERigidIC* pIC)\n{\n\tm_prs->AddInitialCondition(pIC);\n}\n\n//-----------------------------------------------------------------------------\n// model activation\nvoid FEMechModel::Activate()\n{\n    // activate rigid components\n    m_prs->Activate();\n    \n\tFEModel::Activate();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMechModel::Reactivate()\n{\n\tFEModel::Reactivate();\n\tm_prs->Activate();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMechModel::Reset()\n{\n\tif (m_prs->Reset() == false) return false;\n\treturn FEModel::Reset();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMechModel::InitMesh()\n{\n\tif (FEModel::InitMesh() == false) return false;\n\treturn m_prs->InitRigidBodies();\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize shells\nbool FEMechModel::InitShells()\n{\n\t// Base class does most of the work\n\tif (!FEModel::InitShells()) return false;\n\n\t// NOTE: This was moved here because I wanted to FEMaterial::IsRigid to FESolidMaterial::IsRigid\n\t//       This was part of the move to rid the FECore library of rigid stuff\n\n\t// Find the nodes that are on a non-rigid shell. \n\t// These nodes will be assigned rotational degrees of freedom\n\t// TODO: Perhaps I should let the domains do this instead\n\tFEMesh& mesh = GetMesh();\n\tfor (int i = 0; i<mesh.Nodes(); ++i) mesh.Node(i).m_nstate &= ~FENode::SHELL;\n\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tif (dom.Class() == FE_DOMAIN_SHELL)\n\t\t{\n\t\t\tFESolidMaterial* pmat = dynamic_cast<FESolidMaterial*>(dom.GetMaterial());\n\t\t\tif ((pmat == 0) || (pmat->IsRigid() == false))\n\t\t\t{\n\t\t\t\tint N = dom.Elements();\n\t\t\t\tfor (int i = 0; i<N; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tint n = el.Nodes();\n\t\t\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tmesh.Node(el.m_node[j]).m_nstate |= FENode::SHELL;\n\n\t\t\t\t\t\t// TODO: Not sure why, but it looks like this is needed otherwise\n\t\t\t\t\t\t//       the rigid bodies lock. \n\t\t\t\t\t\tmesh.Node(el.m_node[j]).m_nstate |= FENode::RIGID_CLAMP;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// find a parameter value\nFEParamValue FEMechModel::GetParameterValue(const ParamString& paramString)\n{\n\tFEParamValue val = FEModel::GetParameterValue(paramString);\n\n\tif (val.isValid() == false)\n\t{\n\t\treturn m_prs->GetParameterValue(paramString);\n\t}\n\n\treturn val;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMechModel::SerializeGeometry(DumpStream& ar)\n{\n\tFEModel::SerializeGeometry(ar);\n\tm_prs->Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\n//! Build the matrix profile for this model\nvoid FEMechModel::BuildMatrixProfile(FEGlobalMatrix& G, bool breset)\n{\n\tFEModel::BuildMatrixProfile(G, breset);\n\n\tif (breset)\n\t{\n\t\t// Add rigid bodies to the profile\n\t\tm_prs->BuildMatrixProfile(G);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// update rigid part of mesh\nvoid FEMechModel::UpdateRigidMesh()\n{\n\tm_prs->UpdateMesh();\n}\n"
  },
  {
    "path": "FEBioMech/FEMechModel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEModel.h>\n#include \"febiomech_api.h\"\n\n//---------------------------------------------------------------------------------------\nclass FERigidSystem;\nclass FERigidBody;\nclass FERigidBC;\nclass FERigidIC;\nclass FERigidNodeSet;\n\n//---------------------------------------------------------------------------------------\n// This class extends the basic FEModel class by adding a rigid body system\nclass FEBIOMECH_API FEMechModel : public FEModel\n{\npublic:\n\tFEMechModel();\n\n\t// clear all model data\n\tvoid Clear() override;\n\n\t// model initialization\n\tbool Init() override;\n\n\t// model activation\n\tvoid Activate() override;\n\n\t// TODO: temporary construction. Would like to call Activate\n\tvoid Reactivate() override;\n\n\t// reset\n\tbool Reset() override;\n\n\t// initialize mesh\n\tbool InitMesh() override;\n\n\t//! Initialize shells\n\tbool InitShells() override;\n\n\t// find a parameter value\n\tFEParamValue GetParameterValue(const ParamString& param) override;\n\n\t//! serialize data for restarts\n\tvoid SerializeGeometry(DumpStream& ar) override;\n\n\t//! Build the matrix profile for this model\n\tvoid BuildMatrixProfile(FEGlobalMatrix& G, bool breset) override;\n\n\t// update rigid part of mesh\n\tvoid UpdateRigidMesh();\n\npublic:\n\t// get the rigid system\n\tFERigidSystem* GetRigidSystem();\n\n\t// initialize the rigid system\n\tbool InitRigidSystem();\n\n\t// number of rigid bodies\n\tint RigidBodies() const;\n\n\t// get a rigid body\n\tFERigidBody* GetRigidBody(int n);\n\n\t// find a rigid body from a material ID\n\tint FindRigidbodyFromMaterialID(int matId);\n\n\t// return number or rigid BCs\n\tint RigidBCs() const;\n\n\t// return the rigid prescribed displacement\n\tFERigidBC* GetRigidBC(int i);\n\n\t// add a rigid BC\n\tvoid AddRigidBC(FERigidBC* pDC);\n\n\t// add a rigid initial condition\n\tvoid AddRigidInitialCondition(FERigidIC* pIC);\n\nprivate:\n\tFERigidSystem*\tm_prs;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMembraneMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMembraneMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// convet the displacement gradient to a strain\nvoid FEMembraneMaterialPoint::strain(double e[3])\n{\n\tconst double h1[6] = {1, 0, 0, 0, 0, 0};\n\tconst double h2[6] = {0, 0, 0, 0, 1, 0};\n\tconst double h3[6] = {0, 1, 0, 1, 0, 0};\n\n\tconst double H1[6][6] = {\n\t\t{1, 0, 0, 0, 0, 0},\n\t\t{0, 1, 0, 0, 0, 0},\n\t\t{0, 0, 1, 0, 0, 0},\n\t\t{0, 0, 0, 0, 0, 0},\n\t\t{0, 0, 0, 0, 0, 0},\n\t\t{0, 0, 0, 0, 0, 0}};\n\n\tconst double H2[6][6] = {\n\t\t{0, 0, 0, 0, 0, 0},\n\t\t{0, 0, 0, 0, 0, 0},\n\t\t{0, 0, 0, 0, 0, 0},\n\t\t{0, 0, 0, 1, 0, 0},\n\t\t{0, 0, 0, 0, 1, 0},\n\t\t{0, 0, 0, 0, 0, 1}};\n\n\tconst double H3[6][6] = {\n\t\t{0, 0, 0, 1, 0, 0},\n\t\t{0, 0, 0, 0, 1, 0},\n\t\t{0, 0, 0, 0, 0, 1},\n\t\t{1, 0, 0, 0, 0, 0},\n\t\t{0, 1, 0, 0, 0, 0},\n\t\t{0, 0, 1, 0, 0, 0}};\n\n\te[0] = e[1] = e[2] = 0.0;\n\tfor (int i=0; i<6; ++i)\n\t{\n\t\te[0] += h1[i]*g[i];\n\t\te[1] += h2[i]*g[i];\n\t\te[2] += h3[i]*g[i];\n\t\tfor (int j=0; j<6; ++j)\n\t\t{\n\t\t\te[0] += 0.5*g[i]*H1[i][j]*g[j];\n\t\t\te[1] += 0.5*g[i]*H2[i][j]*g[j];\n\t\t\te[2] += 0.5*g[i]*H3[i][j]*g[j];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticMembrane, FEMembraneMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMembrane::Stress(FEMaterialPoint &mp, double s[])\n{\n\tFEMembraneMaterialPoint& pt = *mp.ExtractData<FEMembraneMaterialPoint>();\n\n\t// get the material point strain\n\tdouble e[3];\n\tpt.strain(e);\n\n\t// elasticity tangent\n\tdouble E = 1.0;\n\tdouble v = 0.35;\n\tdouble d = 1.0/(1.0 - v*v);\n\tdouble D[3][3] = {\n\t\t{d*E, d*v*E, 0},\n\t\t{d*v*E, d*E, 0},\n\t\t{0, 0, d*E*(1.0-v)*0.5}};\n\n\t// calculate 2D PK2-stress\n\ts[0] = D[0][0]*e[0] + D[0][1]*e[1] + D[0][2]*e[2];\n\ts[1] = D[1][0]*e[0] + D[1][1]*e[1] + D[1][2]*e[2];\n\ts[2] = D[2][0]*e[0] + D[2][1]*e[1] + D[2][2]*e[2];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMembrane::Tangent(FEMaterialPoint &mp, double D[][3])\n{\n\tFEMembraneMaterialPoint& pt = *mp.ExtractData<FEMembraneMaterialPoint>();\n\n\t// elasticity tangent\n\tdouble E = 1.0;\n\tdouble v = 0.35;\n\tdouble d = 1.0/(1.0 - v*v);\n\tD[0][0] =   d*E; D[0][1] = d*v*E; D[0][2] = 0;\n\tD[1][0] = d*v*E; D[1][1] =   d*E; D[1][2] = 0;\n\tD[2][0] =     0; D[2][1] =     0; D[2][2] = d*E*(1.0-v)*0.5;\n}\n"
  },
  {
    "path": "FEBioMech/FEMembraneMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/DumpStream.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMembraneMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFEMembraneMaterialPoint()\n\t{\n\t\ts[0] = s[1] = s[2] = 0;\n\t}\n\n\tFEMaterialPointData* Copy()\n\t{\n\t\treturn new FEMembraneMaterialPoint(*this);\n\t}\n\n\tvoid Serialize(DumpStream& ar)\n\t{\n\t\tFEMaterialPointData::Serialize(ar);\n\t\tar & g & s;\n\t}\n\npublic:\n\t// calculate membrane strain\n\tvoid strain(double e[3]);\n\npublic:\n\tdouble\tg[6];\t// deformation gradient\n\tdouble\ts[3];\t// in-plane PK2 stress\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEMembraneMaterial : public FEMaterial\n{\npublic:\n\tFEMembraneMaterial(FEModel* pfem) : FEMaterial(pfem) {}\n\tvirtual ~FEMembraneMaterial() {}\n\n\t//! create material point data\n\tFEMaterialPointData* CreateMaterialPointData() { return new FEMembraneMaterialPoint; }\n\npublic:\n\t//! calculate in-plane membrane stress\n\tvirtual void Stress(FEMaterialPoint& mp, double s[3]) = 0;\n\n\t//! calculate in-plane membrane tangent\n\tvirtual void Tangent(FEMaterialPoint& mp, double D[3][3]) = 0;\n\n\tFECORE_BASE_CLASS(FEMembraneMaterial)\n};\n\n//-----------------------------------------------------------------------------\n// class for triangular membranes\nclass FEElasticMembrane : public FEMembraneMaterial\n{\npublic:\n\t//! constructor\n\tFEElasticMembrane(FEModel* pfem) : FEMembraneMaterial(pfem) {}\n\npublic:\n\t//! calculate in-plane membrane stress\n\tvoid Stress(FEMaterialPoint& mp, double s[3]) override;\n\n\t//! calculate in-plane membrane tangent\n\tvoid Tangent(FEMaterialPoint& mp, double D[3][3]) override;\n\npublic:\n\tdouble\tm_E;\n\tdouble\tm_v;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMooneyRivlin.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMooneyRivlin, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric stress\nmat3ds FEMooneyRivlin::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n\t// Wi = dW/dIi\n\tdouble W1 = c1;\n\tdouble W2 = c2;\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\t// T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FEMooneyRivlin::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2;\n\tW1 = c1;\n\tW2 = c2;\n\t// ---\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2;\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\tmat3ds devs = T.dev()*(2.0/J);\n\n\t// Identity tensor\n\tmat3ds I(1,1,1,0,0,0);\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W2*I1) - B2*W2;\n\n\ttens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEMooneyRivlin::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// get material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n    \n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n    double sed = c1*(I1-3) + c2*(I2-3);\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Mooney-Rivlin material\n\nclass FEMooneyRivlin : public FEUncoupledMaterial\n{\npublic:\n\tFEMooneyRivlin(FEModel* pfem) : FEUncoupledMaterial(pfem) {}\n\npublic:\n\tFEParamDouble\tm_c1;\t//!< Mooney-Rivlin coefficient C1\n\tFEParamDouble\tm_c2;\t//!< Mooney-Rivlin coefficient C2\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMooneyRivlinAD.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMooneyRivlinAD.h\"\n#include \"adcm.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMooneyRivlinAD, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, \"c2\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric stress\nmat3ds FEMooneyRivlinAD::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5 * (I1 * I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n\t// Wi = dW/dIi\n\tdouble W1 = c1;\n\tdouble W2 = c2;\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\t// T = F*dW/dC*Ft\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\n\treturn T.dev() * (2.0 / J);\n}\n\nad::mat3ds FEMooneyRivlinAD::PK2Stress_AD(FEMaterialPoint& mp, ad::mat3ds& C)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Jm23 = pow(J, -1.0 / 3.0);\n\n\tad::mat3ds C2 = C.sqr();\n\tad::mat3ds Ci = C.inverse();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tad::number I1 = C.tr();\n\tad::number I2 = 0.5 * (I1 * I1 - C2.tr());\n\n\t// calculate T = dW/dC\n\tad::mat3ds I(1.0);\n\tad::mat3ds T = I*c1 + C*(0.5*c2);\n\n\t// calculte S = 2*DEV[T]\n\tad::mat3ds S = (T - Ci * (T.dotdot(C) / 3.0))*(2.0*Jm23);\n\n\treturn S;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FEMooneyRivlinAD::DevTangent(FEMaterialPoint& mp)\n{\n\t// calculate material tangent\n\ttens4ds C4 = ad::Tangent<FEMooneyRivlinAD>(this, mp);\n\n\t// push forward to get spatial tangent\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\ttens4ds c4 = pt.push_forward(C4);\n\n\treturn c4;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEMooneyRivlinAD::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5 * (I1 * I1 - B2.tr());\n\n\t//\n\t// W = C1*(I1 - 3) + C2*(I2 - 3)\n\t//\n\tdouble sed = c1 * (I1 - 3) + c2 * (I2 - 3);\n\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEMooneyRivlinAD.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModelParam.h>\n#include <FECore/ad.h>\n\n//! Mooney-Rivlin material using automatic differentiation\nclass FEMooneyRivlinAD : public FEUncoupledMaterial\n{\npublic:\n\tFEMooneyRivlinAD(FEModel* pfem) : FEUncoupledMaterial(pfem) {}\n\npublic:\n\tFEParamDouble\tm_c1;\t//!< Mooney-Rivlin coefficient C1\n\tFEParamDouble\tm_c2;\t//!< Mooney-Rivlin coefficient C2\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n\npublic:\n\tad::mat3ds PK2Stress_AD(FEMaterialPoint& pt, ad::mat3ds& C);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMortarContactSurface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMortarContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nFEMortarContactSurface::FEMortarContactSurface(FEModel* pfem) : FEContactSurface(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEMortarContactSurface::Init()\n{\n\tif (FEContactSurface::Init() == false) return false;\n\n\tint NN = Nodes();\n\tm_gap.resize(NN, vec3d(0,0,0));\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMortarContactSurface::UpdateNodalAreas()\n{\n\tint NN = Nodes();\n\tint NF = Elements();\n\tm_A.resize(NN, 0.0);\n\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tdouble a = FaceArea(el);\n\n\t\tint nn = el.Nodes();\n\t\tdouble fa = a / (double) nn;\n\t\tfor (int j=0; j<nn; ++j) m_A[el.m_lnode[j]] += fa;\n\t}\n\n\tfor (int i=0; i<NN; ++i) m_A[i] = 1.0/m_A[i];\n}\n"
  },
  {
    "path": "FEBioMech/FEMortarContactSurface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! This class represents a surface used by the mortar contact interface.\nclass FEMortarContactSurface : public FEContactSurface\n{\npublic:\n\tFEMortarContactSurface(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\n\t//! update nodal areas\n\tvoid UpdateNodalAreas();\n\npublic:\n\tvector<double>\tm_A;\t\t//!< nodal areas\n\tvector<vec3d>\tm_gap;\t\t//!< nodal gaps\n};\n"
  },
  {
    "path": "FEBioMech/FEMortarInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMortarInterface.h\"\n#include \"FECore/mortar.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEMesh.h>\n\n//-----------------------------------------------------------------------------\nFEMortarInterface::FEMortarInterface(FEModel* pfem) : FEContactInterface(pfem)\n{\n\t// set the integration rule\n\tm_pT = dynamic_cast<FESurfaceElementTraits*>(FEElementLibrary::GetElementTraits(FE_TRI3G7));\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMortarInterface::UpdateMortarWeights(FESurface& ss, FESurface& ms)\n{\n\t// allocate sturcture for the integration weights\n\tint NS = ss.Nodes();\n\tint NM = ms.Nodes();\n\tm_n1.resize(NS,NS);\n\tm_n2.resize(NS,NM);\n\n\t// clear weights\n\tm_n1.zero();\n\tm_n2.zero();\n\n\t// number of integration points\n\tconst int MAX_INT = 11;\n\tconst int nint = m_pT->m_nint;\n\tvector<double>& gw = m_pT->gw;\n\tvector<double>& gr = m_pT->gr;\n\tvector<double>& gs = m_pT->gs;\n\n\t// calculate the mortar surface\n\tMortarSurface mortar;\n\tCalculateMortarSurface(ss, ms, mortar);\n\n\t// These arrays will store the shape function values of the projection points \n\t// on the primary and secondary side when evaluating the integral over a pallet\n\tdouble Ns[MAX_INT][4], Nm[MAX_INT][4];\n\n\t// loop over the mortar patches\n\tint NP = mortar.Patches();\n\tfor (int i=0; i<NP; ++i)\n\t{\n\t\t// get the next patch\n\t\tPatch& pi = mortar.GetPatch(i);\n\n\t\t// get the facet ID's that generated this patch\n\t\tint k = pi.GetPrimaryFacetID();\n\t\tint l = pi.GetSecondaryFacetID();\n\n\t\t// get the non-mortar surface element\n\t\tFESurfaceElement& se = ss.Element(k);\n\t\t// get the mortar surface element\n\t\tFESurfaceElement& me = ms.Element(l);\n\n\t\t// loop over all patch triangles\n\t\tint np = pi.Size();\n\t\tfor (int j=0; j<np; ++j)\n\t\t{\n\t\t\t// get the next facet\n\t\t\tPatch::FACET& fj = pi.Facet(j);\n\n\t\t\t// calculate the patch area\n\t\t\t// (We multiply by two because the sum of the integration weights in FEBio sum up to the area\n\t\t\t// of the triangle in natural coordinates (=0.5)).\n\t\t\tdouble Area = fj.Area()*2.0;\n\t\t\tif (Area > 1e-15)\n\t\t\t{\n\t\t\t\t// loop over integration points\n\t\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t\t{\n\t\t\t\t\t// evaluate the spatial position of the integration point on the patch\n\t\t\t\t\tvec3d xp = fj.Position(gr[n], gs[n]);\n\n\t\t\t\t\t// evaluate the integration points on the primary and secondary surfaces\n\t\t\t\t\t// i.e. determine rs, rm\n\t\t\t\t\tdouble r1 = 0, s1 = 0, r2 = 0, s2 = 0;\n\t\t\t\t\tvec3d xs = ss.ProjectToSurface(se, xp, r1, s1);\n\t\t\t\t\tvec3d xm = ms.ProjectToSurface(me, xp, r2, s2);\n\n//\t\t\t\t\tassert((r1>=0.0)&&(s1>=0)&&(r1+s1<1.0));\n//\t\t\t\t\tassert((r2>=0.0)&&(s2>=0)&&(r2+s2<1.0));\n\n\t\t\t\t\t// evaluate shape functions\n\t\t\t\t\tse.shape_fnc(Ns[n], r1, s1);\n\t\t\t\t\tme.shape_fnc(Nm[n], r2, s2);\n\t\t\t\t}\n\n\t\t\t\t// Evaluate the contributions to the integrals\n\t\t\t\tint ns = se.Nodes();\n\t\t\t\tint nm = me.Nodes();\n\t\t\t\tfor (int A=0; A<ns; ++A)\n\t\t\t\t{\n\t\t\t\t\tint a = se.m_lnode[A];\n\n\t\t\t\t\t// loop over all the nodes on the primary facet\n\t\t\t\t\tfor (int B=0; B<ns; ++B)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble n1 = 0;\n\t\t\t\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tn1 += gw[n]*Ns[n][A]*Ns[n][B];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tn1 *= Area;\n\n\t\t\t\t\t\tint b = se.m_lnode[B];\n\t\t\t\t\t\tm_n1[a][b] += n1;\n\t\t\t\t\t}\n\n\t\t\t\t\t// loop over all the nodes on the secondary facet\n\t\t\t\t\tfor (int C = 0; C<nm; ++C)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble n2 = 0;\n\t\t\t\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tn2 += gw[n]*Ns[n][A]*Nm[n][C];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tn2 *= Area;\n\n\t\t\t\t\t\tint c = me.m_lnode[C];\n\t\t\t\t\t\tm_n2[a][c] += n2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\t\t\n\t\t}\n\t}\n\n#ifndef NDEBUG\n\t// Sanity check: sum should add up to contact area\n\t// This is for a hardcoded problem. Remove or generalize this!\n\tdouble sum1 = 0.0;\n\tfor (int A=0; A<NS; ++A)\n\t\tfor (int B=0; B<NS; ++B) sum1 += m_n1[A][B];\n\n\tdouble sum2 = 0.0;\n\tfor (int A=0; A<NS; ++A)\n\t\tfor (int C=0; C<NM; ++C) sum2 += m_n2[A][C];\n\n\tif (fabs(sum1 - 1.0) > 1e-5) feLog(\"WARNING: Mortar weights are not correct (%lg).\\n\", sum1);\n\tif (fabs(sum2 - 1.0) > 1e-5) feLog(\"WARNING: Mortar weights are not correct (%lg).\\n\", sum2);\n#endif\n}\n\n//-----------------------------------------------------------------------------\n//! Update the nodal gaps\nvoid FEMortarInterface::UpdateNodalGaps(FEMortarContactSurface& ss, FEMortarContactSurface& ms)\n{\n\t// reset nodal gaps\n\tvector<vec3d>& gap = ss.m_gap;\n\tzero(ss.m_gap);\n\n\tint NS = ss.Nodes();\n\tint NM = ms.Nodes();\n\n\t// loop over all primary nodes\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\t// loop over all primary nodes\n\t\tfor (int B=0; B<NS; ++B)\n\t\t{\n\t\t\tFENode& nodeB = ss.Node(B);\n\t\t\tvec3d& xB = nodeB.m_rt;\n\t\t\tdouble nAB = m_n1[A][B];\n\t\t\tgap[A] += xB*nAB;\n\t\t}\n\n\t\t// loop over secondary side\n\t\tfor (int C=0; C<NM; ++C)\n\t\t{\n\t\t\tFENode& nodeC = ms.Node(C);\n\t\t\tvec3d& xC = nodeC.m_rt;\n\t\t\tdouble nAC = m_n2[A][C];\n\t\t\tgap[A] -= xC*nAC;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEMortarInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FEMortarContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n// Base class for mortar-type contact formulations\nclass FEMortarInterface : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEMortarInterface(FEModel* pfem);\n\n\t//! update the mortar weights\n\tvoid UpdateMortarWeights(FESurface& ss, FESurface& ms);\n\n\t//! update the nodal gaps\n\tvoid UpdateNodalGaps(FEMortarContactSurface& ss, FEMortarContactSurface& ms);\n\nprotected:\n\tmatrix\tm_n1;\t//!< integration weights n1_AB\n\tmatrix\tm_n2;\t//!< integration weights n2_AB\n\nprivate:\n\t// integration rule\n\tFESurfaceElementTraits*\tm_pT;\n};\n"
  },
  {
    "path": "FEBioMech/FEMortarSlidingContact.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMortarSlidingContact.h\"\n#include \"FECore/mortar.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEDataExport.h>\n\n//=============================================================================\n// FEMortarSlidingSurface\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEMortarSlidingSurface::FEMortarSlidingSurface(FEModel* pfem) : FEMortarContactSurface(pfem) \n{\n\t// class exports\n\tEXPORT_DATA(PLT_VEC3F, FMT_NODE, &m_gap, \"mortar-gap vector\");\n\tEXPORT_DATA(PLT_VEC3F, FMT_NODE, &m_nu , \"mortar-normal\"    );\n}\n\n//-----------------------------------------------------------------------------\nbool FEMortarSlidingSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEMortarContactSurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint NN = Nodes();\n\n\t// allocate data structures\n\tm_p.resize(NN, 0.0);\n\tm_L.resize(NN, 0.0);\n\tm_nu.resize(NN, vec3d(0,0,0));\n\tm_norm0.resize(NN, 0.0);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMortarSlidingSurface::UpdateNormals(bool binit)\n{\n\tconst int NN = Nodes();\n\n\t// reset normals\n\tm_nu.assign(NN, vec3d(0,0,0));\n\n\t// recalculate normals\n\tconst int NF = Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tvec3d r0 = Node(el.m_lnode[j]).m_rt;\n\t\t\tvec3d r1 = Node(el.m_lnode[(j+1)%neln]).m_rt;\n\t\t\tvec3d r2 = Node(el.m_lnode[(j+neln-1)%neln]).m_rt;\n\n\t\t\tvec3d n = (r1 - r0)^(r2 - r0);\n\t\t\tm_nu[el.m_lnode[j]] += n;\n\t\t}\n\t}\n\n\t// normalize\n\tif (binit)\n\t{\n\t\tfor (int i=0; i<NN; ++i) \n\t\t{\n\t\t\tdouble d = m_nu[i].unit();\n\t\t\tm_norm0[i] = 1.0/d;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i=0; i<NN; ++i) m_nu[i] *= m_norm0[i];\n\t}\n}\n\n//=============================================================================\n// FEMortarSlidingContact\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEMortarSlidingContact, FEMortarInterface)\n\tADD_PARAMETER(m_atol   , \"tolerance\"    );\n\tADD_PARAMETER(m_eps    , \"penalty\"      );\n\tADD_PARAMETER(m_naugmin, \"minaug\"       );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"       );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMortarSlidingContact::FEMortarSlidingContact(FEModel* pfem) : FEMortarInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tm_dofX = (pfem ? GetDOFIndex(\"x\") : -1);\n\tm_dofY = (pfem ? GetDOFIndex(\"y\") : -1);\n\tm_dofZ = (pfem ? GetDOFIndex(\"z\") : -1);\n}\n\n//-----------------------------------------------------------------------------\nFEMortarSlidingContact::~FEMortarSlidingContact()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEMortarSlidingContact::Init()\n{\n\t// initialize surfaces\n\tif (m_ms.Init() == false) return false;\n\tif (m_ss.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMortarSlidingContact::Activate()\n{\n\t//! don't forget the base class\n\tFEContactInterface::Activate();\n\n\t// update the normals on the primary surface\n\tm_ss.UpdateNormals(true);\n\n\t// update nodal areas\n\tm_ss.UpdateNodalAreas();\n\n\t// update the mortar weights\n\tUpdateMortarWeights(m_ss, m_ms);\n\n\t// update the nodal gaps\n\t// (must be done after mortar eights are updated)\n\tUpdateNodalGaps(m_ss, m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FEMortarSlidingContact::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\t// For now we'll assume that each node on the primary side is connected to the secondary side\n\t// This is obviously too much, but we'll worry about improving this later\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\tvector<int> LM(3*(NS+NM));\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\tFENode& ni = m_ss.Node(i);\n\t\tLM[3*i  ] = ni.m_ID[0];\n\t\tLM[3*i+1] = ni.m_ID[1];\n\t\tLM[3*i+2] = ni.m_ID[2];\n\t}\n\tfor (int i=0; i<NM; ++i)\n\t{\n\t\tFENode& ni = m_ms.Node(i);\n\t\tLM[3*NS + 3*i  ] = ni.m_ID[0];\n\t\tLM[3*NS + 3*i+1] = ni.m_ID[1];\n\t\tLM[3*NS + 3*i+2] = ni.m_ID[2];\n\t}\n\tK.build_add(LM);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact forces\nvoid FEMortarSlidingContact::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\n\t// loop over all primary nodes\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\tvec3d nuA = m_ss.m_nu[A];\n\t\tvec3d gA = m_ss.m_gap[A];\n\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\tdouble gap = gA*nuA;\n\t\tdouble pA = m_ss.m_L[A] + eps*gap;\n//\t\tif (gap < 0.0) pA = 0.0;\n\t\t\n\t\tvec3d tA = nuA*(pA);\n\n\t\t// loop over all primary nodes\n\t\tvector<int> en(1);\n\t\tvector<int> lm(3);\n\t\tvector<double> fe(3);\n\t\tfor (int B=0; B<NS; ++B)\n\t\t{\n\t\t\tFENode& nodeB = m_ss.Node(B);\n\t\t\ten[0] = m_ss.NodeIndex(B);\n\t\t\tlm[0] = nodeB.m_ID[m_dofX];\n\t\t\tlm[1] = nodeB.m_ID[m_dofY];\n\t\t\tlm[2] = nodeB.m_ID[m_dofZ];\n\n\t\t\tdouble nAB = -m_n1[A][B];\n\t\t\tif (nAB != 0.0)\n\t\t\t{\n\t\t\t\tfe[0] = tA.x*nAB;\n\t\t\t\tfe[1] = tA.y*nAB;\n\t\t\t\tfe[2] = tA.z*nAB;\n\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\n\t\t// loop over secondary side\n\t\tfor (int C=0; C<NM; ++C)\n\t\t{\n\t\t\tFENode& nodeC = m_ms.Node(C);\n\t\t\ten[0] = m_ms.NodeIndex(C);\n\t\t\tlm[0] = nodeC.m_ID[m_dofX];\n\t\t\tlm[1] = nodeC.m_ID[m_dofY];\n\t\t\tlm[2] = nodeC.m_ID[m_dofZ];\n\n\t\t\tdouble nAC = m_n2[A][C];\n\t\t\tif (nAC != 0.0)\n\t\t\t{\n\t\t\t\tfe[0] = tA.x*nAC;\n\t\t\t\tfe[1] = tA.y*nAC;\n\t\t\t\tfe[2] = tA.z*nAC;\n\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact stiffness\nvoid FEMortarSlidingContact::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tContactGapStiffness(LS);\n\tContactNormalStiffness(LS);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact stiffness\nvoid FEMortarSlidingContact::ContactGapStiffness(FELinearSystem& LS)\n{\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\n\t// A. Linearization of the gap function\n\tvector<int> lmi(3), lmj(3);\n\tmatrix kA(3, 3), kG(3, 3);\n\tFEElementMatrix ke;\n\tke.resize(3, 3);\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\tvec3d nuA = m_ss.m_nu[A];\n\t\tdouble eps = m_eps*m_ss.m_A[A];\n\n\t\t// loop over all primary nodes\n\t\tfor (int B=0; B<NS; ++B)\n\t\t{\n\t\t\tFENode& nodeB = m_ss.Node(B);\n\t\t\tlmi[0] = nodeB.m_ID[0];\n\t\t\tlmi[1] = nodeB.m_ID[1];\n\t\t\tlmi[2] = nodeB.m_ID[2];\n\n\t\t\tdouble nAB = m_n1[A][B];\n\t\t\tif (nAB != 0.0)\n\t\t\t{\n\t\t\t\tkA[0][0] = eps*nAB*(nuA.x*nuA.x); kA[0][1] = eps*nAB*(nuA.x*nuA.y); kA[0][2] = eps*nAB*(nuA.x*nuA.z);\n\t\t\t\tkA[1][0] = eps*nAB*(nuA.y*nuA.x); kA[1][1] = eps*nAB*(nuA.y*nuA.y); kA[1][2] = eps*nAB*(nuA.y*nuA.z);\n\t\t\t\tkA[2][0] = eps*nAB*(nuA.z*nuA.x); kA[2][1] = eps*nAB*(nuA.z*nuA.y); kA[2][2] = eps*nAB*(nuA.z*nuA.z);\n\n\t\t\t\t// loop over primary nodes\n\t\t\t\tfor (int C=0; C<NS; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ss.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = m_n1[A][C];\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tkG[0][0] = nAC; kG[0][1] = 0.0; kG[0][2] = 0.0;\n\t\t\t\t\t\tkG[1][0] = 0.0; kG[1][1] = nAC; kG[1][2] = 0.0;\n\t\t\t\t\t\tkG[2][0] = 0.0; kG[2][1] = 0.0; kG[2][2] = nAC;\n\n\t\t\t\t\t\tke = kA*kG;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// loop over secondary nodes\n\t\t\t\tfor (int C=0; C<NM; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ms.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = -m_n2[A][C];\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tkG[0][0] = nAC; kG[0][1] = 0.0; kG[0][2] = 0.0;\n\t\t\t\t\t\tkG[1][0] = 0.0; kG[1][1] = nAC; kG[1][2] = 0.0;\n\t\t\t\t\t\tkG[2][0] = 0.0; kG[2][1] = 0.0; kG[2][2] = nAC;\n\n\t\t\t\t\t\tke = kA*kG;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// loop over all secondary nodes\n\t\tfor (int B=0; B<NM; ++B)\n\t\t{\n\t\t\tFENode& nodeB = m_ms.Node(B);\n\t\t\tlmi[0] = nodeB.m_ID[0];\n\t\t\tlmi[1] = nodeB.m_ID[1];\n\t\t\tlmi[2] = nodeB.m_ID[2];\n\n\t\t\tdouble nAB = -m_n2[A][B];\n\t\t\tif (nAB != 0.0)\n\t\t\t{\n\t\t\t\tkA[0][0] = eps*nAB*(nuA.x*nuA.x); kA[0][1] = eps*nAB*(nuA.x*nuA.y); kA[0][2] = eps*nAB*(nuA.x*nuA.z);\n\t\t\t\tkA[1][0] = eps*nAB*(nuA.y*nuA.x); kA[1][1] = eps*nAB*(nuA.y*nuA.y); kA[1][2] = eps*nAB*(nuA.y*nuA.z);\n\t\t\t\tkA[2][0] = eps*nAB*(nuA.z*nuA.x); kA[2][1] = eps*nAB*(nuA.z*nuA.y); kA[2][2] = eps*nAB*(nuA.z*nuA.z);\n\n\t\t\t\t// loop over primary nodes\n\t\t\t\tfor (int C=0; C<NS; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ss.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = m_n1[A][C];\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tkG[0][0] = nAC; kG[0][1] = 0.0; kG[0][2] = 0.0;\n\t\t\t\t\t\tkG[1][0] = 0.0; kG[1][1] = nAC; kG[1][2] = 0.0;\n\t\t\t\t\t\tkG[2][0] = 0.0; kG[2][1] = 0.0; kG[2][2] = nAC;\n\n\t\t\t\t\t\tke = kA*kG;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// loop over secondary nodes\n\t\t\t\tfor (int C=0; C<NM; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ms.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = -m_n2[A][C];\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tkG[0][0] = nAC; kG[0][1] = 0.0; kG[0][2] = 0.0;\n\t\t\t\t\t\tkG[1][0] = 0.0; kG[1][1] = nAC; kG[1][2] = 0.0;\n\t\t\t\t\t\tkG[2][0] = 0.0; kG[2][1] = 0.0; kG[2][2] = nAC;\n\n\t\t\t\t\t\tke = kA*kG;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact stiffness\nvoid FEMortarSlidingContact::ContactNormalStiffness(FELinearSystem& LS)\n{\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\n\tvector<int> lm1(3);\n\tvector<int> lm2(3);\n\tFEElementMatrix ke;\n\tke.resize(3, 3);\n\tint NF = m_ss.Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& f = m_ss.Element(i);\n\t\tint nn = f.Nodes();\n\t\tfor (int j=0; j<nn; ++j)\n\t\t{\n\t\t\tint jp1 = (j+1)%nn;\n\t\t\tint jm1 = (j+nn-1)%nn;\n\t\t\tint A = f.m_lnode[j];\n\n\t\t\tvec3d vA = m_ss.m_nu[A];\n\t\t\tvec3d gA = m_ss.m_gap[A];\n\t\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\t\tdouble pA = m_ss.m_L[A] + eps*(gA*vA);\n\t\t\tdouble normA = m_ss.m_norm0[A];\n\n\t\t\tmat3d kA = ((vA&gA)*eps + mat3dd(pA));\n\n\t\t\tFENode& nodej1 = m_ss.Node(f.m_lnode[jp1]);\n\t\t\tvec3d& x1 = nodej1.m_rt;\n\t\t\tmat3da k1(x1);\n\t\t\tlm1[0] = nodej1.m_ID[0];\n\t\t\tlm1[1] = nodej1.m_ID[1];\n\t\t\tlm1[2] = nodej1.m_ID[2];\n\n\t\t\tFENode& nodej2 = m_ss.Node(f.m_lnode[jm1]);\n\t\t\tvec3d& x2 = nodej2.m_rt;\n\t\t\tmat3da k2(x2);\n\t\t\tlm2[0] = nodej2.m_ID[0];\n\t\t\tlm2[1] = nodej2.m_ID[1];\n\t\t\tlm2[2] = nodej2.m_ID[2];\n\n\t\t\t// loop over primary nodes\n\t\t\tfor (int B=0; B<NS; ++B)\n\t\t\t{\n\t\t\t\tFENode& nodeB = m_ss.Node(B);\n\t\t\t\t\n\t\t\t\tdouble nAB = m_n1[A][B];\n\t\t\t\tif (nAB != 0.0)\n\t\t\t\t{\n\t\t\t\t\tvector<int> lmi(3);\n\t\t\t\t\tlmi[0] = nodeB.m_ID[0];\n\t\t\t\t\tlmi[1] = nodeB.m_ID[1];\n\t\t\t\t\tlmi[2] = nodeB.m_ID[2];\n\n\t\t\t\t\tmat3d kab = (kA*k1)*(nAB*normA);\n\t\t\t\t\tke[0][0] = kab(0,0); ke[0][1] = kab(0,1); ke[0][2] = kab(0,2);\n\t\t\t\t\tke[1][0] = kab(1,0); ke[1][1] = kab(1,1); ke[1][2] = kab(1,2);\n\t\t\t\t\tke[2][0] = kab(2,0); ke[2][1] = kab(2,1); ke[2][2] = kab(2,2);\n\n\t\t\t\t\tke.SetIndices(lmi, lm2);\n\t\t\t\t\tLS.Assemble(ke);\n\n\t\t\t\t\tkab = (kA*k2)*(-nAB*normA);\n\t\t\t\t\tke[0][0] = kab(0,0); ke[0][1] = kab(0,1); ke[0][2] = kab(0,2);\n\t\t\t\t\tke[1][0] = kab(1,0); ke[1][1] = kab(1,1); ke[1][2] = kab(1,2);\n\t\t\t\t\tke[2][0] = kab(2,0); ke[2][1] = kab(2,1); ke[2][2] = kab(2,2);\n\n\t\t\t\t\tke.SetIndices(lmi, lm1);\n\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// loop over secondary nodes\n\t\t\tfor (int B=0; B<NM; ++B)\n\t\t\t{\n\t\t\t\tFENode& nodeB = m_ms.Node(B);\n\t\t\t\t\n\t\t\t\tdouble nAB = m_n2[A][B];\n\t\t\t\tif (nAB != 0.0)\n\t\t\t\t{\n\t\t\t\t\tvector<int> lmi(3);\n\t\t\t\t\tlmi[0] = nodeB.m_ID[0];\n\t\t\t\t\tlmi[1] = nodeB.m_ID[1];\n\t\t\t\t\tlmi[2] = nodeB.m_ID[2];\n\n\t\t\t\t\tmat3d kab = (kA*k1)*(nAB*normA);\n\t\t\t\t\tke[0][0] = kab(0,0); ke[0][1] = kab(0,1); ke[0][2] = kab(0,2);\n\t\t\t\t\tke[1][0] = kab(1,0); ke[1][1] = kab(1,1); ke[1][2] = kab(1,2);\n\t\t\t\t\tke[2][0] = kab(2,0); ke[2][1] = kab(2,1); ke[2][2] = kab(2,2);\n\n\t\t\t\t\tke.SetIndices(lmi, lm2);\n\t\t\t\t\tLS.Assemble(ke);\n\n\t\t\t\t\tkab = (kA*k2)*(-nAB*normA);\n\t\t\t\t\tke[0][0] = kab(0,0); ke[0][1] = kab(0,1); ke[0][2] = kab(0,2);\n\t\t\t\t\tke[1][0] = kab(1,0); ke[1][1] = kab(1,1); ke[1][2] = kab(1,2);\n\t\t\t\t\tke[2][0] = kab(2,0); ke[2][1] = kab(2,1); ke[2][2] = kab(2,2);\n\n\t\t\t\t\tke.SetIndices(lmi, lm1);\n\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate Lagrangian augmentations\nbool FEMortarSlidingContact::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tdouble max_err = 0.0;\n\tint NS = m_ss.Nodes();\n\t// loop over all primary nodes\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\tvec3d vA = m_ss.m_nu[A];\n\t\tvec3d gA = m_ss.m_gap[A];\n\t\tdouble gap = gA*vA;\n\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\t\n\t\tdouble Lold = m_ss.m_L[A];\n\t\tdouble Lnew = Lold + eps*gap;\n\n\t\tdouble err = fabs((Lold - Lnew)/(Lold + Lnew));\n\t\tif (err > max_err) max_err = err;\n\t}\n\n\tbool bconv = true;\n\tif ((m_atol > 0) && (max_err > m_atol)) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif (m_naugmax <= naug) bconv = true;\n\n\tfeLog(\" mortar interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    normal force : %15le\", max_err);\n\tif (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\n\n\tif (bconv == false)\n\t{\n\t\t// loop over all primary nodes\n\t\tfor (int A=0; A<NS; ++A)\n\t\t{\n\t\t\tvec3d vA = m_ss.m_nu[A];\n\t\t\tvec3d gA = m_ss.m_gap[A];\n\t\t\tdouble gap = gA*vA;\n\t\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\t\n\t\t\tdouble Lold = m_ss.m_L[A];\n\t\t\tdouble Lnew = Lold + eps*gap;\n\t\t\tm_ss.m_L[A] = Lnew;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! update interface data\nvoid FEMortarSlidingContact::Update()\n{\n\tm_ss.UpdateNormals(false);\n\tUpdateMortarWeights(m_ss, m_ms);\n\tUpdateNodalGaps(m_ss, m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! serialize data to archive\nvoid FEMortarSlidingContact::Serialize(DumpStream& ar)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEMortarSlidingContact.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMortarInterface.h\"\n#include \"FEMortarContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! This class represents a surface used by the mortar contact interface.\nclass FEMortarSlidingSurface : public FEMortarContactSurface\n{\npublic:\n\tFEMortarSlidingSurface(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\n\t//! update the normals\n\tvoid UpdateNormals(bool binit);\n\npublic:\n\tvector<double>\tm_p;\t\t//!< nodal contact pressures\n\tvector<double>\tm_L;\t\t//!< Lagrange multipliers\n\tvector<vec3d>\tm_nu;\t\t//!< nodal normals\n\tvector<double>\tm_norm0;\t//!< initial (inverse) normal lenghts\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a mortar contact formulation for frictionless, sliding contact\nclass FEMortarSlidingContact : public FEMortarInterface\n{\npublic:\n\t//! constructor\n\tFEMortarSlidingContact(FEModel* pfem);\n\n\t//! destructor\n\t~FEMortarSlidingContact();\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\npublic:\n\t//! temporary construct to determine if contact interface uses nodal integration rule (or facet)\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! one-time initialization\n\tbool Init() override;\n\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! update interface data\n\tvoid Update() override;\n\nprotected:\n\t// contact stiffness contributions\n\tvoid ContactGapStiffness(FELinearSystem& LS);\n\tvoid ContactNormalStiffness(FELinearSystem& LS);\n\nprivate:\n\tdouble\tm_atol;\t\t//!< augmented Lagrangian tolerance\n\tdouble\tm_eps;\t\t//!< penalty factor\n\tint\t\tm_naugmin;\t//!< minimum number of augmentations\n\tint\t\tm_naugmax;\t//!< maximum number of augmentations\n\nprivate:\n\tFEMortarSlidingSurface\tm_ms;\t//!< mortar surface\n\tFEMortarSlidingSurface\tm_ss;\t//!< non-mortar surface\n\n\tint\t\tm_dofX;\n\tint\t\tm_dofY;\n\tint\t\tm_dofZ;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMortarTiedContact.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMortarTiedContact.h\"\n#include \"FECore/mortar.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n\n//=============================================================================\n// FEMortarTiedSurface\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEMortarTiedSurface::FEMortarTiedSurface(FEModel* pfem) : FEMortarContactSurface(pfem) {}\n\n//-----------------------------------------------------------------------------\nbool FEMortarTiedSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEMortarContactSurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint NN = Nodes();\n\n\t// allocate data structures\n\tm_L.resize(NN, vec3d(0,0,0));\n\n\treturn true;\n}\n\n//=============================================================================\n// FEMortarTiedContact\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEMortarTiedContact, FEMortarInterface)\n\tADD_PARAMETER(m_atol   , \"tolerance\"    );\n\tADD_PARAMETER(m_eps    , \"penalty\"      );\n\tADD_PARAMETER(m_naugmin, \"minaug\"       );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"       );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMortarTiedContact::FEMortarTiedContact(FEModel* pfem) : FEMortarInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tm_dofX = (pfem ? GetDOFIndex(\"x\") : -1);\n\tm_dofY = (pfem ? GetDOFIndex(\"y\") : -1);\n\tm_dofZ = (pfem ? GetDOFIndex(\"z\") : -1);\n}\n\n//-----------------------------------------------------------------------------\nbool FEMortarTiedContact::Init()\n{\n\t// initialize surfaces\n\tif (m_ms.Init() == false) return false;\n\tif (m_ss.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMortarTiedContact::Activate()\n{\n\t//! don't forget the base class\n\tFEContactInterface::Activate();\n\n\tm_ss.UpdateNodalAreas();\n\n\t// update the mortar weights\n\t// For tied interfaces, this is only done once, during activation\n\tUpdateMortarWeights(m_ss, m_ms);\n\n\t// update the nodal gaps\n\t// (must be done after mortar eights are updated)\n\tUpdateNodalGaps(m_ss, m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FEMortarTiedContact::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\t// For now we'll assume that each node on the primary side is connected to the secondary side\n\t// This is obviously too much, but we'll worry about improving this later\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\tvector<int> LM(3*(NS+NM));\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\tFENode& ni = m_ss.Node(i);\n\t\tLM[3*i  ] = ni.m_ID[0];\n\t\tLM[3*i+1] = ni.m_ID[1];\n\t\tLM[3*i+2] = ni.m_ID[2];\n\t}\n\tfor (int i=0; i<NM; ++i)\n\t{\n\t\tFENode& ni = m_ms.Node(i);\n\t\tLM[3*NS + 3*i  ] = ni.m_ID[0];\n\t\tLM[3*NS + 3*i+1] = ni.m_ID[1];\n\t\tLM[3*NS + 3*i+2] = ni.m_ID[2];\n\t}\n\tK.build_add(LM);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact forces\nvoid FEMortarTiedContact::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\n\t// loop over all primary nodes\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\tvec3d gA = m_ss.m_gap[A];\n\t\tvec3d tA = m_ss.m_L[A] + gA*eps;\n\n\t\t// loop over all primary nodes\n\t\tvector<int> en(1);\n\t\tvector<int> lm(3);\n\t\tvector<double> fe(3);\n\t\tfor (int B=0; B<NS; ++B)\n\t\t{\n\t\t\tFENode& nodeB = m_ss.Node(B);\n\t\t\ten[0] = m_ss.NodeIndex(B);\n\t\t\tlm[0] = nodeB.m_ID[m_dofX];\n\t\t\tlm[1] = nodeB.m_ID[m_dofY];\n\t\t\tlm[2] = nodeB.m_ID[m_dofZ];\n\n\t\t\tdouble nAB = -m_n1[A][B];\n\t\t\tif (nAB != 0.0)\n\t\t\t{\n\t\t\t\tfe[0] = tA.x*nAB;\n\t\t\t\tfe[1] = tA.y*nAB;\n\t\t\t\tfe[2] = tA.z*nAB;\n\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\n\t\t// loop over secondary side\n\t\tfor (int C=0; C<NM; ++C)\n\t\t{\n\t\t\tFENode& nodeC = m_ms.Node(C);\n\t\t\ten[0] = m_ms.NodeIndex(C);\n\t\t\tlm[0] = nodeC.m_ID[m_dofX];\n\t\t\tlm[1] = nodeC.m_ID[m_dofY];\n\t\t\tlm[2] = nodeC.m_ID[m_dofZ];\n\n\t\t\tdouble nAC = m_n2[A][C];\n\t\t\tif (nAC != 0.0)\n\t\t\t{\n\t\t\t\tfe[0] = tA.x*nAC;\n\t\t\t\tfe[1] = tA.y*nAC;\n\t\t\t\tfe[2] = tA.z*nAC;\n\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact stiffness\nvoid FEMortarTiedContact::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint NS = m_ss.Nodes();\n\tint NM = m_ms.Nodes();\n\n\t// A. Linearization of the gap function\n\tvector<int> lmi(3), lmj(3);\n\tFEElementMatrix ke;\n\tke.resize(3, 3);\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\tdouble eps = m_eps*m_ss.m_A[A];\n\n\t\t// loop over all primary nodes\n\t\tfor (int B=0; B<NS; ++B)\n\t\t{\n\t\t\tFENode& nodeB = m_ss.Node(B);\n\t\t\tlmi[0] = nodeB.m_ID[0];\n\t\t\tlmi[1] = nodeB.m_ID[1];\n\t\t\tlmi[2] = nodeB.m_ID[2];\n\n\t\t\tdouble nAB = m_n1[A][B]*eps;\n\t\t\tif (nAB != 0.0)\n\t\t\t{\n\t\t\t\t// loop over primary nodes\n\t\t\t\tfor (int C=0; C<NS; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ss.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = m_n1[A][C]*nAB;\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[0][0] = nAC; ke[0][1] = 0.0; ke[0][2] = 0.0;\n\t\t\t\t\t\tke[1][0] = 0.0; ke[1][1] = nAC; ke[1][2] = 0.0;\n\t\t\t\t\t\tke[2][0] = 0.0; ke[2][1] = 0.0; ke[2][2] = nAC;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// loop over secondary nodes\n\t\t\t\tfor (int C=0; C<NM; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ms.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = -m_n2[A][C]*nAB;\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[0][0] = nAC; ke[0][1] = 0.0; ke[0][2] = 0.0;\n\t\t\t\t\t\tke[1][0] = 0.0; ke[1][1] = nAC; ke[1][2] = 0.0;\n\t\t\t\t\t\tke[2][0] = 0.0; ke[2][1] = 0.0; ke[2][2] = nAC;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// loop over all secondary nodes\n\t\tfor (int B=0; B<NM; ++B)\n\t\t{\n\t\t\tFENode& nodeB = m_ms.Node(B);\n\t\t\tlmi[0] = nodeB.m_ID[0];\n\t\t\tlmi[1] = nodeB.m_ID[1];\n\t\t\tlmi[2] = nodeB.m_ID[2];\n\n\t\t\tdouble nAB = -m_n2[A][B]*eps;\n\t\t\tif (nAB != 0.0)\n\t\t\t{\n\t\t\t\t// loop over primary nodes\n\t\t\t\tfor (int C=0; C<NS; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ss.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = m_n1[A][C]*nAB;\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[0][0] = nAC; ke[0][1] = 0.0; ke[0][2] = 0.0;\n\t\t\t\t\t\tke[1][0] = 0.0; ke[1][1] = nAC; ke[1][2] = 0.0;\n\t\t\t\t\t\tke[2][0] = 0.0; ke[2][1] = 0.0; ke[2][2] = nAC;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// loop over secondary nodes\n\t\t\t\tfor (int C=0; C<NM; ++C)\n\t\t\t\t{\n\t\t\t\t\tFENode& nodeC = m_ms.Node(C);\n\t\t\t\t\tlmj[0] = nodeC.m_ID[0];\n\t\t\t\t\tlmj[1] = nodeC.m_ID[1];\n\t\t\t\t\tlmj[2] = nodeC.m_ID[2];\n\n\t\t\t\t\tdouble nAC = -m_n2[A][C]*nAB;\n\t\t\t\t\tif (nAC != 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[0][0] = nAC; ke[0][1] = 0.0; ke[0][2] = 0.0;\n\t\t\t\t\t\tke[1][0] = 0.0; ke[1][1] = nAC; ke[1][2] = 0.0;\n\t\t\t\t\t\tke[2][0] = 0.0; ke[2][1] = 0.0; ke[2][2] = nAC;\n\n\t\t\t\t\t\tke.SetIndices(lmi, lmj);\n\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate Lagrangian augmentations\nbool FEMortarTiedContact::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tdouble max_err = 0.0;\n\tint NS = m_ss.Nodes();\n\t// loop over all primary nodes\n\tfor (int A=0; A<NS; ++A)\n\t{\n\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\tvec3d gA = m_ss.m_gap[A];\n\t\tvec3d Lold = m_ss.m_L[A];\n\t\tvec3d Lnew = Lold + gA*eps;\n\n\t\tdouble uold = Lold.norm();\n\t\tdouble unew = Lnew.norm();\n\n\t\tdouble err = fabs((uold - unew)/(uold + unew));\n\t\tif (err > max_err) max_err = err;\n\t}\n\n\tbool bconv = true;\n\tif ((m_atol > 0) && (max_err > m_atol)) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif (m_naugmax <= naug) bconv = true;\n\n\tfeLog(\" mortar interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    normal force : %15le\", max_err);\n\tif (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\n\tif (bconv == false)\n\t{\n\t\t// loop over all primary nodes\n\t\tfor (int A=0; A<NS; ++A)\n\t\t{\n\t\t\tdouble eps = m_eps*m_ss.m_A[A];\n\t\t\tvec3d gA = m_ss.m_gap[A];\n\t\t\tvec3d Lold = m_ss.m_L[A];\n\t\t\tvec3d Lnew = Lold + gA*eps;\n\t\t\tm_ss.m_L[A] = Lnew;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! update interface data\nvoid FEMortarTiedContact::Update()\n{\n\tUpdateNodalGaps(m_ss, m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! serialize data to archive\nvoid FEMortarTiedContact::Serialize(DumpStream& ar)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEMortarTiedContact.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMortarInterface.h\"\n#include \"FEMortarContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! This class represents a surface used by the mortar contact interface.\nclass FEMortarTiedSurface : public FEMortarContactSurface\n{\npublic:\n\tFEMortarTiedSurface(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\npublic:\n\tvector<vec3d>\tm_L;\t\t//!< Lagrange multipliers\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a mortar based tied interface.\nclass FEMortarTiedContact : public FEMortarInterface\n{\npublic:\n\t//! constructor\n\tFEMortarTiedContact(FEModel* pfem);\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\npublic:\n\t//! temporary construct to determine if contact interface uses nodal integration rule (or facet)\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! one-time initialization\n\tbool Init() override;\n\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! update interface data\n\tvoid Update() override;\n\nprivate:\n\tdouble\tm_atol;\t\t//!< augmented Lagrangian tolerance\n\tdouble\tm_eps;\t\t//!< penalty factor\n\tint\t\tm_naugmin;\t//!< minimum number of augmentations\n\tint\t\tm_naugmax;\t//!< maximum number of augmentations\n\nprivate:\n\tFEMortarTiedSurface\tm_ms;\t//!< mortar surface\n\tFEMortarTiedSurface\tm_ss;\t//!< non-mortar surface\n\n\tint\t\tm_dofX;\n\tint\t\tm_dofY;\n\tint\t\tm_dofZ;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMovingFrameLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMovingFrameLoad.h\"\n#include \"FEElasticMaterialPoint.h\"\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEMovingFrameLoad, FEBodyForce)\n\tADD_PARAMETER(m_wi.x, \"wx\")->setLongName(\"x-angular velocity\")->setUnits(UNIT_ANGULAR_VELOCITY);\n\tADD_PARAMETER(m_wi.y, \"wy\")->setLongName(\"y-angular velocity\")->setUnits(UNIT_ANGULAR_VELOCITY);\n\tADD_PARAMETER(m_wi.z, \"wz\")->setLongName(\"z-angular velocity\")->setUnits(UNIT_ANGULAR_VELOCITY);\n\tADD_PARAMETER(m_at.x, \"ax\")->setLongName(\"x-linear acceleration\")->setUnits(UNIT_ACCELERATION);\n\tADD_PARAMETER(m_at.y, \"ay\")->setLongName(\"y-linear acceleration\")->setUnits(UNIT_ACCELERATION);\n\tADD_PARAMETER(m_at.z, \"az\")->setLongName(\"z-linear acceleration\")->setUnits(UNIT_ACCELERATION);\n\tADD_PARAMETER(m_frame, \"frame\")->setEnums(\"fixed\\0moving\\0\");\n\n\tADD_PARAMETER(m_wt, \"wt\")->setLongName(\"Angular velocity in fixed frame\")->setUnits(UNIT_ANGULAR_VELOCITY)->SetFlags(FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_Wt, \"Wt\")->setLongName(\"Angular velocity in moving frame\")->setUnits(UNIT_ANGULAR_VELOCITY)->SetFlags(FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_rt, \"rt\")->setLongName(\"Rotation vector in fixed frame\")->setUnits(UNIT_RADIAN)->SetFlags(FE_PARAM_HIDDEN);\nEND_FECORE_CLASS();\n\nFEMovingFrameLoad::FEMovingFrameLoad(FEModel* fem) : FEBodyForce(fem)\n{\n\tm_frame = 0; // assume fixed frame angular velocity\n}\n\nvoid FEMovingFrameLoad::Activate()\n{\n\tm_ap = m_at;\n\tm_wp = m_wt;\n\tm_Wp = m_Wt;\n\tm_alp = m_alt;\n\tm_Alp = m_Alt;\n\tm_qp = m_qt;\n\tFEBodyForce::Activate();\n}\n\nvoid FEMovingFrameLoad::PrepStep()\n{\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble dt = tp.timeIncrement;\n\tdouble alpha = tp.alphaf;\n\n\t// evaluate quantities at current time (t) and intermediate time\n\tif (m_frame == 0)\n\t{\n\t\tm_wt = m_wi; // input is ang vel in fixed frame\n\n\t\t// current quantities\n\t\tm_alp = m_alt;\n\t\tm_alt = (m_wt - m_wp) / dt;\n\t\tquatd wt(m_wt.x, m_wt.y, m_wt.z, 0.0);\n\t\tm_qt = m_qp + (wt * m_qp) * (0.5 * dt); m_qt.MakeUnit();\n\n\t\t// intermediate quantities\n\t\tm_al = m_alt * alpha + m_alp * (1.0 - alpha);\n\t\tm_w = m_wt * alpha + m_wp * (1.0 - alpha);\n\t\tquatd wa(m_w.x, m_w.y, m_w.z, 0.0);\n\t\tm_q = m_qp + (wa * m_qp) * (0.5 * dt * alpha); m_q.MakeUnit();\n\n\t\tm_qp = m_qt;\n\t\tm_wp = m_wt;\n\n\t\t// quantities in body frame\n\t\tquatd qti = m_qt.Inverse();\n\t\tm_Wt = qti * m_wt;\n\t\tm_rt = m_qt.GetRotationVector();\n\n\t\tquatd qi = m_q.Inverse();\n\t\tm_Al = qi * m_al;\n\t\tm_W = qi * m_w;\n\t}\n\telse\n\t{\n\t\tm_Wt = m_wi; // input is ang vel in body frame\n\n\t\t// current quantities\n\t\tm_Alp = m_Alt;\n\t\tm_Alt = (m_Wt - m_Wp) / dt;\n\t\tquatd Wt(m_Wt.x, m_Wt.y, m_Wt.z, 0.0);\n\t\tm_qt = m_qp + (m_qp * Wt) * (0.5 * dt); m_qt.MakeUnit();\n\n\t\t// intermediate quantities\n\t\tm_Al = m_Alt * alpha + m_Alp * (1.0 - alpha);\n\t\tm_W = m_Wt * alpha + m_Wp * (1.0 - alpha);\n\t\tquatd Wa(m_W.x, m_W.y, m_W.z, 0.0);\n\t\tm_q = m_qp + (m_qp * Wa) * (0.5 * dt * alpha); m_q.MakeUnit();\n\n\t\tm_qp = m_qt;\n\t\tm_Wp = m_Wt;\n\n\t\t// quantities in fixed frame\n\t\tm_wt = m_qt * m_Wt;\n\t\tm_rt = m_qt.GetRotationVector();\n\t\tm_alp = m_alt;\n\t\tm_alt = m_qt * m_Alt;\n\n\t\tm_al = m_q * m_Al;\n\t\tm_w  = m_q * m_W;\n\t}\n\n\t// linear acceleration (always assumed to be given in the fixed frame)\n\tm_a = m_at * alpha + m_ap * (1.0 - alpha);\n\tm_ap = m_at;\n\tquatd qi = m_q.Inverse();\n\tm_A = qi * m_a;\n\n\tFEBodyForce::PrepStep();\n}\n\nvec3d FEMovingFrameLoad::force(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& ep = *pt.ExtractData<FEElasticMaterialPoint>();\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n\n\t// NOTE: position and velocity at material point are already evaluated at intermediate time\n\tvec3d r = pt.m_rt;\n\tvec3d v = ep.m_v;\n\n\tvec3d f = m_A + (m_Al ^ r) + (m_W ^ (m_W ^ r)) + (m_W ^ v) * 2.0;\n\treturn f;\n}\n\nmat3d FEMovingFrameLoad::stiffness(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& ep = *pt.ExtractData<FEElasticMaterialPoint>();\n\tvec3d v = ep.m_v;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble gamma = tp.gamma;\n\tdouble dt = tp.timeIncrement;\n\tdouble alpha = tp.alphaf;\n\n\tmat3da Sw(m_W);\n\tmat3da A(m_Al);\n\tmat3d S2 = Sw * Sw;\n\tmat3d K = (S2 + A + Sw * (4.0 * gamma / dt))*alpha;\n\n\treturn K;\n}\n"
  },
  {
    "path": "FEBioMech/FEMovingFrameLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBodyForce.h\"\n\n//! This body load emulates the effect of a moving frame\nclass FEMovingFrameLoad : public FEBodyForce\n{\npublic:\n\tFEMovingFrameLoad(FEModel* fem);\n\n\tvoid Activate() override;\n\n\tvoid PrepStep() override;\n\n\tvec3d force(FEMaterialPoint& pt) override;\n\n\tmat3d stiffness(FEMaterialPoint& pt) override;\n\nprivate:\n\tvec3d\tm_at;\t// linear acceleration of moving frame in fixed frame\n\tvec3d\tm_wi;\t// angular velocity input\n\tint\t\tm_frame;\t// fixed (=0), or moving (=1) frame for input angular velocity\n\nprivate:\n\tvec3d\tm_ap, m_a, m_A;\n\tvec3d\tm_wp, m_w, m_Wp, m_W;\n\tvec3d\tm_alt, m_alp, m_al, m_Alt, m_Alp, m_Al;\n\tquatd\tm_qt, m_qp, m_q;\n\n\t// output parameters\n\tvec3d m_wt; // angular velocity in fixed frame (output)\n\tvec3d m_Wt; // angular velocity in moving frame (output)\n\tvec3d m_rt; // rotational vector in fixed frame (output)\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEMuscleMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMuscleMaterial.h\"\n#include <FECore/FEConstValueVec3.h>\n\n#ifndef SQR\n\t#define SQR(x) ((x)*(x))\n#endif\n\n/////////////////////////////////////////////////////////////////////////\n// FEMuscleMaterial\n/////////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMuscleMaterial, FEUncoupledMaterial)\n\tADD_PARAMETER(m_G1, \"g1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G2, \"g2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G3, \"g3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_P1, \"p1\");\n\tADD_PARAMETER(m_P2, \"p2\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_Lofl, \"Lofl\");\n\tADD_PARAMETER(m_smax, \"smax\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_lam1, \"lam_max\");\n\tADD_PARAMETER(m_alpha, \"activation\");\n\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMuscleMaterial::FEMuscleMaterial(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_G1 = 0;\n\tm_G2 = 0;\n\tm_G3 = 0;\n\tm_alpha = 0.0;\n\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress at a material point.\n//!\nmat3ds FEMuscleMaterial::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q*m_fiber->unitVector(mp);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble la = a.unit();\n\tdouble lat = la*pow(J, -1.0/3.0); // i.e. lambda tilde = sqrt(I4)\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = Jm23*F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// calculate Ba = B*a\n\tvec3d Ba = B*a;\n\n\t// ----- invariants of B -----\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr() );\n\tdouble I4 = lat*lat;\n\tdouble I5 = I4*(a*Ba);\n\n\t// ----- calculate new invariants b1 and b2 of B ------\n\n\t// note that we need to be carefull about roundoff errors\n\t// since these functions may create numerical problems\n\n\tdouble g = I5/(I4*I4) - 1;\n\tdouble b1 = (g > 0 ? sqrt(g) : 0);\n\t\n\tdouble b2 = acosh(0.5*(I1*I4 - I5)/lat);\n\n\t// calculate omage (w)\n\tdouble I4r = sqrt(I4);\n\tdouble w = 0.5*(I1*I4 - I5)/sqrt(I4);\n\n\t// set beta and ksi to their limit values\n\tdouble beta = 1.0;\n\tdouble ksi = -1.0/3.0;\n\n\t// if w not equals unity, we can calculate beta and ksi\n\tif (w > 1.0001)\n\t{\n\t\tbeta = b2/sqrt(w*w-1);\n\t\tksi = (1.0/(w*w-1))*(1 - w*b2/sqrt(w*w-1));\n\t}\n\n\t// evaluate material parameters\n\tdouble G1 = m_G1(mp);\n\tdouble G2 = m_G2(mp);\n\tdouble G3 = m_G3(mp);\n\tdouble P1 = m_P1(mp);\n\tdouble P2 = m_P2(mp);\n\tdouble Lofl = m_Lofl(mp);\n\tdouble lam1 = m_lam1(mp);\n\tdouble alpha = m_alpha(mp);\n\tdouble smax  = m_smax(mp);\n\n\t// ----- calculate strain derivatives -----\n\n\t// We assume that W(I1, I4, I5, alpha) = F1(B1(I4, I5)) + F2(B2(I1,I4,I5)) + F3(lam(I4), alpha)\n\tdouble W1, W2, W4, W5;\n\n\t// calculate derivatives for F1\n\tdouble F1D4 = -2*G1*(I5/(I4*I4*I4));\n\tdouble F1D5 = G1/(I4*I4);\n\n\t// calculate derivatives for F2\n\tdouble F2D1 =  G2*beta*lat;\n\tdouble F2D4 =  G2*beta*(I1*I4 + I5)*0.5*pow(I4, -1.5);\n\tdouble F2D5 = -G2*beta/lat;\n\n\t// calculate derivatives for F3\n\t// these terms are proposed to fix the zero-stress problem\n\tdouble F3D4 = 9.0*G3*0.125*log(I4)/I4;\n\n\t// calculate passive fiber force\n\tdouble Fp;\n\tif (lat <= Lofl)\n\t{\n\t\tFp = 0;\n\t}\n\telse if (lat < lam1)\n\t{\n\t\tFp = P1*(exp(P2*(lat/Lofl - 1)) - 1);\n\t}\n\telse\n\t{\n\t\tdouble P3, P4;\n\n\t\tP3 = P1*P2*exp(P2*(lam1/Lofl - 1));\n\t\tP4 = P1*(exp(P2*(lam1/Lofl - 1)) - 1) - P3*lam1/Lofl;\n\n\t\tFp = P3*lat/Lofl + P4;\n\t}\n\n\t// calculate active fiber force\n\tdouble Fa = 0;\n\n\tif ((lat <= 0.4*Lofl) || (lat >= 1.6*Lofl))\n\t{\n\t\t// we have added this part to make sure that \n\t\t// Fa is zero outside the range [0.4, 1.6] *m_Lofl\n\t\tFa = 0;\n\t}\n\telse\n\t{\n\t\tif (lat <= 0.6*Lofl)\n\t\t{\n\t\t\tFa = 9*SQR(lat/Lofl - 0.4);\n\t\t}\n\t\telse if (lat >= 1.4*Lofl)\n\t\t{\n\t\t\tFa = 9*SQR(lat/Lofl - 1.6);\n\t\t}\n\t\telse if ((lat >= 0.6*Lofl) && (lat <= 1.4*Lofl))\n\t\t{\n\t\t\tFa = 1 - 4*SQR(1 - lat/Lofl);\n\t\t}\n\t}\n\n\t// calculate total fiber force\n\tdouble FfDl = smax*(Fp + alpha*Fa)/Lofl;\n\tdouble FfD4  = 0.5*FfDl/lat;\n\n\t// add all derivatives\n\tW1 = F2D1;\n\tW2 = 0;\n\tW4 = F1D4 + F2D4 + F3D4 + FfD4;\n\tW5 = F1D5 + F2D5;\n\n\t// dyadic of a\n\tmat3ds AxA = dyad(a);\n\n\t// symmetric dyad of a and Ba\n\tmat3ds ABA = dyads(a, Ba);\n\n\t// ----- calculate stress -----\n\n\t// calculate T \n\tmat3ds T = B*(W1 + W2*I1) - B2*W2 + AxA*(I4*W4) + ABA*(I4*W5);\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the spatial deviatoric tangent at a material point\n//!\ntens4ds FEMuscleMaterial::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q*m_fiber->unitVector(mp);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble la  = a.unit();\n\tdouble lat = la*pow(J, -1.0/3.0); // i.e. lambda tilde\n\n\t// get deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// calculate B*a\n\tvec3d Ba = B*a;\n\n\t// invariants of B\n\tdouble I1, I2, I4, I5;\n\n\tI1 = B.tr();\n\tI2 = 0.5*(I1*I1 - B2.tr());\n\tI4 = lat*lat;\n\tI5 = I4*(a*Ba);\n\n\t// calculate new invariants\n\tdouble g = I5/(I4*I4) - 1;\n\tdouble b1 = (g > 0 ? sqrt(g) : 0);\n\t\n\tdouble b2 = acosh(0.5*(I1*I4 - I5)/lat);\n\n\t// calculate omage (w)\n\tdouble w = 0.5*(I1*I4 - I5)/lat;\n\n\t// set beta and ksi to their limit values\n\tdouble beta = 1.0;\n\tdouble ksi = -1.0/3.0;\n\n\t// if w not equals unity, we can calculate beta and ksi\n\tif (w > 1.0001)\n\t{\n\t\tbeta = b2/sqrt(w*w-1);\n\t\tksi = (1.0/(w*w-1))*(1 - w*b2/sqrt(w*w-1));\n\t}\n\n\t// evaluate material parameters\n\tdouble G1 = m_G1(mp);\n\tdouble G2 = m_G2(mp);\n\tdouble G3 = m_G3(mp);\n\tdouble P1 = m_P1(mp);\n\tdouble P2 = m_P2(mp);\n\tdouble Lofl = m_Lofl(mp);\n\tdouble lam1 = m_lam1(mp);\n\tdouble alpha = m_alpha(mp);\n\tdouble smax = m_smax(mp);\n\n\t// --- strain energy derivatives ---\n\t// We assume that W(I1, I4, I5, alpha) = F1(B1(I4, I5)) + F2(B2(I1,I4,I5)) + F3(lam(I4), alpha)\n\tdouble W1, W2, W4, W5;\n\n\t// -- A. matrix contribution --\n\t// calculate derivatives for F1\n\tdouble F1D4 = -2*G1*(I5/(I4*I4*I4));\n\tdouble F1D5 = G1/(I4*I4);\n\n\tdouble F1D44 = 6*G1*(I5/(I4*I4*I4*I4));\n\tdouble F1D45 = -2*G1/(I4*I4*I4);\n\n\t// calculate derivatives for F2\n\tdouble F2D1 =  G2*beta*lat;\n\tdouble F2D4 =  G2*beta*(I1*I4 + I5)*0.5*pow(I4, -1.5);\n\tdouble F2D5 = -G2*beta/lat;\n\n\tdouble F2D11 = ksi*G2*I4*0.5;\n\tdouble F2D44 = 2.0*G2*ksi*pow(0.25*(I1*I4+I5)/pow(I4, 1.5), 2) - G2*beta*(0.25*(I1*I4 + 3*I5) / pow(I4, 2.5));\n\tdouble F2D55 = 0.5*G2*ksi/I4;\n\tdouble F2D14 = G2*beta*0.5/lat + G2*ksi*(I1*I4+I5)*0.25/I4;\n\tdouble F2D15 = -0.5*G2*ksi;\n\tdouble F2D45 = G2*beta*0.5*pow(I4, -1.5) - G2*ksi*0.25*(I1*I4+I5)/(I4*I4);\n\n\t// calculate derivatives for F3\n\t// these terms are proposed to fix the zero-stress problem\n\tdouble F3D4  = 9.0*G3*0.125*log(I4)/I4;\n\tdouble F3D44 = 9.0*G3*0.125*(1 - log(I4))/(I4*I4);\n\n\t// -- B. fiber contribution --\n\n\t// calculate passive fiber force\n\tdouble Fp, FpDl;\n\tif (lat <= Lofl)\n\t{\n\t\tFp = 0;\n\t\tFpDl = 0;\n\t}\n\telse if (lat < lam1)\n\t{\n\t\tFp = P1*(exp(P2*(lat/Lofl - 1)) - 1);\n\t\tFpDl = P1*P2*exp(P2*(lat/Lofl-1))/Lofl;\n\t}\n\telse\n\t{\n\t\tdouble P3, P4;\n\n\t\tP3 = P1*P2*exp(P2*(lam1/Lofl - 1));\n\t\tP4 = P1*(exp(P2*(lam1/Lofl - 1)) - 1) - P3*lam1/Lofl;\n\n\t\tFp = P3*lat/Lofl + P4;\n\t\tFpDl = P3/Lofl;\n\t}\n\n\t// calculate active fiber force\n\tdouble Fa = 0, FaDl = 0;\n\n\tif ((lat <= 0.4*Lofl) || (lat >= 1.6*Lofl))\n\t{\n\t\t// we have added this part to make sure that \n\t\t// Fa is zero outside the range [0.4, 1.6] *m_Lofl\n\t\tFa = 0;\n\t\tFaDl = 0;\n\t}\n\telse\n\t{\n\t\tif (lat <= 0.6*Lofl)\n\t\t{\n\t\t\tFa = 9*SQR(lat/Lofl - 0.4);\n\t\t\tFaDl = 18*(lat/Lofl - 0.4)/Lofl;\n\t\t}\n\t\telse if (lat >= 1.4*Lofl)\n\t\t{\n\t\t\tFa = 9*SQR(lat/Lofl - 1.6);\n\t\t\tFaDl = 18*(lat/Lofl - 1.6)/Lofl;\n\t\t}\n\t\telse if ((lat >= 0.6*Lofl) && (lat <= 1.4*Lofl))\n\t\t{\n\t\t\tFa = 1 - 4*SQR(1 - lat/Lofl);\n\t\t\tFaDl = 8*(1 - lat/Lofl)/Lofl;\n\t\t}\n\t}\n\n\t// calculate total fiber force\n\tdouble FfDl = smax*(Fp + alpha*Fa)/Lofl;\n\tdouble FfD4  = 0.5*FfDl/lat;\n\n\tdouble FfDll = smax*(FpDl + alpha*FaDl)/Lofl;\n\tdouble FfD44 = 0.25*(FfDll - FfDl / lat)/I4;\n\n\t// add all derivatives\n\tW1 = F2D1;\n\tW2 = 0;\n\tW4 = F1D4 + F2D4 + F3D4 + FfD4;\n\tW5 = F1D5 + F2D5;\n\n\t// calculate second derivatives\n\tdouble W11, W12, W22, W14, W24, W15, W25, W44, W45, W55;\n\n\tW11 = F2D11;\n\tW12 = 0;\n\tW22 = 0;\n\tW14 = F2D14;\n\tW24 = 0;\n\tW15 = F2D15;\n\tW25 = 0;\n\tW44 = F1D44 + F2D44 + F3D44 + FfD44;\n\tW45 = F1D45 + F2D45;\n\tW55 = F2D55;\n\n\t// calculate dWdC:C\n\tdouble WCC = W1*I1 + 2*W2*I2 + W4*I4 + 2*W5*I5;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CW2CCC = (W11*I1 + W12*I1*I1 + W2*I1 + 2*W12*I2 + 2*W22*I1*I2 + W14*I4 + W24*I1*I4 + 2*W15*I5 + 2*W25*I1*I5)*I1\n\t\t           -(W12*I1 + 2*W22*I2 + W2 + W24*I4 + 2*W25*I5)*(I1*I1 - 2*I2)\n\t\t\t\t   +(W14*I1 + 2*W24*I2 + W44*I4 + 2*W45*I5)*I4 + (W15*I1 + 2*W25*I2 + W45*I4 + 2*W55*I5)*2*I5\n\t\t\t\t   + 2*W5*I5;\n\n\t// second order identity tensor\n\tmat3dd ID(1);\n\n\t// 4th order identity tensors\n\ttens4ds IxI = dyad1s(ID);\n\ttens4ds I   = dyad4s(ID);\n\n\t// dyad of a\n\tmat3ds AxA = dyad(a);\n\n\t// --- calculate elasticity tensor ---\n\n\t// calculate push-forward of dI5/dC = I4*(a*Ba + Ba*a)\n\tmat3ds I5C = dyads(a, Ba)*I4;\n\n\t// calculate push-forward of d2I5/dCdC\n\ttens4ds I5CC = dyad4s(AxA, B)*I4;\n\n\t// calculate push forward of I\n\ttens4ds Ib = dyad4s(B);\n\n\t// calculate push forward of dW/dCdC:C\n\tmat3ds WCCC = \n\t\tB*(W11*I1 + W12*I1*I1 + W2*I1 + 2*W12*I2 + 2*W22*I1*I2 + W14*I4 + W24*I1*I4 + 2*W15*I5 + 2*W25*I1*I5) - \n\t\tB2*(W12*I1 + 2*W22*I2 + W2 + W24*I4 + 2*W25*I5) + \n\t\tAxA *((W14*I1 + 2*W24*I2 + W44*I4 + 2*W45*I5)*I4) + \n\t\tI5C*(W15*I1 + 2*W25*I2 + W45*I4 + 2*W55*I5 + W5);\n\n\t// calculate push-forward of dW2/dCdC\n\ttens4ds W2CC = \n\t\tdyad1s(B)*(W11 + 2.0*W12*I1 + W2 + W22*I1*I1) + \n\t\tdyad1s(B, B2)*(-(W12+W22*I1)) + dyad1s(B2)*W22 - Ib*W2 +\n\t\tdyad1s(B, AxA)*((W14 + W24*I1)*I4) +\n\t\tdyad1s(B, I5C)*(W15 + W25*I1) + \n\t\tdyad1s(B2, AxA)*(-W24*I4) + \n\t\tdyad1s(AxA)*(W44*I4*I4) + \n\t\tdyad1s(AxA, I5C)*(W45*I4) + \n\t\tdyad1s(I5C)*W55 + I5CC*W5;\n\n\t// let's put it all together\n\t// cw\n\ttens4ds cw =  IxI*((4.0/(9.0*J))*(CW2CCC)) + W2CC*(4/J) - dyad1s(WCCC, ID)*(4.0/(3.0*J));\n\n\t// deviatoric Cauchy-stress\n\tmat3ds ABA = dyads(a, Ba);\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2 + AxA * (I4 * W4) + ABA * (I4 * W5);\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// elasticity tensor\n\ttens4ds c = dyad1s(devs, ID)*(-2.0/3.0) + (I - IxI/3.0)*(4.0*WCC/(3.0*J)) + cw;\n\n\treturn tens4ds(c);\n}\n"
  },
  {
    "path": "FEBioMech/FEMuscleMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Muscle Material\n\n//! This material uses the constitutive model developed by Blemker et.al. to model\n//! muscles which undergo active contraction\n\nclass FEMuscleMaterial: public FEUncoupledMaterial\n{\npublic:\n\tFEMuscleMaterial(FEModel* pfem);\n\npublic:\n\t// transverse constants\n\tFEParamDouble m_G1; //!< along-fiber shear modulus\n\tFEParamDouble m_G2; //!< cross-fiber shear modulus\n\tFEParamDouble m_G3; //!< new term\n\n\t// along fiber constants\n\tFEParamDouble\tm_P1; //!< muscle fiber constant P1\n\tFEParamDouble\tm_P2; //!< muscle fiber constant P2\n\t\t\n\tFEParamDouble\tm_Lofl;  //!< optimal sarcomere length\n\tFEParamDouble\tm_smax;  //!< maximum isometric stretch\n\tFEParamDouble\tm_lam1;\n\tFEParamDouble\tm_alpha;\t//!< activation parameter\n\n\tFEVec3dValuator*\tm_fiber;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t// declare the material parameters\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENaturalNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENaturalNeoHookean.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FENaturalNeoHookean, FEElasticMaterial)\n    ADD_PARAMETER(m_E, FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\")->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n    ADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nmat3ds FENaturalNeoHookean::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double J = pt.m_J;\n    \n    // get the material parameters\n    double E = m_E(mp);\n    double v = m_v(mp);\n    double k = E/3/(1-2*v);\n    double mu = E/2/(1+v);\n    \n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate amount of dilatation\n    double K1 = h.tr();\n    \n    // evaluate amount of distortion (always positive)\n    mat3ds hdev = h.dev();\n    double K2 = hdev.norm();\n    \n    // Identity\n    mat3dd I(1);\n    \n    // Phi\n    mat3ds Phi;\n    if (K2 != 0) Phi = hdev/K2;\n    else Phi.zero();\n    \n    // calculate stress\n    mat3ds s = (I*(k*K1) + Phi*(2*mu*K2))/J;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FENaturalNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the material parameters\n    double E = m_E(mp);\n    double v = m_v(mp);\n    double k = E/3/(1-2*v);\n    double mu = E/2/(1+v);\n\n    // jacobian\n    double J = pt.m_J;\n    \n    // get the left Cauchy-Green tensor\n    mat3ds b = pt.LeftCauchyGreen();\n    \n    // get the eigenvalues and eigenvectors of b\n    double lam2[3];    // these are the squares of the eigenvalues of V\n    vec3d n[3];\n    EigenValues(b, lam2, n, 1e-12);\n    \n    // get the eigenvalues of V\n    double lam[3], lnl[3];\n    mat3ds a[3];\n    for (int i=0; i<3; ++i) {\n        lam[i] = sqrt(lam2[i]);\n        lnl[i] = log(lam[i]);\n        a[i] = dyad(n[i]);\n    }\n    \n    // evaluate relevant coefficients\n    double s[3], ss[3], ddW, ds[3];\n    double c1 = 3*k + 4*mu;\n    double c2 = 3*k - 2*mu;\n    double c3 = 3*k + mu;\n    s[0] = (c1*lnl[0] + c2*(lnl[1] + lnl[2]))/(3*J);\n    s[1] = (c1*lnl[1] + c2*(lnl[2] + lnl[0]))/(3*J);\n    s[2] = (c1*lnl[2] + c2*(lnl[0] + lnl[1]))/(3*J);\n    ss[0] = (c1*(1 - 2*lnl[0]) - 2*c2*(lnl[1] + lnl[2]))/(3*J);\n    ss[1] = (c1*(1 - 2*lnl[1]) - 2*c2*(lnl[2] + lnl[0]))/(3*J);\n    ss[2] = (c1*(1 - 2*lnl[2]) - 2*c2*(lnl[0] + lnl[1]))/(3*J);\n    ddW = c2/(3*J);\n    if (lam2[1] != lam2[2])\n        ds[0] = 2*(lam2[2]*s[1] - lam2[1]*s[2])/(lam2[1] - lam2[2]);\n    else\n        ds[0] = (6*mu - 4*c3*lnl[1] - 2*c2*lnl[0])/(3*J);\n    if (lam2[2] != lam2[0])\n        ds[1] = 2*(lam2[0]*s[2] - lam2[2]*s[0])/(lam2[2] - lam2[0]);\n    else\n        ds[1] = (6*mu - 4*c3*lnl[2] - 2*c2*lnl[1])/(3*J);\n    if (lam2[0] != lam2[1])\n        ds[2] = 2*(lam2[1]*s[0] - lam2[0]*s[1])/(lam2[0] - lam2[1]);\n    else\n        ds[2] = (6*mu - 4*c3*lnl[0] - 2*c2*lnl[2])/(3*J);\n    \n    tens4ds c = dyad1s(a[0])*ss[0] + dyad1s(a[1])*ss[1] + dyad1s(a[2])*ss[2]\n    + (dyad1s(a[1], a[2]) + dyad1s(a[2], a[0]) + dyad1s(a[0], a[1]))*ddW\n    + dyad4s(a[1], a[2])*ds[0] + dyad4s(a[2], a[0])*ds[1] + dyad4s(a[0], a[1])*ds[2];\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENaturalNeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the material parameters\n    double E = m_E(mp);\n    double v = m_v(mp);\n    double k = E/3/(1-2*v);\n    double mu = E/2/(1+v);\n\n    // evaluate spatial Hencky (logarithmic) strain\n    mat3ds h = pt.LeftHencky();\n    \n    // evaluate amount of dilatation\n    double K1 = h.tr();\n    \n    // evaluate amount of distortion (always positive)\n    mat3ds hdev = h.dev();\n    double K2 = hdev.norm();\n\n    double sed = K1*K1*k/2 + K2*K2*mu;\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENaturalNeoHookean::EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps)\n{\n    A.eigen(l, r);\n    \n    // correct for numerical inaccuracy\n    double d01 = fabs(l[0] - l[1]);\n    double d12 = fabs(l[1] - l[2]);\n    double d02 = fabs(l[0] - l[2]);\n    \n    if (d01 < eps) l[1] = l[0]; //= 0.5*(l[0]+l[1]);\n    if (d02 < eps) l[2] = l[0]; //= 0.5*(l[0]+l[2]);\n    if (d12 < eps) l[2] = l[1]; //= 0.5*(l[1]+l[2]);\n    \n}\n\n"
  },
  {
    "path": "FEBioMech/FENaturalNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Natural neo Hookean\n\n//! Implementation of a neo-Hookean hyperelastic material\n//! using natural strain (Criscione et al. JMPS 2000)\nclass FEBIOMECH_API FENaturalNeoHookean : public FEElasticMaterial\n{\npublic:\n    FENaturalNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem) {}\n    \npublic:\n    FEParamDouble        m_E;    //!< bulk modulus\n    FEParamDouble        m_v;    //!< shear modulus\n    \npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \nprotected:\n    void EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps = 0);\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENeoHookean.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FENeoHookean, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENeoHookean::FENeoHookean(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\n//-----------------------------------------------------------------------------\nmat3ds FENeoHookean::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\tdouble lndetF = log(detF);\n\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\n\t// Identity\n\tmat3dd I(1);\n\n\t// calculate stress\n\tmat3ds s = (b - I)*(mu*detFi) + I*(lam*lndetF*detFi);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FENeoHookean::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tdouble detF = pt.m_J;\n\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\n\tdouble lam1 = lam / detF;\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\n\t\n    mat3dd I(1);\n\n\treturn dyad1s(I)*lam1 + dyad4s(I)*(2*mu1);\n}\n\n//-----------------------------------------------------------------------------\ndouble FENeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tdouble J = pt.m_J;\n\tdouble lnJ = log(J);\n\t\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble I1 = b.tr();\n\t\n\t// lame parameters\n\tdouble lam = v*E/((1+v)*(1-2*v));\n\tdouble mu  = 0.5*E/(1+v);\n\t\n\tdouble sed = mu*((I1-3)/2.0 - lnJ)+lam*lnJ*lnJ/2.0;\n\t\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FENeoHookean::PK2Stress(FEMaterialPoint& pt, const mat3ds ES)\n{\n    // Identity\n    mat3dd I(1);\n    \n    // calculate right Cauchy-Green tensor\n    mat3ds C = I + ES*2;\n    mat3ds Ci = C.inverse();\n    \n    double detF = sqrt(C.det());\n    double lndetF = log(detF);\n    \n\t// get the material parameters\n\tdouble E = m_E(pt);\n\tdouble v = m_v(pt);\n    \n    // lame parameters\n    double lam = v*E/((1+v)*(1-2*v));\n    double mu  = 0.5*E/(1+v);\n    \n    // calculate stress\n    mat3ds S = (I - Ci)*mu + Ci*(lam*lndetF);\n    \n    return S;\n}\n\n//-----------------------------------------------------------------------------\ntens4dmm FENeoHookean::MaterialTangent(FEMaterialPoint& pt, const mat3ds ES)\n{\n    // calculate right Cauchy-Green tensor\n    mat3ds C = mat3dd(1) + ES*2;\n    mat3ds Ci = C.inverse();\n    double J = sqrt(C.det());\n\n\t// get the material parameters\n\tdouble E = m_E(pt);\n\tdouble v = m_v(pt);\n\n    // lame parameters\n    double lam = v*E/((1+v)*(1-2*v));\n    double mu  = 0.5*E/(1+v);\n    \n    tens4dmm c = dyad1s(Ci)*lam + dyad4s(Ci)*(2*(mu-lam*log(J)));\n    \n    return c;\n}\n"
  },
  {
    "path": "FEBioMech/FENeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Neo Hookean material\n\n//! Implementation of a neo-Hookean hyperelastic material.\nclass FEBIOMECH_API FENeoHookean : public FEElasticMaterial\n{\npublic:\n\tFENeoHookean(FEModel* pfem);\n\npublic:\n\tFEParamDouble\t\tm_E;\t//!< Young's modulus\n\tFEParamDouble\t\tm_v;\t//!< Poisson's ratio\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! calculate the 2nd Piola-Kirchhoff stress at material point\n    mat3ds PK2Stress(FEMaterialPoint& pt, const mat3ds E) override;\n    \n    //! calculate material tangent stiffness at material point\n    tens4dmm MaterialTangent(FEMaterialPoint& pt, const mat3ds E) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENeoHookeanAD.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FENeoHookeanAD.h\"\n#include <FECore/ad.h>\n#include \"adcm.h\"\n\ninline double lambdaFromEV(double E, double v)\n{\n\treturn v* E / ((1.0 + v) * (1.0 - 2.0 * v));\n}\n\ninline double muFromEV(double E, double v)\n{\n\treturn 0.5 * E / (1.0 + v);\n}\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FENeoHookeanAD, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENeoHookeanAD::FENeoHookeanAD(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\nad::number FENeoHookeanAD::StrainEnergy_AD(FEMaterialPoint& mp, ad::mat3ds& C)\n{\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\tdouble lam = lambdaFromEV(E, v);\n\tdouble mu = muFromEV(E, v);\n\n\tad::number I1 = C.tr();\n\tad::number J = ad::sqrt(C.det());\n\tad::number lnJ = ad::log(J);\n\n\tad::number sed = mu * ((I1 - 3) / 2.0 - lnJ) + lam * lnJ * lnJ / 2.0;\n\treturn sed;\n}\n\nad2::number FENeoHookeanAD::StrainEnergy_AD2(FEMaterialPoint& mp, ad2::mat3ds& C)\n{\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\tdouble lam = lambdaFromEV(E, v);\n\tdouble mu = muFromEV(E, v);\n\n\tad2::number I1 = C.tr();\n\tad2::number J = ad2::sqrt(C.det());\n\tad2::number lnJ = ad2::log(J);\n\n\tad2::number sed = mu * ((I1 - 3) / 2.0 - lnJ) + lam * lnJ * lnJ / 2.0;\n\treturn sed;\n}\n\nad::mat3ds FENeoHookeanAD::PK2Stress_AD(FEMaterialPoint& mp, ad::mat3ds& C)\n{\n\t// get the material parameters\n\tdouble E = m_E(mp);\n\tdouble v = m_v(mp);\n\tdouble lam = lambdaFromEV(E, v);\n\tdouble mu = muFromEV(E, v);\n\n\tad::mat3ds I(1.0);\n\tad::mat3ds Ci = C.inverse();\n\tad::number J = ad::sqrt(C.det());\n\tad::number lnJ = ad::log(J);\n\n\tad::mat3ds S = (I - Ci) * mu + Ci * (lam * lnJ);\n\n\treturn S;\n}\n\nmat3ds FENeoHookeanAD::Stress(FEMaterialPoint& mp)\n{\n\t// calculate PK2 stress\n//\tmat3ds S = ad::PK2Stress<FENeoHookeanAD>(this, mp);\n\tmat3ds S = ad2::PK2Stress<FENeoHookeanAD>(this, mp);\n\n\t// push-forward to obtain Cauchy-stress\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3ds s = pt.push_forward(S);\n\n\treturn s;\n}\n\ntens4ds FENeoHookeanAD::Tangent(FEMaterialPoint& mp)\n{\n\t// calculate material tangent\n//\ttens4ds C4 = ad::Tangent<FENeoHookeanAD>(this, mp);\n\ttens4ds C4 = ad2::Tangent<FENeoHookeanAD>(this, mp);\n\n\t// push forward to get spatial tangent\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\ttens4ds c4 = pt.push_forward(C4);\n\n\treturn c4;\n}\n\ndouble FENeoHookeanAD::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\treturn ad::StrainEnergy<FENeoHookeanAD>(this, mp);\n}\n\nmat3ds FENeoHookeanAD::PK2Stress(FEMaterialPoint& mp, const mat3ds ES)\n{\n\tmat3ds C = mat3dd(1) + ES * 2;\n\tmat3ds S = ad::PK2Stress<FENeoHookeanAD>(this, mp, C);\n\treturn S;\n}\n\ntens4dmm FENeoHookeanAD::MaterialTangent(FEMaterialPoint& mp, const mat3ds ES)\n{\n\tmat3ds C = mat3dd(1) + ES * 2;\n\ttens4ds C4 = ad::Tangent<FENeoHookeanAD>(this, mp, C);\n\treturn tens4dmm(C4);\n}\n"
  },
  {
    "path": "FEBioMech/FENeoHookeanAD.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n#include \"adcm.h\"\n\n//! Implementation of a neo-Hookean hyperelastic material using automatic differentation\nclass FEBIOMECH_API FENeoHookeanAD : public FEElasticMaterial\n{\npublic:\n\tFENeoHookeanAD(FEModel* pfem);\n\npublic:\n\tFEParamDouble\t\tm_E;\t//!< Young's modulus\n\tFEParamDouble\t\tm_v;\t//!< Poisson's ratio\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n\n\t//! calculate the 2nd Piola-Kirchhoff stress at material point\n\tmat3ds PK2Stress(FEMaterialPoint& pt, const mat3ds E) override;\n\n\t//! calculate material tangent stiffness at material point\n\ttens4dmm MaterialTangent(FEMaterialPoint& pt, const mat3ds E) override;\n\npublic:\n\tad::number StrainEnergy_AD(FEMaterialPoint& mp, ad::mat3ds& C);\n\tad::mat3ds PK2Stress_AD(FEMaterialPoint& mp, ad::mat3ds& C);\n\tad2::number StrainEnergy_AD2(FEMaterialPoint& mp, ad2::mat3ds& C);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENeoHookeanTransIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENeoHookeanTransIso.h\"\n\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FENeoHookeanTransIso, FEElasticMaterial)\n\tADD_PARAMETER(m_Ep, \"Ep\");\n\tADD_PARAMETER(m_Ez, \"Ez\");\n\tADD_PARAMETER(m_vz, \"vz\");\n\tADD_PARAMETER(m_vp, \"vp\");\n\tADD_PARAMETER(m_gz, \"gz\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// CompNeoHookean_Transiso\n//////////////////////////////////////////////////////////////////////\n\nmat3ds FENeoHookeanTransIso::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\n\t//define jacobian\n\tdouble J = detF;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam = a.unit();\n\n\t// calculate left Cauchy-Green tensor\n\t// (we commented out the matrix components we do not need)\n\tdouble B[3][3];\n\tdouble B2[3][3];\n\n\tB[0][0] = F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2];\n\tB[0][1] = F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2];\n\tB[0][2] = F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2];\n\n \tB[1][0] = F[1][0]*F[0][0]+F[1][1]*F[0][1]+F[1][2]*F[0][2];\n\tB[1][1] = F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2];\n\tB[1][2] = F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2];\n\n  \tB[2][0] = F[2][0]*F[0][0]+F[2][1]*F[0][1]+F[2][2]*F[0][2];\n\tB[2][1] = F[2][0]*F[1][0]+F[2][1]*F[1][1]+F[2][2]*F[1][2];\n\tB[2][2] = F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2];\n\n\t// calculate square of B\n\t// (we commented out the matrix components we do not need)\n\tB2[0][0] = B[0][0]*B[0][0]+B[0][1]*B[1][0]+B[0][2]*B[2][0];\n\tB2[0][1] = B[0][0]*B[0][1]+B[0][1]*B[1][1]+B[0][2]*B[2][1];\n\tB2[0][2] = B[0][0]*B[0][2]+B[0][1]*B[1][2]+B[0][2]*B[2][2];\n\n\tB2[1][0] = B[1][0]*B[0][0]+B[1][1]*B[1][0]+B[1][2]*B[2][0];\n\tB2[1][1] = B[1][0]*B[0][1]+B[1][1]*B[1][1]+B[1][2]*B[2][1];\n\tB2[1][2] = B[1][0]*B[0][2]+B[1][1]*B[1][2]+B[1][2]*B[2][2];\n\n\tB2[2][0] = B[2][0]*B[0][0]+B[2][1]*B[1][0]+B[2][2]*B[2][0];\n\tB2[2][1] = B[2][0]*B[0][1]+B[2][1]*B[1][1]+B[2][2]*B[2][1];\n\tB2[2][2] = B[2][0]*B[0][2]+B[2][1]*B[1][2]+B[2][2]*B[2][2];\n\n\t// calculate Ba\n\tvec3d Ba;\n\tBa.x = B[0][0]*a.x + B[0][1]*a.y + B[0][2]*a.z;\n\tBa.y = B[1][0]*a.x + B[1][1]*a.y + B[1][2]*a.z;\n\tBa.z = B[2][0]*a.x + B[2][1]*a.y + B[2][2]*a.z;\n\n\t//compute invariants\n\tdouble I1,I3,I4,I5;\n\n\tI1 = B[0][0]+B[1][1]+B[2][2];\n\tI3=J*J;\n\tI4 = lam*lam;;\n\tI5 = I4*(a*Ba);\n\n\t// compute lame parameters\n\tdouble m_gp=m_Ep/(2*(1+m_vp));\n\tdouble m_lamp=-((m_Ep*(m_Ez*m_vp + m_Ep*m_vz*m_vz))/((1 + m_vp)*(m_Ez*(-1 + m_vp) + 2*m_Ep*m_vz*m_vz)));\n\tdouble m_lamz=(m_Ez*m_Ez*(-1 + m_vp*m_vp) - m_Ep*(m_Ep + 4*(m_gp + m_gz)*(1 + m_vp))*m_vz*m_vz + m_Ez*(2*m_gz - m_Ep*m_vp - 2*m_gz*m_vp*m_vp - 2*m_gp*(-1 + m_vp*m_vp) + 2*m_Ep*m_vz + 2*m_Ep*m_vp*m_vz))/((1 + m_vp)*(m_Ez*(-1 + m_vp) + 2*m_Ep*m_vz*m_vz));\n\tdouble m_alpha=(m_Ep*(m_Ep*m_vz*m_vz - m_Ez*(m_vp*(-1 + m_vz) + m_vz)))/((1 + m_vp)*(m_Ez*(-1 + m_vp) + 2*m_Ep*m_vz*m_vz));\n\n\n\t// calculate stress\n\tmat3ds s;\n\n\t//trans iso\n\ts.xx()=(2*(B[0][0]*(m_gp/2. + (m_alpha*(-1 + I4))/4.) + a.x*(a.x*B[0][0] + a.y*B[0][1] + a.z*B[0][2])*(-m_gp + m_gz)*I4 + I3*(-m_gp/(2.*I3) + ((-1 + J)*m_lamp)/(2.*J)) + a.x*a.x*I4*(m_gz/2. + (m_alpha*(-3 + I1))/4. - m_gz/(2.*I4) - (-m_gp + m_gz)*I4 + ((-1 + lam)*m_lamz)/(2.*lam))))/J;\n\ts.xy()=(2*(B[0][1]*(m_gp/2. + (m_alpha*(-1 + I4))/4.) + ((a.y*(a.x*B[0][0] + a.y*B[0][1] + a.z*B[0][2]) + a.x*(a.x*B[0][1] + a.y*B[1][1] + a.z*B[1][2]))*(-m_gp + m_gz)*I4)/2. + a.x*a.y*I4*(m_gz/2. + (m_alpha*(-3 + I1))/4. - m_gz/(2.*I4) - (-m_gp + m_gz)*I4 + ((-1 + lam)*m_lamz)/(2.*lam))))/J;\n\ts.xz()=(2*(B[0][2]*(m_gp/2. + (m_alpha*(-1 + I4))/4.) + ((a.z*(a.x*B[0][0] + a.y*B[0][1] + a.z*B[0][2]) + a.x*(a.x*B[0][2] + a.y*B[1][2] + a.z*B[2][2]))*(-m_gp + m_gz)*I4)/2. + a.x*a.z*I4*(m_gz/2. + (m_alpha*(-3 + I1))/4. - m_gz/(2.*I4) - (-m_gp + m_gz)*I4 + ((-1 + lam)*m_lamz)/(2.*lam))))/J;\n\ts.yy()=(2*(B[1][1]*(m_gp/2. + (m_alpha*(-1 + I4))/4.) + a.y*(a.x*B[0][1] + a.y*B[1][1] + a.z*B[1][2])*(-m_gp + m_gz)*I4 + I3*(-m_gp/(2.*I3) + ((-1 + J)*m_lamp)/(2.*J)) + a.y*a.y*I4*(m_gz/2. + (m_alpha*(-3 + I1))/4. - m_gz/(2.*I4) - (-m_gp + m_gz)*I4 + ((-1 + lam)*m_lamz)/(2.*lam))))/J;\n\ts.yz()=(2*(B[1][2]*(m_gp/2. + (m_alpha*(-1 + I4))/4.) + ((a.z*(a.x*B[0][1] + a.y*B[1][1] + a.z*B[1][2]) + a.y*(a.x*B[0][2] + a.y*B[1][2] + a.z*B[2][2]))*(-m_gp + m_gz)*I4)/2. + a.y*a.z*I4*(m_gz/2. + (m_alpha*(-3 + I1))/4. - m_gz/(2.*I4) - (-m_gp + m_gz)*I4 + ((-1 + lam)*m_lamz)/(2.*lam))))/J;\n\ts.zz()=(2*(B[2][2]*(m_gp/2. + (m_alpha*(-1 + I4))/4.) + a.z*(a.x*B[0][2] + a.y*B[1][2] + a.z*B[2][2])*(-m_gp + m_gz)*I4 + I3*(-m_gp/(2.*I3) + ((-1 + J)*m_lamp)/(2.*J)) + a.z*a.z*I4*(m_gz/2. + (m_alpha*(-3 + I1))/4. - m_gz/(2.*I4) - (-m_gp + m_gz)*I4 + ((-1 + lam)*m_lamz)/(2.*lam))))/J;\n\n\n\t//compressible neohookean (for testing--comment out)\n\t//s.x=(2*((B[0][0]*m_gp)/2. + I3*(-m_gp/(2.*I3) + (m_lamp*log(J))/(2.*I3))))/J;\n\t//s.xy=(B[0][1]*m_gp)/J;\n\t//s.xz=(B[0][2]*m_gp)/J;\n\t//s.y=(2*((B[1][1]*m_gp)/2. + I3*(-m_gp/(2.*I3) + (m_lamp*log(J))/(2.*I3))))/J;\n\t//s.yz=(B[1][2]*m_gp)/J;\n\t//s.z=(2*((B[2][2]*m_gp)/2. + I3*(-m_gp/(2.*I3) + (m_lamp*log(J))/(2.*I3))))/J;\n\n\n\treturn s;\n\n}\n\ntens4ds FENeoHookeanTransIso::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3d &F = pt.m_F;\n\tdouble detF = pt.m_J;\n\n\t//define jacobian\n\tdouble J = detF;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam = a.unit();\n\n\t// calculate left Cauchy-Green tensor\n\t// (we commented out the matrix components we do not need)\n\tdouble B[3][3];\n\tdouble B2[3][3];\n\n\tB[0][0] = F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2];\n\tB[0][1] = F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2];\n\tB[0][2] = F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2];\n\n \tB[1][0] = F[1][0]*F[0][0]+F[1][1]*F[0][1]+F[1][2]*F[0][2];\n\tB[1][1] = F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2];\n\tB[1][2] = F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2];\n\n  \tB[2][0] = F[2][0]*F[0][0]+F[2][1]*F[0][1]+F[2][2]*F[0][2];\n\tB[2][1] = F[2][0]*F[1][0]+F[2][1]*F[1][1]+F[2][2]*F[1][2];\n\tB[2][2] = F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2];\n\n\t// calculate square of B\n\t// (we commented out the matrix components we do not need)\n\tB2[0][0] = B[0][0]*B[0][0]+B[0][1]*B[1][0]+B[0][2]*B[2][0];\n\tB2[0][1] = B[0][0]*B[0][1]+B[0][1]*B[1][1]+B[0][2]*B[2][1];\n\tB2[0][2] = B[0][0]*B[0][2]+B[0][1]*B[1][2]+B[0][2]*B[2][2];\n\n\tB2[1][0] = B[1][0]*B[0][0]+B[1][1]*B[1][0]+B[1][2]*B[2][0];\n\tB2[1][1] = B[1][0]*B[0][1]+B[1][1]*B[1][1]+B[1][2]*B[2][1];\n\tB2[1][2] = B[1][0]*B[0][2]+B[1][1]*B[1][2]+B[1][2]*B[2][2];\n\n\tB2[2][0] = B[2][0]*B[0][0]+B[2][1]*B[1][0]+B[2][2]*B[2][0];\n\tB2[2][1] = B[2][0]*B[0][1]+B[2][1]*B[1][1]+B[2][2]*B[2][1];\n\tB2[2][2] = B[2][0]*B[0][2]+B[2][1]*B[1][2]+B[2][2]*B[2][2];\n\n\t// calculate Ba\n\tvec3d Ba;\n\tBa.x = B[0][0]*a.x + B[0][1]*a.y + B[0][2]*a.z;\n\tBa.y = B[1][0]*a.x + B[1][1]*a.y + B[1][2]*a.z;\n\tBa.z = B[2][0]*a.x + B[2][1]*a.y + B[2][2]*a.z;\n\n\t//compute invariants\n\tdouble I1,I3,I4,I5;\n\n\tI1 = B[0][0]+B[1][1]+B[2][2];\n\tI3=J*J;\n\tI4 = lam*lam;;\n\tI5 = I4*(a*Ba);\n\n\t// lame parameters\n\tdouble m_gp=m_Ep/(2*(1+m_vp));\n\tdouble m_lamp=-((m_Ep*(m_Ez*m_vp + m_Ep*m_vz*m_vz))/((1 + m_vp)*(m_Ez*(-1 + m_vp) + 2*m_Ep*m_vz*m_vz)));\n\tdouble m_lamz=(m_Ez*m_Ez*(-1 + m_vp*m_vp) - m_Ep*(m_Ep + 4*(m_gp + m_gz)*(1 + m_vp))*m_vz*m_vz + m_Ez*(2*m_gz - m_Ep*m_vp - 2*m_gz*m_vp*m_vp - 2*m_gp*(-1 + m_vp*m_vp) + 2*m_Ep*m_vz + 2*m_Ep*m_vp*m_vz))/((1 + m_vp)*(m_Ez*(-1 + m_vp) + 2*m_Ep*m_vz*m_vz));\n\tdouble m_alpha=(m_Ep*(m_Ep*m_vz*m_vz - m_Ez*(m_vp*(-1 + m_vz) + m_vz)))/((1 + m_vp)*(m_Ez*(-1 + m_vp) + 2*m_Ep*m_vz*m_vz));\n\n\n\t//trans iso\n\tdouble D[6][6] = {0};\n\tD[0][0]=(2*a.x*a.x*B[0][0]*(m_alpha + 2*m_gz)*I4 + m_gp*(2 - 4*a.x*a.x*B[0][0]*I4 + 4*a.x*a.x*a.x*a.x*I4*I4) + J*m_lamp + a.x*a.x*a.x*a.x*(2*m_gz - 4*m_gz*I4*I4 + lam*m_lamz))/J;\n\tD[0][1]=(a.y*a.y*m_alpha*B[0][0]*I4 + 4*a.x*a.y*B[0][1]*(-m_gp + m_gz)*I4 - J*m_lamp + 2*I3*m_lamp + a.x*a.x*(m_alpha*B[1][1]*I4 + a.y*a.y*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[0][2]=(a.z*a.z*m_alpha*B[0][0]*I4 + 4*a.x*a.z*B[0][2]*(-m_gp + m_gz)*I4 - J*m_lamp + 2*I3*m_lamp + a.x*a.x*(m_alpha*B[2][2]*I4 + a.z*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[0][3]=(a.x*(a.y*B[0][0]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.x*B[0][1]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.x*a.x*a.y*(4*m_gp*I4*I4 + m_gz*(2 - 4*I4*I4) + lam*m_lamz)))/J;\n\tD[0][4]=(a.x*(a.z*B[0][0]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.x*B[0][2]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.x*a.x*a.z*(4*m_gp*I4*I4 + m_gz*(2 - 4*I4*I4) + lam*m_lamz)))/J;\n\tD[0][5]=(a.y*a.z*m_alpha*B[0][0]*I4 - 2*a.x*(a.z*B[0][1] + a.y*B[0][2])*(m_gp - m_gz)*I4 + a.x*a.x*(m_alpha*B[1][2]*I4 + a.y*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[1][1]=(2*a.y*a.y*B[1][1]*(m_alpha + 2*m_gz)*I4 + m_gp*(2 - 4*a.y*a.y*B[1][1]*I4 + 4*a.y*a.y*a.y*a.y*I4*I4) + J*m_lamp + a.y*a.y*a.y*a.y*(2*m_gz - 4*m_gz*I4*I4 + lam*m_lamz))/J;\n\tD[1][2]=(a.z*a.z*m_alpha*B[1][1]*I4 + 4*a.y*a.z*B[1][2]*(-m_gp + m_gz)*I4 - J*m_lamp + 2*I3*m_lamp + a.y*a.y*(m_alpha*B[2][2]*I4 + a.z*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[1][3]=(a.y*(a.y*B[0][1]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.x*(B[1][1]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.y*a.y*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz))))/J;\n\tD[1][4]=(a.y*(a.y*m_alpha*B[0][2] + 2*a.z*B[0][1]*(-m_gp + m_gz))*I4 + a.x*(a.z*m_alpha*B[1][1]*I4 + 2*a.y*B[1][2]*(-m_gp + m_gz)*I4 + a.y*a.y*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[1][5]=(a.y*(a.z*B[1][1]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.y*B[1][2]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.y*a.y*a.z*(4*m_gp*I4*I4 + m_gz*(2 - 4*I4*I4) + lam*m_lamz)))/J;\n\tD[2][2]=(2*a.z*a.z*B[2][2]*(m_alpha + 2*m_gz)*I4 + m_gp*(2 - 4*a.z*a.z*B[2][2]*I4 + 4*a.z*a.z*a.z*a.z*I4*I4) + J*m_lamp + a.z*a.z*a.z*a.z*(2*m_gz - 4*m_gz*I4*I4 + lam*m_lamz))/J;\n\tD[2][3]=(a.z*(a.z*m_alpha*B[0][1] + 2*a.y*B[0][2]*(-m_gp + m_gz))*I4 + a.x*(2*a.z*B[1][2]*(-m_gp + m_gz)*I4 + a.y*(m_alpha*B[2][2]*I4 + a.z*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz))))/J;\n\tD[2][4]=(a.z*(a.z*B[0][2]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.x*(B[2][2]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.z*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz))))/J;\n\tD[2][5]=(a.z*(a.z*B[1][2]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.y*(B[2][2]*(m_alpha - 2*m_gp + 2*m_gz)*I4 + a.z*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz))))/J;\n\tD[3][3]=(a.y*a.y*B[0][0]*m_gz*I4 + 2*a.x*a.y*B[0][1]*(m_alpha + m_gz)*I4 - m_gp*(-1 + 2*a.x*a.y*B[0][1]*I4 + a.x*a.x*B[1][1]*I4 + a.y*a.y*I4*(B[0][0] - 4*a.x*a.x*I4)) + J*m_lamp - I3*m_lamp + a.x*a.x*(B[1][1]*m_gz*I4 + a.y*a.y*(2*m_gz - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[3][4]=(a.y*a.z*B[0][0]*(-m_gp + m_gz)*I4 + a.x*(a.z*B[0][1] + a.y*B[0][2])*(m_alpha - m_gp + m_gz)*I4 + a.x*a.x*(B[1][2]*(-m_gp + m_gz)*I4 + a.y*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[3][5]=(a.y*(a.y*B[0][2]*(-m_gp + m_gz) + a.z*B[0][1]*(m_alpha - m_gp + m_gz))*I4 + a.x*(a.z*B[1][1]*(-m_gp + m_gz)*I4 + a.y*B[1][2]*(m_alpha - m_gp + m_gz)*I4 + a.y*a.y*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[4][4]=(a.z*a.z*B[0][0]*m_gz*I4 + 2*a.x*a.z*B[0][2]*(m_alpha + m_gz)*I4 - m_gp*(-1 + 2*a.x*a.z*B[0][2]*I4 + a.x*a.x*B[2][2]*I4 + a.z*a.z*I4*(B[0][0] - 4*a.x*a.x*I4)) + J*m_lamp - I3*m_lamp + a.x*a.x*(B[2][2]*m_gz*I4 + a.z*a.z*(2*m_gz - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\tD[4][5]=(a.z*(a.z*B[0][1]*(-m_gp + m_gz) + a.y*B[0][2]*(m_alpha - m_gp + m_gz))*I4 + a.x*(a.z*B[1][2]*(m_alpha - m_gp + m_gz)*I4 + a.y*(B[2][2]*(-m_gp + m_gz)*I4 + a.z*a.z*(2*m_gz + 4*m_gp*I4*I4 - 4*m_gz*I4*I4 + lam*m_lamz))))/J;\n\tD[5][5]=(a.z*a.z*B[1][1]*m_gz*I4 + 2*a.y*a.z*B[1][2]*(m_alpha + m_gz)*I4 - m_gp*(-1 + 2*a.y*a.z*B[1][2]*I4 + a.y*a.y*B[2][2]*I4 + a.z*a.z*I4*(B[1][1] - 4*a.y*a.y*I4)) + J*m_lamp - I3*m_lamp + a.y*a.y*(B[2][2]*m_gz*I4 + a.z*a.z*(2*m_gz - 4*m_gz*I4*I4 + lam*m_lamz)))/J;\n\n\n\t// set symmetric components\n\tD[1][0] = D[0][1]; D[2][0] = D[0][2]; D[3][0] = D[0][3]; D[4][0] = D[0][4]; D[5][0] = D[0][5];\n\tD[2][1] = D[1][2]; D[3][1] = D[1][3]; D[4][1] = D[1][4]; D[5][1] = D[1][5];\n\tD[3][2] = D[2][3]; D[4][2] = D[2][4]; D[5][2] = D[2][5];\n\tD[4][3] = D[3][4]; D[5][3] = D[3][5];\n\tD[5][4] = D[4][5];\n\n/*\n\tFILE* fp = fopen(\"C.txt\", \"wt\");\n\tint i, j;\n\tfor (i=0; i<6; ++i)\n\t{\n\t\tfor (j=0; j<6; ++j) fprintf(fp, \"%15.7lg \", D[i][j]);\n\t\tfprintf(fp, \"\\n\");\n\t}\n\tfclose(fp);\n*/\n\n\t//the following code is used for testing purposes and replaces the above stiffness when testing\n\t//comment out for normal use\n\n\t//constant trans iso\n\t//D[0][0]=2*m_gp + m_lamp;\n\t//D[0][1]=m_lamp;\n\t//D[0][2]=m_alpha + m_lamp;\n\t//D[0][3]=0;\n\t//D[0][4]=0;\n\t//D[0][5]=0;\n\t//D[1][1]=2*m_gp + m_lamp;\n\t//D[1][2]=m_alpha + m_lamp;\n\t//D[1][3]=0;\n\t//D[1][4]=0;\n\t//D[1][5]=0;\n\t//D[2][2]=2*m_alpha + 2*m_gp + 2*m_gz + m_lamp + m_lamz;\n\t//D[2][3]=0;\n\t//D[2][4]=0;\n\t//D[2][5]=0;\n\t//D[3][3]=m_gp;\n\t//D[3][4]=0;\n\t//D[3][5]=0;\n\t//D[4][4]=m_gz;\n\t//D[4][5]=0;\n\t//D[5][5]=m_gz;\n\n\n\t//compressible neohookean\n\t//D[0][0]=(2*m_gp + m_lamp - m_lamp*log(I3))/J;\n\t//D[0][1]=m_lamp/J;\n\t//D[0][2]=m_lamp/J;\n\t//D[0][3]=0;\n\t//D[0][4]=0;\n\t//D[0][5]=0;\n\t//D[1][1]=(2*m_gp + m_lamp - m_lamp*log(I3))/J;\n\t//D[1][2]=m_lamp/J;\n\t//D[1][3]=0;\n\t//D[1][4]=0;\n\t//D[1][5]=0;\n\t//D[2][2]=(2*m_gp + m_lamp - m_lamp*log(I3))/J;\n\t//D[2][3]=0;\n\t//D[2][4]=0;\n\t//D[2][5]=0;\n\t//D[3][3]=(m_gp - (m_lamp*log(I3))/2.)/J;\n\t//D[3][4]=0;\n\t//D[3][5]=0;\n\t//D[4][4]=(m_gp - (m_lamp*log(I3))/2.)/J;\n\t//D[4][5]=0;\n\t//D[5][5]=(m_gp - (m_lamp*log(I3))/2.)/J;\n\n\n\t//identity\n\t//D[0][0]=1;\n\t//D[0][1]=0;\n\t//D[0][2]=0;\n\t//D[0][3]=0;\n\t//D[0][4]=0;\n\t//D[0][5]=0;\n\t//D[1][1]=1;\n\t//D[1][2]=0;\n\t//D[1][3]=0;\n\t//D[1][4]=0;\n\t//D[1][5]=0;\n\t//D[2][2]=1;\n\t//D[2][3]=0;\n\t//D[2][4]=0;\n\t//D[2][5]=0;\n\t//D[3][3]=1;\n\t//D[3][4]=0;\n\t//D[3][5]=0;\n\t//D[4][4]=1;\n\t//D[4][5]=0;\n\t//D[5][5]=1;\n\n\treturn tens4ds(D);\n}\n"
  },
  {
    "path": "FEBioMech/FENeoHookeanTransIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This material was added by Shawn Reese\n\nclass FENeoHookeanTransIso : public FEElasticMaterial\n{\npublic:\n\tFENeoHookeanTransIso(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\npublic:\n\tdouble\tm_Ep;\t//!< Young's modulus\n\tdouble\tm_Ez;\t//!< Young's modulus\n\tdouble\tm_vz;\t//!< Poisson's ratio\n\tdouble\tm_vp;\t//!< Poisson's ratio\n\tdouble\tm_gz;\t//!< shear modulus\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENewtonianViscousSolid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonianViscousSolid.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FENewtonianViscousSolid, FEElasticMaterial)\n\tADD_PARAMETER(m_kappa, FE_RANGE_GREATER_OR_EQUAL(      0.0), \"kappa\")->setUnits(\"P.t\")->setLongName(\"bulk viscosity\");\n\tADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(      0.0), \"mu\"   )->setUnits(\"P.t\")->setLongName(\"shear viscosity\");\n    ADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENewtonianViscousSolid::FENewtonianViscousSolid(FEModel* pfem) : FEElasticMaterial(pfem) \n{\n    m_kappa = 0.0;\n    m_mu = 0.0;\n    m_secant_tangent = false;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FENewtonianViscousSolid::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    mat3ds D = pt.RateOfDeformation();\n    \n    // Identity\n    mat3dd I(1);\n    \n    // calculate stress\n    mat3ds s = I*(D.tr()*(m_kappa - 2*m_mu/3)) + D*(2*m_mu);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FENewtonianViscousSolid::Tangent(FEMaterialPoint& mp)\n{\n    const FETimeInfo& tp = GetTimeInfo();\n    tens4ds Cv;\n    \n    if (tp.timeIncrement > 0) {\n        mat3dd I(1);\n        double tmp = tp.alphaf*tp.gamma/(tp.beta*tp.timeIncrement);\n        Cv = (dyad1s(I)*(m_kappa - 2 * m_mu / 3) + dyad4s(I)*(2 * m_mu))*tmp;\n    }\n    else Cv.zero();\n    \n    return Cv;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENewtonianViscousSolid::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n"
  },
  {
    "path": "FEBioMech/FENewtonianViscousSolid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\nclass FENewtonianViscousSolid : public FEElasticMaterial\n{\npublic:\n    FENewtonianViscousSolid(FEModel* pfem);\n    \npublic:\n    double\tm_kappa;\t//!< bulk viscosity\n    double\tm_mu;       //!< shear viscosity\n    bool    m_secant_tangent;   //!< flag for using secant tangent calculation\n    \npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    bool UseSecantTangent() override { return m_secant_tangent; }\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENewtonianViscousSolidUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonianViscousSolidUC.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FENewtonianViscousSolidUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_kappa, FE_RANGE_GREATER_OR_EQUAL(      0.0), \"kappa\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_mu   , FE_RANGE_GREATER_OR_EQUAL(      0.0), \"mu\"   )->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENewtonianViscousSolidUC::FENewtonianViscousSolidUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n    m_kappa = 0.0;\n    m_mu = 0.0;\n    m_secant_tangent = false;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FENewtonianViscousSolidUC::DevStress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    mat3ds D = pe.RateOfDeformation();\n    \n    // Identity\n    mat3dd I(1);\n    \n    // calculate stress\n    mat3ds s = I*(D.tr()*(m_kappa - 2*m_mu/3)) + D*(2*m_mu);\n    \n    // determinant of deformation gradient\n    double J = pe.m_J;\n    \n    return s.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FENewtonianViscousSolidUC::DevTangent(FEMaterialPoint& mp)\n{\n    mat3dd I(1);\n    tens4ds Cv;\n\n    double dt = CurrentTimeIncrement();\n    if (dt > 0)\n        Cv = (dyad1s(I, I)*(m_kappa - 2 * m_mu / 3) + dyad4s(I, I)*(2 * m_mu)) / (2 * dt);\n    else\n        Cv.zero();\n    \n    return Cv;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENewtonianViscousSolidUC::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    return 0;\n}\n\n"
  },
  {
    "path": "FEBioMech/FENewtonianViscousSolidUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\nclass FENewtonianViscousSolidUC : public FEUncoupledMaterial\n{\npublic:\n    FENewtonianViscousSolidUC(FEModel* pfem);\n    \npublic:\n    double\tm_kappa;\t//!< bulk viscosity\n    double\tm_mu;       //!< shear viscosity\n    bool    m_secant_tangent;   //!< flag for using secant tangent calculation\n\npublic:\n    //! calculate stress at material point\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    bool UseSecantTangent() override { return m_secant_tangent; }\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENodalForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FENodalForce.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FENodeSet.h>\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\nBEGIN_FECORE_CLASS(FENodalForce, FENodalLoad)\n\tADD_PARAMETER(m_f, \"value\")->setUnits(UNIT_FORCE)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_shellBottom, \"shell_bottom\");\nEND_FECORE_CLASS();\n\nFENodalForce::FENodalForce(FEModel* fem) : FENodalLoad(fem)\n{\n\tm_f = vec3d(0, 0, 0);\n\tm_shellBottom = false;\n}\n\n// set the value\nvoid FENodalForce::SetValue(const vec3d& v)\n{\n\tm_f = v;\n}\n\nbool FENodalForce::SetDofList(FEDofList& dofList)\n{\n\tif (m_shellBottom)\n\t\treturn dofList.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\telse\n\t\treturn dofList.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n}\n\nvoid FENodalForce::GetNodalValues(int inode, std::vector<double>& val)\n{\n\tassert(val.size() == 3);\n\tconst FENodeSet& nset = *GetNodeSet();\n\tint nid = nset[inode];\n\tconst FENode& node = *nset.Node(inode);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = inode;\n\n\tvec3d f = m_f(mp);\n\n\tval[0] = f.x;\n\tval[1] = f.y;\n\tval[2] = f.z;\n}\n"
  },
  {
    "path": "FEBioMech/FENodalForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENodalLoad.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Class that implements an equivalent nodal force load.\n// This load will be applied directly to the load vector of the system\nclass FEBIOMECH_API FENodalForce : public FENodalLoad\n{\npublic:\n\tFENodalForce(FEModel* fem);\n\n\t// set the value\n\tvoid SetValue(const vec3d& v);\n\nprotected: // required functions of FENodalLoad\n\n\t// Set the dof list\n\tbool SetDofList(FEDofList& dofList) override;\n\n\t// get the nodal values\n\tvoid GetNodalValues(int inode, std::vector<double>& val) override;\n\nprivate:\n\tFEParamVec3\t\tm_f;\t\t//!< the applied force\n\tbool\t\t\tm_shellBottom;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENodalTargetForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FENodalTargetForce.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FENodeSet.h>\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n\n// NOTE: We pass FEModelLoad as the base since I don't want the relative flag\nBEGIN_FECORE_CLASS(FENodalTargetForce, FEModelLoad)\n\tADD_PARAMETER(m_w, \"scale\")->setUnits(UNIT_NONE)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_f, \"force\")->setUnits(UNIT_FORCE);\n\tADD_PARAMETER(m_shellBottom, \"shell_bottom\");\nEND_FECORE_CLASS();\n\nFENodalTargetForce::FENodalTargetForce(FEModel* fem) : FENodalLoad(fem)\n{\n\tm_w = 1;\n\tm_f = vec3d(0, 0, 0);\n\tm_shellBottom = false;\n}\n\nvoid FENodalTargetForce::Activate()\n{\n\t// use a little hack to get the initial reaction forces\n\tm_brelative = true;\n\tFENodalLoad::Activate();\n\tm_brelative = false;\n}\n\nbool FENodalTargetForce::SetDofList(FEDofList& dofList)\n{\n\tif (m_shellBottom)\n\t\treturn dofList.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\telse\n\t\treturn dofList.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n}\n\nvoid FENodalTargetForce::GetNodalValues(int inode, std::vector<double>& val)\n{\n\tassert(val.size() == 3);\n\tconst FENodeSet& nset = *GetNodeSet();\n\tconst FENode& node = *nset.Node(inode);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = inode;\n\n\tvec3d f = m_f(mp);\n\n\tval[0] = m_rval[inode][0] * (1.0 - m_w) + m_w * f.x;\n\tval[1] = m_rval[inode][1] * (1.0 - m_w) + m_w * f.y;\n\tval[2] = m_rval[inode][2] * (1.0 - m_w) + m_w * f.z;\n}\n"
  },
  {
    "path": "FEBioMech/FENodalTargetForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENodalLoad.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FENodalTargetForce : public FENodalLoad\n{\npublic:\n\tFENodalTargetForce(FEModel* fem);\n\n\tvoid Activate() override;\n\nprotected: // required functions of FENodalLoad\n\n\t// Set the dof list\n\tbool SetDofList(FEDofList& dofList) override;\n\n\t// get the nodal values\n\tvoid GetNodalValues(int inode, std::vector<double>& val) override;\n\nprivate:\n\tdouble\t\t\tm_w;\n\tFEParamVec3\t\tm_f;\n\tbool\t\t\tm_shellBottom;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FENodeToNodeConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FENodeToNodeConstraint.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEGlobalMatrix.h>\n\nBEGIN_FECORE_CLASS(FENodeToNodeConstraint, FENLConstraint)\n\tADD_PARAMETER(m_a, FE_RANGE_GREATER(0), \"a\");\n\tADD_PARAMETER(m_b, FE_RANGE_GREATER(0), \"b\");\nEND_FECORE_CLASS();\n\nFENodeToNodeConstraint::FENodeToNodeConstraint(FEModel* fem) : FENLConstraint(fem)\n{\n\tm_a = m_b = -1;\n\tm_Lm = vec3d(0, 0, 0);\n\tm_Lp = vec3d(0, 0, 0);\n}\n\nvoid FENodeToNodeConstraint::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\tar & m_a & m_b & m_Lm & m_Lp;\n}\n\n// allocate equations\nint FENodeToNodeConstraint::InitEquations(int neq)\n{\n\t// we allocate three equations\n\tm_LM.resize(3);\n\tm_LM[0] = neq;\n\tm_LM[1] = neq+1;\n\tm_LM[2] = neq+2;\n\treturn 3;\n}\n\nvoid FENodeToNodeConstraint::UnpackLM(vector<int>& lm)\n{\n\t// get the displacement dofs\n\tint dofX = GetDOFIndex(\"x\");\n\tint dofY = GetDOFIndex(\"y\");\n\tint dofZ = GetDOFIndex(\"z\");\n\n\t// we need to couple the dofs of node A, B, and the LMs\n\tFEMesh& mesh = GetMesh();\n\n\t// add the dofs of node A\n\tFENode& node_a = mesh.Node(m_a - 1);\n\tlm.push_back(node_a.m_ID[dofX]);\n\tlm.push_back(node_a.m_ID[dofY]);\n\tlm.push_back(node_a.m_ID[dofZ]);\n\n\t// add the dofs of node B\n\tFENode& node_b = mesh.Node(m_b - 1);\n\tlm.push_back(node_b.m_ID[dofX]);\n\tlm.push_back(node_b.m_ID[dofY]);\n\tlm.push_back(node_b.m_ID[dofZ]);\n\n\t// add the LM equations\n\tlm.push_back(m_LM[0]);\n\tlm.push_back(m_LM[1]);\n\tlm.push_back(m_LM[2]);\n}\n\nvoid FENodeToNodeConstraint::PrepStep()\n{\n\tm_Lp = m_Lm;\n}\n\nvoid FENodeToNodeConstraint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tm_Lm.x = m_Lp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\tm_Lm.y = m_Lp.x + Ui[m_LM[1]] + ui[m_LM[1]];\n\tm_Lm.z = m_Lp.x + Ui[m_LM[2]] + ui[m_LM[2]];\n}\n\nvoid FENodeToNodeConstraint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tUi[m_LM[0]] += ui[m_LM[0]];\n\tUi[m_LM[1]] += ui[m_LM[1]];\n\tUi[m_LM[2]] += ui[m_LM[2]];\n}\n\n// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\nvoid FENodeToNodeConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEMesh& mesh = GetMesh();\n\tvec3d ra = mesh.Node(m_a - 1).m_rt;\n\tvec3d rb = mesh.Node(m_b - 1).m_rt;\n\tvec3d c = ra - rb;\n\n\tvector<double> fe(9, 0.0);\n\tfe[0] = -m_Lm.x;\n\tfe[1] = -m_Lm.y;\n\tfe[2] = -m_Lm.z;\n\tfe[3] =  m_Lm.x;\n\tfe[4] =  m_Lm.y;\n\tfe[5] =  m_Lm.z;\n\tfe[6] = -c.x;\n\tfe[7] = -c.y;\n\tfe[8] = -c.z;\n\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\tR.Assemble(lm, fe);\n}\n\n// Evaluates the contriubtion to the stiffness matrix\nvoid FENodeToNodeConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEElementMatrix ke(9, 9);\n\tke.zero();\n\n\tke[0][6] = ke[6][0] = 1;\n\tke[1][7] = ke[7][1] = 1;\n\tke[2][8] = ke[8][2] = 1;\n\n\tke[3][6] = ke[6][3] = -1;\n\tke[4][7] = ke[7][4] = -1;\n\tke[5][8] = ke[8][5] = -1;\n\n\tvector<int> lm;\n\tUnpackLM(lm);\n\tke.SetIndices(lm);\n\tLS.Assemble(ke);\n}\n\n// Build the matrix profile\nvoid FENodeToNodeConstraint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n"
  },
  {
    "path": "FEBioMech/FENodeToNodeConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENLConstraint.h>\n\nclass FENodeToNodeConstraint : public FENLConstraint\n{\npublic:\n\tFENodeToNodeConstraint(FEModel* fem);\n\n\t// allocate equations\n\tint InitEquations(int neq) override;\n\n\t// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t// Evaluates the contriubtion to the stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t// Build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\nprotected:\n\tvoid UnpackLM(vector<int>& lm);\n\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\nprivate:\n\tint\t\tm_a, m_b;\n\tvec3d\tm_Lm, m_Lp;\n\n\tvector<int> m_LM;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FENonlinearSpring.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FENonlinearSpring.h\"\n\nBEGIN_FECORE_CLASS(FENonlinearSpringMaterial, FEDiscreteElasticMaterial)\n\tADD_PARAMETER(m_scale, \"scale\");\n\tADD_PARAMETER(m_measure, \"measure\", 0, \"elongation\\0strain\\0stretch\\0\");\n\tADD_PROPERTY(m_F, \"force\");\nEND_FECORE_CLASS();\n\n\nFENonlinearSpringMaterial::FENonlinearSpringMaterial(FEModel* pfem) : FEDiscreteElasticMaterial(pfem)\n{\n\tm_scale = 1.0;\n\tm_measure = 0;\n\tm_F = nullptr;\n}\n\nvec3d FENonlinearSpringMaterial::Force(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d t = mp.m_drt; t.unit();\n\n\t// calculate spring lenghts\n\tdouble L0 = mp.m_dr0.norm();\n\tdouble Lt = mp.m_drt.norm();\n\tdouble DL = Lt - L0;\n\n\t// evaluate the deformation measure\n\tdouble s = 0.0;\n\tswitch (m_measure)\n\t{\n\tcase 0: s = DL; break;\n\tcase 1: s = DL / L0; break;\n\tcase 2: s = Lt / L0; break;\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// get the force\n\tdouble F = m_scale * m_F->value(s);\n\n\t// evaluate the spring force\n\treturn t*F;\n}\n\nmat3d FENonlinearSpringMaterial::Stiffness(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e = mp.m_drt; e.unit();\n\n\t// calculate spring lengths\n\tdouble L0 = mp.m_dr0.norm();\n\tdouble Lt = mp.m_drt.norm();\n\tdouble DL = Lt - L0;\n\n\t// evaluate the deformation measure\n\tdouble s = 0.0, ds = 1.0;;\n\tswitch (m_measure)\n\t{\n\tcase 0: s = DL; break;\n\tcase 1: s = DL / L0; ds = 1.0 / L0; break;\n\tcase 2: s = Lt / L0; ds = 1.0 / L0; break;\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// get the force\n\tdouble F = m_scale * m_F->value(s);\n\n\t// evaluate the stiffness\n\tdouble E = m_scale * m_F->derive(s) * ds;\n\n\tif (Lt == 0) { F = 0; Lt = 1; e = vec3d(1, 1, 1); }\n\n\tmat3d A; A.zero();\n\tA[0][0] = ((E - F / Lt) * e.x * e.x + F / Lt);\n\tA[1][1] = ((E - F / Lt) * e.y * e.y + F / Lt);\n\tA[2][2] = ((E - F / Lt) * e.z * e.z + F / Lt);\n\n\tA[0][1] = A[1][0] = (E - F / Lt) * e.x * e.y;\n\tA[1][2] = A[2][1] = (E - F / Lt) * e.y * e.z;\n\tA[0][2] = A[2][0] = (E - F / Lt) * e.x * e.z;\n\n\treturn A;\n}\n\ndouble FENonlinearSpringMaterial::StrainEnergy(FEDiscreteMaterialPoint& mp)\n{\n\t// TODO: implement this\n\tassert(false);\n\treturn 0.0;\n}\n"
  },
  {
    "path": "FEBioMech/FENonlinearSpring.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDiscreteElasticMaterial.h\"\n#include <FECore/FEFunction1D.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a non-linear spring where users can choose what \n//! deformation measure to use in the force curve. \nclass FEBIOMECH_API FENonlinearSpringMaterial : public FEDiscreteElasticMaterial\n{\npublic:\n\tFENonlinearSpringMaterial(FEModel* pfem);\n\n\tvec3d Force(FEDiscreteMaterialPoint& mp) override;\n\tmat3d Stiffness(FEDiscreteMaterialPoint& mp) override;\n\tdouble StrainEnergy(FEDiscreteMaterialPoint& mp) override;\n\nprivate:\n\tFEFunction1D*\tm_F;\t\t//!< force-displacement function\n\tdouble\t\t\tm_scale;\t//!< scale factor for force\n\tint\t\t\t\tm_measure;\t//!< deformation measure\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEODFFiberDistribution.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEODFFiberDistribution.h\"\n#include <cmath>\n#include <algorithm>\n#include <FECore/FEModel.h>\n#include <FECore/FEDomain.h>\n#include <FECore/FENode.h>\n#include <FEAMR/sphericalHarmonics.h>\n#include <FEAMR/SpherePointsGenerator.h>\n#include <FECore/Timer.h>\n#include <iostream>\n\nusing sphere = SpherePointsGenerator;\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFiberODF, FECoreClass)\n\tADD_PARAMETER(m_shpHar, \"shp_harmonics\");\n\tADD_PARAMETER(m_pos, \"position\");\nEND_FECORE_CLASS();\n\n//=============================================================================\n\nvoid FEElementODF::calcODF(std::vector<std::vector<double>>& ODFs)\n{\n    size_t npts = sphere::GetNumNodes(FULL);\n\n    m_ODF.resize(npts);\n\n    double maxWeight = std::distance(m_weights.begin(), std::max_element(m_weights.begin(), m_weights.end()));\n    m_ODF = ODFs[maxWeight];\n\n    // define gradient descent step size\n    double eps = 0.25;\n\n    // threshold for convegence \n    double thresh = 5e-8; \n\n    double prevPhi = 3;\n    double nPhi = 2;\n\n    std::vector<std::vector<double>> logmaps(ODFs.size(), std::vector<double>(npts, 0));\n\n    while(nPhi < prevPhi)\n    {\n        // compute logmaps from current meanPODF to every other ODF\n        for(int index = 0; index < ODFs.size(); index++)\n        {\n            auto& currentLogmap = logmaps[index];\n            auto& currentODF = ODFs[index];\n            double currentWeight = m_weights[index];\n\n            double dot = 0;\n            for(int index2 = 0; index2 < npts; index2++)\n            {\n                dot += m_ODF[index2]*currentODF[index2];\n            }\n\n            // if the two vectors are the same, tangent is the 0 vector\n            if(abs(1-dot) < 10e-12)\n            {\n                logmaps[index] = std::vector<double>(npts, 0);\n            }\n            else\n            {\n                double denom = sqrt(1-dot*dot)*acos(dot);\n\n                for(int index2 = 0; index2 < npts; index2++)\n                {\n                    currentLogmap[index2] = (currentODF[index2] - dot*m_ODF[index2])/denom;\n                }\n            }\n        }\n\n        // compute tangent vector of each \n        std::vector<double> phi(m_ODF.size(), 0);\n        for(int index = 0; index < logmaps.size(); index++)\n        {\n            for(int index2 = 0; index2 < npts; index2++)\n            {\n                phi[index2] += logmaps[index][index2]*m_weights[index];\n            }\n\n        }\n\n        prevPhi = nPhi;\n\n        // take norm of phi\n        nPhi = 0;\n        for(double val : phi)\n        {\n            nPhi += val*val;\n        }\n        nPhi = sqrt(nPhi);\n\n        if(nPhi < thresh) break;\n\n        // apply exponential map to mean ODF in direction of tanget vector\n        for(int index = 0; index < phi.size(); index++)\n        {\n            phi[index] = phi[index]*eps;\n        }\n\n        double normPhi = 0;\n        for(int index = 0; index < npts; index++)\n        {\n            normPhi += phi[index]*phi[index];\n        }\n        normPhi = sqrt(normPhi);\n\n        if(normPhi >= 10e-12)\n        {\n            for(int index = 0; index < npts; index++)\n            {\n                m_ODF[index] = cos(normPhi)*m_ODF[index] + sin(normPhi)*phi[index]/normPhi;\n            } \n        }\n    }\n\n    //  undo sqaure root transform to obtain mean ODF\n    for(int index = 0; index < npts; index++)\n    {\n        m_ODF[index] = m_ODF[index]*m_ODF[index];\n    }\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEODFFiberDistribution, FEElasticMaterial)\n\n\t// material properties\n\tADD_PROPERTY(m_pFmat, \"fibers\");\n    ADD_PROPERTY(m_ODF, \"fiber_odf\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEODFFiberDistribution::FEODFFiberDistribution(FEModel* pfem) \n    : FEElasticMaterial(pfem), m_lengthScale(10), m_hausd(0.05), m_grad(1.3), m_interpolate(false)\n{\n\tm_pFmat = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEODFFiberDistribution::~FEODFFiberDistribution()\n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEODFFiberDistribution::CreateMaterialPointData()\n{\n\tFEMaterialPointData* mp = FEElasticMaterial::CreateMaterialPointData();\n\tmp->SetNext(m_pFmat->CreateMaterialPointData());\n    return mp;\n}\n\n//-----------------------------------------------------------------------------\nbool FEODFFiberDistribution::Init()\n{\n    Timer totalTime, initTime, interpTime, reduceTime;\n\n    double remeshTime = 0;\n\n    totalTime.start();\n    initTime.start();\n\n    // initialize base class\n\tif (FEElasticMaterial::Init() == false) return false;\n\n    m_interpolate = m_ODF.size() > 1;\n\n    // Get harmonic order by looking at number of coefficients of first ODF\n    m_order = (sqrt(8*m_ODF[0]->m_shpHar.size() + 1) - 3)/2;\n\n    // Initialize spherical coordinates and T matrix\n    auto& nodes = sphere::GetNodes(FULL);\n    getSphereCoords(nodes, m_theta, m_phi);\n    m_T = compSH(m_order, m_theta, m_phi);\n    matrix transposeT = m_T->transpose();\n    matrix B = (transposeT*(*m_T)).inverse()*transposeT;\n\n    // Calculate the ODFs for each of the image subregions\n    #pragma omp parallel for\n    for(int index = 0; index < m_ODF.size(); index++)\n    {\n        FEFiberODF* ODF = m_ODF[index];\n        reconstructODF(ODF->m_shpHar, ODF->m_ODF, m_theta, m_phi);\n    }\n\n    initTime.stop();\n\n    if(m_interpolate)\n    {\n        // Apply the square root transform to each ODF\n        std::vector<std::vector<double>> pODF;\n        pODF.resize(m_ODF.size());\n\n        #pragma omp parallel for\n        for(int index = 0; index < m_ODF.size(); index++)\n        {\n            auto& currentODF = m_ODF[index]->m_ODF;\n            auto& currentPODF = pODF[index];\n\n            currentPODF.resize(currentODF.size());\n\n            for(int index2 = 0; index2 < currentODF.size(); index2++)\n            {\n                currentPODF[index2] = sqrt(currentODF[index2]);\n            }\n        }\n\n        FEMesh& mesh = GetFEModel()->GetMesh();\n\n        std::vector<int> ids;\n        \n        for(int index = 0; index < mesh.Domains(); index++)\n        {\n            FEDomain* domain = &mesh.Domain(index);\n\t\t\tFEMaterial* mat = dynamic_cast<FEMaterial*>(domain->GetMaterial());\n\t\t\tif (mat != this->GetAncestor())\n\t\t\t{\n                continue;\n            }\n\n            #pragma omp parallel for\n            for(int el = 0; el < domain->Elements(); el++)\n            {\n                FEElement& element = domain->ElementRef(el);\n\n                FEElementODF* odf = new FEElementODF(m_ODF.size());\n\n                // Calculate the centroid of the element\n                for(int node = 0; node < element.Nodes(); node++)\n                {\n                    odf->m_pos += mesh.Node(element.m_node[node]).m_r0;\n                }\n\t\t\t\todf->m_pos /= (double)element.Nodes();\n\n                // Calculate weight to each ODF\n                double sum = 0;\n                for(int index = 0; index < m_ODF.size(); index++)\n                {\n                    auto current = m_ODF[index];\n\n                    double weight = 1/(odf->m_pos - current->m_pos).Length();\n                    odf->m_weights[index] = weight;\n                    sum += weight;\n                }\n                // Normalize weights\n                for(int index = 0; index < m_ODF.size(); index++)\n                {\n                    odf->m_weights[index] /= sum;\n                }\n\n                // Add element odf object to map\n                #pragma omp critical\n                {\n                    m_ElODF[element.GetID()] = odf;\n                    ids.push_back(element.GetID());\n                }\n            }\n        }\n\n        interpTime.start();\n        #pragma omp parallel for\n        for(int index = 0; index < ids.size(); index++)\n        {\n            m_ElODF[ids[index]]->calcODF(pODF);\n        }\n        interpTime.stop();\n\n        reduceTime.start();\n        #pragma omp parallel for\n        for(int index = 0; index < ids.size(); index++)\n        {\n            reduceODF(m_ElODF[ids[index]], B, &remeshTime);\n        }\n        reduceTime.stop();\n\n    }\n    else\n    {\n        reduceODF(m_ODF[0], B, new double);\n    }\n\n    totalTime.stop();\n\n    // std::cout << \"Total Time: \" << totalTime.peek() << std::endl;\n    // std::cout << \"Init Time: \" << initTime.peek() << std::endl;\n    // std::cout << \"Interp Time: \" << interpTime.peek() << std::endl;\n    // std::cout << \"Reduce Time: \" << reduceTime.peek() << std::endl;\n    // std::cout << \"Remesh Time: \" << remeshTime/36 << std::endl;\n\n\treturn true;\n}\n\nvoid FEODFFiberDistribution::reduceODF(FEBaseODF* ODF, matrix& B, double* time)\n{\n    Timer remeshTime;\n    vector<double>* sphHar;\n\n    if(dynamic_cast<FEElementODF*>(ODF))\n    {\n        // Calculate spherical harmonics\n        int sphrHarmSize = (m_order+1)*(m_order+2)/2;\n        sphHar = new vector<double>(sphrHarmSize);\n\n        B.mult(ODF->m_ODF, *sphHar);\n    }\n    else\n    {\n        sphHar = &dynamic_cast<FEFiberODF*>(ODF)->m_shpHar;\n    }\n\n    // Calculate the graident\n    std::vector<double> gradient;\n    altGradient(m_order, ODF->m_ODF, gradient);\n    \n    // Remesh the sphere\n    vector<vec3i> elems;\n    remeshTime.start();\n    remesh(gradient, m_lengthScale, m_hausd, m_grad, ODF->m_nodePos, elems);\n    remeshTime.stop();\n\n    #pragma omp critical\n    *time += remeshTime.peek();\n\n    int NN = ODF->m_nodePos.size();\n    int NE = elems.size();\n\n    // Convert the new coordinates to spherical coordinates\n    std::vector<double> theta;\n    std::vector<double> phi;\n    getSphereCoords(ODF->m_nodePos, theta, phi);\n\n    // Compute the new ODF values\n    auto T = compSH(m_order, theta, phi); \n    ODF->m_ODF.resize(NN);\n    (*T).mult(*sphHar, ODF->m_ODF);\n\n    // Properly scale the ODF values based on the sizes of the surrounding elements\n    vector<double> area(NN, 0);\n    for(int index = 0; index < NE; index++)\n    {\n        int n0 = elems[index].x;\n        int n1 = elems[index].y;\n        int n2 = elems[index].z;\n\n        vec3d n01 = ODF->m_nodePos[n0] - ODF->m_nodePos[n1];\n        vec3d n02 = ODF->m_nodePos[n0] - ODF->m_nodePos[n2];\n\n        double temp = (n01^n02).Length()/2;\n\n        area[n0] += temp;\n        area[n1] += temp;\n        area[n2] += temp;\n    }\n\n    // Normalize the ODF\n    double sum = 0;\n    for(int index = 0; index < NN; index++)\n    {\n        double val = ODF->m_ODF[index];\n\n        if(ODF->m_nodePos[index].z != 0)\n        {\n            val *= 2;\n        }\n\n        val *= area[index];\n\n        ODF->m_ODF[index] = val;\n\n        sum += val;\n    }\n\n    for(int index = 0; index < NN; index++)\n    {\n        ODF->m_ODF[index] /= sum;\n    }\n\n    if(dynamic_cast<FEElementODF*>(ODF))\n    {\n        delete sphHar;\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FEODFFiberDistribution::Serialize(DumpStream& ar)\n{\t\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEODFFiberDistribution::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n    FEBaseODF* ODF;\n    if(m_interpolate)\n    {\n        ODF = m_ElODF[mp.m_elem->GetID()];\n    }\n    else\n    {\n        ODF = m_ODF[0];\n    }\n\n\t// calculate stress\n\tmat3ds s; s.zero();\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n    for(int index = 0; index < ODF->m_ODF.size(); index++)\n    {\n        // evaluate ellipsoidally distributed material coefficients\n        double R = ODF->m_ODF[index];\n        \n        // convert fiber to global coordinates\n        vec3d n0 = Q*ODF->m_nodePos[index];\n        \n        // calculate the stress\n        s += m_pFmat->FiberStress(mp, fp.FiberPreStretch(n0))*(R);\n    }\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEODFFiberDistribution::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n    FEBaseODF* ODF;\n    if(m_interpolate)\n    {\n        ODF = m_ElODF[mp.m_elem->GetID()];\n    }\n    else\n    {\n        ODF = m_ODF[0];\n    }\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n    // initialize stress tensor\n\ttens4ds c;\n\tc.zero();\n    for(int index = 0; index < ODF->m_ODF.size(); index++)\n    {\n        //evaluate ellipsoidally distributed material coefficients\n        double R = ODF->m_ODF[index];\n        \n        // convert fiber to global coordinates\n        vec3d n0 = Q*ODF->m_nodePos[index];\n\n        // calculate the tangent\n        c += m_pFmat->FiberTangent(mp, fp.FiberPreStretch(n0))*(R);\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEODFFiberDistribution::StrainEnergyDensity(FEMaterialPoint& mp)\n{ \n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FEFiberMaterialPoint& fp = *mp.ExtractData<FEFiberMaterialPoint>();\n\n    FEBaseODF* ODF;\n    if(m_interpolate)\n    {\n        ODF = m_ElODF[mp.m_elem->GetID()];\n    }\n    else\n    {\n        ODF = m_ODF[0];\n    }\n\n\t// get the local coordinate system\n\tmat3d Q = GetLocalCS(mp);\n\n    double sed = 0.0;\n    for(int index = 0; index < ODF->m_ODF.size(); index++)\n    {\n        // evaluate ellipsoidally distributed material coefficients\n        double R = ODF->m_ODF[index];\n        \n        // convert fiber to global coordinates\n        vec3d n0 = Q*ODF->m_nodePos[index];\n\n        // calculate the stress\n        sed += m_pFmat->FiberStrainEnergyDensity(mp, fp.FiberPreStretch(n0))*(R);\n    }\n\n\treturn sed;\n}"
  },
  {
    "path": "FEBioMech/FEODFFiberDistribution.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreClass.h>\n#include \"FEElasticMaterial.h\"\n#include \"FEFiberMaterial.h\"\n#include \"FEFiberDensityDistribution.h\"\n#include \"FEFiberIntegrationScheme.h\"\n#include \"FEFiberMaterialPoint.h\"\n#include <unordered_map>\n\nclass FEODFFiberDistribution;\n\nclass FEBaseODF\n{\npublic:\n    FEBaseODF() : m_pos(0,0,0) {}\n    virtual ~FEBaseODF() {}\n\npublic:\n    vec3d\tm_pos;\t//!< position\n    std::vector<double> m_ODF; //!< ODF values\n    std::vector<vec3d> m_nodePos; //!< node positions\n};\n\nclass FEFiberODF : public FECoreClass, public FEBaseODF\n{\npublic:\n\tFEFiberODF(FEModel* fem) : FECoreClass(fem) {}\n\npublic:\n    std::vector<double> m_shpHar;\t//!< spherical harmonics Values\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FEFiberODF);\n};\n\nclass FEElementODF : public FEBaseODF\n{\npublic:\n    FEElementODF(int weightsSize) : m_weights(weightsSize) {}\n\n    void calcODF(std::vector<std::vector<double>>& ODFs);\n\npublic:\n    std::vector<double> m_weights; //!< weights for interpolation\n};\n\nclass FEODFFiberDistribution : public FEElasticMaterial\n{\npublic:\n    FEODFFiberDistribution(FEModel* pfem);\n    ~FEODFFiberDistribution();\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // Initialization\n    bool Init() override;\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n    \n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n    \n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tFEFiberMaterial*\t\t\tm_pFmat;    // pointer to fiber material\n    std::vector<FEFiberODF*>    m_ODF;     // ODF objects\n    std::unordered_map<int, FEElementODF*> m_ElODF; // element ODF objects\n\n\tDECLARE_FECORE_CLASS();\n\nprivate:\n    void reduceODF(FEBaseODF* ODF, matrix& B, double* time);\n\nprivate:\n    bool m_interpolate; // whether or not we're interpolating at each element\n    int m_order; // Spherical Harmonic Order\n\n    std::unique_ptr<matrix> m_T;\n\n    std::vector<double> m_theta;\n    std::vector<double> m_phi;\n\n    double m_lengthScale;\n    double m_hausd;\n    double m_grad;\n};"
  },
  {
    "path": "FEBioMech/FEOgdenMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOgdenMaterial.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEOgdenMaterial, FEUncoupledMaterial);\n\tADD_PARAMETER(m_c[0], \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[1], \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[2], \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[3], \"c4\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[4], \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[5], \"c6\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_m[0], FE_RANGE_NOT_EQUAL(0.0), \"m1\");\n\tADD_PARAMETER(m_m[1], FE_RANGE_NOT_EQUAL(0.0), \"m2\");\n\tADD_PARAMETER(m_m[2], FE_RANGE_NOT_EQUAL(0.0), \"m3\");\n\tADD_PARAMETER(m_m[3], FE_RANGE_NOT_EQUAL(0.0), \"m4\");\n\tADD_PARAMETER(m_m[4], FE_RANGE_NOT_EQUAL(0.0), \"m5\");\n\tADD_PARAMETER(m_m[5], FE_RANGE_NOT_EQUAL(0.0), \"m6\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEOgdenMaterial::FEOgdenMaterial(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tm_c[i] = 0;\n\t\tm_m[i] = 1;\n\t}\n\n\tm_eps = 1e-12;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEOgdenMaterial::EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps)\n{\n    A.eigen(l, r);\n    \n    // correct for numerical inaccuracy\n    double d01 = fabs(l[0] - l[1]);\n    double d12 = fabs(l[1] - l[2]);\n    double d02 = fabs(l[0] - l[2]);\n    \n    if (d01 < eps) l[1] = l[0]; //= 0.5*(l[0]+l[1]);\n    if (d02 < eps) l[2] = l[0]; //= 0.5*(l[0]+l[2]);\n    if (d12 < eps) l[2] = l[1]; //= 0.5*(l[1]+l[2]);\n    \n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the Cauchy stress\nmat3ds FEOgdenMaterial::DevStress(FEMaterialPoint &mp)\n{\n    // extract elastic material data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // jacobian\n    double J = pt.m_J;\n    \n    // get the left Cauchy-Green tensor\n    mat3ds b = pt.DevLeftCauchyGreen();\n    \n    // get the eigenvalues and eigenvectors of b\n    double lam2[3];\t// these are the squares of the eigenvalues of V\n    vec3d ev[3];\n    EigenValues(b, lam2, ev, m_eps);\n    \n    // get the eigenvalues of V\n    double lam[3];\n    lam[0] = sqrt(lam2[0]);\n    lam[1] = sqrt(lam2[1]);\n    lam[2] = sqrt(lam2[2]);\n\n    double ci[MAX_TERMS] = { 0 }, mi[MAX_TERMS] = {0};\n\tfor (int i = 0; i < MAX_TERMS; ++i)\n\t{\n\t\tci[i] = m_c[i](mp);\n\t\tmi[i] = m_m[i](mp);\n\t}\n    \n    // stress\n    mat3ds s;\n    s.zero();\n    double T;\n    for (int i=0; i<3; ++i) {\n        T = 0;\n        for (int j=0; j<MAX_TERMS; ++j)\n            T += ci[j]/mi[j]*(pow(lam[i], mi[j]) - 1)/J;\n        s += dyad(ev[i])*T;\n    }\n    \n    return s.dev();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the spatial tangent\ntens4ds FEOgdenMaterial::DevTangent(FEMaterialPoint& mp)\n{\n    int i,j,k;\n    \n    // extract elastic material data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // jacobian\n    double J = pt.m_J;\n    \n    // get the left Cauchy-Green tensor\n    mat3ds b = pt.DevLeftCauchyGreen();\n    \n    // get the eigenvalues and eigenvectors of b\n    double lam2[3];\t// these are the squares of the eigenvalues of V\n    vec3d ev[3];\n    EigenValues(b, lam2, ev, m_eps);\n    \n    // get the eigenvalues of V\n    double lam[3];\n    mat3ds N[3];\n    for (i=0; i<3; ++i) {\n        lam[i] = sqrt(lam2[i]);\n        N[i] = dyad(ev[i]);\n    }\n\n\tdouble ci[MAX_TERMS] = { 0 }, mi[MAX_TERMS] = { 0 };\n\tfor (int i = 0; i < MAX_TERMS; ++i)\n\t{\n\t\tci[i] = m_c[i](mp);\n\t\tmi[i] = m_m[i](mp);\n\t}\n\n    // calculate the powers of eigenvalues\n    double lamp[3][MAX_TERMS];\n    for (j=0; j<MAX_TERMS; ++j)\n    {\n        lamp[0][j] = pow(lam[0], mi[j]);\n        lamp[1][j] = pow(lam[1], mi[j]);\n        lamp[2][j] = pow(lam[2], mi[j]);\n    }\n\n    // principal stresses\n    mat3ds s;\n    s.zero();\n    double T[3];\n    for (i=0; i<3; ++i) {\n        T[i] = 0;\n        for (j=0; j<MAX_TERMS; ++j)\n            T[i] += ci[j]/mi[j]*(lamp[i][j] - 1)/J;\n        s += N[i]*T[i];\n    }\n    \n    // coefficients appearing in elasticity tensor\n    double D[3][3],E[3][3];\n    for (j=0; j<3; ++j) {\n        D[j][j] = 0;\n        for (k=0; k<MAX_TERMS; ++k)\n            D[j][j] += ci[k]/mi[k]*((mi[k]-2)*lamp[j][k]+2)/J;\n        for (i=j+1; i<3; ++i) {\n            D[i][j] = 0;\n            if (lam2[j] != lam2[i])\n                E[i][j] = 2*(lam2[j]*T[i] - lam2[i]*T[j])/(lam2[i]-lam2[j]);\n            else {\n                E[i][j] = 0;\n                for (k=0; k<MAX_TERMS; ++k)\n                    E[i][j] += ci[k]/mi[k]*((mi[k]-2)*lamp[j][k]+2)/J;\n            }\n        }\n    }\n    \n    // spatial elasticity tensor\n    tens4ds c(0.0);\n    mat3dd I(1.0);\n    for (j=0; j<3; ++j) {\n        c += dyad1s(N[j])*D[j][j];\n        for (i=j+1; i<3; ++i) {\n            c += dyad1s(N[i],N[j])*D[i][j];\n            c += dyad4s(N[i],N[j])*E[i][j];\n        }\n    }\n    \n    // This is the final value of the elasticity tensor\n    tens4ds IxI = dyad1s(I);\n    tens4ds I4  = dyad4s(I);\n    \n    c += - 1./3.*(ddots(c,IxI) - IxI*(c.tr()/3.))\n    + 2./3.*((I4-IxI/3.)*s.tr()-dyad1s(s.dev(),I));\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEOgdenMaterial::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    // extract elastic material data\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the left Cauchy-Green tensor\n    mat3ds b = pt.DevLeftCauchyGreen();\n    \n    // get the eigenvalues and eigenvectors of b\n    double lam2[3];\t// these are the squares of the eigenvalues of V\n    vec3d ev[3];\n    EigenValues(b, lam2, ev, m_eps);\n    \n    // get the eigenvalues of V\n    double lam[3];\n    lam[0] = sqrt(lam2[0]);\n    lam[1] = sqrt(lam2[1]);\n    lam[2] = sqrt(lam2[2]);\n    \n\tdouble ci[MAX_TERMS] = { 0 }, mi[MAX_TERMS] = { 0 };\n\tfor (int i = 0; i < MAX_TERMS; ++i)\n\t{\n\t\tci[i] = m_c[i](mp);\n\t\tmi[i] = m_m[i](mp);\n\t}\n\n    // strain energy density\n    double sed = 0;\n    for (int j=0; j<MAX_TERMS; ++j)\n        sed += ci[j]/(mi[j]*mi[j])*\n        (pow(lam[0], mi[j]) + pow(lam[1], mi[j]) + pow(lam[2], mi[j]) - 3);\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEOgdenMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\nclass FEOgdenMaterial :\tpublic FEUncoupledMaterial\n{\npublic:\n\tenum { MAX_TERMS = 6 };\npublic:\n\tFEOgdenMaterial(FEModel* pfem);\n\n\t//! calculate the deviatoric stress\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate the deviatoric tangent\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate the deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \nprotected:\n\tvoid EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps = 0);\n\tdouble\tm_eps;\n\npublic:\n\tFEParamDouble\tm_c[MAX_TERMS];\t//!< coefficients\n\tFEParamDouble\tm_m[MAX_TERMS];\t//!< powers\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEOgdenUnconstrained.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOgdenUnconstrained.h\"\n\nBEGIN_FECORE_CLASS(FEOgdenUnconstrained, FEElasticMaterial);\n\tADD_PARAMETER(m_cp  , \"cp\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[0], \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[1], \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[2], \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[3], \"c4\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[4], \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[5], \"c6\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_m[0], FE_RANGE_NOT_EQUAL(0.0), \"m1\");\n\tADD_PARAMETER(m_m[1], FE_RANGE_NOT_EQUAL(0.0), \"m2\");\n\tADD_PARAMETER(m_m[2], FE_RANGE_NOT_EQUAL(0.0), \"m3\");\n\tADD_PARAMETER(m_m[3], FE_RANGE_NOT_EQUAL(0.0), \"m4\");\n\tADD_PARAMETER(m_m[4], FE_RANGE_NOT_EQUAL(0.0), \"m5\");\n\tADD_PARAMETER(m_m[5], FE_RANGE_NOT_EQUAL(0.0), \"m6\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEOgdenUnconstrained::FEOgdenUnconstrained(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tm_c[i] = 0;\n\t\tm_m[i] = 1;\n\t}\n\t\n\tm_eps = 1e-12;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEOgdenUnconstrained::EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps)\n{\n\tA.eigen(l, r);\n\t\n\t// correct for numerical inaccuracy\n\tdouble d01 = fabs(l[0] - l[1]);\n\tdouble d12 = fabs(l[1] - l[2]);\n\tdouble d02 = fabs(l[0] - l[2]);\n\t\n\tif (d01 < eps) l[1] = l[0]; //= 0.5*(l[0]+l[1]);\n\tif (d02 < eps) l[2] = l[0]; //= 0.5*(l[0]+l[2]);\n\tif (d12 < eps) l[2] = l[1]; //= 0.5*(l[1]+l[2]);\n\t\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the Cauchy stress\nmat3ds FEOgdenUnconstrained::Stress(FEMaterialPoint &mp)\n{\n\t// extract elastic material data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\t\n\t// get the left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\t\n\t// get the eigenvalues and eigenvectors of b\n\tdouble lam2[3];\t// these are the squares of the eigenvalues of V\n\tvec3d ev[3];\n\tEigenValues(b, lam2, ev, m_eps);\n\t\n\t// get the eigenvalues of V\n\tdouble lam[3];\n\tlam[0] = sqrt(lam2[0]);\n\tlam[1] = sqrt(lam2[1]);\n\tlam[2] = sqrt(lam2[2]);\n\t\n\t// evaluate coefficients at material points\n\tdouble cp = m_cp(mp);\n\tdouble ci[MAX_TERMS] = { 0 }, mi[MAX_TERMS] = {0};\n\tfor (int i = 0; i < MAX_TERMS; ++i) {\n\t\tci[i] = m_c[i](mp);\n\t\tmi[i] = m_m[i](mp);\n\t}\n\n\t// stress\n\tmat3ds s;\n\ts.zero();\n\tdouble T;\n\tfor (int i=0; i<3; ++i) {\n\t\tT = cp*(J-1);\n\t\tfor (int j=0; j<MAX_TERMS; ++j)\n\t\t\tT += ci[j]/mi[j]*(pow(lam[i], mi[j]) - 1)/J;\n\t\ts += dyad(ev[i])*T;\n\t}\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the spatial tangent\ntens4ds FEOgdenUnconstrained::Tangent(FEMaterialPoint& mp)\n{\n\tint i,j,k;\n\t\n\t// extract elastic material data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\t\n\t// get the left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\t\n\t// get the eigenvalues and eigenvectors of b\n\tdouble lam2[3];\t// these are the squares of the eigenvalues of V\n\tvec3d ev[3];\n\tEigenValues(b, lam2, ev, m_eps);\n\t\n\t// get the eigenvalues of V\n\tdouble lam[3];\n\tmat3ds N[3];\n\tfor (i=0; i<3; ++i) {\n\t\tlam[i] = sqrt(lam2[i]);\n\t\tN[i] = dyad(ev[i]);\n\t}\n\n\t// evaluate coefficients at material points\n\tdouble cp = m_cp(mp);\n\tdouble ci[MAX_TERMS] = { 0 }, mi[MAX_TERMS] = { 0 };\n\tfor (int i = 0; i < MAX_TERMS; ++i) {\n\t\tci[i] = m_c[i](mp);\n\t\tmi[i] = m_m[i](mp);\n\t}\n\n\t// calculate the powers of eigenvalues\n\tdouble lamp[3][MAX_TERMS];\n\tfor (j=0; j<MAX_TERMS; ++j)\n\t{\n\t\tlamp[0][j] = pow(lam[0], mi[j]);\n\t\tlamp[1][j] = pow(lam[1], mi[j]);\n\t\tlamp[2][j] = pow(lam[2], mi[j]);\n\t}\n\t\n\t// principal stresses\n\tmat3ds s;\n\ts.zero();\n\tdouble T[3];\n\tfor (i=0; i<3; ++i) {\n\t\tT[i] = cp*(J-1);\n\t\tfor (j=0; j<MAX_TERMS; ++j)\n\t\t\tT[i] += ci[j]/mi[j]*(lamp[i][j] - 1)/J;\n\t\ts += N[i]*T[i];\n\t}\n\t\n\t// coefficients appearing in elasticity tensor\n\tdouble D[3][3],E[3][3];\n\tfor (j=0; j<3; ++j) {\n\t\tD[j][j] = cp;\n\t\tfor (k=0; k<MAX_TERMS; ++k)\n\t\t\tD[j][j] += ci[k]/mi[k]*((mi[k]-2)*lamp[j][k]+2)/J;\n\t\tfor (i=j+1; i<3; ++i) {\n\t\t\tD[i][j] = cp*(2*J-1);\n\t\t\tif (lam2[j] != lam2[i])\n\t\t\t\tE[i][j] = 2*(lam2[j]*T[i] - lam2[i]*T[j])/(lam2[i]-lam2[j]);\n\t\t\telse {\n\t\t\t\tE[i][j] = 2*cp*(1-J);\n\t\t\t\tfor (k=0; k<MAX_TERMS; ++k)\n\t\t\t\t\tE[i][j] += ci[k]/mi[k]*((mi[k]-2)*lamp[j][k]+2)/J;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// spatial elasticity tensor\n\ttens4ds c(0.0);\n\tmat3dd I(1.0);\n\tfor (j=0; j<3; ++j) {\n\t\tc += dyad1s(N[j])*D[j][j];\n\t\tfor (i=j+1; i<3; ++i) {\n\t\t\tc += dyad1s(N[i],N[j])*D[i][j];\n\t\t\tc += dyad4s(N[i],N[j])*E[i][j];\n\t\t}\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEOgdenUnconstrained::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// extract elastic material data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n    double lnJ = log(J);\n\t\n\t// get the left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\t\n\t// get the eigenvalues and eigenvectors of b\n\tdouble lam2[3];\t// these are the squares of the eigenvalues of V\n\tvec3d ev[3];\n\tEigenValues(b, lam2, ev, m_eps);\n\t\n\t// get the eigenvalues of V\n\tdouble lam[3];\n\tlam[0] = sqrt(lam2[0]);\n\tlam[1] = sqrt(lam2[1]);\n\tlam[2] = sqrt(lam2[2]);\n\t\n\t// evaluate coefficients at material points\n\tdouble cp = m_cp(mp);\n\tdouble ci[MAX_TERMS] = { 0 }, mi[MAX_TERMS] = { 0 };\n\tfor (int i = 0; i < MAX_TERMS; ++i) {\n\t\tci[i] = m_c[i](mp);\n\t\tmi[i] = m_m[i](mp);\n\t}\n\n\t// strain energy density\n    double sed = cp*(J-1)*(J-1)/2;\n    for (int j=0; j<MAX_TERMS; ++j)\n        sed += ci[j]/(mi[j]*mi[j])*\n        (pow(lam[0], mi[j]) + pow(lam[1], mi[j]) + pow(lam[2], mi[j])\n         - 3 - mi[j]*lnJ);\n\t\n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEOgdenUnconstrained.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\nclass FEOgdenUnconstrained : public FEElasticMaterial\n{\npublic:\n\tenum { MAX_TERMS = 6 };\npublic:\n\tFEOgdenUnconstrained(FEModel* pfem);\n\t\n\t//! calculate the stress\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate the tangent\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\t\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \nprotected:\n\tvoid EigenValues(mat3ds& A, double l[3], vec3d r[3], const double eps = 0);\n\tdouble\tm_eps;\n\t\npublic:\n\tFEParamDouble\tm_cp;\t\t\t\t//!< coefficient mu prime\n\tFEParamDouble\tm_c[MAX_TERMS];\t\t//!< coefficients mu\n\tFEParamDouble\tm_m[MAX_TERMS];\t\t//!< powers\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEOrthoElastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOrthoElastic.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEOrthoElastic, FEElasticMaterial)\n\tADD_PARAMETER(m_E1 , FE_RANGE_GREATER(0.0), \"E1\")->setLongName(\"E1 modulus\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_E2 , FE_RANGE_GREATER(0.0), \"E2\")->setLongName(\"E2 modulus\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_E3 , FE_RANGE_GREATER(0.0), \"E3\")->setLongName(\"E3 modulus\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G12, FE_RANGE_GREATER_OR_EQUAL(0.0), \"G12\")->setLongName(\"G12 shear modulus\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G23, FE_RANGE_GREATER_OR_EQUAL(0.0), \"G23\")->setLongName(\"G23 shear modulus\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G31, FE_RANGE_GREATER_OR_EQUAL(0.0), \"G31\")->setLongName(\"G31 shear modulus\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_v12, \"v12\");\n\tADD_PARAMETER(m_v23, \"v23\");\n\tADD_PARAMETER(m_v31, \"v31\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEOrthoElastic::FEOrthoElastic(FEModel* pfem) : FEElasticMaterial(pfem) {}\n\n//-----------------------------------------------------------------------------\n//! Material initialization.\nbool FEOrthoElastic::Validate()\n{\n\tif (FEElasticMaterial::Validate() == false) return false;\n\n\t// do some sanity checks\n\tif (m_v12.isConst() && m_E1.isConst() && m_E2.isConst())\n\t{\n\t\tdouble v12 = m_v12.constValue();\n\t\tdouble E1 = m_E1.constValue();\n\t\tdouble E2 = m_E2.constValue();\n\t\tif (v12 > sqrt(E1 / E2)) { feLogError(\"Invalid value for v12. Let v12 <= sqrt(E1/E2)\"); return false; }\n\t}\n\n\tif (m_v23.isConst() && m_E2.isConst() && m_E3.isConst())\n\t{\n\t\tdouble v23 = m_v23.constValue();\n\t\tdouble E2 = m_E2.constValue();\n\t\tdouble E3 = m_E3.constValue();\n\n\t\tif (v23 > sqrt(E2 / E3)) { feLogError(\"Invalid value for v23. Let v23 <= sqrt(E2/E3)\"); return false; }\n\t}\n\n\tif (m_v31.isConst() && m_E1.isConst() && m_E3.isConst())\n\t{\n\t\tdouble v31 = m_v31.constValue();\n\t\tdouble E1 = m_E1.constValue();\n\t\tdouble E3 = m_E3.constValue();\n\n\t\tif (v31 > sqrt(E3 / E1)) { feLogError(\"Invalid value for v31. Let v31 <= sqrt(E3/E1)\"); return false; }\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOrthoElastic::EvaluateLameCoefficients(FEMaterialPoint& mp, double lam[3][3], double mu[3])\n{\n\tdouble E1  = m_E1 (mp); double  E2 = m_E2 (mp); double E3  = m_E3 (mp);\n\tdouble v12 = m_v12(mp); double v23 = m_v23(mp); double v31 = m_v31(mp);\n\tdouble G12 = m_G12(mp); double G23 = m_G23(mp); double G31 = m_G31(mp);\n\n\t// Evaluate Lame coefficients\n\tmu[0] = G12 + G31 - G23;\n\tmu[1] = G12 - G31 + G23;\n\tmu[2] =-G12 + G31 + G23;\n\tlam[0][0] = 1.0/E1; lam[0][1] = -v12/E1; lam[0][2] = -v31/E3;\n\tlam[1][0] = -v12/E1; lam[1][1] = 1.0/E2; lam[1][2] = -v23/E2;\n\tlam[2][0] = -v31/E3; lam[2][1] = -v23/E2; lam[2][2] = 1.0/E3;\n\n\t// check that compliance matrix is positive definite\n\tmat3ds c(lam[0][0], lam[1][1], lam[2][2], lam[0][1], lam[1][2], lam[0][2]);\n\tdouble l[3];\n\tc.exact_eigen(l);\n\n\tif ((l[0] < 0) || (l[1] < 0) || (l[2] < 0)) {\n\t\tfeLogError(\"Stiffness matrix is not positive definite.\");\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\t// evaluate stiffness matrix and extract Lame constants\n\tc = c.inverse();\n\tlam[0][0] = c(0,0) - 2*mu[0];\n\tlam[1][1] = c(1,1) - 2*mu[1];\n\tlam[2][2] = c(2,2) - 2*mu[2];\n\tlam[1][2] = c(1,2); lam[2][1] = c(2,1);\n\tlam[2][0] = c(2,0); lam[0][2] = c(0,2);\n\tlam[0][1] = c(0,1); lam[1][0] = c(1,0);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the stress for a linear orthotropic material\nmat3ds FEOrthoElastic::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tvec3d a[3];\t\t\t// texture direction in current configuration\n\tmat3ds A[3];\t\t// texture tensor in current configuration\n\tdouble K[3];\t\t// Ka\n\tdouble L[3];\t\t// La\n\tmat3ds bmi;\t\t\t// B - I\n\t// Evaluate the deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// calculate left and right Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tmat3ds c = pt.RightCauchyGreen();\n\tmat3ds c2 = c.sqr();\n\tmat3dd I(1.);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tK[i] = a0[i]*(c*a0[i]);\n\t\tL[i] = a0[i]*(c2*a0[i]);\n\t\ta[i] = F*a0[i]/sqrt(K[i]);\t// Evaluate the texture direction in the current configuration\n\t\tA[i] = dyad(a[i]);\t\t\t// Evaluate the texture tensor in the current configuration\n\t}\n\n\t// evaluate the Lame coefficients\n\tdouble lam[3][3] = { 0 };\n\tdouble mu[3] = { 0 };\n\tEvaluateLameCoefficients(mp, lam, mu);\n\t\n\t// Evaluate the stress\n\tmat3ds s;\n\ts.zero();\t\t// Initialize for summation\n\tbmi = b - I;\n\tfor (i=0; i<3; i++) {\n\t\ts += (A[i]*bmi).sym()*(2.0*mu[i] * K[i]);\n\t\tfor (j=0; j<3; j++)\n\t\t\ts += lam[i][j]*((K[i]-1)*K[j]*A[j]+(K[j]-1)*K[i]*A[i])/2.;\n\t}\n\ts /= 2.0*J;\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEOrthoElastic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\tint i,j;\n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tvec3d a[3];\t\t\t// texture direction in current configuration\n\tmat3ds A[3];\t\t// texture tensor in current configuration\n\tdouble K[3];\t\t// Ka\n\t// Evaluate the strain and texture\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// calculate left and right Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tmat3ds c = pt.RightCauchyGreen();\n\tmat3dd I(1.);\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tK[i] = a0[i]*(c*a0[i]);\n\t\ta[i] = F*a0[i]/sqrt(K[i]);\t// Evaluate the texture direction in the current configuration\n\t\tA[i] = dyad(a[i]);\t\t\t// Evaluate the texture tensor in the current configuration\n\t}\n\t\n\t// evaluate the Lame coefficients\n\tdouble lam[3][3] = { 0 };\n\tdouble mu[3] = { 0 };\n\tEvaluateLameCoefficients(mp, lam, mu);\n\n\ttens4ds C(0.0);\n\tfor (i=0; i<3; i++) {\n\t\tC += mu[i]*K[i]*dyad4s(A[i],b);\n\t\tfor (j=0; j<3; j++)\n\t\t\tC += lam[i][j]*K[i]*K[j]*dyad1s(A[i],A[j])/2.;\n\t}\n\t\n\t// Elasticity tensor\n\tC /= J;\n\t\n\treturn C;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEOrthoElastic::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    mat3ds E = (pt.RightCauchyGreen() - mat3dd(1))/2;\n    mat3ds E2 = E.sqr();\n    \n\tvec3d a0[3];\t\t// texture direction in reference configuration\n\tmat3ds A0[3];\t\t// texture tensor in current configuration\n    double AE[3], AE2[3];\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (int i=0; i<3; i++) {\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0[i].x = Q[0][i]; a0[i].y = Q[1][i]; a0[i].z = Q[2][i];\n\t\tA0[i] = dyad(a0[i]);\t\t\t// Evaluate the texture tensor in the reference configuration\n        AE[i] = A0[i].dotdot(E);\n        AE2[i] = A0[i].dotdot(E2);\n\t}\n    \n\t// evaluate the Lame coefficients\n\tdouble lam[3][3] = { 0 };\n\tdouble mu[3] = { 0 };\n\tEvaluateLameCoefficients(mp, lam, mu);\n\n\t// calculate strain energy\n    double sed = mu[0]*AE2[0] + mu[1]*AE2[1] + mu[2]*AE2[2]\n    +0.5*(lam[0][0]*AE[0]*AE[0]+lam[1][1]*AE[1]*AE[1]+lam[2][2]*AE[2]*AE[2])\n    +lam[0][1]*AE[0]*AE[1]+lam[1][2]*AE[1]*AE[2]+lam[2][0]*AE[2]*AE[0];\n    \n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEOrthoElastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a version of orthotropic elasticity that remains\n// valid for large deformations\nclass FEOrthoElastic :\tpublic FEElasticMaterial\n{\npublic:\n\tFEParamDouble\tm_E1, m_E2, m_E3;\t\t// Young's moduli\n\tFEParamDouble\tm_v12, m_v23, m_v31;\t// Poisson's ratio\n\tFEParamDouble\tm_G12, m_G23, m_G31;\t// Shear moduli\n\npublic:\n\tFEOrthoElastic(FEModel* pfem);\n\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t//! data initialization\n\tbool Validate() override;\n\nprivate:\n\t// Helper function for evaluating Lame coefficients at material point\n\tbool EvaluateLameCoefficients(FEMaterialPoint& pt, double lam[3][3], double mu[3]);\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEOrthotropicCLE.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOrthotropicCLE.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEOrthotropicCLE, FEElasticMaterial)\n\tADD_PARAMETER(lp11, \"lp11\")->setLongName(\"lambda_11 tensile\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(lm11, \"lm11\")->setLongName(\"lambda_11 compressive\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(lp22, \"lp22\")->setLongName(\"lambda_22 tensile\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(lm22, \"lm22\")->setLongName(\"lambda_22 compressive\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(lp33, \"lp33\")->setLongName(\"lambda_33 tensile\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(lm33, \"lm33\")->setLongName(\"lambda_33 compressive\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(l12, \"l12\")->setLongName(\"lambda_12\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(l23, \"l23\")->setLongName(\"lambda_23\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(l31, \"l31\")->setLongName(\"lambda_13\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(mu1, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu1\")->setLongName(\"mu_1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(mu2, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu2\")->setLongName(\"mu_2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(mu3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu3\")->setLongName(\"mu_3\")->setUnits(UNIT_PRESSURE);\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Check material parameters.\nbool FEOrthotropicCLE::Validate()\n{\n    if (FEElasticMaterial::Validate() == false) return false;\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = mu1;\n    mu[1] = mu2;\n    mu[2] = mu3;\n    lam[0][0] = lm11; lam[0][1] = l12 ; lam[0][2] = l31;\n    lam[1][0] = l12 ; lam[1][1] = lm22; lam[1][2] = l23;\n    lam[2][0] = l31 ; lam[2][1] = l23 ; lam[2][2] = lm33;\n    \n    // check that stiffness matrix is positive definite\n    mat3ds c(lam[0][0]+2*mu[0],lam[1][1]+2*mu[1],lam[2][2]+2*mu[2],lam[0][1],lam[1][2],lam[0][2]);\n    double l[3];\n    c.exact_eigen(l);\n    \n\tif ((l[0] < 0) || (l[1] < 0) || (l[2] < 0)) {\n\t\tfeLogError(\"Stiffness matrix is not positive definite.\");\n\t\treturn false;\n\t}\n    \n    // repeat check with all tensile diagonal first lamé constants\n    lam[0][0] = lp11; lam[1][1] = lp22; lam[2][2] = lp33;\n    // check that compliance matrix is positive definite\n    c = mat3ds(lam[0][0]+2*mu[0],lam[1][1]+2*mu[1],lam[2][2]+2*mu[2],lam[0][1],lam[1][2],lam[0][2]);\n    c.exact_eigen(l);\n    \n\tif ((l[0] < 0) || (l[1] < 0) || (l[2] < 0)) {\n\t\tfeLogError(\"Stiffness matrix is not positive definite.\");\n\t\treturn false;\n\t}\n \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the stress\nmat3ds FEOrthotropicCLE::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = mu1;\n    mu[1] = mu2;\n    mu[2] = mu3;\n    lam[0][0] = lm11; lam[0][1] = l12 ; lam[0][2] = l31;\n    lam[1][0] = l12 ; lam[1][1] = lm22; lam[1][2] = l23;\n    lam[2][0] = l31 ; lam[2][1] = l23 ; lam[2][2] = lm33;\n    \n    int i,j;\n    vec3d a0;           // texture direction in reference configuration\n    mat3ds A0[3];        // texture tensor in reference configuration\n    double AE[3];        // E:A\n    mat3ds E = pt.Strain();\n\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n\n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n    for (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n        // Copy the texture direction in the reference configuration to a0\n        a0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n        A0[i] = dyad(a0);\n        AE[i] = a0*(E*a0);\n    }\n    \n    lam[0][0] = (AE[0] >= 0) ? lp11 : lm11;\n    lam[1][1] = (AE[1] >= 0) ? lp22 : lm22;\n    lam[2][2] = (AE[2] >= 0) ? lp33 : lm33;\n    \n\tmat3ds s; \n\ts.zero();\n    \n    for (i=0; i<3; ++i) {\n        s += (A0[i]*E).sym()*(mu[i]);\n        for (j=0; j<3; ++j) {\n            s += A0[j]*(lam[i][j]*AE[i]);\n        }\n    }\n    s = (F*s*F.transpose()).sym()/J;\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the elasticity tensor\ntens4ds FEOrthotropicCLE::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = mu1;\n    mu[1] = mu2;\n    mu[2] = mu3;\n    lam[0][0] = lm11; lam[0][1] = l12 ; lam[0][2] = l31;\n    lam[1][0] = l12 ; lam[1][1] = lm22; lam[1][2] = l23;\n    lam[2][0] = l31 ; lam[2][1] = l23 ; lam[2][2] = lm33;\n    \n    int i,j;\n    vec3d a0;           // texture direction in reference configuration\n    mat3ds A[3];\t\t// texture tensor in current configuration\n    double AE[3];       // E:A\n    mat3ds E = pt.Strain();\n\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    \n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n    for (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n        // Copy the texture direction in the reference configuration to a0\n        a0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n        A[i] = dyad(F*a0);\n        AE[i] = a0*(E*a0);\n    }\n    \n    lam[0][0] = (AE[0] >= 0) ? lp11 : lm11;\n    lam[1][1] = (AE[1] >= 0) ? lp22 : lm22;\n    lam[2][2] = (AE[2] >= 0) ? lp33 : lm33;\n    \n    tens4ds c;\n    c.zero();\n    \n    mat3ds B = pt.LeftCauchyGreen();\n    for (i=0; i<3; ++i) {\n        c += dyad4s(A[i], B)*mu[i];\n        for (j=0; j<3; ++j) {\n            c += dyad1s(A[i],A[j])*lam[i][j]/2;\n        }\n    }\n    c /= J;\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the strain energy density\ndouble FEOrthotropicCLE::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // Evaluate Lame coefficients\n    double\tlam[3][3];\n    double\tmu[3];\n    mu[0] = mu1;\n    mu[1] = mu2;\n    mu[2] = mu3;\n    lam[0][0] = lm11; lam[0][1] = l12 ; lam[0][2] = l31;\n    lam[1][0] = l12 ; lam[1][1] = lm22; lam[1][2] = l23;\n    lam[2][0] = l31 ; lam[2][1] = l23 ; lam[2][2] = lm33;\n    \n    int i,j;\n    vec3d a0;           // texture direction in reference configuration\n    mat3ds A0;          // texture tensor in reference configuration\n    double AE[3], AE2[3];    // Ka\n    mat3ds E = pt.Strain();\n\n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n    for (i=0; i<3; i++) {\t// Perform sum over all three texture directions\n        // Copy the texture direction in the reference configuration to a0\n        a0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n        A0 = dyad(a0);\n        AE[i] = a0*(E*a0);\n        AE2[i] = a0*(E*E*a0);\n    }\n    \n    lam[0][0] = (AE[0] >= 0) ? lp11 : lm11;\n    lam[1][1] = (AE[1] >= 0) ? lp22 : lm22;\n    lam[2][2] = (AE[2] >= 0) ? lp33 : lm33;\n    \n    double W = 0;\n    \n    for (i=0; i<3; ++i) {\n        W += AE2[i]*mu[i];\n        for (j=0; j<3; ++j) {\n            W += AE[i]*AE[j]*lam[i][j]/2;\n        }\n    }\n    \n    return W;\n}\n"
  },
  {
    "path": "FEBioMech/FEOrthotropicCLE.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements an orthotropic conewise linear elastic (CLE) material (Curnier et al. 1995 J Elasticity).\nclass FEOrthotropicCLE :\tpublic FEElasticMaterial\n{\npublic:\n    double\tlp11, lp22, lp33;\t// diagonal first lamé constants (tension)\n    double\tlm11, lm22, lm33;\t// diagonal first lamé constants (compression)\n    double\tl12, l23, l31;\t// off-diagonal first lamé constants\n    double\tmu1, mu2, mu3;\t// shear moduli\n    \npublic:\n    FEOrthotropicCLE(FEModel* pfem) : FEElasticMaterial(pfem) {}\n    \n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! data initialization\n    bool Validate() override;\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEOsmoticVirialExpansion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOsmoticVirialExpansion.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEOsmoticVirialExpansion, FEElasticMaterial)\n    ADD_PARAMETER(m_phiwr, FE_RANGE_CLOSED(0.0, 1.0), \"phiw0\");\n    ADD_PARAMETER(m_cr   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"cr\");\n    ADD_PARAMETER(m_c1, \"c1\");\n    ADD_PARAMETER(m_c2, \"c2\");\n    ADD_PARAMETER(m_c3, \"c3\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEOsmoticVirialExpansion::FEOsmoticVirialExpansion(FEModel* pfem) : FEElasticMaterial(pfem)\n{ \n\tm_c1 = m_c2 = m_c3 = 0; \n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEOsmoticVirialExpansion::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // jacobian\n    double J = pt.m_J;\n    \n    // calculate concentration in current configuration\n    double c = m_phiwr*m_cr/(J-1+m_phiwr);\n    \n    // calculate osmotic pressure\n    double p = m_c1*c + m_c2*c*c + m_c3*pow(c,3);\n    \n    // calculate T = -p*I\n    mat3dd I(1.0);\t// identity tensor\n    mat3ds s = -p*I;\n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEOsmoticVirialExpansion::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // jacobian\n    double J = pt.m_J;\n    \n    // calculate concentration in current configuration\n    double c = m_phiwr*m_cr/(J-1+m_phiwr);\n    \n    // calculate osmotic pressure\n    double p = m_c1*c + m_c2*c*c + m_c3*pow(c,3);\n    \n    // calculate derivative of osmotic pressure w.r.t. J\n    double dpdJ = -(m_c1 + 2*m_c2*c + 3*m_c3*c*c)*c/(J -1 + m_phiwr);\n    \n    mat3dd I(1.0);\t// Identity\n    \n    tens4ds IxI = dyad1s(I);\n    tens4ds I4  = dyad4s(I);\n    \n    // calculate tangent osmotic modulus\n    tens4ds C = IxI*(-J*dpdJ) + (2.0*I4 - IxI)*p;\n    \n    return C;\n}\n"
  },
  {
    "path": "FEBioMech/FEOsmoticVirialExpansion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class that implements osmotic pressure using a virial expansion.\n//\nclass FEOsmoticVirialExpansion : public FEElasticMaterial\n{\npublic:\n\tFEOsmoticVirialExpansion(FEModel* pfem);\n    \n    //! Returns the Cauchy stress\n    mat3ds Stress(FEMaterialPoint& mp) override;\n    \n    //! Returs the spatial tangent\n    tens4ds Tangent(FEMaterialPoint& mp) override;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n    \npublic:\n    double\tm_phiwr;\t//!< fluid volume fraction in reference configuration\n    double\tm_cr;\t\t//!< concentration in reference configuration\n    double  m_c1;       //!< first virial coefficient\n    double  m_c2;       //!< second virial coefficient\n    double  m_c3;       //!< third virial coefficient\n};\n"
  },
  {
    "path": "FEBioMech/FEPRLig.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPRLig.h\"\n\n// Define the material parameters\nBEGIN_FECORE_CLASS(FEPRLig, FEElasticMaterial)\n\tADD_PARAMETER(m_c1, \"c1\");\n\tADD_PARAMETER(m_c2, \"c2\");\n\tADD_PARAMETER(m_u,  \"mu\");\n\tADD_PARAMETER(m_v0, \"v0\");\n\tADD_PARAMETER(m_m,  \"m\");\n\tADD_PARAMETER(m_k, \"k\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\nextern tens4ds material_to_spatial(tens4ds& C, mat3d& F);\n\n//////////////////////////////////////////////////////////////////////\n//\t\t\t\t\t\t\t\tPRLig\t\t\t\t\t\t\n//////////////////////////////////////////////////////////////////////\n\nFEPRLig::FEPRLig(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n}\n\n\t\t\t\t\t\t\t///////////////////////////\n//////////////////////////////Cauchy Stress Calculation ////////////////////////////////////////////////\n\t\t\t\t\t\t\t////////////////////////////\n\nmat3ds FEPRLig::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// obtain the deformation tensor F\n\tmat3d &F = pt.m_F;\n\n\t// calculate left and right Cauchy-Green tensors and their squares\n\tmat3ds b  =  pt.LeftCauchyGreen();\n\tmat3ds c  =  pt.RightCauchyGreen();\t\t\t\t\n\tmat3ds b2 =  b.sqr();\n\tmat3ds c2 =  c.sqr();\n\n\t// Define the 2nd order identity tensor \n\tmat3dd I(1);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// declare initial material direction vector\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch ; \n\tdouble lam = a.unit();\n\n\n\n//////////////////////////Strain invariants and Derivatives///////////////////////////////////////////////\n\t\n\t// define scalar strain invariants i1:15\n\tdouble i1 =  c.tr();\n\tdouble i2 =  0.5*(i1*i1 - c2.tr());\n\tdouble i3 =  c.det(); \n\tdouble i4 =\t a0*(c*a0);\n\tdouble i5 =  a0*(c2*a0);\n\n\t\n\t// define common terms in the strain energy derivatives\n\tdouble rooti4 = sqrt(i4);\n\tdouble CT1 = i2 - i1*i4 + i5;\n\tdouble CT2 = -2*(m_m-m_v0);\n\tdouble CT3 = -1 + rooti4;\n\tdouble CT4 = exp(4*CT3*m_m);\n\tdouble CT4inv= 1/CT4;\n\tdouble CT5= pow(i4,-2*(m_m - m_v0));\n\tdouble CT5inv= 1/CT5;\n\tdouble CT6 = m_k*log(CT4*CT5*CT1)/CT1;\n\tdouble CTe = exp(m_c2*CT3*CT3);\n\tdouble CT8= m_k*(-CT4*i1*CT5 + 2*CT4*(1/rooti4)*CT5*CT1*m_m - 2*CT4*(1/i4)*CT5*CT1*(m_m - m_v0));\n\t\n\n\t// calculate the strain energy first derivatives with respect to each of the scalar invariants\n\tdouble w1 =  ((m_u)/2) - (i4*CT6); \n\n\tdouble w2 =  CT6;\n\n\tdouble w3 = (-2*m_u)/(4*i3);\n\t\n\tdouble w4 =  m_c1*CTe*(CT3)/(2*rooti4) + (1/CT1)*CT4inv*CT5inv*CT8*CT6*CT1*(1/m_k);\n\n\tdouble w5 =  CT6;\n\n\t\n\t// calculate dyadic tensor terms found in stress calculation \n\n\t// calculate a_i*a_j\n\tmat3ds aa = dyad(a);\n\t// calculate a_i*b_jk*a_k + b_ik*a_k*aj\n\tvec3d ba = b*a;\n\tmat3ds aobas = dyads(a, ba);\n\t// calculate J = sqrt(i3)\n\tdouble J = sqrt(i3);\n\n\t// calculate cauchy stress\n\tmat3ds s = 2/J*(i3*w3*I + (w1 + i1*w2)*b - w2*b2 + i4*w4*aa + i4*w5*aobas);\n\t\n//  Stopping point debugging;  comment out when not testing\n//\tdouble WPR = 0;  \n\n\t// return stress\n\treturn s;\n}\t\t\t\n\n\n\n\t\t\t\t\t\t\t\t\t//////////////////////////////\n////////////////////////////////////Elasticity Tensor Calculation//////////////////////////////////////////////////////////////////////////\n\t\t\t\t\t\t\t\t\t///////////////////////////////\n\ntens4ds FEPRLig::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// obtain the deformation tensor F\n\tmat3d &F = pt.m_F;\n\n\n\t// calculate left and right Cauchy-Green tensors, and their squares\n\tmat3ds b  =  pt.LeftCauchyGreen();\n\tmat3ds c  =  pt.RightCauchyGreen();\t\t\t\t\n\tmat3ds b2 =  b.sqr();\n\tmat3ds c2 =  c.sqr();\n\n\t// define the 2nd order identity tensor \n\tmat3dd I(1);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// declare initial material direction vectors\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch ; \n\tdouble lam = a.unit();\n\n\t\n\t//////////////////////////////////Strain invariants and Derivatives///////////////////////////////////////\n\n\t// define scalar strain invariants i1:i5\n\tdouble i1 =  c.tr();\n\tdouble i2 =  0.5*(i1*i1 - c2.tr());\n\tdouble i3 =  c.det();\n\tdouble i4 =\t a0*(c*a0);\n\tdouble i5 =  a0*(c2*a0);\n\n\t// define common terms in the strain energy derivatives\n\tdouble rooti4 = sqrt(i4);\n\tdouble CT1 = i2 - i1*i4 + i5;\n\tdouble CT1_2= CT1*CT1;\n\tdouble CT2 = -2*(m_m-m_v0);\n\tdouble CT3 = -1 + rooti4;\n\tdouble CT4 = exp(4*CT3*m_m);\n\tdouble CT4inv= 1/CT4;\n\tdouble CT5= pow(i4,-2*(m_m - m_v0));\n\tdouble CT5inv= 1/CT5;\n\tdouble CT6 = m_k*log(CT4*CT5*CT1)/CT1;\n\tdouble CTe = exp(m_c2*CT3*CT3);\n\tdouble i4_2= i4*i4;\n\tdouble CT7= CT6*1/CT1;\n\tdouble CT8= m_k*(-CT4*i1*CT5 + 2*CT4*(1/rooti4)*CT5*CT1*m_m - 2*CT4*(1/i4)*CT5*CT1*(m_m - m_v0));\n\tdouble CT9 = exp(m_c2*CT3*CT3);\n\n\t// calculate the strain energy first derivatives with respect to each of the scalar invariants\n\tdouble w1 =  ((m_u)/2) - (i4*CT6); \n\n\tdouble w2 =  CT6;\n\n\tdouble w3 = (-2*m_u)/(4*i3);\n\t\n\tdouble w4 =  m_c1*CTe*(CT3)/(2*rooti4) + (1/CT1)*CT4inv*CT5inv*CT8*CT6*CT1*(1/m_k);\n\n\tdouble w5 =  CT6;\n\n\t// calculate the second strain strain energy derivatives with respect to each of the scalar invariants\n\tdouble w11 = i4_2*m_k/CT1_2 - i4_2*CT7;\n\n\tdouble w12 = -i4*m_k/CT1_2 + i4*CT7;\n\n\tdouble w13 = 0;\n\n\tdouble w14 = -(1/CT1_2)*(1/CT4)*i4*(1/CT5)*CT8- i1*i4*CT7 - CT6;\n\n\tdouble w15 = w12;\n\n\tdouble w22 = m_k/CT1_2 - CT7;\n\n\tdouble w23 = 0;\n\t\n\tdouble w24 = 1/CT1_2*CT4inv*CT5inv*CT8 + i1*CT7;\n\n\tdouble w25 = w22;\n\n\tdouble w33 = (2*m_u)/(4*i3*i3);\n\n\tdouble w34 = 0;\n\n\tdouble w35 = 0;\n\n\tdouble w44 = (1/(4*i4_2*CT1_2))*(m_c1*CT9*rooti4*(1 + 2*m_c2*(rooti4 - 2*i4 + rooti4*rooti4*rooti4))*CT1_2 + 4*m_k*pow((-2*(i2 + i5)\n\t\t\t\t\t*(CT3*m_m + m_v0)+i1*i4*(1 + 2*CT3*m_m + 2*m_v0)),2)- 4*m_k*(-2*i1*i4*(i2 + i5)*((-2 + rooti4)*m_m + 2*m_v0)\n\t\t\t\t\t+ (i2 + i5)*(i2 + i5)*((-2+ rooti4)*m_m + 2*m_v0) + i1*i1*i4_2*(1 + (-2+ rooti4)*m_m + 2*m_v0))\n\t\t\t\t\t*log(CT4*CT5*CT1));\n\t \n\n\tdouble w45 = 1/(CT1_2)*CT4inv*CT5inv*CT8 + 1/CT1*CT4inv*CT5inv*m_k*(2*CT4*(1/rooti4)*CT5*m_m - 2*CT4*(1/i4)*CT5*(m_m - m_v0))\n\t\t\t\t*CT6*CT1*(1/m_k) - 1/(CT1_2)*CT4inv*CT5inv*CT8*CT6*CT1*(1/m_k);\n\t\n\tdouble w55 = w22;\n\n\t//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\t\n\t// calculate the second order dyadic terms in the elasticity tensor calculation\n\tmat3ds  a02   = dyad(a0); \n\tvec3d\tctimesa0 = c*a0;\n\tmat3ds  di5dc = dyads(a0, ctimesa0);\n\n\tmat3ds cinv=c.inverse();\n\n\tmat3ds  aa        = dyad(a);\n\tvec3d   btimesa   = b*a;\n\tmat3ds  pf_didc   = dyads(btimesa,a);\n\n\t// calculate the fourth order dyadic terms in the elasticity tensor calculation\n\ttens4ds bb\t      = dyad1s(b);\n\ttens4ds bb2       = dyad1s(b, b2);\n\ttens4ds b2b2      = dyad1s(b2);\n\ttens4ds pfi4      = dyad4s(b);    \n\ttens4ds bI        = dyad1s(b,I);\n\ttens4ds b2I       = dyad1s(b2,I);\n\ttens4ds II        = dyad1s(I);  \n\ttens4ds pf_dcindc = dyad4s(I); \n\ttens4ds ba        = dyad1s(b,aa);\n\ttens4ds b2a       = dyad1s(b2,aa);\n\ttens4ds Ia\t\t  = dyad1s(I,aa);\n\ttens4ds b_pfdi\t  = dyad1s(pf_didc,b);\n\ttens4ds b2_pfdi   = dyad1s(pf_didc, b2);\n\ttens4ds I_pfdi    = dyad1s(pf_didc, I);\n\ttens4ds aa_pfdi   = dyad1s(pf_didc,aa);\n\ttens4ds a4        = dyad1s(aa);\n\ttens4ds pfdi_pfdi = dyad1s(pf_didc);\n\ttens4ds pf_ddi5dc2= dyad4s(aa,b); \n\n\n// Calculate Spatial Elasticity tensor\n  tens4ds D = 4/sqrt(i3)*((w11 +2*i1*w12 + w2 + i1*i1*w22)*bb\n\t\t\t\t\t-(w12+i1*w22)*(bb2)\n\t\t\t\t\t+ w22*b2b2 - w2*pfi4\n\t\t\t\t\t+ (i3*w13 + i1*i3*w23)*(bI)\n\t\t\t\t\t- i3*w23*(b2I)\n\t\t\t\t\t+ (i3*w3 + i3*i3*w33)*(II)\n\t\t\t\t\t- w3*i3*(pf_dcindc)\n\t\t\t\t\t+ (i4*w14+i1*i4*w24)*(ba)\n\t\t\t\t\t- i4*w24*(b2a)\n\t\t\t\t\t+ i3*i4*w34*(Ia)\n\t\t\t\t\t+ (i4*w15 + i1*i4*w25)*(b_pfdi)\n\t\t\t\t\t- i4*w25*(b2_pfdi)\n\t\t\t\t\t+ i3*i4*w35*(I_pfdi)\n\t\t\t\t\t+ w45*i4*i4*(aa_pfdi)\n\t\t\t\t\t+ w44*i4*i4*(a4)\n\t\t\t\t\t+ i4*i4*w55*(pfdi_pfdi) + i4*w5*(pf_ddi5dc2)\n\t\t\t\t\t); \n\t\t\t\t\t\n\n\n  //Stopping point for stepping thru and checking stress calculation;  comment out when not testing\n\t// double PPP=1;\n\n\n/////////////////alternate form of elasticity tensor in C^2 basis instead of Cinverse///////////////////////\n/* \nmat3ds b3= b2*b;\n\ntens4ds bb3\t\t=dyad1s(b,b3);\ntens4ds b2b3\t=dyad1s(b2,b3);\ntens4ds b3_pfdi\t=dyad1s(pf_didc,b3);\ntens4ds b3a\t\t=dyad1s(b3,aa);\ntens4ds b3b3 = dyad1s(b3);\ntens4ds pfdc2dc = dyad4s(b2,b); \n\ntens4ds Dc2 =  (4/sqrt(i3))*((w11 +w2 + 2*i1*w12 + 2*i2*w13 + 2*i1*i2*w23 + i1*i1*w22 + i2*i2*w33 + i1*w3)*bb\n\t-(w3 + w12 + i1*w13 + i2*w23 + i1*w22 + i1*i2*w33 + i1*i1*w23)*bb2\n\t+(w22 + 2*i1*w23 + i1*i1*w33)*b2b2\n\t- (w2 + i1*w3)*pfi4\n\t+ (w13 + i1*w23 + i2*w33)*bb3\n\t- (w23+i1*w33)*b2b3\n\t+ w33*b3b3 + w3*pfdc2dc\n\t+ (i4*w14 + i1*i4*w24 + i2*i4*w34)*ba\n\t- (i4*w24 + i1*i4*w34)*b2a\n\t+ i4*w34*b3a\n\t+ (i4*w15 + i1*i4*w25 + i2*i4*w35)*b_pfdi\n\t- (i4*w25 + i1*i4*w35)*b2_pfdi\n\t+ i4*i4*w44*a4\n\t+ i4*w35*b3_pfdi\n\t+ i4*i4*w45*aa_pfdi\n\t+ i4*i4*w55*(pfdi_pfdi) + i4*w5*(pf_ddi5dc2));\n\n\n double PPP2=0;\n */\n\treturn D;\n}\n\n//! calculate strain energy density at material point\ndouble FEPRLig::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    double sed = 0;\n\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    // obtain the deformation tensor F\n    mat3d &F = mp.m_F;\n    \n    // calculate left and right Cauchy-Green tensors and their squares\n    mat3ds c  =  mp.RightCauchyGreen();\n    mat3ds c2 =  c.sqr();\n    \n    // Define the 2nd order identity tensor\n    mat3dd I(1);\n    \n    // get the local coordinate systems\n    mat3d Q = GetLocalCS(pt);\n    \n    // declare initial material direction vector\n    vec3d a0 = Q.col(0);\n    \n    // calculate the current material axis lam*a = F*a0;\n    vec3d a = F*a0;\n    \n    // normalize material axis and store fiber stretch ;\n    double lam = a.unit();\n    \n    //////////////////////////Strain invariants ///////////////////////////////////////////////\n    \n    // define scalar strain invariants i1:15\n    double i1 =  c.tr();\n    double i2 =  0.5*(i1*i1 - c2.tr());\n    double i3 =  c.det();\n    double i4 =     a0*(c*a0);\n    double i5 =  a0*(c2*a0);\n\n    double Wfiber = 0.5*m_c1/m_c2*(exp(m_c2*pow(lam-1,2))-1);\n    double Wmatrix = m_u/2*(i1-3) - m_u*log(sqrt(i3));\n    double Wvol = m_k/2*pow(log((i5-i1*i4+i2)/(pow(i4, 2*(m_m-m_v0)*exp(-4*m_m*(lam-1))))),2);\n    \n    sed = Wfiber + Wmatrix + Wvol;\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEPRLig.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n\nclass FEPRLig : public FEElasticMaterial\n{\npublic:\n\tFEPRLig(FEModel* pfem);\n\npublic:\n\t\n\tdouble\tm_c1;\t //!< fiber constant c1\n\tdouble\tm_c2;\t //!< fiber constant c2 \n\tdouble  m_u;     //!< Lame Coefficient mu of the matrix\n\tdouble\tm_m;\t //!< Poisson's ratio slope \n\tdouble\tm_v0;\t //!< initial Poisson's ratio\n\tdouble\tm_k;\t //!< Penalty for the volumetric strain energy\n\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPerfectOsmometer.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPerfectOsmometer.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPerfectOsmometer, FEElasticMaterial)\n\tADD_PARAMETER(m_phiwr, FE_RANGE_CLOSED(0.0, 1.0), \"phiw0\");\n\tADD_PARAMETER(m_iosm , FE_RANGE_GREATER_OR_EQUAL(0.0), \"iosm\")->setUnits(\"n/L^3\");\n\tADD_PARAMETER(m_bosm , FE_RANGE_GREATER_OR_EQUAL(0.0), \"bosm\")->setUnits(\"n/L^3\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPerfectOsmometer::FEPerfectOsmometer(FEModel* pfem) : FEElasticMaterial(pfem)\n{ \n\tm_Rgas = 0; \n\tm_Tabs = 0; \n}\n\n//-----------------------------------------------------------------------------\n\nbool FEPerfectOsmometer::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\t\n\tm_Rgas = GetGlobalConstant(\"R\");\n\tm_Tabs = GetGlobalConstant(\"T\");\n\t\n\tif (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");\t return false; }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPerfectOsmometer::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\t\n\t// calculate internal concentration in current configuration\n\tdouble iosm = m_iosm*m_phiwr/(J-1+m_phiwr);\n\t\n\t// calculate osmotic pressure\n\tdouble p = m_Rgas*m_Tabs*(iosm - m_bosm);\n\t\n\t// calculate T = -p*I\n\tmat3dd I(1.0);\t// identity tensor\n\tmat3ds s = -p*I;\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPerfectOsmometer::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// jacobian\n\tdouble J = pt.m_J;\n\n\t// calculate internal osmolarity in current configuration\n\tdouble iosm = m_iosm*m_phiwr/(J-1+m_phiwr);\n\t\n\t// calculate osmotic pressure\n\tdouble p = m_Rgas*m_Tabs*(iosm - m_bosm);\n\t\n\t// calculate derivative of osmotic pressure w.r.t. J\n\tdouble dp = -m_Rgas*m_Tabs*iosm/(J-1+m_phiwr);\n\t\n\tmat3dd I(1.0);\t// Identity\n\t\n\ttens4ds I1 = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\t\n\t// calculate tangent osmotic modulus\n\ttens4ds c = -J*dp*I1 + p*(2.0*I4 - I1);\n\treturn c;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEPerfectOsmometer.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class that implements the equilibrium of a perfect osmometer.\n//! When used on its own (not in a solid mixture), this materials\n//! is intrinsically unstable\nclass FEPerfectOsmometer : public FEElasticMaterial\n{\npublic:\n\t// constructor\n\tFEPerfectOsmometer(FEModel* pfem);\n\t\n\t//! Initialization routine\n\tbool Init() override;\n\n\t//! Returns the Cauchy stress\n\tvirtual mat3ds Stress(FEMaterialPoint& mp) override;\n\n\t//! Returs the spatial tangent\n\tvirtual tens4ds Tangent(FEMaterialPoint& mp) override;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\t\npublic:\n\tdouble\tm_phiwr;\t//!< fluid volume fraction in reference configuration\n\tdouble\tm_iosm;\t\t//!< internal osmolarity in reference configuration\n\tdouble\tm_Rgas;\t\t//!< universal gas constant\n\tdouble\tm_Tabs;\t\t//!< absolute temperature\n\tdouble\tm_bosm;\t\t//!< bath osmolarity\n};\n"
  },
  {
    "path": "FEBioMech/FEPeriodicBoundary.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPeriodicBoundary.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FENormalProjection.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\nFEPeriodicSurface::Data::Data()\n{\n\tm_gap = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n\tm_Lm = vec3d(0, 0, 0);\n\tm_Tn = vec3d(0, 0, 0);\n\tm_Fr = vec3d(0, 0, 0);\n}\n\nvoid FEPeriodicSurface::Data::Init()\n{\n\tFEContactMaterialPoint::Init();\n\n\tm_gap = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n\tm_Lm = vec3d(0, 0, 0);\n\tm_Tn = vec3d(0, 0, 0);\n\tm_Fr = vec3d(0, 0, 0);\n}\n\nvoid FEPeriodicSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_gap;\n\tar & m_rs;\n\tar & m_Lm;\n\tar & m_Tn;\n\tar & m_Fr;\n}\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEPeriodicBoundary, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\");\n\tADD_PARAMETER(m_eps      , \"penalty\"  );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\" );\n\tADD_PARAMETER(m_off      , \"offset\"   );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Creates a surface for use with a sliding interface. All surface data\n//! structures are allocated.\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FEPeriodicSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEContactSurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate other surface data\n\tm_data.resize(nn);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurface::CopyFrom(FEPeriodicSurface& s)\n{\n\tm_Node = s.m_Node;\n\tint NE = s.Elements();\n\tCreate(NE);\n\tfor (int i=0; i<NE; ++i) Element(i) = s.Element(i);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the center of mass for this surface\n//!\nvec3d FEPeriodicSurface::CenterOfMass()\n{\n\tvec3d c(0,0,0);\n\tint N = Nodes();\n\tfor (int i=0; i<N; ++i) c += Node(i).m_r0;\n\tif (N != 0) c /= (double) N;\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurface::Serialize(DumpStream& ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tar & m_data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ne = el.Nodes();\n    pt = vec3d(0,0,0);\n    for (int k=0; k<ne; ++k) pt += m_data[el.m_lnode[k]].m_Tn;\n    pt /= ne;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurface::GetNodalContactPressure(int nface, double* pg)\n{\n\tFESurfaceElement& el = Element(nface);\n\tint ne = el.Nodes();\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\tvec3d tn = m_data[el.m_lnode[i]].m_Tn;\n\t\tpg[i] = tn.norm();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurface::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& el = Element(nface);\n\tint ne = el.Nodes();\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\ttn[i] = m_data[el.m_lnode[i]].m_Tn;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// FEPeriodicBoundary\n//-----------------------------------------------------------------------------\n\nFEPeriodicBoundary::FEPeriodicBoundary(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_stol = 0.01;\n\tm_atol = 0;\n\tm_eps = 0;\n\tm_btwo_pass = false;\n\tm_off = vec3d(0,0,0);\n\tm_naugmin = 0;\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicBoundary::Init()\n{\n\t// create the surfaces\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms, false);\n\tProjectSurface(m_ms, m_ss, false);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary::CopyFrom(FESurfacePairConstraint* pci)\n{\n\t// cast to a periodic boundary\n\tFEPeriodicBoundary& pb = dynamic_cast<FEPeriodicBoundary&>(*pci);\n\n\t// copy parameters\n\tGetParameterList() = pb.GetParameterList();\n\n\t// copy nodes\n\tm_ss.CopyFrom(pb.m_ss);\n\tm_ms.CopyFrom(pb.m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\n// TODO: what if two_pass ??\nvoid FEPeriodicBoundary::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6*5);\n\n\tfor (int j=0; j<m_ss.Nodes(); ++j)\n\t{\n\t\tFESurfaceElement& me = *m_ss.m_data[j].m_pme;\n\t\tint* en = &me.m_node[0];\n\n\t\tint n = me.Nodes();\n\t\tif (n == 3)\n\t\t{\n\t\t\tlm[6*(3+1)  ] = -1;\n\t\t\tlm[6*(3+1)+1] = -1;\n\t\t\tlm[6*(3+1)+2] = -1;\n\t\t\tlm[6*(3+1)+3] = -1;\n\t\t\tlm[6*(3+1)+4] = -1;\n\t\t\tlm[6*(3+1)+5] = -1;\n\t\t}\n\n\t\tlm[0] = m_ss.Node(j).m_ID[dof_X];\n\t\tlm[1] = m_ss.Node(j).m_ID[dof_Y];\n\t\tlm[2] = m_ss.Node(j).m_ID[dof_Z];\n\t\tlm[3] = m_ss.Node(j).m_ID[dof_RU];\n\t\tlm[4] = m_ss.Node(j).m_ID[dof_RV];\n\t\tlm[5] = m_ss.Node(j).m_ID[dof_RW];\n\n\t\tfor (int k=0; k<n; ++k)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\tlm[6*(k+1)  ] = id[dof_X];\n\t\t\tlm[6*(k+1)+1] = id[dof_Y];\n\t\t\tlm[6*(k+1)+2] = id[dof_Z];\n\t\t\tlm[6*(k+1)+3] = id[dof_RU];\n\t\t\tlm[6*(k+1)+4] = id[dof_RV];\n\t\t\tlm[6*(k+1)+5] = id[dof_RW];\n\t\t}\n\n\t\tK.build_add(lm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project surface\nvoid FEPeriodicBoundary::ProjectSurface(FEPeriodicSurface& ss, FEPeriodicSurface& ms, bool bmove)\n{\n\tint i;\n\tdouble rs[2];\n\n\t// get the primary's center of mass\n\tvec3d cs = ss.CenterOfMass();\n\n\t// get the secondary's center of mass\n\tvec3d cm = ms.CenterOfMass();\n\n\t// get the relative distance\n\tvec3d cr = cs - cm;\n\n\t// unit vector in direction of cr\n\t// this will serve as the projection distance\n\tvec3d cn(cr); \n\tdouble D = cn.unit();\n\n\t// initialize projection data\n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(1.1*D);\n\tnp.Init();\n\n\t// loop over all primary nodes\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFENode& node = ss.Node(i);\n\n\t\t// get the nodal position\n\t\tvec3d r0 = node.m_r0;\n\n\t\t// find the intersection with the secondary surface\n\t\tss.m_data[i].m_pme = np.Project3(r0, cn, rs);\n\t\tassert(ss.m_data[i].m_pme);\n\n\t\tss.m_data[i].m_rs[0] = rs[0];\n\t\tss.m_data[i].m_rs[1] = rs[1];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary::Update()\n{\n\tint i, j, ne;\n\tFESurfaceElement* pme;\n\n\tFEMesh& mesh = *m_ss.GetMesh();\n\n\tvec3d us, um;\n\tvec3d umi[FEElement::MAX_NODES];\n\n\t// update gap functions\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// off-set sign\n\t\tdouble s = (np==0?1.0:-1.0);\n\n\t\tint N = ss.Nodes();\n\n\t\tfor (i=0; i<N; ++i)\n\t\t{\n\t\t\t// calculate the primary displacement\n\t\t\tFENode& node = ss.Node(i);\n\t\t\tus = node.m_rt - node.m_r0;\n\n\t\t\t// get the secondary element\n\t\t\tpme = ss.m_data[i].m_pme;\n\n\t\t\t// calculate the secondary displacement\n\t\t\tne = pme->Nodes();\n\t\t\tfor (j=0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = ms.Node(pme->m_lnode[j]);\n\t\t\t\tumi[j] = node.m_rt - node.m_r0;\n\t\t\t}\n\t\t\tum = pme->eval(umi, ss.m_data[i].m_rs[0], ss.m_data[i].m_rs[1]);\n\n\t\t\t// calculate gap function\n\t\t\tss.m_data[i].m_gap = us - um + m_off*s;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint j, k, l, m, n;\n\tint nseln, nmeln;\n\n\tdouble *Gr, *Gs;\n\n\t// jacobian\n\tdouble detJ;\n\n\tvec3d dxr, dxs;\n\tdouble* w;\n\n\t// natural coordinates of primary node in secondary element\n\tdouble r, s;\n\n\t// contact force\n\tvec3d tc;\n\n\t// shape function values\n\tdouble N[FEElement::MAX_NODES];\n\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tvec3d r0[FEElement::MAX_NODES], rt[FEElement::MAX_NODES];\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// zero reaction forces\n\t\tfor (int i=0; i<ss.Nodes(); ++i) ss.m_data[i].m_Fr = vec3d(0,0,0);\n\n\t\t// loop over all primary facets\n\t\tint ne = ss.Elements();\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\t// get the primary element\n\t\t\tFESurfaceElement& sel = ss.Element(j);\n\t\t\t\n\t\t\t// get the elements LM vector\n\t\t\tss.UnpackLM(sel, sLM);\n\n\t\t\tnseln = sel.Nodes();\n\n\t\t\tfor (int i=0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.Node(sel.m_lnode[i]).m_r0;\n\t\t\t\trt[i] = ss.Node(sel.m_lnode[i]).m_rt;\n\t\t\t}\n\t\t\tw = sel.GaussWeights();\n\n\t\t\t// loop over primary element nodes (which are the integration points as well)\n\t\t\tfor (n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = sel.Gr(n);\n\t\t\t\tGs = sel.Gs(n);\n\n\t\t\t\tm = sel.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get primary node contact force\n\t\t\t\ttc = ss.m_data[m].m_Lm + ss.m_data[m].m_gap*m_eps;\n\t\t\t\tss.m_data[m].m_Tn = tc;\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& mel = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(mel, mLM);\n\n\t\t\t\tnmeln = mel.Nodes();\n\n\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t// onto the secondary element\n\t\t\t\tr = ss.m_data[m].m_rs[0];\n\t\t\t\ts = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tN[0] = 0.25*(1-r)*(1-s);\n\t\t\t\t\tN[1] = 0.25*(1+r)*(1-s);\n\t\t\t\t\tN[2] = 0.25*(1+r)*(1+s);\n\t\t\t\t\tN[3] = 0.25*(1-r)*(1+s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tN[0] = 1 - r - s;\n\t\t\t\t\tN[1] = r;\n\t\t\t\t\tN[2] = s;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// calculate force vector\n\t\t\t\tfe.resize(3*(nmeln+1));\n\t\t\t\tfe[0] = -detJ*w[n]*tc.x;\n\t\t\t\tfe[1] = -detJ*w[n]*tc.y;\n\t\t\t\tfe[2] = -detJ*w[n]*tc.z;\n\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tfe[3*(l+1)  ] = detJ*w[n]*tc.x*N[l];\n\t\t\t\t\tfe[3*(l+1)+1] = detJ*w[n]*tc.y*N[l];\n\t\t\t\t\tfe[3*(l+1)+2] = detJ*w[n]*tc.z*N[l];\n\t\t\t\t}\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3*(nmeln+1));\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(l+1)  ] = mLM[l*3  ];\n\t\t\t\t\tlm[3*(l+1)+1] = mLM[l*3+1];\n\t\t\t\t\tlm[3*(l+1)+2] = mLM[l*3+2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (l=0; l<nmeln; ++l) en[l+1] = mel.m_node[l];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\n\t\t\t\t// also store in the reaction force vector\n\t\t\t\tvec3d& fr = ss.m_data[m].m_Fr;\n\t\t\t\tfr.x += fe[0];\n\t\t\t\tfr.y += fe[1];\n\t\t\t\tfr.z += fe[2];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint j, k, l, n, m;\n\tint nseln, nmeln, ndof;\n\n\tFEElementMatrix ke;\n\n\tconst int MN = FEElement::MAX_NODES;\n\tvector<int> lm(3*(MN+1));\n\tvector<int> en(MN+1);\n\n\tdouble *Gr, *Gs, *w;\n\tvec3d rt[MN], r0[MN];\n\n\tvec3d rtm[MN];\n\n\tdouble detJ, r, s;\n\tvec3d dxr, dxs;\n\tdouble H[MN];\n\n\tvec3d gap, Lm, tc;\n\n\t// curvature tensor K\n\tdouble K[2][2] = {0};\n\n//\tdouble scale = -0.0035*m_fem.GetMesh().GetBoundingBox().radius();\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary elements\n\t\tint ne = ss.Elements();\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\n\t\t\t// get the element's LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\tnseln = se.Nodes();\n\n\t\t\tfor (int i=0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.Node(se.m_lnode[i]).m_r0;\n\t\t\t\trt[i] = ss.Node(se.m_lnode[i]).m_rt;\n\t\t\t}\n\n\t\t\tw = se.GaussWeights();\n\n\t\t\t// loop over all integration points (that is nodes)\n\t\t\tfor (n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = se.Gr(n);\n\t\t\t\tGs = se.Gs(n);\n\n\t\t\t\tm = se.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\tnmeln = me.Nodes();\n\n\t\t\t\t// get the secondary element node positions\n\t\t\t\tfor (k=0; k<nmeln; ++k) rtm[k] = ms.Node(me.m_lnode[k]).m_rt;\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tr = ss.m_data[m].m_rs[0];\n\t\t\t\ts = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get primary node normal force\n\t\t\t\ttc = ss.m_data[m].m_Lm + ss.m_data[m].m_gap*m_eps; //ss.T[m];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tH[0] = 0.25*(1-r)*(1-s);\n\t\t\t\t\tH[1] = 0.25*(1+r)*(1-s);\n\t\t\t\t\tH[2] = 0.25*(1+r)*(1+s);\n\t\t\t\t\tH[3] = 0.25*(1-r)*(1+s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tH[0] = 1 - r - s;\n\t\t\t\t\tH[1] = r;\n\t\t\t\t\tH[2] = s;\n\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// number of degrees of freedom\n\t\t\t\tndof = 3*(1 + nmeln);\n\n\t\t\t\t// fill stiffness matrix\n\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\tke[0][0] = w[n]*detJ*m_eps;\n\t\t\t\tke[1][1] = w[n]*detJ*m_eps;\n\t\t\t\tke[2][2] = w[n]*detJ*m_eps;\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tke[0][3+3*k  ] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[1][3+3*k+1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[2][3+3*k+2] = -w[n]*detJ*m_eps*H[k];\n\n\t\t\t\t\tke[3+3*k  ][0] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+1][1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+2][2] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t}\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3+3*k  ][3+3*l  ] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+1][3+3*l+1] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+2][3+3*l+2] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t}\n\n\t\t\t\t// create lm array\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicBoundary::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\n\tdouble g;\n\tvec3d lm;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_data[i].m_Lm + m_ss.m_data[i].m_gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ss.m_data[i].m_gap.norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_data[i].m_Lm + m_ms.m_data[i].m_gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ms.m_data[i].m_gap.norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\tfeLog(\" tied interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\n\t// check convergence of constraints\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\n\t// update Lagrange multipliers if we did not converge\n\tif (bconv == false)\n\t{\n\t\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ss.m_data[i].m_Lm = m_ss.m_data[i].m_Lm + m_ss.m_data[i].m_gap*m_eps;\n\t\t}\n\t\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ms.m_data[i].m_Lm = m_ms.m_data[i].m_Lm + m_ms.m_data[i].m_gap*m_eps;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMech/FEPeriodicBoundary.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n#include <FECore/vec2d.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEPeriodicSurface : public FEContactSurface\n{\npublic:\n\tclass Data : public FEContactMaterialPoint\n\t{\n\tpublic:\n\t\tData();\n\n\t\tvoid Init() override;\n\n\t\tvoid Serialize(DumpStream& ar) override;\n\n\tpublic:\n\t\tvec3d\t\t\t\tm_gap;\t//!< gap function at nodes\n\t\tvec2d\t\t\t\tm_rs;\t//!< natural coordinates of projection on secondary surface element\n\t\tvec3d\t\t\t\tm_Lm;\t//!< Lagrange multipliers\n\t\tvec3d\t\t\t\tm_Tn;\t//!< nodal traction forces\n\t\tvec3d\t\t\t\tm_Fr;\t//!< reaction forces\n\t};\n\npublic:\n\t//! constructor\n\tFEPeriodicSurface(FEModel* pfem) : FEContactSurface(pfem) {}\n\n\t//! initializes data\n\tbool Init();\n\n\t//! copy data\n\tvoid CopyFrom(FEPeriodicSurface& s);\n\n\t//! calculates the center of mass of the surface\n\tvec3d CenterOfMass();\n\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt);\n\tvoid GetNodalContactPressure(int nface, double* pg);\n\tvoid GetNodalContactTraction(int nface, vec3d* pt);\n\npublic:\n\tvector<Data>\tm_data;\t// integration point data\n};\n\n//-----------------------------------------------------------------------------\n\nclass FEPeriodicBoundary : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEPeriodicBoundary(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FEPeriodicBoundary(void) {}\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! create a copy of this interface\n\tvoid CopyFrom(FESurfacePairConstraint* pci) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FEPeriodicSurface& ss, FEPeriodicSurface& ms, bool bmove);\n\npublic:\n\tFEPeriodicSurface\tm_ss;\t//!< primary surface\n\tFEPeriodicSurface\tm_ms;\t//!< secondary surface\n\n\tdouble\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\tm_eps;\t\t\t//!< penalty scale factor\n\tdouble\tm_stol;\t\t\t//!< search tolerance\n\tbool\tm_btwo_pass;\t//!< two-pass flag\n\tint\t\tm_naugmin;\t\t//!< minimum number of augmentations\n\tvec3d\tm_off;\t\t\t//!< relative displacement offset\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPeriodicSurfaceConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPeriodicSurfaceConstraint.h\"\n#include \"FECore/FENormalProjection.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n#include <FECore/FEMesh.h>\n#include \"FEBioMech.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEPeriodicSurfaceConstraint, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\");\n\tADD_PARAMETER(m_eps      , \"penalty\");\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Creates a surface for use with an FEPeriodicSurfaceConstraint interface. All surface data\n//! structures are allocated.\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FEPeriodicSurfaceConstraintSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEContactSurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate other surface data\n\tm_gap.resize(nn);\t\t// gap funtion\n\tm_pme.assign(nn, static_cast<FESurfaceElement*>(0));\t// penetrated secondary element\n\tm_rs.resize(nn);\t\t// natural coords of projected primary node on secondary element\n\tm_Lm.resize(nn);\t\t// Lagrangian multipliers\n\n\t// set initial values\n\tzero(m_gap);\n\tzero(m_Lm);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the center of mass for this surface\n//!\nvec3d FEPeriodicSurfaceConstraintSurface::CenterOfMass()\n{\n\tvec3d c(0, 0, 0);\n\tint N = Nodes();\n\tfor (int i = 0; i<N; ++i) c += Node(i).m_r0;\n\tif (N != 0) c /= (double)N;\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurfaceConstraintSurface::Serialize(DumpStream& ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tif (ar.IsShallow())\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tar << m_Lm << m_gap;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar >> m_Lm >> m_gap;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tar << m_gap;\n\t\t\tar << m_rs;\n\t\t\tar << m_Lm;\n\t\t\tar << m_nref;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar >> m_gap;\n\t\t\tar >> m_rs;\n\t\t\tar >> m_Lm;\n\t\t\tar >> m_nref;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// FEPeriodicSurfaceConstraint\n//-----------------------------------------------------------------------------\n\nFEPeriodicSurfaceConstraint::FEPeriodicSurfaceConstraint(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem), m_dofU(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_stol = 0.01;\n\tm_srad = 1.0;\n\tm_atol = 0;\n\tm_eps = 0;\n\tm_btwo_pass = false;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicSurfaceConstraint::Init()\n{\n\t// create the surfaces\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FEPeriodicSurfaceConstraint::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6 * 5);\n\n\tint nref = m_ss.m_nref;\n\tFESurfaceElement* pref = m_ss.m_pme[nref];\n\n\tint n0 = pref->Nodes();\n\tint nr0[FEElement::MAX_NODES];\n\tfor (int j = 0; j<n0; ++j) nr0[j] = pref->m_node[j];\n\n\tassign(lm, -1);\n\n\tlm[0] = m_ss.Node(nref).m_ID[dof_X];\n\tlm[1] = m_ss.Node(nref).m_ID[dof_Y];\n\tlm[2] = m_ss.Node(nref).m_ID[dof_Z];\n\tlm[3] = m_ss.Node(nref).m_ID[dof_RU];\n\tlm[4] = m_ss.Node(nref).m_ID[dof_RV];\n\tlm[5] = m_ss.Node(nref).m_ID[dof_RW];\n\n\tfor (int k = 0; k<n0; ++k)\n\t{\n\t\tvector<int>& id = mesh.Node(nr0[k]).m_ID;\n\t\tlm[6 * (k + 1)] = id[dof_X];\n\t\tlm[6 * (k + 1) + 1] = id[dof_Y];\n\t\tlm[6 * (k + 1) + 2] = id[dof_Z];\n\t\tlm[6 * (k + 1) + 3] = id[dof_RU];\n\t\tlm[6 * (k + 1) + 4] = id[dof_RV];\n\t\tlm[6 * (k + 1) + 5] = id[dof_RW];\n\t}\n\n\tfor (int j = 0; j<m_ss.Nodes(); ++j)\n\t{\n\t\tFESurfaceElement& me = *m_ss.m_pme[j];\n\t\tint* en = &me.m_node[0];\n\n\t\tassign(lm, -1);\n\n\t\tint n = me.Nodes();\n\n\t\tlm[0] = m_ss.Node(j).m_ID[dof_X];\n\t\tlm[1] = m_ss.Node(j).m_ID[dof_Y];\n\t\tlm[2] = m_ss.Node(j).m_ID[dof_Z];\n\t\tlm[3] = m_ss.Node(j).m_ID[dof_RU];\n\t\tlm[4] = m_ss.Node(j).m_ID[dof_RV];\n\t\tlm[5] = m_ss.Node(j).m_ID[dof_RW];\n\n\t\tfor (int k = 0; k<n; ++k)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\tlm[6 * (k + 1)] = id[dof_X];\n\t\t\tlm[6 * (k + 1) + 1] = id[dof_Y];\n\t\t\tlm[6 * (k + 1) + 2] = id[dof_Z];\n\t\t\tlm[6 * (k + 1) + 3] = id[dof_RU];\n\t\t\tlm[6 * (k + 1) + 4] = id[dof_RV];\n\t\t\tlm[6 * (k + 1) + 5] = id[dof_RW];\n\t\t}\n\n\t\tK.build_add(lm);\n\t}\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurfaceConstraint::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms, false);\n\tProjectSurface(m_ms, m_ss, false);\n}\n\n//-----------------------------------------------------------------------------\n//! project surface\n\nvoid FEPeriodicSurfaceConstraint::ProjectSurface(FEPeriodicSurfaceConstraintSurface& ss, FEPeriodicSurfaceConstraintSurface& ms, bool bmove)\n{\n\tFEMesh& mesh = GetMesh();\n\n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(m_srad);\n\tnp.Init();\n\n\tint i;\n\tdouble rs[2];\n\n\t// get the primary's center of mass\n\tvec3d cs = ss.CenterOfMass();\n\n\t// get the secondary's center of mass\n\tvec3d cm = ms.CenterOfMass();\n\n\t// get the relative distance\n\tvec3d cr = cm - cs;\n\n\t// unit vector in direction of cr\n\t// this will serve as the projection distance\n\tvec3d cn(cr); cn.unit();\n\n\t// loop over all primary nodes\n\tfor (i = 0; i<ss.Nodes(); ++i)\n\t{\n\t\tFENode& node = ss.Node(i);\n\n\t\t// get the nodal position\n\t\tvec3d r0 = node.m_r0;\n\n\t\t// find the intersection with the secondary surface\n\t\tss.m_pme[i] = np.Project(r0, cn, rs);\n\t\tassert(ss.m_pme[i]);\n\n\t\tss.m_rs[i][0] = rs[0];\n\t\tss.m_rs[i][1] = rs[1];\n\t}\n\n\t// if the reference node has not been located, we do it now.\n\tif (ss.m_nref < 0)\n\t{\n\t\t// we pick the node that is closest to the center of mass\n\t\tdouble dmin = (ss.Node(0).m_rt - cs).norm(), d;\n\t\tint nref = 0;\n\t\tfor (i = 1; i<ss.Nodes(); ++i)\n\t\t{\n\t\t\td = (ss.Node(i).m_rt - cs).norm();\n\t\t\tif (d < dmin)\n\t\t\t{\n\t\t\t\tdmin = d;\n\t\t\t\tnref = i;\n\t\t\t}\n\t\t}\n\t\tss.m_nref = nref;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurfaceConstraint::Update()\n{\n\tint i, j, ne, n0;\n\tFESurfaceElement* pme;\n\n\tFEMesh& mesh = *m_ss.GetMesh();\n\n\tvec3d us, um, u0;\n\tvec3d umi[FEElement::MAX_NODES];\n\n\t// update gap functions\n\tint npass = (m_btwo_pass ? 2 : 1);\n\tfor (int np = 0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurfaceConstraintSurface& ss = (np == 0 ? m_ss : m_ms);\n\t\tFEPeriodicSurfaceConstraintSurface& ms = (np == 0 ? m_ms : m_ss);\n\n\t\tint N = ss.Nodes();\n\n\t\t// calculate the reference node displacement\n\t\tn0 = ss.m_nref;\n\t\tFENode& node = ss.Node(n0);\n\t\tus = node.m_rt - node.m_r0;\n\n\t\t// get the secondary element\n\t\tpme = ss.m_pme[n0];\n\n\t\t// calculate the secondary displacement\n\t\tne = pme->Nodes();\n\t\tfor (j = 0; j<ne; ++j)\n\t\t{\n\t\t\tFENode& node = ms.Node(pme->m_lnode[j]);\n\t\t\tumi[j] = node.m_rt - node.m_r0;\n\t\t}\n\t\tum = pme->eval(umi, ss.m_rs[n0][0], ss.m_rs[n0][1]);\n\n\t\t// calculate relative reference displacement\n\t\tu0 = us - um;\n\n\t\tfor (i = 0; i<N; ++i)\n\t\t{\n\t\t\t// calculate the primary displacement\n\t\t\tFENode& node = ss.Node(i);\n\t\t\tus = node.m_rt - node.m_r0;\n\n\t\t\t// get the secondary element\n\t\t\tpme = ss.m_pme[i];\n\n\t\t\t// calculate the secondary displacement\n\t\t\tne = pme->Nodes();\n\t\t\tfor (j = 0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = ms.Node(pme->m_lnode[j]);\n\t\t\t\tumi[j] = node.m_rt - node.m_r0;\n\t\t\t}\n\t\t\tum = pme->eval(umi, ss.m_rs[i][0], ss.m_rs[i][1]);\n\n\t\t\t// calculate gap function\n\t\t\tss.m_gap[i] = us - um - u0;\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurfaceConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint j, k, l, m, n;\n\tint nseln, nmeln;\n\n\tdouble *Gr, *Gs;\n\n\t// jacobian\n\tdouble detJ;\n\n\tvec3d dxr, dxs;\n\tvec3d rt[FEElement::MAX_NODES], r0[FEElement::MAX_NODES];\n\tdouble* w;\n\n\t// natural coordinates of primary node in secondary element\n\tdouble r, s;\n\n\t// contact force\n\tvec3d tc;\n\n\t// shape function values\n\tdouble N[FEElement::MAX_NODES];\n\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\tvector<int> LM0;\n\n\tint npass = (m_btwo_pass ? 2 : 1);\n\tfor (int np = 0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurfaceConstraintSurface& ss = (np == 0 ? m_ss : m_ms);\n\t\tFEPeriodicSurfaceConstraintSurface& ms = (np == 0 ? m_ms : m_ss);\n\n\t\t// get the reference element\n\t\tint nref = ss.m_nref;\n\t\tFESurfaceElement* pref = ss.m_pme[nref];\n\n\t\t// loop over all primary facets\n\t\tint ne = ss.Elements();\n\t\tfor (j = 0; j<ne; ++j)\n\t\t{\n\t\t\t// get the primary element\n\t\t\tFESurfaceElement& sel = ss.Element(j);\n\n\t\t\t// get the element's LM vector\n\t\t\tss.UnpackLM(sel, sLM);\n\n\t\t\tnseln = sel.Nodes();\n\n\t\t\tfor (int i = 0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.GetMesh()->Node(sel.m_node[i]).m_r0;\n\t\t\t\trt[i] = ss.GetMesh()->Node(sel.m_node[i]).m_rt;\n\t\t\t}\n\n\t\t\tw = sel.GaussWeights();\n\n\t\t\t// loop over primary element nodes (which are the integration points as well)\n\t\t\tfor (n = 0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = sel.Gr(n);\n\t\t\t\tGs = sel.Gs(n);\n\n\t\t\t\tm = sel.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0, 0, 0);\n\t\t\t\tfor (k = 0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k] * r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k] * r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k] * r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k] * r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k] * r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k] * r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get primary node contact force\n\t\t\t\ttc = ss.m_Lm[m] + ss.m_gap[m] * m_eps;\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& mel = *ss.m_pme[m];\n\t\t\t\tms.UnpackLM(mel, mLM);\n\n\t\t\t\tnmeln = mel.Nodes();\n\n\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t// onto the secondary element\n\t\t\t\tr = ss.m_rs[m][0];\n\t\t\t\ts = ss.m_rs[m][1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tN[0] = 0.25*(1 - r)*(1 - s);\n\t\t\t\t\tN[1] = 0.25*(1 + r)*(1 - s);\n\t\t\t\t\tN[2] = 0.25*(1 + r)*(1 + s);\n\t\t\t\t\tN[3] = 0.25*(1 - r)*(1 + s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tN[0] = 1 - r - s;\n\t\t\t\t\tN[1] = r;\n\t\t\t\t\tN[2] = s;\n\t\t\t\t}\n\n\t\t\t\t// calculate force vector\n\t\t\t\tfe.resize(3 * (nmeln + 1));\n\t\t\t\tfe[0] = -detJ*w[n] * tc.x;\n\t\t\t\tfe[1] = -detJ*w[n] * tc.y;\n\t\t\t\tfe[2] = -detJ*w[n] * tc.z;\n\t\t\t\tfor (l = 0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * (l + 1)] = detJ*w[n] * tc.x*N[l];\n\t\t\t\t\tfe[3 * (l + 1) + 1] = detJ*w[n] * tc.y*N[l];\n\t\t\t\t\tfe[3 * (l + 1) + 2] = detJ*w[n] * tc.z*N[l];\n\t\t\t\t}\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3 * (nmeln + 1));\n\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\t\t\t\tfor (l = 0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (l + 1)] = mLM[l * 3];\n\t\t\t\t\tlm[3 * (l + 1) + 1] = mLM[l * 3 + 1];\n\t\t\t\t\tlm[3 * (l + 1) + 2] = mLM[l * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln + 1);\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (l = 0; l<nmeln; ++l) en[l + 1] = mel.m_node[l];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurfaceConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint j, k, l, n, m;\n\tint nseln, nmeln, ndof;\n\n\tFEElementMatrix ke;\n\n\tvector<int> lm(15);\n\tvector<int> en(5);\n\n\tdouble *Gr, *Gs, *w;\n\n\tvec3d rt[FEElement::MAX_NODES], r0[FEElement::MAX_NODES];\n\tvec3d rtm[FEElement::MAX_NODES];\n\n\tdouble detJ, r, s;\n\tvec3d dxr, dxs;\n\tdouble H[FEElement::MAX_NODES];\n\n\tvec3d gap, Lm, tc;\n\n\t// curvature tensor K\n\tdouble K[2][2] = { 0 };\n\n\t//\tdouble scale = -0.0035*m_fem.GetMesh().GetBoundingBox().radius();\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\tvector<int> LM0;\n\n\tint n0[FEElement::MAX_NODES];\n\n\tint npass = (m_btwo_pass ? 2 : 1);\n\tfor (int np = 0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurfaceConstraintSurface& ss = (np == 0 ? m_ss : m_ms);\n\t\tFEPeriodicSurfaceConstraintSurface& ms = (np == 0 ? m_ms : m_ss);\n\n\t\t// get the reference element\n\t\tint nref = ss.m_nref;\n\t\tFESurfaceElement* pref = ss.m_pme[nref];\n\n\t\t// grab the data we'll need for this element\n\t\tms.UnpackLM(*pref, LM0);\n\t\tint ne0 = pref->Nodes();\n\t\tfor (j = 0; j<ne0; ++j) n0[j] = pref->m_node[j];\n\t\tr = ss.m_rs[nref][0];\n\t\ts = ss.m_rs[nref][1];\n\t\tdouble N0[FEElement::MAX_NODES];\n\t\tpref->shape_fnc(N0, r, s);\n\n\t\t// number of degrees of freedom\n\t\tndof = 3 * (1 + ne0);\n\t\tvector<double> N(ndof / 3);\n\t\tN[0] = 1;\n\t\tfor (k = 0; k<ne0; ++k) N[k + 1] = -N0[k];\n\n\t\t// stiffness matrix\n\t\tke.resize(ndof, ndof); ke.zero();\n\t\tfor (k = 0; k<ndof / 3; ++k)\n\t\t\tfor (l = 0; l<ndof / 3; ++l)\n\t\t\t{\n\t\t\t\tke[3 * k][3 * l] = m_eps*N[k] * N[l];\n\t\t\t\tke[3 * k + 1][3 * l + 1] = m_eps*N[k] * N[l];\n\t\t\t\tke[3 * k + 2][3 * l + 2] = m_eps*N[k] * N[l];\n\t\t\t}\n\n\t\t// fill the lm array\n\t\tlm.resize(3 * (ne0 + 1));\n\t\tlm[0] = ss.Node(nref).m_ID[m_dofU[0]];\n\t\tlm[1] = ss.Node(nref).m_ID[m_dofU[1]];\n\t\tlm[2] = ss.Node(nref).m_ID[m_dofU[2]];\n\t\tfor (l = 0; l<ne0; ++l)\n\t\t{\n\t\t\tlm[3 * (l + 1)] = LM0[l * 3];\n\t\t\tlm[3 * (l + 1) + 1] = LM0[l * 3 + 1];\n\t\t\tlm[3 * (l + 1) + 2] = LM0[l * 3 + 2];\n\t\t}\n\n\t\t// fill the en array\n\t\ten.resize(ne0 + 1);\n\t\ten[0] = ss.NodeIndex(nref);\n\t\tfor (l = 0; l<ne0; ++l) en[l + 1] = n0[l];\n\n\t\t// assemble stiffness matrix\n\t\t//\t\tpsolver->AssembleStiffness(en, lm, ke);\n\n\t\t// loop over all primary elements\n\t\tint ne = ss.Elements();\n\t\tfor (j = 0; j<ne; ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\n\t\t\t// get the element's LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\tnseln = se.Nodes();\n\n\t\t\tfor (int i = 0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.GetMesh()->Node(se.m_node[i]).m_r0;\n\t\t\t\trt[i] = ss.GetMesh()->Node(se.m_node[i]).m_rt;\n\t\t\t}\n\n\t\t\tw = se.GaussWeights();\n\n\t\t\t// loop over all integration points (that is nodes)\n\t\t\tfor (n = 0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = se.Gr(n);\n\t\t\t\tGs = se.Gs(n);\n\n\t\t\t\tm = se.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0, 0, 0);\n\t\t\t\tfor (k = 0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k] * r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k] * r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k] * r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k] * r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k] * r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k] * r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *ss.m_pme[m];\n\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\tnmeln = me.Nodes();\n\n\t\t\t\t// get the secondary element node positions\n\t\t\t\tfor (k = 0; k<nmeln; ++k) rtm[k] = ms.GetMesh()->Node(me.m_node[k]).m_rt;\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tr = ss.m_rs[m][0];\n\t\t\t\ts = ss.m_rs[m][1];\n\n\t\t\t\t// get primary node normal force\n\t\t\t\ttc = ss.m_Lm[m] + ss.m_gap[m] * m_eps; //ss.T[m];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tH[0] = 0.25*(1 - r)*(1 - s);\n\t\t\t\t\tH[1] = 0.25*(1 + r)*(1 - s);\n\t\t\t\t\tH[2] = 0.25*(1 + r)*(1 + s);\n\t\t\t\t\tH[3] = 0.25*(1 - r)*(1 + s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tH[0] = 1 - r - s;\n\t\t\t\t\tH[1] = r;\n\t\t\t\t\tH[2] = s;\n\t\t\t\t}\n\n\t\t\t\t// number of degrees of freedom\n\t\t\t\tndof = 3 * (1 + nmeln);\n\n\t\t\t\tvector<double> N(ndof / 3);\n\t\t\t\tN[0] = 1;\n\t\t\t\tfor (k = 0; k<nmeln; ++k) N[k + 1] = -H[k];\n\n\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\tfor (k = 0; k<ndof / 3; ++k)\n\t\t\t\t\tfor (l = 0; l<ndof / 3; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3 * k][3 * l] = w[n] * detJ*m_eps*N[k] * N[l];\n\t\t\t\t\t\tke[3 * k + 1][3 * l + 1] = w[n] * detJ*m_eps*N[k] * N[l];\n\t\t\t\t\t\tke[3 * k + 2][3 * l + 2] = w[n] * detJ*m_eps*N[k] * N[l];\n\t\t\t\t\t}\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3 * (nmeln + 1));\n\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\t\t\t\tfor (l = 0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (l + 1)] = mLM[l * 3];\n\t\t\t\t\tlm[3 * (l + 1) + 1] = mLM[l * 3 + 1];\n\t\t\t\t\tlm[3 * (l + 1) + 2] = mLM[l * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln + 1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (l = 0; l<nmeln; ++l) en[l + 1] = me.m_node[l];\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicSurfaceConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\tbool bconv = true;\n\n\tdouble g;\n\tvec3d lm;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i = 0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_Lm[i];\n\t\tnormL0 += lm*lm;\n\t}\n\tfor (i = 0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_Lm[i];\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i = 0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_Lm[i] + m_ss.m_gap[i] * m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ss.m_gap[i].norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\n\tfor (i = 0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_Lm[i] + m_ms.m_gap[i] * m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ms.m_gap[i].norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tif (N == 0) N = 1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\t// check convergence of constraints\n\tfeLog(\" surface constraint# %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0) / normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\n\tif (pctn >= m_atol)\n\t{\n\t\tbconv = false;\n\t\tfor (i = 0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ss.m_Lm[i] = m_ss.m_Lm[i] + m_ss.m_gap[i] * m_eps;\n\t\t}\n\n\t\tfor (i = 0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ms.m_Lm[i] = m_ms.m_Lm[i] + m_ms.m_gap[i] * m_eps;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicSurfaceConstraint::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMech/FEPeriodicSurfaceConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEPeriodicSurfaceConstraintSurface : public FEContactSurface\n{\npublic:\n\t//! constructor\n\tFEPeriodicSurfaceConstraintSurface(FEModel* pfem) : FEContactSurface(pfem) { m_nref = -1; }\n\n\t//! initializes data\n\tbool Init();\n\n\t//! calculates the center of mass of the surface\n\tvec3d CenterOfMass();\n\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tvector<vec3d>\t\t\t\tm_gap;\t//!< gap function at nodes\n\tvector<FESurfaceElement*>\tm_pme;\t//!< secondary surface element a node penetrates\n\tvector<vec2d>\t\t\t\tm_rs;\t//!< natural coordinates of projection on secondary surface element\n\tvector<vec3d>\t\t\t\tm_Lm;\t//!< Lagrange multipliers\n\n\tint\t\tm_nref;\t//!< reference node\n};\n\n//-----------------------------------------------------------------------------\n\nclass FEBIOMECH_API FEPeriodicSurfaceConstraint : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEPeriodicSurfaceConstraint(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FEPeriodicSurfaceConstraint(void) {}\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FEPeriodicSurfaceConstraintSurface& ss, FEPeriodicSurfaceConstraintSurface& ms, bool bmove);\n\npublic:\n\tFEPeriodicSurfaceConstraintSurface\tm_ss;\t//!< primary surface\n\tFEPeriodicSurfaceConstraintSurface\tm_ms;\t//!< secondary surface\n\n\tdouble\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\tm_eps;\t\t\t//!< penalty scale factor\n\tdouble\tm_stol;\t\t\t//!< search tolerance\n\tdouble\tm_srad;\t\t\t//!< search radius (%)\n\tbool\tm_btwo_pass;\t//!< nr of passes\n\n\tFEDofList\tm_dofU;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPipetteAspiration.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPipetteAspiration.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include \"FEContactInterface.h\"\n#include \"FESlidingElasticInterface.h\"\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEPipetteAspiration, FESurfaceLoad)\n\tADD_PARAMETER(m_pressure, \"pressure\")->setUnits(UNIT_PRESSURE)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n    ADD_PARAMETER(m_radius, \"radius\")->setUnits(UNIT_LENGTH)->setLongName(\"Pipette radius\");\n    ADD_PARAMETER(m_center, \"center\")->setUnits(UNIT_LENGTH)->setLongName(\"Pipette center\");\n    ADD_PARAMETER(m_normal, \"normal\")->setLongName(\"Pipette normal\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEPipetteAspiration::FEPipetteAspiration(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n\tm_pressure = 0.0;\n    m_radius = 0.0;\n    m_center = vec3d(0,0,0);\n    m_normal = vec3d(0,0,1);\n    m_contact = -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPipetteAspiration::Init()\n{\n\tFESurface& surf = GetSurface();\n    // check if this surface is a contact surface (primary)\n    FEModel& fem = *GetFEModel();\n    if (fem.SurfacePairConstraints() > 0)\n    {\n        // loop over all contact interfaces\n        for (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n        {\n            FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n            FESlidingElasticInterface* pbw = dynamic_cast<FESlidingElasticInterface*>(pci);\n            if (pbw) {\n                FESurface& ps = *pci->GetPrimarySurface();\n                // for now, use quick and dirty way to figure out if these surfaces are the same\n                if (ps.Elements() == surf.Elements()) {\n                    m_contact = i;\n                    surf = ps;\n                    break;\n                }\n            }\n        }\n    }\n    else if (m_contact == -1) {\n        feLogWarning(\"Pipette surface does not match any contact surface!\");\n        return false;\n    }\n\n\t// get the degrees of freedom\n\tm_dof.Clear();\n\tif (surf.IsShellBottom() == false)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\telse\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t}\n\tif (m_dof.IsEmpty()) return false;\n\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPipetteAspiration::PrepStep()\n{\n    FEModel& fem = *GetFEModel();\n    FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(m_contact));\n    double psf = pci->GetPenaltyScaleFactor();\n    FESlidingElasticInterface* sei = dynamic_cast<FESlidingElasticInterface*>(pci);\n    FESurface& surf = *pci->GetPrimarySurface();\n    m_tag.assign(surf.Elements(), std::vector<bool>(FEElement::MAX_INTPOINTS,false));\n\n    // loop over all faces of the surface\n    for (int i=0; i<surf.Elements(); ++i) {\n        FESurfaceElement& el = surf.Element(i);\n        // loop over all integration points of the surface\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMaterialPoint& pt = *el.GetMaterialPoint(j);\n            // get the sliding elastic interface data at this material point\n            FESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(pt);\n            // penalty\n            double eps = sei->m_epsn*data.m_epsn*psf;\n            // normal gap\n            double g = data.m_gap;\n            // normal traction Lagrange multiplier\n            double Lm = data.m_Lmd;\n            double pn = sei->m_btension ? (Lm + eps*g) : MBRACKET(Lm + eps*g);\n            vec3d dx = pt.m_rt - m_center;\n            double r = (dx - m_normal*(dx*m_normal)).Length();\n            if ((r <= m_radius) && (pn == 0))\n                m_tag[i][j] = true;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPipetteAspiration::LoadVector(FEGlobalVector& R)\n{\n    FEModel& fem = *GetFEModel();\n    FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(m_contact));\n    FESurface& surf = *pci->GetPrimarySurface();\n\n\tsurf.LoadVector(R, m_dof, false, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\t\t\n        FESurfaceElement& sel = *pt.SurfaceElement();\n\t\t// evaluate pressure at this material point\n\t\tdouble P = (m_tag[sel.m_lid][pt.m_index]) ? -m_pressure(pt) : 0;\n\t\tif (surf.IsShellBottom()) P = -P;\n        \n\t\tdouble J = (pt.dxr ^ pt.dxs).norm();\n\n\t\t// force vector\n\t\tvec3d N = (pt.dxr ^ pt.dxs); N.unit();\n\t\tvec3d t = N*P;\n\n\t\tdouble H_u = dof_a.shape;\n\n\t\tval[0] = H_u*t.x*J;\n\t\tval[1] = H_u*t.y*J;\n\t\tval[2] = H_u*t.z*J;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPipetteAspiration::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEModel& fem = *GetFEModel();\n    FEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(m_contact));\n    FESurface& surf = *pci->GetPrimarySurface();\n\n\tsurf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& kab) {\n\n        FESurfaceElement& sel = *mp.SurfaceElement();\n        // evaluate pressure at this material point\n        double P = (m_tag[sel.m_lid][mp.m_index]) ? -m_pressure(mp) : 0;\n       if (surf.IsShellBottom()) P = -P;\n        \n\t\tdouble H_i  = dof_a.shape;\n\t\tdouble Gr_i = dof_a.shape_deriv_r;\n\t\tdouble Gs_i = dof_a.shape_deriv_s;\n\n\t\tdouble H_j  = dof_b.shape;\n\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\tvec3d vab(0,0,0);\n        vab = (mp.dxs*Gr_j - mp.dxr*Gs_j)*(P*H_i);\n\n\t\tmat3da K(vab);\n\t\tkab.set(0, 0, K);\n\t});\n}\n"
  },
  {
    "path": "FEBioMech/FEPipetteAspiration.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! The pressure surface is a surface domain that sustains pressure boundary\n//! conditions\n//!\nclass FEPipetteAspiration : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n    FEPipetteAspiration(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\npublic:\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! label material points that get pressure\n    void PrepStep() override;\n\nprotected:\n\tFEParamDouble\tm_pressure;\t//!< pressure value\n    double          m_radius;   //!< pipette radius\n    vec3d           m_center;   //!< pipette center\n    vec3d           m_normal;   //!< pipette normal direction\nprivate:\n    int             m_contact;  //!< contact surface on which pipette is used\n    std::vector< std::vector<bool>>   m_tag;  //!< tag for material points of surface\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPlasticFlowCurve.cpp",
    "content": "//\n//  FEPlasticFlowCurve.cpp\n//  FEBioMech\n//\n//  Created by Gerard Ateshian on 9/6/21.\n//  Copyright © 2021 febio.org. All rights reserved.\n//\n\n#include \"FEPlasticFlowCurve.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n//        F E P L A S T I C F L O W C U R V E M A T E R I A L P O I N T\n//-----------------------------------------------------------------------------\n//! Create a shallow copy of the material point data\nFEMaterialPointData* FEPlasticFlowCurveMaterialPoint::Copy()\n{\n    FEPlasticFlowCurveMaterialPoint* pt = new FEPlasticFlowCurveMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes material point data.\nvoid FEPlasticFlowCurveMaterialPoint::Init()\n{\n    // don't forget to initialize the base class\n    FEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlasticFlowCurveMaterialPoint::Serialize(DumpStream& ar)\n{\n    FEMaterialPointData::Serialize(ar);\n    \n    ar & m_Ky & m_w;\n    ar & m_binit;\n}\n\n//-----------------------------------------------------------------------------\n//              F E P L A S T I C F L O W C U R V E\n//-----------------------------------------------------------------------------\nvector<double> FEPlasticFlowCurve::BondYieldMeasures(FEMaterialPoint& mp)\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *mp.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    return fp.m_Ky;\n}\n\n//-----------------------------------------------------------------------------\nvector<double> FEPlasticFlowCurve::BondMassFractions(FEMaterialPoint& mp)\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *mp.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    return fp.m_w;\n}\n\n//-----------------------------------------------------------------------------\nsize_t FEPlasticFlowCurve::BondFamilies(FEMaterialPoint& mp)\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *mp.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    return fp.m_Ky.size();\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEPlasticFlowCurve::CreateMaterialPointData()\n{\n    return new FEPlasticFlowCurveMaterialPoint(new FEMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//              F E P L A S T I C F L O W C U R V E P A P E R\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPlasticFlowCurvePaper, FEPlasticFlowCurve)\n    ADD_PARAMETER(m_Ymin   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"Y0\"  );\n    ADD_PARAMETER(m_Ymax   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"Ymax\");\n    ADD_PARAMETER(m_wmin   , FE_RANGE_CLOSED(0.0, 1.0)     , \"w0\"  );\n    ADD_PARAMETER(m_we     , FE_RANGE_CLOSED(0.0, 1.0)     , \"we\"  );\n    ADD_PARAMETER(m_n      , FE_RANGE_GREATER(0)           , \"nf\"  );\n    ADD_PARAMETER(m_bias   , FE_RANGE_LEFT_OPEN(0.0, 1.0)  , \"r\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEPlasticFlowCurvePaper::FEPlasticFlowCurvePaper(FEModel* pfem) : FEPlasticFlowCurve(pfem)\n{\n    m_n = 1;\n    m_wmin = 1;\n    m_wmax = 1;\n    m_we = 0;\n    m_Ymin = 0;\n    m_Ymax = 0;\n    m_bias = 0.9;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEPlasticFlowCurvePaper::InitFlowCurve(FEMaterialPoint& mp)\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *mp.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    \n    if (fp.m_binit == false) {\n        double wmin = m_wmin(mp);\n        double wmax = m_wmax(mp);\n        double we = m_we(mp);\n        double Ymin = m_Ymin(mp);\n        double Ymax = m_Ymax(mp);\n        double bias = m_bias(mp);\n        wmax = 1 - we;\n        if (wmax < wmin) {\n            if (m_n ==1)\n                feLogError(\"w0 + we = 1 must be satisfied\");\n            else\n                feLogError(\"w0 + we < 1 must be satisfied\");\n            return false;\n        }\n        \n        fp.m_Ky.assign(m_n,0);\n        fp.m_w.assign(m_n+1,0);\n        vector<double> Kp(m_n,0);\n        \n        if (m_n == 1) {\n            fp.m_Ky[0] = Ymin;\n            fp.m_w[0] = wmin;\n        }\n        else {\n            // use bias r to reduce intervals in Ky and w as they increase proportionally\n            double r = bias;\n            // r= 1 uses uniform intervals\n            if (r == 1) {\n                fp.m_w[0] = wmin;\n                Kp[0] = Ymin;\n                fp.m_Ky[0] = Kp[0];\n                double sw = fp.m_w[0];\n                for (int i=1; i<m_n; ++i) {\n                    fp.m_w[i] = (wmax - wmin)/(m_n-1);\n                    Kp[i] = Ymin + (Ymax - Ymin)*i/(m_n-1);\n                    fp.m_Ky[i] = fp.m_Ky[i-1] + (Kp[i]-Kp[i-1])/(1-sw);\n                    sw += fp.m_w[i];\n                }\n            }\n            else {\n                double c = (1-r)/(1-pow(r, m_n-1));\n                fp.m_w[0] = wmin;\n                fp.m_w[1] = c*(wmax-wmin);\n                Kp[0] = Ymin;\n                Kp[1] = Kp[0] + c*(Ymax - Ymin);\n                double sw = fp.m_w[0];\n                fp.m_Ky[0] = Kp[0];\n                fp.m_Ky[1] = fp.m_Ky[0] + (Kp[1]-Kp[0])/(1-sw);\n                sw += fp.m_w[1];\n                for (int i=2; i<m_n; ++i) {\n                    fp.m_w[i] = fp.m_w[i-1]*r;\n                    Kp[i] = Kp[i-1] + (Kp[i-1]-Kp[i-2])*r;\n                    fp.m_Ky[i] = fp.m_Ky[i-1] + (Kp[i]-Kp[i-1])/(1-sw);\n                    sw += fp.m_w[i];\n                }\n            }\n        }\n        fp.m_w[m_n] = we;\n        \n        fp.m_binit = true;\n        \n        return true;\n    }\n\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n//              F E P L A S T I C F L O W C U R V E U S E R\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPlasticFlowCurveUser, FEPlasticFlowCurve)\n    ADD_PROPERTY(m_Y  , \"plastic_response\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEPlasticFlowCurveUser::FEPlasticFlowCurveUser(FEModel* pfem) : FEPlasticFlowCurve(pfem)\n{\n    m_Y = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEPlasticFlowCurveUser::InitFlowCurve(FEMaterialPoint& mp)\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *mp.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    \n    if (fp.m_binit == false) {\n        m_Y->Init();\n        \n        // get number of points on flow curve\n        int m_n = m_Y->Points();\n        \n        fp.m_Ky.assign(m_n,0);\n        fp.m_w.assign(m_n+1,0);\n        vector<double> Kp(m_n,0);\n        // get first point on assumption that it represents the yield point\n        // and extract Young's modulus\n        LOADPOINT p = m_Y->LoadPoint(0);\n        double Ey = p.value/p.time;\n        for (int i=0; i<m_n; ++i) {\n            LOADPOINT p = m_Y->LoadPoint(i);\n            Kp[i] = p.value;\n            fp.m_Ky[i] = Ey*p.time;\n        }\n        \n        double sw = 0;\n        if (m_n == 1) {\n            fp.m_w[0] = 1;\n            fp.m_w[m_n] = 0;\n        }\n        else {\n            for (int i=0; i<m_n-1; ++i) {\n                fp.m_w[i] = 1 - (Kp[i+1]-Kp[i])/(fp.m_Ky[i+1]-fp.m_Ky[i]) - sw;\n                sw += fp.m_w[i];\n            }\n            fp.m_w[m_n-1] = 1 - sw;\n            fp.m_w[m_n] = 0;\n        }\n        fp.m_binit = true;\n        return true;\n    }\n\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n//              F E P L A S T I C F L O W C U R V E M A T H\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPlasticFlowCurveMath, FEPlasticFlowCurve)\n    ADD_PARAMETER(m_n      , FE_RANGE_GREATER(0)           , \"nf\"  );\n    ADD_PARAMETER(m_emin   , FE_RANGE_GREATER(0)           , \"e0\"  );\n    ADD_PARAMETER(m_emax   , FE_RANGE_GREATER(0)           , \"emax\");\n    ADD_PARAMETER(m_Ymath, \"plastic_response\")->setLongName(\"plastic flow curve\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEPlasticFlowCurveMath::FEPlasticFlowCurveMath(FEModel* pfem) : FEPlasticFlowCurve(pfem)\n{\n    m_n = 1;\n    m_emin = 0;\n    m_emax = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEPlasticFlowCurveMath::InitFlowCurve(FEMaterialPoint& mp)\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *mp.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    \n    if (fp.m_binit == false) {\n\n        FEMathFunction Y(GetFEModel());\n        Y.SetMathString(m_Ymath);\n        if (Y.Init() == false) return false;\n        \n        fp.m_Ky.assign(m_n,0);\n        fp.m_w.assign(m_n+1,0);\n        vector<double> Kp(m_n,0);\n        if (m_n == 1) {\n            fp.m_w[0] = 1;\n            fp.m_w[m_n] = 0;\n            Kp[0] = fp.m_Ky[0] = Y.value(m_emin);\n        }\n        else {\n            // set uniform increments in Kp and find corresponding strains\n            // then evaluate Ky at those strains\n            Kp[0] = fp.m_Ky[0] = Y.value(m_emin);\n            Kp[m_n-1] = Y.value(m_emax);\n            // Extract Young's modulus\n            double Ey = Kp[0]/m_emin;\n            double dKp = (Kp[m_n-1] - Kp[0])/(m_n-1);\n            double e = m_emin;\n            vector<double> enat(m_n,0);\n            enat[0] = m_emin;\n            for (int i=1; i<m_n; ++i) {\n                Kp[i] = Kp[0] + i*dKp;\n                if (Y.invert(Kp[i], e) == false) return false;\n                fp.m_Ky[i] = Ey*e;\n                enat[i] = e;\n            }\n            // evaluate bond mass fractions\n            fp.m_w[0] = (fp.m_Ky[1] - Kp[1])/(fp.m_Ky[1] - Kp[0]);\n            double sw = fp.m_w[0];\n            for (int i=2; i<m_n; ++i) {\n                fp.m_w[i-1] = 1 - sw - (Kp[i] - Kp[i-1])/(fp.m_Ky[i] - fp.m_Ky[i-1]);\n                sw += fp.m_w[i-1];\n            }\n            fp.m_w[m_n-1] = 1 - sw;\n            fp.m_w[m_n] = 0;\n        }\n        \n        fp.m_binit = true;\n        return FEPlasticFlowCurve::Init();\n    }\n    return false;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEPlasticFlowCurve.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/FEPointFunction.h>\n#include \"febiomech_api.h\"\n\nclass FEPlasticFlowCurve;\n\n//-----------------------------------------------------------------------------\n// Material point for plastic flow curve\n class FEPlasticFlowCurveMaterialPoint : public FEMaterialPointData\n{\npublic:\n    FEPlasticFlowCurveMaterialPoint(FEMaterialPointData* pt) : FEMaterialPointData(pt) { m_binit = false; }\n    \n    //! copy material point data\n    FEMaterialPointData* Copy() override;\n    \n    //! Initialize material point data\n    void Init() override;\n    \n    //! Serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    vector<double>  m_Ky;       //!< bond yield measures\n    vector<double>  m_w;        //!< bond mass fractions\n    bool            m_binit;    //!< flag for initialization\n};\n\n//-----------------------------------------------------------------------------\n// Virtual base class for plastic flow curve\n\nclass FEBIOMECH_API FEPlasticFlowCurve : public FEMaterialProperty\n{\npublic:\n    FEPlasticFlowCurve(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\n    //! return\n    vector<double> BondYieldMeasures(FEMaterialPoint& mp);\n    vector<double> BondMassFractions(FEMaterialPoint& mp);\n    size_t BondFamilies(FEMaterialPoint& mp);\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // initialize flow curve. Return true upon successful initialization,\n    // false otherwise\n    virtual bool InitFlowCurve(FEMaterialPoint& mp) = 0;\n    \n    FECORE_BASE_CLASS(FEPlasticFlowCurve);\n};\n\n//-----------------------------------------------------------------------------\n// Flow curve from reactive plasticity paper\n\nclass FEPlasticFlowCurvePaper : public FEPlasticFlowCurve\n{\npublic:\n    FEPlasticFlowCurvePaper(FEModel* pfem);\n    \n    bool InitFlowCurve(FEMaterialPoint& mp) override;\n    \npublic:\n    FEParamDouble   m_wmin;     // initial fraction of yielding bonds\n    FEParamDouble   m_wmax;     // final fraction of yielding bonds\n    FEParamDouble   m_we;       // fraction of unyielding bonds\n    FEParamDouble   m_Ymin;     // initial yield measure\n    FEParamDouble   m_Ymax;     // yield measure when all bonds have yielded\n    int             m_n;        // number of yield levels\n    FEParamDouble   m_bias;     // biasing factor for intervals in yield measures and bond fractions\n\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// User-defined flow curve\n\nclass FEPlasticFlowCurveUser : public FEPlasticFlowCurve\n{\npublic:\n    FEPlasticFlowCurveUser(FEModel* pfem);\n    \n    bool InitFlowCurve(FEMaterialPoint& mp) override;\n    \npublic:\n    FEPointFunction*    m_Y;    //!< true stress-true strain flow curve\n\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Math equation for flow curve\n\nclass FEPlasticFlowCurveMath : public FEPlasticFlowCurve\n{\npublic:\n    FEPlasticFlowCurveMath(FEModel* pfem);\n    \n    bool InitFlowCurve(FEMaterialPoint& mp) override;\n    \npublic:\n    int                 m_n;    //!< number of yield levels\n    double              m_emin; //!< min true strain\n    double              m_emax; //!< max true strain\n    std::string         m_Ymath;    //!< true stress-true strain flow curve\n    \n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMech/FEPointBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPointBodyForce.h\"\n#include <FECore/FEMesh.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPointBodyForce, FEBodyForce);\n\tADD_PARAMETER(m_a, \"a\");\n\tADD_PARAMETER(m_b, \"b\");\n\tADD_PARAMETER(m_rc, \"rc\");\n\tADD_PARAMETER(m_inode, \"node\");\n\tADD_PARAMETER(m_brigid, \"rigid\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPointBodyForce::FEPointBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n\tm_pel = 0; \n\tm_brigid = true; \n\tm_inode = -1; \n}\n\n//-----------------------------------------------------------------------------\nvec3d FEPointBodyForce::force(FEMaterialPoint& mp)\n{\n\tvec3d x = mp.m_rt;\n\tvec3d n = x - m_rc;\n\tdouble l = n.unit();\n\n\tdouble g = m_a*exp(-m_b*l);\n\treturn n*g;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPointBodyForce::divforce(FEMaterialPoint& mp)\n{\n    vec3d x = mp.m_rt;\n    vec3d n = x - m_rc;\n    double l = n.unit();\n    \n    double g = m_a*exp(-m_b*l);\n    return (3-m_b*l)*g;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEPointBodyForce::stiffness(FEMaterialPoint &mp)\n{\n\tvec3d x = mp.m_rt;\n\tvec3d n = x - m_rc;\n\tdouble l = n.unit();\n\n\tmat3ds k;\n\tif (l == 0.0)\n\t{\n\t\tk.zero();\n\t}\n\telse\n\t{\n\t\tdouble g = m_a*exp(-m_b*l);\n\n\t\tmat3ds nxn = dyad(n);\n\t\tmat3ds I = mat3dd(1.0);\n\n\t\tk = (nxn*m_b - (I - nxn)/l)*g;\n\t}\n\n\treturn k;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointBodyForce::Serialize(DumpStream &ar)\n{\n\tFEBodyForce::Serialize(ar);\n\tar & m_a & m_b & m_rc;\n\tar & m_inode & m_brigid;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPointBodyForce::Init()\n{\n\tFEMesh& m = GetMesh();\n\tif (m_inode == -1)\n\t{\n\t\tif (!m_brigid)\n\t\t{\n\t\t\t// find the element in which point r0 lies\n\t\t\tm_pel = m.FindSolidElement(m_rc, m_rs);\n\t\t}\n\t\telse m_pel = 0;\n\t}\n\telse \n\t{\n\t\tm_rc = m.Node(m_inode).m_r0;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Update the position of the body force\nvoid FEPointBodyForce::Update()\n{\n\tif (m_inode == -1)\n\t{\n\t\tif (m_pel)\n\t\t{\n\t\t\tFEMesh& m = GetMesh();\n\t\t\tvec3d x[FEElement::MAX_NODES];\n\t\t\tfor (int i=0; i<8; ++i) x[i] = m.Node(m_pel->m_node[i]).m_rt;\n\n\t\t\tdouble* r = m_rs;\n\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\tH[0] = 0.125*(1 - r[0])*(1 - r[1])*(1 - r[2]);\n\t\t\tH[1] = 0.125*(1 + r[0])*(1 - r[1])*(1 - r[2]);\n\t\t\tH[2] = 0.125*(1 + r[0])*(1 + r[1])*(1 - r[2]);\n\t\t\tH[3] = 0.125*(1 - r[0])*(1 + r[1])*(1 - r[2]);\n\t\t\tH[4] = 0.125*(1 - r[0])*(1 - r[1])*(1 + r[2]);\n\t\t\tH[5] = 0.125*(1 + r[0])*(1 - r[1])*(1 + r[2]);\n\t\t\tH[6] = 0.125*(1 + r[0])*(1 + r[1])*(1 + r[2]);\n\t\t\tH[7] = 0.125*(1 - r[0])*(1 + r[1])*(1 + r[2]);\n\n\t\t\tm_rc = vec3d(0,0,0);\n\t\t\tfor (int i=0; i<8; ++i) m_rc += x[i]*H[i];\n\t\t}\n\t}\n\telse\n\t{\n\t\tFEMesh& m = GetMesh();\n\t\tm_rc = m.Node(m_inode).m_rt;\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEPointBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBodyForce.h\"\n#include <FECore/FESolidElement.h>\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEPointBodyForce : public FEBodyForce\n{\npublic:\n\tFEPointBodyForce(FEModel* pfem);\n\n\tvec3d force(FEMaterialPoint& mp) override;\n\tdouble divforce(FEMaterialPoint& mp) override;\n\tmat3d stiffness(FEMaterialPoint& mp) override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tbool Init() override;\n\tvoid Update() override;\n\npublic:\n\tdouble\tm_a, m_b;           //!< coefficients of exponential decay of body force\n\tvec3d\tm_rc;               //!< center point of body force\n\t\n\tint\t\tm_inode;            //!< node number of center of body force, or -1 if not a node\n\n\tbool\tm_brigid;           //!< flag if center point is located within a rigid body\n\n\tFESolidElement* m_pel;\t\t//!< element in which point m_r0 lies\n\tdouble\t\t\tm_rs[3];\t//!< isoparametric coordinates\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPointConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPointConstraint.h\"\n#include \"FECore/FEMesh.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPointConstraint, FENLConstraint)\n\tADD_PARAMETER(m_eps    , \"penalty\");\n\tADD_PARAMETER(m_node_id, \"node\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPointConstraint::FEPointConstraint(FEModel* pfem) : FENLConstraint(pfem), m_dofU(pfem)\n{\n\tm_node_id = -1;\n\tm_eps = 0.0;\n\n\tm_node = -1;\n\tm_pel = 0;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEPointConstraint::Init()\n{\n\tFEMesh& m = GetMesh();\n\tif ((m_node_id <= 0)||(m_node_id > m.Nodes())) return false;\n\n\t// get the nodal position in the reference state\n\tm_node = m_node_id - 1;\n\tvec3d r = m.Node(m_node).m_r0;\n\n\t// find the element in which this node lies\n\tm_pel = m.FindSolidElement(r, m_rs);\n\tif (m_pel == 0) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointConstraint::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\t// TODO: implement this\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointConstraint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tFEMesh& mesh = GetMesh();\n\tvector<int> lm(3*9);\n\tFENode& n0 = mesh.Node(m_node);\n\tlm[0] = n0.m_ID[m_dofU[0]];\n\tlm[1] = n0.m_ID[m_dofU[1]];\n\tlm[2] = n0.m_ID[m_dofU[2]];\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\tFENode& ni = mesh.Node(m_pel->m_node[i]);\n\t\tlm[3*(i+1)  ] = ni.m_ID[m_dofU[0]];\n\t\tlm[3*(i+1)+1] = ni.m_ID[m_dofU[1]];\n\t\tlm[3*(i+1)+2] = ni.m_ID[m_dofU[2]];\n\t}\n\tM.build_add(lm);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint i;\n\tFEMesh& m = GetMesh();\n\n\t// calculate H matrix\n\tdouble H[9], *r = m_rs;\n\tH[0] = 1.0;\n\tH[1] = -0.125*(1 - r[0])*(1 - r[1])*(1 - r[2]);\n\tH[2] = -0.125*(1 + r[0])*(1 - r[1])*(1 - r[2]);\n\tH[3] = -0.125*(1 + r[0])*(1 + r[1])*(1 - r[2]);\n\tH[4] = -0.125*(1 - r[0])*(1 + r[1])*(1 - r[2]);\n\tH[5] = -0.125*(1 - r[0])*(1 - r[1])*(1 + r[2]);\n\tH[6] = -0.125*(1 + r[0])*(1 - r[1])*(1 + r[2]);\n\tH[7] = -0.125*(1 + r[0])*(1 + r[1])*(1 + r[2]);\n\tH[8] = -0.125*(1 - r[0])*(1 + r[1])*(1 + r[2]);\n\n\t// get the nodal position\n\tvec3d x[9];\n\tx[0] = m.Node(m_node).m_rt;\n\tfor (i=0; i<8; ++i) x[i+1] = m.Node(m_pel->m_node[i]).m_rt;\n\n\t// calculate the constraint\n\tvec3d c(0,0,0);\n\tfor (i=0; i<9; ++i) c += x[i]*H[i];\n\n\t// calculate the force\n\tvec3d T = c*m_eps;\n\n\t// setup the LM matrix\n\tvector<int> LM(3*9), en(9);\n\ten[0] = m_node;\n\tLM[0] = m.Node(m_node).m_ID[m_dofU[0]];\n\tLM[1] = m.Node(m_node).m_ID[m_dofU[1]];\n\tLM[2] = m.Node(m_node).m_ID[m_dofU[2]];\n\tfor (i=0; i<8; ++i)\n\t{\n\t\ten[i+1] = m_pel->m_node[i];\n\t\tFENode& node = m.Node(en[i+1]);\n\t\tLM[(i+1)*3  ] = node.m_ID[m_dofU[0]];\n\t\tLM[(i+1)*3+1] = node.m_ID[m_dofU[1]];\n\t\tLM[(i+1)*3+2] = node.m_ID[m_dofU[2]];\n\t}\n\n\t// set up nodal force vector\n\tvector<double> fe(3*9);\n\tfor (int i=0; i<9; ++i)\n\t{\n\t\tfe[3*i  ] = -T.x*H[i];\n\t\tfe[3*i+1] = -T.y*H[i];\n\t\tfe[3*i+2] = -T.z*H[i];\n\t}\n\n\t// assemble residual\n\tR.Assemble(en, LM, fe);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEMesh& m = GetMesh();\n\n\t// calculate H matrix\n\tdouble H[9], *r = m_rs;\n\tH[0] = 1.0;\n\tH[1] = -0.125*(1 - r[0])*(1 - r[1])*(1 - r[2]);\n\tH[2] = -0.125*(1 + r[0])*(1 - r[1])*(1 - r[2]);\n\tH[3] = -0.125*(1 + r[0])*(1 + r[1])*(1 - r[2]);\n\tH[4] = -0.125*(1 - r[0])*(1 + r[1])*(1 - r[2]);\n\tH[5] = -0.125*(1 - r[0])*(1 - r[1])*(1 + r[2]);\n\tH[6] = -0.125*(1 + r[0])*(1 - r[1])*(1 + r[2]);\n\tH[7] = -0.125*(1 + r[0])*(1 + r[1])*(1 + r[2]);\n\tH[8] = -0.125*(1 - r[0])*(1 + r[1])*(1 + r[2]);\n\n\n\t// setup the LM matrix\n\tvector<int> LM(3*9), en(9);\n\ten[0] = m_node;\n\tLM[0] = m.Node(m_node).m_ID[m_dofU[0]];\n\tLM[1] = m.Node(m_node).m_ID[m_dofU[1]];\n\tLM[2] = m.Node(m_node).m_ID[m_dofU[2]];\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\ten[i+1] = m_pel->m_node[i];\n\t\tFENode& node = m.Node(en[i+1]);\n\t\tLM[(i+1)*3  ] = node.m_ID[m_dofU[0]];\n\t\tLM[(i+1)*3+1] = node.m_ID[m_dofU[1]];\n\t\tLM[(i+1)*3+2] = node.m_ID[m_dofU[2]];\n\t}\n\n\t// setup stiffness matrix\n\tint ndof = 3*9;\n\tFEElementMatrix ke(ndof, ndof); ke.zero();\n\tfor (int i=0; i<9; ++i)\n\t\tfor (int j=0; j<9; ++j)\n\t\t{\n\t\t\tke[3*i  ][3*j  ] = m_eps*H[i]*H[j];\n\t\t\tke[3*i+1][3*j+1] = m_eps*H[i]*H[j];\n\t\t\tke[3*i+2][3*j+2] = m_eps*H[i]*H[j];\n\t\t}\n\tke.SetIndices(LM);\n\tke.SetNodes(en);\n\n\t// assemble stiffness matrix\n\tLS.Assemble(ke);\n}\n"
  },
  {
    "path": "FEBioMech/FEPointConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENLConstraint.h>\n#include <FECore/FESolidElement.h>\n#include <FECore/FEGlobalVector.h>\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a point constraint. That is, it forces a node of a \n//! mesh in the same relative position with respect to the element in which the \n//! node is located.\nclass FEPointConstraint : public FENLConstraint\n{\npublic:\n\t//! constructor\n\tFEPointConstraint(FEModel* pfem);\n\n\t//! initialize data\n\tbool Init() override;\n\n\t//! serialize\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Calculate the constraint force\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate the constraint stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! augmentations \\todo implement this\n\tbool Augment(int naug, const FETimeInfo& tp) override { return true; }\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\npublic:\n\tdouble\t\tm_eps;\t\t//!< penalty parameter\n\tint\t\t\tm_node_id;\t//!< id of node\n\npublic:\n\tint\t\t\t\t\tm_node;\t\t//!< node to which the constraint is applied\n\tFESolidElement*\t\tm_pel;\t\t//!< element in which the node is located.\n\tdouble\t\t\t\tm_rs[3];\t//!< natural coordinates in element m_pel\n\n\tFEDofList\tm_dofU;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPolynomalHyperElastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPolynomialHyperElastic.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPolynomialHyperElastic, FEElasticMaterial)\n//\tADD_PARAMETER(m_c[0][0], \"c00\");  // should always be zero\n\tADD_PARAMETER(m_c[0][1], \"c01\");\n\tADD_PARAMETER(m_c[0][2], \"c02\");\n\tADD_PARAMETER(m_c[1][0], \"c10\");\n\tADD_PARAMETER(m_c[1][1], \"c11\");\n\tADD_PARAMETER(m_c[1][2], \"c12\");\n\tADD_PARAMETER(m_c[2][0], \"c20\");\n\tADD_PARAMETER(m_c[2][1], \"c21\");\n\tADD_PARAMETER(m_c[2][2], \"c22\");\n\tADD_PARAMETER(m_D1, \"D1\");\n\tADD_PARAMETER(m_D2, \"D2\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPolynomialHyperElastic::FEPolynomialHyperElastic(FEModel* pfem) : FEElasticMaterial(pfem) \n{\n\tm_D1 = m_D2 = 0.0;\n\tfor (int i = 0; i < 3; ++i)\n\t\tfor (int j = 0; j < 3; ++j) m_c[i][j] = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric stress\nmat3ds FEPolynomialHyperElastic::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5 * (I1 * I1 - B2.tr());\n\n\t// get material parameters\n\tdouble c[3][3] = { 0 };\n\tfor (int i = 0; i < 3; ++i)\n\t\tfor (int j = 0; j < 3; ++j)\n\t\t\tc[i][j] = m_c[i][j](mp);\n\tc[0][0] = 0.0;\n\n\t// --- put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble I1p1 = I1 - 3.0;\n\tdouble I1p2 = I1p1 * I1p1;\n\tdouble I2p1 = I2 - 3.0;\n\tdouble I2p2 = I2p1 * I2p1;\n\n\tdouble W1 = c[1][0] + c[1][1] * I2p1 + c[1][2] * I2p2 + 2.0 * I1p1 * (c[2][0] + c[2][1] * I2p1 + c[2][2] * I2p2);\n\tdouble W2 = c[0][1] + c[1][1] * I1p1 + c[2][1] * I1p2 + 2.0 * I2p1 * (c[0][2] + c[1][2] * I1p1 + c[2][2] * I1p2);\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\t// T = F*dW/dC*Ft\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\tpt.m_p = UJ(pt.m_J);\n\n\treturn mat3dd(pt.m_p) + devs;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FEPolynomialHyperElastic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0 / J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5 * (I1 * I1 - B2.tr());\n\n\t// get material parameters\n\tdouble c[3][3] = { 0 };\n\tfor (int i = 0; i < 3; ++i)\n\t\tfor (int j = 0; j < 3; ++j)\n\t\t\tc[i][j] = m_c[i][j](mp);\n\tc[0][0] = 0.0;\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble I1p1 = I1 - 3.0;\n\tdouble I1p2 = I1p1 * I1p1;\n\tdouble I2p1 = I2 - 3.0;\n\tdouble I2p2 = I2p1 * I2p1;\n\n\tdouble W1 = c[1][0] + c[1][1] * I2p1 + c[1][2] * I2p2 + 2.0 * I1p1 * (c[2][0] + c[2][1] * I2p1 + c[2][2] * I2p2);\n\tdouble W2 = c[0][1] + c[1][1] * I1p1 + c[2][1] * I1p2 + 2.0 * I2p1 * (c[0][2] + c[1][2] * I1p1 + c[2][2] * I1p2);\n\n\tdouble W11 = 2.0 * (c[2][0] + c[2][1] * I2p1 + c[2][2] * I2p2);\n\tdouble W22 = 2.0 * (c[0][2] + c[1][2] * I1p1 + c[2][2] * I1p2);\n\n\tdouble W12 = c[1][1] + 2.0 * c[1][2] * I2p1 + 2.0 * c[2][1] * I1p1 + 4.0 * c[2][2]*I1p1 * I2p1;\n\tdouble W21 = W12;\n\t// ---\n\n\t// define some helper constants\n\tdouble g1 = W11 + 2.0 * W12 * I1 + W22 * I1 * I1 + W2;\n\tdouble g2 = W12 + I1 * W22;\n\tdouble I2b = B2.tr();\n\n\t// calculate dWdC:C\n\tdouble WC = W1 * I1 + 2 * W2 * I2;\n\n\t// deviatoric cauchy-stress\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// Identity tensor\n\tmat3ds I(1, 1, 1, 0, 0, 0);\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4 = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B2xB2 = dyad1s(B2);\n\ttens4ds B4 = dyad4s(B);\n\n\t// calculate chat\n\ttens4ds ch = BxB * g1 - dyad1s(B, B2) * g2 + B2xB2 * W22 - B4 * W2;\n\n\t// calculate I:ch:I\n\tdouble IchI = W11*I1*I1 + 2.0*W12*I1*I2 + 2.0*W21*I1*I2 + 4.0*W22*I2*I2 + 2.0*W2*I2;\n\n\t// 1:ch\n\tmat3ds Ich = B * (g1*I1-g2*I2b) + B2 * (W22*I2b - g2*I1 - W2);\n\n\ttens4ds cw = ch - dyad1s(Ich, I) * (1.0 / 3.0) + IxI * (1.0 / 9.0 * IchI);\n\n\ttens4ds cs = dyad1s(devs, I)* (-2.0 / 3.0) + (I4 - IxI / 3.0) * (4.0 / 3.0 * Ji * WC);\n\ttens4ds ce = cs + cw*(4.0 * Ji);\n\n\treturn ce + (IxI - I4 * 2) * pt.m_p + IxI * (UJJ(pt.m_J) * pt.m_J);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEPolynomialHyperElastic::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get material parameters\n\tdouble c[3][3] = { 0 };\n\tfor (int i = 0; i < 3; ++i)\n\t\tfor (int j = 0; j < 3; ++j)\n\t\t\tc[i][j] = m_c[i][j](mp);\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5 * (I1 * I1 - B2.tr());\n\n\tdouble W = 0.0;\n\tfor (int i = 0; i < 3; ++i)\n\t\tfor (int j = 0; j < 3; ++j)\n\t\t\tW += c[i][j] * pow(I1 - 3, i) * pow(I2 - 3, j);\n\n\treturn U(pt.m_J) + W;\n}\n\ndouble FEPolynomialHyperElastic::U(double J)\n{\n\treturn m_D1 * pow(J - 1.0, 2.0) + m_D2 * pow(J - 1.0, 4.0);\n}\n\ndouble FEPolynomialHyperElastic::UJ(double J)\n{\n\treturn 2.0*m_D1 * (J - 1.0) + 4.0*m_D2 * pow(J - 1.0, 3.0);\n}\n\ndouble FEPolynomialHyperElastic::UJJ(double J)\n{\n\treturn 2.0*m_D1 + 12.0*m_D2 * pow(J - 1.0, 2.0);\n}\n"
  },
  {
    "path": "FEBioMech/FEPolynomialHyperElastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Polynomial Hyperelastic\nclass FEPolynomialHyperElastic : public FEElasticMaterial\n{\npublic:\n\tFEPolynomialHyperElastic(FEModel* pfem);\n\npublic:\n\tFEParamDouble\tm_c[3][3];\n\tdouble\t\t\tm_D1, m_D2;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble StrainEnergyDensity(FEMaterialPoint& mp) override;\n\nprivate:\n\tdouble U(double J);\n\tdouble UJ(double J);\n\tdouble UJJ(double J);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPreStrainConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPreStrainConstraint.h\"\n#include \"FEElasticMixture.h\"\n#include \"FEUncoupledElasticMixture.h\"\n#include \"FEPreStrainElastic.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEMesh.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPreStrainConstraint, FENLConstraint)\n\tADD_PARAMETER(m_laugon , \"update\"   );\n\tADD_PARAMETER(m_tol    , \"tolerance\");\n\tADD_PARAMETER(m_naugmin, \"min_iters\");\n\tADD_PARAMETER(m_naugmax, \"max_iters\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPreStrainConstraint::FEPreStrainConstraint(FEModel* pfem) : FENLConstraint(pfem)\n{\n\tm_laugon = true; // This feature requires augmentations so turn it on\n\tm_naugmin = 0;\n\tm_naugmax = -1;\n\tm_tol = 0.0;\n}\n\nbool FEPreStrainConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (IsActive() == false) return true;\n\tif (m_laugon == false) return true;\n\n\tFEMesh& m = GetMesh();\n\tint ND = m.Domains();\n\n\t// do pre-strain augmentations\n\tbool bconv = true;\n\tfor (int i=0; i<ND; ++i)\n\t{\n\t\tFEDomain& dom = m.Domain(i);\n\t\tFEMaterial* pmat = dom.GetMaterial();\n\t\tFEPrestrainMaterial* pm = dynamic_cast<FEPrestrainMaterial*>(pmat);\n\t\tif (pm) bconv &= Augment(&dom, 0, naug);\n\t\telse\n\t\t{\n\t\t\tfor (int j=0; j<pmat->Properties(); ++j)\n\t\t\t{\n\t\t\t\tFEPrestrainMaterial* pmj = dynamic_cast<FEPrestrainMaterial*>(pmat->GetProperty(j));\n\t\t\t\tif (pmj) bconv &= Augment(&dom, j, naug);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Check an augmentation for a specific domain/material pair\nbool FEPreStrainConstraint::Augment(FEDomain* psd, int n, int naug)\n{\n\t// make sure this is a prestrain material\n\tFEPrestrainMaterial* pmat = dynamic_cast<FEPrestrainMaterial*>(psd->GetMaterial());\n\tif (pmat == nullptr) return true;\n\n\t// check convergence\n\tdouble\tmax_err = 0;\n\tbool bconv = true;\n\tint NE = psd->Elements();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\tFEElement& el = psd->ElementRef(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int i=0; i<nint; ++i)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *(el.GetMaterialPoint(i)->GetPointData(n));\n\t\t\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\tFEPrestrainMaterialPoint& pt = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\tconst mat3d& Fc = pt.PrestrainCorrection();\n\t\t\tconst mat3d& F = ep.m_F;\n\n\t\t\tmat3d Fc_next = UpdateFc(F, Fc, mp, pmat);\n\n\t\t\tmat3d U = Fc_next - Fc;\n\t\t\tdouble normU = U.norm();\n\t\t\tif (normU >= max_err) max_err = normU;\n\t\t}\n\t}\n\tif ((m_tol > 0) && (max_err >= m_tol)) bconv = false;\n\n\tfeLog(\"max norm = %lg (%lg)\\n\", max_err, m_tol);\n\n\t// ensure we have done the required min or max augmentations\n\tif (naug <  m_naugmin) bconv = false;\n\tif ((m_naugmax >= 0)&&(naug >= m_naugmax)) bconv = true;\n\n\t// only augment when we did not converge\n\tif (bconv == false)\n\t{\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFEElement& el = psd->ElementRef(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int i=0; i<nint; ++i)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *(el.GetMaterialPoint(i)->GetPointData(n));\n\t\t\t\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tFEPrestrainMaterialPoint& pt = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\n\t\t\t\tconst mat3d& Fc = pt.PrestrainCorrection();\n\t\t\t\tconst mat3d& F = ep.m_F;\n\n\t\t\t\tmat3d Fc_next = UpdateFc(F, Fc, mp, pmat);\n\t\t\t\tpt.setPrestrainCorrection(Fc_next);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEGPAConstraint::UpdateFc(const mat3d& F, const mat3d& Fc_prev, FEMaterialPoint& mp, FEPrestrainMaterial* pmat)\n{\n\treturn F * Fc_prev;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEInSituStretchConstraint, FEPreStrainConstraint)\n\tADD_PARAMETER(m_max_stretch, \"max_stretch\");\n\tADD_PARAMETER(m_biso       , \"isochoric\"  );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEInSituStretchConstraint::FEInSituStretchConstraint(FEModel* pfem) : FEPreStrainConstraint(pfem)\n{\n\tm_max_stretch = 0.0;\n\tm_biso = true;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEInSituStretchConstraint::UpdateFc(const mat3d& F, const mat3d& Fc_prev, FEMaterialPoint& mp, FEPrestrainMaterial* pmat)\n{\n\tFEPrestrainMaterialPoint& psp = *mp.ExtractData<FEPrestrainMaterialPoint>();\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tFEElasticMaterial* elasticMat = pmat->GetElasticMaterial();\n\tif (elasticMat == nullptr) return mat3dd(1.0);\n\n\tFEVec3dValuator* fiber = dynamic_cast<FEVec3dValuator*>(elasticMat->GetProperty(\"fiber\"));\n\tif (fiber == nullptr) return mat3dd(1.0);\n\n\t// calculate the fiber stretch\n\tmat3d Q = elasticMat->GetLocalCS(mp);\n\tvec3d a0 = fiber->unitVector(mp);\n\tvec3d ar = Q * a0;\n\tvec3d a = F*ar;\n\tdouble l = a.norm();\n\n\tif ((m_max_stretch == 0.0) ||(l <= m_max_stretch))\n\t{\n\t\tdouble li = (m_biso ? sqrt(l) : 1.0);\n\n\t\t// setup the new correction\n\t\tmat3d U(1.0/l, 0.0, 0.0, 0.0, li, 0.0, 0.0, 0.0, li);\n\t\tmat3d Fc = Q*U*Q.transpose();\n\t\treturn Fc;\n\t}\n\telse\n\t{\n\t\treturn psp.PrestrainCorrection();\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEPreStrainConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENLConstraint.h>\n#include <FECore/FESolidDomain.h>\n#include \"FEPreStrainElastic.h\"\n#include \"FEPreStrainUncoupledElastic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEPreStrainConstraint : public FENLConstraint\n{\npublic:\n\tFEPreStrainConstraint(FEModel* pfem);\n\n\t// This function must be overloaded by derived classes\n\tvirtual mat3d UpdateFc(const mat3d& F, const mat3d& Fc_prev, FEMaterialPoint& mp, FEPrestrainMaterial* pmat) = 0;\n\npublic:\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override {};\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override {};\n\tvirtual void Update(const FETimeInfo& tp) {}\n\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override {}\n\nprivate:\n\tbool Augment(FEDomain* pdom, int n, int naug);\n\npublic:\n\tbool\tm_laugon;\t//!< augmented Lagrangian flag\n\tdouble\tm_tol;\t\t//!< convergence tolerance\n\tint\t\tm_naugmin;\t//!< minimum number of iterations\n\tint\t\tm_naugmax;\t//!< maximum number of iterations\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Implements a constraint that essentially eliminates the distortion\nclass FEGPAConstraint : public FEPreStrainConstraint\n{\npublic:\n\tFEGPAConstraint(FEModel* pfem) : FEPreStrainConstraint(pfem){}\n\n\tmat3d UpdateFc(const mat3d& F, const mat3d& Fc_prev, FEMaterialPoint& mp, FEPrestrainMaterial* pmat) override;\n};\n\n//-----------------------------------------------------------------------------\n// enforces just the fiber stretch\nclass FEInSituStretchConstraint: public FEPreStrainConstraint\n{\npublic:\n\tFEInSituStretchConstraint(FEModel* pfem);\n\tmat3d UpdateFc(const mat3d& F, const mat3d& Fc_prev, FEMaterialPoint& mp, FEPrestrainMaterial* pmat) override;\n\nprivate:\n\tdouble\tm_max_stretch;\n\tbool\tm_biso;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPreStrainElastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPreStrainElastic.h\"\n\n//! constructor\nFEPrestrainMaterialPoint::FEPrestrainMaterialPoint(FEMaterialPointData* mp) : FEMaterialPointData(mp)\n{ \n\tF0.unit(); \n\tFc.unit(); \n}\n\n//! copy\nFEMaterialPointData* FEPrestrainMaterialPoint::Copy()\n{\n\tFEMaterialPointData* mp = new FEPrestrainMaterialPoint(*this);\n\tif (m_pNext) mp->SetNext(m_pNext->Copy());\n\treturn mp;\n}\n\n//! initialization\nvoid FEPrestrainMaterialPoint::Init(bool bflag)\n{\n}\n\n//! Serialization\nvoid FEPrestrainMaterialPoint::Serialize(DumpStream& dmp)\n{\n\tFEMaterialPointData::Serialize(dmp);\n\tdmp & F0 & Fc;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPrestrainElastic, FEElasticMaterial)\n\tADD_PROPERTY(m_mat, \"elastic\");\n\tADD_PROPERTY(m_Fp, \"prestrain\", false);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrestrainElastic::FEPrestrainElastic(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_mat = nullptr;\n\tm_Fp = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// evaluate density in (pre-strained) reference configuration\ndouble FEPrestrainElastic::Density(FEMaterialPoint& mp)\n{\n\tdouble d0 = FEElasticMaterial::Density(mp);\n\n\tmat3d Fp = PrestrainGradient(mp);\n\tdouble Jp = Fp.det();\n\n\treturn d0 / Jp;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEPrestrainElastic::CreateMaterialPointData()\n{ \n\tFEMaterialPointData* pm = new FEPrestrainMaterialPoint(m_mat->CreateMaterialPointData());\n\tif (m_Fp) pm->Append(m_Fp->CreateMaterialPointData());\n\treturn pm;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEPrestrainElastic::PrestrainGradient(FEMaterialPoint& mp)\n{\n\tmat3d F0 = mat3d::identity();\n\tif (m_Fp) F0 = m_Fp->Prestrain(mp);\n\n\tFEPrestrainMaterialPoint& pt = *(mp.ExtractData<FEPrestrainMaterialPoint>());\n\tpt.setInitialPrestrain(F0);\n\n\treturn pt.prestrain();\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrestrainElastic::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n\tFEPrestrainMaterialPoint& pp = *(mp.ExtractData<FEPrestrainMaterialPoint>());\n\n\t// store the original deformation gradient\n\tmat3d F0 = ep.m_F;\n\tdouble J0 = ep.m_J;\n\n\t// pre-multiply the pre-strain\n\tmat3d Fp = PrestrainGradient(mp);\n\tep.m_F = ep.m_F*Fp;\n\tep.m_J = ep.m_F.det();\n\n\t// evaluate the stress\n\tmat3ds s = m_mat->Stress(mp);\n\n\t// restore original deformation gradient\n\tep.m_F = F0;\n\tep.m_J = J0;\n\n\t// return stress\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrestrainElastic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n\tFEPrestrainMaterialPoint& pt = *(mp.ExtractData<FEPrestrainMaterialPoint>());\n\n\t// store the original deformation gradient\n\tmat3d F0 = ep.m_F;\n\tdouble J0 = ep.m_J;\n\n\t// pre-multiply the pre-strain\n\tmat3d Fp = pt.prestrain();\n\tep.m_F = ep.m_F*Fp;\n\tep.m_J = ep.m_F.det();\n\n\t// evaluate the elastic tangent\n\ttens4ds ce = m_mat->Tangent(mp);\n\n\t// restore original deformation gradient\n\tep.m_F = F0;\n\tep.m_J = J0;\n\n\t// return spatial tangent\n\treturn ce;\n}\n"
  },
  {
    "path": "FEBioMech/FEPreStrainElastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Class for stroing pre-strain gradient data\n//! This material point class stores the initial pre-strain \"guess\" and\n//! the correction term. The total prestrain gradient is the product of these two.\nclass FEPrestrainMaterialPoint : public FEMaterialPointData\n{\npublic:\n\t//! constructor\n\tFEPrestrainMaterialPoint(FEMaterialPointData* mp);\n\n\t//! copy\n\tFEMaterialPointData* Copy();\n\n\t//! initialization\n\tvoid Init(bool bflag);\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& dmp);\n\npublic:\n\t//! get the initial prestrain gradient\n\tconst mat3d& initialPrestrain() const { return F0; }\n\n\t//! set the prestrain gradient\n\tvoid setInitialPrestrain(const mat3d& F) { F0 = F; }\n\n\t//! get the prestrain correction\n\tconst mat3d& PrestrainCorrection() const { return Fc; }\n\n\t//! set the prestrain correction\n\tvoid setPrestrainCorrection(const mat3d& F) { Fc = F; }\n\n\t//! get the total prestrain\n\tmat3d prestrain() const { return Fc*F0; }\n\nprotected:\n\tmat3d\tF0;\t//!< initial guess of pre-strain value\n\tmat3d\tFc;\t//!< \"correction\" used to premultiply the prestrain gradient\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for algorithms that will be used to calculate a pre-strain gradient.\n//! This is used by the FEPrestrainElastic class to calculate the initial pre-strain\n//! gradient.\nclass FEBIOMECH_API FEPrestrainGradient : public FEMaterialProperty\n{\npublic:\n\tFEPrestrainGradient(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\tvirtual ~FEPrestrainGradient(){}\n\n\t// evaluate the pre-strain deformation gradient\n\tvirtual mat3d Prestrain(FEMaterialPoint& mp) = 0;\n\n\t// initialize the pre-strain gradient based on a deformation gradient\n\t// This is used by the pre-strain initial condition\n\tvirtual void Initialize(const mat3d& F, FEMaterialPoint& mp) = 0;\n\n\tFECORE_BASE_CLASS(FEPrestrainGradient)\n};\n\n//-----------------------------------------------------------------------------\nclass FEPrestrainMaterial\n{\npublic:\n\tvirtual FEPrestrainGradient* PrestrainGradientProperty() = 0;\n\n\tvirtual FEElasticMaterial* GetElasticMaterial() = 0;\n};\n\n//-----------------------------------------------------------------------------\n//! This material applies a user-defined prestrain deformation gradient\n//! before evaluating the stress and tangents. \nclass FEPrestrainElastic : public FEElasticMaterial, public FEPrestrainMaterial\n{\npublic:\n\t//! constructor\n\tFEPrestrainElastic(FEModel* pfem);\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t//! return the pre-strain gradient property\n\tFEPrestrainGradient* PrestrainGradientProperty() override { return m_Fp; }\n\n\t//! return the elastic material\n\tFEElasticMaterial* GetElasticMaterial() override { return m_mat; }\n\n\t// evaluate density in (pre-strained) reference configuration\n\tdouble Density(FEMaterialPoint& mp) override;\n\npublic:\n\t//! Cauchy stress \n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\t//! spatial tangent\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\nprotected:\n\t//! calculate prestrain deformation gradient\n\tmat3d PrestrainGradient(FEMaterialPoint& mp);\n\nprivate:\n\tFEElasticMaterial*\t\tm_mat;\t//!< elastic base material\n\tFEPrestrainGradient*\tm_Fp;\t//!< pre-strain gradient\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPreStrainUncoupledElastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPreStrainUncoupledElastic.h\"\n\nBEGIN_FECORE_CLASS(FEPreStrainUncoupledElastic, FEUncoupledMaterial)\n\tADD_PROPERTY(m_mat, \"elastic\");\n\tADD_PROPERTY(m_Fp, \"prestrain\", false);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPreStrainUncoupledElastic::FEPreStrainUncoupledElastic(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_mat = nullptr;\n\tm_Fp = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEPreStrainUncoupledElastic::CreateMaterialPointData()\n{ \n\tFEMaterialPointData* pm = m_mat->CreateMaterialPointData();\n\tif (m_Fp) pm->Append(m_Fp->CreateMaterialPointData());\n\treturn new FEPrestrainMaterialPoint(pm);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate (pre-strained) density \ndouble FEPreStrainUncoupledElastic::Density(FEMaterialPoint& mp)\n{\n\tdouble d0 = FEElasticMaterial::Density(mp);\n\n\tmat3d Fp = PrestrainGradient(mp);\n\tdouble Jp = Fp.det();\n\n\treturn d0 / Jp;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEPreStrainUncoupledElastic::PrestrainGradient(FEMaterialPoint& mp)\n{\n\tmat3d F0 = mat3d::identity();\n\tif (m_Fp) F0 = m_Fp->Prestrain(mp);\n\n\tFEPrestrainMaterialPoint& pt = *(mp.ExtractData<FEPrestrainMaterialPoint>());\n\tpt.setInitialPrestrain(F0);\n\n\treturn pt.prestrain();\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPreStrainUncoupledElastic::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n\tFEPrestrainMaterialPoint& pp = *(mp.ExtractData<FEPrestrainMaterialPoint>());\n\n\t// store the original deformation gradient\n\tmat3d F0 = ep.m_F;\n\tdouble J0 = ep.m_J;\n\n\t// get the pre-strain deformation gradient\n\tmat3d Fp = PrestrainGradient(mp);\n\n\t// pre-multiply the pre-strain\n\tep.m_F = ep.m_F*Fp;\n\tep.m_J = ep.m_F.det();\n\n\t// evaluate the stress\n\tmat3ds s = m_mat->DevStress(mp);\n\n\t// restore original deformation gradient\n\tep.m_F = F0;\n\tep.m_J = J0;\n\n\t// return stress\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPreStrainUncoupledElastic::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ep = *(mp.ExtractData<FEElasticMaterialPoint>());\n\tFEPrestrainMaterialPoint& pt = *(mp.ExtractData<FEPrestrainMaterialPoint>());\n\n\t// store the original deformation gradient\n\tmat3d F0 = ep.m_F;\n\tdouble J0 = ep.m_J;\n\n\t// get the pre-strain deformation gradient\n\tmat3d Fp = pt.prestrain();\n\n\t// pre-multiply the pre-strain\n\tep.m_F = ep.m_F*Fp;\n\tep.m_J = ep.m_F.det();\n\n\t// evaluate the tangent\n\ttens4ds c = m_mat->DevTangent(mp);\n\n\t// restore original deformation gradient\n\tep.m_F = F0;\n\tep.m_J = J0;\n\n\t// return spatial tangent\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEPreStrainUncoupledElastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEPreStrainElastic.h\"\n\n//-----------------------------------------------------------------------------\n//! This material applies a user-defined prestrain deformation gradient\n//! before evaluating the stress and tangents. \nclass FEPreStrainUncoupledElastic : public FEUncoupledMaterial, public FEPrestrainMaterial\n{\npublic:\n\t//! constructor\n\tFEPreStrainUncoupledElastic(FEModel* pfem);\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t//! return the pre-strain gradient property\n\tFEPrestrainGradient* PrestrainGradientProperty() override { return m_Fp; }\n\n\t//! return the elastic material property\n\tFEElasticMaterial* GetElasticMaterial() override { return m_mat; }\n\n\t//! calculate (pre-strained) density \n\tdouble Density(FEMaterialPoint& mp) override;\n\npublic:\n\t//! Cauchy stress \n\tmat3ds DevStress(FEMaterialPoint& mp) override;\n\n\t//! spatial tangent\n\ttens4ds DevTangent(FEMaterialPoint& mp) override;\n\nprotected:\n\t//! calculate prestrain deformation gradient\n\tmat3d PrestrainGradient(FEMaterialPoint& mp);\n\nprivate:\n\tFEUncoupledMaterial*\tm_mat;\t//!< uncoupled elastic base material\n\tFEPrestrainGradient*\tm_Fp;\t//!< pre-strain gradient\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionIsotropic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedActiveContractionIsotropic.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionIsotropic, FEElasticMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionIsotropic::FEPrescribedActiveContractionIsotropic(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_T0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionIsotropic::Stress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n    double J = pt.m_J;\n    mat3ds b = pt.LeftCauchyGreen();\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = b*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionIsotropic::Tangent(FEMaterialPoint &mp)\n{\n    tens4ds c;\n    c.zero();\n    \n    return c;\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionIsotropic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of a solid mixture material.\nclass FEPrescribedActiveContractionIsotropic : public FEElasticMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionIsotropic(FEModel* pfem);\n    \n    //! stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble\tm_T0;       // prescribed active stress\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionIsotropicUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedActiveContractionIsotropicUC.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionIsotropicUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionIsotropicUC::FEPrescribedActiveContractionIsotropicUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_T0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionIsotropicUC::DevStress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    double J = pt.m_J;\n    mat3ds b = pt.LeftCauchyGreen();\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = b*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionIsotropicUC::DevTangent(FEMaterialPoint &mp)\n{\n    return tens4ds(0.0);\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionIsotropicUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of a solid mixture material.\nclass FEPrescribedActiveContractionIsotropicUC : public FEUncoupledMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionIsotropicUC(FEModel* pfem);\n    \n    //! stress\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! tangent\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble\tm_T0;       // prescribed active stress\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionTransIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedActiveContractionTransIso.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionTransIso, FEElasticMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionTransIso::FEPrescribedActiveContractionTransIso(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_T0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrescribedActiveContractionTransIso::Validate()\n{\n\tif (FEElasticMaterial::Validate() == false) return false;\n\n    // fiber direction in local coordinate system (reference configuration)\n    m_n0.x = 1;\n    m_n0.y = 0;\n    m_n0.z = 0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedActiveContractionTransIso::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_n0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionTransIso::Stress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    mat3ds b = pt.LeftCauchyGreen();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // evaluate fiber direction in global coordinate system\n    vec3d n0 = Q*m_n0;\n    \n    // evaluate the deformed fiber direction\n    vec3d nt = F*n0;\n    mat3ds N = dyad(nt);\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = (b-N)*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionTransIso::Tangent(FEMaterialPoint &mp)\n{\n    tens4ds c;\n    c.zero();\n    \n    return c;\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionTransIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of a solid mixture material.\nclass FEPrescribedActiveContractionTransIso : public FEElasticMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionTransIso(FEModel* pfem);\n    \n    //! Validation\n    bool Validate() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \n    //! stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble\tm_T0;       // prescribed active stress\n    vec3d\tm_n0;\t\t// unit vector along fiber direction (local coordinate system)\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionTransIsoUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedActiveContractionTransIsoUC.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionTransIsoUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionTransIsoUC::FEPrescribedActiveContractionTransIsoUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_T0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrescribedActiveContractionTransIsoUC::Validate()\n{\n\tif (FEUncoupledMaterial::Validate() == false) return false;\n\n    // fiber direction in local coordinate system (reference configuration)\n    m_n0.x = 1;\n    m_n0.y = 0;\n    m_n0.z = 0;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedActiveContractionTransIsoUC::Serialize(DumpStream& ar)\n{\n\tFEUncoupledMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_n0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionTransIsoUC::DevStress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F;\n    mat3ds b = pt.LeftCauchyGreen();\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // evaluate fiber direction in global coordinate system\n    vec3d n0 = Q*m_n0;\n    \n    // evaluate the deformed fiber direction\n    vec3d nt = F*n0;\n    mat3ds N = dyad(nt);\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = (b-N)*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionTransIsoUC::DevTangent(FEMaterialPoint &mp)\n{\n    return tens4ds(0.0);\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionTransIsoUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of an uncoupled solid mixture material.\nclass FEPrescribedActiveContractionTransIsoUC : public FEUncoupledMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionTransIsoUC(FEModel* pfem);\n    \n\t//! validation\n\tbool Validate() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \n    //! stress\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! tangent\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \npublic: // material parameters\n\tFEParamDouble\tm_T0;       // prescribed active stress\n\nprivate:\n    vec3d\tm_n0;\t\t// unit vector along fiber direction (local coordinate system)\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionUniaxial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedActiveContractionUniaxial.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionUniaxial, FEElasticMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionUniaxial::FEPrescribedActiveContractionUniaxial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_T0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrescribedActiveContractionUniaxial::Validate()\n{\n\tif (FEElasticMaterial::Validate() == false) return false;\n\n    // fiber direction in local coordinate system (reference configuration)\n    m_n0.x = 1;\n    m_n0.y = 0;\n    m_n0.z = 0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedActiveContractionUniaxial::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_n0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionUniaxial::Stress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // evaluate fiber direction in global coordinate system\n    vec3d n0 = Q*m_n0;\n\n    // evaluate the deformed fiber direction\n    vec3d nt = F*n0;\n    mat3ds N = dyad(nt);\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = N*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionUniaxial::Tangent(FEMaterialPoint &mp)\n{\n    tens4ds c;\n    c.zero();\n\n    return c;\n}\n\n\n//=============================================================================\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionFiber, FEElasticMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\n\n\tADD_PROPERTY(m_fiber, \"fiber\")->SetDefaultType(\"vector\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionFiber::FEPrescribedActiveContractionFiber(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_T0 = 0.0;\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionFiber::Stress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // evaluate fiber direction in global coordinate system\n    vec3d n0 = Q*m_fiber->unitVector(mp);\n\n    // evaluate the deformed fiber direction\n    vec3d nt = F*n0;\n    mat3ds N = dyad(nt);\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = N*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionFiber::Tangent(FEMaterialPoint &mp)\n{\n    tens4ds c;\n    c.zero();\n    return c;\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionUniaxial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of a solid mixture material.\nclass FEPrescribedActiveContractionUniaxial : public FEElasticMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionUniaxial(FEModel* pfem);\n    \n    //! validation\n    bool Validate() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \n    //! stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble\tm_T0;       // prescribed active stress\n\nprivate:\n    vec3d\tm_n0;\t\t// unit vector along fiber direction (local coordinate system)\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of a solid mixture material.\nclass FEPrescribedActiveContractionFiber : public FEElasticMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionFiber(FEModel* pfem);\n\n    //! stress\n    mat3ds Stress(FEMaterialPoint& pt) override;\n\n    //! tangent\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n\nprivate:\n    FEParamDouble\tm_T0;       // prescribed active stress\n\tFEVec3dValuator* m_fiber;  // unit vector along fiber direction (local coordinate system)\n\n    DECLARE_FECORE_CLASS();\n};"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionUniaxialUC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedActiveContractionUniaxialUC.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionUniaxialUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\n\n    ADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionUniaxialUC::FEPrescribedActiveContractionUniaxialUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_T0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrescribedActiveContractionUniaxialUC::Validate()\n{\n\tif (FEUncoupledMaterial::Validate() == false) return false;\n\n    // fiber direction in local coordinate system (reference configuration)\n    m_n0.x = 1;\n    m_n0.y = 0;\n    m_n0.z = 0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedActiveContractionUniaxialUC::Serialize(DumpStream& ar)\n{\n\tFEUncoupledMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_n0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionUniaxialUC::DevStress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F;\n   \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // evaluate fiber direction in global coordinate system\n    vec3d n0 = Q*m_n0;\n    \n    // evaluate the deformed fiber direction\n    vec3d nt = F*n0;\n    mat3ds N = dyad(nt);\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = N*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionUniaxialUC::DevTangent(FEMaterialPoint &mp)\n{\n    return tens4ds(0.0);\n}\n\n\n//=============================================================================\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPrescribedActiveContractionFiberUC, FEUncoupledMaterial)\n\tADD_PARAMETER(m_T0 , \"T0\"   );\n\tADD_PARAMETER(m_n0 , \"fiber\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedActiveContractionFiberUC::FEPrescribedActiveContractionFiberUC(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_T0 = 0.0;\n    m_n0 = vec3d(1, 0, 0);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPrescribedActiveContractionFiberUC::DevStress(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    mat3d F = pt.m_F;\n   \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // evaluate fiber direction in global coordinate system\n    vec3d n0 = Q*m_n0.unitVector(mp);\n    \n    // evaluate the deformed fiber direction\n    vec3d nt = F*n0;\n    mat3ds N = dyad(nt);\n    \n    // evaluate the active stress\n\tdouble T0 = m_T0(mp);\n    mat3ds s = N*(T0/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPrescribedActiveContractionFiberUC::DevTangent(FEMaterialPoint &mp)\n{\n    return tens4ds(0.0);\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedActiveContractionUniaxialUC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of an uncoupled solid mixture material.\nclass FEPrescribedActiveContractionUniaxialUC : public FEUncoupledMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionUniaxialUC(FEModel* pfem);\n    \n    //! Validation\n    bool Validate() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \n    //! stress\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! tangent\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble\tm_T0;       // prescribed active stress\n\nprivate:\n\tvec3d\tm_n0;\t\t// unit vector along fiber direction (local coordinate system)\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of an uncoupled solid mixture material.\nclass FEPrescribedActiveContractionFiberUC : public FEUncoupledMaterial\n{\npublic:\n    //! constructor\n    FEPrescribedActiveContractionFiberUC(FEModel* pfem);\n\n    //! stress\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n\n    //! tangent\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n\nprivate:\n    FEParamDouble\tm_T0;       // prescribed active stress\n    FEParamVec3     m_n0;\t\t// unit vector along fiber direction (local coordinate system)\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedDisplacement.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedDisplacement, FENodalBC)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:displacement)\");\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_LENGTH)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedDisplacement::FEPrescribedDisplacement(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedDisplacement : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedDisplacement(FEModel* fem);\n\nprivate:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedNormalDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedNormalDisplacement.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FENode.h>\n#include \"FEBioMech.h\"\n\nBEGIN_FECORE_CLASS(FEPrescribedNormalDisplacement, FEPrescribedSurface)\n\tADD_PARAMETER(m_scale, \"scale\");\n\tADD_PARAMETER(m_hint, \"surface_hint\");\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS()\n\nFEPrescribedNormalDisplacement::FEPrescribedNormalDisplacement(FEModel* fem) : FEPrescribedSurface(fem)\n{\n\tm_scale = 0.0;\n\tm_hint = 0;\n\n\t// set the dof list\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (fem)\n\t{\n\t\tFEDofList dofs(fem);\n\t\tdofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tSetDOFList(dofs);\n\t}\n}\n\n// activation\nvoid FEPrescribedNormalDisplacement::Activate()\n{\n\tconst FESurface& surf = *GetSurface();\n\n\tint N = surf.Nodes();\n\tm_normals.resize(N);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tm_normals[i] = vec3d(0,0,0);\n\t}\n\n\tif (m_hint == 0)\n\t{\n\t\tint NF = surf.Elements();\n\t\tfor (int i=0; i<NF; ++i)\n\t\t{\n\t\t\tconst FESurfaceElement& el = surf.Element(i);\n\t\t\tint nn = el.Nodes();\n\t\t\tif ((nn==3) || (nn == 4))\n\t\t\t{\n\t\t\t\tfor (int n=0; n<nn; ++n)\n\t\t\t\t{\n\t\t\t\t\tint i0 = el.m_lnode[n];\n\t\t\t\t\tint ip = el.m_lnode[(n+1)%nn];\n\t\t\t\t\tint im = el.m_lnode[(n + nn - 1)%nn];\n\n\t\t\t\t\tvec3d r0 = surf.Node(i0).m_r0;\n\t\t\t\t\tvec3d rp = surf.Node(ip).m_r0;\n\t\t\t\t\tvec3d rm = surf.Node(im).m_r0;\n\n\t\t\t\t\tvec3d nu = (rp - r0) ^ (rm - r0);\n\n\t\t\t\t\tm_normals[i0] += nu;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((nn == 6) || (nn == 7))\n\t\t\t{\n\t\t\t\tvec3d normals[7];\n\n\t\t\t\t// corner nodes\n\t\t\t\tfor (int n = 0; n<3; ++n)\n\t\t\t\t{\n\t\t\t\t\tint i0 = el.m_lnode[n];\n\t\t\t\t\tint ip = el.m_lnode[(n + 1) % 3];\n\t\t\t\t\tint im = el.m_lnode[(n + 2) % 3];\n\n\t\t\t\t\tvec3d r0 = surf.Node(i0).m_r0;\n\t\t\t\t\tvec3d rp = surf.Node(ip).m_r0;\n\t\t\t\t\tvec3d rm = surf.Node(im).m_r0;\n\n\t\t\t\t\tvec3d nu = (rp - r0) ^ (rm - r0);\n\n\t\t\t\t\tnormals[n] = nu;\n\t\t\t\t}\n\n\t\t\t\t// edge nodes\n\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t{\n\t\t\t\t\tint n0 = n + 3;\n\t\t\t\t\tint n1 = n;\n\t\t\t\t\tint n2 = (n+1) % 3;\n\n\t\t\t\t\tnormals[n0] = (normals[n1] + normals[n2]) * 0.5;\n\t\t\t\t}\n\n\t\t\t\tif (nn == 7)\n\t\t\t\t{\n\t\t\t\t\tnormals[6] = (normals[0] + normals[1] + normals[2]) / 3.0;\n\t\t\t\t}\n\n\t\t\t\tfor (int n=0; n<nn; ++n) m_normals[el.m_lnode[n]] += normals[n];\n\t\t\t}\n\t\t\telse { assert(false); }\n\t\t}\n\n\t\tfor (int i = 0; i<N; ++i)\n\t\t{\n\t\t\tm_normals[i].unit();\n\t\t}\n\t}\n\telse\n\t{\n\t\tint NN = surf.Nodes();\n\t\tfor (int i=0; i<NN; ++i)\n\t\t{\n\t\t\tvec3d ri = -surf.Node(i).m_r0;\n\t\t\tri.unit();\n\t\t\tm_normals[i] = ri;\n\t\t}\n\t}\n\n\tFEPrescribedSurface::Activate();\n}\n\n// return the values for node nodelid\nvoid FEPrescribedNormalDisplacement::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n\tvec3d v = m_normals[nodelid]*m_scale;\n\tval[0] = v.x;\n\tval[1] = v.y;\n\tval[2] = v.z;\n}\n\n// copy data from another class\nvoid FEPrescribedNormalDisplacement::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFEPrescribedNormalDisplacement* pnd = dynamic_cast<FEPrescribedNormalDisplacement*>(pbc);\n\tassert(pnd);\n\tm_scale = pnd->m_scale;\n\tm_normals = pnd->m_normals;\n\tCopyParameterListState(pnd->GetParameterList());\n}\n\nvoid FEPrescribedNormalDisplacement::Serialize(DumpStream& ar)\n{\n\tFEPrescribedSurface::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_normals;\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedNormalDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FEPrescribedNormalDisplacement : public FEPrescribedSurface\n{\npublic:\n\t// constructor\n\tFEPrescribedNormalDisplacement(FEModel* fem);\n\n\t// activation\n\tvoid Activate() override;\n\npublic:\n\t// return the value for node i, dof j\n\tvoid GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n\t// copy data from another class\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tvector<vec3d>\tm_normals;\n\tdouble\tm_scale;\n\n\t// hint parameter helps to identify the surface geometry\n\t// 0 : no hint (default)\n\t// 1 : sphere with center at origin\n\tint\t\tm_hint;\t//!< hint parameter helps to identify the surface geometry\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedRotation.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedRotation.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedRotation, FEBoundaryCondition)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:rotation)\");\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_RADIAN)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedRotation::FEPrescribedRotation(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedRotation.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedRotation : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedRotation(FEModel* fem);\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPrescribedShellDisplacement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedShellDisplacement.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedShellDisplacement, FENodalBC)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:shell displacement)\");\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_LENGTH)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedShellDisplacement::FEPrescribedShellDisplacement(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioMech/FEPrescribedShellDisplacement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n\nclass FEPrescribedShellDisplacement : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedShellDisplacement(FEModel* fem);\n\nprivate:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPressureLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPressureLoad.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEFacetSet.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEPressureLoad, FESurfaceLoad)\n\tADD_PARAMETER(m_pressure, \"pressure\")->setUnits(UNIT_PRESSURE)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_bsymm   , \"symmetric_stiffness\");\n\tADD_PARAMETER(m_blinear , \"linear\");\n\tADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEPressureLoad::FEPressureLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{ \n\tm_pressure = 0.0;\n\tm_bsymm = true;\n\tm_bshellb = false;\n\tm_blinear = false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPressureLoad::Init()\n{\n\tFESurface& surf = GetSurface();\n\tsurf.SetShellBottom(m_bshellb);\n\n\t// get the degrees of freedom\n\tm_dof.Clear();\n\tif (m_bshellb == false)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\telse\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t}\n\tif (m_dof.IsEmpty()) return false;\n\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPressureLoad::LoadVector(FEGlobalVector& R)\n{\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadVector(R, m_dof, m_blinear, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\t\t\n\t\t// evaluate pressure at this material point\n\t\tdouble P = -m_pressure(pt);\n\t\tif (m_bshellb) P = -P;\n\n\t\tdouble J = (pt.dxr ^ pt.dxs).norm();\n\n\t\t// force vector\n\t\tvec3d N = (pt.dxr ^ pt.dxs); N.unit();\n\t\tvec3d t = N*P;\n\n\t\tdouble H_u = dof_a.shape;\n\n\t\tval[0] = H_u*t.x*J;\n\t\tval[1] = H_u*t.y*J;\n\t\tval[2] = H_u*t.z*J;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPressureLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Don't calculate stiffness for a linear load\n\tif (m_blinear) return;\n\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& kab) {\n\n\t\t// evaluate pressure at this material point\n\t\tdouble P = -m_pressure(mp);\n\t\tif (m_bshellb) P = -P;\n\n\t\tdouble H_i  = dof_a.shape;\n\t\tdouble Gr_i = dof_a.shape_deriv_r;\n\t\tdouble Gs_i = dof_a.shape_deriv_s;\n\n\t\tdouble H_j  = dof_b.shape;\n\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\tvec3d vab(0,0,0);\n\t\tif (m_bsymm)\n\t\t\tvab = (mp.dxr*(H_j * Gs_i - H_i * Gs_j) - mp.dxs*(H_j * Gr_i - H_i * Gr_j)) * 0.5*P;\n\t\telse  \n\t\t\tvab = (mp.dxs*Gr_j - mp.dxr*Gs_j)*(P*H_i);\n\n\t\tmat3da K(vab);\n\t\tkab.set(0, 0, K);\n\t});\n}\n"
  },
  {
    "path": "FEBioMech/FEPressureLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! The pressure surface is a surface domain that sustains pressure boundary\n//! conditions\n//!\nclass FEPressureLoad : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n\tFEPressureLoad(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\npublic:\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\tFEParamDouble\tm_pressure;\t//!< pressure value\n\tbool\t\t\tm_bsymm;\t//!< use symmetric formulation\n\tbool\t\t\tm_blinear;\t//!< is the load linear (i.e. it will be calculated in the reference frame and assummed deformation independent)\n\tbool\t\t\tm_bshellb;\t//!< flag for prescribing pressure on shell bottom\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEPressureRobinBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPressureRobinBC.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n// Parameter block for pressure loads\nBEGIN_FECORE_CLASS(FEPressureRobinBC, FESurfaceLoad)\n\tADD_PARAMETER(m_epsk   , \"spring_eps\")->setUnits(\"P/L\");\n\tADD_PARAMETER(m_epsc   , \"dashpot_eps\")->setUnits(\"P.t/L\");\n\tADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEPressureRobinBC::FEPressureRobinBC(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n\tm_epsk = 0.0;\n    m_epsc = 0.0;\n\tm_bshellb = false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPressureRobinBC::Init()\n{\n\tFESurface& surf = GetSurface();\n\tsurf.SetShellBottom(m_bshellb);\n\n\t// get the degrees of freedom\n\tm_dof.Clear();\n\tif (m_bshellb == false)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\telse\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t}\n\tif (m_dof.IsEmpty()) return false;\n\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPressureRobinBC::Update()\n{\n    FETimeInfo tp = GetFEModel()->GetTime();\n    FESurface& surf = GetSurface();\n    for (int i=0; i<surf.Elements(); ++i) {\n        FESurfaceElement& el = surf.Element(i);\n        int nint = el.GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint* mp = el.GetMaterialPoint(n);\n            mp->Update(tp);\n        }\n    }\n    surf.Update(tp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPressureRobinBC::LoadVector(FEGlobalVector& R)\n{\n    if (GetFEModel()->GetTime().currentTime == 0) return;\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadVector(R, m_dof, false, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\t\t\n\t\t// evaluate pressure at this material point\n        vec3d n = (pt.dxr ^ pt.dxs);\n        double J = n.unit();\n        \n        double epsk = m_epsk(pt);\n        double epsc = m_epsc(pt);\n        vec3d u = pt.m_rt - pt.m_r0;\n        vec3d udot = (dt > 0) ? (pt.m_rt - pt.m_rp)/dt : vec3d(0,0,0);\n        double un = u*n;\n        double vn = udot*n;\n\n        // prescribe a pressure only when the boundary is moving in the direction of the normal\n\t\tdouble p = (un > 0) ? epsk*un + epsc*vn : 0;\n\t\tif (m_bshellb) p = -p;\n\n\t\t// force vector\n\t\tvec3d t = -n*p;\n\n\t\tdouble Na = dof_a.shape;\n\n\t\tval[0] = Na*t.x*J;\n\t\tval[1] = Na*t.y*J;\n\t\tval[2] = Na*t.z*J;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPressureRobinBC::StiffnessMatrix(FELinearSystem& LS)\n{\n    if (GetFEModel()->GetTime().currentTime == 0) return;\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& kab) {\n\n        // evaluate pressure at this material point\n        vec3d n = (pt.dxr ^ pt.dxs);\n        double J = n.unit();\n        \n        mat3dd I(1);\n        \n        double epsk = m_epsk(pt);\n        double epsc = m_epsc(pt);\n        vec3d u = pt.m_rt - pt.m_r0;\n        vec3d udot = (dt > 0) ? (pt.m_rt - pt.m_rp)/dt : vec3d(0,0,0);\n        double un = u*n;\n        double vn = udot*n;\n\n        double p = (un > 0) ? epsk*un + epsc*vn : 0;\n\t\tif (m_bshellb) p = -p;\n\n\t\tdouble Na  = dof_a.shape;\n\t\tdouble dNar = dof_a.shape_deriv_r;\n\t\tdouble dNas = dof_a.shape_deriv_s;\n\n\t\tdouble Nb  = dof_b.shape;\n\t\tdouble dNbr = dof_b.shape_deriv_r;\n\t\tdouble dNbs = dof_b.shape_deriv_s;\n        \n        mat3da Ab = mat3da(pt.dxs*(dNbr/J)-pt.dxr*(dNbs/J));\n        mat3d Kab = (n & n)*(J*Na*Nb)*(epsk+epsc/dt)\n        +(n & (Ab*(u*epsk+udot*epsc))*(Na*J))\n        +(mat3da(pt.dxr*dNbs - pt.dxs*dNbr)*(Na*p));\n        // prescribe a pressure only when the boundary is moving in the direction of the normal\n        if (un > 0) kab.set(0, 0, Kab);\n\t});\n}\n"
  },
  {
    "path": "FEBioMech/FEPressureRobinBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! The pressure surface is a surface domain that sustains pressure boundary\n//! conditions proportional to normal displacement and velocity (Robin BC)\n//!\nclass FEPressureRobinBC : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n    FEPressureRobinBC(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n    \n    //! update\n    void Update() override;\n\npublic:\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\tFEParamDouble   m_epsk;     //!< normal displacement penalty\n    FEParamDouble   m_epsc;     //!< normal velocity penalty\n\tbool\t\t\tm_bshellb;\t//!< flag for prescribing pressure on shell bottom\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERVEDamageMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERVEDamageMaterial.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERVEDamageMaterial, FEElasticMaterial)\n    // set material properties\n    ADD_PROPERTY(m_pRVE , \"viscoelastic\");\n    ADD_PROPERTY(m_pDamg, \"damage\");\n    ADD_PROPERTY(m_pCrit, \"criterion\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFERVEDamageMaterial::FERVEDamageMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_pRVE  = nullptr;\n    m_pBase = nullptr;\n    m_pDamg = nullptr;\n    m_pCrit = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FERVEDamageMaterial::Init()\n{\n    FEUncoupledMaterial* m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pRVE);\n    if (m_pMat != nullptr)\n    {\n        feLogError(\"Viscoelastic material should not be of type uncoupled\");\n        return false;\n    }\n    \n    m_pBase = m_pRVE->GetBaseMaterial();\n    \n    return m_pRVE->Init();\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FERVEDamageMaterial::CreateMaterialPointData()\n{\n    FEReactiveViscoelasticMaterialPoint* pt = new FEReactiveViscoelasticMaterialPoint();\n    // create damage materal point for strong bond (base) material\n    FEDamageMaterialPoint* pbase = new FEDamageMaterialPoint(m_pRVE->GetBaseMaterial()->CreateMaterialPointData());\n    pt->AddMaterialPoint(new FEMaterialPoint(pbase));\n    \n    // create materal point for weak bond material\n    FEReactiveVEMaterialPoint* pbond = new FEReactiveVEMaterialPoint(m_pRVE->GetBondMaterial()->CreateMaterialPointData());\n    pt->AddMaterialPoint(new FEMaterialPoint(pbond));\n    \n    return pt;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FERVEDamageMaterial::Stress(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the stress\n    mat3ds s = m_pRVE->Stress(pt);\n    \n    // return damaged stress\n    return s*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FERVEDamageMaterial::Tangent(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the tangent\n    tens4ds c = m_pRVE->Tangent(pt);\n    \n    // return damaged tangent\n    return c*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FERVEDamageMaterial::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pRVE->StrainEnergyDensity(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strong bond strain energy density at material point\ndouble FERVEDamageMaterial::StrongBondSED(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pRVE->StrongBondSED(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate weak bond strain energy density at material point\ndouble FERVEDamageMaterial::WeakBondSED(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pRVE->WeakBondSED(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FERVEDamageMaterial::Damage(FEMaterialPoint& pt)\n{\n    // get the reactive viscoelastic base material point\n    FEMaterialPoint* pb = m_pRVE->GetBaseMaterialPoint(pt);\n    \n    // get the damage material point data from its base material point\n    FEDamageMaterialPoint& pd = *pb->ExtractData<FEDamageMaterialPoint>();\n    \n    // evaluate the trial value of the damage criterion\n    // this must be done before evaluating the damage\n    pd.m_Etrial = m_pCrit->DamageCriterion(*pb);\n    \n    // evaluate and set the damage\n    double d = m_pDamg->Damage(*pb);\n    pd.m_D = d;\n    \n    return d;\n}\n"
  },
  {
    "path": "FEBioMech/FERVEDamageMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEReactiveViscoelastic.h\"\n#include \"FEDamageMaterialPoint.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n\n//-----------------------------------------------------------------------------\n// This material models damage in any reactive viscoelastic material.\n\nclass FERVEDamageMaterial : public FEElasticMaterial\n{\npublic:\n    FERVEDamageMaterial(FEModel* pfem);\n    \npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n    \n    //! data initialization and checking\n    bool Init() override;\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n    \n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override\n    {\n        m_pRVE->UpdateSpecializedMaterialPoints(mp, tp);\n    }\n    \npublic:\n    double StrongBondSED(FEMaterialPoint& pt) override;\n    double WeakBondSED(FEMaterialPoint& pt) override;\n    \npublic:\n    FEReactiveViscoelasticMaterial* m_pRVE;     // reactive viscoelastic material\n    FEElasticMaterial*              m_pBase;    // base strong bond material\n    FEDamageCDF*                    m_pDamg;    // damage model\n    FEDamageCriterion*              m_pCrit;    // damage criterion\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERVEFatigueMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERVEFatigueMaterial.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERVEFatigueMaterial, FEElasticMaterial)\n    // set material properties\n    ADD_PROPERTY(m_pRVE , \"viscoelastic\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFERVEFatigueMaterial::FERVEFatigueMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_pRVE  = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FERVEFatigueMaterial::Init()\n{\n    FEUncoupledMaterial* m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pRVE);\n    if (m_pMat != nullptr)\n    {\n        feLogError(\"Viscoelastic material should not be of type uncoupled\");\n        return false;\n    }\n    \n    FEReactiveFatigue* pmat = dynamic_cast<FEReactiveFatigue*>(m_pRVE->GetBaseMaterial());\n    if (pmat == nullptr)\n    {\n        feLogError(\"Elastic material should be of type Reactive fatigue\");\n        return false;\n    }\n    \n    return m_pRVE->Init();\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FERVEFatigueMaterial::CreateMaterialPointData()\n{\n    FEReactiveViscoelasticMaterialPoint* pt = new FEReactiveViscoelasticMaterialPoint();\n    // create damage materal point for strong bond (base) material\n    FEReactiveFatigueMaterialPoint* pbase = new FEReactiveFatigueMaterialPoint(m_pRVE->GetBaseMaterial()->CreateMaterialPointData());\n    pt->AddMaterialPoint(new FEMaterialPoint(pbase));\n    \n    // create materal point for weak bond material\n    FEReactiveVEMaterialPoint* pbond = new FEReactiveVEMaterialPoint(m_pRVE->GetBondMaterial()->CreateMaterialPointData());\n    pt->AddMaterialPoint(new FEMaterialPoint(pbond));\n    \n    return pt;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FERVEFatigueMaterial::Stress(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the stress\n    mat3ds s = m_pRVE->Stress(pt);\n    \n    // return damaged stress\n    return s*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FERVEFatigueMaterial::Tangent(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the tangent\n    tens4ds c = m_pRVE->Tangent(pt);\n    \n    // return damaged tangent\n    return c*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FERVEFatigueMaterial::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pRVE->StrainEnergyDensity(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strong bond strain energy density at material point\ndouble FERVEFatigueMaterial::StrongBondSED(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pRVE->StrongBondSED(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate weak bond strain energy density at material point\ndouble FERVEFatigueMaterial::WeakBondSED(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pRVE->WeakBondSED(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FERVEFatigueMaterial::Damage(FEMaterialPoint& pt)\n{\n    // get the reactive viscoelastic base material point\n    FEMaterialPoint* pb = m_pRVE->GetBaseMaterialPoint(pt);\n    \n    // get the reactive fatigue material point data\n    FEReactiveFatigueMaterialPoint& pd = *pb->ExtractData<FEReactiveFatigueMaterialPoint>();\n\n    return pd.m_D;\n}\n"
  },
  {
    "path": "FEBioMech/FERVEFatigueMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEReactiveViscoelastic.h\"\n#include \"FEReactiveFatigue.h\"\n\n//-----------------------------------------------------------------------------\n// This material models damage in any reactive viscoelastic material.\n\nclass FERVEFatigueMaterial : public FEElasticMaterial\n{\npublic:\n    FERVEFatigueMaterial(FEModel* pfem);\n    \npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n        \n    //! data initialization and checking\n    bool Init() override;\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pRVE->GetElasticMaterial(); }\n    \n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override\n    {\n        m_pRVE->UpdateSpecializedMaterialPoints(mp, tp);\n    }\n    \npublic:\n    double StrongBondSED(FEMaterialPoint& pt) override;\n    double WeakBondSED(FEMaterialPoint& pt) override;\n    \npublic:\n    FEReactiveViscoelasticMaterial* m_pRVE;     // reactive viscoelastic material\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERadialBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERadialBodyForce.h\"\n#include \"FEElasticMaterial.h\"\n\nBEGIN_FECORE_CLASS(FERadialBodyForce, FEBodyForce);\n\tADD_PARAMETER(p, \"p\");\n\tADD_PARAMETER(c, \"c\");\n\tADD_PARAMETER(s[0], \"sx\");\n\tADD_PARAMETER(s[1], \"sy\");\n\tADD_PARAMETER(s[2], \"sz\");\nEND_FECORE_CLASS();\n\nFERadialBodyForce::FERadialBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n\tp = 0;\n\tc = vec3d(0, 0, 0);\n\ts[0] = 1;\n\ts[1] = 1;\n\ts[2] = 1;\n}\n\nvec3d FERadialBodyForce::force(FEMaterialPoint& mp)\n{\n\tvec3d x = mp.m_rt - c;\n\tif (p == 0)\n\t{\n\t\tvec3d f(s[0] * x.x, s[1] * x.y, s[2] * x.z);\n\t\treturn f;\n\t}\n\telse\n\t{\n\t\tdouble R = x.norm();\n\t\tdouble Rp = pow(R, p);\n\t\tvec3d f(s[0] * Rp * x.x, s[1] * Rp * x.y, s[2] * Rp * x.z);\n\t\treturn f;\n\t}\n}\n\nmat3d FERadialBodyForce::stiffness(FEMaterialPoint& mp)\n{ \n\tmat3d K; K.zero();\n\tif (p == 0)\n\t{\n\t\tK[0][0] = -s[0];\n\t\tK[1][1] = -s[1];\n\t\tK[2][2] = -s[2];\n\t}\n\telse\n\t{\n\t\tvec3d x = mp.m_rt - c;\n\t\tmat3d XX = (x & x);\n\n\t\tdouble R = x.norm();\n\t\tdouble Rp = -pow(R, p);\n\t\tdouble Rpm2 = -pow(R, p-2 );\n\n\t\tK[0][0] = s[0] * p * Rpm2 * XX[0][0] + s[0] * Rp;\n\t\tK[0][1] = s[0] * p * Rpm2 * XX[0][1];\n\t\tK[0][2] = s[0] * p * Rpm2 * XX[0][2];\n\n\t\tK[1][0] = s[1] * p * Rpm2 * XX[1][0];\n\t\tK[1][1] = s[1] * p * Rpm2 * XX[1][1] + s[1] * Rp;\n\t\tK[1][2] = s[1] * p * Rpm2 * XX[1][2];\n\n\t\tK[2][0] = s[2] * p * Rpm2 * XX[2][0];\n\t\tK[2][1] = s[2] * p * Rpm2 * XX[2][1];\n\t\tK[2][2] = s[2] * p * Rpm2 * XX[2][2] + s[2] * Rp;\n\t}\n\treturn K; \n}\n\ndouble FERadialBodyForce::divforce(FEMaterialPoint& mp)\n{\n\t// TODO: implement this\n\tassert(false);\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioMech/FERadialBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBodyForce.h\"\n\n//! This class defines a radial body force.\n//! The body force depends on the current nodal coordinates\nclass FERadialBodyForce : public FEBodyForce\n{\npublic:\n\tFERadialBodyForce(FEModel* pfem);\n\n\tvec3d force(FEMaterialPoint& mp) override;\n\n\tdouble divforce(FEMaterialPoint& mp) override;\n\n\tmat3d stiffness(FEMaterialPoint& mp) override;\n\npublic:\n\tvec3d\tc; // force center\n\tdouble\tp;\t// power\n\tdouble s[3]; // scale factors for body force components\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEReactiveFatigue.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactiveFatigue.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n#include <FECore/log.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEModel.h>\n\n//////////////////////////// FATIGUE MATERIAL /////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEReactiveFatigue, FEElasticMaterial)\n\tADD_PARAMETER(m_k0   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k0\"  );\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pBase, \"elastic\");\n\tADD_PROPERTY(m_pIdmg, \"elastic_damage\");\n\tADD_PROPERTY(m_pFdmg, \"fatigue_damage\");\n\tADD_PROPERTY(m_pIcrt, \"elastic_criterion\");\n\tADD_PROPERTY(m_pFcrt, \"fatigue_criterion\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEReactiveFatigue::FEReactiveFatigue(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_pBase = 0;\n\tm_pIdmg = 0;\n\tm_pFdmg = 0;\n\tm_pIcrt = 0;\n\tm_pFcrt = 0;\n    \n    m_k0 = 0;\n    m_beta = 0;\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEReactiveFatigue::CreateMaterialPointData()\n{\n\treturn new FEReactiveFatigueMaterialPoint(m_pBase->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEReactiveFatigue::Init()\n{\n    FEUncoupledMaterial* m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pBase);\n\tif (m_pMat != nullptr)\n\t{\n\t\tfeLogError(\"Elastic material should not be of type uncoupled\");\n\t\treturn false;\n\t}\n    \n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEReactiveFatigue::Stress(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the stress\n    mat3ds s = m_pBase->Stress(pt);\n    \n    // return damaged stress\n    return s*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEReactiveFatigue::Tangent(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the tangent\n    tens4ds c = m_pBase->Tangent(pt);\n    \n    // return damaged tangent\n    return c*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEReactiveFatigue::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pBase->StrainEnergyDensity(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FEReactiveFatigue::Damage(FEMaterialPoint& pt)\n{\n    // get the reactive fatigue material point data\n    FEReactiveFatigueMaterialPoint& pd = *pt.ExtractData<FEReactiveFatigueMaterialPoint>();\n    \n    return pd.m_D;\n}\n\n//-----------------------------------------------------------------------------\n// update fatigue material point at each iteration\nvoid FEReactiveFatigue::UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp)\n{\n    double dt = tp.timeIncrement;\n    double k0 = m_k0(pt);\n    double beta = m_beta(pt);\n    \n    // get the fatigue material point data\n    FEReactiveFatigueMaterialPoint& pd = *pt.ExtractData<FEReactiveFatigueMaterialPoint>();\n    \n    // get damage criterion for intact bonds at current time\n    pd.m_Xitrl = m_pIcrt->DamageCriterion(pt);\n    if (pd.m_Xitrl > pd.m_Ximax)\n        pd.m_Fit = m_pIdmg->cdf(pt,pd.m_Xitrl);\n    else\n        pd.m_Fit = pd.m_Fip;\n\n    // get damage criterion for fatigue bonds at current time\n    double Xftrl = m_pFcrt->DamageCriterion(pt);\n    for (int ig=0; ig < pd.m_fb.size(); ++ig) {\n        if (Xftrl > pd.m_fb[ig].m_Xfmax) {\n            pd.m_fb[ig].m_Xftrl = Xftrl;\n            pd.m_fb[ig].m_Fft = m_pFdmg->cdf(pt,pd.m_fb[ig].m_Xftrl);\n        }\n        else\n            pd.m_fb[ig].m_Fft = pd.m_fb[ig].m_Ffp;\n    }\n\n    // evaluate time derivative of intact bond criterion\n    pd.m_aXit = (pd.m_Xitrl - pd.m_Xip)/dt;\n    \n    // evaluate increment in broken bond mass fraction from breaking of intact bonds\n    double dwb_ib = (pd.m_Fip < 1) ? (pd.m_Fit-pd.m_Fip)/(1-pd.m_Fip)*pd.m_wip : pd.m_wip;\n    // kinetics of intact bonds\n    pd.m_wbt = pd.m_wbp + dwb_ib;\n    // wit must be properly initialized before evaluating the fatigue reaction\n    pd.m_wit = pd.m_wip - dwb_ib;\n\n    // solve for fatigue bond mass fractions at current iteration\n    double kt = k0*pow(fabs(pd.m_aXit)*pd.m_wbt,beta);\n    double kp = k0*pow(fabs(pd.m_aXip)*pd.m_wbp,beta);\n    // evaluate increment in fatigue bond mass fraction from fatigue of intact bonds\n    double dwf_if = dt/2*(kt*pd.m_wit+kp*pd.m_wip);\n\n    // kinetics of intact bonds\n    pd.m_wit -= dwf_if;\n    pd.m_wft = pd.m_wfp + dwf_if;\n    \n    // add or update new generation\n    if ((pd.m_fb.size() == 0) || pd.m_fb.back().m_time < tp.currentTime) {\n        // add generation of fatigued bonds\n        FatigueBond fb;\n        fb.m_Ffp = 0;\n        fb.m_Fft = m_pFdmg->cdf(pt,Xftrl);\n        fb.m_wfp = 0;\n        fb.m_wft = dwf_if;\n        fb.m_Xftrl = Xftrl;\n        fb.m_time = tp.currentTime;\n        pd.m_fb.push_back(fb);\n    }\n    else {\n        for (int ig=0; ig < pd.m_fb.size(); ++ig) pd.m_fb[ig].m_wft = pd.m_fb[ig].m_wfp;\n        pd.m_fb.back().m_Fft = m_pFdmg->cdf(pt,Xftrl);\n        pd.m_fb.back().m_wft += dwf_if;\n        pd.m_fb.back().m_Xftrl = Xftrl;\n    }\n    // damage kinetics of fatigued bonds\n    for (int ig=0; ig < pd.m_fb.size(); ++ig) {\n        double dwb_fb = (pd.m_fb[ig].m_Ffp < 1) ? (pd.m_fb[ig].m_Fft-pd.m_fb[ig].m_Ffp)/(1-pd.m_fb[ig].m_Ffp)*pd.m_fb[ig].m_wfp\n        : pd.m_fb[ig].m_wft;\n        pd.m_fb[ig].m_wft -= dwb_fb;\n        pd.m_wbt += dwb_fb;\n        pd.m_wft -= dwb_fb;\n    }\n    // evaluate fatigue bond fraction\n//    pd.m_wft = 0;\n//    for (int ig=0; ig < pd.m_fb.size(); ++ig) pd.m_wft += pd.m_fb[ig].m_wft;\n    // fix round-off errors\n    double wbd = 0;\n    if (pd.m_wbt < pd.m_wbp) {\n        wbd = pd.m_wbp - pd.m_wbt;\n        pd.m_wbt = pd.m_wbp;\n    }\n    else if (pd.m_wbt > 1) {\n        pd.m_wbt = 1;\n    }\n    double wid = 0;\n    if (pd.m_wit > pd.m_wip) {\n        wid = pd.m_wit - pd.m_wip;\n        pd.m_wit = pd.m_wip;\n    }\n    else if (pd.m_wit < 0) {\n        pd.m_wit = 0;\n    }\n    pd.m_wft = 1 - pd.m_wit - pd.m_wbt - wid + wbd;\n    pd.m_D = pd.m_wbt;\n}\n"
  },
  {
    "path": "FEBioMech/FEReactiveFatigue.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2022 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEReactiveFatigueMaterialPoint.h\"\n#include <FECore/FEMaterial.h>\n\n//-----------------------------------------------------------------------------\n// This material models fatigue and damage in any hyper-elastic materials.\n\nclass FEReactiveFatigue : public FEElasticMaterial\n{\npublic:\n    FEReactiveFatigue(FEModel* pfem);\n    \npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n    \n    //! data initialization and checking\n    bool Init() override;\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n    \n    // update fatigue material point at each iteration\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic:\n    \n    FEElasticMaterial*  m_pBase;    // base elastic material\n    FEDamageCDF*        m_pIdmg;    // damage model for intact bonds\n    FEDamageCDF*        m_pFdmg;    // damage model for fatigued bonds\n    FEDamageCriterion*  m_pIcrt;    // damage criterion\n    FEDamageCriterion*  m_pFcrt;    // fatigue criterion\n    \npublic:\n    FEParamDouble       m_k0;       // reaction rate for fatigue reaction\n    FEParamDouble       m_beta;     // power exponent for fatigue reaction\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEReactiveFatigueMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"FEReactiveFatigueMaterialPoint.h\"\n#include <FECore/DumpStream.h>\n\n////////////////////// FATIGUE BOND /////////////////////////////////\nFatigueBond::FatigueBond()\n{\n    m_wft = m_wfp = m_Xfmax = m_Xftrl = m_Fft = m_Ffp = m_time = 0;\n    m_erase = false;\n}\n\nFatigueBond::FatigueBond(const FatigueBond& fb)\n{\n    m_wft = fb.m_wft;\n    m_wfp = fb.m_wfp;\n    m_Xfmax = fb.m_Xfmax;\n    m_Xftrl = fb.m_Xftrl;\n    m_Fft = fb.m_Fft;\n    m_Ffp = fb.m_Ffp;\n    m_time = fb.m_time;\n    m_erase = fb.m_erase;\n}\n\nFatigueBond::FatigueBond(FatigueBond& fb)\n{\n    m_wft = fb.m_wft;\n    m_wfp = fb.m_wfp;\n    m_Xfmax = fb.m_Xfmax;\n    m_Xftrl = fb.m_Xftrl;\n    m_Fft = fb.m_Fft;\n    m_Ffp = fb.m_Ffp;\n    m_time = fb.m_time;\n    m_erase = fb.m_erase;\n}\n\nvoid FatigueBond::Update()\n{\n    m_wfp = m_wft;\n    m_Ffp = m_Fft;\n    if (m_Xftrl > m_Xfmax) m_Xfmax = m_Xftrl;\n}\n\n////////////////////// FATIGUE MATERIAL POINT /////////////////////////////////\n//-----------------------------------------------------------------------------\n// default constructor\nFEReactiveFatigueMaterialPoint::FEReactiveFatigueMaterialPoint(FEMaterialPointData*pt) : FEDamageMaterialPoint(pt)\n{\n    m_D = 0;\n    m_wit = 1;\n    m_wip = 1;\n    m_Ximax = 0;\n    m_Xitrl = 0;\n    m_Xip = 0;\n    m_aXit = 0;\n    m_aXip = 0;\n    m_Fit = 0;\n    m_Fip = 0;\n    m_wbt = 0;\n    m_wbp = 0;\n    m_wft = 0;\n    m_wfp = 0;\n\n    m_fb.clear();\n}\n\n//-----------------------------------------------------------------------------\n// copy constructor\nFEReactiveFatigueMaterialPoint::FEReactiveFatigueMaterialPoint(const FEReactiveFatigueMaterialPoint& rfmp) : FEDamageMaterialPoint(rfmp)\n{\n    m_D = rfmp.m_D;\n    m_wit = rfmp.m_wit;\n    m_wip = rfmp.m_wip;\n    m_Ximax = rfmp.m_Ximax;\n    m_Xitrl = rfmp.m_Xitrl;\n    m_Xip = rfmp.m_Xip;\n    m_aXit = rfmp.m_aXit;\n    m_aXip = rfmp.m_aXip;\n    m_Fit = rfmp.m_Fit;\n    m_Fip = rfmp.m_Fip;\n    m_wbt = rfmp.m_wbt;\n    m_wbp = rfmp.m_wbp;\n    m_wft = rfmp.m_wft;\n    m_wfp = rfmp.m_wfp;\n\n    m_fb.clear();\n    for (int ig=0; ig<m_fb.size(); ++ig)\n        m_fb.push_back(rfmp.m_fb[ig]);\n}\n\nFEReactiveFatigueMaterialPoint::FEReactiveFatigueMaterialPoint(FEReactiveFatigueMaterialPoint& rfmp) : FEDamageMaterialPoint(rfmp)\n{\n    m_D = rfmp.m_D;\n    m_wit = rfmp.m_wit;\n    m_wip = rfmp.m_wip;\n    m_Ximax = rfmp.m_Ximax;\n    m_Xitrl = rfmp.m_Xitrl;\n    m_Xip = rfmp.m_Xip;\n    m_aXit = rfmp.m_aXit;\n    m_aXip = rfmp.m_aXip;\n    m_Fit = rfmp.m_Fit;\n    m_Fip = rfmp.m_Fip;\n    m_wbt = rfmp.m_wbt;\n    m_wbp = rfmp.m_wbp;\n    m_wft = rfmp.m_wft;\n    m_wfp = rfmp.m_wfp;\n\n    m_fb.clear();\n    for (int ig=0; ig<m_fb.size(); ++ig)\n        m_fb.push_back(rfmp.m_fb[ig]);\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEReactiveFatigueMaterialPoint::Copy()\n{\n    FEReactiveFatigueMaterialPoint* pt = new FEReactiveFatigueMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactiveFatigueMaterialPoint::Init()\n{\n\tFEMaterialPointData::Init();\n    \n    // intialize total damate\n    m_D = 0;\n    \n    // initialize intact bond fraction to 1\n    m_wip = m_wit = 1.0;\n    \n    // initialize intact damage criterion\n    m_Ximax = m_Xitrl = m_Xip = 0;\n    m_Fip = m_Fit = 0;\n    \n    // initialize broken and fatigue bond fraction to 0\n    m_wbp = m_wbt = m_wfp = m_wft = 0;\n    \n    // clear the fatigue bond structure\n    m_fb.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactiveFatigueMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointData::Update(timeInfo);\n    \n    // update damage response for fatigues bonds\n    for (int ig=0; ig<m_fb.size(); ++ig) m_fb[ig].Update();\n    \n    // let's check overlapping generations of fatigued bonds\n    if (m_fb.size() > 1) {\n        for (int ig=0; ig < m_fb.size() - 1; ++ig) {\n            double Xfmax = m_fb[ig].m_Xfmax;\n            if (Xfmax <= m_fb.back().m_Xftrl) {\n                m_fb.back().m_wft += m_fb[ig].m_wft;\n                m_fb.back().m_wfp += m_fb[ig].m_wfp;\n                m_fb[ig].m_erase = true;\n            }\n        }\n    }\n    // cull generations that have been marked for erasure\n    std::deque<FatigueBond>::iterator it = m_fb.begin();\n    while (it != m_fb.end()) {\n        if (it->m_erase) it = m_fb.erase(it);\n        else ++it;\n    }\n    \n    // update damage response for intact bonds\n    if (m_Xitrl > m_Ximax) m_Ximax = m_Xitrl;\n    m_Xip = m_Xitrl;\n    m_aXip = m_aXit;\n    m_Fip = m_Fit;\n    \n    // update intact and damage bonds\n    m_wip = m_wit;\n    m_wbp = m_wbt;\n    m_wfp = m_wft;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactiveFatigueMaterialPoint::Serialize(DumpStream& ar)\n{\n    FEDamageMaterialPoint::Serialize(ar);\n    ar & m_wit & m_wip & m_Ximax & m_Xitrl & m_Xip & m_aXit & m_aXip & m_Fit & m_Fip;\n    ar & m_wbt & m_wbp & m_wfp & m_wft;\n    \n    // handle deques and boolean\n    if (ar.IsSaving()) {\n        int n = (int)m_fb.size();\n        ar << n;\n        for (int i=0; i<n; ++i) {\n            ar << m_fb[i].m_wft << m_fb[i].m_wfp;\n            ar << m_fb[i].m_Xfmax << m_fb[i].m_Xftrl;\n            ar << m_fb[i].m_Fft << m_fb[i].m_Ffp;\n            ar << m_fb[i].m_time << m_fb[i].m_erase;\n        }\n    } else {\n        int n;\n        ar >> n;\n        m_fb.clear();\n        for (int i=0; i<n; ++i) {\n            FatigueBond fb;\n            ar >> fb.m_wft >> fb.m_wfp;\n            ar >> fb.m_Xfmax >> fb.m_Xftrl;\n            ar >> fb.m_Fft >> fb.m_Ffp;\n            ar >> fb.m_time >> fb.m_erase;\n            m_fb.push_back(fb);\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEReactiveFatigueMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDamageMaterialPoint.h\"\n#include <deque>\n#include <FECore/FEMaterialPoint.h>\n\n//-----------------------------------------------------------------------------\n// structure for fatigue bonds\nclass FatigueBond\n{\npublic:\n    // constructor\n    FatigueBond();\n    \n    // copy constructor\n    FatigueBond(const FatigueBond& fb);\n    FatigueBond(FatigueBond& fb);\n    // update\n    void Update();\n    \npublic:\n    double  m_wft;      //!< fatigued bond fraction at current time\n    double  m_wfp;      //!< fatigued bond fraction at previous time\n    double  m_Xfmax;    //!< max damage criterion for fatigued bonds\n    double  m_Xftrl;    //!< trial value of Xfmax\n    double  m_Fft;      //!< fatigue bond damage CDF at current time\n    double  m_Ffp;      //!< fatigue bond damage CDF at previous time\n    double  m_time;     //!< fatigue bond generation time\n    bool    m_erase;    //!< flag for erasing a generation\n};\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores the fatigue and damage variables.\nclass FEReactiveFatigueMaterialPoint : public FEDamageMaterialPoint\n{\npublic:\n    // default constructor\n    FEReactiveFatigueMaterialPoint(FEMaterialPointData*pt);\n    \n\t// copy constructors\n    FEReactiveFatigueMaterialPoint(const FEReactiveFatigueMaterialPoint& rfmp);\n    FEReactiveFatigueMaterialPoint(FEReactiveFatigueMaterialPoint& rfmp);\n    \n\tFEMaterialPointData* Copy() override;\n    \n    void Init() override;\n    void Update(const FETimeInfo& timeInfo) override;\n    \n    void Serialize(DumpStream& ar) override;\n    \n    double IntactBonds() const override { return m_wit; }\n    double FatigueBonds() const override { return m_wft; }\n\npublic:\n    double      m_wit;          //!< intact bond mass fraction at current time\n    double      m_wip;          //!< intact bond mass fraction at previous time\n    \n    double      m_Ximax;        //!< max damage criterion for intact bonds\n    double      m_Xitrl;        //!< trial value of Ximax\n    double      m_Xip;          //!< Xi at previous time\n    \n    double      m_aXit;         //!< rate of change of Xi at current time\n    double      m_aXip;         //!< rate of change of Xi at previous time\n    \n    double      m_Fit;          //!< intact bond damage CDF at current time\n    double      m_Fip;          //!< intact bond damage CDF at previous time\n    \n    double      m_wbt;          //!< broken (damaged) bond fraction at current time\n    double      m_wbp;          //!< broken (damaged) bond fraction at previous time\n    \n    double      m_wft;          //!< fatigue bond fraction at current time\n    double      m_wfp;          //!< fatigue bond fraction at previous time\n\n    \n    std::deque <FatigueBond> m_fb;   //!< generations of fatigued bonds\n};\n\n"
  },
  {
    "path": "FEBioMech/FEReactiveMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDamageMaterialPoint.h\"\n"
  },
  {
    "path": "FEBioMech/FEReactiveMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2023 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include \"febiomech_api.h\"\n\n#ifdef WIN32\n#define max(a,b) ((a)>(b)?(a):(b))\n#endif\n\n//-----------------------------------------------------------------------------\n// Define a reactive material point that defines various virtual functions.\nclass FEBIOMECH_API FEReactiveMaterialPoint : public FEMaterialPointData\n{\npublic:\n    FEReactiveMaterialPoint(FEMaterialPointData*pt) : FEMaterialPointData(pt) {}\n    \n    virtual double BrokenBonds() const { return 0.0; }\n    virtual double IntactBonds() const { return 1.0; }\n    virtual double YieldedBonds() const { return 0.0; }\n    virtual double FatigueBonds() const { return 0.0; }\n};\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticDamage.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2019 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEReactivePlasticDamage.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/log.h>\n#include <FECore/matrix.h>\n\n#ifndef max\n#define max(a, b) ((a)>(b)?(a):(b))\n#endif\n\n//////////////////////// PLASTIC DAMAGE MATERIAL  /////////////////////////////////\n// define the material parameters\nBEGIN_FECORE_CLASS(FEReactivePlasticDamage, FEElasticMaterial)\n    // set material properties\n    ADD_PROPERTY(m_pBase,   \"elastic\");\n    ADD_PROPERTY(m_pCrit,   \"yield_criterion\");\n    ADD_PROPERTY(m_pFlow,   \"flow_curve\");\n    ADD_PROPERTY(m_pYDamg,  \"plastic_damage\"          , FEProperty::Optional);\n    ADD_PROPERTY(m_pYDCrit, \"plastic_damage_criterion\", FEProperty::Optional);\n    ADD_PROPERTY(m_pIDamg,  \"elastic_damage\"          , FEProperty::Optional);\n    ADD_PROPERTY(m_pIDCrit, \"elastic_damage_criterion\", FEProperty::Optional);\n\n    ADD_PARAMETER(m_isochrc, \"isochoric\");\n    ADD_PARAMETER(m_rtol   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n\n    ADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEReactivePlasticDamage::FEReactivePlasticDamage(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_isochrc = true;\n    m_rtol = 1e-4;\n    m_pBase = nullptr;\n    m_pCrit = nullptr;\n    m_pFlow = nullptr;\n    m_pYDamg = nullptr;\n    m_pYDCrit = nullptr;\n    m_pIDamg = nullptr;\n    m_pIDCrit = nullptr;\n    m_secant_tangent = true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEReactivePlasticDamage::Init()\n{\n    if (m_pFlow->Init() == false) return false;\n\n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticDamage::Serialize(DumpStream& ar)\n{\n    FEElasticMaterial::Serialize(ar);\n    ar & m_isochrc & m_rtol;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEReactivePlasticDamage::CreateMaterialPointData()\n{\n    FEMaterialPointData* ep = m_pBase->CreateMaterialPointData();\n    FEMaterialPointData* fp = m_pFlow->CreateMaterialPointData();\n    fp->SetNext(ep);\n    return new FEReactivePlasticDamageMaterialPoint(fp, this);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate elastic deformation gradient\nvoid FEReactivePlasticDamage::ElasticDeformationGradient(FEMaterialPoint& pt)\n{\n    // initialize flow curve (if not done yet)\n    if (m_pFlow->InitFlowCurve(pt)) {\n        FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n        pp.Init();\n    }\n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    // extract total deformation gradient\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract inverse of plastic deformation gradient and evaluate elastic deformation gradient\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    FEPlasticFlowCurveMaterialPoint& fp = *pt.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    FEShellElementNew* sel = dynamic_cast<FEShellElementNew*>(pt.m_elem);\n\n    for (int i=0; i<n; ++i) {\n        mat3d Fs = pe.m_F;\n        mat3d R = pe.m_F*pe.RightStretchInverse();\n        // for EAS and ANS shells, adjust calculation of Fs using enhanced strain Es\n        if (sel) {\n            mat3ds Cs = mat3dd(1) + sel->m_E[pt.m_index]*2;\n            double eval[3];\n            vec3d evec[3];\n            Cs.eigen2(eval,evec);\n            mat3ds Us = dyad(evec[0])*sqrt(eval[0]) + dyad(evec[1])*sqrt(eval[1]) + dyad(evec[2])*sqrt(eval[2]);\n            Fs = R*Us;\n        }\n        mat3d Fe = Fs*pp.m_Fusi[i];\n        \n        // store safe copy of total deformation gradient\n        mat3d Ftmp = pe.m_F;\n        double Jtmp = pe.m_J;\n        pe.m_F = Fe; pe.m_J = Fe.det();\n        mat3ds Ue = pe.RightStretch();\n\n        // evaluate yield measure\n        pp.m_Kv[i] = m_pCrit->DamageCriterion(pt);\n        \n        // restore total deformation gradient\n        pe.m_F = Ftmp; pe.m_J = Jtmp;\n        \n        // if there is no yielding, we're done\n        double phi = pp.m_Kv[i] - fp.m_Ky[i];\n        if (phi <= m_rtol*fp.m_Ky[i]) {\n            pp.m_Fvsi[i] = pp.m_Fusi[i];\n            continue;\n        }\n        \n        // check if i-th bond family is yielding\n        if ((pp.m_Kv[i] > pp.m_Ku[i]) && (pp.m_Ku[i] < fp.m_Ky[i]*(1+m_rtol))) {\n            if (pp.m_byldt[i] == false) {\n                pp.m_byldt[i] = true;\n                pp.m_wy[i] = (1.0-pp.m_di[i])*fp.m_w[i];\n            }\n        }\n        // if not, and if this bond family has not yielded at previous times,\n        // reset the mass fraction of yielded bonds to zero (in case m_wy[i] was\n        // set to non-zero during a prior iteration at current time)\n        else if (pp.m_byld[i] == false) {\n            pp.m_byldt[i] = false;\n            pp.m_wy[i] = 0;\n        }\n\n        // find Fv\n        bool conv = false;\n        int iter = 0;\n        double lam = 0;\n        mat3d Fv = Fe;\n        Ftmp = pe.m_F;  // store safe copy\n        Jtmp = pe.m_J;\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        mat3ds Uv = pe.RightStretch();\n        mat3ds Nv = YieldSurfaceNormal(pt);\n        double Nvmag = Nv.norm();\n        mat3dd I(1);\n        double beta = 1;\n        mat3ds ImN = I;\n        double phi0=0, phi1=0, phi2=0, lam1=0, lam2=0, a, b, c=0, d;\n        while (!conv) {\n            ++iter;\n            pe.m_F = Fv; pe.m_J = Fv.det();\n            pp.m_Kv[i] = m_pCrit->DamageCriterion(pt);\n            phi = pp.m_Kv[i] - fp.m_Ky[i];    // phi = 0 => stay on yield surface\n            if (iter == 1) {\n                phi0 = phi;\n                c = phi0;\n            }\n            else if (iter == 2) {\n                phi1 = phi;\n                lam1 = lam;\n            }\n            else if (iter == 3) {\n                phi2 = phi;\n                lam2 = lam;\n            }\n            mat3d dUvdlam = -Ue*Nv*(beta/Nvmag);\n            if (m_isochrc)\n                dUvdlam += Ue*ImN*((ImN.inverse()*Nv/Nvmag).trace()*beta/3.);\n            double dlam = -phi/(Nv*dUvdlam.transpose()).trace();\n            lam += dlam;\n            if (iter == 3) {\n                d = lam1*lam2*(lam1-lam2);\n                if (d == 0) {\n                    lam = (lam1*lam2 == 0) ? 0 : lam2;\n                }\n                else {\n                    a = (lam2*(phi1-phi0)-lam1*(phi2-phi0))/d;\n                    b = ((phi2-phi0)*lam1*lam1-(phi1-phi0)*lam2*lam2)/d;\n                    d = b*b - 4*a*c;\n                    if (d >= 0) {\n                        if (a != 0) {\n                            lam1 = (-b+sqrt(d))/(2*a);\n                            lam2 = (-b-sqrt(d))/(2*a);\n                            lam = (fabs(lam1) < fabs(lam2)) ? lam1 : lam2;\n                        }\n                        else if (b != 0) lam = -c/b;\n                        else lam = 0;\n                    }\n                    else if (a != 0) {\n                        lam = -b/(2*a);\n                    }\n                    else\n                        lam = 0;\n                }\n                conv = true;\n            }\n            ImN = I - Nv*(lam/Nvmag);\n            if (m_isochrc) beta = pow((pp.m_Fusi[i]*ImN).det(), -1./3.);\n            Uv = (Ue*ImN).sym()*beta;\n            Fv = R*Uv;\n            if (fabs(dlam) <= m_rtol*fabs(lam)) conv = true;\n            if (fabs(lam) <= m_rtol*m_rtol) conv = true;\n        }\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        pp.m_Kv[i] = m_pCrit->DamageCriterion(pt);\n        pe.m_F = Ftmp; pe.m_J = Jtmp;\n        pp.m_Fvsi[i] = Fs.inverse()*Fv;\n    }\n    \n    // evaluate octahedral plastic strain\n    OctahedralPlasticStrain(pt);\n    ReactiveHeatSupplyDensity(pt);\n    \n    return;\n}\n\n//-----------------------------------------------------------------------------\n// update plastic damage material point at each iteration\nvoid FEReactivePlasticDamage::UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp)\n{\n    // initialize flow curve (if not done yet)\n    if (m_pFlow->InitFlowCurve(pt)) {\n        FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n        pp.Init();\n    }\n    \n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n\n    // extract total deformation gradient\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic damage material point\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    FEPlasticFlowCurveMaterialPoint& fp = *pt.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n\n    // zero the total damage variable\n    pp.m_D = 0.0;\n    \n    // get intact damage criterion\n    if (m_pIDCrit) pp.m_Etrial = m_pIDCrit->DamageCriterion(pt);\n    double Es = max(pp.m_Etrial, pp.m_Emax);\n    \n    for (int i=0; i<n; ++i) {\n        if (pp.m_byldt[i] == false)\n        {\n            pp.m_di[i] = m_pIDamg ? m_pIDamg->cdf(pt,Es) : 0;\n            pp.m_d[i] = pp.m_di[i]*fp.m_w[i];\n            // what if we iterate here, update damage, then the next iteration decides we actually are yielding?\n            // no mechanism to undo the extra damage we've added\n            // do i need to save the final Eim for each family?\n        }\n        else\n        {\n            mat3d Fp = pp.m_Fvsi[i].inverse();\n            \n            // store safe copy of total deformation gradient\n            mat3d Ftmp = pe.m_F;\n            pe.m_F = Fp;\n            \n            // calculate damage\n            if (m_pYDCrit) pp.m_Eyt[i] = m_pYDCrit->DamageCriterion(pt);\n            double Ey = max(pp.m_Eyt[i], pp.m_Eym[i]);\n            pp.m_dy[i] = m_pYDamg ? m_pYDamg->cdf(pt,Ey) : 0;\n            \n            // restore total deformation gradient\n            pe.m_F = Ftmp;\n            \n            // calculate bond fractions\n            pp.m_wy[i] = (1.0-pp.m_dy[i])*(1.0-pp.m_di[i])*fp.m_w[i];\n            \n            // calculate damage\n            pp.m_d[i] = (pp.m_di[i]+pp.m_dy[i]*(1.0-pp.m_di[i]))*fp.m_w[i];\n        }\n        // sum the damage over all bond families\n        pp.m_D += pp.m_d[i];\n    }\n    // add damage to persistent elastic bonds\n    pp.m_di[n] = m_pIDamg ? m_pIDamg->cdf(pt,Es) : 0;\n    pp.m_d[n] = pp.m_di[n]*fp.m_w[n];\n    pp.m_D += pp.m_d[n];\n}\n\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEReactivePlasticDamage::Stress(FEMaterialPoint& pt)\n{\n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n\n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic damage material point\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    \n    mat3ds s = m_pBase->Stress(pt)*pp.IntactBonds();\n    \n    for (int i=0; i<n; ++i) {\n        // get the elastic deformation gradient\n        mat3d Fv = pe.m_F*pp.m_Fvsi[i];\n        \n        // store safe copy of total deformation gradient\n        mat3d Fs = pe.m_F; double Js = pe.m_J;\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        \n        // evaluate the damaged plastic stress using the elastic deformation gradient\n        s += m_pBase->Stress(pt)*pp.m_wy[i];\n        \n        // restore the original deformation gradient\n        pe.m_F = Fs; pe.m_J = Js;\n    }\n    \n    // return the stress\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEReactivePlasticDamage::Tangent(FEMaterialPoint& pt)\n{\n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n\n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    \n    tens4ds c = m_pBase->Tangent(pt)*pp.IntactBonds();\n    \n    for (int i=0; i<n; ++i) {\n        // get the elastic deformation gradient\n        mat3d Fv = pe.m_F*pp.m_Fvsi[i];\n        \n        // store safe copy of total deformation gradient\n        mat3d Fs = pe.m_F; double Js = pe.m_J;\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        \n        // evaluate the tangent using the elastic deformation gradient\n        c += m_pBase->Tangent(pt)*pp.m_wy[i];\n        \n        // restore the original deformation gradient\n        pe.m_F = Fs; pe.m_J = Js;\n    }\n    \n    // return the tangent\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEReactivePlasticDamage::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n\n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    \n    double sed = m_pBase->StrainEnergyDensity(pt)*pp.IntactBonds();\n    \n    for (int i=0; i<n; ++i) {\n        // get the elastic deformation gradient\n        mat3d Fv = pe.m_F*pp.m_Fvsi[i];\n        double Jvsi = m_isochrc ? 1 : pp.m_Fvsi[i].det();\n\n        // store safe copy of total deformation gradient\n        mat3d Fs = pe.m_F; double Js = pe.m_J;\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        \n        // evaluate the tangent using the elastic deformation gradient\n        sed += m_pBase->StrainEnergyDensity(pt)*pp.m_wy[i]/Jvsi;\n        \n        // restore the original deformation gradient\n        pe.m_F = Fs; pe.m_J = Js;\n    }\n    \n    // return the sed\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FEReactivePlasticDamage::Damage(FEMaterialPoint& pt, int k)\n{\n    //get plastic damage material point data\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    \n    return pp.m_d[k];\n}\n\n//-----------------------------------------------------------------------------\n// get the yield surface normal\nmat3ds FEReactivePlasticDamage::YieldSurfaceNormal(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3ds s = m_pBase->Stress(mp);\n    tens4ds c = m_pBase->Tangent(mp);\n    mat3ds dPhi = m_pCrit->CriterionStressTangent(mp);\n    mat3d M = dPhi*s*2 - mat3dd((dPhi*s).trace()) + c.dot(dPhi);\n    mat3ds Ui = pe.RightStretchInverse();\n    mat3d R = pe.m_F*Ui;\n    mat3ds N = (R.transpose()*M*R*Ui).sym();\n    return N;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate octahedral plastic strain at material point\nvoid FEReactivePlasticDamage::OctahedralPlasticStrain(FEMaterialPoint& pt)\n{\n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    // extract plastic material point\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    \n    double ev[3];\n    for (int i=0; i<n; ++i) {\n        mat3ds Cvsi = (pp.m_Fvsi[i].transpose()*pp.m_Fvsi[i]).sym();\n        Cvsi.eigen2(ev);\n        for (int j=0; j<3; ++j) ev[j] = 1./sqrt(ev[j]);\n        pp.m_gp[i] = sqrt(2.)/3.*sqrt(pow(ev[0] - ev[1],2) + pow(ev[1] - ev[2],2) + pow(ev[2] - ev[0],2));\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate reactive heat supply at material point\nvoid FEReactivePlasticDamage::ReactiveHeatSupplyDensity(FEMaterialPoint& pt)\n{\n    double Rhat = 0;\n    \n    double dt = CurrentTimeIncrement();\n    \n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticDamageMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticDamageMaterialPoint>();\n    \n    if (dt == 0) {\n        pp.m_Rhat = 0;\n        return;\n    }\n    \n    // store safe copy of total deformation gradient\n    mat3d Fs = pe.m_F; double Js = pe.m_J;\n    \n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    for (int i=0; i<n; ++i) {\n        // get the elastic deformation gradients\n        mat3d Fu = Fs*pp.m_Fusi[i];\n        \n        // evaluate strain energy density in the absence of yielding\n        pe.m_F = Fu; pe.m_J = Fu.det();\n        \n        // evaluate the tangent using the elastic deformation gradient\n        Rhat += m_pBase->StrainEnergyDensity(pt)*pp.m_wy[i];\n        \n        mat3d Fv = Fs*pp.m_Fvsi[i];\n        \n        // evaluate strain energy density in the absence of yielding\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        \n        // evaluate the tangent using the elastic deformation gradient\n        Rhat -= m_pBase->StrainEnergyDensity(pt)*pp.m_wy[i];\n    }\n    \n    // get rate\n    Rhat /= dt;\n    \n    // restore the original deformation gradient\n    pe.m_F = Fs; pe.m_J = Js;\n    \n    // return the reactive heat supply\n    pp.m_Rhat = Rhat;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticDamage.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2019 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEPlasticFlowCurve.h\"\n#include \"FEReactivePlasticDamageMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n// This material models damage in any reactive plastic materials.\n\nclass FEReactivePlasticDamage : public FEElasticMaterial\n{\npublic:\n\tFEReactivePlasticDamage(FEModel* pfem);\n    \npublic:\n    //! data initialization and checking\n    bool Init() override;\n    \n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n    \n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n    \n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! evaluate elastic deformation gradient\n    void ElasticDeformationGradient(FEMaterialPoint& pt);\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt, int k);\n    \n    //!< update fatigue material point at each iteration\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp) override;\n    \n\t// returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n    \n    // get the yield surface normal\n    mat3ds YieldSurfaceNormal(FEMaterialPoint& mp);\n    \n    // evaluate octahedral plastic strain\n    void OctahedralPlasticStrain(FEMaterialPoint& pt);\n    \n    // evaluate reactive heat supply\n    void ReactiveHeatSupplyDensity(FEMaterialPoint& pt);\n\n    bool UseSecantTangent() override { return m_secant_tangent; }\n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    FEElasticMaterial*  m_pBase;    // base elastic material\n    FEDamageCriterion*  m_pCrit;    // yield criterion\n    FEPlasticFlowCurve* m_pFlow;    // plastic flow curve\n    FEDamageCDF*        m_pYDamg;   // yield damage model\n    FEDamageCriterion*  m_pYDCrit;  // yield damage criterion\n    FEDamageCDF*        m_pIDamg;   // intact damage model\n    FEDamageCriterion*  m_pIDCrit;  // intact damage criterion\n    \npublic:\n    bool        m_isochrc;  // flag for constraining plastic def grad to be isochoric\n    double      m_rtol;     // user-defined relative tolerance\n    double      m_bias;     // biasing factor for intervals in yield measures and bond fractions\n    bool        m_secant_tangent;   //!< flag for using secant tangent\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticDamageMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2019 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEReactivePlasticDamageMaterialPoint.h\"\n#include \"FEElasticMaterial.h\"\n\n#ifndef max\n#define max(a, b) ((a)>(b)?(a):(b))\n#endif\n\n////////////////////// PLASTIC DAMAGE MATERIAL POINT //////////////////////////////\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEReactivePlasticDamageMaterialPoint::Copy()\n{\n    FEReactivePlasticDamageMaterialPoint* pt = new FEReactivePlasticDamageMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n\n    pt->m_Fusi = m_Fusi;\n    pt->m_Fvsi = m_Fvsi;\n    pt->m_Kv = m_Kv;\n    pt->m_Ku = m_Ku;\n    pt->m_gp = m_gp;\n    pt->m_gpp = m_gpp;\n    pt->m_gc = m_gc;\n    pt->m_wy = m_wy;\n    pt->m_Eyt = m_Eyt;\n    pt->m_Eym = m_Eym;\n    pt->m_di = m_di;\n    pt->m_dy = m_dy;\n    pt->m_d = m_d;\n    pt->m_byld = m_byld;\n    pt->m_byldt = m_byldt;\n\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticDamageMaterialPoint::Init()\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    \n    if (fp.m_binit) {\n        size_t n = fp.m_Ky.size();\n        // intialize data\n        m_Fusi.assign(n, mat3d(1,0,0,\n                               0,1,0,\n                               0,0,1));\n        m_Fvsi.assign(n, mat3d(1,0,0,\n                               0,1,0,\n                               0,0,1));\n        m_Fp = mat3dd(1);\n        m_Ku.assign(n, 0);\n        m_Kv.assign(n, 0);\n        m_gp.assign(n, 0);\n        m_gpp.assign(n, 0);\n        m_gc.assign(n, 0);\n        m_Rhat = 0;\n        m_wy.assign(n,0);\n        m_gp.assign(n, 0);\n        m_Eyt.assign(n, 0);\n        m_Eym.assign(n, 0);\n        m_di.assign(n+1, 0);\n        m_dy.assign(n, 0);\n        m_d.assign(n+1, 0);\n        m_byld.assign(n, false);\n        m_byldt.assign(n, false);\n        \n        // don't forget to initialize the base class\n        FEDamageMaterialPoint::Init();\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticDamageMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n    FEElasticMaterialPoint& pt = *m_pNext->ExtractData<FEElasticMaterialPoint>();\n    for (int i=0; i<m_Fusi.size(); ++i) {\n        m_Fusi[i] = m_Fvsi[i];\n        m_Ku[i] = m_Kv[i];\n        m_gc[i] += fabs(m_gp[i] - m_gpp[i]);\n        m_gpp[i] = m_gp[i];\n        if (m_wy[i] > 0) m_byld[i] = true;\n        m_Eym[i] = max(m_Eym[i], m_Eyt[i]);\n    }\n    m_Emax = max(m_Emax, m_Etrial);\n    m_Fp = pt.m_F;\n    \n    // don't forget to update the base class\n    FEDamageMaterialPoint::Update(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticDamageMaterialPoint::Serialize(DumpStream& ar)\n{\n    FEMaterialPointData::Serialize(ar);\n    \n    if (ar.IsSaving())\n    {\n        ar << m_Fp << m_D << m_Etrial << m_Emax;\n        ar << m_Fusi << m_Fvsi << m_Ku << m_Kv << m_gp << m_gpp << m_gc;\n        ar << m_wy << m_Eyt << m_Eym;\n        ar << m_di << m_dy << m_d << m_byld << m_byldt;\n    }\n    else\n    {\n        ar >> m_Fp >> m_D >> m_Etrial >> m_Emax;\n        ar >> m_Fusi >> m_Fvsi >> m_Ku >> m_Kv >> m_gp >> m_gpp >> m_gc;\n        ar >> m_wy >> m_Eyt >> m_Eym;\n        ar >> m_di >> m_dy >> m_d >> m_byld >> m_byldt;\n    }\n}\n\n//! Evaluate net mass fraction of yielded bonds\ndouble FEReactivePlasticDamageMaterialPoint::YieldedBonds() const\n{\n    double w = 0;\n    for (int i=0; i<m_wy.size(); ++i) w += m_wy[i];\n    return w;\n}\n\n//! Evaluate net mass fraction of intact bonds\ndouble FEReactivePlasticDamageMaterialPoint::IntactBonds() const\n{\n    double w = 0;\n    int n = (int) m_wy.size();\n    if (n == 0) return 1.0;\n    for (int i=0; i<n; ++i) w += m_wy[i] + m_d[i];\n    w += m_d[n];\n    return 1.0-w;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticDamageMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2019 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#pragma once\n#include \"FEDamageMaterialPoint.h\"\n#include \"FEReactivePlasticDamage.h\"\n#include <vector>\n\nclass FEReactivePlasticDamage;\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores the damage and plasticity variables.\nclass FEReactivePlasticDamageMaterialPoint : public FEDamageMaterialPoint\n{\npublic:\n    //! constructor\n    FEReactivePlasticDamageMaterialPoint(FEMaterialPointData *pt, FEReactivePlasticDamage* pmat) : FEDamageMaterialPoint(pt) { m_pMat = pmat; }\n    \n    FEMaterialPointData* Copy() override;\n    \n    //! Initialize material point data\n    void Init() override;\n\n    //! Update material point data\n    void Update(const FETimeInfo& timeInfo) override;\n    \n    //! Serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! Evaluate net mass fraction of yielded bonds\n    double YieldedBonds() const override;\n    \n    // evaluate net mass fraction of intact bonds\n    double IntactBonds() const override;\n    \npublic:\n    vector<mat3d>           m_Fusi;     //!< inverse of plastic deformation gradient at previous yield\n    vector<mat3d>           m_Fvsi;     //!< trial value of plastic deformation gradient at current yield\n    vector<double>          m_Kv;       //!< value of yield measure at current yield\n    vector<double>          m_Ku;       //!< value of yield measure at previous yield\n    vector<double>          m_gp;       //!< current value of octahedral plastic shear strain\n    vector<double>          m_gpp;      //!< previous value of octahedral plastic shear strain\n    vector<double>          m_gc;       //!< cumulative value of octahedral plastic shear strain\n    mat3d                   m_Fp;       //!< deformation gradient at previous time\n    double                  m_Rhat;     //!< reactive heat supply density\n    vector<double>          m_wy;       //!< mass fraction of yielded bonds\n    vector<double>          m_Eyt;      //!< trial yield damage criterion at current time\n    vector<double>          m_Eym;      //!< max yield damage criterion up to current time\n    vector<double>          m_di;       //!< individual family intact damage (0 = no damage, 1 = complete damage)\n    vector<double>          m_dy;       //!< individual family yield damage (0 = no damage, 1 = complete damage)\n    vector<double>          m_d;        //!< total damage for individual family (0 = no damage, 1 = complete damage)\n    vector<bool>            m_byld;     //!< flag on which bonds have already yielded at start of current time\n    vector<bool>            m_byldt;    //!< trial value of m_byld\n    FEReactivePlasticDamage*   m_pMat;     //!< parent material\n};\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include <cmath>\n#include \"FEReactivePlasticity.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/log.h>\n#include <FECore/matrix.h>\n\n//////////////////////// PLASTICITY MATERIAL  /////////////////////////////////\n// define the material parameters\nBEGIN_FECORE_CLASS(FEReactivePlasticity, FEElasticMaterial)\n// set material properties\nADD_PROPERTY(m_pBase, \"elastic\");\nADD_PROPERTY(m_pCrit, \"yield_criterion\");\nADD_PROPERTY(m_pFlow, \"flow_curve\");\n\nADD_PARAMETER(m_isochrc, \"isochoric\");\nADD_PARAMETER(m_rtol   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\nADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEReactivePlasticity::FEReactivePlasticity(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_isochrc = true;\n    m_rtol = 1e-4;\n    m_pBase = nullptr;\n    m_pCrit = nullptr;\n    m_pFlow = nullptr;\n    m_secant_tangent = true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEReactivePlasticity::Init()\n{\n    if (m_pFlow->Init() == false) return false;\n    \n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! serialiation\nvoid FEReactivePlasticity::Serialize(DumpStream& ar)\n{\n    FEElasticMaterial::Serialize(ar);\n    ar & m_isochrc & m_rtol;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEReactivePlasticity::CreateMaterialPointData()\n{\n    FEMaterialPointData* ep = m_pBase->CreateMaterialPointData();\n    FEMaterialPointData* fp = m_pFlow->CreateMaterialPointData();\n    fp->SetNext(ep);\n    return new FEReactivePlasticityMaterialPoint(fp, this);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate elastic deformation gradient\nvoid FEReactivePlasticity::ElasticDeformationGradient(FEMaterialPoint& pt)\n{\n    // initialize flow curve (if not done yet)\n    if (m_pFlow->InitFlowCurve(pt)) {\n        FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n        pp.Init();\n    }\n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    // extract total deformation gradient\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract inverse of plastic deformation gradient and evaluate elastic deformation gradient\n    FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n    FEPlasticFlowCurveMaterialPoint& fp = *pt.ExtractData<FEPlasticFlowCurveMaterialPoint>();\n    FEShellElementNew* sel = dynamic_cast<FEShellElementNew*>(pt.m_elem);\n    \n    for (int i=0; i<n; ++i) {\n        mat3d Fs = pe.m_F;\n        mat3d R = pe.m_F*pe.RightStretchInverse();\n        // for EAS and ANS shells, adjust calculation of Fs using enhanced strain Es\n        if (sel) {\n            mat3ds Cs = mat3dd(1) + sel->m_E[pt.m_index]*2;\n            double eval[3];\n            vec3d evec[3];\n            Cs.eigen2(eval,evec);\n            mat3ds Us = dyad(evec[0])*sqrt(eval[0]) + dyad(evec[1])*sqrt(eval[1]) + dyad(evec[2])*sqrt(eval[2]);\n            Fs = R*Us;\n        }\n        mat3d Fe = Fs*pp.m_Fusi[i];\n        \n        // store safe copy of total deformation gradient\n        mat3d Ftmp = pe.m_F;\n        double Jtmp = pe.m_J;\n        pe.m_F = Fe; pe.m_J = Fe.det();\n        mat3ds Ue = pe.RightStretch();\n        \n        // evaluate yield measure\n        pp.m_Kv[i] = m_pCrit->DamageCriterion(pt);\n        \n        // restore total deformation gradient\n        pe.m_F = Ftmp; pe.m_J = Jtmp;\n        \n        // if there is no yielding, we're done\n        double phi = pp.m_Kv[i] - fp.m_Ky[i];\n        if (phi <= m_rtol*fp.m_Ky[i]) {\n            pp.m_Fvsi[i] = pp.m_Fusi[i];\n            continue;\n        }\n        \n        // check if i-th bond family is yielding\n        if ((pp.m_Kv[i] > pp.m_Ku[i]) && (pp.m_Ku[i] < fp.m_Ky[i]*(1+m_rtol)))\n            pp.m_w[i] = fp.m_w[i];\n        \n        // find Fv\n        bool conv = false;\n        bool done = false;\n        int maxit = 20;\n        int iter = 0;\n        double lam = 0;\n        mat3d Fv = Fe;\n        Ftmp = pe.m_F;  // store safe copy\n        Jtmp = pe.m_J;\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        mat3ds Uv = pe.RightStretch();\n        mat3ds Nv = YieldSurfaceNormal(pt);\n        double Nvmag = Nv.norm();\n        mat3dd I(1);\n        double beta = 1;\n        mat3ds ImN = I;\n        double phi0=0, phi1=0, phi2=0, lam0 = 0, lam1=0, lam2=0, a, b, c=0, d;\n        bool mroot = false;\n        double lroot = 0;\n        while (!done) {\n            pe.m_F = Fv; pe.m_J = Fv.det();\n            pp.m_Kv[i] = m_pCrit->DamageCriterion(pt);\n            phi = pp.m_Kv[i] - fp.m_Ky[i];    // phi = 0 => stay on yield surface\n            int it = iter % 3;\n            if (it == 0) {\n                phi0 = phi;\n                lam0 = lam;\n            }\n            else if (it == 1) {\n                phi1 = phi;\n                lam1 = lam;\n            }\n            else if (it == 2) {\n                phi2 = phi;\n                lam2 = lam;\n            }\n            mat3d dUvdlam = -Ue*Nv*(beta/Nvmag);\n            if (m_isochrc)\n                dUvdlam += Ue*ImN*((ImN.inverse()*Nv/Nvmag).trace()*beta/3.);\n            double dlam = -phi/(Nv*dUvdlam.transpose()).trace();\n            lam += dlam;\n            if (it == 2) {\n                d = (lam0-lam1)*(lam0-lam2)*(lam1-lam2);\n                if (d == 0) {\n                    if (lam0 == lam1) lam = lam1;\n                    else if (lam1 == lam2) lam = lam2;\n                    else lam = lam2;\n                }\n                else {\n                    a = (lam2*(phi1-phi0)+lam1*(phi0-phi2)+lam0*(phi2-phi1))/d;\n                    b = (pow(lam2,2)*(phi0-phi1)+pow(lam0,2)*(phi1-phi2)+pow(lam1,2)*(phi2-phi0))/d;\n                    c = (lam0*lam2*(lam2-lam0)*phi1+pow(lam1,2)*(lam2*phi0-lam0*phi2)+lam1*(phi2*pow(lam0,2)-phi0*pow(lam2,2)))/d;\n                    d = b*b - 4*a*c;\n                    if (d >= 0) {\n                        if (a != 0) {\n                            lam1 = (-b+sqrt(d))/(2*a);\n                            lam2 = (-b-sqrt(d))/(2*a);\n                            if (lam1*lam2 < 0) lam = max(lam1,lam2);\n                            else {\n                                mroot = true;\n                                lam = (fabs(lam1) < fabs(lam2)) ? lam1 : lam2;\n                                lroot = (fabs(lam1) >= fabs(lam2)) ? lam1 : lam2;\n                            }\n                        }\n                        else if (b != 0) lam = -c/b;\n                        else lam = 0;\n                    }\n                    else if (a != 0) {\n                        // first try a least squares linear fit\n                        double sx = lam0 + lam1 + lam2;\n                        double sy = phi0 + phi1 + phi2;\n                        double sx2 = pow(lam0,2) + pow(lam1,2) + pow(lam2,2);\n                        double sxy = lam0*phi0 + lam1*phi1 + lam2*phi2;\n                        double slope = (3*sxy - sx*sy)/(3*sx2 - pow(sx,2));\n                        double intercept = (sy - slope*sx)/3;\n                        lam = -intercept/slope;\n                        // if that produces a negative root, use the minimum of the parabola\n                        if (lam < 0) lam = -b/(2*a);\n                    }\n                    else\n                        lam = (fabs(b) > 0) ? -c/b : 0;\n                }\n                phi = a*pow(lam,2) + b*lam +  c;\n                if (fabs(phi) < m_rtol) { conv = true; done = true; }\n            }\n            ImN = I - Nv*(lam/Nvmag);\n            if (m_isochrc) beta = pow((pp.m_Fusi[i]*ImN).det(), -1./3.);\n            Uv = (Ue*ImN).sym()*beta;\n            Fv = R*Uv;\n            if (fabs(phi) <= m_rtol) { conv = true; done = true;}\n            if (++iter > maxit) done = true;\n            if (done && !conv && mroot) {\n                done = mroot = false;\n                lam = lroot;\n                ImN = I - Nv*(lam/Nvmag);\n                if (m_isochrc) beta = pow((pp.m_Fusi[i]*ImN).det(), -1./3.);\n                Uv = (Ue*ImN).sym()*beta;\n                Fv = R*Uv;\n            }\n        }\n        if (!conv)\n            feLogWarning(\"Plasticity iterations did not converge for bond family %d!\\n\",i);\n        pe.m_F = Fv; pe.m_J = Fv.det();\n        pp.m_Kv[i] = m_pCrit->DamageCriterion(pt);\n        pp.m_Kv[i]  = phi + fp.m_Ky[i];\n        pe.m_F = Ftmp; pe.m_J = Jtmp;\n        pp.m_Fvsi[i] = Fs.inverse()*Fv;\n    }\n    \n    // if bond family has not yielded at this instant, and had not yielded at previous times,\n    // reset the mass fraction of yielded bonds to zero (in case m_w[i] was\n    // set to w[i] during a prior iteration at current time)\n    for (int i=0; i<n; ++i) {\n        if ((pp.m_w[i] > 0) && !pp.m_byld[i]) {\n            if (pp.m_Kv[i] < fp.m_Ky[i]/(1+m_rtol))\n                pp.m_w[i] = 0;\n        }\n    }\n    \n    // evaluate octahedral plastic strain\n    OctahedralPlasticStrain(pt);\n    ReactiveHeatSupplyDensity(pt);\n    \n    return;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEReactivePlasticity::Stress(FEMaterialPoint& pt)\n{\n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n    \n    mat3ds s = m_pBase->Stress(pt)*(1 - pp.YieldedBonds());\n    \n    for (int i=0; i<n; ++i) {\n        if (pp.m_w[i] > 0) {\n            // get the elastic deformation gradient\n            mat3d Fv = pe.m_F*pp.m_Fvsi[i];\n            \n            // store safe copy of total deformation gradient\n            mat3d Fs = pe.m_F; double Js = pe.m_J;\n            pe.m_F = Fv; pe.m_J = Fv.det();\n            \n            // evaluate the stress using the elastic deformation gradient\n            s += m_pBase->Stress(pt)*pp.m_w[i];\n            \n            // restore the original deformation gradient\n            pe.m_F = Fs; pe.m_J = Js;\n        }\n    }\n    \n    // return the stress\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEReactivePlasticity::Tangent(FEMaterialPoint& pt)\n{\n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n    \n    tens4ds c = m_pBase->Tangent(pt)*(1 - pp.YieldedBonds());\n    \n    for (int i=0; i<n; ++i) {\n        if (pp.m_w[i] > 0) {\n            // get the elastic deformation gradient\n            mat3d Fv = pe.m_F*pp.m_Fvsi[i];\n            \n            // store safe copy of total deformation gradient\n            mat3d Fs = pe.m_F; double Js = pe.m_J;\n            pe.m_F = Fv; pe.m_J = Fv.det();\n            \n            // evaluate the tangent using the elastic deformation gradient\n            c += m_pBase->Tangent(pt)*pp.m_w[i];\n            \n            // restore the original deformation gradient\n            pe.m_F = Fs; pe.m_J = Js;\n        }\n    }\n    \n    // return the tangent\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEReactivePlasticity::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    ElasticDeformationGradient(pt);\n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n    \n    double sed = m_pBase->StrainEnergyDensity(pt)*(1 - pp.YieldedBonds());\n    \n    for (int i=0; i<n; ++i) {\n        if (pp.m_w[i] > 0) {\n            // get the elastic deformation gradient\n            mat3d Fv = pe.m_F*pp.m_Fvsi[i];\n            double Jvsi = m_isochrc ? 1 : pp.m_Fvsi[i].det();\n            \n            // store safe copy of total deformation gradient\n            mat3d Fs = pe.m_F; double Js = pe.m_J;\n            pe.m_F = Fv; pe.m_J = Fv.det();\n            \n            // evaluate the tangent using the elastic deformation gradient\n            sed += m_pBase->StrainEnergyDensity(pt)*pp.m_w[i]/Jvsi;\n            \n            // restore the original deformation gradient\n            pe.m_F = Fs; pe.m_J = Js;\n        }\n    }\n    \n    // return the sed\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n// get the yield surface normal\nmat3ds FEReactivePlasticity::YieldSurfaceNormal(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3ds s = m_pBase->Stress(mp);\n    tens4ds c = m_pBase->Tangent(mp);\n    mat3ds dPhi = m_pCrit->CriterionStressTangent(mp);\n    mat3d M = dPhi*s*2 - mat3dd((dPhi*s).trace()) + c.dot(dPhi);\n    mat3ds Ui = pe.RightStretchInverse();\n    mat3d R = pe.m_F*Ui;\n    mat3ds N = (R.transpose()*M*R*Ui).sym();\n    return N;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nvoid FEReactivePlasticity::OctahedralPlasticStrain(FEMaterialPoint& pt)\n{\n    int n = (int)m_pFlow->BondFamilies(pt);\n    // extract plastic material point\n    FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n    \n    double ev[3];\n    for (int i=0; i<n; ++i) {\n        mat3ds Cvsi = (pp.m_Fvsi[i].transpose()*pp.m_Fvsi[i]).sym();\n        Cvsi.eigen2(ev);\n        for (int j=0; j<3; ++j) ev[j] = 1./sqrt(ev[j]);\n        pp.m_gp[i] = sqrt(2.)/3.*sqrt(pow(ev[0] - ev[1],2) + pow(ev[1] - ev[2],2) + pow(ev[2] - ev[0],2));\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate reactive heat supply at material point\nvoid FEReactivePlasticity::ReactiveHeatSupplyDensity(FEMaterialPoint& pt)\n{\n    double Rhat = 0;\n    \n    double dt = CurrentTimeIncrement();\n    \n    // extract elastic material point\n    FEElasticMaterialPoint& pe = *pt.ExtractData<FEElasticMaterialPoint>();\n    // extract plastic material point\n    FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n    \n    if (dt == 0) {\n        pp.m_Rhat = 0;\n        return;\n    }\n    \n    // store safe copy of total deformation gradient\n    mat3d Fs = pe.m_F; double Js = pe.m_J;\n    \n    int n = (int)m_pFlow->BondFamilies(pt);\n    \n    for (int i=0; i<n; ++i) {\n        if (pp.m_w[i] > 0) {\n            // get the elastic deformation gradients\n            mat3d Fu = Fs*pp.m_Fusi[i];\n            \n            // evaluate strain energy density in the absence of yielding\n            pe.m_F = Fu; pe.m_J = Fu.det();\n            \n            // evaluate the tangent using the elastic deformation gradient\n            Rhat += m_pBase->StrainEnergyDensity(pt)*pp.m_w[i];\n            \n            mat3d Fv = Fs*pp.m_Fvsi[i];\n            \n            // evaluate strain energy density in the absence of yielding\n            pe.m_F = Fv; pe.m_J = Fv.det();\n            \n            // evaluate the tangent using the elastic deformation gradient\n            Rhat -= m_pBase->StrainEnergyDensity(pt)*pp.m_w[i];\n        }\n    }\n    \n    // get rate\n    Rhat /= dt;\n    \n    // restore the original deformation gradient\n    pe.m_F = Fs; pe.m_J = Js;\n    \n    // return the reactive heat supply\n    pp.m_Rhat = Rhat;\n}\n\n//-----------------------------------------------------------------------------\n// update plasticity material point at each iteration\nvoid FEReactivePlasticity::UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp)\n{\n    // initialize flow curve (if not done yet)\n    if (m_pFlow->InitFlowCurve(pt)) {\n        FEReactivePlasticityMaterialPoint& pp = *pt.ExtractData<FEReactivePlasticityMaterialPoint>();\n        pp.Init();\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEPlasticFlowCurve.h\"\n#include \"FEReactivePlasticityMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n// This material models reactive plasticity in any hyper-elastic materials.\n\nclass FEReactivePlasticity : public FEElasticMaterial\n{\npublic:\n    FEReactivePlasticity(FEModel* pfem);\n    \npublic:\n    //! data initialization and checking\n    bool Init() override;\n\n    //! serialiation\n    void Serialize(DumpStream& ar) override;\n    \n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! evaluate elastic deformation gradient\n    void ElasticDeformationGradient(FEMaterialPoint& pt);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n    \n    // get the yield surface normal\n    mat3ds YieldSurfaceNormal(FEMaterialPoint& mp);\n    \n    // evaluate octahedral plastic strain\n    void OctahedralPlasticStrain(FEMaterialPoint& pt);\n    \n    // evaluate reactive heat supply\n    void ReactiveHeatSupplyDensity(FEMaterialPoint& pt);\n\n    bool UseSecantTangent() override { return m_secant_tangent; }\n    \n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp) override;\n    \npublic:\n    FEElasticMaterial*  m_pBase;    // base elastic material\n    FEDamageCriterion*  m_pCrit;    // damage criterion\n    FEPlasticFlowCurve* m_pFlow;    // plastic flow curve\n    \npublic:\n    bool        m_isochrc;  // flag for constraining plastic def grad to be isochoric\n    double      m_rtol;     // user-defined relative tolerance\n\n    bool    m_secant_tangent;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticityMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEReactivePlasticityMaterialPoint.h\"\n#include \"FEElasticMaterial.h\"\n\n////////////////////// PLASTICITY MATERIAL POINT //////////////////////////////\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEReactivePlasticityMaterialPoint::Copy()\n{\n    FEReactivePlasticityMaterialPoint* pt = new FEReactivePlasticityMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n\n\tpt->m_Fusi = m_Fusi;\n\tpt->m_Fvsi = m_Fvsi;\n\tpt->m_Ku = m_Ku;\n\tpt->m_Kv = m_Kv;\n\tpt->m_w = m_w;\n\tpt->m_gp = m_gp;\n\tpt->m_gpp = m_gpp;\n\tpt->m_gc = m_gc;\n    pt->m_byld = m_byld;\n\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticityMaterialPoint::Init()\n{\n    FEPlasticFlowCurveMaterialPoint& fp = *ExtractData<FEPlasticFlowCurveMaterialPoint>();\n\n    if (fp.m_binit) {\n        size_t n = fp.m_Ky.size();\n        // intialize data\n        m_Fusi.assign(n, mat3d(1,0,0,\n                               0,1,0,\n                               0,0,1));\n        m_Fvsi.assign(n, mat3d(1,0,0,\n                               0,1,0,\n                               0,0,1));\n        m_Fp = mat3dd(1);\n        m_Ku.assign(n, 0);\n        m_Kv.assign(n, 0);\n        m_w.assign(n,0);\n        m_gp.assign(n, 0);\n        m_gpp.assign(n, 0);\n        m_gc.assign(n, 0);\n        m_byld.assign(n,false);\n        m_Rhat = 0;\n\n        // don't forget to initialize the base class\n        FEMaterialPointData::Init();\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticityMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n    FEElasticMaterialPoint& pt = *m_pNext->ExtractData<FEElasticMaterialPoint>();\n    for (int i=0; i<m_Fusi.size(); ++i) {\n        m_Fusi[i] = m_Fvsi[i];\n        m_Ku[i] = m_Kv[i];\n        m_gc[i] += fabs(m_gp[i] - m_gpp[i]);\n        m_gpp[i] = m_gp[i];\n        if (m_w[i] > 0) m_byld[i] = true;\n    }\n    m_Fp = pt.m_F;\n    \n    // don't forget to update the base class\n\tFEMaterialPointData::Update(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEReactivePlasticityMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\n    if (ar.IsSaving())\n    {\n        ar << m_Rhat << m_Fp;\n        ar << m_w << m_Fusi << m_Fvsi << m_Ku << m_Kv << m_gp << m_gpp << m_gc;\n        ar << m_byld;\n    }\n    else\n    {\n        ar >> m_Rhat >> m_Fp;\n        ar >> m_w >> m_Fusi >> m_Fvsi >> m_Ku >> m_Kv >> m_gp >> m_gpp >> m_gc;\n        ar >> m_byld;\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate net mass fraction of yielded bonds\ndouble FEReactivePlasticityMaterialPoint::YieldedBonds() const\n{\n    double w = 0;\n    for (int i=0; i<m_w.size(); ++i) w += m_w[i];\n    return w;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEReactivePlasticityMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEReactiveMaterialPoint.h\"\n#include \"FEReactivePlasticity.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n// Define a material point that stores the plasticity variables.\nclass FEReactivePlasticityMaterialPoint : public FEReactiveMaterialPoint\n{\npublic:\n    //! constructor\n    FEReactivePlasticityMaterialPoint(FEMaterialPointData*pt, FEElasticMaterial* pmat) : FEReactiveMaterialPoint(pt) { m_pMat = pmat; }\n\n\tFEMaterialPointData* Copy() override;\n    \n    //! Initialize material point data\n    void Init() override;\n    \n    //! Update material point data\n    void Update(const FETimeInfo& timeInfo) override;\n    \n    //! Serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! Evaluate net mass fraction of yielded bonds\n    double YieldedBonds() const override;\n    double IntactBonds() const override { return 1 - YieldedBonds(); }\n\n    \npublic:\n    vector<mat3d>           m_Fusi;     //!< inverse of plastic deformation gradient at previous yield\n    vector<mat3d>           m_Fvsi;     //!< trial value of plastic deformation gradient at current yield\n    vector<double>          m_w;        //!< mass fraction of yielded bonds\n    vector<double>          m_Kv;       //!< value of yield measure at current yield\n    vector<double>          m_Ku;       //!< value of yield measure at previous yield\n    vector<double>          m_gp;       //!< current value of octahedral plastic shear strain\n    vector<double>          m_gpp;      //!< previous value of octahedral plastic shear strain\n    vector<double>          m_gc;       //!< cumulative value of octahedral plastic shear strain\n    vector<bool>            m_byld;     //!< flag on which bonds have already yielded at start of current time\n    mat3d                   m_Fp;       //!< deformation gradient at previous time\n    double                  m_Rhat;     //!< reactive heat supply density\n    FEElasticMaterial*      m_pMat;     //!< parent material\n};\n"
  },
  {
    "path": "FEBioMech/FEReactiveVEMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactiveVEMaterialPoint.h\"\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\nFEReactiveViscoelasticMaterialPoint::FEReactiveViscoelasticMaterialPoint() : FEMaterialPointArray(new FEElasticMaterialPoint)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEReactiveViscoelasticMaterialPoint::Copy()\n{\n    FEReactiveViscoelasticMaterialPoint* pt = new FEReactiveViscoelasticMaterialPoint;\n    pt->m_mp = m_mp;\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEReactiveVEMaterialPoint\n//\n///////////////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\n//! Create a shallow copy of the material point data\nFEMaterialPointData* FEReactiveVEMaterialPoint::Copy()\n{\n    FEReactiveVEMaterialPoint* pt = new FEReactiveVEMaterialPoint(*this);\n    if (m_pNext) pt->m_pNext = m_pNext->Copy();\n    return pt;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes material point data.\nvoid FEReactiveVEMaterialPoint::Init()\n{\n\t// initialize data to zero\n\tm_Uv.clear();\n\tm_Jv.clear();\n\tm_v.clear();\n\tm_f.clear();\n    \n    m_Et = 0;\n    m_wv.clear();\n    \n    // don't forget to initialize the base class\n\tFEMaterialPointData::Init();\n}\n\nvoid FEReactiveVEMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointData::Update(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize data to the archive\nvoid FEReactiveVEMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    \n    if (ar.IsSaving())\n    {\n        int n = (int)m_Uv.size();\n        ar << n;\n        for (int i=0; i<n; ++i) ar << m_Uv[i] << m_Jv[i] << m_v[i] << m_f[i];\n        ar << m_Et;\n        int m = (int)m_wv.size();\n        ar << m;\n        for (int i=0; i<m; ++i) ar << m_wv[i];\n    }\n    else\n    {\n        int n;\n        ar >> n;\n\t\tm_Uv.resize(n);\n\t\tm_Jv.resize(n);\n\t\tm_v.resize(n);\n\t\tm_f.resize(n);\n        for (int i=0; i<n; ++i) ar >> m_Uv[i] >> m_Jv[i] >> m_v[i] >> m_f[i];\n        ar >> m_Et;\n        int m;\n        ar >> m;\n\t\tm_wv.resize(m);\n        for (int i=0; i<m; ++i) ar >> m_wv[i];\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEReactiveVEMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMaterialPoint.h\"\n#include \"FEReactiveViscoelastic.h\"\n#include \"FEUncoupledReactiveViscoelastic.h\"\n#include <deque>\n\nclass FEReactiveViscoelasticMaterial;\nclass FEUncoupledReactiveViscoelasticMaterial;\n\n//-----------------------------------------------------------------------------\n//! Material point data array for reactive viscoelastic materials\n//!\nclass FEReactiveViscoelasticMaterialPoint : public FEMaterialPointArray\n{\npublic:\n    //! constructor\n    FEReactiveViscoelasticMaterialPoint();\n    \n    //! Copy material point data\n    FEMaterialPointData* Copy();\n};\n\n//-----------------------------------------------------------------------------\n//! Material point data for reactive viscoelastic materials\nclass FEReactiveVEMaterialPoint : public FEMaterialPointData\n{\npublic:\n    //! olverloaded constructors\n    FEReactiveVEMaterialPoint(FEMaterialPointData*pt) : FEMaterialPointData(pt) {}\n    \n    //! copy material point data\n\tFEMaterialPointData* Copy() override;\n    \n    //! Initialize material point data\n    void Init() override;\n    \n    //! Update material point data\n    void Update(const FETimeInfo& timeInfo) override;\n\n    //! Serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    // multigenerational material data\n    deque <mat3ds> m_Uv;\t//!< right stretch tensor at tv (when generation u starts breaking)\n    deque <double> m_Jv;\t//!< determinant of Uv (store for efficiency)\n    deque <double> m_v;     //!< time tv when generation starts breaking\n    deque <double> m_f;     //!< mass fraction when generation starts breaking\n    \npublic:\n    // weak bond recruitment parameters\n    double m_Et;            //!< trial strain value at time t\n    deque <double> m_wv;    //!< total mass fraction of weak bonds\n};\n"
  },
  {
    "path": "FEBioMech/FEReactiveViscoelastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactiveViscoelastic.h\"\n#include \"FEElasticMixture.h\"\n#include \"FEElasticFiberMaterial.h\"\n#include \"FEFiberMaterialPoint.h\"\n#include \"FEScaledElasticMaterial.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/log.h>\n#include <limits>\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEReactiveViscoelasticMaterial\n//\n///////////////////////////////////////////////////////////////////////////////\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEReactiveViscoelasticMaterial, FEElasticMaterial)\n    ADD_PARAMETER(m_wmin , FE_RANGE_CLOSED(0.0, 1.0), \"wmin\");\n    ADD_PARAMETER(m_btype, FE_RANGE_CLOSED(1,2), \"kinetics\")->setEnums(\"(invalid)\\0Type I\\0Type II\\0\");\n    ADD_PARAMETER(m_ttype, FE_RANGE_CLOSED(0,2), \"trigger\")->setEnums(\"any strain\\0distortional\\0dilatational\\0\");\n    ADD_PARAMETER(m_emin , FE_RANGE_GREATER_OR_EQUAL(0.0), \"emin\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pBase, \"elastic\");\n\tADD_PROPERTY(m_pBond, \"bond\");\n\tADD_PROPERTY(m_pRelx, \"relaxation\");\n    ADD_PROPERTY(m_pWCDF, \"recruitment\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEReactiveViscoelasticMaterial::FEReactiveViscoelasticMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_wmin = 0;\n    m_btype = 1;\n    m_ttype = 0;\n    m_emin = 0;\n    \n    m_nmax = 0;\n\n\tm_pBase = nullptr;\n\tm_pBond = nullptr;\n\tm_pRelx = nullptr;\n    m_pWCDF = nullptr;\n    \n    m_pDmg = nullptr;\n    m_pFtg = nullptr;\n    m_pRPD = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEReactiveViscoelasticMaterial::Init()\n{\n    FEUncoupledMaterial* m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pBase);\n\tif (m_pMat != nullptr) {\n\t\tfeLogError(\"Elastic material should not be of type uncoupled\");\n\t\treturn false;\n\t}\n    \n    m_pMat = dynamic_cast<FEUncoupledMaterial*>((FEElasticMaterial*)m_pBond);\n\tif (m_pMat != nullptr) {\n\t\tfeLogError(\"Bond material should not be of type uncoupled\");\n\t\treturn false;\n\t}\n    \n    if (!m_pBase->Init()) return false;\n    if (!m_pBond->Init()) return false;\n    if (!m_pRelx->Init()) return false;\n    if (m_pWCDF && !m_pWCDF->Init()) return false;\n    \n    m_pDmg = dynamic_cast<FEDamageMaterial*>(m_pBase);\n    m_pFtg = dynamic_cast<FEReactiveFatigue*>(m_pBase);\n    m_pRPD = dynamic_cast<FEReactivePlasticDamage*>(m_pBase);\n\n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data  arrayfor this material\nFEMaterialPointData* FEReactiveViscoelasticMaterial::CreateMaterialPointData()\n{\n    FEReactiveViscoelasticMaterialPoint* pt = new FEReactiveViscoelasticMaterialPoint();\n    // create materal point for strong bond (base) material\n    FEMaterialPoint* pbase = new FEMaterialPoint(m_pBase->CreateMaterialPointData());\n    pt->AddMaterialPoint(pbase);\n\n    // create materal point for weak bond material\n    FEReactiveVEMaterialPoint* pbond = new FEReactiveVEMaterialPoint(m_pBond->CreateMaterialPointData());\n    pt->AddMaterialPoint(new FEMaterialPoint(pbond));\n    \n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\n//! get base material point\nFEMaterialPoint* FEReactiveViscoelasticMaterial::GetBaseMaterialPoint(FEMaterialPoint& mp)\n{\n    // get the reactive viscoelastic point data\n    FEReactiveViscoelasticMaterialPoint& rvp = *mp.ExtractData<FEReactiveViscoelasticMaterialPoint>();\n    \n    // get the elastic material point\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // extract the strong bond material point data\n    FEMaterialPoint* sb = rvp.GetPointData(0);\n    sb->m_elem = mp.m_elem;\n    sb->m_index = mp.m_index;\n\tsb->m_rt = mp.m_rt;\n\tsb->m_r0 = mp.m_r0;\n\n    // copy the elastic material point data to the strong bond component\n    FEElasticMaterialPoint& epi = *sb->ExtractData<FEElasticMaterialPoint>();\n    epi.m_F = ep.m_F;\n    epi.m_J = ep.m_J;\n    epi.m_v = ep.m_v;\n    epi.m_a = ep.m_a;\n    epi.m_L = ep.m_L;\n\n    return sb;\n}\n\n//-----------------------------------------------------------------------------\n//! get bond material point\nFEMaterialPoint* FEReactiveViscoelasticMaterial::GetBondMaterialPoint(FEMaterialPoint& mp)\n{\n    // get the reactive viscoelastic point data\n    FEReactiveViscoelasticMaterialPoint& rvp = *mp.ExtractData<FEReactiveViscoelasticMaterialPoint>();\n    \n    // get the elastic material point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // extract the weak bond material point data\n    FEMaterialPoint* wb = rvp.GetPointData(1);\n    wb->m_elem = mp.m_elem;\n    wb->m_index = mp.m_index;\n\twb->m_rt = mp.m_rt;\n\twb->m_r0 = mp.m_r0;\n\n    // copy the elastic material point data to the weak bond component\n    FEElasticMaterialPoint& epi = *wb->ExtractData<FEElasticMaterialPoint>();\n    epi.m_F = ep.m_F;\n    epi.m_J = ep.m_J;\n    epi.m_v = ep.m_v;\n    epi.m_a = ep.m_a;\n    epi.m_L = ep.m_L;\n    \n    return wb;\n}\n\n//-----------------------------------------------------------------------------\n//! detect new generation\nbool FEReactiveViscoelasticMaterial::NewGeneration(FEMaterialPoint& mp)\n{\n    double d;\n    double eps = max(m_emin, 10*std::numeric_limits<double>::epsilon());\n\n    // check if the reforming bond mass fraction is above the minimum threshold wmin\n    if (ReformingBondMassFraction(mp) < m_wmin) return false;\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // check if the current deformation gradient is different from that of\n    // the last generation, in which case store the current state\n    // evaluate the relative deformation gradient\n    mat3d F = ep.m_F;\n    int lg = (int)pt.m_Uv.size() - 1;\n    mat3ds Ui = (lg > -1) ? pt.m_Uv[lg].inverse() : mat3dd(1);\n    mat3d Fu = F*Ui;\n\n    switch (m_ttype) {\n        case 0:\n        {\n            // trigger in response to any strain\n            // evaluate the Lagrangian strain\n            mat3ds E = ((Fu.transpose()*Fu).sym() - mat3dd(1))/2;\n            \n            d = E.norm();\n        }\n            break;\n        case 1:\n        {\n            // trigger in response to distortional strain\n            // evaluate spatial Hencky (logarithmic) strain\n            mat3ds Bu = (Fu*Fu.transpose()).sym();\n            double l[3];\n            vec3d v[3];\n            Bu.eigen2(l,v);\n            mat3ds h = (dyad(v[0])*log(l[0]) + dyad(v[1])*log(l[1]) + dyad(v[2])*log(l[2]))/2;\n            \n            // evaluate distortion magnitude (always positive)\n            d = (h.dev()).norm();\n        }\n            break;\n        case 2:\n        {\n            // trigger in response to dilatational strain\n            d = fabs(log(Fu.det()));\n        }\n            break;\n            \n        default:\n            d = 0;\n            break;\n    }\n    \n    if (d > eps) return true;\n    \n    return false;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bond mass fraction\ndouble FEReactiveViscoelasticMaterial::BreakingBondMassFraction(FEMaterialPoint& mp, const int ig, const mat3ds D)\n{\n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // bond mass fraction\n    double w = 0;\n    \n    // current time\n    double time = CurrentTime();\n    double dtv = time - pt.m_v[ig];\n\n    switch (m_btype) {\n        case 1:\n        {\n            if (dtv >= 0)\n                w = pt.m_f[ig]*m_pRelx->Relaxation(mp, dtv, D);\n        }\n            break;\n        case 2:\n        {\n            if (ig == 0) {\n                w = m_pRelx->Relaxation(mp, dtv, D);\n            }\n            else\n            {\n                double dtu = time - pt.m_v[ig-1];\n                w = m_pRelx->Relaxation(mp, dtv, D) - m_pRelx->Relaxation(mp, dtu, D);\n            }\n        }\n            break;\n            \n        default:\n            break;\n    }\n    \n    assert(w >= 0);\n    \n    return w;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bond mass fraction of reforming generation\ndouble FEReactiveViscoelasticMaterial::ReformingBondMassFraction(FEMaterialPoint& mp)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n        \n    mat3ds D = ep.RateOfDeformation();\n    \n    // keep safe copy of deformation gradient\n    mat3d F = ep.m_F;\n    double J = ep.m_J;\n    \n    // get current number of generations\n    int ng = (int)pt.m_Uv.size();\n    \n    double f = 1;\n\n    for (int ig=0; ig<ng-1; ++ig)\n    {\n        // evaluate deformation gradient when this generation starts breaking\n        ep.m_F = pt.m_Uv[ig];\n        ep.m_J = pt.m_Jv[ig];\n        // evaluate the breaking bond mass fraction for this generation\n        f -= BreakingBondMassFraction(mp, ig, D);\n    }\n    \n    // restore safe copy of deformation gradient\n    ep.m_F = F;\n    ep.m_J = J;\n    \n    assert(f >= 0);\n    \n    // return the bond mass fraction of the reforming generation\n    return f;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function for strong bonds\nmat3ds FEReactiveViscoelasticMaterial::StressStrongBonds(FEMaterialPoint& mp)\n{\n    mat3ds s = m_pBase->Stress(*GetBaseMaterialPoint(mp));\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function for weak bonds\nmat3ds FEReactiveViscoelasticMaterial::StressWeakBonds(FEMaterialPoint& mp)\n{\n    double dt = CurrentTime();\n    if (dt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n    \n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n\n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n    \n    // get fiber material point data (if it exists)\n    FEFiberMaterialPoint* fp = wb.ExtractData<FEFiberMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n    \n    // calculate the base material Cauchy stress\n    mat3ds s; s.zero();\n    \n    // current number of breaking generations\n    int ng = (int)pt.m_Uv.size();\n    \n    // no bonds have broken\n    if (ng == 0) {\n        s += m_pBond->Stress(wb);\n    }\n    // bonds have broken\n    else {\n        // keep safe copy of deformation gradient\n        mat3d F = ep.m_F;\n        double J = ep.m_J;\n        \n        double w;\n        mat3ds sb;\n        \n        // calculate the bond stresses for breaking generations\n        for (int ig=0; ig<ng; ++ig) {\n            // evaluate bond mass fraction for this generation\n            ep.m_F = pt.m_Uv[ig];\n            ep.m_J = pt.m_Jv[ig];\n            w = BreakingBondMassFraction(wb, ig, D)*pt.m_wv[ig];\n            // evaluate relative deformation gradient for this generation\n            if (ig > 0) {\n                ep.m_F = F*pt.m_Uv[ig-1].inverse();\n                ep.m_J = J/pt.m_Jv[ig-1];\n                if (fp) fp->SetPreStretch(pt.m_Uv[ig-1]);\n            }\n            else {\n                ep.m_F = F;\n                ep.m_J = J;\n                if (fp) fp->ResetPreStretch();\n            }\n            // evaluate bond stress\n            sb = m_pBond->Stress(wb);\n            // add bond stress to total stress\n            s += (ig > 0) ? sb*w/pt.m_Jv[ig-1] : sb*w;\n        }\n        \n        // restore safe copy of deformation gradient\n        ep.m_F = F;\n        ep.m_J = J;\n    }\n    \n    ep.m_s = s;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FEReactiveViscoelasticMaterial::Stress(FEMaterialPoint& mp)\n{\n    mat3ds s = StressStrongBonds(mp);\n    s+= StressWeakBonds(mp)*(1-Damage(mp));\n    \n\t// return the total Cauchy stress\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent for strong bonds\ntens4ds FEReactiveViscoelasticMaterial::TangentStrongBonds(FEMaterialPoint& mp)\n{\n     // calculate the base material tangent\n    return m_pBase->Tangent(*GetBaseMaterialPoint(mp));\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent for weak bonds\ntens4ds FEReactiveViscoelasticMaterial::TangentWeakBonds(FEMaterialPoint& mp)\n{\n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n\n    // get fiber material point data (if it exists)\n    FEFiberMaterialPoint* fp = wb.ExtractData<FEFiberMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n    \n    // calculate the base material tangent\n    tens4ds c; c.zero();\n    \n    // current number of breaking generations\n    int ng = (int)pt.m_Uv.size();\n    \n    // no bonds have broken\n    if (ng == 0) {\n        c += m_pBond->Tangent(wb);\n    }\n    // bonds have broken\n    else {\n        // keep safe copy of deformation gradient\n        mat3d F = ep.m_F;\n        double J = ep.m_J;\n        \n        double w;\n        tens4ds cb;\n        \n        // calculate the bond tangents for breaking generations\n        for (int ig=0; ig<ng; ++ig) {\n            // evaluate bond mass fraction for this generation\n            ep.m_F = pt.m_Uv[ig];\n            ep.m_J = pt.m_Jv[ig];\n            w = BreakingBondMassFraction(wb, ig, D)*pt.m_wv[ig];\n            // evaluate relative deformation gradient for this generation\n            if (ig > 0) {\n                ep.m_F = F*pt.m_Uv[ig-1].inverse();\n                ep.m_J = J/pt.m_Jv[ig-1];\n                if (fp) fp->SetPreStretch(pt.m_Uv[ig-1]);\n            }\n            else {\n                ep.m_F = F;\n                ep.m_J = J;\n                if (fp) fp->ResetPreStretch();\n            }\n            // evaluate bond tangent\n            cb = m_pBond->Tangent(wb);\n            // add bond tangent to total tangent\n            c += (ig > 0) ? cb*w/pt.m_Jv[ig-1] : cb*w;\n        }\n        \n        // restore safe copy of deformation gradient\n        ep.m_F = F;\n        ep.m_J = J;\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent\ntens4ds FEReactiveViscoelasticMaterial::Tangent(FEMaterialPoint& mp)\n{\n\ttens4ds c = TangentStrongBonds(mp);\n    c += TangentWeakBonds(mp)*(1-Damage(mp));\n    \n\t// return the total tangent\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function in strong bonds\ndouble FEReactiveViscoelasticMaterial::StrongBondSED(FEMaterialPoint& mp)\n{\n    // calculate the base material strain energy density\n    return m_pBase->StrainEnergyDensity(*GetBaseMaterialPoint(mp));\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function in weak bonds\ndouble FEReactiveViscoelasticMaterial::WeakBondSED(FEMaterialPoint& mp)\n{\n    double dt = CurrentTime();\n    if (dt == 0) return 0;\n    \n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n\n    // get fiber material point data (if it exists)\n    FEFiberMaterialPoint* fp = wb.ExtractData<FEFiberMaterialPoint>();\n    \n    // get the viscous point data\n    mat3ds D = ep.RateOfDeformation();\n    \n    double sed = 0;\n    \n    // current number of breaking generations\n    int ng = (int)pt.m_Uv.size();\n    \n    // no bonds have broken\n    if (ng == 0) {\n        sed += m_pBond->StrainEnergyDensity(wb);\n    }\n    // bonds have broken\n    else {\n        // keep safe copy of deformation gradient\n        mat3d F = ep.m_F;\n        double J = ep.m_J;\n        \n        double w;\n        double sedb;\n        \n        // calculate the strain energy density for breaking generations\n        for (int ig=0; ig<ng; ++ig) {\n            // evaluate bond mass fraction for this generation\n            ep.m_F = pt.m_Uv[ig];\n            ep.m_J = pt.m_Jv[ig];\n            w = BreakingBondMassFraction(wb, ig, D)*pt.m_wv[ig];\n            // evaluate relative deformation gradient for this generation\n            if (ig > 0) {\n                ep.m_F = F*pt.m_Uv[ig-1].inverse();\n                ep.m_J = J/pt.m_Jv[ig-1];\n                if (fp) fp->SetPreStretch(pt.m_Uv[ig-1]);\n            }\n            else {\n                ep.m_F = F;\n                ep.m_J = J;\n                if (fp) fp->ResetPreStretch();\n            }\n            // evaluate bond strain energy density\n            sedb = m_pBond->StrainEnergyDensity(wb);\n            // add bond strain energy density to total\n            sed += sedb*w;\n        }\n        \n        // restore safe copy of deformation gradient\n        ep.m_F = F;\n        ep.m_J = J;\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function\ndouble FEReactiveViscoelasticMaterial::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = StrongBondSED(mp);\n    sed += WeakBondSED(mp)*(1-Damage(mp));\n    \n    // return the total strain energy density\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! Cull generations that have relaxed below a threshold\nvoid FEReactiveViscoelasticMaterial::CullGenerations(FEMaterialPoint& mp)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n    \n    // keep safe copy of deformation gradient\n    mat3d F = ep.m_F;\n    double J = ep.m_J;\n    \n    int ng = (int)pt.m_v.size();\n    m_nmax = max(m_nmax, ng);\n    \n    // don't cull if we have too few generations\n    if (ng < 3) return;\n\n    // don't reduce number of generations to less than max value achieved so far\n    if (ng < m_nmax) return;\n\n    // always check oldest generation\n    ep.m_F = pt.m_Uv[0];\n    ep.m_J = pt.m_Jv[0];\n    double w0 = BreakingBondMassFraction(mp, 0, D)*pt.m_wv[0];\n    if (w0 < m_wmin) {\n        ep.m_F = pt.m_Uv[1];\n        ep.m_J = pt.m_Jv[1];\n        double w1 = BreakingBondMassFraction(mp, 1, D)*pt.m_wv[1];\n        pt.m_v[1] = (w0*pt.m_v[0] + w1*pt.m_v[1])/(w0+w1);\n        pt.m_Uv[1] = (pt.m_Uv[0]*w0 + pt.m_Uv[1]*w1)/(w0+w1);\n        pt.m_Jv[1] = pt.m_Uv[1].det();\n        pt.m_f[1] = (w0*pt.m_f[0] + w1*pt.m_f[1])/(w0+w1);\n        pt.m_wv[1] = (w0*pt.m_wv[0] + w1*pt.m_wv[1])/(w0+w1);\n        pt.m_Uv.pop_front();\n        pt.m_Jv.pop_front();\n        pt.m_v.pop_front();\n        pt.m_f.pop_front();\n        pt.m_wv.pop_front();\n    }\n    \n    // restore safe copy of deformation gradient\n    ep.m_F = F;\n    ep.m_J = J;\n\n    return;\n}\n\n//-----------------------------------------------------------------------------\n//! Update specialized material points\nvoid FEReactiveViscoelasticMaterial::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FEMaterialPoint& sb = *GetBaseMaterialPoint(mp);\n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // start by updating specialized material points of base and bond materials\n    m_pBase->UpdateSpecializedMaterialPoints(sb, tp);\n    m_pBond->UpdateSpecializedMaterialPoints(wb, tp);\n    \n    // if the this material is a fiber and if the fiber is in compression, skip this update\n    if ((dynamic_cast<FEElasticFiberMaterial*>(m_pBase)) && (dynamic_cast<FEElasticFiberMaterial*>(m_pBond)))\n        if ((m_pBase->Stress(mp)).norm() == 0) return;\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n\n    mat3ds Uv = ep.RightStretch();\n    double Jv = ep.m_J;\n\n    // if new generation not already created for current time, check if it should\n    if (pt.m_v.empty() || (pt.m_v.back() < tp.currentTime)) {\n        // check if the current deformation gradient is different from that of\n        // the last generation, in which case store the current state\n        if (NewGeneration(wb)) {\n            pt.m_v.push_back(tp.currentTime);\n            pt.m_Uv.push_back(Uv);\n            pt.m_Jv.push_back(Jv);\n            if (m_pWCDF) {\n                pt.m_Et = ScalarStrain(mp);\n                pt.m_wv.push_back(m_pWCDF->brf(mp,pt.m_Et));\n            }\n            else pt.m_wv.push_back(1);\n            double f = (!pt.m_v.empty()) ? ReformingBondMassFraction(wb) : 1;\n            pt.m_f.push_back(f);\n            CullGenerations(wb);\n        }\n    }\n    // otherwise, if we already have a generation for the current time, update the stored values\n    else if (pt.m_v.back() == tp.currentTime) {\n        pt.m_Uv.back() = Uv;\n        pt.m_Jv.back() = Jv;\n        if (m_pWCDF) {\n            pt.m_Et = ScalarStrain(mp);\n            pt.m_wv.back() = m_pWCDF->brf(mp,pt.m_Et);\n        }\n        pt.m_f.back() = ReformingBondMassFraction(wb);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bond mass fraction of reforming generation\nint FEReactiveViscoelasticMaterial::RVEGenerations(FEMaterialPoint& mp)\n{\n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // return the bond mass fraction of the reforming generation\n    return (int)pt.m_v.size();\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate trigger strain\ndouble FEReactiveViscoelasticMaterial::ScalarStrain(FEMaterialPoint& mp)\n{\n    double d;\n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    switch (m_ttype) {\n        case 0:\n        {\n            // evaluate the Lagrangian strain\n            mat3ds E = ep.Strain();\n            \n            d = E.norm();\n        }\n            break;\n        case 1:\n        {\n            // distortional strain\n            // evaluate spatial Hencky (logarithmic) strain\n            mat3ds h = ep.LeftHencky();\n            \n            // evaluate distortion magnitude (always positive)\n            d = (h.dev()).norm();\n        }\n            break;\n        case 2:\n        {\n            // dilatational strain\n            d = fabs(log(ep.m_J));\n        }\n            break;\n            \n        default:\n            d = 0;\n            break;\n    }\n    \n    return d;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEReactiveViscoelasticMaterial::Damage(FEMaterialPoint& mp)\n{\n    double D = 0;\n    if (m_pDmg) D = m_pDmg->Damage(*GetBaseMaterialPoint(mp));\n    else if (m_pFtg) D = m_pFtg->Damage(*GetBaseMaterialPoint(mp));\n    else if (m_pRPD) {\n        FEMaterialPoint& pt = *GetBaseMaterialPoint(mp);\n        const FEReactiveMaterialPoint* ppd = pt.ExtractData<FEReactiveMaterialPoint>();\n        D = ppd->BrokenBonds();\n    }\n    return D;\n}\n\nvoid FEReactiveViscoelasticMaterial::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tar & m_nmax;\n}\n"
  },
  {
    "path": "FEBioMech/FEReactiveViscoelastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"FEBondRelaxation.h\"\n#include \"FEBondRecruitment.h\"\n#include \"FEReactiveVEMaterialPoint.h\"\n#include \"FEDamageMaterial.h\"\n#include \"FEReactiveFatigue.h\"\n#include \"FEReactivePlasticDamage.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a large deformation reactive viscoelastic material\n//\nclass FEReactiveViscoelasticMaterial :\tpublic FEElasticMaterial\n{\npublic:\n\t//! default constructor\n\tFEReactiveViscoelasticMaterial(FEModel* pfem);\n       \n\t//! get the elastic base material\n\tFEElasticMaterial* GetBaseMaterial() { return m_pBase; }\n    \n\t//! Set the base material\n\tvoid SetBaseMaterial(FEElasticMaterial* pbase) { m_pBase = pbase; }\n    \n\t//! get the elastic bond material\n\tFEElasticMaterial* GetBondMaterial() { return m_pBond; }\n    \n\t//! Set the base material\n\tvoid SetBondMaterial(FEElasticMaterial* pbond) { m_pBond = pbond; }\n    \npublic:\n    //! data initialization\n    bool Init() override;\n    \n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! stress function\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n    mat3ds StressStrongBonds(FEMaterialPoint& pt);\n    mat3ds StressWeakBonds(FEMaterialPoint& pt);\n\n\t//! tangent function\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n    tens4ds TangentStrongBonds(FEMaterialPoint& pt);\n    tens4ds TangentWeakBonds(FEMaterialPoint& pt);\n\n    //! strain energy density function\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    double StrongBondSED(FEMaterialPoint& pt) override;\n    double WeakBondSED(FEMaterialPoint& pt) override;\n\n    //! cull generations\n    void CullGenerations(FEMaterialPoint& pt);\n    \n    //! evaluate bond mass fraction for a given generation\n    double BreakingBondMassFraction(FEMaterialPoint& pt, const int ig, const mat3ds D);\n    \n    //! evaluate bond mass fraction of reforming generation\n    double ReformingBondMassFraction(FEMaterialPoint& pt);\n    \n    //! detect new generation\n    bool NewGeneration(FEMaterialPoint& pt);\n    \n    //! return number of generations\n    int RVEGenerations(FEMaterialPoint& pt);\n    \n\t//! returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \n    //! get base material point\n    FEMaterialPoint* GetBaseMaterialPoint(FEMaterialPoint& mp);\n\n    //! get bond material point\n    FEMaterialPoint* GetBondMaterialPoint(FEMaterialPoint& mp);\n    \n    //! evaluate scalar strain measure (same type as trigger strain for bond breaking)\n    double ScalarStrain(FEMaterialPoint& mp);\n\nprivate:\n\tFEElasticMaterial*\tm_pBase;\t//!< pointer to elastic solid material for strong bonds\n\tFEElasticMaterial*\tm_pBond;\t//!< pointer to elastic solid material for reactive bonds\n\tFEBondRelaxation*   m_pRelx;    //!< pointer to bond relaxation material for reactive bonds\n    FEBondRecruitment*  m_pWCDF;    //!< pointer to weak bond recruitment function\n\nprivate:\n    FEDamageMaterial*   m_pDmg;     //!< pointer to base material if it is a FEDamageMaterial\n    FEReactiveFatigue*  m_pFtg;     //!< pointer to base material if it is a FEReactiveFatigue\n    FEReactivePlasticDamage*    m_pRPD;  //!< pointer to base material if it is a FEReactivePlasticDamage\n    double Damage(FEMaterialPoint& mp); //!< return damage in this material\n\npublic:\n    double\tm_wmin;\t\t//!< minimum value of relaxation\n    int     m_btype;    //!< bond kinetics type\n    int     m_ttype;    //!< bond breaking trigger type\n    double  m_emin;     //!< strain threshold for triggering new generation\n    \n    int     m_nmax;     //!< highest number of generations achieved in analysis\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERelativeVolumeCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERelativeVolumeCriterion.h\"\n#include \"FEElasticMaterial.h\"\n\nBEGIN_FECORE_CLASS(FERelativeVolumeCriterion, FEMeshAdaptorCriterion)\nEND_FECORE_CLASS();\n\nFERelativeVolumeCriterion::FERelativeVolumeCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n}\n\nbool FERelativeVolumeCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tFEElasticMaterialPoint* ep = mp.ExtractData<FEElasticMaterialPoint>();\n\tif (ep == nullptr) return false;\n\n\t// evaluate the relative volume at this point\n\tvalue = ep->m_J;\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioMech/FERelativeVolumeCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2024 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n#include \"FEDamageMaterial.h\"\n\nclass FERelativeVolumeCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFERelativeVolumeCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEBioMech/FERemodelingElasticDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERemodelingElasticDomain.h\"\n#include \"FERemodelingElasticMaterial.h\"\n#include \"FECore/FEAnalysis.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFERemodelingElasticDomain::FERemodelingElasticDomain(FEModel* pfem) : FEElasticSolidDomain(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// Reset data\nvoid FERemodelingElasticDomain::Reset()\n{\n\tFEElasticSolidDomain::Reset();\n\n\tFERemodelingElasticMaterial* pre = m_pMat->ExtractProperty<FERemodelingElasticMaterial>();\n\tFEElasticMaterial* pme = pre->GetElasticMaterial();\n\n\t// initialize rhor for each material point\n\tForEachMaterialPoint([&](FEMaterialPoint& mp) {\n\t\tFERemodelingMaterialPoint& pt = *mp.ExtractData<FERemodelingMaterialPoint>();\n\t\tpt.m_rhor = pme->Density(mp);\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo The material point initialization needs to move to the base class.\nbool FERemodelingElasticDomain::Init()\n{\n\t// initialize base class\n\tif (FEElasticSolidDomain::Init() == false) return false;\n\n\tFERemodelingElasticMaterial* pre = m_pMat->ExtractProperty<FERemodelingElasticMaterial>();\n\tif (pre == nullptr) return false;\n\n\tFEElasticMaterial* pme = pre->GetElasticMaterial();\n\tif (pme == nullptr) return false;\n\n\t// initialize rhor for each material point\n\tForEachMaterialPoint([&](FEMaterialPoint& mp) {\n\t\tFERemodelingMaterialPoint& pt = *mp.ExtractData<FERemodelingMaterialPoint>();\n\t\tpt.m_rhor = pme->Density(mp);\n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the global stiffness matrix for this domain\nvoid FERemodelingElasticDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\n\t// I only need this for the element density stiffness\n\tdouble dt = GetFEModel()->GetTime().timeIncrement;\n\n\t#pragma omp parallel for\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 3*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate geometrical stiffness\n\t\tElementGeometricalStiffness(el, ke);\n\n\t\t// calculate material stiffness\n\t\tElementMaterialStiffness(el, ke);\n\n\t\t// calculate density stiffness\n\t\tElementDensityStiffness(dt, el, ke);\n\n\t\t// assign symmetic parts\n\t\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t\t// grabs elements from the upper diagonal matrix?\n\t\tfor (int i=0; i<ndof; ++i)\n\t\t\tfor (int j=i+1; j<ndof; ++j)\n\t\t\t\tke[j][i] = ke[i][j];\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the solid element stiffness matrix\nvoid FERemodelingElasticDomain::ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke)\n{\n\tFESolidElement& el = Element(iel);\n\n\t// calculate material stiffness (i.e. constitutive component)\n\tElementMaterialStiffness(el, ke);\n\n\t// calculate geometrical stiffness\n\tElementGeometricalStiffness(el, ke);\n\n\t// calculate density stiffness\n\tElementDensityStiffness(tp.timeIncrement, el, ke);\n\n\t// assign symmetic parts\n\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t// grabs elements from the upper diagonal matrix?\n\tint ndof = 3*el.Nodes();\n\tfor (int i=0; i<ndof; ++i)\n\t\tfor (int j=i+1; j<ndof; ++j)\n\t\t\tke[j][i] = ke[i][j];\n}\n\n\n//-----------------------------------------------------------------------------\n//! calculates element's density stiffness component for integration point n\n//! \\todo Remove the FEModel parameter. We only need to dt parameter, not the entire model.\n//! \\todo Problems seem to run better without this stiffness matrix\nvoid FERemodelingElasticDomain::ElementDensityStiffness(double dt, FESolidElement &el, matrix &ke)\n{\n\tint n, i, j;\n\n\t// make sure this is a remodeling material\n\tFERemodelingElasticMaterial* pmat = dynamic_cast<FERemodelingElasticMaterial*>(m_pMat); assert(pmat);\n    \n    const int NE = FEElement::MAX_NODES;\n    vec3d gradN[NE];\n    double *Grn, *Gsn, *Gtn;\n    double Gr, Gs, Gt;\n    vec3d kru, kur;\n        \n    // nr of nodes\n    int neln = el.Nodes();\n        \n    // nr of integration points\n    int nint = el.GaussPoints();\n        \n    // jacobian\n    double Ji[3][3], detJt;\n        \n    // weights at gauss points\n    const double *gw = el.GaussWeights();\n        \n    // density stiffness component for the stiffness matrix\n    mat3d kab;\n        \n    // calculate geometrical element stiffness matrix\n    for (n=0; n<nint; ++n)\n    {\n        // calculate jacobian\n        double J = invjact(el, Ji, n);\n        detJt = J*gw[n];\n            \n        Grn = el.Gr(n);\n        Gsn = el.Gs(n);\n        Gtn = el.Gt(n);\n            \n        for (i=0; i<neln; ++i)\n        {\n            Gr = Grn[i];\n            Gs = Gsn[i];\n            Gt = Gtn[i];\n                \n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN[i].x = Ji[0][0]*Gr+Ji[1][0]*Gs+Ji[2][0]*Gt;\n            gradN[i].y = Ji[0][1]*Gr+Ji[1][1]*Gs+Ji[2][1]*Gt;\n            gradN[i].z = Ji[0][2]*Gr+Ji[1][2]*Gs+Ji[2][2]*Gt;\n        }\n            \n        // get the material point data\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        double drhohat = pmat->m_pSupp->Tangent_Supply_Density(mp);\n        mat3ds ruhat = pmat->m_pSupp->Tangent_Supply_Strain(mp);\n        mat3ds crho = pmat->Tangent_Stress_Density(mp);\n        double krr = (drhohat - 1./dt)/J;\n            \n        for (i=0; i<neln; ++i) {\n            kur = (crho*gradN[i])/krr;\n            for (j=0; j<neln; ++j)\n            {\n                kru = ruhat*gradN[j];\n                kab = kur & kru;\n                ke[3*i  ][3*j  ] -= kab(0,0)*detJt;\n                ke[3*i  ][3*j+1] -= kab(0,1)*detJt;\n                ke[3*i  ][3*j+2] -= kab(0,2)*detJt;\n                    \n                ke[3*i+1][3*j  ] -= kab(1,0)*detJt;\n                ke[3*i+1][3*j+1] -= kab(1,1)*detJt;\n                ke[3*i+1][3*j+2] -= kab(1,2)*detJt;\n                    \n                ke[3*i+2][3*j  ] -= kab(2,0)*detJt;\n                ke[3*i+2][3*j+1] -= kab(2,1)*detJt;\n                ke[3*i+2][3*j+2] -= kab(2,2)*detJt;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FERemodelingElasticDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a domain used in an elastic remodeling problem.\n//! It differs from the FEElasticSolidDomain in that it adds a stiffness matrix\n//! due to the deformation dependent density.\nclass FERemodelingElasticDomain : public FEElasticSolidDomain\n{\npublic:\n\t//! constructor\n\tFERemodelingElasticDomain(FEModel* pfem);\n\n\t//! reset element data\n\tvoid Reset() override;\n\n\t//! initialize class\n\tbool Init() override;\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculates the solid element stiffness matrix (\\todo is this actually used anywhere?)\n\tvirtual void ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke) override;\n\nprivate:\n\t//! density stiffness component\n\tvoid ElementDensityStiffness(double dt, FESolidElement& el, matrix& ke);\n};\n"
  },
  {
    "path": "FEBioMech/FERemodelingElasticMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERemodelingElasticMaterial.h\"\n#include \"FECore/FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FERemodelingMaterialPoint::Copy()\n{\n\tFERemodelingMaterialPoint* pt = new FERemodelingMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERemodelingMaterialPoint::Init()\n{\n\t// intialize data to zero\n\tm_sed = m_dsed = 0; \n\tm_rhor = m_rhorp = 0;\n        \n\t// don't forget to initialize the base class\n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERemodelingMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tm_rhorp = m_rhor;\n        \n\t// don't forget to initialize the base class\n\tFEMaterialPointData::Update(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERemodelingMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_sed & m_dsed;\n\tar & m_rhor & m_rhorp;\n}\n\n//=============================================================================\n// FERemodelingElasticMaterial\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FERemodelingElasticMaterial, FEElasticMaterial)\n\tADD_PARAMETER(m_rhormin, \"min_density\")->setUnits(UNIT_DENSITY)->setLongName(\"min density\");\n\tADD_PARAMETER(m_rhormax, \"max_density\")->setUnits(UNIT_DENSITY)->setLongName(\"max density\");\n\n\tADD_PROPERTY(m_pBase, \"solid\");\n\tADD_PROPERTY(m_pSupp, \"supply\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERemodelingElasticMaterial::FERemodelingElasticMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_pBase = 0;\n\tm_pSupp = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density function\ndouble FERemodelingElasticMaterial::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\treturn (dynamic_cast<FERemodelingInterface*>((FEElasticMaterial*)m_pBase))->StrainEnergy(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate referential mass density\ndouble FERemodelingElasticMaterial::Density(FEMaterialPoint& mp)\n{\n    FERemodelingMaterialPoint& rpt = *(mp.ExtractData<FERemodelingMaterialPoint>());\n    return rpt.m_rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FERemodelingElasticMaterial::Stress(FEMaterialPoint& mp)\n{\n\tdouble dt = CurrentTimeIncrement();\n\n    FERemodelingMaterialPoint& rpt = *(mp.ExtractData<FERemodelingMaterialPoint>());\n\n\t// calculate the strain energy density at this material point\n\trpt.m_sed = StrainEnergyDensity(mp);\n\n\t// calculate the sed derivative with respect to mass density at this material point\n    rpt.m_dsed = Tangent_SE_Density(mp);\n                \n\tdouble rhorhat = m_pSupp->Supply(mp);\n\trpt.m_rhor = rhorhat*dt + rpt.m_rhorp;\n\tif (rpt.m_rhor > m_rhormax) rpt.m_rhor = m_rhormax;\n\tif (rpt.m_rhor < m_rhormin) rpt.m_rhor = m_rhormin;\n\n\treturn m_pBase->Stress(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of stress with strain\ntens4ds FERemodelingElasticMaterial::Tangent(FEMaterialPoint& mp)\n{\n\treturn m_pBase->Tangent(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of strain energy density with mass density\ndouble FERemodelingElasticMaterial::Tangent_SE_Density(FEMaterialPoint& pt)\n{\n\treturn (dynamic_cast<FERemodelingInterface*>((FEElasticMaterial*)m_pBase))->Tangent_SE_Density(pt);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of stress with mass density\nmat3ds FERemodelingElasticMaterial::Tangent_Stress_Density(FEMaterialPoint& pt)\n{\n\treturn (dynamic_cast<FERemodelingInterface*>((FEElasticMaterial*)m_pBase))->Tangent_Stress_Density(pt);\n}\n"
  },
  {
    "path": "FEBioMech/FERemodelingElasticMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for solid supply.\n//! These materials need to define the solid supply and tangent supply functions.\n//! The solid supply has units of mass/(referential volume)/time\n//!\n\nclass FEBIOMECH_API FESolidSupply : public FEMaterialProperty\n{\npublic:\n\t//! constructor\n\tFESolidSupply(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\n\t//! solid supply\n\tvirtual double Supply(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of solute supply with respect to strain\n\tvirtual mat3ds Tangent_Supply_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of solute supply with respect to referential density\n\tvirtual double Tangent_Supply_Density(FEMaterialPoint& mp) = 0;\t\n\n\tFECORE_BASE_CLASS(FESolidSupply)\n};\n\n//-----------------------------------------------------------------------------\n//! Material point data for remodeling elastic materials\nclass FEBIOMECH_API FERemodelingMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFERemodelingMaterialPoint(FEMaterialPointData*pt) : FEMaterialPointData(pt) {}\n    \n\tFEMaterialPointData* Copy();\n    \n\tvoid Init();\n    \n\tvoid Update(const FETimeInfo& timeInfo);\n\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tdouble\t\tm_sed;\t\t//!< strain energy density\n\tdouble\t\tm_dsed;\t\t//!< derivative of strain energy density with mass density\n\tdouble\t\tm_rhor;\t\t//!< current referential mass density\n\tdouble\t\tm_rhorp;\t//!< referential mass density at previous time step\n};\n\n//-----------------------------------------------------------------------------\n//! A material that wants to use the remodeling framework needs to implement \n//! this additional interface.\nclass FERemodelingInterface\n{\npublic:\n\t//! calculate strain energy density at material point\n\tvirtual double StrainEnergy(FEMaterialPoint& pt) = 0;\n\n\t//! calculate tangent of strain energy density with solid density at material point\n\tvirtual double Tangent_SE_Density(FEMaterialPoint& pt) = 0;\n\n\t//! calculate tangent of stress with solid density at material point\n\tvirtual mat3ds Tangent_Stress_Density(FEMaterialPoint& pt) = 0;\n    \npublic:\n    int     m_comp;     // mixture component to which this material belongs (if applicable)\n    int     m_sbm;      // sbm ID associated with this material\n};\n\n//-----------------------------------------------------------------------------\n//! Material class for remodeling solids\nclass FEBIOMECH_API FERemodelingElasticMaterial : public FEElasticMaterial, public FERemodelingInterface\n{\npublic:\n\t//! constructor\n\tFERemodelingElasticMaterial(FEModel* pfem);\n\t\n\t//! strain energy density function\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n\t\n\t//! stress function\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! tangent function of stress with strain\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! evaluate referential mass density\n    double Density(FEMaterialPoint& pt) override;\n    \n    //! evaluate strain energy density of remodeling interface\n    double StrainEnergy(FEMaterialPoint& pt) override { return StrainEnergyDensity(pt); }\n\t\n\t//! tangent function of strain energy density with solid mass density\n\tdouble Tangent_SE_Density(FEMaterialPoint& pt) override;\n\t\n\t//! tangent function of stress with solid mass density\n\tmat3ds Tangent_Stress_Density(FEMaterialPoint& pt) override;\n\t\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override\n\t{\n\t\treturn new FERemodelingMaterialPoint(m_pBase->CreateMaterialPointData());\n\t}\n    \n\t// get the elastic material\n\tFEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n\npublic:\n\tFEElasticMaterial*\tm_pBase;\t\t//!< pointer to elastic solid material\n\tFESolidSupply*\t\tm_pSupp;\t\t//!< pointer to solid supply material\n\tdouble\t\t\t\tm_rhormin;\t\t//!< minimum density\n\tdouble\t\t\t\tm_rhormax;\t\t//!< maximum density\n\t\npublic:\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEResidualVector.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEResidualVector.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/DOFS.h\"\n#include \"FEMechModel.h\"\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FEAnalysis.h>\n#include \"FESolidSolver2.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nFEResidualVector::FEResidualVector(FEModel& fem, vector<double>& R, vector<double>& Fr) : FEGlobalVector(fem, R, Fr)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEResidualVector::~FEResidualVector()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FEResidualVector::Assemble(vector<int>& en, vector<int>& elm, vector<double>& fe, bool bdom)\n{\n\tFEMesh& mesh = m_fem.GetMesh();\n\n\tvector<double>& R = m_R;\n\n\t// assemble the element residual into the global residual\n\tint ndof = (int)fe.size();\n\tint ndn = ndof / (int)en.size();\n\tfor (int i = 0; i < ndof; ++i)\n\t{\n\t\tint I = elm[i];\n\n\t\tif (I >= 0) {\n\t\t\t#pragma omp atomic\n\t\t\tR[I] += fe[i];\n\t\t}\n\t\t// TODO: Find another way to store reaction forces\n\t\telse if (-I - 2 >= 0) \n\t\t{\n\t\t\t// Don't add the reaction force to rigid nodes\n\t\t\tint nid = en[i / ndn];\n\t\t\tif (nid >= 0)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(nid);\n\t\t\t\tif (node.m_rid < 0)\n\t\t\t\t{\n#pragma omp atomic\n\t\t\t\t\tm_Fr[-I - 2] -= fe[i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// if there are linear constraints we need to apply them\n\t// process linear constraints\n\tFELinearConstraintManager& LCM = m_fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints())\n\t{\n\t\tLCM.AssembleResidual(R, en, elm, fe);\n\t}\n\n\t// If there are rigid bodies we need to look for rigid dofs\n\tFEMechModel* fem = dynamic_cast<FEMechModel*>(&m_fem);//assert(fem);\n\tif (fem && (fem->RigidBodies() > 0))\n\t{\n\t\tfor (int i = 0; i < ndof; i += ndn)\n\t\t{\n\t\t\tint nid = en[i / ndn];\n\t\t\tif (nid >= 0)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(nid);\n\t\t\t\tif (node.m_rid >= 0)\n\t\t\t\t{\n\t\t\t\t\tvec3d F(fe[i], fe[i + 1], fe[i + 2]);\n\n\t\t\t\t\t// this is an interface dof\n\t\t\t\t\t// get the rigid body this node is connected to\n\t\t\t\t\tFERigidBody& RB = *fem->GetRigidBody(node.m_rid);\n\n\t\t\t\t\t// add to total torque of this body\n\t\t\t\t\tdouble alpha = fem->GetTime().alphaf;\n\t\t\t\t\tvec3d f = F;\n\t\t\t\t\tvec3d a = (node.m_rt - RB.m_rt)* alpha + (node.m_rp - RB.m_rp) * (1 - alpha);\n//\t\t\t\t\tvec3d a = node.m_rt - RB.m_rt;\n\t\t\t\t\tvec3d m = a ^ F;\n\n\t\t\t\t\t// TODO: This code is only relevant when called from the shell domain residual and applies\n\t\t\t\t\t//\t     the reaction of the back-face nodes.\n\t\t\t\t\tif (bdom)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (node.HasFlags(FENode::SHELL) && node.HasFlags(FENode::RIGID_CLAMP)) {\n\t\t\t\t\t\t\tvec3d d = node.m_dt;\n\t\t\t\t\t\t\tvec3d b = a - d;\n\t\t\t\t\t\t\tvec3d Fd(fe[i + 3], fe[i + 4], fe[i + 5]);\n\t\t\t\t\t\t\tf += Fd;\n\t\t\t\t\t\t\tm += b ^ Fd;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tRB.m_Fr.x -= f.x;\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tRB.m_Fr.y -= f.y;\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tRB.m_Fr.z -= f.z;\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tRB.m_Mr.x -= m.x;\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tRB.m_Mr.y -= m.y;\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tRB.m_Mr.z -= m.z;\n\n\t\t\t\t\tconst int* lm = RB.m_LM;\n\n\t\t\t\t\tif (lm[0] >= 0) {\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tR[lm[0]] += f.x;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lm[1] >= 0) {\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tR[lm[1]] += f.y;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lm[2] >= 0) {\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tR[lm[2]] += f.z;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lm[3] >= 0) {\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tR[lm[3]] += m.x;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lm[4] >= 0) {\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tR[lm[4]] += m.y;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lm[5] >= 0) {\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tR[lm[5]] += m.z;\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t// if the rotational degrees of freedom are constrained for a rigid node\n\t\t\t\t\t// then we need to add an additional component to the residual\n\t\t\t\t\tif (node.m_ID[m_dofRU] == lm[3])\n\t\t\t\t\t{\n\t\t\t\t\t\td = node.m_Dt;\n\t\t\t\t\t\tn = lm[3]; if (n >= 0) R[n] += d.y*F.z-d.z*F.y; RB.m_Mr.x -= d.y*F.z-d.z*F.y;\n\t\t\t\t\t\tn = lm[4]; if (n >= 0) R[n] += d.z*F.x-d.x*F.z; RB.m_Mr.y -= d.z*F.x-d.x*F.z;\n\t\t\t\t\t\tn = lm[5]; if (n >= 0) R[n] += d.x*F.y-d.y*F.x; RB.m_Mr.z -= d.x*F.y-d.y*F.x;\n\t\t\t\t\t}\n\t\t\t\t\t*/\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//! Assemble into this global vector\nvoid FEResidualVector::Assemble(int node_id, int dof, double f)\n{\n\t// get the mesh\n\tFEMechModel& mechModel = dynamic_cast<FEMechModel&>(m_fem);\n\tFEMesh& mesh = m_fem.GetMesh();\n\n\t// get the equation number\n\tFENode& node = mesh.Node(node_id);\n\tint n = node.m_ID[dof];\n\n\t// assemble into global vector\n\tif (n >= 0) {\n#pragma omp atomic\n\t\tm_R[n] += f;\n\t}\n\telse {\n\t\tFESolidSolver2* solver = dynamic_cast<FESolidSolver2*>(m_fem.GetCurrentStep()->GetFESolver());\n\t\tif (solver)\n\t\t{\n\t\t\tFERigidSolver* rigidSolver = solver->GetRigidSolver();\n\t\t\trigidSolver->AssembleResidual(node_id, dof, f, m_R);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEResidualVector.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEGlobalVector.h>\n#include <vector>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMechModel;\n\n//-----------------------------------------------------------------------------\n//! The FEResidualVector implements a global vector that stores the residual.\n\nclass FEBIOMECH_API FEResidualVector : public FEGlobalVector\n{\npublic:\n\t//! constructor\n\tFEResidualVector(FEModel& fem, std::vector<double>& R, std::vector<double>& Fr);\n\n\t//! destructor\n\t~FEResidualVector();\n\n\t//! Assemble the element vector into this global vector\n\tvoid Assemble(std::vector<int>& en, std::vector<int>& elm, std::vector<double>& fe, bool bdom = false) override;\n\n\t//! Assemble into this global vector\n\tvoid Assemble(int node, int dof, double f) override;\n};\n"
  },
  {
    "path": "FEBioMech/FERigidAngularDamper.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidAngularDamper.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidAngularDamper, FERigidConnector);\n\tADD_PARAMETER(m_c, \"c\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidAngularDamper::FERigidAngularDamper(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidAngularDamper::Init()\n{\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n    \n    // reset force\n    m_F = vec3d(0,0,0);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidAngularDamper::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n\tar & m_c;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidAngularDamper::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    double alpha = tp.alphaf;\n    \n    // body A\n    vec3d wa = RBa.m_wt*alpha + RBa.m_wp*(1-alpha);\n    \n    // body b\n    vec3d wb = RBb.m_wt*alpha + RBb.m_wp*(1-alpha);\n    \n    m_M = (wb - wa)*m_c;\n    \n    fa[0] = 0;\n    fa[1] = 0;\n    fa[2] = 0;\n    \n    fa[3] = m_M.x;\n    fa[4] = m_M.y;\n    fa[5] = m_M.z;\n    \n    fb[0] = 0;\n    fb[1] = 0;\n    fb[2] = 0;\n    \n    fb[3] = -m_M.x;\n    fb[4] = -m_M.y;\n    fb[5] = -m_M.z;\n    \n    for (int i=0; i<6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n    for (int i=0; i<6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidAngularDamper::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    double alpha = tp.alphaf;\n    double beta  = tp.beta;\n    double gamma = tp.gamma;\n    \n    // get time increment\n    double dt = tp.timeIncrement;\n    \n    int j;\n    \n    vector<int> LM(12);\n\tFEElementMatrix ke;\n\tke.resize(12, 12);\n    ke.zero();\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    mat3dd I(1);\n    \n    // body A\n    vec3d wa = RBa.m_wt*alpha + RBa.m_wp*(1-alpha);\n\tquatd qai = RBa.GetRotation()*RBa.m_qp.Inverse(); qai.MakeUnit();\n    vec3d cai = qai.GetVector()*(2*tan(qai.GetAngle()/2));\n    mat3d Ta = I + skew(cai)/2 + dyad(cai)/4;\n    \n    // body b\n    vec3d wb = RBb.m_wt*alpha + RBb.m_wp*(1-alpha);\n\tquatd qbi = RBb.GetRotation()*RBb.m_qp.Inverse(); qbi.MakeUnit();\n    vec3d cbi = qbi.GetVector()*(2*tan(qbi.GetAngle()/2));\n    mat3d Tb = I + skew(cbi)/2 + dyad(cbi)/4;\n    \n    m_M = (wb - wa)*m_c;\n    \n    mat3d Ba = Ta.transpose()*(gamma/beta/dt);\n    mat3d Bb = Tb.transpose()*(gamma/beta/dt);\n    \n    mat3d K;\n    K.zero();\n    \n    // (2,2)\n    K = Ba*(alpha*m_c);\n    ke[3][3] = K[0][0]; ke[3][4] = K[0][1]; ke[3][5] = K[0][2];\n    ke[4][3] = K[1][0]; ke[4][4] = K[1][1]; ke[4][5] = K[1][2];\n    ke[5][3] = K[2][0]; ke[5][4] = K[2][1]; ke[5][5] = K[2][2];\n    \n    // (2,4)\n    K = Bb*(-alpha*m_c);\n    ke[3][9] = K[0][0]; ke[3][10] = K[0][1]; ke[3][11] = K[0][2];\n    ke[4][9] = K[1][0]; ke[4][10] = K[1][1]; ke[4][11] = K[1][2];\n    ke[5][9] = K[2][0]; ke[5][10] = K[2][1]; ke[5][11] = K[2][2];\n    \n    \n    // (4,2)\n    K = Ba*(-alpha*m_c);\n    ke[9 ][3] = K[0][0]; ke[ 9][4] = K[0][1]; ke[ 9][5] = K[0][2];\n    ke[10][3] = K[1][0]; ke[10][4] = K[1][1]; ke[10][5] = K[1][2];\n    ke[11][3] = K[2][0]; ke[11][4] = K[2][1]; ke[11][5] = K[2][2];\n    \n    // (4,4)\n    K = Bb*(alpha*m_c);\n    ke[9 ][9] = K[0][0]; ke[ 9][10] = K[0][1]; ke[ 9][11] = K[0][2];\n    ke[10][9] = K[1][0]; ke[10][10] = K[1][1]; ke[10][11] = K[1][2];\n    ke[11][9] = K[2][0]; ke[11][10] = K[2][1]; ke[11][11] = K[2][2];\n    \n    for (j=0; j<6; ++j)\n    {\n        LM[j  ] = RBa.m_LM[j];\n        LM[j+6] = RBb.m_LM[j];\n    }\n    \n\tke.SetIndices(LM);\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidAngularDamper::Augment(int naug, const FETimeInfo& tp)\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidAngularDamper::Update()\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n    \n    // body A\n    vec3d wa = RBa.m_wt*alpha + RBa.m_wp*(1-alpha);\n    \n    // body b\n    vec3d wb = RBb.m_wt*alpha + RBb.m_wp*(1-alpha);\n    \n    m_M = (wb - wa)*m_c;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidAngularDamper::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidAngularDamper::RelativeTranslation(const bool global)\n{\n    return vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidAngularDamper::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    return q;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidAngularDamper.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FERigidConnector.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidAngularDamper class implements an angular damper that connects\n//! two rigid bodies.\n\nclass FERigidAngularDamper : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidAngularDamper(FEModel* pfem);\n    \n    //! destructor\n    ~FERigidAngularDamper() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n    void Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global) override;\n\npublic: // parameters\n    double\tm_c;        //! damping constant\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidBody.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidBody.h\"\n#include <FECore/FEMaterial.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEModel.h>\n#include \"RigidBC.h\"\n#include \"FERigidMaterial.h\"\n#include \"FERigidSolidDomain.h\"\n#include \"FERigidShellDomain.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidBody, FECoreBase)\n\tADD_PARAMETER(m_Fr.x, \"Fx\");\n\tADD_PARAMETER(m_Fr.y, \"Fy\");\n\tADD_PARAMETER(m_Fr.z, \"Fz\");\n\tADD_PARAMETER(m_Mr.x, \"Mx\");\n\tADD_PARAMETER(m_Mr.y, \"My\");\n\tADD_PARAMETER(m_Mr.z, \"Mz\");\n\tADD_PARAMETER(m_euler, \"euler\");\n\tADD_PARAMETER(m_r0, \"initial_position\");\n\tADD_PARAMETER(m_rt, \"position\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidBody::FERigidBody(FEModel* pfem) : FECoreBase(pfem)\n{\n    m_bpofr = false;\n\tfor (int i=0; i<6; ++i)\n\t{\n\t\tm_pDC[i] = 0;\n\t\tm_LM[i] = -1;\n\t\tm_BC[i] = DOF_OPEN;\n\t}\n\tm_prb = 0;\n\n\t// zero total displacements\n\tm_Ut[0] = m_Up[0] = 0;\n\tm_Ut[1] = m_Up[1] = 0;\n\tm_Ut[2] = m_Up[2] = 0;\n\tm_Ut[3] = m_Up[3] = 0;\n\tm_Ut[4] = m_Up[4] = 0;\n\tm_Ut[5] = m_Up[5] = 0;\n\n    // initialize velocity and acceleration of center of mass\n    m_vt = m_at = vec3d(0,0,0);\n    \n\t// initialize orientation\n\tm_qt = quatd(0, vec3d(0,0,1));\n\tm_euler = vec3d(0,0,0);\n    \n    // initialize angular velocity and acceleration\n    m_wt = m_alt = vec3d(0,0,0);\n\n\t// initialize reaction forces\n\tm_Fr = m_Fp = vec3d(0,0,0);\n\tm_Mr = m_Mp = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\nFERigidBody::~FERigidBody()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! Reset rigid body data (called from FEM::Reset)\nvoid FERigidBody::Reset()\n{\n\t// zero total displacements\n\tm_Ut[0] = m_Up[0] = 0;\n\tm_Ut[1] = m_Up[1] = 0;\n\tm_Ut[2] = m_Up[2] = 0;\n\tm_Ut[3] = m_Up[3] = 0;\n\tm_Ut[4] = m_Up[4] = 0;\n\tm_Ut[5] = m_Up[5] = 0;\n\n    // initialize velocity and acceleration of center of mass\n    m_vp = m_vt = vec3d(0,0,0);\n    m_ap = m_at = vec3d(0,0,0);\n    \n\t// initialize orientation\n\tm_qp = m_qt = quatd(0, vec3d(0,0,1));\n\tm_euler = vec3d(0,0,0);\n\n    // initialize angular velocity and acceleration\n    m_wp = m_wt = vec3d(0,0,0);\n    m_alp = m_alt = vec3d(0,0,0);\n    \n    // initialize angular momentum and its time rate of change\n    m_hp = m_ht = vec3d(0,0,0);\n    m_dhp = m_dht = vec3d(0,0,0);\n    \n\t// initialize center of mass\n\tm_rt = m_r0;\n\n\t// reset reaction force and torque\n\tm_Fr = vec3d(0,0,0);\n\tm_Mr = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! This function is called at the start of each time step and is used to update\n//! some variables.\nbool FERigidBody::Init()\n{\n\t// clear reaction forces\n\tm_Fr = m_Mr = vec3d(0,0,0);\n\n\t// store previous state\n\tm_rp = m_rt;\n    m_vp = m_vt;\n    m_ap = m_at;\n\tm_qp = m_qt;\n    m_wp = m_wt;\n    m_alp = m_alt;\n    m_hp = m_ht;\n    m_dhp = m_dht;\n\tm_Up[0] = m_Ut[0];\n\tm_Up[1] = m_Ut[1];\n\tm_Up[2] = m_Ut[2];\n\tm_Up[3] = m_Ut[3];\n\tm_Up[4] = m_Ut[4];\n\tm_Up[5] = m_Ut[5];\n\n\t// zero incremental displacements\n\tm_du[0] = m_dul[0] = 0.0;\n\tm_du[1] = m_dul[1] = 0.0;\n\tm_du[2] = m_dul[2] = 0.0;\n\tm_du[3] = m_dul[3] = 0.0;\n\tm_du[4] = m_dul[4] = 0.0;\n\tm_du[5] = m_dul[5] = 0.0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the rigid body's center of mass directly\nvoid FERigidBody::SetCOM(vec3d rc)\n{\n\tm_r0 = m_rp = m_rt = rc;\n}\n\n//-----------------------------------------------------------------------------\n//! Update the mass of the rigid body\nvoid FERigidBody::UpdateMass()\n{\n\t// total mass of rigid body\n\tm_mass = 0;\n\n\t// loop over all domains\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\tif (pm && (pm->GetRigidBodyID() == m_nID))\n\t\t{\n\t\t\tFERigidSolidDomain* pbd = dynamic_cast<FERigidSolidDomain*>(&dom);\n\t\t\tif (pbd) m_mass += pbd->CalculateMass();\n\n\t\t\tFERigidShellDomain* psd = dynamic_cast<FERigidShellDomain*>(&dom);\n\t\t\tif (psd) m_mass += psd->CalculateMass();\n\t\t}\n\t}\n\n\tif (m_mass == 0)\n\t{\n\t\tfeLogWarning(\"Zero mass for rigid body \\\"%s.\\\"\", GetName().c_str());\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the rigid body's total mass, center of mass, and mass moment\n//! of inertia about the center of mass\n//!\nvoid FERigidBody::UpdateCOM()\n{\n\t// center of mass\n\tvec3d rc(0,0,0);\n\n\t// loop over all domains\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int nd=0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\tif (pm && (pm->GetRigidBodyID() == m_nID))\n\t\t{\n\t\t\tFERigidSolidDomain* pbd = dynamic_cast<FERigidSolidDomain*>(&dom);\n\t\t\tif (pbd) rc += pbd->CalculateCOM();\n\n\t\t\tFERigidShellDomain* psd = dynamic_cast<FERigidShellDomain*>(&dom);\n\t\t\tif (psd) rc += psd->CalculateCOM();\n\t\t}\n\t}\n\n\t// normalize com\n\tif (m_mass != 0) rc /= m_mass;\n\n\t// store com\n\tSetCOM(rc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBody::UpdateMOI()\n{\n\t// initialize some data\n\tmat3d moi(0, 0, 0, 0, 0, 0, 0, 0, 0);    // mass moment of inertia about origin\n\n\t// loop over all domains\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\tif (pm && (pm->GetRigidBodyID() == m_nID))\n\t\t{\n\t\t\tFERigidSolidDomain* pbd = dynamic_cast<FERigidSolidDomain*>(&dom);\n\t\t\tif (pbd) moi += pbd->CalculateMOI();\n\n\t\t\tFERigidShellDomain* psd = dynamic_cast<FERigidShellDomain*>(&dom);\n\t\t\tif (psd) moi += psd->CalculateMOI();\n\t\t}\n\t}\n\n\t// use parallel axis theorem to transfer moi to com\n\t// and store moi\n\tmat3dd I(1);        // identity tensor\n\tvec3d rc = m_r0;\n\tm_moi = moi.sym() - m_mass*((rc*rc)*I - dyad(rc));\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidBody::CayleyIncrementalCompoundRotation()\n{\n    // incremental rotation in spatial frame\n    quatd q = m_qt*m_qp.Inverse();\n    q.MakeUnit();                           // clean-up roundoff errors\n    double theta = 2*tan(q.GetAngle()/2);   // get theta from Cayley transform\n    vec3d e = q.GetVector();\n    \n    return e*theta;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBody::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow())\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tar << m_mass;\n\t\t\tar << m_moi;\n\t\t\tar << m_Fr << m_Mr;\n\t\t\tar << m_rp << m_rt;\n\t\t\tar << m_vp << m_vt;\n\t\t\tar << m_ap << m_at;\n\t\t\tar << m_qp << m_qt << m_euler;\n\t\t\tar << m_wp << m_wt;\n\t\t\tar << m_alp << m_alt;\n\t\t\tfor (int i=0; i<6; ++i)\n\t\t\t{\n\t\t\t\tar << m_Up[i];\n\t\t\t\tar << m_Ut[i];\n\t\t\t\tar << m_du[i];\n\t\t\t\tar << m_dul[i];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar >> m_mass;\n\t\t\tar >> m_moi;\n\t\t\tar >> m_Fr >> m_Mr;\n\t\t\tar >> m_rp >> m_rt;\n\t\t\tar >> m_vp >> m_vt;\n\t\t\tar >> m_ap >> m_at;\n\t\t\tar >> m_qp >> m_qt >> m_euler;\n\t\t\tar >> m_wp >> m_wt;\n\t\t\tar >> m_alp >> m_alt;\n\t\t\tfor (int i=0; i<6; ++i)\n\t\t\t{\n\t\t\t\tar >> m_Up[i];\n\t\t\t\tar >> m_Ut[i];\n\t\t\t\tar >> m_du[i];\n\t\t\t\tar >> m_dul[i];\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tar & m_nID & m_mat & m_mass & m_moi & m_Fr & m_Mr;\n\t\tar & m_Fp & m_Mp;\n\t\tar & m_r0 & m_rp & m_rt & m_vp & m_vt & m_ap & m_at;\n\t\tar & m_qp & m_qt & m_euler & m_wp & m_wt & m_alp & m_alt;\n\t\tar & m_hp & m_ht & m_dhp & m_dht;\n\t\tar & m_bpofr;\n\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tar.write(m_BC , sizeof(int), 6);\n\t\t\tar.write(m_LM , sizeof(int), 6);\n\t\t\tar.write(m_Up , sizeof(double), 6);\n\t\t\tar.write(m_Ut , sizeof(double), 6);\n\t\t\tar.write(m_du , sizeof(double), 6);\n\t\t\tar.write(m_dul, sizeof(double), 6);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar.read(m_BC , sizeof(int), 6);\n\t\t\tar.read(m_LM , sizeof(int   ), 6);\n\t\t\tar.read(m_Up , sizeof(double), 6);\n\t\t\tar.read(m_Ut , sizeof(double), 6);\n\t\t\tar.read(m_du , sizeof(double), 6);\n\t\t\tar.read(m_dul, sizeof(double), 6);\n\t\t}\n\n\t\tfor (int i = 0; i < 6; ++i) ar & m_pDC[i];\n\n\t\t// TODO: should I store parent rigid body (m_prb)\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// return the (instantaneous) helical axis relative to the ground\nvoid FERigidBody::InstantaneousHelicalAxis(vec3d& omega, vec3d& s, double& tdot)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n\n    // incremental rotation in spatial frame\n    omega = (m_wp + m_wt)/2;\n    vec3d rdot = (m_vt + m_vp)/2;\n    double rdm = rdot.norm();\n\tvec3d n(omega); n.unit();\n    tdot = rdot*n;\n    vec3d r = (m_rt + m_rp)/2;     // midpoint value of r\n\tvec3d w(omega);\n    double w2 = w.norm2();\n    s = (w2 > 0) ? (w ^ (rdot - n*tdot + (r ^ w)))/(w*w) : vec3d(0,0,0);\n    if ((w2 == 0) && (rdm > 0)) {\n        tdot = rdm/dt;\n        n = rdot.Normalize();\n        omega = n;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// return the (finite) helical axis relative to the ground\nvoid FERigidBody::FiniteHelicalAxis(vec3d& omega, vec3d& s, double& tdot)\n{\n    // incremental rotation in spatial frame\n    quatd dQ = m_qt*m_qp.Inverse();\n    // clean-up roundoff errors\n    dQ.MakeUnit();\n    vec3d rdot = m_rt - m_rp;\n    double rdm = rdot.norm();\n    vec3d w = dQ.GetRotationVector();\n\tvec3d n(w); n.unit();\n\tomega = w;\n    tdot = rdot*n;\n    vec3d r = (m_rt + m_rp)/2;     // midpoint value of r\n    double w2 = w*w;\n    s = (w2 > 0) ? (w ^ (rdot - n*tdot + (r ^ w)))/(w*w) : vec3d(0,0,0);\n    if ((w2 == 0) && (rdm > 0)) {\n        tdot = rdm;\n        n = rdot.Normalize();\n        omega = n;\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FERigidBody.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/fecore_enum.h>\n#include <FECore/vec3d.h>\n#include <FECore/quatd.h>\n#include <FECore/FECoreBase.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FERigidPrescribedBC;\n\n//-----------------------------------------------------------------------------\n//! rigid body class\n//! \\todo perhaps the rigid body should store a list of domains it uses.\n//!       That way, we can have multiple domains per RB using multiple \n//!       materials.\nclass FEBIOMECH_API FERigidBody : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FEOBJECT_ID)\n\tFECORE_BASE_CLASS(FERigidBody)\n\npublic:\n\t// Constructor\n\tFERigidBody(FEModel* pfem);\n\n\t//! desctructor\n\tvirtual ~FERigidBody();\n\n\t//! Set the center of mass directly\n\tvoid SetCOM(vec3d rc);\n\n\t//! Update the mass of the rigid body\n\tvoid UpdateMass();\n\n\t//! Update total mass and center of mass\n\tvoid UpdateCOM();\n\n\t//! Update the moment of intertia\n\tvoid UpdateMOI();\n\n\t//! reset rigid body data\n\tvoid Reset();\n\n\t//! initialize data\n\tbool Init() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! get the material ID\n\tint GetMaterialID() { return m_mat; }\n    \n    //! incremental compound rotation from Cayley transform\n    vec3d CayleyIncrementalCompoundRotation();\n\npublic:\n\tconst quatd& GetRotation() const { return m_qt; }\n\tquatd GetPreviousRotation() const { return m_qp; }\n\tvoid SetRotation(const quatd& q)\n\t{\n\t\tm_qt = q;\n\t\tm_qt.GetEuler(m_euler.x, m_euler.y, m_euler.z);\n\t}\n\npublic:\n\tint\t\tm_nID;\t\t//!< ID of rigid body\n\tint\t\tm_mat;\t\t//!< material ID (TODO: Since rigid bodies can have multiple materials, I want to remove this)\n\tdouble\tm_mass;\t\t//!< total mass of rigid body\n    mat3ds  m_moi;      //!< mass moment of inertia about center of mass\n\tvec3d\tm_Fr, m_Mr;\t//!< reaction force and torque\n\tvec3d\tm_Fp, m_Mp;\t//!< reaction force and torque at the end of the previous step\n\n\tvec3d\tm_r0;\t//!< initial position of rigid body\n\tvec3d\tm_rp;\t//!< previous position of rigid body\n\tvec3d\tm_rt;\t//!< current position of rigid body\n    \n\tvec3d\tm_vp;\t//!< previous velocity of rigid body\n\tvec3d\tm_vt;\t//!< current velocity of rigid body\n    \n\tvec3d\tm_ap;\t//!< previous acceleration of rigid body\n\tvec3d\tm_at;\t//!< current acceleration of rigid body\n\n\tquatd\tm_qp;\t//!< previous orientation of rigid body\n\nprivate:\n\t// TODO: This is a hack!I only need this so I can access the euler angles directly from\n\t//       the optimization module. Need to figure out a better way.\n\tquatd\tm_qt;\t\t//!< current orientation of rigid body\n\tvec3d\tm_euler;\t//!< Euler angles of rotation \n\npublic:\n    vec3d   m_wp;   //!< previous angular velocity of rigid body\n    vec3d   m_wt;   //!< current angular velocity of rigid body\n    \n    vec3d   m_alp;  //!< previous angular acceleration of rigid body\n    vec3d   m_alt;  //!< current angular acceleration of rigid body\n    \n    vec3d   m_hp;   //!< previous angular momentum\n    vec3d   m_ht;   //!< current angular momentum\n    \n    vec3d   m_dhp;  //!< previous time rate of change of angular momentum\n    vec3d   m_dht;  //!< current time rate of change of angular momentum\n    \n\tint\t\tm_BC[6];\t//!< DOF types\n\tint\t\tm_LM[6];\t//!< dof equation numbers\n\tdouble\tm_Up[6];\t//!< previous displacement/rotation vector\n\tdouble\tm_Ut[6];\t//!< total displacement/rotation vector\n\tdouble\tm_du[6];\t//!< incremental displacement vector\n\tdouble\tm_dul[6];\t//!< displacement in local coordinates system\n\n    bool    m_bpofr;    //!< flag for all or none of rotation dofs prescribed/fixed\n    \npublic:\n\tFERigidPrescribedBC*\tm_pDC[6];\t//!< active displacement constraints\n\tFERigidBody*\tm_prb;\t//!< parent rigid body\n\npublic:\n    // return the helical axis relative to the ground\n    void InstantaneousHelicalAxis(vec3d& omega, vec3d& s, double& tdot);\n    void FiniteHelicalAxis(vec3d& omega, vec3d& s, double& tdot);\n\npublic:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidCable.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidCable.h\"\n#include \"FERigidBody.h\"\n#include \"FEMechModel.h\"\n#include <FECore/FELinearSystem.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidCablePoint, FECoreClass)\n\tADD_PARAMETER(m_rb, \"rigid_body_id\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_pos, \"position\");\nEND_FECORE_CLASS();\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidCable, FERigidLoad)\n\tADD_PARAMETER(m_force   , \"force\");\n\tADD_PARAMETER(m_forceDir, \"force_direction\");\n\tADD_PARAMETER(m_brelative, \"relative\");\n\n\t// add the points property\n\tADD_PROPERTY(m_points, \"rigid_cable_point\");\nEND_FECORE_CLASS();\n\nFERigidCable::FERigidCable(FEModel* fem) : FERigidLoad(fem)\n{\n\tm_force = 0;\n\tm_forceDir = vec3d(0,0,-1);\n\tm_brelative = true;\n}\n\n//! initialization\nbool FERigidCable::Init()\n{\n\tif (FEModelLoad::Init() == false) return false;\n\n\t// make sure the force direction is a unit vector\n\tm_forceDir.unit();\n\n\t// get the rigid system\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\n\t// correct the rigid body indices\n\tfor (int i=0; i<m_points.size(); ++i)\n\t{\n\t\tm_points[i]->m_rb = fem.FindRigidbodyFromMaterialID(m_points[i]->m_rb - 1);\n\t\tif (m_points[i]->m_rb == -1) return false;\n\t}\n\n\treturn true;\n}\n\n// helper function for applying forces to rigid bodies\nvoid FERigidCable::applyRigidForce(FERigidBody& rb, const vec3d& F, const vec3d& p, FEGlobalVector& R)\n{\n\tvec3d rd = (m_brelative ? p : p - rb.m_r0);\n\tvec3d M = rd ^ F;\n\tint n;\n\tn = rb.m_LM[0]; if (n >= 0) R[n] += F.x;\n\tn = rb.m_LM[1]; if (n >= 0) R[n] += F.y;\n\tn = rb.m_LM[2]; if (n >= 0) R[n] += F.z;\n\tn = rb.m_LM[3]; if (n >= 0) R[n] += M.x;\n\tn = rb.m_LM[4]; if (n >= 0) R[n] += M.y;\n\tn = rb.m_LM[5]; if (n >= 0) R[n] += M.z;\n\n\trb.m_Fr += F;\n\trb.m_Mr += M;\n}\n\n//! forces\nvoid FERigidCable::LoadVector(FEGlobalVector& R)\n{\n\tint npoints = (int)m_points.size();\n\tif (npoints == 0) return;\n\n\t// get the rigid system\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\n\t// get the last rigid body\n\tint rb0 = m_points[npoints - 1]->m_rb;\n\tFERigidBody& bodyZ = *fem.GetRigidBody(rb0);\n\tvec3d p0 = m_points[npoints - 1]->m_pos;\n\n\t// apply the force to the last attachment point\n\tvec3d F = m_forceDir*m_force;\n\tapplyRigidForce(bodyZ, F, p0, R);\n\n\t// apply paired forces to all the other pairs\n\tfor (int i=0; i<npoints-1; ++i)\n\t{\n\t\tint rbA = m_points[i  ]->m_rb;\n\t\tint rbB = m_points[i+1]->m_rb;\n\n\t\tFERigidBody& bodyA = *fem.GetRigidBody(rbA);\n\t\tFERigidBody& bodyB = *fem.GetRigidBody(rbB);\n\n\t\tvec3d r0A = m_points[i    ]->m_pos;\n\t\tvec3d r0B = m_points[i + 1]->m_pos;\n\n\t\t// get the attachment position in global coordinates for body A\n\t\tvec3d da0 = (m_brelative ? r0A : r0A - bodyA.m_r0);\n\t\tvec3d da = bodyA.GetRotation()*da0;\n\t\tvec3d a = bodyA.m_rt + da;\n\n\t\t// get the attachment position in global coordinates for body B\n\t\tvec3d db0 = (m_brelative ? r0B : r0B - bodyB.m_r0);\n\t\tvec3d db = bodyB.GetRotation()*db0;\n\t\tvec3d b = bodyB.m_rt + db;\n\n\t\t// get the unit axial vector\n\t\tvec3d N = b - a; N.unit();\n\n\t\t// calculate the force value\n\t\tdouble f = m_force;\n\n\t\t// get the axial force\n\t\tvec3d F = N*f;\n\n\t\t// apply the forces\n\t\tapplyRigidForce(bodyA,  F, r0A, R);\n\t\tapplyRigidForce(bodyB, -F, r0B, R);\n\t}\n}\n\n//! Stiffness matrix\nvoid FERigidCable::StiffnessMatrix(FELinearSystem& LS)\n{\n\tint npoints = (int)m_points.size();\n\tif (npoints < 2) return;\n\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tfor (int i=0; i<npoints-1; ++i)\n\t{\n\t\tint idA = m_points[i  ]->m_rb;\n\t\tint idB = m_points[i+1]->m_rb;\n\n\t\t// Get the rigid bodies\n\t\tFERigidBody& bodyA = *fem.GetRigidBody(idA);\n\t\tFERigidBody& bodyB = *fem.GetRigidBody(idB);\n\n\t\t// get the force positions\n\t\tvec3d ra0 = m_points[i]->m_pos;\n\t\tvec3d rb0 = m_points[i+1]->m_pos;\n\n\t\t// get the attachment position in global coordinates for body A\n\t\tvec3d da0 = (m_brelative ? ra0 : ra0 - bodyA.m_r0);\n\t\tvec3d da = bodyA.GetRotation()*da0;\n\t\tvec3d pa = da + bodyA.m_rt;\n\n\t\t// get the attachment position in global coordinates for body B\n\t\tvec3d db0 = (m_brelative ? rb0 : rb0 - bodyB.m_r0);\n\t\tvec3d db = bodyB.GetRotation()*db0;\n\t\tvec3d pb = db + bodyB.m_rt;\n\n\t\t// setup the axial unit vector\n\t\tvec3d N = pb - pa;\n\t\tdouble L = N.unit();\n\n\t\t// build the stiffness matrix components\n\t\tmat3ds NxN = dyad(N);\n\t\tmat3ds M = mat3dd(1.0) - NxN;\n\t\tmat3ds S = M*(m_force / L);\n\t\tmat3da A(-da);\n\t\tmat3da B(-db);\n\t\tmat3da F(m_forceDir*m_force);\n\n\t\tmat3d SA = S*A;\n\t\tmat3d SB = S*B;\n\t\tmat3d AS = A*S;\n\t\tmat3d BS = B*S;\n\t\tmat3d FA = F*A;\n\t\tmat3d FB = F*B;\n\n\t\tmat3d ASA = A*SA;\n\t\tmat3d BSB = B*SB;\n\t\tmat3d ASB = A*SB;\n\n\t\t// put it all together\n\t\tFEElementMatrix K;\n\t\tK.resize(12, 12); K.zero();\n\t\tK.sub(0, 0, S);\n\t\tK.sub(0, 3, SA);\n\t\tK.add(0, 6, S);\n\t\tK.add(0, 9, SB);\n\t\tK.add(3, 3, ASA - FA);\n\t\tK.sub(3, 6, AS);\n\t\tK.sub(3, 9, ASB);\n\t\tK.sub(6, 6, S);\n\t\tK.sub(6, 9, SB);\n\t\tK.add(9, 9, BSB + FB);\n\t\n\t\t// since this is a symmetric matrix, fill the bottom triangular part\n\t\tK.copy_ut();\n\n\t\t// TODO: Not sure why, but it looks like I need a negative sign\n\t\tK *= -1.0;\n\n\t\t// get the equation numbers\n\t\tvector<int> lm(12);\n\t\tlm[0] = bodyA.m_LM[0];\n\t\tlm[1] = bodyA.m_LM[1];\n\t\tlm[2] = bodyA.m_LM[2];\n\t\tlm[3] = bodyA.m_LM[3];\n\t\tlm[4] = bodyA.m_LM[4];\n\t\tlm[5] = bodyA.m_LM[5];\n\n\t\tlm[6] = bodyB.m_LM[0];\n\t\tlm[7] = bodyB.m_LM[1];\n\t\tlm[8] = bodyB.m_LM[2];\n\t\tlm[9] = bodyB.m_LM[3];\n\t\tlm[10] = bodyB.m_LM[4];\n\t\tlm[11] = bodyB.m_LM[5];\n\n\t\t// assemble into global matrix\n\t\tK.SetIndices(lm);\n\t\tLS.Assemble(K);\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidCable.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FERigidForce.h\"\n#include <FECore/FECoreClass.h>\n\nclass FERigidBody;\n\nclass FEBIOMECH_API FERigidCablePoint : public FECoreClass\n{\npublic:\n\tFERigidCablePoint(FEModel* fem) : FECoreClass(fem) {}\n\npublic:\n\tint\t\tm_rb;\t//!< rigid body ID\n\tvec3d\tm_pos;\t//!< position of attachment point\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FERigidCablePoint);\n};\n\nclass FERigidCable : public FERigidLoad\n{\npublic:\n\tFERigidCable(FEModel* fem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! forces\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! Stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprivate:\n\tvoid applyRigidForce(FERigidBody& rb, const vec3d& F, const vec3d& d, FEGlobalVector& R);\n\nprivate:\n\tdouble\tm_force;\t\t//!< magnitude of force (i.e. tension in cable)\n\tvec3d\tm_forceDir;\t\t//!< direction of force at cable's end\n\tbool\tm_brelative;\t//!< positions are defined relative w.r.t. rigid body's COM or not\n\tstd::vector<FERigidCablePoint*>\tm_points;\n\nprivate:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidConnector.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidConnector.h\"\n#include \"FERigidBody.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FEMaterial.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidConnector, FENLConstraint);\n\tADD_PARAMETER(m_nRBa, \"body_a\")->setEnums(\"$(rigid_materials)\");\n\tADD_PARAMETER(m_nRBb, \"body_b\")->setEnums(\"$(rigid_materials)\");\nEND_FECORE_CLASS();\n\nint FERigidConnector::m_ncount = 0;\n\n//-----------------------------------------------------------------------------\nFERigidConnector::FERigidConnector(FEModel* pfem) : FENLConstraint(pfem) \n{\n    m_F = m_M = vec3d(0, 0, 0);\n\tm_rbA = m_rbB = 0;\n};\n\n//-----------------------------------------------------------------------------\nFERigidConnector::~FERigidConnector() {}\n\n//-----------------------------------------------------------------------------\nbool FERigidConnector::Init()\n{\n\t// do base class first\n\tif (FENLConstraint::Init() == false) return false;\n\n\t// When the rigid spring is read in, the ID's correspond to the rigid materials.\n\t// Now we want to make the ID's refer to the rigid body ID's\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_nRBa - 1));\n\tif (pm == nullptr)\n\t{\n\t\tfeLogError(\"Rigid connector %d (spring) does not connect two rigid bodies\\n\", m_nID + 1);\n\t\treturn false;\n\t}\n\tm_nRBa = pm->GetRigidBodyID();\n\n\tpm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_nRBb - 1));\n\tif (pm == nullptr)\n\t{\n\t\tfeLogError(\"Rigid connector %d (spring) does not connect two rigid bodies\\n\", m_nID);\n\t\treturn false;\n\t}\n\tm_nRBb = pm->GetRigidBodyID();\n\n\t// get the actual rigid bodies\n\tm_rbA = fem.GetRigidBody(m_nRBa);\n\tm_rbB = fem.GetRigidBody(m_nRBb);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidConnector::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n    vector<int> lm(12);\n                    \n    int* lm1 = m_rbA->m_LM;\n    int* lm2 = m_rbB->m_LM;\n                    \n    for (int j=0; j<6; ++j) lm[j  ] = lm1[j];\n    for (int j=0; j<6; ++j) lm[j+6] = lm2[j];\n    M.build_add(lm);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidConnector::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n    ar & m_nID;\n    ar & m_nRBa & m_nRBb;\n    ar & m_F & m_M;\n\n\tif (ar.IsLoading())\n\t{\n\t\t// get the actual rigid bodies\n\t\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\t\tm_rbA = fem.GetRigidBody(m_nRBa);\n\t\tm_rbB = fem.GetRigidBody(m_nRBb);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// return the instantaneous helical axis of Body b relative to Body a, in ground CS\nvoid FERigidConnector::InstantaneousHelicalAxis(vec3d& omega, vec3d& s, double& tdot)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    // incremental rotation in spatial frame\n    vec3d oma = (m_rbA->m_wp + m_rbA->m_wt)/2;\n    vec3d omb = (m_rbB->m_wp + m_rbB->m_wt)/2;\n    vec3d va = (m_rbA->m_vp + m_rbA->m_vt)/2;\n    vec3d vb = (m_rbB->m_vp + m_rbB->m_vt)/2;\n    // get the relative motion\n    omega = (omb - oma);\n    vec3d rdot = vb - va;\n    double rdm = rdot.norm();\n\tvec3d n(omega); n.unit();\n    vec3d w(omega);\n    tdot = rdot*n;\n    vec3d ra = (m_rbA->m_rt + m_rbA->m_rp)/2;\n    vec3d rb = (m_rbB->m_rt + m_rbB->m_rp)/2;\n    vec3d r = rb - ra;     // midpoint value of r\n    double w2 = w*w;\n    s = (w2 > 0) ? (w ^ (rdot - n*tdot + (r ^ w)))/(w*w) : vec3d(0,0,0);\n    if ((w2 == 0) && (rdm > 0)) {\n        tdot = rdm;\n        n = rdot.Normalize();\n        omega = n;\n    }\n    // now transform the helical axis to be represented relative to the ground\n    s -= m_rbB->m_rt;\n    omega = m_rbB->GetRotation().Inverse()*omega;\n}\n\n//-----------------------------------------------------------------------------\n// return the helical axis of Body b relative to Body a, in ground CS\nvoid FERigidConnector::FiniteHelicalAxis(vec3d& omega, vec3d& s, double& tdot)\n{\n    // incremental rotation in spatial frame\n    quatd oma = m_rbA->GetRotation()*m_rbA->GetPreviousRotation().Inverse();\n    quatd omb = m_rbB->GetRotation()*m_rbB->GetPreviousRotation().Inverse();\n    // clean-up roundoff errors\n    oma.MakeUnit();\n    omb.MakeUnit();\n    vec3d rda = (m_rbA->m_rt - m_rbA->m_rp);\n    vec3d rdb = (m_rbB->m_rt - m_rbB->m_rp);\n    // get the relative motion\n    quatd dQ = omb - oma; // TODO: Is this valid for rotations?\n\tomega = dQ.GetRotationVector();\n    vec3d rdot = rdb - rda;\n    double rdm = rdot.norm();\n\tvec3d n(omega); n.unit();\n    vec3d w(omega);\n    tdot = rdot*n;\n    vec3d ra = (m_rbA->m_rt + m_rbA->m_rp)/2;\n    vec3d rb = (m_rbB->m_rt + m_rbB->m_rp)/2;\n    vec3d r = rb - ra;     // midpoint value of r\n    double w2 = w*w;\n    s = (w2 > 0) ? (w ^ (rdot - n*tdot + (r ^ w)))/(w*w) : vec3d(0,0,0);\n    if ((w2 == 0) && (rdm > 0)) {\n        tdot = rdm;\n        n = rdot.Normalize();\n        omega = n;\n    }\n    // now transform the helical axis to be represented relative to the ground\n    s -= m_rbB->m_rt;\n    omega = m_rbB->GetRotation().Inverse()*omega;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidConnector.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FENLConstraint.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FERigidBody;\n\n//-----------------------------------------------------------------------------\n//! This is a virtual class for all rigid connectors, including\n//! spherical, revolute, prismatic and cylindrical joints, as well\n//! as springs and dampers that connect rigid bodies.\n\nclass FEBIOMECH_API FERigidConnector : public FENLConstraint\n{\n    FECORE_BASE_CLASS(FERigidConnector)\n\npublic:\n    //! constructor\n    FERigidConnector(FEModel* pfem);\n    \n    //! destructor\n    virtual ~FERigidConnector();\n\n\t//! initialization\n\tbool Init() override;\n    \n    int GetConnectorID() { return m_nID; }\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n    \n    //! evaluate relative translation\n    virtual vec3d RelativeTranslation(const bool global = false) { return vec3d(0,0,0); }\n    \n    //! evaluate relative rotation\n    virtual vec3d RelativeRotation(const bool global = false) { return vec3d(0,0,0); }\n    \npublic:\n    int\tm_nRBa;\t\t//!< rigid body A that the connector connects\n    int\tm_nRBb;\t\t//!< rigid body B that the connector connects\n    \n    vec3d\tm_F;\t//! constraining force\n    vec3d\tm_M;\t//! constraining moment\n    \nprotected:\n    int\t\tm_nID;\t\t//!< ID of rigid connector\n\tbool\tm_binit;\t//!< initialization flag\n\n\tFERigidBody*\tm_rbA;\n\tFERigidBody*\tm_rbB;\n    \n    static int\tm_ncount;\t//!< used to create unique ID's for the nonlinear constraints\n\npublic:\n    // return the helical axis of Body b relative to Body a\n    void InstantaneousHelicalAxis(vec3d& omega, vec3d& s, double& tdot);\n    void FiniteHelicalAxis(vec3d& omega, vec3d& s, double& tdot);\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidContractileForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidContractileForce.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidContractileForce, FERigidConnector);\n\tADD_PARAMETER(m_f0  , \"f0\"         );\n\tADD_PARAMETER(m_a0  , \"insertion_a\");\n\tADD_PARAMETER(m_b0  , \"insertion_b\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidContractileForce::FERigidContractileForce(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n    m_f0 = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidContractileForce::Init()\n{\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n    \n    // reset force\n    m_F = vec3d(0,0,0);\n    \n    // set force insertions relative to rigid body center of mass\n    m_qa0 = m_a0 - m_rbA->m_r0;\n    m_qb0 = m_b0 - m_rbB->m_r0;\n\n\tm_at = m_a0;\n\tm_bt = m_b0;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidContractileForce::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n\tar & m_qa0 & m_qb0;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidContractileForce::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n    \n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n    \n    vec3d n = rb + zb - ra - za;\n    n.unit();\n    m_F = n*m_f0;\n    \n    fa[0] = m_F.x;\n    fa[1] = m_F.y;\n    fa[2] = m_F.z;\n    \n    fa[3] = za.y*m_F.z-za.z*m_F.y;\n    fa[4] = za.z*m_F.x-za.x*m_F.z;\n    fa[5] = za.x*m_F.y-za.y*m_F.x;\n    \n    fb[0] = -m_F.x;\n    fb[1] = -m_F.y;\n    fb[2] = -m_F.z;\n    \n    fb[3] = -zb.y*m_F.z+zb.z*m_F.y;\n    fb[4] = -zb.z*m_F.x+zb.x*m_F.z;\n    fb[5] = -zb.x*m_F.y+zb.y*m_F.x;\n    \n    for (int i=0; i<6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n    for (int i=0; i<6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidContractileForce::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n \tdouble alpha = tp.alphaf;\n     \n    vector<int> LM(12);\n\tFEElementMatrix ke; ke.resize(12, 12);\n    ke.zero();\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n\tvec3d zap = m_qa0; RBa.GetRotation().RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n    \n    vec3d c = rb + zb - ra - za;\n\tvec3d n(c);\n    double L = n.unit();\n    m_F = n*m_f0;\n    mat3ds P;\n    mat3dd I(1);\n    P = I - dyad(n);\n    double k = (L > 0) ? m_f0/L : 0;\n\n\tmat3da chat(c);\n    \n    mat3d K;\n    \n    // (1,1)\n    K = P*(alpha*k);\n    ke[0][0] = K[0][0]; ke[0][1] = K[0][1]; ke[0][2] = K[0][2];\n    ke[1][0] = K[1][0]; ke[1][1] = K[1][1]; ke[1][2] = K[1][2];\n    ke[2][0] = K[2][0]; ke[2][1] = K[2][1]; ke[2][2] = K[2][2];\n    \n    // (1,2)\n    K = (P*zathat)*(-alpha*k);\n    ke[0][3] = K[0][0]; ke[0][4] = K[0][1]; ke[0][5] = K[0][2];\n    ke[1][3] = K[1][0]; ke[1][4] = K[1][1]; ke[1][5] = K[1][2];\n    ke[2][3] = K[2][0]; ke[2][4] = K[2][1]; ke[2][5] = K[2][2];\n    \n    // (1,3)\n    K = P*(-alpha*k);\n    ke[0][6] = K[0][0]; ke[0][7] = K[0][1]; ke[0][8] = K[0][2];\n    ke[1][6] = K[1][0]; ke[1][7] = K[1][1]; ke[1][8] = K[1][2];\n    ke[2][6] = K[2][0]; ke[2][7] = K[2][1]; ke[2][8] = K[2][2];\n    \n    // (1,4)\n    K = P*zbthat*(alpha*k);\n    ke[0][9] = K[0][0]; ke[0][10] = K[0][1]; ke[0][11] = K[0][2];\n    ke[1][9] = K[1][0]; ke[1][10] = K[1][1]; ke[1][11] = K[1][2];\n    ke[2][9] = K[2][0]; ke[2][10] = K[2][1]; ke[2][11] = K[2][2];\n    \n    // (2,1)\n    K = zahat*P*(alpha*k);\n    ke[3][0] = K[0][0]; ke[3][1] = K[0][1]; ke[3][2] = K[0][2];\n    ke[4][0] = K[1][0]; ke[4][1] = K[1][1]; ke[4][2] = K[1][2];\n    ke[5][0] = K[2][0]; ke[5][1] = K[2][1]; ke[5][2] = K[2][2];\n    \n    // (2,2)\n    K = (zahat*P*zathat + chat*zathat)*(-alpha*k);\n    ke[3][3] = K[0][0]; ke[3][4] = K[0][1]; ke[3][5] = K[0][2];\n    ke[4][3] = K[1][0]; ke[4][4] = K[1][1]; ke[4][5] = K[1][2];\n    ke[5][3] = K[2][0]; ke[5][4] = K[2][1]; ke[5][5] = K[2][2];\n    \n    // (2,3)\n    K = zahat*P*(-alpha*k);\n    ke[3][6] = K[0][0]; ke[3][7] = K[0][1]; ke[3][8] = K[0][2];\n    ke[4][6] = K[1][0]; ke[4][7] = K[1][1]; ke[4][8] = K[1][2];\n    ke[5][6] = K[2][0]; ke[5][7] = K[2][1]; ke[5][8] = K[2][2];\n    \n    // (2,4)\n    K = (zahat*P*zbthat)*(alpha*k);\n    ke[3][9] = K[0][0]; ke[3][10] = K[0][1]; ke[3][11] = K[0][2];\n    ke[4][9] = K[1][0]; ke[4][10] = K[1][1]; ke[4][11] = K[1][2];\n    ke[5][9] = K[2][0]; ke[5][10] = K[2][1]; ke[5][11] = K[2][2];\n    \n    \n    // (3,1)\n    K = P*(-alpha*k);\n    ke[6][0] = K[0][0]; ke[6][1] = K[0][1]; ke[6][2] = K[0][2];\n    ke[7][0] = K[1][0]; ke[7][1] = K[1][1]; ke[7][2] = K[1][2];\n    ke[8][0] = K[2][0]; ke[8][1] = K[2][1]; ke[8][2] = K[2][2];\n    \n    // (3,2)\n    K = (P*zathat)*(alpha*k);\n    ke[6][3] = K[0][0]; ke[6][4] = K[0][1]; ke[6][5] = K[0][2];\n    ke[7][3] = K[1][0]; ke[7][4] = K[1][1]; ke[7][5] = K[1][2];\n    ke[8][3] = K[2][0]; ke[8][4] = K[2][1]; ke[8][5] = K[2][2];\n    \n    // (3,3)\n    K = P*(alpha*k);\n    ke[6][6] = K[0][0]; ke[6][7] = K[0][1]; ke[6][8] = K[0][2];\n    ke[7][6] = K[1][0]; ke[7][7] = K[1][1]; ke[7][8] = K[1][2];\n    ke[8][6] = K[2][0]; ke[8][7] = K[2][1]; ke[8][8] = K[2][2];\n    \n    // (3,4)\n    K = P*zbthat*(-alpha*k);\n    ke[6][9] = K[0][0]; ke[6][10] = K[0][1]; ke[6][11] = K[0][2];\n    ke[7][9] = K[1][0]; ke[7][10] = K[1][1]; ke[7][11] = K[1][2];\n    ke[8][9] = K[2][0]; ke[8][10] = K[2][1]; ke[8][11] = K[2][2];\n    \n    \n    // (4,1)\n    K = zbhat*P*(-alpha*k);\n    ke[9 ][0] = K[0][0]; ke[ 9][1] = K[0][1]; ke[ 9][2] = K[0][2];\n    ke[10][0] = K[1][0]; ke[10][1] = K[1][1]; ke[10][2] = K[1][2];\n    ke[11][0] = K[2][0]; ke[11][1] = K[2][1]; ke[11][2] = K[2][2];\n    \n    // (4,2)\n    K = (zbhat*P*zathat)*(alpha*k);\n    ke[9 ][3] = K[0][0]; ke[ 9][4] = K[0][1]; ke[ 9][5] = K[0][2];\n    ke[10][3] = K[1][0]; ke[10][4] = K[1][1]; ke[10][5] = K[1][2];\n    ke[11][3] = K[2][0]; ke[11][4] = K[2][1]; ke[11][5] = K[2][2];\n    \n    // (4,3)\n    K = zbhat*P*(alpha*k);\n    ke[9 ][6] = K[0][0]; ke[ 9][7] = K[0][1]; ke[ 9][8] = K[0][2];\n    ke[10][6] = K[1][0]; ke[10][7] = K[1][1]; ke[10][8] = K[1][2];\n    ke[11][6] = K[2][0]; ke[11][7] = K[2][1]; ke[11][8] = K[2][2];\n    \n    // (4,4)\n    K = (zbhat*P*zbthat - chat*zbthat)*(-alpha*k);\n    ke[9 ][9] = K[0][0]; ke[ 9][10] = K[0][1]; ke[ 9][11] = K[0][2];\n    ke[10][9] = K[1][0]; ke[10][10] = K[1][1]; ke[10][11] = K[1][2];\n    ke[11][9] = K[2][0]; ke[11][10] = K[2][1]; ke[11][11] = K[2][2];\n    \n    for (int j=0; j<6; ++j)\n    {\n        LM[j  ] = RBa.m_LM[j];\n        LM[j+6] = RBb.m_LM[j];\n    }\n    \n\tke.SetIndices(LM);\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidContractileForce::Augment(int naug, const FETimeInfo& tp)\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidContractileForce::Update()\n{\n    vec3d ra, rb, c;\n    vec3d za, zb;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n    \n\tm_at = RBa.m_rt + zat;\n\tm_bt = RBb.m_rt + zbt;\n\n    vec3d n = rb + zb - ra - za;\n    n.unit();\n    m_F = n*m_f0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidContractileForce::Reset()\n{\n    m_F = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_a0 - RBa.m_r0;\n    m_qb0 = m_b0 - RBb.m_r0;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidContractileForce::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n\n    return x;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidContractileForce::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    return q;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidContractileForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidContractileForce class implements a contractile force between\n//! arbitrary points (not necessarily nodes) on two rigid bodies.\n\nclass FERigidContractileForce : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidContractileForce(FEModel* pfem);\n    \n    //! destructor\n    ~FERigidContractileForce() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global) override;\n\npublic: // parameters\n    double\tm_f0;       //! contractile force\n    vec3d\tm_a0;       //! initial absolute position vector of insertion on body A\n    vec3d\tm_b0;       //! initial absolute position vector of insertion on body B\n\n\t// output parameters\n\tvec3d\tm_at;\t\t//!< current absolute position of spring on body A\n\tvec3d\tm_bt;\t\t//!< current absolute position of spring on body B\n\nprotected:\n    vec3d\tm_qa0;      //! initial relative position vector of insertion on body A\n    vec3d\tm_qb0;      //! initial relative position vector of insertion on body B\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidCylindricalJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidCylindricalJoint.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidCylindricalJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon, \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol, \"tolerance\"     );\n\tADD_PARAMETER(m_gtol, \"gaptol\"        );\n\tADD_PARAMETER(m_qtol, \"angtol\"        );\n\tADD_PARAMETER(m_eps , \"force_penalty\" );\n\tADD_PARAMETER(m_ups , \"moment_penalty\");\n\tADD_PARAMETER(m_q0  , \"joint_origin\"  );\n\tADD_PARAMETER(m_e0[0], \"joint_axis\"    );\n\tADD_PARAMETER(m_e0[1], \"transverse_axis\");\n\tADD_PARAMETER(m_naugmin, \"minaug\"        );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"        );\n\tADD_PARAMETER(m_bd  , \"prescribed_translation\");\n\tADD_PARAMETER(m_dp  , \"translation\"   );\n\tADD_PARAMETER(m_Fp  , \"force\"         );\n\tADD_PARAMETER(m_bq  , \"prescribed_rotation\");\n\tADD_PARAMETER(m_qp  , \"rotation\"      );\n\tADD_PARAMETER(m_Mp  , \"moment\"        );\n\tADD_PARAMETER(m_bautopen, \"auto_penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//FERigidCylindricalJoint::FERigidCylindricalJoint(FEModel* pfem) : FENLConstraint(pfem)\nFERigidCylindricalJoint::FERigidCylindricalJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n    m_atol = 0;\n    m_gtol = 0;\n    m_qtol = 0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n    m_dp = 0;\n    m_Fp = 0;\n    m_bd = false;\n    m_qp = 0;\n    m_Mp = 0;\n    m_bq = false;\n\tm_bautopen = false;\n\tm_laugon = FECore::AUGLAG_METHOD;\t// default to augmented Lagrangian\n\tm_eps = m_ups = 1.0;\n\n\tm_F = vec3d(0, 0, 0);\n\tm_Lp = vec3d(0, 0, 0);\n\tm_u = m_up = 0;\n}\n\n//-----------------------------------------------------------------------------\nFERigidCylindricalJoint::~FERigidCylindricalJoint()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! initial position \nvec3d FERigidCylindricalJoint::InitialPosition() const\n{\n\treturn m_q0;\n}\n\n//-----------------------------------------------------------------------------\n//! current position\nvec3d FERigidCylindricalJoint::Position() const\n{\n\tFERigidBody& RBa = *m_rbA;\n\tvec3d qa = m_qa0;\n\tRBa.GetRotation().RotateVector(qa);\n\treturn RBa.m_rt + qa;\n}\n\n//-----------------------------------------------------------------------------\n//! orientation\nquatd FERigidCylindricalJoint::Orientation() const\n{\n\tquatd Q0(vec3d(0, 0, 1), m_e0[0]);\n\tFERigidBody& RBa = *m_rbA;\n\treturn RBa.GetRotation()*Q0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidCylindricalJoint::Init()\n{\n    if (m_bd && (m_Fp != 0)) {\n\t\tfeLogError(\"Translation and force cannot be prescribed simultaneously in rigid connector %d (cylindrical joint)\\n\", m_nID+1);\n        return false;\n    }\n    \n    if (m_bq && (m_Mp != 0)) {\n\t\tfeLogError(\"Rotation and moment cannot be prescribed simultaneously in rigid connector %d (cylindrical joint)\\n\", m_nID+1);\n        return false;\n    }\n    \n    // initialize joint basis\n    m_e0[0].unit();\n    m_e0[2] = m_e0[0] ^ m_e0[1]; m_e0[2].unit();\n    m_e0[1] = m_e0[2] ^ m_e0[0]; m_e0[1].unit();\n    \n    // reset force\n    m_F = vec3d(0,0,0); m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0); m_U = vec3d(0,0,0);\n\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n    \n    m_qa0 = m_q0 - m_rbA->m_r0;\n    m_qb0 = m_q0 - m_rbB->m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nint FERigidCylindricalJoint::InitEquations(int neq)\n{\n\tm_EQ.resize(4, -1);\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\t// we allocate three equations\n\t\tm_EQ[0] = neq;\n\t\tm_EQ[1] = neq + 1;\n\t\tm_EQ[2] = neq + 2;\n\t\tm_EQ[3] = neq + 3;\n\t\treturn 4;\n\t}\n\telse return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Build the matrix profile\nvoid FERigidCylindricalJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\nvoid FERigidCylindricalJoint::PrepStep()\n{\n\tm_Lp = m_F;\n\tm_up = m_u;\n}\n\nvoid FERigidCylindricalJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Lp.x + Ui[m_EQ[0]] + ui[m_EQ[0]];\n\t\tm_F.y = m_Lp.y + Ui[m_EQ[1]] + ui[m_EQ[1]];\n\t\tm_F.z = m_Lp.z + Ui[m_EQ[2]] + ui[m_EQ[2]];\n\t\tm_u   = m_up   + Ui[m_EQ[3]] + ui[m_EQ[3]];\n\t}\n}\n\nvoid FERigidCylindricalJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tUi[m_EQ[0]] += ui[m_EQ[0]];\n\t\tUi[m_EQ[1]] += ui[m_EQ[1]];\n\t\tUi[m_EQ[2]] += ui[m_EQ[2]];\n\t\tUi[m_EQ[3]] += ui[m_EQ[3]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidCylindricalJoint::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.reserve(16);\n\tlm.push_back(m_rbA->m_LM[0]);\n\tlm.push_back(m_rbA->m_LM[1]);\n\tlm.push_back(m_rbA->m_LM[2]);\n\tlm.push_back(m_rbA->m_LM[3]);\n\tlm.push_back(m_rbA->m_LM[4]);\n\tlm.push_back(m_rbA->m_LM[5]);\n\n\t// add the dofs of rigid body B\n\tlm.push_back(m_rbB->m_LM[0]);\n\tlm.push_back(m_rbB->m_LM[1]);\n\tlm.push_back(m_rbB->m_LM[2]);\n\tlm.push_back(m_rbB->m_LM[3]);\n\tlm.push_back(m_rbB->m_LM[4]);\n\tlm.push_back(m_rbB->m_LM[5]);\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tlm.push_back(m_EQ[0]);\n\t\tlm.push_back(m_EQ[1]);\n\t\tlm.push_back(m_EQ[2]);\n\t\tlm.push_back(m_EQ[3]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidCylindricalJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n    ar & m_qa0 & m_qb0;\n    ar & m_L & m_U;\n    ar & m_e0;\n    ar & m_ea0;\n    ar & m_eb0;\n\tar & m_F & m_u;\n\tar & m_Lp & m_up;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidCylindricalJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\t// --- Penalty / Augmented Lagrangian ---\n\t\tvector<double> fa(6);\n\t\tvector<double> fb(6);\n\n\t\tvec3d eat[3], eap[3], ea[3];\n\t\tvec3d ebt[3], ebp[3], eb[3];\n\n\t\tdouble alpha = tp.alphaf;\n\n\t\t// body A\n\t\tvec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1 - alpha);\n\t\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n\t\tvec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n\t\tvec3d za = zat*alpha + zap*(1 - alpha);\n\t\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\t\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\t\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n\t\teap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n\t\teap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n\t\teap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n\t\tea[0] = eat[0] * alpha + eap[0] * (1 - alpha);\n\t\tea[1] = eat[1] * alpha + eap[1] * (1 - alpha);\n\t\tea[2] = eat[2] * alpha + eap[2] * (1 - alpha);\n\n\t\t// body b\n\t\tvec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1 - alpha);\n\t\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n\t\tvec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n\t\tvec3d zb = zbt*alpha + zbp*(1 - alpha);\n\t\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\t\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\t\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n\t\tebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n\t\tebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n\t\tebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n\t\teb[0] = ebt[0] * alpha + ebp[0] * (1 - alpha);\n\t\teb[1] = ebt[1] * alpha + ebp[1] * (1 - alpha);\n\t\teb[2] = ebt[2] * alpha + ebp[2] * (1 - alpha);\n\n\t\tmat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n\t\tvec3d p = m_bd ? ea[0] * m_dp : vec3d(0, 0, 0);\n\t\tvec3d c = P*(rb + zb - ra - za) - p;\n\t\tm_F = m_L + c*m_eps + ea[0] * m_Fp;\n\n\t\tvec3d ksi;\n\t\tif (m_bq) {\n\t\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n\t\t\tquatd a(m_qp, ea[0]);\n\t\t\tquatd r = a*q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tksi = r.GetVector()*r.GetAngle();\n\t\t}\n\t\telse\n\t\t\tksi = (ea[0] ^ eb[0]) / 2;\n\t\tm_M = m_U + ksi*m_ups + ea[0] * m_Mp;\n\n\t\tfa[0] = m_F.x;\n\t\tfa[1] = m_F.y;\n\t\tfa[2] = m_F.z;\n\n\t\tfa[3] = za.y*m_F.z - za.z*m_F.y + m_M.x;\n\t\tfa[4] = za.z*m_F.x - za.x*m_F.z + m_M.y;\n\t\tfa[5] = za.x*m_F.y - za.y*m_F.x + m_M.z;\n\n\t\tfb[0] = -m_F.x;\n\t\tfb[1] = -m_F.y;\n\t\tfb[2] = -m_F.z;\n\n\t\tfb[3] = -zb.y*m_F.z + zb.z*m_F.y - m_M.x;\n\t\tfb[4] = -zb.z*m_F.x + zb.x*m_F.z - m_M.y;\n\t\tfb[5] = -zb.x*m_F.y + zb.y*m_F.x - m_M.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\n\t\tRBa.m_Fr -= vec3d(fa[0], fa[1], fa[2]);\n\t\tRBa.m_Mr -= vec3d(fa[3], fa[4], fa[5]);\n\t\tRBb.m_Fr -= vec3d(fb[0], fb[1], fb[2]);\n\t\tRBb.m_Mr -= vec3d(fb[3], fb[4], fb[5]);\n\t}\n\telse\n\t{\n\t\t// --- Lagrange Multipliers ---\n\t\tvector<double> fe(16);\n\n\t\t// current values of Lagrange multipliers\n\t\tvec3d Lm = m_F;\t// lagrange multiplier\n\t\tdouble mu = m_u;\n\n\t\t// rigid body positions\n\t\tvec3d ra = RBa.m_rt;\n\t\tvec3d rb = RBb.m_rt;\n\n\t\t// rigid body rotations\n\t\tquatd Qa = RBa.GetRotation();\n\t\tquatd Qb = RBb.GetRotation();\n\n\t\t// current joint offset\n\t\tvec3d qa = Qa*m_qa0;\n\t\tvec3d qb = Qb*m_qb0;\n\n\t\t// \"gap\" vector\n\t\tvec3d g = ra + qa - rb - qb;\n\n\t\t// current joint axis\n\t\tvec3d n0 = m_e0[0];\n\t\tvec3d ea = Qa*n0;\n\t\tvec3d eb = Qb*n0;\n\t\tmat3d N;\n\t\tN[0][0] =     0; N[0][1] =  n0.z; N[0][2] = -n0.y;\n\t\tN[1][0] = -n0.z; N[1][1] =     0; N[1][2] =  n0.x;\n\t\tN[2][0] =  n0.y; N[2][1] = -n0.x; N[2][2] =     0;\n\t\tmat3d NT = N.transpose();\n\n\t\t// projection matrix\n\t\tmat3d Pa = mat3dd(1.0) - dyad(ea);\n\n\t\t// setup skew matrices\n\t\tmat3d ya;\n\t\tya[0][0] =     0; ya[0][1] =  qa.z; ya[0][2] = -qa.y;\n\t\tya[1][0] = -qa.z; ya[1][1] =     0; ya[1][2] =  qa.x;\n\t\tya[2][0] =  qa.y; ya[2][1] = -qa.x; ya[2][2] =     0;\n\t\tmat3d yaT = ya.transpose();\n\n\t\tmat3d yb;\n\t\tyb[0][0] =     0; yb[0][1] =  qb.z; yb[0][2] = -qb.y;\n\t\tyb[1][0] = -qb.z; yb[1][1] =     0; yb[1][2] =  qb.x;\n\t\tyb[2][0] =  qb.y; yb[2][1] = -qb.x; yb[2][2] =     0;\n\t\tmat3d ybT = yb.transpose();\n\n\t\tmat3d GaT = mat3dd(ea*g) + (g & ea);\n\n\t\t// net force\n\t\tvec3d F = Pa*Lm;\n\n\t\t// net moment on A\n\t\tvec3d Ma = yaT*Pa*Lm - NT*GaT*Lm + NT*eb*mu;\n\n\t\t// net moment on B\n\t\tvec3d Mb = -ybT*Pa*Lm + NT*(ea*mu);\n\n\t\t// body A\n\t\tfe[ 0] = -F.x;\n\t\tfe[ 1] = -F.y;\n\t\tfe[ 2] = -F.z;\n\t\tfe[ 3] = -Ma.x;\n\t\tfe[ 4] = -Ma.y;\n\t\tfe[ 5] = -Ma.z;\n\n\t\t// body B\n\t\tfe[ 6] = +F.x;\n\t\tfe[ 7] = +F.y;\n\t\tfe[ 8] = +F.z;\n\t\tfe[ 9] = -Mb.x;\n\t\tfe[10] = -Mb.y;\n\t\tfe[11] = -Mb.z;\n\n\t\t// constraints\n\t\tvec3d c = Pa*g;\n\t\tfe[12] = -c.x;\n\t\tfe[13] = -c.y;\n\t\tfe[14] = -c.z;\n\t\tfe[15] = 1.0 - ea*eb;\n\n\t\t// get the equation numbers\n\t\tvector<int> LM;\n\t\tUnpackLM(LM);\n\n\t\t// assemble it all\n\t\tR.Assemble(LM, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidCylindricalJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tdouble alpha = tp.alphaf;\n\n\t\tvec3d eat[3], eap[3], ea[3];\n\t\tvec3d ebt[3], ebp[3], eb[3];\n\n\t\tvector<int> LM(12);\n\t\tFEElementMatrix ke; ke.resize(12, 12);\n\t\tke.zero();\n\n\t\t// body A\n\t\tvec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1 - alpha);\n\t\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n\t\tvec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n\t\tvec3d za = zat*alpha + zap*(1 - alpha);\n\t\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\t\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\t\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n\t\teap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n\t\teap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n\t\teap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n\t\tea[0] = eat[0] * alpha + eap[0] * (1 - alpha);\n\t\tea[1] = eat[1] * alpha + eap[1] * (1 - alpha);\n\t\tea[2] = eat[2] * alpha + eap[2] * (1 - alpha);\n\t\tmat3d zahat; zahat.skew(za);\n\t\tmat3d zathat; zathat.skew(zat);\n\n\t\t// body b\n\t\tvec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1 - alpha);\n\t\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n\t\tvec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n\t\tvec3d zb = zbt*alpha + zbp*(1 - alpha);\n\t\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\t\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\t\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n\t\tebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n\t\tebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n\t\tebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n\t\teb[0] = ebt[0] * alpha + ebp[0] * (1 - alpha);\n\t\teb[1] = ebt[1] * alpha + ebp[1] * (1 - alpha);\n\t\teb[2] = ebt[2] * alpha + ebp[2] * (1 - alpha);\n\t\tmat3d zbhat; zbhat.skew(zb);\n\t\tmat3d zbthat; zbthat.skew(zbt);\n\n\t\tmat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n\t\tvec3d p = m_bd ? ea[0] * m_dp : vec3d(0, 0, 0);\n\t\tvec3d d = rb + zb - ra - za;\n\t\tvec3d c = P*d - p;\n\t\tm_F = m_L + c*m_eps + ea[0] * m_Fp;\n\t\tmat3dd I(1);\n\n\t\tvec3d ksi = (ea[0] ^ eb[0]) / 2;\n\t\tquatd q, a, r;\n\t\tif (m_bq) {\n\t\t\tq = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n\t\t\ta = quatd(m_qp, ea[0]);\n\t\t\tr = a*q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tksi = r.GetVector()*r.GetAngle();\n\t\t}\n\t\tm_M = m_U + ksi*m_ups + ea[0] * m_Mp;\n\n\t\tmat3d eahat[3], ebhat[3], eathat[3], ebthat[3];\n\t\tfor (int j = 0; j < 3; ++j) {\n\t\t\teahat[j] = skew(ea[j]);\n\t\t\tebhat[j] = skew(eb[j]);\n\t\t\teathat[j] = skew(eat[j]);\n\t\t\tebthat[j] = skew(ebt[j]);\n\t\t}\n\t\tmat3d Q = m_bd ? eathat[0] * m_dp : ((ea[0] & d) + mat3dd(1)*(ea[0] * d))*eathat[0];\n\t\tmat3d K, Wba, Wab;\n\t\tWba = (ebhat[0] * eathat[0]) / 2;\n\t\tWab = (eahat[0] * ebthat[0]) / 2;\n\t\tif (m_bq) {\n\t\t\tquatd qa = RBa.GetRotation()*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n\t\t\tquatd qb = RBb.GetRotation()*(alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp).Inverse();\n\t\t\tqa.MakeUnit();\n\t\t\tqb.MakeUnit();\n\t\t\tmat3d Qa = qa.RotationMatrix();\n\t\t\tmat3d Qb = qb.RotationMatrix();\n\t\t\tmat3d A = a.RotationMatrix();\n\t\t\tmat3d R = r.RotationMatrix();\n\t\t\tmat3dd I(1);\n\t\t\tWba = A*(I*Qa.trace() - Qa) / 2;\n\t\t\tWab = R*(I*Qb.trace() - Qb) / 2;\n\t\t}\n\n\t\tmat3da Fhat(m_F);\n\n\t\t// (1,1)\n\t\tK = P*(alpha*m_eps);\n\t\tke[0][0] = K[0][0]; ke[0][1] = K[0][1]; ke[0][2] = K[0][2];\n\t\tke[1][0] = K[1][0]; ke[1][1] = K[1][1]; ke[1][2] = K[1][2];\n\t\tke[2][0] = K[2][0]; ke[2][1] = K[2][1]; ke[2][2] = K[2][2];\n\n\t\t// (1,2)\n\t\tK = (P*zathat + Q)*(-m_eps*alpha)\n\t\t\t+ eathat[0] * (m_Fp*alpha);\n\t\tke[0][3] = K[0][0]; ke[0][4] = K[0][1]; ke[0][5] = K[0][2];\n\t\tke[1][3] = K[1][0]; ke[1][4] = K[1][1]; ke[1][5] = K[1][2];\n\t\tke[2][3] = K[2][0]; ke[2][4] = K[2][1]; ke[2][5] = K[2][2];\n\n\t\t// (1,3)\n\t\tK = P*(-alpha*m_eps);\n\t\tke[0][6] = K[0][0]; ke[0][7] = K[0][1]; ke[0][8] = K[0][2];\n\t\tke[1][6] = K[1][0]; ke[1][7] = K[1][1]; ke[1][8] = K[1][2];\n\t\tke[2][6] = K[2][0]; ke[2][7] = K[2][1]; ke[2][8] = K[2][2];\n\n\t\t// (1,4)\n\t\tK = P*zbthat*(alpha*m_eps);\n\t\tke[0][9] = K[0][0]; ke[0][10] = K[0][1]; ke[0][11] = K[0][2];\n\t\tke[1][9] = K[1][0]; ke[1][10] = K[1][1]; ke[1][11] = K[1][2];\n\t\tke[2][9] = K[2][0]; ke[2][10] = K[2][1]; ke[2][11] = K[2][2];\n\n\t\t// (2,1)\n\t\tK = zahat*P*(alpha*m_eps);\n\t\tke[3][0] = K[0][0]; ke[3][1] = K[0][1]; ke[3][2] = K[0][2];\n\t\tke[4][0] = K[1][0]; ke[4][1] = K[1][1]; ke[4][2] = K[1][2];\n\t\tke[5][0] = K[2][0]; ke[5][1] = K[2][1]; ke[5][2] = K[2][2];\n\n\t\t// (2,2)\n\t\tK = (zahat*(P*zathat + Q)*m_eps + Fhat * zathat + Wba*m_ups)*(-alpha)\n\t\t\t+ eathat[0] * (m_Mp*alpha);\n\t\tke[3][3] = K[0][0]; ke[3][4] = K[0][1]; ke[3][5] = K[0][2];\n\t\tke[4][3] = K[1][0]; ke[4][4] = K[1][1]; ke[4][5] = K[1][2];\n\t\tke[5][3] = K[2][0]; ke[5][4] = K[2][1]; ke[5][5] = K[2][2];\n\n\t\t// (2,3)\n\t\tK = zahat*P*(-alpha*m_eps);\n\t\tke[3][6] = K[0][0]; ke[3][7] = K[0][1]; ke[3][8] = K[0][2];\n\t\tke[4][6] = K[1][0]; ke[4][7] = K[1][1]; ke[4][8] = K[1][2];\n\t\tke[5][6] = K[2][0]; ke[5][7] = K[2][1]; ke[5][8] = K[2][2];\n\n\t\t// (2,4)\n\t\tK = (zahat*P*zbthat*m_eps + Wab*m_ups)*alpha;\n\t\tke[3][9] = K[0][0]; ke[3][10] = K[0][1]; ke[3][11] = K[0][2];\n\t\tke[4][9] = K[1][0]; ke[4][10] = K[1][1]; ke[4][11] = K[1][2];\n\t\tke[5][9] = K[2][0]; ke[5][10] = K[2][1]; ke[5][11] = K[2][2];\n\n\n\t\t// (3,1)\n\t\tK = P*(-alpha*m_eps);\n\t\tke[6][0] = K[0][0]; ke[6][1] = K[0][1]; ke[6][2] = K[0][2];\n\t\tke[7][0] = K[1][0]; ke[7][1] = K[1][1]; ke[7][2] = K[1][2];\n\t\tke[8][0] = K[2][0]; ke[8][1] = K[2][1]; ke[8][2] = K[2][2];\n\n\t\t// (3,2)\n\t\tK = (P*zathat + Q)*(m_eps*alpha)\n\t\t\t- eathat[0] * (m_Fp*alpha);\n\t\tke[6][3] = K[0][0]; ke[6][4] = K[0][1]; ke[6][5] = K[0][2];\n\t\tke[7][3] = K[1][0]; ke[7][4] = K[1][1]; ke[7][5] = K[1][2];\n\t\tke[8][3] = K[2][0]; ke[8][4] = K[2][1]; ke[8][5] = K[2][2];\n\n\t\t// (3,3)\n\t\tK = P*(alpha*m_eps);\n\t\tke[6][6] = K[0][0]; ke[6][7] = K[0][1]; ke[6][8] = K[0][2];\n\t\tke[7][6] = K[1][0]; ke[7][7] = K[1][1]; ke[7][8] = K[1][2];\n\t\tke[8][6] = K[2][0]; ke[8][7] = K[2][1]; ke[8][8] = K[2][2];\n\n\t\t// (3,4)\n\t\tK = P*zbthat*(-alpha*m_eps);\n\t\tke[6][9] = K[0][0]; ke[6][10] = K[0][1]; ke[6][11] = K[0][2];\n\t\tke[7][9] = K[1][0]; ke[7][10] = K[1][1]; ke[7][11] = K[1][2];\n\t\tke[8][9] = K[2][0]; ke[8][10] = K[2][1]; ke[8][11] = K[2][2];\n\n\n\t\t// (4,1)\n\t\tK = zbhat*P*(-alpha*m_eps);\n\t\tke[9][0] = K[0][0]; ke[9][1] = K[0][1]; ke[9][2] = K[0][2];\n\t\tke[10][0] = K[1][0]; ke[10][1] = K[1][1]; ke[10][2] = K[1][2];\n\t\tke[11][0] = K[2][0]; ke[11][1] = K[2][1]; ke[11][2] = K[2][2];\n\n\t\t// (4,2)\n\t\tK = (zbhat*(P*zathat + Q)*m_eps + Wba*m_ups)*alpha\n\t\t\t- eathat[0] * (m_Mp*alpha);\n\t\tke[9][3] = K[0][0]; ke[9][4] = K[0][1]; ke[9][5] = K[0][2];\n\t\tke[10][3] = K[1][0]; ke[10][4] = K[1][1]; ke[10][5] = K[1][2];\n\t\tke[11][3] = K[2][0]; ke[11][4] = K[2][1]; ke[11][5] = K[2][2];\n\n\t\t// (4,3)\n\t\tK = zbhat*P*(alpha*m_eps);\n\t\tke[9][6] = K[0][0]; ke[9][7] = K[0][1]; ke[9][8] = K[0][2];\n\t\tke[10][6] = K[1][0]; ke[10][7] = K[1][1]; ke[10][8] = K[1][2];\n\t\tke[11][6] = K[2][0]; ke[11][7] = K[2][1]; ke[11][8] = K[2][2];\n\n\t\t// (4,4)\n\t\tK = (zbhat*P*zbthat*m_eps - Fhat * zbthat + Wab*m_ups)*(-alpha);\n\t\tke[9][9] = K[0][0]; ke[9][10] = K[0][1]; ke[9][11] = K[0][2];\n\t\tke[10][9] = K[1][0]; ke[10][10] = K[1][1]; ke[10][11] = K[1][2];\n\t\tke[11][9] = K[2][0]; ke[11][10] = K[2][1]; ke[11][11] = K[2][2];\n\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\t// current values of Lagrange multipliers\n\t\tvec3d Lm = m_F;\t// lagrange multiplier\n\t\tdouble mu = m_u;\n\n\t\t// rigid body positions\n\t\tvec3d ra = RBa.m_rt;\n\t\tvec3d rb = RBb.m_rt;\n\n\t\t// rigid body rotations\n\t\tquatd Qa = RBa.GetRotation();\n\t\tquatd Qb = RBb.GetRotation();\n\n\t\t// current joint offset\n\t\tvec3d qa = Qa*m_qa0;\n\t\tvec3d qb = Qb*m_qb0;\n\n\t\t// \"gap\" vector\n\t\tvec3d g = ra + qa - rb - qb;\n\n\t\t// current joint axis\n\t\tvec3d n0 = m_e0[0];\n\t\tvec3d ea = Qa*n0;\n\t\tvec3d eb = Qb*n0;\n\t\tmat3d N;\n\t\tN[0][0] =     0; N[0][1] =  n0.z; N[0][2] = -n0.y;\n\t\tN[1][0] = -n0.z; N[1][1] =     0; N[1][2] =  n0.x;\n\t\tN[2][0] =  n0.y; N[2][1] = -n0.x; N[2][2] =     0;\n\t\tmat3d NT = N.transpose();\n\n\t\t// projection matrix\n\t\tmat3d Pa = mat3dd(1.0) - dyad(ea);\n\n\t\t// setup skew matrices\n\t\tmat3d ya;\n\t\tya[0][0] =     0; ya[0][1] =  qa.z; ya[0][2] = -qa.y;\n\t\tya[1][0] = -qa.z; ya[1][1] =     0; ya[1][2] =  qa.x;\n\t\tya[2][0] =  qa.y; ya[2][1] = -qa.x; ya[2][2] =     0;\n\t\tmat3d yaT = ya.transpose();\n\n\t\tmat3d yb;\n\t\tyb[0][0] =     0; yb[0][1] =  qb.z; yb[0][2] = -qb.y;\n\t\tyb[1][0] = -qb.z; yb[1][1] =     0; yb[1][2] =  qb.x;\n\t\tyb[2][0] =  qb.y; yb[2][1] = -qb.x; yb[2][2] =     0;\n\t\tmat3d ybT = yb.transpose();\n\n\t\tmat3d GaT = mat3dd(ea*g) + (g & ea);\n\t\tmat3d La  = mat3dd(ea*Lm) + (ea & Lm);\n\t\tmat3d LaT = La.transpose();\n\n\t\tFEElementMatrix ke; ke.resize(16, 16);\n\t\tke.zero();\n\n\t\t// row 1\n\t\tke.add_symm(0,  3, -La*N);\n\t\tke.add_symm(0, 12,    Pa);\n\n\t\t// row 2\n\t\tke.add(3, 3, -(yaT*La*N + NT*LaT*ya)-NT*dyads(Lm, g)*N);\n\t\tke.add_symm(3, 6, NT*LaT);\n\t\tke.add_symm(3, 9, NT*LaT*yb + NT*N*mu);\n\t\tke.add_symm(3, 12, yaT*Pa - NT*GaT);\n\t\tke.add_symm(3, 15, NT*eb);\n\n\t\t// row 3\n\t\tke.add_symm(6, 12,   -Pa);\n\n\t\t// row 4\n\t\tke.add_symm(9, 12, -ybT*Pa);\n\t\tke.add_symm(9, 15,   NT*ea);\n\n\t\tdouble e = 0.0;\n\t\tfor (int i = 0; i < 16; ++i)\n\t\t\tfor (int j = i + 1; j < 16; ++j) e = ke(i, j) - ke(j, i);\n\n\t\tvector<int> LM;\n\t\tUnpackLM(LM);\n\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidCylindricalJoint::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d ra, rb, qa, qb, c, ksi, Lm;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    double normF0, normF1;\n    vec3d Um;\n    double normM0, normM1;\n    bool bconv = true;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n    \n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    mat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n    vec3d p = m_bd ? ea[0]*m_dp : vec3d(0,0,0);\n    c = P*(rb + zb - ra - za) - p;\n    \n    normF0 = sqrt(m_L*m_L);\n    \n    // calculate trial multiplier\n    Lm = m_L + c*m_eps;\n    \n    normF1 = sqrt(Lm*Lm);\n    \n    ksi = (ea[0] ^ eb[0])/2;\n    if (m_bq) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(m_qp,ea[0]);\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        ksi = r.GetVector()*r.GetAngle();\n    }\n    \n    normM0 = sqrt(m_U*m_U);\n    \n    // calculate trial multiplier\n    Um = m_U + ksi*m_ups;\n    \n    normM1 = sqrt(Um*Um);\n    \n    // check convergence of constraints\n    feLog(\" rigid connector # %d (cylindrical joint)\\n\", m_nID+1);\n    feLog(\"                  CURRENT        REQUIRED\\n\");\n    double pctn = 0;\n    double gap = c.norm();\n    double qap = ksi.norm();\n    if (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n    if (m_atol) feLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n    else        feLog(\"    force : %15le        ***\\n\", pctn);\n    if (m_gtol) feLog(\"    gap   : %15le %15le\\n\", gap, m_gtol);\n    else        feLog(\"    gap   : %15le        ***\\n\", gap);\n    double qctn = 0;\n    if (fabs(normM1) > 1e-10) qctn = fabs((normM1 - normM0)/normM1);\n    if (m_atol) feLog(\"    moment: %15le %15le\\n\", qctn, m_atol);\n    else        feLog(\"    moment: %15le        ***\\n\", qctn);\n    if (m_qtol) feLog(\"    angle : %15le %15le\\n\", qap, m_qtol);\n    else        feLog(\"    angle : %15le        ***\\n\", qap);\n    \n    if (m_atol && ((pctn >= m_atol) || (qctn >= m_atol))) bconv = false;\n    if (m_gtol && (gap >= m_gtol)) bconv = false;\n    if (m_qtol && (qap >= m_qtol)) bconv = false;\n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    if (!bconv)\n    {\n        // update multipliers\n        m_L = Lm;\n        m_U = Um;\n    }\n    \n    // auto-penalty update (works only with gaptol and angtol)\n\tif (m_bautopen)\n\t{\n\t\tif (m_gtol && (gap > m_gtol)) {\n\t\t\tm_eps = fmax(gap / m_gtol, 100)*m_eps;\n\t\t\tfeLog(\"    force_penalty :         %15le\\n\", m_eps);\n\t\t}\n\t\tif (m_qtol && (qap > m_qtol)) {\n\t\t\tm_ups = fmax(qap / m_qtol, 100)*m_ups;\n\t\t\tfeLog(\"    moment_penalty :        %15le\\n\", m_ups);\n\t\t}\n\t}\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidCylindricalJoint::Update()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) return;\n\n    vec3d ra, rb;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    mat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n    vec3d p = m_bd ? ea[0]*m_dp : vec3d(0,0,0);\n    vec3d c = P*(rb + zb - ra - za) - p;\n    m_F = m_L + c*m_eps + ea[0]*m_Fp;\n    \n    vec3d ksi = (ea[0] ^ eb[0])/2;\n    if (m_bq) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(m_qp,ea[0]);\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        ksi = r.GetVector()*r.GetAngle();\n    }\n    m_M = m_U + ksi*m_ups + ea[0]*m_Mp;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidCylindricalJoint::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n    m_U = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_q0 - RBa.m_r0;\n    m_qb0 = m_q0 - RBb.m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidCylindricalJoint::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n\n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative translation onto local basis\n    vec3d y(x*ea[0], x*ea[1], x*ea[2]);\n    \n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidCylindricalJoint::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    if (global) return q;\n    \n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative rotation onto local basis\n    vec3d y(q*ea[0], q*ea[1], q*ea[2]);\n    \n    return y;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidCylindricalJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidCylindricalJoint class implements a cylindrical joint. The rigid joint\n//! allows the user to connect two rigid bodies at a point in space\n//! and allow rotation about, and translation along, a single prescribed axis.\n\nclass FEBIOMECH_API FERigidCylindricalJoint : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidCylindricalJoint(FEModel* pfem);\n    \n    //! destructor\n\t~FERigidCylindricalJoint();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global = false) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global = false) override;\n\n\t//! initial position \n\tvec3d InitialPosition() const;\n\n\t//! current position\n\tvec3d Position() const;\n\n\t//! orientation\n\tquatd Orientation() const;\n\nprotected:\n\tint InitEquations(int neq) override;\n    \n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\n\tvoid UnpackLM(vector<int>& lm);\n\npublic: // parameters\n    double\tm_atol;\t//! augmented Lagrangian tolerance\n    double  m_gtol; //! augmented Lagrangian gap tolerance\n    double  m_qtol; //! augmented Lagrangian angular gap tolerance\n    int     m_naugmin;  //! minimum number of augmentations\n    int     m_naugmax;  //! maximum number of augmentations\n    double\tm_eps;\t//! penalty factor for constraining force\n    double\tm_ups;\t//! penalty factor for constraining moment\n    vec3d\tm_q0;\t//! initial position of joint\n    double  m_dp;   //! prescribed translation\n    double  m_qp;   //! prescribed rotation\n    bool    m_bd;   //! flag for prescribing translation\n    bool    m_bq;   //! flag for prescribing rotation\n    double  m_Fp;   //! prescribed force\n    double  m_Mp;   //! prescribed moment\n\tbool\tm_bautopen;\t//!< auto-penalty for gap and ang tolerance\n\tint\t\tm_laugon;\t//!< Lagrange multiplier option\n\nprotected:\n    vec3d\tm_qa0;\t//! initial relative position vector of joint w.r.t. A\n    vec3d\tm_qb0;\t//! initial relative position vector of joint w.r.t. B\n    \n    vec3d\tm_e0[3];\t//! initial joint basis\n    vec3d\tm_ea0[3];\t//! initial joint basis w.r.t. A\n    vec3d\tm_eb0[3];\t//! initial joint basis w.r.t. B\n    \n    vec3d\tm_L;\t//! Lagrange multiplier for constraining force\n    vec3d\tm_U;\t//! Lagrange multiplier for constraining moment\n\tdouble\tm_u;\n\n\tvec3d m_Lp;\n\tdouble m_up;\n\n\tvector<int> m_EQ;\n    \n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidDamper.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidDamper.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidDamper, FERigidConnector);\n\tADD_PARAMETER(m_c   , \"c\"          );\n\tADD_PARAMETER(m_a0  , \"insertion_a\");\n\tADD_PARAMETER(m_b0  , \"insertion_b\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidDamper::FERigidDamper(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n    m_c = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidDamper::Init()\n{\n    // reset force\n    m_F = vec3d(0,0,0);\n    \n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n    // set spring insertions relative to rigid body center of mass\n    m_qa0 = m_a0 - m_rbA->m_r0;\n    m_qb0 = m_b0 - m_rbB->m_r0;\n    \n    m_at = m_a0;\n    m_bt = m_b0;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidDamper::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n\tar & m_qa0 & m_qb0;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidDamper::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n    \n    // body A\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n    vec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n    vec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n    vec3d va = vat*alpha + vap*(1-alpha);\n    \n    // body b\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n    vec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n    vec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n    vec3d vb = vbt*alpha + vbp*(1-alpha);\n    \n    m_F = (vb - va)*m_c;\n    \n    fa[0] = m_F.x;\n    fa[1] = m_F.y;\n    fa[2] = m_F.z;\n    \n    fa[3] = za.y*m_F.z-za.z*m_F.y;\n    fa[4] = za.z*m_F.x-za.x*m_F.z;\n    fa[5] = za.x*m_F.y-za.y*m_F.x;\n    \n    fb[0] = -m_F.x;\n    fb[1] = -m_F.y;\n    fb[2] = -m_F.z;\n    \n    fb[3] = -zb.y*m_F.z+zb.z*m_F.y;\n    fb[4] = -zb.z*m_F.x+zb.x*m_F.z;\n    fb[5] = -zb.x*m_F.y+zb.y*m_F.x;\n    \n    for (int i=0; i<6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n    for (int i=0; i<6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidDamper::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tdouble alpha = tp.alphaf;\n\tdouble beta  = tp.beta;\n\tdouble gamma = tp.gamma;\n    \n    // get time increment\n    double dt = tp.timeIncrement;\n    \n    vector<int> LM(12);\n\tFEElementMatrix ke; ke.resize(12, 12);\n    ke.zero();\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    mat3dd I(1);\n    \n    // body A\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    vec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n    vec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n    vec3d va = vat*alpha + vap*(1-alpha);\n\tquatd qai = RBa.GetRotation()*RBa.m_qp.Inverse(); qai.MakeUnit();\n    vec3d cai = qai.GetVector()*(2*tan(qai.GetAngle()/2));\n    mat3d Ta = I + skew(cai)/2 + dyad(cai)/4;\n    \n    // body b\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n    vec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n    vec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n    vec3d vb = vbt*alpha + vbp*(1-alpha);\n\tquatd qbi = RBb.GetRotation()*RBb.m_qp.Inverse(); qbi.MakeUnit();\n    vec3d cbi = qbi.GetVector()*(2*tan(qbi.GetAngle()/2));\n    mat3d Tb = I + skew(cbi)/2 + dyad(cbi)/4;\n    \n    m_F = (vb - va)*m_c;\n    \n    mat3ds A = I*(gamma/beta/dt);\n    mat3d Ba = zathat*Ta.transpose()*(gamma/beta/dt) + skew(RBa.m_wt)*zathat;\n    mat3d Bb = zbthat*Tb.transpose()*(gamma/beta/dt) + skew(RBb.m_wt)*zbthat;\n    \n    mat3d K;\n    \n    // (1,1)\n    K = A*(alpha*m_c);\n    ke[0][0] = K[0][0]; ke[0][1] = K[0][1]; ke[0][2] = K[0][2];\n    ke[1][0] = K[1][0]; ke[1][1] = K[1][1]; ke[1][2] = K[1][2];\n    ke[2][0] = K[2][0]; ke[2][1] = K[2][1]; ke[2][2] = K[2][2];\n    \n    // (1,2)\n    K = Ba*(-alpha*m_c);\n    ke[0][3] = K[0][0]; ke[0][4] = K[0][1]; ke[0][5] = K[0][2];\n    ke[1][3] = K[1][0]; ke[1][4] = K[1][1]; ke[1][5] = K[1][2];\n    ke[2][3] = K[2][0]; ke[2][4] = K[2][1]; ke[2][5] = K[2][2];\n    \n    // (1,3)\n    K = A*(-alpha*m_c);\n    ke[0][6] = K[0][0]; ke[0][7] = K[0][1]; ke[0][8] = K[0][2];\n    ke[1][6] = K[1][0]; ke[1][7] = K[1][1]; ke[1][8] = K[1][2];\n    ke[2][6] = K[2][0]; ke[2][7] = K[2][1]; ke[2][8] = K[2][2];\n    \n    // (1,4)\n    K = Bb*(alpha*m_c);\n    ke[0][9] = K[0][0]; ke[0][10] = K[0][1]; ke[0][11] = K[0][2];\n    ke[1][9] = K[1][0]; ke[1][10] = K[1][1]; ke[1][11] = K[1][2];\n    ke[2][9] = K[2][0]; ke[2][10] = K[2][1]; ke[2][11] = K[2][2];\n    \n    // (2,1)\n    K = zahat*A*(alpha*m_c);\n    ke[3][0] = K[0][0]; ke[3][1] = K[0][1]; ke[3][2] = K[0][2];\n    ke[4][0] = K[1][0]; ke[4][1] = K[1][1]; ke[4][2] = K[1][2];\n    ke[5][0] = K[2][0]; ke[5][1] = K[2][1]; ke[5][2] = K[2][2];\n    \n    // (2,2)\n    K = (zahat*Ba)*(-alpha*m_c);\n    ke[3][3] = K[0][0]; ke[3][4] = K[0][1]; ke[3][5] = K[0][2];\n    ke[4][3] = K[1][0]; ke[4][4] = K[1][1]; ke[4][5] = K[1][2];\n    ke[5][3] = K[2][0]; ke[5][4] = K[2][1]; ke[5][5] = K[2][2];\n    \n    // (2,3)\n    K = zahat*A*(-alpha*m_c);\n    ke[3][6] = K[0][0]; ke[3][7] = K[0][1]; ke[3][8] = K[0][2];\n    ke[4][6] = K[1][0]; ke[4][7] = K[1][1]; ke[4][8] = K[1][2];\n    ke[5][6] = K[2][0]; ke[5][7] = K[2][1]; ke[5][8] = K[2][2];\n    \n    // (2,4)\n    K = (zahat*Bb)*(alpha*m_c);\n    ke[3][9] = K[0][0]; ke[3][10] = K[0][1]; ke[3][11] = K[0][2];\n    ke[4][9] = K[1][0]; ke[4][10] = K[1][1]; ke[4][11] = K[1][2];\n    ke[5][9] = K[2][0]; ke[5][10] = K[2][1]; ke[5][11] = K[2][2];\n    \n    \n    // (3,1)\n    K = A*(-alpha*m_c);\n    ke[6][0] = K[0][0]; ke[6][1] = K[0][1]; ke[6][2] = K[0][2];\n    ke[7][0] = K[1][0]; ke[7][1] = K[1][1]; ke[7][2] = K[1][2];\n    ke[8][0] = K[2][0]; ke[8][1] = K[2][1]; ke[8][2] = K[2][2];\n    \n    // (3,2)\n    K = Ba*(alpha*m_c);\n    ke[6][3] = K[0][0]; ke[6][4] = K[0][1]; ke[6][5] = K[0][2];\n    ke[7][3] = K[1][0]; ke[7][4] = K[1][1]; ke[7][5] = K[1][2];\n    ke[8][3] = K[2][0]; ke[8][4] = K[2][1]; ke[8][5] = K[2][2];\n    \n    // (3,3)\n    K = A*(alpha*m_c);\n    ke[6][6] = K[0][0]; ke[6][7] = K[0][1]; ke[6][8] = K[0][2];\n    ke[7][6] = K[1][0]; ke[7][7] = K[1][1]; ke[7][8] = K[1][2];\n    ke[8][6] = K[2][0]; ke[8][7] = K[2][1]; ke[8][8] = K[2][2];\n    \n    // (3,4)\n    K = Bb*(-alpha*m_c);\n    ke[6][9] = K[0][0]; ke[6][10] = K[0][1]; ke[6][11] = K[0][2];\n    ke[7][9] = K[1][0]; ke[7][10] = K[1][1]; ke[7][11] = K[1][2];\n    ke[8][9] = K[2][0]; ke[8][10] = K[2][1]; ke[8][11] = K[2][2];\n    \n    \n    // (4,1)\n    K = zbhat*A*(-alpha*m_c);\n    ke[9 ][0] = K[0][0]; ke[ 9][1] = K[0][1]; ke[ 9][2] = K[0][2];\n    ke[10][0] = K[1][0]; ke[10][1] = K[1][1]; ke[10][2] = K[1][2];\n    ke[11][0] = K[2][0]; ke[11][1] = K[2][1]; ke[11][2] = K[2][2];\n    \n    // (4,2)\n    K = (zbhat*Ba)*(alpha*m_c);\n    ke[9 ][3] = K[0][0]; ke[ 9][4] = K[0][1]; ke[ 9][5] = K[0][2];\n    ke[10][3] = K[1][0]; ke[10][4] = K[1][1]; ke[10][5] = K[1][2];\n    ke[11][3] = K[2][0]; ke[11][4] = K[2][1]; ke[11][5] = K[2][2];\n    \n    // (4,3)\n    K = zbhat*A*(alpha*m_c);\n    ke[9 ][6] = K[0][0]; ke[ 9][7] = K[0][1]; ke[ 9][8] = K[0][2];\n    ke[10][6] = K[1][0]; ke[10][7] = K[1][1]; ke[10][8] = K[1][2];\n    ke[11][6] = K[2][0]; ke[11][7] = K[2][1]; ke[11][8] = K[2][2];\n    \n    // (4,4)\n    K = (zbhat*Bb)*(-alpha*m_c);\n    ke[9 ][9] = K[0][0]; ke[ 9][10] = K[0][1]; ke[ 9][11] = K[0][2];\n    ke[10][9] = K[1][0]; ke[10][10] = K[1][1]; ke[10][11] = K[1][2];\n    ke[11][9] = K[2][0]; ke[11][10] = K[2][1]; ke[11][11] = K[2][2];\n    \n    for (int j=0; j<6; ++j)\n    {\n        LM[j  ] = RBa.m_LM[j];\n        LM[j+6] = RBb.m_LM[j];\n    }\n    \n\tke.SetIndices(LM);\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidDamper::Augment(int naug, const FETimeInfo& tp)\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidDamper::Update()\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    const FETimeInfo& tp = GetTimeInfo();\n    double alpha = tp.alphaf;\n\n    // body A\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n    vec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n    vec3d va = vat*alpha + vap*(1-alpha);\n    \n    // body b\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n    vec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n    vec3d vb = vbt*alpha + vbp*(1-alpha);\n    \n    m_at = RBa.m_rt + zat;\n    m_bt = RBb.m_rt + zbt;\n    \n    m_F = (vb - va)*m_c;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidDamper::Reset()\n{\n    m_F = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_a0 - RBa.m_r0;\n    m_qb0 = m_b0 - RBb.m_r0;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidDamper::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n\n    return x;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidDamper::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    return q;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidDamper.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidDamper class implements a linear damper that connects\n//! two rigid bodies at arbitrary points (not necessarily nodes).\n//! TODO: This inherits from FENLConstraint, which is not the appropriate base class\nclass FERigidDamper : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidDamper(FEModel* pfem);\n    \n    //! destructor\n    ~FERigidDamper() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global) override;\n\npublic: // parameters\n    double\tm_c;        //! damping constant\n    vec3d\tm_a0;       //! initial absolute position vector of spring on body A\n    vec3d\tm_b0;       //! initial absolute position vector of spring on body B\n\n    // output parameters\n    vec3d    m_at;        //!< current absolute position of spring on body A\n    vec3d    m_bt;        //!< current absolute position of spring on body B\n    \nprotected:\n    vec3d\tm_qa0;      //! initial relative position vector of spring on body A\n    vec3d\tm_qb0;      //! initial relative position vector of spring on body B\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidEulerAngles.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FERigidEulerAngles.h\"\n\nBEGIN_FECORE_CLASS(FERigidEulerAngles, FERigidBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_convention, \"convention\")->setEnums(\"ZXY\\0\");\n\tADD_PARAMETER(m_Ex, \"Ex\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_DEGREE);\n\tADD_PARAMETER(m_Ey, \"Ey\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_DEGREE);\n\tADD_PARAMETER(m_Ez, \"Ez\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_DEGREE);\nEND_FECORE_CLASS();\n\nFERigidEulerAngles::FERigidEulerAngles(FEModel* fem) : FERigidBC(fem)\n{\n\tm_convention = 0;\n\tm_Ex = m_Ey = m_Ez = 0.0;\n\n\tm_rc[0] = new FERigidPrescribedBC(fem); m_rc[0]->SetBC(3);\n\tm_rc[1] = new FERigidPrescribedBC(fem); m_rc[1]->SetBC(4);\n\tm_rc[2] = new FERigidPrescribedBC(fem); m_rc[2]->SetBC(5);\n}\n\nFERigidEulerAngles::~FERigidEulerAngles()\n{\n\tdelete m_rc[0];\n\tdelete m_rc[1];\n\tdelete m_rc[2];\n}\n\nbool FERigidEulerAngles::Init()\n{\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tm_rc[i]->SetRigidMaterial(m_rigidMat);\n\t\tif (m_rc[i]->Init() == false) return false;\n\t}\n\n\treturn true;\n}\n\nvoid FERigidEulerAngles::Activate()\n{\n\tFERigidBC::Activate();\n\tm_rc[0]->Activate();\n\tm_rc[1]->Activate();\n\tm_rc[2]->Activate();\n\tUpdateRotations();\n}\n\nvoid FERigidEulerAngles::Deactivate()\n{\n\tFERigidBC::Deactivate();\n\tm_rc[0]->Deactivate();\n\tm_rc[1]->Deactivate();\n\tm_rc[2]->Deactivate();\n}\n\nvoid FERigidEulerAngles::InitTimeStep()\n{\n\tUpdateRotations();\n}\n\nvoid FERigidEulerAngles::UpdateRotations()\n{\n\tquatd q; \n\tdouble Ex = DEG2RAD * m_Ex;\n\tdouble Ey = DEG2RAD * m_Ey;\n\tdouble Ez = DEG2RAD * m_Ez;\n\tq.SetEuler(Ex, Ey, Ez);\n\tvec3d r = q.GetRotationVector();\n\tm_rc[0]->SetValue(r.x);\n\tm_rc[1]->SetValue(r.y);\n\tm_rc[2]->SetValue(r.z);\n}\n\nvoid FERigidEulerAngles::Serialize(DumpStream& ar)\n{\n\tFERigidBC::Serialize(ar);\n\n\tif (ar.IsShallow() == false)\n\t{\n\t\tfor (int j = 0; j < 3; ++j)\n\t\t{\n\t\t\t// We need to serialize a reference to avoid that\n\t\t\t// restart will try to allocate the m_rc variables\n\t\t\tFERigidPrescribedBC& rc = *m_rc[j];\n\t\t\tar& rc;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidEulerAngles.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"RigidBC.h\"\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FERigidEulerAngles : public FERigidBC\n{\n\tenum EulerConvention {\n\t\tEULER_ZXY\n\t};\n\npublic:\n\tFERigidEulerAngles(FEModel* fem);\n\t~FERigidEulerAngles();\n\n\tbool Init() override;\n\n\tvoid Activate() override;\n\n\tvoid Deactivate() override;\n\n\tvoid InitTimeStep() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tvoid UpdateRotations();\n\nprivate:\n\tint\tm_convention;\n\tdouble\tm_Ex, m_Ey, m_Ez;\n\n\tFERigidPrescribedBC* m_rc[3];\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidFollowerForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidFollowerForce.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEMaterial.h\"\n#include \"FECore/FELoadCurve.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FELinearSystem.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidFollowerForce, FERigidLoad);\n    ADD_PARAMETER(m_rid      , \"rb\"       )->setEnums(\"$(rigid_materials)\");\n    ADD_PARAMETER(m_X        , \"insertion\");\n    ADD_PARAMETER(m_f        , \"force\"    );\n    ADD_PARAMETER(m_brelative, \"relative\" );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidFollowerForce::FERigidFollowerForce(FEModel* pfem) : FERigidLoad(pfem)\n{\n    m_rid = -1;\n    m_X = m_f = vec3d(0,0,0);\n    m_brelative = false;\n}\n\n//-----------------------------------------------------------------------------\n//! do some sanity checks\nbool FERigidFollowerForce::Init()\n{\n    // At this point the rigid ID's are still associated with the materials.\n    // We want to associate them with the rigid objects instead.\n    FEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rid-1));\n    if (pm == 0) return false;\n    m_rid = pm->GetRigidBodyID(); if (m_rid < 0) return false;\n\n    // all is well in the world\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidFollowerForce::Serialize(DumpStream& ar)\n{\n    FEModelLoad::Serialize(ar);\n    ar & m_rid;\n    ar & m_X & m_f;\n    ar & m_brelative;\n}\n\n//-----------------------------------------------------------------------------\n//! Residual\nvoid FERigidFollowerForce::LoadVector(FEGlobalVector& R)\n{\n    FEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& body = *fem.GetRigidBody(m_rid);\n    const FETimeInfo& tp = fem.GetTime();\n\n    double alpha = tp.alphaf;\n    \n    // get the attachment position in global coordinates for body A\n    vec3d Z = (m_brelative ? m_X : m_X - body.m_r0);\n    vec3d zt = body.GetRotation()*Z;\n    vec3d zp = body.GetPreviousRotation()*Z;\n    vec3d z = zt*alpha + zp*(1-alpha);\n\n    // calculate the force value\n    vec3d ft = body.GetRotation()*m_f;\n    vec3d fp = body.GetPreviousRotation()*m_f;\n    vec3d f = ft*alpha + fp*(1-alpha);\n\n    // get the moment about the center of mass\n    vec3d m = z ^ f;\n\n    // apply force and moment to body\n    int n;\n    n = body.m_LM[0]; if (n >= 0) R[n] += f.x;\n    n = body.m_LM[1]; if (n >= 0) R[n] += f.y;\n    n = body.m_LM[2]; if (n >= 0) R[n] += f.z;\n    n = body.m_LM[3]; if (n >= 0) R[n] += m.x;\n    n = body.m_LM[4]; if (n >= 0) R[n] += m.y;\n    n = body.m_LM[5]; if (n >= 0) R[n] += m.z;\n    \n    body.m_Fr += f;\n    body.m_Mr += m;\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix\n//! TODO: Only the stiffness contribution in the were the axial forces are applied\n//!       to the center of mass has been implemented.\nvoid FERigidFollowerForce::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& body = *fem.GetRigidBody(m_rid);\n\n    const FETimeInfo& tp = fem.GetTime();\n    double alpha = tp.alphaf;\n    \n    // get the attachment position in global coordinates for body A\n    vec3d Z = (m_brelative ? m_X : m_X - body.m_r0);\n    vec3d zt = body.GetRotation()*Z;\n    vec3d zp = body.GetPreviousRotation()*Z;\n    vec3d z = zt*alpha + zp*(1-alpha);\n\n    // calculate the force value\n    vec3d ft = body.GetRotation()*m_f;\n    vec3d fp = body.GetPreviousRotation()*m_f;\n    vec3d f = ft*alpha + fp*(1-alpha);\n\n    // build the stiffness matrix components\n    mat3da fthat(ft);\n    mat3da zthat(zt);\n    mat3da fhat(f);\n    mat3da zhat(z);\n    mat3d Krq = fthat*(-alpha);\n    mat3d Kqq = (fhat*zthat - zhat*fthat)*alpha;\n\n    // put it all together\n    FEElementMatrix K; K.resize(6, 6); K.zero();\n    K.sub(0,3, Krq);\n    K.sub(3,3, Kqq);\n\n    // get the equation numbers\n    vector<int> lm(6);\n    lm[ 0] = body.m_LM[0];\n    lm[ 1] = body.m_LM[1];\n    lm[ 2] = body.m_LM[2];\n    lm[ 3] = body.m_LM[3];\n    lm[ 4] = body.m_LM[4];\n    lm[ 5] = body.m_LM[5];\n\n    // assemble into global matrix\n    K.SetIndices(lm);\n    LS.Assemble(K);\n}\n"
  },
  {
    "path": "FEBioMech/FERigidFollowerForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEModelLoad.h\"\n#include \"FERigidForce.h\"\n#include \"febiomech_api.h\"\n//-----------------------------------------------------------------------------\n//! a follower force on a rigid body\nclass FEBIOMECH_API FERigidFollowerForce : public FERigidLoad\n{\npublic:\n    //! constructor\n    FERigidFollowerForce(FEModel* pfem);\n\n    //! initialization\n    bool Init() override;\n\n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! Residual\n    void LoadVector(FEGlobalVector& R) override;\n\n    //! Stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS) override;\n\npublic:\n    int     m_rid;      //!< rigid body ID\n    vec3d   m_X;        //!< coordinates of attachements in reference state\n    vec3d   m_f;        //!< force\n    bool    m_brelative;        //!< if active, the ra0 and rb0 are relative w.r.t. the COM\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidFollowerMoment.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidFollowerMoment.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEMaterial.h\"\n#include \"FECore/FELoadCurve.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FELinearSystem.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidFollowerMoment, FERigidLoad);\n    ADD_PARAMETER(m_rid      , \"rb\"       )->setEnums(\"$(rigid_materials)\");\n    ADD_PARAMETER(m_m        , \"moment\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidFollowerMoment::FERigidFollowerMoment(FEModel* pfem) : FERigidLoad(pfem)\n{\n    m_rid = -1;\n    m_m = vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\n//! do some sanity checks\nbool FERigidFollowerMoment::Init()\n{\n    // At this point the rigid ID's are still associated with the materials.\n    // We want to associate them with the rigid objects instead.\n    FEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rid-1));\n    if (pm == 0) return false;\n    m_rid = pm->GetRigidBodyID(); if (m_rid < 0) return false;\n\n    // all is well in the world\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidFollowerMoment::Serialize(DumpStream& ar)\n{\n    FEModelLoad::Serialize(ar);\n    ar & m_rid;\n    ar & m_m;\n}\n\n//-----------------------------------------------------------------------------\n//! Residual\nvoid FERigidFollowerMoment::LoadVector(FEGlobalVector& R)\n{\n    FEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& body = *fem.GetRigidBody(m_rid);\n\n    const FETimeInfo& tp = fem.GetTime();\n    double alpha = tp.alphaf;\n    \n    // calculate the moment value\n    vec3d mt = body.GetRotation()*m_m;\n    vec3d mp = body.GetPreviousRotation()*m_m;\n    vec3d m = mt*alpha + mp*(1-alpha);\n\n    // apply force and moment to body\n    int n;\n    n = body.m_LM[3]; if (n >= 0) R[n] += m.x;\n    n = body.m_LM[4]; if (n >= 0) R[n] += m.y;\n    n = body.m_LM[5]; if (n >= 0) R[n] += m.z;\n    \n    body.m_Mr += m;\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix\n//! TODO: Only the stiffness contribution in the were the axial forces are applied\n//!       to the center of mass has been implemented.\nvoid FERigidFollowerMoment::StiffnessMatrix(FELinearSystem& LS)\n{\n    FEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n    FERigidBody& body = *fem.GetRigidBody(m_rid);\n\n    const FETimeInfo& tp = fem.GetTime();\n    double alpha = tp.alphaf;\n    \n    // calculate the moment value\n    vec3d mt = body.GetRotation()*m_m;\n\n    // build the stiffness matrix components\n    mat3da mthat(mt);\n    mat3d Kqq = mthat*(-alpha);\n\n    // put it all together\n    FEElementMatrix K; K.resize(3, 3); K.zero();\n    K.sub(0,0, Kqq);\n\n    // get the equation numbers\n    vector<int> lm(3);\n    lm[ 0] = body.m_LM[3];\n    lm[ 1] = body.m_LM[4];\n    lm[ 2] = body.m_LM[5];\n\n    // assemble into global matrix\n    K.SetIndices(lm);\n    LS.Assemble(K);\n}\n"
  },
  {
    "path": "FEBioMech/FERigidFollowerMoment.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEModelLoad.h\"\n#include \"FERigidForce.h\"\n\n//-----------------------------------------------------------------------------\n//! a follower moment on a rigid body\nclass FEBIOMECH_API FERigidFollowerMoment : public FERigidLoad\n{\npublic:\n    //! constructor\n    FERigidFollowerMoment(FEModel* pfem);\n\n    //! initialization\n    bool Init() override;\n\n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n\n    //! Residual\n    void LoadVector(FEGlobalVector& R) override;\n\n    //! Stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS) override;\n\npublic:\n    int     m_rid;      //!< rigid body ID\n    vec3d   m_m;        //!< moment\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidForce.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEMaterial.h\"\n#include \"FECore/FELoadCurve.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidAxialForce, FERigidLoad);\n\tADD_PARAMETER(m_ida      , \"rbA\"     )->setEnums(\"$(rigid_materials)\");\n\tADD_PARAMETER(m_idb      , \"rbB\"     )->setEnums(\"$(rigid_materials)\");\n\tADD_PARAMETER(m_ra0      , \"ra\"      );\n\tADD_PARAMETER(m_rb0      , \"rb\"      );\n\tADD_PARAMETER(m_s        , \"force\"   )->setUnits(UNIT_FORCE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidAxialForce::FERigidAxialForce(FEModel* pfem) : FERigidLoad(pfem)\n{\n\tm_ida = m_idb = -1;\n\tm_ra0 = m_rb0 = vec3d(0,0,0);\n\tm_s = 0.0;\n\tm_brelative = false;\n}\n\n//-----------------------------------------------------------------------------\n//! do some sanity checks\nbool FERigidAxialForce::Init()\n{\n\t// At this point the rigid ID's are still associated with the materials.\n\t// We want to associate them with the rigid objects instead.\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_ida-1));\n\tif (pm == 0) return false;\n\tm_ida = pm->GetRigidBodyID(); if (m_ida < 0) return false;\n\tpm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_idb-1));\n\tif (pm == 0) return false;\n\tm_idb = pm->GetRigidBodyID(); if (m_idb < 0) return false;\n\n\t// get the actual rigid bodies\n\tFERigidBody& bodyA = *fem.GetRigidBody(m_ida);\n\tFERigidBody& bodyB = *fem.GetRigidBody(m_idb);\n\n\t// get the attachment position in global coordinates for body A\n\tvec3d da0 = (m_brelative ? m_ra0 : m_ra0 - bodyA.m_r0);\n\tvec3d da = bodyA.GetRotation()*da0;\n\tvec3d a = da + bodyA.m_rt;\n\n\t// get the attachment position in global coordinates for body B\n\tvec3d db0 = (m_brelative ? m_rb0 : m_rb0 - bodyB.m_r0);\n\tvec3d db = bodyB.GetRotation()*db0;\n\tvec3d b = db + bodyB.m_rt;\n\n\t// get the unit axial vector\n\t// and make sure it is of finite length\n\tvec3d N = b - a; \n\tdouble L = N.unit();\n\tif (L < 1e-17) return false;\n\n\t// all is well in the world\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidAxialForce::Serialize(DumpStream& ar)\n{\n\tFEModelLoad::Serialize(ar);\n\tar & m_ida & m_idb;\n\tar & m_ra0 & m_rb0;\n\tar & m_s & m_brelative;\n}\n\n//-----------------------------------------------------------------------------\n//! Residual\nvoid FERigidAxialForce::LoadVector(FEGlobalVector& R)\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& bodyA = *fem.GetRigidBody(m_ida);\n\tFERigidBody& bodyB = *fem.GetRigidBody(m_idb);\n\n\t// get the attachment position in global coordinates for body A\n\tvec3d da0 = (m_brelative ? m_ra0 : m_ra0 - bodyA.m_r0);\n\tvec3d da = bodyA.GetRotation()*da0;\n\tvec3d a = da + bodyA.m_rt;\n\n\t// get the attachment position in global coordinates for body B\n\tvec3d db0 = (m_brelative ? m_rb0 : m_rb0 - bodyB.m_r0);\n\tvec3d db = bodyB.GetRotation()*db0;\n\tvec3d b = db + bodyB.m_rt;\n\n\t// get the unit axial vector\n\tvec3d N = b - a; N.unit();\n\n\t// calculate the force value\n\tdouble f = m_s;\n\n\t// get the axial force and torques\n\tvec3d F = N*f;\n\tvec3d Ma = da^F;\n\tvec3d Mb = db^F;\n\n\t// apply force and torque to body A\n\tint n;\n\tn = bodyA.m_LM[0]; if (n >= 0) R[n] += F.x;\n\tn = bodyA.m_LM[1]; if (n >= 0) R[n] += F.y;\n\tn = bodyA.m_LM[2]; if (n >= 0) R[n] += F.z;\n\tn = bodyA.m_LM[3]; if (n >= 0) R[n] += Ma.x;\n\tn = bodyA.m_LM[4]; if (n >= 0) R[n] += Ma.y;\n\tn = bodyA.m_LM[5]; if (n >= 0) R[n] += Ma.z;\n\n\t// apply force and torque to body B\n\tn = bodyB.m_LM[0]; if (n >= 0) R[n] -= F.x;\n\tn = bodyB.m_LM[1]; if (n >= 0) R[n] -= F.y;\n\tn = bodyB.m_LM[2]; if (n >= 0) R[n] -= F.z;\n\tn = bodyB.m_LM[3]; if (n >= 0) R[n] -= Mb.x;\n\tn = bodyB.m_LM[4]; if (n >= 0) R[n] -= Mb.y;\n\tn = bodyB.m_LM[5]; if (n >= 0) R[n] -= Mb.z;\n    \n    bodyA.m_Fr += F;\n    bodyA.m_Mr += Ma;\n    bodyB.m_Fr -= F;\n    bodyB.m_Mr -= Mb;\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix\n//! TODO: Only the stiffness contribution in the were the axial forces are applied\n//!       to the center of mass has been implemented. \nvoid FERigidAxialForce::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Get the rigid bodies\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& bodyA = *fem.GetRigidBody(m_ida);\n\tFERigidBody& bodyB = *fem.GetRigidBody(m_idb);\n\n\t// get the attachment position in global coordinates for body A\n\tvec3d da0 = (m_brelative ? m_ra0 : m_ra0 - bodyA.m_r0);\n\tvec3d da = bodyA.GetRotation()*da0;\n\tvec3d pa = da + bodyA.m_rt;\n\n\t// get the attachment position in global coordinates for body B\n\tvec3d db0 = (m_brelative ? m_rb0 : m_rb0 - bodyB.m_r0);\n\tvec3d db = bodyB.GetRotation()*db0;\n\tvec3d pb = db + bodyB.m_rt;\n\n\t// setup the axial unit vector\n\tvec3d N = pb - pa; \n\tdouble L = N.unit();\n\n\t// calculate the force value\n\tdouble f = -m_s / L;\n\n\t// build the stiffness matrix components\n\tmat3ds M = mat3dd(1.0) - dyad(N);\n\tmat3da A(-da);\n\tmat3da B(-db);\n\tmat3da S(-N);\n\n\tmat3d MA = M*A;\n\tmat3d MB = M*B;\n\tmat3d AM = A*M;\n\n\tmat3d AMA = A*MA;\n\tmat3d BMB = B*MB;\n\tmat3d AMB = A*MB;\n\n\tmat3d SA = S*A;\n\tmat3d SB = S*B;\n\n\t// put it all together\n\tFEElementMatrix K; K.resize(12, 12); K.zero();\n\tK.sub(0,0, M);\n\tK.sub(0,3, MA);\n\tK.add(0,6, M);\n\tK.add(0,9, MB);\n\tK.add(3,3, SA*L + AMA);\n\tK.add(3,6, AM);\n\tK.sub(3,9, AMB);\n\tK.sub(6,6,M);\n\tK.sub(6,9, MB);\n\tK.add(9,9, SB*(-L) + BMB);\n\n\t// since this is a symmetric matrix, fill the bottom triangular part\n\tK.copy_ut();\n\n\t// and multiply by f\n\tK *= f;\n\n\t// get the equation numbers\n\tvector<int> lm(12);\n\tlm[ 0] = bodyA.m_LM[0];\n\tlm[ 1] = bodyA.m_LM[1];\n\tlm[ 2] = bodyA.m_LM[2];\n\tlm[ 3] = bodyA.m_LM[3];\n\tlm[ 4] = bodyA.m_LM[4];\n\tlm[ 5] = bodyA.m_LM[5];\n\n\tlm[ 6] = bodyB.m_LM[0];\n\tlm[ 7] = bodyB.m_LM[1];\n\tlm[ 8] = bodyB.m_LM[2];\n\tlm[ 9] = bodyB.m_LM[3];\n\tlm[10] = bodyB.m_LM[4];\n\tlm[11] = bodyB.m_LM[5];\n\n\t// assemble into global matrix\n\tK.SetIndices(lm);\n\tLS.Assemble(K);\n}\n\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FERigidBodyForce, FERigidLoad)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"Rx\\0Ry\\0Rz\");\n\tADD_PARAMETER(m_force, \"value\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_FORCE);\n\tADD_PARAMETER(m_ntype, \"load_type\")->setEnums(\"LOAD\\0FOLLOW\\0TARGET\");\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFERigidBodyForce::FERigidBodyForce(FEModel* pfem) : FERigidLoad(pfem)\n{\n\tm_rigidMat = -1;\n\tm_ntype = FORCE_LOAD;\n\tm_rid = -1;\n\tm_brelative = false;\n\tm_force0 = 0.0;\n\tm_force = 0.0;\n\tm_dof = 0;\n}\n\nvoid FERigidBodyForce::SetRigidMaterialID(int nid) { m_rigidMat = nid; }\n\nvoid FERigidBodyForce::SetDOF(int bc) { m_dof = bc; }\n\nvoid FERigidBodyForce::SetLoadType(int loadType) { m_ntype = loadType; }\n\nvoid FERigidBodyForce::SetForce(double f) { m_force = f; }\n\n//-----------------------------------------------------------------------------\nbool FERigidBodyForce::Init()\n{\n\t// At this point the rigid ID's are still associated with the materials.\n\t// We want to associate them with the rigid objects instead.\n\tFEModel& fem = *GetFEModel();\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\tif (pm == 0) return false;\n\n\t// check the dof value\n\tif ((m_dof < 0) || (m_dof >= 3)) return false;\n\n\treturn FEModelLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBodyForce::Activate()\n{\n\tFEModelLoad::Activate();\n\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\n\tm_rid = pm->GetRigidBodyID(); assert(m_rid >= 0);\n\n\tFERigidBody& rb = *fem.GetRigidBody(m_rid);\n\tif (m_ntype == FORCE_TARGET)\n\t{\n\t\tswitch (m_dof)\n\t\t{\n\t\tcase 0: m_force0 = rb.m_Fr.x; break;\n\t\tcase 1: m_force0 = rb.m_Fr.y; break;\n\t\tcase 2: m_force0 = rb.m_Fr.z; break;\n\t\t}\n\t}\n\tif ((m_ntype == FORCE_LOAD) && m_brelative)\n\t{\n\t\tswitch (m_dof)\n\t\t{\n\t\tcase 0: m_force0 = rb.m_Fr.x; break;\n\t\tcase 1: m_force0 = rb.m_Fr.y; break;\n\t\tcase 2: m_force0 = rb.m_Fr.z; break;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBodyForce::Serialize(DumpStream& ar)\n{\n\tFEModelLoad::Serialize(ar);\n\tar & m_ntype & m_dof & m_rigidMat & m_force & m_rid;\n}\n\n//-----------------------------------------------------------------------------\n//! Residual\nvoid FERigidBodyForce::LoadVector(FEGlobalVector& R)\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(m_rid);\n\tdouble t = CurrentTime();\n\n\tif (m_ntype == FORCE_FOLLOW)\n\t{\n\t\t// setup the force vector\n\t\tvec3d f(0,0,0);\n\t\tif      (m_dof == 0) f.x = m_force;\n\t\telse if (m_dof == 1) f.y = m_force;\n\t\telse if (m_dof == 2) f.z = m_force;\n\t\n\t\t// apply the rigid body rotation\n\t\tf = rb.GetRotation()*f;\n\n\t\t// add to the residual\n\t\tint n;\n\t\tn = rb.m_LM[0]; if (n >= 0) R[n] += f.x;\n\t\tn = rb.m_LM[1]; if (n >= 0) R[n] += f.y;\n\t\tn = rb.m_LM[2]; if (n >= 0) R[n] += f.z;\n\t}\n\telse\n\t{\n\t\tint I = rb.m_LM[m_dof];\n\t\tif (I >= 0)\n\t\t{\n\t\t\tdouble incVal = 0.0;\n\t\t\tif (m_ntype == FORCE_LOAD)\n\t\t\t{\n\t\t\t\tincVal = m_force + m_force0;\n\t\t\t}\n\t\t\telse if (m_ntype == FORCE_TARGET)\n\t\t\t{\n\t\t\t\t// get the current analysis step\n\t\t\t\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t\t\t\tdouble t0 = pstep->m_tstart;\n\t\t\t\tdouble t1 = pstep->m_tend;\n\t\t\t\tdouble w = (t - t0) / (t1 - t0);\n\t\t\t\tassert((w >= -0.0000001) && (w <= 1.0000001));\n\t\t\t\tdouble f0 = m_force0, f1 = m_force;\n\n\t\t\t\tdouble f = f0 * (1.0 - w) + f1 * w;\n\t\t\t\tincVal = f;\n\t\t\t}\n\n\t\t\tR[I] += incVal;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix\nvoid FERigidBodyForce::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// I think for follower forces I need to contribute to the stiffness matrix, but I'm not sure yet what.\n}\n\n\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FERigidBodyMoment, FERigidLoad)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"Ru\\0Rv\\0Rw\\0\");\n\tADD_PARAMETER(m_value, \"value\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_MOMENT);\n\tADD_PARAMETER(m_brelative, \"relative\");\n\tADD_PARAMETER(m_ntype, \"load_type\")->setEnums(\"LOAD\\0FOLLOW\\0TARGET\");\nEND_FECORE_CLASS();\n\nFERigidBodyMoment::FERigidBodyMoment(FEModel* pfem) : FERigidLoad(pfem)\n{\n\tm_rigidMat = -1;\n\tm_rid = -1;\n\tm_brelative = false;\n\tm_value0 = 0.0;\n\tm_value = 0.0;\n\tm_dof = 0;\n\tm_ntype = MOMENT_LOAD;\n}\n\nvoid FERigidBodyMoment::SetRigidMaterialID(int nid) { m_rigidMat = nid; }\n\nvoid FERigidBodyMoment::SetDOF(int bc) { m_dof = bc; }\n\nvoid FERigidBodyMoment::SetValue(double f) { m_value = f; }\n\n//-----------------------------------------------------------------------------\nbool FERigidBodyMoment::Init()\n{\n\t// At this point the rigid ID's are still associated with the materials.\n\t// We want to associate them with the rigid objects instead.\n\tFEModel& fem = *GetFEModel();\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\tif (pm == 0) return false;\n\n\t// follower moments are not supported yet.\n\tif (m_ntype == MOMENT_FOLLOW)\n\t{\n\t\tfeLogError(\"Follower moments are not supported.\");\n\t\treturn false;\n\t}\n\n\treturn FEModelLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBodyMoment::Activate()\n{\n\tFEModelLoad::Activate();\n\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\n\tm_rid = pm->GetRigidBodyID(); assert(m_rid >= 0);\n\n\tFERigidBody& rb = *fem.GetRigidBody(m_rid);\n\tif (m_ntype == MOMENT_TARGET)\n\t{\n\t\tswitch (m_dof)\n\t\t{\n\t\tcase 0: m_value0 = rb.m_Mr.x; break;\n\t\tcase 1: m_value0 = rb.m_Mr.y; break;\n\t\tcase 2: m_value0 = rb.m_Mr.z; break;\n\t\t}\n\t}\n\tif ((m_ntype == MOMENT_LOAD) && m_brelative)\n\t{\n\t\tswitch (m_dof)\n\t\t{\n\t\tcase 0: m_value0 = rb.m_Mr.x; break;\n\t\tcase 1: m_value0 = rb.m_Mr.y; break;\n\t\tcase 2: m_value0 = rb.m_Mr.z; break;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBodyMoment::Serialize(DumpStream& ar)\n{\n\tFEModelLoad::Serialize(ar);\n\tar & m_value0 & m_rid;\n}\n\n//-----------------------------------------------------------------------------\n//! Residual\nvoid FERigidBodyMoment::LoadVector(FEGlobalVector& R)\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& rb = *fem.GetRigidBody(m_rid);\n\tdouble t = CurrentTime();\n\n\tint I = rb.m_LM[m_dof + 3];\n\tif (I >= 0)\n\t{\n\t\tdouble incVal = 0.0;\n\t\tif (m_ntype == MOMENT_LOAD)\n\t\t{\n\t\t\tincVal = m_value + m_value0;\n\t\t}\n\t\telse if (m_ntype == MOMENT_TARGET)\n\t\t{\n\t\t\t// get the current analysis step\n\t\t\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t\t\tdouble t0 = pstep->m_tstart;\n\t\t\tdouble t1 = pstep->m_tend;\n\t\t\tdouble w = (t - t0) / (t1 - t0);\n\t\t\tassert((w >= -0.0000001) && (w <= 1.0000001));\n\t\t\tdouble M0 = m_value0, M1 = m_value;\n\n\t\t\tdouble M = M0 * (1.0 - w) + M1 * w;\n\t\t\tincVal = M;\n\t\t}\n\n\t\tR[I] += incVal;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Stiffness matrix\nvoid FERigidBodyMoment::StiffnessMatrix(FELinearSystem& LS)\n{\n\n}\n"
  },
  {
    "path": "FEBioMech/FERigidForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEModelLoad.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FERigidLoad : public FEModelLoad\n{\npublic:\n\tFERigidLoad(FEModel* fem) : FEModelLoad(fem) {}\n\n\tFECORE_BASE_CLASS(FERigidLoad)\n};\n\n//-----------------------------------------------------------------------------\n//! an axial force between two rigid bodies\nclass FEBIOMECH_API FERigidAxialForce : public FERigidLoad\n{\npublic:\n\t//! constructor\n\tFERigidAxialForce(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! Stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\npublic:\n\tint\t\tm_ida, m_idb;\t\t//!< rigid body ID's\n\tvec3d\tm_ra0, m_rb0;\t\t//!< coordinates of attachements in reference state\n\tdouble\tm_s;\t\t\t\t//!< scale factor\n\tbool\tm_brelative;\t\t//!< if active, the ra0 and rb0 are relative w.r.t. the COM\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n//! rigid body force\n//! TODO: I'd like to split this class into two classes: one handling the case\n//!       were the force is const, and one where the force is a follower force.\n//!       Perhaps I can derive the const force from FENodalLoad since it applies\n//!       a force directly to the rigid \"node\".\nclass FEBIOMECH_API FERigidBodyForce : public FERigidLoad\n{\npublic:\n\tenum { FORCE_LOAD, FORCE_FOLLOW, FORCE_TARGET };\t// values for m_ntype\n\npublic:\n\tFERigidBodyForce(FEModel* pfem);\n\n\t//! Activation\n\tvoid Activate() override;\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! forces\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! Stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\npublic:\n\tvoid SetRigidMaterialID(int nid);\n\n\tvoid SetDOF(int bc);\n\n\tvoid SetLoadType(int loadType);\n\n\tvoid SetForce(double f);\n\nprivate:\n\tint\t\tm_rigidMat;\t\t//!< rigid body material id\n\tint\t\tm_dof;\t\t\t//!< force direction\n\tbool\tm_brelative;\t//!< relative flag\n\n\tint\t\tm_ntype;\t\t//!< type of force\n\tdouble\tm_force;\t\t//!< applied force\n\tdouble\tm_force0;\t\t//!< initial force at activation\n\tint\t\tm_rid;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! rigid body moment\nclass FEBIOMECH_API FERigidBodyMoment : public FERigidLoad\n{\npublic:\n\tenum { MOMENT_LOAD, MOMENT_FOLLOW, MOMENT_TARGET };\t// values for m_ntype\n\npublic:\n\tFERigidBodyMoment(FEModel* pfem);\n\n\t//! Activation\n\tvoid Activate() override;\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! forces\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! Stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\npublic:\n\tvoid SetRigidMaterialID(int nid);\n\n\tvoid SetDOF(int bc);\n\n\tvoid SetValue(double f);\n\nprivate:\n\tint\t\tm_rigidMat;\t\t//!< rigid body material id\n\tint\t\tm_dof;\t\t\t//!< force direction\n\tbool\tm_brelative;\t//!< relative flag\n\tdouble\tm_value;\t\t//!< applied moment\n\tint\t\tm_ntype;\t\t//!< type of moment\n\n\tdouble\tm_value0;\t\t//!< initial moment at activation (used with brelative flag)\n\tint\t\tm_rid;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidJoint.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol   , \"tolerance\");\n\tADD_PARAMETER(m_eps    , \"penalty\"  );\n\tADD_PARAMETER(m_q0     , \"joint\"    );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidJoint::FERigidJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n\tstatic int count = 1;\n\tm_nID = count++;\n\n\tm_laugon = FECore::AUGLAG_METHOD;\t\t// Augmented Lagrangian by default for backward compatibility\n\tm_eps = 0.0;\n\tm_atol = 0.01;\n\n\tm_rbA = m_rbB = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! destructor\nFERigidJoint::~FERigidJoint()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidJoint::Init()\n{\n\t// reset force\n\tm_F = m_Fp = vec3d(0,0,0);\n\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n\t// initialize relative joint positions\n\tm_qa0 = m_q0 - m_rbA->m_r0;\n\tm_qb0 = m_q0 - m_rbB->m_r0;\n\n\t// we make sure we have a non-zero penalty for penalty and auglag method\n\tif ((m_laugon != FECore::LAGMULT_METHOD) && (m_eps == 0.0)) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// allocate equations\nint FERigidJoint::InitEquations(int neq)\n{\n\tm_LM.resize(3, -1);\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\t// we allocate three equations\n\t\tm_LM[0] = neq;\n\t\tm_LM[1] = neq + 1;\n\t\tm_LM[2] = neq + 2;\n\t\treturn 3;\n\t}\n\telse return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Build the matrix profile\nvoid FERigidJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tdouble alpha = tp.alpha;\n\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\t// body A\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n\tvec3d ra = rat * alpha + rap*(1 - alpha);\n\n\tvec3d zat = RBa.GetRotation() * m_qa0;\n\tvec3d zap = RBa.m_qp * m_qa0;\n\tvec3d za = zat * alpha + zap * (1 - alpha);\n\n\t// body b\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = rbt*alpha + rbp*(1 - alpha);\n\n\tvec3d zbt = RBb.GetRotation() * m_qb0;\n\tvec3d zbp = RBb.m_qp * m_qb0;\n\tvec3d zb = zbt * alpha + zbp * (1 - alpha);\n\n\t// constraint\n\tvec3d c = rb + zb - ra - za;\n\n\t// forces\n\tvec3d F = m_L + c * m_eps;\n\tif (m_laugon == FECore::LAGMULT_METHOD) F = -m_F;\n\n\tvec3d Fa =  F;\n\tvec3d Fb = -F;\n\tvec3d Ma =  (za ^ F);\n\tvec3d Mb = -(zb ^ F);\n\n\tvector<double> fe(15, 0.0);\n\tfe[ 0] = Fa.x;\n\tfe[ 1] = Fa.y;\n\tfe[ 2] = Fa.z;\n\tfe[ 3] = Ma.x;\n\tfe[ 4] = Ma.y;\n\tfe[ 5] = Ma.z;\n\tfe[ 6] = Fb.x;\n\tfe[ 7] = Fb.y;\n\tfe[ 8] = Fb.z;\n\tfe[ 9] = Mb.x;\n\tfe[10] = Mb.y;\n\tfe[11] = Mb.z;\n\tfe[12] = c.x;\n\tfe[13] = c.y;\n\tfe[14] = c.z;\n\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\tR.Assemble(lm, fe);\n\n\tRBa.m_Fr -= vec3d(fe[0], fe[1], fe[2]);\n\tRBa.m_Mr -= vec3d(fe[3], fe[4], fe[5]);\n\tRBb.m_Fr -= vec3d(fe[6], fe[7], fe[8]);\n\tRBb.m_Mr -= vec3d(fe[9], fe[10], fe[11]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alpha;\n\n\t// body A\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n\tvec3d ra = rat * alpha + rap * (1 - alpha);\n\n\tvec3d zat = RBa.GetRotation() * m_qa0;\n\tvec3d zap = RBa.m_qp * m_qa0;\n\tvec3d za = zat * alpha + zap * (1 - alpha);\n\n\t// body b\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = rbt * alpha + rbp * (1 - alpha);\n\n\tvec3d zbt = RBb.GetRotation() * m_qb0;\n\tvec3d zbp = RBb.m_qp * m_qb0;\n\tvec3d zb = zbt * alpha + zbp * (1 - alpha);\n\n\tFEElementMatrix ke;\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tke.resize(12, 12);\n\t\tke.zero();\n\n\t\t// constraint\n\t\tvec3d c = rb + zb - ra - za;\n\n\t\t// forces\n\t\tvec3d F = m_L + c * m_eps;\n\t\tmat3da Fhat(F);\n\n\t\tmat3da zahat(za);\n\t\tmat3da zathat(zat);\n\n\t\tmat3da zbhat(zb);\n\t\tmat3da zbthat(zbt);\n\n\t\tmat3dd I(1.0);\n\n\t\tke.set(0, 0,      I); ke.set(0, 3,       -zathat); ke.set(0, 6,     -I); ke.set(0, 9,        zbthat);\n\t\tke.set(3, 0,  zahat); ke.set(3, 3, -zahat*zathat); ke.set(3, 6, -zahat); ke.set(3, 9,  zahat*zbthat);\n\t\tke.set(6, 0,     -I); ke.set(6, 3,        zathat); ke.set(6, 6,      I); ke.set(6, 9,       -zbthat);\n\t\tke.set(9, 0, -zbhat); ke.set(9, 3,  zbhat*zathat); ke.set(9, 6,  zbhat); ke.set(9, 9, -zbhat*zbthat);\n\n\t\t// scale by penalty factor\n\t\tke *= m_eps;\n\n\t\tke.add(3, 3, -Fhat*zathat);\n\t\tke.add(9, 9,  Fhat*zbthat);\n\n\t\tke *= alpha;\n\t}\n\telse\n\t{\n\t\tke.resize(15, 15);\n\t\tke.zero();\n\n\t\tmat3dd I(1.0);\n\t\tmat3da yaT(za);\n\t\tmat3da ybT(zb);\n\n\t\tmat3da Fhat(m_F);\n\n\t\tke.add_symm(0, 12,   I);\n\t\tke.add_symm(3, 12,  yaT);\n\t\tke.add_symm(6, 12,  -I);\n\t\tke.add_symm(9, 12, -ybT);\n\n\t\tke.add(3, 3,  Fhat*yaT);\n\t\tke.add(9, 9, -Fhat*ybT);\n\t}\n\n\t// unpack LM\n\tvector<int> lm;\n\tUnpackLM(lm);\n\tke.SetIndices(lm);\n\n\t// assemle into global stiffness matrix\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidJoint::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon == FECore::PENALTY_METHOD) return true;\n\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n\n\tquatd Qa = RBa.GetRotation();\n\tquatd Qb = RBb.GetRotation();\n\n\tvec3d ra = RBa.m_rt;\n\tvec3d rb = RBb.m_rt;\n\n\tvec3d qa = Qa*m_qa0;\n\tvec3d qb = Qb*m_qb0;\n\n\tvec3d c = rb + qb - ra - qa;\n\n\t// For Lagrange multipliers we just report the values\n\t// of the LM and the constraint\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfeLog(\"\\n=== rigid joint # %d:\\n\", m_nID);\n\t\tfeLog(\"\\tLagrange m. : %15.7lg, %15.7lg, %15.7lg\\n\", m_F.x, m_F.y, m_F.z);\n\t\tfeLog(\"\\tconstraint  : %15.7lg, %15.7lg, %15.7lg\\n\", c.x, c.y, c.z);\n\t\treturn true;\n\t}\n\n\t// augmented Lagrangian\n\tbool bconv = true;\n\n\tdouble normF0 = sqrt(m_L*m_L);\n\n\t// calculate trial multiplier\n\tvec3d Lm = m_L + c*m_eps;\n\n\tdouble normF1 = sqrt(Lm*Lm);\n\n\t// check convergence of constraints\n\tfeLog(\" rigid joint # %d\\n\", m_nID);\n\tfeLog(\"                  CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n\tfeLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap   : %15le       ***\\n\", c.norm());\n\t\t\n\tif (pctn >= m_atol)\n\t{\n\t\tbconv = false;\n\n\t\t// update multiplier\n\t\tm_L = m_L + c*m_eps;\n\t\n\t\t// update force\n\t\tm_F = m_L + c*m_eps;\n\t}\n\t\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n\tar & m_nID;\n\tar & m_q0 & m_qa0 & m_qb0;\n\tar & m_F & m_Fp & m_L & m_eps & m_atol & m_laugon;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidJoint::Update()\n{\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tFERigidBody& RBa = *m_rbA;\n\t\tFERigidBody& RBb = *m_rbB;\n\n\t\tvec3d ra = RBa.m_rt;\n\t\tvec3d rb = RBb.m_rt;\n\n\t\tvec3d qa = m_qa0;\n\t\tRBa.GetRotation().RotateVector(qa);\n\n\t\tvec3d qb = m_qb0;\n\t\tRBb.GetRotation().RotateVector(qb);\n\n\t\tvec3d c = ra + qa - rb - qb;\n\t\tm_F = m_L + c*m_eps;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidJoint::Reset()\n{\n\tm_F = vec3d(0,0,0);\n\tm_Fp = vec3d(0,0,0);\n\tm_L = vec3d(0,0,0);\n\n\tm_qa0 = m_q0 - m_rbA->m_r0;\n\tm_qb0 = m_q0 - m_rbB->m_r0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidJoint::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.reserve(15);\n\tlm.push_back(m_rbA->m_LM[0]);\n\tlm.push_back(m_rbA->m_LM[1]);\n\tlm.push_back(m_rbA->m_LM[2]);\n\tlm.push_back(m_rbA->m_LM[3]);\n\tlm.push_back(m_rbA->m_LM[4]);\n\tlm.push_back(m_rbA->m_LM[5]);\n\n\t// add the dofs of rigid body B\n\tlm.push_back(m_rbB->m_LM[0]);\n\tlm.push_back(m_rbB->m_LM[1]);\n\tlm.push_back(m_rbB->m_LM[2]);\n\tlm.push_back(m_rbB->m_LM[3]);\n\tlm.push_back(m_rbB->m_LM[4]);\n\tlm.push_back(m_rbB->m_LM[5]);\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tlm.push_back(m_LM[0]);\n\t\tlm.push_back(m_LM[1]);\n\t\tlm.push_back(m_LM[2]);\n\t}\n}\n\nvoid FERigidJoint::PrepStep()\n{\n\tm_Fp = m_F;\n}\n\nvoid FERigidJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Fp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\t\tm_F.y = m_Fp.y + Ui[m_LM[1]] + ui[m_LM[1]];\n\t\tm_F.z = m_Fp.z + Ui[m_LM[2]] + ui[m_LM[2]];\n\t}\n}\n\n\nvoid FERigidJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tUi[m_LM[0]] += ui[m_LM[0]];\n\t\tUi[m_LM[1]] += ui[m_LM[1]];\n\t\tUi[m_LM[2]] += ui[m_LM[2]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidJoint class implements a rigid joint. The rigid joint allows the\n//! user to connect two rigid bodies at a point in space.\n\nclass FERigidJoint : public FERigidConnector\n{\npublic:\n\t//! constructor\n\tFERigidJoint(FEModel* pfem);\n\n\t//! destructor\n\t~FERigidJoint();\n\n\t//! initialization\n\tbool Init() override;\n\n\t// allocate equations\n\tint InitEquations(int neq) override;\n\n\t//! calculates the joint forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculates the joint stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentation\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! update state\n\tvoid Update() override;\n\n\t//! Reset data\n\tvoid Reset() override;\n\nprotected:\n\tvoid UnpackLM(vector<int>& lm);\n\n\t// Build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\npublic:\n\tvec3d\tm_q0;\t\t//! initial position of joint\n\tvec3d\tm_qa0;\t//! initial relative position vector of joint w.r.t. A\n\tvec3d\tm_qb0;\t//! initial relative position vector of joint w.r.t. B\n\n\tvec3d\tm_F, m_Fp;\t//!< constraining force\n\tvec3d\tm_L;\t\t//!< Lagrange multiplier\n\tdouble\tm_eps;\t\t//!< penalty factor\n\tdouble\tm_atol;\t\t//!< augmented Lagrangian tolerance\n\tint\t\tm_laugon;\t//!< enforcement method\n\nprotected:\n\tint\t\tm_nID;\t//!< ID of rigid joint\n\n\tvector<int>\t\tm_LM;\t// Lagrange multiplier equation numbers\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidLock.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidLock.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidLock, FERigidConnector);\n\tADD_PARAMETER(m_laugon  , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol    , \"tolerance\"     );\n\tADD_PARAMETER(m_gtol    , \"gaptol\"        );\n\tADD_PARAMETER(m_qtol    , \"angtol\"        );\n\tADD_PARAMETER(m_eps     , \"force_penalty\" );\n\tADD_PARAMETER(m_ups     , \"moment_penalty\");\n\tADD_PARAMETER(m_q0      , \"joint_origin\"  );\n\tADD_PARAMETER(m_e0[0]   , \"first_axis\"    );\n\tADD_PARAMETER(m_e0[1]   , \"second_axis\"   );\n\tADD_PARAMETER(m_naugmin , \"minaug\"        );\n\tADD_PARAMETER(m_naugmax , \"maxaug\"        );\n\tADD_PARAMETER(m_bautopen, \"auto_penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidLock::FERigidLock(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_eps = 1.0;\n    m_ups = 1.0;\n\n    m_nID = m_ncount++;\n\tm_laugon = FECore::AUGLAG_METHOD; // for backward compatibility\n    m_atol = 0;\n    m_gtol = 0;\n    m_qtol = 0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n\tm_bautopen = false;\n    m_eps = m_ups = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidLock::Init()\n{\n    // initialize joint basis\n    m_e0[0].unit();\n    m_e0[2] = m_e0[0] ^ m_e0[1]; m_e0[2].unit();\n    m_e0[1] = m_e0[2] ^ m_e0[0]; m_e0[1].unit();\n    \n    // reset force\n    m_F = vec3d(0,0,0); m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0); m_U = vec3d(0,0,0);\n\tm_U1 = m_U2 = vec3d(0, 0, 0);\n\n\tm_Fp = vec3d(0, 0, 0);\n\tm_U1p = m_U2p = vec3d(0, 0, 0);\n\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n    m_qa0 = m_q0 - m_rbA->m_r0;\n    m_qb0 = m_q0 - m_rbB->m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n    \n    return true;\n}\n\n// allocate equations\nint FERigidLock::InitEquations(int neq)\n{\n\tconst int LMeq = 9;\n\tm_LM.resize(LMeq, -1);\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i=0; i< LMeq; ++i) m_LM[i] = neq + i;\n\t\treturn LMeq;\n\t}\n\telse return 0;\n}\n\n// Build the matrix profile\nvoid FERigidLock::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\nvoid FERigidLock::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.resize(21);\n\tlm[0] = m_rbA->m_LM[0];\n\tlm[1] = m_rbA->m_LM[1];\n\tlm[2] = m_rbA->m_LM[2];\n\tlm[3] = m_rbA->m_LM[3];\n\tlm[4] = m_rbA->m_LM[4];\n\tlm[5] = m_rbA->m_LM[5];\n\n\t// add the dofs of rigid body B\n\tlm[ 6] = m_rbB->m_LM[0];\n\tlm[ 7] = m_rbB->m_LM[1];\n\tlm[ 8] = m_rbB->m_LM[2];\n\tlm[ 9] = m_rbB->m_LM[3];\n\tlm[10] = m_rbB->m_LM[4];\n\tlm[11] = m_rbB->m_LM[5];\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < m_LM.size(); ++i) lm[12 + i] = m_LM[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidLock::Serialize(DumpStream& ar)\n{\n    FERigidConnector::Serialize(ar);\n    ar & m_qa0 & m_qb0;\n    ar & m_L & m_U & m_U1 & m_U2;\n    ar & m_e0;\n    ar & m_ea0;\n    ar & m_eb0;\n\tar & m_LM;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidLock::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    double alpha = tp.alphaf;\n    \n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    // incremental compound rotation of B w.r.t. A\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tvec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2])) / 2;\n\n\t\tvec3d c = rb + zb - ra - za;\n\t\tif (m_laugon != FECore::LAGMULT_METHOD) m_F = m_L + c * m_eps;\n\n\t\tvec3d ksi = vth;\n\t\tif (m_laugon != FECore::LAGMULT_METHOD) m_M = m_U + ksi * m_ups;\n\n\t\tfa[0] = m_F.x;\n\t\tfa[1] = m_F.y;\n\t\tfa[2] = m_F.z;\n\n\t\tfa[3] = za.y * m_F.z - za.z * m_F.y + m_M.x;\n\t\tfa[4] = za.z * m_F.x - za.x * m_F.z + m_M.y;\n\t\tfa[5] = za.x * m_F.y - za.y * m_F.x + m_M.z;\n\n\t\tfb[0] = -m_F.x;\n\t\tfb[1] = -m_F.y;\n\t\tfb[2] = -m_F.z;\n\n\t\tfb[3] = -zb.y * m_F.z + zb.z * m_F.y - m_M.x;\n\t\tfb[4] = -zb.z * m_F.x + zb.x * m_F.z - m_M.y;\n\t\tfb[5] = -zb.x * m_F.y + zb.y * m_F.x - m_M.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\t}\n\telse\n\t{\n\t\tvec3d Ma = (za ^ m_F) + (ea[0] ^ m_U1) + (ea[1] ^ m_U2);\n\t\tvec3d Mb = (zb ^ m_F) + (eb[0] ^ m_U1) + (eb[1] ^ m_U2);\n\n\t\tfa[0] = -m_F.x;\n\t\tfa[1] = -m_F.y;\n\t\tfa[2] = -m_F.z;\n\t\tfa[3] = -Ma.x;\n\t\tfa[4] = -Ma.y;\n\t\tfa[5] = -Ma.z;\n\n\t\tfb[0] =  m_F.x;\n\t\tfb[1] =  m_F.y;\n\t\tfb[2] =  m_F.z;\n\t\tfb[3] =  Mb.x;\n\t\tfb[4] =  Mb.y;\n\t\tfb[5] =  Mb.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\n\t\t// translational constraint\n\t\tvec3d c = rb + zb - ra - za;\n\t\tR[m_LM[0]] += c.x;\n\t\tR[m_LM[1]] += c.y;\n\t\tR[m_LM[2]] += c.z;\n\n\t\t// rotational constraint\n\t\tvec3d ksi1 = eb[0] - ea[0];\n\t\tvec3d ksi2 = eb[1] - ea[1];\n\t\tR[m_LM[3]] += ksi1.x;\n\t\tR[m_LM[4]] += ksi1.y;\n\t\tR[m_LM[5]] += ksi1.z;\n\t\tR[m_LM[6]] += ksi2.x;\n\t\tR[m_LM[7]] += ksi2.y;\n\t\tR[m_LM[8]] += ksi2.z;\n\t}\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidLock::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    double alpha = tp.alphaf;\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n    vec3d ra = rat*alpha + rap*(1-alpha);\n\tvec3d zat = Qat*m_qa0;\n    vec3d zap = Qap*m_qa0;\n    vec3d za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = rbt*alpha + rbp*(1-alpha);\n\tvec3d zbt = Qbt*m_qb0;\n    vec3d zbp = Qbp*m_qb0;\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n    \n    mat3d eahat[3], ebhat[3], eathat[3], ebthat[3];\n    for (int j=0; j<3; ++j) {\n        eahat[j] = skew(ea[j]);\n        ebhat[j] = skew(eb[j]);\n        eathat[j] = skew(eat[j]);\n        ebthat[j] = skew(ebt[j]);\n    }\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tvector<int> LM(12);\n\t\tFEElementMatrix ke; ke.resize(12, 12);\n\t\tke.zero();\n\n\t\tvec3d c = rb + zb - ra - za;\n\t\tm_F = m_L + c * m_eps;\n\n\t\t// incremental compound rotation of B w.r.t. A\n\t\tvec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2])) / 2;\n\t\tvec3d ksi = vth;\n\t\tm_M = m_U + ksi * m_ups;\n\n\t\tmat3da Fhat(m_F);\n\n\t\tmat3dd I(1);\n\t\tmat3d Wba = (ebhat[0] * eathat[0] + ebhat[1] * eathat[1] + ebhat[2] * eathat[2]) / 2;\n\t\tmat3d Wab = (eahat[0] * ebthat[0] + eahat[1] * ebthat[1] + eahat[2] * ebthat[2]) / 2;\n\n\t\t// row 1\n\t\tke.set(0, 0, I * (-m_eps));\n\t\tke.set(0, 3, zathat * (m_eps));\n\t\tke.set(0, 6, I * (m_eps));\n\t\tke.set(0, 9, zbthat * (-m_eps));\n\n\t\t// row 2\n\t\tke.set(3, 0, zahat * (-m_eps));\n\t\tke.set(3, 3, zahat * zathat * (m_eps)+Fhat * zathat + Wba * m_ups);\n\t\tke.set(3, 6, zahat * (m_eps));\n\t\tke.set(3, 9, zahat * zbthat * (-m_eps) + Wab * (-m_ups));\n\n\t\t// row 3\n\t\tke.set(6, 0, I * (m_eps));\n\t\tke.set(6, 3, zathat * (-m_eps));\n\t\tke.set(6, 6, I * (-m_eps));\n\t\tke.set(6, 9, zbthat * (m_eps));\n\n\t\t// row 4\n\t\tke.set(9, 0, zbhat * (m_eps));\n\t\tke.set(9, 3, zbhat * zathat * (-m_eps) + Wba * (-m_ups));\n\t\tke.set(9, 6, zbhat * (-m_eps));\n\t\tke.set(9, 9, zbhat * zbthat * (m_eps)-Fhat * zbthat + Wab * m_ups);\n\n\t\tke *= -alpha;\n\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\tFEElementMatrix ke; ke.resize(21, 21);\n\t\tke.zero();\n\n\t\tmat3dd I(1.0);\n\n\t\t// translation constraint\n\t\tmat3da Fhat(m_F);\n\t\tke.sub(3, 3, -Fhat * zahat);\n\t\tke.sub(9, 9,  Fhat * zbhat);\n\t\tke.sub(0, 12, -I);\n\t\tke.sub(3, 12, -zahat);\n\t\tke.sub(6, 12,  I);\n\t\tke.sub(9, 12,  zbhat);\n\t\tke.sub(12, 0, -I);\n\t\tke.sub(12, 3, zahat);\n\t\tke.sub(12, 6,  I);\n\t\tke.sub(12, 9, -zbhat);\n\n\t\t// rotational constraint 1\n\t\tmat3da U1hat(m_U1);\n\t\tke.sub(3, 3, -U1hat*eahat[0]);\n\t\tke.sub(9, 9,  U1hat*ebhat[0]);\n\t\tke.sub(3, 15, -eahat[0]);\n\t\tke.sub(9, 15,  ebhat[0]);\n\t\tke.sub(15, 3,  eahat[0]);\n\t\tke.sub(15, 9, -ebhat[0]);\n\n\t\t// rotational constraint 2\n\t\tmat3da U2hat(m_U2);\n\t\tke.sub(3, 3, -U2hat*eahat[1]);\n\t\tke.sub(9, 9,  U2hat*ebhat[1]);\n\t\tke.sub(3, 18, -eahat[1]);\n\t\tke.sub(9, 18,  ebhat[1]);\n\t\tke.sub(18, 3,  eahat[1]);\n\t\tke.sub(18, 9, -ebhat[1]);\n\n\t\tvector<int> LM;\n\t\tUnpackLM(LM);\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidLock::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d ra, rb, qa, qb, c, ksi, Lm;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    double normF0, normF1;\n    vec3d Um;\n    double normM0, normM1;\n    bool bconv = true;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    double alpha = tp.alphaf;\n    \n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    // incremental compound rotation of B w.r.t. A\n    vec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2]))/2;\n    \n    c = rb + zb - ra - za;\n    \n    normF0 = sqrt(m_L*m_L);\n    \n    // calculate trial multiplier\n    Lm = m_L + c*m_eps;\n    \n    normF1 = sqrt(Lm*Lm);\n    \n    ksi = vth;\n    \n    normM0 = sqrt(m_U*m_U);\n    \n    // calculate trial multiplier\n    Um = m_U + ksi*m_ups;\n    \n    normM1 = sqrt(Um*Um);\n    \n    // check convergence of constraints\n    feLog(\" rigid connector # %d (lock)\\n\", m_nID+1);\n    feLog(\"                  CURRENT        REQUIRED\\n\");\n    double pctn = 0;\n    double gap = c.norm();\n    double qap = ksi.norm();\n    if (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n    if (m_atol) feLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n    else        feLog(\"    force : %15le        ***\\n\", pctn);\n    if (m_gtol) feLog(\"    gap   : %15le %15le\\n\", gap, m_gtol);\n    else        feLog(\"    gap   : %15le        ***\\n\", gap);\n    double qctn = 0;\n    if (fabs(normM1) > 1e-10) qctn = fabs((normM1 - normM0)/normM1);\n    if (m_atol) feLog(\"    moment: %15le %15le\\n\", qctn, m_atol);\n    else        feLog(\"    moment: %15le        ***\\n\", qctn);\n    if (m_qtol) feLog(\"    angle : %15le %15le\\n\", qap, m_qtol);\n    else        feLog(\"    angle : %15le        ***\\n\", qap);\n    \n    if (m_atol && ((pctn >= m_atol) || (qctn >= m_atol))) bconv = false;\n    if (m_gtol && (gap >= m_gtol)) bconv = false;\n    if (m_qtol && (qap >= m_qtol)) bconv = false;\n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    if (!bconv)\n    {\n        // update multipliers\n        m_L = Lm;\n        m_U = Um;\n    }\n    \n\tif (m_bautopen)\n\t{\n\t\t// auto-penalty update (works only with gaptol and angtol)\n\t\tif (m_gtol && (gap > m_gtol)) {\n\t\t\tm_eps = fmax(gap / m_gtol, 100)*m_eps;\n\t\t\tfeLog(\"    force_penalty :         %15le\\n\", m_eps);\n\t\t}\n\t\tif (m_qtol && (qap > m_qtol)) {\n\t\t\tm_ups = fmax(qap / m_qtol, 100)*m_ups;\n\t\t\tfeLog(\"    moment_penalty :        %15le\\n\", m_ups);\n\t\t}\n\t}\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidLock::Update()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) return;\n\n    vec3d ra, rb;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n    \n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    // incremental compound rotation of B w.r.t. A\n    vec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2]))/2;\n    \n    vec3d c = rb + zb - ra - za;\n    m_F = m_L + c*m_eps;\n    \n    vec3d ksi = vth;\n    m_M = m_U + ksi*m_ups;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidLock::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n    m_U = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_q0 - RBa.m_r0;\n    m_qb0 = m_q0 - RBb.m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidLock::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n\n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative translation onto local basis\n    vec3d y(x*ea[0], x*ea[1], x*ea[2]);\n    \n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidLock::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    if (global) return q;\n    \n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative rotation onto local basis\n    vec3d y(q*ea[0], q*ea[1], q*ea[2]);\n    \n    return y;\n}\n\nvoid FERigidLock::PrepStep()\n{\n\tm_Fp  = m_F;\n\tm_U1p = m_U1;\n\tm_U2p = m_U2;\n}\n\nvoid FERigidLock::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Fp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\t\tm_F.y = m_Fp.y + Ui[m_LM[1]] + ui[m_LM[1]];\n\t\tm_F.z = m_Fp.z + Ui[m_LM[2]] + ui[m_LM[2]];\n\n\t\tm_U1.x = m_U1p.x + Ui[m_LM[3]] + ui[m_LM[3]];\n\t\tm_U1.y = m_U1p.y + Ui[m_LM[4]] + ui[m_LM[4]];\n\t\tm_U1.z = m_U1p.z + Ui[m_LM[5]] + ui[m_LM[5]];\n\n\t\tm_U2.x = m_U2p.x + Ui[m_LM[6]] + ui[m_LM[6]];\n\t\tm_U2.y = m_U2p.y + Ui[m_LM[7]] + ui[m_LM[7]];\n\t\tm_U2.z = m_U2p.z + Ui[m_LM[8]] + ui[m_LM[8]];\n\n\t\tm_M = m_U1 + m_U2;\n\t}\n}\n\nvoid FERigidLock::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int n : m_LM) Ui[n] += ui[n];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidLock.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidLock class implements a locking joint. This rigid joint\n//! allows the user to connect two rigid bodies at a point in space\n//! and prevents any relative motion.\n\nclass FERigidLock : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidLock(FEModel* pfem);\n    \n    //! destructor\n    virtual ~FERigidLock() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global = false) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global = false) override;\n\nprivate: // lag. mult. methods\n\tint InitEquations(int neq) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid UnpackLM(vector<int>& lm);\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid PrepStep() override;\n\npublic: // parameters\n\tint\t\tm_laugon; //!< enforcement method\n    double\tm_atol;\t//! augmented Lagrangian tolerance\n    double  m_gtol; //! augmented Lagrangian gap tolerance\n    double  m_qtol; //! augmented Lagrangian angular gap tolerance\n    int     m_naugmin;  //! minimum number of augmentations\n    int     m_naugmax;  //! maximum number of augmentations\n    vec3d\tm_q0;\t//! initial position of joint\n    double\tm_eps;\t//! penalty factor for constraining force\n    double\tm_ups;\t//! penalty factor for constraining moment\n\tbool\tm_bautopen;\t//!< auto-penalty for gap and ang tolerance\n\nprotected:\n    vec3d\tm_qa0;\t//! initial relative position vector of joint w.r.t. A\n    vec3d\tm_qb0;\t//! initial relative position vector of joint w.r.t. B\n    \n    vec3d\tm_e0[3];\t//! initial joint basis\n    vec3d\tm_ea0[3];\t//! initial joint basis w.r.t. A\n    vec3d\tm_eb0[3];\t//! initial joint basis w.r.t. B\n    \n    vec3d\tm_L;\t//! Lagrange multiplier for constraining force\n    vec3d\tm_U;\t//! Lagrange multiplier for constraining moment\n\n\tvec3d m_U1, m_U2;\n\n\tvec3d m_Fp, m_U1p, m_U2p;\n\n\tvector<int>\t\tm_LM;\t// Lagrange multiplier equation numbers\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidMaterial.h\"\n#include \"FERigidBody.h\"\n#include \"FEMechModel.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FERigidMaterial, FESolidMaterial)\n\tADD_PARAMETER(m_E      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E\"         )->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_v      , FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\")->setLongName(\"Poisson's ratio\");\n\tADD_PARAMETER(m_com    , \"override_com\")->SetFlags(FE_PARAM_WATCH);\n\tADD_PARAMETER(m_rc    , \"center_of_mass\")->SetWatchVariable(&m_com);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// constructor\nFERigidMaterial::FERigidMaterial(FEModel* pfem) : FESolidMaterial(pfem)\n{\n\tm_E = 1;\n\tm_v = 0;\n\tm_rc = vec3d(0, 0, 0);\n\n\tm_com = false;\t// calculate COM automatically\n\tm_pmid = -1;\n\tm_nRB = -1;\n\n\tm_binit = false;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize rigid material data\nbool FERigidMaterial::Init()\n{\n\tif (FESolidMaterial::Init() == false) return false;\n\n\tif (m_binit == false)\n\t{\n\t\t// get this rigid body's ID\n\t\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\t\tFERigidBody& rb = *fem.GetRigidBody(GetRigidBodyID());\n\n\t\tif (m_pmid  > -1)\n\t\t{\n\t\t\tFERigidMaterial* ppm = dynamic_cast<FERigidMaterial*>(GetFEModel()->GetMaterial(m_pmid-1));\n\t\t\tif (ppm == 0) {\n\t\t\t\tfeLogError(\"parent of rigid material %s is not a rigid material\\n\", GetName().c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tFERigidBody& prb = *fem.GetRigidBody(ppm->GetRigidBodyID());\n\t\t\trb.m_prb = &prb;\n\n\t\t\t// mark all degrees of freedom as prescribed\n\t\t\trb.m_BC[0] = DOF_PRESCRIBED;\n\t\t\trb.m_BC[1] = DOF_PRESCRIBED;\n\t\t\trb.m_BC[2] = DOF_PRESCRIBED;\n\t\t\trb.m_BC[3] = DOF_PRESCRIBED;\n\t\t\trb.m_BC[4] = DOF_PRESCRIBED;\n\t\t\trb.m_BC[5] = DOF_PRESCRIBED;\n\t\t}\n\n\t\tm_binit = true;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize data to or from the dump file\nvoid FERigidMaterial::Serialize(DumpStream &ar)\n{\n\tFESolidMaterial::Serialize(ar);\n\tar & m_com;\n\tar & m_nRB;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Rigd body material data\n\n//! Since rigid elements are skipped during the stiffness and residual calculations\n//! we don't implement the Stress and Tangent functions\n//! \\todo make the m_rc a parameter\n//! \\todo Can I remove the m_bc variable?\n\nclass FERigidMaterial : public FESolidMaterial\n{\npublic:\n\tFERigidMaterial(FEModel* pfem);\n\npublic:\n\tdouble\tm_E;\t\t//!< Young's modulus\n\tdouble\tm_v;\t\t//!< Poisson's ratio\n\tvec3d\tm_rc;\t\t//!< center of mass\n\npublic:\n\tint\t\tm_pmid;\t//!< parent material ID\n\tbool\tm_com;\t//!< center of mass input flag\n\tint\t\tm_nRB;\t//!< rigid body ID (TODO: rigid materials can be assigned to mulitple rigid bodies, so does it make sense to store this?)\n\npublic:\n\t// inherited from FEMaterial\n\tbool IsRigid() const override { return true; }\n\npublic:\n\t//! get the ID of the rigid body this material is assigned to (-1 if not)\n\tint GetRigidBodyID() { return m_nRB; }\n\n\t//! Set the rigid body ID this material is assigned to\n\tvoid SetRigidBodyID(int rid) { m_nRB = rid; }\n\npublic:\n\t//! Create a rigid material point\n\tFEMaterialPointData* CreateMaterialPointData() override { return new FEElasticMaterialPoint(); }\n\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override { return mat3ds(); }\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override { return tens4ds(); }\n\n\t//! data initialization\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// declare a parameter list\n\tDECLARE_FECORE_CLASS();\n\nprivate:\n\tbool\tm_binit;\t//!< flag for first initialization\n};\n"
  },
  {
    "path": "FEBioMech/FERigidNodeSet.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidNodeSet.h\"\n#include <FECore/FEModel.h>\n#include \"FERigidMaterial.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidBody.h\"\n\nBEGIN_FECORE_CLASS(FERigidNodeSet, FENodalBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\");\n\tADD_PARAMETER(m_bshellBC, \"clamp_shells\")->SetFlags(FEParamFlag::FE_PARAM_HIDDEN);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidNodeSet::FERigidNodeSet(FEModel* pfem) : FENodalBC(pfem)\n{\n\tm_rigidMat = -1;\n\tm_bshellBC = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidNodeSet::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidNodeSet::Init()\n{\n\t// Make sure the rigid material exists\n\tFEModel& fem = *GetFEModel();\n\tif (m_rigidMat <= 0) return false;\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\tif (pm == 0) return false;\n\tif (pm->GetRigidBodyID() < 0) return false;\n\n\treturn FENodalBC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidNodeSet::Activate()\n{\n\tFENodalBC::Activate();\n\n\t// get the rigid body's ID\n\tFEMechModel& fem = dynamic_cast<FEMechModel&>(*GetFEModel());\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\tint rid = pm->GetRigidBodyID(); assert(rid >= 0);\n\n\tFERigidBody& rb = *fem.GetRigidBody(rid);\n\n\t// assign the rigid body ID\n\tFEStepComponent::Activate();\n\tFEMesh& mesh = fem.GetMesh();\n\tFENodeSet& nset = *GetNodeSet();\n\tfor (size_t i=0; i<nset.Size(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(nset[i]);\n\t\tif (m_bshellBC)\n\t\t{\n\t\t\tif (node.HasFlags(FENode::SHELL)) \n\t\t\t\tnode.SetFlags(FENode::RIGID_CLAMP);\n\t\t}\n\t\tnode.m_rid = rid;\n\n\t\t// we need to take the current position of the node\n\t\t// and transform it into the reference configuration of the rigid body.\n\t\tvec3d dr = node.m_rt - rb.m_rt;\n\t\tquatd q = rb.GetRotation().Inverse();\n\t\tq.RotateVector(dr);\n\t\tnode.m_ra = rb.m_r0 + dr;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidNodeSet::SetRigidMaterialID(int rid)\n{\n\tm_rigidMat = rid;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidNodeSet::Deactivate()\n{\n\tFENodalBC::Deactivate();\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tFENodeSet& nset = *GetNodeSet();\n\tfor (size_t i=0; i<nset.Size(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(nset[i]);\n\t\tif (m_bshellBC)\n\t\t{\n\t\t\tif (node.HasFlags(FENode::SHELL))\n\t\t\t\tnode.UnsetFlags(FENode::RIGID_CLAMP);\n\t\t}\n\t\tnode.m_rid = -1;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidNodeSet::Serialize(DumpStream& ar)\n{\n\tFENodalBC::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_rigidMat & m_bshellBC;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidNodeSet.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENodalBC.h>\n#include \"febiomech_api.h\"\n\nclass FENodeSet;\n\n//-----------------------------------------------------------------------------\n//! rigid node set\nclass FEBIOMECH_API FERigidNodeSet : public FENodalBC\n{\npublic:\n\tFERigidNodeSet(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tvoid Activate() override;\n\tvoid Deactivate() override;\n\n\tvoid SetRigidMaterialID(int rid);\n\n\t// copy data from another class\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate: // parameters\n\tint\t\t\tm_rigidMat;\t\t//!< rigid body's material\n\tbool\t\tm_bshellBC;\t\t//!< flag defining how shells are attached (0=hinged, 1=clamped)\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidPlanarJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidPlanarJoint.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/ad.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidPlanarJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon, \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol, \"tolerance\"     );\n\tADD_PARAMETER(m_gtol, \"gaptol\"        );\n\tADD_PARAMETER(m_qtol, \"angtol\"        );\n\tADD_PARAMETER(m_eps , \"force_penalty\" );\n\tADD_PARAMETER(m_ups , \"moment_penalty\");\n\tADD_PARAMETER(m_q0  , \"joint_origin\"  );\n\tADD_PARAMETER(m_e0[0], \"rotation_axis\" );\n\tADD_PARAMETER(m_e0[1], \"translation_axis_1\");\n\tADD_PARAMETER(m_naugmin, \"minaug\"        );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"        );\n\tADD_PARAMETER(m_bqx , \"prescribed_rotation\");\n\tADD_PARAMETER(m_qpx , \"rotation\"      );\n\tADD_PARAMETER(m_bdy , \"prescribed_translation_1\");\n\tADD_PARAMETER(m_dpy , \"translation_1\" );\n\tADD_PARAMETER(m_bdz , \"prescribed_translation_2\");\n\tADD_PARAMETER(m_dpz , \"translation_2\" );\n\tADD_PARAMETER(m_bautopen, \"auto_penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidPlanarJoint::FERigidPlanarJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n\n\tm_laugon = FECore::AUGLAG_METHOD; // for backward compatibility\n    m_atol = 0;\n    m_gtol = 0;\n    m_qtol = 0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n    m_qpx = 0;\n    m_dpy = 0;\n    m_dpz = 0;\n    m_bqx = false;\n    m_bdy = false;\n    m_bdz = false;\n\tm_bautopen = false;\n    m_eps = m_ups = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! initial position\nvec3d FERigidPlanarJoint::InitialPosition() const\n{\n    return m_q0;\n}\n\n//-----------------------------------------------------------------------------\n//! current position\nvec3d FERigidPlanarJoint::Position() const\n{\n    FERigidBody& RBa = *m_rbA;\n    vec3d qa = m_qa0;\n    RBa.GetRotation().RotateVector(qa);\n    return RBa.m_rt + qa;\n}\n\n//-----------------------------------------------------------------------------\n//! current orientation\nquatd FERigidPlanarJoint::Orientation() const\n{\n\tquatd Q0(vec3d(0, 0, 1), m_e0[0]);\n\tFERigidBody& RBa = *m_rbA;\n\treturn RBa.GetRotation()*Q0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidPlanarJoint::Init()\n{\n    // initialize joint basis\n    m_e0[0].unit();\n    m_e0[2] = m_e0[0] ^ m_e0[1]; m_e0[2].unit();\n    m_e0[1] = m_e0[2] ^ m_e0[0]; m_e0[1].unit();\n    \n    // reset force\n    m_F = vec3d(0,0,0); m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0); m_U = vec3d(0,0,0);\n\tm_Fp = m_Mp = vec3d(0, 0, 0);\n    \n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n    m_qa0 = m_q0 - m_rbA->m_r0;\n    m_qb0 = m_q0 - m_rbB->m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPlanarJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n\tar & m_qa0 & m_qb0;\n\tar & m_L & m_U;\n\tar & m_e0;\n\tar & m_ea0;\n\tar & m_eb0;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidPlanarJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    double alpha = tp.alphaf;\n    \n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tmat3ds P;\n\t\tvec3d p;\n\t\tif (m_bdy && m_bdz) {\n\t\t\tP = mat3dd(1);\n\t\t\tp = ea[1] * m_dpy + ea[2] * m_dpz;\n\t\t}\n\t\telse if (m_bdy) {\n\t\t\tP = mat3dd(1) - dyad(ea[2]);\n\t\t\tp = ea[1] * m_dpy;\n\t\t}\n\t\telse if (m_bdz) {\n\t\t\tP = mat3dd(1) - dyad(ea[1]);\n\t\t\tp = ea[2] * m_dpz;\n\t\t}\n\t\telse {\n\t\t\tP = dyad(ea[0]);\n\t\t\tp = vec3d(0, 0, 0);\n\t\t}\n\t\tvec3d c = P * (rb + zb - ra - za) - p;\n\t\tm_F = m_L + c * m_eps;\n\n\t\tvec3d ksi;\n\t\tif (m_bqx) {\n\t\t\tquatd q = (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp) * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd a(m_qpx, ea[0]);\n\t\t\tquatd r = a * q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tksi = r.GetVector() * r.GetAngle();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tksi = (ea[0] ^ eb[0]) / 2;\n\t\t}\n\t\tm_M = m_U + ksi * m_ups;\n\n\t\tfa[0] = m_F.x;\n\t\tfa[1] = m_F.y;\n\t\tfa[2] = m_F.z;\n\n\t\tfa[3] = za.y * m_F.z - za.z * m_F.y + m_M.x;\n\t\tfa[4] = za.z * m_F.x - za.x * m_F.z + m_M.y;\n\t\tfa[5] = za.x * m_F.y - za.y * m_F.x + m_M.z;\n\n\t\tfb[0] = -m_F.x;\n\t\tfb[1] = -m_F.y;\n\t\tfb[2] = -m_F.z;\n\n\t\tfb[3] = -zb.y * m_F.z + zb.z * m_F.y - m_M.x;\n\t\tfb[4] = -zb.z * m_F.x + zb.x * m_F.z - m_M.y;\n\t\tfb[5] = -zb.x * m_F.y + zb.y * m_F.x - m_M.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\t}\n\telse\n\t{\n\t\tvec3d d = rb + zb - ra - za;\n\t\tmat3dd I(1.0);\n\t\tmat3d P = dyad(ea[0]);\n\t\tmat3d PdT = I * (ea[0] * d) + (d & ea[0]);\n\n\t\tvec3d F = P*m_F;\n\t\tvec3d Ma = (za ^ F) - (ea[0] ^ (PdT*m_F)) + (ea[0]^m_M);\n\t\tvec3d Mb = (zb ^ F) + (eb[0] ^ m_M);\n\n\t\tfa[0] = -F.x;\n\t\tfa[1] = -F.y;\n\t\tfa[2] = -F.z;\n\n\t\tfa[3] = -Ma.x;\n\t\tfa[4] = -Ma.y;\n\t\tfa[5] = -Ma.z;\n\n\t\tfb[0] = F.x;\n\t\tfb[1] = F.y;\n\t\tfb[2] = F.z;\n\n\t\tfb[3] = Mb.x;\n\t\tfb[4] = Mb.y;\n\t\tfb[5] = Mb.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\n\t\t// translational constraint\n\t\tvec3d c = ea[0]*(d*ea[0]);\n\t\tR[m_LM[0]] += c.x;\n\t\tR[m_LM[1]] += c.y;\n\t\tR[m_LM[2]] += c.z;\n\n\t\t// rotational constraint\n\t\tvec3d ksi = eb[0] - ea[0];\n\t\tR[m_LM[3]] += ksi.x;\n\t\tR[m_LM[4]] += ksi.y;\n\t\tR[m_LM[5]] += ksi.z;\n\t}\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidPlanarJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    double alpha = tp.alphaf;\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n    vector<int> LM(12);\n\tFEElementMatrix ke; ke.resize(12, 12);\n    ke.zero();\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n\tvec3d ra = rat * alpha + rap * (1 - alpha);\n\tvec3d zat = Qat * m_qa0;\n\tvec3d zap = Qap * m_qa0;\n\tvec3d za = zat * alpha + zap * (1 - alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = rbt * alpha + rbp * (1 - alpha);\n\tvec3d zbt = Qbt * m_qb0;\n\tvec3d zbp = Qbp * m_qb0;\n\tvec3d zb = zbt * alpha + zbp * (1 - alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n    \n    mat3d eahat[3], ebhat[3], eathat[3], ebthat[3];\n    for (int j=0; j<3; ++j) {\n        eahat[j] = skew(ea[j]);\n        ebhat[j] = skew(eb[j]);\n        eathat[j] = skew(eat[j]);\n        ebthat[j] = skew(ebt[j]);\n    }\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tmat3ds P;\n\t\tvec3d p;\n\t\tmat3d Q, Wba, Wab;\n\t\tvec3d d = rb + zb - ra - za;\n\t\tif (m_bdy && m_bdz) {\n\t\t\tP = mat3dd(1);\n\t\t\tp = ea[1] * m_dpy + ea[2] * m_dpz;\n\t\t\tQ = mat3dd(0);\n\t\t}\n\t\telse if (m_bdy) {\n\t\t\tP = mat3dd(1) - dyad(ea[2]);\n\t\t\tp = ea[1] * m_dpy;\n\t\t\tQ = ((ea[2] & d) + mat3dd(1) * (ea[2] * d)) * eathat[2];\n\t\t}\n\t\telse if (m_bdz) {\n\t\t\tP = mat3dd(1) - dyad(ea[1]);\n\t\t\tp = ea[2] * m_dpz;\n\t\t\tQ = ((ea[1] & d) + mat3dd(1) * (ea[1] * d)) * eathat[1];\n\t\t}\n\t\telse {\n\t\t\tP = dyad(ea[0]);\n\t\t\tp = vec3d(0, 0, 0);\n\t\t\tQ = ((ea[0] & d) + mat3dd(1) * (ea[0] * d)) * eathat[0] * (-1);\n\t\t}\n\t\tvec3d c = P * d - p;\n\t\tm_F = m_L + c * m_eps;\n\n\t\tvec3d ksi;\n\t\tif (m_bqx) {\n\t\t\tquatd q = (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp) * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd a(m_qpx, ea[0]);\n\t\t\tquatd r = a * q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tksi = r.GetVector() * r.GetAngle();\n\t\t\tquatd qa = RBa.GetRotation() * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd qb = RBb.GetRotation() * (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp).Inverse();\n\t\t\tqa.MakeUnit();\n\t\t\tqb.MakeUnit();\n\t\t\tmat3d Qa = qa.RotationMatrix();\n\t\t\tmat3d Qb = qb.RotationMatrix();\n\t\t\tmat3d A = a.RotationMatrix();\n\t\t\tmat3d R = r.RotationMatrix();\n\t\t\tmat3dd I(1);\n\t\t\tWba = A * (I * Qa.trace() - Qa) / 2;\n\t\t\tWab = R * (I * Qb.trace() - Qb) / 2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tksi = (ea[0] ^ eb[0]) / 2;\n\t\t\tWba = (ebhat[0] * eathat[0]) / 2;\n\t\t\tWab = (eahat[0] * ebthat[0]) / 2;\n\t\t}\n\t\tm_M = m_U + ksi * m_ups;\n\n\t\tmat3da Fhat(m_F);\n\n\t\tmat3d K;\n\n\t\t// (1,1)\n\t\tK = P*(alpha*m_eps);\n\t\tke[0][0] = K[0][0]; ke[0][1] = K[0][1]; ke[0][2] = K[0][2];\n\t\tke[1][0] = K[1][0]; ke[1][1] = K[1][1]; ke[1][2] = K[1][2];\n\t\tke[2][0] = K[2][0]; ke[2][1] = K[2][1]; ke[2][2] = K[2][2];\n\n\t\t// (1,2)\n\t\tK = (P*zathat+Q)*(-m_eps*alpha);\n\t\tke[0][3] = K[0][0]; ke[0][4] = K[0][1]; ke[0][5] = K[0][2];\n\t\tke[1][3] = K[1][0]; ke[1][4] = K[1][1]; ke[1][5] = K[1][2];\n\t\tke[2][3] = K[2][0]; ke[2][4] = K[2][1]; ke[2][5] = K[2][2];\n\n\t\t// (1,3)\n\t\tK = P*(-alpha*m_eps);\n\t\tke[0][6] = K[0][0]; ke[0][7] = K[0][1]; ke[0][8] = K[0][2];\n\t\tke[1][6] = K[1][0]; ke[1][7] = K[1][1]; ke[1][8] = K[1][2];\n\t\tke[2][6] = K[2][0]; ke[2][7] = K[2][1]; ke[2][8] = K[2][2];\n\n\t\t// (1,4)\n\t\tK = P*zbthat*(alpha*m_eps);\n\t\tke[0][9] = K[0][0]; ke[0][10] = K[0][1]; ke[0][11] = K[0][2];\n\t\tke[1][9] = K[1][0]; ke[1][10] = K[1][1]; ke[1][11] = K[1][2];\n\t\tke[2][9] = K[2][0]; ke[2][10] = K[2][1]; ke[2][11] = K[2][2];\n\n\t\t// (2,1)\n\t\tK = zahat*P*(alpha*m_eps);\n\t\tke[3][0] = K[0][0]; ke[3][1] = K[0][1]; ke[3][2] = K[0][2];\n\t\tke[4][0] = K[1][0]; ke[4][1] = K[1][1]; ke[4][2] = K[1][2];\n\t\tke[5][0] = K[2][0]; ke[5][1] = K[2][1]; ke[5][2] = K[2][2];\n\n\t\t// (2,2)\n\t\tK = (zahat * (P * zathat + Q) * m_eps + Fhat * zathat + Wba * m_ups) * (-alpha);\n\t\tke[3][3] = K[0][0]; ke[3][4] = K[0][1]; ke[3][5] = K[0][2];\n\t\tke[4][3] = K[1][0]; ke[4][4] = K[1][1]; ke[4][5] = K[1][2];\n\t\tke[5][3] = K[2][0]; ke[5][4] = K[2][1]; ke[5][5] = K[2][2];\n\n\t\t// (2,3)\n\t\tK = zahat*P*(-alpha*m_eps);\n\t\tke[3][6] = K[0][0]; ke[3][7] = K[0][1]; ke[3][8] = K[0][2];\n\t\tke[4][6] = K[1][0]; ke[4][7] = K[1][1]; ke[4][8] = K[1][2];\n\t\tke[5][6] = K[2][0]; ke[5][7] = K[2][1]; ke[5][8] = K[2][2];\n\n\t\t// (2,4)\n\t\tK = (zahat*P*zbthat*m_eps + Wab*m_ups)*alpha;\n\t\tke[3][9] = K[0][0]; ke[3][10] = K[0][1]; ke[3][11] = K[0][2];\n\t\tke[4][9] = K[1][0]; ke[4][10] = K[1][1]; ke[4][11] = K[1][2];\n\t\tke[5][9] = K[2][0]; ke[5][10] = K[2][1]; ke[5][11] = K[2][2];\n\n\n\t\t// (3,1)\n\t\tK = P*(-alpha*m_eps);\n\t\tke[6][0] = K[0][0]; ke[6][1] = K[0][1]; ke[6][2] = K[0][2];\n\t\tke[7][0] = K[1][0]; ke[7][1] = K[1][1]; ke[7][2] = K[1][2];\n\t\tke[8][0] = K[2][0]; ke[8][1] = K[2][1]; ke[8][2] = K[2][2];\n\n\t\t// (3,2)\n\t\tK = (P*zathat+Q)*(m_eps*alpha);\n\t\tke[6][3] = K[0][0]; ke[6][4] = K[0][1]; ke[6][5] = K[0][2];\n\t\tke[7][3] = K[1][0]; ke[7][4] = K[1][1]; ke[7][5] = K[1][2];\n\t\tke[8][3] = K[2][0]; ke[8][4] = K[2][1]; ke[8][5] = K[2][2];\n\n\t\t// (3,3)\n\t\tK = P*(alpha*m_eps);\n\t\tke[6][6] = K[0][0]; ke[6][7] = K[0][1]; ke[6][8] = K[0][2];\n\t\tke[7][6] = K[1][0]; ke[7][7] = K[1][1]; ke[7][8] = K[1][2];\n\t\tke[8][6] = K[2][0]; ke[8][7] = K[2][1]; ke[8][8] = K[2][2];\n\n\t\t// (3,4)\n\t\tK = P*zbthat*(-alpha*m_eps);\n\t\tke[6][9] = K[0][0]; ke[6][10] = K[0][1]; ke[6][11] = K[0][2];\n\t\tke[7][9] = K[1][0]; ke[7][10] = K[1][1]; ke[7][11] = K[1][2];\n\t\tke[8][9] = K[2][0]; ke[8][10] = K[2][1]; ke[8][11] = K[2][2];\n\n\n\t\t// (4,1)\n\t\tK = zbhat*P*(-alpha*m_eps);\n\t\tke[9 ][0] = K[0][0]; ke[ 9][1] = K[0][1]; ke[ 9][2] = K[0][2];\n\t\tke[10][0] = K[1][0]; ke[10][1] = K[1][1]; ke[10][2] = K[1][2];\n\t\tke[11][0] = K[2][0]; ke[11][1] = K[2][1]; ke[11][2] = K[2][2];\n\n\t\t// (4,2)\n\t\tK = (zbhat*(P*zathat+Q)*m_eps + Wba*m_ups)*alpha;\n\t\tke[9 ][3] = K[0][0]; ke[ 9][4] = K[0][1]; ke[ 9][5] = K[0][2];\n\t\tke[10][3] = K[1][0]; ke[10][4] = K[1][1]; ke[10][5] = K[1][2];\n\t\tke[11][3] = K[2][0]; ke[11][4] = K[2][1]; ke[11][5] = K[2][2];\n\n\t\t// (4,3)\n\t\tK = zbhat * P * (alpha * m_eps);\n\t\tke[9 ][6] = K[0][0]; ke[ 9][7] = K[0][1]; ke[ 9][8] = K[0][2];\n\t\tke[10][6] = K[1][0]; ke[10][7] = K[1][1]; ke[10][8] = K[1][2];\n\t\tke[11][6] = K[2][0]; ke[11][7] = K[2][1]; ke[11][8] = K[2][2];\n\n\t\t// (4,4)\n\t\tK = (zbhat*P*zbthat*m_eps - Fhat * zbthat + Wab * m_ups) * (-alpha);\n\t\tke[9 ][9] = K[0][0]; ke[ 9][10] = K[0][1]; ke[ 9][11] = K[0][2];\n\t\tke[10][9] = K[1][0]; ke[10][10] = K[1][1]; ke[10][11] = K[1][2];\n\t\tke[11][9] = K[2][0]; ke[11][10] = K[2][1]; ke[11][11] = K[2][2];\n\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\tdd::vec3d pa(ra);\n\t\tdd::quatd qa(Qat);\n\t\tdd::vec3d pb(rb);\n\t\tdd::quatd qb(Qbt);\n\t\tdd::vec3d lam(m_F);\n\t\tdd::vec3d mu(m_M);\n\n\t\tauto F = [&]() {\n\t\t\tdd::vec3d a = qa * m_ea0[0];\n\t\t\treturn a*(a*lam);\n\t\t\t};\n\n\t\tauto c = [&]() {\n\t\t\tdd::vec3d za = qa * m_qa0;\n\t\t\tdd::vec3d zb = qb * m_qb0;\n\t\t\tdd::vec3d d = pb + zb - pa - za;\n\t\t\tdd::vec3d a = qa * m_ea0[0];\n\t\t\treturn a * (d * a);\n\t\t\t};\n\n\t\tauto m1 = [&]() {\n\t\t\tdd::vec3d a = qa * m_ea0[0];\n\t\t\tdd::vec3d b = qb * m_eb0[0];\n\t\t\treturn b - a;\n\t\t\t};\n\n\t\tauto Ma = [&] {\n\t\t\tdd::vec3d za = qa * m_qa0;\n\t\t\tdd::vec3d ea = qa * m_ea0[0];\n\t\t\tdd::vec3d d = pb + zb - pa - za;\n\t\t\tdd::vec3d F = ea * (lam * ea);\n\t\t\treturn (za ^ F) - (ea ^ (d*(ea*lam) + lam*(ea*d))) + (ea ^ mu);\n\t\t\t};\n\n\t\tauto Mb = [&] {\n\t\t\tdd::vec3d zb = qb * m_qb0;\n\t\t\tdd::vec3d ea = qa * m_ea0[0];\n\t\t\tdd::vec3d eb = qb * m_eb0[0];\n\t\t\tdd::vec3d F = ea * (lam * ea);\n\t\t\treturn (zb ^ F) + (eb ^ mu);\n\t\t\t};\n\n\t\tmat3d K00 = -dd::D( F, pa);\n\t\tmat3d K10 = -dd::D(Ma, pa);\n\t\tmat3d K20 =  dd::D( F, pa);\n\t\tmat3d K30 =  dd::D(Mb, pa);\n\t\tmat3d K40 =  dd::D( c, pa);\n\t\tmat3d K50 =  dd::D(m1, pa);\n\n\t\tmat3d K01 = -dd::D( F, qa);\n\t\tmat3d K11 = -dd::D(Ma, qa);\n\t\tmat3d K21 =  dd::D( F, qa);\n\t\tmat3d K31 =  dd::D(Mb, qa);\n\t\tmat3d K41 =  dd::D( c, qa);\n\t\tmat3d K51 =  dd::D(m1, qa);\n\n\t\tmat3d K02 = -dd::D( F, pb);\n\t\tmat3d K12 = -dd::D(Ma, pb);\n\t\tmat3d K22 =  dd::D( F, pb);\n\t\tmat3d K32 =  dd::D(Mb, pb);\n\t\tmat3d K42 =  dd::D( c, pb);\n\t\tmat3d K52 =  dd::D(m1, pb);\n\n\t\tmat3d K03 = -dd::D( F, qb);\n\t\tmat3d K13 = -dd::D(Ma, qb);\n\t\tmat3d K23 =  dd::D( F, qb);\n\t\tmat3d K33 =  dd::D(Mb, qb);\n\t\tmat3d K43 =  dd::D( c, qb);\n\t\tmat3d K53 =  dd::D(m1, qb);\n\n\t\tmat3d K04 = -dd::D( F, lam);\n\t\tmat3d K14 = -dd::D(Ma, lam);\n\t\tmat3d K24 =  dd::D( F, lam);\n\t\tmat3d K34 =  dd::D(Mb, lam);\n\t\tmat3d K44 =  dd::D( c, lam);\n\t\tmat3d K54 =  dd::D(m1, lam);\n\n\t\tmat3d K05 = -dd::D( F, mu);\n\t\tmat3d K15 = -dd::D(Ma, mu);\n\t\tmat3d K25 =  dd::D( F, mu);\n\t\tmat3d K35 =  dd::D(Mb, mu);\n\t\tmat3d K45 =  dd::D( c, mu);\n\t\tmat3d K55 =  dd::D(m1, mu);\n\n\t\tmatrix kd(18, 18); kd.zero();\n\t\tkd.sub( 0, 0, K00); kd.sub( 0, 3, K01); kd.sub( 0, 6, K02); kd.sub( 0, 9, K03); kd.sub( 0, 12, K04); kd.sub( 0, 15, K05);\n\t\tkd.sub( 3, 0, K10); kd.sub( 3, 3, K11); kd.sub( 3, 6, K12); kd.sub( 3, 9, K13); kd.sub( 3, 12, K14); kd.sub( 3, 15, K15);\n\t\tkd.sub( 6, 0, K20); kd.sub( 6, 3, K21); kd.sub( 6, 6, K22); kd.sub( 6, 9, K23); kd.sub( 6, 12, K24); kd.sub( 6, 15, K25);\n\t\tkd.sub( 9, 0, K30); kd.sub( 9, 3, K31); kd.sub( 9, 6, K32); kd.sub( 9, 9, K33); kd.sub( 9, 12, K34); kd.sub( 9, 15, K35);\n\t\tkd.sub(12, 0, K40); kd.sub(12, 3, K41); kd.sub(12, 6, K42); kd.sub(12, 9, K43); kd.sub(12, 12, K44); kd.sub(12, 15, K45);\n\t\tkd.sub(15, 0, K50); kd.sub(15, 3, K51); kd.sub(15, 6, K52); kd.sub(15, 9, K53); kd.sub(15, 12, K54); kd.sub(15, 15, K55);\n\n\t\tFEElementMatrix ke(18, 18);\n\t\tke = kd;\n\t\tvector<int> LM(18);\n\t\tUnpackLM(LM);\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidPlanarJoint::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d ra, rb, qa, qb, c, ksi, Lm;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    double normF0, normF1;\n    vec3d Um;\n    double normM0, normM1;\n    bool bconv = true;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    double alpha = tp.alphaf;\n    \n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    mat3ds P;\n    vec3d p;\n    if (m_bdy && m_bdz) {\n        P = mat3dd(1);\n        p = ea[1]*m_dpy + ea[2]*m_dpz;\n    }\n    else if (m_bdy) {\n        P = mat3dd(1) - dyad(ea[2]);\n        p = ea[1]*m_dpy;\n    }\n    else if (m_bdz) {\n        P = mat3dd(1) - dyad(ea[1]);\n        p = ea[2]*m_dpz;\n    }\n    else {\n        P = dyad(ea[0]);\n        p = vec3d(0, 0, 0);\n    }\n    c = P*(rb + zb - ra - za) - p;\n    \n    if (m_bqx) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(m_qpx,ea[0]);\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        ksi = r.GetVector()*r.GetAngle();\n    }\n    else\n    {\n        ksi = (ea[0] ^ eb[0])/2;\n    }\n    \n    normF0 = sqrt(m_L*m_L);\n    \n    // calculate trial multiplier\n    Lm = m_L + c*m_eps;\n    \n    normF1 = sqrt(Lm*Lm);\n    \n    normM0 = sqrt(m_U*m_U);\n    \n    // calculate trial multiplier\n    Um = m_U + ksi*m_ups;\n    \n    normM1 = sqrt(Um*Um);\n    \n    // check convergence of constraints\n    feLog(\" rigid connector # %d (planar joint)\\n\", m_nID+1);\n    feLog(\"                  CURRENT        REQUIRED\\n\");\n    double pctn = 0;\n    double gap = c.norm();\n    double qap = ksi.norm();\n    if (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n    if (m_atol) feLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n    else        feLog(\"    force : %15le        ***\\n\", pctn);\n    if (m_gtol) feLog(\"    gap   : %15le %15le\\n\", gap, m_gtol);\n    else        feLog(\"    gap   : %15le        ***\\n\", gap);\n    double qctn = 0;\n    if (fabs(normM1) > 1e-10) qctn = fabs((normM1 - normM0)/normM1);\n    if (m_atol) feLog(\"    moment: %15le %15le\\n\", qctn, m_atol);\n    else        feLog(\"    moment: %15le        ***\\n\", qctn);\n    if (m_qtol) feLog(\"    angle : %15le %15le\\n\", qap, m_qtol);\n    else        feLog(\"    angle : %15le        ***\\n\", qap);\n    \n    if (m_atol && ((pctn >= m_atol) || (qctn >= m_atol))) bconv = false;\n    if (m_gtol && (gap >= m_gtol)) bconv = false;\n    if (m_qtol && (qap >= m_qtol)) bconv = false;\n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    if (!bconv)\n    {\n        // update multipliers\n        m_L = Lm;\n        m_U = Um;\n    }\n    \n    // auto-penalty update (works only with gaptol and angtol)\n\tif (m_bautopen) \n\t{\n\t\tif (m_gtol && (gap > m_gtol)) {\n\t\t\tm_eps = fmax(gap / m_gtol, 100)*m_eps;\n\t\t\tfeLog(\"    force_penalty :         %15le\\n\", m_eps);\n\t\t}\n\t\tif (m_qtol && (qap > m_qtol)) {\n\t\t\tm_ups = fmax(qap / m_qtol, 100)*m_ups;\n\t\t\tfeLog(\"    moment_penalty :        %15le\\n\", m_ups);\n\t\t}\n\t}\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPlanarJoint::Update()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) return;\n\n    vec3d ra, rb;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    const FETimeInfo& tp = GetTimeInfo();\n    double alpha = tp.alphaf;\n    \n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    mat3ds P;\n    vec3d p;\n    if (m_bdy && m_bdz) {\n        P = mat3dd(1);\n        p = ea[1]*m_dpy + ea[2]*m_dpz;\n    }\n    else if (m_bdy) {\n        P = mat3dd(1) - dyad(ea[2]);\n        p = ea[1]*m_dpy;\n    }\n    else if (m_bdz) {\n        P = mat3dd(1) - dyad(ea[1]);\n        p = ea[2]*m_dpz;\n    }\n    else {\n        P = dyad(ea[0]);\n        p = vec3d(0, 0, 0);\n    }\n    vec3d c = P*(rb + zb - ra - za) - p;\n    m_F = m_L + c*m_eps;\n    \n    vec3d ksi;\n    if (m_bqx) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(m_qpx,ea[0]);\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        ksi = r.GetVector()*r.GetAngle();\n    }\n    else\n    {\n        ksi = (ea[0] ^ eb[0])/2;\n    }\n    m_M = m_U + ksi*m_ups;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPlanarJoint::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n    m_U = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_q0 - RBa.m_r0;\n    m_qb0 = m_q0 - RBb.m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidPlanarJoint::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n\n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative translation onto local basis\n    vec3d y(x*ea[0], x*ea[1], x*ea[2]);\n    \n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidPlanarJoint::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    if (global) return q;\n    \n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative rotation onto local basis\n    vec3d y(q*ea[0], q*ea[1], q*ea[2]);\n    \n    return y;\n}\n\n// allocate equations\nint FERigidPlanarJoint::InitEquations(int neq)\n{\n\tconst int LMeq = 6;\n\tm_LM.resize(LMeq, -1);\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < LMeq; ++i) m_LM[i] = neq + i;\n\t\treturn LMeq;\n\t}\n\telse return 0;\n}\n\n// Build the matrix profile\nvoid FERigidPlanarJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\nvoid FERigidPlanarJoint::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.resize(18);\n\tlm[0] = m_rbA->m_LM[0];\n\tlm[1] = m_rbA->m_LM[1];\n\tlm[2] = m_rbA->m_LM[2];\n\tlm[3] = m_rbA->m_LM[3];\n\tlm[4] = m_rbA->m_LM[4];\n\tlm[5] = m_rbA->m_LM[5];\n\n\t// add the dofs of rigid body B\n\tlm[6] = m_rbB->m_LM[0];\n\tlm[7] = m_rbB->m_LM[1];\n\tlm[8] = m_rbB->m_LM[2];\n\tlm[9] = m_rbB->m_LM[3];\n\tlm[10] = m_rbB->m_LM[4];\n\tlm[11] = m_rbB->m_LM[5];\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < m_LM.size(); ++i) lm[12 + i] = m_LM[i];\n\t}\n}\n\nvoid FERigidPlanarJoint::PrepStep()\n{\n\tm_Fp = m_F;\n\tm_Mp = m_M;\n}\n\nvoid FERigidPlanarJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Fp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\t\tm_F.y = m_Fp.y + Ui[m_LM[1]] + ui[m_LM[1]];\n\t\tm_F.z = m_Fp.z + Ui[m_LM[2]] + ui[m_LM[2]];\n\n\t\tm_M.x = m_Mp.x + Ui[m_LM[3]] + ui[m_LM[3]];\n\t\tm_M.y = m_Mp.y + Ui[m_LM[4]] + ui[m_LM[4]];\n\t\tm_M.z = m_Mp.z + Ui[m_LM[5]] + ui[m_LM[5]];\n\t}\n}\n\nvoid FERigidPlanarJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tUi[m_LM[0]] += ui[m_LM[0]];\n\t\tUi[m_LM[1]] += ui[m_LM[1]];\n\t\tUi[m_LM[2]] += ui[m_LM[2]];\n\t\tUi[m_LM[3]] += ui[m_LM[3]];\n\t\tUi[m_LM[4]] += ui[m_LM[4]];\n\t\tUi[m_LM[5]] += ui[m_LM[5]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidPlanarJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FERigidConnector.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidPlanarJoint class implements a planar joint. The rigid joint\n//! allows the user to connect two rigid bodies at a point in space\n//! and allow 2D translation in a plane and rotation about the plane normal.\n\nclass FEBIOMECH_API FERigidPlanarJoint : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidPlanarJoint(FEModel* pfem);\n    \n    //! destructor\n    virtual ~FERigidPlanarJoint() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global = false) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global = false) override;\n    \n    //! initial position\n    vec3d InitialPosition() const;\n\n    //! current position\n    vec3d Position() const;\n\n\t//! current orientation\n\tquatd Orientation() const;\n\nprivate: // lag. mult. methods\n\tint InitEquations(int neq) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid UnpackLM(vector<int>& lm);\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\npublic: // parameters\n\tint     m_laugon; //!< enforcement method\n\tdouble\tm_atol;\t//! augmented Lagrangian tolerance\n    double  m_gtol; //! augmented Lagrangian gap tolerance\n    double  m_qtol; //! augmented Lagrangian angular gap tolerance\n    int     m_naugmin;  //! minimum number of augmentations\n    int     m_naugmax;  //! maximum number of augmentations\n    vec3d\tm_q0;\t//! initial position of joint\n    double  m_qpx;   //! prescribed rotation along first axis\n    double  m_dpy;   //! prescribed translation along second axis\n    double  m_dpz;   //! prescribed translation along third axis\n    bool    m_bqx;   //! flag for prescribing rotation along first axis\n    bool    m_bdy;   //! flag for prescribing translation along second axis\n    bool    m_bdz;   //! flag for prescribing translation along third axis\n    double\tm_eps;\t//! penalty factor for constraining force\n    double\tm_ups;\t//! penalty factor for constraining moment\n\tbool\tm_bautopen;\t//!< auto-penalty for gap and ang tolerance\n\nprotected:\n    vec3d\tm_qa0;\t//! initial relative position vector of joint w.r.t. A\n    vec3d\tm_qb0;\t//! initial relative position vector of joint w.r.t. B\n    \n    vec3d\tm_e0[3];\t//! initial joint basis\n    vec3d\tm_ea0[3];\t//! initial joint basis w.r.t. A\n    vec3d\tm_eb0[3];\t//! initial joint basis w.r.t. B\n    \n    vec3d\tm_L;\t//! Lagrange multiplier for constraining force\n    vec3d\tm_U;\t//! Lagrange multiplier for constraining moment\n\n\tvec3d m_Fp, m_Mp;\n\n\tvector<int>\t\tm_LM;\t// Lagrange multiplier equation numbers\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidPrismaticJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidPrismaticJoint.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/ad.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidPrismaticJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon, \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol, \"tolerance\"     );\n    ADD_PARAMETER(m_gtol, \"gaptol\"        );\n    ADD_PARAMETER(m_qtol, \"angtol\"        );\n    ADD_PARAMETER(m_eps , \"force_penalty\" );\n    ADD_PARAMETER(m_ups , \"moment_penalty\");\n    ADD_PARAMETER(m_cps , \"force_damping\" );\n    ADD_PARAMETER(m_rps , \"moment_damping\");\n    ADD_PARAMETER(m_q0  , \"joint_origin\"  );\n    ADD_PARAMETER(m_e0[0], \"translation_axis\" );\n    ADD_PARAMETER(m_e0[1], \"transverse_axis\");\n    ADD_PARAMETER(m_naugmin, \"minaug\"        );\n    ADD_PARAMETER(m_naugmax, \"maxaug\"        );\n    ADD_PARAMETER(m_bd  , \"prescribed_translation\");\n    ADD_PARAMETER(m_dp  , \"translation\"   );\n    ADD_PARAMETER(m_Fp  , \"force\"         );\n\tADD_PARAMETER(m_bautopen, \"auto_penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidPrismaticJoint::FERigidPrismaticJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n\n\tm_laugon = FECore::AUGLAG_METHOD; // for backward compatibility\n    m_atol = 0;\n    m_gtol = 0;\n    m_qtol = 0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n    m_dp = 0;\n    m_Fp = 0;\n    m_bd = false;\n    m_eps = m_ups = 1.0;\n    m_cps = m_rps = 0.0;\n    m_e0[0] = vec3d(1,0,0);\n    m_e0[1] = vec3d(0,1,0);\n\tm_bautopen = false;\n}\n\nFERigidPrismaticJoint::~FERigidPrismaticJoint()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidPrismaticJoint::Init()\n{\n    if (m_bd && (m_Fp != 0)) {\n        feLogError(\"Translation and force cannot be prescribed simultaneously in rigid connector %d (prismatic joint)\\n\", m_nID+1);\n        return false;\n    }\n    \n    // initialize joint basis\n    m_e0[0].unit();\n    m_e0[2] = m_e0[0] ^ m_e0[1]; m_e0[2].unit();\n    m_e0[1] = m_e0[2] ^ m_e0[0]; m_e0[1].unit();\n    \n    // reset force\n    m_F = vec3d(0,0,0); m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0); m_U = vec3d(0,0,0);\n\tm_U1 = m_U2 = vec3d(0, 0, 0);\n\n\tm_Lp = vec3d(0, 0, 0);\n\tm_U1p = m_U2p = vec3d(0, 0, 0);\n    \n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n    m_qa0 = m_q0 - m_rbA->m_r0;\n    m_qb0 = m_q0 - m_rbB->m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrismaticJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n    ar & m_qa0 & m_qb0;\n    ar & m_L & m_U;\n    ar & m_e0;\n    ar & m_ea0;\n    ar & m_eb0;\n\tar & m_U1 & m_U2 & m_LM;\n\tar & m_Fp & m_U1p & m_U2p;\n}\n\n//-----------------------------------------------------------------------------\n//! initial position \nvec3d FERigidPrismaticJoint::InitialPosition() const\n{\n\treturn m_q0;\n}\n\n//-----------------------------------------------------------------------------\n//! current position\nvec3d FERigidPrismaticJoint::Position() const\n{\n\tFERigidBody& RBa = *m_rbA;\n\tvec3d qa = m_qa0; \n\tRBa.GetRotation().RotateVector(qa);\n\treturn RBa.m_rt + qa;\n}\n\n//-----------------------------------------------------------------------------\n//! current orientation\nquatd FERigidPrismaticJoint::Orientation() const\n{\n\tquatd Q0(vec3d(1, 0, 0), m_e0[0]);\n\tFERigidBody& RBa = *m_rbA;\n\treturn RBa.GetRotation()*Q0;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidPrismaticJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n\n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\t// incremental compound rotation of B w.r.t. A\n\t\tvec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2])) / 2;\n\n\t\tmat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n\t\tvec3d p = m_bd ? ea[0] * m_dp : vec3d(0, 0, 0);\n\t\tvec3d c = P * (rb + zb - ra - za) - p;\n\t\tm_F = m_L + c * m_eps + ea[0] * m_Fp;\n\n\t\tvec3d ksi = vth;\n\t\tm_M = m_U + ksi * m_ups;\n\n\t\t// add damping\n\t\tif (m_cps > 0) {\n\t\t\t// body A\n\t\t\tvec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n\t\t\tvec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n\t\t\tvec3d va = vat * alpha + vap * (1 - alpha);\n\n\t\t\t// body b\n\t\t\tvec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n\t\t\tvec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n\t\t\tvec3d vb = vbt * alpha + vbp * (1 - alpha);\n\n\t\t\tm_F += P * (vb - va) * m_cps;\n\t\t}\n\t\tif (m_rps > 0) {\n\t\t\t// body A\n\t\t\tvec3d wa = RBa.m_wt * alpha + RBa.m_wp * (1 - alpha);\n\n\t\t\t// body b\n\t\t\tvec3d wb = RBb.m_wt * alpha + RBb.m_wp * (1 - alpha);\n\n\t\t\tm_M += (wb - wa) * m_rps;\n\t\t}\n\n\t\tfa[0] = m_F.x;\n\t\tfa[1] = m_F.y;\n\t\tfa[2] = m_F.z;\n\n\t\tfa[3] = za.y * m_F.z - za.z * m_F.y + m_M.x;\n\t\tfa[4] = za.z * m_F.x - za.x * m_F.z + m_M.y;\n\t\tfa[5] = za.x * m_F.y - za.y * m_F.x + m_M.z;\n\n\t\tfb[0] = -m_F.x;\n\t\tfb[1] = -m_F.y;\n\t\tfb[2] = -m_F.z;\n\n\t\tfb[3] = -zb.y * m_F.z + zb.z * m_F.y - m_M.x;\n\t\tfb[4] = -zb.z * m_F.x + zb.x * m_F.z - m_M.y;\n\t\tfb[5] = -zb.x * m_F.y + zb.y * m_F.x - m_M.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\t}\n\telse\n\t{\n\t\tmat3dd I(1.0);\n\t\tmat3d P = I - dyad(ea[0]);\n\n\t\tvec3d d = rb + zb - ra - za;\n\t\tdouble ad = m_ea0[0] * d;\n\t\tmat3d D = I * (ad) + (ea[0] & d);\n\t\tmat3d DT = D.transpose();\n\t\tmat3da eahat(ea[0]);\n\n\t\tvec3d F = P * m_F;\n\t\tvec3d Ma = (za ^ F) + eahat*(DT*m_F);\n\t\tvec3d Mb = (zb ^ F);\n\n\t\tfa[0] = -F.x;\n\t\tfa[1] = -F.y;\n\t\tfa[2] = -F.z;\n\n\t\tfa[3] = -Ma.x;\n\t\tfa[4] = -Ma.y;\n\t\tfa[5] = -Ma.z;\n\n\t\tfb[0] = F.x;\n\t\tfb[1] = F.y;\n\t\tfb[2] = F.z;\n\n\t\tfb[3] = Mb.x;\n\t\tfb[4] = Mb.y;\n\t\tfb[5] = Mb.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\n\t\t// translational constraint\n\t\tvec3d c = P*d;\n\t\tR[m_LM[0]] += c.x;\n\t\tR[m_LM[1]] += c.y;\n\t\tR[m_LM[2]] += c.z;\n\n\t\t// rotational constraint\n\t\tvec3d ksi1 = eb[0] - ea[0];\n\t\tvec3d ksi2 = eb[1] - ea[1];\n\t\tR[m_LM[3]] += ksi1.x;\n\t\tR[m_LM[4]] += ksi1.y;\n\t\tR[m_LM[5]] += ksi1.z;\n\t\tR[m_LM[6]] += ksi2.x;\n\t\tR[m_LM[7]] += ksi2.y;\n\t\tR[m_LM[8]] += ksi2.z;\n\t}\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidPrismaticJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tdouble alpha = tp.alphaf;\n    double beta  = tp.beta;\n    double gamma = tp.gamma;\n    \n    // get time increment\n    double dt = tp.timeIncrement;\n    \n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n\tvec3d ra = rat * alpha + rap * (1 - alpha);\n\tvec3d zat = Qat * m_qa0;\n\tvec3d zap = Qap * m_qa0;\n\tvec3d za = zat * alpha + zap * (1 - alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = rbt * alpha + rbp * (1 - alpha);\n\tvec3d zbt = Qbt * m_qb0;\n\tvec3d zbp = Qbp * m_qb0;\n\tvec3d zb = zbt * alpha + zbp * (1 - alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\t// incremental compound rotation of B w.r.t. A\n\t\tvec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2])) / 2;\n\n\t\tmat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n\t\tvec3d p = m_bd ? ea[0] * m_dp : vec3d(0, 0, 0);\n\t\tvec3d d = rb + zb - ra - za;\n\t\tvec3d c = P * d - p;\n\t\tm_F = m_L + c * m_eps + ea[0] * m_Fp;\n\n\t\tvec3d ksi = vth;\n\t\tm_M = m_U + ksi * m_ups;\n\n\t\tmat3d eahat[3], ebhat[3], eathat[3], ebthat[3];\n\t\tfor (int j = 0; j < 3; ++j) {\n\t\t\teahat[j] = skew(ea[j]);\n\t\t\tebhat[j] = skew(eb[j]);\n\t\t\teathat[j] = skew(eat[j]);\n\t\t\tebthat[j] = skew(ebt[j]);\n\t\t}\n\t\tmat3d Q = m_bd ? eathat[0] * m_dp : ((ea[0] & d) + mat3dd(1) * (ea[0] * d)) * eathat[0];\n\t\tmat3d Wba = (ebhat[0] * eathat[0] + ebhat[1] * eathat[1] + ebhat[2] * eathat[2]) / 2;\n\t\tmat3d Wab = (eahat[0] * ebthat[0] + eahat[1] * ebthat[1] + eahat[2] * ebthat[2]) / 2;\n\t\tmat3d K;\n\n\t\t// add damping\n\t\tmat3ds A;\n\t\tmat3d Ba, Bb, Ca, Cb;\n\t\tA.zero(); Ba.zero(); Bb.zero(); Ca.zero(); Cb.zero();\n\t\tif ((m_cps > 0) || (m_rps > 0)) {\n\t\t\tmat3dd I(1);\n\n\t\t\t// body A\n\t\t\tvec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n\t\t\tvec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n\t\t\tvec3d va = vat * alpha + vap * (1 - alpha);\n\t\t\tquatd qai = RBa.GetRotation() * RBa.m_qp.Inverse(); qai.MakeUnit();\n\t\t\tvec3d cai = qai.GetVector() * (2 * tan(qai.GetAngle() / 2));\n\t\t\tmat3d Ta = I + skew(cai) / 2 + dyad(cai) / 4;\n\t\t\tvec3d wa = RBa.m_wt * alpha + RBa.m_wp * (1 - alpha);\n\n\t\t\t// body b\n\t\t\tvec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n\t\t\tvec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n\t\t\tvec3d vb = vbt * alpha + vbp * (1 - alpha);\n\t\t\tquatd qbi = RBb.GetRotation() * RBb.m_qp.Inverse(); qbi.MakeUnit();\n\t\t\tvec3d cbi = qbi.GetVector() * (2 * tan(qbi.GetAngle() / 2));\n\t\t\tmat3d Tb = I + skew(cbi) / 2 + dyad(cbi) / 4;\n\t\t\tvec3d wb = RBb.m_wt * alpha + RBb.m_wp * (1 - alpha);\n\n\t\t\tm_F += (vb - va) * m_cps;\n\n\t\t\tvec3d w = wb - wa;\n\n\t\t\tm_M += P * w * m_rps;\n\n\t\t\tA = P * (gamma / beta / dt);\n\t\t\tBa = P * (zathat * Ta.transpose() * (gamma / beta / dt) + skew(RBa.m_wt) * zathat);\n\t\t\tBb = P * (zbthat * Tb.transpose() * (gamma / beta / dt) + skew(RBb.m_wt) * zbthat);\n\t\t\tCa = Ta.transpose() * (gamma / beta / dt);\n\t\t\tCb = Tb.transpose() * (gamma / beta / dt);\n\t\t}\n\n\t\tmat3da Fhat(m_F);\n\n\t\tFEElementMatrix ke(12, 12);\n\t\tke.zero();\n\n\t\t// row 1\n\t\tke.set(0, 0, P * (m_eps)+A * (m_cps));\n\t\tke.set(0, 3, (P * zathat + Q) * (-m_eps) + eathat[0]*m_Fp + Ba*(-m_cps));\n\t\tke.set(0, 6, P * (-m_eps) + A * (-m_cps));\n\t\tke.set(0, 9, P * zbthat * (m_eps)+Bb * (m_cps));\n\n\t\t// row 2\n\t\tke.set(3, 0, zahat*P*m_eps + zahat*A*m_cps);\n\t\tke.set(3, 3, zahat*(P * zathat + Q) * (-m_eps) - Fhat * zathat - Wba * m_ups - (zahat * Ba) * m_cps - Ca * m_rps);\n\t\tke.set(3, 6, zahat*P * (-m_eps) + zahat * A * (-m_cps));\n\t\tke.set(3, 9, zahat*P * zbthat * m_eps + Wab * m_ups + zahat * Bb * m_cps + Cb * m_rps);\n\n\t\t// row 3\n\t\tke.set(6, 0, P * (-m_eps) + A * (-m_cps));\n\t\tke.set(6, 3, (P * zathat + Q)*m_eps - eathat[0]*m_Fp + Ba*m_cps);\n\t\tke.set(6, 6, P*m_eps + A*m_cps);\n\t\tke.set(6, 9, P*zbthat*(-m_eps) - Bb*m_cps);\n\n\t\t// row 4\n\t\tke.set(9, 0, zbhat*P*(-m_eps) - zbhat*A*m_cps);\n\t\tke.set(9, 3, zbhat * (P * zathat + Q) * m_eps + Wba * m_ups + (zbhat * Ba) * m_cps + Ca * m_rps);\n\t\tke.set(9, 6, zbhat * P * (m_eps)+zbhat * A * (m_cps));\n\t\tke.set(9, 9, zbhat * P * zbthat * (-m_eps) + Fhat * zbthat - Wab * m_ups - (zbhat * Bb) * m_cps - Cb * m_rps);\n\n\t\tke *= alpha;\n\n\t\tvector<int> LM(12);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j    ] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\tdd::vec3d pa(ra);\n\t\tdd::quatd qa(Qat);\n\t\tdd::vec3d pb(rb);\n\t\tdd::quatd qb(Qbt);\n\t\tdd::vec3d lam(m_F);\n\t\tdd::vec3d mu1(m_U1);\n\t\tdd::vec3d mu2(m_U2);\n\n\t\tauto F = [&]() {\n\t\t\tdd::vec3d a = qa * m_ea0[0];\n\t\t\treturn lam - a*(lam*a);\n\t\t\t};\n\n\t\tauto c = [&]() {\n\t\t\tdd::vec3d za = qa * m_qa0;\n\t\t\tdd::vec3d zb = qb * m_qb0;\n\t\t\tdd::vec3d d = pb + zb - pa - za;\n\t\t\tdd::vec3d a = qa * m_ea0[0];\n\t\t\treturn d - a*(d*a);\n\t\t\t};\n\n\t\tauto m1 = [&]() {\n\t\t\tdd::vec3d a1 = qa * m_ea0[0];\n\t\t\tdd::vec3d b1 = qb * m_eb0[0];\n\t\t\treturn b1 - a1;\n\t\t\t};\n\n\t\tauto m2 = [&]() {\n\t\t\tdd::vec3d a2 = qa * m_ea0[1];\n\t\t\tdd::vec3d b2 = qb * m_eb0[1];\n\t\t\treturn b2 - a2;\n\t\t\t};\n\n\t\tauto Ma = [&] {\n\t\t\tdd::vec3d za = qa * m_qa0;\n\t\t\tdd::vec3d ea0 = qa * m_ea0[0];\n\t\t\tdd::vec3d ea1 = qa * m_ea0[1];\n\t\t\tdd::vec3d d = pb + zb - pa - za;\n\t\t\tdd::vec3d t = lam * (ea0 * d) + d * (ea0 * lam);\n\t\t\treturn (za ^ lam) + (ea0 ^ t) + (ea0 ^ mu1) + (ea1 ^ mu2);\n\t\t\t};\n\n\t\tauto Mb = [&] {\n\t\t\tdd::vec3d zb  = qb * m_qb0;\n\t\t\tdd::vec3d eb0 = qb * m_eb0[0];\n\t\t\tdd::vec3d eb1 = qb * m_eb0[1];\n\t\t\treturn (zb ^ lam) + (eb0 ^ mu1) + (eb1 ^ mu2);\n\t\t\t};\n\n\t\tmat3d K00 = -dd::D( F, pa);\n\t\tmat3d K10 = -dd::D(Ma, pa);\n\t\tmat3d K20 =  dd::D( F, pa);\n\t\tmat3d K30 =  dd::D(Mb, pa);\n\t\tmat3d K40 =  dd::D( c, pa);\n\t\tmat3d K50 =  dd::D(m1, pa);\n\t\tmat3d K60 =  dd::D(m2, pa);\n\n\t\tmat3d K01 = -dd::D( F, qa);\n\t\tmat3d K11 = -dd::D(Ma, qa);\n\t\tmat3d K21 =  dd::D( F, qa);\n\t\tmat3d K31 =  dd::D(Mb, qa);\n\t\tmat3d K41 =  dd::D( c, qa);\n\t\tmat3d K51 =  dd::D(m1, qa);\n\t\tmat3d K61 =  dd::D(m2, qa);\n\n\t\tmat3d K02 = -dd::D( F, pb);\n\t\tmat3d K12 = -dd::D(Ma, pb);\n\t\tmat3d K22 =  dd::D( F, pb);\n\t\tmat3d K32 =  dd::D(Mb, pb);\n\t\tmat3d K42 =  dd::D( c, pb);\n\t\tmat3d K52 =  dd::D(m1, pb);\n\t\tmat3d K62 =  dd::D(m2, pb);\n\n\t\tmat3d K03 = -dd::D( F, qb);\n\t\tmat3d K13 = -dd::D(Ma, qb);\n\t\tmat3d K23 =  dd::D( F, qb);\n\t\tmat3d K33 =  dd::D(Mb, qb);\n\t\tmat3d K43 =  dd::D( c, qb);\n\t\tmat3d K53 =  dd::D(m1, qb);\n\t\tmat3d K63 =  dd::D(m2, qb);\n\n\t\tmat3d K04 = -dd::D( F, lam);\n\t\tmat3d K14 = -dd::D(Ma, lam);\n\t\tmat3d K24 =  dd::D( F, lam);\n\t\tmat3d K34 =  dd::D(Mb, lam);\n\t\tmat3d K44 =  dd::D( c, lam);\n\t\tmat3d K54 =  dd::D(m1, lam);\n\t\tmat3d K64 =  dd::D(m2, lam);\n\n\t\tmat3d K05 = -dd::D( F, mu1);\n\t\tmat3d K15 = -dd::D(Ma, mu1);\n\t\tmat3d K25 =  dd::D( F, mu1);\n\t\tmat3d K35 =  dd::D(Mb, mu1);\n\t\tmat3d K45 =  dd::D( c, mu1);\n\t\tmat3d K55 =  dd::D(m1, mu1);\n\t\tmat3d K65 =  dd::D(m2, mu1);\n\n\t\tmat3d K06 = -dd::D( F, mu2);\n\t\tmat3d K16 = -dd::D(Ma, mu2);\n\t\tmat3d K26 =  dd::D( F, mu2);\n\t\tmat3d K36 =  dd::D(Mb, mu2);\n\t\tmat3d K46 =  dd::D( c, mu2);\n\t\tmat3d K56 =  dd::D(m1, mu2);\n\t\tmat3d K66 =  dd::D(m2, mu2);\n\n\t\tmatrix kd(21, 21); kd.zero();\n\t\tkd.sub( 0, 0, K00); kd.sub( 0, 3, K01); kd.sub( 0, 6, K02); kd.sub( 0, 9, K03); kd.sub( 0, 12, K04); kd.sub( 0, 15, K05); kd.sub( 0, 18, K06);\n\t\tkd.sub( 3, 0, K10); kd.sub( 3, 3, K11); kd.sub( 3, 6, K12); kd.sub( 3, 9, K13); kd.sub( 3, 12, K14); kd.sub( 3, 15, K15); kd.sub( 3, 18, K16);\n\t\tkd.sub( 6, 0, K20); kd.sub( 6, 3, K21); kd.sub( 6, 6, K22); kd.sub( 6, 9, K23); kd.sub( 6, 12, K24); kd.sub( 6, 15, K25); kd.sub( 6, 18, K26);\n\t\tkd.sub( 9, 0, K30); kd.sub( 9, 3, K31); kd.sub( 9, 6, K32); kd.sub( 9, 9, K33); kd.sub( 9, 12, K34); kd.sub( 9, 15, K35); kd.sub( 9, 18, K36);\n\t\tkd.sub(12, 0, K40); kd.sub(12, 3, K41); kd.sub(12, 6, K42); kd.sub(12, 9, K43); kd.sub(12, 12, K44); kd.sub(12, 15, K45); kd.sub(12, 18, K46);\n\t\tkd.sub(15, 0, K50); kd.sub(15, 3, K51); kd.sub(15, 6, K52); kd.sub(15, 9, K53); kd.sub(15, 12, K54); kd.sub(15, 15, K55); kd.sub(15, 18, K56);\n\t\tkd.sub(18, 0, K60); kd.sub(18, 3, K61); kd.sub(18, 6, K62); kd.sub(18, 9, K63); kd.sub(18, 12, K64); kd.sub(18, 15, K65); kd.sub(18, 18, K66);\n\n\t\tFEElementMatrix ke(21, 21);\n\t\tke = kd;\n\t\tvector<int> LM(21);\n\t\tUnpackLM(LM);\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidPrismaticJoint::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d ra, rb, qa, qb, c, ksi, Lm;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    double normF0, normF1;\n    vec3d Um;\n    double normM0, normM1;\n    bool bconv = true;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    // incremental compound rotation of B w.r.t. A\n    vec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2]))/2;\n    \n    mat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n    vec3d p = m_bd ? ea[0]*m_dp : vec3d(0,0,0);\n    c = P*(rb + zb - ra - za) - p;\n    \n    normF0 = sqrt(m_L*m_L);\n    \n    // calculate trial multiplier\n    Lm = m_L + c*m_eps;\n    \n    normF1 = sqrt(Lm*Lm);\n    \n    ksi = vth;\n    \n    normM0 = sqrt(m_U*m_U);\n    \n    // calculate trial multiplier\n    Um = m_U + ksi*m_ups;\n    \n    normM1 = sqrt(Um*Um);\n    \n    // check convergence of constraints\n    feLog(\" rigid connector # %d (prismatic joint)\\n\", m_nID+1);\n    feLog(\"                  CURRENT        REQUIRED\\n\");\n    double pctn = 0;\n    double gap = c.norm();\n    double qap = ksi.norm();\n    if (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n    if (m_atol) feLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n    else        feLog(\"    force : %15le        ***\\n\", pctn);\n    if (m_gtol) feLog(\"    gap   : %15le %15le\\n\", gap, m_gtol);\n    else        feLog(\"    gap   : %15le        ***\\n\", gap);\n    double qctn = 0;\n    if (fabs(normM1) > 1e-10) qctn = fabs((normM1 - normM0)/normM1);\n    if (m_atol) feLog(\"    moment: %15le %15le\\n\", qctn, m_atol);\n    else        feLog(\"    moment: %15le        ***\\n\", qctn);\n    if (m_qtol) feLog(\"    angle : %15le %15le\\n\", qap, m_qtol);\n    else        feLog(\"    angle : %15le        ***\\n\", qap);\n    \n    if (m_atol && ((pctn >= m_atol) || (qctn >= m_atol))) bconv = false;\n    if (m_gtol && (gap >= m_gtol)) bconv = false;\n    if (m_qtol && (qap >= m_qtol)) bconv = false;\n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    if (!bconv)\n    {\n        // update multipliers\n        m_L = Lm;\n        m_U = Um;\n    }\n    \n    // auto-penalty update (works only with gaptol and angtol)\n\tif (m_bautopen)\n\t{\n\t\tif (m_gtol && (gap > m_gtol)) {\n\t\t\tm_eps = fmax(gap / m_gtol, 100)*m_eps;\n\t\t\tfeLog(\"    force_penalty :         %15le\\n\", m_eps);\n\t\t}\n\t\tif (m_qtol && (qap > m_qtol)) {\n\t\t\tm_ups = fmax(qap / m_qtol, 100)*m_ups;\n\t\t\tfeLog(\"    moment_penalty :        %15le\\n\", m_ups);\n\t\t}\n\t}\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrismaticJoint::Update()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) return;\n\n    vec3d ra, rb;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    // incremental compound rotation of B w.r.t. A\n    vec3d vth = ((ea[0] ^ eb[0]) + (ea[1] ^ eb[1]) + (ea[2] ^ eb[2]))/2;\n    \n    mat3ds P = m_bd ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n    vec3d p = m_bd ? ea[0]*m_dp : vec3d(0,0,0);\n    vec3d c = P*(rb + zb - ra - za) - p;\n    m_F = m_L + c*m_eps + ea[0]*m_Fp;\n    \n    vec3d ksi = vth;\n    m_M = m_U + ksi*m_ups;\n    \n    // add damping\n    if (m_cps > 0) {\n        // body A\n        vec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n        vec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n        vec3d va = vat*alpha + vap*(1-alpha);\n        \n        // body b\n        vec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n        vec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n        vec3d vb = vbt*alpha + vbp*(1-alpha);\n        \n        m_F += P*(vb - va)*m_cps;\n    }\n    if (m_rps > 0) {\n        // body A\n        vec3d wa = RBa.m_wt*alpha + RBa.m_wp*(1-alpha);\n        \n        // body b\n        vec3d wb = RBb.m_wt*alpha + RBb.m_wp*(1-alpha);\n        \n        m_M += (wb - wa)*m_rps;\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrismaticJoint::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n    m_U = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_q0 - RBa.m_r0;\n    m_qb0 = m_q0 - RBb.m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidPrismaticJoint::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n\n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative translation onto local basis\n    vec3d y(x*ea[0], x*ea[1], x*ea[2]);\n    \n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidPrismaticJoint::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    if (global) return q;\n    \n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative rotation onto local basis\n    vec3d y(q*ea[0], q*ea[1], q*ea[2]);\n    \n    return y;\n}\n\n// allocate equations\nint FERigidPrismaticJoint::InitEquations(int neq)\n{\n\tconst int LMeq = 9;\n\tm_LM.resize(LMeq, -1);\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < LMeq; ++i) m_LM[i] = neq + i;\n\t\treturn LMeq;\n\t}\n\telse return 0;\n}\n\n// Build the matrix profile\nvoid FERigidPrismaticJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\nvoid FERigidPrismaticJoint::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.resize(21);\n\tlm[0] = m_rbA->m_LM[0];\n\tlm[1] = m_rbA->m_LM[1];\n\tlm[2] = m_rbA->m_LM[2];\n\tlm[3] = m_rbA->m_LM[3];\n\tlm[4] = m_rbA->m_LM[4];\n\tlm[5] = m_rbA->m_LM[5];\n\n\t// add the dofs of rigid body B\n\tlm[6] = m_rbB->m_LM[0];\n\tlm[7] = m_rbB->m_LM[1];\n\tlm[8] = m_rbB->m_LM[2];\n\tlm[9] = m_rbB->m_LM[3];\n\tlm[10] = m_rbB->m_LM[4];\n\tlm[11] = m_rbB->m_LM[5];\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < m_LM.size(); ++i) lm[12 + i] = m_LM[i];\n\t}\n}\n\nvoid FERigidPrismaticJoint::PrepStep()\n{\n\tm_Lp  = m_F;\n\tm_U1p = m_U1;\n\tm_U2p = m_U2;\n}\n\nvoid FERigidPrismaticJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Lp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\t\tm_F.y = m_Lp.y + Ui[m_LM[1]] + ui[m_LM[1]];\n\t\tm_F.z = m_Lp.z + Ui[m_LM[2]] + ui[m_LM[2]];\n\n\t\tm_U1.x = m_U1p.x + Ui[m_LM[3]] + ui[m_LM[3]];\n\t\tm_U1.y = m_U1p.y + Ui[m_LM[4]] + ui[m_LM[4]];\n\t\tm_U1.z = m_U1p.z + Ui[m_LM[5]] + ui[m_LM[5]];\n\n\t\tm_U2.x = m_U2p.x + Ui[m_LM[6]] + ui[m_LM[6]];\n\t\tm_U2.y = m_U2p.y + Ui[m_LM[7]] + ui[m_LM[7]];\n\t\tm_U2.z = m_U2p.z + Ui[m_LM[8]] + ui[m_LM[8]];\n\n\t\tm_M = m_U1 + m_U2;\n\t}\n}\n\nvoid FERigidPrismaticJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int n : m_LM) Ui[n] += ui[n];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidPrismaticJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidPrismaticJoint class implements a prismatic joint. The rigid joint\n//! allows the user to connect two rigid bodies at a point in space\n//! and allow translation along a single prescribed axis.\n\nclass FEBIOMECH_API FERigidPrismaticJoint : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidPrismaticJoint(FEModel* pfem);\n    \n    //! destructor\n\t~FERigidPrismaticJoint();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global = false) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global = false) override;\n\n\t//! initial position \n\tvec3d InitialPosition() const;\n\n\t//! current position\n\tvec3d Position() const;\n\n\t//! current orientation\n\tquatd Orientation() const;\n\nprivate: // lag. mult. methods\n\tint InitEquations(int neq) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid UnpackLM(vector<int>& lm);\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\npublic: // parameters\n\tint     m_laugon; //!< enforcement method\n    double\tm_atol;\t//! augmented Lagrangian tolerance\n    double  m_gtol; //! augmented Lagrangian gap tolerance\n    double  m_qtol; //! augmented Lagrangian angular gap tolerance\n    int     m_naugmin;  //! minimum number of augmentations\n    int     m_naugmax;  //! maximum number of augmentations\n    vec3d\tm_q0;\t//! initial position of joint\n    double  m_dp;   //! prescribed translation\n    bool    m_bd;   //! flag for prescribing translation\n    double  m_Fp;   //! prescribed force\n    double\tm_eps;\t//! penalty factor for constraining force\n    double\tm_ups;\t//! penalty factor for constraining moment\n    double\tm_cps;\t//! linear damping coefficient for constraining force\n    double\tm_rps;\t//! angular damping coefficient for constraining moment\n\tbool\tm_bautopen;\t//!< auto-penalty for gap and ang tolerance\n\nprotected:\n    vec3d\tm_qa0;\t//! initial relative position vector of joint w.r.t. A\n    vec3d\tm_qb0;\t//! initial relative position vector of joint w.r.t. B\n    \n    vec3d\tm_e0[3];\t//! initial joint basis\n    vec3d\tm_ea0[3];\t//! initial joint basis w.r.t. A\n    vec3d\tm_eb0[3];\t//! initial joint basis w.r.t. B\n    \n    vec3d\tm_L;\t//! Lagrange multiplier for constraining force\n    vec3d\tm_U;\t//! Lagrange multiplier for constraining moment\n\n\tvec3d m_Lp, m_U1p, m_U2p;\n\n\tvec3d m_U1, m_U2;\n\tvector<int>\t\tm_LM;\t// Lagrange multiplier equation numbers\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidRevoluteJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidRevoluteJoint.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidRevoluteJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon, \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol, \"tolerance\"     );\n    ADD_PARAMETER(m_gtol, \"gaptol\"        );\n    ADD_PARAMETER(m_qtol, \"angtol\"        );\n    ADD_PARAMETER(m_eps , \"force_penalty\" );\n    ADD_PARAMETER(m_ups , \"moment_penalty\");\n    ADD_PARAMETER(m_cps , \"force_damping\" );\n    ADD_PARAMETER(m_rps , \"moment_damping\");\n    ADD_PARAMETER(m_q0  , \"joint_origin\"  );\n    ADD_PARAMETER(m_e0[0], \"rotation_axis\" );\n    ADD_PARAMETER(m_e0[1], \"transverse_axis\");\n    ADD_PARAMETER(m_naugmin, \"minaug\"        );\n    ADD_PARAMETER(m_naugmax, \"maxaug\"        );\n    ADD_PARAMETER(m_bq  , \"prescribed_rotation\");\n    ADD_PARAMETER(m_qp  , \"rotation\"      );\n    ADD_PARAMETER(m_Mp  , \"moment\"        );\n\tADD_PARAMETER(m_bautopen, \"auto_penalty\");\n\tADD_PARAMETER(m_torsion_stiffness, \"torsion_stiffness\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidRevoluteJoint::FERigidRevoluteJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n\n\tm_laugon = FECore::AUGLAG_METHOD; // for backward compatibility\n\tm_atol = 0;\n    m_gtol = 0;\n    m_qtol = 0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n    m_qp = 0;\n    m_Mp = 0;\n    m_bq = false;\n    m_eps = m_ups = 1.0;\n    m_cps = m_rps = 0.0;\n    m_e0[0] = vec3d(0,0,1);\n    m_e0[1] = vec3d(1,0,0);\n\tm_bautopen = false;\n}\n\n//-----------------------------------------------------------------------------\nFERigidRevoluteJoint::~FERigidRevoluteJoint()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! initial position \nvec3d FERigidRevoluteJoint::InitialPosition() const\n{\n\treturn m_q0;\n}\n\n//-----------------------------------------------------------------------------\n//! current position\nvec3d FERigidRevoluteJoint::Position() const\n{\n\tFERigidBody& RBa = *m_rbA;\n\tvec3d qa = m_qa0;\n\tRBa.GetRotation().RotateVector(qa);\n\treturn RBa.m_rt + qa;\n}\n\n//-----------------------------------------------------------------------------\n//! current axis\nquatd FERigidRevoluteJoint::Orientation() const\n{\n\tquatd Q0(vec3d(0, 0, 1), m_e0[0]);\n\tFERigidBody& RBa = *m_rbA;\n\treturn RBa.GetRotation()*Q0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidRevoluteJoint::Init()\n{\n    if (m_bq && (m_Mp != 0)) {\n        feLogError(\"Rotation and moment cannot be prescribed simultaneously in rigid connector %d (revolute joint)\\n\", m_nID+1);\n        return false;\n    }\n    \n    // initialize joint basis\n    m_e0[0].unit();\n    m_e0[2] = m_e0[0] ^ m_e0[1]; m_e0[2].unit();\n    m_e0[1] = m_e0[2] ^ m_e0[0];\n    \n    // reset force\n    m_F = vec3d(0,0,0); m_L = vec3d(0,0,0); m_Fp = vec3d(0,0,0);\n    m_M = vec3d(0,0,0); m_U = vec3d(0,0,0); m_Up = vec3d(0,0,0);\n    \n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n    m_qa0 = m_q0 - m_rbA->m_r0;\n    m_qb0 = m_q0 - m_rbB->m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidRevoluteJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n    ar & m_qa0 & m_qb0;\n\tar & m_L & m_U;\n\tar & m_e0;\n    ar & m_ea0;\n    ar & m_eb0;\n\tar & m_LM;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidRevoluteJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n\n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tvec3d c = rb + zb - ra - za;\n\t\tm_F = m_L + c * m_eps;\n\n\t\tvec3d ksi;\n\t\tif (m_bq) {\n\t\t\tquatd q = (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp) * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd a(m_qp, ea[0]);\n\t\t\tquatd r = a * q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tksi = r.GetVector() * r.GetAngle();\n\t\t}\n\t\telse\n\t\t\tksi = (ea[0] ^ eb[0]) / 2;\n\t\tm_M = m_U + ksi * m_ups + ea[0] * m_Mp;\n\n\t\t// add damping\n\t\tif (m_cps > 0) {\n\t\t\t// body A\n\t\t\tvec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n\t\t\tvec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n\t\t\tvec3d va = vat * alpha + vap * (1 - alpha);\n\n\t\t\t// body b\n\t\t\tvec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n\t\t\tvec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n\t\t\tvec3d vb = vbt * alpha + vbp * (1 - alpha);\n\n\t\t\tm_F += (vb - va)*m_cps;\n\t\t}\n\t\tif (m_rps > 0) {\n\t\t\t// body A\n\t\t\tvec3d wa = RBa.m_wt * alpha + RBa.m_wp * (1 - alpha);\n\n\t\t\t// body b\n\t\t\tvec3d wb = RBb.m_wt * alpha + RBb.m_wp * (1 - alpha);\n\n\t\t\tmat3ds P = m_bq ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n\n\t\t\tm_M += P * (wb - wa) * m_rps;\n\t\t}\n\n\t\tfa[0] = m_F.x;\n\t\tfa[1] = m_F.y;\n\t\tfa[2] = m_F.z;\n\n\t\tfa[3] = za.y * m_F.z - za.z * m_F.y + m_M.x;\n\t\tfa[4] = za.z * m_F.x - za.x * m_F.z + m_M.y;\n\t\tfa[5] = za.x * m_F.y - za.y * m_F.x + m_M.z;\n\n\t\tfb[0] = -m_F.x;\n\t\tfb[1] = -m_F.y;\n\t\tfb[2] = -m_F.z;\n\n\t\tfb[3] = -zb.y * m_F.z + zb.z * m_F.y - m_M.x;\n\t\tfb[4] = -zb.z * m_F.x + zb.x * m_F.z - m_M.y;\n\t\tfb[5] = -zb.x * m_F.y + zb.y * m_F.x - m_M.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\t}\n\telse\n\t{\n\t\tvec3d Ma = (za ^ m_F) + (ea[0] ^ m_M);\n\t\tvec3d Mb = (zb ^ m_F) + (eb[0] ^ m_M);\n\n\t\tfa[0] = -m_F.x;\n\t\tfa[1] = -m_F.y;\n\t\tfa[2] = -m_F.z;\n\t\tfa[3] = -Ma.x;\n\t\tfa[4] = -Ma.y;\n\t\tfa[5] = -Ma.z;\n\n\t\tfb[0] = m_F.x;\n\t\tfb[1] = m_F.y;\n\t\tfb[2] = m_F.z;\n\t\tfb[3] = Mb.x;\n\t\tfb[4] = Mb.y;\n\t\tfb[5] = Mb.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\n\t\t// translational constraint\n\t\tvec3d c = rb + zb - ra - za;\n\t\tR[m_LM[0]] += c.x;\n\t\tR[m_LM[1]] += c.y;\n\t\tR[m_LM[2]] += c.z;\n\n\t\t// rotational constraint\n\t\tvec3d ksi1 = eb[0] - ea[0];\n\t\tR[m_LM[3]] += ksi1.x;\n\t\tR[m_LM[4]] += ksi1.y;\n\t\tR[m_LM[5]] += ksi1.z;\n\t}\n\n\tif (m_torsion_stiffness != 0)\n\t{\n\t\tvec3d zbat = m_qb0; RBa.GetRotation().RotateVector(zbat);\n\t\tvec3d zbap = m_qb0; RBa.m_qp.RotateVector(zbap);\n\t\tvec3d zba = zbat * alpha + zbap * (1 - alpha);\n\n\t\tvec3d nb(zb); nb.unit();\n\t\tvec3d nba(zba); nba.unit();\n\n\t\tvec3d t = nb ^ nba;\n\n\t\t// Approach A\n//\t\tvec3d T = t*m_torsion_stiffness;\n\n\t\t// Approach B\n\t\tt.unit();\n\t\tdouble angle = acos(nb * nba);\n\t\tvec3d T = t * (angle * m_torsion_stiffness);\n\n\t\t// add it all up\n\t\tdouble ma[3] = { -T.x, -T.y, -T.z };\n\t\tdouble mb[3] = {  T.x,  T.y,  T.z };\n\t\tfor (int i=0;i <3; ++i)\n\t\t{\n\t\t\tif (RBa.m_LM[3 + i] >= 0) R[RBa.m_LM[3 + i]] += ma[i];\n\t\t\tif (RBb.m_LM[3 + i] >= 0) R[RBb.m_LM[3 + i]] += mb[i];\n\t\t}\n\t}\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidRevoluteJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tdouble alpha = tp.alphaf;\n    double beta  = tp.beta;\n    double gamma = tp.gamma;\n    \n    // get time increment\n    double dt = tp.timeIncrement;\n    \n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n\tvec3d ra = rat * alpha + rap * (1 - alpha);\n\tvec3d zat = Qat * m_qa0;\n\tvec3d zap = Qap * m_qa0;\n\tvec3d za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = rbt * alpha + rbp * (1 - alpha);\n\tvec3d zbt = Qbt * m_qb0;\n\tvec3d zbp = Qbp * m_qb0;\n\tvec3d zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n\n\tmat3d eahat[3], ebhat[3], eathat[3], ebthat[3];\n\tfor (int j = 0; j < 3; ++j) {\n\t\teahat[j] = skew(ea[j]);\n\t\tebhat[j] = skew(eb[j]);\n\t\teathat[j] = skew(eat[j]);\n\t\tebthat[j] = skew(ebt[j]);\n\t}\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tvec3d c = rb + zb - ra - za;\n\t\tm_F = m_L + c * m_eps;\n\t\tmat3dd I(1);\n\n\t\tvec3d ksi;\n\t\tquatd q, a, r;\n\t\tif (m_bq) {\n\t\t\tq = (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp) * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\ta = quatd(m_qp, ea[0]);\n\t\t\tr = a * q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tksi = r.GetVector() * r.GetAngle();\n\t\t}\n\t\telse\n\t\t\tksi = (ea[0] ^ eb[0]) / 2;\n\t\tm_M = m_U + ksi * m_ups + ea[0] * m_Mp;\n\n\t\tmat3d K, Wba, Wab;\n\t\tWba = (ebhat[0] * eathat[0]) / 2;\n\t\tWab = (eahat[0] * ebthat[0]) / 2;\n\t\tif (m_bq) {\n\t\t\tquatd qa = RBa.GetRotation() * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd qb = RBb.GetRotation() * (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp).Inverse();\n\t\t\tqa.MakeUnit();\n\t\t\tqb.MakeUnit();\n\t\t\tmat3d Qa = qa.RotationMatrix();\n\t\t\tmat3d Qb = qb.RotationMatrix();\n\t\t\tmat3d A = a.RotationMatrix();\n\t\t\tmat3d R = r.RotationMatrix();\n\t\t\tmat3dd I(1);\n\t\t\tWba = A *(I * Qa.trace() - Qa) / 2;\n\t\t\tWab = R *(I * Qb.trace() - Qb) / 2;\n\t\t}\n\n\t\t// add damping\n\t\tmat3ds A;\n\t\tmat3d Ba, Bb, Ca, Cb, W;\n\t\tA.zero(); Ba.zero(); Bb.zero(); Ca.zero(); Cb.zero(); W.zero();\n\t\tif ((m_cps > 0) || (m_rps > 0)) {\n\t\t\t// body A\n\t\t\tvec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n\t\t\tvec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n\t\t\tvec3d va = vat * alpha + vap * (1 - alpha);\n\t\t\tquatd qai = RBa.GetRotation() * RBa.m_qp.Inverse(); qai.MakeUnit();\n\t\t\tvec3d cai = qai.GetVector() * (2 * tan(qai.GetAngle() / 2));\n\t\t\tmat3d Ta = I + skew(cai) / 2 + dyad(cai) / 4;\n\t\t\tvec3d wa = RBa.m_wt * alpha + RBa.m_wp * (1 - alpha);\n\n\t\t\t// body b\n\t\t\tvec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n\t\t\tvec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n\t\t\tvec3d vb = vbt * alpha + vbp * (1 - alpha);\n\t\t\tquatd qbi = RBb.GetRotation() * RBb.m_qp.Inverse(); qbi.MakeUnit();\n\t\t\tvec3d cbi = qbi.GetVector() * (2 * tan(qbi.GetAngle() / 2));\n\t\t\tmat3d Tb = I + skew(cbi) / 2 + dyad(cbi) / 4;\n\t\t\tvec3d wb = RBb.m_wt * alpha + RBb.m_wp * (1 - alpha);\n\n\t\t\tm_F += (vb - va)*m_cps;\n\n\t\t\tvec3d w = wb - wa;\n\n\t\t\t// angular damping along all directions if rotation is prescribed\n\t\t\tmat3ds P = m_bq ? I : I - dyad(ea[0]);\n\n\t\t\tm_M += P*w*m_rps;\n\n\t\t\tA = I*(gamma/beta/dt);\n\t\t\tBa = zathat * Ta.transpose() * (gamma / beta / dt) + skew(RBa.m_wt) * zathat;\n\t\t\tBb = zbthat * Tb.transpose() * (gamma / beta / dt) + skew(RBb.m_wt) * zbthat;\n\t\t\tCa = P * Ta.transpose() * (gamma / beta / dt);\n\t\t\tCb = P * Tb.transpose() * (gamma / beta / dt);\n\t\t\tW = (mat3dd(ea[0] * w) + (ea[0] & w)) * eathat[0];\n\t\t}\n\n\t\tmat3d Fhat; Fhat.skew(m_F);\n\t\tmat3d Mhat; Mhat.skew(m_M);\n\n\t\tFEElementMatrix ke(12, 12);\n\t\tke.zero();\n\n\t\t// row 1\n\t\tke.set(0, 0, I * (m_eps) + A * (m_cps));\n\t\tke.set(0, 3, zathat * (-m_eps) + Ba * (-m_cps));\n\t\tke.set(0, 6, I * (-m_eps) + A * (-m_cps));\n\t\tke.set(0, 9, zbthat * (m_eps) + Bb * (m_cps));\n\n\t\t// row 2\n\t\tke.set(3, 0, zahat* (m_eps) + zahat * A * (m_cps));\n\t\tke.set(3, 3, (zahat * zathat * m_eps + Fhat * zathat + Wba * m_ups) * (-1.0));\n\t\tke.set(3, 6, zahat * (-m_eps) + zahat * A * (-m_cps));\n\t\tke.set(3, 9, (zahat* zbthat* m_eps + Wab * m_ups));\n\n\t\tif (m_cps)\n\t\t{\n\t\t\tke.add(3, 3, eathat[0] * (m_Mp)+(zahat * Ba) * (-m_cps));\n\t\t\tke.add(3, 9, (zahat * Bb) * (m_cps));\n\t\t}\n\t\tif (m_rps)\n\t\t{\n\t\t\tke.add(3, 3, (W - Ca) * (m_rps));\n\t\t\tke.add(3, 9, Cb * (m_rps));\n\t\t}\n\n\t\t// row 3\n\t\tke.set(6, 0, I* (-m_eps) + A * (-m_cps));\n\t\tke.set(6, 3, zathat * (m_eps) + Ba * (m_cps));\n\t\tke.set(6, 6, I * (m_eps) + A * (m_cps));\n\t\tke.set(6, 9, zbthat * (-m_eps) + Bb * (-m_cps));\n\n\t\t// row 4\n\t\tke.set(9, 0, zbhat* (-m_eps) + zbhat * A * (-m_cps));\n\t\tke.set(9, 3, zbhat* zathat* m_eps + Wba * m_ups);\n\t\tke.set(9, 6, zbhat* (m_eps) + zbhat * A * (m_cps));\n\t\tke.set(9, 9, (zbhat * zbthat * m_eps - Fhat * zbthat + Wab * m_ups) * (-1.0));\n\n\t\tif (m_cps)\n\t\t{\n\t\t\tke.add(9, 0, -eathat[0] * (m_Mp)+(zbhat * Ba) * (m_cps));\n\t\t\tke.add(9, 9, (zbhat * Bb) * (-m_cps));\n\t\t}\n\t\tif (m_rps)\n\t\t{\n\t\t\tke.add(9, 0, -(W - Ca) * (m_rps));\n\t\t\tke.add(9, 9, -Cb * (m_rps));\n\t\t}\n\n\t\tke *= alpha;\n\n\t\tvector<int> LM(12);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\tFEElementMatrix ke; ke.resize(18, 18);\n\t\tke.zero();\n\n\t\tmat3dd I(1.0);\n\n\t\t// translation constraint\n\t\tmat3da Fhat(m_F);\n\t\tke.sub( 3,  3, -Fhat * zahat);\n\t\tke.sub( 9,  9, Fhat* zbhat);\n\t\tke.sub( 0, 12, -I);\n\t\tke.sub( 3, 12, -zahat);\n\t\tke.sub( 6, 12, I);\n\t\tke.sub( 9, 12, zbhat);\n\t\tke.sub(12,  0, -I);\n\t\tke.sub(12,  3, zahat);\n\t\tke.sub(12,  6, I);\n\t\tke.sub(12,  9, -zbhat);\n\n\t\t// rotational constraint 1\n\t\tmat3da Mhat(m_M);\n\t\tke.sub(3, 3, -Mhat*eahat[0]);\n\t\tke.sub(9, 9,  Mhat*ebhat[0]);\n\t\tke.sub(3, 15, -eahat[0]);\n\t\tke.sub(9, 15,  ebhat[0]);\n\t\tke.sub(15, 3,  eahat[0]);\n\t\tke.sub(15, 9, -ebhat[0]);\n\n\t\tvector<int> LM;\n\t\tUnpackLM(LM);\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\n\tif (m_torsion_stiffness != 0)\n\t{\n\t\tvec3d zbat = m_qb0; RBa.GetRotation().RotateVector(zbat);\n\t\tvec3d zbap = m_qb0; RBa.m_qp.RotateVector(zbap);\n\t\tvec3d zba = zbat * alpha + zbap * (1 - alpha);\n\n\t\tvec3d nb(zb); nb.unit();\n\t\tvec3d nba(zba); nba.unit();\n\n\t\tmat3d nbhat = skew(nb);\n\t\tmat3d nbahat = skew(nba);\n\n\t\tFEElementMatrix ke(12, 12);\n\t\tke.zero();\n\n\t\t// Approach A\n/*\t\tmat3d Kbba = nbhat * nbahat * m_torsion_stiffness;\n\t\tmat3d Kbab = nbahat * nbhat * m_torsion_stiffness;\n\t\tke.sub(3, 3,  Kbba); ke.sub(3, 9, -Kbab);\n\t\tke.sub(9, 3, -Kbba); ke.sub(9, 9,  Kbab);\n*/\n\n\t\t// Approach B\n\t\tvec3d t = nb ^ nba; t.unit();\n\t\tdouble angle = acos(nb * nba);\n\t\tif (fabs(angle) < 1e-12) t = ea[0];\n\t\tmat3d NT = (ea[0] & t) * m_torsion_stiffness;\n\t\tmat3d Ea = eahat[0]*(angle*m_torsion_stiffness);\n\n\t\tke.sub(3, 3, -(NT + Ea)); ke.sub(3, 9,  NT);\n\t\tke.sub(9, 3,  (NT + Ea)); ke.sub(9, 9, -NT);\n\n\t\t// don't forget to multiply with alpha\n\t\tke *= alpha;\n\n\t\tvector<int> LM(12);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j    ] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidRevoluteJoint::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d ra, rb, qa, qb, c, ksi, Lm;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    double normF0, normF1;\n    vec3d Um;\n    double normM0, normM1;\n    bool bconv = true;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    c = rb + zb - ra - za;\n    \n    normF0 = sqrt(m_L*m_L);\n    \n    // calculate trial multiplier\n    Lm = m_L + c*m_eps;\n    \n    normF1 = sqrt(Lm*Lm);\n    \n    if (m_bq) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(m_qp,ea[0]);\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        ksi = r.GetVector()*r.GetAngle();\n    }\n    else\n        ksi = (ea[0] ^ eb[0])/2;\n    \n    normM0 = sqrt(m_U*m_U);\n    \n    // calculate trial multiplier\n    Um = m_U + ksi*m_ups;\n    \n    normM1 = sqrt(Um*Um);\n    \n    // check convergence of constraints\n    feLog(\" rigid connector # %d (revolute joint)\\n\", m_nID+1);\n    feLog(\"                  CURRENT        REQUIRED\\n\");\n    double pctn = 0;\n    double gap = c.norm();\n    double qap = ksi.norm();\n    if (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n    if (m_atol) feLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n    else        feLog(\"    force : %15le        ***\\n\", pctn);\n    if (m_gtol) feLog(\"    gap   : %15le %15le\\n\", gap, m_gtol);\n    else        feLog(\"    gap   : %15le        ***\\n\", gap);\n    double qctn = 0;\n    if (fabs(normM1) > 1e-10) qctn = fabs((normM1 - normM0)/normM1);\n    if (m_atol) feLog(\"    moment: %15le %15le\\n\", qctn, m_atol);\n    else        feLog(\"    moment: %15le        ***\\n\", qctn);\n    if (m_qtol) feLog(\"    angle : %15le %15le\\n\", qap, m_qtol);\n    else        feLog(\"    angle : %15le        ***\\n\", qap);\n    \n    if (m_atol && ((pctn >= m_atol) || (qctn >= m_atol))) bconv = false;\n    if (m_gtol && (gap >= m_gtol)) bconv = false;\n    if (m_qtol && (qap >= m_qtol)) bconv = false;\n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    if (!bconv)\n    {\n        // update multipliers\n        m_L = Lm;\n        m_U = Um;\n    }\n\n    // auto-penalty update (works only with gaptol and angtol)\n\tif (m_bautopen)\n\t{\n\t\tif (m_gtol && (gap > m_gtol)) {\n\t\t\tm_eps = fmax(gap / m_gtol, 100)*m_eps;\n\t\t\tfeLog(\"    force_penalty :         %15le\\n\", m_eps);\n\t\t}\n\t\tif (m_qtol && (qap > m_qtol)) {\n\t\t\tm_ups = fmax(qap / m_qtol, 100)*m_ups;\n\t\t\tfeLog(\"    moment_penalty :        %15le\\n\", m_ups);\n\t\t}\n\t}\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidRevoluteJoint::Update()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) return;\n\n    vec3d ra, rb;\n    vec3d za, zb;\n    vec3d eat[3], eap[3], ea[3];\n    vec3d ebt[3], ebp[3], eb[3];\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n\teat[0] = m_ea0[0]; RBa.GetRotation().RotateVector(eat[0]);\n\teat[1] = m_ea0[1]; RBa.GetRotation().RotateVector(eat[1]);\n\teat[2] = m_ea0[2]; RBa.GetRotation().RotateVector(eat[2]);\n    eap[0] = m_ea0[0]; RBa.m_qp.RotateVector(eap[0]);\n    eap[1] = m_ea0[1]; RBa.m_qp.RotateVector(eap[1]);\n    eap[2] = m_ea0[2]; RBa.m_qp.RotateVector(eap[2]);\n    ea[0] = eat[0]*alpha + eap[0]*(1-alpha);\n    ea[1] = eat[1]*alpha + eap[1]*(1-alpha);\n    ea[2] = eat[2]*alpha + eap[2]*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\tebt[0] = m_eb0[0]; RBb.GetRotation().RotateVector(ebt[0]);\n\tebt[1] = m_eb0[1]; RBb.GetRotation().RotateVector(ebt[1]);\n\tebt[2] = m_eb0[2]; RBb.GetRotation().RotateVector(ebt[2]);\n    ebp[0] = m_eb0[0]; RBb.m_qp.RotateVector(ebp[0]);\n    ebp[1] = m_eb0[1]; RBb.m_qp.RotateVector(ebp[1]);\n    ebp[2] = m_eb0[2]; RBb.m_qp.RotateVector(ebp[2]);\n    eb[0] = ebt[0]*alpha + ebp[0]*(1-alpha);\n    eb[1] = ebt[1]*alpha + ebp[1]*(1-alpha);\n    eb[2] = ebt[2]*alpha + ebp[2]*(1-alpha);\n    \n    vec3d c = rb + zb - ra - za;\n    m_F = m_L + c*m_eps;\n    \n    vec3d ksi;\n    if (m_bq) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(m_qp,ea[0]);\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        ksi = r.GetVector()*r.GetAngle();\n    }\n    else\n        ksi = (ea[0] ^ eb[0])/2;\n    m_M = m_U + ksi*m_ups + ea[0]*m_Mp;\n    \n    // add damping\n    if (m_cps > 0) {\n        // body A\n        vec3d vat = RBa.m_vt + (RBa.m_wt ^ zat);\n        vec3d vap = RBa.m_vp + (RBa.m_wp ^ zap);\n        vec3d va = vat*alpha + vap*(1-alpha);\n        \n        // body b\n        vec3d vbt = RBb.m_vt + (RBb.m_wt ^ zbt);\n        vec3d vbp = RBb.m_vp + (RBb.m_wp ^ zbp);\n        vec3d vb = vbt*alpha + vbp*(1-alpha);\n        \n        m_F += (vb - va)*m_cps;\n    }\n    if (m_rps > 0) {\n        // body A\n        vec3d wa = RBa.m_wt*alpha + RBa.m_wp*(1-alpha);\n        \n        // body b\n        vec3d wb = RBb.m_wt*alpha + RBb.m_wp*(1-alpha);\n        \n        mat3ds P = m_bq ? mat3dd(1) : mat3dd(1) - dyad(ea[0]);\n        \n        m_M += P*(wb - wa)*m_rps;\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidRevoluteJoint::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n    m_U = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_q0 - RBa.m_r0;\n    m_qb0 = m_q0 - RBb.m_r0;\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidRevoluteJoint::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n\n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative translation onto local basis\n    vec3d y(x*ea[0], x*ea[1], x*ea[2]);\n    \n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidRevoluteJoint::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    if (global) return q;\n    \n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative rotation onto local basis\n    vec3d y(q*ea[0], q*ea[1], q*ea[2]);\n    \n    return y;\n}\n\n// allocate equations\nint FERigidRevoluteJoint::InitEquations(int neq)\n{\n\tconst int LMeq = 6;\n\tm_LM.resize(LMeq, -1);\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < LMeq; ++i) m_LM[i] = neq + i;\n\t\treturn LMeq;\n\t}\n\telse return 0;\n}\n\n// Build the matrix profile\nvoid FERigidRevoluteJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\nvoid FERigidRevoluteJoint::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.resize(21);\n\tlm[0] = m_rbA->m_LM[0];\n\tlm[1] = m_rbA->m_LM[1];\n\tlm[2] = m_rbA->m_LM[2];\n\tlm[3] = m_rbA->m_LM[3];\n\tlm[4] = m_rbA->m_LM[4];\n\tlm[5] = m_rbA->m_LM[5];\n\n\t// add the dofs of rigid body B\n\tlm[ 6] = m_rbB->m_LM[0];\n\tlm[ 7] = m_rbB->m_LM[1];\n\tlm[ 8] = m_rbB->m_LM[2];\n\tlm[ 9] = m_rbB->m_LM[3];\n\tlm[10] = m_rbB->m_LM[4];\n\tlm[11] = m_rbB->m_LM[5];\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < m_LM.size(); ++i) lm[12 + i] = m_LM[i];\n\t}\n}\n\nvoid FERigidRevoluteJoint::PrepStep()\n{\n\tm_Fp = m_F;\n\tm_Up = m_M;\n}\n\nvoid FERigidRevoluteJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Fp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\t\tm_F.y = m_Fp.y + Ui[m_LM[1]] + ui[m_LM[1]];\n\t\tm_F.z = m_Fp.z + Ui[m_LM[2]] + ui[m_LM[2]];\n\n\t\tm_M.x = m_Up.x + Ui[m_LM[3]] + ui[m_LM[3]];\n\t\tm_M.y = m_Up.y + Ui[m_LM[4]] + ui[m_LM[4]];\n\t\tm_M.z = m_Up.z + Ui[m_LM[5]] + ui[m_LM[5]];\n\t}\n}\n\nvoid FERigidRevoluteJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tUi[m_LM[0]] += ui[m_LM[0]];\n\t\tUi[m_LM[1]] += ui[m_LM[1]];\n\t\tUi[m_LM[2]] += ui[m_LM[2]];\n\t\tUi[m_LM[3]] += ui[m_LM[3]];\n\t\tUi[m_LM[4]] += ui[m_LM[4]];\n\t\tUi[m_LM[5]] += ui[m_LM[5]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidRevoluteJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidRevoluteJoint class implements a revolute joint. The rigid joint\n//! allows the user to connect two rigid bodies at a point in space\n//! and allow rotation about a single prescribed axis.\n\nclass FEBIOMECH_API FERigidRevoluteJoint : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidRevoluteJoint(FEModel* pfem);\n    \n    //! destructor\n\t~FERigidRevoluteJoint();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global = false) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global = false) override;\n\n\t//! initial position \n\tvec3d InitialPosition() const;\n\n\t//! current position\n\tvec3d Position() const;\n\n\t//! current axis\n\tquatd Orientation() const;\n\nprivate: // lag. mult. methods\n\tint InitEquations(int neq) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid UnpackLM(vector<int>& lm);\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\npublic: // parameters\n\tint\t\tm_laugon;\n    double\tm_atol;\t//! augmented Lagrangian tolerance\n    double  m_gtol; //! augmented Lagrangian gap tolerance\n    double  m_qtol; //! augmented Lagrangian angular gap tolerance\n    int     m_naugmin;  //! minimum number of augmentations\n    int     m_naugmax;  //! maximum number of augmentations\n    double\tm_eps;\t//! penalty factor for constraining force\n    double\tm_ups;\t//! penalty factor for constraining moment\n    double\tm_cps;\t//! linear damping coefficient for constraining force\n    double\tm_rps;\t//! angular damping coefficient for constraining moment\n    vec3d\tm_q0;\t//! initial position of joint\n    double  m_qp;   //! prescribed rotation\n    bool    m_bq;   //! flag for prescribing rotation\n    double  m_Mp;   //! prescribed moment\n\tbool\tm_bautopen;\t//!< auto-penalty for gap and ang tolerance\n\n\tdouble m_torsion_stiffness = 0.0; //!< torsional stiffness about the joint axis\n\nprotected:\n    vec3d\tm_qa0;\t//! initial relative position vector of joint w.r.t. A\n    vec3d\tm_qb0;\t//! initial relative position vector of joint w.r.t. B\n    \n    vec3d\tm_e0[3];\t//! initial joint basis\n    vec3d\tm_ea0[3];\t//! initial joint basis w.r.t. A\n    vec3d\tm_eb0[3];\t//! initial joint basis w.r.t. B\n    \n    vec3d\tm_L;\t//! Lagrange multiplier for constraining force\n    vec3d\tm_U;\t//! Lagrange multiplier for constraining moment\n\n\tvec3d m_Fp, m_Up;\n\n\tvector<int>\t\tm_LM;\t// Lagrange multiplier equation numbers\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidRotationVector.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FERigidRotationVector.h\"\n\nBEGIN_FECORE_CLASS(FERigidRotationVector, FERigidBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_vx, \"vx\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_RADIAN);\n\tADD_PARAMETER(m_vy, \"vy\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_RADIAN);\n\tADD_PARAMETER(m_vz, \"vz\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_RADIAN);\nEND_FECORE_CLASS();\n\nFERigidRotationVector::FERigidRotationVector(FEModel* fem) : FERigidBC(fem)\n{\n\tm_vx = m_vy = m_vz = 0.0;\n\n\tm_rc[0] = new FERigidPrescribedBC(fem); m_rc[0]->SetBC(3);\n\tm_rc[1] = new FERigidPrescribedBC(fem); m_rc[1]->SetBC(4);\n\tm_rc[2] = new FERigidPrescribedBC(fem); m_rc[2]->SetBC(5);\n}\n\nFERigidRotationVector::~FERigidRotationVector()\n{\n\tdelete m_rc[0];\n\tdelete m_rc[1];\n\tdelete m_rc[2];\n}\n\nbool FERigidRotationVector::Init()\n{\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tm_rc[i]->SetRigidMaterial(m_rigidMat);\n\t\tif (m_rc[i]->Init() == false) return false;\n\t}\n\n\treturn true;\n}\n\nvoid FERigidRotationVector::Activate()\n{\n\tFERigidBC::Activate();\n\tm_rc[0]->Activate();\n\tm_rc[1]->Activate();\n\tm_rc[2]->Activate();\n\n\tm_rc[0]->SetValue(m_vx);\n\tm_rc[1]->SetValue(m_vy);\n\tm_rc[2]->SetValue(m_vz);\n}\n\nvoid FERigidRotationVector::Deactivate()\n{\n\tFERigidBC::Deactivate();\n\tm_rc[0]->Deactivate();\n\tm_rc[1]->Deactivate();\n\tm_rc[2]->Deactivate();\n}\n\nvoid FERigidRotationVector::InitTimeStep()\n{\n\tm_rc[0]->SetValue(m_vx);\n\tm_rc[1]->SetValue(m_vy);\n\tm_rc[2]->SetValue(m_vz);\n}\n\nvoid FERigidRotationVector::Serialize(DumpStream& ar)\n{\n\tFERigidBC::Serialize(ar);\n\n\tif (ar.IsShallow() == false)\n\t{\n\t\tfor (int j = 0; j < 3; ++j)\n\t\t{\n\t\t\t// We need to serialize a reference to avoid that\n\t\t\t// restart will try to allocate the m_rc variables\n\t\t\tFERigidPrescribedBC& rc = *m_rc[j];\n\t\t\tar & rc;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidRotationVector.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"RigidBC.h\"\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FERigidRotationVector : public FERigidBC\n{\npublic:\n\tFERigidRotationVector(FEModel* fem);\n\t~FERigidRotationVector();\n\n\tbool Init() override;\n\n\tvoid Activate() override;\n\n\tvoid Deactivate() override;\n\n\tvoid InitTimeStep() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tdouble\tm_vx, m_vy, m_vz;\n\n\tFERigidPrescribedBC*\tm_rc[3];\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidShellDomain.h\"\n#include \"FEBodyForce.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n#include \"FEBioMech.h\"\n\n//-----------------------------------------------------------------------------\nFERigidShellDomainOld::FERigidShellDomainOld(FEModel* pfem) : FEElasticShellDomainOld(pfem) {}\n\n//-----------------------------------------------------------------------------\n// NOTE: Although this function doesn't do anything, we need it since \n//       for rigid shell domains we don't want to call the FEElasticShellDomain::Initialize member.\nbool FERigidShellDomainOld::Init()\n{\n\t// just call the base class\n\treturn FEShellDomainOld::Init();\n}\n\n//-----------------------------------------------------------------------------\n// We need to override it since the base class version will not work for rigid domains.\nvoid FERigidShellDomainOld::Reset()\n{\n\t// nothing here\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate stiffness contributions for rigid shells.\n//! Since rigid elements don't generate stress, we don't need to do\n//! anything here.\nvoid FERigidShellDomainOld::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Caught you looking!\n}\n\n\n//-----------------------------------------------------------------------------\n//! calculate residual forces for rigid shells\n//!\nvoid FERigidShellDomainOld::InternalForces(FEGlobalVector& R)\n{\n\t// Nothing to do.\n}\n\n//-----------------------------------------------------------------------------\n//! update stresses for rigid shells.\n//!\nvoid FERigidShellDomainOld::Update(const FETimeInfo& tp)\n{\n\t// Nothing to see here. Please move on.\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidShellDomainOld::MassMatrix(FELinearSystem& LS, double scale)\n{\n\t// Only crickets here ... \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidShellDomainOld::InertialForces(FEGlobalVector& R, std::vector<double>& F)\n{\n\t// chirp, chirp ...\n}\n\n//=======================================================================\nBEGIN_FECORE_CLASS(FERigidShellDomain, FEShellDomain)\n\tADD_PARAMETER(m_h0, \"shell_thickness\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidShellDomain::FERigidShellDomain(FEModel* fem) : FEShellDomain(fem), FEElasticDomain(fem), m_dof(fem)\n{\n\tm_h0 = 0.0;\n\tm_pMat = nullptr;\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (fem)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n}\n\nbool FERigidShellDomain::Create(int nelems, FE_Element_Spec espec)\n{\n\tm_Elem.resize(nelems);\n\tfor (int i = 0; i < nelems; ++i)\n\t{\n\t\tFEShellElement& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (espec.etype != FE_ELEM_INVALID_TYPE)\n\t\tfor (int i = 0; i < nelems; ++i) m_Elem[i].SetType(espec.etype);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidShellDomain::Activate()\n{\n\t// don't need to do anything here\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidShellDomain::AssignDefaultShellThickness()\n{\n\tdouble h0 = m_h0;\n\tif (h0 <= 0.0) return;\n\n\tfor (int j = 0; j < Elements(); ++j)\n\t{\n\t\tFEShellElement& el = Element(j);\n\t\tint ne = el.Nodes();\n\t\tfor (int n = 0; n < ne; ++n) el.m_ht[n] = el.m_h0[n] = h0;\n\t}\n}\n\nvoid FERigidShellDomain::SetMaterial(FEMaterial* pm)\n{\n\tm_pMat = dynamic_cast<FERigidMaterial*>(pm); assert(m_pMat);\n\tFEShellDomain::SetMaterial(pm);\n}\n\nFEMaterial* FERigidShellDomain::GetMaterial()\n{ \n\treturn m_pMat; \n}\n\nvoid FERigidShellDomain::Update(const FETimeInfo& tp)\n{\n\t// nothing to do\n}\n\nvoid FERigidShellDomain::Reset()\n{\n\t// nothing to do\n}\n\nvoid FERigidShellDomain::InternalForces(FEGlobalVector& R)\n{\n\t// nothing to do\n}\n\nvoid FERigidShellDomain::BodyForce(FEGlobalVector& R, FEBodyForce& bf)\n{\n\tint NS = (int)m_Elem.size();\n#pragma omp parallel for\n\tfor (int i = 0; i < NS; ++i)\n\t{\n\t\t// element force vector\n\t\tvector<double> fe;\n\t\tvector<int> lm;\n\n\t\t// get the element\n\t\tFEShellElement& el = m_Elem[i];\n\n\t\t// create the element force vector and initialize to zero\n\t\tint ndof = 3 * el.Nodes();\n\t\tfe.assign(ndof, 0);\n\n\t\t// apply body forces to shells\n\t\tElementBodyForce(bf, el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble the residual\n\t\t// NOTE: Setting bdom parameter to false to avoid crash, but\n\t\t//       need to look further what effect this really has. \n\t\tR.Assemble(el.m_node, lm, fe, false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element body forces for shells\n\nvoid FERigidShellDomain::ElementBodyForce(FEBodyForce& BF, FEShellElement& el, vector<double>& fe)\n{\n\t// integration weights\n\tdouble* gw = el.GaussWeights();\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tfor (int n = 0; n < nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tdouble dens = m_pMat->Density(mp);\n\n\t\t// calculate the jacobian\n\t\tdouble Jw = detJ0(el, n) * gw[n];\n\n\t\tdouble* M = el.H(n);\n\n\t\t// get the force\n\t\tvec3d f = BF.force(mp);\n\n\t\tfor (int i = 0; i < neln; ++i)\n\t\t{\n\t\t\tvec3d fi = f * dens * M[i] * Jw;\n\n\t\t\tfe[3 * i    ] -= fi.x;\n\t\t\tfe[3 * i + 1] -= fi.y;\n\t\t\tfe[3 * i + 2] -= fi.z;\n\t\t}\n\t}\n}\n\nvoid FERigidShellDomain::InertialForces(FEGlobalVector& R, std::vector<double>& F)\n{\n\t// nothing to do\n}\n\nvoid FERigidShellDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// nothing to do\n}\n\nvoid FERigidShellDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n\t// repeat over all shell elements\n\tint NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n\tfor (int iel = 0; iel < NE; ++iel)\n\t{\n\t\tFEShellElement& el = m_Elem[iel];\n\n\t\t// create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = 3 * el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate inertial stiffness\n\t\tElementBodyForceStiffness(bf, el, ke);\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FERigidShellDomain::ElementBodyForceStiffness(FEBodyForce& BF, FEShellElement &el, matrix &ke)\n{\n\tint neln = el.Nodes();\n\n\tdouble* gw = el.GaussWeights();\n\n\tdouble alphaf = GetFEModel()->GetTime().alphaf;\n\n\t// loop over integration points\n\tint nint = el.GaussPoints();\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tdouble Jw = detJ0(el, n) * gw[n] * alphaf;\n\n\t\t// get the stiffness\n\t\tmat3d K = BF.stiffness(mp)*(m_pMat->Density(mp)*Jw);\n\n\t\tdouble* M = el.H(n);\n\n\t\tfor (int i=0, i3=0; i<neln; ++i, i3 += 3)\n\t\t{\n\t\t\tfor (int j=0, j3 = 0; j<neln; ++j, j3 += 3)\n\t\t\t{\n\t\t\t\tmat3d Kij = K*(M[i]*M[j]);\n\n\t\t\t\tke[i3  ][j3  ] += Kij(0,0); ke[i3  ][j3+1] += Kij(0,1); ke[i3  ][j3+2] += Kij(0,2);\n\t\t\t\tke[i3+1][j3  ] += Kij(1,0); ke[i3+1][j3+1] += Kij(1,1); ke[i3+1][j3+2] += Kij(1,2);\n\t\t\t\tke[i3+2][j3  ] += Kij(2,0); ke[i3+2][j3+1] += Kij(2,1); ke[i3+2][j3+2] += Kij(2,2);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FERigidShellDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n\t// nothing to do here\n}\n\ndouble FERigidShellDomain::detJ0(FEShellElement& el, int n)\n{\n\tvector<vec3d> X(FEElement::MAX_NODES);\n\tfor (int i = 0; i < el.Nodes(); ++i) X[i] = Node(el.m_lnode[i]).m_r0;\n\n\tdouble* Gr = el.Hr(n);\n\tdouble* Gs = el.Hs(n);\n\n\tvec3d dr = vec3d(0, 0, 0);\n\tvec3d ds = vec3d(0, 0, 0);\n\tfor (int i = 0; i < el.Nodes(); ++i)\n\t{\n\t\tdr += X[i] * Gr[i];\n\t\tds += X[i] * Gs[i];\n\t}\n\n\tdouble J0 = (dr ^ ds).norm();\n\n\t// multiply with shell thickness\n\tdouble h = 0.0;\n\tdouble* H = el.H(n);\n\tfor (int i = 0; i < el.Nodes(); ++i) h += H[i] * el.m_h0[i];\n\n\treturn J0*h;\n}\n\nmat3d FERigidShellDomain::CalculateMOI()\n{\n\tmat3d moi; moi.zero();\n\tFEMesh* pm = GetMesh();\n\n\tvector<vec3d> r0(FEElement::MAX_NODES);\n\tmat3dd I(1); // identity tensor\n\n\t// loop over all elements\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEShellElement& el = Element(iel);\n\n\t\t// initial coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int i = 0; i < neln; ++i) r0[i] = pm->Node(el.m_node[i]).m_r0;\n\n\t\t// loop over integration points\n\t\tdouble* gw = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t// calculate jacobian\n\t\t\tdouble Jw = detJ0(el, n) * gw[n];\n\n\t\t\t// shape functions at integration point\n\t\t\tdouble* H = el.H(n);\n\n\t\t\tdouble dens = m_pMat->Density(mp);\n\n\t\t\t// add to moi\n\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tmat3d Iij = (r0[i] * r0[j]) * I - (r0[i] & r0[j]);\n\t\t\t\t\tmoi += Iij * (H[i] * H[j] * Jw * dens);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn moi;\n}\n\ndouble FERigidShellDomain::CalculateMass()\n{\n\tdouble mass = 0.0;\n\t// loop over all elements\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEShellElement& el = Element(iel);\n\n\t\t// loop over integration points\n\t\tint nint = el.GaussPoints();\n\t\tdouble* gw = el.GaussWeights();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t// calculate jacobian\n\t\t\tdouble detJ = detJ0(el, n);\n\n\t\t\t// add to total mass\n\t\t\tmass += m_pMat->Density(mp) * detJ * gw[n];\n\t\t}\n\t}\n\treturn mass;\n}\n\nvec3d FERigidShellDomain::CalculateCOM()\n{\n\tvector<vec3d> r0(FEElement::MAX_NODES);\n\tvec3d rc(0, 0, 0);\n\n\t// loop over all elements\n\tfor (int iel = 0; iel < Elements(); ++iel)\n\t{\n\t\tFEShellElement& el = Element(iel);\n\n\t\t// nr of integration points\n\t\tint nint = el.GaussPoints();\n\n\t\t// number of nodes\n\t\tint neln = el.Nodes();\n\n\t\t// initial coordinates\n\t\tfor (int i = 0; i < neln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).m_r0;\n\n\t\t// integration weights\n\t\tdouble* gw = el.GaussWeights();\n\n\t\t// loop over integration points\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t// calculate jacobian\n\t\t\tdouble detJ = detJ0(el, n);\n\n\t\t\t// shape functions at integration point\n\t\t\tdouble* H = el.H(n);\n\n\t\t\tdouble dens = m_pMat->Density(mp);\n\n\t\t\t// add to com\n\t\t\tfor (int i = 0; i < el.Nodes(); ++i)\n\t\t\t{\n\t\t\t\trc += r0[i] * H[i] * detJ * gw[n] * dens;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn rc;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticShellDomain.h\"\n#include \"FEElasticShellDomainOld.h\"\n\nclass FEBodyForce;\nclass FERigidMaterial;\n\n//-----------------------------------------------------------------------------\n//! domain class for 3D rigid shell elements\n//!\nclass FERigidShellDomainOld : public FEElasticShellDomainOld\n{\npublic:\n\t//! constructor\n\tFERigidShellDomainOld(FEModel* pfem);\n\n\t//! Initialize\n\tbool Init() override;\n\n\t//! reset data\n\tvoid Reset() override;\n\npublic:\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculates the internal forces (nothing to do)\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculates mass matrix (nothing to do)\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\n\t//! calculates the inertial forces (nothing to do)\n\tvoid InertialForces(FEGlobalVector& R, std::vector<double>& F) override;\n\n\t// update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n};\n\n//-----------------------------------------------------------------------------\n// Implements a rigid shell domain. We need to inherit from FEShellDomain and FEElasticDomain.\n// The latter is needed because most of the solid solver classes assume that domains inherit this class.\nclass FERigidShellDomain : public FEShellDomain, public FEElasticDomain\n{\npublic:\n\tFERigidShellDomain(FEModel* fem);\n\npublic: // from FEMeshPartition\n\t//! return number of elements\n\tint Elements() const override { return (int)m_Elem.size(); };\n\n\t//! return a reference to an element \\todo this is not the preferred interface but I've added it for now\n\tFEElement& ElementRef(int i) override { return m_Elem[i]; };\n\tconst FEElement& ElementRef(int i) const override { return m_Elem[i]; };\n\npublic: // from FEDomain\n\t// create function\n\tbool Create(int elements, FE_Element_Spec espec) override;\n\n\tvoid Activate() override;\n\n\tconst FEDofList& GetDOFList() const override { return m_dof; }\n\n\tvoid Update(const FETimeInfo& tp) override;\n\n\tvoid Reset() override;\n\n\tvoid SetMaterial(FEMaterial* pm) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\npublic: // from FEShellDomain\n\t// get a shell element\n\tFEShellElement& Element(int i) override { return m_Elem[i]; }\n\n\tvoid AssignDefaultShellThickness() override;\n\npublic: // from FEElasticDomain\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\n\tvoid InertialForces(FEGlobalVector& R, std::vector<double>& F) override;\n\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\npublic:\n\t// calculate contribution of MOI for this domain\n\tmat3d CalculateMOI();\n\n\tdouble CalculateMass();\n\n\tvec3d CalculateCOM();\n\nprivate:\n\t//! Calculate extenral body forces for shell elements\n\tvoid ElementBodyForce(FEBodyForce& BF, FEShellElement& el, vector<double>& fe);\n\n\tvoid ElementBodyForceStiffness(FEBodyForce& BF, FEShellElement& el, matrix& ke);\n\npublic:\n\tdouble detJ0(FEShellElement& el, int n);\n\nprotected:\n\tdouble\tm_h0; // TODO: move to base class?\n\nprotected:\n\tstd::vector<FEShellElement>\tm_Elem;\n\tFEDofList\tm_dof;\n\tFERigidMaterial* m_pMat;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidSolidDomain.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/FEMesh.h>\n#include \"FEBodyForce.h\"\n\n//-----------------------------------------------------------------------------\nFERigidSolidDomain::FERigidSolidDomain(FEModel* pfem) : FEElasticSolidDomain(pfem) {}\n\n//-----------------------------------------------------------------------------\n// NOTE: Although this function doesn't do anything we need it because we don't\n//       want to call the FEElasticSolidDomain::Initialize function.\nbool FERigidSolidDomain::Init()\n{\n\tif (FESolidDomain::Init() == false) return false;\n\n\t// set the rigid nodes ID\n\t// Altough this is already done in FERigidSystem::CreateObjects()\n\t// we do it here again since the mesh adaptors will call this function\n\t// and they may have added some nodes\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(m_pMat); assert(pm);\n\tif (pm == nullptr) return false;\n\tint rbid = pm->GetRigidBodyID();\n\tfor (int i = 0; i < Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tnode.m_rid = rbid;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// We need to override it since the base class version will not work for rigid domains.\nvoid FERigidSolidDomain::Reset()\n{\n\t// nothing to reset here\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the stiffness matrix for 3D rigid elements.\n//! Rigid elements don't generate stress, so there is nothing to do here\nvoid FERigidSolidDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Caught you looking!\n}\n\n//-----------------------------------------------------------------------------\n// Rigid bodies do not generate stress so there is nothing to do here\nvoid FERigidSolidDomain::InternalForces(FEGlobalVector& R)\n{\n\t// what you looking at ?!\n}\n\n//-----------------------------------------------------------------------------\n//! We don't need to update the stresses for rigid elements\n//!\nvoid FERigidSolidDomain::Update(const FETimeInfo& tp)\n{\n\t// Nothing to see here. Please move on.\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolidDomain::MassMatrix(FELinearSystem& LS, double scale)\n{\n\t// Only crickets here ... \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolidDomain::InertialForces(FEGlobalVector& R, std::vector<double>& F)\n{\n\t// chirp, chirp ...\n}\n\n//-----------------------------------------------------------------------------\nmat3d FERigidSolidDomain::CalculateMOI()\n{\n\tmat3dd I(1); // identity tensor\n\n\tmat3d moi; moi.zero();\n\n#pragma omp parallel \n\t{\n\t\tvec3d r0[FEElement::MAX_NODES];\n\t\tmat3d moi_n; moi_n.zero();\n\n\t\t// loop over all elements\n\t\tint NE = Elements();\n#pragma omp for nowait\n\t\tfor (int iel = 0; iel < NE; ++iel)\n\t\t{\n\t\t\tFESolidElement& el = Element(iel);\n\n\t\t\t// initial coordinates\n\t\t\tint neln = el.Nodes();\n\t\t\tfor (int i = 0; i < neln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).m_r0;\n\n\t\t\t// loop over integration points\n\t\t\tdouble* gw = el.GaussWeights();\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdouble Jw = detJ0(el, n) * gw[n];\n\n\t\t\t\t// shape functions at integration point\n\t\t\t\tdouble* H = el.H(n);\n\n\t\t\t\tdouble dens = m_pMat->Density(mp);\n\n\t\t\t\t// add to moi\n\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t{\n\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tmat3d Iij = (r0[i] * r0[j]) * I - (r0[i] & r0[j]);\n\t\t\t\t\t\tmoi_n += Iij * (H[i] * H[j] * Jw * dens);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#pragma omp critical\n\t\tmoi += moi_n;\n\t}\n\n\treturn moi;\n}\n\ndouble FERigidSolidDomain::CalculateMass()\n{\n\tdouble mass = 0.0;\n\tint NE = Elements();\n#pragma omp parallel for reduction(+:mass)\n\tfor (int iel = 0; iel < NE; ++iel)\n\t{\n\t\tFESolidElement& el = Element(iel);\n\n\t\t// loop over integration points\n\t\tint nint = el.GaussPoints();\n\t\tdouble* gw = el.GaussWeights();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t// calculate jacobian\n\t\t\tdouble detJ = detJ0(el, n);\n\n\t\t\t// add to total mass\n\t\t\tmass += m_pMat->Density(mp) * detJ * gw[n];\n\t\t}\n\t}\n\treturn mass;\n}\n\nvec3d FERigidSolidDomain::CalculateCOM()\n{\n\tvec3d rc(0, 0, 0);\n\n\t// loop over all elements\n\tint NE = Elements();\n\t#pragma omp parallel\n\t{\n\t\tvector<vec3d> r0(FEElement::MAX_NODES);\n\t\tvec3d rc_n(0, 0, 0);\n#pragma omp for nowait\n\t\tfor (int iel = 0; iel < NE; ++iel)\n\t\t{\n\t\t\tFESolidElement& el = Element(iel);\n\n\t\t\t// nr of integration points\n\t\t\tint nint = el.GaussPoints();\n\n\t\t\t// number of nodes\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// initial coordinates\n\t\t\tfor (int i = 0; i < neln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).m_r0;\n\n\t\t\t// integration weights\n\t\t\tdouble* gw = el.GaussWeights();\n\n\t\t\t// loop over integration points\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdouble detJ = detJ0(el, n);\n\n\t\t\t\t// shape functions at integration point\n\t\t\t\tdouble* H = el.H(n);\n\n\t\t\t\tdouble dens = m_pMat->Density(mp);\n\n\t\t\t\t// add to com and moi\n\t\t\t\tfor (int i = 0; i < el.Nodes(); ++i)\n\t\t\t\t{\n\t\t\t\t\trc_n += r0[i] * H[i] * detJ * gw[n] * dens;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t#pragma omp critical\n\t\trc += rc_n;\n\t}\n\n\treturn rc;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolidDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n\t// define some parameters that will be passed to lambda\n\tFEBodyForce* bodyForce = &BF;\n\n\t// evaluate the residual contribution\n\tLoadVector(R, m_dofU, [=](FEMaterialPoint& mp, int node_a, std::vector<double>& fa) {\n\n\t\t// evaluate density\n\t\tdouble density = m_pMat->Density(mp);\n\n\t\t// get the force\n\t\tvec3d f = bodyForce->force(mp);\n\n\t\t// get element shape functions\n\t\tdouble* H = mp.m_shape;\n\n\t\t// get the initial Jacobian\n\t\tdouble J0 = mp.m_J0;\n\n\t\t// set integrand\n\t\tfa[0] = -H[node_a] * density * f.x * J0;\n\t\tfa[1] = -H[node_a] * density * f.y * J0;\n\t\tfa[2] = -H[node_a] * density * f.z * J0;\n\t});\n}\n"
  },
  {
    "path": "FEBioMech/FERigidSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! domain class for 3D rigid elements\n//!\nclass FERigidSolidDomain : public FEElasticSolidDomain\n{\npublic:\n\t//! constructor\n\tFERigidSolidDomain(FEModel* pfem);\n\n\t//! Initialize\n\tbool Init() override;\n\n\t//! reset data\n\tvoid Reset() override;\n\npublic:\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculates the residual (nothing to do)\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculates mass matrix (nothing to do)\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override;\n\n\t//! calculates the inertial forces (nothing to do)\n\tvoid InertialForces(FEGlobalVector& R, std::vector<double>& F) override;\n\n\t// update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& BF) override;\n\npublic:\n\t// calculate contribution of MOI for this domain\n\tmat3d CalculateMOI();\n\n\tdouble CalculateMass();\n\n\tvec3d CalculateCOM();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidSolver.h\"\n#include \"FESolidSolver2.h\"\n#include \"FERigidMaterial.h\"\n#include \"FERigidBody.h\"\n#include <FECore/FEModel.h>\n#include \"RigidBC.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/SparseMatrix.h>\n#include <FECore/log.h>\n#include <FECore/FEMaterial.h>\n#include \"FEMechModel.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FESolidAnalysis.h\"\n\nFERigidSolver::FERigidSolver(FEModel* fem)\n{\n\tm_fem = dynamic_cast<FEMechModel*>(fem);\n\n\tm_dofX = m_dofY = m_dofZ = -1;\n\n\tm_bAllowMixedBCs = false;\n}\n\nint FERigidSolver::InitEquations(int neq)\n{\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tif (m_fem == nullptr) return neq;\n\n\tint nrb = m_fem->RigidBodies();\n\tfor (int i = 0; i<nrb; ++i)\n\t{\n\t\tFERigidBody& RB = *m_fem->GetRigidBody(i);\n\t\tfor (int j = 0; j<6; ++j)\n\t\t{\n\t\t\tint bcj = RB.m_BC[j];\n\t\t\tint lmj = RB.m_LM[j];\n\t\t\tif      (bcj == DOF_OPEN      ) { RB.m_LM[j] = neq; neq++; }\n\t\t\telse if (bcj == DOF_PRESCRIBED) { RB.m_LM[j] = -neq - 2; neq++; }\n\t\t\telse if (bcj == DOF_FIXED     ) { RB.m_LM[j] = -1; }\n\t\t\telse { assert(false); return -1; }\n\t\t}\n\t}\n\n\t// get the DOF indices\n\tm_dofX = m_fem->GetDOFIndex(\"x\");\n\tm_dofY = m_fem->GetDOFIndex(\"y\");\n\tm_dofZ = m_fem->GetDOFIndex(\"z\");\n\tm_dofVX = m_fem->GetDOFIndex(\"vx\");\n\tm_dofVY = m_fem->GetDOFIndex(\"vy\");\n\tm_dofVZ = m_fem->GetDOFIndex(\"vz\");\n    m_dofU = m_fem->GetDOFIndex(\"u\");\n    m_dofV = m_fem->GetDOFIndex(\"v\");\n    m_dofW = m_fem->GetDOFIndex(\"w\");\n    m_dofSX = m_fem->GetDOFIndex(\"sx\");\n    m_dofSY = m_fem->GetDOFIndex(\"sy\");\n    m_dofSZ = m_fem->GetDOFIndex(\"sz\");\n    m_dofSVX = m_fem->GetDOFIndex(\"svx\");\n    m_dofSVY = m_fem->GetDOFIndex(\"svy\");\n    m_dofSVZ = m_fem->GetDOFIndex(\"svz\");\n\tint dofRU = m_fem->GetDOFIndex(\"Ru\");\n\tint dofRV = m_fem->GetDOFIndex(\"Rv\");\n\tint dofRW = m_fem->GetDOFIndex(\"Rw\");\n\n\t// we assign the rigid body equation number to\n\t// Also make sure that the nodes are NOT constrained!\n\tFEMesh& mesh = m_fem->GetMesh();\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tFERigidBody& RB = *m_fem->GetRigidBody(node.m_rid);\n\t\t\tnode.m_ID[m_dofX] = (RB.m_LM[0] >= 0 ? -RB.m_LM[0] - 2 : RB.m_LM[0]);\n\t\t\tnode.m_ID[m_dofY] = (RB.m_LM[1] >= 0 ? -RB.m_LM[1] - 2 : RB.m_LM[1]);\n\t\t\tnode.m_ID[m_dofZ] = (RB.m_LM[2] >= 0 ? -RB.m_LM[2] - 2 : RB.m_LM[2]);\n\t\t\tnode.m_ID[dofRU] = (RB.m_LM[3] >= 0 ? -RB.m_LM[3] - 2 : RB.m_LM[3]);\n\t\t\tnode.m_ID[dofRV] = (RB.m_LM[4] >= 0 ? -RB.m_LM[4] - 2 : RB.m_LM[4]);\n\t\t\tnode.m_ID[dofRW] = (RB.m_LM[5] >= 0 ? -RB.m_LM[5] - 2 : RB.m_LM[5]);\n\t\t\tif (node.HasFlags(FENode::SHELL) && node.HasFlags(FENode::RIGID_CLAMP)) {\n                node.m_ID[m_dofU] = (RB.m_LM[0] >= 0 ? -RB.m_LM[0] - 2 : RB.m_LM[0]);\n                node.m_ID[m_dofV] = (RB.m_LM[1] >= 0 ? -RB.m_LM[1] - 2 : RB.m_LM[1]);\n                node.m_ID[m_dofW] = (RB.m_LM[2] >= 0 ? -RB.m_LM[2] - 2 : RB.m_LM[2]);\n                node.m_ID[m_dofSX] = (RB.m_LM[0] >= 0 ? -RB.m_LM[0] - 2 : RB.m_LM[0]);\n                node.m_ID[m_dofSY] = (RB.m_LM[1] >= 0 ? -RB.m_LM[1] - 2 : RB.m_LM[1]);\n                node.m_ID[m_dofSZ] = (RB.m_LM[2] >= 0 ? -RB.m_LM[2] - 2 : RB.m_LM[2]);\n            }\n\t\t}\n\t}\n\n\treturn neq;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialization\nvoid FERigidSolver::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_dofX & m_dofY & m_dofZ;\n\tar & m_dofVX & m_dofVY & m_dofVZ;\n\tar & m_dofU & m_dofV & m_dofW;\n    ar & m_dofSX & m_dofSY & m_dofSZ;\n    ar & m_dofSVX & m_dofSVY & m_dofSVZ;\n\tar & m_bAllowMixedBCs;\n}\n\n//-----------------------------------------------------------------------------\n//! return the FEModel\nFEModel* FERigidSolver::GetFEModel()\n{\n\treturn m_fem;\n}\n\n//-----------------------------------------------------------------------------\n// \\todo: eliminate need for ui parameter\nvoid FERigidSolver::PrepStep(const FETimeInfo& timeInfo, vector<double>& ui)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tint NO = fem.RigidBodies();\n\tfor (int i = 0; i<NO; ++i) fem.GetRigidBody(i)->Init();\n\n\t// calculate local rigid displacements\n\tint NRBC = fem.RigidBCs();\n\tfor (int i = 0; i < NRBC; ++i)\n\t{\n\t\tFERigidBC& rbc = *fem.GetRigidBC(i);\n\t\tif (rbc.IsActive()) rbc.InitTimeStep();\n\t}\n\n\t// calculate global rigid displacements\n\tfor (int i = 0; i<NO; ++i)\n\t{\n\t\tFERigidBody* prb = fem.GetRigidBody(i);\n\t\tif (prb)\n\t\t{\n\t\t\tFERigidBody& RB = *prb;\n\t\t\tif (RB.m_prb == 0)\n\t\t\t{\n\t\t\t\t// if all rotation dofs are fixed or prescribed, set the flag\n\t\t\t\tif (m_bAllowMixedBCs==false)\n\t\t\t\t{\n\t\t\t\t\tRB.m_bpofr = false;\n\t\t\t\t\tif (RB.m_pDC[3] || RB.m_pDC[4] || RB.m_pDC[5])\n\t\t\t\t\t{\n\t\t\t\t\t\tbool bpofr[3] = { false };\n\t\t\t\t\t\tfor (int j = 3; j<6; ++j) if (RB.m_pDC[j] || (RB.m_LM[j] < 0)) bpofr[j - 3] = true;\n\t\t\t\t\t\tif (bpofr[0] && bpofr[1] && bpofr[2]) RB.m_bpofr = true;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfeLogError(\"Rigid body rotations cannot mix prescribed and free components.\\nRigid body: %d, Material: %d\\n\", RB.m_nID, RB.GetMaterialID());\n\t\t\t\t\t\t\tthrow \"FATAL ERROR\";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// see if all or none prescribed dofs are relative\n\t\t\t\t\t\tbool br[3] = { false, false, false };\n\t\t\t\t\t\tfor (int j = 3; j < 6; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFERigidPrescribedBC* dc = RB.m_pDC[j];\n\t\t\t\t\t\t\tif (dc && dc->GetRelativeFlag()) br[j - 3] = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbool bf = ((br[0] == br[1]) && (br[1] == br[2]) && (br[0] == br[2]));\n\t\t\t\t\t\tif (bf==false)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfeLogError(\"All or none rigid rotations must have relative flag set.\\nRigid body: %d, Material: %d\\n\", RB.m_nID, RB.GetMaterialID());\n\t\t\t\t\t\t\tthrow \"FATAL ERROR\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (int j = 0; j<6; ++j) RB.m_du[j] = RB.m_dul[j];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble* dul = RB.m_dul;\n\t\t\t\tvec3d dr = vec3d(dul[0], dul[1], dul[2]);\n\n\t\t\t\tvec3d v = vec3d(dul[3], dul[4], dul[5]);\n\t\t\t\tdouble w = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);\n\t\t\t\tquatd dq = quatd(w, v);\n\n\t\t\t\tFERigidBody* pprb = RB.m_prb;\n\n\t\t\t\tvec3d r0 = RB.m_rt;\n\t\t\t\tquatd Q0 = RB.GetRotation();\n\n\t\t\t\tdr = Q0*dr;\n\t\t\t\tdq = Q0*dq*Q0.Inverse();\n\n\t\t\t\twhile (pprb)\n\t\t\t\t{\n\t\t\t\t\tvec3d r1 = pprb->m_rt;\n\t\t\t\t\tdul = pprb->m_dul;\n\n\t\t\t\t\tquatd Q1 = pprb->GetRotation();\n\n\t\t\t\t\tdr = r0 + dr - r1;\n\n\t\t\t\t\t// grab the parent's local displacements\n\t\t\t\t\tvec3d dR = vec3d(dul[0], dul[1], dul[2]);\n\t\t\t\t\tv = vec3d(dul[3], dul[4], dul[5]);\n\t\t\t\t\tw = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);\n\t\t\t\t\tquatd dQ = quatd(w, v);\n\n\t\t\t\t\tdQ = Q1*dQ*Q1.Inverse();\n\n\t\t\t\t\t// update global displacements\n\t\t\t\t\tquatd Qi = Q1.Inverse();\n\t\t\t\t\tdr = dR + r1 + dQ*dr - r0;\n\t\t\t\t\tdq = dQ*dq;\n\n\t\t\t\t\t// move up in the chain\n\t\t\t\t\tpprb = pprb->m_prb;\n\t\t\t\t\tQ0 = Q1;\n\t\t\t\t}\n\n\t\t\t\t// set global displacements\n\t\t\t\tdouble* du = RB.m_du;\n\n\t\t\t\tdu[0] = dr.x;\n\t\t\t\tdu[1] = dr.y;\n\t\t\t\tdu[2] = dr.z;\n\n\t\t\t\tv = dq.GetVector();\n\t\t\t\tw = dq.GetAngle();\n\t\t\t\tdu[3] = w*v.x;\n\t\t\t\tdu[4] = w*v.y;\n\t\t\t\tdu[5] = w*v.z;\n\t\t\t}\n\t\t}\n\t}\n\n\t// store rigid displacements in Ui vector\n\tfor (int i = 0; i<NO; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j<6; ++j)\n\t\t{\n\t\t\tint I = -RB.m_LM[j] - 2;\n\t\t\tif (I >= 0) ui[I] = RB.m_du[j];\n\t\t}\n\t}\n\n    for (int i = 0; i<NO; ++i)\n    {\n        FERigidBody& RB = *fem.GetRigidBody(i);\n\n        quatd q = RB.GetRotation()*RB.m_qp.Inverse();\n        q.MakeUnit();\n\n        // update RB variables\n        // translation\n        RB.m_rp = RB.m_rt;\n        RB.m_vp = RB.m_vt;\n        RB.m_ap = RB.m_at;\n        // rotation\n        RB.m_qp = RB.GetRotation();\n        RB.m_wp = RB.m_wt;\n        RB.m_alp = RB.m_alt;\n        // angular momentum\n        RB.m_hp = RB.m_ht;\n        RB.m_dhp = RB.m_dht;\n        \n        // rigid body reaction force and moment\n        RB.m_Fp = RB.m_Fr;\n        RB.m_Mp = RB.m_Mr;\n\n        // estimate RB kinematics at current time\n        double dt = timeInfo.timeIncrement;\n        double beta = timeInfo.beta;\n        double gamma = timeInfo.gamma;\n        double a = 1.0 / (beta*dt);\n        double b = a / dt;\n        double c = 1.0 - 0.5 / beta;\n        // acceleration and velocity of center of mass\n        RB.m_at = RB.m_ap*c - RB.m_vp*a;\n        RB.m_vt = RB.m_vp + (RB.m_at*gamma + RB.m_ap*(1-gamma))*dt;\n        // angular acceleration and velocity of rigid body\n        vec3d vq = q.GetVector()*(2 * tan(q.GetAngle() / 2));  // Cayley transform\n        RB.m_wt = vq*(a*gamma) - RB.m_wp + (RB.m_wp + RB.m_alp*dt / 2.)*(2 - gamma / beta);\n        q.RotateVector(RB.m_wt);\n        RB.m_alt = vq*b - RB.m_wp*a + RB.m_alp*c;\n        q.RotateVector(RB.m_alt);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the rigid stiffness matrices\nvoid FERigidSolver::RigidStiffness(SparseMatrix& K, vector<double>& ui, vector<double>& F, const FEElementMatrix& ke, double alpha)\n{\n\tif (m_fem == nullptr) return;\n\n\tconst vector<int>& en = ke.Nodes();\n    int n = (int)en.size();\n    FEMesh& mesh = m_fem->GetMesh();\n    \n    bool bclamped_shell = false;\n    for (int j = 0; j<n; ++j) \n\t{\n\t\tif (en[j] >= 0)\n\t\t{\n\t\t\tif (mesh.Node(en[j]).HasFlags(FENode::SHELL) && mesh.Node(en[j]).HasFlags(FENode::RIGID_CLAMP)) {\n\t\t\t\tbclamped_shell = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n    }\n    if (bclamped_shell)\n        RigidStiffnessShell(K, ui, F, en, ke.RowIndices(), ke.ColumnsIndices(), ke, alpha);\n    else\n        RigidStiffnessSolid(K, ui, F, en, ke.RowIndices(), ke.ColumnsIndices(), ke, alpha);\n    return;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the rigid stiffness matrices\n//! correct stiffness matrix for rigid-solid interfaces\nvoid FERigidSolver::RigidStiffnessSolid(SparseMatrix& K, vector<double>& ui, vector<double>& F, const vector<int>& en, const vector<int>& elmi, const std::vector<int>& elmj, const matrix& ke, double alpha)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tif (fem.RigidBodies() == 0) return;\n\tif (en.empty()) return;\n\n\tint n = (int)en.size();\n\n\t// get nodal DOFS\n\tDOFS& fedofs = m_fem->GetDOFS();\n\tint MAX_NDOFS = fedofs.GetTotalDOFS();\n\n\tint ndof = ke.columns() / n;\n\n\tmatrix kij(ndof, ndof);\n\tmatrix KF(ndof, 6);\n\n\tdouble KR[6][6];\n\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\t// loop over columns\n\tfor (int j = 0; j<n; ++j)\n\t{\n\t\tif (en[j] >= 0)\n\t\t{\n\t\t\tFENode& nodej = mesh.Node(en[j]);\n\t\t\tif (nodej.m_rid >= 0)\n\t\t\t{\n\t\t\t\t// this is a rigid interface node\n\t\t\t\t// get the rigid body this node is attached to\n\t\t\t\tFERigidBody& RBj = *fem.GetRigidBody(nodej.m_rid);\n\n\t\t\t\t// get the rigid body equation nrs.\n\t\t\t\tint* lmj = RBj.m_LM;\n\n\t\t\t\t// get the relative distance to the center of mass\n\t\t\t\tvec3d zj = nodej.m_rt - RBj.m_rt;\n\t\t\t\tmat3d Zj; Zj.skew(zj);\n\n\t\t\t\t// loop over rows\n\t\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t\t{\n\t\t\t\t\t// get the element sub-matrix\n\t\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\t\tfor (int l = 0; l < ndof; ++l)\n\t\t\t\t\t\t\tkij[k][l] = ke[ndof*i + k][ndof*j + l];\n\n\t\t\t\t\tmat3d Kuu(kij[0][0], kij[0][1], kij[0][2],\n\t\t\t\t\t\tkij[1][0], kij[1][1], kij[1][2],\n\t\t\t\t\t\tkij[2][0], kij[2][1], kij[2][2]);\n\n\t\t\t\t\tif (en[i] >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& nodei = mesh.Node(en[i]);\n\n\t\t\t\t\t\tif (nodei.m_rid >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// node i is also a rigid body node\n\t\t\t\t\t\t\t// get the rigid body this node is attached to\n\t\t\t\t\t\t\tFERigidBody& RBi = *fem.GetRigidBody(nodei.m_rid);\n\n\t\t\t\t\t\t\tint* lmi = RBi.m_LM;\n\n\t\t\t\t\t\t\t// get the relative distance (use alpha rule)\n\t\t\t\t\t\t\tvec3d zi = (nodei.m_rt - RBi.m_rt)*alpha + (nodei.m_rp - RBi.m_rp)*(1 - alpha);\n\t\t\t\t\t\t\tmat3d Zi; Zi.skew(zi);\n\n\t\t\t\t\t\t\tmat3d M;\n\n\t\t\t\t\t\t\t// Kuu transformation to Krr\n\t\t\t\t\t\t\tM = Kuu;\n\t\t\t\t\t\t\tKR[0][0] = M[0][0]; KR[0][1] = M[0][1]; KR[0][2] = M[0][2];\n\t\t\t\t\t\t\tKR[1][0] = M[1][0]; KR[1][1] = M[1][1]; KR[1][2] = M[1][2];\n\t\t\t\t\t\t\tKR[2][0] = M[2][0]; KR[2][1] = M[2][1]; KR[2][2] = M[2][2];\n\n\n\t\t\t\t\t\t\t// Kuu transformation to Krq\n\t\t\t\t\t\t\tM = Kuu * Zj*(-1);\n\t\t\t\t\t\t\tKR[0][3] = M[0][0]; KR[0][4] = M[0][1]; KR[0][5] = M[0][2];\n\t\t\t\t\t\t\tKR[1][3] = M[1][0]; KR[1][4] = M[1][1]; KR[1][5] = M[1][2];\n\t\t\t\t\t\t\tKR[2][3] = M[2][0]; KR[2][4] = M[2][1]; KR[2][5] = M[2][2];\n\n\n\t\t\t\t\t\t\t// Kuu transformation to Kqr\n\t\t\t\t\t\t\tM = Zi * Kuu;\n\t\t\t\t\t\t\tKR[3][0] = M[0][0]; KR[3][1] = M[0][1]; KR[3][2] = M[0][2];\n\t\t\t\t\t\t\tKR[4][0] = M[1][0]; KR[4][1] = M[1][1]; KR[4][2] = M[1][2];\n\t\t\t\t\t\t\tKR[5][0] = M[2][0]; KR[5][1] = M[2][1]; KR[5][2] = M[2][2];\n\n\n\t\t\t\t\t\t\t// Kuu transformation to Kqq\n\t\t\t\t\t\t\tM = Zi * Kuu*Zj*(-1);\n\t\t\t\t\t\t\tKR[3][3] = M[0][0]; KR[3][4] = M[0][1]; KR[3][5] = M[0][2];\n\t\t\t\t\t\t\tKR[4][3] = M[1][0]; KR[4][4] = M[1][1]; KR[4][5] = M[1][2];\n\t\t\t\t\t\t\tKR[5][3] = M[2][0]; KR[5][4] = M[2][1]; KR[5][5] = M[2][2];\n\n\t\t\t\t\t\t\t// add the stiffness components to the Krr matrix\n\t\t\t\t\t\t\tfor (int k = 0; k < 6; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 0; l < 6; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint J = lmj[k];\n\t\t\t\t\t\t\t\t\tint I = lmi[l];\n\n\t\t\t\t\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t// multiply KR by alpha for alpha rule\n\t\t\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\t\t\tF[I] -= KR[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse if (J >= 0) K.add(I, J, KR[l][k]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// we still need to couple the non-rigid degrees of node i to the\n\t\t\t\t\t\t\t// rigid dofs of node j\n\t\t\t\t\t\t\tfor (int k = 3; k < ndof; ++k) {\n\t\t\t\t\t\t\t\tvec3d kpu(kij[k][0], kij[k][1], kij[k][2]);\n\t\t\t\t\t\t\t\tvec3d m = kpu;\n\t\t\t\t\t\t\t\tKF[k][0] = m.x; KF[k][1] = m.y; KF[k][2] = m.z;\n\t\t\t\t\t\t\t\tm = Zj * kpu;\n\t\t\t\t\t\t\t\tKF[k][3] = m.x; KF[k][4] = m.y; KF[k][5] = m.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < 6; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 3; l < ndof; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint J = lmj[k];\n\t\t\t\t\t\t\t\t\tint I = elmi[ndof*i + l];\n\n\t\t\t\t\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t// multiply KF by alpha for alpha rule\n\t\t\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse if (J >= 0) K.add(I, J, KF[l][k]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// now the transpose location\n\t\t\t\t\t\t\tfor (int l = 3; l < ndof; ++l) {\n\t\t\t\t\t\t\t\tvec3d kup(kij[0][l], kij[1][l], kij[2][l]);\n\t\t\t\t\t\t\t\tvec3d m = Zi * kup;\n\t\t\t\t\t\t\t\tKF[l][0] = kup.x; KF[l][1] = kup.y; KF[l][2] = kup.z;\n\t\t\t\t\t\t\t\tKF[l][3] = m.x; KF[l][4] = m.y; KF[l][5] = m.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < 6; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 3; l < ndof; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint J = elmj[ndof*j + l];\n\t\t\t\t\t\t\t\t\tint I = lmi[k];\n\n\t\t\t\t\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse if (J >= 0) K.add(I, J, KF[l][k]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// node i is not a rigid body node\n\t\t\t\t\t\t\t// add the stiffness components to the Kfr matrix\n\n\t\t\t\t\t\t\t// Kij\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k) {\n\t\t\t\t\t\t\t\tvec3d kpu(kij[k][0], kij[k][1], kij[k][2]);\n\t\t\t\t\t\t\t\tvec3d m = kpu;\n\t\t\t\t\t\t\t\tKF[k][0] = m.x; KF[k][1] = m.y; KF[k][2] = m.z;\n\t\t\t\t\t\t\t\tm = Zj * kpu;\n\t\t\t\t\t\t\t\tKF[k][3] = m.x; KF[k][4] = m.y; KF[k][5] = m.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < 6; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 0; l < ndof; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint J = lmj[k];\n\t\t\t\t\t\t\t\t\tint I = elmi[ndof*i + l];\n\n\t\t\t\t\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t// multiply KF by alpha for alpha rule\n\t\t\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse if (J >= 0) K.add(I, J, KF[l][k]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// loop over rows\n\t\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t\t{\n\t\t\t\t\tif (en[i] >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& nodei = mesh.Node(en[i]);\n\t\t\t\t\t\tif (nodei.m_rid >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// node i is a rigid body\n\t\t\t\t\t\t\t// get the rigid body this node is attached to\n\t\t\t\t\t\t\tFERigidBody& RBi = *fem.GetRigidBody(nodei.m_rid);\n\n\t\t\t\t\t\t\t// get the rigid body equation nrs.\n\t\t\t\t\t\t\tint* lmi = RBi.m_LM;\n\n\t\t\t\t\t\t\t// get the relative distance (use alpha rule)\n\t\t\t\t\t\t\tvec3d zi = (nodei.m_rt - RBi.m_rt)*alpha + (nodei.m_rp - RBi.m_rp)*(1 - alpha);\n\t\t\t\t\t\t\tmat3d Zi; Zi.skew(zi);\n\n\t\t\t\t\t\t\t// get the element sub-matrix\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 0; l < ndof; ++l)\n\t\t\t\t\t\t\t\t\tkij[k][l] = ke[ndof*i + k][ndof*j + l];\n\n\t\t\t\t\t\t\t// add the stiffness components to the Krf matrix\n\n\t\t\t\t\t\t\t// Kij\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k) {\n\t\t\t\t\t\t\t\tvec3d kup(kij[0][k], kij[1][k], kij[2][k]);\n\t\t\t\t\t\t\t\tvec3d m = Zi * kup;\n\t\t\t\t\t\t\t\tKF[k][0] = kup.x; KF[k][1] = kup.y; KF[k][2] = kup.z;\n\t\t\t\t\t\t\t\tKF[k][3] = m.x; KF[k][4] = m.y; KF[k][5] = m.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < 6; ++k)\n\t\t\t\t\t\t\t\tfor (int l = 0; l < ndof; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint I = lmi[k];\n\t\t\t\t\t\t\t\t\tint J = elmj[ndof*j + l];\n\n\t\t\t\t\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse if (J >= 0) K.add(I, J, KF[l][k]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the rigid stiffness matrices\n//! correct stiffness matrix for rigid bodies accounting for rigid-body-deformable-shell interfaces\nvoid FERigidSolver::RigidStiffnessShell(SparseMatrix& K, vector<double>& ui, vector<double>& F, const vector<int>& en, const vector<int>& elmi, const vector<int>& elmj, const matrix& ke, double alpha)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tif (fem.RigidBodies() == 0) return;\n    \n    int i, j, k, l, n = (int)en.size();\n    \n    // get nodal DOFS\n    DOFS& fedofs = m_fem->GetDOFS();\n    int MAX_NDOFS = fedofs.GetTotalDOFS();\n    \n    vector< vector<double> > kij; kij.assign(MAX_NDOFS, vector<double>(MAX_NDOFS));\n    \n    vector< vector<double> > KF; KF.assign(MAX_NDOFS, vector<double>(6));\n    double KR[6][6];\n    \n    int *lmi, *lmj;\n    int I, J;\n    \n    vec3d ai, aj, bi, bj;\n    mat3d Ai, Aj, Bi, Bj;\n    \n    int ndof = ke.columns() / n;\n    \n    FEMesh& mesh = m_fem->GetMesh();\n    \n    // loop over columns\n    for (j = 0; j<n; ++j)\n    {\n        FENode& nodej = mesh.Node(en[j]);\n        if (nodej.m_rid >= 0)\n        {\n            // this is a rigid interface node\n            // get the rigid body this node is attached to\n            FERigidBody& RBj = *fem.GetRigidBody(nodej.m_rid);\n            \n            // get the rigid body equation nrs.\n            lmj = RBj.m_LM;\n            \n            // get the relative distance to the center of mass\n            aj = nodej.m_rt - RBj.m_rt;\n            Aj.skew(aj);\n            \n            // get the shell director\n            bj = aj - nodej.m_dt;\n            Bj.skew(bj);\n            \n            // loop over rows\n            for (i = 0; i<n; ++i)\n            {\n                // get the element sub-matrix\n                for (k = 0; k<ndof; ++k)\n                    for (l = 0; l<ndof; ++l)\n                        kij[k][l] = ke[ndof*i + k][ndof*j + l];\n                \n                mat3d Kuu(kij[0][0], kij[0][1], kij[0][2],\n                          kij[1][0], kij[1][1], kij[1][2],\n                          kij[2][0], kij[2][1], kij[2][2]);\n                \n                mat3d Kuw(kij[0][3], kij[0][4], kij[0][5],\n                          kij[1][3], kij[1][4], kij[1][5],\n                          kij[2][3], kij[2][4], kij[2][5]);\n                \n                mat3d Kwu(kij[3][0], kij[3][1], kij[3][2],\n                          kij[4][0], kij[4][1], kij[4][2],\n                          kij[5][0], kij[5][1], kij[5][2]);\n                \n                mat3d Kww(kij[3][3], kij[3][4], kij[3][5],\n                          kij[4][3], kij[4][4], kij[4][5],\n                          kij[5][3], kij[5][4], kij[5][5]);\n                \n                FENode& nodei = mesh.Node(en[i]);\n                \n                if (nodei.m_rid >= 0)\n                {\n                    // node i is also a rigid body node\n                    // get the rigid body this node is attached to\n                    FERigidBody& RBi = *fem.GetRigidBody(nodei.m_rid);\n                    \n                    lmi = RBi.m_LM;\n                    \n                    // get the relative distance (use alpha rule)\n                    ai = (nodei.m_rt - RBi.m_rt)*alpha + (nodei.m_rp - RBi.m_rp)*(1 - alpha);\n                    Ai.skew(ai);\n                    \n                    // get the shell director\n                    bi = ai - nodei.m_dt;\n                    Bi.skew(bi);\n                    \n                    mat3d M;\n                    \n                    // Kuu transformation\n                    M = (Kuu + Kwu + Kuw + Kww);\n                    KR[0][0] = M[0][0]; KR[0][1] = M[0][1]; KR[0][2] = M[0][2];\n                    KR[1][0] = M[1][0]; KR[1][1] = M[1][1]; KR[1][2] = M[1][2];\n                    KR[2][0] = M[2][0]; KR[2][1] = M[2][1]; KR[2][2] = M[2][2];\n                    \n                    \n                    // Kuw transformation\n                    M = ((Kuu + Kwu)*Aj + (Kuw + Kww)*Bj)*(-1);\n                    KR[0][3] = M[0][0]; KR[0][4] = M[0][1]; KR[0][5] = M[0][2];\n                    KR[1][3] = M[1][0]; KR[1][4] = M[1][1]; KR[1][5] = M[1][2];\n                    KR[2][3] = M[2][0]; KR[2][4] = M[2][1]; KR[2][5] = M[2][2];\n                    \n                    \n                    // Kwu transformation\n                    M = (Ai*(Kuu + Kuw) + Bi*(Kwu + Kww));\n                    KR[3][0] = M[0][0]; KR[3][1] = M[0][1]; KR[3][2] = M[0][2];\n                    KR[4][0] = M[1][0]; KR[4][1] = M[1][1]; KR[4][2] = M[1][2];\n                    KR[5][0] = M[2][0]; KR[5][1] = M[2][1]; KR[5][2] = M[2][2];\n                    \n                    \n                    // Kww transformation\n                    M = ((Ai*Kuu + Bi*Kwu)*Aj + (Ai*Kuw + Bi*Kww)*Bj)*(-1);\n                    KR[3][3] = M[0][0]; KR[3][4] = M[0][1]; KR[3][5] = M[0][2];\n                    KR[4][3] = M[1][0]; KR[4][4] = M[1][1]; KR[4][5] = M[1][2];\n                    KR[5][3] = M[2][0]; KR[5][4] = M[2][1]; KR[5][5] = M[2][2];\n                    \n                    // add the stiffness components to the Krr matrix\n                    for (k = 0; k<6; ++k)\n                        for (l = 0; l<6; ++l)\n                        {\n                            J = lmj[k];\n                            I = lmi[l];\n                            \n                            if (I >= 0)\n                            {\n                                // multiply KR by alpha for alpha rule\n\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\tF[I] -= KR[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t}\n                                else if (J >= 0) K.add(I, J, KR[l][k]);\n                            }\n                        }\n                    \n                    // we still need to couple the non-rigid degrees of node i to the\n                    // rigid dofs of node j\n                    for (k = 6; k<ndof; ++k) {\n                        vec3d kpu(kij[k][0], kij[k][1], kij[k][2]);\n                        vec3d kpw(kij[k][3], kij[k][4], kij[k][5]);\n                        vec3d m = (kpu + kpw);\n                        KF[k][0] = m.x; KF[k][1] = m.y; KF[k][2] = m.z;\n                        m = (Aj*kpu + Bj*kpw);\n                        KF[k][3] = m.x; KF[k][4] = m.y; KF[k][5] = m.z;\n                    }\n                    \n                    for (k = 0; k<6; ++k)\n                        for (l = 6; l<ndof; ++l)\n                        {\n                            J = lmj[k];\n                            I = elmi[ndof*i + l];\n                            \n                            if (I >= 0)\n                            {\n                                // multiply KF by alpha for alpha rule\n\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t}\n                                else if (J >= 0) K.add(I, J, KF[l][k]);\n                            }\n                        }\n                    \n                    // now the transpose location\n                    for (l = 6; l<ndof; ++l) {\n                        vec3d kup(kij[0][l], kij[1][l], kij[2][l]);\n                        vec3d kwp(kij[3][l], kij[4][l], kij[5][l]);\n                        vec3d m = kup + kwp;\n                        KF[l][0] = m.x; KF[l][1] = m.y; KF[l][2] = m.z;\n                        m = Ai*kup + Bi*kwp;\n                        KF[l][3] = m.x; KF[l][4] = m.y; KF[l][5] = m.z;\n                    }\n                    \n                    for (k = 0; k<6; ++k)\n                        for (l = 6; l<ndof; ++l)\n                        {\n                            J = elmj[ndof*j + l];\n                            I = lmi[k];\n                            \n                            if (I >= 0)\n                            {\n\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t}\n                                else if (J >= 0) K.add(I, J, KF[l][k]);\n                            }\n                        }\n                    \n                }\n                else\n                {\n                    // node i is not a rigid body node\n                    // add the stiffness components to the Kfr matrix\n                    \n                    // Kij\n                    for (k = 0; k<ndof; ++k) {\n                        vec3d kpu(kij[k][0], kij[k][1], kij[k][2]);\n                        vec3d kpw(kij[k][3], kij[k][4], kij[k][5]);\n                        vec3d m = (kpu + kpw);\n                        KF[k][0] = m.x; KF[k][1] = m.y; KF[k][2] = m.z;\n                        m = (Aj*kpu + Bj*kpw);\n                        KF[k][3] = m.x; KF[k][4] = m.y; KF[k][5] = m.z;\n                    }\n                    \n                    for (k = 0; k<6; ++k)\n                        for (l = 0; l<ndof; ++l)\n                        {\n                            J = lmj[k];\n                            I = elmi[ndof*i + l];\n                            \n                            if (I >= 0)\n                            {\n                                // multiply KF by alpha for alpha rule\n\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t}\n                                else if (J >= 0) K.add(I, J, KF[l][k]);\n                            }\n                        }\n                }\n            }\n        }\n        else\n        {\n            // loop over rows\n            for (i = 0; i<n; ++i)\n            {\n                FENode& nodei = mesh.Node(en[i]);\n                if (nodei.m_rid >= 0)\n                {\n                    // node i is a rigid body\n                    // get the rigid body this node is attached to\n                    FERigidBody& RBi = *fem.GetRigidBody(nodei.m_rid);\n                    \n                    // get the rigid body equation nrs.\n                    lmi = RBi.m_LM;\n                    \n                    // get the relative distance (use alpha rule)\n                    ai = (nodei.m_rt - RBi.m_rt)*alpha + (nodei.m_rp - RBi.m_rp)*(1 - alpha);\n                    Ai.skew(ai);\n                    \n                    // get the shell director\n                    bi = ai - nodei.m_dt;\n                    Bi.skew(bi);\n                    \n                    // get the element sub-matrix\n                    for (k = 0; k<ndof; ++k)\n                        for (l = 0; l<ndof; ++l)\n                            kij[k][l] = ke[ndof*i + k][ndof*j + l];\n                    \n                    // add the stiffness components to the Krf matrix\n                    \n                    // Kij\n                    for (k = 0; k<ndof; ++k) {\n                        vec3d kup(kij[0][k], kij[1][k], kij[2][k]);\n                        vec3d kwp(kij[3][k], kij[4][k], kij[5][k]);\n                        vec3d m = kup + kwp;\n                        KF[k][0] = m.x; KF[k][1] = m.y; KF[k][2] = m.z;\n                        m = Ai*kup + Bi*kwp;\n                        KF[k][3] = m.x; KF[k][4] = m.y; KF[k][5] = m.z;\n                    }\n                    \n                    for (k = 0; k<6; ++k)\n                        for (l = 0; l<ndof; ++l)\n                        {\n                            I = lmi[k];\n                            J = elmj[ndof*j + l];\n                            \n                            if (I >= 0)\n                            {\n\t\t\t\t\t\t\t\tif (J < -1) {\n\t\t\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\t\t\tF[I] -= KF[l][k] * ui[-J - 2];\n\t\t\t\t\t\t\t\t}\n                                else if (J >= 0) K.add(I, J, KF[l][k]);\n                            }\n                        }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolver::AssembleResidual(int node_id, int dof, double f, vector<double>& R)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tFEMesh& mesh = m_fem->GetMesh();\n\t\n\t// get the equation number\n\tFENode& node = mesh.Node(node_id);\n\tint n = node.m_ID[dof];\n\n\t// assemble into global vector\n\tif (n >= 0)\n\t{\n\t\t#pragma omp atomic\n\t\tR[n] += f;\n\t}\n\telse if (node.m_rid >= 0)\n\t{\n\t\t// this is a rigid body node\n\t\tFERigidBody& RB = *fem.GetRigidBody(node.m_rid);\n\n\t\t// get the relative position\n\t\tvec3d a = node.m_rt - RB.m_rt;\n\n\t\tint* lm = RB.m_LM;\n\t\tif (dof == m_dofX)\n\t\t{\n\t\t\tif (lm[0] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[0]] += f;\n\t\t\t}\n\t\t\tif (lm[4] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[4]] += a.z * f;\n\t\t\t}\n\t\t\tif (lm[5] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[5]] += -a.y * f;\n\t\t\t}\n        }\n        else if (dof == m_dofY)\n        {\n\t\t\tif (lm[1] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[1]] += f;\n\t\t\t}\n\t\t\tif (lm[3] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[3]] += -a.z * f;\n\t\t\t}\n\t\t\tif (lm[5] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[5]] += a.x * f;\n\t\t\t}\n\t\t}\n\t\telse if (dof == m_dofZ)\n\t\t{\n\t\t\tif (lm[2] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[2]] += f;\n\t\t\t}\n\t\t\tif (lm[3] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[3]] += a.y * f;\n\t\t\t}\n\t\t\tif (lm[4] >= 0) {\n\t\t\t\t#pragma omp atomic\n\t\t\t\tR[lm[4]] += -a.x * f;\n\t\t\t}\n\t\t}\n\t\tif (node.HasFlags(FENode::SHELL) && node.HasFlags(FENode::RIGID_CLAMP)) {\n\t\t\t// get the shell director\n\t\t\tvec3d d = node.m_dt;\n\t\t\tvec3d b = a - d;\n\t\t\tif (dof == m_dofSX)\n\t\t\t{\n\t\t\t\tif (lm[0] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[0]] += f;\n\t\t\t\t}\n\t\t\t\tif (lm[4] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[4]] += b.z * f;\n\t\t\t\t}\n\t\t\t\tif (lm[5] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[5]] += -b.y * f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (dof == m_dofSY)\n\t\t\t{\n\t\t\t\tif (lm[1] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[1]] += f;\n\t\t\t\t}\n\t\t\t\tif (lm[3] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[3]] += -b.z * f;\n\t\t\t\t}\n\t\t\t\tif (lm[5] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[5]] += b.x * f;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (dof == m_dofSZ)\n\t\t\t{\n\t\t\t\tif (lm[2] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[2]] += f;\n\t\t\t\t}\n\t\t\t\tif (lm[3] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[3]] += b.y * f;\n\t\t\t\t}\n\t\t\t\tif (lm[4] >= 0) {\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tR[lm[4]] += -b.x * f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolver::Residual()\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tint NRB = fem.RigidBodies();\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tRB.m_Fr = RB.m_Mr = vec3d(0, 0, 0);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolver::StiffnessMatrix(SparseMatrix& K, const FETimeInfo& tp)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\t// we still need to set the diagonal elements to 1\n\t// for the prescribed rigid body dofs.\n\tint NRB = fem.RigidBodies();\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\tFERigidBody& rb = *fem.GetRigidBody(i);\n\t\tfor (int j = 0; j<6; ++j)\n\t\tif (rb.m_LM[j] < -1)\n\t\t{\n\t\t\tint I = -rb.m_LM[j] - 2;\n\t\t\tK.set(I, I, 1);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contribution to the mass matrix from the rigid bodies\nvoid FERigidSolver::RigidMassMatrix(FELinearSystem& LS, const FETimeInfo& timeInfo)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\t// element stiffness matrix\n\tvector<int> lm;\n\tFEElementMatrix ke;\n\n\t// 6 dofs per rigid body\n\tke.resize(6, 6);\n\n\t// Newmark integration rule\n\tdouble dt = timeInfo.timeIncrement;\n    double alpham = timeInfo.alpham;\n\tdouble beta = timeInfo.beta;\n\tdouble gamma = timeInfo.gamma;\n\tdouble a = 1. / (beta*dt*dt);\n\n\tif (timeInfo.currentTime == 0)\n\t{\n\t\ta = alpham = 1;\n\t}\n\n\tfor (int i=0; i<fem.RigidBodies(); ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\n\t\t// mass matrix\n\t\tdouble M = RB.m_mass*a*alpham;\n\n\t\tke.zero();\n\t\tke[0][0] = M;\n\t\tke[1][1] = M;\n\t\tke[2][2] = M;\n\n\t\t// evaluate mass moment of inertia at t\n\t\tmat3d Rt = RB.GetRotation().RotationMatrix();\n\t\tmat3ds Jt = (Rt*RB.m_moi*Rt.transpose()).sym();\n\n\t\t// incremental rotation in spatial frame\n\t\tquatd q = RB.GetRotation()*RB.m_qp.Inverse();\n\t\tq.MakeUnit();                           // clean-up roundoff errors\n\t\tdouble theta = 2 * tan(q.GetAngle() / 2);   // get theta from Cayley transform\n\t\tvec3d e = q.GetVector();\n\n\t\t// skew-symmetric tensor whose axial vector is the incremental rotation\n\t\tmat3d qhat;\n\t\tqhat.skew(e*theta);\n\n\t\t// generate tensor T(theta)\n\t\tmat3d T = mat3dd(1) + qhat / 2 + dyad(e*theta) / 4;\n\n\t\t// skew-symmetric of angular momentum\n\t\tmat3d hhat;\n\t\thhat.skew(RB.m_ht);\n\n        // skew-symmetric angular velocity\n        mat3d Omega; Omega.skew(RB.m_wt);\n        mat3d Dht; Dht.skew(RB.m_dht);\n        \n\t\t// rotational inertia stiffness\n\t\tmat3d K = ((((Omega*gamma+mat3dd(1./dt))*Jt -  hhat*gamma)/(beta*dt))*T - Dht)*alpham;\n\n\t\tke[3][3] = K(0, 0); ke[3][4] = K(0, 1); ke[3][5] = K(0, 2);\n\t\tke[4][3] = K(1, 0); ke[4][4] = K(1, 1); ke[4][5] = K(1, 2);\n\t\tke[5][3] = K(2, 0); ke[5][4] = K(2, 1); ke[5][5] = K(2, 2);\n\n\t\tlm.assign(RB.m_LM, RB.m_LM + 6);\n\n\t\tke.SetIndices(lm);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//=================================================================================================\n// FERigidSolverOld\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\n//! This function updates the rigid body linear and angular velocity by solving\n//! an overdetermined system of linear equations using the least-square method. \nvoid FERigidSolverOld::UpdateRigidKinematics()\n{\n\t// get the model and mesh\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// loop over all rigid bodies\n\tint NRB = fem.RigidBodies();\n\tfor (int j = 0; j<NRB; ++j)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& rb = *fem.GetRigidBody(j);\n\n\t\t// right-hand side and least-square matrix\n\t\tvector<double> r; r.assign(6, 0.0);\n\t\tmatrix m(6, 6); m.zero();\n\n\t\t// we need to loop over all domains that define this rigid body\n\t\tint ncnt = 0;\n\t\tint NDOM = mesh.Domains();\n\t\tfor (int n = 0; n<NDOM; ++n)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(n);\n\t\t\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\t\tif (prm)\n\t\t\t{\n\t\t\t\tif (prm->GetRigidBodyID() == j)\n\t\t\t\t{\n\t\t\t\t\t// now loop over all the nodes\n\t\t\t\t\tint NN = dom.Nodes();\n\t\t\t\t\tfor (int i = 0; i<NN; ++i, ncnt++)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3d ri = dom.Node(i).m_rt - rb.m_rt;\n\t\t\t\t\t\tvec3d vi = dom.Node(i).get_vec3d(m_dofVX, m_dofVY, m_dofVZ);\n\n\t\t\t\t\t\tvec3d wi = ri ^ vi;\n\n\t\t\t\t\t\t// right-hand side\n\t\t\t\t\t\tr[0] += vi.x;\n\t\t\t\t\t\tr[1] += vi.y;\n\t\t\t\t\t\tr[2] += vi.z;\n\t\t\t\t\t\tr[3] += wi.x;\n\t\t\t\t\t\tr[4] += wi.y;\n\t\t\t\t\t\tr[5] += wi.z;\n\n\t\t\t\t\t\t// least-squares matrix\n\t\t\t\t\t\tm[0][0] += 1.0;\n\t\t\t\t\t\tm[1][1] += 1.0;\n\t\t\t\t\t\tm[2][2] += 1.0;\n\n\t\t\t\t\t\tm[0][4] += ri.z; m[0][5] += -ri.y;\n\t\t\t\t\t\tm[1][3] += -ri.z; m[1][5] += ri.x;\n\t\t\t\t\t\tm[2][3] += ri.y; m[2][4] += -ri.x;\n\n\t\t\t\t\t\tm[3][4] += -ri.z; m[3][5] += ri.y;\n\t\t\t\t\t\tm[4][3] += ri.z; m[4][5] += -ri.x;\n\t\t\t\t\t\tm[5][3] += -ri.y; m[5][4] += ri.x;\n\n\t\t\t\t\t\tm[3][3] += ri.y*ri.y + ri.z*ri.z; m[3][4] += -ri.x*ri.y; m[3][5] += -ri.x*ri.z;\n\t\t\t\t\t\tm[4][4] += ri.x*ri.x + ri.z*ri.z; m[4][3] += -ri.x*ri.y; m[4][5] += -ri.y*ri.z;\n\t\t\t\t\t\tm[5][5] += ri.x*ri.x + ri.y*ri.y; m[5][3] += -ri.x*ri.z; m[5][4] += -ri.y*ri.z;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// solve for the rigid body velocity (if we have enough nodes)\n\t\tif (ncnt > 2)\n\t\t{\n\t\t\tvector<double> VR = r / m;\n\t\t\trb.m_vt = vec3d(VR[0], VR[1], VR[2]);\n\t\t\trb.m_wt = vec3d(VR[3], VR[4], VR[5]);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the rigid body data\nvoid FERigidSolverOld::UpdateRigidBodies(vector<double>& Ui, vector<double>& ui, bool bnewUpdate)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tconst int NRB = fem.RigidBodies();\n\n\t// first calculate the rigid body displacement increments\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tint *lm = RB.m_LM;\n\t\tdouble* du = RB.m_du;\n\n\t\tif (RB.m_prb == 0)\n\t\t{\n\t\t\tfor (int j = 0; j<6; ++j)\n\t\t\t{\n\t\t\t\tdu[j] = (lm[j] >= 0 ? Ui[lm[j]] + ui[lm[j]] : 0);\n\t\t\t}\n\t\t}\n\t}\n\n\t// for prescribed displacements, the displacement increments are evaluated differently\n\t// TODO: Is this really necessary? Why can't the ui vector contain the correct values?\n\tfor (int i = 0; i < NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tif (RB.m_prb == nullptr)\n\t\t{\n\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t{\n\t\t\t\tFERigidPrescribedBC* dc = RB.m_pDC[j];\n\t\t\t\tif (dc && dc->IsActive())\n\t\t\t\t{\n\t\t\t\t\tint I = dc->GetBC();\n\t\t\t\t\tRB.m_du[I] = dc->Value() - RB.m_Up[I];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// update the rigid bodies\n\tfor (int i = 0; i<NRB; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tdouble* du = RB.m_du;\n\n\t\tif (bnewUpdate)\n\t\t{\n\t\t\t// This is the \"new\" update algorithm which addressesses a couple issues\n\t\t\t// with the old method, namely that prescribed rotational dofs aren't update correctly.\n\t\t\t// Unfortunately, it seems to produce worse convergence in some cases, especially with line search\n\t\t\t// and it doesn't work when rigid bodies are used in a hierarchy\n\t\t\tif (RB.m_prb) du = RB.m_dul;\n\t\t\tRB.m_Ut[0] = RB.m_Up[0] + du[0];\n\t\t\tRB.m_Ut[1] = RB.m_Up[1] + du[1];\n\t\t\tRB.m_Ut[2] = RB.m_Up[2] + du[2];\n\t\t\tRB.m_Ut[3] = RB.m_Up[3] + du[3];\n\t\t\tRB.m_Ut[4] = RB.m_Up[4] + du[4];\n\t\t\tRB.m_Ut[5] = RB.m_Up[5] + du[5];\n\n\t\t\tRB.m_rt = RB.m_r0 + vec3d(RB.m_Ut[0], RB.m_Ut[1], RB.m_Ut[2]);\n\n\t\t\tvec3d Rt(RB.m_Ut[3], RB.m_Ut[4], RB.m_Ut[5]);\n\t\t\tRB.SetRotation(quatd(Rt));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// This is the \"old\" update algorithm which has some issues. It does not produce the correct\n\t\t\t// rigid body orientation when the rotational degrees of freedom are prescribed.\n\t\t\tRB.m_rt.x = RB.m_rp.x + du[0];\n\t\t\tRB.m_rt.y = RB.m_rp.y + du[1];\n\t\t\tRB.m_rt.z = RB.m_rp.z + du[2];\n\n\t\t\tvec3d r = vec3d(du[3], du[4], du[5]);\n\t\t\tdouble w = sqrt(r.x*r.x + r.y*r.y + r.z*r.z);\n\t\t\tquatd dq = quatd(w, r);\n\n\t\t\tquatd Q = dq*RB.m_qp;\n\t\t\tQ.MakeUnit();\n\t\t\tRB.SetRotation(Q);\n\n\t\t\tif (RB.m_prb) du = RB.m_dul;\n\t\t\tRB.m_Ut[0] = RB.m_Up[0] + du[0];\n\t\t\tRB.m_Ut[1] = RB.m_Up[1] + du[1];\n\t\t\tRB.m_Ut[2] = RB.m_Up[2] + du[2];\n\t\t\tRB.m_Ut[3] = RB.m_Up[3] + du[3];\n\t\t\tRB.m_Ut[4] = RB.m_Up[4] + du[4];\n\t\t\tRB.m_Ut[5] = RB.m_Up[5] + du[5];\n\t\t}\n\t}\n\n\t// we need to update the position of rigid nodes\n\tfem.UpdateRigidMesh();\n\n\t// Since the rigid nodes are repositioned we need to update the displacement DOFS\n\tFEMesh& mesh = m_fem->GetMesh();\n\tint N = mesh.Nodes();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tvec3d ut = node.m_rt - node.m_r0;\n\t\t\tnode.set_vec3d(m_dofX, m_dofY, m_dofZ, ut);\n\t\t}\n\t}\n}\n\n//=================================================================================================\n// FERigidSolverNew\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nvoid FERigidSolverNew::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tint nrb = fem.RigidBodies();\n\tfor (int i = 0; i<nrb; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tif (RB.m_prb == 0)\n\t\t{\n\t\t\tint *lm = RB.m_LM;\n\t\t\tvec3d v;\n\t\t\tquatd qUi;\n\n\t\t\t// first do the displacements\n\t\t\tfor (int j = 0; j<3; ++j)\n\t\t\tif (lm[j] >= 0) Ui[lm[j]] += ui[lm[j]];\n\n\t\t\t// next, we do the rotations. We do this separately since\n\t\t\t// they need to be interpreted differently than displacements\n\t\t\tvec3d vUi(0, 0, 0);\n\t\t\tvec3d vui(0, 0, 0);\n\t\t\tif (lm[3] >= 0) { vUi.x = Ui[lm[3]]; vui.x = ui[lm[3]]; }\n\t\t\tif (lm[4] >= 0) { vUi.y = Ui[lm[4]]; vui.y = ui[lm[4]]; }\n\t\t\tif (lm[5] >= 0) { vUi.z = Ui[lm[5]]; vui.z = ui[lm[5]]; }\n\t\t\tif (emap) qUi = quatd(vUi);\n\t\t\telse qUi = quatd(2 * atan(vUi.norm() / 2), vUi);     // Cayley transform\n\t\t\tquatd qui(2 * atan(vui.norm() / 2), vui);            // Cayley transform\n\t\t\tquatd q = qui*qUi;\n\t\t\tq.MakeUnit();\n\t\t\tif (emap) v = q.GetVector()*q.GetAngle();\n\t\t\telse v = q.GetVector()*(2 * tan(q.GetAngle() / 2)); // Cayley transform\n\t\t\tif (lm[3] >= 0) Ui[lm[3]] = v.x;\n\t\t\tif (lm[4] >= 0) Ui[lm[4]] = v.y;\n\t\t\tif (lm[5] >= 0) Ui[lm[5]] = v.z;\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n//! Updates the rigid body data\nvoid FERigidSolverNew::UpdateRigidBodies(vector<double>& Ui, vector<double>& ui)\n{\n\t// update rigid bodies\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tint nrb = fem.RigidBodies();\n\tfor (int i = 0; i<nrb; ++i)\n\t{\n\t\t// get the rigid body\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\t\tint *lm = RB.m_LM;\n\t\tdouble* du = RB.m_du;\n\n\t\t// first do the displacements\n\t\tif (RB.m_prb == 0)\n\t\t{\n\t\t\tfor (int j = 0; j<3; ++j)\n\t\t\t{\n\t\t\t\tFERigidPrescribedBC* pdc = RB.m_pDC[j];\n\t\t\t\tif (pdc)\n\t\t\t\t{\n\t\t\t\t\t// TODO: do I need to take the line search step into account here?\n\t\t\t\t\tdu[j] = pdc->Value() - RB.m_Up[j];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdu[j] = (lm[j] >= 0 ? Ui[lm[j]] + ui[lm[j]] : 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tRB.m_rt.x = RB.m_rp.x + du[0];\n\t\tRB.m_rt.y = RB.m_rp.y + du[1];\n\t\tRB.m_rt.z = RB.m_rp.z + du[2];\n\n\t\t// update RB center of mass translations\n\t\tif (RB.m_prb) du = RB.m_dul;\n\t\tRB.m_Ut[0] = RB.m_Up[0] + du[0];\n\t\tRB.m_Ut[1] = RB.m_Up[1] + du[1];\n\t\tRB.m_Ut[2] = RB.m_Up[2] + du[2];\n\n\t\t// next, we do the rotations. We do this seperatly since\n\t\t// they need to be interpreted differently than displacements\n\t\tif (RB.m_prb == 0)\n\t\t{\n\t\t\tif (RB.m_bpofr) {\n\t\t\t\t// if all rotation components are known (prescribed or fixed)\n\t\t\t\t// evaluate net increment from load curve\n\t\t\t\tdouble Ut[3] = { 0 };\n\t\t\t\tfor (int j = 3; j<6; ++j) {\n\t\t\t\t\tif (RB.m_pDC[j]) {\n\t\t\t\t\t\tUt[j - 3] = RB.m_pDC[j]->Value();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tquatd qUt(vec3d(Ut[0], Ut[1], Ut[2]));\n\t\t\t\tRB.SetRotation(qUt);\n\t\t\t\tRB.m_Ut[3] = Ut[0];\n\t\t\t\tRB.m_Ut[4] = Ut[1];\n\t\t\t\tRB.m_Ut[5] = Ut[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// rotation components are either free or fixed\n\t\t\t\tvec3d vUi(0, 0, 0);   // initialize total increment so far\n\t\t\t\tvec3d vui(0, 0, 0);   // initialize current increment\n\t\t\t\tif (lm[3] >= 0) { vUi.x = Ui[lm[3]]; vui.x = ui[lm[3]]; }\n\t\t\t\tif (lm[4] >= 0) { vUi.y = Ui[lm[4]]; vui.y = ui[lm[4]]; }\n\t\t\t\tif (lm[5] >= 0) { vUi.z = Ui[lm[5]]; vui.z = ui[lm[5]]; }\n\t\t\t\tquatd qUi(2 * atan(vUi.norm() / 2), vUi);                   // Cayley transform\n\t\t\t\tquatd qui(2 * atan(vui.norm() / 2), vui);                   // Cayley transform\n\t\t\t\tquatd qdu = qui*qUi;                                        // quaternion of net increment\n\n\t\t\t\tqdu.MakeUnit();                                         // clean-up roundoff errors\n\t\t\t\tquatd Q = qdu*RB.m_qp;\n\t\t\t\tQ.MakeUnit();\n\n\t\t\t\t// update at the current time step\n\t\t\t\tRB.SetRotation(Q);\n\n\t\t\t\t// update RB rotations\n\t\t\t\tvec3d vUt = Q.GetRotationVector();\n\t\t\t\tRB.m_Ut[3] = vUt.x;\n\t\t\t\tRB.m_Ut[4] = vUt.y;\n\t\t\t\tRB.m_Ut[5] = vUt.z;\n\t\t\t}\n\t\t}\n\t}\n\n    // Newmark rule\n    FETimeInfo& timeInfo = GetFEModel()->GetTime();\n    double alpham = timeInfo.alpham;\n    double beta = timeInfo.beta;\n    double gamma = timeInfo.gamma;\n    double dt = timeInfo.timeIncrement;\n    double a = 1.0 / (beta*dt);\n    double b = a / dt;\n    double c = 1.0 - 0.5 / beta;\n    \n    for (int i = 0; i<nrb; ++i)\n    {\n        // get the rigid body\n        FERigidBody& RB = *fem.GetRigidBody(i);\n        \n        // acceleration and velocity of center of mass\n        RB.m_at = (RB.m_rt - RB.m_rp)*b - RB.m_vp*a + RB.m_ap*c;\n        RB.m_vt = RB.m_vp + (RB.m_ap*(1.0 - gamma) + RB.m_at*gamma)*dt;\n        // angular acceleration and velocity of rigid body\n        quatd q = RB.GetRotation()*RB.m_qp.Inverse();\n        q.MakeUnit();\n        vec3d vq = q.GetVector()*(2 * tan(q.GetAngle() / 2));  // Cayley transform\n        RB.m_wt = vq*(a*gamma) - RB.m_wp + (RB.m_wp + RB.m_alp*dt / 2.)*(2 - gamma / beta);\n        q.RotateVector(RB.m_wt);\n        RB.m_alt = vq*b - RB.m_wp*a + RB.m_alp*c;\n        q.RotateVector(RB.m_alt);\n        \n        // evaluate mass moment of inertia at t\n        mat3d Rt = RB.GetRotation().RotationMatrix();\n        mat3ds Jt = (Rt*RB.m_moi*Rt.transpose()).sym();\n        // evaluate angular momentum and its rate of change at current time\n        RB.m_ht = Jt*RB.m_wt;\n        RB.m_dht = (RB.m_ht - RB.m_hp)/(gamma*dt) + RB.m_dhp*(1-1./gamma);\n    }\n    \n\t// update the mesh' nodes\n\tfem.UpdateRigidMesh();\n\n\t// Since the rigid nodes are repositioned we need to update the displacement DOFS\n\tFEMesh& mesh = m_fem->GetMesh();\n\tint N = mesh.Nodes();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tvec3d ut = node.m_rt - node.m_r0;\n\t\t\tnode.set_vec3d(m_dofX, m_dofY, m_dofZ, ut);\n            \n\t\t\tif (node.HasFlags(FENode::SHELL) && node.HasFlags(FENode::RIGID_CLAMP)) {\n                // get the rigid body\n                FERigidBody& RB = *fem.GetRigidBody(node.m_rid);\n                // evaluate the director in the current configuration\n\t\t\t\tvec3d d = RB.GetRotation()*node.m_d0 - node.m_d0;\n                // evaluate the back face displacement increments\n                node.set_vec3d(m_dofSX, m_dofSY, m_dofSZ, ut - d);\n            }\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate inertia forces\nvoid FERigidSolverNew::InertialForces(FEGlobalVector& R, const FETimeInfo& timeInfo)\n{\n\t// Newmark rule\n    double alpham = timeInfo.alpham;\n\n\tif (m_fem == nullptr) return;\n\tFEMechModel& fem = *m_fem;\n\n\tint nrb = fem.RigidBodies();\n\t// calculate rigid body inertial forces\n\tfor (int i = 0; i<nrb; ++i)\n\t{\n\t\tFERigidBody& RB = *fem.GetRigidBody(i);\n\n\t\t// 6 dofs per rigid body\n\t\tvector<double> fe(6);\n\t\tvector<int>\tLM(6);\n\n\t\t// rate of change of linear momentum = mass*acceleration\n\t\tvec3d F = (RB.m_at*alpham + RB.m_ap*(1-alpham))*RB.m_mass;\n\n\t\tfe[0] = -F.x;\n\t\tfe[1] = -F.y;\n\t\tfe[2] = -F.z;\n\n\t\t// evaluate mass moment of inertia at t and tp\n\t\tmat3d Rt = RB.GetRotation().RotationMatrix();\n\t\tmat3ds Jt = (Rt*RB.m_moi*Rt.transpose()).sym();\n        RB.m_ht = Jt*RB.m_wt;\n\n\t\t// evaluate rate of change of angular momentum\n        RB.m_dht = (RB.m_wt ^ RB.m_ht) + Jt*RB.m_alt;\n\n        vec3d M = (RB.m_dht*alpham + RB.m_dhp*(1-alpham));\n\n\t\tfe[3] = -M.x;\n\t\tfe[4] = -M.y;\n\t\tfe[5] = -M.z;\n\n\t\t// pack the equation numbers\n\t\tLM[0] = RB.m_LM[0];\n\t\tLM[1] = RB.m_LM[1];\n\t\tLM[2] = RB.m_LM[2];\n\t\tLM[3] = RB.m_LM[3];\n\t\tLM[4] = RB.m_LM[4];\n\t\tLM[5] = RB.m_LM[5];\n\t\tR.Assemble(LM, fe);\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBodyForce.h\"\n#include <FECore/FETimeInfo.h>\n#include <FECore/FESolver.h>\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass matrix;\nclass FEModel;\nclass SparseMatrix;\nclass FEGlobalVector;\nclass FERigidBody;\nclass FESolidSolver2;\nclass DumpStream;\nclass FEElementMatrix;\nclass FEMechModel;\n\n//-----------------------------------------------------------------------------\n//! This is a helper class that helps the solid deformables solvers update the \n//! state of the rigid system.\nclass FEBIOMECH_API FERigidSolver\n{\npublic:\n\tFERigidSolver(FEModel* fem);\n\n\t// destructor\n\tvirtual ~FERigidSolver(){}\n\n\t// initialize the equation\n\t// neq is the number of equations that already have been assigned\n\t// returns the new total number of equations or -1 on error\n\tint InitEquations(int neq);\n\n\t// This is called at the start of each time step\n\tvoid PrepStep(const FETimeInfo& timeInfo, vector<double>& ui);\n\n\t// correct stiffness matrix for rigid bodies\n\tvoid RigidStiffness(SparseMatrix& K, std::vector<double>& ui, std::vector<double>& F, const FEElementMatrix& ke, double alpha);\n\n    // correct stiffness matrix for rigid bodies accounting for rigid-body-deformable-shell interfaces\n    void RigidStiffnessSolid(SparseMatrix& K, std::vector<double>& ui, std::vector<double>& F, const std::vector<int>& en, const std::vector<int>& lmi, const std::vector<int>& lmj, const matrix& ke, double alpha);\n    \n    // correct stiffness matrix for rigid bodies accounting for rigid-body-deformable-shell interfaces\n    void RigidStiffnessShell(SparseMatrix& K, std::vector<double>& ui, std::vector<double>& F, const std::vector<int>& en, const std::vector<int>& lmi, const std::vector<int>& lmj, const matrix& ke, double alpha);\n    \n\t// adjust residual for rigid-deformable interface nodes\n\tvoid AssembleResidual(int node_id, int dof, double f, std::vector<double>& R);\n    \n\t// this is called during residual evaluation\n\t// Currently, this is used for resetting rigid body forces\n\tvoid Residual();\n\n\t// contribution from rigid bodies to stiffness matrix\n\tvoid StiffnessMatrix(SparseMatrix& K, const FETimeInfo& tp);\n\n\t// calculate contribution to mass matrix from a rigid body\n\tvoid RigidMassMatrix(FELinearSystem& LS, const FETimeInfo& timeInfo);\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar);\n\n\t//! return the FEModel\n\tFEModel* GetFEModel();\n\npublic:\n\tvoid AllowMixedBCs(bool b) { m_bAllowMixedBCs = b; }\n\nprotected:\n\tFEMechModel*\tm_fem;\n\tint\t\t\tm_dofX, m_dofY, m_dofZ;\n\tint\t\t\tm_dofVX, m_dofVY, m_dofVZ;\n    int\t\t\tm_dofU, m_dofV, m_dofW;\n    int         m_dofSX, m_dofSY, m_dofSZ;\n    int         m_dofSVX, m_dofSVY, m_dofSVZ;\n\tbool\t\tm_bAllowMixedBCs;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FERigidSolverOld : public FERigidSolver\n{\npublic:\n\tFERigidSolverOld(FEModel* fem) : FERigidSolver(fem) { AllowMixedBCs(true); }\n\n\t//! update rigid body kinematics for dynamic problems\n\tvoid UpdateRigidKinematics();\n\n\t//! Update rigid body data\n\tvoid UpdateRigidBodies(std::vector<double>& Ui, std::vector<double>& ui, bool bnewUpdate);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FERigidSolverNew : public FERigidSolver\n{\npublic:\n\tFERigidSolverNew(FEModel* fem) : FERigidSolver(fem){}\n\n\t// evaluate inertial data\n\tvoid InertialForces(FEGlobalVector& R, const FETimeInfo& timeInfo);\n\n\t// update rigid DOF increments\n\tvoid UpdateIncrements(std::vector<double>& Ui, std::vector<double>& ui, bool emap);\n\n\t// update rigid bodies\n\tvoid UpdateRigidBodies(vector<double> &Ui, vector<double>& ui);\n};\n"
  },
  {
    "path": "FEBioMech/FERigidSphericalJoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidSphericalJoint.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidSphericalJoint, FERigidConnector);\n\tADD_PARAMETER(m_laugon, \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol, \"tolerance\"     );\n\tADD_PARAMETER(m_gtol, \"gaptol\"        );\n\tADD_PARAMETER(m_qtol, \"angtol\"        );\n\tADD_PARAMETER(m_eps , \"force_penalty\" );\n\tADD_PARAMETER(m_ups , \"moment_penalty\");\n\tADD_PARAMETER(m_q0  , \"joint_origin\"  );\n\tADD_PARAMETER(m_naugmin, \"minaug\"        );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"        );\n\tADD_PARAMETER(m_bq     , \"prescribed_rotation\");\n\tADD_PARAMETER(m_qpx    , \"rotation_x\" );\n\tADD_PARAMETER(m_qpy    , \"rotation_y\" );\n\tADD_PARAMETER(m_qpz    , \"rotation_z\" );\n\tADD_PARAMETER(m_Mpx    , \"moment_x\"   );\n\tADD_PARAMETER(m_Mpy    , \"moment_y\"   );\n\tADD_PARAMETER(m_Mpz    , \"moment_z\"   );\n\tADD_PARAMETER(m_bautopen, \"auto_penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidSphericalJoint::FERigidSphericalJoint(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n\n\tm_laugon = FECore::AUGLAG_METHOD; // for backwards compatibility\n    m_atol = 0;\n    m_gtol = 0;\n    m_qtol = 0;\n    m_eps = 1.0;\n    m_ups = 1.0;\n    m_naugmin = 0;\n    m_naugmax = 10;\n    m_qpx = m_qpy = m_qpz = 0;\n    m_Mpx = m_Mpy = m_Mpz = 0;\n    m_bq = false;\n\tm_bautopen = false;\n}\n\n//-----------------------------------------------------------------------------\n//! initial position\nvec3d FERigidSphericalJoint::InitialPosition() const\n{\n    return m_q0;\n}\n\n//-----------------------------------------------------------------------------\n//! current position\nvec3d FERigidSphericalJoint::Position() const\n{\n    FERigidBody& RBa = *m_rbA;\n    vec3d qa = m_qa0;\n    RBa.GetRotation().RotateVector(qa);\n    return RBa.m_rt + qa;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidSphericalJoint::Init()\n{\n    if (m_bq && ((m_Mpx != 0) || (m_Mpy != 0) || (m_Mpz != 0))) {\n        feLogError(\"Rotation and moment cannot be prescribed simultaneously in rigid connector %d (spherical joint)\\n\", m_nID+1);\n        return false;\n    }\n    \n    // reset force\n    m_F = vec3d(0,0,0); m_L = vec3d(0,0,0); m_Fp = vec3d(0,0,0);\n    m_M = vec3d(0,0,0); m_U = vec3d(0,0,0);\n    \n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n\n    m_qa0 = m_q0 - m_rbA->m_r0;\n    m_qb0 = m_q0 - m_rbB->m_r0;\n    \n    m_e0[0] = vec3d(1,0,0); m_e0[1] = vec3d(0,1,0); m_e0[2] = vec3d(0,0,1);\n    \n    m_ea0[0] = m_e0[0]; m_ea0[1] = m_e0[1]; m_ea0[2] = m_e0[2];\n    m_eb0[0] = m_e0[0]; m_eb0[1] = m_e0[1]; m_eb0[2] = m_e0[2];\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSphericalJoint::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n    ar & m_qa0 & m_qb0;\n    ar & m_L & m_U;\n    ar & m_Fp;\n\tar & m_e0;\n\tar & m_ea0;\n\tar & m_eb0;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidSphericalJoint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n    \n    // body A\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n    \n    // body b\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tvec3d c = rb + zb - ra - za;\n\t\tm_F = m_L + c * m_eps;\n\n\t\tif (m_bq) {\n\t\t\tquatd q = (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp) * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd a(vec3d(m_qpx, m_qpy, m_qpz));\n\t\t\tquatd r = a * q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tvec3d ksi = r.GetVector() * r.GetAngle();\n\t\t\tm_M = m_U + ksi * m_ups;\n\t\t}\n\t\telse m_M = vec3d(m_Mpx, m_Mpy, m_Mpz);\n\n\t\tfa[0] = m_F.x;\n\t\tfa[1] = m_F.y;\n\t\tfa[2] = m_F.z;\n\n\t\tfa[3] = za.y * m_F.z - za.z * m_F.y + m_M.x;\n\t\tfa[4] = za.z * m_F.x - za.x * m_F.z + m_M.y;\n\t\tfa[5] = za.x * m_F.y - za.y * m_F.x + m_M.z;\n\n\t\tfb[0] = -m_F.x;\n\t\tfb[1] = -m_F.y;\n\t\tfb[2] = -m_F.z;\n\n\t\tfb[3] = -zb.y * m_F.z + zb.z * m_F.y - m_M.x;\n\t\tfb[4] = -zb.z * m_F.x + zb.x * m_F.z - m_M.y;\n\t\tfb[5] = -zb.x * m_F.y + zb.y * m_F.x - m_M.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\t}\n\telse\n\t{\n\t\tvec3d Ma = (za ^ m_F);\n\t\tvec3d Mb = (zb ^ m_F);\n\n\t\tfa[0] = -m_F.x;\n\t\tfa[1] = -m_F.y;\n\t\tfa[2] = -m_F.z;\n\t\tfa[3] = -Ma.x;\n\t\tfa[4] = -Ma.y;\n\t\tfa[5] = -Ma.z;\n\n\t\tfb[0] = m_F.x;\n\t\tfb[1] = m_F.y;\n\t\tfb[2] = m_F.z;\n\t\tfb[3] = Mb.x;\n\t\tfb[4] = Mb.y;\n\t\tfb[5] = Mb.z;\n\n\t\tfor (int i = 0; i < 6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n\t\tfor (int i = 0; i < 6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n\n\t\t// translational constraint\n\t\tvec3d c = rb + zb - ra - za;\n\t\tR[m_LM[0]] += c.x;\n\t\tR[m_LM[1]] += c.y;\n\t\tR[m_LM[2]] += c.z;\n\t}\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidSphericalJoint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tdouble alpha = tp.alphaf;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n\tvec3d ra = rat*alpha + rap*(1-alpha);\n\tvec3d zat = Qat * m_qa0;\n\tvec3d zap = Qap * m_qa0;\n\tvec3d za = zat*alpha + zap*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n    vec3d rb = rbt*alpha + rbp*(1-alpha);\n\tvec3d zbt = Qbt * m_qb0;\n\tvec3d zbp = Qbp * m_qb0;\n\tvec3d zb = zbt*alpha + zbp*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tvec3d c = rb + zb - ra - za;\n\t\tm_F = m_L + c * m_eps;\n\t\tmat3dd I(1);\n\n\t\tquatd q, a, r;\n\t\tif (m_bq) {\n\t\t\tq = (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp) * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\ta = quatd(vec3d(m_qpx, m_qpy, m_qpz));\n\t\t\tr = a * q.Inverse();\n\t\t\tr.MakeUnit();\n\t\t\tvec3d ksi = r.GetVector() * r.GetAngle();\n\t\t\tm_M = m_U + ksi * m_ups;\n\t\t}\n\t\telse m_M = vec3d(m_Mpx, m_Mpy, m_Mpz);\n\n\t\tmat3d Wba(0, 0, 0, 0, 0, 0, 0, 0, 0), Wab(0, 0, 0, 0, 0, 0, 0, 0, 0);\n\t\tif (m_bq) {\n\t\t\tquatd qa = RBa.GetRotation() * (alpha * RBa.GetRotation() + (1 - alpha) * RBa.m_qp).Inverse();\n\t\t\tquatd qb = RBb.GetRotation() * (alpha * RBb.GetRotation() + (1 - alpha) * RBb.m_qp).Inverse();\n\t\t\tqa.MakeUnit();\n\t\t\tqb.MakeUnit();\n\t\t\tmat3d Qa = qa.RotationMatrix();\n\t\t\tmat3d Qb = qb.RotationMatrix();\n\t\t\tmat3d A = a.RotationMatrix();\n\t\t\tmat3d R = r.RotationMatrix();\n\t\t\tmat3dd I(1);\n\t\t\tWba = A * (I * Qa.trace() - Qa) / 2;\n\t\t\tWab = R * (I * Qb.trace() - Qb) / 2;\n\t\t}\n\n\t\tmat3d Fhat; Fhat.skew(m_F);\n\n\t\tFEElementMatrix ke(12, 12);\n\t\tke.zero();\n\n\t\t// row 1\n\t\tke.set(0, 0, I * (alpha * m_eps));\n\t\tke.set(0, 3, zathat * (-m_eps * alpha));\n\t\tke.set(0, 6, I * (-alpha * m_eps));\n\t\tke.set(0, 9, zbthat * (alpha * m_eps));\n\n\t\t// row 2\n\t\tke.set(3, 0, zahat * (alpha * m_eps));\n\t\tke.set(3, 3, (zahat * zathat * m_eps + Fhat * zathat + Wba * m_ups) * (-alpha));\n\t\tke.set(3, 6, zahat * (-alpha * m_eps));\n\t\tke.set(3, 9, (zahat * zbthat * m_eps + Wab * m_ups) * alpha);\n\n\t\t// row 3\n\t\tke.set(6, 0, I * (-alpha * m_eps));\n\t\tke.set(6, 3, zathat * (m_eps * alpha));\n\t\tke.set(6, 6, I * (alpha * m_eps));\n\t\tke.set(6, 9, zbthat * (-alpha * m_eps));\n\n\t\t// row 4\n\t\tke.set(9, 0, zbhat * (-alpha * m_eps));\n\t\tke.set(9, 3, (zbhat * zathat * m_eps + Wba * m_ups) * alpha);\n\t\tke.set(9, 6, zbhat * (alpha * m_eps));\n\t\tke.set(9, 9, (zbhat * zbthat * m_eps - Fhat * zbthat + Wab * m_ups) * (-alpha));\n\n\t\tvector<int> LM(12);\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tLM[j    ] = RBa.m_LM[j];\n\t\t\tLM[j + 6] = RBb.m_LM[j];\n\t\t}\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\tFEElementMatrix ke; ke.resize(15, 15);\n\t\tke.zero();\n\n\t\tmat3dd I(1.0);\n\n\t\t// translation constraint\n\t\tmat3da Fhat(m_F);\n\t\tke.sub( 3,  3, -Fhat*zahat);\n\t\tke.sub( 9,  9,  Fhat*zbhat);\n\t\tke.sub( 0, 12,     -I);\n\t\tke.sub( 3, 12, -zahat);\n\t\tke.sub( 6, 12,      I);\n\t\tke.sub( 9, 12,  zbhat);\n\t\tke.sub(12,  0,     -I);\n\t\tke.sub(12,  3,  zahat);\n\t\tke.sub(12,  6,      I);\n\t\tke.sub(12,  9, -zbhat);\n\n\t\tvector<int> LM;\n\t\tUnpackLM(LM);\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidSphericalJoint::Augment(int naug, const FETimeInfo& tp)\n{\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d ra, rb, qa, qb, c, ksi, Lm;\n    vec3d za, zb;\n    double normF0, normF1;\n    vec3d Um;\n    double normM0, normM1;\n    bool bconv = true;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n    \n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n    \n    c = rb + zb - ra - za;\n    \n    normF0 = sqrt(m_L*m_L);\n\n    // calculate trial multiplier\n    Lm = m_L + c*m_eps;\n    \n    normF1 = sqrt(Lm*Lm);\n    \n    // check convergence of constraints\n    feLog(\" rigid connector # %d (spherical joint)\\n\", m_nID+1);\n    feLog(\"                  CURRENT        REQUIRED\\n\");\n    double pctn = 0;\n    double gap = c.norm();\n    double qap = ksi.norm();\n    if (fabs(normF1) > 1e-10) pctn = fabs((normF1 - normF0)/normF1);\n    if (m_atol) feLog(\"    force : %15le %15le\\n\", pctn, m_atol);\n    else        feLog(\"    force : %15le        ***\\n\", pctn);\n    if (m_gtol) feLog(\"    gap   : %15le %15le\\n\", gap, m_gtol);\n    else        feLog(\"    gap   : %15le        ***\\n\", gap);\n    if (m_atol && (pctn >= m_atol)) bconv = false;\n    if (m_gtol && (gap >= m_gtol)) bconv = false;\n\n    if (m_bq) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(vec3d(m_qpx,m_qpy,m_qpz));\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        vec3d ksi = r.GetVector()*r.GetAngle();\n        normM0 = sqrt(m_U*m_U);\n        \n        // calculate trial multiplier\n        Um = m_U + ksi*m_ups;\n        \n        normM1 = sqrt(Um*Um);\n        \n        double qctn = 0;\n        if (fabs(normM1) > 1e-10) qctn = fabs((normM1 - normM0)/normM1);\n        if (m_atol) feLog(\"    moment: %15le %15le\\n\", qctn, m_atol);\n        else        feLog(\"    moment: %15le        ***\\n\", qctn);\n        if (m_qtol) feLog(\"    angle : %15le %15le\\n\", qap, m_qtol);\n        else        feLog(\"    angle : %15le        ***\\n\", qap);\n        if (m_atol && (qctn >= m_atol)) bconv = false;\n        if (m_qtol && (qap >= m_qtol)) bconv = false;\n    }\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    if (!bconv)\n    {\n        // update multipliers\n        m_L = Lm;\n        m_U = Um;\n    }\n    \n    // auto-penalty update (works only with gaptol and angtol)\n\tif (m_bautopen)\n\t{\n\t\tif (m_gtol && (gap > m_gtol)) {\n\t\t\tm_eps = fmax(gap / m_gtol, 100)*m_eps;\n\t\t\tfeLog(\"    force_penalty :         %15le\\n\", m_eps);\n\t\t}\n\t\tif (m_qtol && (qap > m_qtol)) {\n\t\t\tm_ups = fmax(qap / m_qtol, 100)*m_ups;\n\t\t\tfeLog(\"    moment_penalty :        %15le\\n\", m_ups);\n\t\t}\n\t}\n\n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSphericalJoint::Update()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD) return;\n\n    vec3d ra, rb;\n    vec3d za, zb;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n    \n    vec3d c = rb + zb - ra - za;\n    m_F = m_L + c*m_eps;\n    \n    if (m_bq) {\n\t\tquatd q = (alpha*RBb.GetRotation() + (1 - alpha)*RBb.m_qp)*(alpha*RBa.GetRotation() + (1 - alpha)*RBa.m_qp).Inverse();\n        quatd a(vec3d(m_qpx,m_qpy,m_qpz));\n        quatd r = a*q.Inverse();\n        r.MakeUnit();\n        vec3d ksi = r.GetVector()*r.GetAngle();\n        m_M = m_U + ksi*m_ups;\n    }\n    else m_M = vec3d(m_Mpx,m_Mpy,m_Mpz);\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSphericalJoint::Reset()\n{\n    m_F = vec3d(0,0,0);\n    m_L = vec3d(0,0,0);\n    m_M = vec3d(0,0,0);\n    m_U = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_q0 - RBa.m_r0;\n    m_qb0 = m_q0 - RBb.m_r0;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidSphericalJoint::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n\n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative translation onto local basis\n    vec3d y(x*ea[0], x*ea[1], x*ea[2]);\n    \n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidSphericalJoint::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    if (global) return q;\n    \n    // evaluate local basis for body A\n    vec3d ea[3];\n    ea[0] = m_ea0[0]; RBa.GetRotation().RotateVector(ea[0]);\n    ea[1] = m_ea0[1]; RBa.GetRotation().RotateVector(ea[1]);\n    ea[2] = m_ea0[2]; RBa.GetRotation().RotateVector(ea[2]);\n\n    // project relative rotation onto local basis\n    vec3d y(q*ea[0], q*ea[1], q*ea[2]);\n    \n    return y;\n}\n\n// allocate equations\nint FERigidSphericalJoint::InitEquations(int neq)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tconst int LMeq = 3;\n\t\tm_LM.resize(LMeq, -1);\n\t\tfor (int i = 0; i < LMeq; ++i) m_LM[i] = neq + i;\n\t\treturn LMeq;\n\t}\n\telse return 0;\n}\n\n// Build the matrix profile\nvoid FERigidSphericalJoint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> lm;\n\tUnpackLM(lm);\n\n\t// add it to the pile\n\tM.build_add(lm);\n}\n\nvoid FERigidSphericalJoint::UnpackLM(vector<int>& lm)\n{\n\t// add the dofs of rigid body A\n\tlm.resize(12 + m_LM.size());\n\tlm[0] = m_rbA->m_LM[0];\n\tlm[1] = m_rbA->m_LM[1];\n\tlm[2] = m_rbA->m_LM[2];\n\tlm[3] = m_rbA->m_LM[3];\n\tlm[4] = m_rbA->m_LM[4];\n\tlm[5] = m_rbA->m_LM[5];\n\n\t// add the dofs of rigid body B\n\tlm[6] = m_rbB->m_LM[0];\n\tlm[7] = m_rbB->m_LM[1];\n\tlm[8] = m_rbB->m_LM[2];\n\tlm[9] = m_rbB->m_LM[3];\n\tlm[10] = m_rbB->m_LM[4];\n\tlm[11] = m_rbB->m_LM[5];\n\n\t// add the LM equations\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < m_LM.size(); ++i) lm[12 + i] = m_LM[i];\n\t}\n}\n\nvoid FERigidSphericalJoint::PrepStep()\n{\n\tm_Fp = m_F;\n}\n\nvoid FERigidSphericalJoint::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tm_F.x = m_Fp.x + Ui[m_LM[0]] + ui[m_LM[0]];\n\t\tm_F.y = m_Fp.y + Ui[m_LM[1]] + ui[m_LM[1]];\n\t\tm_F.z = m_Fp.z + Ui[m_LM[2]] + ui[m_LM[2]];\n\t}\n}\n\nvoid FERigidSphericalJoint::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tUi[m_LM[0]] += ui[m_LM[0]];\n\t\tUi[m_LM[1]] += ui[m_LM[1]];\n\t\tUi[m_LM[2]] += ui[m_LM[2]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidSphericalJoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidSphericalJoint class implements a rigid spherical joint that\n//! functions under static and dynamic conditions. This joint allows the\n//! user to connect two rigid bodies at a point in space.\n\nclass FEBIOMECH_API FERigidSphericalJoint : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidSphericalJoint(FEModel* pfem);\n    \n    //! destructor\n    virtual ~FERigidSphericalJoint() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global = false) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global = false) override;\n    \n    //! initial position\n    vec3d InitialPosition() const;\n\n    //! current position\n    vec3d Position() const;\n\nprivate: // lag. mult. methods\n\tint InitEquations(int neq) override;\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\tvoid UnpackLM(vector<int>& lm);\n\tvoid PrepStep();\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\npublic: // parameters\n\tint\t\tm_laugon;\t//!< enforcement method\n    double\tm_atol;     //! augmented Lagrangian tolerance\n    double  m_gtol;     //! augmented Lagrangian gap tolerance\n    double  m_qtol;     //! augmented Lagrangian angular gap tolerance\n    double\tm_eps;      //! penalty factor for constraining force\n    double\tm_ups;      //! penalty factor for constraining moment\n    vec3d\tm_q0;       //! initial position of joint\n    int     m_naugmin;  //! minimum number of augmentations\n    int     m_naugmax;  //! maximum number of augmentations\n    bool    m_bq;           //! flag for prescribing rotation\n    double  m_qpx;          //! prescribed rotation x-component\n    double  m_qpy;          //! prescribed rotation y-component\n    double  m_qpz;          //! prescribed rotation z-component\n    double  m_Mpx;          //! prescribed moment along x\n    double  m_Mpy;          //! prescribed moment along y\n    double  m_Mpz;          //! prescribed moment along z\n\tbool\tm_bautopen;\t//!< auto-penalty for gap and ang tolerance\n\nprotected:\n    vec3d\tm_qa0;      //! initial relative position vector of joint w.r.t. A\n    vec3d\tm_qb0;      //! initial relative position vector of joint w.r.t. B\n   \n    vec3d\tm_e0[3];        //! initial joint basis\n    vec3d\tm_ea0[3];       //! initial joint basis w.r.t. A\n    vec3d\tm_eb0[3];       //! initial joint basis w.r.t. B\n    \n    vec3d\tm_L;        //! Lagrange multiplier for constraining force\n    vec3d\tm_U;        //! Lagrange multiplier for constraining moment\n\n\tvec3d m_Fp;\n\n\tvector<int>\t\tm_LM;\t// Lagrange multiplier equation numbers\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidSpring.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidSpring.h\"\n#include \"FERigidBody.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidSpring, FERigidConnector);\n    ADD_PARAMETER(m_k   , \"k\"          );\n    ADD_PARAMETER(m_a0  , \"insertion_a\");\n    ADD_PARAMETER(m_b0  , \"insertion_b\");\n    ADD_PARAMETER(m_L0  , \"free_length\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidSpring::FERigidSpring(FEModel* pfem) : FERigidConnector(pfem)\n{\n    m_nID = m_ncount++;\n    m_L0 = 0;\n    m_k = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n//! TODO: This function is called twice: once in the Init and once in the Solve\n//!       phase. Is that necessary?\nbool FERigidSpring::Init()\n{\n\t// base class first\n\tif (FERigidConnector::Init() == false) return false;\n    \n    // reset force\n    m_F = vec3d(0,0,0);\n    \n    // if not specified by use, get free length of spring\n    if (m_L0 == 0) m_L0 = (m_b0 - m_a0).norm();\n\n    // set spring insertions relative to rigid body center of mass\n    m_qa0 = m_a0 - m_rbA->m_r0;\n    m_qb0 = m_b0 - m_rbB->m_r0;\n\n\tm_at = m_a0;\n\tm_bt = m_b0;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSpring::Serialize(DumpStream& ar)\n{\n\tFERigidConnector::Serialize(ar);\n    ar & m_qa0 & m_qb0;\n    ar & m_L0 & m_k;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidSpring::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<double> fa(6);\n    vector<double> fb(6);\n    \n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n\n\tdouble alpha = tp.alphaf;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = Qat*m_qa0;\n    vec3d zap = Qap*m_qa0;\n    vec3d za = zat*alpha + zap*(1-alpha);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n    vec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = Qbt*m_qb0;\n    vec3d zbp = Qbp*m_qb0;\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n    \n    vec3d c = rb + zb - ra - za;\n    double L = c.norm();\n    m_F = (L > 0) ? c*((1 - m_L0/L)*m_k) : c*m_k;\n    \n    fa[0] = m_F.x;\n    fa[1] = m_F.y;\n    fa[2] = m_F.z;\n    \n    fa[3] = za.y*m_F.z-za.z*m_F.y;\n    fa[4] = za.z*m_F.x-za.x*m_F.z;\n    fa[5] = za.x*m_F.y-za.y*m_F.x;\n    \n    fb[0] = -m_F.x;\n    fb[1] = -m_F.y;\n    fb[2] = -m_F.z;\n    \n    fb[3] = -zb.y*m_F.z+zb.z*m_F.y;\n    fb[4] = -zb.z*m_F.x+zb.x*m_F.z;\n    fb[5] = -zb.x*m_F.y+zb.y*m_F.x;\n    \n    for (int i=0; i<6; ++i) if (RBa.m_LM[i] >= 0) R[RBa.m_LM[i]] += fa[i];\n    for (int i=0; i<6; ++i) if (RBb.m_LM[i] >= 0) R[RBb.m_LM[i]] += fb[i];\n    \n    RBa.m_Fr -= vec3d(fa[0],fa[1],fa[2]);\n    RBa.m_Mr -= vec3d(fa[3],fa[4],fa[5]);\n    RBb.m_Fr -= vec3d(fb[0],fb[1],fb[2]);\n    RBb.m_Mr -= vec3d(fb[3],fb[4],fb[5]);\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Why is this class not using the FESolver for assembly?\nvoid FERigidSpring::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tdouble alpha = tp.alphaf;\n    \n    vector<int> LM(12);\n    FEElementMatrix ke(12,12);\n    ke.zero();\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    // body A\n\tquatd Qat = RBa.GetRotation();\n\tquatd Qap = RBa.m_qp;\n\tvec3d rat = RBa.m_rt;\n\tvec3d rap = RBa.m_rp;\n    vec3d ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    vec3d za = zat*alpha + zap*(1-alpha);\n    mat3d zahat; zahat.skew(za);\n    mat3d zathat; zathat.skew(zat);\n    \n    // body b\n\tquatd Qbt = RBb.GetRotation();\n\tquatd Qbp = RBb.m_qp;\n\tvec3d rbt = RBb.m_rt;\n\tvec3d rbp = RBb.m_rp;\n\tvec3d rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    vec3d zb = zbt*alpha + zbp*(1-alpha);\n    mat3d zbhat; zbhat.skew(zb);\n    mat3d zbthat; zbthat.skew(zbt);\n    \n    vec3d c = rb + zb - ra - za;\n    double L = c.norm();\n    vec3d n = c/L;\n    m_F = (L > 0) ? c*((1 - m_L0/L)*m_k) : c*m_k;\n    mat3ds P;\n    mat3dd I(1);\n    P = (L > 0) ? I*(1 - m_L0/L) + dyad(n)*(m_L0/L) : I;\n\n\tmat3da Fhat(m_F);\n    \n    mat3d K;\n    \n    // row 0\n\tke.set(0, 0, P * (m_k));\n\tke.set(0, 3, P * zathat * (-m_k));\n\tke.set(0, 6, P * (-m_k));\n\tke.set(0, 9, P * zbthat * (m_k));\n    \n    // row 1\n\tke.set(3, 0, (zahat * P) * (m_k));\n\tke.set(3, 3, (zahat * P * zathat) * (-m_k) + (Fhat * zathat) * (-1.0));\n\tke.set(3, 6, (zahat * P) * (- m_k));\n\tke.set(3, 9, (zahat * P * zbthat) * (m_k));\n    \n    // row 2\n\tke.set(6, 0, P * (-m_k));\n\tke.set(6, 3, P * zathat * (m_k));\n\tke.set(6, 6, P * (m_k));\n\tke.set(6, 9, P * zbthat * (-m_k));\n    \n    // row 3\n\tke.set(9, 0, (zbhat * P)* (-m_k));\n\tke.set(9, 3, (zbhat * P * zathat) * (m_k));\n\tke.set(9, 6, (zbhat * P)* (m_k));\n\tke.set(9, 9, (zbhat * P * zbthat) * (-m_k) + (Fhat * zbthat));\n\n\tif (alpha != 1) ke *= alpha;\n    \n    for (int j=0; j<6; ++j)\n    {\n        LM[j  ] = RBa.m_LM[j];\n        LM[j+6] = RBb.m_LM[j];\n    }\n    \n\tke.SetIndices(LM);\n\tLS.Assemble(ke);\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidSpring::Augment(int naug, const FETimeInfo& tp)\n{\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSpring::Update()\n{\n    vec3d ra, rb, c;\n    vec3d za, zb;\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\tdouble alpha = tp.alphaf;\n\n    ra = RBa.m_rt*alpha + RBa.m_rp*(1-alpha);\n    rb = RBb.m_rt*alpha + RBb.m_rp*(1-alpha);\n    \n\tvec3d zat = m_qa0; RBa.GetRotation().RotateVector(zat);\n    vec3d zap = m_qa0; RBa.m_qp.RotateVector(zap);\n    za = zat*alpha + zap*(1-alpha);\n    \n\tvec3d zbt = m_qb0; RBb.GetRotation().RotateVector(zbt);\n    vec3d zbp = m_qb0; RBb.m_qp.RotateVector(zbp);\n    zb = zbt*alpha + zbp*(1-alpha);\n\n\tm_at = RBa.m_rt + zat;\n\tm_bt = RBb.m_rt + zbt;\n\n    c = rb + zb - ra - za;\n    double L = c.norm();\n    m_F = (L > 0) ? c*((1 - m_L0/L)*m_k) : c*m_k;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSpring::Reset()\n{\n    m_F = vec3d(0,0,0);\n    \n\tFERigidBody& RBa = *m_rbA;\n\tFERigidBody& RBb = *m_rbB;\n\n    m_qa0 = m_a0 - RBa.m_r0;\n    m_qb0 = m_b0 - RBb.m_r0;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidSpring::RelativeTranslation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // body A\n    vec3d ra = RBa.m_rt;\n    vec3d za = m_qa0; RBa.GetRotation().RotateVector(za);\n    \n    // body B\n    vec3d rb = RBb.m_rt;\n    vec3d zb = m_qb0; RBb.GetRotation().RotateVector(zb);\n\n    // relative translation in global coordinate system\n    vec3d x = rb + zb - ra - za;\n    \n    if (global) return x;\n    \n    vec3d y = vec3d(1,0,0)*x.norm();\n\n    return y;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidSpring::RelativeRotation(const bool global)\n{\n    FERigidBody& RBa = *m_rbA;\n    FERigidBody& RBb = *m_rbB;\n    \n    // get relative rotation\n    quatd Q = RBb.GetRotation()*RBa.GetRotation().Inverse(); Q.MakeUnit();\n    \n    // relative rotation vector\n    vec3d q = Q.GetRotationVector();\n    \n    return q;\n}\n"
  },
  {
    "path": "FEBioMech/FERigidSpring.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/vec3d.h\"\n#include \"FERigidConnector.h\"\n\n//-----------------------------------------------------------------------------\n//! The FERigidSpring class implements a linear spring that connects\n//! two rigid bodies at arbitrary points (not necessarily nodes).\n\nclass FERigidSpring : public FERigidConnector\n{\npublic:\n    //! constructor\n    FERigidSpring(FEModel* pfem);\n    \n    //! destructor\n    ~FERigidSpring() {}\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculates the joint forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculates the joint stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! update state\n\tvoid Update() override;\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! evaluate relative translation\n    vec3d RelativeTranslation(const bool global) override;\n    \n    //! evaluate relative rotation\n    vec3d RelativeRotation(const bool global) override;\n\npublic: // parameters\n    vec3d\tm_a0;       //! initial absolute position vector of spring on body A\n    vec3d\tm_b0;       //! initial absolute position vector of spring on body B\n    double\tm_k;        //! spring constant\n\n\t// output parameters\n\tvec3d\tm_at;\t\t//!< current absolute position of spring on body A\n\tvec3d\tm_bt;\t\t//!< current absolute position of spring on body B\n\nprotected:\n    double  m_L0;       //! spring free length\n\tvec3d\tm_qa0;      //! initial relative position vector of spring on body A\n    vec3d\tm_qb0;      //! initial relative position vector of spring on body B\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FERigidSystem.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERigidSystem.h\"\n#include \"FERigidBody.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEDomain.h>\n#include \"RigidBC.h\"\n#include <FECore/FEGlobalMatrix.h>\n#include \"FERigidMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! constructor\nFERigidSystem::FERigidSystem(FEModel* pfem) : m_fem(*pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Add a rigid body\nvoid FERigidSystem::AddRigidBody(FERigidBody* prb)\n{\n\tif (prb) m_RB.push_back(prb);\n}\n\n//-----------------------------------------------------------------------------\nFERigidSystem::~FERigidSystem()\n{\n\tClear();\n}\n\n//-----------------------------------------------------------------------------\nint FERigidSystem::Objects() const\n{\n\treturn (int) m_RB.size();\n}\n\n//-----------------------------------------------------------------------------\nstd::vector<FERigidBody*>& FERigidSystem::RigidBodyList()\n{\n\treturn m_RB;\n}\n\n//-----------------------------------------------------------------------------\n//! Get a rigid body\nFERigidBody* FERigidSystem::Object(int i)\n{\n\treturn m_RB[i];\n}\n\n//-----------------------------------------------------------------------------\n//! delete all rigid bodies\nvoid FERigidSystem::Clear()\n{\n\tfor (int i=0; i<(int)m_RB.size (); ++i) delete m_RB [i]; m_RB.clear ();\n\tfor (int i=0; i<(int)m_RBC.size(); ++i) delete m_RBC[i]; m_RBC.clear();\n\tfor (int i=0; i<(int)m_RIC.size(); ++i) delete m_RIC[i]; m_RIC.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSystem::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow())\n\t{\n\t\tfor (int i=0; i<(int) m_RB.size(); ++i) m_RB[i]->Serialize(ar);\n\t}\n\telse\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\t// rigid body constraints\n\t\t\tar << m_RBC;\n\n\t\t\t// rigid body initial conditions\n\t\t\tar << m_RIC;\n\n\t\t\t// rigid objects\n\t\t\t// (Do these last, since they contain references to rbcs)\n\t\t\tint nrb = Objects();\n\t\t\tar << nrb;\n\t\t\tfor (int i = 0; i < nrb; ++i) m_RB[i]->Serialize(ar);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tClear();\n\n\t\t\t// rigid body constraints\n\t\t\tar >> m_RBC;\n\n\t\t\t// rigid body initial conditions\n\t\t\tar >> m_RIC;\n\n\t\t\t// rigid bodies\n\t\t\tint nrb = 0;\n\t\t\tar >> nrb;\n\t\t\tfor (int i=0; i<nrb; ++i)\n\t\t\t{\n\t\t\t\tFERigidBody* prb = new FERigidBody(&m_fem);\n\t\t\t\tprb->Serialize(ar);\n\t\t\t\tAddRigidBody(prb);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSystem::Activate()\n{\n\t// fixed rigid body dofs\n\tfor (int i=0; i<(int) m_RBC.size(); ++i)\n\t{\n\t\tFERigidBC& rc = *m_RBC[i];\n\t\tif (rc.IsActive()) rc.Activate();\n\t}\n\n\t// initial rigid conditions\n\tfor (int i=0; i<(int) m_RIC.size(); ++i)\n\t{\n\t\tFERigidIC& ric = *m_RIC[i];\n\t\tif (ric.IsActive()) ric.Activate();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidSystem::Init()\n{\n\t// create the rigid bodies from the rigid material list\n\tif (CreateObjects() == false) return false;\n\n\t// the rigid body constraints are still associated with the rigid materials\n\t// so we now associate them with the rigid bodies\n\t// NOTE: This is now done in the Init() member function for each class\n\tFEModel& fem = m_fem;\n\tfor (int i=0; i<(int) m_RBC.size(); ++i)\n\t{\n\t\tFERigidBC& BC = *m_RBC[i];\n\t\tif (BC.Init() == false) return false;\n\t}\n\tfor (int i=0; i<(int) m_RIC.size(); ++i)\n\t{\n\t\tFERigidIC& IC = *m_RIC[i];\n\t\tif (IC.Init() == false) return false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidSystem::InitRigidBodies()\n{\n\tFEModel& fem = m_fem;\n\n\t// initialize rigid body COM\n\t// only set the rigid body com if this is the main rigid body material\n\tfor (int i = 0; i < m_RB.size(); ++i)\n\t{\n\t\tFERigidBody& rb = *m_RB[i];\n\n\t\t// first, calculate the mass\n\t\trb.UpdateMass();\n\n\t\tFERigidMaterial* prm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(rb.m_mat));\n\t\tassert(prm);\n\n\t\t// next, calculate the center of mass, or just set it\n\t\tif (prm->m_com == false)\n\t\t{\n\t\t\trb.UpdateCOM();\n\t\t}\n\t\telse\n\t\t{\n\t\t\trb.SetCOM(prm->m_rc);\n\t\t}\n\n\t\t// finally, determine moi\n\t\trb.UpdateMOI();\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! In FEBio rigid bodies are defined implicitly through a list of rigid materials.\n//! However, a rigid body can be composed of multiple rigid materials, so we can't\n//! just create a rigid body for each rigid material. We must look at the connectivity\n//! of the materials as well.\nbool FERigidSystem::CreateObjects()\n{\n\tFEModel& fem = m_fem;\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// count the number of rigid materials\n\tint NMAT = fem.Materials();\n\tint nrm = 0;\n\tfor (int i=0; i<NMAT; ++i)\n\t{\n\t\tif (dynamic_cast<FERigidMaterial*>(fem.GetMaterial(i))) nrm++;\n\t}\n\t\n\t// make sure there are rigid materials\n\tif (nrm == 0) return true;\n\n\t// First we need to figure out how many rigid bodies there are.\n\t// This is not the same as rigid materials, since a rigid body\n\t// may be composed of different rigid materials (similarly to a deformable\n\t// body that may contain different materials). Although there can\n\t// only be one deformable mesh, there can be several rigid bodies.\n\n\t// The mrb array will contain an index to the rigid body the material\n\t// is attached to. For now, we assume one rigid body per rigid material.\n\tvector<int> mrb(NMAT);\n\tint n = 0;\n\tfor (int i=0; i<NMAT; ++i)\n\t{\n\t\tif (dynamic_cast<FERigidMaterial*>(fem.GetMaterial(i))) mrb[i] = n++;\n\t\telse mrb[i] = -1;\n\t}\n\n\t// Next, we assign to all nodes a rigid node number\n\t// This number is preliminary since rigid materials can be merged\n\t// Note that we do solid domains first. This is to avoid a complication\n\t// with shells on solids.\n\n\t// solid domains first\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tif (dom.Class() == FE_DOMAIN_SOLID)\n\t\t{\n\t\t\tFERigidMaterial* pmat = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\t\tif (pmat)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < dom.Elements(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tint n = el.m_node[j];\n\t\t\t\t\t\tFENode& node = mesh.Node(n);\n\t\t\t\t\t\tnode.m_rid = pmat->GetID() - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// non-solid domains\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tif (dom.Class() != FE_DOMAIN_SOLID)\n\t\t{\n\t\t\tFERigidMaterial* pmat = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\t\tif (pmat)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < dom.Elements(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tint n = el.m_node[j];\n\t\t\t\t\t\tFENode& node = mesh.Node(n);\n\t\t\t\t\t\tnode.m_rid = pmat->GetID() - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// now we can merge rigid materials\n\t// if a rigid element has two nodes that connect to two different\n\t// rigid materials we need to merge. \n\tbool bdone;\n\tdo\n\t{\n\t\tbdone = true;\n\t\tfor (int nd=0; nd<mesh.Domains(); ++nd)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(nd);\n\t\t\tFERigidMaterial* pmat = dynamic_cast<FERigidMaterial*>(dom.GetMaterial());\n\t\t\tif (pmat)\n\t\t\t{\n\t\t\t\tfor (int i=0; i<dom.Elements(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\n\t\t\t\t\tint m = mesh.Node(el.m_node[0]).m_rid;\n\t\t\t\t\tfor (int j=1; j<el.Nodes(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tint n = mesh.Node(el.m_node[j]).m_rid;\n\t\t\t\t\t\tif (mrb[n] != mrb[m])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (mrb[n]<mrb[m]) mrb[m] = mrb[n]; else mrb[n] = mrb[m];\n\t\t\t\t\t\t\tbdone = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\twhile (!bdone);\n\n\t// since we may have lost a rigid body in the merge process\n\t// we reindex the RB's.\n\tvector<int> mrc; mrc.assign(NMAT, -1);\n\tfor (int i=0; i<NMAT; ++i) if (mrb[i] >= 0) mrc[mrb[i]] = 0;\n\tint nrb = 0;\n\tfor (int i=0; i<NMAT; ++i)\n\t{\n\t\tif (mrc[i] == 0) mrc[i] = nrb++;\n\t}\n\n\tfor (int i=0; i<NMAT; ++i) \n\t{\n\t\tif (mrb[i] >= 0) mrb[i] = mrc[mrb[i]];\n\t}\n\n\t// set rigid body index for materials\n\tfor (int i=0; i<NMAT; ++i)\n\t{\n\t\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(i));\n\t\tif (pm)\t\n\t\t{\n\t\t\tpm->SetRigidBodyID(mrb[i]);\n\t\t}\n\t}\n\n\t// assign rigid body index to nodes\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0) \n\t\t{\n\t\t\tnode.m_rid = mrb[ node.m_rid ];\n\t\t\tnode.m_ra = node.m_r0;\n\t\t}\n\t}\n\n\t// let's clear all rigid bodies\n\tif (m_RB.empty() == false)\n\t{\n\t\tfor (int i=0; i<(int) m_RB.size(); ++i) delete m_RB[i]; \n\t\tm_RB.clear();\n\t}\n\n\t// Ok, we now know how many rigid bodies there are\n\t// so let's create them\n\tfor (int i=0; i<nrb; ++i)\n\t{\n\t\t// create a new rigid body\n\t\tFERigidBody* prb = new FERigidBody(&fem);\n\t\tprb->m_nID = i;\n\n\t\t// Since a rigid body may contain several rigid materials\n\t\t// we find the first material that this body has and use\n\t\t// that materials data to set up the rigid body data\n\t\tint j;\n\t\tFERigidMaterial* pm = 0;\n\t\tfor (j=0; j<NMAT; ++j)\n\t\t{\n\t\t\tpm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(j));\n\t\t\tif (pm && (pm->GetRigidBodyID() == i)) break;\n\t\t}\n\t\tif (j >= NMAT) return false;\n\t\tprb->m_mat = j;\n\t\tprb->SetName(pm->GetName());\n\n\t\t// add it to the pile\n\t\tm_RB.push_back(prb);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Reset rigid system.\nbool FERigidSystem::Reset()\n{\n\tint nrb = (int)m_RB.size();\n\tfor (int i=0; i<nrb; ++i) m_RB[i]->Reset();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a parameter\ndouble* FERigidSystem::FindParameter(int nmat, ParamString& sz, int index)\n{\n\t// the rigid bodies are dealt with differently\n\tint nrb = (int)m_RB.size();\n\tfor (int i=0; i<nrb; ++i)\n\t{\n\t\tFERigidBody& ob = *m_RB[i];\n\t\tif (ob.GetMaterialID() == nmat)\n\t\t{\n\t\t\tFEParam* pp = ob.FindParameter(sz);\n\t\t\tif (pp) return pp->pvalue<double>(index);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// find a parameter value\nFEParamValue FERigidSystem::GetParameterValue(const ParamString& paramString)\n{\n\tParamString next = paramString.next();\n\tif (next == \"rigidbody\")\n\t{\n\t\tint NRB = Objects();\n\n\t\tif (next.Index() >= 0)\n\t\t{\n\t\t\tint index = next.Index();\n\t\t\tif ((index >= 0) && (index < NRB))\n\t\t\t{\n\t\t\t\tParamString paramName = next.next();\n\n\t\t\t\tFERigidBody* ob = Object(index);\n\t\t\t\tFEParam* pp = ob->FindParameter(paramName);\n\t\t\t\tif (pp) return GetParameterComponent(paramName.last(), pp);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEMaterial* mat = 0;\n\t\t\tif (next.IDString()) mat = m_fem.FindMaterial(next.IDString());\n\t\t\tif ((mat != 0) && (dynamic_cast<FERigidMaterial*>(mat)))\n\t\t\t{\n\t\t\t\tParamString paramName = next.next();\n\n\t\t\t\t// the rigid bodies are dealt with differently\n\t\t\t\tint nmat = mat->GetID() - 1;\n\t\t\t\tfor (int i = 0; i < NRB; ++i)\n\t\t\t\t{\n\t\t\t\t\tFERigidBody* ob = Object(i);\n\t\t\t\t\tif (ob && (ob->GetMaterialID() == nmat))\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParam* pp = ob->FindParameter(paramName);\n\t\t\t\t\t\tif (pp) return GetParameterComponent(paramName.last(), pp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn FEParamValue();\n}\n\n//-----------------------------------------------------------------------------\n//! Call this function after the rigid body kinematics are updated to ensure\n//! that the mesh (or at least the part of the mesh corresponding to the rigid\n//! bodies) is updated.\nvoid FERigidSystem::UpdateMesh()\n{\n\tFEMesh& mesh = m_fem.GetMesh();\n\tint N = mesh.Nodes();\n#pragma omp parallel for\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid >= 0)\n\t\t{\n\t\t\tFERigidBody& RB = *Object(node.m_rid);\n\t\t\tvec3d a0 = node.m_ra - RB.m_r0;\n\t\t\tvec3d at = RB.GetRotation()*a0;\n\t\t\tnode.m_rt = RB.m_rt + at;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidSystem::BuildMatrixProfile(FEGlobalMatrix& G)\n{\n\tif (Objects())\n\t{\n\t\tvector<int> lm(6);\n\t\tint nrb = Objects();\n\t\tfor (int i=0; i<nrb; ++i)\n\t\t{\n\t\t\tFERigidBody& rb = *Object(i);\n\t\t\tfor (int j=0; j<6; ++j) lm[j] = rb.m_LM[j];\n\t\t\tG.build_add(lm);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FERigidSystem.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreBase.h>\n#include <vector>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FEModel;\nclass FERigidBody;\nclass FEModelComponent;\nclass FERigidBC;\nclass FERigidIC;\nclass FEGlobalMatrix;\n\n//-----------------------------------------------------------------------------\n//! The FERigidSystem class manages all rigid body paraphernalia.\nclass FEBIOMECH_API FERigidSystem\n{\npublic:\n\t//! constructor\n\tFERigidSystem(FEModel* pfem);\n\n\t//! destructor\n\t~FERigidSystem();\n\n\t//! Add a rigid body\n\tvoid AddRigidBody(FERigidBody* prb);\n\n\t//! return the number of rigid bodies\n\tint Objects() const;\n\n\t//! get a rigid body\n\tFERigidBody* Object(int i);\n\n\t//! Activate\n\tvoid Activate();\n\n\t//! Clear\n\tvoid Clear();\n\n\t//! Initialization\n\t//! This creates the rigid bodies.\n\t//! (This must be called before any rigid materials are initialized!)\n\tbool Init();\n\n\t//! Initialization of Rigid bodies\n\t//! (Must be called after the mesh domains are initialized!)\n\tbool InitRigidBodies();\n\n\t//! Reset data\n\tbool Reset();\n\n\t//! place data on stream for restarts\n\tvoid Serialize(DumpStream& dmp);\n\n\t// Find a parameter (from a rigid material index)\n\tdouble* FindParameter(int nmat, ParamString& sz, int index);\n\n\tFEParamValue GetParameterValue(const ParamString& paramString);\n\n\t// update the mesh geometry\n\tvoid UpdateMesh();\n\n\t// build the matrix profile for the rigid system\n\tvoid BuildMatrixProfile(FEGlobalMatrix& G);\n\npublic:\n\tint RigidBCs() { return (int) m_RBC.size(); }\n\tFERigidBC* RigidBC(int i) { return m_RBC[i]; }\n\tvoid AddRigidBC(FERigidBC* pbc) { m_RBC.push_back(pbc); }\n\n\tvoid AddInitialCondition(FERigidIC* ric) { m_RIC.push_back(ric); }\n\n\tstd::vector<FERigidBody*>& RigidBodyList();\n\nprotected:\n\tbool CreateObjects();\n\nprotected:\n\t// Boundary/Initial conditions for rigid bodies\n\t// TODO: I'd like to do something different with this. Perhaps place them in the BC or in some constraint section.\n\tvector<FERigidBC*>\t\t\tm_RBC;\t//!< rigid body fixed\n\tvector<FERigidIC*>\t\t\tm_RIC;\t//!< rigid body initial conditions\n\nprivate:\n\tFEModel&\t\t\t\tm_fem;\t//!< the FE model this system is attached to\n\tvector<FERigidBody*>\tm_RB;\t//!< the list of rigid bodies in this system\n};\n"
  },
  {
    "path": "FEBioMech/FERigidWallInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERigidWallInterface.h\"\n#include <FECore/FENNQuery.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/log.h>\n#include <FEBioMech/FEElasticShellDomainOld.h>\n#include <FECore/FELinearSystem.h>\n\n// Macauley bracket\n#define MBRACKET(x) ((x)>=0? (x): 0)\n#define HEAVYSIDE(x) ((x)>=0?1:0)\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FERigidWallInterface, FESurfaceConstraint)\n\tADD_PARAMETER(m_laugon , \"laugon\"    )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol   , \"tolerance\" );\n\tADD_PARAMETER(m_eps    , \"penalty\"   );\n\tADD_PARAMETER(m_d\t   , \"offset\"    );\n\tADD_PARAMETER(m_a      , 4, \"plane\"  );\nEND_FECORE_CLASS();\n\n///////////////////////////////////////////////////////////////////////////////\n// FERigidWallSurface\n///////////////////////////////////////////////////////////////////////////////\n\n\nFERigidWallSurface::FERigidWallSurface(FEModel* pfem) : FESurface(pfem) \n{ \n\tm_NQ.Attach(this); \n\n\t// I want to use the FEModel class for this, but don't know how\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tm_dofX = dofs.GetDOF(\"x\");\n\t\tm_dofY = dofs.GetDOF(\"y\");\n\t\tm_dofZ = dofs.GetDOF(\"z\");\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Creates a surface for use with a sliding interface. All surface data\n//! structures are allocated.\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FERigidWallSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FESurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate other surface data\n\tm_gap.assign(nn, 0);\t\t// gap funtion\n\tm_nu.resize(nn);\t\t// node normal \n\tm_pme.assign(nn, static_cast<FESurfaceElement*>(0));\t\t// penetrated secondary surface element\n\tm_rs.resize(nn);\t\t// natural coords of projected primary node on secondary element\n\tm_rsp.resize(nn);\n\tm_Lm.assign(nn, 0);\n\tm_M.resize(nn);\n\tm_Lt.resize(nn);\n\tm_off.assign(nn, 0.0);\n\tm_eps.assign(nn, 1.0);\n\n\t// we calculate the gap offset values\n\t// This value is used to take the shell thickness into account\n\t// note that we force rigid shells to have zero thickness\n\tFEMesh& m = *m_pMesh;\n\tvector<double> tag(m.Nodes());\n\tzero(tag);\n\tfor (int nd=0; nd<m.Domains(); ++nd)\n\t{\n\t\tFEElasticShellDomainOld* psd = dynamic_cast<FEElasticShellDomainOld*>(&m.Domain(nd));\n\t\tif (psd)\n\t\t{\n\t\t\tfor (int i=0; i<psd->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEShellElement& el = psd->Element(i);\n\t\t\t\tint n = el.Nodes();\n\t\t\t\tfor (int j=0; j<n; ++j) tag[el.m_node[j]] = 0.5*el.m_h0[j];\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i=0; i<nn; ++i) m_off[i] = tag[NodeIndex(i)];\n\n\treturn true;\n}\n\n\n//-----------------------------------------------------------------------------\n//! \n\nvec3d FERigidWallSurface::traction(int inode)\n{\n\tvec3d t(0,0,0);\n\tFESurfaceElement* pe = m_pme[inode];\n\tif (pe)\n\t{\n\t\tFESurfaceElement& el = *pe;\n\t\tdouble Tn = m_Lm[inode];\n\t\tdouble T1 = m_Lt[inode][0];\n\t\tdouble T2 = m_Lt[inode][1];\n\t\tdouble r = m_rs[inode][0];\n\t\tdouble s = m_rs[inode][1];\n\n\t\tvec3d tn = m_nu[inode]*Tn, tt;\n\t\tvec3d e[2];\n\t\tContraBaseVectors0(el, r, s, e);\n\t\ttt = e[0]*T1 + e[1]*T2;\n\t\tt = tn + tt;\n\t}\n\n\treturn t;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FERigidWallSurface::UpdateNormals()\n{\n\tint i, j, jp1, jm1;\n\tint N = Nodes();\n\tint NE = Elements();\n\tfor (i=0; i<N; ++i) m_nu[i] = vec3d(0,0,0);\n\tvec3d y[FEElement::MAX_NODES], e1, e2;\n\n\tfor (i=0; i<NE; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\tjp1 = (j+1)%ne;\n\t\t\tjm1 = (j+ne-1)%ne;\n\n\t\t\te1 = y[jp1] - y[j];\n\t\t\te2 = y[jm1] - y[j];\n\n\t\t\tm_nu[el.m_lnode[j]] -= e1 ^ e2;\t\t\t\t\t\t\n\t\t}\n\t}\n\n\tfor (i=0; i<N; ++i) m_nu[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidWallSurface::Serialize(DumpStream &ar)\n{\n\tFESurface::Serialize(ar);\n\tar & m_gap;\n\tar & m_nu;\n\tar & m_rs;\n\tar & m_rsp;\n\tar & m_Lm;\n\tar & m_M;\n\tar & m_Lt;\n\tar & m_off;\n\tar & m_eps;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidWallSurface::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.resize(N*3);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\tlm[3*i  ] = id[m_dofX];\n\t\tlm[3*i+1] = id[m_dofY];\n\t\tlm[3*i+2] = id[m_dofZ];\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// FERigidWallInterface\n///////////////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\n//! constructor\nFERigidWallInterface::FERigidWallInterface(FEModel* pfem) : FESurfaceConstraint(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_laugon = FECore::PENALTY_METHOD;\n\n\tm_a[0] = m_a[1] = m_a[2] = m_a[3] = 0.0;\n\n\tm_ss = new FERigidWallSurface(pfem);\n\tm_eps = 0;\n\tm_atol = 0;\n\tm_d = 0.0;\n};\n\n//-----------------------------------------------------------------------------\nFERigidWallInterface::~FERigidWallInterface()\n{\n\tdelete m_ss;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes the rigid wall interface data\n\nbool FERigidWallInterface::Init()\n{\n\t// create the surface\n\tif (m_ss->Init() == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FERigidWallInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFERigidWallSurface& surf = *m_ss;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6);\n\tfor (int j=0; j< surf.Nodes(); ++j)\n\t{\n\t\tif (surf.m_gap[j] >= 0)\n\t\t{\n\t\t\tlm[0] = surf.Node(j).m_ID[dof_X];\n\t\t\tlm[1] = surf.Node(j).m_ID[dof_Y];\n\t\t\tlm[2] = surf.Node(j).m_ID[dof_Z];\n\t\t\tlm[3] = surf.Node(j).m_ID[dof_RU];\n\t\t\tlm[4] = surf.Node(j).m_ID[dof_RV];\n\t\t\tlm[5] = surf.Node(j).m_ID[dof_RW];\n\n\t\t\tK.build_add(lm);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidWallInterface::Activate()\n{\n\t// don't forget to call the base class\n\tFESurfaceConstraint::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(*m_ss);\n}\n\n//-----------------------------------------------------------------------------\n//!  Projects the primary surface onto the plane\n\nvoid FERigidWallInterface::ProjectSurface(FERigidWallSurface& ss)\n{\n\tFERigidWallSurface& surf = *m_ss;\n\n\t// loop over all primary surface nodes\n\tfor (int i=0; i< surf.Nodes(); ++i)\n\t{\n\t\t// get the nodal position\n\t\tvec3d r = surf.Node(i).m_rt;\n\n\t\t// project this node onto the plane\n\t\tvec3d q = ProjectToPlane(r);\n\n\t\t// get the local surface normal\n\t\tvec3d np = PlaneNormal(q);\n\n\t\t// calculate offset\n\t\tq += np*m_d;\n\n\t\t// the normal is set to the secondary surface element normal\n\t\tsurf.m_nu[i] = np;\n\t\n\t\t// calculate initial gap\n\t\tsurf.m_gap[i] = -(np*(r - q)) + surf.m_off[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//!  Updates rigid wall data\n\nvoid FERigidWallInterface::Update()\n{\n\t// project primary surface onto secondary surface\n\tProjectSurface(*m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FERigidWallInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint j, k, m, n;\n\tint nseln;\n\n\tdouble *Gr, *Gs;\n\n\t// jacobian\n\tdouble detJ;\n\n\tvec3d dxr, dxs;\n\tvec3d rt[FEElement::MAX_NODES], r0[FEElement::MAX_NODES];\n\tdouble* w;\n\n\t// normal force\n\tdouble tn;\n\n\t// element contact force vector\n\t// note that this assumes that the element to which the integration node\n\t// connects is a four noded quadrilateral\n\tvector<double> fe(3);\n\n\t// the lm array for this force vector\n\tvector<int> lm(3);\n\n\t// the en array\n\tvector<int> en(1);\n\n\tvector<int> sLM;\n\n\t// penalty value\n\tdouble pen = m_eps, eps;\n\t\n\t// loop over all primary surface facets\n\tFERigidWallSurface& surf = *m_ss;\n\n\tint ne = surf.Elements();\n\tfor (j=0; j<ne; ++j)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& sel = surf.Element(j);\n\n\t\t// get the element's LM vector\n\t\tsurf.UnpackLM(sel, sLM);\n\n\t\tnseln = sel.Nodes();\n\n\t\tfor (int i=0; i<nseln; ++i)\n\t\t{\n\t\t\tr0[i] = surf.GetMesh()->Node(sel.m_node[i]).m_r0;\n\t\t\trt[i] = surf.GetMesh()->Node(sel.m_node[i]).m_rt;\n\t\t}\n\t\tw = sel.GaussWeights();\n\n\t\t// loop over element nodes (which are the integration points as well)\n\t\tfor (n=0; n<nseln; ++n)\n\t\t{\n\t\t\tGr = sel.Gr(n);\n\t\t\tGs = sel.Gs(n);\n\n\t\t\tm = sel.m_lnode[n];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has a secondary surface element associated with it\n//\t\t\tif (ss.Lm[m] >= 0)\n\t\t\t{\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get node normal force\n\t\t\t\teps = pen* surf.m_eps[m];\n\t\t\t\ttn = surf.m_Lm[m] + eps* surf.m_gap[m];\n\t\t\t\ttn = MBRACKET(tn);\n\n\t\t\t\t// get the node normal\n\t\t\t\tvec3d& nu = surf.m_nu[m];\n\n\t\t\t\t// calculate force vector\n\t\t\t\tfe[0] = detJ*w[n]*tn*nu.x;\n\t\t\t\tfe[1] = detJ*w[n]*tn*nu.y;\n\t\t\t\tfe[2] = detJ*w[n]*tn*nu.z;\n\t\n\t\t\t\t// fill the lm array\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\t// fill the en array\n\t\t\t\ten[0] = sel.m_node[n];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness contribution for the rigid wall\n//! interface.\n//! \\todo I think there are a couple of stiffness terms missing in this formulation\n\nvoid FERigidWallInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint j, k, l, n, m;\n\tint nseln, ndof;\n\n\tFEElementMatrix ke;\n\n\tvector<int> lm(3);\n\tvector<int> en(1);\n\n\tdouble *Gr, *Gs, *w;\n\tvec3d rt[FEElement::MAX_NODES], r0[FEElement::MAX_NODES];\n\n\tdouble detJ, tn;\n\tvec3d dxr, dxs;\n\tdouble N[3];\n\n\tdouble gap, Lm;\n\n\tvector<int> sLM;\n\n\t// penalty value\n\tdouble pen = m_eps, eps;\n\n\t// loop over all primary surface elements\n\tFERigidWallSurface& surf = *m_ss;\n\tint ne = surf.Elements();\n\tfor (j=0; j<ne; ++j)\n\t{\n\t\tFESurfaceElement& se = surf.Element(j);\n\n\t\t// get the element's LM vector\n\t\tsurf.UnpackLM(se, sLM);\n\n\t\tnseln = se.Nodes();\n\n\t\tfor (int i=0; i<nseln; ++i)\n\t\t{\n\t\t\tr0[i] = surf.GetMesh()->Node(se.m_node[i]).m_r0;\n\t\t\trt[i] = surf.GetMesh()->Node(se.m_node[i]).m_rt;\n\t\t}\n\n\t\tw = se.GaussWeights();\n\n\t\t// loop over all integration points (that is nodes)\n\t\tfor (n=0; n<nseln; ++n)\n\t\t{\n\t\t\tGr = se.Gr(n);\n\t\t\tGs = se.Gs(n);\n\n\t\t\tm = se.m_lnode[n];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has a secondary surface element associated with it\n//\t\t\tif (ss.Lm[m] >= 0)\n\t\t\t{\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// gap\n\t\t\t\tgap = surf.m_gap[m];\n\n\t\t\t\t// lagrange multiplier\n\t\t\t\tLm = surf.m_Lm[m];\n\n\t\t\t\t// get node normal force\n\t\t\t\teps = pen* surf.m_eps[m];\n\n\t\t\t\ttn = surf.m_Lm[m] + eps* surf.m_gap[m];\n\t\t\t\ttn = MBRACKET(tn);\n\n\t\t\t\t// get the node normal\n\t\t\t\tvec3d& nu = surf.m_nu[m];\n\n\t\t\t\t// set up the N vector\n\t\t\t\tN[0] = nu.x;\n\t\t\t\tN[1] = nu.y;\n\t\t\t\tN[2] = nu.z;\n\n\t\t\t\tndof = 3;\n\n\t\t\t\t// fill stiffness matrix\n\t\t\t\t// TODO: I don't think this is correct, since\n\t\t\t\t// if the rigid wall is not a plance, some terms\n\t\t\t\t// are probably missing\n\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\tfor (k=0; k<ndof; ++k)\n\t\t\t\t\tfor (l=0; l<ndof; ++l)\n\t\t\t\t\t\tke[k][l] = w[n]*detJ*eps*HEAVYSIDE(Lm+eps*gap)*N[k]*N[l];\n\n\t\t\t\t// create lm array\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\t// create the en array\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\t\t\t\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nbool FERigidWallInterface::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\tdouble Lm;\n\tbool bconv = true;\n\n\tFERigidWallSurface& surf = *m_ss;\n\n\t// penalty value\n\tdouble pen = m_eps, eps;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i=0; i< surf.Nodes(); ++i) normL0 += surf.m_Lm[i]* surf.m_Lm[i];\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i=0; i< surf.Nodes(); ++i)\n\t{\n\t\t// update Lagrange multipliers\n\t\teps = pen* surf.m_eps[i];\n\n\t\tLm = surf.m_Lm[i] + eps* surf.m_gap[i];\n\t\tLm = MBRACKET(Lm);\n\t\tnormL1 += Lm*Lm;\n\t\tif (surf.m_gap[i] > 0)\n\t\t{\n\t\t\tnormgc += surf.m_gap[i]* surf.m_gap[i];\n\t\t\t++N;\n\t\t}\n\t}\t\n\tif (N==0) N = 1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\t// check convergence of constraints\n\tfeLog(\" rigid wall interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\t\t\n\tif (pctn >= m_atol)\n\t{\n\t\tbconv = false;\n\t\tfor (i=0; i< surf.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\teps = pen* surf.m_eps[i];\n\n\t\t\tLm = surf.m_Lm[i] + eps* surf.m_gap[i];\n\t\t\tsurf.m_Lm[i] = MBRACKET(Lm);\n\t\t}\t\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidWallInterface::PlaneNormal(const vec3d& r)\n{\n\tvec3d n(m_a[0], m_a[1], m_a[2]);\n\tn.unit();\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FERigidWallInterface::ProjectToPlane(const vec3d& r)\n{\n\tdouble d = m_a[3];\n\n\tdouble l = m_a[0] * r.x + m_a[1] * r.y + m_a[2] * r.z - d;\n\treturn vec3d(r.x - l * m_a[0], r.y - l * m_a[1], r.z - l * m_a[2]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidWallInterface::Serialize(DumpStream &ar)\n{\n\tFESurfaceConstraint::Serialize(ar);\n\tm_ss->Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMech/FERigidWallInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/vector.h>\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FESurface.h>\n#include <FECore/vec3d.h>\n#include <FECore/vec2d.h>\n#include <FECore/FENNQuery.h>\n\n//-----------------------------------------------------------------------------\nclass FERigidWallSurface : public FESurface\n{\npublic:\n\t//! constructor\n\tFERigidWallSurface(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\n\t//! Update the surface data\n\tvoid Update() {}\n\n\t//! Calculate the total traction at a node\n\tvec3d traction(int inode);\n\n\tvoid UpdateNormals();\n\n\tvoid Serialize(DumpStream& ar);\n\n\t//! Unpack surface element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm);\n\npublic:\n\tvector<double>\t\t\t\tm_gap;\t//!< gap function at nodes\n\tvector<vec3d>\t\t\t\tm_nu;\t//!< secondary surface normal at primary surface node\n\tvector<FESurfaceElement*>\tm_pme;\t//!< secondary surface element a primary surface node penetrates\n\tvector<vec2d>\t\t\t\tm_rs;\t//!< natural coordinates of projection on secondary surface element\n\tvector<vec2d>\t\t\t\tm_rsp;\t//!< natural coordinates at previous time step\n\tvector<double>\t\t\t\tm_Lm;\t//!< Lagrange multipliers for contact pressure\n\tvector<mat2d>\t\t\t\tm_M;\t//!< surface metric tensor\n\tvector<vec2d>\t\t\t\tm_Lt;\t//!< Lagrange multipliers for friction\n\tvector<double>\t\t\t\tm_off;\t//!< gap offset (= shell thickness)\n\tvector<double>\t\t\t\tm_eps;\t//!< normal penalty factors\n\n\tint\tm_dofX;\n\tint\tm_dofY;\n\tint\tm_dofZ;\n\n\tFENNQuery\t\tm_NQ;\t\t//!< used in finding the secondary surface element that corresponds to a primary surface node\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a sliding contact interface with a rigid wall\n\n//! This class is a specialization of the general sliding interface where\n//! the secondary surface is a rigid wall\n\nclass FERigidWallInterface : public FESurfaceConstraint\n{\npublic:\n\t//! constructor\n\tFERigidWallInterface(FEModel* pfem);\n\n\t~FERigidWallInterface();\n\n\t//! intializes rigid wall interface\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetSurface() override { return m_ss; }\n\n\t//! return integration rule class\n\t//! TODO: Need to fix this! Maybe move to FESurfaceConstraint?\n//\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprivate:\n\t//! project surface nodes onto plane\n\tvoid ProjectSurface(FERigidWallSurface& s);\n\nprivate:\n\t//! return plane normal\n\tvec3d PlaneNormal(const vec3d& r);\n\n\t//! project node onto plane\n\tvec3d ProjectToPlane(const vec3d& r);\n\npublic:\n\tFERigidWallSurface*\tm_ss;\t\t//!< primary surface\n\n\tdouble\tm_a[4];\t//!< plane equation\n\n\tint\t\t\tm_laugon;\t//!< augmentation flag\n\tdouble\t\tm_atol;\t\t//!< augmentation tolerance\n\tdouble\t\tm_eps;\t\t//!< penalty scale factor\n\tdouble\t\tm_d;\t\t//!< normal offset\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESRIElasticSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESRIElasticSolidDomain.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FECore/FEElementLibrary.h\"\n\n//-----------------------------------------------------------------------------\nFESRIElasticSolidDomain::FESRIElasticSolidDomain(FEModel* pfem) : FEElasticSolidDomain(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FESRIElasticSolidDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n\t// jacobian matrix, inverse jacobian matrix and determinants\n\tdouble Ji[3][3];\n\n\t// first, evaluate the deviatoric component using the full-integration rule\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tdouble*\tgw = el.GaussWeights();\n\n\t// repeat for all integration points\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// calculate the jacobian\n\t\tdouble detJt = invjact(el, Ji, n);\n\n\t\tdetJt *= gw[n];\n\n\t\t// get the stress vector for this integration point\n\t\t// we only use the deviatoric component\n\t\tmat3ds s = pt.m_s.dev();\n\n\t\tdouble* Gr = el.Gr(n);\n\t\tdouble* Gs = el.Gs(n);\n\t\tdouble* Gt = el.Gt(n);\n\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tdouble Gx = Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i];\n\t\t\tdouble Gy = Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i];\n\t\t\tdouble Gz = Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i];\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[3*i  ] -= ( Gx*s.xx() +\n\t\t\t\t           Gy*s.xy() +\n\t\t\t\t\t       Gz*s.xz() )*detJt;\n\n\t\t\tfe[3*i+1] -= ( Gy*s.yy() +\n\t\t\t\t           Gx*s.xy() +\n\t\t\t\t\t       Gz*s.yz() )*detJt;\n\n\t\t\tfe[3*i+2] -= ( Gz*s.zz() +\n\t\t\t\t           Gy*s.yz() +\n\t\t\t\t\t       Gx*s.xz() )*detJt;\n\t\t}\n\t}\n\n\t// next, the pressure component is evaluated using the reduced integration rule\n\tconst int NME = FEElement::MAX_NODES;\n\tdouble Gr[NME], Gs[NME], Gt[NME];\n\tFESRISolidElementTraits* prt_ri = dynamic_cast<FESRISolidElementTraits*>(el.GetTraits());\n\n\t// if this element does not have a reduced rule, we use the full integration rule\n\tFESolidElementTraits* prt = (prt_ri ? prt_ri->m_pTRI : dynamic_cast<FESolidElementTraits*>(el.GetTraits()));\n\n\tnint = prt->m_nint;\n\tvector<double>& gr = prt->gr;\n\tvector<double>& gs = prt->gs;\n\tvector<double>& gt = prt->gt;\n\tgw = &(prt->gw[0]);\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tdouble r = gr[n];\n\t\tdouble s = gs[n];\n\t\tdouble t = gt[n];\n\n\t\t// setup the material point\n\t\tFEElasticMaterialPoint pt;\n\t\tFEMaterialPoint mp(&pt);\n\n\t\tpt.m_J = defgrad(el, pt.m_F, r, s, t);\n\n\t\t// calculate the jacobian\n\t\tdouble detJt = invjact(el, Ji, r, s, t);\n\n\t\t// get the shape function derivatives\n\t\tel.shape_deriv(Gr, Gs, Gt, r, s, t);\n\n\t\t// calculate the stress\n\t\tmat3ds stress = m_pMat->Stress(mp);\n\n\t\t// evaluate the pressure\n\t\tdouble p = -(stress.tr()/3.0);\n\n\t\tdetJt *= gw[n];\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tdouble Gx = Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i];\n\t\t\tdouble Gy = Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i];\n\t\t\tdouble Gz = Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i];\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[3*i  ] += ( Gx*p )*detJt;\n\t\t\tfe[3*i+1] += ( Gy*p )*detJt;\n\t\t\tfe[3*i+2] += ( Gz*p )*detJt;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FESRIElasticSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Class implementing selective reduced integration for the evaluation of the internal force vector\nclass FESRIElasticSolidDomain : public FEElasticSolidDomain\n{\npublic:\n\tFESRIElasticSolidDomain(FEModel* pfem);\n\npublic:\n\t//! internal stress forces\n\tvirtual void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n};\n"
  },
  {
    "path": "FEBioMech/FESSIShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESSIShellDomain.h\"\n#include \"FECore/FEElemElemList.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEModelParam.h>\n#include <FECore/FEDataStream.h>\n#include <FECore/log.h>\n#include <FECore/FEMesh.h>\n#include \"FEBioMech.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FESSIShellDomain, FEShellDomainNew)\n\tADD_PARAMETER(m_bnodalnormals, \"shell_normal_nodal\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESSIShellDomain::FESSIShellDomain(FEModel* pfem) : FEShellDomainNew(pfem), m_dofU(pfem), m_dofSU(pfem), m_dofR(pfem)\n{\n    m_bnodalnormals = true;\n\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n        m_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESSIShellDomain::Init()\n{\n\tFEShellDomain::Init();\n    FindSSI();\n\n\t// check for initially inverted shells\n\ttry {\n\t\tfor (int i = 0; i < Elements(); ++i)\n\t\t{\n\t\t\tFEShellElementNew& el = ShellElement(i);\n\t\t\tint neln = el.Nodes();\n\t\t\tint nint = el.GaussPoints();\n\t\t\tel.m_E.resize(nint, mat3ds(0, 0, 0, 0, 0, 0));\n\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tdouble J0 = detJ0(el, n);\n\t\t\t}\n\t\t}\n\t}\n\tcatch (NegativeJacobian e)\n\t{\n\t\tfeLogError(\"Zero or negative jacobians detected at integration points of domain: %s\\n\", GetName().c_str());\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESSIShellDomain::Serialize(DumpStream& ar)\n{\n\tFEShellDomainNew::Serialize(ar);\n\tar & m_bnodalnormals;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate all shell normals (i.e. the shell directors).\n//! And find shell nodes\nbool FESSIShellDomain::InitShells()\n{\n\tif (!FEShellDomain::InitShells()) return false;\n\n\tFEMesh& mesh = *GetMesh();\n\n\t// check for zero shell thickness\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFEShellElementNew& el = ShellElement(i);\n\t\tint nn = el.Nodes();\n\t\tfor (int j = 0; j < nn; ++j)\n\t\t{\n\t\t\tif (el.m_h0[j] <= 0.0)\n\t\t\t{\n\t\t\t\tstring name = GetName();\n\t\t\t\tfeLogError(\"Zero shell thickness found in \\\"%s\\\"\", name.c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n    if (!m_bnodalnormals) {\n        for (int i = 0; i<Elements(); ++i)\n        {\n            FEShellElementNew& el = ShellElement(i);\n            int ne = el.Nodes();\n            vec3d gr(0,0,0), gs(0,0,0);\n            double Mr[FEElement::MAX_NODES], Ms[FEElement::MAX_NODES];\n            el.shape_deriv(Mr, Ms, 0, 0);\n            for (int j = 0; j<ne; ++j)\n            {\n                gr += mesh.Node(el.m_node[j]).m_r0*Mr[j];\n                gs += mesh.Node(el.m_node[j]).m_r0*Ms[j];\n            }\n            vec3d d0 = gr ^ gs;\n            d0.unit();\n            for (int j = 0; j<ne; ++j)\n            {\n                el.m_d0[j] = d0 * el.m_h0[j];\n            }\n        }\n    }\n\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFEShellElementNew& el = ShellElement(i);\n\t\tint ni = el.GaussPoints();\n\t\tvec3d g0[3];\n\t\tfor (int j = 0; j < ni; ++j)\n\t\t{\n\t\t\t//NOTE: calculate covariant first since contravariant depends on it!\n\t\t\tCoBaseVectors0(el, j, g0);\n\t\t\tel.m_gt[0][j] = el.m_gp[0][j] = el.m_g0[0][j] = g0[0];\n\t\t\tel.m_gt[1][j] = el.m_gp[1][j] = el.m_g0[1][j] = g0[1];\n\t\t\tel.m_gt[2][j] = el.m_gp[2][j] = el.m_g0[2][j] = g0[2];\n\n\t\t\tContraBaseVectors0(el, j, g0);\n\t\t\tel.m_G0[0][j] = el.m_Gt[0][j] = g0[0];\n\t\t\tel.m_G0[1][j] = el.m_Gt[1][j] = g0[1];\n\t\t\tel.m_G0[2][j] = el.m_Gt[2][j] = g0[2];\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESSIShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tFEShellDomain::PreSolveUpdate(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\n//! Find interfaces between solid element faces and shell elements\n//!\n\nvoid FESSIShellDomain::FindSSI()\n{\n\t// find out if there are solid domains in this model\n\tvector<FESolidDomain*> psd;\n\tFEMesh& mesh = *GetMesh();\n\tint ndom = mesh.Domains();\n\tfor (int i = 0; i<ndom; ++i)\n\t{\n\t\tFEDomain& pdom = mesh.Domain(i);\n\t\tFESolidDomain* psdom = dynamic_cast<FESolidDomain*>(&pdom);\n\t\tif (psdom) psd.push_back(psdom);\n\t}\n\tsize_t nsdom = psd.size();\n\n\t// if there are no solid domains we're done\n\tif (nsdom == 0) return;\n\n\t// tag all nodes that belong to this shell domain\n\tvector<int> tag(mesh.Nodes(), 0);\n\tfor (int i=0; i<Nodes(); ++i) tag[m_Node[i]] = 1;\n\n\tint nelem = Elements();\n\tint nf[9];\n\tvec3d g[3];\n\n\t// loop over all solid domains\n\tfor (int i=0; i<nsdom; ++i)\n\t{\n\t\t// get the next domain\n\t\tFESolidDomain& di = *psd[i];\n\n\t\t// see if there are any potential candidates\n\t\t// NOTE: We can't use the domain's node list since it might not be initialized yet\n\t\t//       since shell domains are initialized before solid domains\n\t\tbool hasNodes = false;\n\t\tfor (int j = 0; j < di.Elements(); ++j)\n\t\t{\n\t\t\tFEElement& el = di.Element(j);\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int k = 0; k < ne; ++k)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(el.m_node[k]);\n\t\t\t\tif (tag[el.m_node[k]] == 1)\n\t\t\t\t{\n\t\t\t\t\thasNodes = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasNodes) break;\n\t\t}\n\n\t\t// if the solid domain shares nodes with this shell domain,\n\t\t// there might be matches in this domain\n\t\tif (hasNodes)\n\t\t{\n\t\t\t// build a node-element list for faster lookup\n\t\t\tFENodeElemList NEL; NEL.Create(di);\n\n\t\t\t// check all shell elements\n\t\t\tfor (int l = 0; l < nelem; ++l)\n\t\t\t{\n\t\t\t\tFEShellElement& sel = Element(l);\n\n\t\t\t\tint n0 = sel.m_node[0];\n\t\t\t\tint nval = NEL.Valence(n0);\n\n\t\t\t\t// see if we can match any shells\n\t\t\t\tFEElement** ppe = NEL.ElementList(n0);\n\t\t\t\tfor (int j = 0; j < nval; ++j)\n\t\t\t\t{\n\t\t\t\t\tFESolidElement& elj = dynamic_cast<FESolidElement&>(*ppe[j]);\n\n\t\t\t\t\t// loop over all its faces\n\t\t\t\t\tint nfaces = elj.Faces();\n\t\t\t\t\tfor (int k = 0; k < nfaces; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tint nn = elj.GetFace(k, nf);\n\n\t\t\t\t\t\t// see if it matches this face\n\t\t\t\t\t\tbool found = false;\n\t\t\t\t\t\tif (nn == sel.Nodes())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch (nn)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase 3: if (sel.HasNode(nf[0]) && sel.HasNode(nf[1]) && sel.HasNode(nf[2])) found = true; break;\n\t\t\t\t\t\t\tcase 4: if (sel.HasNode(nf[0]) && sel.HasNode(nf[1]) && sel.HasNode(nf[2]) && sel.HasNode(nf[3])) found = true; break;\n\t\t\t\t\t\t\tcase 6: if (sel.HasNode(nf[0]) && sel.HasNode(nf[1]) && sel.HasNode(nf[2])) found = true; break;\n\t\t\t\t\t\t\tcase 7: if (sel.HasNode(nf[0]) && sel.HasNode(nf[1]) && sel.HasNode(nf[2])) found = true; break;\n\t\t\t\t\t\t\tcase 8: if (sel.HasNode(nf[0]) && sel.HasNode(nf[1]) && sel.HasNode(nf[2]) && sel.HasNode(nf[3])) found = true; break;\n\t\t\t\t\t\t\tcase 9: if (sel.HasNode(nf[0]) && sel.HasNode(nf[1]) && sel.HasNode(nf[2]) && sel.HasNode(nf[3])) found = true; break;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tassert(false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// see if we found a match\n\t\t\t\t\t\tif (found)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// check interface side\n\t\t\t\t\t\t\t// get outward normal to solid element face\n\t\t\t\t\t\t\tvec3d n0 = mesh.Node(nf[0]).m_r0, n1 = mesh.Node(nf[1]).m_r0, n2 = mesh.Node(nf[2]).m_r0;\n\t\t\t\t\t\t\tvec3d nsld = (n1 - n0) ^ (n2 - n1);\n\t\t\t\t\t\t\t// get outward normal to shell face\n\t\t\t\t\t\t\tCoBaseVectors0(sel, 0, g);\n\t\t\t\t\t\t\tvec3d nshl = g[2];\n\n\t\t\t\t\t\t\t// compare normals\n\t\t\t\t\t\t\tif (nsld*nshl > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// set solid element attached to shell back face\n\t\t\t\t\t\t\t\tsel.m_elem[0] = elj.GetID();\n\n\t\t\t\t\t\t\t\t// store result\n\t\t\t\t\t\t\t\tif (m_bnodalnormals) {\n\t\t\t\t\t\t\t\t\telj.m_bitfc.resize(elj.Nodes(), false);\n\t\t\t\t\t\t\t\t\tfor (int n = 0; n < nn; ++n) {\n\t\t\t\t\t\t\t\t\t\tint m = elj.FindNode(nf[n]);\n\t\t\t\t\t\t\t\t\t\tif (m > -1) elj.m_bitfc[m] = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// set solid element attached to shell front face\n\t\t\t\t\t\t\t\tsel.m_elem[1] = elj.GetID();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// the same element cannot be both front and back of a shell\n\t\t\t\t\t\t\t// so we can leave the loop over the faces\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n    \n    // check for elements that only have one or two shell nodes\n    // but don't share a whole face\n\n    // get the node element list\n    FENodeElemList& NEL = mesh.NodeElementList();\n    \n    for (int i = 0; i<ndom; ++i) {\n        FEDomain& pdom = mesh.Domain(i);\n        FEShellDomain* psdom = dynamic_cast<FEShellDomain*>(&pdom);\n        if (psdom) {\n            // find the solid domain attached to the back of these shells\n            FESolidDomain* sldmn = nullptr;\n            for (int j=0; j<psdom->Elements(); ++j) {\n                FEShellElement& el1 = psdom->Element(j);\n                // identify solid domain at back of shell domain\n                if (el1.m_elem[0] != -1) {\n                    FEElement* sel = mesh.FindElementFromID(el1.m_elem[0]);\n                    if (sel) sldmn = dynamic_cast<FESolidDomain*>(sel->GetMeshPartition());\n                    break;\n                }\n            }\n            if (sldmn) {\n                // for each node in this shell domain, check the solid elements it belongs to\n                for (int j=0; j<psdom->Nodes(); ++j) {\n                    int nid = psdom->NodeIndex(j);\n                    int nval = NEL.Valence(nid);\n                    FEElement** pe = NEL.ElementList(nid);\n                    for (int k=0; k<nval; ++k)\n                    {\n                        // get the element\n                        FEElement& el = *pe[k];\n                        // check that it belongs to the solid domain at the back of the shell domain\n                        if ((el.GetMeshPartition() == sldmn) && m_bnodalnormals)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFESolidElement& sel = dynamic_cast<FESolidElement&>(el);\n                            if (sel.m_bitfc.size() == 0)\n                                sel.m_bitfc.resize(el.Nodes(), false);\n                            int lid = sel.FindNode(nid);\n                            sel.m_bitfc[lid] = true;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FESSIShellDomain::CoBaseVectors0(FEShellElement& el, int n, vec3d g[3])\n{\n\tint i;\n\n\tint neln = el.Nodes();\n\n\t// current nodal coordinates and directors\n\tvec3d r[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n\tfor (i = 0; i<neln; ++i)\n\t{\n\t\tFENode& ni = m_pMesh->Node(el.m_node[i]);\n\t\tr[i] = ni.m_r0;\n        D[i] = m_bnodalnormals ? ni.m_d0 : el.m_d0[i];\n\t}\n\n\tdouble eta = el.gt(n);\n\n\tdouble* Mr = el.Hr(n);\n\tdouble* Ms = el.Hs(n);\n\tdouble* M = el.H(n);\n\n\t// initialize covariant basis vectors\n\tg[0] = g[1] = g[2] = vec3d(0, 0, 0);\n\n\tfor (i = 0; i<neln; ++i)\n\t{\n\t\tg[0] += (r[i] - D[i] * (1 - eta) / 2)*Mr[i];\n\t\tg[1] += (r[i] - D[i] * (1 - eta) / 2)*Ms[i];\n\t\tg[2] += D[i] * (M[i] / 2);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FESSIShellDomain::CoBaseVectors0(FEShellElement& el, double r, double s, double t, vec3d g[3])\n{\n    int i;\n    \n    int neln = el.Nodes();\n    \n    // current nodal coordinates and directors\n    vec3d r0[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n    for (i = 0; i<neln; ++i)\n    {\n        FENode& ni = m_pMesh->Node(el.m_node[i]);\n        r0[i] = ni.m_r0;\n        D[i] = m_bnodalnormals ? ni.m_d0 : el.m_d0[i];\n    }\n    \n    double eta = t;\n    \n    double M[FEElement::MAX_NODES];\n    double Mr[FEElement::MAX_NODES];\n    double Ms[FEElement::MAX_NODES];\n    el.shape_fnc(M, r, s);\n    el.shape_deriv(Mr, Ms, r, s);\n    \n    // initialize covariant basis vectors\n    g[0] = g[1] = g[2] = vec3d(0, 0, 0);\n    \n    for (i = 0; i<neln; ++i)\n    {\n        g[0] += (r0[i] - D[i] * (1 - eta) / 2)*Mr[i];\n        g[1] += (r0[i] - D[i] * (1 - eta) / 2)*Ms[i];\n        g[2] += D[i] * (M[i] / 2);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FESSIShellDomain::ContraBaseVectors0(FEShellElement& el, int n, vec3d gcnt[3])\n{\n\tvec3d gcov[3];\n//\tCoBaseVectors0(el, n, gcov);\n\tgcov[0] = el.m_g0[0][n];\n\tgcov[1] = el.m_g0[1][n];\n\tgcov[2] = el.m_g0[2][n];\n\n\tmat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n\t\tgcov[0].y, gcov[1].y, gcov[2].y,\n\t\tgcov[0].z, gcov[1].z, gcov[2].z);\n\tmat3d Ji = J.inverse();\n\n\tgcnt[0] = vec3d(Ji(0, 0), Ji(0, 1), Ji(0, 2));\n\tgcnt[1] = vec3d(Ji(1, 0), Ji(1, 1), Ji(1, 2));\n\tgcnt[2] = vec3d(Ji(2, 0), Ji(2, 1), Ji(2, 2));\n\n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FESSIShellDomain::ContraBaseVectors0(FEShellElement& el, double r, double s, double t, vec3d gcnt[3])\n{\n    vec3d gcov[3];\n    CoBaseVectors0(el, r, s, t, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0, 0), Ji(0, 1), Ji(0, 2));\n    gcnt[1] = vec3d(Ji(1, 0), Ji(1, 1), Ji(1, 2));\n    gcnt[2] = vec3d(Ji(2, 0), Ji(2, 1), Ji(2, 2));\n    \n}\n\n//-----------------------------------------------------------------------------\ndouble FESSIShellDomain::invjac0(FEShellElement& el, double Ji[3][3], int n)\n{\n\tvec3d gcov[3];\n//\tCoBaseVectors0(el, n, gcov);\n\tgcov[0] = el.m_g0[0][n];\n\tgcov[1] = el.m_g0[1][n];\n\tgcov[2] = el.m_g0[2][n];\n\n\tmat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n\t\tgcov[0].y, gcov[1].y, gcov[2].y,\n\t\tgcov[0].z, gcov[1].z, gcov[2].z);\n\n\tdouble det = J.det();\n\n\t// make sure the determinant is positive\n\tif (det <= 0) throw NegativeJacobian(el.GetID(), n + 1, det);\n\n\t// calculate the inverse of the jacobian\n\tdouble deti = 1.0 / det;\n\n\tJi[0][0] = deti*(J[1][1] * J[2][2] - J[1][2] * J[2][1]);\n\tJi[1][0] = deti*(J[1][2] * J[2][0] - J[1][0] * J[2][2]);\n\tJi[2][0] = deti*(J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\tJi[0][1] = deti*(J[0][2] * J[2][1] - J[0][1] * J[2][2]);\n\tJi[1][1] = deti*(J[0][0] * J[2][2] - J[0][2] * J[2][0]);\n\tJi[2][1] = deti*(J[0][1] * J[2][0] - J[0][0] * J[2][1]);\n\n\tJi[0][2] = deti*(J[0][1] * J[1][2] - J[1][1] * J[0][2]);\n\tJi[1][2] = deti*(J[0][2] * J[1][0] - J[0][0] * J[1][2]);\n\tJi[2][2] = deti*(J[0][0] * J[1][1] - J[0][1] * J[1][0]);\n\n\treturn det;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESSIShellDomain::invjac0(FEShellElement& el, double Ji[3][3], double r, double s, double t)\n{\n    vec3d gcov[3];\n    CoBaseVectors0(el, r, s, t, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    \n    double det = J.det();\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), -1, det);\n    \n    // calculate the inverse of the jacobian\n    double deti = 1.0 / det;\n    \n    Ji[0][0] = deti*(J[1][1] * J[2][2] - J[1][2] * J[2][1]);\n    Ji[1][0] = deti*(J[1][2] * J[2][0] - J[1][0] * J[2][2]);\n    Ji[2][0] = deti*(J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n    \n    Ji[0][1] = deti*(J[0][2] * J[2][1] - J[0][1] * J[2][2]);\n    Ji[1][1] = deti*(J[0][0] * J[2][2] - J[0][2] * J[2][0]);\n    Ji[2][1] = deti*(J[0][1] * J[2][0] - J[0][0] * J[2][1]);\n    \n    Ji[0][2] = deti*(J[0][1] * J[1][2] - J[1][1] * J[0][2]);\n    Ji[1][2] = deti*(J[0][2] * J[1][0] - J[0][0] * J[1][2]);\n    Ji[2][2] = deti*(J[0][0] * J[1][1] - J[0][1] * J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to reference frame\ndouble FESSIShellDomain::detJ0(FEShellElement &el, int n)\n{\n\tvec3d gcov[3];\n//\tCoBaseVectors0(el, n, gcov);\n\tgcov[0] = el.m_g0[0][n];\n\tgcov[1] = el.m_g0[1][n];\n\tgcov[2] = el.m_g0[2][n];\n\n\tmat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n\t\tgcov[0].y, gcov[1].y, gcov[2].y,\n\t\tgcov[0].z, gcov[1].z, gcov[2].z);\n\treturn J.det();\n}\n\n//-----------------------------------------------------------------------------\n// jacobian with respect to current frame\ndouble FESSIShellDomain::detJ0(FEShellElement& el, double r, double s, double t)\n{\n    vec3d Gcov[3];\n    CoBaseVectors0(el, r, s, t, Gcov);\n    \n    mat3d J = mat3d(Gcov[0].x, Gcov[1].x, Gcov[2].x,\n                    Gcov[0].y, Gcov[1].y, Gcov[2].y,\n                    Gcov[0].z, Gcov[1].z, Gcov[2].z);\n    return J.det();\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FESSIShellDomain::CoBaseVectors(FEShellElement& el, int n, vec3d g[3])\n{\n    int neln = el.Nodes();\n    \n    // current nodal coordinates and directors\n    vec3d r[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n    {\n        FENode& ni = m_pMesh->Node(el.m_node[i]);\n        r[i] = ni.m_rt;\n        D[i] = m_bnodalnormals ? ni.m_d0 : el.m_d0[i];\n        D[i] +=  ni.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]) - ni.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n    }\n    \n    double eta = el.gt(n);\n    \n    double* Mr = el.Hr(n);\n    double* Ms = el.Hs(n);\n    double* M  = el.H(n);\n    \n    // initialize covariant basis vectors\n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n    \n    for (int i=0; i<neln; ++i)\n    {\n        g[0] += (r[i] - D[i]*(1-eta)/2)*Mr[i];\n        g[1] += (r[i] - D[i]*(1-eta)/2)*Ms[i];\n        g[2] += D[i]*(M[i]/2);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point at previous time\nvoid FESSIShellDomain::CoBaseVectorsP(FEShellElement& el, int n, vec3d g[3])\n{\n    int i;\n    \n    int neln = el.Nodes();\n    \n    // previous time nodal coordinates and directors\n    vec3d r[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n    for (i=0; i<neln; ++i)\n    {\n        FENode& ni = m_pMesh->Node(el.m_node[i]);\n        r[i] = ni.m_rp;\n        D[i] = m_bnodalnormals ? ni.m_d0 : el.m_d0[i];\n        D[i] += ni.m_rp - ni.m_r0 - ni.get_vec3d_prev(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n    }\n    \n    double eta = el.gt(n);\n    \n    double* Mr = el.Hr(n);\n    double* Ms = el.Hs(n);\n    double* M  = el.H(n);\n    \n    // initialize covariant basis vectors\n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n    \n    for (i=0; i<neln; ++i)\n    {\n        g[0] += (r[i] - D[i]*(1-eta)/2)*Mr[i];\n        g[1] += (r[i] - D[i]*(1-eta)/2)*Ms[i];\n        g[2] += D[i]*(M[i]/2);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point at intermediate time\nvoid FESSIShellDomain::CoBaseVectors(FEShellElement& el, int n, vec3d g[3], const double alpha)\n{\n    vec3d gt[3], gp[3];\n//    CoBaseVectors(el, n, gt);\n\tgt[0] = el.m_gt[0][n];\n\tgt[1] = el.m_gt[1][n];\n\tgt[2] = el.m_gt[2][n];\n//    CoBaseVectorsP(el, n, gp);\n\tgp[0] = el.m_gp[0][n];\n\tgp[1] = el.m_gp[1][n];\n\tgp[2] = el.m_gp[2][n];\n    for (int i=0; i<3; ++i)\n        g[i] = gt[i]*alpha + gp[i]*(1-alpha);\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FESSIShellDomain::CoBaseVectors(FEShellElement& el, double r, double s, double t, vec3d g[3])\n{\n    int i;\n    \n    int neln = el.Nodes();\n    \n    // current nodal coordinates and directors\n    vec3d rt[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n    for (i=0; i<neln; ++i)\n    {\n        FENode& ni = m_pMesh->Node(el.m_node[i]);\n        rt[i] = ni.m_rt;\n        D[i] = m_bnodalnormals ? ni.m_d0 : el.m_d0[i];\n        D[i] += ni.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]) - ni.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n    }\n    \n    double eta = t;\n    \n    double M[FEElement::MAX_NODES];\n    double Mr[FEElement::MAX_NODES];\n    double Ms[FEElement::MAX_NODES];\n    el.shape_fnc(M, r, s);\n    el.shape_deriv(Mr, Ms, r, s);\n    \n    // initialize covariant basis vectors\n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n    \n    for (i=0; i<neln; ++i)\n    {\n        g[0] += (rt[i] - D[i]*(1-eta)/2)*Mr[i];\n        g[1] += (rt[i] - D[i]*(1-eta)/2)*Ms[i];\n        g[2] += D[i]*(M[i]/2);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates covariant basis vectors at an integration point\nvoid FESSIShellDomain::CoBaseVectors(FEShellElement& el, double r, double s, double t, vec3d g[3], const double alpha)\n{\n    int i;\n    \n    int neln = el.Nodes();\n    \n    // current nodal coordinates and directors\n    vec3d rt[FEElement::MAX_NODES], D[FEElement::MAX_NODES];\n    for (i=0; i<neln; ++i)\n    {\n        FENode& ni = m_pMesh->Node(el.m_node[i]);\n        rt[i] = ni.m_rt*alpha + ni.m_rp*(1-alpha);\n        D[i] = m_bnodalnormals ? ni.m_d0 : el.m_d0[i];\n        D[i] += (ni.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]) - ni.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]))*alpha\n        + (ni.get_vec3d_prev(m_dofU[0], m_dofU[1], m_dofU[2]) - ni.get_vec3d_prev(m_dofSU[0], m_dofSU[1], m_dofSU[2]))*(1-alpha);\n    }\n    \n    double eta = t;\n    \n    double M[FEElement::MAX_NODES];\n    double Mr[FEElement::MAX_NODES];\n    double Ms[FEElement::MAX_NODES];\n    el.shape_fnc(M, r, s);\n    el.shape_deriv(Mr, Ms, r, s);\n    \n    // initialize covariant basis vectors\n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n    \n    for (i=0; i<neln; ++i)\n    {\n        g[0] += (rt[i] - D[i]*(1-eta)/2)*Mr[i];\n        g[1] += (rt[i] - D[i]*(1-eta)/2)*Ms[i];\n        g[2] += D[i]*(M[i]/2);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FESSIShellDomain::ContraBaseVectors(FEShellElement& el, int n, vec3d gcnt[3])\n{\n    vec3d gcov[3];\n//    CoBaseVectors(el, n, gcov);\n\tgcov[0] = el.m_gt[0][n];\n\tgcov[1] = el.m_gt[1][n];\n\tgcov[2] = el.m_gt[2][n];\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n    \n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FESSIShellDomain::ContraBaseVectors(FEShellElement& el, int n, vec3d gcnt[3], const double alpha)\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, n, gcov, alpha);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n    \n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FESSIShellDomain::ContraBaseVectors(FEShellElement& el, double r, double s, double t, vec3d gcnt[3])\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, r, s, t, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n    \n}\n\n//-----------------------------------------------------------------------------\n//! calculates contravariant basis vectors at an integration point\nvoid FESSIShellDomain::ContraBaseVectors(FEShellElement& el, double r, double s, double t, vec3d gcnt[3], const double alpha)\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, r, s, t, gcov, alpha);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n    \n}\n\n//-----------------------------------------------------------------------------\n// jacobian with respect to current frame\ndouble FESSIShellDomain::detJ(FEShellElement& el, int n)\n{\n    vec3d gcov[3];\n//    CoBaseVectors(el, n, gcov);\n\tgcov[0] = el.m_gt[0][n];\n\tgcov[1] = el.m_gt[1][n];\n\tgcov[2] = el.m_gt[2][n];\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    return J.det();\n}\n\n//-----------------------------------------------------------------------------\n// jacobian with respect to intermediate time frame\ndouble FESSIShellDomain::detJ(FEShellElement& el, int n, const double alpha)\n{\n    vec3d gcovt[3], gcovp[3], gcov[3];\n//    CoBaseVectors(el, n, gcovt);\n\tgcovt[0] = el.m_gt[0][n];\n\tgcovt[1] = el.m_gt[1][n];\n\tgcovt[2] = el.m_gt[2][n];\n//    CoBaseVectorsP(el, n, gcovp);\n\tgcovp[0] = el.m_gp[0][n];\n\tgcovp[1] = el.m_gp[1][n];\n\tgcovp[2] = el.m_gp[2][n];\n\tfor (int i=0; i<3; ++i)\n        gcov[i] = gcovt[i]*alpha + gcovp[i]*(1-alpha);\n\n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    return J.det();\n}\n\n//-----------------------------------------------------------------------------\n// jacobian with respect to current frame\ndouble FESSIShellDomain::detJ(FEShellElement& el, double r, double s, double t)\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, r, s, t, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    return J.det();\n}\n\n//-----------------------------------------------------------------------------\ndouble FESSIShellDomain::defgrad(FEShellElement& el, mat3d& F, int n)\n{\n    vec3d gcov[3], Gcnt[3];\n//    CoBaseVectors(el, n, gcov);\n\tgcov[0] = el.m_gt[0][n];\n\tgcov[1] = el.m_gt[1][n];\n\tgcov[2] = el.m_gt[2][n];\n//\tContraBaseVectors0(el, n, Gcnt);\n\tGcnt[0] = el.m_G0[0][n];\n\tGcnt[1] = el.m_G0[1][n];\n\tGcnt[2] = el.m_G0[2][n];\n\n    F = (gcov[0] & Gcnt[0]) + (gcov[1] & Gcnt[1]) + (gcov[2] & Gcnt[2]);\n    double J = F.det();\n    if (J <= 0) throw NegativeJacobian(el.GetID(), n, J, &el);\n    \n    return J;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESSIShellDomain::defgradp(FEShellElement& el, mat3d& F, int n)\n{\n    vec3d gcov[3], Gcnt[3];\n//    CoBaseVectorsP(el, n, gcov);\n\tgcov[0] = el.m_gp[0][n];\n\tgcov[1] = el.m_gp[1][n];\n\tgcov[2] = el.m_gp[2][n];\n//    ContraBaseVectors0(el, n, Gcnt);\n\tGcnt[0] = el.m_G0[0][n];\n\tGcnt[1] = el.m_G0[1][n];\n\tGcnt[2] = el.m_G0[2][n];\n\n    F = (gcov[0] & Gcnt[0]) + (gcov[1] & Gcnt[1]) + (gcov[2] & Gcnt[2]);\n    double J = F.det();\n    if (J <= 0) throw NegativeJacobian(el.GetID(), n, J, &el);\n    \n    return J;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESSIShellDomain::defgrad(FEShellElement& el, mat3d& F, double r, double s, double t)\n{\n    vec3d gcov[3], Gcnt[3];\n    CoBaseVectors(el, r, s, t, gcov);\n    ContraBaseVectors0(el, r, s, t, Gcnt);\n    \n    F = (gcov[0] & Gcnt[0]) + (gcov[1] & Gcnt[1]) + (gcov[2] & Gcnt[2]);\n    double J = F.det();\n    if (J <= 0) throw NegativeJacobian(el.GetID(), -1, J, &el);\n    \n    return J;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate a vector function over the shell\nvec3d FESSIShellDomain::evaluate(FEShellElement& el, vec3d* vn, vec3d* dvn, int n)\n{\n\tvec3d gcnt[3];\n//\tContraBaseVectors(el, n, gcnt);\n\tgcnt[0] = el.m_Gt[0][n];\n\tgcnt[1] = el.m_Gt[1][n];\n\tgcnt[2] = el.m_Gt[2][n];\n\n\tdouble eta = el.gt(n);\n\tconst double* M = el.H(n);\n\tvec3d v(0, 0, 0);\n\tint neln = el.Nodes();\n\tfor (int i=0; i<neln; ++i)\n    {\n        double Mu = (1+eta)/2*M[i];\n        double Md = (1-eta)/2*M[i];\n        v += vn[i]*Mu + dvn[i]*Md;\n    }\n    \n    return v;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the current frame at\n//! integration point n. The inverse jacobian is return in Ji. The return value\n//! is the determinant of the jacobian (not the inverse!)\ndouble FESSIShellDomain::invjact(FEShellElement& el, double Ji[3][3], int n)\n{\n    vec3d gcov[3];\n//    CoBaseVectors(el, n, gcov);\n\tgcov[0] = el.m_gt[0][n];\n\tgcov[1] = el.m_gt[1][n];\n\tgcov[2] = el.m_gt[2][n];\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    \n    double det = J.det();\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n    \n    // calculate the inverse of the jacobian\n    double deti = 1.0 / det;\n    \n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate a scalar function over the shell\ndouble FESSIShellDomain::evaluate(FEShellElement& el, double* pn, double* dpn, int n)\n{\n\tvec3d gcnt[3];\n//    ContraBaseVectors(el, n, gcnt);\n\tgcnt[0] = el.m_Gt[0][n];\n\tgcnt[1] = el.m_Gt[1][n];\n\tgcnt[2] = el.m_Gt[2][n];\n\n\tdouble eta = el.gt(n);\n\tconst double* M = el.H(n);\n\tdouble p = 0;\n\tint neln = el.Nodes();\n    for (int i=0; i<neln; ++i)\n    {\n        double Mu = (1+eta)/2*M[i];\n        double Md = (1-eta)/2*M[i];\n        p += Mu*pn[i] + Md*dpn[i];\n    }\n    \n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the gradient of a scalar function over the shell\nvec3d FESSIShellDomain::gradient(FEShellElement& el, double* pn, double* dpn, int n)\n{\n\tvec3d gcnt[3];\n//    ContraBaseVectors(el, n, gcnt);\n\tgcnt[0] = el.m_Gt[0][n];\n\tgcnt[1] = el.m_Gt[1][n];\n\tgcnt[2] = el.m_Gt[2][n];\n\n\tdouble eta = el.gt(n);\n\n\tconst double* Mr = el.Hr(n);\n\tconst double* Ms = el.Hs(n);\n\tconst double* M = el.H(n);\n\n\tvec3d gradp(0, 0, 0);\n\tint neln = el.Nodes();\n    for (int i=0; i<neln; ++i)\n    {\n        vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n\t\tvec3d gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n\t\tvec3d gradMd = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n        gradp += gradMu*pn[i] + gradMd*dpn[i];\n    }\n    \n    return gradp;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate a scalar function over the shell\ndouble FESSIShellDomain::evaluate(FEShellElement& el, vector<double> pn, vector<double> dpn, int n)\n{\n\tvec3d gcnt[3];\n//    ContraBaseVectors(el, n, gcnt);\n\tgcnt[0] = el.m_Gt[0][n];\n\tgcnt[1] = el.m_Gt[1][n];\n\tgcnt[2] = el.m_Gt[2][n];\n\n\tdouble eta = el.gt(n);\n\tconst double* M = el.H(n);\n\n\tdouble p = 0;\n\tint neln = el.Nodes();\n    for (int i=0; i<neln; ++i)\n    {\n        double Mu = (1+eta)/2*M[i];\n        double Md = (1-eta)/2*M[i];\n        p += Mu*pn[i] + Md*dpn[i];\n    }\n    \n    return p;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the gradient of a scalar function over the shell\nvec3d FESSIShellDomain::gradient(FEShellElement& el, vector<double> pn, vector<double> dpn, int n)\n{\n\tvec3d gcnt[3];\n//    ContraBaseVectors(el, n, gcnt);\n\tgcnt[0] = el.m_Gt[0][n];\n\tgcnt[1] = el.m_Gt[1][n];\n\tgcnt[2] = el.m_Gt[2][n];\n\n\tdouble eta = el.gt(n);\n\tconst double* Mr = el.Hr(n);\n\tconst double* Ms = el.Hs(n);\n\tconst double* M = el.H(n);\n\n    int neln = el.Nodes();\n\tvec3d gradp(0, 0, 0);\n\tfor (int i=0; i<neln; ++i)\n    {\n        vec3d gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n        vec3d gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n        vec3d gradMd = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n        gradp += gradMu*pn[i] + gradMd*dpn[i];\n    }\n    \n    return gradp;\n}\n\nvoid FESSIShellDomain::Update(const FETimeInfo& tp)\n{\n\tint NS = Elements();\n\tFEMesh& mesh = *GetMesh();\n\tint NE = Elements();\n#pragma omp parallel for\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEShellElement& e = Element(i);\n\t\tif (e.isActive())\n\t\t{\n\t\t\tint n = e.Nodes();\n\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t{\n\t\t\t\tFENode& nj = mesh.Node(e.m_node[j]);\n\t\t\t\tvec3d D = nj.m_dt;\n\t\t\t\tdouble h = D.norm();\n\n\t\t\t\te.m_ht[j] = h;\n\t\t\t}\n\n\t\t\tvec3d g[3];\n\t\t\tint ni = e.GaussPoints();\n\t\t\tfor (int j = 0; j < ni; ++j)\n\t\t\t{\n\t\t\t\tCoBaseVectors(e, j, g);\n\t\t\t\te.m_gt[0][j] = g[0];\n\t\t\t\te.m_gt[1][j] = g[1];\n\t\t\t\te.m_gt[2][j] = g[2];\n\n\t\t\t\tCoBaseVectorsP(e, j, g);\n\t\t\t\te.m_gp[0][j] = g[0];\n\t\t\t\te.m_gp[1][j] = g[1];\n\t\t\t\te.m_gp[2][j] = g[2];\n\n\t\t\t\t// NOTE: calculate covariant vectors first since contravariant depends on them\n\t\t\t\tContraBaseVectors(e, j, g);\n\t\t\t\te.m_Gt[0][j] = g[0];\n\t\t\t\te.m_Gt[1][j] = g[1];\n\t\t\t\te.m_Gt[2][j] = g[2];\n\t\t\t}\n\t\t}\n\t};\n}\n\n//=================================================================================================\ntemplate <class T> void _writeIntegratedElementValueT(FESSIShellDomain& dom, FEDataStream& ar, std::function<T (const FEMaterialPoint& mp)> fnc)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i)\n\t{\n\t\tFEShellElement& el = dom.Element(i);\n\t\tdouble* gw = el.GaussWeights();\n\n\t\t// integrate\n\t\tT ew(0.0);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tT vj = fnc(mp);\n\t\t\tdouble detJ = dom.detJ0(el, j)*gw[j];\n\t\t\tew += vj*detJ;\n\t\t}\n\t\tar << ew;\n\t}\n}\n\nvoid writeIntegratedElementValue(FESSIShellDomain& dom, FEDataStream& ar, std::function<double(const FEMaterialPoint& mp)> fnc) { _writeIntegratedElementValueT<double>(dom, ar, fnc); }\nvoid writeIntegratedElementValue(FESSIShellDomain& dom, FEDataStream& ar, std::function<vec3d (const FEMaterialPoint& mp)> fnc) { _writeIntegratedElementValueT<vec3d >(dom, ar, fnc); }\n"
  },
  {
    "path": "FEBioMech/FESSIShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEShellDomain.h>\n#include <FECore/FEModelParam.h>\n#include <functional>\n#include \"febiomech_api.h\"\n#include <FECore/FEDofList.h>\nclass FEDataStream;\n\n//-----------------------------------------------------------------------------\n// This class extends the FEShellDomain and implements the solid-shell interface (SSI) logic.\n// It is used by the new shell formulation.\nclass FEBIOMECH_API FESSIShellDomain : public FEShellDomainNew\n{\npublic:\n\tFESSIShellDomain(FEModel* pfem);\n\n\t//! initialize domain\n\t//! one-time initialization, called during model initialization\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Update element data prior to solving time step\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\t//! Initialize shell normals\n\tbool InitShells() override;\n\nprotected:\n\t//! Find interfaces between solid element faces and shell elements\n\tvoid FindSSI();\n\npublic:\n\t//! calculates covariant basis vectors at an integration point\n\tvoid CoBaseVectors0(FEShellElement& el, int n, vec3d g[3]);\n\n    //! calculates covariant basis vectors at any point\n    void CoBaseVectors0(FEShellElement& el, double r, double s, double t, vec3d g[3]);\n\n\t//! calculates contravariant basis vectors at an integration point\n\tvoid ContraBaseVectors0(FEShellElement& el, int n, vec3d g[3]);\n\n    //! calculates contravariant basis vectors at any point\n    void ContraBaseVectors0(FEShellElement& el, double r, double s, double t, vec3d g[3]);\n\n\t// inverse jacobian with respect to reference frame at an integration point\n\tdouble invjac0(FEShellElement& el, double J[3][3], int n);\n\n    // inverse jacobian with respect to reference frame at any point\n    double invjac0(FEShellElement& el, double J[3][3], double r, double s, double t);\n    \n\t// jacobian with respect to reference frame\n\tdouble detJ0(FEShellElement& el, int n);\n\n    // jacobian with respect to current frame at any point\n    double detJ0(FEShellElement& el, double r, double s, double t);\n    \npublic:\n    //! calculates covariant basis vectors at an integration point\n    void CoBaseVectors(FEShellElement& el, int n, vec3d g[3]);\n    \n    //! calculates covariant basis vectors at any point\n    void CoBaseVectors(FEShellElement& el, double r, double s, double t, vec3d g[3]);\n    \n    //! calculates covariant basis vectors at any point\n    void CoBaseVectors(FEShellElement& el, double r, double s, double t, vec3d g[3], const double alpha);\n    \n    //! calculates covariant basis vectors at an integration point at previous time\n    void CoBaseVectorsP(FEShellElement& el, int n, vec3d g[3]);\n    \n    //! calculates covariant basis vectors at an integration point at intermediate time\n    void CoBaseVectors(FEShellElement& el, int n, vec3d g[3], const double alpha);\n    \n    //! calculates contravariant basis vectors at an integration point\n    void ContraBaseVectors(FEShellElement& el, int n, vec3d g[3]);\n    \n    //! calculates contravariant basis vectors at an integration point at intermediate time\n    void ContraBaseVectors(FEShellElement& el, int n, vec3d g[3], const double alpha);\n    \n    //! calculates contravariant basis vectors at any point\n    void ContraBaseVectors(FEShellElement& el, double r, double s, double t, vec3d g[3]);\n    \n    //! calculates contravariant basis vectors at any point\n    void ContraBaseVectors(FEShellElement& el, double r, double s, double t, vec3d g[3], const double alpha);\n    \n    // jacobian with respect to current frame at an integration point\n    double detJ(FEShellElement& el, int n);\n    \n    // jacobian with respect to current frame at an integration point at intermediate time\n    double detJ(FEShellElement& el, int n, const double alpha);\n    \n    // jacobian with respect to current frame at any point\n    double detJ(FEShellElement& el, double r, double s, double t);\n    \n    // calculate deformation gradient at an integration point\n    double defgrad(FEShellElement& el, mat3d& F, int n);\n    \n    // calculate deformation gradient at any point\n    double defgrad(FEShellElement& el, mat3d& F, double r, double s, double t);\n    \n    // calculate deformation gradient at an integration point at previous time\n    double defgradp(FEShellElement& el, mat3d& F, int n);\n    \n    // inverse jacobian with respect to current frame\n    double invjact(FEShellElement& el, double J[3][3], int n);\n    \n    //! evaluate a vector function over the shell\n    vec3d evaluate(FEShellElement& el, vec3d* vn, vec3d* dvn, int n);\n    \n    //! evaluate a scalar function over the shell\n    double evaluate(FEShellElement& el, double* pn, double* dpn, int n);\n    \n    //! calculate the gradient of a scalar function over the shell\n    vec3d gradient(FEShellElement& el, double* pn, double* dpn, int n);\n    \n    //! evaluate a scalar function over the shell\n    double evaluate(FEShellElement& el, vector<double> pn, vector<double> dpn, int n);\n    \n    //! calculate the gradient of a scalar function over the shell\n    vec3d gradient(FEShellElement& el, vector<double> pn, vector<double> dpn, int n);\n    \n\t//! Functions for element-DOF updates\n\tvirtual void UpdateEAS(vector<double>& ui) {}\n\tvirtual void UpdateIncrementsEAS(vector<double>& ui, const bool binc) {}\n\n\tvoid Update(const FETimeInfo& tp) override;\n\nprotected:\n\tFEDofList\tm_dofU;\t\t// displacement dofs\n\tFEDofList\tm_dofSU;\t// shell displacement dofs\n\tFEDofList\tm_dofR;\t\t// rigid rotation\n    \npublic:\n    bool        m_bnodalnormals; // flag for nodal (true) or element (false) normals\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\nvoid writeIntegratedElementValue(FESSIShellDomain& dom, FEDataStream& ar, std::function<double (const FEMaterialPoint& mp)> fnc);\nvoid writeIntegratedElementValue(FESSIShellDomain& dom, FEDataStream& ar, std::function<vec3d  (const FEMaterialPoint& mp)> fnc);\n"
  },
  {
    "path": "FEBioMech/FEScaledElasticMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FEScaledElasticMaterial.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEScaledElasticMaterial, FEElasticMaterial)\n\tADD_PROPERTY(m_pBase, \"solid\");\n    ADD_PARAMETER(m_scale, FE_RANGE_CLOSED(0,1), \"scale\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! stress function\nmat3ds FEScaledElasticMaterial::Stress(FEMaterialPoint& pt)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double scale = m_scale(pt);\n    return m_pBase->Stress(pt)*scale;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent function\ntens4ds FEScaledElasticMaterial::Tangent(FEMaterialPoint& pt)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double scale = m_scale(pt);\n    return m_pBase->Tangent(pt)*scale;\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function\ndouble FEScaledElasticMaterial::StrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double scale = m_scale(pt);\n    return m_pBase->StrainEnergyDensity(pt)*scale;\n}\n\nFEMaterialPointData* FEScaledElasticMaterial::CreateMaterialPointData()\n{\n    return m_pBase->CreateMaterialPointData();\n}\n\n"
  },
  {
    "path": "FEBioMech/FEScaledElasticMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a scaled elastic material with user-specified scale\n//\nclass FEScaledElasticMaterial : public FEElasticMaterial\n{\npublic:\n    //! default constructor\n    FEScaledElasticMaterial(FEModel* pfem) : m_pBase(nullptr), FEElasticMaterial(pfem) {}\n    \npublic:\n    //! stress function\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent function\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! strain energy density function\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n\n    FEMaterialPointData* CreateMaterialPointData() override;\n\n    DECLARE_FECORE_CLASS();\n    \nprivate:\n    FEElasticMaterial*  m_pBase;    //!< pointer to elastic solid material to scale\n    FEParamDouble       m_scale;    //!< scale factor\n};\n"
  },
  {
    "path": "FEBioMech/FEScaledUncoupledMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FEScaledUncoupledMaterial.h\"\n\nBEGIN_FECORE_CLASS(FEScaledUncoupledMaterial, FEUncoupledMaterial)\n\tADD_PROPERTY(m_pBase, \"solid\");\n    ADD_PARAMETER(m_scale, FE_RANGE_CLOSED(0,1), \"scale\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! stress function\nmat3ds FEScaledUncoupledMaterial::DevStress(FEMaterialPoint& pt)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double scale = m_scale(pt);\n    return m_pBase->DevStress(pt)*scale;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent function\ntens4ds FEScaledUncoupledMaterial::DevTangent(FEMaterialPoint& pt)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double scale = m_scale(pt);\n    return m_pBase->DevTangent(pt)*scale;\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function\ndouble FEScaledUncoupledMaterial::DevStrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& mp = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double scale = m_scale(pt);\n    return m_pBase->DevStrainEnergyDensity(pt)*scale;\n}\n\nFEMaterialPointData* FEScaledUncoupledMaterial::CreateMaterialPointData()\n{\n    return m_pBase->CreateMaterialPointData();\n}"
  },
  {
    "path": "FEBioMech/FEScaledUncoupledMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a scaled elastic material with user-specified scale\n//\nclass FEScaledUncoupledMaterial : public FEUncoupledMaterial\n{\npublic:\n    //! default constructor\n    FEScaledUncoupledMaterial(FEModel* pfem) : m_pBase(nullptr), FEUncoupledMaterial(pfem) {}\n    \npublic:\n    //! stress function\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! tangent function\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! strain energy density function\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n\n    FEMaterialPointData* CreateMaterialPointData() override;\n\n    DECLARE_FECORE_CLASS();\n    \nprivate:\n    FEUncoupledMaterial*    m_pBase;    //!< pointer to elastic solid material to scale\n    FEParamDouble           m_scale;    //!< scale factor\n};\n"
  },
  {
    "path": "FEBioMech/FEScriptedBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEScriptedBodyForce.h\"\n\nFEScriptedBodyForce::FEScriptedBodyForce(FEModel* pfem) : FEScripted<FEBodyForce>(pfem)\n{\n\tScriptContext sc;\n\tsc.returnType = FEValueType::Vec3d;\n\tsc.addVariable(\"pos0\", FEValueType::Vec3d, false);\n\tsc.addVariable(\"pos\" , FEValueType::Vec3d, true);\n\tsc.addVariable(\"time\", FEValueType::Double, false);\n\tSetScriptContext(sc);\n}\n\nvec3d FEScriptedBodyForce::force(FEMaterialPoint& mp)\n{\n\tstd::vector<FEValue> var(3);\n\tvar[0] = mp.m_r0;\n\tvar[1] = mp.m_rt;\n\tvar[2] = GetTimeInfo().currentTime;\n\treturn Value(mp, var).toVec3d();\n}\n\nmat3d FEScriptedBodyForce::stiffness(FEMaterialPoint& pt)\n{\n\tif (HasDerivative(1))\n\t{\n\t\tstd::vector<FEValue> var(3);\n\t\tvar[0] = pt.m_r0;\n\t\tvar[1] = pt.m_rt;\n\t\tvar[2] = GetTimeInfo().currentTime;\n\t\treturn -DerivValue(pt, var, 1).toMat3d();\n\t}\n\n\treturn mat3d(0.0);\n}\n"
  },
  {
    "path": "FEBioMech/FEScriptedBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include \"FEBodyForce.h\"\n#include <FECore/FEScriptedBehavior.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for body forces\n//! Derived classes need to implement the force and stiffness functions.\n//\nclass FEBIOMECH_API FEScriptedBodyForce : public FEScripted<FEBodyForce>\n{\npublic:\n\t//! constructor\n\tFEScriptedBodyForce(FEModel* pfem);\n\npublic:\n\t//! calculate the body force at a material point\n\tvec3d force(FEMaterialPoint& pt) override;\n\n\t//! calculate constribution to stiffness matrix\n\tmat3d stiffness(FEMaterialPoint& pt) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEScriptedDisplacementBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEScriptedDisplacementBC.h\"\n#include <FECore/FENode.h>\n#include <FECore/FEMaterialPoint.h>\n#include \"FEBioMech.h\"\n\nFEScriptedDisplacementBC::FEScriptedDisplacementBC(FEModel* fem) : FEScripted<FEPrescribedNodeSet>(fem)\n{\n\tSetDefaultContext(FEValueType::Vec3d, this);\n}\n\nbool FEScriptedDisplacementBC::Init()\n{\n\tFEDofList dofs(GetFEModel());\n\tdofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\tSetDOFList(dofs);\n\n\treturn FEPrescribedNodeSet::Init();\n}\n\n// return the value for node i\nvoid FEScriptedDisplacementBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n\tFENodeSet* nodeSet = GetNodeSet();\n\tFENode& node = *nodeSet->Node(nodelid);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = nodelid;\n\n\tvec3d v = Value(mp).toVec3d();\n\tval[0] = v.x;\n\tval[1] = v.y;\n\tval[2] = v.z;\n}\n\nvoid FEScriptedDisplacementBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n\n}\n"
  },
  {
    "path": "FEBioMech/FEScriptedDisplacementBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/FEScriptedBehavior.h>\n\nclass FEScriptedDisplacementBC : public FEScripted<FEPrescribedNodeSet>\n{\npublic:\n\tFEScriptedDisplacementBC(FEModel* fem);\n\n\tbool Init() override;\n\n\t// return the value for node i, dof j\n\tvoid GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEScriptedPressureLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBioMech.h\"\n#include \"FEScriptedPressureLoad.h\"\n\nFEScriptedPressureLoad::FEScriptedPressureLoad(FEModel* pfem) : FEScripted<FESurfaceLoad>(pfem)\n{\n\tScriptContext sc;\n\tsc.returnType = FEValueType::Double;\n\tsc.addVariable(\"pos\"   , FEValueType::Vec3d, true);\n\tsc.addVariable(\"normal\", FEValueType::Vec3d, true);\n\tsc.addVariable(\"time\"  , FEValueType::Double, false);\n\tSetScriptContext(sc);\n}\n\nbool FEScriptedPressureLoad::Init()\n{\n\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\treturn FESurfaceLoad::Init();\n}\n\nvoid FEScriptedPressureLoad::LoadVector(FEGlobalVector& R)\n{\n\tdouble t = GetTimeInfo().currentTime;\n\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadVector(R, m_dof, false, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\n\t\tvec3d normal = (pt.dxr ^ pt.dxs).normalized();\n\n\t\tstd::vector<FEValue> vars(3);\n\t\tvars[0] = pt.m_rt;\n\t\tvars[1] = normal;\n\t\tvars[2] = t;\n\n\t\t// evaluate pressure at this material point\n\t\tdouble P = -Value(pt, vars).toDouble();\n\n\t\t// force vector\n\t\tvec3d N = (pt.dxr ^ pt.dxs);\n\t\tvec3d t = N * P;\n\n\t\tdouble H_u = dof_a.shape;\n\n\t\tval[0] = H_u * t.x;\n\t\tval[1] = H_u * t.y;\n\t\tval[2] = H_u * t.z;\n\t});\n}\n\nvoid FEScriptedPressureLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\tdouble t = GetTimeInfo().currentTime;\n\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\tvec3d normal = (mp.dxr ^ mp.dxs).normalized();\n\n\t\tstd::vector<FEValue> vars(3);\n\t\tvars[0] = mp.m_rt;\n\t\tvars[1] = normal;\n\t\tvars[2] = t;\n\n\t\tdouble H_i = dof_a.shape;\n\t\tdouble H_j = dof_b.shape;\n\n\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\tmat3da Gr(mp.dxr);\n\t\tmat3da Gs(mp.dxs);\n\t\tmat3d Grs_j = Gr * Gs_j - Gs * Gr_j;\n\n\t\t// evaluate pressure at this material point\n\t\tdouble P = Value(mp, vars).toDouble();\n\n\t\tmat3d K = Grs_j * (P * H_i);\n\t\tKab.set(0, 0, K);\n\n\t\tif (HasDerivative(0))\n\t\t{\n\t\t\tvec3d dPx = DerivValue(mp, vars, 0).toVec3d();\n\t\t\tvec3d N = (mp.dxr ^ mp.dxs);\n\t\t\tK = (N & dPx) * (H_i * H_j);\n\t\t\tKab.add(0, 0, K);\n\t\t}\n\n\t\tif (HasDerivative(1))\n\t\t{\n\t\t\tmat3dd I(1.0);\n\t\t\tmat3d nxn = (normal & normal);\n\n\t\t\tvec3d dPn = DerivValue(mp, vars, 1).toVec3d();\n\t\t\tK = Grs_j * ((normal & dPn)* (I - nxn) * H_i);\n\t\t\tKab.add(0, 0, K);\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "FEBioMech/FEScriptedPressureLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEScriptedBehavior.h>\n\nclass FEScriptedPressureLoad : public FEScripted<FESurfaceLoad>\n{\npublic:\n\tFEScriptedPressureLoad(FEModel* pfem);\n\t~FEScriptedPressureLoad() override = default;\n\tbool Init() override;\n\n\tMatrix_Type PreferredMatrixType() override { return Matrix_Type::REAL_UNSYMMETRIC; }\n\npublic:\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEScriptedTractionLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBioMech.h\"\n#include \"FEScriptedTractionLoad.h\"\n\nFEScriptedTractionLoad::FEScriptedTractionLoad(FEModel* pfem) : FEScripted<FESurfaceLoad>(pfem)\n{\n\tScriptContext sc;\n\tsc.returnType = FEValueType::Vec3d;\n\tsc.addVariable(\"pos\"   , FEValueType::Vec3d , true);\n\tsc.addVariable(\"normal\", FEValueType::Vec3d , true);\n\tsc.addVariable(\"time\"  , FEValueType::Double, false);\n\tSetScriptContext(sc);\n}\n\nbool FEScriptedTractionLoad::Init()\n{\n\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\treturn FESurfaceLoad::Init();\n}\n\nvoid FEScriptedTractionLoad::LoadVector(FEGlobalVector& R)\n{\n\tdouble t = GetTimeInfo().currentTime;\n\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadVector(R, m_dof, false, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\n\t\tvec3d N = (pt.dxr ^ pt.dxs);\n\t\tdouble J = N.unit();\n\n\t\tstd::vector<FEValue> vars(3);\n\t\tvars[0] = pt.m_rt;\n\t\tvars[1] = N;\n\t\tvars[2] = t;\n\n\t\t// evaluate traction at this material point\n\t\tvec3d t = Value(pt, vars).toVec3d();\n\n\t\tdouble H_u = dof_a.shape;\n\n\t\tval[0] = H_u * t.x * J;\n\t\tval[1] = H_u * t.y * J;\n\t\tval[2] = H_u * t.z * J;\n\t});\n}\n\nvoid FEScriptedTractionLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\tdouble t = GetTimeInfo().currentTime;\n\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\tvec3d N = (mp.dxr ^ mp.dxs);\n\t\tdouble J = N.unit();\n\n\t\tstd::vector<FEValue> vars(3);\n\t\tvars[0] = mp.m_rt;\n\t\tvars[1] = N;\n\t\tvars[2] = t;\n\n\t\tdouble H_i = dof_a.shape;\n\t\tdouble H_j = dof_b.shape;\n\n\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\tmat3da Gr(mp.dxr);\n\t\tmat3da Gs(mp.dxs);\n\t\tmat3d Grs_j = Gr * Gs_j - Gs * Gr_j;\n\n\t\t// evaluate traction at this material point\n\t\tvec3d t = -Value(mp, vars).toVec3d();\n\t\tmat3d K = (t & N)*Grs_j*H_i;\n\t\tKab.set(0, 0, K);\n\n\t\t// evaluate traction gradient w.r.t. position\n\t\tif (HasDerivative(0))\n\t\t{\n\t\t\tmat3d dtdx = -DerivValue(mp, vars, 0).toMat3d();\n\t\t\tK = dtdx * (H_i * H_j * J);\n\t\t\tKab.add(0, 0, K);\n\t\t}\n\n\t\t// evaluate traction gradient w.r.t. normal\n\t\tif (HasDerivative(1))\n\t\t{\n\t\t\tmat3d dtdn = -DerivValue(mp, vars, 1).toMat3d();\n\t\t\tmat3dd I(1.0);\n\t\t\tK = dtdn * (I - (N & N)) * Grs_j * H_i;\n\t\t\tKab.add(0, 0, K);\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "FEBioMech/FEScriptedTractionLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEScriptedBehavior.h>\n\nclass FEScriptedTractionLoad : public FEScripted<FESurfaceLoad>\n{\npublic:\n\tFEScriptedTractionLoad(FEModel* pfem);\n\t~FEScriptedTractionLoad() override = default;\n\tbool Init() override;\n\n\tMatrix_Type PreferredMatrixType() override { return Matrix_Type::REAL_UNSYMMETRIC; }\n\npublic:\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEShenoyMaterial.cpp",
    "content": "// ShenoyMaterial.cpp : Defines the exported functions for the DLL application.\n//\n\n#include \"stdafx.h\"\n#include \"FEShenoyMaterial.h\"\n\nBEGIN_FECORE_CLASS(FEShenoyMaterial, FEElasticMaterial)\n\tADD_PARAMETER(m_mu, FE_RANGE_GREATER_OR_EQUAL(0.0), \"mu\");\n\tADD_PARAMETER(m_k , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\");\n\tADD_PARAMETER(m_Ef, FE_RANGE_GREATER_OR_EQUAL(0.0), \"Ef\");\n\tADD_PARAMETER(m_lamc, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lam_c\");\n\tADD_PARAMETER(m_lamt, FE_RANGE_NOT_EQUAL(0.0), \"lam_t\");\n\tADD_PARAMETER(m_n   , FE_RANGE_GREATER_OR_EQUAL(1.0), \"n\");\n\tADD_PARAMETER(m_m   , FE_RANGE_GREATER_OR_EQUAL(1.0), \"m\");\nEND_FECORE_CLASS();\n\nFEShenoyMaterial::FEShenoyMaterial(FEModel* fem) : FEElasticMaterial(fem)\n{\n\tm_mu = 0.0;\n\tm_k  = 0.0;\n\tm_Ef = 0.0;\n\tm_lamc = 1.0;\n\tm_lamt = 0.0;\n\tm_n = 5;\n\tm_m = 1;\n}\n\ndouble FEShenoyMaterial::fiberStress(double lam)\n{\n\tdouble lam1 = m_lamc - m_lamt * 0.5;\n\tdouble lam2 = m_lamc + m_lamt * 0.5;\n\n\tif (lam < lam1) return 0.0;\n\telse if (lam > lam2)\n\t{\n\t\treturn m_Ef*((lam2 - lam1) / (m_n + 1.0) + (pow(1.0 + lam - lam2, m_m + 1.0) - 1.0) / (m_m + 1.0));\n\t}\n\telse\n\t{\n\t\treturn m_Ef*(pow((lam - lam1)/(lam2 - lam1), m_n)*(lam - lam1)) / (m_n + 1);\n\t}\n}\n\ndouble FEShenoyMaterial::fiberTangent(double lam)\n{\n\tdouble lam1 = m_lamc - m_lamt * 0.5;\n\tdouble lam2 = m_lamc + m_lamt * 0.5;\n\n\tif (lam < lam1) return 0.0;\n\telse if (lam > lam2)\n\t{\n\t\treturn m_Ef*pow(1.0 + lam - lam2, m_m);\n\t}\n\telse\n\t{\n\t\treturn m_Ef*pow((lam - lam1) / (lam2 - lam1), m_n);\n\t}\n}\n\nmat3ds FEShenoyMaterial::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tdouble J = pt.m_J;\n\tdouble Jm23 = pow(J, -2.0 / 3.0);\n\n\tmat3ds Bbar = B*Jm23;\n\tmat3dd I(1.0);\n\n\tdouble lam[3] = {0};\n\tvec3d n[3];\n\tB.eigen(lam, n);\n\tlam[0] = (lam[0] >= 0.0 ? sqrt(lam[0]) : 0.0);\n\tlam[1] = (lam[1] >= 0.0 ? sqrt(lam[1]) : 0.0);\n\tlam[2] = (lam[2] >= 0.0 ? sqrt(lam[2]) : 0.0);\n\n\t// isotropic component of the stress\n\tmat3ds s_b = Bbar.dev()*(m_mu / J) + I*(m_k * (J - 1.0));\n\n\t// anisotropic (fiber) contribution of the stress\n\tdouble df[3] = {0};\n\tdf[0] = fiberStress(lam[0]);\n\tdf[1] = fiberStress(lam[1]);\n\tdf[2] = fiberStress(lam[2]);\n\n\tmat3ds s_f = (dyad(n[0])*(df[0] * lam[0]) + dyad(n[1])*(df[1] * lam[1]) + dyad(n[2])*(df[2] * lam[2])) / J;\n\n\t// add them together\n\tmat3ds s = s_b + s_f;\n\n\t// and done\n\treturn s;\n}\n\ntens4ds FEShenoyMaterial::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tmat3ds B = pt.LeftCauchyGreen();\n\tdouble J = pt.m_J;\n\tdouble Jm23 = pow(J, -2.0 / 3.0);\n\n\tmat3ds Bbar = B*Jm23;\n\tmat3dd I(1.0);\n\n\tdouble lam[3] = { 0 };\n\tvec3d n[3];\n\tB.eigen(lam, n);\n\tlam[0] = (lam[0] >= 0.0 ? sqrt(lam[0]) : 0.0);\n\tlam[1] = (lam[1] >= 0.0 ? sqrt(lam[1]) : 0.0);\n\tlam[2] = (lam[2] >= 0.0 ? sqrt(lam[2]) : 0.0);\n\n\tdouble I1bar = Bbar.tr();\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4 = dyad4s(I);\n\n\ttens4ds c_b = (I4*(2.0*I1bar / 3.0) - (dyad1s(Bbar, I))*(2.0 / 3.0) + IxI*(I1bar*2.0 / 9.0))*(m_mu / J) \\\n\t\t\t\t+ (IxI*(2.0*J - 1) - I4*(2.0*(J - 1.0)))*m_k;\n\n\tdouble df[3] = {0}, ddf[3] = {0}, sf[3] = {0};\n\tdf[0] = fiberStress(lam[0]);\n\tdf[1] = fiberStress(lam[1]);\n\tdf[2] = fiberStress(lam[2]);\n\n\tddf[0] = fiberTangent(lam[0]);\n\tddf[1] = fiberTangent(lam[1]);\n\tddf[2] = fiberTangent(lam[2]);\n\n\tsf[0] = lam[0] * df[0] / J;\n\tsf[1] = lam[1] * df[1] / J;\n\tsf[2] = lam[2] * df[2] / J;\n\n\ttens4ds c_f; c_f.zero();\n\tfor (int a=0; a<3; ++a)\n\t{\n\t\tc_f += dyad1s(dyad(n[a]))*(lam[a]*(lam[a]*ddf[a] - df[a])/J);\n\t}\n\t\n\tint p[][2] = {{0,1},{1,2},{0,2}};\n\n\tfor (int i=0; i < 3; ++i)\n\t{\n\t\tint a = p[i][0];\n\t\tint b = p[i][1];\n\n\t\tdouble g = 0.0;\n\t\tif (fabs(lam[a] - lam[b]) <= 1e-10)\n\t\t{\n\t\t\tg = 0.5*lam[a]*lam[a]*ddf[a] / J - sf[a];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tg = (sf[a]*lam[b]*lam[b] - sf[b]*lam[a]*lam[a])/(lam[a]*lam[a] - lam[b]*lam[b]);\n\t\t}\n\n\t\tmat3ds Nab = dyads(n[a], n[b]);\n\t\tc_f += dyad1s(Nab)*(g);\n\t}\n\n\ttens4ds c = c_b + c_f;\n\n\treturn c;\n}\n"
  },
  {
    "path": "FEBioMech/FEShenoyMaterial.h",
    "content": "#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n\n// This plugin implements the constitutive model by \n// Wang et al. (Biophysical Journal, 107, 2014, pp:2592 - 2603).\n// This novel material proposes a mechanism for long-range force transmission in \n// fibrous matrices enabled by tension-driven alignment of fibers.\nclass FEShenoyMaterial : public FEElasticMaterial\n{\npublic:\n\tFEShenoyMaterial(FEModel* fem);\n\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\nprivate:\n\tdouble fiberStress(double lam);\n\tdouble fiberTangent(double lam);\n\nprivate:\n\tdouble\tm_mu;\n\tdouble\tm_k;\n\tdouble\tm_Ef;\n\tdouble\tm_lamc;\n\tdouble\tm_lamt;\n\tdouble\tm_n;\n\tdouble\tm_m;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESlideLineConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlideLineConstraint.h\"\n#include <FECore/FEShellDomain.h>\n#include \"FECore/FEClosestPointProjection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEAnalysis.h>\n\nFESlideLine::FESlidingPoint::FESlidingPoint()\n{\n\tpme = nullptr;\n\tgap = 0;\n\tnu = vec3d(0, 0, 0);\n\tr = 0;\n\tLm = 0.0;\n\tLn = 0.0;\n}\n\nvoid FESlideLine::FESlidingPoint::Serialize(DumpStream& ar)\n{\n\tar & gap & nu;\n\tar & r;\n\tar & Lm & Ln;\n}\n\nvoid FESlideLine::FESlidingPoint::Init()\n{\n\tpme = nullptr;\n\tgap = 0;\n\tnu = vec3d(0, 0, 0);\n\tr = 0;\n\tLm = Ln = 0.0;\n}\n\nFESlideLine::FESlideLine(FEModel* pfem) : FEEdge(pfem) {}\n\nbool FESlideLine::Init()\n{\n\t// always intialize base class first!\n\tif (FEEdge::Init() == false) return false;\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate integration point data\n\tm_data.resize(nn);\n\tfor (int i = 0; i < nn; ++i)\n\t{\n\t\tFESlidingPoint& d = m_data[i];\n\t\td.Init();\n\t}\n\n\treturn true;\n}\n\nFEMaterialPoint* FESlideLine::CreateMaterialPoint()\n{\n\treturn new FELineMaterialPoint;\n}\n\nFESlideLine::Projection FESlideLine::ClosestProjection(const vec3d& x)\n{\n\tFESlideLine::Projection P;\n\tint NE = Elements();\n\tdouble L2min = 0;\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\t\tvec3d a = Node(el.m_lnode[0]).m_rt;\n\t\tvec3d b = Node(el.m_lnode[1]).m_rt;\n\t\tvec3d e = (b - a);\n\n\t\tdouble D2 = e.norm2();\n\t\tif (D2 != 0)\n\t\t{\n\t\t\tdouble l = ((x - a) * e) / D2;\n\t\t\tif (l < 0) l = 0;\n\t\t\telse if (l > 1) l = 1;\n\n\t\t\tvec3d q = a + e * l;\n\t\t\tdouble L2 = (x - q).norm2();\n\t\t\tif ((P.pe == nullptr) || (L2 < L2min))\n\t\t\t{\n\t\t\t\tP.pe = &el;\n\t\t\t\tP.r = 2.0 * l - 1;\n\t\t\t\tP.q = q;\n\t\t\t\tL2min = L2;\n\t\t\t}\n\t\t}\n\t}\n\tassert(P.pe);\n\treturn P;\n}\n\nvoid FESlideLine::Serialize(DumpStream& ar)\n{\n\t// serialize base class\n\tFEEdge::Serialize(ar);\n\n\t// serialize data\n//\tar & m_data;\n}\n\nBEGIN_FECORE_CLASS(FESlideLineConstraint, FENLConstraint)\n\tADD_PARAMETER(m_laugon       , \"laugon\"       )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol         , \"tolerance\"    );\n\tADD_PARAMETER(m_eps          , \"penalty\"      );\n\tADD_PARAMETER(m_gtol         , \"gaptol\"       );\n\tADD_PARAMETER(m_naugmin      , \"minaug\"       );\n\tADD_PARAMETER(m_naugmax      , \"maxaug\"       );\n\tADD_PARAMETER(m_nsegup       , \"seg_up\"       );\n\n\tADD_PROPERTY(pl, \"primary\")->AddFlag(FEProperty::Reference);\n\tADD_PROPERTY(sl, \"secondary\")->AddFlag(FEProperty::Reference);\n\nEND_FECORE_CLASS();\n\n//! build the matrix profile for use in the stiffness matrix\nvoid FESlideLineConstraint::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tconst int LMSIZE = 6 * (FEElement::MAX_NODES + 1);\n\tvector<int> lm(LMSIZE);\n\n\tfor (int j = 0; j < pl.Nodes(); ++j)\n\t{\n\t\tFENode& nj = pl.Node(j);\n\t\tFELineElement* pe = pl.m_data[j].pme;\n\t\tif (pe != 0)\n\t\t{\n\t\t\tFELineElement& me = *pe;\n\t\t\tint* en = &me.m_lnode[0];\n\n\t\t\tint n = me.Nodes();\n\t\t\tlm.assign(LMSIZE, -1);\n\n\t\t\tlm[0] = nj.m_ID[dof_X];\n\t\t\tlm[1] = nj.m_ID[dof_Y];\n\t\t\tlm[2] = nj.m_ID[dof_Z];\n\t\t\tlm[3] = nj.m_ID[dof_RU];\n\t\t\tlm[4] = nj.m_ID[dof_RV];\n\t\t\tlm[5] = nj.m_ID[dof_RW];\n\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\t\tlm[6 * (k + 1)    ] = id[dof_X];\n\t\t\t\tlm[6 * (k + 1) + 1] = id[dof_Y];\n\t\t\t\tlm[6 * (k + 1) + 2] = id[dof_Z];\n\t\t\t\tlm[6 * (k + 1) + 3] = id[dof_RU];\n\t\t\t\tlm[6 * (k + 1) + 4] = id[dof_RV];\n\t\t\t\tlm[6 * (k + 1) + 5] = id[dof_RW];\n\t\t\t}\n\n\t\t\tK.build_add(lm);\n\t\t}\n\t}\n}\n\nFESlideLineConstraint::FESlideLineConstraint(FEModel* pfem) : FENLConstraint(pfem), pl(pfem), sl(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_laugon = 0; // penalty method by default\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n\tm_gtol = 0;\n\n\tm_nsegup = 0;\t// always do segment updates\n};\n\nbool FESlideLineConstraint::Init()\n{\n\t// set data\n\tm_bfirst = true;\n\tm_normg0 = 0.0;\n\n\t// create the surfaces\n\tif (pl.Init() == false) return false;\n\tif (sl.Init() == false) return false;\n\n\treturn true;\n}\n\nvoid FESlideLineConstraint::Activate()\n{\n\t// don't forget to call the base class\n\tFENLConstraint::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectPoints(true);\n}\n\nvoid FESlideLineConstraint::ProjectPoints(bool bupseg)\n{\n\t// loop over all primary nodes\n\tfor (int i=0; i<pl.Nodes(); ++i)\n\t{\n\t\tFENode& node = pl.Node(i);\n\t\tvec3d x = node.m_rt;\n\n\t\tFESlideLine::FESlidingPoint& pti = pl.m_data[i];\n\n\t\tFESlideLine::Projection P = sl.ClosestProjection(x);\n\t\tif (P.pe)\n\t\t{\n\t\t\tFELineElement* pme = pti.pme;\n\n\t\t\tif ((P.pe == pme) || bupseg)\n\t\t\t{\n\t\t\t\tpti.pme = P.pe;\n\t\t\t\tpti.r = P.r;\n\t\t\t\t// calculate gap\n\t\t\t\tvec3d e = P.q - x;\n\t\t\t\tpti.gap = e.unit();\n\t\t\t\tpti.nu = e;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// point has left contact\n\t\t\tpti.pme = nullptr;\n\t\t\tpti.gap = 0;\n\t\t\tpti.Ln = pti.Lm = 0;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! updates sliding interface data\n//! niter is the number of Newton iterations.\nvoid FESlideLineConstraint::Update()\n{\n\tint niter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n\n\t// should we do a segment update or not?\n\t// TODO: check what happens when m_nsegup == -1 and m_npass = 2;\n\t// We have to make sure that in this case, both surfaces get at least\n\t// one pass!\n\tbool bupdate = (m_bfirst || (m_nsegup == 0)? true : (niter <= m_nsegup));\n\n\tProjectPoints(bupdate);\n\n\t// Update the net contact pressures\n\tUpdateContactPressures();\n\n\t// set the first-entry-flag to false\n\tm_bfirst = false;\n}\n\nvoid FESlideLineConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\t// the elements LM vectors\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble w[MN];\n\tdouble detJ[MN];\n\n\t// loop over all primary surface facets\n\tint ne = pl.Elements();\n\tfor (int j=0; j<ne; ++j)\n\t{\n\t\t// get the next element\n\t\tFELineElement& sel = pl.Element(j);\n\t\tint nseln = sel.Nodes();\n\n\t\tvec3d a = pl.Node(sel.m_lnode[0]).m_r0;\n\t\tvec3d b = pl.Node(sel.m_lnode[1]).m_r0;\n\t\tdouble L = (b - a).norm();\n\n\t\t// get the element's LM array\n\t\t// TODO: This assumes dofs are indexed at (0,1,2)!\n\t\tsLM.resize(3 * nseln);\n\t\tfor (int a = 0; a < nseln; ++a)\n\t\t{\n\t\t\tFENode& node = pl.Node(sel.m_lnode[a]);\n\t\t\tsLM[3 * a] = node.m_ID[0];\n\t\t\tsLM[3 * a + 1] = node.m_ID[1];\n\t\t\tsLM[3 * a + 2] = node.m_ID[2];\n\t\t}\n\n\t\t// we calculate all the metrics we need before we\n\t\t// calculate the nodal forces\n\t\tfor (int n=0; n<nseln; ++n)\n\t\t{\n\t\t\t// jacobians\n\t\t\tdetJ[n] = L/2; // TODO: This assumes local jacobian is 2!\n\n\t\t\t// integration weights\n\t\t\tw[n] = 1;// sel.GaussWeights()[n];\n\t\t}\n\n\t\t// loop over primary surface element nodes (which are the integration points as well)\n\t\t// and calculate the contact nodal force\n\t\tfor (int n=0; n<nseln; ++n)\n\t\t{\n\t\t\t// get the local node number\n\t\t\tint m = sel.m_lnode[n];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has an element associated with it\n\t\t\t// TODO: is this a good way to test for an active constraint\n\t\t\t// The rigid wall criteria seems to work much better.\n\t\t\tif (pl.m_data[m].pme != 0)\n\t\t\t{\n\t\t\t\t// This node is active and could lead to a non-zero\n\t\t\t\t// contact force.\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFELineElement& mel = *pl.m_data[m].pme;\n\t\t\t\tint nmeln = mel.Nodes();\n\n\t\t\t\tmLM.resize(3 * nmeln);\n\t\t\t\tfor (int b = 0; b < nmeln; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = sl.Node(mel.m_lnode[b]);\n\t\t\t\t\tmLM[3 * b] = node.m_ID[0];\n\t\t\t\t\tmLM[3 * b + 1] = node.m_ID[1];\n\t\t\t\t\tmLM[3 * b + 2] = node.m_ID[2];\n\t\t\t\t}\n\n\t\t\t\t// calculate the nodal force\n\t\t\t\tint ndof = 3 * (nmeln + 1);\n\t\t\t\tfe.resize(ndof);\n\t\t\t\tContactNodalForce(m, mel, fe);\n\n\t\t\t\t// multiply force with weights\n\t\t\t\tfor (int l=0; l<ndof; ++l) fe[l] *= detJ[n]*w[n];\n\t\t\t\t\t\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3*(nmeln+1));\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(l+1)  ] = mLM[l*3  ];\n\t\t\t\t\tlm[3*(l+1)+1] = mLM[l*3+1];\n\t\t\t\t\tlm[3*(l+1)+2] = mLM[l*3+2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (int l=0; l<nmeln; ++l) en[l+1] = mel.m_node[l];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//! Calculates the contact force on a node.\n//! \\param[in] m local node number\n//! \\param[out] fe force vector\nvoid FESlideLineConstraint::ContactNodalForce(int m, FELineElement& mel, vector<double>& fe)\n{\n\t// max nr of element nodes\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\t// get primary node force\n\tdouble gap = pl.m_data[m].gap;\n\tdouble eps = Penalty();\n\tdouble Ln = pl.m_data[m].Lm;\n\tdouble tn = Ln + eps*gap;\n\n\t// get the primary node normal\n\tvec3d& nu = pl.m_data[m].nu;\n\n\tint nmeln = mel.Nodes();\n\tint ndof = 3*(1 + nmeln);\n\n\t// isoparametric coordinates of the projected node\n\t// onto the secondary surface element\n\tdouble r = pl.m_data[m].r;\n\n\t// get the secondary surface element shape function values at this node\n\tdouble H[MAXMN];\n\tmel.shape(H, r);\n\n\t// --- N O R M A L   T R A C T I O N ---\n\n\t// calculate contact vectors for normal traction\n\tdouble N[3 * (MAXMN + 1)];\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\tfor (int l=0; l<nmeln; ++l)\n\t{\n\t\tN[3*(l+1)  ] = -H[l]*nu.x;\n\t\tN[3*(l+1)+1] = -H[l]*nu.y;\n\t\tN[3*(l+1)+2] = -H[l]*nu.z;\n\t}\n\n\t// calculate force vector\n\tfor (int l=0; l<ndof; ++l) fe[l] = tn*N[l];\n}\n\nvoid FESlideLineConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEElementMatrix ke;\n\n\tconst int MAXMN = FEElement::MAX_NODES;\n\tvector<int> lm(3*(MAXMN + 1));\n\tvector<int> en(MAXMN+1);\n\n\tdouble w[MAXMN];\n\tdouble detJ[MAXMN];\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\t// loop over all primary elements\n\tint ne = pl.Elements();\n\tfor (int j=0; j<ne; ++j)\n\t{\n\t\t// unpack the next element\n\t\tFELineElement& se = pl.Element(j);\n\t\tint nseln = se.Nodes();\n\n\t\tvec3d a = pl.Node(se.m_lnode[0]).m_r0;\n\t\tvec3d b = pl.Node(se.m_lnode[1]).m_r0;\n\t\tdouble L = (b - a).norm();\n\n\t\t// get the element's LM array\n\t\t// TODO: This assumes dofs are indexed at (0,1,2)!\n\t\tsLM.resize(3 * nseln);\n\t\tfor (int a = 0; a < nseln; ++a)\n\t\t{\n\t\t\tFENode& node = pl.Node(se.m_lnode[a]);\n\t\t\tsLM[3 * a] = node.m_ID[0];\n\t\t\tsLM[3 * a + 1] = node.m_ID[1];\n\t\t\tsLM[3 * a + 2] = node.m_ID[2];\n\t\t}\n\n\t\t// get all the metrics we need \n\t\tfor (int n=0; n<nseln; ++n)\n\t\t{\n\t\t\tdetJ[n] = L/2;\n\t\t\tw[n] = 1;\n\t\t}\n\n\t\t// loop over all integration points (that is nodes)\n\t\tfor (int n=0; n<nseln; ++n)\n\t\t{\n\t\t\tint m = se.m_lnode[n];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has an element associated with it\n\t\t\tif (pl.m_data[m].pme != 0)\n\t\t\t{\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFELineElement& me = *pl.m_data[m].pme;\n\n\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\tint ndof = 3 * (nmeln + 1);\n\n\t\t\t\t// get the secondary surface element's LM array\n\t\t\t\tmLM.resize(3 * nmeln);\n\t\t\t\tfor (int b = 0; b < nmeln; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = sl.Node(me.m_lnode[b]);\n\t\t\t\t\tmLM[3 * b] = node.m_ID[0];\n\t\t\t\t\tmLM[3 * b + 1] = node.m_ID[1];\n\t\t\t\t\tmLM[3 * b + 2] = node.m_ID[2];\n\t\t\t\t}\n\n\t\t\t\t// calculate the stiffness matrix\n\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\tContactNodalStiffness(m, me, ke);\n\n\t\t\t\t// muliply with weights\n\t\t\t\tfor (int k=0; k<ndof; ++k)\n\t\t\t\t\tfor (int l=0; l<ndof; ++l) ke[k][l] *= detJ[n]*w[n];\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (int k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\t\t\t\t\t\t\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlideLineConstraint::ContactNodalStiffness(int m, FELineElement& mel, matrix& ke)\n{\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\tvector<int> lm(3*(MAXMN+1));\n\tvector<int> en(MAXMN + 1);\n\n\tdouble H[MAXMN], Hr[MAXMN];\n\n\tdouble N[3][3 * (MAXMN + 1)] = { 0 };\n\tdouble AN[3][3 * (MAXMN + 1)] = { 0 };\n\n\t// get the mesh\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\t// nr of element nodes and degrees of freedom \n\tint nmeln = mel.Nodes();\n\tint ndof = 3*(1 + nmeln);\n\n\t// penalty factor\n\tdouble eps = Penalty();\n\n\t// nodal coordinates\n\tvec3d rt[MAXMN];\n\tfor (int j=0; j<nmeln; ++j) rt[j] = mesh.Node(mel.m_node[j]).m_rt;\n\n\t// node natural coordinates in secondary surface element\n\tdouble r = pl.m_data[m].r;\n\n\t// gap\n\tdouble gap = pl.m_data[m].gap;\n\n\t// lagrange multiplier\n\tdouble Lm = pl.m_data[m].Lm;\n\n\t// get node normal force\n\tdouble tn = Lm + eps*gap;\n\n\t// get the node normal\n\tvec3d& nu = pl.m_data[m].nu;\n\n\t// get the secondary surface element shape function values and the derivatives at this node\n\tmel.shape(H, r);\n\tmel.shape_deriv(Hr, r);\n\n\t// set up the N vector\n\tN[0][0] = 1;\n\tN[1][1] = 1;\n\tN[2][2] = 1;\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tN[0][(k+1)*3  ] = -H[k];\n\t\tN[1][(k+1)*3+1] = -H[k];\n\t\tN[2][(k+1)*3+2] = -H[k];\n\t}\n\n\t// get the tangent vector\n\tvec3d tau(0, 0, 0);\n\tfor (int i = 0; i < nmeln; ++i)\n\t{\n\t\ttau += mesh.Node(mel.m_node[i]).m_rt * Hr[i];\n\t}\n\n\t// calculate curvature tensor\n\tdouble K = 0;\n\tdouble Grr[FEElement::MAX_NODES];\n\tmel.shape_deriv2(Grr, r);\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tK += (nu*rt[k])*Grr[k];\n\t}\n\n\t// setup A matrix A = M + gK\n\tdouble A = (tau * tau) - gap * K;\n\n\tmat3d TxT = (tau & tau);\n\n\tmat3d B = mat3dd(1.0) - TxT/A;\n\n\tmat3d M = B.transpose() * B;\n\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j = 0; j < ndof; ++j)\n\t\t{\n\t\t\tAN[i][j] = 0;\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tAN[i][j] += M[i][k] * N[k][j];\n\t\t}\n\n\tfor (int k=0; k<ndof; ++k)\n\t\tfor (int l=0; l<ndof; ++l)\n\t\t\t{\n\t\t\t\tdouble sum = 0;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tsum += N[i][k] * AN[i][l];\n\n\t\t\t\tke[k][l] = eps*sum;\n\t\t\t}\n}\n\nbool FESlideLineConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tdouble Ln;\n\tbool bconv = true;\n\tmat2d Mi;\n\n\t// penalty factor\n\tdouble eps = Penalty();\n\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\t// a. normal component\n\tdouble normL0 = 0;\n\tfor (int i=0; i<pl.Nodes(); ++i) normL0 += pl.m_data[i].Lm*pl.m_data[i].Lm;\n\tnormL0 = sqrt(normL0);\n\n\t// --- c a l c u l a t e   c u r r e n t   n o r m s ---\n\t// a. normal component\n\tdouble normL1 = 0;\t// force norm\n\tdouble normg1 = 0;\t// gap norm\n\tint N = 0;\n\tfor (int i=0; i<pl.Nodes(); ++i)\n\t{\n\t\t// update Lagrange multipliers\n\t\tLn = pl.m_data[i].Lm + eps*pl.m_data[i].gap;\n\t\tnormL1 += Ln*Ln;\n\t\tif (pl.m_data[i].gap > 0)\n\t\t{\n\t\t\tnormg1 += pl.m_data[i].gap*pl.m_data[i].gap;\n\t\t\t++N;\n\t\t}\n\t}\t\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormg1 = sqrt(normg1 / N);\n\n\tif (naug == 0) m_normg0 = 0;\n\n\t// calculate and print convergence norms\n\tdouble lnorm = 0, gnorm = 0;\n\tif (normL1 != 0) lnorm = fabs(normL1 - normL0)/normL1; else lnorm = fabs(normL1 - normL0);\n//\tif (normg1 != 0) gnorm = fabs(normg1 - m_normg0)/normg1; else gnorm = fabs(normg1 - m_normg0);\n\tgnorm = fabs(normg1 - m_normg0);\n\n\tfeLog(\" slide line # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    normal force : %15le\", lnorm);\n\tif (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\tfeLog(\"    gap function : %15le\", gnorm);\n\tif (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n\n\t// check convergence\n\tbconv = true;\n\tif ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n\tif ((m_gtol > 0) && (gnorm > m_gtol)) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif (m_naugmax <= naug) bconv = true;\n\t\t\n\tif (bconv == false)\n\t{\n\t\t// we did not converge so update multipliers\n\t\tfor (int i=0; i<pl.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tLn = pl.m_data[i].Lm + eps*pl.m_data[i].gap;\n\t\t\tpl.m_data[i].Lm = Ln;\n\t\t}\n\t}\n\n\t// store the last gap norm\n\tm_normg0 = normg1;\n\n\treturn bconv;\n}\n\nvoid FESlideLineConstraint::UpdateContactPressures()\n{\n\tdouble eps = Penalty();\n\t// loop over all nodes of the primary line\n\tfor (int n=0; n<pl.Nodes(); ++n)\n\t{\n\t\t// get the normal tractions at the integration points\n\t\tdouble gap = pl.m_data[n].gap;\n\t\tpl.m_data[n].Ln = pl.m_data[n].Lm + eps*gap;\n\t}\n}\n\nvoid FESlideLineConstraint::Serialize(DumpStream& ar)\n{\n\t// store contact data\n\tFENLConstraint::Serialize(ar);\n\n\t// store contact surface data\n\tpl.Serialize(ar);\n\tsl.Serialize(ar);\n\n\t// restore element pointers\n\tSerializePointers(ar);\n\n\tar & m_bfirst & m_normg0;\n}\n\nvoid FESlideLineConstraint::SerializePointers(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tfor (int i = 0; i < pl.m_data.size(); i++)\n\t\t{\n\t\t\tFELineElement* pe = pl.m_data[i].pme;\n\t\t\tint eid = (pe ? pe->GetLocalID() : -1);\n\t\t\tar << eid;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < pl.m_data.size(); i++)\n\t\t{\n\t\t\tint eid = -1;\n\t\t\tar >> eid;\n\t\t\tif (eid >= 0)\n\t\t\t{\n\t\t\t\tFELineElement* pe = &sl.Element(eid);\n\t\t\t\tpl.m_data[i].pme = pe;\n\t\t\t}\n\t\t\telse pl.m_data[i].pme = nullptr;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FESlideLineConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENLConstraint.h>\n#include <FECore/FEEdge.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FESlideLine : public FEEdge\n{\npublic:\n\tclass FESlidingPoint\n\t{\n\tpublic:\n\t\tFESlidingPoint();\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\t\tvoid Init();\n\n\tpublic:\n\t\tFELineElement*\t\tpme = nullptr;\n\t\tdouble\t\t\t\tgap = 0;\n\t\tvec3d\t\t\t\tnu;\n\t\tdouble\t\t\t\tr = 0;\t//!< natural coordinates of primary surface projection on secondary surface element\n\t\tdouble\t\t\t\tLn = 0;\t//!< contact pressure\n\t\tdouble\t\t\t\tLm = 0;\t//!< Lagrange multipliers for contact pressure\n\t};\n\npublic:\n\tstruct Projection\n\t{\n\t\tFELineElement* pe = nullptr;\n\t\tvec3d q;\n\t\tdouble r = 0;\n\t};\n\npublic:\n\t//! constructor\n\tFESlideLine(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\t//! Serialize data to archive\n\tvoid Serialize(DumpStream& ar);\n\n\tProjection ClosestProjection(const vec3d& x);\n\npublic:\n\tvector<FESlidingPoint>\t\tm_data;\t//!< sliding contact surface data\n};\n\nclass FEBIOMECH_API FESlideLineConstraint : public FENLConstraint\n{\npublic:\n\t//! constructor\n\tFESlideLineConstraint(FEModel* pfem);\n\n\t//! Initializes sliding interface\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! projects primary line nodes onto secondary line nodes\n\tvoid ProjectPoints(bool bupseg);\n\n\t//! calculate penalty value\n\tdouble Penalty() { return m_eps; }\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! calculate contact pressures for file output\n\tvoid UpdateContactPressures();\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\t//! calculate the nodal force of a primary surface node\n\tvoid ContactNodalForce(int m, FELineElement& mel, vector<double>& fe);\n\n\t//! calculate the stiffness contribution of a single primary surface node\n\tvoid ContactNodalStiffness(int m, FELineElement& mel, matrix& ke);\n\nprivate:\n\tvoid SerializePointers(DumpStream& ar);\n\npublic:\n\tint\t\t\t\tm_laugon;\n\tint\t\t\t\tm_naugmax;\t//!< maximum nr of augmentations\n\tint\t\t\t\tm_naugmin;\t//!< minimum nr of augmentations\n\tdouble\t\t\tm_gtol;\t\t//!< gap tolerance\n\tdouble\t\t\tm_atol;\t\t//!< augmentation tolerance\n\n\tdouble\t\t\tm_eps;\t\t//!< penalty scale factor \n\n\tint\t\t\t\tm_nsegup;\t//!< segment update parameter\n\nprivate:\n\tbool\tm_bfirst;\t//!< flag to indicate the first time we enter Update\n\tdouble\tm_normg0;\t//!< initial gap norm\n\nprivate:\n\tFESlideLine pl; // primary line\n\tFESlideLine sl; // secondary line\n\npublic:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESlidingElasticInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingElasticInterface.h\"\n#include \"FECore/FENormalProjection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingElasticInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"             )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n\tADD_PARAMETER(m_gtol     , \"gaptol\"             )->setUnits(UNIT_LENGTH);\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n    ADD_PARAMETER(m_srad     , \"search_radius\"      )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_nsegup   , \"seg_up\"             );\n\tADD_PARAMETER(m_btension , \"tension\"            );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\n\tADD_PARAMETER(m_breloc   , \"node_reloc\"         );\n\tADD_PARAMETER(m_mu       , \"fric_coeff\"         );\n\tADD_PARAMETER(m_bsmaug   , \"smooth_aug\"         );\n\tADD_PARAMETER(m_bflips   , \"flip_primary\"       );\n\tADD_PARAMETER(m_bflipm   , \"flip_secondary\"     );\n    ADD_PARAMETER(m_bshellbs , \"shell_bottom_primary\"  );\n    ADD_PARAMETER(m_bshellbm , \"shell_bottom_secondary\");\n    ADD_PARAMETER(m_offset   , \"offset\"             )->setUnits(UNIT_LENGTH);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESlidingElasticSurface::Data::Data()\n{\n    m_Lmd = 0.0;\n    m_epsn = 1.0;\n    m_Lmt = vec3d(0,0,0);\n    m_nu = m_s1 = m_dg = vec3d(0,0,0);\n    m_tr = vec3d(0,0,0);\n    m_rs = m_rsp = vec2d(0,0);\n    m_bstick = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::Data::Init()\n{\n\tFEContactMaterialPoint::Init();\n\n\tm_Lmd = 0.0;\n\tm_epsn = 1.0;\n\tm_Lmt = vec3d(0, 0, 0);\n\tm_nu = m_s1 = m_dg = vec3d(0, 0, 0);\n\tm_tr = vec3d(0, 0, 0);\n\tm_rs = m_rsp = vec2d(0, 0);\n\tm_bstick = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_dg;\n    ar & m_Lmd;\n    ar & m_epsn;\n    ar & m_Lmt;\n    ar & m_nu;\n\tar & m_s1;\n    ar & m_tr;\n\tar & m_rs;\n\tar & m_rsp;\n\tar & m_bstick;\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingElasticSurface\n//-----------------------------------------------------------------------------\n\nFESlidingElasticSurface::FESlidingElasticSurface(FEModel* pfem) : FEContactSurface(pfem)\n{\n    \n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingElasticSurface::Init()\n{\n    // initialize surface data first\n    if (FEContactSurface::Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::InitSlidingSurface()\n{\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n\t\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(j));\n            // Store current surface projection values as previous\n            data.m_rsp = data.m_rs;\n\t\t\tdata.m_pmep = data.m_pme;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FESlidingElasticSurface::CreateMaterialPoint()\n{\n\treturn new FESlidingElasticSurface::Data;\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FESlidingElasticSurface::Serialize(DumpStream& ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tar & m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingElasticSurface::GetContactForce()\n{\n    return m_Ft;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingElasticSurface::GetContactArea()\n{\n    // initialize contact area\n    double a = 0;\n    \n    // loop over all elements of the primary surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        int nint = el.GaussPoints();\n        \n        // evaluate the contact force for that element\n        for (int i=0; i<nint; ++i)\n        {\n            // get data for this integration point\n\t\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(i));\n            double s = (data.m_Ln > 0) ? 1 : 0;\n            \n            // get the base vectors\n            vec3d g[2];\n            CoBaseVectors(el, i, g);\n            \n            // normal (magnitude = area)\n            vec3d n = g[0] ^ g[1];\n            \n            // gauss weight\n            double w = el.GaussWeights()[i];\n            \n            // contact force\n            a += n.norm()*(w*s);\n        }\n    }\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::GetVectorGap(int nface, vec3d& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_dg;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tpt += data.m_tr;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::GetNodalVectorGap(int nface, vec3d* pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vec3d gi[FEElement::MAX_INTPOINTS];\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tgi[k] = data.m_dg;\n\t}\n    el.project_to_nodes(gi, pg);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::GetNodalContactPressure(int nface, double* pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    double ti[FEElement::MAX_INTPOINTS];\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tti[k] = data.m_Ln;\n\t}\n    el.FEElement::project_to_nodes(ti, pg);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::GetNodalContactTraction(int nface, vec3d* pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vec3d ti[FEElement::MAX_INTPOINTS];\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tti[k] = data.m_tr;\n\t}\n    el.project_to_nodes(ti, pt);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticSurface::GetStickStatus(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tif (data.m_bstick) pg += 1.0;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingElasticInterface\n//-----------------------------------------------------------------------------\n\nFESlidingElasticInterface::FESlidingElasticInterface(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n    static int count = 1;\n    SetID(count++);\n    \n    // initial values\n    m_knmult = 0;\n    m_atol = 0.1;\n    m_epsn = 1;\n    m_btwo_pass = false;\n    m_gtol = 0;\n    m_stol = 0.01;\n    m_bsymm = true;\n    m_srad = 1.0;\n    m_nsegup = 0;\n    m_bautopen = false;\n\tm_bupdtpen = false;\n    m_btension = false;\n    m_breloc = false;\n    m_bsmaug = false;\n    m_mu = 0.0;\n    \n    m_naugmin = 0;\n    m_naugmax = 10;\n    \n    m_bfreeze = false;\n    m_bflipm = m_bflips = false;\n    m_bshellbm = m_bshellbs = false;\n    \n    m_offset = 0;\n\n    // set parents\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n\n    m_ss.SetSibling(&m_ms);\n    m_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\nFESlidingElasticInterface::~FESlidingElasticInterface()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingElasticInterface::Init()\n{\n    \n    \n    // check friction and tension parameters\n    // since they cannot be used simultaneously\n\tif ((m_mu != 0) && m_btension) {\n\t\tfeLogError(\"The tension option cannot be used with friction in sliding-elastic.\");\n\t\treturn false;\n\t}\n    \n    // when the friction coefficient is non-zero include higher-order terms in stiffness matrix\n    if (m_mu != 0) m_knmult = 1;\n    \n    // initialize surface data\n    if (m_ss.Init() == false) return false;\n    if (m_ms.Init() == false) return false;\n    \n\t// Flip secondary and primary surfaces, if requested.\n\t// Note that we turn off those flags because otherwise we keep flipping, each time we get here (e.g. in optimization)\n\t// TODO: Of course, we shouldn't get here more than once. I think we also get through the FEModel::Reset, so I'll have\n\t//       look into that. \n\tif (m_bflips) { m_ss.Invert(); m_bflips = false; }\n\tif (m_bflipm) { m_ms.Invert(); m_bflipm = false; }\n    if (m_bshellbs) { m_ss.SetShellBottom(m_bshellbs); m_bshellbs = false; }\n    if (m_bshellbm) { m_ms.SetShellBottom(m_bshellbm); m_bshellbm = false; }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::Activate()\n{\n    // don't forget to call the base class!\n    FEContactInterface::Activate();\n    \n    UpdateAutoPenalty();\n    \n    // update sliding interface data\n    Update();\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FESlidingElasticInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // get the DOFS\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_RU = fem.GetDOFIndex(\"Ru\");\n    const int dof_RV = fem.GetDOFIndex(\"Rv\");\n    const int dof_RW = fem.GetDOFIndex(\"Rw\");\n    \n    vector<int> lm(6*FEElement::MAX_NODES*2);\n    \n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingElasticSurface& ss = (np == 0? m_ss : m_ms);\n        \n        int k, l;\n        for (int j=0; j<ss.Elements(); ++j)\n        {\n            FESurfaceElement& se = ss.Element(j);\n            int nint = se.GaussPoints();\n            int* sn = &se.m_node[0];\n            for (k=0; k<nint; ++k)\n            {\n\t\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*se.GetMaterialPoint(k));\n\n                FESurfaceElement* pe = data.m_pme;\n                \n                if (pe != 0)\n                {\n                    FESurfaceElement& me = *pe;\n                    int* mn = &me.m_node[0];\n                    \n                    assign(lm, -1);\n                    \n                    int nseln = se.Nodes();\n                    int nmeln = me.Nodes();\n                    \n                    for (l=0; l<nseln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(sn[l]).m_ID;\n                        lm[6*l  ] = id[dof_X];\n                        lm[6*l+1] = id[dof_Y];\n                        lm[6*l+2] = id[dof_Z];\n                        lm[6*l+3] = id[dof_RU];\n                        lm[6*l+4] = id[dof_RV];\n                        lm[6*l+5] = id[dof_RW];\n                    }\n                    \n                    for (l=0; l<nmeln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(mn[l]).m_ID;\n                        lm[6*(l+nseln)  ] = id[dof_X];\n                        lm[6*(l+nseln)+1] = id[dof_Y];\n                        lm[6*(l+nseln)+2] = id[dof_Z];\n                        lm[6*(l+nseln)+3] = id[dof_RU];\n                        lm[6*(l+nseln)+4] = id[dof_RV];\n                        lm[6*(l+nseln)+5] = id[dof_RW];\n                    }\n                    \n                    K.build_add(lm);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::CalcAutoPenalty(FESlidingElasticSurface& s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*el.GetMaterialPoint(j));\n            data.m_epsn = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::ProjectSurface(FESlidingElasticSurface& ss, FESlidingElasticSurface& ms, bool bupseg, bool bmove)\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    \n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    // if we need to project the nodes onto the secondary surface,\n    // let's do this first\n    if (bmove)\n    {\n        int NN = ss.Nodes();\n        int NE = ss.Elements();\n        // first we need to calculate the node normals\n        vector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n        for (int i=0; i<NE; ++i)\n        {\n            FESurfaceElement& el = ss.Element(i);\n            int ne = el.Nodes();\n            for (int j=0; j<ne; ++j)\n            {\n                vec3d r0, rp, rm;\n                if (!ss.IsShellBottom()) {\n                    r0 = ss.Node(el.m_lnode[ j         ]).m_rt;\n                    rp = ss.Node(el.m_lnode[(j+   1)%ne]).m_rt;\n                    rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).m_rt;\n                }\n                else {\n                    r0 = ss.Node(el.m_lnode[ j         ]).st();\n                    rp = ss.Node(el.m_lnode[(j+   1)%ne]).st();\n                    rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).st();\n                }\n                vec3d n = (rp - r0)^(rm - r0);\n                normal[el.m_lnode[j]] += n;\n            }\n        }\n        for (int i=0; i<NN; ++i) normal[i].unit();\n        if (ss.IsShellBottom()) for (int i=0; i<NN; ++i) normal[i] = -normal[i];\n        \n        // loop over all nodes\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = ss.Node(i);\n            \n            // get the spatial nodal coordinates\n            vec3d rt = ss.IsShellBottom() ? node.st() : node.m_rt;\n            vec3d nu = normal[i];\n            \n            // project onto the secondary surface\n            vec3d q;\n            double rs[2] = {0,0};\n            FESurfaceElement* pme = np.Project(rt, nu, rs);\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double gap = nu*(rt - q);\n                \n                if (gap>0) {\n                    if (!ss.IsShellBottom()) {\n                        node.m_r0 = node.m_rt = q;\n                    }\n                    else {\n                        node.m_r0 = node.m_rt = q + node.m_d0;\n                    }\n                }\n                \n            }\n        }\n    }\n    \n    // loop over all integration points\n#pragma omp parallel for schedule(dynamic)\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint nint = el.GaussPoints();\n\n\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t{\n\t\t\t\t// get the integration point data\n\t\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\n\t\t\t\t// calculate the global position of the integration point\n\t\t\t\tvec3d r = ss.Local2Global(el, j);\n\n\t\t\t\t// calculate the normal at this integration point\n\t\t\t\tvec3d nu = ss.SurfaceNormal(el, j);\n\n\t\t\t\t// first see if the old intersected face is still good enough\n\t\t\t\tFESurfaceElement* pme = data.m_pme;\n\t\t\t\tdouble rs[2] = { 0,0 };\n\t\t\t\tif (pme && pme->isActive())\n\t\t\t\t{\n\t\t\t\t\tdouble g;\n\n\t\t\t\t\t// see if the ray intersects this element\n\t\t\t\t\tif (ms.Intersect(*pme, r, nu, rs, g, m_stol))\n\t\t\t\t\t{\n\t\t\t\t\t\tdata.m_rs[0] = rs[0];\n\t\t\t\t\t\tdata.m_rs[1] = rs[1];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpme = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// find the intersection point with the secondary surface\n\t\t\t\tif (pme == 0 && bupseg) pme = np.Project(r, nu, rs);\n\n\t\t\t\tdata.m_pme = pme;\n\t\t\t\tdata.m_nu = nu;\n\t\t\t\tdata.m_rs[0] = rs[0];\n\t\t\t\tdata.m_rs[1] = rs[1];\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\t// the node could potentially be in contact\n\t\t\t\t\t// find the global location of the intersection point\n\t\t\t\t\tvec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n\n\t\t\t\t\t// calculate the gap function\n\t\t\t\t\t// NOTE: this has the opposite sign compared\n\t\t\t\t\t// to Gerard's notes.\n\t\t\t\t\tdouble g = nu * (r - q) + m_offset;\n\n\t\t\t\t\tdouble eps = m_epsn * data.m_epsn * psf;\n\n\t\t\t\t\tdouble Ln = data.m_Lmd + eps * g;\n\n\t\t\t\t\tdata.m_gap = (g <= m_srad ? g : 0);\n\n\t\t\t\t\tif ((g > m_srad) || ((!m_btension) && (Ln < 0))) {\n\t\t\t\t\t\tdata.m_Lmd = 0;\n\t\t\t\t\t\tdata.m_pme = 0;\n\t\t\t\t\t\tdata.m_gap = 0;\n\t\t\t\t\t\tdata.m_dg = data.m_Lmt = vec3d(0, 0, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// the node is not in contact\n\t\t\t\t\tdata.m_Lmd = 0;\n\t\t\t\t\tdata.m_gap = 0;\n\t\t\t\t\tdata.m_dg = data.m_Lmt = vec3d(0, 0, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingElasticInterface::Update()\n{\n    static int naug = 0;\n    static int biter = 0;\n    \n    FEModel& fem = *GetFEModel();\n    \n    // get the iteration number\n    // we need this number to see if we can do segment updates or not\n    // also reset number of iterations after each augmentation\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolver* psolver = pstep->GetFESolver();\n    if (psolver->m_niter == 0) {\n        biter = 0;\n        naug = psolver->m_naug;\n        // check update of auto-penalty\n        if (m_bautopen && m_bupdtpen) UpdateAutoPenalty();\n    } else if (psolver->m_naug > naug) {\n        biter = psolver->m_niter;\n        naug = psolver->m_naug;\n    }\n    int niter = psolver->m_niter - biter;\n    bool bupseg = ((m_nsegup == 0)? true : (niter <= m_nsegup));\n    // get the logfile\n    //\tLogfile& log = GetLogfile();\n    //\tlog.printf(\"seg_up iteration # %d\\n\", niter+1);\n    \n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    static bool bfirst = true;\n    ProjectSurface(m_ss, m_ms, bupseg, (m_breloc && bfirst));\n    bfirst = false;\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss, bupseg);\n    \n\tint nsolve_iter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n    if (nsolve_iter == 0)\n    {\n        m_ss.InitSlidingSurface();\n        if (m_btwo_pass) m_ms.InitSlidingSurface();\n        m_bfreeze = false;\n    }\n    \n    // Update the net contact pressures\n    UpdateContactPressures();\n    \n    if (niter == 0) m_bfreeze = false;\n    \n    return;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingElasticInterface::SlipTangent(FESlidingElasticSurface& ss, const int nel, const int nint, FESlidingElasticSurface& ms, double& dh, vec3d& r)\n{\n    vec3d s1(0,0,0);\n    dh = 0;\n    \n    // get primary surface element\n    FESurfaceElement& se = ss.Element(nel);\n    \n    // get integration point data\n\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*se.GetMaterialPoint(nint));\n\tdouble g = data.m_gap;\n    vec3d nu = data.m_nu;\n    \n    // find secondary surface element\n    FESurfaceElement* pme = data.m_pme;\n    \n    // calculate previous positions\n    vec3d x2p = ms.Local2GlobalP(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d x1p = ss.Local2GlobalP(se, nint);\n    \n    // calculate dx2\n    vec3d x2 = ms.Local2Global(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d dx2 = x2 - x2p;\n    \n    // calculate dx1\n    vec3d x1 = ss.Local2Global(se, nint);\n    vec3d dx1 = x1 - x1p;\n    \n    // get current and previous covariant basis vectors\n    vec3d gscov[2], gscovp[2];\n    ss.CoBaseVectors(se, nint, gscov);\n    ss.CoBaseVectorsP(se, nint, gscovp);\n    \n    // calculate delta gscov\n    vec3d dgscov[2];\n    dgscov[0] = gscov[0] - gscovp[0];\n    dgscov[1] = gscov[1] - gscovp[1];\n    \n    // calculate m, J, Nhat\n    vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n    double detJ = (gscov[0] ^ gscov[1]).norm();\n    mat3d Nhat = (mat3dd(1) - (nu & nu));\n    \n    // calculate q\n    vec3d c = Nhat*m*(1.0/detJ);\n    \n    // calculate slip direction s1\n    double norm = (Nhat*(c*(-g)*m_knmult + dx1 - dx2)).norm();\n    if (norm != 0)\n    {\n        s1 = (Nhat*(c*(-g)*m_knmult + dx1 - dx2))/norm;\n        dh = norm;\n        r = c*(-g)*m_knmult + dx1 - dx2;\n    }\n    \n    return s1;\n    \n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingElasticInterface::ContactTraction(FESlidingElasticSurface& ss, const int nel, const int n, FESlidingElasticSurface& ms, double& pn)\n{\n    vec3d s1(0,0,0);\n    vec3d drdot(0,0,0);\n    vec3d t(0,0,0);\n    pn = 0;\n    double tn = 0, ts = 0;\n    \n    double psf = GetPenaltyScaleFactor();\n\n\t// get the primary surface element\n\tFESurfaceElement& se = ss.Element(nel);\n\n    // get the integration point data\n\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*se.GetMaterialPoint(n));\n\n    // penalty\n    double eps = m_epsn*data.m_epsn*psf;\n    \n    // normal gap\n    double g = data.m_gap;\n    \n    // normal traction Lagrange multiplier\n    double Lm = data.m_Lmd;\n    \n    // vector traction Lagrange multiplier\n    vec3d Lt = data.m_Lmt;\n    \n    // calculate the normal at this integration point\n    vec3d nu = data.m_nu;\n    \n    // get current and previous secondary elements\n    FESurfaceElement* pme = data.m_pme;\n    FESurfaceElement* pmep = data.m_pmep;\n    \n    // if we just returned from an augmentation, do not update stick or slip status\n    if (m_bfreeze && pme) {\n        if (data.m_bstick) {\n            // calculate current global position of the integration point\n            vec3d xo = ss.Local2Global(se, n);\n            \n            // calculate current global position of the previous intersection point\n            vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n            \n            // vector and normal gaps\n            vec3d dg = xt - xo;\n            \n            // calculate trial stick traction, normal component, shear component\n            t = Lt + dg*eps;\n            tn = t*nu;\n            ts = (t - nu*tn).norm();\n            \n            // contact pressure\n            pn = m_btension ? (-tn) : MBRACKET(-tn);\n            \n            // store the previous values as the current\n            data.m_pme = data.m_pmep;\n            data.m_rs = data.m_rsp;\n            \n            // recalculate gap\n            data.m_dg = dg + nu*m_offset;\n        }\n        else {\n            // recalculate contact pressure for slip\n            pn = m_btension ? (Lm + eps*g) : MBRACKET(Lm + eps*g);\n            \n            if (pn != 0)\n            {\n                \n                double dh = 0;\n                \n                // slip direction\n                s1 = FESlidingElasticInterface::SlipTangent(ss, nel, n, ms, dh, drdot);\n                \n                // total traction\n                t = (nu + s1*(m_mu))*(-pn);\n                \n                // reset slip direction\n                data.m_s1 = s1;\n            }\n            else\n            {\n                t = vec3d(0,0,0);\n            }\n        }\n    }\n    // update contact tractions\n    else {\n        data.m_bstick = false;\n        \n        if (pme)\n        {\n            // assume stick and calculate traction\n            if (pmep)\n            {\n                // calculate current global position of the integration point\n                vec3d xo = ss.Local2Global(se, n);\n                \n                // calculate current global position of the previous intersection point\n                vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n                \n                // vector and normal gaps\n                vec3d dg = xt - xo;\n                \n                // calculate trial stick traction, normal component, shear component\n                t = Lt + dg*eps;\n                tn = t*nu;\n                ts = (t - nu*tn).norm();\n                \n                // check if stick\n                if ( (tn < 0) && (ts < m_mu*fabs(tn)) )\n                {\n                    // set boolean flag for stick\n                    data.m_bstick = true;\n                    \n                    // contact pressure\n                    pn = m_btension ? (-tn) : MBRACKET(-tn);\n                    \n                    // store the previous values as the current\n                    data.m_pme = data.m_pmep;\n                    data.m_rs = data.m_rsp;\n                    \n                    // recalculate gap\n                    data.m_dg = dg + nu*m_offset;\n                }\n                else\n                {\n                    // recalculate contact pressure for slip\n                    pn = m_btension ? (Lm + eps*g) : MBRACKET(Lm + eps*g);\n                    \n                    if (pn != 0)\n                    {\n                        \n                        double dh = 0;\n                        \n                        // slip direction\n                        s1 = FESlidingElasticInterface::SlipTangent(ss, nel, n, ms, dh, drdot);\n                        \n                        // total traction\n                        t = (nu + s1*(m_mu))*(-pn);\n                        \n                        // reset slip direction\n                        data.m_s1 = s1;\n                        data.m_bstick = false;\n                    }\n                    else\n                    {\n                        t = vec3d(0,0,0);\n                    }\n                    \n                }\n            }\n            else\n            {\n                // assume slip upon first contact\n                // calculate contact pressure for slip\n                pn = m_btension ? (Lm + eps*g) : MBRACKET(Lm + eps*g);\n                \n                if (pn != 0)\n                {\n                    \n                    double dh = 0;\n                    \n                    // slip direction\n                    s1 = FESlidingElasticInterface::SlipTangent(ss, nel, n, ms, dh, drdot);\n                    \n                    // calculate frictional traction\n                    t = (nu + s1*(m_mu))*(-pn);\n                    \n                    // reset slip direction\n                    data.m_s1 = s1;\n                    data.m_bstick = false;\n                }\n            }\n        }\n    }\n    \n    return t;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    const int MN = FEElement::MAX_NODES;\n    \n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    double detJ[MN], w[MN], Hm[MN];\n    double N[MN*6];\n    \n    m_ss.m_Ft = vec3d(0,0,0);\n    m_ms.m_Ft = vec3d(0,0,0);\n    \n    // loop over the nr of passes\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        // get primary and secondary surface\n        FESlidingElasticSurface& ss = (np == 0? m_ss : m_ms);\n        FESlidingElasticSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all primary elements\n        //#pragma omp parallel for private(sLM, mLM, LM, en, fe, detJ, w, Hm, N)\n        for (int i=0; i<ss.Elements(); ++i)\n        {\n            // get the surface element\n            FESurfaceElement& se = ss.Element(i);\n\t\t\tif (se.isActive())\n\t\t\t{\n\t\t\t\t// get the nr of nodes and integration points\n\t\t\t\tint nseln = se.Nodes();\n\t\t\t\tint nint = se.GaussPoints();\n\n\t\t\t\t// copy the LM vector; we'll need it later\n\t\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t\t// we calculate all the metrics we need before we\n\t\t\t\t// calculate the nodal forces\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get the base vectors\n\t\t\t\t\tvec3d g[2];\n\t\t\t\t\tss.CoBaseVectors(se, j, g);\n\n\t\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\n\t\t\t\t\t// integration weights\n\t\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t\t}\n\n\t\t\t\t// loop over all integration points\n\t\t\t\t// note that we are integrating over the current surface\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get integration point data\n\t\t\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t\t// calculate contact pressure and account for stick\n\t\t\t\t\tdouble pn;\n\t\t\t\t\tvec3d t = ContactTraction(ss, i, j, ms, pn);\n\n\t\t\t\t\t// get the secondary element\n\t\t\t\t\tFESurfaceElement* pme = data.m_pme;\n\n\t\t\t\t\tif (pme)\n\t\t\t\t\t{\n\t\t\t\t\t\t// get the secondary element\n\t\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\t\t// get the nr of secondary element nodes\n\t\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\t\t// copy LM vector\n\t\t\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\t\tint ndof = 3 * (nseln + nmeln);\n\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * k] = sLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * k + 1] = sLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * k + 2] = sLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * (k + nseln)] = mLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 1] = mLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 2] = mLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// build the en vector\n\t\t\t\t\t\ten.resize(nseln + nmeln);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) en[k] = se.m_node[k];\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + nseln] = me.m_node[k];\n\n\t\t\t\t\t\t// get primary element shape functions\n\t\t\t\t\t\tdouble* Hs = se.H(j);\n\n\t\t\t\t\t\t// get secondary element shape functions\n\t\t\t\t\t\tdouble r = data.m_rs[0];\n\t\t\t\t\t\tdouble s = data.m_rs[1];\n\t\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t\t\tif (pn != 0) {\n\n\t\t\t\t\t\t\t// calculate the force vector\n\t\t\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\t\t\tzero(fe);\n\n\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tN[3 * k] = Hs[k] * t.x;\n\t\t\t\t\t\t\t\tN[3 * k + 1] = Hs[k] * t.y;\n\t\t\t\t\t\t\t\tN[3 * k + 2] = Hs[k] * t.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tN[3 * (k + nseln)] = -Hm[k] * t.x;\n\t\t\t\t\t\t\t\tN[3 * (k + nseln) + 1] = -Hm[k] * t.y;\n\t\t\t\t\t\t\t\tN[3 * (k + nseln) + 2] = -Hm[k] * t.z;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k) fe[k] += N[k] * detJ[j] * w[j];\n\n\t\t\t\t\t\t\t// calculate contact forces\n\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tss.m_Ft += vec3d(fe[k * 3], fe[k * 3 + 1], fe[k * 3 + 2]);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tms.m_Ft += vec3d(fe[(k + nseln) * 3], fe[(k + nseln) * 3 + 1], fe[(k + nseln) * 3 + 2]);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// assemble the global residual\n\t\t\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    // see how many reformations we've had to do so far\n    int nref = GetSolver()->m_nref;\n    \n    const int MN = FEElement::MAX_NODES;\n    \n    double detJ[MN], w[MN], Hm[MN];\n    double N[MN*6];\n    vector<int> sLM, mLM, LM, en;\n    FEElementMatrix ke;\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    // do single- or two-pass\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np < npass; ++np)\n    {\n        // get the primary and secondary surface\n        FESlidingElasticSurface& ss = (np == 0? m_ss : m_ms);\n        FESlidingElasticSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all primary elements\n        //#pragma omp parallel for private(detJ, w, Hm, N, sLM, mLM, LM, en, ke)\n\t\tfor (int i = 0; i < ss.Elements(); ++i)\n\t\t{\n\t\t\t// get ths primary element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\tif (se.isActive())\n\t\t\t{\n\t\t\t\t// get nr of nodes and integration points\n\t\t\t\tint nseln = se.Nodes();\n\t\t\t\tint nint = se.GaussPoints();\n\n\t\t\t\t// copy the LM vector\n\t\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t\t// we calculate all the metrics we need before we\n\t\t\t\t// calculate the nodal forces\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get the base vectors\n\t\t\t\t\tvec3d g[2];\n\t\t\t\t\tss.CoBaseVectors(se, j, g);\n\n\t\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\n\t\t\t\t\t// integration weights\n\t\t\t\t\tw[j] = se.GaussWeights()[j];\n\n\t\t\t\t}\n\n\t\t\t\t// loop over all integration points\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get integration point data\n\t\t\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t\t// calculate contact pressure and account for stick\n\t\t\t\t\tdouble pn;\n\t\t\t\t\tvec3d t = ContactTraction(ss, i, j, ms, pn);\n\n\t\t\t\t\t// get the secondary element\n\t\t\t\t\tFESurfaceElement* pme = data.m_pme;\n\n\t\t\t\t\tif (pme)\n\t\t\t\t\t{\n\t\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\t\t// get the nr of secondary nodes\n\t\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\t\t// copy the LM vector\n\t\t\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\t\tint ndpn = 3;\n\t\t\t\t\t\tint ndof = ndpn * (nseln + nmeln);\n\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * k] = sLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * k + 1] = sLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * k + 2] = sLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3 * (k + nseln)] = mLM[3 * k];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 1] = mLM[3 * k + 1];\n\t\t\t\t\t\t\tLM[3 * (k + nseln) + 2] = mLM[3 * k + 2];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// build the en vector\n\t\t\t\t\t\ten.resize(nseln + nmeln);\n\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) en[k] = se.m_node[k];\n\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + nseln] = me.m_node[k];\n\n\t\t\t\t\t\t// primary shape functions\n\t\t\t\t\t\tdouble* Hs = se.H(j);\n\n\t\t\t\t\t\t// secondary shape functions\n\t\t\t\t\t\tdouble r = data.m_rs[0];\n\t\t\t\t\t\tdouble s = data.m_rs[1];\n\t\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t\t\t// get primary normal vector\n\t\t\t\t\t\tvec3d nu = data.m_nu;\n\n\t\t\t\t\t\t// gap function\n\t\t\t\t\t\tdouble g = data.m_gap;\n\n\t\t\t\t\t\t// penalty\n\t\t\t\t\t\tdouble eps = m_epsn * data.m_epsn * psf;\n\n\t\t\t\t\t\t// only evaluate stiffness matrix if contact traction is non-zero\n\t\t\t\t\t\tif (pn != 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// if stick\n\t\t\t\t\t\t\tif (data.m_bstick)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble dtn = eps;\n\n\t\t\t\t\t\t\t\t// create the stiffness matrix\n\t\t\t\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\n\t\t\t\t\t\t\t\t// evaluate basis vectors on primary surface\n\t\t\t\t\t\t\t\tvec3d gscov[2];\n\t\t\t\t\t\t\t\tss.CoBaseVectors(se, j, gscov);\n\n\t\t\t\t\t\t\t\t// identity tensor\n\t\t\t\t\t\t\t\tmat3d I = mat3dd(1);\n\n\t\t\t\t\t\t\t\t// evaluate Mc and Ac and combine them into As\n\t\t\t\t\t\t\t\tdouble* Gsr = se.Gr(j);\n\t\t\t\t\t\t\t\tdouble* Gss = se.Gs(j);\n\t\t\t\t\t\t\t\tmat3d As[MN];\n\t\t\t\t\t\t\t\tmat3d gscovh[2];\n\t\t\t\t\t\t\t\tgscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) {\n\t\t\t\t\t\t\t\t\tmat3d Ac = (gscovh[1] * Gsr[k] - gscovh[0] * Gss[k]) / detJ[j];\n\t\t\t\t\t\t\t\t\tAs[k] = t & (Ac * nu);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// --- S O L I D - S O L I D   C O N T A C T ---\n\n\t\t\t\t\t\t\t\t// a. I-term\n\t\t\t\t\t\t\t\t//------------------------------------\n\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) N[k] = Hs[k];\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) N[k + nseln] = -Hm[k];\n\n\t\t\t\t\t\t\t\tdouble tmp = dtn * detJ[j] * w[j];\n\t\t\t\t\t\t\t\tfor (int l = 0; l < nseln + nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln + nmeln; ++k)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn] -= -tmp * N[k] * N[l] * I[0][0];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 1] -= -tmp * N[k] * N[l] * I[0][1];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 2] -= -tmp * N[k] * N[l] * I[0][2];\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn] -= -tmp * N[k] * N[l] * I[1][0];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 1] -= -tmp * N[k] * N[l] * I[1][1];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 2] -= -tmp * N[k] * N[l] * I[1][2];\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn] -= -tmp * N[k] * N[l] * I[2][0];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 1] -= -tmp * N[k] * N[l] * I[2][1];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 2] -= -tmp * N[k] * N[l] * I[2][2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// b. A-term\n\t\t\t\t\t\t\t\t//-------------------------------------\n\n\t\t\t\t\t\t\t\ttmp = detJ[j] * w[j];\n\t\t\t\t\t\t\t\t// non-symmetric\n\t\t\t\t\t\t\t\tfor (int l = 0; l < nseln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln + nmeln; ++k)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn] -= tmp * N[k] * As[l][0][0];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 1] -= tmp * N[k] * As[l][0][1];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 2] -= tmp * N[k] * As[l][0][2];\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn] -= tmp * N[k] * As[l][1][0];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 1] -= tmp * N[k] * As[l][1][1];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 2] -= tmp * N[k] * As[l][1][2];\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn] -= tmp * N[k] * As[l][2][0];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 1] -= tmp * N[k] * As[l][2][1];\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 2] -= tmp * N[k] * As[l][2][2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// assemble the global stiffness\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// if slip\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble tn = -pn;\n\n\t\t\t\t\t\t\t\t// create the stiffness matrix\n\t\t\t\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\n\t\t\t\t\t\t\t\t// obtain the slip direction s1 and inverse of spatial increment dh\n\t\t\t\t\t\t\t\tdouble dh = 0, hd = 0;\n\t\t\t\t\t\t\t\tvec3d dr(0, 0, 0);\n\t\t\t\t\t\t\t\tvec3d s1 = FESlidingElasticInterface::SlipTangent(ss, i, j, ms, dh, dr);\n\n\t\t\t\t\t\t\t\tif (dh != 0)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\thd = 1.0 / dh;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// evaluate basis vectors on both surfaces\n\t\t\t\t\t\t\t\tvec3d gscov[2], gmcov[2];\n\t\t\t\t\t\t\t\tss.CoBaseVectors(se, j, gscov);\n\t\t\t\t\t\t\t\tms.CoBaseVectors(me, r, s, gmcov);\n\t\t\t\t\t\t\t\tmat2d A;\n\t\t\t\t\t\t\t\tA[0][0] = gscov[0] * gmcov[0]; A[0][1] = gscov[0] * gmcov[1];\n\t\t\t\t\t\t\t\tA[1][0] = gscov[1] * gmcov[0]; A[1][1] = gscov[1] * gmcov[1];\n\t\t\t\t\t\t\t\tmat2d a = A.inverse();\n\n\t\t\t\t\t\t\t\t// evaluate covariant basis vectors on primary surface at previous time step\n\t\t\t\t\t\t\t\tvec3d gscovp[2];\n\t\t\t\t\t\t\t\tss.CoBaseVectorsP(se, j, gscovp);\n\n\t\t\t\t\t\t\t\t// calculate delta gscov\n\t\t\t\t\t\t\t\tvec3d dgscov[2];\n\t\t\t\t\t\t\t\tdgscov[0] = gscov[0] - gscovp[0];\n\t\t\t\t\t\t\t\tdgscov[1] = gscov[1] - gscovp[1];\n\n\t\t\t\t\t\t\t\t// evaluate contravariant basis vectors\n\t\t\t\t\t\t\t\tvec3d gscnt[2], gmcnt[2];\n\t\t\t\t\t\t\t\tif (m_knmult == 0)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t// evaluate true contravariant basis vectors when gap = 0\n\t\t\t\t\t\t\t\t\tss.ContraBaseVectors(se, j, gscnt);\n\t\t\t\t\t\t\t\t\tms.ContraBaseVectors(me, r, s, gmcnt);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t// evaluate approximate contravariant basis vectors when gap != 0\n\t\t\t\t\t\t\t\t\tgmcnt[0] = gscov[0] * a[0][0] + gscov[1] * a[0][1];\n\t\t\t\t\t\t\t\t\tgmcnt[1] = gscov[0] * a[1][0] + gscov[1] * a[1][1];\n\t\t\t\t\t\t\t\t\tgscnt[0] = gmcov[0] * a[0][0] + gmcov[1] * a[1][0];\n\t\t\t\t\t\t\t\t\tgscnt[1] = gmcov[0] * a[0][1] + gmcov[1] * a[1][1];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// evaluate N and S tensors and approximations when gap != 0\n\t\t\t\t\t\t\t\tmat3ds N1 = dyad(nu);\n\t\t\t\t\t\t\t\tmat3d Nh1 = mat3dd(1) - (nu & nu);\n\t\t\t\t\t\t\t\tmat3d Nb1 = mat3dd(1) - (gscov[0] & gscnt[0]) * m_knmult - (gscov[1] & gscnt[1]) * m_knmult;\n\t\t\t\t\t\t\t\tmat3d Nt1 = nu & (Nb1 * nu);\n\t\t\t\t\t\t\t\tmat3d S1 = s1 & nu;\n\t\t\t\t\t\t\t\tmat3d Sh1 = (mat3dd(1) - (s1 & s1)) * hd;\n\t\t\t\t\t\t\t\tmat3d Sb1 = s1 & (Nb1 * nu);\n\n\t\t\t\t\t\t\t\t// evaluate m, c, B, and R\n\t\t\t\t\t\t\t\t// evaluate L1 from Mg and R\n\t\t\t\t\t\t\t\tvec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n\t\t\t\t\t\t\t\tvec3d c = Sh1 * Nh1 * m * (1 / detJ[j]);\n\t\t\t\t\t\t\t\tmat3d Mg = (mat3dd(1) * (nu * m) + (nu & m)) * (1 / detJ[j]);\n\t\t\t\t\t\t\t\tmat3d B = (c & (Nb1 * nu)) * m_knmult - Sh1 * Nh1;\n\t\t\t\t\t\t\t\tmat3d R = mat3dd(1) * (nu * dr) + (nu & dr);\n\t\t\t\t\t\t\t\tmat3d L1 = Sh1 * ((Nh1 * Mg - mat3dd(1)) * (-g) * m_knmult + R) * Nh1;\n\n\t\t\t\t\t\t\t\t// evaluate Mc and Ac and combine them into As\n\t\t\t\t\t\t\t\t// evaluate s1 dyad (N1*mc - Ac*nu) + c dyad (N1*mc + Ac*nu)*g*hd as Pc\n\t\t\t\t\t\t\t\t// evaluate Fc from Ac_bar (Ab)\n\t\t\t\t\t\t\t\tdouble* Gsr = se.Gr(j);\n\t\t\t\t\t\t\t\tdouble* Gss = se.Gs(j);\n\t\t\t\t\t\t\t\tmat3d As[MN];\n\t\t\t\t\t\t\t\tmat3d gscovh[2];\n\t\t\t\t\t\t\t\tmat3d dgscovh[2];\n\t\t\t\t\t\t\t\tmat3d Pc[MN];\n\t\t\t\t\t\t\t\tmat3d Jc[MN];\n\t\t\t\t\t\t\t\tgscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n\t\t\t\t\t\t\t\tdgscovh[0].skew(dgscov[0]); dgscovh[1].skew(dgscov[1]);\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) {\n\t\t\t\t\t\t\t\t\tvec3d mc = gscnt[0] * Gsr[k] + gscnt[1] * Gss[k];\n\t\t\t\t\t\t\t\t\tmat3d Mc = nu & mc;\n\t\t\t\t\t\t\t\t\tmat3d Ac = (gscovh[1] * Gsr[k] - gscovh[0] * Gss[k]) / detJ[j];\n\t\t\t\t\t\t\t\t\tmat3d Ab = (dgscovh[1] * Gsr[k] - dgscovh[0] * Gss[k]) / detJ[j];\n\t\t\t\t\t\t\t\t\tPc[k] = (s1 & (N1 * mc * m_knmult - Ac * nu)) + ((c & (N1 * mc + Ac * nu)) * (-g) * m_knmult);\n\t\t\t\t\t\t\t\t\tAs[k] = Ac + Mc * N1 * m_knmult;\n\t\t\t\t\t\t\t\t\tJc[k] = (L1 * Ac - (Sh1 * Nh1 * Ab * (-g) * m_knmult));\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// evaluate Mb\n\t\t\t\t\t\t\t\t// evaluate s1 dyad mb and combine as Psb\n\t\t\t\t\t\t\t\tdouble Gmr[MN], Gms[MN];\n\t\t\t\t\t\t\t\tme.shape_deriv(Gmr, Gms, r, s);\n\t\t\t\t\t\t\t\tmat3d Pb[MN];\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) {\n\t\t\t\t\t\t\t\t\tvec3d n(0, 0, 0);\n\t\t\t\t\t\t\t\t\tif (m_knmult == 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tn = gmcnt[0] ^ gmcnt[1];\n\t\t\t\t\t\t\t\t\t\tn.unit();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tn = -nu;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tvec3d mb = gmcnt[0] * Gmr[k] + gmcnt[1] * Gms[k];\n\t\t\t\t\t\t\t\t\tmat3d Mb = n & mb;\n\t\t\t\t\t\t\t\t\tPb[k] = Mb - ((s1 & mb) * m_mu);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// evaluate Gbc\n\t\t\t\t\t\t\t\tmatrix Gbc(nmeln, nseln);\n\t\t\t\t\t\t\t\tfor (int b = 0; b < nmeln; ++b) {\n\t\t\t\t\t\t\t\t\tfor (int c = 0; c < nseln; ++c) {\n\t\t\t\t\t\t\t\t\t\tGbc(b, c)\n\t\t\t\t\t\t\t\t\t\t\t= (a[0][0] * Gmr[b] * Gsr[c]\n\t\t\t\t\t\t\t\t\t\t\t\t+ a[0][1] * Gmr[b] * Gss[c]\n\t\t\t\t\t\t\t\t\t\t\t\t+ a[1][0] * Gms[b] * Gsr[c]\n\t\t\t\t\t\t\t\t\t\t\t\t+ a[1][1] * Gms[b] * Gss[c]) * (-g) * m_knmult;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// define T, Ttb\n\t\t\t\t\t\t\t\tmat3d T = N1 + (S1 * m_mu);\n\t\t\t\t\t\t\t\tmat3d Ttb = Nt1 + (Sb1 * m_mu);\n\n\t\t\t\t\t\t\t\t// --- S O L I D - S O L I D   C O N T A C T ---\n\n\t\t\t\t\t\t\t\t// a. NxN-term\n\t\t\t\t\t\t\t\t//------------------------------------\n\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln; ++k) N[k] = Hs[k];\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) N[k + nseln] = -Hm[k];\n\n\t\t\t\t\t\t\t\tdouble tmp = detJ[j] * w[j];\n\t\t\t\t\t\t\t\tfor (int l = 0; l < nseln + nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln + nmeln; ++k)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn] -= -tmp * N[k] * N[l] * (eps * Ttb[0][0] + m_mu * tn * B[0][0]);\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 1] -= -tmp * N[k] * N[l] * (eps * Ttb[0][1] + m_mu * tn * B[0][1]);\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 2] -= -tmp * N[k] * N[l] * (eps * Ttb[0][2] + m_mu * tn * B[0][2]);\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn] -= -tmp * N[k] * N[l] * (eps * Ttb[1][0] + m_mu * tn * B[1][0]);\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 1] -= -tmp * N[k] * N[l] * (eps * Ttb[1][1] + m_mu * tn * B[1][1]);\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 2] -= -tmp * N[k] * N[l] * (eps * Ttb[1][2] + m_mu * tn * B[1][2]);\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn] -= -tmp * N[k] * N[l] * (eps * Ttb[2][0] + m_mu * tn * B[2][0]);\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 1] -= -tmp * N[k] * N[l] * (eps * Ttb[2][1] + m_mu * tn * B[2][1]);\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 2] -= -tmp * N[k] * N[l] * (eps * Ttb[2][2] + m_mu * tn * B[2][2]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// b. Na,Nb-term\n\t\t\t\t\t\t\t\t//-------------------------------------\n\n\t\t\t\t\t\t\t\ttmp = tn * detJ[j] * w[j];\n\t\t\t\t\t\t\t\t// non-symmetric\n\t\t\t\t\t\t\t\tfor (int l = 0; l < nseln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (int k = 0; k < nseln + nmeln; ++k)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn] -= -tmp * N[k] * (As[l][0][0] + m_mu * (Pc[l][0][0] - Jc[l][0][0]));\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 1] -= -tmp * N[k] * (As[l][0][1] + m_mu * (Pc[l][0][1] - Jc[l][0][1]));\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn][l * ndpn + 2] -= -tmp * N[k] * (As[l][0][2] + m_mu * (Pc[l][0][2] - Jc[l][0][2]));\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn] -= -tmp * N[k] * (As[l][1][0] + m_mu * (Pc[l][1][0] - Jc[l][1][0]));\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 1] -= -tmp * N[k] * (As[l][1][1] + m_mu * (Pc[l][1][1] - Jc[l][1][1]));\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 1][l * ndpn + 2] -= -tmp * N[k] * (As[l][1][2] + m_mu * (Pc[l][1][2] - Jc[l][1][2]));\n\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn] -= -tmp * N[k] * (As[l][2][0] + m_mu * (Pc[l][2][0] - Jc[l][2][0]));\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 1] -= -tmp * N[k] * (As[l][2][1] + m_mu * (Pc[l][2][1] - Jc[l][2][1]));\n\t\t\t\t\t\t\t\t\t\tke[k * ndpn + 2][l * ndpn + 2] -= -tmp * N[k] * (As[l][2][2] + m_mu * (Pc[l][2][2] - Jc[l][2][2]));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// c. Nc,Nd-term\n\t\t\t\t\t\t\t\t//---------------------------------------\n\n\t\t\t\t\t\t\t\ttmp = tn * detJ[j] * w[j];\n\t\t\t\t\t\t\t\t// non-symmetric\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (int l = 0; l < nseln + nmeln; ++l)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn][l * ndpn] -= tmp * N[l] * Pb[k][0][0];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn][l * ndpn + 1] -= tmp * N[l] * Pb[k][0][1];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn][l * ndpn + 2] -= tmp * N[l] * Pb[k][0][2];\n\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 1][l * ndpn] -= tmp * N[l] * Pb[k][1][0];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 1][l * ndpn + 1] -= tmp * N[l] * Pb[k][1][1];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 1][l * ndpn + 2] -= tmp * N[l] * Pb[k][1][2];\n\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 2][l * ndpn] -= tmp * N[l] * Pb[k][2][0];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 2][l * ndpn + 1] -= tmp * N[l] * Pb[k][2][1];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 2][l * ndpn + 2] -= tmp * N[l] * Pb[k][2][2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// c. Gbc-term\n\t\t\t\t\t\t\t\t//---------------------------------------\n\n\t\t\t\t\t\t\t\ttmp = tn * detJ[j] * w[j];\n\t\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (int l = 0; l < nseln; ++l)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tmat3d gT = T * (Gbc[k][l] * tmp);\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn][l * ndpn] -= gT[0][0];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn][l * ndpn + 1] -= gT[0][1];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn][l * ndpn + 2] -= gT[0][2];\n\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 1][l * ndpn] -= gT[1][0];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 1][l * ndpn + 1] -= gT[1][1];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 1][l * ndpn + 2] -= gT[1][2];\n\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 2][l * ndpn] -= gT[2][0];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 2][l * ndpn + 1] -= gT[2][1];\n\t\t\t\t\t\t\t\t\t\tke[(k + nseln) * ndpn + 2][l * ndpn + 2] -= gT[2][2];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// assemble the global stiffness\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::UpdateContactPressures()\n{\n    double psf = GetPenaltyScaleFactor();\n    \n    int npass = (m_btwo_pass?2:1);\n    const int MN = FEElement::MAX_NODES;\n    const int MI = FEElement::MAX_INTPOINTS;\n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingElasticSurface& ss = (np == 0? m_ss : m_ms);\n        FESlidingElasticSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all elements of the primary surface\n#pragma omp parallel for\n        for (int n=0; n<ss.Elements(); ++n)\n        {\n            FESurfaceElement& el = ss.Element(n);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint nint = el.GaussPoints();\n\n\t\t\t\t// get the normal tractions at the integration points\n\t\t\t\tfor (int i = 0; i < nint; ++i)\n\t\t\t\t{\n\t\t\t\t\t// get integration point data\n\t\t\t\t\tFESlidingElasticSurface::Data& sd = static_cast<FESlidingElasticSurface::Data&>(*el.GetMaterialPoint(i));\n\n\t\t\t\t\tdouble pn = 0;\n\n\t\t\t\t\t// evaluate traction on primary surface\n\t\t\t\t\tdouble eps = m_epsn * sd.m_epsn * psf;\n\t\t\t\t\tif (sd.m_bstick) {\n\t\t\t\t\t\t// if stick, evaluate total traction\n\t\t\t\t\t\tsd.m_tr = sd.m_Lmt + sd.m_dg * eps;\n\t\t\t\t\t\t// then derive normal component\n\t\t\t\t\t\tsd.m_Ln = -sd.m_tr * sd.m_nu;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// if slip, evaluate normal traction\n\t\t\t\t\t\tdouble Ln = sd.m_Lmd + eps * sd.m_gap;\n\t\t\t\t\t\tsd.m_Ln = m_btension ? Ln : MBRACKET(Ln);\n\t\t\t\t\t\t// then derive total traction\n\t\t\t\t\t\tsd.m_tr = -(sd.m_nu + sd.m_s1 * m_mu) * sd.m_Ln;\n\t\t\t\t\t}\n\n\t\t\t\t\tFESurfaceElement* pme = sd.m_pme;\n\n\t\t\t\t\tif (m_btwo_pass && pme)\n\t\t\t\t\t{\n\t\t\t\t\t\t// get secondary element data\n\t\t\t\t\t\tint mint = pme->GaussPoints();\n\t\t\t\t\t\tdouble pi[MI];\n\t\t\t\t\t\tvec3d ti[MI];\n\t\t\t\t\t\tfor (int j = 0; j < mint; ++j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFESlidingElasticSurface::Data& md = static_cast<FESlidingElasticSurface::Data&>(*pme->GetMaterialPoint(j));\n\n\t\t\t\t\t\t\tpn = 0;\n\t\t\t\t\t\t\t// evaluate traction on secondary surface\n\t\t\t\t\t\t\tdouble eps = m_epsn * md.m_epsn * psf;\n\t\t\t\t\t\t\tif (md.m_bstick) {\n\t\t\t\t\t\t\t\t// if stick, evaluate total traction\n\t\t\t\t\t\t\t\tti[j] = md.m_Lmt + md.m_dg * eps;\n\t\t\t\t\t\t\t\t// then derive normal component\n\t\t\t\t\t\t\t\tpi[j] = -ti[j] * md.m_nu;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t// if slip, evaluate normal traction\n\t\t\t\t\t\t\t\tdouble Ln = md.m_Lmd + eps * md.m_gap;\n\t\t\t\t\t\t\t\tpi[j] = m_btension ? Ln : MBRACKET(Ln);\n\t\t\t\t\t\t\t\t// then derive total traction\n\t\t\t\t\t\t\t\tti[j] = -(md.m_nu + md.m_s1 * m_mu) * pi[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// project the data to the nodes\n\t\t\t\t\t\tdouble pn[MN];\n\t\t\t\t\t\tvec3d tn[MN];\n\t\t\t\t\t\tpme->FEElement::project_to_nodes(pi, pn);\n\t\t\t\t\t\tpme->project_to_nodes(ti, tn);\n\t\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\t\tdouble Ln = pme->eval(pn, sd.m_rs[0], sd.m_rs[1]);\n\t\t\t\t\t\tvec3d trac = pme->eval(tn, sd.m_rs[0], sd.m_rs[1]);\n\t\t\t\t\t\tsd.m_Ln += (m_btension ? Ln : MBRACKET(Ln));\n\t\t\t\t\t\t// tractions on secondary-primary are opposite, so subtract\n\t\t\t\t\t\tsd.m_tr -= trac;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingElasticInterface::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n    if (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\t// don't augment if tolerance is zero (or negative)\n\tif ((m_atol <= 0.0) && (m_gtol <= 0.0))\n\t{\n\t\tfeLogInfo(\"Augmentation skipped since tolerance is zero.\");\n\t\treturn true;\n\t}\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    double Ln;\n    bool bconv = true;\n    \n    int NS = m_ss.Elements();\n    int NM = m_ms.Elements();\n    \n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0;\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& se = m_ss.Element(i);\n\t\tif (se.isActive())\n\t\t{\n\t\t\tfor (int j = 0; j < se.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFESlidingElasticSurface::Data& ds = static_cast<FESlidingElasticSurface::Data&>(*se.GetMaterialPoint(j));\n\t\t\t\tif (ds.m_bstick)\n\t\t\t\t\tnormL0 += ds.m_Lmt * ds.m_Lmt;\n\t\t\t\telse\n\t\t\t\t\tnormL0 += ds.m_Lmd * ds.m_Lmd;\n\t\t\t}\n\t\t}\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& me = m_ms.Element(i);\n\t\tif (me.isActive())\n\t\t{\n\t\t\tfor (int j = 0; j < me.GaussPoints(); ++j)\n\t\t\t{\n\t\t\t\tFESlidingElasticSurface::Data& dm = static_cast<FESlidingElasticSurface::Data&>(*me.GetMaterialPoint(j));\n\t\t\t\tif (dm.m_bstick)\n\t\t\t\t\tnormL0 += dm.m_Lmt * dm.m_Lmt;\n\t\t\t\telse\n\t\t\t\t\tnormL0 += dm.m_Lmd * dm.m_Lmd;\n\n\t\t\t}\n\t\t}\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0;\n    \n    // update Lagrange multipliers\n    double normL1 = 0;\n    for (int i=0; i<m_ss.Elements(); ++i) {\n        FESurfaceElement& el = m_ss.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tvec3d tn[FEElement::MAX_INTPOINTS];\n\t\t\tif (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j) {\n\t\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t// update Lagrange multipliers on primary surface\n\t\t\t\tif (data.m_bstick) {\n\t\t\t\t\t// if stick, augment total traction\n\t\t\t\t\tif (m_bsmaug) {\n\t\t\t\t\t\tdata.m_Lmt = tn[j];\n\t\t\t\t\t\tif (m_btwo_pass) data.m_Lmt /= 2;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdouble eps = m_epsn * data.m_epsn * psf;\n\t\t\t\t\t\tdata.m_Lmt += data.m_dg * eps;\n\t\t\t\t\t}\n\t\t\t\t\t// then derive normal component\n\t\t\t\t\tdata.m_Lmd = -data.m_Lmt * data.m_nu;\n\t\t\t\t\tLn = data.m_Lmd;\n\t\t\t\t\tnormL1 += data.m_Lmt * data.m_Lmt;\n\n\t\t\t\t\tif (m_btension)\n\t\t\t\t\t\tmaxgap = max(maxgap, data.m_dg.norm());\n\t\t\t\t\telse if (Ln > 0) maxgap = max(maxgap, data.m_dg.norm());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// if slip, augment normal traction\n\t\t\t\t\tif (m_bsmaug) {\n\t\t\t\t\t\tLn = -(tn[j] * data.m_nu);\n\t\t\t\t\t\tdata.m_Lmd = m_btension ? Ln : MBRACKET(Ln);\n\t\t\t\t\t\tif (m_btwo_pass) data.m_Lmd /= 2;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdouble eps = m_epsn * data.m_epsn * psf;\n\t\t\t\t\t\tLn = data.m_Lmd + eps * data.m_gap;\n\t\t\t\t\t\tdata.m_Lmd = m_btension ? Ln : MBRACKET(Ln);\n\t\t\t\t\t}\n\t\t\t\t\t// then derive total traction\n\t\t\t\t\tdata.m_Lmt = -(data.m_nu + data.m_s1 * m_mu) * data.m_Lmd;\n\t\t\t\t\tnormL1 += data.m_Lmd * data.m_Lmd;\n\n\t\t\t\t\tif (m_btension)\n\t\t\t\t\t\tmaxgap = max(maxgap, fabs(data.m_gap));\n\t\t\t\t\telse if (Ln > 0) maxgap = max(maxgap, fabs(data.m_gap));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n    for (int i=0; i<m_ms.Elements(); ++i) {\n        FESurfaceElement& el = m_ms.Element(i);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tvec3d tn[FEElement::MAX_INTPOINTS];\n\t\t\tif (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j) {\n\t\t\t\tFESlidingElasticSurface::Data& data = static_cast<FESlidingElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\t\t// update Lagrange multipliers on secondary surface\n\t\t\t\tif (data.m_bstick) {\n\t\t\t\t\t// if stick, augment total traction\n\t\t\t\t\tif (m_bsmaug) {\n\t\t\t\t\t\tdata.m_Lmt = tn[j];\n\t\t\t\t\t\tif (m_btwo_pass) data.m_Lmt /= 2;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdouble eps = m_epsn * data.m_epsn * psf;\n\t\t\t\t\t\tdata.m_Lmt += data.m_dg * eps;\n\t\t\t\t\t}\n\t\t\t\t\t// then derive normal component\n\t\t\t\t\tdata.m_Lmd = -data.m_Lmt * data.m_nu;\n\t\t\t\t\tLn = data.m_Lmd;\n\t\t\t\t\tnormL1 += data.m_Lmt * data.m_Lmt;\n\n\t\t\t\t\tif (m_btension)\n\t\t\t\t\t\tmaxgap = max(maxgap, fabs(data.m_dg.norm()));\n\t\t\t\t\telse if (Ln > 0) maxgap = max(maxgap, fabs(data.m_dg.norm()));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// if slip, augment normal traction\n\t\t\t\t\tif (m_bsmaug) {\n\t\t\t\t\t\tLn = -(tn[j] * data.m_nu);\n\t\t\t\t\t\tdata.m_Lmd = m_btension ? Ln : MBRACKET(Ln);\n\t\t\t\t\t\tif (m_btwo_pass) data.m_Lmd /= 2;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdouble eps = m_epsn * data.m_epsn * psf;\n\t\t\t\t\t\tLn = data.m_Lmd + eps * data.m_gap;\n\t\t\t\t\t\tdata.m_Lmd = m_btension ? Ln : MBRACKET(Ln);\n\t\t\t\t\t}\n\t\t\t\t\t// then derive total traction\n\t\t\t\t\tdata.m_Lmt = -(data.m_nu + data.m_s1 * m_mu) * data.m_Lmd;\n\t\t\t\t\tnormL1 += data.m_Lmd * data.m_Lmd;\n\n\t\t\t\t\tif (m_btension)\n\t\t\t\t\t\tmaxgap = max(maxgap, fabs(data.m_gap));\n\t\t\t\t\telse if (Ln > 0) maxgap = max(maxgap, fabs(data.m_gap));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n    }\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0));\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" sliding interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n    \n    feLog(\"    maximum gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n    \n    ProjectSurface(m_ss, m_ms, true);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss, true);\n    \n    m_bfreeze = true;\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingElasticInterface::Serialize(DumpStream &ar)\n{\n    // serialize contact data\n    FEContactInterface::Serialize(ar);\n\tar & m_bfreeze;\n    \n    // serialize contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n\n\t// restore pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n"
  },
  {
    "path": "FEBioMech/FESlidingElasticInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\n// Elastic sliding contact, reducing the algorithm of biphasic sliding contact\n// (FESlidingInterface2) to elastic case.  The algorithm derives from Bonet\n// & Wood's treatment of surface pressures\n\n//-----------------------------------------------------------------------------\nclass FESlidingElasticSurface : public FEContactSurface\n{\npublic:\n    // data for each integration point\n    class Data : public FEContactMaterialPoint\n    {\n    public:\n        Data();\n\n\t\tvoid Init() override;\n\n\t\tvoid Serialize(DumpStream& ar) override;\n        \n    public:\n        vec3d   m_dg;       //!< vector gap\n        double\tm_Lmd;\t\t//!< Lagrange multiplier for normal traction\n        double\tm_epsn;\t\t//!< penalty factor\n        vec3d   m_Lmt;      //!< Lagrange multipliers for vector traction\n        vec3d\tm_nu;\t\t//!< local normal\n        vec3d   m_s1;       //!< tangent along slip direction\n        vec3d   m_tr;       //!< contact traction\n        vec2d\tm_rs;\t\t//!< natural coordinates of this integration point\n        vec2d   m_rsp;      //!< m_rs at the previous time step\n        bool    m_bstick;   //!< stick flag\n    };\n    \npublic:\n    //! constructor\n    FESlidingElasticSurface(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! initialize sliding surface and store previous values\n    void InitSlidingSurface();\n    \n    //! evaluate net contact force\n    vec3d GetContactForce() override;\n    \n    //! evaluate net contact area\n    double GetContactArea() override;\n    \n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n    void GetVectorGap      (int nface, vec3d& pg) override;\n    void GetContactTraction(int nface, vec3d& pt) override;\n    void GetNodalVectorGap      (int nface, vec3d* pg) override;\n    void GetNodalContactPressure(int nface, double* pg) override;\n    void GetNodalContactTraction(int nface, vec3d* pt) override;\n    void GetStickStatus(int nface, double& pg) override;\n     \npublic:\n    vec3d    m_Ft;     //!< total contact force (from equivalent nodal forces)\n};\n\n//-----------------------------------------------------------------------------\nclass FESlidingElasticInterface : public FEContactInterface\n{\npublic:\n    //! constructor\n    FESlidingElasticInterface(FEModel* pfem);\n    \n    //! destructor\n    ~FESlidingElasticInterface();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! interface activation\n    void Activate() override;\n    \n    //! calculate the slip direction on the primary surface\n    vec3d SlipTangent(FESlidingElasticSurface& ss, const int nel, const int nint, FESlidingElasticSurface& ms, double& dh, vec3d& r);\n    \n    //! calculate contact traction\n    vec3d ContactTraction(FESlidingElasticSurface& ss, const int nel, const int n, FESlidingElasticSurface& ms, double& pn);\n   \n    //! calculate contact pressures for file output\n    void UpdateContactPressures();\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n    //! return integration rule class\n    bool UseNodalIntegration() override { return false; }\n    \n    //! build the matrix profile for use in the stiffness matrix\n    void BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n    void ProjectSurface(FESlidingElasticSurface& ss, FESlidingElasticSurface& ms, bool bupseg, bool bmove = false);\n    \n    //! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n    void CalcAutoPenalty(FESlidingElasticSurface& s);\n\npublic:\n    FESlidingElasticSurface\tm_ss;\t//!< primary surface\n\tFESlidingElasticSurface\tm_ms;\t//!< secondary surface\n\n    int\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n    bool\t\t\tm_btwo_pass;\t//!< two-pass flag\n    double\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n    double\t\t\tm_gtol;\t\t\t//!< normal gap tolerance\n    double\t\t\tm_stol;\t\t\t//!< search tolerance\n    bool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n    double\t\t\tm_srad;\t\t\t//!< contact search radius\n    int\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n    int\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n    int\t\t\t\tm_nsegup;\t\t//!< segment update parameter\n    bool\t\t\tm_breloc;\t\t//!< node relocation on activation\n    bool            m_bsmaug;       //!< smooth augmentation\n    \n    double\t\t\tm_epsn;\t\t\t//!< normal penalty factor\n    bool\t\t\tm_bautopen;\t\t//!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n\n    bool\t\t\tm_btension;\t\t//!< allow tension across interface\n    \n    double          m_mu;           //!< friction coefficient\n    \n    bool            m_bfreeze;      //!< freeze stick/slip status\n\tbool            m_bflips;       //!< flip primary surface normal\n\tbool            m_bflipm;       //!< flip secondary surface normal\n\tbool            m_bshellbs;     //!< flag for prescribing pressure on shell bottom for primary surface\n\tbool            m_bshellbm;     //!< flag for prescribing pressure on shell bottom for secondary surface\n\n    double          m_offset;       //!< allow an offset that separates the contact surfaces\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESlidingInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingInterface.h\"\n#include <FECore/FEShellDomain.h>\n#include \"FECore/FEClosestPointProjection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include \"FECore/log.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEAnalysis.h>\n\nFESlidingSurface::FESlidingPoint::FESlidingPoint()\n{\n\tm_gap = 0.0;\n\tm_nu = vec3d(0,0,0);\n\tm_rs = vec2d(0,0);\n\tm_rsp = vec2d(0,0);\n\tm_Lm = 0.0;\n\tm_M.zero();\n\tm_Lt = vec2d(0,0);\n\tm_off = 0.0;\n\tm_eps = 1.0;\n\tm_Ln = 0.0;\n}\n\nvoid FESlidingSurface::FESlidingPoint::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\n\tar & m_nu & m_eps & m_off;\n\tar & m_rs & m_rsp;\n\tar & m_Lm & m_Lt;\n\tar & m_M;\n}\n\nvoid FESlidingSurface::FESlidingPoint::Init()\n{\n\tFEContactMaterialPoint::Init();\n\tm_gap = 0.0;\n\tm_nu = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n\tm_rsp = vec2d(0, 0);\n\tm_Lm = 0.0;\n\tm_M.zero();\n\tm_Lt = vec2d(0, 0);\n\tm_off = 0.0;\n\tm_eps = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon       , \"laugon\"       )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol         , \"tolerance\"    );\n\tADD_PARAMETER(m_eps          , \"penalty\"      );\n\tADD_PARAMETER(m_bautopen     , \"auto_penalty\" );\n\tADD_PARAMETER(m_btwo_pass    , \"two_pass\"     );\n\tADD_PARAMETER(m_gtol         , \"gaptol\"       );\n\tADD_PARAMETER(m_mu           , \"fric_coeff\"   );\n\tADD_PARAMETER(m_epsf         , \"fric_penalty\" );\n\tADD_PARAMETER(m_naugmin      , \"minaug\"       );\n\tADD_PARAMETER(m_naugmax      , \"maxaug\"       );\n\tADD_PARAMETER(m_stol         , \"search_tol\"   );\n\tADD_PARAMETER(m_ktmult       , \"ktmult\"       );\n\tADD_PARAMETER(m_knmult       , \"knmult\"       );\n\tADD_PARAMETER(m_breloc       , \"node_reloc\"   );\n\tADD_PARAMETER(m_nsegup       , \"seg_up\"       );\n\tADD_PARAMETER(m_sradius      , \"search_radius\");\n\tADD_PARAMETER(m_bupdtpen     , \"update_penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESlidingSurface::FESlidingSurface(FEModel* pfem) : FEContactSurface(pfem) {}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FESlidingInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\t// TODO: this is currently for max 6 nodes (hence 7=6+1)\n\tvector<int> lm(6*7);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface& ss = (np==0? m_ss : m_ms);\n\t\tFESlidingSurface& ms = (np==0? m_ms : m_ss);\n\n\t\tfor (int j=0; j<ss.Nodes(); ++j)\n\t\t{\n\t\t\tFESurfaceElement* pe = ss.m_data[j].m_pme;\n\n\t\t\tif (pe != 0)\n\t\t\t{\n\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\tint* en = &me.m_node[0];\n\n\t\t\t\t// Note that we need to grab the rigid degrees of freedom as well\n\t\t\t\t// this is in case one of the nodes belongs to a rigid body.\n\t\t\t\tint n = me.Nodes();\n\t\t\t\tif (n == 3)\n\t\t\t\t{\n\t\t\t\t\tlm[6*(3+1)  ] = -1;lm[6*(3+2)  ] = -1;lm[6*(3+3)  ] = -1;\n\t\t\t\t\tlm[6*(3+1)+1] = -1;lm[6*(3+2)+1] = -1;lm[6*(3+3)+1] = -1;\n\t\t\t\t\tlm[6*(3+1)+2] = -1;lm[6*(3+2)+2] = -1;lm[6*(3+3)+2] = -1;\n\t\t\t\t\tlm[6*(3+1)+3] = -1;lm[6*(3+2)+3] = -1;lm[6*(3+3)+3] = -1;\n\t\t\t\t\tlm[6*(3+1)+4] = -1;lm[6*(3+2)+4] = -1;lm[6*(3+3)+4] = -1;\n\t\t\t\t\tlm[6*(3+1)+5] = -1;lm[6*(3+2)+5] = -1;lm[6*(3+3)+5] = -1;\n\t\t\t\t}\n\t\t\t\tif (n == 4)\n\t\t\t\t{\n\t\t\t\t\tlm[6*(4+1)  ] = -1;lm[6*(4+2)  ] = -1;\n\t\t\t\t\tlm[6*(4+1)+1] = -1;lm[6*(4+2)+1] = -1;\n\t\t\t\t\tlm[6*(4+1)+2] = -1;lm[6*(4+2)+2] = -1;\n\t\t\t\t\tlm[6*(4+1)+3] = -1;lm[6*(4+2)+3] = -1;\n\t\t\t\t\tlm[6*(4+1)+4] = -1;lm[6*(4+2)+4] = -1;\n\t\t\t\t\tlm[6*(4+1)+5] = -1;lm[6*(4+2)+5] = -1;\n\t\t\t\t}\n\n\t\t\t\tlm[0] = ss.Node(j).m_ID[dof_X];\n\t\t\t\tlm[1] = ss.Node(j).m_ID[dof_Y];\n\t\t\t\tlm[2] = ss.Node(j).m_ID[dof_Z];\n\t\t\t\tlm[3] = ss.Node(j).m_ID[dof_RU];\n\t\t\t\tlm[4] = ss.Node(j).m_ID[dof_RV];\n\t\t\t\tlm[5] = ss.Node(j).m_ID[dof_RW];\n\n\t\t\t\tfor (int k=0; k<n; ++k)\n\t\t\t\t{\n\t\t\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\t\t\tlm[6*(k+1)  ] = id[dof_X];\n\t\t\t\t\tlm[6*(k+1)+1] = id[dof_Y];\n\t\t\t\t\tlm[6*(k+1)+2] = id[dof_Z];\n\t\t\t\t\tlm[6*(k+1)+3] = id[dof_RU];\n\t\t\t\t\tlm[6*(k+1)+4] = id[dof_RV];\n\t\t\t\t\tlm[6*(k+1)+5] = id[dof_RW];\n\t\t\t\t}\n\n\t\t\t\tK.build_add(lm);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Creates a surface for use with a sliding interface. All surface data\n//! structures are allocated.\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FESlidingSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEContactSurface::Init() == false) return false;\n\n\t// make sure the sibling surface has been set\n\tassert(m_pSibling);\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate integration point data\n\tm_data.resize(nn);\n\tfor (int i = 0; i < nn; ++i)\n\t{\n\t\tFESlidingPoint& d = m_data[i];\n\t\td.Init();\n\t}\n\n\t// we calculate the gap offset values\n\t// This value is used to take the shell thickness into account\n\t// note that we force rigid shells to have zero thickness\n\tFEMesh& m = *m_pMesh;\n\tvector<double> tag(m.Nodes());\n\tzero(tag);\n\tfor (int nd=0; nd<m.Domains(); ++nd)\n\t{\n\t\tFEShellDomain* psd = dynamic_cast<FEShellDomain*>(&m.Domain(nd));\n\t\tif (psd)\n\t\t{\n\t\t\tfor (int i=0; i<psd->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEShellElement& el = psd->Element(i);\n\t\t\t\tint n = el.Nodes();\n\t\t\t\tfor (int j=0; j<n; ++j) tag[el.m_node[j]] = 0.5*el.m_h0[j];\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i=0; i<nn; ++i) m_data[i].m_off = tag[NodeIndex(i)];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! \nvec3d FESlidingSurface::traction(int inode)\n{\n\tvec3d t(0,0,0);\n\tif (m_data[inode].m_pme)\n\t{\n\t\tFESurfaceElement& el = *m_data[inode].m_pme;\n\t\tdouble Tn = m_data[inode].m_Lm;\n\t\tdouble T1 = -m_data[inode].m_Lt[0];\n\t\tdouble T2 = -m_data[inode].m_Lt[1];\n\t\tdouble r = m_data[inode].m_rs[0];\n\t\tdouble s = m_data[inode].m_rs[1];\n        \n\t\tvec3d tn = m_data[inode].m_nu*Tn, tt;\n\t\tvec3d e[2];\n\t\tContraBaseVectors(el, r, s, e);\n\t\ttt = e[0]*T1 + e[1]*T2;\n\t\tt = tn + tt;\n\t}\n    \n\treturn t;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface::GetContactForce()\n{\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble Tn[MN],T1[MN],T2[MN];\n\t\n\t// initialize contact force\n\tvec3d f(0,0,0);\n\t\n\t// loop over all elements of the surface\n\tfor (int n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nseln = el.Nodes();\n\t\t\n\t\t// nodal contact pressures and frictional tractions\n\t\tfor (int i=0; i<nseln; ++i) {\n            Tn[i] =  m_data[el.m_lnode[i]].m_Ln;\n            T1[i] = -m_data[el.m_lnode[i]].m_Lt[0];\n            T2[i] = -m_data[el.m_lnode[i]].m_Lt[1];\n        }\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// evaluate the contact force for that element\n\t\tfor (int i=0; i<nint; ++i)\n\t\t{\n\t\t\t// area in reference configuration\n\t\t\tvec3d g0[2],g[2];\n\t\t\tdouble r = el.gr(i);\n\t\t\tdouble s = el.gs(i);\n\t\t\tCoBaseVectors0(el, r, s, g0);\n\t\t\tdouble A = (g0[0] ^ g0[1]).unit();\n\t\t\t// traction components at integration point\n            double t1 = el.eval(T1,i);\n            double t2 = el.eval(T2,i);\n\t\t\tdouble t3 = el.eval(Tn,i);\n\t\t\t// unit normal vector\n\t\t\tvec3d n = SurfaceNormal(el, i);\n            // contravariant basis in spatial frame\n            ContraBaseVectors(el, r, s, g);\n            // Piola traction\n            vec3d t = g[0]*t1 + g[1]*t2 + n*t3;\n\t\t\t// gauss weight\n\t\t\tdouble w = el.GaussWeights()[i];\n\t\t\t// contact force\n\t\t\tf += t*(w*A);\n\t\t}\n\t}\n\t\n\treturn f;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurface::GetContactArea()\n{\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble Tn[MN];\n    \n\t// initialize contact area\n\tdouble a = 0;\n\t\n\t// loop over all elements of the primary surface\n\tfor (int n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\tint nseln = el.Nodes();\n\t\t\n\t\t// nodal contact pressures\n\t\tfor (int i=0; i<nseln; ++i) {\n            Tn[i] = m_data[el.m_lnode[i]].m_Ln;\n        }\n        \n\t\t// evaluate the contact force for that element\n\t\tfor (int i=0; i<nint; ++i)\n\t\t{\n\t\t\t// get data for this integration point\n\t\t\tdouble Ln = el.eval(Tn,i);\n            double s = (Ln > 0) ? 1 : 0;\n            \n\t\t\t// get the base vectors\n\t\t\tvec3d g[2];\n\t\t\tCoBaseVectors(el, i, g);\n            \n\t\t\t// normal (magnitude = area)\n\t\t\tvec3d n = g[0] ^ g[1];\n            \n\t\t\t// gauss weight\n\t\t\tdouble w = el.GaussWeights()[i];\n            \n\t\t\t// contact force\n\t\t\ta += n.norm()*(w*s);\n\t\t}\n\t}\n\t\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface::Serialize(DumpStream& ar)\n{\n\t// serialize base class\n\tFEContactSurface::Serialize(ar);\n\n\t// serialize data\n\tar & m_data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ne = el.Nodes();\n    pt = vec3d(0,0,0);\n    for (int k=0; k<ne; ++k)\n    {\n        int nj = el.m_lnode[k];\n        if (m_data[nj].m_gap > 0) pt += m_data[nj].m_nu*m_data[nj].m_Ln;\n    }\n    pt /= ne;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface::GetNodalContactPressure(int nface, double* pg)\n{\n\tFESurfaceElement& f = Element(nface);\n\tint ne = f.Nodes();\n\tfor (int j=0; j<ne; ++j) pg[j] = m_data[f.m_lnode[j]].m_Ln;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& e = Element(nface);\n\tint ne = e.Nodes();\n\tfor (int j=0; j<ne; ++j)\n\t{\n\t\tint nj = e.m_lnode[j];\n\t\tdouble gi = m_data[nj].m_gap;\n\t\tdouble Li = m_data[nj].m_Ln;\n\t\tvec3d ti  = m_data[nj].m_nu;\n\t\tif (gi > 0) tn[j] = ti*Li; else tn[j] = vec3d(0,0,0);\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// FESlidingInterface\n///////////////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESlidingInterface::FESlidingInterface(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_mu = 0;\n\tm_epsf = 0;\n\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n\tm_gtol = 0;\n\n\tm_stol = 0.01;\n\n\tm_ktmult = 0;\n\tm_knmult = 1;\n\n\tm_breloc = false;\n\n\tm_bupdtpen = false;\n\n\tm_nsegup = 0;\t// always do segment updates\n\tm_bautopen = false;\t// don't use auto-penalty\n\tm_btwo_pass = false; // don't use two-pass\n\tm_sradius = 0;\t\t\t\t// no search radius limitation\n\n\t// set parents\n\tm_ms.SetContactInterface(this);\n\tm_ss.SetContactInterface(this);\n\n\t// set the siblings\n\tm_ms.SetSibling(&m_ss);\n\tm_ss.SetSibling(&m_ms);\n};\n\n//-----------------------------------------------------------------------------\n//! Calculates the auto penalty factor\n\nvoid FESlidingInterface::CalcAutoPenalty(FESlidingSurface& s)\n{\n\t// zero penalty values\n\tfor (int i=0; i<s.Nodes(); ++i) s.m_data[i].m_eps = 0.0;\n\n\t// get the mesh\n\tFEMesh& mesh = *s.GetMesh();\n\n\t// get the node element list for this surface\n\tFENodeElemList NEL;\n\tNEL.Create(s);\n\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the next face\n\t\tFESurfaceElement& face = s.Element(i);\n\n\t\t// we need a measure for the modulus\n\t\tdouble eps = AutoPenalty(face, s);\n\n\t\t// distribute values over nodes\n\t\tfor (int k=0; k<face.Nodes(); ++k)\n\t\t{\n\t\t\tint m = face.m_lnode[k];\n\t\t\ts.m_data[m].m_eps += eps;\n\t\t}\n\t}\n\n\t// scale values according to valence (TODO: Why are we doing this?)\n\tfor (int i=0; i<s.Nodes(); ++i) s.m_data[i].m_eps /= NEL.Valence(i);\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes the sliding interface data\n\nbool FESlidingInterface::Init()\n{\n\t// set data\n\tm_bfirst = true;\n\tm_normg0 = 0.0;\n\n\t// create the surfaces\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms, true, m_breloc);\n\tif (m_bautopen) CalcAutoPenalty(m_ss);\n\n\t// for two-pass algorithms we repeat the previous\n\t// two steps with primary and secondary surface switched\n\tif (m_btwo_pass)\n\t{\n\t\tProjectSurface(m_ms, m_ss, true);\n\t\tif (m_bautopen) CalcAutoPenalty(m_ms);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//!  Projects the primary surface onto the secondary surface.\n//!  That is, for each primary surface node we determine the closest\n//!  secondary surface element and the projection of that node onto\n//!  this element.\n\n//! \\todo this function needs to identify the different types of contact:\n//!   1/ first contact\n//!   2/ crossing of element boundary\n//!\t  3/ contact termination \n//!\t\t\teither by failure to find projection or when g < tolerance\n\nvoid FESlidingInterface::ProjectSurface(FESlidingSurface& ss, FESlidingSurface& ms, bool bupseg, bool bmove)\n{\n\t// node projection data\n\tdouble r, s;\n\tvec3d q;\n\n\tFEClosestPointProjection cpp(ms);\n\tcpp.SetTolerance(m_stol);\n\tcpp.SetSearchRadius(m_sradius);\n\tcpp.HandleSpecialCases(true);\n\tcpp.Init();\n\n\t// loop over all primary surface nodes\n\tfor (int i=0; i<ss.Nodes(); ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = ss.Node(i);\n\n\t\t// get the nodal position\n\t\tvec3d x = node.m_rt;\n\n\t\t// get the global node number\n\t\tint m = ss.NodeIndex(i);\n\n\t\t// get the previous secondary surface element (if any)\n\t\tFESurfaceElement* pme = ss.m_data[i].m_pme;\n\n\t\t// If the node is in contact, let's see if the node still is \n\t\t// on the same element\n\t\tif (pme != 0)\n\t\t{\n\t\t\tFESurfaceElement& mel = *pme;\n\n\t\t\tr = ss.m_data[i].m_rs[0];\n\t\t\ts = ss.m_data[i].m_rs[1];\n\n\t\t\tq = ms.ProjectToSurface(mel, x, r, s);\n\t\t\tss.m_data[i].m_rs[0] = r;\n\t\t\tss.m_data[i].m_rs[1] = s;\n\n\t\t\t// we only check when we can update the segments\n\t\t\t// otherwise, we just stick with this element, even\n\t\t\t// if the node is no longer inside it.\n\t\t\tif (bupseg)\n\t\t\t{\n\t\t\t\tif (!ms.IsInsideElement(mel, r, s, m_stol))\n\t\t\t\t{\n\t\t\t\t\t// see if the node might have moved to another element\n\t\t\t\t\tFESurfaceElement* pold = pme; \n\t\t\t\t\tss.m_data[i].m_rs = vec2d(0,0);\n\n\t\t\t\t\tpme = cpp.Project(m, q, ss.m_data[i].m_rs);\n\n\t\t\t\t\tif (pme == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t// nope, it has genuinly left contact\n\t\t\t\t\t\tint* n = &pold->m_node[0];\n//\t\t\t\t\t\tfeLog(\"node %d has left element (%d, %d, %d, %d)\\n\", m+1, n[0]+1, n[1]+1, n[2]+1, n[3]+1);\n\t\t\t\t\t}\n\t\t\t\t\telse \n\t\t\t\t\t{\n/*\t\t\t\t\t\tif (pme != pold)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfeLog(\"node %d has switched segments: \", m + 1);\n\t\t\t\t\t\t\tint* n = &pold->m_node[0];\n\t\t\t\t\t\t\tfeLog(\"from (%d, %d, %d, %d), \", n[0] + 1, n[1] + 1, n[2] + 1, n[3] + 1);\n\t\t\t\t\t\t\tn = &pme->m_node[0];\n\t\t\t\t\t\t\tfeLog(\"to (%d, %d, %d, %d)\\n\", n[0] + 1, n[1] + 1, n[2] + 1, n[3] + 1);\n\t\t\t\t\t\t}\n*/\n\t\t\t\t\t\tif (m_mu*m_epsf > 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// the node has moved to another segment.\n\t\t\t\t\t\t\t// If friction is active we need to translate the frictional\n\t\t\t\t\t\t\t// data to the new segment.\n\t\t\t\t\t\t\tFESurfaceElement& eo = *pold;\n\t\t\t\t\t\t\tFESurfaceElement& en = *pme;\n\t\t\t\t\t\t\tMapFrictionData(i, ss, ms, en, eo, q);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (bupseg)\n\t\t{\n\t\t\t// get the secondary surface element\n\t\t\t// don't forget to initialize the search for the first node!\n\t\t\tss.m_data[i].m_rs = vec2d(0,0);\n\t\t\tpme = cpp.Project(m, q, ss.m_data[i].m_rs);\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// the node has come into contact so make sure to initialize\n\t\t\t\t// the previous natural coordinates for friction.\n\t\t\t\tss.m_data[i].m_rsp = ss.m_data[i].m_rs;\n\t\t\t}\n\t\t}\n\n\t\t// if we found a secondary surface element, update the gap and normal data\n\t\tss.m_data[i].m_pme = pme;\n\t\tif (pme != 0)\n\t\t{\n\t\t\tFESurfaceElement& mel =  *ss.m_data[i].m_pme;\n\n\t\t\tr = ss.m_data[i].m_rs[0];\n\t\t\ts = ss.m_data[i].m_rs[1];\n\n\t\t\t// if this is a new contact, copy the current coordinates\n\t\t\t// to the previous ones\n\t\t\tss.m_data[i].m_M = ss.Metric0(mel, r, s);\n\n\t\t\t// the normal is set to the secondary surface element normal\n\t\t\tss.m_data[i].m_nu = ss.SurfaceNormal(mel, r, s);\n\n\t\t\t// calculate gap\n\t\t\tss.m_data[i].m_gap = -(ss.m_data[i].m_nu*(x - q)) + ss.m_data[i].m_off;\n\t\t\tif (bmove && (ss.m_data[i].m_gap>0))\n\t\t\t{\n\t\t\t\tnode.m_r0 = node.m_rt = q + ss.m_data[i].m_nu*ss.m_data[i].m_off;\n\t\t\t\tss.m_data[i].m_gap = 0;\n\t\t\t}\n\n\t\t\t// TODO: what should we do if the gap function becomes\n\t\t\t// negative? setting the Lagrange multipliers to zero\n\t\t\t// might make the system unstable.\n/*\t\t\tif (ss.gap[i] < 0)\n\t\t\t{\n\t\t\t\tss.Lm[i] = 0;\n\t\t\t\tss.Lt[i][0] = 0;\n\t\t\t\tss.Lt[i][1] = 0;\n\t\t\t\tss.pme[i] = 0;\n\t\t\t}\n*/\t\t}\n\t\telse\n\t\t{\n\t\t\t// TODO: Is this a good criteria for out-of-contact?\n\t\t\t//\t\t perhaps this is not even necessary.\n\t\t\t// since the node is not in contact, we set the gap function \n\t\t\t// and Lagrangian multiplier to zero\n\t\t\tss.m_data[i].m_gap = 0;\n\t\t\tss.m_data[i].m_Lm  = 0;\n\t\t\tss.m_data[i].m_Lt[0] = ss.m_data[i].m_Lt[1] = 0;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! updates sliding interface data\n//! niter is the number of Newton iterations.\nvoid FESlidingInterface::Update()\n{\n\tint niter = GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter;\n\n\t// should we do a segment update or not?\n\t// TODO: check what happens when m_nsegup == -1 and m_npass = 2;\n\t// We have to make sure that in this case, both surfaces get at least\n\t// one pass!\n\tbool bupdate = (m_bfirst || (m_nsegup == 0)? true : (niter <= m_nsegup));\n\n\t// project primary surface onto secondary surface\n\t// this also calculates the nodal gap functions\n\tProjectSurface(m_ss, m_ms, bupdate);\n\tif (m_btwo_pass) ProjectSurface(m_ms, m_ss, bupdate);\n\n\t// Update the net contact pressures\n\tUpdateContactPressures();\n\n\t// set the first-entry-flag to false\n\tm_bfirst = false;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\t// the elements LM vectors\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d r0[MN];\n\tdouble w[MN];\n\tdouble* Gr, *Gs;\n\tdouble detJ[MN];\n\tvec3d dxr, dxs;\n\n\t// do two-pass\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\t// pick the primary and secondary surfaces\n\t\tFESlidingSurface& ss = (np==0? m_ss : m_ms);\n\t\tFESlidingSurface& ms = (np==0? m_ms : m_ss);\n\n\t\t// loop over all primary surface facets\n\t\tint ne = ss.Elements();\n\t\tfor (int j=0; j<ne; ++j)\n\t\t{\n\t\t\t// get the next element\n\t\t\tFESurfaceElement& sel = ss.Element(j);\n\t\t\tint nseln = sel.Nodes();\n\n\t\t\t// get the element's LM array\n\t\t\tss.UnpackLM(sel, sLM);\n\n\t\t\t// nodal coordinates\n\t\t\tfor (int i=0; i<nseln; ++i) r0[i] = ss.GetMesh()->Node(sel.m_node[i]).m_r0;\n\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (int n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = sel.Gr(n);\n\t\t\t\tGs = sel.Gs(n);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\t// note that we are integrating over the reference surface\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\t// jacobians\n\t\t\t\tdetJ[n] = (dxr ^ dxs).norm();\n\n\t\t\t\t// integration weights\n\t\t\t\tw[n] = sel.GaussWeights()[n];\n\t\t\t}\n\n\t\t\t// loop over primary surface element nodes (which are the integration points as well)\n\t\t\t// and calculate the contact nodal force\n\t\t\tfor (int n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\t// get the local node number\n\t\t\t\tint m = sel.m_lnode[n];\n\n\t\t\t\t// see if this node's constraint is active\n\t\t\t\t// that is, if it has an element associated with it\n\t\t\t\t// TODO: is this a good way to test for an active constraint\n\t\t\t\t// The rigid wall criteria seems to work much better.\n\t\t\t\tif (ss.m_data[m].m_pme != 0)\n\t\t\t\t{\n\t\t\t\t\t// This node is active and could lead to a non-zero\n\t\t\t\t\t// contact force.\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement& mel = *ss.m_data[m].m_pme;\n\t\t\t\t\tms.UnpackLM(mel, mLM);\n\n\t\t\t\t\t// calculate the degrees of freedom\n\t\t\t\t\tint nmeln = mel.Nodes();\n\t\t\t\t\tint ndof = 3*(nmeln+1);\n\t\t\t\t\tfe.resize(ndof);\n\n\t\t\t\t\t// calculate the nodal force\n\t\t\t\t\tContactNodalForce(m, ss, mel, fe);\n\n\t\t\t\t\t// multiply force with weights\n\t\t\t\t\tfor (int l=0; l<ndof; ++l) fe[l] *= detJ[n]*w[n];\n\t\t\t\t\t\n\t\t\t\t\t// fill the lm array\n\t\t\t\t\tlm.resize(3*(nmeln+1));\n\t\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tlm[3*(l+1)  ] = mLM[l*3  ];\n\t\t\t\t\t\tlm[3*(l+1)+1] = mLM[l*3+1];\n\t\t\t\t\t\tlm[3*(l+1)+2] = mLM[l*3+2];\n\t\t\t\t\t}\n\n\t\t\t\t\t// fill the en array\n\t\t\t\t\ten.resize(nmeln+1);\n\t\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\t\tfor (int l=0; l<nmeln; ++l) en[l+1] = mel.m_node[l];\n\n\t\t\t\t\t// assemble into global force vector\n\t\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact force on a node.\n//! \\param[in] m local node number\n//! \\param[out] fe force vector\n\nvoid FESlidingInterface::ContactNodalForce(int m, FESlidingSurface& ss, FESurfaceElement& mel, vector<double>& fe)\n{\n\tvec3d dxr, dxs;\n\n\t// normal force\n\tdouble tn, Ln;\n\n\t// gap function\n\tdouble gap;\n\n\t// tangents\n\tvec3d tau1, tau2;\n\n\t// max nr of element nodes\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\t// secondary surface element nodes\n\tvec3d rtm[MAXMN];\n\n\t// shape function values\n\tdouble H[MAXMN], Hr[MAXMN], Hs[MAXMN];\n\n\t// contact vectors\n\tdouble N[3*(MAXMN+1)], N1[3*(MAXMN+1)], N2[3*(MAXMN+1)];\n\tdouble T1[3*(MAXMN+1)], T2[3*(MAXMN+1)], D1[3*(MAXMN+1)], D2[3*(MAXMN+1)];\n\n\t// surface metrics\n\tdouble A[2][2], M[2][2], K[2][2];\n\tdouble detA;\n\n\tdouble eps, scale = Penalty();\n\n\t// get the mesh\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tdouble Tt[2];\n\n\tint nmeln, ndof;\n\n\t// gap function\n\tgap = ss.m_data[m].m_gap;\n\n\t// normal penalty\n\teps = ss.m_data[m].m_eps*scale;\n\n\t// get primary surface node normal force\n\tLn = ss.m_data[m].m_Lm;\n\ttn = Ln + eps*gap;\n\ttn = MBRACKET(tn);\n\n\t// get the primary surface  node normal\n\tvec3d& nu = ss.m_data[m].m_nu;\n\n\tnmeln = mel.Nodes();\n\tndof = 3*(1 + nmeln);\n\n\t// metric tensors\n\tmat2d Mk = ss.m_data[m].m_M;\n\tmat2d Mki = Mk.inverse();\n\n\t// get the secondary surface element node positions\n\tfor (int k=0; k<nmeln; ++k) rtm[k] = mesh.Node(mel.m_node[k]).m_rt;\n\n\t// isoparametric coordinates of the projected node\n\t// onto the secondary surface element\n\tdouble r = ss.m_data[m].m_rs[0];\n\tdouble s = ss.m_data[m].m_rs[1];\n\n\t// get the coordinates at the previous step\n\tdouble rp = ss.m_data[m].m_rsp[0];\n\tdouble sp = ss.m_data[m].m_rsp[1];\n\n\t// get the secondary surface element shape function values at this node\n\tmel.shape_fnc(H, r, s);\n\n\t// --- N O R M A L   T R A C T I O N ---\n\n\t// calculate contact vectors for normal traction\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\tfor (int l=0; l<nmeln; ++l)\n\t{\n\t\tN[3*(l+1)  ] = -H[l]*nu.x;\n\t\tN[3*(l+1)+1] = -H[l]*nu.y;\n\t\tN[3*(l+1)+2] = -H[l]*nu.z;\n\t}\n\n\t// calculate force vector\n\tfor (int l=0; l<ndof; ++l) fe[l] = tn*N[l];\n\n\t// --- T A N G E N T I A L   T R A C T I O N ---\n\tif (m_mu*m_epsf > 0)\n\t{\n\t\t// Lagrangian traction\n\t\tdouble Lt[2];\n\t\tLt[0] = ss.m_data[m].m_Lt[0];\n\t\tLt[1] = ss.m_data[m].m_Lt[1];\n\n\t\t// calculate contact vector for tangential traction\n\t\t// only if both the friction coefficient and friction\n\t\t// penalty factor are non-zero\n\n\t\t// get the secondary surface shape function derivative values at this node\n\t\tmel.shape_deriv(Hr, Hs, r, s);\n\n\t\t// get the tangent vectors\n\t\ttau1 = tau2 = vec3d(0,0,0);\n\t\tfor (int k=0; k<nmeln; ++k)\n\t\t{\n\t\t\ttau1.x += Hr[k]*rtm[k].x;\n\t\t\ttau1.y += Hr[k]*rtm[k].y;\n\t\t\ttau1.z += Hr[k]*rtm[k].z;\n\t\t\n\t\t\ttau2.x += Hs[k]*rtm[k].x;\n\t\t\ttau2.y += Hs[k]*rtm[k].y;\n\t\t\ttau2.z += Hs[k]*rtm[k].z;\n\t\t}\n\n\t\t// set up the Ti vectors\n\t\tT1[0] = tau1.x; T2[0] = tau2.x;\n\t\tT1[1] = tau1.y; T2[1] = tau2.y;\n\t\tT1[2] = tau1.z; T2[2] = tau2.z;\n\n\t\tfor (int k=0; k<nmeln; ++k)\n\t\t{\n\t\t\tT1[(k+1)*3  ] = -H[k]*tau1.x;\n\t\t\tT1[(k+1)*3+1] = -H[k]*tau1.y;\n\t\t\tT1[(k+1)*3+2] = -H[k]*tau1.z;\n\n\t\t\tT2[(k+1)*3  ] = -H[k]*tau2.x;\n\t\t\tT2[(k+1)*3+1] = -H[k]*tau2.y;\n\t\t\tT2[(k+1)*3+2] = -H[k]*tau2.z;\n\t\t}\n\n\t\t// set up the Ni vectors\n\t\tN1[0] = N2[0] = 0;\n\t\tN1[1] = N2[1] = 0;\n\t\tN1[2] = N2[2] = 0;\n\n\t\tfor (int k=0; k<nmeln; ++k)\n\t\t{\n\t\t\tN1[(k+1)*3  ] = -Hr[k]*nu.x;\n\t\t\tN1[(k+1)*3+1] = -Hr[k]*nu.y;\n\t\t\tN1[(k+1)*3+2] = -Hr[k]*nu.z;\n\n\t\t\tN2[(k+1)*3  ] = -Hs[k]*nu.x;\n\t\t\tN2[(k+1)*3+1] = -Hs[k]*nu.y;\n\t\t\tN2[(k+1)*3+2] = -Hs[k]*nu.z;\n\t\t}\n\n\t\t// calculate metric tensor\n\t\tM[0][0] = tau1*tau1; M[0][1] = tau1*tau2; \n\t\tM[1][0] = tau2*tau1; M[1][1] = tau2*tau2; \n\n\t\t// calculate curvature tensor\n\t\tK[0][0] = 0; K[0][1] = 0;\n\t\tK[1][0] = 0; K[1][1] = 0;\n\n\t\tdouble Grr[FEElement::MAX_NODES];\n\t\tdouble Grs[FEElement::MAX_NODES];\n\t\tdouble Gss[FEElement::MAX_NODES];\n\t\tmel.shape_deriv2(Grr, Grs, Gss, r, s);\n\t\tfor (int k=0; k<nmeln; ++k)\n\t\t{\n\t\t\tK[0][0] += (nu*rtm[k])*Grr[k];\n\t\t\tK[0][1] += (nu*rtm[k])*Grs[k];\n\t\t\tK[1][0] += (nu*rtm[k])*Grs[k];\n\t\t\tK[1][1] += (nu*rtm[k])*Gss[k];\n\t\t}\n\n\t\t// setup A matrix\n\t\tA[0][0] = M[0][0] + gap*K[0][0];\n\t\tA[0][1] = M[0][1] + gap*K[0][1];\n\t\tA[1][0] = M[1][0] + gap*K[1][0];\n\t\tA[1][1] = M[1][1] + gap*K[1][1];\n\n\t\tdetA = A[0][0]*A[1][1] - A[0][1]*A[1][0];\n\n\t\t// setup Di vectors\n\t\tfor (int k=0; k<ndof; ++k)\n\t\t{\n\t\t\tD1[k] = (1/detA)*(A[1][1]*(T1[k]+gap*N1[k]) - A[0][1]*(T2[k] + gap*N2[k]));\n\t\t\tD2[k] = (1/detA)*(A[0][0]*(T2[k]+gap*N2[k]) - A[0][1]*(T1[k] + gap*N1[k]));\n\t\t}\n\n\t\t// calculate friction tractions\n\t\t// a. calculate trial state\n\t\tTt[0] = Lt[0] + m_epsf*(Mk[0][0]*(r - rp) + Mk[0][1]*(s - sp));\n\t\tTt[1] = Lt[1] + m_epsf*(Mk[1][0]*(r - rp) + Mk[1][1]*(s - sp));\n\n\t\tdouble TMT = Tt[0]*(Mki[0][0]*Tt[0]+Mki[0][1]*Tt[1])+Tt[1]*(Mki[1][0]*Tt[0]+Mki[1][1]*Tt[1]);\n\t\tassert(TMT >= 0);\n\n\t\tdouble phi = sqrt(TMT) - m_mu* tn;\n\n\t\t// b. return map\n\t\tif (phi > 0)\n\t\t{\n\t\t\tTt[0] = m_mu* tn *Tt[0]/sqrt(TMT);\n\t\t\tTt[1] = m_mu* tn *Tt[1]/sqrt(TMT);\n\t\t}\n\n\t\t// tangential force vector\n\t\tfor (int l=0; l<ndof; ++l) fe[l] -= (Tt[0]*D1[l] + Tt[1]*D2[l]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEElementMatrix ke;\n\n\tconst int MAXMN = FEElement::MAX_NODES;\n\tvector<int> lm(3*(MAXMN + 1));\n\tvector<int> en(MAXMN+1);\n\n\tdouble *Gr, *Gs, w[MAXMN];\n\tvec3d r0[MAXMN];\n\n\tdouble detJ[MAXMN];\n\tvec3d dxr, dxs;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\t// do two-pass\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\t// get the primary and secondary surface\n\t\tFESlidingSurface& ss = (np==0?m_ss:m_ms);\t\n\t\tFESlidingSurface& ms = (np==0?m_ms:m_ss);\t\n\n\t\t// loop over all primary surface elements\n\t\tint ne = ss.Elements();\n\t\tfor (int j=0; j<ne; ++j)\n\t\t{\n\t\t\t// unpack the next element\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\t\t\tint nseln = se.Nodes();\n\n\t\t\t// get the element's LM array\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t// get the nodal coordinates\n\t\t\tfor (int i=0; i<nseln; ++i) r0[i] = ss.GetMesh()->Node(se.m_node[i]).m_r0;\n\n\t\t\t// get all the metrics we need \n\t\t\tfor (int n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = se.Gr(n);\n\t\t\t\tGs = se.Gs(n);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (int k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ[n] = (dxr ^ dxs).norm();\n\t\t\t\tw[n] = se.GaussWeights()[n];\n\t\t\t}\n\n\t\t\t// loop over all integration points (that is nodes)\n\t\t\tfor (int n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tint m = se.m_lnode[n];\n\n\t\t\t\t// see if this node's constraint is active\n\t\t\t\t// that is, if it has an element associated with it\n\t\t\t\tif (ss.m_data[m].m_pme != 0)\n\t\t\t\t{\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement& me = *ss.m_data[m].m_pme;\n\n\t\t\t\t\t// get the secondary surface element's LM array\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\tint ndof = 3*(nmeln+1);\n\n\t\t\t\t\t// calculate the stiffness matrix\n\t\t\t\t\tke.resize(ndof, ndof);\n\t\t\t\t\tContactNodalStiffness(m, ss, me, ke);\n\n\t\t\t\t\t// muliply with weights\n\t\t\t\t\tfor (int k=0; k<ndof; ++k)\n\t\t\t\t\t\tfor (int l=0; l<ndof; ++l) ke[k][l] *= detJ[n]*w[n];\n\n\t\t\t\t\t// fill the lm array\n\t\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t\t\t}\n\n\t\t\t\t\t// create the en array\n\t\t\t\t\ten.resize(nmeln+1);\n\t\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\t\tfor (int k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\t\t\t\t\t\t\n\t\t\t\t\t// assemble stiffness matrix\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(lm);\n\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface::ContactNodalStiffness(int m, FESlidingSurface& ss, FESurfaceElement& mel, matrix& ke)\n{\n\tconst int MAXMN = FEElement::MAX_NODES;\n\n\tvector<int> lm(3*(MAXMN+1));\n\tvector<int> en(MAXMN + 1);\n\n\tvec3d dxr, dxs;\n\tdouble H[MAXMN], Hr[MAXMN], Hs[MAXMN];\n\n\tdouble N[3*(MAXMN+1)], T1[3*(MAXMN+1)], T2[3*(MAXMN+1)];\n\tdouble N1[3*(MAXMN+1)], N2[3*(MAXMN+1)], D1[3*(MAXMN+1)], D2[3*(MAXMN+1)];\n\tdouble Nb1[3*(MAXMN+1)], Nb2[3*(MAXMN+1)];\n\n\t// get the mesh\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\t// nr of element nodes and degrees of freedom \n\tint nmeln = mel.Nodes();\n\tint ndof = 3*(1 + nmeln);\n\n\t// penalty factor\n\tdouble scale = Penalty();\n\tdouble eps = ss.m_data[m].m_eps*scale;\n\n\t// nodal coordinates\n\tvec3d rt[MAXMN];\n\tfor (int j=0; j<nmeln; ++j) rt[j] = mesh.Node(mel.m_node[j]).m_rt;\n\n\t// node natural coordinates in secondary surface element\n\tdouble r = ss.m_data[m].m_rs[0];\n\tdouble s = ss.m_data[m].m_rs[1];\n\n\t// gap\n\tdouble gap = ss.m_data[m].m_gap;\n\n\t// lagrange multiplier\n\tdouble Lm = ss.m_data[m].m_Lm;\n\n\t// get node normal force\n\tdouble tn = Lm + eps*gap;\n\ttn = MBRACKET(tn);\n\n\t// get the node normal\n\tvec3d& nu = ss.m_data[m].m_nu;\n\n\t// get the secondary surface element shape function values and the derivatives at this node\n\tmel.shape_fnc(H, r, s);\n\tmel.shape_deriv(Hr, Hs, r, s);\n\n\t// get the tangent vectors\n\tvec3d tau[2];\n\tss.CoBaseVectors(mel, r, s, tau);\n\n\t// set up the N vector\n\tN[0] = nu.x;\n\tN[1] = nu.y;\n\tN[2] = nu.z;\n\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tN[(k+1)*3  ] = -H[k]*nu.x;\n\t\tN[(k+1)*3+1] = -H[k]*nu.y;\n\t\tN[(k+1)*3+2] = -H[k]*nu.z;\n\t}\n\t\n\t// set up the Ti vectors\n\tT1[0] = tau[0].x; T2[0] = tau[1].x;\n\tT1[1] = tau[0].y; T2[1] = tau[1].y;\n\tT1[2] = tau[0].z; T2[2] = tau[1].z;\n\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tT1[(k+1)*3  ] = -H[k]*tau[0].x;\n\t\tT1[(k+1)*3+1] = -H[k]*tau[0].y;\n\t\tT1[(k+1)*3+2] = -H[k]*tau[0].z;\n\n\t\tT2[(k+1)*3  ] = -H[k]*tau[1].x;\n\t\tT2[(k+1)*3+1] = -H[k]*tau[1].y;\n\t\tT2[(k+1)*3+2] = -H[k]*tau[1].z;\n\t}\n\n\t// set up the Ni vectors\n\tN1[0] = N2[0] = 0;\n\tN1[1] = N2[1] = 0;\n\tN1[2] = N2[2] = 0;\n\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tN1[(k+1)*3  ] = -Hr[k]*nu.x;\n\t\tN1[(k+1)*3+1] = -Hr[k]*nu.y;\n\t\tN1[(k+1)*3+2] = -Hr[k]*nu.z;\n\n\t\tN2[(k+1)*3  ] = -Hs[k]*nu.x;\n\t\tN2[(k+1)*3+1] = -Hs[k]*nu.y;\n\t\tN2[(k+1)*3+2] = -Hs[k]*nu.z;\n\t}\n\n\t// calculate metric tensor\n\tmat2d M;\n\tM[0][0] = tau[0]*tau[0]; M[0][1] = tau[0]*tau[1]; \n\tM[1][0] = tau[1]*tau[0]; M[1][1] = tau[1]*tau[1]; \n\n\t// calculate reciprocal metric tensor\n\tmat2d Mi = M.inverse();\n\n\t// calculate curvature tensor\n\tdouble K[2][2] = {0};\n\tdouble Grr[FEElement::MAX_NODES];\n\tdouble Grs[FEElement::MAX_NODES];\n\tdouble Gss[FEElement::MAX_NODES];\n\tmel.shape_deriv2(Grr, Grs, Gss, r, s);\n\tfor (int k=0; k<nmeln; ++k)\n\t{\n\t\tK[0][0] += (nu*rt[k])*Grr[k];\n\t\tK[0][1] += (nu*rt[k])*Grs[k];\n\t\tK[1][0] += (nu*rt[k])*Grs[k];\n\t\tK[1][1] += (nu*rt[k])*Gss[k];\n\t}\n\n\t// setup A matrix A = M + gK\n\tdouble A[2][2];\n\tA[0][0] = M[0][0] + gap*K[0][0];\n\tA[0][1] = M[0][1] + gap*K[0][1];\n\tA[1][0] = M[1][0] + gap*K[1][0];\n\tA[1][1] = M[1][1] + gap*K[1][1];\n\n\t// calculate determinant of A\n\tdouble detA = A[0][0]*A[1][1] - A[0][1]*A[1][0];\n\n\t// setup Di vectors\n\tfor (int k=0; k<ndof; ++k)\n\t{\n\t\tD1[k] = (1/detA)*(A[1][1]*(T1[k]+gap*N1[k]) - A[0][1]*(T2[k] + gap*N2[k]));\n\t\tD2[k] = (1/detA)*(A[0][0]*(T2[k]+gap*N2[k]) - A[0][1]*(T1[k] + gap*N1[k]));\n\t}\n\n\t// setup Nbi vectors\n\tfor (int k=0; k<ndof; ++k)\n\t{\n\t\tNb1[k] = N1[k] - K[0][1]*D2[k];\n\t\tNb2[k] = N2[k] - K[0][1]*D1[k];\n\t}\n\n\t// --- N O R M A L   S T I F F N E S S ---\n\tdouble sum;\n\tfor (int k=0; k<ndof; ++k)\n\t\tfor (int l=0; l<ndof; ++l)\n\t\t\t{\n\t\t\t\tsum = 0;\n\n\t\t\t\tsum = Mi[0][0]*Nb1[k]*Nb1[l]+Mi[0][1]*(Nb1[k]*Nb2[l]+Nb2[k]*Nb1[l])+Mi[1][1]*Nb2[k]*Nb2[l];\n\t\t\t\tsum *= gap;\n\t\t\t\tsum -= D1[k]*N1[l]+D2[k]*N2[l]+N1[k]*D1[l]+N2[k]*D2[l];\n\t\t\t\tsum += K[0][1]*(D1[k]*D2[l]+D2[k]*D1[l]);\n\t\t\t\tsum *= tn*m_knmult;\n\n\t\t\t\tsum += eps*HEAVYSIDE(Lm+eps*gap)*N[k]*N[l];\n\t\n\t\t\t\tke[k][l] = sum;\n\t\t\t}\n\n\t// --- T A N G E N T I A L   S T I F F N E S S ---\n\t// We only calculate the tangential stiffness if friction is enabled. We also\n\t// make sure that the gap >= 0, i.e. the node is actually in contact, otherwise\n\t// I've noticed that the solution can diverge quickly.\n\tif ((m_mu*m_epsf > 0) && (gap >=0))\n\t{\n\t\t// get the traction multipliers\n\t\tdouble Lt[2];\n\t\tLt[0] = ss.m_data[m].m_Lt[0];\n\t\tLt[1] = ss.m_data[m].m_Lt[1];\n\n\t\t// get the metric tensor and its inverse\n\t\tmat2d& Mk = ss.m_data[m].m_M;\n\t\tmat2d Mki = Mk.inverse();\n\n\t\t// get the previous isoparameteric coordinates\n\t\tdouble rp = ss.m_data[m].m_rsp[0];\n\t\tdouble sp = ss.m_data[m].m_rsp[1];\n\n\t\t// get the traction\n\t\tdouble Tt[2];\n\t\t// a. trial state\n\t\tTt[0] = Lt[0] + Mk[0][0]*(r - rp) + Mk[0][1]*(s - sp);\n\t\tTt[1] = Lt[1] + Mk[1][0]*(r - rp) + Mk[1][1]*(s - sp);\n\n\t\tdouble TMT = Tt[0]*(Mki[0][0]*Tt[0] + Mki[0][1]*Tt[1]) + Tt[1]*(Mki[1][0]*Tt[0] + Mki[1][1]*Tt[1]);\n\n\t\t// calculate the normalized traction vector\n\t\tvec3d pt = tau[0]*Tt[0] + tau[1]*Tt[1];\n\t\tpt.unit();\n\t\t\n\t\t// calculate the covariant version\n\t\tdouble Pt[2] = {Tt[0]/sqrt(TMT), Tt[1]/sqrt(TMT)};\n\t\tdouble Ptc[2];\n\t\tPtc[0] = Mki[0][0]*Pt[0]+Mki[0][1]*Pt[1];\n\t\tPtc[1] = Mki[1][0]*Pt[0]+Mki[1][1]*Pt[1];\n\n\t\t//b. return map\n\t\tbool bstick = true;\n\t\tdouble phi = sqrt(TMT) - m_mu*tn;\n\t\tif (phi > 0)\n\t\t{\n\t\t\tTt[0] = m_mu*Tt[0]/sqrt(TMT);\n\t\t\tTt[1] = m_mu*Tt[1]/sqrt(TMT);\n\t\t\tbstick = false;\n\t\t}\n\n\t\t// we need to define additional arrays for the tangential\n\t\t// contribution of the contact stiffness\n\t\tdouble T11[3*(MAXMN+1)] = {0}, T12[3*(MAXMN+1)] = {0}, T21[3*(MAXMN+1)] = {0}, T22[3*(MAXMN+1)] = {0};\t// Tab matrix\n\t\tdouble N11[3*(MAXMN+1)] = {0}, N12[3*(MAXMN+1)] = {0}, N21[3*(MAXMN+1)] = {0}, N22[3*(MAXMN+1)] = {0};\t// Nab matrix\n\t\tdouble P1[3*(MAXMN+1)] = {0}, P2[3*(MAXMN+1)] = {0};\t// P arrays\n\t\tdouble Tb11[3*(MAXMN+1)], Tb21[3*(MAXMN+1)], Tb12[3*(MAXMN+1)], Tb22[3*(MAXMN+1)]; // Tbar matrix\n\t\tdouble Pb1[3*(MAXMN+1)], Pb2[3*(MAXMN+1)]; // Pbar array\n\n\t\tfor (int k=0; k<nmeln; ++k)\n\t\t{\n\t\t\tT11[3*(k+1)  ] = -Hr[k]*tau[0].x;\n\t\t\tT11[3*(k+1)+1] = -Hr[k]*tau[0].y;\n\t\t\tT11[3*(k+1)+2] = -Hr[k]*tau[0].z;\n\n\t\t\tT12[3*(k+1)  ] = -Hs[k]*tau[0].x;\n\t\t\tT12[3*(k+1)+1] = -Hs[k]*tau[0].y;\n\t\t\tT12[3*(k+1)+2] = -Hs[k]*tau[0].z;\n\n\t\t\tT21[3*(k+1)  ] = -Hr[k]*tau[1].x;\n\t\t\tT21[3*(k+1)+1] = -Hr[k]*tau[1].y;\n\t\t\tT21[3*(k+1)+2] = -Hr[k]*tau[1].z;\n\n\t\t\tT22[3*(k+1)  ] = -Hs[k]*tau[1].x;\n\t\t\tT22[3*(k+1)+1] = -Hs[k]*tau[1].y;\n\t\t\tT22[3*(k+1)+2] = -Hs[k]*tau[1].z;\n\n\t\t\tif (nmeln==4)\n\t\t\t{\n\t\t\t\tN12[3*(k+1)  ] = N21[3*(k+1)  ] = -0.25*nu.x;\n\t\t\t\tN12[3*(k+1)+1] = N21[3*(k+1)+1] = -0.25*nu.y;\n\t\t\t\tN12[3*(k+1)+2] = N21[3*(k+1)+2] = -0.25*nu.z;\n\t\t\t}\n\t\t\telse if (nmeln == 6) assert(false);\n\n\t\t\tP1[3*(k+1)  ] = -Hr[k]*pt.x;\n\t\t\tP1[3*(k+1)+1] = -Hr[k]*pt.y;\n\t\t\tP1[3*(k+1)+2] = -Hr[k]*pt.z;\n\n\t\t\tP2[3*(k+1)  ] = -Hs[k]*pt.x;\n\t\t\tP2[3*(k+1)+1] = -Hs[k]*pt.y;\n\t\t\tP2[3*(k+1)+2] = -Hs[k]*pt.z;\n\t\t}\n\n\t\tvec3d g12(0,0,0);\n\t\tif (nmeln == 4) \n\t\t{\n\t\t\tconst double Grs[4] = {0.25, -0.25, 0.25, -0.25};\n\t\t\tg12 = rt[0]*Grs[0]+rt[1]*Grs[1]+rt[2]*Grs[2]+rt[3]*Grs[3];\n\t\t}\n\t\telse if (nmeln == 6)\n\t\t{\n\t\t\tconst double Grs[6] = {4.0, 0.0, 0.0, -4.0, 4.0, -4.0};\n\t\t\tg12 = rt[0]*Grs[0]+rt[1]*Grs[1]+rt[2]*Grs[2]+rt[3]*Grs[3]+rt[4]*Grs[4]+rt[5]*Grs[5];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tassert(false);\n\t\t}\n\t\tdouble gt1 = g12*tau[0];\n\t\tdouble gt2 = g12*tau[1];\n\t\tdouble gp = g12*pt;\n\n\t\tfor (int k=0; k<ndof; ++k)\n\t\t{\n\t\t\tTb11[k] = T11[k] - gt1*D2[k];\n\t\t\tTb12[k] = T12[k] - gt1*D1[k];\n\n\t\t\tTb21[k] = T21[k] - gt2*D2[k];\n\t\t\tTb22[k] = T22[k] - gt2*D1[k];\n\n\t\t\tPb1[k] = P1[k] - gp*D2[k];\n\t\t\tPb2[k] = P2[k] - gp*D1[k];\n\t\t}\n\n\t\t// raise the indices of A\n\t\tdouble Ac[2][2];\n\t\tfor (int k=0; k<2; ++k)\n\t\t\tfor (int l=0; l<2; ++l)\n\t\t\t{\n\t\t\t\tAc[k][l] = 0;\n\t\t\t\tfor (int i=0; i<2; ++i)\n\t\t\t\t\tfor (int j=0; j<2; ++j) Ac[k][l] += Mki[k][i]*Mki[l][j]*A[i][j];\n\t\t\t}\n\n\t\tvec3d Hrs[2][2] = {{vec3d(0,0,0),vec3d(0,0,0)},{vec3d(0,0,0),vec3d(0,0,0)}};\n\t\tif (nmeln == 4) \n\t\t{\n\t\t\tconst double Grs[4] = {0.25, -0.25, 0.25, -0.25};\n\t\t\tHrs[0][1] = Hrs[1][0] = rt[0]*Grs[0]+rt[1]*Grs[1]+rt[2]*Grs[2]+rt[3]*Grs[3];\n\t\t}\n\t\telse if (nmeln == 6)\n\t\t{\n\t\t\tconst double Grs[6] = {4.0, 0.0, 0.0, -4.0, 4.0, -4.0};\n\t\t\tHrs[0][1] = Hrs[1][0] = rt[0]*Grs[0]+rt[1]*Grs[1]+rt[2]*Grs[2]+rt[3]*Grs[3]+rt[4]*Grs[4]+rt[5]*Grs[5];\n\t\t}\n\n\t\t// calculate stiffness matrix\n\t\t// NOTE: I think I need Mi (iso Mki) for KT1 and KT2. I only need Mk (iso M) for the direct stiffnesses\n\t\tdouble kij;\n\t\tfor (int i=0; i<ndof; ++i)\n\t\t\tfor (int j=0; j<ndof; ++j)\n\t\t\t{\n\t\t\t\t// KT1\n\t\t\t\tkij  = T11[i]*D1[j] + T12[i]*D2[j];\n\t\t\t\tkij += D1[i]*T11[j] + D2[i]*T12[j];\n\t\t\t\tkij -= (Hrs[0][1]*tau[0])*D1[i]*D2[j] + (Hrs[1][0]*tau[0])*D2[i]*D1[j];\n\t\t\t\tkij += Tb11[i]*D1[j] + Tb21[i]*D2[j];\n\t\t\t\tkij += D1[i]*Tb11[j] + D2[i]*Tb21[j];\n\t\t\t\tkij += gap*(N11[i]*D1[j] + N12[i]*D2[j] + D1[i]*N11[j] + D2[i]*N12[j]);\n\t\t\t\tkij -= N[i]*Nb1[j] - Nb1[i]*N[j];\n\t\t\t\tkij -= T1[i]*Mi[0][0]*Tb11[j] + T1[i]*Mi[0][1]*Tb21[j] + T2[i]*Mi[1][0]*Tb11[j] + T2[i]*Mi[1][1]*Tb21[j];\n\t\t\t\tkij -= Tb11[i]*Mi[0][0]*T1[j] + Tb21[i]*Mi[0][1]*T1[j] + Tb11[i]*Mi[1][0]*T2[j] + Tb21[i]*Mi[1][1]*T2[j];\n\n\t\t\t\tke[i][j] += m_ktmult*(Tt[0]*Ac[0][0] + Tt[1]*Ac[1][0])*kij;\n\n\t\t\t\t// KT2\n\t\t\t\tkij  = T21[i]*D1[j] + T22[i]*D2[j];\n\t\t\t\tkij += D1[i]*T21[j] + D2[i]*T22[j];\n\t\t\t\tkij -= (Hrs[0][1]*tau[1])*D1[i]*D2[j] + (Hrs[1][0]*tau[1])*D2[i]*D1[j];\n\t\t\t\tkij += Tb12[i]*D1[j] + Tb22[i]*D2[j];\n\t\t\t\tkij += D1[i]*Tb12[j] + D2[i]*Tb22[j];\n\t\t\t\tkij += gap*(N21[i]*D1[j] + N22[i]*D2[j] + D1[i]*N21[j] + D2[i]*N22[j]);\n\t\t\t\tkij -= N[i]*Nb2[j] - Nb2[i]*N[j];\n\t\t\t\tkij -= T1[i]*Mi[0][0]*Tb12[j] + T1[i]*Mi[0][1]*Tb22[j] + T2[i]*Mi[1][0]*Tb12[j] + T2[i]*Mi[1][1]*Tb22[j];\n\t\t\t\tkij -= Tb12[i]*Mi[0][0]*T1[j] + Tb22[i]*Mi[0][1]*T1[j] + Tb12[i]*Mi[1][0]*T2[j] + Tb22[i]*Mi[1][1]*T2[j];\n\n\t\t\t\tke[i][j] += m_ktmult*(Tt[0]*Ac[0][1] + Tt[1]*Ac[1][1])*kij;\n\n\t\t\t\t// kdirect\n\t\t\t\tif (bstick)\n\t\t\t\t{\n\t\t\t\t\tkij = Mk[0][0]*D1[i]*D1[j] + Mk[0][1]*D1[i]*D2[j] + Mk[1][0]*D2[i]*D1[j] + Mk[1][1]*D2[i]*D2[j];\n\t\t\t\t\tke[i][j] += m_epsf*kij;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tkij  = (1.0 - Ptc[0]*Pt[0])*(Mk[0][0]*D1[i]*D1[j]+Mk[0][1]*D1[i]*D2[j]);\n\t\t\t\t\tkij += (    - Ptc[0]*Pt[1])*(Mk[1][0]*D1[i]*D1[j]+Mk[1][1]*D1[i]*D2[j]);\n\t\t\t\t\tkij += (    - Ptc[1]*Pt[0])*(Mk[0][0]*D2[i]*D1[j]+Mk[0][1]*D2[i]*D2[j]);\n\t\t\t\t\tkij += (1.0 - Ptc[1]*Pt[1])*(Mk[1][0]*D2[i]*D1[j]+Mk[1][1]*D2[i]*D2[j]);\n\t\t\t\t\t\n\t\t\t\t\tke[i][j] += m_ktmult*m_epsf*m_mu*tn/sqrt(TMT)*kij;\n\t\t\t\t}\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nbool FESlidingInterface::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tdouble Ln;\n\tdouble Lt[2];\n\tbool bconv = true;\n\tmat2d Mi;\n\n\t// penalty factor\n\tdouble eps, scale = Penalty();\n\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\t// a. normal component\n\tdouble normL0 = 0;\n\tfor (int i=0; i<m_ss.Nodes(); ++i)\tnormL0 += m_ss.m_data[i].m_Lm*m_ss.m_data[i].m_Lm;\n\tfor (int i=0; i<m_ms.Nodes(); ++i)\tnormL0 += m_ms.m_data[i].m_Lm*m_ms.m_data[i].m_Lm;\n\n\t// b. tangential component\n\tif (m_mu*m_epsf > 0)\n\t{\n\t\tfor (int i=0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\tif (m_ss.m_data[i].m_pme)\n\t\t\t{\n\t\t\t\tLt[0] = m_ss.m_data[i].m_Lt[0];\n\t\t\t\tLt[1] = m_ss.m_data[i].m_Lt[1];\n\t\t\t\tmat2d& M = m_ss.m_data[i].m_M;\n\t\t\t\tMi = M.inverse();\n\t\t\t\tnormL0 += Lt[0]*(Mi[0][0]*Lt[0] + Mi[0][1]*Lt[1]) + Lt[1]*(Mi[1][0]*Lt[0] + Mi[1][1]*Lt[1]);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i=0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\tif (m_ms.m_data[i].m_pme)\n\t\t\t{\n\t\t\t\tLt[0] = m_ms.m_data[i].m_Lt[0];\n\t\t\t\tLt[1] = m_ms.m_data[i].m_Lt[1];\n\t\t\t\tmat2d& M = m_ms.m_data[i].m_M;\n\t\t\t\tMi = M.inverse();\n\t\t\t\tnormL0 += Lt[0]*(Mi[0][0]*Lt[0] + Mi[0][1]*Lt[1]) + Lt[1]*(Mi[1][0]*Lt[0] + Mi[1][1]*Lt[1]);\n\t\t\t}\n\t\t}\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// --- c a l c u l a t e   c u r r e n t   n o r m s ---\n\t// a. normal component\n\tdouble normL1 = 0;\t// force norm\n\tdouble normg1 = 0;\t// gap norm\n\tint N = 0;\n\tfor (int i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\teps = m_ss.m_data[i].m_eps*scale;\n\n\t\t// update Lagrange multipliers\n\t\tLn = m_ss.m_data[i].m_Lm + eps*m_ss.m_data[i].m_gap;\n\t\tLn = MBRACKET(Ln);\n\n\t\tnormL1 += Ln*Ln;\n\n\t\tif (m_ss.m_data[i].m_gap > 0)\n\t\t{\n\t\t\tnormg1 += m_ss.m_data[i].m_gap*m_ss.m_data[i].m_gap;\n\t\t\t++N;\n\t\t}\n\t}\t\n\n\tfor (int i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\teps = m_ms.m_data[i].m_eps*scale;\n\n\t\t// update Lagrange multipliers\n\t\tLn = m_ms.m_data[i].m_Lm + eps*m_ms.m_data[i].m_gap;\n\t\tLn = MBRACKET(Ln);\n\n\t\tnormL1 += Ln*Ln;\n\t\tif (m_ms.m_data[i].m_gap > 0)\n\t\t{\n\t\t\tnormg1 += m_ms.m_data[i].m_gap*m_ms.m_data[i].m_gap;\n\t\t\t++N;\n\t\t}\n\t}\n\tif (N == 0) N=1;\n\n\t// b. tangential component\n\tif (m_mu*m_epsf > 0)\n\t{\n\t\tdouble r, s, rp, sp;\n\t\tfor (int i=0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\tif (m_ss.m_data[i].m_pme)\n\t\t\t{\n\t\t\t\tr = m_ss.m_data[i].m_rs[0];\n\t\t\t\ts = m_ss.m_data[i].m_rs[1];\n\t\t\t\trp = m_ss.m_data[i].m_rsp[0];\n\t\t\t\tsp = m_ss.m_data[i].m_rsp[1];\n\t\t\t\tLn = m_ss.m_data[i].m_Lm;\n\t\n\t\t\t\tmat2d& Mk = m_ss.m_data[i].m_M;\n\t\t\t\tMi = Mk.inverse();\n\n\t\t\t\tLt[0] = m_ss.m_data[i].m_Lt[0] + m_epsf*(Mk[0][0]*(r - rp) + Mk[0][1]*(s - sp));\n\t\t\t\tLt[1] = m_ss.m_data[i].m_Lt[1] + m_epsf*(Mk[1][0]*(r - rp) + Mk[1][1]*(s - sp));\n\n\t\t\t\tdouble TMT = Lt[0]*(Mi[0][0]*Lt[0]+Mi[0][1]*Lt[1])+Lt[1]*(Mi[1][0]*Lt[0]+Mi[1][1]*Lt[1]);\n\t\t\t\tdouble phi = sqrt(TMT) - m_mu*Ln;\n\n\t\t\t\t// b. return map\n\t\t\t\tif (phi > 0)\n\t\t\t\t{\n\t\t\t\t\tLt[0] = m_mu*Ln*Lt[0]/sqrt(TMT);\n\t\t\t\t\tLt[1] = m_mu*Ln*Lt[1]/sqrt(TMT);\n\t\t\t\t}\n\n\t\t\t\tnormL1 += Lt[0]*(Mi[0][0]*Lt[0] + Mi[0][1]*Lt[1]) + Lt[1]*(Mi[1][0]*Lt[0] + Mi[1][1]*Lt[1]);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i=0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\tif (m_ms.m_data[i].m_pme)\n\t\t\t{\n\t\t\t\tr = m_ms.m_data[i].m_rs[0];\n\t\t\t\ts = m_ms.m_data[i].m_rs[1];\n\t\t\t\trp = m_ms.m_data[i].m_rsp[0];\n\t\t\t\tsp = m_ms.m_data[i].m_rsp[1];\n\t\t\t\tLn = m_ms.m_data[i].m_Lm;\n\n\t\t\t\tmat2d& Mk = m_ms.m_data[i].m_M;\n\t\t\t\tMi = Mk.inverse();\n\n\t\t\t\tLt[0] = m_ms.m_data[i].m_Lt[0] + m_epsf*(Mk[0][0]*(r - rp) + Mk[0][1]*(s - sp));\n\t\t\t\tLt[1] = m_ms.m_data[i].m_Lt[1] + m_epsf*(Mk[1][0]*(r - rp) + Mk[1][1]*(s - sp));\n\n\t\t\t\tdouble TMT = Lt[0]*(Mi[0][0]*Lt[0]+Mi[0][1]*Lt[1])+Lt[1]*(Mi[1][0]*Lt[0]+Mi[1][1]*Lt[1]);\n\t\t\t\tdouble phi = sqrt(TMT) - m_mu*Ln;\n\n\t\t\t\t// b. return map\n\t\t\t\tif (phi > 0)\n\t\t\t\t{\n\t\t\t\t\tLt[0] = m_mu*Ln*Lt[0]/sqrt(TMT);\n\t\t\t\t\tLt[1] = m_mu*Ln*Lt[1]/sqrt(TMT);\n\t\t\t\t}\n\n\t\t\t\tnormL1 += Lt[0]*(Mi[0][0]*Lt[0] + Mi[0][1]*Lt[1]) + Lt[1]*(Mi[1][0]*Lt[0] + Mi[1][1]*Lt[1]);\n\t\t\t}\n\t\t}\n\t}\n\n\tnormL1 = sqrt(normL1);\n\tnormg1 = sqrt(normg1 / N);\n\n\tif (naug == 0) m_normg0 = 0;\n\n\t// calculate and print convergence norms\n\tdouble lnorm = 0, gnorm = 0;\n\tif (normL1 != 0) lnorm = fabs(normL1 - normL0)/normL1; else lnorm = fabs(normL1 - normL0);\n//\tif (normg1 != 0) gnorm = fabs(normg1 - m_normg0)/normg1; else gnorm = fabs(normg1 - m_normg0);\n\tgnorm = fabs(normg1 - m_normg0);\n\n\tfeLog(\" sliding interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    normal force : %15le\", lnorm);\n\tif (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\tfeLog(\"    gap function : %15le\", gnorm);\n\tif (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n\n\t// check convergence\n\tbconv = true;\n\tif ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n\tif ((m_gtol > 0) && (gnorm > m_gtol)) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\tif (m_naugmax <= naug) bconv = true;\n\t\t\n\tif (bconv == false)\n\t{\n\t\t// we did not converge so update multipliers\n\t\tfor (int i=0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\teps = m_ss.m_data[i].m_eps*scale;\n\n\t\t\t// update Lagrange multipliers\n\t\t\tLn = m_ss.m_data[i].m_Lm + eps*m_ss.m_data[i].m_gap;\n\t\t\tm_ss.m_data[i].m_Lm = MBRACKET(Ln);\n\n\t\t\tif ((m_mu*m_epsf > 0) && (m_ss.m_data[i].m_pme))\n\t\t\t{\n\t\t\t\t// update the metrics\n\t\t\t\tFESurfaceElement& mel = *m_ss.m_data[i].m_pme;\n\n\t\t\t\tdouble r = m_ss.m_data[i].m_rs[0], s = m_ss.m_data[i].m_rs[1];\n\t\t\t\tdouble rp = m_ss.m_data[i].m_rsp[0], sp = m_ss.m_data[i].m_rsp[1];\n\n\t\t\t\tdouble Ln = m_ss.m_data[i].m_Lm;\n\n\t\t\t\tmat2d Mk = m_ss.m_data[i].m_M;\n\t\t\t\tmat2d Mki = Mk.inverse();\n\t\t\t\t\n\t\t\t\n\t\t\t\t// update traction multipliers\n\t\t\t\t// a. trial state\n\t\t\t\tLt[0] = m_ss.m_data[i].m_Lt[0] + m_epsf*(Mk[0][0]*(r - rp) + Mk[0][1]*(s - sp));\n\t\t\t\tLt[1] = m_ss.m_data[i].m_Lt[1] + m_epsf*(Mk[1][0]*(r - rp) + Mk[1][1]*(s - sp));\n\n\t\t\t\tdouble TMT = Lt[0]*(Mki[0][0]*Lt[0]+Mki[0][1]*Lt[1])+Lt[1]*(Mki[1][0]*Lt[0]+Mki[1][1]*Lt[1]);\n\t\t\t\tassert(TMT >= 0);\n\n\t\t\t\tdouble phi = sqrt(TMT) - m_mu*Ln;\n\n\t\t\t\t// b. return map\n\t\t\t\tif (phi > 0)\n\t\t\t\t{\n\t\t\t\t\tLt[0] = m_mu*Ln*Lt[0]/sqrt(TMT);\n\t\t\t\t\tLt[1] = m_mu*Ln*Lt[1]/sqrt(TMT);\n\t\t\t\t}\n\n\t\t\t\tm_ss.m_data[i].m_M = m_ss.Metric0(mel, r, s);\n\n\t\t\t\tm_ss.m_data[i].m_Lt[0] = Lt[0];\n\t\t\t\tm_ss.m_data[i].m_Lt[1] = Lt[1];\n\t\t\t}\n\t\t}\t\n\n\t\tfor (int i=0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\teps = m_ms.m_data[i].m_eps*scale;\n\n\t\t\t// update Lagrange multipliers\n\t\t\tLn = m_ms.m_data[i].m_Lm + eps*m_ms.m_data[i].m_gap;\n\t\t\tm_ms.m_data[i].m_Lm = MBRACKET(Ln);\n\n\t\t\tif ((m_mu*m_epsf > 0) && (m_ms.m_data[i].m_pme))\n\t\t\t{\n\t\t\t\t// update the metrics\n\t\t\t\tFESurfaceElement& mel = *m_ms.m_data[i].m_pme;\n\n\t\t\t\tdouble r = m_ms.m_data[i].m_rs[0], s = m_ms.m_data[i].m_rs[1];\n\t\t\t\tdouble rp = m_ms.m_data[i].m_rsp[0], sp = m_ms.m_data[i].m_rsp[1];\n\n\t\t\t\tdouble Ln = m_ms.m_data[i].m_Lm;\n\n\t\t\t\tmat2d Mk = m_ms.m_data[i].m_M;\n\t\t\t\tmat2d Mki = Mk.inverse();\n\t\t\t\t\n\t\t\t\t// update traction multipliers\n\t\t\t\t// a. trial state\n\t\t\t\tdouble Lt[2];\n\t\t\t\tLt[0] = m_ms.m_data[i].m_Lt[0] + m_epsf*(Mk[0][0]*(r - rp) + Mk[0][1]*(s - sp));\n\t\t\t\tLt[1] = m_ms.m_data[i].m_Lt[1] + m_epsf*(Mk[1][0]*(r - rp) + Mk[1][1]*(s - sp));\n\n\t\t\t\tdouble TMT = Lt[0]*(Mki[0][0]*Lt[0]+Mki[0][1]*Lt[1])+Lt[1]*(Mki[1][0]*Lt[0]+Mki[1][1]*Lt[1]);\n\t\t\t\tassert(TMT >= 0);\n\n\t\t\t\tdouble phi = sqrt(TMT) - m_mu*Ln;\n\n\t\t\t\t// b. return map\n\t\t\t\tif (phi > 0)\n\t\t\t\t{\n\t\t\t\t\tLt[0] = m_mu*Ln*Lt[0]/sqrt(TMT);\n\t\t\t\t\tLt[1] = m_mu*Ln*Lt[1]/sqrt(TMT);\n\t\t\t\t}\n\n\t\t\t\tm_ms.m_data[i].m_M = m_ms.Metric0(mel, r, s);\n\n\t\t\t\tm_ms.m_data[i].m_Lt[0] = Lt[0];\n\t\t\t\tm_ms.m_data[i].m_Lt[1] = Lt[1];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bconv)\n\t{\n\t\tfor (int i=0; i<m_ss.Nodes(); ++i) m_ss.m_data[i].m_rsp = m_ss.m_data[i].m_rs;\n\t\tfor (int i=0; i<m_ms.Nodes(); ++i) m_ms.m_data[i].m_rsp = m_ms.m_data[i].m_rs;\n\t}\n\n\t// store the last gap norm\n\tm_normg0 = normg1;\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! This function transforms friction data between two segments\n\nvoid FESlidingInterface::MapFrictionData(int inode, FESlidingSurface& ss, FESlidingSurface& ms, FESurfaceElement &en, FESurfaceElement &eo, vec3d &q)\n{\n\t// first we find the projection of the old point on the new segment\n\tdouble r = ss.m_data[inode].m_rs[0];\n\tdouble s = ss.m_data[inode].m_rs[1];\n\tdouble rp = ss.m_data[inode].m_rsp[0], ro = rp;\n\tdouble sp = ss.m_data[inode].m_rsp[1], so = sp;\n\tvec3d xn = ms.Local2Global(eo, rp, sp);\n\tvec3d qn;\n\tqn = ms.ProjectToSurface(en, xn, rp, sp);\n\tss.m_data[inode].m_rsp[0] = rp;\n\tss.m_data[inode].m_rsp[1] = sp;\n\n\t// next, we transform the frictional traction\n\t// since these tractions are decomposed in the local \n\t// element coordinate system, we have to do a coordinate transformation\n\t// note that this transformation needs to be done in curvilinear\n\t// coordinates since the base vectors may not be orthonormal. Also\n\t// note that we are doing this in the reference configuration\n\tvec3d to[2], tn[2];\n\tms.ContraBaseVectors0(eo, ro, so, to);\n\tms.CoBaseVectors0(en, r, s, tn);\n\n\tdouble Lt[2];\n\tLt[0] = ss.m_data[inode].m_Lt[0];\n\tLt[1] = ss.m_data[inode].m_Lt[1];\n\t\n\tvec3d t;\n\tt = to[0]*Lt[0] + to[1]*Lt[1];\n\n\tLt[0] = t*tn[0];\n\tLt[1] = t*tn[1];\n\n\tss.m_data[inode].m_Lt[0] = Lt[0];\n\tss.m_data[inode].m_Lt[1] = Lt[1];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface::UpdateContactPressures()\n{\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all nodes of the primary surface\n\t\tfor (int n=0; n<ss.Nodes(); ++n)\n\t\t{\n\t\t\t// get the normal tractions at the integration points\n\t\t\tdouble gap = ss.m_data[n].m_gap;\n\t\t\tdouble eps = m_eps*ss.m_data[n].m_eps;\n\t\t\tss.m_data[n].m_Ln = MBRACKET(ss.m_data[n].m_Lm + eps*gap);\n\t\t\tFESurfaceElement* pme = ss.m_data[n].m_pme;\n\t\t\tif (m_btwo_pass && pme)\n\t\t\t{\n\t\t\t\tint me = pme->Nodes();\n\t\t\t\tif (me < 6)\n\t\t\t\t{\n\t\t\t\t\tdouble ti[6];\n\t\t\t\t\tfor (int j=0; j<me; ++j) {\n\t\t\t\t\t\tint k = pme->m_lnode[j];\n\t\t\t\t\t\tgap = ms.m_data[k].m_gap;\n\t\t\t\t\t\teps = m_eps*ms.m_data[k].m_eps;\n\t\t\t\t\t\tti[j] = MBRACKET(ms.m_data[k].m_Lm + m_eps*ms.m_data[k].m_eps*ms.m_data[k].m_gap);\n\t\t\t\t\t}\n\t\t\t\t\t// project the data to the nodes\n\t\t\t\t\tdouble tn[6];\n\t\t\t\t\tpme->FEElement::project_to_nodes(ti, tn);\n\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\tdouble Ln = pme->eval(tn, ss.m_data[n].m_rs[0], ss.m_data[n].m_rs[1]);\n\t\t\t\t\tss.m_data[n].m_Ln += MBRACKET(Ln);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface::Serialize(DumpStream& ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n\n\t// restore element pointers\n\tSerializePointers(m_ss, m_ms, ar);\n\tSerializePointers(m_ms, m_ss, ar);\n\n\tar & m_bfirst & m_normg0;\n}\n\nvoid FESlidingInterface::SerializePointers(FESlidingSurface& ss, FESlidingSurface& ms, DumpStream& ar)\n{\n\tif (ar.IsSaving())\n\t{\n\t\tfor (int i = 0; i < ss.m_data.size(); i++)\n\t\t{\n\t\t\tFESurfaceElement* pe = ss.m_data[i].m_pme;\n\t\t\tint eid = (pe ? pe->m_lid : -1);\n\t\t\tar << eid;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < ss.m_data.size(); i++)\n\t\t{\n\t\t\tint eid = -1;\n\t\t\tar >> eid;\n\t\t\tif (eid >= 0)\n\t\t\t{\n\t\t\t\tFESurfaceElement* pe = &ms.Element(eid);\n\t\t\t\tss.m_data[i].m_pme = pe;\n\t\t\t}\n\t\t\telse ss.m_data[i].m_pme = nullptr;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FESlidingInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactSurface.h\"\n#include \"FEContactInterface.h\"\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/vector.h>\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FESlidingSurface : public FEContactSurface\n{\n\tclass FESlidingPoint : public FEContactMaterialPoint \n\t{\n\tpublic:\n\t\tFESlidingPoint();\n\n\t\tvoid Serialize(DumpStream& ar) override;\n\n\t\tvoid Init() override;\n\n\tpublic:\n\t\tvec3d\t\t\t\tm_nu;\t  //!< secondary surface normal at primary surface node\n\t\tvec2d\t\t\t\tm_rs;\t  //!< natural coordinates of primary surface projection on secondary surface element\n\t\tvec2d\t\t\t\tm_rsp;  //!< natural coordinates at previous time step\n\t\tdouble\t\t\t\tm_Lm;\t  //!< Lagrange multipliers for contact pressure\n\t\tmat2d\t\t\t\tm_M;\t  //!< surface metric tensor\n\t\tvec2d\t\t\t\tm_Lt;\t  //!< Lagrange multipliers for friction\n\t\tdouble\t\t\t\tm_off;  //!< gap offset (= shell thickness)\n\t\tdouble\t\t\t\tm_eps;  //!< normal penalty factors\n\t};\n\npublic:\n\t//! constructor\n\tFESlidingSurface(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\n\t//! Calculate the total traction at a node\n\tvec3d traction(int inode);\n\n\t//! evaluate net contact force\n\tvec3d GetContactForce();\n\t\n\t//! evaluate net contact area\n\tdouble GetContactArea();\n    \n\t//! Serialize data to archive\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt);\n\tvoid GetNodalContactPressure(int nface, double* pg);\n\tvoid GetNodalContactTraction(int nface, vec3d* pt);\n\npublic:\n\tvector<FESlidingPoint>\t\tm_data;\t//!< sliding contact surface data\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a sliding interface\n\n//! The FESlidingInterface class defines an interface between two surfaces.\n\nclass FEBIOMECH_API FESlidingInterface : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFESlidingInterface(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FESlidingInterface(){}\n\n\t//! Initializes sliding interface\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! projects primary surface nodes onto secondary surface nodes\n\tvoid ProjectSurface(FESlidingSurface& ss, FESlidingSurface& ms, bool bupseg, bool bmove = false);\n\n\t//! calculate penalty value\n\tdouble Penalty() { return m_eps; }\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! calculate contact pressures for file output\n\tvoid UpdateContactPressures();\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\t//! calculate auto penalty factor\n\tvoid CalcAutoPenalty(FESlidingSurface& s);\n\n\t//! calculate the nodal force of a primary surface node\n\tvoid ContactNodalForce(int m, FESlidingSurface& ss, FESurfaceElement& mel, vector<double>& fe);\n\n\t//! calculate the stiffness contribution of a single primary surface node\n\tvoid ContactNodalStiffness(int m, FESlidingSurface& ss, FESurfaceElement& mel, matrix& ke);\n\n\t//! map the frictional data from the old element to the new element\n\tvoid MapFrictionData(int inode, FESlidingSurface& ss, FESlidingSurface& ms, FESurfaceElement& sn, FESurfaceElement& so, vec3d& q);\n\nprivate:\n\tvoid SerializePointers(FESlidingSurface& ss, FESlidingSurface& ms, DumpStream& ar);\n\npublic:\n\tFESlidingSurface\tm_ss;\t//!< primary surface\n\tFESlidingSurface\tm_ms;\t//!< secondary surface\n\n\tbool\t\t\tm_btwo_pass;\t//!< two pass algorithm flag\n\n\tdouble\t\t\tm_sradius;\t\t\t//!< search radius for self contact\n\n\tint\t\t\t\tm_naugmax;\t//!< maximum nr of augmentations\n\tint\t\t\t\tm_naugmin;\t//!< minimum nr of augmentations\n\tdouble\t\t\tm_gtol;\t\t//!< gap tolerance\n\tdouble\t\t\tm_atol;\t\t//!< augmentation tolerance\n\tdouble\t\t\tm_ktmult;\t//!< multiplier for tangential stiffness\n\tdouble\t\t\tm_knmult;\t//!< multiplier for normal stiffness\n\n\tdouble\t\t\tm_stol;\t\t//!< search tolerance\n\n\tbool\t\t\tm_bautopen;\t//!< auto penalty calculation\n\tdouble\t\t\tm_eps;\t\t//!< penalty scale factor \n\tbool\t\t\tm_bupdtpen;\t//!< update penalty \n\n\tbool\t\t\tm_breloc;\t//!< initial node relocation\n\n\tdouble\t\t\tm_mu;\t\t//!< friction coefficient\n\tdouble\t\t\tm_epsf;\t\t//!< penalty scale factor for friction\n\n\tint\t\t\t\tm_nsegup;\t//!< segment update parameter\n\nprivate:\n\tbool\tm_bfirst;\t//!< flag to indicate the first time we enter Update\n\tdouble\tm_normg0;\t//!< initial gap norm\n\npublic:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESolidAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FESolidAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FESolidAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STATIC\\0DYNAMIC\\0\");\nEND_FECORE_CLASS()\n\nFESolidAnalysis::FESolidAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioMech/FESolidAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FESolidAnalysis : public FEAnalysis\n{\npublic:\n\tenum SolidAnalysisType {\n\t\tSTATIC,\n\t\tDYNAMIC\n\t};\n\npublic:\n\tFESolidAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESolidDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolidDomainFactory.h\"\n#include \"FERigidMaterial.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FEElasticSolidDomain.h\"\n#include \"FEElasticShellDomain.h\"\n#include \"FELinearTrussDomain.h\"\n#include \"FERigidSolidDomain.h\"\n#include \"FERigidShellDomain.h\"\n#include \"FERemodelingElasticDomain.h\"\n#include \"FEUDGHexDomain.h\"\n#include \"FEUT4Domain.h\"\n#include \"FE3FieldElasticSolidDomain.h\"\n#include \"FEDiscreteElasticDomain.h\"\n#include \"FERemodelingElasticMaterial.h\"\n#include \"FECore/FEDiscreteMaterial.h\"\n#include \"FEDiscreteElementMaterial.h\"\n#include \"FESRIElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEDomain* FESolidDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tFEModel* pfem = pmat->GetFEModel();\n\tFE_Element_Class eclass = spec.eclass;\n\tFE_Element_Shape eshape = spec.eshape;\n\tFE_Element_Type etype = spec.etype;\n\n\t// this will store the domain we are going to allocate\n\tFEDomain* pd = nullptr;\n\n\t// try to allocate the domain\n\tif (eclass == FE_ELEM_SOLID)\n\t{\n\t\tif (dynamic_cast<FESolidMaterial*>(pmat) == nullptr) return nullptr;\n\n\t\tconst char* sztype = nullptr;\n\t\tif      (dynamic_cast<FERigidMaterial*            >(pmat)) sztype = \"rigid-solid\";\n\t\telse if (dynamic_cast<FERemodelingElasticMaterial*>(pmat)) sztype = \"remodeling-solid\";\n\t\telse if (eshape == ET_HEX8)\n\t\t{\n\t\t\t// three-field implementation for uncoupled materials\n\t\t\tif (dynamic_cast<FEUncoupledMaterial*>(pmat) && (spec.m_bthree_field)) sztype = \"three-field-solid\";\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (etype == FE_HEX8G1) sztype = \"udg-hex\";\n\t\t\t\telse sztype = \"elastic-solid\";\n\t\t\t}\n\t\t}\n\t\telse if ((eshape == ET_TET10) || (eshape == ET_TET15) || (eshape == ET_TET20))\n\t\t{\n\t\t\tif ((etype == FE_TET10G8RI4) || (etype == FE_TET10G4RI1))\n\t\t\t{\n\t\t\t\tsztype = \"sri-solid\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dynamic_cast<FEUncoupledMaterial*>(pmat) && (spec.m_bthree_field)) sztype = \"three-field-solid\";\n\t\t\t\telse sztype = \"elastic-solid\";\n\t\t\t}\n\t\t}\n\t\telse if ((eshape == ET_HEX20) || (eshape == ET_HEX27) || (eshape == ET_PYRA13))\n\t\t{\n//\t\t\tif (dynamic_cast<FEUncoupledMaterial*>(pmat) && (spec.m_bthree_field_hex)) sztype = \"three-field-solid\";\n\t\t\tsztype = \"elastic-solid\";\n\t\t}\n\t\telse if (eshape == ET_TET4)\n\t\t{\n\t\t\tif (spec.m_but4) sztype = \"ut4-solid\";\n\t\t\telse sztype = \"elastic-solid\";\n\t\t}\n\t\telse if (eshape == ET_TET5)\n\t\t{\n\t\t\tsztype = \"elastic-solid\";\n\t\t}\n\t\telse if (eshape == ET_PENTA6)\n\t\t{\n\t\t\t// three-field implementation for uncoupled materials\n\t\t\tif (dynamic_cast<FEUncoupledMaterial*>(pmat) && (spec.m_bthree_field)) sztype = \"three-field-solid\";\n\t\t\telse sztype = \"elastic-solid\";\n\t\t}\n\t\telse if (eshape == ET_PENTA15)\n\t\t{\n\t\t\t// three-field implementation for uncoupled materials\n//          if (dynamic_cast<FEUncoupledMaterial*>(pmat) && (spec.m_bthree_field_hex)) sztype = \"three-field-solid\";\n\t\t\tsztype = \"elastic-solid\";\n\t\t}\n\t\telse if (eshape == ET_PYRA5)\n\t\t{\n\t\t\t// three-field implementation for uncoupled materials\n\t\t\tif (dynamic_cast<FEUncoupledMaterial*>(pmat) && (spec.m_bthree_field)) sztype = \"three-field-solid\";\n\t\t\telse sztype = \"elastic-solid\";\n\t\t}\n\n\t\tif (sztype) pd = fecore_new<FESolidDomain>(sztype, pfem);\n\t}\n\telse if (eclass == FE_ELEM_SHELL)\n\t{\n\t\tif (dynamic_cast<FESolidMaterial*>(pmat) == nullptr) return nullptr;\n\n\t\tconst char* sztype = nullptr;\n\t\tif (dynamic_cast<FERigidMaterial*>(pmat))\n\t\t{\n\t\t\tif (spec.m_shell_formulation == OLD_SHELL) sztype = \"rigid-shell-old\";\n\t\t\telse sztype = \"rigid-shell\";\n\t\t}\n\t\telse if (dynamic_cast<FESolidMaterial*>(pmat))\n\t\t{\n\t\t\tif ((eshape == ET_QUAD4) || (eshape == ET_TRI3) || (eshape == ET_QUAD8) || (eshape == ET_TRI6))\n\t\t\t{\n\t\t\t\tswitch (spec.m_shell_formulation)\n\t\t\t\t{\n\t\t\t\tcase NEW_SHELL:\n\t\t\t\t\t// three-field implementation for uncoupled materials\n\t\t\t\t\tif (dynamic_cast<FEUncoupledMaterial*>(pmat)) {\n\t\t\t\t\t\tif (spec.m_bthree_field) sztype = \"three-field-shell\";\n\t\t\t\t\t\telse if ((eshape == ET_QUAD4) && spec.m_bthree_field) sztype = \"three-field-shell\";\n\t\t\t\t\t\telse if ((eshape == ET_TRI3) && spec.m_bthree_field) sztype = \"three-field-shell\";\n\t\t\t\t\t\telse sztype = \"elastic-shell\";\n\t\t\t\t\t}\n\t\t\t\t\telse sztype = \"elastic-shell\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase OLD_SHELL: sztype = \"elastic-shell-old\"; break;\n\t\t\t\tcase EAS_SHELL: sztype = \"elastic-shell-eas\"; break;\n\t\t\t\tcase ANS_SHELL: sztype = \"elastic-shell-ans\"; break;\n\t\t\t\tdefault:\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (sztype) pd = fecore_new<FEShellDomain>(sztype, pfem);\n\t}\n\telse if (eclass == FE_ELEM_TRUSS)\n\t{\n\t\tconst char* sztype = nullptr;\n\t\tif (dynamic_cast<FETrussMaterial*>(pmat))\n\t\t\tif (eshape == ET_TRUSS2) sztype = \"linear-truss\";\n\n\t\tif (sztype) pd = fecore_new<FETrussDomain>(sztype, pfem);\n\t}\n\telse if (eclass == FE_ELEM_DISCRETE)\n\t{\n\t\tconst char* sztype = nullptr;\n\t\tif (dynamic_cast<FEDiscreteMaterial*>(pmat))\n\t\t{\n\t//\t\tif      (eclass == FE_ELEM_WIRE    ) sztype = \"deformable-spring\";\n\t\t\tif      (eclass == FE_ELEM_WIRE    ) sztype = \"deformable-spring2\";\n\t\t\telse if (eclass == FE_ELEM_DISCRETE)\n\t\t\t{\n\t\t\t\tif (dynamic_cast<FEDiscreteElasticMaterial*>(pmat)) sztype = \"discrete\";\n\t\t\t}\n\t\t\telse return 0;\n\t\t}\n\n\t\tif (sztype) pd = fecore_new<FEDiscreteDomain>(sztype, pfem);\n\t}\n\n\tif (pd) pd->SetMaterial(pmat);\n\treturn pd;\n}\n"
  },
  {
    "path": "FEBioMech/FESolidDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\nclass FESolidDomainFactory : public FEDomainFactory\n{\npublic:\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioMech/FESolidLinearSystem.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FESolidLinearSystem.h\"\n#include \"FESolidSolver.h\"\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FEModel.h>\n\nFESolidLinearSystem::FESolidLinearSystem(FEModel* fem, FERigidSolver* rigidSolver, FEGlobalMatrix& K, std::vector<double>& F, std::vector<double>& u, bool bsymm, double alpha, int nreq) : FELinearSystem(fem, K, F, u, bsymm)\n{\n\tm_fem = fem;\n\tm_rigidSolver = rigidSolver;\n\tm_alpha = alpha;\n\tm_nreq = nreq;\n\tm_stiffnessScale = 1.0;\n}\n\n// scale factor for stiffness matrix\nvoid FESolidLinearSystem::StiffnessAssemblyScaleFactor(double a)\n{\n\tm_stiffnessScale = a;\n}\n\nvoid FESolidLinearSystem::Assemble(const FEElementMatrix& ke)\n{\n\t// Rigid joints require a different assembly approach in that we can do \n\t// a direct assembly as defined by the base class. \n\t// Currently, we assume that if the node list of the element matrix is not\n\t// defined, then we are dealing with rigid joints.\n\tif (ke.Nodes().empty())\n\t{\n\t\tFELinearSystem::Assemble(ke);\n\t}\n\telse\n\t{\n\t\t// assemble into global stiffness matrix\n\t\tif (m_stiffnessScale == 1.0)\n\t\t{\n\t\t\tm_K.Assemble(ke);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// NOTE: What is doing here?\n\t\t\tFEElementMatrix kes(ke, m_stiffnessScale);\n\t\t\tm_K.Assemble(kes);\n\t\t}\n\n\t\t// get the vector that stores the prescribed BC values\n\t\tvector<double>& ui = m_u;\n\n\t\t// adjust for linear constraints\n\t\tFELinearConstraintManager& LCM = m_fem->GetLinearConstraintManager();\n\t\tif (LCM.LinearConstraints() > 0)\n\t\t{\n\t\t\t#pragma omp critical (LCM_assemble)\n\t\t\tLCM.AssembleStiffness(m_K, m_F, m_u, ke.Nodes(), ke.RowIndices(), ke.ColumnsIndices(), ke);\n\t\t}\n\n\t\t// adjust stiffness matrix for prescribed degrees of freedom\n\t\t// NOTE: I had to comment this if statement out since otherwise\n\t\t//       poroelastic DOF's that are set as free-draining in the\n\t\t//       sliding2 contact code are skipt and zeroes will appear\n\t\t//       on the diagonal of the stiffness matrix.\n\t\t//\tif (m_fem.m_DC.size() > 0)\n\t\t{\n\t\t\tSparseMatrix& K = m_K;\n\n\t\t\tint N = ke.rows();\n\n\t\t\t// loop over columns\n\t\t\tconst vector<int>& elmi = ke.RowIndices();\n\t\t\tconst vector<int>& elmj = ke.ColumnsIndices();\n\t\t\tfor (int j = 0; j < N; ++j)\n\t\t\t{\n\t\t\t\tint J = -elmj[j] - 2;\n\t\t\t\tif ((J >= 0) && (J < m_nreq))\n\t\t\t\t{\n\t\t\t\t\t// dof j is a prescribed degree of freedom\n\n\t\t\t\t\t// loop over rows\n\t\t\t\t\tfor (int i = 0; i < N; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tint I = elmi[i];\n\t\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// dof i is not a prescribed degree of freedom\n\t\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\t\tm_F[I] -= ke[i][j] * ui[J];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// set the diagonal element of K to 1\n\t\t\t\t\tK.set(J, J, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// see if there are any rigid body dofs here\n\t\t#pragma omp critical (rigidStiffness)\n\t\tm_rigidSolver->RigidStiffness(m_K, m_u, m_F, ke, m_alpha);\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FESolidLinearSystem.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n\n#include <FECore/FELinearSystem.h>\n#include \"febiomech_api.h\"\n\nclass FERigidSolver;\n\nclass FEBIOMECH_API FESolidLinearSystem : public FELinearSystem\n{\npublic:\n\tFESolidLinearSystem(FEModel* fem, FERigidSolver* rigidSolver, FEGlobalMatrix& K, std::vector<double>& F, std::vector<double>& u, bool bsymm, double alpha, int nreq);\n\n\t// Assembly routine\n\t// This assembles the element stiffness matrix ke into the global matrix.\n\t// The contributions of prescribed degrees of freedom will be stored in m_F\n\tvoid Assemble(const FEElementMatrix& ke) override;\n\n\t// scale factor for stiffness matrix\n\tvoid StiffnessAssemblyScaleFactor(double a);\n\nprivate:\n\tFEModel* fem;\n\tFERigidSolver*\tm_rigidSolver;\n\tdouble\t\t\tm_alpha;\n\tint\t\t\t\tm_nreq;\n\n\tdouble\tm_stiffnessScale;\n};\n"
  },
  {
    "path": "FEBioMech/FESolidMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolidMaterial.h\"\n#include \"FEElasticMaterial.h\"\n\n// Material parameters for FEElasticMaterial\nBEGIN_FECORE_CLASS(FESolidMaterial, FEMaterial)\n\tADD_PARAMETER(m_density, FE_RANGE_GREATER_OR_EQUAL(0.0), \"density\")->setUnits(UNIT_DENSITY)->MakeTopLevel(true);\nEND_FECORE_CLASS();\n\nFESolidMaterial::FESolidMaterial(FEModel* pfem) : FEMaterial(pfem)\n{\n    m_density = 1.0;\n}\n\n//! set the material density\nvoid FESolidMaterial::SetDensity(const double d)\n{ \n\tm_density = d;\n}\n\n//! evaluate density\ndouble FESolidMaterial::Density(FEMaterialPoint& pt)\n{\n\treturn m_density(pt);\n}\n\ntens4dmm FESolidMaterial::SolidTangent(FEMaterialPoint& mp)\n{\n\treturn (UseSecantTangent() ? SecantTangent(mp) : Tangent(mp));\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FESolidMaterial::SecantStress(FEMaterialPoint& pt, bool PK2)\n{\n    assert(false);\n    return mat3ds(0.0);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the 2nd Piola-Kirchhoff stress at material point, using prescribed Lagrange strain\n//! needed for EAS analyses where the compatible strain (calculated from displacements) is enhanced\nmat3ds FESolidMaterial::PK2Stress(FEMaterialPoint& mp, const mat3ds E)\n{\n    // Evaluate right Cauchy-Green tensor from E\n    mat3ds C = mat3dd(1) + E*2;\n    \n    // Evaluate right stretch tensor U from C\n    vec3d v[3];\n    double lam[3];\n    C.eigen2(lam, v);\n    lam[0] = sqrt(lam[0]); lam[1] = sqrt(lam[1]); lam[2] = sqrt(lam[2]);\n    mat3ds U = dyad(v[0])*lam[0] + dyad(v[1])*lam[1] + dyad(v[2])*lam[2];\n    double J = lam[0]*lam[1]*lam[2];\n    \n    // temporarily replace F in material point with U\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d Fsafe = pt.m_F;\n    double Jsafe = pt.m_J;\n    pt.m_F = U;\n    pt.m_J = J;\n    \n    // Evaluate Cauchy stress\n    mat3ds s = Stress(mp);\n    \n    // Restore original F\n    pt.m_F = Fsafe;\n    pt.m_J = Jsafe;\n    \n    // Convert Cauchy stress to 2nd P-K stress\n    mat3ds Ui = dyad(v[0])/lam[0] + dyad(v[1])/lam[1] + dyad(v[2])/lam[2];\n    mat3ds S = (Ui*s*Ui).sym()*J;\n    \n    return S;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate material tangent stiffness at material point, using prescribed Lagrange strain\n//! needed for EAS analyses where the compatible strain (calculated from displacements) is enhanced\ntens4dmm FESolidMaterial::MaterialTangent(FEMaterialPoint& mp, const mat3ds E)\n{\n    // Evaluate right Cauchy-Green tensor from E\n    mat3ds C = mat3dd(1) + E*2;\n    \n    // Evaluate right stretch tensor U from C\n    vec3d v[3];\n    double lam[3];\n    C.eigen2(lam, v);\n    lam[0] = sqrt(lam[0]); lam[1] = sqrt(lam[1]); lam[2] = sqrt(lam[2]);\n    mat3d U = dyad(v[0])*lam[0] + dyad(v[1])*lam[1] + dyad(v[2])*lam[2];\n    double J = lam[0]*lam[1]*lam[2];\n    \n    // temporarily replace F in material point with U\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d Fsafe = pt.m_F;\n    double Jsafe = pt.m_J;\n    pt.m_F = U;\n    pt.m_J = J;\n    \n    // Evaluate Cauchy stress\n    tens4dmm c = SolidTangent(mp);\n    \n    // Restore original F\n    pt.m_F = Fsafe;\n    pt.m_J = Jsafe;\n    \n    // Convert spatial tangent to material tangent\n    mat3d Ui = dyad(v[0])/lam[0] + dyad(v[1])/lam[1] + dyad(v[2])/lam[2];\n    tens4dmm Cm = c.pp(Ui)*J;\n    \n    return Cm;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial tangent stiffness at material point, using secant method\ntens4dmm FESolidMaterial::SecantTangent(FEMaterialPoint& mp, bool mat)\n{\n    // extract the deformation gradient\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    mat3ds E = pt.Strain();\n    mat3dd I(1);\n\n    // calculate the 2nd P-K stress at the current deformation gradient\n    mat3ds S = PK2Stress(mp,E);\n    \n    // create deformation gradient increment\n    double eps = 1e-9;\n    vec3d e[3];\n    e[0] = vec3d(1,0,0); e[1] = vec3d(0,1,0); e[2] = vec3d(0,0,1);\n    tens4dmm C;\n    for (int k=0; k<3; ++k) {\n        for (int l=k; l<3; ++l) {\n            // evaluate incremental stress\n            mat3ds dE = dyads(e[k], e[l])*(eps/2);\n            mat3ds dS = (PK2Stress(mp,E+dE) - S)/eps;\n            \n            // evaluate the secant modulus\n            C(0,0,k,l) = C(0,0,l,k) = dS.xx();\n            C(1,1,k,l) = C(1,1,l,k) = dS.yy();\n            C(2,2,k,l) = C(2,2,l,k) = dS.zz();\n            C(0,1,k,l) = C(1,0,k,l) = C(0,1,l,k) = C(1,0,l,k) = dS.xy();\n            C(1,2,k,l) = C(2,1,k,l) = C(1,2,l,k) = C(2,1,l,k) = dS.yz();\n            C(2,0,k,l) = C(0,2,k,l) = C(2,0,l,k) = C(0,2,l,k) = dS.xz();\n        }\n    }\n    \n    if (mat) return C;\n    else {\n        \n        // push from material to spatial frame\n        tens4dmm c = C.pp(F)/J;\n        \n        // return secant tangent\n        return c;\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FESolidMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for solid-materials.\n//! These materials need to define the stress and tangent functions.\n//!\nclass FEBIOMECH_API FESolidMaterial : public FEMaterial\n{\npublic:\n\t//! constructor\n\tFESolidMaterial(FEModel* pfem);\n\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) = 0;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) = 0;\n\n\t//! calculate the 2nd Piola-Kirchhoff stress at material point\n\tvirtual mat3ds PK2Stress(FEMaterialPoint& pt, const mat3ds E);\n\n\t//! calculate material tangent stiffness at material point\n\tvirtual tens4dmm MaterialTangent(FEMaterialPoint& pt, const mat3ds E);\n\n    //! calculate secant tangent stiffness at material point\n    virtual tens4dmm SecantTangent(FEMaterialPoint& pt, bool mat = false);\n\n\t//! return the material density\n\tvoid SetDensity(const double d);\n\n\t//! evaluate density\n\tvirtual double Density(FEMaterialPoint& pt);\n\n\t//! Is this a rigid material or not\n\tvirtual bool IsRigid() const { return false; }\n\n\ttens4dmm SolidTangent(FEMaterialPoint& pt);\n\n\tvirtual mat3ds SecantStress(FEMaterialPoint& pt, bool PK2 = false);\n\tvirtual bool UseSecantTangent() { return false; }\n\nprotected:\n\tFEParamDouble\tm_density;\t//!< material density\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FESolidMaterial)\n};\n"
  },
  {
    "path": "FEBioMech/FESolidModule.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FESolidModule.h\"\n#include <FECore/DOFS.h>\n#include <FECore/FEModel.h>\n#include \"FEBioMech.h\"\n\nFESolidModule::FESolidModule() {}\n\nvoid FESolidModule::InitModel(FEModel* fem)\n{\n\t// Allocate degrees of freedom\n\tDOFS& dofs = fem->GetDOFS();\n\tint varD = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT), VAR_VEC3);\n\tdofs.SetDOFName(varD, 0, \"x\");\n\tdofs.SetDOFName(varD, 1, \"y\");\n\tdofs.SetDOFName(varD, 2, \"z\");\n\tint varQ = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION), VAR_VEC3);\n\tdofs.SetDOFName(varQ, 0, \"u\");\n\tdofs.SetDOFName(varQ, 1, \"v\");\n\tdofs.SetDOFName(varQ, 2, \"w\");\n\tint varQR = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION), VAR_VEC3);\n\tdofs.SetDOFName(varQR, 0, \"Ru\");\n\tdofs.SetDOFName(varQR, 1, \"Rv\");\n\tdofs.SetDOFName(varQR, 2, \"Rw\");\n\tint varV = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY), VAR_VEC3);\n\tdofs.SetDOFName(varV, 0, \"vx\");\n\tdofs.SetDOFName(varV, 1, \"vy\");\n\tdofs.SetDOFName(varV, 2, \"vz\");\n\tint varSU = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT), VAR_VEC3);\n\tdofs.SetDOFName(varSU, 0, \"sx\");\n\tdofs.SetDOFName(varSU, 1, \"sy\");\n\tdofs.SetDOFName(varSU, 2, \"sz\");\n\tint varSV = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY), VAR_VEC3);\n\tdofs.SetDOFName(varSV, 0, \"svx\");\n\tdofs.SetDOFName(varSV, 1, \"svy\");\n\tdofs.SetDOFName(varSV, 2, \"svz\");\n\tint varSA = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION), VAR_VEC3);\n\tdofs.SetDOFName(varSA, 0, \"sax\");\n\tdofs.SetDOFName(varSA, 1, \"say\");\n\tdofs.SetDOFName(varSA, 2, \"saz\");\n\tint varBW = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::BEAM_ANGULAR_VELOCITY), VAR_VEC3);\n\tdofs.SetDOFName(varBW, 0, \"bwx\");\n\tdofs.SetDOFName(varBW, 1, \"bwy\");\n\tdofs.SetDOFName(varBW, 2, \"bwz\");\n\tint varBA = dofs.AddVariable(FEBioMech::GetVariableName(FEBioMech::BEAM_ANGULAR_ACCELERATION), VAR_VEC3);\n\tdofs.SetDOFName(varBA, 0, \"bax\");\n\tdofs.SetDOFName(varBA, 1, \"bay\");\n\tdofs.SetDOFName(varBA, 2, \"baz\");\n}\n"
  },
  {
    "path": "FEBioMech/FESolidModule.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEModule.h>\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FESolidModule : public FEModule\n{\npublic:\n\tFESolidModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n"
  },
  {
    "path": "FEBioMech/FESolidSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolidSolver.h\"\n#include \"FE3FieldElasticSolidDomain.h\"\n#include \"FE3FieldElasticShellDomain.h\"\n#include \"FEResidualVector.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FEContactInterface.h\"\n#include \"FESolidLinearSystem.h\"\n#include <FECore/sys.h>\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEBodyLoad.h>\n#include <assert.h>\n#include \"FEBioMech.h\"\n#include \"FESolidAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FESolidSolver, FENewtonSolver)\n\tADD_PARAMETER(m_Dtol        , FE_RANGE_GREATER_OR_EQUAL(0.0), \"dtol\");\n\tADD_PARAMETER(m_Etol        , FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n\tADD_PARAMETER(m_Rtol        , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n\tADD_PARAMETER(m_beta        , \"beta\"        );\n\tADD_PARAMETER(m_gamma       , \"gamma\"       );\n\tADD_PARAMETER(m_bnew_update , \"use_new_rigid_update\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FESolidSolver Construction\n//\nFESolidSolver::FESolidSolver(FEModel* pfem) : FENewtonSolver(pfem), m_rigidSolver(pfem),\\\nm_dofU(pfem), m_dofV(pfem), m_dofSU(pfem), m_dofRQ(pfem)\n{\n\t// default values\n\tm_Rtol = 0;\t// deactivate residual convergence \n\tm_Dtol = 0.001;\n\tm_Etol = 0.01;\n\tm_Rmin = 1.0e-20;\n\n\tm_niter = 0;\n\tm_nreq = 0;\n\n\t// default Newmark parameters for unconditionally stable time integration\n\tm_beta = 0.25;\n\tm_gamma = 0.5;\n\n\tm_bnew_update = false;\n\n\tm_rigidSolver.AllowMixedBCs(true);\n\n\t// get the DOF indices\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofRQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFESolidSolver::~FESolidSolver()\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FESolidSolver\n//\nbool FESolidSolver::Init()\n{\n\t// initialize base class\n\tif (FENewtonSolver::Init() == false) return false;\n\n\t// allocate vectors\n\tint neq = m_neq;\n//\tm_Fn.assign(neq, 0);\n\tm_Fr.assign(neq, 0);\n\tm_Ui.assign(neq, 0);\n\tm_Ut.assign(neq, 0);\n\n\t// we need to fill the total displacement vector m_Ut\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, m_dofU[0]);\n\tgather(m_Ut, mesh, m_dofU[1]);\n\tgather(m_Ut, mesh, m_dofU[2]);\n\tgather(m_Ut, mesh, m_dofSU[0]);\n\tgather(m_Ut, mesh, m_dofSU[1]);\n\tgather(m_Ut, mesh, m_dofSU[2]);\n\n\t// set the dynamic update flag only if we are running a dynamic analysis\n\tbool b = (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC ? true : false);\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEElasticSolidDomain* d = dynamic_cast<FEElasticSolidDomain*>(&mesh.Domain(i));\n\t\tif (d) d->SetDynamicUpdateFlag(b);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FESolidSolver::Serialize(DumpStream& ar)\n{\n\t// Serialize parameters\n\tFENewtonSolver::Serialize(ar);\n\t\n\tar & m_nrhs;\n\tar & m_niter;\n\tar & m_nref & m_ntotref;\n\tar & m_naug;\n\tar & m_nreq;\n\n\tif (ar.IsLoading())\n\t{\n\t\t// re-initialize data\n\t\tInit();\n\t}\n\n\tm_rigidSolver.Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\n//! Determine the number of linear equations and assign equation numbers\n//!\tThis function initializes the equation system.\n//! It is assumed that all free dofs up until now have been given an ID >= 0\n//! and the fixed or rigid dofs an ID < 0.\n//! After this operation the nodal ID array will contain the equation\n//! number assigned to the corresponding degree of freedom. To distinguish\n//! between free or unconstrained dofs and constrained ones the following rules\n//! apply to the ID array:\n//!\n//!           /\n//!          |  >=  0 --> dof j of node i is a free dof\n//! ID[i][j] <  == -1 --> dof j of node i is a fixed (no equation assigned too)\n//!          |  <  -1 --> dof j of node i is constrained and has equation nr = -ID[i][j]-2\n//!           \\\n//!\nbool FESolidSolver::InitEquations()\n{\n\t// First call the base class.\n\t// This will initialize all equation numbers, except the rigid body equation numbers\n\tif (FENewtonSolver::InitEquations() == false) return false;\n\n\t// store the number of equations we currently have\n\tm_nreq = m_neq;\n\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tint neq = m_rigidSolver.InitEquations(m_neq);\n\tif (neq == -1) return false;\n\telse m_neq = neq;\n\n\t// All initialization is done\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FESolidSolver::UpdateKinematics(vector<double>& ui)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update rigid bodies\n\t// (this also updates the kinematics of rigid nodes)\n\tm_rigidSolver.UpdateRigidBodies(m_Ui, ui, m_bnew_update);\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tfor (size_t i=0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n\t// update flexible nodes\n\t// translational dofs\n\tscatter(U, mesh, m_dofU[0]);\n\tscatter(U, mesh, m_dofU[1]);\n\tscatter(U, mesh, m_dofU[2]);\n\t// rotational dofs\n\tscatter(U, mesh, m_dofSU[0]);\n\tscatter(U, mesh, m_dofSU[1]);\n\tscatter(U, mesh, m_dofSU[2]);\n\n\t// make sure the boundary conditions are fullfilled\n\tint nbcs = fem.BoundaryConditions();\n\tfor (int i = 0; i<nbcs; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Update();\n\t}\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// Update the spatial nodal positions\n\t// Don't update rigid nodes since they are already updated\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid == -1)\n\t\t\tnode.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n\t}\n\n\t// update velocity and accelerations\n\t// for dynamic simulations\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tif (pstep->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t{\n\t\tint N = mesh.Nodes();\n\t\tdouble dt = fem.GetTime().timeIncrement;\n\t\tdouble a = 1.0 / (m_beta*dt);\n\t\tdouble b = a / dt;\n\t\tdouble c = 1.0 - 0.5/m_beta;\n\t\tfor (int i = 0; i<N; ++i)\n\t\t{\n\t\t\tFENode& n = mesh.Node(i);\n\t\t\tn.m_at = (n.m_rt - n.m_rp)*b - n.m_vp*a + n.m_ap*c;\n\t\t\tvec3d vt = n.m_vp + (n.m_ap*(1.0 - m_gamma) + n.m_at*m_gamma)*dt;\n\t\t\tn.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n\t\t}\n\n\t\t// update the rigid body kinematics\n\t\tm_rigidSolver.UpdateRigidKinematics();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FESolidSolver::Update(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// update kinematics\n\tUpdateKinematics(ui);\n\n\t// update element stresses\n\tfem.Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration. \nvoid FESolidSolver::PrepStep()\n{\n\t// zero total displacements\n\tzero(m_Ui);\n\n\tconst FETimeInfo& tp = GetFEModel()->GetTime();\n\tdouble dt = tp.timeIncrement;\n\n\t// store previous mesh state\n\t// we need them for velocity and acceleration calculations\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& ni = mesh.Node(i);\n\t\tni.m_rp = ni.m_rt;\n\t\tni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tni.m_ap = ni.m_at;\n\n\t\t// initial guess at start of new time step\n\t\tni.m_at = ni.m_ap * (1 - 0.5 / m_beta) - ni.m_vp / (m_beta * dt);\n\t\tvec3d vs = ni.m_vp + (ni.m_at * m_gamma + ni.m_ap * (1 - m_gamma)) * dt;\n\t\tni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vs);\n\t}\n\n\t// apply concentrated nodal forces\n\t// since these forces do not depend on the geometry\n\t// we can do this once outside the NR loop.\n//\tvector<double> dummy(m_neq, 0.0);\n//\tzero(m_Fn);\n//\tFEResidualVector Fn(*GetFEModel(), m_Fn, dummy);\n//\tNodalLoads(Fn, tp);\n\n\t// apply boundary conditions\n\t// we save the prescribed displacements increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i = 0; i<nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& dc = *fem.BoundaryCondition(i);\n\t\tif (dc.IsActive()) dc.PrepStep(ui);\n\t}\n\n\t// initialize rigid bodies\n\tm_rigidSolver.PrepStep(tp, ui);\n\n\t// intialize material point data\n\t// NOTE: do this before the stresses are updated\n\t// TODO: does it matter if the stresses are updated before\n\t//       the material point data is initialized\n\tfor (int i=0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n\n\t// update stresses\n\tfem.Update();\n\n\t// see if we need to do contact augmentations\n\tm_baugment = false;\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n\t\tif (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n\t}\n\n\t// see if we need to do incompressible augmentations\n\t// TODO: Should I do these augmentations in a nlconstraint class instead?\n\tint ndom = mesh.Domains();\n\tfor (int i = 0; i < ndom; ++i)\n\t{\n\t\tFEDomain* dom = &mesh.Domain(i);\n\t\tFE3FieldElasticSolidDomain* dom3f = dynamic_cast<FE3FieldElasticSolidDomain*>(dom);\n\t\tif (dom3f && dom3f->DoAugmentations()) m_baugment = true;\n\n\t\tFE3FieldElasticShellDomain* dom3fs = dynamic_cast<FE3FieldElasticShellDomain*>(dom);\n\t\tif (dom3fs && dom3fs->DoAugmentations()) m_baugment = true;\n\t}\n\n\t// see if we have to do nonlinear constraint augmentations\n\tfor (int i=0; i<fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint& ci = *fem.NonlinearConstraint(i);\n\t\tif (ci.IsActive()) m_baugment = true;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Implements the BFGS algorithm to solve the nonlinear FE equations.\nbool FESolidSolver::Quasin()\n{\n\tvector<double> u0(m_neq);\n\tvector<double> Rold(m_neq);\n\n\t// convergence norms\n\tdouble\tnormR1;\t\t// residual norm\n\tdouble\tnormE1;\t\t// energy norm\n\tdouble\tnormU;\t\t// displacement norm\n\tdouble\tnormu;\t\t// displacement increment norm\n\tdouble\tnormRi;\t\t// initial residual norm\n\tdouble\tnormEi;\t\t// initial energy norm\n\tdouble\tnormEm;\t\t// max energy norm\n\tdouble\tnormUi;\t\t// initial displacement norm\n\n\t// Get the current step\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// prepare for the first iteration\n\tPrepStep();\n\n\t// Init QN method\n\tif (QNInit() == false) return false;\n\n\t// loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter+1);\n\n\t\t// assume we'll converge. \n\t\tbconv = true;\n\n\t\t// solve the equations (returns line search; solution stored in m_ui)\n\t\tdouble s = QNSolve();\n\n\t\t// set initial convergence norms\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tnormRi = fabs(m_R0*m_R0);\n\t\t\tnormEi = fabs(m_ui*m_R0);\n\t\t\tnormUi = fabs(m_ui*m_ui);\n\t\t\tnormEm = normEi;\n\t\t}\n\n\t\t// calculate norms\n\t\tnormR1 = m_R1*m_R1;\n\t\tnormu  = (m_ui*m_ui)*(s*s);\n\t\tnormE1 = s*fabs(m_ui*m_R1);\n\n\t\t// check for nans\n\t\tif (ISNAN(normR1)) throw NANInResidualDetected();\n\t\tif (ISNAN(normu)) throw NANInSolutionDetected();\n\n\t\t// update total displacements\n\t\tint neq = (int)m_Ui.size();\n\t\tfor (int i = 0; i<neq; ++i) m_Ui[i] += s*m_ui[i];\n\t\tnormU  = m_Ui*m_Ui;\n\n\t\t// check residual norm\n\t\tif ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\t\n\n\t\t// check displacement norm\n\t\tif ((m_Dtol > 0) && (normu  > (m_Dtol*m_Dtol)*normU )) bconv = false;\n\n\t\t// check energy norm\n\t\tif ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n\n\t\t// check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n\n\t\t// check energy divergence\n\t\tif (m_bdivreform)\n\t\t{\n\t\t\tif (normE1 > normEm) bconv = false;\n\t\t}\n\n\t\t// print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime); \n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n\t\tfeLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n\t\tfeLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n\t\tfeLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n\t\tfeLog(\"\\t   displacement     %15le %15le %15le \\n\", normUi, normu ,(m_Dtol*m_Dtol)*normU );\n\n\t\t// see if we may have a small residual\n\t\tif ((bconv == false) && (normR1 < m_Rmin))\n\t\t{\n\t\t\t// check for almost zero-residual on the first iteration\n\t\t\t// this might be an indication that there is no force on the system\n\t\t\tfeLogWarning(\"No force acting on the system.\");\n\t\t\tbconv = true;\n\t\t}\n\n\t\t// check if we have converged. \n\t\t// If not, calculate the BFGS update vectors\n\t\tif (bconv == false)\n\t\t{\n\t\t\tif (s < m_lineSearch->m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\t\t\telse if ((normE1 > normEm) && m_bdivreform)\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n\t\t\t\tnormEm = normE1;\n\t\t\t\tnormEi = normE1;\n\t\t\t\tnormRi = normR1;\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// Do augmentations\n\t\t\tbconv = DoAugmentations();\n\t\t}\n\t\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile (bconv == false);\n\n\t// if converged we update the total displacements\n\tif (bconv)\n\t{\n\t\tm_Ut += m_Ui;\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\nbool FESolidSolver::StiffnessMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// setup the linear syster\n\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), 1.0, m_nreq);\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// calculate the stiffness matrix for each domain\n\tfor (int i=0; i<mesh.Domains(); ++i) \n\t{\n\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n\t\tdom.StiffnessMatrix(LS);\n\t}\n\n\t// calculate the model load stiffness matrix\n\tint NML = fem.ModelLoads();\n\tfor (int j = 0; j<NML; ++j)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(j);\n\t\tif (pml->IsActive()) pml->StiffnessMatrix(LS);\n\t}\n\n\t// Add mass matrix for dynamic problems\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tif (pstep->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t{\n\t\t// scale factor\n\t\tdouble dt = tp.timeIncrement;\n\t\tdouble a = 1.0 / (m_beta*dt*dt);\n\n\t\t// loop over all domains\n\t\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n\t\t\tdom.MassMatrix(LS, a);\n\t\t}\n\t}\n\n\t// calculate contact stiffness\n\tif (fem.SurfacePairConstraints() > 0)\n\t{\n\t\tContactStiffness(LS);\n\t}\n\n\t// calculate stiffness matrices for surface loads\n/*\tint nsl = fem.SurfaceLoads();\n\tfor (int i = 0; i<nsl; ++i)\n\t{\n\t\tFESurfaceLoad* psl = fem.SurfaceLoad(i);\n\t\tif (psl->IsActive())\n\t\t{\n\t\t\tpsl->StiffnessMatrix(LS, tp); \n\t\t}\n\t}\n*/\n\t// calculate nonlinear constraint stiffness\n\t// note that this is the contribution of the \n\t// constrainst enforced with augmented lagrangian\n\tNonLinearConstraintStiffness(LS, tp);\n\n\t// calculate the stiffness contributions for the rigid forces\n\tfor (int i = 0; i<fem.ModelLoads(); ++i) fem.ModelLoad(i)->StiffnessMatrix(LS);\n\n\t// we still need to set the diagonal elements to 1\n\t// for the prescribed rigid body dofs.\n\tm_rigidSolver.StiffnessMatrix(*m_pK, tp);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FESolidSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i=0; i<N; ++i) \n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FESolidSolver::ContactStiffness(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FESolidSolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->LoadVector(R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FESolidSolver::Residual(vector<double>& R)\n{\n\t// get the time information\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// initialize residual with concentrated nodal loads\n\tzero(R);// = m_Fn;\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup the global vector\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tm_rigidSolver.Residual();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// calculate the internal (stress) forces\n\tfor (int i=0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n\t\tdom.InternalForces(RHS);\n\t}\n\n\t// calculate the body forces\n\tfor (int j = 0; j<fem.ModelLoads(); ++j)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(j);\n\t\tif (pml->IsActive()) pml->LoadVector(RHS);\n\t}\n\n\t// calculate inertial forces for dynamic problems\n\tif (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC) InertialForces(RHS);\n\n\t// calculate forces due to surface loads\n/*\tint nsl = fem.SurfaceLoads();\n\tfor (int i=0; i<nsl; ++i)\n\t{\n\t\tFESurfaceLoad* psl = fem.SurfaceLoad(i);\n\t\tif (psl->IsActive()) psl->LoadVector(RHS, tp);\n\t}\n*/\n\t// calculate contact forces\n\tContactForces(RHS);\n\n\t// calculate nonlinear constraint forces\n\t// note that these are the linear constraints\n\t// enforced using the augmented lagrangian\n\tNonLinearConstraintForces(RHS, tp);\n\n\t// forces due to point constraints\n//\tfor (i=0; i<(int) fem.m_PC.size(); ++i) fem.m_PC[i]->Residual(this, R);\n\n\t// add model loads\n/*\tint NML = fem.ModelLoads();\n\tfor (int i=0; i<NML; ++i)\n\t{\n\t\tFEModelLoad& mli = *fem.ModelLoad(i);\n\t\tif (mli.IsActive())\n\t\t{\n\t\t\tmli.LoadVector(RHS, tp);\n\t\t}\n\t}\n*/\n\t// set the nodal reaction forces\n\t// TODO: Is this a good place to do this?\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces \nvoid FESolidSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i=0; i<N; ++i) \n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->LoadVector(R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the inertial forces for dynamic problems\n\nvoid FESolidSolver::InertialForces(FEGlobalVector& R)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// allocate F\n\tvector<double> F(3*mesh.Nodes());\n\tzero(F);\n\n\t// get the time information\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// calculate F\n\tdouble dt = tp.timeIncrement;\n\tdouble a = 1.0 / (m_beta*dt);\n\tdouble b = a / dt;\n\tdouble c = 1.0 - 0.5/m_beta;\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tvec3d& rt = node.m_rt;\n\t\tvec3d& rp = node.m_rp;\n\t\tvec3d& vp = node.m_vp;\n\t\tvec3d& ap = node.m_ap;\n\n\t\tF[3*i  ] = b*(rt.x - rp.x) - a*vp.x + c * ap.x;\n\t\tF[3*i+1] = b*(rt.y - rp.y) - a*vp.y + c * ap.y;\n\t\tF[3*i+2] = b*(rt.z - rp.z) - a*vp.z + c * ap.z;\n\t}\n\n\t// now multiply F with the mass matrix\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(nd));\n\t\tdom.InertialForces(R, F);\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FESolidSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FECore/FENewtonSolver.h\"\n#include <FECore/FETimeInfo.h>\n#include \"FECore/FEGlobalVector.h\"\n#include \"FERigidSolver.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! The FESolidSolver class solves large deformation solid mechanics problems\n//! It can deal with quasi-static and dynamic problems\n//! \nclass FESolidSolver : public FENewtonSolver\n{\npublic:\n\t//! constructor\n\tFESolidSolver(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FESolidSolver();\n\n\t//! serialize data to/from dump file\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Initializes data structures\n\tbool Init() override;\n\n\t//! Initialize linear equation system\n\tbool InitEquations() override;\n\n\t// get the rigid solver\n\tFERigidSolver* GetRigidSolver() { return &m_rigidSolver; }\n\npublic:\n\t//{ --- evaluation and update ---\n\t\t//! Perform an update\n\t\tvoid Update(vector<double>& ui) override;\n\t//}\n\n\t//{ --- Solution functions ---\n\n\t\t//! prepares the data for the first QN iteration\n\t\tvoid PrepStep() override;\n\n\t\t//! Performs a Newton-Raphson iteration\n\t\tbool Quasin() override;\n\n\t\t//! update nodal positions, velocities, accelerations, etc.\n\t\tvirtual void UpdateKinematics(vector<double>& ui);\n\t//}\n\n\t//{ --- Stiffness matrix routines ---\n\n\t\t//! calculates the global stiffness matrix\n\t\tvirtual bool StiffnessMatrix() override;\n\n\t\t//! contact stiffness\n\t\tvoid ContactStiffness(FELinearSystem& LS);\n\n\t\t//! calculates stiffness contributon of nonlinear constraints\n\t\tvoid NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n\t//}\n\n\t//{ --- Residual routines ---\n\n\t\t//! Calculate inertial forces for dynamic problems\n\t\tvoid InertialForces(FEGlobalVector& R);\n\n\t\t//! Calculate the contact forces\n\t\tvoid ContactForces(FEGlobalVector& R);\n\n\t\t//! Calculates residual\n\t\tvirtual bool Residual(vector<double>& R) override;\n\n\t\t//! Calculate nonlinear constraint forces\n\t\tvoid NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\t//}\n\npublic:\n\t// convergence tolerances\n\tdouble\tm_Dtol;\t\t\t//!< displacement tolerance\n\n\t// equation numbers\n\tint\t\tm_nreq;\t\t\t//!< start of rigid body equations\n\n\t// Newmark parameters (for dynamic analyses)\n\tdouble\tm_beta;\t\t\t//!< Newmark parameter beta (displacement integration)\n\tdouble\tm_gamma;\t\t//!< Newmark parameter gamme (velocity integration)\n\npublic:\n\tvector<double> m_Fr;\t//!< nodal reaction forces\n\npublic:\n\tbool\tm_bnew_update;\t//!< use new rigid body update algorithm\n\nprotected:\n\tFEDofList\tm_dofU, m_dofV, m_dofSU, m_dofRQ;\n\nprotected:\n\tFERigidSolverOld\tm_rigidSolver;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESolidSolver2.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolidSolver2.h\"\n#include \"FERigidConnector.h\"\n#include \"FESlidingElasticInterface.h\"\n#include \"FE3FieldElasticSolidDomain.h\"\n#include \"FE3FieldElasticShellDomain.h\"\n#include \"FEElasticEASShellDomain.h\"\n#include \"FEElasticANSShellDomain.h\"\n#include \"FEBodyForce.h\"\n#include \"FEResidualVector.h\"\n#include \"FEUncoupledMaterial.h\"\n#include \"FEContactInterface.h\"\n#include \"FESSIShellDomain.h\"\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FECore/sys.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/LinearSolver.h>\n#include <FECore/vector.h>\n#include \"FESolidLinearSystem.h\"\n#include \"FEBioMech.h\"\n#include \"FESolidAnalysis.h\"\n#include \"FETrussMaterial.h\"\n#include \"FELinearTrussDomain.h\"\n#include \"FEMechModel.h\"\n#include \"FERigidBody.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FESolidSolver2, FENewtonSolver)\n\tBEGIN_PARAM_GROUP(\"Nonlinear solver\");\t// make sure this matches FENewtonSolver. \n\t\tADD_PARAMETER(m_Dtol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"dtol\"        );\n\t\tADD_PARAMETER(m_Etol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n\t\tADD_PARAMETER(m_Rtol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n\t\tADD_PARAMETER(m_rhoi      , \"rhoi\"        );\n\t\tADD_PARAMETER(m_alpha     , \"alpha\"       );\n\t\tADD_PARAMETER(m_beta      , \"beta\"        );\n\t\tADD_PARAMETER(m_gamma     , \"gamma\"       );\n\t\tADD_PARAMETER(m_logSolve  , \"logSolve\"    );\n\t\tADD_PARAMETER(m_arcLength , \"arc_length\"  );\n\t\tADD_PARAMETER(m_al_scale  , \"arc_length_scale\");\n\t\tADD_PARAMETER(m_init_accelerations, \"init_accelerations\")->SetFlags(FE_PARAM_HIDDEN);\n\tEND_PARAM_GROUP();\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FESolidSolver2 Construction\n//\nFESolidSolver2::FESolidSolver2(FEModel* pfem) : FENewtonSolver(pfem), m_rigidSolver(pfem),\\\nm_dofU(pfem), m_dofV(pfem), m_dofQ(pfem), m_dofRQ(pfem), m_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem),\nm_dofBW(pfem), m_dofBA(pfem)\n{\n\t// default values\n\tm_Rtol = 0;\t// deactivate residual convergence \n\tm_Dtol = 0.001;\n\tm_Etol = 0.01;\n\tm_Rmin = 1.0e-20;\n\tm_Rmax = 0;\t// not used if zero\n\n\tm_niter = 0;\n\tm_nreq = 0;\n\n\tm_logSolve = false;\n\n\t// default Newmark parameters (trapezoidal rule)\n    m_rhoi = -2;\n    m_alpha = m_alphaf = 1.0;\n    m_alpham = 1.0;\n\tm_beta  = 0.25;\n\tm_gamma = 0.5;\n\n\tm_init_accelerations = true;\n\n\t// arc-length parameters\n\tm_arcLength = ARC_LENGTH_METHOD::NONE; // no arc-length\n\tm_al_scale = 0.0;\n\tm_al_lam = 0.0;\n\tm_al_inc = 0.0;\n\tm_al_ds = 0.0;\n\n\tm_solutionNorm.push_back(ConvergenceInfo());\n\n    // get the DOF indices\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::ROTATION));\n\t\tm_dofRQ.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t\tm_dofV.AddVariable(FEBioMech::GetVariableName(FEBioMech::VELOCITY));\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofSV.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_VELOCITY));\n\t\tm_dofSA.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_ACCELERATION));\n\t\tm_dofBW.AddVariable(FEBioMech::GetVariableName(FEBioMech::BEAM_ANGULAR_VELOCITY));\n\t\tm_dofBA.AddVariable(FEBioMech::GetVariableName(FEBioMech::BEAM_ANGULAR_ACCELERATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFESolidSolver2::~FESolidSolver2()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! Return the rigid solver\nFERigidSolver* FESolidSolver2::GetRigidSolver()\n{\n\treturn &m_rigidSolver;\n}\n\n//-----------------------------------------------------------------------------\n//! Generate warnings if needed\nvoid FESolidSolver2::SolverWarnings()\n{\n\tFEModel& fem = *GetFEModel();\n\n    // Generate warning if rigid connectors are used with symmetric stiffness\n    if (m_msymm == REAL_SYMMETRIC) {\n        for (int i=0; i<fem.NonlinearConstraints(); ++i)\n        {\n            FENLConstraint* plc = fem.NonlinearConstraint(i);\n            FERigidConnector* prc = dynamic_cast<FERigidConnector*>(plc);\n            if (prc) {\n                feLogWarning(\"Rigid connectors require non-symmetric stiffness matrix.\\nSet symmetric_stiffness flag to 0 in Control section.\");\n                break;\n            }\n        }\n        \n        // Generate warning if sliding-elastic contact is used with symmetric stiffness\n\t\tif (fem.SurfacePairConstraints() > 0)\n        {\n            // loop over all contact interfaces\n\t\t\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n            {\n\t\t\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n                FESlidingElasticInterface* pbw = dynamic_cast<FESlidingElasticInterface*>(pci);\n                if (pbw) {\n\t\t\t\t\tfeLogWarning(\"The sliding-elastic contact algorithm runs better with a non-symmetric stiffness matrix.\\nYou may set symmetric_stiffness flag to 0 in Control section.\");\n                    break;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures used by the FESolidSolver2\n//\nbool FESolidSolver2::Init()\n{\n\t// initialize base class\n\tif (FENewtonSolver::Init() == false) return false;\n\n\tFEModel& fem = *GetFEModel();\n\n\tif (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t{\n\t\tif (m_rhoi == -1) {\n\t\t\t// Euler integration\n\t\t\tm_alpha = m_alphaf = m_alpham = 1.0;\n\t\t\tm_beta = pow(1 + m_alpham - m_alphaf, 2) / 4;\n\t\t\tm_gamma = 0.5 + m_alpham - m_alphaf;\n\t\t}\n\t\telse if ((m_rhoi >= 0) && (m_rhoi <= 1)) {\n\t\t\t// Generalized-alpha integration (2nd order system)\n\t\t\tm_alpha = m_alphaf = 1.0 / (1 + m_rhoi);\n\t\t\tm_alpham = (2 - m_rhoi) / (1 + m_rhoi);\n\t\t\tm_beta = pow(1 + m_alpham - m_alphaf, 2) / 4;\n\t\t\tm_gamma = 0.5 + m_alpham - m_alphaf;\n\t\t}\n\t\telse {\n\t\t\t// for any other value of rhoi, use the user-defined alpha, beta, gamma parameters\n\t\t\tm_alphaf = m_alpham = m_alpha;\n\t\t}\n\t}\n\telse\n\t{\n\t\tm_alpha = m_alphaf = m_alpham = 1.0;\n\t}\n\n\t// allocate vectors\n//\tm_Fn.assign(m_neq, 0);\n\tm_Fr.assign(m_neq, 0);\n\tm_Ui.assign(m_neq, 0);\n\tm_Ut.assign(m_neq, 0);\n\tm_Uip.assign(m_neq, 0);\n\n\t// we need to fill the total displacement vector m_Ut\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, m_dofU[0]);\n\tgather(m_Ut, mesh, m_dofU[1]);\n\tgather(m_Ut, mesh, m_dofU[2]);\n\tgather(m_Ut, mesh, m_dofQ[0]);\n\tgather(m_Ut, mesh, m_dofQ[1]);\n\tgather(m_Ut, mesh, m_dofQ[2]);\n    gather(m_Ut, mesh, m_dofSU[0]);\n    gather(m_Ut, mesh, m_dofSU[1]);\n    gather(m_Ut, mesh, m_dofSU[2]);\n\n    SolverWarnings();\n\n\tif (m_arcLength > 0)\n\t{\n\t\tm_Fint.assign(m_neq, 0.0);\n\t\tm_Fext.assign(m_neq, 0.0);\n\t}\n    \n\t// set the dynamic update flag only if we are running a dynamic analysis\n\tbool b = (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC ? true : false);\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEElasticSolidDomain* d = dynamic_cast<FEElasticSolidDomain*>(&mesh.Domain(i));\n        FEElasticShellDomain* s = dynamic_cast<FEElasticShellDomain*>(&mesh.Domain(i));\n        FEElasticEASShellDomain* seas = dynamic_cast<FEElasticEASShellDomain*>(&mesh.Domain(i));\n        FEElasticANSShellDomain* sans = dynamic_cast<FEElasticANSShellDomain*>(&mesh.Domain(i));\n\t\tif (d) d->SetDynamicUpdateFlag(b);\n        if (s) s->SetDynamicUpdateFlag(b);\n        if (seas) seas->SetDynamicUpdateFlag(b);\n        if (sans) sans->SetDynamicUpdateFlag(b);\n\t}\n\n\t// For dynamic problems we need to calculate the initial accelerations\n\t// TODO: We currently only do this when time == 0. But what if the first step is static and the\n\t// second is dynamic? \n\tif (m_init_accelerations)\n\t{\n\t\tFEAnalysis* pstep = fem.GetCurrentStep();\n\t\tdouble currentTime = fem.GetTime().currentTime;\n\t\tif ((pstep->m_nanalysis == FESolidAnalysis::DYNAMIC) && (currentTime == 0.0))\n\t\t{\n\t\t\tif (InitAccelerations() == false) return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool FESolidSolver2::InitAccelerations()\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// calculate applied force vector\n\t// TODO: What if there is internal stress at time 0? \n\tvector<double> F(m_neq, 0.0), dummy(m_neq, 0.0);\n\tFEResidualVector RHS(fem, F, dummy);\n\tExternalForces(RHS);\n\n\t// Only calculate initial accelerations \n\t// if a nonzero inital force is applied.\n\tdouble f_norm = sqrt(F * F);\n\tif (f_norm > 0)\n\t{\n\t\t// We need to solve the linear system of equations F = M*a\n\t\t// So, we build the mass matrix and then solve for a\n\t\tconst FETimeInfo& tp = fem.GetTime();\n\n\t\t// Form the stiffness matrix\n\t\tif (!CreateStiffness(true)) return false;\n\n\t\t// setup the linear system\n\t\tm_pK->Zero();\n\t\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), 1.0, m_nreq);\n\n\t\t// build the global mass matrix\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\t\tif (edom) edom->MassMatrix(LS, 1.0);\n\t\t}\n\t\tm_rigidSolver.RigidMassMatrix(LS, tp);\n\n\t\t// Don't forget to factor the matrix first!\n\t\tif (m_plinsolve == nullptr) return false;\n\t\tif (m_plinsolve->Factor() == false)\n\t\t{\n\t\t\tthrow FactorizationError();\n\t\t}\n\n\t\t// Solve for the initial accelerations\n\t\tvector<double> a0(m_neq);\n\t\tSolveEquations(a0, F);\n\n\t\t// apply to nodes\n\t\tfor (int i = 0, n; i < mesh.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& nodei = mesh.Node(i);\n\t\t\tn = nodei.m_ID[m_dofU[0]]; if (n >= 0) nodei.m_at.x = a0[n];\n\t\t\tn = nodei.m_ID[m_dofU[1]]; if (n >= 0) nodei.m_at.y = a0[n];\n\t\t\tn = nodei.m_ID[m_dofU[2]]; if (n >= 0) nodei.m_at.z = a0[n];\n\n\t\t\tvec3d aqt(0, 0, 0);\n\t\t\tn = nodei.m_ID[m_dofSU[0]]; if (n >= 0) aqt.x = a0[n];\n\t\t\tn = nodei.m_ID[m_dofSU[1]]; if (n >= 0) aqt.y = a0[n];\n\t\t\tn = nodei.m_ID[m_dofSU[2]]; if (n >= 0) aqt.z = a0[n];\n\t\t\tnodei.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n\t\t}\n\n\t\t// apply to rigid bodies\n\t\tFEMechModel& mech = dynamic_cast<FEMechModel&>(fem);\n\t\tfor (int i = 0, n; i < mech.RigidBodies(); ++i)\n\t\t{\n\t\t\tFERigidBody& rb = *mech.GetRigidBody(i);\n\t\t\tn = rb.m_LM[0]; if (n >= 0) rb.m_at.x = rb.m_ap.x = a0[n];\n\t\t\tn = rb.m_LM[1]; if (n >= 0) rb.m_at.y = rb.m_ap.y = a0[n];\n\t\t\tn = rb.m_LM[2]; if (n >= 0) rb.m_at.z = rb.m_ap.z = a0[n];\n\t\t\tn = rb.m_LM[3]; if (n >= 0) rb.m_alt.x = rb.m_alp.x = a0[n];\n\t\t\tn = rb.m_LM[4]; if (n >= 0) rb.m_alt.y = rb.m_alp.y = a0[n];\n\t\t\tn = rb.m_LM[5]; if (n >= 0) rb.m_alt.z = rb.m_alp.z = a0[n];\n\t\t}\n\n\t\t// since rigid nodes don't get equations assigned, we'll grab\n\t\t// their initial accelerations from the rigid bodies\n\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& nodei = mesh.Node(i);\n\t\t\tif (nodei.m_rid >= 0)\n\t\t\t{\n\t\t\t\tFERigidBody& rb = *mech.GetRigidBody(nodei.m_rid);\n\t\t\t\t// TODO: What if the rb has initial rotation or rotational acceleration?\n\t\t\t\tnodei.m_at = rb.m_at;\n\t\t\t\tnodei.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], rb.m_at);\n\t\t\t}\n\t\t}\n\n\t\t// TODO: What if there are linear constraints present? We'll probably need to do\n\t\t//       something similar for the constrained nodes since they also don't have equations assigned.\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FESolidSolver2::Serialize(DumpStream& ar)\n{\n\t// Serialize parameters\n\tFENewtonSolver::Serialize(ar);\n\t\n\tar & m_nrhs;\n\tar & m_niter;\n\tar & m_nref & m_ntotref;\n\tar & m_naug;\n\tar & m_nreq;\n\n\tar & m_alphaf;\n\tar & m_alpham;\n\n\tar & m_Ut & m_Ui;\n\n\tar & m_arcLength;\n\tar & m_al_scale;\n\tar & m_al_lam & m_al_inc;// &m_al_ds;\n\n\tif (ar.IsLoading())\n\t{\n//\t\tm_Fn.assign(m_neq, 0);\n\t\tm_Fr.assign(m_neq, 0);\n//\t\tm_Ui.assign(m_neq, 0);\n\t}\n\n\t// serialize rigid solver\n\tm_rigidSolver.Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\nbool FESolidSolver2::InitEquations()\n{\n\t// First call the base class.\n\t// This will initialize all equation numbers, except the rigid body equation numbers\n\tif (FENewtonSolver::InitEquations() == false) return false;\n\n\t// store the number of equations we currently have\n\tm_nreq = m_neq;\n\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tint neq = m_rigidSolver.InitEquations(m_neq);\n\tif (neq == -1) return false; \n\telse m_neq = neq;\n\n\t// Next, we add any Lagrange Multipliers\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* lmc = fem.NonlinearConstraint(i);\n\t\tif (lmc->IsActive())\n\t\t{\n\t\t\tm_neq += lmc->InitEquations(m_neq);\n\t\t}\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive())\n\t\t{\n\t\t\tm_neq += spc->InitEquations(m_neq);\n\t\t}\n\t}\n\n\t// All initialization is done\n\treturn true;\n}\n\n\n//-----------------------------------------------------------------------------\nbool FESolidSolver2::InitEquations2()\n{\n\t// First call the base class.\n\t// This will initialize all equation numbers, except the rigid body equation numbers\n\tif (FENewtonSolver::InitEquations2() == false) return false;\n\n\t// store the number of equations we currently have\n\tm_nreq = m_neq;\n\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tint neq = m_rigidSolver.InitEquations(m_neq);\n\tif (neq == -1) return false;\n\telse m_neq = neq;\n\n\t// Next, we add any Lagrange Multipliers\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* lmc = fem.NonlinearConstraint(i);\n\t\tif (lmc->IsActive())\n\t\t{\n\t\t\tm_neq += lmc->InitEquations(m_neq);\n\t\t}\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive())\n\t\t{\n\t\t\tm_neq += spc->InitEquations(m_neq);\n\t\t}\n\t}\n\n\t// All initialization is done\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Update the kinematics of the model, such as nodal positions, velocities,\n//! accelerations, etc.\nvoid FESolidSolver2::UpdateKinematics(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update rigid bodies\n\tm_rigidSolver.UpdateRigidBodies(m_Ui, ui);\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tint U_size = (int)U.size();\n#pragma omp parallel for\n\tfor (int i = 0; i < U_size; ++i)\n\t{\n\t\tU[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\t}\n\n\t// update flexible nodes\n\t// translational dofs\n\tscatter3(U, mesh, m_dofU[0], m_dofU[1], m_dofU[2]);\n\t// rotational dofs\n\tscatter3(U, mesh, m_dofQ[0], m_dofQ[1], m_dofQ[2]);\n\t// shell dofs\n\tscatter3(U, mesh, m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\n\t// make sure the boundary conditions are fullfilled\n\tint nbcs = fem.BoundaryConditions();\n\tfor (int i = 0; i<nbcs; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Update();\n\t}\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// Update the spatial nodal positions\n\t// Don't update rigid nodes since they are already updated\n\tint NN = mesh.Nodes();\n\t#pragma omp parallel\n\t{\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < NN; ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tif (node.m_rid == -1) {\n\t\t\t\tnode.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n\t\t\t}\n\t\t\tnode.m_dt = node.m_d0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2])\n\t\t\t\t- node.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\t\t}\n\n\t\t// update velocity and accelerations\n\t\t// for dynamic simulations\n\t\tFEAnalysis* pstep = fem.GetCurrentStep();\n\t\tif (pstep->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t\t{\n\t\t\tdouble dt = fem.GetTime().timeIncrement;\n\t\t\tdouble a = 1.0 / (m_beta * dt);\n\t\t\tdouble b = a / dt;\n\t\t\tdouble c = 1.0 - 0.5 / m_beta;\n\t\t\t#pragma omp for nowait\n\t\t\tfor (int i = 0; i < NN; ++i)\n\t\t\t{\n\t\t\t\tFENode& n = mesh.Node(i);\n\t\t\t\tn.m_at = (n.m_rt - n.m_rp) * b - n.m_vp * a + n.m_ap * c;\n\t\t\t\tvec3d vt = n.m_vp + (n.m_ap * (1.0 - m_gamma) + n.m_at * m_gamma) * dt;\n\t\t\t\tn.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n\n\t\t\t\t// shell kinematics\n\t\t\t\t{\n\t\t\t\t\tvec3d qt = n.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\t\t\t\t\tvec3d qp = n.get_vec3d_prev(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\t\t\t\t\tvec3d vqp = n.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2]);\n\t\t\t\t\tvec3d aqp = n.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2]);\n\t\t\t\t\tvec3d aqt = (qt - qp) * b - vqp * a + aqp * c;\n\t\t\t\t\tvec3d vqt = vqp + (aqp * (1.0 - m_gamma) + aqt * m_gamma) * dt;\n\t\t\t\t\tn.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n\t\t\t\t\tn.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vqt);\n\t\t\t\t}\n\n\t\t\t\t// beam kinematics\n\t\t\t\t{\n\t\t\t\t\tvec3d Rp = n.get_vec3d_prev(m_dofQ[0], m_dofQ[1], m_dofQ[2]);\n\t\t\t\t\tvec3d wp = n.get_vec3d_prev(m_dofBW[0], m_dofBW[1], m_dofBW[2]);\n\t\t\t\t\tvec3d ap = n.get_vec3d_prev(m_dofBA[0], m_dofBA[1], m_dofBA[2]);\n\n\t\t\t\t\t// rotation at previous time step\n\t\t\t\t\tquatd Qp(Rp);\n\t\t\t\t\tquatd Qp_T = Qp.Conjugate();\n\n\t\t\t\t\t// convert to material quantities\n\t\t\t\t\tvec3d Wp = Qp_T * wp;\n\t\t\t\t\tvec3d Ap = Qp_T * ap;\n\n\t\t\t\t\t// get equation numbers for rotations\n\t\t\t\t\t// (and ensure that they are all free or all prescribed)\n\t\t\t\t\tint eq[3] = { n.m_ID[m_dofQ[0]], n.m_ID[m_dofQ[1]], n.m_ID[m_dofQ[2]] };\n\t\t\t\t\tassert(((eq[0] >= 0) && (eq[1] >= 0) && (eq[2] >= 0)) ||\n\t\t\t\t\t\t((eq[0] < 0) && (eq[1] < 0) && (eq[2] < 0)));\n\n\t\t\t\t\t// get rotation increment\n\t\t\t\t\tvec3d ri, Ri;\n\t\t\t\t\tint m;\n\t\t\t\t\tm = eq[0]; if (m >= 0) { ri.x = ui[m]; Ri.x = m_Ui[m]; }\n\t\t\t\t\tm = eq[1]; if (m >= 0) { ri.y = ui[m]; Ri.y = m_Ui[m]; }\n\t\t\t\t\tm = eq[2]; if (m >= 0) { ri.z = ui[m]; Ri.z = m_Ui[m]; }\n\t\t\t\t\tquatd dq(ri), qi(Ri);\n\t\t\t\t\tquatd qn = dq * qi;\n\t\t\t\t\tvec3d rn = qn.GetRotationVector();\n\n\t\t\t\t\t// check for prescribed values\n\t\t\t\t\tif ((eq[0] < 0) && (eq[1] < 0) && (eq[2] < 0))\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3d Rt;\n\t\t\t\t\t\tm = eq[0]; if (m < -1) { Rt.x = n.get(m_dofQ[0]); }\n\t\t\t\t\t\tm = eq[1]; if (m < -1) { Rt.y = n.get(m_dofQ[1]); }\n\t\t\t\t\t\tm = eq[2]; if (m < -1) { Rt.z = n.get(m_dofQ[2]); }\n\t\t\t\t\t\tquatd Qt(Rt);\n\t\t\t\t\t\tqn = Qp.Conjugate() * Qt;\n\t\t\t\t\t\trn = qn.GetRotationVector();\n\t\t\t\t\t}\n\n\t\t\t\t\t// convert to material increment\n\t\t\t\t\tvec3d Qn = Qp_T * rn;\n\n\t\t\t\t\t// update material angular velocity and angular acceleration\n\t\t\t\t\tvec3d At = (Qn - Wp * dt) * b + Ap * c;\n\t\t\t\t\tvec3d Wt = Qn * (m_gamma * a) + Wp * (1.0 - m_gamma / m_beta) + Ap * (dt * (1.0 - 0.5 * m_gamma / m_beta));\n\n\t\t\t\t\t// convert to spatial\n\t\t\t\t\tquatd Qt = qn * Qp;\n\t\t\t\t\tvec3d wt = Qt * Wt;\n\t\t\t\t\tvec3d at = Qt * At;\n\n\t\t\t\t\t// store updated values\n\t\t\t\t\tif ((eq[0] >= 0) && (eq[1] >= 0) && (eq[2] >= 0))\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3d Rt = Qt.GetRotationVector();\n\t\t\t\t\t\tn.set_vec3d(m_dofQ[0], m_dofQ[1], m_dofQ[2], Rt);\n\t\t\t\t\t}\n\t\t\t\t\tn.set_vec3d(m_dofBW[0], m_dofBW[1], m_dofBW[2], wt);\n\t\t\t\t\tn.set_vec3d(m_dofBA[0], m_dofBA[1], m_dofBA[2], at);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// update nonlinear constraints (needed for updating Lagrange Multiplier)\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* nlc = fem.NonlinearConstraint(i);\n\t\tif (nlc->IsActive()) nlc->Update(m_Ui, ui);\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive()) spc->Update(m_Ui, ui);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Update DOF increments\nvoid FESolidSolver2::UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n    \n\t// update rigid bodies\n\tm_rigidSolver.UpdateIncrements(Ui, ui, emap);\n        \n\t// update flexible nodes\n\tint n;\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n        \n\t\t// displacement dofs\n\t\t// current position = initial + total at prev conv step + total increment so far + current increment\n\t\tif ((n = node.m_ID[m_dofU[0]]) >= 0) Ui[n] += ui[n];\n\t\tif ((n = node.m_ID[m_dofU[1]]) >= 0) Ui[n] += ui[n];\n\t\tif ((n = node.m_ID[m_dofU[2]]) >= 0) Ui[n] += ui[n];\n \n\t\t// beam rotations\n\t\t{\n\t\t\tvec3d ri, Ri;\n\t\t\tif ((n = node.m_ID[m_dofQ[0]]) >= 0) { ri.x = ui[n]; Ri.x = Ui[n]; }\n\t\t\tif ((n = node.m_ID[m_dofQ[1]]) >= 0) { ri.y = ui[n]; Ri.y = Ui[n]; }\n\t\t\tif ((n = node.m_ID[m_dofQ[2]]) >= 0) { ri.z = ui[n]; Ri.z = Ui[n]; }\n\t\t\tquatd qi(ri), Qi(Ri);\n\t\t\tquatd Qn = qi * Qi;\n\t\t\tvec3d rn = Qn.GetRotationVector();\n\t\t\tif ((n = node.m_ID[m_dofQ[0]]) >= 0) { Ui[n] = rn.x; }\n\t\t\tif ((n = node.m_ID[m_dofQ[1]]) >= 0) { Ui[n] = rn.y; }\n\t\t\tif ((n = node.m_ID[m_dofQ[2]]) >= 0) { Ui[n] = rn.z; }\n\t\t}\n\n\t\t// shell dofs\n\t\t{\n\t\t\tif ((n = node.m_ID[m_dofSU[0]]) >= 0) Ui[n] += ui[n];\n\t\t\tif ((n = node.m_ID[m_dofSU[1]]) >= 0) Ui[n] += ui[n];\n\t\t\tif ((n = node.m_ID[m_dofSU[2]]) >= 0) Ui[n] += ui[n];\n\t\t}\n\t}\n\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc && plc->IsActive()) plc->UpdateIncrements(Ui, ui);\n\t}\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->UpdateIncrements(Ui, ui);\n\t}\n\n\t// TODO: This is a hack!\n\t// The problem is that I only want to call the domain's IncrementalUpdate during\n\t// the quasi-Newtoon loop. However, this function is also called after the loop\n\t// converges. The emap parameter is used here to detect wether we are inside the \n\t// loop (emap == false), or not (emap == true).\n\tif (emap == false)\n\t{\n\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tdom.IncrementalUpdate(ui, true);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\nvoid FESolidSolver2::Update(vector<double>& ui)\n{\n    FEModel& fem = *GetFEModel();\n    FETimeInfo& tp = fem.GetTime();\n    tp.currentIteration = m_niter;\n    \n    // update EAS\n    UpdateEAS(ui);\n    UpdateIncrementsEAS(ui, true);\n\n\t// update kinematics\n\tUpdateKinematics(ui);\n\n\t// update domains \n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.IncrementalUpdate(ui, false);\n\t}\n\n\t// update model state\n\tUpdateModel();\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\n//! NOTE: The ui vector also contains prescribed displacement increments. Also note that this\n//!       only works for a limited set of FEBio features (no rigid bodies!).\nvoid FESolidSolver2::Update2(const vector<double>& ui)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tfor (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n\t// update free nodes\n\tscatter3(U, mesh, m_dofU[0], m_dofU[1], m_dofU[2]);\n\n\t// Update the spatial nodal positions\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n        vec3d du(0, 0, 0);\n        int nx = -node.m_ID[m_dofU[0]] - 2; if (nx >= 0) du.x = ui[nx];\n        int ny = -node.m_ID[m_dofU[1]] - 2; if (ny >= 0) du.y = ui[ny];\n        int nz = -node.m_ID[m_dofU[2]] - 2; if (nz >= 0) du.z = ui[nz];\n        \n\t\tif (node.m_rid == -1)\n\t\t{\n\t\t\tvec3d rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]) + du;\n\t\t\tnode.m_rt = rt;\n\t\t}\n        vec3d db(0, 0, 0);\n        int nbx = -node.m_ID[m_dofSU[0]] - 2; if (nbx >= 0) db.x = ui[nbx];\n        int nby = -node.m_ID[m_dofSU[1]] - 2; if (nby >= 0) db.y = ui[nby];\n        int nbz = -node.m_ID[m_dofSU[2]] - 2; if (nbz >= 0) db.z = ui[nbz];\n\n        vec3d dt = node.m_d0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]) + du\n        - (node.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]) + db);\n        node.m_dt = dt;\n\t}\n\n\t// update model state\n\tUpdateModel();\n}\n\n//-----------------------------------------------------------------------------\n//! Update EAS\nvoid FESolidSolver2::UpdateEAS(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n    FEMesh& mesh = fem.GetMesh();\n\n    // update EAS on shell domains\n    for (int i=0; i<mesh.Domains(); ++i) {\n        FESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n        if (sdom && sdom->IsActive()) sdom->UpdateEAS(ui);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Update EAS\nvoid FESolidSolver2::UpdateIncrementsEAS(vector<double>& ui, const bool binc)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n    \n    // update EAS on shell domains\n    for (int i=0; i<mesh.Domains(); ++i) {\n        FESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n        if (sdom && sdom->IsActive()) sdom->UpdateIncrementsEAS(ui, binc);\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESolidSolver2::InitStep(double time)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// set time integration parameters\n\tFETimeInfo& tp = fem.GetTime();\n\ttp.alpha = m_alpha;\n\ttp.beta = m_beta;\n\ttp.gamma = m_gamma;\n\ttp.alphaf = m_alphaf;\n\ttp.alpham = m_alpham;\n\n    // evaluate load curve values at current (or intermediate) time\n\tdouble t = tp.currentTime;\n//\tdouble dt = tp.timeIncrement;\n//\tdouble ta = (t > 0) ? t - (1-m_alpha)*dt : m_alpha*dt;\n//\treturn FESolver::InitStep(ta);\n    return FESolver::InitStep(t);\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first BFGS-iteration. \nvoid FESolidSolver2::PrepStep()\n{\n\tFEModel& fem = *GetFEModel();\n\n    FETimeInfo& tp = fem.GetTime();\n\tdouble dt = tp.timeIncrement;\n\ttp.augmentation = 0;\n    \n\t// zero total displacements\n\tzero(m_Ui);\n\n\t// store previous mesh state\n\t// we need them for velocity and acceleration calculations\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& ni = mesh.Node(i);\n\t\tni.m_rp = ni.m_rt;\n\t\tni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tni.m_ap = ni.m_at;\n        ni.m_dp = ni.m_dt;\n\t\tni.UpdateValues();\n\n        // initial guess at start of new time step\n        // solid\n        ni.m_at = ni.m_ap*(1-0.5/m_beta) - ni.m_vp/(m_beta*dt);\n        vec3d vs = ni.m_vp + (ni.m_at*m_gamma + ni.m_ap*(1-m_gamma))*dt;\n        ni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vs);\n        \n        // solid shell\n        vec3d aqp = ni.get_vec3d_prev(m_dofSA[0], m_dofSA[1], m_dofSA[2]);\n        vec3d vqp = ni.get_vec3d_prev(m_dofSV[0], m_dofSV[1], m_dofSV[2]);\n        vec3d aqt = aqp*(1-0.5/m_beta) - vqp/(m_beta*dt);\n        ni.set_vec3d(m_dofSA[0], m_dofSA[1], m_dofSA[2], aqt);\n        vec3d vqt = vqp + (aqt*m_gamma + aqp*(1-m_gamma))*dt;\n        ni.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vqt);\n\n\t\t// beams (rotational kinematics)\n\t\t{\n\t\t\t// get rotation\n\t\t\tvec3d rp = ni.get_vec3d_prev(m_dofQ[0], m_dofQ[1], m_dofQ[2]);\n\t\t\tquatd Q(rp);\n\t\t\tquatd Qt = Q.Conjugate();\n\n\t\t\t// get previous spatial quantities\n\t\t\tvec3d wp = ni.get_vec3d_prev(m_dofBW[0], m_dofBW[1], m_dofBW[2]);\n\t\t\tvec3d ap = ni.get_vec3d_prev(m_dofBA[0], m_dofBA[1], m_dofBA[2]);\n\n\t\t\t// convert to material frame\n\t\t\tvec3d Wp = Qt * wp;\n\t\t\tvec3d Ap = Qt * ap;\n\n\t\t\t// initial guess \n\t\t\tvec3d At = Ap * (1.0 - 0.5/m_beta) - Wp / (m_beta*dt);\n\t\t\tvec3d Wt = Wp + (Ap * (1.0 - m_gamma) + At*m_gamma)*dt;\n\n\t\t\t// back to spatial frame\n\t\t\tvec3d at = Q * At;\n\t\t\tvec3d wt = Q * Wt;\n\n\t\t\tni.set_vec3d(m_dofBW[0], m_dofBW[1], m_dofBW[2], wt);\n\t\t\tni.set_vec3d(m_dofBA[0], m_dofBA[1], m_dofBA[2], at);\n\t\t}\n    }\n\n    // apply concentrated nodal forces\n\t// since these forces do not depend on the geometry\n\t// we can do this once outside the NR loop.\n//\tvector<double> dummy(m_neq, 0.0);\n//\tzero(m_Fn);\n//\tFEResidualVector Fn(*GetFEModel(), m_Fn, dummy);\n//\tNodalLoads(Fn, tp);\n\n\t// apply boundary conditions\n\t// we save the prescribed displacements increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i=0; i<nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& dc = *fem.BoundaryCondition(i);\n\t\tif (dc.IsActive()) dc.PrepStep(ui);\n\t}\n\n\t// do the linear constraints\n\tfem.GetLinearConstraintManager().PrepStep();\n\n\t// initialize rigid bodies\n\tm_rigidSolver.PrepStep(tp, ui);\n\n\t// intialize material point data\n\t// NOTE: do this before the stresses are updated\n\t// TODO: does it matter if the stresses are updated before\n\t//       the material point data is initialized\n\tfor (int i=0; i<mesh.Domains(); ++i) \n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tif (dom.IsActive()) dom.PreSolveUpdate(tp);\n\t}\n\n\t// update model state\n\tUpdateModel();\n\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc && plc->IsActive()) plc->PrepStep();\n\t}\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n\n\tfor (int i = 0; i < fem.ModelLoads(); ++i)\n\t{\n\t\tFEModelLoad* pl = fem.ModelLoad(i);\n\t\tif (pl->IsActive()) pl->PrepStep();\n\t}\n\n\t// see if we need to do contact augmentations\n\tm_baugment = false;\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n\t\tif (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n\t}\n\n\t// see if we need to do incompressible augmentations\n\t// TODO: Should I do these augmentations in a nlconstraint class instead?\n\tint ndom = mesh.Domains();\n\tfor (int i = 0; i < ndom; ++i)\n\t{\n\t\tFEDomain* dom = &mesh.Domain(i);\n\t\tFE3FieldElasticSolidDomain* dom3f = dynamic_cast<FE3FieldElasticSolidDomain*>(dom);\n\t\tif (dom3f && dom3f->DoAugmentations()) m_baugment = true;\n\n\t\tFE3FieldElasticShellDomain* dom3fs = dynamic_cast<FE3FieldElasticShellDomain*>(dom);\n\t\tif (dom3fs && dom3fs->DoAugmentations()) m_baugment = true;\n\t}\n\n\t// see if we have to do nonlinear constraint augmentations\n\tif (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\n// Performs the quasi-newton iterations.\nbool FESolidSolver2::Quasin()\n{\n\tvector<double> u0(m_neq);\n\tvector<double> Rold(m_neq);\n\n\t// convergence norms\n\tdouble\tnormR1;\t\t// residual norm\n\tdouble\tnormE1;\t\t// energy norm\n\tdouble\tnormU;\t\t// displacement norm\n\tdouble\tnormu;\t\t// displacement increment norm\n\tdouble\tnormRi;\t\t// initial residual norm\n\tdouble\tnormEi;\t\t// initial energy norm\n\tdouble\tnormEm;\t\t// max energy norm\n\tdouble\tnormUi;\t\t// initial displacement norm\n\n\tFEModel& fem = *GetFEModel();\n\n\t// Get the current step\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t// set the time information\n\tFETimeInfo& tp = fem.GetTime();\n\n\t// initialize arc length stuff\n\tif (m_arcLength > 0)\n\t{\n\t\tm_al_inc = 0.0;\n\n\t\t// store the total increment from the last time step\n\t\t// we need it later to decide in which direction to proceed.\n\t\tm_Uip = m_Ui;\n\t}\n\n\t// prepare for the first iteration\n\tPrepStep();\n\n\t// Initialize the QN-method\n\tif (QNInit() == false) return false;\n\n\t// loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter+1);\n\n\t\t// assume we'll converge. \n\t\tbconv = true;\n\n\t\t// solve the equations\n\t\tSolveEquations(m_ui, m_R0);\n\n\t\t// apply arc-length method\n\t\tif (m_arcLength > 0) DoArcLength();\n\n\t\t// do the line search\n\t\tdouble s = DoLineSearch();\n\n\t\t// set initial convergence norms\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tnormRi = fabs(m_R0*m_R0);\n\t\t\tnormEi = fabs(m_ui*m_R0);\n\t\t\tnormUi = fabs(m_ui*m_ui);\n\t\t\tnormEm = normEi;\n\n\t\t\tm_residuNorm.norm0 = normRi;\n\t\t\tm_energyNorm.norm0 = normEi;\n\t\t\tm_solutionNorm[0].norm0 = normUi;\n\t\t}\n\n\t\t// calculate actual displacement increment\n\t\t// NOTE: We don't apply the line search directly to m_ui since we need the unscaled search direction for the QN update below\n\t\tint neq = (int)m_Ui.size();\n\t\tvector<double> ui(m_ui);\n\t\tfor (int i = 0; i<neq; ++i) ui[i] *= s;\n\n\t\t// update total displacements\n\t\tUpdateIncrements(m_Ui, ui, false);\n\n\t\t// calculate norms\n\t\tnormR1 = m_R1*m_R1;\n\t\tnormu  = ui*ui;\n\t\tnormU  = m_Ui*m_Ui;\n\t\tnormE1 = fabs(ui*m_R1);\n\n\t\tm_residuNorm.norm = normR1;\n\t\tm_energyNorm.norm = normE1;\n\t\tm_solutionNorm[0].norm = normu;\n\n\t\t// check for nans\n\t\tif (ISNAN(normR1)) throw NANInResidualDetected();\n\t\tif (ISNAN(normu)) throw NANInSolutionDetected();\n\n\t\t// check residual norm\n\t\tif ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\t\n\n\t\t// check displacement norm\n\t\tif ((m_Dtol > 0) && (normu  > (m_Dtol*m_Dtol)*normU )) bconv = false;\n\n\t\t// check energy norm\n\t\tif ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n\n\t\t// check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n\n\t\t// check energy divergence\n\t\tif (m_bdivreform)\n\t\t{\n\t\t\tif (normE1 > normEm) bconv = false;\n\t\t}\n\n\t\t// print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n\t\tfeLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n\t\tfeLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n\t\tfeLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n\t\tfeLog(\"\\t   displacement     %15le %15le %15le \\n\", normUi, normu ,(m_Dtol*m_Dtol)*normU );\n\n\t\t// see if we may have a small residual\n\t\tif ((bconv == false) && (normR1 < m_Rmin))\n\t\t{\n\t\t\t// check for almost zero-residual on the first iteration\n\t\t\t// this might be an indication that there is no force on the system\n\t\t\tfeLogWarning(\"No force acting on the system.\");\n\t\t\tbconv = true;\n\t\t}\n\n\t\t// see if we have exceeded the max residual\n\t\tif ((bconv == false) && (m_Rmax > 0) && (normR1 >= m_Rmax))\n\t\t{\n\t\t\t// doesn't look like we're getting anywhere, so let's retry the time step\n\t\t\tthrow MaxResidualError();\n\t\t}\n\n\t\t// check if we have converged. \n\t\t// If not, calculate the BFGS update vectors\n\t\tif (bconv == false)\n\t\t{\n\t\t\t// do additional checks that may trigger a stiffness reformation\n\t\t\tif (s < m_lineSearch->m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\t\t\telse if ((normE1 > normEm) && m_bdivreform)\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n\t\t\t\tnormEm = normE1;\n\t\t\t\tnormEi = normE1;\n\t\t\t\tnormRi = normR1;\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// do the augmentations\n\t\t\tbconv = DoAugmentations();\n\t\t}\n\t\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile (bconv == false);\n\n\tfem.DoCallback(CB_QUASIN_CONVERGED);\n\n\t// if converged we update the total displacements\n\tif (bconv)\n\t{\n        UpdateIncrementsEAS(m_Ui, false);\n        UpdateIncrements(m_Ut, m_Ui, true);\n\n\t\t// TODO: To zero or not to zero. That is the question!\n\t\t//       The arc-length method requires that we do NOT zero\n\t\t//       here, so that m_Uip gets initialized properly.\n\t\t//       However, the \"jfnk-tangent test\" requires that we zero this\n\t\t//       Otherwise the displacement increment can get counted twice in\n\t\t//       Update2 (from m_Ui and m_Ut since we just added it)\n\t\tif (m_arcLength == 0)\n\t\t\tzero(m_Ui);\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n// Exception that is thrown when the arc-length method has failed\nclass ArcLengthFailed : public FEException\n{\npublic:\n\tArcLengthFailed() : FEException(\"arc-length update has failed\") {}\n};\n\n//-----------------------------------------------------------------------------\nbool quadratic_solve(double a, double b, double c, double x[2])\n{\n\tdouble D2 = b*b - 4.0*a*c;\n\tif (D2 < 0) return false;\n\tdouble D = sqrt(D2);\n\tif (b >= 0)\n\t{\n\t\tx[0] = (-b - D) / (2.0*a);\n\t\tx[1] = 2.0*c / (-b - D);\n\t}\n\telse\n\t{\n\t\tx[0] = 2.0*c / (-b + D);\n\t\tx[1] = (-b + D) / (2.0*a);\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Apply arc-length\nvoid FESolidSolver2::DoArcLength()\n{\n\t// auxiliary displacement\n\tvector<double> uF(m_neq, 0.0);\n\n\t// the arc-length scale factor\n\tdouble psi = m_al_scale;\n\tassert(psi == 0.0);\n\n\tconst FETimeInfo& tp = GetFEModel()->GetTime();\n\n\tm_al_gamma = 0.0;\n\tif (m_arcLength == ARC_LENGTH_METHOD::CRISFIELD)\n\t{\n\t\t// solve for auxiliary displacement\n\t\tSolveEquations(uF, m_Fext);\n\n\t\t// if this is the first time step, we pick a special gamma\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tif (GetFEModel()->GetCurrentStep()->m_ntimesteps == 0)\n\t\t\t{\n\t\t\t\t// The first time we get here, we simply pick the arc-length step size\n\t\t\t\t// from the solution \n\t\t\t\tm_al_gamma = tp.timeIncrement;\n\t\t\t\tm_al_ds = m_al_gamma*sqrt(uF*uF + psi*(m_Fext*m_Fext));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_al_gamma = m_al_ds / sqrt(uF*uF + psi*(m_Fext*m_Fext));\n\t\t\t\tdouble uFdx = uF*m_Uip;\n\t\t\t\tif (uFdx < 0.0) m_al_gamma = -m_al_gamma;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// The general case requires solving a quadratic equation\n\t\t\t// setup quadratic equation\n\t\t\tdouble Fe_norm2 = m_Fext*m_Fext;\n\t\t\tdouble a = uF*uF + (psi*psi)*Fe_norm2;\n\t\t\tdouble b = 2.0*(uF*(m_Ui + m_ui)) + 2 * m_al_inc*(psi*psi)*Fe_norm2;\n\t\t\tdouble c = m_ui*(m_Ui*2.0 + m_ui) + m_Ui*m_Ui - m_al_ds*m_al_ds + (psi*psi)*(m_al_inc*m_al_inc)*Fe_norm2;\n\n\t\t\t// solve quadratic equation\n\t\t\tdouble g[2];\n\t\t\tif (quadratic_solve(a, b, c, g) == false)\n\t\t\t{\n\t\t\t\tm_al_ds *= 0.5;\n\t\t\t\tthrow ArcLengthFailed();\n\t\t\t}\n\n\t\t\t// two possible solution vectors\n\t\t\tvector<double> u1 = m_ui + uF*g[0];\n\t\t\tvector<double> u2 = m_ui + uF*g[1];\n\n\t\t\t// calculate two s-vectors\n\t\t\tvector<double> sk(2 * m_neq, 0.0), s1(2 * m_neq, 0.0), s2(2 * m_neq, 0.0);\n\t\t\tfor (int i = 0; i < m_neq; ++i)\n\t\t\t{\n\t\t\t\tsk[i] = m_Ui[i];\n\t\t\t\tsk[i + m_neq] = m_al_inc*psi*m_Fext[i];\n\n\t\t\t\ts1[i] = m_Ui[i] + u1[i];\n\t\t\t\ts1[i + m_neq] = (m_al_inc + g[0])*psi*m_Fext[i];\n\n\t\t\t\ts2[i] = m_Ui[i] + u2[i];\n\t\t\t\ts2[i + m_neq] = (m_al_inc + g[1])*psi*m_Fext[i];\n\t\t\t}\n\n\t\t\t// calculate the norms of these vectors\n\t\t\t// NOTE: Should be Ds!!\n\t\t\tdouble norm_sk = 0, norm_s1 = 0, norm_s2 = 0;\n\t\t\tfor (int i = 0; i < 2 * m_neq; ++i)\n\t\t\t{\n\t\t\t\tnorm_sk += sk[i] * sk[i];\n\t\t\t\tnorm_s1 += s1[i] * s1[i];\n\t\t\t\tnorm_s2 += s2[i] * s2[i];\n\t\t\t}\n\t\t\tnorm_sk = sqrt(norm_sk);\n\t\t\tnorm_s1 = sqrt(norm_s1);\n\t\t\tnorm_s2 = sqrt(norm_s2);\n\n\t\t\t// see which one produces the closest angle to the current path\n\t\t\tdouble c1 = (sk*s1) / (norm_sk*norm_s1);\n\t\t\tdouble c2 = (sk*s2) / (norm_sk*norm_s2);\n\n\t\t\tif (c1 >= c2)\n\t\t\t{\n\t\t\t\tm_al_gamma = g[0];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_al_gamma = g[1];\n\t\t\t}\n\t\t}\n\n\t\tm_al_inc += m_al_gamma;\n\t\tm_al_lam += m_al_gamma;\n\t\tm_ui = m_ui + uF*m_al_gamma;\n\t}\n\n\t// evaluate the arc-length equation. \n\tdouble sk2 = (m_Ui + m_ui)*(m_Ui + m_ui) + (psi*psi)*m_al_inc*m_al_inc*(m_Fext*m_Fext);\n\tdouble sk = sqrt(sk2);\n\tdouble serr = fabs((sk - m_al_ds) / m_al_ds);\n\tfeLog(\"\\tarc-length increment : %lg (%lg)\\n\", m_al_inc, m_al_gamma);\n\tfeLog(\"\\tarc-length factor    : %lg\\n\", m_al_lam);\n\tfeLog(\"\\tarc-length constraint: %lg (%lg, err = %lg)\\n\", sk, m_al_ds, serr);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FESolidSolver2::StiffnessMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// setup the linear system\n\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), m_alpha, m_nreq);\n\n\t// calculate the stiffness matrix for each domain\n\tfor (int i=0; i<mesh.Domains(); ++i) \n\t{\n\t\tif (mesh.Domain(i).IsActive()) \n\t\t{\n\t\t\tFEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n\t\t\tdom.StiffnessMatrix(LS);\n\t\t}\n\t}\n\n\t// calculate the body force stiffness matrix for each non-rigid domain\n\tfor (int j = 0; j<fem.ModelLoads(); ++j)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(j);\n\t\tif (pml->IsActive()) pml->StiffnessMatrix(LS);\n\t}\n    \n    // TODO: add body force stiffness for rigid bodies\n\n\t// Add mass matrix for dynamic problems\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tif (pstep->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t{\n\t\t// scale factor\n\t\tdouble dt = tp.timeIncrement;\n\t\tdouble a = tp.alpham / (m_beta*dt*dt);\n\n\t\t// loop over all elastic domains\n\t\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\t\tif (edom) edom->MassMatrix(LS, a);\n\t\t}\n\n\t\tm_rigidSolver.RigidMassMatrix(LS, tp);\n\t}\n\n\t// calculate contact stiffness\n\tContactStiffness(LS);\n\n\t// calculate stiffness matrices for surface loads\n\t// for arclength method we need to apply the scale factor to all the \n\t// external forces stiffness matrix. \n\tif (m_arcLength > 0) LS.StiffnessAssemblyScaleFactor(m_al_lam);\n/*\tint nsl = fem.SurfaceLoads();\n\tfor (int i = 0; i<nsl; ++i)\n\t{\n\t\tFESurfaceLoad* psl = fem.SurfaceLoad(i);\n\t\tif (psl->IsActive())\n\t\t{\n\t\t\tpsl->StiffnessMatrix(LS, tp);\n\t\t}\n\t}\n\tif (m_arcLength > 0) LS.StiffnessAssemblyScaleFactor(1.0);\n*/\n\t// calculate nonlinear constraint stiffness\n\t// note that this is the contribution of the \n\t// constrainst enforced with augmented lagrangian\n\tNonLinearConstraintStiffness(LS, tp);\n\n\t// add contributions from rigid bodies\n\tm_rigidSolver.StiffnessMatrix(*m_pK, tp);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FESolidSolver2::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i=0; i<N; ++i) \n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact stiffness matrix\n\nvoid FESolidSolver2::ContactStiffness(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the contact forces\nvoid FESolidSolver2::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->LoadVector(R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FESolidSolver2::Residual(vector<double>& R)\n{\n\t// get the time information\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup the global vector\n\tzero(R);\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tm_rigidSolver.Residual();\n\n\t// calculate the internal (stress) forces\n\tInternalForces(RHS);\n\n\t// calculate nodal reaction forces\n\tfor (int i = 0; i < m_neq; ++i) m_Fr[i] -= R[i];\n\n\t// extract the internal forces\n\t// (only when we really need it, below)\n\tif (m_logSolve && fem.GetCurrentStep()->m_ntimesteps > 0)\n\t{\n\t\tm_Fint = R;\n\t}\n\n\tif (m_arcLength > 0)\n\t{\n\t\t// Note the negative sign. This is because during residual assembly\n\t\t// a negative sign is applied to the internal force vector. \n\t\t// The model loads assume the residual is Fe - Fi (i.e. -R)\n\t\tm_Fint = -R;\n\t}\n\n\t// calculate external forces\n\tExternalForces(RHS);\n\n\t// For arc-length we need the external loads\n\tif (m_arcLength > 0)\n\t{\n\t\t// extract the external force\n\t\tm_Fext = R + m_Fint;\n\n\t\t// we need to apply the arc-length factor to the external loads\n        for (int i=0; i<R.size();++i) R[i] = m_Fext[i]* m_al_lam - m_Fint[i];\n\t}\n\n\t// apply the residual transformation\n\t// NOTE: This is an implementation of Ankush Aggarwal method to accelerate the Newton convergence\n\tif (m_logSolve && fem.GetCurrentStep()->m_ntimesteps > 0)\n\t{\n\t\tdouble TOL = 1.e-8;\n\t\tbool logused = false;\n\t\tvector<double> RHSlog;\n\t\tRHSlog.resize(R.size());\n\t\tfor (int i = 0; i<m_Fint.size(); ++i)\n\t\t{\n\t\t\tif (fabs(RHS[i] - m_Fint[i])>TOL && fabs(m_Fint[i])>TOL && (m_Fint[i] - RHS[i]) / m_Fint[i]>0)\n\t\t\t{\n\t\t\t\tRHSlog[i] = -m_Fint[i] * log((m_Fint[i] - RHS[i]) / m_Fint[i]);\n\t\t\t\tlogused = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tRHSlog[i] = RHS[i];\n\t\t\t}\n\t\t}\n\t\tfor (int i = 0; i<m_Fint.size(); ++i) R[i] = RHSlog[i];\n\t\tif (logused)\n\t\t\tfeLog(\"Log method used\\n\");\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Internal forces\nvoid FESolidSolver2::InternalForces(FEGlobalVector& R)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\tif (edom) edom->InternalForces(R);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! external forces\nvoid FESolidSolver2::ExternalForces(FEGlobalVector& RHS)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// apply loads\n\tfor (int j = 0; j<fem.ModelLoads(); ++j)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(j);\n\t\tif (pml->IsActive()) pml->LoadVector(RHS);\n\t}\n\n\t// calculate inertial forces for dynamic problems\n\tif (fem.GetCurrentStep()->m_nanalysis == FESolidAnalysis::DYNAMIC)\n\t{\n\t\t// allocate F\n\t\tvector<double> F;\n\n\t\t// calculate the inertial forces for all elastic domains\n\t\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t\t{\n\t\t\tFEElasticDomain* edom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(nd));\n\t\t\tif (edom) edom->InertialForces(RHS, F);\n\t\t}\n\n\t\t// update rigid bodies\n\t\tm_rigidSolver.InertialForces(RHS, tp);\n\t}\n\n\t// calculate forces due to surface loads\n/*\tint nsl = fem.SurfaceLoads();\n\tfor (int i = 0; i<nsl; ++i)\n\t{\n\t\tFESurfaceLoad* psl = fem.SurfaceLoad(i);\n\t\tif (psl->IsActive()) psl->LoadVector(RHS, tp);\n\t}\n*/\n\t// calculate contact forces\n\tContactForces(RHS);\n\n\t// calculate nonlinear constraint forces\n\t// note that these are the linear constraints\n\t// enforced using the augmented lagrangian\n\tNonLinearConstraintForces(RHS, tp);\n\n\t// forces due to point constraints\n\t//\tfor (i=0; i<(int) fem.m_PC.size(); ++i) fem.m_PC[i]->Residual(this, R);\n\n\t// set the nodal reaction forces\n\t// TODO: Is this a good place to do this?\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\n\t\tint n;\n\t\tif ((n = node.m_ID[m_dofU[0]]) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\n\t\tif ((n = node.m_ID[m_dofU[1]]) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\n\t\tif ((n = node.m_ID[m_dofU[2]]) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\n\t\t// add nodal loads\n\t\tdouble s = (m_arcLength>0 ? m_al_lam : 1.0);\n//\t\tif ((n = node.m_ID[m_dofU[0]]) >= 0) node.set_load(m_dofU[0], -m_Fn[n]*s);\n//\t\tif ((n = node.m_ID[m_dofU[1]]) >= 0) node.set_load(m_dofU[1], -m_Fn[n]*s);\n//\t\tif ((n = node.m_ID[m_dofU[2]]) >= 0) node.set_load(m_dofU[2], -m_Fn[n]*s);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the nonlinear constraint forces \nvoid FESolidSolver2::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i=0; i<N; ++i) \n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->LoadVector(R, tp);\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FESolidSolver2.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FECore/FENewtonSolver.h\"\n#include <FECore/FETimeInfo.h>\n#include \"FECore/FEGlobalVector.h\"\n#include \"FERigidSolver.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! The FESolidSolver2 class solves large deformation solid mechanics problems\n//! It can deal with quasi-static and dynamic problems\n//! \nclass FEBIOMECH_API FESolidSolver2 : public FENewtonSolver\n{\n\tenum ARC_LENGTH_METHOD {\n\t\tNONE,\n\t\tCRISFIELD,\n\t};\n\npublic:\n\t//! constructor\n\tFESolidSolver2(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FESolidSolver2();\n\n\t//! serialize data to/from dump file\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Initializes data structures\n\tbool Init() override;\n\n\t//! initialize the step\n\tbool InitStep(double time) override;\n\n\t//! Initialize linear equation system\n\tbool InitEquations() override;\n\tbool InitEquations2() override;\n\n    //! Generate warnings if needed\n    void SolverWarnings();\n\n\t//! Return the rigid solver\n\tFERigidSolver* GetRigidSolver();\n\npublic:\n\t//{ --- evaluation and update ---\n\t\t//! Perform an update\n\t\tvoid Update(vector<double>& ui) override;\n\n\t\t//! perform an updated where ui also contains displacement increments of prescribed displacements\n\t\t//! NOTE: This is a temporary hack that is only by the JFNKMatrix\n\t\tvoid Update2(const vector<double>& ui) override;\n\n\t\t//! update nodal positions, velocities, accelerations, etc.\n\t\tvirtual void UpdateKinematics(vector<double>& ui);\n\n\t\t//! Update EAS\n\t\tvoid UpdateEAS(vector<double>& ui);\n\t\tvoid UpdateIncrementsEAS(vector<double>& ui, const bool binc);\n\n\t\t//! update DOF increments\n\t\tvirtual void UpdateIncrements(vector<double>& Ui, vector<double>& ui, bool emap);\n\t//}\n\n\t//{ --- Solution functions ---\n\n\t\t//! prepares the data for the first QN iteration\n\t\tvoid PrepStep() override;\n\n\t\t//! Performs a Newton-Raphson iteration\n\t\tbool Quasin() override;\n\n\t\t//! Apply arc-length\n\t\tvoid DoArcLength();\n\t//}\n\n\t//{ --- Stiffness matrix routines ---\n\n\t\t//! calculates the global stiffness matrix\n\t\tvirtual bool StiffnessMatrix() override;\n\n\t\t//! contact stiffness\n\t\tvoid ContactStiffness(FELinearSystem& LS);\n\n\t\t//! calculates stiffness contributon of nonlinear constraints\n\t\tvoid NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n\t//}\n\n\t//{ --- Residual routines ---\n\n\t\t//! Calculate the contact forces\n\t\tvoid ContactForces(FEGlobalVector& R);\n\n\t\t//! Calculates residual\n\t\tvirtual bool Residual(vector<double>& R) override;\n\n\t\t//! Calculate nonlinear constraint forces\n\t\tvoid NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n\t\t//! Internal forces\n\t\tvoid InternalForces(FEGlobalVector& R);\n\n\t\t//! external forces\n\t\tvoid ExternalForces(FEGlobalVector& R);\n\t//}\n\nprivate:\n\t//! Calculate initial accelerations for dynamics problems\n\tbool InitAccelerations();\n\npublic:\n\t// convergence tolerances\n\tdouble\tm_Dtol;\t\t\t//!< displacement tolerance\n\n\tbool\tm_logSolve;\t\t//!< flag to use Aggarwal's log method\n\n\t// equation numbers\n\tint\t\tm_nreq;\t\t\t//!< start of rigid body equations\n\npublic:\n\tvector<double> m_Fr;\t//!< nodal reaction forces\n\tvector<double> m_Fint;\t//!< internal load vector\n\tvector<double> m_Fext;\t//!< external load vector\n\tvector<double> m_Uip;\t//!< previous converged displacement increment\n\n    // generalized alpha method (for dynamic analyses)\n    double  m_rhoi;         //!< spectral radius\n    double  m_alphaf;       //!< alpha step for Y={v,e}\n    double  m_alpham;       //!< alpha step for Ydot={∂v/∂t,∂e/∂t}\n\tdouble\tm_alpha;\t\t//!< Newmark parameter alpha (force integration)\n\tdouble\tm_beta;\t\t\t//!< Newmark parameter beta (displacement integration)\n\tdouble\tm_gamma;\t\t//!< Newmark parameter gamme (velocity integration)\n\n\tbool\tm_init_accelerations;\t//!< calculate initial accelerations for dynamic problems\n\n\t// arc-length parameters\n\tint\t\tm_arcLength;\t//!< arc-length method flag (0 = off, 1 = Crisfield)\n\tdouble\tm_al_scale;\t\t//!< arc-length scaling parameter (i.e. psi).\n\tdouble\tm_al_lam;\t\t//!< current arc-length lambda\n\tdouble\tm_al_inc;\t\t//!< arc-length lambda increment at current timestep\n\tdouble\tm_al_ds;\t\t//!< arc-length constraint\n\tdouble\tm_al_gamma;\t\t//!< acr-length increment at current iteration\n\nprotected:\n\tFEDofList\tm_dofU, m_dofV;\n\tFEDofList\tm_dofQ;\n\tFEDofList\tm_dofRQ;\n\tFEDofList\tm_dofSU, m_dofSV, m_dofSA;\n\tFEDofList\tm_dofBW, m_dofBA;\n\nprotected:\n\tFERigidSolverNew\tm_rigidSolver;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESphericalFiberDistribution.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESphericalFiberDistribution.h\"\n\n// The following file contains the integration points and weights\n// for the integration over a unit sphere in spherical coordinates\n#include \"geodesic.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESphericalFiberDistribution, FEElasticMaterial)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" );\n\tADD_PARAMETER(m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\"  );\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FESphericalFiberDistribution\n//-----------------------------------------------------------------------------\n\nFESphericalFiberDistribution::FESphericalFiberDistribution(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FESphericalFiberDistribution::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wl;\n\tconst double eps = 0;\n\tmat3ds s;\n\ts.zero();\n\t\n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\t\t\n\t\t// calculate material coefficients\n\t\tdouble ksi  = m_ksi(mp);\n\t\tdouble alpha = m_alpha(mp);\n\t\tdouble beta = m_beta(mp);\n\t\t\n\t\t// --- quadrant 1,1,1 ---\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,1,1 ---\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,-1,1 ---\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant 1,-1,1 ---\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t}\n\t\n\t// we multiply by two to add contribution from other half-sphere\n\treturn s*(4.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FESphericalFiberDistribution::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wll;\n\tconst double eps = 0;\n\ttens4ds cf, cfw; cf.zero();\n\tmat3ds N2;\n\ttens4ds N4;\n\ttens4ds c;\n\tc.zero();\n\t\n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\t\t\n\t\t// calculate material coefficients\n\t\tdouble ksi  = m_ksi(mp);\n\t\tdouble alpha = m_alpha(mp);\n\t\tdouble beta = m_beta(mp);\n\t\t\n\t\t// --- quadrant 1,1,1 ---\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,1,1 ---\n\t\t\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,-1,1 ---\n\t\t\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant 1,-1,1 ---\n\t\t\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t}\n\t\n\t// multiply by two to integrate over other half of sphere\n\treturn c*(2.0*4.0/J);\n}\n\n//-----------------------------------------------------------------------------\ndouble FESphericalFiberDistribution::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, W;\n\tconst double eps = 0;\n\tdouble sed = 0.0;\n\t\n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\t\t\n\t\t// calculate material coefficients\n\t\tdouble ksi  = m_ksi(mp);\n\t\tdouble alpha = m_alpha(mp);\n\t\tdouble beta = m_beta(mp);\n\t\t\n\t\t// --- quadrant 1,1,1 ---\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (alpha > 0)\n                W = ksi/alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t\t\n\t\t// --- quadrant -1,1,1 ---\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (alpha > 0)\n                W = ksi/alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t\t\n\t\t// --- quadrant -1,-1,1 ---\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (alpha > 0)\n                W = ksi/alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t\t\n\t\t// --- quadrant 1,-1,1 ---\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (alpha > 0)\n                W = ksi/alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t}\n\t\n\t// we multiply by two to add contribution from other half-sphere\n\treturn sed*2.0;\n}\n"
  },
  {
    "path": "FEBioMech/FESphericalFiberDistribution.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for the spherical fiber distribution\n//!\n\nclass FESphericalFiberDistribution : public FEElasticMaterial\n{\npublic:\n\tFESphericalFiberDistribution(FEModel* pfem);\n\t\n\t//! Cauchy stress\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\t\n\t// Spatial tangent\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\t\n\t// Strain energy density\n\tvirtual double StrainEnergyDensity(FEMaterialPoint& mp) override;\n\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\t\npublic:\n\tFEParamDouble   m_beta;     // power in power-law relation\n\tFEParamDouble   m_ksi;      // coefficient in power-law relation\n\tFEParamDouble   m_alpha;\t// coefficient of exponential argument\n};\n"
  },
  {
    "path": "FEBioMech/FESpringMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESpringMaterial.h\"\n#include <FECore/FEElement.h>\n\nvec3d FESpringMaterial::Force(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e = mp.m_drt; e.unit();\n\n\t// calculate spring lengths\n\tdouble L0 = mp.m_dr0.norm();\n\tdouble Lt = mp.m_drt.norm();\n\tdouble DL = Lt - L0;\n\n\t// evaluate the spring force\n\treturn e*force(DL);\n}\n\nmat3d FESpringMaterial::Stiffness(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e = mp.m_drt; e.unit();\n\n\t// calculate spring lengths\n\tdouble L0 = mp.m_dr0.norm();\n\tdouble Lt = mp.m_drt.norm();\n\tdouble DL = Lt - L0;\n\n\t// evaluate the stiffness\n\tdouble F = force(DL);\n\tdouble E = stiffness(DL);\n\n\tif (Lt == 0) { F = 0; Lt = 1; e = vec3d(1, 1, 1); }\n\n\tmat3d A; A.zero();\n\tA[0][0] = ((E - F / Lt)*e.x*e.x + F / Lt);\n\tA[1][1] = ((E - F / Lt)*e.y*e.y + F / Lt);\n\tA[2][2] = ((E - F / Lt)*e.z*e.z + F / Lt);\n\n\tA[0][1] = A[1][0] = (E - F / Lt)*e.x*e.y;\n\tA[1][2] = A[2][1] = (E - F / Lt)*e.y*e.z;\n\tA[0][2] = A[2][0] = (E - F / Lt)*e.x*e.z;\n\n\treturn A;\n}\n\ndouble FESpringMaterial::StrainEnergy(FEDiscreteMaterialPoint& mp)\n{\n\t// calculate spring lengths\n\tdouble L0 = mp.m_dr0.norm();\n\tdouble Lt = mp.m_drt.norm();\n\tdouble DL = Lt - L0;\n\n\t// evaluate the spring force\n\treturn strainEnergy(DL);\n}\n\n//-----------------------------------------------------------------------------\n// FELinearSpring\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FELinearSpring, FESpringMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\");\nEND_FECORE_CLASS();\n\nFELinearSpring::FELinearSpring(FEModel* pfem) : FESpringMaterial(pfem) \n{\n\tm_E = 1.0;\n}\n\ndouble FELinearSpring::force(double dl)\n{\n\treturn m_E*dl;\n}\n\ndouble FELinearSpring::stiffness(double dl)\n{\n\treturn m_E;\n}\n\ndouble FELinearSpring::strainEnergy(double dl)\n{\n\treturn m_E*dl*dl/2;\n}\n\n//-----------------------------------------------------------------------------\n// FETensionOnlyLinearSpring\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETensionOnlyLinearSpring, FESpringMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\");\nEND_FECORE_CLASS();\n\ndouble FETensionOnlyLinearSpring::force(double dl)\n{\n\tif (dl >= 0) return m_E*dl; else return 0;\n}\n\ndouble FETensionOnlyLinearSpring::stiffness(double dl)\n{\n\treturn (dl >= 0 ? m_E : 0);\n}\n\ndouble FETensionOnlyLinearSpring::strainEnergy(double dl)\n{\n\tif(dl >= 0) return m_E*dl*dl/2; else return 0;\n}\n\n//-----------------------------------------------------------------------------\n// FEExperimentalSpring\n//-----------------------------------------------------------------------------\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEExperimentalSpring, FESpringMaterial)\n\tADD_PARAMETER(m_E , \"E\");\n\tADD_PARAMETER(m_sM, \"sM\");\n\tADD_PARAMETER(m_sm, \"sm\");\nEND_FECORE_CLASS();\n\nFEExperimentalSpring::FEExperimentalSpring(FEModel* pfem) : FESpringMaterial(pfem)\n{\n\tm_E = 0.0;\n\tm_sM = 0.0;\n\tm_sm = 0.0;\n}\n\ndouble FEExperimentalSpring::force(double dl)\n{\n\tif (dl >= 0.0)\n\t\treturn m_sM*(1.0 - exp(-m_E*dl / m_sM));\n\telse\n\t\treturn -m_sm*(1.0 - exp(m_E*dl / m_sm));\n}\n\ndouble FEExperimentalSpring::stiffness(double dl)\n{\n\tif (dl >= 0.0)\n\t\treturn m_E*exp(-m_E*dl / m_sM);\n\telse\n\t\treturn m_E*exp(m_E*dl / m_sm);\n}\n\ndouble FEExperimentalSpring::strainEnergy(double dl)\n{\n\tif (dl >= 0.0)\n\t\treturn m_sM*(m_sM/m_E*(exp(-m_E*dl/m_sM) - 1) + dl);\n\telse\n\t\treturn m_sm*(m_sm/m_E*(exp(-m_E*dl/m_sm) - 1) - dl);\n}\n"
  },
  {
    "path": "FEBioMech/FESpringMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiscreteElasticMaterial.h\"\n#include <FECore/FEFunction1D.h>\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! material class for discrete elements\nclass FEBIOMECH_API FESpringMaterial : public FEDiscreteElasticMaterial\n{\npublic:\n\tFESpringMaterial(FEModel* pfem) : FEDiscreteElasticMaterial(pfem) {}\n\n\tvec3d Force(FEDiscreteMaterialPoint& mp) override;\n\tmat3d Stiffness(FEDiscreteMaterialPoint& mp) override;\n\tvirtual double StrainEnergy(FEDiscreteMaterialPoint& mp) override;\n\n\tvirtual double force    (double dl) = 0;\n\tvirtual double stiffness(double dl) = 0;\n\tvirtual double strainEnergy(double dl) = 0;\n};\n\n//-----------------------------------------------------------------------------\n//! linear spring\nclass FEBIOMECH_API FELinearSpring : public FESpringMaterial\n{\npublic:\n\tFELinearSpring(FEModel* pfem);\n\tdouble force    (double dl) override;\n\tdouble stiffness(double dl) override;\n\tdouble strainEnergy(double dl) override;\n\npublic:\n\tdouble m_E;\t//!< spring constant\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! tension-only linear spring\nclass FEBIOMECH_API FETensionOnlyLinearSpring : public FESpringMaterial\n{\npublic:\n\tFETensionOnlyLinearSpring(FEModel* pfem) : FESpringMaterial(pfem){}\n\tdouble force    (double dl) override;\n\tdouble stiffness(double dl) override;\n\tdouble strainEnergy(double dl) override;\n\npublic:\n\tdouble m_E = 0; //!< spring constant\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FEExperimentalSpring : public FESpringMaterial\n{\npublic:\n\tFEExperimentalSpring(FEModel* fem);\n\n\tdouble force(double dl) override;\n\tdouble stiffness(double dl) override;\n\tdouble strainEnergy(double dl) override;\n\npublic:\n\tdouble\tm_E;\n\tdouble\tm_sM, m_sm;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESpringRuptureCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FESpringRuptureCriterion.h\"\n#include \"FEDiscreteElasticMaterial.h\"\n#include <FECore/FEElement.h>\n\nBEGIN_FECORE_CLASS(FESpringForceCriterion, FEMeshAdaptorCriterion)\nEND_FECORE_CLASS();\n\nFESpringForceCriterion::FESpringForceCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n}\n\nbool FESpringForceCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tFEDiscreteElasticMaterialPoint* ep = mp.ExtractData<FEDiscreteElasticMaterialPoint>();\n\tif (ep == nullptr) return false;\n\n\tvec3d& Ft = ep->m_Ft;\n\tdouble F = ep->m_drt*Ft;\n\n\tvalue = F;\n\treturn true;\n}\n\nBEGIN_FECORE_CLASS(FESpringStretchCriterion, FEMeshAdaptorCriterion)\nEND_FECORE_CLASS();\n\nFESpringStretchCriterion::FESpringStretchCriterion(FEModel* fem) : FEMeshAdaptorCriterion(fem)\n{\n}\n\nbool FESpringStretchCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& value)\n{\n\tFEDiscreteElasticMaterialPoint* ep = mp.ExtractData<FEDiscreteElasticMaterialPoint>();\n\tif (ep == nullptr) return false;\n\n\tdouble L0 = ep->m_dr0.norm();\n\tdouble Lt = ep->m_drt.norm();\n\tdouble s = Lt / L0;\n\n\tvalue = s;\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioMech/FESpringRuptureCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEMeshAdaptorCriterion.h>\n\nclass FESpringForceCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFESpringForceCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\n\tDECLARE_FECORE_CLASS()\n};\n\nclass FESpringStretchCriterion : public FEMeshAdaptorCriterion\n{\npublic:\n\tFESpringStretchCriterion(FEModel* fem);\n\n\tbool GetMaterialPointValue(FEMaterialPoint& mp, double& value) override;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FEBioMech/FEStVenantKirchhoff.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEStVenantKirchhoff.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEStVenantKirchhoff, FEElasticMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FEStVenantKirchhoff\n//////////////////////////////////////////////////////////////////////\n\nFEStVenantKirchhoff::FEStVenantKirchhoff(FEModel* pfem) : FEElasticMaterial(pfem) \n{\n\tm_E = 0.0;\n\tm_v = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEStVenantKirchhoff::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\n\t// calculate left Cauchy-Green tensor (ie. b-matrix)\n\tmat3ds b = pt.LeftCauchyGreen();\n\tmat3ds b2 = b.sqr();\n\n\t// calculate trace of Green-Lagrance strain tensor\n\tdouble trE = 0.5*(b.tr()-3);\n\n\t// inverse jacobian\n\tdouble Ji = 1.0 / pt.m_J;\n\n\t// calculate stress\n\t// s = (lam*trE*b - mu*(b2 - b))/J;\n\treturn b*(lam*trE*Ji) + (b2 - b)*(mu*Ji);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEStVenantKirchhoff::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// jacobian\n\tdouble J = pt.m_J;\n\n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\n\tdouble lam1 = lam / J;\n\tdouble mu1  = mu / J;\n\n\t// left cauchy-green matrix (i.e. the 'b' matrix)\n\tmat3ds b = pt.LeftCauchyGreen();\n\n\ttens4ds c = dyad1s(b)*lam1 + dyad4s(b)*(2.0*mu1);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEStVenantKirchhoff::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// lame parameters\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n    \n\t// calculate right Cauchy-Green tensor\n\tmat3ds C = pt.RightCauchyGreen();\n\tmat3ds C2 = C.sqr();\n    \n\tdouble trE = 0.5*(C.tr()-3);\n    double trE2 = 0.25*(C2.tr() - 2*C.tr() + 3);\n    \n\t// calculate strain energy density\n\treturn lam*trE*trE/2.0 + mu*trE2;\n}\n"
  },
  {
    "path": "FEBioMech/FEStVenantKirchhoff.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Linear elatic material for large deformations\n\n//! This material can be used when a body undergoes large rotations\n//! but small strains.\n\nclass FEStVenantKirchhoff : public FEElasticMaterial\n{\npublic:\n\tFEStVenantKirchhoff(FEModel* pfem);\n\npublic:\n\tdouble\tm_E;\t//!< Young's modulus\n\tdouble\tm_v;\t//!< Poisson's ratio\n\npublic:\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEStickyInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEStickyInterface.h\"\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\nFEStickySurface::Data::Data() \n{ \n\tgap = vec3d(0.0, 0.0, 0.0); \n\tscalar_gap = 0;\n\tpme = nullptr; \n}\n\nvoid FEStickySurface::Data::Serialize(DumpStream& ar)\n{\n\tar & gap;\n\tar & rs;\n\tar & Lm;\n\tar & tn;\n}\n\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEStickyInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon , \"laugon\"          )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol   , \"tolerance\"       );\n\tADD_PARAMETER(m_eps    , \"penalty\"         );\n\tADD_PARAMETER(m_naugmin, \"minaug\"          );\n\tADD_PARAMETER(m_naugmax, \"maxaug\"          );\n\tADD_PARAMETER(m_stol   , \"search_tolerance\");\n\tADD_PARAMETER(m_tmax   , \"max_traction\"    );\n\tADD_PARAMETER(m_snap   , \"snap_tol\"        );\n\tADD_PARAMETER(m_flip_secondary, \"flip_secondary\");\n\tADD_PARAMETER(m_gap_offset, \"gap_offset\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Creates a surface for use with a sliding interface. All surface data\n//! structures are allocated.\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FEStickySurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEContactSurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate other surface data\n\tm_data.resize(nn);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStickySurface::Serialize(DumpStream &ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tar & m_data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStickySurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ne = el.Nodes();\n    pt = vec3d(0,0,0);\n    for (int k=0; k<ne; ++k) pt += m_data[el.m_lnode[k]].tn;\n    pt /= ne;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStickySurface::GetNodalContactPressure(int nface, double* pn)\n{\n\tFESurfaceElement& f = Element(nface);\n\tint ne = f.Nodes();\n\tfor (int j= 0; j< ne; ++j) pn[j] = m_data[f.m_lnode[j]].tn.norm();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStickySurface::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& f = Element(nface);\n\tint ne = f.Nodes();\n\tfor (int j= 0; j< ne; ++j) tn[j] = m_data[f.m_lnode[j]].tn;\n}\n\n//=============================================================================\n//\n//\t\tF E S T I C K Y I N T E R F A C E\n//\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Constructor. Initialize default values.\nFEStickyInterface::FEStickyInterface(FEModel* pfem) : FEContactInterface(pfem), ss(pfem), ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\t// set parents\n\tss.SetContactInterface(this);\n\tms.SetContactInterface(this);\n\t\n\t// define sibling relationships\n\tss.SetSibling(&ms);\n\tms.SetSibling(&ss);\n\n\t// initial parameter values\n\tm_atol = 0.01;\n\tm_eps = 1.0;\n\tm_stol = 0.0001;\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\tm_tmax = 0.0;\n\tm_snap = 0.0;\n\tm_flip_secondary = false;\n\tm_gap_offset = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization. This function intializes the surfaces data and projects the\n//! primary surface onto the secondary surface.\n//! \nbool FEStickyInterface::Init()\n{\n\t// create the surfaces\n\tif (ss.Init() == false) return false;\n\tif (ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FEStickyInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tconst int LMSIZE = 6*(FEElement::MAX_NODES+1);\n\tvector<int> lm(LMSIZE);\n\n\tfor (int j=0; j<ss.Nodes(); ++j)\n\t{\n\t\tFEStickySurface::Data& snj = ss.m_data[j];\n\t\tFESurfaceElement* pe = snj.pme;\n\t\tif (pe != 0)\n\t\t{\n\t\t\tFESurfaceElement& me = *pe;\n\t\t\tint* en = &me.m_lnode[0];\n\n\t\t\tint n = me.Nodes();\n\t\t\tlm.assign(LMSIZE, -1);\n\n\t\t\tlm[0] = ss.Node(j).m_ID[dof_X];\n\t\t\tlm[1] = ss.Node(j).m_ID[dof_Y];\n\t\t\tlm[2] = ss.Node(j).m_ID[dof_Z];\n\t\t\tlm[3] = ss.Node(j).m_ID[dof_RU];\n\t\t\tlm[4] = ss.Node(j).m_ID[dof_RV];\n\t\t\tlm[5] = ss.Node(j).m_ID[dof_RW];\n\n\t\t\tfor (int k=0; k<n; ++k)\n\t\t\t{\n\t\t\t\tvector<int>& id = ms.Node(en[k]).m_ID;\n\t\t\t\tlm[6*(k+1)  ] = id[dof_X];\n\t\t\t\tlm[6*(k+1)+1] = id[dof_Y];\n\t\t\t\tlm[6*(k+1)+2] = id[dof_Z];\n\t\t\t\tlm[6*(k+1)+3] = id[dof_RU];\n\t\t\t\tlm[6*(k+1)+4] = id[dof_RV];\n\t\t\t\tlm[6*(k+1)+5] = id[dof_RW];\n\t\t\t}\n\n\t\t\tK.build_add(lm);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Interface activation\nvoid FEStickyInterface::Activate()\n{\n\t// Don't forget to call base member!\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(ss, ms, false);\n}\n\n//-----------------------------------------------------------------------------\n//! Update sticky interface data. This function re-evaluates the gaps between\n//! the primary node and their projections onto the secondary surface.\n//!\nvoid FEStickyInterface::Update()\n{\n\t// closest point projection method\n\tFEClosestPointProjection cpp(ms);\n\tcpp.HandleSpecialCases(true);\n\tcpp.SetTolerance(m_stol);\n\tcpp.Init();\n\n\t// get the mesh\n\tFEMesh& mesh = *ss.GetMesh();\n\n\t// loop over all primary nodes\n#pragma omp parallel for shared(cpp, mesh) schedule(dynamic, 5)\n\tfor (int i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFEStickySurface::Data& sni = ss.m_data[i];\n\t\tFESurfaceElement* pme = sni.pme;\n\t\tif (pme)\n\t\t{\n\t\t\t// get the current primary nodal position\n\t\t\tvec3d rt = ss.Node(i).m_rt;\n\t\t\tif (m_gap_offset != 0)\n\t\t\t{\n\t\t\t\tvec3d n = ss.NodeNormal(i);\n\t\t\t\trt += n * m_gap_offset;\n\t\t\t}\n\n\t\t\t// get the natural coordinates of the primary projection\n\t\t\t// onto the secondary element\n\t\t\tdouble r = sni.rs[0];\n\t\t\tdouble s = sni.rs[1];\n\n\t\t\t// get the nodal coordinates\n\t\t\tint ne = pme->Nodes();\n\t\t\tvec3d y[FEElement::MAX_NODES];\n\t\t\tfor (int l=0; l<ne; ++l) y[l] = ms.Node( pme->m_lnode[l] ).m_rt;\n\n\t\t\t// calculate the primary node projection\n\t\t\tvec3d q = pme->eval(y, r, s);\n\n\t\t\t// calculate the gap function\n\t\t\tsni.gap = rt - q;\n\n\t\t\t// see if the max traction was exceded\n\t\t\tif (m_tmax > 0.0)\n\t\t\t{\n\t\t\t\t// get primary node contact force\n\t\t\t\tvec3d tc = sni.Lm + sni.gap*m_eps;\n\n\t\t\t\t// calculate the secondary normal\n\t\t\t\tvec3d nu = ms.SurfaceNormal(*sni.pme, sni.rs[0], sni.rs[1]);\n\t\t\t\tif (m_flip_secondary) nu = -nu;\n\n\t\t\t\tdouble t = nu*tc;\n\t\t\t\tif (t > m_tmax)\n\t\t\t\t{\n\t\t\t\t\t// detach this node\n\t\t\t\t\tsni.gap = vec3d(0,0,0);\n\t\t\t\t\tsni.pme = 0;\n\t\t\t\t\tsni.Lm = vec3d(0,0,0);\n\t\t\t\t\tsni.tn = vec3d(0,0,0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (m_eps != 0)\n\t\t{\n\t\t\t// get the nodal position of this primary node\n\t\t\tFENode& node = ss.Node(i);\n\t\t\tvec3d x = node.m_rt;\n\t\t\tif (m_gap_offset != 0)\n\t\t\t{\n\t\t\t\tvec3d n = ss.NodeNormal(i);\n\t\t\t\tx += n * m_gap_offset;\n\t\t\t}\n\n\t\t\t// find the secondary element\n\t\t\tvec3d q; vec2d rs;\n\t\t\tFESurfaceElement* pme = cpp.Project(x, q, rs);\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// calculate the secondary normal\n\t\t\t\tvec3d nu = ms.SurfaceNormal(*pme, rs[0], rs[1]);\n\t\t\t\tif (m_flip_secondary) nu = -nu;\n\n\t\t\t\t// calculate gap\n\t\t\t\tdouble d = nu*(q - x);\n\n\t\t\t\t// only allow contact after penetration\n\t\t\t\tif (d > -m_snap)\n\t\t\t\t{\n\t\t\t\t\t// calculate signed distance\n\t\t\t\t\tsni.gap = x - q;\n\t\t\t\t\tsni.scalar_gap = d;\n\n\t\t\t\t\t// store the secondary element\n\t\t\t\t\tsni.pme = pme;\n\t\t\t\t\tsni.rs[0] = rs[0];\n\t\t\t\t\tsni.rs[1] = rs[1];\n\t\t\t\t}\n\t\t\t}\t\t\t\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project surface\n\nvoid FEStickyInterface::ProjectSurface(FEStickySurface& ss, FEStickySurface& ms, bool bmove)\n{\n\tif (m_eps == 0) return;\n\n\t// closest point projection method\n\tFEClosestPointProjection cpp(ms);\n\tcpp.HandleSpecialCases(true);\n\tcpp.SetTolerance(m_stol);\n\tcpp.Init();\n\n\t// loop over all primary nodes\n#pragma omp parallel for shared(cpp) schedule(dynamic, 5)\n\tfor (int i=0; i<ss.Nodes(); ++i)\n\t{\n\t\t// get the next node\n\t\tFENode& node = ss.Node(i);\n\t\tFEStickySurface::Data& sni = ss.m_data[i];\n\n\t\t// assume we won't find a projection\n\t\tsni.pme = 0;\n\n\t\t// get the nodal position of this primary node\n\t\tvec3d x = node.m_rt;\n\t\tif (m_gap_offset != 0)\n\t\t{\n\t\t\tvec3d n = ss.NodeNormal(i);\n\t\t\tx += n* m_gap_offset;\n\t\t}\n\n\t\t// find the secondary element\n\t\tvec3d q; vec2d rs;\n\t\tFESurfaceElement* pme = cpp.Project(x, q, rs);\n\t\tif (pme)\n\t\t{\n\t\t\t// calculate the secondary normal\n\t\t\tvec3d nu = ms.SurfaceNormal(*pme, rs[0], rs[1]);\n\t\t\tif (m_flip_secondary) nu = -nu;\n\n\t\t\t// calculate gap\n\t\t\tdouble d = nu*(q - x);\n\n\t\t\t// only allow contact after penetration\n\t\t\tif (d > 0)\n\t\t\t{\n\t\t\t\t// calculate signed distance\n\t\t\t\tsni.gap = x - q;\n\n\t\t\t\t// store the secondary element\n\t\t\t\tsni.pme = pme;\n\t\t\t\tsni.rs[0] = rs[0];\n\t\t\t\tsni.rs[1] = rs[1];\n\n\n\t\t\t\t// move the node if necessary\n\t\t\t\tif (bmove && (sni.gap.norm()>0))\n\t\t\t\t{\n\t\t\t\t\tnode.m_r0 = node.m_rt = q;\n\t\t\t\t\tsni.gap = vec3d(0,0,0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact forces for a tied interface.\n\nvoid FEStickyInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n#pragma omp parallel shared(R, tp)\n\t{\n\t\t// shape function values\n\t\tdouble N[FEElement::MAX_NODES];\n\n\t\t// element contact force vector\n\t\tvector<double> fe;\n\n\t\t// the lm array for this force vector\n\t\tvector<int> lm;\n\n\t\t// the en array\n\t\tvector<int> en;\n\n\t\tvector<int> sLM;\n\t\tvector<int> mLM;\n\n\t\t// loop over all primary facets\n\t\tconst int NE = ss.Elements();\n#pragma omp for\n\t\tfor (int j = 0; j < NE; ++j)\n\t\t{\n\t\t\t// get the primary element\n\t\t\tFESurfaceElement& sel = ss.Element(j);\n\n\t\t\t// get the element's LM vector\n\t\t\tss.UnpackLM(sel, sLM);\n\n\t\t\tint nseln = sel.Nodes();\n\n\t\t\tdouble* w = sel.GaussWeights();\n\n\t\t\t// loop over primary element nodes (which are the integration points as well)\n\t\t\tfor (int n = 0; n < nseln; ++n)\n\t\t\t{\n\t\t\t\tint m = sel.m_lnode[n];\n\n\t\t\t\tFEStickySurface::Data& sm = ss.m_data[m];\n\n\t\t\t\t// see if this node's constraint is active\n\t\t\t\t// that is, if it has a secondary element associated with it\n\t\t\t\t// TODO: is this a good way to test for an active constraint\n\t\t\t\t// The rigid wall criteria seems to work much better.\n\t\t\t\tif (sm.pme != 0)\n\t\t\t\t{\n\t\t\t\t\t// calculate jacobian\n\t\t\t\t\tdouble detJ = ss.jac0(sel, n);\n\n\t\t\t\t\t// get primary node contact force\n\t\t\t\t\tvec3d tc = sm.Lm + sm.gap * m_eps;\n\n\t\t\t\t\t// cap it\n\t\t\t\t\tif (m_tmax > 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\t// calculate the secondary normal\n\t\t\t\t\t\tvec3d nu = ms.SurfaceNormal(*sm.pme, sm.rs[0], sm.rs[1]);\n\t\t\t\t\t\tif (m_flip_secondary) nu = -nu;\n\n\t\t\t\t\t\tdouble t = nu * tc;\n\t\t\t\t\t\tif (t > m_tmax) tc = vec3d(0, 0, 0);\n\t\t\t\t\t}\n\n\t\t\t\t\t// store traction\n\t\t\t\t\tsm.tn = tc;\n\n\t\t\t\t\t// get the secondary element\n\t\t\t\t\tFESurfaceElement& mel = *sm.pme;\n\t\t\t\t\tms.UnpackLM(mel, mLM);\n\n\t\t\t\t\tint nmeln = mel.Nodes();\n\n\t\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t\t// onto the secondary element\n\t\t\t\t\tdouble r = sm.rs[0];\n\t\t\t\t\tdouble s = sm.rs[1];\n\n\t\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\t\tmel.shape_fnc(N, r, s);\n\n\t\t\t\t\t// calculate force vector\n\t\t\t\t\tfe.resize(3 * (nmeln + 1));\n\t\t\t\t\tfe[0] = -detJ * w[n] * tc.x;\n\t\t\t\t\tfe[1] = -detJ * w[n] * tc.y;\n\t\t\t\t\tfe[2] = -detJ * w[n] * tc.z;\n\t\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tfe[3 * (l + 1)] = detJ * w[n] * tc.x * N[l];\n\t\t\t\t\t\tfe[3 * (l + 1) + 1] = detJ * w[n] * tc.y * N[l];\n\t\t\t\t\t\tfe[3 * (l + 1) + 2] = detJ * w[n] * tc.z * N[l];\n\t\t\t\t\t}\n\n\t\t\t\t\t// fill the lm array\n\t\t\t\t\tlm.resize(3 * (nmeln + 1));\n\t\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\n\t\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tlm[3 * (l + 1)] = mLM[l * 3];\n\t\t\t\t\t\tlm[3 * (l + 1) + 1] = mLM[l * 3 + 1];\n\t\t\t\t\t\tlm[3 * (l + 1) + 2] = mLM[l * 3 + 2];\n\t\t\t\t\t}\n\n\t\t\t\t\t// fill the en array\n\t\t\t\t\ten.resize(nmeln + 1);\n\t\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\t\tfor (int l = 0; l < nmeln; ++l) en[l + 1] = mel.m_node[l];\n\n\t\t\t\t\t// assemble into global force vector\n\t\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness matrix contribution.\nvoid FEStickyInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, lm, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tFEElementMatrix ke;\n\n\t// shape functions\n\tdouble H[MN];\n\n\t// loop over all primary elements\n\tconst int NE = ss.Elements();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& se = ss.Element(i);\n\t\tint nseln = se.Nodes();\n\n\t\t// get the element's LM vector\n\t\tss.UnpackLM(se, sLM);\n\n\t\tdouble* w = se.GaussWeights();\n\n\t\t// loop over all integration points (that is nodes)\n\t\tfor (int n=0; n<nseln; ++n)\n\t\t{\n\t\t\tint m = se.m_lnode[n];\n\n\t\t\tFEStickySurface::Data& sm = ss.m_data[m];\n\n\t\t\t// get the secondary element\n\t\t\tFESurfaceElement* pme = sm.pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *pme;\n\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdouble detJ = ss.jac0(se, n);\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tdouble r = sm.rs[0];\n\t\t\t\tdouble s = sm.rs[1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tme.shape_fnc(H, r, s);\n\n\t\t\t\t// number of degrees of freedom\n\t\t\t\tint ndof = 3*(1 + nmeln);\n\n\t\t\t\t// fill stiffness matrix\n\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\tke[0][0] = w[n]*detJ*m_eps;\n\t\t\t\tke[1][1] = w[n]*detJ*m_eps;\n\t\t\t\tke[2][2] = w[n]*detJ*m_eps;\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tke[0][3+3*k  ] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[1][3+3*k+1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[2][3+3*k+2] = -w[n]*detJ*m_eps*H[k];\n\n\t\t\t\t\tke[3+3*k  ][0] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+1][1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+2][2] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t}\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t\tfor (int l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3+3*k  ][3+3*l  ] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+1][3+3*l+1] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+2][3+3*l+2] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t}\n\n\t\t\t\t// create lm array\n\t\t\t\tlm.resize(3*(1+nmeln));\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (int k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (int k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\t\t\t\t\t\t\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Do an augmentation.\nbool FEStickyInterface::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFEStickySurface::Data& si = ss.m_data[i];\n\t\tvec3d lm = si.Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFEStickySurface::Data& si = ss.m_data[i];\n\n\t\tvec3d lm = si.Lm + si.gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tif (si.pme != 0)\n\t\t{\n\t\t\tdouble g = si.gap.norm();\n\t\t\tnormgc += g*g;\n\t\t\t++N;\n\t\t}\n\t}\t\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\t// check convergence of constraints\n\tfeLog(\" tied interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\t\t\n\t// check convergence\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tif (bconv == false) \n\t{\n\t\tfor (i=0; i<ss.Nodes(); ++i)\n\t\t{\n\t\t\tFEStickySurface::Data& si = ss.m_data[i];\n\t\t\t// update Lagrange multipliers\n\t\t\tsi.Lm = si.Lm + si.gap*m_eps;\n\t\t}\t\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize the data to the archive.\nvoid FEStickyInterface::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tms.Serialize(ar);\n\tss.Serialize(ar);\n\n\t// restore secondary element pointers\n\tSerializePointers(ss, ms, ar);\n\tSerializePointers(ms, ss, ar);\n}\n\nvoid FEStickyInterface::SerializePointers(FEStickySurface& ss, FEStickySurface& ms, DumpStream& ar)\n{\n\tif (ar.IsSaving())\n\t{\n\t\tfor (int i = 0; i < ss.m_data.size(); i++)\n\t\t{\n\t\t\tFESurfaceElement* pe = ss.m_data[i].pme;\n\t\t\tint eid = (pe ? pe->m_lid : -1);\n\t\t\tar << eid;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < ss.m_data.size(); i++)\n\t\t{\n\t\t\tint eid = -1;\n\t\t\tar >> eid;\n\t\t\tif (eid >= 0)\n\t\t\t{\n\t\t\t\tFESurfaceElement* pe = &ms.Element(eid);\n\t\t\t\tss.m_data[i].pme = pe;\n\t\t\t}\n\t\t\telse ss.m_data[i].pme = nullptr;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEStickyInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! This class describes a contact surface used for sticky contact.\n\n//!\tthis class is used in contact analyses to describe a contacting\n//! surface in a sticky contact interface.\n\nclass FEStickySurface : public FEContactSurface\n{\npublic:\n\tclass Data \n\t{\n\tpublic:\n\t\tData();\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\tpublic:\n\t\tvec3d\t\t\t\tgap;\t//!< \"gap\" function\n\t\tdouble\t\t\t\tscalar_gap;\n\t\tvec2d\t\t\t\trs;\t\t//!< natural coordinates of projection on secondary surface element\n\t\tvec3d\t\t\t\tLm;\t\t//!< Lagrange multiplier\n\t\tvec3d\t\t\t\ttn;\t\t//!< traction vector\n\t\tFESurfaceElement*\tpme;\t//!< secondary surface element a node penetrates\n\t};\n\npublic:\n\t//! constructor\n\tFEStickySurface(FEModel* pfem) : FEContactSurface(pfem) {}\n\n\t//! Initializes data structures\n\tbool Init();\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt);\n\tvoid GetNodalContactPressure(int nface, double* pn);\n\tvoid GetNodalContactTraction(int nface, vec3d* tn);\n\npublic:\n\tvector<Data>\tm_data;\t//!< node contact data\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a sticky interface.\n//! A sticky interface is like tied, but nodes are only tied when they come into\n//! contact.\n\nclass FEStickyInterface : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEStickyInterface(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FEStickyInterface(){}\n\n\t//! Initializes sliding interface\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! projects nodes onto secondary surface\n\tvoid ProjectSurface(FEStickySurface& ss, FEStickySurface& ms, bool bmove = false);\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &ss; }\n\tFESurface* GetSecondarySurface() override { return &ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprivate:\n\tvoid SerializePointers(FEStickySurface& ss, FEStickySurface& ms, DumpStream& ar);\n\npublic:\n\tFEStickySurface\tss;\t//!< primary surface\n\tFEStickySurface\tms;\t//!< secondary surface\n\n\tdouble\t\tm_atol;\t\t//!< augmentation tolerance\n\tdouble\t\tm_eps;\t\t//!< penalty scale factor\n\tdouble\t\tm_stol;\t\t//!< search tolerance\n\tint\t\t\tm_naugmax;\t//!< maximum nr of augmentations\n\tint\t\t\tm_naugmin;\t//!< minimum nr of augmentations\n\tdouble\t\tm_tmax;\t\t//!< max traction\n\tdouble\t\tm_snap;\t\t//!< snap tolerance\n\tbool\t\tm_flip_secondary; //!< flip the normal on the secondary surface\n\tdouble\t\tm_gap_offset; //!< offset added to gap\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESurfaceAttractionBodyForce.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfaceAttractionBodyForce.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FESurfaceAttractionBodyForce, FEBodyForce);\n    ADD_PARAMETER(m_blt     , \"blt\"          )->setUnits(UNIT_LENGTH);\n    ADD_PARAMETER(m_bsf     , \"bsf\"          );\n    ADD_PARAMETER(m_stol    , \"search_tol\"   );\n    ADD_PARAMETER(m_sradius , \"search_radius\")->setUnits(UNIT_LENGTH);\n\n\tADD_PROPERTY(m_s, \"surface\", FEProperty::Reference);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESurfaceAttractionBodyForce::FESurfaceAttractionBodyForce(FEModel* pfem) : FEBodyForce(pfem)\n{\n    m_blt = 1;\n    m_bsf = 0;\n    m_stol = 0.01;\n    m_sradius = 0;      // no search radius limitation\n\n\tm_s = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FESurfaceAttractionBodyForce::Init()\n{\n\tif (m_s == nullptr) return false;\n\n    if (!m_s->Init()) return false;\n    FEBodyLoad::Init();\n\n\tFEClosestPointProjection cpp(*m_s);\n\tcpp.SetTolerance(m_stol);\n\tcpp.SetSearchRadius(m_sradius);\n\tcpp.HandleSpecialCases(true);\n\tcpp.Init();\n\n    // allocate projection point vector array\n    int nel = GetMesh().Elements();\n    m_q.resize(nel);\n    \n    const char s[] = \"%%\";\n\n    feLog(\"Initializing projections to attractive surface...\\n\");\n    \n    // evaluate projection of integration points to attractive surface\n    for (int i=0; i<Domains(); ++i) {\n        FEDomain* dom = Domain(i);\n        int nde = dom->Elements();\n        feLog(\"Domain %d: %d elements\\n\",i+1,nde);\n        int pcp = 0;\n        feLog(\"\\r%d %s done\",pcp,s);\n        for (int j=0; j<nde; ++j) {\n            int pc = 100*(j+1)/nde;\n            if (pc > pcp) {\n                feLog(\"\\r%d %s done\",pc,s);\n                pcp = pc;\n            }\n            FEElement& el = dom->ElementRef(j);\n            int nint = el.GaussPoints();\n            int eid = el.GetID() - 1;\n            m_q[eid].resize(nint);\n            for (int k=0; k<nint; ++k) {\n                FEMaterialPoint* mp = el.GetMaterialPoint(k);\n                vec3d x = mp->m_r0;\n\n\t\t\t\tvec2d rs(0, 0);\n\t\t\t\tFESurfaceElement* pme = nullptr;\n\t\t\t\tpme = cpp.Project(x, m_q[eid][k], rs);\n\n                if (pme == nullptr)\n                    m_q[eid][k] = x + vec3d(100*m_blt,100*m_blt,100*m_blt);\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESurfaceAttractionBodyForce::force(FEMaterialPoint& mp)\n{\n    // get element number for this material point\n    int eid = mp.m_elem->GetID() - 1;\n    \n    vec3d q = m_q[eid][mp.m_index];\n    \n    // initialize net force\n    vec3d f(0,0,0);\n\n    // calculate net force\n    vec3d g = mp.m_r0 - q;\n    double r = g.unit();\n    f = g*(m_bsf*exp(-r/m_blt));\n\n    return f;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESurfaceAttractionBodyForce::divforce(FEMaterialPoint& mp)\n{\n    // get element number for this material point\n    int eid = mp.m_elem->GetID() - 1;\n    \n    vec3d q = m_q[eid][mp.m_index];\n    \n    // calculate net force\n    vec3d g = mp.m_r0 - q;\n    double r = g.unit();\n    \n    return (3-r/m_blt)*(m_bsf*exp(-r/m_blt));\n}\n\nmat3d FESurfaceAttractionBodyForce::stiffness(FEMaterialPoint& mp)\n{\n    return mat3ds(0,0,0,0,0,0);\n}\n"
  },
  {
    "path": "FEBioMech/FESurfaceAttractionBodyForce.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBodyForce.h\"\n#include <FECore/FESurface.h>\n\n//-----------------------------------------------------------------------------\n//! This class defines a surface attraction body force\n\nclass FESurfaceAttractionBodyForce : public FEBodyForce\n{\npublic:\n    FESurfaceAttractionBodyForce(FEModel* pfem);\n\n    vec3d force(FEMaterialPoint& mp) override;\n\n    double divforce(FEMaterialPoint& mp) override;\n    \n    mat3d stiffness(FEMaterialPoint& mp) override;\n    \n    // initialize\n    bool Init() override;\n   \nprivate:\n    vector< vector<vec3d> > m_q;    //!<  projection points\n\npublic:\n    FESurface*  m_s;    //!< the attractive surface\n    \npublic:\n    double  m_blt;      //!<  boundary layer thickness\n    double  m_bsf;      //!<  body force scale factor\n    double  m_stol;     //!<  search tolerance\n    double  m_sradius;  //!<  search radius\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESurfaceForceUniform.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfaceForceUniform.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEFacetSet.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FESurfaceForceUniform, FESurfaceLoad)\n    ADD_PARAMETER(m_scale   , \"scale\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n    ADD_PARAMETER(m_force   , \"force\")->SetFlags(0)->setUnits(UNIT_FORCE);\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESurfaceForceUniform::FESurfaceForceUniform(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_scale = 1.0;\n    m_force = m_traction = vec3d(0, 0, 0);\n    m_bshellb = false;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FESurfaceForceUniform::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n// initialization\nbool FESurfaceForceUniform::Init()\n{\n    // get the degrees of freedom\n    m_dof.Clear();\n    if (m_bshellb == false)\n    {\n        m_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n    }\n    else\n    {\n        m_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n    }\n    if (m_dof.IsEmpty()) return false;\n    \n    if (FESurfaceLoad::Init() == false) return false;\n    \n    // evaluate uniform traction based on applied force\n    FESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n    double area = 0;\n    for (int i=0; i<surf.Elements(); ++i) {\n        FESurfaceElement& el = surf.Element(i);\n        area += surf.FaceArea(el);\n    }\n    m_traction = m_force/area;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceForceUniform::LoadVector(FEGlobalVector& R)\n{\n    FESurface& surf = GetSurface();\n    surf.SetShellBottom(m_bshellb);\n    \n    // evaluate the integral\n    FESurfaceForceUniform* load = this;\n    surf.LoadVector(R, m_dof, true, [=](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n        \n        // evaluate traction at this material point\n        vec3d t = m_traction*m_scale;\n        if (load->m_bshellb) t = -t;\n        \n        double J = (pt.dxr ^ pt.dxs).norm();\n        \n        double H_u = dof_a.shape;\n        \n        val[0] = H_u * t.x*J;\n        val[1] = H_u * t.y*J;\n        val[2] = H_u * t.z*J;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceForceUniform::StiffnessMatrix(FELinearSystem& LS)\n{\n    // Nothing to do here.\n    // TODO: I think if the linear flag is false, I do need to evaluate a stiffness.\n}\n"
  },
  {
    "path": "FEBioMech/FESurfaceForceUniform.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! FESurfaceForceUniform is a surface that has a constant (deformation independant)\n//! force on it, distributed as a uniform traction\n//!\nclass FESurfaceForceUniform : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FESurfaceForceUniform(FEModel* pfem);\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    // initialization\n    bool Init() override;\n    \npublic:\n    //! calculate contact forces\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! calculate stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \nprotected:\n    double          m_scale;    //!< scale factor for traction\n    vec3d           m_force;    //!< vector force\n    bool            m_bshellb;  //!< flag to apply force on shell bottom\nprivate:\n    vec3d           m_traction; //!< calculated uniform traction\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FESymmetryPlane.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESymmetryPlane.h\"\n#include \"FECore/FECoreKernel.h\"\n//#include <FECore/FEModel.h>\n\nBEGIN_FECORE_CLASS(FESymmetryPlane, FESurfaceConstraint)\n    ADD_PARAMETER(m_lc.m_laugon, \"laugon\");\n    ADD_PARAMETER(m_lc.m_tol, \"tol\");\n    ADD_PARAMETER(m_lc.m_eps, \"penalty\");\n    ADD_PARAMETER(m_lc.m_rhs, \"rhs\");\n    ADD_PARAMETER(m_lc.m_naugmin, \"minaug\");\n    ADD_PARAMETER(m_lc.m_naugmax, \"maxaug\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESymmetryPlane::FESymmetryPlane(FEModel* pfem) : FESurfaceConstraint(pfem), m_surf(pfem), m_lc(pfem)\n{\n\tm_binit = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures.\nvoid FESymmetryPlane::Activate()\n{\n    // don't forget to call base class\n    FESurfaceConstraint::Activate();\n\n    if (m_binit == false)\n    {\n        // get the dof indices\n        int dofX = GetDOFIndex(\"x\");\n        int dofY = GetDOFIndex(\"y\");\n        int dofZ = GetDOFIndex(\"z\");\n        int dofSX = GetDOFIndex(\"sx\");\n        int dofSY = GetDOFIndex(\"sy\");\n        int dofSZ = GetDOFIndex(\"sz\");\n\n        // evaluate the nodal normals\n        m_surf.UpdateNodeNormals();\n\n        // create linear constraints\n        // for a symmetry plane the constraint on (ux, uy, uz) is\n        // nx*ux + ny*uy + nz*uz = 0\n        for (int i = 0; i < m_surf.Nodes(); ++i) {\n            FENode node = m_surf.Node(i);\n            if ((node.HasFlags(FENode::EXCLUDE) == false) && (node.m_rid == -1)) {\n                vec3d nu = m_surf.NodeNormal(i);\n                FEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n                pLC->AddDOF(node.GetID(), dofX, nu.x);\n                pLC->AddDOF(node.GetID(), dofY, nu.y);\n                pLC->AddDOF(node.GetID(), dofZ, nu.z);\n                // add the linear constraint to the system\n                m_lc.add(pLC);\n            }\n        }\n\n        // for nodes that belong to shells, also constraint the shell bottom face displacements\n        for (int i = 0; i < m_surf.Nodes(); ++i) {\n            FENode node = m_surf.Node(i);\n            if ((node.HasFlags(FENode::EXCLUDE) == false) && (node.HasFlags(FENode::SHELL)) && (node.m_rid == -1)) {\n                vec3d nu = m_surf.NodeNormal(i);\n                FEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, GetFEModel());\n                pLC->AddDOF(node.GetID(), dofSX, nu.x);\n                pLC->AddDOF(node.GetID(), dofSY, nu.y);\n                pLC->AddDOF(node.GetID(), dofSZ, nu.z);\n                // add the linear constraint to the system\n                m_lc.add(pLC);\n            }\n        }\n\n        m_lc.Init();\n        m_lc.Activate();\n\n        m_binit = true;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESymmetryPlane::Init()\n{\n\t// initialize surface\n    return m_surf.Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESymmetryPlane::Serialize(DumpStream& ar) \n{ \n\tFESurfaceConstraint::Serialize(ar);\n\tar& m_binit;\n\tm_lc.Serialize(ar); \n\tm_surf.Serialize(ar);\n}\nvoid FESymmetryPlane::LoadVector(FEGlobalVector& R, const FETimeInfo& tp) { m_lc.LoadVector(R, tp); }\nvoid FESymmetryPlane::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) { m_lc.StiffnessMatrix(LS, tp); }\nbool FESymmetryPlane::Augment(int naug, const FETimeInfo& tp) { return m_lc.Augment(naug, tp); }\nvoid FESymmetryPlane::BuildMatrixProfile(FEGlobalMatrix& M) { m_lc.BuildMatrixProfile(M); }\nint FESymmetryPlane::InitEquations(int neq) { return m_lc.InitEquations(neq); }\nvoid FESymmetryPlane::Update(const std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.Update(Ui, ui); }\nvoid FESymmetryPlane::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) { m_lc.UpdateIncrements(Ui, ui); }\nvoid FESymmetryPlane::PrepStep() { m_lc.PrepStep(); }\n"
  },
  {
    "path": "FEBioMech/FESymmetryPlane.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FESurface.h>\n\n//-----------------------------------------------------------------------------\n//! The FESymmetryPlane class implements a symmetry plane\n//! as a linear constraint on the components of the solid displacement.\n\nclass FESymmetryPlane : public FESurfaceConstraint\n{\npublic:\n    //! constructor\n    FESymmetryPlane(FEModel* pfem);\n    \n    //! destructor\n    ~FESymmetryPlane() {}\n    \n    //! Activation\n    void Activate() override;\n    \n    //! initialization\n    bool Init() override;\n    \n    //! Get the surface\n    FESurface* GetSurface() override { return &m_surf; }\n\n    // allocate equations\n    int InitEquations(int neq) override;\n    \nprotected:\n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \npublic:\n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n\n    //! add the linear constraint contributions to the residual\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n    //! add the linear constraint contributions to the stiffness matrix\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n    //! do the augmentation\n    bool Augment(int naug, const FETimeInfo& tp) override;\n\n    //! build connectivity for matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n    \nprotected:\n    FESurface    m_surf;\n\tbool\t\tm_binit;\n\nprivate:\n    FELinearConstraintSet m_lc;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETCNonlinearOrthotropic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <limits>\n#include \"FETCNonlinearOrthotropic.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETCNonlinearOrthotropic, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, \"c1\");\n\tADD_PARAMETER(m_c2, \"c2\");\n\tADD_PARAMETER(m_beta, 3, FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi , 3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\" );\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FETCNonlinearOrthotropic\n//////////////////////////////////////////////////////////////////////\n\nFETCNonlinearOrthotropic::FETCNonlinearOrthotropic(FEModel* pfem) : FEUncoupledMaterial(pfem) \n{\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\tm_beta[0] = m_beta[1] = m_beta[2] = 0.0;\n\tm_ksi[0] = m_ksi[1] = m_ksi[2] = 0.0;\n\n\tm_epsf = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the deviatoric Cauchy stress\nmat3ds FETCNonlinearOrthotropic::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0 / J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\tdouble Jm23 = Jm13*Jm13;\n\tdouble twoJi = 2.0*Ji;\n\n\t// invariants of B\n\tdouble I1, I2, I4a, I4b, I4c;\n\n\t// strain energy derivatives\n\tdouble W1, W2, W4a, W4b, W4c;\n\n\t// current local material axis\n\tvec3d a0, b0, c0, a, b, c;\n\tdouble la, lb, lc, lat, lbt, lct;\n\n\tdouble w1pw2i1; // = W1 + W2*I1\n\n\tconst double third = 1.0/3.0;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber directions\n\ta0.x = Q[0][0]; b0.x = Q[0][1]; c0.x = Q[0][2];\n\ta0.y = Q[1][0]; b0.y = Q[1][1]; c0.y = Q[1][2];\n\ta0.z = Q[2][0]; b0.z = Q[2][1]; c0.z = Q[2][2];\n\n\t// calculate the current material axes lam*a = F*a0;\n\ta = F*a0;\n\tb = F*b0;\n\tc = F*c0;\n\n\t// normalize material axis and store fiber stretch\n\tla  = a.unit();\n\tlat = la*Jm13; // i.e. lambda tilde\n\n\tlb  = b.unit();\n\tlbt = lb*Jm13;\n\n\tlc  = c.unit();\n\tlct = lc*Jm13;\n\n\t// get deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tI1 = B.tr();\n\tI2 = 0.5*(I1*I1 - B2.tr());\n\tI4a = lat*lat;\n\tI4b = lbt*lbt;\n\tI4c = lct*lct;\n\n\t// --- put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tW1 = m_c1;\n\tW2 = m_c2;\n\n\t// fiber a\n\tif (lat > 1)\n\t{\n\t\tdouble lati = 1.0/lat;\n\t\tdouble Wl;\n\t\tWl  = m_beta[0]*m_ksi[0]*pow((lat - 1.0), m_beta[0]-1.0);\n\t\tW4a = 0.5*lati*Wl;\n\t}\n\telse \n\t{\n\t\tW4a = 0;\n\t}\n\n\t// fiber b\n\tif (lbt > 1)\n\t{\n\t\tdouble lbti = 1.0/lbt;\n\t\tdouble Wl;\n\t\tWl  = m_beta[1]*m_ksi[1]*pow((lbt - 1.0), m_beta[1]-1.0);\n\t\tW4b = 0.5*lbti*Wl;\n\t}\n\telse \n\t{\n\t\tW4b = 0;\n\t}\n\n\t// fiber c\n\tif (lct > 1)\n\t{\n\t\tdouble lcti = 1.0/lct;\n\t\tdouble Wl;\n\t\tWl  = m_beta[2]*m_ksi[2]*pow((lct - 1.0), m_beta[2]-1.0);\n\t\tW4c = 0.5*lcti*Wl;\n\t}\n\telse \n\t{\n\t\tW4c = 0;\n\t}\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\t// (we commented out the matrix components we do not need)\n\tw1pw2i1 = W1 + W2*I1;\n\t\n\tmat3ds AxA = dyad(a);\n\tmat3ds BxB = dyad(b);\n\tmat3ds CxC = dyad(c);\n\n\tmat3ds T = B*w1pw2i1 - B2*W2 + AxA*(W4a*I4a) + BxB*(W4b*I4b) + CxC*(W4c*I4c);\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FETCNonlinearOrthotropic::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\tconst double third = 1.0 / 3.0;\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\tdouble Jm23 = Jm13*Jm13;\n\tdouble Ji = 1.0/J;\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tdouble s[3][3], trs;\n\tmat3ds& es = pt.m_s;\n\ts[0][0] = es.xx();\n\ts[1][1] = es.yy();\n\ts[2][2] = es.zz();\n\ts[0][1] = s[1][0] = es.xy();\n\ts[1][2] = s[2][1] = es.yz();\n\ts[0][2] = s[2][0] = es.xz();\n\n\ttrs = (s[0][0] + s[1][1] + s[2][2])*third;\n\ts[0][0] -= trs;\t\n\ts[1][1] -= trs;\t\n\ts[2][2] -= trs;\n\n\t// current material axis lam*a = F*a0;\n\tvec3d a, b, c, a0, b0, c0;\n\tdouble la, lb, lc, lat, lbt, lct;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber directions\n\ta0.x = Q[0][0]; b0.x = Q[0][1]; c0.x = Q[0][2];\n\ta0.y = Q[1][0]; b0.y = Q[1][1]; c0.y = Q[1][2];\n\ta0.z = Q[2][0]; b0.z = Q[2][1]; c0.z = Q[2][2];\n\n\t// calculate the current material axes lam*a = F*a0;\n\ta.x = F[0][0]*a0.x + F[0][1]*a0.y + F[0][2]*a0.z;\n\ta.y = F[1][0]*a0.x + F[1][1]*a0.y + F[1][2]*a0.z;\n\ta.z = F[2][0]*a0.x + F[2][1]*a0.y + F[2][2]*a0.z;\n\n\tb.x = F[0][0]*b0.x + F[0][1]*b0.y + F[0][2]*b0.z;\n\tb.y = F[1][0]*b0.x + F[1][1]*b0.y + F[1][2]*b0.z;\n\tb.z = F[2][0]*b0.x + F[2][1]*b0.y + F[2][2]*b0.z;\n\n\tc.x = F[0][0]*c0.x + F[0][1]*c0.y + F[0][2]*c0.z;\n\tc.y = F[1][0]*c0.x + F[1][1]*c0.y + F[1][2]*c0.z;\n\tc.z = F[2][0]*c0.x + F[2][1]*c0.y + F[2][2]*c0.z;\n\n\tla = a.unit();\n\tlb = b.unit();\n\tlc = c.unit();\n\n\t// deviatoric stretch\n\tlat = la*Jm13;\n\tlbt = lb*Jm13;\n\tlct = lc*Jm13;\n\n\t// deviatoric right Cauchy-Green tensor: C = Ft*F\n\tdouble C[3][3];\n\tC[0][0] = Jm23*(F[0][0]*F[0][0]+F[1][0]*F[1][0]+F[2][0]*F[2][0]);\n\tC[0][1] = Jm23*(F[0][0]*F[0][1]+F[1][0]*F[1][1]+F[2][0]*F[2][1]);\n\tC[0][2] = Jm23*(F[0][0]*F[0][2]+F[1][0]*F[1][2]+F[2][0]*F[2][2]);\n\n\tC[1][0] = Jm23*(F[0][1]*F[0][0]+F[1][1]*F[1][0]+F[2][1]*F[2][0]);\n\tC[1][1] = Jm23*(F[0][1]*F[0][1]+F[1][1]*F[1][1]+F[2][1]*F[2][1]);\n\tC[1][2] = Jm23*(F[0][1]*F[0][2]+F[1][1]*F[1][2]+F[2][1]*F[2][2]);\n\n\tC[2][0] = Jm23*(F[0][2]*F[0][0]+F[1][2]*F[1][0]+F[2][2]*F[2][0]);\n\tC[2][1] = Jm23*(F[0][2]*F[0][1]+F[1][2]*F[1][1]+F[2][2]*F[2][1]);\n\tC[2][2] = Jm23*(F[0][2]*F[0][2]+F[1][2]*F[1][2]+F[2][2]*F[2][2]);\n\n\t// square of C\n\t// (we commented out the components we don't need)\n\tdouble C2[3][3];\n\tC2[0][0] = C[0][0]*C[0][0]+C[0][1]*C[1][0]+C[0][2]*C[2][0];\n//\tC2[0][1] = C[0][0]*C[0][1]+C[0][1]*C[1][1]+C[0][2]*C[2][1];\n//\tC2[0][2] = C[0][0]*C[0][2]+C[0][1]*C[1][2]+C[0][2]*C[2][2];\n\n//\tC2[1][0] = C[1][0]*C[0][0]+C[1][1]*C[1][0]+C[1][2]*C[2][0];\n\tC2[1][1] = C[1][0]*C[0][1]+C[1][1]*C[1][1]+C[1][2]*C[2][1];\n//\tC2[1][2] = C[1][0]*C[0][2]+C[1][1]*C[1][2]+C[1][2]*C[2][2];\n\n//\tC2[2][0] = C[2][0]*C[0][0]+C[2][1]*C[1][0]+C[2][2]*C[2][0];\n//\tC2[2][1] = C[2][0]*C[0][1]+C[2][1]*C[1][1]+C[2][2]*C[2][1];\n\tC2[2][2] = C[2][0]*C[0][2]+C[2][1]*C[1][2]+C[2][2]*C[2][2];\n\n\t// Invariants of C\n\tdouble I1 = C[0][0] + C[1][1] + C[2][2];\n\tdouble I2 = 0.5*(I1*I1 - (C2[0][0] + C2[1][1] + C2[2][2]));\n\tdouble I4a = lat*lat;\n\tdouble I4b = lbt*lbt;\n\tdouble I4c = lct*lct;\n\n\t// calculate left Cauchy-Green tensor: B = F*Ft\n\tdouble B[3][3];\n\tB[0][0] = Jm23*(F[0][0]*F[0][0]+F[0][1]*F[0][1]+F[0][2]*F[0][2]);\n\tB[0][1] = Jm23*(F[0][0]*F[1][0]+F[0][1]*F[1][1]+F[0][2]*F[1][2]);\n\tB[0][2] = Jm23*(F[0][0]*F[2][0]+F[0][1]*F[2][1]+F[0][2]*F[2][2]);\n\n\tB[1][0] = Jm23*(F[1][0]*F[0][0]+F[1][1]*F[0][1]+F[1][2]*F[0][2]);\n\tB[1][1] = Jm23*(F[1][0]*F[1][0]+F[1][1]*F[1][1]+F[1][2]*F[1][2]);\n\tB[1][2] = Jm23*(F[1][0]*F[2][0]+F[1][1]*F[2][1]+F[1][2]*F[2][2]);\n\n\tB[2][0] = Jm23*(F[2][0]*F[0][0]+F[2][1]*F[0][1]+F[2][2]*F[0][2]);\n\tB[2][1] = Jm23*(F[2][0]*F[1][0]+F[2][1]*F[1][1]+F[2][2]*F[1][2]);\n\tB[2][2] = Jm23*(F[2][0]*F[2][0]+F[2][1]*F[2][1]+F[2][2]*F[2][2]);\n\n\t// calculate square of B\n\t// (we commented out the components we don't need)\n\tdouble B2[3][3];\n\tB2[0][0] = B[0][0]*B[0][0]+B[0][1]*B[1][0]+B[0][2]*B[2][0];\n\tB2[0][1] = B[0][0]*B[0][1]+B[0][1]*B[1][1]+B[0][2]*B[2][1];\n\tB2[0][2] = B[0][0]*B[0][2]+B[0][1]*B[1][2]+B[0][2]*B[2][2];\n\n//\tB2[1][0] = B[1][0]*B[0][0]+B[1][1]*B[1][0]+B[1][2]*B[2][0];\n\tB2[1][1] = B[1][0]*B[0][1]+B[1][1]*B[1][1]+B[1][2]*B[2][1];\n\tB2[1][2] = B[1][0]*B[0][2]+B[1][1]*B[1][2]+B[1][2]*B[2][2];\n\n//\tB2[2][0] = B[2][0]*B[0][0]+B[2][1]*B[1][0]+B[2][2]*B[2][0];\n//\tB2[2][1] = B[2][0]*B[0][1]+B[2][1]*B[1][1]+B[2][2]*B[2][1];\n\tB2[2][2] = B[2][0]*B[0][2]+B[2][1]*B[1][2]+B[2][2]*B[2][2];\n\n\t// --- put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2, W4a, W4b, W4c, W44a, W44b, W44c;\n\tW1 = m_c1;\n\tW2 = m_c2;\n\n\tconst double eps = m_epsf*std::numeric_limits<double>::epsilon();\n\n\t// fiber a\n\tif (lat >= 1 + eps)\n\t{\n\t\tdouble lati = 1.0/lat;\n\n\t\tdouble Wl, Wll;\n\t\tWl  = m_beta[0]*m_ksi[0]*pow((lat - 1.0), m_beta[0]-1.0);\n\t\tWll = (m_beta[0]-1.0)*m_beta[0]*m_ksi[0]*pow((lat - 1.0), m_beta[0]-2.0);\n\t\tW4a  = 0.5*lati*Wl;\n\t\tW44a = 0.25*lati*lati*(Wll - lati*Wl);\n\t}\n\telse \n\t{\n\t\tW4a  = 0;\n\t\tW44a = 0;\n\t}\n\n\t// fiber b\n\tif (lbt >= 1 + eps)\n\t{\n\t\tdouble lbti = 1.0/lbt;\n\n\t\tdouble Wl, Wll;\n\t\tWl  = m_beta[1]*m_ksi[1]*pow((lbt - 1.0), m_beta[1]-1.0);\n\t\tWll = (m_beta[1]-1.0)*m_beta[1]*m_ksi[1]*pow((lbt - 1.0), m_beta[1]-2.0);\n\t\tW4b  = 0.5*lbti*Wl;\n\t\tW44b = 0.25*lbti*lbti*(Wll - lbti*Wl);\n\t}\n\telse \n\t{\n\t\tW4b  = 0;\n\t\tW44b = 0;\n\t}\n\n\t// fiber c\n\tif (lct >= 1 + eps)\n\t{\n\t\tdouble lcti = 1.0/lct;\n\n\t\tdouble Wl, Wll;\n\t\tWl  = m_beta[2]*m_ksi[2]*pow((lct - 1.0), m_beta[2]-1.0);\n\t\tWll = (m_beta[2]-1.0)*m_beta[2]*m_ksi[2]*pow((lct - 1.0), m_beta[2]-2.0);\n\t\tW4c  = 0.5*lcti*Wl;\n\t\tW44c = 0.25*lcti*lcti*(Wll - lcti*Wl);\n\t}\n\telse \n\t{\n\t\tW4c  = 0;\n\t\tW44c = 0;\n\t}\n\t// ------------------------------------\n\n\tdouble D[6][6] = {0};\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2 + W4a*I4a + W4b*I4b + W4c*I4c;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2 + W44a*I4a*I4a + W44b*I4b*I4b + W44c*I4c*I4c;\n\n\t// D[0][0] = c(0,0,0,0)\n\tD[0][0] =  -(4.0/3.0)*s[0][0] + (8.0/9.0)*Ji*WC;\n\tD[0][0] += 4.0*Ji*W44a*I4a*I4a*a.x*a.x*a.x*a.x;\n\tD[0][0] += 4.0*Ji*W44b*I4b*I4b*b.x*b.x*b.x*b.x;\n\tD[0][0] += 4.0*Ji*W44c*I4c*I4c*c.x*c.x*c.x*c.x;\n\tD[0][0] += (4.0/9.0)*Ji*CWWC;\n\tD[0][0] -= (8.0/3.0)*Ji*(W2*I1*B[0][0] - W2*B2[0][0] + W44a*I4a*I4a*a.x*a.x + W44b*I4b*I4b*b.x*b.x + W44c*I4c*I4c*c.x*c.x);\n\n\t// D[1][1] = c(1,1,1,1)\n\tD[1][1] = -(4.0/3.0)*s[1][1] + 8.0/9.0*Ji*WC;\n\tD[1][1] += 4.0*Ji*W44a*I4a*I4a*a.y*a.y*a.y*a.y;\n\tD[1][1] += 4.0*Ji*W44b*I4b*I4b*b.y*b.y*b.y*b.y;\n\tD[1][1] += 4.0*Ji*W44c*I4c*I4c*c.y*c.y*c.y*c.y;\n\tD[1][1] += (4.0/9.0)*Ji*CWWC;\n\tD[1][1] -= (8.0/3.0)*Ji*(W2*I1*B[1][1] - W2*B2[1][1] + W44a*I4a*I4a*a.y*a.y + W44b*I4b*I4b*b.y*b.y + W44c*I4c*I4c*c.y*c.y);\n\n\t// D[2][2] = c(2,2,2,2)\n\tD[2][2] = -(4.0/3.0)*s[2][2] + (8.0/9.0)*Ji*WC;\n\tD[2][2] += 4.0*Ji*W44a*I4a*I4a*a.z*a.z*a.z*a.z;\n\tD[2][2] += 4.0*Ji*W44b*I4b*I4b*b.z*b.z*b.z*b.z;\n\tD[2][2] += 4.0*Ji*W44c*I4c*I4c*c.z*c.z*c.z*c.z;\n\tD[2][2] += (4.0/9.0)*Ji*CWWC;\n\tD[2][2] -= (8.0/3.0)*Ji*(W2*I1*B[2][2] - W2*B2[2][2] + W44a*I4a*I4a*a.z*a.z + W44b*I4b*I4b*b.z*b.z + W44c*I4c*I4c*c.z*c.z);\n\n\n\n\t// D[0][1] = D[1][0] = c(0,0,1,1)\n\tD[0][1] = -(2.0/3.0)*(s[0][0] + s[1][1]) - (4.0/9.0)*Ji*WC;\n\tD[0][1] += 4.0*Ji*W44a*I4a*I4a*a.x*a.x*a.y*a.y;\n\tD[0][1] += 4.0*Ji*W44b*I4b*I4b*b.x*b.x*b.y*b.y;\n\tD[0][1] += 4.0*Ji*W44c*I4c*I4c*c.x*c.x*c.y*c.y;\n\tD[0][1] += 4.0*Ji*W2*(B[0][0]*B[1][1] - B[0][1]*B[0][1]);\n\tD[0][1] += (4.0/9.0)*Ji*CWWC;\n\tD[0][1] -= (4.0/3.0)*Ji*(W2*(I1*B[0][0] - B2[0][0]) + W44a*I4a*I4a*a.x*a.x + W44b*I4b*I4b*b.x*b.x + W44c*I4c*I4c*c.x*c.x);\n\tD[0][1] -= (4.0/3.0)*Ji*(W2*(I1*B[1][1] - B2[1][1]) + W44a*I4a*I4a*a.y*a.y + W44b*I4b*I4b*b.y*b.y + W44c*I4c*I4c*c.y*c.y);\n\n\t// D[1][2] = D[2][1] = c(1,1,2,2)\n\tD[1][2] = -(2.0/3.0)*(s[1][1] + s[2][2]) - (4.0/9.0)*Ji*WC;\n\tD[1][2] += 4.0*Ji*W44a*I4a*I4a*a.y*a.y*a.z*a.z;\n\tD[1][2] += 4.0*Ji*W44b*I4b*I4b*b.y*b.y*b.z*b.z;\n\tD[1][2] += 4.0*Ji*W44c*I4c*I4c*c.y*c.y*c.z*c.z;\n\tD[1][2] += 4.0*Ji*W2*(B[1][1]*B[2][2] - B[1][2]*B[1][2]);\n\tD[1][2] += (4.0/9.0)*Ji*CWWC;\n\tD[1][2] -= (4.0/3.0)*Ji*(W2*(I1*B[1][1] - B2[1][1]) + W44a*I4a*I4a*a.y*a.y + W44b*I4b*I4b*b.y*b.y + W44c*I4c*I4c*c.y*c.y);\n\tD[1][2] -= (4.0/3.0)*Ji*(W2*(I1*B[2][2] - B2[2][2]) + W44a*I4a*I4a*a.z*a.z + W44b*I4b*I4b*b.z*b.z + W44c*I4c*I4c*c.z*c.z);\n\n\t// D[0][2] = D[2][0] = c(0,0,2,2)\n\tD[0][2] = -(2.0/3.0)*(s[0][0] + s[2][2]) - (4.0/9.0)*Ji*WC;\n\tD[0][2] += 4.0*Ji*W44a*I4a*I4a*a.x*a.x*a.z*a.z;\n\tD[0][2] += 4.0*Ji*W44b*I4b*I4b*b.x*b.x*b.z*b.z;\n\tD[0][2] += 4.0*Ji*W44c*I4c*I4c*c.x*c.x*c.z*c.z;\n\tD[0][2] += 4.0*Ji*W2*(B[0][0]*B[2][2] - B[0][2]*B[0][2]);\n\tD[0][2] += (4.0/9.0)*Ji*CWWC;\n\tD[0][2] -= (4.0/3.0)*Ji*(W2*(I1*B[0][0] - B2[0][0]) + W44a*I4a*I4a*a.x*a.x + W44b*I4b*I4b*b.x*b.x + W44c*I4c*I4c*c.x*c.x);\n\tD[0][2] -= (4.0/3.0)*Ji*(W2*(I1*B[2][2] - B2[2][2]) + W44a*I4a*I4a*a.z*a.z + W44b*I4b*I4b*b.z*b.z + W44c*I4c*I4c*c.z*c.z);\n\n\n\n\t// D[3][3] = 0.5*(c(0,1,0,1) + c(0,1,1,0))\n\tD[3][3] = (2.0/3.0)*Ji*WC;\n\tD[3][3] += 4.0*Ji*W44a*I4a*I4a*a.x*a.y*a.x*a.y;\n\tD[3][3] += 4.0*Ji*W44b*I4b*I4b*b.x*b.y*b.x*b.y;\n\tD[3][3] += 4.0*Ji*W44c*I4c*I4c*c.x*c.y*c.x*c.y;\n\tD[3][3] += 2.0*Ji*W2*(B[0][1]*B[0][1] - B[0][0]*B[1][1]);\n\n\t// D[4][4] = 0.5*(c(1,2,1,2) + c(1,2,2,1))\n\tD[4][4] = (2.0/3.0)*Ji*WC;\n\tD[4][4] += 4.0*Ji*W44a*I4a*I4a*a.y*a.z*a.y*a.z;\n\tD[4][4] += 4.0*Ji*W44b*I4b*I4b*b.y*b.z*b.y*b.z;\n\tD[4][4] += 4.0*Ji*W44c*I4c*I4c*c.y*c.z*c.y*c.z;\n\tD[4][4] += 2.0*Ji*W2*(B[1][2]*B[1][2] - B[1][1]*B[2][2]);\n\n\t// D[5][5] = 0.5*(c(0,2,0,2) + c(0,2,2,0))\n\tD[5][5] = (2.0/3.0)*Ji*WC;\n\tD[5][5] += 4.0*Ji*W44a*I4a*I4a*a.x*a.z*a.x*a.z;\n\tD[5][5] += 4.0*Ji*W44b*I4b*I4b*b.x*b.z*b.x*b.z;\n\tD[5][5] += 4.0*Ji*W44c*I4c*I4c*c.x*c.z*c.x*c.z;\n\tD[5][5] += 2.0*Ji*W2*(B[0][2]*B[0][2] - B[0][0]*B[2][2]);\n\n\n\n\t// D[0][3] = 0.5*(c(0,0,0,1) + c(0,0,1,0))\n\tD[0][3] =  -(2.0/3.0)*s[0][1];\n\tD[0][3] += 4.0*Ji*W44a*I4a*I4a*a.x*a.x*a.x*a.y;\n\tD[0][3] += 4.0*Ji*W44b*I4b*I4b*b.x*b.x*b.x*b.y;\n\tD[0][3] += 4.0*Ji*W44c*I4c*I4c*c.x*c.x*c.x*c.y;\n\tD[0][3] -= (4.0/3.0)*Ji*(W2*(I1*B[0][1] - B2[0][1]) + W44a*I4a*I4a*a.x*a.y + W44b*I4b*I4b*b.x*b.y + W44c*I4c*I4c*c.x*c.y);\n\n\t// D[0][4] = 0.5*(c(0,0,1,2) + c(0,0,2,1))\n\tD[0][4] =  -(2.0/3.0)*s[1][2];\n\tD[0][4] += 4.0*Ji*W44a*I4a*I4a*a.x*a.x*a.y*a.z;\n\tD[0][4] += 4.0*Ji*W44b*I4b*I4b*b.x*b.x*b.y*b.z;\n\tD[0][4] += 4.0*Ji*W44c*I4c*I4c*c.x*c.x*c.y*c.z;\n\tD[0][4] += 4.0*Ji*W2*(B[0][0]*B[1][2] - B[0][1]*B[0][2]);\n\tD[0][4] -= (4.0/3.0)*Ji*(W2*(I1*B[1][2] - B2[1][2]) + W44a*I4a*I4a*a.y*a.z + W44b*I4b*I4b*b.y*b.z + W44c*I4c*I4c*c.y*c.z);\n\n\t// D[0][5] = 0.5*(c(0,0,0,2) + c(0,0,2,0))\n\tD[0][5] =  -(2.0/3.0)*s[0][2];\n\tD[0][5] += 4.0*Ji*W44a*I4a*I4a*a.x*a.x*a.x*a.z;\n\tD[0][5] += 4.0*Ji*W44b*I4b*I4b*b.x*b.x*b.x*b.z;\n\tD[0][5] += 4.0*Ji*W44c*I4c*I4c*c.x*c.x*c.x*c.z;\n\tD[0][5] -= (4.0/3.0)*Ji*(W2*(I1*B[0][2] - B2[0][2]) + W44a*I4a*I4a*a.x*a.z + W44b*I4b*I4b*b.x*b.z + W44c*I4c*I4c*c.x*c.z);\n\n\t// D[1][3] = 0.5*(c(1,1,0,1) + c(1,1,1,0))\n\tD[1][3] =  -(2.0/3.0)*s[0][1];\n\tD[1][3] += 4.0*Ji*W44a*I4a*I4a*a.y*a.y*a.x*a.y;\n\tD[1][3] += 4.0*Ji*W44b*I4b*I4b*b.y*b.y*b.x*b.y;\n\tD[1][3] += 4.0*Ji*W44c*I4c*I4c*c.y*c.y*c.x*c.y;\n\tD[1][3] -= (4.0/3.0)*Ji*(W2*(I1*B[0][1] - B2[0][1]) + W44a*I4a*I4a*a.x*a.y + W44b*I4b*I4b*b.x*b.y + W44c*I4c*I4c*c.x*c.y);\n\n\t// D[1][4] = 0.5*(c(1,1,1,2) + c(1,1,2,1))\n\tD[1][4] =  -(2.0/3.0)*s[1][2];\n\tD[1][4] += 4.0*Ji*W44a*I4a*I4a*a.y*a.y*a.y*a.z;\n\tD[1][4] += 4.0*Ji*W44b*I4b*I4b*b.y*b.y*b.y*b.z;\n\tD[1][4] += 4.0*Ji*W44c*I4c*I4c*c.y*c.y*c.y*c.z;\n\tD[1][4] -= (4.0/3.0)*Ji*(W2*(I1*B[1][2] - B2[1][2]) + W44a*I4a*I4a*a.y*a.z + W44b*I4b*I4b*b.y*b.z + W44c*I4c*I4c*c.y*c.z);\n\n\t// D[1][5] = 0.5*(c(1,1,0,2) + c(1,1,2,0))\n\tD[1][5] =  -(2.0/3.0)*s[0][2];\n\tD[1][5] += 4.0*Ji*W44a*I4a*I4a*a.y*a.y*a.x*a.z;\n\tD[1][5] += 4.0*Ji*W44b*I4b*I4b*b.y*b.y*b.x*b.z;\n\tD[1][5] += 4.0*Ji*W44c*I4c*I4c*c.y*c.y*c.x*c.z;\n\tD[1][5] += 4.0*Ji*W2*(B[1][1]*B[0][2] - B[0][1]*B[1][2]);\n\tD[1][5] -= (4.0/3.0)*Ji*(W2*(I1*B[0][2] - B2[0][2]) + W44a*I4a*I4a*a.x*a.z + W44b*I4b*I4b*b.x*b.z + W44c*I4c*I4c*c.x*c.z);\n\n\t// D[2][3] = 0.5*(c(2,2,0,1) + c(2,2,1,0))\n\tD[2][3] =  -(2.0/3.0)*s[0][1];\n\tD[2][3] += 4.0*Ji*W44a*I4a*I4a*a.z*a.z*a.x*a.y;\n\tD[2][3] += 4.0*Ji*W44b*I4b*I4b*b.z*b.z*b.x*b.y;\n\tD[2][3] += 4.0*Ji*W44c*I4c*I4c*c.z*c.z*c.x*c.y;\n\tD[2][3] += 4.0*Ji*W2*(B[2][2]*B[0][1] - B[0][2]*B[1][2]);\n\tD[2][3] -= (4.0/3.0)*Ji*(W2*(I1*B[0][1] - B2[0][1]) + W44a*I4a*I4a*a.x*a.y + W44b*I4b*I4b*b.x*b.y + W44c*I4c*I4c*c.x*c.y);\n\n\t// D[2][4] = 0.5*(c(2,2,1,2) + c(2,2,2,1))\n\tD[2][4] =  -(2.0/3.0)*s[1][2];\n\tD[2][4] += 4.0*Ji*W44a*I4a*I4a*a.z*a.z*a.y*a.z;\n\tD[2][4] += 4.0*Ji*W44b*I4b*I4b*b.z*b.z*b.y*b.z;\n\tD[2][4] += 4.0*Ji*W44c*I4c*I4c*c.z*c.z*c.y*c.z;\n\tD[2][4] -= (4.0/3.0)*Ji*(W2*(I1*B[1][2] - B2[1][2]) + W44a*I4a*I4a*a.y*a.z + W44b*I4b*I4b*b.y*b.z + W44c*I4c*I4c*c.y*c.z);\n\n\t// D[2][5] = 0.5*(c(2,2,0,2) + c(2,2,2,0))\n\tD[2][5] =  -(2.0/3.0)*s[0][2];\n\tD[2][5] += 4.0*Ji*W44a*I4a*I4a*a.z*a.z*a.x*a.z;\n\tD[2][5] += 4.0*Ji*W44b*I4b*I4b*b.z*b.z*b.x*b.z;\n\tD[2][5] += 4.0*Ji*W44c*I4c*I4c*c.z*c.z*c.x*c.z;\n\tD[2][5] -= (4.0/3.0)*Ji*(W2*(I1*B[0][2] - B2[0][2]) + W44a*I4a*I4a*a.x*a.z + W44b*I4b*I4b*b.x*b.z + W44c*I4c*I4c*c.x*c.z);\n\n\n\n\t// D[3][4] = 0.5*(c(0,1,1,2) + c(0,1,2,1))\n\tD[3][4] = 4.0*Ji*W44a*I4a*I4a*a.x*a.y*a.y*a.z;\n\tD[3][4] = 4.0*Ji*W44b*I4b*I4b*b.x*b.y*b.y*b.z;\n\tD[3][4] = 4.0*Ji*W44c*I4c*I4c*c.x*c.y*c.y*c.z;\n\tD[3][4] += 2.0*Ji*W2*(B[0][1]*B[1][2] - B[0][2]*B[1][1]);\n\n\t// D[3][5] = 0.5*(c(0,1,0,2) + c(0,1,2,0))\n\tD[3][5] = 4.0*Ji*W44a*I4a*I4a*a.x*a.y*a.x*a.z;\n\tD[3][5] = 4.0*Ji*W44b*I4b*I4b*b.x*b.y*b.x*b.z;\n\tD[3][5] = 4.0*Ji*W44c*I4c*I4c*c.x*c.y*c.x*c.z;\n\tD[3][5] += 2.0*Ji*W2*(B[0][1]*B[0][2] - B[0][0]*B[1][2]);\n\n\t// D[4][5] = 0.5*(c(1,2,0,2) + c(1,2,2,0))\n\tD[4][5] = 4.0*Ji*W44a*I4a*I4a*a.y*a.z*a.x*a.z;\n\tD[4][5] = 4.0*Ji*W44b*I4b*I4b*b.y*b.z*b.x*b.z;\n\tD[4][5] = 4.0*Ji*W44c*I4c*I4c*c.y*c.z*c.x*c.z;\n\tD[4][5] += 2.0*Ji*W2*(B[1][2]*B[0][2] - B[0][1]*B[2][2]);\n\n\n\n\t// set symmetric components\n\tD[1][0] = D[0][1]; D[2][0] = D[0][2]; D[3][0] = D[0][3]; D[4][0] = D[0][4]; D[5][0] = D[0][5];\n\tD[2][1] = D[1][2]; D[3][1] = D[1][3]; D[4][1] = D[1][4]; D[5][1] = D[1][5];\n\tD[3][2] = D[2][3]; D[4][2] = D[2][4]; D[5][2] = D[2][5];\n\tD[4][3] = D[3][4]; D[5][3] = D[3][5];\n\tD[5][4] = D[4][5];\n\n\treturn tens4ds(D);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the deviatoric strain energy density\ndouble FETCNonlinearOrthotropic::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n    \n\t// invariants of B\n\tdouble I1, I2;\n    \n\t// current local material axis\n\tvec3d a0, b0, c0, a, b, c;\n\tdouble la, lb, lc, lat, lbt, lct;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber directions\n\ta0.x = Q[0][0]; b0.x = Q[0][1]; c0.x = Q[0][2];\n\ta0.y = Q[1][0]; b0.y = Q[1][1]; c0.y = Q[1][2];\n\ta0.z = Q[2][0]; b0.z = Q[2][1]; c0.z = Q[2][2];\n    \n\t// calculate the current material axes lam*a = F*a0;\n\ta = F*a0;\n\tb = F*b0;\n\tc = F*c0;\n    \n\t// normalize material axis and store fiber stretch\n\tla  = a.unit();\n\tlat = la*Jm13; // i.e. lambda tilde\n    \n\tlb  = b.unit();\n\tlbt = lb*Jm13;\n    \n\tlc  = c.unit();\n\tlct = lc*Jm13;\n    \n\t// get deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tI1 = B.tr();\n\tI2 = 0.5*(I1*I1 - B2.tr());\n    \n    double sed = m_c1*(I1-3) + m_c2*(I2-3);\n    \n\t// fiber a\n\tif (lat > 1)\n\t\tsed += m_ksi[0]*pow((lat - 1.0), m_beta[0]);\n    \n\t// fiber b\n\tif (lbt > 1)\n\t\tsed += m_ksi[1]*pow((lbt - 1.0), m_beta[1]);\n    \n\t// fiber c\n\tif (lct > 1)\n\t\tsed += m_ksi[2]*pow((lct - 1.0), m_beta[2]);\n    \n\treturn sed;\n}\n"
  },
  {
    "path": "FEBioMech/FETCNonlinearOrthotropic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Tension-compression nonlinear orthrotropic\n\n//! This is Gerard's material model for articular cartilage.\n//! \\todo Make an orthotropic material base class where we \n//!       can derive this material from.\n\nclass FETCNonlinearOrthotropic : public FEUncoupledMaterial\n{\npublic:\n\tFETCNonlinearOrthotropic(FEModel* pfem);\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! Strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \npublic:\n\tdouble\tm_beta[3];\n\tdouble\tm_ksi[3];\n\n\tdouble m_c1;\t//!< Mooney-Rivlin coefficient c1\n\tdouble m_c2;\t//!< Mooney-Rivlin coefficient c2\n\n\tdouble m_epsf;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETendonMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETendonMaterial.h\"\n#include <FECore/FEConstValueVec3.h>\n\n#ifndef SQR\n\t#define SQR(x) ((x)*(x))\n#endif\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETendonMaterial, FEUncoupledMaterial)\n\tADD_PARAMETER(m_G1  , \"g1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_G2  , \"g2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_L1  , \"l1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_L2  , \"l2\");\n\tADD_PARAMETER(m_lam1, \"lam_max\");\n\n\tADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n\n//////////////////////////////////////////////////////////////////////\n// FETendonMaterial\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nFETendonMaterial::FETendonMaterial(FEModel* pfem) : FEUncoupledMaterial(pfem) \n{\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the deviatoric stress at a material point.\n//!\nmat3ds FETendonMaterial::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q * m_fiber->unitVector(mp);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble la  = a.unit();\n\tdouble lat = la*pow(J, -1.0/3.0); // i.e. lambda tilde\n\n\t// get deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// calculate B*a\n\tvec3d Ba = B*a;\n\n\t// invariants of B\n\tdouble I1, I2, I4, I5;\n\n\tI1 = B.tr();\n\tI2 = 0.5*(I1*I1 - B2.tr());\n\tI4 = lat*lat;\n\tI5 = I4*(a*Ba);\n\n\t// ----- calculate new invariants b1 and b2 of B ------\n\n\t// note that we need to be carefull about roundoff errors\n\t// since these functions may create numerical problems\n\n\tdouble g = I5/(I4*I4) - 1;\n\tdouble b1 = (g > 0 ? sqrt(g) : 0);\n\t\n\tdouble b2 = acosh(0.5*(I1*I4 - I5)/lat);\n\n\t// calculate omage (w)\n\tdouble w = 0.5*(I1*I4 - I5)/lat;\n\n\t// set beta and ksi to their limit values\n\tdouble beta = 1.0;\n\tdouble ksi = -1.0/3.0;\n\n\t// if w not equals unity, we can calculate beta and ksi\n\tif (w > 1.0001)\n\t{\n\t\tbeta = b2/sqrt(w*w-1);\n\t\tksi = (1.0/(w*w-1))*(1 - w*b2/sqrt(w*w-1));\n\t}\n\n\t// evaluate parameters\n\tdouble G1 = m_G1(mp);\n\tdouble G2 = m_G2(mp);\n\tdouble L1 = m_L1(mp);\n\tdouble L2 = m_L2(mp);\n\tdouble lam1 = m_lam1(mp);\n\n\t// ----- calculate strain derivatives -----\n\n\t// We assume that W(I1, I4, I5, alpha) = F1(B1(I4, I5)) + F2(B2(I1,I4,I5)) + F3(lam(I4), alpha)\n\tdouble W1, W2, W4, W5;\n\n\t// calculate derivatives for F1\n\tdouble F1D4 = -2*G1*(I5/(I4*I4*I4));\n\tdouble F1D5 = G1/(I4*I4);\n\n\t// calculate derivatives for F2\n\tdouble F2D1 =  G2*beta*lat;\n\tdouble F2D4 =  G2*beta*(I1*I4 + I5)*0.5*pow(I4, -1.5);\n\tdouble F2D5 = -G2*beta/lat;\n\n\t// calculate passive fiber force\n\tdouble Fp;\n\tif (lat <= 1)\n\t{\n\t\tFp = 0;\n\t}\n\telse if (lat < lam1)\n\t{\n\t\tFp = L1*(exp(L2*(lat - 1)) - 1);\n\t}\n\telse\n\t{\n\t\tdouble L3, L4;\n\n\t\tL3 = L1*L2*exp(L2*(lam1 - 1));\n\t\tL4 = L1*(exp(L2*(lam1 - 1)) - 1) - L3*lam1;\n\n\t\tFp = L3*lat + L4;\n\t}\n\n\t// calculate total fiber force\n\tdouble FfDl = Fp/lat;\n\tdouble FfD4  = 0.5*FfDl/lat;\n\n\t// add all derivatives\n\tW1 = F2D1;\n\tW2 = 0;\n\tW4 = F1D4 + F2D4 + FfD4;\n\tW5 = F1D5 + F2D5;\n\n\t// ----- calculate stress -----\n\n\t// dyadic of a\n\tmat3ds AxA = dyad(a);\n\n\t// symmetric dyad of a and Ba\n\tmat3ds ABA = dyads(a, Ba);\n\n\t// ----- calculate stress -----\n\n\t// calculate T \n\tmat3ds T = B*(W1 + W2*I1) - B2*W2 + AxA*(I4*W4) + ABA*(I4*W5);\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the spatial deviatoric tangent at a material point\n//!\ntens4ds FETendonMaterial::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q * m_fiber->unitVector(mp);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble la  = a.unit();\n\tdouble lat = la*pow(J, -1.0/3.0); // i.e. lambda tilde\n\n\t// get deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// calculate B*a\n\tvec3d Ba = B*a;\n\n\t// invariants of B\n\tdouble I1, I2, I4, I5;\n\n\tI1 = B.tr();\n\tI2 = 0.5*(I1*I1 - B2.tr());\n\tI4 = lat*lat;\n\tI5 = I4*(a*Ba);\n\n\t// calculate new invariants\n\tdouble g = I5/(I4*I4) - 1;\n\tdouble b1 = (g > 0 ? sqrt(g) : 0);\n\t\n\tdouble b2 = acosh(0.5*(I1*I4 - I5)/lat);\n\n\t// calculate omage (w)\n\tdouble w = 0.5*(I1*I4 - I5)/lat;\n\n\t// set beta and ksi to their limit values\n\tdouble beta = 1.0;\n\tdouble ksi = -1.0/3.0;\n\n\t// if w not equals unity, we can calculate beta and ksi\n\tif (w > 1.0001)\n\t{\n\t\tbeta = b2/sqrt(w*w-1);\n\t\tksi = (1.0/(w*w-1))*(1 - w*b2/sqrt(w*w-1));\n\t}\n\n\t// evaluate parameters\n\tdouble G1 = m_G1(mp);\n\tdouble G2 = m_G2(mp);\n\tdouble L1 = m_L1(mp);\n\tdouble L2 = m_L2(mp);\n\tdouble lam1 = m_lam1(mp);\n\n\t// --- strain energy derivatives ---\n\t// We assume that W(I1, I4, I5, alpha) = F1(B1(I4, I5)) + F2(B2(I1,I4,I5)) + F3(lam(I4), alpha)\n\tdouble W1, W2, W4, W5;\n\n\t// -- A. matrix contribution --\n\t// calculate derivatives for F1\n\tdouble F1D4 = -2*G1*(I5/(I4*I4*I4));\n\tdouble F1D5 = G1/(I4*I4);\n\n\tdouble F1D44 = 6*G1*(I5/(I4*I4*I4*I4));\n\tdouble F1D45 = -2*G1/(I4*I4*I4);\n\n\t// calculate derivatives for F2\n\tdouble F2D1 =  G2*beta*lat;\n\tdouble F2D4 =  G2*beta*(I1*I4 + I5)*0.5*pow(I4, -1.5);\n\tdouble F2D5 = -G2*beta/lat;\n\n\tdouble F2D11 = ksi*G2*I4*0.5;\n\tdouble F2D44 = 2.0*G2*ksi*pow(0.25*(I1*I4+I5)/pow(I4, 1.5), 2) - G2*beta*(0.25*(I1*I4 + 3*I5) / pow(I4, 2.5));\n\tdouble F2D55 = 0.5*G2*ksi/I4;\n\tdouble F2D14 = G2*beta*0.5/lat + G2*ksi*(I1*I4+I5)*0.25/I4;\n\tdouble F2D15 = -0.5*G2*ksi;\n\tdouble F2D45 = G2*beta*0.5*pow(I4, -1.5) - G2*ksi*0.25*(I1*I4+I5)/(I4*I4);\n\n\t// -- B. fiber contribution --\n\n\t// calculate passive fiber force\n\tdouble Fp, FpDl;\n\tif (lat <= 1)\n\t{\n\t\tFp = 0;\n\t\tFpDl = 0;\n\t}\n\telse if (lat < lam1)\n\t{\n\t\tFp = L1*(exp(L2*(lat - 1)) - 1);\n\t\tFpDl = L1*L2*exp(L2*(lat - 1));\n\t}\n\telse\n\t{\n\t\tdouble L3, L4;\n\n\t\tL3 = L1*L2*exp(L2*(lam1 - 1));\n\t\tL4 = L1*(exp(L2*(lam1 - 1)) - 1) - L3*lam1;\n\n\t\tFp = L3*lat + L4;\n\t\tFpDl = L3;\n\t}\n\n\t// calculate total fiber force\n\tdouble FfDl = Fp/lat;\n\tdouble FfD4  = 0.5*FfDl/lat;\n\n\tdouble FfDll = FpDl/lat - Fp/(lat*lat);\n\tdouble FfD44 = 0.25*(FfDll - FfDl / lat)/I4;\n\n\t// add all derivatives\n\tW1 = F2D1;\n\tW2 = 0;\n\tW4 = F1D4 + F2D4 + FfD4;\n\tW5 = F1D5 + F2D5;\n\n\t// calculate second derivatives\n\tdouble W11, W12, W22, W14, W24, W15, W25, W44, W45, W55;\n\n\tW11 = F2D11;\n\tW12 = 0;\n\tW22 = 0;\n\tW14 = F2D14;\n\tW24 = 0;\n\tW15 = F2D15;\n\tW25 = 0;\n\tW44 = F1D44 + F2D44 + FfD44;\n\tW45 = F1D45 + F2D45;\n\tW55 = F2D55;\n\n\t// calculate dWdC:C\n\tdouble WCC = W1*I1 + 2*W2*I2 + W4*I4 + 2*W5*I5;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CW2CCC = (W11*I1 + W12*I1*I1 + W2*I1 + 2*W12*I2 + 2*W22*I1*I2 + W14*I4 + W24*I1*I4 + 2*W15*I5 + 2*W25*I1*I5)*I1\n\t\t           -(W12*I1 + 2*W22*I2 + W2 + W24*I4 + 2*W25*I5)*(I1*I1 - 2*I2)\n\t\t\t\t   +(W14*I1 + 2*W24*I2 + W44*I4 + 2*W45*I5)*I4 + (W15*I1 + 2*W25*I2 + W45*I4 + 2*W55*I5)*2*I5\n\t\t\t\t   + 2*W5*I5;\n\n\t// second order identity tensor\n\tmat3dd ID(1);\n\n\t// 4th order identity tensors\n\ttens4ds IxI = dyad1s(ID);\n\ttens4ds I   = dyad4s(ID);\n\n\t// dyad of a\n\tmat3ds AxA = dyad(a);\n\n\t// --- calculate elasticity tensor ---\n\n\t// calculate push-forward of dI5/dC = I4*(a*Ba + Ba*a)\n\tmat3ds I5C = dyads(a, Ba)*I4;\n\n\t// calculate push-forward of d2I5/dCdC\n\ttens4ds I5CC = dyad4s(AxA, B)*I4;\n\n\t// calculate push forward of I\n\ttens4ds Ib = dyad4s(B);\n\n\t// calculate push forward of dW/dCdC:C\n\tmat3ds WCCC = \n\t\tB*(W11*I1 + W12*I1*I1 + W2*I1 + 2*W12*I2 + 2*W22*I1*I2 + W14*I4 + W24*I1*I4 + 2*W15*I5 + 2*W25*I1*I5) - \n\t\tB2*(W12*I1 + 2*W22*I2 + W2 + W24*I4 + 2*W25*I5) + \n\t\tAxA *((W14*I1 + 2*W24*I2 + W44*I4 + 2*W45*I5)*I4) + \n\t\tI5C*(W15*I1 + 2*W25*I2 + W45*I4 + 2*W55*I5 + W5);\n\n\t// calculate push-forward of dW2/dCdC\n\ttens4ds W2CC = \n\t\tdyad1s(B)*(W11 + 2.0*W12*I1 + W2 + W22*I1*I1) + \n\t\tdyad1s(B, B2)*(-(W12+W22*I1)) + dyad1s(B2)*W22 - Ib*W2 +\n\t\tdyad1s(B, AxA)*((W14 + W24*I1)*I4) +\n\t\tdyad1s(B, I5C)*(W15 + W25*I1) + \n\t\tdyad1s(B2, AxA)*(-W24*I4) + \n\t\tdyad1s(AxA)*(W44*I4*I4) + \n\t\tdyad1s(AxA, I5C)*(W45*I4) + \n\t\tdyad1s(I5C)*W55 + I5CC*W5;\n\n\t// let's put it all together\n\t// cw\n\ttens4ds cw =  IxI*((4.0/(9.0*J))*(CW2CCC)) + W2CC*(4/J) - dyad1s(WCCC, ID)*(4.0/(3.0*J));\n\n\t// deviatoric Cauchy stress\n\tmat3ds ABA = dyads(a, Ba);\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2 + AxA * (I4 * W4) + ABA * (I4 * W5);\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// elasticity tensor\n\treturn dyad1s(devs, ID)*(-2.0/3.0) + (I - IxI/3.0)*(4.0*WCC/(3.0*J)) + cw;\n}\n"
  },
  {
    "path": "FEBioMech/FETendonMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Tendon Material\n\n//! This material uses the constitutive model developed by Blemker et.al. to model tendons\nclass FETendonMaterial : public FEUncoupledMaterial\n{\npublic:\n\tFETendonMaterial(FEModel* pfem);\n\npublic:\n\t// transverse constants\n\tFEParamDouble\tm_G1;\t//!< along-fiber shear modulus\n\tFEParamDouble\tm_G2;\t//!< cross-fiber shear modulus\n\n\t// along fiber constants\n\tFEParamDouble\tm_L1;\t//!< tendon fiber constant L1\n\tFEParamDouble\tm_L2;\t//!< tendon fiber constant L2\n\n\tFEParamDouble\tm_lam1;\t//!< max exponential fiber stretch\n\n\tFEVec3dValuator*\tm_fiber;\n\t\npublic:\n\t//! calculate deviatoric stress at material point\n\tvirtual mat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\tvirtual tens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETiedContactSurface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedContactSurface.h\"\n#include \"FECore/FEShellDomain.h\"\n#include \"FECore/vector.h\"\n#include <FECore/FEMesh.h>\n\nFETiedContactSurface::Data::Data()\n{\n\tm_vgap = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n\tm_Lm = vec3d(0, 0, 0);\n\tm_Lp = vec3d(0, 0, 0);\n\tm_Tc = vec3d(0, 0, 0);\n\tm_off = 0.0;\n}\n\nvoid FETiedContactSurface::Data::Init()\n{\n\tFEContactMaterialPoint::Init();\n\tm_vgap = vec3d(0, 0, 0);\n\tm_rs = vec2d(0, 0);\n\tm_Lm = vec3d(0, 0, 0);\n\tm_Lp = vec3d(0, 0, 0);\n\tm_Tc = vec3d(0, 0, 0);\n\tm_off = 0.0;\n}\n\nvoid FETiedContactSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_vgap;\n\tar & m_rs;\n\tar & m_Lm & m_Lp;\n\tar & m_Tc;\n\tar & m_off;\n}\n\n//-----------------------------------------------------------------------------\nFETiedContactSurface::FETiedContactSurface(FEModel* pfem) : FEContactSurface(pfem)\n{ \n\tm_boffset = false; \n}\n\n//-----------------------------------------------------------------------------\n//! Creates a surface for use with a sliding interface. All surface data\n//! structures are allocated.\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FETiedContactSurface::Init()\n{\n\t// always intialize base class first!\n\tif (FEContactSurface::Init() == false) return false;\n\n\t// get the number of nodes\n\tint nn = Nodes();\n\n\t// allocate other surface data\n\tm_data.resize(nn);\n\n\t// we calculate the gap offset values\n\t// This value is used to take the shell thickness into account\n\tif (m_boffset)\n\t{\n\t\tFEMesh& m = *m_pMesh;\n\t\tvector<double> tag(m.Nodes());\n\t\tzero(tag);\n\t\tfor (int nd = 0; nd<m.Domains(); ++nd)\n\t\t{\n\t\t\tFEShellDomain* psd = dynamic_cast<FEShellDomain*>(&m.Domain(nd));\n\t\t\tif (psd)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i<psd->Elements(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEShellElement& el = psd->Element(i);\n\t\t\t\t\tint n = el.Nodes();\n\t\t\t\t\tfor (int j = 0; j<n; ++j) tag[el.m_node[j]] = 0.5*el.m_h0[j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (int i = 0; i<nn; ++i) m_data[i].m_off = tag[NodeIndex(i)];\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedContactSurface::Serialize(DumpStream &ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tar & m_data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedContactSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ne = el.Nodes();\n    pt = vec3d(0,0,0);\n    for (int k=0; k<ne; ++k) pt += m_data[el.m_lnode[k]].m_Tc;\n    pt /= ne;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedContactSurface::GetNodalContactPressure(int nface, double* pn)\n{\n\tFESurfaceElement& f = Element(nface);\n\tint ne = f.Nodes();\n\tfor (int j= 0; j< ne; ++j) pn[j] = m_data[f.m_lnode[j]].m_Tc.norm();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedContactSurface::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& f = Element(nface);\n\tint ne = f.Nodes();\n\tfor (int j= 0; j< ne; ++j) \n\t{\n\t\ttn[j] = m_data[f.m_lnode[j]].m_Tc;\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FETiedContactSurface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! This class describes a contact surface used for tied contact.\n\n//!\tthis class is used in contact analyses to describe a contacting\n//! surface in a tied contact interface.\n\nclass FETiedContactSurface : public FEContactSurface\n{\npublic:\n\tclass Data : public FEContactMaterialPoint\n\t{\n\tpublic:\n\t\tData();\n\n\t\tvoid Init() override;\n\n\t\tvoid Serialize(DumpStream& ar) override;\n\n\tpublic:\n\t\tvec3d\t\t\t\tm_vgap;\t//!< gap function at nodes\n\t\tvec2d\t\t\t\tm_rs;\t//!< natural coordinates of projection on secondary surface element\n\t\tvec3d\t\t\t\tm_Lm, m_Lp;\t//!< Lagrange multipliers (current, previous time step)\n\t\tvec3d\t\t\t\tm_Tc;\t//!< contact forces\n\t\tdouble\t\t\t\tm_off;\t//!< offset values (used for shells)\n\t};\n\npublic:\n\t//! constructor\n\tFETiedContactSurface(FEModel* pfem);\n\n\t//! Initializes data structures\n\tbool Init();\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar);\n\n\t//! offset shell surfaces (must be called before Init())\n\tvoid SetShellOffset(bool b) { m_boffset = b; }\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt);\n\tvoid GetNodalContactPressure(int nface, double* pn);\n\tvoid GetNodalContactTraction(int nface, vec3d* tn);\n\npublic:\n\tvector<Data>\tm_data;\t//!< integration point data\n\nprotected:\n\tbool\tm_boffset;\t\t//!< offset shells\n};\n"
  },
  {
    "path": "FEBioMech/FETiedElasticInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedElasticInterface.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FETiedElasticInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"             )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n\tADD_PARAMETER(m_gtol     , \"gaptol\"             );\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n\tADD_PARAMETER(m_srad     , \"search_radius\"      );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\n\tADD_PARAMETER(m_bflips   , \"flip_primary\"       );\n\tADD_PARAMETER(m_bflipm   , \"flip_secondary\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETiedElasticSurface::Data::Data()\n{\n    m_Gap = vec3d(0,0,0);\n    m_dg = vec3d(0,0,0);\n    m_nu = vec3d(0,0,0);\n    m_rs = vec2d(0,0);\n    m_Lmd = vec3d(0,0,0);\n    m_tr = vec3d(0,0,0);\n    m_epsn = 1.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticSurface::Data::Serialize(DumpStream& ar)\n{\n\tFEContactMaterialPoint::Serialize(ar);\n\tar & m_Gap;\n\tar & m_dg;\n\tar & m_nu;\n\tar & m_rs;\n\tar & m_Lmd;\n\tar & m_epsn;\n\tar & m_tr;\n}\n\nvoid FETiedElasticSurface::Data::Init()\n{\n    FEContactMaterialPoint::Init();\n    m_Gap = vec3d(0, 0, 0);\n    m_dg = vec3d(0, 0, 0);\n    m_nu = vec3d(0, 0, 0);\n    m_rs = vec2d(0, 0);\n    m_Lmd = vec3d(0, 0, 0);\n    m_tr = vec3d(0, 0, 0);\n    m_epsn = 1.0;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedElasticSurface\n//-----------------------------------------------------------------------------\n\nFETiedElasticSurface::FETiedElasticSurface(FEModel* pfem) : FEContactSurface(pfem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedElasticSurface::Init()\n{\n    // initialize surface data first\n    if (FEContactSurface::Init() == false) return false;\n    \n    // allocate node normals\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    \n    // initialize nodal force vector\n    m_Fn.assign(Nodes(), vec3d(0, 0, 0));\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the\n//! element normals at the node\n\nvoid FETiedElasticSurface::UpdateNodeNormals()\n{\n    int N = Nodes(), i, j, ne, jp1, jm1;\n    vec3d y[FEElement::MAX_NODES], n;\n    \n    // zero nodal normals\n    zero(m_nn);\n    \n    // loop over all elements\n    for (i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        ne = el.Nodes();\n        \n        // get the nodal coordinates\n        for (j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n        \n        // calculate the normals\n        for (j=0; j<ne; ++j)\n        {\n            jp1 = (j+1)%ne;\n            jm1 = (j+ne-1)%ne;\n            n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n            m_nn[el.m_lnode[j]] += n;\n        }\n    }\n    \n    // normalize all vectors\n    for (i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FETiedElasticSurface::CreateMaterialPoint()\n{\n\treturn new FETiedElasticSurface::Data;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticSurface::Serialize(DumpStream& ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tar & m_nn & m_Fn;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticSurface::GetVectorGap(int nface, vec3d& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_dg;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n\t\tData& data = static_cast<Data&>(*el.GetMaterialPoint(k));\n\t\tpt += data.m_tr;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETiedElasticSurface::GetContactForce()\n{\n    // initialize contact force\n    vec3d f(0, 0, 0);\n\n    // loop over all elements of the primary surface\n    for (int i = 0; i < m_Fn.size(); ++i) f += m_Fn[i];\n\n    return f;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate net contact area\ndouble FETiedElasticSurface::GetContactArea()\n{\n    // initialize contact area\n    double a = 0;\n\n    // loop over all elements of the primary surface\n    for (int n = 0; n < Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        int nint = el.GaussPoints();\n\n        // evaluate the contact force for that element\n        for (int i = 0; i < nint; ++i)\n        {\n            // get data for this integration point\n            Data& data = static_cast<Data&>(*el.GetMaterialPoint(i));\n            double T = data.m_tr.norm2();\n            if (data.m_pme && (T != 0.0))\n            {\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n\n                // normal (magnitude = area)\n                vec3d n = g[0] ^ g[1];\n\n                // gauss weight\n                double w = el.GaussWeights()[i];\n\n                // contact force\n                a += n.norm() * w;\n            }\n        }\n    }\n\n    return a;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedElasticInterface\n//-----------------------------------------------------------------------------\n\nFETiedElasticInterface::FETiedElasticInterface(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n    static int count = 1;\n    SetID(count++);\n    \n    // initial values\n    m_knmult = 1;\n    m_atol = 0.1;\n    m_epsn = 1;\n    m_btwo_pass = false;\n    m_stol = 0.01;\n    m_bsymm = true;\n    m_srad = 1.0;\n    m_gtol = -1;    // we use augmentation tolerance by default\n    m_bautopen = false;\n    m_bupdtpen = false;\n    \n    m_naugmin = 0;\n    m_naugmax = 10;\n\n\tm_bflips = false;\n\tm_bflipm = false;\n\n    // set parents\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n\n    m_ss.SetSibling(&m_ms);\n    m_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFETiedElasticInterface::~FETiedElasticInterface()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedElasticInterface::Init()\n{\n    // initialize surface data\n    if (m_ss.Init() == false) return false;\n    if (m_ms.Init() == false) return false;\n\n\t// Flip secondary and primary surfaces, if requested.\n\t// Note that we turn off those flags because otherwise we keep flipping, each time we get here (e.g. in optimization)\n\t// TODO: Of course, we shouldn't get here more than once. I think we also get through the FEModel::Reset, so I'll have\n\t//       look into that. \n\tif (m_bflips) { m_ss.Invert(); m_bflips = false; }\n\tif (m_bflipm) { m_ms.Invert(); m_bflipm = false; }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FETiedElasticInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n    FEMesh& mesh = GetMesh();\n    \n    // get the DOFS\n    const int dof_X = GetDOFIndex(\"x\");\n    const int dof_Y = GetDOFIndex(\"y\");\n    const int dof_Z = GetDOFIndex(\"z\");\n    const int dof_RU = GetDOFIndex(\"Ru\");\n    const int dof_RV = GetDOFIndex(\"Rv\");\n    const int dof_RW = GetDOFIndex(\"Rw\");\n    \n    const int ndpn = 6;\n    vector<int> lm(ndpn*FEElement::MAX_NODES*2);\n    \n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FETiedElasticSurface& ss = (np == 0? m_ss : m_ms);\n        \n        int ni = 0, k, l;\n        for (int j=0; j<ss.Elements(); ++j)\n        {\n            FESurfaceElement& se = ss.Element(j);\n            int nint = se.GaussPoints();\n            int* sn = &se.m_node[0];\n            for (k=0; k<nint; ++k, ++ni)\n            {\n\t\t\t\tFETiedElasticSurface::Data& pt = static_cast<FETiedElasticSurface::Data&>(*se.GetMaterialPoint(k));\n                FESurfaceElement* pe = pt.m_pme;\n                if (pe != 0)\n                {\n                    FESurfaceElement& me = *pe;\n                    int* mn = &me.m_node[0];\n                    \n                    assign(lm, -1);\n                    \n                    int nseln = se.Nodes();\n                    int nmeln = me.Nodes();\n                    \n                    for (l=0; l<nseln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(sn[l]).m_ID;\n                        lm[ndpn*l  ] = id[dof_X];\n                        lm[ndpn*l+1] = id[dof_Y];\n                        lm[ndpn*l+2] = id[dof_Z];\n                        lm[ndpn*l+3] = id[dof_RU];\n                        lm[ndpn*l+4] = id[dof_RV];\n                        lm[ndpn*l+5] = id[dof_RW];\n                    }\n                    \n                    for (l=0; l<nmeln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(mn[l]).m_ID;\n                        lm[ndpn*(l+nseln)  ] = id[dof_X];\n                        lm[ndpn*(l+nseln)+1] = id[dof_Y];\n                        lm[ndpn*(l+nseln)+2] = id[dof_Z];\n                        lm[ndpn*(l+nseln)+3] = id[dof_RU];\n                        lm[ndpn*(l+nseln)+4] = id[dof_RV];\n                        lm[ndpn*(l+nseln)+5] = id[dof_RW];\n                    }\n                    \n                    K.build_add(lm);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticInterface::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        if (m_btwo_pass) CalcAutoPenalty(m_ms);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticInterface::Activate()\n{\n    // don't forget to call the base class\n    FEContactInterface::Activate();\n    \n    UpdateAutoPenalty();\n    \n    // project the surfaces onto each other\n    // this will evaluate the gap functions in the reference configuration\n    InitialProjection(m_ss, m_ms);\n    if (m_btwo_pass) InitialProjection(m_ms, m_ss);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticInterface::CalcAutoPenalty(FETiedElasticSurface& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n\t\t\tFETiedElasticSurface::Data& pt = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Perform initial projection between tied surfaces in reference configuration\nvoid FETiedElasticInterface::InitialProjection(FETiedElasticSurface& ss, FETiedElasticSurface& ms)\n{\n    FESurfaceElement* pme;\n    vec3d r, nu;\n    double rs[2];\n    \n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n\n\t// let's count the number of contact pairs we find.\n\tint contacts = 0;\n    \n    // loop over all integration points\n    int n = 0;\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        \n        int nint = el.GaussPoints();\n        \n        for (int j=0; j<nint; ++j, ++n)\n        {\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // calculate the normal at this integration point\n            nu = ss.SurfaceNormal(el, j);\n            \n            // find the intersection point with the secondary surface\n            pme = np.Project2(r, nu, rs);\n            \n\t\t\tFETiedElasticSurface::Data& pt = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_pme = pme;\n            pt.m_rs[0] = rs[0];\n            pt.m_rs[1] = rs[1];\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                pt.m_Gap = q - r;\n\n\t\t\t\tcontacts++;\n            }\n            else\n            {\n                // the node is not in contact\n                pt.m_Gap = vec3d(0,0,0);\n            }\n        }\n    }\n\n\t// if we found no contact pairs, let's report this since this is probably not the user's intention\n\tif (contacts == 0)\n\t{\n\t\tstd::string name = GetName();\n\t\tfeLogWarning(\"No contact pairs found for tied interface \\\"%s\\\".\\nThis contact interface may not have any effect.\", name.c_str());\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate gap functions for position and fluid pressure\nvoid FETiedElasticInterface::ProjectSurface(FETiedElasticSurface& ss, FETiedElasticSurface& ms)\n{\n    FESurfaceElement* pme;\n    vec3d r;\n    \n    // loop over all integration points\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        \n        int nint = el.GaussPoints();\n        \n        for (int j=0; j<nint; ++j)\n        {\n\t\t\tFETiedElasticSurface::Data& pt = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // calculate the normal at this integration point\n            pt.m_nu = ss.SurfaceNormal(el, j);\n            \n            // if this node is tied, evaluate gap functions\n            pme = pt.m_pme;\n            if (pme)\n            {\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, pt.m_rs[0], pt.m_rs[1]);\n                \n                // calculate the gap function\n                vec3d g = q - r;\n                pt.m_dg = g - pt.m_Gap;\n\t\t\t\tpt.m_gap = pt.m_dg.norm();                \n            }\n            else\n            {\n                // the node is not tied\n                pt.m_dg = vec3d(0,0,0);\n\t\t\t\tpt.m_gap = 0.0;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FETiedElasticInterface::Update()\n{\n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    ProjectSurface(m_ss, m_ms);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss);\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    int i, j, k;\n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    double N[8*MN];\n\n    // zero nodal forces\n    m_ss.m_Fn.assign(m_ss.Nodes(), vec3d(0, 0, 0));\n    m_ms.m_Fn.assign(m_ms.Nodes(), vec3d(0, 0, 0));\n\n    // Update auto-penalty if requested\n    if (m_bupdtpen && (GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter == 0))\n        UpdateAutoPenalty();\n    \n    // loop over the nr of passes\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        // get primary and secondary surface\n        FETiedElasticSurface& ss = (np == 0? m_ss : m_ms);\n        FETiedElasticSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all primary elements\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get the surface element\n            FESurfaceElement& se = ss.Element(i);\n            \n            // get the nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // copy the LM vector; we'll need it later\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n            }\n            \n            // loop over all integration points\n            // note that we are integrating over the current surface\n            for (j=0; j<nint; ++j)\n            {\n\t\t\t\tFETiedElasticSurface::Data& pt = static_cast<FETiedElasticSurface::Data&>(*se.GetMaterialPoint(j));\n\n                // get the secondary element\n                FESurfaceElement* pme = pt.m_pme;\n                if (pme)\n                {\n                    // get the secondary element\n                    FESurfaceElement& me = *pme;\n                    \n                    // get the nr of secondary element nodes\n                    int nmeln = me.Nodes();\n                    \n                    // copy LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    // calculate degrees of freedom\n                    int ndof = 3*(nseln + nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    for (k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[3*k  ];\n                        LM[3*k+1] = sLM[3*k+1];\n                        LM[3*k+2] = sLM[3*k+2];\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[3*k  ];\n                        LM[3*(k+nseln)+1] = mLM[3*k+1];\n                        LM[3*(k+nseln)+2] = mLM[3*k+2];\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // get primary element shape functions\n                    Hs = se.H(j);\n                    \n                    // get secondary element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // gap function\n                    vec3d dg = pt.m_dg;\n                    \n                    // lagrange multiplier\n                    vec3d Lm = pt.m_Lmd;\n                    \n                    // penalty\n                    double eps = m_epsn*pt.m_epsn;\n                    \n                    // contact traction\n                    vec3d t = Lm + dg*eps;\n                    pt.m_tr = t;\n                    \n                    // calculate the force vector\n                    fe.resize(ndof);\n                    zero(fe);\n                    \n                    for (k=0; k<nseln; ++k)\n                    {\n                        N[3*k  ] = Hs[k]*t.x;\n                        N[3*k+1] = Hs[k]*t.y;\n                        N[3*k+2] = Hs[k]*t.z;\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        N[3*(k+nseln)  ] = -Hm[k]*t.x;\n                        N[3*(k+nseln)+1] = -Hm[k]*t.y;\n                        N[3*(k+nseln)+2] = -Hm[k]*t.z;\n                    }\n                    \n                    for (k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n\n                    for (int k = 0; k < nseln; ++k) ss.m_Fn[se.m_lnode[k]] += vec3d(fe[3 * k], fe[3 * k + 1], fe[3 * k + 2]);\n                    for (int k = 0; k < nmeln; ++k) ms.m_Fn[me.m_lnode[k]] += vec3d(fe[3 * nseln + 3 * k], fe[3 * nseln + 3 * k + 1], fe[3 * nseln + 3 * k + 2]);\n\n                    // assemble the global residual\n                    R.Assemble(en, LM, fe);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    int i, j, k, l;\n    vector<int> sLM, mLM, LM, en;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    FEElementMatrix ke;\n    \n    // see how many reformations we've had to do so far\n    int nref = GetSolver()->m_nref;\n    \n    // set higher order stiffness mutliplier\n    // NOTE: this algrotihm doesn't really need this\n    // but I've added this functionality to compare with the other contact\n    // algorithms and to see the effect of the different stiffness contributions\n    double knmult = m_knmult;\n    if (m_knmult < 0)\n    {\n        int ni = int(-m_knmult);\n        if (nref >= ni)\n        {\n            knmult = 1;\n            feLog(\"Higher order stiffness terms included.\\n\");\n        }\n        else knmult = 0;\n    }\n    \n    // do single- or two-pass\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np < npass; ++np)\n    {\n        // get the primary and secondary surface\n        FETiedElasticSurface& ss = (np == 0? m_ss : m_ms);\n        FETiedElasticSurface& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all primary elements\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get ths primary element\n            FESurfaceElement& se = ss.Element(i);\n            \n            // get nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // copy the LM vector\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n                \n            }\n            \n            // loop over all integration points\n            for (j=0; j<nint; ++j)\n            {\n\t\t\t\tFETiedElasticSurface::Data& pt = static_cast<FETiedElasticSurface::Data&>(*se.GetMaterialPoint(j));\n\n                // get the secondary element\n                FESurfaceElement* pme = pt.m_pme;\n                if (pme)\n                {\n                    FESurfaceElement& me = *pme;\n                    \n                    // get the nr of secondary nodes\n                    int nmeln = me.Nodes();\n                    \n                    // copy the LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    int ndpn;    // number of dofs per node\n                    int ndof;    // number of dofs in stiffness matrix\n                    \n                    // calculate degrees of freedom for elastic-on-elastic contact\n                    ndpn = 3;\n                    ndof = ndpn*(nseln + nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    \n                    for (k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[3*k  ];\n                        LM[3*k+1] = sLM[3*k+1];\n                        LM[3*k+2] = sLM[3*k+2];\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[3*k  ];\n                        LM[3*(k+nseln)+1] = mLM[3*k+1];\n                        LM[3*(k+nseln)+2] = mLM[3*k+2];\n                    }\n\n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // primary shape functions\n                    Hs = se.H(j);\n                    \n                    // secondary shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // get primary normal vector\n                    vec3d nu = pt.m_nu;\n                    \n                    // gap function\n                    vec3d dg = pt.m_dg;\n                    \n                    // lagrange multiplier\n                    vec3d Lm = pt.m_Lmd;\n                    \n                    // penalty\n                    double eps = m_epsn*pt.m_epsn;\n                    \n                    // contact traction\n                    vec3d t = Lm + dg*eps;\n                    \n                    // create the stiffness matrix\n                    ke.resize(ndof, ndof); ke.zero();\n                    \n                    // --- S O L I D - S O L I D   C O N T A C T ---\n                    \n                    // a. I-term\n                    //------------------------------------\n                    \n                    for (k=0; k<nseln; ++k) {\n                        for (l=0; l<nseln; ++l)\n                        {\n                            ke[ndpn*k    ][ndpn*l    ] += eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n                            ke[ndpn*k + 1][ndpn*l + 1] += eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n                            ke[ndpn*k + 2][ndpn*l + 2] += eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n                        }\n                        for (l=0; l<nmeln; ++l)\n                        {\n                            ke[ndpn*k    ][ndpn*(nseln+l)    ] += -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n                            ke[ndpn*k + 1][ndpn*(nseln+l) + 1] += -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n                            ke[ndpn*k + 2][ndpn*(nseln+l) + 2] += -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n                        }\n                    }\n                    \n                    for (k=0; k<nmeln; ++k) {\n                        for (l=0; l<nseln; ++l)\n                        {\n                            ke[ndpn*(nseln+k)    ][ndpn*l    ] += -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n                            ke[ndpn*(nseln+k) + 1][ndpn*l + 1] += -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n                            ke[ndpn*(nseln+k) + 2][ndpn*l + 2] += -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n                        }\n                        for (l=0; l<nmeln; ++l)\n                        {\n                            ke[ndpn*(nseln+k)    ][ndpn*(nseln+l)    ] += eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n                            ke[ndpn*(nseln+k) + 1][ndpn*(nseln+l) + 1] += eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n                            ke[ndpn*(nseln+k) + 2][ndpn*(nseln+l) + 2] += eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n                        }\n                    }\n                    \n                    // b. A-term\n                    //-------------------------------------\n                    \n                    double* Gr = se.Gr(j);\n                    double* Gs = se.Gs(j);\n                    vec3d gs[2];\n                    ss.CoBaseVectors(se, j, gs);\n                    \n                    vec3d as[FEElement::MAX_NODES];\n                    mat3d As[FEElement::MAX_NODES];\n                    for (l=0; l<nseln; ++l) {\n                        as[l] = nu ^ (gs[1]*Gr[l] - gs[0]*Gs[l]);\n                        As[l] = t & as[l];\n                    }\n                    \n                    if (!m_bsymm)\n                    {\n                        // non-symmetric\n                        for (k=0; k<nseln; ++k) {\n                            for (l=0; l<nseln; ++l)\n                            {\n                                ke[ndpn*k    ][ndpn*l    ] += Hs[k]*As[l](0,0)*w[j];\n                                ke[ndpn*k    ][ndpn*l + 1] += Hs[k]*As[l](0,1)*w[j];\n                                ke[ndpn*k    ][ndpn*l + 2] += Hs[k]*As[l](0,2)*w[j];\n                                \n                                ke[ndpn*k + 1][ndpn*l    ] += Hs[k]*As[l](1,0)*w[j];\n                                ke[ndpn*k + 1][ndpn*l + 1] += Hs[k]*As[l](1,1)*w[j];\n                                ke[ndpn*k + 1][ndpn*l + 2] += Hs[k]*As[l](1,2)*w[j];\n                                \n                                ke[ndpn*k + 2][ndpn*l    ] += Hs[k]*As[l](2,0)*w[j];\n                                ke[ndpn*k + 2][ndpn*l + 1] += Hs[k]*As[l](2,1)*w[j];\n                                ke[ndpn*k + 2][ndpn*l + 2] += Hs[k]*As[l](2,2)*w[j];\n                            }\n                        }\n                        \n                        for (k=0; k<nmeln; ++k) {\n                            for (l=0; l<nseln; ++l)\n                            {\n                                ke[ndpn*(nseln+k)    ][ndpn*l    ] += -Hm[k]*As[l](0,0)*w[j];\n                                ke[ndpn*(nseln+k)    ][ndpn*l + 1] += -Hm[k]*As[l](0,1)*w[j];\n                                ke[ndpn*(nseln+k)    ][ndpn*l + 2] += -Hm[k]*As[l](0,2)*w[j];\n                                \n                                ke[ndpn*(nseln+k) + 1][ndpn*l    ] += -Hm[k]*As[l](1,0)*w[j];\n                                ke[ndpn*(nseln+k) + 1][ndpn*l + 1] += -Hm[k]*As[l](1,1)*w[j];\n                                ke[ndpn*(nseln+k) + 1][ndpn*l + 2] += -Hm[k]*As[l](1,2)*w[j];\n                                \n                                ke[ndpn*(nseln+k) + 2][ndpn*l    ] += -Hm[k]*As[l](2,0)*w[j];\n                                ke[ndpn*(nseln+k) + 2][ndpn*l + 1] += -Hm[k]*As[l](2,1)*w[j];\n                                ke[ndpn*(nseln+k) + 2][ndpn*l + 2] += -Hm[k]*As[l](2,2)*w[j];\n                            }\n                        }\n                        \n                    }\n                    else\n                    {\n                        // symmetric\n                        for (k=0; k<nseln; ++k) {\n                            for (l=0; l<nseln; ++l)\n                            {\n                                ke[ndpn*k    ][ndpn*l    ] += 0.5*(Hs[k]*As[l](0,0)+Hs[l]*As[k](0,0))*w[j];\n                                ke[ndpn*k    ][ndpn*l + 1] += 0.5*(Hs[k]*As[l](0,1)+Hs[l]*As[k](1,0))*w[j];\n                                ke[ndpn*k    ][ndpn*l + 2] += 0.5*(Hs[k]*As[l](0,2)+Hs[l]*As[k](2,0))*w[j];\n                                \n                                ke[ndpn*k + 1][ndpn*l    ] += 0.5*(Hs[k]*As[l](1,0)+Hs[l]*As[k](0,1))*w[j];\n                                ke[ndpn*k + 1][ndpn*l + 1] += 0.5*(Hs[k]*As[l](1,1)+Hs[l]*As[k](1,1))*w[j];\n                                ke[ndpn*k + 1][ndpn*l + 2] += 0.5*(Hs[k]*As[l](1,2)+Hs[l]*As[k](2,1))*w[j];\n                                \n                                ke[ndpn*k + 2][ndpn*l    ] += 0.5*(Hs[k]*As[l](2,0)+Hs[l]*As[k](0,2))*w[j];\n                                ke[ndpn*k + 2][ndpn*l + 1] += 0.5*(Hs[k]*As[l](2,1)+Hs[l]*As[k](1,2))*w[j];\n                                ke[ndpn*k + 2][ndpn*l + 2] += 0.5*(Hs[k]*As[l](2,2)+Hs[l]*As[k](2,2))*w[j];\n                            }\n                        }\n                        \n                        for (k=0; k<nmeln; ++k) {\n                            for (l=0; l<nseln; ++l)\n                            {\n                                ke[ndpn*(nseln+k)    ][ndpn*l    ] += -0.5*Hm[k]*As[l](0,0)*w[j];\n                                ke[ndpn*(nseln+k)    ][ndpn*l + 1] += -0.5*Hm[k]*As[l](0,1)*w[j];\n                                ke[ndpn*(nseln+k)    ][ndpn*l + 2] += -0.5*Hm[k]*As[l](0,2)*w[j];\n                                \n                                ke[ndpn*(nseln+k) + 1][ndpn*l    ] += -0.5*Hm[k]*As[l](1,0)*w[j];\n                                ke[ndpn*(nseln+k) + 1][ndpn*l + 1] += -0.5*Hm[k]*As[l](1,1)*w[j];\n                                ke[ndpn*(nseln+k) + 1][ndpn*l + 2] += -0.5*Hm[k]*As[l](1,2)*w[j];\n                                \n                                ke[ndpn*(nseln+k) + 2][ndpn*l    ] += -0.5*Hm[k]*As[l](2,0)*w[j];\n                                ke[ndpn*(nseln+k) + 2][ndpn*l + 1] += -0.5*Hm[k]*As[l](2,1)*w[j];\n                                ke[ndpn*(nseln+k) + 2][ndpn*l + 2] += -0.5*Hm[k]*As[l](2,2)*w[j];\n                            }\n                        }\n                        \n                        for (k=0; k<nseln; ++k) {\n                            for (l=0; l<nmeln; ++l)\n                            {\n                                ke[ndpn*k    ][ndpn*(nseln+l)    ] += -0.5*Hm[l]*As[k](0,0)*w[j];\n                                ke[ndpn*k    ][ndpn*(nseln+l) + 1] += -0.5*Hm[l]*As[k](1,0)*w[j];\n                                ke[ndpn*k    ][ndpn*(nseln+l) + 2] += -0.5*Hm[l]*As[k](2,0)*w[j];\n                                \n                                ke[ndpn*k + 1][ndpn*(nseln+l)    ] += -0.5*Hm[l]*As[k](0,1)*w[j];\n                                ke[ndpn*k + 1][ndpn*(nseln+l) + 1] += -0.5*Hm[l]*As[k](1,1)*w[j];\n                                ke[ndpn*k + 1][ndpn*(nseln+l) + 2] += -0.5*Hm[l]*As[k](2,1)*w[j];\n                                \n                                ke[ndpn*k + 2][ndpn*(nseln+l)    ] += -0.5*Hm[l]*As[k](0,2)*w[j];\n                                ke[ndpn*k + 2][ndpn*(nseln+l) + 1] += -0.5*Hm[l]*As[k](1,2)*w[j];\n                                ke[ndpn*k + 2][ndpn*(nseln+l) + 2] += -0.5*Hm[l]*As[k](2,2)*w[j];\n                            }\n                        }\n                    }\n                    // assemble the global stiffness\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\tLS.Assemble(ke);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedElasticInterface::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n    if (m_laugon != FECore::AUGLAG_METHOD) return true;\n    \n    int i;\n    vec3d Ln;\n    bool bconv = true;\n    \n    int NS = m_ss.Elements();\n\tint NM = m_ms.Elements();\n    \n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0, normP = 0, normDP = 0;\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n\t\t\tFETiedElasticSurface::Data& ds = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += ds.m_Lmd*ds.m_Lmd;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j=0; j<el.GaussPoints(); ++j)\n        {\n\t\t\tFETiedElasticSurface::Data& dm = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += dm.m_Lmd*dm.m_Lmd;\n        }\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0;\n    \n    // update Lagrange multipliers\n    double normL1 = 0, eps;\n    for (i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n\t\t\tFETiedElasticSurface::Data& ds = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on primary surface\n            eps = m_epsn*ds.m_epsn;\n            ds.m_Lmd = ds.m_Lmd + ds.m_dg*eps;\n            \n            normL1 += ds.m_Lmd*ds.m_Lmd;\n            \n            maxgap = max(maxgap,sqrt(ds.m_dg*ds.m_dg));\n        }\n    }\n    \n\tfor (int i = 0; i<NM; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n\t\t\tFETiedElasticSurface::Data& dm = static_cast<FETiedElasticSurface::Data&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on secondary surface\n            eps = m_epsn*dm.m_epsn;\n            dm.m_Lmd = dm.m_Lmd + dm.m_dg*eps;\n            \n            normL1 += dm.m_Lmd*dm.m_Lmd;\n            \n            maxgap = max(maxgap,sqrt(dm.m_dg*dm.m_dg));\n        }\n    }\n    \n    // Ideally normP should be evaluated from the fluid pressure at the\n    // contact interface (not easily accessible).  The next best thing\n    // is to use the contact traction.\n    normP = normL1;\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0));\n    double pnorm = (normP != 0 ? (normDP/normP) : normDP);\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    \n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    if ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" sliding interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n    \n    feLog(\"    maximum gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedElasticInterface::Serialize(DumpStream &ar)\n{\n    // store contact data\n    FEContactInterface::Serialize(ar);\n    \n    // store contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n    \n    // serialize pointers\n    if (ar.IsShallow() == false)\n    {\n\t\tSerializeElementPointers(m_ss, m_ms, ar);\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FETiedElasticInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FETiedElasticSurface : public FEContactSurface\n{\npublic:\n    //! Integration point data\n    class Data : public FEContactMaterialPoint\n    {\n    public:\n        Data();\n\n\t\tvoid Serialize(DumpStream& ar) override;\n\n        void Init() override;\n        \n    public:\n        vec3d    m_Gap;     //!< initial gap in reference configuration\n        vec3d    m_dg;      //!< gap function at integration points\n        vec3d    m_nu;      //!< normal at integration points\n        vec2d    m_rs;      //!< natural coordinates of projection of integration point\n        vec3d    m_Lmd;     //!< lagrange multipliers for displacements\n        vec3d    m_tr;      //!< contact traction\n        double   m_epsn;    //!< penalty factors\n    };\n    \n    //! constructor\n    FETiedElasticSurface(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculate the nodal normals\n    void UpdateNodeNormals();\n    \n    void Serialize(DumpStream& ar) override;\n\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n    \npublic:\n    void GetVectorGap      (int nface, vec3d& pg) override;\n    void GetContactTraction(int nface, vec3d& pt) override;\n\n    //! evaluate net contact force\n    vec3d GetContactForce() override;\n\n    //! evaluate net contact area\n    double GetContactArea() override;\n\npublic:\n    vector<vec3d>           m_nn;   //!< node normals\n    vector<vec3d>           m_Fn;   //!< nodal forces\n};\n\n//-----------------------------------------------------------------------------\nclass FETiedElasticInterface : public FEContactInterface\n{\npublic:\n    //! constructor\n    FETiedElasticInterface(FEModel* pfem);\n    \n    //! destructor\n    ~FETiedElasticInterface();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! interface activation\n    void Activate() override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n    //! return integration rule class\n    bool UseNodalIntegration() override { return false; }\n    \n    //! build the matrix profile for use in the stiffness matrix\n    void BuildMatrixProfile(FEGlobalMatrix& K) override;\n    \npublic:\n    //! calculate contact forces\n    void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n    \n    //! calculate contact stiffness\n    void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n    \n    //! calculate Lagrangian augmentations\n    bool Augment(int naug, const FETimeInfo& tp) override;\n    \n    //! update\n    void Update() override;\n    \nprotected:\n    void InitialProjection(FETiedElasticSurface& ss, FETiedElasticSurface& ms);\n    void ProjectSurface(FETiedElasticSurface& ss, FETiedElasticSurface& ms);\n    \n    //! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n    void CalcAutoPenalty(FETiedElasticSurface& s);\n    \npublic:\n    FETiedElasticSurface    m_ss;    //!< primary surface\n\tFETiedElasticSurface    m_ms;    //!< secondary surface\n\n    int         m_knmult;       //!< higher order stiffness multiplier\n    bool        m_btwo_pass;    //!< two-pass flag\n    double      m_atol;         //!< augmentation tolerance\n    double      m_gtol;         //!< gap tolerance\n    double      m_stol;         //!< search tolerance\n    bool        m_bsymm;        //!< use symmetric stiffness components only\n    double      m_srad;         //!< contact search radius\n    int         m_naugmax;      //!< maximum nr of augmentations\n    int         m_naugmin;      //!< minimum nr of augmentations\n    \n    double      m_epsn;         //!< normal penalty factor\n    bool        m_bautopen;     //!< use autopenalty factor\n    bool        m_bupdtpen;     //!< update penalty at each time step\n\n\tbool            m_bflips;       //!< flip primary surface normal\n\tbool            m_bflipm;       //!< flip secondary surface normal\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETiedInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedInterface.h\"\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FETiedInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon  , \"laugon\"          )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_atol    , \"tolerance\"       );\n\tADD_PARAMETER(m_eps     , \"penalty\"         );\n\tADD_PARAMETER(m_naugmin , \"minaug\"          );\n\tADD_PARAMETER(m_naugmax , \"maxaug\"          );\n\tADD_PARAMETER(m_stol    , \"search_tolerance\");\n\tADD_PARAMETER(m_boffset , \"offset_shells\"   );\n\tADD_PARAMETER(m_Dmax    , \"max_distance\"    );\n\tADD_PARAMETER(m_bspecial, \"special\"         );\n\tADD_PARAMETER(m_breloc  , \"node_reloc\"      );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. Initialize default values.\nFETiedInterface::FETiedInterface(FEModel* pfem) : FEContactInterface(pfem), ss(pfem), ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\t// define sibling relationships\n\tss.SetSibling(&ms);\n\tms.SetSibling(&ss);\n\n\t// initial parameter values\n\tm_laugon = FECore::PENALTY_METHOD;\n\tm_atol = 0.01;\n\tm_eps = 1.0;\n\tm_stol = 0.0001;\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\tm_boffset = false;\n\tm_Dmax = 0.0;\n\tm_bspecial = true;\n\tm_breloc = false;\n\n\t// set parents\n\tss.SetContactInterface(this);\n\tms.SetContactInterface(this);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization. This function intializes the surfaces data and projects the\n//! primary surface onto the secondary surface.\n//! \nbool FETiedInterface::Init()\n{\n\t// set surface options\n\tss.SetShellOffset(m_boffset);\n\n\t// create the surfaces\n\tif (ss.Init() == false) return false;\n\tif (ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FETiedInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t{\n\t\tconst int LMSIZE = 6 * (FEElement::MAX_NODES + 1);\n\t\tvector<int> lm(LMSIZE);\n\n\t\tfor (int j = 0; j < ss.Nodes(); ++j)\n\t\t{\n\t\t\tFESurfaceElement* pe = ss.m_data[j].m_pme;\n\t\t\tif (pe != 0)\n\t\t\t{\n\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\tint* en = &me.m_lnode[0];\n\n\t\t\t\tint n = me.Nodes();\n\t\t\t\tlm.assign(LMSIZE, -1);\n\n\t\t\t\tlm[0] = ss.Node(j).m_ID[dof_X];\n\t\t\t\tlm[1] = ss.Node(j).m_ID[dof_Y];\n\t\t\t\tlm[2] = ss.Node(j).m_ID[dof_Z];\n\t\t\t\tlm[3] = ss.Node(j).m_ID[dof_RU];\n\t\t\t\tlm[4] = ss.Node(j).m_ID[dof_RV];\n\t\t\t\tlm[5] = ss.Node(j).m_ID[dof_RW];\n\n\t\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t\t{\n\t\t\t\t\tvector<int>& id = ms.Node(en[k]).m_ID;\n\t\t\t\t\tlm[6 * (k + 1)] = id[dof_X];\n\t\t\t\t\tlm[6 * (k + 1) + 1] = id[dof_Y];\n\t\t\t\t\tlm[6 * (k + 1) + 2] = id[dof_Z];\n\t\t\t\t\tlm[6 * (k + 1) + 3] = id[dof_RU];\n\t\t\t\t\tlm[6 * (k + 1) + 4] = id[dof_RV];\n\t\t\t\t\tlm[6 * (k + 1) + 5] = id[dof_RW];\n\t\t\t\t}\n\n\t\t\t\tK.build_add(lm);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tvector<int> lm;\n\t\tfor (int j = 0; j < ss.Nodes(); ++j)\n\t\t{\n\t\t\tFESurfaceElement* pe = ss.m_data[j].m_pme;\n\t\t\tif (pe != 0)\n\t\t\t{\n\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\tint* en = &me.m_lnode[0];\n\n\t\t\t\tint n = me.Nodes();\n\t\t\t\tlm.assign(3*(n+2), -1);\n\n\t\t\t\tlm[0] = ss.Node(j).m_ID[dof_X];\n\t\t\t\tlm[1] = ss.Node(j).m_ID[dof_Y];\n\t\t\t\tlm[2] = ss.Node(j).m_ID[dof_Z];\n\n\t\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t\t{\n\t\t\t\t\tvector<int>& id = ms.Node(en[k]).m_ID;\n\t\t\t\t\tlm[3 * (k + 1)    ] = id[dof_X];\n\t\t\t\t\tlm[3 * (k + 1) + 1] = id[dof_Y];\n\t\t\t\t\tlm[3 * (k + 1) + 2] = id[dof_Z];\n\t\t\t\t}\n\n\t\t\t\tlm[3 * (n + 1)  ] = m_LM[3 * j   ];\n\t\t\t\tlm[3 * (n + 1)+1] = m_LM[3 * j +1];\n\t\t\t\tlm[3 * (n + 1)+2] = m_LM[3 * j +2];\n\n\t\t\t\tK.build_add(lm);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Interface activation\nvoid FETiedInterface::Activate()\n{\n\t// Don't forget to call base member!\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(ss, ms, m_breloc);\n}\n\n//-----------------------------------------------------------------------------\n//! return number of equations to be allocated for Lagrange multipliers\nint FETiedInterface::InitEquations(int neq)\n{\n\t// make sure we want to use Lagrange Multiplier method\n\tif (m_laugon != FECore::LAGMULT_METHOD) return 0;\n\n\t// allocate three equations per primary node\n\tint NN = ss.Nodes();\n\n\tm_LM.resize(3 * NN);\n\tfor (int i = 0; i < 3 * NN; ++i) m_LM[i] = neq++;\n\n\treturn 3 * NN;\n}\n\n//-----------------------------------------------------------------------------\n//! Update tied interface data. This function re-evaluates the gaps between\n//! the primary node and their projections onto the secondary surface.\n//!\nvoid FETiedInterface::Update()\n{\n\t// get the mesh\n\tFEMesh& mesh = *ss.GetMesh();\n\n\t// loop over all primary nodes\n\tfor (int i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFESurfaceElement* pme = ss.m_data[i].m_pme;\n\t\tif (pme)\n\t\t{\n\t\t\t// get the current primary nodal position\n\t\t\tvec3d rt = ss.Node(i).m_rt;\n\n\t\t\t// get the natural coordinates of the primary projection\n\t\t\t// onto the secondary element\n\t\t\tdouble r = ss.m_data[i].m_rs[0];\n\t\t\tdouble s = ss.m_data[i].m_rs[1];\n\n\t\t\t// get the nodal coordinates\n\t\t\tint ne = pme->Nodes();\n\t\t\tvec3d y[FEElement::MAX_NODES];\n\t\t\tfor (int l=0; l<ne; ++l) y[l] = ms.Node( pme->m_lnode[l] ).m_rt;\n\n\t\t\t// calculate the primary node projection\n\t\t\tvec3d q = pme->eval(y, r, s);\n\n\t\t\t// calculate the secondary normal\n\t\t\tvec3d nu = ss.SurfaceNormal(*pme, r, s);\n\n\t\t\t// calculate the gap function\n\t\t\t// (taking possible offset into account)\n\t\t\tss.m_data[i].m_vgap = (rt - q) - nu*ss.m_data[i].m_off;\n\t\t\tss.m_data[i].m_gap = ss.m_data[i].m_vgap.norm();\n\n\t\t\t// calculate force\n\t\t\tss.m_data[i].m_Tc = ss.m_data[i].m_Lm + ss.m_data[i].m_vgap*m_eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project surface\n\nvoid FETiedInterface::ProjectSurface(FETiedContactSurface& ss, FETiedContactSurface& ms, bool bmove)\n{\n\t// closest point projection method\n\tFEClosestPointProjection cpp(ms);\n\tcpp.SetTolerance(m_stol);\n\tcpp.HandleSpecialCases(m_bspecial);\n\tcpp.Init();\n\n\t// let's count contact pairs\n\tint contacts = 0;\n\n\t// loop over all primary nodes\n\tfor (int i=0; i<ss.Nodes(); ++i)\n\t{\n\t\t// get the next node\n\t\tFENode& node = ss.Node(i);\n\t\tss.m_data[i].m_pme = nullptr;\n\n\t\t// get the nodal position of this primary node\n\t\tvec3d x = node.m_rt;\n\n\t\t// find the secondary element\n\t\tvec3d q; vec2d rs;\n\t\tFESurfaceElement* pme = cpp.Project(x, q, rs);\n\t\tif (pme)\n\t\t{\n\t\t\t// make sure we are within the max distance\n\t\t\tdouble D = (x - q).norm();\n\t\t\tif ((m_Dmax == 0.0) || (D <= m_Dmax))\n\t\t\t{\n\t\t\t\t// store the secondary element\n\t\t\t\tss.m_data[i].m_pme = pme;\n\n\t\t\t\t// store the natural coordinates of the projection on the secondary element\n\t\t\t\tss.m_data[i].m_rs = rs;\n\n\t\t\t\t// calculate the secondary normal\n\t\t\t\tvec3d nu = ms.SurfaceNormal(*pme, rs[0], rs[1]);\n\n\t\t\t\t// calculate gap\n\t\t\t\tss.m_data[i].m_vgap = (x - q) - nu*ss.m_data[i].m_off;\n\n\t\t\t\t// move the node if necessary\n\t\t\t\tif (bmove && (ss.m_data[i].m_vgap.norm()>0))\n\t\t\t\t{\n\t\t\t\t\tnode.m_r0 = node.m_rt = q + nu*ss.m_data[i].m_off;\n\t\t\t\t\tss.m_data[i].m_vgap = vec3d(0,0,0);\n\t\t\t\t}\n\n\t\t\t\t// calculate force\n\t\t\t\tss.m_data[i].m_Tc = ss.m_data[i].m_Lm + ss.m_data[i].m_vgap*m_eps;\n\n\t\t\t\tcontacts++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// if we found no contact pairs, let's report this since this is probably not the user's intention\n\tif (contacts == 0)\n\t{\n\t\tstd::string name = GetName();\n\t\tfeLogWarning(\"No contact pairs found for tied interface \\\"%s\\\".\\nThis contact interface may not have any effect.\", name.c_str());\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contact forces for a tied interface.\n\nvoid FETiedInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// shape function values\n\tdouble N[FEElement::MAX_NODES];\n\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\t// loop over all primary facets\n\tconst int NE = ss.Elements();\n\tfor (int j = 0; j < NE; ++j)\n\t{\n\t\t// get the primary element\n\t\tFESurfaceElement& sel = ss.Element(j);\n\n\t\t// get the element's LM vector\n\t\tss.UnpackLM(sel, sLM);\n\n\t\tint nseln = sel.Nodes();\n\n\t\tdouble* w = sel.GaussWeights();\n\n\t\t// loop over primary element nodes (which are the integration points as well)\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tint m = sel.m_lnode[n];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has a secondary element associated with it\n\t\t\t// TODO: is this a good way to test for an active constraint\n\t\t\t// The rigid wall criteria seems to work much better.\n\t\t\tif (ss.m_data[m].m_pme != 0)\n\t\t\t{\n\t\t\t\t// calculate jacobian and weight\n\t\t\t\tdouble Jw = ss.jac0(sel, n)*w[n];\n\n\t\t\t\t// get nodal contact force\n\t\t\t\tvec3d tc = ss.m_data[m].m_Lm;\n\n\t\t\t\t// add penalty contribution for penalty and aug lag method\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) tc += ss.m_data[m].m_vgap*m_eps;\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& mel = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(mel, mLM);\n\t\t\t\tint nmeln = mel.Nodes();\n\n\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t// onto the secondary element\n\t\t\t\tdouble r = ss.m_data[m].m_rs[0];\n\t\t\t\tdouble s = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tmel.shape_fnc(N, r, s);\n\n\t\t\t\t// allocate \"element\" force vector\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) fe.resize(3 * (nmeln + 1));\n\t\t\t\telse fe.resize(3 * (nmeln + 2));\n\n\t\t\t\t// calculate contribution to force vector from nodes\n\t\t\t\tfe[0] = -Jw * tc.x;\n\t\t\t\tfe[1] = -Jw * tc.y;\n\t\t\t\tfe[2] = -Jw * tc.z;\n\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * (l + 1)    ] = Jw * tc.x*N[l];\n\t\t\t\t\tfe[3 * (l + 1) + 1] = Jw * tc.y*N[l];\n\t\t\t\t\tfe[3 * (l + 1) + 2] = Jw * tc.z*N[l];\n\t\t\t\t}\n\n\t\t\t\t// setup lm vector\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) lm.resize(3 * (nmeln + 1));\n\t\t\t\telse lm.resize(3 * (nmeln + 2));\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (l + 1)    ] = mLM[l * 3];\n\t\t\t\t\tlm[3 * (l + 1) + 1] = mLM[l * 3 + 1];\n\t\t\t\t\tlm[3 * (l + 1) + 2] = mLM[l * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) en.resize(nmeln + 1);\n\t\t\t\telse en.resize(nmeln + 2);\n\n\t\t\t\t// fill the en array\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (int l = 0; l < nmeln; ++l) en[l + 1] = mel.m_node[l];\n\n\t\t\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t\t\t{\n\t\t\t\t\t// get the gap function\n\t\t\t\t\tvec3d g = ss.m_data[m].m_vgap;\n\n\t\t\t\t\t// add contribution from Lagrange multipliers\n\t\t\t\t\tfe[3 * (nmeln + 1)  ] = -Jw * g.x;\n\t\t\t\t\tfe[3 * (nmeln + 1)+1] = -Jw * g.y;\n\t\t\t\t\tfe[3 * (nmeln + 1)+2] = -Jw * g.z;\n\n\t\t\t\t\t// add the Lagrange multiplier equations to lm\n\t\t\t\t\tlm[3 * (nmeln + 1)  ] = m_LM[3 * m  ];\n\t\t\t\t\tlm[3 * (nmeln + 1)+1] = m_LM[3 * m+1];\n\t\t\t\t\tlm[3 * (nmeln + 1)+2] = m_LM[3 * m+2];\n\n\t\t\t\t\t// fill the en array\n\t\t\t\t\ten[nmeln + 1] = -1;\n\t\t\t\t}\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness matrix contribution.\nvoid FETiedInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, lm, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tFEElementMatrix ke;\n\n\t// shape functions\n\tdouble H[MN];\n\n\t// loop over all primary elements\n\tconst int NE = ss.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& se = ss.Element(i);\n\t\tint nseln = se.Nodes();\n\n\t\t// get the element's LM vector\n\t\tss.UnpackLM(se, sLM);\n\n\t\tdouble* w = se.GaussWeights();\n\n\t\t// loop over all integration points (that is nodes)\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tint m = se.m_lnode[n];\n\n\t\t\t// get the secondary element\n\t\t\tFESurfaceElement* pme = ss.m_data[m].m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *pme;\n\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdouble Jw = ss.jac0(se, n)*w[n];\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tdouble r = ss.m_data[m].m_rs[0];\n\t\t\t\tdouble s = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tme.shape_fnc(H, r, s);\n\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD)\n\t\t\t\t{\n\t\t\t\t\t// number of degrees of freedom\n\t\t\t\t\tint ndof = 3 * (1 + nmeln);\n\n\t\t\t\t\t// fill stiffness matrix\n\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\t\tke[0][0] = Jw*m_eps;\n\t\t\t\t\tke[1][1] = Jw*m_eps;\n\t\t\t\t\tke[2][2] = Jw*m_eps;\n\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[0][3 + 3 * k    ] = -Jw*m_eps*H[k];\n\t\t\t\t\t\tke[1][3 + 3 * k + 1] = -Jw*m_eps*H[k];\n\t\t\t\t\t\tke[2][3 + 3 * k + 2] = -Jw*m_eps*H[k];\n\n\t\t\t\t\t\tke[3 + 3 * k    ][0] = -Jw*m_eps*H[k];\n\t\t\t\t\t\tke[3 + 3 * k + 1][1] = -Jw*m_eps*H[k];\n\t\t\t\t\t\tke[3 + 3 * k + 2][2] = -Jw*m_eps*H[k];\n\t\t\t\t\t}\n\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tke[3 + 3 * k    ][3 + 3 * l    ] = Jw*m_eps*H[k] * H[l];\n\t\t\t\t\t\t\tke[3 + 3 * k + 1][3 + 3 * l + 1] = Jw*m_eps*H[k] * H[l];\n\t\t\t\t\t\t\tke[3 + 3 * k + 2][3 + 3 * l + 2] = Jw*m_eps*H[k] * H[l];\n\t\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\t// number of degrees of freedom\n\t\t\t\t\tint ndof = 3 * (2 + nmeln);\n\n\t\t\t\t\t// fill stiffness matrix\n\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\n\t\t\t\t\tint L = 3 * (nmeln + 1);\n\t\t\t\t\tke[0][L  ] = ke[L  ][0] = Jw;\n\t\t\t\t\tke[1][L+1] = ke[L+1][1] = Jw;\n\t\t\t\t\tke[2][L+2] = ke[L+2][2] = Jw;\n\t\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3 + 3*k    ][L    ] = -Jw*H[k];\n\t\t\t\t\t\tke[3 + 3*k + 1][L + 1] = -Jw*H[k];\n\t\t\t\t\t\tke[3 + 3*k + 2][L + 2] = -Jw*H[k];\n\n\t\t\t\t\t\tke[L  ][3 + 3*k    ] = -Jw*H[k];\n\t\t\t\t\t\tke[L+1][3 + 3*k + 1] = -Jw*H[k];\n\t\t\t\t\t\tke[L+2][3 + 3*k + 2] = -Jw*H[k];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// create lm array\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) lm.resize(3 * (1 + nmeln));\n\t\t\t\telse lm.resize(3 * (2 + nmeln));\n\n\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (k + 1)] = mLM[k * 3];\n\t\t\t\t\tlm[3 * (k + 1) + 1] = mLM[k * 3 + 1];\n\t\t\t\t\tlm[3 * (k + 1) + 2] = mLM[k * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (nmeln + 1)] = m_LM[3 * m];\n\t\t\t\t\tlm[3 * (nmeln + 1) + 1] = m_LM[3 * m + 1];\n\t\t\t\t\tlm[3 * (nmeln + 1) + 2] = m_LM[3 * m + 2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\tif (m_laugon != FECore::LAGMULT_METHOD) en.resize(nmeln + 1);\n\t\t\t\telse en.resize(nmeln + 2);\n\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + 1] = me.m_node[k];\n\n\t\t\t\tif (m_laugon == FECore::LAGMULT_METHOD) en[nmeln + 1] = -1;\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Do an augmentation.\nbool FETiedInterface::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tvec3d lm = ss.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tvec3d lm = ss.m_data[i].m_Lm + ss.m_data[i].m_vgap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tif (ss.m_data[i].m_pme != 0)\n\t\t{\n\t\t\tdouble g = ss.m_data[i].m_vgap.norm();\n\t\t\tnormgc += g*g;\n\t\t\t++N;\n\t\t}\n\t}\t\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\t// check convergence of constraints\n\tfeLog(\" tied interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\t\t\n\t// check convergence\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tif (bconv == false) \n\t{\n\t\tfor (i=0; i<ss.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tss.m_data[i].m_Lm = ss.m_data[i].m_Lm + ss.m_data[i].m_vgap*m_eps;\n\t\t}\t\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize the data to the archive.\nvoid FETiedInterface::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tms.Serialize(ar);\n\tss.Serialize(ar);\n\n\t// serialize pointers\n\tif (ar.IsShallow() == false)\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tint NN = ss.Nodes();\n\t\t\tar << NN;\n\t\t\tfor (int i=0; i<NN; ++i)\n\t\t\t{\n\t\t\t\tFESurfaceElement* pe = ss.m_data[i].m_pme;\n\t\t\t\tif (pe) ar << pe->m_lid; else ar << -1;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint NN, lid;\n\t\t\tar >> NN;\n\t\t\tfor (int i=0; i<NN; ++i)\n\t\t\t{\n\t\t\t\tar >> lid;\n\t\t\t\tif (lid < 0) ss.m_data[i].m_pme = nullptr; else ss.m_data[i].m_pme = &ms.Element(lid);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FETiedInterface::PrepStep()\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < ss.Nodes(); ++i)\n\t\t{\n\t\t\tss.m_data[i].m_Lp = ss.m_data[i].m_Lm;\n\t\t}\n\t}\n}\n\n//! Update Lagrange multipliers\nvoid FETiedInterface::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon == FECore::LAGMULT_METHOD)\n\t{\n\t\tfor (int i = 0; i < ss.Nodes(); ++i)\n\t\t{\n\t\t\tss.m_data[i].m_Lm.x = ss.m_data[i].m_Lp.x + Ui[m_LM[3 * i    ]] + ui[m_LM[3 * i    ]];\n\t\t\tss.m_data[i].m_Lm.y = ss.m_data[i].m_Lp.y + Ui[m_LM[3 * i + 1]] + ui[m_LM[3 * i + 1]];\n\t\t\tss.m_data[i].m_Lm.z = ss.m_data[i].m_Lp.z + Ui[m_LM[3 * i + 2]] + ui[m_LM[3 * i + 2]];\n\t\t}\n\t}\n}\n\nvoid FETiedInterface::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n\tif (m_laugon != FECore::LAGMULT_METHOD) return;\n\n\tfor (int i = 0; i < ss.Nodes(); ++i)\n\t{\n\t\tUi[m_LM[3 * i    ]] += ui[m_LM[3 * i    ]];\n\t\tUi[m_LM[3 * i + 1]] += ui[m_LM[3 * i + 1]];\n\t\tUi[m_LM[3 * i + 2]] += ui[m_LM[3 * i + 2]];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FETiedInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEContactInterface.h\"\n#include \"FETiedContactSurface.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a tied interface.\n\nclass FETiedInterface : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFETiedInterface(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FETiedInterface(){}\n\n\t//! Initializes sliding interface\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! projects nodes onto secondary surface\n\tvoid ProjectSurface(FETiedContactSurface& ss, FETiedContactSurface& ms, bool bmove = false);\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &ss; }\n\tFESurface* GetSecondarySurface() override { return &ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! return number of equations to be allocated for Lagrange multipliers\n\tint InitEquations(int neq) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\n\t//! prepare for next time step\n\tvoid PrepStep() override;\n\n\t//! Update Lagrange multipliers\n\tvoid Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n\n\tvoid UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n\npublic:\n\tFETiedContactSurface\tss;\t//!< primary surface\n\tFETiedContactSurface\tms;\t//!< secondary surface\n\npublic:\n\tdouble\t\tm_atol;\t\t//!< augmentation tolerance\n\tdouble\t\tm_eps;\t\t//!< penalty scale factor\n\tdouble\t\tm_stol;\t\t//!< search tolerance\n\tint\t\t\tm_naugmax;\t//!< maximum nr of augmentations\n\tint\t\t\tm_naugmin;\t//!< minimum nr of augmentations\n\tbool\t\tm_boffset;\t//!< offset primary surface for shells\n\tdouble\t\tm_Dmax;\t\t//!< max distance for contact\n\tbool\t\tm_bspecial;\t//!< handle special cases in projection\n\tbool\t\tm_breloc;\t//!< node relocation on initialization\n\n\tvector<int>\tm_LM;\t//!< Lagrange multiplier equations\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETiedLineConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FETiedLineConstraint.h\"\n#include <FECore/FEClosestPointProjection.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/log.h>\n\nFETiedLine::FETiedLine(FEModel* fem) : FEEdge(fem)\n{\n\n}\n\nFEMaterialPoint* FETiedLine::CreateMaterialPoint()\n{\n\treturn new FELineMaterialPoint;\n}\n\nvoid FETiedLine::Update()\n{\n\n}\n\nbool FETiedLine::Create(FESegmentSet& eset)\n{\n\treturn FEEdge::Create(eset, FE_LINE2NI); // TODO: Does this assume linear edges?\n}\n\nbool FETiedLine::Init()\n{\n\tif (FEEdge::Init() == false) return false;\n\tint NN = Nodes();\n\tm_data.resize(NN);\n\treturn true;\n}\n\nFETiedLine::Projection FETiedLine::ClosestProjection(const vec3d& x)\n{\n\tFETiedLine::Projection P;\n\tint NE = Elements();\n\tdouble L2min = 0;\n\tconst double eps = 1e-5;\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\t\tvec3d a = Node(el.m_lnode[0]).m_rt;\n\t\tvec3d b = Node(el.m_lnode[1]).m_rt;\n\t\tvec3d e = (b - a);\n\n\t\tdouble D2 = e.norm2();\n\t\tif (D2 != 0)\n\t\t{\n\t\t\tdouble l = ((x-a)*e)/D2;\n\t\t\tif ((l >= -eps) && (l <= 1.0 + eps))\n\t\t\t{\n\t\t\t\tvec3d q = a + e * l;\n\t\t\t\tdouble L2 = (x - q).norm2();\n\t\t\t\tif ((P.pe == nullptr) || (L2 < L2min))\n\t\t\t\t{\n\t\t\t\t\tP.pe = &el;\n\t\t\t\t\tP.r = 2.0 * l - 1;\n\t\t\t\t\tP.q = q;\n\t\t\t\t\tL2min = L2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tassert(P.pe);\n\treturn P;\n}\n\nBEGIN_FECORE_CLASS(FETiedLineConstraint, FENLConstraint)\n\tADD_PARAMETER(m_laugon  , \"laugon\"          )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol    , \"tolerance\"       );\n\tADD_PARAMETER(m_eps     , \"penalty\"         );\n\tADD_PARAMETER(m_naugmin , \"minaug\"          );\n\tADD_PARAMETER(m_naugmax , \"maxaug\"          );\n\tADD_PARAMETER(m_Dmax    , \"max_distance\"    );\n\n\tADD_PROPERTY(pl, \"primary\")->AddFlag(FEProperty::Reference);\n\tADD_PROPERTY(sl, \"secondary\")->AddFlag(FEProperty::Reference);\n\nEND_FECORE_CLASS();\n\nFETiedLineConstraint::FETiedLineConstraint(FEModel* pfem) : FENLConstraint(pfem), pl(pfem), sl(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\t// initial parameter values\n\tm_laugon = FECore::PENALTY_METHOD;\n\tm_atol = 0.01;\n\tm_eps = 1.0;\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\tm_Dmax = 0.0;\n}\n\nbool FETiedLineConstraint::Init()\n{\n\tif (pl.Init() == false) return false;\n\tif (sl.Init() == false) return false;\n\n\treturn true;\n}\n\nvoid FETiedLineConstraint::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tconst int dof_RU = GetDOFIndex(\"Ru\");\n\tconst int dof_RV = GetDOFIndex(\"Rv\");\n\tconst int dof_RW = GetDOFIndex(\"Rw\");\n\n\tconst int LMSIZE = 6 * (FEElement::MAX_NODES + 1);\n\tvector<int> lm(LMSIZE);\n\n\tfor (int j = 0; j < pl.Nodes(); ++j)\n\t{\n\t\tFENode& nj = pl.Node(j);\n\t\tFELineElement* pe = pl.m_data[j].me;\n\t\tif (pe != 0)\n\t\t{\n\t\t\tFELineElement& me = *pe;\n\t\t\tint* en = &me.m_lnode[0];\n\n\t\t\tint n = me.Nodes();\n\t\t\tlm.assign(LMSIZE, -1);\n\n\t\t\tlm[0] = nj.m_ID[dof_X];\n\t\t\tlm[1] = nj.m_ID[dof_Y];\n\t\t\tlm[2] = nj.m_ID[dof_Z];\n\t\t\tlm[3] = nj.m_ID[dof_RU];\n\t\t\tlm[4] = nj.m_ID[dof_RV];\n\t\t\tlm[5] = nj.m_ID[dof_RW];\n\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tvector<int>& id = sl.Node(en[k]).m_ID;\n\t\t\t\tlm[6 * (k + 1)    ] = id[dof_X];\n\t\t\t\tlm[6 * (k + 1) + 1] = id[dof_Y];\n\t\t\t\tlm[6 * (k + 1) + 2] = id[dof_Z];\n\t\t\t\tlm[6 * (k + 1) + 3] = id[dof_RU];\n\t\t\t\tlm[6 * (k + 1) + 4] = id[dof_RV];\n\t\t\t\tlm[6 * (k + 1) + 5] = id[dof_RW];\n\t\t\t}\n\n\t\t\tK.build_add(lm);\n\t\t}\n\t}\n}\n\nvoid FETiedLineConstraint::Activate()\n{\n\t// Don't forget to call base member!\n\tFENLConstraint::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectLines(pl, sl);\n}\n\nvoid FETiedLineConstraint::Update()\n{\n\t// get the mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// loop over all primary nodes\n\tfor (int i=0; i<pl.Nodes(); ++i)\n\t{\n\t\tFELineElement* pme = pl.m_data[i].me;\n\t\tif (pme)\n\t\t{\n\t\t\t// get the current primary nodal position\n\t\t\tvec3d rt = pl.Node(i).m_rt;\n\n\t\t\t// get the natural coordinates of the primary projection\n\t\t\t// onto the secondary element\n\t\t\tdouble r = pl.m_data[i].r;\n\n\t\t\t// get the nodal coordinates\n\t\t\tint ne = pme->Nodes();\n\t\t\tvec3d y[FEElement::MAX_NODES];\n\t\t\tfor (int l=0; l<ne; ++l) y[l] = sl.Node( pme->m_lnode[l] ).m_rt;\n\n\t\t\t// calculate the primary node projection\n\t\t\tvec3d q;\n\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\tpme->shape(H, r);\n\t\t\tfor (int n = 0; n < ne; ++n) q += y[n] * H[n];\n\n\t\t\t// calculate the gap function\n\t\t\tpl.m_data[i].vgap = (rt - q);\n\n\t\t\t// calculate force\n\t\t\tpl.m_data[i].Tc = pl.m_data[i].Lm + pl.m_data[i].vgap*m_eps;\n\t\t}\n\t}\n}\n\nvoid FETiedLineConstraint::ProjectLines(FETiedLine& pl, FETiedLine& sl)\n{\n\t// let's count contact pairs\n\tint contacts = 0;\n\n\t// loop over all primary nodes\n\tfor (int i=0; i< pl.Nodes(); ++i)\n\t{\n\t\t// get the next node\n\t\tFENode& node = pl.Node(i);\n\t\tpl.m_data[i].me = nullptr;\n\n\t\t// get the nodal position of this primary node\n\t\tvec3d x = node.m_rt;\n\n\t\t// find the secondary element\n\t\tFETiedLine::Projection P = sl.ClosestProjection(x);\n\t\tif (P.pe)\n\t\t{\n\t\t\t// make sure we are within the max distance\n\t\t\tdouble D = (x - P.q).norm();\n\t\t\tif ((m_Dmax == 0.0) || (D <= m_Dmax))\n\t\t\t{\n\t\t\t\t// store the secondary element\n\t\t\t\tpl.m_data[i].me = P.pe;\n\n\t\t\t\t// store the natural coordinates of the projection on the secondary element\n\t\t\t\tpl.m_data[i].r = P.r;\n\n\t\t\t\t// calculate vector gap\n\t\t\t\tpl.m_data[i].vgap = (x - P.q);\n\n\t\t\t\t// calculate force\n\t\t\t\tpl.m_data[i].Tc = pl.m_data[i].Lm + pl.m_data[i].vgap*m_eps;\n\n\t\t\t\tcontacts++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// if we found no contact pairs, let's report this since this is probably not the user's intention\n\tif (contacts == 0)\n\t{\n\t\tstd::string name = GetName();\n\t\tfeLogWarning(\"No contact pairs found for 1D slide line\\\"%s\\\".\\nThis contact interface may not have any effect.\", name.c_str());\n\t}\n}\n\nvoid FETiedLineConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// shape function values\n\tdouble N[FEElement::MAX_NODES];\n\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\t// loop over all primary facets\n\tconst int NE = pl.Elements();\n\tfor (int j = 0; j < NE; ++j)\n\t{\n\t\t// get the primary element\n\t\tFELineElement& sel = pl.Element(j);\n\t\tint nseln = sel.Nodes();\n\n\t\t// get the element's LM vector\n\t\t// TODO: This assumes dofs are indexed at (0,1,2)!\n\t\tsLM.resize(3 * nseln);\n\t\tfor (int a = 0; a < nseln; ++a)\n\t\t{\n\t\t\tFENode& node = pl.Node(sel.m_lnode[a]);\n\t\t\tsLM[3 * a    ] = node.m_ID[0];\n\t\t\tsLM[3 * a + 1] = node.m_ID[1];\n\t\t\tsLM[3 * a + 2] = node.m_ID[2];\n\t\t}\n\n\t\tvec3d a = pl.Node(sel.m_lnode[0]).m_r0;\n\t\tvec3d b = pl.Node(sel.m_lnode[1]).m_r0;\n\t\tdouble L = (b - a).norm();\n\n\t\tdouble* w = sel.GaussWeights();\n\n\t\t// loop over primary element nodes (which are the integration points as well)\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tint m = sel.m_lnode[n];\n\n\t\t\t// see if this node's constraint is active\n\t\t\t// that is, if it has a secondary element associated with it\n\t\t\t// TODO: is this a good way to test for an active constraint\n\t\t\t// The rigid wall criteria seems to work much better.\n\t\t\tif (pl.m_data[m].me != 0)\n\t\t\t{\n\t\t\t\t// calculate jacobian and weight\n\t\t\t\tdouble Jw = w[n] * (L / 2);\n\n\t\t\t\t// get nodal contact force\n\t\t\t\tvec3d tc = pl.m_data[m].Lm;\n\n\t\t\t\t// add penalty contribution for penalty and aug lag method\n\t\t\t\ttc += pl.m_data[m].vgap*m_eps;\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFELineElement& mel = *pl.m_data[m].me;\n\t\t\t\tint nmeln = mel.Nodes();\n\n\t\t\t\tmLM.resize(3 * nmeln);\n\t\t\t\tfor (int b = 0; b < nmeln; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = sl.Node(mel.m_lnode[b]);\n\t\t\t\t\tmLM[3 * b    ] = node.m_ID[0];\n\t\t\t\t\tmLM[3 * b + 1] = node.m_ID[1];\n\t\t\t\t\tmLM[3 * b + 2] = node.m_ID[2];\n\t\t\t\t}\n\n\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t// onto the secondary element\n\t\t\t\tdouble r = pl.m_data[m].r;\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tmel.shape(N, r);\n\n\t\t\t\t// allocate \"element\" force vector\n\t\t\t\tfe.resize(3 * (nmeln + 1));\n\n\t\t\t\t// calculate contribution to force vector from nodes\n\t\t\t\tfe[0] = -Jw * tc.x;\n\t\t\t\tfe[1] = -Jw * tc.y;\n\t\t\t\tfe[2] = -Jw * tc.z;\n\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tfe[3 * (l + 1)    ] = Jw * tc.x*N[l];\n\t\t\t\t\tfe[3 * (l + 1) + 1] = Jw * tc.y*N[l];\n\t\t\t\t\tfe[3 * (l + 1) + 2] = Jw * tc.z*N[l];\n\t\t\t\t}\n\n\t\t\t\t// setup lm vector\n\t\t\t\tlm.resize(3 * (nmeln + 1));\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (l + 1)    ] = mLM[l * 3];\n\t\t\t\t\tlm[3 * (l + 1) + 1] = mLM[l * 3 + 1];\n\t\t\t\t\tlm[3 * (l + 1) + 2] = mLM[l * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\ten.resize(nmeln + 1);\n\n\t\t\t\t// fill the en array\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (int l = 0; l < nmeln; ++l) en[l + 1] = mel.m_node[l];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stiffness matrix contribution.\nvoid FETiedLineConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, lm, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tFEElementMatrix ke;\n\n\t// shape functions\n\tdouble H[MN];\n\n\t// loop over all primary elements\n\tconst int NE = pl.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFELineElement& se = pl.Element(i);\n\t\tint nseln = se.Nodes();\n\n\t\t// get the element's LM vector\n\t\t// TODO: This assumes dofs are indexed at (0,1,2)!\n\t\tsLM.resize(3 * nseln);\n\t\tfor (int a = 0; a < nseln; ++a)\n\t\t{\n\t\t\tFENode& node = pl.Node(se.m_lnode[a]);\n\t\t\tsLM[3 * a    ] = node.m_ID[0];\n\t\t\tsLM[3 * a + 1] = node.m_ID[1];\n\t\t\tsLM[3 * a + 2] = node.m_ID[2];\n\t\t}\n\n\t\tdouble* w = se.GaussWeights();\n\n\t\tvec3d a = pl.Node(se.m_lnode[0]).m_r0;\n\t\tvec3d b = pl.Node(se.m_lnode[1]).m_r0;\n\t\tdouble L = (b - a).norm();\n\n\t\t// loop over all integration points (that is nodes)\n\t\tfor (int n = 0; n < nseln; ++n)\n\t\t{\n\t\t\tint m = se.m_lnode[n];\n\n\t\t\t// get the secondary element\n\t\t\tFELineElement* pme = pl.m_data[m].me;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// get the secondary element\n\t\t\t\tFELineElement& me = *pme;\n\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\tmLM.resize(3 * nmeln);\n\t\t\t\tfor (int b = 0; b < nmeln; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = sl.Node(me.m_lnode[b]);\n\t\t\t\t\tmLM[3 * b    ] = node.m_ID[0];\n\t\t\t\t\tmLM[3 * b + 1] = node.m_ID[1];\n\t\t\t\t\tmLM[3 * b + 2] = node.m_ID[2];\n\t\t\t\t}\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdouble Jw = w[n]*(L/2);\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tdouble r = pl.m_data[m].r;\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tme.shape(H, r);\n\n\t\t\t\t// number of degrees of freedom\n\t\t\t\tint ndof = 3 * (1 + nmeln);\n\n\t\t\t\t// fill stiffness matrix\n\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\tke[0][0] = Jw*m_eps;\n\t\t\t\tke[1][1] = Jw*m_eps;\n\t\t\t\tke[2][2] = Jw*m_eps;\n\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tke[0][3 + 3 * k    ] = -Jw*m_eps*H[k];\n\t\t\t\t\tke[1][3 + 3 * k + 1] = -Jw*m_eps*H[k];\n\t\t\t\t\tke[2][3 + 3 * k + 2] = -Jw*m_eps*H[k];\n\n\t\t\t\t\tke[3 + 3 * k    ][0] = -Jw*m_eps*H[k];\n\t\t\t\t\tke[3 + 3 * k + 1][1] = -Jw*m_eps*H[k];\n\t\t\t\t\tke[3 + 3 * k + 2][2] = -Jw*m_eps*H[k];\n\t\t\t\t}\n\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t\tfor (int l = 0; l < nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3 + 3 * k    ][3 + 3 * l    ] = Jw*m_eps*H[k] * H[l];\n\t\t\t\t\t\tke[3 + 3 * k + 1][3 + 3 * l + 1] = Jw*m_eps*H[k] * H[l];\n\t\t\t\t\t\tke[3 + 3 * k + 2][3 + 3 * l + 2] = Jw*m_eps*H[k] * H[l];\n\t\t\t\t\t}\n\n\t\t\t\t// create lm array\n\t\t\t\tlm.resize(3 * (1 + nmeln));\n\n\t\t\t\tlm[0] = sLM[n * 3];\n\t\t\t\tlm[1] = sLM[n * 3 + 1];\n\t\t\t\tlm[2] = sLM[n * 3 + 2];\n\t\t\t\tfor (int k = 0; k < nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3 * (k + 1)    ] = mLM[k * 3];\n\t\t\t\t\tlm[3 * (k + 1) + 1] = mLM[k * 3 + 1];\n\t\t\t\t\tlm[3 * (k + 1) + 2] = mLM[k * 3 + 2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln + 1);\n\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (int k = 0; k < nmeln; ++k) en[k + 1] = me.m_node[k];\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool FETiedLineConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (int i=0; i< pl.Nodes(); ++i)\n\t{\n\t\tvec3d lm = pl.m_data[i].Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (int i=0; i<pl.Nodes(); ++i)\n\t{\n\t\tvec3d lm = pl.m_data[i].Lm + pl.m_data[i].vgap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tif (pl.m_data[i].me != 0)\n\t\t{\n\t\t\tdouble g2 = pl.m_data[i].vgap.norm2();\n\t\t\tnormgc += g2;\n\t\t\t++N;\n\t\t}\n\t}\t\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\t// check convergence of constraints\n\tconst std::string& name = GetName();\n\tif (name.empty())\n\t\tfeLog(\" tied-line # %d\\n\", GetID());\n\telse\n\t\tfeLog(\" %s [tied-line]\\n\", name.c_str());\n\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\t\t\n\t// check convergence\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tif (bconv == false) \n\t{\n\t\tfor (int i=0; i<pl.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tpl.m_data[i].Lm = pl.m_data[i].Lm + pl.m_data[i].vgap*m_eps;\n\t\t}\t\n\t}\n\n\treturn bconv;\n}\n\nvoid FETiedLineConstraint::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFENLConstraint::Serialize(ar);\n\n\t// store contact surface data\n\tpl.Serialize(ar);\n\tsl.Serialize(ar);\n\n\t// serialize pointers\n\tif (ar.IsShallow() == false)\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tint NN = pl.Nodes();\n\t\t\tar << NN;\n\t\t\tfor (int i=0; i<NN; ++i)\n\t\t\t{\n\t\t\t\tFELineElement* pe = pl.m_data[i].me;\n\t\t\t\tif (pe) ar << pe->GetLocalID(); else ar << -1;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint NN, lid;\n\t\t\tar >> NN;\n\t\t\tfor (int i=0; i<NN; ++i)\n\t\t\t{\n\t\t\t\tar >> lid;\n\t\t\t\tif (lid < 0) pl.m_data[i].me = nullptr; \n\t\t\t\telse pl.m_data[i].me = &sl.Element(lid);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FETiedLineConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENLConstraint.h>\n#include <FECore/FEEdge.h>\n\nclass FETiedLine : public FEEdge\n{\npublic:\n\tstruct NodeData\n\t{\n\t\tFELineElement* me = nullptr;\n\t\tdouble r;\n\t\tvec3d vgap;\n\t\tvec3d Tc;\n\t\tvec3d Lm;\n\t};\n\n\tstruct Projection\n\t{\n\t\tFELineElement* pe = nullptr;\n\t\tvec3d q;\n\t\tdouble r = 0;\n\t};\n\npublic:\n\tFETiedLine(FEModel* fem);\n\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\tvoid Update();\n\n\tbool Create(FESegmentSet& eset) override;\n\n\tbool Init() override;\n\n\tProjection ClosestProjection(const vec3d& x);\n\npublic:\n\tstd::vector<NodeData> m_data;\n};\n\n\n//! This class implements a tied-line.\nclass FETiedLineConstraint : public FENLConstraint\n{\npublic:\n\t//! constructor\n\tFETiedLineConstraint(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FETiedLineConstraint(){}\n\n\t//! Initializes sliding interface\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\tvoid ProjectLines(FETiedLine& pl, FETiedLine& sl);\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\npublic:\n\tint\t\t\tm_laugon;\t//!< enforcement method (0=penalty, 1=aug. Lag.)\n\tdouble\t\tm_atol;\t\t//!< augmentation tolerance\n\tdouble\t\tm_eps;\t\t//!< penalty scale factor\n\tint\t\t\tm_naugmax;\t//!< maximum nr of augmentations\n\tint\t\t\tm_naugmin;\t//!< minimum nr of augmentations\n\tdouble\t\tm_Dmax;\t\t//!< max distance for contact\n\nprivate:\n\tFETiedLine pl; // primary line\n\tFETiedLine sl; // secondary line\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETorsionalSpring.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FETorsionalSpring.h\"\n\nBEGIN_FECORE_CLASS(FETorsionalSpring, FEDiscreteElasticMaterial)\n\tADD_PARAMETER(m_r, \"r\");\nEND_FECORE_CLASS();\n\nFETorsionalSpring::FETorsionalSpring(FEModel* fem) : FEDiscreteElasticMaterial(fem)\n{\n\tm_r = 0.0;\n}\n\n// evaluate the force at a discrete element\nvec3d FETorsionalSpring::Force(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e0 = mp.m_dr0; e0.unit();\n\tvec3d et = mp.m_drt; \n\tdouble l = et.unit();\n\n\tdouble w = e0 * et;\n\n\tmat3ds exe = dyad(et);\n\tmat3dd I(1.0);\n\n\tmat3ds P = (I - exe) / l;\n\tvec3d t = P * e0;\n\n\tdouble F = -m_r*w;\n\n\treturn t*F;\n}\n\n// evaluate the stiffness at a discrete element (= dF / dr)\nmat3d FETorsionalSpring::Stiffness(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e0 = mp.m_dr0; e0.unit();\n\tvec3d et = mp.m_drt;\n\tdouble l = et.unit();\n\n\tdouble w = e0 * et;\n\n\tmat3ds exe = dyad(et);\n\tmat3dd I(1.0);\n\n\tmat3ds P = (I - exe) / l;\n\tvec3d t = P * e0;\n\n\tmat3ds Q = dyads(e0, et) + I*w - exe*(3.0*w);\n\n\tmat3ds T = dyad(t);\n\n\treturn T*(m_r) + Q*(m_r*w);\n}\n\ndouble FETorsionalSpring::StrainEnergy(FEDiscreteMaterialPoint& mp)\n{\n\tvec3d e0 = mp.m_dr0; e0.unit();\n\tvec3d et = mp.m_drt; \n\tdouble l = et.unit();\n\n\tdouble w = e0 * et;\n\n\tdouble E = m_r*(1-w*w)/2;\n\n\treturn E;\n\n}"
  },
  {
    "path": "FEBioMech/FETorsionalSpring.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDiscreteElasticMaterial.h\"\n#include \"febiomech_api.h\"\n\nclass FEBIOMECH_API FETorsionalSpring : public FEDiscreteElasticMaterial\n{\npublic:\n\tFETorsionalSpring(FEModel* fem);\n\n\t// evaluate the force at a discrete element\n\tvec3d Force(FEDiscreteMaterialPoint& mp) override;\n\n\t// evaluate the stiffness at a discrete element (= dF / dr)\n\tmat3d Stiffness(FEDiscreteMaterialPoint& mp) override;\n\n\t// evaluate the force at a discrete element\n\tdouble StrainEnergy(FEDiscreteMaterialPoint& mp) override;\n\nprivate:\n\tdouble\tm_r;\t//!< rotational stiffness\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETraceFreeNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETraceFreeNeoHookean.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FETraceFreeNeoHookean, FEElasticMaterial)\n    ADD_PARAMETER(m_mu, FE_RANGE_GREATER(0.0), \"mu\")->setLongName(\"shear modulus\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nmat3ds FETraceFreeNeoHookean::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double J = pt.m_J;\n    \n    // get the material parameter\n    double mu = m_mu(mp);\n    \n    // calculate left Cauchy-Green tensor\n    mat3ds b = pt.LeftCauchyGreen();\n    \n    double I1 = b.tr();\n    \n    // Identity\n    mat3dd I(1);\n    \n    // calculate stress\n    mat3ds s = (b*(3/I1) - I)*(mu/J);\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FETraceFreeNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double J = pt.m_J;\n    \n    // get the material parameter\n    double mu = m_mu(mp);\n\n    mat3dd I(1);\n    \n    return dyad4s(I)*(2*mu/J);\n}\n\n//-----------------------------------------------------------------------------\ndouble FETraceFreeNeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double J = pt.m_J;\n    \n    // get the material parameter\n    double mu = m_mu(mp);\n\n    // calculate left Cauchy-Green tensor\n    mat3ds b = pt.LeftCauchyGreen();\n    double I1 = b.tr();\n    \n    double sed = mu*log(pow(I1/3,1.5)/J);\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FETraceFreeNeoHookean::PK2Stress(FEMaterialPoint& mp, const mat3ds ES)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the material parameter\n    double mu = m_mu(mp);\n    \n    mat3ds C = pt.RightCauchyGreen();\n    double I1 = C.tr();\n    \n    // Identity\n    mat3dd I(1);\n    \n    // calculate stress\n    mat3ds S = (I*(3/I1) - C.inverse())*mu;\n    \n    return S;\n}\n\n//-----------------------------------------------------------------------------\ntens4dmm FETraceFreeNeoHookean::MaterialTangent(FEMaterialPoint& mp, const mat3ds ES)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the material parameter\n    double mu = m_mu(mp);\n    \n    mat3ds C = pt.RightCauchyGreen();\n    tens4dmm c = dyad4s(C.inverse())*(2*mu);\n    \n    return c;\n}\n"
  },
  {
    "path": "FEBioMech/FETraceFreeNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Trace-Free Neo Hookean material\n\n//! Implementation of a trace-free neo-Hookean hyperelastic material.\nclass FEBIOMECH_API FETraceFreeNeoHookean : public FEElasticMaterial\n{\npublic:\n    FETraceFreeNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem) {}\n    \npublic:\n    FEParamDouble       m_mu;   //!< shear modulus\n    \npublic:\n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! calculate the 2nd Piola-Kirchhoff stress at material point\n    mat3ds PK2Stress(FEMaterialPoint& pt, const mat3ds E) override;\n    \n    //! calculate material tangent stiffness at material point\n    tens4dmm MaterialTangent(FEMaterialPoint& pt, const mat3ds E) override;\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETractionLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETractionLoad.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEFacetSet.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FETractionLoad, FESurfaceLoad)\n\tADD_PARAMETER(m_scale   , \"scale\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_traction, \"traction\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_bshellb , \"shell_bottom\");\n\tADD_PARAMETER(m_blinear, \"linear\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETractionLoad::FETractionLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n\tm_scale = 1.0;\n\tm_traction = vec3d(0, 0, 0);\n\tm_bshellb = false;\n\tm_blinear = false;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETractionLoad::SetSurface(FESurface* ps)\n{\n\tFESurfaceLoad::SetSurface(ps);\n\tm_traction.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\n// initialization\nbool FETractionLoad::Init()\n{\n\tFESurface& surf = GetSurface();\n\tsurf.SetShellBottom(m_bshellb);\n\n\t// get the degrees of freedom\n\tm_dof.Clear();\n\tif (m_bshellb == false)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\telse\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t}\n\tif (m_dof.IsEmpty()) return false;\n\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETractionLoad::LoadVector(FEGlobalVector& R)\n{\n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tFETractionLoad* load = this;\n\tsurf.LoadVector(R, m_dof, m_blinear, [=](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\n\t\t// evaluate traction at this material point\n\t\tvec3d t = m_traction(pt)*m_scale;\n\t\tif (load->m_bshellb) t = -t;\n\n\t\tdouble J = (pt.dxr ^ pt.dxs).norm();\n\n\t\tdouble H_u = dof_a.shape;\n\n\t\tval[0] = H_u * t.x*J;\n\t\tval[1] = H_u * t.y*J;\n\t\tval[2] = H_u * t.z*J;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FETractionLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Nothing to do here.\n\t// TODO: I think if the linear flag is false, I do need to evaluate a stiffness.\n}\n"
  },
  {
    "path": "FEBioMech/FETractionLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! FETractionLoad is a surface that has a constant (deformation independant)\n//! traction force on it.\n//!\nclass FETractionLoad : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n\tFETractionLoad(FEModel* pfem);\n\n\t//! Set the surface to apply the load to\n\tvoid SetSurface(FESurface* ps) override;\n\n\t// initialization\n\tbool Init() override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\tdouble\t\t\tm_scale;\t//!< scale factor for traction\n\tFEParamVec3\t\tm_traction;\t//!< vector traction\n\tbool\t\t\tm_bshellb;\n\tbool\t\t\tm_blinear;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETractionRobinBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETractionRobinBC.h\"\n#include \"FEBioMech.h\"\n#include <FECore/FEFacetSet.h>\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FETractionRobinBC, FESurfaceLoad)\n    ADD_PARAMETER(m_epsk   , \"spring_eps\")->setUnits(\"P/L\");\n    ADD_PARAMETER(m_epsc   , \"dashpot_eps\")->setUnits(\"P.t/L\");\n\tADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFETractionRobinBC::FETractionRobinBC(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_epsk = 0.0;\n    m_epsc = 0.0;\n    m_bshellb = false;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FETractionRobinBC::SetSurface(FESurface* ps)\n{\n\tFESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n// initialization\nbool FETractionRobinBC::Init()\n{\n\tFESurface& surf = GetSurface();\n\tsurf.SetShellBottom(m_bshellb);\n\n\t// get the degrees of freedom\n\tm_dof.Clear();\n\tif (m_bshellb == false)\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n\telse\n\t{\n\t\tm_dof.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t}\n\tif (m_dof.IsEmpty()) return false;\n\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETractionRobinBC::Update()\n{\n    FETimeInfo tp = GetFEModel()->GetTime();\n    FESurface& surf = GetSurface();\n    for (int i=0; i<surf.Elements(); ++i) {\n        FESurfaceElement& el = surf.Element(i);\n        int nint = el.GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint* mp = el.GetMaterialPoint(n);\n            mp->Update(tp);\n        }\n    }\n    surf.Update(tp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETractionRobinBC::LoadVector(FEGlobalVector& R)\n{\n    if (GetFEModel()->GetTime().currentTime == 0) return;\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n\t// evaluate the integral\n\tFESurface& surf = GetSurface();\n\tsurf.LoadVector(R, m_dof, false, [=](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, std::vector<double>& val) {\n\n\t\t// evaluate traction at this material point\n        double epsk = m_epsk(pt);\n        double epsc = m_epsc(pt);\n        vec3d u = pt.m_rt - pt.m_r0;\n        vec3d udot = (dt > 0) ? (pt.m_rt - pt.m_rp)/dt : vec3d(0,0,0);\n\n        vec3d t = -u*epsk - udot*epsc;\n\t\tif (m_bshellb) t = -t;\n\n\t\tdouble J = (pt.dxr ^ pt.dxs).norm();\n\n\t\tdouble Na = dof_a.shape;\n\n\t\tval[0] = Na * t.x*J;\n\t\tval[1] = Na * t.y*J;\n\t\tval[2] = Na * t.z*J;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FETractionRobinBC::StiffnessMatrix(FELinearSystem& LS)\n{\n    if (GetFEModel()->GetTime().currentTime == 0) return;\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // evaluate the integral\n    FESurface& surf = GetSurface();\n    surf.LoadStiffness(LS, m_dof, m_dof, [&](FESurfaceMaterialPoint& pt, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& kab) {\n        \n        // evaluate pressure at this material point\n        vec3d n = (pt.dxr ^ pt.dxs);\n        double J = n.unit();\n        \n        mat3dd I(1);\n        \n        double epsk = m_epsk(pt);\n        double epsc = m_epsc(pt);\n        vec3d u = pt.m_rt - pt.m_r0;\n        vec3d udot = (dt > 0) ? (pt.m_rt - pt.m_rp)/dt : vec3d(0,0,0);\n        \n        vec3d t = -u*epsk - udot*epsc;\n        if (m_bshellb) t = -t;\n\n        double Na  = dof_a.shape;\n        double dNar = dof_a.shape_deriv_r;\n        double dNas = dof_a.shape_deriv_s;\n        \n        double Nb  = dof_b.shape;\n        double dNbr = dof_b.shape_deriv_r;\n        double dNbs = dof_b.shape_deriv_s;\n        \n        mat3d Kab = (J*Na*Nb)*(epsk+epsc/dt)*mat3dd(1)\n        +((t & n)*mat3da(pt.dxr*dNbs - pt.dxs*dNbr)*(Na*J));\n        kab.set(0, 0, Kab);\n    });\n}\n"
  },
  {
    "path": "FEBioMech/FETractionRobinBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! FETractionRobinBC is a surface that has a traction proportional to the surface displacement and velocity\n//!\nclass FETractionRobinBC : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n    FETractionRobinBC(FEModel* pfem);\n\n\t//! Set the surface to apply the load to\n\tvoid SetSurface(FESurface* ps) override;\n\n\t// initialization\n\tbool Init() override;\n\n    //! update\n    void Update() override;\n    \npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! calculate stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n    FEParamDouble   m_epsk;     //!< displacement penalty\n    FEParamDouble   m_epsc;     //!< velocity penalty\n\tbool\t\t\tm_bshellb;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETransIsoMREstrada.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETransIsoMREstrada.h\"\n\n//////////////////////////////////////////////////////////////////////\n// FETransIsoMREstrada\n//////////////////////////////////////////////////////////////////////\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETransIsoMREstrada, FEUncoupledMaterial)\n    ADD_PARAMETER(c1, \"c1\");\n    ADD_PARAMETER(c2, \"c2\");\n    ADD_PARAMETER(m_fib.m_c3, \"c3\");\n    ADD_PARAMETER(m_fib.m_c4, \"c4\");\n    ADD_PARAMETER(m_fib.m_c5, \"c5\");\n    ADD_PARAMETER(m_fib.m_lam1, \"lam_max\");\n    \n    ADD_PROPERTY(m_fiber, \"fiber\");\n    ADD_PROPERTY(m_ac, \"active_contraction\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETransIsoMREstrada::FETransIsoMREstrada(FEModel* pfem) : FEUncoupledMaterial(pfem), m_fib(pfem)\n{\n    m_ac = nullptr;\n    m_fib.SetParent(this);\n    m_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPointData* FETransIsoMREstrada::CreateMaterialPointData() \n{\n    // create the elastic solid material point\n    FEMaterialPointData* ep = new FEElasticMaterialPoint;\n    \n    // create the material point from the active contraction material\n    if (m_ac) ep->Append(m_ac->CreateMaterialPointData());\n\n\treturn ep;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FETransIsoMREstrada::DevStress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    \n    // calculate deviatoric left Cauchy-Green tensor\n    mat3ds B = pt.DevLeftCauchyGreen();\n    \n    // calculate square of B\n    mat3ds B2 = B.sqr();\n\n    // material axes\n    mat3d Q = GetLocalCS(mp);\n\n    // get the fiber vector in local coordinates\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d a0 = Q * fiber;\n    \n    // Invariants of B (= invariants of C)\n    // Note that these are the invariants of Btilde, not of B!\n    double I1 = B.tr();\n    \n    // --- TODO: put strain energy derivatives here ---\n    // Wi = dW/dIi\n    double W1 = c1(mp);\n    double W2 = c2(mp);\n    // ------------------------------------------------\n    \n    // calculate T = F*dW/dC*Ft\n    mat3ds T = B*(W1 + W2*I1) - B2*W2;\n    \n    // calculate stress s = pI + 2/J * dev(T)\n    mat3ds s = T.dev()*(2.0/J);\n    \n    // calculate the passive fiber stress\n    mat3ds fs = m_fib.DevFiberStress(mp, a0);\n    \n    // calculate the active fiber stress (if provided)\n    if (m_ac) fs += m_ac->ActiveStress(mp, a0);\n    \n    return s + fs;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric tangent\ntens4ds FETransIsoMREstrada::DevTangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // deformation gradient\n    double J = pt.m_J;\n    double Ji = 1.0/J;\n    \n    // calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n    mat3ds B = pt.DevLeftCauchyGreen();\n    \n    // calculate square of B\n    mat3ds B2 = B.sqr();\n    \n    // Invariants of B (= invariants of C)\n    double I1 = B.tr();\n    double I2 = 0.5*(I1*I1 - B2.tr());\n    \n    // --- TODO: put strain energy derivatives here ---\n    // Wi = dW/dIi\n    double W1, W2;\n    W1 = c1(mp);\n    W2 = c2(mp);\n    // ------------------------------------\n    \n    // calculate dWdC:C\n    double WC = W1*I1 + 2*W2*I2;\n    \n    // calculate C:d2WdCdC:C\n    double CWWC = 2*I2*W2;\n    \n    mat3dd I(1);    // Identity\n    tens4ds IxI = dyad1s(I);\n    tens4ds I4  = dyad4s(I);\n    tens4ds BxB = dyad1s(B);\n    tens4ds B4  = dyad4s(B);\n\n    // material axes\n    mat3d Q = GetLocalCS(mp);\n\n    // get the fiber vector in local coordinates\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d a0 = Q * fiber;\n    \n    // deviatoric cauchy-stress\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n    // d2W/dCdC:C\n    mat3ds WCCxC = B*(W2*I1) - B2*W2;\n    \n    tens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n    tens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n    \n    // add the passive fiber stiffness\n    c += m_fib.DevFiberTangent(mp, a0);\n    \n    // add the active fiber stiffness\n    if (m_ac) c += m_ac->ActiveStiffness(mp, a0);\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FETransIsoMREstrada::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate deviatoric left Cauchy-Green tensor\n    mat3ds B = pt.DevLeftCauchyGreen();\n    \n    // calculate square of B\n    mat3ds B2 = B.sqr();\n    \n    // material axes\n    mat3d Q = GetLocalCS(mp);\n\n    // get the fiber vector in local coordinates\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d a0 = Q * fiber;\n\n    // Invariants of B (= invariants of C)\n    // Note that these are the invariants of Btilde, not of B!\n    double I1 = B.tr();\n    double I2 = 0.5*(I1*I1 - B2.tr());\n    \n    // calculate sed\n    double sed = c1(mp)*(I1-3) + c2(mp)*(I2-3);\n    \n    // add the fiber sed\n    sed += m_fib.DevFiberStrainEnergyDensity(mp, a0);\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n// update force-velocity material point\nvoid FETransIsoMREstrada::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& timeInfo)\n{\n    // material axes\n    mat3d Q = GetLocalCS(mp);\n\n    // get the fiber vector in local coordinates\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d a0 = Q * fiber;\n\n    if (m_ac) m_ac->UpdateSpecializedMaterialPoints(mp, timeInfo, a0);\n}\n"
  },
  {
    "path": "FEBioMech/FETransIsoMREstrada.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n#include \"FEActiveContractionMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Transversely Isotropic Mooney-Rivlin-Estrada material based on doi: 10.1115/1.4044030\n\n//! This material has an isotopric Mooney-Rivlin basis and single preferred\n//! fiber direction.\n\nclass FETransIsoMREstrada: public FEUncoupledMaterial\n{\npublic:\n    enum { MAX_TERMS = 3 };\n    \npublic:\n    FETransIsoMREstrada(FEModel* pfem);\n    \npublic:\n    FEParamDouble   c1;     //!< Mooney-Rivlin coefficient C1\n    FEParamDouble   c2;     //!< Mooney-Rivlin coefficient C2\n    \npublic:\n    //! calculate deviatoric stress at material point\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! calculate deviatoric tangent stiffness at material point\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! calculate deviatoric strain energy density at material point\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! create material point data\n    FEMaterialPointData* CreateMaterialPointData() override;\n\n    // update force-velocity material point\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \nprotected:\n    FEFiberExpLinearUC              m_fib;\n    FEActiveContractionMaterial*    m_ac;\n    FEVec3dValuator* m_fiber;\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETransIsoMooneyRivlin.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETransIsoMooneyRivlin.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETransIsoMooneyRivlin, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1          , \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2          , \"c2\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_c3  , \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_c4  , \"c4\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_fib.m_c5  , \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_lam1, \"lam_max\")->setUnits(UNIT_NONE);\n\t\n\tADD_PROPERTY(m_fiber, \"fiber\")->SetDefaultType(\"vector\");\n\n\tADD_PROPERTY(m_ac, \"active_contraction\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FETransIsoMooneyRivlin\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nFETransIsoMooneyRivlin::FETransIsoMooneyRivlin(FEModel* pfem) : FEUncoupledMaterial(pfem), m_fib(pfem)\n{\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\n\tm_ac = nullptr;\n\tm_fib.SetParent(this);\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPointData* FETransIsoMooneyRivlin::CreateMaterialPointData() \n{\n    // create the elastic solid material point\n    FEMaterialPointData* ep = new FEElasticMaterialPoint;\n    \n    // create the material point from the active contraction material\n    if (m_ac) ep->Append(m_ac->CreateMaterialPointData());\n\n\treturn ep;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FETransIsoMooneyRivlin::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\n\t// material axes\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1 = m_c1(mp);\n\tdouble W2 = m_c2(mp);\n\t// ------------------------------------------------\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\t// calculate stress s = pI + 2/J * dev(T) \n\tmat3ds s = T.dev()*(2.0/J);\n\n\t// calculate the passive fiber stress\n\tmat3ds fs = m_fib.DevFiberStress(mp, a0);\n\n\t// calculate the active fiber stress (if provided)\n\tif (m_ac) fs += m_ac->ActiveStress(mp, a0);\n\n\treturn s + fs;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric tangent\ntens4ds FETransIsoMooneyRivlin::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// fiber vector\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2;\n\tW1 = m_c1(mp);\n\tW2 = m_c2(mp);\n\t// ------------------------------------\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = 2*I2*W2;\n\n\tmat3dd I(1);\t// Identity\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// deviatoric cauchy-stress\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(W2*I1) - B2*W2;\n\n\ttens4ds cw = (BxB - B4)*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\t// add the passive fiber stiffness\n\tc += m_fib.DevFiberTangent(mp, a0);\n\n\t// add the active fiber stiffness\n\tif (m_ac) c += m_ac->ActiveStiffness(mp, a0);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FETransIsoMooneyRivlin::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// fiber vector\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n    \n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n    \n\t// calculate sed\n\tdouble sed = m_c1(mp)*(I1-3) + m_c2(mp)*(I2-3);\n    \n\t// add the fiber sed\n\tsed += m_fib.DevFiberStrainEnergyDensity(mp, a0);\n    \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n// update force-velocity material point\nvoid FETransIsoMooneyRivlin::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& timeInfo)\n{\n\t// fiber vector\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n    // get the material fiber axis\n    if (m_ac) m_ac->UpdateSpecializedMaterialPoints(mp, timeInfo, a0);\n}\n"
  },
  {
    "path": "FEBioMech/FETransIsoMooneyRivlin.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n#include \"FEActiveContractionMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Transversely Isotropic Mooney-Rivlin material\n\n//! This material has an isotopric Mooney-Rivlin basis and single preferred\n//! fiber direction.\n\nclass FETransIsoMooneyRivlin: public FEUncoupledMaterial\n{\npublic:\n\tFETransIsoMooneyRivlin(FEModel* pfem);\n\npublic:\n\tFEParamDouble   m_c1;\t\t\t//!< Mooney-Rivlin coefficient C1\n    FEParamDouble   m_c2;\t\t\t//!< Mooney-Rivlin coefficient C2\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n\n    //! create material point data\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // update force-velocity material point\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \nprotected:\n\tFEFiberExpLinearUC\t\t       m_fib;\n    FEActiveContractionMaterial*   m_ac;\n\tFEVec3dValuator* m_fiber;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETransIsoVerondaWestmann.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETransIsoVerondaWestmann.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETransIsoVerondaWestmann, FEUncoupledMaterial)\n\tADD_PARAMETER(      m_c1  , \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(      m_c2  , \"c2\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_fib.m_c3  , \"c3\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_c4  , \"c4\")->setUnits(UNIT_NONE);\n\tADD_PARAMETER(m_fib.m_c5  , \"c5\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_fib.m_lam1, \"lam_max\");\n\t\n\tADD_PROPERTY(m_fiber, \"fiber\");\n\tADD_PROPERTY(m_ac, \"active_contraction\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// FETransIsoVerondaWestmann\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nFETransIsoVerondaWestmann::FETransIsoVerondaWestmann(FEModel* pfem) : FEUncoupledMaterial(pfem), m_fib(pfem)\n{\n\tm_c1 = 0.0;\n\tm_c2 = 0.0;\n\n\tm_ac = nullptr;\n\tm_fib.SetParent(this);\n\tm_fiber = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPointData* FETransIsoVerondaWestmann::CreateMaterialPointData() \n{\n    // create the elastic solid material point\n    FEMaterialPointData* ep = new FEElasticMaterialPoint;\n    \n    // create the material point from the active contraction material\n    if (m_ac) ep->Append(m_ac->CreateMaterialPointData());\n\n\treturn ep;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FETransIsoVerondaWestmann::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// material axes\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\n    double c1 = m_c1(mp);\n    double c2 = m_c2(mp);\n    \n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1 = c1*c2*exp(c2*(I1-3));\n\tdouble W2 = -0.5*c1*c2;\n\t// ------------------------------------------------\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\t// calculate stress s = pI + 2/J * dev(T) \n\tmat3ds s = T.dev()*(2.0/J);\n\n\t// add the passive fiber stress\n\ts += m_fib.DevFiberStress(mp, a0);\n\n    // calculate the active fiber stress (if provided)\n    if (m_ac) s += m_ac->ActiveStress(mp, a0);\n    \n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric tangent\ntens4ds FETransIsoVerondaWestmann::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n    double c1 = m_c1(mp);\n    double c2 = m_c2(mp);\n    \n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2, W11;\n\tW1 = c1*c2*exp(c2*(I1-3));\n\tW2 = -0.5*c1*c2;\n\tW11 = c2*W1;\n\t// ---\n\n\t// material axes\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = W11*I1*I1+2*I2*W2;\n\n\t// deviatoric cauchy-stress\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\tmat3dd I(1);\t// Identity\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(I1*(W11 + W2)) - B2*W2;\n\n\ttens4ds cw = BxB*((W11 + W2)*4.0*Ji) - B4*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n    // add the active fiber stiffness\n    if (m_ac) c += m_ac->ActiveStiffness(mp, a0);\n    \n\treturn c + m_fib.DevFiberTangent(mp, a0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FETransIsoVerondaWestmann::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// material axes\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n    \n    double c1 = m_c1(mp);\n    double c2 = m_c2(mp);\n    \n\t// calculate sed\n    double sed = c1*(exp(c2*(I1-3))-1) - c1*c2*(I2-3)/2;\n    \n\t// add the fiber strain energy density\n\tsed += m_fib.DevFiberStrainEnergyDensity(mp, a0);\n    \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\n// update force-velocity material point\nvoid FETransIsoVerondaWestmann::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& timeInfo)\n{\n\t// material axes\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the fiber vector in local coordinates\n\tvec3d fiber = m_fiber->unitVector(mp);\n\n\t// convert to global coordinates\n\tvec3d a0 = Q * fiber;\n\n    if (m_ac) m_ac->UpdateSpecializedMaterialPoints(mp, timeInfo, a0);\n}\n"
  },
  {
    "path": "FEBioMech/FETransIsoVerondaWestmann.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n#include \"FEActiveContractionMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Transversely Isotropic Veronda-Westmann material\n\n//! This material has an isotopric Veronda-Westmann basis and single preferred\n//! fiber direction.\n\nclass FETransIsoVerondaWestmann : public FEUncoupledMaterial\n{\npublic:\n\tFETransIsoVerondaWestmann(FEModel* pfem);\n\npublic:\n    FEParamDouble   m_c1;\t//!< Veronda-Westmann coefficient C1\n    FEParamDouble   m_c2;\t//!< Veronda-Westmann coefficient C2\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! create material point data\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    // update force-velocity material point\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n\nprotected:\n    FEFiberExpLinearUC\t\t       m_fib;\n    FEActiveContractionMaterial*    m_ac;\n\tFEVec3dValuator* m_fiber;\n\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FETrussMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETrussMaterial.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FETrussMaterial, FEMaterial)\n\tADD_PARAMETER(m_rho, FE_RANGE_GREATER(0.0), \"density\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETrussMaterial::FETrussMaterial(FEModel* pfem) : FEMaterial(pfem) \n{\n\tm_rho = 1.0;\n\n\tAddDomainParameter(new FETrussStress());\n}\n\n//-----------------------------------------------------------------------------\nFETrussMaterial::~FETrussMaterial() \n{\n}\n\n//-----------------------------------------------------------------------------\n//! material density\ndouble FETrussMaterial::Density(FEMaterialPoint& mp)\n{\n\treturn m_rho(mp);\n}\n\n//=============================================================================\n// define the material parameters\nBEGIN_FECORE_CLASS(FELinearTrussMaterial, FETrussMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_v, FE_RANGE_CLOSED(-1, 0.5), \"v\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFELinearTrussMaterial::FELinearTrussMaterial(FEModel* fem) : FETrussMaterial(fem)\n{\n\tm_E = 0.0;\n\tm_v = 0.5;\n}\n\n//-----------------------------------------------------------------------------\n// Note that this function returns the Kirchhoff stress!\ndouble FELinearTrussMaterial::Stress(FEMaterialPoint &mp)\n{\n\tFETrussMaterialPoint& pt = *mp.ExtractData<FETrussMaterialPoint>();\n\tdouble E = m_E(mp);\n\treturn E*log(pt.m_lam);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELinearTrussMaterial::Tangent(FEMaterialPoint &mp)\n{\n\tdouble E = m_E(mp);\n\treturn E;\n}\n\nFETrussStress::FETrussStress() : FEDomainParameter(\"stress\") {}\n\nFEParamValue FETrussStress::value(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\treturn pt.m_s;\n}\n"
  },
  {
    "path": "FEBioMech/FETrussMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FEElasticMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n// Material point class for truss materials\nclass FETrussMaterialPoint : public FEElasticMaterialPoint\n{\npublic:\n\tFEMaterialPointData* Copy()\n\t{\n\t\tFETrussMaterialPoint* pt = new FETrussMaterialPoint(*this);\n\t\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\t\treturn pt;\n\t}\n\n\tvoid Serialize(DumpStream& ar)\n\t{\n\t\tFEElasticMaterialPoint::Serialize(ar);\n\t\tar & m_lam & m_tau;\n\t}\n\n\tvoid Init()\n\t{\n\t\tFEElasticMaterialPoint::Init();\n\t\tm_lam = 1;\n\t\tm_tau = 0;\n\t}\n\npublic:\n\tdouble\tm_lam;\t// stretch\n\tdouble\tm_tau;\t// Kirchoff stress\n};\n\n//-----------------------------------------------------------------------------\n// Base class for truss element materials\nclass FEBIOMECH_API FETrussMaterial : public FEMaterial\n{\npublic:\n\tFETrussMaterial(FEModel* pfem);\n\t~FETrussMaterial();\n\npublic:\n\tFEParamDouble m_rho;\t// density\n\npublic:\n\t//! calculate Kirchhoff stress of truss\n\tvirtual double Stress(FEMaterialPoint& pt) = 0;\n\n\t//! calculate elastic tangent\n\tvirtual double Tangent(FEMaterialPoint& pt) = 0;\n\n\t//! create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override { return new FETrussMaterialPoint; }\n\n\t//! material density\n\tdouble Density(FEMaterialPoint& mp);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FETrussMaterial);\n};\n\n//-----------------------------------------------------------------------------\nclass FELinearTrussMaterial : public FETrussMaterial\n{\npublic:\n\tFELinearTrussMaterial(FEModel* fem);\n\n\t//! calculate Kirchhoff stress of truss\n\tdouble Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate elastic tangent\n\tdouble Tangent(FEMaterialPoint& pt) override;\n\npublic:\n\tFEParamDouble\tm_E;\t// Elastic modulus\n\tdouble\tm_v;\t// Poisson's ratio\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEBIOMECH_API FETrussStress : public FEDomainParameter\n{\npublic:\n\tFETrussStress();\n\tFEParamValue value(FEMaterialPoint& mp) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEUDGHexDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUDGHexDomain.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEUDGHexDomain, FEElasticSolidDomain)\n\tADD_PARAMETER(m_hg, \"hg\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEUDGHexDomain::FEUDGHexDomain(FEModel* pfem) : FEElasticSolidDomain(pfem)\n{ \n\tm_hg = 1.0; \n}\n\n//-----------------------------------------------------------------------------\n//! Set the hourglass parameter\nvoid FEUDGHexDomain::SetHourGlassParameter(double hg)\n{\n\tm_hg = hg;\n}\n\n//-----------------------------------------------------------------------------\nbool FEUDGHexDomain::Create(int nelems, FE_Element_Spec spec)\n{\n\t// make sure these solid, hex8 elements are requested\n\tif (spec.eclass == FE_Element_Class::FE_ELEM_INVALID_CLASS) spec.eclass = FE_Element_Class::FE_ELEM_SOLID;\n\telse if (spec.eclass != FE_Element_Class::FE_ELEM_SOLID) return false;\n\n\tif (spec.eshape == FE_Element_Shape::FE_ELEM_INVALID_SHAPE) spec.eshape = FE_Element_Shape::ET_HEX8;\n\telse if (spec.eshape != FE_Element_Shape::ET_HEX8) return false;\n\n\t// we need to enforce HEX8G1 integration rule\n\tspec.etype = FE_HEX8G1;\n\n\t// now allocate the domain\n\treturn FESolidDomain::Create(nelems, spec);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUDGHexDomain::InternalForces(FEGlobalVector& R)\n{\n\tint NE = (int)m_Elem.size();\n#pragma omp parallel for\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the element force vector and initialize it to zero\n\t\tint ndof = 3*el.Nodes();\n\n\t\t// element force vector\n\t\tvector<double> fe;\n\t\tfe.assign(ndof, 0);\n\n\t\t// calculate internal force vector\n\t\tUDGInternalForces(el, fe);\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble element 'fe'-vector into global R vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for enhanced strain\n//! solid elements.\n\nvoid FEUDGHexDomain::UDGInternalForces(FESolidElement& el, vector<double>& fe)\n{\n\t// get the stress data\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\tmat3ds& s = pt.m_s;\n\n\t// calculate the average cartesian derivatives\n\tdouble GX[8], GY[8], GZ[8];\n\tAvgCartDerivs(el, GX, GY, GZ);\n\n\t// calculate average deformation gradient Fbar\n\tmat3d Fb;\n\tAvgDefGrad(el, Fb, GX, GY, GZ);\n\n\t// calculate the transposed inverse of Fbar\n\tmat3d Fti = Fb.transinv();\n\n\t// calculate current element volume\n\tdouble ve = HexVolume(el, 1);\n\n\t// current averaged shape derivatives\n\tdouble Gx, Gy, Gz;\n\n\t// calculate the internal force\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\tGx = Fti(0,0)*GX[i]+Fti(0,1)*GY[i]+Fti(0,2)*GZ[i];\n\t\tGy = Fti(1,0)*GX[i]+Fti(1,1)*GY[i]+Fti(1,2)*GZ[i];\n\t\tGz = Fti(2,0)*GX[i]+Fti(2,1)*GY[i]+Fti(2,2)*GZ[i];\n\n\t\tfe[3*i  ] -= ve*(Gx*s.xx() + Gy*s.xy() + Gz*s.xz());\n\t\tfe[3*i+1] -= ve*(Gx*s.xy() + Gy*s.yy() + Gz*s.yz());\n\t\tfe[3*i+2] -= ve*(Gx*s.xz() + Gy*s.yz() + Gz*s.zz());\n\t}\n\n\t// add hourglass forces\n\tUDGHourglassForces(el, fe);\n}\n\n\n//-----------------------------------------------------------------------------\n//! calculates the hourglass forces\n\nvoid FEUDGHexDomain::UDGHourglassForces(FESolidElement &el, vector<double> &fe)\n{\n\tint i;\n\n\tconst double h4[8] = { 1,-1, 1,-1, 1,-1, 1,-1 };\n\tconst double h5[8] = { 1,-1,-1, 1,-1, 1, 1,-1 };\n\tconst double h6[8] = { 1, 1,-1,-1,-1,-1, 1, 1 };\n\tconst double h7[8] = {-1, 1,-1, 1, 1,-1, 1,-1 };\n\n\tint neln = el.Nodes();\n\n\tvec3d r0[8], rt[8];\n\tfor (i=0; i<neln; ++i)\n\t{\n\t\tr0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\t\trt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n\t}\n\n\tdouble x4 = 0, x5 = 0, x6 = 0, x7 = 0;\n\tdouble y4 = 0, y5 = 0, y6 = 0, y7 = 0;\n\tdouble z4 = 0, z5 = 0, z6 = 0, z7 = 0;\n\n\tdouble X4 = 0, X5 = 0, X6 = 0, X7 = 0;\n\tdouble Y4 = 0, Y5 = 0, Y6 = 0, Y7 = 0;\n\tdouble Z4 = 0, Z5 = 0, Z6 = 0, Z7 = 0;\n\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tX4 += h4[i]*r0[i].x; Y4 += h4[i]*r0[i].y; Z4 += h4[i]*r0[i].z;\n\t\tX5 += h5[i]*r0[i].x; Y5 += h5[i]*r0[i].y; Z5 += h5[i]*r0[i].z;\n\t\tX6 += h6[i]*r0[i].x; Y6 += h6[i]*r0[i].y; Z6 += h6[i]*r0[i].z;\n\t\tX7 += h7[i]*r0[i].x; Y7 += h7[i]*r0[i].y; Z7 += h7[i]*r0[i].z;\n\n\t\tx4 += h4[i]*rt[i].x; y4 += h4[i]*rt[i].y; z4 += h4[i]*rt[i].z;\n\t\tx5 += h5[i]*rt[i].x; y5 += h5[i]*rt[i].y; z5 += h5[i]*rt[i].z;\n\t\tx6 += h6[i]*rt[i].x; y6 += h6[i]*rt[i].y; z6 += h6[i]*rt[i].z;\n\t\tx7 += h7[i]*rt[i].x; y7 += h7[i]*rt[i].y; z7 += h7[i]*rt[i].z;\n\t}\n\n\tdouble GX[8], GY[8], GZ[8];\n\tAvgCartDerivs(el, GX, GY, GZ);\n\n\tmat3d F;\n\tAvgDefGrad(el, F, GX, GY, GZ);\n\n\tdouble u4 = 0, u5 = 0, u6 = 0, u7 = 0;\n\tdouble v4 = 0, v5 = 0, v6 = 0, v7 = 0;\n\tdouble w4 = 0, w5 = 0, w6 = 0, w7 = 0;\n\n\tu4 = x4 - (F[0][0]*X4 + F[0][1]*Y4 + F[0][2]*Z4);\n\tv4 = y4 - (F[1][0]*X4 + F[1][1]*Y4 + F[1][2]*Z4);\n\tw4 = z4 - (F[2][0]*X4 + F[2][1]*Y4 + F[2][2]*Z4);\n\n\tu5 = x5 - (F[0][0]*X5 + F[0][1]*Y5 + F[0][2]*Z5);\n\tv5 = y5 - (F[1][0]*X5 + F[1][1]*Y5 + F[1][2]*Z5);\n\tw5 = z5 - (F[2][0]*X5 + F[2][1]*Y5 + F[2][2]*Z5);\n\n\tu6 = x6 - (F[0][0]*X6 + F[0][1]*Y6 + F[0][2]*Z6);\n\tv6 = y6 - (F[1][0]*X6 + F[1][1]*Y6 + F[1][2]*Z6);\n\tw6 = z6 - (F[2][0]*X6 + F[2][1]*Y6 + F[2][2]*Z6);\n\n\tu7 = x7 - (F[0][0]*X7 + F[0][1]*Y7 + F[0][2]*Z7);\n\tv7 = y7 - (F[1][0]*X7 + F[1][1]*Y7 + F[1][2]*Z7);\n\tw7 = z7 - (F[2][0]*X7 + F[2][1]*Y7 + F[2][2]*Z7);\n\n\tdouble g4[8] = {0}, g5[8] = {0}, g6[8] = {0}, g7[8] = {0};\n\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tg4[i] = h4[i] - (GX[i]*X4 + GY[i]*Y4 + GZ[i]*Z4);\n\t\tg5[i] = h5[i] - (GX[i]*X5 + GY[i]*Y5 + GZ[i]*Z5);\n\t\tg6[i] = h6[i] - (GX[i]*X6 + GY[i]*Y6 + GZ[i]*Z6);\n\t\tg7[i] = h7[i] - (GX[i]*X7 + GY[i]*Y7 + GZ[i]*Z7);\n\t}\n\n\t// calculate hourglass forces\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tfe[3*i  ] -= m_hg*(g4[i]*u4 + g5[i]*u5 + g6[i]*u6 + g7[i]*u7);\n\t\tfe[3*i+1] -= m_hg*(g4[i]*v4 + g5[i]*v5 + g6[i]*v6 + g7[i]*v7);\n\t\tfe[3*i+2] -= m_hg*(g4[i]*w4 + g5[i]*w5 + g6[i]*w6 + g7[i]*w7);\n\t}\n}\n\nvoid FEUDGHexDomain::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tvector<int> lm;\n\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\n\t\t// create the element's stiffness matrix\n\t\tint ndof = 3*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate material stiffness\n\t\tUDGMaterialStiffness(el, ke);\n\n\t\t// calculate geometrical stiffness\n\t\tUDGGeometricalStiffness(el, ke);\n\n\t\t// add hourglass stiffness\n\t\tUDGHourglassStiffness(fem, el, ke);\n\n\t\t// assign symmetic parts\n\t\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t\t// grabs elements from the upper diagonal matrix?\n\t\tfor (int i=0; i<ndof; ++i)\n\t\t\tfor (int j=i+1; j<ndof; ++j)\n\t\t\t\tke[j][i] = ke[i][j];\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the hourglass stiffness for UDG hex elements\n\nvoid FEUDGHexDomain::UDGHourglassStiffness(FEModel& fem, FESolidElement& el, matrix& ke)\n{\n\tint i, j;\n\n\tconst double h4[8] = { 1,-1, 1,-1, 1,-1, 1,-1 };\n\tconst double h5[8] = { 1,-1,-1, 1,-1, 1, 1,-1 };\n\tconst double h6[8] = { 1, 1,-1,-1,-1,-1, 1, 1 };\n\tconst double h7[8] = {-1, 1,-1, 1, 1,-1, 1,-1 };\n\n\tint neln = el.Nodes();\n\n\tvec3d r0[8], rt[8];\n\tfor (i=0; i<neln; ++i)\n\t{\n\t\tr0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\t\trt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n\t}\n\n\tdouble x4 = 0, x5 = 0, x6 = 0, x7 = 0;\n\tdouble y4 = 0, y5 = 0, y6 = 0, y7 = 0;\n\tdouble z4 = 0, z5 = 0, z6 = 0, z7 = 0;\n\n\tdouble X4 = 0, X5 = 0, X6 = 0, X7 = 0;\n\tdouble Y4 = 0, Y5 = 0, Y6 = 0, Y7 = 0;\n\tdouble Z4 = 0, Z5 = 0, Z6 = 0, Z7 = 0;\n\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tX4 += h4[i]*r0[i].x; Y4 += h4[i]*r0[i].y; Z4 += h4[i]*r0[i].z;\n\t\tX5 += h5[i]*r0[i].x; Y5 += h5[i]*r0[i].y; Z5 += h5[i]*r0[i].z;\n\t\tX6 += h6[i]*r0[i].x; Y6 += h6[i]*r0[i].y; Z6 += h6[i]*r0[i].z;\n\t\tX7 += h7[i]*r0[i].x; Y7 += h7[i]*r0[i].y; Z7 += h7[i]*r0[i].z;\n\n\t\tx4 += h4[i]*rt[i].x; y4 += h4[i]*rt[i].y; z4 += h4[i]*rt[i].z;\n\t\tx5 += h5[i]*rt[i].x; y5 += h5[i]*rt[i].y; z5 += h5[i]*rt[i].z;\n\t\tx6 += h6[i]*rt[i].x; y6 += h6[i]*rt[i].y; z6 += h6[i]*rt[i].z;\n\t\tx7 += h7[i]*rt[i].x; y7 += h7[i]*rt[i].y; z7 += h7[i]*rt[i].z;\n\t}\n\n\tFEMesh& mesh = *GetMesh();\n\n\tdouble GX[8], GY[8], GZ[8];\n\tAvgCartDerivs(el, GX, GY, GZ);\n\n\tdouble g4[8] = {0}, g5[8] = {0}, g6[8] = {0}, g7[8] = {0};\n\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tg4[i] = h4[i] - (GX[i]*X4 + GY[i]*Y4 + GZ[i]*Z4);\n\t\tg5[i] = h5[i] - (GX[i]*X5 + GY[i]*Y5 + GZ[i]*Z5);\n\t\tg6[i] = h6[i] - (GX[i]*X6 + GY[i]*Y6 + GZ[i]*Z6);\n\t\tg7[i] = h7[i] - (GX[i]*X7 + GY[i]*Y7 + GZ[i]*Z7);\n\t}\n\n\t// calculate hourglass stiffness\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tfor (j=i; j<8; ++j)\n\t\t{\n\t\t\tdouble kab = m_hg*(g4[i]*g4[j] + g5[i]*g5[j] + g6[i]*g6[j] + g7[i]*g7[j]);\n\n\t\t\tke[3*i  ][3*j  ] += kab;\n\t\t\tke[3*i+1][3*j+1] += kab;\n\t\t\tke[3*i+2][3*j+2] += kab;\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n\nvoid FEUDGHexDomain::UDGGeometricalStiffness(FESolidElement& el, matrix& ke)\n{\n\tint i, j;\n\n\t// stiffness component for the initial stress component of stiffness matrix\n\tdouble kab;\n\n\t// get the material point data\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t// element's Cauchy-stress tensor at gauss point n\n\t// s is the voight vector\n\tmat3ds& s = pt.m_s;\n\n\tFEMesh& mesh = *GetMesh();\n\n\t// calculate the average cartesian derivatives\n\tdouble GX[8], GY[8], GZ[8];\n\tAvgCartDerivs(el, GX, GY, GZ);\n\n\t// calculate average deformation gradient Fbar\n\tmat3d Fb;\n\tAvgDefGrad(el, Fb, GX, GY, GZ);\n\n\t// calculate the transposed inverse of Fbar\n\tmat3d Fti = Fb.transinv();\n\n\t// calculate current element volume\n\tdouble ve = HexVolume(el, 1);\n\n\t// current averaged shape derivatives\n\tdouble Gx[8], Gy[8], Gz[8];\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tGx[i] = Fti(0,0)*GX[i]+Fti(0,1)*GY[i]+Fti(0,2)*GZ[i];\n\t\tGy[i] = Fti(1,0)*GX[i]+Fti(1,1)*GY[i]+Fti(1,2)*GZ[i];\n\t\tGz[i] = Fti(2,0)*GX[i]+Fti(2,1)*GY[i]+Fti(2,2)*GZ[i];\n\t}\n\n\tfor (i=0; i<8; ++i)\n\t\tfor (j=i; j<8; ++j)\n\t\t{\n\t\t\tkab = (Gx[i]*(s.xx()*Gx[j]+s.xy()*Gy[j]+s.xz()*Gz[j]) +\n\t\t\t\t   Gy[i]*(s.xy()*Gx[j]+s.yy()*Gy[j]+s.yz()*Gz[j]) + \n\t\t\t\t   Gz[i]*(s.xz()*Gx[j]+s.yz()*Gy[j]+s.zz()*Gz[j]))*ve;\n\n\t\t\tke[3*i  ][3*j  ] += kab;\n\t\t\tke[3*i+1][3*j+1] += kab;\n\t\t\tke[3*i+2][3*j+2] += kab;\n\t\t}\n}\n\n\n//-----------------------------------------------------------------------------\n\nvoid FEUDGHexDomain::UDGMaterialStiffness(FESolidElement &el, matrix &ke)\n{\n\t// make sure we have the right element type\n\tassert(el.Type() == FE_HEX8G1);\n\n\tint i, i3, j, j3;\n\n\t// Get the current element's data\n\tconst int neln = el.Nodes();\n\n\tdouble Gxi, Gyi, Gzi;\n\tdouble Gxj, Gyj, Gzj;\n\n\t// The 'D' matrix\n\tdouble D[6][6] = {0};\t// The 'D' matrix\n\n\t// The 'D*BL' matrix\n\tdouble DBL[6][3];\n\n\tFEMesh& mesh = *GetMesh();\n\n\t// calculate the average cartesian derivatives\n\tdouble GX[8], GY[8], GZ[8];\n\tAvgCartDerivs(el, GX, GY, GZ);\n\n\t// calculate average deformation gradient Fbar\n\tmat3d Fb;\n\tAvgDefGrad(el, Fb, GX, GY, GZ);\n\n\t// calculate the transposed inverse of Fbar\n\tmat3d Fti = Fb.transinv();\n\n\t// calculate current element volume\n\tdouble ve = HexVolume(el, 1);\n\n\t// current averaged shape derivatives\n\tdouble Gx[8], Gy[8], Gz[8];\n\tfor (i=0; i<8; ++i)\n\t{\n\t\tGx[i] = Fti(0,0)*GX[i]+Fti(0,1)*GY[i]+Fti(0,2)*GZ[i];\n\t\tGy[i] = Fti(1,0)*GX[i]+Fti(1,1)*GY[i]+Fti(1,2)*GZ[i];\n\t\tGz[i] = Fti(2,0)*GX[i]+Fti(2,1)*GY[i]+Fti(2,2)*GZ[i];\n\t}\n\n\t// setup the material point\n\t// NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t// get the 'D' matrix\n\ttens4ds C = m_pMat->Tangent(mp);\n\tC.extract(D);\n\n\t// we only calculate the upper triangular part\n\t// since ke is symmetric. The other part is\n\t// determined below using this symmetry.\n\tfor (i=0, i3=0; i<neln; ++i, i3 += 3)\n\t{\n\t\tGxi = Gx[i];\n\t\tGyi = Gy[i];\n\t\tGzi = Gz[i];\n\n\t\tfor (j=i, j3 = i3; j<neln; ++j, j3 += 3)\n\t\t{\n\t\t\tGxj = Gx[j];\n\t\t\tGyj = Gy[j];\n\t\t\tGzj = Gz[j];\n\n\t\t\t// calculate D*BL matrices\n\t\t\tDBL[0][0] = (D[0][0]*Gxj+D[0][3]*Gyj+D[0][5]*Gzj);\n\t\t\tDBL[0][1] = (D[0][1]*Gyj+D[0][3]*Gxj+D[0][4]*Gzj);\n\t\t\tDBL[0][2] = (D[0][2]*Gzj+D[0][4]*Gyj+D[0][5]*Gxj);\n\n\t\t\tDBL[1][0] = (D[1][0]*Gxj+D[1][3]*Gyj+D[1][5]*Gzj);\n\t\t\tDBL[1][1] = (D[1][1]*Gyj+D[1][3]*Gxj+D[1][4]*Gzj);\n\t\t\tDBL[1][2] = (D[1][2]*Gzj+D[1][4]*Gyj+D[1][5]*Gxj);\n\n\t\t\tDBL[2][0] = (D[2][0]*Gxj+D[2][3]*Gyj+D[2][5]*Gzj);\n\t\t\tDBL[2][1] = (D[2][1]*Gyj+D[2][3]*Gxj+D[2][4]*Gzj);\n\t\t\tDBL[2][2] = (D[2][2]*Gzj+D[2][4]*Gyj+D[2][5]*Gxj);\n\n\t\t\tDBL[3][0] = (D[3][0]*Gxj+D[3][3]*Gyj+D[3][5]*Gzj);\n\t\t\tDBL[3][1] = (D[3][1]*Gyj+D[3][3]*Gxj+D[3][4]*Gzj);\n\t\t\tDBL[3][2] = (D[3][2]*Gzj+D[3][4]*Gyj+D[3][5]*Gxj);\n\n\t\t\tDBL[4][0] = (D[4][0]*Gxj+D[4][3]*Gyj+D[4][5]*Gzj);\n\t\t\tDBL[4][1] = (D[4][1]*Gyj+D[4][3]*Gxj+D[4][4]*Gzj);\n\t\t\tDBL[4][2] = (D[4][2]*Gzj+D[4][4]*Gyj+D[4][5]*Gxj);\n\n\t\t\tDBL[5][0] = (D[5][0]*Gxj+D[5][3]*Gyj+D[5][5]*Gzj);\n\t\t\tDBL[5][1] = (D[5][1]*Gyj+D[5][3]*Gxj+D[5][4]*Gzj);\n\t\t\tDBL[5][2] = (D[5][2]*Gzj+D[5][4]*Gyj+D[5][5]*Gxj);\n\n\t\t\tke[i3  ][j3  ] += (Gxi*DBL[0][0] + Gyi*DBL[3][0] + Gzi*DBL[5][0] )*ve;\n\t\t\tke[i3  ][j3+1] += (Gxi*DBL[0][1] + Gyi*DBL[3][1] + Gzi*DBL[5][1] )*ve;\n\t\t\tke[i3  ][j3+2] += (Gxi*DBL[0][2] + Gyi*DBL[3][2] + Gzi*DBL[5][2] )*ve;\n\n\t\t\tke[i3+1][j3  ] += (Gyi*DBL[1][0] + Gxi*DBL[3][0] + Gzi*DBL[4][0] )*ve;\n\t\t\tke[i3+1][j3+1] += (Gyi*DBL[1][1] + Gxi*DBL[3][1] + Gzi*DBL[4][1] )*ve;\n\t\t\tke[i3+1][j3+2] += (Gyi*DBL[1][2] + Gxi*DBL[3][2] + Gzi*DBL[4][2] )*ve;\n\n\t\t\tke[i3+2][j3  ] += (Gzi*DBL[2][0] + Gyi*DBL[4][0] + Gxi*DBL[5][0] )*ve;\n\t\t\tke[i3+2][j3+1] += (Gzi*DBL[2][1] + Gyi*DBL[4][1] + Gxi*DBL[5][1] )*ve;\n\t\t\tke[i3+2][j3+2] += (Gzi*DBL[2][2] + Gyi*DBL[4][2] + Gxi*DBL[5][2] )*ve;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUDGHexDomain::Update(const FETimeInfo& tp)\n{\n\tint nint, neln;\n\tdouble* gw;\n\tvec3d r0[8], rt[8];\n\n\tfor (int i=0; i<(int) m_Elem.size(); ++i)\n\t{\n\t\t// get the solid element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the number of integration points\n\t\tnint = el.GaussPoints();\n\n\t\t// number of nodes\n\t\tneln = el.Nodes();\n\n\t\t// nodal coordinates\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tr0[j] = m_pMesh->Node(el.m_node[j]).m_r0;\n\t\t\trt[j] = m_pMesh->Node(el.m_node[j]).m_rt;\n\t\t}\n\n\t\t// get the integration weights\n\t\tgw = el.GaussWeights();\n\n\t\t// for the enhanced strain hex we need a slightly different procedure\n\t\t// for calculating the element's stress. For this element, the stress\n\t\t// is evaluated using an average deformation gradient.\n\n\t\t// get the material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(0);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// material point coordinates\n\t\t// TODO: I'm not entirly happy with this solution\n\t\t//\t\t since the material point coordinates are used by most materials.\n\t\tmp.m_r0 = el.Evaluate(r0, 0);\n\t\tmp.m_rt = el.Evaluate(rt, 0);\n\n\t\t// get the average cartesian derivatives\n\t\tdouble GX[8], GY[8], GZ[8];\n\t\tAvgCartDerivs(el, GX, GY, GZ);\n\n\t\t// get the average deformation gradient and determinant\n\t\tAvgDefGrad(el, pt.m_F, GX, GY, GZ);\n\t\tpt.m_J = pt.m_F.det();\n\n\t\t// calculate the stress at this material point\n\t\tpt.m_s = m_pMat->Stress(mp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the average Cartesian derivatives\n//! Note that we assume that the GX, GY and GX contain the averaged \n//! Cartesian derivatives\n\nvoid FEUDGHexDomain::AvgDefGrad(FESolidElement& el, mat3d& F, double GX[8], double GY[8], double GZ[8])\n{\n\tvec3d rt[8];\n\tfor (int j=0; j<8; ++j) rt[j] = m_pMesh->Node(el.m_node[j]).m_rt;\n\n\tF.zero();\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\tF[0][0] += rt[i].x*GX[i];\n\t\tF[0][1] += rt[i].x*GY[i];\n\t\tF[0][2] += rt[i].x*GZ[i];\n\n\t\tF[1][0] += rt[i].y*GX[i];\n\t\tF[1][1] += rt[i].y*GY[i];\n\t\tF[1][2] += rt[i].y*GZ[i];\n\n\t\tF[2][0] += rt[i].z*GX[i];\n\t\tF[2][1] += rt[i].z*GY[i];\n\t\tF[2][2] += rt[i].z*GZ[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the average Cartesian derivatives\n\nvoid FEUDGHexDomain::AvgCartDerivs(FESolidElement& el, double GX[8], double GY[8], double GZ[8], int nstate)\n{\n\t// get the nodal coordinates\n\tint neln = el.Nodes();\n\tvec3d r[8];\n\tif (nstate == 0)\n\t{\n\t\tfor (int i=0; i<neln; ++i) r[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\t}\n\telse\n\t{\n\t\tfor (int i=0; i<neln; ++i) r[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n\t}\n\n\tdouble x1 = r[0].x, y1 = r[0].y, z1 = r[0].z;\n\tdouble x2 = r[1].x, y2 = r[1].y, z2 = r[1].z;\n\tdouble x3 = r[2].x, y3 = r[2].y, z3 = r[2].z;\n\tdouble x4 = r[3].x, y4 = r[3].y, z4 = r[3].z;\n\tdouble x5 = r[4].x, y5 = r[4].y, z5 = r[4].z;\n\tdouble x6 = r[5].x, y6 = r[5].y, z6 = r[5].z;\n\tdouble x7 = r[6].x, y7 = r[6].y, z7 = r[6].z;\n\tdouble x8 = r[7].x, y8 = r[7].y, z8 = r[7].z;\n\n\tconst double f12 = 1.0/12.0;\n\n\t// set up the B-matrix\n\t// we use the G arrays to store the B-matrix\n\tGX[0] = f12*(y2*((z6-z3)-(z4-z5))+y3*(z2-z4)+y4*((z3-z8)-(z5-z2))+y5*((z8-z6)-(z2-z4))+y6*(z5-z2)+y8*(z4-z5));\n\tGY[0] = f12*(z2*((x6-x3)-(x4-x5))+z3*(x2-x4)+z4*((x3-x8)-(x5-x2))+z5*((x8-x6)-(x2-x4))+z6*(x5-x2)+z8*(x4-x5));\n\tGZ[0] = f12*(x2*((y6-y3)-(y4-y5))+x3*(y2-y4)+x4*((y3-y8)-(y5-y2))+x5*((y8-y6)-(y2-y4))+x6*(y5-y2)+x8*(y4-y5));\n\n\tGX[1] = f12*(y3*((z7-z4)-(z1-z6))+y4*(z3-z1)+y1*((z4-z5)-(z6-z3))+y6*((z5-z7)-(z3-z1))+y7*(z6-z3)+y5*(z1-z6));\n\tGY[1] = f12*(z3*((x7-x4)-(x1-x6))+z4*(x3-x1)+z1*((x4-x5)-(x6-x3))+z6*((x5-x7)-(x3-x1))+z7*(x6-x3)+z5*(x1-x6));\n\tGZ[1] = f12*(x3*((y7-y4)-(y1-y6))+x4*(y3-y1)+x1*((y4-y5)-(y6-y3))+x6*((y5-y7)-(y3-y1))+x7*(y6-y3)+x5*(y1-y6));\n\n\tGX[2] = f12*(y4*((z8-z1)-(z2-z7))+y1*(z4-z2)+y2*((z1-z6)-(z7-z4))+y7*((z6-z8)-(z4-z2))+y8*(z7-z4)+y6*(z2-z7));\n\tGY[2] = f12*(z4*((x8-x1)-(x2-x7))+z1*(x4-x2)+z2*((x1-x6)-(x7-x4))+z7*((x6-x8)-(x4-x2))+z8*(x7-x4)+z6*(x2-x7));\n\tGZ[2] = f12*(x4*((y8-y1)-(y2-y7))+x1*(y4-y2)+x2*((y1-y6)-(y7-y4))+x7*((y6-y8)-(y4-y2))+x8*(y7-y4)+x6*(y2-y7));\n\n\tGX[3] = f12*(y1*((z5-z2)-(z3-z8))+y2*(z1-z3)+y3*((z2-z7)-(z8-z1))+y8*((z7-z5)-(z1-z3))+y5*(z8-z1)+y7*(z3-z8));\n\tGY[3] = f12*(z1*((x5-x2)-(x3-x8))+z2*(x1-x3)+z3*((x2-x7)-(x8-x1))+z8*((x7-x5)-(x1-x3))+z5*(x8-x1)+z7*(x3-x8));\n\tGZ[3] = f12*(x1*((y5-y2)-(y3-y8))+x2*(y1-y3)+x3*((y2-y7)-(y8-y1))+x8*((y7-y5)-(y1-y3))+x5*(y8-y1)+x7*(y3-y8));\n\n\tGX[4] = f12*(y8*((z4-z7)-(z6-z1))+y7*(z8-z6)+y6*((z7-z2)-(z1-z8))+y1*((z2-z4)-(z8-z6))+y4*(z1-z8)+y2*(z6-z1));\n\tGY[4] = f12*(z8*((x4-x7)-(x6-x1))+z7*(x8-x6)+z6*((x7-x2)-(x1-x8))+z1*((x2-x4)-(x8-x6))+z4*(x1-x8)+z2*(x6-x1));\n\tGZ[4] = f12*(x8*((y4-y7)-(y6-y1))+x7*(y8-y6)+x6*((y7-y2)-(y1-y8))+x1*((y2-y4)-(y8-y6))+x4*(y1-y8)+x2*(y6-y1));\n\n\tGX[5] = f12*(y5*((z1-z8)-(z7-z2))+y8*(z5-z7)+y7*((z8-z3)-(z2-z5))+y2*((z3-z1)-(z5-z7))+y1*(z2-z5)+y3*(z7-z2));\n\tGY[5] = f12*(z5*((x1-x8)-(x7-x2))+z8*(x5-x7)+z7*((x8-x3)-(x2-x5))+z2*((x3-x1)-(x5-x7))+z1*(x2-x5)+z3*(x7-x2));\n\tGZ[5] = f12*(x5*((y1-y8)-(y7-y2))+x8*(y5-y7)+x7*((y8-y3)-(y2-y5))+x2*((y3-y1)-(y5-y7))+x1*(y2-y5)+x3*(y7-y2));\n\n\tGX[6] = f12*(y6*((z2-z5)-(z8-z3))+y5*(z6-z8)+y8*((z5-z4)-(z3-z6))+y3*((z4-z2)-(z6-z8))+y2*(z3-z6)+y4*(z8-z3));\n\tGY[6] = f12*(z6*((x2-x5)-(x8-x3))+z5*(x6-x8)+z8*((x5-x4)-(x3-x6))+z3*((x4-x2)-(x6-x8))+z2*(x3-x6)+z4*(x8-x3));\n\tGZ[6] = f12*(x6*((y2-y5)-(y8-y3))+x5*(y6-y8)+x8*((y5-y4)-(y3-y6))+x3*((y4-y2)-(y6-y8))+x2*(y3-y6)+x4*(y8-y3));\n\n\tGX[7] = f12*(y7*((z3-z6)-(z5-z4))+y6*(z7-z5)+y5*((z6-z1)-(z4-z7))+y4*((z1-z3)-(z7-z5))+y3*(z4-z7)+y1*(z5-z4));\n\tGY[7] = f12*(z7*((x3-x6)-(x5-x4))+z6*(x7-x5)+z5*((x6-x1)-(x4-x7))+z4*((x1-x3)-(x7-x5))+z3*(x4-x7)+z1*(x5-x4));\n\tGZ[7] = f12*(x7*((y3-y6)-(y5-y4))+x6*(y7-y5)+x5*((y6-y1)-(y4-y7))+x4*((y1-y3)-(y7-y5))+x3*(y4-y7)+x1*(y5-y4));\n\n\t// calculate the volume\n\tdouble Vi = 1./(x1*GX[0]+x2*GX[1]+x3*GX[2]+x4*GX[3]+x5*GX[4]+x6*GX[5]+x7*GX[6]+x8*GX[7]);\n\n\t// divide the B-matrix by the volume\n\tGX[0] *= Vi; GY[0] *= Vi; GZ[0] *= Vi;\n\tGX[1] *= Vi; GY[1] *= Vi; GZ[1] *= Vi;\n\tGX[2] *= Vi; GY[2] *= Vi; GZ[2] *= Vi;\n\tGX[3] *= Vi; GY[3] *= Vi; GZ[3] *= Vi;\n\tGX[4] *= Vi; GY[4] *= Vi; GZ[4] *= Vi;\n\tGX[5] *= Vi; GY[5] *= Vi; GZ[5] *= Vi;\n\tGX[6] *= Vi; GY[6] *= Vi; GZ[6] *= Vi;\n\tGX[7] *= Vi; GY[7] *= Vi; GZ[7] *= Vi;\n}\n//-----------------------------------------------------------------------------\n//! Calculates the exact volume of a hexahedral element\n\ndouble FEUDGHexDomain::HexVolume(FESolidElement& el, int state)\n{\n\t// let's make sure this is indeed a hex element\n//\tassert(el.Type() == FE_HEX8G8);\n\n\tint neln = el.Nodes();\n\tvec3d r[8];\n\tif (state == 0)\n\t{\n\t\tfor (int i=0; i<neln; ++i) r[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\t}\n\telse\n\t{\n\t\tfor (int i=0; i<neln; ++i) r[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n\t}\n\n\t// get the nodal coordinates\n\tdouble x1 = r[0].x, y1 = r[0].y, z1 = r[0].z;\n\tdouble x2 = r[1].x, y2 = r[1].y, z2 = r[1].z;\n\tdouble x3 = r[2].x, y3 = r[2].y, z3 = r[2].z;\n\tdouble x4 = r[3].x, y4 = r[3].y, z4 = r[3].z;\n\tdouble x5 = r[4].x, y5 = r[4].y, z5 = r[4].z;\n\tdouble x6 = r[5].x, y6 = r[5].y, z6 = r[5].z;\n\tdouble x7 = r[6].x, y7 = r[6].y, z7 = r[6].z;\n\tdouble x8 = r[7].x, y8 = r[7].y, z8 = r[7].z;\n\n\t// set up the B-matrix\n\tdouble B1[8];\n//\tdouble B2[8];\n//\tdouble B3[8];\n\n\tconst double f12 = 1.0/12.0;\n\n\tB1[0] = f12*(y2*((z6-z3)-(z4-z5))+y3*(z2-z4)+y4*((z3-z8)-(z5-z2))+y5*((z8-z6)-(z2-z4))+y6*(z5-z2)+y8*(z4-z5));\n//\tB2[0] = f12*(z2*((x6-x3)-(x4-x5))+z3*(x2-x4)+z4*((x3-x8)-(x5-x2))+z5*((x8-x6)-(x2-x4))+z6*(x5-x2)+z8*(x4-x5));\n//\tB3[0] = f12*(x2*((y6-y3)-(y4-y5))+x3*(y2-y4)+x4*((y3-y8)-(y5-y2))+x5*((y8-y6)-(y2-y4))+x6*(y5-y2)+x8*(y4-y5));\n\n\tB1[1] = f12*(y3*((z7-z4)-(z1-z6))+y4*(z3-z1)+y1*((z4-z5)-(z6-z3))+y6*((z5-z7)-(z3-z1))+y7*(z6-z3)+y5*(z1-z6));\n//\tB2[1] = f12*(z3*((x7-x4)-(x1-x6))+z4*(x3-x1)+z1*((x4-x5)-(x6-x3))+z6*((x5-x7)-(x3-x1))+z7*(x6-x3)+z5*(x1-x6));\n//\tB3[1] = f12*(x3*((y7-y4)-(y1-y6))+x4*(y3-y1)+x1*((y4-y5)-(y6-y3))+x6*((y5-y7)-(y3-y1))+x7*(y6-y3)+x5*(y1-y6));\n\n\tB1[2] = f12*(y4*((z8-z1)-(z2-z7))+y1*(z4-z2)+y2*((z1-z6)-(z7-z4))+y7*((z6-z8)-(z4-z2))+y8*(z7-z4)+y6*(z2-z7));\n//\tB2[2] = f12*(z4*((x8-x1)-(x2-x7))+z1*(x4-x2)+z2*((x1-x6)-(x7-x4))+z7*((x6-x8)-(x4-x2))+z8*(x7-x4)+z6*(x2-x7));\n//\tB3[2] = f12*(x4*((y8-y1)-(y2-y7))+x1*(y4-y2)+x2*((y1-y6)-(y7-y4))+x7*((y6-y8)-(y4-y2))+x8*(y7-y4)+x6*(y2-y7));\n\n\tB1[3] = f12*(y1*((z5-z2)-(z3-z8))+y2*(z1-z3)+y3*((z2-z7)-(z8-z1))+y8*((z7-z5)-(z1-z3))+y5*(z8-z1)+y7*(z3-z8));\n//\tB2[3] = f12*(z1*((x5-x2)-(x3-x8))+z2*(x1-x3)+z3*((x2-x7)-(x8-x1))+z8*((x7-x5)-(x1-x3))+z5*(x8-x1)+z7*(x3-x8));\n//\tB3[3] = f12*(x1*((y5-y2)-(y3-y8))+x2*(y1-y3)+x3*((y2-y7)-(y8-y1))+x8*((y7-y5)-(y1-y3))+x5*(y8-y1)+x7*(y3-y8));\n\n\tB1[4] = f12*(y8*((z4-z7)-(z6-z1))+y7*(z8-z6)+y6*((z7-z2)-(z1-z8))+y1*((z2-z4)-(z8-z6))+y4*(z1-z8)+y2*(z6-z1));\n//\tB2[4] = f12*(z8*((x4-x7)-(x6-x1))+z7*(x8-x6)+z6*((x7-x2)-(x1-x8))+z1*((x2-x4)-(x8-x6))+z4*(x1-x8)+z2*(x6-x1));\n//\tB3[4] = f12*(x8*((y4-y7)-(y6-y1))+x7*(y8-y6)+x6*((y7-y2)-(y1-y8))+x1*((y2-y4)-(y8-y6))+x4*(y1-y8)+x2*(y6-y1));\n\n\tB1[5] = f12*(y5*((z1-z8)-(z7-z2))+y8*(z5-z7)+y7*((z8-z3)-(z2-z5))+y2*((z3-z1)-(z5-z7))+y1*(z2-z5)+y3*(z7-z2));\n//\tB2[5] = f12*(z5*((x1-x8)-(x7-x2))+z8*(x5-x7)+z7*((x8-x3)-(x2-x5))+z2*((x3-x1)-(x5-x7))+z1*(x2-x5)+z3*(x7-x2));\n//\tB3[5] = f12*(x5*((y1-y8)-(y7-y2))+x8*(y5-y7)+x7*((y8-y3)-(y2-y5))+x2*((y3-y1)-(y5-y7))+x1*(y2-y5)+x3*(y7-y2));\n\n\tB1[6] = f12*(y6*((z2-z5)-(z8-z3))+y5*(z6-z8)+y8*((z5-z4)-(z3-z6))+y3*((z4-z2)-(z6-z8))+y2*(z3-z6)+y4*(z8-z3));\n//\tB2[6] = f12*(z6*((x2-x5)-(x8-x3))+z5*(x6-x8)+z8*((x5-x4)-(x3-x6))+z3*((x4-x2)-(x6-x8))+z2*(x3-x6)+z4*(x8-x3));\n//\tB3[6] = f12*(x6*((y2-y5)-(y8-y3))+x5*(y6-y8)+x8*((y5-y4)-(y3-y6))+x3*((y4-y2)-(y6-y8))+x2*(y3-y6)+x4*(y8-y3));\n\n\tB1[7] = f12*(y7*((z3-z6)-(z5-z4))+y6*(z7-z5)+y5*((z6-z1)-(z4-z7))+y4*((z1-z3)-(z7-z5))+y3*(z4-z7)+y1*(z5-z4));\n//\tB2[7] = f12*(z7*((x3-x6)-(x5-x4))+z6*(x7-x5)+z5*((x6-x1)-(x4-x7))+z4*((x1-x3)-(x7-x5))+z3*(x4-x7)+z1*(x5-x4));\n//\tB3[7] = f12*(x7*((y3-y6)-(y5-y4))+x6*(y7-y5)+x5*((y6-y1)-(y4-y7))+x4*((y1-y3)-(y7-y5))+x3*(y4-y7)+x1*(y5-y4));\n\n\t// calculate the volume V= xi*B1[i] = yi*B2[i] = zi*B3[i] (sum over i)\n\treturn (x1*B1[0]+x2*B1[1]+x3*B1[2]+x4*B1[3]+x5*B1[4]+x6*B1[5]+x7*B1[6]+x8*B1[7]);\n}\n"
  },
  {
    "path": "FEBioMech/FEUDGHexDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! domain class for uniform-deformation-gradient hex elements (UDG)\nclass FEBIOMECH_API FEUDGHexDomain : public FEElasticSolidDomain\n{\npublic:\n\t//! constructor\n\tFEUDGHexDomain(FEModel* pfem);\n\n\t//! Set the hourglass parameter\n\tvoid SetHourGlassParameter(double hg);\n\n\t//! allocate elements\n\tbool Create(int nelems, FE_Element_Spec spec) override;\n\npublic:\n\t//! calculates the residual\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t// update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\nprotected: // element residual contributions\n\t//! Calculates the internal stress vector for enhanced strain hex elements\n\tvoid UDGInternalForces(FESolidElement& el, vector<double>& fe);\n\n\t//! calculates hourglass forces for the UDG element\n\tvoid UDGHourglassForces(FESolidElement& el, vector<double>& fe);\n\nprotected: // element stiffness contributions\n\t//! hourglass stiffness for UDG hex elements\n\tvoid UDGHourglassStiffness(FEModel& fem, FESolidElement& el, matrix& ke);\n\n\t//! geometrical stiffness for UDG hex elements\n\tvoid UDGGeometricalStiffness(FESolidElement& el, matrix& ke);\n\n\t//! material stiffness for UDG hex elements\n\tvoid UDGMaterialStiffness(FESolidElement& el, matrix& ke);\n\nprotected:\n\tvoid AvgCartDerivs(FESolidElement& el, double GX[8], double GY[8], double GZ[8], int state = 0);\n\tvoid AvgDefGrad(FESolidElement& el, mat3d& F, double GX[8], double GY[8], double GZ[8]);\n\tdouble HexVolume(FESolidElement& el, int state = 0);\n\npublic:\n\tdouble\tm_hg;\t//!< hourglass parameter\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUT4Domain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUT4Domain.h\"\n#include \"FECore/FEMesh.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n\n//-----------------------------------------------------------------------------\n// This function converts the Cauchy stress to a 2nd-PK stress\nmat3ds cauchy_to_pk2(mat3ds& s, mat3d& F)\n{\n\tmat3d Fi = F.inverse();\n\tmat3d Fit = Fi.transpose();\n\tdouble J = F.det();\n\n\tmat3d S = (Fi*s*Fit)*J;\n\treturn S.sym();\n}\n\n//-----------------------------------------------------------------------------\n// This function converts the 2nd-PK stress to a Cauchy stress\nmat3ds pk2_to_cauchy(mat3ds& S, mat3d& F)\n{\n\tmat3d Ft = F.transpose();\n\tdouble Ji = 1/F.det();\n\n\tmat3d s = (F*S*Ft)*Ji;\n\treturn s.sym();\n}\n\n//-----------------------------------------------------------------------------\n// This function converts the spatial tangent to the material tangent\ntens4ds spatial_to_material(tens4ds& c, mat3d& F)\n{\n\tmat3d Fi = F.inverse();\n\tdouble J = F.det();\n\n\tdouble C[6][6] = {0};\n\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t{\n\t\t\t\t\tC[0][0] += J*Fi[0][i]*Fi[0][j]*Fi[0][k]*Fi[0][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][1] += J*Fi[0][i]*Fi[0][j]*Fi[1][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][2] += J*Fi[0][i]*Fi[0][j]*Fi[2][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][3] += J*Fi[0][i]*Fi[0][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][4] += J*Fi[0][i]*Fi[0][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[0][5] += J*Fi[0][i]*Fi[0][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[1][1] += J*Fi[1][i]*Fi[1][j]*Fi[1][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][2] += J*Fi[1][i]*Fi[1][j]*Fi[2][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][3] += J*Fi[1][i]*Fi[1][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][4] += J*Fi[1][i]*Fi[1][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[1][5] += J*Fi[1][i]*Fi[1][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[2][2] += J*Fi[2][i]*Fi[2][j]*Fi[2][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[2][3] += J*Fi[2][i]*Fi[2][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[2][4] += J*Fi[2][i]*Fi[2][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[2][5] += J*Fi[2][i]*Fi[2][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[3][3] += J*Fi[0][i]*Fi[1][j]*Fi[0][k]*Fi[1][l]*c(i,j,k,l);\n\t\t\t\t\tC[3][4] += J*Fi[0][i]*Fi[1][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[3][5] += J*Fi[0][i]*Fi[1][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[4][4] += J*Fi[1][i]*Fi[2][j]*Fi[1][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t\tC[4][5] += J*Fi[1][i]*Fi[2][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\n\t\t\t\t\tC[5][5] += J*Fi[0][i]*Fi[2][j]*Fi[0][k]*Fi[2][l]*c(i,j,k,l);\n\t\t\t\t}\n\n\treturn tens4ds(C);\n}\n\n//-----------------------------------------------------------------------------\n// This function converts the material tangent to the spatial tangent\ntens4ds material_to_spatial(tens4ds& C, mat3d& F)\n{\n\tdouble Ji = 1/F.det();\n\n\tdouble c[6][6] = {0};\n\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t{\n\t\t\t\t\tc[0][0] += Ji*F[0][i]*F[0][j]*F[0][k]*F[0][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][1] += Ji*F[0][i]*F[0][j]*F[1][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][2] += Ji*F[0][i]*F[0][j]*F[2][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][3] += Ji*F[0][i]*F[0][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][4] += Ji*F[0][i]*F[0][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[0][5] += Ji*F[0][i]*F[0][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[1][1] += Ji*F[1][i]*F[1][j]*F[1][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][2] += Ji*F[1][i]*F[1][j]*F[2][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][3] += Ji*F[1][i]*F[1][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][4] += Ji*F[1][i]*F[1][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[1][5] += Ji*F[1][i]*F[1][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[2][2] += Ji*F[2][i]*F[2][j]*F[2][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[2][3] += Ji*F[2][i]*F[2][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[2][4] += Ji*F[2][i]*F[2][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[2][5] += Ji*F[2][i]*F[2][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[3][3] += Ji*F[0][i]*F[1][j]*F[0][k]*F[1][l]*C(i,j,k,l);\n\t\t\t\t\tc[3][4] += Ji*F[0][i]*F[1][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[3][5] += Ji*F[0][i]*F[1][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[4][4] += Ji*F[1][i]*F[2][j]*F[1][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t\tc[4][5] += Ji*F[1][i]*F[2][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\n\t\t\t\t\tc[5][5] += Ji*F[0][i]*F[2][j]*F[0][k]*F[2][l]*C(i,j,k,l);\n\t\t\t\t}\n\n\treturn tens4ds(c);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUT4Domain::UT4NODE::Serialize(DumpStream& ar)\n{\n\tar & inode;\n\tar & Vi & vi;\n\tar & Fi & si;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEUT4Domain, FEElasticSolidDomain)\n\tADD_PARAMETER(m_alpha, \"alpha\");\n\tADD_PARAMETER(m_bdev, \"iso_stab\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor for the UT4Domain\nFEUT4Domain::FEUT4Domain(FEModel* pfem) : FEElasticSolidDomain(pfem)\n{\n\tm_alpha = 0.05;\n\tm_bdev = false;\n\n\tm_Be = 0;\n\tm_DB = 0;\n\tm_Ge = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUT4Domain::Serialize(DumpStream& ar)\n{\n\tFEElasticSolidDomain::Serialize(ar);\n\tar & m_alpha & m_bdev;\n\tar & m_tag;\n\tar & m_NODE;\n\tar & m_Ve0;\n\n\tif (ar.IsLoading())\n\t{\n\t\t// create the node-element list\n\t\tm_NEL.Create(*this);\n\t\t\n\t\t// find the largest valence\n\t\tint Nmax = m_NEL.MaxValence();\n\n\t\t// allocate buffers\n\t\tm_Ge = new double[Nmax*4][4][3];\n\t\tm_Be = new double[Nmax*4][6][3];\n\t\tm_DB = new double[Nmax*4][6][3];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEUT4Domain::~FEUT4Domain()\n{\n\tif (m_DB) delete [] m_DB;\n\tif (m_Be) delete [] m_Be;\n\tif (m_Ge) delete [] m_Ge;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUT4Domain::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_NDOFS = fedofs.GetTotalDOFS();\n\n\t// we'll need the node-element list\n\tFENodeElemList& NEL = GetNodeElemList();\n\tassert(NEL.Size() > 0);\n\n\tvector<int> LM, elm;\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tint NE = NEL.Valence(i);\n\t\tif (NE > 0)\n\t\t{\n\t\t\tLM.assign(NE*4*MAX_NDOFS, -1);\n\t\t\tFEElement** ppe = NEL.ElementList(i);\n\t\t\tfor (int n=0; n<NE; ++n)\n\t\t\t{\n\t\t\t\tUnpackLM(*ppe[n], elm);\n\t\t\t\tfor (int j=0; j<(int)elm.size(); ++j) LM[n*4*MAX_NDOFS + j] = elm[j];\n\t\t\t}\n\t\t\tM.build_add(LM);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Set UT4 parameters\nvoid FEUT4Domain::SetUT4Parameters(double alpha, bool bdev)\n{\n\tm_alpha = alpha;\n\tm_bdev = bdev;\n}\n\n//-----------------------------------------------------------------------------\n//! override Create so we can grab the ut4 parameters\nbool FEUT4Domain::Create(int nelems, FE_Element_Spec espec)\n{\n\t// NOTE: Commented this out, since during restart the espec is a dummy variable. \n\t// some sanity checks first\n//\tif (espec.eclass != FE_Element_Class::FE_ELEM_SOLID) return false;\n//\tif (espec.eshape != FE_Element_Shape::ET_TET4) return false;\n\n\tm_alpha = espec.m_ut4_alpha;\n\tm_bdev = espec.m_ut4_bdev;\n\tespec.m_but4 = true;\n\n\treturn FEElasticSolidDomain::Create(nelems, espec);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization for the UT4Domain.\n//! Note that we first initialize the base class before initializing the domain.\nbool FEUT4Domain::Init()\n{\n\t// first call the base class\n\tif (FEElasticSolidDomain::Init() == false) return false;\n\n\t// next, we need to identify all the nodes that belong to this domain\n\t// we do this by looping over all the elements and tagging the nodes\n\tint NN = m_pMesh->Nodes();\n\tm_tag.assign(NN, -1);\n\n\tint i, j;\n\tint N = (int)m_Node.size();\n\tfor (i=0; i<N; ++i) m_tag[m_Node[i]] = i;\n\n\t// allocate node structure\n\tm_NODE.resize(N);\n\n\t// initialize nodes\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tUT4NODE& n = m_NODE[i];\n\t\tn.inode = m_Node[i];\n\t\tn.Vi = n.vi = 0.0;\n\t\tn.si.zero();\n\t}\n\n\t// calculate the reference nodal volume\n\t// we do this here since this volume never changes\n\tint NE = Elements();\n\tm_Ve0.resize(NE);\n\tdouble Ve;\n\tvec3d r0[4];\n\tfor (i=0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the current element coordinates\n\t\tfor (j=0; j<4; ++j) r0[j] = m_pMesh->Node(el.m_node[j]).m_r0;\n\n\t\t// calculate the initial volume\n\t\tm_Ve0[i] = Ve = TetVolume(r0);\n\n\t\t// now assign one-quart to each node\n\t\tfor (int j=0; j<4; ++j) m_NODE[ m_tag[el.m_node[j]]].Vi += 0.25*Ve;\n\t}\n\n\t// create the node-element list\n\tm_NEL.Create(*this);\n\n\t// find the largest valence\n\tint Nmax = m_NEL.MaxValence();\n\n\t// allocate buffers\n\tm_Ge = new double[Nmax*4][4][3];\n\tm_Be = new double[Nmax*4][6][3];\n\tm_DB = new double[Nmax*4][6][3];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Update the nodal and element stresses\nvoid FEUT4Domain::Update(const FETimeInfo& tp)\n{\n\t// updating the element stresses is easy, since we only\n\t// need to call the base class\n\tFEElasticSolidDomain::Update(tp);\n\n\t// next we update the nodal data\n\tint i, j, NE = Elements();\n\tfor (i=0; i<(int) m_NODE.size(); ++i) { m_NODE[i].vi = 0; m_NODE[i].Fi.zero(); }\n\tdouble Ve, ve;\n\tmat3d Fe;\n\tvec3d rt[4];\n\tfor (i=0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// nodal coordinates\n\t\tfor (j=0; j<4; ++j) rt[j] = m_pMesh->Node(el.m_node[j]).m_rt;\n\n\t\t// calculate the volume\n\t\tVe = m_Ve0[i];\n\t\tve = TetVolume(rt);\n\n\t\t// calculate the deformation gradient\n\t\tdefgrad(el, Fe, 0);\n\n\t\t// now assign one-quart to each node\n\t\tfor (j=0; j<4; ++j) \n\t\t{\n\t\t\tUT4NODE& n = m_NODE[ m_tag[el.m_node[j]]];\n\t\t\tn.vi += 0.25*ve;\n\t\t\tn.Fi += Fe*(0.25*Ve / n.Vi);\n\t\t}\n\t}\n\n\t// create a material point\n\t// TODO: this will set the Q variable to a unit-matrix\n\t//\t\t in other words, we loose the material axis orientation\n\t//\t\t For now, I solve this by copying the Q parameter\n\t//       from the first element that the node connects to\n\tFEElasticMaterialPoint* ep = new FEElasticMaterialPoint();\n\tFEMaterialPoint mp(ep);\n\tmp.Init();\n\n\t// loop over all the nodes\n\tfor (i=0; i<(int) m_NODE.size(); ++i)\n\t{\n\t\tUT4NODE& node = m_NODE[i];\n\n\t\t// set the material point data\n\t\tmp.m_r0 = m_pMesh->Node(node.inode).m_r0;\n\t\tmp.m_rt = m_pMesh->Node(node.inode).m_rt;\n\n\t\tep->m_F = node.Fi;\n\t\tep->m_J = ep->m_F.det();\n\n\t\t// calculate the stress\n\t\tnode.si = m_pMat->Stress(mp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate volume for tets using the determinant rule\ndouble FEUT4Domain::TetVolume(vec3d* r)\n{\n\tdouble A[4][4];\n\tA[0][0] = r[0].x; A[0][1] = r[1].x; A[0][2] = r[2].x; A[0][3] = r[3].x;\n\tA[1][0] = r[0].y; A[1][1] = r[1].y; A[1][2] = r[2].y; A[1][3] = r[3].y;\n\tA[2][0] = r[0].z; A[2][1] = r[1].z; A[2][2] = r[2].z; A[2][3] = r[3].z;\n\tA[3][0] =    1.0; A[3][1] =    1.0; A[3][2] =    1.0; A[3][3] =    1.0;\n\n\tdouble V = 0;\n\tV += mat3d(A[0][1], A[0][2], A[0][3], A[1][1], A[1][2], A[1][3], A[2][1], A[2][2], A[2][3]).det();\n\tV -= mat3d(A[0][0], A[0][2], A[0][3], A[1][0], A[1][2], A[1][3], A[2][0], A[2][2], A[2][3]).det();\n\tV += mat3d(A[0][0], A[0][1], A[0][3], A[1][0], A[1][1], A[1][3], A[2][0], A[2][1], A[2][3]).det();\n\tV -= mat3d(A[0][0], A[0][1], A[0][2], A[1][0], A[1][1], A[1][2], A[2][0], A[2][1], A[2][2]).det();\n\n\treturn V/6.0;\n}\n\n//-----------------------------------------------------------------------------\n//! The residual is defined as the sum of the nodal residual and the\n//! element residual\nvoid FEUT4Domain::InternalForces(FEGlobalVector& R)\n{\n\t// Calculate the nodal contribution\n\tNodalInternalForces(R);\n\n\t// Calculate the element contribution\n\tElementInternalForces(R);\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the nodal contribution to the residual\nvoid FEUT4Domain::NodalInternalForces(FEGlobalVector& R)\n{\n\t// inverse jacobian matrix\n\tdouble Ji[3][3];\n\n\t// shape function derivatives\n\tdouble Gx[4], Gy[4], Gz[4];\n\n\t// B-matrix\n\tdouble Be[6][3] = {0};\n\n\tint i, j, n;\n\tdouble Ve;\n\tvector<int> elm;\n\tvector<int> LM(3);\n\tvector<int> en(1);\n\tvector<double> fe(3);\n\n\t// loop over all the nodes\n\tint NN = (int) m_NODE.size();\n\tfor (i=0; i<NN; ++i)\n\t{\n\t\tUT4NODE& node = m_NODE[i];\n\n\t\t// get the elements that belong to this list\n\t\tint NE = m_NEL.Valence(node.inode);\n\t\tFEElement** ppel = m_NEL.ElementList(node.inode);\n\t\tint* peli = m_NEL.ElementIndexList(node.inode);\n\n\t\t// get the nodal deformation gradient\n\t\tmat3d FI = node.Fi;\n\n\t\tmat3ds S;\n\t\tif (m_bdev)\n\t\t{\n\t\t\tS = node.si - node.si.dev()*m_alpha;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tS = node.si*(1 - m_alpha);\n\t\t}\n\n\t\t// convert the Cauchy stress to a PK2-stress\n\t\tS = cauchy_to_pk2(S, FI);\n\n\t\t// loop over all elements that belong to this node\n\t\tfor (n=0; n<NE; ++n)\n\t\t{\n\t\t\tFESolidElement& el = dynamic_cast<FESolidElement&>(*ppel[n]);\n\t\t\tUnpackLM(el, elm);\n\n\t\t\t// calculate element volume\n\t\t\t// TODO: we should store this somewhere instead of recalculating it\n\t\t\tVe = m_Ve0[peli[n]];\n\n\t\t\t// The '-' sign is so that the internal forces get subtracted from the residual (R = Fext - Fint)\n\t\t\tdouble w = -0.25* Ve;\n\n\t\t\t// calculate the jacobian\n\t\t\tinvjac0(el, Ji, 0);\n\n\t\t\t// get the shape function derivatives\n\t\t\tconst double* Gr, *Gs, *Gt;\n\t\t\tGr = el.Gr(0);\n\t\t\tGs = el.Gs(0);\n\t\t\tGt = el.Gt(0);\n\n\t\t\tfor (j=0; j<4; ++j)\n\t\t\t{\n\t\t\t\t// calculate global gradient of shape functions\n\t\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\t\tGx[j] = Ji[0][0]*Gr[j]+Ji[1][0]*Gs[j]+Ji[2][0]*Gt[j];\n\t\t\t\tGy[j] = Ji[0][1]*Gr[j]+Ji[1][1]*Gs[j]+Ji[2][1]*Gt[j];\n\t\t\t\tGz[j] = Ji[0][2]*Gr[j]+Ji[1][2]*Gs[j]+Ji[2][2]*Gt[j];\n\t\t\t}\n\n\t\t\t// get the element deformation gradient\n\t\t\tmat3d Fe = FI;\n\n\t\t\t// loop over element nodes\n\t\t\tfor (j=0; j<4; ++j)\n\t\t\t{\n\t\t\t\t// setup nonlinear element B-matrix\n\t\t\t\tBe[0][0] = Fe[0][0]*Gx[j]; Be[0][1] = Fe[1][0]*Gx[j]; Be[0][2] = Fe[2][0]*Gx[j];\n\t\t\t\tBe[1][0] = Fe[0][1]*Gy[j]; Be[1][1] = Fe[1][1]*Gy[j]; Be[1][2] = Fe[2][1]*Gy[j];\n\t\t\t\tBe[2][0] = Fe[0][2]*Gz[j]; Be[2][1] = Fe[1][2]*Gz[j]; Be[2][2] = Fe[2][2]*Gz[j];\n\t\t\t\tBe[3][0] = Fe[0][0]*Gy[j] + Fe[0][1]*Gx[j]; Be[3][1] = Fe[1][0]*Gy[j] + Fe[1][1]*Gx[j]; Be[3][2] = Fe[2][0]*Gy[j] + Fe[2][1]*Gx[j];\n\t\t\t\tBe[4][0] = Fe[0][1]*Gz[j] + Fe[0][2]*Gy[j]; Be[4][1] = Fe[1][1]*Gz[j] + Fe[1][2]*Gy[j]; Be[4][2] = Fe[2][1]*Gz[j] + Fe[2][2]*Gy[j];\n\t\t\t\tBe[5][0] = Fe[0][2]*Gx[j] + Fe[0][0]*Gz[j]; Be[5][1] = Fe[1][2]*Gx[j] + Fe[1][0]*Gz[j]; Be[5][2] = Fe[2][2]*Gx[j] + Fe[2][0]*Gz[j];\n\n\t\t\t\tLM[0] = elm[3*j  ];\n\t\t\t\tLM[1] = elm[3*j+1];\n\t\t\t\tLM[2] = elm[3*j+2];\n\n\t\t\t\ten[0] = el.m_node[j];\n\t\n\t\t\t\t// calculate nodal force\n\t\t\t\tfe[0] = w*(Be[0][0]*S.xx() + Be[1][0]*S.yy() + Be[2][0]*S.zz() + Be[3][0]*S.xy() + Be[4][0]*S.yz() + Be[5][0]*S.xz());\n\t\t\t\tfe[1] = w*(Be[0][1]*S.xx() + Be[1][1]*S.yy() + Be[2][1]*S.zz() + Be[3][1]*S.xy() + Be[4][1]*S.yz() + Be[5][1]*S.xz());\n\t\t\t\tfe[2] = w*(Be[0][2]*S.xx() + Be[1][2]*S.yy() + Be[2][2]*S.zz() + Be[3][2]*S.xy() + Be[4][2]*S.yz() + Be[5][2]*S.xz());\n\n\t\t\t\t// assemble in global residual\n\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the element contribution to the residual\nvoid FEUT4Domain::ElementInternalForces(FEGlobalVector& R)\n{\n\t// element force vector\n\tvector<double> fe;\n\n\tvector<int> lm;\n\n\tint NE = (int)m_Elem.size();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the element force vector and initialize it to zero\n\t\tint ndof = 3*el.Nodes();\n\t\tfe.assign(ndof, 0);\n\n\t\t// calculate internal force vector\n\t\tElementInternalForces(el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble element 'fe'-vector into global R vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEUT4Domain::ElementInternalForces(FESolidElement& el, vector<double>& fe)\n{\n\tint i, n;\n\n\t// jacobian matrix, inverse jacobian matrix and determinants\n\tdouble Ji[3][3], detJt;\n\n\tdouble Gx, Gy, Gz;\n\n\tconst double* Gr, *Gs, *Gt;\n\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tdouble*\tgw = el.GaussWeights();\n\n\t// repeat for all integration points\n\tfor (n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// calculate the jacobian\n\t\tdetJt = invjact(el, Ji, n);\n\n\t\tdetJt *= gw[n];\n\n\t\t// get the stress vector for this integration point\n\t\tmat3ds s = pt.m_s;\n\n\t\t// take the deviatoric component and multiply it\n\t\t// with the stabilization factor\n\t\tif (m_bdev)\n\t\t{\n\t\t\ts = s.dev()*m_alpha;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts = s*m_alpha;\n\t\t}\n\n\t\tGr = el.Gr(n);\n\t\tGs = el.Gs(n);\n\t\tGt = el.Gt(n);\n\n\t\tfor (i=0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tGx = Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i];\n\t\t\tGy = Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i];\n\t\t\tGz = Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i];\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[3*i  ] -= ( Gx*s.xx() +\n\t\t\t\t           Gy*s.xy() +\n\t\t\t\t\t       Gz*s.xz() )*detJt;\n\n\t\t\tfe[3*i+1] -= ( Gy*s.yy() +\n\t\t\t\t           Gx*s.xy() +\n\t\t\t\t\t       Gz*s.yz() )*detJt;\n\n\t\t\tfe[3*i+2] -= ( Gz*s.zz() +\n\t\t\t\t           Gy*s.yz() +\n\t\t\t\t\t       Gx*s.xz() )*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the stiffness matrix. The stiffness matrix is a sum of the\n//! nodal and element stiffness matrices\nvoid FEUT4Domain::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// calculate nodal stiffness matrix\n\tNodalStiffnessMatrix(LS);\n\n\t// calculate element stiffness matrix\n\tElementalStiffnessMatrix(LS);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the nodal contribution to the global stiffness matrix\nvoid FEUT4Domain::NodalStiffnessMatrix(FELinearSystem& LS)\n{\n\tvector<int> elm;\n\tvector<int> LM;\n\tvector<int> en;\n\n\t// loop over all the nodes\n\tint NN = (int) m_NODE.size(), ni, nj;\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\t// get the next node\n\t\tUT4NODE& node = m_NODE[i];\n\t\tFEElement** ppe = m_NEL.ElementList(node.inode);\n\n\t\t// get its valence\n\t\tint NE = m_NEL.Valence(node.inode);\n\n\t\t// allocate an element stiffness matrix\n\t\tFEElementMatrix ke(NE*4*3, NE*4*3); ke.zero();\n\n\t\t// calculate the geometry stiffness for this node\n\t\tNodalGeometryStiffness(node, ke);\n\n\t\t// calculate the material stiffness for this node\n\t\tNodalMaterialStiffness(node, ke, m_pMat);\n\n\t\t// it is assumed that the previous function only build the upper-triangular part\n\t\t// so now we build the lower-triangular by copying it from the upper-triangular part\n\t\tfor (ni=0; ni<NE*12; ++ni)\n\t\t{\n\t\t\tfor (nj=0; nj<ni; ++nj)\n\t\t\t{\n\t\t\t\tke[ni][nj] = ke[nj][ni];\n\t\t\t}\n\t\t}\n\n\t\t// create the LM and the en array\n\t\tLM.resize(NE*4*3);\n\t\ten.resize(NE*4  );\n\t\tfor (ni=0; ni<NE; ++ni)\n\t\t{\n\t\t\tFEElement& el = *ppe[ni];\n\t\t\tUnpackLM(el, elm);\n\t\t\tfor (int i=0; i<4; ++i)\n\t\t\t{\n\t\t\t\tLM[ni*4*3+3*i  ] = elm[3*i  ];\n\t\t\t\tLM[ni*4*3+3*i+1] = elm[3*i+1];\n\t\t\t\tLM[ni*4*3+3*i+2] = elm[3*i+2];\n\n\t\t\t\ten[ni*4+i] = el.m_node[i];\n\t\t\t}\n\t\t}\n\t\n\t\t// assemble the stiffness\n\t\tke.SetNodes(en);\n\t\tke.SetIndices(LM);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the nodal geometry stiffness contribution\nvoid FEUT4Domain::NodalGeometryStiffness(UT4NODE& node, matrix& ke)\n{\n\tint i, j, ni, nj;\n\n\t// get the element list \n\tint NE = m_NEL.Valence(node.inode);\n\tFEElement** ppe = m_NEL.ElementList(node.inode);\n\tint* peli = m_NEL.ElementIndexList(node.inode);\n\n\t// get the nodal stress\n\tmat3ds S;\n\tif (m_bdev)\n\t{\n\t\tS = node.si - node.si.dev()*m_alpha;\n\t}\n\telse\n\t{\n\t\tS = node.si*(1 - m_alpha);\n\t}\n\n\t// convert Cauchy stress to PK2 stress\n\tS = cauchy_to_pk2(S, node.Fi);\n\n\t// calculate the gradN matrices\n\tfor (ni=0; ni<NE; ++ni)\n\t{\n\t\tFESolidElement& ei = dynamic_cast<FESolidElement&>(*ppe[ni]);\n\n\t\t// calculate the jacobian\n\t\tdouble Ji[3][3];\n\t\tinvjac0(ei, Ji, 0);\n\n\t\t// get the shape function derivatives\n\t\tconst double* Gr, *Gs, *Gt;\n\t\tGr = ei.Gr(0);\n\t\tGs = ei.Gs(0);\n\t\tGt = ei.Gt(0);\n\n\t\tfor (j=0; j<4; ++j)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tm_Ge[ni][j][0] = Ji[0][0]*Gr[j]+Ji[1][0]*Gs[j]+Ji[2][0]*Gt[j];\n\t\t\tm_Ge[ni][j][1] = Ji[0][1]*Gr[j]+Ji[1][1]*Gs[j]+Ji[2][1]*Gt[j];\n\t\t\tm_Ge[ni][j][2] = Ji[0][2]*Gr[j]+Ji[1][2]*Gs[j]+Ji[2][2]*Gt[j];\n\t\t}\n\t}\n\n\t// stiffness matrices\n\tdouble kij;\n\n\t// loop over all the elements\n\tfor (ni=0; ni<NE; ++ni)\n\t{\n\t\tFESolidElement& ei = dynamic_cast<FESolidElement&>(*ppe[ni]);\n\n\t\t// calculate element volume\n\t\tdouble Vi = m_Ve0[peli[ni]];\n\t\tdouble wi = 0.25* Vi*node.Vi/ node.Vi;\n\n\t\t// loop over the elements again\n\t\tfor (nj=ni; nj<NE; ++nj)\n\t\t{\n\t\t\tFESolidElement& ej = dynamic_cast<FESolidElement&>(*ppe[nj]);\n\n\t\t\t// calculate element volume\n\t\t\tdouble Vj = m_Ve0[peli[nj]];\n\t\t\tdouble wj = 0.25* Vj / node.Vi;\n\n\t\t\t// We're ready to rock and roll!\n\t\t\tdouble sg[3];\n\t\t\tfor (i=0; i<4; ++i)\n\t\t\t{\n\t\t\t\tdouble (&Gi)[3] = *(m_Ge[ni] + i);\n\t\t\t\tint mi = ni*12+i*3;\n\t\t\t\tint j0 = (ni==nj?i:0);\n\t\t\t\tfor (j=j0; j<4; ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble (&Gj)[3] = *(m_Ge[nj] + j);\n\t\t\t\t\tint mj = nj*12+j*3;\n\n\t\t\t\t\tsg[0] = S.xx()*Gj[0] + S.xy()*Gj[1] + S.xz()*Gj[2];\n\t\t\t\t\tsg[1] = S.xy()*Gj[0] + S.yy()*Gj[1] + S.yz()*Gj[2];\n\t\t\t\t\tsg[2] = S.xz()*Gj[0] + S.yz()*Gj[1] + S.zz()*Gj[2];\n\n\t\t\t\t\tkij = wi*wj*(Gi[0]*sg[0]+Gi[1]*sg[1]+Gi[2]*sg[2]);\n\n\t\t\t\t\t// copy to element stiffness matrix\n\t\t\t\t\tke[mi  ][mj  ] += kij;\n\t\t\t\t\tke[mi+1][mj+1] += kij;\n\t\t\t\t\tke[mi+2][mj+2] += kij;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the volumetric contribution of the spatial tangent stiffness\n//!\ntens4ds FEUT4Domain::Cvol(const tens4ds& C, const mat3ds& S)\n{\n\tdouble p = -S.tr()/3.0;\n\n\tmat3dd I(1);\t// Identity\n\ttens4ds I4  = dyad4s(I);\n\n\t// Note the slightly different form than in the paper.\n\t// This is because Cvol needs to have the proper symmetries\n\treturn I4*(2*p) + dyad1s(I, S)/3.0 + dyad1s(I, C.dot(I))/6.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the nodal material stiffness contribution\nvoid FEUT4Domain::NodalMaterialStiffness(UT4NODE& node, matrix& ke, FESolidMaterial* pme)\n{\n\t// get the number of elements this nodes connects\n\tint NE = m_NEL.Valence(node.inode);\n\tFEElement** ppe = m_NEL.ElementList(node.inode);\n\tint* peli = m_NEL.ElementIndexList(node.inode);\n\n\t// create a material point\n\tFEElasticMaterialPoint* ep = new FEElasticMaterialPoint;\n\tFEMaterialPoint mp(ep);\n\tmp.Init();\n\n\t// set the material point data\n\tmp.m_r0 = m_pMesh->Node(node.inode).m_r0;\n\tmp.m_rt = m_pMesh->Node(node.inode).m_rt;\n\n\tep->m_F = node.Fi;\n\tep->m_J = ep->m_F.det();\n\n\t// set the Cauchy-stress\n\tep->m_s = node.si;\n\n\t// Calculate the spatial tangent\n\ttens4ds C = pme->Tangent(mp);\n\n\t// Next, we need to subtract the volumetric contribution Cvol\n\tif (m_bdev)\n\t{\n\t\t// subtract the isochoric component from C;\n\t\t// C = C - a*Ciso = C - (a*(C - Cvol)) = (1-a)*C + a*Cvol\n\t\tC = C*(1 - m_alpha) + Cvol(C, ep->m_s)*m_alpha;\n\t}\n\telse\n\t{\n\t\tC = C*(1 - m_alpha);\n\t}\n\n\t// convert spatial matrix to material\n\tC = spatial_to_material(C, ep->m_F);\n\n\t// extract the 'D' matrix\n\tdouble D[6][6] = {0};\n\tC.extract(D);\n\n\t// loop over all the elements\n\tint i, j, ni, nj;\n\n\t// calculate B-matrices\n\tfor (ni=0; ni<NE; ++ni)\n\t{\n\t\tFESolidElement& el = dynamic_cast<FESolidElement&>(*ppe[ni]);\n\n\t\t// calculate the jacobian\n\t\tdouble Ji[3][3];\n\t\tinvjac0(el, Ji, 0);\n\n\t\t// get the shape function derivatives\n\t\tconst double* Gr, *Gs, *Gt;\n\t\tGr = el.Gr(0);\n\t\tGs = el.Gs(0);\n\t\tGt = el.Gt(0);\n\n\t\t// get element deformation gradient\n\t\tmat3d Fe = node.Fi;\n\n\t\tdouble Gx, Gy, Gz;\n\t\tfor (j=0; j<4; ++j)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tGx = Ji[0][0]*Gr[j]+Ji[1][0]*Gs[j]+Ji[2][0]*Gt[j];\n\t\t\tGy = Ji[0][1]*Gr[j]+Ji[1][1]*Gs[j]+Ji[2][1]*Gt[j];\n\t\t\tGz = Ji[0][2]*Gr[j]+Ji[1][2]*Gs[j]+Ji[2][2]*Gt[j];\n\n\t\t\tdouble (&Bi)[6][3] = *(m_Be+(4*ni+j));\n\t\t\tBi[0][0] = Fe[0][0]*Gx; Bi[0][1] = Fe[1][0]*Gx; Bi[0][2] = Fe[2][0]*Gx;\n\t\t\tBi[1][0] = Fe[0][1]*Gy; Bi[1][1] = Fe[1][1]*Gy; Bi[1][2] = Fe[2][1]*Gy;\n\t\t\tBi[2][0] = Fe[0][2]*Gz; Bi[2][1] = Fe[1][2]*Gz; Bi[2][2] = Fe[2][2]*Gz;\n\t\t\tBi[3][0] = Fe[0][0]*Gy + Fe[0][1]*Gx; Bi[3][1] = Fe[1][0]*Gy + Fe[1][1]*Gx; Bi[3][2] = Fe[2][0]*Gy + Fe[2][1]*Gx;\n\t\t\tBi[4][0] = Fe[0][1]*Gz + Fe[0][2]*Gy; Bi[4][1] = Fe[1][1]*Gz + Fe[1][2]*Gy; Bi[4][2] = Fe[2][1]*Gz + Fe[2][2]*Gy;\n\t\t\tBi[5][0] = Fe[0][2]*Gx + Fe[0][0]*Gz; Bi[5][1] = Fe[1][2]*Gx + Fe[1][0]*Gz; Bi[5][2] = Fe[2][2]*Gx + Fe[2][0]*Gz;\n\n\t\t\tdouble (&DBi)[6][3] = *(m_DB+(4*ni+j));\n\t\t\tDBi[0][0] = (D[0][0]*Bi[0][0]+D[0][1]*Bi[1][0]+D[0][2]*Bi[2][0]+D[0][3]*Bi[3][0]+D[0][4]*Bi[4][0]+D[0][5]*Bi[5][0]);\n\t\t\tDBi[0][1] = (D[0][0]*Bi[0][1]+D[0][1]*Bi[1][1]+D[0][2]*Bi[2][1]+D[0][3]*Bi[3][1]+D[0][4]*Bi[4][1]+D[0][5]*Bi[5][1]);\n\t\t\tDBi[0][2] = (D[0][0]*Bi[0][2]+D[0][1]*Bi[1][2]+D[0][2]*Bi[2][2]+D[0][3]*Bi[3][2]+D[0][4]*Bi[4][2]+D[0][5]*Bi[5][2]);\n\n\t\t\tDBi[1][0] = (D[1][0]*Bi[0][0]+D[1][1]*Bi[1][0]+D[1][2]*Bi[2][0]+D[1][3]*Bi[3][0]+D[1][4]*Bi[4][0]+D[1][5]*Bi[5][0]);\n\t\t\tDBi[1][1] = (D[1][0]*Bi[0][1]+D[1][1]*Bi[1][1]+D[1][2]*Bi[2][1]+D[1][3]*Bi[3][1]+D[1][4]*Bi[4][1]+D[1][5]*Bi[5][1]);\n\t\t\tDBi[1][2] = (D[1][0]*Bi[0][2]+D[1][1]*Bi[1][2]+D[1][2]*Bi[2][2]+D[1][3]*Bi[3][2]+D[1][4]*Bi[4][2]+D[1][5]*Bi[5][2]);\n\n\t\t\tDBi[2][0] = (D[2][0]*Bi[0][0]+D[2][1]*Bi[1][0]+D[2][2]*Bi[2][0]+D[2][3]*Bi[3][0]+D[2][4]*Bi[4][0]+D[2][5]*Bi[5][0]);\n\t\t\tDBi[2][1] = (D[2][0]*Bi[0][1]+D[2][1]*Bi[1][1]+D[2][2]*Bi[2][1]+D[2][3]*Bi[3][1]+D[2][4]*Bi[4][1]+D[2][5]*Bi[5][1]);\n\t\t\tDBi[2][2] = (D[2][0]*Bi[0][2]+D[2][1]*Bi[1][2]+D[2][2]*Bi[2][2]+D[2][3]*Bi[3][2]+D[2][4]*Bi[4][2]+D[2][5]*Bi[5][2]);\n\n\t\t\tDBi[3][0] = (D[3][0]*Bi[0][0]+D[3][1]*Bi[1][0]+D[3][2]*Bi[2][0]+D[3][3]*Bi[3][0]+D[3][4]*Bi[4][0]+D[3][5]*Bi[5][0]);\n\t\t\tDBi[3][1] = (D[3][0]*Bi[0][1]+D[3][1]*Bi[1][1]+D[3][2]*Bi[2][1]+D[3][3]*Bi[3][1]+D[3][4]*Bi[4][1]+D[3][5]*Bi[5][1]);\n\t\t\tDBi[3][2] = (D[3][0]*Bi[0][2]+D[3][1]*Bi[1][2]+D[3][2]*Bi[2][2]+D[3][3]*Bi[3][2]+D[3][4]*Bi[4][2]+D[3][5]*Bi[5][2]);\n\n\t\t\tDBi[4][0] = (D[4][0]*Bi[0][0]+D[4][1]*Bi[1][0]+D[4][2]*Bi[2][0]+D[4][3]*Bi[3][0]+D[4][4]*Bi[4][0]+D[4][5]*Bi[5][0]);\n\t\t\tDBi[4][1] = (D[4][0]*Bi[0][1]+D[4][1]*Bi[1][1]+D[4][2]*Bi[2][1]+D[4][3]*Bi[3][1]+D[4][4]*Bi[4][1]+D[4][5]*Bi[5][1]);\n\t\t\tDBi[4][2] = (D[4][0]*Bi[0][2]+D[4][1]*Bi[1][2]+D[4][2]*Bi[2][2]+D[4][3]*Bi[3][2]+D[4][4]*Bi[4][2]+D[4][5]*Bi[5][2]);\n\n\t\t\tDBi[5][0] = (D[5][0]*Bi[0][0]+D[5][1]*Bi[1][0]+D[5][2]*Bi[2][0]+D[5][3]*Bi[3][0]+D[5][4]*Bi[4][0]+D[5][5]*Bi[5][0]);\n\t\t\tDBi[5][1] = (D[5][0]*Bi[0][1]+D[5][1]*Bi[1][1]+D[5][2]*Bi[2][1]+D[5][3]*Bi[3][1]+D[5][4]*Bi[4][1]+D[5][5]*Bi[5][1]);\n\t\t\tDBi[5][2] = (D[5][0]*Bi[0][2]+D[5][1]*Bi[1][2]+D[5][2]*Bi[2][2]+D[5][3]*Bi[3][2]+D[5][4]*Bi[4][2]+D[5][5]*Bi[5][2]);\n\t\t}\n\t}\n\n\t// loop over the elements again\n\t// (we only build the upper-triangular (block) matrix\n\tfor (ni=0; ni<NE; ++ni)\n\t{\n\t\t// calculate element volume\n\t\tdouble Vi = m_Ve0[peli[ni]];\n\t\tdouble wi = 0.25* Vi*node.Vi/ node.Vi;\n\n\t\tfor (nj=ni; nj<NE; ++nj)\n\t\t{\n\t\t\t// calculate element volume\n\t\t\tdouble Vj = m_Ve0[peli[nj]];\n\t\t\tdouble wj = 0.25* Vj / node.Vi;\n\n\t\t\tdouble wij = wi*wj;\n\n\t\t\t// We're ready to rock and roll!\n\t\t\tfor (i=0; i<4; ++i)\n\t\t\t{\n\t\t\t\tdouble (&Bi)[6][3] = *(m_Be+(ni*4 + i));\n\t\t\t\tint mi = ni*12+i*3;\n\t\t\t\tint j0 = (nj==ni?i:0);\n\n\t\t\t\tfor (j=j0; j<4; ++j)\n\t\t\t\t{\n\t\t\t\t\t// calculate the Bi*D*Bj term\n\t\t\t\t\tdouble (&DBj)[6][3] = *(m_DB+(nj*4 + j));\n\t\t\t\t\tint mj = nj*12+j*3;\n\n\t\t\t\t\tke[mi  ][mj  ] += wij*(Bi[0][0]*DBj[0][0]+Bi[1][0]*DBj[1][0]+Bi[2][0]*DBj[2][0]+Bi[3][0]*DBj[3][0]+Bi[4][0]*DBj[4][0]+Bi[5][0]*DBj[5][0]);\n\t\t\t\t\tke[mi  ][mj+1] += wij*(Bi[0][0]*DBj[0][1]+Bi[1][0]*DBj[1][1]+Bi[2][0]*DBj[2][1]+Bi[3][0]*DBj[3][1]+Bi[4][0]*DBj[4][1]+Bi[5][0]*DBj[5][1]);\n\t\t\t\t\tke[mi  ][mj+2] += wij*(Bi[0][0]*DBj[0][2]+Bi[1][0]*DBj[1][2]+Bi[2][0]*DBj[2][2]+Bi[3][0]*DBj[3][2]+Bi[4][0]*DBj[4][2]+Bi[5][0]*DBj[5][2]);\n\n\t\t\t\t\tke[mi+1][mj  ] += wij*(Bi[0][1]*DBj[0][0]+Bi[1][1]*DBj[1][0]+Bi[2][1]*DBj[2][0]+Bi[3][1]*DBj[3][0]+Bi[4][1]*DBj[4][0]+Bi[5][1]*DBj[5][0]);\n\t\t\t\t\tke[mi+1][mj+1] += wij*(Bi[0][1]*DBj[0][1]+Bi[1][1]*DBj[1][1]+Bi[2][1]*DBj[2][1]+Bi[3][1]*DBj[3][1]+Bi[4][1]*DBj[4][1]+Bi[5][1]*DBj[5][1]);\n\t\t\t\t\tke[mi+1][mj+2] += wij*(Bi[0][1]*DBj[0][2]+Bi[1][1]*DBj[1][2]+Bi[2][1]*DBj[2][2]+Bi[3][1]*DBj[3][2]+Bi[4][1]*DBj[4][2]+Bi[5][1]*DBj[5][2]);\n\n\t\t\t\t\tke[mi+2][mj  ] += wij*(Bi[0][2]*DBj[0][0]+Bi[1][2]*DBj[1][0]+Bi[2][2]*DBj[2][0]+Bi[3][2]*DBj[3][0]+Bi[4][2]*DBj[4][0]+Bi[5][2]*DBj[5][0]);\n\t\t\t\t\tke[mi+2][mj+1] += wij*(Bi[0][2]*DBj[0][1]+Bi[1][2]*DBj[1][1]+Bi[2][2]*DBj[2][1]+Bi[3][2]*DBj[3][1]+Bi[4][2]*DBj[4][1]+Bi[5][2]*DBj[5][1]);\n\t\t\t\t\tke[mi+2][mj+2] += wij*(Bi[0][2]*DBj[0][2]+Bi[1][2]*DBj[1][2]+Bi[2][2]*DBj[2][2]+Bi[3][2]*DBj[3][2]+Bi[4][2]*DBj[4][2]+Bi[5][2]*DBj[5][2]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the element contribution to the global stiffness matrix\nvoid FEUT4Domain::ElementalStiffnessMatrix(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// element stiffness matrix\n\tFEElementMatrix ke;\n\n\tvector<int> elm;\n\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// create the element's stiffness matrix\n\t\tint ndof = 3*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate the element stiffness matrix\n\t\tElementStiffness(tp, iel, ke);\n\n\t\t// get the element equation numbers\n\t\tUnpackLM(el, elm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tke.SetNodes(el.m_node);\n\t\tke.SetIndices(elm);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUT4Domain::ElementStiffness(const FETimeInfo& tp, int iel, matrix &ke)\n{\n\tFESolidElement& el = Element(iel);\n\n\t// TODO: I need to figure out how to deal with incompressible materials\n\t//       Incompressible materials require an additional dilatational \n\t//       stiffness which is calculated differently from the geometric and\n\t//       material stiffnesses. \n\t// TODO: I don't know if the comment above applies anymore\n\n\t// calculate material stiffness (i.e. constitutive component)\n\tElementMaterialStiffness(el, ke);\n\n\t// calculate geometrical stiffness\n\tElementGeometricalStiffness(el, ke);\n\n\t// assign symmetic parts\n\t// TODO: Can this be omitted by changing the Assemble routine so that it only\n\t// grabs elements from the upper diagonal matrix?\n\tint ndof = 3*el.Nodes();\n\tint i, j;\n\tfor (i=0; i<ndof; ++i)\n\t\tfor (j=i+1; j<ndof; ++j)\n\t\t\tke[j][i] = ke[i][j];\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element's geometrical stiffness component for integration point n\n\nvoid FEUT4Domain::ElementGeometricalStiffness(FESolidElement &el, matrix &ke)\n{\n\tvec3d G[FEElement::MAX_NODES];\n\n\t// nr of nodes\n\tint neln = el.Nodes();\n\n\t// nr of integration points\n\tint nint = el.GaussPoints();\n\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// stiffness component for the initial stress component of stiffness matrix\n\tdouble kab;\n\n\t// calculate geometrical element stiffness matrix\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdouble detJt = ShapeGradient(el, n, G)*gw[n];\n\n\t\t// get the material point data\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// element's Cauchy-stress tensor at gauss point n\n\t\t// s is the voight vector\n\t\tmat3ds s = pt.m_s;\n\n\t\t// we work with the deviatoric component only\n\t\tif (m_bdev)\n\t\t{\n\t\t\ts = s.dev()*m_alpha;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts = s*m_alpha;\n\t\t}\n\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t\tfor (int j = i; j<neln; ++j)\n\t\t\t{\n\t\t\t\tkab = (G[i]*( s*G[j]) )*detJt;\n\n\t\t\t\tke[3*i  ][3*j  ] += kab;\n\t\t\t\tke[3*i+1][3*j+1] += kab;\n\t\t\t\tke[3*i+2][3*j+2] += kab;\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\n\nvoid FEUT4Domain::ElementMaterialStiffness(FESolidElement &el, matrix &ke)\n{\n\t// Get the current element's data\n\tconst int nint = el.GaussPoints();\n\tconst int neln = el.Nodes();\n\tconst int ndof = 3*neln;\n\n\t// global derivatives of shape functions\n\tvec3d G[FEElement::MAX_NODES];\n\n\tdouble Gxi, Gyi, Gzi;\n\tdouble Gxj, Gyj, Gzj;\n\n\t// The 'D' matrix\n\tdouble D[6][6] = {0};\t// The 'D' matrix\n\n\t// The 'D*BL' matrix\n\tdouble DBL[6][3];\n\n\n\t// weights at gauss points\n\tconst double *gw = el.GaussWeights();\n\n\t// calculate element stiffness matrix\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// calculate jacobian and shape function gradients\n\t\tdouble detJt = ShapeGradient(el, n, G)*gw[n];\n\n\t\t// setup the material point\n\t\t// NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// Calculate the tangent\n\t\ttens4ds C = m_pMat->Tangent(mp);\n\n\t\t// Next, we need to subtract the volumetric contribution Cvol\n\t\tif (m_bdev)\n\t\t{\n\t\t\t// subtract the volumetric tensor from C;\n\t\t\tC = (C - Cvol(C, pt.m_s))*m_alpha;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tC = C*m_alpha;\n\t\t}\n\n\t\t// extract the 'D' matrix\n\t\tC.extract(D);\n\n\t\t// we only calculate the upper triangular part\n\t\t// since ke is symmetric. The other part is\n\t\t// determined below using this symmetry.\n\t\tfor (int i = 0, i3 = 0; i<neln; ++i, i3 += 3)\n\t\t{\n\t\t\tGxi = G[i].x;\n\t\t\tGyi = G[i].y;\n\t\t\tGzi = G[i].z;\n\n\t\t\tfor (int j = i, j3 = i3; j<neln; ++j, j3 += 3)\n\t\t\t{\n\t\t\t\tGxj = G[j].x;\n\t\t\t\tGyj = G[j].y;\n\t\t\t\tGzj = G[j].z;\n\n\t\t\t\t// calculate D*BL matrices\n\t\t\t\tDBL[0][0] = (D[0][0]*Gxj+D[0][3]*Gyj+D[0][5]*Gzj);\n\t\t\t\tDBL[0][1] = (D[0][1]*Gyj+D[0][3]*Gxj+D[0][4]*Gzj);\n\t\t\t\tDBL[0][2] = (D[0][2]*Gzj+D[0][4]*Gyj+D[0][5]*Gxj);\n\n\t\t\t\tDBL[1][0] = (D[1][0]*Gxj+D[1][3]*Gyj+D[1][5]*Gzj);\n\t\t\t\tDBL[1][1] = (D[1][1]*Gyj+D[1][3]*Gxj+D[1][4]*Gzj);\n\t\t\t\tDBL[1][2] = (D[1][2]*Gzj+D[1][4]*Gyj+D[1][5]*Gxj);\n\n\t\t\t\tDBL[2][0] = (D[2][0]*Gxj+D[2][3]*Gyj+D[2][5]*Gzj);\n\t\t\t\tDBL[2][1] = (D[2][1]*Gyj+D[2][3]*Gxj+D[2][4]*Gzj);\n\t\t\t\tDBL[2][2] = (D[2][2]*Gzj+D[2][4]*Gyj+D[2][5]*Gxj);\n\n\t\t\t\tDBL[3][0] = (D[3][0]*Gxj+D[3][3]*Gyj+D[3][5]*Gzj);\n\t\t\t\tDBL[3][1] = (D[3][1]*Gyj+D[3][3]*Gxj+D[3][4]*Gzj);\n\t\t\t\tDBL[3][2] = (D[3][2]*Gzj+D[3][4]*Gyj+D[3][5]*Gxj);\n\n\t\t\t\tDBL[4][0] = (D[4][0]*Gxj+D[4][3]*Gyj+D[4][5]*Gzj);\n\t\t\t\tDBL[4][1] = (D[4][1]*Gyj+D[4][3]*Gxj+D[4][4]*Gzj);\n\t\t\t\tDBL[4][2] = (D[4][2]*Gzj+D[4][4]*Gyj+D[4][5]*Gxj);\n\n\t\t\t\tDBL[5][0] = (D[5][0]*Gxj+D[5][3]*Gyj+D[5][5]*Gzj);\n\t\t\t\tDBL[5][1] = (D[5][1]*Gyj+D[5][3]*Gxj+D[5][4]*Gzj);\n\t\t\t\tDBL[5][2] = (D[5][2]*Gzj+D[5][4]*Gyj+D[5][5]*Gxj);\n\n\t\t\t\tke[i3  ][j3  ] += (Gxi*DBL[0][0] + Gyi*DBL[3][0] + Gzi*DBL[5][0] )*detJt;\n\t\t\t\tke[i3  ][j3+1] += (Gxi*DBL[0][1] + Gyi*DBL[3][1] + Gzi*DBL[5][1] )*detJt;\n\t\t\t\tke[i3  ][j3+2] += (Gxi*DBL[0][2] + Gyi*DBL[3][2] + Gzi*DBL[5][2] )*detJt;\n\n\t\t\t\tke[i3+1][j3  ] += (Gyi*DBL[1][0] + Gxi*DBL[3][0] + Gzi*DBL[4][0] )*detJt;\n\t\t\t\tke[i3+1][j3+1] += (Gyi*DBL[1][1] + Gxi*DBL[3][1] + Gzi*DBL[4][1] )*detJt;\n\t\t\t\tke[i3+1][j3+2] += (Gyi*DBL[1][2] + Gxi*DBL[3][2] + Gzi*DBL[4][2] )*detJt;\n\n\t\t\t\tke[i3+2][j3  ] += (Gzi*DBL[2][0] + Gyi*DBL[4][0] + Gxi*DBL[5][0] )*detJt;\n\t\t\t\tke[i3+2][j3+1] += (Gzi*DBL[2][1] + Gyi*DBL[4][1] + Gxi*DBL[5][1] )*detJt;\n\t\t\t\tke[i3+2][j3+2] += (Gzi*DBL[2][2] + Gyi*DBL[4][2] + Gxi*DBL[5][2] )*detJt;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEUT4Domain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FENodeElemList.h\"\n#include \"FECore/tens4d.h\"\n#include \"FEElasticMaterial.h\"\n#include \"FEElasticSolidDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain for nodally integrated tet elements \n\n//! This class implements the uniform nodal strain tetrahedron with\n//! isochoric stabilization as described by Gee, Dohrmann, Key and Wall (2009)\n//!\nclass FEBIOMECH_API FEUT4Domain : public FEElasticSolidDomain\n{\npublic:\n\tstruct UT4NODE\n\t{\n\t\tint\t\tinode;\t// index of FE node\n\t\tdouble\tVi;\t\t// reference nodal volume\n\t\tdouble\tvi;\t\t// current nodal volume\n\t\tmat3d\tFi;\t\t// average deformation gradient\n\t\tmat3ds\tsi;\t\t// nodal stress\n\n\t\tvoid Serialize(DumpStream& ar);\n\t};\n\npublic:\n\t//! constructor\n\tFEUT4Domain(FEModel* pfem);\n\n\t//! destructor\n\t~FEUT4Domain();\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! get nodal data\n\tint UT4Nodes() { return (int) m_NODE.size(); }\n\tUT4NODE& UT4Node(int i) { return m_NODE[i]; }\n\n\t//! return the node-element list for this domain\n\tFENodeElemList& GetNodeElemList() { return m_NEL; }\n\n\t//! initialization function\n\tbool Init() override;\n\n\t//! build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t//! Set UT4 parameters\n\tvoid SetUT4Parameters(double alpha, bool bdev);\n\n\t//! override Create so we can grab the ut4 parameters\n\tbool Create(int nelems, FE_Element_Spec espec) override;\n\npublic: // overrides from FEElasticDomain\n\n\t//! Update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! calculates the internal force vector\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\t//! calculates the nodal internal forces\n\tvoid NodalInternalForces(FEGlobalVector& R);\n\n\t//! calculates the element internal forces\n\tvoid ElementInternalForces(FEGlobalVector& R);\n\n\t//! Calculates the internal stress vector for solid elements\n\tvoid ElementInternalForces(FESolidElement& el, vector<double>& fe);\n\nprotected:\n\t//! Calculates the element stiffness matrix\n\tvoid ElementalStiffnessMatrix(FELinearSystem& LS);\n\n\t//! calculates the solid element stiffness matrix\n\tvoid ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke) override;\n\n\t//! Calculates the nodal stiffness matrix\n\tvoid NodalStiffnessMatrix(FELinearSystem& LS);\n\n\t//! geometrical stiffness (i.e. initial stress)\n\tvoid ElementGeometricalStiffness(FESolidElement& el, matrix& ke) override;\n\n\t//! material stiffness component\n\tvoid ElementMaterialStiffness(FESolidElement& el, matrix& ke) override;\n\n\t//! nodal geometry stiffness contribution\n\tvoid NodalGeometryStiffness(UT4NODE& node, matrix& ke);\n\n\t//! nodal material stiffness contribution\n\tvoid NodalMaterialStiffness(UT4NODE& node, matrix& ke, FESolidMaterial* pme);\n\nprotected:\n\t//! calculate the volume of a tet element\n\tdouble TetVolume(vec3d* r);\n\n\ttens4ds Cvol(const tens4ds& C, const mat3ds& S);\n\n\tdouble\tm_alpha;\t//!< stabilization factor alpha\n\tbool\tm_bdev;\t\t//!< use deviatoric components only for the element contribution\n\nprivate:\n\tvector<int>\t\tm_tag;\t//!< nodal tags\n\tvector<UT4NODE>\tm_NODE;\t//!< Nodal data\n\tvector<double>\tm_Ve0;\t//!< initial element volumes\n\n\tdouble\t(*m_Be)[6][3];\n\tdouble\t(*m_DB)[6][3];\n\tdouble\t(*m_Ge)[4][3];\n\n\tFENodeElemList\tm_NEL;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledActiveContraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledActiveContraction.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledActiveContraction, FEUncoupledMaterial)\n\tADD_PARAMETER(m_Tmax , \"Tmax\" );\n\tADD_PARAMETER(m_ca0  , \"ca0\"  );\n\tADD_PARAMETER(m_camax, \"camax\");\n\tADD_PARAMETER(m_beta , \"beta\" );\n\tADD_PARAMETER(m_l0   , \"l0\"   );\n\tADD_PARAMETER(m_refl , \"refl\" );\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEUncoupledActiveContraction::FEUncoupledActiveContraction(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEUncoupledActiveContraction::DevStress(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d& F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0/3.0);\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// get the initial fiber direction\n\tvec3d a0 = Q.col(0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F*a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam = a.unit();\n\tdouble lamd = lam*Jm13; // i.e. lambda tilde\n\n\t// current sarcomere length\n\tdouble strl = m_refl*lamd;\n\n\t// sarcomere length change\n\tdouble dl = strl - m_l0;\n\n\t// calculate stress\n\tmat3ds s; s.zero();\n\tif (dl >= 0)\n\t{\n\t\t// calcium sensitivity\n\t\tdouble eca50i = (exp(m_beta*dl) - 1);\n\n\t\t// ratio of Camax/Ca0\n\t\tdouble rca = m_camax/m_ca0;\n\n\t\t// active fiber stress\n\t\tdouble saf = m_Tmax*(eca50i / ( eca50i + rca*rca ));\n\n\t\t// calculate dyad of a: AxA = (a x a)\n\t\tmat3ds AxA = dyad(a);\n\n\t\t// add saf*(a x a)\n\t\ts += AxA*saf;\n\t}\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n// \\todo Implement this\ntens4ds FEUncoupledActiveContraction::DevTangent(FEMaterialPoint &pt)\n{\n\treturn tens4ds(0.0);\n}\n"
  },
  {
    "path": "FEBioMech/FEUncoupledActiveContraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This material implements an active contraction model which can be used\n// as a component of an uncoupled solid matrix material.\nclass FEUncoupledActiveContraction : public FEUncoupledMaterial\n{\npublic:\n\t//! constructor\n\tFEUncoupledActiveContraction(FEModel* pfem);\n\n\t//! deviatoric stress\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! deviatoric tangent\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\npublic:\n\tdouble\tm_Tmax;\n\tdouble\tm_ca0;\n\tdouble\tm_camax;\n\tdouble\tm_beta;\n\tdouble\tm_l0;\n\tdouble\tm_refl;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledActiveFiberContraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEUncoupledActiveFiberContraction.h\"\n#include \"FEElasticMaterial.h\"\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(FEUncoupledActiveFiberContraction, FEActiveContractionMaterial);\n\tADD_PARAMETER(m_ascl, \"ascl\");\n\tADD_PARAMETER(m_Tmax, \"Tmax\");\n\tADD_PARAMETER(m_ca0, \"ca0\");\n\tADD_PARAMETER(m_camax, \"camax\");\n\tADD_PARAMETER(m_beta, \"beta\");\n\tADD_PARAMETER(m_l0, \"l0\");\n\tADD_PARAMETER(m_refl, \"refl\");\nEND_FECORE_CLASS();\n\nFEUncoupledActiveFiberContraction::FEUncoupledActiveFiberContraction(FEModel* pfem) : FEActiveContractionMaterial(pfem)\n{\n\tm_ascl = 0;\n\tm_Tmax = 1.0;\n\tm_ca0 = 1.0;\n\tm_camax = 0.0;\n\tm_beta = 1;\n\tm_l0 = 1;\n\tm_refl = 1;\n}\n\nbool FEUncoupledActiveFiberContraction::Init()\n{\n\tif (FEActiveContractionMaterial::Init() == false) return false;\n\n\t// for backward compatibility we set m_camax to m_ca0 if it is not defined\n\tif (m_camax == 0.0) m_camax = m_ca0;\n\tif (m_camax <= 0.0) { feLogError(\"camax must be larger than zero\"); return false; }\n\n\treturn true;\n}\n\nmat3ds FEUncoupledActiveFiberContraction::ActiveStress(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F * a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam * Jm13; // i.e. lambda tilde\n\n\t// calculate dyad of a: AxA = (a x a)\n\tmat3ds AxA = dyad(a);\n\n\t// get the activation\n\tdouble s = 0.0;\n\tif (m_ascl > 0)\n\t{\n\t\t// current sarcomere length\n\t\tdouble strl = m_refl * lamd;\n\n\t\t// sarcomere length change\n\t\tdouble dl = strl - m_l0;\n\n\t\tif (dl >= 0)\n\t\t{\n\t\t\t// calcium sensitivity\n\t\t\tdouble eca50i = (exp(m_beta * dl) - 1);\n\n\t\t\t// ratio of Camax/Ca0\n\t\t\tdouble rca = m_camax / m_ca0;\n\n\t\t\t// active fiber stress\n\t\t\ts = m_Tmax * (eca50i / (eca50i + rca * rca)) * m_ascl;\n\t\t}\n\t}\n\n\tmat3ds sa = (AxA * (s / J));\n\n\treturn sa.dev();\n}\n\ntens4ds FEUncoupledActiveFiberContraction::ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F * a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam * Jm13; // i.e. lambda tilde\n\tdouble lamd2 = lamd * lamd;\n\tdouble lamd3 = lamd2 * lamd;\n\n\t// calculate dyad of a: AxA = (a x a)\n\tmat3dd I(1.0);\n\tmat3ds A = dyad(a);\n\ttens4ds AxA = dyad1s(A);\n\ttens4ds IxI = dyad1s(I);\n\n\ttens4ds Ax1 = dyad1s(A, I);\n\ttens4ds i4 = dyad4s(I);\n\n\tdouble sa = 0;\n\tdouble dsa = 0;\n\tif (m_ascl > 0)\n\t{\n\t\t// current sarcomere length\n\t\tdouble strl = m_refl * lamd;\n\n\t\t// sarcomere length change\n\t\tdouble dl = strl - m_l0;\n\n\t\tif (dl >= 0)\n\t\t{\n\t\t\t// calcium sensitivity\n\t\t\tdouble eca50i = (exp(m_beta * dl) - 1);\n\n\t\t\t// ratio of Camax/Ca0\n\t\t\tdouble rca = m_camax / m_ca0;\n\t\t\tdouble r2 = rca * rca;\n\n\t\t\t// active fiber stress\n\t\t\tdouble D = eca50i + r2;\n\t\t\tsa = m_Tmax * (eca50i / D) * m_ascl;\n\n\t\t\tdsa = -2.0 * sa / lamd3 + (1.0 / lamd2) * m_Tmax * m_ascl * m_beta * m_refl * r2 * (eca50i + 1.0) / (D * D);\n\t\t}\n\t}\n\n\ttens4ds Jc = Ax1 * (-(2.0 / 3.0) * sa)\n\t\t+ (i4 + IxI / 3.0) * (2.0 * sa / 3.0)\n\t\t+ (AxA - Ax1 / 3.0 + IxI / 9.0) * (lamd3 * dsa);\n\n\treturn Jc / J;\n}\n"
  },
  {
    "path": "FEBioMech/FEUncoupledActiveFiberContraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEActiveContractionMaterial.h\"\n\n//! A material class describing the active fiber contraction for use in uncoupled materials\nclass FEUncoupledActiveFiberContraction : public FEActiveContractionMaterial\n{\npublic:\n\tFEUncoupledActiveFiberContraction(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! calculate the fiber stress\n\tmat3ds ActiveStress(FEMaterialPoint& mp, const vec3d& a0) override;\n\n\t//! active contraction stiffness contribution\n\ttens4ds ActiveStiffness(FEMaterialPoint& mp, const vec3d& a0) override;\n\nprotected:\n\tdouble\tm_ascl;\t\t//!< activation scale factor\n\tdouble\tm_Tmax;\t\t//!< activation scale factor\n\tdouble\tm_ca0;\t\t//!< intracellular calcium concentration\n\tdouble\tm_camax;\t//!< peak calcium concentration\n\tdouble\tm_beta;\t\t//!< shape of peak isometric tension-sarcomere length relation\n\tdouble\tm_l0;\t\t//!< unloaded length\n\tdouble\tm_refl;\t\t//!< sarcomere length\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledElasticMixture.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledElasticMixture.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/log.h>\n\n// define the material parameters\n BEGIN_FECORE_CLASS(FEUncoupledElasticMixture, FEUncoupledMaterial)\n\tADD_PROPERTY(m_pMat, \"solid\");\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//////////////////////////////////////////////////////////////////////\n// Mixture of uncoupled elastic solids\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nFEUncoupledElasticMixture::FEUncoupledElasticMixture(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEUncoupledElasticMixture::CreateMaterialPointData() \n{ \n\tFEElasticMixtureMaterialPoint* pt = new FEElasticMixtureMaterialPoint();\n\tint NMAT = Materials();\n\tfor (int i=0; i<NMAT; ++i) pt->AddMaterialPoint(new FEMaterialPoint(m_pMat[i]->CreateMaterialPointData()));\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEUncoupledElasticMixture::AddMaterial(FEElasticMaterial* pm)\n{ \n\tm_pMat.push_back(dynamic_cast<FEUncoupledMaterial*>(pm)); \n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEUncoupledElasticMixture::Init()\n{\n    // check if any of the solid materials are elastic mixtures -- none allowed,\n    // otherwise FEBio does not know which FEElasticMixtureMaterialPoint to access\n    int nmix = 0;\n    for (int i=0; i<Materials(); ++i) {\n        if (dynamic_cast<FEElasticMixture*>(m_pMat[i])) nmix++;\n        if (dynamic_cast<FEUncoupledElasticMixture*>(m_pMat[i])) nmix++;\n    }\n    \n    if (nmix > 0) {\n        feLogError(\"Solids in uncoupled solid mixture material cannot be solid mixtures\");\n        return false;\n    }\n    \n    return FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEUncoupledElasticMixture::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\tvector<double>& w = pt.m_w;\n\tassert(w.size() == m_pMat.size());\n\n\t// get the elastic material point\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate stress\n\tmat3ds s; s.zero();\n\tfor (int i=0; i < (int)m_pMat.size(); ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem  = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt    = mp.m_rt;\n\t\tmpi.m_r0    = mp.m_r0;\n\n\t\t// copy the elastic material point data to the components\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\t\tepi.m_F = ep.m_F;\n\t\tepi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n\n        FEUncoupledMaterial* uMat = dynamic_cast<FEUncoupledMaterial*>(m_pMat[i]);\n        if (uMat)\n            s += epi.m_s = uMat->DevStress(mpi)*w[i];\n        else\n            s += epi.m_s = m_pMat[i]->Stress(mpi)*w[i];\n\t}\n    \n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEUncoupledElasticMixture::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\tvector<double>& w = pt.m_w;\n\tassert(w.size() == m_pMat.size());\n\n\t// get the elastic material point\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate elasticity tensor\n\ttens4ds c(0.);\n\tfor (int i=0; i < (int)m_pMat.size(); ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem  = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt    = mp.m_rt;\n\t\tmpi.m_r0    = mp.m_r0;\n\n\t\t// copy the elastic material point data to the components\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\t\tepi.m_F = ep.m_F;\n\t\tepi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n\n        FEUncoupledMaterial* uMat = dynamic_cast<FEUncoupledMaterial*>(m_pMat[i]);\n        if (uMat)\n            c += uMat->DevTangent(mpi)*w[i];\n        else\n            c += m_pMat[i]->Tangent(mpi)*w[i];\n\t}\n    \n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEUncoupledElasticMixture::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n\tvector<double>& w = pt.m_w;\n\tassert(w.size() == m_pMat.size());\n    \n\t// get the elastic material point\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate strain energy density\n\tdouble sed = 0.0;\n\tfor (int i=0; i < (int)m_pMat.size(); ++i)\n\t{\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem  = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt    = mp.m_rt;\n\t\tmpi.m_r0    = mp.m_r0;\n\n\t\t// copy the elastic material point data to the components\n\t\tFEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n\t\tepi.m_F = ep.m_F;\n\t\tepi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n\n        FEUncoupledMaterial* uMat = dynamic_cast<FEUncoupledMaterial*>(m_pMat[i]);\n        if (uMat)\n            sed += uMat->DevStrainEnergyDensity(mpi)*w[i];\n        else\n            sed += m_pMat[i]->StrainEnergyDensity(mpi)*w[i];\n\t}\n    \n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEUncoupledElasticMixture::StrongBondDevSED(FEMaterialPoint& mp)\n{\n    FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n    vector<double>& w = pt.m_w;\n    assert(w.size() == m_pMat.size());\n    \n    // get the elastic material point\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate strain energy density\n    double sed = 0.0;\n    for (int i=0; i < (int)m_pMat.size(); ++i)\n    {\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem  = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt    = mp.m_rt;\n\t\tmpi.m_r0    = mp.m_r0;\n\n        // copy the elastic material point data to the components\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        epi.m_F = ep.m_F;\n        epi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n        \n        FEUncoupledMaterial* uMat = dynamic_cast<FEUncoupledMaterial*>(m_pMat[i]);\n        if (uMat)\n            sed += uMat->StrongBondDevSED(mpi)*w[i];\n        else\n            sed += m_pMat[i]->StrongBondSED(mpi)*w[i];\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEUncoupledElasticMixture::WeakBondDevSED(FEMaterialPoint& mp)\n{\n    FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n    vector<double>& w = pt.m_w;\n    assert(w.size() == m_pMat.size());\n    \n    // get the elastic material point\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate strain energy density\n    double sed = 0.0;\n    for (int i=0; i < (int)m_pMat.size(); ++i)\n    {\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem  = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt    = mp.m_rt;\n\t\tmpi.m_r0    = mp.m_r0;\n\n        // copy the elastic material point data to the components\n        FEElasticMaterialPoint& epi = *mpi.ExtractData<FEElasticMaterialPoint>();\n        epi.m_F = ep.m_F;\n        epi.m_J = ep.m_J;\n        epi.m_v = ep.m_v;\n        epi.m_a = ep.m_a;\n        epi.m_L = ep.m_L;\n        \n        FEUncoupledMaterial* uMat = dynamic_cast<FEUncoupledMaterial*>(m_pMat[i]);\n        if (uMat)\n            sed += uMat->WeakBondDevSED(*pt.GetPointData(i))*w[i];\n        else\n            sed += m_pMat[i]->WeakBondSED(*pt.GetPointData(i))*w[i];\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! specialized material points\nvoid FEUncoupledElasticMixture::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n    \n    for (int i=0; i < (int) m_pMat.size(); ++i)\n    {\n\t\tFEMaterialPoint& mpi = *pt.GetPointData(i);\n\t\tmpi.m_elem  = mp.m_elem;\n\t\tmpi.m_index = mp.m_index;\n\t\tmpi.m_rt    = mp.m_rt;\n\t\tmpi.m_r0    = mp.m_r0;\n\n        FEMaterial* pmj = GetMaterial(i);\n        pmj->UpdateSpecializedMaterialPoints(mpi, tp);\n    }\n}\n"
  },
  {
    "path": "FEBioMech/FEUncoupledElasticMixture.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEElasticMixture.h\"\n\n//-----------------------------------------------------------------------------\n//! Uncoupled elastic mixtures\n\n//! This class describes a mixture of uncoupled elastic solids.  The user must declare\n//! uncoupled elastic solids that can be combined within this class.  The stress and\n//! tangent tensors evaluated in this class represent the sum of the respective\n//! tensors of all the solids forming the mixture.\n//! \\todo This class defines two accessor interfaces. Modify to use the FEMaterial interface only.\n\nclass FEUncoupledElasticMixture : public FEUncoupledMaterial\n{\npublic:\n\tFEUncoupledElasticMixture(FEModel* pfem);\n\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// return number of materials\n\tint Materials() { return (int)m_pMat.size(); }\n\n\t// return a material component\n\tFEElasticMaterial* GetMaterial(int i) { return m_pMat[i]; }\n\n\t// Add a material component\n\tvoid AddMaterial(FEElasticMaterial* pm);\n\n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n\npublic:\n    //! data initialization\n    bool Init() override;\n    \n\t//! calculate stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\t\n\t//! calculate strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n\npublic:\n    double StrongBondDevSED(FEMaterialPoint& pt) override;\n    double WeakBondDevSED(FEMaterialPoint& pt) override;\n\nprivate:\n\t// TODO: temporarily reverted back to uncoupled materials. This was needed to make sure that \n\t//       FEBio Studio displays the uncoupled materials as options. \n\t//       Need to figure out a way to allow elastic materials again.\n\tstd::vector<FEUncoupledMaterial*>\tm_pMat;\t//!< pointers to elastic materials\n//\tstd::vector<FEElasticMaterial*>\tm_pMat;\t//!< pointers to elastic materials\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledFiberExpLinear.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledFiberExpLinear.h\"\n#include <stdlib.h>\n#include <limits>\n#include <FECore/expint_Ei.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFiberExpLinearUC, FEFiberMaterialUncoupled);\nADD_PARAMETER(m_c3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c3\");\nADD_PARAMETER(m_c4, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c4\");\nADD_PARAMETER(m_c5, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c5\");\nADD_PARAMETER(m_lam1, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lambda\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFiberExpLinearUC::FEFiberExpLinearUC(FEModel* pfem) : FEFiberMaterialUncoupled(pfem)\n{\n\tm_c3 = 0;\n\tm_c4 = 0;\n\tm_c5 = 0;\n\tm_lam1 = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Fiber material stress\nmat3ds FEFiberExpLinearUC::DevFiberStress(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0 / J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\tdouble twoJi = 2.0 * Ji;\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F * a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam = a.unit();\n\tdouble lamd = lam * Jm13; // i.e. lambda tilde\n\n\t// invariant I4\n\tdouble I4 = lamd * lamd;\n\n\t// calculate stress:\n\tmat3ds s; s.zero();\n\n\tif (lamd >= 1)\n\t{\n\t\tdouble c3 = m_c3(mp);\n\t\tdouble c4 = m_c4(mp);\n\t\tdouble c5 = m_c5(mp);\n\t\tdouble lam1 = m_lam1(mp);\n\n\t\t// calculate dyad of a: AxA = (a x a)\n\t\tmat3ds AxA = dyad(a);\n\n\t\tif (c3 == 0) {\n\t\t\tc3 = c5 / c4 * exp(-c4 * (lam1 - 1));\n\t\t}\n\n\t\t// calculate fiber stress\n\t\tdouble sn = 0.0;\n\t\tif (lamd < lam1)\n\t\t{\n\t\t\tsn = c3 * (exp(c4 * (lamd - 1.0)) - 1.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = c3 * (exp(c4 * (lam1 - 1)) - 1) - c5 * lam1;\n\t\t\tsn = c5 * lamd + c6;\n\t\t}\n\t\tmat3ds T = AxA * (sn / J);\n\t\ts = T.dev();\n\t}\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! Fiber material tangent\ntens4ds FEFiberExpLinearUC::DevFiberTangent(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\tdouble Ji = 1.0 / J;\n\n\t// calculate current local material axis\n\tvec3d a = F * a0;\n\n\tdouble lam = a.unit();\n\n\t// deviatoric stretch\n\tdouble lamd = lam * Jm13;\n\n\tdouble I4 = lamd * lamd;\n\n\tconst double eps = 0;// std::numeric_limits<double>::epsilon();\n\n\ttens4ds c; c.zero();\n\n\tif (lamd >= 1 + eps)\n\t{\n\t\tdouble c3 = m_c3(mp);\n\t\tdouble c4 = m_c4(mp);\n\t\tdouble c5 = m_c5(mp);\n\t\tdouble lam1 = m_lam1(mp);\n\n\t\tmat3dd I(1);    // Identity\n\t\ttens4ds IxI = dyad1s(I);\n\t\ttens4ds Id4 = dyad4s(I);\n\n\t\tmat3ds AxA = dyad(a);\n\t\ttens4ds AxAxAxA = dyad1s(AxA);\n\n\t\tif (c3 == 0) {\n\t\t\tc3 = c5 / c4 * exp(-c4 * (lam1 - 1));\n\t\t}\n\n\t\tdouble sn = 0;\n\t\tdouble cn = 0;\n\t\tif (lamd < lam1) {\n\t\t\tsn = c3 * (exp(c4 * (lamd - 1.0)) - 1.0);\n\t\t\tcn = c3 * (2 + exp(c4 * (lamd - 1)) * (c4 * lamd - 2));\n\t\t}\n\t\telse {\n\t\t\tdouble c6 = c3 * (exp(c4 * (lam1 - 1)) - 1) - c5 * lam1;\n\t\t\tsn = c5 * lamd + c6;\n\t\t\tcn = -c5 * lamd - 2 * c6;\n\t\t}\n\t\tmat3ds T = AxA * (sn / J);\n\t\tc = AxAxAxA * (cn / J);\n\t\tc += -1. / 3. * (ddots(c, IxI) - IxI * (c.tr() / 3.))\n\t\t\t+ 2. / 3. * ((Id4 - IxI / 3.) * T.tr() - dyad1s(T.dev(), I));\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! Fiber material strain energy density\ndouble FEFiberExpLinearUC::DevFiberStrainEnergyDensity(FEMaterialPoint& mp, const vec3d& a0)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the deformation gradient\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Jm13 = pow(J, -1.0 / 3.0);\n\n\t// calculate the current material axis lam*a = F*a0;\n\tvec3d a = F * a0;\n\n\t// normalize material axis and store fiber stretch\n\tdouble lam, lamd;\n\tlam = a.unit();\n\tlamd = lam * Jm13; // i.e. lambda tilde\n\n\t// strain energy density\n\tdouble sed = 0.0;\n\tif (lamd >= 1)\n\t{\n\t\tdouble c3 = m_c3(mp);\n\t\tdouble c4 = m_c4(mp);\n\t\tdouble c5 = m_c5(mp);\n\t\tdouble lam1 = m_lam1(mp);\n\n\t\tif (c3 == 0) c3 = c5 / c4 * exp(-c4 * (lam1 - 1));\n\n\t\tif (lamd < lam1)\n\t\t{\n\t\t\tsed = c3 * exp(-c4) * (expint_Ei(c4 * lamd) - expint_Ei(c4)) - c3 * log(lamd);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble c6 = c3 * (exp(c4 * (lam1 - 1)) - 1) - c5 * lam1;\n\t\t\tsed = c5 * (lamd - lam1) + c6 * log(lamd / lam1)\n\t\t\t\t+ c3 * exp(-c4) * (expint_Ei(c4 * lam1) - expint_Ei(c4)) - c3 * log(lam1);\n\t\t}\n\t}\n\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEUncoupledFiberExpLinear, FEElasticFiberMaterialUC);\nADD_PARAMETER(m_fib.m_c3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c3\");\nADD_PARAMETER(m_fib.m_c4, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c4\");\nADD_PARAMETER(m_fib.m_c5, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c5\");\nADD_PARAMETER(m_fib.m_lam1, FE_RANGE_GREATER_OR_EQUAL(1.0), \"lambda\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMech/FEUncoupledFiberExpLinear.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEFiberMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Uncoupled formulation of the fiber-exp-linear material for use with uncoupled\n//! solid mixtures.\nclass FEFiberExpLinearUC : public FEFiberMaterialUncoupled\n{\npublic:\n\t//! Constructor\n\tFEFiberExpLinearUC(FEModel* pfem);\n\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevFiberStress(FEMaterialPoint& pt, const vec3d& n0) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevFiberTangent(FEMaterialPoint& pt, const vec3d& n0) override;\n\n\t//! calculate deviatoric strain energy density at material point\n\tdouble DevFiberStrainEnergyDensity(FEMaterialPoint& pt, const vec3d& n0) override;\n\npublic:\n    FEParamDouble   m_c3;        //!< Exponential stress coefficient\n    FEParamDouble   m_c4;       //!< fiber uncrimping coefficient\n    FEParamDouble   m_c5;       //!< modulus of straightened fibers\n    FEParamDouble   m_lam1;        //!< fiber stretch for straightened fibers\n\n\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEUncoupledFiberExpLinear : public FEElasticFiberMaterialUC_T<FEFiberExpLinearUC>\n{\npublic: \n\tFEUncoupledFiberExpLinear(FEModel* fem) : FEElasticFiberMaterialUC_T<FEFiberExpLinearUC>(fem) {}\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// Material parameters for FEUncoupledMaterial\nBEGIN_FECORE_CLASS(FEUncoupledMaterial, FEElasticMaterial)\n\tADD_PARAMETER(m_K      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\")->setUnits(UNIT_PRESSURE)->MakeTopLevel(true)->setLongName(\"bulk modulus\");\n    ADD_PARAMETER(m_npmodel, \"pressure_model\")->setEnums(\"default\\0NIKE3D\\0Abaqus\\0Abaqus (GOH)\\0\")->MakeTopLevel(true);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEUncoupledMaterial::FEUncoupledMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_K = 0;\t// invalid value!\n    m_npmodel = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEUncoupledMaterial::Init()\n{\n\t// K has to be non-zero if this is a top-level material, otherwise\n\t// it should be zero. \n\tFECoreBase* parent = GetParent();\n\tFEUncoupledMaterial* parentMat = dynamic_cast<FEUncoupledMaterial*>(GetParent());\n\tif (parentMat)\n\t{\n\t\tif (m_K != 0.0)\n\t\t{\n\t\t\tfeLogWarning(\"K should only be defined at the top-level.\\nValue will be ignored.\");\n\n\t\t\t// NOTE: This should not be neccessary, but just to be safe. \n\t\t\tm_K = 0.0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (m_K <= 0.0)\n\t\t{\n\t\t\tfeLogError(\"K must be a positive number.\");\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn FEElasticMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! The stress function calculates the total Cauchy stress as a sum of \n//! two terms, namely the deviatoric stress and the pressure. \nmat3ds FEUncoupledMaterial::Stress(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// calculate the stress as a sum of deviatoric stress and pressure\n    pt.m_p = UJ(pt.m_J);\n\treturn mat3dd(pt.m_p) + DevStress(mp);\n}\n\n//------------------------------------------------------------------------------\n//! The tangent function calculates the total spatial tangent, that is it calculates\n//! the push-forward of the derivative of the 2ndPK stress with respect to C. However,\n//! for an uncoupled material, the 2ndPK stress decouples in a deviatoric and a \n//! dilatational component. The deviatoric tangent is provided by the particular\n//! material and the dilatational component is added here.\n//!\ntens4ds FEUncoupledMaterial::Tangent(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// 2nd-order identity tensor\n\tmat3dd I(1);\n\n\t// 4th-order identity tensors\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\t\n\t// pressure\n\tpt.m_p = UJ(pt.m_J);\n\t\n\t// tangent is sum of three terms\n\t// C = c_tilde + c_pressure + c_k\n\t//\n\t// + c_tilde is the derivative of the deviatoric stress with respect to C\n\t// + c_pressure is p*d(JC)/dC\n\t// + c_k comes from the derivative of p with respect to C\n\t// \n\t// Note that the c_k term is not necessary in the 3F formulation (since p is independant variable) \n\t// but we do need to add it here.\n\t//\n\t//        c_tilde         c_pressure            c_k\n\treturn DevTangent(mp) + (IxI - I4*2)*pt.m_p + IxI*(UJJ(pt.m_J)*pt.m_J);\n}\n\n//-----------------------------------------------------------------------------\n//! The strain energy density function calculates the total sed as a sum of\n//! two terms, namely the deviatoric sed and U(J).\ndouble FEUncoupledMaterial::StrainEnergyDensity(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate the stress as a sum of deviatoric stress and pressure\n\treturn U(pt.m_J) + DevStrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! The strain energy density function calculates the total sed as a sum of\n//! two terms, namely the deviatoric sed and U(J).\ndouble FEUncoupledMaterial::StrongBondSED(FEMaterialPoint &mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // calculate the stress as a sum of deviatoric stress and pressure\n    return U(pt.m_J) + StrongBondDevSED(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! The strain energy density function calculates the total sed as a sum of\n//! two terms, namely the deviatoric sed and U(J).\ndouble FEUncoupledMaterial::WeakBondSED(FEMaterialPoint &mp)\n{\n    // calculate the stress as a sum of deviatoric stress and pressure\n    return WeakBondDevSED(mp);\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEUncoupledMaterial::CreateMaterialPointData()\n{\n\tFEElasticMaterialPoint* mp = new FEElasticMaterialPoint;\n\tmp->m_buncoupled = true;\n\treturn mp;\n}\n"
  },
  {
    "path": "FEBioMech/FEUncoupledMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include \"febiomech_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for uncoupled hyperelastic material formulations.\n\n//! In FEBio, for uncoupled materials it is assumed that the strain energy function \n//! is a sum of two terms, a deviatoric strain energy term, which only depends on the \n//! deviatoric right Cauchy-Green tensor C_tilde, and a volumetric term which only \n//! depends on J, the determinant of the deformation gradient. The total Cauchy stress \n//! is therefore also a sum of two contributions, namely the deviatoric Cauchy stress\n//! and a pressure term where p = dU/dJ. \n\n//! One of the main motivations for the alternative material interface is that some\n//! finite element implementations can take advantage of the uncoupling. For example,\n//! the three-field formulation integrates the two different material terms differently\n//! in order to avoid locking problems for (nearly-) incompressible materials. \n\n//! When implementing a new material derived from this base class, the developer needs\n//! to provide the deviatoric stress function and the pressure function as well as their\n//! derivatives. \n\nclass FEBIOMECH_API FEUncoupledMaterial : public FEElasticMaterial\n{\npublic:\n\t//! constructor\n\tFEUncoupledMaterial(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\npublic:\n\n//----------------->\n\t// The following functions need to be overloaded\n\t// for each material derived from this class.\n\n\t//! Deviatoric Cauchy stress\n\tvirtual mat3ds DevStress(FEMaterialPoint& mp) = 0;\n\n\t//! Deviatoric spatial Tangent\n\tvirtual tens4ds DevTangent(FEMaterialPoint& mp) = 0;\n\n\t//! Deviatoric strain energy density\n\tvirtual double DevStrainEnergyDensity(FEMaterialPoint& mp) { return 0; }\n    \npublic:\n    virtual double StrongBondDevSED(FEMaterialPoint& pt) { return DevStrainEnergyDensity(pt); }\n    virtual double WeakBondDevSED(FEMaterialPoint& pt) { return 0; }\n\npublic:\n    // TODO: removing virtual from the following 3 functions causes changes\n    // to the convergence criteria on macOS, despite these functions not\n    // being overridden anywhere.\n\t//! strain energy density U(J)\n    virtual double U(double J) {\n        switch (m_npmodel) {\n            case 0: return 0.5*m_K*pow(log(J),2); break;    // FEBio default\n            case 1: return 0.25*m_K*(J*J - 2.0*log(J) - 1.0); break;    // NIKE3D's Ogden material\n            case 2: return 0.5*m_K*(J-1)*(J-1); break;      // ABAQUS\n            case 3: return 0.5*m_K*((J*J-1)/2-log(J)); break;      // ABAQUS - GOH\n            default: { assert(false); return 0; }\n        }\n    }\n\t//! pressure, i.e. first derivative of U(J)\n\tvirtual double UJ(double J) {\n        switch (m_npmodel) {\n            case 0: return m_K*log(J)/J; break;\n            case 1: return 0.5*m_K*(J - 1.0/J); break;\n            case 2: return m_K*(J-1); break;\n            case 3: return 0.5*m_K*(J-1.0/J); break;\n\t\t\tdefault: { assert(false); return 0; }\n\t\t}\n    }\n\n\t//! second derivative of U(J) \n\tvirtual double UJJ(double J) {\n        switch (m_npmodel) {\n            case 0: return m_K*(1-log(J))/(J*J); break;\n            case 1: return 0.5*m_K*(1 + 1.0/(J*J)); break;\n            case 2: return m_K; break;\n            case 3: return 0.5*m_K*(1+1.0/(J*J)); break;\n\t\t\tdefault: { assert(false); return 0; }\n\t\t}\n    }\n\npublic:\n\t// incompressibility constraint fnc and derivs\n\tdouble h  (double J) { return log(J); }\n\tdouble hp (double J) { return 1.0 / J; }\n\tdouble hpp(double J) { return -1.0 / (J*J); }\n\npublic:\n\t//! total Cauchy stress (do not overload!)\n\tmat3ds Stress(FEMaterialPoint& mp) final;\n\n\t//! total spatial tangent (do not overload!)\n\ttens4ds Tangent(FEMaterialPoint& mp) final;\n\n\t//! calculate strain energy (do not overload!)\n\tdouble StrainEnergyDensity(FEMaterialPoint& pt) final;\n    double StrongBondSED(FEMaterialPoint& pt) final;\n    double WeakBondSED(FEMaterialPoint& pt) final;\n\n\t// Create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \npublic:\n\tdouble\tm_K;\t\t\t//!< bulk modulus\n\tint     m_npmodel;      //!< pressure model for U(J)\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FEUncoupledMaterial)\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledReactiveFatigue.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledReactiveFatigue.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/DumpStream.h>\n#include <FECore/log.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEModel.h>\n\n//////////////////////////// FATIGUE MATERIAL /////////////////////////////////\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledReactiveFatigue, FEUncoupledMaterial)\nADD_PARAMETER(m_k0   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"k0\"  );\nADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(0.0), \"beta\");\n\n// set material properties\nADD_PROPERTY(m_pBase, \"elastic\");\nADD_PROPERTY(m_pIdmg, \"elastic_damage\");\nADD_PROPERTY(m_pFdmg, \"fatigue_damage\");\nADD_PROPERTY(m_pIcrt, \"elastic_criterion\");\nADD_PROPERTY(m_pFcrt, \"fatigue_criterion\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEUncoupledReactiveFatigue::FEUncoupledReactiveFatigue(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n    m_pBase = 0;\n    m_pIdmg = 0;\n    m_pFdmg = 0;\n    m_pIcrt = 0;\n    m_pFcrt = 0;\n    \n    m_k0 = 0;\n    m_beta = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEUncoupledReactiveFatigue::CreateMaterialPointData()\n{\n\treturn new FEReactiveFatigueMaterialPoint(m_pBase->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEUncoupledReactiveFatigue::Init()\n{\n    return FEUncoupledMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! calculate stress at material point\nmat3ds FEUncoupledReactiveFatigue::DevStress(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the stress\n    mat3ds s = m_pBase->DevStress(pt);\n    \n    // return damaged stress\n    return s*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent stiffness at material point\ntens4ds FEUncoupledReactiveFatigue::DevTangent(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the tangent\n    tens4ds c = m_pBase->DevTangent(pt);\n    \n    // return damaged tangent\n    return c*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEUncoupledReactiveFatigue::DevStrainEnergyDensity(FEMaterialPoint& pt)\n{\n    // evaluate the damage\n    double d = Damage(pt);\n    \n    // evaluate the strain energy density\n    double sed = m_pBase->DevStrainEnergyDensity(pt);\n    \n    // return damaged sed\n    return sed*(1-d);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate damage at material point\ndouble FEUncoupledReactiveFatigue::Damage(FEMaterialPoint& pt)\n{\n    // get the reactive fatigue material point data\n    FEReactiveFatigueMaterialPoint& pd = *pt.ExtractData<FEReactiveFatigueMaterialPoint>();\n    \n    return pd.m_D;\n}\n\n//-----------------------------------------------------------------------------\n// update fatigue material point at each iteration\nvoid FEUncoupledReactiveFatigue::UpdateSpecializedMaterialPoints(FEMaterialPoint& pt, const FETimeInfo& tp)\n{\n    double dt = tp.timeIncrement;\n    double k0 = m_k0(pt);\n    double beta = m_beta(pt);\n\n    // get the fatigue material point data\n    FEReactiveFatigueMaterialPoint& pd = *pt.ExtractData<FEReactiveFatigueMaterialPoint>();\n    \n    // get damage criterion for intact bonds at current time\n    pd.m_Xitrl = m_pIcrt->DamageCriterion(pt);\n    if (pd.m_Xitrl > pd.m_Ximax)\n        pd.m_Fit = m_pIdmg->cdf(pt,pd.m_Xitrl);\n    else\n        pd.m_Fit = pd.m_Fip;\n    \n    // get damage criterion for fatigue bonds at current time\n    double Xftrl = m_pFcrt->DamageCriterion(pt);\n    for (int ig=0; ig < pd.m_fb.size(); ++ig) {\n        if (Xftrl > pd.m_fb[ig].m_Xfmax) {\n            pd.m_fb[ig].m_Xftrl = Xftrl;\n            pd.m_fb[ig].m_Fft = m_pFdmg->cdf(pt,pd.m_fb[ig].m_Xftrl);\n        }\n        else\n            pd.m_fb[ig].m_Fft = pd.m_fb[ig].m_Ffp;\n    }\n    \n    // evaluate time derivative of intact bond criterion\n    pd.m_aXit = (pd.m_Xitrl - pd.m_Xip)/dt;\n    \n    // solve for bond mass fractions iteratively\n    double eps = 1e-6;\n    int maxit = 10;\n    double wbi = 0;\n    int iter = 0;\n    do {\n        wbi = pd.m_wbt;\n        // get current and previous k value\n        double kn1 = k0*(pow(fabs(pd.m_aXit)*pd.m_wbt,beta));\n        double kn = k0*(pow(fabs(pd.m_aXip)*pd.m_wbp,beta));\n        // evaluate mass supply from fatigue of intact bonds\n        double dwf = ((kn1*dt + kn*dt)/(2 + kn1*dt))*pd.m_wip;\n        double Fdwf = m_pFdmg->cdf(pt,Xftrl);\n        \n        // kinetics of intact bonds\n        pd.m_wit = (pd.m_Fip < 1) ? (pd.m_wip - dwf)*(1-pd.m_Fit)/(1-pd.m_Fip) : pd.m_wip;\n        pd.m_wbt = (pd.m_Fip < 1) ? pd.m_wbp + (pd.m_wip - dwf)*(pd.m_Fit - pd.m_Fip)/(1-pd.m_Fip) : pd.m_wbp;\n        // add or update new generation\n        if ((pd.m_fb.size() == 0) || pd.m_fb.back().m_time < tp.currentTime) {\n            // add generation of fatigued bonds\n            FatigueBond fb;\n            fb.m_Fft = Fdwf;\n            fb.m_wfp = dwf;\n            fb.m_Xftrl = Xftrl;\n            fb.m_time = tp.currentTime;\n            pd.m_fb.push_back(fb);\n        }\n        else {\n            pd.m_fb.back().m_Fft = Fdwf;\n            pd.m_fb.back().m_wfp = dwf;\n            pd.m_fb.back().m_Xftrl = Xftrl;\n        }\n        // damage kinetics of fatigued bonds\n        for (int ig=0; ig < pd.m_fb.size(); ++ig) {\n            pd.m_fb[ig].m_wft = (pd.m_fb[ig].m_Ffp < 1) ? pd.m_fb[ig].m_wfp*(1-pd.m_fb[ig].m_Fft)/(1-pd.m_fb[ig].m_Ffp) : 0;\n            pd.m_wbt += (pd.m_fb[ig].m_Ffp < 1) ? pd.m_fb[ig].m_wfp*(pd.m_fb[ig].m_Fft-pd.m_fb[ig].m_Ffp)/(1-pd.m_fb[ig].m_Ffp) : pd.m_fb[ig].m_wfp;\n        }\n        // roundoff corrections\n        if (pd.m_wit < 0) pd.m_wit = 0;\n        if (pd.m_wbt > 1) pd.m_wbt = 1;\n        // evaluate fatigue bond fraction\n        pd.m_wft = 0;\n        for (int ig=0; ig < pd.m_fb.size(); ++ig) pd.m_wft += pd.m_fb[ig].m_wft;\n    } while ((pd.m_wbt > 0) && (pd.m_wbt < 1) && (fabs(wbi-pd.m_wbt) > eps*pd.m_wbt) && (++iter<maxit));\n    \n    pd.m_D = pd.m_wbt;\n}\n"
  },
  {
    "path": "FEBioMech/FEUncoupledReactiveFatigue.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEDamageCriterion.h\"\n#include \"FEDamageCDF.h\"\n#include \"FEReactiveFatigueMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\n// This material models fatigue and damage in any hyper-elastic materials.\n\nclass FEUncoupledReactiveFatigue : public FEUncoupledMaterial\n{\npublic:\n    FEUncoupledReactiveFatigue(FEModel* pfem);\n    \npublic:\n    //! calculate stress at material point\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! damage\n    double Damage(FEMaterialPoint& pt);\n    \n    //! data initialization and checking\n    bool Init() override;\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n    \n    // get the elastic material\n    FEElasticMaterial* GetElasticMaterial() override { return m_pBase; }\n    \n    // update fatigue material point at each iteration\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic:\n    FEUncoupledMaterial*    m_pBase;    // base uncoupled material\n    FEDamageCDF*            m_pIdmg;    // damage model for intact bonds\n    FEDamageCDF*            m_pFdmg;    // damage model for fatigued bonds\n    FEDamageCriterion*      m_pIcrt;    // damage criterion\n    FEDamageCriterion*      m_pFcrt;    // fatigue criterion\n    \npublic:\n    FEParamDouble           m_k0;       // reaction rate for fatigue reaction\n    FEParamDouble           m_beta;     // power exponent for fatigue reaction\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledReactiveViscoelastic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledReactiveViscoelastic.h\"\n#include \"FEUncoupledElasticMixture.h\"\n#include \"FEFiberMaterialPoint.h\"\n#include \"FEElasticFiberMaterialUC.h\"\n#include \"FEScaledUncoupledMaterial.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/log.h>\n#include <limits>\n\n///////////////////////////////////////////////////////////////////////////////\n//\n// FEUncoupledReactiveViscoelasticMaterial\n//\n///////////////////////////////////////////////////////////////////////////////\n\n// Material parameters for the FEUncoupledReactiveViscoelastic material\nBEGIN_FECORE_CLASS(FEUncoupledReactiveViscoelasticMaterial, FEUncoupledMaterial)\n\tADD_PARAMETER(m_wmin , FE_RANGE_CLOSED(0.0, 1.0), \"wmin\");\n\tADD_PARAMETER(m_btype, FE_RANGE_CLOSED(1, 2), \"kinetics\")->setEnums(\"(invalid)\\0Type I\\0Type II\\0\");\n\tADD_PARAMETER(m_ttype, FE_RANGE_CLOSED(0, 2), \"trigger\" )->setEnums(\"any strain\\0distortional\\0dilatational\\0\");\n    ADD_PARAMETER(m_emin , FE_RANGE_GREATER_OR_EQUAL(0.0), \"emin\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pBase, \"elastic\");\n    ADD_PROPERTY(m_pBond, \"bond\");\n\tADD_PROPERTY(m_pRelx, \"relaxation\");\n    ADD_PROPERTY(m_pWCDF, \"recruitment\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEUncoupledReactiveViscoelasticMaterial::FEUncoupledReactiveViscoelasticMaterial(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n    m_wmin = 0;\n    m_btype = 1;\n    m_ttype = 0;\n    m_emin = 0;\n\n    m_nmax = 0;\n\n    m_pBase = nullptr;\n    m_pBond = nullptr;\n    m_pRelx = nullptr;\n    m_pWCDF = nullptr;\n    \n    m_pDmg = nullptr;\n    m_pFtg = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEUncoupledReactiveViscoelasticMaterial::Init()\n{\n    if (!m_pBase->Init()) return false;\n    if (!m_pBond->Init()) return false;\n    if (!m_pRelx->Init()) return false;\n    if (m_pWCDF && !m_pWCDF->Init()) return false;\n    \n    m_pDmg = dynamic_cast<FEDamageMaterialUC*>(m_pBase);\n    m_pFtg = dynamic_cast<FEUncoupledReactiveFatigue*>(m_pBase);\n\n    return FEUncoupledMaterial::Init();\n}\n\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEUncoupledReactiveViscoelasticMaterial::CreateMaterialPointData()\n{\n    FEReactiveViscoelasticMaterialPoint* pt = new FEReactiveViscoelasticMaterialPoint();\n    // create materal point for strong bond (base) material\n    FEMaterialPointData* pbase = m_pBase->CreateMaterialPointData();\n    pt->AddMaterialPoint(new FEMaterialPoint(pbase));\n\n    // create materal point for weak bond material\n    FEReactiveVEMaterialPoint* pbond = new FEReactiveVEMaterialPoint(m_pBond->CreateMaterialPointData());\n    pt->AddMaterialPoint(new FEMaterialPoint(pbond));\n    \n    return pt;\n}\n\n//-----------------------------------------------------------------------------\n//! get base material point\nFEMaterialPoint* FEUncoupledReactiveViscoelasticMaterial::GetBaseMaterialPoint(FEMaterialPoint& mp)\n{\n    // get the reactive viscoelastic point data\n    FEReactiveViscoelasticMaterialPoint& rvp = *mp.ExtractData<FEReactiveViscoelasticMaterialPoint>();\n    \n    // get the elastic material point\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // extract the strong bond material point data\n    FEMaterialPoint* sb = rvp.GetPointData(0);\n    sb->m_elem = mp.m_elem;\n    sb->m_index = mp.m_index;\n\tsb->m_rt = mp.m_rt;\n\tsb->m_r0 = mp.m_r0;\n\n    // copy the elastic material point data to the strong bond component\n    FEElasticMaterialPoint& epi = *sb->ExtractData<FEElasticMaterialPoint>();\n    epi.m_F = ep.m_F;\n    epi.m_J = ep.m_J;\n    epi.m_v = ep.m_v;\n    epi.m_a = ep.m_a;\n    epi.m_L = ep.m_L;\n    \n    return sb;\n}\n\n//-----------------------------------------------------------------------------\n//! get bond material point\nFEMaterialPoint* FEUncoupledReactiveViscoelasticMaterial::GetBondMaterialPoint(FEMaterialPoint& mp)\n{\n    // get the reactive viscoelastic point data\n    FEReactiveViscoelasticMaterialPoint& rvp = *mp.ExtractData<FEReactiveViscoelasticMaterialPoint>();\n    \n    // get the elastic material point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // extract the weak bond material point data\n    FEMaterialPoint* wb = rvp.GetPointData(1);\n    wb->m_elem = mp.m_elem;\n    wb->m_index = mp.m_index;\n\twb->m_rt = mp.m_rt;\n\twb->m_r0 = mp.m_r0;\n\n    // copy the elastic material point data to the weak bond component\n    FEElasticMaterialPoint& epi = *wb->ExtractData<FEElasticMaterialPoint>();\n    epi.m_F = ep.m_F;\n    epi.m_J = ep.m_J;\n    epi.m_v = ep.m_v;\n    epi.m_a = ep.m_a;\n    epi.m_L = ep.m_L;\n    \n    return wb;\n}\n\n//-----------------------------------------------------------------------------\n//! detect new generation\nbool FEUncoupledReactiveViscoelasticMaterial::NewGeneration(FEMaterialPoint& mp)\n{\n    double d;\n    double eps = max(m_emin, 10*std::numeric_limits<double>::epsilon());\n\n    // check if the reforming bond mass fraction is above the minimum threshold wmin\n    if (ReformingBondMassFraction(mp) < m_wmin) return false;\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n        \n    // check if the current deformation gradient is different from that of\n    // the last generation, in which case store the current state\n    // evaluate the relative deformation gradient\n    mat3d F = ep.m_F;\n    int lg = (int)pt.m_Uv.size() - 1;\n    mat3ds Ui = (lg > -1) ? pt.m_Uv[lg].inverse() : mat3dd(1);\n    mat3d Fu = F*Ui;\n    \n    switch (m_ttype) {\n        case 0:\n        {\n            // trigger in response to any strain\n            // evaluate the Lagrangian strain\n            mat3ds E = ((Fu.transpose()*Fu).sym() - mat3dd(1))/2;\n            \n            d = E.norm();\n        }\n            break;\n        case 1:\n        {\n            // trigger in response to distortional strain\n            // evaluate spatial Hencky (logarithmic) strain\n            mat3ds Bu = (Fu*Fu.transpose()).sym();\n            double l[3];\n            vec3d v[3];\n            Bu.eigen2(l,v);\n            mat3ds h = (dyad(v[0])*log(l[0]) + dyad(v[1])*log(l[1]) + dyad(v[2])*log(l[2]))/2;\n            \n            // evaluate distortion magnitude (always positive)\n            d = (h.dev()).norm();\n        }\n            break;\n        case 2:\n        {\n            // trigger in response to dilatational strain\n            d = fabs(log(Fu.det()));\n        }\n            break;\n            \n        default:\n            d = 0;\n            break;\n    }\n    \n    if (d > eps) return true;\n    \n    return false;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bond mass fraction\ndouble FEUncoupledReactiveViscoelasticMaterial::BreakingBondMassFraction(FEMaterialPoint& mp, const int ig, const mat3ds D)\n{\n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // bond mass fraction\n    double w = 0;\n    \n    // current time\n    double time = CurrentTime();\n    double dtv = time - pt.m_v[ig];\n\n    switch (m_btype) {\n        case 1:\n        {\n            if (dtv >= 0)\n                w = pt.m_f[ig]*m_pRelx->Relaxation(mp, dtv, D);\n        }\n            break;\n        case 2:\n        {\n            if (ig == 0) {\n                w = m_pRelx->Relaxation(mp, dtv, D);\n            }\n            else\n            {\n                double dtu = time - pt.m_v[ig-1];\n                w = m_pRelx->Relaxation(mp, dtv, D) - m_pRelx->Relaxation(mp, dtu, D);\n            }\n        }\n            break;\n            \n        default:\n            break;\n    }\n    \n    assert(w >= 0);\n    \n    return w;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bond mass fraction of reforming generation\ndouble FEUncoupledReactiveViscoelasticMaterial::ReformingBondMassFraction(FEMaterialPoint& mp)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n    \n    // keep safe copy of deformation gradient\n    mat3d F = ep.m_F;\n    double J = ep.m_J;\n    \n    // get current number of generations\n    int ng = (int)pt.m_Uv.size();\n    \n    double f = 1;\n\n    for (int ig=0; ig<ng-1; ++ig)\n    {\n        // evaluate deformation gradient when this generation starts breaking\n        ep.m_F = pt.m_Uv[ig];\n        ep.m_J = pt.m_Jv[ig];\n        // evaluate the breaking bond mass fraction for this generation\n        f -= BreakingBondMassFraction(mp, ig, D);\n    }\n    \n    // restore safe copy of deformation gradient\n    ep.m_F = F;\n    ep.m_J = J;\n    \n    assert(f >= 0);\n    \n    // return the bond mass fraction of the reforming generation\n    return f;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function in strong bonds\nmat3ds FEUncoupledReactiveViscoelasticMaterial::DevStressStrongBonds(FEMaterialPoint& mp)\n{\n    mat3ds s = m_pBase->DevStress(*GetBaseMaterialPoint(mp));\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function in weak bonds\nmat3ds FEUncoupledReactiveViscoelasticMaterial::DevStressWeakBonds(FEMaterialPoint& mp)\n{\n    double dt = CurrentTimeIncrement();\n    if (dt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n    \n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n    // get fiber material point data (if it exists)\n    FEFiberMaterialPoint* fp = wb.ExtractData<FEFiberMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n\n    // calculate the base material Cauchy stress\n    mat3ds s; s.zero();\n    \n    // current number of breaking generations\n    int ng = (int)pt.m_Uv.size();\n    \n    // no bonds have broken\n    if (ng == 0) {\n        s += m_pBond->DevStress(wb);\n    }\n    // bonds have broken\n    else {\n        // keep safe copy of deformation gradient\n        mat3d F = ep.m_F;\n        double J = ep.m_J;\n\n        double w;\n        mat3ds sb;\n        \n        // calculate the bond stresses for breaking generations\n        for (int ig=0; ig<ng; ++ig) {\n            // evaluate bond mass fraction for this generation\n            ep.m_F = pt.m_Uv[ig];\n            ep.m_J = pt.m_Jv[ig];\n            w = BreakingBondMassFraction(wb, ig, D)*pt.m_wv[ig];\n            // evaluate relative deformation gradient for this generation\n            if (ig > 0) {\n                ep.m_F = F*pt.m_Uv[ig-1].inverse();\n                ep.m_J = J/pt.m_Jv[ig-1];\n                if (fp) fp->SetPreStretch(pt.m_Uv[ig-1]);\n            }\n            else {\n                ep.m_F = F;\n                ep.m_J = J;\n                if (fp) fp->ResetPreStretch();\n            }\n            // evaluate bond stress\n            sb = m_pBond->DevStress(wb);\n            // add bond stress to total stress\n            s += (ig > 0) ? sb*w/pt.m_Jv[ig-1] : sb*w;\n        }\n        \n        // restore safe copy of deformation gradient\n        ep.m_F = F;\n        ep.m_J = J;\n    }\n    \n    ep.m_s = s;\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FEUncoupledReactiveViscoelasticMaterial::DevStress(FEMaterialPoint& mp)\n{\n    // calculate the base material Cauchy stress\n    mat3ds s = DevStressStrongBonds(mp);\n    s+= DevStressWeakBonds(mp)*(1-Damage(mp));\n    \n    // return the total Cauchy stress\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent in strong bonds\ntens4ds FEUncoupledReactiveViscoelasticMaterial::DevTangentStrongBonds(FEMaterialPoint& mp)\n{\n     // calculate the base material tangent\n    return m_pBase->DevTangent(*GetBaseMaterialPoint(mp));\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent in weak bonds\ntens4ds FEUncoupledReactiveViscoelasticMaterial::DevTangentWeakBonds(FEMaterialPoint& mp)\n{\n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n\n    // get fiber material point data (if it exists)\n    FEFiberMaterialPoint* fp = wb.ExtractData<FEFiberMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n\n    // calculate the base material tangent\n    tens4ds c; c.zero();\n    \n    // current number of breaking generations\n    int ng = (int)pt.m_Uv.size();\n    \n    // no bonds have broken\n    if (ng == 0) {\n        c += m_pBond->DevTangent(wb);\n    }\n    // bonds have broken\n    else {\n        // keep safe copy of deformation gradient\n        mat3d F = ep.m_F;\n        double J = ep.m_J;\n        \n        double w;\n        tens4ds cb;\n        \n        // calculate the bond tangents for breaking generations\n        for (int ig=0; ig<ng; ++ig) {\n            // evaluate bond mass fraction for this generation\n            ep.m_F = pt.m_Uv[ig];\n            ep.m_J = pt.m_Jv[ig];\n            w = BreakingBondMassFraction(wb, ig, D)*pt.m_wv[ig];\n            // evaluate relative deformation gradient for this generation\n            if (ig > 0) {\n                ep.m_F = F*pt.m_Uv[ig-1].inverse();\n                ep.m_J = J/pt.m_Jv[ig-1];\n                if (fp) fp->SetPreStretch(pt.m_Uv[ig-1]);\n            }\n            else {\n                ep.m_F = F;\n                ep.m_J = J;\n                if (fp) fp->ResetPreStretch();\n            }\n            // evaluate bond tangent\n            cb = m_pBond->DevTangent(wb);\n            // add bond tangent to total tangent\n            c += (ig > 0) ? cb*w/pt.m_Jv[ig-1] : cb*w;\n        }\n        \n        // restore safe copy of deformation gradient\n        ep.m_F = F;\n        ep.m_J = J;\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent\ntens4ds FEUncoupledReactiveViscoelasticMaterial::DevTangent(FEMaterialPoint& mp)\n{\n    tens4ds c = DevTangentStrongBonds(mp);\n    c+= DevTangentWeakBonds(mp)*(1-Damage(mp));\n    \n    // return the total tangent\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function for weak bonds\ndouble FEUncoupledReactiveViscoelasticMaterial::StrongBondDevSED(FEMaterialPoint& mp)\n{\n    // calculate the base material deviatoric strain energy density\n    return m_pBase->DevStrainEnergyDensity(*GetBaseMaterialPoint(mp));\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function\ndouble FEUncoupledReactiveViscoelasticMaterial::WeakBondDevSED(FEMaterialPoint& mp)\n{\n    double dt = CurrentTimeIncrement();\n    if (dt == 0) return 0;\n    \n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n\n    // get fiber material point data (if it exists)\n    FEFiberMaterialPoint* fp = wb.ExtractData<FEFiberMaterialPoint>();\n    \n    // get the viscous point data\n    mat3ds D = ep.RateOfDeformation();\n\n    double sed = 0;\n    \n    // current number of breaking generations\n    int ng = (int)pt.m_Uv.size();\n    \n    // no bonds have broken\n    if (ng == 0) {\n        sed += m_pBond->DevStrainEnergyDensity(wb);\n    }\n    // bonds have broken\n    else {\n        // keep safe copy of deformation gradient\n        mat3d F = ep.m_F;\n        double J = ep.m_J;\n        \n        double w;\n        double sedb;\n        \n        // calculate the strain energy density for breaking generations\n        for (int ig=0; ig<ng; ++ig) {\n            // evaluate bond mass fraction for this generation\n            ep.m_F = pt.m_Uv[ig];\n            ep.m_J = pt.m_Jv[ig];\n            w = BreakingBondMassFraction(wb, ig, D)*pt.m_wv[ig];\n            // evaluate relative deformation gradient for this generation\n            if (ig > 0) {\n                ep.m_F = F*pt.m_Uv[ig-1].inverse();\n                ep.m_J = J/pt.m_Jv[ig-1];\n                if (fp) fp->SetPreStretch(pt.m_Uv[ig-1]);\n            }\n            else {\n                ep.m_F = F;\n                ep.m_J = J;\n                if (fp) fp->ResetPreStretch();\n            }\n            // evaluate bond strain energy density\n            sedb = m_pBond->DevStrainEnergyDensity(wb);\n            // add bond stress to total strain energy density\n            sed += sedb*w;\n        }\n        \n        // restore safe copy of deformation gradient\n        ep.m_F = F;\n        ep.m_J = J;\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! strain energy density function\ndouble FEUncoupledReactiveViscoelasticMaterial::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = StrongBondDevSED(mp);\n    sed += WeakBondDevSED(mp)*(1-Damage(mp));\n    \n    // return the total strain energy density\n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! Cull generations that have relaxed below a threshold\nvoid FEUncoupledReactiveViscoelasticMaterial::CullGenerations(FEMaterialPoint& mp)\n{\n    // get the elastic material point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *mp.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    mat3ds D = ep.RateOfDeformation();\n    \n    // keep safe copy of deformation gradient\n    mat3d F = ep.m_F;\n    double J = ep.m_J;\n    \n    int ng = (int)pt.m_v.size();\n    m_nmax = max(m_nmax, ng);\n    \n    // don't cull if we have too few generations\n    if (ng < 3) return;\n    \n    // don't reduce number of generations to less than max value achieved so far\n    if (ng < m_nmax) return;\n\n    // always check oldest generation\n    ep.m_F = pt.m_Uv[0];\n    ep.m_J = pt.m_Jv[0];\n    double w0 = BreakingBondMassFraction(mp, 0, D)*pt.m_wv[0];\n    if (w0 < m_wmin) {\n        ep.m_F = pt.m_Uv[1];\n        ep.m_J = pt.m_Jv[1];\n        double w1 = BreakingBondMassFraction(mp, 1, D)*pt.m_wv[1];\n        pt.m_v[1] = (w0*pt.m_v[0] + w1*pt.m_v[1])/(w0+w1);\n        pt.m_Uv[1] = (pt.m_Uv[0]*w0 + pt.m_Uv[1]*w1)/(w0+w1);\n        pt.m_Jv[1] = pt.m_Uv[1].det();\n        pt.m_f[1] = (w0*pt.m_f[0] + w1*pt.m_f[1])/(w0+w1);\n        pt.m_wv[1] = (w0*pt.m_wv[0] + w1*pt.m_wv[1])/(w0+w1);\n        pt.m_Uv.pop_front();\n        pt.m_Jv.pop_front();\n        pt.m_v.pop_front();\n        pt.m_f.pop_front();\n        pt.m_wv.pop_front();\n    }\n    \n    // restore safe copy of deformation gradient\n    ep.m_F = F;\n    ep.m_J = J;\n\n    return;\n}\n\n//-----------------------------------------------------------------------------\n//! Update specialized material points\nvoid FEUncoupledReactiveViscoelasticMaterial::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FEMaterialPoint& sb = *GetBaseMaterialPoint(mp);\n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // start by updating specialized material points of base and bond materials\n    m_pBase->UpdateSpecializedMaterialPoints(sb, tp);\n    m_pBond->UpdateSpecializedMaterialPoints(wb, tp);\n    \n    // if the this material is a fiber and if the fiber is in compression, skip this update\n    if ((dynamic_cast<FEElasticFiberMaterialUC*>(m_pBase)) && (dynamic_cast<FEElasticFiberMaterialUC*>(m_pBond)))\n        if ((m_pBase->DevStress(mp)).norm() == 0) return;\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *wb.ExtractData<FEElasticMaterialPoint>();\n    \n    mat3ds Uv = ep.RightStretch();\n    double Jv = ep.m_J;\n\n    // if new generation not already created for current time, check if it should\n    if (pt.m_v.empty() || (pt.m_v.back() < tp.currentTime)) {\n        // check if the current deformation gradient is different from that of\n        // the last generation, in which case store the current state\n        if (NewGeneration(wb)) {\n            pt.m_v.push_back(tp.currentTime);\n            pt.m_Uv.push_back(Uv);\n            pt.m_Jv.push_back(Jv);\n            double f = (!pt.m_v.empty()) ? ReformingBondMassFraction(wb) : 1;\n            pt.m_f.push_back(f);\n            if (m_pWCDF) {\n                pt.m_Et = ScalarStrain(wb);\n                pt.m_wv.push_back(m_pWCDF->brf(mp,pt.m_Et));\n            }\n            else pt.m_wv.push_back(1);\n            CullGenerations(wb);\n        }\n    }\n    // otherwise, if we already have a generation for the current time, update the stored values\n    else if (pt.m_v.back() == tp.currentTime) {\n        pt.m_Uv.back() = Uv;\n        pt.m_Jv.back() = Jv;\n        if (m_pWCDF) {\n            pt.m_Et = ScalarStrain(wb);\n            pt.m_wv.back() = m_pWCDF->brf(mp,pt.m_Et);\n        }\n        pt.m_f.back() = ReformingBondMassFraction(wb);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate bond mass fraction of reforming generation\nint FEUncoupledReactiveViscoelasticMaterial::RVEGenerations(FEMaterialPoint& mp)\n{\n    FEMaterialPoint& wb = *GetBondMaterialPoint(mp);\n    \n    // get the reactive viscoelastic point data\n    FEReactiveVEMaterialPoint& pt = *wb.ExtractData<FEReactiveVEMaterialPoint>();\n    \n    // return the bond mass fraction of the reforming generation\n    return (int)pt.m_v.size();\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate trigger strain\ndouble FEUncoupledReactiveViscoelasticMaterial::ScalarStrain(FEMaterialPoint& mp)\n{\n    double d;\n    // get the elastic point data\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    switch (m_ttype) {\n        case 0:\n        {\n            // evaluate the Lagrangian strain\n            mat3ds E = ep.Strain();\n            \n            d = E.norm();\n        }\n            break;\n        case 1:\n        {\n            // distortional strain\n            // evaluate spatial Hencky (logarithmic) strain\n            mat3ds h = ep.LeftHencky();\n            \n            // evaluate distortion magnitude (always positive)\n            d = (h.dev()).norm();\n        }\n            break;\n        case 2:\n        {\n            // dilatational strain\n            d = fabs(log(ep.m_J));\n        }\n            break;\n            \n        default:\n            d = 0;\n            break;\n    }\n    \n    return d;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEUncoupledReactiveViscoelasticMaterial::Damage(FEMaterialPoint& mp)\n{\n    double D = 0;\n    if (m_pDmg) D = m_pDmg->Damage(*GetBaseMaterialPoint(mp));\n    else if (m_pFtg) D = m_pFtg->Damage(*GetBaseMaterialPoint(mp));\n    return D;\n}\n"
  },
  {
    "path": "FEBioMech/FEUncoupledReactiveViscoelastic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEBondRelaxation.h\"\n#include \"FEBondRecruitment.h\"\n#include \"FEReactiveVEMaterialPoint.h\"\n#include \"FEDamageMaterialUC.h\"\n#include \"FEUncoupledReactiveFatigue.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a large deformation reactive viscoelastic material\n//! with uncoupled strain energy density formulation\n//\nclass FEUncoupledReactiveViscoelasticMaterial :\tpublic FEUncoupledMaterial\n{\npublic:\n    //! default constructor\n    FEUncoupledReactiveViscoelasticMaterial(FEModel* pfem);\n        \n    //! get the elastic base material\n\tFEUncoupledMaterial* GetBaseMaterial() { return m_pBase; }\n    \n    //! Set the base material\n    void SetBaseMaterial(FEUncoupledMaterial* pbase) { m_pBase = pbase; }\n    \n    //! get the elastic bond material\n\tFEUncoupledMaterial* GetBondMaterial() { return m_pBond; }\n    \n    //! Set the base material\n    void SetBondMaterial(FEUncoupledMaterial* pbond) { m_pBond = pbond; }\n    \npublic:\n    //! data initialization\n    bool Init() override;\n\n    //! stress function\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    mat3ds DevStressStrongBonds(FEMaterialPoint& pt);\n    mat3ds DevStressWeakBonds(FEMaterialPoint& pt);\n\n    //! tangent function\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    tens4ds DevTangentStrongBonds(FEMaterialPoint& pt);\n    tens4ds DevTangentWeakBonds(FEMaterialPoint& pt);\n\n    //! strain energy density function\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    double StrongBondDevSED(FEMaterialPoint& pt) override;\n    double WeakBondDevSED(FEMaterialPoint& pt) override;\n\n    //! cull generations\n    void CullGenerations(FEMaterialPoint& pt);\n    \n    //! evaluate bond mass fraction for a given generation\n    double BreakingBondMassFraction(FEMaterialPoint& pt, const int ig, const mat3ds D);\n    \n    //! evaluate bond mass fraction of reforming generation\n    double ReformingBondMassFraction(FEMaterialPoint& pt);\n    \n    //! detect new generation\n    bool NewGeneration(FEMaterialPoint& pt);\n    \n    //! return number of generations\n    int RVEGenerations(FEMaterialPoint& pt);\n    \n    //! returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n\n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n\n    //! get base material point\n    FEMaterialPoint* GetBaseMaterialPoint(FEMaterialPoint& mp);\n    \n    //! get bond material point\n    FEMaterialPoint* GetBondMaterialPoint(FEMaterialPoint& mp);\n    \n    //! evaluate scalar strain measure (same type as trigger strain for bond breaking)\n    double ScalarStrain(FEMaterialPoint& mp);\n    \nprivate:\n    FEUncoupledMaterial*\tm_pBase;\t//!< pointer to elastic solid material for strong bonds\n\tFEUncoupledMaterial*\tm_pBond;\t//!< pointer to elastic solid material for reactive bonds\n\tFEBondRelaxation*\t\tm_pRelx;    //!< pointer to bond relaxation material for reactive bonds\n    FEBondRecruitment*      m_pWCDF;    //!< pointer to weak bond recruitment function\n    \nprivate:\n    FEDamageMaterialUC*                 m_pDmg; //!< pointer to base material if it is a FEDamageMaterialUC\n    FEUncoupledReactiveFatigue*         m_pFtg; //!< pointer to base material if it is a FEUncoupledReactiveFatigue\n    double Damage(FEMaterialPoint& mp);         //!< return damage in the base material\n    \npublic:\n    double\tm_wmin;\t\t//!< minimum value of relaxation\n    int     m_btype;    //!< bond kinetics type\n    int     m_ttype;    //!< bond breaking trigger type\n    double  m_emin;     //!< strain threshold for triggering new generation\n\n    int     m_nmax;     //!< highest number of generations achieved in analysis\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledViscoElasticDamage.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FEUncoupledViscoElasticDamage.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEUncoupledViscoElasticDamage, FEUncoupledMaterial)\n\n    // material parameters\n    ADD_PARAMETER(m_t[0], \"t1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[1], \"t2\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[2], \"t3\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[3], \"t4\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[4], \"t5\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[5], \"t6\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_g0  , \"g0\");\n    ADD_PARAMETER(m_g[0], \"g1\");\n    ADD_PARAMETER(m_g[1], \"g2\");\n    ADD_PARAMETER(m_g[2], \"g3\");\n    ADD_PARAMETER(m_g[3], \"g4\");\n    ADD_PARAMETER(m_g[4], \"g5\");\n    ADD_PARAMETER(m_g[5], \"g6\");\n\n    // define the material properties\n    ADD_PROPERTY(m_pDmg, \"elastic\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEUncoupledViscoElasticDamage::FEUncoupledViscoElasticDamage(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n    m_g0 = 1;\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        m_t[i] = 1;\n        m_g[i] = 0;\n    }\n    m_binit = false;\n\n    m_pDmg = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEUncoupledViscoElasticDamage::Init()\n{\n    if (dynamic_cast<FEDamageMaterialUC*>(m_pDmg) == 0) {\n        feLogError(\"elastic component of viscoelastic damage material must be of type uncoupled elastic damage!\");\n        return false;\n    }\n    \n    if (m_pDmg->Init() == false) return false;\n    \n    // combine bulk modulus from base material and uncoupled viscoelastic material\n    if (m_binit == false) m_K += m_pDmg->m_K;\n    \n    m_binit = true;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEUncoupledViscoElasticDamage::CreateMaterialPointData()\n{\n    return new FEViscoElasticMaterialPoint(m_pDmg->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FEUncoupledViscoElasticDamage::DevStress(FEMaterialPoint& mp)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    if (dt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n    \n    // get the elastic part\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    \n    // Calculate the new elastic Cauchy stress\n    mat3ds se = m_pDmg->GetElasticMaterial()->DevStress(mp);\n    \n    // pull-back to get PK2 stress\n    mat3ds Se = pt.m_Se = ep.pull_back(se);\n    \n    // get elastic PK2 stress of previous timestep\n    mat3ds Sep = pt.m_Sep;\n    \n    // calculate new history variables\n    // terms are accumulated in S, the total PK2-stress\n    mat3ds S = Se*m_g0;\n    double g, h;\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        g = exp(-dt/m_t[i]);\n        h = (1 - g)/(dt/m_t[i]);\n        \n        pt.m_H[i] = pt.m_Hp[i]*g + (Se - Sep)*h;\n        S += pt.m_H[i]*m_g[i];\n    }\n    \n    // return the total Cauchy stress,\n    // which is the push-forward of S\n    double D = m_pDmg->Damage(mp);\n    \n    return ep.push_forward(S)*(1-D);\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent\ntens4ds FEUncoupledViscoElasticDamage::DevTangent(FEMaterialPoint& pt)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // calculate the spatial elastic tangent\n    tens4ds C = m_pDmg->GetElasticMaterial()->DevTangent(pt);\n    if (dt == 0.0) return C;\n    \n    // calculate the visco scale factor\n    double f = m_g0, g, h;\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        g = exp(-dt/m_t[i]);\n        h = ( 1 - exp(-dt/m_t[i]) )/( dt/m_t[i] );\n        f += m_g[i]*h;\n    }\n    \n    double D = m_pDmg->Damage(pt);\n    \n    // multiply tangent with visco-factor\n    return C*(f*(1-D));\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density function\ndouble FEUncoupledViscoElasticDamage::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n    \n    // Calculate the new elastic strain energy density\n    pt.m_sed = m_pDmg->GetElasticMaterial()->DevStrainEnergyDensity(mp);\n    double sed = pt.m_sed;\n    \n    double sedt = sed*m_g0;\n    if (SeriesStretchExponent(mp)) {\n        // get the elastic point data and evaluate the right-stretch tensor\n        for (int i=0; i<MAX_TERMS; ++i)\n        {\n            if (m_g[i] > 0) {\n                mat3ds C = et.RightCauchyGreen();\n                double l2[3], l[3];\n                vec3d v[3];\n                C.eigen2(l2, v);\n                l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n                mat3ds Ua = dyad(v[0])*pow(l[0],pt.m_alpha[i])\n                + dyad(v[1])*pow(l[1],pt.m_alpha[i]) + dyad(v[2])*pow(l[2],pt.m_alpha[i]);\n                et.m_F = Ua; et.m_J = Ua.det();\n                sedt += m_g[i]*m_pDmg->GetElasticMaterial()->DevStrainEnergyDensity(mp);\n            }\n        }\n    }\n    else\n        throw std::runtime_error(\"FEUncoupledViscoElasticDamage::strain energy density calculation did not converge!\");\n    \n    et.m_F = Fsafe; et.m_J = Jsafe;\n    \n    double D = m_pDmg->Damage(mp);\n    \n    // return the total strain energy density\n    return sedt*(1-D);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate exponent of right-stretch tensor in series spring\nbool FEUncoupledViscoElasticDamage::SeriesStretchExponent(FEMaterialPoint& mp)\n{\n    const double errrel = 1e-6;\n    const double almin = 0.001;\n    const int maxiter = 50;\n    // get the elastic point data and evaluate the right-stretch tensor\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the right stretch tensor\n    mat3ds C = et.RightCauchyGreen();\n    double l2[3], l[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n    mat3ds U = dyad(v[0])*l[0] + dyad(v[1])*l[1] + dyad(v[2])*l[2];\n    double gamma = 0;\n    for (int i=0; i<MAX_TERMS; ++i) gamma += m_g[i];\n    \n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    \n    // use previous time solution as initial guess for the exponent\n    mat3ds Se = pt.m_Se;\n    mat3ds S = et.pull_back(et.m_s);\n    double fmag = Se.dotdot(U);\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n    for (int i=0; i<MAX_TERMS; ++i) {\n        if (m_g[i] > 0) {\n            double alpha = pt.m_alphap[i];\n            bool done = false;\n            int iter = 0;\n            do {\n                mat3ds Ua = dyad(v[0])*pow(l[0],alpha) + dyad(v[1])*pow(l[1],alpha) + dyad(v[2])*pow(l[2],alpha);\n                et.m_F = Ua; et.m_J = Ua.det();\n                mat3ds Sea = et.pull_back(m_pDmg->GetElasticMaterial()->Stress(mp));\n                double f = (Sea*m_g[i] - S + Se).dotdot(U);\n                tens4ds Cea = et.pull_back(m_pDmg->GetElasticMaterial()->Tangent(mp));\n                mat3ds U2ap = dyad(v[0])*(pow(l[0],2*alpha)*log(l[0]))\n                + dyad(v[1])*(pow(l[1],2*alpha)*log(l[1]))\n                + dyad(v[2])*(pow(l[2],2*alpha)*log(l[2]));\n                double fprime = (Cea.dot(U2ap)).dotdot(U)*m_g[i];\n                if (fprime != 0) {\n                    double dalpha = -f/fprime;\n                    alpha += dalpha;\n                    if (fabs(f) < errrel*fmag) done = true;\n                    else if (fabs(dalpha) < errrel*fabs(alpha)) done = true;\n                    else if (alpha > 1) { alpha = 1; done = true; }\n                    else if (alpha < almin) { alpha = 0; done = true; }\n                    else if (++iter > maxiter) done = true;\n                }\n                else\n                    done = true;\n            } while (!done);\n            if (iter > maxiter) {\n                et.m_F = Fsafe; et.m_J = Jsafe;\n                return false;\n            }\n            pt.m_alpha[i] = alpha;\n        }\n    }\n    et.m_F = Fsafe; et.m_J = Jsafe;\n    return true;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEUncoupledViscoElasticDamage.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscoElasticMaterial.h\"\n#include \"FEDamageMaterialUC.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a large deformation visco-elastic material\n//\nclass FEUncoupledViscoElasticDamage : public FEUncoupledMaterial\n{\npublic:\n    // NOTE: make sure that this parameter is the\n    //       same as the MAX_TERMS in the FEViscoElasticMaterialPoint class\n    enum { MAX_TERMS = FEViscoElasticMaterialPoint::MAX_TERMS };\n    \npublic:\n    //! default constructor\n    FEUncoupledViscoElasticDamage(FEModel* pfem);\n    \npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! stress function\n    mat3ds DevStress(FEMaterialPoint& pt) override;\n    \n    //! tangent function\n    tens4ds DevTangent(FEMaterialPoint& pt) override;\n    \n    //! strain energy density\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! calculate exponent of right-stretch tensor in series spring\n    bool SeriesStretchExponent(FEMaterialPoint& pt);\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \npublic:\n    // material parameters\n    double    m_g0;            //!< intitial visco-elastic coefficient\n    double    m_g[MAX_TERMS];    //!< visco-elastic coefficients\n    double    m_t[MAX_TERMS];    //!< relaxation times\n    \nprivate:\n    bool                    m_binit;    //!< initialization flag\n    FEDamageMaterialUC*     m_pDmg;     //!< pointer to uncoupled elastic damage material\n\npublic:\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEUncoupledViscoElasticMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEUncoupledViscoElasticMaterial.h\"\n#include <FECore/FECoreKernel.h>\n#include <stdexcept>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEUncoupledViscoElasticMaterial, FEUncoupledMaterial)\n    ADD_PARAMETER(m_t[0], \"t1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[1], \"t2\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[2], \"t3\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[3], \"t4\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[4], \"t5\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[5], \"t6\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_g0  , \"g0\");\n\tADD_PARAMETER(m_g[0], \"g1\");\n\tADD_PARAMETER(m_g[1], \"g2\");\n\tADD_PARAMETER(m_g[2], \"g3\");\n\tADD_PARAMETER(m_g[3], \"g4\");\n\tADD_PARAMETER(m_g[4], \"g5\");\n\tADD_PARAMETER(m_g[5], \"g6\");\n\n\tADD_PROPERTY(m_pBase, \"elastic\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEUncoupledViscoElasticMaterial::FEUncoupledViscoElasticMaterial(FEModel* pfem) : FEUncoupledMaterial(pfem)\n{\n\tm_g0 = 1;\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tm_t[i] = 1;\n\t\tm_g[i] = 0;\n\t}\n\tm_binit = false;\n\n\tm_pBase = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! data initialization and checking\nbool FEUncoupledViscoElasticMaterial::Init()\n{\n\t// combine bulk modulus from base material and uncoupled viscoelastic material\n\tif (m_binit == false) m_K += m_pBase->m_K;\n\n\tif (FEUncoupledMaterial::Init() == false) return false;\n\n\tm_binit = true;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data\nFEMaterialPointData* FEUncoupledViscoElasticMaterial::CreateMaterialPointData()\n{ \n\treturn new FEViscoElasticMaterialPoint(m_pBase->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FEUncoupledViscoElasticMaterial::DevStress(FEMaterialPoint& mp)\n{\n\tdouble dt = CurrentTimeIncrement();\n\tif (dt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n    \n\t// get the elastic part\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// get the viscoelastic point data\n\tFEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n\t\n\t// Calculate the new elastic Cauchy stress\n\tmat3ds se = m_pBase->DevStress(mp);\n\t\n\t// pull-back to get PK2 stress\n\tmat3ds Se = pt.m_Se = ep.pull_back(se);\n\t\n\t// get elastic PK2 stress of previous timestep\n\tmat3ds Sep = pt.m_Sep;\n\t\n\t// calculate new history variables\n\t// terms are accumulated in S, the total PK2-stress\n\tmat3ds S = Se*m_g0;\n\tdouble g, h;\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tg = exp(-dt/m_t[i]);\n\t\th = (1 - g)/(dt/m_t[i]);\n\t\t\n\t\tpt.m_H[i] = pt.m_Hp[i]*g + (Se - Sep)*h;\n\t\tS += pt.m_H[i]*m_g[i];\n\t}\n\t\n\t// return the total Cauchy stress,\n\t// which is the push-forward of S\n\treturn ep.push_forward(S);\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent\ntens4ds FEUncoupledViscoElasticMaterial::DevTangent(FEMaterialPoint& pt)\n{\n\tdouble dt = CurrentTimeIncrement();\n\n\t// calculate the spatial elastic tangent\n\ttens4ds C = m_pBase->DevTangent(pt);\n\tif (dt == 0.0) return C;\n\t\n\t// calculate the visco scale factor\n\tdouble f = m_g0, g, h;\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tg = exp(-dt/m_t[i]);\n\t\th = ( 1 - exp(-dt/m_t[i]) )/( dt/m_t[i] );\n\t\tf += m_g[i]*h; \n\t}\n\t\n\t// multiply tangent with visco-factor\n\treturn C*f;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density function\n/*double FEUncoupledViscoElasticMaterial::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the viscoelastic point data\n\tFEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n\n\t// Calculate the new elastic strain energy density\n\tpt.m_sed = m_pBase->DevStrainEnergyDensity(mp);\n    double sed = pt.m_sed;\n\t\n    double sedt = sed*m_g0;\n    if (SeriesStretchExponent(mp)) {\n        // get the elastic point data and evaluate the right-stretch tensor\n        for (int i=0; i<MAX_TERMS; ++i)\n        {\n            if (m_g[i] > 0) {\n                mat3ds C = et.RightCauchyGreen();\n                double l2[3], l[3];\n                vec3d v[3];\n                C.eigen2(l2, v);\n                l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n                mat3ds Ua = dyad(v[0])*pow(l[0],pt.m_alpha[i])\n                + dyad(v[1])*pow(l[1],pt.m_alpha[i]) + dyad(v[2])*pow(l[2],pt.m_alpha[i]);\n                et.m_F = Ua; et.m_J = Ua.det();\n                sedt += m_g[i]*m_pBase->DevStrainEnergyDensity(mp);\n            }\n        }\n    }\n    else\n        throw std::runtime_error(\"FEUncoupledViscoElasticMaterial::deviatoric strain energy density calculation did not converge!\");\n    \n    et.m_F = Fsafe; et.m_J = Jsafe;\n\n\t// return the total strain energy density\n\treturn sedt;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate exponent of right-stretch tensor in series spring\nbool FEUncoupledViscoElasticMaterial::SeriesStretchExponent(FEMaterialPoint& mp)\n{\n    const double errrel = 1e-6;\n    const double almin = 0.001;\n    const int maxiter = 50;\n    // get the elastic point data and evaluate the right-stretch tensor\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the right stretch tensor\n    mat3ds C = et.DevRightCauchyGreen();\n    double l2[3], l[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n    mat3ds U = dyad(v[0])*l[0] + dyad(v[1])*l[1] + dyad(v[2])*l[2];\n    double gamma = 0;\n    for (int i=0; i<MAX_TERMS; ++i) gamma += m_g[i];\n    \n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    \n    // use previous time solution as initial guess for the exponent\n    mat3ds Se = pt.m_Se;\n    mat3ds S = et.pull_back(et.m_s);\n    double fmag = Se.dotdot(U);\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n    for (int i=0; i<MAX_TERMS; ++i) {\n        if (m_g[i] > 0) {\n            double alpha = pt.m_alphap[i];\n            bool done = false;\n            int iter = 0;\n            do {\n                mat3ds Ua = dyad(v[0])*pow(l[0],alpha) + dyad(v[1])*pow(l[1],alpha) + dyad(v[2])*pow(l[2],alpha);\n                et.m_F = Ua; et.m_J = Ua.det();\n                mat3ds Sea = et.pull_back(m_pBase->DevStress(mp));\n                double f = (Sea*m_g[i] - S + Se).dotdot(U);\n                tens4ds Cea = et.pull_back(m_pBase->DevTangent(mp));\n                mat3ds U2ap = dyad(v[0])*(pow(l[0],2*alpha)*log(l[0]))\n                + dyad(v[1])*(pow(l[1],2*alpha)*log(l[1]))\n                + dyad(v[2])*(pow(l[2],2*alpha)*log(l[2]));\n                double fprime = (Cea.dot(U2ap)).dotdot(U)*m_g[i];\n                if (fprime != 0) {\n                    double dalpha = -f/fprime;\n                    alpha += dalpha;\n                    if (fabs(f) < errrel*fmag) done = true;\n                    else if (fabs(dalpha) < errrel*fabs(alpha)) done = true;\n                    else if (alpha > 1) { alpha = 1; done = true; }\n                    else if (alpha < almin) { alpha = 0; done = true; }\n                    else if (++iter > maxiter) done = true;\n                }\n                else\n                    done = true;\n            } while (!done);\n            if (iter > maxiter) {\n                et.m_F = Fsafe; et.m_J = Jsafe;\n                return false;\n            }\n            pt.m_alpha[i] = alpha;\n        }\n    }\n    et.m_F = Fsafe; et.m_J = Jsafe;\n    return true;\n}\n */\n"
  },
  {
    "path": "FEBioMech/FEUncoupledViscoElasticMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include \"FEViscoElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a large deformation uncoupled visco-elastic material\n//\nclass FEUncoupledViscoElasticMaterial :\tpublic FEUncoupledMaterial\n{\npublic:\n\t// NOTE: make sure that this parameter is the \n\t//       same as the MAX_TERMS in the FEViscoElasticMaterialPoint class\n\tenum { MAX_TERMS = FEViscoElasticMaterialPoint::MAX_TERMS };\n\t\npublic:\n\t//! default constructor\n\tFEUncoupledViscoElasticMaterial(FEModel* pfem);\n\n\t// get the elastic base material\n\tFEUncoupledMaterial* GetBaseMaterial() { return m_pBase; }\n\n\t// set the elastic base material\n\tvoid SetBaseMaterial(FEUncoupledMaterial* pbase) { m_pBase = pbase; }\n\npublic:\n\t//! data initialization and checking\n\tbool Init() override;\n\t\n\t//! deviatoric stress function\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\t\n\t//! deviatoric tangent function\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\t\n\t//! deviatoric strain energy density function\n    double DevStrainEnergyDensity(FEMaterialPoint& pt) override { return 0; }\n    \n    //! calculate exponent of right-stretch tensor in series spring\n//    bool SeriesStretchExponent(FEMaterialPoint& pt);\n\n\t//! returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\t\npublic:\n\tdouble\tm_t[MAX_TERMS];\t//!< relaxation times\n\tdouble\tm_g0;\t\t\t//!< intitial visco-elastic coefficient\n\tdouble\tm_g[MAX_TERMS];\t//!< visco-elastic coefficients\n\t\nprivate:\n\tFEUncoupledMaterial*\tm_pBase;\t//!< pointer to elastic solid material\n\tbool\t\t\t\t\tm_binit;\t//!< initialization flag\n\t\npublic:\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEVerondaWestmann.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEVerondaWestmann.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEVerondaWestmann, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c1, FE_RANGE_GREATER(0.0), \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c2, FE_RANGE_GREATER(0.0), \"c2\")->setUnits(UNIT_NONE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric stress\nmat3ds FEVerondaWestmann::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient and its determinant\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// Invariants of B (= invariants of C)\n\t// Note that these are the invariants of Btilde, not of B!\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// --- TODO: put strain energy derivatives here ---\n\t//\n\t// W = C1*(exp(C2*(I1-3)-1)-0.5*C1*C2*(I2 - 3)\n\t//\n\t// Wi = dW/dIi\n\tdouble W1 = c1*c2*exp(c2*(I1-3));\n\tdouble W2 = -0.5*c1*c2;\n\t// ---\n\n\t// calculate T = F*dW/dC*Ft\n\tmat3ds T = B*(W1 + W2*I1) - B2*W2;\n\n\treturn T.dev()*(2.0/J);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate deviatoric tangent\ntens4ds FEVerondaWestmann::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\tdouble Ji = 1.0/J;\n\n\t// calculate deviatoric left Cauchy-Green tensor: B = F*Ft\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n\tdouble I2 = 0.5*(I1*I1 - B2.tr());\n\n\t// material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// --- TODO: put strain energy derivatives here ---\n\t// Wi = dW/dIi\n\tdouble W1, W2, W11;\n\tW1 = c1*c2*exp(c2*(I1-3));\n\tW2 = -0.5*c1*c2;\n\tW11 = c2*W1;\n\t// ---\n\n\t// calculate dWdC:C\n\tdouble WC = W1*I1 + 2*W2*I2;\n\n\t// calculate C:d2WdCdC:C\n\tdouble CWWC = W11*I1*I1+2*I2*W2;\n\n\t// deviatoric cauchy-stress, trs = trace[s]/3\n\tmat3ds T = B * (W1 + W2 * I1) - B2 * W2;\n\tmat3ds devs = T.dev() * (2.0 / J);\n\n\tmat3ds I(1,1,1,0,0,0);\t// Identity\n\n\ttens4ds IxI = dyad1s(I);\n\ttens4ds I4  = dyad4s(I);\n\ttens4ds BxB = dyad1s(B);\n\ttens4ds B4  = dyad4s(B);\n\n\t// d2W/dCdC:C\n\tmat3ds WCCxC = B*(I1*(W11 + W2)) - B2*W2;\n\n\ttens4ds cw = BxB*((W11 + W2)*4.0*Ji) - B4*(W2*4.0*Ji) - dyad1s(WCCxC, I)*(4.0/3.0*Ji) + IxI*(4.0/9.0*Ji*CWWC);\n\n\ttens4ds c = dyad1s(devs, I)*(-2.0/3.0) + (I4 - IxI/3.0)*(4.0/3.0*Ji*WC) + cw;\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEVerondaWestmann::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the elastic material point data\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// calculate left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n    \n\t// calculate square of B\n\tmat3ds B2 = B.sqr();\n    \n\t// material parameters\n\tdouble c1 = m_c1(mp);\n\tdouble c2 = m_c2(mp);\n\n\t// Invariants of B (= invariants of C)\n\tdouble I1 = B.tr();\n    double I2 = (I1*I1 - B2.tr())/2.0;\n    \n    double sed = c1*(exp(c2*(I1-3))-1) - c1*c2*(I2-3)/2;\n    \n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEVerondaWestmann.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//!  Veronda-Westmann material model\n\nclass FEVerondaWestmann : public FEUncoupledMaterial\n{\npublic:\n\tFEVerondaWestmann(FEModel* pfem) : FEUncoupledMaterial(pfem) {}\n\npublic:\n\tFEParamDouble\tm_c1;\t//!< Veronda-Westmann coefficient C1;\n\tFEParamDouble\tm_c2;\t//!< Veronda-Westmann coefficient C2;\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate strain energy density at material point\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEViscoElasticDamage.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n#include \"FEViscoElasticDamage.h\"\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEViscoElasticDamage, FEElasticMaterial)\n\n    // material parameters\n    ADD_PARAMETER(m_t[0], \"t1\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[1], \"t2\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[2], \"t3\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[3], \"t4\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[4], \"t5\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_t[5], \"t6\")->setUnits(UNIT_TIME);\n    ADD_PARAMETER(m_g0  , \"g0\");\n    ADD_PARAMETER(m_g[0], \"g1\");\n    ADD_PARAMETER(m_g[1], \"g2\");\n    ADD_PARAMETER(m_g[2], \"g3\");\n    ADD_PARAMETER(m_g[3], \"g4\");\n    ADD_PARAMETER(m_g[4], \"g5\");\n    ADD_PARAMETER(m_g[5], \"g6\");\n\n    // define the material properties\n    ADD_PROPERTY(m_pDmg, \"elastic\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEViscoElasticDamage::FEViscoElasticDamage(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n    m_g0 = 1;\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        m_t[i] = 1;\n        m_g[i] = 0;\n    }\n    \n    m_pDmg = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! data initialization\nbool FEViscoElasticDamage::Init()\n{\n    if (dynamic_cast<FEDamageMaterial*>(m_pDmg) == 0) {\n        feLogError(\"elastic component of viscoelastic damage material must be of type elastic damage!\");\n        return false;\n    }\n    return m_pDmg->Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEViscoElasticDamage::CreateMaterialPointData()\n{\n    return new FEViscoElasticMaterialPoint(m_pDmg->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FEViscoElasticDamage::Stress(FEMaterialPoint& mp)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    if (dt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n    \n    // get the elastic part\n    FEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    \n    // Calculate the new elastic Cauchy stress\n    mat3ds se = m_pDmg->GetElasticMaterial()->Stress(mp);\n    \n    // pull-back to get PK2 stress\n    mat3ds Se = pt.m_Se = ep.pull_back(se);\n    \n    // get elastic PK2 stress of previous timestep\n    mat3ds Sep = pt.m_Sep;\n    \n    // calculate new history variables\n    // terms are accumulated in S, the total PK2-stress\n    mat3ds S = Se*m_g0;\n    double g, h;\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        g = exp(-dt/m_t[i]);\n        h = (1 - g)/(dt/m_t[i]);\n        \n        pt.m_H[i] = pt.m_Hp[i]*g + (Se - Sep)*h;\n        S += pt.m_H[i]*m_g[i];\n    }\n    \n    // return the total Cauchy stress,\n    // which is the push-forward of S\n    double D = m_pDmg->Damage(mp);\n    \n    return ep.push_forward(S)*(1-D);\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent\ntens4ds FEViscoElasticDamage::Tangent(FEMaterialPoint& pt)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // calculate the spatial elastic tangent\n    tens4ds C = m_pDmg->GetElasticMaterial()->Tangent(pt);\n    if (dt == 0.0) return C;\n    \n    // calculate the visco scale factor\n    double f = m_g0, g, h;\n    for (int i=0; i<MAX_TERMS; ++i)\n    {\n        g = exp(-dt/m_t[i]);\n        h = ( 1 - exp(-dt/m_t[i]) )/( dt/m_t[i] );\n        f += m_g[i]*h;\n    }\n    \n    double D = m_pDmg->Damage(pt);\n    \n    // multiply tangent with visco-factor\n    return C*(f*(1-D));\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density function\ndouble FEViscoElasticDamage::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n    \n    // Calculate the new elastic strain energy density\n    pt.m_sed = m_pDmg->GetElasticMaterial()->StrainEnergyDensity(mp);\n    double sed = pt.m_sed;\n    \n    double sedt = sed*m_g0;\n    if (SeriesStretchExponent(mp)) {\n        // get the elastic point data and evaluate the right-stretch tensor\n        for (int i=0; i<MAX_TERMS; ++i)\n        {\n            if (m_g[i] > 0) {\n                mat3ds C = et.RightCauchyGreen();\n                double l2[3], l[3];\n                vec3d v[3];\n                C.eigen2(l2, v);\n                l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n                mat3ds Ua = dyad(v[0])*pow(l[0],pt.m_alpha[i])\n                + dyad(v[1])*pow(l[1],pt.m_alpha[i]) + dyad(v[2])*pow(l[2],pt.m_alpha[i]);\n                et.m_F = Ua; et.m_J = Ua.det();\n                sedt += m_g[i]*m_pDmg->GetElasticMaterial()->StrainEnergyDensity(mp);\n            }\n        }\n    }\n    else\n        throw std::runtime_error(\"FEViscoElasticDamage::strain energy density calculation did not converge!\");\n    \n    et.m_F = Fsafe; et.m_J = Jsafe;\n    \n    double D = m_pDmg->Damage(mp);\n    \n    // return the total strain energy density\n    return sedt*(1-D);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate exponent of right-stretch tensor in series spring\nbool FEViscoElasticDamage::SeriesStretchExponent(FEMaterialPoint& mp)\n{\n    const double errrel = 1e-6;\n    const double almin = 0.001;\n    const int maxiter = 50;\n    // get the elastic point data and evaluate the right-stretch tensor\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // get the right stretch tensor\n    mat3ds C = et.RightCauchyGreen();\n    double l2[3], l[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n    mat3ds U = dyad(v[0])*l[0] + dyad(v[1])*l[1] + dyad(v[2])*l[2];\n    double gamma = 0;\n    for (int i=0; i<MAX_TERMS; ++i) gamma += m_g[i];\n    \n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    \n    // use previous time solution as initial guess for the exponent\n    mat3ds Se = pt.m_Se;\n    mat3ds S = et.pull_back(et.m_s);\n    double fmag = Se.dotdot(U);\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n    for (int i=0; i<MAX_TERMS; ++i) {\n        if (m_g[i] > 0) {\n            double alpha = pt.m_alphap[i];\n            bool done = false;\n            int iter = 0;\n            do {\n                mat3ds Ua = dyad(v[0])*pow(l[0],alpha) + dyad(v[1])*pow(l[1],alpha) + dyad(v[2])*pow(l[2],alpha);\n                et.m_F = Ua; et.m_J = Ua.det();\n                mat3ds Sea = et.pull_back(m_pDmg->GetElasticMaterial()->Stress(mp));\n                double f = (Sea*m_g[i] - S + Se).dotdot(U);\n                tens4ds Cea = et.pull_back(m_pDmg->GetElasticMaterial()->Tangent(mp));\n                mat3ds U2ap = dyad(v[0])*(pow(l[0],2*alpha)*log(l[0]))\n                + dyad(v[1])*(pow(l[1],2*alpha)*log(l[1]))\n                + dyad(v[2])*(pow(l[2],2*alpha)*log(l[2]));\n                double fprime = (Cea.dot(U2ap)).dotdot(U)*m_g[i];\n                if (fprime != 0) {\n                    double dalpha = -f/fprime;\n                    alpha += dalpha;\n                    if (fabs(f) < errrel*fmag) done = true;\n                    else if (fabs(dalpha) < errrel*fabs(alpha)) done = true;\n                    else if (alpha > 1) { alpha = 1; done = true; }\n                    else if (alpha < almin) { alpha = 0; done = true; }\n                    else if (++iter > maxiter) done = true;\n                }\n                else\n                    done = true;\n            } while (!done);\n            if (iter > maxiter) {\n                et.m_F = Fsafe; et.m_J = Jsafe;\n                return false;\n            }\n            pt.m_alpha[i] = alpha;\n        }\n    }\n    et.m_F = Fsafe; et.m_J = Jsafe;\n    return true;\n}\n\n"
  },
  {
    "path": "FEBioMech/FEViscoElasticDamage.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEViscoElasticMaterial.h\"\n#include \"FEDamageMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a large deformation visco-elastic material\n//\nclass FEViscoElasticDamage : public FEElasticMaterial\n{\npublic:\n    // NOTE: make sure that this parameter is the\n    //       same as the MAX_TERMS in the FEViscoElasticMaterialPoint class\n    enum { MAX_TERMS = FEViscoElasticMaterialPoint::MAX_TERMS };\n    \npublic:\n    //! default constructor\n    FEViscoElasticDamage(FEModel* pfem);\n    \npublic:\n    //! initialization\n    bool Init() override;\n    \n    //! stress function\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! tangent function\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! calculate exponent of right-stretch tensor in series spring\n    bool SeriesStretchExponent(FEMaterialPoint& pt);\n    \n    // returns a pointer to a new material point object\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \npublic:\n    // material parameters\n    double    m_g0;            //!< intitial visco-elastic coefficient\n    double    m_g[MAX_TERMS];    //!< visco-elastic coefficients\n    double    m_t[MAX_TERMS];    //!< relaxation times\n    \nprivate:\n    FEDamageMaterial*    m_pDmg;    //!< pointer to elastic damage material\n\npublic:\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEViscoElasticMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEViscoElasticMaterial.h\"\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModel.h>\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEViscoElasticMaterial, FEElasticMaterial)\n\n\t// material parameters\n\tADD_PARAMETER(m_t[0], \"t1\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_t[1], \"t2\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_t[2], \"t3\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_t[3], \"t4\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_t[4], \"t5\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_t[5], \"t6\")->setUnits(UNIT_TIME);\n\tADD_PARAMETER(m_g0  , \"g0\");\n\tADD_PARAMETER(m_g[0], \"g1\");\n\tADD_PARAMETER(m_g[1], \"g2\");\n\tADD_PARAMETER(m_g[2], \"g3\");\n\tADD_PARAMETER(m_g[3], \"g4\");\n\tADD_PARAMETER(m_g[4], \"g5\");\n\tADD_PARAMETER(m_g[5], \"g6\");\n\n\t// define the material properties\n\tADD_PROPERTY(m_Base, \"elastic\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEViscoElasticMaterialPoint::FEViscoElasticMaterialPoint(FEMaterialPointData* mp) : FEMaterialPointData(mp)\n{\n\tm_sed = 0.0;\n\tm_sedp = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a shallow copy of the material point data\nFEMaterialPointData* FEViscoElasticMaterialPoint::Copy()\n{\n\tFEViscoElasticMaterialPoint* pt = new FEViscoElasticMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes material point data.\nvoid FEViscoElasticMaterialPoint::Init()\n{\n\t// intialize data to zero\n\tm_Se.zero();\n\tm_Sep.zero();\n\tm_sed = 0.0;\n    m_sedp = 0.0;\n\tfor (int i=0; i<MAX_TERMS; ++i) {\n\t\tm_H[i].zero();\n\t\tm_Hp[i].zero();\n        m_alpha[i] = m_alphap[i] = 1.0;\n\t}\n\n    // don't forget to initialize the base class\n    FEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Update material point data.\nvoid FEViscoElasticMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\t// the elastic stress stored in pt is the Cauchy stress.\n\t// however, we need to store the 2nd PK stress\n\tm_Sep = m_Se;\n    m_sedp = m_sed;\n\n\t// copy previous data\n\tfor (int i=0; i<MAX_TERMS; ++i) {\n\t\tm_Hp[i] = m_H[i];\n        m_alphap[i] = m_alpha[i];\n    }\n    \n    // don't forget to call the base class\n    FEMaterialPointData::Update(timeInfo);\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize data to the archive\nvoid FEViscoElasticMaterialPoint::Serialize(DumpStream& ar)\n{\n    FEMaterialPointData::Serialize(ar);\n\tar & m_Se;\n\tar & m_Sep;\n\tar & m_H & m_Hp;\n    ar & m_sed & m_sedp;\n    ar & m_alpha & m_alphap;\n}\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEViscoElasticMaterial::FEViscoElasticMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_g0 = 1;\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tm_t[i] = 1;\n\t\tm_g[i] = 0;\n\t}\n\n\tm_Base = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! get the elastic base material \\todo I want to call this GetElasticMaterial, but this name is being used\nFEElasticMaterial* FEViscoElasticMaterial::GetBaseMaterial()\n{ \n\treturn m_Base; \n}\n\n//-----------------------------------------------------------------------------\n//! Set the base material\nvoid FEViscoElasticMaterial::SetBaseMaterial(FEElasticMaterial* pbase)\n{ \n\tm_Base = pbase; \n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data for this material\nFEMaterialPointData* FEViscoElasticMaterial::CreateMaterialPointData()\n{\n\treturn new FEViscoElasticMaterialPoint(m_Base->CreateMaterialPointData());\n}\n\n//-----------------------------------------------------------------------------\n//! Stress function\nmat3ds FEViscoElasticMaterial::Stress(FEMaterialPoint& mp)\n{\n\tdouble dt = GetFEModel()->GetTime().timeIncrement;\n\tif (dt == 0) return mat3ds(0, 0, 0, 0, 0, 0);\n    \n\t// get the elastic part\n\tFEElasticMaterialPoint& ep = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the viscoelastic point data\n\tFEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n\n\t// Calculate the new elastic Cauchy stress\n\tmat3ds se = m_Base->Stress(mp);\n\n\t// pull-back to get PK2 stress\n\tmat3ds Se = pt.m_Se = ep.pull_back(se);\n\n\t// get elastic PK2 stress of previous timestep\n\tmat3ds Sep = pt.m_Sep;\n\n\t// calculate new history variables\n\t// terms are accumulated in S, the total PK2-stress\n\tmat3ds S = Se* m_g0(mp);\n\tdouble g, h;\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tg = exp(-dt/m_t[i]);\n\t\th = (1 - g)/(dt/m_t[i]);\n\n\t\tpt.m_H[i] = pt.m_Hp[i]*g + (Se - Sep)*h;\n\t\tS += pt.m_H[i]*m_g[i];\n\t}\n\n\t// return the total Cauchy stress,\n\t// which is the push-forward of S\n\treturn ep.push_forward(S);\n}\n\n//-----------------------------------------------------------------------------\n//! Material tangent\ntens4ds FEViscoElasticMaterial::Tangent(FEMaterialPoint& pt)\n{\n\tdouble dt = GetFEModel()->GetTime().timeIncrement;\n\n\t// calculate the spatial elastic tangent\n\ttens4ds C = m_Base->Tangent(pt);\n\tif (dt == 0.0) return C;\n\n\t// calculate the visco scale factor\n\tdouble f = m_g0(pt), g, h;\n\tfor (int i=0; i<MAX_TERMS; ++i)\n\t{\n\t\tg = exp(-dt/m_t[i]);\n\t\th = ( 1 - exp(-dt/m_t[i]) )/( dt/m_t[i] );\n\t\tf += m_g[i]*h; \n\t}\n\n\t// multiply tangent with visco-factor\n\treturn C*f;\n}\n\n//-----------------------------------------------------------------------------\n//! Strain energy density function\n/*\ndouble FEViscoElasticMaterial::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\t// get the viscoelastic point data\n\tFEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n\n\t// Calculate the new elastic strain energy density\n\tpt.m_sed = m_Base->StrainEnergyDensity(mp);\n    double sed = pt.m_sed;\n    \n\tdouble sedt = sed*m_g0(mp);\n    if (SeriesStretchExponent(mp)) {\n        // get the elastic point data and evaluate the right-stretch tensor\n        for (int i=0; i<MAX_TERMS; ++i)\n        {\n            if (m_g[i] > 0) {\n                mat3ds C = et.RightCauchyGreen();\n                double l2[3], l[3];\n                vec3d v[3];\n                C.eigen2(l2, v);\n                l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n                mat3ds Ua = dyad(v[0])*pow(l[0],pt.m_alpha[i])\n                + dyad(v[1])*pow(l[1],pt.m_alpha[i]) + dyad(v[2])*pow(l[2],pt.m_alpha[i]);\n                et.m_F = Ua; et.m_J = Ua.det();\n                sedt += m_g[i]*m_Base->StrainEnergyDensity(mp);\n            }\n        }\n    }\n    else\n        throw std::runtime_error(\"FEViscoElasticMaterial::strain energy density calculation did not converge!\");\n    \n    et.m_F = Fsafe; et.m_J = Jsafe;\n\n\t// return the total strain energy density\n\treturn sedt;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate exponent of right-stretch tensor in series spring\nbool FEViscoElasticMaterial::SeriesStretchExponent(FEMaterialPoint& mp)\n{\n    const double errrel = 1e-6;\n    const double almin = 0.001;\n    const int maxiter = 50;\n    // get the elastic point data and evaluate the right-stretch tensor\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\n    // get the right stretch tensor\n    mat3ds C = et.RightCauchyGreen();\n    double l2[3], l[3];\n    vec3d v[3];\n    C.eigen2(l2, v);\n    l[0] = sqrt(l2[0]); l[1] = sqrt(l2[1]); l[2] = sqrt(l2[2]);\n    mat3ds U = dyad(v[0])*l[0] + dyad(v[1])*l[1] + dyad(v[2])*l[2];\n    double gamma = 0;\n    for (int i=0; i<MAX_TERMS; ++i) gamma += m_g[i];\n    \n    // get the viscoelastic point data\n    FEViscoElasticMaterialPoint& pt = *mp.ExtractData<FEViscoElasticMaterialPoint>();\n\n    // use previous time solution as initial guess for the exponent\n    mat3ds Se = pt.m_Se;\n    mat3ds S = et.pull_back(et.m_s);\n    double fmag = Se.dotdot(U);\n    mat3d Fsafe = et.m_F; double Jsafe = et.m_J;\n    for (int i=0; i<MAX_TERMS; ++i) {\n        if (m_g[i] > 0) {\n            double alpha = pt.m_alphap[i];\n            bool done = false;\n            int iter = 0;\n            do {\n                mat3ds Ua = dyad(v[0])*pow(l[0],alpha) + dyad(v[1])*pow(l[1],alpha) + dyad(v[2])*pow(l[2],alpha);\n                et.m_F = Ua; et.m_J = Ua.det();\n                mat3ds Sea = et.pull_back(m_Base->Stress(mp));\n                double f = (Sea*m_g[i] - S + Se).dotdot(U);\n                tens4ds Cea = et.pull_back(m_Base->Tangent(mp));\n                mat3ds U2ap = dyad(v[0])*(pow(l[0],2*alpha)*log(l[0]))\n                + dyad(v[1])*(pow(l[1],2*alpha)*log(l[1]))\n                + dyad(v[2])*(pow(l[2],2*alpha)*log(l[2]));\n                double fprime = (Cea.dot(U2ap)).dotdot(U)*m_g[i];\n                if (fprime != 0) {\n                    double dalpha = -f/fprime;\n                    alpha += dalpha;\n                    if (fabs(f) < errrel*fmag) done = true;\n                    else if (fabs(dalpha) < errrel*fabs(alpha)) done = true;\n                    else if (alpha > 1) { alpha = 1; done = true; }\n                    else if (alpha < almin) { alpha = 0; done = true; }\n                    else if (++iter > maxiter) done = true;\n                }\n                else\n                    done = true;\n            } while (!done);\n            if (iter > maxiter) {\n                et.m_F = Fsafe; et.m_J = Jsafe;\n                return false;\n            }\n            pt.m_alpha[i] = alpha;\n        }\n    }\n    et.m_F = Fsafe; et.m_J = Jsafe;\n    return true;\n}\n*/\n"
  },
  {
    "path": "FEBioMech/FEViscoElasticMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n\n//-----------------------------------------------------------------------------\n//! Material point data for visco-elastic materials\nclass FEViscoElasticMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tenum { MAX_TERMS = 6 };\n\npublic:\n\t//! constructor\n\tFEViscoElasticMaterialPoint(FEMaterialPointData* mp = nullptr);\n\n\t//! copy material point data\n\tFEMaterialPointData* Copy();\n\n\t//! Initialize material point data\n\tvoid Init();\n\n\t//! Update material point data\n\tvoid Update(const FETimeInfo& timeInfo);\n\n\t//! Serialize data to archive\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tmat3ds\tm_Se;\t//!< elastic PK2 stress\n\tmat3ds\tm_Sep;\t//!< elastic 2nd PK stress at previous time\n\n\tmat3ds\tm_H[MAX_TERMS];\t\t//!< internal variables\n\tmat3ds\tm_Hp[MAX_TERMS];\t//!< internal variables at previous timestep\n\n    double  m_alpha[MAX_TERMS];     //!< exponent of right-stretch tensor in series spring\n    double  m_alphap[MAX_TERMS];    //!< alpha at previous time step\n    \n\tdouble\tm_sed;\t//!< elastic strain energy density\n\tdouble\tm_sedp;\t//!< elastic strain energy density at previous time\n};\n\n\n//-----------------------------------------------------------------------------\n//! This class implements a large deformation visco-elastic material\n//\nclass FEViscoElasticMaterial :\tpublic FEElasticMaterial\n{\npublic:\n\t// NOTE: make sure that this parameter is the \n\t//       same as the MAX_TERMS in the FEViscoElasticMaterialPoint class\n\tenum { MAX_TERMS = FEViscoElasticMaterialPoint::MAX_TERMS };\n\npublic:\n\t//! default constructor\n\tFEViscoElasticMaterial(FEModel* pfem);\n\n\t//! get the elastic base material\n\tFEElasticMaterial* GetBaseMaterial();\n\n\t//! Set the base material\n\tvoid SetBaseMaterial(FEElasticMaterial* pbase);\n\npublic:\n\t//! stress function\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! tangent function\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& pt) override { return 0; }\n    \n    //! calculate exponent of right-stretch tensor in series spring\n//    bool SeriesStretchExponent(FEMaterialPoint& pt);\n    \n    // returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic: \n\t// material parameters\n\tFEParamDouble m_g0;\t\t\t//!< intitial visco-elastic coefficient\n\tdouble\tm_g[MAX_TERMS];\t//!< visco-elastic coefficients\n\tdouble\tm_t[MAX_TERMS];\t//!< relaxation times\n\nprivate:\n\tFEElasticMaterial*\tm_Base;\t//!< pointer to elastic solid material\n\npublic:\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEVolumeConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEVolumeConstraint.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/FEDataExport.h>\n#include <FECore/DumpStream.h>\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEVolumeSurface::FEVolumeSurface(FEModel* fem) : FESurface(fem)\n{\n\tm_Lp = 0.0;\n\tm_p  = 0.0;\n\tm_V0 = 0.0;\n\tm_Vt = 0.0;\n\n\t// define class exports\n\tEXPORT_DATA(PLT_FLOAT, FMT_REGION, &m_p, \"volume pressure\");\n}\n\n//-----------------------------------------------------------------------------\nbool FEVolumeSurface::Init()\n{\n\tif (FESurface::Init() == false) return false;\n\n\t// evaluate the initial volume\n\tm_V0 = CalculateSurfaceVolume(*this);\n\tm_Vt = m_V0;\n\n\treturn (m_V0 != 0.0);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeSurface::CopyFrom(FEVolumeSurface& s)\n{\n\tm_Node = s.m_Node;\n\n\t// create elements\n\tint NE = s.Elements();\n\tCreate(NE);\n\tfor (int i=0; i<NE; ++i) Element(i) = s.Element(i);\n\n\t// copy surface data\n\tm_Lp = s.m_Lp;\n\tm_p  = s.m_p;\n\tm_V0 = s.m_V0;\n\tm_Vt = s.m_Vt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeSurface::Serialize(DumpStream& ar)\n{\n\tFESurface::Serialize(ar);\n\tar & m_Lp & m_p & m_V0 & m_Vt;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEVolumeConstraint, FESurfaceConstraint);\n\tADD_PARAMETER(m_blaugon, \"laugon\" ); \n\tADD_PARAMETER(m_atol   , \"augtol\" );\n\tADD_PARAMETER(m_eps    , \"penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor. Set default parameter values\nFEVolumeConstraint::FEVolumeConstraint(FEModel* pfem) : FESurfaceConstraint(pfem)\n{\n\tm_s = new FEVolumeSurface(pfem);\n\n\tm_eps = 0.0;\n\tm_atol = 0.0;\n\tm_blaugon = false;\n\tm_binit = false;\t// will be set to true during activation\n\n\t// get the degrees of freedom\n\tm_dofX = (pfem ? pfem->GetDOFIndex(\"x\") : -1);\n\tm_dofY = (pfem ? pfem->GetDOFIndex(\"y\") : -1);\n\tm_dofZ = (pfem ? pfem->GetDOFIndex(\"z\") : -1);\n}\n\n//-----------------------------------------------------------------------------\nFEVolumeConstraint::~FEVolumeConstraint()\n{\n\tdelete m_s;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::CopyFrom(FENLConstraint* plc)\n{\n\t// cast to a periodic boundary\n\tFEVolumeConstraint& vc = dynamic_cast<FEVolumeConstraint&>(*plc);\n\n\t// copy parameters\n\tGetParameterList() = vc.GetParameterList();\n\n\t// copy nodes\n\tm_s->CopyFrom(*vc.m_s);\n}\n\n//-----------------------------------------------------------------------------\n//! Returns the surface\nFESurface* FEVolumeConstraint::GetSurface()\n{\n\treturn m_s;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEVolumeConstraint::EnclosedVolume() const\n{\n\treturn m_s->m_Vt;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEVolumeConstraint::Pressure() const\n{\n\treturn m_s->m_p;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures. \nvoid FEVolumeConstraint::Activate()\n{\n\t// don't forget to call base class\n\tFENLConstraint::Activate();\n\n\t// initialize the surface\n\tif (m_binit == false) m_s->Init();\n\n\t// set flag that initial volume is calculated\n\tm_binit = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\t// We don't do anything here since the connectivity of a surface\n\t// is implied by the domain to which it is attached.\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tint N = el.Nodes();\n\tlm.resize(N*3);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = mesh.Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\tlm[3*i  ] = id[m_dofX];\n\t\tlm[3*i+1] = id[m_dofY];\n\t\tlm[3*i+2] = id[m_dofZ];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEVolumeSurface& s = *m_s;\n\n\tFEMesh& mesh = *s.GetMesh();\n\n\tvector<double> fe;\n\tvector<int> lm;\n\n\t// get the pressure\n\tdouble p = s.m_p;\n\n\t// loop over all elements\n\tint NE = s.Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// get the nodal coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j) x[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// allocate element residual vector\n\t\tint ndof = 3*neln;\n\t\tfe.resize(ndof);\n\t\tzero(fe);\n\n\t\t// loop over all integration points\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// calculate the tangent vectors\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\t\t\tvec3d dxr(0,0,0), dxs(0,0,0);\n\t\t\tfor (int j=0; j<neln; ++j) \n\t\t\t{\n\t\t\t\tdxr += x[j]*Gr[j];\n\t\t\t\tdxs += x[j]*Gs[j];\n\t\t\t}\n\n\t\t\t// evaluate the \"normal\" vector\n\t\t\tvec3d v = (dxr ^ dxs)*w[n]*p;\n\n\t\t\t// evaluate the element forces\n\t\t\tdouble* H = el.H(n);\n\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t{\n\t\t\t\tfe[3*j  ] += H[j]*v.x;\n\t\t\t\tfe[3*j+1] += H[j]*v.y;\n\t\t\t\tfe[3*j+2] += H[j]*v.z;\n\t\t\t}\n\t\t}\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// add element force vector to global force vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEVolumeSurface& s = *m_s;\n\n\tFEMesh& mesh = *s.GetMesh();\n\n\t// get the pressure\n\tdouble p = s.m_p;\n\n\t// element stiffness matrix\n\tvector<int> lm;\n\tvector<double> fe;\n\n\t// loop over all elements\n\tint NE = s.Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tfor (int l=0; l<NE; ++l)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& el = s.Element(l);\n\n\t\tFEElementMatrix ke(el);\n\n\t\t// get the nodal coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j) x[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// allocate the stiffness matrix\n\t\tint ndof = 3*neln;\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\t\tfe.resize(ndof);\n\t\tzero(fe);\n\n\t\t// repeat over integration points\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// calculate tangent vectors\n\t\t\tdouble* N = el.H(n);\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\t\t\tvec3d dxr(0,0,0), dxs(0,0,0);\n\t\t\tfor (int j=0; j<neln; ++j) \n\t\t\t{\n\t\t\t\tdxr += x[j]*Gr[j];\n\t\t\t\tdxs += x[j]*Gs[j];\n\t\t\t}\n\n\t\t\t// calculate pressure contribution\n\t\t\tvec3d v = (dxr ^ dxs)*w[n]*m_eps;\n\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t{\n\t\t\t\tfe[3*i  ] += N[i]*v.x;\n\t\t\t\tfe[3*i+1] += N[i]*v.y;\n\t\t\t\tfe[3*i+2] += N[i]*v.z;\n\t\t\t}\n\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tke[3*i  ][3*j  ] += fe[3*i  ]*fe[3*j  ];\n\t\t\t\t\tke[3*i  ][3*j+1] += fe[3*i  ]*fe[3*j+1];\n\t\t\t\t\tke[3*i  ][3*j+2] += fe[3*i  ]*fe[3*j+2];\n\n\t\t\t\t\tke[3*i+1][3*j  ] += fe[3*i+1]*fe[3*j  ];\n\t\t\t\t\tke[3*i+1][3*j+1] += fe[3*i+1]*fe[3*j+1];\n\t\t\t\t\tke[3*i+1][3*j+2] += fe[3*i+1]*fe[3*j+2];\n\n\t\t\t\t\tke[3*i+2][3*j  ] += fe[3*i+2]*fe[3*j  ];\n\t\t\t\t\tke[3*i+2][3*j+1] += fe[3*i+2]*fe[3*j+1];\n\t\t\t\t\tke[3*i+2][3*j+2] += fe[3*i+2]*fe[3*j+2];\n\t\t\t\t}\n\n\t\t\n\t\t\t// calculate displacement contribution\n\t\t\tvec3d kab;\n\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tkab = (dxr*(N[j]*Gs[i]-N[i]*Gs[j])\n\t\t\t\t\t\t   -dxs*(N[j]*Gr[i]-N[i]*Gr[j]))*w[n]*0.5*p;\n\n\t\t\t\t\tke[3*i  ][3*j  ] +=      0;\n\t\t\t\t\tke[3*i  ][3*j+1] += -kab.z;\n\t\t\t\t\tke[3*i  ][3*j+2] +=  kab.y;\n\n\t\t\t\t\tke[3*i+1][3*j  ] +=  kab.z;\n\t\t\t\t\tke[3*i+1][3*j+1] +=      0;\n\t\t\t\t\tke[3*i+1][3*j+2] += -kab.x;\n\n\t\t\t\t\tke[3*i+2][3*j  ] += -kab.y;\n\t\t\t\t\tke[3*i+2][3*j+1] +=  kab.x;\n\t\t\t\t\tke[3*i+2][3*j+2] +=      0;\n\t\t\t\t}\n\t\t}\n\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEVolumeConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\tFEVolumeSurface& s = *m_s;\n\n\t// make sure we are augmenting\n\tif ((m_blaugon == false) || (m_atol <= 0.0)) return true;\n\n\tfeLog(\"\\nvolume constraint:\\n\");\n\n\tdouble Dp = m_eps*(s.m_Vt - s.m_V0);\n\tdouble Lp = s.m_p;\n\tdouble err = fabs(Dp/Lp);\n\tfeLog(\"\\tpressure: %lg\\n\", Lp);\n\tfeLog(\"\\tnorm : %lg (%lg)\\n\", err, m_atol);\n\tfeLog(\"\\tvolume ratio: %lg\\n\", s.m_Vt / s.m_V0);\n\n\t// check convergence\n\tif (err < m_atol) return true;\n\n\t// update Lagrange multiplier (and pressure variable)\n\ts.m_Lp = Lp;\n\ts.m_p = Lp + Dp;\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\tm_s->Serialize(ar);\n\tar & m_binit;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEVolumeConstraint::Reset()\n{\n}\n\n//-----------------------------------------------------------------------------\n// This function is called when the FE model's state needs to be updated.\nvoid FEVolumeConstraint::Update()\n{\n\tFEVolumeSurface& s = *m_s;\n\n\t// calculate the current volume\n\ts.m_Vt = CalculateSurfaceVolume(s);\n\n\t// update pressure variable\n\ts.m_p = s.m_Lp + m_eps*(s.m_Vt - s.m_V0);\n}\n"
  },
  {
    "path": "FEBioMech/FEVolumeConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FESurface.h>\n\n//-----------------------------------------------------------------------------\nclass FEVolumeSurface : public FESurface\n{\npublic:\n\t//! constructor\n\tFEVolumeSurface(FEModel* fem);\n\n\t//! Initialization\n\tbool Init();\n\n\t//! copy data\n\tvoid CopyFrom(FEVolumeSurface& s);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tdouble\tm_Lp;\t//!< Lagrange multipler pressure\n\tdouble\tm_p;\t//!< applied pressure (= Lp + eps*DV)\n\tdouble\tm_V0;\t//!< Initial volume\n\tdouble\tm_Vt;\t//!< current volume\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a constraint that tries to maintain the volume of the \n// enclosed space using an isochoric pressure.\nclass FEVolumeConstraint : public FESurfaceConstraint\n{\npublic:\n\t//! constructor\n\tFEVolumeConstraint(FEModel* pfem);\n\n\t~FEVolumeConstraint();\n\n\tvoid Activate() override;\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\tvoid Serialize(DumpStream& ar) override;\n\tvoid CopyFrom(FENLConstraint* plc) override;\n\n\t// update state\n\tvoid Reset() override;\n\tvoid Update() override;\n\n\t//! Unpack surface element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm);\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t// get the surface\n\tFESurface* GetSurface() override;\n\npublic:\n\tdouble EnclosedVolume() const;\n\n\tdouble Pressure() const;\n\npublic:\n\tdouble\tm_eps;\t\t//!< penalty parameter\n\tdouble\tm_atol;\t\t//!< augmented Lagrangian tolerance\n\tbool\tm_blaugon;\t//!< augmentation flag\n\nprivate:\n\tbool\tm_binit;\t//!< flag indicating whether the constraint is initialized\n\n\t// degrees of freedom\n\t// (TODO: find a better way of defining this. \n\t//        I don't want to have to do this in each class)\n\tint\tm_dofX;\n\tint\tm_dofY;\n\tint\tm_dofZ;\n\n\tFEVolumeSurface* m_s;\t//!< the bounding surface\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEVonMisesPlasticity.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEVonMisesPlasticity.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEVonMisesPlasticity, FESolidMaterial)\n\tADD_PARAMETER(m_E, FE_RANGE_GREATER(0.0), \"E\");\n\tADD_PARAMETER(m_v, FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\");\n\tADD_PARAMETER(m_Y, FE_RANGE_GREATER(0.0), \"Y\");\n\tADD_PARAMETER(m_H, FE_RANGE_GREATER_OR_EQUAL(0.0), \"H\");\nEND_FECORE_CLASS();\n\n\n//-----------------------------------------------------------------------------\nFEVonMisesPlasticity::FEVonMisesPlasticity(FEModel* pfem) : FESolidMaterial(pfem)\n{\n\tm_E = m_v = m_Y = m_H = 0;\n\tm_K = m_G = 0;\n\n\tAddDomainParameter(new FESolidStress());\n}\n\n//-----------------------------------------------------------------------------\nbool FEVonMisesPlasticity::Init()\n{\n\tif (FESolidMaterial::Init() == false) return false;\n\n\tm_K = m_E/(3.0*(1.0 - 2*m_v));\n\tm_G = m_E/(2.0*(1.0 +   m_v));\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEVonMisesPlasticity::CreateMaterialPointData()\n{\n\tFEJ2PlasticMaterialPoint* pt = new FEJ2PlasticMaterialPoint;\n\tpt->Y0 = m_Y;\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEVonMisesPlasticity::Stress(FEMaterialPoint &mp)\n{\n\tFEJ2PlasticMaterialPoint& pp = *mp.ExtractData<FEJ2PlasticMaterialPoint>();\n\tmat3d& F = pp.m_F;\n\t\n\t// get the current strain\n\tmat3ds e = F.sym() - mat3dd(1.0);\n\n\t// calculate strain increment\n\tmat3ds de = (e - pp.e0);\n\n\t// get the trial stress\n\tmat3ds strial = pp.sn + (de.dev()*(2.0*m_G) + de.iso()*(3.0*m_K));\n\tmat3ds dev_strial = strial.dev();\n\tdouble devs_norm = dev_strial.norm();\n\n\t// get current yield strenght\n\tdouble Y = pp.Y0;\n\n\tdouble k = Y / sqrt(3.0);\n\tdouble fac = devs_norm / (sqrt(2.0)*k);\n\n\tmat3ds s;\n\tif (fac<=1)\n\t{\n\t\ts = strial;\n\t\tpp.b = false;\n\t}\n\telse\n\t{\n\t\t// calculate plastic strain rate\n\t\tdouble L = (devs_norm - sqrt(2.0/3.0)*Y)/(2*m_G + m_H);\n\n\t\t// update yield strength\n\t\tpp.Y1 = Y + m_H*L/sqrt(2.0/3.0);\n\n\t\t// update stress\n\t\ts = strial.iso() + dev_strial*(1.0 - 2.0*m_G*L/devs_norm);\n\t\tpp.b = true;\n\t}\n\n\t// store the current strain measure\n\tpp.e1 = e;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEVonMisesPlasticity::Tangent(FEMaterialPoint &mp)\n{\n\tFEJ2PlasticMaterialPoint& pp = *mp.ExtractData<FEJ2PlasticMaterialPoint>();\n\n\t// lame parameters\n\tdouble lam = m_K - m_G*2.0/3.0;\n\tdouble mu  = m_G;\n\n\tdouble D[6][6] = {0};\n\tD[0][0] = lam+2.*mu; D[0][1] = lam      ; D[0][2] = lam      ;\n\tD[1][0] = lam      ; D[1][1] = lam+2.*mu; D[1][2] = lam      ;\n\tD[2][0] = lam      ; D[2][1] = lam      ; D[2][2] = lam+2.*mu;\n\tD[3][3] = mu;\n\tD[4][4] = mu;\n\tD[5][5] = mu;\n\ttens4ds C(D);\n\n\t// see if we are in plastic flow mode\n\tif (pp.b)\n\t{\n\t\t// get the stress\n\t\tmat3ds s = pp.m_s;\n\t\tmat3ds n = s.dev()*2.0;\n\n\t\tmat3ds A = C.dot(n);\n\t\tdouble G = n.dotdot(A) + m_H;\n\n\t\tC -= dyad4s(A)/G;\n\t}\n\n\treturn C;\n}\n\nFESolidStress::FESolidStress() : FEDomainParameter(\"stress\") {}\nFEParamValue FESolidStress::value(FEMaterialPoint& mp)\n{\n\tFEJ2PlasticMaterialPoint& pp = *mp.ExtractData<FEJ2PlasticMaterialPoint>();\n\treturn pp.m_s;\n}\n"
  },
  {
    "path": "FEBioMech/FEVonMisesPlasticity.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial.h\"\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nclass FEJ2PlasticMaterialPoint : public FEElasticMaterialPoint\n{\npublic:\n\tFEJ2PlasticMaterialPoint() {}\n\n\tFEMaterialPointData* Copy() \n\t{\n\t\treturn new FEJ2PlasticMaterialPoint(*this);\n\t}\n\n\tvoid Init()\n\t{\n\t\t// intialize data to zero\n\t\te0.zero();\n\t\te1.zero();\n\t\tsn.zero();\n\t\tb = false;\n\t\tY1 = Y0;\n\n\t\t// don't forget to intialize the nested data\n\t\tFEMaterialPointData::Init();\n\t}\n\n\tvoid Update(const FETimeInfo& timeInfo)\n\t{\n\t\te0 = e1;\n\t\tsn = m_s;\n\t\tY0 = Y1;\n\n\t\t// don't forget to call the base class\n\t\tFEElasticMaterialPoint::Update(timeInfo);\n\t}\n\n\tvoid Serialize(DumpStream& ar)\n\t{\n\t\tFEElasticMaterialPoint::Serialize(ar);\n\t\tar & e0 & e1 & sn;\n\t\tar & Y0 & Y1 & b;\n\t}\n\npublic:\n\tmat3ds\te0, e1;\t\t// strain at time n and n+1\n\tmat3ds\tsn;\t\t\t// stress at time n\n\tdouble\tY0, Y1;\t\t// yield strenght at time n, n+1\n\tbool\tb;\t\t\t// plasticity flag\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a simple von-Mises plasticity model with isotropic\n//! hardening. \nclass FEVonMisesPlasticity : public FESolidMaterial\n{\npublic:\n\tFEVonMisesPlasticity(FEModel* pfem);\n\npublic:\n\tdouble\tm_E;\t//!< Young's modulus\n\tdouble\tm_v;\t//!< Poisson's ratio\n\n\tdouble\tm_K;\t//!< bulk modulus\n\tdouble\tm_G;\t//!< shear modulus\n\tdouble\tm_Y;\t//!< initial yield strength\n\tdouble\tm_H;\t//!< hardening modulus \n\npublic:\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! data initialization and checking\n\tbool Init() override;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEBIOMECH_API FESolidStress : public FEDomainParameter\n{\npublic:\n\tFESolidStress();\n\tFEParamValue value(FEMaterialPoint& mp) override;\n};\n"
  },
  {
    "path": "FEBioMech/FEWrinkleOgdenMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEWrinkleOgdenMaterial.h\"\n#include \"FECore/mat2d.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEWrinkleOgdenMaterial, FEMembraneMaterial)\n\tADD_PARAMETER(m_u, FE_RANGE_GREATER(0.0), \"mu\");\n\tADD_PARAMETER(m_a, FE_RANGE_GREATER_OR_EQUAL(2.0), \"alpha\");\n\tADD_PARAMETER(m_bwrinkle, \"wrinkle\");\n\tADD_PARAMETER(m_l0, 2, \"prestretch\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid eig(double *A, double *B, double *C);\nvoid eigen2d(double l[2], double r[4], double m[4]);\n\n//-----------------------------------------------------------------------------\nFEWrinkleOgdenMaterial::FEWrinkleOgdenMaterial(FEModel* pfem) : FEMembraneMaterial(pfem)\n{\n\tm_a = 0;\n\tm_u = 0;\n\tm_l0[0] = m_l0[1] = 0.0;\n\tm_bwrinkle = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEWrinkleOgdenMaterial::principals(FEMaterialPoint& mp, double l[2], double v[4])\n{\n\tFEMembraneMaterialPoint& pt = *mp.ExtractData<FEMembraneMaterialPoint>();\n\n\t// get the def gradient\n\tdouble* g = pt.g;\n\n\t// extract the 2D component\n\tmat2d Fm(1 + g[0], g[3], g[1], 1 + g[4]);\n\n\t// 2D right Cauchy-Green tensor\n\tmat2d Cm = Fm.transpose()*Fm;\n\n\t// get eigenvalues and vectors\n\teig(l, v, Cm[0]);\n\n\t// get principal stretches \n\tl[0] = sqrt(l[0]) + m_l0[0];\n\tl[1] = sqrt(l[1]) + m_l0[1];\n\n\t// lambda0 should be the bigger stretch\n\tif (l[1] > l[0])\n\t{\n\t\tdouble t = l[0]; l[0] = l[1]; l[1] = t; \n\t\tt = v[0]; v[0] = v[2]; v[2] = t;\n\t\tt = v[1]; v[1] = v[3]; v[3] = t;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEWrinkleOgdenMaterial::Stress(FEMaterialPoint& mp, double s[3])\n{\n\t// get the principal strains and vectors\n\tdouble l[2], ec[4];\n\tprincipals(mp, l, ec);\n\n\t// evaluate stress based on principal stretches\n\t// note that we calculate the 2PK stress in principal directions\n\tdouble S[2][2] = {0};\n\tif (m_bwrinkle && (l[0] < 0.999)) // biaxial compression (wrinkling in both directions)\n\t{\n\t\t// stress is zero\n\t}\n\telse if (m_bwrinkle && (l[1] < 1.0/sqrt(l[0]))) // uniaxial tension (wrinkling in one direction)\n\t{\n\t\tS[0][0] = m_u*(pow(l[0], m_a-2.0)-pow(l[0],-2.0-0.5*m_a));\n\t}\n\telse // biaxial tension (no wrinkling)\n\t{\n\t\tS[0][0] = m_u*(-pow(l[0], -m_a-2.0)*pow(l[1], -m_a) + pow(l[0], m_a-2.0));\n\t\tS[1][1] = m_u*(-pow(l[1], -m_a-2.0)*pow(l[0], -m_a) + pow(l[1], m_a-2.0));\n\t}\n\n\t// setup coordinate transformation matrix to\n\t// transform from principal coordinates to element coordinates\n\tdouble Q[2][2] = {\n\t\t{ ec[0],  ec[1]},\n\t\t{-ec[1],  ec[0]}};\n\n\t// now we convert back to element matrix\n\tdouble Sv[2][2];\n\tfor (int i=0; i<2; ++i)\n\t\tfor (int j=0; j<2; ++j)\n\t\t{\n\t\t\tSv[i][j] = 0;\n\t\t\tfor (int k=0; k<2; ++k) \n\t\t\t\tfor (int l=0; l<2; ++l) Sv[i][j] += Q[k][i]*S[k][l]*Q[l][j];\n\t\t}\n\n\t// copy stress components\n\ts[0] = Sv[0][0]; s[1] = Sv[1][1]; s[2] = Sv[0][1];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEWrinkleOgdenMaterial::Tangent(FEMaterialPoint &mp, double D[3][3])\n{\n\t// get the principal strains and vectors\n\tdouble l[2], ec[4];\n\tprincipals(mp, l, ec);\n\n\t// evaluate stress based on principal stretches\n\t// note that we calculate the 2PK stress in principal directions\n\tdouble S[2] = {0};\n\tif (m_bwrinkle && (l[0] < 0.999)) // biaxial compression (wrinkling in both directions)\n\t{\n\t\t// no stress\n\t}\n\telse if (m_bwrinkle && (l[1] < 1.0/sqrt(l[0]))) // uniaxial tension (wrinkling in one direction)\n\t{\n\t\tS[0] = m_u*(pow(l[0], m_a-2.0)-pow(l[0],-2.0-0.5*m_a));\n\t}\n\telse // biaxial tension (no wrinkling)\n\t{\n\t\tS[0] = m_u*(-pow(l[0], -m_a-2.0)*pow(l[1], -m_a) + pow(l[0], m_a-2.0));\n\t\tS[1] = m_u*(-pow(l[1], -m_a-2.0)*pow(l[0], -m_a) + pow(l[1], m_a-2.0));\n\t}\n\n\t// evaluate tangent based on principal stretches\n\tdouble Cp[2][2] = {0}, ks = 0;\n/*\tif (m_bwrinkle && (l[0] < 1)) // biaxial compression (wrinkling in both directions)\n\t{\n\t\t// tangent is zero\n\t}\n\telse if (m_bwrinkle && (l[1] < sqrt(l[0])))  // uniaxial tension (wrinkling in uniaxial)\n\t{\n\t\tCp[0][0] = m_u*((2+m_a*0.5)*pow(l[0], -3-m_a*0.5) + (m_a-2.0)*pow(l[0], m_a-3.0))/l[0];\n\t}\n\telse // biaxial tension (no wrinkling)\n*/\t{\n\t\tCp[0][0] = m_u*((m_a + 2)*pow(l[1], -m_a)*pow(l[0], -m_a-3) + (m_a+2)*pow(l[0], m_a-3))/l[0];\n\t\tCp[1][1] = m_u*((m_a + 2)*pow(l[0], -m_a)*pow(l[1], -m_a-3) + (m_a+2)*pow(l[1], m_a-3))/l[1];\n\t\tCp[0][1] = Cp[1][0] = m_u*m_a*pow(l[0], -m_a-2)*pow(l[1], -m_a-2);\n\n\t\tif (fabs(l[0] - l[1]) < 1e-9)\n\t\t\tks = (Cp[0][0] - Cp[0][1])/(2*l[0]);\n\t\telse\n\t\t\tks = (S[0] - S[1])/(l[0]*l[0] - l[1]*l[1]);\n\t}\n\n\t double ct = ec[0], st = ec[1];\n\t double T[2][3] = {{ct*ct, st*st, st*ct},{st*st, ct*ct, -st*ct}};\n\n\t double z[3] = {2*ct*st, -2*ct*st, st*st - ct*ct};\n\n\t // transform to element coordinate system\n\t for (int i=0; i<3; ++i)\n\t\t for (int j=0; j<3; ++j)\n\t\t {\n\t\t\t D[i][j] = 0;\n\t\t\t for (int k=0; k<2; k++)\n\t\t\t\t for (int l=0; l<2; ++l)\n\t\t\t\t\t D[i][j] += T[k][i]*Cp[k][l]*T[l][j];\n\t\t\t D[i][j] += ks*z[i]*z[j];\n\t\t }\n}\n\n//-----------------------------------------------------------------------------\nvoid eig(double *A, double *B, double *C)\n{\n/* calculate eigenvalues A and vectors B of 2x2 matrix C */\ndouble tr,det,t2,pm;\nint i,j;\nif (((C[1]<1e-6)&&(C[1]>-1e-6))&&((C[2]<1e-6)&&(C[2]>-1e-6))){ /* not equal to zero, plus a tolerance */\nA[0]=C[0];A[1]=C[3];\nB[0]=1;B[1]=0;B[2]=0;B[3]=1;\n}\nelse{\ntr=C[0]+C[3];\ndet=C[0]*C[3]-C[1]*C[2];\nt2=tr/2;\npm=pow((tr*tr/4-det),0.5);\nif (pm<1e-4){\nA[0]=t2;A[1]=t2;\nB[0]=1;B[1]=0;B[2]=0;B[3]=1;\n}\nelse {\nA[0]=t2+pm;A[1]=t2-pm;\n\n/* calculate eigenvectors B */\nif ((C[1]>1e-9)||(C[1]<-1e-9)){ /* not equal to zero, plus a tolerance for rounding errors */\nB[0]=A[0]-C[3];\nB[1]=C[1];\nB[2]=A[1]-C[3];\nB[3]=C[1];\n/* normalise eigenvectors to unit length, reusing tr temporarily */\nfor (i=0;i<2;i++){\n   j=i*2;\n   tr=pow((B[j]*B[j]+B[j+1]*B[j+1]),0.5);\n   B[j]=B[j]/tr;B[j+1]=B[j+1]/tr;\n   }\n}\nelse if ((C[2]>1e-9)||(C[2]<-1e-9)){ \nB[0]=C[2];\nB[1]=A[0]-C[0];\nB[2]=C[2];\nB[3]=A[1]-C[0];\n/* normalise eigenvectors to unit length, reusing tr temporarily */\nfor (i=0;i<2;i++){\n   j=i*2;\n   tr=pow((B[j]*B[j]+B[j+1]*B[j+1]),0.5);\n   B[j]=B[j]/tr;B[j+1]=B[j+1]/tr;\n   }\n}\nelse {\nB[0]=1;B[1]=0;B[2]=0;B[3]=1;\n}\n}\n}\n}\n\n#define ROTATE(a, i, j, k, l) g=a[i][j]; h=a[k][l];a[i][j]=g-s*(h+g*tau); a[k][l] = h + s*(g - h*tau);\nvoid eigen2d(double l[2], double r[4], double m[4])\n{\n\tconst int NMAX = 50;\n\tdouble sm, tresh, g, h, t, c, tau, s, th;\n\tint i, j, k;\n\n\t// copy the matrix components since we will be overwriting them\n\tdouble a[2][2] = {\n\t\t\t{m[0], m[1]},\n\t\t\t{m[2], m[3]}\n\t};\n\n\t// the v matrix contains the eigen vectors\n\t// intialize to identity\n\tdouble v[2][2] = {\n\t\t{ 1, 0 },\n\t\t{ 0, 1 }\n\t};\n\n\t// initialize b and d to the diagonal of a\n\tdouble b[2] = {a[0][0], a[1][1]};\n\tdouble d[2] = {a[0][0], a[1][1]};\n\tdouble z[2] = {0};\n\n\tconst double eps = 0;//1.0e-15;\n\n\t// loop\n\tint n, nrot = 0;\n\tfor (n=0; n<NMAX; ++n)\n\t{\n\t\t// sum off-diagonal elements\n\t\tsm = fabs(a[0][1]);\n\t\tif (sm <= eps) break;\n\n\t\t// set the treshold\n\t\tif (n < 2) tresh = 0.2*sm/9.0; else tresh = 0.0;\n\n\t\t// loop over off-diagonal elements\n\t\tfor (i=0; i<1; ++i)\n\t\t{\n\t\t\tfor (j=i+1; j<2; ++j)\n\t\t\t{\n\t\t\t\tg = 100.0*fabs(a[i][j]);\n\n\t\t\t\t// after four sweeps, skip the rotation if the off-diagonal element is small\n\t\t\t\tif ((n > 3) && ((fabs(d[i])+g) == fabs(d[i]))\n\t\t\t\t\t\t\t&& ((fabs(d[j])+g) == fabs(d[j])))\n\t\t\t\t{\n\t\t\t\t\ta[i][j] = 0.0;\n\t\t\t\t}\n\t\t\t\telse if (fabs(a[i][j]) > tresh)\n\t\t\t\t{\n\t\t\t\t\th = d[j] - d[i];\n\t\t\t\t\tif ((fabs(h)+g) == fabs(h))\n\t\t\t\t\t\tt = a[i][j]/h;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tth = 0.5*h/a[i][j];\n\t\t\t\t\t\tt = 1.0/(fabs(th) + sqrt(1+th*th));\n\t\t\t\t\t\tif (th < 0.0) t = -t;\n\t\t\t\t\t}\n\n\t\t\t\t\tc = 1.0/sqrt(1.0 + t*t);\n\t\t\t\t\ts = t*c;\n\t\t\t\t\ttau = s/(1.0+c);\n\t\t\t\t\th = t*a[i][j];\n\t\t\t\t\tz[i] -= h;\n\t\t\t\t\tz[j] += h;\n\t\t\t\t\td[i] -= h;\n\t\t\t\t\td[j] += h;\n\t\t\t\t\ta[i][j] = 0;\n\n\t\t\t\t\tfor (k=  0; k<=i-1; ++k) { ROTATE(a, k, i, k, j) }\n\t\t\t\t\tfor (k=i+1; k<=j-1; ++k) { ROTATE(a, i, k, k, j) }\n\t\t\t\t\tfor (k=j+1; k<   2; ++k) { ROTATE(a, i, k, j, k) }\n\t\t\t\t\tfor (k=  0; k<   2; ++k) { ROTATE(v, k, i, k, j) }\n\t\t\t\t\t++nrot;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i=0; i<2; ++i) \n\t\t{\n\t\t\tb[i] += z[i];\n\t\t\td[i] = b[i];\n\t\t\tz[i] = 0.0;\n\t\t}\n\t}\n\n\t// we sure we converged\n\tassert(n < NMAX);\n\n\t// copy eigenvalues\n\tl[0] = d[0];\n\tl[1] = d[1];\n\n\t// copy eigenvectors\n\tif (r)\n\t{\n\t\tr[0] = v[0][0]; r[1] = v[1][0];\n\t\tr[2] = v[0][1]; r[3] = v[1][1];\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/FEWrinkleOgdenMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMembraneMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a wrinkle model for an Ogden material\n//\nclass FEWrinkleOgdenMaterial : public FEMembraneMaterial\n{\npublic:\n\tFEWrinkleOgdenMaterial(FEModel* pfem);\n\npublic: // material parameters\n\tdouble\tm_u;\n\tdouble\tm_a;\n\tdouble\tm_l0[2];\t// pre-stretch\n\tbool\tm_bwrinkle;\t// wrinkle flag\n\npublic: // material interface\n\n\t//! calculate stress at material point\n\tvoid Stress(FEMaterialPoint& pt, double s[3]) override;\n\n\t//! calculate tangent stiffness at material point\n\tvoid Tangent(FEMaterialPoint& pt, double D[3][3]) override;\n\nprotected:\n\tvoid principals(FEMaterialPoint& pt, double l[2], double v[4]);\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/FEYeoh.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEYeoh.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEYeoh, FEUncoupledMaterial)\n\tADD_PARAMETER(m_c[0], \"c1\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_c[1], \"c2\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_c[2], \"c3\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_c[3], \"c4\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_c[4], \"c5\")->setUnits(UNIT_PRESSURE);\n    ADD_PARAMETER(m_c[5], \"c6\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric stress\nmat3ds FEYeoh::DevStress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n    double c[MAX_TERMS];\n    \n\t// get material parameters\n    for (int i=0; i<MAX_TERMS; ++i) c[i] = m_c[i](mp);\n\n\t// determinant of deformation gradient\n\tdouble J = pt.m_J;\n\n\t// calculate deviatoric left Cauchy-Green tensor\n\tmat3ds B = pt.DevLeftCauchyGreen();\n\n\t// Invariant of B tilde (= invariants of C tilde)\n\tdouble I1 = B.tr();\n\n    double sum = 0;\n    for (int i=0; i<MAX_TERMS; ++i)\n        sum += c[i]*pow(I1-3,i);\n    \n\t// calculate sigma tilde\n\tmat3ds sig = B*(sum*2.0/J);\n\n\treturn sig.dev();\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deviatoric tangent\ntens4ds FEYeoh::DevTangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n    double c[MAX_TERMS];\n    \n    // get material parameters\n    for (int i=0; i<MAX_TERMS; ++i) c[i] = m_c[i](mp);\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n    \n    // calculate deviatoric left Cauchy-Green tensor\n    mat3ds B = pt.DevLeftCauchyGreen();\n    \n    // Invariant of B tilde (= invariants of C tilde)\n    double I1 = B.tr();\n    \n    double sum = 0;\n    for (int i=0; i<MAX_TERMS; ++i)\n        sum += c[i]*pow(I1-3,i);\n    double csum = 0;\n    for (int i=1; i<MAX_TERMS; ++i)\n        csum += c[i]*pow(I1-3,i-1);\n\n    // calculate sigma tilde\n    mat3ds sd = (B*(sum*2.0/J));\n    \n    // identity tensor\n    mat3dd I(1);\n    tens4ds IxI = dyad1s(I);\n    tens4ds I4 = dyad4s(I);\n    \n\ttens4ds BxB = dyad1s(B);\n\n\ttens4ds C = BxB*(csum*4.0/J);\n\n    C += - 1./3.*(ddots(C,IxI) - IxI*(C.tr()/3.))\n    + 2./3.*((I4-IxI/3.)*sd.tr()-dyad1s(sd.dev(),I));\n\n\treturn C;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate deviatoric strain energy density\ndouble FEYeoh::DevStrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double c[MAX_TERMS];\n    \n    // get material parameters\n    for (int i=0; i<MAX_TERMS; ++i) c[i] = m_c[i](mp);\n    \n    // determinant of deformation gradient\n    double J = pt.m_J;\n    \n    // calculate deviatoric left Cauchy-Green tensor\n    mat3ds B = pt.DevLeftCauchyGreen();\n    \n    // Invariant of B tilde (= invariants of C tilde)\n    double I1 = B.tr();\n    \n    double sed = 0;\n    for (int i=0; i<MAX_TERMS; ++i)\n        sed += c[i]*pow(I1-3,i+1);\n\n    return sed;\n}\n"
  },
  {
    "path": "FEBioMech/FEYeoh.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEUncoupledMaterial.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Yeoh material\n\nclass FEYeoh : public FEUncoupledMaterial\n{\npublic:\n    enum { MAX_TERMS = 6 };\n\npublic:\n\tFEYeoh(FEModel* pfem) : FEUncoupledMaterial(pfem) {}\n\npublic:\n\tFEParamDouble\tm_c[MAX_TERMS];\t//!< Yeoh coefficients\n\npublic:\n\t//! calculate deviatoric stress at material point\n\tmat3ds DevStress(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric tangent stiffness at material point\n\ttens4ds DevTangent(FEMaterialPoint& pt) override;\n\n\t//! calculate deviatoric strain energy density\n\tdouble DevStrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/ObjectDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"ObjectDataRecord.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModel.h>\n#include \"FERigidBody.h\"\n#include <FECore/FEMaterial.h>\n#include \"FEMechModel.h\"\n#include \"FERigidMaterial.h\"\n\n//-----------------------------------------------------------------------------\nObjectDataRecord::ObjectDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_RB) \n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid ObjectDataRecord::SetData(const char* szexpr)\n{\n\tchar szcopy[MAX_STRING] = {0};\n\tstrcpy(szcopy, szexpr);\n\tchar* sz = szcopy, *ch;\n\tm_Data.clear();\n\tstrcpy(m_szdata, szexpr);\n\tdo\n\t{\n\t\tch = strchr(sz, ';');\n\t\tif (ch) *ch++ = 0;\n\t\tFELogObjectData* pdata = fecore_new<FELogObjectData>(sz, GetFEModel());\n\t\tif (pdata) m_Data.push_back(pdata);\n\t\telse throw UnknownDataField(sz);\n\t\tsz = ch;\n\t}\n\twhile (ch);\n}\n\n//-----------------------------------------------------------------------------\ndouble ObjectDataRecord::Evaluate(int item, int ndata)\n{\n\tFEMechModel* fem = dynamic_cast<FEMechModel*>(GetFEModel());\n\n\tFEMesh& mesh = fem->GetMesh();\n\tint nrb = item - 1;\n\tif ((nrb < 0) || (nrb >= fem->Materials())) return 0;\n\n\tdouble val = 0;\n\n\t// find the rigid body that has this material\n\tint NRB = fem->RigidBodies();\n\tfor (int i=0; i<NRB; ++i)\n\t{\n\t\tFERigidBody& obj = *fem->GetRigidBody(i);\n\t\tif (obj.GetMaterialID() == nrb) return m_Data[ndata]->value(obj);\n\t}\n\n\treturn val;\n}\n\n//-----------------------------------------------------------------------------\nint ObjectDataRecord::Size() const { return (int)m_Data.size(); }\n\n//-----------------------------------------------------------------------------\nvoid ObjectDataRecord::SelectAllItems()\n{\n\tFEMechModel* fem = dynamic_cast<FEMechModel*>(GetFEModel());\n\n\tint n = 0, i;\n\tfor (i=0; i<fem->Materials(); ++i)\n\t{\n\t\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem->GetMaterial(i));\n\t\tif (pm) ++n;\n\t}\n\n\tif (n > 0)\n\t{\n\t\tm_item.resize(n);\n\t\tn = 0;\n\t\tfor (i=0; i<fem->Materials(); ++i)\n\t\t{\n\t\t\tFERigidMaterial* pm  = dynamic_cast<FERigidMaterial*>(fem->GetMaterial(i));\n\t\t\tif (pm)\n\t\t\t{\n\t\t\t\tm_item[n++] = i+1;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/ObjectDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreBase.h>\n#include <FECore/DataRecord.h>\n#include \"FERigidBody.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for object log data (e.g. rigid bodies)\nclass FEBIOMECH_API FELogObjectData : public FELogData\n{\n\tFECORE_SUPER_CLASS(FELOGOBJECTDATA_ID)\n\tFECORE_BASE_CLASS(FELogObjectData)\n\npublic:\n\tFELogObjectData(FEModel* fem) : FELogData(fem) {}\n\tvirtual ~FELogObjectData(){}\n\tvirtual double value(FERigidBody& rb) = 0;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API ObjectDataRecord : public DataRecord\n{\npublic:\n\tObjectDataRecord(FEModel* pfem);\n\tdouble Evaluate(int item, int ndata) override;\n\tvoid SetData(const char* sz) override;\n\tvoid SelectAllItems() override;\n\tint Size() const override;\n\nprivate:\n\tvector<FELogObjectData*>\tm_Data;\n};\n"
  },
  {
    "path": "FEBioMech/RigidBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"RigidBC.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEMesh.h>\n#include \"FERigidBody.h\"\n#include <FECore/FEMaterial.h>\n#include <FECore/FELoadCurve.h>\n#include \"FEMechModel.h\"\n#include \"FERigidMaterial.h\"\n#include <FECore/DumpStream.h>\n#include <FECore/log.h>\n\n//=============================================================================\nFERigidBC::FERigidBC(FEModel* fem) : FEBoundaryCondition(fem)\n{\n\tm_rigidMat = -1;\n\tm_rb = -1;\n\n\tm_binit = false;\n}\n\nvoid FERigidBC::SetRigidMaterial(int rigidMat)\n{\n\tm_rigidMat = rigidMat;\n}\n\nvoid FERigidBC::InitTimeStep()\n{\n\n}\n\nbool FERigidBC::Init()\n{\n\t// Make sure the rigid material ID is valid\n\tFEModel& fem = *GetFEModel();\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1));\n\tif (pm == nullptr) return false;\n\n\tm_rb = pm->GetRigidBodyID(); assert(m_rb >= 0);\n\n\tm_binit = true;\n\n\treturn FEBoundaryCondition::Init();\n}\n\nvoid FERigidBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFERigidBC* rbc = dynamic_cast<FERigidBC*>(pbc);\n\tGetParameterList() = rbc->GetParameterList();\n\tm_rb = rbc->m_rb;\n}\n\nvoid FERigidBC::Serialize(DumpStream& ar)\n{\n\tar & m_rb & m_binit;\n\tFEBoundaryCondition::Serialize(ar);\n}\n\nFERigidBody& FERigidBC::GetRigidBody()\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& RB = *fem.GetRigidBody(m_rb);\n\treturn RB;\n}\n\n//=============================================================================\nFERigidFixedBC::FERigidFixedBC(FEModel* pfem) : FERigidBC(pfem)\n{\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidFixedBCNew, FERigidFixedBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_dof[0], \"Rx_dof\")->setLongName(\"X-displacement\");\n\tADD_PARAMETER(m_dof[1], \"Ry_dof\")->setLongName(\"Y-displacement\");\n\tADD_PARAMETER(m_dof[2], \"Rz_dof\")->setLongName(\"Z-displacement\");\n\tADD_PARAMETER(m_dof[3], \"Ru_dof\")->setLongName(\"X-rotation\");\n\tADD_PARAMETER(m_dof[4], \"Rv_dof\")->setLongName(\"Y-rotation\");\n\tADD_PARAMETER(m_dof[5], \"Rw_dof\")->setLongName(\"Z-rotation\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidFixedBCNew::FERigidFixedBCNew(FEModel* pfem) : FERigidFixedBC(pfem)\n{\n\tfor (int i = 0; i < 6; ++i) m_dof[i] = false;\n\tm_binit = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidFixedBCNew::Activate()\n{\n\tFERigidFixedBC::Activate();\n\n\tif (m_binit == false) Init();\n\tif (m_binit)\n\t{\n\t\tFERigidBody& RB = GetRigidBody();\n\n\t\t// we only fix the open dofs. If a user accidentally applied a fixed and prescribed\n\t\t// rigid degree of freedom, then we make sure the prescribed takes precedence.\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tif (m_dof[i] && (RB.m_BC[i] == DOF_OPEN)) RB.m_BC[i] = DOF_FIXED;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidFixedBCNew::Deactivate()\n{\n\tif (m_binit)\n\t{\n\t\tFERigidBody& RB = GetRigidBody();\n\n\t\t// Since fixed rigid dofs can be overwritten by prescribed dofs, \n\t\t// we have to make sure that this dof is actually a fixed dof.\n\t\tfor (int i = 0; i < 6; ++i)\n\t\t{\n\t\t\tif (m_dof[i] && (RB.m_BC[i] == DOF_FIXED)) RB.m_BC[i] = DOF_OPEN;\n\t\t}\n\t}\n\n\tFERigidBC::Deactivate();\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidFixedBCOld, FERigidFixedBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_dofs, \"dofs\", 0, \"Rx\\0Ry\\0Rz\\0Ru\\0Rv\\0Rw\\0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidFixedBCOld::FERigidFixedBCOld(FEModel* pfem) : FERigidFixedBC(pfem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidFixedBCOld::Init()\n{\n\t// make sure we have a valid dof\n\t// TODO: Can I test this automatically during validation?\n\tfor (int i = 0; i < m_dofs.size(); ++i)\n\t{\n\t\tint dof_i = m_dofs[i];\n\t\tif ((dof_i < 0) || (dof_i >= 6))\n\t\t{\n\t\t\tfeLogError(\"Invalid value for dofs of fixed rigid constraint %s\", GetName().c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn FERigidFixedBC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidFixedBCOld::Activate()\n{\n\tFERigidFixedBC::Activate();\n\n\tif (m_binit == false) Init();\n\tif (m_binit)\n\t{\n\t\tFERigidBody& RB = GetRigidBody();\n\n\t\t// we only fix the open dofs. If a user accidentally applied a fixed and prescribed\n\t\t// rigid degree of freedom, then we make sure the prescribed takes precedence.\n\t\tfor (int i = 0; i < m_dofs.size(); ++i)\n\t\t{\n\t\t\tint dof_i = m_dofs[i];\n\t\t\tif (RB.m_BC[dof_i] == DOF_OPEN) RB.m_BC[dof_i] = DOF_FIXED;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidFixedBCOld::Deactivate()\n{\n\tif (m_binit)\n\t{\n\t\tFERigidBody& RB = GetRigidBody();\n\n\t\t// Since fixed rigid dofs can be overwritten by prescribed dofs, \n\t\t// we have to make sure that this dof is actually a fixed dof.\n\t\tfor (int i = 0; i < m_dofs.size(); ++i)\n\t\t{\n\t\t\tint dof_i = m_dofs[i];\n\t\t\tif (RB.m_BC[dof_i] == DOF_FIXED) RB.m_BC[dof_i] = DOF_OPEN;\n\t\t}\n\t}\n}\n\n//=============================================================================\nFERigidPrescribedBC::FERigidPrescribedBC(FEModel* pfem) : FERigidBC(pfem)\n{\n\tm_dof = -1;\n\tm_val = 0.0;\n\tm_ref= 0.0; \n\tm_brel = false; \n\tm_binit = false;\n}\n\n//-----------------------------------------------------------------------------\nbool FERigidPrescribedBC::Init()\n{\n\t// make sure we have a valid dof\n\tif ((m_dof < 0)||(m_dof >=6)) return false;\n\tm_binit = true;\n\treturn FERigidBC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrescribedBC::Activate()\n{\n\t// don't forget to call the base class\n\tFERigidBC::Activate();\n\n\tif (m_binit == false) Init();\n\n\t// get the rigid body\n\tFERigidBody& RB = GetRigidBody();\n\n\t// set some stuff\n\tRB.m_pDC[m_dof] = this;\n\n\t// mark the dof as prescribed\n\tRB.m_BC[m_dof] = DOF_PRESCRIBED;\n\n\t// set the relative offset\n\tm_ref = 0.0;\n\tif (m_brel)\n\t{\n\t\tquatd Q = RB.GetRotation();\n\t\tvec3d q = Q.GetRotationVector();\n\t\tswitch (m_dof)\n\t\t{\n\t\tcase 0: m_ref = RB.m_rt.x - RB.m_r0.x; break;\n\t\tcase 1: m_ref = RB.m_rt.y - RB.m_r0.y; break;\n\t\tcase 2: m_ref = RB.m_rt.z - RB.m_r0.z; break;\n\t\tcase 3: m_ref = q.x; break;\n\t\tcase 4: m_ref = q.y; break;\n\t\tcase 5: m_ref = q.z; break;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrescribedBC::Deactivate()\n{\n\tFERigidBC::Deactivate();\n\n\t// get the rigid body\n\t// Since Deactivate is called before Init (for multi-step analysis; in the FEBio input)\n\t// we have to make sure the data is initialized\n\tif (m_binit)\n\t{\n\t\tFERigidBody& RB = GetRigidBody();\n\n\t\t// turn off the prescribed displacement\n\t\tRB.m_pDC[m_dof] = 0;\n\t\tRB.m_BC[m_dof] = DOF_OPEN;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrescribedBC::Serialize(DumpStream& ar)\n{\n\tFERigidBC::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_dof & m_ref & m_binit & m_brel;\n}\n\n//-----------------------------------------------------------------------------\ndouble FERigidPrescribedBC::Value()\n{\n\treturn m_val + m_ref;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidPrescribedBC::InitTimeStep()\n{\n\tFERigidBody& RB = GetRigidBody();\n\tint I = GetBC();\n\tRB.m_dul[I] = Value() - RB.m_Ut[I];\n}\n\n//===============================================================================\nBEGIN_FECORE_CLASS(FERigidDisplacement, FERigidPrescribedBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_bc, \"dof\", 0, \"$(dof_list:displacement)\");\n\tADD_PARAMETER(m_val, \"value\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_LENGTH);\n\tADD_PARAMETER(m_brel, \"relative\");\nEND_FECORE_CLASS();\n\nFERigidDisplacement::FERigidDisplacement(FEModel* fem) : FERigidPrescribedBC(fem)\n{\n\tm_bc = -1;\n}\n\nbool FERigidDisplacement::Init()\n{\n\tint dofX = GetDOFIndex(\"x\");\n\tint dofY = GetDOFIndex(\"y\");\n\tint dofZ = GetDOFIndex(\"z\");\n\tif (m_bc == dofX) m_dof = 0;\n\tif (m_bc == dofY) m_dof = 1;\n\tif (m_bc == dofZ) m_dof = 2;\n\n\treturn FERigidPrescribedBC::Init();\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidRotation, FERigidPrescribedBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_bc, \"dof\", 0, \"$(dof_list:rigid rotation)\");\n\tADD_PARAMETER(m_val, \"value\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE)->setUnits(UNIT_RADIAN);\n\tADD_PARAMETER(m_brel, \"relative\");\nEND_FECORE_CLASS();\n\nFERigidRotation::FERigidRotation(FEModel* fem) : FERigidPrescribedBC(fem)\n{\n\tm_bc = -1;\n}\n\nbool FERigidRotation::Init()\n{\n\tint dofRu = GetDOFIndex(\"Ru\");\n\tint dofRv = GetDOFIndex(\"Rv\");\n\tint dofRw = GetDOFIndex(\"Rw\");\n\tif (m_bc == dofRu) m_dof = 3;\n\tif (m_bc == dofRv) m_dof = 4;\n\tif (m_bc == dofRw) m_dof = 5;\n\n\treturn FERigidPrescribedBC::Init();\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FERigidPrescribedOld, FERigidPrescribedBC)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"Rx\\0Ry\\0Rz\\0Ru\\0Rv\\0Rw\\0\");\n\tADD_PARAMETER(m_val, \"value\");\n\tADD_PARAMETER(m_brel, \"relative\");\nEND_FECORE_CLASS();\n\nFERigidPrescribedOld::FERigidPrescribedOld(FEModel* fem) : FERigidPrescribedBC(fem) {}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidIC, FEInitialCondition)\n\tADD_PARAMETER(m_rigidMat, \"rb\")->setEnums(\"$(rigid_materials)\")->setLongName(\"Rigid material\");\nEND_FECORE_CLASS();\n\nFERigidIC::FERigidIC(FEModel* fem) : FEInitialCondition(fem)\n{\n\tm_rigidMat = -1;\n\tm_rb = -1;\n}\n\nbool FERigidIC::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tint matIndex = m_rigidMat - 1;\n\tif ((matIndex < 0) || (matIndex >= fem.Materials()))\n\t{\n\t\tfeLogError(\"Invalid value for rb\");\n\t\treturn false;\n\t}\n\n\t// Make sure the rigid material ID is valid\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(matIndex));\n\tif (pm == nullptr) return false;\n\n\treturn FEInitialCondition::Init();\n}\n\nvoid FERigidIC::Activate()\n{\n\t// Get the Rigidbody ID\n\tFEModel& fem = *GetFEModel();\n\tFERigidMaterial* pm = dynamic_cast<FERigidMaterial*>(fem.GetMaterial(m_rigidMat - 1)); assert(pm);\n\tm_rb = pm->GetRigidBodyID(); assert(m_rb >= 0);\n\n\tFEInitialCondition::Activate();\n}\n\nvoid FERigidIC::Serialize(DumpStream& ar)\n{\n\tar& m_rb;\n\tFEInitialCondition::Serialize(ar);\n}\n\nFERigidBody& FERigidIC::GetRigidBody()\n{\n\tFEMechModel& fem = static_cast<FEMechModel&>(*GetFEModel());\n\tFERigidBody& RB = *fem.GetRigidBody(m_rb);\n\treturn RB;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidBodyVelocity, FERigidIC)\n\tADD_PARAMETER(m_vel, \"value\")->setUnits(UNIT_VELOCITY);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidBodyVelocity::FERigidBodyVelocity(FEModel* pfem) : FERigidIC(pfem) \n{\n\tm_vel = vec3d(0, 0, 0);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBodyVelocity::Activate()\n{\n\tFERigidIC::Activate();\n\tFERigidBody& RB = GetRigidBody();\n\tRB.m_vp = RB.m_vt = m_vel;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FERigidBodyAngularVelocity, FERigidIC)\n\tADD_PARAMETER(m_w, \"value\")->setUnits(UNIT_ANGULAR_VELOCITY);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFERigidBodyAngularVelocity::FERigidBodyAngularVelocity(FEModel* pfem) : FERigidIC(pfem)\n{\n\tm_w = vec3d(0, 0, 0);\n}\n\n//-----------------------------------------------------------------------------\nvoid FERigidBodyAngularVelocity::Activate()\n{\n\tFERigidIC::Activate();\n\tFERigidBody& RB = GetRigidBody();\n\tRB.m_wp = RB.m_wt = m_w;\n\n\t// TODO: We need to set the initial angular momentum as well,\n\t//       but I'd rather have the RB do this somewhere else instead.\n\tRB.m_hp = RB.m_ht = RB.m_moi * m_w;\n}\n"
  },
  {
    "path": "FEBioMech/RigidBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FEInitialCondition.h>\n#include \"febiomech_api.h\"\n\nclass FERigidBody;\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FERigidBC : public FEBoundaryCondition\n{\n\tFECORE_BASE_CLASS(FERigidBC)\n\npublic:\n\tFERigidBC(FEModel* fem);\n\n\tbool Init() override;\n\n\tvirtual void InitTimeStep();\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tFERigidBody& GetRigidBody();\n\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\n\tvoid SetRigidMaterial(int rigidMat);\n\nprivate:\n\tint\t\tm_rb;\t\t\t// rigid body ID\n\nprotected:\n\tint\t\tm_rigidMat;\t\t// rigid material ID\n\tbool\tm_binit;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMECH_API FERigidIC : public FEInitialCondition\n{\n\tFECORE_BASE_CLASS(FERigidIC)\n\npublic:\n\tFERigidIC(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Activate() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tFERigidBody& GetRigidBody();\n\nprivate:\n\tint\t\tm_rigidMat;\t\t// rigid material ID\n\tint\t\tm_rb;\t\t\t// rigid body ID\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! fixed rigid body constraint\nclass FEBIOMECH_API FERigidFixedBC : public FERigidBC\n{\npublic:\n\tFERigidFixedBC(FEModel* pfem);\n};\n\n//-----------------------------------------------------------------------------\n//! fixed rigid body constraint\nclass FEBIOMECH_API FERigidFixedBCNew : public FERigidFixedBC\n{\npublic:\n\tFERigidFixedBCNew(FEModel* pfem);\n\n\tvoid Activate() override;\n\n\tvoid Deactivate() override;\n\npublic:\n\tbool\tm_dof[6];\t\t//!< constrained dof list\n\nprivate:\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! fixed rigid body constraint\nclass FEBIOMECH_API FERigidFixedBCOld : public FERigidFixedBC\n{\npublic:\n\tFERigidFixedBCOld(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid Activate() override;\n\n\tvoid Deactivate() override;\n\npublic:\n\tvector<int>\tm_dofs;\t\t//!< constrained dof list\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n//! rigid body displacement\n\nclass FEBIOMECH_API FERigidPrescribedBC : public FERigidBC\n{\npublic:\n\tFERigidPrescribedBC(FEModel* pfem);\n\n\tbool Init() override;\n\n\tdouble Value();\n\n\tvoid InitTimeStep() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tvoid Activate() override;\n\n\tvoid Deactivate() override;\n\n\tvoid SetBC(int bc) { m_dof = bc; }\n\tint GetBC() const { return m_dof; }\n\n\tvoid SetRelativeFlag(bool b) { m_brel = b; }\n\tbool GetRelativeFlag() const { return m_brel; }\n\n\tvoid SetValue(double v) { m_val = v; }\n\nprotected:\n\tint\t\tm_dof;\t\t//!< displacement direction\n\tdouble\tm_val;\t//!< displacement value\n\tdouble\tm_ref;\t//!< reference value for relative displacement\n\tbool\tm_brel;\t//!< relative displacement flag\n\n\tbool\tm_binit;\t//!init flag\n};\n\n//-----------------------------------------------------------------------------\n//! prescribed rigid body rotation\n\nclass FEBIOMECH_API FERigidDisplacement : public FERigidPrescribedBC\n{\npublic:\n\tFERigidDisplacement(FEModel* pfem);\n\n\tbool Init() override;\n\nprivate:\n\tint\tm_bc;\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n//! prescribed rigid body rotation\n\nclass FEBIOMECH_API FERigidRotation : public FERigidPrescribedBC\n{\npublic:\n\tFERigidRotation(FEModel* pfem);\n\n\tbool Init() override;\n\nprivate:\n\tint\tm_bc;\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Obsolete rigid prescribed bc class. Used only for backward compatibility.\nclass FEBIOMECH_API FERigidPrescribedOld : public FERigidPrescribedBC\n{\npublic:\n\tFERigidPrescribedOld(FEModel* pfem);\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! rigid body initial velocity\nclass FEBIOMECH_API FERigidBodyVelocity : public FERigidIC\n{\npublic:\n\tFERigidBodyVelocity(FEModel* pfem);\n\n\tvoid Activate() override;\n\npublic:\n\tvec3d\tm_vel;\t//!< initial velocity\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! rigid body initial angular velocity\nclass FEBIOMECH_API FERigidBodyAngularVelocity : public FERigidIC\n{\npublic:\n\tFERigidBodyAngularVelocity(FEModel* pfem);\n\n\tvoid Activate() override;\n\npublic:\n\tvec3d\tm_w;\t//!< value\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMech/adcm.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/ad.h>\n#include <FECore/ad2.h>\n\nnamespace ad {\n\n\ttemplate <class T>\n\tdouble StrainEnergy(T* p, FEMaterialPoint& mp)\n\t{\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t::mat3ds C = pt.RightCauchyGreen();\n\t\tauto W = std::bind(&T::StrainEnergy_AD, p, mp, std::placeholders::_1);\n\t\treturn ad::Evaluate(W, C);\n\t}\n\n\ttemplate <class T>\n\t::mat3ds PK2Stress(T* p, FEMaterialPoint& mp, ::mat3ds& C)\n\t{\n\t\tauto W = std::bind(&T::StrainEnergy_AD, p, mp, std::placeholders::_1);\n\t\treturn ad::Derive(W, C) * 2.0;\n\t}\n\n\ttemplate <class T>\n\t::mat3ds PK2Stress(T* p, FEMaterialPoint& mp)\n\t{\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t::mat3ds C = pt.RightCauchyGreen();\n\t\treturn PK2Stress(p, mp, C);\n\t}\n\n\ttemplate <class T>\n\t::tens4ds Tangent(T* p, FEMaterialPoint& mp, ::mat3ds& C)\n\t{\n\t\tauto S = std::bind(&T::PK2Stress_AD, p, mp, std::placeholders::_1);\n\t\treturn Derive(S, C) * 2.0;\n\t}\n\n\ttemplate <class T>\n\t::tens4ds Tangent(T* p, FEMaterialPoint& mp)\n\t{\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t::mat3ds C = pt.RightCauchyGreen();\n\t\treturn Tangent(p, mp, C);\n\t}\n}\n\nnamespace ad2 {\n\ttemplate <class T>\n\t::mat3ds PK2Stress(T* p, FEMaterialPoint& mp)\n\t{\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t::mat3ds C = pt.RightCauchyGreen();\n\t\tauto W = std::bind(&T::StrainEnergy_AD2, p, mp, std::placeholders::_1);\n\t\treturn ad2::Derive(W, C) * 2.0;\n\t}\n\n\ttemplate <class T>\n\t::tens4ds Tangent(T* p, FEMaterialPoint& mp)\n\t{\n\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t::mat3ds C = pt.RightCauchyGreen();\n\t\tauto W = std::bind(&T::StrainEnergy_AD2, p, mp, std::placeholders::_1);\n\t\treturn ad2::Derive2(W, C) * 4.0;\n\t}\n}\n"
  },
  {
    "path": "FEBioMech/febiomech_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febiomech_EXPORTS\n\t\t\t#define FEBIOMECH_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOMECH_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIOMECH_API\n\t#endif\n#else\n\t#define FEBIOMECH_API\n#endif\n"
  },
  {
    "path": "FEBioMech/gauss.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// gaussian quadrature\nconst int nint1 = 1;\nconst double gp1[nint1] = {\n    0\n};\nconst double gw1[nint1] = {\n    2\n};\n\n\nconst int nint2 = 2;\nconst double gp2[nint2] = {\n    -sqrt(3.)/3.,\n    sqrt(3.)/3.\n};\nconst double gw2[nint2] = {\n    1,\n    1\n};\n\nconst int nint3 = 3;\nconst double gp3[nint3] = {\n    -sqrt(3./5.),\n    0.,\n    sqrt(3./5.)\n};\nconst double gw3[nint3] = {\n    5./9.,\n    8./9.,\n    5./9.\n};\n\nconst int nint4 = 4;\nconst double gp4[nint4] = {\n    -sqrt((3+2*sqrt(6./5.))/7.),\n    -sqrt((3-2*sqrt(6./5.))/7.),\n    sqrt((3-2*sqrt(6./5.))/7.),\n    sqrt((3+2*sqrt(6./5.))/7.)\n};\nconst double gw4[nint4] = {\n    (18-sqrt(30.))/36.,\n    (18+sqrt(30.))/36.,\n    (18+sqrt(30.))/36.,\n    (18-sqrt(30.))/36.\n};\n\nconst int nint5 = 5;\nconst double gp5[nint5] = {\n    -1./3.*sqrt(5+2*sqrt(10./7.)),\n    -1./3.*sqrt(5-2*sqrt(10./7.)),\n    0.,\n    1./3.*sqrt(5-2*sqrt(10./7.)),\n    1./3.*sqrt(5+2*sqrt(10./7.))\n};\nconst double gw5[nint5] = {\n    (322-13*sqrt(70.))/900.,\n    (322+13*sqrt(70.))/900.,\n    128./225.,\n    (322+13*sqrt(70.))/900.,\n    (322-13*sqrt(70.))/900.\n};\n\nconst int nint6 = 6;\nconst double gp6[nint6] = {\n    0.661209386466264,\n    -0.661209386466264,\n    -0.238619186083196,\n    0.238619186083196,\n    -0.932469514203152,\n    0.932469514203152\n};\nconst double gw6[nint6] = {\n    0.360761573048138,\n    0.360761573048138,\n    0.467913934572691,\n    0.467913934572691,\n    0.171324492379170,\n    0.171324492379170\n};\n\nconst int nint7 = 7;\nconst double gp7[nint7] = {\n    0.000000000000000,\n    0.405845151377397,\n    -0.405845151377397,\n    -0.741531185599394,\n    0.741531185599394,\n    -0.949107912342758,\n    0.949107912342758\n};\nconst double gw7[nint7] = {\n    0.417959183673469,\n    0.381830050505118,\n    0.381830050505118,\n    0.279705391489276,\n    0.279705391489276,\n    0.129484966168869,\n    0.129484966168869\n};\n\nconst int nint8 = 8;\nconst double gp8[nint8] = {\n    -0.183434642495649,\n    0.183434642495649,\n    -0.525532409916329,\n    0.525532409916329,\n    -0.796666477413626,\n    0.796666477413626,\n    -0.960289856497536,\n    0.960289856497536\n};\nconst double gw8[nint8] = {\n    0.362683783378362,\n    0.362683783378362,\n    0.313706645877887,\n    0.313706645877887,\n    0.222381034453374,\n    0.222381034453374,\n    0.101228536290376,\n    0.101228536290376\n};\n\nconst int nint9 = 9;\nconst double gp9[nint9] = {\n    0.000000000000000,\n    -0.836031107326635,\n    0.836031107326635,\n    -0.968160239507626,\n    0.968160239507626,\n    -0.324253423403808,\n    0.324253423403808,\n    -0.613371432700590,\n    0.613371432700590\n};\nconst double gw9[nint9] = {\n    0.330239355001259,\n    0.180648160694857,\n    0.180648160694857,\n    0.081274388361574,\n    0.081274388361574,\n    0.312347077040002,\n    0.312347077040002,\n    0.260610696402935,\n    0.260610696402935\n};\n\nconst int nint10 = 10;\nconst double gp10[nint10] = {\n    -0.148874338981631,\n    0.148874338981631,\n    -0.433395394129247,\n    0.433395394129247,\n    -0.679409568299024,\n    0.679409568299024,\n    -0.865063366688984,\n    0.865063366688984,\n    -0.973906528517171,\n    0.973906528517171\n};\nconst double gw10[nint10] = {\n    0.295524224714752,\n    0.295524224714752,\n    0.269266719309996,\n    0.269266719309996,\n    0.219086362515982,\n    0.219086362515982,\n    0.149451349150580,\n    0.149451349150580,\n    0.0666713443086881,\n    0.0666713443086881\n};\n"
  },
  {
    "path": "FEBioMech/gausskronrod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n//gaussian kronrod\n\nconst int nint7 = 7;\nconst double gp7[nint7] = {\n    -0.960491268708,-0.774596669241,-0.434243749347,0,0.434243749347,0.774596669241,0.960491268708\n    };\nconst double gw7[nint7] = {\n    0.104656226026,0.268488089868,0.401397414776,0.450916538658,0.401397414776,0.268488089868,0.104656226026\n};\n\nconst int nint11 = 11;\nconst double gp11[nint11] = {\n    0.984085360095,0.906179845939,0.754166726571,0.538469310106,0.279630413162,0,-0.279630413162,-0.538469310106,-0.754166726571,-0.906179845939,-0.984085360095\n\n};\nconst double gw11[nint11] = {\n    0.0425820367511,0.115233316622,0.186800796556,0.241040339229,0.272849801913,0.282987417857,0.272849801913,0.241040339229,0.186800796556,0.115233316622,0.0425820367511\n\n};\n\n\n\nconst int nint15 = 15;\nconst double gp15[nint15] = {\n    -0.9914553711208126,\n    -0.9491079123427585,\n    -0.8648644233597691,\n    -0.7415311855993944,\n    -0.5860872354676911,\n    -0.4058451513773972,\n    -0.2077849550078985,\n    0, 0.2077849550078985,\n    0.4058451513773972,\n    0.5860872354676911,\n    0.7415311855993944,\n    0.8648644233597691,\n    0.9491079123427585,\n    0.9914553711208126\n};\nconst double gw15[nint15] = {\n    0.02293532201052922,\n    0.06309209262997855,\n    0.1047900103222502,\n    0.1406532597155259,\n    0.1690047266392679,\n    0.1903505780647854,\n    0.2044329400752989,\n    0.2094821410847278,\n    0.2044329400752989,\n    0.1903505780647854,\n    0.1690047266392679,\n    0.1406532597155259,\n    0.1047900103222502,\n    0.06309209262997855,\n    0.02293532201052922\n};\n\nconst int nint19 = 19;\nconst double gp19[nint19] = {\n    0.994678160677,0.968160239508,0.91496350725,0.836031107327,0.734486765184,0.613371432701,0.475462479112,0.324253423404,0.164223563615,0,-0.164223563615,-0.324253423404,-0.475462479112,-0.613371432701,-0.734486765184,-0.836031107327,-0.91496350725,-0.968160239508,-0.994678160677\n\n};\nconst double gw19[nint19] = {\n    0.0143047756438,0.0396318951603,0.0665181559403,0.0907906816887,0.111789134684,0.130001406855,0.145239588384,0.156413527788,0.16286282744,0.164896012828,0.16286282744,0.156413527788,0.145239588384,0.130001406855,0.111789134684,0.0907906816887,0.0665181559403,0.0396318951603,0.0143047756438\n\n};\n\nconst int nint23 = 23;\nconst double gp23[nint23] = {\n    0.99636961389,0.978228658146,0.941677108578,0.887062599768,0.816057456656,0.730152005574,0.630599520162,0.519096129207,0.397944140952,0.269543155952,0.136113000799,0,-0.136113000799,-0.269543155952,-0.397944140952,-0.519096129207,-0.630599520162,-0.730152005574,-0.816057456656,-0.887062599768,-0.941677108578,-0.978228658146,-0.99636961389\n\n};\nconst double gw23[nint23] = {\n    0.00976544104596,0.0271565546821,0.0458293785644,0.0630974247504,0.0786645719322,0.0929530985969,0.105872074481,0.116739502461,0.1251587991,0.13128068423,0.1351935728,0.136577794711,0.1351935728,0.13128068423,0.1251587991,0.116739502461,0.105872074481,0.0929530985969,0.0786645719322,0.0630974247504,0.0458293785644,0.0271565546821,0.00976544104596\n\n};\n\nconst int nint27 = 27;\nconst double gp27[nint27] = {\n    0.997366176995,0.984183054719,0.957552468386,0.917598399223,0.865333160266,0.801578090733,0.726948849321,0.64234933944,0.549079957957,0.448492751036,0.341832463022,0.230458315955,0.115971089745,0,-0.115971089745,-0.230458315955,-0.341832463022,-0.448492751036,-0.549079957957,-0.64234933944,-0.726948849321,-0.801578090733,-0.865333160266,-0.917598399223,-0.957552468386,-0.984183054719,-0.997366176995\n\n};\nconst double gw27[nint27] = {\n    0.00708784635125,0.0197537463827,0.0334435899896,0.0462790179738,0.0581152104231,0.0693036332478,0.0798059621695,0.0891684418775,0.0971417348761,0.103830601169,0.109266351095,0.113210259172,0.115488799091,0.116209612363,0.115488799091,0.113210259172,0.109266351095,0.103830601169,0.0971417348761,0.0891684418775,0.0798059621695,0.0693036332478,0.0581152104231,0.0462790179738,0.0334435899896,0.0197537463827,0.00708784635125\n\n};\n\n\n\n"
  },
  {
    "path": "FEBioMech/geodesic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// This file contains a list of integration points and weights to integrate\n// over a unit sphere in spherical coordinates.\n\n// \"low\" resolution\nconst int NSTL = 320;\nconst double THETAL[NSTL] = {\n\t 1.884960, 2.28362, 2.37224, 2.40299,1.48629,1.88496,2.07108,1.39767,\n\t 1.698830, 1.36692, 1.88496, 2.17290,2.29151,1.59701,1.88496,1.47840,\n\t 3.141590,-2.74292,-2.65431,-2.62356,2.74292,-3.14159,-2.95547,2.65431,\n\t 2.955470, 2.62356,-3.14159,-2.85365,-2.73503,2.85365,3.14159,2.73503,\n\t-1.884960,-1.48629,-1.39767,-1.36692,-2.28362,-1.88496,-1.69883,-2.37224,\n\t-2.071080,-2.40299,-1.88496,-1.59701,-1.4784,-2.1729,-1.88496,-2.29151,\n\t-0.628319,-0.229651,-0.141035,-0.110281,-1.02699,-0.628319,-0.442194,-1.1156,\n\t-0.814443,-1.14636,-0.628319,-0.340375,-0.22176,-0.916262,-0.628319,-1.03488,\n\t 0.628319, 1.02699,1.1156,1.14636,0.229651,0.628319,0.814443,0.141035,\n\t 0.442194, 0.110281,0.628319,0.916262,1.03488,0.340375,0.628319,0.22176,\n\t-0.738600,-0.769354,-0.857969,-1.25664,-1.07051,-1.25664,-1.6553,-1.44276,\n\t-1.743920,-1.77467,-0.850079,-0.968693,-1.25664,-1.25664,-1.54458,-1.6632,\n\t 0.518037, 0.487283,0.398668,0.,0.186124,0.,-0.398668,-0.186124,\n\t-0.487283,-0.518037,0.406559,0.287944,0.,0.,-0.287944,-0.406559,\n\t 1.774670, 1.74392,1.6553,1.25664,1.44276,1.25664,0.857969,1.07051,\n\t 0.769354, 0.7386,1.6632,1.54458,1.25664,1.25664,0.968693,0.850079,\n\t 3.031310, 3.00056,2.91194,2.51327,2.6994,2.51327,2.11461,2.32715,\n\t 2.025990, 1.99524,2.91983,2.80122,2.51327,2.51327,2.22533,2.10672,\n\t-1.995240,-2.02599,-2.11461,-2.51327,-2.32715,-2.51327,-2.91194,-2.6994,\n\t-3.000560,-3.03131,-2.10672,-2.22533,-2.51327,-2.51327,-2.80122,-2.91983,\n\t 1.418940, 1.71865,2.05126,2.35097,1.57042,1.88496,2.19949,1.72788,\n\t 2.042040, 1.88496,1.55369,1.88496,2.21623,1.72788,2.04204,1.88496,\n\t 2.675580, 2.97528,-2.97528,-2.67558,2.82706,3.14159,-2.82706,2.98451,\n\t-2.984510, 3.14159,2.81032,3.14159,-2.81032,2.98451,-2.98451,3.14159,\n\t-2.350970,-2.05126,-1.71865,-1.41894,-2.19949,-1.88496,-1.57042,-2.04204,\n\t-1.727880,-1.88496,-2.21623,-1.88496,-1.55369,-2.04204,-1.72788,-1.88496,\n\t-1.094340,-0.794628,-0.462009,-0.162302,-0.94285,-0.628319,-0.313787,-0.785398,\n\t-0.471239,-0.628319,-0.959588,-0.628319,-0.297049,-0.785398,-0.471239,-0.628319,\n\t 0.162302, 0.462009,0.794628,1.09434,0.313787,0.628319,0.94285,0.471239,\n\t 0.785398, 0.628319,0.297049,0.628319,0.959588,0.471239,0.785398,0.628319,\n\t-0.790621,-1.09033,-1.42295,-1.72265,-0.942106,-1.25664,-1.57117,-1.09956,\n\t-1.413720,-1.25664,-0.925367,-1.25664,-1.58791,-1.09956,-1.41372,-1.25664,\n\t 0.466017, 0.166309,-0.166309,-0.466017,0.314531,0.,-0.314531,0.15708,\n\t-0.157080, 0.,0.33127,0.,-0.33127,0.15708,-0.15708,0.,\n\t 1.722650, 1.42295,1.09033,0.790621,1.57117,1.25664,0.942106,1.41372,\n\t 1.099560, 1.25664,1.58791,1.25664,0.925367,1.41372,1.09956,1.25664,\n\t 2.979290, 2.67958,2.34697,2.04726,2.82781,2.51327,2.19874,2.67035,\n\t 2.356190, 2.51327,2.84454,2.51327,2.182,2.67035,2.35619,2.51327,\n\t-2.047260,-2.34697,-2.67958,-2.97929,-2.19874,-2.51327,-2.82781,-2.35619,\n\t-2.670350,-2.51327,-2.182,-2.51327,-2.84454,-2.35619,-2.67035,-2.51327};\nconst double PHIL[NSTL] = {\n\t0.156457,0.41342,0.708355,0.983031,0.41342,0.652358,0.932965,0.708355,\n\t0.932965,0.983031,0.292631,0.580336,0.880271,0.580336,0.833138,0.880271,\n\t0.156457,0.41342,0.708355,0.983031,0.41342,0.652358,0.932965,0.708355,\n\t0.932965,0.983031,0.292631,0.580336,0.880271,0.580336,0.833138,0.880271,\n\t0.156457,0.41342,0.708355,0.983031,0.41342,0.652358,0.932965,0.708355,\n\t0.932965,0.983031,0.292631,0.580336,0.880271,0.580336,0.833138,0.880271,\n\t0.156457,0.41342,0.708355,0.983031,0.41342,0.652358,0.932965,0.708355,\n\t0.932965,0.983031,0.292631,0.580336,0.880271,0.580336,0.833138,0.880271,\n\t0.156457,0.41342,0.708355,0.983031,0.41342,0.652358,0.932965,0.708355,\n\t0.932965,0.983031,0.292631,0.580336,0.880271,0.580336,0.833138,0.880271,\n\t2.15856,2.43324,2.72817,2.98514,2.20863,2.48923,2.72817,2.20863,\n\t2.43324,2.15856,2.26132,2.56126,2.84896,2.30845,2.56126,2.26132,\n\t2.15856,2.43324,2.72817,2.98514,2.20863,2.48923,2.72817,2.20863,\n\t2.43324,2.15856,2.26132,2.56126,2.84896,2.30845,2.56126,2.26132,\n\t2.15856,2.43324,2.72817,2.98514,2.20863,2.48923,2.72817,2.20863,\n\t2.43324,2.15856,2.26132,2.56126,2.84896,2.30845,2.56126,2.26132,\n\t2.15856,2.43324,2.72817,2.98514,2.20863,2.48923,2.72817,2.20863,\n\t2.43324,2.15856,2.26132,2.56126,2.84896,2.30845,2.56126,2.26132,\n\t2.15856,2.43324,2.72817,2.98514,2.20863,2.48923,2.72817,2.20863,\n\t2.43324,2.15856,2.26132,2.56126,2.84896,2.30845,2.56126,2.26132,\n\t1.16072,1.11535,1.11535,1.16072,1.39535,1.38209,1.39535,1.64926,\n\t1.64926,1.87799,1.21486,1.20131,1.21486,1.47441,1.47441,1.74181,\n\t1.16072,1.11535,1.11535,1.16072,1.39535,1.38209,1.39535,1.64926,\n\t1.64926,1.87799,1.21486,1.20131,1.21486,1.47441,1.47441,1.74181,\n\t1.16072,1.11535,1.11535,1.16072,1.39535,1.38209,1.39535,1.64926,\n\t1.64926,1.87799,1.21486,1.20131,1.21486,1.47441,1.47441,1.74181,\n\t1.16072,1.11535,1.11535,1.16072,1.39535,1.38209,1.39535,1.64926,\n\t1.64926,1.87799,1.21486,1.20131,1.21486,1.47441,1.47441,1.74181,\n\t1.16072,1.11535,1.11535,1.16072,1.39535,1.38209,1.39535,1.64926,\n\t1.64926,1.87799,1.21486,1.20131,1.21486,1.47441,1.47441,1.74181,\n\t1.98088,2.02625,2.02625,1.98088,1.74624,1.75951,1.74624,1.49233,\n\t1.49233,1.26361,1.92673,1.94029,1.92673,1.66718,1.66718,1.39978,\n\t1.98088,2.02625,2.02625,1.98088,1.74624,1.75951,1.74624,1.49233,\n\t1.49233,1.26361,1.92673,1.94029,1.92673,1.66718,1.66718,1.39978,\n\t1.98088,2.02625,2.02625,1.98088,1.74624,1.75951,1.74624,1.49233,\n\t1.49233,1.26361,1.92673,1.94029,1.92673,1.66718,1.66718,1.39978,\n\t1.98088,2.02625,2.02625,1.98088,1.74624,1.75951,1.74624,1.49233,\n\t1.49233,1.26361,1.92673,1.94029,1.92673,1.66718,1.66718,1.39978,\n\t1.98088,2.02625,2.02625,1.98088,1.74624,1.75951,1.74624,1.49233,\n\t1.49233,1.26361,1.92673,1.94029,1.92673,1.66718,1.66718,1.39978};\nconst double AREAL[NSTL] = {\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187,\n\t0.0308214,0.0401741,0.0401741,0.0308214,0.0401741,0.0467453,0.0401741,0.0401741,\n\t0.0401741,0.0308214,0.0383187,0.0443694,0.0383187,0.0443694,0.0443694,0.0383187};\n\n\n// High resolution\nconst int NSTH = 1280;\n\nconst double THETAH[NSTH] = {\n\t1.88496, 2.28396, 2.38043, 2.42078, 2.44115, 2.45231, 2.45889, \n\t2.46321, 1.48595, 1.88496, 2.07971, 2.1854, 2.24917, 2.29095, \n\t2.32028, 1.38948, 1.6902, 1.88496, 2.01114, 2.09628, 2.15657, \n\t1.34913, 1.58451, 1.75877, 1.88496, 1.97742, 1.32876, 1.52074, \n\t1.67363, 1.79249, 1.3176, 1.47896, 1.61334, 1.31102, 1.44963, 1.3067, \n\t1.88496, 2.18058, 2.28751, 2.34341, 2.37904, 2.40431, 2.42315, \n\t1.58933, 1.88496, 2.04831, 2.14849, 2.21592, 2.26429, 1.48241, \n\t1.7216, 1.88496, 1.99911, 2.08192, 1.4265, 1.62142, 1.77081, 1.88496, \n\t1.39087, 1.55399, 1.68799, 1.36561, 1.50562, 1.34676, 3.14159, \n\t-2.74259, -2.64612, -2.60577, -2.5854, -2.57424, -2.56766, -2.56334, \n\t2.74259, 3.14159, -2.94683, -2.84115, -2.77738, -2.7356, -2.70627, \n\t2.64612, 2.94683, 3.14159, -3.01541, -2.93027, -2.86997, 2.60577, \n\t2.84115, 3.01541, 3.14159, -3.04913, 2.5854, 2.77738, 2.93027, \n\t3.04913, 2.57424, 2.7356, 2.86997, 2.56766, 2.70627, 2.56334, \n\t3.14159, -2.84597, -2.73904, -2.68313, -2.64751, -2.62224, -2.6034, \n\t2.84597, -3.14159, -2.97824, -2.87806, -2.81063, -2.76226, 2.73904, \n\t2.97824, -3.14159, -3.02744, -2.94463, 2.68313, 2.87806, 3.02744, \n\t3.14159, 2.64751, 2.81063, 2.94463, 2.62224, 2.76226, 2.6034, \n\t-1.88496, -1.48595, -1.38948, -1.34913, -1.32876, -1.3176, -1.31102, \n\t-1.3067, -2.28396, -1.88496, -1.6902, -1.58451, -1.52074, -1.47896, \n\t-1.44963, -2.38043, -2.07971, -1.88496, -1.75877, -1.67363, -1.61334, \n\t-2.42078, -2.1854, -2.01114, -1.88496, -1.79249, -2.44115, -2.24917, \n\t-2.09628, -1.97742, -2.45231, -2.29095, -2.15657, -2.45889, -2.32028, \n\t-2.46321, -1.88496, -1.58933, -1.48241, -1.4265, -1.39087, -1.36561, \n\t-1.34676, -2.18058, -1.88496, -1.7216, -1.62142, -1.55399, -1.50562, \n\t-2.28751, -2.04831, -1.88496, -1.77081, -1.68799, -2.34341, -2.14849, \n\t-1.99911, -1.88496, -2.37904, -2.21592, -2.08192, -2.40431, -2.26429, \n\t-2.42315, -0.628319, -0.229317, -0.132846, -0.0924929, -0.0721276, \n\t-0.0609656, -0.0543856, -0.050062, -1.02732, -0.628319, -0.43356, \n\t-0.327873, -0.264104, -0.222327, -0.192993, -1.12379, -0.823077, \n\t-0.628319, -0.502135, -0.416997, -0.3567, -1.16414, -0.928764, \n\t-0.754502, -0.628319, -0.535853, -1.18451, -0.992533, -0.83964, \n\t-0.720784, -1.19567, -1.03431, -0.899938, -1.20225, -1.06364, \n\t-1.20658, -0.628319, -0.332697, -0.225769, -0.169861, -0.134232, \n\t-0.108969, -0.0901211, -0.92394, -0.628319, -0.464966, -0.364783, \n\t-0.297352, -0.248987, -1.03087, -0.791671, -0.628319, -0.514169, \n\t-0.431354, -1.08678, -0.891854, -0.742468, -0.628319, -1.1224, \n\t-0.959285, -0.825283, -1.14767, -1.00765, -1.16652, 0.628319, \n\t1.02732, 1.12379, 1.16414, 1.18451, 1.19567, 1.20225, 1.20658, \n\t0.229317, 0.628319, 0.823077, 0.928764, 0.992533, 1.03431, 1.06364, \n\t0.132846, 0.43356, 0.628319, 0.754502, 0.83964, 0.899938, 0.0924929, \n\t0.327873, 0.502135, 0.628319, 0.720784, 0.0721276, 0.264104, \n\t0.416997, 0.535853, 0.0609656, 0.222327, 0.3567, 0.0543856, 0.192993, \n\t0.050062, 0.628319, 0.92394, 1.03087, 1.08678, 1.1224, 1.14767, \n\t1.16652, 0.332697, 0.628319, 0.791671, 0.891854, 0.959285, 1.00765, \n\t0.225769, 0.464966, 0.628319, 0.742468, 0.825283, 0.169861, 0.364783, \n\t0.514169, 0.628319, 0.134232, 0.297352, 0.431354, 0.108969, 0.248987, \n\t0.0901211, -0.678381, -0.682704, -0.689284, -0.700446, -0.720811, \n\t-0.761164, -0.857635, -1.25664, -0.821311, -0.850646, -0.892423, \n\t-0.956191, -1.06188, -1.25664, -1.65564, -0.985018, -1.04532, \n\t-1.13045, -1.25664, -1.4514, -1.75211, -1.16417, -1.25664, -1.38282, \n\t-1.55708, -1.79246, -1.3491, -1.46796, -1.62085, -1.81283, -1.52826, \n\t-1.66263, -1.82399, -1.69196, -1.83057, -1.83489, -0.71844, \n\t-0.737287, -0.762551, -0.798179, -0.854087, -0.961015, -1.25664, \n\t-0.877306, -0.925671, -0.993101, -1.09328, -1.25664, -1.55226, \n\t-1.05967, -1.14249, -1.25664, -1.41999, -1.65919, -1.25664, -1.37079, \n\t-1.52017, -1.71509, -1.4536, -1.5876, -1.75072, -1.63597, -1.77599, \n\t-1.79483, 0.578257, 0.573933, 0.567353, 0.556191, 0.535826, 0.495473, \n\t0.399002, 0., 0.435326, 0.405991, 0.364214, 0.300446, 0.194759, 0., \n\t-0.399002, 0.271619, 0.211321, 0.126184, 0., -0.194759, -0.495473, \n\t0.0924652, 0., -0.126184, -0.300446, -0.535826, -0.0924652, \n\t-0.211321, -0.364214, -0.556191, -0.271619, -0.405991, -0.567353, \n\t-0.435326, -0.573933, -0.578257, 0.538197, 0.51935, 0.494086, \n\t0.458458, 0.40255, 0.295622, 0., 0.379331, 0.330967, 0.263536, \n\t0.163353, 0., -0.295622, 0.196964, 0.11415, 0., -0.163353, -0.40255, \n\t0., -0.11415, -0.263536, -0.458458, -0.196964, -0.330967, -0.494086, \n\t-0.379331, -0.51935, -0.538197, 1.83489, 1.83057, 1.82399, 1.81283, \n\t1.79246, 1.75211, 1.65564, 1.25664, 1.69196, 1.66263, 1.62085, \n\t1.55708, 1.4514, 1.25664, 0.857635, 1.52826, 1.46796, 1.38282, \n\t1.25664, 1.06188, 0.761164, 1.3491, 1.25664, 1.13045, 0.956191, \n\t0.720811, 1.16417, 1.04532, 0.892423, 0.700446, 0.985018, 0.850646, \n\t0.689284, 0.821311, 0.682704, 0.678381, 1.79483, 1.77599, 1.75072, \n\t1.71509, 1.65919, 1.55226, 1.25664, 1.63597, 1.5876, 1.52017, \n\t1.41999, 1.25664, 0.961015, 1.4536, 1.37079, 1.25664, 1.09328, \n\t0.854087, 1.25664, 1.14249, 0.993101, 0.798179, 1.05967, 0.925671, \n\t0.762551, 0.877306, 0.737287, 0.71844, 3.09153, 3.08721, 3.08063, \n\t3.06947, 3.0491, 3.00875, 2.91228, 2.51327, 2.9486, 2.91927, 2.87749, \n\t2.81372, 2.70803, 2.51327, 2.11427, 2.78489, 2.7246, 2.63946, \n\t2.51327, 2.31852, 2.0178, 2.60574, 2.51327, 2.38709, 2.21283, \n\t1.97745, 2.42081, 2.30195, 2.14906, 1.95708, 2.24166, 2.10728, \n\t1.94592, 2.07795, 1.93934, 1.93502, 3.05147, 3.03262, 3.00736, \n\t2.97173, 2.91582, 2.8089, 2.51327, 2.89261, 2.84424, 2.77681, \n\t2.67663, 2.51327, 2.21765, 2.71024, 2.62742, 2.51327, 2.34992, \n\t2.11072, 2.51327, 2.39912, 2.24974, 2.05482, 2.31631, 2.18231, \n\t2.01919, 2.13394, 1.99392, 1.97508, -1.93502, -1.93934, -1.94592, \n\t-1.95708, -1.97745, -2.0178, -2.11427, -2.51327, -2.07795, -2.10728, \n\t-2.14906, -2.21283, -2.31852, -2.51327, -2.91228, -2.24166, -2.30195, \n\t-2.38709, -2.51327, -2.70803, -3.00875, -2.42081, -2.51327, -2.63946, \n\t-2.81372, -3.0491, -2.60574, -2.7246, -2.87749, -3.06947, -2.78489, \n\t-2.91927, -3.08063, -2.9486, -3.08721, -3.09153, -1.97508, -1.99392, \n\t-2.01919, -2.05482, -2.11072, -2.21765, -2.51327, -2.13394, -2.18231, \n\t-2.24974, -2.34992, -2.51327, -2.8089, -2.31631, -2.39912, -2.51327, \n\t-2.67663, -2.91582, -2.51327, -2.62742, -2.77681, -2.97173, -2.71024, \n\t-2.84424, -3.00736, -2.89261, -3.03262, -3.05147, 1.33424, 1.47117, \n\t1.62709, 1.79722, 1.97269, 2.14282, 2.29875, 2.43567, 1.40966, \n\t1.55431, 1.71493, 1.88496, 2.05498, 2.2156, 2.36025, 1.48731, \n\t1.63808, 1.80115, 1.96876, 2.13183, 2.2826, 1.56675, 1.72176, \n\t1.88496, 2.04815, 2.20316, 1.64735, 1.80443, 1.96548, 2.12256, \n\t1.72812, 1.88496, 2.04179, 1.80777, 1.96214, 1.88496, 1.39159, \n\t1.54136, 1.70817, 1.88496, 2.06174, 2.22856, 2.37832, 1.47675, \n\t1.63136, 1.79884, 1.97107, 2.13855, 2.29316, 1.56255, 1.71957, \n\t1.88496, 2.05034, 2.20736, 1.64735, 1.80443, 1.96548, 2.12256, \n\t1.72982, 1.88496, 2.04009, 1.80915, 1.96076, 1.88496, 2.59087, \n\t2.7278, 2.88373, 3.05386, -3.05386, -2.88373, -2.7278, -2.59087, \n\t2.6663, 2.81094, 2.97157, 3.14159, -2.97157, -2.81094, -2.6663, \n\t2.74395, 2.89472, 3.05778, -3.05778, -2.89472, -2.74395, 2.82339, \n\t2.9784, 3.14159, -2.9784, -2.82339, 2.90399, 3.06107, -3.06107, \n\t-2.90399, 2.98476, 3.14159, -2.98476, 3.0644, -3.0644, 3.14159, \n\t2.64823, 2.79799, 2.96481, 3.14159, -2.96481, -2.79799, -2.64823, \n\t2.73338, 2.88799, 3.05548, -3.05548, -2.88799, -2.73338, 2.81919, \n\t2.97621, 3.14159, -2.97621, -2.81919, 2.90399, 3.06107, -3.06107, \n\t-2.90399, 2.98645, 3.14159, -2.98645, 3.06579, -3.06579, 3.14159, \n\t-2.43567, -2.29875, -2.14282, -1.97269, -1.79722, -1.62709, -1.47117, \n\t-1.33424, -2.36025, -2.2156, -2.05498, -1.88496, -1.71493, -1.55431, \n\t-1.40966, -2.2826, -2.13183, -1.96876, -1.80115, -1.63808, -1.48731, \n\t-2.20316, -2.04815, -1.88496, -1.72176, -1.56675, -2.12256, -1.96548, \n\t-1.80443, -1.64735, -2.04179, -1.88496, -1.72812, -1.96214, -1.80777, \n\t-1.88496, -2.37832, -2.22856, -2.06174, -1.88496, -1.70817, -1.54136, \n\t-1.39159, -2.29316, -2.13855, -1.97107, -1.79884, -1.63136, -1.47675, \n\t-2.20736, -2.05034, -1.88496, -1.71957, -1.56255, -2.12256, -1.96548, \n\t-1.80443, -1.64735, -2.04009, -1.88496, -1.72982, -1.96076, -1.80915, \n\t-1.88496, -1.17904, -1.04211, -0.886184, -0.716053, -0.540584, \n\t-0.370453, -0.214529, -0.0775996, -1.10361, -0.958967, -0.798343, \n\t-0.628319, -0.458294, -0.29767, -0.153027, -1.02596, -0.875192, \n\t-0.712127, -0.54451, -0.381445, -0.230673, -0.946525, -0.791516, \n\t-0.628319, -0.465121, -0.310112, -0.865925, -0.708845, -0.547792, \n\t-0.390713, -0.785153, -0.628319, -0.471484, -0.705507, -0.55113, \n\t-0.628319, -1.12168, -0.971918, -0.805104, -0.628319, -0.451533, \n\t-0.284719, -0.134953, -1.03653, -0.881917, -0.714436, -0.542201, \n\t-0.37472, -0.220109, -0.950723, -0.793703, -0.628319, -0.462934, \n\t-0.305914, -0.865925, -0.708845, -0.547792, -0.390713, -0.783457, \n\t-0.628319, -0.47318, -0.704125, -0.552512, -0.628319, 0.0775996, \n\t0.214529, 0.370453, 0.540584, 0.716053, 0.886184, 1.04211, 1.17904, \n\t0.153027, 0.29767, 0.458294, 0.628319, 0.798343, 0.958967, 1.10361, \n\t0.230673, 0.381445, 0.54451, 0.712127, 0.875192, 1.02596, 0.310112, \n\t0.465121, 0.628319, 0.791516, 0.946525, 0.390713, 0.547792, 0.708845, \n\t0.865925, 0.471484, 0.628319, 0.785153, 0.55113, 0.705507, 0.628319, \n\t0.134953, 0.284719, 0.451533, 0.628319, 0.805104, 0.971918, 1.12168, \n\t0.220109, 0.37472, 0.542201, 0.714436, 0.881917, 1.03653, 0.305914, \n\t0.462934, 0.628319, 0.793703, 0.950723, 0.390713, 0.547792, 0.708845, \n\t0.865925, 0.47318, 0.628319, 0.783457, 0.552512, 0.704125, 0.628319, \n\t-0.705918, -0.842848, -0.998772, -1.1689, -1.34437, -1.5145, \n\t-1.67043, -1.80736, -0.781346, -0.925988, -1.08661, -1.25664, \n\t-1.42666, -1.58729, -1.73193, -0.858992, -1.00976, -1.17283, \n\t-1.34045, -1.50351, -1.65428, -0.93843, -1.09344, -1.25664, -1.41983, \n\t-1.57484, -1.01903, -1.17611, -1.33716, -1.49424, -1.0998, -1.25664, \n\t-1.41347, -1.17945, -1.33383, -1.25664, -0.763271, -0.913037, \n\t-1.07985, -1.25664, -1.43342, -1.60024, -1.75, -0.848428, -1.00304, \n\t-1.17052, -1.34275, -1.51024, -1.66485, -0.934232, -1.09125, \n\t-1.25664, -1.42202, -1.57904, -1.01903, -1.17611, -1.33716, -1.49424, \n\t-1.1015, -1.25664, -1.41178, -1.18083, -1.33244, -1.25664, 0.550719, \n\t0.413789, 0.257865, 0.0877348, -0.0877348, -0.257865, -0.413789, \n\t-0.550719, 0.475291, 0.330649, 0.170024, 0., -0.170024, -0.330649, \n\t-0.475291, 0.397645, 0.246873, 0.0838083, -0.0838083, -0.246873, \n\t-0.397645, 0.318207, 0.163197, 0., -0.163197, -0.318207, 0.237606, \n\t0.0805264, -0.0805264, -0.237606, 0.156834, 0., -0.156834, 0.0771885, \n\t-0.0771885, 0., 0.493366, 0.3436, 0.176786, 0., -0.176786, -0.3436, \n\t-0.493366, 0.408209, 0.253599, 0.0861171, -0.0861171, -0.253599, \n\t-0.408209, 0.322405, 0.165384, 0., -0.165384, -0.322405, 0.237606, \n\t0.0805264, -0.0805264, -0.237606, 0.155138, 0., -0.155138, 0.0758069, \n\t-0.0758069, 0., 1.80736, 1.67043, 1.5145, 1.34437, 1.1689, 0.998772, \n\t0.842848, 0.705918, 1.73193, 1.58729, 1.42666, 1.25664, 1.08661, \n\t0.925988, 0.781346, 1.65428, 1.50351, 1.34045, 1.17283, 1.00976, \n\t0.858992, 1.57484, 1.41983, 1.25664, 1.09344, 0.93843, 1.49424, \n\t1.33716, 1.17611, 1.01903, 1.41347, 1.25664, 1.0998, 1.33383, \n\t1.17945, 1.25664, 1.75, 1.60024, 1.43342, 1.25664, 1.07985, 0.913037, \n\t0.763271, 1.66485, 1.51024, 1.34275, 1.17052, 1.00304, 0.848428, \n\t1.57904, 1.42202, 1.25664, 1.09125, 0.934232, 1.49424, 1.33716, \n\t1.17611, 1.01903, 1.41178, 1.25664, 1.1015, 1.33244, 1.18083, \n\t1.25664, 3.06399, 2.92706, 2.77114, 2.60101, 2.42554, 2.25541, \n\t2.09948, 1.96256, 2.98857, 2.84392, 2.6833, 2.51327, 2.34325, \n\t2.18263, 2.03798, 2.91092, 2.76015, 2.59708, 2.42947, 2.2664, \n\t2.11563, 2.83148, 2.67647, 2.51327, 2.35008, 2.19507, 2.75088, \n\t2.5938, 2.43275, 2.27567, 2.67011, 2.51327, 2.35644, 2.59046, \n\t2.43609, 2.51327, 3.00664, 2.85687, 2.69006, 2.51327, 2.33649, \n\t2.16967, 2.01991, 2.92148, 2.76687, 2.59939, 2.42716, 2.25968, \n\t2.10506, 2.83568, 2.67866, 2.51327, 2.34789, 2.19087, 2.75088, \n\t2.5938, 2.43275, 2.27567, 2.66841, 2.51327, 2.35814, 2.58908, \n\t2.43747, 2.51327, -1.96256, -2.09948, -2.25541, -2.42554, -2.60101, \n\t-2.77114, -2.92706, -3.06399, -2.03798, -2.18263, -2.34325, -2.51327, \n\t-2.6833, -2.84392, -2.98857, -2.11563, -2.2664, -2.42947, -2.59708, \n\t-2.76015, -2.91092, -2.19507, -2.35008, -2.51327, -2.67647, -2.83148, \n\t-2.27567, -2.43275, -2.5938, -2.75088, -2.35644, -2.51327, -2.67011, \n\t-2.43609, -2.59046, -2.51327, -2.01991, -2.16967, -2.33649, -2.51327, \n\t-2.69006, -2.85687, -3.00664, -2.10506, -2.25968, -2.42716, -2.59939, \n\t-2.76687, -2.92148, -2.19087, -2.34789, -2.51327, -2.67866, -2.83568, \n\t-2.27567, -2.43275, -2.5938, -2.75088, -2.35814, -2.51327, -2.66841, \n\t-2.43747, -2.58908, -2.51327};\n\nconst double PHIH[NSTH] = {\n\t0.0738271, 0.191596, 0.329774, 0.478675, 0.631463, 0.781007, \n\t0.921164, 1.04793, 0.191596, 0.293272, 0.428239, 0.578348, 0.731922, \n\t0.879802, 1.0156, 0.329774, 0.428239, 0.558218, 0.703468, 0.850791, \n\t0.990445, 0.478675, 0.578348, 0.703468, 0.840265, 0.97653, 0.631463, \n\t0.731922, 0.850791, 0.97653, 0.781007, 0.879802, 0.990445, 0.921164, \n\t1.0156, 1.04793, 0.129534, 0.256665, 0.403765, 0.560078, 0.717061, \n\t0.866931, 1.00399, 0.256665, 0.373855, 0.516767, 0.670013, 0.82223, \n\t0.965013, 0.403765, 0.516767, 0.652358, 0.796338, 0.937381, 0.560078, \n\t0.670013, 0.796338, 0.927295, 0.717061, 0.82223, 0.937381, 0.866931, \n\t0.965013, 1.00399, 0.0738271, 0.191596, 0.329774, 0.478675, 0.631463, \n\t0.781007, 0.921164, 1.04793, 0.191596, 0.293272, 0.428239, 0.578348, \n\t0.731922, 0.879802, 1.0156, 0.329774, 0.428239, 0.558218, 0.703468, \n\t0.850791, 0.990445, 0.478675, 0.578348, 0.703468, 0.840265, 0.97653, \n\t0.631463, 0.731922, 0.850791, 0.97653, 0.781007, 0.879802, 0.990445, \n\t0.921164, 1.0156, 1.04793, 0.129534, 0.256665, 0.403765, 0.560078, \n\t0.717061, 0.866931, 1.00399, 0.256665, 0.373855, 0.516767, 0.670013, \n\t0.82223, 0.965013, 0.403765, 0.516767, 0.652358, 0.796338, 0.937381, \n\t0.560078, 0.670013, 0.796338, 0.927295, 0.717061, 0.82223, 0.937381, \n\t0.866931, 0.965013, 1.00399, 0.0738271, 0.191596, 0.329774, 0.478675, \n\t0.631463, 0.781007, 0.921164, 1.04793, 0.191596, 0.293272, 0.428239, \n\t0.578348, 0.731922, 0.879802, 1.0156, 0.329774, 0.428239, 0.558218, \n\t0.703468, 0.850791, 0.990445, 0.478675, 0.578348, 0.703468, 0.840265, \n\t0.97653, 0.631463, 0.731922, 0.850791, 0.97653, 0.781007, 0.879802, \n\t0.990445, 0.921164, 1.0156, 1.04793, 0.129534, 0.256665, 0.403765, \n\t0.560078, 0.717061, 0.866931, 1.00399, 0.256665, 0.373855, 0.516767, \n\t0.670013, 0.82223, 0.965013, 0.403765, 0.516767, 0.652358, 0.796338, \n\t0.937381, 0.560078, 0.670013, 0.796338, 0.927295, 0.717061, 0.82223, \n\t0.937381, 0.866931, 0.965013, 1.00399, 0.0738271, 0.191596, 0.329774, \n\t0.478675, 0.631463, 0.781007, 0.921164, 1.04793, 0.191596, 0.293272, \n\t0.428239, 0.578348, 0.731922, 0.879802, 1.0156, 0.329774, 0.428239, \n\t0.558218, 0.703468, 0.850791, 0.990445, 0.478675, 0.578348, 0.703468, \n\t0.840265, 0.97653, 0.631463, 0.731922, 0.850791, 0.97653, 0.781007, \n\t0.879802, 0.990445, 0.921164, 1.0156, 1.04793, 0.129534, 0.256665, \n\t0.403765, 0.560078, 0.717061, 0.866931, 1.00399, 0.256665, 0.373855, \n\t0.516767, 0.670013, 0.82223, 0.965013, 0.403765, 0.516767, 0.652358, \n\t0.796338, 0.937381, 0.560078, 0.670013, 0.796338, 0.927295, 0.717061, \n\t0.82223, 0.937381, 0.866931, 0.965013, 1.00399, 0.0738271, 0.191596, \n\t0.329774, 0.478675, 0.631463, 0.781007, 0.921164, 1.04793, 0.191596, \n\t0.293272, 0.428239, 0.578348, 0.731922, 0.879802, 1.0156, 0.329774, \n\t0.428239, 0.558218, 0.703468, 0.850791, 0.990445, 0.478675, 0.578348, \n\t0.703468, 0.840265, 0.97653, 0.631463, 0.731922, 0.850791, 0.97653, \n\t0.781007, 0.879802, 0.990445, 0.921164, 1.0156, 1.04793, 0.129534, \n\t0.256665, 0.403765, 0.560078, 0.717061, 0.866931, 1.00399, 0.256665, \n\t0.373855, 0.516767, 0.670013, 0.82223, 0.965013, 0.403765, 0.516767, \n\t0.652358, 0.796338, 0.937381, 0.560078, 0.670013, 0.796338, 0.927295, \n\t0.717061, 0.82223, 0.937381, 0.866931, 0.965013, 1.00399, 2.09367, \n\t2.22043, 2.36059, 2.51013, 2.66292, 2.81182, 2.95, 3.06777, 2.12599, \n\t2.26179, 2.40967, 2.56324, 2.71335, 2.84832, 2.95, 2.15115, 2.2908, \n\t2.43812, 2.58338, 2.71335, 2.81182, 2.16506, 2.30133, 2.43812, \n\t2.56324, 2.66292, 2.16506, 2.2908, 2.40967, 2.51013, 2.15115, \n\t2.26179, 2.36059, 2.12599, 2.22043, 2.09367, 2.1376, 2.27466, \n\t2.42453, 2.58151, 2.73783, 2.88493, 3.01206, 2.17658, 2.31936, \n\t2.47158, 2.62483, 2.76774, 2.88493, 2.20421, 2.34525, 2.48923, \n\t2.62483, 2.73783, 2.2143, 2.34525, 2.47158, 2.58151, 2.20421, \n\t2.31936, 2.42453, 2.17658, 2.27466, 2.1376, 2.09367, 2.22043, \n\t2.36059, 2.51013, 2.66292, 2.81182, 2.95, 3.06777, 2.12599, 2.26179, \n\t2.40967, 2.56324, 2.71335, 2.84832, 2.95, 2.15115, 2.2908, 2.43812, \n\t2.58338, 2.71335, 2.81182, 2.16506, 2.30133, 2.43812, 2.56324, \n\t2.66292, 2.16506, 2.2908, 2.40967, 2.51013, 2.15115, 2.26179, \n\t2.36059, 2.12599, 2.22043, 2.09367, 2.1376, 2.27466, 2.42453, \n\t2.58151, 2.73783, 2.88493, 3.01206, 2.17658, 2.31936, 2.47158, \n\t2.62483, 2.76774, 2.88493, 2.20421, 2.34525, 2.48923, 2.62483, \n\t2.73783, 2.2143, 2.34525, 2.47158, 2.58151, 2.20421, 2.31936, \n\t2.42453, 2.17658, 2.27466, 2.1376, 2.09367, 2.22043, 2.36059, \n\t2.51013, 2.66292, 2.81182, 2.95, 3.06777, 2.12599, 2.26179, 2.40967, \n\t2.56324, 2.71335, 2.84832, 2.95, 2.15115, 2.2908, 2.43812, 2.58338, \n\t2.71335, 2.81182, 2.16506, 2.30133, 2.43812, 2.56324, 2.66292, \n\t2.16506, 2.2908, 2.40967, 2.51013, 2.15115, 2.26179, 2.36059, \n\t2.12599, 2.22043, 2.09367, 2.1376, 2.27466, 2.42453, 2.58151, \n\t2.73783, 2.88493, 3.01206, 2.17658, 2.31936, 2.47158, 2.62483, \n\t2.76774, 2.88493, 2.20421, 2.34525, 2.48923, 2.62483, 2.73783, \n\t2.2143, 2.34525, 2.47158, 2.58151, 2.20421, 2.31936, 2.42453, \n\t2.17658, 2.27466, 2.1376, 2.09367, 2.22043, 2.36059, 2.51013, \n\t2.66292, 2.81182, 2.95, 3.06777, 2.12599, 2.26179, 2.40967, 2.56324, \n\t2.71335, 2.84832, 2.95, 2.15115, 2.2908, 2.43812, 2.58338, 2.71335, \n\t2.81182, 2.16506, 2.30133, 2.43812, 2.56324, 2.66292, 2.16506, \n\t2.2908, 2.40967, 2.51013, 2.15115, 2.26179, 2.36059, 2.12599, \n\t2.22043, 2.09367, 2.1376, 2.27466, 2.42453, 2.58151, 2.73783, \n\t2.88493, 3.01206, 2.17658, 2.31936, 2.47158, 2.62483, 2.76774, \n\t2.88493, 2.20421, 2.34525, 2.48923, 2.62483, 2.73783, 2.2143, \n\t2.34525, 2.47158, 2.58151, 2.20421, 2.31936, 2.42453, 2.17658, \n\t2.27466, 2.1376, 2.09367, 2.22043, 2.36059, 2.51013, 2.66292, \n\t2.81182, 2.95, 3.06777, 2.12599, 2.26179, 2.40967, 2.56324, 2.71335, \n\t2.84832, 2.95, 2.15115, 2.2908, 2.43812, 2.58338, 2.71335, 2.81182, \n\t2.16506, 2.30133, 2.43812, 2.56324, 2.66292, 2.16506, 2.2908, \n\t2.40967, 2.51013, 2.15115, 2.26179, 2.36059, 2.12599, 2.22043, \n\t2.09367, 2.1376, 2.27466, 2.42453, 2.58151, 2.73783, 2.88493, \n\t3.01206, 2.17658, 2.31936, 2.47158, 2.62483, 2.76774, 2.88493, \n\t2.20421, 2.34525, 2.48923, 2.62483, 2.73783, 2.2143, 2.34525, \n\t2.47158, 2.58151, 2.20421, 2.31936, 2.42453, 2.17658, 2.27466, \n\t2.1376, 1.13116, 1.10015, 1.07544, 1.06154, 1.06154, 1.07544, \n\t1.10015, 1.13116, 1.23703, 1.21513, 1.19975, 1.19418, 1.19975, \n\t1.21513, 1.23703, 1.35574, 1.34297, 1.33571, 1.33571, 1.34297, \n\t1.35574, 1.48324, 1.47812, 1.47623, 1.47812, 1.48324, 1.61352, \n\t1.61323, 1.61323, 1.61352, 1.73991, 1.74117, 1.73991, 1.85677, \n\t1.85677, 1.96062, 1.15079, 1.12812, 1.11267, 1.10715, 1.11267, \n\t1.12812, 1.15079, 1.26345, 1.24994, 1.2425, 1.2425, 1.24994, 1.26345, \n\t1.38939, 1.38398, 1.38209, 1.38398, 1.38939, 1.52357, 1.52357, \n\t1.52357, 1.52357, 1.65885, 1.66059, 1.65885, 1.78785, 1.78785, \n\t1.90491, 1.13116, 1.10015, 1.07544, 1.06154, 1.06154, 1.07544, \n\t1.10015, 1.13116, 1.23703, 1.21513, 1.19975, 1.19418, 1.19975, \n\t1.21513, 1.23703, 1.35574, 1.34297, 1.33571, 1.33571, 1.34297, \n\t1.35574, 1.48324, 1.47812, 1.47623, 1.47812, 1.48324, 1.61352, \n\t1.61323, 1.61323, 1.61352, 1.73991, 1.74117, 1.73991, 1.85677, \n\t1.85677, 1.96062, 1.15079, 1.12812, 1.11267, 1.10715, 1.11267, \n\t1.12812, 1.15079, 1.26345, 1.24994, 1.2425, 1.2425, 1.24994, 1.26345, \n\t1.38939, 1.38398, 1.38209, 1.38398, 1.38939, 1.52357, 1.52357, \n\t1.52357, 1.52357, 1.65885, 1.66059, 1.65885, 1.78785, 1.78785, \n\t1.90491, 1.13116, 1.10015, 1.07544, 1.06154, 1.06154, 1.07544, \n\t1.10015, 1.13116, 1.23703, 1.21513, 1.19975, 1.19418, 1.19975, \n\t1.21513, 1.23703, 1.35574, 1.34297, 1.33571, 1.33571, 1.34297, \n\t1.35574, 1.48324, 1.47812, 1.47623, 1.47812, 1.48324, 1.61352, \n\t1.61323, 1.61323, 1.61352, 1.73991, 1.74117, 1.73991, 1.85677, \n\t1.85677, 1.96062, 1.15079, 1.12812, 1.11267, 1.10715, 1.11267, \n\t1.12812, 1.15079, 1.26345, 1.24994, 1.2425, 1.2425, 1.24994, 1.26345, \n\t1.38939, 1.38398, 1.38209, 1.38398, 1.38939, 1.52357, 1.52357, \n\t1.52357, 1.52357, 1.65885, 1.66059, 1.65885, 1.78785, 1.78785, \n\t1.90491, 1.13116, 1.10015, 1.07544, 1.06154, 1.06154, 1.07544, \n\t1.10015, 1.13116, 1.23703, 1.21513, 1.19975, 1.19418, 1.19975, \n\t1.21513, 1.23703, 1.35574, 1.34297, 1.33571, 1.33571, 1.34297, \n\t1.35574, 1.48324, 1.47812, 1.47623, 1.47812, 1.48324, 1.61352, \n\t1.61323, 1.61323, 1.61352, 1.73991, 1.74117, 1.73991, 1.85677, \n\t1.85677, 1.96062, 1.15079, 1.12812, 1.11267, 1.10715, 1.11267, \n\t1.12812, 1.15079, 1.26345, 1.24994, 1.2425, 1.2425, 1.24994, 1.26345, \n\t1.38939, 1.38398, 1.38209, 1.38398, 1.38939, 1.52357, 1.52357, \n\t1.52357, 1.52357, 1.65885, 1.66059, 1.65885, 1.78785, 1.78785, \n\t1.90491, 1.13116, 1.10015, 1.07544, 1.06154, 1.06154, 1.07544, \n\t1.10015, 1.13116, 1.23703, 1.21513, 1.19975, 1.19418, 1.19975, \n\t1.21513, 1.23703, 1.35574, 1.34297, 1.33571, 1.33571, 1.34297, \n\t1.35574, 1.48324, 1.47812, 1.47623, 1.47812, 1.48324, 1.61352, \n\t1.61323, 1.61323, 1.61352, 1.73991, 1.74117, 1.73991, 1.85677, \n\t1.85677, 1.96062, 1.15079, 1.12812, 1.11267, 1.10715, 1.11267, \n\t1.12812, 1.15079, 1.26345, 1.24994, 1.2425, 1.2425, 1.24994, 1.26345, \n\t1.38939, 1.38398, 1.38209, 1.38398, 1.38939, 1.52357, 1.52357, \n\t1.52357, 1.52357, 1.65885, 1.66059, 1.65885, 1.78785, 1.78785, \n\t1.90491, 2.01043, 2.04144, 2.06615, 2.08005, 2.08005, 2.06615, \n\t2.04144, 2.01043, 1.90456, 1.92647, 1.94184, 1.94741, 1.94184, \n\t1.92647, 1.90456, 1.78586, 1.79862, 1.80588, 1.80588, 1.79862, \n\t1.78586, 1.65835, 1.66348, 1.66537, 1.66348, 1.65835, 1.52807, \n\t1.52836, 1.52836, 1.52807, 1.40169, 1.40042, 1.40169, 1.28483, \n\t1.28483, 1.18098, 1.9908, 2.01347, 2.02893, 2.03444, 2.02893, \n\t2.01347, 1.9908, 1.87814, 1.89166, 1.89909, 1.89909, 1.89166, \n\t1.87814, 1.7522, 1.75761, 1.75951, 1.75761, 1.7522, 1.61803, 1.61803, \n\t1.61803, 1.61803, 1.48275, 1.481, 1.48275, 1.35374, 1.35374, 1.23668, \n\t2.01043, 2.04144, 2.06615, 2.08005, 2.08005, 2.06615, 2.04144, \n\t2.01043, 1.90456, 1.92647, 1.94184, 1.94741, 1.94184, 1.92647, \n\t1.90456, 1.78586, 1.79862, 1.80588, 1.80588, 1.79862, 1.78586, \n\t1.65835, 1.66348, 1.66537, 1.66348, 1.65835, 1.52807, 1.52836, \n\t1.52836, 1.52807, 1.40169, 1.40042, 1.40169, 1.28483, 1.28483, \n\t1.18098, 1.9908, 2.01347, 2.02893, 2.03444, 2.02893, 2.01347, 1.9908, \n\t1.87814, 1.89166, 1.89909, 1.89909, 1.89166, 1.87814, 1.7522, \n\t1.75761, 1.75951, 1.75761, 1.7522, 1.61803, 1.61803, 1.61803, \n\t1.61803, 1.48275, 1.481, 1.48275, 1.35374, 1.35374, 1.23668, 2.01043, \n\t2.04144, 2.06615, 2.08005, 2.08005, 2.06615, 2.04144, 2.01043, \n\t1.90456, 1.92647, 1.94184, 1.94741, 1.94184, 1.92647, 1.90456, \n\t1.78586, 1.79862, 1.80588, 1.80588, 1.79862, 1.78586, 1.65835, \n\t1.66348, 1.66537, 1.66348, 1.65835, 1.52807, 1.52836, 1.52836, \n\t1.52807, 1.40169, 1.40042, 1.40169, 1.28483, 1.28483, 1.18098, \n\t1.9908, 2.01347, 2.02893, 2.03444, 2.02893, 2.01347, 1.9908, 1.87814, \n\t1.89166, 1.89909, 1.89909, 1.89166, 1.87814, 1.7522, 1.75761, \n\t1.75951, 1.75761, 1.7522, 1.61803, 1.61803, 1.61803, 1.61803, \n\t1.48275, 1.481, 1.48275, 1.35374, 1.35374, 1.23668, 2.01043, 2.04144, \n\t2.06615, 2.08005, 2.08005, 2.06615, 2.04144, 2.01043, 1.90456, \n\t1.92647, 1.94184, 1.94741, 1.94184, 1.92647, 1.90456, 1.78586, \n\t1.79862, 1.80588, 1.80588, 1.79862, 1.78586, 1.65835, 1.66348, \n\t1.66537, 1.66348, 1.65835, 1.52807, 1.52836, 1.52836, 1.52807, \n\t1.40169, 1.40042, 1.40169, 1.28483, 1.28483, 1.18098, 1.9908, \n\t2.01347, 2.02893, 2.03444, 2.02893, 2.01347, 1.9908, 1.87814, \n\t1.89166, 1.89909, 1.89909, 1.89166, 1.87814, 1.7522, 1.75761, \n\t1.75951, 1.75761, 1.7522, 1.61803, 1.61803, 1.61803, 1.61803, \n\t1.48275, 1.481, 1.48275, 1.35374, 1.35374, 1.23668, 2.01043, 2.04144, \n\t2.06615, 2.08005, 2.08005, 2.06615, 2.04144, 2.01043, 1.90456, \n\t1.92647, 1.94184, 1.94741, 1.94184, 1.92647, 1.90456, 1.78586, \n\t1.79862, 1.80588, 1.80588, 1.79862, 1.78586, 1.65835, 1.66348, \n\t1.66537, 1.66348, 1.65835, 1.52807, 1.52836, 1.52836, 1.52807, \n\t1.40169, 1.40042, 1.40169, 1.28483, 1.28483, 1.18098, 1.9908, \n\t2.01347, 2.02893, 2.03444, 2.02893, 2.01347, 1.9908, 1.87814, \n\t1.89166, 1.89909, 1.89909, 1.89166, 1.87814, 1.7522, 1.75761, \n\t1.75951, 1.75761, 1.7522, 1.61803, 1.61803, 1.61803, 1.61803, \n\t1.48275, 1.481, 1.48275, 1.35374, 1.35374, 1.23668};\n\nconst double AREAH[NSTH] = {\n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048, \n\t0.0068027, 0.00814412, 0.00930384, 0.00999082, 0.00999082, \n\t0.00930384, 0.00814412, 0.0068027, 0.00814412, 0.00963703, 0.0107661, \n\t0.0111921, 0.0107661, 0.00963703, 0.00814412, 0.00930384, 0.0107661, \n\t0.0116467, 0.0116467, 0.0107661, 0.00930384, 0.00999082, 0.0111921, \n\t0.0116467, 0.0111921, 0.00999082, 0.00999082, 0.0107661, 0.0107661, \n\t0.00999082, 0.00930384, 0.00963703, 0.00930384, 0.00814412, \n\t0.00814412, 0.0068027, 0.00773048, 0.00909235, 0.0101134, 0.0104968, \n\t0.0101134, 0.00909235, 0.00773048, 0.00909235, 0.0104969, 0.0113403, \n\t0.0113403, 0.0104969, 0.00909235, 0.0101134, 0.0113403, 0.0118051, \n\t0.0113403, 0.0101134, 0.0104968, 0.0113403, 0.0113403, 0.0104968, \n\t0.0101134, 0.0104969, 0.0101134, 0.00909235, 0.00909235, 0.00773048};\n\nconst double XYZ2[45][4] = {\n\t{         1,         0,         0,0.003394024},\n\t{         0,         1,         0,0.003394024},\n\t{         0,         0,         1,0.003394024},\n\t{ 0.7071068, 0.7071068,         0,0.02550091},\n\t{         0, 0.7071068, 0.7071068,0.02550091},\n\t{ 0.7071068,         0, 0.7071068,0.02550091},\n\t{ 0.9486833, 0.3162278,         0, 0.0180476},\n\t{ 0.3162278, 0.9486833,         0, 0.0180476},\n\t{         0, 0.9486833, 0.3162278, 0.0180476},\n\t{         0, 0.3162278, 0.9486833, 0.0180476},\n\t{ 0.9486833,         0, 0.3162278, 0.0180476},\n\t{ 0.3162278,         0, 0.9486833, 0.0180476},\n\t{ 0.4082483, 0.8164966, 0.4082483,0.06535968},\n\t{ 0.4082483, 0.4082483, 0.8164966,0.06535968},\n\t{ 0.8164966, 0.4082483, 0.4082483,0.06535968},\n\t{ 0.9899495, 0.1414214,         0,0.01273219},\n\t{ 0.8574929, 0.5144958,         0,0.02322682},\n\t{ 0.5144958, 0.8574929,         0,0.02322682},\n\t{ 0.1414214, 0.9899495,         0,0.01273219},\n\t{         0, 0.9899495, 0.1414214,0.01273219},\n\t{         0, 0.8574929, 0.5144958,0.02322682},\n\t{         0, 0.5144958, 0.8574929,0.02322682},\n\t{         0, 0.1414214, 0.9899495,0.01273219},\n\t{ 0.9899495,         0, 0.1414214,0.01273219},\n\t{ 0.8574929,         0, 0.5144958,0.02322682},\n\t{ 0.5144958,         0, 0.8574929,0.02322682},\n\t{ 0.1414214,         0, 0.9899495,0.01273219},\n\t{ 0.5883484, 0.7844645, 0.1961161,0.05866665},\n\t{ 0.1961161, 0.7844645, 0.5883484,0.05866665},\n\t{ 0.5883484, 0.1961161, 0.7844645,0.05866665},\n\t{ 0.1961161, 0.5883484, 0.7844645,0.05866665},\n\t{ 0.7844645, 0.5883484, 0.1961161,0.05866665},\n\t{ 0.7844645, 0.1961161, 0.5883484,0.05866665},\n\t{ 0.9128709, 0.3651484, 0.1825742,0.04814243},\n\t{ 0.9128709, 0.1825742, 0.3651484,0.04814243},\n\t{ 0.9733285, 0.1622214, 0.1622214,0.03438731},\n\t{ 0.1622214, 0.9733285, 0.1622214,0.03438731},\n\t{ 0.1825742, 0.9128709, 0.3651484,0.04814243},\n\t{ 0.3651484, 0.9128709, 0.1825742,0.04814243},\n\t{ 0.1825742, 0.3651484, 0.9128709,0.04814243},\n\t{ 0.1622214, 0.1622214, 0.9733285,0.03438731},\n\t{ 0.3651484, 0.1825742, 0.9128709,0.04814243},\n\t{ 0.6396021, 0.4264014, 0.6396021,0.07332545},\n\t{ 0.4264014, 0.6396021, 0.6396021,0.07332545},\n\t{ 0.6396021, 0.6396021, 0.4264014,0.07332545}\n};\n"
  },
  {
    "path": "FEBioMech/stdafx.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n"
  },
  {
    "path": "FEBioMech/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <stdlib.h>\n\n"
  },
  {
    "path": "FEBioMech/triangle_sphere.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\nconst int NST20 = 20;\nconst double AREA20[NST20] = {\n    0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718,0.628318530718\n};\nconst double PHI20[NST20] = {\n    2.48923451381,2.48923451381,2.48923451381,2.48923451381,2.48923451381,0.652358139784,0.652358139784,0.652358139784,0.652358139784,0.652358139784,1.38208579601,1.75950685758,1.75950685758,1.75950685758,1.38208579601,1.38208579601,1.75950685758,1.75950685758,1.38208579601,1.38208579601\n};\nconst double THETA20[NST20] = {\n    2.51327412287,-1.25663706144,0,3.76991118431,1.25663706144,0.628318530718,4.39822971503,3.14159265359,-0.628318530718,1.88495559215,3.14159265359,2.51327412287,3.76991118431,0,0.628318530718,-0.628318530718,-1.25663706144,1.25663706144,4.39822971503,1.88495559215\n};\n\nconst int NST34 = 34;\nconst double AREA34[NST34] = {\n    0.383935577992,0.363384852082,0.404286699707,0.399662586846,0.354335283849,0.352347120947,0.393903444368,0.379809289616,0.348615415676,0.362162027018,0.352706956245,0.334201951381,0.38550990319,0.353366062299,0.353210862208,0.390916630874,0.350536536666,0.334426717588,0.401460216421,0.403845317927,0.339702141107,0.395964760734,0.354184187245,0.35253909316,0.379408560901,0.394927777324,0.35332404861,0.391201373908,0.379167729391,0.339928651639,0.397540807177,0.35214902586,0.352731483864,0.380977520538\n    \n};\nconst double PHI34[NST34] = {\n    0.992311860958,0.540938308295,0.740973555387,1.21706836975,1.4250943943,1.48988159961,2.21160798775,1.64632156581,1.61309409716,0.272718163585,1.12766830359,0.692114402353,0.331284915403,0.825383424239,1.24305999687,1.18761290633,1.81917628368,1.21867315735,1.21642807486,0.887592308181,2.54850378579,2.24712659371,1.71737990968,1.6503477742,2.04868066283,2.75219649767,1.90134757641,1.78830605485,2.14096763772,2.56416958122,2.86656487935,2.31787616339,2.01399005833,2.09881196833\n    \n};\nconst double THETA34[NST34] = {\n    2.84381372042,2.45969310542,1.44397272665,1.45316745591,2.50154805757,1.95763275851,0.161250110851,0.427494793002,0.976186514632,3.82722948119,-1.34124680328,0.591757041849,-0.374424949851,-0.786805077371,-0.366748217522,0.210267741996,4.21323110868,3.42427462827,4.33893673216,3.93695829964,-1.24684183905,-0.465522391576,-0.637552827163,-1.18265444754,-1.5544160045,3.8807156385,2.7749120359,3.32757398959,3.7256260405,0.797024363447,2.2253590719,2.35938161601,1.80394081727,1.20832067743\n    \n};\n\n\nconst int NST60 = 60;\nconst double AREA60[NST60] = {\n    0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239,0.209439510239\n    \n};\nconst double PHI60[NST60] = {\n    2.77901624839,2.77901624839,2.77901624839,2.77901624839,2.77901624839,0.362576405201,0.362576405201,0.362576405201,0.362576405201,0.362576405201,2.397020341,2.11317043701,1.73299308984,2.11317043701,1.73299308984,0.744572312593,1.02842221657,1.40859956375,1.02842221657,1.40859956375,2.397020341,2.11317043701,1.73299308984,1.73299308984,2.11317043701,2.397020341,2.11317043701,2.11317043701,1.73299308984,1.73299308984,0.744572312593,1.02842221657,1.40859956375,1.40859956375,1.02842221657,0.744572312593,1.02842221657,1.02842221657,1.40859956375,1.40859956375,2.11317043701,2.397020341,2.11317043701,1.73299308984,1.73299308984,2.11317043701,2.397020341,1.73299308984,2.11317043701,1.73299308984,1.02842221657,0.744572312593,1.02842221657,1.40859956375,1.40859956375,1.02842221657,0.744572312593,1.40859956375,1.40859956375,1.02842221657\n    \n};\nconst double THETA60[NST60] = {\n    1.88495559215,4.39822971503,-0.628318530718,3.14159265359,0.628318530718,1.25663706144,-1.25663706144,3.76991118431,0,2.51327412287,3.14159265359,3.54640644495,2.9287380957,2.73677886223,3.35444721148,0,-0.404813791358,0.21285455789,0.404813791358,-0.21285455789,-0.628318530718,-0.22350473936,-0.415463972828,-0.841173088608,-1.03313232208,0.628318530718,1.03313232208,0.22350473936,0.415463972828,0.841173088608,3.76991118431,3.36509739295,3.55705662642,3.9827657422,4.17472497567,2.51327412287,2.10846033151,2.91808791423,2.72612868076,2.30041956498,-1.4801418008,4.39822971503,3.99341592367,4.61108427292,4.18537515714,2.28976938351,1.88495559215,2.09781015004,1.4801418008,1.67210103426,4.62173445439,-1.25663706144,-0.851823270078,-1.04378250355,-1.46949161933,0.851823270078,1.25663706144,1.04378250355,1.46949161933,1.66145085279\n    \n};\n\nconst int NST74 = 74;\nconst double AREA74[NST74] = {\n    0.172194497108,0.172616214738,0.156384928339,0.158507077935,0.181989031701,0.164489052852,0.170463214221,0.16791900153,0.174140795926,0.167638690642,0.163799740359,0.167974639108,0.174154539066,0.171879495364,0.166744662057,0.169537017558,0.185616681294,0.168799231951,0.168829055331,0.158481556172,0.17671435797,0.172868926325,0.18167436801,0.185611507665,0.15636355032,0.172247969404,0.18070564458,0.167150673674,0.181726232028,0.165314830546,0.169961200128,0.172654595308,0.171222346552,0.171246768459,0.167601272159,0.169450198726,0.167629614669,0.169508601315,0.165328142768,0.169914886884,0.164214499781,0.167677242098,0.17192283869,0.190385295886,0.16381928312,0.167103261273,0.180784609354,0.190430641824,0.164227106766,0.168116666569,0.180247005644,0.181982148896,0.172609862041,0.176818987391,0.158837157007,0.158858544541,0.168442689446,0.168486647778,0.160628620296,0.168636058657,0.159225581499,0.160273422768,0.168645193049,0.16061638219,0.168661465192,0.160240964724,0.159275111334,0.168607230802,0.166728581783,0.169458074264,0.168183535321,0.180160965744,0.170522960063,0.164487167827\n    \n    \n};\nconst double PHI74[NST74] = {\n    1.37637747565,1.77848403736,1.40219574588,1.67945305427,1.92268587646,2.31568591025,2.34029288949,1.74919536568,2.0904221287,2.65976168911,2.07826715601,2.07982156114,2.31224176828,2.79200030874,2.9309419527,2.58144952406,1.1534990749,1.13996555245,1.30505558926,1.40234552605,1.73756509933,2.00215145978,0.19103172404,0.7696420396,0.731695622618,0.498670411242,1.59204101919,1.71932119701,1.11126669369,1.22012811405,0.987522913199,0.592108360768,1.55921318852,1.69204624116,1.47866187997,1.13117589437,1.16637514167,0.932676730736,0.469112235723,0.572650532114,1.92280468579,2.18769815342,2.51317688672,1.56555227458,1.48104448229,1.13721016656,0.824791654243,2.36395408104,2.73274234576,2.89973890344,2.54482479876,1.37368120261,1.07179674998,0.466803260621,0.786073451869,0.194230542378,0.801816920121,0.552063943806,1.31992333966,1.39377623123,1.09644735409,0.791733455504,0.941592618213,2.21540832028,1.8284785983,1.70848167882,1.96385985674,2.30044204202,2.31792447645,2.31575385231,1.98922622439,1.69986240845,1.98690641289,1.69868183315\n    \n    \n};\nconst double THETA74[NST74] = {\n    4.23372155256,3.77350281851,3.84608000918,3.18484837654,3.45809438838,3.39077080754,2.91506076853,0.250156160968,0.316825916633,-0.143154113368,-0.84076048198,-0.426340636629,-0.106690018794,0.818610130238,2.33492710176,2.48483746082,3.5931710259,2.89909309915,3.22927360111,2.61469093203,2.80180069559,2.654831488,1.308041588,2.831818526,2.32376205684,1.78656726746,-1.30884279106,-0.960196437309,4.45822975191,-1.41123318914,-1.12411539369,-1.08459745398,-0.0945343132877,-0.363600184289,-0.696117769684,-0.744259801597,-0.0547273435112,-0.357678326852,0.321737854417,-0.353212976842,0.992995023213,0.714451163504,1.03342605354,0.861716307267,0.493572491076,0.334686032515,0.575808887909,-1.15430788691,-0.98649181298,4.15257530946,3.87038000761,2.24934454968,2.09486017238,4.63887041185,4.26918611221,3.70514048067,3.73735278376,3.33042501982,1.08705818641,1.47334175658,1.68393403727,1.42390002369,0.995745287995,-1.55050565608,-1.56134266036,4.37856330849,4.10130884779,4.24511018959,1.55945328606,2.04234454148,1.3922866091,1.63240927868,2.21673002442,2.01964818044\n    \n};\n\n\n\n\nconst int NST196 = 196;\nconst double AREA196[NST196] = {\n    0.0638972311061,0.0668894692436,0.0649858303874,0.0616304009286,0.0652668671976,0.065266527635,0.0663350077825,0.0619973712494,0.0659695136296,0.0677211405895,0.0640119463478,0.0658673121802,0.0638707648582,0.0613608629933,0.0624495545788,0.060868522918,0.0674482226427,0.0671445089851,0.064837595448,0.062626039322,0.0617571980229,0.0626253474998,0.0657849326183,0.0650523303174,0.0644063159435,0.0632107557901,0.0661364206817,0.0670360363369,0.0641293778863,0.0611082806036,0.0663154376387,0.0631159265005,0.0644200031677,0.0639622415538,0.0624689285189,0.0670199827974,0.0618141056617,0.0661940957789,0.0626199310979,0.0661333082831,0.0630759889626,0.0646358006381,0.0632064948448,0.0657527651672,0.0650919188211,0.0614645705149,0.068038372082,0.0621728888174,0.0621604388567,0.0624503500411,0.0615810462997,0.0623772508079,0.0628872780195,0.0643338947915,0.0629750384363,0.0673882292199,0.0633495486256,0.0659467764361,0.0645347447336,0.0639615118178,0.0634791780095,0.0646338089292,0.0632190912109,0.0650657772645,0.0610130299864,0.0649286976652,0.0645341052298,0.0615521843399,0.0631564345319,0.0635087658748,0.0641850864396,0.0648102851225,0.0639681320617,0.0640018140136,0.0669712301109,0.0640833922652,0.0639546643343,0.065864856972,0.0629714016573,0.0647429367746,0.0649855121913,0.0656913043705,0.0610866775775,0.0627823095172,0.0650003056205,0.0638208777769,0.0613889570916,0.0638986821682,0.0648972457364,0.0657808734732,0.0647655104122,0.0642512111769,0.0624994214407,0.0675120161824,0.0654779359653,0.0651195316694,0.0613319113968,0.0681258859813,0.0635523806897,0.0634059245265,0.0635785126812,0.0635843220386,0.0672131922246,0.0674297560572,0.0633540087023,0.0633556232024,0.0622259647217,0.0621016756343,0.0625175919109,0.0615427568012,0.0625274688479,0.0663983992939,0.0617283672137,0.0649753513125,0.0644470747974,0.0668101556072,0.0612965420155,0.0671188646658,0.066844693631,0.0639306465074,0.0635281786511,0.0620441236157,0.0620105509386,0.0620206299095,0.0620323741906,0.0619472652273,0.0613419067267,0.0645874503395,0.0625951627553,0.0682956166143,0.0618125564724,0.0660826280633,0.0635518632664,0.0625994172267,0.0636431475865,0.0656899318151,0.0614506375482,0.069319385953,0.0626899203954,0.0639075010672,0.0610870417725,0.0659135629029,0.0648390635504,0.0645356233343,0.0615356150467,0.0651728841148,0.0647208956059,0.0614134063328,0.0632181648813,0.0671536214318,0.0664316351668,0.0634431678513,0.0635170076154,0.064137433268,0.0619366174429,0.0662827425601,0.0632340633187,0.0666931621427,0.0634852057939,0.0655424402549,0.0627586118204,0.0637870642746,0.0659670181209,0.0649179542241,0.0643158934673,0.0664030078897,0.0615439641304,0.0646235217071,0.0619671421584,0.0648177739233,0.0624309475518,0.0683794315543,0.0649214754714,0.0610500616617,0.0616095715887,0.0648169853985,0.0651418225484,0.0655188258374,0.0620442301568,0.0630219105744,0.0663489073152,0.0647882352238,0.0621295964819,0.066430603159,0.0626539499938,0.0640667376352,0.0637282714912,0.0654284504408,0.0610327460579,0.0665293001472,0.064299664322,0.0627676549845,0.0670393417749,0.0614484705382,0.0640106207734,0.0660463769293\n    \n};\n\nconst double PHI196[NST196] = {\n    1.80836671617,1.29832776991,0.94228751199,0.732905150597,2.19377370325,2.31639125438,0.966431658401,0.818889029927,1.37953490986,1.22925672388,1.05037045195,1.25926285733,1.18709403222,0.88111579647,1.10552091779,1.14838705952,1.31124258856,1.64990671373,1.36519575348,1.94572963976,1.57512415122,1.72009677992,2.12963710354,2.04756241373,2.04486196597,2.17108671434,2.11519971854,2.40247205336,0.602805457992,0.370214502354,0.265896414357,0.459050383423,2.3993218573,2.15960844178,2.14015125695,2.04323188674,2.36132260345,2.49982090068,2.37013126348,2.52237961342,2.72859268381,2.74927033539,2.45912165436,2.23865900628,2.25771531208,1.80552440002,1.68742933601,2.01869423775,2.0624576012,1.78824900142,1.68895467049,1.85577193941,1.39354056866,1.31843835311,1.45381725971,1.66974915202,1.45011354454,1.52010206323,2.32743187247,2.49552311052,2.71562823291,2.69941844724,2.31356410972,2.46836390872,2.23761848557,2.41885969296,2.36272762192,0.508161558671,0.590138992775,1.03121493528,1.10234167365,0.926146424803,1.33503863901,1.17749089228,1.48878894412,1.43103441945,1.23125592847,1.62364221231,2.03308212549,0.973514445626,1.69088311595,1.47606327184,1.06048610543,1.26774168139,1.10204828398,1.4265683055,1.33466591432,0.539478631249,0.772476502065,0.921903797273,0.875826122064,0.0895315987298,0.137851673947,0.327797088242,1.06443825113,1.27434703233,1.41698102098,1.34733271759,0.985625000209,1.12914259348,2.00882415627,1.96242175598,1.73300599931,1.8216024004,1.59791619364,1.553833713,1.71835252331,1.83555509629,1.49357365456,1.46983162977,1.68364939523,0.715958399061,0.948251487118,0.823591668672,0.64799502201,1.08545745922,1.02755896126,2.67811597869,2.86835724998,2.87289785746,3.07601049864,2.33993349868,2.40693462402,2.63558623579,2.49463322079,2.71176455438,1.77092556584,2.00337323549,1.70664796972,1.62778226531,1.93596754464,2.09347795197,1.5277243242,1.58761099372,1.87774948053,1.68226789429,1.96065151405,1.82328089079,1.78984467507,1.71260908411,1.87922075866,2.62444258213,2.81122931044,2.99553578578,2.55642150561,2.81834768778,2.62662967867,2.13211709458,1.76745276656,1.98062982002,1.12495708594,1.45562363485,1.35664636459,1.3539172001,0.729157727476,0.531935703048,0.625937913466,0.762894289957,0.391524660781,0.314600928392,0.737166675241,0.674089966637,0.478384402152,0.290930734262,1.0267248652,1.11358749938,1.16258939196,1.37316397925,2.00580168006,1.79674329049,2.12955413302,2.02811677239,2.12481846107,2.00872828191,2.42642342061,2.36143083527,2.24969869344,2.07292286777,1.01616262327,0.717088536216,0.785989747102,1.14922105765,0.882725643433,1.08160587844,0.432245579661,0.413317952937,0.80375186523,0.659586646746,0.794396809397,0.634598863078,1.68355379272,1.79551428286,1.44970886684,1.35309487299,1.4792185133,1.6857176227\n    \n};\nconst double THETA196[NST196] = {\n    -1.39879016116,4.29290552688,-0.937064908269,-0.945764591142,0.869098980069,0.690132440686,4.00870550433,3.82380312656,0.133015465789,0.599313147326,-0.686521809384,-0.682918425682,0.172434683271,0.423138512991,0.411658556429,0.826635878645,0.992875148352,-1.23314119079,-0.886905027792,-0.96884804196,-0.869354478659,-1.04471898523,4.27325532862,4.50345754587,-1.35099218529,-1.53568741585,-1.09978895672,-1.49114157014,-0.622642130078,-0.637223824463,-0.0122066818501,0.286428223023,2.99803767246,2.96752777002,2.5384756761,2.74233298388,2.15165335374,1.90767868759,2.46926237165,2.68651765489,1.95644177919,2.53906368875,3.2944324046,3.76584122856,3.48887883771,2.7481727468,2.95028166459,3.14501287265,3.39061927511,3.12744171073,3.33923410152,3.50062744154,-0.480259347054,-0.280261383655,-0.0955986160253,-0.115072124391,0.560337364087,0.332811467341,-0.966088665087,-1.15358840735,-1.00046483975,-0.495079531284,-0.656905027578,-0.426289208525,1.15963296772,1.33027812736,1.62841713147,3.46452720013,3.87637290747,2.57254124801,4.42729833968,4.2979258923,4.05520263476,3.91130051605,3.37808403709,3.60568728296,3.66232850164,1.34933038882,1.25389783742,1.72062116747,2.5624323531,2.57770111415,1.30135084276,1.21086041804,1.55989690753,1.38396784491,1.59913908711,0.714237250946,0.678104317038,0.891467913842,1.16206278221,0.980860351692,3.7366501396,3.17053219172,-1.1609736903,-1.11483105316,-1.28202865411,-1.50653839836,-1.42055632555,4.68832861271,3.85934507402,4.07417898369,4.1234102221,3.70891434698,3.76977202055,3.97532239097,4.69341175964,4.49919725968,4.63742602288,4.40643541284,4.32299331985,-0.376787542551,-0.447800956207,0.126996343883,0.00339147810881,-0.246137658807,-0.0156009829997,3.45024745599,-1.52694920829,3.07068782174,3.97148448643,4.24701962691,3.94122471399,3.87663179939,4.50847308402,4.36225610912,-0.316895021078,-0.34084997707,-0.682444583324,-0.495236001174,-0.731694944088,-0.575115932759,0.938202810665,0.728783458353,1.07319603318,1.11700351484,0.839544182254,0.675566688733,0.0735329036302,0.294226246794,0.463895962205,0.335725758055,0.0408824865674,0.662965888283,0.739559114522,1.38708455103,1.13181546416,1.67766181592,1.53839731511,1.49197997337,2.78805152743,2.96980789241,2.78285090759,3.18564748945,4.4485884031,4.2418379763,-1.25866365282,-1.50771503471,-1.24790804871,4.48019766728,1.65781415434,1.3329621944,1.15233580815,1.53197984437,2.14960463908,1.96547091301,2.37163917892,2.37807131896,2.32927203473,2.34494634088,2.11105553679,1.90941226796,-0.144943351745,0.0670534847881,0.162117270986,-0.139931312373,0.392525929233,0.321756086501,3.00977844775,3.30463633459,3.02426735215,3.21838405974,3.53138794426,3.47196174541,2.62748473851,2.08062052909,2.50204962909,2.7205217448,2.18801705959,1.96178119228,1.75043866232,1.93156157162,1.77544514533,1.97567098299,2.16404739813,2.14589799835\n    \n};\n\n\nconst int NST210 = 210;\nconst double AREA210[NST210] = {\n    0.0617261268408,0.0572021838321,0.0585417938648,0.0579280011801,0.0628324889081,0.0600692862341,0.0609854756845,0.0591470501748,0.058467055142,0.0572211510884,0.058526820186,0.0584063297697,0.0629212944849,0.058485970962,0.0587998679634,0.0597919176565,0.0617154681067,0.0593629151904,0.0584048672317,0.0584563269676,0.0565036549011,0.0604426835554,0.0616212055909,0.0567587686408,0.0590195766734,0.0626737849368,0.0604578213511,0.0580331450718,0.0601845861277,0.0609650862119,0.0602381574602,0.0614350575904,0.057675434257,0.0614435647344,0.0601357747143,0.0606436947824,0.058516660023,0.0603949705967,0.0612807382314,0.058676514521,0.062078711286,0.0591027958568,0.0627753295078,0.0568733542969,0.0614661025879,0.0599697922713,0.0578262691074,0.0604992858605,0.0573446399649,0.0608952893807,0.0575454564564,0.0596462828169,0.0584359546775,0.0613956781304,0.0581551890517,0.0596317463966,0.0568177903081,0.0624661475829,0.0594039733269,0.0595157041224,0.0625432382959,0.0589886090546,0.0599593815068,0.0614006997236,0.0609971295995,0.059644376798,0.0575743926486,0.0576005519456,0.0614582203163,0.0579799506881,0.0572147898719,0.0632280691993,0.0573247291651,0.0577851114325,0.0609633833708,0.0598527056738,0.061395890046,0.0588336341589,0.0589329561808,0.0588594830734,0.0575033699712,0.0636153242894,0.0614667698898,0.059836059967,0.0609490477428,0.0599427215578,0.060992235703,0.0617394120729,0.0597898915972,0.0616086081686,0.0599585833968,0.0603997301358,0.0579381733046,0.0613763590138,0.0607981981826,0.060961085144,0.0584203973588,0.0599616864129,0.0572786402672,0.0598138660989,0.0588934323424,0.0600343770278,0.0602415630196,0.0583684733324,0.0607680389403,0.062993320632,0.0574086256585,0.0584923329581,0.0601812167544,0.0579784139407,0.0573779165296,0.0630017327435,0.0583718210939,0.0585171184189,0.058155534865,0.0613669118278,0.0579409057712,0.062817072186,0.0613784399263,0.0597049728596,0.0585632128005,0.0586823214224,0.0628116061211,0.0572428072035,0.0629258462517,0.0584716026651,0.0590894912888,0.062075774712,0.0605919214638,0.0586325221713,0.058894588923,0.0595104634257,0.0625367518162,0.0590212952343,0.0567714437982,0.0594129165014,0.062469960423,0.0614876120822,0.0600550869591,0.0604977268336,0.0617609855461,0.059978943823,0.0608883354762,0.0596841192514,0.0607528377946,0.0573074068185,0.059779199051,0.0578427640479,0.0621323486405,0.0578323006126,0.0587510828722,0.0629538807279,0.0586873422163,0.0627785760775,0.0568780687601,0.0579805899646,0.061716254378,0.0593746734986,0.0629321845602,0.0578386572947,0.0589624905788,0.0620820432839,0.0628970090094,0.0584960013579,0.056514592837,0.0615457759188,0.0604177830247,0.0604087851543,0.0616301921087,0.0590449330796,0.0601335773987,0.0576854913489,0.0580541040744,0.0604638351777,0.0602104325046,0.0609567743297,0.0612931762639,0.0603727593784,0.0585154840033,0.0602179813966,0.0606605959591,0.0607402992078,0.0624186049465,0.0596818112761,0.0605764843196,0.0586418303659,0.0629173567506,0.0584563520542,0.0596765598385,0.0599895918732,0.0624447935436,0.0586029827012,0.0616856101553,0.0575607114475,0.0597098614745,0.0583943402342,0.0567541791485,0.062639679851,0.0572141888902,0.0632281967766,0.0584620551142,0.0613922234501,0.0591338295615,0.0613816159532,0.0574750795995,0.0635785441598,0.0578159100694,0.0572651244042,0.0628414012199,0.0587136259924\n    \n};\n\nconst double PHI210[NST210] = {\n    1.64205163325,1.69576891668,1.55302696835,1.65786003915,1.81035172201,2.95259004663,2.1249446318,0.741493568919,0.723553593224,1.93599048719,2.14282114279,1.25007811366,1.04182127262,1.02635892657,1.89145345108,1.79361677253,1.35938818867,1.42485102221,1.14838278674,0.99705814157,0.879634603933,0.661399804237,1.19669679328,0.901890653771,1.25556457096,1.09770289637,0.579064320031,0.621791804787,0.844020570294,0.984616886048,1.33441999678,1.06374203844,0.964463053263,0.832904133211,0.787902716094,0.384126785123,0.193770593424,0.230221614714,0.0258511719668,2.29424459936,1.57959835114,1.66096165612,2.02038929766,1.87437724004,2.7967870441,2.57806523377,2.54229714419,2.74506583765,2.56484307545,2.76896456441,2.79822718898,2.40784507977,2.39643720059,2.3985253715,2.01370066215,2.17944064897,2.24307873494,2.16227518747,2.26993357228,2.49615785582,2.59775839819,2.44354220202,0.689805061893,0.57833006518,0.229284323085,0.360440748487,1.0073335915,1.41544068882,1.36449124173,1.28860835751,0.959186224399,1.07431141218,1.3581705353,0.934410333228,1.24797450007,1.05837374614,1.18734698209,1.36397470589,1.57816229938,1.21923419499,1.55309323497,1.57152650421,2.17261512819,2.38703490527,2.39338068556,1.42189920262,1.40660925281,1.7655980271,1.57984248649,0.806267324898,0.818214934415,0.596523173251,0.386029486161,2.73802075954,2.51135842722,1.72581766695,1.45542742879,1.51104402873,2.19551383769,2.07767437816,1.8662363286,2.1120863193,0.924544541244,0.690862683385,0.463363640366,0.815794901293,0.763338309699,0.575726556235,0.39331749424,1.9457428196,1.32072054276,1.16256974512,1.21952868619,1.52597051393,0.397865345695,0.623162592102,0.528110489743,0.336544697462,0.771426356384,0.723598572609,1.64128627014,2.0707360693,1.92155721418,1.72141391838,2.28106769262,2.40959460303,2.28062500769,2.47147745477,2.51885325753,2.33072695699,2.79251702411,0.616314122133,0.806705678975,0.815716331747,0.661954521122,0.405339624566,0.4521778649,0.925064218937,1.0589084659,1.03320964047,1.28856108935,1.39045917049,1.26440078704,1.0242404107,1.19744440575,1.03369958106,1.21646246586,1.39237158135,1.40225505447,1.76758417533,1.93700633118,1.76454410323,1.9499660554,2.14730644024,2.13545656816,1.98608460922,1.6168700403,1.78783418139,2.05458452062,2.3685657813,2.00855332643,2.15563926497,1.61093369862,1.80538304526,1.54683911135,1.93510725406,1.84878497536,1.65814535928,1.09766452354,1.26262357329,1.14102277134,1.02812642169,1.44663233161,1.37207112602,1.33295478463,1.14864395093,1.89770419679,1.82155289681,1.74645486036,1.59454684036,1.52214661489,1.44352702927,1.75186435933,1.62439043882,1.72292328059,1.94695209096,2.07866750423,1.97770340891,2.69636664599,2.87665546543,2.63360042872,2.73120366038,3.09064928718,2.93583934137,1.67302751761,1.63464142831,1.47493025448,1.45193138294,2.18301658924,2.00211427025,1.82023680452,1.8367515343,2.19174625482,2.0262701945,2.59972261129,2.41393475512,2.24074845605,2.57216772373,2.37119677121,2.22279345708\n    \n    \n};\nconst double THETA210[NST210] = {\n    2.3098846686,2.51498827854,2.68012833101,0.0253666627587,0.169392501915,0.657387339984,0.490992496048,2.65116816848,2.99039328198,-1.33637194821,-1.25929241163,-0.820476371327,-0.914252056679,-1.14541300435,2.61460431107,2.16523771779,2.05625646871,2.2600818858,2.00125526765,2.17173043291,-0.74576225749,-0.774975400854,1.37253440654,1.67259232958,1.61581745265,1.76047374954,0.966195650031,1.31010293167,1.39389491435,1.24123813693,1.25316102984,0.485401641265,0.965288700881,0.534975301238,0.805233885293,0.745276341802,1.21251649094,-0.764791583187,-1.12050850745,3.41360652402,3.05851654701,2.88457603863,3.00851756579,2.85098373261,0.161582322815,0.369567296303,-0.39571709708,-0.42610149364,-1.17299105175,-1.05040359745,1.26023224126,1.6191403593,-0.133349902,0.15804166089,0.0978082156619,0.257530292958,1.16067774104,0.919665482458,0.691073140548,0.68949601478,1.0575356908,1.27195547124,3.53384992091,3.21130754361,3.5834542682,3.03368656478,3.92616241998,3.77002143123,2.64946743748,2.43191979551,2.60811904911,2.40408063841,3.06261372911,3.06693442885,2.8588724022,2.844681848,4.07671837497,3.99066661754,-1.52993974539,-1.26792675509,4.10440742845,4.31203289817,-1.03235218752,-0.654603856943,-0.93371694055,-0.948376107971,-1.17259391141,-1.20673370066,-1.30175727126,-1.26120094063,-1.50510564814,-1.08064272864,-1.25879854111,1.86229151811,1.88769911124,1.95660042833,1.69543596408,1.90082692269,1.64348140361,1.8453758932,1.79774553277,1.3867140271,-0.463593331996,0.2993046879,0.285856421731,0.041182803929,-0.243671501861,-0.410145218339,-0.216522785756,0.916946204154,1.01696649562,0.873326483872,0.649257589157,0.956032860751,2.45251451828,2.41164607697,1.67153574443,1.8714005006,2.13046071178,1.8582050896,3.76128011224,3.85059706488,4.0269999738,3.9668915638,3.90980617712,3.67880926501,-1.4912014996,-1.51903458212,4.38233131068,4.19517607477,2.37620326197,3.83849032401,4.0337289192,4.30668850618,4.53228423379,4.00956877064,4.51110072448,3.50625848105,3.6827104587,3.28074139158,3.62143125874,3.42060521145,3.25877645417,4.42868725391,4.32026717414,4.67694501915,-1.49811204608,4.4350505116,4.65698916594,4.39365473246,-1.5703398017,4.61773603201,4.25255832151,4.34731261372,4.59255061866,-0.889545282254,-0.855347040364,-0.980104266793,2.49810713195,2.15703945805,2.24630581705,2.09035935479,1.53255344431,1.57641652788,1.32967977034,1.36736049108,1.13238464709,1.14075761621,-0.411900644574,-0.595308140516,0.277774073429,0.0706636747074,0.0726806696981,0.258746996198,-0.129780691837,-0.149468007419,0.535945714201,0.735798218433,0.381562620205,0.765355530577,0.420094078399,0.607744191893,3.60197558083,3.4114790067,3.24402822918,3.23324483432,3.42444692424,3.62831376894,4.22893605183,4.67549316729,3.74614924369,3.3674029025,4.22950188064,3.03273391803,-0.198591200116,-0.638277362782,-0.301397371083,-0.510682787489,-0.534757890292,-0.653251989401,-0.533215802892,-0.298009885792,-0.26480792881,-0.153162415167,2.62995761815,2.4684206133,2.63803785048,3.04010873968,3.12922806285,2.92481792802\n    \n    \n};\n\n\nconst int NST396 = 396;\nconst double AREA396[NST396] = {\n    0.03196806835,0.0307724814763,0.0326670909823,0.0316753895894,0.03241593564,0.0318400794937,0.0325723045501,0.0316902612656,0.0313380298612,0.0304442548717,0.0329109704209,0.0316294159061,0.0311261854815,0.0324481846991,0.0302529332871,0.0299337495325,0.0336586239237,0.0298606799971,0.038943019973,0.0313518618684,0.0324459235281,0.0300835348858,0.032919542974,0.030007112045,0.0339456101877,0.031439248786,0.0311274136036,0.0308412242929,0.0332299213076,0.0318391625081,0.0307473344078,0.0327271256857,0.0320582010291,0.0317433101989,0.0315504238471,0.0315502765064,0.0329668656051,0.0307051346218,0.0314888481773,0.0326729310569,0.0313903090479,0.0328447962789,0.0312767914791,0.0317042423325,0.0328018064884,0.0310766125467,0.0317446089275,0.0327723364577,0.0316257100321,0.0303708758793,0.0309141591621,0.0310952382243,0.0302798893944,0.0308007312497,0.0327397848397,0.0315083013587,0.0325358857685,0.0319347878067,0.0321014582275,0.032970012598,0.0294522431313,0.0318511860402,0.0321735593042,0.0317112070203,0.0319092112013,0.0316329736358,0.0307040867842,0.0306476131915,0.0308413200566,0.0304029429484,0.0333360012636,0.0303345472719,0.0307816747325,0.0310187657823,0.0336805232324,0.030452737782,0.0308530647428,0.0314817165838,0.0324917977933,0.0321975864164,0.0322787063524,0.032131488421,0.0313014745753,0.032024567557,0.0323005973735,0.0308533426957,0.030761375968,0.0304687609196,0.0328390590568,0.0314926720471,0.0323383202758,0.031274873493,0.0319807975883,0.0319575858874,0.0299756554502,0.0326093302885,0.030577241214,0.0380165862533,0.0336345019351,0.0310158066808,0.0313364596216,0.0325033809997,0.0310069926698,0.0327023874213,0.0306648669175,0.0331308246036,0.0316587111431,0.0316290706979,0.0312239071564,0.0318444879588,0.0313310499878,0.0326993452984,0.0319956577793,0.0323353884764,0.031139279012,0.0318422796366,0.031488419299,0.0317694166571,0.0325892444505,0.0315655265794,0.0322149103406,0.0318590285214,0.0321988505034,0.0321150233477,0.0312783897417,0.0314978456415,0.0308145540461,0.0319389362847,0.0305842802071,0.0304751069434,0.0303407069125,0.0309307558749,0.0312477297644,0.0329290265655,0.0304135212529,0.0327095998468,0.0318167332071,0.0322522842709,0.0311297488753,0.0313816509232,0.0317180516934,0.0313433164486,0.031234905619,0.0328214865829,0.0312255669176,0.0332255183054,0.0327989491842,0.0309694878391,0.031226670151,0.0323111999794,0.0327167412032,0.031385334616,0.032328856459,0.0325014437715,0.0318304554517,0.0318672729691,0.0318016591845,0.0314340208606,0.0323390971949,0.0306674931114,0.0320663397708,0.0327848186549,0.0334842664009,0.031650894515,0.031044259039,0.032426730159,0.0318835323752,0.031932534153,0.032123778789,0.031383197724,0.0313208234634,0.0328927228934,0.0319576405568,0.0311663378929,0.0317523680796,0.031391973863,0.0309971828016,0.0323414232078,0.0315839127426,0.0324004692026,0.0314150856752,0.0307800163387,0.032834147999,0.031808738222,0.031655069784,0.0323372575461,0.03002487425,0.0316408645269,0.0319456214575,0.0320576490491,0.0324915309134,0.0310442404533,0.0314989764372,0.0311289313323,0.031855686058,0.0313448749221,0.0316085724141,0.0318854131335,0.0329953570563,0.0317759351331,0.0309336870324,0.0326655163441,0.0318016069401,0.0317647839363,0.0311175453967,0.0320308170714,0.032119204769,0.031264618296,0.0320172078262,0.0320723692976,0.0322087189005,0.0316590903441,0.0322845740305,0.0317335423549,0.0310281091071,0.0304268850935,0.0331252581373,0.0308080108274,0.0317667212385,0.0326650019839,0.0312366672035,0.0324600392685,0.0301425063795,0.0305081962146,0.0306387982171,0.0312662513038,0.0300178583765,0.0304862962875,0.0324863640379,0.0335447409856,0.0308724659125,0.0317489484094,0.0308301367763,0.0318638768295,0.0331208227813,0.0313004376747,0.0311332290901,0.0310870703121,0.0311057145924,0.0331623093442,0.0313980020344,0.029995772295,0.0324180209079,0.031055061027,0.030518750923,0.0329702883695,0.0310360502093,0.0328873948018,0.0305873140198,0.0322710005692,0.0309229268826,0.0328371562254,0.0315999428187,0.0322659138972,0.0312670341958,0.0311668524703,0.0326672952385,0.0297563832912,0.0336979069779,0.0315124209716,0.032117704645,0.0302264892837,0.0323117122241,0.0321733819469,0.0320926545123,0.0309395063436,0.0331193552439,0.0309339094277,0.0305028805452,0.0329795725317,0.0318733256954,0.0333498422757,0.0318489477896,0.032861365092,0.0312299493823,0.0312764709868,0.0325225197675,0.0330937604043,0.0306935680727,0.0312378311115,0.0322687370109,0.031857647371,0.0311325778664,0.0321099153684,0.0321593297224,0.0317731416173,0.0320959937696,0.0301597393625,0.032420414026,0.0304356824728,0.0328472759056,0.0313759407128,0.0317236405112,0.0325905593951,0.0310072231131,0.0318237805372,0.0320668938192,0.0320413439128,0.0316171356179,0.0324894629487,0.03286636986,0.0310258885552,0.0327809345445,0.031272363915,0.0325601836509,0.0326020913087,0.0313420807296,0.0316103856774,0.0318864546126,0.0315831993899,0.0323906971195,0.0328880274412,0.0315266328732,0.0320219870382,0.0309247504999,0.033227062701,0.0309841400221,0.0309614505782,0.0303308291027,0.0312634025704,0.0314136225711,0.0302883287319,0.0326226568884,0.0303145772644,0.0329859873399,0.0319066766656,0.0311712542463,0.0309737471274,0.0333330127418,0.0313757270155,0.030933370407,0.0326489853417,0.0333350285315,0.0316133563944,0.0307919537013,0.0326898482914,0.0317186371716,0.0332078927676,0.0310222651778,0.0326850446859,0.0314299624938,0.031412241056,0.0327099745256,0.0310583622241,0.0322798738471,0.0316147893588,0.0312112154822,0.0329953333098,0.0310051159533,0.032236563216,0.0317650427946,0.0308034153633,0.0325879614426,0.0308239041695,0.0305579741665,0.0334965959073,0.0304723714499,0.0317272809071,0.0323531238259,0.0311920205473,0.0323627164706,0.0326259036942,0.0307573090302,0.0318530716458,0.03230015494,0.0312356351217,0.0324697891176,0.0309473562468,0.0305052914482,0.0333881565102,0.0327027978552,0.0304401523955,0.031966884925,0.0325524307364,0.0319887962319,0.0319682287202,0.0306708152369,0.032373562358,0.0312290681946,0.0317476091343,0.0327822735816,0.0319771327094,0.0314941880356,0.0311844250801,0.0327073658651,0.0308447664465,0.0305961093274,0.0309767782876,0.0305810178556,0.0308007887344,0.0304984092436,0.0308613805317,0.0305237462805,0.0305747855136,0.0306106529603,0.0308269037684\n    \n};\n\nconst double PHI396[NST396] = {\n    2.31176052497,0.696785381799,1.26730066,1.41299632057,1.52601790063,1.50079731645,0.775639585955,0.435847923935,0.581910041599,0.837524756219,0.697660808152,0.650714932317,0.888189413513,0.812527781427,0.314181108795,0.391954236848,0.396118112416,0.0794623070853,0.202185899367,0.186753495252,0.338428563595,0.161379184414,0.30559673095,0.459391806863,0.517718908522,1.45387779152,1.2990637431,1.50525316265,1.49167317972,1.81030593945,1.04803871295,1.92814462286,1.66858401143,1.76512334192,1.67144971628,1.7909592956,1.48857132891,1.63425644758,2.17352033451,2.12458949333,1.65121572249,1.79754025429,2.34332732387,2.19737877394,2.12713449724,2.0968365024,0.854692620619,1.0124522328,1.15939952474,1.0030725586,0.916133241006,1.15632542176,1.17168419701,1.02711253342,0.948959210375,1.51270808094,1.52062129106,1.26831403289,1.40091050632,0.456965478185,0.32015255276,0.490129245876,0.346494294942,0.574989217298,0.429714156186,0.914059332287,1.14784167592,0.867337286872,0.858076781153,0.551464985608,0.613171705703,0.764042190623,0.54343606281,0.577929553359,0.769388604386,0.683596319248,1.35319666546,1.23906885126,1.35988061229,1.21740663206,1.64639883387,1.65326547226,1.78680610482,1.86253861326,2.02837239074,1.79138734028,1.65326858106,1.68881591235,1.13532036251,1.07443044207,1.17989900745,1.29301455888,2.15789536711,2.04144494827,2.12576615189,1.98020105939,2.37642869741,2.24309328244,2.33625527902,1.34741736534,2.05838246415,1.92267875337,2.19474801031,2.19810634856,1.59388777332,1.62267094885,1.71656412252,1.86400372621,1.77496575107,1.89426882882,2.31928601496,2.46260322377,2.1264833159,1.97150452213,1.94798592265,2.72232881227,2.57354541606,2.99603855532,3.00136900511,2.83796512203,1.75822014365,1.64014222598,1.89456770068,2.16307814052,2.43884813992,2.55653218888,1.98518913089,2.1277584552,1.01477773256,1.00822806796,1.88303141384,1.98576513015,2.05925892802,1.90971327622,1.5202758141,1.6547449955,1.6373892574,1.5167944079,1.78174483646,1.79596176859,2.22172919178,2.07734269049,1.94646351176,1.94145923617,2.73608647634,2.73960850572,2.48295059454,2.59339797312,2.08325095155,2.22227743697,1.26520612642,1.22173063964,1.36026769806,1.12053712269,1.09829258493,1.22190257114,1.37776749941,1.26111725606,1.5712875067,1.0266920436,0.893336439274,1.3712570788,1.12483708553,1.22211089045,1.09306209718,1.2910478717,1.42476870382,1.11631511601,1.06676988672,1.449771098,0.936314060352,0.964648146386,1.07602787105,1.09163836884,0.659414320464,0.526859267083,0.811911689083,0.797927770303,0.704981606838,0.562873589921,0.829198058417,0.78924436294,0.876041791699,0.678644861875,0.632951094183,0.564831814146,0.123017001608,0.269966193751,0.169807589087,0.322465894021,0.389680059315,0.411664384201,0.728096159587,0.586058656447,0.864771919022,0.862014772549,0.732397018757,0.594739937965,0.846916496037,0.870863430152,0.992103011174,1.00062089176,0.425435021745,0.564623769417,0.439190189339,0.593368212513,0.715898571219,0.700351112435,0.72302764987,0.772486533884,1.01710743794,1.3113148681,1.16111297043,1.77144805314,1.91370255831,2.02419348936,2.04675901517,1.61847563217,1.62086056626,1.75689129124,1.87786559618,1.76519790435,1.88378732653,2.0190105833,1.95602062484,2.06905691542,2.21849590339,2.18381440884,1.38813271973,1.59049753297,1.43586247662,1.33497090301,1.64615624215,1.54390315194,1.4951636962,1.5488616194,1.59729132853,1.70338407446,1.75079233708,1.80424877721,2.33167022,2.41070695686,2.30782548391,2.47249915878,2.13177411287,2.27635980261,2.04068949643,2.08062867781,2.24564024092,2.26920588791,2.34907929689,2.49312295534,2.70098271424,2.85549736761,2.56078104076,2.74270059303,2.60280545857,2.49628252718,2.48347462691,2.62615241007,2.7635171918,2.59681628973,2.74937161546,1.73807928124,1.59827006103,1.58606200037,1.71818227886,2.15760627088,2.25560824354,2.2876639813,2.01493330104,1.13077690085,1.38084074082,1.38486134993,1.26490307779,1.12625865823,1.25577733428,1.93829459795,1.89712750559,1.81366226064,2.44165521039,2.29506571102,2.27697516462,2.33785138039,2.40594939038,2.54576442129,2.39082087556,2.3010866836,2.14458049228,1.91734856658,2.05930957492,2.16191845626,2.08503070171,1.9183449732,1.85114038973,2.48477118988,2.34601016357,2.59543757575,2.30787674523,2.53572503191,2.3920042891,0.954414959859,0.824344560586,0.673190988701,0.670602038639,0.988306605106,0.838811125455,1.11300122308,1.09417597452,0.818364169072,0.949826443206,1.84599357322,1.9996108456,2.01828907951,1.72218455552,1.75111256124,1.89308845029,1.3843921634,1.26619702982,1.52488866549,1.34459333957,1.50593400833,1.38588361215,1.5079723174,1.44893068153,1.20419342163,1.71582827889,1.83639514791,1.84883958191,1.69414129196,1.30283348825,1.44304154412,1.30142254085,1.57723027195,1.43214018816,1.56746223346,0.917284988669,1.1776549164,1.02047305606,1.50121846428,1.47917964055,1.32896052958,2.5053054119,2.35503579713,2.27035706687,2.24672756588,2.79547488062,2.95556649655,2.96396829628,2.81193859628,1.85270292566,2.10577150347,1.99284804991,1.84476876448,2.09978361572,1.97922051903,1.7438798564,1.48117556358,1.61891659507,1.7375966849,2.62401045801,2.71288124931,2.56723913424,2.53182872808,1.17255587961,0.980731507507,0.998114056929,1.29588473473,1.10587368579,1.24691718189,0.987199791866,0.926368717584,1.03933387921,1.20013412338,2.42405587056,2.42679633471,2.54892490199,2.69865938298,2.55773310729,2.70484906098,1.62875388525,1.53919788753,1.65858323704,1.38880328479,1.36364811454,1.48478363959,2.76725055687,2.82709730345,2.84275386461,3.00403327366,2.98319561957,1.13191993571,1.23385640099,1.22198071564,1.37673049277,1.38359121376\n    \n};\nconst double THETA396[NST396] = {\n    0.553414398579,-1.46680480798,3.19185953489,3.10677317961,3.1914837276,3.35686814715,2.98877217408,-1.12363409936,-1.03685227222,-1.15213283879,-1.22323609443,-0.784922264222,-0.956189863155,-0.786192405505,1.42448328374,1.01546851901,0.625931087037,1.5603395751,1.82782980013,-0.778899767247,-0.787527664044,0.151967437922,0.244571413549,1.63630584209,1.90426767204,1.50119676711,1.44614038868,3.83869608563,3.68213941956,-0.688405530599,-0.962261685846,-1.3985132466,-1.48158335601,-1.36774457859,3.11509809976,3.20145408165,1.66094721759,1.72747857406,0.601096228029,0.793747937775,0.0518067800135,-0.023995315774,0.326566922594,0.971106186227,1.30257443391,1.13356266269,-1.53727124322,2.68897157331,2.60231016969,3.16387657158,3.00082598453,3.10373314017,2.92844436547,2.85481736472,3.89876643038,-0.0361175165889,-0.199292669235,-0.263582709924,-0.313640943589,2.23000348396,2.40453454764,2.94252108588,2.88410511533,4.65906104804,-1.50862969316,-0.624328978866,-0.379284602795,0.136376804354,0.478483106896,0.545430844609,0.288013293243,0.297813293728,1.12726316592,1.41083317729,2.07712552284,1.8881072705,3.60932550961,3.36554549869,3.44382667866,3.69681232148,3.9233724526,4.08898861767,4.18461600917,-0.823341875558,-0.840767160841,-0.1686657108,-0.25395300564,-0.410208045204,-0.831207409441,-0.66379863812,-0.546562904832,-0.866031234745,4.50115287374,4.38150490816,4.68134619831,-1.56025542802,-1.44453481533,-1.4263910316,-0.948129849994,-1.02276893737,4.20569145034,4.11144237937,3.93137236624,4.1203233827,4.5516028652,4.40456821546,4.65183387708,4.60612501294,4.34342431258,4.44271297558,3.79838553014,3.86438577859,2.93649127591,2.99368205233,3.14422896414,3.73985387172,3.66561319498,4.403514793,-0.63482506635,-0.570652346497,1.96540432745,1.89300229567,1.86370187554,1.66402297435,1.88864950231,2.07936895242,2.66814277518,2.76752888127,0.22978027942,0.423915081812,0.682069637054,0.812087863168,0.467477359034,0.527251818838,0.280102616627,0.199462083245,0.514456237608,0.442639976718,0.433178848604,0.278857398993,0.209563191116,0.296567157691,0.0470280027431,0.205056844662,-0.190889463105,0.234021374966,0.220874767174,0.390384867859,-0.0541666553725,0.0109349217212,1.29421229937,0.968767285324,0.703499349572,0.529884700702,0.711574335669,0.796938677949,0.533972097744,0.448145691724,1.41232818553,4.27557202393,4.5780605498,3.92212964984,4.13854303376,3.85544725333,3.96063420487,4.45478988144,4.57927220951,4.44165399155,4.57666155712,2.46700475182,3.70611967315,3.33417783727,3.61455334211,3.44576673129,3.71379514823,3.54744827303,3.39290946941,3.60382603869,3.21392463529,3.23983594077,4.03012793321,4.4008242198,4.23358853084,3.95895414116,4.39654836058,4.15233339889,3.52804416946,3.3583250748,4.57855267445,4.46634264953,3.68679125539,4.09226279736,2.29437806227,2.40263137297,2.42136691789,2.61182346885,2.7579361343,2.6942941403,-0.0515576004261,-0.441423159555,-0.143410341807,-0.317789181651,-0.0909872633567,0.0130777875215,-0.465005775075,-0.547734726266,-0.375195568882,-0.148788093063,1.45699095261,1.68032124545,2.3490005665,2.38056839044,2.44645906181,3.8582771236,3.94849591509,3.69505415559,3.85859502752,3.59947056109,3.43780475179,3.68894621092,3.61771507026,3.36045358883,3.44989142829,-1.27465141613,-1.11946920972,-0.992644517625,-1.06290050962,-1.25902851931,-0.745539909745,-0.517708842462,-0.476936513068,-0.591714752689,-0.666926953687,-0.778805134969,-1.05006471464,-1.19716631209,-0.927287748597,-1.22000553298,-0.949186479929,-1.09582479067,4.22659748348,4.59642991522,4.43833062907,4.11595888822,3.5636897925,3.58868181186,3.40741731843,3.25923448804,2.48975625871,2.67483831377,2.32688176607,2.33797582139,2.03030197517,4.49782943037,4.55744195121,4.13849332495,4.27324831799,-1.26981907913,-1.02958796615,-1.44417960967,-1.35308933686,-0.820012678235,-0.909103968521,2.13866510858,2.22979382122,2.39358360541,2.47574703556,1.84788837149,2.15033055432,1.9627035981,1.94041362928,0.158729246705,0.0246872833503,0.195508723414,0.271046067853,-0.030925743913,-0.0864323733494,1.10579132549,0.960982580816,1.22000476067,1.65490840088,1.5564405091,1.35590924202,0.966784189508,0.738822605206,0.69232155951,1.18718019161,-0.729864010367,-0.701157454783,-0.273792064013,-0.234271968516,-0.378206453491,-0.549177468287,-0.555847263842,-0.426606211557,-0.0529160495018,-0.13109764163,-0.259967587619,-0.345798780264,-0.550599923544,-0.547114290947,0.794569446828,0.67108652452,0.738039153187,0.979922320882,1.35386554554,1.29438565282,1.23727018664,1.06640171106,1.08783369873,0.982242842493,1.36854555179,1.42006294332,1.58641163313,1.47088556883,1.62794705717,1.68936573855,4.07165987851,4.17981333022,4.15185609817,4.33175366093,4.31043686019,4.70002858749,-1.46300795453,-1.32555302809,4.70817953218,2.63784589704,2.89230859788,2.73184678687,2.9543395849,2.68795870798,2.6213022856,2.85129965886,2.70795558138,2.94072487906,2.86610758503,1.68676685763,1.56032717634,1.52833448263,1.98973596441,2.15496912228,2.23033779162,3.40447628604,3.40144280014,3.05113015897,3.2290986715,2.38125717074,2.44236412773,3.34760958725,3.38073004416,2.21955299147,2.21745625451,2.12528374601,2.39407330714,2.40778344334,2.48972267473,0.918253772112,0.777605240312,0.681601383135,0.753512919597,0.957832210892,1.64516785318,1.50216296014,1.22305437473,-1.44286561188,-1.2556631844,-1.41227329364,-1.31595026923,-1.13338656686,-1.15904240957,1.86550912598,2.04703799761,2.18285309119,2.134022404,2.76158631682,2.97991358575,2.59698272757,2.68500622191,3.14919709708,3.06219485542,1.00909268071,1.2555633819,1.16736648247,1.19429187601,1.03684533286,0.941636681709,0.956757715176,1.39815897235,0.514195987453,0.436231460551,1.57552687306,1.85308094522,1.98379325327,1.70770498523,1.75204248748,1.91639528377\n    \n};\n\n\nconst int NST410 = 410;\nconst double AREA410[NST410] = {\n    0.0299129824597,0.0307320240289,0.0309153261461,0.0299696286292,0.0319534183687,0.0294691475196,0.030023485182,0.0303026642568,0.0308562736467,0.0298191461686,0.0311331473389,0.030612002736,0.0307907608038,0.0319426978796,0.0309633508536,0.0306973265997,0.0301794958262,0.0305731407362,0.0311035896528,0.0308408536944,0.0309598498266,0.0309392594887,0.0318102128804,0.0309601109884,0.0299417024093,0.0304855959924,0.0313938609109,0.0302074249079,0.0306246022142,0.0318000823921,0.029998410983,0.0297420498881,0.0310306646117,0.0304270638651,0.0320652589326,0.0297625264347,0.0290255316998,0.0316085515414,0.0296115775697,0.0325747085322,0.0295452794979,0.0302729706974,0.0286061602708,0.0310961935849,0.0308494547391,0.0310935787133,0.0312587957644,0.0309131648657,0.0316958038622,0.0303013470206,0.0312245227667,0.030272519866,0.0309710741949,0.0303570951594,0.030893364684,0.0299311903796,0.0309800310946,0.0302318339392,0.0303718081532,0.0297274128963,0.0308124659236,0.0300514293267,0.0317377640383,0.0308039356793,0.0308764943404,0.0300617889642,0.0314358583758,0.029765762832,0.0316050043829,0.0304439199212,0.0301702717785,0.0312268294637,0.0303051984036,0.0307298918567,0.0306690421719,0.0306571926243,0.0304507918055,0.0302169963709,0.0316185008885,0.0314752830163,0.0315320734599,0.030902351624,0.0311638658398,0.0307933033015,0.0304434756777,0.0299480034398,0.0292859714715,0.0308249397375,0.0304907401372,0.0292091807266,0.0295931275008,0.0313919805121,0.0301248316218,0.0305154385749,0.0317658060508,0.0317187173641,0.0301446816851,0.0318398401571,0.0301952898698,0.0303795504453,0.0316384004301,0.0321805731835,0.0294645187812,0.0320609447599,0.0290485445939,0.0317996897304,0.0292303503153,0.02965929487,0.0332441387196,0.0309247859261,0.030816087616,0.0309984490658,0.0308799605778,0.0308306155927,0.0310129302208,0.030939213497,0.0319028673155,0.0291278976722,0.0294659816308,0.0300411609629,0.0315313513416,0.0308916659388,0.0306849685504,0.0315292465398,0.0298874496687,0.0306026312778,0.030470431744,0.030236039873,0.0295412956344,0.0317172390244,0.0292569707308,0.0322127054158,0.0297760026204,0.0316530681698,0.0294459780912,0.0310295751943,0.0306835665842,0.0311185578547,0.030069126698,0.0297826622024,0.0296354513127,0.030052286933,0.0295516536586,0.0316589367617,0.030056586263,0.0312072025853,0.0305813024071,0.0309921633675,0.0301914266726,0.030020424037,0.0316523873737,0.0295361647593,0.0295354979509,0.0299823489676,0.0319934365347,0.0291096087685,0.029923329285,0.031364411003,0.0316809046034,0.0316222272502,0.0301206268156,0.0298315945889,0.0291388312107,0.0299357224509,0.0312065035374,0.0302518564518,0.0301406174903,0.0308157270653,0.0308016705586,0.0306678812575,0.0302485507401,0.030792207603,0.029930570345,0.0311923445463,0.0301768036651,0.0298608399015,0.0295616841075,0.0313346099056,0.0301475700746,0.0312543461898,0.030176680937,0.0314060305622,0.0309896311177,0.0305024650262,0.0292679273851,0.0310759052233,0.0307527669866,0.0316129079446,0.030965034841,0.0313615577728,0.030669334324,0.0316084260038,0.0308816976349,0.0303145889298,0.0313658887989,0.0302449343788,0.0303395279741,0.0312519695258,0.0314380679724,0.0301170433977,0.0306686669286,0.0311095918031,0.0297020132732,0.0304355910579,0.0310651613969,0.0311802663682,0.0312936431483,0.0321310877235,0.0307458684122,0.0307460898388,0.0309950219528,0.0301877309588,0.0302151325138,0.0298547396812,0.0315109254777,0.0295627515854,0.0317062929798,0.0300104451315,0.0306010981062,0.0313361122384,0.0302241548151,0.0294489936194,0.0297907436981,0.0296230594953,0.0296774361447,0.0294817024298,0.0315989105478,0.0304229551128,0.0310896515175,0.0304268939309,0.0294646882226,0.0297483700194,0.0298926457858,0.0298403537265,0.0315778618945,0.0296071357864,0.0305690333738,0.0320621713813,0.0309874780337,0.0300963605887,0.0311910002725,0.0315032172952,0.0301617286562,0.0313181522078,0.0303186449238,0.0304486253852,0.0313165487644,0.0298299854575,0.0314776121318,0.030076500068,0.0300246910773,0.031328761322,0.0304409717027,0.0300274109162,0.0308390895042,0.0319496844781,0.0301255915414,0.0310881160854,0.0290815572054,0.0299847138934,0.0319559816127,0.0295477924393,0.0322169961222,0.0318203226009,0.0298167547018,0.0314530549827,0.0293415855739,0.0323375971545,0.0314010899119,0.0303801069701,0.0312816502259,0.0305253300803,0.0309496656844,0.030987357797,0.0299486991471,0.0311547833035,0.029439104906,0.0333708611358,0.0303004975337,0.03163179902,0.0300136282978,0.0311251878812,0.0309338554975,0.0298105010126,0.0317726110973,0.0298309022899,0.0299813436874,0.0313329131616,0.031225375632,0.0307152065066,0.0299870375341,0.0315669213318,0.0310069265204,0.0309555520175,0.0316736587337,0.0308349894336,0.0310102490455,0.0289813978977,0.0316981825778,0.0309273367437,0.0304082390044,0.0307878414466,0.0315796535706,0.0302561284289,0.0310037229568,0.0294316475516,0.0305244715365,0.0310213598203,0.0317998225601,0.0290569696971,0.0307081204205,0.0298625794615,0.0299585137948,0.0318982180957,0.0315864556666,0.0301196045651,0.0315333371795,0.0298539002282,0.0299331680664,0.0317187209179,0.0300509596342,0.0315098266571,0.0302036543084,0.0289282082694,0.0310956719125,0.030220154128,0.028631660003,0.0362327530196,0.0292309971705,0.030327431934,0.0295290340108,0.030852334661,0.029162731607,0.0309210964491,0.0310809249519,0.0302558775191,0.0308360409944,0.0311435721574,0.0303166325831,0.0306422912407,0.0295869804444,0.0297213858306,0.0317589796641,0.0312282754206,0.0310160578024,0.0310517379724,0.029765275103,0.0315166675688,0.0300934575307,0.0298321656549,0.0294149519979,0.0322128408144,0.0293060949493,0.0309099154567,0.0303071328887,0.0294991792814,0.0315871440393,0.0303780832523,0.0302445763883,0.030846907202,0.0309176055113,0.0311104505302,0.0302080288306,0.031145593049,0.030316578581,0.0314758520407,0.0311292945133,0.0304988808147,0.0309548730729,0.0304143310228,0.0295977924873,0.0315857518118,0.0296928972381,0.0314958394983,0.031716920616,0.0300696857084,0.0303309583233,0.0303121284687,0.0312810114906,0.0315982596429,0.0308405763402,0.0300214065963,0.0318272065916,0.0316762774121,0.0298907050404,0.0297776567928,0.029740321963,0.0314617719273,0.0313560539584,0.0286992805296,0.0313396541203,0.0308761220603,0.0310023500675,0.0303950501978,0.0297332552768,0.031305875983,0.0310950726996,0.0305543074724,0.0308583861412,0.0303963961715,0.0296806831435,0.0296144496653,0.030064767368,0.0298234603482,0.0297687222342,0.0349891118046,0.0291406322241,0.0330612047124,0.032101109251,0.0292492977166\n    \n};\nconst double PHI410[NST410] = {\n    2.86032076088,2.33372649195,2.39270650727,2.17095880499,2.09986127386,1.02546867271,1.31024657447,2.43342238627,1.79173846398,1.67890464929,1.75969664006,1.79066124912,1.91494110116,0.940336286455,2.71633997831,3.01004070729,2.45121661339,2.17449420354,1.52170772702,1.44658789936,1.43989679015,1.28039580316,1.60664897957,1.52788360362,1.56677129665,1.67129742604,1.61871858361,1.3686354429,1.54549369364,0.159865658595,0.313580782093,1.06012197017,0.901333984109,1.87878751841,1.90522748918,1.78942225291,1.65150105672,1.4468275287,1.47514798151,1.62088384372,1.73274664103,2.04048637281,2.02272377701,2.12668505097,2.15905869536,2.04180505934,2.39886561389,2.05047036351,2.31502816775,2.30842062779,2.17078754355,2.17976891404,2.05159221692,2.18253866454,2.05645186456,1.91929834065,1.92167664196,1.92719541175,1.98682771428,1.30542245346,1.1965294465,0.990888512766,1.02335571649,1.28305430709,1.34361701782,1.04199312027,1.12329549588,1.1075895259,1.24880738526,1.04164891818,1.01047609631,1.11303874269,2.13683172728,2.18119778581,2.24122454453,1.08939577757,1.08442051042,1.21503939567,1.21302939772,0.868688364089,1.78025211423,1.35851471797,1.49560277147,1.65113822451,1.64189595405,0.815755771957,0.775321193943,0.62250022047,0.505908519742,0.591882436589,0.511871970109,2.96908501772,2.86029685699,3.12233937018,2.58524880699,2.82968904342,2.29771051721,2.30970730926,2.57846828059,2.60264656895,2.42183801432,2.04579608098,1.76407021605,1.53051429101,1.61007600946,1.98549794995,1.95106622016,2.20124737343,2.06416156303,2.82424636905,2.87758578499,2.73642511102,2.66202131483,2.58864950348,2.55813856605,2.30405760619,2.44321254244,2.30294925327,2.46938397395,2.33851918771,2.33250364119,2.27371148644,2.30356110305,2.16788779118,1.34725854119,1.48620501891,1.52254367021,1.6840409908,1.69219249659,1.76865422593,1.49125774717,1.35784639397,1.53459223248,1.44833327653,1.05927565021,1.20141289931,1.21528406666,1.28694545706,1.750910695,2.02548566745,0.938702270104,1.51745693156,1.49838904852,1.75574775773,1.67878755971,1.6521757848,1.87996186966,1.74156396616,1.70810159259,1.94252021576,1.85949302424,1.17640623434,1.47052483641,0.692359418911,0.416952937705,0.558595939514,1.07248614926,1.20333748758,0.587623466462,0.438231935823,0.437977567753,0.711778020402,0.344752303086,0.115929713487,0.277764026521,0.352624656664,0.32910431125,0.26732464865,0.171614199479,0.115784841481,0.700331743091,0.853359054507,0.954043719559,0.97701118931,0.573109797194,0.638162615434,0.819487646631,0.689858184402,0.868451285806,0.79870585411,0.187170842199,0.435804837495,0.19287283202,0.338741970375,2.05947195081,2.19567519982,2.08142021113,2.19669779558,1.90736238483,1.89936524079,1.7698278445,1.64429462509,1.52070765204,1.55843375738,1.69814029353,1.81053256344,1.27183824824,1.37124557466,1.11847555078,1.33272717142,1.4436417391,1.51835388626,1.25182248952,1.37232578074,1.24033621456,1.16486146607,1.27271356475,1.45843964241,0.571738791938,0.40757593493,0.415501128845,1.77586403461,1.9056628229,1.62274614611,1.6283154483,1.74752758687,1.8823656108,2.71475360797,2.86886594386,2.97805015447,2.86426663926,2.68685634736,2.57816515396,2.58185138086,2.43835753015,2.43560274084,2.57776380917,2.70821935241,2.64988247059,2.42199369959,2.17494561374,2.33142270802,2.39281631121,2.16984404889,2.13485673842,2.23886007895,2.25789834379,2.61588181369,2.84837345076,2.69682958716,2.90518625826,2.07239273483,2.1183641812,1.81970842796,1.91929346215,1.86006245857,2.00496526063,1.24434125022,1.1053338667,0.99838662459,1.05224344179,1.20574955441,1.29221263235,1.51508007184,1.45274210653,1.42205653022,1.28605069723,1.30071504872,1.22674830347,1.0434215701,0.971027628111,0.819912727133,0.737554522105,0.966851885093,0.815721143614,0.831662049988,1.03608503295,0.913827687826,0.985553506438,1.06877780185,1.20789112877,1.09160233489,1.41027209908,1.36584087325,0.883036264924,0.803075042113,0.843906749739,0.701121062774,0.426625739452,0.335214366591,0.438416497734,0.58539159372,0.643598563992,0.576329712452,1.16537399472,1.12898260656,1.31310233373,1.41109599521,1.37081490686,1.23648894642,1.07085281343,1.18646701952,0.910652799269,0.869993236248,1.41606550702,1.64844716705,1.78191231395,1.52089109507,1.53329872144,1.58140856926,1.62046635907,1.8710972439,1.77008084311,1.3967733419,1.65401137452,1.5222646758,1.88623183657,1.98914959028,1.58909886389,1.73091366924,1.48698678609,1.5462871771,0.557980728209,0.598721761386,0.852512473315,0.753025665938,1.96630264159,2.10354115251,1.90359995656,2.43919181086,2.31774348015,2.32697875859,2.58920210037,2.00976605618,2.15946094387,2.18856206256,2.0551523511,1.88930233392,1.87127353752,2.77209248523,2.64466499815,2.70386153142,2.55246513174,2.45530368961,2.48833698897,0.684572258814,0.580040026572,0.419923108273,0.395615811816,0.893912057552,0.783783426547,0.531938716315,0.658400460486,0.57929318602,0.743935139391,0.823540196158,1.83390183046,1.69443023228,1.67337979615,1.79451062183,1.95724805639,1.93892667581,1.63063917333,1.73934106297,1.68395976576,1.52575120389,1.41571472184,1.46631404084,1.93144906618,2.02083275799,1.94579115163,1.77443313635,1.78623096889,1.70297033884,0.692973286945,0.685539930114,0.953129174065,0.834007320236,0.949800272898,0.824351613896,2.20172683575,2.46103018463,2.34899988027,2.59562178478,2.65512258381,2.71775677311,2.45312997451,2.49591691427,2.41137277295,0.809827908358,0.646774873439,0.586382804607,0.708095373932,1.00828756864,1.16314895266,1.28199249991,1.34663421161,1.29742609087,1.13758170663,1.04461891843,2.14953935689,2.00532184898,1.9211780992,1.98922021007,2.23015294664,2.3858308471,2.16701864746,2.24487050544,2.48845378061,2.40713436997,0.850596996327,0.895690384084,0.992532055449,1.06150034358,1.2687454915,1.12762840857,1.37909861426,1.37803186572,1.25560947976,1.12094523251\n    \n};\nconst double THETA410[NST410] = {\n    2.9837431682,2.40619034425,2.22899884976,2.42010341856,2.58663159374,0.546173902807,0.459303944287,-1.19926142814,4.64525696982,3.22218314742,3.35305779021,-1.47924550431,-1.37335227363,-1.16364269032,3.16628180938,1.47113485741,1.80737706364,1.59779274007,3.23667428927,3.11894734528,3.38156741894,3.41466447476,1.9775203127,2.97236961748,2.12504050577,2.23972689492,2.38800577825,-0.313903964523,-0.0613687240967,-1.20979759425,-1.37534818035,-0.257667629864,-0.234262057121,0.223223666592,0.0464667477931,-0.0253571411146,0.0525034027599,0.408708052318,0.251725550113,0.201840848801,0.2981795423,1.49382565854,0.770526172409,3.83138885464,4.01737549118,4.14334492753,-0.989347477331,-1.42773274503,-1.56645728587,-1.35098156629,-1.29804197514,4.40225995612,4.32212963588,4.5979970417,4.67724606112,4.41350389169,4.57610792061,3.63191851812,3.7934796215,-0.874486432958,-1.00534131185,-0.843684635847,-0.991982900044,-0.436195981689,-0.58553107827,-0.551467000488,-0.415787464308,-0.711174017051,-0.725138765228,-1.33323413534,-1.5267506627,4.62649777664,-1.11695032666,-0.802933252336,-0.967712082397,4.44825450759,4.09313513269,4.35386783935,4.18987890142,4.70595950785,4.33715603018,4.42574715021,4.34519376357,4.56181022241,4.41412932318,-1.19811506142,-1.41843667112,-1.4409217119,3.03751118982,3.30025202345,3.55530838217,3.49385280963,3.94967078424,4.11284020147,-1.28218775526,4.48663284158,4.07079843171,4.2828233929,2.67104042966,2.97703121514,2.6066668063,-0.0408785400594,-0.188738661302,-0.330484620367,-0.208569590762,0.31295858409,0.485780597538,0.557752786762,0.603360611145,2.46875287155,1.99307981006,1.77978504681,2.43767879992,1.94154791133,2.19771331714,1.26627760785,1.57126676712,1.4921811997,0.529636514932,0.675516037982,0.847251990341,2.0751022269,1.89169324847,1.77310490149,4.12135280356,4.20199639138,3.49370496065,3.47978886769,3.73608980021,3.61474420429,3.90203038447,3.95912420423,3.74822461155,3.64088062052,3.00935967827,3.29410868677,3.01665108375,3.14330243889,1.9366117951,1.83489976472,2.24523534636,1.71333493716,1.86838302354,3.08309368249,2.95089072321,2.66541832444,2.77132245975,2.79821111015,2.51117037093,2.61151713934,2.48424339177,2.46344329912,2.40753120408,-1.03993378389,-1.07380052783,-1.18414254499,0.370454929481,0.332537105597,0.930381105672,1.36484088638,1.00444268735,1.09408375646,3.06125645611,4.02570699056,3.90362657227,3.51911835928,1.63579231562,2.64974253733,1.4802757776,2.48976391893,0.00631890973032,-0.0621313854439,0.241665333323,0.0715307907066,0.434533727164,0.692519882795,0.340149737001,0.25209458791,0.546832003581,0.704541395524,-0.269847296575,0.336281223415,0.609758088592,0.664797667101,1.32246797858,1.20407739687,0.941611890906,0.975090244391,1.55376957215,1.72278039353,1.77464628131,1.66205129429,0.673000437169,0.515266180308,0.455888623009,0.552226272664,0.621699470701,0.72804810764,0.6707670527,0.886911433177,0.990529283631,4.63827621666,4.6763928616,4.57320052346,-1.13417972566,-1.29326944021,-1.43581421169,-0.874630115274,4.60090507231,4.19074055572,4.54105766048,4.18976706276,4.09079395475,3.98574711964,4.12579830656,3.88536730907,3.92400185883,-1.08144023816,-0.290511882345,-0.820827123749,-1.25936770734,4.49180226533,4.25526114653,-1.5481600191,4.61910718462,4.37594720296,3.99595388968,3.79139365232,3.50180177545,3.92089988917,2.72917891691,2.75725059561,0.326836490266,0.061637847864,0.241113156561,0.367719237259,-0.630930341958,0.561325108482,0.240508187947,0.270014844351,0.83323044637,2.29057986339,2.12058634389,2.20814767838,2.32842389524,2.05097937878,2.00376003751,3.85301652415,3.91564952066,3.79515277094,3.61687850876,3.573473061,3.68692532452,2.69400297011,2.84849752905,2.55992787647,2.58566650989,2.8688869731,2.73991197203,3.31130679346,3.1510325754,3.12367006737,3.30873979215,3.4751653386,3.50083746244,2.1066233498,2.53625440167,2.44033623093,2.83761504966,2.71841340211,2.31182510476,2.19542843845,2.14820743173,2.28367382195,-0.562700309747,-0.404974781129,-0.761281514658,-0.826055078013,-0.688424454078,-0.342849972729,-0.0398856530142,-0.149918692181,-0.402056482521,-0.631052468897,-0.131060922325,0.029177567406,-0.165234928549,-0.0353736418988,0.12970143971,0.158367140469,0.836655818508,0.944414062797,0.871238230225,1.03500255277,1.15531628835,1.50112584152,1.44657557236,1.40683892637,1.25430429326,0.934130871011,0.774161214433,0.81308151595,0.714173791146,-1.39369572818,-1.41777913391,-1.49788245662,-1.20964706014,-1.08623975124,-0.470461819171,-0.492603095082,-0.602579754255,-0.743167604197,4.04843620241,3.7727978306,3.85912253365,3.70959373106,2.91636491195,2.89795367643,3.07599725811,0.113013014077,-0.019508304982,-0.227601017478,0.0319228052722,-0.511166801204,-0.485264935045,-0.303035638948,-0.205522373214,-0.27826556799,-0.407280971984,1.06079234054,0.864916590209,1.43570405353,1.3943511884,1.16922809852,0.976403114042,2.15221008327,1.95174820299,1.99047191255,2.34933827555,1.91695489667,2.54376658603,2.52610927858,2.40556807116,2.83236219908,2.91367901679,2.76978973616,0.974456114832,1.03468809016,1.19146959722,1.28776983139,1.06656874791,1.22518423123,-1.26724795699,-1.16716523887,-1.01728743961,-1.00356833879,-1.13046658536,-1.24573257005,-0.93020441913,-0.798053957018,-0.654608654186,-0.90493450921,-0.641110061501,-0.759141559473,4.42083903302,4.18022981895,4.37294003615,4.49584265768,4.17705155655,4.06963370333,3.04705239164,3.10730372207,2.97901981667,-0.259553798775,-0.777883808031,-0.456091642304,-0.365173237141,-0.789733181177,-0.595945756372,1.74538133682,1.72889404508,1.47935483665,1.32201064106,1.15854682783,1.10539210198,1.21074709542,1.89000281801,2.02331941304,2.03993631294,1.90344784004,3.20965884447,3.50017530502,3.35615913998,3.21759067844,3.68799735986,3.71814115311,3.51566298287,3.36291510277,3.52223961718,3.33994765108,1.40522714442,1.59189348192,1.3143232722,1.59242212643,1.73821907671,1.73373541572,1.62661719098,1.47892946459,1.38349580531,1.43165172092\n    \n};\n\nconst int NST596 = 596;\nconst double AREA596[NST596] = {\n    0.0207418467233,0.021201950136,0.0218438654825,0.0217732927175,0.0221207973113,0.0201131008696,0.0203860530267,0.0209902556852,0.020508597112,0.0218105913961,0.0204798331283,0.0210479448125,0.02036959506,0.0211322163962,0.0213817298685,0.0212982959931,0.0211217750273,0.0213850109292,0.020441456647,0.0205447429056,0.0219673609047,0.020860910778,0.021640949461,0.0214113338626,0.0211028499496,0.0212318416095,0.0212735269132,0.0207806229143,0.0211703877243,0.0212552478533,0.0214813169977,0.0208663003006,0.0202065805811,0.0206930988773,0.0214787050283,0.0213728115806,0.0218785045406,0.0214461603282,0.020130378224,0.0221161865295,0.0206987692975,0.0215051539397,0.0217491700731,0.0218951799977,0.0202090651616,0.0212423274931,0.0212391153069,0.0210388860234,0.0250662157312,0.0213480891419,0.0209255316251,0.0218232953501,0.0198289127188,0.0211601384457,0.0211627300811,0.021664693345,0.0213748932633,0.0215525188781,0.0211633010486,0.0206516372086,0.0219123000519,0.0208046258256,0.021658860122,0.0207221290126,0.0202920103278,0.020156315849,0.0202087370285,0.0214632032687,0.0213915763145,0.0211147062941,0.0210158042314,0.0216834294434,0.021338371248,0.0201909128189,0.0199554707913,0.0208187807307,0.0211547302151,0.0220061689785,0.0203837657494,0.0219940685475,0.0202900844403,0.0201199840093,0.0208795601221,0.0203938627049,0.021844015416,0.0206600737104,0.021817609096,0.0216010885642,0.0205298903038,0.0206330582633,0.0206093977008,0.021830366254,0.0206959033684,0.0216138154863,0.0206170192284,0.0212564080689,0.0201126658455,0.0200899196234,0.0226014849418,0.0217698984954,0.0205815538055,0.0211516633538,0.0210243457752,0.0214192369633,0.0216518618548,0.0207378311901,0.0209453332386,0.0214574934937,0.0208411742325,0.0207048053762,0.021078135076,0.02133997033,0.0204749535507,0.0201349976221,0.0218844023492,0.0209210009207,0.0208031238994,0.0217649794521,0.0212197605595,0.021199123532,0.0211494931332,0.0211906041195,0.0209108484973,0.0220244088015,0.0205549852569,0.0210821893261,0.0216330502495,0.0211959439981,0.020785633518,0.0212415104961,0.0210603219935,0.0204974688762,0.0217780214715,0.0210977116444,0.0209727440926,0.0206269628012,0.0213499633192,0.0209569096193,0.020883064314,0.021359164437,0.0211564392895,0.0210635684069,0.0214502824725,0.0205188699007,0.0207409241181,0.020862684862,0.0209327555545,0.0214778383725,0.021133414418,0.0215579482905,0.0205679573015,0.0213455797792,0.0210311442513,0.02176870169,0.0212796765678,0.020648454705,0.0212926480985,0.0214391372035,0.0210703172497,0.0212942298901,0.0216945918486,0.0214948855878,0.0215030640078,0.0206321077822,0.020672778477,0.020863357779,0.0205406948095,0.0218141607065,0.0206741354147,0.0215479350273,0.0204634808851,0.0210982262792,0.0218407679983,0.0204241267557,0.0201218967815,0.0209788822376,0.0214824040415,0.0205377664873,0.0210534639703,0.0210424842152,0.0208641443671,0.0209028667192,0.0210234407763,0.021312051589,0.0213605961855,0.0207039405113,0.0206243579197,0.0216704010074,0.0206150023132,0.0218558898549,0.020540428959,0.0202912538541,0.0197941548104,0.0211369718065,0.0210663427603,0.0214144861662,0.021762901545,0.0212048291399,0.020967545314,0.0211800934732,0.021295652311,0.0211299021758,0.0212704070806,0.0208869366673,0.0208667709585,0.020874990895,0.0208121331011,0.0216065995881,0.0205298406802,0.0204564447156,0.0217263149416,0.0212079768379,0.0212815653793,0.0210296140663,0.0207588696376,0.0209189556201,0.0216919732292,0.0207489553815,0.0204067954922,0.0199048115822,0.0222200350448,0.0210116854451,0.0206142499328,0.021480588325,0.0203331983656,0.0211010497958,0.0212957771598,0.0214545984486,0.0205081747172,0.020670705248,0.0218712109857,0.0202615956576,0.0210518589325,0.0215918127593,0.0208831652388,0.0214032432461,0.0204362182033,0.0204195855663,0.0204010500316,0.0203964802398,0.0203040067456,0.0208515000933,0.0216447433341,0.0206809532901,0.0207117940677,0.0217202290861,0.0207456385724,0.0206832131156,0.0211327509732,0.0213444473463,0.0203826996685,0.0207446529033,0.0215719389215,0.0208455729093,0.0214196866494,0.0216189994907,0.0206235925823,0.0210709321712,0.0213027052198,0.0205450346838,0.0217910718146,0.0219793032797,0.0199174139448,0.0206709073495,0.0206684633531,0.0212291318808,0.0209276238288,0.0209354947881,0.021401773734,0.0209562085105,0.0213820518862,0.0198772489766,0.0201865613677,0.0211656011542,0.0205032043417,0.0217070724081,0.0206238555889,0.0202134266703,0.0205688950833,0.0202517515533,0.0218209443672,0.0220253297558,0.0210282218702,0.0206997322882,0.0214847205408,0.0212776280569,0.0205355355379,0.0209160355809,0.0215674019675,0.0217692509908,0.0207424517256,0.020740239109,0.0217252031075,0.0217418184476,0.0207665444631,0.0214117606414,0.02076082615,0.0205668539531,0.0215773968024,0.0209563309619,0.0205222521903,0.0201094680781,0.0218945397651,0.020584560879,0.0215784303212,0.0204219732554,0.0217308761006,0.0210510947189,0.0211640791235,0.0210409537721,0.0211551008733,0.0207725436465,0.0212373659923,0.0207093437287,0.0215964723895,0.0206731039481,0.0212773129352,0.0209609086336,0.0207617080963,0.0215332578891,0.0204779698338,0.0221042642324,0.0203987454758,0.0211364550564,0.0208446299976,0.0214744723703,0.0211509771786,0.0206179633594,0.0206993067091,0.0217122546902,0.0213212831945,0.0213697630531,0.021160343545,0.0210859865135,0.0206737055882,0.0217485837519,0.0206492129257,0.0212086430471,0.0205661733939,0.0215401377881,0.0214003879581,0.0210941656594,0.0208482702189,0.0216781914942,0.0206135596337,0.0216214773855,0.020869202357,0.0216294182743,0.0210187622978,0.0214810750615,0.0202470277924,0.0203811679926,0.0206755235836,0.0200916795275,0.0205826106231,0.020647435137,0.0206270464162,0.0218247330784,0.0217406686409,0.0215379691107,0.020860560772,0.0215121206001,0.0207946391322,0.0210453083964,0.0199256756289,0.0205552689397,0.0207019061094,0.0205886031534,0.0216287631875,0.0218554154658,0.020914289404,0.0216577990755,0.0205827174499,0.0212875586897,0.0206481886434,0.0211711164459,0.0206416626521,0.0212780780056,0.0211721532959,0.0204958396871,0.0213795364774,0.0211044660566,0.0213737313942,0.0207482169846,0.021701924445,0.0218169045909,0.020519433254,0.0210417146416,0.021255816412,0.0207879312156,0.0213590407103,0.0207862458441,0.0211916453854,0.0208614177589,0.0216200644277,0.0209331523072,0.0213862200112,0.0210703024863,0.0208984280014,0.0208539361922,0.02147103059,0.0210185131117,0.0217308340556,0.0204100607272,0.0209035696298,0.0214267373132,0.0209170840114,0.0214929970712,0.0209165052665,0.021489043737,0.0210776649502,0.0206077855555,0.0218748449885,0.021420458558,0.0206790512642,0.0216794836784,0.0204290372996,0.0208208103773,0.0214789786871,0.0201466637669,0.020527888285,0.0241279987666,0.0201251888742,0.0223808542966,0.0203139167625,0.0209070284843,0.0210048080616,0.0198959841946,0.0217151707294,0.0216703558263,0.021132682549,0.0214869327192,0.0209047883715,0.0208992090286,0.0215024570862,0.0213456085762,0.02120245456,0.0211521498078,0.0212664669032,0.0212673630774,0.0211473536224,0.0209310071578,0.0215314716255,0.020753158517,0.0215357423812,0.0209789941711,0.02017676779,0.0214919485027,0.0206327062931,0.0216306666663,0.0206718581538,0.0206151395307,0.0217901117801,0.0213205767553,0.0208663776861,0.021520928087,0.0213464402254,0.0208890226034,0.0210999226799,0.0216172510852,0.0209980682253,0.0207048819924,0.0212552281529,0.021165862964,0.0210737448675,0.0219084479488,0.0222956338005,0.0207931697646,0.0212921134531,0.021574366499,0.0208468067836,0.0211513337799,0.0212569683059,0.0211979854134,0.0210773674546,0.0214227319763,0.0207131737504,0.0211462614827,0.0208396563777,0.0215929241589,0.0216281397662,0.0209438335553,0.0215317096111,0.0208612502321,0.0205627836141,0.0212451178947,0.021280055632,0.0212040622042,0.0207005302963,0.0211010354682,0.0208747133587,0.0213915039848,0.0215225967828,0.0208716893193,0.0209662750115,0.021436759915,0.021616190962,0.0204829080489,0.0206325737659,0.0209525565733,0.0214752511621,0.0215969292876,0.0206669558721,0.0214264086728,0.0210032192543,0.0211784562129,0.0212406854986,0.0210578282687,0.021259471401,0.0212096364106,0.020943567449,0.0214757926981,0.0212620737992,0.0212156499948,0.0212091701733,0.0210762892179,0.0213121779658,0.0213363160825,0.0211912982648,0.021478587737,0.0212518441258,0.0212314183645,0.0207420380776,0.0208971452902,0.0202564170097,0.0219377043065,0.0205765195728,0.0215231820683,0.0215188711339,0.0204393938766,0.0211922412605,0.0211131528247,0.0218709385654,0.0204602810333,0.0214750338543,0.0199850603361,0.0197490609137,0.0214542663378,0.0246783417771,0.0203614225401,0.0207453662452,0.0214009013249,0.0216755866297,0.0210389380799,0.0207319113075,0.0215000425062,0.0209189149183,0.0207654187421,0.0210151048441,0.0212658558966,0.0204502502357,0.0217864672063,0.0200870459681,0.0221297305001,0.0203834747527,0.0212064121188,0.0207772099139,0.0215381842121,0.0207661215139,0.0217436159641,0.0205544653071,0.0208710508032,0.0219432935451,0.0199727946134,0.021250582563,0.0208922263608,0.0212567810996,0.0206732708887,0.0218831289338,0.0205911140836,0.0218271630055,0.0202705368956,0.0216144684835,0.0203731334772,0.0239316064948,0.0201628944461,0.0212399528354,0.0204454294346,0.0217395388934,0.0202848437942,0.0220188291106,0.0205863305868,0.0202972819792,0.0198929738779,0.0222577525772,0.0214443509464,0.0210664386242,0.0210282946161,0.0215184330406,0.0215768636781,0.0209416712853,0.0206067920161,0.0204333384524,0.0203415871417,0.0207553169186,0.0202440030227\n    \n};\nconst double PHI596[NST596] = {\n    0.927308635196,1.17223773007,0.135468336317,1.05741612615,1.32038937752,1.66465784303,1.70893057935,1.43484509412,1.10079756977,1.22453630263,1.24813462008,1.5040410818,1.84697176124,2.12338680198,2.0959145497,2.12155649852,2.00647264025,1.99410637066,0.0592679544769,0.645809940063,0.546640132369,0.940838509277,0.86640676925,1.3510551601,1.22025803915,0.71876568094,0.664046356302,0.259510715902,0.848612419087,0.737360561062,0.472236816019,0.362100208226,0.180112497766,0.203064714574,0.676824085613,1.03923054871,0.18136951243,1.00725138174,0.590034517691,1.9615370692,1.80993384082,1.93706261575,1.71490381639,1.7859090614,1.69918284462,1.65481719744,1.77369837351,1.87672263031,1.39030098694,1.61458696233,1.64169591944,1.29203276599,1.39711493172,1.52191109952,1.5085672189,2.05913661908,1.91735250152,1.69024135211,2.02831580199,2.10416965819,1.56878344253,1.26015528606,1.07078022662,1.1279816039,1.12319889009,1.25304567614,1.28844466466,1.91723785972,2.44859873794,2.51544060603,2.04528324352,2.2968738542,3.02440464313,2.47625996875,2.41774510566,2.49928626914,1.52749296368,1.54873639593,1.46572178021,1.34557017459,1.76389107753,1.83737409868,2.06925833522,1.87407934867,1.97713324676,1.8302091547,1.90064796121,2.09066605858,2.02837384034,1.69816110091,1.51245708636,1.63937577753,1.61502367188,1.56735324783,1.7458676255,2.08765877503,2.07282830467,2.28278281406,2.17881325602,1.99371099554,1.90621961779,1.99593876559,1.916057961,2.01294117197,2.17567078045,2.21654769162,2.26208969778,2.3739694296,1.33255726362,1.45474805378,1.5484378779,1.5606235702,1.57965121719,1.36872216743,1.46786435639,1.72231032269,1.98220834893,2.62960529741,1.90065428686,1.68451475109,1.78537136755,1.67853617063,2.15030842727,1.94393119961,2.07073953137,2.15481460982,2.10164707067,2.01129772785,1.9976032002,1.90358656384,1.79429693692,1.78018750433,1.87617044682,2.21890609597,2.23307670705,2.67699173078,2.79992603144,2.22824394655,1.29315403939,1.27109025853,1.08889500987,1.20200283323,1.06195451183,1.153678577,1.3416054499,1.58891940604,1.36868451877,1.49092887314,0.0915570524386,0.183685651144,0.314278725683,0.86250102226,0.766886262642,0.635281537212,1.04735371308,1.14997950096,0.828579374829,0.898688928243,1.00977523724,0.887131555667,1.0867516822,1.06885110658,0.309632103689,0.280623246416,0.936894493636,0.708333096157,0.708602141411,0.590882748024,0.577216284341,0.69024249463,0.505799832108,0.55177228335,0.381800803117,0.309300759968,0.334314613661,0.567212414488,0.493703883805,0.379424985415,0.871125400772,0.76097525697,0.969596893893,0.959549972968,0.77055977096,0.833212337179,0.848353525934,0.973500403043,0.963089169481,1.02670386135,0.498660937099,0.396927778779,0.251105325216,0.80319268738,0.724561555135,0.513771658459,0.437733491888,0.638734942099,0.685428993658,0.504696601397,0.623981422711,0.776990047826,0.648024044681,0.71349701883,0.614190864826,0.808226626016,0.893732771338,0.951069140178,0.921063345107,0.863573946135,0.752991618773,0.717574310992,0.798625692952,1.87152712719,1.84057587097,1.72643037545,1.78108929644,1.98490345145,2.06567329338,2.17485670278,2.02197491382,2.22372014866,2.15140202226,1.51450018464,1.43720095925,1.41398636196,1.85890401228,1.73983041751,1.63713219457,1.87893572386,1.50544839222,1.39693946407,1.5278401354,1.57252876163,1.28790976641,1.27202198481,1.16438368818,1.0625299342,1.37107290288,1.51674190521,1.38821643748,1.57653923399,1.48777933343,2.06382267622,2.12027372582,2.24586202725,2.127534978,2.25452285575,2.31747854678,1.93891349473,1.76037222829,1.87277721304,1.69813461545,2.64659497544,2.68921075902,1.48833030877,1.33372225324,1.30761952766,1.14845325913,1.35624818055,1.27827149779,1.18331083378,1.10122085243,1.17900030943,1.18223489488,1.06386544641,1.07993699312,1.96388429039,1.86306528826,1.73314142841,1.74712027079,1.83096343245,1.94540941618,1.59356842917,1.48119361528,2.43681603994,2.25938164518,2.22621998999,2.3087867954,2.61063815049,2.83217224202,2.62441435305,2.70927335394,1.75417728012,1.62452658688,1.63370577942,1.73552241815,1.85946156249,1.86896496533,2.20450397756,2.27731802934,1.62358778232,1.65122072865,1.67887049206,1.59962223143,1.8304803862,1.9572855618,2.04151702545,1.78287623278,1.70919066936,1.80544405322,1.78508350375,1.66260997017,1.58931624217,1.56287101683,2.29298738408,2.20345366651,2.41323358595,2.43616208289,2.39475972512,2.4563135312,2.57335492589,2.46056950691,2.40297387107,2.59043714506,2.65292818691,1.31558836848,1.19406283579,1.41229915935,1.42590822501,1.29395646087,1.18582083611,1.69382081414,1.68097411628,1.56920004898,3.02306558142,2.88875378642,2.29640293316,2.26603818426,2.50483379707,2.30983077504,2.43011800431,2.26949879514,2.33370569881,2.44734398658,1.4187098469,1.40431725281,1.31946264485,1.21409023653,0.998648065889,1.16590426587,1.05279617395,1.66107064409,1.57003104711,1.26943832907,1.28885271189,1.1977819672,1.15263567897,2.0585474887,1.92864987689,1.81832943508,1.83592113691,1.91059272127,1.98583088928,1.7861040286,1.78051859097,1.90313848071,2.45875819903,2.58224185956,2.57868102125,2.45938562487,2.35147937695,2.34810732359,2.21182457675,2.0929817858,2.07617921272,2.50788786657,2.43428761752,2.30817721804,1.41029500107,1.43776097866,1.51118927056,1.56253889767,1.65999726543,1.63582141825,0.895105707717,0.978956034709,0.770253416156,0.748606419684,0.853455881001,0.958335449058,0.42068463239,0.192232486986,0.35289862603,0.245257764633,0.528044001432,0.358330989486,0.409711353116,0.306576926712,0.560789281633,0.680785241521,0.488286779265,0.559429891057,0.739098872622,0.68685450233,0.952798569047,0.96655238022,0.878171852936,0.761383044363,0.738793047334,0.743088023164,0.844061433185,0.842497165714,1.14041413183,1.05402917481,1.07904562028,0.921238014851,0.805393871357,0.800522251408,0.916674088641,1.0262154135,1.0259082706,0.569718570206,0.438401018781,0.375116925638,0.638434251958,0.546103808353,0.444733100574,0.462187790223,0.588943772343,0.682556245357,0.657730781891,0.943211767684,0.859753483586,0.894707631511,1.11221890117,1.00994973787,0.861887313178,0.992981754241,0.924353402862,0.830801834637,1.39213622076,1.162433267,1.21225043124,1.32770289771,1.3498598414,1.23610150381,1.1920716809,1.1278394493,1.21850067255,1.00672223449,0.979335549077,1.07450284114,1.86369982167,1.93628159398,1.87896862188,1.75120777298,2.5875014552,2.37459254074,2.47002458641,2.35278858261,2.98812894527,2.80869673361,3.02261482888,2.90147461501,1.54384195569,1.67678194846,1.73268012225,1.46664458702,2.08920706635,2.00534918278,2.21385017428,2.03682332389,1.84200293396,1.88682979339,1.80340649409,1.68496040635,2.62535542863,2.6850292006,2.67970959725,2.79636516836,2.88611626364,2.82167490488,2.16874242596,2.25325476065,2.38185148281,2.4190760753,1.38809649486,1.46954053667,1.25691203598,1.81167175945,1.86474524605,1.88917127666,1.99581172906,2.07535013989,2.02084211958,2.33029015352,2.32294556125,2.12066990547,2.21962536552,2.11074552484,2.20220723443,2.85666578985,2.79099146919,2.60110334416,2.50328145448,2.18948942708,2.29644183791,2.08807274775,2.3048767833,2.98588711506,2.85286150862,2.78300760919,3.02711897235,2.55239219611,2.54303199278,2.63796799064,2.76380204277,2.76691686901,2.65198793939,1.46431119668,1.24695213502,1.59821314193,1.58700514664,1.49072494038,1.37177926276,1.36144813171,1.46931736771,1.51293764122,1.48595822776,1.57779130712,1.69502860809,1.72341563414,1.6328566329,1.05390777585,0.932667055512,0.993842529601,1.0797271028,0.869979961476,0.833353813385,2.07646794907,2.19687320273,1.97414417676,1.96363899339,2.19991760484,2.08975480348,2.17555821767,2.2882120739,2.24742814291,2.15426784757,2.38790790046,2.40639227873,0.538077008797,0.631225402822,0.422228872601,0.628222082825,0.416209440876,0.528550831863,1.13864813018,1.15161823658,1.35486689724,1.24539169369,1.36818621353,1.27137000654,1.23845806969,1.20402774578,1.29959644404,1.42276056265,1.36229246333,1.4519442854,1.65735288484,1.71380525508,1.63957082912,1.52488266536,1.45170184657,1.51060882605,1.2127048809,1.04443055156,1.08244313242,1.49432777388,1.41897362257,1.29385003972,1.44145787585,2.48881784416,2.60685680106,2.40161133997,2.43821633847,2.57685003169,2.66292409342,2.68404775747,2.81271423494,1.12706303651,1.24584686201,1.31877252054,1.2691968498,1.07235716226,1.14192911622,2.49158242461,2.60141674888,2.40571833322,2.61738875048,2.41269786557,2.51233223378,2.71863379079,2.71099725468,2.81569399097,2.91819854702,2.83164819419\n    \n};\nconst double THETA596[NST596] = {\n    0.942166650706,3.38270065383,-1.38324398335,0.95126396881,0.924889777421,4.20768646732,4.08253296898,3.36332080138,-0.147165166233,-0.183287958206,-0.305129582509,4.62709647551,-0.682661821125,-0.311362009689,-0.602972544308,-0.161453305656,-0.0835551115401,0.0585071118829,3.66532822379,1.88279984208,2.03282129726,1.21491764993,1.08086842032,3.25943847034,3.26479344633,-0.401699704169,-0.604266455985,4.6631874612,-0.748743757699,-0.782473117505,-1.44708461237,-1.33931209036,-0.661667753935,0.0398201680366,-0.193540568921,3.39249004666,3.4418055836,2.7155949106,2.24042140875,2.38885030346,2.21018271106,2.25221407652,2.29424011849,3.0861253808,3.00408438604,0.108633471278,0.0499597187199,0.121564349669,0.625399919123,0.482806843248,0.233655909523,0.41168224511,0.472673210181,0.286608965454,0.409207651665,1.48074284619,1.73053666346,1.69573482814,1.74696907144,1.62101324052,1.45383811552,1.05256313408,1.19661012197,1.07133678341,0.83658440182,0.828056186922,0.693164970837,0.992859142747,1.22631704653,1.4108147285,0.97347655481,0.925180812847,0.353156641877,0.710735317192,0.894834289538,1.05064747986,4.37277236102,4.24151590502,4.14605255542,4.1810943955,4.28855722984,4.07684782235,4.13176331483,4.20923559538,4.24774537283,3.85579019302,3.97083980735,3.86593046879,3.98527283293,3.86768624601,4.01749689159,3.98801993379,3.23924325853,3.35013886625,3.21386856361,2.601432199,2.44828765381,2.41650772218,2.35231569017,3.1309939598,3.03781274312,2.67791773611,2.89357337115,2.83022504949,4.17231610174,3.87922302005,4.04174297366,4.0850487193,-0.0985988918593,-0.148550524925,0.0417873601359,-0.082425574488,-0.338572477038,-0.348922809732,-0.277950187959,-1.51415959024,4.39718222935,-0.797717723181,-0.14844903619,-0.271778441176,-0.079590853584,-0.143980215312,-1.15511144846,-0.772731071124,-0.741332362732,-0.863469394927,-1.00970208933,-0.368176516371,-0.508431680263,-0.285338543159,-0.34288195808,-0.47421758381,-0.555201038914,-0.558739338552,-0.404533841776,-0.540502672583,-0.416867852903,-0.0892498711617,2.51533710068,2.3860739948,2.5920087106,2.61983956211,2.43966642072,2.33976620406,2.17224028451,2.25438588996,2.29838795862,2.33823888897,1.43579507908,0.617248909072,0.769242425713,1.88263340251,1.96964624733,1.67479561292,2.86736761948,3.14338246202,-0.407179256243,-0.246141195771,-0.264697498435,-0.574619615943,-0.00890028630505,0.274693162423,4.26488042193,3.82390996429,-0.877512031451,4.71034500711,-0.981405554344,-1.05161775702,-1.28724620687,-1.38121936263,-0.879947494733,-0.626290844289,-0.970831010681,-0.701191065661,-0.0133436557362,-0.153313779763,-0.38078166915,-0.362547335015,-0.0746059394592,-0.0307915942779,0.0424641397471,0.193120486923,3.13365233496,3.27790093015,2.98480490999,2.99749883472,3.2682427248,3.13692038268,3.82805169583,3.66172002489,2.9547003915,4.36203421266,4.01346201736,4.09594122771,4.3090981507,4.15202420972,4.3509423384,4.56230129378,4.54556535167,3.44198754955,3.49316835416,3.81932762169,3.7092842598,2.82654211243,2.68953353891,2.39264827747,2.22658479093,2.5169270199,2.46902106701,2.27582156265,2.15837895665,1.98543227109,1.84919293271,1.82663673728,2.08416034649,2.0181480119,1.89923967717,1.92781238402,2.15950551995,2.08338708356,2.19678525244,2.46061541763,2.6707438166,2.54900083387,2.46750602828,2.4166782628,2.49817037254,2.60434325838,2.91778803413,3.1466702388,3.14017114237,3.03184583678,1.90986884607,1.76952936461,1.68245789259,1.74227223403,1.71148331965,1.55706505787,1.57686509425,1.67628871643,1.77046034866,1.22378194836,1.09225714179,1.08169806947,1.35473328817,1.36590720088,1.22400283947,1.46807623342,1.57925898556,1.59103272554,1.45664221266,1.43459033093,1.69069357955,1.34482533777,1.14652712841,1.49260608941,1.304453633,1.36281557837,1.27326116994,1.53817361764,1.44031728909,0.482164634099,0.611451886891,0.414423196331,0.70011060315,0.341108715152,0.25522673031,0.435180587961,0.307539472121,0.523035717125,0.482618890432,0.603794799959,0.681879235106,1.55905710599,1.80324262839,1.64671480641,1.51919421152,0.727253857829,1.08257422191,0.988837180355,1.17635954512,4.40750806623,4.57783902848,4.45686411073,4.6505248455,4.59898007193,4.47567004021,3.60115359216,3.73982693563,3.77294922402,3.44644848509,3.65623057528,3.56060527402,3.30318010658,3.26971434631,3.37097634581,3.42561653785,2.8715962836,2.81117111754,2.67271293814,2.61848713648,2.81768647603,2.7017377705,2.59510816009,2.68341685697,2.66288977063,2.84994310311,4.2797891496,3.92836032411,3.96639207036,3.56875497964,3.74242709182,3.54467551237,3.76040593277,0.0228471244221,0.0745335954451,0.216880328481,0.0959639686318,0.277124805812,0.209491022372,-1.27431341349,-1.14504671959,-1.08460999022,-0.640258235264,-0.689652309689,-1.34543319613,-1.17152782385,-0.817151897286,-0.681809183062,-0.648722092328,-0.85008267055,-1.01294950807,-1.01394599418,4.41336759874,4.54768522907,4.31827980526,4.36357271727,-0.563729085371,-0.418270152025,-0.415355455772,-0.537139635537,-0.473615790368,-1.5510970432,4.59595223251,4.50752772785,-1.49120953492,-1.27123605921,-1.23860192491,-1.32258254716,-1.44037969477,-0.894344055174,-1.00835655397,-0.940925645516,-1.07559025351,-1.12001010458,-0.453789755773,-0.142771455063,-0.376072289818,-0.0637875513013,-0.338521484967,-0.16890205839,0.0708710408952,0.138064248105,0.288380878719,0.28900015955,0.11636050839,0.167052160474,1.96885078624,2.0910876843,1.88904708894,2.13252340758,2.04714174164,1.92496232914,1.36438812952,1.48042203422,1.40613238481,1.58960279508,1.70326731385,1.64121721687,1.9798515592,1.79180431836,2.29667338614,2.39049277286,1.54364741966,1.11059780052,1.67227161401,1.47683806041,0.880642020027,0.906647967026,1.09959407439,1.30458890633,1.08598623416,1.26437056014,0.502169138451,0.661599807837,0.786448369898,0.748253078021,0.557469934084,0.165271893982,0.267695807803,0.441022681213,-1.34872374505,-0.830761496991,-0.689033483327,-1.03705178075,-1.10338292186,-1.27871351265,-1.35647750201,-1.27687186706,-1.12840389923,3.33417089643,3.36514819961,3.07937714478,3.13613446413,2.47352830113,2.56590517522,2.87401638075,2.94684784232,2.79082599578,2.60103767671,3.98817826799,4.08197596277,4.23368053432,4.26004755931,4.31034039129,3.55940699713,3.52247980455,3.82009521199,3.72863127309,2.90680108656,2.88155993157,3.01891006806,3.02747269169,2.77497577767,2.75873641201,1.97579976765,2.1961910909,2.11746358528,2.13099375888,1.97178774801,1.89729010331,1.11422224888,1.22595817899,1.34391690119,1.34449986583,1.855812299,1.86930971872,1.75985493761,2.06198689137,2.56051189306,1.80820764722,1.57570886327,1.47892917614,1.24067761752,1.23785099956,1.12535202715,1.13273915953,0.836708851421,0.72748798996,0.800354631366,0.584334337775,0.88818621058,0.758491273378,0.65692059193,0.689024007194,0.300427875329,0.552141367835,0.0388867663026,-0.0240635887624,0.319663981721,0.648451030262,3.33752434769,3.45434290601,3.420716635,3.23979046904,3.47720454988,3.57767084782,3.49065700566,3.63926720877,3.52230739372,3.74397411389,3.5020215477,3.61404948153,3.73955510939,2.95098937369,3.1265355504,2.92369249423,2.85604659568,3.08300891616,3.18629599942,2.52272344321,2.19421028886,4.21520814231,4.36244826435,4.34022058273,4.40053146308,4.44702840236,4.57963109672,3.34166535287,3.43015122778,3.78247848403,4.35722839269,2.93463194955,3.16038523069,3.30829869922,3.19110274741,2.8330157206,2.76233948274,-1.15322057106,-1.2814220533,-1.46351382519,-1.34027663676,-1.53159649267,-1.47606426926,-1.34720778326,-1.28067310012,-0.748787740104,-0.887188442883,-0.949109280674,-0.875346543428,-0.738242146328,-0.674426211138,-1.57058296085,-1.51018907365,4.46900926057,4.56260023574,4.51001886465,4.66546269891,-1.40339816253,-1.45759983139,4.6696583501,-1.4865551245,4.66793203835,4.59843676426,0.386892690768,0.334059986786,0.648302633044,0.543430988711,0.589700717244,0.426065917068,0.102513973691,0.251092536867,0.209962288395,0.47873117451,0.537985835074,0.63324356258,-1.06588278828,-0.92533355019,-1.08463699436,-1.14494325717,-0.953116848898,-0.874490373056,-0.533783836784,-0.656790995705,-0.746111914193,-0.687103261233,-0.484796149008,-0.548386552536,1.0248379848,0.908619754585,0.811713598516,1.02909876933,0.915929351729,0.814523124011,3.61090137068,3.76462315677,3.63173759794,3.79626877298,3.69698343429,3.71612666425,3.92073623606,2.50227178405,2.50700190269,2.3425366353,2.17151994731,2.09012622756,2.26210097079,-1.0380309576,-1.07601660617,3.87295252589,3.84644419872,3.95140275465,4.08349085645,4.00916822824,4.11501264731,-1.22420686005,-1.26095360885,-1.39316114186,-1.5324408412,4.68332334588,4.5931182881,4.64323635889,4.32257938939,4.12555656594,4.45342550152,-1.44552975174\n    \n};\n\nconst int NST610 = 610;\nconst double AREA610[NST610] = {\n    0.0207677809853,0.0210747716537,0.0206091699039,0.0202578252173,0.0205397776616,0.0208527439447,0.0204630724571,0.0202294860657,0.0211561416615,0.0208033913821,0.0203323388747,0.0207810350004,0.0201480008343,0.0207052864878,0.0201120589415,0.0209451480664,0.0210908208621,0.0202013109455,0.0202710790103,0.0198777876135,0.0204812884273,0.0207593996023,0.0200147292643,0.0212932341567,0.021096945645,0.0214408282899,0.0200136973805,0.0210245443352,0.0198200285058,0.0212877706111,0.0200790222703,0.021452264645,0.0199379861363,0.020167979164,0.0205696499718,0.0206985368707,0.0211151411668,0.0205850863504,0.0207789836225,0.0205298880571,0.0208127736778,0.0209681371254,0.0199979928524,0.0209388128051,0.0202202916767,0.0209872541622,0.0208005760986,0.020230019936,0.0215132975506,0.0217323457722,0.0197455743928,0.0212135724651,0.0207577934218,0.0216187682097,0.0206784467359,0.0207860186924,0.0206847693345,0.0205780056078,0.0197579690832,0.0205719089737,0.0202199839176,0.021021302343,0.021120411638,0.0202225190974,0.0211576908584,0.0201075424013,0.0207103506101,0.0201087631136,0.0197949082029,0.0209709982348,0.0209827957181,0.02036733974,0.0209037230934,0.0205685450034,0.0203517263263,0.0207273507237,0.0195916055915,0.0199571313253,0.0203819402201,0.0208645285704,0.0205965025775,0.0202542528056,0.0205370861334,0.0203596738013,0.02094749365,0.0217125525441,0.0213846782792,0.02052319795,0.0211185788681,0.0198649415453,0.0201815815903,0.0199398488516,0.0211138073257,0.0204495676098,0.0211191185607,0.0205033575853,0.0205200332826,0.020833532471,0.020246286595,0.0203979433534,0.0203659189971,0.020606632014,0.0214317616603,0.0201989686938,0.0200817179531,0.0212298675508,0.0201136179691,0.0201317506146,0.0206914254676,0.0208415969594,0.0202063651925,0.020833201621,0.0205263726401,0.0205747221332,0.0206394111703,0.0208350460189,0.0212321643216,0.0202240365946,0.0206545582027,0.020190716284,0.0200094273577,0.0212074245061,0.0197691968293,0.0205565414405,0.0197055424117,0.020711956953,0.0206797087767,0.0206186442765,0.0208007818629,0.0205247106702,0.0207111392458,0.020725284602,0.020480552254,0.0211625893784,0.0195386286093,0.0211519863253,0.0197128047629,0.0202273835674,0.0202047579594,0.0203208795583,0.019739467431,0.0215982191363,0.0196654034222,0.0201485881531,0.0211445386391,0.0200442622492,0.0211734950505,0.0205832043275,0.020836785601,0.0213581327057,0.0204323620222,0.0208987179756,0.0204790674308,0.020130410291,0.0206673443003,0.0207877083504,0.0200989093916,0.0216079560069,0.0214773474932,0.0197501114136,0.0202233666005,0.0202013166838,0.0197383368064,0.0211840570267,0.0205136246519,0.0206972855286,0.0207736490056,0.0204176477329,0.0210434561922,0.0204711977185,0.020114074343,0.0208471539924,0.020978753829,0.0205369020701,0.0207725199346,0.0205444065391,0.0207589815213,0.0202307966568,0.0195419632385,0.0204204345962,0.0206656335852,0.0209527818157,0.0208177003511,0.0199573864588,0.0202978202047,0.0207628403831,0.0202639721097,0.0209540914584,0.0202714140192,0.0210754239368,0.0212469427623,0.0203570200233,0.019857533814,0.0209830145473,0.0207637042707,0.0196728979126,0.0198073962493,0.02006660405,0.0197416120091,0.0195740297756,0.0196261494201,0.020229727592,0.0212181812832,0.0201752239799,0.0211803704674,0.0208838417914,0.0205445549365,0.0207762080046,0.0202533913974,0.0202140646281,0.021265597738,0.0203788595481,0.0205917287887,0.0206907921961,0.0208154282249,0.020945747319,0.0204887060478,0.019871419065,0.0198605125801,0.0217273408472,0.0216211102463,0.0210504837365,0.0210501781573,0.0197270939691,0.0195250695634,0.0200162680775,0.0204510478465,0.0243187525108,0.0209255229038,0.0210403494687,0.0214424283582,0.0206763495356,0.0204510167464,0.020978691857,0.020583768876,0.0206669512471,0.0203851399995,0.0209917316227,0.0207516610839,0.0207834772767,0.0208166730208,0.0205525482725,0.0198088365618,0.0199580969325,0.0210805094521,0.0192013758928,0.0213602017428,0.0210412335808,0.0207105460142,0.0203677967355,0.0210124223874,0.0209302435051,0.0212493077573,0.0203492770895,0.0202396271736,0.021123862135,0.0202305903818,0.021360940537,0.0200575836881,0.0208285591074,0.0208306019255,0.0206716299851,0.0201077964663,0.0198585799525,0.0213834297647,0.0207628160738,0.020743833842,0.0203987724037,0.0199648900088,0.0194389621524,0.0208250415798,0.020316055762,0.0206017642622,0.0206598789725,0.0209051329652,0.0208935023614,0.0205449602764,0.0206467371462,0.0207422468755,0.0208339222749,0.0209085188037,0.0204773037695,0.021306590143,0.0199808186429,0.0200091422145,0.0209172772676,0.0207283057056,0.0207602102717,0.0205278657199,0.0200383700445,0.0212220882174,0.0206905498621,0.0199379671655,0.0207559707219,0.0201280511878,0.0205533097965,0.0209000200442,0.0206762022234,0.0206588402945,0.0210463945502,0.021170589322,0.0209693590827,0.0204521176925,0.0197846162293,0.0216484684536,0.019903595555,0.0212136781588,0.0210708338398,0.0202580551751,0.0202691283126,0.0208748044818,0.0204232801379,0.0211161579187,0.0201373209773,0.020281696002,0.0211509484449,0.0202601497503,0.0208567909441,0.0204836964306,0.0198263322801,0.0204558719385,0.0197410675109,0.0205151695693,0.0202712751334,0.0208597901564,0.0207495297018,0.0205422959797,0.0201979951866,0.0209923246393,0.0202599855663,0.0203830865515,0.0211059848123,0.0200303803447,0.0208695149119,0.0205340823972,0.0198878896049,0.0215134500697,0.0206537543955,0.0205975833692,0.0208799837687,0.0198227119369,0.0207569142564,0.0208393598745,0.0209755078832,0.0209406795577,0.0197347754326,0.0206593556303,0.0200904858568,0.0212562508914,0.0209104137449,0.0205867015422,0.020473204272,0.0204571859174,0.0210939654758,0.0200602341252,0.0214342366515,0.0200757331095,0.0204358112019,0.0210200350047,0.0201748988299,0.0209688577725,0.0204457555492,0.0208974388539,0.0201563592242,0.0207627718587,0.0204538611723,0.0205631100086,0.0208730265267,0.0209614725605,0.0204111679549,0.0209755083781,0.0211594383275,0.0199089196407,0.021084943424,0.0206378682675,0.0208398933831,0.0206776458511,0.0207972426889,0.0202841542237,0.0204068395292,0.0209401485954,0.0202360991787,0.0210297831909,0.0208576762501,0.0203298993587,0.0203563918648,0.0207833075771,0.020548436593,0.0206704279946,0.0205484390304,0.0205880833422,0.0212276435483,0.020059184785,0.0207613323509,0.020031427011,0.0211267695341,0.020603962477,0.0205308698238,0.0203819774943,0.0202053594794,0.0213270609766,0.0214505643033,0.0198948634082,0.0200325145314,0.019829840412,0.0217554219822,0.0195834220421,0.0207659617522,0.0205662601955,0.0203999052423,0.0210043914624,0.0204489909096,0.020480794405,0.0203432777588,0.0209652242638,0.0198875060224,0.0200355383978,0.0209435877185,0.0206432298678,0.0199368834822,0.0204768324409,0.0217483101761,0.0213372062138,0.0209666539851,0.020342554713,0.019701060683,0.0235601257866,0.0192260138993,0.0212639225113,0.0211697715548,0.019478636594,0.0205406138861,0.0202444087725,0.0206478365624,0.0198250249118,0.0204185486423,0.0193123478314,0.0196666072187,0.0207062564945,0.019839372865,0.0204684687973,0.0210628000022,0.0217206918316,0.0199853945992,0.0208940582496,0.019667086008,0.0214693595792,0.0198803469916,0.0218769240936,0.0197369493672,0.021884165128,0.0195495716083,0.0206848062904,0.021133764553,0.0199957320606,0.0206724076389,0.0205042704551,0.0202670421371,0.020767808742,0.0205654833093,0.0204498976664,0.020487701463,0.0206559225101,0.0207699350036,0.0202156621039,0.0206961699766,0.0205359575984,0.01986758743,0.0208874222736,0.0199452352383,0.0210503615528,0.0201479712523,0.0207813477425,0.0198789827323,0.0215166257097,0.021347645334,0.0201265113859,0.0203563446896,0.0203001359391,0.0210678407563,0.0208210535571,0.0202868249086,0.0207593827972,0.0197371019207,0.0235967689255,0.0197761981649,0.0195295395137,0.0211507385832,0.020368764299,0.0208947369053,0.0206677598262,0.0207717397921,0.0205953314611,0.0198766419811,0.0199480440754,0.0198525122671,0.0200256565752,0.0203788271989,0.0213684182668,0.0199075587116,0.0208316081808,0.0200142595241,0.0203203552612,0.0212417459695,0.0206954341639,0.0206292664443,0.0204977186702,0.0208965571213,0.0204902433428,0.0208052573214,0.0204973194858,0.0210297361066,0.0210884626248,0.020258359635,0.0208779052136,0.0205639358696,0.021300896362,0.0202022720987,0.021337731307,0.0214280813621,0.0198553549236,0.0200765741969,0.0204233967932,0.0209063319843,0.0209989902649,0.0208428296366,0.0207910711173,0.0207791107456,0.0208936929356,0.0206430697669,0.0204225917445,0.0209360436975,0.0206412913202,0.0205810864582,0.0200491941537,0.0204246077246,0.0208931457016,0.0206446279579,0.0206979299649,0.0207686197975,0.0199156126268,0.0202003031917,0.0201460395894,0.0212866527455,0.0208687331107,0.0203629400377,0.0197238512867,0.0198248659962,0.0216122032373,0.0196667346172,0.0216351835577,0.0198751176212,0.0236738971628,0.0235865030156,0.0197483021216,0.0212928698826,0.0192627614097,0.0210165027523,0.0199527187486,0.0198792651072,0.0195232541811,0.0210632950245,0.0210985221489,0.0202050852748,0.0212557881969,0.0202189558768,0.0214966055934,0.020056678105,0.0203376598271,0.0200853842472,0.0212835381204,0.0202334733981,0.021226234922,0.0212365558467,0.0202479012644,0.0210561103714,0.0207954644707,0.0203630700312,0.020947033514,0.0203204821206,0.02036514406,0.0211067339343,0.0201329214731,0.0206659972286,0.0202804154226,0.0209609935376,0.0198134447597,0.0210206585773,0.0210325104228,0.0203062909512,0.0203715860388,0.0210913303159,0.0203295814304,0.0211895079005,0.0197921873649,0.0239928221003,0.0211639097295,0.0200065452928,0.0204551572947,0.0204125202809,0.0209083314026,0.0209348966447,0.0205305141602,0.0207815422081,0.0191459534476,0.0202138831417,0.0202082915799,0.0212284811819,0.0207502192498,0.0206348163171\n    \n};\nconst double PHI610[NST610] = {\n    2.21952532407,3.01343352775,0.897630559855,1.61520420282,1.74522092105,2.00987728528,2.1412421905,1.16778397103,1.09892627514,2.18348606156,2.26959697265,2.34841052678,2.52267386018,2.3978365385,2.96014849684,2.76643039601,2.51414653526,2.45196927347,2.31904263708,2.54038563976,2.41183100409,2.18803441764,2.05669682601,1.98982830951,2.25127582425,1.98689332948,1.86163286982,1.333631015,1.12670860208,1.13421745806,1.24912288031,1.237116683,1.07173207786,1.67315825307,1.535310841,2.06216853637,1.70541531647,1.76269253276,1.58422529833,0.99969922401,0.954700618919,0.684607003294,0.963944176724,1.79917136044,1.92931886261,1.52962185376,1.39316068331,1.30490518751,2.11416633378,2.24509046249,2.29738856006,1.15697005621,0.746797447024,0.894675686221,1.04002837904,1.31111109075,1.39242030643,1.36178164394,1.29416342476,1.52566847469,1.53708541517,1.29198541372,1.47405514638,1.35046557406,0.619015169665,0.548281707905,0.970694009713,0.722269069445,0.930274379874,0.813348608119,1.86604906283,1.83700869954,2.26397354458,2.26515365905,1.92514892447,2.0450042799,2.78639553561,2.66002681956,2.58723035955,2.46946480895,2.41324989154,2.21375589151,2.20625149702,2.32481899214,2.32274396605,2.98369591557,2.85539083338,2.95964325123,2.85400946644,3.08809113343,2.96619450932,3.04155763263,2.87021295344,2.75861862588,2.0526547544,2.15493888218,2.21587990227,2.13439241671,2.0195491859,2.16180625351,1.77762052557,2.584861623,2.63274406668,2.77756910218,2.75306560625,2.84057808096,1.90189348015,2.06299399409,2.25513496116,2.36770118671,2.04604271481,1.96762600055,2.01481499242,2.14426138403,2.23095235261,2.17910923435,1.80313095455,1.86196528819,1.83503684705,1.94586925011,1.82143297313,1.93298957076,1.72324080101,1.72426803643,1.50180010752,1.59973765658,1.78765981452,1.82482532746,1.67898429649,1.69866352423,1.35340015315,1.43113681663,1.61840565328,1.52137145657,1.65071881673,1.17160293048,1.10949242969,1.04046719457,0.937282477855,1.55981063051,1.33985242759,1.45350813828,1.54937232913,1.6787241884,1.66175511431,1.47398023417,1.37196584957,1.60165902645,1.63038718581,0.310707511686,0.56320192588,0.652996937474,0.542709780149,1.39087347169,1.54526054717,1.52125985553,1.13121973764,1.38287443074,1.18082457156,1.3049641751,0.726775223232,0.862991649823,0.911696221279,0.855595166164,0.769249226377,0.781798705614,0.886805326661,0.90350507267,1.01167034781,1.00702847161,0.612329829059,0.958394404278,0.828767988201,0.751459892857,1.10649403105,1.18186547999,1.13332889683,1.00605564839,1.47021528485,0.656247564457,1.57201890194,1.75307213095,1.71091016104,1.66973043128,1.91388374556,2.04477526603,2.05455832832,2.11886373766,1.92293184519,1.85548873426,1.6630813559,1.72629794071,2.2440461889,2.1833249823,2.30952283853,2.33373054585,1.17218771761,1.10288963942,1.09491546547,0.975513075498,0.96887643967,1.28395934001,1.35698907357,1.48708419617,1.54314978721,1.53887548637,1.35494576923,1.47591620625,1.47732487222,1.28892945951,1.34875032787,1.1698073104,1.21611431103,1.3453279259,1.42938528534,1.6588971491,1.53314328107,1.28015533789,1.1538262014,1.14465967779,1.07809011398,0.563067549599,0.609639912712,0.543130089441,0.415775817181,0.411651378782,0.614977577376,0.854336364956,0.52946699754,0.595602828191,0.788657760086,0.791220826608,2.39655289129,2.28132544013,1.91482691302,1.93254110181,2.04956446316,2.15311642083,2.22704409648,2.29522925469,2.11635542719,2.2342963493,1.68782401841,1.75623679648,1.87590477894,1.69891804009,1.81424861058,2.06147169262,2.18783645933,2.1035547222,2.02061511016,2.42887497073,2.59551550273,2.42293759079,2.46853052796,2.39180494735,2.64333740923,2.71379651224,2.84018476156,2.68182013699,2.79827202255,2.89441455582,2.63807547842,2.42811572022,2.51059945936,2.65945680497,2.54906560292,2.44743704221,2.76957172508,2.63690928887,2.33409678415,2.41059040592,2.36254649334,2.47466580177,1.84137847248,2.03304246082,1.97105986261,1.95820366442,1.82440971322,1.76873764156,2.24245456058,2.36577710417,2.65220916985,2.58632206066,2.57177305093,2.44772783929,2.38995590508,2.45241598187,1.55763887655,2.2631804396,2.14310239519,2.31657309377,2.35174130684,2.18678465612,2.10145610297,1.84044507433,1.79048269896,1.63799052709,1.76264149954,1.63374249052,1.85920532744,1.68481380178,1.79957151876,1.46137523684,1.58360941228,1.63846751123,1.39085549194,1.56355792932,1.43866545729,1.12515126247,1.21927007451,0.954954432838,0.880781299832,0.949486009553,1.09018231836,1.15107339984,1.08330505489,1.24488543514,1.14456949292,1.27243639391,1.18585745337,1.04395002587,1.05738820359,0.835720372086,0.902023593689,0.956880694613,0.991834034483,0.939978248373,0.832802805409,0.741023768606,0.788814855301,0.919617768842,0.980895955717,1.8176720056,1.69538726379,1.65406272263,1.73591592493,1.62327825901,1.60039010086,1.51807920159,1.48151583554,1.52446718669,1.44641979286,1.47767781896,1.35354531645,1.79571094037,1.98103232936,1.77272698083,1.8529127317,1.74886321291,1.7703672803,1.97019807832,1.84864608296,2.11151892819,2.00540426264,1.90011723171,1.89154820941,2.10625430623,1.99200527873,0.412227370278,0.629627327279,0.544732677449,0.42783966912,0.197943720333,0.484529082218,0.663247605226,0.821733902716,0.789693097142,0.73397499556,0.60467040689,0.563313322844,1.21745742845,1.34462386812,1.44838373848,1.42881574196,0.751679540216,0.741401790404,0.639284808011,0.532358269131,0.668733428268,0.552791419602,0.739207381778,0.928299112848,0.734376281313,0.804555509082,0.981278150476,0.917620440348,0.79621534409,1.28895466849,1.34850795007,1.16369807121,1.22302021179,1.43744176907,1.32208719263,0.854805263589,0.964478343097,1.0853723733,1.09720355942,1.65934759998,1.72282410334,1.72488732408,1.85297612127,2.03775832413,1.84420591256,1.90910083918,1.02907893259,1.137473864,1.59004461992,1.66520709296,1.21538983682,1.2551563112,1.38503941201,1.46578606924,1.71395394193,1.83926387302,1.88395158569,1.64801004592,1.69901366446,1.80939145739,1.46773340745,1.27801581569,1.33873661696,1.52795284164,1.35001605925,1.47051388846,0.231654642865,0.348834996557,0.442345129032,0.425934500519,0.766124599661,0.731126294658,0.684853656659,0.612574757495,0.774372786005,0.729069416562,0.904304146395,0.957694076649,0.851811071157,0.564829705878,0.66842389588,0.574566857925,0.670860647568,0.35375893517,0.448573133939,0.440416045803,0.0191838562154,0.105171124273,0.151539059523,0.225980790345,0.885897004888,1.00858497506,1.09863866461,0.885168939797,1.0035141187,1.09388100297,2.08391393569,2.20102885455,1.99222437206,2.01647335172,2.2310959758,2.13741994046,2.02903849075,2.10871886194,1.90660470814,1.86555953149,2.05766936044,1.93794517461,2.39843033838,2.27018200903,2.3062619488,2.22604606615,2.61869011616,2.66757037761,2.43546351344,2.48577581516,2.56344266497,2.4705178441,2.54503648121,2.6392495522,2.54176797903,2.65035788522,2.76457332316,2.7733021529,2.92869989187,2.81219546012,2.90288421776,2.69776148655,2.55633833611,2.43501429751,2.34871804385,2.36774328285,2.47896067389,2.58225810628,2.03886646849,2.16249044725,1.97310853225,2.1710144602,2.09651149359,2.00972537077,2.04894731313,2.4683296591,2.37462074724,2.43343338801,2.25983045304,2.30585801808,2.22440630726,1.52103165662,1.60270040944,1.93392974257,1.80944528919,1.72935321058,1.97873817299,0.739132314825,0.559224373235,0.688387692354,0.771189964139,0.525234035571,0.624824605802,1.38379597919,1.17580433451,1.27327104924,1.39774534326,1.1883187146,1.29925545895,1.11426513263,1.24249620892,1.31833042099,1.27179288854,1.14779312828,1.0653713946,0.438923369322,0.426899108782,0.356522948793,0.235041390253,1.18434376603,1.14469299508,1.27822349075,1.29239897447,1.0278569061,1.05406422383,1.29808244756,1.24257883294,1.14555081669,1.18196696898,0.98671361282,1.10641119874,1.64114716106,1.58402672335,1.469392808,1.3932295097,1.37341081467,1.50130725274,1.60573940538,2.13848486065,2.05126179979,1.97965694635,2.10143579593,1.89077696229,1.92697481152,1.22824736418,1.29651614707,1.416706291,0.515658945242,0.35474578614,0.483384283585,0.554518880167,0.349820441174,0.237282395564,0.416765919829,0.198636409592,0.306870215424,0.403234692323,2.78379662078,2.69277489213,2.7228110432,2.60090714106,2.53494232903,2.5721551665,1.77430587952,1.89859299635,1.86335546414,1.94206705517,0.325528678543,0.367695542536,0.124999495654,0.208552508491,0.217216362387,0.323402603372,0.873533354297,0.940486035267,0.994766146661,1.02428011001,0.777312820686,0.815377585252,1.25173447314,1.29519960809,1.45616189105,1.34525948313,1.6944852406,1.48985697151,1.56835421726,1.53932604556,1.66348230551,1.74060456345,1.43663411869,1.68094353437,1.62434584298,1.5041488292,1.61404439502,1.49028188634\n    \n};\nconst double THETA610[NST610] = {\n    3.00171533486,-1.51117382415,1.93952130626,3.15768192007,3.15351167662,3.13237969827,3.13152494603,4.7104685808,4.61239594785,0.876367372945,0.993357516514,2.99683230823,3.18870117641,3.15449576157,-0.7442240367,-0.276533438966,4.21169381737,4.0365796956,4.02174833729,-0.619629220133,-0.68245386358,-1.21218665501,-1.19602402981,-1.07970035288,4.14086939619,-1.31523021497,-1.31752169683,2.64480633801,1.51685896095,1.77818504783,1.70546762395,1.57703351823,0.910494451192,1.52813456869,0.596949353496,0.914786182609,1.63659091341,1.86040572573,2.23474885148,2.34012723917,2.21446064149,1.41191035076,0.829626589487,3.25594714729,3.24968437187,3.25733822414,3.24724119597,3.34594605907,3.87898766097,3.86864479159,3.74100807729,3.78321796614,3.55702149035,3.83114450832,-0.772513459317,-0.372055737915,-0.474685686676,-0.25976366254,4.68988018701,4.6995882346,4.01691407275,4.01346921962,4.1278901026,4.12973854426,-0.675735703034,-0.516374720434,4.64656489931,4.70587857947,-1.48352865462,-1.4405082396,1.77380920473,1.65745006704,2.3946434288,2.22540091415,1.55288887448,1.56528285518,1.32575099481,1.27078691717,1.48598935664,1.46877209947,1.28632523038,0.4049868702,0.723975283866,0.489241461064,0.660764018837,0.881670449079,1.0303373342,0.0152976699607,0.072552128006,3.60729081984,3.22657781153,1.60665986709,3.55835957598,3.36296680289,2.35965468766,2.45495036602,2.72584339894,2.61014899628,2.65346922385,2.87503511449,2.72398499101,4.65671602601,-0.756689491506,-1.2379559156,-0.638525341651,-0.889909418581,-0.0277595225412,-0.214431634368,-1.09729511138,-0.833132513859,-0.958881574049,-0.848003148966,-0.724555336462,-0.71125380324,-0.83450361475,-0.959220475724,-1.20442498974,-1.08598857869,2.33647337337,2.4132584487,2.59552372666,2.55412145179,2.51591253923,2.3959598921,2.42947334487,2.3447020852,1.97476735318,2.21240004315,2.05179906824,2.16342000941,3.13816341337,2.67755112207,2.59874492082,2.56446127431,2.72867770152,3.33439749463,2.58746159983,1.05993309828,1.3461958142,1.35841365261,1.50049334696,1.55756435389,1.48869046894,0.449972526728,0.562594127918,1.68427661715,1.75443939239,1.72288691696,1.83148547347,3.17337541646,1.45611685347,1.8045768629,1.6886042969,1.874738216,2.01913828449,1.91197380364,2.35474084008,2.40584145088,2.4837250859,2.50792881085,1.23454688046,0.935203317387,1.08713532578,1.22437514184,1.71471192246,1.54353868026,1.79323835878,1.49220444124,1.58369400192,1.71875604029,-0.313349187243,-0.679119333363,-0.735692756837,-0.619636217275,-0.268048293834,-0.382483265679,-0.504898615817,-0.532778319189,-0.0299165475294,0.405604560329,3.3610948911,3.46432032219,3.36346167897,3.57010879234,4.24823139191,4.2519879622,4.0164502476,4.12779306713,4.02498860371,4.13298428423,4.01868157582,4.12909985941,3.58389985785,3.26634007391,3.29189014061,3.45141286739,4.01429078064,3.89959806408,4.13041006014,3.93281487132,4.08908933146,3.56256240708,3.45719731373,3.46161424584,3.57034778916,3.79109500794,3.89762705813,3.902883158,3.6775985065,3.78281157737,3.6743913251,-0.730128226921,-0.607314546949,-0.588261380443,-0.686938272848,4.23776478314,4.23840906368,4.2412867859,4.47795591834,4.24200071409,4.36682433045,0.273714047996,0.0552796822129,-0.122033474476,-0.500504189741,-0.179379898887,4.32861209022,4.14800011911,4.50895847554,-1.55487774855,-1.27124451567,-0.911464368429,1.63578433074,1.60653749043,2.0179621474,2.14560861874,2.21393881041,2.14328751295,1.15045777746,1.29459957609,1.43910750171,1.44639891878,1.31986739607,1.42621593414,1.42942108223,0.0194251263892,0.0588453207619,0.171371379474,0.241332315031,-0.0676073319215,0.0246196402968,0.386010041681,1.0673535572,0.769797032828,1.10222327394,0.954812512747,4.24994855386,4.01942420925,4.00440966816,4.51624285625,4.67789081624,4.43703894054,3.80114464745,3.71868166183,3.85498366662,3.53040433208,3.4092066661,3.52413963817,1.69678389683,1.71580003727,2.68864505489,2.83126905338,2.50876684762,2.44348970425,2.81996498516,2.89660285666,2.79218479495,3.02330103021,3.03884735276,2.93991197523,-1.3646136616,-1.43133000472,-1.20156606955,-0.989101634628,-1.38680715303,-1.31548700916,-1.12966789665,-0.989960403451,-0.670334019421,-0.282922407883,-0.320700806789,-0.553864314213,-0.399331257505,-0.578439175048,-0.463991155056,-0.860184441703,-0.977783520801,-0.769752990772,-0.755638660451,4.7082911958,-1.56188427505,-1.44680594971,-1.44044704518,2.81281362434,2.82920971562,2.94417781954,2.91868470596,3.04711588394,3.03836397564,3.21558594133,3.11912608734,3.43144596607,3.56133102044,3.68161478467,3.43992048527,3.55733099508,3.66973917269,2.76160585678,2.72796204258,2.90125723649,3.00083015156,2.82669557981,2.96196597604,2.98157154954,3.29858107386,3.05190203848,3.1896342093,2.74253852333,2.80168400248,2.67584865726,2.49449834048,2.47206441543,2.58913783688,0.972816390267,0.998900557201,1.11563335907,1.20981680335,0.79407799089,0.911002945484,0.713000337254,0.956909505393,1.15808102135,1.08782730711,1.29130393956,1.36319990211,0.398345916183,0.251215007396,0.268726833632,0.191801694952,0.760633426747,0.644092193681,0.817103260177,0.850525012268,0.476969275195,0.39670936455,0.464598703561,0.599421645247,0.62849289513,0.680838484369,2.96171468902,2.74277017761,2.57397618153,2.64203280938,2.96321777007,1.24229567767,1.99976873949,2.20083451449,2.04774344853,2.34772485602,2.35382453224,2.15197798796,2.25921795209,2.28528193949,2.19993860544,2.0946342793,0.895856304048,0.701337512712,0.621559312739,0.766546946061,1.05413200368,1.01677712846,0.0284616057007,-0.418775392587,-0.300538418926,-0.44794775134,-0.27528801077,-0.139907656337,-0.135483545708,-0.14940286099,-0.0372534923761,-0.146961390149,0.448346831406,0.507592458504,0.537386852704,0.605293218995,0.687505496406,0.625132174663,0.492997981491,3.79682325765,3.91028460542,3.68316528068,3.91266241995,3.78105006429,3.68874477117,3.80445482437,-1.37939165461,-1.43170779425,-0.886141419809,-0.989219444904,-0.947552123386,-0.824288713059,-0.800946285074,-0.900220502674,4.34955851219,4.35576379339,4.4752030814,4.46184060049,4.58519834235,4.59178086517,4.34898345088,4.46312879005,4.35032685579,4.46081179644,4.57612466788,4.57543643674,0.102453386085,0.0784002942238,0.352355925993,0.647225433232,3.86490221431,4.02943818346,3.71056185442,4.10231932241,4.54209798279,4.37703272274,4.52814457627,4.38324839175,4.2819622885,-1.34861317084,-1.20871330065,-0.894848491161,-1.00639087201,-0.759715750829,-0.991961677134,-1.26560631321,-0.246684951344,3.6100213675,-0.457480356592,-0.892449948844,-1.16796891621,-1.23098668266,-1.14778095673,-1.00143648273,-0.922880830847,-1.00115617505,1.70378617349,1.73619628357,1.80634615447,1.93773310205,1.89546904647,1.99222580682,1.05081911089,1.16795409151,1.07409013834,1.19508333273,1.30510990554,1.30819033803,0.205847427992,0.149962841966,-0.12159043913,-0.0133693853433,0.078867164747,-0.181496054958,-0.100812024638,0.0625476517276,-0.379730295577,-0.300744472215,0.463310659293,0.825926390122,0.686260639882,0.311424945398,0.732861665401,0.409579777564,2.10676146896,1.99170163932,2.67507166534,2.22663716715,1.88619070054,1.82657919557,1.95389492615,2.13711935987,2.23405687129,2.12019820183,-1.43538462773,-1.46555450173,-1.55839347032,4.65664948378,4.37134448566,4.48647513287,4.60735698506,4.55165454803,4.66081228761,4.35857839943,4.57448106849,4.30312814405,4.40548030239,-0.45914998693,-0.555615151775,-0.618007022929,-0.63625072661,-0.536147316102,-0.489217218189,3.07121110767,3.33170560978,3.3810272355,3.25674669903,3.09338427197,2.96768689935,0.883620678876,0.857152559021,0.94224082776,0.75557303566,0.712108357188,0.670513831067,1.14764217243,1.08252888961,1.15462530549,1.29763130588,1.37254328074,1.29551622082,2.10095272289,1.80942304823,2.36947077901,2.37790687331,2.13454271678,1.89657580242,1.94264563383,2.05690061808,1.97958521283,2.10516566219,0.0842348594316,0.32792565044,0.232162961375,0.0998182481623,-0.00452951333123,-0.0186502041565,0.244254496494,0.357447288576,0.379746972031,0.177676511589,0.290148084585,0.103385336881,0.127051748803,3.54299066428,3.63709198538,3.36702444761,3.3825727201,3.47334732835,3.58573151777,-1.16523616731,-1.04225512313,-1.01919175167,3.97324152621,3.53481448071,3.53036615293,3.7320217605,-1.51934046717,-1.50155291138,4.45390548105,4.17478022961,3.91353080241,4.11677132664,2.74075733826,2.50583200739,3.03848072104,3.00810532988,2.80517278468,2.59009638662,-0.416179895026,-0.388177510704,-0.162379464262,-0.255827373333,1.60768461694,1.24655004854,1.24529499866,1.79989950977,0.699528347622,0.872182563868,0.446037166971,0.139906594502,0.399535269385,0.25711181834,0.33463617986,0.167608763617,-1.37243281719,-1.25696033176,-1.4632520374,-1.47379226978,-0.320568425102,-0.247166058464,-0.343558322924,-0.129471437774,-0.104149275659,-0.19842373178,-1.23587641147,-1.21269250983,-1.33196882893,-1.33948016771,-1.1058630531,-1.12143197334\n    \n};\n\n\nconst int NST796 = 796;\nconst double AREA796[NST796] = {\n    0.0159234609509,0.0163249587318,0.0160387337561,0.0151771900476,0.0152544541949,0.0157020354637,0.0157384164576,0.015847268734,0.0159226136745,0.0157914819581,0.0159468024879,0.0158947097968,0.0154574034904,0.0155680496508,0.0160995428217,0.0156516968255,0.0160601387404,0.0147568329141,0.0154352856389,0.0161730583588,0.0161618907849,0.0151312711975,0.0160300409708,0.0160563588532,0.0155332993719,0.0150003942028,0.0158189940377,0.015250998753,0.0154608430018,0.0162468207922,0.0154546623078,0.016152897284,0.0154074544035,0.0162560130167,0.016371694212,0.0154510414099,0.0158376525602,0.0159225409796,0.0157152303099,0.0151612342092,0.0154487352597,0.0151403300345,0.0167301961814,0.0150678715112,0.015552329899,0.0162005261775,0.015204850866,0.0154275994658,0.0150043328449,0.0153992293873,0.01598312999,0.015633608828,0.0159751219931,0.0155138837941,0.0162135804799,0.0155940804807,0.0160686924959,0.0154312949822,0.0156151704764,0.0154991326603,0.0153580721352,0.0162726039644,0.0162755104821,0.0157320947351,0.01575576399,0.0180831054267,0.0151194711095,0.016210370554,0.0156324826234,0.0158395248188,0.0163462529353,0.0156015316207,0.0153774351721,0.0161830202039,0.0156239521013,0.0158180233905,0.0162683830997,0.0161576378689,0.0156529362532,0.0157679615108,0.0159406489418,0.0149928658331,0.0148500445133,0.0153744397303,0.0181185880899,0.0156119402426,0.0154723185742,0.0159019238676,0.0157216075415,0.0161296619114,0.0155839402702,0.015247712917,0.0161370493803,0.0154358184859,0.0162759048164,0.0154415584359,0.0156049301862,0.0161247657627,0.0158535601651,0.0158197399483,0.0154745453798,0.0162504818172,0.0161884061265,0.016012035472,0.0155628449635,0.0159974262137,0.0158140548983,0.0157179325214,0.015934291778,0.0158425799752,0.0154704206227,0.0158749783461,0.0155687062804,0.0159921776154,0.0156782441193,0.0158876642939,0.0157752472423,0.0161819468612,0.0163908301643,0.01533768081,0.0156493009414,0.0155720743707,0.0155672254575,0.016012654376,0.0149344138372,0.0165187311131,0.0152687031188,0.0181962035553,0.0152416603898,0.0161790286195,0.0155201824214,0.0155036928113,0.0161858238715,0.0157512724339,0.0159829992807,0.0154817731473,0.0160174542155,0.0155904185503,0.0161161347456,0.0154882773128,0.0156977925575,0.0156833131359,0.0160483677892,0.0151624411724,0.0168420340772,0.0149270647404,0.0155084443012,0.0152289664927,0.0163039116243,0.0149691694011,0.0156095688915,0.0160355802786,0.0158530092058,0.0157295841067,0.015988388595,0.0157408775052,0.0154967518775,0.016144363945,0.0154491308622,0.0158407268798,0.0152337003001,0.0158732852654,0.0156508934928,0.0160867392834,0.0156449699033,0.0160357550591,0.0156794157438,0.0160554543961,0.0157520656802,0.0155385279907,0.0163224085989,0.0156749577741,0.0154788650097,0.0157976585719,0.016169747729,0.0156326164708,0.015681809074,0.0160314471688,0.0158128795637,0.0154483594146,0.0159530895864,0.0158483938194,0.0159328718034,0.015841931074,0.0158847885181,0.0159339302735,0.0157864556676,0.0159030459932,0.0158662047548,0.0160916940911,0.0155636882211,0.0155638040342,0.0162034469142,0.0154903508296,0.0161212489425,0.0155805377459,0.0155399931552,0.016258567265,0.0149213267565,0.0162135698137,0.0154796135392,0.0162432403425,0.015482682062,0.0155322460565,0.0162086655182,0.0150895483331,0.0156972581259,0.0159750748336,0.0160299535167,0.0166361818504,0.0165511458529,0.0156421901596,0.0158986511764,0.0159306863312,0.0155605521762,0.0160766456858,0.0161373657617,0.0156297790631,0.0154047731508,0.0162624669689,0.0158447513277,0.01549659947,0.0162618348173,0.0153599915769,0.0161030565437,0.0149935156132,0.0153451177304,0.0163539561747,0.0160038786282,0.0157452680288,0.0152600369064,0.0154142943105,0.0157358768454,0.0160847782608,0.0158080711931,0.0160901714575,0.015729799299,0.0158025320574,0.0157073260431,0.0157919348343,0.015932102188,0.0159108836242,0.0157732680514,0.0156625127745,0.0155908617279,0.0160813742345,0.0161034353321,0.0148823661726,0.0160375535676,0.0179638468386,0.0148593028342,0.0153038598466,0.0160208131799,0.0159895476149,0.015727187846,0.0154498706614,0.0161923493497,0.0154944463147,0.0156657286216,0.0153749113639,0.0161509526851,0.0155655813532,0.0161150394836,0.0155722227087,0.0155738528899,0.016017834562,0.0157819714045,0.0159186680253,0.0160670979316,0.0148374055255,0.0160344101473,0.0157524347383,0.0161347821173,0.0156410711192,0.0160738363131,0.015275982892,0.01496753902,0.0151610878774,0.0149166960716,0.0156251579002,0.0160146159806,0.015456853748,0.0157590901386,0.0156436643919,0.0160994025892,0.0158000145706,0.0157910790214,0.0161835678409,0.0162979273173,0.0155725486198,0.0154312869132,0.0161956057985,0.0162000370691,0.0157164776597,0.016094466538,0.0156065970461,0.015924713285,0.0154422712524,0.0161489139374,0.0154294501519,0.0160125033988,0.0154405214685,0.0160239756083,0.0155779539843,0.0159510406241,0.0158551635617,0.0158013185976,0.0153716293398,0.015672910499,0.0156343259019,0.0160891125104,0.0157720824188,0.0160100243752,0.0155129961721,0.0159718091092,0.0155424365008,0.0161833306674,0.0157766960841,0.0159354720238,0.0157834899367,0.0159082371419,0.0156834903396,0.0159836756783,0.0158674972567,0.0157927340636,0.0152185578784,0.0154859732311,0.0151909304805,0.0163280871094,0.015418728645,0.0154987969584,0.01611155377,0.0155999446722,0.0165793526732,0.0148896235163,0.0148859423617,0.0167316343106,0.015124411439,0.0151858461193,0.0185958397213,0.0161310540471,0.0154676070339,0.0153676053175,0.0162452519654,0.0159665972994,0.0157110364711,0.0155258673658,0.0158738112769,0.0158610926665,0.0155257706214,0.0160298986466,0.0158019266636,0.0157767593523,0.0155266685238,0.0156984670317,0.0160303831632,0.0153935163476,0.0156637115749,0.0151440658374,0.0162997466167,0.0152238194773,0.0161925979302,0.0161566192096,0.0156084019398,0.0160191980263,0.0157660226256,0.016128649029,0.0155474031664,0.0157693494239,0.0158080356686,0.0159660465423,0.0156076451513,0.015795501245,0.0159427057678,0.0158316270802,0.0158443038426,0.0161291763744,0.0159987820998,0.0159265689126,0.0159147268503,0.0158382804309,0.015930916249,0.0152509346707,0.0161908167117,0.0156385015996,0.0159709944629,0.0160282518761,0.0158996432038,0.0157851136728,0.0159248719266,0.0160563153865,0.0159902206977,0.0157224281721,0.0158224272834,0.0158686343687,0.0157177733359,0.0155639431617,0.0154712530698,0.0160798588183,0.0160742283009,0.0155482781857,0.0150677774367,0.0160630662526,0.0159978566521,0.0157003867471,0.0160272457359,0.015800013197,0.0155417724501,0.0161585112182,0.0151995481331,0.0163303923807,0.015218334413,0.015780556695,0.0154623738099,0.016118140045,0.0152646254978,0.0155350926349,0.0149895997827,0.0152817288925,0.0160141630436,0.0158988029082,0.0149114516276,0.0154758010801,0.0160467472156,0.015445705793,0.0154614479463,0.0161380152288,0.0158390099646,0.015475027209,0.0158761915425,0.0155526659933,0.0160726290914,0.0150972282769,0.0149027098674,0.0165008527779,0.0150311875508,0.0165371755855,0.0148021914416,0.0159949098892,0.0159059999093,0.0155781583551,0.0157656349096,0.0158666365022,0.0155066899423,0.0161865066907,0.0157663920336,0.0156563530335,0.015966345434,0.0159266557573,0.0158830639942,0.0157211528562,0.0153109373096,0.0150056701238,0.0164212039047,0.0157244995588,0.0147313148177,0.0160805666518,0.0151193698,0.0186884092301,0.0150726312946,0.0159083577536,0.0157577298973,0.0155746936307,0.0161467387505,0.0154828606989,0.0153898722844,0.0155344448618,0.0163279758774,0.0154504031049,0.0158623575203,0.015735385887,0.0161708739772,0.0157660610449,0.0156079588299,0.0160182786904,0.0151981156528,0.0153015656212,0.0152553084839,0.0150809623765,0.0156174536146,0.0165296445014,0.0159327027486,0.0172625356852,0.0155910792469,0.0160256244139,0.0160638008913,0.0158896485685,0.0158637167459,0.0158940903861,0.0158178367047,0.0158266167856,0.015899529065,0.0159604582895,0.0155264647067,0.0156001830234,0.0158195235049,0.0154248339942,0.0160953282042,0.0156448154452,0.016148209455,0.0155440058761,0.0162768938457,0.0155769490093,0.0161180591492,0.0154154087998,0.0157795016721,0.0159068299108,0.0155490054581,0.0158271592023,0.0157691686243,0.0159828750001,0.0162180841797,0.0154811177043,0.0154433836814,0.0162180787949,0.0156420629183,0.0161570056979,0.015397486933,0.0151680207687,0.0151993656069,0.015423020733,0.0154836796452,0.0149922489109,0.016225284945,0.0155442723434,0.0161339188724,0.0154238032779,0.0160818878914,0.0157014330583,0.0160643248738,0.0163914216728,0.0158680781785,0.015830764253,0.0154260641137,0.0162016954203,0.0152436353218,0.0164592246214,0.0161331599147,0.0159265508144,0.0156640555255,0.0160288056339,0.0158446852041,0.015417868476,0.0163011313371,0.0153367545766,0.0155110320994,0.016324103687,0.0168114291222,0.015971980298,0.0156799419369,0.0154125802461,0.0161395232567,0.0156160579237,0.0151690787305,0.0162827907235,0.0154875879326,0.0151753083667,0.0163157617063,0.0155816240819,0.0161288151358,0.0161147354718,0.0156448149167,0.0156082132962,0.0160432592684,0.0157180343892,0.0160042263498,0.0157154985408,0.0157141169715,0.0160051340205,0.0157877071657,0.0159727391139,0.0160365029693,0.0156908273401,0.0162691019971,0.0152963602776,0.015438899119,0.0157051194469,0.0160292002126,0.0155118705674,0.0154397685962,0.0161588037508,0.0155752148347,0.0161728003506,0.0160713731898,0.0156095365079,0.0161489847291,0.0155261526922,0.0158890901496,0.0159422289676,0.0156644591313,0.0157471701213,0.0161484951969,0.0155145046706,0.018535965342,0.0152063038987,0.0148952965958,0.0165856343219,0.0158718569985,0.0154423750815,0.0162908237927,0.0154535643305,0.0161707139139,0.0155614758281,0.0158941397884,0.0157263783003,0.0155200916122,0.0161030280946,0.0155715804644,0.0154674172207,0.0150557650158,0.0159375296198,0.015449714876,0.0157652755505,0.0158142737055,0.0158823973233,0.015669158998,0.0162636022646,0.0159086609836,0.0160305449415,0.0157875304593,0.0161802820173,0.0156637587952,0.0157195104588,0.015572153847,0.0160465191964,0.0156689451397,0.015740841241,0.016246236347,0.0151941939696,0.0161556927225,0.0152554338702,0.0157817186918,0.0157723905682,0.0155926664477,0.0161729550683,0.0155232771617,0.0160490575534,0.0152118521637,0.015326436827,0.0153928707374,0.0150934396472,0.0153168006884,0.0155494908057,0.0154594772764,0.016188766086,0.016110846746,0.016023790248,0.0156738847726,0.0154205049175,0.0162718219165,0.0162716964012,0.0155032507167,0.0156360066526,0.0160200952209,0.0148443369078,0.0159881184802,0.0159576368048,0.0156601028763,0.0156549685601,0.0156870420148,0.0156725280009,0.0161265879303,0.0156224378392,0.0155600434109,0.0160953599866,0.0156390732273,0.0160971914491,0.0154557025034,0.016196601127,0.0155530849263,0.0155913778283,0.016037871956,0.015696074668,0.0155292763647,0.0160943606307,0.0157807981773,0.0156509943887,0.0154717114823,0.0155209506264,0.0161960110097,0.0156340504373,0.0161398382939,0.0161020465777,0.0156737509478,0.0157215691184,0.0157206225335,0.015709075163,0.015902663816,0.0160964738285,0.0157262716719,0.0157996865242,0.0160438158695,0.0157811034304,0.0156766902556,0.0156668654672,0.0159750499105,0.01603265127,0.0156729933596,0.0157820906661,0.0160486513328,0.0156955191965,0.0159819573589,0.0157064368791,0.0159685536518,0.0154611678187,0.0162081561493,0.0162824790663,0.0155530184969,0.015627493332,0.0159231944876,0.0148767251278,0.0164642552633,0.0166307976187,0.0185191423006,0.0152498871408,0.0155354374448,0.0158756733163,0.0152292242891,0.0155582328011,0.0154562012856,0.0165043222515,0.015057576819,0.0158611702726,0.0153980191459,0.016150246917,0.0152981994741,0.0164069593285,0.0160154461799,0.0159647165216,0.0158154924606,0.0155866466185,0.0159884663011,0.015561699255,0.0156149721976,0.0159270255695,0.0157425174161,0.016040804639,0.0161162654513,0.0152187972754,0.0149259513664,0.0167424285812,0.0158529300714,0.0154727450508,0.0160587986785,0.0150847463998,0.0151186047041,0.015141970249,0.0149953128318,0.0155230558489,0.0157212890892,0.0158586393992,0.0159938746383,0.0157045519065,0.0163416286033,0.0155341605964,0.0160832179766,0.0154892007259,0.0162188138257,0.0162653853968,0.0155277670196,0.0160475151569,0.0155600626954,0.0154477870093,0.016244687873,0.0161110549667,0.0156171286425,0.015950540949,0.0162793356338,0.0162927269194,0.0155001795436,0.0157970368634,0.0158204600176,0.0158740974098,0.0157630224598,0.0155762234499,0.0161522567281,0.0159252619428,0.0158554546471,0.0154222847744,0.0158344335083,0.0154861174423,0.0162413732075,0.0161386813129,0.0155264133266,0.015823668196,0.0158220980699,0.015535632844,0.0158912933049,0.0155778694649,0.0158634259589,0.0155181108091,0.0161443098924,0.0157425035877,0.016117646071,0.0154494236506,0.0162024786467\n    \n};\n\nconst double PHI796[NST796] = {\n    1.50502206474,1.77037834345,1.60855157565,1.24942939558,1.22019678582,2.8611680822,0.561928575835,0.660436796879,0.733722933387,0.830460379455,0.857743324482,1.69105024214,1.64303696248,1.39622816391,1.29646897024,1.193661306,1.19449212828,1.74633285535,1.71805862414,1.87768165073,1.51622980026,1.5959319518,1.98806334681,1.7692503344,1.94714652737,1.83839314157,1.59731770107,1.58303622452,1.68681955003,1.6994873269,1.80859852444,1.99385724471,1.89608336148,2.4178583568,2.07884820436,2.1829785661,0.491502225487,0.416221146137,1.0083672724,1.10491419855,1.15511739327,0.955494195856,0.965504060792,1.06109438698,0.884093830605,0.784068862851,0.697360738052,0.0845002719227,0.0746914349686,0.182750021623,1.19141903646,0.992910158481,0.996619465718,1.37299416276,1.24987410977,1.13725172989,1.11717146962,1.33942919409,2.44691125976,2.47342252155,1.14773089879,1.24520422447,1.27475843668,2.76879424352,2.56285071911,1.83045966226,1.73817394934,0.258177417662,0.359319072724,0.948114556124,0.114716024457,0.310792293216,0.142256090827,0.248267539207,1.09769197862,0.995612252134,1.29149859351,1.26461750805,1.35071598894,1.41034707918,1.33103799311,0.968391956527,1.16601212929,0.985163636046,1.07633608798,1.79543376312,1.4976915053,1.62086518118,1.60504021187,1.88054216572,1.98451937081,1.27630615005,1.39280834173,1.49045484859,1.48869578536,1.39560312312,1.29393159952,1.29847973984,1.61292096053,1.60696809967,0.961342595843,1.06782004325,1.71829756098,1.81611862936,1.72157281934,1.02603563061,0.955811904279,1.09865048852,0.997000674252,0.993122393127,0.899049636422,1.09436509649,1.09757251105,0.910198096652,1.0070646277,1.51114506154,1.41804523794,1.47693234627,1.35086569451,1.3698399445,1.8018247932,1.85756438322,1.91130924774,1.94031064702,1.76901307289,1.86879897568,1.70978961359,1.72999834221,1.82267501522,1.81486200325,1.81795897383,1.71684072079,1.72029144161,2.19046599842,2.29359258119,2.45712212229,2.56529876704,2.39050827696,2.37525196307,2.31005763395,2.09720080586,2.16055952733,2.26342186684,2.05962489633,1.94752421589,1.92335301641,2.13964992054,2.00037935823,2.10370176006,2.82150684291,0.594126077524,0.629758808866,0.465354786421,0.573433477743,0.638373907734,0.609837941422,0.611945461427,0.596299891756,1.06001438551,0.792557871123,0.588407960407,0.481674818201,0.669805407134,0.678646290404,0.71928269884,0.621736209977,0.558232002773,0.615275710272,0.406034596729,0.451304986991,0.597163922185,0.540940836586,0.57162771745,0.474316112511,0.119794149495,0.137709553464,0.232192475317,0.299402273953,0.541302307222,0.392991053448,0.494073186358,0.643341620373,0.696659506377,0.796188322182,0.846444553905,1.00271004626,1.1051732229,1.37782430314,1.29176691898,1.08860396446,1.09200696485,1.18459065461,1.28008132013,1.48160573029,1.47022717541,1.56219405603,1.5863310428,1.67495850036,1.66201977363,1.31816866695,1.40883409297,1.39160985591,1.28749731508,1.20895893353,1.19521852213,1.85767551025,1.77824150399,2.27441117646,2.37658089554,2.12638352091,2.12323874561,2.62349543706,1.93393035212,2.13626194805,2.03838500863,2.04054810075,2.09673807066,2.83636316081,2.45458335792,2.4156846293,2.36482108733,2.43489813506,2.40614029147,2.29835376358,1.60722960099,2.12248042227,1.25432500981,0.922711945659,1.42223691016,1.5212758545,2.02028151298,2.01986221013,1.65932877048,1.56314665551,1.55843051443,2.47817806027,2.49041056185,2.58356512006,2.63871485053,2.74724861776,2.64564203914,2.54181748984,2.55001916004,2.91764373487,3.04286116665,2.95713501392,2.93847446364,1.5951165255,1.49359323848,1.69226642413,1.67759867753,1.56665957856,1.47951868161,0.898769111041,0.912719449136,0.714778576631,0.800635986705,0.797867417223,0.269774458352,0.219879152639,0.297818617571,1.01409960114,1.10700024181,1.01852899961,1.20831353327,1.04084087777,1.22631137837,1.14704352466,1.65946540414,1.63587959667,1.53638655294,1.45000238879,1.73280937263,1.72253800852,1.8113041136,2.19625944528,2.20903414263,2.32306055397,2.37858802133,2.29288617711,1.10439178256,1.15619400361,1.33328204732,1.25928223005,1.34452007618,2.00392200459,1.90895048048,1.68007770002,1.27214858815,1.20000040557,1.15924135349,1.12253622385,2.19294430745,2.19240839711,1.15681736751,1.12467910241,1.51590832935,1.62279009478,1.62594337924,1.53122218314,1.6228364501,1.51965278794,1.64211950064,1.56256820047,1.72371557668,1.66911613944,1.75083659723,1.4333843627,1.58547213321,1.61739821277,1.53774146491,1.40303842733,1.47572597512,1.91532076202,2.10347335205,2.00818094583,1.91508525914,2.00718163041,2.1019838988,2.13055361409,2.17844139455,2.26649612273,2.23965982971,2.07139156091,2.04981843538,2.38702205623,2.29093301632,2.37768971856,2.28901969831,2.55829480561,2.47557112532,2.5866775718,2.63123511299,2.58416783544,2.67903111997,2.48245485975,2.49112218354,2.54818392283,2.70850252354,2.64427636581,2.96558633037,2.86680489587,2.76832677268,2.74526053536,0.970154051297,0.989754647785,0.320795238266,0.424787079492,0.506804097796,0.4981374343,0.794087450254,0.892564642583,0.892226251507,0.692280141754,0.799167831823,0.6971592771,1.05445315648,1.12957669314,1.20681543624,1.10395835655,1.27491296091,1.24632043808,0.955826125097,0.923440081184,0.819007947094,0.744533469572,0.948778847422,0.908336156968,0.802550280298,0.735982021798,0.787545553982,0.893466625605,1.20041588373,1.40214246855,1.05696936885,1.06805472835,1.16060342698,1.18322157936,1.27401623512,1.28567627403,1.06476266851,0.891574656217,0.780883984108,0.776498251917,0.884370570379,0.974499035457,0.500931642276,0.439202943941,0.312952352803,0.408352622809,0.236755641315,0.3945342664,0.296662139934,0.806441544852,0.704376410537,0.91530635714,0.807919837986,0.752823655619,0.720553190589,0.964882331072,0.877665078408,0.860210769915,0.936623235635,1.30616505044,1.40619672652,1.95636700716,2.16667943777,1.51552197967,1.6032454988,1.38460039037,1.46773051747,1.38455389721,1.83396605076,2.0172674415,1.93494810598,2.18261678543,2.27734399464,2.20203683846,2.48036890782,2.67227852403,2.57770181214,2.4699975086,2.37242264861,2.84720164101,2.51617359842,2.53705676206,1.81761961998,1.92853157048,2.23345735057,2.32808767731,2.42874832595,2.43492407033,2.1935802673,2.32886741193,2.23975772748,2.12556679728,2.13983698809,2.65824280896,2.74677824429,2.92809632939,3.01154978145,2.94327860735,2.89976752655,3.03865571908,3.12224153049,2.82994497967,2.6892782514,2.7957596761,2.62229047182,2.73559186195,2.63874709699,2.30658680632,2.1510691381,2.25554874365,1.78828165622,2.3269498111,2.43056333119,2.35365496092,2.29354559167,2.46849252007,2.5128022372,0.809683765517,0.727723574576,0.769755010071,0.87238084176,1.37260724251,1.55942988667,1.72946771152,1.83433631656,1.65015899292,1.65025075999,1.93202948718,1.75041429829,1.83521136834,1.74603417589,1.47153041733,1.29638296386,1.36407014931,1.45981778157,1.37287113125,1.46085286995,1.27503671044,1.35682844473,1.25992320411,2.82709099004,2.6782680261,2.73457318128,2.63691772182,2.58434457496,2.62573163701,2.80724372206,2.74624813284,0.480512340942,0.394266169686,1.82981323983,1.83890618837,2.02306297886,1.93770507336,1.91092735641,1.99935482892,2.09809293171,2.10432852755,2.01371159219,1.91934652643,2.01738179681,1.90756342834,1.83151902845,1.80591030731,1.90196334257,1.78647925125,1.86226644554,1.49775059963,1.60615921674,1.62509928195,1.53365888872,1.46110824489,1.31183195125,1.34790586277,1.31370372616,1.2581202701,1.42554481756,1.33853077104,1.44266172504,1.32261930158,1.30173470693,1.20198192769,1.2107870613,1.41227725389,1.42458291461,2.45621799073,2.37613861305,2.31316518468,2.41666061117,2.27012555516,2.24535354083,0.864033221398,0.860254339757,0.676039172824,0.776870694211,1.3397479058,1.31853010212,1.41807538417,1.52927049721,1.54129294832,1.45103054826,1.17646023925,1.08512436153,1.20724507081,1.00295197102,0.759085375014,0.297595053993,0.403120597107,0.40068993436,0.233084011171,0.33485415004,0.379537931999,0.194303364029,0.280777985278,1.23352541028,1.14129002653,0.946523772397,0.84533401122,1.03867056991,1.03418433085,0.938761170516,0.841745921507,1.13099775904,1.32322423208,1.22750674973,0.946347800693,0.85891963999,0.984366604076,0.879541826178,1.14303194006,1.05116563106,1.23268799848,1.22209071555,1.12158247338,1.04011985475,1.4477031622,1.53343173634,1.63542734217,1.46688201767,1.79296812384,1.70641811592,1.72084755092,1.81960086642,1.75069295978,1.6510665289,1.56811671125,1.75290309114,1.65415906813,1.56900688191,2.37448387184,2.31496861112,2.36562286845,2.46556887992,1.98931748992,2.15599050923,2.04834277091,1.9687101779,2.28016670633,2.18869328584,2.09548509003,2.08771533273,2.27421572873,2.17530093604,1.8068283419,1.70151136648,1.7089279294,2.11906957796,2.01850117434,2.7203565012,2.71454173193,2.78468827215,2.36536118959,2.46958485537,2.56169457522,2.03966288357,2.15120386054,2.24736481041,2.22657561078,2.21591990653,1.93903213414,1.8466139067,1.44926619055,1.47037359908,1.35703280329,1.36898405342,1.70671364778,1.79059919082,1.51737198305,1.52906603963,1.41797832933,1.32842877758,1.33650945188,1.43762861233,1.19335130729,1.08695644741,1.19336359946,1.09019823481,1.02110004627,1.55614764901,1.63538327174,1.64651194512,1.4572857279,1.53249311796,1.44465454407,1.93685709949,1.83927919506,1.93233203499,1.74540364326,1.83508778035,1.74581043193,2.50238913157,2.54490606354,2.5533192765,2.658448107,2.65365875283,2.72075958822,2.77211150061,2.75005633017,2.81601464843,2.93203666383,2.96379001964,2.86388763291,1.53640174254,1.69111007383,1.72260392087,1.64667074688,1.5571263579,1.58238943918,1.50330699754,1.39238670472,0.76476963866,0.585354251025,0.574292376199,0.667015734797,1.15335332808,1.13267864322,1.01909599788,1.06137555886,0.924347401776,0.946593965153,0.748571092072,0.7584191279,0.576634482379,0.675753546605,0.467927838535,0.563230572731,0.653134688992,0.660562828548,0.580471680679,0.478594187468,1.32908640243,1.36476766558,1.43235033011,1.45073446903,1.13707970879,1.17227446995,1.25692106957,1.23937889868,1.06573692114,1.04773164887,2.00257371606,1.90605672599,2.08931596497,1.89361373125,2.07667425789,1.97792857737,1.88960300422,1.98985027029,2.07085373972,1.84351128659,1.82484627911,2.60771472388,2.53588344583,2.01365594546,1.9031619634,2.07908529728,2.10496095524,1.96887315201,1.8835323955,2.0792321828,2.10245145625,1.97131412041,1.88839601542,2.23164604725,2.3461192773,2.34178884147,2.13500385389,1.63019559078,1.7180526439,0.862520228393,0.828172913594,0.417466054597,0.512853068145,0.36446446573,0.769456691584,0.888938952816,0.875318952251,0.68143266283,0.80918466618,0.709786794515,1.94212365619,2.04629376097,2.12180662829,1.95229884081,2.06093670524,2.42877098523,2.3491889028,2.23907645743,2.21011727097,2.22621464034,2.27985349671,2.38709548777,2.60173322982,2.55550648813,2.52032106262,2.44346205827,2.02811270474,2.01084316545,1.90520654704,1.81874557147,2.1436108043,2.04056758248,2.04401721528,2.13679094289,2.24113041732,2.24442667508,1.55126845605,1.640880578,1.56062585109,1.65793861218,0.722956117749,0.705574674071,0.655499614862,0.557432113821,2.26867028097,2.31918602691,2.40977810708,2.3777887448,2.2072219183,2.18762855745,1.94139022237,1.9338181366,1.84566299088,1.74814217066,1.83041263752,1.74037444513,0.609750807144,0.435649417536,0.433756323085,0.526024611525,0.535640057623,0.615673111603\n    \n};\nconst double THETA796[NST796] = {\n    4.65852185733,-0.604294971823,4.59788888255,3.61618927535,3.72994072729,2.32603002935,-1.27316432755,-1.22748191917,-1.36507547262,-1.32489600502,-1.17186075377,3.35865202136,2.96612457097,4.61480482796,4.67069440085,4.61882290548,4.5047919304,-0.498061550743,0.0250962374492,-0.642095159213,4.12372644524,4.1865301634,3.78111265739,3.83210591301,3.88762475631,3.90848082967,4.50116751541,4.29557370342,4.33983493956,4.43687259487,4.47243631167,4.42518825717,4.3944979486,3.72771097826,4.33699099565,4.3742141197,3.13955456937,2.95784995568,2.37653604791,3.74613621336,3.55237625123,3.45420070425,3.59383346209,3.62986959733,0.250318304553,0.222349675889,1.46087677945,-0.119447049904,4.59030906807,-1.39854956004,1.86264221279,2.0001369363,2.25429134158,1.97053035691,2.54467701114,2.51973367026,2.40882459884,2.4707031571,0.992418810484,1.6100204342,1.29878968426,1.25878551329,0.30064943825,2.13836851992,1.95501821787,1.94627330594,1.91750944659,4.61554720401,-1.45965023256,-0.894698649624,3.49825119423,3.02668107345,2.61450255365,2.68782461716,4.44285418911,4.49979527012,3.80289464004,3.91872640888,3.98955379566,4.17127893793,4.10540017425,4.03140074718,3.95922652285,3.88950506008,3.86065920234,3.31748902843,3.33152737474,3.18522337459,3.29356608214,3.39129392223,3.3552573946,2.64622143492,4.50813403771,4.45604535811,4.3467065004,4.28588858083,4.45103508286,4.33729995062,-0.024810924123,-0.134216875086,0.146929542673,0.188984815478,-1.19885346642,-1.36710101567,-1.31437600104,-1.26666001005,-1.15270489261,-1.0151450638,-1.02023058074,4.62527892846,4.70372482348,4.68077270824,-1.48467954514,-1.43651090771,-1.39928637748,-0.198445842352,-0.154225036553,3.43478538629,3.57769703454,3.46815928142,3.72103306519,3.5027507004,3.69106815522,3.58287274844,4.25716371849,4.2752719855,4.16055915884,4.05899698245,4.0286036629,4.57992015356,-1.48132558391,4.64010146691,-1.53626189886,3.27326667265,3.21847859976,3.567535031,3.51370858668,3.30188146363,3.45337032436,3.74305974216,3.7674285773,3.88112700406,3.87963959677,4.21910218354,4.18909058583,4.08442262091,4.12244286159,3.99267693064,3.99985139864,-1.4364506945,3.07580420146,2.88941162068,2.71700853063,2.71986302022,2.55502212838,2.37292901181,1.79940185141,1.98721407407,2.61682107693,0.922849072746,0.856849121985,0.931384019896,1.13105035816,0.974194459388,0.357250160914,0.676919725208,0.504812894654,0.333216023235,0.773041132594,0.515582214326,1.44481340113,1.63565679645,1.24647529869,1.18082215322,0.820393704926,1.76672835031,0.624905723257,0.903107520203,-0.880171748184,-1.14661668178,-1.09543213663,-0.882939594755,-1.04884099702,-1.0359977488,-0.890622770497,1.86927426393,1.80347273869,1.85525387165,1.79907794589,2.05458825253,2.17636085239,1.98450013311,2.03511780171,2.13075525034,2.02124727806,1.96151302602,2.17638788727,2.11141150194,2.00537529122,2.3668479159,2.30008620603,2.19335805412,2.14929449489,2.33094348518,2.21917652243,2.07655671735,2.15304650988,2.23879492744,2.16126710424,2.79600742354,2.66374921386,0.749740447496,0.112124364938,0.0913927080674,0.277157314125,0.16446051439,-0.706417056373,0.885857048495,1.91225274824,1.75886086748,2.02256233181,1.31411298585,1.47036701633,1.48949480316,1.53571710136,0.684998727936,1.14455705142,0.393632256809,-0.0386432799856,0.025813752148,0.511920464639,0.636420088334,0.863602537641,0.804961993331,0.692856206872,3.03485354025,3.2175472701,3.30589444547,3.65875486297,1.07927209681,0.966336015959,1.26498353228,1.07627186604,1.56866162255,2.5939826234,2.09309428303,1.04824792591,1.6422312043,1.68557163709,1.71929239633,1.81818767571,1.8523431122,1.79758631867,4.42722901088,4.2945130716,-1.53753207647,4.64579454416,4.49139134011,4.14875141265,3.70279838566,3.40872386766,-0.780139900269,4.32191823195,4.24801637179,4.27127596738,4.12105534519,4.15309308548,4.07805448052,3.86716501807,3.97414460319,4.01215867514,3.94437783613,3.02564222283,3.13834747651,3.20480480521,3.00412692726,2.86537365963,2.84002736874,2.98505595752,3.07825238681,3.05536286141,0.117389575014,0.00792503534179,0.177458996617,0.126797777351,-1.35983061013,-1.30347936759,-0.967158198953,-0.919530791761,-0.707932903003,-0.907544949771,-0.797831120275,4.50692298175,-1.49611135778,-1.12554315418,-1.24548068304,-1.52794298216,-1.48263577307,-1.37421594347,-1.32359697879,-0.352578687321,-0.309977238549,-0.462681712604,3.50574852471,3.64846417249,3.46821578307,3.53785404649,3.6508458904,3.79353213144,3.68539559581,3.61288229022,3.7624222795,3.83220702723,4.6264453004,-1.55522512011,-1.48365638908,-1.54062594531,4.55091078127,4.59386693382,3.6487200383,3.40098687368,3.49415908692,3.62643427964,3.43787411617,3.55506537814,4.46667234666,4.55708958839,-1.50733182595,4.70546876207,4.21901418028,3.87248525002,3.86251980604,4.0474386591,4.4283327817,4.50194998021,4.70834529496,4.54318720977,-1.41610586013,-1.52398387769,-1.36008414735,4.32690960434,4.5103619008,4.33011281338,4.06896399164,3.19887996867,3.08000644775,2.3899141101,2.47846158084,2.30821383691,2.08017365726,2.00325266076,2.20732012393,2.07276467056,2.0820972848,2.30155025851,2.25064839523,2.84855937895,2.94998743961,2.73901267169,2.73511756329,2.83475429046,2.93251521523,2.60538740262,2.47464477084,2.44943365649,2.57581158796,2.85113230431,2.97828276938,3.00000240738,2.87112135294,2.72308733276,2.72604059736,3.13392071833,3.26602503786,3.38643802938,3.27411464305,3.4460657362,3.23556926836,3.40127715428,3.30153798037,1.20237410126,1.00938927725,1.34131310761,1.20261803412,1.1372905979,1.23072061793,-0.662543709009,1.64740497818,2.02185848948,1.91838753668,1.69398912633,1.3805113994,1.31003617906,-0.744973583702,-0.724725732671,1.80025547569,1.8590082743,1.60791971415,1.75682535154,1.37927715896,1.42282001277,1.57395495079,1.66264175144,1.68015630447,1.62503501112,2.11075881435,2.18740215433,2.34469636362,2.28429529474,2.6842735485,2.85426747217,2.79824734438,2.61616611761,2.60659957046,2.67297904704,2.45018278396,2.37253148245,2.58736773951,2.24440534276,2.27341637587,2.15830621647,-1.27001522067,-1.34619932867,-1.09927252246,0.39410163342,0.0339974420437,-0.0362385042177,0.0051586902804,0.137674258445,0.0528179058326,0.274512917317,0.121997311046,0.576576699189,0.349732532326,0.284937508103,0.465195099269,0.348513361826,-1.08825154581,-0.944719866205,0.0697777823682,0.543665865964,-0.858862715448,-0.411293204565,-1.29788338855,0.357773912142,0.225350319503,0.556118709195,0.555628073194,0.358781647808,0.0334753818821,0.139520125019,1.75339376247,1.63044113409,1.62433827656,1.68168026062,0.775985328287,0.831718954301,0.507689720006,0.608626710322,0.551762460681,0.709286216591,0.78793392478,0.663887146184,0.515313547878,0.522652817806,0.36408952956,0.470772961753,0.12869076866,0.173293171156,0.522653884686,0.63658951315,0.696743802329,0.809282963769,0.635722833871,0.693704383157,0.855814605441,0.906946942427,0.811020609123,0.644311902433,0.483127249327,0.535483971555,0.54411213897,0.710794571767,0.663867642461,2.69847206758,3.17297371044,1.35571309236,1.8007100111,1.60442274872,1.4102725026,1.59186532639,1.84685421916,3.38438023743,3.54477736133,2.97304762438,2.85490273653,2.85932217568,2.79587546377,3.15946692647,3.23514262093,3.19163272137,3.06058650191,2.98547739151,3.03957376931,-0.795735397728,-0.759654691417,-0.829990832381,-1.13312782882,-1.18424376552,-1.01504832858,-0.947020573296,-0.984951682532,-1.03392138514,-1.1482439434,-1.21247659968,-0.872057758498,-0.734008637635,-0.83647108376,-1.01849875014,-1.11712788405,-1.04824991123,-1.20509690587,-1.1617988057,-1.3024966677,-1.50619006123,-1.4416092993,-1.33343937748,-1.46638729996,-1.36968291638,4.19026502978,4.31563255099,4.01728742628,4.02532753115,4.2765856771,4.14083306193,3.24837893563,3.39465136934,3.2077799175,3.15460652138,3.00728725357,3.10432834392,3.16938776029,3.12729010723,3.02018179546,2.9659239166,1.04758948659,1.06419369682,0.915451374799,0.966236079854,0.0596562843657,0.245788922353,0.277798031544,-0.610544452523,-0.881028221641,-0.878138293096,-0.314985329832,-0.387484543443,-0.140432523039,-0.0577476846185,0.000253719315485,0.0156655167047,-0.0406926720208,-0.0577709211259,-0.184260693638,-0.255615647781,-0.188113580682,-0.240645263328,-0.222674860579,-0.173623753683,-0.39146956393,-0.483287196705,-0.655556551542,-0.625608815608,1.42329919765,1.47376468217,1.49633389548,1.61763095937,1.6764617759,1.60604465691,2.51309323228,2.45448298045,2.50696275835,2.62555684829,2.26663617116,2.33223210267,2.44574434124,2.4980900918,2.67798391841,2.62167861047,2.68108858008,2.79508530998,2.85310207698,2.79613620232,2.72556577076,2.6090035362,2.46497421365,2.42005812542,-1.111866902,-0.94653601781,-0.911322247141,-0.991790510754,-1.27606759212,-1.35418362697,-1.29078992103,-1.16080799417,-1.12716264207,-1.07753262783,-0.136553473843,-0.183909171043,-0.291301160065,-0.0400752695815,-0.072196775868,-0.67869395815,-0.231097490001,-0.444023235109,-1.03502840088,-1.10395026271,-0.995768108966,2.02858660619,2.06631970868,1.98229988102,1.8636724589,1.38464909831,0.817202662722,0.870931293499,1.18257068223,0.965974044649,1.1110419819,1.00481849724,1.49205000761,1.5603664663,1.46600549177,1.35781283306,1.51135343517,1.44574893326,1.33173991197,1.2898875375,0.48171863901,0.517712732585,0.357273403017,0.306121978949,0.40617307446,0.359708111585,0.189602821219,0.297768821925,0.305843587647,0.137343358519,0.190983937351,0.334753090293,0.284354730559,0.453891663142,0.347106982299,0.51705558553,0.461623386377,2.72348361152,2.55673078397,2.89242276268,2.92943085636,2.50857743277,2.71121047167,3.28337481553,3.5853162101,3.80747946161,3.77740039406,3.23061617788,3.05762094776,-0.809837175542,-0.678201045255,-0.786931846938,-0.855511677738,-0.526652022655,-0.63638551661,-0.698969829148,-0.659117291224,3.47275784205,3.69469685494,3.48435573564,3.38428087689,0.709095181036,0.817110623608,0.850251241038,0.625539651464,0.761157967787,0.643075707462,-0.26970285152,-0.432302871237,-0.496770047274,-0.551002864739,-0.171040619331,-0.284891258267,-0.183457522004,0.00407927372258,0.13815021269,0.0770782351199,-0.335017756223,-0.553835424834,-0.377306357407,-0.484527775386,-0.361211767286,-0.596172907368,-0.517776085387,-0.405321929716,-0.562605743294,-0.438803168717,2.48142378533,2.43025265651,2.40155518772,2.31158460105,2.27062798491,2.23298984359,-0.216979952542,-0.195199945614,-0.282810337082,-0.41367215662,-0.316226775441,-0.645986871541,-0.802272345959,1.91796992768,1.87019377326,1.7342624527,1.83885975616,1.69327896297,1.75006711041,1.53012042299,1.4180182483,1.57275164596,1.5082116461,1.26020624943,1.0713769062,1.21228049968,1.18116045848,1.31365434826,1.37754343368,4.05777079424,4.19510450405,3.83922113514,3.87517786236,4.12333001813,3.62494577967,3.81306581532,3.68385414825,3.73968589996,3.92468598076,3.91406158721,-0.444842974895,-0.390840602395,-0.487676198977,-0.567476851026,-0.59563397601,-0.769982126717,-0.887921114092,-0.852010726145,-0.726106175538,-0.482924750605,-0.61075810554,-0.616611301845,-0.28334723133,-0.466249798955,-0.143693354442,-0.472062819479,1.22657045375,1.34078470718,1.38786471569,1.32622173236,1.05715490739,0.875818968496,0.992212027304,0.804812168458,0.859776492319,0.998277513721,1.1397197748,1.20221455344,1.02872033081,0.977805123628,4.22966299561,4.39762135834,4.08245891199,4.09277125955,-0.352433198414,-0.0846147285005,-0.186537238722,-0.336043169525,-0.128860416255,-0.250009937834,1.04768170841,1.15967875088,0.986164652347,1.0379421828,1.21140656107,1.15202678121,4.46860598195,4.66968464951,4.37954330778,4.31816890486,-1.4926458601,4.66928211856\n    \n};\n\nconst int NST810 = 810;\nconst double AREA810[NST810] = {\n    0.0155399449398,0.0161688300168,0.0152359394744,0.015635052276,0.0153558882534,0.0159236110923,0.0154529053544,0.0153777231596,0.0158747930543,0.0174902452703,0.0163646282334,0.0157136301376,0.0156947663925,0.0145642782173,0.0157282224595,0.0154152162751,0.015779657788,0.0159386383424,0.0152711411302,0.0153143450106,0.0159087696578,0.0155769700931,0.015613521867,0.0156582629643,0.0186824678569,0.0156193418111,0.0156050348831,0.0156960244849,0.0153404024449,0.0154906822447,0.0154632406866,0.0152584092614,0.015599537725,0.0155817342077,0.0158350555085,0.0153990220042,0.0156023182605,0.0158195731828,0.0156548519196,0.0146961693208,0.0155412355947,0.0155752599902,0.0150223780388,0.015313336346,0.0158349272788,0.0160724348125,0.0155368260347,0.0149373140279,0.0157730889448,0.0157028213385,0.015159446115,0.0154160767054,0.0153732314094,0.0156760858905,0.0157690076316,0.0155355294589,0.0156848525259,0.015460908645,0.0152319716482,0.0158303383026,0.0154092622719,0.0155088656775,0.0150326993552,0.0147998287839,0.0156010446847,0.0155443426125,0.0154889660265,0.0154580631902,0.0156977203383,0.0149563878115,0.0152884062191,0.0156386129204,0.0154984108545,0.0156925427389,0.0157854031038,0.0154469109323,0.015500800021,0.0154345776176,0.0153468597298,0.0152057365421,0.0157084125364,0.0154844579875,0.0145010125618,0.0153402715544,0.014976082705,0.0162568861633,0.0152803898975,0.0154015794122,0.017710361432,0.0160664318192,0.0146953871472,0.0161405834856,0.014902931141,0.0155670857324,0.0147319773892,0.0161980065045,0.016155481072,0.0148801007133,0.0149737524107,0.0149600137977,0.0159481144975,0.0154579258458,0.0151676044853,0.0149665356148,0.0146198102419,0.0180849835452,0.0144245195022,0.0160017031572,0.0154600143893,0.0157503270276,0.0153229506396,0.0154319022186,0.0157477237024,0.0157672725891,0.0155156095423,0.0153749381926,0.0156847813367,0.0157283502291,0.0154099653366,0.0148154835412,0.0164561590436,0.014841434964,0.0151020895547,0.0152537318006,0.0153632410112,0.0157432915211,0.0156431268058,0.0154477718599,0.0154290916632,0.0155501416761,0.0156861945732,0.0153664452299,0.0157170657907,0.0154759251235,0.01554689155,0.015522451902,0.0157212733489,0.0153171612847,0.0152876405088,0.015872643788,0.0153631474043,0.0157305583241,0.0162098987446,0.0152735357551,0.0156391329427,0.0155053972569,0.0152576065813,0.0157120977976,0.0157281904876,0.0154568986031,0.0155336319987,0.016047960057,0.0151064878971,0.0159589669405,0.0151741779739,0.015869857075,0.0153159508664,0.0158481199992,0.0152373510461,0.0159498165727,0.0152287094446,0.0161735119254,0.0150975071303,0.0155146149029,0.015815491883,0.0153824439585,0.0151242477404,0.0158710078242,0.0157886224041,0.0152794757919,0.0158226018691,0.0152143719559,0.0156739183651,0.0154831545336,0.0156813246914,0.0157554029423,0.0154131377166,0.0156452036988,0.0153045024865,0.014590539258,0.0161237805596,0.0163084272534,0.0145803135472,0.0163599773157,0.0158004623144,0.0154566950715,0.0154390507417,0.015769724491,0.0155141702916,0.015565748242,0.0156235024824,0.0145857930979,0.0148226055962,0.0187275797572,0.0152589204403,0.0154136514979,0.0188383697097,0.0162366870957,0.0159652812986,0.014604989175,0.0154299624986,0.0156195182983,0.0156205300061,0.0154968188677,0.0161028502029,0.0164354677854,0.0147009664917,0.0151136630909,0.0149786485471,0.0159951008312,0.0152642358339,0.0151457476976,0.0158332118753,0.0157245038702,0.0154457120771,0.0156729414993,0.0155431843474,0.0155637134586,0.0155135342162,0.0152519980458,0.0158033566085,0.0156029898167,0.0155093862206,0.0154472085807,0.0154486843824,0.0155858795215,0.0154236925269,0.0156844148136,0.0156703679453,0.0154246791842,0.0145281989467,0.0155531540567,0.0154667115486,0.0156770726485,0.0154395269551,0.0154665333667,0.0156705935873,0.016051801765,0.01525396156,0.0152897022652,0.0159027962616,0.0152124824979,0.0154658000877,0.0152622666542,0.0145668436324,0.0154259147017,0.015520003151,0.0153165109989,0.0157497234302,0.0154607412251,0.0157411526229,0.0151472026079,0.0158822798847,0.0157607498885,0.0154272978643,0.0152921971868,0.0158973490236,0.0153633521162,0.0154233367756,0.0147483700824,0.0153360435566,0.0155460555431,0.0155255976301,0.0157103848373,0.0156514828116,0.0154394537501,0.0152042485027,0.0150729621588,0.0158180729598,0.0155989613565,0.0156461586305,0.0154417401625,0.0150778509989,0.0152248086163,0.015059077409,0.0151894285875,0.0161083824718,0.0148238857638,0.0151609341507,0.0155418903944,0.0158175559437,0.0153657364129,0.0155454385132,0.0156120338461,0.0152148534702,0.015257262332,0.0158558949509,0.0156911396872,0.0156151835111,0.0155743994118,0.015160463759,0.0155182329696,0.0152040897519,0.0161731387997,0.0150166689044,0.0152783457005,0.0160057585954,0.0152909294022,0.0157363616502,0.0153376531741,0.0145669274091,0.0158161050679,0.0157834094686,0.0152522023849,0.0159467115873,0.0151489750456,0.0156110820062,0.0155171814289,0.0156417373362,0.0155410581671,0.0154291996009,0.015773852633,0.0157910853035,0.015360019029,0.0157471759147,0.0160225602049,0.0148506229406,0.015219881556,0.0147717471355,0.0151886607936,0.014774510378,0.0148257652767,0.0159150775923,0.015927558412,0.0158348795006,0.0152443180483,0.0144689559696,0.0160226385721,0.0156921580201,0.015507155179,0.014465359073,0.0181352761708,0.0151438021758,0.0151042732684,0.0160379451752,0.0160440063384,0.0151393784557,0.0156796699989,0.0158287632208,0.0147558460765,0.0162405112836,0.015125478759,0.015012727976,0.0150398112594,0.0148479485204,0.0150714218599,0.0149083671623,0.0155065774988,0.0151670032767,0.0158508224094,0.0149888400162,0.015777268001,0.0152869741177,0.0156554866071,0.0151728953281,0.0160379019337,0.0149479824402,0.0160392668355,0.0149681767776,0.0160231265194,0.0150402066735,0.0164241380732,0.0164394988868,0.0155087685326,0.0157426094769,0.0153531417364,0.0157856486038,0.0148897693126,0.0147463525739,0.0153597406237,0.0157431931311,0.0152944977655,0.0146302836822,0.0158444189001,0.0155046754714,0.015932673263,0.0154131977854,0.0145394933565,0.0162226798271,0.0152154809821,0.0152180732821,0.0158329878039,0.015232071224,0.0157874687918,0.0158045735371,0.0153777232787,0.0154292479589,0.0155751729109,0.0157230412015,0.0155033389394,0.0155813074162,0.0150214165745,0.015539165494,0.0156178908509,0.0144291833142,0.0162306071262,0.0154755448052,0.0153252114028,0.0152683257684,0.015875441194,0.0160672129996,0.0154734774257,0.0157165834775,0.0154474046194,0.0155215422069,0.0155830115374,0.0153475455913,0.0156296083195,0.0155015351418,0.0156140231664,0.0156018769504,0.0152743415252,0.0157989211998,0.0160613858664,0.0150870166583,0.015251950858,0.015981171476,0.0155502121309,0.0156533693019,0.0156069314817,0.0155283120956,0.0158202720738,0.0153550589678,0.0156177789714,0.0154955910007,0.0155548720626,0.0155184971987,0.0154461587371,0.0152973209231,0.0156626694744,0.0156926264625,0.015419938785,0.0146024801815,0.0157737590324,0.015532485276,0.0156417485879,0.0155003576053,0.0156169670064,0.0153153142485,0.0158030221745,0.0153434264132,0.0152168452157,0.0148578214715,0.01524747918,0.0147067375346,0.0151846769821,0.0145585410226,0.0156589439081,0.0155005132633,0.0156277422476,0.0155614743451,0.0154851197868,0.0156314026664,0.0148666311924,0.0149779309099,0.0147105087631,0.0153149747398,0.0145277010658,0.0147016535814,0.0161676647174,0.0164110350719,0.0147140883999,0.0155060204557,0.0156761507655,0.0151326604914,0.0152104587388,0.0159606734803,0.01579631224,0.0153949285732,0.0157879427039,0.015094875273,0.014787246892,0.0159363552828,0.015030572553,0.0151760048777,0.0158777790783,0.0158724844994,0.0150246130998,0.0147812832773,0.0162737603629,0.0153238141489,0.0158640020727,0.0157085229603,0.0154874754355,0.015733368645,0.0153545034547,0.0156021732374,0.0156494388971,0.0152603609219,0.0159662233062,0.0158689543631,0.0150107902162,0.0153851719763,0.015205227914,0.0162222053406,0.0146980856372,0.0189891577647,0.01482031712,0.0160435649705,0.0145577564127,0.0147920019171,0.0185750216189,0.014674775146,0.014516162197,0.0156290382337,0.0148649263663,0.0158350026962,0.0154987637488,0.0156386766585,0.0159727808907,0.0147210156805,0.0163367469573,0.0148072941544,0.014478141671,0.0162702205567,0.0148523604368,0.0186473978612,0.0153755181868,0.0146412082976,0.015881494549,0.0152173313823,0.0157808665253,0.0152586903818,0.0155487480958,0.0156965835095,0.0152870395786,0.0152761824983,0.01603938378,0.0144313455141,0.0155620676932,0.0156977038632,0.0155175646661,0.0154812265644,0.0154605325685,0.0154351390741,0.0156512633931,0.0151361973453,0.0159373811297,0.0156289129506,0.0153115425739,0.0157331767809,0.0152253982681,0.0155492495123,0.0145984557019,0.0147893916555,0.0151494823247,0.0175015000777,0.0165454665136,0.0150333006837,0.0162039970294,0.0148161232967,0.0149909813969,0.0153097558434,0.0153792477188,0.0157481760648,0.0158105251486,0.0153547722764,0.0153760713784,0.0158545528697,0.0156327036004,0.0154104165734,0.015554327854,0.0153671554,0.0156436776286,0.0154808236972,0.0159742467573,0.0152835902576,0.0144585578932,0.0155636303186,0.0147634787954,0.015091166156,0.0181420583019,0.0162755711076,0.0144693423654,0.0165211687598,0.01486044244,0.015021245315,0.0153755611214,0.0165815228193,0.0147439629769,0.0159792998114,0.0149589472055,0.0153100797616,0.0156420815553,0.0155361702902,0.0152444469392,0.0155739061481,0.0154995100714,0.0147442790055,0.0151735693801,0.0151472669633,0.0157616779427,0.015115592321,0.0147793239021,0.0148002092789,0.0154573456078,0.0153286350668,0.0154619035739,0.0154791879461,0.0152394611225,0.0154341450801,0.0158924793705,0.0148451364993,0.0153777690168,0.0152807197667,0.0159861602165,0.0150351328122,0.0158959638723,0.0150768299248,0.0151599585485,0.0159246199323,0.0155063427108,0.0155947200148,0.015148146497,0.0148419864635,0.0145433300534,0.0158051780706,0.0153246050351,0.0157877084822,0.0153858280826,0.0156888379358,0.0155032095526,0.0152028634685,0.015761802524,0.0154634674429,0.015157404781,0.0155159839232,0.0151496535391,0.0158557952489,0.0159899411052,0.0152054480183,0.0156526252257,0.0154882125311,0.0153513384175,0.0157674884118,0.015448180335,0.0156811980801,0.0156524497701,0.0153074230961,0.0152235677661,0.0159324439868,0.0157025302662,0.0152220591218,0.0158861731822,0.0152179419289,0.0158215695303,0.0153239582366,0.0158602805066,0.0152877963309,0.0154997509467,0.014514280969,0.0157678232221,0.0154349864531,0.0156563688998,0.0154720540475,0.0155140131297,0.0155912356862,0.0156539204443,0.0153893596498,0.0152429968179,0.0151473023816,0.0149507176569,0.0160486792047,0.0162984128772,0.0147684498732,0.0152771777726,0.0159584345444,0.0157487304349,0.0154704670357,0.0157880643931,0.0153116047098,0.0146351247135,0.0154598881482,0.0145867790988,0.0161231304247,0.0156631372775,0.0155061899621,0.0152304235393,0.0152241853971,0.0158712373182,0.0150811701488,0.015033900187,0.0155220647055,0.0156203161384,0.01531504288,0.0156895835308,0.0159741609657,0.0157913424458,0.0156471173995,0.0155994659912,0.0155860925976,0.0155340850339,0.0155244732276,0.0155466199557,0.0156468877236,0.0153846629968,0.0159032237097,0.015402504729,0.0158473579641,0.0153948476701,0.0159461459629,0.0152408607568,0.0157715693305,0.0155167702081,0.0154616350936,0.0157017946822,0.0154363983174,0.0156816947393,0.0158375971598,0.0153393600483,0.0153738696339,0.0157989845461,0.0157353206739,0.0153182296937,0.0158874865229,0.0156524358726,0.0156278613191,0.0154993633034,0.0153769862723,0.0158722536773,0.0152432185031,0.01427368713,0.0192511719647,0.0148282019053,0.0159861577235,0.015441016981,0.0147719061745,0.0160086273712,0.0156653806232,0.0155421538643,0.0153878935102,0.0153151297628,0.0155935486758,0.0153476082771,0.0158123476985,0.0154552740884,0.0156002047103,0.0160236714549,0.0150662692568,0.0159734331235,0.0146978229999,0.0153001202366,0.0153695540603,0.0157371084867,0.0150849376796,0.0154910764744,0.0153877926528,0.0156387335706,0.0158990378835,0.0157865553146,0.0152814912493,0.015581374229,0.0153704244969,0.0150380829671,0.015687118104,0.0152981040938,0.0155938424177,0.0155894088768,0.0155861630986,0.0154956967967,0.0156323092487,0.0154122507495,0.0157820702192,0.0153705593272,0.0162488762348,0.0144549978701,0.0150556490171,0.0155289710384,0.0157284997044,0.0153678789915,0.0151495734602,0.018051197456,0.0145395729207,0.0161551594732,0.0145433355263,0.0163338450793,0.015671684427,0.0153780403049,0.0158321635723,0.0151945976907,0.0157700458657,0.0154111662754,0.015717265893,0.0155275674272,0.0155812621153,0.0153875806968,0.0158126608221,0.0153645862231,0.0153681289065,0.0157403394458,0.0152658092655,0.0158480596713,0.0154204706004,0.0155676078012,0.0159183974371,0.015280121391,0.0153067924223,0.0157698679913,0.0153938736211,0.0153395926754,0.0150860390945,0.0159344494194,0.0157041634983,0.0153745109373,0.0154497487242,0.0156910669375,0.0155871013938,0.0153820275619,0.0155434819478,0.0154167577349\n    \n};\nconst double PHI810[NST810] = {\n    0.761235344749,1.9907799862,1.74308537818,1.13285973652,0.744807762039,0.811390672278,0.573663673569,0.569665610298,0.640925058905,0.9957509979,1.11270146186,0.630470744584,0.589255334008,0.920113481925,0.805195233172,0.74391745672,0.810484495401,0.971499566487,0.922290070985,1.08675169932,0.984215227207,0.0870089156405,0.0958438678254,0.207026230325,1.04078533853,1.24830456458,0.601952840767,1.04269914654,1.10794614876,1.61937281881,1.58810902227,1.79957224546,1.8905438517,1.75062234696,1.71321038578,1.78322606962,1.88855495981,2.50271096916,2.04562810142,2.17867676852,2.03103836227,1.72194297069,1.768993322,1.9428548956,1.88227813805,1.62426805604,1.65753917056,2.64362387222,2.22579491025,1.78549664562,1.89282020333,1.19173221259,1.91221837231,1.96031686134,1.85306352834,1.92997716653,2.0429054046,2.0776893509,2.13929090427,2.08831215823,2.14026955009,2.24916060599,2.18601947379,1.21671903564,1.06340108596,1.14665166373,1.5503286032,1.68342590504,1.57443477542,1.51212888609,1.45503166399,1.57635198489,1.40623883952,1.46388995556,1.53212710438,0.579320648763,0.638964151663,0.64130386856,0.959686713583,0.914218077724,1.07134559116,0.961338189494,0.499135509519,0.606622708643,0.45534991895,0.41619839458,0.937915784805,0.832324015254,1.29800295306,1.08724009648,1.24072568412,1.1407130129,1.38519269718,1.54142317499,1.43805992803,1.19529714845,1.37566316099,0.963732479879,1.01905306723,0.94501242548,0.869173489878,0.779849646216,0.85100401437,0.797078357418,0.752927925321,0.76840566515,0.860920476383,0.841102424689,0.266213938853,0.116283372765,0.130140992182,0.501054229489,0.606039994718,0.311572400161,0.422922843712,0.243852870199,0.315500241749,0.468557058092,0.422356879861,1.1125086502,0.958748913594,0.913799713065,0.941044597428,0.924999397948,1.00270926956,0.912709009241,1.02722050137,1.09404968668,0.78357326541,0.711991117078,0.536955208067,0.426898283816,0.451272569239,0.375473271083,1.10921998997,1.00908091061,0.927656043726,0.956555063956,1.13726573271,1.06536991884,0.827624058967,0.818616383403,2.15539957762,2.1403741015,1.86012246577,1.9283034318,1.14854140389,1.60383098679,1.79552867592,1.19664753957,1.11885711677,1.00756830744,0.981687573214,1.48478633662,1.41666153694,1.73587400916,1.62588905099,1.56190790224,1.7792105407,1.70832765324,1.60129221719,1.6613854048,1.58944176445,1.63407881306,1.74698125396,1.88415264793,2.0213429251,1.99549976797,2.43699812943,2.51916283657,2.59309681952,2.54613388987,2.6809298597,2.58493516932,2.32760015267,2.22008432684,2.37182127517,2.40931294402,2.34807882283,2.25901156872,2.25485222679,2.30703169493,2.12304978353,2.20585158199,2.10548920302,2.03909189616,2.0793906011,2.21636658833,2.26020272486,2.18904743545,1.95830579974,2.063928047,2.09554119589,2.13842824883,1.83935987014,2.05233891192,2.08809017173,1.87176685935,1.91242094697,2.01472364361,1.92861784421,1.90615738372,1.72317685214,1.80104203654,1.61211596431,1.70390379549,1.74431141196,1.85193293142,2.02975158073,1.92518412209,1.88180696856,2.06110277897,1.98604643907,1.55404275661,1.52987019677,1.42789966929,1.34940356161,1.87575529744,1.90772921527,2.09841150585,2.18647389927,1.99841202159,1.98296057586,2.17129976075,2.06807946683,1.85465226337,1.72161476483,1.7449411259,1.73504620377,1.65586165643,1.691485677,1.67776767111,1.59112108022,1.78605145703,1.80271466271,1.7147579086,1.61157348209,2.55196279129,2.53747164325,2.2814816511,2.36941198838,2.46212150949,2.46262957702,1.80398427186,1.76278250033,1.74090911529,1.8011195722,1.64657482782,1.75063373083,1.90544833727,1.96632133075,2.0556773744,1.95304003835,2.11819749327,2.07603952148,1.98040776707,1.81012264443,1.9211660422,1.32054317013,1.29168309148,1.432122476,2.11648572723,2.07279619927,2.13597358068,2.24984061811,2.3084602767,2.61900257942,2.74190477644,2.43142977934,2.68341846674,2.13881140785,1.98525512936,2.76491113589,2.071182341,2.05876376738,2.1442111516,2.14870372926,2.23696643608,1.31400899627,1.50990642761,1.48320492746,1.38895645961,1.0106585044,1.0877175052,1.24998252947,1.43509702019,1.33343450404,1.64651627031,1.76914753576,1.67349462766,1.7175096093,1.57324290943,1.61022874877,1.53839023139,1.43906811211,1.42540636833,1.2949766947,1.40378509114,1.4663397518,1.94166673214,1.98022678848,1.91611481127,1.35045673255,1.27782577676,1.31692267162,1.24878589762,1.1655776961,1.13581414134,1.09125239974,0.798500581598,0.752925465842,0.909315995352,0.826883330142,0.977309848855,0.93941936385,0.988683017589,1.23583012017,1.125710774,1.08764097972,0.298027375329,0.394450857336,0.397706548678,0.577632827417,0.482653128545,0.601359100092,0.498023556822,1.0455630863,0.964902713256,1.03669385481,0.94831156513,0.588360757581,0.495362315118,0.530219673813,0.48173169098,0.404882241764,0.294465231641,0.286235171523,0.472771369844,0.387859214398,1.46408682957,1.4675255329,1.55955809957,1.09750493429,1.18819119665,1.13823193672,1.2491920515,1.28007049181,0.666576992307,0.842814659306,0.773226288802,0.85070686781,0.757796962509,0.659018652583,0.814460303351,0.929120828633,0.969519077838,0.90482517778,0.58837531789,0.670107165548,0.478438162865,0.462232674571,0.6614035053,0.564950728973,0.189676826278,0.268492634117,0.365792941815,0.255358789581,0.670556342233,0.573130451746,1.42945424501,1.39146601864,1.28676191153,1.21693065272,1.08277468093,1.15410767869,1.3075253357,1.26643048847,1.12556913585,1.06910960119,1.12898987205,1.2408594782,1.29270067265,1.23652582317,0.990035269927,0.963857954751,1.04717651041,1.24774783706,1.04320198271,1.16911699065,1.06555613937,1.10990175907,1.2128931931,1.18607268548,1.09730020896,1.28482221611,1.29881705528,1.37288771837,1.04201843387,0.891984278131,0.93291989753,0.968820826365,0.745814955874,0.641194617662,0.558884389265,0.59651914183,0.930133874494,0.871596371957,0.885789234657,0.775868891543,0.760024452906,0.707415568795,2.03476729468,2.00447654229,1.95123697549,1.84544633866,1.89642025794,1.82074328912,1.76988172319,1.66101235127,1.69601665493,1.62407778358,1.73358272121,1.65993722498,1.55172483682,1.51370923836,1.47713591028,1.33701354061,1.30210594544,1.51379355431,1.44595976224,1.37652230524,1.47897628293,3.08526437873,3.05408264657,3.00565537548,2.8549592743,2.75364843275,2.90659222468,2.7001533501,2.72750520894,2.82437692601,2.35162324132,2.23625296492,2.38074452727,2.27308387779,2.41004043507,2.31647991215,1.98619390466,2.02653854458,1.95309279767,1.84313335998,1.87589919196,1.80589611739,2.04484614355,1.97171810104,2.07022771324,1.8924522865,1.93237683552,2.11195033611,2.05914681992,2.10391407489,2.2040554211,1.29083019783,1.22179921781,1.56598714765,1.58736130143,1.63156170933,1.47532873838,1.40590781075,1.45271176912,1.54852866531,1.53037371794,1.5935291728,1.42294305436,1.79895063997,1.9253740348,1.6108688359,1.58608319819,1.48253511331,1.40099156115,2.40125995986,2.43415687932,2.29344258196,2.22269544053,2.25225234578,2.3547493948,2.28446346793,2.37276766669,2.35769560787,2.2557620089,1.97259890286,1.91640936054,2.08315775274,2.12544003192,2.04864293497,1.9509141289,1.81933065372,1.85349274589,1.59090309326,1.48876373957,1.49298742076,1.3974627667,1.39908938876,2.22191153784,2.29097902194,2.22077440235,2.5831435419,2.46999631477,2.42029630967,2.638849835,2.74196473723,2.73246072674,2.81409891073,2.90779480634,2.80526447542,2.9691530062,2.90934560297,2.60905803108,2.73486036523,2.71941324691,2.53320826245,2.55055473455,2.64190826269,2.29355793346,2.39631509724,2.24494482977,2.73244205791,2.7919587046,2.8974895648,2.76620731607,2.94825901964,2.86738859263,2.28694367534,2.17444814987,2.11743948516,2.09388214032,2.94479004375,2.8587492184,2.83820661455,3.04408446235,2.51074342233,2.61718878556,2.73812156979,2.52953141545,2.35986453892,2.34906947584,2.40941531455,2.17428386871,2.25044289154,2.16845459997,2.23757200147,2.41323281544,2.33283803505,1.00391583579,1.11135709714,0.968476644987,1.04235118587,1.1944875694,1.27365381004,1.74387335272,1.8073677623,1.63714322217,1.49536986464,1.67018750759,1.56648846637,0.990144320077,1.0170529947,1.06819108967,1.12864332525,1.17945725188,1.2355712295,1.16012662607,1.40189452203,1.3457533592,1.3644884234,1.27013577134,0.230063242881,0.116262370347,0.309185889405,0.302219934738,0.120703439101,0.221625251809,0.669819346728,0.764594763208,0.853591450139,0.861973205963,0.782418546985,0.679604330175,0.647109750253,0.862586213066,0.686538743328,0.791438660236,1.88925207248,1.54905702432,1.44959067588,0.309093225626,0.399534039715,0.228250271607,0.28685497975,0.632649201945,0.728345847975,0.738589017001,0.659996935113,0.352230912132,0.438661275709,0.394446340949,0.494336369633,0.537902827199,0.556572590688,0.624654197049,0.732545527544,0.790462720359,0.739589485765,0.796619204958,0.948382545873,0.90774863209,1.38927591559,1.35752264302,1.24974928326,1.31514915863,1.17196430715,1.20591039905,1.09722464253,1.22397068332,1.12322502373,1.07486886444,1.27514115808,1.21205910083,1.10755632246,1.24013242785,1.14448967648,1.51716303867,1.40875448423,1.37927057382,1.45736328392,1.47833565549,1.44055331589,1.40594831086,1.32971409787,1.296109366,1.25722566086,2.81099906808,2.89009478558,2.99447575482,2.98019230368,2.44877069806,2.63880293467,2.54022370944,2.45008289496,2.32383239919,2.2169793479,2.163773899,2.21047596438,1.3729997844,1.27034319395,1.3857424194,1.43580110674,1.20721946998,1.26028054111,1.8202957762,1.76380501949,1.65913234488,1.76094375528,1.64896565235,1.60324350092,1.93593611503,1.90321943664,2.04182866576,2.11602120461,2.07713267666,1.97057229076,1.88213022882,1.91800152333,1.83328108752,2.67494095714,2.56460510314,2.66453206801,2.39475184508,2.56945217445,2.46680955043,2.58440194391,2.39543967723,2.49864602871,2.58269480186,2.22776094829,2.29394673264,2.26319971782,2.37066740051,2.31356191797,2.33704481507,2.23639666772,2.22406384835,2.42234621325,2.43368631447,2.63167481287,2.53195676762,2.51127221208,2.68460196319,2.5809871131,2.7119075955,2.62416044741,2.52873928658,1.29307375131,1.22518784904,1.36770575145,1.33358156531,1.18389997706,1.14976049194,1.49356487572,1.59813785007,1.45747792119,1.52849789996,0.718433712502,0.775952287542,0.690629843936,0.877765494861,0.903161706065,0.828326457353,1.68628841604,1.58134983402,1.57427610689,1.70840413925,1.62902757045,1.79833552516,1.80141785661,1.69625662185,1.61403889589,1.43047112503,1.51529838346,1.31940454059,1.30545512289,1.39375697661,1.49245036604,0.409932373008,0.493930849548,0.589521826324,0.449091056687,0.560887884041,0.620232193893,0.882826446728,0.868041144499,0.796584483885,0.688394137383,0.67858477506,0.771591896229,1.40475525356,1.30273083534,1.48645786417,1.17444392354,1.23340471854,1.15033311206,1.27899951503,1.5894330749,1.76286777861,1.69733489352,1.55889450333,1.63068276775,1.72656436322,2.38621119334,2.49576879219,2.55039456196,2.48061364337,2.3667691305,2.32493802467,2.07420113085,1.99826738195,2.18045453148,2.19482242807,2.10394758701,2.01422209726,2.28899090371,2.29778623755,2.38563603342,2.47895365085,2.40291093684,2.48928853688,2.62423674844,2.55488277319,2.56890666542,2.4590923728,2.40414553891,2.44659576737,2.8749718521,2.81131650088,2.70836686266,2.80425424646,2.69963614368,2.66012124701,1.6326602455,1.83897308665,1.76871413131,1.66646580482,1.80725906044,1.70346804382,1.65786168276,1.66913992992,1.74368185905,1.84382279091,1.35797251007,1.46319845393,1.41813994799,1.33626993224,1.5478210017,1.52423723584,1.77890798871,1.76874196628,1.96575752744,1.87663638334,1.95692703893,1.85767738414\n    \n};\nconst double THETA810[NST810] = {\n    2.99361173966,3.75753618789,0.67105552789,2.01998695478,1.4115179321,1.53767283164,3.88917819302,3.1560996294,2.99406156847,3.4522951692,3.69913111233,3.29575799163,3.48273765675,3.33822263466,3.11431629146,3.25532805691,3.36397547356,3.22276252773,3.10987254112,2.99595633004,2.99555587343,-0.802577954985,4.13305607898,4.18883486531,0.592494997397,4.64452327901,4.08041154755,4.36777363345,4.46582359049,3.05254239333,2.94644705102,0.296399504609,0.497772335423,1.57506601931,1.46833626979,1.38193942571,1.40346167765,1.11796302101,3.3190218868,2.32873178285,3.19779018756,4.33131669075,4.23422643967,4.31071286456,4.22297537478,-0.120429699948,-0.799340981247,-0.575814653769,0.0444949819274,4.42569737802,4.41651103992,-1.41758847407,0.304975908016,0.40324214009,0.68757300987,0.599023849456,0.613267499417,0.723436033678,-1.44738505129,-1.33930739731,-1.2222793992,-1.21143438025,0.758212782068,1.95508047972,2.21375566248,2.14287751534,1.83462074949,1.65820909356,1.63997521883,1.72670732067,0.424127325784,0.256930057682,0.319617505761,0.236344686572,0.636439498029,1.2626353063,1.42790687535,1.11178019669,1.38276874033,1.51053374485,1.37339464516,2.15692982089,2.77079678403,2.84257447549,3.16058108153,2.90957740524,2.87147708102,2.86588972935,3.21905779756,3.21921097976,3.10048811181,3.10761986762,3.21661453493,3.12956844797,3.10744935847,3.61196726786,3.46708448297,3.56544508823,3.68212716265,3.79024139388,-0.297681882747,-0.202973503334,-0.425240929855,-0.654283629936,-0.506659629438,-0.785752600213,-0.900151070979,-1.03999490845,4.55425622381,0.346622495736,1.20073216359,0.853658262687,0.933171624353,1.51785887367,1.00929760615,1.19914659719,0.913535254464,1.24888893125,1.4671116433,0.522961085042,0.302785730934,-0.638921532609,-0.509187952445,-1.12180324606,-1.32246082927,-1.26166390992,-1.05651062777,4.26066367888,3.99384998601,4.11283444314,4.23927739361,4.22422170497,4.66123830742,4.4694735498,-1.49782686398,-1.45501868211,-1.54498349192,4.6050916995,4.66726917753,4.57869199333,-1.50042547909,-1.34652186524,2.02998738815,1.89319008704,1.59810453183,1.51316927909,2.88807623428,2.06508980349,2.10448691468,-0.207488132281,-0.117208896291,-0.141169688851,-0.260693952724,0.0479401642538,0.132098880014,0.380526630024,0.360413828704,0.444263839163,0.483677456348,0.568046747962,0.549029299402,-0.0156576814803,0.0696903773634,0.174610453047,0.193839536469,0.789902746741,0.918985063301,0.810656206224,1.4695508101,1.78681974353,1.63749471331,1.45855318806,1.20101832044,1.26155071409,1.85959554357,1.81397411204,1.6077731727,1.74913169726,2.00114348056,2.22227445615,2.0905010088,0.954253964962,0.968525065176,0.897600506747,1.43510880868,1.5336584825,1.64696208668,1.45236342681,1.58465817072,1.67934769891,1.31269819425,1.32395230418,1.09883581254,1.19928616283,2.52338625393,2.56300087243,2.32638557455,2.32458655413,2.43357716613,2.44971978356,3.15720335224,3.04293049926,3.08474387085,3.00849228187,4.3407038502,4.14093250551,4.04604331616,4.03154677317,4.08560129403,4.11436036094,3.92048801957,3.96402220897,3.88241441405,-0.329698806599,-0.437649884754,-0.464106088728,-0.381021199358,-0.743026892308,-0.525487061729,-0.53885171373,-0.610465191956,-0.593187769358,-0.708150836661,-0.745644771451,-0.787110716877,-0.848138630989,-0.975038380711,-0.874099834742,-0.382710264467,-0.302887911741,-0.199631502613,-0.698322141877,-0.622909829313,-0.668731123595,-0.565027240883,-0.49030936418,-0.517915160759,-0.665854176986,-0.862744952285,-0.54518877841,-0.631730517533,-0.555648615973,-0.372073822178,0.109512809623,0.00495404137551,4.52470455275,4.62398933103,-1.56230892902,-1.55903194948,4.62440917219,-1.55551120596,4.50652542807,4.51569169687,4.6132629811,-1.55689147553,-1.34842952025,-1.45828028811,-1.45488248027,-1.55303050464,-1.44870949578,-1.56279928692,0.516232415461,0.406918943347,0.297574558345,-1.45209054631,-1.33351356442,-1.00862679507,-1.43913930965,-0.0155910567153,0.94138294182,2.79319332128,2.96427930828,4.55103827409,3.70291633069,3.57097993804,3.37063670623,3.50469130416,3.57434755789,1.99896122746,2.04685365829,1.93066522061,1.91157186265,2.41919659864,2.33784583432,2.18630181376,2.1368113569,2.10851015001,1.85428402341,1.98860449603,1.96889679527,1.76490191522,1.33954621127,1.44635908659,1.53452928996,1.51380698725,1.21310207087,1.38518512145,1.40476173934,1.31885682417,1.00580816307,1.11396897398,1.20328884689,1.00295170628,1.08809363481,1.19304035692,1.27896286339,1.07234640376,1.26889788197,1.16707239582,1.26912890512,1.13403576807,1.2687467565,1.01564639085,1.15793577614,1.03901394755,1.6158920927,1.47332368221,1.47106932792,1.58721240537,1.86616995205,2.01548016894,2.31360052706,2.00783170172,1.89076769845,1.60718443621,1.65439098839,1.82850297797,1.7472492803,1.9552145698,2.01956157781,2.20096784233,2.3650838536,2.55134731759,3.56167422543,3.38291626812,3.4622430421,3.85411872985,3.79797773398,3.96029605757,3.30036371734,3.39892602629,3.23757608522,3.43404211365,3.50687087584,3.31591631673,3.32311438973,3.43513778271,3.58934907964,3.74660880605,3.51573939448,3.59955499128,3.83960160339,3.77376495759,-0.070642435343,-0.0476657595302,0.0746122736847,0.178515505843,-0.149869345202,-0.258967183586,-0.213009656173,-0.435437534541,-0.442623603997,-0.545321311967,-0.85924588124,-0.480417561276,-0.593026967011,-1.2830242631,-0.824932938928,-0.728617461815,0.619889972251,0.512102249993,0.495355044675,0.580501708835,0.0869903023192,-0.0100159427132,0.111545481033,0.00657256309772,0.396108434398,0.301062875044,0.190158927638,0.198070659385,0.302100587872,0.389983621965,-0.721598033182,-0.852943502475,-0.926473716563,-0.407944970501,-0.471140693245,-0.32043840114,-0.34836834103,-1.11837913242,-1.06309143932,-1.2967639082,-1.2462969923,-1.24111010814,-1.13374269019,3.97367771804,4.15175024356,4.01980994717,4.14360048935,3.91297176944,4.67645173245,-1.5548719427,4.59732820201,4.4130688711,4.37685948919,4.26151425985,4.50230415041,4.52644786686,4.25873298276,4.39899890904,1.84889971504,1.73537249515,1.92989548642,1.90028149552,1.70754923887,1.79067821359,2.89932927169,2.8699995385,2.68762806429,2.76560575987,2.50141690915,2.58348622779,2.56013869712,2.74175630377,2.64005947811,-0.0778466887883,-0.185304156544,-0.140024549741,-0.0573757732911,-0.270690780929,-0.246322116968,-0.539176118195,0.990700789532,1.94907517525,1.37361273922,1.41766478606,1.77420705079,1.66391995379,1.92284036774,2.04957617965,2.3009051504,1.20032060567,1.33876005397,1.34297634439,1.17752156648,1.09355121871,2.65541471788,2.76756874913,2.85136766307,2.82155306833,2.63195015687,2.71381142162,2.22041432492,2.03988474006,2.09340859275,2.12548521414,2.23491450009,4.39494977868,4.29781746055,4.18025519429,4.15133633445,4.54399307096,4.454677352,4.4393133955,4.62487860502,4.52876118868,4.6272208598,4.53699078454,4.44497209632,4.25488246903,4.06872385808,4.15212014965,4.0783460565,-1.05947338184,-1.24922383621,-1.00019610287,-1.09850899768,-1.14192363828,-1.08480748067,-1.06201985057,-0.905239329422,-1.08095893904,-0.972208886092,-0.83636045581,-0.790807584447,-0.392918136404,-0.299487880721,-0.13597295552,-0.0948080036662,0.202774468065,0.110305724173,0.191620195835,0.0694728488215,-0.0297469398502,-0.00124000916624,-0.166505358704,-0.0655265580741,-1.46372596434,-1.46674092325,-1.25495627046,-1.30449872855,-1.39296370137,4.59198893994,4.6984198017,4.44189482062,-1.19455775765,-1.20011353542,-1.34191870048,-1.39167208443,-0.692593282545,-0.946111977085,-0.43973525767,-1.17188082798,-1.1775427085,-0.684616122367,-0.317028976832,0.0973893530471,-0.209268633339,0.0450863577826,-0.0549441472477,-0.254234896002,-0.340540501533,0.163135046168,0.146191269068,0.294965273218,0.51469525919,0.268843104851,0.231942372301,0.79810631634,0.679753426886,0.966345527172,2.94360408496,2.91309068629,3.11981319411,2.9994074008,4.61414788365,4.37273564964,3.98070669477,4.25863067968,3.01845904892,3.0847156557,3.95068122159,3.91221821535,3.76597106842,4.01152297647,3.89542671709,3.78483626259,3.72245948295,3.9244242743,4.01680941063,3.61921457439,3.509477641,2.76197366764,2.77772939091,2.63806708373,2.54342970486,2.37600229552,2.29661464223,1.2742783804,1.186767652,1.25415983199,0.830715881022,0.757941810972,0.742723238854,0.820925663304,0.94481020423,0.73728348762,0.967779786031,1.66008840219,1.83065268654,1.77077738556,1.71591965098,1.80480635181,1.60796157873,1.58696819356,3.15414599124,3.12514637425,2.82128902893,2.49489555646,2.14502185143,2.18306618048,1.91439093222,2.00818919477,1.93962041636,1.79075506513,1.68841356157,1.73821890668,2.54701587354,2.61035205101,2.70784533941,2.72777231035,3.69075378494,3.95617185354,3.8943129449,0.55648086331,-0.0251070423429,0.261211300187,-0.0823072905391,-1.3756096214,-1.27914869573,-1.10841660093,-1.01099603203,-1.18260618367,-1.37319929703,-0.884657540809,-0.891003036148,-1.27368064185,-1.0735673735,0.0309850952934,0.315603189885,0.174902485947,0.0479543950222,0.426724616314,0.545258219939,0.412956069629,0.812469088022,0.707509521703,0.685263690424,0.89784628743,0.770719987331,0.878904795177,-0.679277674329,-0.523653249963,-0.55803602264,3.93167137945,3.96031539259,4.06055948309,4.04760555987,3.84202697076,3.82697047152,2.4572227105,2.43294887249,2.32670212181,2.24536515533,2.92297130616,2.82230479361,3.00661700809,2.80547038242,2.99711849126,2.89635100071,3.34301771934,3.60558472201,3.45447711106,2.71621976477,2.24265010984,2.06733794478,1.9717965657,2.06904856213,2.45098844101,2.46559576764,2.57686751494,2.69531071693,4.17001914122,4.35712936624,4.35475685259,4.26314452608,4.26152451515,4.16594527402,-1.26270336039,-1.36532274666,-1.18128257118,-1.16787735887,-1.36758591069,-1.27843116909,-0.931679374167,-1.04272399149,-0.90666550157,-0.999387204103,-1.11977902412,-1.13732434185,-0.232772230027,-0.412715988169,-0.345528413884,4.40411535176,4.0952589923,4.1580473885,4.65853079167,-1.5547420517,-1.50361541387,4.51557380121,0.854839054645,0.927388370588,0.826159759267,0.53333926445,0.422205271921,0.667136392201,0.705117183216,3.07658222511,3.36266419473,3.29592235886,3.16449296894,3.12737928349,3.28628443137,2.29690225957,2.36401764179,3.57965509197,3.70930748498,3.73912446706,3.45640064045,3.30565307017,3.38729491941,2.70279166246,2.48747162223,2.61832242913,2.5136849385,2.68382993815,2.5725562744,1.12759509782,1.14771892159,1.02202059376,0.935566594692,2.42802677853,2.16912749373,2.26317536776,2.23486229181,2.37493915325,2.47039010272,3.58956424478,3.54493364846,3.44416533905,3.93302491723,3.89295161947,3.85356483482,3.74678306326,3.69403180116,3.76015151407,3.79536427084,3.72492319681,3.75700300688,3.65401431848,3.58168816394,3.61358423043,0.452905159008,0.616293258061,0.534691777338,0.198615534252,0.187408415857,0.346000568758,0.78140875387,0.650011777398,0.880149503209,0.825358977613,0.65137889567,0.581024399166,-0.572742849387,-0.604731427839,-0.650728526473,-0.758719290635,-0.949571638609,-0.876118209241,-0.719066241042,2.3762730918,2.31020384372,2.39671282898,2.27020241745,2.18031999265,2.19965861595,2.56997712558,2.54669884392,2.701783647,2.85380969565,2.83413448547,2.7048096981,-0.152221966788,-0.23109024948,-0.191340915016,-0.329625835781,-0.408146163484,-0.351214560039,4.24357670456,4.38815738795,4.16632682466,4.22623202131,4.49274701806,4.41062960211,0.472106486431,0.624920884476,0.282281087614,0.28788849695,0.433135450426,0.583827388672,2.71115645835,2.40281099926,2.47242546108,3.00540858874,2.92927115798,2.70186611984,0.952896338947,0.986973104497,1.07855673891,1.05980343539,0.878568559639,0.863638857781,3.27036022388,3.37915312595,3.19512941394,3.23238553269,-0.791225330541,-0.755497874959,-0.969107126176,-0.905038563471,-0.828092465703,-0.928633963529,3.52678161895,3.41635028061,3.50997748414,3.57448913861,3.39053496512,3.34554796509\n    \n};\n\n\n\nconst int NST996 = 996;\nconst double AREA996[NST996] = {\n    0.0124857992829,0.0126828741137,0.0125184014875,0.0128358140002,0.0127090064398,0.0127975238237,0.0132433411671,0.0121885428733,0.0128358379244,0.0126213160783,0.0125536759372,0.0127893036403,0.0124257214309,0.0128533592399,0.0127217921705,0.0121363617427,0.012921979963,0.0126049019802,0.013857314232,0.0124487879018,0.0124816577559,0.0128317495644,0.0127367625669,0.0128679748734,0.0128333284615,0.012522238749,0.0130710565716,0.0124718988375,0.0134878024292,0.0121363226402,0.0126838839511,0.011966449436,0.012409483946,0.0124414705065,0.0126177073084,0.0127339921714,0.0125914028789,0.0126433520902,0.0128259326019,0.0124397021536,0.0126155278322,0.0127535867899,0.0125231719999,0.012665352027,0.0127757075984,0.0128307876078,0.0124209678906,0.012391998649,0.0128746083884,0.0127113638998,0.0119768789116,0.0123996772788,0.0123159574129,0.0129278842807,0.0124407148589,0.0125975461957,0.0126988734242,0.0126975935252,0.012580349257,0.0126203041624,0.0127429182115,0.0125221683108,0.012664785813,0.0130360637122,0.0132615597256,0.0128233890657,0.0123873298295,0.0127862971971,0.0132209749638,0.0123613590925,0.0124256761597,0.0129130171622,0.0129105287607,0.0124184193849,0.0121154297228,0.011968798085,0.0125504982556,0.0127264949097,0.0126420007134,0.0126929807898,0.0124765955152,0.0128612666665,0.0120711530249,0.0124865086923,0.0125518953282,0.0119379637873,0.0121466263996,0.0128806071415,0.0124079533032,0.0130013446701,0.0128537866369,0.0124191060087,0.0148178026425,0.0126041862962,0.0126019799706,0.0119471432606,0.011982641158,0.0125271676943,0.0120796155069,0.0118903493955,0.0120665508054,0.0124901005007,0.0128150973684,0.0125836195165,0.0126798514631,0.0128410020404,0.0124924164871,0.0125814946069,0.0124506189978,0.0121940312524,0.0127611894941,0.0124934071654,0.0128632860177,0.0123456845315,0.0119755982065,0.0126813726794,0.0124208174308,0.0127545902796,0.0125479248296,0.0122289369237,0.0122935805423,0.0126878122916,0.0125581116411,0.0126657482011,0.0126574068209,0.0126446415109,0.0126782643612,0.0127412093188,0.0124044027641,0.0117725376756,0.0125633911094,0.0127279115861,0.0130088328469,0.0124491566243,0.012540253171,0.0127671129094,0.0128459470403,0.0126265081445,0.0124950744629,0.0128459303594,0.0117526888362,0.0129915567285,0.014465168067,0.0125298306099,0.0143901943074,0.0125770738208,0.0127551756953,0.0127097201044,0.0126823064165,0.012480205004,0.0128988425916,0.0126295836759,0.01278501293,0.0124878292944,0.0126440332597,0.0126896177415,0.0125360853397,0.0127144339734,0.0128164814681,0.0125700489342,0.0126057507443,0.0121591429909,0.0123834120202,0.012622010642,0.0122954858709,0.0125841187748,0.01277743071,0.0128778511575,0.0124970338298,0.0127662066433,0.0126079681503,0.012290413344,0.0127142952978,0.0128555765923,0.0124249313224,0.0121192378651,0.0125529170624,0.0120261639353,0.0126919942123,0.0128460887347,0.0123603205638,0.0125918221716,0.012213462916,0.0132513206317,0.012175017712,0.0127451462065,0.0125156039562,0.0124346548174,0.0128502943217,0.012228192612,0.0126717337268,0.0126756185495,0.0126732438997,0.0124787248071,0.0126289243991,0.0123039861245,0.0123069192866,0.0123270181744,0.0120664338177,0.0124652207295,0.0120449289403,0.0131644821766,0.0124327796472,0.0129243425754,0.0126441512534,0.0126198741816,0.0132983257553,0.0128073304436,0.0129182872209,0.0127593909282,0.0123915547712,0.0127919587563,0.0123865792783,0.0132599081482,0.0121357313499,0.0120454216041,0.0126979008449,0.0125819497361,0.0125935019696,0.0127484360559,0.0125588877955,0.0125183633926,0.0125155577295,0.0127043824169,0.0127235433058,0.0126038003191,0.0128531506365,0.0124145441654,0.0128567285389,0.0126332709322,0.0131039179265,0.012091182226,0.0123817564693,0.012628124647,0.0129396178823,0.0127449036751,0.0125211429286,0.0121260405654,0.0126071569386,0.0118855637035,0.0125915481048,0.0129325565945,0.0121298800437,0.0131388503193,0.012799878241,0.0118607573697,0.012595676972,0.0120129672017,0.0142739658104,0.0141216534323,0.0119572362544,0.0127618304764,0.0125494302399,0.0125070121142,0.0127776542642,0.0119891913549,0.0127053634416,0.012528253551,0.0129016443855,0.012738753138,0.0124756964325,0.0124449489225,0.0124047034593,0.0128656341485,0.0126067606959,0.0128672424188,0.0126679978271,0.0126782887677,0.0131209770734,0.0121470365957,0.0135358279308,0.012627687001,0.0123924064067,0.0123777833641,0.0130296352464,0.0120748426963,0.0125272883613,0.0126562207575,0.0126819215779,0.0126280921901,0.0124854752583,0.0128285395366,0.0128903703256,0.0124734158803,0.0128365374397,0.01243385708,0.0124949337329,0.0126799783922,0.0126034380067,0.0120901757888,0.012741977664,0.0126324566366,0.0124685555344,0.0121899160993,0.012355796953,0.011986755724,0.0125925296022,0.0124638423508,0.0146853964355,0.0119654889542,0.011959901357,0.0123944928168,0.0127180606733,0.011837811526,0.0129063091034,0.0132022703509,0.012356311284,0.0123340695691,0.0122272343084,0.0140857806373,0.0120636468856,0.0127646080853,0.014775683477,0.0119898678286,0.0120374153477,0.0122442286373,0.0122335083517,0.014181121009,0.012981116996,0.0127038939412,0.0131319838658,0.0127991772127,0.012282829005,0.0121210036518,0.0122577086705,0.0118908215519,0.0127006559838,0.0129213605254,0.0122320117428,0.013218490397,0.0148451839638,0.011998659417,0.01254974413,0.012855339859,0.0127469835089,0.0125064247792,0.0124887595223,0.0128433881281,0.0124351075298,0.0127713553417,0.012533144686,0.012739938128,0.0128100324578,0.0125149797048,0.0124550547267,0.0125501355661,0.0124831930223,0.0127500989859,0.0119525742295,0.0128834469721,0.0131178367446,0.0134943951767,0.011891158022,0.0117707154966,0.0122071184968,0.0127071997314,0.0127454255023,0.0126199658858,0.0124677752592,0.0128418373809,0.012476850432,0.0123918665642,0.0128004825893,0.0125779949911,0.0126736159987,0.0117870319319,0.0129576472303,0.0129352616138,0.0120187089773,0.0118886991416,0.0117608256555,0.0120676529593,0.0122237141959,0.014878547645,0.0129365238867,0.012280147539,0.0123410519984,0.0125916152175,0.01353311567,0.0131314043585,0.0120269954504,0.0130757840584,0.0122899297022,0.0128051731983,0.0128202299846,0.0124897012882,0.0127512540746,0.0128370951205,0.0129254178887,0.0130814236812,0.0129563147787,0.0122503311355,0.0123446656089,0.0123769771857,0.0127913474306,0.0125006351486,0.0122721063256,0.0120310409469,0.0118636832468,0.0124099338377,0.0123655807169,0.0128995248845,0.0127663948088,0.0126597662084,0.0121331252064,0.0118954226393,0.0121671540886,0.0145111469272,0.0119677330993,0.0127868130781,0.0128420316674,0.0125422883726,0.0127317711831,0.012799092127,0.0125812117126,0.0125989106627,0.0125652288721,0.0126173126848,0.0127053311673,0.0125206527618,0.0123645077473,0.0123328110448,0.0127664722084,0.0126176465074,0.01265899367,0.0127116480092,0.0126137701482,0.012743008355,0.0126253409403,0.0126857697065,0.0130097562983,0.0124600799529,0.012886440848,0.0125007918489,0.0123600054291,0.0130091758871,0.0121968761656,0.012010325191,0.0118873761006,0.0121289370395,0.0123001925663,0.0126630226217,0.0126685512341,0.0127605118413,0.012534441681,0.0124954281218,0.0125168975282,0.0130099706759,0.0118676521151,0.0126231690723,0.012689448416,0.0128809852426,0.0125061442078,0.0124348764046,0.0128249582877,0.0126524222388,0.0126843544378,0.012624756224,0.0127451389434,0.0127204739317,0.0126540213951,0.0126785079173,0.0126481641865,0.0124068146308,0.0128434638909,0.012678552854,0.0124409297722,0.0124690119742,0.0130331316413,0.0129291515412,0.0124055742865,0.0128839114835,0.0124503360502,0.0123597893651,0.013061399134,0.0127804844383,0.0123529540803,0.0129489413241,0.0123956214994,0.0124114462368,0.0128915383882,0.0125066329722,0.0127898362485,0.0127697218575,0.0126358963623,0.0125231599482,0.012880728446,0.0128392707494,0.0123954152524,0.0129302292165,0.0128361470086,0.0124688721365,0.0126153829516,0.0124746704901,0.0119292034799,0.0121861944152,0.0145343926444,0.0129522345961,0.0123571941252,0.0126401180365,0.0124005902814,0.013109587836,0.01298859999,0.0123122813251,0.0129749487395,0.0122959044909,0.0123615736274,0.0124780181617,0.0123121482717,0.0128743343538,0.0122920220921,0.0127949595021,0.0124243790901,0.0131028495257,0.0127707215379,0.0124188789528,0.0126790233728,0.0123360718868,0.0128045776881,0.0118711797482,0.0120033585869,0.0130310175605,0.0119938320771,0.0149539044999,0.0124143852176,0.0126705475543,0.0124875153697,0.0128644570028,0.0125765257479,0.0127993422823,0.0127128711379,0.0126792880398,0.0127035872167,0.0126392475958,0.0125347068039,0.012703232103,0.0126251016913,0.0126647809182,0.0126706307332,0.0126675854245,0.012165455524,0.0120830337335,0.0127803062311,0.0128120713634,0.0126725221264,0.0127232472887,0.012596246379,0.0124337405928,0.012772750549,0.0125505301552,0.0125850965398,0.0127700856904,0.0127622271209,0.0124111922999,0.0125878346268,0.0127925260921,0.0127851780785,0.012618424055,0.0120222843576,0.0129281962261,0.0124904802431,0.0131200100462,0.0123204135959,0.0127704358476,0.0125266606631,0.0126922932743,0.0125888940639,0.0130112273712,0.0122720361323,0.0129136420004,0.0132176480217,0.0125449568858,0.0126457081839,0.0128390421724,0.0121107018419,0.0126474220898,0.0124973157661,0.012515420358,0.0125734808522,0.0127434705951,0.0125571416764,0.0127317445294,0.0124847364886,0.0127883617719,0.0124682592417,0.0128814305213,0.012460691912,0.0128641101444,0.0125860194645,0.0126889512186,0.0128164238025,0.0125424513571,0.0127359719075,0.0127390962545,0.012606449445,0.0126393268669,0.012621975712,0.0126255492968,0.0127667086849,0.0124661097762,0.012567579029,0.0126039157328,0.0124054684267,0.0129723226315,0.012439749702,0.0123917339758,0.0129204981515,0.0127715364028,0.0123794622557,0.0126380621059,0.0124152339535,0.0129302755576,0.0123901063863,0.0123783274279,0.0130870480259,0.0120123307523,0.0127320178647,0.0126717945587,0.0127426923249,0.0120851713183,0.0130531552297,0.0127785599511,0.0124270738612,0.0136454947154,0.0131005342615,0.012835838022,0.0125080305889,0.0128625442008,0.012458408805,0.0125541154092,0.0127716930733,0.0120848487048,0.0120418621458,0.0124232138066,0.0120404070268,0.0119494861281,0.0126796692371,0.0125530819096,0.0125703497022,0.0118255999544,0.0124683797894,0.012898777381,0.0125813294776,0.012363904344,0.012929178722,0.0123694043428,0.0121976052696,0.0119837709002,0.013367489156,0.0116463899708,0.0126435157245,0.012260926638,0.0127882459124,0.0123996827555,0.0127223497077,0.0126213734082,0.0129088860252,0.0124196006329,0.0124862915381,0.0130701937185,0.0123127732967,0.0128390638192,0.0128972400375,0.01233670534,0.0124539974489,0.0143424071803,0.0121448909955,0.0129805520917,0.0130137281205,0.0120448320375,0.0122720089151,0.0129012145008,0.0123487079934,0.0126871569983,0.0128604851169,0.0124855373398,0.0127985907084,0.0124929180343,0.0125444486887,0.0123640439201,0.012435595513,0.0123185406443,0.0126440466877,0.0127056485273,0.0128508406545,0.0124349826095,0.0129568479605,0.0123605185733,0.0129464123302,0.0124436214017,0.0125024364373,0.0127636838856,0.0125164160645,0.01251993149,0.0128609787988,0.013030322279,0.013017984764,0.0123569608778,0.0120922172965,0.012966244036,0.0125395163355,0.0123410503777,0.0130788333699,0.0122707811812,0.0128299937256,0.0127678651021,0.0125326384449,0.0126738220497,0.0126250420468,0.0126646631415,0.0125131043065,0.0127860521591,0.0126877445213,0.0125818710958,0.0125816527848,0.0128663030376,0.0124004330813,0.0124810109464,0.0126396656789,0.0126594924345,0.0125701447586,0.012609877616,0.0126584709085,0.0126380709267,0.0126267764445,0.0125862563318,0.0126927941391,0.0125423328503,0.0124172258234,0.0124382697011,0.0129180072494,0.0127918902044,0.0128297757421,0.0125707422115,0.0127663599249,0.0127181365023,0.0125801850596,0.0124838273307,0.0148076041579,0.0128801620204,0.0126064347925,0.0117700792984,0.0123553989411,0.0129227740058,0.0123835223336,0.0127901828219,0.0126915309396,0.0126095189364,0.0129075738348,0.0124070143541,0.0123018645472,0.0131187142099,0.0129808163491,0.0124466342668,0.0125033188846,0.0128888254265,0.0124504403669,0.0128909466248,0.0124673948286,0.0128756521068,0.0125589999598,0.0128727949656,0.0124678284506,0.0122812010117,0.0119582632013,0.0130539505685,0.012398581649,0.0123211710361,0.013344121513,0.01299479672,0.0126158412896,0.0126940300323,0.0124972362685,0.0129967199018,0.0124075169437,0.0126931787347,0.0125230828775,0.0124649926002,0.0120028001058,0.0128386635137,0.0126581379893,0.012728251531,0.0127108235137,0.0124094856826,0.0128809444929,0.0124113864391,0.0128830804909,0.0124489648014,0.0128483304035,0.0133562052337,0.013355148935,0.0121567845541,0.0129955877311,0.0124067808012,0.0125792905626,0.012158383082,0.0129931096716,0.0125063474615,0.0128452281657,0.0119330567988,0.0121544213793,0.012099937656,0.0125568808574,0.0118115510699,0.0125252981165,0.0127599858465,0.012463089843,0.0128805643612,0.0128072770944,0.0125205193593,0.012492594123,0.012634131596,0.0125680492155,0.0127646051531,0.0120085192925,0.0130476371235,0.0120854327337,0.0143927240463,0.0118661786062,0.0128717944894,0.0122692058853,0.0119856650354,0.0121710876854,0.0120893509322,0.0121750335975,0.0128619759386,0.012626018845,0.0127468346231,0.0125425703265,0.0128539698915,0.0126932473277,0.0120966030033,0.0129861388585,0.0123745313345,0.0125330128136,0.0129700998591,0.0124250731587,0.0128862949058,0.0123658981596,0.0128560329257,0.0124132999072,0.0127777304203,0.0125055731394,0.0125176988929,0.0128072350393,0.0143173924909,0.0120651705496,0.0128479203001,0.0120409986779,0.0129016436655,0.0121125204904,0.0125773793139,0.0123929860785,0.012698279964,0.0125090280854,0.0122605854778,0.0128617210506,0.0125121071092,0.0127887973643,0.0125312551594,0.0127842769473,0.0127752691125,0.0126381621245,0.0125755956994,0.0125694884018,0.0124503436842,0.012790748263,0.0129172603382,0.0125057162291,0.0124487289861,0.0128779771968,0.0125893880561,0.0129172564718,0.0125302867948,0.0127041193743,0.0128825579457,0.0124097496046,0.0126672910568,0.0127451340685,0.0120784515293,0.0128683556673,0.0124393953838,0.0124163726209,0.0130001007112,0.0120441764457,0.0126688429993,0.0126643448847,0.012447273403,0.0122763435413,0.0124955802584,0.0124194335981,0.0127691298327,0.0123704150372,0.0126180201542,0.0127070519214,0.0119715311562,0.0130245272419,0.0125877499015,0.0123585558986,0.0127987196507,0.013273149749,0.0120660443979,0.0125435298028,0.0126141522633,0.0128458706445,0.0127221870265,0.0125758806986,0.0126503185551,0.0125418649374,0.0127765391583,0.0126043906039,0.0127148660996,0.0124469922882,0.0128667293205,0.0124573047694,0.0128883106769,0.0124503531067,0.0128994345256,0.0127199936803,0.0123922855635,0.0129111931893,0.0125107481607,0.0119830729726,0.0121635775684,0.0121042420934,0.0119903497711,0.0123515101363,0.0121071799564,0.0133413090733,0.0133919715661,0.0123190811163,0.0129052451097,0.0124789743359,0.0127729292764,0.0126182676477,0.0126206549614,0.0122000163167,0.012689674501,0.0125964433228,0.012356281663,0.0119557282667,0.0131566326727,0.0124185632187,0.0123049751107,0.0128942998775,0.0122833352189,0.0131447823261,0.0120202006898,0.0124626779112,0.0128596110745,0.0129473796084,0.0128667165891,0.0124369356513,0.0130046823296,0.0151332531606,0.0123763213729,0.0130207137639,0.0117554724943,0.0126802078375,0.0125721176356,0.0127620211478,0.0121760513852,0.0119419701842,0.0120526139655,0.0123741408867,0.0118988277054,0.0125533743014,0.0127806231785,0.0124834065076,0.0128276019265,0.0125442168663,0.0127815096108,0.0129447438271,0.0131261035541,0.0122830733049,0.0130091041058,0.0125870049171,0.0123520675069,0.0128849338986,0.0124314473281,0.0126836909967,0.0127674190069,0.0123619387528,0.0129489745298,0.0124818639461,0.0128058453711,0.0126685811839,0.0121973602749,0.0132624119996,0.0117773277224,0.0124184895861,0.0123862567483,0.0122319951227,0.0130251263011,0.0123860606083,0.0129325526644\n    \n};\n\nconst double PHI996[NST996] = {\n    2.54757754322,2.68876540457,1.53700968414,1.52551185934,1.50434374498,1.48623705749,1.31600045188,1.39592325428,1.38531106044,1.29886680567,1.81035649949,1.81744985068,2.39201242164,2.32577850405,3.1046406245,2.26728661193,2.35895721656,2.21032763438,2.24921201437,1.98955877927,2.15052882721,2.24641652111,1.12173988184,1.03706698392,1.55061188563,1.55511465975,2.21462762607,2.2403141289,1.38594814879,1.29607012154,0.571830943849,0.0330578500801,1.71843979671,2.02927130495,1.80393714852,1.62522071758,1.6329142785,1.72039319161,0.823733508093,0.694554690086,1.10873066182,1.00883380121,1.66589907947,1.42770834479,1.32982986905,1.52071138005,1.39652414695,1.2741146881,1.18386783811,1.3624353496,1.46253071948,1.4590839644,1.25544325903,1.33797693513,1.41712700141,1.51103132744,1.40230053652,1.30833668556,1.29752468766,1.794748648,1.7840072902,1.73909811281,1.58677297392,1.85423506543,1.84703306963,1.67857703209,1.75247273844,1.89397830293,2.4878810829,2.59140454855,2.50240199758,2.48383125645,2.59185156555,2.5015451842,2.22350020982,2.30540837341,2.54893369168,2.64597976146,1.89150324225,1.88426204485,1.96599590676,2.05882007831,1.93411118236,2.03022602746,2.10876561879,2.1907049874,1.85941975408,1.89206764621,2.06002222669,2.13266606386,2.01588397792,2.1111171708,2.47222028866,2.73996215665,2.65644744079,2.47741094693,2.38840706487,2.74503607339,2.54590343426,2.63235014007,2.55568571617,2.44231264236,2.52311548868,2.58796600613,2.54224774358,2.388268651,2.29011885953,1.86886991714,1.82145121062,2.39338260647,2.46903034785,2.47635299981,2.31389688074,2.39532718727,2.55617749003,1.12979661341,1.05092539601,1.24160989506,1.22666084564,0.989991765631,1.0571364008,1.46950044753,0.960288304065,1.11681584125,1.01988538006,1.20301350071,1.19466903023,0.538850511779,0.0649398976252,0.222367335206,0.528272592966,0.437105251468,0.895853965054,0.178354786154,0.226960153737,0.141200364292,1.66311000578,1.73863105262,1.59188642717,1.62448605776,1.26880601079,1.17060276056,1.49217642573,1.27151369218,1.31920788665,1.96006494711,1.86303395294,1.70633306341,1.79753687288,1.97885425171,2.07016641805,2.08203434472,2.04981230997,0.410133882062,0.558854768249,0.640810365559,0.54282753104,0.697093416188,0.503933770969,0.572678195222,0.666136151604,0.137127060815,0.497767151022,1.00064438694,1.00455472453,1.09322650918,1.0950268255,1.45759832397,1.46356617054,1.19855535941,1.19294489632,1.29263744842,1.28564997027,1.37963954019,1.37823328017,1.43855136513,1.41548827755,1.25574670932,1.31769339348,1.18951610223,1.19137986787,1.28406645263,1.37065649665,1.36296254309,1.27808580063,0.740341042862,0.833464527304,0.906745917173,0.91269627091,1.16314703111,1.11242536417,1.57270316772,1.49867127973,1.55028758803,1.45225506031,2.15700741631,1.37149372284,1.46154423205,1.44840031508,1.1803469901,1.55392856995,1.47158595547,1.4107373886,1.50722399325,1.36994153593,1.42440848451,1.08271721165,1.27044900852,1.28426531261,1.31234268974,1.1558845648,1.24042877332,1.341013724,1.44403629712,1.49936655164,1.46829618143,2.1582771202,2.23850881384,1.96455452785,1.88626568694,2.05461784935,2.06599039869,1.79727370422,1.78086041927,1.6870224737,1.61571555066,1.5486276047,1.63056383631,1.71817820245,2.23565854475,2.16207650626,2.24287543711,1.54995945607,1.36242295598,1.3669943123,1.27057441512,1.27225530357,1.54751359459,1.56415285045,1.23127565521,1.22459747794,1.13765687217,1.06626627782,1.28119352356,1.38001214094,1.40424078773,1.15464092068,0.96835442632,0.929002528704,1.14384377431,1.06578994297,1.61631543432,1.6940627349,1.52793399781,1.51523654378,1.3515928879,1.3814654705,1.89596529047,2.06908796259,2.16497473408,2.16311212566,2.07414568955,1.98152174253,1.98490080373,2.25518386826,2.42434930515,2.33971015477,2.42354941686,2.39184712377,2.18520255983,2.28893475372,2.91994932577,2.7039466051,2.76930794898,2.67447824991,2.65148305517,2.83564780339,2.93027631269,3.00239872031,2.93903240148,2.67190651062,2.64581428184,2.76692497285,2.70187925056,2.80041284163,2.84247662531,2.75944611636,2.67104921031,2.55152370567,2.35724014007,2.41120557934,2.50841065549,2.08278982361,2.05980710061,2.12917263495,2.07928920676,1.97982819253,2.0653553969,2.02240716698,2.08779937895,1.91860904787,1.90235355085,1.98360092759,2.06519231176,1.38445602017,1.3850944018,1.29577608453,1.29333867493,1.76131765904,1.69894254681,1.61255768076,1.71961689591,1.60504615751,1.56024688202,1.85935013337,1.77170214934,1.76109759438,1.70049653863,1.56865398699,2.58648122245,2.6333342137,2.68609607789,2.71392363466,2.39844318557,2.26459350685,2.36250455232,2.82761215766,2.97318276496,2.57179677083,2.48466340913,2.48029239221,2.53889878652,2.80538548563,2.79314999785,2.93235561751,2.84144075853,2.29208895153,2.38913770212,2.42538830228,2.23588410986,2.14296320224,1.95985175316,2.10285681626,2.00986321598,2.42497400563,2.52211232963,2.37861214501,2.35656429341,2.46700892811,2.54249232346,2.33500192518,2.33579703416,2.40998154028,2.4060662431,2.51223122293,2.20076399362,2.21892364951,2.30392756995,2.37782453457,2.3582369998,2.26626929438,2.81299260711,2.64209654171,2.64132783772,2.71897982292,1.17100809286,1.07750772172,1.00841251643,1.04283463094,1.15135400092,1.33555057037,1.14185075698,1.28510346989,1.2009712786,1.68623336265,1.76728934853,1.48623477834,1.47319733737,1.22540241969,1.31554031101,1.2155671843,1.29678079189,1.39636434475,1.38752017078,0.879260028507,1.05165818999,1.12938718386,1.21890658726,1.06725446251,1.21513186976,1.21328274295,1.13107392622,1.12624108098,0.525967515926,0.542336172356,0.634438566488,0.551275730203,0.356283391151,0.817858403511,0.764507081513,0.619968645492,0.668626030153,0.766560438568,0.693014964237,0.2716911297,0.325230442966,0.120558768948,0.189595328514,0.163259070379,0.194093900912,0.111580291561,0.166343832876,0.254804004603,0.439810656711,0.360636523763,0.450003866235,0.275911977093,0.388900105118,0.296940248148,0.906959842601,0.846236914281,0.860015647069,0.919416354832,0.934948556299,0.575986626078,0.764506576663,0.484239455869,0.307161783301,0.392139909992,0.47342743327,1.42797813257,1.29663603653,1.32833694977,1.36134211587,1.49349417381,1.45836814318,1.46109970211,1.48634011811,1.41169533489,1.57755288584,1.55895675194,2.096640777,1.99961272285,1.70521140958,1.80445428107,1.69704672914,1.77578707932,1.60452364407,1.59408335674,1.76286858918,1.67380363231,1.96987235214,1.87862880586,1.9402762212,1.86606374007,1.9949110873,1.92383090144,1.83680397068,1.82316322446,1.89471067933,1.97932354658,1.19651827992,1.16587350362,1.12975374459,1.02737489614,1.06502103246,0.893241483947,0.992966068678,0.219433898755,0.349951348112,0.255963292177,0.497369449917,0.402196254507,0.893488049002,0.667189475359,0.836734613764,0.755477783105,0.586404359029,0.666298928929,0.605703536762,0.699624106768,0.794147515435,0.857093738491,1.88915947843,1.98848479665,1.60160239225,1.62318588349,1.73909958489,1.71862904921,1.79203382411,1.94627417902,2.01575724262,1.85169691867,1.83089731519,1.29388304054,1.34423614771,1.32168434781,1.00871699469,1.09476927439,0.925187353724,1.20284622724,1.38194670667,1.28157049002,1.37082448717,1.21996048738,1.30976982748,1.56617071512,1.66532657441,1.5785609551,1.52391877333,1.67849480328,1.72330270029,1.21691582759,1.11428284531,1.73166578637,1.72535927455,1.90412838967,2.07379534687,2.13397568397,2.22518602045,2.18072030031,2.11318223756,2.28405380883,2.30941192797,1.55133886289,1.55025523699,1.64226185978,1.64042828842,1.45985035587,1.45690536493,2.04691313679,2.11419566435,1.9966647708,2.08647497059,1.98286351303,1.98712359529,2.07172427782,2.15275307962,2.14563394012,2.06056368486,1.64499916685,1.72134752103,1.81320582756,1.54912212025,1.54874782724,1.4554139827,1.45639704128,1.73273739638,1.64184107125,1.64123734526,1.81921636882,1.89548302533,1.81478248347,1.7277649449,0.954041720749,1.04234163805,1.04536726984,0.977101967064,0.91227866066,0.820921961286,0.962564491484,0.987000835503,0.931836535261,1.08338301404,0.983346532023,0.715461412187,0.80673038968,0.64801554693,0.67954731108,0.840408726147,0.780007780991,1.82329478278,1.68528855689,1.66263934423,1.73027411311,1.61499109669,1.73197175769,1.58899427303,1.56875719505,1.63843212521,1.75279634253,1.67934536098,1.94501253937,1.85024257421,1.77963075675,1.96799332791,1.89638650504,1.80308008088,1.37410479721,1.28189515294,1.44896788319,1.45722168055,1.54627554685,1.72330472528,1.62647468476,1.71489817725,1.48343723511,1.473331345,1.65237982576,1.57387146705,1.6436083845,1.55506961794,1.89343823683,2.19599623793,2.04842284861,2.10208205941,2.03131741133,2.33301664052,2.31533104039,2.24840307131,2.15424569848,2.14148721345,2.2183801152,2.05794547364,2.15819684151,2.80720938973,2.39022137071,2.48553100712,2.47730459768,2.8224386872,2.75540400989,2.66397307727,2.63203103047,2.77468391063,2.67851137228,2.01199856997,1.8617607028,1.95420351546,2.03189202632,1.91783375264,1.84516843602,1.6122400927,1.54798645132,1.45797623944,1.55922686765,1.46099765897,2.1522679865,2.24084310927,2.13549029809,2.31542193633,2.20904892459,2.30079220324,2.81257112984,2.87346887189,2.80889484905,2.71574513491,2.23709399985,2.32174954332,2.40224264832,2.61341065154,2.6070070096,2.51011241117,2.88235451855,2.98719423668,3.04736898931,2.96906555383,2.88971545989,2.84703117993,1.38801544874,1.19841127155,1.23215761143,1.32730540291,1.35208299889,1.25796650665,2.50290681001,2.55720045187,2.55041506218,2.64053073251,2.70979116866,2.6718369428,1.44012694869,1.52060064452,1.60393957657,1.59361090317,1.50138941461,1.43117809013,0.590768023236,0.680666404725,0.576457157842,0.755429224948,0.899124201007,0.995324464285,0.8673334919,0.872987300482,0.705719778663,0.795578221375,0.607155185743,0.61163878207,0.704433742639,0.694629414429,0.784976541024,0.779078891469,0.475331386222,0.380425115727,0.318438946316,0.378392749754,0.520234951479,0.481510783706,0.438097466403,0.529103463395,0.604301484445,0.589530572546,0.498931331691,0.422340846817,0.271635434834,0.418081007458,0.324490702902,0.49139020048,0.327640370265,0.412901604451,0.335131861664,0.750639123956,0.608266316085,0.703932065403,0.711505525817,0.816690764725,0.821264345776,0.729365761788,0.641758481152,0.64882914224,0.740488971955,2.06885042922,2.19033019213,2.09636220903,2.13289398745,2.26027055765,2.22994891107,1.64411315684,1.52130263639,1.54554380474,1.48474123164,1.68178449287,1.655237831,1.6195250983,1.84156303094,1.97271314053,1.93933957017,1.77971614692,0.89214737253,0.872733531951,0.783700875527,0.788597505898,1.15102137457,1.0576489558,0.990640222311,1.02775773605,1.12777718476,1.18434666147,0.829364374526,0.732043425713,0.680913142624,0.714722728846,0.739702593088,1.01066978934,1.10021052346,1.11174000447,1.03538077655,0.866271868855,0.770985262404,0.755614306227,0.838151269134,0.928309037706,0.941754661075,0.951554072803,1.11022285597,1.02008855822,0.995480491698,1.15185848456,1.10498938893,0.948059604625,0.962881042613,1.00747439153,0.859500087851,0.797435839165,0.842172146418,2.01214784164,1.93782059645,1.77092836201,1.72645389216,1.65666335549,1.67614109879,1.81836685269,1.84357044921,1.88544732982,2.06798419042,1.99417586977,1.90406669768,2.05100673479,1.95886230352,1.13122466124,1.20677860042,1.18942891292,1.10227512354,0.931485562851,0.853837190897,1.03414863066,1.02257841751,0.960490016994,0.871943711158,1.06337942669,1.1065526349,0.961762582068,1.03930667788,0.946973536123,1.81887710142,1.98584257574,1.89624608633,1.81440828442,1.99002278457,1.90490627083,1.88602604578,1.96005342284,1.93708483567,1.84495329571,1.63213901936,1.71778343817,1.60181917261,1.6691684298,1.77689656197,1.79541417237,1.07410666187,1.14550002724,1.23261704693,1.21627313327,1.11976997224,1.81091104612,1.64163104774,1.7246452697,1.81021595832,1.75409732021,1.66275475139,1.97046442758,1.89133392748,1.81013934427,1.82217440015,1.91520354325,1.982443312,1.56582692268,1.65004023864,1.72944443695,1.56152213558,1.64437463533,1.72805151827,2.0571032581,2.15464496746,2.66004303756,2.74679326866,2.72505576562,2.78455189082,2.9755518561,2.90370509531,3.06834291511,3.00726035901,2.9150864505,2.88183604332,2.22799295601,2.31846312155,2.33139414143,2.2446581274,2.15570931408,2.15160945363,2.4188657753,2.49656581592,2.4165361065,2.50158880848,2.58808626702,2.59123993653,2.32232450982,2.23790449861,2.14248420073,2.1273007496,2.18464189047,2.22549342233,2.12826653665,2.11032702967,2.31286772605,2.21475091611,2.17144538507,2.21918033065,2.5068993213,2.46409006457,2.36449389091,2.31355700458,2.35725411039,2.45053176595,2.3500314034,2.28129693421,2.30452493448,2.39669959597,1.42632327458,1.36095874811,1.35092057266,1.26271833776,1.27699120903,1.18674141668,1.18264439345,0.824762835867,0.66327016889,0.725239880369,0.711231131337,1.0189056228,1.1054371079,0.660679735918,0.667228076255,0.748720812113,0.835928561694,0.461813049729,0.557719488794,0.498051901967,0.426341091732,0.58945920296,0.615719263632,1.75184767657,1.93858212646,1.90926221066,1.81433434547,1.87419445842,1.78269351678,0.705860766293,0.560728421891,0.657773201823,0.725952409969,0.832425536482,0.872321391719,0.912473178775,0.99951719782,0.972628994523,0.418415675314,0.251776363353,0.320839855002,0.623719100888,0.541235683933,0.619099948734,2.02279398253,2.07599711667,1.92210087281,1.87681616189,1.92341303278,2.02128832963,2.30342164924,2.34722390586,2.37905396821,2.2042965791,2.24780504271,2.17950363871,2.44462004544,2.50840777313,2.60080636587,2.63041749486,2.56047841125,2.46870351221,1.47452229351,1.40572426772,0.84989252214,1.02806047467,0.923081848359,0.930461913574,1.01718332375,1.01297499794,1.10161857681,1.09859242991,0.855905572481,0.953155189874,0.959607834442,0.802522672545,0.867968516917,0.841677372616,0.878734065382,0.779956271287,0.760825524991,0.935628723251,0.95398605432,0.552917922199,0.444837049557,0.542244706416,0.586580548243,1.40215386995,1.49622971366,1.52005170302,1.44910992357,1.35361289162,1.33034655112,1.23605340974,1.16204818234,1.24252691527,1.31024116058,1.2833965147,1.18653606036,1.11523384518,1.14499144262,0.464738304275,0.457002725791,0.309619920135,0.397989693874,0.296923700398,0.378988880703\n    \n};\nconst double THETA996[NST996] = {\n    1.41384085293,1.69354523046,3.06396839898,2.96282828373,-0.0546440347069,-0.148197759291,-0.125992071944,-0.185857101175,-0.290302945755,-0.337954976391,4.44771928958,4.3432272422,3.69239903319,3.57581389347,3.80593651371,3.84352938298,3.83086000845,3.95590432662,4.06040927512,-0.837799104276,1.8666274081,1.86453099607,3.54589031269,3.6151544602,3.26341777849,3.36205018641,3.73008183871,3.59945239121,-0.49965343315,-0.44434137628,-1.16793806528,-0.195337767145,2.47882170223,2.362182902,3.18895960121,3.10483172497,3.20423023904,3.24535403982,2.35997054255,2.57343059503,3.44672649169,3.40877424172,0.269506217816,0.00889126361683,-0.0240965156078,0.388862933271,1.62570836182,1.00713859096,0.96131354609,0.955109964002,-0.450862982076,-0.34725235831,0.0369186273459,4.09941634455,4.16147457188,4.12042943211,4.2569327655,4.293797265,4.39139299594,4.65172370043,-1.53033371215,4.28500734232,4.17382700477,4.02968483168,4.13414168093,4.12712652269,4.1819359812,3.23478207404,2.99106173458,3.26232487039,3.16687468286,3.66481264635,3.44629888577,3.50401668757,-1.10071326531,-1.02115588024,3.80042216465,4.14498020927,4.50695493769,4.61033982276,4.67006766759,4.62334511176,3.97887703444,4.02008737488,3.94459705934,4.15010484173,-0.391404159596,-0.819385602352,-1.15399024907,-1.07159409486,-0.93925456139,-0.960049853102,2.6128872068,0.112991448787,0.214876871369,0.358548835494,-1.10490006136,1.88576510245,2.7102382988,2.61024676272,2.89556628576,1.86307160064,1.57512089886,1.71501908616,1.86837899435,1.98798744936,1.9791957693,1.68337350505,1.77901049276,0.451123452166,1.30484770236,1.14850767398,1.10752379662,1.05535276004,1.05921475861,4.26572522342,4.19186632787,4.1322966147,4.23183862345,3.80402721881,3.71490625884,3.41910511058,-0.781785707264,4.36587128863,4.40472605857,4.42884658533,4.52768591204,-0.984846857487,2.84025087921,-0.135343269721,-1.34957942814,-1.37943112358,3.79812968502,4.03877340409,3.15659536827,3.44362146845,2.65405889806,2.58115286983,2.36259926366,2.45633975425,2.50329433228,2.49580497386,2.76484923673,2.68166735537,2.60422530934,1.86911431836,1.86853623095,3.0475026461,3.08998555285,3.17945153836,3.23024654762,2.70982762625,2.4755142806,2.94774347993,3.22205789652,2.70727854865,2.71346766084,2.83807793245,2.89763580528,3.03673628908,2.98746921945,2.10129195399,1.07490617234,3.30513972174,3.08907849982,3.15076771556,3.24898735367,3.12404637005,3.22455020424,3.38759198019,3.292275742,3.42994201125,3.23885491343,3.3770912747,3.28065766597,2.91962971253,2.81558402252,2.84584318948,2.77799072312,3.10108836465,3.00610215828,3.14493688238,3.08675030718,2.9830453996,2.94674670493,3.0911060191,3.04697357097,3.24964584019,3.12983752227,2.67048789631,2.58579818261,0.230914125477,0.290702041807,0.133599625863,0.101508267805,0.177708096563,0.586217266307,0.541823698005,0.445268200164,0.857892296474,1.58183428609,1.55111496275,1.89886962251,1.9201482603,1.80089604575,1.72757831068,1.72815986065,1.77768081119,1.48971067566,1.59454960335,1.64185629041,1.67256634967,1.4206977909,1.45091600971,1.38444744051,1.28632293888,0.710381416247,0.641798732842,0.498533864183,0.558508749238,0.546097993593,0.657960240576,0.51348622897,0.410944436495,0.369575986532,0.428756988504,0.585126994568,0.528828911323,0.571679755247,1.02035019084,0.827271418054,0.891434377281,1.23057431953,0.861845612244,0.676691079213,0.811312968139,0.715280889692,-0.308779912602,-0.213708006203,-0.16770252089,-0.275540957191,-0.325201570175,-0.260401956586,0.138110541686,0.169109320826,0.261278640444,0.00696437039988,-0.108643924856,-0.0179756217633,-0.0968739229597,-0.139786846294,-1.55361156382,-1.49175803311,-1.51564911561,-1.4147615116,4.00529855202,4.45021858064,3.33571504017,3.5510811881,3.39910033357,3.51036473312,3.33862927164,3.4902185921,3.38778338797,3.34760354374,3.23298845552,3.43279177769,3.39184009943,2.93781334681,-0.875018802957,-0.905156508651,-1.45028450366,2.76372483694,3.23730178564,3.15160253319,2.9559516355,4.26366865842,4.39070388777,3.96814737772,3.55958996768,3.5771563279,3.77755133664,3.49742797038,3.96358794458,3.97260981565,3.69274347632,4.44814523212,4.35627375337,4.11513886576,4.07462572084,3.95682148915,3.95591644764,-1.2648739838,3.6537634712,3.74230090172,3.84524156913,4.46059407135,4.51641488924,4.12804476448,4.17733377745,4.1895169979,4.29571932286,4.35324133749,4.29610652124,-0.806946830434,-0.600328181652,-0.757317063481,-0.653002877876,-0.425243988264,-0.359360862192,-0.386222448121,-0.530538941761,-0.554676608826,-0.476539081818,-0.726303591922,-0.585440577375,-0.69120609887,-0.753591177382,-0.892852966192,2.03435226529,2.41738104476,2.07343190278,2.29000533562,2.67134548637,2.78828098877,2.81328075296,0.256577748829,0.868852041028,0.11177220188,0.205151069256,-1.09749967607,-1.26598197701,2.42103255344,2.72786018987,3.05735568422,3.01431797997,1.74735358848,1.73702811996,1.60117331563,1.63896480759,1.65262119042,1.67403664962,1.76351125996,1.77014434295,2.12222768743,2.1601193165,2.36388494333,2.22926280385,2.42614394903,2.33498683628,0.692857108176,0.828937111245,0.904991228325,0.60492496898,0.888546701182,1.41242601466,1.28348807743,1.24905145665,1.35024053718,1.48900417945,1.51499035566,1.37249021234,1.13645476973,1.34424058502,1.47373560536,4.06068391306,4.08572542055,4.0007223867,3.90095200374,3.75546749516,3.82882231267,3.86704229862,3.9201890318,3.9419606422,4.02448458708,3.97563746178,3.70964372871,3.51441404174,3.69663090058,3.73485390154,3.58656403978,3.53039311745,3.66741934612,3.57222486604,-0.854437798809,-0.832359518247,-0.770893486477,-0.818134353808,-0.947833142936,-0.494858810977,-0.603285797504,-0.43904943187,-0.659332753727,-0.585784476709,-0.388762276525,-0.331607876607,0.276367447255,0.380513188182,-0.0209546369525,-0.144985197176,-0.0367662798399,-0.170626162277,0.626644142917,0.52490450538,0.577862175692,0.865784955369,0.747992436052,0.393134898458,1.42186913171,-0.592897987676,-1.00643407689,4.64485265713,-1.49433344132,-0.708268350258,-0.597974989072,-0.947344097732,-0.790965420325,-1.15753437607,-1.14220755357,4.01866677427,3.91302391752,3.67983821854,3.47830482304,3.58680933739,3.59567862087,3.66130342139,3.54384736051,3.3546240255,3.20373628709,3.32523147824,2.43133809136,2.32871448409,2.42220357437,2.24900679519,2.34996362096,2.26075500357,2.51681012802,2.67713234359,2.60825962917,2.62473819237,2.52893349178,1.96913053492,1.96497764973,1.94866040962,1.9557455229,2.9503607929,2.89022729714,2.90699255958,2.81271122829,2.78860030275,2.75494244149,3.07392225469,3.03104392005,2.86030203681,2.9278427104,2.67490539827,2.75184261593,2.71822000466,2.6112403546,2.53190556439,2.56151401197,2.3176143101,2.22440742604,2.40347005465,2.39729274348,2.21068636876,2.27169628002,2.29702527951,2.3334760057,2.72991282122,2.74769523443,2.53330775125,2.49723012782,1.35377013815,0.847275773739,0.847148431031,0.77350244059,1.12475845627,1.00950554687,1.29605386841,1.31452977511,2.81657396097,2.91926926948,-0.298765093516,-0.278267283529,-0.0226038955829,0.0734342779967,0.207815660075,0.108659303692,0.0433369387011,0.391094110791,0.324965222211,0.349253607548,0.245834075876,0.515986470473,0.425476832368,0.339196769203,0.849182866606,0.799155071686,0.784895329708,1.17157070015,1.25024358027,1.10866637476,1.14789414558,1.27880378475,1.31764110505,1.8466265298,1.86090636572,1.68114221836,1.75192542936,1.69345417907,1.77857561546,1.85497415731,1.83723323337,0.866773369779,0.671644411075,0.977725681687,0.882484527354,0.481050426876,0.523639041686,0.293772758912,0.366181557966,0.305968766252,0.422197436339,0.681917062547,0.866406221111,0.820307274156,0.725076979584,0.728557085604,0.818398665887,1.34335698989,1.4357762055,1.56742144123,1.5529905993,1.15130532074,1.04202374704,0.996000740542,1.06874550567,1.19206631543,1.22748558737,1.53397511351,1.60540616636,1.5950689784,1.13888066526,0.95629623416,1.00090752226,1.09287482311,0.962725829751,1.00554742402,1.0967605912,1.01920676851,1.18930645419,1.12222573254,1.15628199412,-0.664659759592,-0.604989037281,-0.492489373078,-0.307655346944,-0.221862061893,-0.253990601138,-0.42697078234,0.298643926553,0.192511220988,0.08694670351,0.081080138049,0.370876454112,0.341375357901,0.249364403554,0.10495690791,0.207931515531,0.094967205435,-0.894430921453,-1.02891999972,-0.927911137206,-0.863789542223,-1.09067748616,-1.22809491027,-1.35288813018,-1.25210291555,-1.19082507633,-1.32836376596,-1.3915096261,-1.01879963183,-0.992856357329,-1.06292970977,-1.12276158225,-1.19571245486,-1.16230757727,4.54681775427,4.5861543753,4.70557569554,4.60624322627,4.56906187882,4.49002155041,4.6299796557,4.59119553393,4.31569016966,4.41194108696,4.33007671607,4.27407438872,4.43106915865,4.47085330918,3.54147731692,2.8682206024,3.0106135932,2.82636310891,2.89943814803,3.15877984103,3.03372026914,3.22839683354,3.1730888349,3.05784175923,2.98962608703,-0.752978021965,-0.76848876189,-0.831882900728,4.20490803714,4.24410241219,-1.39124116651,-1.36535848954,-1.55873516905,-1.48437071939,-1.28632890722,-1.07706794219,-1.08714234895,-1.33689521158,-1.46809391849,-1.50882998733,-1.44476573451,-1.2994345908,-1.36586510448,-0.722696918111,-0.797440949703,-0.755438338933,-0.631461891516,-0.652438720539,2.62585888145,2.66203138047,2.5043769979,2.57989810184,2.41530827156,2.45370293179,0.544986653055,0.821019225987,1.06906886889,0.98901310815,0.104868786153,0.178171908881,0.121540162542,-0.940563809932,-0.745348931579,-0.965486896591,2.2602861726,1.47386740423,2.17470612285,2.53902011936,1.59587055034,1.90843050978,2.07353624917,2.03945800196,2.14016937094,2.15332640794,1.97585359191,1.95515329509,0.628430292071,0.768319976131,0.470335405426,0.42924082207,0.592472440705,0.781610293168,3.96660995453,4.01934660411,3.97058458468,3.86618414277,3.81257986708,3.86394663483,-1.50124752927,-1.46280824772,4.59964778651,-1.56667280448,-0.973636690148,-1.01538992264,-0.603742927655,-0.476981123264,-0.440614650318,-0.39170555414,-0.676912250442,-0.849036702052,-0.898310797204,-0.595338655773,-0.806162953923,-0.669952694087,-0.248204721307,-0.317977007982,-0.106108129157,0.140455114539,-0.0521219326512,0.119701150895,0.528245569907,0.459071919754,0.581414032812,0.760578192033,0.8637574923,0.761046956701,3.97667507416,3.71851671444,3.67687849633,4.51703905326,4.54937451654,4.65841301,4.23189064099,3.91549653861,3.76674600174,3.78121702735,4.0512554972,3.307481338,3.43508967325,3.23478891822,3.31033034813,3.47512447294,3.5223926034,2.1668144332,2.29691770005,2.27376177889,2.0736086364,2.20274613413,2.08535870118,2.02790418194,2.18181452065,2.01383506949,2.09003474541,2.11793850781,2.28725382517,2.19544409337,2.04690723072,2.15315679547,2.05534443785,2.12964102737,1.88348857605,2.14597013647,2.08770007628,1.94811735321,1.34862420393,1.30873008441,1.38421946685,1.49926191636,1.53148556848,1.45715094678,1.45650312337,1.45201784337,1.59225752056,1.86186665558,1.71428147206,0.966388807941,1.01848084912,1.12808109176,1.19456612148,1.2340797823,1.20082701397,1.0627006262,0.978365716669,1.03137200915,1.15126370569,2.88946540222,2.94287854623,2.97481278782,2.77792636539,2.83997778488,2.76171904884,2.68838878744,2.49116271169,2.58768697459,2.4809699816,2.58433615805,2.69622959501,-0.179218511936,-0.101777881827,-0.0545945244831,-0.250711491821,-0.182892181965,-0.0848964771083,-0.224095802491,-0.125610331044,0.0747519656796,0.140769354568,0.215536385743,0.178570694177,0.0295419560299,0.00117073091985,0.466984937901,0.540132345369,0.649841029897,0.686036721064,0.663241867714,0.580906592388,0.505423646007,0.615612582909,0.421049097801,0.447241681534,1.92111956078,2.02278585715,1.94535228282,2.10908043258,2.07198077823,0.81788648606,0.714141374045,0.663007698614,0.717372711425,0.823172797847,0.871673256882,1.2928953049,1.36992071738,1.47748817018,1.49551694769,1.28899297026,1.25738549522,1.38608734393,1.44975845908,1.42207232179,1.32634528305,0.28508351308,0.368101683991,0.314536645988,0.205278594575,0.184031164203,3.49399656583,3.40311651695,3.34610223282,3.39004868759,3.86934213924,3.81429986571,3.70094265272,3.65000711683,3.70690078988,3.81576942455,3.87056089057,3.81247975857,3.65826113123,3.70894318092,3.65529864848,3.55775017099,3.50400817416,3.55042707435,-0.362914961155,-0.350217191615,-0.588361726252,-0.598013782703,-0.141767858789,-0.331714959684,-1.00640441691,-0.72050281964,-0.596262526575,0.256756511143,0.0481536272832,-0.317409769698,4.26576388194,4.30393258647,4.43938161119,4.51293959699,4.45897059578,4.34490276473,4.49614502877,-1.55166078688,4.64478173525,4.40378103164,4.66354623991,4.47800026878,4.70327831803,4.6318049581,4.68312106738,-1.48822159036,-0.240091287759,-0.0107432223996,-0.0479194195557,-0.154589612008,-0.441384171289,-0.449573372462,-0.558811681605,-0.669502735269,-0.697515737418,-0.558783671585,-0.55777174324,-0.67645760192,-0.806334826129,-0.825257523341,-0.315999515167,-0.215325320792,-0.0910739135566,-0.0363607740721,-1.37536026919,-1.53984866426,-1.43808954479,-1.39690908441,4.68388685985,-1.55277329745,-1.45169013269,-1.06349883736,-1.16895758898,-1.03910556892,-1.30990287272,-1.13613680442,-1.37109489671,4.50434099046,4.34867325012,4.57721089472,4.50813220756,3.93105056513,3.92281299413,4.30365605386,4.15217047278,4.23991303504,4.0724750429,2.30398963784,2.34156725021,2.2388581593,2.22413709288,2.42458743546,2.40212250493,2.17202541571,2.38599452208,2.42955708306,2.32176875657,1.68762848507,1.56880141213,1.76887634902,1.69929500348,1.58136055719,1.23188898451,1.42332893164,1.14944517998,1.91898727429,2.19522579899,2.09556026902,-0.65455767999,-0.559562297407,-0.64678864358,-0.559453192383,-0.470889717014,-0.46329648031,-1.45745182714,-1.23926833711,-1.35994982183,-1.41867128036,-1.22072153203,-1.30219242193,-0.285478905684,-0.409715979773,-0.389691493406,-0.202990287936,-0.0668122129897,-0.126728623876,-1.21283855107,-1.27392201038,-1.51564357487,-1.40847478082,4.56889542162,4.69153863227,-1.54397975949,4.51227803214,4.57111285206,4.67703509201,-1.17940102683,-1.21023677174,-1.33590594833,-1.29579723488,-1.395203426,4.37819729837,4.13254984458,4.16140062669,4.29537938223,4.33375347546,4.21969775771,1.79505293583,1.44622635374,1.45168858399,1.61175311586,-0.912457354268,-0.955843764426,-1.05302423097,-1.11355608028,-1.070354459,-0.96593267285,-0.921723861987,-0.988873714004,-1.29159808273,-1.2315662861,-1.12681446757,-1.08986793474,-1.16829413167,-1.26528316361,1.85743659075,2.09184730323,1.73956439916,1.67545712513,2.08866480895,2.23175237338\n    \n};\n\nconst int NST1010= 1010;\nconst double AREA1010[NST1010] = {\n    0.0124299296763,0.0125351810871,0.0120562856962,0.012375946706,0.0125941480088,0.0127945223199,0.0123495231232,0.012683752365,0.0118924571799,0.0123784183386,0.0126082093541,0.0123259763507,0.0126774174972,0.0125641155111,0.0125181129023,0.0124813613113,0.0126311091378,0.0125064245984,0.0125180115164,0.0124010360858,0.0126934006505,0.0122641033471,0.0122608327765,0.012287042425,0.0126050590514,0.0122942279071,0.0124566879501,0.0125056155249,0.0121910561936,0.0127460124488,0.0127676141388,0.0124788007833,0.0125333433979,0.012261769768,0.0120537417533,0.0117212622177,0.0118754187773,0.0116809995964,0.012306268984,0.012433943302,0.012604219621,0.0122148210652,0.0131504593833,0.0128066070005,0.0122759108436,0.0127883576715,0.0123919875785,0.0118448396031,0.0128052208767,0.012209549724,0.0126229039352,0.0127365023111,0.0122961256711,0.0116854580792,0.0122461079312,0.0123162955312,0.0148520441431,0.0119075599984,0.0115935811041,0.0128832603034,0.0118153980238,0.0118080078058,0.011823930501,0.0122057046292,0.0122074480664,0.0144344693889,0.0125427825633,0.0125194952293,0.0123660849415,0.0122320402518,0.0122585979143,0.0122928733423,0.0121550900372,0.012331404555,0.0122604157905,0.0126989702943,0.0124219735533,0.0123254685575,0.012698214708,0.0122437111951,0.012284794452,0.0126213309971,0.012326210442,0.0123607062984,0.012483317499,0.0128695057445,0.0119701931493,0.0125328798671,0.0127084566048,0.01220579679,0.0125119305054,0.0121237854928,0.0123099396207,0.0130339169031,0.0124599877103,0.0117868738617,0.012214159278,0.0116334490086,0.0128695228118,0.0126273337427,0.0123348956764,0.0125841505877,0.0127066223678,0.0122704581373,0.0126152382591,0.0123115395157,0.0121456977266,0.0123284123627,0.01208598949,0.0127080066477,0.0120296087782,0.0129074399965,0.011938046968,0.0123211373904,0.0116806293432,0.0126135703844,0.0123001557254,0.012431965604,0.0123284814306,0.0126722066063,0.0120640572833,0.0128147330018,0.0124209808245,0.0127914831285,0.0122309909393,0.0126523001656,0.0128296924494,0.012741246726,0.0127261310686,0.0126452330912,0.0122710857891,0.0122724581266,0.0125069843027,0.0123420094274,0.0123346440298,0.0123206185482,0.0125651400377,0.0127620687508,0.0122098125055,0.0130717107235,0.0127224033202,0.0121157858421,0.0126925987192,0.0126806288836,0.0123770979915,0.0124339684986,0.0125439204709,0.0118542463264,0.012498444723,0.0119955405934,0.012031825529,0.0121980450429,0.0125063138499,0.0120765751929,0.0127921151006,0.0121989796224,0.012696980681,0.0122694391412,0.0126200239892,0.0125064848701,0.0124071319928,0.0126348878018,0.0122145313309,0.0122929424916,0.0122940749765,0.0127261601851,0.0121660457344,0.012730457051,0.0127733589438,0.0122558120422,0.0123276617994,0.0123027429771,0.012761742278,0.0123621831097,0.0125851236599,0.0125891664888,0.0123781876932,0.012386484152,0.0125365755006,0.0123415494781,0.012657689798,0.0126171639845,0.0123502002319,0.012255696691,0.0137894509536,0.0121932260107,0.0121379847322,0.0117901580675,0.0127038969203,0.0121743572213,0.0122020096599,0.0120930034688,0.012077609742,0.0129371819965,0.0149812108795,0.0116187856541,0.0123839245375,0.0125322033086,0.0124727076447,0.012441829285,0.0125376486075,0.0125234197054,0.0124996267073,0.0122393053272,0.0124971637563,0.0123488097593,0.0125966019809,0.0124402694885,0.0124786875251,0.0125419428982,0.0122908854477,0.0122442992348,0.0126582465425,0.0125688108554,0.0124289953845,0.0123642447917,0.0125694336307,0.0125321439743,0.0123873267315,0.0120781225301,0.0123896657393,0.0119119819063,0.0119572350701,0.0120274786019,0.0124661043354,0.011989749628,0.012678807141,0.0123927930944,0.0120523087013,0.0127838817383,0.0122802607626,0.0121507950555,0.0121187209227,0.0127262215171,0.0122648711238,0.012695004371,0.0121827074681,0.0122866978262,0.0125525705007,0.0123572189504,0.0124551707958,0.0125240654046,0.0127268745697,0.0121952620293,0.0122881959254,0.0120626148221,0.0120956823003,0.0128720805318,0.0118694418325,0.011937237375,0.0128016779121,0.0124119054631,0.0127800302406,0.0118695274309,0.0126363831874,0.0124783383506,0.0130200846724,0.0127925452952,0.0127101696931,0.0126562386375,0.0125820803334,0.0116364151515,0.0142258833357,0.0119044810759,0.0122933412071,0.0118144673888,0.0132921457402,0.0119390489914,0.0143622704449,0.0124324608983,0.0125846984756,0.0126073159886,0.0122713566842,0.0122460277394,0.0123786840784,0.0117763720008,0.0129929059604,0.0120344869074,0.0124698387146,0.0114772076788,0.0129961070054,0.0128247872028,0.011720489384,0.0125142274463,0.0117504584064,0.0118095162155,0.0149872336949,0.012583446908,0.012200035066,0.0127592295689,0.0121844934289,0.0126805546913,0.0127109337902,0.0124113091243,0.0123391354644,0.0123184974238,0.0116621802507,0.0128776423391,0.0127895926562,0.0119637358563,0.0124268149713,0.0122292999059,0.0117816755543,0.0125705829427,0.0126236190606,0.0123530740324,0.0126736882274,0.012340687405,0.0124266921223,0.0126834204108,0.0125039977164,0.0124063376089,0.0127011460992,0.0122280097939,0.0124768018499,0.0124195774212,0.0123677723572,0.0126717949645,0.0123065053271,0.0126702708559,0.0126619860661,0.0123025411131,0.0127365668248,0.0116317010423,0.0122750574568,0.0144847924886,0.0124496535659,0.0120621005186,0.0127149670783,0.0123494407435,0.0131883746212,0.0120052196159,0.0127816278612,0.0124541521541,0.0123344458521,0.0121845771906,0.012527868708,0.0126914642644,0.0126051719267,0.0124332475319,0.0126664469502,0.0122512736829,0.0122325430104,0.0124268715075,0.012384427318,0.0126048965088,0.0126894876326,0.01257962104,0.0123442662568,0.0125020831183,0.0124240057425,0.012630692978,0.0123838571358,0.0130469913484,0.0116695935777,0.012282100625,0.0127330812374,0.0122067831704,0.0123994227249,0.0125789597609,0.0126883923699,0.0125315893099,0.0124140518856,0.0130467044775,0.0131053153376,0.0127211901378,0.0122009559358,0.0121687974282,0.0128240837497,0.0120797922624,0.0122154510082,0.0128201600007,0.0120396104998,0.0121698223702,0.0124480616812,0.0126173485924,0.0125590636072,0.0124097342307,0.0124063304842,0.0124795583814,0.0124423674108,0.0125098588764,0.0125236114007,0.0123652474451,0.0124261740759,0.0125297911457,0.0125284040039,0.0123656434474,0.0121568931653,0.0123429860799,0.0126443082567,0.0122442949883,0.0120797179135,0.0121787963026,0.0124220592906,0.0122010889977,0.0127618113554,0.0122229684742,0.0122998291099,0.0120273698136,0.0128342740559,0.0121469846195,0.0119404153827,0.0128696541245,0.0123647101413,0.0126633574548,0.0125618538249,0.0123876538426,0.0122448684723,0.0127277744898,0.012551172478,0.0125573519341,0.0135289966862,0.0126718379761,0.0126194963332,0.0120027872155,0.0128038972791,0.011651521033,0.0118217571739,0.012191756435,0.0143987142442,0.0119256163252,0.0124276393787,0.0126247150284,0.0122613077204,0.0122141674468,0.0121839579928,0.0127742699564,0.01236373452,0.0125371254609,0.0126205237844,0.0126654976266,0.0122476990219,0.0122374807595,0.0127202655064,0.0124558298082,0.0126533445152,0.0123437406883,0.0127096113658,0.01233100102,0.0125541842644,0.0126496179861,0.0125702098623,0.0122977490413,0.0123709623324,0.0125762588094,0.0124314150534,0.0125229093713,0.0123737221012,0.0128772610189,0.0123936612189,0.0123590152841,0.0126662141556,0.0122073182587,0.0123298527375,0.0127706035304,0.0123628140816,0.0122079662888,0.0121395940995,0.0126224663261,0.0123217033264,0.0126389543457,0.0125631579961,0.0124468885104,0.0125115786728,0.0123032059831,0.0122693400003,0.012187619147,0.0126197850887,0.0127144595248,0.0121932276603,0.0125472961826,0.0125032875498,0.0125432029837,0.0124364925275,0.0129455219802,0.0120322801156,0.012241296397,0.0116776281423,0.0126973305766,0.0123221440245,0.0122575372812,0.0128205535602,0.0122795745425,0.0127230952789,0.0126629127902,0.0123171791525,0.0125679093733,0.0122117070987,0.0127268823997,0.0127116251416,0.0127068484516,0.0131829232178,0.0116343982356,0.0119009085587,0.0148738132404,0.0119335352026,0.0124402002954,0.012793740657,0.0119525986577,0.0126491290195,0.0124323718798,0.0125383953663,0.0122478550403,0.012337331047,0.0126730613712,0.0125508670007,0.0125005809633,0.0124740591552,0.0124391405045,0.0125714183694,0.0123876848827,0.0125405232166,0.0123287056608,0.0127008371208,0.0122840049111,0.0121302081575,0.0127367771017,0.0128270951968,0.0122295721779,0.0120733611624,0.0132275026353,0.0117371902992,0.0123848125306,0.0126880712855,0.012077081038,0.0124998444063,0.0124251233157,0.0127261707283,0.0122319176239,0.0127667966475,0.0127430965039,0.0122303553886,0.0124840028021,0.0125421916307,0.0124570333051,0.0124855935482,0.0127595439471,0.0129465360293,0.0126098602596,0.0120952020016,0.012436462547,0.0121295051408,0.0126287537466,0.0128298719413,0.0125440125566,0.012311215285,0.0120885080467,0.0128518437225,0.0121818182636,0.0126161159368,0.0123289685529,0.0128007973145,0.0122170320268,0.0127597930048,0.0121802535424,0.0127998793399,0.012186249266,0.0126491648293,0.012274766206,0.0125553532883,0.0123008730591,0.0124308791212,0.0125412526606,0.0124529289509,0.0125646085751,0.0125119504896,0.0124301684525,0.0124313450881,0.0125885720034,0.0125774455277,0.0123919130671,0.0128653441558,0.0122342836126,0.0123700721731,0.0125526986509,0.0124115738748,0.0122110588622,0.0122475566505,0.0125249359082,0.0127131842815,0.0122532068033,0.0124190690839,0.0124289432664,0.0126898940298,0.0128439915327,0.01266066969,0.0120664166737,0.0123939586795,0.0122003096501,0.0127005118682,0.0122769090671,0.012107640831,0.0118916915053,0.0120767629847,0.0120453997255,0.0117580883706,0.0123800413564,0.0123704497921,0.0125415131343,0.0124153763654,0.0126203421043,0.0121940893378,0.0124888564861,0.0128744911933,0.0118193562651,0.0125650386728,0.0124623187789,0.0122681163729,0.0125442125737,0.0120451443947,0.0120849619705,0.0119600989598,0.011959301522,0.0120787481143,0.0123340536522,0.0126659825241,0.0123469222493,0.0126519187933,0.0125609903486,0.0123582157788,0.012481355075,0.0125114860381,0.0124750690496,0.0125580238636,0.0125065600772,0.0124791858336,0.0125848419825,0.0123441059058,0.0126777474619,0.0123374472888,0.0125441060443,0.0124592829911,0.0125743081583,0.0124144355809,0.0122988397822,0.0122831991963,0.0125361806162,0.0124066647714,0.0126916163567,0.0125325579189,0.0123227094823,0.012457609979,0.0121270824971,0.0126903445369,0.0122607653414,0.0126620954348,0.0123423296229,0.0126243265515,0.011560954012,0.0127191927586,0.0127074521339,0.012512617123,0.0123631844733,0.0126289654299,0.012365332595,0.0125066040466,0.0123549584557,0.0125904005912,0.0125151997322,0.0123477718385,0.0125415165501,0.0123386158908,0.0126520333999,0.0127301585655,0.0121616973368,0.0143138579592,0.0119255907583,0.0116583965814,0.0122556506259,0.0118220662647,0.0126108576496,0.0123041561248,0.0124251040245,0.0122739940365,0.0127686063225,0.0125095197844,0.0124744949258,0.0125788677527,0.0126274148355,0.0124643919902,0.0125555075804,0.0124804817539,0.0123776345758,0.0124335963843,0.0122043562551,0.0122097519966,0.0127112272082,0.0126722819165,0.0123163286829,0.0123690650316,0.0126483143304,0.0124312635118,0.0127882840035,0.0128144524142,0.0122012924316,0.0122566122287,0.0125581430897,0.0124354972272,0.0124749323759,0.012583073747,0.0123991957151,0.0124042884182,0.0118806416871,0.0120902956891,0.0121116135157,0.0119592678867,0.0119534439935,0.01272561697,0.0126595090737,0.0122711234622,0.0123566730017,0.012594140873,0.0122405843332,0.0123894992485,0.012325031262,0.0126739593776,0.0122678920864,0.0125013799905,0.012432817958,0.0122558631481,0.0127110835158,0.0119237025952,0.0124633672058,0.0120992285164,0.0131645071464,0.0117587284379,0.0125583568525,0.0121453965195,0.0121361728667,0.0128341131815,0.0123612413327,0.0124722731897,0.0122353544991,0.0127108242957,0.0127188943177,0.0125247678069,0.0121258510111,0.0116900913944,0.0130446639279,0.0122296274848,0.0121289220781,0.0127676896686,0.0125766975735,0.0123688791782,0.0124460145165,0.0123711914155,0.0123063074813,0.0126547879705,0.0127426488791,0.0122941938523,0.0127741085353,0.0122325733826,0.0122796758968,0.0126919928068,0.0118986989436,0.0123855133352,0.0125730774306,0.0125777480588,0.0124059991161,0.0123507644305,0.0122768753548,0.0126655065501,0.0126874781443,0.0122036358585,0.0126207191928,0.0126408660602,0.0124056971713,0.0123710947531,0.0122614191294,0.0127813384959,0.0126416525829,0.0122874918443,0.0124439609306,0.0123395315941,0.012683245169,0.0122784918212,0.0124121829241,0.0125392290159,0.0126357587301,0.0124181961676,0.0129203387611,0.0123980159811,0.0126469075366,0.0123530763513,0.0126437770275,0.0124028776567,0.012251730246,0.0126458987258,0.0127361912145,0.0124851648964,0.0123567718288,0.0125740530359,0.0122782838126,0.0125040148762,0.0122312944297,0.0127185698125,0.0121848711922,0.0125635177617,0.0125915076795,0.0124694566761,0.0123800898978,0.0122360450246,0.0126973448608,0.012157400485,0.0129128837739,0.0118609381356,0.0121438906403,0.0131661592072,0.0119665060682,0.0122910867633,0.0122971652376,0.0126436665307,0.012505798594,0.0126125316095,0.0123350406478,0.0121592187982,0.0123048598649,0.0127097069341,0.0124668698989,0.0125460599162,0.0124406339121,0.0125274216441,0.0125038449905,0.0125040302119,0.012172835787,0.0126769784212,0.0123022454737,0.0127053976629,0.0121449785505,0.0123096811886,0.0123810852334,0.0125126686662,0.0124399073057,0.012460276853,0.0125061269312,0.012244619889,0.0126099193934,0.0129632911161,0.0120238240773,0.0122273821915,0.0126516954561,0.0122216481391,0.0122893609117,0.0117245876919,0.0127708987687,0.0123140087304,0.0124675477043,0.0125103272789,0.0122842557887,0.0123349790289,0.0125390640141,0.012420440108,0.0121755947006,0.0126004988328,0.0124158553549,0.0124539612383,0.0125466829491,0.0124356074302,0.0122251916663,0.0126365323178,0.0123286107631,0.0125000763159,0.0122782049996,0.0125756194184,0.0124564637228,0.0126268176837,0.0123567732577,0.0123976460331,0.0125386187862,0.0126707179921,0.0125029702538,0.012316051311,0.0125574544951,0.0124083387655,0.0125407422307,0.0127536603538,0.0122554432337,0.0124841006309,0.0125858447533,0.0123943951061,0.0125169830808,0.0119331175736,0.011989683157,0.0120367914554,0.0119423757488,0.0119937391587,0.0125378746132,0.012536054412,0.012451980795,0.0125313731507,0.0124994497464,0.0124666970133,0.0130692753449,0.0117095344832,0.0120898726869,0.0123938100851,0.0127750902806,0.0117025244305,0.0125201467877,0.0124598383908,0.0121966574761,0.0120143667676,0.0119212077542,0.0118765954256,0.0128038960922,0.0121733326368,0.0127252977358,0.0123247018807,0.0126876079178,0.0122126900398,0.0125940942676,0.012447037476,0.0125487249727,0.012534979937,0.0125289698458,0.0124939976829,0.0123306079107,0.0126387461187,0.0124267883949,0.0127624196478,0.01234622446,0.0126718844216,0.0123080521303,0.0126369158622,0.0124385925386,0.0122323947547,0.0127775732018,0.0121334234139,0.0125090863056,0.012448670393,0.0124938772876,0.0125293135149,0.0122995287038,0.0122604782136,0.0127105296513,0.0126373364218,0.0124414987209,0.0124906919779,0.0127271040849,0.0123062440441,0.0125665778379,0.0124112917545,0.0123087259952,0.0126581463398,0.0122374015989,0.0127256617407,0.0122467786969,0.0127344812446,0.0122916677397,0.0125825766748,0.0122993077247,0.0126886912449,0.0124630656639,0.0124788516517,0.012227350574,0.0127187548641,0.0121159147386,0.0124414343261,0.0125493416264,0.0122828932387,0.0123101026057,0.0124822301773,0.0124806144297,0.0122564284748,0.0121250958956,0.0127813178778,0.0119561708134,0.0127682278839,0.0120222835841,0.0126921089297,0.0123618244872,0.0124782863196,0.0122837041637,0.0125420300917,0.0125708102432,0.0122570827289,0.0122925693353,0.0127001072529,0.0127170634496,0.012257698978,0.0127226362189,0.0126618463233,0.0127864922105,0.0121998151679,0.0126601422175,0.0123214067671,0.0122910170423,0.0126414513444,0.012614358638,0.0123784109108,0.0123020219957,0.0126231600276,0.0124003500735,0.0126000719804,0.0123373570532,0.0123307468593,0.0122083713105,0.0126323774535,0.012344171522,0.0124813538645,0.0124845601901,0.0124069956196,0.0125436937953,0.0125289885877,0.0123425393249,0.0123747345447,0.0124868152604,0.0123877795807,0.012590208592\n    \n};\nconst double PHI1010[NST1010] = {\n    2.24639082309,2.21849567011,1.30195843331,1.84407574052,1.92209035602,2.32564078358,2.17326274065,2.08860420888,2.13671805105,2.11509715295,2.20792305752,3.11064047693,3.02468014701,3.02320506322,2.94095211194,1.89012118829,1.81130793762,1.73310489855,1.43126516321,1.44937794061,1.76831134201,1.6794232202,1.60642741149,1.57681969114,1.66212001362,1.49976128037,1.51215406198,1.333319254,1.1533155387,1.42787434794,1.64403276003,1.69729290714,1.59970755765,1.57288597517,1.61635822242,1.67460881724,1.60721751183,1.5095665445,1.51978424623,1.69283590848,1.61805746671,1.63722539876,1.44104603867,1.56507014695,1.27569597778,1.37712757937,1.46617980435,1.21823828469,1.56194354002,1.4780058009,1.43072946151,1.09858267466,1.09886974962,1.37051765888,2.01299591624,2.09779811144,2.22003364982,2.25718476956,2.2747160507,2.36899905666,2.21966647033,2.34534224694,2.25875952672,2.27822650196,2.25102580583,2.32628528601,2.04366082203,1.95247816232,1.88049536866,1.89063132511,2.22427686078,2.31674083697,2.61379138461,2.56081280986,2.66427116507,2.69199306575,2.91594252831,2.94724073213,2.85988073777,2.78161763293,2.79897968352,2.86031735443,2.95985547158,2.96952885963,2.85070817946,2.28322927297,2.29513365148,2.71253309157,2.17581069432,2.1980438639,1.85481847151,1.86590567862,1.88411325687,1.78312801126,1.79122649812,2.60267883895,1.75483357442,2.06365447401,1.61384948969,1.71783453747,1.63205204334,1.62321722179,1.56563362902,1.54690935485,1.36993206884,1.21665100112,1.4907976919,1.39283212709,1.34413366121,1.31685766753,1.60089534055,1.51099481099,1.44002774715,1.60296343244,2.0909904519,2.11873624581,1.83666966022,1.81871208317,1.99680937539,1.92759505203,1.98063738676,1.89351697008,1.32052734437,1.30544034841,1.39930046804,1.40617577169,1.13259222521,1.06079240533,1.19744396859,1.26284136086,1.34495256491,1.18263647363,1.1953691103,1.93112978174,1.86506507117,1.7423831219,1.76903310465,2.17013746921,2.09647970698,1.76504859586,1.95946291843,1.80539561363,1.89782708216,1.35149336551,1.52777127654,1.75165349396,1.73085250614,1.52605961845,1.67897260249,1.58613936247,1.40308971393,1.49972224888,1.51691448751,1.4438878269,0.741091791397,0.313547603813,1.01712097127,0.930299195795,0.769937726427,0.844607781742,0.852192261067,0.947985433838,1.02742868954,0.933374477997,0.803072624984,0.83632567515,0.704300913836,0.797516863863,0.9371265327,1.03263980927,0.191352987476,1.26582663723,1.0984453812,1.11155993471,1.19519706472,1.37356096917,1.34920627308,1.18581881143,1.25493527119,1.48500511931,1.41164177347,1.51294569075,1.42507471256,0.87001934654,1.36905740541,2.22281420841,2.24208612958,2.33599808933,2.13946151573,2.36190895013,1.49447431506,1.52846063362,1.38026009725,1.46261034936,1.61181549174,1.51694680949,1.34927241097,1.18429678944,1.26433082747,1.35995996762,1.12520948766,1.09773323563,1.1597107119,1.26981605458,1.2453562239,1.11461325156,1.20226969386,1.55065925832,1.52687686253,1.47761570519,1.38338013222,1.43151462841,1.36087680681,1.4496103859,1.31161174736,1.35653647141,1.28708227726,1.40651638666,1.4752397167,1.31404705306,1.25347097768,1.38073374136,1.33709005445,1.69751710375,1.43527443134,1.52887110202,1.01867639591,0.921333767944,1.23651317489,1.17689589225,1.07785081067,1.05475673015,1.2188813836,0.970095344136,0.895446679685,0.553793927617,0.467837194473,1.62901697644,1.77335026396,1.67501650662,1.74487492707,0.989069295727,1.05421340435,1.06560019728,0.967199973071,1.30690554369,1.13609138169,1.13363619879,1.21604842986,1.38675764959,1.29410765762,1.13366142132,1.23608817295,1.27609533836,2.07972023828,2.52354821761,2.36515678122,2.19529019958,2.02356973863,2.05880900866,2.18660298751,2.17093381574,2.25390603204,2.34126718313,1.44360908178,1.5305789621,1.60977351421,1.4306783511,1.3906358458,1.72219733523,1.64821988687,1.78858747913,1.71615188777,1.76784851652,1.67181811013,1.44572705231,1.41329283739,1.53975477348,1.70934577896,1.5400372696,1.61352046883,1.80661684189,1.72410302582,1.80491206279,1.71703324125,1.62628973017,1.6209707617,2.49200699091,2.39109330512,2.34889991762,2.3986838792,2.70193123673,2.49944423231,2.65511680183,2.55140903454,2.78362844004,2.42409850498,2.49205574609,2.39775141775,2.37469572094,2.49422740309,2.3898689198,2.21682539622,2.02411091887,2.14781777217,2.05307268895,2.18711713653,2.05760965423,2.08913667754,2.32387485099,2.22896011333,2.2345224019,1.97477363059,2.05589128951,2.06051826534,1.97594308911,2.14694540117,2.14353343967,2.66807358556,2.72411529597,2.70067303773,2.82646946916,2.65707355015,2.5627500496,2.55803606986,2.51693404866,2.82250782901,2.80308405943,2.87193360173,2.72146782097,2.704211616,2.6668197071,2.12355140541,2.05548121878,2.13640981453,2.03253110203,1.96495536932,1.95482583607,1.8293819002,1.81520854315,2.01630622832,1.92758560339,2.08519670266,1.91357466077,2.06812138358,1.98528270117,2.6655832191,2.81690109213,2.76442629014,1.96564787228,1.91024001399,2.04315468758,1.94725873206,1.68850579946,1.46125290388,2.07191294737,2.167234327,2.18762038502,2.00461975014,1.91453248637,1.844985574,1.79253899864,1.88573791609,2.13501642518,2.05774620219,2.02752527896,2.11517753096,1.88322580978,1.79322932915,1.78201559368,1.86119132078,1.95331622559,1.96417321732,1.95149015007,2.04805362066,1.19206777042,1.25098688352,1.26968424661,1.99586385892,2.07204027789,2.18728336843,2.17016706254,2.02992499412,1.94146376325,1.93330012947,2.01262383019,2.11498178261,2.10568547511,1.12685001982,1.21614051316,1.22435788983,0.59206176905,0.627422670975,1.05919738942,1.09455123711,1.02407124409,0.92773623507,0.896008291211,1.22071471226,1.34854925946,1.32462497392,1.15399468956,1.28600642975,1.19588027359,1.81887503372,1.84111093867,1.89021981391,1.98496644303,1.93364722751,2.00642929627,1.21299355198,1.25344768514,1.68312199965,1.85893698011,1.85659950713,1.77082402355,1.774579652,1.68967349871,1.68438348753,1.77447662816,1.58714698224,1.59235786746,0.521031822426,0.488786343813,0.563528153365,0.62470065738,0.660229462727,0.689770247542,0.761372993532,0.754672806272,0.679982213699,0.871610508616,1.0192607799,0.853325261904,0.932553014185,1.12620433145,1.0341825248,0.96568683018,0.994502091106,1.08847511154,1.15187063291,0.505665055404,0.397464949266,0.409483838531,0.583388429526,0.470955953991,0.28506213523,0.335534946631,0.42887960488,0.76940336783,0.59510410302,0.562800236199,0.616752643971,0.705856963768,0.619079816811,1.05302628843,0.678840736282,0.773215724082,0.757415816455,0.672852188645,0.846257314235,0.855852548832,0.510041580707,0.438040698267,0.349843697385,1.30620799552,1.32557147854,1.14870303134,1.21289935843,1.25317018418,1.16970596582,1.57349209469,1.75388813125,1.677613464,1.58810819549,1.02744876052,1.08707545885,1.19540287331,1.30203925998,1.34738341908,1.29856035559,0.75804582982,0.719273560423,0.908609220405,0.8527561047,0.994720321826,0.897682899421,0.863514891436,0.90433951557,0.850692851905,2.23703626148,2.35533848679,2.29092708484,2.30240060545,2.49885646684,2.46576026659,2.37843592055,2.12330088137,2.1726594054,2.19456669567,2.5156014027,2.56543264492,2.50314745913,2.4123053399,1.45153168297,1.52710329806,1.28551974185,1.23182465483,1.30406564038,1.13808157807,1.11950772767,1.19490719061,0.996422997149,0.93272586711,0.831230912614,0.793204711578,0.960486531736,1.04759844148,0.892487591907,1.07008556288,1.43599671365,1.47257732956,1.56117568829,1.50100148925,1.59747922864,1.62122700232,1.22648737888,1.31628893543,1.16104995988,1.1882186,1.28288698725,1.3457316628,0.566445940732,0.660615348844,0.464538277309,0.524109243737,0.491052009727,0.481394632324,1.47004712358,1.59139614986,1.49942611556,1.65370000378,1.5282862326,1.61976956002,1.60207942444,1.76609231814,1.69151170085,1.58771088954,1.66570166131,1.75453841899,1.35488695809,1.30838257825,1.15968966858,1.04609155904,1.14733863496,1.20439419118,1.00189934914,1.05902250912,0.8085535074,0.637448433391,1.67601098483,1.76813765028,1.86187837664,2.03650530524,1.9695805143,1.88391049829,0.824232082145,0.897212431399,0.732814002613,0.886440878621,0.721478550665,0.802477790825,1.27693658644,1.22207637803,2.00547439411,1.91516075537,1.8428050074,1.85505144851,1.94495561337,2.0310759366,1.94102051675,2.01917294702,2.11669075554,2.10861239168,2.72197688641,2.55003910141,2.45883264749,2.37531956261,2.44911253668,2.3525861555,2.27255779901,2.28543103856,2.1979694778,2.12855457413,2.11134209208,2.23133268395,2.27862366907,1.80184609255,1.78289636724,1.89488731565,1.96450009603,1.85796633901,1.94641296537,2.28440337085,2.2784855409,2.35040705171,2.64384039203,2.47777049598,2.44457342378,2.46617215897,2.63728769922,2.54019398499,2.48356151033,2.63590319087,2.53631394171,2.81126059089,2.83174873005,2.90931878408,2.88003465687,2.99673120214,2.9142645705,3.07520752915,2.9993311551,2.11602320743,2.07324264183,2.20907509357,2.11759974806,2.79459025895,2.96645850433,2.86974300017,2.79533644281,2.95886250681,2.86293416118,2.56831807173,2.66827107578,2.70357649628,2.62488364771,2.53171238211,2.4568638145,2.62861172299,1.72788706454,1.61726448457,1.70784665141,1.65303792872,1.5576804674,1.54178496977,2.3649073789,2.29248297019,2.31095464992,2.52170341837,2.61732959188,2.46216441801,1.96950284438,1.92093281267,1.82269954436,1.77581181064,1.76774580648,1.82181804965,1.91690456922,1.80389281747,2.10461962889,2.06376995676,1.59834747348,1.65495993224,1.45028154566,1.49734596296,1.56645982517,1.54969886524,1.67493362055,1.75241758746,1.63444693144,1.72848994605,1.90189879632,1.71686330668,1.7890865373,1.88128004016,1.15279753701,1.13882471788,1.07384013259,1.04175976131,0.955899631132,0.972180468263,1.15474623031,1.13877861392,1.29808388659,1.47272719067,1.4086154843,1.32246175443,1.45445950227,1.36620067419,0.34881874316,0.404883166008,0.398081556916,0.560508313604,0.636388317595,0.536232411535,0.491623955621,0.574766469401,0.673156187393,0.665215864768,0.593479025709,0.497767345949,0.486314667695,0.992799184525,0.894203922861,1.01721272126,0.935458156198,0.855695278581,0.853226929957,0.717768867303,0.752569213838,0.790123521544,1.08960886189,1.02078727708,1.06476262755,0.967123175016,0.890930735425,0.920320522132,0.348267760053,0.422653104111,0.731028005372,0.772351141415,1.22858427356,1.33481321495,1.24613526226,1.18803121463,1.34637697798,1.39479189922,0.777808532747,0.931089396606,0.871449333664,1.04717070808,1.14598573541,1.06510047684,1.00522660061,1.16472429104,1.20417221136,2.32861457067,2.45009595556,2.36144847928,2.3912331892,2.5187347413,2.49250705809,2.10665482544,2.20017015011,2.06443433428,2.11437795219,2.25620308765,2.21224780857,2.36051059455,2.26017086737,2.33575302192,2.402421836,2.23685462018,2.20268829782,1.69264181985,1.39733196695,1.47047377169,1.41951354048,0.965687056863,1.03267820726,0.841515149034,0.864511513496,1.00425559878,0.914317872176,0.180208645074,0.320334335483,0.276004466949,0.0103118360256,0.112545019408,0.494970178565,0.104665905392,0.164379969024,0.689635685506,0.775270197356,0.673011125182,0.627225027554,0.37759123694,0.388958471721,0.286216662103,0.188241813187,0.571419744158,1.16408219291,1.49254576725,1.47504651225,1.37262999055,1.41315576576,1.32230472203,1.36747069285,1.38119049522,1.47182523071,1.48295872974,1.52866557668,0.736509383756,0.766127879344,0.576220149988,0.646364646366,1.24057539301,1.18553244705,1.31232500099,1.28909665834,1.01262652854,1.11031582977,1.1424431482,2.62809919648,2.59349659581,2.50183228326,2.54789248206,2.44609297397,2.43217556388,2.73494713271,2.80108441953,2.82191540233,2.69784534223,2.62358063121,2.64144255191,2.28903986397,2.20457710348,2.20524782072,2.29252475809,2.38611357788,2.38176934467,2.46775309751,2.34686843452,2.36825683953,2.4141830842,2.51249938175,2.54583753554,2.72878653126,2.63532668449,2.74690488457,2.65969882922,2.57095254398,2.56315228468,2.2087758321,2.24894660995,2.34941855593,2.25803172538,2.39395819738,2.34177664339,2.38174325983,2.4642589381,2.54260323271,2.51561471099,2.35887777393,2.41574082089,2.65515249871,2.76570106949,2.75227098803,2.67562100907,2.48397648333,2.41216698905,2.40458464831,2.49943443753,2.58811475799,2.57976395459,1.89488107906,1.92463533277,2.0133508397,2.07814196287,2.04844069045,1.9540376988,1.51757237948,1.60968724519,1.45944722337,1.50630112611,1.66668536588,1.62113474349,1.8419019987,1.82707839106,1.67448349552,1.76425392159,1.65930600271,1.73436141947,1.09365357286,1.07278281425,1.04195702688,1.04573090619,0.258567867427,0.0787725599242,0.150712957154,0.229786196632,0.677783545524,0.643737968713,0.777534107933,0.806798431268,0.72860389276,0.473402291137,0.603879393621,0.512765600257,0.439757785691,0.632283574553,0.573338358331,1.82345239532,1.78834281866,1.92525142233,1.98120532204,1.61990361061,1.69053203406,1.53617708754,1.51372026172,0.467600840888,0.438680301874,0.338714043557,0.481034849242,0.295884025156,0.195969052718,0.185954152475,0.285450408752,0.368521738986,0.372853429143,0.207575183808,0.158325887828,0.23533067065,0.323340268617,0.359842398443,0.313807438993,1.23234376714,1.24901290972,1.35380202917,1.32062867512,1.42386307811,1.44046797215,0.562760328252,0.612363060357,0.711274201606,0.755782342516,0.706965261375,0.613832897999,0.946136101426,0.850073500312,0.786943577745,0.827624612573,1.08086689794,1.07549737828,1.12441515926,0.976381217754,0.926612034453,0.98262858517,1.06115758157,1.02555734095,0.932549236464,0.873668780173,1.01022076927,0.9161849355,0.959563386799,0.97105258994,0.902109857959,0.874343409215,0.875369955677,0.796650122804,0.977464891462,0.96412897376,0.90476826139,0.813302261932,1.78386957924,1.93404066263,1.84351786345,1.58574524923,1.56454076121,1.63850844573,1.62958217561,1.7389869025,1.64819813256,0.311643224822,0.413709322278,0.257070938853,0.323134832419,0.460581676979,0.42017138893,0.544468224131,0.710075772618,0.745989405454,0.723476544838,0.779209896671,0.624296261686,0.795388327733,0.632524441411,0.709433159572,0.65340255563,0.746480709458,0.811710428205,2.15394363363,2.19377391092,2.22661726128,1.81079372602,1.73580676335,1.68170365039,1.70325284156,1.75798671849,0.652799758584,0.544503209718,0.632741559209,0.585919821341,0.486370539308,0.464098396535,1.90851219416,2.09820114528,2.06370539078,1.97254843071,1.85749638152,1.9333108424,2.05473878692,2.03144487755,1.81571291739,1.79844845693,1.90850631407,1.87657280498,1.99017303578,1.97541508973\n    \n};\nconst double THETA1010[NST1010] = {\n    3.49412297161,3.38185430939,3.01184372088,-1.45249496318,-1.38352336827,-1.01175831029,-1.17880039335,-1.24862026344,3.15026286033,0.284627166574,0.328927843406,1.21938729691,2.02914391835,0.393946778077,0.749791391831,0.687045239757,2.3258398904,3.13413240755,3.41161231818,3.50150531862,3.34032170376,3.31084685632,3.37815224184,3.18505325903,3.20908420864,3.25663228625,3.348390433,3.38361570356,3.79652836111,-0.480873930541,-0.225010363515,-0.0436714432891,-0.0646604267266,-0.154601900375,-0.313081325867,-0.393016140154,-0.471015091695,-0.43681379096,-0.340046324418,4.69536912894,4.62696524833,4.53134457715,4.29811163882,4.46165070205,4.24263060342,4.21972549015,-1.23223354117,2.96168847686,3.08301258293,3.0631097309,-0.581925596438,-0.472733109178,-0.585620399048,1.00433623242,-1.41624414369,-1.35038989609,3.15010483073,3.04390188475,3.28009107652,3.28209100496,2.92241167105,0.0517798508984,0.124468716778,0.248860734034,-0.772450823336,-0.0676233465669,0.353856017664,0.310142827926,0.373765266075,0.479161659325,0.450407150876,0.497833830903,1.6965748563,2.03625044635,2.01592935686,1.80797400453,1.18711045308,1.65006520023,1.85559341871,1.68814500284,-0.547857765499,-0.781789134549,-0.730981303014,-0.164289411768,0.558909226464,2.71817499664,2.83872279151,3.44545026914,1.16594732202,1.04532800089,1.09181467335,0.989749207473,0.788741895318,0.92664922123,0.829799191722,1.50902857349,2.40711828874,3.06694978812,2.90116367246,3.03072706551,3.00400562526,3.47285124593,3.62729937262,3.53252133233,3.56573377959,3.70902392329,3.68487998655,3.65474414665,3.81286292083,3.72448327966,3.82381941853,3.78295430028,3.84094719132,3.92735861611,3.26026500338,3.36210608981,3.26984178867,3.16511148437,3.23470962607,3.30456143081,3.12822380855,3.09355386239,3.29135946205,3.10430913002,3.13622829694,3.22817774296,3.00556077366,3.9967282628,4.17037810277,-0.475757312823,-0.426490275004,-0.418467857116,-0.308705923314,-0.176269989909,-0.0967101741045,-0.208264600607,-0.116506992827,0.089928107502,0.169604751445,-0.385574508966,-0.3609894183,-0.288643345079,-0.276834109269,4.70580465497,4.65426206139,4.40010353065,4.49951251533,4.28712167808,4.33916142175,4.3684352228,4.13119673894,4.09124781144,3.98777695658,3.94248408863,0.374692959312,-0.737142065448,-0.6489412029,-0.599105536491,-0.877560003498,-0.669562236466,-0.792826905786,-0.833828547427,-0.762183448238,-0.0273583401125,-0.205971524994,-0.0719367999755,-0.248704569798,4.24367930282,4.63296878416,4.5901066123,-1.35455693106,-1.55150758214,-1.49726086979,4.66906575708,4.64709513727,-1.19828926508,-1.09792278917,-1.12533527052,-1.06020663212,-1.33178708062,-1.40115399355,-1.53088627056,-1.50257690641,4.00371098061,2.59804178282,1.37620239725,1.25005501112,1.2262207489,1.40907547944,2.63357171301,2.77836862396,2.8761696558,2.93990802574,2.95878471215,-0.557548647425,-0.623713348594,-0.633561515095,-0.637120504344,-0.582370396688,-0.736260885417,0.463288917594,0.654155755029,0.565841564866,0.0115986365891,-0.0934465593555,0.0519387519324,0.0853865525745,0.0953532110779,0.00429598100368,0.165719616346,0.142161643873,-0.0232047048847,0.0438689898342,-0.280527122301,-0.158333277034,-0.322287220633,-0.262621332351,-0.120704917771,-0.182768371182,0.49388321223,0.575428864502,1.76016407317,1.85106700143,0.964918481898,1.07798847114,1.0456331901,1.95286211113,1.91775418333,1.83763104185,1.90776174741,1.8751442905,1.76101317362,2.00612438664,1.7133979291,1.79055805637,1.49976795652,1.58644460836,1.99218574587,1.61034908446,1.90142066348,1.70954948293,1.35328483768,1.53045585881,1.42025800077,1.59049695159,1.68773044134,1.58252293386,1.6916009294,1.73415143757,0.67987926501,0.672175186323,0.748683479335,0.756744738156,0.843191116506,4.42833270149,-1.10299134276,-1.55025689097,-1.39621259252,-1.52329526725,4.54097662479,4.27271862235,4.38717030987,4.47719195376,4.4430919407,0.766530862933,0.945589903267,0.90471197115,0.918638224906,0.844977906197,0.0475092847348,0.117193214274,0.333518931116,0.394665724924,0.234973193864,0.207361880822,0.603046795304,0.513611638406,0.645658478554,0.771325368482,0.748433977747,0.807505840022,0.535888588399,0.492416755368,0.636589935749,0.679037635606,0.52619422157,0.612832597407,-0.943542863814,-0.911335516461,-0.784232296121,-0.667746414453,-0.628274759372,-0.674305413233,-0.831474277803,-0.824517271486,4.38688728571,0.147033774659,0.451966291111,0.406139789513,0.277293755339,-0.12144184236,-0.154442130004,-0.108101777063,-0.157805960993,-0.0260493243043,-0.0512831550244,-0.218862448094,-0.345727951422,-0.242599529155,0.624025585468,0.807993290964,0.689868163895,0.526850111191,0.466136723858,0.688090953794,0.634734080747,0.63216665478,0.517045816449,2.3665296053,2.18798032549,2.57425940461,2.17378376632,0.393042828594,0.339872297085,0.0299246175458,0.158390500489,0.25425992455,-0.251946773873,-0.0401637078798,0.227404713399,-0.153656903595,0.0445230038764,0.973196782236,0.798188406957,0.858971933283,1.01623757744,0.845562442333,0.950363865334,1.3008487003,1.4059241347,1.12652607313,1.16200081666,1.20317433747,1.27023608799,1.32031547325,1.35155113617,1.36932138682,1.18555769332,1.41353604071,2.24665780458,2.33366021886,2.44403020843,2.42960726795,2.83132447528,2.58449699739,2.54816410189,2.5705003361,2.68280699951,2.63458717783,2.61564699362,2.69547830876,2.49988161656,2.51421024135,2.88925023312,2.96419082442,2.74402638969,2.76895943895,2.99097125234,2.95956575067,2.86261504643,2.79384435341,2.82280602016,2.92364752899,3.40965648105,3.44162434067,3.61522280328,3.44938419672,3.54219816658,3.61489185304,3.54463838069,3.68649736821,3.576424758,3.93030718086,3.88524269914,3.78265831512,3.71870892956,3.86751540988,3.75745862031,3.11582472463,3.16326898712,3.25909996758,2.81993088254,2.64795722057,2.93713274099,4.1877979709,4.1005665343,4.10937453765,4.22182139455,4.08049162123,3.97925539553,4.06471075583,3.99448941532,3.89699205622,3.89628181219,0.071976703007,0.169797911343,0.0010617693902,0.0275032992297,0.204188165091,0.133470324172,4.53711471759,4.3347867236,3.96617036604,4.04499508037,3.94368451857,3.9027460258,4.19819701517,4.24201633193,4.06058350136,4.09786353394,4.12480113831,4.20668991105,0.694727023251,0.495121101724,0.399408699509,0.726972309876,0.469407558848,0.606917480886,-0.475200166396,-0.61021680664,-0.388682382322,-0.282481153669,-0.414336514085,-0.413735396469,-0.475966574513,-0.24513182885,-0.295261315923,-0.227479631224,-0.105111289094,-0.06225135,-0.133577963347,-0.311047863756,-0.594086077019,-0.354587231493,-0.440447528529,-1.27751666661,-1.32471246433,-1.04004124625,-1.07183529136,-1.49538323639,4.69550320098,-1.25992861767,-1.40946593894,-1.37803677053,-1.10194415009,-1.2701182481,4.42682815363,4.37099114095,4.64286831381,4.58652975752,4.56949899298,4.45238372954,4.62868023735,-1.50118392024,4.70997380211,-1.26535351563,-1.36892235921,-1.30238399778,-1.23054952942,-1.44401151316,-1.41237176186,-1.36290683554,-1.42167072292,-1.49021777548,-1.46098581819,2.74688373218,2.82379007852,2.34741782843,2.51302015259,2.42521816451,2.34407939931,2.47477314397,2.61518863029,2.56864487154,2.46407461257,2.34921856757,2.34885694543,2.00709353416,2.12933568021,2.23247821077,1.82670594228,2.04422862987,1.57694968864,1.45518421423,1.49692880159,1.36431873032,1.36109977726,1.52979764381,1.7305422856,1.61706537192,2.1726303062,2.32845203298,2.44315960166,2.16316157137,-0.778685033538,-0.718366244995,-0.792459422574,-0.956266864582,-0.895986272025,-0.914152820534,-0.806001358144,-0.745232565866,0.644530553105,0.741460601881,0.739289608714,0.624330922646,0.0966866166454,0.133006176005,0.188776842424,0.247571389314,0.33567216047,0.435175574679,0.446478021649,0.258012630224,0.277425329914,0.366684458246,0.193230615013,0.217253281823,0.273741921333,0.377706912846,0.398374783768,0.318859057619,1.3197508554,1.27091384714,0.839169936714,1.01409099163,1.18604845383,1.79951159766,1.74782731063,1.64103185344,1.65279499168,1.7227367857,1.8312256371,1.81811340163,1.10457675781,1.12841402357,1.06514327367,1.20239362455,1.26374761465,1.22961081287,2.25907233108,2.17889675347,2.0822925705,2.25251638372,2.25883846694,2.17557316888,2.14872792233,2.06213306207,1.73703291797,1.59600755102,2.07258440007,2.23825598942,1.59291607599,1.55286975311,1.4642382303,1.48740974603,1.31895947917,1.39958537195,1.37000880132,1.5284123261,1.52057551705,1.59541160042,1.01612167561,0.928663465241,4.36579028568,4.41481962124,4.35666870746,4.25124535937,4.09241398532,4.03694770064,4.19906171257,4.25471664944,4.09133835739,4.20343146554,4.60369505838,-1.43120565151,-1.34752446307,-1.40568969231,-1.19214050001,-1.13735954293,-1.21730255133,-1.33483509681,-1.50394400996,4.6073459215,-1.56413372217,4.58557404467,4.69947296437,4.56218719401,4.6628082239,4.52150490382,4.58434730129,-1.55407798485,4.69168312782,3.72613176331,4.22239484622,4.30136836858,3.60893217243,3.82595875573,4.25936676146,4.11841258729,-0.491846282822,-0.535646381786,-0.394809389332,-0.282578755007,-0.247619480398,3.48072147392,3.7861171243,-1.52315145278,4.38242664217,-1.28476446364,3.98940474861,4.35297316046,3.72357320642,-0.439595184189,-0.545299491109,-0.657123419474,-0.651248255361,2.96329710389,2.59501354287,2.47023099692,2.67540444579,3.1460392308,3.20198579826,3.26861643974,3.25384270002,3.04143563381,2.69399616762,2.60853944317,2.69302704706,2.89632668994,1.42999563183,1.54653506035,1.52935388871,1.35957128645,1.38629908117,1.47438287199,1.10235823811,1.00590372784,0.88140902213,1.19885116531,1.18714492189,1.07351728843,2.06031905109,2.1537896607,2.15334351336,2.06787599588,1.89139289057,1.97737073665,1.96942671659,1.79470502138,2.35441476176,2.25260470225,2.48202473145,2.40136355956,2.41467063077,2.48885230709,2.71274907391,2.63382518526,2.74043108936,2.67394795191,2.56752709071,2.5793465235,3.57935074895,3.50717916998,3.44237229334,3.47658973947,3.42154597194,3.32270417812,4.28473691626,4.48673300989,4.41439590314,4.30902817341,4.35869642557,4.45651170656,4.52009027403,4.48351005215,4.40158363304,4.41496396085,4.58087674982,4.60368086826,-0.12925540416,0.103289805489,0.369056631318,0.207461807297,-0.129588399075,-0.135548763741,0.0542907951153,-0.608907277734,-0.834400573563,-0.6865658509,-0.945716131427,-0.90199447208,-0.704587125148,-1.3507617201,-1.34084287689,-1.46758718934,-1.54070130734,-1.46245233642,-1.23650229887,-1.11313298448,-1.2385471066,-1.00367041446,-1.08802607526,-1.16049257242,-0.980161099666,-0.944515067207,-1.02761352623,-1.13624955123,4.41161581739,3.9011808778,4.13781325659,4.00914075127,2.69221007273,2.68217437303,2.85770092065,2.78942160423,2.84450921084,2.76547547089,2.72582286184,2.78150981661,2.69095955013,2.44652403337,2.43842200601,2.638113266,2.55105117192,2.61820633755,2.52268702199,1.80891344648,1.64159683437,1.67128093326,1.91991190092,1.76035013071,1.90331070551,1.95303386348,1.94446731876,2.05709853631,2.1564812701,2.0498156444,2.1581614431,2.27869842752,2.2679921515,2.50652128466,2.40559894439,2.48068467913,2.36785079364,-0.591555669427,-0.936438912131,-0.875873829289,-1.03495027539,0.541601333376,0.449370889204,0.409486397203,0.524550821748,0.337024935176,0.315537321868,4.39909644281,3.91346879948,4.22423371398,3.14619556995,3.94482736598,2.88234904609,1.94131748744,2.43975280873,1.11992011622,0.854601117542,0.860822323981,1.00074660085,1.46594106651,1.23034468466,1.61686530683,1.46226038037,2.50381182855,1.38495356042,1.23368952992,1.32225952383,1.34381006471,1.16862260423,2.01536904907,2.09536148695,1.93116164633,2.08863446263,1.923509769,2.00115219383,1.82554719136,1.97106219741,1.87083790714,1.76372325471,1.10906097726,1.29285639575,1.18767708089,1.2747520086,1.24008022764,1.2163603876,1.11906800491,4.57011373889,4.38928926972,4.38944893533,4.69593681243,4.6394398016,4.50953648662,-1.44470227258,-1.04093960807,-1.32445425101,-1.00732265737,-1.15840068755,-1.33709484563,4.09244155866,3.91401543693,4.03259932851,3.84529681109,3.90149222137,4.03237768744,3.68252911423,3.5188926329,3.64267459667,3.4089555646,3.41743525972,3.57019567438,4.16973674844,4.20640426005,3.92118395283,3.80573764644,3.90129180366,4.0688128715,-0.431626512857,-0.313573537211,-0.542412972946,-0.543205868099,-0.406749784512,-0.289976291731,2.90967626834,2.85350088254,2.96303500525,3.13176886658,3.04668650617,3.1533793023,1.0053729775,0.710805832652,0.962155195725,0.597442487492,0.923167374745,0.688398587905,0.830705680976,0.612743751466,0.696476573561,0.870336316793,1.78312245365,1.68018972102,1.66386108765,1.75331013354,1.8586727712,1.87159190402,2.16426563518,2.31906943458,2.25100940083,2.32559670623,2.23741330415,2.15711385381,3.74269456495,3.64254223069,3.76396510503,3.80362100705,3.66510638704,3.6050965899,3.59618156314,3.49451886229,3.16941040089,3.28178240267,-0.123096364882,-0.125693979992,-0.782932949528,-0.53574293722,0.00262373186949,0.155993640792,0.0183142510909,0.154566493937,0.244335908137,4.0774810197,4.33501243162,4.41887754511,4.2967557623,4.16841497781,4.04188053065,-0.46754565797,-0.553857305152,-0.45507784495,-0.545359006559,-0.755934231491,-0.695582455351,-1.16750163504,-1.07021659011,3.09598847731,2.73073113883,2.73206582607,2.52369862362,2.98533316338,3.01028474204,3.49253708166,3.63008855806,3.44310578092,3.19237657085,1.02539095259,0.515386171422,0.289407239508,0.502794662317,0.79373144299,1.01953613773,1.53951096732,1.45069261453,1.42839919437,1.60085871345,1.57877532201,1.49440976905,2.17877104007,2.04366388497,2.07795346218,2.21759393137,2.34466842809,2.33976393009,1.15705772475,1.1878974509,1.09049678192,0.963834582819,1.03135388492,0.840071076027,0.933618816573,0.842683500805,0.949914154411,1.04463657666,3.79184332454,3.68481728628,3.67104568194,3.77554464547,3.89379468815,3.89178894442,3.10868119628,2.98526375128,2.90788728939,3.17111120451,3.30114065345,3.38534232777,3.46448470855,3.34917626831,3.55121528687,3.52223122224,-0.722207175994,-0.642533519781,-0.638589877682,-1.00863028549,-0.912397055945,-0.853185088139,-1.19853631098,-1.32385425692,-1.29474268735,1.90461442705,1.94453584417,2.22322264596,2.424114037,2.15705542259,2.34248700183,3.19645344029,3.33554881491,3.62945529,3.88567064901,3.76436964299,3.88593435802,3.10274952833,3.10125104304,3.17953937897,2.93029231625,2.87069013848,2.96065251749,-1.07154067638,-0.8731319149,-0.986294860772,-0.812869937883,-0.883212327806,-1.03900834146,-1.1320122608,-0.973192672838,3.60352866494,3.38910406659,3.44311889338,3.73683950913,3.71733044613,3.52256154976,-0.829963340814,-0.858829554727,-0.752100554956,-0.743495674764,-0.997751889681,-0.925345666341,-1.04559169623,-0.945236544395,-1.25491737739,-1.15882748892,-1.28320966551,-1.09089726049,-1.21505856038,-1.11815161832\n    \n};\n\n\nconst int NST1196 = 1196;\nconst double AREA1196[NST1196] = {\n    0.0105413280503,0.0104684519079,0.0106691150452,0.0103394603654,0.0103664994121,0.0105256950535,0.0105122734251,0.0106738694517,0.0100294481215,0.0107387747231,0.0103714552469,0.0102553599552,0.0108393971118,0.0100254841885,0.0106813192593,0.0108000767359,0.00996876674383,0.0116690910713,0.0104894586529,0.0100870699359,0.0103024258835,0.0102543865634,0.0104841027585,0.010370849411,0.0105334564756,0.0105733679033,0.0105395048446,0.0111017191903,0.00982437570698,0.0104734639299,0.01058292671,0.010761337823,0.0100814243555,0.0102489960059,0.0100766263298,0.0100708166792,0.0109961564616,0.0101003056023,0.0102030450105,0.00999864780198,0.0106810620315,0.00989274892885,0.0102105016332,0.0103985820081,0.0103397354438,0.0107401556934,0.0110557964424,0.0104527915134,0.0105768727994,0.0107396469992,0.0102084948769,0.010400693081,0.0106007057951,0.0104944960259,0.0105845336627,0.0104131811371,0.0106054613707,0.0104764313124,0.0106024853212,0.0103473693542,0.0106511463271,0.0107398702769,0.0104174349402,0.0105692941786,0.0104133143245,0.0106676889015,0.0106739545143,0.0105578510927,0.0104540566879,0.0103487721351,0.0107639906508,0.0102549418747,0.010221418405,0.0107226846025,0.0101819883419,0.0106037370125,0.0104032094461,0.0105631930731,0.0106373054183,0.0103275797804,0.0108041690475,0.0102778224321,0.0107507869777,0.0102028794199,0.0100683229828,0.0103393960485,0.0103319454066,0.0105326927412,0.0107138536692,0.0102771441278,0.010397955464,0.0103885572623,0.0104558967874,0.0103578584587,0.0108559790899,0.0107349105976,0.0105563043499,0.00996571182433,0.0104489201232,0.010450217357,0.00985655555083,0.0100949377995,0.0106590914123,0.0105325707841,0.0106435995496,0.010428294729,0.0106167297473,0.0104901182397,0.0104559364111,0.0103438008893,0.0106557354763,0.0104097313614,0.0104741261666,0.0107473613022,0.0104235775329,0.0104859044507,0.0106835082259,0.0100245402692,0.0102322530641,0.0104577375395,0.0104465599201,0.0106543315422,0.0106106531093,0.0106193985011,0.0108439513215,0.0103997046333,0.0104710557648,0.0102720391437,0.0102888217542,0.0106873263584,0.00987433435392,0.0107463142361,0.0103814359287,0.0103601233673,0.0106930550552,0.0105205752269,0.0105460779841,0.0105710474284,0.0105490914623,0.0100186299285,0.0100223136968,0.0106977558575,0.0105149625334,0.0105473287249,0.0110518157187,0.00993869851403,0.0105164054639,0.0105260357096,0.0105634334051,0.0105676410737,0.0103681048245,0.0105438919582,0.0105392784988,0.0106394395784,0.010442023957,0.0121027854162,0.0102942780059,0.0105125387183,0.0106771865372,0.0105553392779,0.0106890632829,0.0104110504342,0.0106597983192,0.0103859192589,0.0106835248333,0.010441230153,0.0106544599268,0.0103605174719,0.0107235192019,0.0127631379934,0.0105671148665,0.00998227870515,0.00990562774348,0.00997504259897,0.0113405288869,0.0105821372483,0.00993909399229,0.0104894151544,0.0104164648344,0.010046550741,0.010488139058,0.0123317484939,0.01027833154,0.0122146886712,0.0102847272445,0.0107121221424,0.0105800618209,0.0124849653291,0.0106877930344,0.00975210980363,0.0106028714768,0.0104556333156,0.0103280693403,0.010555217428,0.010546479007,0.0103500870003,0.0105118409917,0.010756676416,0.0103527546049,0.0104932588313,0.0105552903787,0.0107843175455,0.0105513405432,0.010534363238,0.0104051757541,0.0108427652908,0.0102992011662,0.0102607420711,0.0103572048254,0.0106929960984,0.0104728811771,0.0102008475302,0.0101586476057,0.0107638510766,0.010341407631,0.0103819534185,0.0106776875671,0.010109141918,0.0109197310208,0.0111719366537,0.0102386791147,0.0103383778244,0.0106354331252,0.0104506213037,0.0106225881791,0.0106805304194,0.0105871442197,0.0104886717073,0.0106561286035,0.0106966541817,0.010482341442,0.0104386145726,0.010524072449,0.0105889832514,0.0100612010769,0.0105679105271,0.0106337267556,0.0103427682942,0.010482371405,0.0104812768048,0.0105540427753,0.0105326670512,0.0104119944991,0.0104968887546,0.0106000180295,0.0103847916202,0.0106625262926,0.0104322779155,0.0106536375604,0.01044497673,0.0110863139093,0.0104827856633,0.0106537377067,0.0104844572201,0.0105846131926,0.0102513765916,0.0104360229361,0.0106668066431,0.0106662599567,0.0103706016138,0.010483471806,0.0103604901782,0.0105261507867,0.0104922550924,0.010293515267,0.0106955907242,0.0121120747249,0.0107338050864,0.00990238136536,0.0104414002977,0.0105858478239,0.01036782393,0.0102529016619,0.0106648348898,0.0101810414926,0.0111502829087,0.0100633742783,0.0105987636134,0.0104280217727,0.0106480946281,0.0104894007919,0.0106619817633,0.0103828923064,0.0106920999957,0.0105562383272,0.010539079371,0.0106802953612,0.00986251518471,0.0106528646955,0.0104567304616,0.0104606990552,0.0103891834661,0.0107519664916,0.0104078396337,0.00986313007667,0.0105740454622,0.0104551815171,0.0104360528675,0.0104387098591,0.0106226615774,0.010494138006,0.0108076122362,0.0106336326959,0.0104211189729,0.00985909965503,0.010608742492,0.0106911476268,0.0102849762304,0.0101055598053,0.0104590759743,0.0107180344541,0.0107546567125,0.0102068741647,0.010352527791,0.010380357519,0.0121054256764,0.00993308839347,0.0103922123126,0.0104305471478,0.0106130578993,0.0106605225408,0.0104977140962,0.0107859349291,0.0105438370297,0.0107235701783,0.010400334144,0.0103978062185,0.0106750453436,0.0105879257424,0.0106383827594,0.0104668613804,0.010560879488,0.0104734969267,0.0105373391541,0.0105359322904,0.0107159571864,0.0106498590824,0.0107171758118,0.0103637349556,0.0106405747756,0.0103920179636,0.0102317456005,0.0102782711837,0.0102306424099,0.0104925772525,0.0105689227756,0.010483926767,0.0103753463295,0.0104160478235,0.0106840200621,0.0105343735995,0.0104591402475,0.0104285832236,0.0106291700945,0.0104209598998,0.0104844134777,0.0106096329812,0.0106238544157,0.0104482784385,0.0106186984166,0.0105152819691,0.0104696085581,0.0102165891398,0.0102265650681,0.0107111687503,0.010087790885,0.010055950184,0.0121859908696,0.00998887234291,0.0105847225571,0.0101845511517,0.010583818276,0.0106811472654,0.0103639164126,0.0102744809835,0.0105115085688,0.0104117514942,0.0104666543103,0.0105074083194,0.0107661629497,0.0106374597796,0.0103557751506,0.010838539556,0.012386203499,0.0098519961931,0.0108972997812,0.0105958406578,0.0103903427101,0.0105241813142,0.0105873507891,0.0105367535199,0.0105189028298,0.0103146454346,0.0107712040742,0.0106564603437,0.0104933648065,0.0105700804161,0.0105763980587,0.0106563920065,0.0104275972131,0.0108160031241,0.0103439504127,0.0107154427803,0.0105565308685,0.010630198451,0.0103731885801,0.0102653399914,0.0105003705735,0.0101308778079,0.0124036941446,0.00975012651035,0.0103608210502,0.0107562363389,0.010693388333,0.0105854055275,0.0104969328756,0.0107924617008,0.010487660126,0.0106483649869,0.0103361250072,0.010654020103,0.0103599343624,0.0104883030676,0.0102665137571,0.0101475821916,0.010678471239,0.0102130844556,0.0107305485524,0.0104864495754,0.0105253482066,0.0107052948703,0.0103916704198,0.0106536732303,0.0104230936495,0.0105316072465,0.0109520995472,0.0102741930634,0.0105725723158,0.0106714585309,0.0104765120086,0.0105682311644,0.0104909062794,0.0106088252434,0.0105430159499,0.0104855544749,0.0106082866387,0.0105732104379,0.0104971696456,0.010592038429,0.0103026755736,0.0106745377365,0.0107150514854,0.0105873230032,0.010424777474,0.0106550744342,0.0105415407532,0.0105117230158,0.0103800199665,0.0107175429258,0.0103859532634,0.0105379458503,0.0105270426654,0.0105515263399,0.010500333118,0.0106005440768,0.0103660576237,0.00980802571455,0.0113620920927,0.0106245643842,0.0104334035436,0.010329598081,0.010567320789,0.0108346452189,0.0103148955751,0.0104855509645,0.0103785059337,0.0103266793725,0.0107380333985,0.010684391394,0.010266088025,0.0108093373051,0.0105311288536,0.0106304099111,0.0104187531695,0.0109838707036,0.0109664653421,0.0102840128021,0.0107281428861,0.0104718412632,0.0100015547297,0.0101973852734,0.0106324823304,0.01030295682,0.0108257076284,0.0105818305079,0.0102422798949,0.0100156633482,0.0108529049915,0.0099186811474,0.011947755285,0.0103423799619,0.00975086178623,0.0099553625274,0.0103414997157,0.0104056584558,0.00988426643209,0.010839983236,0.0100973017971,0.0102744712831,0.010380528511,0.0107154208747,0.0102591498878,0.0103985101817,0.0107294002277,0.0099949789525,0.0105330067426,0.00994189742896,0.0100584644443,0.0120889247999,0.0104425624018,0.0106102445785,0.0104527409861,0.010567725271,0.0102648031635,0.0106153154444,0.0104836659766,0.0107387854974,0.0112039326982,0.0105278821051,0.0109046730295,0.0101053723756,0.0104250883762,0.0107611682881,0.00979927174551,0.0121046056762,0.00996610307903,0.0105558808275,0.0106437361603,0.0104486129188,0.0106185638146,0.0106464961465,0.0104299736777,0.0103614234779,0.0105630325274,0.0105373487319,0.010462021972,0.010585351089,0.0100830704425,0.0107206252568,0.0101410580733,0.0103905773728,0.0103477030796,0.0100003550479,0.0106710619174,0.010209934545,0.0107084345097,0.010417606439,0.0105304329252,0.010597984253,0.0106693760868,0.0106513151948,0.0104279499837,0.0104168183051,0.0106705304217,0.0104190772693,0.0104394605767,0.0104158069902,0.0104235291419,0.0106498050882,0.0106774434822,0.0104113523676,0.0104423571729,0.0104250364797,0.0106812778436,0.00990627952874,0.0105831162717,0.00968756922421,0.0110253938862,0.0100831132524,0.0104241656085,0.00992374366765,0.0112444602851,0.010185106792,0.0107664366133,0.0103870273504,0.0107241463036,0.0103288277731,0.0103765355861,0.0105109537345,0.0106148181256,0.0104335257378,0.0101040845752,0.0102095681338,0.0105408871352,0.0103548399625,0.0103560881543,0.010713066672,0.0104478248407,0.0105571148082,0.0106499277693,0.0104206672871,0.0105716798689,0.0105113049448,0.0103487552328,0.0107325370461,0.0105038892254,0.0105008956066,0.0104700406048,0.0106607999616,0.0104229252321,0.010448662818,0.0104671488292,0.010609876302,0.0103277702961,0.0106426133629,0.0104132535637,0.0106454293402,0.0104252797638,0.0102252846112,0.0100452891149,0.0108877323263,0.0103731377656,0.0107702572502,0.0103078405523,0.0107796520019,0.0105602083783,0.0104185209142,0.00996993476542,0.0107684101339,0.0105925272523,0.0105947263945,0.0103901124666,0.0104010734091,0.0106506960627,0.0104072781116,0.0105040722234,0.0104496275521,0.0105978870986,0.0104523241581,0.0101359314312,0.010382134515,0.0100961237461,0.011028047347,0.0102111179231,0.0104575438696,0.00989210264954,0.0121884169609,0.0108409569567,0.01008101151,0.0108211418058,0.00983811937558,0.0105049661349,0.0105481527556,0.0103742814291,0.0103290190904,0.0101674150994,0.0107159643187,0.0102845002715,0.0108108566825,0.0101311660911,0.0104019052027,0.0105515664498,0.0103901433962,0.0106668470989,0.0104942342364,0.01055761588,0.0107187080098,0.0103056891575,0.0104837293244,0.0105448898193,0.0106462796862,0.0108835963517,0.010164877824,0.0106201050646,0.0105131476348,0.0102683583027,0.0104715701143,0.0103950700898,0.010653863042,0.0103334941363,0.0106147942465,0.010520770888,0.0105355621432,0.0105660663749,0.01047343553,0.0103521805225,0.0107227920353,0.010636203717,0.010368233075,0.0104736826421,0.00992933927643,0.00998247662066,0.0107164993204,0.0119350668724,0.0101341494313,0.0104565542623,0.010614703803,0.0104744019878,0.0106246008299,0.0104576065787,0.0104308294043,0.0103576571522,0.0107020135743,0.0103025696882,0.0107885969657,0.0105985589875,0.0108470249904,0.0107302905359,0.0102952921546,0.0108442348443,0.0104022214346,0.0105059743883,0.010636563418,0.0104085790967,0.0106989270208,0.0104625678035,0.0106490966891,0.0104779166956,0.00991922454857,0.00989127327126,0.0100250054226,0.0100130552231,0.00989710390356,0.00990478906271,0.0107212419638,0.0106103839471,0.0105881343174,0.0104649682234,0.0105864854082,0.0107089204725,0.010678033688,0.0106627881132,0.0103171822978,0.0104849625947,0.0103454061017,0.0106434175356,0.0104113424232,0.0106240277513,0.0104098906882,0.0104000787281,0.0106567581777,0.0105646859665,0.0105996419194,0.0104712134939,0.0104234096708,0.0106540559454,0.0103311974067,0.0105336043315,0.0105515972872,0.0105807496846,0.0102906901698,0.0107878749055,0.010549665599,0.0104908508138,0.0103972171676,0.0106226870701,0.0103898215253,0.0107092594412,0.0104848609032,0.0105882121625,0.010372898023,0.0101907743406,0.0107873833992,0.0103323753953,0.0107506867555,0.0102117466316,0.00993134469103,0.0118288474602,0.00986165372906,0.0106730003727,0.0102131130267,0.0106859171585,0.0107051384643,0.0104091973814,0.0103147466621,0.0105613589243,0.0105255823908,0.0105547492197,0.0105257302438,0.0104037127495,0.0106917279181,0.0103842368866,0.0103746935207,0.010429331209,0.0107146086584,0.0106862486665,0.01048207846,0.0107082975786,0.0103189214095,0.0104783336899,0.0105548042522,0.0105379364863,0.0106249722437,0.010567030543,0.0105008654284,0.0104665714022,0.0105303471105,0.0102075487131,0.010163179351,0.0109879481843,0.0104590777437,0.0108326882656,0.0099927628168,0.0107214930949,0.0103765908392,0.0107963313957,0.0103859529987,0.010366240048,0.0107280495156,0.0106081984128,0.010450240417,0.010909569138,0.0102338304927,0.0108521811689,0.0104374983246,0.0106522421913,0.0105748162297,0.0105196941536,0.0104787622014,0.0106587431614,0.0102418028122,0.0107417448794,0.0103183904545,0.0104754975504,0.0105203257397,0.0107758616695,0.0103545784599,0.011001551931,0.0106234067554,0.0101458865395,0.0104906426879,0.0100790112266,0.00997541396784,0.0105717740092,0.0101916180047,0.011061627555,0.0105204010191,0.0104525903401,0.0103533758628,0.0105850637925,0.0105143514807,0.0105239734866,0.0101339928103,0.0108239134994,0.0102471852393,0.0100071046877,0.0102607695621,0.0103784375921,0.0104146508648,0.0100757633503,0.0107903489411,0.0106532348968,0.0109935612077,0.009929218254,0.0101499644422,0.011066623662,0.0104166265839,0.0104385259966,0.0105178053048,0.0106262175263,0.010418927001,0.0107139964448,0.00994313880125,0.010643044951,0.0103043192597,0.00987408520473,0.0104152249917,0.0100420258721,0.0105428657625,0.0102908029576,0.0102372203453,0.0108287397893,0.00996229435401,0.0104084234093,0.0106466521393,0.0105716621788,0.0105585106762,0.0105449045226,0.0105052022736,0.0104402866901,0.0104496878729,0.01064033243,0.0105394867609,0.0105932841602,0.0104665679119,0.0105396801485,0.0105133278243,0.0106843527232,0.0105324531161,0.010345531399,0.010784182541,0.0103275819064,0.0106541212123,0.0107289797921,0.0103822794666,0.0103091198923,0.0107208323627,0.0106693211005,0.0103638255549,0.0103821838656,0.0107363554369,0.0104175182518,0.010689078604,0.0104047653132,0.0103560552053,0.0106975838551,0.010417187991,0.0103549818581,0.0107821722519,0.010543153753,0.0105397163908,0.00997695301573,0.0102562423637,0.0104647284513,0.0101783600522,0.01046999537,0.0106095318701,0.0106519469543,0.0104418344933,0.0104278686863,0.0106428618114,0.0106381646342,0.0104215073793,0.0107055385399,0.0105036468338,0.0103657071103,0.0106985755366,0.0104601719759,0.0103178922649,0.0107378698701,0.0105759791294,0.0105915666036,0.0104664040791,0.0105960907137,0.0100688387961,0.0101420689437,0.00995721916397,0.0126502040572,0.00986303026052,0.0103900083935,0.0101631909766,0.0107870962719,0.0107220117559,0.010362194416,0.0103656409005,0.0107171695681,0.0107942424079,0.0109114381706,0.0103217584572,0.0106394632545,0.0108275571859,0.0102993603503,0.0101031258984,0.01029644255,0.010194137422,0.0110847895735,0.0106056448133,0.0104447371265,0.0103108042388,0.0106835698109,0.0105484902106,0.0106420309846,0.0105739698147,0.0104450314481,0.0105665651767,0.0105267569441,0.0108582233585,0.0100157484839,0.0103287825216,0.0103580097878,0.0107676594033,0.0102350183492,0.0104200613453,0.0107943005828,0.01061001635,0.0104432231174,0.0102620458187,0.01074074281,0.0103557392302,0.0102267023788,0.0109437258763,0.00998568820048,0.0105123278531,0.0104989176643,0.010492289828,0.0106016397796,0.0107174018357,0.0104430735479,0.0105689421919,0.0105378739185,0.0104425018359,0.0103828649677,0.0104119645856,0.0106714432754,0.0104412019371,0.0106699189407,0.0104147937356,0.010437277164,0.0105880700834,0.0105217491498,0.0110562257497,0.0105563399039,0.0106241491893,0.0104876163215,0.0103810054003,0.0106984524735,0.0104021187195,0.0105692420448,0.0105019155161,0.0105418172561,0.010699466536,0.0100423266327,0.0103761360446,0.00985667890422,0.00999199598108,0.0105316282455,0.0105739456971,0.0105929522968,0.010510312118,0.0105861478761,0.0105515537672,0.0105475195892,0.0105296505571,0.0103952432968,0.0106852822386,0.0105460073128,0.0112915970621,0.0103139288665,0.010706741432,0.0103127903612,0.010715614148,0.0103470180004,0.0103877115346,0.0107079185885,0.0104348854557,0.0105735676763,0.0103480768755,0.0106606847913,0.0104756773659,0.0105942651619,0.0102539697446,0.0103924589355,0.0104840815671,0.0106563792819,0.00977996205358,0.010856977039,0.0104821689963,0.0110848608707,0.00990288979203,0.0111748762927,0.0100734300484,0.0103199973845,0.010661143181,0.0107719809971,0.0100106249864,0.0103783372224,0.0103948463042,0.0104641648236,0.0105968260736,0.0105622355168,0.0104597360585,0.0106738594487,0.0103125707824,0.0102550813313,0.0107548419136,0.0106234847672,0.0104440004807,0.010507127359,0.0105965477158,0.00986195320277,0.0105202956167,0.0105257387212,0.0116282204944,0.0103918430359,0.0123132813577,0.00989134829845,0.0104834586074,0.0107037756302,0.010118839989,0.0101238686413,0.0113031732712,0.0101294334197,0.0104912948816,0.0103622547448,0.0105276236603,0.0105470527167,0.0106348190959,0.0104596236973,0.0108818476776,0.0112111607113,0.0106873069131,0.0102588250845,0.010880432215,0.010089655822,0.0103914698952,0.0105813561989,0.0104050871835,0.0106776944698,0.0105560616852,0.0105139988658,0.0106706404518,0.0103578271632,0.0107213616644,0.0102640044167,0.0104930892839,0.0106622240966,0.010543224538,0.0102885480743,0.0105893256349,0.0104159556197,0.010828008363,0.0101687861367,0.0101203134104,0.0110675828415,0.00979829244324,0.0105211376691,0.0103741103697,0.0106648211362,0.0107029997572,0.0102861554528,0.0102883788416,0.0105147138665,0.012528715277,0.0100680295644,0.00999416623141,0.0105380554314,0.0107178789057,0.0105820525677,0.0104117149345,0.0105268474132,0.00979281685892,0.00976628410851,0.0111711657112,0.0100905885315,0.0126440839844,0.00992647126683,0.0103867776955,0.0103927894556,0.0106740220206,0.0105234269965,0.0104405808871,0.0105989080067,0.0105772285568,0.0103555507923,0.0105403800597,0.0100160601848,0.0103243282654,0.010780805974,0.0105939044194,0.0105804121349,0.0104924452737,0.0104147704275,0.0103981276532,0.0106935697245,0.0103982082259,0.0103188948232,0.0107848548425,0.0104042969134,0.0107585688659,0.0097388702479,0.0102478837781,0.0109027155225,0.010592031862,0.0104394689929,0.0106714874087,0.0104119132158,0.0103089989265,0.0108178938453,0.0102122142193,0.0107553221452,0.00982443275802,0.0107491341003,0.0099184707404,0.0122984453185,0.01023304204,0.0104431923249,0.0104513020286,0.0105386075706,0.0103492179854,0.0104942258253,0.0106246589642,0.010502577223,0.0105799625312,0.0105293990786,0.0105452535444,0.0101612150281,0.0100121148462,0.00995625993227,0.00999261975487,0.0101974763915,0.0105943331114,0.0105317119961,0.0105416015927,0.0104297027931,0.0106445010797,0.0104011583358\n    \n};\n\nconst double PHI1196[NST1196] = {\n    0.89204871472,1.38192306396,1.43247529268,1.563492401,2.04782291969,2.11776648671,0.974820136238,0.11237124964,0.594177476608,1.57996329799,1.51287251665,1.5511566219,1.64242740237,1.6838822821,1.03995222965,1.06883830204,1.0150605779,0.917494924864,1.53665481134,1.45631225868,1.40843810868,1.69195777423,1.75632561941,1.84633899223,1.71013936613,1.75436946427,1.40312941772,1.52133864084,1.49633753737,2.58608525153,2.19093535966,2.11009697546,1.51449958315,1.58259945233,1.55706536467,1.54972430316,1.59920511182,1.43420716675,1.45573928719,2.19263778905,2.02288916332,2.28477872295,2.31485071029,2.10730032615,2.06458140413,2.05694659781,0.540777248952,0.395860650232,0.35652532197,0.542307300806,0.489546480311,0.263490709557,0.0868930388114,1.04279021549,1.03484051476,1.19162803453,1.12447480754,0.633720937392,0.993513837379,1.42042598046,1.37734734045,1.50906952939,0.592217724484,0.673201576525,0.200650753299,0.355718391441,0.595905410314,0.699755132124,0.680563424336,0.352406410772,0.43587001194,0.507073887284,0.512868538784,0.579134332851,0.645562590126,0.887628794774,1.15344182237,1.20910486381,1.23914164317,1.18073677998,1.52572605414,1.61106577806,0.963759245213,0.883069485222,0.885485547635,0.975929214429,1.01188902334,0.807846810069,1.21848867618,1.69245499491,2.17436126369,2.26270480631,1.40415205463,1.66724246513,1.76147558171,1.64570303922,1.938225318,2.1124418095,1.11101624361,1.28998564185,0.773877664562,0.866812267859,1.04934945181,1.5592055687,1.60893353073,1.51730436252,1.49306702366,0.884973892934,0.9598404179,1.25339447678,1.18230540374,1.10731287323,1.36800470489,1.34504687792,1.26084759076,2.08622655342,2.09417976866,2.16911728481,2.24254943236,1.68121674452,1.72395554503,1.66389792108,1.60934845508,1.70856304365,1.09297892406,1.02023721103,0.761323993397,0.934722869605,1.06638180427,1.43864065652,1.48061396901,1.56857733447,1.61829876246,1.64973473857,1.6753898016,2.13665146834,2.15992210291,2.04457209766,1.97748908179,1.67378211701,1.76462736001,2.44717382201,2.51455224555,2.43260520584,1.6081702623,1.66973722465,2.0283429899,2.4358836418,2.46670641707,2.49585156565,2.49083054414,2.48712428316,2.5644905568,2.67032939838,2.01401243071,2.09547174696,1.78552719999,2.24206706645,2.19799511947,2.09870750313,2.05883638348,1.79677614079,1.70577742594,1.93271521212,1.97630775546,1.79679820961,1.84253644572,2.0665166787,1.97778962069,2.54602717603,2.71146335437,2.37808895553,2.46393122928,2.46757617013,1.36323141801,1.2149220683,1.1578724704,1.1421393798,1.21597354957,1.29067133839,1.40815141394,1.2547804841,1.31404841068,1.42424104926,1.46023482313,0.835197194441,1.06229121565,2.12784791017,1.96416775901,2.03304225296,2.00873469366,1.94494886325,1.78998025511,1.93864528826,1.86099561563,1.72631000152,1.883095955,1.79687853265,1.87413393279,2.45653623651,2.53556278983,2.39158086185,2.54489284891,1.84058699837,1.92964754647,1.91656692139,1.96971384678,1.98219259629,1.89837903402,2.23037505052,1.73259347432,1.66128235046,1.82807671911,1.81999435436,1.59663572546,1.96212972986,1.88738051379,2.42541312433,2.47075212048,2.33155690471,2.56035927542,2.73385260429,2.59578221385,2.67647537185,2.09703771108,0.249956549095,0.214827905962,0.175075855753,1.4477848821,1.27432919826,1.38026451587,1.2928172236,0.822854629633,0.692269170501,0.630136399685,0.671322954013,0.761205230474,0.673674921794,0.813767295566,0.930915283343,0.843274404681,0.781676443179,0.938272040478,0.903446108156,0.959449690049,1.55671734159,1.6486498321,1.69796456362,1.38231027304,1.42875623897,0.880604726642,0.957254671585,0.557584742789,0.644614814505,0.724581142179,0.597958802559,0.557980541483,0.268722817454,0.20869168586,0.267519255626,0.117946602011,0.126539355684,0.673316284544,0.638835025821,0.496873956203,0.428577790202,0.751250756113,0.885231030111,0.798462741492,0.60474374995,0.698395282586,0.852754080554,0.732093030538,0.7597033576,0.90732500122,0.872723248164,0.790706327708,1.02044791036,0.979790543245,1.53491583221,1.51431773757,1.11744088783,1.02993499905,0.992098078563,1.04409626521,1.29340863226,1.32190834385,1.4953921721,1.43477510397,1.3494291285,1.32338768871,1.38497506707,1.47143266088,1.27719186574,1.19898993958,1.35484427664,1.42922783307,1.1394015466,1.06056364221,0.984061276686,1.11468686664,1.1966864626,1.28674462017,1.27863958019,0.711346025799,0.657019834884,0.442690362698,0.50352439,0.924691039799,1.01068554094,1.06648502855,1.03469683327,0.974078167652,1.12013229613,1.13872756366,0.761271390998,0.844660471687,0.881180908338,1.21564633627,1.07811028753,1.1318819642,1.06107921364,0.85681949994,0.816661749378,0.766785974662,0.745482331175,0.923149859583,0.903894919019,1.18252388879,1.23004599007,1.16319665326,2.32619404325,2.41431995924,2.47537039255,2.43997765926,2.14719958571,1.46060890024,1.59377448018,1.15587950568,1.32272096359,1.77900759137,1.86503113927,1.72159712018,1.6324379202,0.669145439666,0.738555807914,0.505331451332,0.333316009045,0.972361056873,1.01957891068,1.1173426943,1.15695560092,1.28934560364,1.24506676858,1.13457557984,1.2695442139,1.18729917647,1.30055399046,1.25083472482,1.16721799455,0.885368166335,0.826467429616,0.887867229451,0.956516483853,0.934477974059,0.851850126548,0.649955375744,0.727530664327,0.602687775293,0.79275768763,0.778972035157,0.593424470509,1.2101245725,1.2946202027,1.55402702295,0.97166692134,0.995730253159,0.967586385167,0.7883979582,0.963077642522,0.816186207593,0.903069228976,0.925616919037,0.748597310017,0.795361697734,0.881272830465,1.56869615255,1.48189646979,1.61767517209,1.57339767441,1.70503461782,1.6120332218,1.20234377225,1.15474703021,1.42712639729,1.33559141972,1.29321328007,1.34222895902,1.43373409268,1.47646139973,1.34729045907,1.29887415303,1.88942600199,1.98050664678,2.1634689412,2.11901313674,1.94590449983,2.01621286404,1.54183547585,1.61963264891,1.6490450864,1.60551315779,1.5863709595,1.7976747544,1.92254824023,1.89161974106,1.73605311347,3.01611720696,2.62946119549,2.67129439219,2.28036177276,2.36165784957,2.3603150918,2.20854831186,2.20566591496,2.27596550415,1.84106444876,1.75123118683,1.71436367425,1.68749505634,1.90486686058,1.99567439903,2.02664782739,2.05828378811,1.92289637152,1.86969937547,1.83218077175,1.80646908911,1.74188584518,1.84188205675,1.7508937541,1.70120825708,1.89063240514,1.98386690156,1.84717945696,1.89321594664,2.02876948068,1.9812972381,1.80584287014,2.15846445644,2.14985595232,2.24016695443,3.07874177329,3.11313549532,3.03772855838,2.40215087031,2.41578777582,2.34227985054,2.26174699793,2.24809625847,2.31530361665,2.57787826971,2.62115547497,3.03039474433,2.87555241579,2.95616756681,2.17026051477,2.15174601332,2.25793906977,2.32461548315,2.07256929204,2.00977596638,1.83440711473,1.79041275856,2.10848703058,2.06466296103,2.19764274703,2.11019925653,2.10091484103,2.04952305699,1.96482787782,2.06263637034,1.97241545218,1.92562027278,2.38286627145,2.33976470663,2.24425600661,2.20040540122,2.18623822536,2.21421410738,2.3264941249,2.24031603095,2.35807859579,2.29963125226,3.00046853907,1.88432749861,1.9076181686,1.98327628723,1.61303575882,1.59263958759,1.79887971392,2.46756920143,2.54432390483,2.50087019367,2.59211212475,2.62861762395,2.24118940349,2.32606230579,2.19680056269,2.24227548909,2.33483283004,2.37447279764,0.870349525787,0.617580934523,0.645986980342,0.685812392913,0.768133984764,0.736746870545,0.790927302519,0.750071671381,0.753568208636,0.829326151494,0.905304573129,1.58179480618,1.5073192397,1.81476670307,1.82455171919,1.74761923898,1.73533239576,1.66263590728,1.66522188947,2.61783613471,2.69034272611,1.8301878764,1.76877227763,1.68887317044,1.79409975543,1.38481539081,1.29483742451,1.38978207565,1.52239432446,1.43157140691,2.13571674531,1.79838114066,1.72194732281,1.63422054781,1.62160564904,2.13641375138,2.16018294836,2.12988220175,1.4277076233,1.34181532732,1.48240831034,1.49754005332,1.40090852118,1.33007927268,1.33988690004,1.70227453015,1.65375313178,1.51355249637,1.37971377543,1.42302905675,1.42638748031,1.51741167358,1.56146687474,0.344463875379,0.478377622973,0.39261988279,0.317760748589,0.246813147153,0.233297761597,0.0488739132157,0.0744171337336,0.161514740028,1.04317003702,0.965301554003,0.916163705412,0.895302658864,0.80745409276,0.726595912167,0.74644051422,0.813576526708,0.661083396286,0.646852306948,0.276036330071,0.216686384239,0.270304787652,0.353073387988,0.429600201849,0.513991843465,0.527394924858,0.509032329993,0.419279662766,0.549235047238,0.46140996081,0.456956676699,0.407050498194,1.29210787254,1.24624134771,1.24833935004,1.15908950108,1.15560676723,1.1119861065,1.28385975913,1.36588514984,1.43188141972,1.26637171988,1.12985102202,1.18355342576,1.26761965374,1.30200655285,1.16573526496,1.25358703787,1.42138418427,1.39806393077,1.45052015558,1.46193423152,1.38695873528,1.30196923166,1.36840546387,1.29474828728,1.54662131301,1.71079738935,1.70288196401,1.62061151488,1.58856028774,0.990810721036,0.930368084891,0.917896283873,1.11438876586,1.03938070364,1.03512350289,1.12212707977,1.19780006982,1.19437098891,1.44102337802,1.53547786952,1.52427980761,1.36906315868,0.628390077306,0.726590319822,0.654094308245,0.594089600917,0.745919305132,0.784828594761,0.915912866873,0.936064081018,1.07222295832,0.993052124835,1.0938108622,1.03525161819,2.34894266918,2.29547087811,2.2052055693,1.61007135983,1.71317346955,1.7009837719,1.54260712598,1.56667016087,1.64629027618,0.574529578614,0.494701346118,0.513918988039,0.582872797,0.423973099358,0.41263648092,0.354785105183,0.442850600746,0.361496723962,0.30522843272,0.450057070942,0.484628092527,0.911560905679,0.996470280579,1.03206063345,0.980041111855,0.738409929059,0.83399132313,0.878420090898,0.77041165448,0.901068356673,0.85883197221,0.92132677487,0.808498639473,0.731235579825,0.817125010788,0.74835276222,0.653472234949,0.662434653613,0.618170986031,0.569980745861,0.738632834092,0.708311053549,0.625279158666,0.702348993271,1.10053830184,1.18343615113,1.24468865305,1.07804781807,1.32911229535,1.39537424401,1.43697233939,1.35253240169,1.50279472542,1.48445315866,1.3751791712,1.5713002032,1.48210064324,1.59692639876,1.53195537398,1.41796603843,1.44232465014,1.30458349922,1.32886262045,1.19720107942,1.11478132828,1.0499737801,1.21813266185,0.799609330514,0.797700256312,0.886855831833,1.30696213644,1.2715155414,1.34201598555,1.02273413172,1.06452594044,1.15800927157,1.20575280451,1.02626674928,1.06622609917,1.15262291424,1.28686599317,1.19719035854,2.38404399913,2.25994012297,2.33988489824,2.40862134734,2.43935841065,2.39231060239,2.2552564486,2.30103312827,2.30149949488,2.39635296939,2.44193565782,1.88484820077,1.85803133149,1.76567913681,1.81411343695,1.71946537276,1.69765430234,2.72089018911,2.74498585163,2.76930539001,2.85418581697,2.56683820907,2.43051441495,2.51382625169,2.58650379189,2.47709866783,2.41267139522,1.87924694517,1.75822968476,1.78603297402,1.93875277649,1.90146462308,1.81447538295,2.06201365417,1.98293422895,1.95494212591,2.01733678807,2.12358255965,2.10679685813,2.16526868114,2.11912730746,2.25501209185,2.16244637625,2.25362355259,2.30049899843,1.99888149243,2.06766367971,2.05296998317,1.9699658445,2.11790296049,2.22166524139,2.20434054849,2.98702816393,2.96885114126,2.82198687545,2.87574821117,2.55347498978,2.6145619979,2.70292408037,2.56951371009,2.75870974712,2.84887159387,2.74044428553,2.71137910896,1.91450906722,1.77493613605,1.8267871278,1.9367737758,1.86765109395,1.79385834132,1.97590182209,1.92922967813,1.83873551757,1.93233743275,1.79456383636,1.84180106115,2.33531292551,2.38425361481,2.51897367049,2.47798746471,2.52264887695,2.6736790032,2.81906749011,2.60209577155,2.6929729292,2.72801885615,2.66315029404,2.05503053398,2.13283532153,2.20162153237,2.1766672346,2.04546292965,2.08434067795,2.03125489833,1.9364125602,1.95263713519,1.89632793929,1.65162675328,1.61767652389,1.79046514792,1.74488254721,1.7579693506,1.67852751444,0.852403661425,0.836386937031,1.00452775469,0.939881462078,0.989265172519,0.908856221057,2.61661420818,2.57406916371,2.76288823326,2.90765989379,2.83792273869,1.43742097139,1.40373797419,1.54159737571,1.46071419085,2.28148854247,2.29027347652,2.20642360589,2.15953138012,2.19167349402,2.24957939751,1.75348482602,1.78447760073,1.68167931162,1.69552765892,1.94670832972,2.01184810913,1.85814551036,1.84083891243,1.74547598699,1.6625128973,1.74354316312,1.66373701036,2.06775097858,1.90234924455,1.97378780683,2.05168562972,1.4549578649,1.36763591796,1.25251832195,1.2281564831,1.14268500633,1.07953861275,1.56496316559,1.56726597968,1.52017754283,1.65733823092,1.70484488681,1.65935217793,0.481177577023,0.437538675143,0.498556615656,0.574521385707,0.589357910828,0.623177316558,0.483268982946,0.397772468152,0.510733701113,0.334836211109,0.466311925604,0.37847600763,0.136315609087,0.314987932992,0.199165352976,0.28244542239,0.205007640035,0.295743595317,0.353739563851,0.345800418932,0.278334801859,0.194034891438,0.524428089674,0.548685724074,0.436575392136,0.366552236661,0.632442942404,0.67463431471,0.637817636458,0.561798009506,1.41428705633,1.33091013403,1.48034404489,1.31380106713,1.46486635247,1.38177913006,1.4928938829,1.48288664257,1.64705827677,1.57517631247,1.63835129276,1.55652085997,1.35575123883,1.2760024563,1.27390690537,1.58554996982,1.58623020495,1.50915549082,1.43195755191,1.01069201487,1.00321368567,1.13402582982,1.06030054332,1.17940542608,1.32388176996,1.26742105106,1.337250015,2.03440079008,2.07412404147,2.07810132382,2.16745413556,0.688788622113,0.616750858778,0.554453585133,0.594431203231,0.887948276902,0.833231672059,0.855056927427,0.739859112183,0.767160081381,0.706345685437,1.22313424993,1.13960814127,1.28754082312,1.26544640784,1.03160519258,1.15542157867,1.11700283987,1.17864223684,1.00746667437,1.06987911937,0.929370567717,0.889372588486,1.02187708529,1.06674027443,1.02251505675,0.937702638112,1.63318118645,1.72577151639,1.56191961795,1.46945493107,1.21779028585,1.1647597333,1.07563989357,1.04725572117,1.11267567391,1.19379692157,1.08013133917,1.19287459676,1.04781827549,1.10555084573,1.34382908045,1.3102206805,1.22299098511,1.16775740778,1.19820797879,1.29308761725,1.15647547472,1.20444489243,1.32926442659,1.42008905724,1.46216519449,1.41820430448,1.33194774218,1.28485522678,1.87226953096,2.25313691896,2.29338038829,2.2376151546,2.02310917894,1.97383068173,2.11509589507,2.15895211449,1.9995501756,2.08966304703,2.55812548301,2.52997490686,2.43872769345,2.53098204325,2.43848584419,2.39244058502,1.92036932435,1.76141364022,1.82610813587,1.90611923142,1.77363912125,1.85246647452,2.11313130775,2.14973382555,2.09269596094,2.02267758261,2.53738719232,2.51608543341,2.38704101047,2.42260591532,2.35919514064,2.26905434804,2.29439710442,2.23944002503,2.89213706194,2.98220343066,2.83047013236,2.82721643232,2.98173567993,2.89678630467,2.46916046477,2.34886882508,2.32235683062,2.37668578425,2.63413878432,2.60976717005,2.71731634624,2.69159534446,2.76524457209,2.66844553857,2.85193089978,1.57306643574,1.52569556445,1.70491266996,1.66098596063,1.66016615325,1.56907848284,2.59504334431,2.58415283182,2.49853468768,2.43165277246,2.42394572946,2.33793834995,2.28531161532,2.31353260835,2.20988527754,2.28680088148,2.20083898924,2.26976502207,1.8991595289,2.00596138152,2.03649844128,1.9814959975,1.91862453499,1.83856292311,1.83514971613,0.402338096019,0.359510588113,0.420879546073,0.494668006717,0.544875561485,0.512287792647,1.50968298223,1.35315783074,1.5078846094,1.43136756831,1.35443458636,1.43446570202,1.07950708401,1.16267020192,1.14399752561,1.07180108752,1.22670552957,1.23768577565,2.21435519354,2.30509218795,2.19894799904,2.16440304057,2.28903386499,2.34572415588,1.46671450717,1.4310496941,1.48491696075,1.57136522566,1.60450437495,1.55390918406,1.74835663423,1.84051595536,1.69773457368,1.74043919689,1.88255798706,1.83283247499,2.01501950927,1.96296329776,1.99923671955,2.10794665576,2.0900518088,2.146799689,2.18961511276,2.11076765641,2.18890713575,2.10518851142,1.89548878958,1.96668720846,2.03541209876,2.0306208397,2.64157841273,2.8012839054,2.64821809693,2.7214945068,2.71228363424,2.53406148836,2.58087120223,2.66562470963,2.58061072898,2.53287173382,2.00590101983,1.86396473212,1.94797361411,1.9722026882,1.83179720205,1.88233265695,2.37908769895,2.31587903091,2.30606924266,2.46272031019,2.39541605778,2.46942360386,2.78687774042,2.93563958267,2.86796554832,2.40285218851,2.57656186624,2.54919623992,2.46157427256,1.55186695963,1.60279880012,1.595286733,1.68839450496,1.87658811113,1.76552806291,1.79186703368,1.83465752989,1.92725807668,1.94295029154,2.78898316324,2.70360279015,2.66941521255,2.71059576702,2.93415288041,2.96285200134,2.84492318716,2.79742646367,2.87224958647,2.80133031856,2.7559651972,2.64886084663,2.61543676357,2.66312451643,2.35412010975,2.42376927294,2.50902976804,2.51733312456,2.43996290588,2.36224798418,1.72914729743,1.83258664685,1.82034617796,1.74806397498,1.68684409052,2.79984148512,2.91611774047,2.89338203961,2.83427625944,2.75290813602,2.73788079242\n    \n};\nconst double THETA1196[NST1196] = {\n    4.55339487549,3.19224121155,3.27183836,1.60011099376,1.51924025983,1.45735399508,4.60009281588,0.405614948206,3.25757204547,1.69018556775,3.25787539977,3.17091967417,3.165263682,3.23862627781,3.53275217518,3.43101049725,3.35231706336,3.37371128743,4.63797394167,4.08210908963,4.25805597365,-0.0718549388784,-0.453543726579,-0.604692087838,-0.535929651574,-0.609028891735,-1.51956397556,4.14799155934,4.22904978765,3.89172088547,2.14163837345,2.53245735229,0.122058796919,0.0636064470473,-0.168137667491,-0.0225591135476,-0.0881865152205,0.0746590734801,-0.0171408661776,0.689446911535,0.597727733145,0.706104825405,0.588620892331,2.34820511109,2.44665547832,1.62058317587,-1.12337804319,-1.29387134132,-1.50136435152,-1.42055808464,-1.28558901368,-1.554865998,4.3418666184,4.53535742501,4.42647764147,4.51642138004,4.57706303221,-1.3860884088,4.70913135515,3.10322498941,3.02601539849,3.09595252584,0.211022922437,0.274888918699,0.541869166008,-1.07120719892,0.0520425712868,-0.146091914489,-0.00950132691857,3.31041208223,3.20819552221,3.31677502791,3.49852952127,2.4269913486,2.33976998361,2.60806227276,2.17626150979,2.09931828571,1.93000972499,2.00413896028,0.206734017475,0.240177774208,1.25876625081,1.21181985635,1.73052562742,1.36522914728,1.57105226309,1.27922583873,1.83337310154,3.08835120707,3.92827509137,3.90834076336,3.36476632002,3.63225738089,3.60624572925,3.71327860496,3.52634825463,3.57856926831,2.87714688084,2.8683104568,3.28035178377,3.29190101466,3.25344737586,-1.56145525308,-1.39734794622,-1.41514491317,-1.49820143503,4.43529193677,4.37444307841,4.35930495409,4.41492992202,4.36754963453,4.11392056487,4.20927475187,4.2583419147,0.423701853913,0.532286488262,0.575609044788,0.514791411316,0.179273034616,0.0173720513806,0.0870318770493,-0.236598657676,-0.380801624756,-0.0954236019942,-0.629818778838,-0.402005733115,-0.434001053979,-0.721213435711,-0.400017216993,-0.321724693786,-0.317971150752,-0.391069169651,-1.54435345209,-1.4630283398,4.58148979582,4.68018592101,4.56365101481,4.6358616264,4.26889990277,4.29768891516,3.19610433724,3.78805389055,3.83883385036,4.12109852232,4.1752728017,-0.601839350195,4.4455247135,4.5798446059,-1.40898354987,-1.25857217069,4.33049728781,4.0527782603,3.82742802615,3.46370676762,3.48869502003,3.09902405512,2.22074314747,2.32827909769,2.17037950502,2.26965596639,2.6321760699,2.47795661611,2.54851045319,2.4595337802,2.47382232234,2.55251589362,2.62804879141,2.6311284107,2.72771890458,2.91070288396,2.71847551512,2.52350839463,2.67302416959,0.116248568263,0.213447838694,-0.0341820605302,0.0624054678524,0.116192536496,0.0659807404649,-0.0766208311784,-0.0815866958051,-0.035887330266,-0.234465842832,-0.162118725711,0.388653533305,0.10705936221,0.74573625516,0.755793051075,0.697267362758,0.389696275584,0.557980759221,0.486991596367,0.45794092789,0.424631140743,0.639212070202,0.718367079464,0.582607003499,0.619870899114,0.64638725567,0.600806216065,0.545992059963,0.432063293317,2.3909830928,2.38083179828,2.21463955492,2.28926470334,1.67072395932,1.62803886459,2.03102604879,1.65213890263,1.71426415122,1.76743488513,1.68214637394,1.28245322521,1.48222443419,1.5399898949,0.784336620043,0.899072518249,0.798288842065,0.893630766961,0.861325033557,0.729965149156,0.690094149978,1.35307251309,4.39093683261,3.7220499233,4.12283488624,4.61549315856,4.55525415511,4.67757048027,4.64915642166,4.6240509266,-1.49052159939,-1.12172224623,-1.24852117633,-1.23487234941,-0.976821880205,-1.33505196523,-1.50168387441,-1.53873270857,-1.45436500152,-1.20353984538,-1.31120635698,-1.39261885801,3.01530077501,3.01331735245,2.93744987505,2.70666883993,2.62919468757,1.08977410253,1.03690617763,1.24151872732,1.30327596884,1.22520539094,0.779177766873,1.08525477373,0.275126155801,-1.23872740875,-0.971974958642,-1.12572573128,-0.353580347236,-0.387686420766,-0.250731977602,2.34759353154,2.4688949213,-0.988274309745,-1.1120670958,-1.11968270333,2.58840398024,2.60772238146,2.50549195094,2.38426850934,2.49683219479,2.41061232414,2.29470323656,2.27781657682,2.70451849467,2.61253308017,1.84537048477,1.75475645115,2.52961723646,2.52293040011,2.42296291342,2.33575138614,2.11647446226,2.20958397354,1.99335787694,2.05875314181,2.04203096136,1.95049035996,1.88225933997,1.9081358619,0.768514634601,0.814639199353,0.807641873223,0.75309883388,0.262423900787,0.210607839892,0.263490006229,0.94953809133,0.905336302201,1.32398080849,1.23013772101,1.70842673832,1.4415342876,2.02946664026,2.15510497239,2.20592918545,2.234151816,2.15533110184,1.67463934251,1.74817168112,1.7054101741,1.80673275568,1.84194886985,1.84121336326,2.09138499163,1.38132678423,1.50427664945,1.34235743856,1.40267479493,1.62756066119,1.39938192153,1.60604049545,1.48080759251,1.54169878186,1.43676411305,1.6371640293,1.47705763662,1.53908629073,3.99711546361,3.97205712196,4.07814225458,4.2065359598,4.02606455671,3.44095182214,3.5742958351,3.41347138197,3.38007658552,3.52391596838,3.48294073664,4.00233660757,4.02739803851,4.6531747385,4.57129246691,4.15173908635,4.27244888059,2.79601774829,2.8836884997,3.05432155156,2.96051332028,3.0337198738,2.95376359282,3.23530767274,3.30071368823,3.31650385139,3.20565413136,3.12449408169,3.13815726782,3.89175625985,3.80720059342,3.47580807379,3.55488245963,3.66495697499,3.69047680797,3.38690419109,3.38863566355,3.52930047564,3.50305623185,3.60868467133,4.11132512623,3.49082155452,3.47337820004,3.73862040687,3.85619827655,3.74603350533,4.25953333893,-0.181456082459,-0.241798977446,-0.301547904025,-0.325659754203,-0.628502479267,-0.656274805631,-0.520223299628,-0.527833824585,-0.625338852408,-0.4778026962,-0.54468529723,-0.472585455201,-0.689468343559,-0.697058971836,-0.6403642782,-0.723328418596,-0.712269555069,-0.718246571322,-0.641023675776,-0.559614015789,-0.556612761161,-0.632096974594,-0.401852544195,-0.482630793142,-0.685685958006,-0.687426232164,-0.698938636847,-0.603681919909,4.30404437639,4.47537282028,4.31004822794,4.32994788622,4.42947072973,4.57676417831,4.49265792352,4.37567823846,4.46598505882,4.38431073157,4.44640214121,2.77373911184,3.26837721575,3.63239997053,3.79395427478,3.75187026931,3.6205015741,3.72064201685,3.61064407177,3.55548938598,3.901597166,3.91909039354,3.77245255398,3.85543461747,3.97189486974,3.95692356143,4.12774080033,4.03551042883,3.62392542782,3.81275624692,3.66198071721,3.7475424704,-0.141044428309,-0.284678589213,-0.296967161279,-0.22594215535,-0.359590347648,-0.518160017872,-0.444654665517,-0.522757132383,-0.429248805021,-0.348830514493,0.0393556718304,0.355395838167,0.249180438274,0.402282631763,2.11809333384,-0.977446140902,-0.146977214071,4.67429772925,-1.47533425594,-1.40198940542,-1.45540747509,-1.57005706089,4.63531496703,4.34015941657,4.18942659894,4.53352797225,4.71083841853,-1.38824964482,3.40605764684,3.30228492939,3.43899978117,3.36772609253,3.27505680576,3.35377594375,3.03178592185,2.94505070552,2.90303785289,2.80907086073,2.72181853112,2.71936989048,3.08597702801,3.16730883637,3.14430685429,2.98636641317,2.97308835641,3.04937620137,2.93662010916,2.82284470318,2.8208910623,2.91263867925,3.10696523671,3.21608252897,3.03849154762,3.01748851342,3.15999641091,3.24725461509,1.54555350861,1.90793447561,1.81270106556,1.77094928104,2.10212260491,2.02184004646,2.06552504108,2.95343480646,2.85966957605,3.09171969154,3.11059171089,2.95338507085,2.6193547017,2.6150614172,2.52136142021,2.41577849078,2.4017567424,2.50384043993,0.638945613451,0.516066019083,0.650783502162,0.416087056434,0.464228948632,0.694653240194,0.608649417672,0.201160245053,0.071272021383,0.264415656211,0.20672501552,0.740567341358,0.791630914974,0.775545640852,0.872815819108,0.919112697674,0.733945732234,0.783702824113,0.872061233934,0.353165653088,0.474128588969,2.2278663907,2.14459851508,2.15580725522,2.31291994114,2.54846234993,2.54283744699,2.38901805024,2.47817235704,2.47267547595,1.67185922358,1.51026848737,1.56778864413,1.54019279994,1.45536546807,0.841533393174,1.2814045208,1.18257857808,4.53028415003,4.49893824775,4.38564018799,4.46899227701,4.35240941464,4.40388353434,-1.45419783682,2.78536854925,2.8613673551,2.93974640058,2.86546888485,2.9431944263,2.78569512148,2.78537024778,2.86171722246,1.65994127711,1.34385184387,1.25910047521,1.40370233877,0.893752448519,1.26836174426,2.94964730924,1.31580693736,1.55652366042,0.888190214363,0.925006893443,0.740419651271,0.846198385057,1.03070294967,1.09698675219,0.83187677366,0.897002184677,0.891037619639,1.02375899832,-0.0512393687728,-0.337739340242,-0.62500211203,0.373934297714,0.230483404581,0.308235096771,0.477324497395,-0.971526004077,-0.922192561584,2.70385746493,3.01035688556,2.67084160168,2.84347697326,2.70571163403,2.7875736973,2.62258820289,2.61956713519,2.78958362673,2.70464887,1.7634495875,1.79142543355,1.72698560124,1.66830994216,2.35222425595,2.27184579998,2.28779406012,2.378531525,2.44636662459,2.45647471613,0.66109176203,0.481654032713,0.257176187049,0.344428658364,0.393729385911,0.353529090643,0.214578967399,0.261147113575,0.381158825667,0.452150320772,0.360746178225,0.327722357895,1.19287838149,0.370688263333,0.544871955145,0.436515351838,1.04790465442,1.19879734083,1.09738629627,1.24405105675,1.18979698248,1.09429385711,1.30349968171,1.42517785413,1.33751099439,1.35956790281,1.94780849094,1.94824390514,2.18642621926,2.091336586,2.16182937584,2.06219307926,1.91899549918,2.02258964998,1.88265455219,1.85491810069,1.9841286264,2.05809048001,4.21048037437,4.10861469711,4.11650349066,3.49373274515,3.38872147926,3.46982846484,3.42655964308,3.33623599592,3.32263533136,4.42840056589,4.32998727874,4.69177300951,4.59124609767,4.62409812997,4.41492030678,4.01948098271,3.99933638259,3.57163616912,3.77563418025,3.63068211572,3.81862754274,3.18573094175,3.16823427122,3.06603676486,2.97911824285,2.71385261676,2.70538130561,2.80152548516,4.04456723151,4.19728346048,4.00825014304,4.08081816651,4.37348856039,4.43642950541,4.24865188571,4.16966205854,4.36011632576,4.21502943132,3.96469352269,3.82655071192,3.82494587081,3.94587657746,3.67394341119,3.69046635747,3.61132491049,3.58746589847,3.6596728796,3.71499964578,3.63755011506,3.70396082117,3.53059439743,3.54751905709,3.59644857667,3.68010632187,3.78964298143,3.97038092216,3.99798013731,3.88146945333,3.82411341257,3.93617750567,3.85098464111,4.0618092934,3.96777241499,4.20762803772,4.26029103249,4.20553937299,4.10601494771,-0.88851166691,-0.757607110153,-0.723608254172,-0.324718763628,-0.170737923068,-0.235613800024,-0.450423433778,-0.545967185144,-0.554529296894,-0.478384270756,-1.18489294725,-1.0867044832,-0.897948180691,-0.802196379677,-0.808082747898,-1.06464706439,-1.21555268904,-1.273013485,-1.19948207222,-0.974280922147,-0.625797953364,-0.71280266738,-0.618224297717,-0.822129430795,-0.846067987704,-0.74389557135,4.62106056097,4.53897115442,4.52826237087,4.68862787599,4.67382169392,4.59519113118,3.2957226091,3.50544513541,3.08925831335,3.04501568229,3.38884193173,3.55775289204,3.63417792473,3.55134989987,3.33673210566,3.41916027718,4.05610876882,4.14811048847,4.06875095472,4.13301551752,4.21975901927,4.22203953703,3.66716457733,3.68838314624,3.79208449968,3.86550524997,3.7518711505,3.84680583222,-0.512908534028,-0.422032544666,-0.513558710805,-0.325974340706,-0.31434638111,-0.411138892091,0.286328564596,0.214814053507,0.113099796917,0.086201166331,0.0392789761071,0.178385281799,0.069204518091,0.47054346314,1.04142854745,0.820057310514,1.07021075831,4.6224258209,4.500496184,4.54391815056,-1.49545253739,4.36985031548,4.39494202362,3.96451273443,4.17340538718,3.21770639537,3.25248581087,3.19015008925,3.32018119842,3.38595765246,3.34652896517,2.80265623029,2.88336857593,2.87290921357,2.71602341314,2.78918460942,2.71301270484,2.19358606176,2.28709090521,2.41819933583,2.28062044167,2.15494978715,1.58641494206,1.30744103024,1.04172348127,1.05356932497,1.24965238526,1.39167051437,1.83038116008,1.78244463568,1.84547774767,1.95982194314,2.09935079697,1.99896662776,1.93411830267,1.96870460723,2.12373842887,2.05825548206,1.96529532157,1.8738944297,1.91757844205,1.98626549387,1.83044020352,1.80761350012,-0.098055390008,0.0205279848299,-0.0618110005423,-0.133407383566,0.0452090907072,0.0906855168524,0.163942983955,-0.171999935637,0.379912939145,0.327741356756,0.534744740032,2.31370842773,2.2248071496,2.1631565753,2.14859955898,1.79139789083,0.908457405909,0.916064589656,1.01168825753,1.10487866042,1.31347702117,1.25529028883,1.42610050764,1.31359182614,1.3991973846,1.38993605609,1.32350388005,1.36412239299,1.27857674882,1.00344368134,1.04690390769,1.16820919449,1.13721386347,0.991257176046,0.915555912661,0.856205799378,0.892276687518,-1.34829790069,-1.36570201069,-1.47809577047,4.71087257864,4.67817994532,-1.53850861296,2.70894854684,2.5565774631,2.63178168134,2.7092733493,2.63231189043,2.55677735854,1.84011860971,1.67448119195,1.51971837853,1.82714411905,1.54955962042,1.69294102521,0.972280452208,1.02949787794,0.79354525147,0.843904391549,0.624888554137,0.61157651957,2.87707224307,2.8487430162,3.27751001236,3.13611002678,1.97839213003,1.90705876404,2.10523977951,2.37360018216,2.56470848352,2.44127150594,-0.0439643726805,-0.215212216031,0.0277274065428,-0.120820198281,-0.518231724491,-0.668244057982,-0.823723262846,-0.819538375141,1.63524860541,1.60482792275,1.57322731834,1.51124798587,1.48276883279,1.45155650489,0.609771380561,0.520551685348,0.599136196942,0.650405114534,0.507342735632,0.469733232263,0.901511683809,0.952769658946,1.04403708051,1.01128477505,0.920362039176,0.883440937236,0.938437312248,0.585664232203,0.697741141482,0.738413871036,0.766514866778,0.648044188705,0.53160382114,0.674874752038,0.622216643236,4.30436323869,4.39420026211,4.21470092969,4.21318083525,2.84057355341,3.11863928112,2.98948401253,2.85382131236,2.99390054479,2.9044280002,3.09910021655,2.9334411287,3.14760008999,3.0747567798,3.75515526765,3.78619300257,3.81830508958,3.91161502302,3.92681684514,4.05085005908,3.88816000525,3.9500865416,4.03550977012,4.09712768499,-1.0092971789,-0.915760288243,-1.00290395991,-0.906909801991,-0.81494577602,-0.817983269956,-1.31674636018,-1.29694660347,-0.77765296979,-0.786158415745,-0.316550531688,-0.388941792707,-0.369877590556,-0.268967581651,-0.194678078554,-0.222471579068,-1.26439911837,-1.41040608525,-1.36479366196,-1.43761561951,-1.20572466586,-1.2954645034,-1.31594175268,-1.24309915586,-0.980173745286,-1.13271323792,-1.0727609491,-1.14936126138,-0.878470326618,-0.868364735809,-0.9412828715,-1.02737747817,-1.04252018657,-0.966586502905,-1.01118700375,-0.906306155313,-1.02157225221,-1.09653330496,-0.772445970084,-0.855400761006,-0.781818271319,-0.880040735159,-1.55875419522,-1.53311360896,-1.15611754477,-1.0040642154,-0.291348123395,-0.298743440441,-0.51758431193,-0.407542922833,0.257923494676,0.204529595667,0.134584988231,0.158931241306,0.298841275136,0.327472837178,-0.241566691017,-0.141625934354,-0.062662028623,-0.257150780257,0.102796555404,-0.0421245196986,-0.187801574324,-0.0663057514158,0.0229768445663,-0.009426691336,-0.207088995546,-0.119174048894,4.10171066679,3.98847382492,3.59261534996,3.88093820393,3.40815784331,3.35100966308,2.03924382238,1.86183383779,1.98915205148,2.07203284993,2.61090951445,2.44550904276,2.68724519429,0.0380909713756,0.124837449767,-0.163827812607,0.0226692551703,2.24865196102,2.32284069072,2.32108949768,2.24733628767,2.40330220169,2.40370678252,1.67567683827,1.84741554784,1.89915336175,1.80678672531,1.02361237383,1.01940634546,1.12027483498,1.2310197547,1.61263600058,1.66753710932,1.49826534029,1.42722073212,1.20226721655,1.05402954397,1.1519038825,1.22072952387,1.01421584832,1.05532028674,1.13025788609,-0.330604559711,-0.546702879974,-0.701725520717,-0.347194141631,-0.515097492063,-0.662958207168,1.15826624767,1.08500818579,1.06663051066,1.0310776624,1.17615806291,1.21346148141,0.524547714207,0.559732205947,0.362934363196,0.417502526023,0.405697133905,0.496314011418,4.31002005149,4.31551148152,4.50082644637,4.40123267105,4.52035847713,4.42691776481,-1.10153025764,-1.18977050847,-1.26189509033,-1.24839736624,-0.849137710406,-0.930513518806,-0.763123003034,-0.763527959862,-0.842139413948,-0.915641278787,-0.848189840365,-0.921051574706,-0.940351550463,-1.02343444904,-1.10676179712,-0.955068553404,-1.12469050216,-1.05717486628,-1.27905848591,-1.22939507045,-1.39207588854,-1.43531883613,-1.23212940841,-1.2028055799,-1.27892482775,-1.37346280343,-1.21531692637,-1.40779995835,-1.41220980017,-1.52477674258,-0.328303604483,-0.734769718253,-0.864949184075,-0.511679571449,-0.471218166209,-0.569512428705,-0.0855943317026,-0.033239921773,-0.0111480907719,-0.180559567931,-0.124687211779,-0.197188976635,0.145913939523,0.339920920764,0.220837658429,0.197976902107,0.40725769675,0.34295048358,2.55871641126,2.49347637476,2.70513265496,1.26011311745,1.33601237432,1.17701599597,1.15445332267,-1.08699135045,-1.16161386668,-1.00205070313,-0.994105983556,-1.34050324085,-1.44794109311,-1.36217027008,-1.51408877532,-1.49368505851,-1.40534252002,-1.14271081402,-1.07970004554,-0.874502516409,-0.701392224969,-0.931067113542,-0.496376091583,-0.914910135456,-0.679913755962,-0.25818031439,-0.396694352828,2.31501161198,1.96905590529,2.14081751238,2.29362893911,1.47304190714,1.39025955354,1.44034935168,1.59305171514,1.66713562462,1.60084953551,-1.06648913466,-1.17325522189,-1.07775379539,-1.21570028702,-1.14899196242,2.11330436717,1.76816050621,2.11088763729,1.58462403444,1.70020162385,1.92227235776\n    \n};\n\nconst int NST1210 = 1210;\nconst double AREA1210[NST1210] = {\n    0.0105815883748,0.0102644697028,0.0104742230477,0.0101228244027,0.0102778786277,0.0105767279545,0.0102115947054,0.0104148922907,0.0104393595463,0.0103726242421,0.0102692103925,0.0103217424558,0.0105131183484,0.0120667339444,0.01022560461,0.0104616103637,0.0103126447991,0.0107995393712,0.0115537350518,0.0103980021895,0.0104701261581,0.0104202677378,0.0104032617992,0.0103175669261,0.0104961212921,0.0105548146995,0.0104372123936,0.0106007132435,0.0105827946548,0.0102469637394,0.0101932987443,0.0105293486584,0.0106285571503,0.0104933508411,0.0103611740815,0.0103961454428,0.0104170163534,0.0105174286489,0.0099753028681,0.0102834549034,0.0104068022485,0.0104420427935,0.0103811507109,0.0104252593459,0.0103063172844,0.0100554913097,0.0109846527399,0.010321035523,0.0105077330421,0.0103035458075,0.0104128387912,0.0103882853077,0.0105752463397,0.0105611618263,0.0103449475104,0.0103132287976,0.0102283922993,0.0102343914771,0.0105934950677,0.0103998604332,0.0103044361257,0.00987679968378,0.0105876851044,0.0104631508568,0.0102559503428,0.0104037420954,0.0102516577104,0.0105767282596,0.0104523685296,0.0103347920941,0.0106589337092,0.00996380546814,0.010108243519,0.0103099151758,0.0102914590053,0.0109198980792,0.009964430837,0.0102963650385,0.0104925752787,0.0103666477341,0.0100911068216,0.0120841664683,0.0102857274078,0.0109600175455,0.0102958181392,0.0104432663056,0.0103189035617,0.0102832458158,0.0103560590655,0.0104748686079,0.0105863982892,0.0104943774439,0.0102965851622,0.01054290173,0.0102691459518,0.0103406991862,0.0103406962665,0.0104727307703,0.0106172361432,0.0105470190165,0.0102622973396,0.0102160239831,0.0103796467278,0.0106947182487,0.010714446147,0.0106740638035,0.010690951782,0.0106880805801,0.0105630808166,0.0104898188451,0.0103757060715,0.0102289095535,0.0104200361736,0.0102781971857,0.0105440703406,0.0101065645837,0.0104037406759,0.0104520907029,0.0097627893333,0.0100988158698,0.00988555472511,0.0099786861237,0.00965370089592,0.0125030532463,0.00988210906848,0.010299173925,0.0105361639788,0.0103899148201,0.00971535743967,0.0105957302075,0.00982670796505,0.0101792440939,0.0102284088233,0.010539932162,0.00991278333349,0.0105110118218,0.0102540782241,0.00986942374763,0.0102491043966,0.0100552612772,0.0105577024759,0.0102641956867,0.0104540626571,0.0102125829929,0.00983627521059,0.0104080262433,0.0104193734776,0.0105448821806,0.0103666311533,0.0102947016475,0.0102661842223,0.0105621394289,0.0096992980693,0.0107708638655,0.0098979620282,0.00965283760768,0.0128903307138,0.0101955664823,0.0103132466465,0.0104420204548,0.010277925134,0.0102488695992,0.0106181619889,0.0103044496208,0.0105367002053,0.0102527160946,0.0103015799929,0.0105684044325,0.0103607349557,0.0104381654239,0.0104243973608,0.010344058014,0.0105035426792,0.0104471079359,0.0103854048374,0.0103043647427,0.00980962843679,0.0112293928536,0.0101798815941,0.0105441072953,0.010540281906,0.0103908639322,0.0105214042198,0.0103117794916,0.010296991709,0.0103164857432,0.0104867610257,0.0103228916973,0.010349554102,0.0102588182716,0.0105896802269,0.0102398876233,0.0105011514741,0.010371464687,0.0108026478126,0.00984867498031,0.0100598352636,0.0103403446021,0.0101329989664,0.0101964363164,0.0108329295962,0.00981355697703,0.00993604973807,0.0116705197684,0.0101131236941,0.0102661011738,0.00993891527786,0.0105484632863,0.0100357804769,0.0103336120241,0.0103833340105,0.0100996097185,0.0107883755675,0.00976243221501,0.0103726823229,0.0102970231156,0.0103873972098,0.0101840275449,0.0105141745996,0.0103306257918,0.0105384625792,0.0102934852398,0.0104748143175,0.0105852990553,0.0102324895736,0.0104991879776,0.0103477484654,0.0103363527839,0.0100908358678,0.0121087768377,0.0100011366004,0.00990217522895,0.0105757765571,0.0104523321884,0.0105176875887,0.0101361445671,0.0104233794565,0.0104199584909,0.0103533837002,0.0104369976822,0.0101144032396,0.0100306965546,0.00979897295644,0.0109607159854,0.0103759182024,0.0102144418933,0.00995550156391,0.00983018115058,0.0105038438099,0.0102826134614,0.0107485448589,0.010133050021,0.0106006782045,0.0103215580486,0.0106308432735,0.0096580254756,0.0106480095225,0.0105660108159,0.0102722516355,0.0105645102348,0.0102211605036,0.0102680951458,0.0105453789053,0.0104209609716,0.0102708224938,0.0104504505926,0.0103722572591,0.0104885925456,0.0104430093234,0.0104373786661,0.0103979297728,0.0104552322245,0.0103678905248,0.010492343908,0.0104883486149,0.0103630832353,0.0104215618951,0.0103773136011,0.0102754466101,0.0104600754232,0.0105403200067,0.01037977422,0.0103190193238,0.0104464777031,0.0104887116206,0.0103765017187,0.0104511273858,0.0104599323375,0.0101823087867,0.00976869496094,0.0106456612029,0.0104006365873,0.0103526006817,0.0105185015193,0.0105259659,0.0102554934394,0.0105948047764,0.0102222578007,0.0106259104843,0.0104516634894,0.0104405860361,0.010714987457,0.00997604858463,0.0101797681823,0.0102044898556,0.0105375652157,0.00998694889754,0.010377928368,0.0104752946476,0.0103520998581,0.0105082117879,0.010123226609,0.010371494188,0.0110566280807,0.0107854086365,0.0097351474627,0.0099177606816,0.0103707970393,0.0111709658623,0.0101153574577,0.0109304830237,0.0100413972892,0.0107084536386,0.00994436342312,0.0116648305149,0.0101673957081,0.0106939633208,0.010246675378,0.0106309181049,0.0102429916609,0.0105265753454,0.0102237883044,0.0102364469523,0.0103177593795,0.0105450458958,0.0102732871496,0.010344889535,0.0103502652066,0.0103109245757,0.0105564070314,0.0100498063725,0.0104754564724,0.0102842411303,0.0103210760168,0.0103545576256,0.010501144871,0.0106328668672,0.0104123707862,0.0106614200486,0.0111008433349,0.0104313563875,0.0103978316838,0.0104618367233,0.0104408121196,0.0102311975159,0.010594569657,0.0102881011466,0.010559197041,0.0102567301601,0.0105673265405,0.00974606282466,0.0104994380077,0.0103227268988,0.0103697330172,0.0121312324189,0.0106864063125,0.00973803790808,0.0105064244364,0.0100211239544,0.0101799682672,0.00981168327983,0.0109772043798,0.00985221830469,0.00987825414446,0.0101786377768,0.0101988756394,0.0104998006673,0.0102714177581,0.0105025906866,0.0104179948491,0.010350010057,0.0099761496408,0.0111800002736,0.00990237662541,0.0101783625425,0.0105835565999,0.0102607369946,0.0103587689816,0.0101995613179,0.0105088604475,0.010276215539,0.00976015322181,0.010157089374,0.00992336738837,0.00978368033997,0.0100400996165,0.0103065759828,0.0103322346085,0.0102116935777,0.0105886327323,0.0102265766534,0.0105137641093,0.0104586680318,0.0103992889891,0.0104224949383,0.0104052776472,0.0104135169212,0.0105040931062,0.0103174431438,0.0104784252388,0.0103955571563,0.00976044954318,0.0102161475475,0.0104416022899,0.0102807797964,0.0099711563704,0.0108580353313,0.0101127765342,0.0107203636702,0.0103777927311,0.0105737624686,0.0103962647065,0.0104717187303,0.0103514443933,0.0102881329807,0.0102738480376,0.010253545398,0.0106725430595,0.00986967055622,0.0100276029023,0.0106337483152,0.01028522635,0.0106000728296,0.0105023718281,0.0103404556766,0.00986616305163,0.0106254665048,0.0103323435399,0.0106065518053,0.0102441324305,0.0120746548805,0.0100768772784,0.0104214376709,0.0100179159025,0.0106883866933,0.00994988512799,0.0105245391924,0.0103424815193,0.0102966939178,0.00970730995485,0.0103640554517,0.0105388022069,0.0103750207516,0.0101670944617,0.01039859654,0.0103961864732,0.0103781137374,0.0104431829672,0.0104959505355,0.0105217337709,0.0101837999163,0.01056186595,0.0102448866489,0.0105921397472,0.0105748173116,0.0103727424581,0.0104246067804,0.0104176194571,0.0103231563426,0.0105060963878,0.0102863151705,0.00971114447492,0.0104324598658,0.0103600132474,0.0104277608279,0.0103706605739,0.0104076970299,0.0102814063818,0.0104987502958,0.0102835874794,0.0106081078822,0.010592017503,0.0102428887562,0.0102629752859,0.0105203971328,0.0102480187265,0.0102882362998,0.010451180679,0.0104282753384,0.0106186662983,0.0105292860999,0.0104733444611,0.0103392348471,0.0105121636793,0.0102615788879,0.0101327483935,0.010811384018,0.0097438733712,0.0102979879123,0.0101285092329,0.0106079271394,0.0105460555148,0.00990747766312,0.0104767843465,0.0104226647448,0.0105021414433,0.0105334951333,0.0102397353546,0.0103961729941,0.0104556824194,0.0103616595735,0.0103683398521,0.0103632326143,0.0104391452807,0.0104228507832,0.0103839368704,0.0105115724107,0.00996461734536,0.0104395111131,0.0104316337979,0.0103486712669,0.0104076884758,0.010459831501,0.00989571973841,0.0110128923641,0.00952232344722,0.0102138822643,0.0103943332182,0.0105906234915,0.0105292108757,0.0101533166746,0.00982591793702,0.0106649976806,0.0102849112605,0.0117732305632,0.0105512684382,0.010312173806,0.0100421366445,0.0102501031924,0.0107649790586,0.010084691178,0.0103755522019,0.0104905464696,0.0103478594685,0.010484199422,0.0105086707979,0.0105295115049,0.010291798551,0.0103647570713,0.0104416496222,0.01047308938,0.0103733645614,0.0103743628836,0.0105073792251,0.010330108746,0.0102596752631,0.0103069057915,0.0105347325764,0.0104536405338,0.010472229937,0.0102366610954,0.0101560019673,0.0105896844936,0.0105366013466,0.0103099364011,0.01041532927,0.0106462361553,0.0108243210215,0.0105910475128,0.0101272638051,0.0104804720872,0.0104055947958,0.0100466106325,0.0100381711442,0.0103550962451,0.0104915161771,0.0102806457072,0.0102593903976,0.0105538803987,0.0103996407634,0.0104567773444,0.0101618930094,0.0106028949502,0.0104786758055,0.0104330327898,0.0104136683698,0.0103893725113,0.0104259929469,0.0103545296735,0.0102306004294,0.0104238556532,0.010389990338,0.0104969973671,0.0103071219815,0.0101780761597,0.0105312462354,0.0106284845886,0.0105883380851,0.00990116183061,0.0102376848555,0.0101161807539,0.0100690912966,0.0106607269657,0.010406263158,0.0102272190085,0.0104894198139,0.0103177864842,0.0104275494447,0.0104233062869,0.010476373758,0.0102466143619,0.0104983529903,0.0103154571044,0.010026907239,0.0108085622162,0.0101478157693,0.0106957722818,0.0100169152971,0.010455390772,0.0103024991747,0.0101326038192,0.0104763716269,0.0103716727811,0.0104512056108,0.0103352545601,0.0105057537535,0.0103066071368,0.0103777107978,0.0103351472571,0.0104729284094,0.0105304279431,0.0102784138383,0.0104472084587,0.0104105646332,0.0104046390967,0.0103735291194,0.0105213028297,0.0102955191536,0.0103199719611,0.0104353926598,0.0103801909395,0.0103816307822,0.0105494398901,0.0100165544272,0.0103996804008,0.0106374620613,0.0104824765169,0.0103194283971,0.0100577910466,0.0112101625943,0.00996356775442,0.0100947478687,0.00989814650082,0.010439548638,0.0102679761422,0.0112385359254,0.010421211493,0.0100379463691,0.0103950151787,0.010726230978,0.0104432167631,0.0104336127093,0.0103559008815,0.0104157627818,0.0102148823818,0.0104090095288,0.0104257625263,0.0107357904547,0.00999945313973,0.0109118094665,0.0100409723257,0.0117604297485,0.0102814527367,0.0105849948808,0.0104823360426,0.0103737661143,0.010520602389,0.0106408431912,0.0102315426789,0.0108889680865,0.0102208515909,0.0106068762633,0.0105064446017,0.0102439741615,0.0106259091726,0.0102377147531,0.01038566821,0.010636013675,0.0102191499325,0.0105922974907,0.0103018993821,0.0105325554545,0.0104663030199,0.0103335734599,0.0102861435747,0.0105332779112,0.0102964311894,0.010457252943,0.0103915147182,0.0105177784279,0.010286436175,0.0105712252127,0.00973953315413,0.00976272593107,0.0104286388705,0.0107000130994,0.0108024844564,0.0107783961604,0.0101580358099,0.0100672300583,0.0108181672204,0.0102174766005,0.0104603372505,0.0105154613828,0.0103020738729,0.0104224027739,0.0102564961927,0.0102326989554,0.0105952849481,0.0104793243408,0.0103844203258,0.0104232799925,0.0103145748686,0.0102726369438,0.0105533022088,0.0101479557541,0.0107301513358,0.0100486045995,0.010360789415,0.0102545180342,0.0105005056614,0.0102609970001,0.0106145460361,0.00994765073092,0.00981465065597,0.00982489664599,0.00976559610814,0.0109414473467,0.0106201235939,0.0101092808495,0.0105736946518,0.0103694327304,0.0103192313396,0.0105177136969,0.0104718653874,0.0104270017035,0.0103250640859,0.0100817221137,0.0109109738748,0.00994814793658,0.0104254161388,0.0103180800543,0.0103030789878,0.010524625347,0.0103350740716,0.0103944217331,0.0102442632863,0.0105244301898,0.0103439904592,0.0104573647864,0.0104722468957,0.01028267819,0.0103674787171,0.0103502957597,0.0102667621535,0.0105681576668,0.0104663708965,0.0104769532039,0.0103457698529,0.0103574577707,0.0104382733318,0.0103197006878,0.0100538388717,0.00995075402731,0.0101206060627,0.00995331507407,0.0100578207457,0.0104158303816,0.0104301169891,0.0104386145452,0.0104064498619,0.0103399351751,0.0105209214779,0.0103452285742,0.0104419732953,0.0107071224213,0.0106752611645,0.0102406310456,0.0104923136905,0.0103097721844,0.0104393903775,0.0102898168233,0.0105393525089,0.0101767462282,0.0102841450902,0.00998942151685,0.0101800367877,0.01084721793,0.009734897682,0.0109260257052,0.0102456676399,0.0105789024109,0.0104599864875,0.0104822281837,0.0103820741704,0.0104285369685,0.0103455148101,0.0104303997288,0.0106010102233,0.0102591025994,0.0102517169704,0.0106078654894,0.0108245977589,0.0103558918938,0.0101363038169,0.0105674304632,0.0104333354227,0.0102716723924,0.0103054485242,0.0105362431125,0.0104126955424,0.0103473132946,0.0104267742674,0.0105557182469,0.0103527299755,0.0104703279724,0.0103228305301,0.0103221024539,0.0104720191505,0.0103837305983,0.0104902052888,0.0103264793627,0.0105685604239,0.0102930359814,0.0105793136321,0.0102713441523,0.0103212061712,0.0105033942738,0.0105440901264,0.0105701321081,0.0104635661701,0.0103323114209,0.0105792881274,0.0102786768001,0.0104765528355,0.0103877353952,0.0104298319571,0.0104368954615,0.0104860066706,0.010181002528,0.0103764408875,0.0104649322915,0.0105715309563,0.0102029533268,0.0104765573466,0.0102962145868,0.010377042657,0.0104474353656,0.0105120052172,0.010446452957,0.0104517699589,0.0103801454764,0.0126117052418,0.010514356636,0.0103360202172,0.0104086295161,0.010379064172,0.0104386936899,0.0103268469775,0.0104984825647,0.0103061638974,0.0102802037239,0.0105264753332,0.0103544616703,0.0104735849024,0.0100856485609,0.0108943217064,0.00981062591665,0.0101495740381,0.0101051903435,0.0106256969358,0.00985767338481,0.0105083919744,0.00977400037058,0.010466987402,0.0099711860491,0.0122879598083,0.010109674594,0.00999176835826,0.012313514403,0.0101107492776,0.0105366457437,0.0105861923899,0.00973484714554,0.0106009464116,0.0102165289935,0.0102025663645,0.0105801859246,0.0105723333349,0.0102275651765,0.010248460351,0.010189044006,0.0102292453826,0.0106316436328,0.00977009858869,0.0113309269221,0.0111063695494,0.0107866946227,0.0107264625043,0.00998991010352,0.0106636013034,0.0100952071429,0.0102984111942,0.0106433956275,0.0105718056954,0.0102402217631,0.0105381434608,0.0103984183882,0.0105132563727,0.0103803649239,0.0108026400336,0.00984326758333,0.0100265135398,0.0107042515987,0.01028666347,0.0105122349979,0.00971816063564,0.0102200356363,0.00978196840318,0.0105213138109,0.0103704634776,0.0105033392396,0.0103426082918,0.0105118603761,0.0103541931863,0.0101001745445,0.0102947291216,0.00978377143856,0.0108766034506,0.0101029910007,0.0106194911627,0.0103369900269,0.0103152565103,0.0103162823072,0.0105034481012,0.010454645163,0.0103455305102,0.0104551342674,0.0103522697453,0.0104513079097,0.0103719067083,0.010429672737,0.0103945441147,0.0104315955703,0.0104178289827,0.0103764778845,0.0105277505475,0.0103108660918,0.0104980631862,0.0101878997834,0.0111708221392,0.0098292427362,0.0103846361875,0.0104640541941,0.0103774099447,0.0104573617106,0.010518849425,0.0102449887704,0.0105328661897,0.0103937715743,0.0104028140343,0.0104708661801,0.0101974823052,0.0102041634612,0.0105451045129,0.0103657840394,0.0103179733448,0.0105348791332,0.0101971217943,0.0102799432237,0.0105376923549,0.0104837583315,0.0103273773779,0.0104039900379,0.0102459878554,0.010564352648,0.0105360951252,0.0102148302114,0.0103499442861,0.0104167938561,0.0102720268997,0.00995263560462,0.0107659051632,0.0104442333927,0.0102988444499,0.0104479433355,0.0101298691408,0.0104807213927,0.0103470643527,0.0104688806579,0.0103789354974,0.0104460541162,0.0103832977757,0.0103401179088,0.0104873969886,0.0102896653105,0.0104552630651,0.010330784568,0.0104379573025,0.0103707004801,0.0102870585058,0.0104459602852,0.0105361969029,0.0103221388172,0.0102516440799,0.0102826247961,0.0105801969368,0.0104693530802,0.00996696624034,0.0101232138894,0.0102851108575,0.00996453096387,0.010269631969,0.0102653695654,0.0105875722514,0.0103842156691,0.0103915801797,0.0104537872253,0.0101144597229,0.0102781139795,0.0102416553707,0.0105798788237,0.0105974583607,0.0102990443397,0.0104979684585,0.0105996268063,0.010458716422,0.0103251244274,0.00971443551925,0.0108631965795,0.0101171462212,0.0102389939703,0.0101441783305,0.0106264602867,0.0105919050444,0.010264506373,0.0105826002222,0.0102265318749,0.0106365873095,0.0102239071152,0.0118452417,0.010006523078,0.0100807716581,0.00997590707994,0.00986124087442,0.0106741410185,0.0100314476527,0.0101869763477,0.0102823024125,0.0105386690093,0.0101303840856,0.0104915255053,0.0103320531191,0.0103474528655,0.0102308842903,0.0106424402995,0.010031145399,0.0105391378433,0.0103266564548,0.0105670987977,0.0102270911374,0.00983690986944,0.0103572666865,0.00993030324082,0.0116984823348,0.00996989611834,0.010564560988,0.0103372883495,0.0102901266503,0.0104959796218,0.0101513240611,0.0107953890218,0.00968670248461,0.0103414739227,0.0102819233958,0.0102712761527,0.010516094657,0.0102196488809,0.0105661896321,0.0104210540684,0.010323668271,0.0103915174444,0.0103083852474,0.0102627345415,0.0104522171906,0.0104622067846,0.010344578738,0.0102724997522,0.0101165026992,0.0108071142406,0.00964052880432,0.00981228132036,0.010261497294,0.00989648386037,0.0113490892888,0.0105308281335,0.00998533726909,0.0105736954246,0.010206639795,0.0102930963238,0.0105325937631,0.010321284861,0.0102425013678,0.0104996214576,0.0103055240111,0.00978564784786,0.0101568514701,0.0106011502919,0.0101748377971,0.0104312961324,0.0104085802323,0.0104457125616,0.0102159738571,0.0102893855808,0.0102600730987,0.0105990581274,0.0101831061674,0.0106041619118,0.0101820775503,0.00974975534291,0.0103430455291,0.0103875397665,0.0104206789484,0.0103525996707,0.0104207724676,0.00998163268985,0.0104182517095,0.0104206474616,0.0104709458888,0.0111103689831,0.00988777538662,0.0102812259436,0.0107277842339,0.00993074780976,0.0102493219154,0.0106170291905,0.0102456302664,0.00979602911541,0.0106451664731,0.0107113932713,0.0109760838109,0.0109145214197,0.0100012761805,0.0109007596452,0.0101841337823,0.0105582099846,0.0102192285419,0.0100742900767,0.00998596401348,0.0107054116917,0.00988152376167,0.0110425862967,0.0108760446164,0.00989191724094,0.0101217233438,0.010134878759,0.00999259715767,0.0107150694152,0.0098593512599,0.00991954904544,0.00980840686291,0.0104569314988,0.0104606792545,0.0103252079822,0.0105616768184,0.0103524704496,0.0104822869481,0.0105447886297,0.00992501071612,0.00988965271451,0.0101754608903,0.0105235017559,0.0105265699774,0.0102490824411,0.0103076669891,0.0107131780819,0.00979060339584,0.010029194837,0.0100999177045,0.0109459494847,0.010962588777,0.0103099856973,0.0104116346993,0.0103351316633,0.010529481017,0.0104618972089,0.0103706264382,0.00984331493696,0.0119909456688,0.0102202016912,0.010362622068,0.00976856865114,0.0103040954981,0.0120205113236,0.0105562986467,0.0107819527423,0.0101087123915,0.0106769178624,0.01039411808,0.0103464138827\n    \n};\nconst double PHI1210[NST1210] = {\n    1.51621748696,1.42546447445,0.109058343346,1.56445379233,1.55798683491,0.520980156459,0.469899633873,0.87373173256,0.954501863164,1.03511866581,0.393425208283,0.510671615249,0.591789667674,1.47112653296,0.752864111266,0.843304679863,1.56498865885,1.51865874951,2.83456368525,2.86991016082,1.65072729668,1.55291854816,1.50920324608,1.50991498292,1.42171950984,1.0207988842,1.63981274311,1.78972890118,1.63841200247,1.7117897205,1.57046937264,1.56567311076,1.6498045011,1.41752518702,1.50592781646,1.49807896573,2.15882685451,2.23660332996,1.27915839414,1.52166066125,2.061721988,1.81808439621,2.14476664434,1.28386869155,1.36638352668,1.14751335141,1.17295588267,1.82264821059,1.75451684981,1.65887477765,1.70304843556,1.79312812917,0.200057567547,0.381278832452,0.445883474125,0.256513761537,0.346048782375,0.619218716796,1.28946687519,0.960429121203,0.888589437371,0.989360451037,0.907229217026,0.718407180851,0.646655605475,0.637233688422,0.606283109897,0.664252151439,0.510007642709,0.592312319773,0.66202481531,0.663677505974,0.757638447601,0.969912780932,1.45998092358,1.38309007407,0.94125192599,1.01976903239,1.2740644261,1.18824717469,1.13953306308,1.19904326906,0.991349896651,1.01301779024,1.04706137615,0.994551729828,0.927890808349,1.00876443541,0.86220882135,0.901030579275,0.726705431498,0.935438293162,0.856987601762,0.782260933653,0.793873721134,0.903446914126,0.946686669536,0.879487615093,0.694753182722,0.402298193234,0.337428296842,0.245926004518,0.0753444502176,1.00056918283,1.06642130372,1.08069139571,1.12045267983,1.07560481409,1.44724190656,1.46842180743,1.50085320051,1.13382602304,1.23631397355,1.27284682049,1.22096651596,1.1392902951,1.01754547767,1.0495131794,1.35876735159,1.44976805515,1.56408302728,1.54398573104,0.995246030941,0.941802903674,0.865988348564,1.09618704417,1.1470078176,1.28790338176,1.40270384287,1.38542159045,1.4305816398,2.11019468331,2.28095869006,2.36387190523,2.67324507032,2.11686689782,2.07360110188,2.75501996197,2.61770380514,2.7026767109,2.71139849403,2.68574973495,2.37355631626,2.16783347667,2.24575042977,1.37676447051,1.41893306669,1.28638175188,1.37425991061,1.38322514765,1.40237174935,1.43542131751,2.0660843809,2.14474543623,1.9948969773,1.9175977813,1.98608104252,2.21557782354,2.19267019536,1.58838533313,1.65991413722,1.71949567449,1.79081667336,1.71428020128,2.54577867832,2.46534458112,2.1538026667,2.12948321251,2.15586001525,1.92794133358,2.08117430588,1.92556268542,2.00102552088,2.00477395763,2.08253681563,1.84907341052,1.61739446592,1.61931622624,1.6973574852,1.77521418768,1.92823281521,2.08730703273,2.01127209031,1.9323349165,2.15421182913,2.00134262949,2.08147381255,2.16554050611,2.24205212794,2.32119093523,2.31490808102,1.70626483554,1.77977538072,1.31712966935,1.33727179584,1.34134232814,1.41540170915,1.49113733373,1.17479227678,1.12558717781,1.17506127358,1.26077963103,1.3222351575,1.27720848287,1.31410229819,1.54647554488,1.40320407797,1.46544769758,1.40215714737,1.76947014894,1.80610528942,1.99574767591,1.85706787095,1.94023246978,2.34212132719,2.16330169743,1.9038583572,2.07521589081,2.02735219681,1.94461207487,2.14217329809,2.29826044789,2.21053579703,2.16320260033,2.19851608795,1.44148849256,1.52246922475,1.25480845733,1.12805198472,1.29432137145,1.21868125777,1.21346841489,1.27480751613,1.61878658926,1.52854651741,1.60718249163,1.124634882,1.20880916689,1.22066117625,1.05073945676,1.07853534877,1.02085560505,1.134943739,1.11211021177,1.12226538172,1.04968433011,1.3057402632,1.25306720784,1.20680601886,1.21556417046,1.43317781257,1.44382660597,1.36908900928,1.28398188141,1.45491304368,1.37468443343,1.36035535067,1.91799623027,1.99964589637,1.97542875051,1.90556797852,2.06049642712,2.07251254336,1.76525248273,1.84564258119,1.7733920621,1.85056106318,1.80547472892,1.72119743093,1.76577638084,1.70162140908,1.75394937577,1.83052141039,1.7046661718,1.65635815812,1.70106439141,1.84286015619,1.79833773814,1.82658935581,1.9114940973,1.76715954499,1.93553131855,1.78797661948,1.8712804856,2.36655159941,0.480611488298,0.569981750219,0.245979658071,1.22574472538,0.873004343111,0.844734426653,0.716191030292,0.796283068156,0.805408543896,0.639676714409,0.559497534439,0.411106024682,0.495948625414,0.564206108719,0.754334342969,0.718007521774,0.937307865486,1.01324106495,1.17357745353,1.2458886128,1.29639879695,1.26007464778,0.972206068648,1.01477817641,0.700123556798,0.881820475048,1.4127740756,1.75057202843,0.77657090957,0.869064219957,0.884341502677,0.708802378054,0.826931740893,0.744822268548,0.966448482132,1.03043456344,0.95976169299,1.11581441294,1.13837599718,1.01373041691,1.0909685482,1.43276183986,1.34051078414,1.08798057271,1.17144376064,1.08497543019,1.15664737865,0.321760010476,0.506819466206,0.436534026658,0.587993559024,0.609738299562,0.162529415947,0.317373368073,0.233200502628,0.920629240865,0.751293899718,0.641076692096,0.489348684258,0.606018851568,0.517347699528,0.723357949878,0.723602455217,1.08755645306,1.27691571419,0.75127565442,0.813155212937,0.793305595835,0.663589077967,1.01485260341,0.946717676798,0.966041099499,1.10069671692,0.640269752675,0.473347013309,1.18559552978,1.0316913021,1.09946891195,0.602023243749,0.218279419074,0.191385124784,0.1148180178,0.101028080254,0.269600879529,0.340659461224,0.254715009671,0.420727314368,1.16306272756,1.06946757815,1.04321291353,0.953182115511,0.87468832841,0.80275876655,0.96252770523,0.895885868154,0.814724284857,1.11625630152,1.26317453868,1.19154265442,1.8211545125,1.19580790929,1.36285560159,1.34014121063,1.16695987653,1.30934676049,1.22594157233,1.07461204656,1.11320880095,0.982582952745,0.967550223917,1.05206826706,1.3838074738,1.3479395398,1.2482494423,1.3316841505,1.21150773602,1.2591966438,1.51662414796,1.42815130134,1.37629781033,1.48685174111,1.39789956561,1.36081274481,1.41287006953,1.20080419798,1.29268319762,0.828572708653,0.733204656174,0.689118976497,0.742491636257,2.17141482351,2.13141750076,1.95600296371,2.02215969881,1.86925409128,1.84902933874,2.26526177373,2.32992381535,2.30803465357,2.74765021823,2.41160819502,2.43005160247,2.51566451626,2.5800920616,2.96389953208,3.01337152006,3.10217365136,3.08159200375,2.77525594451,2.79190638933,2.86611188244,2.94353249107,2.92381062593,2.78079040411,2.832285651,2.68966235731,2.76378892706,2.807416985,2.82423779997,2.6758623166,2.75235042698,2.52733669605,2.45119394635,2.4499729615,2.60564100422,2.52346851599,2.60339896034,2.29920903436,2.14529152973,2.50903566048,2.42360502333,2.45595056069,2.29876206851,2.25777760214,2.39096663195,2.43591916852,2.37172090159,2.35924291393,2.2745421722,2.20940175931,2.22121656685,2.29852960606,1.97354235465,2.06624399309,2.10364225423,2.04762717024,1.9073529231,1.95743913154,1.92044251652,1.8206646673,1.23945446826,1.10566980448,1.15029841005,1.285377037,1.13363567848,1.19409806413,1.31417530776,1.28382146245,1.52242400145,1.55858635392,1.64546722934,1.47804589605,1.42427655996,1.45625783033,2.30706209879,2.2960085323,2.4444309568,2.36216672625,2.45676200477,2.38524997385,2.23674892497,2.15502211807,2.08575437609,2.23220494627,2.08358086332,2.14828270669,2.76712151989,2.50523742639,1.52630423263,1.59821135725,1.74190700732,2.46632586911,2.54811759785,2.62783683967,2.61782531807,2.39030850366,2.30996438205,2.31286371551,2.39162721559,2.23710099637,2.23548172245,2.29826488674,1.70292650367,1.70206758043,1.7789294834,1.85782048516,1.8569091702,1.77892739242,2.28322058945,2.14321490598,2.11264825762,2.01129249008,1.9333645814,2.55584574488,2.40084602811,2.42694454061,2.36815802848,2.38743609112,1.44695502062,1.52615175082,1.33399286132,1.35941559683,1.40874051353,1.56389775747,1.49074685877,1.41893113306,1.66370473724,1.60891271899,1.78605970057,1.75252174124,1.87589711531,2.03297652876,1.94455914855,2.07412670221,2.02190013353,1.89520833479,1.93165843809,2.11925099314,2.17448107779,2.08292436574,2.10384921598,2.22146374272,2.14258612661,2.01058442775,2.03866790716,2.20928681373,2.24593626836,2.18787008773,1.99124013472,2.05936399154,2.0422918762,2.28817272254,2.46713379306,2.34118059935,2.4278270104,1.59388718,1.27747857533,1.4431702802,1.37120089402,1.67276264547,1.32023350252,1.45633006977,1.30441473947,1.37518754299,0.63666580982,0.717382527554,0.957215970136,0.968337620955,0.876419077333,0.804679557991,0.958028072496,0.96664254161,0.875790188666,0.80054989109,0.74717858288,0.812109086118,0.895725155146,0.770476976517,1.06390386231,1.16802601226,1.14946496969,1.10224314373,1.4656223874,1.39645739602,1.37916238453,1.39925181719,1.31506164745,1.30268407209,1.55429442502,1.42538247254,1.40514915028,1.47055773444,1.65786921443,1.5739098148,1.51026508567,1.52563360952,1.60542068673,1.67409825377,1.55718985175,1.62973517701,1.71884165587,1.7321353406,1.61434380654,1.51478556439,1.6519495976,1.68279165972,1.84361079657,1.83805426454,1.79442365599,1.93154729157,1.98133163103,1.9367467107,1.9682804287,1.93284810839,1.98176295555,2.11924277388,2.07158814229,0.441716998491,0.35254743424,0.330274693655,0.501109928311,0.48460368558,0.404782138027,0.651088171066,0.736355832355,0.757386239602,0.586280876028,0.617751211939,0.702532237927,0.835278745497,0.998052896503,0.880462771506,0.962430977657,0.804822987963,0.872220882686,0.89812575617,0.84774356933,0.742866722367,0.759679457863,1.11888543509,0.978847870055,1.03028197992,1.01914793791,1.15500576574,1.10602259928,0.737826748999,0.876899819366,0.830969486968,1.8244206642,1.81646147262,1.88663398332,1.96816270934,1.33315662658,1.23882781766,1.25020877709,1.30165751193,1.38303627085,0.35640762745,0.288568466038,0.362819034626,0.30510921371,0.552464333335,0.411312361612,0.461746540897,0.900901910056,0.579652836474,0.502696956338,0.209779923224,0.353951341788,0.287141419934,0.203975431884,0.902764866892,0.865490314477,0.77805002145,0.726743659708,0.904345899967,0.869176887066,0.855738290259,0.767936846218,0.780970433069,0.727527839718,0.703525260488,0.709341375487,0.641037389095,0.556276070248,0.5491919685,0.628082669577,1.15957736915,1.25133651127,1.16655724786,1.20837065414,1.29886664347,1.32145960818,1.25836131412,1.11284051893,1.17278450403,1.14529006913,1.02406243583,0.993370607029,1.05480371127,0.554291917949,0.487908561455,0.604325150637,0.574810776932,0.33891566895,0.427471930387,0.466324070812,0.294196412419,0.435574566968,0.355408212834,0.654343450213,0.572671353839,0.50752701173,0.67059831732,0.640737193886,0.610336478529,0.529328151065,0.59429240779,0.268183741057,0.360612861194,0.249432490657,0.362313678113,0.421345380927,0.47467634668,0.394828759869,1.11271966972,1.11646098735,1.03062841096,1.0328710576,1.27809094087,1.19517664278,1.19372186868,1.35261609623,1.34467670084,1.26868659907,1.58577859045,1.64075134935,1.75927872434,1.72892064332,1.62836204629,1.53892875783,1.64318497604,1.68073143005,1.55392370514,1.50184928539,1.17429838528,1.12286061237,1.21433709989,1.24096514557,1.08113731475,1.0561063067,0.867121294524,0.957703521102,1.00583421193,0.967587335607,0.87719380758,0.824379608794,2.05018243755,1.99794949382,1.91197653135,1.90719352459,1.99315625415,2.01863878546,2.1836631177,2.11743442033,2.03607542766,2.20854185233,2.24741235717,2.33600040045,2.3886304165,2.80489933839,2.89703163809,2.65688029857,2.59410332213,2.50842532449,2.47809997023,2.45167144781,2.47542049723,2.55806187411,3.01088667697,2.93127632427,2.89512818356,2.97157423919,2.84679825964,2.85620030374,2.8021692739,2.72353962806,2.96209993431,3.04642519262,2.87343252588,2.88632625766,3.00714243604,2.92274593079,2.14404330657,2.22040048186,2.13004235291,2.21186405002,2.5287817085,2.71640472174,2.65609342062,2.57053671326,2.37658886665,2.21369265236,2.30693269709,2.2263201143,2.41778902564,2.30035904457,2.38580484576,2.25012142441,2.27730302162,2.35903056916,1.77696374983,1.6432245697,1.68727542108,1.69244010785,1.78619507892,1.82684245168,1.16750806591,1.24374589509,1.29431600725,1.25816068548,1.11314034494,1.15343145825,1.56385548927,1.73648973307,1.61813145971,1.70333251756,1.68185730638,1.59459208502,1.57386165855,1.54061092591,2.29353226854,2.20291229762,2.89677369517,2.927271491,3.01377680727,2.99060373274,2.85085601582,2.8373281509,2.74708135941,2.71027862553,2.37962497945,2.44465444907,2.2968960005,2.41890577989,2.3298822336,2.27244117665,2.59057015505,1.80055318486,1.81290772888,1.68050447035,1.75272735508,1.68889963424,1.76959756093,1.84441416454,1.83585551385,2.45498366463,2.36751604158,2.50399929633,2.52708785353,2.25710447468,2.23141949984,2.2883464753,2.34514800042,2.37850325408,2.41028484988,1.49356781955,1.93529698461,1.86492284492,1.86055924787,2.01047108568,1.95460929027,2.02547395575,2.08429576479,2.22337325538,2.14988226603,2.08728142904,2.16546630404,2.23575768772,2.51411314638,2.56081195031,2.69096530017,2.65023411215,2.48120308273,2.47381396957,2.56201339306,2.542957103,2.6413819743,2.62905294332,1.35981421936,1.39021908125,1.47047721061,1.47345467871,1.62241851765,1.55046283134,1.62576923811,1.55675184001,1.90859742327,1.96919917116,1.84896237231,1.87777441706,1.89754688588,1.92932847575,2.00315666403,1.98193994166,2.51253586681,2.47737312032,2.3878118346,2.33285140557,1.91093592961,1.84183647676,2.55061351845,2.60425602692,2.6101259984,2.56875618637,2.47418646435,2.43310841312,2.47648659315,2.56512014717,2.61736102115,2.31653981324,2.25654522923,2.4269166859,2.40786607817,2.35996513051,2.2805554838,1.59565737671,1.67585714512,1.59963896319,1.67701991537,1.43430926728,1.43719910919,1.51504910589,1.51399468646,1.35575218562,1.35391307859,1.19511156641,1.11725756057,1.03336259397,1.03486832727,1.43537862053,1.44141423597,1.51966117046,1.51682219939,1.23008598392,1.29689477646,1.15010285769,1.67306812284,1.59839441167,1.59524461154,1.75369552744,1.75374517465,1.7523955656,1.8315158638,0.851535288127,0.770147399351,0.749067112067,0.815594349145,0.913993190497,0.898419674762,0.85056269056,0.959979942694,0.879660648178,1.01535054509,0.91140542175,0.995175889825,1.61573079759,1.54658863444,1.69509116223,1.69836837523,1.47144789064,1.62203608033,1.5496256502,1.61632898677,1.53607970655,1.46357466364,1.50825350128,1.57602514033,1.59826315906,1.52000615091,1.6723019196,1.66174531183,2.22416085083,2.19510735473,2.10884739428,2.05407611199,2.08065291427,2.16408357545,2.33489907429,2.24787758497,2.20876716055,2.25527809093,2.38718182308,2.34613359647,0.689526489685,0.595693145996,1.90438162471,1.9862044515,1.90710414691,2.06392183866,1.97805498182,2.05849683398,1.49276753735,1.40342830402,1.42513252075,1.51867766041,1.56167390153,0.207588223672,0.0618928276701,0.147392788638,0.0871973350548,0.174952203737,0.218773410563,0.920149731696,0.982535207083,1.0101966909,1.04124604734,0.767579889225,0.705047672248,0.741131879996,0.858185761523,0.834672726521,0.889839438047,0.805557771052,0.816361665072,0.744481406289,0.658274976046,0.72623517854,0.651332301031,0.589015228886,0.613085605533,0.5542690033,0.462928239515,0.43656846243,0.505367003926,1.26383513314,1.24986764043,1.3463860039,1.42903972423,1.3055321989,0.472868453724,0.391949041072,0.509236570011,0.46611917278,0.375061807393,0.33544767458,0.609006447029,0.529714259147,0.669546477586,0.654518627508,0.572803559422,0.508094889711,2.6196779205,2.52765814665,2.47839485977,2.5117286553,2.56327545965,2.50214870943,2.68291534924,2.66363854114,2.60418116174,2.52339960222,1.65830471799,1.77925218707,1.69390299571,1.71243913985,1.80275345206,1.83399260944,2.14965429921,2.05813796169,2.12347051976,2.18330476963,2.03232622368,2.00039515476,2.52826774177,2.59413347446,2.60857732283,2.53480397333,2.68944232341,2.68135542417,2.78208316492,2.74385943499,2.7144229312,2.62660544906,2.60243544975,2.65399602106,1.86541582902,1.86999641229,1.95360753504,2.19778240276,2.14858331011,2.21665409849,1.90970965467,1.89743280431,1.99362133641,2.06655880692,1.81684007347,1.64995163822,1.69194740715,1.7778928562,1.62150351211,1.68567467492,1.76639770586,1.94040117115,1.95881156068,1.81302350034,1.89547415915,1.64512446347,2.39476707334,2.30981488668,2.46901895625,2.44915026847,2.29446737227,2.36013672687,2.70591665033,2.84642118334,2.75460203025,2.69538353875,2.85882848053,2.77717700854,1.28741554504,1.35675400706,1.35998430691,1.75581881278,1.83526941655,1.83697099464,1.6783267637,1.68107538847,1.75674831501,1.90732801321,2.06445360196,1.98535455268,1.83215182333,1.83123495708,1.90847672126,1.38474360316,1.44638560831,1.41341482495,1.49419200888,1.53803791873,1.55740068927,2.66382688565,2.75542791541,2.62672968579,2.60401392271,2.7076930235,2.77736753446,2.08994323787,2.01194302555,2.11838514913,2.05517453718,1.95485230866,1.97019737529,1.51856905098,1.52923932319,1.46811710867,1.90042302761,1.87565430499,1.79360374983,1.2077258229,1.04323679267,1.05383010808,1.13713191263,1.98227037229,2.12947901779,2.06003322282,1.66429982117,1.60080661164,1.74902948937,1.76248742667,1.69186794384,1.61498064894,1.73075292915,1.6025632435,1.57538454729,1.63959372321,1.19858160404,1.19279065809,1.11342786988,1.11745587875,1.27332897427,1.27513911845,2.09116874565,2.14297477749,2.10890453347,2.01248787998,1.99831469606,1.92787926352,1.92534907494,1.68667241687,1.75079293997,1.83814155669,1.85719611602,1.79303248366,1.70999352128\n    \n};\nconst double THETA1210[NST1210] = {\n    3.11837166216,3.11932414714,-1.22963658636,3.19412399388,3.04317603454,-1.41134560043,-1.26275007292,4.43556506202,4.38441765386,0.341892793979,1.15728500376,-1.08255320677,-1.07893654314,1.42538814013,1.49480570074,1.51856668002,3.34510391288,3.27175332363,-1.02814170562,-1.30245110029,3.04776369139,2.89072324328,2.96574198838,2.81054402584,2.80585910181,2.70993139419,-0.0563353781581,0.0445747224544,0.0373263334072,0.0880206445911,-0.202449977954,-0.109546829708,-0.242583708083,-0.211451337836,-0.342129809157,-0.253465672965,-0.799469028517,0.644813677546,0.0840530427988,2.00035622248,1.51097219769,1.70377208021,1.503766553,4.66775871893,4.70784426497,-0.724740910668,-0.824202867523,-1.06874404746,-1.02255643391,3.3469418467,3.4281442663,3.44326214087,-1.29999416217,-1.28938303689,3.59116229367,4.6674735045,-1.52958396516,3.67033187683,3.81525718569,4.27818853466,4.20773825767,4.06346686287,4.0944693894,4.56263433465,4.64608051552,-1.22200126314,-1.37495681289,-1.48932329842,3.45957572499,3.51962535768,3.29081390308,3.43059861126,3.46514258788,2.79801295711,3.7617053599,3.83565021177,3.52575011201,3.58727976664,3.71471118928,3.67714724639,0.172609153455,0.0810911906712,0.251338248249,0.0740099666707,0.166749346555,0.434383348028,0.634534656684,1.08803517947,0.971496370244,0.74217778796,1.3709960997,1.1455042016,1.09096312852,1.15907014709,1.28419157129,1.43800127615,1.25730408357,1.32606886665,1.60126803508,0.927189417082,0.776289650834,0.854194557097,1.4069017414,-0.781449671497,-0.703870362461,1.93938144249,2.03067476472,2.36949568234,2.23754231268,2.0746491912,2.16049019563,1.85826645861,1.70914474266,1.80014475136,1.87361318383,0.504453341667,0.612267304817,0.517233665488,1.32703686476,1.34091537716,1.53812872272,1.44292689264,1.93121477759,2.04209268759,2.04624335931,1.76478156878,1.68914603845,1.63940751783,1.50339321376,3.2002148721,3.27681764946,-1.2171998106,3.87948422521,3.92906162731,3.83538602594,3.05219580623,3.14340368351,-1.03022351193,-0.882124461722,-0.830875032161,-0.634147064147,-0.237708911364,4.06027001913,-1.30237137128,4.59040164403,3.04207431433,2.96361699981,3.04370956624,2.88390508472,2.72432927891,2.56469656464,2.64692931875,2.70531832224,2.66937932202,2.5304223086,2.644844393,2.62261303318,2.74017873607,2.85104093092,-0.382827619333,-0.334639797984,-0.192267565004,-0.048829402661,-0.098652606996,-0.777932674139,-0.838260326878,-0.906787242771,-1.11672705693,-0.583515707017,-0.739329093951,-0.639118256828,-0.644198132044,-0.592287379586,-0.789882809723,-0.742351611599,0.65042834881,0.579599465993,0.496449764927,0.626289405519,0.593093557002,0.612467537412,0.526855389052,0.472648188549,0.51670785938,0.691528838564,0.671065350218,0.630222920544,0.476758290183,0.532299178236,0.472595135281,0.3521081073,0.180655733761,0.22780671715,0.473209139724,-0.0737597320978,0.0151050527403,-0.121835955549,-0.0712933998157,0.250387654561,0.334720670884,0.414282641178,0.402075540817,0.173877354151,0.248513825904,0.320388859131,0.451134144587,0.346140182675,0.494259780919,0.442126122204,1.78491022453,1.86657927541,2.37182149394,2.43880178348,2.4479761964,1.91205817196,1.88309760379,1.70275498463,1.6971530215,1.60775568594,1.61293691155,0.797800145728,1.79916728222,1.79175953726,1.6941995772,1.59726073897,4.65957778409,4.70116003304,-1.2842521954,-1.15610095051,-0.999201353016,-1.02933563976,-1.13027358567,-1.18895190039,-1.35553200813,-1.49243682422,-1.44878583288,4.68263073763,-1.56225185242,-1.47004195469,-1.54045770969,-1.07014618784,-0.879001821614,-0.987897801208,-0.900285859946,-0.532677631706,-0.600805994501,-0.916187753059,-0.837115308598,-0.564351673373,-0.654099999793,-0.394670803757,-0.484033170732,-0.538443273013,-0.506255155654,-1.02630678736,-1.06384262081,-1.1527863273,-0.930849276014,-0.888592016764,-1.08197355066,-1.02480307109,-1.04757293326,-0.947486156972,-0.927409867419,-0.880023140276,-0.740714714024,-0.786576311667,-1.16538192325,-1.21349059485,-1.35830807734,-1.3088129645,4.11178976703,4.1478014875,3.27739617221,3.19790411198,3.12671102794,3.37265656914,3.28788762891,3.53209294385,3.55246442965,3.60134381051,3.64659797342,3.69124334943,3.71574833624,3.4539648412,3.78576703105,3.79865007752,4.32378648055,3.86321292675,3.90052391218,4.00849599088,4.42767433064,4.37063879998,4.24726112216,4.34759394231,4.41379565532,4.60662241644,4.68906242843,4.58236522798,-1.07469917328,-1.20614609461,3.41373976476,3.36518771177,3.29975601855,3.12496562751,3.20413759164,3.28670936533,2.98224799672,2.88738165998,3.01677339408,2.98921202379,3.90007973446,4.0190071403,3.56583950125,3.59030428239,3.70110770805,3.67716102143,3.79485900449,3.79467507579,3.75345529045,3.7044817178,3.86909928288,3.74644187321,3.83187782008,3.94605616858,3.92259991149,3.69590442195,3.67027211162,3.54507711044,3.59139164553,3.43415122945,3.40002276104,2.78441062539,3.27085719598,3.15477212605,3.19894504541,3.0467098274,1.52992181677,1.3209794253,1.20926193963,0.0520557050989,-0.0111041319525,0.479497926491,0.844090664081,0.625232230858,0.662553751545,-0.286328786961,-0.424768790898,1.14075541203,0.883226179763,0.694153863072,0.779729489609,0.90314393469,0.733309721862,0.980570147581,0.92040906335,0.812990153716,0.936924892391,1.33400963422,1.23345086636,1.275752332,1.30115478121,1.24318426616,1.8721355232,-0.395876528186,0.512253220305,-0.427528696831,0.390192555403,-0.647090026357,-1.07597190545,-1.010824326,-0.257311821968,0.00159327438444,0.00123005312775,-0.0971723355906,-0.244691145379,-0.295427648697,-0.227407867804,-0.134069344697,-0.0590462290842,-0.100050703288,-0.154820191929,-0.128150858911,-0.094079994922,2.35033477579,2.21090053283,2.22907114737,2.39683186456,2.38260027352,2.30936714012,2.30298467792,2.11076480323,2.20090350896,2.13000012705,2.24124316862,2.27933920814,2.0632875868,1.97595032994,2.12890497511,2.14051176811,2.03906396099,1.96331840181,1.60887938972,1.59188768604,1.66031880117,1.91659338357,1.90363221342,1.81737525792,1.74742194242,0.577229282646,0.558368279874,1.92819652748,1.71734449532,1.84287532955,1.94580433162,-1.5033501868,-1.40820870743,-1.17487711186,-1.24028807522,-1.21402980619,-1.3119299812,3.76150220185,3.67552146252,3.55414274841,2.8825568235,3.71260772277,3.84959784851,3.9057829976,3.80964560205,-1.37940336937,-0.918176143706,-0.569263657795,3.30329868658,-0.281741754039,-0.527273656148,-0.696764383538,-0.54911632607,-0.110818931473,0.14698944896,-0.0755801774771,4.01508562209,4.09441058646,-1.52482352367,4.49469139936,4.42922945552,4.3489651884,4.06056624997,4.1318409981,4.27433892518,4.13704749455,4.36939704083,4.30735392684,4.12031548277,4.22412018364,4.51929589641,4.54828497131,-0.982835060468,4.67959479378,-1.49994664564,4.67160793245,-1.50008047576,4.32359355246,4.45095943067,4.48335066335,4.40186553974,4.28746532976,4.24342416849,2.98320146311,2.97275821257,2.87719303842,2.80375176794,2.74738937025,2.82257518306,2.90702972745,2.75124409549,2.96316670019,2.88382249678,2.96648048338,2.88181291026,2.54643863208,2.46846308257,2.55930370291,2.47596223297,2.65206664207,2.73423999449,2.74033903449,2.32242385046,2.40124921686,2.4859963519,2.57447430905,2.69838624061,2.73619186868,2.78068237443,2.59232373275,2.51519522062,2.50959954645,2.56156488972,2.5059453843,2.39157124603,2.40298981934,2.34272737415,2.66387052569,1.25710821708,-0.520778119848,-0.472456358473,-0.376779593002,-0.553630158545,-0.620793503962,-0.536003797967,-0.368986062637,-0.627595370192,-0.571409345743,-0.814521611525,-0.758877487349,-0.74902617384,-0.635176648602,-0.450964082682,0.450585003493,0.362601798471,0.317897211553,0.367411593411,0.462106277157,0.500077688045,0.10123505184,-0.0598011040823,0.0339175089547,0.374510422334,0.324437925658,0.687789298905,0.532772413658,0.0593521376375,0.15684755728,0.276187184,0.587562756473,0.627213146695,0.704906118763,0.620449777757,0.156403198682,0.07925793861,0.0241719224356,0.0621535098549,1.93645733843,2.01042176294,2.02532769442,1.94348651441,2.03208919727,1.78721674419,1.78747367364,1.87577018056,1.95972410881,1.87014484548,1.9539558368,1.30698986318,1.39923898619,1.11430059044,1.0089008294,1.17446512069,1.19860958606,1.42180867379,1.32196091003,0.867808065136,1.05928253501,0.978289930066,0.77075748547,0.835408730958,0.93682304994,1.58910093417,1.56503347371,1.69338108391,1.69435097996,4.39052380356,4.57739019768,4.0453597975,3.98715782078,4.43132436202,-1.33886469471,-1.44318297279,-1.43243399855,-1.48412031441,-0.933034486614,-0.94472135887,-0.46145751032,-0.569871114784,-0.414744428142,-0.482470361156,4.59818806747,4.70451001071,4.5493601062,4.61687578559,-1.45375776935,-1.54459562139,-1.50729524111,-1.32219700873,-1.44131947749,-1.31726737111,-1.40987071892,-1.24789490294,-0.933560952336,-0.888534854966,-0.623832672831,-0.798096497389,-0.764958341197,-0.680861319334,-1.30820167128,-1.20853590835,-1.29966574589,-1.35189787056,-1.16612463055,-1.21384728008,-1.16667538202,-1.07321375436,-1.02600673777,-1.07144967539,3.77466049805,3.72254181294,3.75204624706,3.83836245071,3.64156529611,3.62821360921,3.4969235183,3.58155838484,3.21651192787,3.06204447792,3.13482744689,3.06477567509,3.14524783542,3.22434399728,3.47819987364,3.38695486242,3.31005502491,3.23046086282,3.31806903779,3.95839803003,3.97041722939,4.21827946765,4.11846316493,4.30440955311,4.37843886551,4.20256277251,4.1622386466,4.03513289663,4.08900282812,3.93951914632,3.92782256731,-1.07276630762,-1.07066031785,-0.96134247878,-0.966975143454,3.3479244948,3.32459960043,3.1941020475,3.09334898982,3.2404192543,3.1142995242,3.22859538598,3.17812919269,3.25855691658,3.07010611471,3.13580455652,3.05808514899,2.89488707933,2.7937492374,2.89002562456,3.9615987815,3.87056482101,3.809199354,3.83939068563,3.57027168382,3.44732143283,3.52908565363,3.37375124907,3.36779080359,3.25206931086,3.06963679052,3.51740883309,3.71667318395,2.92526506352,2.78166329715,2.9528694053,2.3020242808,2.03635504778,2.1035507899,1.93654492895,2.31791036065,2.50457731049,2.39004840188,0.245551818308,0.138565693819,0.118169166964,0.224594800908,0.443676469217,0.549394366337,0.345314539298,0.34526314109,0.57033854587,0.465079665279,1.10075018065,0.961311597883,0.879636913094,0.94838076621,1.12033940838,1.18549588849,1.09501622667,0.972281807355,0.997258753535,1.36319042262,1.3872554937,1.47948060066,1.54668064996,1.5955487666,1.52310759607,1.43171897706,1.57180700914,1.47037079015,1.40331659579,1.74157744948,1.42099514456,1.5954623316,1.44966702144,1.5787338829,1.5868495068,1.764095608,1.84724185335,1.96942118787,2.05046679083,-0.204114864744,-0.273941677756,-0.172406711454,-0.0539859507104,0.209120337636,0.057505697431,0.0124334140353,0.344183649248,-0.125161633524,-0.114481900185,0.235886191325,-0.634984763249,-0.482836749633,-0.896890388144,-0.868229427474,-0.248080858045,-0.438321343365,-0.299836067717,-0.399016741277,-0.417255293668,-0.381619753955,-0.289716188633,-0.360499077092,-0.265551800684,-0.230830744685,2.16832507803,2.09269832138,2.18238350381,2.10041834551,1.8553678014,1.84568503036,1.70106485949,1.78082774691,1.69265480631,1.76263899132,0.662501482588,0.841026610758,0.813041231216,0.727826629286,0.684287879726,0.778234701603,1.82650746643,1.83393109797,1.74839905099,1.6486313337,1.62829305812,1.72049893638,-1.41329703558,-1.33088038055,-1.36335608048,-1.46206522929,-1.49615362828,3.67120850842,3.72741646438,3.80293718071,3.77109188192,3.03881354052,2.93263063369,2.90582064557,3.00214919523,3.09320459353,3.10734780807,2.91675967461,2.78960709741,2.83572048183,2.97891650582,3.47227592721,3.61134929496,3.65059543429,3.75798091435,3.46894033273,4.27670059623,4.36245108534,3.97831185466,3.68396630286,0.392556162182,0.521987179509,0.280727219171,0.566620828074,0.848314821636,0.522669290809,1.29949655136,1.17498995061,4.12009971434,4.06194400111,3.90873677207,3.95024039879,-1.49682914543,-1.49560448368,4.62219933178,4.64366776208,-1.02258232365,-1.08724239837,-0.935919149016,-0.976373196527,-1.25928060251,-1.39250901154,-1.38509351085,-1.290296873,-1.17482993728,-1.15224891154,2.8283741146,2.89647453159,2.82241192678,2.97603188004,2.98224589884,2.90854842477,2.63193499482,2.7993663794,2.71998611342,2.63703559891,2.71339090863,2.79796992272,2.32940082022,2.34366173201,2.25249905048,2.25865746006,2.42018938095,2.41222345059,2.57382423299,2.48983950592,2.00528678564,1.98392904182,1.99646785115,2.74282518267,2.53319550254,1.95568188737,2.54890725578,2.25744991678,2.19967512325,1.99907853722,2.37924478258,2.30027510505,2.32334293693,2.16145905626,2.12243088006,2.2031915972,1.27409373704,-0.235073072553,-0.327867871015,-0.515248522276,-0.468122938173,-0.605908971542,-0.64940230678,-0.600660956573,-0.509636962773,-0.417536094108,-0.37348318245,-0.187044899043,-0.323273383998,-0.213502822965,-0.0931954516426,-0.0222553111522,-0.25529436572,-0.0532549847771,-0.165655411613,1.26044084004,0.226376069738,0.0887284125577,0.179670858718,0.174646057199,0.0297499164949,0.0685723755223,0.216992415744,0.170859252889,0.133771905373,0.319199593897,0.36622949336,0.302707890101,0.072999761862,-0.0609589836709,0.114113916772,-0.0618693169968,0.461960281041,0.323018342973,0.530095088205,0.219985254216,0.43617727065,0.258817217995,0.870699516354,0.7773112305,0.304568496894,0.211407678612,0.314960809705,0.357314562064,0.222806153716,0.172412034474,2.11550274545,2.28561694864,2.19049143101,2.27368984305,1.52600481211,1.43147476415,1.13849736286,1.2382961182,0.985194833135,1.10988254558,1.11912142625,1.02815572703,0.806040552752,0.744850898944,1.54856780834,1.68376934533,1.40896847591,1.82722814427,2.06479521627,1.93141771012,1.81909248368,2.10367350426,1.97922633917,1.48165808494,1.38619655448,1.33581074333,1.45743588898,1.24477296269,1.26903892994,4.30335057935,4.25523034397,4.12786646999,4.16595991042,4.39541398203,4.5709384801,4.52456041265,4.4369019563,4.52928781227,4.44099680254,4.53526801337,4.58669685841,4.44011571008,4.5414471028,4.3079117133,4.13225734314,4.17601970946,4.26262696226,3.9632238708,4.02113904367,4.0024994382,4.51938870714,4.65421307095,4.56586913537,4.56104455184,4.29537255973,4.3846917296,4.2425427209,-0.846001385549,-0.828069455812,-0.696008982521,-0.606061958379,-0.754800942544,-0.645355207726,-1.30255583976,-1.17622257569,-1.18389967646,-1.26732014122,-1.39563418584,-1.36881141816,-0.932309812346,-0.88710773695,-0.882953298148,-0.789268638694,-0.749948576264,-0.745589185003,-0.793737825377,-0.653171391242,-0.611181423273,-0.659478233972,3.91146548665,3.86014595251,4.03831200082,3.99992424316,3.98401003503,3.89398744251,3.53249901266,3.42529456359,3.41277047182,3.49573976311,3.59507651468,3.6186950952,3.33641107817,3.33022825481,3.22979700711,3.12930425751,3.22715637543,3.12005021767,2.78498289014,2.78397002747,3.99723368858,4.13087415455,4.09110629769,4.07449287475,3.93695446756,3.97374676878,3.56309519573,3.53164051618,3.43912068184,3.4214823334,3.4921768025,3.19755824659,2.8599368788,2.80528687432,4.1519693153,4.02519816678,3.63916405812,2.41805327888,2.61881496767,2.44550676832,2.53711186661,2.46077346972,2.55663536556,2.67477561011,2.49214003934,2.68879346733,2.6026979731,2.14372512916,2.25753178739,2.34115340328,2.28797055852,2.08096982458,2.13729764101,2.37196272276,2.5252691959,2.64029590929,2.61007711713,2.41146213213,2.29550071453,1.20757136311,1.12813223503,1.23441516768,1.19390472134,1.05150657371,0.160812236824,0.122947907496,0.343916669682,0.517193364332,0.539219819135,0.322007195163,-0.776792527587,-0.739390956092,-0.658465845473,-0.508673237341,-0.445993749574,-0.555299741618,3.08436892962,3.09706432299,3.22774859578,3.35688994191,-1.36100957855,-1.24052862679,-1.17049379695,-1.33526516921,-1.05398803876,-1.09122775105,2.57831707496,2.66771351176,2.66275152527,2.50246617702,2.51430373453,2.59533401186,2.06439522403,2.04798237195,2.23738061663,2.16740999504,2.21267371921,2.12539083666,2.36398399323,2.26774244373,2.61766472447,2.52459169453,2.53666270378,2.33858770546,0.890058696082,1.09503588319,0.72811267792,0.79008246953,0.962658270056,1.10168636636,-0.0908045844539,-0.183573511431,-0.0571096291961,-0.287649500147,-0.475185853635,-0.407733656157,-0.457086430024,-0.366276098117,-0.495605932101,-0.43504271622,1.53340428207,1.54367322101,1.62287611085,1.62146719565,1.39165415471,1.45924921515,1.45050521205,1.06521011309,0.968592506858,0.933229531443,0.90300823799,0.986559342924,0.659486146307,0.709776880278,0.741799113273,0.87206619799,0.827834257835,0.905993446555,1.45059409001,1.74864651281,1.80850731224,1.65030863237,1.4272035249,1.32299756909,4.12193698649,4.26384920983,4.1743799457,4.6498418457,-1.50486135007,4.68801820312,4.69613748449,-1.49608571203,-1.4525170459,4.2807602833,4.27304024241,4.22626112179,4.51839148839,4.42973520785,4.37912058017,1.04285051896,1.11380674664,0.950837627717,0.939887136428,1.09186226366,1.00977131762,3.23887125029,3.27908741936,3.53769788417,3.37898002281,3.63764831285,3.53791212238,-0.148631966943,-0.137729341535,-0.257575482332,-0.336269037891,-0.216444876293,-0.306648463618,0.852349726905,0.701859872078,0.770132611647,1.25496559714,1.3486072223,1.3600626629,4.15887929314,4.2365405693,4.13413166548,4.09754147691,4.4213151257,4.43437269257,4.37018582963,0.900063330347,0.835976129884,0.871133548435,0.779264751435,0.718444142535,0.745871551818,1.28946496587,1.15746819352,1.24626681829,1.31203537719,4.25454971929,4.44226805203,4.39321636059,4.2949146728,4.3966709598,4.30654839189,4.5332125846,4.6060970197,4.6917751917,4.70420877262,4.52039093662,4.64821267195,4.566705559,1.13954856842,1.20553179148,1.18280606683,1.09242846249,1.02634901722,1.05042299454\n    \n};\n\n\nconst int NST1396 = 1396;\nconst double AREA1396[NST1396] = {\n    0.00894018181647,0.00898190985275,0.00867251788703,0.009150701645,0.0104968461728,0.00862231377865,0.00899449729583,0.00874033629379,0.00939149485975,0.0089838496196,0.00915268615624,0.00888460276935,0.00895829332014,0.00930653610979,0.00916509516134,0.00869548588248,0.0090927836731,0.00920653230867,0.00894923424682,0.00877493050388,0.00862584990874,0.00938537797909,0.00892078607148,0.00895601436726,0.00910987672998,0.00869419894767,0.00887666867214,0.00852025222886,0.008955792361,0.00875006747972,0.00948137642964,0.00886322073709,0.00883286011585,0.00888942520634,0.00876435588376,0.00920615704042,0.00889576969031,0.00906959060715,0.00911609496644,0.00909347632403,0.00906206640256,0.00893933260409,0.00903301369616,0.00900773209736,0.00910940711343,0.00901671738745,0.0090792278104,0.00897711388331,0.00899986164277,0.00899258008533,0.00904807765263,0.00906514807217,0.00936210103878,0.00885044011472,0.00923171643839,0.00886319687979,0.00923331589779,0.00860488679994,0.00912818438017,0.00874278990686,0.00913962603316,0.00900982188294,0.00906326719033,0.00838425474961,0.00922373818429,0.00909367346905,0.00923647741988,0.00896505294824,0.00894184748471,0.0091044387789,0.00902321938426,0.00894457250028,0.00909007240014,0.00899365313326,0.00911598896703,0.00895019283801,0.00873392073821,0.00892321649495,0.00846409591391,0.0103613825681,0.00888497231921,0.00905184190441,0.00888402796087,0.00864291898841,0.00869830639888,0.00865819211661,0.00865193417673,0.00902500548487,0.00906046964309,0.00844135827346,0.00897152433919,0.00913810205854,0.00906533853823,0.00894757504031,0.00924295025494,0.00913968227195,0.00924280132434,0.00913498483163,0.00890825050722,0.00890746735805,0.00914222135819,0.00910050149405,0.00920122492835,0.00909369190868,0.0088551461056,0.00907792990976,0.0093934017487,0.00902926994333,0.00898277861005,0.00897203791064,0.00894396204089,0.00882364984166,0.00911488345564,0.00900544304636,0.00992883719765,0.00917332784354,0.00923501554781,0.00884572732488,0.00909508217842,0.00897844078871,0.00903407444474,0.00916686465053,0.00899310737491,0.00856911261047,0.00853842047526,0.00910841198192,0.00890522813135,0.00904537220316,0.00925409163002,0.00889643040831,0.00853287128049,0.00903565645626,0.00905356580203,0.00894540542949,0.00933580198024,0.00909131760892,0.00883535243506,0.00889553647872,0.0091009262343,0.00929898531415,0.00914201018594,0.00908291042211,0.00901120453878,0.00898175967136,0.00893308867564,0.0089757609442,0.00909120569018,0.0086464595328,0.00850244487149,0.00883462919356,0.00883392735912,0.00917622937396,0.0090856997451,0.00895203607293,0.00945816734825,0.00891922709028,0.00872550437503,0.00922409676412,0.00888470265311,0.00915464296697,0.00912014612062,0.00861830269521,0.00892834983485,0.0103146683518,0.00900313419685,0.0090931174162,0.00914363004999,0.00893449289136,0.00870314797351,0.00906919086183,0.00898111702759,0.008938583441,0.00908333854578,0.00946669542072,0.00923173790308,0.00938930990817,0.008546643907,0.00968987737695,0.00857862220914,0.00868049310841,0.010242409869,0.00861546050394,0.00901034696304,0.00905532442441,0.00900366049615,0.00900692228145,0.00920806819106,0.00919500770707,0.0091967469328,0.00900116593844,0.00901983172682,0.00880412447364,0.0091274748248,0.0089516613288,0.00849865417715,0.00857509864337,0.00855543571949,0.00846543321635,0.00846135401465,0.0103933021656,0.0087659991676,0.00899563627628,0.00907759436512,0.00919360793825,0.00896520016618,0.00899971952551,0.0087584364514,0.00883967666979,0.00859647641738,0.00897713513349,0.00898663522149,0.00885838151696,0.00912774892885,0.0090431269295,0.00895457507364,0.00884806785326,0.00921567974039,0.00887317507527,0.00885168138432,0.00917457983906,0.00922013709616,0.00883920314793,0.00904150504916,0.00906409620498,0.00899747916134,0.00909656028954,0.00924999228537,0.00885109205036,0.0090499450975,0.00910771713553,0.00895731982346,0.00904093199704,0.00915300250915,0.00896471186739,0.00885313433618,0.00915171920589,0.00868930175211,0.00873203318811,0.00860903224494,0.0085515091821,0.0084449390776,0.00928186204632,0.00927564416792,0.00918185043943,0.0088933538498,0.00889800967479,0.0091520121121,0.00902062478499,0.00895194244022,0.00907623884454,0.0088494701927,0.0090476930798,0.00892398119483,0.00861820089181,0.00910747909348,0.00895294229834,0.00918154716686,0.00890454073325,0.00912893413227,0.00903848993385,0.00898167812993,0.00904162317997,0.00893204529694,0.0090945772763,0.00909102837277,0.00891780351454,0.00882159618047,0.00911040596927,0.00898389395732,0.00894234779399,0.00888559368932,0.00916186555271,0.00865995499348,0.00936180660448,0.0088982133177,0.00910979580298,0.010326554403,0.0088262436829,0.00893485203447,0.00901584018015,0.00859076074956,0.00897949759597,0.00896722255753,0.00883190464112,0.00921091324661,0.00870675673091,0.00895171403112,0.0087505250752,0.00962827977253,0.00865982569064,0.00874205988484,0.00859541912656,0.00919375583643,0.00887362524008,0.0104096312468,0.00854122003634,0.00847412301128,0.00856517189859,0.00857798842088,0.00928294003184,0.00881961078751,0.00903438743835,0.00903858503928,0.00915868654833,0.00885551269725,0.00885070894788,0.00871644207157,0.00931217890398,0.00893514332781,0.00908090021821,0.00898293479739,0.00896250758843,0.00897933760474,0.00907840827204,0.00877782591175,0.00847450360287,0.00904244673722,0.00902411207846,0.00895444440649,0.0085331554389,0.00879402483336,0.00920909098549,0.00844638660534,0.00895009480414,0.00894562398321,0.00907951996392,0.00897860435239,0.00904438708378,0.00931479993945,0.00896408579268,0.00932732414882,0.00852659583103,0.00918137788231,0.00874456072717,0.0102549869536,0.00889165452592,0.00876167313161,0.00864789312492,0.00864905201357,0.00922218510312,0.00865726903169,0.00937031020274,0.00845296685132,0.00917730436893,0.00908797469741,0.0091151783162,0.00888936578568,0.00885903714144,0.00886739821618,0.00876754275964,0.00887145934179,0.00924260978035,0.00869995541654,0.00948292697758,0.00852527218398,0.00847854380484,0.0093135753568,0.00885705442148,0.00906830549196,0.00902297086716,0.00899144619648,0.00910275733205,0.00999580723752,0.00874556758529,0.00915572218506,0.00855017964477,0.00931713821267,0.00856635158189,0.00864100029913,0.00857102095176,0.00882801187153,0.00927264381925,0.00867499399973,0.00890530154341,0.00915389146472,0.00879665306997,0.00918069168634,0.00887192357818,0.00894566109939,0.00913056250288,0.00891057684561,0.00901782163788,0.00899144935528,0.00900672874129,0.00887962146152,0.00919078304473,0.0088728603449,0.00861357336076,0.00914249703895,0.00900942975393,0.00891973324858,0.00910348893551,0.00886806468574,0.00889421699208,0.00912819358406,0.00888453361303,0.00920088148954,0.00910690722228,0.009014886988,0.00894843138961,0.00903455169153,0.00904903356504,0.00902761978893,0.00901231261484,0.00905003764119,0.00893699312257,0.00884785829246,0.00911005888725,0.0089494585639,0.00908723847685,0.00893404110098,0.00881837686376,0.00923040679993,0.00866077765947,0.00929655058641,0.00859092916007,0.00966811006624,0.00853259454498,0.00852686316872,0.0106380037462,0.00861011300994,0.00904747086428,0.00889436808111,0.00910297956692,0.00895178780628,0.00907152199063,0.00926309178449,0.00887024605781,0.00892598642762,0.00907959901589,0.00896049772257,0.00911204046818,0.00891096267251,0.00896865139502,0.0092381044589,0.00866877571627,0.00909423139162,0.00911392320807,0.00890440783872,0.00872750559329,0.00887417211827,0.00912685766936,0.0089206401194,0.00902882302375,0.00912395504261,0.00892598963528,0.00900840611622,0.00937696879929,0.00901734954041,0.0088806473895,0.00911297469771,0.00893906566323,0.00911236344217,0.009145497754,0.00906559500156,0.00897216764015,0.00890073267382,0.00910615055856,0.00920650548412,0.00893406888503,0.00912650882859,0.00884945993102,0.00886604663272,0.00921989961671,0.0089302583196,0.00912717873411,0.00896063423381,0.0090914172584,0.00889690750135,0.00890916438682,0.00909174100358,0.00898460947526,0.00863376133939,0.00915951729888,0.00883478342071,0.00919575499112,0.00909554576151,0.00878638036517,0.00909608937852,0.00933140080726,0.00920616128452,0.00898590163703,0.00892146110184,0.00901148464649,0.00901143344113,0.00903393016064,0.00854836217733,0.00935704640766,0.00867900167451,0.0096618340009,0.0086364114073,0.00912951566352,0.0090448089523,0.00907270252126,0.00886779744091,0.00902257050941,0.00876125234842,0.00922167359137,0.00906521002767,0.00894740693115,0.00901849867716,0.00889106007265,0.00910250437807,0.00904864921134,0.00900343225946,0.00890640372073,0.00887309657576,0.00910398605899,0.00895898772995,0.00903504433995,0.00887003606193,0.00895933784473,0.0087048768496,0.00863879473001,0.00865333487889,0.00923839220761,0.00876785580637,0.00912331869027,0.00886331669235,0.00900119292525,0.00901915971404,0.00901304045746,0.00903266766736,0.00876703310494,0.00916567316366,0.00890172300072,0.0090524922964,0.00903457604533,0.00900068574752,0.00901177571698,0.00903077807934,0.00899347143121,0.00901100684269,0.0091947236564,0.00907006640461,0.0088913578931,0.00914621105704,0.00887235843095,0.00884498085857,0.00911849500393,0.00904256600553,0.00893042693569,0.00910635255506,0.00888177999019,0.00881687952644,0.00936573506856,0.00865542284522,0.00946844272693,0.00933294460436,0.00908928369063,0.00853330005479,0.00900472604742,0.00901869878766,0.00893202601314,0.00911799106976,0.00896922599642,0.0088792812085,0.00918644795116,0.00889176450591,0.00893610287833,0.00886917938495,0.00913453701546,0.00908275981792,0.00883750903793,0.00897492905406,0.00884444936433,0.00916012555426,0.00888066014512,0.00897086237293,0.00881298878396,0.00920208602072,0.00905154110195,0.00897343446874,0.00922153888031,0.00887533514735,0.00886868176328,0.00920710283528,0.00849956215555,0.00943860426567,0.00921437232392,0.00901997447488,0.00887825458424,0.00879278375023,0.00900289949109,0.00858282853879,0.00934296962687,0.00888679610612,0.00908127978088,0.00875310175656,0.0089454757621,0.00882084678959,0.00923885597249,0.00930965945045,0.00881486869295,0.00892263236549,0.00892841857256,0.00883677900165,0.00920176670144,0.00895033859444,0.00907977806445,0.0089811861487,0.00908850154908,0.00895937946089,0.00910946609569,0.00909455220095,0.00885924250426,0.00900023100598,0.00908375217996,0.00896510956254,0.00907473403066,0.00883477732184,0.0088902227613,0.00917608770137,0.00920206233922,0.00884937391484,0.00915153081126,0.00884096630284,0.0090797192304,0.00888421084727,0.0088280315913,0.00899067367484,0.00927259187578,0.00902262091603,0.00901834198131,0.00902514356806,0.00894572474556,0.00911815832943,0.00889602951403,0.00893292985725,0.00907301452085,0.00911965142324,0.00893136199822,0.0090554414989,0.00901819898773,0.00903636478775,0.00901504625659,0.00880904160748,0.00913829929916,0.00906698816167,0.00895323058532,0.0090029766731,0.0090943363678,0.00897497890949,0.00893887523819,0.00908147291044,0.00867696404315,0.00886126473659,0.00883216487636,0.00966697217971,0.0091310108045,0.00938045464987,0.00869850519262,0.00910356951384,0.00898974552697,0.00914079921845,0.00893672530324,0.00867884274594,0.00836788381408,0.0104036231847,0.00887919305952,0.00902576108366,0.00905428882419,0.00907877321747,0.00892847976268,0.00913032640417,0.00858211687394,0.00873022721089,0.00956943213325,0.00905794422237,0.00922523945763,0.00885509835882,0.00917321991461,0.00904828136806,0.00917808003835,0.00890736441472,0.0091066942795,0.0092626718391,0.00892504161876,0.00885538878327,0.00907042141307,0.00893368392911,0.00903958570077,0.00901187469578,0.00899504739525,0.00907886899241,0.00906622442202,0.00899326094203,0.00900094200996,0.00896788574071,0.00909312747161,0.00891225629944,0.00908862686241,0.0088343018203,0.00898127508001,0.00886338009119,0.00898490985023,0.00906936590815,0.00852838516772,0.00834874392155,0.0088503389592,0.0104803712025,0.008544774087,0.00909825214352,0.009243694576,0.00911592284361,0.00891724409674,0.00892843927359,0.0091239114152,0.00890398278581,0.00904012076161,0.00899410865948,0.00903986612097,0.00898009800956,0.00914157645147,0.00889795044859,0.00891682775825,0.00887615850699,0.00914761960972,0.00900107194686,0.00875130489641,0.00889551712423,0.00916357014974,0.00886879220661,0.00852645271018,0.00929658092211,0.00909608437364,0.00856461513573,0.00896900037195,0.00861683402429,0.00963480210964,0.0087131256555,0.00889806685111,0.00904848999427,0.00854846320759,0.0104607706485,0.00843001570329,0.0101405419384,0.008995843026,0.00852734623501,0.0092327166597,0.00895339338177,0.008792981739,0.00913286770759,0.0089398904372,0.00907509107026,0.00903790520775,0.00905742609952,0.00889431644313,0.00899058231119,0.00894005299035,0.00864333337914,0.00918971170416,0.00886052587865,0.00916217319862,0.0088382986597,0.00893489759847,0.00919027703289,0.00929555415277,0.00907397592584,0.00895347447769,0.00910433231314,0.00917807084844,0.00899775367337,0.00910593661867,0.0089002207995,0.00912395739721,0.00880335053144,0.0089296497892,0.00909198833163,0.00896933693168,0.00857540518964,0.00855976223624,0.0100605501726,0.0089617974054,0.0092102530412,0.00860294228807,0.00903586678252,0.0088981299645,0.00909421336342,0.0089664023618,0.008899375286,0.00916051114098,0.00899754333456,0.00903959276469,0.00900116398003,0.00897577095174,0.00902170785629,0.00902071587287,0.008960724269,0.00924554101257,0.00884124636382,0.00911891668284,0.00908968578628,0.00884409251623,0.00903315745122,0.00901164690061,0.00906762066073,0.00896457027809,0.00899628292686,0.00890802113874,0.00914761318856,0.00887202154472,0.0089727831834,0.00913332381603,0.00886685580454,0.00858066442055,0.00872073207009,0.00971542345045,0.00899767558098,0.00858030929888,0.00899941516085,0.00901028434336,0.00900466789579,0.00910010084498,0.00896006290051,0.00908230463959,0.00890903472828,0.00893067567034,0.00904259432722,0.0087330003146,0.00887413042866,0.00916514143747,0.00895583518321,0.00886564515787,0.00910203546982,0.0105688091132,0.00871356532967,0.0084012974985,0.00846298385192,0.00853462360854,0.0089126789934,0.00934849590144,0.0087960330396,0.00993262921113,0.00908705405867,0.00917858278529,0.00907052966149,0.0089105188785,0.00910810886799,0.0088840361766,0.00903403659287,0.0091003034802,0.00938399186347,0.00852269852588,0.0089627709833,0.00910620218682,0.00897471095941,0.00907021277555,0.00903572456593,0.0090057202437,0.00862822863877,0.00924788759998,0.008844844594,0.00897158341394,0.00903837592839,0.00888670720097,0.00915719347738,0.00922044553686,0.0088695049298,0.008828004086,0.00902009928353,0.00899840072723,0.00881591198662,0.00873696514728,0.0095373059534,0.00942797739806,0.00845600026283,0.00922912416849,0.00898131522893,0.00908251552124,0.00904163896841,0.00921143157743,0.00925706670599,0.00883705641713,0.00887338138677,0.00908679314236,0.0089897711341,0.00915544380238,0.00849730376389,0.00898046841346,0.00892338029721,0.00897152075476,0.00906265781476,0.00899282273748,0.00910480877681,0.00901805404251,0.00902639325784,0.00916410588253,0.00886568325064,0.009192796137,0.00888996627894,0.0088518639191,0.00919472937365,0.00888850830246,0.00895715999668,0.00918337409883,0.00886180687763,0.00923321081932,0.00893438398044,0.00913716374285,0.00889354134261,0.00905013654332,0.00912153637417,0.00899732923084,0.00867411119051,0.00925159592404,0.00879644784955,0.00898981304937,0.00887351938405,0.00910103120683,0.00919327189621,0.00902652440718,0.00904799432679,0.00892572406353,0.00903596138546,0.00907598075089,0.0089405395136,0.00901833365199,0.0102184192327,0.00858107170212,0.0086125248602,0.00940958470017,0.00918692732057,0.00898776086166,0.00901407361258,0.00897820075319,0.00905697552937,0.00891737014289,0.00909447665624,0.00881058204675,0.00883542265152,0.00852058854428,0.00940717317487,0.00923487891464,0.00872825147537,0.00860954094543,0.00866552253333,0.00955520186769,0.00856663278833,0.00935261259995,0.0102169140167,0.00876812778031,0.00896787549342,0.00904054327342,0.00904945216228,0.0089708818813,0.00909676745096,0.00893536786323,0.00887014574465,0.00920215188397,0.00919016932364,0.00886802410618,0.00886468069782,0.00921230040136,0.00915118339741,0.00895680659144,0.00903489351315,0.00903236423349,0.00917329524331,0.00874540013004,0.00884348443799,0.00885425896553,0.00916276124673,0.00873247369693,0.00895506347131,0.00880673404411,0.00907524124699,0.00901838789217,0.00907591497611,0.0089373909606,0.00910324788563,0.00892826773925,0.00892640715679,0.00889061468806,0.00913477412839,0.00888684067793,0.00917681442322,0.00894455669523,0.00899719330522,0.00903885037698,0.00859922677961,0.00857600465644,0.00862068360965,0.00898445236475,0.00905564266427,0.0090511403714,0.00902034059518,0.00901476780781,0.00903138000939,0.00879850840515,0.00924061963319,0.00907003062161,0.00896463979285,0.00870136537821,0.00897417561834,0.00861152570434,0.00958126296034,0.00856077898198,0.00938470255899,0.0086398975747,0.00872183486491,0.00865401094255,0.0102479755816,0.00859363762692,0.00854220945077,0.00934320329221,0.00871975606452,0.00893102261486,0.00927505409973,0.00877254033705,0.00894872821193,0.00904042091936,0.00892590383732,0.00905145213539,0.00895952872607,0.00908700772311,0.00888858024836,0.00921086466006,0.00899641647695,0.00906011054819,0.00898359017056,0.00924316697861,0.00914292339813,0.00883546107972,0.00890261715439,0.00897383005307,0.00915764172486,0.00892317342303,0.0084730654558,0.00912584947717,0.00894988970348,0.00911191094418,0.0088789041061,0.00897408526097,0.00908518297596,0.00918346105277,0.00905307617826,0.00907423108156,0.00896445363753,0.00907970386648,0.00892725870517,0.00904263108159,0.00895820003099,0.00906013697215,0.00911567077139,0.00894521703067,0.00900583766655,0.00945568368044,0.00936684688943,0.00907130428547,0.00895276957783,0.00933060082481,0.00877944042447,0.00915464969972,0.00895184195671,0.00888828026922,0.00914879102669,0.00908202605497,0.00898557786498,0.008914144751,0.00905208749239,0.009202135267,0.00910687347817,0.00895136095163,0.00901265345921,0.00907887062723,0.00900985822215,0.0090339570374,0.00926009947363,0.00883191930358,0.00914835101179,0.0089091576992,0.00854948616812,0.0109401245338,0.00832933739791,0.00919153999975,0.00878181885923,0.00850622105441,0.00908939709927,0.0090763228982,0.00910114473296,0.00895350638197,0.00903437852627,0.00884158637085,0.00888476912549,0.00919439155627,0.00905030175965,0.00894109095645,0.00910978172418,0.00886225939468,0.00901904334128,0.00905711661309,0.00897747653028,0.0090865411277,0.00907830834102,0.00892716755268,0.00904861381954,0.00899496181915,0.0089467281271,0.00947085361322,0.00872398352009,0.00921557631538,0.00892776638939,0.00905532688681,0.0089946020228,0.00892874181343,0.00895842333487,0.0090682627208,0.00942886888207,0.00920010852972,0.00880300601184,0.00866104332946,0.00853915908583,0.00854568168341,0.00907663285931,0.00934396165592,0.00899790118127,0.00903775812849,0.00865220355307,0.00939328880022,0.00957798500423,0.00931510100628,0.00923901233422,0.00892901011213,0.00852208519392,0.00866190742073,0.00883910913749,0.00890171446556,0.00925846606871,0.00872510548202,0.00917068732535,0.00880877782075,0.00916004297926,0.00893017154199,0.00909604719947,0.00898680987943,0.00907355113965,0.00891384504587,0.00889806284352,0.00891718343143,0.00914024997416,0.00890564853969,0.00911442877582,0.00906073454012,0.00871746783539,0.00922201276553,0.00880860247777,0.00896823858408,0.00921699545688,0.00878498652764,0.00920664379133,0.00888397225933,0.00917365226354,0.00886637332513,0.0105850019097,0.00915571809538,0.00918693044839,0.00885136483407,0.00920569449598,0.00886810059725,0.00922005001684,0.00884722423895,0.00884927578896,0.00914592962854,0.00897920182419,0.00922213545553,0.00889553310012,0.00938073945554,0.00874073222963,0.00887673174284,0.00913760698674,0.00882417471715,0.00899902697849,0.00888800217924,0.0090057168279,0.00903341039795,0.00914534756758,0.0088503065463,0.00858284445304,0.00852809742158,0.00916199200116,0.0087339267331,0.00881912657477,0.00888245571841,0.0089203605108,0.00898412714121,0.00886221458677,0.00894631419341,0.00910828424503,0.0088539184165,0.00913791555617,0.00918713202868,0.00886618322291,0.0088638578925,0.00916049244956,0.00889153446659,0.00903564986508,0.00893362206335,0.00904193189535,0.00909506578262,0.00893296999519,0.00903650043163,0.00890041309231,0.00910208391355,0.00888104065095,0.0091137832464,0.00896947135161,0.00903182828178,0.00898049097894,0.00910641336189,0.00893752496502,0.0090559270263,0.00888391598795,0.00906646093018,0.00871034648755,0.00948035560301,0.00856324980928,0.00916592624877,0.00885039836419,0.00889491862813,0.00897426801243,0.00930070552191,0.00887397864144,0.00896400955982,0.00900227334467,0.00909350337648,0.00885108184111,0.00916754434313,0.00896316534882,0.00893587430783,0.00911616394232,0.00894142405787,0.00896144786923,0.00907332423459,0.00882752835803,0.00870861314385,0.00929179200246,0.00893037632197,0.00912700993507,0.00892815816821,0.00908505470801,0.00902025902801,0.00903176872796,0.00891646552116,0.00912286663881,0.00899962241501,0.0090306035759,0.00892925771921,0.00910055044847,0.00923833045553,0.00872030142622,0.00888322112265,0.0088331212948,0.00900107971207,0.0089156220887,0.00915747268537,0.0089806161344,0.00908103079945,0.0088575189919,0.00886531290922,0.00921394171299,0.00873645883324,0.00918657394258,0.00869420437839,0.00907485201513,0.00890806637751,0.00912793376191,0.00894908766824,0.00892178200665,0.00912471132782,0.00915749743799,0.00894805190845,0.00893474522116,0.00912098268082,0.00920039592953,0.00903077909718,0.00898271650765,0.00879400238974,0.00921152690955,0.00905723241308,0.00896491718322,0.00899054302933,0.00899137960872,0.00902689982014,0.00897316718943,0.0090862671005,0.00883863399618,0.00889905648878,0.0103763388321,0.00865956340821,0.0091202987565,0.00843471517361,0.00927356159696,0.00927001375776,0.00885104717356,0.0091725388632,0.00903061643432,0.00844426029877,0.00864240793322,0.00877470617456,0.00881042817387,0.0084933460231,0.00884975869938,0.00881669976165,0.00927554289878,0.00874857513283,0.00857170777348,0.00929247298069,0.00843062922178,0.0091518637133,0.0102869070985,0.00848574020327,0.00898302702422,0.00907945368132,0.00904755881109,0.0089554294609,0.00889009552039,0.0092244370426,0.00863153996657,0.00893008851074,0.00884064318576,0.00907081826636,0.0089963150163,0.00897566957247,0.00905032984022,0.0090061984542,0.00917805493017,0.0088607007856,0.00889464751664,0.00890381189022,0.00916224408852,0.00911344845199,0.00893353087706,0.00914037355771,0.00892064616556,0.00891594656715,0.0090450337702,0.00902635345456,0.00897103443681,0.00906963103634,0.00903160721961,0.00895086066797,0.009076979252,0.00899963035239,0.00889575716863,0.00878761570063,0.00924140908744,0.00888730254577,0.00913625769046,0.00879610998338,0.00886676695124,0.00914228018205,0.00916286997831,0.00889951350434,0.00885930268739,0.00915711078878,0.00914473444643,0.00893859470725,0.00910310565832,0.00892642544568,0.00898928572747,0.00905796669962,0.00911906103182,0.00891526496139,0.00894575997317,0.00902863036575,0.00905848106889,0.00898542232433,0.00873336683637,0.00856125241584,0.00878497140584,0.00896281204806,0.0089683309849,0.00909888884802,0.00879410353345,0.00925073892448,0.00897507533311,0.00891207387156,0.00919114740204,0.00838938093151,0.00895245791394,0.00894274802292,0.00912208030304,0.00892191406565,0.00911262354204,0.00914949357814,0.00893833333641,0.00890684835808,0.0091388648925,0.00952877065142,0.00850974987783,0.00947733775239,0.00856780409863,0.0087257273796,0.0104880862071,0.00862791466217\n    \n};\n\nconst double PHI1396[NST1396] = {\n    1.54276431814,1.47170298581,1.50081248944,0.88698383876,1.48811893215,1.41653240579,1.0230608617,1.25861174738,1.18539335127,1.07448140644,1.15601920404,1.21876724693,1.28597807986,0.783341404612,0.742175392397,2.05647857789,1.5386818973,1.32598996496,1.46676618981,2.73830673409,2.81987772425,2.819628769,2.364645473,2.5792763134,2.49939194063,2.20827804719,2.36824652105,1.3392079103,1.17692571543,1.23219275483,1.31905078882,0.827649129355,1.16845947905,1.21324620217,1.28422571953,1.20919115423,0.367852558919,0.370500023155,0.305036241477,0.215523075555,0.298932987033,0.33614505113,0.389512306443,0.385209393642,0.313968755009,0.233979399417,0.43915662464,0.312528018977,0.238425504552,0.352350309349,0.322506160665,1.10024791744,0.866405637283,1.48593896363,1.51775038424,1.58211291221,1.564595605,1.40722098711,1.46053984908,1.32693582778,1.29640318659,1.10117900644,1.18360005916,1.56006494502,1.54177606551,1.79252174858,1.63700303483,1.86865629898,1.87465995076,1.76270014173,1.72705033642,1.68129353129,1.66343042293,1.77760868399,1.86700584568,1.85642504431,1.61264741456,2.47499249757,0.807423458904,0.902536862239,0.932520225568,0.75719172188,1.09746702924,1.17954728062,0.989973881407,1.04019450576,1.12011114478,0.914201269793,0.834984865833,0.952938872309,1.06090113194,1.04785765501,0.982550178916,0.975321665219,1.4585620691,1.29966612545,1.54445570323,1.47237851461,1.54180976557,1.36301073915,1.35722549785,1.29598465155,1.87618570176,1.63864036727,1.79689095153,1.09465581982,0.214075610716,0.2314649685,0.181371347567,0.226225996134,0.167692271483,0.0974868195866,0.083199487977,0.207505842408,0.532657941291,0.757425401918,0.75667866148,0.753352741729,0.89929946033,0.827298481248,1.27918136643,0.993794559298,0.623240188193,0.640561408204,0.717781643248,0.718925472117,0.685669471839,0.776643340363,0.622030250638,0.824879150124,2.13619383201,2.38368088706,2.46311921752,1.60858931609,1.74443654819,1.39754071622,1.32854327922,1.3243118618,1.3955866529,1.25329051412,1.25101827989,1.61402363852,1.54092022054,1.47094644801,1.61536009498,1.47232092464,1.54362630805,1.8089226082,1.88915902871,1.81131037707,2.05518264033,1.97569940901,2.59346618849,2.66996615598,2.21561185441,2.21895888762,2.29529688375,2.29538670771,1.89490357739,1.97460988716,2.14718029293,2.14366418137,2.07004828783,2.00704437624,2.13277981521,2.21127119536,2.09889831625,2.0633823911,2.34042233891,1.87128062243,1.82891882565,1.99171840762,1.95353474807,1.74273969449,1.56861158235,2.73550670345,2.7816321829,2.89116334158,2.85957098554,2.98763156022,2.90955329715,2.86764600486,2.28869310238,2.36520628607,2.61852481815,1.17268182029,1.21229905412,0.744625224877,0.517719087503,0.676249092819,0.690745870973,0.704215297668,0.646122898522,0.570037860227,0.792182638766,0.742386254176,0.652217327177,0.699013733554,0.867754081377,0.76266294562,0.846387846263,0.467296979105,0.421099462776,0.726995294758,0.358196029233,0.439068260677,0.441408974535,0.415140185693,0.33478890765,0.479795676796,0.586670957605,0.613397291656,0.566507332392,0.509741799697,0.448500245944,1.0527310898,1.09065934558,1.05459503043,1.53263713557,1.61477158795,1.46444049019,1.476631826,1.38527997026,1.46580830857,1.52256408902,1.49757941108,1.42382991615,1.43821848353,1.07095754395,1.02726551255,1.12816656187,1.11055671267,2.0121146118,1.70662027981,1.62559266268,1.6035942036,1.82548725488,1.80257903415,1.87030130614,1.94046031793,1.91173576997,1.79699941644,1.7718669827,1.01011841579,1.13048517337,1.0284823355,1.10768890996,1.07404201761,0.968870933419,0.991836538674,1.70053419336,1.77817597726,1.72879217636,1.58660947706,1.65098575668,2.19622079825,2.1555161696,2.09524725397,2.11527176784,2.26102804118,2.26020461681,2.30303766939,2.43738430453,2.30265365039,1.61412155944,1.61248575838,1.82346852201,1.82858411173,1.75786858964,1.68410442513,1.74961093674,1.68090239983,1.96913819211,1.9017941544,2.04312211021,2.26887763352,1.88108510654,1.98769813494,2.19378564221,2.06813109978,2.13498809778,1.839452973,1.81745508697,2.40078185512,2.34905175224,2.2730676425,0.796834101058,0.887768516496,0.917086200399,0.876111706027,1.12034004049,1.04288873735,0.681740428938,1.04729676415,1.10069590654,1.0378815113,1.18532884719,1.05204216958,1.10883505997,1.08071994056,1.39586191466,1.41626858205,1.36061177638,1.31804750288,1.47967102916,1.35906905269,1.49610198344,1.44006491213,1.65612032334,1.73574275008,1.43958444984,1.50839203509,1.58906290696,1.60048093408,1.42454651852,1.56236630304,1.49910526955,1.2176870147,1.22194741117,1.66097547105,1.69574040017,1.77426572882,1.31964848954,1.16769004152,0.80198691676,0.849750653408,1.03024295146,0.456257601629,0.362636890993,0.41347657869,0.550101014774,0.604074765647,0.466487097457,0.446065283828,0.582905502141,0.510206222146,0.356907573087,0.336987293373,0.290424618267,0.0912714820457,0.172112653651,0.101463913897,0.17993801974,0.449450600697,0.286603363375,0.352780084147,0.427750777342,0.93357639462,0.869963468282,0.964912377319,0.981766404617,0.919622712818,0.846374412202,0.882019634971,0.825557573242,0.796888841036,0.868961404418,0.945714802862,0.957039406382,0.819923909185,0.894063122341,0.714672886787,0.720316017454,0.794526240866,0.863284365359,0.805231624677,0.86796745522,0.576226146422,0.662251449307,0.68476140083,0.82990003296,0.843183378747,0.917027774225,0.898713442598,0.974021839511,0.979462752376,1.44938480369,1.34811628344,1.28402716513,1.36057829967,1.36997523337,0.852910338032,0.69810009338,1.20652869822,1.20982457812,1.14089932445,1.06458688895,0.48058408991,0.556711129025,0.594216341617,0.671409617272,0.542457138357,0.585028917628,0.68045626654,0.716024570329,0.89084664643,0.699006922401,0.856626011719,0.986019351977,1.09975072826,1.06558159861,1.08848173171,1.19336467023,1.11488298268,1.24370155458,1.3941432284,1.46023772165,0.853923508421,0.936491945074,0.843080261591,0.804139202867,2.1679309063,2.11424605204,2.10442040497,1.89312556688,1.88358533051,1.96575854652,2.03012477651,2.04918875202,2.00950301567,1.93566035303,2.11523343878,2.03241526122,2.28139428183,2.36307272122,2.41924013477,1.89054589788,1.97510874573,2.5352875329,2.47598993367,2.50037942174,2.55301549692,2.63433973777,2.52415854775,1.95292956352,1.82587700482,1.86908179727,1.60566768225,1.67279926157,1.86835823586,1.93309906112,1.92761604531,1.97149913627,1.98321337705,1.8641290635,1.9460547523,1.93905915187,1.70484584008,2.35573892617,2.73903907686,2.66991619083,2.20744507328,2.17335467035,2.35860854996,2.26020062693,2.27804296511,2.44346082072,2.43953128829,2.47819861273,2.50187850491,2.57730897223,2.52171057477,2.7275203724,1.86359856842,1.78302313247,2.15331942783,2.17808120983,2.11725443462,2.03679648625,2.0890266634,1.96627453356,2.05090521247,2.80108048053,2.65099056442,2.73407571674,2.78575726545,2.61079153471,2.52250873793,2.4749845506,2.47724251066,2.38999361716,2.50695794565,2.51825004071,2.55816805641,2.51032772229,2.4315864478,3.05982019743,3.007974161,2.99944488821,2.92303037069,2.88904173357,2.6136062104,2.62632583396,2.68155539657,2.76293365192,2.63877193013,2.66996792859,2.20009855335,2.21559493203,2.25387716749,2.12745513144,2.11275191768,2.07811394826,1.86748198736,1.82697154459,1.82358029881,1.99929559038,1.95762406259,1.99955925589,2.08685485035,1.48278305852,1.32155925266,1.69871079731,1.58133824511,1.61233734836,1.74227386858,1.69907700891,1.5667840249,1.61153098511,2.41495581257,2.38068966576,2.49622757792,2.41795129837,2.6506826471,2.59914419183,2.51984233283,2.49191642064,2.6155124844,2.53664869985,2.55361645226,2.41574932474,2.55713803185,2.48551508898,0.581058084423,0.501979597245,0.495926842261,0.473268823113,0.550885053563,0.462207548784,0.534558724634,0.614254750748,0.60863706672,1.15357600752,1.01294833266,1.0770969782,0.929099980165,0.919910693027,0.738732520541,0.823151612281,0.996793126109,0.947718068674,0.867095136751,0.83291674777,0.60460970864,0.552483668543,0.593040519693,0.688755654345,0.723682137677,0.679283199955,0.469712601738,0.509097344065,0.445137282103,0.554809571128,0.836471131104,0.994087462561,0.89113180397,0.967526935665,0.694980051431,0.653633661495,0.734113467836,0.753664500236,0.587672113909,0.60984107102,0.972605010339,0.808102138424,0.938555237841,0.856526025373,1.45140309146,1.37604424369,1.56036550377,1.71176696862,1.63039368398,1.3472338576,1.32871815959,1.20820726518,1.28281583863,1.19128419569,1.24747256804,1.29307447945,1.30945318625,1.38319129291,1.37176174478,1.22334511401,1.21845081226,1.95052958335,1.82616879176,1.86909397031,1.80832991207,2.05176420113,2.06702595345,2.0009562551,1.92210272763,1.90665054965,1.97039738595,1.57093434682,1.59817481638,1.73566871176,1.67990748366,1.43380621943,1.4879123822,1.35015086182,1.68703592784,1.63173068895,1.72650359469,1.66450506461,1.58681387571,1.71371116243,1.4508980642,1.53106508338,2.1308877369,2.18231299048,2.04656121023,2.29195500784,2.37065211071,2.39667731486,2.3384601884,2.2583422418,2.23731672977,1.94211849535,2.01048245789,1.94521620697,2.01502303566,2.21187610877,2.14598796848,2.29098605696,2.3008023347,1.68112540713,1.68364192184,1.68786046636,1.68747581845,1.76030093372,2.04863763027,2.04850945284,2.12377432065,2.12321176763,1.80786568881,1.99254418956,1.90992373065,1.88114529208,1.76715254105,1.84831186719,1.90554441008,2.18543441525,2.24946965644,2.15472136614,2.23302417242,2.02738735984,1.8753027098,1.93696865166,2.0074132457,1.91747338431,1.93243319767,2.00822008937,2.29325997489,2.35775189667,2.23973811839,2.15454042089,2.10800207908,0.762018809859,0.81212772441,0.789676197687,0.712390069172,1.31248314504,1.18416706826,1.25712950354,1.32206065487,1.02722386424,1.00143542611,0.945602742076,0.932682896404,0.540989887151,0.472793775846,0.494637566865,1.33197253564,1.40197663324,1.33135572526,1.40172958251,1.19297634837,1.20486978113,1.12865245164,1.12282704272,1.11325872219,1.1377697912,1.17521027399,1.25618996587,1.22174761231,1.27845048072,1.43340239254,1.45320497572,1.52969361858,1.56357998505,1.5218848168,1.15476669067,1.02466170848,1.16528113094,1.10349127639,1.22216914152,1.18220496897,1.22678572568,1.05522025579,0.927820844627,0.971793330896,1.44007179313,1.48652819124,0.818933779275,0.773708887121,0.713183834013,0.736026104577,1.0433406053,0.21057172147,0.294234666528,0.326547410831,0.314192898111,0.441542255574,0.399945066971,0.500486136316,0.430008492732,0.565031507972,0.557068786909,0.48118726077,0.418794048663,0.242670893882,0.324068801821,0.308521328559,0.240894466898,0.385996016154,0.429276723029,0.406595039758,0.738991141895,0.571280734827,0.937121719344,0.94057353743,1.01323627845,1.08163472827,0.425292584586,0.413779558893,0.631044396335,0.618919275298,0.567126343667,0.485440880416,0.547993565991,0.478664259493,1.42772625099,1.42007008074,1.48653787351,1.48880587154,1.57462857594,1.56559768938,1.31739418125,1.30397911325,1.29239811962,1.42475156423,1.35052100755,1.28483039314,1.1495628166,1.08071513526,1.09385784054,1.22590050201,1.23641610322,1.17298553634,0.711429088081,0.665271078683,0.642201443124,0.791351926707,0.810281432736,0.752259940768,0.845231382128,0.847214977971,0.768123265135,0.769993649255,0.917544198805,0.917693524071,0.879757513951,0.95812716356,1.01152513901,0.855968041585,0.989177237147,0.912181985117,1.35132283495,1.50549644193,1.45053816019,1.37358058092,1.40982019234,1.48693945731,1.14257955138,1.21912958715,1.27486103631,1.25651285755,2.34363390596,2.27178451464,2.19727495475,2.19311062936,2.38880019656,2.43601678727,2.39624817722,1.72177892752,1.64769920385,1.83589613336,1.79936413711,1.86073021883,2.32112201542,2.30143861452,2.37970662534,2.24533733085,2.39969903484,2.33689145051,2.26112312834,1.85116072604,1.79080335965,1.53198818385,1.39377364477,1.46051830983,1.53473515379,1.39624365769,1.46573060831,1.72446165866,1.79241133028,1.64886123632,1.79879392212,1.9241963641,2.05463363825,2.0034545349,2.01626083809,1.92294309488,1.88181702556,1.85221970521,2.0948885378,2.0952080812,2.05395675646,1.82263143411,1.8605977384,1.81619433277,1.66911176862,1.59149755172,2.52382164863,2.44833900613,2.37768310764,2.3768200761,2.52841284344,2.47546667194,2.77222100064,2.7190163528,2.63557063066,2.60744494505,2.65516199392,2.73553284166,2.51494763057,2.65903298612,2.58672084133,2.56386866746,2.29203136473,2.32035173727,2.59544956095,2.51086766853,2.36973164643,2.45222024379,2.4748434938,2.80160846657,2.63419775583,2.71151922761,2.76212519025,2.60133655582,2.71720678669,2.63562181464,2.93123273453,1.93401720565,1.9229434976,2.07374915916,2.00713565577,2.0669687065,1.98931094612,1.75102220405,1.83080195059,1.84419816346,1.77461446884,1.69301454402,1.68272918998,2.04024522156,1.93400170151,2.01483217373,2.07065512026,1.82697147807,1.9073156392,1.95785980713,1.9217959553,1.83660168402,1.79212666766,2.74733088305,2.61852422607,2.60496599027,2.66332038653,2.89199116628,2.962116873,3.02801188181,2.99428378268,2.91219486992,2.86842304217,2.64755870383,2.59037235241,2.61138875569,2.73712125039,2.13101845085,2.08516170466,2.12020396882,2.20333538615,2.54876451961,2.54519927635,2.47441163697,2.92529491563,2.89221835898,2.80575317536,2.75420250751,1.73939706065,2.41274493803,2.47908669764,2.33769525387,2.32651236133,2.3869796713,2.46419362443,0.643589715691,0.722617567985,0.708707705068,0.639331299902,0.788781712508,0.77931948569,1.06796558827,1.1322704908,1.13202334352,0.986509876299,1.06007074251,0.991092218519,0.954840442501,0.970979184995,1.01089122098,1.03066251278,0.929142566642,0.865815781883,0.888197159229,0.943768899408,0.862647587922,0.975127109129,0.930886330195,0.811558351355,0.848672472279,1.31730364861,1.25003539616,1.40320195193,1.41298831398,1.27207056978,1.34891369337,1.57378941131,1.51122354599,1.49977487089,1.59186312095,1.62512712535,1.73699428356,1.76202638937,1.70800960821,1.65669550617,1.59872401133,1.51742044029,1.46068340814,1.16669037659,1.57090261854,1.54392433978,1.62356699606,1.63588344382,1.38478378205,1.24290048273,1.30649358439,2.01770496476,1.93394855678,1.93475326666,2.23172055335,2.23081128358,2.15762112282,2.08714651644,2.08569003927,2.15521426257,1.90355351774,1.83286547879,1.97681813828,1.97634940936,1.53350509362,1.55220075247,1.74569513325,1.58051420694,1.6323496858,1.59951001609,1.67163313893,1.85907532951,1.71815588363,1.76816002556,1.7465819794,1.77064947159,1.79976213909,1.74300932132,1.66063731324,1.63411890293,1.68761254274,2.10958348176,2.0431431543,2.01806863137,2.0951147681,1.97957016714,1.87254203735,1.84460131778,1.89807498277,1.97724874644,2.0079156898,1.9541496956,2.27728197011,2.39312603632,2.361438962,2.33712505997,0.683260151161,0.555323134435,0.636412118214,0.521550528435,0.575481807031,0.655728225877,0.621341698783,0.688449344786,0.767592213933,0.783678681079,1.11976086138,1.26145711104,1.1902543652,1.26111202677,1.19101291646,1.12020986477,1.26485257613,1.40411397461,1.33342797063,1.40876899808,1.34410990165,1.27091909852,1.37225746914,1.38296976659,1.3109456028,1.2359362607,1.22784470439,1.29221590565,1.43852639512,1.47977595109,1.35443555114,1.30977413275,1.48138310675,1.56803396914,1.44645728527,1.11868057075,1.27123751511,1.25550959641,1.17875875888,0.873143856348,0.850600685242,0.908771011211,0.984699503169,0.152927589325,0.0693718009196,0.0656068749438,0.148638172999,0.710291092296,0.683078681475,0.653900105126,0.573384305793,0.530149724777,0.58529910159,1.00585054486,1.00977638309,1.14654298612,1.07764232849,1.08547422027,1.15054811596,1.5172265504,1.50489363955,1.37535515001,1.36574875233,1.4324545267,1.50550408026,1.51511486352,1.45164462843,1.21662207904,1.22726109669,1.18044710915,1.16453106941,1.04584925862,1.12212386441,2.14214473962,2.18213482976,2.26934086859,2.31306423359,2.25443383814,2.30623522633,2.26962526749,2.18644897987,2.13870334242,2.17055506791,1.63811667375,1.54213106271,1.55325078381,1.62906151866,1.71347770735,1.76081721931,1.99736510619,1.93764801337,1.59393055046,1.65861451898,1.67183680128,1.60197087057,1.74582216816,1.74057721852,1.69109146745,1.73814892008,2.46733669028,2.42491806439,1.80918405107,1.84635436279,1.7262298413,1.68314500736,1.79967527782,1.71959275743,2.57770640529,2.59251096523,2.52324175043,2.44672372837,2.43529785765,2.49725622677,2.5061092985,2.42503858837,2.40441128238,2.46252810967,2.75547606675,2.82863219595,2.54909707138,2.60232982714,2.68351216454,2.69313073863,2.61722626799,2.55540554238,2.6028466521,2.54799652851,2.57155997238,2.65225819684,2.34866292481,2.17919199097,2.85206502295,2.81297908619,2.73247207566,2.79537832548,2.71553689233,2.68811545104,2.86677980485,2.88148729022,2.84157849473,2.94300077257,2.76032270512,2.84801530882,2.84879295601,2.69072184337,2.69480723531,2.76573800393,2.25574125142,2.33983611931,2.26244829684,2.21812549742,2.39014086037,2.34973721528,1.98059465128,1.9162953124,2.2314266896,2.84347316416,2.84980227834,2.77103055954,2.69904566586,2.69466724309,2.76086752056,1.57275279614,1.61310614291,1.69799042349,1.6175413651,1.7408654681,1.70059749726,1.69639399513,1.74024774412,1.69770936439,1.6100913718,1.60995867964,1.56601333019,1.66180049247,1.79947261056,1.72312151591,1.65511224579,1.80408027922,1.73438237812,1.24645494212,1.30617289915,1.39868218833,1.42876108142,1.49232693786,1.47759297248,2.14515284912,2.06544359966,2.18152541712,2.13113478552,2.04633346359,2.0164371073,1.75932859828,1.83221515404,1.75708519044,1.90339017526,1.81803053481,1.88066612831,1.95674449705,1.96752867411,1.8994341766,1.82607288042,2.0557831429,2.07187036086,2.14902042167,2.21397537289,0.640722190308,0.61409931288,0.581347275498,0.723427102516,0.700090199402,0.750632541924,1.3500628929,1.30689016329,1.35014956747,1.43586503817,1.47909643781,1.4363585827,1.35340140336,1.30822322795,0.933326968565,0.970666306975,1.21270145606,1.13702480153,1.49345749607,1.61431760252,1.57816919949,0.204261793645,0.340470570151,0.278979889289,0.200467445939,0.286956248887,0.34522050628,1.64995858319,1.57050135819,1.71775177326,1.7039187905,1.55974271411,1.62546673951,1.14125912278,0.992358973706,1.06289143635,1.13598250975,1.02738931655,1.08783935622,0.949503152002,1.0751476791,0.934068585475,0.999337294425,1.66517175733,1.5979543019,1.61208439915,1.95547141671,1.97660765126,2.2420500222,2.22260463779,2.14064340544,2.07982100661,1.81138978929,1.89049383971,1.89909684258,1.7449133956,1.78740991335,1.8477114083,1.70873802353,1.69376533388,1.83450894136,1.75948516349,2.43316765879,2.47730605151,2.42979533572,2.34378620016,2.31367329768,2.30552080413,2.23103717731,2.17917940814,2.21943765455,3.00381772289,3.11787722572,3.08095815382,2.96091161812,3.03128854904,2.97260642241,1.9842599864,2.06552206636,1.9256032303,1.95045697894,2.40578984309,2.33212477733,2.26377342121,2.26424942697,2.17532890197,2.09280258675,2.03651663935,2.19119701603,2.05225760032,2.12418120307,2.46904366064,2.45102273599,2.40377475481,2.32967518498,2.36936679173,2.31285706233,1.26934921332,1.23835860772,1.21214993938,1.32152017631,1.37812529804,1.35195978563,1.40933592696,1.5260574994,1.4466671804,1.38627469682,1.54854286495,1.49208752216,1.33448338243,1.25706233285,1.28759648776,1.34990711216,2.11454569111,2.14200122206,2.0896724746,2.19530962974,2.25446407344,2.22559482889,1.36007311794,1.31328195572,1.32262121563,1.22820349712,1.23954499379,1.19167999142,1.182764707,1.09903623809,1.17979185801,1.22290286868,1.09555581385,1.05467517443,1.06246242082,1.10995424017,0.981056816712,0.951408287599,1.00506225842,1.08188272358,1.45525261765,1.34623759079,1.37098486331,2.17828322907,2.12218622184,2.18964665072,2.04613631852,2.03355485706,2.09821836699,2.3055787462,2.3479834458,2.30539082353,2.22018488044,2.17956184249,2.22289121739,1.11848333285,1.19434107817,1.05642131622,1.2095667697,1.07109589149,1.14821058271,1.56252596311,1.48826524017,1.59634729858,1.55503572928,1.46988094408,1.40823711843,1.41933948797\n    \n};\nconst double THETA1396[NST1396] = {\n    3.04793725109,3.09698465332,4.70279172318,-0.988787469234,-1.50608554965,-1.48286888729,2.66103924185,2.54476362269,2.5918613384,3.51324953644,3.54391026961,3.48723952804,-0.184764322671,2.8673967804,1.15632464419,2.38800401674,2.71159462345,2.58914004738,2.67217035376,-1.4813810341,4.48762574443,-1.55116994264,-0.815682168158,-1.41002169467,-1.38658839183,-0.883787361287,-0.582584141727,-1.52185263187,-1.54094791755,4.6693254661,4.68670553049,-1.07011530997,-1.37051455837,-1.29159810999,-1.4530998296,-1.45471629296,4.51845849022,4.281736693,4.68164749499,-1.04473025435,-1.11757105561,-1.35584803195,3.50608817481,3.2991318402,3.15315760591,3.24685007276,3.87399050605,4.11654968754,3.58917705813,3.88069843213,3.66291008326,-1.54412124764,4.42398828009,3.97546345117,4.13968079911,4.08636030739,4.00469264553,-1.39419675039,-1.33728387646,-1.37685547493,-1.29962971495,-1.20188870203,-1.21081388116,-1.4435553082,-1.36329807244,-1.5444484228,-1.47181686503,4.69947908914,4.6099896014,4.22221999455,4.05800988337,4.19130080331,4.11276898392,4.30649924783,4.43218691659,4.34508771561,3.34063708176,-1.25989935484,2.76021373175,2.7500962478,2.66936867352,2.66579679992,2.71887203007,2.68175583101,2.49381039973,2.57828264746,2.54631216736,3.44476185693,3.40333762257,2.82738848716,3.42105810345,3.23373991657,3.38374765869,3.28677328465,3.58381149715,3.51898418347,3.30649038251,3.18434787127,3.21809919697,-0.317251508216,-0.229983803466,-0.36314308158,-0.2649320938,-0.202091594256,-0.243557345275,0.0616276449083,1.58883574332,4.20469654112,3.89061082696,4.5780117252,-1.40242433589,3.84646498256,-1.51715686179,-0.653100266228,-0.109895136412,0.200262714599,3.10942599896,3.2331589674,3.23895061439,3.29317766717,1.86929617744,2.04138006962,2.02103315692,1.34241311144,1.27143217701,0.948805198553,1.06491950493,1.35343368761,1.87480984169,1.13211033245,2.54390528537,1.85579976263,1.80787251462,2.66609714628,2.57353573748,2.80283586639,2.84966272372,2.67717854941,2.71754618635,2.80994033118,2.72230437277,2.83517631883,2.79576024079,2.84131572444,2.91901998845,2.92589587554,2.96390799262,2.52761661845,2.40844685982,2.43710162089,0.417135009701,0.423493388784,-1.56204557981,4.66996411214,-0.788337651289,-0.587081443746,-0.743577884553,-0.637796911648,-0.349937296492,-0.376311688867,-0.641588860466,-0.738337965711,-0.798255077493,-0.752753826537,-0.444610289435,-0.485454009882,0.500198178332,0.58658410006,0.740125654646,0.966045980228,1.03489970522,0.746951683191,0.822292822134,1.16698553948,1.15826576457,3.49046459961,3.66158175101,4.28896327762,4.03625328969,3.45643035682,3.4793340339,3.72231531736,3.49704929052,3.47744470131,3.22898114463,4.4206775971,4.59023260869,-1.04618160794,2.68362786708,2.6682115239,3.02872192802,2.90087326917,2.79767718484,2.82206048435,4.40406765383,4.30060392234,4.32762471056,4.09900750367,4.08859917604,4.20952751765,4.19501884864,-1.18977442794,-1.34926025858,-1.46731494329,-0.942746111674,-1.01273271803,2.69723409045,2.88930110064,2.91568272984,4.02155358767,4.25025972962,4.10771717724,3.99741164822,4.31482769931,4.20251764757,-1.28260047877,-1.3704912968,-1.45797234835,4.21917858116,4.244120665,4.27273618626,4.34965785046,3.86122282313,3.89162600198,3.8379092958,3.75370504932,4.0307382441,4.1135368766,4.64855980091,4.46714084044,4.5728514938,4.48874112087,4.01398703917,3.97859156935,3.95191425712,3.8687759155,-0.87055373841,-0.787185511365,-0.737909991732,-0.792767976311,-0.87896170754,-1.00908582668,-0.925393841533,-0.838883494982,-1.04278807127,-0.93493232508,-0.954589721025,-1.11568163514,-1.00957726172,-1.10263139606,-1.42206711804,-1.45767400634,4.69103569221,4.6820678047,-1.55542210505,4.16508761773,3.97155493773,4.0404057775,4.13256724103,3.80300675898,3.60388710078,3.70241754986,-1.48670411009,4.64039159024,3.08579485585,3.16888187055,2.7001471733,2.78483540611,2.83131612047,2.79138798126,2.66172478863,2.70619081238,2.78865256332,2.82932955412,2.83825269159,3.09178958013,3.60274015147,3.92882957762,3.13392238598,-0.900532727193,-0.935926015654,-1.40666853314,-1.31959018953,-1.03282169839,-0.933652957896,-0.955499331327,2.56162711071,2.37249404904,2.48330848974,2.57534153783,2.45094891297,2.4141321557,3.29315320243,3.14061160733,2.80514285263,2.84919995133,2.8547909444,2.94909439933,3.92899880771,3.84630901003,3.63677261609,3.72219123717,3.77581291633,3.60467633165,3.36066612924,3.46618237494,3.44700722089,3.49870784305,-0.282580052419,-0.304449794263,-0.354584730561,-0.307318584205,-0.336969474126,-0.413730508219,-0.186974714747,-0.176054906006,-0.226617208896,-0.229419264606,-0.320926155862,-0.0628038051706,-0.140597829923,-0.159949363336,0.159386135845,0.0951433533454,0.741388340031,0.648932725409,0.125750316173,0.71770527895,1.2612607587,0.873111321298,1.0042369704,1.10520239959,1.03647549598,1.2075674775,1.27251223558,1.34058171071,1.47321219383,1.87547315138,1.6481517135,2.06295842333,1.96869259757,2.94204117521,2.92667448317,-0.419953264341,-0.53604272202,-0.701818657415,-0.615270610429,-0.813035748991,-0.887462838375,-0.535175658761,-0.637332930393,-0.707415586358,-0.672331057205,-0.494437758581,-0.561652369607,-0.337825581953,-0.3865085011,-0.0141439489185,0.090417236104,0.120907393911,0.160945791439,-0.172337236822,-0.0497171421151,-0.224203078941,-0.168660559057,0.00596643614084,-0.0579956768503,0.0049805755666,0.0210922639056,0.156380232439,3.06755061757,2.95167281192,2.92376571202,3.13518811582,3.09091065631,2.99402249201,1.63739970439,1.92114292315,1.78276645023,1.74974193375,1.66697275843,2.1583693179,2.07286517732,1.90713835038,1.99659263157,2.03934513256,1.99230356093,2.06812262802,2.11702381452,0.608597085719,0.845334656504,0.733016636993,0.857379377954,0.624210240905,0.736641979126,1.51014050545,1.81548159611,1.72629951879,1.09928320321,0.997386821218,1.08607635155,1.25589245053,1.1476039918,1.16310231955,1.21904606877,1.50235923358,1.55816454337,1.02844348494,1.01619934139,0.838650965582,0.937662625001,2.63613990283,2.79018982231,2.69925299063,2.64699974837,2.56239540102,2.69047203991,2.64932752025,2.5547795795,2.48316096719,2.48946708007,2.05266880818,2.07446998637,2.00377575949,1.97322059518,2.06123810987,2.04018443377,2.01212547663,2.6735273335,2.77165203981,2.03250673751,2.14397451418,2.12107098838,1.89464747449,1.12558452969,1.1833106806,1.11466854114,2.58045358665,2.53264408495,2.12241001565,0.346687398282,0.189675120763,0.262312871447,0.588074948461,0.668802387457,0.668957663851,0.508109026261,-0.00064995443332,4.23980700398,4.3937716256,4.48968099955,-0.117117921998,-0.0201083120749,-0.468172641103,-0.31882127634,-0.423329437469,-0.649485722564,-0.779269305171,-1.014972017,-0.877365229695,-0.838870010967,-1.13533909545,-0.719466833677,-0.657302809446,-0.623720050762,-0.187157784081,-0.284945183962,-0.347690356504,-0.315859479135,0.000473310010646,0.10529998302,0.0900119592653,2.05675722973,0.780721086387,0.725935037583,0.88554389752,0.934966287414,0.93662761403,1.04627155058,0.819448638315,0.828432191252,1.17357848118,2.27791409166,2.4104270147,2.53121968805,2.50893428978,2.30416483634,1.71197949276,2.84413957906,1.81763551043,2.08210478373,2.71781506579,2.88709873128,2.27060633246,2.26552377287,2.42639089028,2.59418975923,0.666755877974,0.836944276968,0.751869437172,0.831275394578,0.668944049454,0.748657229455,0.819041594086,0.889783196253,0.743668566745,1.05292605283,0.97311905372,0.900062307549,0.907344891921,1.17015055436,1.20167424971,1.2273028408,1.29936756272,1.22165693278,1.02480566237,1.08821186278,1.0204824383,1.08524820921,3.58106013585,3.69979918559,3.82073423191,3.81466014928,3.52125780918,3.39303764099,3.42698516677,3.56538142528,3.68104238757,3.68979995956,3.13109185049,2.98264494416,2.97658861442,2.91019070027,-0.958400443058,-0.894812237118,-0.722891395479,3.02897836395,2.97863849723,3.21169996717,3.29886915788,3.07943615365,3.22293747285,4.33127342897,4.3778590851,4.31058261605,4.34726020545,4.25314721404,3.99835897902,4.00166712236,3.83115188958,3.91005210517,3.90226816555,3.80262115152,-1.10587770909,-1.21497306941,-1.35199408831,-1.13755048003,-1.25429538258,-1.35813824723,-1.50251021104,4.48943109431,4.59916151694,-1.48128263191,4.6229947154,4.64183465174,4.53226212794,4.54414195739,4.70520461431,4.45912226103,4.49631285076,4.60814081832,4.54896057375,4.68641867004,-1.27856563329,-1.26526705124,-1.18453100921,-1.17353117376,4.63167951428,4.6212745156,4.37259558289,4.35202227795,4.32092196604,4.00223619144,3.91640809785,4.03356883776,4.06053508956,3.94601364606,3.88717675965,4.14533192585,4.30158221889,4.24952413839,4.16952862358,4.27279975847,4.19612894324,4.07172924107,4.17430075327,4.03819337024,4.08826283773,4.19214188413,4.28412734926,4.33655340235,4.29727873633,4.21040821335,4.15820196156,-1.22536624405,-1.3069992256,-1.28608825767,-1.33725765526,-1.25394185146,-1.19848325464,-1.23363187706,-0.898214061232,-0.952322745304,-0.759205692166,-0.815343747215,-0.787529908132,-0.676035986304,-0.435945996702,-0.465114074655,3.88000672494,3.79762228041,3.86411609267,3.90709402761,3.92112121613,4.03877842511,4.12592180626,4.09490846507,3.9928461733,4.47526961514,4.42794328472,4.56647826838,4.61454441603,4.26578572867,4.32451141695,4.30840929382,4.41835951048,3.29293058291,3.20930559121,2.95885993423,3.04189939219,2.9152522087,2.92947755865,3.11410570274,2.97936447334,3.07509841843,3.62378912303,3.48136801488,3.50638107458,3.80388410842,3.92021362498,3.94917246444,3.89769920482,3.2358028903,3.30670093124,3.43665907894,3.41180719175,-1.07506414223,-1.03408101056,-0.959002346756,-0.975043314822,-1.44241981918,-1.533050666,4.70929945646,-1.53387824543,-1.45620584729,-1.05431094504,-1.03749749175,-1.10269394772,2.44884151876,2.35213588896,2.23503020082,2.20228954007,2.31854341258,2.40484716338,2.45312732907,2.40939708311,2.32488644467,2.13759590009,2.29437701927,2.19630367959,3.45572649475,3.56475635247,3.72444675455,3.11338839684,3.06184483449,2.93700743179,2.97449371929,3.21979551349,3.39977667815,3.36486658762,3.27377142261,3.69188963701,3.77690057546,3.63003934948,3.65880173504,3.80082137236,3.74482184254,-0.101875630617,0.0660868595094,0.0312833143957,-0.0437917795407,-0.0955001936872,-0.369386959766,-0.476098792527,-0.462401498407,-0.516411893712,0.909149607778,0.989412389493,1.06237185041,0.918281186684,0.837155500474,0.923571528951,0.683597862774,0.609983269669,0.545937294493,0.322123153684,0.410435430803,0.526793807128,0.225413408161,1.18205428513,1.08684899389,0.859891520088,-0.274601085648,-0.0928608378677,-0.260223673613,1.51750482777,1.58659005464,1.63015304371,1.79435809663,1.88483957367,1.79296509729,2.23043573703,2.10373897617,2.64353398641,2.56286343193,2.53108279986,2.36633768137,2.19107152005,-0.552743355101,-0.644977834169,-0.224340523615,-0.329083836022,-0.376442187025,-0.324543656017,0.528673685158,0.100664963634,0.374888636661,0.240258498472,0.473221730929,0.415904587725,0.15988529652,0.229124900229,1.8011937687,1.88642344679,1.93799285135,2.02554715812,2.15951359253,2.07439058912,1.52609203332,1.6118047264,2.13761894481,2.06255426473,2.0100824087,2.0472395527,2.12920269358,2.17770705453,2.26766653957,2.17649535738,2.26843753769,2.31075520815,1.6955996794,1.48013841771,1.59479219681,1.65859067521,1.54488362202,1.46804178788,2.05204364384,1.83570467511,2.00141975879,1.88651552115,1.99488073487,1.89181778312,1.21717039627,1.197245339,1.27642965541,1.3244129984,1.37622498426,1.40372213587,1.3546651196,1.31677662388,1.25105706816,1.26925602874,1.41880595675,1.3993388413,1.32861906973,1.30756951082,1.37536354638,1.46352969721,2.92632748865,2.98445644884,2.93195328587,2.8331800255,2.17785544739,2.28199433608,2.39136340877,1.31072400403,1.35429868155,1.97709359551,1.82614966859,1.88764519835,1.78440791601,2.58110358822,2.60715833021,2.66299050808,2.72952520945,2.80922433653,2.77015643288,1.27330561191,1.34000722566,2.45499736851,2.45591997564,2.41290629083,2.54059802945,2.54419662857,2.58654889688,2.14009018914,2.16715545481,2.1978813536,2.25871171082,2.18365003116,2.23286741145,2.16401267134,2.31835740276,2.3324974217,2.26706971418,0.355112415026,0.163489866329,0.329525774902,0.251421989837,0.592887011377,0.51299387276,0.436815074125,0.0777189984887,0.090498120087,4.62222578235,4.67061046984,4.59422202035,4.47522561467,3.95578850514,4.06638009435,4.04362086348,4.19239049142,4.14046735557,3.97886860929,3.83576243681,3.84630709799,-0.59190233771,-0.606772516874,-0.679170969693,0.0216146135191,-0.141217687535,-0.24580908908,0.669359633763,0.702304652427,0.62440005325,0.595817328872,0.478958774216,-0.618684787806,-0.964731655045,-0.925361912137,-1.09802029039,-1.12634074667,-1.28923951783,-1.27589629082,-0.19443618924,-0.607759584516,-0.520491235855,-0.596583012282,-0.647425333498,-0.500369328338,-0.46463328451,-0.38519670955,-0.408976476727,-0.491670514715,-0.544888212544,-0.517140239753,-0.439777819506,-0.0692379132018,-0.201488116925,-0.225598973521,-0.160289266599,-0.0949863139149,-0.11487696692,-0.0482879167549,0.0369255295554,0.0518126737072,-0.0132901598839,1.89747216103,1.67796464221,1.84168497081,1.95380838662,0.497794420217,0.238131883826,0.476840194776,1.1139868276,1.10173021941,0.82900555968,1.07180919327,1.1998181854,1.35077846404,1.08121479941,0.993843727157,1.06612703701,1.15680252639,1.18189628887,1.60120020528,1.45267396729,1.67361304805,2.72918509297,2.43352882,2.46917882033,2.64066689225,0.741450456578,3.10953842948,3.18813332689,3.15859952919,3.27283478086,3.35730727019,3.32384445339,-0.87642873191,-0.928975236785,-0.675393922405,-0.729489980447,-0.853195688212,-0.742490793512,4.01451964511,4.08051468317,4.15871541116,4.18781844632,4.21396176866,4.0111642681,4.09102719247,3.7370104625,3.57468599222,3.66773023696,3.54433246748,3.61611997259,3.71638168602,-1.55285543282,-1.55919561657,-1.45967454223,-1.36817918446,-1.46427115672,-1.36615088576,4.37686395949,4.43700597012,4.40134746335,4.47524480532,4.52282878417,4.53622755567,4.44694168236,4.56921949904,4.4975406506,4.60118383272,-1.17064260334,-1.06365258499,-1.14826525636,-1.20090674061,-1.03435255329,-1.08854025994,-1.06116717526,-1.11621746195,-0.886866147778,-0.705246637177,-0.544078785307,-0.569747714722,-0.650135084078,-0.485775264663,-0.50098983479,-0.450702310732,3.78226809831,3.66008986287,3.75300085448,4.57785342666,4.47311850699,4.62282057836,4.5693638229,4.4733302025,4.42307094975,2.9206157741,2.9596313189,3.06031563834,2.97074394413,3.61555764046,3.70027746654,3.55804721148,3.47862671959,3.42660023522,3.56230596486,3.59540262173,3.45065782094,3.42778152465,3.48103000935,3.34208807594,3.70093226436,3.77706977731,3.84143658198,3.81684812325,3.73237164437,3.68088791054,3.2685815028,3.20652439802,3.3918011988,3.3651129304,-1.14369365157,-1.26472430275,-1.1784044402,-1.11881656252,-1.38667776036,-1.23445411614,-1.2956894764,-1.14385327838,-1.25162276833,-1.14528663964,-1.3462721796,2.43034997958,2.53448779493,2.5451666348,2.388200813,2.26957764419,2.299746861,3.50514162157,3.42280520607,3.47066622653,3.58535815409,2.99433179639,3.07569086156,2.944069066,2.98635516286,3.12810710633,3.08946207381,3.2552549446,3.2371783171,3.20165255226,3.32537036106,3.37886843105,3.34461357854,-0.0561850855743,0.0290010460923,0.0772293605058,0.040093665035,-0.0477342847139,-0.0955706921442,1.10583609824,1.02914652939,1.1189961101,1.05133234066,0.749800161036,0.744012240224,0.543836111637,0.252542584065,0.301215283365,0.213559732619,0.18700098807,0.458726945201,0.350027671962,0.26943349323,0.297770582441,0.865027020359,0.972593337522,-0.356386479205,-0.306574393162,-0.370037225284,-0.466690940212,-0.258556402652,-0.243925934798,-0.389270668283,-0.499074470678,-0.17536881416,-0.0743496404125,-0.18143173666,-0.227602590821,-0.0343898052206,-0.0883375339585,1.6879417945,1.76927521849,2.27803198239,2.18866481079,2.15104493611,2.19887818493,2.2844301024,2.32563531067,1.72884097364,1.64024020909,1.48868749051,1.58004622928,1.44855965385,1.42190534781,2.3828421624,2.46793167876,2.47719109571,2.38666424174,2.10615513248,2.19128532108,2.29094793091,2.29638072054,2.21594499497,2.12508665922,1.42841061145,1.53132572964,1.4557378732,2.03575931404,2.06272415917,1.99134990349,1.92555199459,1.86449479824,2.3248497215,2.27791767612,2.4459815896,2.40841712195,2.39648897856,2.31157367507,0.371421495728,0.444571377397,0.25059293603,0.369835294206,0.28348424855,0.202384061369,0.29201699297,0.224229467955,0.132915327654,0.146145965633,4.24922575917,4.40522787765,4.48012060472,4.41261365421,4.28515949155,4.20048290588,-0.452665129216,-0.39897668775,-0.279441504873,-0.194258677723,0.504402962769,0.357071708712,0.273961376411,0.164078685633,0.205019030551,0.403940334176,0.505140186012,0.427032163149,-0.110490844318,-0.226165344262,-0.367831324551,-0.428339873606,0.353998443415,0.324338142701,-0.162376536943,0.0805337798532,0.0502437421786,-0.368382124597,-0.306351587337,-0.124860633025,-0.779240297436,-1.33995564587,-1.06976797865,-0.614434090885,1.26668419644,1.3358697605,1.58779106774,1.40067210685,1.58664180898,1.68395924114,1.10700137196,1.12768130208,0.92831159333,1.00909799563,1.03412926462,0.932612706513,1.5127767094,1.44841470484,1.28387737662,3.21465247945,2.96098769852,2.85522560656,2.9672230141,3.16138440889,3.28651614751,0.603602905438,0.671448343926,0.668972594501,0.529069916029,0.595258552702,0.523172803146,0.811169834477,0.88408598913,0.950268821706,0.811287028954,0.948970412026,0.882876955209,4.5610717042,4.47646778495,4.43527386246,4.47815757414,4.56352766751,4.60487832998,-0.90951778997,-0.848426754011,-0.568813657358,-0.733994641266,-0.678698838333,-0.597647786227,3.70536763286,3.70600650494,3.61217084964,3.53082877542,3.54825750823,3.6303503457,3.08496830296,3.04666259424,3.16959526396,3.09678613331,3.30827180943,3.36313054859,3.32970240723,3.24036371087,3.18493838691,3.21905428352,-1.42359681867,-1.520730533,-1.56129942802,-1.49855741148,3.6376545568,3.8696599965,3.74356181269,3.66782818391,3.88125842587,3.78621425352,0.829266665364,0.904038196219,0.973215381466,0.964228844566,0.889446957662,0.823647362392,0.686446635289,0.758200523183,0.658545847021,0.749707991232,0.368234798461,0.3464779807,0.469079474926,0.381580928712,0.460664232003,0.495295626135,0.179741595971,-0.0144365178492,0.0801686024545,0.601994824954,0.437864682341,1.79230154006,1.81949176235,1.84845236947,1.92456326498,1.90326437061,1.95193454645,1.7634444906,1.84449059083,1.89748721125,1.8557432395,1.54670048194,1.6109738508,1.58027843969,1.70631317598,1.68388393988,1.74488009179,1.71385486795,1.65998803971,1.58230396245,1.77469553948,1.59844065874,1.82763286263,1.93084384685,1.96008320102,1.89574128533,1.74515079079,1.71729649264,1.63149751111,1.68933493137,1.43047962607,1.47907882554,1.47477349987,1.55338865641,1.57665170064,1.60625820295,0.140999910709,0.0253343688997,-0.0744251626429,-0.0563199406423,0.551324881704,0.434366463152,0.579795143234,0.496114128961,0.408092358884,-0.911434632676,3.92433728015,-0.430169293517,-1.38663939285,4.0034139128,4.40405663423,1.20728199787,1.22467568948,1.28622920205,1.36369331118,1.60824822208,1.66877893259,1.50070022804,1.61167725067,1.3506580327,1.31712734379,1.37650004357,1.45467068112,1.46895903129,1.51233811523,1.40154961881,1.27021448662,1.4812771128,1.4328669014,1.23794445505,1.31860310189,-0.993219445009,-1.14052399527,-1.05842551495,-1.15515773871,-1.09332012125,-1.01327650433,-0.95445494779,-0.842737812487,-0.81599195775,-0.873041037778,-0.924827645063,-0.979283067277,-0.62211693284,-0.589698875867,-0.763635537662,-0.706540811936,-1.36539126822,-1.20402348986,-1.26663921164,-1.39924338036,-1.32567895276,-1.22907483381,0.544390588217,0.613254572419,0.468193490249,0.606893084921,0.455227946551,0.5248792245,0.679594914678,0.673683300312,0.83395632555,0.757809066549,0.83503952185,0.752918001419,0.586922249442,0.510998034404,0.575226516542,0.477734803538,0.39807718134,0.419168200239,0.402996799548,0.321365717057,0.401771063984,1.76633839463,1.61256372667,1.66462211086,1.65382731821,1.7447959473,1.80430292219,0.0445291117033,0.141550231779,0.235659158671,0.0565534979661,0.150719011745,0.233541310494,-0.611246464577,-0.645616129766,-0.67283024618,-0.735489053766,-0.769428723414,-0.797257551834,0.309603507701,0.322613771701,0.231588232422,0.166339234754,0.151548572723,0.196249580917,0.270916590823\n    \n};\n\nconst int NST1410 = 1410;\nconst double AREA1410[NST1410] = {\n    0.00879695591057,0.00901969149878,0.00880105390457,0.00908467662938,0.00889606258752,0.00895260183949,0.00919475003504,0.00896162037782,0.00897112300605,0.00898653693402,0.0084479989165,0.00837889825232,0.0103674467502,0.0085360562692,0.00881680587249,0.0085068376872,0.00871738935461,0.00885212317268,0.00937873782519,0.00893116007229,0.0089411373258,0.00883505549369,0.00900118020878,0.00898905475171,0.00915006142058,0.00914118874667,0.00911842831015,0.00866805724649,0.00888392142495,0.00867841256828,0.00865713997234,0.00881005313485,0.00878490581873,0.00883844457803,0.00880441328137,0.0090951626048,0.00882608886723,0.00906928513503,0.00883557733167,0.00874202088132,0.00893640771079,0.00895370784695,0.00903647978762,0.00904466967158,0.00883654829243,0.00890175622161,0.00847409056227,0.00862607946967,0.00910077472162,0.00896693376229,0.00899756322092,0.00885858873145,0.0101657247266,0.00884606832158,0.00938619006078,0.00857334292192,0.00901568746878,0.00891231546562,0.00904908764919,0.00894337745994,0.00911273371368,0.00903477579649,0.00897639688818,0.0088834479457,0.00895858042556,0.00910098238226,0.00876753107427,0.00894993485138,0.00894854259355,0.00885763923343,0.00880245954698,0.00908416948147,0.00880560251645,0.00908713043757,0.00881505870482,0.00908545783662,0.00865155005444,0.00919191592417,0.00881840533939,0.00903015328062,0.00885295152286,0.00901528218003,0.00866414386931,0.00940640900068,0.00842298775941,0.00881052114343,0.00866309483993,0.00912950498092,0.00905804687837,0.00882105005088,0.0088852610403,0.00890234411781,0.00893713116948,0.00920372744413,0.00894076088685,0.00918767303018,0.00913410699827,0.0083117355162,0.00899360065223,0.00923787261448,0.0090783946289,0.00862891857522,0.00885265171917,0.0090072250639,0.00893335163923,0.00888873850784,0.00900244584007,0.00889074198759,0.00943499713772,0.00887112524727,0.00859839097294,0.00929361923148,0.0106293460608,0.00944441849842,0.00835108489124,0.00908852058608,0.00870332001405,0.00841256128312,0.00884426443479,0.0090275263923,0.00890545244038,0.00841903759594,0.0088950259818,0.00907534110489,0.00950091487424,0.0101579857191,0.00914656304184,0.0090403743405,0.0089564094832,0.00875132228015,0.00916957609473,0.00909505361317,0.00927483257642,0.00887739665044,0.00866517402582,0.00884950707141,0.00849131897046,0.00901457200018,0.0088112353148,0.00854516404688,0.00928024060507,0.00922727102148,0.00895689795684,0.00885414427556,0.00891619236987,0.00896314768585,0.00893506396848,0.00887980958532,0.00901296888453,0.00887962419771,0.00898801459531,0.00876235975402,0.00894628765517,0.00882916566135,0.00884231080163,0.00904745368996,0.00891200147198,0.00895794640131,0.00892260993648,0.0089309936498,0.00894388341735,0.00894272149333,0.00913486525511,0.00888303621749,0.00885890784189,0.0090091417515,0.00901390566605,0.0088644262756,0.00893195415907,0.00892224228213,0.0089398714884,0.00920087726271,0.00891293350389,0.00890910914976,0.00892024894349,0.00884793811438,0.0089672682404,0.00899182000857,0.00891133514795,0.00889278215121,0.00881258741713,0.00888049629279,0.00883007407414,0.00888638466362,0.00899707609687,0.0087826103036,0.00880390332684,0.00880244254772,0.00904443911717,0.00891149297102,0.00899300559022,0.00888113404403,0.00902068897608,0.00883257804178,0.00905445775764,0.00880083433031,0.00893810030164,0.00891704199986,0.00877880729654,0.00894674323994,0.00875734781822,0.00880180148475,0.00908751213639,0.00899492275052,0.00912011814577,0.00901113196726,0.0088897905092,0.00895443344658,0.00897304698826,0.0087434804966,0.00887905207536,0.00899252730279,0.0088676907674,0.00913323428851,0.00843034769567,0.0086464677594,0.00899046061341,0.00887115846345,0.00882491298965,0.00890795259777,0.00888672157158,0.00892308135492,0.00835262722826,0.00898070647581,0.00902440814244,0.00835831799831,0.00952237163008,0.00907093765958,0.00850156564483,0.00925036196563,0.00821089198865,0.00939280516903,0.00860163837781,0.00850622352082,0.00926604449384,0.0104232041887,0.00907048800459,0.00875090127083,0.00899145726676,0.00896398436979,0.0089943720877,0.00903814952232,0.00879847350817,0.00879806266139,0.0090364869402,0.00885211582351,0.00903422226654,0.00878621124436,0.00922231024265,0.00874817864282,0.00908889741089,0.00880012379769,0.00884309082143,0.00904982955141,0.00879934868034,0.00883590680012,0.00883963705048,0.00904204819235,0.00897914667597,0.00877629526258,0.00902060559704,0.00881412885225,0.00885384068543,0.00911568997059,0.00867110228784,0.00899767808553,0.00896857821898,0.00890835976444,0.00900534141511,0.00893298792337,0.00894904703186,0.00905285933102,0.00884306809316,0.00893735181587,0.00884453807941,0.00914309974651,0.0085554293387,0.00875928412603,0.00871051437522,0.00876337109785,0.00887815807592,0.00888908450405,0.00900714810825,0.00896343406727,0.00891160657147,0.00918759016032,0.00835862835279,0.00857643424341,0.00904588886004,0.00879215464576,0.0102879969531,0.00839934945712,0.00845156825446,0.00890210530618,0.00896364747443,0.00890045226698,0.0090362099804,0.00892899747896,0.00878330215573,0.00895224313476,0.0083531097962,0.00904813065696,0.00883536103113,0.00878174042429,0.00913839408054,0.00883502065936,0.00897082774137,0.0088975259089,0.00903469589001,0.00886060204634,0.00864182202097,0.00905851000678,0.0086049337212,0.00883579767449,0.00893751482931,0.00886336167885,0.00878136718139,0.00905103681456,0.00896344671394,0.00879147681453,0.00898051111692,0.00900493905298,0.00882343406851,0.00877873977012,0.0089291059318,0.00893774269177,0.00893307395233,0.00890154944976,0.00890034667294,0.00900057665609,0.00885057608824,0.00891444953487,0.00908776592986,0.00877396008658,0.0089086705059,0.00894017458178,0.00895437882184,0.00893801298114,0.00894194958279,0.00886898371893,0.00895331839559,0.00888890920812,0.00898204574746,0.00893813290084,0.00894512544201,0.0087687137676,0.00909430037727,0.00913144689687,0.00880458097615,0.00877043994201,0.00909766900868,0.00860549470341,0.0088000535159,0.00846653948476,0.00878600851897,0.00867358396577,0.0091844740698,0.0105063708543,0.00897064077816,0.00900578995914,0.00876119137926,0.0091633008674,0.00828968195065,0.00894360260396,0.00890232555308,0.00879523920688,0.00880752888675,0.00907338816529,0.00875305400465,0.00888192520725,0.00938866249402,0.00896709647703,0.00896254393334,0.00890314145867,0.0089372661435,0.00893766102166,0.00889703196623,0.00885110578493,0.00895276955576,0.00895594973333,0.0090626438191,0.00896242734866,0.0089159378059,0.0090308545324,0.00865064814596,0.00902694702495,0.00884193064168,0.00891534903597,0.00898593480152,0.00890742394196,0.00879078123522,0.00876781297126,0.00905060014728,0.0089613334047,0.00890475238981,0.00891823200453,0.00893468892357,0.00903369516541,0.00886806699919,0.00939274436454,0.00854860071155,0.00878607906441,0.00889539462972,0.0088271209004,0.00901129390032,0.00904979155269,0.00919292460309,0.00920891934711,0.00872919785464,0.00878942545071,0.0089785218939,0.00876671394686,0.00915515879737,0.00868408210348,0.00899893638389,0.0100795240291,0.00838678349877,0.00897221513545,0.00877208656628,0.00904197313093,0.00863979814234,0.00871069099895,0.00853353088489,0.00846482880713,0.00857681273471,0.00894100078837,0.0090688136457,0.00901173413274,0.00887041627004,0.00890293244524,0.0089845445927,0.00879661076484,0.00861830058393,0.00936459934023,0.00880991283372,0.00864727436784,0.00857690242,0.00930311975539,0.0088400435689,0.00899493377185,0.0088715759683,0.00891697432174,0.00888570344183,0.00896707643557,0.0089971421182,0.00891220148835,0.00889670674982,0.00900294796578,0.00874393802015,0.00890617243841,0.00899061267156,0.00891539041708,0.00888812095923,0.0089081968019,0.00895004201346,0.00899046189517,0.00887755293395,0.00900696835446,0.00899150813693,0.00886501338235,0.00905022251078,0.00878544744253,0.00909989429639,0.00877798218141,0.00886900745137,0.00883354088095,0.0090594194448,0.00868733259029,0.00884920353442,0.00883897175605,0.00896116745791,0.00876202908103,0.00868835709263,0.00931443111641,0.0084348545882,0.00854064653323,0.00865344284939,0.00904534759902,0.00882741323731,0.00882758118742,0.00901404445825,0.00885106623037,0.00901574331898,0.00905197522186,0.00877438583097,0.00861613613588,0.00885063565376,0.00904493249544,0.00894017246426,0.00889978447527,0.00895844293367,0.00891361260186,0.00887702566524,0.00890753463726,0.00885840894287,0.00884652213897,0.00896718965629,0.00886545455249,0.00897678047945,0.0090790276842,0.00878464357167,0.00885890749564,0.00884560933213,0.00896777292149,0.00877384560846,0.00909746099156,0.00904542871926,0.0090821688861,0.00873562848198,0.00884607999377,0.00880999556536,0.00903951123328,0.00878727606993,0.00904018703827,0.0088528162558,0.00902150598695,0.00884607187631,0.009041768211,0.00883815694394,0.00903880601253,0.00876426444401,0.00897744631426,0.00895499083471,0.00890919551361,0.00894541085107,0.00899453511157,0.00891483433141,0.00892663616978,0.00893834044908,0.00894889252497,0.00886273371001,0.00888718960823,0.00901990486088,0.00882879403116,0.00835188080587,0.00909443206108,0.00881217973403,0.00852673828644,0.00913134911051,0.00884307538129,0.00900693934063,0.00886663401374,0.00892556525593,0.00886635058058,0.00898034098021,0.0089987589391,0.00880953342918,0.0087336905731,0.00934822186815,0.00854061696942,0.0106084181733,0.00915062850619,0.00875248595884,0.00889152613058,0.00899353483902,0.00903963930082,0.00889931410073,0.00901251126993,0.00898026146648,0.00888535982705,0.00890192872228,0.00887445389565,0.00900344325716,0.00903710368379,0.00897764012898,0.00885011847824,0.00900019850268,0.00886885072669,0.00887000839056,0.00897764757338,0.00890119194041,0.00877090331577,0.00905155968089,0.00838317815503,0.00837182567496,0.00852315188295,0.00880601190054,0.00897469367833,0.00898864192952,0.00892594517224,0.00876270049711,0.00908487230766,0.00877479559236,0.00884049140503,0.00899506533181,0.0096645225056,0.00875760821901,0.0090346055211,0.00853627143757,0.00914853457362,0.00840702114052,0.00844680483426,0.00838523749924,0.00889482011217,0.00871160709215,0.00917941795751,0.00840824386204,0.00873106715244,0.00878458413723,0.00895405641068,0.0089848036654,0.00882196342872,0.00903956839038,0.00905338679308,0.00883800947701,0.00904628215029,0.00885542635255,0.00911335971469,0.00878982006576,0.00905537198979,0.00881532143884,0.0090416418818,0.00877736438478,0.00895774016838,0.00896177144692,0.00887809349331,0.008752074165,0.00847055573834,0.0105790707602,0.00840401940024,0.00914398361594,0.00884864727889,0.00882933080859,0.00903821713634,0.00844691983911,0.00878136985999,0.00936199524822,0.00879226999875,0.0087368856931,0.00897008051126,0.0089104410447,0.00909967330621,0.00878420944798,0.00910333044556,0.00879245503804,0.00906757262724,0.00884805956482,0.00904730243267,0.00898353433205,0.00880764617513,0.0090516221885,0.00882440526911,0.00905744200314,0.00889306100464,0.00879247889326,0.00906195848308,0.00911158128249,0.00877295855354,0.00882159987374,0.00902600394158,0.00880890037965,0.00907746622002,0.00899412353764,0.00887350690443,0.00880344325989,0.0090776318477,0.00900764665913,0.00897460686625,0.00891638237069,0.00884687126446,0.00873717555441,0.00890958553206,0.00895469023782,0.00887179341117,0.00898151842786,0.00890760867425,0.00885382279736,0.00891242843203,0.00914414850825,0.00870264481425,0.00907023071694,0.00897335157788,0.00901769894033,0.00884230933399,0.00891392395059,0.00892806958317,0.00905172257726,0.00889952517204,0.00900171187283,0.00885019653883,0.00865055560039,0.00930236139697,0.00918240958866,0.00853871785585,0.00914351464991,0.00853964372047,0.0087421878657,0.00888550581672,0.00885887640291,0.00896125272677,0.00879738436592,0.00879248847375,0.00846689580979,0.00910564669965,0.00884390222509,0.00906504773326,0.00879970659261,0.00913442687645,0.00884357953745,0.00900403282236,0.00934643608862,0.00881063860448,0.00880372745419,0.00878295125341,0.00905798897954,0.00909294973966,0.00881646948639,0.00898393290004,0.0088667805132,0.00902094966254,0.00884501955619,0.00903922103738,0.00891614801451,0.00884184522442,0.00899668550411,0.00888563490099,0.00882604656765,0.00898722875654,0.00897020783899,0.00887740209137,0.00893916601146,0.00893871327899,0.00893530347354,0.00958723016634,0.0090125424951,0.00854147491635,0.00900839946679,0.00946746749059,0.00849224829723,0.0105247157697,0.00842524970495,0.0093059362116,0.00891469102637,0.00898971190157,0.00882673159401,0.0090403524668,0.00897490053301,0.00890641282084,0.0089395828794,0.00890400718435,0.00899380776542,0.00888687018255,0.00895333984354,0.0088833225699,0.00896881684965,0.00880758920014,0.00908152020276,0.00867066030325,0.00885355649646,0.00881784667858,0.00908652387958,0.00879823962264,0.00890813547082,0.00898557840235,0.00879643079726,0.00908327487958,0.00878184643564,0.00909382608442,0.00833215006406,0.00887014305248,0.00890424025876,0.00896250852804,0.00877257162621,0.00891935517849,0.00892377782365,0.00891858605177,0.00890460268188,0.00894617902886,0.00893114789207,0.00887154012828,0.00899083752397,0.00892592385692,0.00890416934922,0.00894564653621,0.00896070139359,0.00891683571328,0.00896506291862,0.00894048833721,0.0089109445157,0.00889789668491,0.00897350445237,0.00887129172065,0.0089723388851,0.00872340094066,0.00850639819127,0.008984882518,0.00851926294455,0.0105598482273,0.00929651694695,0.00834017462514,0.00944781328927,0.00865261358031,0.00899095481885,0.00886182877566,0.00899013297512,0.00895511198102,0.00906380395005,0.0083364589066,0.00874782240526,0.00859652271495,0.00865402926801,0.00845720165824,0.00899831562562,0.0088001587914,0.00904444506459,0.00882627093724,0.00884338797915,0.00897252815364,0.00890561091177,0.00889714027877,0.00887949845907,0.00899977060368,0.00889091663527,0.00896178916032,0.008898863622,0.00897594806973,0.00898381085567,0.00888950302016,0.00885996919378,0.00896357334091,0.00889770774883,0.008981695626,0.00897978173418,0.00893159496847,0.00862030748206,0.00904864148538,0.00898940297528,0.0089031659968,0.00900370500071,0.00885444898473,0.0088980612559,0.00882040836323,0.00904498593502,0.0088008705867,0.00902712685147,0.00883613157177,0.00888536283639,0.00899603390767,0.00876375763312,0.00915911348757,0.00921296648141,0.00862954896359,0.00885829885993,0.00874743564012,0.00882843313001,0.00912596667323,0.0091082540198,0.00865878909004,0.00890609432952,0.00901314709066,0.00904826827082,0.00899893830038,0.00908682312365,0.0088826445966,0.00900266428874,0.00880854681504,0.00880572199402,0.00902629397129,0.00896254824911,0.00895045868024,0.00857021452639,0.00888717726519,0.00864050359711,0.00828837805195,0.00890839686613,0.00894180599164,0.0084837497115,0.00897937852293,0.00869146128206,0.0089278845419,0.00904605968273,0.00883963523539,0.00870914878704,0.00873078822836,0.00911231324864,0.00847592269712,0.00856608881195,0.0084141826784,0.00898445595674,0.00876767999923,0.00901250889277,0.00887774439352,0.0090499502702,0.00883372147544,0.00895479539045,0.00906652673727,0.00884031306801,0.00900085851042,0.00907303766024,0.00881732036654,0.00885672987049,0.00899936118802,0.00882599171705,0.0090471448386,0.00904663494403,0.00888511038447,0.00902384273038,0.0088537782769,0.00908558235877,0.00887408640145,0.00883426044859,0.00942018474284,0.00939651662276,0.00858918518054,0.00919563710287,0.00862576550113,0.00915524375348,0.00865166400019,0.00943758496649,0.0089730575641,0.0100698772609,0.00888923200668,0.00889157497796,0.00896313486177,0.00899319304639,0.00885192215227,0.00884497483448,0.00903420950447,0.00882833122025,0.00865727813719,0.00861777701998,0.00907654076684,0.00926860757025,0.00860715337826,0.00909460599124,0.00884716941754,0.00904177089211,0.00880324699189,0.00882961065417,0.00903731838105,0.00880157502584,0.00889375621353,0.00902271599225,0.00879848761495,0.00897127755574,0.00888133176212,0.00887176830621,0.00869165732922,0.00910090131375,0.00859607469423,0.00939388575688,0.0083523786384,0.00893147506677,0.00898696183216,0.00890072023246,0.00879909802138,0.0090271221129,0.00900521317586,0.00881852374134,0.00898697375158,0.00888931824176,0.00883082388701,0.0088959386786,0.00890187657548,0.00894323690612,0.00894891278825,0.00892459087361,0.00889380892835,0.00888348307988,0.00900052290252,0.00891621368103,0.00885780699153,0.00881010846491,0.00908574103048,0.00876902020601,0.00903943995578,0.00890439128173,0.00900235311174,0.00887273147173,0.00881908864555,0.00888497227251,0.00879694010763,0.0090830063958,0.00892345676553,0.00895090689847,0.00895475769385,0.00888763242252,0.00900236321235,0.00887285594013,0.00895513334527,0.00893184738045,0.00892067820123,0.00905595882239,0.00883057236488,0.00892470935731,0.00896671782981,0.00879204622426,0.00909835088606,0.00891419506276,0.00860974161539,0.0090046318406,0.00888001440003,0.00888713271023,0.0089503258742,0.00890901929395,0.00896215629701,0.00856212383182,0.00882064188488,0.00910842800997,0.00879050934604,0.00913110781241,0.00903018548742,0.0088487871725,0.00891333897506,0.00899057047456,0.00908020039011,0.00878695079875,0.00897996133414,0.00878498069401,0.0090543417897,0.00884015417746,0.00878413945963,0.00912322476762,0.00873801392821,0.00909300570453,0.0087205105904,0.00879337323882,0.00839703249844,0.00877440157218,0.00909442094147,0.0087434146614,0.00923142593241,0.00857000772209,0.00864091965174,0.00849565114879,0.0084667008803,0.00858919332644,0.00880470732495,0.00905988775961,0.00891351917593,0.00890261524135,0.00883102516915,0.00895216674887,0.00906241158571,0.00878575625615,0.00906614029014,0.00885146187174,0.00849504807067,0.00845856144134,0.00845112649505,0.00855393221092,0.00871201580968,0.00856676830551,0.0094193949367,0.00935109709462,0.00853764903614,0.00847446448771,0.00850020666887,0.00881813344082,0.00903082313615,0.00871678630061,0.00861421881003,0.00843716197653,0.009077012975,0.00855594567948,0.0102142702366,0.0089815655802,0.00890137546504,0.00887926769077,0.00885707612094,0.00886481361838,0.00899520330667,0.00895124719399,0.00891290112738,0.00899691633152,0.00884971758772,0.00909968037066,0.00874070452788,0.00901946586832,0.00890493320222,0.00899350676499,0.00885937495965,0.00898104149609,0.00897045203713,0.00890183836465,0.00894620556416,0.00894630094273,0.00892984245881,0.00888376888095,0.00888348388004,0.00901025080612,0.0089626697483,0.00894771735228,0.00911014562312,0.0087906267227,0.00879453491343,0.00903922051468,0.00883152121936,0.00899015082364,0.00856079873661,0.00870723150138,0.00831449803441,0.00842439697964,0.00859169595393,0.00897630845948,0.00879809603989,0.00911916051964,0.00908126149701,0.00902348481979,0.0088022599961,0.00878032907763,0.00903579518092,0.0087734197411,0.00866993108334,0.0083621442184,0.00943669316434,0.00887478532059,0.00896085520808,0.00891370973377,0.00886817750501,0.00884451249353,0.00884320813081,0.00903683331596,0.00904742894676,0.00893671184921,0.00893398283122,0.00892480431469,0.0087127238416,0.00901156509284,0.00878475054163,0.00848807331325,0.00928443538248,0.00968353277362,0.008986885571,0.00847336945307,0.00891574358152,0.00881488369853,0.00835927811349,0.0103478182876,0.00849873570289,0.0090017672651,0.00886074638275,0.00902219159187,0.00877185769403,0.00879598063787,0.00914817317511,0.00864905084329,0.00879332200066,0.00869363651164,0.00905840327588,0.00903051896779,0.00877485850303,0.0088186408825,0.00840956961437,0.00866742079831,0.00886900047038,0.00899446130104,0.00898403766128,0.00883141938684,0.00881677006257,0.00878968070315,0.00901645670854,0.0091026540897,0.00868280575595,0.00895720078339,0.00884329120592,0.00892423806325,0.00895324553309,0.00890818385618,0.00898120678317,0.00884596261059,0.0090014767927,0.00882399630375,0.00903539162964,0.00889328599552,0.00897507163971,0.00897168262236,0.00884446021576,0.00901179676007,0.0088550039983,0.00895711089528,0.00888594287639,0.00894880862994,0.00898092230594,0.00891784406842,0.00891207460063,0.00888054914792,0.0089150619906,0.0089032011288,0.00897774928929,0.00900554134234,0.00875172046167,0.00917786122392,0.00850010111338,0.00912116106275,0.00850189774847,0.00852909938619,0.00852227340112,0.0101572407998,0.00878147371096,0.00908959873662,0.00895245136946,0.00893846336523,0.00890161156806,0.00880970472706,0.00884810053877,0.00903263397783,0.00901559163647,0.00892644206875,0.00888908418279,0.00893679542778,0.00874861313244,0.00887204483538,0.0090348457326,0.00918920315998,0.00945189496491,0.00906636857525,0.00895130703554,0.0090518351454,0.00882412821146,0.0089761128421,0.00857668257773,0.00901658811654,0.00885614789529,0.00902598440359,0.00886439607833,0.00880473374674,0.00905670483851,0.00872253737665,0.00899013336526,0.00888866685742,0.0090103050711,0.00895425420434,0.00893146268367,0.00900234521951,0.00895433797489,0.00889351360095,0.00889469964313,0.00898982254589,0.00896112258817,0.00887849243451,0.00884098308439,0.00902710224915,0.00881559231668,0.00889318275809,0.00897252325467,0.0089881971532,0.00907332682669,0.00877840488251,0.0083848308466,0.0101979444623,0.00862722173606,0.00875930149557,0.00976519684244,0.00889586874851,0.00860574717262,0.00852096703418,0.0089361177413,0.0085683624604,0.00853428775789,0.00925891829506,0.00854704316141,0.00897788735296,0.00970628453367,0.00865567132572,0.00883404783714,0.00871987576733,0.00906880123356,0.00869977216146,0.00922672964802,0.00845221980922,0.00892644685331,0.00890173732906,0.00885362197113,0.00897578230781,0.00884376710187,0.00899218575665,0.00886308417252,0.00865455527591,0.00864505024491,0.00841620725118,0.00902191868037,0.00877263028073,0.00900433468906,0.00887234398233,0.00917049136031,0.00836732562086,0.00908762099012,0.00862532714241,0.00879066931145,0.0101553264157,0.00862797302057,0.00840645689611,0.0085308902231,0.00892465574498,0.00895262578461,0.00890681057737,0.00875044636041,0.00884246077316,0.00903816918999,0.00901019704855,0.00889365234268,0.008998631029,0.00889355252711,0.00899468876866,0.00889446926703,0.00881812204396,0.00906926205474,0.00886091226149,0.0092626383925,0.00868500440783,0.00873863495053,0.00892360738593,0.00890034174888,0.00898874513333,0.00983193489248,0.0103198468436,0.0090986056107,0.00877932861109,0.00862399958446,0.00842083439339,0.00878474606816,0.00906599294908,0.00874430380466,0.00882394720892,0.00920195649837,0.0086775148853,0.00862403284423,0.00858416945499,0.00852790540818,0.00856746827137,0.00841831239768,0.00889119099975,0.0088496815647,0.00900144080127,0.00876160099291,0.00896649887209,0.00890928243685,0.00881964232986,0.00909055276105,0.00878806256151,0.00888522894043,0.00898477624246,0.008893071833,0.00884347664768,0.00849363791149,0.00878935623035,0.00896280098725,0.00919187769723,0.00874797786896,0.00909873776158,0.0089611805349,0.00909926494255,0.0087368899609,0.00877174060104,0.00918081946232,0.00875737440418,0.00905773331474,0.00828373400235,0.00914213903425,0.0103749930291,0.00856334800694,0.0084994196546,0.00908945036219,0.0089136410776,0.0090452733153,0.00853645596396,0.00873111066608,0.00910536428147,0.00870851806853,0.00856539050356,0.00875691832165,0.00912889353682,0.00853547972423,0.00898404371595,0.00888461645017,0.00898798440919,0.00891886491244,0.00872348110762,0.00872514206869,0.00854500642154,0.00877276654315,0.00893390265408,0.00888719934348,0.00896192890922,0.00920708454984,0.00868457022022,0.00911131101878,0.0088791432208,0.00880336095789,0.00907435415751,0.00912396923302,0.00901279170168,0.008939233483,0.00897313504337,0.0088787233631,0.00895007212562,0.00889383581123,0.00913651295566,0.00902752235853,0.00874692242258,0.00884921389445,0.00904815512673,0.00877339341017,0.00887609711497,0.00898843103224,0.00884530510241,0.00903529158794,0.0087696520448,0.00864117614223,0.00914173987326,0.00872431513148,0.0084971076317,0.00939301466191,0.00850566834496,0.00900541226838,0.00912699234381,0.00876694097616,0.00905857535893,0.00880889798373\n    \n};\nconst double PHI1410[NST1410] = {\n    1.62457401368,1.66062228507,1.74513083613,1.44987160312,2.34303542073,2.42118783456,2.18842189116,2.61572603018,2.04428100555,1.96280522385,1.66571472535,1.72497906235,1.62971709389,1.60396796237,1.63750058239,1.95865567696,2.02534985046,1.9422772128,1.60273857104,1.13149693494,1.41525821125,1.61182532589,1.85060010504,1.78118587327,0.117015482937,1.52827383976,1.76740189572,1.68869318699,1.29809371262,0.898049023432,0.96884282768,1.11572398196,1.12006498116,2.60149412575,2.71738650334,2.69078848303,2.3190111387,2.65314185775,2.5760523588,2.26913427715,2.27587943049,2.3480323331,2.33267675154,2.57015265485,2.64876206189,3.01451419056,2.68826433706,2.75175007043,2.23961009185,2.15582278234,2.24029270961,2.28474097554,2.5231993295,2.59582623532,2.73455179315,2.65058663509,2.62259512519,2.66730975721,2.53527156424,2.61973765237,1.48197213053,2.20841730399,2.29830116477,2.25456083216,2.17471875994,2.25657197694,2.37814993822,2.28419574619,2.28888445215,2.23929992789,3.06738171536,3.03662678158,2.95178139307,2.79697421496,2.80350959945,2.87341562462,2.45839686131,2.48389935441,2.28015699582,2.32728527476,1.52685998764,1.47828947959,1.39057133496,1.45489150267,1.52457985362,1.4112863199,1.54179767836,1.49272012002,0.600329815786,2.09918986204,2.06760396283,2.16882381544,2.14865833429,1.62119157417,1.90570096884,1.75067878492,2.17301754713,2.09670495448,1.98761355223,2.07360269724,1.94695771207,1.9876246585,2.11745602115,2.07198041051,1.70249332613,1.86337526662,2.19726873232,2.2029881425,1.88729238033,1.72414908166,1.83267671321,1.75633464383,1.78096415264,1.85592570544,1.72853994795,1.76940843759,1.64330439938,1.74729234153,1.59164884633,1.624643711,1.57232625849,1.495663024,1.33093443549,1.41012212664,1.2906879496,1.43868554925,1.15484723211,0.897873889368,0.970609741419,1.25907207814,1.26855616941,1.85733985497,1.68689436434,1.81887460943,1.73442196572,1.34084608364,1.65709409237,1.54945021774,1.63081858646,0.829512403686,0.824729010884,0.273869863167,0.640602588792,1.30785047085,1.2609133861,1.17941321693,1.16811884058,1.12370574022,1.16516763107,1.24893472487,1.38795204108,1.55054178553,1.50142842826,1.42410203188,1.54622724436,1.50722476209,1.29621247474,1.37913867301,1.41761864504,1.37406021523,1.29068151632,1.25113957882,1.63437062363,1.80572790253,1.38519067246,1.42021570113,1.29261358471,1.37539324881,1.62592847534,1.54148757949,1.49871914601,0.766094742419,1.64694215045,1.76522576712,1.73162116964,2.15517809559,2.11550443824,1.98621525879,1.86510605773,1.90109070699,2.03161712366,1.88348546063,2.10777894585,2.0223491303,1.96991723273,1.37995016085,1.2167074012,1.25157953751,1.33028074723,1.32055988903,1.28992802882,1.33883849621,1.3096719238,1.35951175122,0.671121228108,0.685654373133,1.016300318,1.04930648416,0.602833819314,0.40519551362,0.558260169868,0.46250178353,0.538096369996,0.407759039467,0.560331708046,0.437992030725,0.513200530869,0.330979858343,0.319624848123,0.184735328897,0.332309046875,0.285261794531,0.208470842942,1.68810562416,1.58821298344,1.66629482445,1.93052859092,2.01287908488,1.63651981353,1.28027266544,1.3914362756,1.36323984781,1.43980291829,1.36601937383,1.36412728864,1.04211765176,1.0374359457,1.18330382678,1.03947852731,1.11331940362,1.04744143224,1.04146624535,0.996200761255,0.902646043563,0.969004979045,1.18316707968,0.965165364356,0.52894564832,1.42279541108,1.40231474412,1.70760346789,2.68119142738,2.7327630765,2.60999155741,2.57087713267,2.47911597633,2.55848633257,2.2324266322,2.34139150157,2.37315768548,2.31494172232,2.33572039028,2.28073432421,2.26943014107,2.41876869223,2.41029259529,2.54589698039,2.46951225693,2.41932304136,2.86906764268,2.92691821668,2.88066211575,2.78622661871,2.7530320356,2.79377982479,2.59562179438,2.64699690772,2.58649904875,2.7264539946,2.7343858398,2.66397757218,2.45423174321,2.53693628218,2.44572061911,2.41155130452,2.89842377252,2.82814337558,2.96129960174,2.91561432603,2.39200971344,2.26419959264,2.34203047908,2.40865131057,2.24926498276,2.31079359986,2.45227787045,2.42804967555,2.58595635345,2.53279612767,2.5631637732,2.48655919915,2.46879858332,2.45313117881,2.28863300242,2.34633111943,2.20716824235,2.18082748221,2.36390893284,2.27791343792,2.41147979364,2.49259105251,2.41052151424,2.36951994594,2.49742650104,2.53844214849,2.48257280037,2.39798319807,2.34242291147,2.50738252966,2.12958579069,2.11398189836,2.23610840576,2.20111392176,2.2117250598,2.2792112579,2.28658645946,1.79951750435,1.88022584389,2.13146958191,2.17611712729,2.02062885597,2.13764583583,2.05822027647,2.42423399825,2.27520867921,2.42439176906,2.35541008686,2.49617786851,2.48938303372,2.11726501722,2.17545775818,2.21660859737,2.13467562027,2.21122100292,2.51326063387,2.45767657466,2.33779952686,2.41567356391,2.47860925688,2.34956855153,2.23768171029,2.21324492991,2.26817007331,2.37590875051,2.31860750785,2.98482932764,2.93947840685,2.9212764594,2.85553686439,2.84159695143,2.8155713851,2.75053213865,2.84978501714,2.67790839913,2.51074403666,2.72554463323,2.76478488129,1.83507706608,1.73680461186,1.87980915517,2.03952984956,1.97800845875,1.90222097757,2.19584478445,2.14711987129,2.40789701663,2.5160189742,2.4375020581,2.38133155697,2.52721458117,1.3162953939,1.15571632844,0.863668577574,0.91999440317,0.901383228281,0.602349183097,0.52201624677,0.673859362689,0.824376429464,1.3609671789,1.2621257253,1.39398259332,1.34654196793,1.14394296741,1.25491792475,1.27897604221,1.22742010958,1.11791237324,1.17746598782,1.88811682589,1.93882115987,1.83360158776,1.85764987908,2.14340029057,2.10956481735,1.77839708612,1.85836913568,1.74466261162,1.78820799206,1.43928301502,1.54234087675,1.38231654119,1.76496780138,1.82477465951,1.80057609914,1.6813708571,1.90818296481,1.69426235536,1.66069459686,1.72022702437,2.31282035324,2.23426701766,2.24674094158,2.20300320997,2.05838831937,2.03721578377,2.12347363074,2.13159076272,1.25103705393,1.21078157617,1.22999612532,1.25662472185,1.33887017141,1.29800395884,1.36647549693,1.46636385079,1.37893022247,1.38049396303,1.33671889715,1.50727827706,1.46347302123,1.41415480278,1.34438016942,1.35851775559,1.13890431949,1.11963355377,1.20706343179,1.19905119521,0.828194750428,0.836032154922,0.77269150804,0.910894404642,1.00165657523,0.925286505446,1.06241829311,0.975903728661,1.05087580812,1.38870165336,1.49161727798,1.60561720698,1.59770982412,1.64368831705,1.5110991795,1.47292467492,1.52161876776,0.954168072067,0.963518439205,1.04026845902,1.27348325016,1.24532222283,1.30365902336,1.35626126864,1.41061894382,1.386042904,1.19910691176,1.2812365428,0.54764055875,0.286121681051,0.358655230751,0.528709833851,0.600515135045,0.607319681074,0.292624617584,0.461184932565,0.454950166386,0.208919719513,0.224537416017,0.45875164463,0.391969051773,0.532564885359,0.410137072826,0.491038680663,0.547113958248,1.51136724332,1.43204749321,1.76727909555,1.12513770582,1.05259353429,1.92515571335,1.79798293915,1.88608652223,1.66701368065,1.75197427034,1.54060394233,1.50181812673,1.66585634281,1.62430967045,1.6268310355,1.54663009472,0.742101909193,0.794128413356,0.780042402686,0.879950431265,1.59566541027,1.51213055705,1.46109024307,2.14144303444,2.22677632059,2.25750454677,2.00203433155,1.97311831237,1.94485576267,2.08787288519,1.18881512176,1.25554031836,1.30343826241,1.27120276708,1.17200317652,1.13736795026,1.43858901503,1.49065056649,1.55894178002,1.39980386991,1.51202623305,1.43231535313,1.41736946246,1.44784553978,1.52714358304,1.57707617913,1.54770682323,1.46743014427,0.530881138116,0.598001263651,0.458040051108,0.601955696639,0.539815481146,0.463908626592,0.734498668708,1.13127246416,1.09304079874,1.03931172527,1.00132722053,0.253062802528,0.232425190415,0.275133554693,0.182907920291,1.54503360923,1.62665481539,1.65274624285,1.59062780351,1.52061023704,1.81697348486,1.89926177399,1.94427831363,1.9065505855,1.51587592032,1.56624077122,1.66166017216,1.77145742146,1.74579678837,1.71214965637,1.60189491839,1.62686686696,1.41746612769,1.14696853025,1.2290024348,1.48022111455,1.45469923245,1.36923329011,1.30942565315,1.1142255635,1.03095672019,0.999621186919,1.4344743222,1.57632975627,1.51720667388,0.981556637948,1.06124881023,1.02433732849,1.0820248215,0.840738623722,0.920944383127,0.943298323228,0.785017429443,1.17696020773,1.2556153154,1.16087669453,1.21893573278,1.32022405335,1.29956011826,1.27667878215,1.24428268222,1.31210507307,1.37834194677,1.36148549188,1.1090365548,1.17830567395,1.17745012514,1.10556633023,1.32753793346,1.2498294074,1.24731148283,1.31993578953,1.10505851985,1.12258875671,1.20330827445,1.22586990468,0.824016748968,0.888249996105,0.880096418551,1.02907106655,0.599903620172,0.747422846916,0.706988357578,0.685436543298,0.609590606734,0.545486354315,0.887878174482,0.930161766227,0.894684460358,0.817239225029,1.64592155397,1.50426084276,1.56555740703,0.746515172854,0.771069812319,0.674468138169,0.804979627809,0.864634576043,0.916458928387,0.888114827434,0.683542831362,0.699505123348,0.780682051342,0.841562010259,0.730483522396,0.779875734702,0.748103684315,0.735838942253,0.649777385351,0.627772645401,0.657008021878,0.607983480711,0.546644491818,0.274335921625,0.391313577921,0.253252426379,0.320674943933,0.39764961195,0.480793642358,0.408204464292,0.35609463743,0.489870613612,0.523104075107,0.156596376473,0.235226324487,0.158367828263,2.36850942629,2.44978658842,2.49104316819,2.33052662285,2.44447132163,2.36608545241,2.16735639291,2.13345231888,2.0575372842,1.83086919908,2.22781312105,2.20694689036,2.2579033886,2.43225933455,2.50994930982,2.36274361205,2.36021797582,2.43391058294,2.50890678642,2.55868757192,2.49056903718,2.48598109601,2.5501741011,2.71568491109,2.6660066415,2.832394231,2.80064736466,2.35948658957,2.38842950784,2.27386627763,2.22309094929,2.20246020906,2.25328013662,2.3329886475,2.23254704075,2.36594507318,2.31626619311,2.4459584141,2.45687856728,2.39002695099,2.31729039607,2.30408011438,2.36613417424,1.49589254783,1.54576017915,1.39518777,1.47546934413,1.40021182284,1.62223077104,1.69303412962,2.05808702465,2.09075344669,2.1206071833,2.18818929154,2.17179321872,1.85541881226,1.91063750515,2.15745869446,2.1042463744,2.12680902472,2.0447337014,1.99261605092,2.02171609441,1.8216329542,1.88108927624,1.96522267182,2.3796426806,2.27712941637,2.29209922848,2.24555990258,2.51282545252,2.61556076759,2.54686379565,2.64154083852,2.59671034465,2.17904180177,2.19759633786,2.34962794469,2.27324391063,2.41205734266,2.39167092584,2.31102722403,2.25461346706,2.14519224204,2.13509858964,2.41574699535,2.36976430252,2.40205813301,2.48341622851,2.56658421923,2.62574794082,2.57936776666,2.65544533953,2.72609151991,2.70822328443,1.93459546188,1.89315717906,2.28486434688,2.32356880299,1.29626569368,1.35496329437,1.33329030244,1.2131492827,1.19139939855,1.25288288163,1.03996321764,0.720724192288,0.762935334765,0.7840887937,0.763097891637,0.730896232052,0.651180231358,0.626123795167,0.685322074016,1.96672343977,2.09489277117,2.01651687375,2.12401216675,2.07229840491,1.9936049655,1.98422328777,1.90488562418,2.02900173127,1.86829026442,1.90933545025,1.99052327585,1.30235277812,1.30374833054,1.23798920711,1.39116586986,1.40576314166,1.5754827145,1.44135487887,1.51718075849,1.9753297759,2.10640404608,2.12000762758,2.05254193682,1.78559708638,1.72402159263,1.8950961527,1.97836842408,1.86091468739,1.91850479311,1.9916436461,1.08431889267,1.12843474327,1.02228476241,1.0988386607,1.12577172466,1.08835599645,1.13757418736,1.22097477031,1.2560870595,1.20982826455,1.00587168049,0.843394332883,0.891450530683,0.972703311302,0.999744578206,1.02502492253,1.05811553119,1.10709632653,1.16254606315,1.13859536795,0.656285120532,0.604963482316,0.751243669507,0.754698260659,0.680979586788,0.612816685434,0.696382536344,0.557629270428,0.63451078737,0.686188655328,0.547311833778,0.614171926014,1.44716852303,1.52946887084,1.59506376006,1.57935283851,0.441842974516,0.294864716944,0.403061361079,0.327932437305,0.480132632704,0.478314811654,0.350689644733,0.418849885226,0.410680669977,0.344040683508,0.563925121677,0.651014737398,0.517555062631,0.565545819265,0.3646727983,0.524723645514,0.478854519793,0.397955477763,0.42110531771,0.498299636663,0.539671891342,0.208345756308,0.169159141264,0.186605187614,0.119121295365,1.69534887869,1.5422309655,1.79667904886,1.75125205002,1.76000049326,1.6752779463,1.5193378902,1.5663086583,1.63851573427,1.67158931537,1.55221120469,1.63216523424,1.91649349655,2.00303259111,2.01550077841,1.75814499311,1.67638258935,1.76286575546,1.80499536643,1.63141042372,1.67120086437,1.79360234685,1.8760144188,1.79060172831,1.75003572627,1.92055163496,1.8785155333,1.62723776816,1.71196544162,1.60147178678,1.57312178552,1.6858107834,1.7423253124,1.82831270223,1.85772906644,1.79875994878,1.65788074233,1.71296445676,2.15666036571,2.0762826101,2.22197706236,2.19907508735,2.05731963825,2.11509360961,0.823627947463,0.805878559204,0.826838817333,0.685729119818,0.745364041205,0.866967240682,0.909267544514,0.915313364521,0.995416184588,1.00188928007,1.03999473764,0.112270339181,0.0506028035006,0.132069441645,0.146000656499,0.0388876302635,0.0892491094601,1.64794331241,1.68873266417,1.75024816924,1.73027191797,1.54554200164,1.6071384471,1.58620228594,1.46386552964,1.4839795052,1.36109151518,1.44312778812,1.50397693061,1.83130424327,1.71696528767,1.79120115058,1.78646168524,1.70106930439,1.67142111759,1.67629484949,1.59114637744,1.53126034759,1.34053386502,1.26122082741,1.34556554159,1.26334726227,1.4463099321,1.42004680813,1.38903475951,1.33485177316,1.16665561118,1.25047545258,1.30515313875,1.27708851614,1.12165224629,1.16853087834,1.10561098571,1.20033045258,1.29508356396,1.16690224586,1.24429906896,1.30500402022,1.06243822626,0.944765752997,0.922059760774,0.982249180724,0.808476925217,0.669239218884,0.668577544797,0.739676855295,1.22631246112,1.15936926081,1.22519418578,1.15763105222,2.02731582127,2.04088746104,1.662152027,1.75194439494,1.73966536053,1.6850440707,1.0981294077,0.808973690386,0.740935728035,0.748695053013,0.82504924464,0.645513462886,0.613223257569,0.637909752253,0.662822892672,0.554788302496,0.608784834558,0.493485510292,0.523560551181,0.936784634274,0.529742110899,0.612490392851,0.482864812152,0.537092204507,0.370316837881,0.29398083851,1.94090162108,2.01535720327,2.87797205285,2.80205564224,3.02763572634,2.94505227846,2.90935388104,2.62971200689,2.69725421109,2.62619940377,2.68899518507,2.77324473295,2.7679267539,1.80940863081,1.72355369392,1.62682509685,1.704285269,1.55684132903,1.56675595205,1.50086392367,1.46693158394,1.4331765639,1.55408783334,1.5743516083,1.46502455161,1.54032829263,1.45604391797,1.52071563467,1.60883997007,1.59816027637,1.76991372915,1.77658070669,2.13563755165,2.06076428497,2.47395226645,2.48961013232,2.41038757658,2.38873853091,2.35453535674,1.59690945133,1.73344494087,1.67779871547,1.86295167301,1.85033185326,1.8064819653,1.99365177588,2.05646743636,2.49685769509,2.54599700634,2.61339913174,2.53300304833,2.67498982893,2.643565917,1.89213224125,1.93761613804,1.92969786578,2.02459761898,2.01349477105,2.0631619161,2.1557875303,2.19819183017,2.06883782595,2.02301475698,2.30177827807,2.27294065336,0.881985450392,1.07426548409,1.01793107809,0.937039009487,0.91460436878,0.959544884663,0.974031461448,1.11237821911,1.05254856964,0.885409201642,0.882637304828,0.814862887253,0.742254613516,0.835563547676,0.820673866473,0.679223893653,0.745458128743,1.24600961228,1.23452606207,1.10316291275,1.18133330869,1.48152566458,1.57761004607,1.49343104011,1.61662282307,1.55726267166,1.92742044141,1.86698576284,1.88379523035,1.96282277549,2.00768882203,2.02642400864,1.74407667482,1.67897891878,1.69482824531,1.82237501642,1.77179665626,1.83435466349,1.00311263916,0.961711890607,0.880931282941,0.970322100469,1.42871933645,1.32771730228,1.34528477878,1.39626227254,1.48042027384,1.49540219809,0.639083890886,0.766028243666,0.691868292136,0.679972327761,0.802483084187,0.767856887438,0.253600393531,0.408842419624,0.457570480309,0.461423648213,0.39121125003,0.319412435921,0.334767378911,0.400842828806,1.68760035989,1.63847051689,1.67188346643,1.75135982125,1.82421992948,1.91132532384,1.93486047838,1.7680606703,1.87822144861,1.79853745497,0.934842078649,0.906273947435,1.02067090506,0.877095277332,0.867074354638,1.44439257662,1.36933489625,1.46939961254,1.40602911541,1.49075105952,1.44486293466,1.38439205765,1.55175828279,1.52739686008,1.26071617986,1.38551074904,1.30751038452,1.24483472632,1.40295146094,1.34078062689,1.08488041645,1.16737364539,1.05394717063,1.02713536614,1.19334090845,1.13739879888,1.08712147561,0.948421131824,1.02025475796,1.08777084563,1.93012853194,1.94720147965,1.84970579795,1.88259589593,1.78783589756,1.80375665048,1.89483998241,1.82822187583,1.98118985458,1.97294612865,1.83826515489,1.9131018174,0.981154704141,1.01593427242,0.898280167759,0.853692102398,0.821653109855,0.884737277265,0.963790821066,1.21464103586,1.13371365091,0.880668388484,0.948431197285,1.01966544915,0.889181722035,1.02841862618,0.965557933907,0.885485172501,0.868823405773,0.917293762959,0.782838861951,0.664869385022,0.748388651291,0.802997111702,0.642465728576,0.655690989479,0.376868816333,0.403500341148,0.240446579126,0.296961416182,0.357184797956,0.277546475353,1.89085571974,1.82204316252,1.89634499193,1.9237319147,1.87023197608,1.7904333616,1.76940597797,2.12236952025,2.04642764287,1.99921480323,2.15150982375,2.57715223118,2.6310220783,2.52764415147,2.55151715836,2.69955900358,2.78106353181,2.65972375731,2.69083624682,2.82532084712,2.77260108293,2.640048909,2.5579977,2.56709367449,3.0738807895,3.09729570555,3.01436317359,1.63985558111,1.64258979773,1.70291782838,1.70739822634,2.05757622267,1.97203442767,2.13077227954,2.1800816549,2.19976691866,2.08905345865,2.05829252354,1.92059120251,1.99137564198,1.85231643029,1.99360894687,1.70747465691,2.07883648292,2.07354283348,2.00601334889,1.94049051168,1.99475702337,1.93440403565,1.84575781281,1.99068001599,2.07419213923,1.93052257096,1.95392609062,2.09756813453,2.03732434665,2.25207273885,2.24494434003,2.17325032287,2.06220116425,2.01258162005,2.15009805025,2.18660068858,0.980188540792,0.903441401087,1.00753179363,0.957561594029,0.868725391247,0.840946098018,0.634920757239,0.699833827035,0.769573178642,1.16216682344,1.097981599,1.02379761341,1.16010721923,1.02120549707,1.09329131916,0.892470511343,0.867819003464,0.790313586414,0.735399797377,0.764871589459,0.844047146562,1.15957744262,1.07635674916,1.21142855645,1.0007363834,0.961728423788,1.08875755361,1.42147777614,1.40085463819,1.44742812358,0.907630027573,0.843929076979,1.06840277252,1.1491204893,0.999264391447,1.0865023099,0.432039105569,0.506560545545,0.567959964041,0.43565480595,0.515400302142,0.573150674859,0.77946318879,0.70276312187,0.833120559846,0.710775279358,0.794778307655,1.99030445065,1.96971499666,2.07066788192,2.1264149744,2.10096648941,2.02481487693,2.78698872843,2.7135873544,1.85509988223,1.92535543711,1.78251612967,1.91504330136,1.83760301229,1.77516822611,1.49956411228,1.55620962212,1.63174818664,1.49707944359,1.57666614871,1.47280022682,1.526821346,1.60606434223,1.7644323385,1.84691662031,1.91205494957,1.89141460318,2.00268302295,2.04832001231,2.04521131986,2.12046187113,2.16394118023,2.13417019355,0.776587871704,0.800874847236,0.750709697752,0.69602030844,0.638679016291,0.669971789773,1.00118069596,1.05075713559,0.966629960468,0.945189875081,1.34900203333,1.3217828788,1.246473254,0.933714015791,0.986478253154,0.959620169854,1.04046290416,2.70312231363,2.64831260282,2.59669289528,2.62125742141,2.82351139239,2.88714572507,2.96405117338,3.00200058747,2.93304361485,2.86112415953,2.96198039108,1.74512189305,1.70016022271,1.7846658098,1.80702903358,1.63983939677,1.66254272303,1.07715607119,1.20305917902,1.12575107173,1.23208291587,1.18258289014,1.10517250871,1.30107020003,1.32703703264,1.27799525118,1.20089323992,2.84672342746,2.73193681794,2.76421516733,2.88762486714,2.7568476611,2.82542503965,1.06722356563,1.2259957631,1.09509450307,1.17483123813,1.11948382407,1.19859252156\n    \n};\nconst double THETA1410[NST1410] = {\n    3.15026418775,3.07710262364,3.07326826409,-0.0911346091709,-1.24130495534,-1.20992705766,4.38575066458,3.45162189132,3.66717626721,3.64338582012,3.21981026785,3.36361819701,3.34972758506,3.28377861572,4.48701675354,4.5182004306,4.45981437689,-1.45526001619,-1.50567947786,1.77488557233,2.00818447701,3.01168462934,2.85274265301,3.00065815094,2.39443767123,-0.122626403851,0.268153561099,0.262674432481,-0.0211281815933,-1.05545043246,-0.993920256401,-0.597428551996,-0.693675741637,4.58806940225,-1.19817754128,-1.38527222256,-1.35083734868,-1.07374826186,-1.12985828707,4.35123058326,4.2451089902,4.19639597009,4.42565862531,1.95361158377,1.9860644221,2.74258143974,3.48675967943,3.34680017881,3.0819272472,2.91022130309,2.90212057522,2.9907701746,3.24812118168,3.28389135856,3.16171580788,3.13988076964,2.98833899796,2.83355589231,2.7379200421,2.70375514067,-1.08894350174,-0.909374369025,0.209351506219,0.304590303252,0.298189918286,0.11273708358,0.209011363069,1.0992142254,0.406064741221,0.490946397154,4.40299924005,3.48096833608,-0.8467646265,-1.13291445507,-0.890014296509,-0.724506989473,-0.514878484161,-0.631124068167,1.61285973461,1.70151886851,3.02601675669,2.96642925074,3.21370773596,3.27552776289,3.24926249064,3.12845165594,3.16517748043,3.10643762468,3.74921519546,3.59874840193,3.75716963327,3.8841327447,3.78673832526,3.77636290045,3.70474917087,3.43893158562,4.48209665065,4.51078056732,-1.5201305049,-1.52012916447,4.67614384884,4.60837559777,4.67117788215,4.60442148893,4.63882279485,4.65873347437,4.08454515606,4.18648285064,4.50033819434,4.49079652191,4.57331740963,4.56338575022,4.2670085187,4.41587440146,4.35091337818,4.41674562026,3.8557126928,4.20747019087,4.55020713804,4.62952103342,4.69573234696,4.68188690739,-1.10710816658,-1.13979842977,-1.40993675582,-1.52636546727,-1.52969316539,-1.16002483871,-1.19871525602,-1.15755833858,-1.24302250086,-1.47839611722,-1.49814700765,-1.56075124915,-1.57060010674,4.37307089455,4.21599158017,4.12170177547,4.14048899742,-0.899329198197,-1.00577864241,-1.31201378616,4.42252793786,1.63666185258,1.70922854432,1.7006246466,1.8568473725,1.93300127959,2.01181464896,2.01147504444,1.64311509832,1.64914620425,1.86275576518,1.7189129352,1.79126568642,1.72040357974,1.78750311842,1.79060396268,1.86364201538,1.93635845307,1.93658854876,1.8607028988,1.64872622329,1.63459384789,2.23014433463,2.15410474237,2.08559147844,2.08253104159,1.92893671224,1.93262974849,2.005319698,2.40799826429,2.93789745808,2.85762563754,2.93075389913,3.08018430706,2.99709043542,2.92162311119,2.99908575317,2.9242269027,2.99870863945,2.77825615904,2.833703595,2.84325517806,2.77152087847,2.8422820233,2.87709562953,2.79248149486,2.77615762482,2.38600076214,2.46690982614,2.54023579723,2.62281564297,2.69332470862,2.58750570623,2.45320378205,1.67502395954,1.76518803142,3.60175411993,3.39145596254,3.34510122569,3.54897967694,3.49956497185,2.99164168333,3.05433014552,3.19290635793,3.19984748543,3.42383935068,3.69581096175,2.71634336134,2.94054149649,3.17314942686,3.15198222133,-0.175085270285,-0.0733636188926,-0.0986809005846,0.281379903445,0.287592283219,0.331076668345,0.91084515352,1.00215859058,0.924115355094,-0.0116744349146,0.0229546404508,0.109696308638,-0.567131799584,-1.03792128135,-1.12413536925,-1.13880427966,-1.17728298475,-0.742259327377,-0.833682676247,-0.659207567523,-0.842946813538,-0.887206556841,-0.750105836221,-0.183571122993,-0.0335603033133,-0.600576330037,-0.519616630587,-0.548302989809,4.56425337844,-1.55591180804,-1.40420278439,-1.54964303867,-1.30683907168,-1.27876255462,-0.549359391127,-0.389280573242,-0.497548882041,-0.577192659962,-0.683951632958,-0.853700370824,-0.751255461714,-0.719528175051,4.39337092271,4.46356099944,4.49031202865,4.26803959752,2.49802768675,2.7472664029,3.01505833941,2.58517391392,2.80383002497,2.98246599389,2.27580460755,2.53643371126,2.4400194156,2.4558813803,2.24137400741,2.16745263399,1.80509845332,1.81277448704,1.5805650229,1.69508703131,3.34713497327,3.46212680937,3.62890662503,3.99367425918,3.89775306263,4.0299122626,4.08163357155,4.01655772762,3.92406917622,3.8584080097,3.81151346176,3.69414441282,3.74649025082,3.85123316838,3.58581868924,3.5760727729,3.33666424839,3.47120856104,3.75070594452,3.673319598,3.71979341392,3.6212168168,2.78970356943,2.80755612826,2.87936704176,3.09945134141,3.08808338806,2.98630211986,2.86569304332,2.98514540284,2.63988813112,2.6771480286,2.6011501696,2.50518361785,-0.867925014831,-1.45184749208,-1.36853029804,-1.46002103088,-1.00663464849,-1.16607273812,-1.06128029145,0.34700873747,0.354205007709,0.638460189295,0.117002067181,0.121294741349,0.207127156112,0.206791323738,0.101607825056,0.986443120009,1.11088675819,1.16508405618,1.18650430622,1.32400156259,1.3925386975,1.45973055951,1.14850316608,1.29416935519,1.25526968115,0.688267101294,0.779564379715,0.926181066786,0.982775405542,0.911264766726,0.622232870432,0.769866737176,0.666914262543,0.593403737761,0.737681681532,0.810967091212,4.63991780536,-1.25904846849,4.3542193895,-1.32207375578,4.46779124351,-1.56571236621,-0.0682833136307,-0.449716483727,0.0230958167281,0.0890371837114,-0.274447747971,-0.445867918422,1.31779371695,0.628978946783,1.47372273099,1.42349847531,1.36258750829,1.38942783985,1.63029597161,1.55705050513,1.9077852677,2.06440398068,2.02806387883,2.11534598526,2.2114082879,3.2365808787,3.28818044015,4.1330793726,4.0544525911,3.95088564147,4.29108228643,4.27223538506,3.81614099558,3.91369750949,3.06724308021,2.94208716207,2.98550961594,2.92358706825,3.04772475127,3.17280661511,3.08842764708,3.02536174437,3.13732168623,3.19882428799,3.38929828351,3.55746700947,3.45676943325,3.5374566721,3.25418888098,3.16459085472,3.29911659416,3.30676879416,3.22106166113,3.14772778507,3.35973474714,3.75910161298,3.72447235001,3.740224005,3.68181630114,3.60017373325,3.71769463179,4.11090406488,3.50164748086,3.64127298419,3.5824034259,4.53586218328,4.55690315919,-1.54586826028,4.65304449335,4.18293644668,4.37246700695,4.32730373361,4.2326946602,4.50862965733,4.58418058644,-1.54431936173,4.65663130286,4.6591360474,-1.48603602753,-1.53777422735,4.46320435898,4.59304727097,4.45085221879,4.51525624348,4.53899213829,4.60440562225,-1.21712405143,-1.26946030109,-1.35532303928,-1.44043592218,-1.26620783252,-1.37942376214,-1.29805416767,-1.22881239557,-1.3411786031,-1.4235142194,-1.37197227355,-1.49617358265,-1.47607085086,-1.42111304429,-1.29942680429,-1.32762322497,4.30924623189,4.18301430922,4.27509924079,4.41260271085,4.35045363318,4.40004598112,4.32379108217,4.26105574504,3.77947796886,3.88267964366,3.91654913977,4.2159650226,4.13639788413,4.07200909922,4.23032171526,4.16697911796,4.08803668273,3.97247467217,3.99316259932,-0.933365782443,4.6650162445,4.20926063808,0.132911342398,0.209058590488,0.355405573423,-0.150202447332,-0.293007062924,-0.123784956703,-1.13298675083,-0.735358946175,3.73928766253,3.83683134122,3.83015004309,4.05202025522,4.10787200811,3.99287117885,1.57492456753,1.57044956941,1.33679007647,2.08976302084,2.25167663778,1.97313484954,1.91546751578,1.90585815594,1.9970562265,1.99031822237,2.07493872529,2.14940327375,2.1390464738,2.07036356324,2.21456227158,2.22032634177,2.20929331491,2.29246213481,2.10067446423,2.27051179056,2.87345608923,2.88756169765,2.82540352168,2.74429555957,2.72636786981,2.63123633485,2.69342967594,2.54890025909,2.62635030009,2.67625819876,2.31932009389,2.16210351137,2.23500738389,2.31289518257,2.1658910864,2.24537280545,2.68077758373,2.74660798159,2.3687604324,2.38025435863,2.2971133626,2.30215529453,2.53159536265,2.45239998131,2.44604122943,2.51641740576,2.59279567376,2.6014928362,2.56370617108,2.65186534693,2.64964324514,2.79757695503,2.89471282299,2.84286364419,2.66161962176,2.89547681435,2.98309683721,1.92889495438,1.84281388673,3.82141001359,4.40107024541,4.14689584329,3.57153236525,-0.206241223882,-0.2331719124,0.183484995399,0.0137778801407,0.0437463120254,0.196252439254,0.202161708461,0.12284128471,0.0403041200699,0.453805385942,0.600914471792,0.405049892661,0.490935119996,0.415362588705,0.553920508326,0.467258143915,0.540763073156,0.859451078586,0.968466184622,0.979139187207,0.586389325448,0.512568126007,0.492861171509,0.548599509487,0.884417774708,0.874958899035,0.787783274421,0.14583949227,0.179162011872,0.114993634577,-0.282404548434,-0.316560729106,-0.473913204235,-0.410497999528,-0.324835881692,-0.351126003,-0.449082090262,-0.413696270399,-0.537684821562,-0.560530033692,-0.444054406785,-0.388341350079,-0.49608108711,-0.417060956458,-0.635517621724,-0.720823509548,-0.781332963425,-0.733196806131,-0.651863508211,-0.890818281102,-0.855778747303,-1.03606792116,-0.98904980486,-1.02750417278,-0.990389819833,-0.90760013896,-0.863028091892,0.314024256069,0.406198969892,0.437901382989,0.523528485628,-0.21596982347,-0.144371023933,-0.0361854608651,-0.124569877348,-0.0907985450563,-0.179598388401,-0.397708147771,-0.270954895524,-0.238670291741,-0.344741412711,-0.735403296608,-0.649518962407,-0.532449390405,-0.523812124364,-0.603071003645,-0.62892135472,-0.576217343192,1.17437034002,1.0539163144,1.21561463475,0.659530974158,0.857780971602,0.774602162021,0.67669860481,0.40177930115,0.527230117312,0.553085079275,0.472021622656,0.961013765301,0.85373295709,0.75319718328,2.00512206408,2.00306685303,2.36165990685,2.23190963632,2.12122974888,2.40374325379,2.19436366421,2.54447576833,2.51420574623,2.67015680119,1.9642012139,1.98070269169,2.32978026861,2.16105673127,2.28480064971,2.12942322246,1.82041064994,1.90116245174,1.27160486493,-1.44341814229,-1.4322004998,-1.54844610385,-1.5474062703,4.61785035612,4.62978651554,-0.0676121327138,0.0275123036632,0.0320103178781,-0.433713290298,-0.267508308595,-0.449759182323,-0.370085056665,-0.836580314256,-0.896478362534,-1.0136593173,-0.901661593343,-1.08501359647,-1.0362028676,4.31158705502,4.21638952154,4.07674737004,4.00092643062,3.67739497728,3.8023305758,3.92997701031,3.70686502825,3.18538054657,3.28873184697,3.17493639704,3.26233683073,3.44608992039,3.36151238676,3.37659683973,3.54563156843,3.48159504754,3.56606489446,2.42957144945,2.29556811815,2.23968105961,2.3099598809,2.42470166307,2.48614308611,-1.24888506392,-0.95381353165,-0.896445051699,-1.00692001428,-0.978490313223,-0.981817195103,-0.930300693848,-0.921188186573,-0.591182727211,-0.775178592336,-0.715252266101,-0.619075350981,0.502795487178,0.434599203898,0.472632777708,0.546092822744,0.381069781326,0.370938871779,0.44494224573,0.528880475217,0.644039208892,0.581077418183,0.596568785584,0.00497363038267,-0.177551894113,0.0160742740694,-0.0745387101195,-0.398229003204,-0.111215143303,-0.0670639078223,-0.277333644797,-0.405925317033,0.832801116081,0.935328232303,1.28422343695,1.32335106483,1.36469245195,1.48442830115,1.50945242182,1.43021892707,1.09078383099,0.989151310353,0.318194354159,0.419954169706,0.534220139273,0.557420566907,-0.669970222,-0.565969380305,-0.822701369066,-0.900481768999,-0.797610852829,-0.609247078668,1.82827971841,1.76301769001,1.79648857242,1.8930588219,3.31854234405,3.37978889169,3.45651242097,3.34384438411,3.43192307398,3.48278565277,3.16725524004,4.42361156552,4.31653894178,4.1080168321,3.99100501661,4.20088834978,4.17725146013,4.04074559373,3.94917671438,3.40172971761,3.33678108963,3.32615561018,3.42986715059,3.50672271173,3.48944981737,3.23946385154,3.23269279772,3.16068145229,3.15161436288,3.07560354551,3.07781724435,3.69646214776,3.6131920109,3.57040230187,3.59103369499,3.5213401272,3.61766438965,3.66163854734,3.67664340957,4.05266256414,3.94275042752,4.03833291127,4.08959778557,3.81937977641,3.87595586502,4.34708526629,4.32722459946,4.26386650396,4.19687331786,4.23133869511,4.51084710615,4.5896856093,4.69091018965,4.67719651208,4.42999506704,4.34795806528,4.27318422306,4.28389193873,4.36217267673,4.43338725726,4.34098294426,4.32574805787,4.23592493469,4.24982155814,4.079513006,4.17267016527,4.01062066365,4.18875981437,4.12017785632,4.03469609804,4.66734225928,4.55199341796,-1.06354187241,-1.1834410776,-0.989015491075,-1.04151683284,-1.38758124975,-1.43648705445,-1.48177983856,-1.25797757893,-1.27368357382,-1.19350947215,4.02738758344,4.04479238402,3.98794842138,3.91323198368,-0.635486865609,-0.681359679363,-0.440658245039,-0.414430231665,-0.991495436883,-1.18445914249,-0.904883167675,-0.842057259921,-1.28619202951,-1.16580719986,-0.767472948673,-0.503101039758,-0.625623806785,-0.485397142622,4.62129992283,4.56749722231,4.41937695181,4.41567133188,-1.4990354321,-1.55157970441,0.451976999645,-0.0556615390552,-0.392493024243,0.383445378636,0.771144388351,1.28913722308,1.25803280264,1.49192919474,1.42700280108,1.57047840693,1.57749386988,1.42044703627,1.34363508208,1.35309233321,1.43292221235,1.5020516796,1.50504995451,2.26685982803,2.28402150613,2.37745302804,1.85015292029,1.7178115244,1.71079696297,1.77528253133,1.789071896,1.85690292437,2.05765743278,2.19724265084,2.20173525196,2.13308910948,2.12095336067,2.05000241469,2.79892187999,2.79033417187,2.65968708728,2.73436549374,2.6512615615,2.71720482228,2.71015224599,2.63609579173,2.57026676193,2.51039517288,2.57818388907,2.39411789631,2.42776199147,2.46374217805,2.55712386606,2.52443917204,2.58393899364,2.47679759374,2.5979951524,2.83814083296,2.86254355282,2.79486930159,2.09369706614,2.00594240536,2.17928043711,2.00903910432,2.17372390593,2.091145034,3.78469145338,2.94216262386,-1.32607020001,4.40499559984,0.367336296748,-0.627945427087,-0.310248319043,-0.468042675998,-0.411375562059,-0.331754770938,-0.496382961655,-0.443213112003,-0.36480271761,-0.46837436783,-0.255362199068,-0.359651732895,-0.387246808798,-0.337088084797,0.044312020528,-0.0298053022008,-0.0350655509834,0.12095560897,0.113764201362,0.0424011647346,0.689479327338,0.674791734806,0.734967127665,1.07085999078,1.06122835543,0.413136753286,0.38360547105,0.720635619523,0.645724876893,0.78237931568,0.627927796419,0.815201736043,0.830801985933,0.767183146635,0.68739539767,-0.260054252672,-0.115397992424,-0.166328413297,-0.297939418607,0.156842756266,0.259012047421,0.296449066105,0.245372055572,0.466360368045,0.601102015956,0.50215007473,0.434762443675,0.0140385499187,0.13430259983,-0.00259431466177,-0.0560896369867,0.02379873014,-0.022106057141,0.115446771138,0.165983182855,-0.653375382359,-0.741905469582,-0.684597448938,-0.794933370812,-0.711866467986,-0.849043971573,1.04235203633,0.131609030973,0.196373648787,0.32020294278,0.362781117399,0.978685213967,1.11578249227,0.625642875429,0.746768968959,0.600271313138,0.864423432477,0.724821315642,0.870438685768,1.65428471187,1.84655999451,1.87608445925,1.02969181544,1.15780770708,1.75241889766,1.66872978688,-0.04701001056,-0.0536790493385,2.19602189709,2.09458931539,2.09832601224,1.96110130674,1.62022827513,4.25918436211,4.3813881314,4.08264683928,3.99501091101,4.30876592364,4.08798419432,-1.42030607257,-1.42991364152,-1.06081865114,-1.0906200513,-1.11452393116,-1.19743425345,-1.32274595496,-1.45329872441,-1.37644996211,-1.44661606329,-1.36612491178,-0.844586305093,-0.873209117729,-0.762791421853,-0.710009216279,-0.820255800753,-0.738594936172,-0.960968826848,-1.04262767972,-1.05484186869,-1.01174355905,-0.292911203267,-0.155138635361,-0.100555788977,-0.300160143233,-0.191208086136,1.19049305696,1.14759635179,1.21192987891,1.09551168809,1.23793918768,1.16127099,0.993734933348,0.943931579559,0.319221173962,0.197706560834,0.447636167331,0.444395355967,0.3098378331,0.181766638689,1.6193467695,1.68317416439,1.53681139957,1.66656048184,1.51405707985,1.57973690095,1.7228651356,1.80047601842,1.73615863298,1.81803404717,2.08058747928,1.97714476598,3.23011054384,3.32314677059,3.2639891276,3.30042312139,3.40492738153,3.5686738169,3.4645820724,3.47240402736,3.41986003481,3.62325890076,3.73442893671,3.79912516477,3.74567523436,3.45073474313,3.56618774904,3.54855337664,3.62206920517,3.83132577597,3.74597672455,3.8556126786,3.88686674751,3.50079202477,3.40856431619,3.41572042242,3.4853155,3.54223312338,3.79121133343,3.84707099307,3.93358648,3.96621420454,3.81950113848,3.90832754273,3.95584545443,4.01002981508,4.08593402721,3.98539298552,4.12241530214,4.07653492124,4.51426844514,4.42644327118,4.42532717399,4.60721929178,3.95078495839,3.85614074597,3.93286010542,3.79933706883,3.81871867605,3.89375774699,-0.742864902218,-0.822988258438,-0.852170070218,-0.617736079412,-0.715144737891,-0.621171107896,0.537541701952,0.698263427574,0.210783728435,0.388607510833,0.501590763523,0.377204754874,0.0762482524057,0.0421143701047,2.43433511816,2.3631858788,2.28570801279,2.27881977924,2.49661073252,2.48530090272,2.40949991805,2.42868616583,2.34297986827,2.35054477987,2.34880387822,2.4413448332,2.33596122502,2.76216593129,2.6514122098,1.16347487682,1.15354458201,1.24812889299,0.359957185293,0.380614423656,0.227078651352,0.278977144674,0.320301784668,0.247438696429,-0.246609957874,-0.144411098734,-0.109494425192,-0.157359782035,-0.225261168619,-0.279167285856,0.557250168983,0.582310334337,0.71329133298,0.623980744452,0.66716929059,0.731101886061,0.122087437517,0.0223726052371,-0.0257932684034,0.025083341333,-0.540452466831,-0.625675713879,-0.516121518167,-0.683309625397,-0.573785679721,-0.656101820306,-0.767680281847,-0.823198035712,-0.885292473411,-0.798091149589,-0.908308175556,-0.939573245958,0.958640644051,1.04182925868,0.955131727023,1.04490726344,1.22876949499,1.14588145348,1.13868479403,1.13441250523,1.12629336329,0.185042354887,0.127289429101,0.175308305093,0.293983331847,0.274957360247,0.334859401451,1.73174509798,1.91345133156,1.83079425661,1.90541080796,1.77587675873,1.80137684697,1.7108146466,1.63895839736,1.35097612788,1.28018558432,1.06600258388,1.16515741908,1.377466482,0.879840224903,0.872846031369,-0.371145867979,-0.120066179803,-0.127913662946,-0.215890507009,-0.287689448194,-0.270886431584,-0.1910695856,-0.154932259375,-0.145605350604,-0.226787831883,-0.252191472604,1.68230030231,1.37681286623,1.56069975939,1.42152904979,1.83851832105,1.86125901709,1.67368470525,1.50373521561,1.6480737279,1.45786242643,1.20957499168,0.971471689816,1.12212136371,1.23397558374,-0.491646829889,-0.459659610879,-1.22235278941,-1.31749192064,-1.34560198918,-1.16954828092,-1.36132954382,-1.3656295769,-1.14684748883,-1.29631157191,-1.20211489974,-1.29595682651,-1.20346629051,-1.03005211383,-1.06586491043,-1.08000779139,-1.16592593217,1.06107971128,1.13739190014,1.23526974744,1.0863880812,1.13062410457,1.27224855251,1.21965583488,0.721669476423,0.67895759737,0.703146123913,0.740786464809,0.822082676437,0.795198371363,0.851716368416,2.26173686084,2.1539696796,2.30486206521,1.89156125046,1.96885227901,1.88731523863,1.96821564052,3.09996985285,3.12631688784,3.0046986325,2.93445077913,2.93816125647,3.03551394811,3.07470908419,2.98222612498,3.05584546628,3.61502315102,3.56849270819,3.61925864048,3.70808940284,3.72014822697,3.76154915221,4.61862721396,-1.5601238411,-1.53953802101,4.64743204729,4.53231787146,4.52490598359,2.40019988076,2.41023204451,2.47446267419,2.844606579,2.77061681084,2.82746219177,1.3246445324,1.488504818,1.41146641911,1.54356163919,1.32446126031,1.60070508103,1.61628553474,1.21208572085,1.19958597412,1.61064311513,1.68037688017,1.57712916684,1.40517756174,1.32444508283,1.41606347142,1.60299987104,1.5598199157,1.51355281292,1.42647750888,1.40623775818,-0.477653584278,-0.390478176513,-0.500234259181,-0.429123289298,-0.333977752001,-0.318166459222,1.23106793598,1.12224600772,-1.16541947447,-1.21490028966,-1.20654248769,-1.30537927312,-1.33648940774,-1.2877235903,0.872626776352,0.810066212478,1.04664278064,1.09461623286,1.111574275,1.01588443727,0.951152178929,0.964811563645,0.996540277718,1.01705565875,0.960287612333,0.880158662519,2.122444262,2.04762243727,2.21631047245,2.22531670831,2.13493081172,2.05108667659,3.38139228151,3.26425112968,3.18098607565,3.42173580883,3.32373249443,3.19985954996,2.67787739099,2.49554497471,2.5131800126,2.6043029495,1.31536798047,1.22883310918,1.2193749495,1.36639377936,1.31653827158,1.47656907774,1.51205661958,0.933325223584,0.595463208887,0.723030084148,0.877432472211,0.0298422592345,-0.16199026811,-0.0344806550956,0.949809782811,1.25509529718,1.09529665407,0.455676727961,0.918443751186,0.764769966395,0.781645815381,0.859169959869,0.824966156277,0.901374151244,2.6622975868,2.7216626959,2.73839525103,2.6346983124,2.55983375361,2.57051379826,1.39240838323,1.47884486512,1.55416196771,1.54264263463,0.814624667573,0.596462236845,0.783134844131,0.532719402577,0.383561791754,0.303288808399,1.35515650192,1.38169770165,1.44172740714,1.45643113644,1.28344956598,1.29509084431\n    \n};\n\n\nconst int NST1596 = 1596;\nconst double AREA1596[NST1596] = {\n    0.00898161472215,0.00793682351914,0.00783726493283,0.00782135123312,0.0074515533379,0.00795027285775,0.00754964557486,0.00754709851389,0.00789529320983,0.00779422962641,0.0079598426657,0.00781208999869,0.00782222139311,0.00793534499444,0.00785454321021,0.00787019625809,0.00789994885333,0.00790234378106,0.00789239710852,0.00794186991099,0.00902995471139,0.00740452989968,0.00771354178207,0.0077928398572,0.00790263620368,0.00792652528232,0.0079634858516,0.0079683816427,0.00785004525586,0.00788545009992,0.00786835858881,0.00834110622304,0.00760578122914,0.00817281886622,0.00798607134963,0.00867766491901,0.00811427399573,0.00752765823815,0.00732914423251,0.00805994337421,0.00763119737763,0.00798127102672,0.00786152117516,0.00776565252017,0.0077537397352,0.00753720328731,0.00772085304708,0.00752374898059,0.00748862773388,0.00780995092341,0.00795562496885,0.0078777278841,0.00800603742547,0.0077889175066,0.00784319815166,0.00801618045518,0.00777821788753,0.00797682885569,0.00782875378278,0.00768468291508,0.00810254353271,0.00772306506771,0.00795275629356,0.00772903805536,0.00797767947585,0.0081371903108,0.00786369969534,0.00789445354644,0.00786416718552,0.00797746159238,0.00786882936882,0.00756299057667,0.00814079455955,0.00784613639084,0.00780160697227,0.00792102959074,0.00745670930428,0.00748200498895,0.00782134771026,0.00795317289266,0.00767401433596,0.00801610949628,0.00784972361624,0.00780643416237,0.00786437909415,0.00783814293829,0.00780153681811,0.00783024067183,0.00800366105568,0.00780102084296,0.00795358205916,0.00786317933465,0.00787993546148,0.00797125353596,0.00791593752927,0.00781347555591,0.00785045788102,0.00796436721636,0.00782778116135,0.00791269597233,0.00784978175952,0.00750260869672,0.00756237168506,0.00774604241931,0.00754645494411,0.00804202871288,0.00791089290936,0.00774589772623,0.00798192053622,0.00782458635071,0.00765570924067,0.00795675883932,0.00774984513443,0.0081034102948,0.00792981152629,0.0080613304939,0.00771900213821,0.00765215407236,0.00828701935049,0.00754954439258,0.00794568879158,0.00788818566097,0.0078373845923,0.0077606510394,0.00801362272013,0.00774636913226,0.00781870563392,0.0076398073947,0.00799830347767,0.00762493512953,0.00786456002171,0.00845775861574,0.00750382673007,0.00858935446889,0.00748497121451,0.00868060566407,0.00753618968285,0.0075875387935,0.00778913765524,0.00797809433969,0.00797912331517,0.00787986329649,0.00793431323721,0.00783634585003,0.00800865463389,0.00775140401935,0.00777637810634,0.00932436942175,0.00802639600159,0.00776616512533,0.00802696140042,0.00774012409232,0.00800807369337,0.0078590915576,0.00791011882051,0.00786170636672,0.00828292836645,0.00777633158101,0.00800169814087,0.00753012056473,0.00745626449743,0.00763095847552,0.00768240110701,0.007882909447,0.00795223006364,0.00844685363646,0.00817073762648,0.00767568915754,0.00795096123014,0.00776221396848,0.00787829201953,0.00740190807849,0.00793489135432,0.00784633567435,0.00786262881205,0.00797874086825,0.00778118816078,0.00797728411855,0.0078716129665,0.00798654519255,0.00780531895904,0.00753596230686,0.00744429925992,0.0076607193185,0.00774911272191,0.00793339659714,0.00740683174483,0.00806700677297,0.00753235100743,0.00753077756248,0.00766197177015,0.00789339400098,0.00762443342574,0.00809812088108,0.00743495832411,0.00765661224421,0.00802196937256,0.00830425266874,0.00793845943583,0.00784417574329,0.00784784333255,0.00791213374715,0.00794246834537,0.00786751688385,0.00804449234747,0.00775731454438,0.008030697364,0.00807251910468,0.00775881960301,0.00778746496758,0.00782473640615,0.007945568543,0.00799390170644,0.00875599370939,0.00745258445338,0.00829210518705,0.00788561252421,0.00775102019069,0.0076694224547,0.00804980943045,0.00759057946423,0.00776877192468,0.00797931728957,0.00836134361084,0.0075475440366,0.00832868379063,0.00809217764196,0.00775957594264,0.00791863332924,0.00780502429122,0.00801135803047,0.00786747813958,0.00825691008205,0.00789820576041,0.00791440164334,0.00793424750219,0.00785157363822,0.00801769266529,0.00765987992365,0.00780635463159,0.00795219325783,0.0075689187208,0.00739551099448,0.00907639089007,0.0078260776299,0.00773341920624,0.0079722063769,0.00787388115359,0.00780895475646,0.00796152673288,0.00798356026236,0.00785421335867,0.00799744127467,0.00796340594202,0.007829251001,0.00762403098454,0.00804782958036,0.00777788739819,0.0075096304737,0.00793149115036,0.00769028513404,0.00778335690256,0.00796203527193,0.007925155324,0.00779841618373,0.00784956435458,0.00787167394594,0.00752322586812,0.00755844482713,0.00809091779818,0.00794891675652,0.00795135008896,0.00793707900216,0.00781816800954,0.00789497734284,0.00787187079194,0.00782029853949,0.0079461099228,0.00790998607656,0.00782944453148,0.00774313176919,0.00795241599832,0.00809771039586,0.00892257020693,0.00751398019817,0.00746755895482,0.00781079128685,0.00807399739546,0.00756744986881,0.00781255259201,0.00780828986893,0.00944582149556,0.00748813325657,0.00742288160874,0.00800543670324,0.00740368784394,0.00819542193622,0.00791146620867,0.00795151507219,0.00795712187195,0.00781913685054,0.00797678946032,0.00779573624692,0.00790673285202,0.00792670286958,0.00794480763419,0.00776590104756,0.00727388418052,0.0075698372814,0.00741955680551,0.0096109326308,0.00792843572582,0.00771041887396,0.00819574647762,0.00750290696944,0.0074698479175,0.00779150364274,0.00741307807809,0.00793352090197,0.00802580338751,0.00791569994824,0.00784018905772,0.00797038594176,0.00785248080886,0.0078506498152,0.00779472918281,0.00779643016091,0.00737377112012,0.00785238843586,0.00789451127617,0.00784126768817,0.00795431833201,0.0082234266937,0.00771060330347,0.00793355694875,0.00779792023466,0.00805045565278,0.00794748677954,0.00775155340021,0.00801411954029,0.00789539341334,0.00855434140899,0.00733280329735,0.00812061519702,0.00800045776998,0.00783685871514,0.00794954701622,0.00779897618126,0.00777321445654,0.00784777676483,0.00795364610248,0.007826522324,0.00785729207921,0.00809521460806,0.00793979288522,0.00783279011606,0.00795435258391,0.00770924654185,0.00795551688282,0.00780138685649,0.00792943594364,0.00792048928009,0.00786143748922,0.00768777758566,0.00809478380802,0.00761513533244,0.00780289579129,0.00805241642981,0.00780379211225,0.00789110111037,0.00793120656405,0.00796395054774,0.00789386472916,0.00743000792658,0.00931785859398,0.00763248831567,0.00780212119099,0.00756086023991,0.00781262986502,0.0081827823486,0.00764551917382,0.00769529438687,0.00770223589443,0.00775220230086,0.00793646489298,0.00804806048146,0.00767416234391,0.00797547330996,0.00752515836204,0.00803176294025,0.00789609638356,0.007523871932,0.00796394103641,0.00781242242407,0.0079070064246,0.00793710195838,0.00790428340276,0.00780838720027,0.00800880157937,0.00789634794017,0.00779151344865,0.00787690597472,0.00792805369793,0.00783057339385,0.00755287763165,0.00779637069873,0.00789483839103,0.00786926235568,0.00787637360133,0.00791162299064,0.00791572979029,0.00793543893778,0.00780063436585,0.00782490068295,0.00795674518041,0.00755883938052,0.00742982843782,0.00769439588882,0.00774046624811,0.00796631391351,0.00781558098516,0.00801045122604,0.00797824586066,0.00789835789793,0.00788448408665,0.00733652782533,0.00780227649692,0.00800407846594,0.00784479370249,0.00799069358939,0.00781332706339,0.00741132535669,0.00751145041221,0.00779590165208,0.00775061931008,0.00800919341037,0.00794785202086,0.00777812537784,0.00801303349629,0.00777926750626,0.00804634837315,0.00794734573526,0.00788408344207,0.00790085743777,0.00790130229524,0.00773913715038,0.00792327119343,0.00785010707717,0.00797548317015,0.00779822051433,0.00787300251216,0.00788834098145,0.00795495765438,0.00785685827125,0.00764020297288,0.00781310969381,0.00775881822039,0.00798074955596,0.00807224167424,0.00808076165428,0.0080121317127,0.00775874851292,0.00784296327073,0.00795505190089,0.00775004036848,0.00795925736437,0.00772895261493,0.00803449609705,0.00784700418724,0.00790827515684,0.00787524423953,0.00783089707125,0.00778538647788,0.00783502220014,0.00783954006314,0.00776027908066,0.00799236785917,0.00791815273808,0.00783285578282,0.00801969253297,0.00744770300373,0.00749889613953,0.00788187044379,0.00802256378544,0.00781929136026,0.00798349093721,0.00790310764457,0.00790935382644,0.00894667577537,0.00740660785219,0.00789554146633,0.0079301441629,0.00774813536482,0.00801427525687,0.0078557966207,0.00793049525545,0.00783976228579,0.00783919395448,0.00826391026028,0.00758103709196,0.00793628264197,0.00791600464083,0.0078620371548,0.00792691200604,0.00804609707436,0.00782794900684,0.00791010184384,0.00778696421264,0.00801438304171,0.00781595083935,0.00822742672938,0.00814513611125,0.00767736130958,0.00809060251919,0.00785131355085,0.00784885143289,0.00784780342493,0.00805788594668,0.00772617345362,0.00799089919533,0.00813293975282,0.00918712298985,0.00774469869069,0.00815496447822,0.00736808301975,0.00778495073601,0.00790054335922,0.00764053298041,0.00773407211814,0.0079158742946,0.00790838764107,0.00778219459799,0.00796833868911,0.00781277284376,0.00747769619482,0.00747438536289,0.00743564057436,0.00789114673637,0.00772928582434,0.00812844739038,0.00763495710114,0.00743666336433,0.0076677230459,0.00950346621535,0.0074863417532,0.00730013077296,0.00803158369592,0.00787475909549,0.00786854898161,0.00789044270878,0.00789180483787,0.00784555760611,0.00792398924501,0.00789368113344,0.00750357995486,0.00812063335605,0.00746649840081,0.00751492553462,0.0089071357774,0.00771115245739,0.00807264280049,0.00797335985552,0.00802898159655,0.00757503374798,0.00823812999792,0.00777944815652,0.00777808043909,0.00796676307564,0.00776785510954,0.00797785078258,0.0078459336433,0.00785082490509,0.00792543475211,0.00878269442426,0.00834119776826,0.00744254724837,0.00777390492164,0.00786935812821,0.00798904035843,0.00789118336677,0.00789762396944,0.00786082154454,0.00746502643529,0.0075353760945,0.00815250395729,0.00873447183251,0.00752714311934,0.00790760517078,0.00785923732888,0.00790647895631,0.00780784882497,0.00782148861247,0.00796268815885,0.00791238539914,0.00782032620954,0.008053967829,0.00774480400065,0.00781383535449,0.00798692886326,0.00794504695302,0.00783135950991,0.00785456467681,0.00791952286654,0.00779128616865,0.00791608041964,0.00756161434532,0.00831696159165,0.00761751466561,0.00781282799321,0.00805488464172,0.0077902546926,0.00795145137857,0.00794952662686,0.00794002593938,0.00781560696476,0.00785194952938,0.00790040956457,0.00777146154469,0.00787048175755,0.00796262000429,0.0078205044852,0.00793512716227,0.00788720966311,0.00788063572965,0.00784686782722,0.00773573941672,0.00741809381588,0.00814117477468,0.00781175295198,0.007929059676,0.00786775801672,0.00790399292116,0.00777058521706,0.00795302226801,0.00782431422204,0.00804708048399,0.00782200904316,0.00795714170502,0.00794132284062,0.00773790023507,0.00743274151104,0.0077510722545,0.00803276794127,0.00894762129206,0.00790922249173,0.00774098877011,0.00781573380639,0.00802358392978,0.00779664983708,0.00792090365217,0.00777786265477,0.00795955736791,0.00780691737944,0.00798519691411,0.00785834384104,0.00787244617958,0.00774998236824,0.0080664767601,0.00787275741489,0.00778933341074,0.00803310867477,0.00794488000906,0.00780566833411,0.00801680981646,0.00785878288573,0.00788775372838,0.00791592370534,0.00786084684302,0.00779957208287,0.00796598283068,0.00775779664267,0.00769011701941,0.00804812789254,0.00792314033744,0.00792169488155,0.00785339815225,0.00788855772636,0.00790668692376,0.00786101687101,0.00801586037214,0.00806529783275,0.00770438004729,0.00779296303467,0.00816211733094,0.00726657586694,0.00786840909776,0.00755696353182,0.00747483972999,0.00755081404953,0.00792050908866,0.00823049820048,0.00753172323464,0.00778695999128,0.00812792598941,0.00854194676723,0.00783630970131,0.00792664487316,0.00790016911123,0.0078751810745,0.00781121367148,0.00788255911903,0.00788627300556,0.00786720767714,0.00785365628202,0.00772860765406,0.00816788480422,0.00737405968969,0.00784498559951,0.00791835710094,0.00784915207285,0.00798633043351,0.00793792507423,0.00781581372087,0.00803173064833,0.00750655702712,0.0080505293692,0.00777475665104,0.00801123284673,0.00771267608501,0.00807473008243,0.0073882381082,0.00756890433859,0.00798196941161,0.00771461468506,0.0090811913529,0.00782268876176,0.00781746770255,0.00796528525763,0.00785957092211,0.00793665183472,0.00782285518583,0.00748313268358,0.00798748891948,0.00774604889165,0.00772081481949,0.00800825506085,0.0081821286143,0.00814534332053,0.0079991985802,0.00771197220557,0.00832230163434,0.00737619732843,0.00799112779667,0.00776277067556,0.00780560993041,0.00794638172018,0.00794134626331,0.00780741096034,0.00794665699244,0.00781369707562,0.00798367681495,0.00782957420915,0.00745451893812,0.00743206538487,0.00744905415183,0.00781191091561,0.00792450576746,0.00789060268258,0.00790430470954,0.00801011595644,0.00796937343604,0.00781066668512,0.00784703654803,0.00793950518681,0.00784282904539,0.00793347393029,0.00794865712157,0.00783640430103,0.00794752827819,0.00786355876301,0.00787028654145,0.00791489608808,0.00790957394225,0.00789802193959,0.00787996798858,0.0078681939893,0.00772223191273,0.0077809139666,0.00778133925617,0.00782700195031,0.00783276876007,0.00791856631682,0.00798846558538,0.00786143449408,0.00747044811096,0.00761611020966,0.00834008038573,0.00753999393388,0.00780693431652,0.00817863618827,0.00789037846456,0.0087700012967,0.00804555680542,0.00746602710776,0.0078693505795,0.00816792967008,0.0091024413166,0.00745160549307,0.0080887888476,0.00834017377377,0.00834138796984,0.00794864274188,0.00772591762379,0.00795565053043,0.00758516647976,0.00762033156375,0.00767323777416,0.0074980430554,0.00752020484473,0.00746030773166,0.00943604109294,0.00760028351396,0.00777522786961,0.00780625992098,0.00740250921599,0.00803746157009,0.00783804507342,0.00792652044886,0.00782926610912,0.00790022776178,0.0079302616295,0.00799464837436,0.00780190933088,0.00809404872772,0.0077359929001,0.00827895268203,0.00803082772333,0.00802704599905,0.00776969440025,0.00787197447673,0.00783545571484,0.00786569063849,0.00916045807513,0.0077044403654,0.00743021626476,0.00785460738642,0.0079557181882,0.00783350881472,0.00801303948938,0.00779831916188,0.00791742374803,0.007604947549,0.00798228869901,0.00780592738123,0.00821750011533,0.00795454969436,0.00753427938425,0.00781952087185,0.00801596025667,0.00767682951005,0.00783614863889,0.00845868792252,0.00768785828736,0.00799132366591,0.0080702811605,0.00756180021394,0.00748200012045,0.00731962892396,0.0083645348747,0.00822291738553,0.00743072996493,0.00792501343223,0.00784359167875,0.00784309495434,0.00780711042074,0.00795236939857,0.00789349649792,0.00785914600542,0.00789792495338,0.00790318485156,0.00785847990547,0.00798568654126,0.00778049792202,0.00791749574666,0.00793140578968,0.00779862332357,0.00788885706679,0.00792079187632,0.00784626086286,0.00779183737282,0.00783431727761,0.00795384097457,0.0076603435856,0.00760144838988,0.00828592524492,0.00738559621774,0.00779837468304,0.00789565902114,0.0079318101836,0.00783403839738,0.00784578077018,0.00780583775505,0.00797608851022,0.00807844197095,0.00762035857166,0.00773641018392,0.00774405886815,0.00778419582049,0.00799708073865,0.00768160664771,0.00776079180715,0.00805845979982,0.00744006565337,0.00794800105396,0.00791539332046,0.00785834850326,0.00792200208045,0.00795726049855,0.00781353587977,0.00784581383,0.00788709456756,0.007910466034,0.00784939571497,0.00789251397224,0.00788623243936,0.00790413395993,0.00786195357812,0.00779735990497,0.00791702101578,0.00803181768693,0.00773652569428,0.00802587983961,0.00791499470013,0.00782467832068,0.00785668205417,0.00784455971593,0.00778648764146,0.00798830567778,0.00805163770882,0.00796063955917,0.0078165957992,0.00795851231203,0.0078321396043,0.00775388558355,0.00793299675051,0.00786266613181,0.00783792704336,0.00771450682605,0.00782359563289,0.0078924735016,0.00805260580878,0.00794087236314,0.00785600141789,0.00789465640694,0.00764128106524,0.0075169633459,0.00821925789034,0.00772713584325,0.00782976277977,0.00784352703441,0.0079334413492,0.00781629561513,0.00794112852773,0.00776075207975,0.0080248735478,0.00804301853881,0.00781163107054,0.00778566621131,0.00798376966736,0.00796425412421,0.00782101318948,0.0077832213642,0.00789829509242,0.00802460762447,0.00790946820831,0.00781253682366,0.00793341209148,0.00781940568387,0.00782360857194,0.00784275030529,0.00792436062646,0.00739423125791,0.00810027908285,0.00744547038023,0.00779181561309,0.00805224012698,0.00764750791094,0.00777526636411,0.00798933151788,0.0077228314432,0.00801255282202,0.00785819914145,0.00795202704805,0.00934032976881,0.0076042933487,0.00796731976407,0.00739231820895,0.00824569061529,0.0074054835819,0.00741260133575,0.0077437702048,0.00786076331993,0.00778796193783,0.00777248817827,0.00756545808675,0.00824722920813,0.00762649787713,0.00785147871896,0.00784356527502,0.00792980413022,0.00779090555875,0.00781359065032,0.0077998883778,0.00795493878806,0.00799269826364,0.00778678896015,0.00788297096595,0.00788850635671,0.00793502832778,0.00780031674882,0.00794875301675,0.0078422389772,0.00794990909881,0.00785045010388,0.0078358154408,0.00795693050619,0.00781925613869,0.0079731479906,0.007915136338,0.00780820321458,0.00778526383766,0.00797094587883,0.00816787512008,0.0080381874042,0.00799188468219,0.00780670056845,0.00801371922923,0.00783650126085,0.00770810270451,0.00811636727853,0.00750492652289,0.00803962786242,0.00779418953792,0.00772758135897,0.00791587532138,0.0079384384868,0.00784914947499,0.00785854056041,0.00802690575096,0.00777998381322,0.00795586069881,0.00784983800314,0.00785847380267,0.00777769344458,0.0080222051483,0.00776141775842,0.00784468176228,0.00796758108709,0.00862410752633,0.00751955720686,0.00751879462776,0.00795626681719,0.00786012151699,0.00778264242388,0.00800989690213,0.0077234995504,0.00776051244607,0.00755588234718,0.00811747762322,0.00810341652426,0.00762069794227,0.00772439356385,0.0080367006034,0.00804664054621,0.00760298154158,0.00762063439351,0.00801617444646,0.00789896357144,0.00786638503968,0.00783500438893,0.00794697267129,0.00797967166151,0.00788991431228,0.00786780819023,0.00786329907347,0.00782658592608,0.00803076065866,0.00761033521219,0.00798435111909,0.00778723252434,0.0078133128218,0.00792244262248,0.0078153065182,0.00800020733483,0.00749426700794,0.00775393513254,0.00774116356684,0.00795062000193,0.00784623124681,0.00791007971193,0.00782135692469,0.00789335739308,0.00787289502923,0.00787768533614,0.00770463205381,0.00801804591068,0.00778753722538,0.00784051714102,0.00807174312434,0.00771587628385,0.00758849433959,0.00781491429931,0.00787510127507,0.00823163031868,0.00791934818377,0.00847222005199,0.00743774389723,0.00746791021826,0.00761665988774,0.00778718744084,0.00751229853668,0.00796540047514,0.00777909964982,0.00784245005859,0.0080055918642,0.00796499706837,0.00781729003539,0.00794607770732,0.00772189720085,0.00795050963454,0.00754562891341,0.00850170632401,0.00768412928721,0.00786216995385,0.00789981188138,0.00787716514181,0.00785577827606,0.00784936507325,0.00794134424797,0.00790502320069,0.00785005097931,0.00792037232615,0.00786722562337,0.00788648429141,0.00790492473615,0.00784193211937,0.00794473214094,0.00787302396574,0.00788998427499,0.00790294087577,0.00791490596191,0.00786396539584,0.0077911446359,0.00798303345523,0.00777311807198,0.00794226702608,0.00781231476204,0.00786539044973,0.00782280124327,0.00798399601519,0.0077468226289,0.00739211833529,0.00744159432726,0.00769272328417,0.00746126715607,0.00928073849411,0.00728178742481,0.0082576573793,0.00779152819352,0.00771157854839,0.00812102720178,0.00748167459215,0.00780519620945,0.0077081413799,0.00803488128225,0.00790785660056,0.00772365121095,0.00785056375646,0.00791074538329,0.00761290344378,0.00810452804037,0.0078250129133,0.00781535782041,0.00793175341011,0.0078106073748,0.00800561961881,0.00792614399092,0.00799500969624,0.00783832313089,0.00788464127967,0.00793520646451,0.00791765198443,0.00787106626999,0.00780315334719,0.00783334735819,0.00790736512764,0.00786188161401,0.00767280737248,0.00769421365591,0.00818683783636,0.00819842870783,0.00791090240952,0.00802157923355,0.00795687672081,0.0077454098971,0.00774724853437,0.00803693369654,0.00784799294748,0.00793622014998,0.00793355724673,0.00785375138942,0.00792599963111,0.00787950088134,0.00786800894469,0.00793671402032,0.0078939067884,0.00787203790062,0.00785632856099,0.00784820211696,0.00794313363593,0.00783122551738,0.00790297280976,0.00789149902286,0.00760671048206,0.00915063410599,0.00750316262689,0.00750265490106,0.00794585331599,0.00779136210299,0.00782060869232,0.00787323002182,0.00740460938884,0.0074578533547,0.00918978730834,0.00807458828801,0.00800728735177,0.00748203287986,0.00818617013651,0.0073300299701,0.0074483147966,0.00787658878072,0.00885043496912,0.00756229394576,0.00812974856644,0.00762970000403,0.00841960166729,0.00796307930609,0.0077995246139,0.00792083247947,0.00786302170016,0.00791670529317,0.00752425459254,0.00751746263343,0.0075157378518,0.00779757473156,0.00750992986189,0.00739735815646,0.00814373351178,0.00768814526,0.00804402066718,0.00741201264867,0.00829280486981,0.00779909456205,0.00797955890757,0.00781152796158,0.00798322938579,0.0077962782489,0.00799503502763,0.00778476402719,0.00801414911705,0.00778207745586,0.0080035261637,0.00799958625721,0.00779191626486,0.00790443769045,0.00785328660404,0.00784772312551,0.00790032067833,0.00783462640542,0.00793124583671,0.00802236040955,0.00776990473501,0.0080621493485,0.00766637392512,0.00777797374016,0.008020324438,0.00802066147777,0.00777825742808,0.00798612141405,0.00778494890801,0.00802139898742,0.00776059283736,0.00803413090766,0.00777731658168,0.00790433236287,0.00796448997238,0.00796054580579,0.00780429042927,0.00785193455906,0.00783934533118,0.00747716516935,0.00853626566336,0.00779741705303,0.00754496082338,0.00793422026839,0.00785885924877,0.00783761505978,0.0079052003565,0.00801874713404,0.00777763434831,0.0077672399186,0.00792789554881,0.00788169947103,0.00791055983078,0.00758633279888,0.00947950797006,0.00748212737919,0.00794929047555,0.00813079596078,0.00727864872959,0.00783058756235,0.0079183879984,0.00781014958081,0.00799376331315,0.00781020342795,0.00794961081156,0.00792948322962,0.00788505718217,0.00787954119594,0.00792423971423,0.00784774293483,0.00791256245098,0.00784113674254,0.00792762748743,0.00792532492544,0.00782412243565,0.00793738326271,0.00783423042605,0.00780009701745,0.00797346650136,0.00751572235093,0.00811945747897,0.00742355996054,0.00905598621981,0.00738634697704,0.00802908097751,0.00784059393298,0.00777057744083,0.00784893913386,0.00793819316552,0.00795825832928,0.00754837121835,0.00815398501008,0.00746947751106,0.00780753380437,0.00921696112216,0.00799927996439,0.0074298985325,0.00806514821116,0.00808189295041,0.00761994031969,0.0080407288147,0.00765569703945,0.00833822777789,0.00824604342379,0.00778630324924,0.00794804891797,0.00776114249294,0.0080096370961,0.0079387120651,0.00783116350239,0.00779213160958,0.00791685953512,0.00796788771445,0.00774454726808,0.00787567047442,0.00793653778957,0.00776367133852,0.0080069633507,0.00779652667877,0.00784441139208,0.00798772779016,0.00768070580055,0.00750821749079,0.0075103310307,0.00766231484178,0.00732880719697,0.00772435765697,0.00910371145291,0.00794785880169,0.00743372554331,0.007995182638,0.00774970359998,0.00797209818927,0.0078125898921,0.00806102354583,0.00779449357928,0.00758969498271,0.00768003895753,0.00843959953582,0.00780028646027,0.0079689443456,0.00775644138821,0.0079445063522,0.00790943039102,0.00788434160022,0.00784443446437,0.0077461508836,0.00757820629947,0.008463107985,0.00764818696095,0.00777653668776,0.00797532347408,0.00747417609133,0.00819212409576,0.00753568601249,0.00849590871768,0.00775419787426,0.00806819788002,0.00767328650142,0.00777316583985,0.00818195706458,0.00763986117175,0.00804157057308,0.0077426947953,0.00776009011314,0.00772212661359,0.00766982499761,0.00810064436947,0.00779453726987,0.00794678209556,0.00799059841488,0.00772302549161,0.00781006348589,0.00781865026698,0.00759667331496,0.00777874210681,0.0078740990624,0.00754403538148,0.00788169463953,0.00789798222866,0.00787531995411,0.00792321483812,0.0078577487178,0.00792090217265,0.0078271981662,0.0079088783514,0.00787946108295,0.00782425059332,0.00789224560719,0.00781829754446,0.00787412103766,0.00787755949006,0.00821507751134,0.00778751398644,0.0078190565905,0.00789553825817,0.00800674686308,0.00769034368886,0.00788784608431,0.00785856897352,0.00795237731769,0.00767638804386,0.00819436501518,0.00755587925327,0.00776449615566,0.00781026036136,0.00808132655964,0.00810777247683,0.00853611199943,0.00770311100334,0.00751396538805,0.00799159141309,0.00796059252975,0.00772608270099,0.00769003458039,0.00781918263445,0.00786695900415,0.00790056747279,0.00795865543371,0.0076228958275,0.00830468546277,0.00763811233704,0.00777625271811,0.00780842439258,0.00801136992103,0.00770262397204,0.00810960660613,0.00776138970492,0.00781538739563,0.00781554262924,0.00797703221904,0.0079008357367,0.00788818726365,0.00789152196312,0.00789659933572,0.00744539501645,0.00767114756518,0.00745909911281,0.00747858649294,0.00807151067345,0.00797204961926,0.00776589592584,0.0078989281127,0.00787741388473,0.00784332179375,0.00781948402257,0.00772654345552,0.00754909355056,0.00799220112999,0.00769336813385,0.00798943652177,0.00786173734567,0.00792609530051,0.00787392523422,0.00787218176903,0.00792568692507,0.00786554618988,0.00786704712941,0.00782127634166,0.00796543735385,0.00789602962058,0.00790499847869,0.00783728055532,0.00783263428074,0.00782184384362,0.00794622462833,0.00795585875889,0.00772487545037,0.008147614614,0.00746920628012,0.00751619204365,0.00750732119292,0.00795183654413,0.00787588072183,0.00786057516225,0.0078060717454,0.00771949784601,0.00813536553471,0.00778716379402,0.00796125328993,0.0079723973733,0.00781558895083,0.00783488330055,0.00798160264582,0.00828133286928,0.00781785482322,0.00798737139131,0.00754687381335,0.00807000838106,0.0076665209269,0.00783526185521,0.00776123267256,0.00800093197903,0.00777155770378,0.00794223570567,0.00790407675357,0.00786620512486,0.00778710556539,0.00770558549321,0.00794928589739,0.0078555716816,0.00778915008503,0.00800913809054,0.00798366668218,0.00791766180164,0.00793537590768,0.00788686763923,0.00802387244036,0.00792917000448,0.00780155962906,0.00783067491742,0.00799055122464,0.00778096221095,0.00793586582683,0.00782472010571,0.00781783822769,0.00797138509473,0.00781493827489,0.00794755916565,0.00797154250706,0.00796498263202,0.00783635691197,0.00791600820946,0.00765477888501,0.00747418895083,0.00831077279529,0.0078980483916,0.00786325278881,0.00805669978545,0.00793828667631,0.00784477918472,0.00783403498389,0.00792218650684,0.00786495815678,0.00792034680265,0.00787030643408,0.0079089602233,0.00789168315541,0.00789062851105,0.00773521531167,0.00797271523231,0.00778098753326,0.00774544856647,0.00813492264146,0.00749762520459\n    \n};\n\nconst double PHI1596[NST1596] = {\n    1.65154573632,0.878777661509,0.801075377441,0.72379754617,1.64925334578,1.58340147682,1.52949679808,1.4625426712,1.65479418575,1.63361181874,1.68300209764,2.04552171761,1.6393151619,1.56567431819,1.50239773682,2.12603510904,1.75793573253,1.81152025462,1.82252455386,2.25112608671,1.24161407244,1.30105465723,1.37297074303,1.17839887022,1.19618889749,1.27244203313,1.10521449661,1.27073243314,1.34650424944,1.24470819778,1.29623089613,0.882543287182,0.954531964847,0.954893761187,0.617716506095,0.545797171586,0.606149206889,0.5400098331,0.814314337888,0.980424352552,0.916421196486,0.803949334848,0.847758173776,0.79107020231,0.847694329049,0.491258004323,0.3670622599,0.415764806778,0.229787585604,0.906515571303,0.828973362547,0.568369737488,0.95450502041,1.03014867838,0.971167165092,0.85530553917,0.930947424948,0.986291555902,1.23982807887,1.02770215741,1.03353229334,1.10589860068,1.16965822255,0.627095602804,0.702193588992,0.614234756789,0.963950531404,0.722657723603,0.654379995299,0.1225121376,0.196565962158,0.566359008628,0.505165969751,1.19469936764,1.28762206911,1.061188819,0.955979865043,1.03638126746,1.18002702475,1.16368577921,1.18565212855,1.21129897842,2.09268421881,1.80632196774,2.03038381199,2.05633342179,2.3709960333,2.25436677091,1.71513056656,1.30569213732,1.44228241541,1.58062915171,1.51068414127,1.29213885772,1.23710447214,1.22824417626,1.77814153823,1.77227494192,1.70622436782,1.02987463363,1.03070397102,1.19857301646,1.14375636816,1.11080007775,1.01571501951,1.13833688403,1.09145245972,1.34243851562,1.26589425662,1.21584685339,1.18575550233,2.1225615827,2.14200472186,2.06887831693,2.02123758964,1.96221221728,2.03730589135,2.14166559684,2.17151810968,2.24130862074,1.83723576638,1.94372182819,1.91596108084,1.72770177972,2.64018971062,1.59303157286,1.59482895621,1.53381708321,2.31697067419,2.34913773948,2.22777606371,2.29889675471,2.23796011849,2.17334324365,2.32125335271,2.2604687701,2.27892148034,2.29254373058,1.78540987639,1.71679122232,1.78081157507,1.7668288734,1.84111144549,1.84843098081,1.79090881775,1.71955723333,1.64143407166,1.87995013882,1.92382450621,1.29075960756,1.24025077467,1.1682196444,1.74048664066,1.43774933048,1.38925147974,1.45283046853,1.71958219606,1.41489310958,1.34241012209,2.1119709172,1.58438751674,1.61336958718,1.55579956963,2.7563713405,2.77809612045,1.6280304357,1.60159508933,1.65829288142,1.73258010308,1.76177430531,1.78679162958,2.18134246223,2.19646109593,2.26001893433,2.19995642622,2.86291251444,2.66935176914,2.74085633811,2.32993554115,1.68030708404,1.18087793772,1.11614788587,1.24899235618,1.61156674916,1.54804127764,1.42758945098,1.32558173864,1.45688565753,1.40804323749,1.36992692263,1.4316052133,1.17523562448,1.11013037338,0.812084674409,0.747209010244,0.679951338026,0.679622345856,0.879022732888,1.36061000006,1.35853987702,1.29023332212,1.22304164603,1.15937397774,1.15606896792,1.02299093794,1.09502740448,0.672241546068,0.807381421758,0.809189321317,0.74280487904,1.0089387407,1.0320415703,0.931792379321,0.880569739459,0.909967239859,0.983444113951,1.04644726928,0.870599938005,0.901225580468,0.909081059071,0.97389841302,0.983316756578,0.919264088746,1.04580013047,1.04586578251,0.724527851125,0.303147373591,0.757271896531,0.96855084258,0.999454143088,0.268000956479,0.402480961388,0.308164483118,0.671140300425,0.591340077793,0.490822845123,0.433214362422,0.354590970423,0.33950053444,1.02571505103,1.24821004793,0.618270731575,1.00386966476,0.938365700439,1.12777755813,1.05063504589,1.11956334348,0.85313713143,0.273677472928,0.345474820437,0.533349666254,0.472718249518,0.605018326012,0.570994696582,0.494285359093,0.585857392401,0.583026459438,0.514243967365,0.264808957854,0.134640134648,0.207128588702,0.345408852336,0.419715146912,0.253751060235,0.381471023997,0.251651398457,0.323145979136,0.322682824376,0.443349822848,0.379041654277,1.31557554506,1.38106715664,1.27010189859,1.30362564728,1.13926135597,1.01975369468,0.901398934577,0.940971950653,1.05975074258,1.37539052637,1.16693864666,1.07662725503,1.09219482126,0.850702641444,0.917735477835,0.984346738357,1.13842234414,1.11678041529,1.04571592851,1.04301321952,1.088824801,0.842875042493,0.801208271774,0.934692613147,0.965389057254,0.848867318337,0.92120696027,1.8483931697,1.91458887595,2.12988144061,2.14979024471,2.22291794771,2.17919902766,2.04311074648,2.10484948745,2.01984426068,2.00318649344,1.6889978467,1.67615369332,1.56189145492,1.62010949701,1.95462336277,2.50698052103,2.52220829266,2.5900434673,2.50499151227,2.38588402722,2.45499440571,2.18964272629,1.85502769432,1.50880603683,1.37163835707,1.30298957942,1.57797209176,1.30829222795,1.32593141671,1.35955000255,1.49929969148,1.43768805801,1.36688497015,1.10483823689,1.09880685934,1.18294598001,1.10807445779,1.09957813613,1.049945914,1.07026279864,1.36811356025,2.62173258561,2.67454056131,2.28996902644,2.26904579195,2.22612633361,2.21792062029,2.04059869906,2.07434603757,2.14938844564,2.07766823655,1.99261765401,1.4776702038,1.55483269509,1.602999062,1.44959683172,1.69649073671,2.58333608786,2.50611636611,2.44604109531,2.28627614967,2.48386850497,2.61187541692,2.65543357113,2.53367393165,2.4983753291,1.86505668505,1.89676592955,1.9157006644,1.53951712935,1.621407658,1.26289933143,1.23650427893,1.21233750482,1.77255031269,1.84998056465,1.75960380507,1.70899992302,1.64680969299,1.73152933802,1.4660831292,1.31917859113,1.78464307289,1.85679626454,2.08331932022,2.00545220957,2.08745052784,2.24091852337,2.12553150411,2.20161241894,2.10911861448,2.15246225821,2.18452497657,2.17411682486,2.10622722037,2.02803172924,2.08486668668,2.57685008778,2.42380021712,2.46914041195,2.54462098642,2.2804649935,2.18596979344,2.20486307479,2.24010450457,2.31803768413,2.33971237351,2.35968853111,2.36412304272,1.89692184697,1.9066680659,1.96016617827,1.9808561408,1.24844831298,1.38518170625,1.64549889907,1.58125295405,1.51334142637,1.82936877103,1.85634167593,1.79216663607,1.95568813009,2.00648964187,1.97861906086,1.89893290564,2.1098021864,2.14062684346,2.2142059159,1.96756669785,2.05592778196,1.9846950181,2.11377605076,2.01923643044,2.09384291036,1.78703051389,1.85873155865,1.93006202209,1.86119532297,1.85850304909,1.51217152901,1.53828830085,1.61416567007,1.66308223719,0.791879255964,1.33491626812,1.32568267073,1.25760657618,1.1973569644,1.14588167084,1.20684138923,1.27629864203,1.21663354565,1.29075700136,1.26639222986,1.31483302105,1.46096045073,1.40251375333,1.66128795188,1.56016218478,1.68232663974,1.63509970542,1.57215377234,1.59199549242,1.46726190955,1.53802581464,1.48102009123,1.4133056353,1.4981285666,1.44713106363,1.69800939239,1.68188115993,2.17949413723,2.11793466338,2.04506149759,2.03403309913,2.84789716707,2.71808518457,2.71306364862,2.58854364813,2.64845552191,2.228811705,2.16882205916,2.09630507556,2.5680601451,2.55975313253,1.91830654303,1.8383576066,1.78635869939,1.81383532422,1.89360008717,1.94623283985,2.23833246147,2.62862037231,2.79972843318,2.75344310225,2.59855692714,2.60201370327,2.266374699,2.33278065367,1.96754128066,2.05016010527,2.08349626438,2.1664612043,1.19049319973,1.11841530477,1.05468264878,1.06785208748,1.16395631905,1.14396509186,1.20295507041,1.24145730828,1.29677226932,1.27820043419,1.69054061309,1.59766356758,1.52134732516,1.50580613793,1.65592292505,1.41100164615,1.33654592052,1.47151757397,1.45867876662,1.31975866153,1.38146779694,1.29759519934,1.22695295922,1.32436855972,1.35108170243,1.22208490472,1.24937454116,1.20532074295,0.817320049507,0.888034597996,0.898562365567,0.837958247992,0.762495513552,0.752351313351,0.888630184988,0.890083298765,0.839431712378,1.08779420306,1.0191285991,0.94735765631,0.949357319938,0.744332412118,0.675880098613,0.766714313356,0.726246207589,0.644691481968,0.615896123885,0.972257160296,0.886512586905,0.95885557735,1.14264953396,1.06624842384,1.1675142646,0.644282881077,0.715844281568,0.63451360453,0.694716420042,0.771453949863,0.78217268215,0.666793211819,0.686830181692,0.455694187534,0.350774207719,0.336302331406,0.404575685081,0.426499559801,0.474830565674,0.482614989153,0.400616082023,0.693485426878,0.271700470903,0.293494354019,0.270050543087,0.207209534836,0.662326389674,0.723105075044,0.782773422955,0.702679881777,0.821050911905,0.46283018084,0.54235770128,0.481182331645,0.546165110713,0.418238506403,0.409006182112,0.548889307288,0.492766671353,1.09389723317,1.16498195444,1.09532422277,1.16263542627,1.23251967794,1.23155259213,1.08847023298,1.0216037411,1.11670370311,1.18883186482,1.05937777356,1.07980252414,0.895726697743,0.684063053159,0.755551126136,0.687369148637,0.829848322821,0.757514081701,0.729001264897,0.663213658127,0.674630191621,0.800702402242,0.99148356018,1.05239452844,1.04143310013,0.969501555389,0.932546411789,1.06504657559,0.987306756308,0.830202990005,0.798662663511,0.661157371056,0.736005798343,0.752232517195,0.696179195753,0.833158986433,0.801632735496,0.723150076166,0.680807704019,0.798163921049,0.72488394058,0.569024607814,0.524018026052,0.618677422222,0.59933868152,0.373865061391,0.450601313535,0.391143950303,0.339085969893,0.465842327023,0.492419930567,0.061105270571,0.115970948509,0.23646361786,0.210248894615,0.13893539122,0.209379751779,0.407584841001,0.557186836623,0.523836352682,0.546165492463,0.50668858379,0.433955099936,1.41860686616,1.49889461458,1.41698932047,1.37769772554,1.53891748996,1.4977743617,1.73518998949,1.77529889803,1.1820761826,1.15346290979,1.20526629571,1.26141342025,1.29811136177,1.14221323856,1.18378509485,1.15043076168,1.07373528942,1.02396043064,0.911992302021,0.94463320253,1.06601167312,0.956087373296,1.03062324229,1.31211184613,1.24022704509,1.17681984123,1.32142537245,1.51259626269,1.44841955622,1.22369802894,0.696832222713,0.602392477379,0.619298662335,0.667983251743,0.87159121891,0.828684453186,0.814016517335,0.735491630062,0.747959048497,1.05714758931,1.10653939688,1.08350497793,0.980615098581,0.946478858698,1.00028521359,1.98507469818,2.12316022349,2.05029830482,2.11979732668,2.12408847554,2.12410813648,1.8518998243,1.78405809853,1.63677286468,1.70227510934,1.69379499662,1.62672140067,1.49740433477,1.56582029469,1.56422932814,1.49494552491,1.42692913124,1.4260692384,1.75560860429,1.74453126031,1.92517281252,1.96733572311,1.85214091003,1.92907068834,1.92771052741,1.904784762,1.80089384745,1.87274855433,1.76822047888,1.81735200689,2.31887158126,2.23744124024,2.31025904821,2.17632919493,2.18418777442,2.25384174358,2.65161312468,2.53962372042,2.56266205755,2.659306847,2.58453660387,2.69273305006,2.63956901242,2.32044020644,2.38541836274,2.45771722437,2.45643088912,2.25660606928,2.32301760674,2.19023136485,2.32142973763,2.19016708072,2.25489310013,2.39036898675,2.39169697948,2.45959853245,2.45689259201,1.7136507233,1.73643679828,1.81579098142,2.20134372552,2.12433983859,2.18752390733,2.12064529329,2.43507046989,1.98560177063,1.96329851253,1.43127194162,1.49967130898,1.36628035296,1.43386456747,1.29986508207,1.30043023111,1.37001253329,1.36820835358,1.43879736001,1.43754361998,1.71669016711,1.78561157077,1.85553888499,1.85496915736,1.31413326052,1.24198121206,1.24387451239,1.31947872806,1.29548467698,1.34501525033,2.48712075,2.54361182145,2.2705800389,2.20305636675,2.15992883339,2.19281049814,2.0460897121,2.16544972758,2.08912614067,2.36285880526,2.51756779301,2.42307830428,2.49977448379,1.95920507258,2.1044394297,2.0882803545,2.01599261602,1.6416953841,1.65880826475,1.57433815972,1.62052931422,1.49820117091,1.60041850288,1.74076681345,1.81762555476,1.85262290819,1.79433623979,1.72316498563,2.56906944704,2.61090993731,2.66674104015,2.5961481866,2.59107370942,2.6598277953,2.53083911113,2.52961553506,2.73369203013,2.76607965077,2.84043330438,2.37979674904,2.42025858741,2.36196506136,2.41211346986,1.82122182587,1.66744951563,1.59069435963,1.61677557537,1.50172309714,1.39158234859,1.42370685509,1.79312809689,1.69230158,1.76805127179,1.89078417928,1.96870036308,2.00909714119,1.96906161367,1.89133123055,1.85331914487,1.50963500094,1.37616947822,1.41633735601,1.4581087444,1.51669887742,1.52411464841,1.44554936119,1.3775649988,1.38277200249,2.0299255379,2.08252817537,1.95333701066,1.93074980585,1.98305148591,2.0579074596,2.03508706352,1.90134717507,1.86613886701,1.90161224155,1.97250570426,2.68835401652,2.75676532231,2.87628394034,2.8067936576,2.78974208763,2.67861978768,2.69750196481,2.65397148412,2.75166392815,2.52862527016,2.41921366125,2.3993054612,2.45161578442,2.4280032608,2.41557827095,2.62220007184,2.55005342926,2.49248070445,2.4969536626,2.62690157527,2.55922731603,1.7281446706,1.622153245,1.70019351064,1.74845859835,2.14916089487,2.2140343434,2.13590630722,2.10669096554,2.25859597257,2.26013664549,2.22413081055,1.75894935436,1.80539305394,1.78921173201,1.86864084507,1.9964975811,1.91703062174,1.88386019892,2.03611586918,1.99213481396,1.91963212138,1.99817765681,2.13350659538,2.1317344092,2.06324907816,1.92225489259,1.92772121691,1.98915541348,2.06144967562,2.06540210435,1.99861332075,0.96641324724,0.889006978636,0.873408440965,1.0251103794,0.935375356168,1.00951399316,1.07214349992,1.06357819069,1.15818210526,1.1191427397,1.09992465693,1.19432225176,1.52794388032,1.65477871896,1.58775517009,1.66204852558,1.53725041872,1.60429425053,1.28798561553,1.35736262709,1.23083618563,1.24785165406,1.32173828663,1.37415485318,1.96181459321,1.91753734366,1.86267521319,1.88594342102,1.85757096423,1.73478759182,1.80504375961,1.83512310224,1.70979338805,1.75775931884,1.94872431584,1.92827984369,2.05386179443,1.98185595837,1.87294048613,1.94710631744,1.82358203378,1.89649508091,1.76940259066,1.91538878549,1.78521098193,1.85822267027,2.67878432199,2.62971271419,2.64125388496,2.55264610386,2.51448574349,2.39315326915,2.5262656278,2.46333189351,2.3109553541,2.29921885382,2.38292676575,2.44148276166,2.07580275157,2.02428016367,2.15723596429,2.79077796055,2.75558412495,2.68376232459,2.73620512262,2.66195714454,2.64227796615,2.09523594155,2.01704214805,1.99595582135,2.49356698663,2.47360907669,2.35864793098,2.42858298211,2.34064444584,2.39183832196,2.50608130751,2.50292511236,2.30214798636,2.30870744532,2.37484329688,2.36344552556,2.43594691619,2.44018607814,3.11228505292,2.90200199487,2.97523854929,2.77526738433,2.84972272782,2.77790441166,2.84890264048,2.52334115837,2.53156843262,2.46549882,2.39817186715,2.39158994265,2.45073208332,2.18333236786,2.19417591798,2.26224742187,2.32218353991,2.23726287207,2.30786790121,1.89258531288,1.91732218572,1.58367563091,1.53569751127,1.5611234844,1.63484044075,1.84633294651,1.56233012699,1.44439207476,1.49408684327,1.1461216239,1.11746397408,1.08852759328,1.13187191035,1.01216405616,0.978102932131,1.02270891724,1.0998873186,1.01310013294,0.990908901207,1.03965282928,0.582109471581,0.595810216614,0.51131842681,0.451547201021,0.46891580032,0.54183246405,0.531363538335,0.569533938012,0.643021565307,0.68199432423,0.419285154468,0.353333149205,0.370868572585,0.444944419232,0.659947694341,0.580300632726,0.783899300993,0.831882318547,0.803566280463,0.703455065362,0.727094655811,0.674540345385,0.528379444704,0.591939105068,0.600537055329,0.548811113818,1.02827276245,0.964185172911,0.963180044921,1.02627528969,1.22384735246,1.15754972489,1.29313605429,1.29621055315,0.830286731627,0.977858540685,0.966917360638,0.897987997753,0.699502957844,0.636838180825,0.660065901922,1.08934876058,1.16565216174,1.14367024216,1.19143319841,0.960027935749,0.911725318121,0.944257889105,1.03884058075,1.02089803258,1.06749850215,0.314680165075,0.452558754527,0.444938993932,0.379652833774,0.329360702086,0.40066090428,0.264896170679,0.223521653475,0.149950299208,0.122728876353,0.250173922546,0.188353560682,0.189960764581,0.3368403202,0.289077765655,0.21535477103,0.329695107323,0.259760579241,0.14693526407,0.149132711375,0.0830795807866,0.0199810546078,0.0935865279035,0.466443223529,0.467704214094,0.540335229679,0.54269176412,0.609468262672,0.610859672315,1.65935537247,1.66236734315,1.62036833472,1.73809418706,1.78171622239,1.74477121675,1.25955877798,1.27073918628,1.21009268809,1.13784421017,1.12575429926,1.18688585796,1.68324073674,1.39482930179,1.45858086602,1.53344966225,2.0576182624,1.99062422102,2.0565987674,1.98808310439,1.92107436778,1.92260382757,2.05881462583,2.05742283815,1.99063208028,1.99175929463,1.9243195913,1.92448759885,1.961134719,1.88963352768,1.97234241297,1.90894442115,1.83692203542,1.8286907902,2.76439384571,2.90060670902,2.71734894247,2.83575540666,2.77949904127,2.68947298458,2.71644158426,3.01381944571,1.8534811149,1.78231510789,1.7785067505,1.91804324616,1.84006883121,1.90826374225,2.2424539184,2.30245672453,2.31855235587,2.25042995899,2.38422137969,2.37626226708,2.41329418949,2.35402375848,2.43104360656,2.46252759435,2.28672685432,2.33919884843,2.23471728314,2.31097851835,2.17279591287,2.03301762146,2.11380702341,2.04561852594,2.21229877211,2.18810008506,2.15831672157,2.08685561834,1.93974261029,1.88142830735,1.83194681939,1.86064893501,2.07985904352,1.98592242713,2.11056935063,2.06307527789,2.00281327412,1.95621060609,1.57097868785,1.63967631144,1.71344976528,1.71101011822,1.57682216441,1.50641131157,1.50465308563,1.57337485485,1.64619213085,1.64449437405,1.71784210288,1.64927564851,1.64849264535,1.71793414793,1.78663263812,1.78733826427,1.36952399045,1.45063336078,1.5046860001,1.48833067089,1.16432209578,1.23174987878,1.16435669357,1.23375418528,1.22294324846,1.09293836574,1.16419970925,1.19669480274,1.12192140396,1.07690161814,1.31953536531,1.26826753796,1.3965830669,1.27265160104,1.32930133397,1.50689046713,1.5543931552,1.42628568457,1.39532345532,2.35347784571,2.4103165928,2.5150720601,2.5202754017,2.56098424312,2.33483767661,2.37916398283,2.45739659238,2.45607788174,2.3826770113,1.53448221076,1.56169109316,1.4562143471,1.40294768773,2.84340547434,2.79085311352,2.30261511881,2.15170802212,2.18984971517,2.26588376386,2.18436936704,2.25902363353,2.40610975302,2.37721224221,2.30036988333,2.25389867278,2.3530026154,2.27789126226,2.53604988096,2.48971822901,2.61389544408,2.51318072004,2.585490776,2.64065262768,1.69552354234,1.72171143053,1.85271708657,1.79961352361,1.51393438538,1.36402691322,1.48869514845,1.41418085177,1.33686865415,1.38762710523,1.46290542089,1.58889306102,1.56431177114,1.48730681131,1.43804220914,1.53628321478,1.6389820511,1.56160971719,1.54456368678,1.51288860605,1.62037599393,1.54432351994,1.6545212525,1.49894748258,1.25998232142,1.2967121115,1.41907704794,1.37666072365,1.25744708106,1.29639022418,1.17939230138,1.25644662869,1.17708941643,1.13879595009,1.97979377538,2.04092518244,1.98796562949,2.04560247976,2.11841417409,2.12180742338,2.54541650266,2.47118364906,2.71227506794,2.65717482457,2.58182227405,2.5589788144,2.94053250474,3.07270951164,3.00672250093,3.02669420186,2.80690112983,2.88288177356,2.93409752836,2.88940041319,2.77592932384,2.81097803951,2.33333857847,2.4833195832,2.35984992805,2.433111937,0.984932412492,0.989458476355,0.921798567362,0.857437830521,0.855121554588,0.923747943559,2.04647445663,2.08197572291,2.00680297366,1.98972126507,2.21509396785,2.26767097519,2.14095444564,2.12224980048,2.25314685406,2.18212308516,2.15188391545,2.22520435164,2.2793862446,2.71134419156,2.71394022663,2.63794561326,2.57518682234,2.63882837627,2.57381922792,2.87824507782,2.90916803205,2.97970561703,2.9077505895,3.03434067604,2.98334397298,1.78838758698,1.81613578962,1.76366816506,1.68748201569,1.71022395347,1.66165134527,1.88118537471,1.86248185915,1.78542613576,1.7302432521,1.75106448622,1.82489555307,1.97482532805,1.91828261938,1.95527621221,2.03702284653,2.04730270459,1.92780864708,1.93652413244,1.97636067151,1.52314084481,1.37306287073,1.42900708389,1.50373599897,1.39437573023,1.46965679343,1.70244472565,1.63772591457,1.78424066815,0.912874248229,0.867417622856,0.790750375579,0.758535633758,0.577784022977,0.654038240895,0.557220116834,0.615797574689,0.565227135735,0.505396651112,0.844678720786,0.861624113565,0.888146620568,0.962854532246,0.888894563878,1.02251956947,1.00951572343,0.770351029192,0.787926213833,0.864462670448,0.835957073023,0.914617491463,0.925984168605,0.397619786992,0.463257607964,0.335870339203,0.475680332933,0.35305338568,0.426022067768,1.09627734783,1.16068644687,1.16342987684,1.09729750144,1.23180909839,1.23007857242,0.737835586362,0.870467372229,0.807861494061,0.74669415505,1.67311647796,1.73686838414,1.72519924296,1.65077053073,1.58727543528,1.59801359131,1.34311099034,1.40537816204,2.79598810172,2.83789855498,2.81804234234,2.88924390081,2.91691548199,2.95011038668,1.42705627625,1.36226786254,1.29676068368,1.29870951326,1.36842549193,1.43128631212,1.57605980143,1.52493538791,1.54848138022,1.47114585251,1.42030907611,1.44666288663,2.4401754547,2.28142961136,2.39543993826,2.31626708519,1.52989347635,1.50820311485,1.47380289026,1.39884539782,1.37449081026,1.42784466266,2.92039163488,2.96135385525,3.07412886026,3.03437934012,2.76712164362,2.71555653196,2.73035765082,2.79705420213,1.74784770227,1.76997660755,1.71974221452,1.84882711325,1.87792083999,1.82700742545,1.46031167154,1.36077059358,1.43580898508,1.31083891851,1.33636497005,1.4106800326,1.62256974701,1.66835962752,1.66173868573,1.74239886229,1.74659748349,1.53695815871,1.53738697521,1.49662492212,1.61638232344,1.65616689046,1.61738476346,1.30392371699,1.27764995449,1.42823334287,1.3829043251,1.40369291982,1.33224599076,2.67165590542,2.59716320943,2.71946034858,2.56671922102,2.68189415493,2.60489677029,2.44554402271,2.36871850285,2.3365035113,2.49012480928,2.45183460263,2.37558114523,2.12878946091,2.02527191809,2.05123933717,2.07610854094,1.59687644383,1.65294874277,1.8565500497,1.7919078552,1.72537750531,0.883937000284,0.914482864073,0.93574827525,0.806753949263,0.734999234695,0.767550061653,0.498035692982,0.530308891277,0.553869258138,0.609667499398,0.63208490977,0.656438911972,0.791001282012,0.907336374694,0.918436107878,0.862779565747,0.835811439523,0.775231148164,1.50600710199,1.29622252779,1.35503104707,1.42703276706,1.43687399383,1.30790625365,1.37652926005,2.25855008007,2.20116205525,2.15462195811,2.18153631315,2.43555954762,2.40076666038,2.38682341786,2.30950402803,2.32163151397,2.27871318872,2.93406729728,2.84480795129,2.86173779417,2.88874380706,2.96667983543,3.00059775198,1.57118863796,1.62058827735,1.59894680987,1.67784585223,0.70676609312,0.782383063053,0.688564121912,0.749846435235,0.823958633032,0.83868749132,0.729365107263,0.690850343695,0.629185446499,0.650601782937,0.762397330714,0.780405037152,1.64083605447,1.56927481721,1.70752819771,1.70084153481,1.47911063474,1.48889104549,1.56196372514,1.5434760167,1.61811415059,1.62686458789,1.74729478646,1.69850018162,1.72810586318,1.80832344536,1.85743441567,1.82582515051\n    \n};\nconst double THETA1596[NST1596] = {\n    3.24066028244,2.37326418767,3.45413313757,3.43130741464,3.15141406102,3.11207682255,0.326429816102,0.283827886981,0.569504712009,0.439505319258,0.497010190697,1.45308563367,-0.0536263820861,-0.0256305773417,-0.0705294886142,-0.553072577261,-0.381653371172,-0.257188650031,-0.334126266113,-0.451866858628,1.33037112033,1.37471785922,1.73094348779,2.14642548701,2.06349482298,2.03816940038,2.1761105812,1.82320648597,1.80600890014,1.90243926233,1.95968446899,2.91859843441,2.97142366182,3.06055949903,2.87218849021,2.5822436481,2.35011098445,2.41571218131,0.989023267522,1.20125784912,1.14852442429,1.5351727397,1.21060711767,1.41877040301,1.32792214096,2.67126617444,2.76207218507,2.60146560262,1.93045278467,3.57286030454,3.554864153,3.37863312905,3.49954707881,3.52020155228,3.24601751449,3.37927587992,3.40608715734,3.33899854411,3.18656584166,3.09936061167,3.18675473533,3.21710598115,3.16045454793,3.28420066577,3.31930379518,3.15124656007,-0.0658979439754,-0.857341598329,-0.924948228852,1.4364955694,1.59294437431,1.03251777869,1.10385090002,4.14120670131,4.27914692554,3.60675186478,-0.594784780472,-0.608715747611,-0.671810156242,4.21969479049,4.36587408696,4.2889784399,2.26133502334,1.7039753437,2.00431185784,2.08810697294,2.26630236561,2.91833249828,3.11484293893,3.13447158181,3.04332236164,3.03506928255,3.00051983719,2.33681333158,2.20082419172,2.28436019598,2.53278217992,2.45398553882,0.626459029076,-0.0226484661253,0.0687422144436,0.305954208986,0.361955284942,0.910447129135,0.77918963961,0.826214284754,0.764096612221,0.843330566307,0.863755197677,0.804310575897,0.438405259564,1.43416796272,1.34663702386,1.60640995556,1.53348780682,1.69096513929,1.68832856771,1.60124002144,1.50919406645,1.50005853382,1.63154937797,1.54576171248,1.62141440598,1.71833878176,2.14037318608,3.27613803175,3.36225948827,3.38612749656,3.76894227401,3.86668453418,3.94758553964,3.96111569245,4.315970015,4.33475058659,4.05665690632,4.1419163238,4.22545455158,4.39760272463,-0.684656774304,-0.728614027991,-0.609079346572,-0.457286522858,-0.48833837645,-0.565416220063,-0.836409880281,-0.80414215472,4.70884887272,-1.08607561855,-0.925135937555,-1.56113953038,4.65832196197,4.67565746306,-1.46252807259,-1.38228484032,-1.44069533021,-0.781133202276,-1.17155176831,-1.30305585962,-1.28100612056,0.25674574057,0.178277653269,0.102256735406,0.0499609513053,0.984791680494,1.19831501445,0.233473261481,0.306940020485,0.365302303405,0.349518353061,0.483571355074,0.410129144558,0.209145858164,-0.601656935925,-0.554061420938,-0.699152902344,-0.438758256236,-0.848977290684,-0.76441328205,-0.609306496466,1.66206708292,1.3625660092,1.30164616608,1.23624292897,1.19490562166,1.14296138468,1.5836897535,1.45438064864,1.51003692135,1.45488735827,1.33226557321,1.38180104424,1.45464263635,1.47539962545,2.96471497019,2.90889382523,2.96163814723,3.08074409771,2.57058073606,2.54336135743,2.46537668662,2.41897396814,2.45134871801,2.31522432996,2.40042628965,2.29606042998,2.2618198531,2.41799706772,2.5273753175,2.41803104353,2.35863840698,1.02251781496,0.936071835767,1.05487050825,0.978131031437,0.876265585655,0.866881028549,2.11822884676,1.55273028238,1.65132245031,1.35326661603,1.29031735166,1.48034645157,1.46266739669,1.33767798738,1.41943927246,1.39344434175,1.90339834392,1.63283094094,0.703204388049,0.623397246797,4.17220622157,4.39477891944,3.61232058164,3.52319319124,3.50770754409,3.33839835527,3.45964432874,3.41519556161,3.19825111479,2.92446207998,3.2682560757,-0.473623607385,-0.437554128678,-0.510500037514,-0.815812857999,-0.696911827574,-0.728573317084,-1.3089309672,4.4746186026,4.54392545895,4.35693474055,4.46052929983,-1.50035842696,4.65244071945,4.62659218529,-0.733496789955,-0.866422553769,-0.945031446387,0.496976202764,0.785001489802,0.754760068568,1.68353942863,1.70273007891,1.02761557078,1.30488500792,1.35135568617,1.45172120366,0.933146850126,0.99754662688,1.0831343824,4.20443606839,4.05919223755,4.13589505516,4.06239368378,3.62166283607,3.67727096073,3.74206906697,3.66438095861,3.76033974251,-0.15362579284,-0.585771381758,-0.467075277454,-0.554344477072,4.60289198924,4.61732773361,4.5604700304,4.52310643035,4.60713598924,4.62246495901,4.15448542572,4.22812426546,4.2606600266,4.18040085519,4.25079873163,4.16546815159,4.09089459798,4.08459727999,2.56492910387,2.51705546286,2.10349768408,2.19370803636,2.21483790459,2.02802957219,2.41038441146,2.3500103908,2.23807048436,2.15461854516,2.09180001402,2.17248844854,2.18807151433,2.2175567085,1.9949415854,3.31361598556,3.17709931903,3.12634984385,2.8815162294,2.82006632017,2.77219544357,2.95987712685,3.04493101755,2.92460082786,3.01072997002,3.05666447952,2.88391615655,2.17263431156,2.09255149757,2.30674466163,2.24351159374,2.19844794677,2.225753968,-0.157379629928,-0.0702331464247,0.226513759038,0.19315255678,0.106390757257,0.249906633435,0.33974747526,0.765365346183,0.529179506028,0.650546804802,1.16540632389,1.39352099426,1.22220923567,1.3155218123,1.84749750948,1.93069035954,1.93870884036,1.76851329748,1.38772624566,1.64171068602,1.62435738873,1.68100624761,1.71608556814,1.79516950515,2.24980344795,2.21316989222,2.29868251897,1.58941144344,2.09395159858,1.99649791025,1.85724084699,1.98897734204,1.87389768884,4.21659546149,4.35922908791,4.28252078487,4.0506906389,4.04727109348,4.57770411597,4.43212292814,4.51125504483,3.64900877555,3.64159053212,3.50925509156,3.42988645355,3.43470581291,3.58307526079,3.34120044953,3.29100700368,4.06457877667,3.92757970204,3.91730647236,3.91493143365,3.76328011657,3.76680747061,3.84335006644,3.85074995515,4.61531905011,4.43672255419,4.12382316457,4.02531585571,4.0033163829,-1.43078713728,4.69751359052,3.9226176803,3.87822252369,3.77697712827,3.78814613082,4.49343750943,4.60224769344,4.51322131761,4.67944285123,4.66818729862,4.57039222452,4.25136730179,4.36025767293,-0.363046527556,-0.441943093195,-0.311925023344,-0.474657073061,-0.701353485563,-0.755662498138,-0.699177932033,-0.750266420941,-0.725505007262,-1.03643349426,-0.955610283106,-0.912174135895,4.63842156136,4.70588251626,-1.50213805461,-1.49396016894,-1.26976018512,-1.18124351819,-1.16716572609,-1.0613736969,-0.952055374834,-0.980739353892,-1.01689506695,-1.12263330719,-1.10741201075,-1.19455728043,-1.15565824528,-0.841050524281,-0.796191373197,-0.719649152914,-1.40284182386,-1.48002988654,-1.49929033603,-1.44277789556,-0.906844605555,-0.895861483182,-0.81244590335,-0.786062409757,-0.843465481678,-0.984525029607,-0.928138263244,-0.953890620222,-1.31719856121,-1.33958266804,-1.48042588958,-1.42020580443,-0.862908825319,-0.920640395535,-1.23001447953,-1.34542426917,-1.30897762479,-1.36599894322,-1.12916061606,-1.20812893928,-1.24475083818,-1.26642681014,-1.02754689105,-1.00347986555,-1.1080826414,-1.16522779931,-0.000432334693382,0.0800577318003,-0.408825965659,-0.461617599965,-0.424839250783,-0.340611192756,1.30542730136,1.31488134837,1.51157279458,1.31941031265,1.23172806152,-0.251021956521,-0.313908594077,-0.283076377222,-0.326799392776,-0.175737402573,0.67044861803,0.677512924021,0.614725257659,0.544215762922,0.532749443598,0.595998590402,0.263050130608,-0.0925358913197,0.812355688844,0.628259587588,-0.615071886431,-0.761097321123,-0.76007779161,-0.718966439615,0.737477835661,0.887908102537,0.974062471519,1.15181215559,1.18452093616,1.21745446048,1.16515315542,1.07928459854,0.966529350234,1.04935905154,1.1018092177,0.942057363989,0.998046182709,1.07537934816,1.17346014469,1.2666671112,1.28688922868,1.36112522588,1.32151357179,1.10889177808,1.129955569,1.16270632556,1.23512580436,1.20848603342,1.25957533272,1.5201435497,1.52629932395,1.66720114246,1.59426191207,1.76149845801,1.68038174739,1.61382437562,3.07338460959,3.11497092193,3.21391061689,3.27929017518,3.2432796974,3.13509832809,2.66129519217,2.82816242216,2.74558580175,2.43427118928,2.3838347309,2.42563195405,2.51508946464,2.58749828814,2.53911865895,2.71818907799,2.78815944005,2.75165713053,2.63252340054,2.14656528644,2.27379252904,2.23933446152,2.00656014915,2.03118283227,1.92372417387,1.24030035279,1.27699301571,1.10956133022,1.04622191158,1.08344173599,1.18543431988,1.49332763924,1.61747206748,1.86728189501,2.09316399388,2.31996512356,2.42028265504,2.039912667,2.32905665141,2.16343699951,0.236884007576,0.282108254027,3.04344926419,2.76589555782,2.48573910584,2.32497421436,4.20625737033,4.14851029119,3.6381005097,3.62974543099,3.7340502797,3.62055744021,3.6248166031,3.17943581102,3.09133431279,2.91742198729,3.09548371444,2.92619040173,2.84153050469,2.96013440003,3.07673502007,3.04651511749,2.91042044415,3.02544629741,2.94504303952,2.52160050197,2.56002845148,3.30258099693,3.32719866937,3.36462606166,3.45286940886,-0.011266368285,0.169018018231,0.104849338089,-0.0581723159019,-0.0547908584922,0.000967748700403,-0.743703090711,-0.681417019454,-0.568604805367,-0.676652197923,-0.348959735929,-0.292957249642,-0.204422728888,-0.161227225501,-1.32198238957,-1.27157655896,-1.25130692002,-1.2147876757,-1.01408552657,-1.04761102036,-1.08518919965,-1.19300385085,-1.28064159467,-1.48645727104,-1.39328038171,-1.38766216382,-1.49329393312,4.69959574434,4.68763953397,-1.3762549579,-1.09533991301,-1.26277907047,-1.1355013953,-1.52772213686,-1.51798142587,-1.1620607306,-1.32344087497,-1.2108124922,-1.36760555838,-0.492694123626,0.170828263978,3.61647195188,3.96255155635,4.13575906963,3.22952455131,-0.159020257108,-0.222225871022,-0.636766620067,-0.486914870793,-0.347262526014,-0.344393843282,3.99030393193,3.9877300277,3.85785512903,3.92376092855,3.9208053738,3.85746944574,3.71923722317,3.78677661304,3.55628869884,3.47371029256,3.41047989871,3.99248475837,3.92270358425,3.91834178912,3.9933923041,4.06872118342,4.07338409645,3.83427223667,3.91026403439,3.82889227751,3.91518932472,3.99512095595,3.99495848768,-0.194583983614,-0.155284191448,-0.198236583064,-0.274817228367,-0.145999436483,-0.189353131991,-0.533161274879,4.57470589464,4.41307710148,4.54738994118,4.33262918929,4.42121639391,4.50706770398,4.34190726981,4.38032350583,4.49156858748,4.31132176423,4.37967887169,4.45811378675,4.32279212258,4.41275898998,4.47947599233,2.55040475214,2.62289220057,2.49779673267,2.53106692812,2.8083484835,2.90247011857,2.64488922311,2.68836541058,2.46775128973,2.42332643271,2.34286843047,2.31154888585,2.47062093108,2.43243308959,2.3564352858,2.31808717193,2.43049557475,2.35369672707,2.29572836756,2.21923961541,1.91625508047,1.84250599457,1.76899006195,1.76614013578,2.13388450363,2.0577064529,2.16984914626,2.19294781184,2.09195334859,2.0391559856,2.44330182979,2.31102125856,2.34024818923,2.37791800059,2.47216948852,2.50737431225,3.21948015439,3.54288204097,3.41289564931,3.6648866964,3.66395722157,3.50179859237,3.37301233041,2.98114826475,2.93517826698,3.10288313025,2.98921083758,2.6086011495,2.6540240585,2.66440264445,2.7626835965,2.76231054857,2.81433026495,2.48453213097,2.59528718172,2.65510684216,2.41552532706,3.27938792474,3.3588007399,3.36198460306,3.67995125696,3.68393053374,3.05895335198,3.09175590474,3.34480973783,3.14988598386,3.32492324364,2.58375359088,2.54617865728,2.69954707327,2.66025936319,2.81995191867,2.89935647326,2.93349218681,2.77770020334,2.89043997767,2.81376185244,2.80572430446,2.76591413834,2.88487484071,2.80467334628,0.214564248976,0.177446404801,0.725245465819,0.706828136602,0.566673210868,0.627269876769,0.463195702346,0.559292121314,1.06241547196,0.890661074575,0.978266070753,1.0570486129,0.733578939345,0.810865937641,0.811932076809,1.18853022068,1.25328124851,1.09597270152,1.12220119361,1.24454131428,1.18072662322,1.28277422373,1.30446242848,2.0353590458,1.95592956785,1.757332234,1.81513199021,1.77496428824,1.89475261832,1.85211780533,1.84024230191,1.91127451801,1.97135307328,1.9332675477,2.85890817103,2.97323087414,2.46925977774,2.39067298377,2.68968094671,2.6426509508,2.59906101254,2.46758920695,1.8432606126,1.64911177541,1.57997337621,1.77302900522,1.87368498504,1.5790486706,1.67131496542,4.38398050094,4.43383002861,4.65249034845,4.57900997569,4.1194008163,4.19712438469,4.12530640648,4.24506189519,4.36214871519,4.33563517599,3.70806710851,3.69889096905,3.76755748434,3.84493257852,3.85127471846,3.78310557818,3.46402025032,3.78998103092,3.72602520829,3.2692285529,3.15321565832,3.2282486813,3.11926343324,3.16417851827,3.23938461156,4.39306291981,4.4554372969,4.41903576133,4.49622491023,4.55978798373,4.54537575754,4.05185902685,4.14282907832,4.0752269255,4.00063089107,3.9942051937,4.49514938762,4.41840943364,-0.734081109532,-0.874341100105,-1.09832912154,4.1275491802,3.82434268831,3.95722635722,4.20513210077,4.02433824679,4.18843959771,4.07508547054,3.9922125032,4.42715814961,4.54348880658,4.22670311621,4.16227353058,4.2410001706,4.37405937671,4.39121917501,4.45469411191,-0.961426213401,-1.07350847347,-1.09378375246,-1.03931874185,-1.34924842255,-1.51609985684,-1.51147869975,-1.43153603795,-1.24941826026,-1.43016105624,-1.34322370819,-1.33002577021,-1.27405800513,-1.4069010616,-1.42137292714,-1.35748862354,-1.35628489774,-1.2859288495,-1.27864650091,-1.20554100709,-1.21376266549,-0.808699299837,-0.73804687689,-0.831002914421,-0.86362795262,-0.600246295371,-0.679269635065,-0.556276112491,-0.597296565683,-0.685516294287,-0.722946231733,-1.1647802633,-1.1419631134,-1.04773794151,-1.10055884249,-0.983589354473,-1.01288224096,-0.955114058689,-0.870690723158,-1.0679711776,-1.2106513358,-1.12579841322,-1.23465769376,-0.887480958627,-0.854876280361,-0.830669150406,-0.936615061924,-0.969471378841,-0.994212378722,-1.03732985961,-1.06191395373,-1.09471511898,-1.17688678592,-1.20064794674,-1.14333080847,0.302634298904,0.457923720766,0.396182587876,0.319108063567,0.178026013733,0.139436270336,0.117025051372,0.258222915276,0.216975231333,0.27453680437,0.0729899493393,0.156619344198,0.197731950251,0.2200551367,-0.205270382912,-0.230752652496,0.0353522778012,0.0128449324214,-0.0225738696181,-0.0687995888134,-0.102092771346,-0.125582792762,0.947937305759,1.06939622192,0.796967000368,1.03018939414,-0.415643745862,-0.554709242438,-0.551626111323,-0.616329189019,-0.393335634601,-0.286722014317,-0.439739986345,-0.367616052981,0.113452677462,0.0521839298464,0.113975123056,-0.407972528839,-0.204454440761,-0.216055036988,-0.570714934443,-0.517732373368,-0.362894149017,0.341248802718,0.364331529971,0.442820594368,-0.147882979082,-0.03608718724,-0.215748677873,-0.249653519016,-0.104887870973,-0.0282863861324,0.0858006452377,0.342647899914,0.22436709065,0.112472359742,0.082142238726,0.304041201528,0.265537062465,0.154710278732,0.801477029059,-0.16112818602,-0.112920563073,0.434121372288,0.344806101085,0.00498907077649,0.0716107313568,-0.953484289183,-0.820933429138,-0.74285467281,-0.788983337816,-0.905818095701,-0.98997099361,-0.993243886282,-0.895096305259,-0.863965522911,-0.93833830568,-1.06905194059,-1.0461759729,1.48142156482,1.40352839216,1.54927116987,1.49308776847,1.41718721953,1.3979746143,1.12919947043,1.07033736442,0.747513669023,0.806107517763,1.77986687343,1.86304067332,1.5631668756,1.63013568742,1.57151366223,1.65151504204,1.72850101628,1.71517418363,1.96758495333,1.81220054061,1.88203522655,1.32700096136,1.46574695945,1.27138442653,1.37789326867,1.55303238824,1.5808055953,1.8566663285,1.72180696942,1.72916108886,1.84382365774,0.422318788432,0.558004860657,0.720940530958,0.796973710557,3.7354120316,3.74644994744,3.8225067588,3.91160256092,4.00344149016,3.83148339828,4.02913038338,3.94841400768,4.19858986177,4.13113961672,3.98833808498,3.88454093144,2.83366055323,2.79075029104,2.69621290628,2.65093669188,2.53468441283,2.57065169025,2.57993264838,2.6599109127,0.148936102456,0.220660336855,0.125537195965,0.0896723529338,-0.168888137372,-0.250085593897,-0.361963697615,-1.35609759556,-1.37746704112,-1.52355692067,-1.46025149971,-1.40677477638,-1.48658729055,-1.56868322155,-1.42048217434,4.70916070517,-1.50531368166,-0.819782642521,-0.678261983847,-0.860429679857,-0.952223287196,-0.564460677109,-0.528753166914,-1.27383685132,-1.55264630903,4.70063956361,-1.0156548512,-0.95486337569,-0.752394319871,-0.00297003067537,-0.116586448554,-0.335702781657,-0.348914690046,0.128564070426,0.229962331676,2.52696934567,3.07950503155,2.05056507245,3.19963769371,3.61993123615,0.1355315119,-0.0301186873089,0.20014398134,-0.0856179984746,0.120236935072,-0.00599121311057,3.85361191659,3.98273103705,3.91923551627,3.85438350513,3.92693944479,3.98966926195,-0.319180513855,-0.400956244112,-0.449479956271,-0.415251839381,-0.329481733126,-0.282242311516,-0.352807371725,-0.309052130359,-0.26484910924,-0.295401026976,2.76144845293,2.80495358694,2.67328261243,2.6346188455,2.68084933582,2.76342863645,2.94030911532,3.03116049628,3.06215240547,2.89016552409,2.92849383412,3.01013960099,2.29863716155,2.27457728309,2.38272006978,2.43632658225,2.40571617395,2.32768197126,3.47282993359,3.32668750888,3.11138830512,3.12716298699,3.221276202,2.93696742756,2.77405170022,3.94602672038,3.12337465533,3.15790000962,3.23332099289,3.17587128462,3.28403286341,3.26150268808,3.22659103825,3.29799716551,3.08981485563,3.12510206983,3.15609870606,3.26394222929,3.46113100602,3.67215923855,3.66802462446,3.5581140589,3.3987103263,3.47979299814,3.59126632652,3.58154173996,3.25388106522,3.30235045254,3.18592033555,3.21254824403,3.42139183338,3.51284993453,3.34879391421,3.37009675147,3.40800586393,3.56469615766,3.49847826976,3.42210328019,3.60910622325,3.47184533379,3.52654873044,3.45695600683,3.61980714019,3.55225694634,2.58309239531,2.54311423235,2.65314170378,2.57720326953,2.80879140629,2.77306191469,2.69745254536,2.65812541182,2.76913320507,2.69381805789,2.88197765677,2.9957397293,2.92024496126,3.03591799798,3.00080080021,2.92206415683,0.17298589736,0.212546416571,0.169324040475,0.0878090753584,0.0565356176427,0.0937182122011,-0.0292798490208,-0.0730132116384,0.581012181884,0.52227746798,0.508404287994,0.660005690932,0.676754665984,0.611825224363,0.484551306331,0.428993036065,0.334486430567,0.347420820749,0.305114359533,0.401428227137,0.456026747679,0.40788653247,0.474197797443,0.411447504024,0.491568164964,0.679303358406,0.903134546645,0.793246445142,1.38090701769,1.28484421008,1.32029722937,1.44380015667,1.4763871199,2.11025107085,2.04498694089,2.12157359277,2.06908164048,2.85154644601,2.70235338497,1.77396667966,1.7710048566,1.85802826001,1.86346644191,1.68550480227,1.68318798914,2.07499263146,1.96945204976,1.95834601605,2.041945885,2.16035189409,2.13909933121,1.75378392037,1.65296581319,1.7300204323,1.52553816821,1.47334632349,1.57348622877,4.56133125381,4.48834138968,4.51763536925,4.46530407854,4.66964253708,4.70393239941,-1.53780428055,-1.51913478783,4.56404660605,4.6263794831,4.61089584824,4.44963436544,4.52198284693,4.53658259011,4.263179399,4.39040890681,4.30573781201,4.31804173641,4.18318880827,4.25522714037,3.51831285315,3.52372451511,3.58722929237,3.59579768191,3.57156779782,3.64723107991,3.5947478334,3.65659269647,3.85123329641,3.78443008417,3.84594168305,3.71107701907,3.70088736554,3.76896892416,4.16599300982,4.1283973314,4.25542535958,4.30551585081,4.26633943007,4.17775341458,4.59684988024,4.62948243266,-1.1400177964,-1.01222195156,-1.05209428204,-1.18889852152,-0.891241302659,-0.967025703347,-0.610893095115,4.56509037401,4.06690197565,4.15527626722,3.91441702379,3.62811661846,3.84922696578,3.65705214696,-1.23700401588,-1.20697770666,-1.13111566838,-1.10874274412,-0.747093312125,-0.834072933946,-0.685699525808,-0.728968586598,-0.846612152938,-0.888442773182,-0.031302078076,-0.19764783916,-0.174417591308,-0.0918625511001,-0.152352792094,-0.0783297467612,-0.130652623147,-0.0466407514572,0.0283391924695,0.037414069558,0.403123874964,0.373947009139,0.441948056629,0.308130406962,0.131095064659,0.374152123929,0.288365449866,0.0527823119451,0.137913030937,0.821044619903,0.528828574535,1.25825118811,1.11893048098,0.819696092193,0.383736858651,1.57031957532,1.49568837457,1.43638985039,1.45485690703,1.58642268498,1.53038090642,1.26714174981,1.34313586099,1.36154560592,1.30372089259,1.22722569425,1.20928535621,1.16729904101,1.1100863127,1.03329379229,1.0390724749,1.12608844559,0.964428317635,0.812228120326,0.884502675136,0.938969508855,0.976958938766,1.03297725696,1.01438695333,0.900341517017,0.881131020035,1.10370326355,1.05279085029,1.07763861222,1.82320679476,1.73928461514,1.73930609055,1.83802823975,1.97888873079,1.95915542836,2.11679071328,2.21208632804,0.8748776907,0.749767498637,0.614746637913,0.791007694499,0.705361648437,0.540156458806,0.529801558869,0.475160040816,0.397035336512,0.312294018007,0.415643665554,0.43447529401,0.245181706614,0.280169776698,0.367260228493,4.18735243824,4.10228829857,4.05635736121,3.92491176916,3.82091016759,3.78531954134,2.69765257572,2.65630054142,2.82649257219,2.78595345613,2.78207826424,2.69992480072,-0.396105297674,-0.486085889133,-0.560304892768,-0.514651477475,-0.278268175773,-0.230452038418,-0.154777851689,-0.128682189149,-0.175922109063,-0.250078312368,-0.433075365054,-0.385828161414,-1.49193486956,-1.28462553947,4.56130318769,4.46398467807,-1.25496501231,4.70154814866,0.0489901686861,0.090627613935,0.0488700675299,-0.0324804907838,-0.0734790469616,-0.0340240195585,0.585176288468,0.529324401431,0.658488377511,0.673738019046,0.612185541142,0.540971400219,0.894795585999,0.889484045573,0.993240311968,0.983330830162,1.91136244075,1.98238804293,1.84947852502,1.86553145002,1.94015051902,1.99494110873,2.76833778835,3.10770360081,2.47891119116,3.27882403844,2.02147257245,2.17588091666,2.37185499043,2.45935876346,4.61570424951,-1.53812507457,4.6898820313,-1.55503746211,4.65241554228,4.59527569738,4.40168606494,4.48748834654,4.47565002243,4.42218313769,4.34591363966,4.337388385,4.17676059163,4.23640855259,4.10954289123,4.11975522621,4.20252782578,3.65854063656,3.79045094125,3.72685086066,3.65537725763,3.72141310295,3.78818197384,3.50990323705,3.43108861616,3.46512346049,3.52361382018,3.39091868401,3.37087328045,4.67171939476,4.70466717669,-1.46877195143,-1.44000331416,-1.30522758321,-1.31125636499,-1.53689061803,-1.52656017822,-1.42898945079,-1.43286581669,-1.32260349584,-1.32946220641,0.491374354602,0.586098244703,0.507493486211,0.656527015939,0.920583046599,0.978266173124,0.95885138167,1.00872570352,0.960577024453,1.91423246844,2.08096638105,1.9884935476,1.92883548401,0.501075646143,0.605510218376,0.452986645323,0.592503018817,0.33969880474,0.594241671531,0.369305203608,0.486342190863,-0.331323237293,-0.2126996194,-0.312672819036,-0.376181151482,-0.162019853683,-0.215865024565,-0.645826504075,-0.56369979148,-0.512452147209,-0.540570502481,-0.619704890812,-0.646007569728,-0.673702923919,0.540986264999,0.723102887278,0.647447468351,0.560430968514,0.690877696496,0.796119797841,0.600388817656,0.618357013993,0.800606766255,0.713925968453,2.42767354583,2.04391809074,2.30083898305,1.7912991795,1.73053585231,2.15680299698,0.788626151696,0.84513577942,0.714671583721,0.698437925497,2.05452234222,2.03088363853,2.17187281842,2.24772825664,2.20900881279,2.10805830147,0.700765952341,0.923004426011,0.832515361901,0.704037125259,0.892756157883,0.795600952399,-0.623463855908,-0.595105923056,-0.577603119645,-0.502579962261,-0.415605184879,-0.49122685883,-0.519419254475,-0.369633970043,-0.398600797374,-0.473009079987,0.885993733864,0.828307529445,0.755460050439,0.746331873253,0.813836159446,0.880600133812\n    \n};\n\nconst int NST1610 = 1610;\nconst double AREA1610[NST1610] = {\n    0.00771503984311,0.00783155983693,0.00761471595757,0.00777918555608,0.00791141620966,0.0080217882768,0.00749152743438,0.00764204235133,0.00769057810026,0.00798442227314,0.00785766998554,0.00795615719593,0.00796989063162,0.00771458884476,0.00791503008815,0.00770839579274,0.0079595854035,0.00785271294883,0.00770998847668,0.00770383638623,0.0074759712762,0.00884346048924,0.0077884782018,0.0084490957535,0.0072159936648,0.00806479930367,0.00758070745827,0.00809971114913,0.00740556821427,0.00827592531071,0.00739413560098,0.0075201059156,0.00733655391478,0.00771268604458,0.0077449621368,0.00770827229896,0.00790888027419,0.00771814433879,0.00793061137015,0.00790145957736,0.00793336445721,0.00793179933016,0.00771539163481,0.0078375764227,0.0076592572713,0.00779764381365,0.00773343790266,0.00772006407586,0.0078759568387,0.00778586534801,0.00786207327374,0.00771430098962,0.00790106721573,0.00769839449166,0.00795196290757,0.00781691637162,0.00773123272518,0.00783398689329,0.00782436502583,0.00777517909659,0.00787736981382,0.00793767708824,0.00772379689841,0.00772905493685,0.00782729021564,0.00783347171633,0.00784076283077,0.00770996380578,0.00779509569292,0.00784897978828,0.0075245963651,0.00779458751538,0.00797634768752,0.00778976091918,0.00780080862307,0.00787776816677,0.00777547191858,0.00773833682416,0.00776611506779,0.00778160068711,0.00820824399863,0.00826773186149,0.00791152014857,0.00773935345521,0.00786508471801,0.00783154295374,0.00792586107777,0.00785417730817,0.00764268014425,0.00795650192337,0.00776420399386,0.00791581762682,0.00797495495286,0.00771652735488,0.00794887676029,0.00768973978039,0.00769406081509,0.00795785065113,0.00755571978303,0.00799265604692,0.00749461520731,0.00771167742168,0.00790174625165,0.00801596306083,0.00794274955962,0.00795837912301,0.00770986786525,0.00786565059291,0.00787174098261,0.00779078271412,0.00775523447757,0.00764219749425,0.00757025844981,0.00775961072152,0.00774602324975,0.00796917986708,0.00783555949219,0.00772583650037,0.0078216376343,0.00781729010401,0.00776120744424,0.00793941808756,0.00794617914673,0.00771061893242,0.0076680936725,0.00765585270002,0.00801014713125,0.00755730434038,0.00925850459697,0.00742074644569,0.00735626301417,0.00796936900305,0.00742198384254,0.00807953140765,0.00747116435438,0.0073964879588,0.00751699542749,0.00740954515195,0.00768575874656,0.00770901517772,0.00777013341176,0.00754273370493,0.00920397469626,0.00822570313004,0.0078143432916,0.00781899627533,0.00779358623289,0.00784109841343,0.00775576461388,0.00785854567946,0.00800544793133,0.00798125714345,0.00804723301107,0.00783880478802,0.0077737385004,0.00778723530467,0.00782764185292,0.00798211870506,0.00785891450475,0.00777390216014,0.00778671426087,0.00775317133432,0.00777712527479,0.0079865109801,0.00772685654528,0.00781015112916,0.00783430969516,0.00782107603466,0.00773035076306,0.00786116838549,0.00785679809625,0.00779512090726,0.00779934170361,0.00770041393432,0.00778509124411,0.0078717031972,0.00779507838324,0.00774452121784,0.00790028047001,0.00767927813923,0.00777571430721,0.0078487233728,0.00786952837835,0.0086934613461,0.00740806137969,0.00794230183867,0.00781959926272,0.00734847759835,0.00792603979822,0.00775698584867,0.0077457933569,0.00793112037389,0.00783673310203,0.00780902955435,0.00782999479246,0.00783094230874,0.00781608811334,0.00794782295005,0.00789347167184,0.00772676325301,0.00774301599689,0.00890066780675,0.00770202112385,0.00796666031277,0.00772224353267,0.00792990406499,0.00803164333313,0.00764559464739,0.0074136769891,0.00773063406791,0.0073644897317,0.00913374163661,0.00734227128271,0.00791978414346,0.00810831912314,0.00784468231111,0.00771266431724,0.00790670453254,0.00795270301366,0.00794961356177,0.00772114648734,0.00747869810184,0.00796058379306,0.00793855449633,0.00777387039104,0.0077908350681,0.00823276767915,0.0074629313782,0.00803379457318,0.00763091382472,0.00766436888365,0.00888648588761,0.00761206754866,0.00760592860339,0.00746518120274,0.00781633110023,0.00781883963502,0.00791819625378,0.00766287005191,0.00795783642919,0.00770733666247,0.00760825731401,0.00821181722963,0.00798396996979,0.00771482372472,0.00793592926269,0.00780688682076,0.00783947667818,0.00740953274761,0.00752758176245,0.0076158215182,0.00759180642106,0.00821519283102,0.00743477394919,0.00787707773905,0.0077264884424,0.00788072898921,0.00773454481511,0.00798623340684,0.00798894344355,0.00767779221408,0.00768351131518,0.00787798728797,0.00795804059034,0.00774326577302,0.00793109226422,0.00777026871809,0.00790736007183,0.00773251336195,0.00780601713462,0.00784574320099,0.00779485546602,0.0079600228968,0.00768963431268,0.00783455213675,0.00770937136509,0.00793941956379,0.00791142120654,0.00778946373316,0.00779924999237,0.00776336732,0.00775210297298,0.00782231595188,0.00783285816231,0.00739121641647,0.00798628033387,0.00790612706683,0.00763058939041,0.00783755881389,0.00783085174186,0.00782685292844,0.00781315113146,0.00780186402642,0.00784641850318,0.00821011204296,0.00754956609225,0.00772742000821,0.00772406639963,0.00788048291337,0.00775222265317,0.00788898051928,0.00776860889576,0.00917999148633,0.00728150416131,0.00742741659545,0.00778347068437,0.00744800952015,0.00852760731072,0.00748913269914,0.00772670898099,0.00760557359103,0.00809796127961,0.00745947421489,0.00749874230127,0.00761746218947,0.00736126356153,0.00757462713584,0.00760513620832,0.00769658713876,0.00782721205099,0.0077451737991,0.0079623427969,0.0078627140712,0.007782904125,0.0078318041231,0.00782611909243,0.00768902679637,0.00781458358499,0.0077960377507,0.00784620758861,0.0077822321933,0.00777997154003,0.00787152207902,0.00778261053971,0.00783226621086,0.0078269464452,0.00790471507671,0.00775813772195,0.00784315572226,0.00771484534368,0.00787057995494,0.00795503754897,0.00764700122159,0.00796486017657,0.00801839048978,0.00770955562432,0.00791555912196,0.00776786410375,0.00768535073176,0.00794580108567,0.00770667921145,0.00786919679242,0.00783173071056,0.00780827193708,0.00772266114189,0.00793165708808,0.00772042903042,0.00772777313769,0.0077053806239,0.00766718311063,0.00792250669589,0.00777541699135,0.00797956619099,0.0076005385595,0.00820351215416,0.00785059886487,0.00750187541424,0.00791234842265,0.00788215065593,0.0073708368243,0.00806471076155,0.00768069461781,0.00776513713338,0.00778452327477,0.00783810736703,0.00789336218585,0.00769936781246,0.00781872776656,0.00766830976984,0.00794312746334,0.00775396617063,0.00785206057264,0.00782993063019,0.00779983814906,0.00785164979105,0.00778819725122,0.00782708337653,0.00782129967535,0.00786870444309,0.00776899300325,0.00772965659031,0.00785874217669,0.00780197803573,0.00787810554133,0.00787632005265,0.00780104649211,0.00779156861586,0.00780176977944,0.00778208532804,0.0077176863127,0.00792305110323,0.00793290160351,0.00772253396271,0.00748591338889,0.00793599013594,0.00805480962963,0.00771840671378,0.00773030257689,0.00792217091302,0.0089874457002,0.00776793266532,0.00774372993609,0.00786391239409,0.00752923348981,0.00790761098453,0.00777003106626,0.00791430575893,0.00774916298703,0.00771046472403,0.00791569162337,0.00771842804855,0.00776960339684,0.00787519642658,0.00779282672916,0.00796558341082,0.00772849391296,0.00787425079057,0.00785000593442,0.00783196634893,0.00778586276425,0.00765476760647,0.00789160089413,0.00778046808208,0.00786563572133,0.00761183462191,0.00781360281466,0.00779625112049,0.00805113962048,0.00753208273029,0.00790481494594,0.00788444052887,0.00744696668515,0.00734093629284,0.00789055546536,0.00799095237588,0.00765900300182,0.00803579963046,0.00773657982879,0.00762097341136,0.00778309965508,0.00744329555108,0.00738163770685,0.00827283921711,0.00797401495357,0.00733397517833,0.0077950216627,0.00749365585935,0.00905355995825,0.00772904570258,0.00790044743158,0.00789980778566,0.00770796410278,0.00779594624114,0.00747069384342,0.00742177276338,0.00773425764254,0.00739369785153,0.00737231495155,0.00786406602073,0.00777382297269,0.00787042533951,0.00773654973736,0.00787752662727,0.00776233126589,0.00786791878472,0.00777733614365,0.00793048382312,0.00750566547071,0.00785018211248,0.0078091692768,0.00786503923957,0.00778532994799,0.00774624090024,0.00790756531553,0.00773088747957,0.00788597544814,0.00785831162012,0.00767308432385,0.00782128905842,0.00783672411527,0.00787998782295,0.00779591152712,0.007883477181,0.00773396623906,0.00772813294847,0.00792776319386,0.00749578109282,0.00768089398069,0.00795255637597,0.00801391869451,0.00775557408064,0.0077948047026,0.00748697617459,0.00791694254697,0.00787487339272,0.00762232634769,0.00767419068201,0.00804128026579,0.00788209448607,0.00774588553731,0.00803295169855,0.00768969852836,0.00785063503723,0.00779246360169,0.0076560960492,0.00785375512924,0.00778048258631,0.00788077356414,0.0077734621822,0.00787146712416,0.00805625842768,0.00801208924349,0.00791329410523,0.00773956124489,0.00764799673523,0.00805912049312,0.00790088852789,0.00778494216592,0.00766159165074,0.00796661697103,0.00767968537318,0.00753337036504,0.00743703258676,0.00783581684478,0.00755778437161,0.00812739453781,0.00788686095472,0.00781670525208,0.00780494751608,0.00776996165909,0.00736646318143,0.00879516322208,0.00800910149898,0.00748865470993,0.00789069925456,0.00746051893892,0.00771807692333,0.00786206070939,0.00773040850509,0.00788739308761,0.00788725550462,0.00776398953909,0.00780575702137,0.00785444935871,0.00745100372717,0.00736578796447,0.00846189342071,0.00769102029153,0.0082636926079,0.00756956417765,0.00788679743534,0.00751852215377,0.00774003608436,0.00757195499202,0.00816038428982,0.00751692599906,0.00758978648477,0.00759984120463,0.00744259944426,0.0073878211204,0.00741992909822,0.00783371659397,0.00779126600874,0.0077302707915,0.00794128514922,0.00762941952361,0.0080655660124,0.00798925954671,0.00809816468247,0.00789727070313,0.00785296303841,0.00774701311574,0.00791151380191,0.00784806604147,0.00777442176201,0.0082652529514,0.00780807953117,0.00792865204578,0.00770734953548,0.0078734589838,0.0080370420026,0.00786546677724,0.00786104632357,0.00778593777396,0.00786578198754,0.00774707615833,0.00791776476372,0.00776063434347,0.00788060197453,0.00781566185735,0.00780285203202,0.00777469328427,0.00784266348647,0.00774641168208,0.00789104452361,0.00865502132355,0.00747952474317,0.00793809675685,0.00770805183474,0.00792525925108,0.00773546471309,0.00782345100497,0.00784363341087,0.00779167503905,0.00784057158088,0.00783803124732,0.00786122574466,0.0078677759949,0.00737951851358,0.00927005614038,0.00761011143529,0.00873204333743,0.00743462470401,0.00760004530762,0.00772464134063,0.00739067195952,0.00791045738481,0.00734105221818,0.00797003122013,0.00781262110512,0.00788329872315,0.00735682340477,0.00747709903606,0.00757016214204,0.00737137237005,0.00765046100485,0.00773627205505,0.00792233960245,0.00787056400262,0.00793419873328,0.00786786743529,0.00779409653213,0.00783637637519,0.00785276986363,0.00797014586249,0.00769265261824,0.00777180009448,0.00768195160738,0.0076317463627,0.00795471931005,0.00751130903492,0.0079324687813,0.00742800079944,0.00816547561026,0.00781163644603,0.0078316330668,0.007685071871,0.00791056847476,0.00784963004654,0.00751319427977,0.00779912877054,0.00790336411117,0.00774152816854,0.00788988966162,0.00773489162944,0.00779990029696,0.00780709718703,0.00778218393668,0.00786349291178,0.00751651206688,0.00782523368045,0.00756630625256,0.00808315643775,0.00763068946243,0.00802106156083,0.00830274868588,0.00768703891681,0.00798940614265,0.00790384030863,0.00764309956323,0.00780860149438,0.00784040951585,0.00789064053803,0.00776988268298,0.00778343804376,0.00798941833285,0.00764407747978,0.00773246682763,0.00793584034491,0.00766717896323,0.00761849787728,0.00750041777565,0.00786104435787,0.00751724696519,0.00859709632126,0.00892108583839,0.00818072771131,0.00735699880181,0.00782807863236,0.00785350132169,0.00785155879464,0.00777380323918,0.00781517136902,0.00782361938724,0.00789355759429,0.00778166241017,0.00780022023762,0.00783714166812,0.00781890569675,0.00780868150165,0.00784047125379,0.00781069755797,0.00780241358289,0.00789397609471,0.00783842004646,0.00781519070203,0.00783179493537,0.00776506179,0.00782482696212,0.00785833198383,0.00777966457459,0.00778656274584,0.00774114502017,0.00773354181522,0.00786748176899,0.00791026316402,0.00770164623078,0.00775688912201,0.00780332668002,0.00776149114363,0.0079127705611,0.00786819588758,0.00775570540116,0.00788812080283,0.00786828772394,0.00777381099698,0.00779439882588,0.00772642665315,0.00771611206745,0.00786238056537,0.00775136319801,0.0077976078132,0.00781426830403,0.0077946229431,0.00783370276778,0.00782781790118,0.00749324649911,0.00749169399699,0.00755526232213,0.00818861192419,0.00762164482695,0.00768288325579,0.00764263990718,0.00797270816404,0.00788099127739,0.0076795335583,0.0077530059995,0.00783616872208,0.00768082522349,0.00767097508146,0.00802666433131,0.00774660865512,0.0078551744894,0.00778702270409,0.00783004133695,0.00780813354077,0.0078420124199,0.00785264559526,0.00779412786165,0.00779485797321,0.00784747809942,0.00775772764322,0.00787518065653,0.00777325170809,0.00772588130746,0.00791909739939,0.00776811717746,0.00774729217201,0.00776837961406,0.00784320142334,0.00790979067484,0.0076799970805,0.00773280629636,0.00769941068583,0.00790573204869,0.00735457054828,0.00801389135799,0.00741080663697,0.00903879483096,0.00737766483516,0.00762338780972,0.00766314494072,0.00753861528359,0.00826599668138,0.00741199257906,0.00813419442921,0.00737904402324,0.00803498219295,0.00885281477529,0.00781048119717,0.00821204730823,0.00774087680025,0.00751041810462,0.00793180130282,0.00760836331316,0.0079877066919,0.00778196516886,0.00788183389095,0.00774468425694,0.00788064579588,0.00785210058145,0.00776830054743,0.00784340385543,0.00782956650487,0.00776876719761,0.00784428514067,0.00780122961817,0.00784043651677,0.00779140622607,0.00772353734657,0.00775805219398,0.00785657623785,0.00773056982444,0.00745052540305,0.00792319696429,0.00789700146794,0.00792749114336,0.00774088314972,0.00779239763724,0.00776841176715,0.00776961680793,0.00780569494914,0.0078736284412,0.00787819360764,0.00784845645063,0.0078850403918,0.00773064307037,0.00778412766409,0.00777147152534,0.00790071183389,0.00774688349155,0.00779999846261,0.007738187024,0.00781397697363,0.0081884730281,0.00791097654206,0.00760772765493,0.0082297935407,0.00777555409728,0.00737966282869,0.00850242781355,0.00773061767801,0.00767779316764,0.0073611734071,0.0074372338506,0.00772339413107,0.00771905658526,0.00787599255105,0.00772994829406,0.00779475120051,0.00771812708878,0.00788946144713,0.00774682547101,0.00793128555701,0.00774093336582,0.00790514364034,0.00766109996954,0.00783601456486,0.00774179210858,0.00792390057596,0.0077995538247,0.00784334339013,0.00774663459519,0.00792246810074,0.00787644139524,0.00787650115064,0.00786290983617,0.00778353905747,0.00747863905647,0.00740707632042,0.00740984554312,0.00755739238915,0.00778257980858,0.00781434772278,0.00787684404855,0.00775796539871,0.00773053032324,0.00794347871477,0.00766250543691,0.00769601634893,0.0076909435435,0.00748718046199,0.00796510679727,0.00753577747219,0.00732717775212,0.0078312509599,0.00779087510261,0.00777985345265,0.00780226699142,0.00778812596534,0.00783746747339,0.0076353318076,0.00779308682092,0.00813006403184,0.00741937334137,0.00773411962295,0.00786308239432,0.00772939944466,0.0078979307256,0.00790380356798,0.00773904715286,0.00787534541519,0.00775018635077,0.00780511707029,0.00785571793597,0.00785728007108,0.00778478196512,0.00773673372669,0.00788200469752,0.00775470082063,0.00781896648942,0.00778362978344,0.00789542805532,0.00775625615665,0.00788832599066,0.00775510346146,0.00789374305232,0.00791749116462,0.0077437226492,0.00751863590079,0.00897663964064,0.00730505088152,0.00784604672954,0.00744515096653,0.00795101880277,0.00770552219271,0.00780600217475,0.00782115897593,0.00788960672922,0.00780606210153,0.00770382439128,0.00737960325438,0.00802005279995,0.00788718249264,0.00755895514169,0.0079067034667,0.00770829694786,0.00770387943604,0.00788457810658,0.00764975205419,0.0078282404158,0.00800509926341,0.0078423896285,0.00757329943238,0.00750883383314,0.00749538212175,0.00741577304055,0.00746029244201,0.00780477864641,0.00789941957356,0.00750105251222,0.00818179842275,0.00731603111593,0.00778074305109,0.00781523410832,0.00789364265313,0.00787663691492,0.0077817617258,0.00776148259856,0.00800702117418,0.00764378511294,0.00800124873072,0.00803038647367,0.00765040594269,0.00754395009203,0.00763892801955,0.0073944410096,0.00781560518837,0.00777725276765,0.00781795600322,0.00784203368073,0.00776112614771,0.00780233300565,0.00742454303822,0.00771431217522,0.0078585081919,0.00771668511537,0.00787474160342,0.00786659882617,0.00762722205463,0.00756550513966,0.00806047273977,0.00775448136002,0.00791696553572,0.00779454172551,0.00782349179355,0.0077773267351,0.00768917571052,0.00787414362507,0.00783964726346,0.00767248800017,0.00775331740809,0.00790383609495,0.00768833991352,0.0079480055231,0.00764444265768,0.00774330924661,0.00780396150488,0.00794455672239,0.00774269905725,0.00789092411194,0.00778590345504,0.00791999725304,0.00788435132659,0.00775694549927,0.00781441703386,0.00784190931712,0.00760942425364,0.00776717117856,0.00784521312871,0.00746838432408,0.00813357699635,0.00766906878288,0.00793297087668,0.00769151985183,0.00780843389265,0.00778476222553,0.00787122112521,0.00776905175552,0.00788468085798,0.00793977194427,0.00790142872132,0.00777755918284,0.00780616618294,0.00796803883469,0.00775175274402,0.00793268648931,0.0077220696319,0.0078571550936,0.00777993514495,0.0079309914976,0.00766992604266,0.00777473087266,0.007739493186,0.00785426209823,0.00780976324991,0.00783146116155,0.00735999771193,0.0081123096775,0.00789885426427,0.00786112638323,0.00776071776695,0.00765315862822,0.00739815448774,0.00732417122796,0.00783257576694,0.00786262497487,0.00780519510113,0.00776100021488,0.00780027947724,0.00789656032687,0.00769579256193,0.00789360665017,0.00788354504205,0.00778396757006,0.00774486215528,0.00786355469856,0.00780123104142,0.00775822773474,0.00772730909927,0.00790534262592,0.00786490715139,0.00775139994641,0.00773073312583,0.00780471894266,0.00782755424204,0.00795277890209,0.00782162158747,0.00778018221906,0.00782392244939,0.00776304887766,0.00783228665736,0.0078428280895,0.00733554674585,0.00778059744958,0.00771915853307,0.00899345397934,0.00782584369865,0.00779063704129,0.00772221834117,0.00792936049015,0.00768677583768,0.00794281918629,0.00767617748234,0.00799706981294,0.00763602866379,0.00796596643603,0.00769409304757,0.00785138182908,0.00779760476656,0.00784748953832,0.00782731758503,0.00762348953426,0.00799053228129,0.00762773210148,0.00793307945138,0.00777496608013,0.00787899509789,0.00744914549524,0.00762864018698,0.0075225895019,0.00754290745445,0.0089318515429,0.00843324338287,0.0080519763776,0.00779871760417,0.00783181814729,0.00770834260685,0.00785414231988,0.00778874133755,0.00782816064216,0.00781455184344,0.00899605622682,0.00740026312519,0.00730052526101,0.00779658154987,0.00799796462643,0.00737394514467,0.0079631233141,0.00733718281132,0.00768666341089,0.00786365820215,0.00781635333459,0.00802853682235,0.00788472654876,0.00782818497326,0.00785250396756,0.00779764516451,0.00781129133393,0.00774003662268,0.0077607529884,0.00790091295953,0.00773150483712,0.0079266516112,0.00771162675279,0.00769206756605,0.00790404655921,0.00766640462491,0.00795064243811,0.00772067261383,0.00793916143926,0.00781235637046,0.0078695490026,0.00773932716326,0.00799886867606,0.00767067014408,0.0075732148253,0.00767882599545,0.00797230595319,0.00780147351124,0.00846343274922,0.00778151880449,0.00772812500174,0.00813712315037,0.00754493418376,0.00778274197249,0.00764919313162,0.00761262427331,0.00799422818969,0.00777110349029,0.00754320135779,0.00794438879657,0.00755162530103,0.00828809779529,0.00752747885647,0.00776072654511,0.0078335169803,0.00778747385752,0.00786444303802,0.00726232915426,0.00900096193086,0.00760109593592,0.00794543543956,0.00747895692457,0.00786096392095,0.00798338536896,0.00764624652789,0.00785272953672,0.00771356475136,0.00741167582022,0.00760946511505,0.00818677809038,0.00751837651037,0.00784423828394,0.00762924271881,0.00772786195927,0.00785014038682,0.00790153694122,0.0076596907124,0.00778283742605,0.0078437045231,0.00785136304539,0.00781447011667,0.00773009156271,0.00788059004852,0.00894243915647,0.00737256691828,0.00777646560505,0.00774834029584,0.00794963099328,0.00743362679289,0.0078625332,0.00737299232772,0.00773493213892,0.00782555232678,0.0077434048613,0.00794303822644,0.00785707242679,0.0077808839196,0.00790648710314,0.00780595628179,0.00776493559751,0.00784294583556,0.00779333569964,0.00770003734942,0.0079218507177,0.00777714983897,0.00784748135349,0.00783722936142,0.00782753792469,0.00786284570517,0.00783799357284,0.00784158485157,0.00780361262807,0.00783464652253,0.00781610260024,0.00782898957441,0.00773615624233,0.00783864120424,0.00779738390101,0.00783406759189,0.00785216447879,0.00771539890151,0.00776516916622,0.00786809494341,0.00797689618,0.00786709981122,0.00782789449957,0.0077977326603,0.00785274109055,0.00778946000554,0.00778859608251,0.00778819329529,0.0078511137332,0.00787524078568,0.00776003955837,0.00787202662995,0.00788771154585,0.00778218220211,0.00785571619951,0.00767095447538,0.00801297948971,0.00763345023747,0.00781396651969,0.00742564888519,0.00782095458559,0.00776841203276,0.00779494897274,0.00775751245597,0.00800151773015,0.00781473017191,0.00792860642529,0.00769517558506,0.00777653124347,0.00781744889391,0.0074416867394,0.00737107945246,0.00774536099303,0.00898799771231,0.00774036170313,0.00792953561525,0.00801855908954,0.00785345759397,0.0079131670287,0.00769641590098,0.00736686074026,0.00738249088936,0.00742490347747,0.00783945941671,0.00779772103167,0.00786382017946,0.00779771836962,0.0078486864676,0.0078782839754,0.0077431878204,0.00792356785442,0.00773046082913,0.00773093239337,0.00800938312734,0.00738073384001,0.00805162925449,0.00742800831442,0.00797839002613,0.00734563669006,0.00774561536998,0.00743935599341,0.0074073164253,0.00901393744808,0.00772352597217,0.00799436923158,0.00744085848258,0.00785460220953,0.00776199739787,0.00783017705877,0.00785810762029,0.00739977683746,0.00743032340584,0.00738439270872,0.00780035257673,0.00752642392439,0.00779293613582,0.00782464814993,0.00763666204503,0.00742011660228,0.00833518780496,0.00747741782766,0.00770295620289,0.00783157107487,0.00767452416886,0.00773800410602,0.00758817080153,0.00797674372251,0.00792098800441,0.00768032139216,0.00787887233601,0.00777968689182,0.00789343278439,0.00781902150798,0.00780849706799,0.00789424034102,0.00784580416566,0.0078512756001,0.00777157375622,0.0077274979306,0.00784920823022,0.00785851418832,0.00773262845607,0.00784477308175,0.00789097501245,0.00780379047978,0.0078223951439,0.00781132163747,0.00788199110135,0.00770694708292,0.00786115753549,0.00784988817515,0.00783103282718,0.00786870070857,0.0078272183725,0.00784122724476,0.00801560523568,0.00762352149769,0.00781998856687,0.00793300836126,0.00784890301661,0.00773681507351,0.00785352730664,0.00783871308654,0.00786196558504,0.00780863534003,0.00772240670374,0.00779788002194,0.00815712492709,0.00785477397641,0.00815322939954,0.00813436170372,0.00786067757078,0.00781939959537,0.00784594708638,0.00761603121592,0.0076762615778,0.00778486347253,0.00759024388681,0.00800531531917,0.00784730748342,0.0077650321143,0.0077679583939,0.00779298160167,0.00764952161262,0.0079751722417,0.00786253397888,0.00779598474361,0.00778498963152,0.00767016574455,0.00796795455615,0.0077631333185,0.00789450048597,0.00775233081129,0.00805799873914,0.00736066571538,0.00791461494762,0.00750114864426,0.00778615081126,0.00893823859779,0.00779522226415,0.0078027630165,0.00801116272777,0.00784854908596,0.00773956082434,0.00776109796913,0.00768999360686,0.00792913263857,0.00772644046913,0.00791078630294,0.00785838030732,0.00777877447954,0.00789531286119,0.00775481890836,0.00782864049853,0.00782809611694,0.0078307544477,0.00782228353906,0.00781852995926,0.00777331915126,0.00787371958015,0.00773012340455,0.00792243423153,0.00777595017617,0.00787513675514,0.00773607111211,0.00796079695975,0.00763131988533,0.00774274991531,0.00786438905595,0.00787880456027,0.00777626571373,0.00771998546446,0.00779335501961,0.00786933022597,0.00778781469502,0.00775455066059,0.00787479207361,0.00788860763387,0.00788857427877,0.00791686525408,0.00792024141829,0.00779297277714,0.00785183440206,0.00780082450571,0.00787835816041,0.00776924745002,0.0077784479555,0.00786858020688,0.0078293270702,0.00788411230152,0.00778387409976,0.00888770703262,0.00796758960024,0.00809237172095,0.00733611566701,0.00772265875453,0.00773492728799,0.00791277582542,0.00771817442996,0.00789984767218,0.00745431688447,0.00793752126021,0.00734005960501,0.00745195351633,0.00813729610485,0.00905189114296,0.00732800155324,0.00736623136025,0.007993981278,0.00779852539183,0.00769920093573,0.00788651912346,0.00771513964915,0.00792674843068,0.00771661949455,0.00753918978559,0.00758869169021,0.00747120675813,0.00751340914831,0.00760136905651,0.00779112150103,0.00793834635916,0.00781649613062,0.00772145133321,0.00785422833461,0.00789084316185,0.00778157938023,0.00780575426774,0.00789383814128,0.00776328093782,0.00782402434413,0.00779914106546,0.00780132695838,0.00781218292212,0.00776099902084,0.00788807555739,0.00777488226516,0.00765508006325,0.0076889352147,0.00743144556996,0.00804248685064,0.0077971034252,0.00777031982688,0.00787377772819,0.00776074156782,0.00777626756958,0.0077865867006,0.00763833674366,0.0081907620478,0.0074679961324,0.00788429904188,0.00743921042552,0.00781551383834,0.00776815496168,0.00896104022596,0.00777700798847,0.00784566577665,0.00789902379438,0.00774313386002,0.00763423598063,0.00920225069227,0.00747467480093,0.00800034809068,0.00730240494684,0.00781923038516,0.00779537100711,0.00783221710711,0.00777513574122,0.00778239731592,0.00786322557568,0.00783042069911,0.00774778968959,0.00781762525851,0.00766040954904,0.00767558257591,0.00796915248568,0.00789793216058,0.00775682616364,0.00775172099174,0.00791003549639,0.00776726043726,0.00786123007483,0.00775737688003,0.00782830482259,0.00786496541989,0.00778283292471,0.00786430208561,0.00780279100842,0.00789892862302,0.00776054233458,0.00775346483759,0.00787330420858,0.00791452055988,0.00790272620388,0.0078065900449,0.00780484710327,0.00736219451939,0.00738426930234,0.00746126618399,0.00742884559526,0.00773022699643,0.00889855116351,0.0079133824106,0.00736228258321,0.00800346888932,0.00777516267376,0.00817985051978,0.00750396305573,0.00747224662162,0.00775485638708,0.00827026283003,0.00782117393007,0.00792635242989,0.0077739624072,0.00784650430957,0.0077329852824,0.00774833436936,0.00789298980843,0.00783553868668,0.00769960016587,0.00783461410339,0.00782522494529,0.00780221632372,0.00797664085567,0.00771956287226,0.00786471549106,0.00778706298623,0.0078236202702,0.00774515006067,0.00793495307233,0.00771486885687,0.00791068994005,0.00791767202467,0.00771159788401,0.00752368581582,0.00752248605557,0.00751462932857,0.00754434139149,0.00752108604145,0.00782035533736,0.00782459153632,0.00781983105978,0.00774319829939,0.0078868361636,0.00775455176814\n};\nconst double PHI1610[NST1610] = {\n    1.78931303873,1.65288815053,1.75860399374,2.30909135267,1.68164876231,1.99066953958,1.98320088197,1.92255044518,1.85232786157,2.22289421717,2.20202752018,1.68941047368,1.62251240835,1.66828732861,1.20758811057,1.26398114832,1.72328690797,1.80270671843,1.85483456704,1.94756211289,1.89632977599,2.02566842214,1.96191234078,2.06334967706,1.94415811346,1.98684037902,1.62498510224,1.76626728011,1.83316691969,1.69947282827,1.69904658615,1.86202498654,1.80643021905,1.82124451778,2.0770914483,2.12735375462,2.06676240863,1.52032991558,1.51850312493,1.38140591501,1.31434874587,1.45189854571,1.45030887724,0.117742706528,0.368119308193,1.09155602771,1.6788510091,1.74532367476,1.74865814184,1.60614353165,2.24056154295,1.8240249211,1.82364252863,1.75381003763,1.75463110565,1.61283701593,1.59669995909,1.71915400409,1.67587756904,1.72971751375,1.66766246977,1.82175505661,1.88817895425,1.75210412592,1.74693551356,0.923810425998,1.03339762405,1.24107353206,1.54850138196,1.54468147544,0.925773752989,0.891277773338,0.938495151048,1.41077126678,1.46896071143,1.33593328688,1.40423215551,1.61155275077,1.33396866531,0.99626796929,2.7221813061,2.66301947305,1.78777426952,1.71867137407,1.65279069223,2.04602127221,2.36910375535,2.31509265358,2.06370871131,2.06695774602,1.78837384439,1.78718878647,1.92656467568,1.9270365868,1.8584746018,1.85742266745,1.99777888525,1.99726823562,1.92411633375,1.85066741398,1.91362225985,1.82319745772,1.8190160337,1.89409573782,2.15875766775,2.48456422931,2.25979907057,2.33503469327,2.43105004111,2.35136774736,2.42391828445,1.51860355209,1.63057862194,1.50381579748,1.55804428992,1.564014451,1.0751307599,1.13430549091,1.53446701179,1.52768599626,1.5151054518,1.53336049665,1.56412605578,1.58848600941,1.69725375011,1.61802496687,1.59368703499,1.64551993963,1.83001146563,1.75754142388,1.87305270841,1.82804017454,1.72244885335,1.7499988638,2.08380605094,2.10170379856,2.18194007491,2.15173234375,2.21059616848,1.51004929057,1.49544173357,1.84954880849,1.79473558429,1.7241898215,2.59380653392,2.63802485345,2.52530453218,2.59927195163,2.22883753204,2.30195729911,2.08638723132,2.23636408654,2.28825229317,1.98023741666,1.9931535538,2.74839555865,2.71249137331,2.86180177933,2.82372294058,2.97500034715,0.905198191845,0.839685113148,0.568176548813,0.156973756207,0.526428768912,0.30675414954,0.239313127046,0.188162809581,1.22565625646,1.16868832589,0.976810682231,1.04435572061,1.58495435322,1.51790050641,2.01890611151,2.08415984949,2.2396260065,2.15965587311,2.16692373339,1.96108729442,1.89450007188,1.89408632512,1.54131218152,1.3934189236,1.46223392584,1.53335533212,1.77939531348,1.90332824841,1.83315783611,1.76972311494,1.85195855088,1.91258094508,1.58674159128,1.6471827743,1.81046939463,1.86349431003,1.80142003265,1.93602758868,1.88205580621,1.94555721893,1.96695179112,1.97541880686,0.944223132983,0.795805479737,0.847179118914,0.920385121126,0.895707693633,0.820979277504,1.38847311802,1.45642692603,1.30304624276,1.30538594228,1.37811449849,1.44856208105,1.39186414722,1.40390192399,1.4666362529,1.47047639636,1.02056158521,1.27147920882,1.3339926657,1.25811713871,1.32572439151,1.28087675998,1.16468458524,1.13261992092,1.00512165878,1.05189167292,0.854797781074,0.923788848675,0.988119580513,1.00344249216,1.00530458264,1.11656068812,1.03812973602,0.899386003689,0.95594818035,1.61409030766,1.61655022431,1.68474276697,1.68498321879,1.47208978208,1.40134965398,1.54756928582,1.54485892998,0.75936707249,0.81532633286,0.885449226907,1.03278902599,1.14166600131,1.20355550355,1.33397068334,1.33250559375,1.26936385139,1.46683642261,1.40014727569,1.53604958294,1.53868417074,1.46841954538,1.39906326909,0.72382936918,1.13052906218,1.26634756883,1.20010594432,1.26745537761,0.704313548653,0.746246684719,0.829633574969,0.759245538752,0.973759933162,1.08693980344,1.23154946342,1.30576494676,1.36347026836,1.34976093002,1.72066285984,1.65475112042,1.60874888396,1.53813020301,1.38550434597,1.3168782506,1.18087081948,1.25019200042,1.25409439367,1.08436971207,1.12008632477,0.731531679571,0.779347206951,0.872156160145,0.726105179858,0.836096788476,0.7627073968,0.840644792743,0.768479344956,0.405626855238,0.47619378963,0.483880889137,0.68850256823,0.550626952991,0.658591368177,0.634798337104,0.564539821382,0.236889680105,0.150107516573,0.988928113983,0.945703250237,1.10350378853,1.07435357728,2.58701486587,2.44134971475,2.49559940599,2.57189536492,2.79139826697,2.84400220699,2.91912056735,2.66302766843,2.59113690096,2.59049055427,2.44011678152,2.50193536471,2.78072013241,2.27022886318,1.59847522246,1.59021270418,1.5208251006,1.45879592663,1.72299253941,1.86598440257,2.03482614631,2.00431661852,1.899981366,1.94580381341,1.96073997673,1.98978524653,1.98281479766,1.91094753431,1.92402350543,1.85390039206,1.84776766605,2.06494846266,2.06050231034,2.5253808811,2.522484206,2.4516542575,2.52753625861,2.52419287787,2.45488701161,2.20114764488,2.13666819095,2.1352859611,2.20120308581,1.72182726902,1.65442880782,1.72176387967,1.58709520733,1.58732856514,1.6547169239,1.66642074222,1.65809371832,1.78613889981,1.72130018333,1.75127442983,1.7382384447,1.79477383007,1.86976300826,1.82379571074,1.88575490737,1.88371245271,2.01218385567,2.02507577992,1.95933252206,1.95303463907,2.0179148468,2.09009255446,2.08927354779,2.28249075963,2.46790476371,2.51960754291,2.36764095126,2.39126944349,2.41436509176,2.48889353779,2.28896745873,2.21506993402,2.15062318694,2.15708639831,1.00016087135,0.938992132742,1.12165803661,1.7477255397,1.68271549235,1.61203836712,1.60564932614,1.71397618956,1.64211163399,1.58162900191,1.59144385403,2.57460670226,2.53517975834,1.48667406653,1.33693000754,1.46539465775,1.39170780259,1.54418648266,1.69312070763,1.59735066813,1.55766942269,1.6375063237,1.661334694,3.11225663093,3.04555191309,2.8602405374,2.91253429332,3.0058981142,2.99237527736,2.61546805389,2.60585902174,2.8686079329,2.50922069759,2.4608447537,2.47698152182,2.55240367739,2.54828238954,2.7996370349,3.00219100998,2.92240708847,2.87653048217,1.40050084043,1.36418638745,1.42011342732,1.32419475718,1.43826810779,1.4569986963,1.55194011926,1.62695859899,1.53301867784,1.58958963743,1.68490620044,1.66635280514,2.3654510623,2.47919506484,2.26000053903,2.1828840512,2.01576691846,1.95462958085,1.88571570437,1.87412798003,2.0627914029,1.97324772879,2.04061810828,2.00768562789,1.92215844692,1.93498497573,2.1114095258,2.16283010451,2.03798507623,2.023517909,2.1377368825,2.07187130013,1.75922572196,1.90613622641,1.77319658783,1.84607929611,2.93451138689,3.02306838646,2.95319399853,3.06090584479,2.98279012232,2.65355914098,2.49776660289,2.88907482017,2.87575988742,0.500333872972,0.436013598067,0.367385095912,0.1190718413,0.98631633587,1.05812574337,0.769241426842,0.766493286146,0.632799919313,0.700612536171,0.743155878022,0.795160665593,0.779847608719,0.708390241847,0.193189012269,0.12533419166,0.0983189162541,0.0539684087483,0.26210954538,0.331945021069,1.30147655136,1.35648070304,1.4548647739,1.43243157677,1.45875762635,1.38018032833,1.4484595651,1.05778353425,0.983357961102,1.02404084013,1.24885893988,1.18206383758,1.11542703503,1.12244764468,1.99722498731,1.98530588523,2.13403891187,2.07188023939,2.02969251172,2.09377833963,2.16487672572,2.16956273152,1.56916423733,1.69112860876,1.70026998268,1.63792645984,1.62046300338,1.56080678429,1.5056472209,1.37910219634,1.45278721228,1.51401357773,2.34710222374,2.27101285688,2.22319726337,2.24558557126,2.12430935075,2.19170529723,2.11097495222,2.1519172053,2.00187023733,2.02902007238,2.03912933479,2.10679780029,1.06992722594,1.04670993474,1.17488469372,1.10161503192,1.24513502639,1.17445120094,1.2681874594,1.28874177137,1.19321512547,1.14298142879,1.35174355671,1.48974606781,1.4826610633,1.41231413043,1.43672316706,1.37775580043,1.42859108328,1.3609661775,1.22112209525,1.08639283957,1.14376978479,1.10340627982,0.83004079578,0.749859676484,1.06203711284,1.06502718407,1.13495711224,1.20283114637,1.19760872663,1.13253358117,1.47803416281,1.4805880843,1.40721904281,1.35478193178,1.40139594539,0.805225054785,0.867126032321,0.905799707747,0.981678274919,0.881487360331,0.855260489455,1.01005339282,0.96013974323,1.07268698768,0.934206647413,0.942648472212,1.01370147128,0.997500945074,1.06482995452,0.416007992962,0.782352410114,0.823397230859,0.801411276494,0.675573946703,0.538436941946,0.80539356628,0.931065229167,0.865196624782,0.800185579758,1.19907011648,1.13053940019,1.06368819493,1.06827602909,1.1469849279,1.21819665628,1.13828116653,1.27879032251,1.27146966448,1.2022191223,1.23897283925,1.17957158716,1.52216318378,1.58918983179,1.59191069748,1.52958616693,1.49087205744,1.47986474002,1.35057915651,1.40934020743,1.04307434672,1.11294921126,1.22772573859,1.24857720537,1.28555361255,1.14050068113,1.11690244942,1.16707422123,1.32610802007,1.24755418532,1.3212306576,1.24573268953,1.19569020604,0.425774564719,0.653714360744,0.61667412716,0.346248351701,0.267088519067,0.358943743829,0.2316167416,0.298891294206,0.0340073505091,0.105030468053,0.0894334878676,0.167891439665,2.50962521713,2.46535853232,2.3910701431,2.35876876223,2.46979159421,2.39398327498,2.5831103981,2.61437047107,2.69325050998,2.62064609619,2.82112128259,2.86780384447,2.90208901671,2.93178525729,2.62676690042,2.70010238031,2.71738243023,2.65538150604,2.30776601395,2.0267136022,2.18718858532,2.75043849213,2.78946310796,2.73724622866,2.66109082063,2.72165753354,2.64293733021,2.58967277385,2.58051330346,1.79175501162,1.93743854021,2.19995507444,2.13648179231,2.00283852678,2.06933544393,2.19876396193,2.53216938391,2.47414384232,2.30283759537,2.40249241855,2.38134663768,2.61896844777,2.63000238784,2.66865093498,1.94834919818,1.80930254784,1.80153808972,1.66884384147,1.73083598486,1.86000635939,1.91570833502,1.78405010585,1.78776953595,1.8376908569,1.89686083253,2.14567476592,2.07332408628,2.19380749857,2.15489936154,1.3546383683,1.42737960088,1.36033596141,1.43346958605,1.5017095335,1.66120454758,1.56376340507,2.16984722555,2.09544802039,2.0797416695,2.19622461041,2.1815825281,2.11826485675,2.12581829212,2.26730002491,2.32750726739,2.39475551592,2.2689828556,1.87625062242,1.85821049812,1.93110294149,1.9398436281,2.29630651683,2.36574652928,2.22960143812,2.22882635693,2.36338486892,2.29486589256,1.05183882845,0.91822682655,0.989480119453,1.04351014619,1.7416371802,1.67026101516,1.66280739384,1.80552718372,1.72490276283,1.79619112726,2.33442553982,2.30296484304,1.98069862961,1.93279301904,1.00170341481,1.05497753959,1.18929877325,1.13357634154,1.15845864294,1.36261731671,1.42626413796,1.46565336863,1.4398853773,1.49586852715,1.60595833931,1.57465565188,1.62926319821,2.54866658813,2.48105932818,2.54165233765,2.46782296552,2.41125260472,2.2677972028,2.3369507495,2.64510530565,2.56659136955,2.53371171668,2.51340576397,2.74526460156,2.66922104201,2.61961625435,2.61027673632,2.65045688587,2.69079915026,2.76763464567,2.80135538263,1.48104361626,1.49491581595,1.57013034021,2.44008088077,2.58471938485,2.49994822013,2.57279077935,2.37652592589,2.30234386233,2.40559137685,2.35110554816,2.21512553074,2.14389209107,2.27412722063,2.25367048594,2.12845339707,2.17923749407,1.88999226527,1.81608105343,1.93013095223,1.9497913019,1.85399994876,1.79955012902,2.84926827302,2.79025266022,2.93179865753,2.92911194768,2.81359196917,2.71417909308,2.84713916342,2.78868160674,2.73321307501,2.69094184064,2.55064545379,2.4070235083,2.42385850124,2.4928808303,2.52954544297,2.45538239184,2.62111330539,2.6767538185,2.74895708396,0.242545895607,0.245139263859,0.196047886605,0.231028899944,0.302310218509,0.192601747705,0.163782130446,1.10602537515,1.17840997586,1.23341767337,1.51087395496,0.591927242828,0.60083585094,0.174233200126,0.213175569692,0.29194362617,0.409016921589,0.331317320497,0.544938889549,0.447400993256,0.342759070959,0.330457432232,0.418411897502,0.4724682575,0.46226904586,0.396548194122,0.374293683554,0.31932174304,0.76468636673,0.941116518936,0.850885869548,0.922725822522,1.44200227875,1.57988876624,1.31727298929,1.25293414197,1.19314838151,1.09092687444,2.04174362026,2.04237692306,2.11623833004,2.26723235998,2.34115468187,2.36038277338,2.29964856554,2.21085006028,2.22442136314,2.58113831093,2.56449049434,2.48963104907,2.43417084012,2.03193196601,2.0265495626,2.09896326233,2.10072569194,1.95926461221,1.9629169837,1.30054579107,1.2315271993,1.17120738726,1.3092188133,1.25012289411,1.18119053089,0.955042357453,0.874616195038,0.97806003794,0.85251330446,0.915238392624,0.738566446988,0.677531466888,0.610508541859,0.735412342619,0.669796798668,0.605912749453,0.6346477893,0.618585137001,0.493140000884,0.545697677298,0.290385334113,0.370105292596,0.935777749555,0.875041046307,1.00555505661,1.01559299066,0.886467240332,0.957124144218,1.43841887778,1.43196731059,1.37305101651,1.30862919263,1.30423388183,1.3622451532,1.40008743654,1.46210135397,1.33064559028,1.32442915439,1.38916699157,1.45738521737,0.974196116673,0.903811457421,0.841318181567,0.835641914176,0.867216578507,0.887663392084,0.959468759898,1.18827899289,1.11648955144,0.578289938977,0.650548795703,0.670264733285,0.622743094474,0.548531379225,0.449140460028,0.523288001,2.7133893081,2.78108199414,2.80005545051,2.73367261779,2.66104327811,2.66365856458,2.31207098675,2.26757550257,2.13831775038,2.05971340969,1.98553676548,2.03889617877,1.93885095528,2.01206884697,1.71417749431,1.71476852156,1.64672026342,1.57784486087,1.64764796651,1.57937713887,1.99846526045,2.07343634966,1.53852551887,1.68144614422,2.38780475887,2.32892262446,2.35396808224,2.27371999894,2.25897655889,1.93634472106,1.93319109054,2.06382293762,2.12951436695,2.11856497684,1.99442306754,2.42776301113,2.39072161577,2.50649819103,2.54983559691,2.44787018615,2.38903288624,2.47027430286,2.50063416932,2.34070300771,2.36966999711,2.07540361697,1.96435238908,2.0416621427,1.3720167754,1.41569066532,1.53745594736,1.46720047976,1.40727197478,1.41396130527,1.7930936149,1.71802920659,1.67759303726,1.74727374828,1.82583217618,1.58029910694,1.54623193859,1.50347500102,1.48743017748,2.13640696571,2.11417236628,2.16607173575,2.26103388577,2.25389997068,2.38831303541,2.31704901633,2.33225680566,2.4595909394,2.46112651021,2.39794274554,2.32935953414,2.3935925454,1.65613986031,1.6493455686,2.39432984431,2.41132432683,2.48076765794,2.47132880969,2.29644028878,2.3413105447,2.45609779287,2.42052604239,2.19546733538,2.16733213952,2.08845316879,2.180416302,2.21605325062,2.10126566425,2.0578255663,1.93462762021,2.00263451367,1.70559668608,1.82686542374,1.7721621481,1.95932750089,1.90536437427,1.92803930346,2.03772440435,1.39993226106,1.32260649308,1.26727255761,1.47088933985,1.42278784824,1.50191659951,1.52649548553,2.75507229059,2.68798228137,2.60086905725,2.57905778129,2.72922538884,2.67457116479,2.68266813643,2.74957855659,3.02409792681,3.0802059009,3.02045608572,2.26888200434,2.33028624723,2.33791453638,1.6260461972,1.73538298787,1.70319207176,2.44818407405,2.38053992007,2.3801090948,2.44604689406,2.51957983574,2.51781094827,2.55640062522,2.61480427324,2.56271754932,2.62847580391,2.82277217836,2.75858520187,2.69321668032,2.68536460395,2.81143974523,2.74021769036,1.27901992836,1.21981801983,1.26829998801,1.19631955683,1.0289044757,1.09600701561,1.22602085919,1.2875837397,1.38955439581,1.51199570948,1.58158405167,1.58518156208,1.216889711,1.35851939649,1.32036403385,1.25145478982,0.85402731406,0.86416671188,0.655446129448,0.660085112387,0.785127033708,0.721885794104,0.728150045714,0.786681730641,0.362652865223,0.36471721994,0.304847017218,0.230549714552,0.232472230347,0.30318407292,0.405524983515,0.898877778131,0.834772313091,0.84411629243,0.918846512977,0.635708521499,0.703328415602,1.1748141327,1.05161326274,1.01216591995,1.06848118834,1.32364605223,1.3960391701,1.3201625894,1.35573684053,1.36778977385,1.3086209749,1.23735319465,1.27080156847,1.28401725719,1.2247616654,2.15207228744,2.1192506603,2.24036988449,2.28193295992,2.23989969198,2.16614321096,0.234198423791,0.282145527032,0.277981088503,0.584857173495,0.516689400796,0.704791269801,0.731785451818,0.978244254983,1.05060742294,0.913464893478,0.924282117497,0.506371776635,0.544236387673,0.514908043999,0.443943833026,0.31881202075,0.397325235003,0.396178256305,0.430278183943,0.273366478823,0.319836742004,1.79433699663,1.86131777256,1.86712930904,1.80617345302,1.92065749525,1.98771995955,2.04662267712,2.04750841034,1.92031764974,1.98830987748,1.859741828,1.79682571633,1.86675081258,1.79310859193,1.72560371759,1.7223573451,1.68263545999,1.79723460377,1.67152496366,1.72526581069,1.6176736956,1.55524730434,1.61946332682,1.54629555914,1.47593619214,1.48315414547,2.42751245805,2.4833570867,2.53869920735,2.50730070414,2.26501024585,2.2332430231,2.16563795867,2.21315115927,2.11920745479,2.13519388386,2.41164957672,2.40503622404,2.08714587675,2.02293758924,2.01664840366,1.87829756784,1.88398401845,1.94959391526,1.24833583737,0.93554500627,1.5454373543,1.48356446094,1.61516206073,1.62142793562,1.48958874677,1.5577028637,1.32947948838,1.40087000943,1.31872934769,1.37955506603,1.90503236598,1.83575281152,1.94526863301,1.91425346323,1.6396211763,1.70754810319,1.6337560978,1.78059114519,1.67470205576,1.59760499473,1.53742031183,1.55474832543,1.8703005228,1.81098003258,1.73471035989,1.77639518971,1.76535768467,1.71340653929,1.64257647493,1.63073363993,1.69058393069,2.24426189113,2.29717676157,2.40304029847,2.37795940249,2.32791885457,2.37731039543,2.43439386927,2.40779982153,2.29852956564,2.27530161934,2.14018106718,2.06152939862,2.00567658348,1.92612913116,1.85346855521,1.28982460351,1.2365815386,1.39154704606,1.36790496124,1.26234474364,1.33911827051,2.88915773674,2.82108146843,2.80549719391,2.95262286989,2.73178638039,2.62415816888,2.69913977437,2.5834508349,2.60708381074,2.67909790096,2.80308443383,2.84916875327,2.92450920302,2.8162760879,2.88473192335,2.94960483542,1.67371675038,1.67545985963,1.60431018711,1.53632874,1.54190573182,1.61163370602,1.20588737163,1.32758696157,1.33693520297,1.2782698585,1.52403066008,1.5185582945,1.40312600754,1.26961293839,1.34237949884,1.27963183244,1.10838882456,1.14121900316,1.09657411343,1.03046099775,0.983397858814,1.01690784259,1.22424523361,1.21163049235,1.13243174399,0.456617505576,0.437406555835,0.713638064302,0.822698318968,0.804201891919,0.769060311676,0.693072966347,0.673077516745,0.730986870825,0.643063303905,0.629356565594,0.526142732493,0.434651056851,0.451886768103,0.437101316644,0.452219645762,0.502354867093,0.566894320337,0.525777053369,0.575677142406,0.763024579785,0.695755204897,0.700242152244,0.506099401538,0.577017209364,0.788362340543,0.718293148501,1.41615919733,1.3831124132,1.44502303303,1.50938618074,1.4960378362,1.26621001905,1.19979785985,1.13949823366,1.26260740533,1.18842258264,1.13179370446,1.19829418126,1.18649765043,0.357340966286,0.505504113546,0.403386799782,0.47254420438,0.406811319098,0.354977405186,0.389891632384,0.468162323351,0.575365426965,0.685212980807,0.615417027356,0.714178616324,0.679944863152,0.611568067571,1.05931890724,1.14603899837,1.13314190205,1.08705504168,1.01265990019,0.997607049265,1.73299780338,1.66229279512,1.67660973246,1.73900353269,1.59677354569,1.60509056808,1.74664945078,1.74726652591,1.80958688294,1.87371114906,1.87230823155,1.80759620393,2.34092813869,2.34313957333,2.26797161154,2.19680911621,2.2578705623,2.23018889847,2.31063677457,2.32649543046,2.16576881884,2.17781994591,2.12027490499,2.11131007118,1.97977340288,2.03515547609,2.07633221543,2.01586072076,1.99680720391,2.06145314522,1.93843326058,1.94297160651,1.10666170834,1.03524451054,1.0544346357,1.11508262402,0.970274671177,0.981118606942,1.24062924634,1.30350200274,1.16602007401,1.17033703055,1.2978795069,1.23063522589,1.4585453065,1.58771654125,1.53038101167,1.44794720894,1.50867523724,1.57656544902,1.69245196193,1.69871373231,1.75591792407,1.82351989411,1.83915002396,1.77773725771,1.85297837009,1.90983919952,1.813465457,1.88982435329,1.7170282896,1.63980821229,1.75645812625,1.77608073581,1.67940825424,1.62169800543,2.2682377463,2.30471194721,2.36471720041,2.34589948844,2.22999934881,2.21207678721,2.16089921988,2.23913782117,2.25411628947,1.8364974581,1.79495147695,1.87219389601,1.89280778035,1.73962383165,1.76068345707,1.04118334348,0.97244173503,0.909102352097,1.44420974518,1.31446841159,1.44691372672,1.38336922789,1.18064953389,1.25096220729,1.19484765378,1.25793317001,1.46492579149,1.45871209154,1.39983751534,1.329027831,1.19988692159,1.05226131905,0.524099439732,0.643769251507,0.568627083952,0.584616593745,0.658806771863,0.532409825353,0.562712777432,0.676123893282,0.68330936945,0.638789907759,0.57569865875,0.832695935974,0.898267820228,0.963350298982,0.969714275838,0.447365033721,0.374656352987,0.31942500887,0.351145515883,0.88590722859,0.814092388358,0.933265718811,1.00923379007,1.06605977528,1.08041378168,1.010117467,1.15197956083,1.13855285195,0.507425250705,0.627443534008,0.584283355968,0.603884795085,0.533750276051,0.479673207592,2.14045181647,2.20173628625,2.172762011,2.25357943206,2.27092599,2.10134262223,1.98427618114,2.02493346037,1.96779584091,1.17525872726,1.10596101317,1.18777281924,1.11626139644,1.37667902728,1.31047496976,1.24164077828,1.24035578043,1.32254084277,1.38717060625,1.25110713753,1.2452315691,0.786072253876,1.09805037503,1.06256553322,1.15293142319,1.13859852331,0.751834162056,0.796104326961,0.873799542873,0.904927226353,0.641383701646,0.668492933688,0.837530578806,0.770673242924,0.697638071881,0.497695075305,0.564366985943,0.498224372552,0.569071436144,0.969981302872,0.97832137342,0.972428457646,0.843847376767,0.915928994116,0.937024749966,0.897628741796,0.893139251464,0.770240901253,0.8072071322,0.70661289373,0.636164436512,0.446623969548,0.592902308906,0.504705724011,0.57560140543,2.11602204364,2.19758038683,2.19041309255,2.05597945209,2.13317421217,2.06599679357,1.1723253011,1.17468042899,1.1043315843,1.03675616402,1.03909671397,1.10893919997,1.37670572836,1.38223324938,1.30921942469,1.31178935248,1.44534569473,1.44832799451,0.80626409341,0.757988301917,0.864603997233,0.789706504037,0.693847963475,0.626361664377,0.700568972286,0.630458081779,1.06864852444,1.03255587109,1.14749499912,1.0930442861,1.1583143379,1.0555397595,1.04627901945,1.12383278087,1.11642763148,0.988558813822,0.918107013481,0.986643294016,0.841120566835,0.913571545155,0.775531466356,0.92269149318,0.831278106505,0.900330240956,0.900666611223,0.760961890299,0.834298958277,0.763659113404,0.652366011906,0.693538983325,0.593537717145,0.666912764041,0.818466823125,0.771235669702,0.694075061131,0.667160818582,0.794422713225,0.720827092916,0.879252603249,1.02532780992,0.979616939218,0.906200335264,0.931381174585,1.00380166833,0.860166101835,0.862593348396,0.788524757089,0.737259215528,0.784758104649,0.57747843932,0.469626427154,0.543999229374,0.427408736845,0.469893168104,0.544410345391\n};\nconst double THETA1610[NST1610] = {\n    3.16383736864,3.22592112967,0.447316867188,1.77438754874,1.6955163572,2.8905268851,2.81187973107,2.93541301582,2.89324955894,0.165367712105,0.892869119268,0.477350922799,0.433691456685,-0.131762220569,-0.0187543814041,0.0395478975425,-0.0740850335772,-0.0868891466392,-0.0276271829491,0.104404587802,0.378992590377,0.37132350609,0.328481047478,0.175753815553,0.247652363501,0.181349564627,0.361816810972,0.365928282068,0.334151990279,0.324372776637,0.253886155326,0.254207592292,1.45531094029,1.61217300278,0.776479725384,0.920269841299,0.861523880524,2.98193404035,3.1355116209,3.13172749008,3.09091674083,3.01710775312,3.09405945793,4.49894222206,4.20609280948,-0.138887839085,1.62241726986,1.50729474651,1.58136011861,1.58784486827,1.82754577154,1.84183702837,1.68801827071,1.72999098369,1.80533412359,2.03524077925,2.19055131979,2.27423945576,2.07542681992,2.19497423591,2.15386246534,1.92016295312,1.9592087451,1.9594227112,2.03756759764,-0.343067455217,-0.202269450291,0.755720218983,0.46689753239,0.539215208107,0.825207010877,0.731135827648,0.644812141887,1.37910112309,1.50539230207,1.4895459297,1.45794607039,1.73350639545,1.56942990751,1.59172882754,2.75204363003,2.87569054096,3.24188545754,3.27337329958,4.6668639341,3.43622473913,2.739766237,2.82553101282,2.92196766822,3.00641894925,3.00711347161,2.92662775853,3.0136646349,3.17348217862,3.12983376365,3.05171397998,3.05271954454,3.13483780523,2.68908530304,2.81001523625,2.77372443915,0.49368262318,0.571415024296,0.457359097338,0.475335722665,0.864546270109,0.960535147896,0.934611894884,0.669393942479,0.830145297376,0.78868295216,0.103674323658,0.212328818786,0.182653950831,0.316934596137,0.237674290072,-0.0567020788071,0.00358314932545,0.687189683566,0.7627810453,-1.5431810216,-0.171259170246,-0.0454944567591,-0.117010206257,-0.00563888198299,0.00806136699341,0.0818230131975,0.135309228173,0.194208000168,0.197985517197,0.11105045368,0.0478133940745,0.124041770697,0.0567878084602,0.333656172779,0.242675985436,0.238468671957,0.387748506241,0.328493517598,0.915625401273,0.993009113697,1.30865541428,1.36221147158,1.19607031889,1.06486234798,1.19571594007,1.32591831006,1.33525297698,1.63723783122,1.67365612708,1.56506400185,1.055740994,1.13158478408,0.970812879259,0.888747664323,1.3648882758,1.1883038206,1.15996738906,1.39308926377,2.4670367303,3.09243613282,3.14955221193,2.92439261172,0.678024550631,0.0801467441636,4.35790743958,4.2659588401,4.56163858223,-0.0981591357556,-0.157619466442,3.13482335784,3.08819005963,3.25577418181,3.21122139618,2.04259227719,2.08929127136,1.92193690146,2.05332614377,1.96469076576,1.67256899333,1.72129025786,1.80011308295,2.07168672109,2.12764231661,2.17968001607,2.14879846802,2.31699081083,2.40981609113,2.4411174995,2.39683147858,2.28192802633,2.33033024725,2.2684208318,2.3107667288,2.07884514053,2.20202564293,2.15806663462,2.16453257468,2.04007451343,2.0814631549,2.53848192151,2.46407143773,0.992761618563,1.02256927533,1.10695786271,1.08508827323,0.912772132486,0.920129262528,0.751379138436,0.791053748491,0.860179988794,0.793384056672,0.894809804872,0.868042501391,0.679461600854,0.526492094007,0.643804927869,0.57159896683,0.97761668335,0.504288600561,0.555707107637,0.670784847974,0.636778007378,0.422578114856,0.790242110469,0.875112405117,0.824550007566,0.896677791288,0.521713327093,0.544452412785,0.482799435761,0.393039516676,0.663275677697,0.736361298669,0.749656449476,-0.253349895267,-0.182999010725,1.80699166677,1.95781121605,1.92011545504,1.84405596705,1.80996582975,1.85411239965,1.91895378433,1.84393040013,1.95280158847,1.87433039286,1.90685871651,1.96162886042,1.84897313179,1.80186485675,1.7304805528,1.81534657681,1.8491254502,1.58425379574,1.61642554043,1.62546217822,1.69830339669,1.73754727296,1.69716626443,1.03948929615,1.67865528878,1.60325349781,1.72123388383,1.68382946162,1.15814171286,1.36006486074,1.21118514903,1.24438431845,1.15673395907,1.282974685,1.22610153215,1.1997384592,1.2515228325,1.32973416107,2.87979923947,2.91374345668,2.62810818183,2.66398451712,2.97464657457,3.01050219214,3.00538713272,2.96728282181,2.88747038999,2.12492761062,2.20400657777,2.15479551114,2.06221659301,2.35573305069,2.36946849835,2.26292248857,2.26212955237,2.45447090108,2.46873389466,1.5063176579,1.60365797945,1.77758678916,1.91941667403,1.8352378134,2.1434834912,2.016121984,1.9866790594,1.30885073605,1.17698667871,2.26851743056,2.34953776925,2.3431614241,2.27387232999,2.82016620039,2.76089843534,2.66266429008,2.68517586554,2.789626489,2.59828841569,2.69755891125,3.02969683318,3.10688420932,3.25638812238,3.62719635714,3.71069411488,3.88029845112,4.2061250649,4.54515946384,4.62406199024,4.66084774904,4.61888163078,4.63349761279,4.57275954718,3.52093847864,3.6865224116,3.49713990379,3.6297153872,3.5492909098,3.30140342294,3.38472825881,3.41687065279,3.25435406792,3.28841249591,3.36798845444,3.17800349951,3.26414757162,3.32817674229,3.46222723514,3.51022959709,3.0525663795,2.9153144949,2.88121420626,2.98038869581,3.04231530035,3.13241343456,3.17543388203,3.03861494548,3.14782968372,3.11749747051,3.10222125411,3.02534918545,2.99232815858,2.67266357125,2.75338840462,2.76283973217,2.7990052583,2.55589205538,2.63638326732,2.68084045671,2.64330953352,2.52078633398,2.56345690796,0.620223359092,0.723703776693,0.469609790395,0.504419102532,0.589927354536,0.640972332293,0.519340501005,0.605603436184,0.356190223999,0.986181910333,1.08473462361,1.11555647705,1.01297634704,1.20982153726,1.20672437296,0.765170773527,0.800903519447,0.743897183072,0.6551409133,-0.0314157782597,-0.0937075568101,0.0885566324896,0.602363150864,0.554927163455,0.583325085244,0.659350245513,0.912013366764,0.93796625712,0.887296213285,0.811215938096,0.134919679651,0.0077024360458,-0.0261885246074,0.0168308020033,0.0493801679258,0.0723340400477,-0.631601653003,-0.201382935901,-0.580948065836,-0.241395486943,-0.25660185779,-0.327392179719,2.25735362463,2.79987054149,3.82147361654,4.03906315075,3.45087768846,3.98543074138,4.59289286866,4.43877784436,4.29503798718,-1.00555588268,-0.904168788327,-0.782216507234,-0.603849287842,-0.74110358239,-0.195513116656,-0.0141101009982,-0.00837165282582,-0.257972255676,1.09805190653,0.971041855404,1.02154145823,1.12302027056,1.22303395563,1.14608710892,1.03995980235,1.01343416307,1.11670771674,1.16275187836,1.06622226978,1.13897605686,1.60717105586,1.43587158915,1.21994600152,1.2471914576,1.53135261911,1.58763919109,1.56095285358,1.47781314863,1.37699344339,1.27933201097,1.28482723433,1.44718210977,1.34455499512,1.4206286458,1.00894765793,1.07628840085,1.03181355933,1.1157760588,1.17908295087,1.19518674213,1.04230328752,0.993521957642,0.963324757429,0.937907315994,1.13498975598,0.54591067561,0.749963410314,1.30826112288,1.49681364829,1.96994390862,0.604851649977,0.560894474962,0.257007664188,3.0012634408,2.91432371646,3.01730975099,3.7870486719,2.43150728455,2.42006106809,3.10658129657,2.99527731743,2.9960220127,2.9349967459,2.58030614588,2.66540386638,2.77438455959,2.81418370492,-0.0845956903618,0.173472524102,-1.04156770317,-0.242736835166,0.0843098609954,4.60498822957,-0.117287862698,-0.0608502703833,-0.154443140494,-0.0807166391655,3.57727666677,3.20916753781,3.2469483382,3.26071383136,3.22473803292,3.55655171034,3.13039881052,3.08844672101,3.13073863636,3.2139992388,2.208700216,2.2949765631,2.22710233538,2.17266642912,1.70616579803,1.65543841768,1.69364627925,1.78423987213,2.42463792119,2.51247916091,2.43281907253,2.38970094808,2.54819352081,2.50368795661,2.37906001493,2.28463289206,2.25476546457,2.30155462961,2.63505284054,2.62237145351,2.70333612359,2.80261480465,2.85874360217,2.88045161682,2.60036934693,2.68640388528,2.66971588686,2.59913066623,2.74859380247,2.76342147505,1.04642176771,1.1336599399,1.17073812989,1.19659674036,0.921845148151,0.935939267555,1.06640867875,0.990815424396,1.08743742226,1.02633499674,0.396027485815,0.342947801222,0.421287595349,0.448223404825,0.207898980645,0.152098360856,0.289143360039,0.315083102586,0.371020093532,0.35272938378,0.402887782872,0.26540624232,0.419779499316,0.388683963801,0.521101810141,0.602017294792,0.482859576419,0.532203790953,0.617925019947,0.649220844049,2.03153367494,1.95601972239,1.92541135762,1.98838153015,2.05633154923,1.76353292134,1.69907263707,2.00597834422,2.02608353356,2.1755179414,2.0817841805,2.11410811596,2.18731315128,1.81589799366,1.74192381303,1.84002489864,1.87382142906,1.68619445361,1.7256368692,1.3086086892,0.818400231081,0.717331121002,0.607765995794,1.40663257637,1.52445396929,1.42848375734,1.54326483196,1.59402543993,1.53968844114,1.55686093269,1.59446558502,1.5476416853,1.45928315883,1.33886790056,1.30823799707,1.42391075531,1.35975448004,1.44102826115,1.47417162692,2.34261149518,2.37320168981,2.90388473072,2.86894641565,2.78917501494,2.74450057362,2.53877022504,2.61934552603,2.60981022394,2.65518545036,3.00139916896,2.96139679061,2.07015451755,1.93074693394,1.99391628806,2.06172356373,1.98628536076,1.93231445653,2.15451984283,2.12956751753,2.23657528565,2.26105986536,2.19981857072,1.90251988373,2.37898574633,2.26020776822,1.61047119503,1.55671858302,1.83850295772,1.85852265718,1.99821454093,2.07710193416,1.66693245467,3.12690915993,2.04023740465,2.43135053819,2.32264186,2.33899385708,2.44273814776,2.54631673785,2.54102570911,2.42339757292,2.28175006612,2.56840700256,2.56149481688,3.01408299697,3.54304203378,3.03242184885,3.3284564594,1.48268220678,1.5176050368,1.7007087997,1.8043400141,1.97568026429,4.05860379295,4.04015973856,4.05853248057,4.24808065769,4.39195354191,4.33511856596,3.73970452963,3.77856477833,3.54110667763,3.67557468964,-1.39131603223,-1.32674525045,-0.769215239709,-0.734176462576,-0.748714481381,-0.788158746816,-0.873773809244,4.38926472936,4.47420297966,4.30031999055,4.43296873423,4.32177722029,3.91793568528,4.18030151543,4.05196056062,-1.40945493263,4.44483070225,4.5245524849,4.51013023631,4.55545722493,4.6495832344,4.7123111155,-1.52851677394,4.67918407398,-1.46175709768,-1.47422376965,4.28971397542,4.1270736667,4.20117676311,4.12257549546,4.36737708217,4.25189937566,4.28494122251,4.17106992565,4.14006244309,3.92472180705,4.18785887326,3.54925093703,3.57769869716,3.66327666384,3.2706886836,3.45812660457,3.40244653595,3.31317514356,3.01079826298,2.93472798507,2.96786163782,3.1168332251,0.701427052477,0.860048228722,0.833951363051,0.753502659758,0.665344709742,0.612850218225,0.518970201761,0.613327482208,0.500650971262,0.460294547212,0.116366271503,0.0940025515009,0.0572435142206,0.207361896681,0.678903563309,0.707240607281,0.783754115017,0.728853268994,0.833652774963,0.807343233692,0.279099498501,0.177231801374,0.0300015115286,-0.0388467515678,-0.353741499591,-0.283814651518,-0.2346098517,-0.297051514391,-0.373412828882,-0.546376330653,-0.688515343757,-0.618135777548,-0.552398484557,-0.491126606154,-0.381040974955,-0.506080856129,-0.453627850787,4.66415844906,4.59924634542,-1.48180565323,-1.43966474083,-0.710404189501,-0.710520659948,-0.759023651488,-0.129239316244,-0.112524864854,-0.340226674611,-0.217790678772,-0.342173595747,-0.284212887969,-0.536777298077,-0.386225381093,0.155277484891,0.0109192247077,0.00807859245134,0.21092232751,1.34821393664,1.27011847046,1.23940877169,1.64453838257,1.7390014624,1.55968489219,1.59504768059,1.31175737173,1.30588628555,1.41910292619,1.50398829694,1.54207907465,1.50867101975,1.47627800427,1.38487607808,1.40401488657,1.34588457774,1.07078595233,1.09396106778,1.20581109175,1.13172113014,1.22608633539,1.17526204282,1.63709881015,1.78172423607,1.76015457411,2.10451775327,2.38791243984,2.07729414906,2.15828379909,2.00608031607,2.40235039832,2.25376888093,0.382792743669,0.307581420101,0.426603207592,0.47193507209,0.244779094086,0.217228164935,0.433432355138,0.319220866842,0.372935688147,3.36484289463,3.92483338087,3.67487970477,3.05196775144,2.90412489283,2.46058503884,2.84652425023,2.4887393303,2.4625979378,2.51450174316,-1.38625031847,0.0107194006788,-0.130737199526,-0.806688514593,-0.445645894314,-0.467357083798,-0.657907981682,-0.681472324268,-0.231412639881,-0.492391404205,-0.276852965916,-0.0472991120585,-0.321590102133,-0.188986408781,-0.0135984360181,0.0713176539731,3.23208293178,3.40319504299,4.12474967138,3.38290170155,3.2523356278,3.28412093463,3.64731522541,3.33318138124,3.2568980121,3.21935513494,3.26040844716,3.62153948298,2.43326589388,2.34355498229,2.31190489689,2.27059775888,2.25081519749,2.14521778121,2.07490107474,2.19982434788,2.10877316266,2.02923165602,2.16979988024,2.2015709104,2.11352132181,1.79281845456,1.96028717264,1.91900242251,1.83211130394,1.91867830455,1.83754045665,0.260380907505,0.289587423032,0.234208669101,0.178415305384,0.121657695519,0.147952844353,0.342952842273,0.350833354425,0.244920147871,0.247487183856,0.191412715346,1.7175128834,1.79328116751,1.74133557532,1.5974234931,1.53489246109,1.60108826637,1.18941960612,1.32353516891,1.26283325367,1.37096059963,1.13217814647,1.16382371966,1.44457553856,1.38533808435,1.4054760827,1.31368869789,1.28273217274,1.25094094352,2.41227175956,2.49311105727,2.36494975019,2.39791820336,2.48034019988,2.52811482542,2.73587940436,2.78033517764,2.77179297252,2.851842562,2.89550893049,2.86016528002,2.95363631974,2.99721693004,2.84260617905,2.9445062088,2.63985819755,2.53978128097,2.52338541874,2.84200182731,2.87737882331,2.77913894849,2.73304556813,2.60656056779,2.50526102138,2.52959113114,2.73155214871,2.67936354059,3.16931125897,3.19558258162,3.44533291936,3.559328942,3.31700744333,3.47137642199,4.12184017701,4.03406309399,3.96779126482,3.98145380795,3.76744241105,3.83081730194,3.92890746322,3.91385536072,-1.49625173802,-1.42082542449,-1.38500889976,-1.42407577015,-1.53817268954,-1.50203278689,-1.2793401349,-1.30992011821,-0.930033312555,-0.850928655316,-0.928734792363,-0.85737377639,-1.02972903103,-1.0154194274,-0.913074152674,-0.802128789454,-0.890960651949,-0.873746956694,-0.923340919183,-1.00801594453,-0.922843567443,4.23642766675,4.13009435942,4.26031692687,4.15855222679,-1.32423583575,-1.11831509635,-1.11984676839,-1.22989558336,-1.20932158921,-1.30630393682,4.28397895052,4.21373682821,4.19989498822,4.1221097388,4.4141493999,4.50234632883,4.53906573058,4.49533556278,3.80053355817,3.95552059771,3.97460486205,4.43131843204,4.39715137896,3.5255292676,3.67802850146,3.82560329982,3.70105732871,3.77447269223,3.72669645649,3.81369625958,3.88276820355,3.32207241025,3.42103715287,3.43998714774,3.48252640719,3.15884477362,3.26322979482,3.1355457822,3.08738529945,3.2698257863,3.32486557533,-1.16167418381,-1.30977593318,-0.386041539342,-0.601963715916,-0.539811603273,-0.422350582831,0.00136420958789,0.0932019020044,0.00491440067211,0.103628399514,-0.156976214928,-0.0738387038903,-0.0622137279281,0.082519553828,0.0026524886286,0.0893889412247,0.0209919491918,-0.630725057649,-0.663600494532,-0.474548097597,-0.160486050466,-0.217095345282,-0.113501370683,-0.175738984619,-0.251075474174,-0.127525525782,-0.210179916579,-0.193092796937,-0.250710818923,-0.423365732974,-0.282642808106,-0.296862267974,-0.365890188272,-0.544313527336,-0.627693240489,-1.38601129889,-1.24675503456,-1.29599676248,-1.43098680048,4.67946708166,4.59096401191,-0.603012265096,-1.25257759949,4.52519020025,-0.614227709101,-0.455336737698,-0.555848155678,1.28488787639,1.34087930335,1.26547206847,1.76022495841,1.82193659173,1.93030384501,1.99608518062,1.81623242307,1.94638639639,0.816219801089,0.915064760611,0.67292372645,0.594717399417,0.701741634619,0.583134450044,0.681918336678,0.864565385888,0.950846647272,1.00245731526,2.645209272,2.59790029605,2.72676536878,2.76103144186,4.22428989557,4.26703411413,4.35545893333,4.40307949342,4.65688855754,-1.30863235506,-1.27147238993,-1.19571390347,-0.526356253982,-0.697559512073,-0.621153966569,-0.612945596812,0.0370307727462,-0.0630531441522,0.0848412168049,0.218123078213,0.0862168203875,0.0252680868472,0.270822017601,0.200776177531,-1.2732472948,-1.05788730482,-0.915353650153,-1.03676441792,-1.38428656815,-1.44197926129,4.61280022721,4.13223396821,4.0781528809,3.9752066431,3.94084494733,4.08950787455,4.04926992949,3.60064718268,3.4885504437,3.4059594386,3.34392058179,3.35048244349,3.51818781931,3.53609703021,3.7500123626,3.66995476855,3.61819750506,3.64726342998,3.86108479269,3.77970175526,3.72893475271,2.53051641728,2.45929482078,2.53488431907,2.44961084863,2.36624012855,2.37980169747,0.623208391762,0.844488148572,0.361275261584,1.07697932182,1.10150223813,0.470679751838,0.587640816061,2.86333673927,2.82771884966,2.80605603586,2.71123277257,2.40321380711,2.25982337479,2.11204493349,2.09045304211,2.23466528683,2.60035373654,2.25115146259,2.41837072137,2.45804561979,2.6552058576,-1.08411046388,-1.03444117472,-0.94696910884,-0.915010593043,-1.06967270762,-1.01443832731,-1.04985849194,-1.1408544473,-1.15767875313,-1.19352920764,-1.20538999608,-1.32286336096,-1.28886047353,-1.16715243884,-1.20482103465,-1.27742967518,-0.686717077598,-0.576164088662,-0.605790143679,-0.552022639576,-0.819092174037,-0.708755802967,-0.738825126328,-0.858016354599,-0.814559669081,-0.745300411633,4.03504416303,3.82503926698,3.92643037067,4.03912852871,-1.19820244971,-1.0934815133,-1.0841107081,-1.2715782214,-1.16687429902,-1.2501458455,4.66141183839,-1.51157215112,-1.39132053063,-1.44321559719,4.34434959562,4.41407849221,4.32952620311,4.29795618165,4.02374465096,3.85046936242,4.42348047355,4.37920992138,4.38769081883,4.31039163444,4.29918357077,4.26637244881,3.91071351062,3.88007646565,3.99167904282,4.04096435739,4.01665263556,4.02607799017,4.07872301959,4.16058980048,4.0795840523,4.05128770253,4.15733921685,4.0872448936,3.57898009644,3.60468901496,3.55789650978,3.48434104348,3.65441895149,3.60269245851,3.62789789627,3.39887011238,3.47646398375,3.35108323839,3.38079138327,3.45682678949,3.50445153759,3.8681169889,3.94681258048,3.8413814046,3.93956366996,-0.0879490996438,-0.282320262331,-0.198155717705,-0.0946079683226,-0.26120963176,-0.169488555246,-0.226163958009,-0.208544574142,-0.269506684072,-0.54779513936,-0.520956874787,-0.325628123494,-0.385698562528,-0.412139167683,-0.340262164145,-0.462115348819,-0.473720916998,-0.561389863144,-0.675301929201,-0.914016254868,-0.782709299657,-0.94599780058,-1.11995800576,-1.12063018399,-0.988822020872,-0.843708339763,-0.803273617566,-1.33683611467,-1.1306295502,-1.14412951854,4.71012956655,4.58133784108,-1.51410953073,1.47515486365,1.4015322719,1.51437551718,1.47339106834,1.39481600669,1.36239418837,4.52344322124,4.61418153106,4.53218781408,4.48687268905,-1.07964769576,-1.15565530153,-0.842167518446,-0.902138510313,-0.782288215684,-0.80922580651,-0.441311395421,-0.517857319179,-0.589973817536,-0.435116595849,-0.511762077885,-0.590174047338,-0.760518406386,-0.675453514675,-0.66069917977,-0.801491202422,-0.977825388224,-0.936136864074,-0.232814610203,-0.130793101298,-0.313645402206,-0.29230817159,-0.172514219756,-0.0934245737457,4.50769969092,4.22823575813,4.70615217012,-1.3485473333,-1.51842833956,4.29511572282,4.46711050511,4.20516626358,4.29034419543,4.542038494,4.45859353851,4.23557823923,4.29626918532,4.41249565344,4.04396054889,3.99424780722,3.90015984417,3.92609940256,3.45252404555,3.37688709354,3.32144385574,3.36290280899,3.43823139918,3.40095338314,3.3485567908,3.38521317779,3.48492863259,3.52009725589,3.47262624107,3.89103514259,3.9730120837,0.794539264755,0.795091283509,0.971850710916,0.955409402092,0.276118466264,0.415133583205,0.61737226897,0.637937072152,0.801322862366,0.690474047073,0.674750354606,0.812127776278,0.930941911559,0.935949690643,2.74188081705,2.62918627967,2.71105492737,2.57209700654,2.59500061408,2.68338631771,-1.04748688837,-1.08714688919,-0.931604418007,-0.965569053003,-1.04541650772,-0.971514728731,-0.715358878473,-0.799555564053,-0.829394784842,-0.772838951902,-0.686204393662,-0.658855508599,4.50551218046,4.61118357683,4.65857056864,4.60683812125,-1.53631532805,-1.36039179692,-1.38370454272,-1.47548980195,-1.41978645874,-1.50467394708,4.64212021214,-1.55694895969,4.69595510132,-1.5244729034,4.48176857951,4.42411026655,4.60127305212,4.58020438047,4.54121252411,4.46196826162,4.09544774429,4.13593206569,3.959546825,4.00814301611,4.08733092844,3.9977852655,4.10509472022,4.15391348314,4.22496099684,4.14122960945,4.23616306801,4.27074258003,3.9300368122,3.95206338518,3.90183650963,4.01020388669,4.06012152638,4.03038474708,4.27394625498,4.19918120756,4.31684709196,4.28024714285,4.19569383231,4.15773334775,3.73168206376,3.78761417544,3.88314390279,3.8662399268,3.70157869392,3.7257836544,3.82647237303,3.75337512497,3.8488553325,3.80015001299,3.77730266548,3.58426567342,3.65706762386,3.75856470433,3.61295229738,3.70538864876,-0.31014655606,-0.332040680036,-0.423663967255,-0.442689035298,-0.291010238604,-0.309529107638,-0.386669670253,-0.345888492789,-0.420652405261,-1.43327145305,-1.38689363645,-1.4340135814,-1.42560852317,-1.50639915568,-1.50482051246,-1.54573478,-1.51127769267,-1.54874691813,4.6070471707,4.65213015185,-0.962546761897,-1.038511529,-0.918390930153,-0.951083899114,-0.931596620975,-0.988690102968,-0.498843030607,-0.390809014867,-0.373978340776,-0.877835322993,-0.846215353723,-0.768618915017,-0.629798391915,-0.507311791554,-0.726456103871,-0.620936528655,-1.47821723831,4.28878937025,4.23329600383,4.27574743122,-1.29195049522,3.92973415882,3.99167118539,3.8354638265,3.61440105772,3.77672730704,3.7911607828,3.6930873566,3.8161463053,3.86803078964,3.69386755356,3.72256503457,3.75430811245,3.83519816671,0.508285404225,0.438062210408,0.539343135815,0.300702095458,0.240970460996,0.343580439666,4.45854145108,4.52144481991,4.36848581348,4.37333848124,4.46918588348,-0.372978706904,-0.491927636052,-0.350356368888,-0.40872638671,-1.42861057703,-1.38760750489,-1.01437881724,-1.04380415053,-1.38601977872,-1.42561993648,-1.38563064974,-1.30491820886,-1.03073503475,-1.07373385228,-1.06427430447,-1.14397416869,-0.90689110355,-0.738102055947,-0.904980247587,-0.799591163073,-0.877710487486,-0.508974524659,-0.414600835301,-0.422753424676,-0.510369076764,-1.51618592633,4.63648422194,-1.38882687012,-1.45334601908,-1.41655138544,-1.24571611282,-1.31383570101,-1.07967536603,-1.01787635399,4.38333901001,-1.01909904061,-1.10836495989,-0.983363416492,-0.955395054328,3.54658981116,3.61510177553,3.45445727149,3.70128004153,3.60766622464,3.17643172773,3.12862922006,3.28259465321,3.35493971649,3.16475741021,3.21592024338,-0.458061420352,-0.577335882582,-0.486100665781,-0.518216646046,-0.637922753966,-0.607487458687,-1.26162477074,-1.17886666728,-1.30087427663,-1.2522394978,-1.16260241439,-1.12903056208,-1.30722017567,-1.15145825253,-1.26642692289,-1.18785420617,-1.2694526665,-1.19197919258,-0.800288618701,-0.710979848749,-0.599974931628,-0.606804407871,-1.2979628457,-1.23725661337,-1.0550521476,-1.10256174705,4.49730765206,4.41410084509,4.47700065386,4.35034756914,4.39273646134,4.57627762965,-1.5247125195,4.63920604622,-1.56117654675,4.60367350601,-1.53601935947,4.7036150572,4.38894226374,4.44057098287,4.45022640754,4.54279576526,-1.28979681597,-1.24078092265,-1.14610289233,-1.23749157126,-1.08584106839,-1.12672931034,3.60553445722,3.71332282724,3.85597616482,3.83024370633,3.42942490243,3.51351390465,3.49934944965,3.38275263637,3.32539998279,3.29400114201,-0.782279487124,-0.751525048364,-0.674691289398,-0.685653027502,-0.86004340725,-0.840719836607,4.69331622486,4.58919895961,4.54842524117,4.63810158885,-1.55287752811,3.60507213828,3.45277306884,3.47004520802,3.60784668417,3.75957050514,3.73974751123\n};\n\n\nconst int NST1796 = 1796;\nconst double AREA1796[NST1796] = {\n    0.00692599989559,0.00710417279517,0.00690379583366,0.00807252480222,0.00660941358854,0.00698787022317,0.00688049591296,0.00707651260945,0.00679992205263,0.00688881157811,0.00801179453387,0.00660485772371,0.00713085788951,0.00696280001851,0.00691826061145,0.00705474302191,0.00699895720716,0.00655356904615,0.00715469159308,0.00670907193504,0.0066559363709,0.00658217547323,0.00714103511184,0.00708596555483,0.00700878674025,0.00695640006306,0.00819080302376,0.00655929944844,0.00715044689307,0.00687103160787,0.00756553583713,0.00669509932191,0.00696174341006,0.00709333660424,0.00710422980888,0.00688077111082,0.00671692926548,0.00701900357319,0.0069992180459,0.00701791689171,0.00702933437887,0.00691773408958,0.00662555908723,0.00695907423578,0.00693141959828,0.00695610589587,0.00698339364758,0.00717573853945,0.00670848442547,0.00710308646798,0.00778197057468,0.00713264466259,0.00702288513012,0.0069390287544,0.0069253486694,0.00704895322726,0.00714066641203,0.00709038816631,0.00737540316544,0.00695276124416,0.00697929107072,0.00694497967348,0.00730324079724,0.00697484096899,0.00702003132512,0.00712851766418,0.00706325183531,0.00698426229123,0.00697547321946,0.00714925605896,0.00695612275916,0.00706435390283,0.00714598043064,0.00697658990131,0.00691448165359,0.00686466313643,0.00688253804416,0.00677246718143,0.00727564266802,0.00723601911965,0.00689038375888,0.00706475655485,0.00700157249276,0.00700023052512,0.00700007449144,0.00700140082026,0.00689050284167,0.00702883225788,0.00670286837742,0.00692667362319,0.00701051609526,0.00654779955093,0.0067614666878,0.00715886704226,0.00651931079504,0.00680164535594,0.00706859962123,0.00722998024816,0.00692658599397,0.00737913326978,0.00711339202994,0.00694945655018,0.00705060348005,0.00709807018821,0.0069763381191,0.00697886891048,0.0070776443463,0.00693675196101,0.00699838198241,0.00702297011107,0.00693256081662,0.00697870245478,0.00718923751482,0.00705226826535,0.00657404509947,0.00705687964187,0.00688738938208,0.00684135510444,0.0071542462222,0.00664806949347,0.00707705193449,0.00686093944027,0.00704033613583,0.00703885700877,0.00709422945549,0.00692279546971,0.00699795574023,0.0070648305986,0.00700791402203,0.00702788751559,0.00693948460818,0.00703692627638,0.00701445691794,0.00700156547815,0.00699982519151,0.00701080225748,0.00705112420964,0.00693141174752,0.0071056039227,0.00699424492776,0.00701741168611,0.0070250256679,0.00701864158232,0.00700416099865,0.00739945788679,0.0069054375062,0.0067512257637,0.00695035580331,0.00734033103101,0.00647582239145,0.00688520212333,0.00680024897461,0.00741971621407,0.00668478224184,0.00660855345393,0.00822373418215,0.00682054678316,0.00702299779109,0.00699055577608,0.00691852567722,0.00721445310731,0.00683651531091,0.00700377121753,0.00655967427848,0.00661930723441,0.00690066979349,0.0067639462358,0.00710272885838,0.0071804423813,0.00687683988381,0.00656308630704,0.00726754807779,0.00684212723994,0.00673776086459,0.00828105304397,0.00667753794435,0.00747435403421,0.00660547327732,0.00696741744056,0.00705539826914,0.00703184822313,0.00694463509627,0.00702778704411,0.00678391055768,0.00724785202683,0.00711052719164,0.00701933556072,0.00692831055049,0.00702107123575,0.00705279369097,0.00708400627395,0.00707025232897,0.00692743074504,0.00695912069561,0.00706463442555,0.00692537737733,0.00748812911043,0.00714630961803,0.00693255057882,0.0068858176212,0.00712489296072,0.00697411977804,0.00674879657805,0.00727886678168,0.00693077829747,0.00695879514842,0.00691820687751,0.00822890543809,0.00661497274063,0.00695628319323,0.00706637174179,0.00733749225609,0.00696519622145,0.00700140943124,0.00706720715137,0.00698717576541,0.00702433864431,0.00698937508997,0.00696513323465,0.00687842107548,0.00709726460203,0.00701234457721,0.00699545469173,0.00706721319651,0.00700380682123,0.0070462536617,0.00696256155107,0.00690532021553,0.00660701020509,0.00719773277242,0.00662088092449,0.00798923805078,0.00702787174439,0.00663228590463,0.00694101327248,0.00709305936932,0.00692136553132,0.00695160124161,0.00707475228646,0.00693856114964,0.00715338299125,0.00677833682089,0.0069793521361,0.00705789224329,0.00702355759063,0.00690111477193,0.00675020234822,0.00697597355664,0.00663149127897,0.00657154590566,0.00658673735391,0.00679627561521,0.00708630112144,0.00687800713005,0.00692201724068,0.00695732062125,0.0070302351423,0.0069701339464,0.00693616975914,0.00710440024815,0.00691354544418,0.00706673811269,0.00687516588577,0.00705416667583,0.00694385918534,0.00710247076129,0.00798645787609,0.00660283660853,0.00677334116727,0.00675262391302,0.00738039698269,0.00695589844069,0.00706331106469,0.006968311192,0.00699013118587,0.00711063304458,0.00687783598806,0.00693830371136,0.00661788684992,0.00660227829141,0.00703283433547,0.00698828656485,0.00687667411885,0.00704804769703,0.00690538521611,0.00686408978787,0.00716742797545,0.00729158193241,0.00715760522465,0.00704559065801,0.00709995971794,0.00683959915414,0.00772332980663,0.00694030690579,0.00755930724977,0.00721049532887,0.0068301712742,0.00710750037157,0.00683131846598,0.00697128594836,0.00669450381235,0.00728982351842,0.00708676490041,0.00728967626881,0.00701789715213,0.0069704170132,0.00719563428107,0.00679139892295,0.00720209553961,0.00689235727059,0.00698092936343,0.00671241225927,0.00752833178122,0.00665077108099,0.0066344347663,0.0067676219114,0.00729068650101,0.00701377536103,0.0070296744865,0.00696429175261,0.00707640222472,0.00712283734335,0.00696566170546,0.00706455465486,0.00704430908997,0.00697702414908,0.00703745191912,0.00703058469787,0.00670473535327,0.00688715173852,0.006987354352,0.00707857956517,0.00701563648029,0.00693731242727,0.00691319294547,0.00702276647309,0.00714751803378,0.00703713387578,0.00691472981649,0.00690150836383,0.00708657201329,0.00659093502852,0.00702288557084,0.00720329041109,0.00659317273847,0.00723780702929,0.0068359537714,0.00702335827337,0.00697480288485,0.00710394079555,0.00694096741678,0.00706131411942,0.0069909038207,0.00696150932588,0.00769163681173,0.0067746789751,0.00687207401923,0.00692591184694,0.00710754839523,0.00683141632985,0.00659057501161,0.00675658790754,0.00705720530137,0.00721600990045,0.00679057458975,0.00731136667946,0.0071613845972,0.0070108602065,0.00690621221679,0.00700780215877,0.00706384004203,0.00690348351239,0.0070819126054,0.00693648711297,0.00692459699288,0.00711348960346,0.00702749803957,0.00702197303138,0.00699881710798,0.00695059103347,0.00716233457293,0.00692437116275,0.00700017071404,0.00748999711562,0.00700089854251,0.00704652462957,0.00704508957062,0.00705165280081,0.00696335637311,0.00673036856874,0.00735080483148,0.00695497250912,0.00698202044836,0.00705660466208,0.00696205117289,0.00702512486158,0.00699585989102,0.006988988991,0.00703119420535,0.00702522615269,0.00699266179589,0.00696591540835,0.00708190918251,0.00667579639722,0.00705406685587,0.00701690341268,0.00707399174591,0.00693726371627,0.00690801427441,0.00722278544143,0.00715591410813,0.00692313967284,0.00710992447631,0.00696313323108,0.00702601009615,0.00702430950709,0.0070132295464,0.00695965754257,0.0069498894207,0.00699348279606,0.00691486159637,0.00693645574034,0.00704804403335,0.00713228817254,0.00687437112053,0.00700278767488,0.00703453606102,0.0069841449652,0.00701878436835,0.00700354620178,0.00701768501649,0.00708655857546,0.00693511419306,0.00696339505987,0.00709411592195,0.00707401604305,0.00692510958839,0.00702317543661,0.0070142756625,0.00701963632233,0.00706651752546,0.00687170930122,0.00709207036185,0.00708007032976,0.00693818945155,0.00707736201548,0.00697665745485,0.00692387705751,0.00695003334475,0.00712676603487,0.00662163806346,0.00664943299632,0.00658200661046,0.00692687608771,0.00717628711424,0.00675068500963,0.00696944702628,0.0070872193315,0.0069662638797,0.00709624960848,0.00710011288855,0.00707849801331,0.00735295587039,0.00677165102952,0.00711967229385,0.00691513666541,0.00805032380554,0.00677552958195,0.00651541519904,0.00660489713448,0.00697424348068,0.00708971309806,0.0070131530943,0.00657651296817,0.00686330956198,0.00689922459492,0.00699109885928,0.00703571217624,0.00698269592402,0.0070854500615,0.0069860783346,0.00696411030536,0.00706255505636,0.00692576495957,0.00705656811508,0.00703545990089,0.00694809843592,0.00709669985147,0.00693728697182,0.00709758237628,0.00698084349835,0.00709321204493,0.00696819410568,0.0069544197899,0.00706402162806,0.00695814637232,0.00703050881306,0.00705369545404,0.00704605124271,0.00698065595904,0.00704555812542,0.0069725688393,0.00693528820111,0.00700861435453,0.0069288311628,0.00704776353689,0.0069445844184,0.00705706525424,0.00693633372393,0.00709033855424,0.00702728182653,0.00698186584672,0.00659727861711,0.00798752526289,0.00697596717181,0.00706728321281,0.0069425403369,0.00709327306666,0.00759955731817,0.00670416294221,0.00692324227539,0.00703517523794,0.0069859626076,0.00713117064541,0.00693829641113,0.00699702546788,0.00700829198367,0.0070017166832,0.00705895358062,0.00672468322008,0.00704246455116,0.00663008845426,0.00713298619462,0.00690433595743,0.00698881487602,0.00693976136331,0.00705499179264,0.00700113704343,0.00702356051032,0.00704648443612,0.00698478324881,0.00711581883446,0.00697833415359,0.00701679554084,0.00696918030611,0.00698405505769,0.00703805762646,0.00717767995525,0.00702469330795,0.00695827253952,0.0070586204913,0.00697726151572,0.00704003946482,0.00697657580482,0.00691652580843,0.00711398808632,0.00692727886183,0.00705013198352,0.00689362154115,0.00706658439844,0.00673869455572,0.00717155491062,0.00705821320788,0.00695630657192,0.00710443828319,0.00681855748409,0.00684018418657,0.00726536966228,0.0070325151301,0.00701495274199,0.00699487412469,0.0070072836632,0.00704474941296,0.00698283486758,0.00693440679647,0.00694585241881,0.00704450561601,0.00689159028617,0.00663169619377,0.00717445262702,0.00697654374111,0.00689842907392,0.0070551615415,0.00697851480328,0.00695996642152,0.00713858655555,0.00696486224596,0.00690652456157,0.00659811782764,0.00678238296738,0.00682337048679,0.00760231269506,0.00657110585823,0.00674396060421,0.00667743237974,0.00717504633995,0.00790086949556,0.00704874128747,0.00684551118603,0.00693151329207,0.00679465881107,0.0070436065664,0.0070613479441,0.00695053132116,0.00694857231162,0.00704700384562,0.00706336907871,0.006827859681,0.00714735562766,0.00688312518082,0.00673134130664,0.0070008450684,0.00655491026394,0.00705394464078,0.00666718776178,0.00723040512211,0.0081910994342,0.00702243583365,0.00701329917928,0.00695471840378,0.00705150832699,0.00705249886065,0.00697073164543,0.00693355308034,0.00704868014898,0.00696879201428,0.0071775090124,0.00686951034549,0.00713525189208,0.00719321726345,0.00704470940187,0.00696813626889,0.00700210762851,0.00692494735104,0.0071092391523,0.00689223398827,0.00698513622764,0.00704837607696,0.00699116424651,0.00702559030352,0.00708214951568,0.00694828830277,0.00692736654442,0.00699308011044,0.0070111733974,0.00699758242621,0.00706243970749,0.00709762497334,0.00692425699376,0.00701673771345,0.0066803426918,0.00713302375482,0.00681642757311,0.00792775076545,0.00694707682926,0.00698601896602,0.00694809306453,0.00707297964239,0.00702293356505,0.00699299616245,0.00693542974839,0.0070845240509,0.00700451806257,0.00694637092051,0.00686853336307,0.00720233351237,0.00690827023531,0.00713290113407,0.00690977954791,0.00684423480606,0.00715652684653,0.0068667663845,0.00654820393929,0.00672574033593,0.00663420259662,0.00685372829458,0.0070626455687,0.0073837814231,0.00672039721636,0.00685024735719,0.00719602207098,0.00754093313127,0.00698936657229,0.00668078167336,0.0066116049435,0.00740954721877,0.00718120587936,0.00740214199047,0.00673684243239,0.00825923211542,0.00656601645705,0.00706346040939,0.00696436730832,0.00690997712815,0.00704181036396,0.00695384206899,0.00708673601289,0.00695193171509,0.00707757568868,0.00693469335672,0.00725039217654,0.00694067263606,0.00689797571413,0.00713198992383,0.00700671175768,0.00683381495859,0.00704941672243,0.0069446911411,0.00671149532304,0.00671223142753,0.00673856726588,0.00666361046393,0.00682864371094,0.00729503887058,0.00743375806187,0.00687671624427,0.00709528661385,0.00726543718393,0.00671971681683,0.00737488185446,0.00683954598957,0.00698459461513,0.00707458565814,0.00694028231054,0.00703432311659,0.0069204999994,0.00682355280218,0.00688157741015,0.00739962307121,0.00664232943066,0.00732843412645,0.00671893046115,0.00685881901446,0.00693428558171,0.007066429921,0.00695757090781,0.00701421815842,0.00704244436942,0.00694263001743,0.0069730667623,0.00706672985947,0.00697736614079,0.00701719855324,0.00696943121036,0.00720016152196,0.00671612674916,0.00679664903951,0.00672411542975,0.00721364829685,0.00697331740255,0.0071580984597,0.00705490446931,0.00673522056166,0.00692286428916,0.00823222804871,0.00670726040952,0.00703105935255,0.00724025587422,0.00662199370651,0.00700155273068,0.00693599831384,0.00714043407459,0.00691941538839,0.00699109401658,0.00691952077584,0.00700760311593,0.00674017776088,0.00658790275606,0.00678292818614,0.00703183421726,0.00783060719923,0.00701231953034,0.00672061275586,0.00715807218984,0.00773513395844,0.00703193928824,0.00771830931684,0.00665168691099,0.00678069559928,0.00668566405984,0.00767697441649,0.00674417453732,0.00659463142447,0.00704213681748,0.00698310837269,0.00701462439121,0.00693548920565,0.00696416162184,0.0070754979926,0.00710565144252,0.00692390103978,0.00690379317887,0.00711715368436,0.00702239025907,0.00698591503229,0.00707755700571,0.00711596808133,0.00711971242259,0.00691486402286,0.00669726161546,0.00660410749282,0.00784458513399,0.00672033053756,0.00802507549139,0.00684360260913,0.00678042798848,0.00743643517919,0.00667753465514,0.00712757579599,0.00828689957365,0.00672381096081,0.00700961211531,0.00691407115748,0.00694046652177,0.00707671406271,0.00692427167701,0.00695479296041,0.00653112980305,0.00713224476109,0.00707355592145,0.00696864374323,0.00701820076052,0.00707343299798,0.00700435783238,0.00700035638225,0.00711186664936,0.00703023571092,0.00700393276393,0.00700263541043,0.00705591295516,0.0069363331546,0.00709107042018,0.00699982728378,0.00703646428796,0.00696850495954,0.00708546069591,0.00707548533321,0.00732026377152,0.00688261845831,0.00722797798321,0.00675434099847,0.00686586268454,0.00715713663336,0.00681053077337,0.00708099578685,0.00731135987288,0.00708962165709,0.0069078105268,0.0069142180866,0.00706759639596,0.00695075652359,0.00691583196667,0.00678080718755,0.00683067584636,0.00675624121394,0.00716616963533,0.00674586901948,0.00711682624432,0.0070563411658,0.00693205710304,0.00696516619364,0.00703767889933,0.00679003979189,0.00686623155429,0.0067242720097,0.00720219607439,0.00806491653039,0.00668588795145,0.00665766492693,0.00741971594367,0.00680636081594,0.00678869557314,0.00733069239398,0.0073704765793,0.00688116937152,0.00686561532116,0.00684628606586,0.00715456780568,0.00709882333608,0.00695242619852,0.00702848351306,0.00707620824385,0.00689695223834,0.00711499713025,0.00695772391909,0.00702602060582,0.0070764893654,0.00696758603615,0.00696184121912,0.00706951904797,0.00693350844199,0.00707649301208,0.00691259547265,0.00709559941659,0.00702961874049,0.00697735092874,0.00723679568063,0.00688133736496,0.00722469931215,0.00726037131385,0.00709512325988,0.00798829723588,0.00692793232935,0.00700162110194,0.00687365445791,0.00712861856912,0.0070314512142,0.00696767179506,0.00708152794344,0.0069320080703,0.00708860212007,0.00712687172534,0.00699927977363,0.00704009864065,0.00685741398069,0.00704012654997,0.00708217446417,0.00691456974121,0.00690265640108,0.0069179437856,0.00704798027965,0.00715891531778,0.00687660339019,0.00703291370222,0.00709204015989,0.00713551227171,0.00690269995775,0.00699945484837,0.00702236381515,0.0070000354444,0.00697687903858,0.0070435869755,0.00688522646018,0.00674744179447,0.00676410582306,0.00703077759555,0.00697600036793,0.00742883462724,0.00680423611861,0.0072129638974,0.00683818276413,0.007277368362,0.0070262737934,0.00698296542585,0.00713064510109,0.00695583950895,0.00658122970894,0.00690366388816,0.0071514792486,0.00712108677602,0.00682473509221,0.00667193765687,0.00714706006611,0.00757167946538,0.00714893634884,0.00698608176871,0.0070950932027,0.00705362407944,0.00695533439187,0.00695016529824,0.00685420701176,0.00675106141764,0.00729843765256,0.00691878319258,0.00694956394366,0.00687907757902,0.0070678698215,0.00708795289555,0.00691685532469,0.00707026212367,0.00693124513814,0.00704648264306,0.0070127173775,0.00702711488678,0.00700146121624,0.00695478284922,0.00705613559951,0.00697371494589,0.00697919337332,0.00703173761272,0.00702600772746,0.00691886152957,0.00696781377582,0.00702035219919,0.00691191117918,0.00708753839457,0.00695556870296,0.00760712811945,0.00692250535787,0.0071288898314,0.00671791940005,0.00660737639473,0.00796876715362,0.00676810605304,0.00696014490486,0.0069834324995,0.00681087195954,0.00708941043477,0.0069202504086,0.00705599466958,0.00712349296587,0.00696885505635,0.00700325011055,0.00709621304522,0.00697498411866,0.00693022399553,0.00700732972091,0.00702209764383,0.00691506332805,0.00709705888093,0.0068553791454,0.00669326444274,0.00715600879065,0.00711640330219,0.00687251339463,0.00712183838134,0.00710898688726,0.00693788288183,0.00701376317805,0.00691945622453,0.00704517784263,0.00693141137143,0.00709202901252,0.00695634043423,0.00706441123049,0.00694148291639,0.00722127711854,0.0070589672735,0.00676706650175,0.00701714077466,0.00699779641075,0.00695714727233,0.00706204307725,0.00676477177912,0.00726780527202,0.00661751845187,0.00706485351227,0.00712020658053,0.00703758726918,0.00694914128949,0.00700688472124,0.00701038932285,0.00708624690954,0.00695531629861,0.00694822953607,0.00703172591291,0.00700006368888,0.00709555433015,0.00701708727425,0.00698753995218,0.00701871440755,0.00702038940333,0.00700105258767,0.00703425762726,0.00692504253025,0.00701991047439,0.00711072847778,0.00700195342611,0.00690337812577,0.00714625265134,0.00777904255592,0.00700549777739,0.0078217932183,0.00680605154397,0.00695742338406,0.00710773144897,0.00666890013837,0.00714336353051,0.00697786904395,0.00702588967659,0.00670877383597,0.00697917712791,0.00668747153236,0.00677184178718,0.00683742365457,0.00696849876369,0.00706757938552,0.006923855895,0.00692393156672,0.0069451075532,0.00706788794213,0.00707403269257,0.00693515828569,0.0069791799731,0.00699146744658,0.00707605917654,0.00688249454224,0.00675992187496,0.00664214901485,0.00660392553603,0.00661246169941,0.00695853003924,0.00704160192517,0.00698439361977,0.00704619839098,0.00690177048816,0.00686528052203,0.00731301386896,0.00678094089378,0.00680628654401,0.00732032508143,0.00659030459889,0.00694682790933,0.00689758911083,0.00719934763455,0.0071158794948,0.00713407785278,0.00690442584121,0.00693673380757,0.0071486644184,0.00695145960108,0.00706411726801,0.00703944823034,0.00698833122206,0.00703039531949,0.00699131313353,0.00704414804418,0.00697766329128,0.00705034471766,0.00699321857482,0.00704023797445,0.00698537842312,0.00698491634199,0.00705459580093,0.00824879912585,0.00665214266516,0.00706857087715,0.00660417149871,0.00717304056646,0.00665181938994,0.00699515063355,0.00700471070525,0.00706965527651,0.00694693260134,0.00700971209938,0.00698051800281,0.00725630853366,0.00758356360493,0.00694464934645,0.00680968675523,0.0067444870777,0.00717206507577,0.00675177800254,0.00752626505665,0.0070719425006,0.00713900439373,0.00675887906795,0.00703455349558,0.00662249363543,0.00682047324881,0.00663906679091,0.00674069226154,0.00645859474561,0.00697325790624,0.00693672141891,0.00702409734347,0.00692753024042,0.00709246932932,0.00705461767775,0.00697357961393,0.00692236661886,0.00710878472444,0.00696066072924,0.00703442097089,0.00706983857272,0.00695400620922,0.00694540976718,0.0069225668293,0.00703599586858,0.00694424146936,0.00712316054864,0.00699125581705,0.0070204186872,0.00696521241187,0.00695251622263,0.00709129854654,0.00702474362731,0.00721575930944,0.00696310755231,0.00687712631776,0.00701939301518,0.00706792904797,0.00695323488054,0.00690846065269,0.0071102262541,0.00684853859009,0.0065699277637,0.00673384393561,0.00666610386814,0.00691501729606,0.00674931100769,0.00676516155442,0.00719390117576,0.00736565491063,0.00659036525981,0.00709703592387,0.00692008811026,0.00702189004768,0.00700658403117,0.00698927071172,0.00691334716503,0.00713805428187,0.00686867025479,0.00659004427511,0.00725387253227,0.00667090845729,0.00806508637976,0.00682696980723,0.00676500227747,0.00658311439696,0.0068684386673,0.00644764941457,0.00666929695215,0.00704322748874,0.00680904270948,0.00749244826637,0.00656312049623,0.0070699807821,0.00689969590842,0.00702404028817,0.00700704557025,0.00727634732527,0.00677919644431,0.00707624819161,0.00713639938464,0.00687949005143,0.00691592225726,0.00691321942719,0.00667776175845,0.00745372340055,0.00716088623672,0.00698558606599,0.00717871706191,0.0068125053018,0.00655260933412,0.00797792274381,0.00671888115901,0.00717711786754,0.00707373222504,0.00663803353293,0.00691720341273,0.00658273308459,0.00709561668236,0.00660458440796,0.00829815523083,0.00653505880435,0.00658114339355,0.00802089060697,0.00677571493708,0.00693803775819,0.00707634650948,0.00705346624525,0.0069589742408,0.0068144893074,0.0071223612017,0.00721914065647,0.0066992822863,0.00666320404861,0.00730782243262,0.00828980107678,0.00660129914939,0.00659347360565,0.00717182320947,0.00708237855596,0.00688204269942,0.00703494685113,0.00695515291451,0.00693510256786,0.00707199014843,0.00736066563946,0.00678740475478,0.0069121729289,0.00704688139945,0.00695796519581,0.00706504444321,0.00691430497017,0.00692810352597,0.0068148038111,0.00717034476581,0.00687280577978,0.00669682565738,0.00691775229858,0.00703491437681,0.00683701186241,0.00671496079573,0.00675065131444,0.0073409325411,0.00676693820999,0.00714297491428,0.00691735158533,0.00704044097524,0.00696105969997,0.00694181242077,0.00674458207311,0.0070763441664,0.00695836667903,0.00695330319374,0.00704576052532,0.00700275462108,0.00704120043323,0.00693415398266,0.00692681680435,0.00705380931419,0.0069635173542,0.00703074812378,0.00693067653402,0.00704702969223,0.00696612696363,0.0069145480293,0.0070864038371,0.00699490936998,0.00704905426288,0.0070313943323,0.0069729791354,0.00695390580269,0.00699524570794,0.00703858761505,0.00699481096471,0.00677602138521,0.0066207328033,0.00672833168557,0.00686074497142,0.00661082469561,0.00698967025136,0.00705449782874,0.00698427542975,0.00702392280601,0.00683013259135,0.00669170402321,0.00683772460393,0.00709250236348,0.00726391944077,0.00708860840408,0.00698185623603,0.00701507198124,0.00695895866185,0.0070969877944,0.00694041751728,0.00695300622561,0.00708309770471,0.00701139795681,0.00701667848424,0.00714819975526,0.00658005819838,0.00690268575106,0.00698124596769,0.00698698402838,0.00703540049684,0.00690696602983,0.00690179433143,0.00687378656583,0.00719850837399,0.00708635915189,0.00693934759132,0.0070093959428,0.00720731664492,0.00679119429938,0.00673699746512,0.00723879879097,0.00662039530476,0.00658661793646,0.00689618521246,0.00677687081112,0.00659243930946,0.00667712029257,0.00671806895787,0.00691538598049,0.00665543473781,0.00669415400131,0.00667163775785,0.00711893243298,0.00709668102798,0.0073885028229,0.00682867265505,0.00693352743396,0.00692152549764,0.00710519840775,0.00707015065227,0.00691149036682,0.00713508810108,0.00671083592525,0.00694289435182,0.00707439436788,0.00698029255783,0.00703989625469,0.00695806032516,0.00708014802419,0.00656307466572,0.00724117565553,0.00685432967812,0.00704451797697,0.00695300837394,0.00707491466348,0.00708888540202,0.00700554745422,0.00695378292636,0.00704845054375,0.00707415771971,0.0069474913979,0.00700225707517,0.00700508302964,0.00706288217221,0.007080808322,0.00693813463236,0.00706759939875,0.00701016129212,0.00691787128127,0.00667767219259,0.00719844820715,0.00705493576558,0.00696686745952,0.00696392502303,0.00704757093023,0.00706217573884,0.00695500961636,0.00703924887379,0.00697922454807,0.00705498139366,0.00705471322417,0.00695053431118,0.00701204533835,0.00688273450999,0.00690038810986,0.00695416315389,0.00707928851465,0.00708662322284,0.00691670764493,0.0071181381366,0.00671263331949,0.00697531124144,0.00805892850628,0.00691116535222,0.00685956518293,0.00727982975889,0.00662653857781,0.00803585001051,0.00660578488276,0.006980638691,0.00699787383399,0.00703974829417,0.00699736992461,0.00703430179564,0.00698635007395,0.00695810329586,0.0070052233588,0.00699228510336,0.00702971609581,0.00698858335906,0.00710161507669,0.00693344022921,0.00715979029702,0.00706077141112,0.00707974613861,0.00693426766286,0.00710279575384,0.00693133389719,0.00697549618531,0.00694456797816,0.0070734100023,0.00691052341417,0.00708144971397,0.00694823967887,0.00695227603957,0.00702424428234,0.00697109366254,0.00708724826923,0.00709304022186,0.00692589971949,0.0071937380677,0.00686663186945,0.00692658352657,0.00710493989607,0.00690726769365,0.00663587052154,0.00675588451467,0.00675936946632,0.00703195651333,0.00693864069242,0.00701490346602,0.00697165234381,0.00705749700671,0.00695827321796,0.00692224691608,0.00709647170227,0.00691120795264,0.00711247105941,0.00694805015986,0.00706667363767,0.00704291722141,0.00693024304332,0.00698307509276,0.00693918344167,0.00697577919853,0.00703053745986,0.00700011823991,0.00700781176187,0.00698276016195,0.00705176611085,0.00695215435631,0.00702990241567,0.00696809749615,0.00693355051203,0.006995757056,0.00702699145647,0.00693076182555,0.00712372562107,0.00692460485918,0.00711150034536,0.00699997626213,0.00704187390623,0.00698637726865,0.00704082354065,0.00699760220016,0.00701666906233,0.00710415045803,0.00705642380606,0.00695866679661,0.00697564507867,0.00695821244866,0.00705052360465,0.00703937657848,0.00738900209034,0.00685885805472,0.00707139910427,0.00716957840939,0.00687265774065,0.00689710084998,0.00670582200831,0.00714611332748,0.0066751572743,0.00714818843961,0.00647283105396,0.00771350782983,0.00667353954592,0.00716940166015,0.00771589635721,0.00669752509554,0.00699566626996,0.00700085571423,0.00694521601756,0.00708433894054,0.00730302748767,0.00675252467021,0.00687694479868,0.00683112140993,0.00687223374918,0.00715032306388,0.00705314434239,0.00722995982508,0.00686486067534,0.00710907022903,0.00697799537142,0.00665661009537,0.00703818260728,0.00688184907591,0.00705368102926,0.00697279218043,0.0070154479075,0.00703539517559,0.00698767270853,0.00694173484084,0.00696334053076,0.00706801383525,0.00702476321383,0.00702113202665,0.00697440239308,0.00691004431288,0.00705060541724,0.00697310590178,0.0068907442756,0.00684563448324,0.00715190005046,0.00700398260086,0.00690828430608,0.0070517594985,0.00694342091691,0.00714152448559,0.00685345798693,0.00691755012831,0.00690251581025,0.00709203070155,0.00709534292426,0.00711054449588,0.00691377541966,0.00713130819899,0.00691776468412,0.00711586298591,0.00691537761489,0.00689398783159,0.00716266415962,0.00709456188976,0.00690498606947,0.00667051388863,0.00657179330403,0.00680676553696,0.00662411437056,0.00685333554408,0.00688241001968,0.00719694149992,0.00721446537719,0.00675487167197,0.00733719555686,0.00689552478145,0.00713520624968,0.00674082233096,0.00715696295932,0.00670722549904,0.00660356539518,0.00715184202052,0.00709014741161,0.00674748173244,0.00695234437487,0.00659792677842,0.00708811292431,0.00664071206897,0.00702509957415,0.00702840085658,0.00699629269088,0.0070044710697,0.007018634346,0.00700431531906,0.0070007227246,0.00694647896823,0.00695090949577,0.00703333695666,0.0069375952762,0.0070505024627,0.00688498378668,0.00717649325742,0.00713405875989,0.00695354945572,0.00707185705981,0.0069196884462,0.00694024993937,0.00692636843992,0.00699230018789,0.00701153876734,0.0070277613519,0.00694965593393,0.00708905577956,0.00694244127986,0.00710425957381,0.00710062887571,0.00693684719583,0.00680394575516,0.00665766440832,0.00713935932353,0.00676062137672,0.00675775034594,0.00756413469772,0.00695446039028,0.0070148728282,0.00701101848802,0.00701471600013,0.00697790448346,0.00705218581173,0.00695295826649,0.00691223872775,0.00701968971356,0.0070072470255,0.00695432802686,0.0069930823042,0.00704423268506,0.00701120537132,0.00696414228546,0.00697136022398,0.00705714427342,0.0070596335074,0.00697336726551,0.00694553159555,0.00707445671119,0.00668382528116,0.00665551659927,0.00664681056243,0.00666907756478,0.00690745102881,0.00690143485603,0.00708812163381,0.00721407679825,0.0068595557068,0.00708106535329,0.00689943117646,0.00706467224958,0.00695575551823,0.00719326462853,0.00690546317992,0.00684735025877,0.0071191834716,0.00685328606898,0.00713580795614,0.00692061023261,0.00694357539324,0.00689865131516,0.00710100952774,0.00680494567949,0.0071718167543,0.00705355233406,0.00693451019707,0.00712536686411,0.00714396229503,0.0070435179424,0.00692078100539,0.0073799915175,0.00665851658922,0.00829482308249,0.00661527327239,0.0066460060289,0.00676845878284,0.00665167209091,0.00692467877869,0.00703443216665,0.00693672932187,0.00710041834074,0.00703844412355,0.00706653993023,0.00696570918528,0.00699274061028,0.00704325445675,0.00707112053857,0.00696089555998,0.00694194077148,0.00708690175442,0.00694187400432,0.00693089286126,0.00693209602284,0.00658593648311,0.00716945032053,0.00704946846858,0.00696044657262,0.00700589448648,0.00700262412195,0.00699524672613,0.00703713707965,0.00705352819275,0.00695259508204,0.0070808954424,0.00695774891272,0.00703756050349,0.00698922425644,0.00715832999692,0.00690333534926,0.00709111768162,0.00684938878564,0.00697884556386,0.00699075366144,0.00658983167649,0.00786648844821,0.00694967475262,0.00808330402047,0.00663001405852,0.00703734963743,0.00699122492928,0.0069674636432,0.0069835071021,0.00712538142467,0.00681271003918,0.00685945668413,0.00696661603323,0.0071497072586,0.00674273564895,0.00695717756443,0.00702536284083,0.00706772707225,0.00693842921102,0.00699755750839,0.00714084871423,0.0069297110427,0.00700855070996,0.00704576333846,0.00683687623979,0.00725175278027,0.00686792169501,0.00735145151511,0.0065974383155,0.00678266406227,0.00693381152909,0.00713273758931,0.00684848518757,0.00695006810902,0.00697426167172,0.00694994183701,0.00705346240841,0.00689486751628,0.00705864187498,0.00703392675884,0.00701329974064,0.00695234925879,0.00709051767033,0.00696201180521,0.00701071220916,0.00695515208297,0.0070340367342,0.00704961745685,0.00698711609104,0.00696630976862,0.00706519979436\n    \n};\n\nconst double PHI1796[NST1796] = {\n    1.58420912087,1.51101882331,3.04302378009,1.87312173349,1.93584936399,2.14511559175,2.28471900732,2.30983528622,2.80690693406,2.19770356865,1.74751082084,1.82370508787,1.83363620784,1.52713315956,1.50740662462,1.47939760106,1.36036872367,1.41834591208,1.48305783666,1.05832494016,1.20458468179,1.0729596368,0.998762256254,1.00563388938,0.0519493458812,0.4998107985,0.496449696587,0.410111432835,2.78924346987,2.72652156045,2.65911153052,2.72279028846,2.34070716885,2.18903972576,2.13899896125,2.2110526965,2.41642804733,2.14816493571,2.12563289709,2.1746312025,2.18511361536,2.05321756303,1.98622808547,2.10272933652,1.83603962743,1.8251450989,1.89492925555,2.00250291571,1.8835848715,1.93122354515,2.19433879698,2.27776053324,2.15147412122,2.11964683053,2.1733855357,1.42902727021,2.88020694212,2.55854723563,2.75787968128,2.76845494232,2.78779064683,2.64847082596,2.69390491721,2.29169935343,2.22312765296,2.26777904607,2.31768204004,2.38193230691,2.33306302307,2.3298328036,2.29753770411,2.03822487661,2.02974395359,1.90289449185,1.9610442668,2.32648988857,2.31885305644,2.4579020925,2.39636878418,1.85627375996,1.78068230409,1.56390150862,1.61057273475,1.6814255614,1.36854164365,1.43986623544,1.91118636776,1.43317510718,1.35927272045,1.50047805683,1.45320690011,1.21278132478,1.13822629268,1.06535030195,1.12520215362,1.38020858902,1.32590591929,1.25064106318,1.36576995487,1.33657122814,1.28244679703,1.35277402276,2.19458095454,2.13150816414,1.47425861666,1.44009689519,1.92587264185,1.99314365383,1.70183912938,1.77630601804,1.89069754811,1.81573423675,1.79271116455,1.41975985511,1.57294179028,1.50194112192,1.76542976174,1.62369649154,1.45047748175,1.47484436607,1.36007275435,1.43070953859,1.00102042759,1.07098160827,1.23596512545,1.16324027696,1.01798651029,1.04669895948,1.79868887267,1.72755296191,1.62273256212,2.21062868253,2.18094256269,2.10521276456,2.16185410069,2.07273198741,2.05824404413,1.913054513,1.29181900029,1.22684035936,1.23127228145,1.36521267271,1.30109121968,1.30765302292,1.56089399388,1.49479396872,1.57079824974,1.43492758418,1.54014848388,1.52804686882,1.57090306115,1.61682208647,1.60777513036,1.66327432463,1.73147144206,1.40743061469,1.46019221835,1.52974791126,1.48336243681,0.975696899313,0.973612636484,0.400532193294,0.44529740282,1.46714384682,1.74085205514,1.3585210996,1.32007882487,1.15207498553,1.08329913191,1.02800592875,1.44031334118,1.41016970352,1.39064822034,1.40014182581,1.44259405317,1.41557040922,1.27317176525,1.32469218711,0.982383498889,0.879946676024,0.958571668296,0.946608362242,0.571229942208,1.2839963028,2.44421055905,2.51791820681,2.66711493774,2.22766814953,2.14225975902,2.15881358585,2.23583745912,2.02480517244,2.09700733438,1.9826058877,2.12699295962,2.25528063985,2.45928781197,2.3071611087,2.23073212705,2.19860402382,2.24091972248,2.63841732549,2.5641156984,2.53264526617,2.25072522472,2.31012123779,2.24151242699,2.30644774792,2.71184376593,2.63371979918,2.89042036207,2.06419136785,2.13975662035,2.21501955888,2.22867302782,2.24664604812,2.26774908549,2.33720217685,2.19108086601,2.0848766895,2.15717082961,2.19377128219,2.15377456052,2.02901480434,1.97266504653,2.04555109496,2.075119296,2.06966687759,1.85352757048,1.78619497868,1.97434643756,1.96014062441,2.09027063862,2.02012272434,2.44051780105,2.37214018588,2.85554819963,2.91268841179,2.96827694946,2.93153300991,2.94958867356,2.57541410418,2.64051420473,2.54844864019,2.42487526512,2.49721942636,2.30132666343,2.37304713331,2.38403896755,2.31055520797,2.25969438676,2.07666977758,1.92729340114,1.77062232171,1.8223864517,1.45557147415,1.47820252641,1.85314095955,1.72834263826,1.80178221692,2.97318794366,2.97979929776,2.91044890952,2.85404288758,2.66570178574,2.7294830238,2.60783975019,2.66039883584,2.68802314851,2.62234321293,2.77715709885,2.18753146018,2.11311876923,2.0757821205,2.22093318867,2.1765447379,2.10609878084,2.3868294402,2.52725429923,2.55851049981,2.58322421006,2.42905892999,2.40165491684,2.40532015145,2.43340588401,2.51160108588,2.4422442411,2.54764977179,2.50550218838,1.78347531964,1.77464143762,2.57700454151,2.5446844452,2.10365326335,2.28163249974,2.29470482023,2.23013910896,2.16570880352,2.08468710246,2.0709757941,2.20657382688,2.15516584542,2.38889843886,2.39348256414,2.51269752108,2.44986374417,2.51555527677,2.45716701085,2.26188898038,2.25966088483,2.18603460443,2.33727541004,2.26824827511,2.21533981317,2.22013312573,2.3393552092,2.27520124858,1.73350670565,1.63765618323,1.71015329576,1.75865728668,1.94091942013,1.58764313311,1.53921714457,1.75516607744,1.68293809913,1.65899689023,1.70619331085,1.20680794668,1.46818062327,1.46030697096,1.55338881882,1.53228877365,1.50284216572,1.5822739522,1.5145416359,1.46630913985,1.58814875905,1.61035635722,1.48975506369,1.56010680829,1.86457497927,1.94169547364,1.86841241876,1.83793999008,1.41790527638,1.3478971082,1.28953925266,1.34496902276,1.43009003473,1.40599485212,1.54691728379,1.52367043415,1.19311309597,1.31473969113,1.24356791283,0.999668032316,1.38398961619,1.3120338766,2.07391083618,2.06767035148,2.30294241589,2.2151154198,2.23667506123,2.18733355815,2.11835402683,1.44913703091,1.4799898169,1.49118018683,1.55388670094,1.7799344254,1.81848952242,1.39408416808,1.46347591112,1.3662751011,1.41000109515,1.66245103686,1.54849613063,1.5883322247,1.6164679694,1.74064674232,1.66685380821,0.947688423164,0.662390899225,1.30933869594,1.62434474444,1.7339325481,1.66189831489,1.60745165997,1.75350897277,1.69831137416,1.47910134778,1.43866963728,1.51411449785,1.53328991365,1.40493032404,1.38357909656,1.58446725261,1.51100609004,1.45635536823,1.47565048827,1.49167921652,1.41822629913,1.39764888718,1.4672032721,1.8113258956,1.91961765867,1.88578999764,1.58265623307,1.61084475725,1.68567299511,1.38442071502,1.46019602714,1.12218071057,1.10707776333,1.06349109437,0.939991670212,1.00821034619,0.908800265971,1.22085252405,1.2633138161,1.32427267761,1.25368083398,1.3375828119,1.36588705482,1.15024979327,1.11979011921,1.04951488434,1.00990127292,1.11294444669,1.04306390762,1.62923728156,1.7016348481,1.60234113516,1.74679875176,1.64839390285,1.72035258667,1.98272942116,2.05897989674,2.08616803711,2.03629437175,1.78013343254,1.97470900126,1.7898149076,1.85284793045,1.9157702615,1.97923839005,1.97677301809,1.8497460862,1.91021671999,1.29184087494,1.349402265,1.31994267161,1.23005223123,1.19422670431,1.2449921713,1.16769928107,1.03979822503,1.10024334137,0.43885600094,0.646353642613,0.70417418285,1.74402054027,1.68476335181,1.89833887688,1.96774028617,1.63189666233,1.67167768236,1.74419966523,1.66328865773,1.4106743887,1.55082841392,1.50380702207,1.53036672341,1.46323577362,1.25307616394,1.24492446993,1.17482319619,0.991711599833,1.12833313596,1.04804297693,1.11552958251,0.552808760437,0.509674006106,0.54574839834,0.661150515928,0.861048753031,0.834192935558,1.18002803447,1.03664782873,1.29497405278,1.25300907874,1.63139111688,1.67523985569,1.88189475723,1.94985578748,2.00490005432,1.7148564613,1.63904203484,1.61159077005,1.6593444569,1.74225445009,2.51087905075,2.11118704206,2.18934588157,2.12423907216,2.14482976862,2.16270847098,2.19293218723,2.26385113089,1.26519381111,1.20105579835,1.1307320667,1.13822358204,1.4259159719,1.05212275193,1.07899320227,1.15010384613,0.373092944055,0.294324197494,0.111722985848,0.0868674113013,0.152657686601,0.260233370662,0.188115974748,0.535220739072,0.593772189461,0.353543046884,0.44381985768,0.465421712,0.371453336649,0.327003031787,0.537676472446,0.588525234199,0.660613801498,0.966020084887,0.93841147643,0.682271910235,0.958954832844,0.806975254602,0.904884047358,0.921215476332,0.359413549666,0.712737347355,0.822523873168,0.782946671885,0.835556003095,0.678411876115,0.396580383835,0.933540252446,0.873738637037,0.629093511445,0.755405679929,0.698068227212,1.17114889463,1.13884707392,1.06678343039,1.02749010406,2.47764882403,2.60533956264,2.60050685056,2.53484754361,2.48284990228,2.54449167722,2.28403863293,2.3525391266,2.39306430964,2.41014954011,2.2941037785,2.31928079702,2.26740271199,2.19465456008,2.17131432409,2.21911130966,2.17638452927,2.05294274433,2.11221900864,2.1727984994,2.04861977261,2.10441939788,2.41761419549,2.29911086459,2.35149751977,2.29552059227,2.42224601663,2.35968388547,2.33953851016,2.25955073543,2.42190051719,2.34621586848,2.31199481926,2.37181874187,2.37722771595,2.65262869616,2.6691102498,2.62055942451,2.71367919087,2.67976827408,2.73967190148,2.87618709879,2.7721624692,2.83590880729,2.7911350095,2.84893879608,2.86565913462,2.90024811738,2.8322044466,2.82941805411,2.76045521184,2.70188013898,2.03840692049,1.94830120598,2.02182198575,2.23166905655,2.1615729606,2.24872531388,2.10241386269,2.29320509615,2.26384441527,2.26750996196,2.303936088,2.41361846179,2.37707252943,1.90875338707,1.95595655075,1.92901330825,1.83546534023,2.03243368136,2.01590070473,1.97529270566,1.90533079744,1.9438479135,1.89037500464,2.09723093125,2.04788555354,1.76716510309,1.7291298522,1.80087834001,1.81960967938,2.40153022084,2.30123963043,2.37377892907,2.25851718091,2.28352285003,2.3535828001,2.47188814273,2.49270715319,2.80898082432,2.73504379482,2.90301308159,2.85188886736,2.90860999989,2.85252279355,2.77604984944,2.52136317084,2.44959865816,2.52610563539,2.56440648191,2.67157020606,2.62167380229,2.63610768812,2.70352415541,2.74581638835,2.76404602327,2.04688767691,2.02583284694,1.95021019719,1.89658566849,1.58242087088,1.4866802328,1.50796486821,2.65398403666,2.72585627979,2.61489001717,2.65291380695,2.72522423431,2.50120490955,2.51634381565,2.57535138731,2.54363202882,2.61738697179,2.63532378133,1.69222836947,1.70951450005,1.58906124658,1.65198422516,1.29906570126,1.36188767237,1.52091953754,1.52661420647,1.60172275027,1.65894284541,1.72528923208,1.72853225628,1.60531785766,1.66576274119,2.10400140077,2.04152250345,2.04338785672,2.10637653448,2.29853203625,2.17063946652,2.23295069772,2.17121634737,2.22505258658,2.28268309673,2.23259728417,2.29857886613,2.35802364453,2.35706947385,2.42808187992,2.47679762464,2.42832666098,2.49284579928,2.43231967279,2.47627567994,2.40798819235,2.35340011506,2.36164644085,2.00671592766,1.86299341406,1.81841740298,1.93590936383,1.96241176059,1.84449443007,1.91548262976,1.88067507903,1.83017660212,1.95698010726,1.97393122424,1.85200743874,1.91949728936,2.01111336224,2.05963698295,2.13356652828,2.15229400966,1.63456857211,1.65969615884,1.61225021852,1.56228273982,1.80218526663,1.77705877359,1.82351606924,1.89462772325,0.961462625729,1.0819399234,1.23257692957,1.26239527684,1.32899247386,1.83481884062,1.6837460359,1.73412697492,1.80432906942,1.95533773315,1.92975647925,2.03459881364,2.07913537369,2.04669394433,1.97655321466,1.15055872367,1.15898437802,1.21612011088,1.23637411174,1.29070201609,1.3025297183,1.34881002678,1.39675381982,1.36185801387,1.43868438896,1.51192523909,1.40551455491,1.38047611748,1.48856881816,1.5368552846,0.992609389552,1.15903868191,1.13524930359,1.0046267292,1.06604188405,0.983402093172,1.05637040399,1.10835553995,1.29393139849,1.32688893286,1.38246395085,1.36358955441,1.286419164,1.31098525486,1.33434908745,1.1927031582,1.26633357242,1.25204812978,1.29463623085,1.5520196676,1.5733984453,1.64696237717,1.60392783228,1.69877765328,1.67703126992,1.21410657803,1.18311943357,1.22081965242,1.32682419177,1.27760432116,1.24333165413,1.27008464341,1.22379325646,1.15987388664,0.738005358394,1.97441621546,1.99184776274,2.0406981732,2.10739709745,2.00034025294,2.03395595281,2.1162201287,2.14557551839,2.02963244323,2.0978837506,2.15457234525,2.22809188721,2.2655310499,2.22498661993,2.14931694648,2.11605961575,1.93010497631,2.00370050144,2.04278140928,2.00554909249,1.9301744245,1.89363304174,1.63891401331,1.59315882789,1.66689247473,1.705661728,1.56120064276,0.934467946038,0.926983555112,0.853579764781,0.789515869793,0.730288297305,0.736616648092,0.801289344544,0.859077171837,0.923512322743,0.440988308117,0.83710414257,1.32903080951,1.37545527265,1.35264457724,1.44737098683,1.53498759372,1.42507035777,1.34670077992,1.41706814085,1.45925647533,1.33941321731,1.30310564396,1.70833603919,1.63641137345,1.68053466768,1.75230352346,1.56498957651,1.48970914095,1.47128384792,1.4421020878,1.59441092223,1.54765331952,1.54551098221,1.5917413386,1.787686745,1.71346441122,1.66530575644,1.81349877995,1.73266012543,1.80811326927,1.82481581689,1.85462022554,1.62858069618,1.60096082881,1.64706287681,1.72037683596,1.74944763729,1.70366172982,1.28286877999,1.25746483005,1.35691083361,1.40479747162,1.18479361565,1.13731846367,1.09098645034,1.16183397069,1.21690960441,1.16360616699,0.880627519482,0.950992708503,1.83427243984,1.7633294369,1.7318307216,1.77302138179,2.00049504215,1.84936992271,2.09785956801,2.15711527483,2.01869981792,2.02851593563,2.1446051775,2.07572902673,1.66191211486,1.58864984776,1.59594676989,1.16440590846,1.03573409013,1.09813913336,1.16114573134,1.12203582005,0.985440185676,1.04146820254,1.10707699097,1.46686004129,1.4062600968,1.54133411741,1.53580675451,1.44198169669,1.45807670393,1.51172655704,1.51977268478,1.38689759395,1.3788038149,0.833188144973,0.902200665496,0.793668657484,0.774509934742,0.865402613687,0.915623230856,0.827025902625,0.827725997903,1.81134479045,1.81922507026,1.94680770624,1.8870822155,1.76355238712,1.64810567531,1.63825702399,1.69635800839,1.26380472954,1.19102218742,1.21740212291,1.16790983928,1.38618834603,1.4330307206,1.29082327102,1.31422944033,1.3426343838,1.41282797995,0.916252584528,0.799035579925,0.924070587068,0.867956246591,0.617635909589,0.665217979013,0.765412033996,0.741860305781,0.671692294579,0.621120317918,0.720893310093,0.65063583408,0.723540036063,0.723244613277,0.851223688189,0.790114052918,0.561610298256,0.694922524689,0.759553990129,0.758882531585,0.694504333493,0.625870840418,0.625605519318,0.57270696845,0.517965196254,0.704079040532,0.775029201873,0.665038187413,0.645532574026,0.738275051303,0.790091540424,1.09191463758,1.09581814792,0.40171840095,1.69445919849,1.72057474324,1.68577871083,1.6200030555,1.5071914793,1.58563605994,1.61903079298,1.57336625288,1.47066681126,1.57139561826,1.64304360448,1.53716877368,1.68053612311,1.96040626177,2.29735367536,2.39945842796,2.33480605412,2.44522868238,2.4111360434,2.32425740771,2.29337944765,1.7927663794,1.80735985257,1.87204406434,1.8643379815,1.81698830624,1.84148515643,2.53080345438,2.60574737603,2.59995358632,2.31387342058,2.40734715825,2.33177135404,2.28768844556,2.43673016597,2.38708576755,1.28256614727,1.33314969562,1.38115054599,1.35696770836,1.40430904837,1.45256385251,1.52793676096,1.5558347856,1.35366130035,1.28285527363,1.34299432903,0.184239200186,0.329634175677,0.234678575881,0.303517690576,0.273304718119,0.209891747473,0.296003687996,0.223477844216,0.378753803946,0.310877478377,0.30684855923,0.432848727098,0.375089540876,0.432693464701,0.249884225986,0.276925928793,0.124861033531,0.180185680966,0.415253930408,0.469268662045,0.10900759748,0.0365485471943,0.0763342388777,0.144460534664,0.62688832253,0.48223556573,0.369706413895,0.433762796463,0.868455665226,0.728457920349,0.708949442715,0.738034309498,0.754389170308,0.825310861056,0.563349896657,0.635114225934,0.659395193048,0.603357133938,0.5374220192,0.890610858096,0.843845931016,0.77511062923,0.870500165354,0.752840213261,0.801656435258,1.00583616637,1.07491375505,1.02840465229,0.951808797192,0.560495111752,0.50176110973,0.616096733248,0.530578997654,0.606348760051,0.496317835421,0.555341585507,0.396258324242,0.372159007575,0.622035370281,0.694109744354,1.38715439226,1.46477527772,1.49768553674,1.35145950245,1.39521426227,1.46368608605,1.24126575274,1.17180986582,1.24662429095,1.2808496089,1.24473055121,1.27826142911,1.28558652557,1.35338008255,1.39461449519,1.36020333047,2.47316265154,2.53937992891,2.38819030283,2.45777328724,2.50714107098,2.48126146217,2.4091529568,2.36499950183,2.52228147257,2.48615609872,2.51717641037,2.58922724383,2.47688888409,2.43046611961,2.37574876698,2.44806432432,2.33167037305,2.35851126582,2.50717683119,2.57458066682,2.44874356012,2.44661494604,2.57558480492,2.51032416282,2.56186842408,2.59067062165,2.48669009544,2.55245553912,2.43175649232,2.4980764772,2.53881692681,2.94294638155,3.07955138463,3.01111779059,2.95631880572,2.91372833093,2.84598930197,2.5395998011,2.5679238863,2.64017250619,2.58647001964,2.65235057225,2.75245636111,2.65517803002,2.72463499459,2.7784671603,2.67766632709,2.63300143911,2.11410424448,2.06995006151,2.0840502223,2.01204527666,1.92433588665,1.97468372387,1.85220234177,1.67699533751,1.60433665658,1.64326962616,1.69637660777,1.5697289803,1.55009134768,2.5612388216,2.61293476535,2.68421109573,2.57221498913,2.63630682488,2.63579630627,2.69629900359,2.97494217423,3.07641146821,3.00837758028,3.104486587,3.03030164815,2.98143878574,2.75775162724,2.69193353044,2.69284064246,1.9146682986,1.86251495018,2.00437934862,1.9892975384,1.88585297235,1.95312783523,1.70599441102,1.63325749491,1.25427446488,1.32503983653,1.33569059777,1.19688640478,1.21256602823,1.28050384768,2.10316546956,2.03594685949,2.14070815436,2.10133523769,2.12831286211,2.20038827013,2.19462875507,2.11824317028,2.0759601227,1.02468225544,1.04249662754,1.14130881014,1.08213750778,1.1576021965,1.1161843289,0.956592606151,0.891185512795,0.891229121969,1.01940359965,1.02092340972,0.958043195383,1.44605695185,1.3769784284,1.49343804208,1.46683664255,1.23968498433,1.27202859957,1.28891819459,1.36373972547,1.39233538435,1.3497974976,1.80321973068,1.78618665439,1.74288883623,1.67454059671,1.6541364946,1.70541283076,1.1615940847,1.1594246628,1.22889680389,1.28067091261,1.23271714096,1.2866697136,0.837469305315,0.885537762583,0.947492437212,1.16505389967,1.21315248301,0.92028945901,0.952342133954,1.06132779108,1.02325630892,1.17149720936,1.13437650087,1.03844313896,0.973279634444,1.08869975373,1.10112410201,1.01024079333,0.956966741696,0.834722873001,0.88597478222,0.808660894206,0.873758768994,1.60178018913,1.67242055116,1.83382922616,1.75859700679,1.70722067764,0.90498511185,0.834815966637,0.937033506053,0.901822703435,0.855055302831,0.741600424427,0.80869907878,0.537228737144,0.602593651044,0.414012389797,0.490285116712,0.528203926777,0.513571925341,0.581493304816,0.69696473426,0.631574247358,0.809506907084,0.855953474158,0.761528923548,0.832711630605,0.715858505158,0.740836158965,1.25870804077,1.11751067256,1.21903313971,1.14905490774,1.13418686701,1.1596684015,1.23075805398,1.18337854233,1.25635259988,1.27841865268,1.51947164032,1.66238250757,1.63660557523,1.56497517976,1.54324540861,1.61444388594,1.65000934382,1.57618654745,1.58281320787,1.54249836732,1.87877968886,1.79544222079,1.9102737089,1.86760597241,1.65880627156,1.69220427023,1.76606765866,1.77745751819,1.71131023636,1.37994589472,1.43035337082,1.40884132013,1.30677336883,1.16127916357,1.09733636572,1.25471181489,1.20006030025,1.24054093665,1.17152721717,1.0925092297,1.04140358529,0.971939367671,1.04208878598,0.972221403143,1.06564706243,0.924905289791,1.02108749938,0.950467450627,1.84796498293,1.88845682153,1.87775784384,1.95321668626,1.99718294133,1.96207615465,1.72104593238,1.83868555821,1.77186103773,1.71453342,1.84652196592,1.7861587099,1.07246511401,1.09524121194,1.0490998415,1.00171956333,0.952650373546,0.977616239692,1.48378159981,1.4177194504,1.36559856683,1.39768826509,1.47261104883,1.93363853007,1.9882912319,1.96058430834,1.88315036353,1.86621537424,0.741170274561,0.789766659872,0.92017093764,0.91424124371,0.851324376439,0.902516916083,0.962908701507,0.91443013626,0.983915219279,1.0388870541,1.02979369444,0.51913595537,0.447084360486,0.573050045411,0.561995534742,1.36816209359,1.40094007387,1.58029288635,1.50953343478,1.47411459373,1.5168592572,1.59188085479,1.61999041031,1.74663263723,1.88928960671,1.87615223484,1.82914430093,1.84788233682,1.77611020001,1.75552351783,1.7306470932,1.99181351619,2.04975781457,1.9760804431,1.94882380648,2.20491668232,2.0628173489,2.1630300817,2.09372093396,1.75162820379,1.89423334113,1.78605341529,1.85682813888,1.7474894998,1.78648288979,1.85820865427,1.99981366406,1.96598988289,1.99309838022,1.92826296479,2.04620918025,1.91218593237,1.93676881563,2.05278160143,1.98216865835,2.07661000933,2.02931218295,1.96027959417,2.53049234395,2.5428992254,2.45021962891,2.40669425706,2.47600365032,2.48259293905,1.17478447072,1.23811298794,1.26480259705,1.43146482988,1.48774969826,1.53542226823,1.50751648827,1.3838498226,1.41233872092,1.09673027516,0.242918262944,0.290964130236,0.29789528496,0.167024016301,0.345772952275,0.208035823495,0.489441382609,0.464884202305,0.638660502835,0.743111156163,0.761683874558,0.712579775106,0.593887984678,0.661680942938,0.531446919418,0.54397698185,0.672358828899,0.616769079835,0.303800209567,0.234090569062,0.182390213793,0.221859267166,0.32907456823,0.294988036348,0.246995650997,0.171620115615,0.735302627071,0.697623929603,0.700486241437,0.352074367588,0.280870995949,0.28958450821,0.84422685231,0.812523798274,0.917496688742,0.956472861046,0.924888396233,0.854390350301,1.04575950365,1.18457540201,1.16256788207,1.09352780731,1.14048098101,1.07079894308,0.643918387682,0.628775471599,0.714972952383,0.765388563449,0.685891670949,0.751233220095,0.42427689897,0.430423679776,0.502034596576,0.491532852166,0.556269876985,0.56081311978,0.684890406249,0.813643894801,0.810067605291,0.749111845953,0.593535645971,0.62101963185,0.4717619185,0.519908544525,0.630300976626,0.756383827961,0.688665736029,1.16992139502,1.06177123954,1.13314855632,1.13457273723,1.02488495778,1.06106446187,2.57738422355,2.6613984368,2.5946633352,2.44839185707,2.47316275239,2.37398668357,2.33115549895,2.35333465077,2.42023970316,3.04506816708,2.97300746682,3.03262359869,2.96189260759,2.84287125727,2.77825763708,2.9095500181,2.90589903399,2.75554341575,2.78069255042,2.84075386738,1.89621005983,1.96818097205,1.99614698567,1.95012063067,2.87037214228,2.80510038867,2.88844625845,2.75825382625,2.8251023867,2.76286494127,1.65699231203,1.51010737131,1.53699630961,1.60904354031,1.97544454027,2.01772227876,1.90153227161,1.87285395554,1.91893988375,1.98987115505,1.04202050948,1.0899658952,1.06386956671,0.989044279076,0.966149916537,0.939068791069,0.728984871729,0.803146931542,0.845022947368,0.949691682222,0.91259991329,0.839027007687,0.808349759643,0.922258877423,0.85483729421,1.07214566741,1.14759396845,1.0280794211,1.06219891445,1.17871729789,1.13722478518,0.895173415089,0.936811977655,0.880476672928,0.967028125198,1.50592162084,1.57412676262,1.61727504668,1.48018576241,1.52463161697,1.59344352567,1.6842230706,1.72877964561,1.78350256798,1.71178184187,1.91192094227,1.84849885788,1.79488755359,1.89342517189,1.82209207361,0.715974904262,0.642471196801,0.667065789948,0.614300387007,0.456605814746,0.344889643187,0.334107281228,0.394164948076,0.41739317328,0.470075916147,0.795618251309,0.829144266314,0.793860209285,0.723755919008,0.542675816227,0.509046157554,0.61613743074,0.644279050543,1.69078763421,1.80514618177,1.76434662676,1.77138095267,1.69708173053,1.65717723436,1.28427328013,1.21213032077,1.31609696169,1.33602321116,1.24465021584,1.1923417806,0.97631846333,1.03832466353,0.914494167364,1.07462954236,1.12899981004,1.11453239485,1.04526797212,0.814675631685,0.845913236305,0.917150448892,0.957822291308,0.954077971975,0.883920954005,0.833454269061,0.858080091727,0.975477797374,0.929800233624,0.799502647566,0.763741344901,0.74310927173,0.866790868595,0.835611367556,0.882770428549,0.430371901515,0.429710227769,0.492038251008,0.492094287458,0.360582628518,0.361144936965,1.77901157521,1.83739430455,1.73460568526,1.76277029354,2.2224228909,2.10387334871,2.07214388296,2.17649732809,2.11121770083,2.21864294782,2.18492328668,2.16913233304,2.09732802466,2.21400816574,2.18295731663,2.42370255621,2.32798930135,2.40344795302,1.12945629575,1.01987995373,1.06050371386,1.00415815485,1.16177495927,1.22392325529,1.16660169299,1.30984539984,1.26500955098,1.19327913481,1.30626619716,1.21205121523,1.23677930118,1.18938227222,1.33037903336,1.28340253861,0.338851609819,0.404288336402,0.292648181948,0.275596958506,0.366796636962,0.41645840733,0.109417678754,0.157635539968,0.226966533231,0.256453243699,0.160161172184,0.229786455716,0.398601655397,0.364693766391,0.46465793425,0.398426324772,0.583992853936,0.62587250447,0.509061416467,0.580945743251,0.520663457434,0.472181205604,0.505957470728,2.62467325665,2.70509096032,2.63136592941,2.59473733873,1.85130806788,1.73359033576,1.77967004244,1.7576546513,1.8296940184,1.87735144132,1.63137324417,1.67565695401,1.52920117325,1.55663362059,1.64388526052,1.57226201756,0.69613820301,0.645814780494,0.594481800257,0.621742246828,0.816172412353,0.864671222275,0.842531882267,0.770818882529,0.718322512862,0.741883689825,0.761005541058,0.832781317031,0.794868828585,0.740020254712,0.677220537581,0.600388937548,0.686985458415,0.714031450439,0.545798524496,0.560957235597,0.621612488133,1.88217336621,1.85182079054,1.95434696082,1.89213731685,2.35058036811,2.36092599671,2.28103751664,2.22796244593,2.24171104847,2.30528512215,1.18897956013,1.2389575982,1.1149040846,1.09278799174,1.21807535401,1.14606176396,1.12147792539,1.00624757863,1.07410894781,0.988513256188,1.04159329698,1.10660489467,0.473386697878,0.51287878882,0.515583308954,0.591150126351,0.624781012168,0.587553097818,2.74098434533,2.69617520304,2.84028534388,2.81580391482,2.78440850139,2.71639414855,0.988864517365,0.920222203081,0.864501986822,0.881568234128,0.952112521506,1.00382764653,2.10956792546,1.96444082074,2.06865814636,1.99643755188,2.00239136293,2.07515370213\n    \n};\nconst double THETA1796[NST1796] = {\n    3.11696720803,3.10394409469,4.39023560552,-1.47996102071,-1.50261725072,4.53780397933,2.92368667255,3.01548183309,2.02774669381,2.43913840621,1.6431567108,1.6806258982,1.75347738825,4.13934084989,4.26030305167,4.19385519434,2.50759607104,2.55555889384,2.53329064556,0.5041092846,0.716253056205,0.664232349476,0.543698835418,0.631696657453,-0.628940248171,0.675704520069,-1.11278715307,-1.10602889818,-1.46300610741,-1.37387066433,-1.14361409461,-1.18466438769,-0.961397494954,-0.920225572459,-0.602567111427,-0.60750493335,-0.972131052944,4.30676781216,4.45385847153,4.39022260989,3.92489628396,4.43601138882,-1.44079949178,-1.34473364758,4.67479176683,-1.52761438223,4.62104161492,4.49840341626,4.53905066014,4.48162584529,0.470807279253,0.629174615509,0.405209861567,0.269150288279,0.316542736561,0.268857302237,2.01883805256,1.10526514833,2.20649690837,1.85691156015,1.6530488156,1.76572080988,1.88773126948,2.58469644033,2.75818159908,2.41554768156,2.48896736705,3.03917379644,2.6670646452,2.84395096528,2.75584011586,1.85871986443,1.77655009591,1.788609347,1.74396641816,3.5334404844,3.63465677441,3.19032894296,3.14806433543,3.50044730199,3.48294703703,3.24387761448,3.18669337893,3.19954614299,3.84917463367,3.86380430893,3.85274824422,-0.593298359479,-0.592355574076,0.0595526512153,0.198975457347,-0.594471982184,-0.616911229541,-0.74655643152,-0.703599806994,-0.32943236255,-0.529691758185,-0.530387332172,-0.46452290929,-0.395442636697,-0.24740993869,-0.257116681859,0.126111301358,0.180722839472,-0.533710300542,-0.467633734934,-0.452829995679,-0.592590585973,-0.434282275303,-0.442778229229,-0.386234992005,-0.382652955997,0.213570323198,-1.27868639082,-1.56320891179,-1.54518874946,4.06112806996,4.03121391069,4.68352865054,4.61914767175,4.56321077222,4.56061190678,3.94717304501,3.9711473413,4.29164633518,4.28372836644,-1.54878996229,4.65389548521,3.15240685143,3.14086530791,2.8648195471,2.91381662195,2.83600353743,2.83580245668,2.98494963432,2.76566942477,2.47915514615,2.09963729794,2.53752284579,2.49604227844,2.42205385632,2.43650121669,2.39274191183,2.32161175866,2.43232942803,2.45924840781,2.35637943804,2.41062766778,2.58450705363,2.65277530061,2.7156329554,2.47388828711,2.5521697905,2.58839457167,2.55593025031,2.64126432365,2.68703422164,2.97903460319,3.03458955081,2.40291300204,2.49632167889,3.98430694245,3.83652118274,0.883785410768,1.57577058819,0.279220593823,0.622426595222,0.769743709558,0.744080059866,0.801581894418,0.648380097631,0.527485306951,0.593708118749,0.713487960603,0.767912849135,0.824535540422,0.744889612375,0.699342509255,1.19399412268,0.63657853055,0.771152975618,0.680356381203,0.724170803152,1.84270016608,-1.06908359912,-1.08790983877,-1.4736376954,-1.33674493581,-1.20916691518,-1.2964240076,-1.43206030532,-0.666331559308,-0.674570070752,-0.731957398379,-0.754261705815,-0.530406346273,-0.869645770747,-0.869570149249,-0.8533511522,-0.767354112435,-0.692780025383,-0.998989787458,-0.989399880722,-0.866375273084,0.163616245194,0.0937712006308,0.266635746658,0.297674249032,-0.351582659351,0.1817781513,0.577839615279,3.86156697227,3.85262141795,4.56659798067,4.65934749613,4.41091082921,4.50396744131,4.53566538649,4.23657665973,4.14874158517,4.1544155729,4.07960764459,4.00203175083,4.35990288415,4.21062726221,4.21862970236,4.2946712886,-1.18058910901,-1.3937039607,-1.37065850911,4.70852286388,4.63868614877,4.59461982832,4.57363759132,3.67191987711,3.70486160884,4.03256266757,4.53926609136,4.28172156808,3.97807725351,3.60280217705,3.29738876473,0.879203764477,0.659632456865,0.598973946005,0.570455281922,0.541488428608,0.522263675427,0.420444590184,0.380624775421,0.457533606485,0.430996500798,0.458931227821,0.284200665562,0.342518798111,0.393701077906,0.326686565105,0.469594071212,0.420121145492,0.410765780769,1.15715513953,1.6008956816,1.73294012193,1.55514545865,1.60719403901,1.53447616694,2.38303652697,2.02748677667,2.17630378858,2.26007388697,2.38994679319,2.68306753989,2.69079723292,2.62317266962,2.598167004,2.52650681956,2.54347382169,2.46860245296,2.4155394324,2.17105286442,2.04370161769,2.9571983196,2.85320255642,2.65805217288,2.55469045912,2.75923835288,2.75626025173,2.63899073172,2.53284538734,1.87014617086,1.79393362094,1.80318905743,1.94157386386,1.89649828634,1.74092012906,1.83739665902,1.88903393365,1.84867024245,1.7263619017,1.64095355672,1.70526810984,1.75998193062,3.4886400959,3.3771550786,3.5041190629,3.55510612647,3.37324183144,3.31397783794,3.47753448973,3.08387066844,3.06391312088,3.32558610272,3.38086488719,3.33456983978,3.23856720065,3.21834075865,3.17933631557,3.53703575341,3.64502231335,3.6619796832,3.60792444818,3.17396744358,3.31411906079,3.37070978353,3.41260789311,3.39765480021,3.32763408808,3.27034254429,3.36427125586,3.35805364977,3.9332336833,4.01578699248,3.94647862293,4.07112758592,3.89091437366,3.68101551885,3.73597484237,3.69793853016,3.76674294878,3.80720058815,3.82093585265,3.99244478999,4.13760825774,4.13301548335,4.06708410417,3.41658437241,3.40452190831,-1.31609382752,-1.26290615952,0.0688752185723,0.138742980195,0.120541836334,0.190204131215,-0.727251124159,-0.662650012155,-0.664148531697,-0.699820866827,0.00848741055833,0.0187052143494,0.139399233478,0.0557814498934,-0.0120156991934,-0.130299704269,-0.0372095992412,0.0297746374591,0.00199767892065,-0.338687823289,-0.407896990091,-0.27721679556,-0.41580271502,-0.315184855276,-0.254694506196,-0.194646561869,-0.20545324569,-0.123018139495,-0.0620473397403,-0.492650013368,-0.541446912863,-0.483297027879,0.111908104668,0.156624148111,0.170111043244,-1.53766386653,-1.04278942058,-1.38559090297,-1.51541508146,-1.42144190952,-1.39781840448,-1.44511137896,-1.49236950371,-1.53889899918,-1.47788372776,-1.34500041285,-1.35980453147,-1.42575122839,-1.46561270838,-1.39851604542,-1.12231520216,-1.10740754295,-1.15968678037,-1.22676551895,-1.03975545821,-1.02263584981,-0.955080195856,-0.658577131878,-0.510885154629,-0.586595540787,-0.517383146097,4.2762561986,4.34786841412,4.36405968649,4.30645843175,4.31526767164,3.91373836876,3.83164491113,-1.47935400087,4.41989452267,4.57963681975,4.33506264277,4.42754432216,4.36102583151,4.49643999938,4.49751426378,4.36619771715,4.43167476004,4.42625777768,4.35173531456,4.34669011599,4.42236149967,4.49965929887,4.50087134124,3.06004078327,3.07099892009,2.99137310438,3.01114718626,2.93336243777,2.94209963644,2.90156892058,2.90562610719,2.97723374988,3.04362216346,2.61376186282,2.14120779654,2.02333959025,1.98329380104,2.02175669294,1.98205516128,1.9024279626,1.90616240445,1.86533235513,2.60888043145,2.66454714431,2.7369060899,2.64819334802,2.77444131185,2.72398103454,2.37793700409,2.36565066937,2.41270366055,2.86420496594,3.20836591276,3.28555599027,2.48705032518,2.44137303573,2.25308778873,2.21918559974,2.72316774349,2.66431834953,2.681370736,2.80336283646,3.01941928164,2.85323198207,2.90934564957,2.78416624801,2.76426749282,1.91568641275,2.27519149355,2.30185585023,2.22394552235,2.1703169235,2.27926539958,2.25077422282,2.75365795152,2.87615887155,3.00719314997,2.5656284766,3.89226142163,3.69951106258,1.37274604013,1.36013448672,1.31420619098,1.37787352811,1.07581603898,1.01194573487,1.63667279634,1.66459721178,1.61202227618,1.26486661611,1.26223845976,1.19948480513,1.13847786046,1.32817429596,1.19493224764,1.49340894611,1.61318693872,1.58261982428,1.33776517938,1.42724535218,1.26883902613,1.28893276101,0.58441186803,0.632092603167,0.533306483196,0.606049533509,0.943816914402,1.2088429764,1.28944832833,1.29899684398,-1.26542623772,-1.2995025981,3.89192797693,4.63782220326,-1.42986935729,-1.07224634926,-1.02803055765,2.61162411257,2.51944675355,-0.183640411496,0.785416086843,0.938013728044,0.745747229569,0.91765112058,0.953321491535,0.851645473703,0.887004109234,1.35258289368,1.26683449803,1.00199058211,1.1059256916,1.68848018571,0.828111124457,0.921979677149,0.54946123301,0.812767938629,0.688961258467,0.853217973635,0.790600818522,-0.799097921033,-0.332358095412,0.494679276023,0.538174769695,0.647704983603,0.639316266978,0.699292124201,1.51171913314,1.44081497988,1.4378837985,1.51280429182,-1.29574810223,-1.40235641562,-1.2564811474,-1.2135956703,-1.41879003991,-1.47983994021,-1.28305324779,-1.32355272439,-1.15243686813,-1.25589006468,-1.03132456721,-1.12551247906,-1.18749235102,-1.15468583189,-1.06751486314,-1.00669449875,-1.4776915364,-1.47408608304,-1.43144992941,-1.56955036747,-1.55661597895,4.67829870334,4.68590736067,-1.48200887803,4.64000843117,4.70012963471,-1.48174869689,-1.42822493012,-0.364990931853,-0.203997801907,-0.784532119441,-0.79393672128,-0.703399407161,0.125067504785,0.226095779137,-0.241004209566,-0.0776268857319,0.0317820872515,-0.514971655334,-0.859097653855,-0.0496970383681,-0.0287273183247,-0.23139004552,-0.269972881554,0.865666866575,1.29627344813,0.847947098807,1.0972075252,0.427010593875,0.18832939196,0.129824093225,0.247751482274,3.71388235007,3.79583681879,3.7977565754,3.75643233721,3.7734549519,3.66573337916,3.69892117491,4.33938809431,4.24812471116,4.08254772268,4.1689886613,4.08398275692,4.17668783511,4.40604401091,4.34687149041,4.27352992371,4.38997071453,-1.31257922758,-1.23505746211,-1.36531315405,-1.34091687837,-1.21179045082,-1.26464091263,-1.0436533569,-1.10171220246,-1.29652121345,-1.15363385081,-1.17111815744,-1.24352142698,3.89097572596,3.99732516655,3.99119500178,3.91752509421,3.82747474927,3.80772520598,3.87296419576,3.75557511753,3.85809466337,3.90573369563,2.90921354981,2.71068744568,2.28423586887,2.46449769343,2.81005049064,0.776152322144,0.976621245401,0.987086068527,0.88136071656,0.739994357822,0.62772580838,0.483005637999,0.416796190754,0.708511711488,0.522818529101,0.298842528355,0.372866578856,0.387928677724,0.330549036786,0.437936297034,0.513929583719,0.448434771919,2.4938985958,2.51549847202,2.63139505741,2.76143974986,2.71061573937,2.97759908146,3.1044928108,3.15035809016,2.87681490497,2.88755638988,3.03911638654,1.67993360002,1.75804432543,1.7713206344,1.80027459565,1.97718901413,1.83499247361,1.80741668458,1.87530455804,1.91585127853,1.87717736442,1.91097564332,1.98730835213,1.98972514815,2.02715741506,2.15124400801,2.1049509619,2.02327577729,1.98185487549,2.03388696986,2.11480854344,1.98196422208,2.02648869573,2.26032911071,2.32240383248,2.16847338744,2.1330994398,1.98904285335,1.8851086467,1.85202393118,1.95047171072,2.03654582449,2.20647539543,2.1399963432,2.33057685177,2.36062148027,2.28871133313,2.19027056733,3.53867412314,2.95834339661,3.01996939112,2.96567108544,3.03629550887,3.09146270186,3.10035153545,3.57176948949,3.62495600779,3.5945701296,3.66700131629,3.70096191419,3.71991028954,3.1832311062,3.11714840127,3.12932529302,3.20966113297,3.45316652754,3.52231148588,3.57611666187,3.4398482444,3.35575590228,3.28340835756,3.22425781468,3.23682940005,3.32888166396,3.43566349173,3.23925550481,3.31336671696,3.33006728648,3.83104804558,3.78532516967,3.73360632799,3.75538987078,3.92523987411,3.99025009035,3.93276564362,4.00082664126,4.0726909949,4.06952594896,3.47741573745,3.54792047379,3.43489692886,3.57141925583,3.4582000026,3.52461624621,3.77778990157,3.7213314469,3.65349144931,3.48448035265,3.49496203967,3.60115075955,3.53857519174,3.61408193648,3.56220220405,3.14943539986,3.10929675115,3.04221755294,3.06869143659,3.0243656209,-0.873916286467,-0.829348903862,-0.871524847962,-1.04730660541,-1.19388918885,-1.14278807967,-1.07320996866,0.0893962876491,0.219655468686,0.148924848526,-0.0311831623967,-0.0417072612364,-0.174857677244,-0.112985649415,0.315203106504,0.248484992273,0.237158339118,0.371584255308,0.29415990887,0.362000313783,-0.931140183985,-0.855738620967,-0.797374628897,-0.920080717151,-0.961387230785,-0.311496222682,-0.387223558149,-0.45671508091,-0.445627900742,-0.348207002254,-0.0295276506541,0.0419812734123,-0.383793060553,-0.526062210403,-0.453835977636,-0.524415785998,-0.23271608136,-0.14887473599,-0.101176117165,-0.0839300300074,-0.301768031461,-0.291929875939,-0.369292446587,-0.450919835996,-0.451810308187,-0.380049458763,-0.186214452813,-0.177337516807,-0.243433544334,-0.316378951866,-0.319839383202,-0.255848705771,0.0410470773476,-0.357219109796,-0.366322441342,-0.30735913151,-0.287789692371,-0.735437059042,-0.826834940872,-0.971470674378,-1.02805125306,-0.978285059115,-0.865374594899,-0.816590914905,-0.871938502908,-1.44892352172,4.14349028759,4.32939524035,4.63245231378,4.69416442464,-1.52215486002,-0.902808796032,-0.790353267127,-0.715847104609,-0.720842741145,-0.835824621778,-0.780299181461,-0.842192306642,-0.791865285743,-1.08305672269,-1.06827055266,-0.81437034077,-0.827664423008,4.39638986738,4.37981953364,4.49858144483,4.43640582575,4.63926875871,4.59298422379,4.51688550693,4.47133095044,4.44352046419,4.4298297871,4.48852914218,4.51718174971,4.30796122428,4.32001667205,4.19529074096,4.26289837487,4.22575877911,4.15520000598,4.10251677784,4.11755111727,4.18574444789,4.24121402229,4.2300939203,4.15977617476,4.23962187165,4.18207930522,4.14691417086,4.20914478965,4.05286909147,4.07133529469,-1.29817065359,-1.35407892911,3.98688897515,4.01095078851,2.88686165457,2.87954496948,2.80929670179,2.74611635607,2.63129645238,2.61889432817,2.23398025035,2.28872116687,2.34306663368,2.26522841561,2.37503971833,2.39764933094,2.10303001,2.21226025003,2.13952876017,2.61498036398,2.54062798254,2.49494009849,2.53369117252,2.74646681417,2.68275465005,2.62711692013,2.66222544839,2.13120717658,2.08618464103,2.02655163156,2.10067404757,2.34106431039,2.20085033461,2.31391418509,2.24284637381,2.22671353854,2.29607073571,3.31461940539,3.27578913655,3.15179677995,3.25169072828,3.12556921873,3.18567494322,3.40984993629,3.60255008609,2.45041965486,2.36739777042,2.37149685947,2.32788457408,2.33247580623,2.25302254251,2.32627468847,2.3695404843,2.99053235255,2.978759244,2.84909411763,2.90592107892,2.94685945577,2.89247154909,2.86331251959,2.93208946228,2.80192994648,2.81628926128,2.35008764072,2.23551875198,2.25686190305,2.19785570649,3.00171766344,3.09911801137,2.98424167167,3.07776797206,2.6800438418,2.77317421579,2.89503973641,2.89108166864,2.38866802478,2.49791613322,2.39251420113,2.33796651453,3.63351782679,3.61373564144,3.55612905978,3.45477994075,3.3948992111,3.44306117614,3.56278361519,3.7623053044,3.86310421473,3.721253725,3.7593202056,3.90717919391,3.79646424394,3.93222223594,3.86064305931,3.59124657919,3.6675195782,3.02257643631,1.38976905596,1.45321088729,1.52293853573,1.52223174779,1.57209551131,1.58143749752,1.65074231636,1.69969035634,1.51168715206,0.94973393627,0.947608412391,0.885653198278,0.880626962086,0.732312738026,0.960759574355,0.699749015543,0.879345397477,0.7862532263,0.881193106625,0.70954585987,0.794753654236,1.46270945627,1.53612318007,1.55933674742,1.27801323164,1.33612110367,1.40745718139,1.31956245154,1.51911251564,1.37492118231,1.21803084595,1.06361300667,1.04975408782,1.12610575574,1.16501853782,1.24231082721,0.504848843992,0.352120070051,0.408651755945,0.477098146816,1.06740804378,1.00950988141,1.01345575591,1.07526539841,0.928253023362,0.8247181568,0.852009383583,3.93964080223,3.97827412514,3.67432745603,3.75351885807,-1.55497895939,4.5887931331,4.19429284021,4.24505628154,-0.932343070648,-0.873340413466,-0.626445695776,-0.802209613386,-0.522523333407,-0.623447229057,-0.480481681792,-0.21094911804,-0.32941438755,-0.631745503904,2.69493821707,2.5687855688,3.21838399509,1.17451083797,2.50361084661,2.22887727069,1.40045686278,1.25627170601,1.12357231771,1.10316953747,1.25346271343,1.22125683901,1.41150655242,1.50706396924,1.32468980144,1.33458544775,1.07874171325,1.09163738661,1.20570960227,1.29636923973,1.22130662056,1.08392519485,1.15820326906,1.13644569749,0.987914411318,1.03103512475,0.95729632106,2.13663164757,2.11198049078,1.81597254579,1.67212487014,-0.64345807321,-0.55472142577,-0.852916553622,-1.00787086205,-0.984462591211,-0.86349137823,-0.780204566391,-0.0147995823451,0.158814539327,0.523948855431,-0.256309279004,1.63639019264,1.63293471325,1.69126397737,1.70380019549,1.76704497199,1.75802012525,1.64635813541,1.79141305512,1.78236697508,1.7114301888,1.51144311207,1.57701491309,1.44573963462,1.57473136902,1.51089709929,1.44743140381,4.61639948096,4.66845525692,4.46377855776,4.49718002045,4.41150681595,4.2938605949,4.27658115165,4.3597373599,4.19323302993,4.08301372178,3.97149652212,3.95634214594,-0.595160727044,-0.669162385987,-0.454349583742,-0.466252959326,-0.537950991619,-0.626255348908,0.2009120258,0.259539935293,0.37837768551,0.264899729228,0.397304840028,0.44910638315,-0.389970133458,-0.262660440735,-0.374516574124,-0.0102063294203,0.0427765747676,0.0782001321385,-0.142574245876,0.00601955178813,0.421684421902,0.714312824246,0.424519998694,-1.4324860522,-1.32771859965,-0.627145434822,-0.756046804808,-0.741717642468,-0.514681272417,-0.578891727481,1.03473948486,1.28300408705,1.3468590693,1.22407648854,1.01545556984,1.13496108751,-0.90185931164,-0.964078182108,-0.82184505929,-0.807894704542,-1.13617781088,-1.08085431592,-1.11686876563,-1.20647720344,-1.19010262581,-1.32749245856,-1.27713665245,-1.30900853312,-1.24211553436,3.72100100052,3.82130232931,3.7812228692,3.58370819907,3.36593445775,3.52443037951,3.61686657209,2.85557666918,3.57012185993,3.2968947531,2.01297744112,2.0015212262,2.40050883426,2.9972613678,3.0902902311,3.27204738528,0.260346543239,0.201908952499,0.172136819246,0.244035676406,0.126077848959,0.112855706669,0.487452174627,0.495541317707,2.20219622953,2.17953960227,2.10843242252,2.14876802878,2.07113027778,2.05371738279,3.27572726885,3.26190959923,3.35427252348,3.41582931237,3.48796530522,3.51963616924,3.60810629216,3.62359173518,3.55161920218,3.29083900722,3.20070005731,3.31908020993,3.34728809064,3.2390539726,3.17816391503,3.4195917698,3.46100147795,3.55347782442,3.46951573604,3.5553384591,3.59718805818,3.28714513112,3.27137055758,3.22966873543,3.15938958136,3.11481853341,3.18343347501,3.0597577597,3.07377433276,3.14451139293,3.19898152722,3.94374922252,3.8773390351,3.99070690146,3.97586767749,3.90473101067,3.85346910171,3.77871511958,3.69756726033,3.80713700381,3.75601924226,3.64484088241,3.67428167995,2.9539216358,3.02162670288,3.00131703823,0.0411505772022,0.101175104279,-0.0774673712606,-0.155487577127,-0.368553405706,-0.301292276833,-0.302456080283,-0.367218884543,-0.450700628157,-0.470575799021,-0.577381318011,-0.499485099873,-0.616577408112,-0.558475645912,-0.511845033079,-0.582388513259,-0.712599102516,-0.677133667205,-0.227530574211,-0.237973915952,0.0702937261669,0.0890230343523,0.0318117366306,4.66846121645,4.67654616754,4.58288683413,4.50156634137,-1.42910948176,-1.49121782118,-1.509292626,-1.2272449256,-1.45287493696,-1.40154408397,-1.35929010452,-1.46448593581,4.66573759983,4.60892211461,4.6949603182,4.70887464996,4.23528119277,4.15405212626,4.03561562792,4.05806423923,4.11900372088,4.22281195226,4.6369316257,4.64771992392,4.56880173025,4.57223354759,-1.49135467806,-1.5671523463,4.70875811471,-1.42918185607,-1.44250007881,-1.51214207874,-0.915798988534,-0.942260394595,-0.87180457352,-0.858904011497,-0.985319562152,-0.998784536973,-0.744584541458,-0.733527863488,-0.608655607761,-0.666166789419,-0.649364403764,-0.769366530182,-0.721060927646,-0.78214878428,4.61958277999,4.55207763088,4.56680937658,4.64285962985,4.67666300812,4.11395759846,4.05764008838,3.98891817812,4.10132440831,-0.989967425925,-0.957689165588,-1.17185140731,-1.2226500105,-1.09452991764,-1.06747904876,-1.33533305942,-1.3981606359,-1.37924546645,4.1161511768,4.09849262968,4.19710717493,4.17008839269,4.26650696161,4.25745072654,2.75560005241,2.69614186442,2.82622437544,2.8335558603,2.76752076286,2.70084956396,2.13936793481,2.21307655319,2.25279665987,2.21684274982,2.13729671994,2.09974753088,2.7977103375,2.88293814215,2.93760338537,2.76954333477,2.8350477162,2.91781929627,1.98660672723,2.01553578759,1.96144874951,1.89202520336,1.90912923293,2.44427762411,2.49698385152,2.56981019029,2.56178750184,2.48365788546,1.88576574968,2.54395044806,2.64178729155,2.54090107085,2.49064363348,3.73809753769,3.68564439992,3.83045817427,3.86074276062,3.80300017561,3.7194923923,3.14013835617,3.16649156593,3.24224668426,3.37012259288,1.31848945548,1.38393359812,1.44983096898,1.44859744627,1.38427649309,1.32124521838,1.3228348912,1.3871501703,0.744246403886,0.735827938597,0.600064209507,0.538323724042,0.670066301537,0.675459244658,0.54668018102,0.613910751645,0.658878637532,0.507259707381,0.519905221714,0.592456852002,0.637860377623,0.653202234186,0.563615028426,0.574025395289,0.877978892483,0.874043457703,0.809245555403,0.806257839053,1.0106238182,0.943657214767,0.942710140398,0.801962119415,0.873297264978,1.52899897525,1.50445936044,1.46900807111,1.42563664477,1.29032650816,1.23905523893,1.22744545292,1.32064301333,1.38529397654,1.3668302057,1.71674634103,1.58651785039,1.75512268109,1.3455775614,1.39066392293,1.51659087871,0.465076084668,0.450659263448,0.366197270177,1.13244562279,1.25895944757,1.19788578754,1.13623401592,1.19164220024,1.25661671372,1.14224548684,0.0239781162741,0.45448097974,0.216359154494,0.0858884601664,2.64803757601,2.46120909621,2.08445691954,2.40161733295,2.02161872323,2.17148109297,2.06602095188,1.99110093928,2.39142148848,2.33087594291,2.32181108082,2.18498050931,2.21443683738,2.14104115218,3.25922149368,3.35309596791,3.10896124271,2.79024033499,3.03601741832,2.82585434496,1.84873166455,1.81369347188,1.69820230009,1.60304591923,1.80080803545,1.31615015709,1.38367517023,1.6440666017,1.59777339107,1.51159403373,1.59496456995,1.51343933926,1.43083816753,1.4264540289,1.96655219168,1.93166416542,2.01077348039,2.02976977244,1.86794042683,1.8836073242,-0.47844089008,-0.598310545222,-0.453529684208,-0.535199250422,-0.678893698822,-0.642404263683,0.450068924717,0.281311833234,0.230613627249,0.528071564573,0.456457396268,0.321710607899,0.467783918478,0.378705146279,0.482066731217,0.529701191525,-0.384766564051,-0.266818252516,-0.299064297547,-0.412668254677,0.276554077544,0.312468692375,0.352191888194,1.65259910229,1.58760284532,1.58355470982,1.72669889779,1.66529423725,1.73537920384,4.44473473768,4.64405934806,4.58482867205,-0.270130917236,-0.163097903373,-0.273352219729,-0.189899816775,-0.0902768168966,-0.0677744153504,-0.4511389988,-0.345725725006,-1.17050316526,-1.15759252611,-0.532175781396,-0.641332360876,-0.625778834415,-0.903105934498,-0.831157957069,-1.02012880904,-1.06542849017,-0.855470099794,-0.870216502126,-0.946471994039,-1.00495304291,3.18080736276,3.16586980735,3.47725010151,3.3372580041,3.63477844579,3.53014014793,0.621789534303,0.638919204901,0.570513537266,0.562761430185,3.46040820854,3.4016794987,3.44516268546,3.37075618469,3.31110372133,3.32678875268,-0.0135586615153,0.0517652598129,0.125602075505,0.140482614396,-0.0054408852545,0.0751364640118,-0.156536557824,-0.155881613433,-0.0719929255661,-0.310308921406,-0.234879815041,-0.240781554533,-0.333794095312,-0.398072520335,-0.415102105002,-0.0860340563008,-0.092979634873,-0.155751063764,-0.226072042445,-0.16546475819,-0.22894675777,0.2486923197,0.400955189181,0.342299781067,0.223326284778,-0.144074643224,-0.155530500961,-0.0935411895492,-0.0722355822052,-0.0111124873352,-0.0208866840842,-0.10494008234,-0.0416190385953,-0.18630700687,-0.177400871782,-0.0427003302502,0.00214885637961,-0.0523219337062,-0.12349109155,-0.124521920636,-1.38282168112,-1.35799606198,-1.16152883297,-1.24069677208,4.58839683513,4.36386964028,4.58732921793,4.68838631077,4.31122078684,4.42356303076,4.41150904662,4.49904190249,4.58860929436,4.58840638681,3.99486597046,4.12747214751,4.00298632858,4.11124519394,-0.687120258627,-0.639688357858,-0.698537342118,-0.570405881536,-0.560688014887,-0.618513595472,4.03059096618,4.01369289575,3.90418077469,3.97466681365,3.88421282647,3.9375344346,-0.964346059059,-1.00506760158,-1.01587440413,-1.25574298684,-1.19922398425,-1.1211381977,-1.09164943906,1.86565758451,1.77090420841,1.75750783184,1.83107608401,2.07517446342,2.10280401357,2.03602994484,1.94026014483,1.98688745268,1.9192993707,2.6441702932,2.8089623123,2.71087886089,2.69113998342,2.85015231232,2.79900952229,3.33123699837,3.67312933474,3.5757707123,3.42783456007,3.60528704725,3.4009611331,1.07580130334,1.20847164088,1.13972517439,1.20345438571,0.95468219346,0.724890728754,0.80031962414,0.720214551979,0.874018641118,0.796519117381,0.875019042206,1.18362283172,1.17132425076,1.11236383913,1.02961316491,1.56981255604,1.67977340828,1.68208433221,0.396194460186,0.282416038535,0.42030846813,0.367195799124,0.852635335599,0.878913890979,1.15758608382,1.18331418309,1.24387428081,1.23391819364,0.981554220372,1.09615243281,0.959918804181,1.01778950114,1.05523427364,1.11176401829,2.43318851517,2.3232918836,2.06870138515,2.31486005264,2.01275290141,2.13507121453,0.963795015983,1.35324472523,1.20559454229,0.914917274346,0.525372474674,0.631210804037,1.82017753667,1.66624234234,1.4429253752,1.48360769793,1.49827180904,1.60448501535,1.54186675026,-0.158659094231,0.0903053910204,-0.027516633673,-0.161439138973,4.33745991515,4.06900110655,4.07641697586,4.20224154985,-0.914407778921,-0.956543631298,-0.899478949794,-1.02797943357,-1.04414155398,-0.987754783672,0.68795649576,0.748789397178,0.761052270978,0.697246980175,0.81646035,0.819363899493,-0.0585387275581,0.158382054824,0.0661744897213,-0.0500744722343,0.0163775501669,0.0910765144051,0.182404734197,0.208699405128,0.131977968258,0.0301265155833,-1.30373994372,-1.33181455406,-1.13250076613,-1.19866555776,4.48886992694,4.49322183812,4.30477552594,4.40088473566,4.39191149448,4.26087958373,4.23837525287,1.1462132695,1.07715311306,1.15271700107,1.0110848546,1.41320036571,1.51398177223,1.38193686056,1.45069564479,1.54785543802,1.57910515662,0.173764345286,0.232532880427,0.187406454354,0.261893327684,0.306363668498,0.321868800495,0.996487395094,1.03810026685,1.05926500592,0.949673022159,0.886676681479,0.91194222197,1.81452024126,1.69756191336,1.94948511388,1.9321525938,1.81587142747,1.71416709763,4.22465435786,4.36599130616,4.45602278411,4.23933386343,4.60343014773,4.53668425745,-1.14418581263,-1.10997527039,-1.16857682587,-1.26375274179,-1.29102567062,-1.23073380244,1.02280377336,1.01352051793,1.09300226759,1.08619770409,0.944428132969,0.946805731377\n    \n};\n\n\n"
  },
  {
    "path": "FEBioMix/FEActiveConstantSupply.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEActiveConstantSupply.h\"\n#include \"FEBioMech/FEElasticMaterial.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEActiveConstantSupply, FEActiveMomentumSupply)\n    ADD_PARAMETER(m_asupp, \"supply\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEActiveConstantSupply::FEActiveConstantSupply(FEModel* pfem) : FEActiveMomentumSupply(pfem)\n{\n    m_asupp = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Active momentum supply vector.\n//! The momentum supply is oriented along the first material axis\nvec3d FEActiveConstantSupply::ActiveSupply(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n    // active momentum supply vector direction\n    vec3d V(Q[0][0], Q[1][0], Q[2][0]);\n    \n    mat3d F = et.m_F;\n    vec3d pw = (F*V)*m_asupp;\n\n    return pw;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\nvec3d FEActiveConstantSupply::Tangent_ActiveSupply_Strain(FEMaterialPoint &mp)\n{\n    return vec3d(0,0,0);\n}\n"
  },
  {
    "path": "FEBioMix/FEActiveConstantSupply.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEActiveMomentumSupply.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a constant active momentum supply\n\nclass FEBIOMIX_API FEActiveConstantSupply :\tpublic FEActiveMomentumSupply\n{\npublic:\n    //! constructor\n    FEActiveConstantSupply(FEModel* pfem);\n    \n    //! active momentum supply\n    vec3d ActiveSupply(FEMaterialPoint& pt) override;\n    \n    //! Tangent of active momentum supply\n    vec3d Tangent_ActiveSupply_Strain(FEMaterialPoint& mp) override;\n        \npublic:\n    double\tm_asupp;\t\t\t//!< active momentum supply\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEActiveMomentumSupply.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEActiveMomentumSupply.h\"\n\n//-----------------------------------------------------------------------------\n// Derivative of active momentum supply w.r.t. solute concentration at material point\n// Set this to zero by default because biphasic problems do not require it\nvec3d FEActiveMomentumSupply::Tangent_ActiveSupply_Concentration(FEMaterialPoint& pt, const int isol)\n{\n    return vec3d(0,0,0);\n}\n\n"
  },
  {
    "path": "FEBioMix/FEActiveMomentumSupply.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for active momentum supply.\n//! These materials need to define the momentum supply and its tangents.\n//!\nclass FEBIOMIX_API FEActiveMomentumSupply : public FEMaterialProperty\n{\n    FECORE_BASE_CLASS(FEActiveMomentumSupply)\n\npublic:\n    FEActiveMomentumSupply(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    virtual ~FEActiveMomentumSupply(){}\n    \n    //! active momentum supply\n    virtual vec3d ActiveSupply(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of active momentum supply with respect to strain\n    virtual vec3d Tangent_ActiveSupply_Strain(FEMaterialPoint& mp) = 0;\n    \n    //! tangent of hydraulic permeability with respect to concentration\n    vec3d Tangent_ActiveSupply_Concentration(FEMaterialPoint& mp, const int isol);\n};\n"
  },
  {
    "path": "FEBioMix/FEBioMix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMix.h\"\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FEMultiphasicStandard.h\"\n#include \"FEMultiphasicMultigeneration.h\"\n#include \"FESolute.h\"\n#include \"FETriphasic.h\"\n#include \"FEDiffConstIso.h\"\n#include \"FEDiffConstOrtho.h\"\n#include \"FEDiffRefIso.h\"\n#include \"FEDiffAlbroIso.h\"\n#include \"FEPermConstIso.h\"\n#include \"FEPermHolmesMow.h\"\n#include \"FEPermExpIso.h\"\n#include \"FEPermRefIso.h\"\n#include \"FEPermRefOrtho.h\"\n#include \"FEPermRefTransIso.h\"\n#include \"FEOsmCoefConst.h\"\n#include \"FEOsmCoefManning.h\"\n#include \"FESFDSBM.h\"\n#include \"FEFiberExpPowSBM.h\"\n#include \"FEFiberPowLinearSBM.h\"\n#include \"FESolventSupplyStarling.h\"\n#include \"FESolubConst.h\"\n#include \"FESolubManning.h\"\n#include \"FESupplyBinding.h\"\n#include \"FESupplyConst.h\"\n#include \"FESupplySynthesisBinding.h\"\n#include \"FESupplyMichaelisMenten.h\"\n#include \"FECarterHayes.h\"\n#include \"FEReactionRateConst.h\"\n#include \"FEReactionRateHuiskes.h\"\n#include \"FEReactionRateRuberti.h\"\n#include \"FEReactionRateNims.h\"\n#include \"FEReactionRateExpSED.h\"\n#include \"FEReactionRateSoluteAsSBM.h\"\n#include \"FEMembraneReactionRateConst.h\"\n#include \"FEMembraneReactionRateIonChannel.h\"\n#include \"FEMembraneReactionRateVoltageGated.h\"\n#include \"FEMassActionForward.h\"\n#include \"FEMassActionForwardEffective.h\"\n#include \"FEMichaelisMenten.h\"\n#include \"FEMassActionReversible.h\"\n#include \"FEMassActionReversibleEffective.h\"\n#include \"FEConcentrationIndependentReaction.h\"\n#include \"FEMembraneMassActionForward.h\"\n#include \"FEMembraneMassActionReversible.h\"\n#include \"FEActiveConstantSupply.h\"\n#include \"FEPorousNeoHookean.h\"\n\n#include \"FEMixtureNormalTraction.h\"\n#include \"FEFluidFlux.h\"\n#include \"FESoluteFlux.h\"\n#include \"FESoluteNaturalFlux.h\"\n#include \"FEPressureStabilization.h\"\n#include \"FEMatchingOsmoticCoefficientLoad.h\"\n#include \"FEMatchingOsmoticCoefficientBC.h\"\n#include \"FEMultiphasicFluidPressureLoad.h\"\n\n#include \"FESlidingInterface2.h\"\n#include \"FESlidingInterfaceBiphasic.h\"\n#include \"FESlidingInterfaceBiphasicMixed.h\"\n#include \"FESlidingInterface3.h\"\n#include \"FESlidingInterfaceMP.h\"\n#include \"FETiedBiphasicInterface.h\"\n#include \"FETiedMultiphasicInterface.h\"\n\n#include \"FEBiphasicSolver.h\"\n#include \"FEBiphasicSoluteSolver.h\"\n#include \"FEMultiphasicSolver.h\"\n\n#include \"FEBioMixPlot.h\"\n#include \"FEBioMixData.h\"\n\n#include \"FEMixDomainFactory.h\"\n#include \"FEBiphasicSolidDomain.h\"\n#include \"FEBiphasicShellDomain.h\"\n#include \"FEBiphasicSoluteSolidDomain.h\"\n#include \"FEBiphasicSoluteShellDomain.h\"\n#include \"FETriphasicDomain.h\"\n#include \"FEMultiphasicSolidDomain.h\"\n#include \"FEMultiphasicShellDomain.h\"\n\n#include \"FESBMPointSource.h\"\n#include \"FESolutePointSource.h\"\n\n#include \"FEFixedFluidPressure.h\"\n#include \"FEPrescribedNodalFluidPressure.h\"\n#include \"FEFixedConcentration.h\"\n#include \"FEPrescribedConcentration.h\"\n#include \"FEMultiphasicFluidPressureBC.h\"\n\n#include \"FEInitialEffectiveFluidPressure.h\"\n#include \"FEInitialConcentration.h\"\n#include \"FENodalFluidFlux.h\"\n\n#include \"FEBiphasicModule.h\"\n#include \"FEBiphasicAnalysis.h\"\n#include \"FEBiphasicSoluteAnalysis.h\"\n#include \"FEMultiphasicAnalysis.h\"\n#include <FECore/FETimeStepController.h>\n\n//-----------------------------------------------------------------------------\nconst char* FEBioMix::GetVariableName(FEBioMix::FEBIOMIX_VARIABLE var)\n{\n\tswitch (var)\n\t{\n\tcase FLUID_PRESSURE: return \"fluid pressure\"; break;\n\t}\n\n\tassert(false);\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization of the FEBioMix module. This function registers all the classes\n//! in this module with the FEBio framework.\nvoid FEBioMix::InitModule()\n{\n//-----------------------------------------------------------------------------\n// Domain factory\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tfebio.RegisterDomain(new FEMixDomainFactory);\n\n\t// extensions to \"solid\" module\n\tfebio.SetActiveModule(\"solid\");\n\tREGISTER_FECORE_CLASS(FECarterHayes      , \"Carter-Hayes\");\n\tREGISTER_FECORE_CLASS(FEPorousNeoHookean , \"porous neo-Hookean\");\n    REGISTER_FECORE_CLASS(FEFiberExpPowSBM   , \"fiber-exp-pow sbm\" );\n    REGISTER_FECORE_CLASS(FEFiberPowLinearSBM, \"fiber-pow-linear sbm\");\n\tREGISTER_FECORE_CLASS(FEMixtureNormalTraction, \"normal_traction\");\n\n//======================================================================\n// setup the \"biphasic\" module\n\tfebio.CreateModule(new FEBiphasicModule, \"biphasic\", \n\t\t\"{\"\n\t\t\"   \\\"title\\\" : \\\"Biphasic Analysis\\\",\"\n\t\t\"   \\\"info\\\"  : \\\"Transient or quasi-static biphasic analysis.\\\"\"\n\t\t\"}\");\n\n\tfebio.AddModuleDependency(\"solid\");\n\n\t//-----------------------------------------------------------------------------\n\t// analyis classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FEBiphasicAnalysis, \"biphasic\");\n\n\t//-----------------------------------------------------------------------------\n\t// solver classes\n\tREGISTER_FECORE_CLASS(FEBiphasicSolver      , \"biphasic\");\n\n\t//-----------------------------------------------------------------------------\n\t// Domain classes\n\tREGISTER_FECORE_CLASS(FEBiphasicSolidDomain, \"biphasic-solid\");\n\tREGISTER_FECORE_CLASS(FEBiphasicShellDomain, \"biphasic-shell\");\n\n\t//-----------------------------------------------------------------------------\n\t// Materials\n\tREGISTER_FECORE_CLASS(FEBiphasic             , \"biphasic\");\n\tREGISTER_FECORE_CLASS(FEPermConstIso         , \"perm-const-iso\");\n\tREGISTER_FECORE_CLASS(FEPermHolmesMow        , \"perm-Holmes-Mow\");\n\tREGISTER_FECORE_CLASS(FEPermExpIso           , \"perm-exp-iso\");\n\tREGISTER_FECORE_CLASS(FEPermRefIso           , \"perm-ref-iso\");\n\tREGISTER_FECORE_CLASS(FEPermRefOrtho         , \"perm-ref-ortho\");\n\tREGISTER_FECORE_CLASS(FEPermRefTransIso      , \"perm-ref-trans-iso\");\n\tREGISTER_FECORE_CLASS(FESolventSupplyStarling, \"Starling\");\n\tREGISTER_FECORE_CLASS(FEActiveConstantSupply , \"active-const-supply\");\n\n\t//-----------------------------------------------------------------------------\n\t// Boundary conditions\n\tREGISTER_FECORE_CLASS(FEFixedFluidPressure          , \"zero fluid pressure\");\n\tREGISTER_FECORE_CLASS(FEPrescribedNodalFluidPressure, \"prescribed fluid pressure\");\n\tREGISTER_FECORE_CLASS(FEPrescribedShellFluidPressure, \"prescribed shell fluid pressure\");\n\n\t//-----------------------------------------------------------------------------\n\t// Initial conditions\n\tREGISTER_FECORE_CLASS(FEInitialEffectiveFluidPressure, \"initial fluid pressure\");\n\tREGISTER_FECORE_CLASS(FEInitialShellEffectiveFluidPressure, \"initial shell fluid pressure\");\n\tREGISTER_FECORE_CLASS(FEInitialConcentration     , \"initial concentration\");\n\tREGISTER_FECORE_CLASS(FEInitialShellConcentration, \"initial shell concentration\");\n\n\t//-----------------------------------------------------------------------------\n\t// Nodal loads\n\tREGISTER_FECORE_CLASS(FENodalFluidFlux, \"nodal fluidflux\");\n\n\t//-----------------------------------------------------------------------------\n\t// Surface loads\n\tREGISTER_FECORE_CLASS(FEFluidFlux         , \"fluidflux\");\n\tREGISTER_FECORE_CLASS(FEPressureStabilization, \"pressure_stabilization\");\n\n\t//-----------------------------------------------------------------------------\n\t// Contact interfaces\n\tREGISTER_FECORE_CLASS(FESlidingInterface2            , \"sliding2\");\n\tREGISTER_FECORE_CLASS(FESlidingInterfaceBiphasic     , \"sliding-biphasic\");\n\tREGISTER_FECORE_CLASS(FESlidingInterfaceBiphasicMixed, \"sliding-biphasic-mixed\");\n\tREGISTER_FECORE_CLASS(FETiedBiphasicInterface        , \"tied-biphasic\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEPlotData\n    REGISTER_FECORE_CLASS(FEPlotMPSpecificStrainEnergy           , \"specific strain energy\");\n\tREGISTER_FECORE_CLASS(FEPlotEffectiveElasticity\t\t         , \"effective elasticity\"            );\n\tREGISTER_FECORE_CLASS(FEPlotEffectiveFluidPressure\t\t     , \"effective fluid pressure\"        );\n\tREGISTER_FECORE_CLASS(FEPlotEffectiveShellFluidPressure      , \"effective shell fluid pressure\"  );\n\tREGISTER_FECORE_CLASS(FEPlotActualFluidPressure              , \"fluid pressure\"                  );\n\tREGISTER_FECORE_CLASS(FEPlotFluidFlux                        , \"fluid flux\"                      );\n\tREGISTER_FECORE_CLASS(FEPlotNodalFluidFlux                   , \"nodal fluid flux\");\n    REGISTER_FECORE_CLASS(FEPlotContactGapMP                     , \"contact gap\"         );\n\tREGISTER_FECORE_CLASS(FEPlotPressureGap\t\t\t\t\t     , \"pressure gap\"        );\n\tREGISTER_FECORE_CLASS(FEPlotFluidForce                       , \"fluid force\"         );\n\tREGISTER_FECORE_CLASS(FEPlotFluidForce2                      , \"fluid force2\"        );\n\tREGISTER_FECORE_CLASS(FEPlotFluidLoadSupport                 , \"fluid load support\"  );\n\tREGISTER_FECORE_CLASS(FEPlotMixtureFluidFlowRate             , \"fluid flow rate\"     );\n\tREGISTER_FECORE_CLASS(FEPlotReferentialSolidVolumeFraction   , \"referential solid volume fraction\");\n\tREGISTER_FECORE_CLASS(FEPlotPorosity                         , \"porosity\"            );\n\tREGISTER_FECORE_CLASS(FEPlotPerm                             , \"permeability\"        );\n\tREGISTER_FECORE_CLASS(FEPlotSolidStress                      , \"solid stress\"        );\n\tREGISTER_FECORE_CLASS(FEPlotLocalFluidLoadSupport            , \"local fluid load support\"        );\n\tREGISTER_FECORE_CLASS(FEPlotEffectiveFrictionCoeff           , \"effective friction coefficient\"  );\n\n\t//-----------------------------------------------------------------------------\n\t// Element log data\n\tREGISTER_FECORE_CLASS(FELogElemFluidPressure         , \"p\");\n\tREGISTER_FECORE_CLASS(FELogElemFluidFluxX            , \"wx\");\n\tREGISTER_FECORE_CLASS(FELogElemFluidFluxY            , \"wy\");\n\tREGISTER_FECORE_CLASS(FELogElemFluidFluxZ            , \"wz\");\n\tREGISTER_FECORE_CLASS(FELogElemPorosity              , \"porosity\");\n\tREGISTER_FECORE_CLASS_T(FELogElemPermeability_T, 0     , \"Kpxx\");\n\tREGISTER_FECORE_CLASS_T(FELogElemPermeability_T, 1     , \"Kpyy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemPermeability_T, 2     , \"Kpzz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemPermeability_T, 3     , \"Kpxy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemPermeability_T, 4     , \"Kpyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemPermeability_T, 5     , \"Kpxz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSolidStress_T, 0, \"esxx\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSolidStress_T, 1, \"esyy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSolidStress_T, 2, \"eszz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSolidStress_T, 3, \"esxy\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSolidStress_T, 4, \"esyz\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSolidStress_T, 5, \"esxz\");\n\n//======================================================================\n// setup the \"solute\" module (i.e. biphasic-solute)\n\tfebio.CreateModule(new FEBiphasicSoluteModule, \"solute\",\n\t\t\"{\"\n\t\t\"   \\\"title\\\" : \\\"Biphasic Solute Analysis\\\",\"\n\t\t\"   \\\"info\\\"  : \\\"Transient or quasi-static biphasic analysis with a single solute.\\\"\"\n\t\t\"}\");\n\n\tfebio.AddModuleDependency(\"biphasic\");\n\n\t//-----------------------------------------------------------------------------\n\t// Global data classes\n\tREGISTER_FECORE_CLASS(FESoluteData, \"solute\");\n\n\t//-----------------------------------------------------------------------------\n\t// analyis classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FEBiphasicSoluteAnalysis, \"solute\");\n\n\t//-----------------------------------------------------------------------------\n\t// solver classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FEBiphasicSoluteSolver, \"solute\");\n\n\t//-----------------------------------------------------------------------------\n\t// Domain classes\n\tREGISTER_FECORE_CLASS(FEBiphasicSoluteSolidDomain, \"biphasic-solute-solid\");\n\tREGISTER_FECORE_CLASS(FEBiphasicSoluteShellDomain, \"biphasic-solute-shell\");\n\tREGISTER_FECORE_CLASS(FETriphasicDomain, \"triphasic-solid\");\n\n\t//-----------------------------------------------------------------------------\n\t// Materials\n\tREGISTER_FECORE_CLASS(FEBiphasicSolute        , \"biphasic-solute\");\n\tREGISTER_FECORE_CLASS(FESoluteMaterial        , \"solute\");\n\tREGISTER_FECORE_CLASS(FETriphasic             , \"triphasic\");\n\tREGISTER_FECORE_CLASS(FEDiffConstIso          , \"diff-const-iso\");\n\tREGISTER_FECORE_CLASS(FEDiffConstOrtho        , \"diff-const-ortho\");\n\tREGISTER_FECORE_CLASS(FEDiffRefIso            , \"diff-ref-iso\");\n\tREGISTER_FECORE_CLASS(FEDiffAlbroIso          , \"diff-Albro-iso\");\n\tREGISTER_FECORE_CLASS(FEOsmCoefConst          , \"osm-coef-const\");\n\tREGISTER_FECORE_CLASS(FEOsmCoefManning        , \"osm-coef-Manning\");\n\tREGISTER_FECORE_CLASS(FESolubConst            , \"solub-const\");\n\tREGISTER_FECORE_CLASS(FESolubManning          , \"solub-Manning\");\n\tREGISTER_FECORE_CLASS(FESupplyBinding         , \"supply-binding\");\n\tREGISTER_FECORE_CLASS(FESupplyConst           , \"supply-const\");\n\tREGISTER_FECORE_CLASS(FESupplySynthesisBinding, \"supply-synthesis-binding\");\n\tREGISTER_FECORE_CLASS(FESupplyMichaelisMenten , \"supply-Michaelis-Menten\");\n\n\t//-----------------------------------------------------------------------------\n\t// Surface loads\n\tREGISTER_FECORE_CLASS(FESoluteFlux, \"soluteflux\");\n    REGISTER_FECORE_CLASS(FESoluteNaturalFlux, \"solute natural flux\");\n    REGISTER_FECORE_CLASS(FEMultiphasicFluidPressureLoad, \"fluid pressure\", 0x0400); // Deprecated, use the BC version.\n\n\t//-----------------------------------------------------------------------------\n\t// boundary conditions\n\tREGISTER_FECORE_CLASS(FEFixedConcentration, \"zero concentration\");\n\tREGISTER_FECORE_CLASS(FEPrescribedConcentration, \"prescribed concentration\");\n    REGISTER_FECORE_CLASS(FEMultiphasicFluidPressureBC, \"actual fluid pressure\");\n\n\t//-----------------------------------------------------------------------------\n\t// Contact interfaces\n\tREGISTER_FECORE_CLASS(FESlidingInterface3, \"sliding-biphasic-solute\");\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEPlotData\n\tREGISTER_FECORE_CLASS(FEPlotEffectiveSoluteConcentration     , \"effective solute concentration\");\n\tREGISTER_FECORE_CLASS(FEPlotEffectiveShellSoluteConcentration, \"effective shell solute concentration\");\n\tREGISTER_FECORE_CLASS(FEPlotActualSoluteConcentration        , \"solute concentration\");\n    REGISTER_FECORE_CLASS(FEPlotConcentrationGap                 , \"concentration gap\"   );\n    REGISTER_FECORE_CLASS(FEPlotPartitionCoefficient             , \"partition coefficient\");\n\tREGISTER_FECORE_CLASS(FEPlotSoluteFlux\t\t                 , \"solute flux\"                     );\n    REGISTER_FECORE_CLASS(FEPlotSoluteVolumetricFlux             , \"solute volumetric flux\"          );\n\tREGISTER_FECORE_CLASS(FEPlotOsmolarity                       , \"osmolarity\");\n\tREGISTER_FECORE_CLASS(FEPlotCurrentDensity                   , \"current density\"     );\n\tREGISTER_FECORE_CLASS(FEPlotFixedChargeDensity               , \"fixed charge density\");\n\tREGISTER_FECORE_CLASS(FEPlotReferentialFixedChargeDensity    , \"referential fixed charge density\");\n\tREGISTER_FECORE_CLASS(FEPlotElectricPotential                , \"electric potential\"  );\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FELogNodeData\n\tREGISTER_FECORE_CLASS(FENodeConcentration, \"c\");\n    REGISTER_FECORE_CLASS(FENodeFluidPressure, \"pe\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 0, \"ce1\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 1, \"ce2\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 2, \"ce3\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 3, \"ce4\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 4, \"ce5\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 5, \"ce6\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 6, \"ce7\");\n    REGISTER_FECORE_CLASS_T(FENodeSoluteConcentration_T, 7, \"ce8\");\n\n\t//-----------------------------------------------------------------------------\n\t// Element log data\n\tREGISTER_FECORE_CLASS(FELogElemSoluteConcentration   , \"c\");\n\tREGISTER_FECORE_CLASS(FELogElemSoluteFluxX           , \"jx\");\n\tREGISTER_FECORE_CLASS(FELogElemSoluteFluxY           , \"jy\");\n\tREGISTER_FECORE_CLASS(FELogElemSoluteFluxZ           , \"jz\");\n\tREGISTER_FECORE_CLASS(FELogElemSoluteRefConcentration, \"crc\");\n\tREGISTER_FECORE_CLASS(FELogFixedChargeDensity        , \"cF\");\n\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 0, \"c1\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 1, \"c2\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 2, \"c3\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 3, \"c4\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 4, \"c5\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 5, \"c6\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 6, \"c7\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteConcentration_T, 7, \"c8\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 0, \"j1x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 0, \"j1y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 0, \"j1z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 1, \"j2x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 1, \"j2y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 1, \"j2z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 2, \"j3x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 2, \"j3y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 2, \"j3z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 3, \"j4x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 3, \"j4y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 3, \"j4z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 4, \"j5x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 4, \"j5y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 4, \"j5z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 5, \"j6x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 5, \"j6y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 5, \"j6z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 6, \"j7x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 6, \"j7y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 6, \"j7z\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxX_T, 7, \"j8x\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxY_T, 7, \"j8y\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSoluteFluxZ_T, 7, \"j8z\");\n\n//======================================================================\n// setup the \"multiphasic\" module\n\tfebio.CreateModule(new FEMultiphasicModule, \"multiphasic\",\n\t\t\"{\"\n\t\t\"   \\\"title\\\" : \\\"Multiphasic Analysis\\\",\"\n\t\t\"   \\\"info\\\"  : \\\"Transient or quasi-static analysis with solutes.\\\"\"\n\t\t\"}\");\n\n\tfebio.AddModuleDependency(\"solute\");\n\n\t//-----------------------------------------------------------------------------\n\t// Global data classes\n\tREGISTER_FECORE_CLASS(FESBMData, \"solid_bound\");\n\n\t//-----------------------------------------------------------------------------\n\t// analyis classes (default type must match module name!)\n\tREGISTER_FECORE_CLASS(FEMultiphasicAnalysis, \"multiphasic\");\n\n\t//-----------------------------------------------------------------------------\n\t// solver classes\n\tREGISTER_FECORE_CLASS(FEMultiphasicSolver   , \"multiphasic\");\n\n\t//-----------------------------------------------------------------------------\n\t// Domain classes\n\tREGISTER_FECORE_CLASS(FEMultiphasicSolidDomain, \"multiphasic-solid\");\n\tREGISTER_FECORE_CLASS(FEMultiphasicShellDomain, \"multiphasic-shell\");\n\n\t//-----------------------------------------------------------------------------\n\t// Materials\n\tREGISTER_FECORE_CLASS(FEMultiphasicStandard               , \"multiphasic\"       );\n\tREGISTER_FECORE_CLASS(FEMultiphasicMultigeneration        , \"multiphasic-multigeneration\");\n\tREGISTER_FECORE_CLASS(FESFDSBM                            , \"spherical fiber distribution sbm\");\n\tREGISTER_FECORE_CLASS(FEReactionRateConst\t\t    \t  , \"constant reaction rate\"    );\n\tREGISTER_FECORE_CLASS(FEReactionRateHuiskes\t\t    \t  , \"Huiskes reaction rate\"     );\n    REGISTER_FECORE_CLASS(FEReactionRateRuberti               , \"Ruberti reaction rate\"     );\n\tREGISTER_FECORE_CLASS(FEReactionRateNims\t\t    \t  , \"Nims reaction rate\"        );\n\tREGISTER_FECORE_CLASS(FEReactionRateExpSED                , \"exp-sed reaction rate\"     );\n    REGISTER_FECORE_CLASS(FEReactionRateSoluteAsSBM           , \"solute-as-sbm reaction rate\");\n\tREGISTER_FECORE_CLASS(FEMembraneReactionRateConst         , \"membrane constant reaction rate\");\n\tREGISTER_FECORE_CLASS(FEMembraneReactionRateIonChannel    , \"membrane ion channel reaction rate\");\n\tREGISTER_FECORE_CLASS(FEMembraneReactionRateVoltageGated  , \"membrane voltage-gated reaction rate\");\n\tREGISTER_FECORE_CLASS(FEMassActionForward\t\t    \t  , \"mass-action-forward\"       );\n\tREGISTER_FECORE_CLASS(FEMassActionForwardEffective\t\t  , \"mass-action-forward-effective\");\n\tREGISTER_FECORE_CLASS(FEMembraneMassActionForward         , \"membrane-mass-action-forward\");\n\tREGISTER_FECORE_CLASS(FEConcentrationIndependentReaction  , \"concentration-independent\");\n\tREGISTER_FECORE_CLASS(FEMassActionReversible              , \"mass-action-reversible\"   );\n\tREGISTER_FECORE_CLASS(FEMassActionReversibleEffective     , \"mass-action-reversible-effective\");\n\tREGISTER_FECORE_CLASS(FEMembraneMassActionReversible      , \"membrane-mass-action-reversible\");\n\tREGISTER_FECORE_CLASS(FEMichaelisMenten                   , \"Michaelis-Menten\"         );\n\tREGISTER_FECORE_CLASS(FESolidBoundMolecule                , \"solid_bound\"              );\n\n\tREGISTER_FECORE_CLASS(FEReactantSpeciesRef, \"vR\");\n\tREGISTER_FECORE_CLASS(FEProductSpeciesRef , \"vP\");\n\tREGISTER_FECORE_CLASS(FEInternalReactantSpeciesRef, \"vRi\");\n\tREGISTER_FECORE_CLASS(FEInternalProductSpeciesRef , \"vPi\");\n\tREGISTER_FECORE_CLASS(FEExternalReactantSpeciesRef, \"vRe\");\n\tREGISTER_FECORE_CLASS(FEExternalProductSpeciesRef , \"vPe\");\n    \n\t//-----------------------------------------------------------------------------\n\t// Surface loads\n\tREGISTER_FECORE_CLASS(FEMatchingOsmoticCoefficientLoad, \"matching_osm_coef\", 0x0300); // deprecated, use BC version\n\n\t//-----------------------------------------------------------------------------\n\t// Boundary conditions\n\tREGISTER_FECORE_CLASS(FEMatchingOsmoticCoefficientBC, \"matching_osm_coef\");\n\n\t//-----------------------------------------------------------------------------\n\t// Body loads\n\tREGISTER_FECORE_CLASS(FESBMPointSource   , \"sbm point source\");\n\tREGISTER_FECORE_CLASS(FESolutePointSource, \"solute point source\");\n\n\t//-----------------------------------------------------------------------------\n\t// Contact interfaces\n\tREGISTER_FECORE_CLASS(FESlidingInterfaceMP      , \"sliding-multiphasic\"    );\n\tREGISTER_FECORE_CLASS(FEAmbientConcentration    , \"ambient_concentration\"  );\n\tREGISTER_FECORE_CLASS(FETiedMultiphasicInterface, \"tied-multiphasic\"       );\n\n\t//-----------------------------------------------------------------------------\n\t// classes derived from FEPlotData\n\tREGISTER_FECORE_CLASS(FEPlotReceptorLigandConcentration      , \"receptor-ligand concentration\"   );\n\tREGISTER_FECORE_CLASS(FEPlotSBMConcentration                 , \"sbm concentration\"\t\t\t     );\n    REGISTER_FECORE_CLASS(FEPlotSBMArealConcentration            , \"sbm areal concentration\"         );\n\tREGISTER_FECORE_CLASS(FEPlotSBMRefAppDensity\t\t\t     , \"sbm referential apparent density\");\n\tREGISTER_FECORE_CLASS(FEPlotOsmoticCoefficient               , \"osmotic coefficient\"             );\n\n\t//-----------------------------------------------------------------------------\n\t// Element log data\n\tREGISTER_FECORE_CLASS(FELogElemElectricPotential     , \"psi\");\n\tREGISTER_FECORE_CLASS(FELogElemCurrentDensityX       , \"Iex\");\n\tREGISTER_FECORE_CLASS(FELogElemCurrentDensityY       , \"Iey\");\n\tREGISTER_FECORE_CLASS(FELogElemCurrentDensityZ       , \"Iez\");\n\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 0, \"sbm1\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 1, \"sbm2\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 2, \"sbm3\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 3, \"sbm4\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 4, \"sbm5\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 5, \"sbm6\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 6, \"sbm7\");\n\tREGISTER_FECORE_CLASS_T(FELogElemSBMConcentration_T, 7, \"sbm8\");\n\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 1, \"sbm1_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 2, \"sbm2_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 3, \"sbm3_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 4, \"sbm4_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 5, \"sbm5_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 6, \"sbm6_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 7, \"sbm7_referential_apparent_density\");\n\tREGISTER_FECORE_CLASS_T(FELogSBMRefAppDensity_T, 8, \"sbm8_referential_apparent_density\");\n\n\t//-----------------------------------------------------------------------------\n\t// domain log data\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 0, \"sbm1_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 1, \"sbm2_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 2, \"sbm3_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 3, \"sbm4_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 4, \"sbm5_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 5, \"sbm6_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 6, \"sbm7_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSBMConcentration_T, 7, \"sbm8_integral\");\n\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 0, \"c1_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 1, \"c2_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 2, \"c3_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 3, \"c4_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 4, \"c5_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 5, \"c6_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 6, \"c7_integral\");\n\tREGISTER_FECORE_CLASS_T(FELogDomainIntegralSoluteConcentration_T, 7, \"c8_integral\");\n\n\tfebio.SetActiveModule(0);\n}\n"
  },
  {
    "path": "FEBioMix/FEBioMix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include\"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEBioMix module \n\n//! The FEBioMix module adds mixture capabilites to FEBio, including biphasic,\n//! biphasic-solute, multiphasic, charged solutes and chemical reations.\n//!\nnamespace FEBioMix {\n\n\tFEBIOMIX_API void InitModule();\n\n\tenum FEBIOMIX_VARIABLE {\n\t\tFLUID_PRESSURE\n\t};\n\n\tFEBIOMIX_API const char* GetVariableName(FEBIOMIX_VARIABLE var);\n\n}\n"
  },
  {
    "path": "FEBioMix/FEBioMixData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMixData.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FETriphasic.h\"\n#include \"FEMultiphasic.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\ndouble FENodeConcentration::value(const FENode& node) \n{\n\tconst int dof_C = GetFEModel()->GetDOFIndex(\"concentration\", 0);\n\treturn node.get(dof_C); \n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeFluidPressure::value(const FENode& node)\n{\n    const int dof_P = GetFEModel()->GetDOFIndex(\"p\");\n    return node.get(dof_P);\n}\n\n//-----------------------------------------------------------------------------\ndouble FENodeSoluteConcentration_::value(const FENode& node)\n{\n    double val = 0.0;\n    const int dof_C = GetFEModel()->GetDOFIndex(\"concentration\", m_nsol);\n    return node.get(dof_C);\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidPressure::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEBiphasicMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEBiphasicMaterialPoint>();\n\t\tif (ppt) val += ppt->m_pa;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidFluxX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEBiphasicMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEBiphasicMaterialPoint>();\n\t\tif (ppt) val += ppt->m_w.x;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidFluxY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEBiphasicMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEBiphasicMaterialPoint>();\n\t\tif (ppt) val += ppt->m_w.y;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemFluidFluxZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEBiphasicMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FEBiphasicMaterialPoint>();\n\t\tif (ppt) val += ppt->m_w.z;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteConcentration::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_ca[0];\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteFluxX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_j[0].x;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteFluxY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_j[0].y;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteFluxZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_j[0].z;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteRefConcentration::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_sbmr[0];\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogFixedChargeDensity::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_cF;\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteConcentration_::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_ca[m_nsol];\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteFluxX_::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_j[m_nsol].x;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteFluxY_::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_j[m_nsol].y;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSoluteFluxZ_::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_j[m_nsol].z;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemElectricPotential::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_psi;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemCurrentDensityX::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_Ie.x;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemCurrentDensityY::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_Ie.y;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemCurrentDensityZ::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_Ie.z;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSBMConcentration_::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(i)->ExtractData<FESolutesMaterialPoint>();\n\t\tif (ppt) val += ppt->m_sbmr[m_nsol];\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPorosity::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tconst FEMaterialPoint* mp = el.GetMaterialPoint(i);\n\t\tconst FEElasticMaterialPoint* et = (mp->ExtractData<FEElasticMaterialPoint>());\n\t\tconst FEBiphasicMaterialPoint* pt = (mp->ExtractData<FEBiphasicMaterialPoint>());\n\n\t\tdouble p = (et && pt ? (1 - pt->m_phi0t / et->m_J) : 0.0);\n\t\tval += p;\n\t}\n\treturn val / (double) nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemPermeability::value(FEElement& el)\n{\n\tFEDomain* dom = dynamic_cast<FEDomain*>(el.GetMeshPartition());\n\tif (dom == nullptr) return 0.0;\n\tFEMaterial* mat = dom->GetMaterial();\n\tif (mat == nullptr) return 0.0;\n\n\tFEBiphasic* biphasic = mat->ExtractProperty<FEBiphasic>();\n\tif (biphasic == nullptr) return 0.0;\n\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tconst FEMaterialPoint* mp = el.GetMaterialPoint(i);\n\t\tmat3ds K = biphasic->Permeability(const_cast<FEMaterialPoint&>(*mp));\n\n\t\tswitch (m_comp)\n\t\t{\n\t\tcase 0: val += K(0, 0); break;\n\t\tcase 1: val += K(1, 1); break;\n\t\tcase 2: val += K(2, 2); break;\n\t\tcase 3: val += K(0, 1); break;\n\t\tcase 4: val += K(1, 2); break;\n\t\tcase 5: val += K(0, 2); break;\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogElemSolidStress::value(FEElement& el)\n{\n\tdouble val = 0.0;\n\tint nint = el.GaussPoints();\n\tfor (int i = 0; i < nint; ++i)\n\t{\n\t\tconst FEMaterialPoint* mp = el.GetMaterialPoint(i);\n\t\tconst FEBiphasicMaterialPoint* pt = (mp->ExtractData<FEBiphasicMaterialPoint>());\n\t\tif (pt)\n\t\t{\n\t\t\tmat3ds ss = pt->m_ss;\n\n\t\t\tswitch (m_comp)\n\t\t\t{\n\t\t\tcase 0: val += ss(0, 0); break;\n\t\t\tcase 1: val += ss(1, 1); break;\n\t\t\tcase 2: val += ss(2, 2); break;\n\t\t\tcase 3: val += ss(0, 1); break;\n\t\t\tcase 4: val += ss(1, 2); break;\n\t\t\tcase 5: val += ss(0, 2); break;\n\t\t\t}\n\t\t}\n\t}\n\treturn val / (double)nint;\n}\n\nFELogSBMRefAppDensity::FELogSBMRefAppDensity(FEModel* fem, int n) : FELogElemData(fem), sbmid(n)\n{\n}\n\ndouble FELogSBMRefAppDensity::value(FEElement& el)\n{\n\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(el.GetMeshPartition());\n\tif (dom == nullptr) return 0;\n\n\tFEMultiphasic* pm = dynamic_cast<FEMultiphasic*> (dom->GetMaterial());\n\tif (pm == nullptr) return 0;\n\n\t// figure out the local SBM IDs. This depends on the material\n\tint n = -1;\n\tfor (int i = 0; i < pm->SBMs(); ++i)\n\t\tif (pm->GetSBM(i)->GetSBMID() == sbmid) { n = i; break; }\n\n\tif (n == -1) return 0;\n\n\t// calculate average concentration\n\tdouble ew = 0;\n\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\tFESolutesMaterialPoint* st = (mp.ExtractData<FESolutesMaterialPoint>());\n\n\t\tif (st && (n >= 0) && (n < st->m_sbmr.size())) ew += st->m_sbmr[n];\n\t}\n\tew /= el.GaussPoints();\n\n\treturn ew;\n}\n\n//=============================================================================\n\nFELogDomainIntegralSBMConcentration::FELogDomainIntegralSBMConcentration(FEModel* fem, int sbm) : FELogDomainData(fem) \n{\n\tm_sbm = sbm;\n}\n\ndouble FELogDomainIntegralSBMConcentration::value(FEDomain& dom)\n{\n\tdouble sum = 0.0;\n\tif (dynamic_cast<FESolidDomain*>(&dom))\n\t{\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i < solidDomain.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain.Element(i);\n\t\t\tdouble val = 0.0;\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* gw = el.GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(n)->ExtractData<FESolutesMaterialPoint>();\n\t\t\t\tif (ppt)\n\t\t\t\t{\n\t\t\t\t\tdouble Jw = solidDomain.detJt(el, n) * gw[n];\n\t\t\t\t\tval += ppt->m_sbmr[m_sbm] * Jw;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsum += val;\n\t\t}\n\t}\n\treturn sum;\n}\n\n//=============================================================================\nFELogDomainIntegralSoluteConcentration::FELogDomainIntegralSoluteConcentration(FEModel* fem, int sol) : FELogDomainData(fem)\n{\n\tm_nsol = sol;\n}\n\ndouble FELogDomainIntegralSoluteConcentration::value(FEDomain& dom)\n{\n\tdouble sum = 0.0;\n\tif (dynamic_cast<FESolidDomain*>(&dom))\n\t{\n\t\tFESolidDomain& solidDomain = dynamic_cast<FESolidDomain&>(dom);\n\t\tfor (int i = 0; i < solidDomain.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain.Element(i);\n\t\t\tdouble val = 0.0;\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* gw = el.GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFESolutesMaterialPoint* ppt = el.GetMaterialPoint(n)->ExtractData<FESolutesMaterialPoint>();\n\t\t\t\tif (ppt)\n\t\t\t\t{\n\t\t\t\t\tdouble Jw = solidDomain.detJt(el, n) * gw[n];\n\t\t\t\t\tval += ppt->m_ca[m_nsol] * Jw;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsum += val;\n\t\t}\n\t}\n\treturn sum;\n}\n"
  },
  {
    "path": "FEBioMix/FEBioMixData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/NodeDataRecord.h\"\n#include \"FECore/ElementDataRecord.h\"\n#include <FECore/DomainDataRecord.h>\n\n//=============================================================================\n// N O D E  D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// This class uses the deprecated \"c\" variable to denote concentrations.\nclass FENodeConcentration : public FELogNodeData\n{ \npublic: \n\tFENodeConcentration(FEModel* pfem) : FELogNodeData(pfem){} \n\tdouble value(const FENode& node); \n};\n\n//-----------------------------------------------------------------------------\n// return the (nodal) effective fluid pressure\nclass FENodeFluidPressure : public FELogNodeData\n{\npublic:\n    FENodeFluidPressure(FEModel* pfem) : FELogNodeData(pfem) {}\n    double value(const FENode& node);\n};\n\n//-----------------------------------------------------------------------------\nclass FENodeSoluteConcentration_ : public FELogNodeData\n{\nprotected:\n    FENodeSoluteConcentration_(FEModel* pfem, int nsol) : FELogNodeData(pfem), m_nsol(nsol) {}\n    double value(const FENode& node);\nprivate:\n    int m_nsol;\n};\n\ntemplate <int N> class FENodeSoluteConcentration_T : public FENodeSoluteConcentration_\n{\npublic:\n    FENodeSoluteConcentration_T(FEModel* pfem) : FENodeSoluteConcentration_(pfem, N) {}\n    double value(const FENode& node) { return FENodeSoluteConcentration_::value(node); }\n};\n\n\n//=============================================================================\n// E L E M E N T   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidPressure : public FELogElemData\n{\npublic:\n\tFELogElemFluidPressure(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidFluxX : public FELogElemData\n{\npublic:\n\tFELogElemFluidFluxX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidFluxY : public FELogElemData\n{\npublic:\n\tFELogElemFluidFluxY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemFluidFluxZ : public FELogElemData\n{\npublic:\n\tFELogElemFluidFluxZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteConcentration : public FELogElemData\n{\npublic:\n\tFELogElemSoluteConcentration(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteFluxX : public FELogElemData\n{\npublic:\n\tFELogElemSoluteFluxX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteFluxY : public FELogElemData\n{\npublic:\n\tFELogElemSoluteFluxY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteFluxZ : public FELogElemData\n{\npublic:\n\tFELogElemSoluteFluxZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteRefConcentration : public FELogElemData\n{\npublic:\n\tFELogElemSoluteRefConcentration(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogFixedChargeDensity : public FELogElemData\n{\npublic:\n\tFELogFixedChargeDensity(FEModel* pfem) : FELogElemData(pfem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteConcentration_ : public FELogElemData\n{\nprotected:\n\tFELogElemSoluteConcentration_(FEModel* pfem, int nsol) : FELogElemData(pfem), m_nsol(nsol) {}\n\tdouble value(FEElement& el);\nprivate:\n\tint m_nsol;\n};\n\ntemplate <int N> class FELogElemSoluteConcentration_T : public FELogElemSoluteConcentration_\n{\npublic:\n\tFELogElemSoluteConcentration_T(FEModel* pfem) : FELogElemSoluteConcentration_(pfem, N) {}\n\tdouble value(FEElement& el) { return FELogElemSoluteConcentration_::value(el); }\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteFluxX_ : public FELogElemData\n{\nprotected:\n\tFELogElemSoluteFluxX_(FEModel* pfem, int nsol) : FELogElemData(pfem), m_nsol(nsol) {}\n\tdouble value(FEElement& el);\nprivate:\n\tint\tm_nsol;\n};\n\ntemplate <int N> class FELogElemSoluteFluxX_T : public FELogElemSoluteFluxX_\n{\npublic:\n\tFELogElemSoluteFluxX_T(FEModel* pfem) : FELogElemSoluteFluxX_(pfem, N) {}\n\tdouble value(FEElement& el) { return FELogElemSoluteFluxX_::value(el); }\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteFluxY_ : public FELogElemData\n{\nprotected:\n\tFELogElemSoluteFluxY_(FEModel* pfem, int nsol) : FELogElemData(pfem), m_nsol(nsol) {}\n\tdouble value(FEElement& el);\nprivate:\n\tint\tm_nsol;\n};\n\ntemplate <int N> class FELogElemSoluteFluxY_T : public FELogElemSoluteFluxY_\n{\npublic:\n\tFELogElemSoluteFluxY_T(FEModel* pfem) : FELogElemSoluteFluxY_(pfem, N) {}\n\tdouble value(FEElement& el) { return FELogElemSoluteFluxY_::value(el); }\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSoluteFluxZ_ : public FELogElemData\n{\nprotected:\n\tFELogElemSoluteFluxZ_(FEModel* pfem, int nsol) : FELogElemData(pfem), m_nsol(nsol) {}\n\tdouble value(FEElement& el);\nprivate:\n\tint\tm_nsol;\n};\n\ntemplate <int N> class FELogElemSoluteFluxZ_T : public FELogElemSoluteFluxZ_\n{\npublic:\n\tFELogElemSoluteFluxZ_T(FEModel* pfem) : FELogElemSoluteFluxZ_(pfem, N) {}\n\tdouble value(FEElement& el) { return FELogElemSoluteFluxZ_::value(el); }\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemElectricPotential : public FELogElemData\n{\npublic:\n\tFELogElemElectricPotential(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemCurrentDensityX : public FELogElemData\n{\npublic:\n\tFELogElemCurrentDensityX(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemCurrentDensityY : public FELogElemData\n{\npublic:\n\tFELogElemCurrentDensityY(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemCurrentDensityZ : public FELogElemData\n{\npublic:\n\tFELogElemCurrentDensityZ(FEModel* pfem) : FELogElemData(pfem){}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSBMConcentration_: public FELogElemData\n{\nprotected:\n\tFELogElemSBMConcentration_(FEModel* pfem, int nsol) : FELogElemData(pfem), m_nsol(nsol) {}\n\tdouble value(FEElement& el);\nprivate:\n\tint\tm_nsol;\n};\n\ntemplate <int N> class FELogElemSBMConcentration_T: public FELogElemSBMConcentration_\n{\npublic:\n\tFELogElemSBMConcentration_T(FEModel* pfem) : FELogElemSBMConcentration_(pfem, N) {}\n\tdouble value(FEElement& el) { return FELogElemSBMConcentration_::value(el); }\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPorosity : public FELogElemData\n{\npublic:\n\tFELogElemPorosity(FEModel* fem) : FELogElemData(fem) {}\n\tdouble value(FEElement& el);\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemPermeability : public FELogElemData\n{\npublic:\n\tFELogElemPermeability(FEModel* fem, int n) : FELogElemData(fem) { m_comp = n; }\n\tdouble value(FEElement& el);\nprotected:\n\tint\tm_comp;\n};\n\ntemplate <int n>\nclass FELogElemPermeability_T : public FELogElemPermeability\n{\npublic: FELogElemPermeability_T(FEModel* fem) : FELogElemPermeability(fem, n) {}\n};\n\n//-----------------------------------------------------------------------------\nclass FELogElemSolidStress : public FELogElemData\n{\npublic:\n\tFELogElemSolidStress(FEModel* fem, int n) : FELogElemData(fem) { m_comp = n; }\n\tdouble value(FEElement& el);\nprotected:\n\tint\tm_comp;\n};\n\ntemplate <int n>\nclass FELogElemSolidStress_T : public FELogElemSolidStress\n{\npublic: FELogElemSolidStress_T(FEModel* fem) : FELogElemSolidStress(fem, n) {}\n};\n\nclass FELogSBMRefAppDensity : public FELogElemData\n{\npublic:\n\tFELogSBMRefAppDensity(FEModel* fem, int n);\n\tdouble value(FEElement& el);\nprivate:\n\tint sbmid = -1;\n};\n\ntemplate <int n>\nclass FELogSBMRefAppDensity_T : public FELogSBMRefAppDensity\n{\npublic: FELogSBMRefAppDensity_T(FEModel* fem) : FELogSBMRefAppDensity(fem, n) {}\n};\n\n//=============================================================================\n// D O M A I N   D A T A\n//=============================================================================\n\nclass FELogDomainIntegralSBMConcentration : public FELogDomainData\n{\npublic:\n\tFELogDomainIntegralSBMConcentration(FEModel* fem, int sbm);\n\tdouble value(FEDomain& dom) override;\n\nprotected:\n\tint\tm_sbm;\n};\n\ntemplate <int N> class FELogDomainIntegralSBMConcentration_T : public FELogDomainIntegralSBMConcentration\n{\npublic:\n\tFELogDomainIntegralSBMConcentration_T(FEModel* pfem) : FELogDomainIntegralSBMConcentration(pfem, N) {}\n};\n\n//-------------------------------------------------------------------------------------------\nclass FELogDomainIntegralSoluteConcentration : public FELogDomainData\n{\npublic:\n\tFELogDomainIntegralSoluteConcentration(FEModel* fem, int sol);\n\tdouble value(FEDomain& dom) override;\n\nprotected:\n\tint\tm_nsol;\n};\n\ntemplate <int N> class FELogDomainIntegralSoluteConcentration_T : public FELogDomainIntegralSoluteConcentration\n{\npublic:\n\tFELogDomainIntegralSoluteConcentration_T(FEModel* pfem) : FELogDomainIntegralSoluteConcentration(pfem, N) {}\n};\n"
  },
  {
    "path": "FEBioMix/FEBioMixPlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMixPlot.h\"\n#include \"FEBiphasicSolidDomain.h\"\n#include \"FEBiphasicShellDomain.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n#include \"FEBiphasicSoluteSolidDomain.h\"\n#include \"FEBiphasicSoluteShellDomain.h\"\n#include \"FETriphasicDomain.h\"\n#include \"FEMultiphasicSolidDomain.h\"\n#include \"FEMultiphasicShellDomain.h\"\n#include <FEBioMech/FEElasticSolidDomain.h>\n#include <FEBioMech/FESlidingInterface.h>\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FETriphasic.h\"\n#include \"FEMultiphasic.h\"\n#include \"FEBiphasicContactSurface.h\"\n#include \"FESlidingInterfaceMP.h\"\n#include \"FETiedBiphasicInterface.h\"\n#include \"FETiedMultiphasicInterface.h\"\n#include \"FEBioMech/FEDonnanEquilibrium.h\"\n#include \"FEBioMech/FEElasticMixture.h\"\n#include <FECore/FEModel.h>\n#include <FECore/writeplot.h>\n#include <FECore/FEEdgeList.h>\n\n//=============================================================================\n//                       S U R F A C E    D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Plot local fluid load support\nbool FEPlotLocalFluidLoadSupport::Save(FESurface& surf, FEDataStream& a)\n{\n    FEBiphasicContactSurface* pcs = dynamic_cast<FEBiphasicContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n    writeElementValue<double>(surf, a, [=](int nface) {\n        double gn;\n        pcs->GetLocalFLS(nface, gn);\n        return gn;\n    });\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Plot effective friction coefficient\nbool FEPlotEffectiveFrictionCoeff::Save(FESurface& surf, FEDataStream& a)\n{\n    FEBiphasicContactSurface* pcs = dynamic_cast<FEBiphasicContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n    writeElementValue<double>(surf, a, [=](int nface) {\n        double gn;\n        pcs->GetMuEffective(nface, gn);\n        return gn;\n    });\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotMixtureFluidFlowRate::Save(FESurface &surf, FEDataStream &a)\n{\n    FESurface* pcs = &surf;\n    if (pcs == 0) return false;\n    \n    int NF = pcs->Elements();\n    double fn = 0;    // initialize\n    \n    // calculate net flow rate normal to this surface\n    for (int j = 0; j<NF; ++j)\n    {\n        FESurfaceElement& el = pcs->Element(j);\n        \n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // evaluate the average fluid flux in this element\n            int nint = pe->GaussPoints();\n            vec3d w(0, 0, 0);\n            for (int n = 0; n<nint; ++n)\n            {\n                FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n                FEBiphasicMaterialPoint* ptf = mp.ExtractData<FEBiphasicMaterialPoint>();\n                if (ptf) w += ptf->m_w;\n            }\n            w /= nint;\n\n\t\t\tvec3d area = pcs->SurfaceNormal(el, 0, 0) * pcs->FaceArea(el);\n\n            \n            // Evaluate contribution to net flow rate across surface.\n            fn += w*area;\n        }\n    }\n    \n    // save results\n    a << fn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Plot contact gap\nbool FEPlotContactGapMP::Save(FESurface& surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n    writeAverageElementValue<double>(surf, a, [](const FEMaterialPoint& mp) {\n        const FEContactMaterialPoint* pt = dynamic_cast<const FEContactMaterialPoint*>(&mp);\n        double d = (pt ? pt->m_gap : 0);\n        if (d == 0) {\n            const FETiedBiphasicContactPoint* pd = dynamic_cast<const FETiedBiphasicContactPoint*>(&mp);\n            vec3d vd = (pd ? pd->m_dg : vec3d(0,0,0));\n            d = (pd ? vd.unit() : 0);\n        }\n        return d;\n    });\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Plot pressure gap\nbool FEPlotPressureGap::Save(FESurface& surf, FEDataStream& a)\n{\n\tFEBiphasicContactSurface* pcs = dynamic_cast<FEBiphasicContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n    \n    writeAverageElementValue<double>(surf, a, [](const FEMaterialPoint& mp) {\n\t\tconst FEBiphasicContactPoint* pt = dynamic_cast<const FEBiphasicContactPoint*>(&mp);\n\t\treturn (pt ? pt->m_pg : 0);\n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidForce::Save(FESurface &surf, FEDataStream &a)\n{\n\tFEBiphasicContactSurface* pcs = dynamic_cast<FEBiphasicContactSurface*>(&surf);\n\tif (pcs == 0) return false;\n    \n\tvec3d fn = pcs->GetFluidForce();\n\ta << fn;\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nclass FEFluidForce2\n{\npublic:\n\tFEFluidForce2(FESurface& surf, vector<double>& nodalPressures) : m_surf(surf), m_nodalPressures(nodalPressures) {}\n\n\tFEFluidForce2(const FEFluidForce2& fl) : m_surf(fl.m_surf), m_nodalPressures(fl.m_nodalPressures) {}\n\n\tvec3d operator ()(const FEMaterialPoint& mp)\n\t{\n\t\tFEElement* pe = mp.m_elem;\n\t\tdouble pn[FEElement::MAX_NODES];\n\t\tint neln = pe->Nodes();\n\t\tfor (int j = 0; j<neln; ++j) pn[j] = m_nodalPressures[pe->m_node[j]];\n\n\t\tFESurfaceElement& face = static_cast<FESurfaceElement&>(*pe);\n\n\t\t// get the base vectors\n\t\tvec3d g[2];\n\t\tm_surf.CoBaseVectors(face, mp.m_index, g);\n\n\t\t// normal (magnitude = area)\n\t\tvec3d n = g[0] ^ g[1];\n\n\t\t// gauss weight\n\t\tdouble w = face.GaussWeights()[mp.m_index];\n\n\t\t// fluid pressure\n\t\tdouble p = face.eval(pn, mp.m_index);\n\n\t\t// contact force\n\t\treturn n*(w*p);\n\t}\n\nprivate:\n\tFESurface&\tm_surf;\n\tvector<double>& m_nodalPressures;\n};\n\nbool FEPlotFluidForce2::Save(FESurface &surf, FEDataStream &a)\n{\n\t// get number of facets\n\tint NF = surf.Elements();\n\tif (NF == 0) return false;\n\n\t// this assumes that the surface sits on top of a single domain\n\t// so that we can figure out the domain from a single element\n\tFESurfaceElement& ref = surf.Element(0);\n\tif (ref.m_elem[0].pe == nullptr) return false;\n\n\t// get the element\n\tFEMesh& mesh = *surf.GetMesh();\n\tFEElement* el = ref.m_elem[0].pe;\n\tif (el == 0) return false;\n\n\t// get the domain this element belongs to\n\tFEMeshPartition* dom = el->GetMeshPartition();\n\tif (dom == 0) return false;\n\n\t// see if this is a biphasic domain\n\tFEBiphasicSolidDomain* biphasicDomain = dynamic_cast<FEBiphasicSolidDomain*>(dom);\n\tif (biphasicDomain == 0) return false;\n\n\t// The biphasic solid domain contains actual nodal pressures.\n\t// we want to evaluate this over the surface. \n\tvector<double> nodalPressures;\n\tbiphasicDomain->GetNodalPressures(nodalPressures);\n\n\t// calculate element values\n\twriteIntegratedElementValue<vec3d>(surf, a, FEFluidForce2(surf, nodalPressures));\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidLoadSupport::Save(FESurface &surf, FEDataStream &a)\n{\n    FEBiphasicContactSurface* pcs = dynamic_cast<FEBiphasicContactSurface*>(&surf);\n    if (pcs == 0) return false;\n    \n    double fn = pcs->GetFluidLoadSupport();\n    a << fn;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Plot concentration gap\nFEPlotConcentrationGap::FEPlotConcentrationGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n\tSetUnits(UNIT_CONCENTRATION);\n}\n\nbool FEPlotConcentrationGap::Save(FESurface& surf, FEDataStream& a)\n{\n    FEContactSurface* pcs = dynamic_cast<FEContactSurface*>(&surf);\n    if (pcs == 0) return false;\n\n    for (int i=0; i<surf.Elements(); ++i) {\n        FESurfaceElement& el = surf.Element(i);\n\n        FEElement* se = (el.m_elem[0]).pe;\n        FEMaterial* mat = GetFEModel()->GetMaterial(se->GetMatID());\n        FESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(mat);\n        if ((pm == 0) || (pm->Solutes() == 0)) return false;\n        \n        // figure out the local solute IDs. This depends on the material\n        int nsols = (int)m_sol.size();\n        vector<int> lid(nsols, -1);\n        int nsc = 0;\n        for (int i = 0; i<(int)m_sol.size(); ++i)\n        {\n            lid[i] = pm->FindLocalSoluteID(m_sol[i]);\n            if (lid[i] != -1) nsc++;\n        }\n        if (nsc == 0) return false;\n        \n        for (int k=0; k<nsols; ++k)\n        {\n            int nsid = lid[k];\n            if (nsid == -1) a << 0.f;\n            else\n            {\n                // calculate average concentration gp\n                double ew = 0;\n                for (int j = 0; j<el.GaussPoints(); ++j)\n                {\n                    FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                    const FEMultiphasicContactPoint* pt = dynamic_cast<const FEMultiphasicContactPoint*>(&mp);\n                    const FETiedMultiphasicContactPoint* tt = dynamic_cast<const FETiedMultiphasicContactPoint*>(&mp);\n                    if (pt) ew += pt->m_cg[nsid];\n                    else if (tt) ew += tt->m_cg[nsid];\n                }\n                ew /= el.GaussPoints();\n                a << ew;\n            }\n        }\n    }\n    return true;\n}\n\n//=============================================================================\n//\t\t\t\t\t\t\tD O M A I N   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nclass FEMPSpecificStrainEnergy\n{\npublic:\n    FEMPSpecificStrainEnergy(FEMultiphasic* pm) : m_mat(pm) {}\n    double operator()(const FEMaterialPoint& mp)\n    {\n        return m_mat->GetElasticMaterial()->StrainEnergyDensity(const_cast<FEMaterialPoint&>(mp))/m_mat->SolidReferentialApparentDensity(const_cast<FEMaterialPoint&>(mp));\n    }\nprivate:\n    FEMultiphasic*    m_mat;\n};\n\nbool FEPlotMPSpecificStrainEnergy::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEMultiphasic* pme = dom.GetMaterial()->ExtractProperty<FEMultiphasic>();\n    if (pme == 0) return false;\n    \n    if (dom.Class() == FE_DOMAIN_SOLID)\n    {\n        FEMPSpecificStrainEnergy psi(pme);\n        writeAverageElementValue<double>(dom, a, psi);\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotActualFluidPressure::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n    FEShellDomain& bsd = static_cast<FEShellDomain&>(dom);\n\tif ((dynamic_cast<FEBiphasicSolidDomain* >(&bd)) ||\n\t\t(dynamic_cast<FEBiphasicSoluteSolidDomain*>(&bd)) ||\n\t\t(dynamic_cast<FETriphasicDomain*     >(&bd)) ||\n\t\t(dynamic_cast<FEMultiphasicSolidDomain*   >(&bd)))\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEBiphasicMaterialPoint* pt = mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\t\treturn (pt ? pt->m_pa : 0.0);\n\t\t});\n\t\treturn true;\n\t}\n    else if (dynamic_cast<FEElasticSolidDomain* >(&bd))\n    {\n        for (int i=0; i<bd.Elements(); ++i)\n        {\n            FESolidElement& el = bd.Element(i);\n            FEElasticMixture* pem  = dynamic_cast<FEElasticMixture*> (dom.GetMaterial());\n            \n            if (pem == nullptr) return false;\n            \n            // extract fixed-charge density\n            double ew = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n                for (int k=0; k<pem->Materials(); ++k) {\n                    FEDonnanEquilibriumMaterialPoint* pd = pt.GetPointData(k)->ExtractData<FEDonnanEquilibriumMaterialPoint>();\n                    if (pd) ew += pd->m_p;\n                }\n            }\n            \n            ew /= el.GaussPoints();\n            \n            a << ew;\n        }\n        return true;\n    }\n \n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSolidStress::Save(FEDomain &dom, FEDataStream& a)\n{\n    FESolidDomain* bd = dynamic_cast<FESolidDomain*>(&dom);\n    FEShellDomain* bsd = dynamic_cast<FEShellDomain*>(&dom);\n    if (bd && (\n               (dynamic_cast<FEBiphasicSolidDomain* >(bd)) ||\n               (dynamic_cast<FEBiphasicSoluteSolidDomain*>(bd)) ||\n               (dynamic_cast<FETriphasicDomain*     >(bd)) ||\n               (dynamic_cast<FEMultiphasicSolidDomain*>(bd))))\n    {\n        writeAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n            const FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n            return (pt ? pt->m_ss : mat3ds(0.0));\n        });\n        return true;\n    }\n    else if (bsd && (\n                     (dynamic_cast<FEBiphasicShellDomain*>(bsd)) ||\n                     (dynamic_cast<FEBiphasicSoluteShellDomain*>(bsd)) ||\n                     (dynamic_cast<FEMultiphasicShellDomain*>(bsd))\n                     ))\n    {\n        writeAverageElementValue<mat3ds>(dom, a, [](const FEMaterialPoint& mp) {\n            const FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n            return (pt ? pt->m_ss : mat3ds(0.0));\n        });\n        return true;\n    }\n    \n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFluidFlux::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESolidDomain* bd = dynamic_cast<FESolidDomain*>(&dom);\n    FEShellDomain* bsd = dynamic_cast<FEShellDomain*>(&dom);\n\tif (bd && (\n        (dynamic_cast<FEBiphasicSolidDomain* >(bd)) ||\n\t\t(dynamic_cast<FEBiphasicSoluteSolidDomain*>(bd)) ||\n\t\t(dynamic_cast<FETriphasicDomain*     >(bd)) ||\n\t\t(dynamic_cast<FEMultiphasicSolidDomain*>(bd))))\n\t{\n\t\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\t\treturn (pt ? pt->m_w : vec3d(0.0));\n\t\t});\n\t\treturn true;\n\t}\n    else if (bsd && (\n                     (dynamic_cast<FEBiphasicShellDomain*>(bsd)) ||\n                     (dynamic_cast<FEBiphasicSoluteShellDomain*>(bsd)) ||\n                     (dynamic_cast<FEMultiphasicShellDomain*>(bsd))\n                     ))\n    {\n\t\twriteAverageElementValue<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\t\treturn (pt ? pt->m_w : vec3d(0.0));\n\t\t});\n\t\treturn true;\n    }\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotNodalFluidFlux::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESolidDomain& bd = static_cast<FESolidDomain&>(dom);\n\tif ((dynamic_cast<FEBiphasicSolidDomain* >(&bd)) ||\n\t\t(dynamic_cast<FEBiphasicSoluteSolidDomain*>(&bd)) ||\n\t\t(dynamic_cast<FETriphasicDomain*     >(&bd)) ||\n\t\t(dynamic_cast<FEMultiphasicSolidDomain*>(&bd)))\n\t{\n\t\twriteNodalProjectedElementValues<vec3d>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEBiphasicMaterialPoint* pt = mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\t\treturn pt->m_w;\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Finds the solute id of a solute with given ID nsol.\n// This currently returns either nsol if a solute was found or -1 if not\nint GetSoluteID(FEModel& fem, int nsol)\n{\n\tint N = fem.GlobalDataItems();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n\t\tif (psd)\n\t\t{\n\t\t\tif (psd->GetID()-1 == nsol) return psd->GetID();\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// Finds the id of a sbm with given ID nsbm.\n// This currently returns either nsbm if a solute was found or -1 if not\nint GetSBMID(FEModel& fem, int nsol)\n{\n\tint N = fem.GlobalDataItems();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFESBMData* psd = dynamic_cast<FESBMData*>(fem.GetGlobalData(i));\n\t\tif (psd)\n\t\t{\n\t\t\tif (psd->GetID()-1 == nsol) return psd->GetID();\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// Finds the solute ID given the name of the solute\nint GetSoluteID(FEModel& fem, const char* sz)\n{\n\tstring soluteName(sz);\n\t// find the solute with that name\n\tint N = fem.GlobalDataItems();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n\t\tif (psd)\n\t\t{\n\t\t\tif (psd->GetName() == soluteName) return psd->GetID();\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// Finds the sbm ID given the name of the sbm\nint GetSBMID(FEModel& fem, const char* sz)\n{\n\tstring sbmName(sz);\n\t// find the sbm with that name\n\tint N = fem.GlobalDataItems();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFESBMData* psd = dynamic_cast<FESBMData*>(fem.GetGlobalData(i));\n\t\tif (psd)\n\t\t{\n\t\t\tif (psd->GetName() == sbmName) return psd->GetID();\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// find the local SBM ID, given a global ID. If the material is not a \n// multiphasic material, this returns -1.\nint GetLocalSBMID(FEMultiphasic* pmm, int nsbm)\n{\n\t// figure out the SBM ID to export. This depends on the material type.\n\tint nsid = -1;\n\n\t// Check if this solute is present in this specific multiphasic mixture\n\tfor (int i=0; i<pmm->SBMs(); ++i)\n\t\tif (pmm->GetSBM(i)->GetSBMID() == nsbm) {nsid = i; break;}\n\t\n\treturn nsid;\n}\n\n//=================================================================================================\n//-----------------------------------------------------------------------------\nFEPlotActualSoluteConcentration::FEPlotActualSoluteConcentration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n    SetUnits(UNIT_CONCENTRATION);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotActualSoluteConcentration::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif (pm == 0) return false;\n\n\t// figure out the local solute IDs. This depends on the material\n\tint nsols = (int)m_sol.size();\n\tvector<int> lid(nsols, -1);\n\tint negs = 0;\n\tfor (int i = 0; i<(int)m_sol.size(); ++i)\n\t{\n\t\tlid[i] = pm->FindLocalSoluteID(m_sol[i]);\n\t\tif (lid[i] < 0) negs++;\n\t}\n\tif (negs == nsols) return false;\n\n\t// loop over all elements\n\tint N = dom.Elements();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tfor (int k=0; k<nsols; ++k)\n\t\t{\n\t\t\tint nsid = lid[k];\n\t\t\tif (nsid == -1) a << 0.f;\n\t\t\telse\n\t\t\t{\n\t\t\t\t// calculate average concentration\n\t\t\t\tdouble ew = 0;\n\t\t\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\t\tew += pm->GetActualSoluteConcentration(mp, nsid);\n\t\t\t\t}\n\t\t\t\tew /= el.GaussPoints();\n\t\t\t\ta << ew;\n\t\t\t}\n\t\t}\n\n\t}\n\treturn true;\n}\n\n//=================================================================================================\n//-----------------------------------------------------------------------------\nFEPlotPartitionCoefficient::FEPlotPartitionCoefficient(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPartitionCoefficient::Save(FEDomain &dom, FEDataStream& a)\n{\n    FESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n    if (pm == 0) return false;\n    \n    // figure out the local solute IDs. This depends on the material\n    int nsols = (int)m_sol.size();\n    vector<int> lid(nsols, -1);\n    int negs = 0;\n    for (int i = 0; i<(int)m_sol.size(); ++i)\n    {\n        lid[i] = pm->FindLocalSoluteID(m_sol[i]);\n        if (lid[i] < 0) negs++;\n    }\n    if (negs == nsols) return false;\n    \n    // loop over all elements\n    int N = dom.Elements();\n    for (int i = 0; i<N; ++i)\n    {\n        FEElement& el = dom.ElementRef(i);\n        \n        for (int k=0; k<nsols; ++k)\n        {\n            int nsid = lid[k];\n            if (nsid == -1) a << 0.f;\n            else\n            {\n                // calculate average concentration\n                double ew = 0;\n                for (int j = 0; j<el.GaussPoints(); ++j)\n                {\n                    FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\t\tew += pm->GetPartitionCoefficient(mp, nsid);\n                }\n                ew /= el.GaussPoints();\n                a << ew;\n            }\n        }\n        \n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nFEPlotSoluteFlux::FEPlotSoluteFlux(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY_VEC3F, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n    SetUnits(UNIT_MOLAR_FLUX);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSoluteFlux::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif ((pm == 0) || (pm->Solutes() == 0)) return false;\n\n\t// figure out the local solute IDs. This depends on the material\n\tint nsols = (int)m_sol.size();\n\tvector<int> lid(nsols, -1);\n\tint nsc = 0;\n\tfor (int i = 0; i<(int)m_sol.size(); ++i)\n\t{\n\t\tlid[i] = pm->FindLocalSoluteID(m_sol[i]);\n\t\tif (lid[i] != -1) nsc++;\n\t}\n\tif (nsc == 0) return false;\n\n\tfor (int i = 0; i<dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tfor (int k=0; k<nsols; ++k)\n\t\t{\n\t\t\tint nsid = lid[k];\n\t\t\tif (nsid == -1) a << vec3d(0, 0, 0);\n\t\t\telse\n\t\t\t{\n\t\t\t\t// calculate average flux\n\t\t\t\tvec3d ew = vec3d(0, 0, 0);\n\t\t\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\t\tew += pm->GetSoluteFlux(mp, nsid);\n\t\t\t\t}\n\n\t\t\t\tew /= el.GaussPoints();\n\n\t\t\t\ta << ew;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEPlotSoluteVolumetricFlux::FEPlotSoluteVolumetricFlux(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY_VEC3F, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n    SetUnits(UNIT_VELOCITY);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSoluteVolumetricFlux::Save(FEDomain &dom, FEDataStream& a)\n{\n    FESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n    if ((pm == 0) || (pm->Solutes() == 0)) return false;\n    \n    // figure out the local solute IDs. This depends on the material\n    int nsols = (int)m_sol.size();\n    vector<int> lid(nsols, -1);\n    int nsc = 0;\n    for (int i = 0; i<(int)m_sol.size(); ++i)\n    {\n        lid[i] = pm->FindLocalSoluteID(m_sol[i]);\n        if (lid[i] != -1) nsc++;\n    }\n    if (nsc == 0) return false;\n    \n    for (int i = 0; i<dom.Elements(); ++i)\n    {\n        FEElement& el = dom.ElementRef(i);\n        \n        for (int k=0; k<nsols; ++k)\n        {\n            int nsid = lid[k];\n            if (nsid == -1) a << vec3d(0, 0, 0);\n            else\n            {\n                // calculate average flux\n                vec3d ew = vec3d(0, 0, 0);\n                for (int j = 0; j<el.GaussPoints(); ++j)\n                {\n                    FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                    FESolutesMaterialPoint* pt = (mp.ExtractData<FESolutesMaterialPoint>());\n                    \n                    if (pt && (pt->m_ca[nsid] > 0)) ew += pt->m_j[nsid]/pt->m_ca[nsid];\n                }\n                \n                ew /= el.GaussPoints();\n                \n                a << ew;\n            }\n        }\n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotOsmolarity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n    FESolidDomain* sdom = dynamic_cast<FESolidDomain*>(&dom);\n    FEShellDomain* ldom = dynamic_cast<FEShellDomain*>(&dom);\n    if (sdom && psm)\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\t\tdouble ew = psm->GetOsmolarity(mp);\n\t\t\treturn ew;\n\t\t});\n\n        return true;\n    }\n    else if (sdom && dynamic_cast<FEElasticSolidDomain*>(&dom)) {\n        for (int i=0; i<sdom->Elements(); ++i)\n        {\n            FESolidElement& el = sdom->Element(i);\n            FEElasticMixture* pem  = dynamic_cast<FEElasticMixture*> (dom.GetMaterial());\n            \n            if (pem == nullptr) return false;\n            \n            // extract fixed-charge density\n            double ew = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n                for (int k=0; k<pem->Materials(); ++k) {\n                    FEDonnanEquilibriumMaterialPoint* pd = pt.GetPointData(k)->ExtractData<FEDonnanEquilibriumMaterialPoint>();\n                    if (pd) ew += pd->m_osm;\n                }\n            }\n            \n            ew /= el.GaussPoints();\n            \n            a << ew;\n        }\n        return true;\n    }\n    else if (ldom && (\n             dynamic_cast<FEBiphasicSoluteShellDomain*>(&dom) ||\n             dynamic_cast<FEMultiphasicShellDomain*>(&dom))) {\n\n\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FESolutesMaterialPoint* pt = mp.ExtractData<FESolutesMaterialPoint>();\n\t\t\tdouble ew = 0.0;\n\t\t\tfor (int isol = 0; isol<(int)pt->m_ca.size(); ++isol) ew += pt->m_ca[isol];\n\t\t\treturn ew;\n\t\t});\n\n\t\treturn true;\n    }\n\treturn false;\n}\n\n//=================================================================================================\n// FEPlotSBMConcentration\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFEPlotSBMConcentration::FEPlotSBMConcentration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\t// count SBMs\n\t\tint sbms = 0;\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> names;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESBMData* sbm = dynamic_cast<FESBMData*>(pfem->GetGlobalData(i));\n\t\t\tif (sbm)\n\t\t\t{\n\t\t\t\tnames.push_back(sbm->GetName());\n\t\t\t\tm_sbm.push_back(sbm->GetID());\n\t\t\t\tsbms++;\n\t\t\t}\n\t\t}\n\n\t\tSetArraySize(sbms);\n\t\tSetArrayNames(names);\n\t}\n    SetUnits(UNIT_CONCENTRATION);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSBMConcentration::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEMultiphasic* pm = dynamic_cast<FEMultiphasic*> (dom.GetMaterial());\n\tif (pm == 0) return false;\n\n\t// figure out the local SBM IDs. This depend on the material\n\tint nsbm = (int)m_sbm.size();\n\tvector<int> lid(nsbm, -1);\n\tfor (int i=0; i<(int)m_sbm.size(); ++i)\n\t{\n\t\tlid[i] = GetLocalSBMID(pm, m_sbm[i]);\n\t}\n\n\tint N = dom.Elements();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tfor (int k=0; k<nsbm; ++k)\n\t\t{\n\t\t\tint nk = lid[k];\n\t\t\tif (nk == -1) a << 0.f;\n\t\t\telse\n\t\t\t{\n\t\t\t\t// calculate average concentration\n\t\t\t\tdouble ew = 0;\n\t\t\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\t\tFESolutesMaterialPoint* pt = (mp.ExtractData<FESolutesMaterialPoint>());\n\n\t\t\t\t\tif (pt) ew += pm->SBMConcentration(mp, nk);\n\t\t\t\t}\n\t\t\t\tew /= el.GaussPoints();\n\n\t\t\t\ta << ew;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\n//=================================================================================================\n// FEPlotSBMArealConcentration\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFEPlotSBMArealConcentration::FEPlotSBMArealConcentration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n    // count SBMs\n\tif (pfem)\n\t{\n\t\tint sbms = 0;\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> names;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESBMData* sbm = dynamic_cast<FESBMData*>(pfem->GetGlobalData(i));\n\t\t\tif (sbm)\n\t\t\t{\n\t\t\t\tnames.push_back(sbm->GetName());\n\t\t\t\tm_sbm.push_back(sbm->GetID());\n\t\t\t\tsbms++;\n\t\t\t}\n\t\t}\n\n\t\tSetArraySize(sbms);\n\t\tSetArrayNames(names);\n\t}\n    SetUnits(UNIT_MOLAR_AREAL_CONCENTRATION);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSBMArealConcentration::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEShellDomain* bsd = static_cast<FEShellDomain*>(&dom);\n    if (bsd == nullptr) return false;\n    \n    FEMultiphasic* pm = dynamic_cast<FEMultiphasic*> (dom.GetMaterial());\n    if (pm == 0) return false;\n    \n    // figure out the local SBM IDs. This depend on the material\n    int nsbm = (int)m_sbm.size();\n    vector<int> lid(nsbm, -1);\n    for (int i=0; i<(int)m_sbm.size(); ++i)\n    {\n        lid[i] = GetLocalSBMID(pm, m_sbm[i]);\n    }\n    \n    int N = dom.Elements();\n    for (int i = 0; i<N; ++i)\n    {\n        FEElement& el = dom.ElementRef(i);\n        \n        for (int k=0; k<nsbm; ++k)\n        {\n            int nk = lid[k];\n            if (nk == -1) a << 0.f;\n            else\n            {\n                // calculate average concentration\n                double ew = 0;\n                for (int j = 0; j<el.GaussPoints(); ++j)\n                {\n                    FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                    FESolutesMaterialPoint* pt = (mp.ExtractData<FESolutesMaterialPoint>());\n                    \n                    if (pt) ew += pm->SBMArealConcentration(mp, nk);\n                }\n                ew /= el.GaussPoints();\n                \n                a << ew;\n            }\n        }\n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotElectricPotential::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif (psm == nullptr) return false;\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\treturn psm->GetElectricPotential(mp);\n\t\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotCurrentDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif (psm == nullptr) return false;\n\twriteAverageElementValue<vec3d>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\treturn psm->GetCurrentDensity(mp);\n\t\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotReferentialSolidVolumeFraction::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(dom.GetMaterial());\n\tif (pbm == nullptr) return false;\n\n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tdouble phif0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\t\treturn phif0;\n\t});\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPorosity::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasicSolidDomain* bmd = dynamic_cast<FEBiphasicSolidDomain*>(&dom);\n    FEBiphasicShellDomain* bsd = dynamic_cast<FEBiphasicShellDomain*>(&dom);\n    FEMultiphasicSolidDomain* pmd = dynamic_cast<FEMultiphasicSolidDomain*>(&dom);\n    FEMultiphasicShellDomain* psd = dynamic_cast<FEMultiphasicShellDomain*>(&dom);\n    if (bmd || bsd || pmd || psd)\n    {\n        writeAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n            const FEElasticMaterialPoint* et = (mp.ExtractData<FEElasticMaterialPoint>());\n            const FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n            return (pt ? (1 - pt->m_phi0t/et->m_J) : 0.0);\n        });\n        return true;\n    }\n    return false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotPerm::Save(FEDomain &dom, FEDataStream& a)\n{\n    FEBiphasic* bp = dom.GetMaterial()->ExtractProperty<FEBiphasic>();\n    if (bp == 0) return false;\n\n    writeAverageElementValue<mat3ds>(dom, a, [=](const FEMaterialPoint& mp) {\n            return bp->Permeability(const_cast<FEMaterialPoint&>(mp));\n        });\n        return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotFixedChargeDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n    FEElasticSolidDomain* ped = dynamic_cast<FEElasticSolidDomain*>(&dom);\n\tif (psm)\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n            double cf = psm->GetFixedChargeDensity(mp);\n\t\t\treturn cf;\n\t\t});\n\t\treturn true;\n\t}\n    else if (ped)\n    {\n        for (int i=0; i<ped->Elements(); ++i)\n        {\n            FESolidElement& el = ped->Element(i);\n            FEElasticMixture* pem  = dynamic_cast<FEElasticMixture*> (dom.GetMaterial());\n            \n            if (pem == nullptr) return false;\n            \n            // extract fixed-charge density\n            double ew = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n                for (int k=0; k<pem->Materials(); ++k) {\n                    FEDonnanEquilibriumMaterialPoint* pd = pt.GetPointData(k)->ExtractData<FEDonnanEquilibriumMaterialPoint>();\n                    if (pd) ew += pd->m_cF;\n                }\n            }\n            \n            ew /= el.GaussPoints();\n            \n            a << ew;\n        }\n        return true;\n    }\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotReferentialFixedChargeDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n    FEElasticSolidDomain* ped = dynamic_cast<FEElasticSolidDomain*>(&dom);\n\tif (psm)\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n            double cf = psm->GetReferentialFixedChargeDensity(mp);\n\t\t\treturn cf;\n\t\t});\n\t\treturn true;\n\t}\n    else if (ped)\n    {\n        for (int i=0; i<ped->Elements(); ++i)\n        {\n            FESolidElement& el = ped->Element(i);\n            FEElasticMixture* pem  = dynamic_cast<FEElasticMixture*> (dom.GetMaterial());\n            \n            if (pem == nullptr) return false;\n            \n            // extract fixed-charge density\n            double ew = 0;\n            for (int j=0; j<el.GaussPoints(); ++j)\n            {\n                FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n                FEElasticMixtureMaterialPoint& pt = *mp.ExtractData<FEElasticMixtureMaterialPoint>();\n                for (int k=0; k<pem->Materials(); ++k) {\n                    FEDonnanEquilibriumMaterialPoint* pd = pt.GetPointData(k)->ExtractData<FEDonnanEquilibriumMaterialPoint>();\n                    if (pd) ew += pd->m_cFr;\n                }\n            }\n            \n            ew /= el.GaussPoints();\n            \n            a << ew;\n        }\n        return true;\n    }\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEffectiveFluidPressure::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEBiphasicDomain*       pd = dynamic_cast<FEBiphasicDomain*      >(&dom);\n\tFEBiphasicSoluteDomain* psd = dynamic_cast<FEBiphasicSoluteDomain*>(&dom);\n\tFETriphasicDomain*      ptd = dynamic_cast<FETriphasicDomain*     >(&dom);\n\tFEMultiphasicDomain*    pmd = dynamic_cast<FEMultiphasicDomain*   >(&dom);\n\n\t// special handling of mixed biphasic formulation\n\tif (pd)\n\t{\n\t\t// get the pressure dof index\n\t\tint dof_p = GetFEModel()->GetDOFIndex(\"p\");\n\t\tif (dof_p == -1) return false;\n\n\t\tDOFS& dofs = GetFEModel()->GetDOFS();\n\t\tint varU = dofs.GetVariableIndex(\"displacement\");\n\t\tint varP = dofs.GetVariableIndex(\"fluid pressure\");\n\n\t\tint kd = dofs.GetVariableInterpolationOrder(varU);\n\t\tint kp = dofs.GetVariableInterpolationOrder(varP);\n\t\tif ((kd != 1) && (kp == 1))\n\t\t{\n\t\t\tint N = dom.Nodes();\n\t\t\tvector<double> p(N, 0.0);\n\t\t\tfor (int i = 0; i < dom.Nodes(); ++i)\n\t\t\t{\n\t\t\t\tFENode& node = dom.Node(i);\n\t\t\t\tp[i] = node.get(dof_p);\n\t\t\t}\n\n\t\t\tFEEdgeList EL;\n\t\t\tif (EL.Create(&dom) == false) return false;\n\n\t\t\tfor (int i = 0; i < EL.Edges(); ++i)\n\t\t\t{\n\t\t\t\tconst FEEdgeList::EDGE& edge = EL.Edge(i);\n\t\t\t\tassert(edge.ntype == 3);\n\t\t\t\tp[edge.node[2]] = 0.5*(p[edge.node[0]] + p[edge.node[1]]);\n\t\t\t}\n\n\t\t\ta << p;\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (pd || psd || ptd || pmd)\n\t{\n\t\t// get the pressure dof index\n\t\tint dof_p = GetFEModel()->GetDOFIndex(\"p\");\n\t\tif (dof_p == -1) return false;\n\n\t\t// write the nodal values\n\t\twriteNodalValues<double>(dom, a, [=, &dom](int i) {\n\t\t\tFENode& node = dom.Node(i);\n\t\t\treturn node.get(dof_p);\n\t\t});\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEffectiveShellFluidPressure::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEBiphasicShellDomain* pbsd = dynamic_cast<FEBiphasicShellDomain*>(&dom);\n\tFEBiphasicSoluteShellDomain* pbssd = dynamic_cast<FEBiphasicSoluteShellDomain*>(&dom);\n\tFEMultiphasicShellDomain* pmpsd = dynamic_cast<FEMultiphasicShellDomain*>(&dom);\n\tif (pbsd || pbssd || pmpsd)\n\t{\n\t\t// get the pressure dof index\n\t\tint dof_q = GetFEModel()->GetDOFIndex(\"q\");\n\t\tassert(dof_q != -1);\n\n\t\t// write the nodal values\n\t\twriteNodalValues<double>(dom, a, [=, &dom](int i) {\n\t\t\tFENode& node = dom.Node(i);\n\t\t\treturn node.get(dof_q);\n\t\t});\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//=================================================================================================\n// FEPlotEffectiveSoluteConcentration\n//=================================================================================================\n\nFEPlotEffectiveSoluteConcentration::FEPlotEffectiveSoluteConcentration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_NODE)\n{\n\tif (pfem)\n\t{\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nsol = dofs.GetVariableSize(\"concentration\");\n\t\tSetArraySize(nsol);\n\n\t\t// collect the names\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> s;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESoluteData* ps = dynamic_cast<FESoluteData*>(pfem->GetGlobalData(i));\n\t\t\tif (ps)\n\t\t\t{\n\t\t\t\ts.push_back(ps->GetName());\n\t\t\t\tm_sol.push_back(ps->GetID());\n\t\t\t}\n\t\t}\n\t\tassert(nsol == (int)s.size());\n\t\tSetArrayNames(s);\n\t}\n    SetUnits(UNIT_CONCENTRATION);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEffectiveSoluteConcentration::Save(FEDomain &dom, FEDataStream& a)\n{\n\t// get the dof\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint nsol = dofs.GetVariableSize(\"concentration\");\n\tif (nsol == -1) return false;\n\n\t// get the start index\n\tconst int dof_C = GetFEModel()->GetDOFIndex(\"concentration\", 0);\n\n\tFESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif (pm == 0) return false;\n\n\t// figure out the local solute IDs. This depends on the material\n\tint nsols = (int)m_sol.size();\n\tvector<int> lid(nsols, -1);\n\tint negs = 0;\n\tfor (int i = 0; i<(int)m_sol.size(); ++i)\n\t{\n\t\tlid[i] = pm->FindLocalSoluteID(m_sol[i]);\n\t\tif (lid[i] < 0) negs++;\n\t}\n\tif (negs == nsol) return false;\n\n\t// save the concentrations\n\tint N = dom.Nodes();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = dom.Node(i);\n\t\tfor (int j=0; j<nsol; ++j)\n\t\t{\n\t\t\tdouble c = (lid[j] >= 0 ? node.get(dof_C + j) : 0.0);\n\t\t\ta << c;\n\t\t}\n\t}\n\treturn true;\n}\n\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFEPlotEffectiveShellSoluteConcentration::FEPlotEffectiveShellSoluteConcentration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_NODE)\n{\n\tm_nsol = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Resolve solute by name\nbool FEPlotEffectiveShellSoluteConcentration::SetFilter(const char* sz)\n{\n\tm_nsol = GetSoluteID(*GetFEModel(), sz);\n\treturn (m_nsol != -1);\n}\n\n//-----------------------------------------------------------------------------\n// Resolve solute by solute ID\nbool FEPlotEffectiveShellSoluteConcentration::SetFilter(int nsol)\n{\n\tm_nsol = GetSoluteID(*GetFEModel(), nsol);\n\treturn (m_nsol != -1);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEffectiveShellSoluteConcentration::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif (pm == 0) return false;\n\n\t// make sure we have a valid index\n\tint nsid = pm->FindLocalSoluteID(m_nsol);\n\tif (nsid == -1) return false;\n\n\t// get the dof\n\tconst int dof_D = GetFEModel()->GetDOFIndex(\"shell concentration\", nsid);\n\tif (dof_D == -1) return false;\n\n\tint N = dom.Nodes();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = dom.Node(i);\n\t\ta << node.get(dof_D);\n\t}\n\treturn true;\n}\n\n//=================================================================================================\nbool FEPlotReceptorLigandConcentration::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEBiphasicSoluteSolidDomain* pbd = dynamic_cast<FEBiphasicSoluteSolidDomain*>(&dom);\n    FEBiphasicSoluteShellDomain* psd = dynamic_cast<FEBiphasicSoluteShellDomain*>(&dom);\n\tif (pbd || psd)\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FESolutesMaterialPoint* pt = (mp.ExtractData<FESolutesMaterialPoint>());\n\t\t\treturn (pt ? pt->m_sbmr[0] : 0.0);\n\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//=================================================================================================\nFEPlotSBMRefAppDensity::FEPlotSBMRefAppDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_ARRAY, FMT_ITEM)\n{\n\tif (pfem)\n\t{\n\t\t// count SBMs\n\t\tint sbms = 0;\n\t\tint ndata = pfem->GlobalDataItems();\n\t\tvector<string> names;\n\t\tfor (int i = 0; i < ndata; ++i)\n\t\t{\n\t\t\tFESBMData* sbm = dynamic_cast<FESBMData*>(pfem->GetGlobalData(i));\n\t\t\tif (sbm)\n\t\t\t{\n\t\t\t\tnames.push_back(sbm->GetName());\n\t\t\t\tm_sbm.push_back(sbm->GetID());\n\t\t\t\tsbms++;\n\t\t\t}\n\t\t}\n\n\t\tSetArraySize(sbms);\n\t\tSetArrayNames(names);\n\t}\n    SetUnits(UNIT_DENSITY);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotSBMRefAppDensity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEMultiphasic* pm = dynamic_cast<FEMultiphasic*> (dom.GetMaterial());\n\tif (pm == 0) return false;\n\n\t// figure out the local SBM IDs. This depend on the material\n\tint nsbm = (int)m_sbm.size();\n\tvector<int> lid(nsbm, -1);\n\tfor (int i = 0; i<(int)m_sbm.size(); ++i)\n\t{\n\t\tlid[i] = GetLocalSBMID(pm, m_sbm[i]);\n\t}\n\n\t// figure out the sbm ID to export. This depends on the material type.\n\t// loop over all elements\n\tfor (int i = 0; i<dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tfor (int k=0; k<nsbm; ++k)\n\t\t{\n\t\t\tint nsid = lid[k];\n\t\t\tif (nsid == -1) a << 0.f;\n\t\t\telse\n\t\t\t{\n\t\t\t\t// calculate average concentration\n\t\t\t\tdouble ew = 0;\n\t\t\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\t\t\tFESolutesMaterialPoint* st = (mp.ExtractData<FESolutesMaterialPoint>());\n\n\t\t\t\t\tif (st) ew += st->m_sbmr[nsid];\n\t\t\t\t}\n\t\t\t\tew /= el.GaussPoints();\n\n\t\t\t\ta << ew;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotEffectiveElasticity::Save(FEDomain &dom, FEDataStream& a)\n{\n\tFEBiphasic*       pb  = dynamic_cast<FEBiphasic      *> (dom.GetMaterial());\n\tFEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (dom.GetMaterial());\n\tFETriphasic*      ptp = dynamic_cast<FETriphasic     *> (dom.GetMaterial());\n\tFEMultiphasic*    pmp = dynamic_cast<FEMultiphasic   *> (dom.GetMaterial());\n\tif ((pb == 0) && (pbs == 0) && (ptp == 0) && (pmp == 0)) return false;\n\n\tfor (int i=0; i<dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\n\t\tint nint = el.GaussPoints();\n\t\tdouble f = 1.0 / (double) nint;\n\n\t\ttens4ds s(0.0);\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(j);\n            if      (pb ) s += (pb->Tangent(pt)).supersymm();\n\t\t\telse if (pbs) s += pbs->Tangent(pt);\n\t\t\telse if (ptp) s += ptp->Tangent(pt);\n\t\t\telse if (pmp) s += pmp->Tangent(pt);\n\t\t}\n\t\ts *= f;\n\n\t\t// store average elasticity\n\t\ta << s;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotOsmoticCoefficient::Save(FEDomain &dom, FEDataStream& a)\n{\n    if ((dom.Class() != FE_DOMAIN_SOLID) && (dom.Class() != FE_DOMAIN_SHELL)) return false;\n\n\tFESoluteInterface* pm = dynamic_cast<FESoluteInterface*>(dom.GetMaterial());\n\tif (pm == nullptr) return false;\n\n\tFEOsmoticCoefficient* osm = pm->GetOsmoticCoefficient();\n\tif (osm == nullptr) return false;\n    \n\twriteAverageElementValue<double>(dom, a, [=](const FEMaterialPoint& mp) {\n\t\tdouble c = osm->OsmoticCoefficient(const_cast<FEMaterialPoint&>(mp));\n\t\treturn c;\n\t\t});\n\n    return true;\n}\n"
  },
  {
    "path": "FEBioMix/FEBioMixPlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPlotData.h>\n#include <FECore/FEElement.h>\n#include <FECore/units.h>\n\n//=============================================================================\n//                         S U R F A C E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Local fluid load support\n//!\nclass FEPlotLocalFluidLoadSupport : public FEPlotSurfaceData\n{\npublic:\n    FEPlotLocalFluidLoadSupport(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Effective friction coefficient\n//!\nclass FEPlotEffectiveFrictionCoeff : public FEPlotSurfaceData\n{\npublic:\n    FEPlotEffectiveFrictionCoeff(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid flow rate\n//!\nclass FEPlotMixtureFluidFlowRate : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotMixtureFluidFlowRate(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION) {\n        SetUnits(UNIT_FLOW_RATE);\n    }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//=============================================================================\n//\t\t\t\t\t\t\tD O M A I N   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Specific strain energy\nclass FEPlotMPSpecificStrainEnergy : public FEPlotDomainData\n{\npublic:\n    FEPlotMPSpecificStrainEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_SPECIFIC_ENERGY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Actual fluid pressure\nclass FEPlotActualFluidPressure : public FEPlotDomainData\n{\npublic:\n    FEPlotActualFluidPressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Solid stress\nclass FEPlotSolidStress : public FEPlotDomainData\n{\npublic:\n    FEPlotSolidStress(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid flux\nclass FEPlotFluidFlux : public FEPlotDomainData\n{\npublic:\n\tFEPlotFluidFlux(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_VELOCITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal Fluid flux\nclass FEPlotNodalFluidFlux : public FEPlotDomainData\n{\npublic:\n\tFEPlotNodalFluidFlux(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_MULT) { SetUnits(UNIT_VELOCITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Actual solute concentration\nclass FEPlotActualSoluteConcentration : public FEPlotDomainData\n{\npublic:\n\tFEPlotActualSoluteConcentration(FEModel* pfem);\n\tbool Save(FEDomain& dom, FEDataStream& a);\nprotected:\n\tvector<int>\tm_sol;\n};\n\n//-----------------------------------------------------------------------------\n//! Actual solute concentration\nclass FEPlotPartitionCoefficient : public FEPlotDomainData\n{\npublic:\n    FEPlotPartitionCoefficient(FEModel* pfem);\n    bool Save(FEDomain& dom, FEDataStream& a);\nprotected:\n    vector<int>    m_sol;\n};\n\n//-----------------------------------------------------------------------------\n//! Solute flux (for biphasic solute problems)\nclass FEPlotSoluteFlux : public FEPlotDomainData\n{\npublic:\n\tFEPlotSoluteFlux(FEModel* pfem);\n\tbool Save(FEDomain& dom, FEDataStream& a);\n\nprotected:\n\tvector<int>\tm_sol;\n};\n\n//-----------------------------------------------------------------------------\n//! Solute volumetric flux\nclass FEPlotSoluteVolumetricFlux : public FEPlotDomainData\n{\npublic:\n    FEPlotSoluteVolumetricFlux(FEModel* pfem);\n    bool Save(FEDomain& dom, FEDataStream& a);\n    \nprotected:\n    vector<int>    m_sol;\n};\n\n//-----------------------------------------------------------------------------\n//! Osmolarity\nclass FEPlotOsmolarity : public FEPlotDomainData\n{\npublic:\n    FEPlotOsmolarity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_CONCENTRATION); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotSBMConcentration : public FEPlotDomainData\n{\npublic:\n\tFEPlotSBMConcentration(FEModel* pfem);\n\tbool Save(FEDomain& dom, FEDataStream& a);\nprotected:\n\tvector<int>\tm_sbm;\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotSBMArealConcentration : public FEPlotDomainData\n{\npublic:\n    FEPlotSBMArealConcentration(FEModel* pfem);\n    bool Save(FEDomain& dom, FEDataStream& a);\nprotected:\n    vector<int>    m_sbm;\n};\n\n//-----------------------------------------------------------------------------\n//! Electric potential\nclass FEPlotElectricPotential : public FEPlotDomainData\n{\npublic:\n\tFEPlotElectricPotential(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_VOLTAGE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Current density\nclass FEPlotCurrentDensity : public FEPlotDomainData\n{\npublic:\n\tFEPlotCurrentDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_VEC3F, FMT_ITEM) { SetUnits(UNIT_CURRENT_DENSITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Referential solid volume fraction\nclass FEPlotReferentialSolidVolumeFraction : public FEPlotDomainData\n{\npublic:\n    FEPlotReferentialSolidVolumeFraction(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Porosity\nclass FEPlotPorosity : public FEPlotDomainData\n{\npublic:\n    FEPlotPorosity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Permeability\nclass FEPlotPerm : public FEPlotDomainData\n{\npublic:\n    FEPlotPerm(FEModel* pfem) : FEPlotDomainData(pfem, PLT_MAT3FS, FMT_ITEM) { SetUnits(UNIT_PERMEABILITY); }\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fixed charge density\nclass FEPlotFixedChargeDensity : public FEPlotDomainData\n{\npublic:\n    FEPlotFixedChargeDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_CONCENTRATION); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Referential fixed charge density\nclass FEPlotReferentialFixedChargeDensity : public FEPlotDomainData\n{\npublic:\n\tFEPlotReferentialFixedChargeDensity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_CONCENTRATION); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal effective fluid pressures\nclass FEPlotEffectiveFluidPressure : public FEPlotDomainData\n{\npublic:\n    FEPlotEffectiveFluidPressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_NODE) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal effective downstream fluid pressures\nclass FEPlotEffectiveShellFluidPressure : public FEPlotDomainData\n{\npublic:\n\tFEPlotEffectiveShellFluidPressure(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_NODE) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& m, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal effective solute concentrations (for biphasic-solute problems)\nclass FEPlotEffectiveSoluteConcentration : public FEPlotDomainData\n{\npublic:\n\tFEPlotEffectiveSoluteConcentration(FEModel* pfem);\n\tbool Save(FEDomain& m, FEDataStream& a);\nprotected:\n\tvector<int>\tm_sol;\n};\n\n//-----------------------------------------------------------------------------\n//! Nodal effective solute concentrations (for biphasic-solute problems)\nclass FEPlotEffectiveShellSoluteConcentration : public FEPlotDomainData\n{\npublic:\n\tFEPlotEffectiveShellSoluteConcentration(FEModel* pfem);\n\tbool SetFilter(const char* sz);\n\tbool SetFilter(int nsol);\n\tbool Save(FEDomain& m, FEDataStream& a);\nprotected:\n\tint\t\t\tm_nsol;\n};\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand complex concentration\nclass FEPlotReceptorLigandConcentration : public FEPlotDomainData\n{\npublic:\n    FEPlotReceptorLigandConcentration(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_DENSITY); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for solid-bound molecule referential apparent density\nclass FEPlotSBMRefAppDensity : public FEPlotDomainData\n{\npublic:\n\tFEPlotSBMRefAppDensity(FEModel* pfem);\n\tbool Save(FEDomain& dom, FEDataStream& a);\nprotected:\n\tvector<int>\tm_sbm;\n};\n\n//-----------------------------------------------------------------------------\n//! effective elasticity\nclass FEPlotEffectiveElasticity : public FEPlotDomainData\n{\npublic:\n\tFEPlotEffectiveElasticity(FEModel* pfem) : FEPlotDomainData(pfem, PLT_TENS4FS, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Osmotic coefficient\nclass FEPlotOsmoticCoefficient : public FEPlotDomainData\n{\npublic:\n    FEPlotOsmoticCoefficient(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM){}\n    bool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//=============================================================================\n//                         S U R F A C E   D A T A\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Fluid force\n//!\nclass FEPlotFluidForce : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotFluidForce(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION) {  SetUnits(UNIT_FORCE); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid force\n//! \nclass FEPlotFluidForce2 : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotFluidForce2(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_VEC3F, FMT_REGION) { SetUnits(UNIT_FORCE); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Contact gap for multiphasic interfaces\n//!\nclass FEPlotContactGapMP : public FEPlotSurfaceData\n{\npublic:\n    FEPlotContactGapMP(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_LENGTH); }\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid pressure gap\n//!\nclass FEPlotPressureGap : public FEPlotSurfaceData\n{\npublic:\n\tFEPlotPressureGap(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_ITEM) { SetUnits(UNIT_PRESSURE); }\n\tbool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Fluid load support\n//!\nclass FEPlotFluidLoadSupport : public FEPlotSurfaceData\n{\npublic:\n    FEPlotFluidLoadSupport(FEModel* pfem) : FEPlotSurfaceData(pfem, PLT_FLOAT, FMT_REGION){}\n    bool Save(FESurface& surf, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! concentration gap\n//!\nclass FEPlotConcentrationGap : public FEPlotSurfaceData\n{\npublic:\n    FEPlotConcentrationGap(FEModel* pfem);\n    bool Save(FESurface& surf, FEDataStream& a);\n\nprotected:\n    vector<int>    m_sol;\n};\n\n"
  },
  {
    "path": "FEBioMix/FEBiphasic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasic.h\"\n#include \"FECore/FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\n// Material parameters for the FEBiphasic material\nBEGIN_FECORE_CLASS(FEBiphasic, FEMaterial)\n\tADD_PARAMETER(m_phi0 , FE_RANGE_CLOSED(0.0, 1.0), \"phi0\");\n\tADD_PARAMETER(m_rhoTw, FE_RANGE_GREATER_OR_EQUAL(0.0), \"fluid_density\")->setUnits(UNIT_DENSITY);\n    ADD_PARAMETER(m_tau  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"tau\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pSolid, \"solid\", FEProperty::Required | FEProperty::TopLevel);\n\tADD_PROPERTY(m_pPerm, \"permeability\");\n\tADD_PROPERTY(m_pSupp, \"solvent_supply\", FEProperty::Optional);\n\tADD_PROPERTY(m_pAmom, \"active_supply\", FEProperty::Optional);\n\n    ADD_PROPERTY(m_Q, \"mat_axis\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//============================================================================\n// FEBiphasicMaterialPoint\n//============================================================================\nFEBiphasicMaterialPoint::FEBiphasicMaterialPoint(FEMaterialPointData* ppt) : FEMaterialPointData(ppt) {}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEBiphasicMaterialPoint::Copy()\n{\n\tFEBiphasicMaterialPoint* pt = new FEBiphasicMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_p & m_gradp & m_gradpp;\n\tar & m_w & m_pa & m_phi0 & m_phi0t & m_phi0p & m_phi0hat & m_Jp;\n    ar & m_ss;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicMaterialPoint::Init()\n{\n\tm_p = m_pa = 0;\n\tm_gradp = m_gradpp = vec3d(0,0,0);\n\tm_w = vec3d(0,0,0);\n\tm_phi0 = m_phi0t = m_phi0p = 0;\n\tm_phi0hat = 0;\n\tm_Jp = 1;\n    m_ss.zero();\n\n\tFEMaterialPointData::Init();\n}\n\n//============================================================================\n// FEBiphasic\n//============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEBiphasic constructor\n\nFEBiphasic::FEBiphasic(FEModel* pfem) : FEMaterial(pfem)\n{ \n\tm_rhoTw = 0; \n\tm_phi0 = 0;\n    m_tau = 0;\n\n\tm_pSolid = 0;\n\tm_pPerm = 0;\n\tm_pSupp = 0;\n\tm_pAmom = 0;\n}\n\n//-----------------------------------------------------------------------------\n// initialize\nbool FEBiphasic::Init()\n{\n    if (!m_pSolid->Init()) return false;\n    if (!m_pPerm->Init()) return false;\n    if (m_pSupp && !m_pSupp->Init()) return false;\n    if (m_pAmom && !m_pAmom->Init()) return false;\n    return FEMaterial::Init();\n}\n\n//-----------------------------------------------------------------------------\n// returns a pointer to a new material point object\nFEMaterialPointData* FEBiphasic::CreateMaterialPointData()\n{\n\t// create the solid material point\n\tFEMaterialPointData* ep = m_pSolid->CreateMaterialPointData();\n\n    // create biphasic material point\n    FEBiphasicMaterialPoint* pt = new FEBiphasicMaterialPoint(ep);\n    \n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\n// update specialized material points\nvoid FEBiphasic::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    m_pSolid->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pPerm->UpdateSpecializedMaterialPoints(mp, tp);\n    if (m_pSupp) m_pSupp->UpdateSpecializedMaterialPoints(mp, tp);\n    if (m_pAmom) m_pAmom->UpdateSpecializedMaterialPoints(mp, tp);\n}\n\n//-----------------------------------------------------------------------------\n//! Porosity in current configuration\ndouble FEBiphasic::Porosity(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pet = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// porosity\n//\tdouble phiw = 1 - m_phi0/J;\n\tdouble phi0 = pet.m_phi0t;\n\tdouble phiw = 1 - phi0/J;\n\t// check for pore collapse\n\t// TODO: throw an error if pores collapse\n\tphiw = (phiw > 0) ? phiw : 0;\n\t\n\treturn phiw;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a poro-elastic material is the sum of the fluid pressure\n//! and the elastic stress. Note that this function is declared in the base class\n//! so you do not have to reimplement it in a derived class, unless additional\n//! pressure terms are required.\n\nmat3ds FEBiphasic::Stress(FEMaterialPoint& mp)\n{\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// calculate solid material stress\n\tmat3ds s = m_pSolid->Stress(mp);\n\t\n\t// add fluid pressure\n\ts.xx() -= pt.m_p;\n\ts.yy() -= pt.m_p;\n\ts.zz() -= pt.m_p;\n\t\n\treturn s;\n}\n\nmat3ds FEBiphasic::SecantStress(FEMaterialPoint& mp)\n{\n    FEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n    \n    // calculate solid material stress\n    mat3ds s = m_pSolid->SecantStress(mp);\n    \n    // add fluid pressure\n    s.xx() -= pt.m_p;\n    s.yy() -= pt.m_p;\n    s.zz() -= pt.m_p;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent is the sum of the elastic tangent plus the fluid tangent. Note\n//! that this function is declared in the base class, so you don't have to \n//! reimplement it unless additional tangent components are required.\n\ntens4dmm FEBiphasic::Tangent(FEMaterialPoint& mp)\n{\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// call solid tangent routine\n\ttens4dmm c = m_pSolid->Tangent(mp);\n\t\n\t// fluid pressure\n\tdouble p = pt.m_p;\n\t\n\t// adjust tangent for pressures\n\tdouble D[6][6] = {0};\n\tc.extract(D);\n\t\n\tD[0][0] -= -p;\n\tD[1][1] -= -p;\n\tD[2][2] -= -p;\n\t\n\tD[0][1] -= p; D[1][0] -= p;\n\tD[1][2] -= p; D[2][1] -= p;\n\tD[0][2] -= p; D[2][0] -= p;\n\t\n\tD[3][3] -= -p;\n\tD[4][4] -= -p;\n\tD[5][5] -= -p;\n\t\n\treturn tens4dmm(D);\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent is the sum of the elastic tangent plus the fluid tangent. Note\n//! that this function is declared in the base class, so you don't have to\n//! reimplement it unless additional tangent components are required.\n\ntens4dmm FEBiphasic::SecantTangent(FEMaterialPoint& mp)\n{\n    FEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n    \n    // call solid tangent routine\n    tens4dmm c = m_pSolid->SecantTangent(mp);\n    \n    // fluid pressure\n    double p = pt.m_p;\n    \n    // adjust tangent for pressures\n    double D[6][6] = {0};\n    c.extract(D);\n    \n    D[0][0] -= -p;\n    D[1][1] -= -p;\n    D[2][2] -= -p;\n    \n    D[0][1] -= p; D[1][0] -= p;\n    D[1][2] -= p; D[2][1] -= p;\n    D[0][2] -= p; D[2][0] -= p;\n    \n    D[3][3] -= -p;\n    D[4][4] -= -p;\n    D[5][5] -= -p;\n    \n    return tens4dmm(D);\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure (same as effective pressure here)\n\ndouble FEBiphasic::Pressure(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\treturn ppt.m_p;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the permeability tensor as a double array\n\nvoid FEBiphasic::Permeability(double k[3][3], FEMaterialPoint& pt)\n\n{\n\tmat3ds kt = m_pPerm->Permeability(pt);\n\t\n\tk[0][0] = kt.xx();\n\tk[1][1] = kt.yy();\n\tk[2][2] = kt.zz();\n\tk[0][1] = k[1][0] = kt.xy();\n\tk[1][2] = k[2][1] = kt.yz();\n\tk[2][0] = k[0][2] = kt.xz();\n\t\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEBiphasic::Permeability(FEMaterialPoint& mp)\n{\n    return m_pPerm->Permeability(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! return tangent of permeability with strain\ntens4dmm FEBiphasic::Tangent_Permeability_Strain(FEMaterialPoint& mp)\n{\n    return m_pPerm->Tangent_Permeability_Strain(mp);\n}\n\n//! return the material permeability property\nmat3ds FEBiphasic::MaterialPermeability(FEMaterialPoint& mp, const mat3ds E)\n{\n    // Evaluate right Cauchy-Green tensor from E\n    mat3ds C = mat3dd(1) + E*2;\n    \n    // Evaluate right stretch tensor U from C\n    vec3d v[3];\n    double lam[3];\n    C.eigen2(lam, v);\n    lam[0] = sqrt(lam[0]); lam[1] = sqrt(lam[1]); lam[2] = sqrt(lam[2]);\n    mat3ds U = dyad(v[0])*lam[0] + dyad(v[1])*lam[1] + dyad(v[2])*lam[2];\n    double J = lam[0]*lam[1]*lam[2];\n    \n    // temporarily replace F in material point with U\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3ds Ui = dyad(v[0])/lam[0] + dyad(v[1])/lam[1] + dyad(v[2])/lam[2];\n    mat3d Fsafe = pt.m_F;\n    double Jsafe = pt.m_J;\n    pt.m_F = U;\n    pt.m_J = J;\n    \n    // Evaluate hydraulic permeability\n    mat3ds k = Permeability(mp);\n    \n    // Restore original F\n    pt.m_F = Fsafe;\n    pt.m_J = Jsafe;\n    \n    // Convert spatial permeability to material permeability\n    mat3ds K = (Ui*k*Ui).sym()*J;\n\n    return K;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial tangent stiffness at material point, using secant method\ntens4dmm FEBiphasic::SecantTangent_Permeability_Strain(FEMaterialPoint& mp)\n{\n    // extract the deformation gradient\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    mat3d F = pt.m_F;\n    double J = pt.m_J;\n    mat3ds E = pt.Strain();\n    mat3dd I(1);\n    \n    // calculate the material permeability at the current deformation gradient\n    mat3ds K = MaterialPermeability(mp,E);\n    \n    // create deformation gradient increment\n    double eps = 1e-9;\n    vec3d e[3];\n    e[0] = vec3d(1,0,0); e[1] = vec3d(0,1,0); e[2] = vec3d(0,0,1);\n    tens4dmm Kmm;\n    for (int k=0; k<3; ++k) {\n        // evaluate incremental material permeability\n        mat3ds dE = dyads(e[k], e[k])*(eps/2);\n        mat3ds dK = (MaterialPermeability(mp,E+dE) - K)/eps;\n        \n        // evaluate the secant modulus\n        Kmm(0,0,k,k) = dK.xx();\n        Kmm(1,1,k,k) = dK.yy();\n        Kmm(2,2,k,k) = dK.zz();\n        Kmm(0,1,k,k) = Kmm(1,0,k,k) = dK.xy();\n        Kmm(1,2,k,k) = Kmm(2,1,k,k) = dK.yz();\n        Kmm(2,0,k,k) = Kmm(0,2,k,k) = dK.xz();\n        for (int l=0; l<3; ++l) {\n            if (l != k) {\n                // evaluate incremental material permeability\n                mat3ds dE = dyads(e[k], e[l])*(eps/2);\n                mat3ds dK = (MaterialPermeability(mp,E+dE) - K)/eps;\n                \n                // evaluate the secant modulus\n                Kmm(0,0,k,l) = Kmm(0,0,l,k) = dK.xx();\n                Kmm(1,1,k,l) = Kmm(1,1,l,k) = dK.yy();\n                Kmm(2,2,k,l) = Kmm(2,2,l,k) = dK.zz();\n                Kmm(0,1,k,l) = Kmm(0,1,l,k) = Kmm(1,0,k,l) = Kmm(1,0,l,k) = dK.xy();\n                Kmm(1,2,k,l) = Kmm(1,2,l,k) = Kmm(2,1,k,l) = Kmm(2,1,l,k) = dK.yz();\n                Kmm(2,0,k,l) = Kmm(2,0,l,k) = Kmm(0,2,k,l) = Kmm(0,2,l,k) = dK.xz();\n            }\n        }\n    }\n    \n    return Kmm.pp(F)/J;\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include \"FEHydraulicPermeability.h\"\n#include \"FESolventSupply.h\"\n#include \"FEActiveMomentumSupply.h\"\n#include <FEBioMech/FEBodyForce.h>\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Biphasic material point class.\n//\nclass FEBIOMIX_API FEBiphasicMaterialPoint : public FEMaterialPointData\n{\npublic:\n\t//! constructor\n\tFEBiphasicMaterialPoint(FEMaterialPointData* ppt);\n\n\t//! create a shallow copy\n\tFEMaterialPointData* Copy() override;\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Data initialization\n\tvoid Init() override;\n\npublic:\n\t// poro-elastic material data\n\t// The actual fluid pressure is the same as the effective fluid pressure\n\t// in a poroelastic material without solute(s).  The actual fluid pressure\n\t// is included here so that models that include both poroelastic and\n\t// solute-poroelastic domains produce plotfiles with consistent fluid\n\t// pressure fields.\n\tdouble\t\tm_p;\t\t//!< fluid pressure\n\tvec3d\t\tm_gradp;\t//!< spatial gradient of p\n    vec3d\t\tm_gradpp;\t//!< gradp at previous time\n\tvec3d\t\tm_w;\t\t//!< fluid flux\n\tdouble\t\tm_pa;\t\t//!< actual fluid pressure\n    double      m_phi0;     //!< referential solid volume fraction at initial time\n\tdouble\t\tm_phi0t;\t//!< referential solid volume fraction at current time\n\tdouble\t\tm_phi0p;\t//!< referential solid volume fraction at previous time\n\tdouble\t\tm_phi0hat;\t//!< referential solid volume fraction supply at current time\n    double      m_Jp;       //!< determinant of solid deformation gradient at previous time\n    mat3ds      m_ss;       //!< solid (elastic or effective) stress\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FEBiphasicInterface\n{\npublic:\n\tFEBiphasicInterface() {}\n\tvirtual ~FEBiphasicInterface() {}\n\npublic:\n\tvirtual double GetReferentialSolidVolumeFraction(const FEMaterialPoint& mp) { return 0.0; }\n\n\t// TODO: These are only used by multiphasic materials. Perhapse move to separate interface class? \n\t//! solid referential apparent density\n\tvirtual double SolidReferentialApparentDensity(FEMaterialPoint& pt) { return 0.0; }\n\n\t//! solid referential volume fraction\n\tvirtual double SolidReferentialVolumeFraction(FEMaterialPoint& pt) { return 0.0; };\n    \n    virtual double TangentSRVFStrain(FEMaterialPoint& pt) { return 0.0; }\n\n\t// TODO: This is a bit of a hack to get the fluid pressure. \n\tvirtual double GetActualFluidPressure(const FEMaterialPoint& pt) { return 0.0; }\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for biphasic materials.\n\nclass FEBIOMIX_API FEBiphasic : public FEMaterial, public FEBiphasicInterface\n{\npublic:\n\tFEBiphasic(FEModel* pfem);\n\t\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// Get the elastic component (overridden from FEMaterial)\n\tFEElasticMaterial* GetElasticMaterial() { return m_pSolid; }\n\t\npublic:\n    //! initialize\n    bool Init() override;\n    \n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt);\n\t\n    //! calculate secant stress at material point\n    mat3ds SecantStress(FEMaterialPoint& pt);\n    \n\t//! calculate tangent stiffness at material point\n\ttens4dmm Tangent(FEMaterialPoint& pt);\n\n    //! calculate secant tangent stiffness at material point\n    tens4dmm SecantTangent(FEMaterialPoint& pt);\n    \n\t//! return the permeability tensor as a matrix\n\tvoid Permeability(double k[3][3], FEMaterialPoint& pt);\n\n\t//! return the permeability as a tensor\n\tmat3ds Permeability(FEMaterialPoint& pt);\n\n    //! return tangent of permeability with strain\n    tens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp);\n    \n    //! return secant tangent of permeability with strain\n    tens4dmm SecantTangent_Permeability_Strain(FEMaterialPoint& mp);\n    \n\t//! return the permeability property\n\tFEHydraulicPermeability* GetPermeability() { return m_pPerm; }\n\t\n    //! return the material permeability property\n    mat3ds MaterialPermeability(FEMaterialPoint& mp, const mat3ds E);\n    \n\t//! calculate actual fluid pressure\n\tdouble Pressure(FEMaterialPoint& pt);\n\n\t//! porosity\n\tdouble Porosity(FEMaterialPoint& pt);\n\t\n    //! solid density\n    double SolidDensity(FEMaterialPoint& mp) { return m_pSolid->Density(mp); }\n    \n\t//! fluid density\n\tdouble FluidDensity() { return m_rhoTw; }\n\n\t//! get the solvent supply\n\tdouble SolventSupply(FEMaterialPoint& mp) { return (m_pSupp? m_pSupp->Supply(mp) : 0); }\n\n\t//! get the solvent supply property\n\tFESolventSupply* GetSolventSupply() { return m_pSupp; }\n\n\t//! Get the active momentum supply\n\tFEActiveMomentumSupply* GetActiveMomentumSupply() { return m_pAmom; }\n\npublic: // overridden from FEBiphasicInterface\n\n\tdouble GetReferentialSolidVolumeFraction(const FEMaterialPoint& mp) override {\n\t\tconst FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\treturn pt->m_phi0t;\n\t}\n\n\tdouble GetActualFluidPressure(const FEMaterialPoint& mp) override { \n\t\tconst FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\treturn pt->m_pa;\n\t}\n\n    //! evaluate and return solid referential volume fraction\n    double SolidReferentialVolumeFraction(FEMaterialPoint& mp) override {\n        double phisr = m_phi0(mp);\n        FEBiphasicMaterialPoint* bp = (mp.ExtractData<FEBiphasicMaterialPoint>());\n        bp->m_phi0 = bp->m_phi0t = phisr;\n        return phisr;\n    };\n    \npublic: // material parameters\n\tdouble\t\t\t\t\t\tm_rhoTw;\t//!< true fluid density\n\tFEParamDouble               m_phi0;\t\t//!< solid volume fraction in reference configuration\n    double                      m_tau;      //!< characteristic time constant for stabilization\n    vector<FEBodyForce*>        m_bf;       //!< body forces acting on this biphasic material\n\nprivate: // material properties\n\tFEElasticMaterial*\t\t\tm_pSolid;\t//!< pointer to elastic solid material\n\tFEHydraulicPermeability*\tm_pPerm;\t//!< pointer to permeability material\n\tFESolventSupply*\t\t\tm_pSupp;\t//!< pointer to solvent supply\n\tFEActiveMomentumSupply*\t\tm_pAmom;\t//!< pointer to active momentum supply\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBiphasicAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEBiphasicAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0TRANSIENT\\0\");\nEND_FECORE_CLASS()\n\nFEBiphasicAnalysis::FEBiphasicAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEBiphasicAnalysis : public FEAnalysis\n{\npublic:\n\tenum BiphasicAnalysisType{\n\t\tSTEADY_STATE,\n\t\tTRANSIENT\n\t};\n\npublic:\n\tFEBiphasicAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicContactSurface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicContactSurface.h\"\n#include \"FEBiphasic.h\"\n#include <FECore/FEModel.h>\n\nvoid FEBiphasicContactPoint::Serialize(DumpStream& ar)\n{\n    FEContactMaterialPoint::Serialize(ar);\n    ar & m_dg;\n    ar & m_Lmd;\n    ar & m_Lmt;\n    ar & m_epsn;\n    ar & m_epsp;\n    ar & m_p1;\n    ar & m_nu;\n    ar & m_s1;\n    ar & m_tr;\n    ar & m_rs;\n    ar & m_rsp;\n    ar & m_bstick;\n    ar & m_Lmp & m_pg & m_mueff & m_fls;\n}\n\n//-----------------------------------------------------------------------------\nFEBiphasicContactSurface::FEBiphasicContactSurface(FEModel* pfem) : FEContactSurface(pfem)\n{\n\tm_dofP = -1;\n}\n\n//-----------------------------------------------------------------------------\nFEBiphasicContactSurface::~FEBiphasicContactSurface()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEBiphasicContactSurface::Init()\n{\n\t// I want to use the FEModel class for this, but don't know how\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tm_dofP = dofs.GetDOF(\"p\");\n\treturn FEContactSurface::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEBiphasicContactSurface::Serialize(DumpStream& ar)\n{\n\tFEContactSurface::Serialize(ar);\n\tif (ar.IsShallow() == false) ar & m_dofP;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEBiphasicContactSurface::GetFluidForce()\n{\n\tassert(false);\n    return vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEBiphasicContactSurface::GetFluidLoadSupport()\n{\n    int n, i;\n    \n    // initialize contact force\n    double FLS = 0;\n    double A = 0;\n    \n    // loop over all elements of the surface\n    for (n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        // evaluate the fluid force for that element\n        for (i=0; i<el.GaussPoints(); ++i)\n        {\n            FEBiphasicContactPoint *cp = dynamic_cast<FEBiphasicContactPoint*>(el.GetMaterialPoint(i));\n            if (cp) {\n                double w = el.GaussWeights()[i];\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n                // normal (magnitude = area)\n                vec3d n = g[0] ^ g[1];\n                double da = n.norm();\n                FLS += cp->m_fls*w*da;\n            }\n        }\n    }\n    \n    A = GetContactArea();\n    \n    return (A > 0) ? FLS/A : 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicContactSurface::GetMuEffective(int nface, double& pg)\n{\n    pg = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicContactSurface::GetLocalFLS(int nface, double& pg)\n{\n    pg = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicContactSurface::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint N = el.Nodes();\n\tlm.assign(N*4, -1);\n\n\t// pack the equation numbers\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3*i  ] = id[m_dofX];\n\t\tlm[3*i+1] = id[m_dofY];\n\t\tlm[3*i+2] = id[m_dofZ];\n\n\t\t// now the pressure dofs\n\t\tif (m_dofP >= 0) lm[3*N+i] = id[m_dofP];\n\t}\n}\n//-----------------------------------------------------------------------------\n// Evaluate the local fluid load support projected from the element to the surface Gauss points\nvoid FEBiphasicContactSurface::GetGPLocalFLS(int nface, double* pt, double pamb)\n{\n    FESurfaceElement& el = Element(nface);\n    FEElement* e = el.m_elem[0].pe;\n    FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n    if (se) {\n        mat3ds s; s.zero();\n        double p = 0;\n        for (int i=0; i<se->GaussPoints(); ++i) {\n            FEMaterialPoint* pt = se->GetMaterialPoint(i);\n            FEElasticMaterialPoint* ep = pt->ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint* bp = pt->ExtractData<FEBiphasicMaterialPoint>();\n            if (ep) s += ep->m_s;\n            if (bp) p += bp->m_p;\n        }\n        s /= se->GaussPoints();\n        p /= se->GaussPoints();\n        // account for ambient pressure\n        p -= pamb;\n        // evaluate FLS at integration points of that face\n        for (int i=0; i<el.GaussPoints(); ++i) {\n            double *H = el.H(i);\n            pt[i] = 0;\n            for (int j=0; j<el.Nodes(); ++j) {\n                vec3d n = SurfaceNormal(el, j);\n                double tn = n*(s*n);\n                double fls = (tn != 0) ? -p/tn : 0;\n                pt[i] += fls*H[j];\n            }\n        }\n    }\n    else\n        for (int i=0; i<el.Nodes(); ++i) pt[i] = 0;\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicContactSurface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEContactSurface.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FEBiphasicContactPoint : public FEContactMaterialPoint\n{\npublic:\n    vec3d   m_dg;       //!< vector gap\n    double  m_Lmd;      //!< Lagrange multipliers for normal traction\n    vec3d   m_Lmt;      //!< Lagrange multipliers for vector traction\n    double  m_epsn;     //!< penalty factor\n    double  m_epsp;     //!< pressure penalty factor\n    double  m_p1;       //!< fluid pressure\n    vec3d   m_nu;       //!< normal at integration points\n    vec3d   m_s1;       //!< tangent along slip direction\n    vec3d   m_tr;       //!< contact traction\n    vec2d   m_rs;       //!< natural coordinates of projection\n    vec2d   m_rsp;      //!< m_rs at the previous time step\n    bool    m_bstick;   //!< stick flag\n\tdouble  m_Lmp;      //!< lagrange multipliers for fluid pressures\n\tdouble\tm_pg;       //!< pressure \"gap\" for biphasic contact\n    double  m_mueff;    //!< effective friction coefficient\n    double  m_fls;      //!< local fluid load support\n\n\tvoid Init() override\n\t{\n\t\tFEContactMaterialPoint::Init();\n        m_Lmd   = 0.0;\n        m_Lmt   = m_tr = vec3d(0,0,0);\n        m_Lmp   = 0.0;\n        m_epsn  = 1.0;\n        m_epsp  = 1.0;\n        m_pg    = 0.0;\n        m_p1    = 0.0;\n        m_mueff = 0.0;\n        m_fls   = 0.0;\n        m_nu    = m_s1 = m_dg = vec3d(0,0,0);\n        m_rs    = m_rsp = vec2d(0,0);\n        m_bstick = false;\n\t\tm_Lmp = 0.0;\n\t\tm_pg = 0.0;\n\t\tm_mueff = 0.0;\n\t\tm_fls = 0.0;\n\t}\n\n\tvoid Serialize(DumpStream& ar) override;\n};\n\n//-----------------------------------------------------------------------------\n//! This class describes a contact surface used in a biphasic/multiphasic analysis.\nclass FEBIOMIX_API FEBiphasicContactSurface : public FEContactSurface\n{\npublic:\n\t//! constructor\n\tFEBiphasicContactSurface(FEModel* pfem);\n\n\t//! destructor\n\t~FEBiphasicContactSurface();\n\n\t//! initialization\n\tbool Init();\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar);\n\n\tvoid UnpackLM(FEElement& el, vector<int>& lm);\n\npublic:\n\t//! Get the total force exerted by the fluid\n    virtual vec3d GetFluidForce();\n\n    //! Get the total force exerted by the fluid\n    virtual double GetFluidLoadSupport();\n    \n    //! Get the effective friction coefficient\n    virtual void GetMuEffective    (int nface, double& pg);\n    \n    //! Get the local fluid load support\n    virtual void GetLocalFLS       (int nface, double& pg);\n    \n    //! Get the local fluid load support projected from the element to the surface Gauss points\n    void GetGPLocalFLS(int nface, double* pt, double ambp = 0);\n\nprotected:\n\tint\tm_dofP;\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicDomain.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFEBiphasicDomain::FEBiphasicDomain(FEModel* pfem) : FEElasticDomain(pfem)\n{\n    m_pMat = 0;\n\tif (pfem)\n\t{\n\t\tm_dofP = pfem->GetDOFIndex(\"p\");\n\t\tm_dofQ = pfem->GetDOFIndex(\"q\");\n\n\t\tm_dofVX = pfem->GetDOFIndex(\"vx\");\n\t\tm_dofVY = pfem->GetDOFIndex(\"vy\");\n\t\tm_dofVZ = pfem->GetDOFIndex(\"vz\");\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <FEBioMech/FEElasticDomain.h>\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalVector;\nclass FEBodyForce;\nclass FESolver;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for biphasic domains.\n\n//! A biphasic domain is used by the biphasic solver.\n//! This interface defines the functions that have to be implemented by a\n//! biphasic domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOMIX_API FEBiphasicDomain : public FEElasticDomain\n{\npublic:\n    FEBiphasicDomain(FEModel* pfem);\n    \n    // --- R E S I D U A L ---\n    \n    //! internal work for steady-state case\n    virtual void InternalForcesSS(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! calculates the global stiffness matrix for this domain\n    virtual void StiffnessMatrix(FELinearSystem& LS, bool bsymm) = 0;\n    \n    //! calculates the global stiffness matrix (steady-state case)\n    virtual void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) = 0;\n    \npublic: // biphasic domain \"properties\"\n    virtual vec3d FluidFlux(FEMaterialPoint& mp) = 0;\n\nprotected:\n    FEBiphasic*\tm_pMat;\n    int\t\t\tm_dofP;\t\t//!< pressure dof index\n    int\t\t\tm_dofQ;     //!< shell extra pressure dof index\n    int\t\t\tm_dofVX;\n    int\t\t\tm_dofVY;\n    int\t\t\tm_dofVZ;\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicModule.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBiphasicModule.h\"\n#include <FECore/DOFS.h>\n#include <FECore/FEModel.h>\n#include \"FEBioMix.h\"\n\n//=============================================================================\nFEBiphasicModule::FEBiphasicModule() {}\n\nvoid FEBiphasicModule::InitModel(FEModel* fem)\n{\n\tFESolidModule::InitModel(fem);\n\n\t// Allocate degrees of freedom\n\tDOFS& dofs = fem->GetDOFS();\n\tint varP = dofs.AddVariable(\"fluid pressure\");\n\tdofs.SetDOFName(varP, 0, \"p\");\n\tint varQ = dofs.AddVariable(\"shell fluid pressure\");\n\tdofs.SetDOFName(varQ, 0, \"q\");\n}\n\n//=============================================================================\nFEBiphasicSoluteModule::FEBiphasicSoluteModule() {}\n\nvoid FEBiphasicSoluteModule::InitModel(FEModel* fem)\n{\n\tFEBiphasicModule::InitModel(fem);\n\n\t// Allocate degrees of freedom\n\t// (We start with zero concentration degrees of freedom)\n\tDOFS& dofs = fem->GetDOFS();\n\tint varC = dofs.AddVariable(\"concentration\", VAR_ARRAY);\n\tint varD = dofs.AddVariable(\"shell concentration\", VAR_ARRAY);\n}\n\n//=============================================================================\nFEMultiphasicModule::FEMultiphasicModule() {}\n\nvoid FEMultiphasicModule::InitModel(FEModel* fem)\n{\n\tFEBiphasicSoluteModule::InitModel(fem);\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicModule.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEModule.h>\n#include <FEBioMech/FESolidModule.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEBiphasicModule : public FESolidModule\n{\npublic:\n\tFEBiphasicModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n\nclass FEBIOMIX_API FEBiphasicSoluteModule : public FEBiphasicModule\n{\npublic:\n\tFEBiphasicSoluteModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n\nclass FEBIOMIX_API FEMultiphasicModule : public FEBiphasicSoluteModule\n{\npublic:\n\tFEMultiphasicModule();\n\tvoid InitModel(FEModel* fem) override;\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicShellDomain.h\"\n#include \"FECore/FEMesh.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FELinearSystem.h>\n\nBEGIN_FECORE_CLASS(FEBiphasicShellDomain, FESSIShellDomain)\n    ADD_PARAMETER(m_secant_stress, \"secant_stress\");\n    ADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\n    ADD_PARAMETER(m_secant_perm_tangent, \"secant_permeability_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEBiphasicShellDomain::FEBiphasicShellDomain(FEModel* pfem) : FESSIShellDomain(pfem), FEBiphasicDomain(pfem), m_dof(pfem)\n{\n\tif (pfem)\n\t{\n\t\tm_dofSX = pfem->GetDOFIndex(\"sx\");\n\t\tm_dofSY = pfem->GetDOFIndex(\"sy\");\n\t\tm_dofSZ = pfem->GetDOFIndex(\"sz\");\n\t}\n    m_secant_stress = false;\n    m_secant_tangent = false;\n    m_secant_perm_tangent = false;\n}\n\n//-----------------------------------------------------------------------------\n//! get the material (overridden from FEDomain)\nFEMaterial* FEBiphasicShellDomain::GetMaterial()\n{\n\treturn m_pMat;\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof\nconst FEDofList& FEBiphasicShellDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FEBiphasic*>(pmat);\n    assert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEBiphasicShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    // initialize base class\n    FESSIShellDomain::PreSolveUpdate(timeInfo);\n\n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    double pn[NE], qn[NE], p;\n    FEMesh& m = *GetMesh();\n    for (size_t iel=0; iel<m_Elem.size(); ++iel)\n    {\n        FEShellElement& el = m_Elem[iel];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n            xt[i] = m.Node(el.m_node[i]).m_rt;\n            pn[i] = m.Node(el.m_node[i]).get(m_dofP);\n            qn[i] = m.Node(el.m_node[i]).get(m_dofQ);\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            r0 = el.Evaluate(x0, j);\n            rt = el.Evaluate(xt, j);\n            p = evaluate(el, pn, qn, j);\n            \n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pb = *mp.ExtractData<FEBiphasicMaterialPoint>();\n            mp.m_r0 = r0;\n            mp.m_rt = rt;\n            \n            pt.m_J = defgrad(el, pt.m_F, j);\n            \n            pb.m_Jp = pt.m_J;\n            \n            pb.m_p = p;\n            pb.m_gradp = gradient(el, pn, qn, j);\n            pb.m_gradpp = pb.m_gradp;\n            pb.m_phi0p = pb.m_phi0t;\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::Activate()\n{\n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n                \n                if (node.HasFlags(FENode::SHELL))\n                {\n                    node.set_active(m_dofSU[0]);\n                    node.set_active(m_dofSU[1]);\n                    node.set_active(m_dofSU[2]);\n                }\n            }\n            \n            node.set_active(m_dofP);\n            \n            if (node.HasFlags(FENode::SHELL))\n                node.set_active(m_dofQ);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEBiphasicShellDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int N = el.Nodes();\n    lm.resize(N*11);\n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        FENode& node = m_pMesh->Node(n);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[8*i  ] = id[m_dofU[0]];\n        lm[8*i+1] = id[m_dofU[1]];\n        lm[8*i+2] = id[m_dofU[2]];\n        \n        // next the shell dofs\n        lm[8*i+3] = id[m_dofSX];\n        lm[8*i+4] = id[m_dofSY];\n        lm[8*i+5] = id[m_dofSZ];\n        \n        // now the pressure dofs\n        lm[8*i+6] = id[m_dofP];\n        lm[8*i+7] = id[m_dofQ];\n        \n        // rigid rotational dofs\n        // TODO: Do I really need this?\n        lm[8*N + 3*i  ] = id[m_dofR[0]];\n        lm[8*N + 3*i+1] = id[m_dofR[1]];\n        lm[8*N + 3*i+2] = id[m_dofR[2]];\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::Reset()\n{\n    // reset base class data\n    FESSIShellDomain::Reset();\n    \n    // get the biphasic material\n    FEBiphasic* pmb = m_pMat;\n    \n    // initialize all element data\n\tForEachMaterialPoint([=](FEMaterialPoint& mp) {\n\t\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\n\t\t// initialize referential solid volume fraction\n\t\tpt.m_phi0 = pt.m_phi0t = pmb->m_phi0(mp);\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 8*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for shell elements\n\nvoid FEBiphasicShellDomain::ElementInternalForce(FEShellElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    vec3d gradM, gradMu, gradMd;\n    double Mu, Md;\n    mat3ds s;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // next we get the determinant\n        double Jp = bpt.m_Jp;\n        double J = pt.m_J;\n        \n        // and then finally\n        double divv = ((J-Jp)/dt)/J;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solvent supply\n        double phiwhat = m_pMat->SolventSupply(mp);\n        \n        for (i=0; i<neln; ++i)\n        {\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            vec3d fu = s*gradMu;\n            vec3d fd = s*gradMd;\n            Mu = (1+eta)/2*M[i];\n            Md = (1-eta)/2*M[i];\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[8*i  ] -= fu.x*detJt;\n            fe[8*i+1] -= fu.y*detJt;\n            fe[8*i+2] -= fu.z*detJt;\n            \n            fe[8*i+3] -= fd.x*detJt;\n            fe[8*i+4] -= fd.y*detJt;\n            fe[8*i+5] -= fd.z*detJt;\n            \n            fe[8*i+6] -= dt*(w*gradMu + (phiwhat - divv)*Mu)*detJt;\n            fe[8*i+7] -= dt*(w*gradMd + (phiwhat - divv)*Md)*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 8*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for shell elements\n//! stead-state analysis\n\nvoid FEBiphasicShellDomain::ElementInternalForceSS(FEShellElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    vec3d gradM, gradMu, gradMd;\n    double Mu, Md;\n    mat3ds s;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solvent supply\n        double phiwhat = m_pMat->SolventSupply(mp);\n        \n        for (i=0; i<neln; ++i)\n        {\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            vec3d fu = s*gradMu;\n            vec3d fd = s*gradMd;\n            Mu = (1+eta)/2*M[i];\n            Md = (1-eta)/2*M[i];\n            \n            // calculate internal force\n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[8*i  ] -= fu.x*detJt;\n            fe[8*i+1] -= fu.y*detJt;\n            fe[8*i+2] -= fu.z*detJt;\n            \n            fe[8*i+3] -= fd.x*detJt;\n            fe[8*i+4] -= fd.y*detJt;\n            fe[8*i+5] -= fd.z*detJt;\n            \n            fe[8*i+6] -= dt*(w*gradMu + phiwhat*Mu)*detJt;\n            fe[8*i+7] -= dt*(w*gradMd + phiwhat*Md)*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared(NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = neln*8;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementBiphasicStiffness(el, ke, bsymm);\n        \n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for shared(NE)\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = neln*8;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementBiphasicStiffnessSS(el, ke, bsymm);\n        \n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FEBiphasicShellDomain::ElementBiphasicStiffness(FEShellElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    double tau = m_pMat->m_tau;\n    \n    vector<vec3d> gradMu(neln), gradMd(neln);\n    vector<double> Mu(neln), Md(neln);\n    vec3d gradM;\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Md[i] = (1-eta)/2*M[i];\n        }\n        \n        // get the stress\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4dmm c = (m_secant_tangent) ? m_pMat->SecantTangent(mp) : m_pMat->Tangent(mp);\n\n        // get the fluid flux and pressure gradient\n        vec3d gradp = pt.m_gradp + (pt.m_gradp - pt.m_gradpp)*(tau/dt);\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->Permeability(mp);\n        tens4dmm dKdE = (m_secant_perm_tangent) ? m_pMat->SecantTangent_Permeability_Strain(mp) : m_pMat->Tangent_Permeability_Strain(mp);\n\n        // evaluate the solvent supply and its derivatives\n        double phiwhat = 0;\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        if (m_pMat->GetSolventSupply()) {\n            phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        \n        // Kuu matrix\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                mat3d Kuu = (mat3dd(gradMu[i]*(s*gradMu[j])) + vdotTdotv(gradMu[i], c, gradMu[j]))*detJt;\n                mat3d Kud = (mat3dd(gradMu[i]*(s*gradMd[j])) + vdotTdotv(gradMu[i], c, gradMd[j]))*detJt;\n                mat3d Kdu = (mat3dd(gradMd[i]*(s*gradMu[j])) + vdotTdotv(gradMd[i], c, gradMu[j]))*detJt;\n                mat3d Kdd = (mat3dd(gradMd[i]*(s*gradMd[j])) + vdotTdotv(gradMd[i], c, gradMd[j]))*detJt;\n                \n                ke[8*i  ][8*j  ] += Kuu[0][0]; ke[8*i  ][8*j+1] += Kuu[0][1]; ke[8*i  ][8*j+2] += Kuu[0][2];\n                ke[8*i+1][8*j  ] += Kuu[1][0]; ke[8*i+1][8*j+1] += Kuu[1][1]; ke[8*i+1][8*j+2] += Kuu[1][2];\n                ke[8*i+2][8*j  ] += Kuu[2][0]; ke[8*i+2][8*j+1] += Kuu[2][1]; ke[8*i+2][8*j+2] += Kuu[2][2];\n                \n                ke[8*i  ][8*j+3] += Kud[0][0]; ke[8*i  ][8*j+4] += Kud[0][1]; ke[8*i  ][8*j+5] += Kud[0][2];\n                ke[8*i+1][8*j+3] += Kud[1][0]; ke[8*i+1][8*j+4] += Kud[1][1]; ke[8*i+1][8*j+5] += Kud[1][2];\n                ke[8*i+2][8*j+3] += Kud[2][0]; ke[8*i+2][8*j+4] += Kud[2][1]; ke[8*i+2][8*j+5] += Kud[2][2];\n                \n                ke[8*i+3][8*j  ] += Kdu[0][0]; ke[8*i+3][8*j+1] += Kdu[0][1]; ke[8*i+3][8*j+2] += Kdu[0][2];\n                ke[8*i+4][8*j  ] += Kdu[1][0]; ke[8*i+4][8*j+1] += Kdu[1][1]; ke[8*i+4][8*j+2] += Kdu[1][2];\n                ke[8*i+5][8*j  ] += Kdu[2][0]; ke[8*i+5][8*j+1] += Kdu[2][1]; ke[8*i+5][8*j+2] += Kdu[2][2];\n                \n                ke[8*i+3][8*j+3] += Kdd[0][0]; ke[8*i+3][8*j+4] += Kdd[0][1]; ke[8*i+3][8*j+5] += Kdd[0][2];\n                ke[8*i+4][8*j+3] += Kdd[1][0]; ke[8*i+4][8*j+4] += Kdd[1][1]; ke[8*i+4][8*j+5] += Kdd[1][2];\n                ke[8*i+5][8*j+3] += Kdd[2][0]; ke[8*i+5][8*j+4] += Kdd[2][1]; ke[8*i+5][8*j+5] += Kdd[2][2];\n                \n            }\n        \n        // kup matrix\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                vec3d kup = gradMu[i]*(Mu[j]*detJt);\n                vec3d kuq = gradMu[i]*(Md[j]*detJt);\n                vec3d kdp = gradMd[i]*(Mu[j]*detJt);\n                vec3d kdq = gradMd[i]*(Md[j]*detJt);\n                \n                ke[8*i  ][8*j+6] -= kup.x;\n                ke[8*i+1][8*j+6] -= kup.y;\n                ke[8*i+2][8*j+6] -= kup.z;\n                \n                ke[8*i  ][8*j+7] -= kuq.x;\n                ke[8*i+1][8*j+7] -= kuq.y;\n                ke[8*i+2][8*j+7] -= kuq.z;\n                \n                ke[8*i+3][8*j+6] -= kdp.x;\n                ke[8*i+4][8*j+6] -= kdp.y;\n                ke[8*i+5][8*j+6] -= kdp.z;\n                \n                ke[8*i+3][8*j+7] -= kdq.x;\n                ke[8*i+4][8*j+7] -= kdq.y;\n                ke[8*i+5][8*j+7] -= kdq.z;\n                \n            }\n        \n        // kpu matrix\n        mat3ds Q = mat3dd(1/dt-phiwhat) - Phie*ept.m_J;\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                vec3d kpu = (vdotTdotv(gradMu[i], dKdE, gradMu[j])*gradp + Q*(gradMu[j]*Mu[i]))*(detJt*dt);\n                vec3d kpd = (vdotTdotv(gradMu[i], dKdE, gradMd[j])*gradp + Q*(gradMd[j]*Mu[i]))*(detJt*dt);\n                vec3d kqu = (vdotTdotv(gradMd[i], dKdE, gradMu[j])*gradp + Q*(gradMu[j]*Md[i]))*(detJt*dt);\n                vec3d kqd = (vdotTdotv(gradMd[i], dKdE, gradMd[j])*gradp + Q*(gradMd[j]*Md[i]))*(detJt*dt);\n                \n                ke[8*i+6][8*j  ] -= kpu.x; ke[8*i+6][8*j+1] -= kpu.y; ke[8*i+6][8*j+2] -= kpu.z;\n                \n                ke[8*i+6][8*j+3] -= kpd.x; ke[8*i+6][8*j+4] -= kpd.y; ke[8*i+6][8*j+5] -= kpd.z;\n                \n                ke[8*i+7][8*j  ] -= kqu.x; ke[8*i+7][8*j+1] -= kqu.y; ke[8*i+7][8*j+2] -= kqu.z;\n                \n                ke[8*i+7][8*j+3] -= kqd.x; ke[8*i+7][8*j+4] -= kqd.y; ke[8*i+7][8*j+5] -= kqd.z;\n                \n            }\n        \n        // kpp matrix\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                double kpp = (gradMu[i]*(K*gradMu[j])*(1+tau/dt) - Phip*Mu[i]*Mu[j])*(detJt*dt);\n                double kpq = (gradMu[i]*(K*gradMd[j])*(1+tau/dt) - Phip*Mu[i]*Md[j])*(detJt*dt);\n                double kqp = (gradMd[i]*(K*gradMu[j])*(1+tau/dt) - Phip*Md[i]*Mu[j])*(detJt*dt);\n                double kqq = (gradMd[i]*(K*gradMd[j])*(1+tau/dt) - Phip*Md[i]*Md[j])*(detJt*dt);\n                \n                ke[8*i+6][8*j+6] -= kpp;\n                \n                ke[8*i+6][8*j+7] -= kpq;\n                \n                ke[8*i+7][8*j+6] -= kqp;\n                \n                ke[8*i+7][8*j+7] -= kqq;\n                \n            }\n        \n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for the steady-state response (zero solid velocity)\n//!\nbool FEBiphasicShellDomain::ElementBiphasicStiffnessSS(FEShellElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, n;\n    \n    // jacobian matrix determinant\n    double detJt;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    vector<vec3d> gradMu(neln), gradMd(neln);\n    vector<double> Mu(neln), Md(neln);\n    vec3d gradM;\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Md[i] = (1-eta)/2*M[i];\n        }\n        \n        // get the stress\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4dmm c = (m_secant_tangent) ? m_pMat->SecantTangent(mp) : m_pMat->Tangent(mp);\n\n        // get the fluid flux and pressure gradient\n        vec3d gradp = pt.m_gradp;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->Permeability(mp);\n        tens4dmm dKdE = (m_secant_perm_tangent) ? m_pMat->SecantTangent_Permeability_Strain(mp) : m_pMat->Tangent_Permeability_Strain(mp);\n\n        // evaluate the solvent supply and its derivatives\n        double phiwhat = 0;\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        if (m_pMat->GetSolventSupply()) {\n            phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        \n        // Kuu matrix\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                mat3d Kuu = (mat3dd(gradMu[i]*(s*gradMu[j])) + vdotTdotv(gradMu[i], c, gradMu[j]))*detJt;\n                mat3d Kud = (mat3dd(gradMu[i]*(s*gradMd[j])) + vdotTdotv(gradMu[i], c, gradMd[j]))*detJt;\n                mat3d Kdu = (mat3dd(gradMd[i]*(s*gradMu[j])) + vdotTdotv(gradMd[i], c, gradMu[j]))*detJt;\n                mat3d Kdd = (mat3dd(gradMd[i]*(s*gradMd[j])) + vdotTdotv(gradMd[i], c, gradMd[j]))*detJt;\n                \n                ke[8*i  ][8*j  ] += Kuu[0][0]; ke[8*i  ][8*j+1] += Kuu[0][1]; ke[8*i  ][8*j+2] += Kuu[0][2];\n                ke[8*i+1][8*j  ] += Kuu[1][0]; ke[8*i+1][8*j+1] += Kuu[1][1]; ke[8*i+1][8*j+2] += Kuu[1][2];\n                ke[8*i+2][8*j  ] += Kuu[2][0]; ke[8*i+2][8*j+1] += Kuu[2][1]; ke[8*i+2][8*j+2] += Kuu[2][2];\n                \n                ke[8*i  ][8*j+3] += Kud[0][0]; ke[8*i  ][8*j+4] += Kud[0][1]; ke[8*i  ][8*j+5] += Kud[0][2];\n                ke[8*i+1][8*j+3] += Kud[1][0]; ke[8*i+1][8*j+4] += Kud[1][1]; ke[8*i+1][8*j+5] += Kud[1][2];\n                ke[8*i+2][8*j+3] += Kud[2][0]; ke[8*i+2][8*j+4] += Kud[2][1]; ke[8*i+2][8*j+5] += Kud[2][2];\n                \n                ke[8*i+3][8*j  ] += Kdu[0][0]; ke[8*i+3][8*j+1] += Kdu[0][1]; ke[8*i+3][8*j+2] += Kdu[0][2];\n                ke[8*i+4][8*j  ] += Kdu[1][0]; ke[8*i+4][8*j+1] += Kdu[1][1]; ke[8*i+4][8*j+2] += Kdu[1][2];\n                ke[8*i+5][8*j  ] += Kdu[2][0]; ke[8*i+5][8*j+1] += Kdu[2][1]; ke[8*i+5][8*j+2] += Kdu[2][2];\n                \n                ke[8*i+3][8*j+3] += Kdd[0][0]; ke[8*i+3][8*j+4] += Kdd[0][1]; ke[8*i+3][8*j+5] += Kdd[0][2];\n                ke[8*i+4][8*j+3] += Kdd[1][0]; ke[8*i+4][8*j+4] += Kdd[1][1]; ke[8*i+4][8*j+5] += Kdd[1][2];\n                ke[8*i+5][8*j+3] += Kdd[2][0]; ke[8*i+5][8*j+4] += Kdd[2][1]; ke[8*i+5][8*j+5] += Kdd[2][2];\n                \n            }\n        \n        // kup matrix\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                vec3d kup = gradMu[i]*(Mu[j]*detJt);\n                vec3d kuq = gradMu[i]*(Md[j]*detJt);\n                vec3d kdp = gradMd[i]*(Mu[j]*detJt);\n                vec3d kdq = gradMd[i]*(Md[j]*detJt);\n                \n                ke[8*i  ][8*j+6] -= kup.x;\n                ke[8*i+1][8*j+6] -= kup.y;\n                ke[8*i+2][8*j+6] -= kup.z;\n                \n                ke[8*i  ][8*j+7] -= kuq.x;\n                ke[8*i+1][8*j+7] -= kuq.y;\n                ke[8*i+2][8*j+7] -= kuq.z;\n                \n                ke[8*i+3][8*j+6] -= kdp.x;\n                ke[8*i+4][8*j+6] -= kdp.y;\n                ke[8*i+5][8*j+6] -= kdp.z;\n                \n                ke[8*i+3][8*j+7] -= kdq.x;\n                ke[8*i+4][8*j+7] -= kdq.y;\n                ke[8*i+5][8*j+7] -= kdq.z;\n                \n            }\n        \n        // kpu matrix\n        mat3ds Q = mat3dd(-phiwhat) - Phie*ept.m_J;\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                vec3d kpu = (vdotTdotv(gradMu[i], dKdE, gradMu[j])*gradp + Q*(gradMu[j]*Mu[i]))*(detJt*dt);\n                vec3d kpd = (vdotTdotv(gradMu[i], dKdE, gradMd[j])*gradp + Q*(gradMd[j]*Mu[i]))*(detJt*dt);\n                vec3d kqu = (vdotTdotv(gradMd[i], dKdE, gradMu[j])*gradp + Q*(gradMu[j]*Md[i]))*(detJt*dt);\n                vec3d kqd = (vdotTdotv(gradMd[i], dKdE, gradMd[j])*gradp + Q*(gradMd[j]*Md[i]))*(detJt*dt);\n                \n                ke[8*i+6][8*j  ] -= kpu.x; ke[8*i+6][8*j+1] -= kpu.y; ke[8*i+6][8*j+2] -= kpu.z;\n                \n                ke[8*i+6][8*j+3] -= kpd.x; ke[8*i+6][8*j+4] -= kpd.y; ke[8*i+6][8*j+5] -= kpd.z;\n                \n                ke[8*i+7][8*j  ] -= kqu.x; ke[8*i+7][8*j+1] -= kqu.y; ke[8*i+7][8*j+2] -= kqu.z;\n                \n                ke[8*i+7][8*j+3] -= kqd.x; ke[8*i+7][8*j+4] -= kqd.y; ke[8*i+7][8*j+5] -= kqd.z;\n                \n            }\n        \n        // kpp matrix\n        for (i=0; i<neln; ++i)\n            for (j=0; j<neln; ++j)\n            {\n                double kpp = (gradMu[i]*(K*gradMu[j]) - Phip*Mu[i]*Mu[j])*(detJt*dt);\n                double kpq = (gradMu[i]*(K*gradMd[j]) - Phip*Mu[i]*Mu[j])*(detJt*dt);\n                double kqp = (gradMd[i]*(K*gradMu[j]) - Phip*Md[i]*Mu[j])*(detJt*dt);\n                double kqq = (gradMd[i]*(K*gradMd[j]) - Phip*Md[i]*Md[j])*(detJt*dt);\n                \n                ke[8*i+6][8*j+6] -= kpp;\n                \n                ke[8*i+6][8*j+7] -= kpq;\n                \n                ke[8*i+7][8*j+6] -= kqp;\n                \n                ke[8*i+7][8*j+7] -= kqq;\n                \n            }\n        \n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::Update(const FETimeInfo& tp)\n{\n\tFESSIShellDomain::Update(tp);\n\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::UpdateElementStress(int iel)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // get the solid element\n    FEShellElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // get the number of nodes\n    int neln = el.Nodes();\n    \n    // get the nodal data\n    FEMesh& mesh = *m_pMesh;\n    vec3d r0[FEElement::MAX_NODES];\n    vec3d rt[FEElement::MAX_NODES];\n    double pn[FEElement::MAX_NODES];\n    double qn[FEElement::MAX_NODES];\n    for (int j=0; j<neln; ++j)\n    {\n        r0[j] = mesh.Node(el.m_node[j]).m_r0;\n        rt[j] = mesh.Node(el.m_node[j]).m_rt;\n        pn[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        qn[j] = mesh.Node(el.m_node[j]).get(m_dofQ);\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //\t\t since the material point coordinates are used by most materials.\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(rt, n);\n        \n        // get the deformation gradient and determinant\n        pt.m_J = defgrad(el, pt.m_F, n);\n        mat3d Fp;\n        defgradp(el, Fp, n);\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (pt.m_F - Fp)*Fi / dt;\n\n        // biphasic data\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // evaluate fluid pressure at gauss-point\n        ppt.m_p = evaluate(el, pn, qn, n);\n        \n        // calculate the gradient of p at gauss-point\n        ppt.m_gradp = gradient(el, pn, qn, n);\n        \n        // for biphasic materials also update the fluid flux\n        //\t\tppt.m_w = m_pMat->Flux(mp);\n        ppt.m_w = FluidFlux(mp);\n        ppt.m_pa = m_pMat->Pressure(mp);\n        \n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = (m_secant_stress) ? m_pMat->GetElasticMaterial()->SecantStress(mp) : m_pMat->GetElasticMaterial()->Stress(mp);\n\n        // calculate the stress at this material point\n        pt.m_s = (m_secant_stress) ? m_pMat->SecantStress(mp) : m_pMat->Stress(mp);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for\n    for (int i=0; i<NE; ++i)\n    {\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 8*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // apply body forces\n        ElementBodyForce(BF, el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the body forces\n\nvoid FEBiphasicShellDomain::ElementBodyForce(FEBodyForce& BF, FEShellElement& el, vector<double>& fe)\n{\n    // get true solid and fluid densities\n    double rhoTw = m_pMat->FluidDensity();\n    \n    // jacobian\n    double detJt, eta;\n    double *M;\n    double* gw = el.GaussWeights();\n    vec3d b, fu, fd;\n    \n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES], rt[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n    {\n        r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n        rt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n    }\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(rt, n);\n        \n        detJt = detJ(el, n)*gw[n];\n        \n        // get the force\n        b = BF.force(mp);\n        \n        eta = el.gt(n);\n        \n        // evaluate apparent solid and fluid densities and mixture density\n        double phiw = m_pMat->Porosity(mp);\n        double rhos = (1-phiw)*m_pMat->SolidDensity(mp);\n        double rhow = phiw*rhoTw;\n        double rho = rhos + rhow;\n        \n        M = el.H(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            fu = b*(rho*(1+eta)/2*M[i]*detJt);\n            fd = b*(rho*(1-eta)/2*M[i]*detJt);\n            fe[8*i  ] -= fu.x;\n            fe[8*i+1] -= fu.y;\n            fe[8*i+2] -= fu.z;\n            \n            fe[8*i+3] -= fd.x;\n            fe[8*i+4] -= fd.y;\n            fe[8*i+5] -= fd.z;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicShellDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    FEBiphasic* pmb = dynamic_cast<FEBiphasic*>(GetMaterial()); assert(pmb);\n    \n    // element stiffness matrix\n    vector<int> lm;\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FEShellElement& el = m_Elem[iel];\n        \n        // create the element's stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint neln = el.Nodes();\n        int ndof = 8*neln;\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEBiphasicShellDomain::ElementBodyForceStiffness(FEBodyForce& BF, FEShellElement &el, matrix &ke)\n{\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // get true solid and fluid densities\n    double rhoTw = m_pMat->FluidDensity();\n    \n    // jacobian\n    double detJt;\n    const double* Mr, *Ms, *M;\n    vector<vec3d> gradMu(neln), gradMd(neln);\n    vector<double> Mu(neln), Md(neln);\n    vec3d gradM;\n    \n    double* gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    vec3d b;\n    mat3d gradb;\n    mat3d Kuu, Kud, Kdu, Kdd;\n    vec3d kpu, kpd, kqu, kqd;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        \n        // get the body force\n        b = BF.force(mp);\n        \n        // get the body force stiffness\n        gradb = BF.stiffness(mp);\n        \n        // evaluate apparent solid and fluid densities and mixture density\n        double phiw = m_pMat->Porosity(mp);\n        double rhos = (1-phiw)*m_pMat->SolidDensity(mp);\n        double rhow = phiw*rhoTw;\n        double rho = rhos + rhow;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->Permeability(mp);\n        tens4dmm dKdE = m_pMat->Tangent_Permeability_Strain(mp);\n        \n        // calculate the jacobian\n        detJt = detJ(el, n);\n        \n        detJt *= gw[n];\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMd[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Md[i] = (1-eta)/2*M[i];\n        }\n        \n        for (int i=0; i<neln; ++i)\n            for (int j=0; j<neln; ++j)\n            {\n                Kuu = (gradb*(Mu[j]*rho) + (b & gradMu[j])*rhoTw)*(Mu[i]*detJt);\n                Kud = (gradb*(Md[j]*rho) + (b & gradMd[j])*rhoTw)*(Mu[i]*detJt);\n                Kdu = (gradb*(Mu[j]*rho) + (b & gradMu[j])*rhoTw)*(Md[i]*detJt);\n                Kdd = (gradb*(Md[j]*rho) + (b & gradMd[j])*rhoTw)*(Md[i]*detJt);\n                \n                ke[8*i  ][8*j  ] += Kuu(0,0); ke[8*i  ][8*j+1] += Kuu(0,1); ke[8*i  ][8*j+2] += Kuu(0,2);\n                ke[8*i+1][8*j  ] += Kuu(1,0); ke[8*i+1][8*j+1] += Kuu(1,1); ke[8*i+1][8*j+2] += Kuu(1,2);\n                ke[8*i+2][8*j  ] += Kuu(2,0); ke[8*i+2][8*j+1] += Kuu(2,1); ke[8*i+2][8*j+2] += Kuu(2,2);\n                \n                ke[8*i  ][8*j+3] += Kud(0,0); ke[8*i  ][8*j+4] += Kud(0,1); ke[8*i  ][8*j+5] += Kud(0,2);\n                ke[8*i+1][8*j+3] += Kud(1,0); ke[8*i+1][8*j+4] += Kud(1,1); ke[8*i+1][8*j+5] += Kud(1,2);\n                ke[8*i+2][8*j+3] += Kud(2,0); ke[8*i+2][8*j+4] += Kud(2,1); ke[8*i+2][8*j+5] += Kud(2,2);\n                \n                ke[8*i+3][8*j  ] += Kdu(0,0); ke[8*i+3][8*j+1] += Kdu(0,1); ke[8*i+3][8*j+2] += Kdu(0,2);\n                ke[8*i+4][8*j  ] += Kdu(1,0); ke[8*i+4][8*j+1] += Kdu(1,1); ke[8*i+4][8*j+2] += Kdu(1,2);\n                ke[8*i+5][8*j  ] += Kdu(2,0); ke[8*i+5][8*j+1] += Kdu(2,1); ke[8*i+5][8*j+2] += Kdu(2,2);\n                \n                ke[8*i+3][8*j+3] += Kdd(0,0); ke[8*i+3][8*j+4] += Kdd(0,1); ke[8*i+3][8*j+5] += Kdd(0,2);\n                ke[8*i+4][8*j+3] += Kdd(1,0); ke[8*i+4][8*j+4] += Kdd(1,1); ke[8*i+4][8*j+5] += Kdd(1,2);\n                ke[8*i+5][8*j+3] += Kdd(2,0); ke[8*i+5][8*j+4] += Kdd(2,1); ke[8*i+5][8*j+5] += Kdd(2,2);\n                \n                kpu = (vdotTdotv(gradMu[i], dKdE, gradMu[j])*b\n                       + ((b & gradMu[j]) + gradb*Mu[j])*K*gradMu[i])*(rhoTw*detJt*dt);\n                kpd = (vdotTdotv(gradMu[i], dKdE, gradMd[j])*b\n                       + ((b & gradMd[j]) + gradb*Md[j])*K*gradMu[i])*(rhoTw*detJt*dt);\n                kqu = (vdotTdotv(gradMd[i], dKdE, gradMu[j])*b\n                       + ((b & gradMu[j]) + gradb*Mu[j])*K*gradMd[i])*(rhoTw*detJt*dt);\n                kqd = (vdotTdotv(gradMd[i], dKdE, gradMd[j])*b\n                       + ((b & gradMd[j]) + gradb*Md[j])*K*gradMd[i])*(rhoTw*detJt*dt);\n                \n                ke[8*i+6][8*j  ] -= kpu.x; ke[8*i+6][8*j+1] -= kpu.y; ke[8*i+6][8*j+2] -= kpu.z;\n                ke[8*i+6][8*j+3] -= kpd.x; ke[8*i+6][8*j+4] -= kpd.y; ke[8*i+6][8*j+5] -= kpd.z;\n                ke[8*i+7][8*j  ] -= kqu.x; ke[8*i+7][8*j+1] -= kqu.y; ke[8*i+7][8*j+2] -= kqu.z;\n                ke[8*i+7][8*j+3] -= kqd.x; ke[8*i+7][8*j+4] -= kqd.y; ke[8*i+7][8*j+5] -= kqd.z;\n            }\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\nvec3d FEBiphasicShellDomain::FluidFlux(FEMaterialPoint& mp)\n{\n    FEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n    \n    // pressure gradient\n    vec3d gradp = ppt.m_gradp;\n    \n    // fluid flux w = -k*grad(p)\n    mat3ds kt = m_pMat->Permeability(mp);\n    \n    vec3d w = -(kt*gradp);\n    \n    // get true fluid density\n    double rhoTw = m_pMat->FluidDensity();\n    \n    // body force contribution\n    FEModel& fem = *m_pMat->GetFEModel();\n    int nbf = fem.ModelLoads();\n    if (nbf) {\n        vec3d b(0,0,0);\n        for (int i=0; i<nbf; ++i)\n        {\n            FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(i));\n            if (pbf && pbf->IsActive())\n            {\n                // negate b because body forces are defined with a negative sign in FEBio\n                b -= pbf->force(mp);\n            }\n        }\n        w += (kt*b)*(rhoTw);\n    }\n    \n    // active momentum supply contribution\n    FEActiveMomentumSupply* pAmom = m_pMat->GetActiveMomentumSupply();\n    if (pAmom) {\n        vec3d pw = pAmom->ActiveSupply(mp);\n        w += kt*pw;\n    }\n    \n    return w;\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FESSIShellDomain.h>\n#include \"FEBiphasicDomain.h\"\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain class for biphasic 3D shell elements\n//!\nclass FEBIOMIX_API FEBiphasicShellDomain : public FESSIShellDomain, public FEBiphasicDomain\n{\npublic:\n    //! constructor\n    FEBiphasicShellDomain(FEModel* pfem);\n    \n    //! activate\n    void Activate() override;\n    \n    //! reset domain data\n    void Reset() override;\n    \n    //! intitialize element data\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! Unpack shell element data  (overridden from FEDomain)\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n\t//! get material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\n\t//! get the total dof\n\tconst FEDofList& GetDOFList() const override;\n\n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n    \npublic:\n    // update domain data\n    void Update(const FETimeInfo& tp) override;\n    \n    // update element stress\n    void UpdateElementStress(int iel);\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS, bool bsymm) override;\n    \n    //! calculates the global stiffness matrix (steady-state case)\n    void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) override;\n    \npublic:\n    // internal work (overridden from FEElasticDomain)\n    void InternalForces(FEGlobalVector& R) override;\n    \n    // internal work (steady-state case)\n    void InternalForcesSS(FEGlobalVector& R) override;\n    \npublic:\n    //! element internal force vector\n    void ElementInternalForce(FEShellElement& el, vector<double>& fe);\n    \n    //! element internal force vector (stead-state case)\n    void ElementInternalForceSS(FEShellElement& el, vector<double>& fe);\n    \n    //! calculates the element biphasic stiffness matrix\n    bool ElementBiphasicStiffness(FEShellElement& el, matrix& ke, bool bsymm);\n    \n    //! calculates the element biphasic stiffness matrix for steady-state response\n    bool ElementBiphasicStiffnessSS(FEShellElement& el, matrix& ke, bool bsymm);\n\npublic: // overridden from FEElasticDomain, but not all implemented in this domain\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n    void ElementBodyForce(FEBodyForce& BF, FEShellElement& el, vector<double>& fe);\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    void ElementBodyForceStiffness(FEBodyForce& BF, FEShellElement &el, matrix &ke);\n    void MassMatrix(FELinearSystem& LS, double scale) override {}\n    \npublic: // biphasic domain \"properties\"\n    // NOTE: I'm thinking about defining properties for domain classes. These would be similar to material\n    // properties (and may require material properties to be evaluated), but are different in that they are\n    // not meant to be customized. For example, the biphasic solver assumes Darcy's law in the evaluation\n    // of the fluid flux. Although one can see the fluid flux as a material property, since the code makes explicit\n    // use of this constitutive equation (apparent from the fact that the biphasic material needs to define the permeability and its\n    // strain derivate) it is not a true material property: i.e. it is not meant to be changed and is an inherent\n    // assumption in this implementation. Consequently, the fluid flux would be a good example of a domain property.\n    // That is why I've taken this calculation out of the FEBiphasic class and placed it here.\n    vec3d FluidFlux(FEMaterialPoint& mp) override;\n\nprotected:\n    bool    m_secant_stress;    //!< use secant approximation to stress\n    bool    m_secant_tangent;   //!< flag for using secant tangent\n    bool    m_secant_perm_tangent;   //!< flag for using secant tangent on permeability\n    \nprotected:\n    int\t\t\t\t\tm_dofSX;\n    int\t\t\t\t\tm_dofSY;\n    int\t\t\t\t\tm_dofSZ;\n\tFEDofList\t\t\tm_dof;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSolidDomain.h\"\n#include \"FECore/FEMesh.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEDataExport.h>\n#include <FECore/FEModel.h>\n#include <FEBioMech/FEBioMech.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBioMix.h\"\n\nBEGIN_FECORE_CLASS(FEBiphasicSolidDomain, FESolidDomain)\n    ADD_PARAMETER(m_secant_stress, \"secant_stress\");\n    ADD_PARAMETER(m_secant_tangent, \"secant_tangent\");\n    ADD_PARAMETER(m_secant_perm_tangent, \"secant_permeability_tangent\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEBiphasicSolidDomain::FEBiphasicSolidDomain(FEModel* pfem) : FESolidDomain(pfem), FEBiphasicDomain(pfem), m_dofU(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n\tEXPORT_DATA(PLT_FLOAT, FMT_NODE, &m_nodePressure, \"NPR fluid pressure\");\n    m_secant_stress = false;\n    m_secant_tangent = false;\n    m_secant_perm_tangent = false;\n\n\tif (pfem)\n\t{\n\t\tm_varU = pfem->GetDOFS().GetVariableIndex(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT)); assert(m_varU >= 0);\n\t\tm_varP = pfem->GetDOFS().GetVariableIndex(FEBioMix::GetVariableName(FEBioMix::FLUID_PRESSURE)); assert(m_varP >= 0);\n\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t\tm_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n\t\tm_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof list\nconst FEDofList& FEBiphasicSolidDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::Serialize(DumpStream& ar)\n{\n    FESolidDomain::Serialize(ar);\n\n    if (ar.IsShallow() == false)\n    {\n        ar & m_nodePressure;\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FEBiphasic*>(pmat);\n\tassert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nvoid FEBiphasicSolidDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tconst int NE = FEElement::MAX_NODES;\n\tvec3d x0[NE], xt[NE], r0, rt;\n    double pn[NE], p;\n    FEMesh& m = *GetMesh();\n\tfor (size_t iel=0; iel<m_Elem.size(); ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\t\tint neln = el.Nodes();\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n            FENode& node = m.Node(el.m_node[i]);\n\t\t\tx0[i] = node.m_r0;\n\t\t\txt[i] = node.m_rt;\n            if (el.m_bitfc.size()>0 && el.m_bitfc[i] && node.m_ID[m_dofQ] != -1)\n                pn[i] = node.get(m_dofQ);\n            else\n                pn[i] = node.get(m_dofP);\n        }\n\n\t\tint n = el.GaussPoints();\n\t\tfor (int j=0; j<n; ++j) \n\t\t{\n\t\t\tr0 = el.Evaluate(x0, j);\n\t\t\trt = el.Evaluate(xt, j);\n            p = el.Evaluate(pn, j);\n            \n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pb = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\t\tmp.m_r0 = r0;\n\t\t\tmp.m_rt = rt;\n\n\t\t\tpt.m_J = defgrad(el, pt.m_F, j);\n\n            pb.m_Jp = pt.m_J;\n            \n            pb.m_p = p;\n            pb.m_gradp = gradient(el, pn, j);\n            pb.m_gradpp = pb.m_gradp;\n            pb.m_phi0p = pb.m_phi0t;\n            \n            mp.Update(timeInfo);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEBiphasicSolidDomain::Init()\n{\n\t// initialize base class\n\tif (FESolidDomain::Init() == false) return false;\n    \n    // initialize body forces\n\tFEModel& fem = *GetFEModel();\n\tm_pMat->m_bf.clear();\n    for (int j=0; j<fem.ModelLoads(); ++j)\n    {\n        FEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(j));\n        if (pbf) m_pMat->m_bf.push_back(pbf);\n    }\n\n\t// allocate nodal pressures\n\tm_nodePressure.resize(Nodes(), 0.0);\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::Activate()\n{\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\t\t}\n\t}\n\n    // Activate dof_P, except when a biphasic solid is connected to the\n    // back of a shell element, in which case activate dof_Q for those nodes.\n    FEMesh& m = *GetMesh();\n    for (int i=0; i<Elements(); ++i) {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m.Node(el.m_node[j]);\n            if (el.m_bitfc.size()>0 && el.m_bitfc[j])\n                node.set_active(m_dofQ);\n            else\n                node.set_active(m_dofP);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data. \nvoid FEBiphasicSolidDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n\t// number of nodes for velocity interpolation\n\tint neln_d = el.ShapeFunctions(degree_d);\n\n\t// number of nodes for pressure interpolation\n\tint neln_p = el.ShapeFunctions(degree_p);\n\n\t// allocate lm\n\tlm.resize(neln_d*4 + 3*neln_d);\n\n\t// displacement dofs\n\tfor (int i=0; i<neln_d; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n        // first the displacement dofs\n        lm[4*i  ] = id[m_dofU[0]];\n        lm[4*i+1] = id[m_dofU[1]];\n        lm[4*i+2] = id[m_dofU[2]];\n\n\t\t// now the pressure dofs\n\t\tlm[4*i + 3] = id[m_dofP];\n\n\t\t// rigid rotational dofs\n\t\tlm[4 * neln_d + 3 * i    ] = id[m_dofR[0]];\n\t\tlm[4 * neln_d + 3 * i + 1] = id[m_dofR[1]];\n\t\tlm[4 * neln_d + 3 * i + 2] = id[m_dofR[2]];\n\t}\n\n    // substitute interface dofs for solid-shell interfaces\n\tFESolidElement& sel = static_cast<FESolidElement&>(el);\n\tfor (int i = 0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(el.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the back-face displacement dofs\n            lm[4*i  ] = id[m_dofSU[0]];\n            lm[4*i+1] = id[m_dofSU[1]];\n            lm[4*i+2] = id[m_dofSU[2]];\n            \n            // now the pressure dof (if the shell has it)\n            if (id[m_dofQ] > -1) lm[4*i + 3] = id[m_dofQ];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::Reset()\n{\n\t// reset base class data\n\tFESolidDomain::Reset();\n\n\t// initialize all element data\n\tForEachMaterialPoint([=](FEMaterialPoint& mp) {\n\t\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\n\t\t// initialize referential solid volume fraction\n\t\tpt.m_phi0 = pt.m_phi0t = m_pMat->m_phi0(mp);\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::InternalForces(FEGlobalVector& R)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n\tint NE = (int)m_Elem.size();\n\t#pragma omp parallel for shared (NE)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// element force vector\n\t\tvector<double> fe;\n\t\tvector<int> lm;\n\t\t\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\tint nel_d = el.ShapeFunctions(degree_d);\n\t\tint nel_p = el.ShapeFunctions(degree_p);\n\n\t\t// get the element force vector and initialize it to zero\n\t\tint ndof = 4*nel_d;\n\t\tfe.assign(ndof, 0);\n\n\t\t// calculate internal force vector\n\t\tElementInternalForce(el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble element 'fe'-vector into global R vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEBiphasicSolidDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3];\n\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n    int nint = el.GaussPoints();\n\tint nel_d = el.ShapeFunctions(degree_d);\n\tint nel_p = el.ShapeFunctions(degree_p);\n\n    double*\tgw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n\t\t// calculate the jacobian\n\t\tdouble Jw = invjact(el, Ji, n)*gw[n];\n\n        // get the stress vector for this integration point\n        mat3ds s = pt.m_s;\n        \n        double* Gr = el.Gr(n);\n        double* Gs = el.Gs(n);\n        double* Gt = el.Gt(n);\n        \n        double* H = el.H(n);\n\n\t\t// --- stress contribution\n        for (int i=0; i<nel_d; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            vec3d gradN(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                        Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                        Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n\n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[4*i  ] -= fu.x*Jw;\n            fe[4*i+1] -= fu.y*Jw;\n            fe[4*i+2] -= fu.z*Jw;\n        }\n\n\t\t// --- pressure contribution\n\n\t\t// next we get the determinant\n\t\tdouble Jp = bpt.m_Jp;\n\t\tdouble J = pt.m_J;\n\n\t\t// and then finally\n\t\tdouble divv = ((J - Jp) / dt) / J;\n\n\t\t// get the flux\n\t\tvec3d& w = bpt.m_w;\n\n\t\t// get the solvent supply\n\t\tdouble phiwhat = m_pMat->SolventSupply(mp);\n\n\t\t// pressure shape functions\n\t\tdouble* Hp  = el.H(degree_p, n);\n\t\tdouble* Gpr = el.Gr(degree_p, n);\n\t\tdouble* Gps = el.Gs(degree_p, n);\n\t\tdouble* Gpt = el.Gt(degree_p, n);\n\n\t\tfor (int i = 0; i<nel_p; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tvec3d gradHp = vec3d(Ji[0][0] * Gpr[i] + Ji[1][0] * Gps[i] + Ji[2][0] * Gpt[i],\n\t\t\t\t                 Ji[0][1] * Gpr[i] + Ji[1][1] * Gps[i] + Ji[2][1] * Gpt[i],\n\t\t\t\t                 Ji[0][2] * Gpr[i] + Ji[1][2] * Gps[i] + Ji[2][2] * Gpt[i]);\n\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[4*i + 3] -= dt*(w*gradHp + (phiwhat - divv)*Hp[i])*Jw;\n\t\t}\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 4*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements (steady-state)\n\nvoid FEBiphasicSolidDomain::ElementInternalForceSS(FESolidElement& el, vector<double>& fe)\n{\n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradN, GradN;\n   \n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n    int nint = el.GaussPoints();\n\tint nel_d = el.ShapeFunctions(degree_d);\n\tint nel_p = el.ShapeFunctions(degree_p);\n\n    double*\tgw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        mat3d s = pt.m_s;\n        \n        double* Gr = el.Gr(n);\n        double* Gs = el.Gs(n);\n        double* Gt = el.Gt(n);\n        \n        double* H = el.H(n);\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solvent supply\n        double phiwhat = m_pMat->SolventSupply(mp);\n        \n        for (int i=0; i<nel_d; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[4*i  ] -= fu.x*detJt;\n            fe[4*i+1] -= fu.y*detJt;\n            fe[4*i+2] -= fu.z*detJt;\n        }\n\n\t\t// --- pressure contribution\n\n\t\tdouble* Gpr = el.Gr(degree_p, n);\n\t\tdouble* Gps = el.Gs(degree_p, n);\n\t\tdouble* Gpt = el.Gt(degree_p, n);\n\n\t\tdouble* Hp = el.H(degree_p, n);\n\n\t\tfor (int i = 0; i<nel_p; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tvec3d gradH(Ji[0][0] * Gpr[i] + Ji[1][0] * Gps[i] + Ji[2][0] * Gpt[i],\n\t\t\t\t\t\tJi[0][1] * Gpr[i] + Ji[1][1] * Gps[i] + Ji[2][1] * Gpt[i],\n\t\t\t\t\t\tJi[0][2] * Gpr[i] + Ji[1][2] * Gps[i] + Ji[2][2] * Gpt[i]);\n\t\t\t\n\t\t\tfe[4*i + 3] -= dt*(w*gradH + phiwhat*Hp[i])*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n    \n    #pragma omp parallel for shared(NE)\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = el.Nodes()*4;\n\t\tke.resize(ndof, ndof);\n\t\t\n\t\t// calculate the element stiffness matrix\n\t\tElementBiphasicStiffness(el, ke, bsymm);\n\t\t\n\t\t// TODO: the problem here is that the LM array that is returned by the UnpackLM\n\t\t// function does not give the equation numbers in the right order. For this reason we\n\t\t// have to create a new lm array and place the equation numbers in the right order.\n\t\t// What we really ought to do is fix the UnpackLM function so that it returns\n\t\t// the LM vector in the right order for poroelastic elements.\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\n\t#pragma omp parallel for shared(NE)\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint ndof = el.Nodes()*4;\n\t\tke.resize(ndof, ndof);\n\t\t\n\t\t// calculate the element stiffness matrix\n\t\tElementBiphasicStiffnessSS(el, ke, bsymm);\n\t\t\n\t\t// TODO: the problem here is that the LM array that is returned by the UnpackLM\n\t\t// function does not give the equation numbers in the right order. For this reason we\n\t\t// have to create a new lm array and place the equation numbers in the right order.\n\t\t// What we really ought to do is fix the UnpackLM function so that it returns\n\t\t// the LM vector in the right order for poroelastic elements.\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FEBiphasicSolidDomain::ElementBiphasicStiffness(FESolidElement& el, matrix& ke, bool bsymm)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n    int nint = el.GaussPoints();\n\tint nel_d = el.ShapeFunctions(degree_d);\n\tint nel_p = el.ShapeFunctions(degree_p);\n\n    // jacobian\n    double Ji[3][3];\n    \n    // Bp-matrix\n    vector<vec3d> gradNu(FEElement::MAX_NODES), gradNp(FEElement::MAX_NODES);\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    double tau = m_pMat->m_tau;\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate jacobian\n        double detJ = invjact(el, Ji, n);\n        \n        // contravariant basis vectors in spatial frame\n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n\t\t// displacement shape functions\n        double* Hu = el.H(n);\n        double* Gur = el.Gr(n);\n        double* Gus = el.Gs(n);\n        double* Gut = el.Gt(n);\n\n\t\t// pressure shape functions\n\t\tdouble* Hp  = el.H (degree_p, n);\n\t\tdouble* Gpr = el.Gr(degree_p, n);\n\t\tdouble* Gps = el.Gs(degree_p, n);\n\t\tdouble* Gpt = el.Gt(degree_p, n);\n\n        for (int i=0; i<nel_d; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradNu[i] = g1*Gur[i] + g2*Gus[i] + g3*Gut[i];\n        }\n\n\t\tfor (int i = 0; i<nel_p; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tgradNp[i] = g1*Gpr[i] + g2*Gps[i] + g3*Gpt[i];\n\t\t}\n\n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4dmm c = (m_secant_tangent) ? m_pMat->SecantTangent(mp) : m_pMat->Tangent(mp);\n\n        // get the fluid flux and pressure gradient\n        vec3d gradp = pt.m_gradp + (pt.m_gradp - pt.m_gradpp)*(tau/dt);\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->Permeability(mp);\n        tens4dmm dKdE = (m_secant_perm_tangent) ? m_pMat->SecantTangent_Permeability_Strain(mp) : m_pMat->Tangent_Permeability_Strain(mp);\n\n        // evaluate the solvent supply and its derivatives\n        double phiwhat = 0;\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        if (m_pMat->GetSolventSupply()) {\n            phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        \n        // Kuu matrix\n        double Jw = detJ*gw[n];\n        for (int i=0; i<nel_d; ++i)\n            for (int j=0; j<nel_d; ++j)\n            {\n                mat3d Kuu = (mat3dd(gradNu[i]*(s*gradNu[j])) + vdotTdotv(gradNu[i], c, gradNu[j]))*Jw;\n                \n\t\t\t\tke.add(4 * i, 4 * j, Kuu);\n            }\n        \n        // calculate the kpp matrix\n        for (int i=0; i<nel_p; ++i)\n            for (int j=0; j<nel_p; ++j)\n            {\n                ke[4*i+3][4*j+3] += (Hp[i]*Hp[j]*Phip - gradNp[i]*(K*gradNp[j])*(1+tau/dt))*(dt*Jw);\n            }\n        \n        if (!bsymm) {\n            // calculate the kup matrix\n            for (int i=0; i<nel_d; ++i) {\n                for (int j=0; j<nel_p; ++j)\n                {\n                    ke[4*i  ][4*j+3] -= Jw*gradNu[i].x*Hp[j];\n                    ke[4*i+1][4*j+3] -= Jw*gradNu[i].y*Hp[j];\n                    ke[4*i+2][4*j+3] -= Jw*gradNu[i].z*Hp[j];\n                }\n            }\n            \n            // calculate the kpu matrix\n            mat3ds Q = Phie*ept.m_J + mat3dd(phiwhat - 1./dt);\n            for (int i=0; i<nel_p; ++i) {\n                for (int j=0; j<nel_d; ++j)\n                {\n                    vec3d vt = ((vdotTdotv(-gradNp[i], dKdE, gradNu[j])*gradp)\n                                +(Q*gradNu[j])*Hp[i])*(Jw*dt);\n                    ke[4*i+3][4*j  ] += vt.x;\n                    ke[4*i+3][4*j+1] += vt.y;\n                    ke[4*i+3][4*j+2] += vt.z;\n                }\n            }\n            \n        } else {\n            // calculate the kup matrix and let kpu be its symmetric part\n            for (int i=0; i<nel_d; ++i) {\n                for (int j=0; j<nel_p; ++j)\n                {\n                    ke[4*i  ][4*j+3] -= Jw*gradNu[i].x*Hp[j];\n                    ke[4*i+1][4*j+3] -= Jw*gradNu[i].y*Hp[j];\n                    ke[4*i+2][4*j+3] -= Jw*gradNu[i].z*Hp[j];\n                    \n                    ke[4*j+3][4*i  ] -= Jw*gradNu[i].x*Hp[j];\n                    ke[4*j+3][4*i+1] -= Jw*gradNu[i].y*Hp[j];\n                    ke[4*j+3][4*i+2] -= Jw*gradNu[i].z*Hp[j];\n                }\n            }\n        }\n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for the steady-state response (zero solid velocity)\n//!\nbool FEBiphasicSolidDomain::ElementBiphasicStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n    int nint = el.GaussPoints();\n\tint nel_d = el.ShapeFunctions(degree_d);\n\tint nel_p = el.ShapeFunctions(degree_p);\n\n    // jacobian\n    double Ji[3][3];\n    \n    // Bp-matrix\n    vector<vec3d> gradNu(nel_d), gradNp(nel_p);\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        \n        // calculate jacobian\n        double detJ = invjact(el, Ji, n);\n        \n        // contravariant basis vectors in spatial frame\n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        double* Hu = el.H(n);\n\t\tdouble* Hp = el.H(degree_p, n);\n\n        double* Gur = el.Gr(n);\n        double* Gus = el.Gs(n);\n        double* Gut = el.Gt(n);\n\n\t\tdouble* Gpr = el.Gr(degree_p, n);\n\t\tdouble* Gps = el.Gs(degree_p, n);\n\t\tdouble* Gpt = el.Gt(degree_p, n);\n\n        for (int i=0; i<nel_d; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradNu[i] = g1*Gur[i] + g2*Gus[i] + g3*Gut[i];\n        }\n\n\t\tfor (int i = 0; i<nel_p; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tgradNp[i] = g1*Gpr[i] + g2*Gps[i] + g3*Gpt[i];\n\t\t}\n\n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4dmm c = (m_secant_tangent) ? m_pMat->SecantTangent(mp) : m_pMat->Tangent(mp);\n\n        // get the fluid flux and pressure gradient\n        vec3d gradp = pt.m_gradp;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->Permeability(mp);\n        tens4dmm dKdE = (m_secant_perm_tangent) ? m_pMat->SecantTangent_Permeability_Strain(mp) : m_pMat->Tangent_Permeability_Strain(mp);\n\n        // evaluate the solvent supply and its derivatives\n        double phiwhat = 0;\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        if (m_pMat->GetSolventSupply()) {\n            phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        \n        // Kuu matrix\n        tmp = detJ*gw[n];\n        for (int i=0; i<nel_d; ++i)\n            for (int j=0; j<nel_d; ++j)\n            {\n                mat3d Kuu = (mat3dd(gradNu[i]*(s*gradNu[j])) + vdotTdotv(gradNu[i], c, gradNu[j]))*tmp;\n\t\t\t\tke.add(4 * i, 4 * j, Kuu);\n            }\n        \n        // calculate the kpp matrix\n        tmp = detJ*gw[n]*dt;\n        for (int i=0; i<nel_p; ++i)\n            for (int j=0; j<nel_p; ++j)\n            {\n                ke[4*i+3][4*j+3] += (Hp[i]*Hp[j]*Phip - gradNp[i]*(K*gradNp[j]))*tmp;\n            }\n        \n        if (!bsymm) {\n            // calculate the kup matrix\n            for (int i=0; i<nel_d; ++i) {\n                for (int j=0; j<nel_p; ++j)\n                {\n                    tmp = detJ*gw[n]*Hp[j];\n                    ke[4*i    ][4*j+3] -= tmp*gradNu[i].x;\n                    ke[4*i + 1][4*j+3] -= tmp*gradNu[i].y;\n                    ke[4*i + 2][4*j+3] -= tmp*gradNu[i].z;\n                }\n            }\n            \n            // calculate the kpu matrix\n            //\t\t\ttmp = detJ*gw[n];\n            tmp = detJ*gw[n]*dt;\n            for (int i=0; i<nel_p; ++i) {\n                for (int j=0; j<nel_d; ++j)\n                {\n                    vec3d vt = ((vdotTdotv(-gradp, dKdE, gradNu[j])*(gradNp[i]))\n                                +(mat3dd(phiwhat) + Phie*ept.m_J)*gradNu[j]*Hp[i])*tmp;\n                    ke[4*i+3][4*j  ] += vt.x;\n                    ke[4*i+3][4*j+1] += vt.y;\n                    ke[4*i+3][4*j+2] += vt.z;\n                }\n            }\n            \n        } else {\n            // calculate the kup matrix and let kpu be its symmetric part\n            tmp = detJ*gw[n];\n            for (int i=0; i<nel_d; ++i) {\n                for (int j=0; j<nel_p; ++j)\n                {\n                    ke[4*i  ][4*j+3] -= tmp*Hu[j]*gradNp[i].x;\n                    ke[4*i+1][4*j+3] -= tmp*Hu[j]*gradNp[i].y;\n                    ke[4*i+2][4*j+3] -= tmp*Hu[j]*gradNp[i].z;\n                    \n                    ke[4*j+3][4*i  ] -= tmp*Hu[j]*gradNp[i].x;\n                    ke[4*j+3][4*i+1] -= tmp*Hu[j]*gradNp[i].y;\n                    ke[4*j+3][4*i+2] -= tmp*Hu[j]*gradNp[i].z;\n                }\n            }\n        }\n    }\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::Update(const FETimeInfo& tp)\n{\n\tbool berr = false;\n\tint NE = (int) m_Elem.size();\n\t#pragma omp parallel for shared(NE, berr)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\ttry\n\t\t{\n\t\t\tUpdateElementStress(i);\n\t\t}\n\t\tcatch (NegativeJacobian e)\n\t\t{\n\t\t\t#pragma omp critical\n\t\t\t{\n\t\t\t\tberr = true;\n\t\t\t\tif (e.DoOutput()) feLogError(e.what());\n\t\t\t}\n\t\t}\n\t}\n\n    if (berr) throw NegativeJacobianDetected();\n\n\t// also update the nodal pressures\n\tUpdateNodalPressures();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::UpdateElementStress(int iel)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n   // extract the elastic component\n    FEElasticMaterial* pme = m_pMat->GetElasticMaterial();\n\n\t// get the solid element\n\tFESolidElement& el = m_Elem[iel];\n\t\t\n\t// get the number of integration points\n\tint nint = el.GaussPoints();\n\t\t\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n\t// get the number of nodes\n\tint nel_d = el.ShapeFunctions(degree_d);\n\tint nel_p = el.ShapeFunctions(degree_p);\n\n\t// get the nodal data\n\tFEMesh& mesh = *m_pMesh;\n\tvec3d rt[FEElement::MAX_NODES];\n\tdouble pn[FEElement::MAX_NODES];\n    GetCurrentNodalCoordinates(el, rt, 1.0);\n\tfor (int j = 0; j<nel_p; ++j)\n\t{\n\t\tFENode& node = mesh.Node(el.m_node[j]);\n\t\tif (el.m_bitfc.size()>0 && el.m_bitfc[j] && node.m_ID[m_dofQ] != -1)\n\t\t\tpn[j] = node.get(m_dofQ);\n\t\telse\n\t\t\tpn[j] = node.get(m_dofP);\n\t}\n\n\t// loop over the integration points and calculate\n\t// the stress at the integration point\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\t\n\t\t// material point coordinates\n\t\t// TODO: I'm not entirly happy with this solution\n\t\t//\t\t since the material point coordinates are used by most materials.\n\t\tmp.m_rt = el.Evaluate(rt, n);\n\t\t\t\n\t\t// get the deformation gradient and determinant\n\t\tpt.m_J = defgrad(el, pt.m_F, n);\n        mat3d Fp;\n        defgradp(el, Fp, n);\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (pt.m_F - Fp)*Fi / dt;\n\n\t\t// poroelasticity data\n\t\tFEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\t\t\n\t\t// evaluate fluid pressure at gauss-point\n\t\tppt.m_p = el.Evaluate(degree_p, pn, n);\n\t\t\t\n\t\t// calculate the gradient of p at gauss-point\n\t\tppt.m_gradp = gradient(el, degree_p, pn, n);\n\t\t\t\n\t\t// for biphasic materials also update the fluid flux\n\t\tppt.m_w = FluidFlux(mp);\n\t\tppt.m_pa = m_pMat->Pressure(mp);\n\t\t\t\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = (m_secant_stress) ? m_pMat->GetElasticMaterial()->SecantStress(mp) : m_pMat->GetElasticMaterial()->Stress(mp);\n\n\t\t// calculate the stress at this material point\n\t\tpt.m_s = (m_secant_stress) ? m_pMat->SecantStress(mp) : m_pMat->Stress(mp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::BodyForce(FEGlobalVector& R, FEBodyForce& BF)\n{\n\tFEBodyForce* bf = &BF;\n\tLoadVector(R, m_dofU, [=](FEMaterialPoint& mp, int node_a, vector<double>& fa) {\n\n\t\t// get true solid and fluid densities\n\t\tdouble rhoTs = m_pMat->SolidDensity(mp);\n\t\tdouble rhoTw = m_pMat->FluidDensity();\n\n\t\t// Jacobian\n\t\tdouble detJ = mp.m_Jt;\n\n\t\t// get the force\n\t\tvec3d b = bf->force(mp);\n\n\t\t// evaluate apparent solid and fluid densities and mixture density\n\t\tdouble phiw = m_pMat->Porosity(mp);\n\t\tdouble rhos = (1 - phiw)*rhoTs;\n\t\tdouble rhow = phiw*rhoTw;\n\t\tdouble rho = rhos + rhow;\n\n\t\tdouble* H = mp.m_shape;\n\n\t\tfa[0] = -H[node_a] * rho*b.x*detJ;\n\t\tfa[1] = -H[node_a] * rho*b.y*detJ;\n\t\tfa[2] = -H[node_a] * rho*b.z*detJ;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf)\n{\n    FEBiphasic* pmb = dynamic_cast<FEBiphasic*>(GetMaterial()); assert(pmb);\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    for (int iel=0; iel<NE; ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = 4*neln;\n        ke.resize(ndof, ndof);\n        ke.zero();\n        \n        // calculate inertial stiffness\n        ElementBodyForceStiffness(bf, el, ke);\n        \n        // TODO: the problem here is that the LM array that is returned by the UnpackLM\n        // function does not give the equation numbers in the right order. For this reason we\n        // have to create a new lm array and place the equation numbers in the right order.\n        // What we really ought to do is fix the UnpackLM function so that it returns\n        // the LM vector in the right order for poroelastic elements.\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the stiffness due to body forces\nvoid FEBiphasicSolidDomain::ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke)\n{\n    int neln = el.Nodes();\n    \n    // get true solid and fluid densities\n    double rhoTw = m_pMat->FluidDensity();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // jacobian\n    double detJt, Ji[3][3];\n    double *N;\n    double* gw = el.GaussWeights();\n    vec3d gradN[FEElement::MAX_NODES];\n    double *Grn, *Gsn, *Gtn;\n    double Gr, Gs, Gt;\n    \n    vec3d b, kpu;\n    mat3d gradb;\n    mat3d Kw, Kuu;\n    \n    // loop over integration points\n    int nint = el.GaussPoints();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        \n        // get the body force\n        b = BF.force(mp);\n        \n        // get the body force stiffness\n        gradb = BF.stiffness(mp);\n        \n        // evaluate apparent solid and fluid densities and mixture density\n        double phiw = m_pMat->Porosity(mp);\n        double rhos = (1-phiw)*m_pMat->SolidDensity(mp);\n        double rhow = phiw*rhoTw;\n        double rho = rhos + rhow;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->Permeability(mp);\n        tens4dmm dKdE = m_pMat->Tangent_Permeability_Strain(mp);\n        \n        N = el.H(n);\n        \n        // calculate jacobian\n        detJt = invjact(el, Ji, n)*gw[n];\n        \n        Grn = el.Gr(n);\n        Gsn = el.Gs(n);\n        Gtn = el.Gt(n);\n        \n        for (int i=0; i<neln; ++i)\n        {\n            Gr = Grn[i];\n            Gs = Gsn[i];\n            Gt = Gtn[i];\n            \n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN[i] = vec3d(Ji[0][0]*Gr+Ji[1][0]*Gs+Ji[2][0]*Gt,\n                             Ji[0][1]*Gr+Ji[1][1]*Gs+Ji[2][1]*Gt,\n                             Ji[0][2]*Gr+Ji[1][2]*Gs+Ji[2][2]*Gt);\n        }\n        \n        for (int i=0; i<neln; ++i)\n            for (int j=0; j<neln; ++j)\n            {\n                Kw = b & gradN[j];\n                Kuu = (gradb*(N[j]*rho) + Kw*rhoTw)*(N[i]*detJt);\n                ke[4*i  ][4*j  ] += Kuu(0,0); ke[4*i  ][4*j+1] += Kuu(0,1); ke[4*i  ][4*j+2] += Kuu(0,2);\n                ke[4*i+1][4*j  ] += Kuu(1,0); ke[4*i+1][4*j+1] += Kuu(1,1); ke[4*i+1][4*j+2] += Kuu(1,2);\n                ke[4*i+2][4*j  ] += Kuu(2,0); ke[4*i+2][4*j+1] += Kuu(2,1); ke[4*i+2][4*j+2] += Kuu(2,2);\n                \n                kpu = (vdotTdotv(gradN[i], dKdE, gradN[j])*b\n                       + (Kw + gradb*N[j])*K*gradN[i])*(rhoTw*detJt*dt);\n                ke[4*i+3][4*j  ] -= kpu.x; ke[4*i+3][4*j+1] -= kpu.y; ke[4*i+3][4*j+2] -= kpu.z;\n            }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEBiphasicSolidDomain::FluidFlux(FEMaterialPoint& mp)\n{\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// pressure gradient\n\tvec3d gradp = ppt.m_gradp;\n\t\n\t// fluid flux w = -k*grad(p)\n\tmat3ds kt = m_pMat->Permeability(mp);\n    \n    vec3d w = -(kt*gradp);\n    \n    double tau = m_pMat->m_tau;\n    if (tau > 0) {\n        double dt = GetFEModel()->GetTime().timeIncrement;\n        w -= kt*(gradp - ppt.m_gradpp)*(tau/dt);\n    }\n    \n\t// get true fluid density\n\tdouble rhoTw = m_pMat->FluidDensity();\n    \n    // body force contribution\n\tFEModel& fem = *m_pMat->GetFEModel();\n    int nbf = fem.ModelLoads();\n    if (nbf) {\n        vec3d b(0,0,0);\n        for (int i=0; i<nbf; ++i)\n\t\t{\n\t\t\tFEBodyForce* pbf = dynamic_cast<FEBodyForce*>(fem.ModelLoad(i));\n\t\t\tif (pbf && pbf->IsActive())\n\t\t\t{\n\t\t\t\t// negate b because body forces are defined with a negative sign in FEBio\n\t\t\t\tb -= pbf->force(mp);\n\t\t\t}\n\t\t}\n\t\tw += (kt*b)*(rhoTw);\n    }\n    \n    // active momentum supply contribution\n\tFEActiveMomentumSupply* pAmom = m_pMat->GetActiveMomentumSupply();\n    if (pAmom) {\n        vec3d pw = pAmom->ActiveSupply(mp);\n        w += kt*pw;\n    }\n    \n    return w;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolidDomain::UpdateNodalPressures()\n{\n\tvector<double> pi(FEElement::MAX_INTPOINTS);\n\tvector<double> pn(FEElement::MAX_NODES);\n\n\tint NN = Nodes();\n\tvector<int> tag(NN, 0);\n\tm_nodePressure.assign(NN, 0.0);\n\n\tfor (int i = 0; i<Elements(); ++i)\n\t{\n\t\tFESolidElement& el = Element(i);\n\n\t\tint nint = el.GaussPoints();\n\t\tint neln = el.Nodes();\n\n\t\t// get integration point pressures\n\t\tdouble pavg = 0.0;\n\t\tint c = 0;\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tFEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\n\t\t\tif (pt) { pavg += pt->m_pa; c++; }\n\t\t}\n\t\tif (c > 0) pavg /= (double) c;\n\n\t\t// store the nodal values\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tint m = el.m_lnode[j];\n\t\t\tm_nodePressure[m] += pavg;\n\t\t\ttag[m]++;\n\t\t}\n\t}\n\n\tfor (int i=0; i<NN; ++i)\n\t\tif (tag[i] > 0) m_nodePressure[i] /= (double) tag[i];\n}\n\n//-----------------------------------------------------------------------------\n// Note that the data vector stores the values for all of the nodes of the mesh, not just the domain nodes.\n// The values will be set to zero for nodes that don't belong to this domain.\nvoid FEBiphasicSolidDomain::GetNodalPressures(vector<double>& data)\n{\n\tFEMesh& mesh = *GetMesh();\t\n\tdata.resize(mesh.Nodes(), 0.0);\n\n\tint NN = Nodes();\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tdata[NodeIndex(i)] = m_nodePressure[i];\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEDofList.h>\n#include \"FEBiphasicDomain.h\"\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain class for biphasic 3D solid elements\n//! Note that this class inherits from FEElasticSolidDomain since the biphasic domain\n//! also needs to calculate elastic stiffness contributions.\n//!\nclass FEBIOMIX_API FEBiphasicSolidDomain : public FESolidDomain, public FEBiphasicDomain\n{\npublic:\n\t//! constructor\n\tFEBiphasicSolidDomain(FEModel* pfem);\n\n\t//! initialize class\n\tbool Init() override;\n\n\t//! activate\n\tvoid Activate() override;\n\n\t//! reset domain data\n\tvoid Reset() override;\n\n\t//! intitialize element data\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\t//! Unpack solid element data  (overridden from FEDomain)\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override { return m_pMat; }\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! get the total dof list\n\tconst FEDofList& GetDOFList() const override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t// update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t// update element stress\n\tvoid UpdateElementStress(int iel);\n\n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS, bool bsymm) override;\n\n\t//! calculates the global stiffness matrix (steady-state case)\n\tvoid StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) override;\n\t\npublic:\n\t// internal work (overridden from FEElasticDomain)\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n    // internal work (steady-state case)\n    void InternalForcesSS(FEGlobalVector& R) override;\n    \npublic:\n\t//! element internal force vector\n\tvoid ElementInternalForce(FESolidElement& el, vector<double>& fe);\n\t\n    //! element internal force vector (steady-state case)\n    void ElementInternalForceSS(FESolidElement& el, vector<double>& fe);\n    \n\t//! calculates the element biphasic stiffness matrix\n\tbool ElementBiphasicStiffness(FESolidElement& el, matrix& ke, bool bsymm);\n\t\n\t//! calculates the element biphasic stiffness matrix for steady-state response\n\tbool ElementBiphasicStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm);\n\t\npublic: // overridden from FEElasticDomain, but not all implemented in this domain\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override;\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n\tvoid StiffnessMatrix(FELinearSystem& LS) override {}\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override;\n    void ElementBodyForceStiffness(FEBodyForce& BF, FESolidElement &el, matrix &ke);\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override {}\n\npublic: // biphasic domain \"properties\"\n\t// NOTE: I'm thinking about defining properties for domain classes. These would be similar to material\n\t// properties (and may require material properties to be evaluated), but are different in that they are\n\t// not meant to be customized. For example, the biphasic solver assumes Darcy's law in the evaluation \n\t// of the fluid flux. Although one can see the fluid flux as a material property, since the code makes explicit\n\t// use of this constitutive equation (apparent from the fact that the biphasic material needs to define the permeability and its\n\t// strain derivate) it is not a true material property: i.e. it is not meant to be changed and is an inherent\n\t// assumption in this implementation. Consequently, the fluid flux would be a good example of a domain property.\n\t// That is why I've taken this calculation out of the FEBiphasic class and placed it here. \n\tvec3d FluidFlux(FEMaterialPoint& mp) override;\n\n\t// Evaluate the nodal pressures\n\t// Note that the data vector stores the values for all of the nodes of the mesh, not just the domain nodes.\n\t// The values will be set to zero for nodes that don't belong to this domain.\n\tvoid GetNodalPressures(vector<double>& data);\n\nprivate:\n\t// NOTE: This is a temporary construction. Just trying something out here.\n\t// Idea is here to construct a data export for the nodal pressures (the ones from the integration points, not the nodal dofs).\n\tvector<double>\tm_nodePressure;\t//!< nodal pressures projected from the integration points\n\n\t// This function updates the m_nodePressure variable\n\tvoid UpdateNodalPressures();\n\nprotected:\n    bool    m_secant_stress;    //!< use secant approximation to stress\n    bool    m_secant_tangent;   //!< flag for using secant tangent\n    bool    m_secant_perm_tangent;   //!< flag for using secant tangent on permeability\n    \nprotected:\n\tint\t\t\tm_varU, m_varP;\t// displacement, pressure field indices\n\n\tFEDofList\tm_dofU;\t\t// displacement dofs\n\tFEDofList\tm_dofSU;\t// shell displacement dofs\n\tFEDofList\tm_dofR;\t\t// rigid rotation\n\tFEDofList\tm_dof;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSolute.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSolute.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/log.h>\n\n//=============================================================================\n//                 B I P H A S I C S O L U T E\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Material parameters for the FEBiphasicSolute material\nBEGIN_FECORE_CLASS(FEBiphasicSolute, FEMaterial)\n\tADD_PARAMETER(m_phi0 , FE_RANGE_CLOSED(0.0, 1.0)     , \"phi0\");\n\tADD_PARAMETER(m_rhoTw, FE_RANGE_GREATER_OR_EQUAL(0.0), \"fluid_density\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pSolid , \"solid\", FEProperty::Required | FEProperty::TopLevel);\n\tADD_PROPERTY(m_pPerm  , \"permeability\");\n\tADD_PROPERTY(m_pOsmC  , \"osmotic_coefficient\");\n\tADD_PROPERTY(m_pSolute, \"solute\");\n\n    ADD_PROPERTY(m_Q, \"mat_axis\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEBiphasicSolute constructor\n\nFEBiphasicSolute::FEBiphasicSolute(FEModel* pfem) : FEMaterial(pfem)\n{\n\tm_phi0 = 0;\n\tm_rhoTw = 0;\n\tm_rhoTu = 0;\n\tm_Mu = 0;\n\tm_Rgas = 0;\n\tm_Tabs = 0; \n\n\tm_pSolid = 0;\n\tm_pPerm = 0;\n\tm_pOsmC = 0;\n\tm_pSolute = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEBiphasicSolute::CreateMaterialPointData() \n{\n\tFEBiphasicMaterialPoint* pbp = new FEBiphasicMaterialPoint(m_pSolid->CreateMaterialPointData());\n\treturn new FESolutesMaterialPoint(pbp);\n}\n\n//-----------------------------------------------------------------------------\nbool FEBiphasicSolute::Init()\n{\n\t// we need to set the solute ID before we call FEMaterial::Init()\n\t// because it is used in FESolute::Init()\n\tm_pSolute->SetSoluteLocalID(0);\n\n    if (!m_pSolid->Init()) return false;\n    if (!m_pPerm->Init()) return false;\n    if (!m_pOsmC->Init()) return false;\n    if (!m_pSolute->Init()) return false;\n    \n\t// Call base class which calls the Init member of all properties\n\tif (FEMaterial::Init() == false) return false;\n\t\n\tm_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n\tm_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\t\n\tif (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");\t return false; }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// update specialized material points\nvoid FEBiphasicSolute::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    m_pSolid->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pPerm->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pOsmC->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pSolute->UpdateSpecializedMaterialPoints(mp, tp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolute::Serialize(DumpStream& ar)\n{\n\tFEMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Rgas & m_Tabs & m_Mu;\n}\n\n//-----------------------------------------------------------------------------\n//! Porosity in current configuration\ndouble FEBiphasicSolute::Porosity(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pet = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// porosity\n//\tdouble phiw = 1 - m_phi0/J;\n\tdouble phi0 = pet.m_phi0t;\n\tdouble phiw = 1 - phi0/J;\n\t// check for pore collapse\n\t// TODO: throw an error if pores collapse\n\tphiw = (phiw > 0) ? phiw : 0;\n\t\n\treturn phiw;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a solute-poroelastic material is the sum of the fluid pressure\n//! and the elastic stress. Note that this function is declared in the base class\n//! so you do not have to reimplement it in a derived class, unless additional\n//! pressure terms are required.\n\nmat3ds FEBiphasicSolute::Stress(FEMaterialPoint& mp)\n{\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// calculate solid material stress\n\tmat3ds s = m_pSolid->Stress(mp);\n\t\n\t// add fluid pressure\n\ts.xx() -= pt.m_pa;\n\ts.yy() -= pt.m_pa;\n\ts.zz() -= pt.m_pa;\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent is the elastic tangent. Note\n//! that this function is declared in the base class, so you don't have to \n//! reimplement it unless additional tangent components are required.\n\ntens4ds FEBiphasicSolute::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ept = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// call solid tangent routine\n\ttens4ds C = m_pSolid->Tangent(mp);\n\t\n\t// relative volume\n\tdouble J = ept.m_J;\n\t\n\t// fluid pressure and solute concentration\n\tdouble p = ppt.m_pa;\n\tdouble c = spt.m_c[0];\n\t\n\t// solubility and its derivative w.r.t. strain\n\tdouble kappa = m_pSolute->m_pSolub->Solubility(mp);\n\tdouble dkdJ = m_pSolute->m_pSolub->Tangent_Solubility_Strain(mp);\n\t\n\t// osmotic coefficient and its derivative w.r.t. strain\n\tdouble osmc = m_pOsmC->OsmoticCoefficient(mp);\n\tdouble dodJ = m_pOsmC->Tangent_OsmoticCoefficient_Strain(mp);\n\t\n\tdouble dp = m_Rgas*m_Tabs*c*J*(dodJ*kappa+osmc*dkdJ);\n\t\n\t// adjust tangent for pressures\n\tdouble D[6][6] = {0};\n\tC.extract(D);\n\t\n\tD[0][0] -= -p + dp;\n\tD[1][1] -= -p + dp;\n\tD[2][2] -= -p + dp;\n\t\n\tD[0][1] -= p + dp; D[1][0] -= p + dp;\n\tD[1][2] -= p + dp; D[2][1] -= p + dp;\n\tD[0][2] -= p + dp; D[2][0] -= p + dp;\n\t\n\tD[3][3] -= -p;\n\tD[4][4] -= -p;\n\tD[5][5] -= -p;\n\t\n\treturn tens4ds(D);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate fluid flux\n\nvec3d FEBiphasicSolute::FluidFlux(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// fluid volume fraction (porosity) in current configuration\n\tdouble phiw = Porosity(pt);\n\t\n\t// pressure gradient\n\tvec3d gradp = ppt.m_gradp;\n\t\n\t// concentration\n\tdouble c = spt.m_c[0];\n\t\n\t// concentration gradient\n\tvec3d gradc = spt.m_gradc[0];\n\t\n\t// hydraulic permeability\n\tmat3ds kt = m_pPerm->Permeability(pt);\n\t\n\t// solute diffusivity in mixture\n\tmat3ds D = m_pSolute->m_pDiff->Diffusivity(pt);\n\t\n\t// solute free diffusivity\n\tdouble D0 = m_pSolute->m_pDiff->Free_Diffusivity(pt);\n\t\n\t// solubility\n\tdouble kappa = m_pSolute->m_pSolub->Solubility(pt);\n\t\n\t// identity matrix\n\tmat3dd I(1);\n\t\n\t// effective hydraulic permeability\n\tmat3ds ke = kt.inverse() + (I-D/D0)*(m_Rgas*m_Tabs*kappa*c/phiw/D0);\n\tke = ke.inverse();\n\t\n\t// fluid flux w\n\tvec3d w = -(ke*(gradp + (D*gradc)*(m_Rgas*m_Tabs*kappa/D0)));\n\t\n\treturn w;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate solute molar flux\n\nvec3d FEBiphasicSolute::SoluteFlux(FEMaterialPoint& pt)\n{\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// fluid volume fraction (porosity) in current configuration\n\tdouble phiw = Porosity(pt);\n\t\n\t// concentration\n\tdouble c = spt.m_c[0];\n\t\n\t// concentration gradient\n\tvec3d gradc = spt.m_gradc[0];\n\t\n\t// solute diffusivity in mixture\n\tmat3ds D = m_pSolute->m_pDiff->Diffusivity(pt);\n\t\n\t// solute free diffusivity\n\tdouble D0 = m_pSolute->m_pDiff->Free_Diffusivity(pt);\n\t\n\t// solubility\n\tdouble kappa = m_pSolute->m_pSolub->Solubility(pt);\n\t\n\t// fluid flux w\n\tvec3d w = FluidFlux(pt);\n\t\n\t// solute flux j\n\tvec3d j = D*(w*(c/D0) - gradc*phiw)*kappa;\n\t\n\treturn j;\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure\ndouble FEBiphasicSolute::Pressure(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// effective pressure\n\tdouble p = ppt.m_p;\n\t\n\t// effective concentration\n\tdouble c = spt.m_c[0];\n\t\n\t// osmotic coefficient\n\tdouble osmc = m_pOsmC->OsmoticCoefficient(pt);\n\t\n\t// solubility\n\tdouble kappa = m_pSolute->m_pSolub->Solubility(pt);\n\t\n\t// actual pressure\n\tdouble pa = p + m_Rgas*m_Tabs*osmc*kappa*c;\n\t\n\treturn pa;\n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FEBiphasicSolute::Concentration(FEMaterialPoint& pt)\n{\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// solubility\n\tdouble kappa = m_pSolute->m_pSolub->Solubility(pt);\n\t\n\t// actual concentration = solubility * effective concentration\n\tdouble ca = kappa*spt.m_c[0];\n\t\n\treturn ca;\n}\n\n//-----------------------------------------------------------------------------\n//! referential solute concentration\ndouble FEBiphasicSolute::ReferentialConcentration(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\n\tdouble J = ept.m_J;\n\tdouble phiw = Porosity(pt);\n\tdouble cr = J*phiw*Concentration(pt);\n\t\n\treturn cr;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficients and their derivatives\nvoid FEBiphasicSolute::PartitionCoefficientFunctions(FEMaterialPoint& mp, double& kappa,\n                                                double& dkdJ, double& dkdc)\n{\n    // evaluate the solubility and its derivatives w.r.t. J and c\n    kappa = m_pSolute->m_pSolub->Solubility(mp);\n    dkdJ = m_pSolute->m_pSolub->Tangent_Solubility_Strain(mp);\n    dkdc = m_pSolute->m_pSolub->Tangent_Solubility_Concentration(mp,0);\n}\n\ndouble FEBiphasicSolute::GetReferentialFixedChargeDensity(const FEMaterialPoint& mp)\n{\n\tconst FEElasticMaterialPoint* ept = (mp.ExtractData<FEElasticMaterialPoint >());\n\tconst FEBiphasicMaterialPoint* bpt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\tconst FESolutesMaterialPoint* spt = (mp.ExtractData<FESolutesMaterialPoint >());\n\tdouble cf = (ept->m_J - bpt->m_phi0t) * spt->m_cF / (1 - bpt->m_phi0);\n\treturn cf;\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSolute.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FESolute.h\"\n#include \"FEOsmoticCoefficient.h\"\n#include \"FESoluteInterface.h\"\n#include <FECore/FEModelParam.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for solute diffusion in biphasic materials.\n\nclass FEBIOMIX_API FEBiphasicSolute : public FEMaterial, public FEBiphasicInterface, public FESoluteInterface_T<FESolutesMaterialPoint>\n{\npublic:\n\tFEBiphasicSolute(FEModel* pfem);\n\t\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// Get the elastic component (overridden from FEMaterial)\n\tFEElasticMaterial* GetElasticMaterial() { return m_pSolid; }\n\n\t//! Get the solid\n\tFEElasticMaterial* GetSolid() { return m_pSolid; }\n\n\t//! Get the permeability\n\tFEHydraulicPermeability* GetPermeability() { return m_pPerm; }\n\n// solute interface\npublic:\n\t// number of solutes \n\tint Solutes() override { return 1; }\n\n\t//! Get the solute\n\tFESolute* GetSolute(int i=0) override { return (i==0 ? (FESolute*)m_pSolute : 0); }\n\tdouble GetReferentialFixedChargeDensity(const FEMaterialPoint& mp) override;\n\n\tdouble GetFixedChargeDensity(const FEMaterialPoint& mp) override {\n\t\tconst FESolutesMaterialPoint* spt = (mp.ExtractData<FESolutesMaterialPoint>());\n\t\treturn spt->m_cF;\n\t}\n\n\t//! Get the osmotic coefficient\n\tFEOsmoticCoefficient* GetOsmoticCoefficient() override { return m_pOsmC; }\n\npublic: // overridden from FEBiphasicInterface\n    //! Evaluate and return solid referential volume fraction\n    double SolidReferentialVolumeFraction(FEMaterialPoint& mp) override {\n        double phisr = m_phi0(mp);\n        FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n        pt->m_phi0 = pt->m_phi0t = phisr;\n        return phisr;\n    };\n    // Return solid referential volume fraction\n\tdouble GetReferentialSolidVolumeFraction(const FEMaterialPoint& mp) override {\n\t\tconst FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\treturn pt->m_phi0t;\n\t}\n    // Return actual fluid pressure\n\tdouble GetActualFluidPressure(const FEMaterialPoint& mp) override {\n\t\tconst FEBiphasicMaterialPoint* pt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\treturn pt->m_pa;\n\t}\n\npublic:\n\tbool Init() override;\n    \n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt);\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt);\n\t\n\t//! calculate fluid (solvent) flux\n\tvec3d FluidFlux(FEMaterialPoint& pt);\n\t\n\t//! calculate solute molar flux\n\tvec3d SoluteFlux(FEMaterialPoint& pt);\n\t\n\t//! actual fluid pressure (as opposed to effective pressure)\n\tdouble Pressure(FEMaterialPoint& pt);\n\t\n\t//! actual concentration (as opposed to effective concentration)\n\tdouble Concentration(FEMaterialPoint& pt);\n\n\t//! referential concentration (normalized to mixture volume in reference state)\n\tdouble ReferentialConcentration(FEMaterialPoint& pt);\n\n\t//! porosity\n\tdouble Porosity(FEMaterialPoint& pt);\n\t\n    //! partition coefficient derivatives\n    void PartitionCoefficientFunctions(FEMaterialPoint& mp, double& kappa,\n                                       double& dkdJ, double& dkdc);\n\t//! fluid density\n\tdouble FluidDensity() { return m_rhoTw; }\n\t\npublic: // material parameters\n\tdouble\t\t\t\t\t\tm_rhoTw;\t\t//!< true fluid density\n\tFEParamDouble\t\t\t\tm_phi0;\t\t\t//!< solid volume fraction in reference configuration\n\npublic:\n\tdouble\t\t\t\t\t\tm_Mu;\t\t\t//!< solute molecular weight\n\tdouble\t\t\t\t\t\tm_rhoTu;\t\t//!< true solute density\n\tdouble\t\t\t\t\t\tm_Rgas;\t\t\t//!< universal gas constant\n\tdouble\t\t\t\t\t\tm_Tabs;\t\t\t//!< absolute temperature\n\nprivate: // material properties\n\tFEElasticMaterial*\t\t\tm_pSolid;\t\t//!< pointer to elastic solid material\n\tFEHydraulicPermeability*\tm_pPerm;\t\t//!< pointer to permeability material\n\tFEOsmoticCoefficient*\t\tm_pOsmC;\t\t//!< pointer to osmotic coefficient material\n\tFESolute*\t\t\t\t\tm_pSolute;\t\t//!< pointer to solute material\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEBiphasicSoluteAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEBiphasicSoluteAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0TRANSIENT\\0\");\nEND_FECORE_CLASS()\n\nFEBiphasicSoluteAnalysis::FEBiphasicSoluteAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEBiphasicSoluteAnalysis : public FEAnalysis\n{\npublic:\n\tenum BiphasicSoluteAnalysisType {\n\t\tSTEADY_STATE,\n\t\tTRANSIENT\n\t};\n\npublic:\n\tFEBiphasicSoluteAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFEBiphasicSoluteDomain::FEBiphasicSoluteDomain(FEModel* pfem) : FEElasticDomain(pfem)\n{\n    m_pMat = nullptr;\n\n\tif (pfem)\n\t{\n\t\tm_dofP = pfem->GetDOFIndex(\"p\");\n\t\tm_dofQ = pfem->GetDOFIndex(\"q\");\n\n\t\tm_dofC = pfem->GetDOFIndex(\"concentration\", 0);\n\t\tm_dofD = pfem->GetDOFIndex(\"shell concentration\", 0);\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEElasticDomain.h\"\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalVector;\nclass FEBodyForce;\nclass FESolver;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for biphasic-solute domains.\n\n//! A biphasic domain is used by the biphasic solver.\n//! This interface defines the functions that have to be implemented by a\n//! biphasic domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOMIX_API FEBiphasicSoluteDomain : public FEElasticDomain\n{\npublic:\n    FEBiphasicSoluteDomain(FEModel* pfem);\n    virtual ~FEBiphasicSoluteDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! internal work for steady-state case\n    virtual void InternalForcesSS(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! calculates the global stiffness matrix for this domain\n    virtual void StiffnessMatrix(FELinearSystem& LS, bool bsymm) = 0;\n    \n    //! calculates the global stiffness matrix (steady-state case)\n    virtual void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) = 0;\n    \nprotected:\n    FEBiphasicSolute*   m_pMat;\n    int                 m_dofP;\t\t//!< pressure dof index\n    int                 m_dofQ;     //!< shell extra pressure dof index\n    int                 m_dofC;\t\t//!< concentration dof index\n    int                 m_dofD;\t\t//!< shell extra concentration dof index\n    int                 m_dofVX;\n    int                 m_dofVY;\n    int                 m_dofVZ;\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSoluteShellDomain.h\"\n#include \"FECore/FEMaterial.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FEBiphasicAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nFEBiphasicSoluteShellDomain::FEBiphasicSoluteShellDomain(FEModel* pfem) : FESSIShellDomain(pfem), FEBiphasicSoluteDomain(pfem), m_dof(pfem)\n{\n\tif (pfem)\n\t{\n\t\tm_dofSX = pfem->GetDOFIndex(\"sx\");\n\t\tm_dofSY = pfem->GetDOFIndex(\"sy\");\n\t\tm_dofSZ = pfem->GetDOFIndex(\"sz\");\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the material (overridden from FEDomain)\nFEMaterial* FEBiphasicSoluteShellDomain::GetMaterial()\n{\n\treturn m_pMat;\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof\nconst FEDofList& FEBiphasicSoluteShellDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FEBiphasicSolute*>(pmat);\n    assert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::Activate()\n{\n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    \n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n\n                if (node.HasFlags(FENode::SHELL))\n                {\n                    node.set_active(m_dofSU[0]);\n                    node.set_active(m_dofSU[1]);\n                    node.set_active(m_dofSU[2]);\n                }\n            }\n            \n            node.set_active(m_dofP);\n            node.set_active(dofc  );\n\n            if (node.HasFlags(FENode::SHELL)) {\n                node.set_active(m_dofQ);\n                node.set_active(dofd  );\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::InitMaterialPoints()\n{\n    FEMesh& m = *GetMesh();\n    \n    const int NE = FEElement::MAX_NODES;\n    double p0[NE], q0[NE], c0[NE], d0[NE];\n    \n    int id0 = m_pMat->GetSolute()->GetSoluteDOF();\n    \n    for (int i = 0; i<(int)m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the number of nodes\n        int neln = el.Nodes();\n        // get initial values of fluid pressure and solute concentrations\n        for (int i = 0; i<neln; ++i)\n        {\n            p0[i] = m.Node(el.m_node[i]).get(m_dofP);\n            q0[i] = m.Node(el.m_node[i]).get(m_dofQ);\n            c0[i] = m.Node(el.m_node[i]).get(m_dofC + id0);\n            d0[i] = m.Node(el.m_node[i]).get(m_dofD + id0);\n        }\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEElasticMaterialPoint& pm = *(mp.ExtractData<FEElasticMaterialPoint>());\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            // initialize effective fluid pressure, its gradient, and fluid flux\n            pt.m_p = evaluate(el, p0, q0, n);\n            pt.m_gradp = gradient(el, p0, q0, n);\n            pt.m_w = m_pMat->FluidFlux(mp);\n            \n            // initialize effective solute concentrations\n            ps.m_c[0] = evaluate(el, c0, d0, n);\n            ps.m_gradc[0] = gradient(el, c0, d0, n);\n            ps.m_ca[0] = m_pMat->Concentration(mp);\n            ps.m_j[0] = m_pMat->SoluteFlux(mp);\n            ps.m_crp[0] = pm.m_J*m_pMat->Porosity(mp)*ps.m_ca[0];\n            pt.m_pa = m_pMat->Pressure(mp);\n            \n            // initialize referential solid volume fraction\n            pt.m_phi0t = m_pMat->m_phi0(mp);\n            \n            // calculate stress\n            pm.m_s = m_pMat->Stress(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEBiphasicSoluteShellDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    int N = el.Nodes();\n    int ndpn = 10;\n    lm.resize(N*(ndpn+3));\n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        FENode& node = m_pMesh->Node(n);\n        \n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[ndpn*i  ] = id[m_dofU[0]];\n        lm[ndpn*i+1] = id[m_dofU[1]];\n        lm[ndpn*i+2] = id[m_dofU[2]];\n        \n        // next the rotational dofs\n        lm[ndpn*i+3] = id[m_dofSU[0]];\n        lm[ndpn*i+4] = id[m_dofSU[1]];\n        lm[ndpn*i+5] = id[m_dofSU[2]];\n        \n        // now the pressure dofs\n        lm[ndpn*i+6] = id[m_dofP];\n        lm[ndpn*i+7] = id[m_dofQ];\n        \n        // concentration dofs\n        lm[ndpn*i+8] = id[dofc];\n        lm[ndpn*i+9] = id[dofd];\n        \n        // rigid rotational dofs\n        // TODO: Do I really need this\n        lm[ndpn*N + 3*i  ] = id[m_dofR[0]];\n        lm[ndpn*N + 3*i+1] = id[m_dofR[1]];\n        lm[ndpn*N + 3*i+2] = id[m_dofR[2]];\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::Reset()\n{\n    // reset base class\n    FESSIShellDomain::Reset();\n    \n    const int nsol = 1;\n    const int nsbm = 1;\n\n\t// loop over all material points\n\tForEachMaterialPoint([=](FEMaterialPoint& mp) {\n        FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  ps = *(mp.ExtractData<FESolutesMaterialPoint >());\n            \n        // initialize referential solid volume fraction\n        pt.m_phi0 = pt.m_phi0t = m_pMat->m_phi0(mp);\n            \n        // initialize multiphasic solutes\n        ps.m_nsol = nsol;\n        ps.m_c.assign(nsol,0);\n        ps.m_ca.assign(nsol,0);\n        ps.m_crp.assign(nsol, 0);\n        ps.m_gradc.assign(nsol,vec3d(0,0,0));\n        ps.m_k.assign(nsol, 0);\n        ps.m_dkdJ.assign(nsol, 0);\n        ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n        ps.m_j.assign(nsol,vec3d(0,0,0));\n        ps.m_nsbm = nsbm;\n        ps.m_sbmr.assign(nsbm,0);\n        ps.m_sbmrp.assign(nsbm,0);\n        ps.m_sbmrhat.assign(nsbm,0);\n        ps.m_sbmrhatp.assign(nsbm,0);\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    FESSIShellDomain::PreSolveUpdate(timeInfo);\n    \n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    \n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    double pn[NE], qn[NE], p;\n    double cn[NE], dn[NE], c;\n    FEMesh& m = *GetMesh();\n    for (size_t iel=0; iel<m_Elem.size(); ++iel)\n    {\n        FEShellElement& el = m_Elem[iel];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n            xt[i] = m.Node(el.m_node[i]).m_rt;\n            pn[i] = m.Node(el.m_node[i]).get(m_dofP);\n            qn[i] = m.Node(el.m_node[i]).get(m_dofQ);\n            cn[i] = m.Node(el.m_node[i]).get(dofc);\n            dn[i] = m.Node(el.m_node[i]).get(dofd);\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            r0 = el.Evaluate(x0, j);\n            rt = el.Evaluate(xt, j);\n            p = evaluate(el, pn, qn, j);\n            c = evaluate(el, cn, dn, j);\n            \n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pb = *mp.ExtractData<FEBiphasicMaterialPoint>();\n            FESolutesMaterialPoint&  ps = *(mp.ExtractData<FESolutesMaterialPoint >());\n            mp.m_r0 = r0;\n            mp.m_rt = rt;\n            \n            pt.m_J = defgrad(el, pt.m_F, j);\n            \n            pb.m_Jp = pt.m_J;\n            \n            pb.m_p = p;\n            pb.m_gradp = gradient(el, pn, qn, j);\n            pb.m_phi0p = pb.m_phi0t;\n            \n            ps.m_c[0] = c;\n            ps.m_gradc[0] = gradient(el, cn, dn, j);\n            \n            // reset referential actual solute concentration at previous time\n            ps.m_crp[0] = pt.m_J*m_pMat->Porosity(mp)*ps.m_ca[0];\n            // reset referential receptor-ligand complex concentration at previous time\n            ps.m_sbmrp[0] = ps.m_sbmr[0];\n            // reset referential receptor-ligand complex concentration supply at previous time\n            ps.m_sbmrhatp[0] = ps.m_sbmrhat[0];\n\n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 10*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEBiphasicSoluteShellDomain::ElementInternalForce(FEShellElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradM, gradMu, gradMw;\n    double Mu, Mw;\n    mat3ds s;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    vec3d gcnt[3];\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // next we get the determinant\n        double Jp = bpt.m_Jp;\n        double J = pt.m_J;\n        \n        // and then finally\n        double divv = ((J-Jp)/dt)/J;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solute flux\n        vec3d& j = spt.m_j[0];\n        \n        // Evaluate porosity and solute supply and receptor-ligand kinetics\n        double phiw = m_pMat->Porosity(mp);\n        double crhat = 0;\n        if (m_pMat->GetSolute()->m_pSupp) crhat = m_pMat->GetSolute()->m_pSupp->Supply(mp);\n        \n        for (i=0; i<neln; ++i)\n        {\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu = (1+eta)/2*M[i];\n            Mw = (1-eta)/2*M[i];\n            \n            // calculate internal force\n            vec3d fu = s*gradMu;\n            vec3d fw = s*gradMw;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[10*i  ] -= fu.x*detJt;\n            fe[10*i+1] -= fu.y*detJt;\n            fe[10*i+2] -= fu.z*detJt;\n            fe[10*i+3] -= fw.x*detJt;\n            fe[10*i+4] -= fw.y*detJt;\n            fe[10*i+5] -= fw.z*detJt;\n            fe[10*i+6] -= dt*(w*gradMu - divv*Mu)*detJt;\n            fe[10*i+7] -= dt*(w*gradMw - divv*Mw)*detJt;\n            fe[10*i+8] -= dt*(gradMu*j\n                             + Mu*(crhat/J - (phiw*spt.m_ca[0] - spt.m_crp[0]/J)/dt)\n                             )*detJt;\n            fe[10*i+9] -= dt*(gradMw*j\n                             + Mw*(crhat/J - (phiw*spt.m_ca[0] - spt.m_crp[0]/J)/dt)\n                             )*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 10*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEBiphasicSoluteShellDomain::ElementInternalForceSS(FEShellElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradM, gradMu, gradMw;\n    double Mu, Mw;\n    mat3ds s;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // next we get the determinant\n        double J = pt.m_J;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solute flux\n        vec3d& j = spt.m_j[0];\n        \n        // Evaluate solute supply and receptor-ligand kinetics\n        double crhat = 0;\n        if (m_pMat->GetSolute()->m_pSupp) crhat = m_pMat->GetSolute()->m_pSupp->Supply(mp);\n        \n        for (i=0; i<neln; ++i)\n        {\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu = (1+eta)/2*M[i];\n            Mw = (1-eta)/2*M[i];\n            \n            // calculate internal force\n            vec3d fu = s*gradMu;\n            vec3d fw = s*gradMw;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[10*i  ] -= fu.x*detJt;\n            fe[10*i+1] -= fu.y*detJt;\n            fe[10*i+2] -= fu.z*detJt;\n            fe[10*i+3] -= fw.x*detJt;\n            fe[10*i+4] -= fw.y*detJt;\n            fe[10*i+5] -= fw.z*detJt;\n            fe[10*i+6] -= dt*(w*gradMu)*detJt;\n            fe[10*i+7] -= dt*(w*gradMw)*detJt;\n            fe[10*i+8] -= dt*(gradMu*j + Mu*(crhat/J))*detJt;\n            fe[10*i+9] -= dt*(gradMw*j + Mw*(crhat/J))*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        \n        // allocate stiffness matrix\n        int neln = el.Nodes();\n        int ndof = neln*10;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementBiphasicSoluteStiffness(el, ke, bsymm);\n\n\t\t// get lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n\n//-----------------------------------------------------------------------------\n\nvoid FEBiphasicSoluteShellDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = neln*10;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementBiphasicSoluteStiffnessSS(el, ke, bsymm);\n\n\t\t// get lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FEBiphasicSoluteShellDomain::ElementBiphasicSoluteStiffness(FEShellElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, n;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    const double* Mr, *Ms, *M;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradMu(neln), gradMw(neln);\n    vector<double> Mu(neln), Mw(neln);\n    vec3d gradM;\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Mw[i] = (1-eta)/2*M[i];\n        }\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // get the fluid flux and pressure gradient\n        vec3d gradp = ppt.m_gradp;\n        vec3d w = ppt.m_w;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        mat3ds dKdc = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp, 0);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        \n        // get the effective concentration, its gradient and its time derivative\n        double c = spt.m_c[0];\n        vec3d gradc = spt.m_gradc[0];\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the solubility and its derivatives\n        double kappa = spt.m_k[0];\n        double dkdJ = spt.m_dkdJ[0];\n        double dkdc = spt.m_dkdc[0][0];\n        \n        // evaluate the diffusivity tensor and its derivatives\n        mat3ds D = m_pMat->GetSolute()->m_pDiff->Diffusivity(mp);\n        mat3ds dDdc = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Concentration(mp, 0);\n        tens4dmm dDdE = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Strain(mp);\n        \n        // evaluate the solute free diffusivity\n        double D0 = m_pMat->GetSolute()->m_pDiff->Free_Diffusivity(mp);\n        double dD0dc = m_pMat->GetSolute()->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,0);\n        \n        // evaluate the osmotic coefficient and its derivatives\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodc = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp, 0);\n        \n        // evaluate the stress tangent with concentration\n        //\t\tmat3ds dTdc = pm->GetSolid()->Tangent_Concentration(mp, 0);\n        mat3ds dTdc(0,0,0,0,0,0);\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds ImD = I-D/D0;\n        mat3ds Ke = (Ki + ImD*(R*T*kappa*c/phiw/D0)).inverse();\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE)\n        +dyad1(ImD,I)*(R*T*c*J/D0/phiw*(dkdJ-kappa/phiw*dpdJ))\n        +(dyad1(I,I) - dyad2(I,I)*2 - dDdE/D0)*(R*T*kappa*c/phiw/D0);\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        mat3ds Gc = -(Ki*dKdc*Ki).sym() + ImD*(R*T/phiw/D0*(dkdc*c+kappa-kappa*c/D0*dD0dc))\n        +R*T*kappa*c/phiw/D0/D0*(D*dD0dc/D0 - dDdc);\n        mat3ds dKedc = -(Ke*Gc*Ke).sym();\n        \n        // evaluate the tangents of solute supply\n        double dcrhatdJ = 0;\n        double dcrhatdc = 0;\n        if (m_pMat->GetSolute()->m_pSupp)\n        {\n            dcrhatdJ = m_pMat->GetSolute()->m_pSupp->Tangent_Supply_Strain(mp);\n            double dcrhatdcr = m_pMat->GetSolute()->m_pSupp->Tangent_Supply_Concentration(mp);\n            dcrhatdc = J*phiw*(kappa + c*dkdc)*dcrhatdcr;\n        }\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,gc,qpu,qpw,qcu,qcw,wc,wd,jc,jd;\n        mat3d wu,ww,ju,jw;\n        double qcc, qcd;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradMu[i]*(s*gradMu[j])) + vdotTdotv(gradMu[i], C, gradMu[j]))*detJ;\n                mat3d Kuw = (mat3dd(gradMu[i]*(s*gradMw[j])) + vdotTdotv(gradMu[i], C, gradMw[j]))*detJ;\n                mat3d Kwu = (mat3dd(gradMw[i]*(s*gradMu[j])) + vdotTdotv(gradMw[i], C, gradMu[j]))*detJ;\n                mat3d Kww = (mat3dd(gradMw[i]*(s*gradMw[j])) + vdotTdotv(gradMw[i], C, gradMw[j]))*detJ;\n                \n                ke[10*i  ][10*j  ] += Kuu[0][0]; ke[10*i  ][10*j+1] += Kuu[0][1]; ke[10*i  ][10*j+2] += Kuu[0][2];\n                ke[10*i+1][10*j  ] += Kuu[1][0]; ke[10*i+1][10*j+1] += Kuu[1][1]; ke[10*i+1][10*j+2] += Kuu[1][2];\n                ke[10*i+2][10*j  ] += Kuu[2][0]; ke[10*i+2][10*j+1] += Kuu[2][1]; ke[10*i+2][10*j+2] += Kuu[2][2];\n                \n                ke[10*i  ][10*j+3] += Kuw[0][0]; ke[10*i  ][10*j+4] += Kuw[0][1]; ke[10*i  ][10*j+5] += Kuw[0][2];\n                ke[10*i+1][10*j+3] += Kuw[1][0]; ke[10*i+1][10*j+4] += Kuw[1][1]; ke[10*i+1][10*j+5] += Kuw[1][2];\n                ke[10*i+2][10*j+3] += Kuw[2][0]; ke[10*i+2][10*j+4] += Kuw[2][1]; ke[10*i+2][10*j+5] += Kuw[2][2];\n                \n                ke[10*i+3][10*j  ] += Kwu[0][0]; ke[10*i+3][10*j+1] += Kwu[0][1]; ke[10*i+3][10*j+2] += Kwu[0][2];\n                ke[10*i+4][10*j  ] += Kwu[1][0]; ke[10*i+4][10*j+1] += Kwu[1][1]; ke[10*i+4][10*j+2] += Kwu[1][2];\n                ke[10*i+5][10*j  ] += Kwu[2][0]; ke[10*i+5][10*j+1] += Kwu[2][1]; ke[10*i+5][10*j+2] += Kwu[2][2];\n                \n                ke[10*i+3][10*j+3] += Kww[0][0]; ke[10*i+3][10*j+4] += Kww[0][1]; ke[10*i+3][10*j+5] += Kww[0][2];\n                ke[10*i+4][10*j+3] += Kww[1][0]; ke[10*i+4][10*j+4] += Kww[1][1]; ke[10*i+4][10*j+5] += Kww[1][2];\n                ke[10*i+5][10*j+3] += Kww[2][0]; ke[10*i+5][10*j+4] += Kww[2][1]; ke[10*i+5][10*j+5] += Kww[2][2];\n                \n                // calculate the kup matrix\n                vec3d kup = gradMu[i]*(-Mu[j]*detJ);\n                vec3d kuq = gradMu[i]*(-Mw[j]*detJ);\n                vec3d kwp = gradMw[i]*(-Mu[j]*detJ);\n                vec3d kwq = gradMw[i]*(-Mw[j]*detJ);\n                \n                ke[10*i  ][10*j+6] += kup.x; ke[10*i  ][10*j+7] += kuq.x;\n                ke[10*i+1][10*j+6] += kup.y; ke[10*i+1][10*j+7] += kuq.y;\n                ke[10*i+2][10*j+6] += kup.z; ke[10*i+2][10*j+7] += kuq.z;\n                \n                ke[10*i+3][10*j+6] += kwp.x; ke[10*i+3][10*j+7] += kwq.x;\n                ke[10*i+4][10*j+6] += kwp.y; ke[10*i+4][10*j+7] += kwq.y;\n                ke[10*i+5][10*j+6] += kwp.z; ke[10*i+5][10*j+7] += kwq.z;\n                \n                // calculate the kuc matrix\n                vec3d kuc = (dTdc*gradMu[i] - gradMu[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mu[j]*detJ;\n                vec3d kud = (dTdc*gradMu[i] - gradMu[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mw[j]*detJ;\n                vec3d kwc = (dTdc*gradMw[i] - gradMw[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mu[j]*detJ;\n                vec3d kwd = (dTdc*gradMw[i] - gradMw[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mw[j]*detJ;\n                \n                ke[10*i  ][10*j+8] += kuc.x; ke[10*i  ][10*j+9] += kud.x;\n                ke[10*i+1][10*j+8] += kuc.y; ke[10*i+1][10*j+9] += kud.y;\n                ke[10*i+2][10*j+8] += kuc.z; ke[10*i+2][10*j+9] += kud.z;\n                \n                ke[10*i+3][10*j+8] += kwc.x; ke[10*i+3][10*j+9] += kwd.x;\n                ke[10*i+4][10*j+8] += kwc.y; ke[10*i+4][10*j+9] += kwd.y;\n                ke[10*i+5][10*j+8] += kwc.z; ke[10*i+5][10*j+9] += kwd.z;\n                \n                // calculate the kpu matrix\n                gp = gradp+(D*gradc)*R*T*kappa/D0;\n                wu = vdotTdotv(-gp, dKedE, gradMu[j])\n                -(((Ke*(D*gradc)) & gradMu[j])*(J*dkdJ - kappa)\n                  +Ke*(2*kappa*(gradMu[j]*(D*gradc))))*R*T/D0\n                - Ke*vdotTdotv(gradc, dDdE, gradMu[j])*(kappa*R*T/D0);\n                ww = vdotTdotv(-gp, dKedE, gradMw[j])\n                -(((Ke*(D*gradc)) & gradMw[j])*(J*dkdJ - kappa)\n                  +Ke*(2*kappa*(gradMw[j]*(D*gradc))))*R*T/D0\n                - Ke*vdotTdotv(gradc, dDdE, gradMw[j])*(kappa*R*T/D0);\n                qpu = -gradMu[j]*(1.0/dt);\n                qpw = -gradMw[j]*(1.0/dt);\n                vec3d kpu = (wu.transpose()*gradMu[i] + qpu*Mu[i])*(detJ*dt);\n                vec3d kpw = (ww.transpose()*gradMu[i] + qpw*Mu[i])*(detJ*dt);\n                vec3d kqu = (wu.transpose()*gradMw[i] + qpu*Mw[i])*(detJ*dt);\n                vec3d kqw = (ww.transpose()*gradMw[i] + qpw*Mw[i])*(detJ*dt);\n                ke[10*i+6][10*j  ] += kpu.x; ke[10*i+6][10*j+1] += kpu.y; ke[10*i+6][10*j+2] += kpu.z;\n                ke[10*i+6][10*j+3] += kpw.x; ke[10*i+6][10*j+4] += kpw.y; ke[10*i+6][10*j+5] += kpw.z;\n                ke[10*i+7][10*j  ] += kqu.x; ke[10*i+7][10*j+1] += kqu.y; ke[10*i+7][10*j+2] += kqu.z;\n                ke[10*i+7][10*j+3] += kqw.x; ke[10*i+7][10*j+4] += kqw.y; ke[10*i+7][10*j+5] += kqw.z;\n                \n                // calculate the kpp matrix\n                ke[10*i+6][10*j+6] -= gradMu[i]*(Ke*gradMu[j])*(detJ*dt); ke[10*i+6][10*j+7] -= gradMu[i]*(Ke*gradMw[j])*(detJ*dt);\n                ke[10*i+7][10*j+6] -= gradMw[i]*(Ke*gradMu[j])*(detJ*dt); ke[10*i+7][10*j+7] -= gradMw[i]*(Ke*gradMw[j])*(detJ*dt);\n                \n                // calculate the kpc matrix\n                wc = (dKedc*gp)*(-Mu[j])\n                -Ke*((((D*(dkdc-kappa*dD0dc/D0)+dDdc*kappa)*gradc)*Mu[j]\n                      +(D*gradMu[j])*kappa)*(R*T/D0));\n                wd = (dKedc*gp)*(-Mw[j])\n                -Ke*((((D*(dkdc-kappa*dD0dc/D0)+dDdc*kappa)*gradc)*Mw[j]\n                      +(D*gradMw[j])*kappa)*(R*T/D0));\n                ke[10*i+6][10*j+8] += (gradMu[i]*wc)*(detJ*dt);\n                ke[10*i+6][10*j+9] += (gradMu[i]*wd)*(detJ*dt);\n                ke[10*i+7][10*j+8] += (gradMw[i]*wc)*(detJ*dt);\n                ke[10*i+7][10*j+9] += (gradMw[i]*wd)*(detJ*dt);\n                \n                // calculate the kcu matrix\n                gc = -gradc*phiw + w*c/D0;\n                ju = ((D*gc) & gradMu[j])*(J*dkdJ)\n                + vdotTdotv(gc, dDdE, gradMu[j])*kappa\n                + (((D*gradc) & gradMu[j])*(-phis)\n                   +(D*((gradMu[j]*w)*2) - ((D*w) & gradMu[j]))*c/D0\n                   )*kappa\n                +D*wu*(kappa*c/D0);\n                jw = ((D*gc) & gradMw[j])*(J*dkdJ)\n                + vdotTdotv(gc, dDdE, gradMw[j])*kappa\n                + (((D*gradc) & gradMw[j])*(-phis)\n                   +(D*((gradMw[j]*w)*2) - ((D*w) & gradMw[j]))*c/D0\n                   )*kappa\n                +D*ww*(kappa*c/D0);\n                qcu = qpu*(c*(kappa+J*phiw*dkdJ));\n                qcw = qpw*(c*(kappa+J*phiw*dkdJ));\n                vec3d kcu = (ju.transpose()*gradMu[i] + qcu*Mu[i])*(detJ*dt);\n                vec3d kcw = (jw.transpose()*gradMu[i] + qcw*Mu[i])*(detJ*dt);\n                vec3d kdu = (ju.transpose()*gradMw[i] + qcu*Mw[i])*(detJ*dt);\n                vec3d kdw = (jw.transpose()*gradMw[i] + qcw*Mw[i])*(detJ*dt);\n                ke[10*i+8][10*j  ] += kcu.x; ke[10*i+8][10*j+1] += kcu.y; ke[10*i+8][10*j+2] += kcu.z;\n                ke[10*i+8][10*j+3] += kcw.x; ke[10*i+8][10*j+4] += kcw.y; ke[10*i+8][10*j+5] += kcw.z;\n                ke[10*i+9][10*j  ] += kdu.x; ke[10*i+9][10*j+1] += kdu.y; ke[10*i+9][10*j+2] += kdu.z;\n                ke[10*i+9][10*j+3] += kdw.x; ke[10*i+9][10*j+4] += kdw.y; ke[10*i+9][10*j+5] += kdw.z;\n                \n                // calculate the kcp matrix\n                ke[10*i+8][10*j+6] -= (gradMu[i]*((D*Ke)*gradMu[j]))*(kappa*c/D0)*(detJ*dt); ke[10*i+8][10*j+7] -= (gradMu[i]*((D*Ke)*gradMw[j]))*(kappa*c/D0)*(detJ*dt);\n                ke[10*i+9][10*j+6] -= (gradMw[i]*((D*Ke)*gradMu[j]))*(kappa*c/D0)*(detJ*dt); ke[10*i+9][10*j+7] -= (gradMw[i]*((D*Ke)*gradMw[j]))*(kappa*c/D0)*(detJ*dt);\n                \n                // calculate the kcc matrix\n                jc = (D*(-gradMu[j]*phiw+w*(Mu[j]/D0)))*kappa\n                +((D*dkdc+dDdc*kappa)*gc)*Mu[j]\n                +(D*(w*(-Mu[j]*dD0dc/D0)+wc))*(kappa*c/D0);\n                jd = (D*(-gradMw[j]*phiw+w*(Mw[j]/D0)))*kappa\n                +((D*dkdc+dDdc*kappa)*gc)*Mw[j]\n                +(D*(w*(-Mw[j]*dD0dc/D0)+wd))*(kappa*c/D0);\n                qcc = -Mu[j]*phiw/dt*(c*dkdc + kappa);\n                qcd = -Mw[j]*phiw/dt*(c*dkdc + kappa);\n                ke[10*i+8][10*j+8] += (gradMu[i]*jc + Mu[i]*qcc)*(detJ*dt);\n                ke[10*i+8][10*j+9] += (gradMu[i]*jd + Mu[i]*qcd)*(detJ*dt);\n                ke[10*i+9][10*j+8] += (gradMw[i]*jc + Mw[i]*qcc)*(detJ*dt);\n                ke[10*i+9][10*j+9] += (gradMw[i]*jd + Mw[i]*qcd)*(detJ*dt);\n                \n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<5*neln; ++i)\n            for (j=i+1; j<5*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for steady-state response (zero solid velocity, zero time derivative of\n//! solute concentration)\n//!\nbool FEBiphasicSoluteShellDomain::ElementBiphasicSoluteStiffnessSS(FEShellElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, n;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    const double* Mr, *Ms, *M;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradMu(neln), gradMw(neln);\n    vector<double> Mu(neln), Mw(neln);\n    vec3d gradM;\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Mw[i] = (1-eta)/2*M[i];\n        }\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // get the fluid flux and pressure gradient\n        vec3d gradp = ppt.m_gradp;\n        vec3d w = ppt.m_w;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        mat3ds dKdc = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp, 0);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        \n        // get the effective concentration, its gradient and its time derivative\n        double c = spt.m_c[0];\n        vec3d gradc = spt.m_gradc[0];\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the solubility and its derivatives\n        double kappa = spt.m_k[0];\n        double dkdJ = spt.m_dkdJ[0];\n        double dkdc = spt.m_dkdc[0][0];\n        \n        // evaluate the diffusivity tensor and its derivatives\n        mat3ds D = m_pMat->GetSolute()->m_pDiff->Diffusivity(mp);\n        mat3ds dDdc = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Concentration(mp, 0);\n        tens4dmm dDdE = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Strain(mp);\n        \n        // evaluate the solute free diffusivity\n        double D0 = m_pMat->GetSolute()->m_pDiff->Free_Diffusivity(mp);\n        double dD0dc = m_pMat->GetSolute()->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,0);\n        \n        // evaluate the osmotic coefficient and its derivatives\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodc = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp, 0);\n        \n        // evaluate the stress tangent with concentration\n        //\t\tmat3ds dTdc = pm->GetSolid()->Tangent_Concentration(mp, 0);\n        mat3ds dTdc(0,0,0,0,0,0);\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds ImD = I-D/D0;\n        mat3ds Ke = (Ki + ImD*(R*T*kappa*c/phiw/D0)).inverse();\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE)\n        +dyad1(ImD,I)*(R*T*c*J/D0/phiw*(dkdJ-kappa/phiw*dpdJ))\n        +(dyad1(I,I) - dyad2(I,I)*2 - dDdE/D0)*(R*T*kappa*c/phiw/D0);\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        mat3ds Gc = -(Ki*dKdc*Ki).sym() + ImD*(R*T/phiw/D0*(dkdc*c+kappa-kappa*c/D0*dD0dc))\n        +R*T*kappa*c/phiw/D0/D0*(D*dD0dc/D0 - dDdc);\n        mat3ds dKedc = -(Ke*Gc*Ke).sym();\n        \n        // evaluate the tangents of solute supply\n        double dcrhatdJ = 0;\n        double dcrhatdc = 0;\n        if (m_pMat->GetSolute()->m_pSupp)\n        {\n            dcrhatdJ = m_pMat->GetSolute()->m_pSupp->Tangent_Supply_Strain(mp);\n            double dcrhatdcr = m_pMat->GetSolute()->m_pSupp->Tangent_Supply_Concentration(mp);\n            dcrhatdc = J*phiw*(kappa + c*dkdc)*dcrhatdcr;\n        }\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,gc,wc,wd,jc,jd;\n        mat3d wu,ww,ju,jw;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradMu[i]*(s*gradMu[j])) + vdotTdotv(gradMu[i], C, gradMu[j]))*detJ;\n                mat3d Kuw = (mat3dd(gradMu[i]*(s*gradMw[j])) + vdotTdotv(gradMu[i], C, gradMw[j]))*detJ;\n                mat3d Kwu = (mat3dd(gradMw[i]*(s*gradMu[j])) + vdotTdotv(gradMw[i], C, gradMu[j]))*detJ;\n                mat3d Kww = (mat3dd(gradMw[i]*(s*gradMw[j])) + vdotTdotv(gradMw[i], C, gradMw[j]))*detJ;\n                \n                ke[10*i  ][10*j  ] += Kuu[0][0]; ke[10*i  ][10*j+1] += Kuu[0][1]; ke[10*i  ][10*j+2] += Kuu[0][2];\n                ke[10*i+1][10*j  ] += Kuu[1][0]; ke[10*i+1][10*j+1] += Kuu[1][1]; ke[10*i+1][10*j+2] += Kuu[1][2];\n                ke[10*i+2][10*j  ] += Kuu[2][0]; ke[10*i+2][10*j+1] += Kuu[2][1]; ke[10*i+2][10*j+2] += Kuu[2][2];\n                \n                ke[10*i  ][10*j+3] += Kuw[0][0]; ke[10*i  ][10*j+4] += Kuw[0][1]; ke[10*i  ][10*j+5] += Kuw[0][2];\n                ke[10*i+1][10*j+3] += Kuw[1][0]; ke[10*i+1][10*j+4] += Kuw[1][1]; ke[10*i+1][10*j+5] += Kuw[1][2];\n                ke[10*i+2][10*j+3] += Kuw[2][0]; ke[10*i+2][10*j+4] += Kuw[2][1]; ke[10*i+2][10*j+5] += Kuw[2][2];\n                \n                ke[10*i+3][10*j  ] += Kwu[0][0]; ke[10*i+3][10*j+1] += Kwu[0][1]; ke[10*i+3][10*j+2] += Kwu[0][2];\n                ke[10*i+4][10*j  ] += Kwu[1][0]; ke[10*i+4][10*j+1] += Kwu[1][1]; ke[10*i+4][10*j+2] += Kwu[1][2];\n                ke[10*i+5][10*j  ] += Kwu[2][0]; ke[10*i+5][10*j+1] += Kwu[2][1]; ke[10*i+5][10*j+2] += Kwu[2][2];\n                \n                ke[10*i+3][10*j+3] += Kww[0][0]; ke[10*i+3][10*j+4] += Kww[0][1]; ke[10*i+3][10*j+5] += Kww[0][2];\n                ke[10*i+4][10*j+3] += Kww[1][0]; ke[10*i+4][10*j+4] += Kww[1][1]; ke[10*i+4][10*j+5] += Kww[1][2];\n                ke[10*i+5][10*j+3] += Kww[2][0]; ke[10*i+5][10*j+4] += Kww[2][1]; ke[10*i+5][10*j+5] += Kww[2][2];\n                \n                // calculate the kup matrix\n                vec3d kup = gradMu[i]*(-Mu[j]*detJ);\n                vec3d kuq = gradMu[i]*(-Mw[j]*detJ);\n                vec3d kwp = gradMw[i]*(-Mu[j]*detJ);\n                vec3d kwq = gradMw[i]*(-Mw[j]*detJ);\n                \n                ke[10*i  ][10*j+6] += kup.z; ke[10*i  ][10*j+7] += kuq.x;\n                ke[10*i+1][10*j+6] += kup.y; ke[10*i+1][10*j+7] += kuq.y;\n                ke[10*i+2][10*j+6] += kup.z; ke[10*i+2][10*j+7] += kuq.z;\n                \n                ke[10*i+3][10*j+6] += kwp.x; ke[10*i+3][10*j+7] += kwq.x;\n                ke[10*i+4][10*j+6] += kwp.y; ke[10*i+4][10*j+7] += kwq.y;\n                ke[10*i+5][10*j+6] += kwp.z; ke[10*i+5][10*j+7] += kwq.z;\n                \n                // calculate the kuc matrix\n                vec3d kuc = (dTdc*gradMu[i] - gradMu[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mu[j]*detJ;\n                vec3d kud = (dTdc*gradMu[i] - gradMu[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mw[j]*detJ;\n                vec3d kwc = (dTdc*gradMw[i] - gradMw[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mu[j]*detJ;\n                vec3d kwd = (dTdc*gradMw[i] - gradMw[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*Mw[j]*detJ;\n                \n                ke[10*i  ][10*j+8] += kuc.z; ke[10*i  ][10*j+9] += kud.x;\n                ke[10*i+1][10*j+8] += kuc.y; ke[10*i+1][10*j+9] += kud.y;\n                ke[10*i+2][10*j+8] += kuc.z; ke[10*i+2][10*j+9] += kud.z;\n                \n                ke[10*i+3][10*j+8] += kwc.x; ke[10*i+3][10*j+9] += kwd.x;\n                ke[10*i+4][10*j+8] += kwc.y; ke[10*i+4][10*j+9] += kwd.y;\n                ke[10*i+5][10*j+8] += kwc.z; ke[10*i+5][10*j+9] += kwd.z;\n                \n                // calculate the kpu matrix\n                gp = gradp+(D*gradc)*R*T*kappa/D0;\n                wu = vdotTdotv(-gp, dKedE, gradMu[j])\n                -(((Ke*(D*gradc)) & gradMu[j])*(J*dkdJ - kappa)\n                  +Ke*(2*kappa*(gradMu[j]*(D*gradc))))*R*T/D0\n                - Ke*vdotTdotv(gradc, dDdE, gradMu[j])*(kappa*R*T/D0);\n                ww = vdotTdotv(-gp, dKedE, gradMw[j])\n                -(((Ke*(D*gradc)) & gradMw[j])*(J*dkdJ - kappa)\n                  +Ke*(2*kappa*(gradMw[j]*(D*gradc))))*R*T/D0\n                - Ke*vdotTdotv(gradc, dDdE, gradMw[j])*(kappa*R*T/D0);\n                vec3d kpu = (wu.transpose()*gradMu[i])*(detJ*dt);\n                vec3d kpw = (ww.transpose()*gradMu[i])*(detJ*dt);\n                vec3d kqu = (wu.transpose()*gradMw[i])*(detJ*dt);\n                vec3d kqw = (ww.transpose()*gradMw[i])*(detJ*dt);\n                ke[10*i+6][10*j  ] += kpu.x; ke[10*i+6][10*j+1] += kpu.y; ke[10*i+6][10*j+2] += kpu.z;\n                ke[10*i+6][10*j+3] += kpw.x; ke[10*i+6][10*j+4] += kpw.y; ke[10*i+6][10*j+5] += kpw.z;\n                ke[10*i+7][10*j  ] += kqu.x; ke[10*i+7][10*j+1] += kqu.y; ke[10*i+7][10*j+2] += kqu.z;\n                ke[10*i+7][10*j+3] += kqw.x; ke[10*i+7][10*j+4] += kqw.y; ke[10*i+7][10*j+5] += kqw.z;\n                \n                // calculate the kpp matrix\n                ke[10*i+6][10*j+6] -= gradMu[i]*(Ke*gradMu[j])*(detJ*dt); ke[10*i+6][10*j+7] -= gradMu[i]*(Ke*gradMw[j])*(detJ*dt);\n                ke[10*i+7][10*j+6] -= gradMw[i]*(Ke*gradMu[j])*(detJ*dt); ke[10*i+7][10*j+7] -= gradMw[i]*(Ke*gradMw[j])*(detJ*dt);\n                \n                // calculate the kpc matrix\n                wc = (dKedc*gp)*(-Mu[j])\n                -Ke*((((D*(dkdc-kappa*dD0dc/D0)+dDdc*kappa)*gradc)*Mu[j]\n                      +(D*gradMu[j])*kappa)*(R*T/D0));\n                wd = (dKedc*gp)*(-Mw[j])\n                -Ke*((((D*(dkdc-kappa*dD0dc/D0)+dDdc*kappa)*gradc)*Mw[j]\n                      +(D*gradMw[j])*kappa)*(R*T/D0));\n                ke[10*i+6][10*j+8] += (gradMu[i]*wc)*(detJ*dt);\n                ke[10*i+6][10*j+9] += (gradMu[i]*wd)*(detJ*dt);\n                ke[10*i+7][10*j+8] += (gradMw[i]*wc)*(detJ*dt);\n                ke[10*i+7][10*j+9] += (gradMw[i]*wd)*(detJ*dt);\n                \n                // calculate the kcu matrix\n                gc = -gradc*phiw + w*c/D0;\n                ju = ((D*gc) & gradMu[j])*(J*dkdJ)\n                + vdotTdotv(gc, dDdE, gradMu[j])*kappa\n                + (((D*gradc) & gradMu[j])*(-phis)\n                   +(D*((gradMu[j]*w)*2) - ((D*w) & gradMu[j]))*c/D0\n                   )*kappa\n                +D*wu*(kappa*c/D0);\n                jw = ((D*gc) & gradMw[j])*(J*dkdJ)\n                + vdotTdotv(gc, dDdE, gradMw[j])*kappa\n                + (((D*gradc) & gradMw[j])*(-phis)\n                   +(D*((gradMw[j]*w)*2) - ((D*w) & gradMw[j]))*c/D0\n                   )*kappa\n                +D*ww*(kappa*c/D0);\n                vec3d kcu = (ju.transpose()*gradMu[i])*(detJ*dt);\n                vec3d kcw = (jw.transpose()*gradMu[i])*(detJ*dt);\n                vec3d kdu = (ju.transpose()*gradMw[i])*(detJ*dt);\n                vec3d kdw = (jw.transpose()*gradMw[i])*(detJ*dt);\n                ke[10*i+8][10*j  ] += kcu.x; ke[10*i+8][10*j+1] += kcu.y; ke[10*i+8][10*j+2] += kcu.z;\n                ke[10*i+8][10*j+3] += kcw.x; ke[10*i+8][10*j+4] += kcw.y; ke[10*i+8][10*j+5] += kcw.z;\n                ke[10*i+9][10*j  ] += kdu.x; ke[10*i+9][10*j+1] += kdu.y; ke[10*i+9][10*j+2] += kdu.z;\n                ke[10*i+9][10*j+3] += kdw.x; ke[10*i+9][10*j+4] += kdw.y; ke[10*i+9][10*j+5] += kdw.z;\n                \n                // calculate the kcp matrix\n                ke[10*i+8][10*j+6] -= (gradMu[i]*((D*Ke)*gradMu[j]))*(kappa*c/D0)*(detJ*dt); ke[10*i+8][10*j+7] -= (gradMu[i]*((D*Ke)*gradMw[j]))*(kappa*c/D0)*(detJ*dt);\n                ke[10*i+9][10*j+6] -= (gradMw[i]*((D*Ke)*gradMu[j]))*(kappa*c/D0)*(detJ*dt); ke[10*i+9][10*j+7] -= (gradMw[i]*((D*Ke)*gradMw[j]))*(kappa*c/D0)*(detJ*dt);\n                \n                // calculate the kcc matrix\n                jc = (D*(-gradMu[j]*phiw+w*(Mu[j]/D0)))*kappa\n                +((D*dkdc+dDdc*kappa)*gc)*Mu[j]\n                +(D*(w*(-Mu[j]*dD0dc/D0)+wc))*(kappa*c/D0);\n                jd = (D*(-gradMw[j]*phiw+w*(Mw[j]/D0)))*kappa\n                +((D*dkdc+dDdc*kappa)*gc)*Mw[j]\n                +(D*(w*(-Mw[j]*dD0dc/D0)+wd))*(kappa*c/D0);\n                ke[10*i+8][10*j+8] += (gradMu[i]*jc)*(detJ*dt);\n                ke[10*i+8][10*j+9] += (gradMu[i]*jd)*(detJ*dt);\n                ke[10*i+9][10*j+8] += (gradMw[i]*jc)*(detJ*dt);\n                ke[10*i+9][10*j+9] += (gradMw[i]*jd)*(detJ*dt);\n                \n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<5*neln; ++i)\n            for (j=i+1; j<5*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::Update(const FETimeInfo& tp)\n{\n\tFESSIShellDomain::Update(tp);\n\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteShellDomain::UpdateElementStress(int iel)\n{\n    FEModel& fem = *GetFEModel();\n    double dt = fem.GetTime().timeIncrement;\n    bool sstate = (fem.GetCurrentStep()->m_nanalysis == FEBiphasicAnalysis::STEADY_STATE);\n    \n    // get the solid element\n    FEShellElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // get the number of nodes\n    int neln = el.Nodes();\n    \n    // get the biphasic-solute material\n    int id0 = m_pMat->GetSolute()->GetSoluteDOF();\n    \n    // get the nodal data\n    FEMesh& mesh = *m_pMesh;\n    vec3d r0[FEElement::MAX_NODES];\n    vec3d rt[FEElement::MAX_NODES];\n    double pn[FEElement::MAX_NODES], qn[FEElement::MAX_NODES];\n    double cn[FEElement::MAX_NODES], dn[FEElement::MAX_NODES];\n    for (int j=0; j<neln; ++j)\n    {\n        r0[j] = mesh.Node(el.m_node[j]).m_r0;\n        rt[j] = mesh.Node(el.m_node[j]).m_rt;\n        pn[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        qn[j] = mesh.Node(el.m_node[j]).get(m_dofQ);\n        cn[j] = mesh.Node(el.m_node[j]).get(m_dofC + id0);\n        dn[j] = mesh.Node(el.m_node[j]).get(m_dofD + id0);\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //\t\t since the material point coordinates are used by most materials.\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(rt, n);\n        \n        // get the deformation gradient and determinant\n        pt.m_J = defgrad(el, pt.m_F, n);\n        mat3d Fp;\n        defgradp(el, Fp, n);\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (pt.m_F - Fp)*Fi / dt;\n\n        // biphasic-solute data\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // evaluate fluid pressure at gauss-point\n        ppt.m_p = evaluate(el, pn, qn, n);\n        \n        // calculate the gradient of p at gauss-point\n        ppt.m_gradp = gradient(el, pn, qn, n);\n        \n        // evaluate effective solute concentration at gauss-point\n        spt.m_c[0] = evaluate(el, cn, dn, n);\n        \n        // calculate the gradient of c at gauss-point\n        spt.m_gradc[0] = gradient(el, cn, dn, n);\n        \n        // for biphasic-solute materials also update the porosity, fluid and solute fluxes\n        // and evaluate the actual fluid pressure and solute concentration\n        ppt.m_w = m_pMat->FluidFlux(mp);\n        ppt.m_pa = m_pMat->Pressure(mp);\n        spt.m_j[0] = m_pMat->SoluteFlux(mp);\n        spt.m_ca[0] = m_pMat->Concentration(mp);\n        if (m_pMat->GetSolute()->m_pSupp)\n        {\n            if (sstate)\n                spt.m_sbmr[0] = m_pMat->GetSolute()->m_pSupp->ReceptorLigandConcentrationSS(mp);\n            else {\n                // update m_crc using midpoint rule\n                spt.m_sbmrhat[0] = m_pMat->GetSolute()->m_pSupp->ReceptorLigandSupply(mp);\n                spt.m_sbmr[0] = spt.m_sbmrp[0] + (spt.m_sbmrhat[0]+spt.m_sbmrhatp[0])/2*dt;\n                // update phi0 using backward difference integration\n                \n                // NOTE: MolarMass was removed since not used\n                ppt.m_phi0hat = 0;\n                //\t\t\t\tppt.m_phi0hat = pmb->GetSolid()->MolarMass()/pmb->GetSolid()->Density()*pmb->GetSolute()->m_pSupp->SolidSupply(mp);\n                \n                ppt.m_phi0t = ppt.m_phi0p + ppt.m_phi0hat*dt;\n            }\n        }\n        \n        m_pMat->PartitionCoefficientFunctions(mp, spt.m_k[0], spt.m_dkdJ[0], spt.m_dkdc[0][0]);\n\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = m_pMat->GetElasticMaterial()->Stress(mp);\n        \n        // calculate the stress at this material point (must be done after evaluating m_pa)\n        pt.m_s = m_pMat->Stress(mp);\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FESSIShellDomain.h>\n#include \"FEBiphasicSolute.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain class for biphasic-solute 3D solid elements\n//! Note that this class inherits from FEElasticSolidDomain since this domain\n//! also needs to calculate elastic stiffness contributions.\n//!\nclass FEBIOMIX_API FEBiphasicSoluteShellDomain : public FESSIShellDomain, public FEBiphasicSoluteDomain\n{\npublic:\n    //! constructor\n    FEBiphasicSoluteShellDomain(FEModel* pfem);\n    \n    //! reset domain data\n    void Reset() override;\n    \n\t//! get material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\n\t//! get the total dof\n\tconst FEDofList& GetDOFList() const override;\n\n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n    \n    //! Unpack solid element data (overridden from FEDomain)\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! Activate\n    void Activate() override;\n    \n    //! initialize material points in the domain\n    void InitMaterialPoints() override;\n    \n    //! initialize elements for this domain\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    // update domain data\n    void Update(const FETimeInfo& tp) override;\n    \n    // update element stress\n    void UpdateElementStress(int iel);\n    \npublic:\n    // internal work (overridden from FEElasticDomain)\n    void InternalForces(FEGlobalVector& R) override;\n    \n    // internal work (steady-state analyses)\n    void InternalForcesSS(FEGlobalVector& R) override;\n    \npublic:\n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS, bool bsymm) override;\n    \n    //! calculates the global stiffness matrix for this domain (steady-state case)\n    void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) override;\n    \nprotected:\n    //! element internal force vector\n    void ElementInternalForce(FEShellElement& el, vector<double>& fe);\n    \n    //! element internal force vector (steady-state analyses)\n    void ElementInternalForceSS(FEShellElement& el, vector<double>& fe);\n    \n    //! calculates the element solute-poroelastic stiffness matrix\n    bool ElementBiphasicSoluteStiffness(FEShellElement& el, matrix& ke, bool bsymm);\n    \n    //! calculates the element solute-poroelastic stiffness matrix\n    bool ElementBiphasicSoluteStiffnessSS(FEShellElement& el, matrix& ke, bool bsymm);\n    \nprotected: // overridden from FEElasticDomain, but not implemented in this domain\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n    void MassMatrix(FELinearSystem& LS, double scale) override {}\n    \nprotected:\n\tFEDofList\t\tm_dof;\n    int\t\t\t\t\tm_dofSX;\n    int\t\t\t\t\tm_dofSY;\n    int\t\t\t\t\tm_dofSZ;\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSoluteSolidDomain.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEModel.h>\n#include <FEBioMech/FEBioMech.h>\n#include <FECore/FELinearSystem.h>\n#include \"FEBiphasicAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nFEBiphasicSoluteSolidDomain::FEBiphasicSoluteSolidDomain(FEModel* pfem) : FESolidDomain(pfem), FEBiphasicSoluteDomain(pfem), m_dofU(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n        m_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! get the material (overridden from FEDomain)\nFEMaterial* FEBiphasicSoluteSolidDomain::GetMaterial()\n{\n\treturn m_pMat;\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof\nconst FEDofList& FEBiphasicSoluteSolidDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FEBiphasicSolute*>(pmat);\n    assert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::Activate()\n{\n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    \n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n            }\n        }\n    }\n    \n    // Activate dof_P and dof_C, except when a solid element is connected to the\n    // back of a shell element, in which case activate dof_Q and dof_D for those nodes.\n    FEMesh& m = *GetMesh();\n    for (int i=0; i<Elements(); ++i) {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m.Node(el.m_node[j]);\n            if (el.m_bitfc.size()>0 && el.m_bitfc[j]) {\n                node.set_active(m_dofQ);\n                node.set_active(dofd  );\n            }\n            else {\n                node.set_active(m_dofP);\n                node.set_active(dofc  );\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::InitMaterialPoints()\n{\n    FEMesh& m = *GetMesh();\n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n\n    const int NE = FEElement::MAX_NODES;\n    double p0[NE], c0[NE];\n    \n    for (int i = 0; i<(int)m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the number of nodes\n        int neln = el.Nodes();\n        // get initial values of fluid pressure and solute concentrations\n        for (int i = 0; i<neln; ++i)\n        {\n            FENode& node = m.Node(el.m_node[i]);\n            p0[i] = node.get(m_dofP);\n            c0[i] = node.get(dofc);\n        }\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEElasticMaterialPoint& pm = *(mp.ExtractData<FEElasticMaterialPoint>());\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            // initialize effective fluid pressure, its gradient, and fluid flux\n            pt.m_p = el.Evaluate(p0, n);\n            pt.m_gradp = gradient(el, p0, n);\n            pt.m_w = m_pMat->FluidFlux(mp);\n            \n            // initialize effective solute concentrations\n            ps.m_c[0] = el.Evaluate(c0, n);\n            ps.m_gradc[0] = gradient(el, c0, n);\n            ps.m_ca[0] = m_pMat->Concentration(mp);\n            ps.m_j[0] = m_pMat->SoluteFlux(mp);\n            ps.m_crp[0] = pm.m_J*m_pMat->Porosity(mp)*ps.m_ca[0];\n            pt.m_pa = m_pMat->Pressure(mp);\n            \n            // determine if solute is 'solid-bound'\n            FESolute* soli = m_pMat->GetSolute();\n            if (soli->m_pDiff->Diffusivity(mp).norm() == 0) ps.m_bsb[0] = true;\n            \n            // initialize referential solid volume fraction\n            pt.m_phi0t = m_pMat->m_phi0(mp);\n            \n            // calculate stress\n            pm.m_s = m_pMat->Stress(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEBiphasicSoluteSolidDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    \n    int N = el.Nodes();\n    lm.resize(N*8);\n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        FENode& node = m_pMesh->Node(n);\n        \n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[5*i  ] = id[m_dofU[0]];\n        lm[5*i+1] = id[m_dofU[1]];\n        lm[5*i+2] = id[m_dofU[2]];\n        \n        // now the pressure dofs\n        lm[5*i+3] = id[m_dofP];\n        \n        // concentration dofs\n        lm[5*i+4] = id[dofc];\n        \n        // rigid rotational dofs\n        // TODO: Do I really need this\n        lm[5*N + 3*i  ] = id[m_dofR[0]];\n        lm[5*N + 3*i+1] = id[m_dofR[1]];\n        lm[5*N + 3*i+2] = id[m_dofR[2]];\n    }\n    \n    // substitute interface dofs for solid-shell interfaces\n\tFESolidElement& sel = static_cast<FESolidElement&>(el);\n\tfor (int i = 0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(el.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the back-face displacement dofs\n            lm[5*i  ] = id[m_dofSU[0]];\n            lm[5*i+1] = id[m_dofSU[1]];\n            lm[5*i+2] = id[m_dofSU[2]];\n            \n            // now the pressure dof (if the shell has it)\n            if (id[m_dofQ] > -1) lm[5*i+3] = id[m_dofQ];\n            \n            // concentration dofs\n            if (id[dofd] > -1) lm[5*i+4] = id[dofd];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::Reset()\n{\n    // reset base class\n    FESolidDomain::Reset();\n    \n    const int nsol = 1;\n    const int nsbm = 1;\n\n\t// loop over all material points\n\tForEachMaterialPoint([=](FEMaterialPoint& mp) {\n        FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  ps = *(mp.ExtractData<FESolutesMaterialPoint >());\n            \n        // initialize referential solid volume fraction\n        pt.m_phi0 = pt.m_phi0t = m_pMat->m_phi0(mp);\n            \n        // initialize multiphasic solutes\n        ps.m_nsol = nsol;\n        ps.m_c.assign(nsol,0);\n        ps.m_ca.assign(nsol,0);\n        ps.m_crp.assign(nsol, 0);\n        ps.m_gradc.assign(nsol,vec3d(0,0,0));\n        ps.m_bsb.assign(nsol, false);\n        ps.m_k.assign(nsol, 0);\n        ps.m_dkdJ.assign(nsol, 0);\n        ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n        ps.m_j.assign(nsol,vec3d(0,0,0));\n        ps.m_nsbm = nsbm;\n        ps.m_sbmr.assign(nsbm,0);\n        ps.m_sbmrp.assign(nsbm,0);\n        ps.m_sbmrhat.assign(nsbm,0);\n        ps.m_sbmrhatp.assign(nsbm,0);\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    \n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    double pn[NE], p, ct[NE], c;\n    FEMesh& m = *GetMesh();\n    for (size_t iel=0; iel<m_Elem.size(); ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            FENode& node = m.Node(el.m_node[i]);\n            x0[i] = node.m_r0;\n            xt[i] = node.m_rt;\n            pn[i] = m.Node(el.m_node[i]).get(m_dofP);\n            if (el.m_bitfc.size()>0 && el.m_bitfc[i]) {\n                pn[i] = (node.m_ID[m_dofQ] != -1) ? node.get(m_dofQ) : node.get(m_dofP);\n                ct[i] = (node.m_ID[dofd] != -1) ? node.get(dofd) : node.get(dofc);\n            }\n            else {\n                pn[i] = node.get(m_dofP);\n                ct[i] = node.get(dofc);\n            }\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            r0 = el.Evaluate(x0, j);\n            rt = el.Evaluate(xt, j);\n            p = el.Evaluate(pn, j);\n            c = el.Evaluate(ct, j);\n            \n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pb = *mp.ExtractData<FEBiphasicMaterialPoint>();\n            FESolutesMaterialPoint&  ps = *(mp.ExtractData<FESolutesMaterialPoint >());\n            mp.m_r0 = r0;\n            mp.m_rt = rt;\n            \n            pt.m_J = defgrad(el, pt.m_F, j);\n            \n            pb.m_Jp = pt.m_J;\n            \n            pb.m_p = p;\n            pb.m_gradp = gradient(el, pn, j);\n            pb.m_phi0p = pb.m_phi0t;\n            ps.m_c[0] = c;\n            ps.m_gradc[0] = gradient(el, ct, j);\n            \n            // reset referential actual solute concentration at previous time\n            ps.m_crp[0] = pt.m_J*m_pMat->Porosity(mp)*ps.m_ca[0];\n            // reset referential receptor-ligand complex concentration at previous time\n            ps.m_sbmrp[0] = ps.m_sbmr[0];\n            // reset referential receptor-ligand complex concentration supply at previous time\n            ps.m_sbmrhatp[0] = ps.m_sbmrhat[0];\n\n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::InternalForces(FEGlobalVector& R)\n{\n    size_t NE = m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 5*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEBiphasicSoluteSolidDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradN;\n    mat3ds s;\n    \n    const double* Gr, *Gs, *Gt, *H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // next we get the determinant\n        double Jp = bpt.m_Jp;\n        double J = pt.m_J;\n        \n        // and then finally\n        double divv = ((J-Jp)/dt)/J;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solute flux\n        vec3d& j = spt.m_j[0];\n        \n        // Evaluate porosity and solute supply and receptor-ligand kinetics\n        double phiw = m_pMat->Porosity(mp);\n        double crhat = 0;\n        if (m_pMat->GetSolute()->m_pSupp) crhat = m_pMat->GetSolute()->m_pSupp->Supply(mp);\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[5*i  ] -= fu.x*detJt;\n            fe[5*i+1] -= fu.y*detJt;\n            fe[5*i+2] -= fu.z*detJt;\n            fe[5*i+3] -= dt*(w*gradN - divv*H[i])*detJt;\n            fe[5*i+4] -= dt*(gradN*j\n                             + H[i]*(crhat/J - (phiw*spt.m_ca[0] - spt.m_crp[0]/J)/dt)\n                             )*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    size_t NE = m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 5*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEBiphasicSoluteSolidDomain::ElementInternalForceSS(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradN;\n    mat3ds s;\n    \n    const double* Gr, *Gs, *Gt, *H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // next we get the determinant\n        double J = pt.m_J;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solute flux\n        vec3d& j = spt.m_j[0];\n        \n        // Evaluate solute supply and receptor-ligand kinetics\n        double crhat = 0;\n        if (m_pMat->GetSolute()->m_pSupp) crhat = m_pMat->GetSolute()->m_pSupp->Supply(mp);\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[5*i  ] -= fu.x*detJt;\n            fe[5*i+1] -= fu.y*detJt;\n            fe[5*i+2] -= fu.z*detJt;\n            fe[5*i+3] -= dt*(w*gradN)*detJt;\n            fe[5*i+4] -= dt*(gradN*j + H[i]*(crhat/J))*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n    // repeat over all solid elements\n    const int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = neln*5;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementBiphasicSoluteStiffness(el, ke, bsymm);\n\n\t\t// get lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n\n//-----------------------------------------------------------------------------\n\nvoid FEBiphasicSoluteSolidDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n    // repeat over all solid elements\n    const int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = neln*5;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementBiphasicSoluteStiffnessSS(el, ke, bsymm);\n\n\t\t// get lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FEBiphasicSoluteSolidDomain::ElementBiphasicSoluteStiffness(FESolidElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, n;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double *Gr, *Gs, *Gt, *H;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradN(neln);\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // get the fluid flux and pressure gradient\n        vec3d gradp = ppt.m_gradp;\n        vec3d w = ppt.m_w;\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        mat3ds dKdc = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp, 0);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        \n        // get the effective concentration, its gradient and its time derivative\n        double c = spt.m_c[0];\n        vec3d gradc = spt.m_gradc[0];\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the solubility and its derivatives\n        double kappa = spt.m_k[0];\n        double dkdJ = spt.m_dkdJ[0];\n        double dkdc = spt.m_dkdc[0][0];\n        \n        // evaluate the diffusivity tensor and its derivatives\n        mat3ds D = m_pMat->GetSolute()->m_pDiff->Diffusivity(mp);\n        mat3ds dDdc = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Concentration(mp, 0);\n        tens4dmm dDdE = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Strain(mp);\n        \n        // evaluate the solute free diffusivity\n        double D0 = m_pMat->GetSolute()->m_pDiff->Free_Diffusivity(mp);\n        double dD0dc = m_pMat->GetSolute()->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,0);\n        \n        // evaluate the osmotic coefficient and its derivatives\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodc = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp, 0);\n        \n        // evaluate the stress tangent with concentration\n        //\t\tmat3ds dTdc = pm->GetSolid()->Tangent_Concentration(mp, 0);\n        mat3ds dTdc(0,0,0,0,0,0);\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds ImD = I-D/D0;\n        mat3ds Ke = (Ki + ImD*(R*T*kappa*c/phiw/D0)).inverse();\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE)\n        +dyad1(ImD,I)*(R*T*c*J/D0/phiw*(dkdJ-kappa/phiw*dpdJ))\n        +(dyad1(I,I) - dyad2(I,I)*2 - dDdE/D0)*(R*T*kappa*c/phiw/D0);\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        mat3ds Gc = -(Ki*dKdc*Ki).sym() + ImD*(R*T/phiw/D0*(dkdc*c+kappa-kappa*c/D0*dD0dc))\n        +R*T*kappa*c/phiw/D0/D0*(D*dD0dc/D0 - dDdc);\n        mat3ds dKedc = -(Ke*Gc*Ke).sym();\n        \n        // evaluate the tangents of solute supply\n        double dcrhatdJ = 0;\n        double dcrhatdc = 0;\n        if (m_pMat->GetSolute()->m_pSupp)\n        {\n            dcrhatdJ = m_pMat->GetSolute()->m_pSupp->Tangent_Supply_Strain(mp);\n            double dcrhatdcr = m_pMat->GetSolute()->m_pSupp->Tangent_Supply_Concentration(mp);\n            dcrhatdc = J*phiw*(kappa + c*dkdc)*dcrhatdcr;\n        }\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,gc,qpu,qcu,wc,jc;\n        mat3d wu,ju;\n        double qcc;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradN[i]*(s*gradN[j])) + vdotTdotv(gradN[i], C, gradN[j]))*detJ;\n                ke[5*i  ][5*j  ] += Kuu[0][0]; ke[5*i  ][5*j+1] += Kuu[0][1]; ke[5*i  ][5*j+2] += Kuu[0][2];\n                ke[5*i+1][5*j  ] += Kuu[1][0]; ke[5*i+1][5*j+1] += Kuu[1][1]; ke[5*i+1][5*j+2] += Kuu[1][2];\n                ke[5*i+2][5*j  ] += Kuu[2][0]; ke[5*i+2][5*j+1] += Kuu[2][1]; ke[5*i+2][5*j+2] += Kuu[2][2];\n                \n                // calculate the kup matrix\n                vtmp = -gradN[i]*H[j]*detJ;\n                ke[5*i  ][5*j+3] += vtmp.x;\n                ke[5*i+1][5*j+3] += vtmp.y;\n                ke[5*i+2][5*j+3] += vtmp.z;\n                \n                // calculate the kuc matrix\n                vtmp = (dTdc*gradN[i] - gradN[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*H[j]*detJ;\n                ke[5*i  ][5*j+4] += vtmp.x;\n                ke[5*i+1][5*j+4] += vtmp.y;\n                ke[5*i+2][5*j+4] += vtmp.z;\n                \n                // calculate the kpu matrix\n                gp = gradp+(D*gradc)*R*T*kappa/D0;\n                wu = vdotTdotv(-gp, dKedE, gradN[j])\n                -(((Ke*(D*gradc)) & gradN[j])*(J*dkdJ - kappa)\n                  +Ke*(2*kappa*(gradN[j]*(D*gradc))))*R*T/D0\n                - Ke*vdotTdotv(gradc, dDdE, gradN[j])*(kappa*R*T/D0);\n                qpu = -gradN[j]*(1.0/dt);\n                vtmp = (wu.transpose()*gradN[i] + qpu*H[i])*(detJ*dt);\n                ke[5*i+3][5*j  ] += vtmp.x; ke[5*i+3][5*j+1] += vtmp.y; ke[5*i+3][5*j+2] += vtmp.z;\n                \n                // calculate the kpp matrix\n                ke[5*i+3][5*j+3] -= gradN[i]*(Ke*gradN[j])*(detJ*dt);\n                \n                // calculate the kpc matrix\n                wc = (dKedc*gp)*(-H[j])\n                -Ke*((((D*(dkdc-kappa*dD0dc/D0)+dDdc*kappa)*gradc)*H[j]\n                      +(D*gradN[j])*kappa)*(R*T/D0));\n                ke[5*i+3][5*j+4] += (gradN[i]*wc)*(detJ*dt);\n                \n                // calculate the kcu matrix\n                gc = -gradc*phiw + w*c/D0;\n                ju = ((D*gc) & gradN[j])*(J*dkdJ)\n                + vdotTdotv(gc, dDdE, gradN[j])*kappa\n                + (((D*gradc) & gradN[j])*(-phis)\n                   +(D*((gradN[j]*w)*2) - ((D*w) & gradN[j]))*c/D0\n                   )*kappa\n                +D*wu*(kappa*c/D0);\n                qcu = qpu*(c*(kappa+J*phiw*dkdJ));\n                vtmp = (ju.transpose()*gradN[i] + qcu*H[i])*(detJ*dt);\n                ke[5*i+4][5*j  ] += vtmp.x; ke[5*i+4][5*j+1] += vtmp.y; ke[5*i+4][5*j+2] += vtmp.z;\n                \n                // calculate the kcp matrix\n                ke[5*i+4][5*j+3] -= (gradN[i]*((D*Ke)*gradN[j]))*(kappa*c/D0)*(detJ*dt);\n                \n                // calculate the kcc matrix\n                jc = (D*(-gradN[j]*phiw+w*(H[j]/D0)))*kappa\n                +((D*dkdc+dDdc*kappa)*gc)*H[j]\n                +(D*(w*(-H[j]*dD0dc/D0)+wc))*(kappa*c/D0);\n                qcc = -H[j]*phiw/dt*(c*dkdc + kappa);\n                ke[5*i+4][5*j+4] += (gradN[i]*jc + H[i]*qcc)*(detJ*dt);\n                \n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<5*neln; ++i)\n            for (j=i+1; j<5*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for steady-state response (zero solid velocity, zero time derivative of\n//! solute concentration)\n//!\nbool FEBiphasicSoluteSolidDomain::ElementBiphasicSoluteStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, n;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double *Gr, *Gs, *Gt, *H;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradN(neln);\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        // get the effective concentration, its gradient and its time derivative\n        double c = spt.m_c[0];\n        vec3d gradc = spt.m_gradc[0];\n        \n        // evaluate the permeability and its derivatives\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        mat3ds dKdc = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp, 0);\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the solubility and its derivatives\n        double kappa = spt.m_k[0];\n        double dkdJ = spt.m_dkdJ[0];\n        double dkdc = spt.m_dkdc[0][0];\n        \n        // evaluate the diffusivity tensor and its derivatives\n        mat3ds D = m_pMat->GetSolute()->m_pDiff->Diffusivity(mp);\n        mat3ds dDdc = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Concentration(mp, 0);\n        tens4dmm dDdE = m_pMat->GetSolute()->m_pDiff->Tangent_Diffusivity_Strain(mp);\n        \n        // evaluate the solute free diffusivity\n        double D0 = m_pMat->GetSolute()->m_pDiff->Free_Diffusivity(mp);\n        double dD0dc = m_pMat->GetSolute()->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,0);\n        \n        // evaluate the osmotic coefficient and its derivatives\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        double dodc = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp, 0);\n        \n        // evaluate the stress tangent with concentration\n        //\t\tmat3ds dTdc = pm->GetSolid()->Tangent_Concentration(mp, 0);\n        mat3ds dTdc(0,0,0,0,0,0);\n        \n        // Miscellaneous constants\n        mat3dd I(1);\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds ImD = I-D/D0;\n        mat3ds Ke = (Ki + ImD*(R*T*kappa*c/phiw/D0)).inverse();\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE)\n        +dyad1(ImD,I)*(R*T*c*J/D0/phiw*(dkdJ-kappa/phiw*dpdJ))\n        +(dyad1(I,I) - dyad2(I,I)*2 - dDdE/D0)*(R*T*kappa*c/phiw/D0);\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        mat3ds Gc = -(Ki*dKdc*Ki).sym() + ImD*(R*T/phiw/D0*(dkdc*c+kappa-kappa*c/D0*dD0dc))\n        +R*T*kappa*c/phiw/D0/D0*(D*dD0dc/D0 - dDdc);\n        mat3ds dKedc = -(Ke*Gc*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,gc,wc,jc;\n        mat3d wu,ju;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradN[i]*(s*gradN[j])) + vdotTdotv(gradN[i], C, gradN[j]))*detJ;\n                \n                ke[5*i  ][5*j  ] += Kuu[0][0]; ke[5*i  ][5*j+1] += Kuu[0][1]; ke[5*i  ][5*j+2] += Kuu[0][2];\n                ke[5*i+1][5*j  ] += Kuu[1][0]; ke[5*i+1][5*j+1] += Kuu[1][1]; ke[5*i+1][5*j+2] += Kuu[1][2];\n                ke[5*i+2][5*j  ] += Kuu[2][0]; ke[5*i+2][5*j+1] += Kuu[2][1]; ke[5*i+2][5*j+2] += Kuu[2][2];\n                \n                // calculate the kpu matrix\n                gp = gradp+(D*gradc)*R*T*kappa/D0;\n                wu = vdotTdotv(-gp, dKedE, gradN[j])\n                -(((Ke*(D*gradc)) & gradN[j])*(J*dkdJ - kappa)\n                  +Ke*(2*kappa*(gradN[j]*(D*gradc))))*R*T/D0\n                - Ke*vdotTdotv(gradc, dDdE, gradN[j])*(kappa*R*T/D0);\n                vtmp = (wu.transpose()*gradN[i])*(detJ*dt);\n                ke[5*i+3][5*j  ] += vtmp.x;\n                ke[5*i+3][5*j+1] += vtmp.y;\n                ke[5*i+3][5*j+2] += vtmp.z;\n                \n                // calculate the kcu matrix\n                gc = -gradc*phiw + w*c/D0;\n                ju = ((D*gc) & gradN[j])*(J*dkdJ)\n                + vdotTdotv(gc, dDdE, gradN[j])*kappa\n                + (((D*gradc) & gradN[j])*(-phis)\n                   +(D*((gradN[j]*w)*2) - ((D*w) & gradN[j]))*c/D0\n                   )*kappa\n                +D*wu*(kappa*c/D0);\n                vtmp = (ju.transpose()*gradN[i])*(detJ*dt);\n                ke[5*i+4][5*j  ] += vtmp.x;\n                ke[5*i+4][5*j+1] += vtmp.y;\n                ke[5*i+4][5*j+2] += vtmp.z;\n                \n                // calculate the kup matrix\n                vtmp = -gradN[i]*H[j]*detJ;\n                ke[5*i  ][5*j+3] += vtmp.x;\n                ke[5*i+1][5*j+3] += vtmp.y;\n                ke[5*i+2][5*j+3] += vtmp.z;\n                \n                // calculate the kpp matrix\n                ke[5*i+3][5*j+3] -= gradN[i]*(Ke*gradN[j])*(detJ*dt);\n                \n                // calculate the kcp matrix\n                ke[5*i+4][5*j+3] -= (gradN[i]*((D*Ke)*gradN[j]))*(kappa*c/D0)*(detJ*dt);\n                \n                // calculate the kuc matrix\n                vtmp = (dTdc*gradN[i] - gradN[i]*(R*T*(dodc*kappa*c+osmc*dkdc*c+osmc*kappa)))*H[j]*detJ;\n                ke[5*i  ][5*j+4] += vtmp.x;\n                ke[5*i+1][5*j+4] += vtmp.y;\n                ke[5*i+2][5*j+4] += vtmp.z;\n                \n                // calculate the kpc matrix\n                wc = (dKedc*gp)*(-H[j])\n                -Ke*((((D*(dkdc-kappa*dD0dc/D0)+dDdc*(kappa/D0))*gradc)*H[j]\n                      +(D*gradN[j])*kappa)*(R*T/D0));\n                ke[5*i+3][5*j+4] += (gradN[i]*wc)*(detJ*dt);\n                \n                // calculate the kcc matrix\n                jc = (D*(-gradN[j]*phiw+w*(H[j]/D0)))*kappa\n                +((D*dkdc+dDdc*kappa)*gc)*H[j]\n                +(D*(w*(-H[j]*dD0dc/D0)+wc))*(kappa*c/D0);\n                ke[5*i+4][5*j+4] += (gradN[i]*jc)*(detJ*dt);\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<5*neln; ++i)\n            for (j=i+1; j<5*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::Update(const FETimeInfo& tp)\n{\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolidDomain::UpdateElementStress(int iel)\n{\n    FEModel& fem = *GetFEModel();\n    double dt = fem.GetTime().timeIncrement;\n    bool sstate = (fem.GetCurrentStep()->m_nanalysis == FEBiphasicAnalysis::STEADY_STATE);\n    \n    int dofc = m_dofC + m_pMat->GetSolute()->GetSoluteDOF();\n    int dofd = m_dofD + m_pMat->GetSolute()->GetSoluteDOF();\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    int nint = el.GaussPoints();\n    \n    // get the number of nodes\n    int neln = el.Nodes();\n    \n    // get the nodal data\n    FEMesh& mesh = *m_pMesh;\n    vec3d r0[FEElement::MAX_NODES];\n    vec3d rt[FEElement::MAX_NODES];\n    double pn[FEElement::MAX_NODES], ct[FEElement::MAX_NODES];\n    for (int j=0; j<neln; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j]);\n        r0[j] = node.m_r0;\n        rt[j] = node.m_rt;\n        if (el.m_bitfc.size()>0 && el.m_bitfc[j]) {\n            pn[j] = (node.m_ID[m_dofQ] != -1) ? node.get(m_dofQ) : node.get(m_dofP);\n            ct[j] = (node.m_ID[dofd] != -1) ? node.get(dofd) : node.get(dofc);\n        }\n        else {\n            pn[j] = node.get(m_dofP);\n            ct[j] = node.get(dofc);\n        }\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //\t\t since the material point coordinates are used by most materials.\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(rt, n);\n        \n        // get the deformation gradient and determinant\n        pt.m_J = defgrad(el, pt.m_F, n);\n        mat3d Fp;\n        defgradp(el, Fp, n);\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (pt.m_F - Fp)*Fi / dt;\n\n        // solute-poroelastic data\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // evaluate fluid pressure at gauss-point\n        ppt.m_p = el.Evaluate(pn, n);\n        \n        // calculate the gradient of p at gauss-point\n        ppt.m_gradp = gradient(el, pn, n);\n        \n        // evaluate effective solute concentration at gauss-point\n        spt.m_c[0] = el.Evaluate(ct, n);\n        \n        // calculate the gradient of c at gauss-point\n        spt.m_gradc[0] = gradient(el, ct, n);\n        \n        // for biphasic-solute materials also update the porosity, fluid and solute fluxes\n        // and evaluate the actual fluid pressure and solute concentration\n        ppt.m_w = m_pMat->FluidFlux(mp);\n        ppt.m_pa = m_pMat->Pressure(mp);\n        spt.m_j[0] = m_pMat->SoluteFlux(mp);\n        spt.m_ca[0] = m_pMat->Concentration(mp);\n        if (m_pMat->GetSolute()->m_pSupp)\n        {\n            if (sstate)\n                spt.m_sbmr[0] = m_pMat->GetSolute()->m_pSupp->ReceptorLigandConcentrationSS(mp);\n            else {\n                // update m_crc using midpoint rule\n                spt.m_sbmrhat[0] = m_pMat->GetSolute()->m_pSupp->ReceptorLigandSupply(mp);\n                spt.m_sbmr[0] = spt.m_sbmrp[0] + (spt.m_sbmrhat[0]+spt.m_sbmrhatp[0])/2*dt;\n                // update phi0 using backward difference integration\n                \n                // NOTE: MolarMass was removed since not used\n                ppt.m_phi0hat = 0;\n                //\t\t\t\tppt.m_phi0hat = pmb->GetSolid()->MolarMass()/pmb->GetSolid()->Density()*pmb->GetSolute()->m_pSupp->SolidSupply(mp);\n                \n                ppt.m_phi0t = ppt.m_phi0p + ppt.m_phi0hat*dt;\n            }\n        }\n        \n        m_pMat->PartitionCoefficientFunctions(mp, spt.m_k[0], spt.m_dkdJ[0], spt.m_dkdc[0][0]);\n        \n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = m_pMat->GetElasticMaterial()->Stress(mp);\n        \n        // calculate the stress at this material point (must be done after evaluating m_pa)\n        pt.m_s = m_pMat->Stress(mp);\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FESolidDomain.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! Domain class for biphasic-solute 3D solid elements\n//! Note that this class inherits from FEElasticSolidDomain since this domain\n//! also needs to calculate elastic stiffness contributions.\n//!\nclass FEBIOMIX_API FEBiphasicSoluteSolidDomain : public FESolidDomain, public FEBiphasicSoluteDomain\n{\npublic:\n    //! constructor\n    FEBiphasicSoluteSolidDomain(FEModel* pfem);\n    \n    //! reset domain data\n    void Reset() override;\n    \n\t//! get material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\n\t//! get the total dof\n\tconst FEDofList& GetDOFList() const override;\n\n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n    \n    //! Unpack solid element data (overridden from FEDomain)\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! Activate\n    void Activate() override;\n    \n    //! initialize material points in the domain\n    void InitMaterialPoints() override;\n    \n    //! initialize elements for this domain\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    // update domain data\n    void Update(const FETimeInfo& tp) override;\n    \n    // update element stress\n    void UpdateElementStress(int iel);\n    \npublic:\n    // internal work (overridden from FEElasticDomain)\n    void InternalForces(FEGlobalVector& R) override;\n    \n    // internal work (steady-state analyses)\n    void InternalForcesSS(FEGlobalVector& R) override;\n    \npublic:\n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS, bool bsymm) override;\n    \n    //! calculates the global stiffness matrix for this domain (steady-state case)\n    void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) override;\n    \nprotected:\n    //! element internal force vector\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! element internal force vector (steady-state analyses)\n    void ElementInternalForceSS(FESolidElement& el, vector<double>& fe);\n    \n    //! calculates the element solute-poroelastic stiffness matrix\n    bool ElementBiphasicSoluteStiffness(FESolidElement& el, matrix& ke, bool bsymm);\n    \n    //! calculates the element solute-poroelastic stiffness matrix\n    bool ElementBiphasicSoluteStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm);\n    \nprotected: // overridden from FEElasticDomain, but not implemented in this domain\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n    void MassMatrix(FELinearSystem& LS, double scale) override {}\n\nprotected:\n\tFEDofList\tm_dofU;\n\tFEDofList\tm_dofSU;\n\tFEDofList\tm_dofR;\n\tFEDofList\tm_dof;\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSoluteSolver.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n#include \"FEBiphasicDomain.h\"\n#include <FEBioMech/FEElasticDomain.h>\n#include <FEBioMech/FEResidualVector.h>\n#include <FEBioMech/FESolidLinearSystem.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include \"FECore/sys.h\"\n#include \"FEBiphasicSoluteAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nFEBiphasicSoluteSolver::FEBiphasicSoluteSolver(FEModel* pfem) : FEBiphasicSolver(pfem), m_dofC(pfem), m_dofD(pfem)\n{\n\tm_Ctol = 0.01;\n    \n\tm_msymm = REAL_UNSYMMETRIC; // assume non-symmetric stiffness matrix by default\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures.\n//\nbool FEBiphasicSoluteSolver::Init()\n{\n\t// initialize base class\n\tif (FEBiphasicSolver::Init() == false) return false;\n\n\tint i;\n    \n    // get number of DOFS\n\tFEModel& fem = *GetFEModel();\n\tDOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    int MAX_DDOFS = fedofs.GetVariableSize(\"shell concentration\");\n    \n\t// allocate concentration-vectors\n\tm_ci.assign(MAX_CDOFS,vector<double>(0,0));\n\tm_Ci.assign(MAX_CDOFS,vector<double>(0,0));\n\tfor (i=0; i<MAX_CDOFS; ++i) {\n\t\tm_ci[i].assign(m_nceq[i], 0);\n\t\tm_Ci[i].assign(m_nceq[i], 0);\n\t}\n\t\n\t// we need to fill the total displacement vector m_Ut\n\tvector<int> dofs;\n\tfor (int j=0; j<MAX_CDOFS; ++j) \n\t{\n\t\tif (m_nceq[j])\n\t\t\tdofs.push_back(m_dofC[j]);\n\t}\n    for (int j=0; j<MAX_DDOFS; ++j)\n    {\n        if (m_nceq[j])\n            dofs.push_back(m_dofD[j]);\n    }\n\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, dofs);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEBiphasicSoluteSolver::InitEquations()\n{\n\t// get number of DOFS\n\tFEModel& fem = *GetFEModel();\n\tDOFS& fedofs = fem.GetDOFS();\n\tint MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n\n\tm_dofC.Clear();\n\tfor (int i=0; i<MAX_CDOFS; ++i)\n\t\tm_dofC.AddDof(fem.GetDOFIndex(\"concentration\", i));\n\t\n\tm_dofD.Clear();\n\tfor (int i = 0; i<MAX_CDOFS; ++i)\n\t\tm_dofD.AddDof(fem.GetDOFIndex(\"shell concentration\", i));\n\n\tAddSolutionVariable(&m_dofC, -1, \"concentration\", m_Ctol);\n\tAddSolutionVariable(&m_dofD, -1, \"shell concentration\", m_Ctol);\n\n\t// base class does most of the work\n\tFEBiphasicSolver::InitEquations();\n\t\n\t// determined the nr of concentration equations\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int j=0; j<(int)m_nceq.size(); ++j) m_nceq[j] = 0;\n\n    m_nceq.assign(MAX_CDOFS, 0);\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& n = mesh.Node(i);\n        for (int j=0; j<MAX_CDOFS; ++j) {\n\t\t\tif (n.m_ID[m_dofC[j]] != -1) m_nceq[j]++;\n            if (n.m_ID[m_dofD[j]] != -1) m_nceq[j]++;\n        }\n\t}\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first QN iteration. \n//!\nvoid FEBiphasicSoluteSolver::PrepStep()\n{\n\tfor (int j=0; j<(int)m_nceq.size(); ++j) if (m_nceq[j]) zero(m_Ci[j]);\n\n\t// for concentration nodal loads we need to multiply the time step size\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.ModelLoads(); ++i)\n\t{\n\t\tFENodalDOFLoad* pl = dynamic_cast<FENodalDOFLoad*>(fem.ModelLoad(i));\n\t\tif (pl && pl->IsActive())\n\t\t{\n\t\t\tbool adjust = false;\n\t\t\tint dof = pl->GetDOF();\n\t\t\tif      ((m_dofC[0] > -1) && (dof == m_dofC[0])) adjust = true;\n\t\t\telse if ((m_dofD[0] > -1) && (dof == m_dofD[0])) adjust = true;\n\n\t\t\tif (adjust)\n\t\t\t{\n\t\t\t\tpl->SetDtScale(true);\n\t\t\t}\n\t\t}\n\t}\n\n\n\tFEBiphasicSolver::PrepStep();\n}\n\n//-----------------------------------------------------------------------------\n//! Implements the BFGS algorithm to solve the nonlinear FE equations.\nbool FEBiphasicSoluteSolver::Quasin()\n{\n\t// convergence norms\n\tdouble\tnormR1;\t\t// residual norm\n\tdouble\tnormE1;\t\t// energy norm\n\tdouble\tnormD;\t\t// displacement norm\n\tdouble\tnormd;\t\t// displacement increment norm\n\tdouble\tnormRi;\t\t// initial residual norm\n\tdouble\tnormEi;\t\t// initial energy norm\n\tdouble\tnormEm;\t\t// max energy norm\n\tdouble\tnormDi;\t\t// initial displacement norm\n\n\t// poro convergence norms data\n\tdouble\tnormPi;\t\t// initial pressure norm\n\tdouble\tnormP;\t\t// current pressure norm\n\tdouble\tnormp;\t\t// incremement pressure norm\n\n    // get number of DOFS\n\tFEModel& fem = *GetFEModel();\n\tDOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\t// solute convergence data\n\tvector<double>\tnormCi(MAX_CDOFS);\t// initial concentration norm\n\tvector<double>\tnormC(MAX_CDOFS);\t// current concentration norm\n\tvector<double>\tnormc(MAX_CDOFS);\t// incremement concentration norm\n\n\t// prepare for the first iteration\n\tconst FETimeInfo& tp = fem.GetTime();\n\tPrepStep();\n\n\t// init QN method\n\tif (QNInit() == false) return false;\n\n\t// loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter+1);\n\n\t\t// assume we'll converge. \n\t\tbconv = true;\n\n\t\t// solve the equations (returns line search; solution stored in m_ui)\n\t\tdouble s = QNSolve();\n\n\t\t// extract the pressure increments\n\t\tGetDisplacementData(m_di, m_ui);\n\n\t\t// set initial convergence norms\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tnormRi = fabs(m_R0*m_R0);\n\t\t\tnormEi = fabs(m_ui*m_R0);\n\t\t\tnormDi = fabs(m_di*m_di);\n\t\t\tnormEm = normEi;\n\n\t\t\tm_residuNorm.norm0 = normRi;\n\t\t\tm_energyNorm.norm0 = normEi;\n\t\t\tm_solutionNorm[0].norm0 = normDi;\n\t\t}\n\n\t\t// update all degrees of freedom\n\t\tfor (int i=0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n\n\t\t// update displacements\n\t\tfor (int i = 0; i<m_ndeq; ++i) m_Di[i] += s*m_di[i];\n\n\t\t// calculate norms\n\t\tnormR1 = m_R1*m_R1;\n\t\tnormd  = (m_di*m_di)*(s*s);\n\t\tnormD  = m_Di*m_Di;\n\t\tnormE1 = s*fabs(m_ui*m_R1);\n\n\t\tm_residuNorm.norm = normR1;\n\t\tm_energyNorm.norm = normE1;\n\t\tm_solutionNorm[0].norm = normd;\n\n\t\t// check residual norm\n\t\tif ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\t\n\n\t\t// check displacement norm\n\t\tif ((m_Dtol > 0) && (normd  > (m_Dtol*m_Dtol)*normD )) bconv = false;\n\n\t\t// check energy norm\n\t\tif ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n\n\t\t// check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n\n\t\t// check energy divergence\n\t\tif (normE1 > normEm) bconv = false;\n\n\t\t// check poroelastic convergence\n\t\t// extract the pressure increments\n\t\tGetPressureData(m_pi, m_ui);\n\n\t\t// set initial norm\n\t\tif (m_niter == 0) normPi = fabs(m_pi*m_pi);\n\n\t\t// update total pressure\n\t\tfor (int i = 0; i<m_npeq; ++i) m_Pi[i] += s*m_pi[i];\n\n\t\t// calculate norms\n\t\tnormP = m_Pi*m_Pi;\n\t\tnormp = (m_pi*m_pi)*(s*s);\n\n\t\t// check convergence\n\t\tif ((m_Ptol > 0) && (normp > (m_Ptol*m_Ptol)*normP)) bconv = false;\n\n\t\t// check solute convergence\n\t\t{\n\t\t\t// extract the concentration increments\n\t\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j) {\n\t\t\t\tif (m_nceq[j]) {\n\t\t\t\t\tGetConcentrationData(m_ci[j], m_ui,j);\n\t\t\t\t\t\n\t\t\t\t\t// set initial norm\n\t\t\t\t\tif (m_niter == 0)\n\t\t\t\t\t\tnormCi[j] = fabs(m_ci[j]*m_ci[j]);\n\t\t\t\t\t\n\t\t\t\t\t// update total concentration\n\t\t\t\t\tfor (int i = 0; i<m_nceq[j]; ++i) m_Ci[j][i] += s*m_ci[j][i];\n\t\t\t\t\t\n\t\t\t\t\t// calculate norms\n\t\t\t\t\tnormC[j] = m_Ci[j]*m_Ci[j];\n\t\t\t\t\tnormc[j] = (m_ci[j]*m_ci[j])*(s*s);\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// check convergence\n\t\t\tif (m_Ctol > 0) {\n\t\t\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j)\n\t\t\t\t\tif (m_nceq[j]) bconv = bconv && (normc[j] <= (m_Ctol*m_Ctol)*normC[j]);\n\t\t\t}\n\t\t}\n\n\t\t// print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n\t\tfeLog(\"\\tconvergence norms :        INITIAL         CURRENT         REQUIRED\\n\");\n\t\tfeLog(\"\\t residual               %15le %15le %15le\\n\", normRi, normR1, m_Rtol*normRi);\n\t\tfeLog(\"\\t energy                 %15le %15le %15le\\n\", normEi, normE1, m_Etol*normEi);\n\t\tfeLog(\"\\t displacement           %15le %15le %15le\\n\", normDi, normd ,(m_Dtol*m_Dtol)*normD );\n\t\tfeLog(\"\\t fluid pressure         %15le %15le %15le\\n\", normPi, normp ,(m_Ptol*m_Ptol)*normP );\n\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j) {\n\t\t\tif (m_nceq[j])\n\t\t\t\tfeLog(\"\\t solute %d concentration %15le %15le %15le\\n\", j+1, normCi[j], normc[j] ,(m_Ctol*m_Ctol)*normC[j] );\n\t\t}\n\n\t\tif ((bconv == false) && (normR1 < m_Rmin))\n\t\t{\n\t\t\t// check for almost zero-residual on the first iteration\n\t\t\t// this might be an indication that there is no force on the system\n\t\t\tfeLogWarning(\"No force acting on the system.\");\n\t\t\tbconv = true;\n\t\t}\n\n\t\t// check if we have converged. \n\t\t// If not, calculate the BFGS update vectors\n\t\tif (bconv == false)\n\t\t{\n\t\t\tif (s < m_lineSearch->m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\t\t\telse if (normE1 > normEm)\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n\t\t\t\tnormEm = normE1;\n\t\t\t\tnormEi = normE1;\n\t\t\t\tnormRi = normR1;\n\t\t\t\tnormDi = normd;\n\t\t\t\tnormPi = normp;\n\t\t\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j)\n\t\t\t\t\tif (m_nceq[j]) normCi[j] = normc[j];\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// Do augmentations\n\t\t\tbconv = DoAugmentations();\n\t\t}\n\t\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile (bconv == false);\n\n\t// if converged we update the total displacements\n\tif (bconv)\n\t{\n\t\tm_Ut += m_Ui;\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEBiphasicSoluteSolver::Residual(vector<double>& R)\n{\n\tint i;\n\n\t// get the time information\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// initialize residual with concentrated nodal loads\n\tzero(R);\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup global RHS vector\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tm_rigidSolver.Residual();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// internal stress work\n\tfor (i=0; i<mesh.Domains(); ++i)\n\t{\n        FEDomain& dom = mesh.Domain(i);\n        FEElasticDomain* ped = dynamic_cast<FEElasticDomain*>(&dom);\n        FEBiphasicDomain*  pbd = dynamic_cast<FEBiphasicDomain* >(&dom);\n        FEBiphasicSoluteDomain* psd = dynamic_cast<FEBiphasicSoluteDomain*>(&dom);\n        if (psd) {\n            if (fem.GetCurrentStep()->m_nanalysis == FEBiphasicSoluteAnalysis::STEADY_STATE)\n                psd->InternalForcesSS(RHS);\n            else\n                psd->InternalForces(RHS);\n        }\n        else if (pbd) {\n            if (fem.GetCurrentStep()->m_nanalysis == FEBiphasicSoluteAnalysis::STEADY_STATE)\n                pbd->InternalForcesSS(RHS);\n            else\n                pbd->InternalForces(RHS);\n        }\n        else if (ped)\n            ped->InternalForces(RHS);\n    }\n    \n\t// calculate contact forces\n\tif (fem.SurfacePairConstraints() > 0)\n\t{\n\t\tContactForces(RHS);\n\t}\n\n\t// calculate linear constraint forces\n\t// note that these are the linear constraints\n\t// enforced using the augmented lagrangian\n\tNonLinearConstraintForces(RHS, tp);\n\n\t// add model loads\n\tint NML = fem.ModelLoads();\n\tfor (i=0; i<NML; ++i)\n\t{\n\t\tFEModelLoad& mli = *fem.ModelLoad(i);\n\t\tif (mli.IsActive())\n\t\t{\n\t\t\tmli.LoadVector(RHS);\n\t\t}\n\t}\n\n\t// set the nodal reaction forces\n\t// TODO: Is this a good place to do this?\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEBiphasicSoluteSolver::StiffnessMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// setup the linear system\n\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), m_alpha, m_nreq);\n\n\t// calculate the stiffness matrix for each domain\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tbool bsymm = (m_msymm == REAL_SYMMETRIC);\n\tif (pstep->m_nanalysis == FEBiphasicSoluteAnalysis::STEADY_STATE)\n\t{\n\t\tfor (int i=0; i<mesh.Domains(); ++i) \n\t\t{\n            // Biphasic-solute analyses may also include biphasic and elastic domains\n\t\t\tFEBiphasicSoluteDomain* psdom = dynamic_cast<FEBiphasicSoluteDomain*>(&mesh.Domain(i));\n\t\t\tFEBiphasicDomain*  pbdom = dynamic_cast<FEBiphasicDomain*>(&mesh.Domain(i));\n\t\t\tFEElasticDomain*   pedom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\t\tif (psdom) psdom->StiffnessMatrixSS(LS, bsymm);\n\t\t\telse if (pbdom) pbdom->StiffnessMatrixSS(LS, bsymm);\n            else if (pedom) pedom->StiffnessMatrix(LS);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t\t{\n            // Biphasic-solute analyses may also include biphasic and elastic domains\n\t\t\tFEBiphasicSoluteDomain* psdom = dynamic_cast<FEBiphasicSoluteDomain*>(&mesh.Domain(i));\n\t\t\tFEBiphasicDomain* pbdom = dynamic_cast<FEBiphasicDomain*>(&mesh.Domain(i));\n\t\t\tFEElasticDomain* pedom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\t\tif (psdom) psdom->StiffnessMatrix(LS, bsymm);\n\t\t\telse if (pbdom) pbdom->StiffnessMatrix(LS, bsymm);\n            else if (pedom) pedom->StiffnessMatrix(LS);\n\t\t}\n\t}\n\n\t// calculate contact stiffness\n\tif (fem.SurfacePairConstraints() > 0) \n\t{\n\t\tContactStiffness(LS);\n\t}\n\n\t// calculate stiffness matrices for surface loads\n\tint nml = fem.ModelLoads();\n\tfor (int i = 0; i<nml; ++i)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(i);\n\t\tif (pml->IsActive()) pml->StiffnessMatrix(LS);\n\t}\n\n\t// calculate nonlinear constraint stiffness\n\t// note that this is the contribution of the \n\t// constrainst enforced with augmented lagrangian\n\tNonLinearConstraintStiffness(LS, tp);\n\n\t// add contributions from rigid bodies\n\tm_rigidSolver.StiffnessMatrix(*m_pK, tp);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSoluteSolver::GetConcentrationData(vector<double> &ci, vector<double> &ui, const int sol)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.GetMesh().Nodes(), nid, m = 0;\n\tzero(ci);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = fem.GetMesh().Node(i);\n\t\tnid = n.m_ID[m_dofC[sol]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tci[m++] = ui[nid];\n\t\t\tassert(m <= (int) ci.size());\n\t\t}\n        nid = n.m_ID[m_dofD[sol]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ci[m++] = ui[nid];\n            assert(m <= (int) ci.size());\n        }\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\n//! Update the model's kinematic data. This is overriden from FEBiphasicSolver so\n//! that solute data is updated\nvoid FEBiphasicSoluteSolver::UpdateKinematics(vector<double>& ui)\n{\n\t// first update all solid-mechanics kinematics\n\tFEBiphasicSolver::UpdateKinematics(ui);\n\n\t// update solute-poroelastic data\n\tUpdateSolute(ui);\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the solute data\nvoid FEBiphasicSoluteSolver::UpdateSolute(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tdouble dt = fem.GetTime().timeIncrement;\n\t\n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    int MAX_DDOFS = fedofs.GetVariableSize(\"shell concentration\");\n    \n\t// update solute data\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\t\n\t\t// update nodal concentration\n\t\tfor (int j=0; j<MAX_CDOFS; ++j) {\n\t\t\tint n = node.m_ID[m_dofC[j]];\n\t\t\t// Force the concentrations to remain positive\n\t\t\tif (n >= 0) {\n\t\t\t\tdouble ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n\t\t\t\tif (ct < 0) ct = 0.0;\n\t\t\t\tnode.set(m_dofC[j], ct);\n\t\t\t}\n\t\t}\n        for (int j=0; j<MAX_DDOFS; ++j) {\n            int n = node.m_ID[m_dofD[j]];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if (ct < 0) ct = 0.0;\n                node.set(m_dofD[j], ct);\n            }\n        }\n\t}\n\t\n\t// update solute data\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\t\n\t\t// update velocities\n\t\tvec3d vt = (node.m_rt - node.m_rp) / dt;\n\t\tnode.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEBiphasicSoluteSolver::Serialize(DumpStream& ar)\n{\n\tFEBiphasicSolver::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Ctol & m_nceq & m_dofC & m_dofD;\n\tar & m_ci;\n\tar & m_Ci;\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSoluteSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolver.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n// This class adds additional functionality to the FEBiphasicSolver to solve\n// solute problems. \nclass FEBIOMIX_API FEBiphasicSoluteSolver : public FEBiphasicSolver\n{\npublic:\n\t//! con/descructor\n\tFEBiphasicSoluteSolver(FEModel* pfem);\n\tvirtual ~FEBiphasicSoluteSolver(){}\n\n\t//! Initialize data structures\n\tbool Init() override;\n\n\t//! Initialize equations\n\tbool InitEquations() override;\n\n\t//! prepares the data for the first QN iteration\n\tvoid PrepStep() override;\n\n\t//! Performs a Newton-Raphson iteration\n\tbool Quasin() override;\n\n\t//! serialize data to/from dump file\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t//! Calculates residual (overridden from FEBiphasicSolver)\n\tbool Residual(vector<double>& R) override;\n\n\t//! calculates the global stiffness matrix (overridden from FESolidSolver2)\n\tbool StiffnessMatrix() override;\n\n\t//! update kinematics\n\tvoid UpdateKinematics(vector<double>& ui) override;\n\n\t//! Update solute data\n\tvoid UpdateSolute(vector<double>& ui);\n\nprotected:\n\tvoid GetConcentrationData(vector<double>& ci, vector<double>& ui, const int sol);\n\npublic:\n\t// solute data\n\tvector< vector<double> >\tm_ci;\t//!< concentration increment vector\n\tvector< vector<double> >\tm_Ci;\t//!< Total concentration vector for iteration\n\n\tFEDofList\tm_dofC;\t//!< concentration dof\n    FEDofList\tm_dofD;\t//!< shell concentration dof\n\n};\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicSolver.h\"\n#include \"FEBiphasicDomain.h\"\n#include <FEBioMech/FESlidingElasticInterface.h>\n#include \"FESlidingInterface2.h\"\n#include \"FESlidingInterface3.h\"\n#include \"FESlidingInterfaceBiphasic.h\"\n#include \"FESlidingInterfaceBiphasicMixed.h\"\n#include <FEBioMech/FEElasticDomain.h>\n#include <FEBioMech/FEPressureLoad.h>\n#include <FEBioMech/FEResidualVector.h>\n#include <FEBioMech/FESolidLinearSystem.h>\n#include <FEBioMech/FESSIShellDomain.h>\n#include <FEBioMech/FERigidConnector.h>\n#include <FECore/log.h>\n#include <FECore/sys.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FELinearConstraintManager.h>\n#include \"FEBiphasicAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEBiphasicSolver, FENewtonSolver)\n\tBEGIN_PARAM_GROUP(\"Nonlinear solver\");\t// make sure this matches FENewtonSolver. \n\t\tADD_PARAMETER(m_Dtol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"dtol\"        );\n\t\tADD_PARAMETER(m_Etol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n\t\tADD_PARAMETER(m_Rtol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n\t\tADD_PARAMETER(m_Ptol, \"ptol\"        );\n        ADD_PARAMETER(m_Ctol, \"ctol\"        );\n\t\tADD_PARAMETER(m_biphasicFormulation, \"mixed_formulation\");\n\tEND_PARAM_GROUP();\n\n\t// obsolete parameters that used to be inherited from FESolidSolver2\n\tADD_PARAMETER(m_rhoi      , \"rhoi\"            )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_alpha     , \"alpha\"           )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_beta      , \"beta\"            )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_gamma     , \"gamma\"           )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_logSolve  , \"logSolve\"        )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_arcLength , \"arc_length\"      )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_al_scale  , \"arc_length_scale\")->SetFlags(FE_PARAM_OBSOLETE);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEBiphasicSolver::FEBiphasicSolver(FEModel* pfem) : FENewtonSolver(pfem), \n\tm_dofU(pfem), m_dofV(pfem), m_dofRQ(pfem),\n\tm_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem),\n\tm_dofP(pfem), m_dofSP(pfem),\n\tm_rigidSolver(pfem)\n{\n\t// default values\n\tm_Rtol = 0;\t// deactivate residual convergence \n\tm_Dtol = 0.001;\n\tm_Etol = 0.01;\n\tm_Ptol = 0.01;\n    m_Ctol = 0; // only needed for biphasic-solute analyses\n\tm_Rmin = 1.0e-20;\n\tm_Rmax = 0;\t// not used if zero\n\n\tm_ndeq = 0;\n\tm_npeq = 0;\n\tm_nreq = 0;\n\tm_niter = 0;\n\n\tm_msymm = REAL_UNSYMMETRIC; // assume non-symmetric stiffness matrix by default\n\n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n    \n\t// set default formulation (full shape functions)\n\tm_biphasicFormulation = 0;\n\n\tm_solutionNorm.push_back(ConvergenceInfo());\n\n\t// get pressure dof\n\tif (pfem)\n\t{\n\t\tm_dofP.AddDof(pfem->GetDOFIndex(\"p\"));\n\t\tm_dofSP.AddDof(pfem->GetDOFIndex(\"q\"));\n\t\tm_dofU.AddVariable(\"displacement\");\n\t\tm_dofRQ.AddVariable(\"rigid rotation\");\n\t\tm_dofV.AddVariable(\"velocity\");\n\t\tm_dofSU.AddVariable(\"shell displacement\");\n\t\tm_dofSV.AddVariable(\"shell velocity\");\n\t\tm_dofSA.AddVariable(\"shell acceleration\");\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEBiphasicSolver::~FEBiphasicSolver() {}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures.\n//\nbool FEBiphasicSolver::Init()\n{\n\t// initialize base class\n\tif (FENewtonSolver::Init() == false) return false;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// allocate vectors\n\t//\tm_Fn.assign(m_neq, 0);\n\tm_Fr.assign(m_neq, 0);\n\tm_Ui.assign(m_neq, 0);\n\tm_Ut.assign(m_neq, 0);\n\n\t// we need to fill the total displacement vector m_Ut\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, m_dofU[0]);\n\tgather(m_Ut, mesh, m_dofU[1]);\n\tgather(m_Ut, mesh, m_dofU[2]);\n\tgather(m_Ut, mesh, m_dofSU[0]);\n\tgather(m_Ut, mesh, m_dofSU[1]);\n\tgather(m_Ut, mesh, m_dofSU[2]);\n\n\tSolverWarnings();\n\n\t// allocate poro-vectors\n    assert((m_ndeq > 0) || (m_npeq > 0));\n    m_di.assign(m_ndeq, 0);\n\tm_Di.assign(m_ndeq, 0);\n\n\tif (m_npeq > 0) {\n\t\tm_pi.assign(m_npeq, 0);\n\t\tm_Pi.assign(m_npeq, 0);\n\n\t\t// we need to fill the total displacement vector m_Ut\n\t\t// (displacements are already handled in base class)\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tgather(m_Ut, mesh, m_dofP[0]);\n        gather(m_Ut, mesh, m_dofSP[0]);\n    }\n\n\treturn true;\n}\n\n//! Generate warnings if needed\nvoid FEBiphasicSolver::SolverWarnings()\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// Generate warning if rigid connectors are used with symmetric stiffness\n\tif (m_msymm == REAL_SYMMETRIC) {\n\t\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\t\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(plc);\n\t\t\tif (prc) {\n\t\t\t\tfeLogWarning(\"Rigid connectors require non-symmetric stiffness matrix.\\nSet symmetric_stiffness flag to 0 in Control section.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Generate warning if sliding-elastic contact is used with symmetric stiffness\n\t\tif (fem.SurfacePairConstraints() > 0)\n\t\t{\n\t\t\t// loop over all contact interfaces\n\t\t\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t\t\t{\n\t\t\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\t\t\tFESlidingElasticInterface* pbw = dynamic_cast<FESlidingElasticInterface*>(pci);\n\t\t\t\tif (pbw) {\n\t\t\t\t\tfeLogWarning(\"The sliding-elastic contact algorithm runs better with a non-symmetric stiffness matrix.\\nYou may set symmetric_stiffness flag to 0 in Control section.\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//! Initialize equations\nbool FEBiphasicSolver::InitEquations()\n{\n\t// define the solution variables for the Newton solver\n\t// Do this before calling base class!\n\t// TODO: Maybe I can get default values from the domains?\n\tint pressureOrder = (m_biphasicFormulation == 1 ? 1 : -1);\n\tAddSolutionVariable(&m_dofU, -1, \"displacement\", m_Dtol);\n\tAddSolutionVariable(&m_dofSU, -1, \"shell displacement\", m_Dtol);\n\tAddSolutionVariable(&m_dofP, pressureOrder, \"pressure\", m_Ptol);\n\tAddSolutionVariable(&m_dofSP, pressureOrder, \"shell fluid pressure\", m_Ptol);\n\n\t// set the interpolation orders\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint var_u = dofs.GetVariableIndex(\"displacement\");\n\tint var_p = dofs.GetVariableIndex(\"fluid pressure\");\n\tdofs.SetVariableInterpolationOrder(var_u, -1);\n\tdofs.SetVariableInterpolationOrder(var_p, pressureOrder);\n\n\t// First call the base class.\n\t// This will initialize all equation numbers, except the rigid body equation numbers\n\tif (FENewtonSolver::InitEquations2() == false) return false;\n\n\t// store the number of equations we currently have\n\tm_nreq = m_neq;\n\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tint neq = m_rigidSolver.InitEquations(m_neq);\n\tif (neq == -1) return false;\n\telse m_neq = neq;\n\n\t// Next, we add any Lagrange Multipliers\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* lmc = fem.NonlinearConstraint(i);\n\t\tif (lmc->IsActive())\n\t\t{\n\t\t\tm_neq += lmc->InitEquations(m_neq);\n\t\t}\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive())\n\t\t{\n\t\t\tm_neq += spc->InitEquations(m_neq);\n\t\t}\n\t}\n\t\n\t// determined the nr of pressure and concentration equations\n\tm_ndeq = m_npeq = 0;\n\t\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& n = mesh.Node(i);\n\t\tif (n.m_ID[m_dofU[0]] != -1) m_ndeq++;\n\t\tif (n.m_ID[m_dofU[1]] != -1) m_ndeq++;\n\t\tif (n.m_ID[m_dofU[2]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[0]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[1]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[2]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofP [0]] != -1) m_npeq++;\n        if (n.m_ID[m_dofSP[0]] != -1) m_npeq++;\n    }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Prepares the data for the first QN iteration. \n//!\n//! \\todo There is some more stuff in the base method that \n//!       I need to move to this method, but since it will\n//!       change the order of some operations I need to make\n//!       sure it won't break anything\nvoid FEBiphasicSolver::PrepStep()\n{\n\tzero(m_Pi);\n\tzero(m_Di);\n\n\t// for pressure nodal loads we need to multiply the time step size\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.ModelLoads(); ++i)\n\t{\n\t\tFENodalDOFLoad* pl = dynamic_cast<FENodalDOFLoad*>(fem.ModelLoad(i));\n\t\tif (pl && pl->IsActive())\n\t\t{\n\t\t\tif ((pl->GetDOF() == m_dofP[0]) || (pl->GetDOF() == m_dofSP[0]))\n\t\t\t{\n\t\t\t\tpl->SetDtScale(true);\n\t\t\t}\n\t\t}\n\t}\n\n\tFETimeInfo& tp = fem.GetTime();\n\tdouble dt = tp.timeIncrement;\n\ttp.augmentation = 0;\n\n\t// zero total displacements\n\tzero(m_Ui);\n\n\t// store previous mesh state\n\t// we need them for velocity and acceleration calculations\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& ni = mesh.Node(i);\n\t\tvec3d vs = (ni.m_rt - ni.m_rp)/dt;\n\t\tvec3d vq = (ni.m_dt - ni.m_dp)/dt;\n\t\tni.m_rp = ni.m_rt;\n\t\tni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tni.m_dp = ni.m_dt;\n\t\tni.UpdateValues();\n\n\t\tni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vs);\n\n\t\t// solid shell\n\t\tni.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vs - vq);\n\t}\n\n\t// apply concentrated nodal forces\n\t// since these forces do not depend on the geometry\n\t// we can do this once outside the NR loop.\n//\tvector<double> dummy(m_neq, 0.0);\n//\tzero(m_Fn);\n//\tFEResidualVector Fn(*GetFEModel(), m_Fn, dummy);\n//\tNodalLoads(Fn, tp);\n\n\t// apply boundary conditions\n\t// we save the prescribed displacements increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i = 0; i < nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& dc = *fem.BoundaryCondition(i);\n\t\tif (dc.IsActive()) dc.PrepStep(ui);\n\t}\n\n\t// do the linear constraints\n\tfem.GetLinearConstraintManager().PrepStep();\n\n\t// initialize rigid bodies\n\tm_rigidSolver.PrepStep(tp, ui);\n\n\t// intialize material point data\n\t// NOTE: do this before the stresses are updated\n\t// TODO: does it matter if the stresses are updated before\n\t//       the material point data is initialized\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tif (dom.IsActive()) dom.PreSolveUpdate(tp);\n\t}\n\n\t// update model state\n\tUpdateModel();\n\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc && plc->IsActive()) plc->PrepStep();\n\t}\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n\n\t// see if we need to do contact augmentations\n\tm_baugment = false;\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n\t\tif (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n\t}\n\n\t// see if we have to do nonlinear constraint augmentations\n\tif (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\n//! Implements the BFGS algorithm to solve the nonlinear FE equations.\nbool FEBiphasicSolver::Quasin()\n{\n\t// convergence norms\n\tdouble\tnormR1;\t\t// residual norm\n\tdouble\tnormE1;\t\t// energy norm\n\tdouble\tnormD;\t\t// displacement norm\n\tdouble\tnormd;\t\t// displacement increment norm\n\tdouble\tnormRi = 0; // initial residual norm\n\tdouble\tnormEi = 0; // initial energy norm\n\tdouble\tnormEm = 0;\t// max energy norm\n\tdouble\tnormDi = 0;\t// initial displacement norm\n\n\t// poro convergence norms data\n\tdouble\tnormPi = 0;\t// initial pressure norm\n\tdouble\tnormP;\t\t// current pressure norm\n\tdouble\tnormp;\t\t// incremement pressure norm\n\n\t// prepare for the first iteration\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tPrepStep();\n\n\t// init QN method\n\tif (QNInit() == false) return false;\n\n\t// loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter+1);\n\n\t\t// assume we'll converge. \n\t\tbconv = true;\n\n\t\t// solve the equations (returns line search; solution stored in m_ui)\n\t\tdouble s = QNSolve();\n\n\t\t// extract the pressure increments\n\t\tGetDisplacementData(m_di, m_ui);\n\n\t\t// set initial convergence norms\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tnormRi = fabs(m_R0*m_R0);\n\t\t\tnormEi = fabs(m_ui*m_R0);\n\t\t\tnormDi = fabs(m_di*m_di);\n\t\t\tnormEm = normEi;\n\n\t\t\tm_residuNorm.norm0 = normRi;\n\t\t\tm_energyNorm.norm0 = normEi;\n\t\t\tm_solutionNorm[0].norm0 = normDi;\n\t\t}\n\n\t\t// update all degrees of freedom\n\t\tfor (int i = 0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n\n\t\t// update displacements\n\t\tfor (int i = 0; i<m_ndeq; ++i) m_Di[i] += s*m_di[i];\n\n\t\t// calculate norms\n\t\tnormR1 = m_R1*m_R1;\n\t\tnormd  = (m_di*m_di)*(s*s);\n\t\tnormD  = m_Di*m_Di;\n\t\tnormE1 = s*fabs(m_ui*m_R1);\n\n\t\tm_residuNorm.norm = normR1;\n\t\tm_energyNorm.norm = normE1;\n\t\tm_solutionNorm[0].norm = normd;\n\n\t\t// check residual norm\n\t\tif ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\t\n\n\t\t// check displacement norm\n\t\tif ((m_Dtol > 0) && (normd  > (m_Dtol*m_Dtol)*normD )) bconv = false;\n\n\t\t// check energy norm\n\t\tif ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n\n\t\t// check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n\n\t\t// check energy divergence\n\t\tif (m_bdivreform)\n\t\t{\n\t\t\tif (normE1 > normEm) bconv = false;\n\t\t}\n\n\t\t// check poroelastic convergence\n\t\t{\n\t\t\t// extract the pressure increments\n\t\t\tGetPressureData(m_pi, m_ui);\n\n\t\t\t// set initial norm\n\t\t\tif (m_niter == 0) normPi = fabs(m_pi*m_pi);\n\n\t\t\t// update total pressure\n\t\t\tfor (int i = 0; i<m_npeq; ++i) m_Pi[i] += s*m_pi[i];\n\n\t\t\t// calculate norms\n\t\t\tnormP = m_Pi*m_Pi;\n\t\t\tnormp = (m_pi*m_pi)*(s*s);\n\n\t\t\t// check convergence\n\t\t\tif ((m_Ptol > 0) && (normp > (m_Ptol*m_Ptol)*normP)) bconv = false;\n\t\t}\n\n\t\t// print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n\t\tfeLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n\t\tfeLog(\"\\t   residual         %15le %15le %15le \\n\", normRi, normR1, m_Rtol*normRi);\n\t\tfeLog(\"\\t   energy           %15le %15le %15le \\n\", normEi, normE1, m_Etol*normEi);\n\t\tfeLog(\"\\t   displacement     %15le %15le %15le \\n\", normDi, normd ,(m_Dtol*m_Dtol)*normD );\n\t\tfeLog(\"\\t   fluid pressure   %15le %15le %15le \\n\", normPi, normp ,(m_Ptol*m_Ptol)*normP );\n\n\t\tif ((bconv == false) && (normR1 < m_Rmin))\n\t\t{\n\t\t\t// check for almost zero-residual on the first iteration\n\t\t\t// this might be an indication that there is no force on the system\n\t\t\tfeLogWarning(\"No force acting on the system.\");\n\t\t\tbconv = true;\n\t\t}\n\n\t\t// check if we have converged. \n\t\t// If not, calculate the BFGS update vectors\n\t\tif (bconv == false)\n\t\t{\n\t\t\tif (s < m_lineSearch->m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\t\t\telse if ((normE1 > normEm) && m_bdivreform)\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n\t\t\t\tnormEm = normE1;\n\t\t\t\tnormEi = normE1;\n\t\t\t\tnormRi = normR1;\n\t\t\t\tnormDi = normd;\n\t\t\t\tnormPi = normp;\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// Do augmentations\n\t\t\tbconv = DoAugmentations();\n\t\t}\n\t\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile (bconv == false);\n\n\t// if converged we update the total displacements\n\tif (bconv)\n\t{\n\t\tm_Ut += m_Ui;\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEBiphasicSolver::Residual(vector<double>& R)\n{\n\t// get the time information\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup global RHS vector\n    zero(R);\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tm_rigidSolver.Residual();\n\n\t// calculate internal stress force\n    InternalForces(RHS);\n\n    // calculate nodal reaction forces\n    for (int i = 0; i < m_neq; ++i) m_Fr[i] -= R[i];\n    \n    // calculate external forces\n    ExternalForces(RHS);\n    \n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEBiphasicSolver::StiffnessMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// setup the linear system of equations\n\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), m_alpha, m_nreq);\n\n\t// calculate the stiffness matrix for each domain\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tbool bsymm = (m_msymm == REAL_SYMMETRIC);\n\tif (pstep->m_nanalysis == FEBiphasicAnalysis::STEADY_STATE)\n\t{\n\t\tfor (int i=0; i<mesh.Domains(); ++i) \n\t\t{\n            // Biphasic analyses may include biphasic and elastic domains\n\t\t\tFEBiphasicDomain* pbdom = dynamic_cast<FEBiphasicDomain*>(&mesh.Domain(i));\n\t\t\tif (pbdom) pbdom->StiffnessMatrixSS(LS, bsymm);\n            else\n\t\t\t{\n\t\t\t\tFEElasticDomain* pedom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\t\t\tif (pedom) pedom->StiffnessMatrix(LS);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i=0; i<mesh.Domains(); ++i) \n\t\t{\n            // Biphasic analyses may include biphasic and elastic domains\n\t\t\tFEBiphasicDomain* pbdom = dynamic_cast<FEBiphasicDomain*>(&mesh.Domain(i));\n\t\t\tif (pbdom) pbdom->StiffnessMatrix(LS, bsymm);\n            else \n\t\t\t{\n\t\t\t\tFEElasticDomain* pedom = dynamic_cast<FEElasticDomain*>(&mesh.Domain(i));\n\t\t\t\tif (pedom) pedom->StiffnessMatrix(LS);\n\t\t\t}\n\t\t}\n\t}\n\n\t// calculate contact stiffness\n\tContactStiffness(LS);\n\n\t// calculate stiffness matrices for surface loads\n\tint nml = fem.ModelLoads();\n\tfor (int i=0; i<nml; ++i)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(i);\n\t\tif (pml->IsActive()) pml->StiffnessMatrix(LS);\n\t}\n\n\t// calculate nonlinear constraint stiffness\n\t// note that this is the contribution of the \n\t// constrainst enforced with augmented lagrangian\n\tNonLinearConstraintStiffness(LS, tp);\n\n\t// add contributions from rigid bodies\n\tm_rigidSolver.StiffnessMatrix(*m_pK, tp);\n\n\treturn true;\n}\n\n//! Update the model's kinematic data. \nvoid FEBiphasicSolver::UpdateKinematics(vector<double>& ui)\n{\n\t// first update all solid-mechanics kinematics\n\tFEModel& fem = *GetFEModel();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update rigid bodies\n\tm_rigidSolver.UpdateRigidBodies(m_Ui, ui);\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tint U_size = (int)U.size();\n#pragma omp parallel for\n\tfor (int i = 0; i < U_size; ++i)\n\t{\n\t\tU[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\t}\n\n\t// update flexible nodes\n\t// translational dofs\n\tscatter3(U, mesh, m_dofU[0], m_dofU[1], m_dofU[2]);\n\t// shell dofs\n\tscatter3(U, mesh, m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\n\t// make sure the boundary conditions are fullfilled\n\tint nbcs = fem.BoundaryConditions();\n\tfor (int i = 0; i < nbcs; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Update();\n\t}\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// Update the spatial nodal positions\n\t// Don't update rigid nodes since they are already updated\n\tint NN = mesh.Nodes();\n\t{\n\t\tfor (int i = 0; i < NN; ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tif (node.m_rid == -1) {\n\t\t\t\tnode.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n\t\t\t}\n\t\t\tnode.m_dt = node.m_d0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2])\n\t\t\t\t- node.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\t\t}\n\t}\n\n\t// update nonlinear constraints (needed for updating Lagrange Multiplier)\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* nlc = fem.NonlinearConstraint(i);\n\t\tif (nlc->IsActive()) nlc->Update(m_Ui, ui);\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive()) spc->Update(m_Ui, ui);\n\t}\n\n\t// update poroelastic data\n\tUpdatePoro(ui);\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the poroelastic data\nvoid FEBiphasicSolver::UpdatePoro(vector<double>& ui)\n{\n    int i, n;\n    \n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tdouble dt = fem.GetTime().timeIncrement;\n\n\t// update poro-elasticity data\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\n\t\t// update nodal pressures\n\t\tn = node.m_ID[m_dofP[0]];\n\t\tif (n >= 0) node.set(m_dofP[0], 0 + m_Ut[n] + m_Ui[n] + ui[n]);\n        n = node.m_ID[m_dofSP[0]];\n        if (n >= 0) node.set(m_dofSP[0], 0 + m_Ut[n] + m_Ui[n] + ui[n]);\n    }\n\n\t// update poro-elasticity data\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\n\t\t// update velocities\n\t\tvec3d vt = (node.m_rt - node.m_rp) / dt;\n\t\tnode.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n\t}\n}\n\n//! Updates the current state of the model\nvoid FEBiphasicSolver::Update(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tFETimeInfo& tp = fem.GetTime();\n\ttp.currentIteration = m_niter;\n\n\t// update EAS\n\tUpdateEAS(ui);\n\tUpdateIncrementsEAS(ui, true);\n\n\t// update kinematics\n\tUpdateKinematics(ui);\n\n\t// update domains \n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.IncrementalUpdate(ui, false);\n\t}\n\n\t// update model state\n\tUpdateModel();\n}\n\n//! Update EAS\nvoid FEBiphasicSolver::UpdateEAS(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update EAS on shell domains\n\tfor (int i = 0; i < mesh.Domains(); ++i) {\n\t\tFESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n\t\tif (sdom && sdom->IsActive()) sdom->UpdateEAS(ui);\n\t}\n}\n\n//! Update EAS\nvoid FEBiphasicSolver::UpdateIncrementsEAS(vector<double>& ui, const bool binc)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update EAS on shell domains\n\tfor (int i = 0; i < mesh.Domains(); ++i) {\n\t\tFESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n\t\tif (sdom && sdom->IsActive()) sdom->UpdateIncrementsEAS(ui, binc);\n\t}\n}\n\nvoid FEBiphasicSolver::UpdateModel()\n{\n\t// mark all free-draining surfaces\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\n\t\tFESlidingInterface2* psi2 = dynamic_cast<FESlidingInterface2*>(pci);\n\t\tif (psi2) psi2->MarkFreeDraining();\n\t\tFESlidingInterface3* psi3 = dynamic_cast<FESlidingInterface3*>(pci);\n\t\tif (psi3) psi3->MarkAmbient();\n        FESlidingInterfaceBiphasic* psib = dynamic_cast<FESlidingInterfaceBiphasic*>(pci);\n        if (psib) psib->MarkFreeDraining();\n\t\tFESlidingInterfaceBiphasicMixed* psbm = dynamic_cast<FESlidingInterfaceBiphasicMixed*>(pci);\n\t\tif (psbm) psbm->MarkFreeDraining();\n\t}\n\n\t// Update all contact interfaces\n\t// NOTE: note that we call the base class version here, not the overridden one!!\n\tFENewtonSolver::UpdateModel();\n\n\t// set free-draining boundary conditions\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\n\t\tFESlidingInterface2* psi2 = dynamic_cast<FESlidingInterface2*>(pci);\n\t\tif (psi2) psi2->SetFreeDraining();\n\t\tFESlidingInterface3* psi3 = dynamic_cast<FESlidingInterface3*>(pci);\n\t\tif (psi3) psi3->SetAmbient();\n        FESlidingInterfaceBiphasic* psib = dynamic_cast<FESlidingInterfaceBiphasic*>(pci);\n        if (psib) psib->SetFreeDraining();\n\t\tFESlidingInterfaceBiphasicMixed* psbm = dynamic_cast<FESlidingInterfaceBiphasicMixed*>(pci);\n\t\tif (psbm) psbm->SetFreeDraining();\n\t}\n    \n    // make sure the prescribed BCs (fluid pressure) are fullfilled\n    int nbcs = fem.BoundaryConditions();\n    for (int i = 0; i<nbcs; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.Repair();\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolver::GetDisplacementData(vector<double> &di, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.GetMesh().Nodes(), nid, m = 0;\n\tzero(di);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = fem.GetMesh().Node(i);\n\t\tnid = n.m_ID[m_dofU[0]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tdi[m++] = ui[nid];\n\t\t\tassert(m <= (int) di.size());\n\t\t}\n\t\tnid = n.m_ID[m_dofU[1]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tdi[m++] = ui[nid];\n\t\t\tassert(m <= (int) di.size());\n\t\t}\n\t\tnid = n.m_ID[m_dofU[2]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tdi[m++] = ui[nid];\n\t\t\tassert(m <= (int) di.size());\n\t\t}\n        nid = n.m_ID[m_dofSU[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            di[m++] = ui[nid];\n            assert(m <= (int) di.size());\n        }\n        nid = n.m_ID[m_dofSU[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            di[m++] = ui[nid];\n            assert(m <= (int) di.size());\n        }\n        nid = n.m_ID[m_dofSU[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            di[m++] = ui[nid];\n            assert(m <= (int) di.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBiphasicSolver::GetPressureData(vector<double> &pi, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.GetMesh().Nodes(), nid, m = 0;\n\tzero(pi);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = fem.GetMesh().Node(i);\n\t\tnid = n.m_ID[m_dofP[0]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tpi[m++] = ui[nid];\n\t\t\tassert(m <= (int) pi.size());\n\t\t}\n        nid = n.m_ID[m_dofSP[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            pi[m++] = ui[nid];\n            assert(m <= (int) pi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEBiphasicSolver::Serialize(DumpStream& ar)\n{\n\t// Serialize parameters\n\tFENewtonSolver::Serialize(ar);\n\n\tar& m_nrhs;\n\tar& m_niter;\n\tar& m_nref& m_ntotref;\n\tar& m_naug;\n\tar& m_nreq;\n\n\tar& m_Ut& m_Ui;\n\n\tar& m_arcLength;\n\tar& m_al_scale;\n\n\tif (ar.IsLoading())\n\t{\n//\t\tm_Fn.assign(m_neq, 0);\n\t\tm_Fr.assign(m_neq, 0);\n//\t\tm_Ui.assign(m_neq, 0);\n\t}\n\n\t// serialize rigid solver\n\tm_rigidSolver.Serialize(ar);\n\n\tif (ar.IsShallow()) return;\n\tar & m_Ptol & m_ndeq & m_npeq;\n\tar & m_nceq;\n\tar & m_di & m_Di & m_pi & m_Pi;\n}\n\n//-----------------------------------------------------------------------------\n//! Internal forces\nvoid FEBiphasicSolver::InternalForces(FEGlobalVector& RHS)\n{\n    // get the time information\n    FEModel& fem = *GetFEModel();\n    const FETimeInfo& tp = fem.GetTime();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // calculate internal stress force\n    if (fem.GetCurrentStep()->m_nanalysis == FEBiphasicAnalysis::STEADY_STATE)\n    {\n        for (int i=0; i<mesh.Domains(); ++i)\n        {\n            FEBiphasicDomain* pdom = dynamic_cast<FEBiphasicDomain*>(&mesh.Domain(i));\n            if (pdom) pdom->InternalForcesSS(RHS);\n            else\n            {\n                FEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n                dom.InternalForces(RHS);\n            }\n        }\n    }\n    else\n    {\n        for (int i=0; i<mesh.Domains(); ++i)\n        {\n            FEBiphasicDomain* pdom = dynamic_cast<FEBiphasicDomain*>(&mesh.Domain(i));\n            if (pdom) pdom->InternalForces(RHS);\n            else\n            {\n                FEElasticDomain& dom = dynamic_cast<FEElasticDomain&>(mesh.Domain(i));\n                dom.InternalForces(RHS);\n            }\n        }\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\n//! External forces\nvoid FEBiphasicSolver::ExternalForces(FEGlobalVector& RHS)\n{\n    // get the time information\n    FEModel& fem = *GetFEModel();\n    const FETimeInfo& tp = fem.GetTime();\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    // add model loads\n    int NML = fem.ModelLoads();\n    for (int i=0; i<NML; ++i)\n    {\n        FEModelLoad& mli = *fem.ModelLoad(i);\n        if (mli.IsActive()) mli.LoadVector(RHS);\n    }\n    \n    // calculate contact forces\n    ContactForces(RHS);\n    \n    // calculate nonlinear constraint forces\n    // note that these are the linear constraints\n    // enforced using the augmented lagrangian\n    NonLinearConstraintForces(RHS, tp);\n    \n    // set the nodal reaction forces\n    // TODO: Is this a good place to do this?\n    for (int i=0; i<mesh.Nodes(); ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.set_load(m_dofU[0], 0);\n        node.set_load(m_dofU[1], 0);\n        node.set_load(m_dofU[2], 0);\n        node.set_load(m_dofP[0], 0);\n\n        int n;\n        if ((n = node.m_ID[m_dofU[0]]) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n        \n        if ((n = node.m_ID[m_dofU[1]]) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n        \n        if ((n = node.m_ID[m_dofU[2]]) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n        \n        if ((n = node.m_ID[m_dofP[0]]) >= 0) node.set_load(m_dofP[0], -m_Fr[n]);\n        if ((n = -node.m_ID[m_dofP[0]] - 2) >= 0) node.set_load(m_dofP[0], -m_Fr[n]);\n    }\n}\n\n//! Calculates the contact forces\nvoid FEBiphasicSolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->LoadVector(R, tp);\n\t}\n}\n\n//! This function calculates the contact stiffness matrix\nvoid FEBiphasicSolver::ContactStiffness(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n\t}\n}\n\n//! calculate the nonlinear constraint forces \nvoid FEBiphasicSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->LoadVector(R, tp);\n\t}\n}\n\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEBiphasicSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEBiphasicSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FEElementTraits.h>\n#include <FECore/FEDofList.h>\n#include <FEBioMech/FERigidSolver.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n// This class adds additional functionality to the FESolidSolver to solve\n// biphasic problems. \nclass FEBIOMIX_API FEBiphasicSolver : public FENewtonSolver\n{\npublic:\n\t//! constructor\n\tFEBiphasicSolver(FEModel* pfem);\n\tvirtual ~FEBiphasicSolver();\n\n\t//! Initialize data structures\n\tbool Init() override;\n\n\t//! Initialize linear equation system\n\tbool InitEquations() override;\n\n\t//! prepares the data for the first QN iteration\n\tvoid PrepStep() override;\n\n\t//! Performs a Newton-Raphson iteration\n\tbool Quasin() override;\n\n\t//! serialize data to/from dump file\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Generate warnings if needed\n\tvoid SolverWarnings();\n\npublic:\n\tvoid Update(vector<double>& ui) override;\n\n\t//! update contact\n\tvoid UpdateModel() override;\n\n\t//! update kinematics\n\tvirtual void UpdateKinematics(vector<double>& ui);\n\n\t//! Update EAS\n\tvoid UpdateEAS(vector<double>& ui);\n\tvoid UpdateIncrementsEAS(vector<double>& ui, const bool binc);\n\n\t//! Update poroelastic data\n\tvoid UpdatePoro(vector<double>& ui);\n\npublic:\n\n\t//! Calculates residual (overridden from FESolidSolver2)\n\tbool Residual(vector<double>& R) override;\n\n\t//! calculates the global stiffness matrix (overridden from FESolidSolver2)\n\tbool StiffnessMatrix() override;\n\n    //! Internal forces\n    void InternalForces(FEGlobalVector& R);\n    \n    //! external forces\n    void ExternalForces(FEGlobalVector& R);\n\n\tvoid ContactForces(FEGlobalVector& R);\n\n\tvoid ContactStiffness(FELinearSystem& LS);\n\n\tvoid NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n\tvoid NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n\nprotected:\n\tvoid GetDisplacementData(vector<double>& di, vector<double>& ui);\n\tvoid GetPressureData(vector<double>& pi, vector<double>& ui);\n\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n\t// additional convergence norms\n\tdouble\tm_Dtol;\t\t\t//!< displacement tolerance\n\tdouble\tm_Ptol;\t\t\t//!< pressure tolerance\n    double  m_Ctol;         //!< needed only for biphasic-solute analyses\n\n\t// biphasic formulation\n\tint\t\tm_biphasicFormulation;\t// = 0: standard, =1: mixed (linear pressure)\n\n\t// equation numbers\n\tint\t\tm_ndeq;\t\t\t\t//!< number of equations related to displacement dofs\n\tint\t\tm_npeq;\t\t\t\t//!< number of equations related to pressure dofs\n\tint\t\tm_nreq;\t\t\t//!< start of rigid body equations\n\tvector<int>\t\tm_nceq;\t//!< number of equations related to concentration dofs\n\n\tvector<double> m_Fr;\t//!< nodal reaction forces\n\n\t// poro data\n\tvector<double>\tm_di;\t//!< displacement increment vector\n\tvector<double>\tm_Di;\t//!< total displacement vector for iteration\n\tvector<double>\tm_pi;\t//!< pressure increment vector\n\tvector<double>\tm_Pi;\t//!< Total pressure vector for iteration\n\nprotected:\n\tFEDofList\tm_dofU, m_dofV;\n\tFEDofList\tm_dofRQ;\n\tFEDofList\tm_dofSU, m_dofSV, m_dofSA;\n\tFEDofList\tm_dofP;\t//!< pressure dof index\n\tFEDofList\tm_dofSP;\t//!< shell pressure dof index\n\n\t// obsolete parameters\n\tdouble  m_rhoi = -2;\n\tdouble\tm_alpha = 1;\n\tdouble\tm_beta = 0.25;\n\tdouble\tm_gamma = 0.5;\n\tbool\tm_logSolve = false;\n\tint\t\tm_arcLength = 0;\n\tdouble\tm_al_scale = 0;\n\nprotected:\n\tFERigidSolverNew\tm_rigidSolver;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FECarterHayes.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECarterHayes.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n#include <FEBioMech/FEElasticMixture.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FECarterHayes, FEElasticMaterial)\n\tADD_PARAMETER(m_E0  , FE_RANGE_GREATER(0.0)         , \"E0\"   )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_rho0, FE_RANGE_GREATER(0.0)         , \"rho0\" )->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_g   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma\");\n\tADD_PARAMETER(m_v   , FE_RANGE_RIGHT_OPEN(-1.0, 0.5), \"v\"    );\n\tADD_PARAMETER(m_sbm , \"sbm\")->setEnums(\"$(sbms)\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FECarterHayes::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\t\n\t// get the parent material which must be a multiphasic material\n    FEMultiphasic* pMP = GetAncestor()->ExtractProperty<FEMultiphasic>();\n\tif (pMP == 0) {\n\t\tfeLogError(\"Parent material must be multiphasic\");\n\t\treturn false;\n\t}\n\n\t// extract the local id of the SBM whose density controls Young's modulus from the global id\n\tm_lsbm = pMP->FindLocalSBMID(m_sbm);\n\tif (m_lsbm == -1) {\n\t\tfeLogError(\"Invalid value for sbm\");\n\t\treturn false;\n\t}\n\n    FEElasticMaterial* pem = pMP->GetSolid();\n    FEElasticMixture* psm = dynamic_cast<FEElasticMixture*>(pem);\n    if (psm == nullptr) {\n        m_comp = -1;    // in case material is not a solid mixture\n        return true;\n    }\n    \n    for (int i=0; i<psm->Materials(); ++i) {\n        pem = psm->GetMaterial(i);\n        if (pem == this) {\n            m_comp = i;\n            break;\n        }\n    }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECarterHayes::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_lsbm;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data\nFEMaterialPointData* FECarterHayes::CreateMaterialPointData()\n{\n\treturn new FERemodelingMaterialPoint(new FEElasticMaterialPoint);\n}\n\n//-----------------------------------------------------------------------------\n//! update specialize material point data\nvoid FECarterHayes::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    FERemodelingMaterialPoint* rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n    rpt->m_rhor = spt.m_sbmr[m_lsbm];\n    rpt->m_rhorp = spt.m_sbmrp[m_lsbm];\n    rpt->m_sed = StrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\ndouble FECarterHayes::StrainEnergy(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble detF = pt.m_J;\n\tdouble lndetF = log(detF);\n\t\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\tdouble I1 = b.tr();\n\t\n\t// lame parameters\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n\tdouble m_E = YoungModulus(rhor);\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\t\n\tdouble sed = mu*((I1-3)/2 - lndetF)+lam*lndetF*lndetF/2;\n\t\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FECarterHayes::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble detF = pt.m_J;\n\tdouble detFi = 1.0/detF;\n\tdouble lndetF = log(detF);\n\n\t// evaluate the strain energy\n\tFERemodelingMaterialPoint& rpt = *mp.ExtractData<FERemodelingMaterialPoint>();\n\trpt.m_sed = StrainEnergy(mp);\n\t\n\t// calculate left Cauchy-Green tensor\n\tmat3ds b = pt.LeftCauchyGreen();\n\t\n\t// lame parameters\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n\tdouble m_E = YoungModulus(rhor);\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// calculate stress\n\tmat3ds s = (b - I)*(mu*detFi) + I*(lam*lndetF*detFi);\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FECarterHayes::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// deformation gradient\n\tdouble detF = pt.m_J;\n\t\n\t// lame parameters\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n\tdouble m_E = YoungModulus(rhor);\n\tdouble lam = m_v*m_E/((1+m_v)*(1-2*m_v));\n\tdouble mu  = 0.5*m_E/(1+m_v);\n\t\n\tdouble lam1 = lam / detF;\n\tdouble mu1  = (mu - lam*log(detF)) / detF;\n\t\n\tdouble D[6][6] = {0};\n\tD[0][0] = lam1+2.*mu1; D[0][1] = lam1       ; D[0][2] = lam1       ;\n\tD[1][0] = lam1       ; D[1][1] = lam1+2.*mu1; D[1][2] = lam1       ;\n\tD[2][0] = lam1       ; D[2][1] = lam1       ; D[2][2] = lam1+2.*mu1;\n\tD[3][3] = mu1;\n\tD[4][4] = mu1;\n\tD[5][5] = mu1;\n\t\n\treturn tens4ds(D);\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate referential mass density\ndouble FECarterHayes::Density(FEMaterialPoint& pt)\n{\n    FERemodelingMaterialPoint* rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n    if (rpt) return rpt->m_rhor;\n    else {\n        FEElasticMixtureMaterialPoint* emp = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        if (emp) {\n            rpt = emp->GetPointData(m_comp)->ExtractData<FERemodelingMaterialPoint>();\n            if (rpt) return rpt->m_rhor;\n        }\n    }\n    return 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of strain energy density with mass density\ndouble FECarterHayes::Tangent_SE_Density(FEMaterialPoint& mp)\n{\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n    return StrainEnergy(mp)*m_g/rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of stress with mass density\nmat3ds FECarterHayes::Tangent_Stress_Density(FEMaterialPoint& mp)\n{\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n    return Stress(mp)*m_g/rhor;\n}\n\n"
  },
  {
    "path": "FEBioMix/FECarterHayes.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include \"FEBioMech/FERemodelingElasticMaterial.h\"\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This is a neo-Hookean material whose Young's modulus is evaluated from the density\n//! according to the power-law relation proposed by Carter and Hayes for trabecular bone\n\nclass FEBIOMIX_API FECarterHayes : public FEElasticMaterial, public FERemodelingInterface\n{\npublic:\n\tFECarterHayes(FEModel* pfem) : FEElasticMaterial(pfem) { m_E0 = 0; m_rho0 = 1; m_sbm = -1; m_lsbm = -1; m_g = 0; }\n\t\npublic: // parameters\n\tdouble\tm_E0;\t//!< Young's modulus at reference sbm density\n\tdouble\tm_g;\t//!< gamma exponent for calculation of Young's modulus\n\tdouble\tm_v;\t//!< prescribed Poisson's ratio\n    double  m_rho0; //!< reference sbm density\n//\tint\t\tm_sbm;\t//!< global id of solid-bound molecule\n\nprotected:\n\tint\t\tm_lsbm;\t//!< local id of solid-bound molecule\n\npublic:\n\t//! data initialization and checking\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n    //! evaluate referential mass density\n    double Density(FEMaterialPoint& pt) override;\n    \n\t//! Create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\t\n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override { return StrainEnergy(pt); }\n    \n    //! update specialize material point data\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic: // --- remodeling interface ---\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergy(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent of strain energy density with mass density\n\tdouble Tangent_SE_Density(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent of stress with mass density\n\tmat3ds Tangent_Stress_Density(FEMaterialPoint& pt) override;\n\n\t//! return Young's modulus\n\tdouble YoungModulus(double rhor) { return m_E0*pow(rhor/m_rho0, m_g);}\n\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEChemicalReaction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEChemicalReaction.h\"\n#include <FECore/FEElementTraits.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include \"FESoluteInterface.h\"\n#include \"FESolute.h\"\n#include <stdlib.h>\n\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEChemicalReaction, FEReaction)\n    ADD_PARAMETER(m_Vovr, \"override_vbar\")->SetFlags(FE_PARAM_WATCH);\n\tADD_PARAMETER(m_Vbar , \"Vbar\")->SetWatchVariable(&m_Vovr);\n\tADD_PROPERTY(m_vRtmp, \"vR\", FEProperty::Optional)->SetLongName(\"Reactants\");\n    ADD_PROPERTY(m_vPtmp, \"vP\", FEProperty::Optional)->SetLongName(\"Products\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEChemicalReaction::FEChemicalReaction(FEModel* pfem) : FEReaction(pfem)\n{\n    // additional initializations\n    m_Vbar = 0.0;\n\tm_Vovr = false; \n    m_nsol = -1;\n\n\tm_pFwd = m_pRev = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEChemicalReaction::Init() \n{\n    // set the parents for the reaction rates\n    if (m_pFwd) m_pFwd->m_pReact = this;\n    if (m_pRev) m_pRev->m_pReact = this;\n    \n    // initialize base class\n    if (FEReaction::Init() == false) return false;\n    \n\t// initialize the reaction coefficients\n\tint isol, isbm, itot;\n\n    int nsol = m_psm->Solutes();\n    int nsbm = m_psm->SBMs();\n    int ntot = nsol + nsbm;\n\n\t// initialize the stoichiometric coefficients to zero\n\tm_nsol = nsol;\n\tm_vR.assign(ntot, 0);\n\tm_vP.assign(ntot, 0);\n\tm_v.assign(ntot, 0);\n\n    // create the intmaps\n    for (int i = 0; i < m_vRtmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvr = m_vRtmp[i];\n        pvr->Init();\n        if (pvr->IsSolute()) SetStoichiometricCoefficient(m_solR, pvr->m_speciesID - 1, pvr->m_v);\n        if (pvr->IsSBM()   ) SetStoichiometricCoefficient(m_sbmR, pvr->m_speciesID - 1, pvr->m_v);\n    }\n    for (int i = 0; i < m_vPtmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvp = m_vPtmp[i];\n        pvp->Init();\n        if (pvp->IsSolute()) SetStoichiometricCoefficient(m_solP, pvp->m_speciesID - 1, pvp->m_v);\n        if (pvp->IsSBM()   ) SetStoichiometricCoefficient(m_sbmP, pvp->m_speciesID - 1, pvp->m_v);\n    }\n\n\t// cycle through all the solutes in the mixture and determine\n\t// if they participate in this reaction\n\titrmap it;\n\tintmap solR = m_solR;\n\tintmap solP = m_solP;\n\tfor (isol = 0; isol<nsol; ++isol) {\n        int sid = m_psm->GetSolute(isol)->GetSoluteID() - 1;\n\t\tit = solR.find(sid);\n\t\tif (it != solR.end()) m_vR[isol] = it->second;\n\t\tit = solP.find(sid);\n\t\tif (it != solP.end()) m_vP[isol] = it->second;\n\t}\n\n\t// cycle through all the solid-bound molecules in the mixture\n\t// and determine if they participate in this reaction\n\tintmap sbmR = m_sbmR;\n\tintmap sbmP = m_sbmP;\n\tfor (isbm = 0; isbm<nsbm; ++isbm) {\n\t\tint sid = m_psm->GetSBM(isbm)->GetSBMID() - 1;\n\t\tit = sbmR.find(sid);\n\t\tif (it != sbmR.end()) m_vR[nsol + isbm] = it->second;\n\t\tit = sbmP.find(sid);\n\t\tif (it != sbmP.end()) m_vP[nsol + isbm] = it->second;\n\t}\n\n\t// evaluate the net stoichiometric coefficient\n\tfor (itot = 0; itot<ntot; ++itot) {\n\t\tm_v[itot] = m_vP[itot] - m_vR[itot];\n\t}\n\n\t// evaluate the weighted molar volume of reactants and products\n\tif (!m_Vovr) {\n\t\tm_Vbar = 0;\n\t\tfor (isol = 0; isol<nsol; ++isol)\n        {\n            FESolute* sol = m_psm->GetSolute(isol);\n            m_Vbar += m_v[isol] * sol->MolarMass() / sol->Density();\n        }\n        for (isbm = 0; isbm < nsbm; ++isbm)\n        {\n            FESolidBoundMolecule* sbm = m_psm->GetSBM(isbm);\n            m_Vbar += m_v[nsol + isbm] * sbm->MolarMass() / sbm->Density();\n        }\n\t}\n\n\t// check that the chemical reaction satisfies electroneutrality\n\tint znet = 0;\n\tfor (isol = 0; isol<nsol; ++isol)\n    {\n        znet += m_v[isol] * m_psm->GetSolute(isol)->ChargeNumber();\n    }\n    for (isbm = 0; isbm < nsbm; ++isbm)\n    {\n        znet += m_v[nsol + isbm] * m_psm->GetSBM(isbm)->ChargeNumber();\n    }\n\n\tif (znet != 0) {\n\t\tfeLogError(\"chemical reaction must satisfy electroneutrality\");\n\t\treturn false;\n\t}\n    \n    if (m_pFwd && !m_pFwd->Init()) return false;\n    if (m_pRev && !m_pRev->Init()) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Data serialization\nvoid FEChemicalReaction::Serialize(DumpStream& ar)\n{\n    FEReaction::Serialize(ar);\n    \n    if (ar.IsShallow() == false)\n    {\n        if (ar.IsSaving())\n        {\n            itrmap p;\n            ar << m_nsol << m_vR << m_vP << m_v << m_Vovr;\n            ar << (int) m_solR.size();\n            for (p = m_solR.begin(); p!=m_solR.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_solP.size();\n            for (p = m_solP.begin(); p!=m_solP.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_sbmR.size();\n            for (p = m_sbmR.begin(); p!=m_sbmR.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_sbmP.size();\n            for (p = m_sbmP.begin(); p!=m_sbmP.end(); ++p) {ar << p->first; ar << p->second;}\n            }\n        else\n        {\n            // restore pointers\n            if (m_pFwd) m_pFwd->m_pReact = this;\n            if (m_pRev) m_pRev->m_pReact = this;\n            \n            ar >> m_nsol >> m_vR >> m_vP >> m_v >> m_Vovr;\n            int size, id, vR;\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solR, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solP, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_sbmR, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_sbmP, id, vR);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FEChemicalReaction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEReaction.h\"\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for reaction rates.\n\nclass FEBIOMIX_API FEReactionRate : public FEMaterialProperty\n{\npublic:\n    //! constructor\n    FEReactionRate(FEModel* pfem) : FEMaterialProperty(pfem), m_pReact(nullptr) {}\n    \n    //! reaction rate at material point\n    virtual double ReactionRate(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of reaction rate with strain at material point\n    virtual mat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of reaction rate with effective fluid pressure at material point\n    virtual double Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) = 0;\n    \n    //! reset, initialize and update chemical reaction data in the FESolutesMaterialPoint\n    virtual void ResetElementData(FEMaterialPoint& mp) {}\n    virtual void InitializeElementData(FEMaterialPoint& mp) {}\n    virtual void UpdateElementData(FEMaterialPoint& mp) {}\n    \npublic:\n    FEReaction*    m_pReact;    //!< pointer to parent reaction\n\n    FECORE_BASE_CLASS(FEReactionRate)\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for chemical reactions.\n\nclass FEBIOMIX_API FEChemicalReaction : public FEReaction\n{\npublic:\n\t//! constructor\n    FEChemicalReaction(FEModel* pfem);\n    \n\t//! initialization\n\tbool Init() override;\n\npublic:\n    //! set the forward reaction rate\n    void SetForwardReactionRate(FEReactionRate* pfwd) { m_pFwd = pfwd; }\n    \n    //! set the reverse reaction rate\n    void SetReverseReactionRate(FEReactionRate* prev) { m_pRev = prev; }\n    \npublic:\n    //! reset, initialize and update optional chemical reaction data in the FESolutesMaterialPoint\n    void ResetElementData(FEMaterialPoint& mp)\n    {\n        if (m_pFwd) m_pFwd->ResetElementData(mp);\n        if (m_pRev) m_pRev->ResetElementData(mp);\n    }\n    void InitializeElementData(FEMaterialPoint& mp)\n    {\n        if (m_pFwd) m_pFwd->InitializeElementData(mp);\n        if (m_pRev) m_pRev->InitializeElementData(mp);\n    }\n    void UpdateElementData(FEMaterialPoint& mp)\n    {\n        if (m_pFwd) m_pFwd->UpdateElementData(mp);\n        if (m_pRev) m_pRev->UpdateElementData(mp);\n    }\n    \npublic:\n    //! molar supply at material point\n    virtual double ReactionSupply(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of molar supply with strain at material point\n    virtual mat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of molar supply with effective pressure at material point\n    virtual double Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of molar supply with effective concentration at material point\n    virtual double Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) = 0;\n    \npublic:\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n    vector<FEReactantSpeciesRef*> m_vRtmp;\t//!< helper variable for reading in stoichiometric coefficients for reactants\n    vector<FEProductSpeciesRef*> m_vPtmp;\t//!< helper variable for reading in stoichiometric coefficients for products\n\n    FEReactionRate*    m_pFwd;        //!< pointer to forward reaction rate\n    FEReactionRate*    m_pRev;        //!< pointer to reverse reaction rate\n    \npublic:\n\tintmap\t\t\tm_solR;\t\t//!< stoichiometric coefficients of solute reactants (input)\n\tintmap\t\t\tm_solP;\t\t//!< stoichiometric coefficients of solute products (input)\n\tintmap\t\t\tm_sbmR;\t\t//!< stoichiometric coefficients of solid-bound reactants (input)\n\tintmap\t\t\tm_sbmP;\t\t//!< stoichiometric coefficients of solid-bound products (input)\n\t\npublic:\n\tint\t\t\t\tm_nsol;\t\t//!< number of solutes in the mixture\n\tvector<int>\t\tm_vR;\t\t//!< stoichiometric coefficients of reactants\n\tvector<int>\t\tm_vP;\t\t//!< stoichiometric coefficients of products\n\tvector<int>\t\tm_v;\t\t//!< net stoichiometric coefficients of reactants and products\n    double          m_Vbar;     //!< weighted molar volume of reactants and products\n    bool            m_Vovr;     //!< override flag for m_Vbar\n\n\tDECLARE_FECORE_CLASS();\n    FECORE_BASE_CLASS(FEChemicalReaction)\n};\n"
  },
  {
    "path": "FEBioMix/FEConcentrationIndependentReaction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEConcentrationIndependentReaction.h\"\n\nBEGIN_FECORE_CLASS(FEConcentrationIndependentReaction, FEChemicalReaction)\n\t// set material properties\n\tADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConcentrationIndependentReaction::FEConcentrationIndependentReaction(FEModel* pfem) : FEChemicalReaction(pfem) \n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEConcentrationIndependentReaction::ReactionSupply(FEMaterialPoint& pt)\n{\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// get reaction rate\n\tdouble kF = m_pFwd->ReactionRate(pt);\n\t\n\t// evaluate the reaction molar supply\n\tdouble zhat = kF;\n\t\n\t// contribution of solid-bound molecules\n\tconst int nsol = (int)spt.m_ca.size();\n\tconst int nsbm = (int)spt.m_sbmr.size();\n\tfor (int i=0; i<nsbm; ++i) {\n\t\tint vR = m_vR[nsol+i];\n\t\tif (vR > 0) {\n\t\t\tdouble c = m_psm->SBMConcentration(pt, i);\n\t\t\tzhat *= pow(c, vR);\n\t\t}\n\t}\n\t\n\treturn zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\nmat3ds FEConcentrationIndependentReaction::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& bpt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\tconst int nsol = m_nsol;\n\tconst int nsbm = (int)m_v.size() - nsol;\n\tdouble J = ept.m_J;\n\tdouble phi0 = bpt.m_phi0t;\n    \n\tdouble kF = m_pFwd->ReactionRate(pt);\n\tmat3ds dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n\tdouble zhat = ReactionSupply(pt);\n\tmat3ds dzhatde = mat3dd(0);\n\tif (kF > 0) {\n\t\tdzhatde += dkFde/kF;\n\t}\n\tmat3ds I = mat3dd(1);\n\tfor (int isbm = 0; isbm<nsbm; ++isbm)\n\t\tdzhatde -= I*(m_vR[nsol+isbm]/(J-phi0));\n\t\n\tdzhatde *= zhat;\n\t\n\treturn dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEConcentrationIndependentReaction::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n\tdouble kF = m_pFwd->ReactionRate(pt);\n\tdouble dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n\tdouble zhat = ReactionSupply(pt);\n\tdouble dzhatdp = 0;\n\tif (kF > 0) {\n\t\tdzhatdp = dkFdp*zhat/kF;\n\t}\n\treturn dzhatdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEConcentrationIndependentReaction::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioMix/FEConcentrationIndependentReaction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMultiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! Concentration-independent forward chemical reaction.\n\nclass FEBIOMIX_API FEConcentrationIndependentReaction : public FEChemicalReaction\n{\npublic:\n\t//! constructor\n\tFEConcentrationIndependentReaction(FEModel* pfem);\n\t\t\n\t//! molar supply at material point\n\tdouble ReactionSupply(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with strain (J) at material point\n\tmat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective pressure at material point\n\tdouble Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective concentration at material point\n\tdouble Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEDiffAlbroIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiffAlbroIso.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDiffAlbroIso, FESoluteDiffusivity)\n\tADD_PARAMETER(m_diff0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"free_diff\")->setUnits(UNIT_DIFFUSIVITY)->setLongName(\"free diffusivity\");\n\tADD_PARAMETER(m_cdinv , FE_RANGE_GREATER_OR_EQUAL(0.0), \"cdinv\"    )->setUnits(\"L^3/n\");\n\tADD_PARAMETER(m_alphad, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alphad\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEDiffAlbroIso::FEDiffAlbroIso(FEModel* pfem) : FESoluteDiffusivity(pfem)\n{\n\tm_diff0 = 1;\n\tm_cdinv = 0;\n    m_alphad = 0;\n    m_lsol = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization.\nbool FEDiffAlbroIso::Init()\n{\n\tif (FESoluteDiffusivity::Init() == false) return false;\n\n\t// get the grandparent material which must be\n    // a biphasic-solute/triphasic/multiphasic material\n    FESolute* pSol = dynamic_cast<FESolute*> (GetParent());\n    m_lsol = pSol->GetSoluteLocalID();\n    \n\tif (m_lsol == -1) {\n\t\tfeLogError(\"Invalid value for sol\"); \n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Free diffusivity\ndouble FEDiffAlbroIso::Free_Diffusivity(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\t\n    // solute concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    \n    // diffusivity coefficient\n    double d = m_diff0(mp)*exp(-m_cdinv(mp)*ca);\n    \n\treturn d;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of free diffusivity with respect to concentration\ndouble FEDiffAlbroIso::Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\n    // solute concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double dkdc = psm->dkdc(mp, m_lsol, isol);\n    double k = psm->GetPartitionCoefficient(mp, m_lsol);\n    \n    // diffusivity coefficient\n    double d = m_diff0(mp)*exp(-m_cdinv(mp)*ca);\n    // derivative of d w.r.t. actual concentration\n    double dc = -m_cdinv(mp)*d;\n    \n    // tangent w.r.t. concentration\n    if (isol == m_lsol)\n        return dc*(k+dkdc*c);\n    else\n        return dc*dkdc*c;\n}\n\n//-----------------------------------------------------------------------------\n//! Diffusivity tensor.\nmat3ds FEDiffAlbroIso::Diffusivity(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n\tFEElasticMaterialPoint* et = mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et->m_J;\n\t\n\t// solid volume fraction in reference configuration\n\tdouble phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    // porosity in current configuration\n    double phiw = 1 - phi0/J;\n    // solute concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    \n    // diffusivity coefficient\n    double d = m_diff0(mp)*exp(-m_alphad(mp)*(1-phiw)/phiw - m_cdinv(mp)*ca);\n\t\n\t// diffusivity tensor\n    mat3dd dt(d);\n\t\n\treturn dt;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to strain\ntens4dmm FEDiffAlbroIso::Tangent_Diffusivity_Strain(FEMaterialPoint &mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n    FEElasticMaterialPoint* et = mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// relative volume\n\tdouble J = et->m_J;\n\t\n\t// solid volume fraction in reference configuration\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    // porosity in current configuration\n    double phiw = 1 - phi0/J;\n    // solute concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double dkdJ = psm->dkdJ(mp, m_lsol);\n    \n    // diffusivity coefficient\n    double d = m_diff0(mp)*exp(-m_alphad(mp)*(1-phiw)/phiw - m_cdinv(mp)*ca);\n    \n    // derivative of (J d) w.r.t. J\n    double dJ = d*(1+J*(m_alphad(mp)*phi0/(J-phi0)/(J-phi0) - m_cdinv(mp)*c*dkdJ));\n\t\t\n\ttens4dmm D4 = dyad1s(I)*dJ-dyad4s(I)*(2*d);\n\t\n\treturn D4;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to concentration\nmat3ds FEDiffAlbroIso::Tangent_Diffusivity_Concentration(FEMaterialPoint &mp, const int isol)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n    FEElasticMaterialPoint* et = mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et->m_J;\n\t\n\t// solid volume fraction in reference configuration\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    // porosity in current configuration\n    double phiw = 1 - phi0/J;\n    // solute concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double dkdc = psm->dkdc(mp, m_lsol, isol);\n    double k = psm->GetPartitionCoefficient(mp, m_lsol);\n    \n    // diffusivity coefficient\n    double d = m_diff0(mp)*exp(-m_alphad(mp)*(1-phiw)/phiw - m_cdinv(mp)*ca);\n    // derivative of d w.r.t. actual concentration\n    double dc = -m_cdinv(mp)*d;\n    \n    // tangent w.r.t. concentration\n    if (isol == m_lsol) {\n        return mat3dd(dc*(k+dkdc*c));\n    } else\n        return mat3dd(dc*dkdc*c);\n}\n"
  },
  {
    "path": "FEBioMix/FEDiffAlbroIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a porosity and concentration\n// dependent diffusivity which is isotropic, according to the constitutive relation\n// of Albro et al (CMBE 2009)\n\nclass FEBIOMIX_API FEDiffAlbroIso : public FESoluteDiffusivity\n{\npublic:\n    //! constructor\n    FEDiffAlbroIso(FEModel* pfem);\n    \n    //! free diffusivity\n    double Free_Diffusivity(FEMaterialPoint& pt) override;\n    \n    //! Tangent of free diffusivity with respect to concentration\n    double Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& pt, const int isol) override;\n    \n    //! diffusivity\n    mat3ds Diffusivity(FEMaterialPoint& pt) override;\n    \n    //! Tangent of diffusivity with respect to strain\n    tens4dmm Tangent_Diffusivity_Strain(FEMaterialPoint& mp) override;\n    \n    //! Tangent of diffusivity with respect to concentration\n    mat3ds Tangent_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol=0) override;\n    \n    //! data initialization and checking\n    bool Init() override;\n    \npublic:\n    FEParamDouble\tm_diff0;        //!< free diffusivity\n    FEParamDouble\tm_cdinv;\t\t//!< inverse of characteristic concentration c_D\n    FEParamDouble\tm_alphad;\t\t//!< non-dimensional coefficient for porosity term\n    int     m_lsol;                 //!< local ID of solute\n    \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEDiffConstIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiffConstIso.h\"\n#include <FECore/log.h>\n#include <FECore/FEMesh.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDiffConstIso, FESoluteDiffusivity)\n\tADD_PARAMETER(m_free_diff, FE_RANGE_GREATER_OR_EQUAL(0.0), \"free_diff\")->setUnits(UNIT_DIFFUSIVITY)->setLongName(\"free diffusivity\");\n\tADD_PARAMETER(m_diff     , FE_RANGE_GREATER_OR_EQUAL(0.0), \"diff\"     )->setUnits(UNIT_DIFFUSIVITY)->setLongName(\"diffusivity\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEDiffConstIso::FEDiffConstIso(FEModel* pfem) : FESoluteDiffusivity(pfem)\n{\n\tm_free_diff = 1;\n    m_diff = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Validation\nbool FEDiffConstIso::Validate()\n{\n\tif (FESoluteDiffusivity::Validate() == false) return false;\n    FEMesh& mesh = GetMesh();\n    for (int i=0; i<mesh.Elements(); ++i) {\n        FEElement& elem = *mesh.Element(i);\n        for (int n=0; n<elem.GaussPoints(); ++n) {\n            FEMaterialPoint& mp = *elem.GetMaterialPoint(n);\n            if (m_free_diff(mp) < m_diff(mp)) {\n                feLogError(\"free_diff must be >= diff in element %i\", elem.GetID());\n                return false;\n            }\n        }\n    }\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Free diffusivity\ndouble FEDiffConstIso::Free_Diffusivity(FEMaterialPoint& mp)\n{\n\treturn m_free_diff(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of free diffusivity with respect to concentration\ndouble FEDiffConstIso::Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Diffusivity tensor\nmat3ds FEDiffConstIso::Diffusivity(FEMaterialPoint& mp)\n{\n\t// --- constant isotropic diffusivity ---\n\t\n\treturn mat3dd(m_diff(mp));\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to strain\ntens4dmm FEDiffConstIso::Tangent_Diffusivity_Strain(FEMaterialPoint &mp)\n{\n\ttens4dmm D;\n\tD.zero();\n\treturn D;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to concentration\nmat3ds FEDiffConstIso::Tangent_Diffusivity_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\tmat3ds d;\n\td.zero();\n\treturn d;\n}\n"
  },
  {
    "path": "FEBioMix/FEDiffConstIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a constant diffusivity\n\nclass FEBIOMIX_API FEDiffConstIso :\tpublic FESoluteDiffusivity\n{\npublic:\n\t//! constructor\n\tFEDiffConstIso(FEModel* pfem);\n\t\n\t//! free diffusivity\n\tdouble Free_Diffusivity(FEMaterialPoint& pt) override;\n\n\t//! Tangent of diffusivity with respect to concentration\n\tdouble Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol) override;\n\n\t//! diffusivity\n\tmat3ds Diffusivity(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of diffusivity with respect to strain\n\ttens4dmm Tangent_Diffusivity_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of diffusivity with respect to concentration\n\tmat3ds Tangent_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol) override;\n\t\n\t//! data checking\n\tbool Validate() override;\n\t\npublic:\n\tFEParamDouble\tm_free_diff;\t//!< free diffusivity\n    FEParamDouble\tm_diff;\t\t\t//!< diffusivity\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEDiffConstOrtho.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiffConstOrtho.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDiffConstOrtho, FESoluteDiffusivity)\n\tADD_PARAMETER(m_free_diff,    FE_RANGE_GREATER(0.0)         , \"free_diff\")->setUnits(UNIT_DIFFUSIVITY)->setLongName(\"free diffusivity\");\n\tADD_PARAMETER(m_diff     , 3, FE_RANGE_GREATER_OR_EQUAL(0.0), \"diff\"     )->setUnits(UNIT_DIFFUSIVITY)->setLongName(\"diffusivity\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEDiffConstOrtho::FEDiffConstOrtho(FEModel* pfem) : FESoluteDiffusivity(pfem)\n{\n\tm_free_diff = 1;\n    m_diff[0] = 1;\n    m_diff[1] = 1;\n    m_diff[2] = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization. \nbool FEDiffConstOrtho::Validate()\n{\n\tif (FESoluteDiffusivity::Validate() == false) return false;\n    FEMesh& mesh = GetMesh();\n    for (int i=0; i<mesh.Elements(); ++i) {\n        FEElement& elem = *mesh.Element(i);\n        for (int n=0; n<elem.GaussPoints(); ++n) {\n            FEMaterialPoint& mp = *elem.GetMaterialPoint(n);\n            if (m_free_diff(mp) < m_diff[0](mp)) { feLogError(\"free_diff must be >= diff1 in element %i\", elem.GetID()); return false; }\n            if (m_free_diff(mp) < m_diff[1](mp)) { feLogError(\"free_diff must be >= diff2 in element %i\", elem.GetID()); return false; }\n            if (m_free_diff(mp) < m_diff[2](mp)) { feLogError(\"free_diff must be >= diff3 in element %i\", elem.GetID()); return false; }\n        }\n    }\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Free diffusivity\ndouble FEDiffConstOrtho::Free_Diffusivity(FEMaterialPoint& mp)\n{\n\treturn m_free_diff(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of free diffusivity with respect to concentration\ndouble FEDiffConstOrtho::Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Diffusivity tensor\nmat3ds FEDiffConstOrtho::Diffusivity(FEMaterialPoint& mp)\n{\n\tvec3d a0;\t\t\t\t// texture direction in reference configuration\n\tmat3ds d(0,0,0,0,0,0);\t// diffusion tensor\n\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// --- constant orthotropic diffusivity ---\n\tfor (int i=0; i<3; i++) {\t// Perform sum over all three texture directions\n\t\t\n\t\t// Copy the texture direction in the reference configuration to a0\n\t\ta0.x = Q[0][i]; a0.y = Q[1][i]; a0.z = Q[2][i];\n\t\t\n\t\t// Evaluate the texture tensor in the current configuration\n\t\td += dyad(a0)*m_diff[i](mp);\n\t}\n\t\n\treturn d;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to strain\ntens4dmm FEDiffConstOrtho::Tangent_Diffusivity_Strain(FEMaterialPoint &mp)\n{\n\ttens4dmm D;\n\tD.zero();\n\treturn D;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to concentration\nmat3ds FEDiffConstOrtho::Tangent_Diffusivity_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\tmat3ds d;\n\td.zero();\n\treturn d;\n}\n"
  },
  {
    "path": "FEBioMix/FEDiffConstOrtho.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n#include <FECore/FEMesh.h>\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a constant orthotropic diffusivity\n\nclass FEBIOMIX_API FEDiffConstOrtho :\tpublic FESoluteDiffusivity\n{\npublic:\n\t//! constructor\n\tFEDiffConstOrtho(FEModel* pfem);\n\t\n\t//! free diffusivity\n\tdouble Free_Diffusivity(FEMaterialPoint& pt) override;\n\n\t//! Tangent of free diffusivity with respect to concentration\n\tdouble Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol) override;\n\t\n\t//! diffusivity\n\tmat3ds Diffusivity(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of diffusivity with respect to strain\n\ttens4dmm Tangent_Diffusivity_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of diffusivity with respect to concentration\n\tmat3ds Tangent_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol) override;\n\t\n\t//! data checking\n\tbool Validate() override;\n\t\npublic:\n\tFEParamDouble\tm_free_diff;\t//!< free diffusivity\n    FEParamDouble\tm_diff[3];\t\t//!< principal diffusivities\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEDiffRefIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiffRefIso.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEDiffRefIso, FESoluteDiffusivity)\n\tADD_PARAMETER(m_free_diff, FE_RANGE_GREATER_OR_EQUAL(0.0), \"free_diff\")->setUnits(UNIT_DIFFUSIVITY)->setLongName(\"free diffusivity\");\n\tADD_PARAMETER(m_diff0    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"diff0\"    )->setUnits(UNIT_DIFFUSIVITY);\n\tADD_PARAMETER(m_diff1    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"diff1\"    )->setUnits(UNIT_DIFFUSIVITY);\n\tADD_PARAMETER(m_diff2    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"diff2\"    )->setUnits(UNIT_DIFFUSIVITY);\n\tADD_PARAMETER(m_M        , FE_RANGE_GREATER_OR_EQUAL(0.0), \"M\"        );\n\tADD_PARAMETER(m_alpha    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\"    );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEDiffRefIso::FEDiffRefIso(FEModel* pfem) : FESoluteDiffusivity(pfem)\n{\n\tm_free_diff = 1;\n\tm_diff0 = 1;\n\tm_diff1 = 0;\n\tm_diff2 = 0;\n\tm_M = 0;\n    m_alpha = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Free diffusivity\ndouble FEDiffRefIso::Free_Diffusivity(FEMaterialPoint& mp)\n{\n\treturn m_diff0(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of free diffusivity with respect to concentration\ndouble FEDiffRefIso::Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Diffusivity tensor.\nmat3ds FEDiffRefIso::Diffusivity(FEMaterialPoint& mp)\n{\n\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t\n\t// solid volume fraction in reference configuration\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\t\n\t// --- strain-dependent permeability ---\n\t\n\tdouble f = pow((J-phi0)/(1-phi0),m_alpha(mp))*exp(m_M(mp)*(J*J-1.0)/2.0);\n\tdouble d0 = m_diff0(mp)*f;\n\tdouble d1 = m_diff1(mp)/(J*J)*f;\n\tdouble d2 = 0.5*m_diff2(mp)/pow(J,4)*f;\n\tmat3ds dt = d0*I+d1*b+2*d2*b.sqr();\n\t\n\treturn dt;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to strain\ntens4dmm FEDiffRefIso::Tangent_Diffusivity_Strain(FEMaterialPoint &mp)\n{\n\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t\n\t// solid volume fraction in reference configuration\n\tdouble phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    double M = m_M(mp);\n    double alpha = m_alpha(mp);\n\tdouble f = pow((J-phi0)/(1-phi0),alpha)*exp(M*(J*J-1.0)/2.0);\n\tdouble d0 = m_diff0(mp)*f;\n\tdouble d1 = m_diff1(mp)/(J*J)*f;\n\tdouble d2 = 0.5*m_diff2(mp)/pow(J,4)*f;\n\tdouble D0prime = (J*J*M+(J*(alpha+1)-phi0)/(J-phi0))*d0;\n\tdouble D1prime = (J*J*M+(J*(alpha-1)+phi0)/(J-phi0))*d1;\n\tdouble D2prime = (J*J*M+(J*(alpha-3)+3*phi0)/(J-phi0))*d2;\n\tmat3ds d0hat = I*D0prime;\n\tmat3ds d1hat = I*D1prime;\n\tmat3ds d2hat = I*D2prime;\n\t\n\ttens4dmm D4 = dyad1mm(I,d0hat)-dyad4s(I)*(2*d0)\n\t+ dyad1mm(b,d1hat)\n\t+ dyad1mm(b.sqr(),d2hat)*2+dyad4s(b)*(4*d2);\n\t\n\treturn D4;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of diffusivity with respect to concentration\nmat3ds FEDiffRefIso::Tangent_Diffusivity_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\tmat3ds d;\n\td.zero();\n\treturn d;\n}\n"
  },
  {
    "path": "FEBioMix/FEDiffRefIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a strain-dependent\n// diffusivity which is isotropic in the reference state, but exhibits\n// strain-induced anisotropy, according to the constitutive relation\n// of Ateshian and Weiss (JBME 2010)\n\nclass FEBIOMIX_API FEDiffRefIso : public FESoluteDiffusivity\n{\npublic:\n\t//! constructor\n\tFEDiffRefIso(FEModel* pfem);\n\t\n\t//! free diffusivity\n\tdouble Free_Diffusivity(FEMaterialPoint& pt) override;\n\n\t//! Tangent of free diffusivity with respect to concentration\n\tdouble Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& pt, const int isol) override;\n\t\t\n\t\n\t//! diffusivity\n\tmat3ds Diffusivity(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of diffusivity with respect to strain\n\ttens4dmm Tangent_Diffusivity_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of diffusivity with respect to concentration\n\tmat3ds Tangent_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol=0) override;\n\t\npublic:\n    FEParamDouble\tm_free_diff;\t//!< free diffusivity\n    FEParamDouble\tm_diff0;\t\t//!< diffusivity for I term\n    FEParamDouble\tm_diff1;\t\t//!< diffusivity for b term\n    FEParamDouble\tm_diff2;\t\t//!< diffusivity for b^2 term\n    FEParamDouble\tm_M;\t\t\t//!< nonlinear exponential coefficient\n    FEParamDouble\tm_alpha;\t\t//!< nonlinear power exponent\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEFiberExpPowSBM.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberExpPowSBM.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n#include <FEBioMech/FEElasticMixture.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberExpPowSBM, FEElasticMaterial)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" );\n\tADD_PARAMETER(m_ksi0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi0\" )->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_rho0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rho0\" )->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_g    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma\");\n    ADD_PARAMETER(m_sbm , \"sbm\")->setEnums(\"$(sbms)\");\n\n    ADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEFiberExpPow\n//-----------------------------------------------------------------------------\n\nbool FEFiberExpPowSBM::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n    // get the parent material which must be a multiphasic material\n    FEMultiphasic* pMP = dynamic_cast<FEMultiphasic*> (GetAncestor());\n\tif (pMP == 0) {\n\t\tfeLogError(\"Parent material must be multiphasic\");\n\t\treturn false;\n\t}\n    \n    // extract the local id of the SBM whose density controls Young's modulus from the global id\n    m_lsbm = pMP->FindLocalSBMID(m_sbm);\n\tif (m_lsbm == -1) {\n\t\tfeLogError(\"Invalid value for sbm\");\n\t\treturn false;\n\t}\n    \n    FEElasticMaterial* pem = pMP->GetSolid();\n    FEElasticMixture* psm = dynamic_cast<FEElasticMixture*>(pem);\n    if (psm == nullptr) m_comp = -1;    // in case material is not a solid mixture\n    for (int i=0; i<psm->Materials(); ++i) {\n        pem = psm->GetMaterial(i);\n        if (pem == this) {\n            m_comp = i;\n            break;\n        }\n    }\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data\nFEMaterialPointData* FEFiberExpPowSBM::CreateMaterialPointData()\n{\n    return new FERemodelingMaterialPoint(new FEElasticMaterialPoint);\n}\n\n//-----------------------------------------------------------------------------\n//! update specialize material point data\nvoid FEFiberExpPowSBM::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    FERemodelingMaterialPoint* rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n    rpt->m_rhor = spt.m_sbmr[m_lsbm];\n    rpt->m_rhorp = spt.m_sbmrp[m_lsbm];\n    rpt->m_sed = StrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberExpPowSBM::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    \n    // initialize material constants\n    double rhor = spt.m_sbmr[m_lsbm];\n    double ksi = FiberModulus(mp, rhor);\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    vec3d nt;\n    double In_1, Wl;\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    mat3ds s;\n    \n    // get the material coordinate system\n    mat3d Q = GetLocalCS(mp);\n\n    // get local fiber direction\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d n0 = Q*fiber;\n    \n    // Calculate In = n0*C*n0\n    In_1 = n0*(C*n0) - 1.0;\n    \n    // only take fibers in tension into consideration\n    if (In_1 >= eps)\n    {\n        double alpha = m_alpha(mp);\n        double beta = m_beta(mp);\n        \n        // get the global spatial fiber direction in current configuration\n        nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate strain energy derivative\n        Wl = ksi*pow(In_1, beta-1.0)*exp(alpha*pow(In_1, beta));\n        \n        // calculate the fiber stress\n        s = N*(2.0*Wl/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberExpPowSBM::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    \n    // initialize material constants\n    double rhor = spt.m_sbmr[m_lsbm];\n    double ksi = FiberModulus(mp, rhor);\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    vec3d nt;\n    double In_1, Wll;\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    tens4ds c;\n    \n    // get the material coordinate system\n    mat3d Q = GetLocalCS(mp);\n\n    // get local fiber direction\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d n0 = Q*fiber;\n    \n    // Calculate In = n0*C*n0\n    In_1 = n0*(C*n0) - 1.0;\n    \n    // only take fibers in tension into consideration\n    if (In_1 >= eps)\n    {\n        double alpha = m_alpha(mp);\n        double beta = m_beta(mp);\n        \n        // get the global spatial fiber direction in current configuration\n        nt = F*n0;\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // calculate strain energy 2nd derivative\n        double tmp = alpha*pow(In_1, beta);\n        Wll = ksi*pow(In_1, beta-2.0)*((tmp+1)*beta-1.0)*exp(tmp);\n        \n        // calculate the fiber tangent\n        c = NxN*(4.0*Wll/J);\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberExpPowSBM::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = 0.0;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    \n    // initialize material constants\n    double rhor = spt.m_sbmr[m_lsbm];\n    double ksi = FiberModulus(mp, rhor);\n    \n    // loop over all integration points\n    double In_1;\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    \n    // get the material coordinate system\n    mat3d Q = GetLocalCS(mp);\n\n    // get local fiber direction\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d n0 = Q*fiber;\n    \n    // Calculate In = n0*C*n0\n    In_1 = n0*(C*n0) - 1.0;\n    \n    // only take fibers in tension into consideration\n    if (In_1 >= eps)\n    {\n        double alpha = m_alpha(mp);\n        double beta = m_beta(mp);\n        \n        // calculate strain energy derivative\n        if (alpha > 0) {\n            sed = ksi/(alpha*beta)*(exp(alpha*pow(In_1, beta))-1);\n        }\n        else\n            sed = ksi/beta*pow(In_1, beta);\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate referential mass density\ndouble FEFiberExpPowSBM::Density(FEMaterialPoint& pt)\n{\n    FERemodelingMaterialPoint* rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n    if (rpt) return rpt->m_rhor;\n    else {\n        FEElasticMixtureMaterialPoint* emp = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        if (emp) {\n            rpt = emp->GetPointData(m_comp)->ExtractData<FERemodelingMaterialPoint>();\n            if (rpt) return rpt->m_rhor;\n        }\n    }\n    return 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEFiberExpPowSBM::StrainEnergy(FEMaterialPoint& mp)\n{\n    return StrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of strain energy density with mass density\ndouble FEFiberExpPowSBM::Tangent_SE_Density(FEMaterialPoint& mp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    double rhor = spt.m_sbmr[m_lsbm];\n    return StrainEnergy(mp)*m_g(mp)/rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of stress with mass density\nmat3ds FEFiberExpPowSBM::Tangent_Stress_Density(FEMaterialPoint& mp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    double rhor = spt.m_sbmr[m_lsbm];\n    return Stress(mp)*m_g(mp)/rhor;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEFiberExpPowSBM.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include <FEBioMech/FERemodelingElasticMaterial.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Exponential-power law\n//! Fiber modulus depends on SBM content\n\nclass FEBIOMIX_API FEFiberExpPowSBM : public FEElasticMaterial, public FERemodelingInterface\n{\npublic:\n    FEFiberExpPowSBM(FEModel* pfem) : FEElasticMaterial(pfem) { m_sbm = -1; m_fiber = nullptr; }\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! Cauchy stress\n    mat3ds Stress(FEMaterialPoint& mp) override;\n    \n    // Spatial tangent\n    tens4ds Tangent(FEMaterialPoint& mp) override;\n    \n    //! Strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    //! evaluate referential mass density\n    double Density(FEMaterialPoint& pt) override;\n    \n    //! return fiber modulus\n    double FiberModulus(FEMaterialPoint& pt, double rhor) { return m_ksi0(pt)*pow(rhor/m_rho0(pt), m_g(pt));}\n    \n    //! Create material point data\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! update specialize material point data\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic: // --- remodeling interface ---\n\n    //! calculate strain energy density at material point\n    double StrainEnergy(FEMaterialPoint& pt) override;\n\n    //! calculate tangent of strain energy density with mass density\n    double Tangent_SE_Density(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent of stress with mass density\n    mat3ds Tangent_Stress_Density(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble   m_alpha;\t// coefficient of (In-1) in exponential\n    FEParamDouble   m_beta;\t\t// power of (In-1) in exponential\n    FEParamDouble   m_ksi0;\t\t// fiber modulus ksi = ksi0*(rhor/rho0)^gamma\n    FEParamDouble   m_rho0;     // rho0\n    FEParamDouble   m_g;        // gamma\n    int             m_lsbm;     //!< local id of solid-bound molecule\n\npublic:\n    FEVec3dValuator*    m_fiber;    //!< fiber orientation\n\n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEFiberPowLinearSBM.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFiberPowLinearSBM.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n#include <FEBioMech/FEElasticMixture.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEFiberPowLinearSBM, FEElasticMaterial)\n    ADD_PARAMETER(m_E0   , FE_RANGE_GREATER_OR_EQUAL(0.0), \"E0\"   )->setUnits(UNIT_PRESSURE);;\n\tADD_PARAMETER(m_lam0 , FE_RANGE_GREATER         (1.0), \"lam0\" );\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\" );\n    ADD_PARAMETER(m_rho0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rho0\" )->setUnits(UNIT_DENSITY);;\n\tADD_PARAMETER(m_g    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma\");\n    ADD_PARAMETER(m_sbm , \"sbm\")->setEnums(\"$(sbms)\");\n\n    ADD_PROPERTY(m_fiber, \"fiber\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEFiberPowLinear\n//-----------------------------------------------------------------------------\n\nbool FEFiberPowLinearSBM::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n    // get the parent material which must be a multiphasic material\n    FEMultiphasic* pMP = dynamic_cast<FEMultiphasic*> (GetAncestor());\n\tif (pMP == 0) {\n\t\tfeLogError(\"Parent material must be multiphasic\");\n\t\treturn false;\n\t}\n    \n    // extract the local id of the SBM whose density controls Young's modulus from the global id\n    m_lsbm = pMP->FindLocalSBMID(m_sbm);\n\tif (m_lsbm == -1) {\n\t\tfeLogError(\"Invalid value for sbm\");\n\t\treturn false;\n\t}\n    \n    FEElasticMaterial* pem = pMP->GetSolid();\n    FEElasticMixture* psm = dynamic_cast<FEElasticMixture*>(pem);\n    if (psm == nullptr) m_comp = -1;    // in case material is not a solid mixture\n    for (int i=0; i<psm->Materials(); ++i) {\n        pem = psm->GetMaterial(i);\n        if (pem == this) {\n            m_comp = i;\n            break;\n        }\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data\nFEMaterialPointData* FEFiberPowLinearSBM::CreateMaterialPointData()\n{\n    return new FERemodelingMaterialPoint(new FEElasticMaterialPoint);\n}\n\n//-----------------------------------------------------------------------------\n//! update specialize material point data\nvoid FEFiberPowLinearSBM::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    FERemodelingMaterialPoint* rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n    rpt->m_rhor = spt.m_sbmr[m_lsbm];\n    rpt->m_rhorp = spt.m_sbmrp[m_lsbm];\n    rpt->m_sed = StrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEFiberPowLinearSBM::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    \n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n    \n    // initialize material constants\n    double rhor = spt.m_sbmr[m_lsbm];\n    double E = FiberModulus(mp, rhor);\n    double I0 = lam0*lam0;\n    double ksi = E/4/(beta-1)*pow(I0, -3./2.)*pow(I0-1, 2-beta);\n    double b = ksi*pow(I0-1, beta-1) + E/2/sqrt(I0);\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    vec3d nt;\n    double In, sn;\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    mat3ds s;\n    \n    // get the material coordinate system\n    mat3d Q = GetLocalCS(mp);\n\n    // get local fiber direction\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d n0 = Q*fiber;\n    \n    // Calculate In\n    In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n    if (In - 1 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        nt = F*n0/sqrt(In);\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        \n        // calculate the fiber stress magnitude\n        sn = (In < I0) ?\n        2*In*ksi*pow(In-1, beta-1) :\n        2*b*In - E*sqrt(In);\n        \n        // calculate the fiber stress\n        s = N*(sn/J);\n    }\n    else\n    {\n        s.zero();\n    }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEFiberPowLinearSBM::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    \n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n    \n    // initialize material constants\n    double rhor = spt.m_sbmr[m_lsbm];\n    double E = FiberModulus(mp, rhor);\n    double I0 = lam0*lam0;\n    double ksi = E/4/(beta-1)*pow(I0, -3./2.)*pow(I0-1, 2-beta);\n    \n    // deformation gradient\n    mat3d &F = pt.m_F;\n    double J = pt.m_J;\n    \n    // loop over all integration points\n    vec3d nt;\n    double In, cn;\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    tens4ds c;\n    \n    // get the material coordinate system\n    mat3d Q = GetLocalCS(mp);\n\n    // get local fiber direction\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d n0 = Q*fiber;\n    \n    // Calculate In\n    In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n    if (In - 1 >= eps)\n    {\n        // get the global spatial fiber direction in current configuration\n        nt = F*n0/sqrt(In);\n        \n        // calculate the outer product of nt\n        mat3ds N = dyad(nt);\n        tens4ds NxN = dyad1s(N);\n        \n        // calculate modulus\n        cn = (In < I0) ?\n        4*In*In*ksi*(beta-1)*pow(In-1, beta-2) :\n        E*sqrt(In);\n        \n        // calculate the fiber tangent\n        c = NxN*(cn/J);\n    }\n    else\n    {\n        c.zero();\n    }\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEFiberPowLinearSBM::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    double sed = 0.0;\n    \n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    \n    double lam0 = m_lam0(mp);\n    double beta = m_beta(mp);\n    \n    // initialize material constants\n    double rhor = spt.m_sbmr[m_lsbm];\n    double E = FiberModulus(mp, rhor);\n    double I0 = lam0*lam0;\n    double ksi = E/4/(beta-1)*pow(I0, -3./2.)*pow(I0-1, 2-beta);\n    double b = ksi*pow(I0-1, beta-1) + E/2/sqrt(I0);\n    \n    // loop over all integration points\n    double In;\n    const double eps = 0;\n    mat3ds C = pt.RightCauchyGreen();\n    \n    // get the material coordinate system\n    mat3d Q = GetLocalCS(mp);\n\n    // get local fiber direction\n    vec3d fiber = m_fiber->unitVector(mp);\n\n    // convert to global coordinates\n    vec3d n0 = Q*fiber;\n    \n    // Calculate In = n0*C*n0\n    In = n0*(C*n0);\n    \n    // only take fibers in tension into consideration\n    if (In - 1 >= eps)\n    {\n        // calculate strain energy density\n        sed = (In < I0) ?\n        ksi/beta*pow(In-1, beta) :\n        b*(In-I0) - E*(sqrt(In)-sqrt(I0)) + ksi/beta*pow(I0-1, beta);\n    }\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate referential mass density\ndouble FEFiberPowLinearSBM::Density(FEMaterialPoint& pt)\n{\n    FERemodelingMaterialPoint* rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n    if (rpt) return rpt->m_rhor;\n    else {\n        FEElasticMixtureMaterialPoint* emp = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        if (emp) {\n            rpt = emp->GetPointData(m_comp)->ExtractData<FERemodelingMaterialPoint>();\n            if (rpt) return rpt->m_rhor;\n        }\n    }\n    return 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate strain energy density at material point\ndouble FEFiberPowLinearSBM::StrainEnergy(FEMaterialPoint& mp)\n{\n    return StrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of strain energy density with mass density\ndouble FEFiberPowLinearSBM::Tangent_SE_Density(FEMaterialPoint& mp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    double rhor = spt.m_sbmr[m_lsbm];\n    return StrainEnergy(mp)*m_g(mp)/rhor;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of stress with mass density\nmat3ds FEFiberPowLinearSBM::Tangent_Stress_Density(FEMaterialPoint& mp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    double rhor = spt.m_sbmr[m_lsbm];\n    return Stress(mp)*m_g(mp)/rhor;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEFiberPowLinearSBM.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include <FEBioMech/FERemodelingElasticMaterial.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for single fiber, tension only\n//! Power law - linear\n//! Fiber modulus depends on SBM content\n\nclass FEBIOMIX_API FEFiberPowLinearSBM : public FEElasticMaterial, public FERemodelingInterface\n{\npublic:\n    FEFiberPowLinearSBM(FEModel* pfem) : FEElasticMaterial(pfem) { m_sbm = -1; m_fiber = nullptr; }\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! Cauchy stress\n    mat3ds Stress(FEMaterialPoint& mp) override;\n    \n    // Spatial tangent\n    tens4ds Tangent(FEMaterialPoint& mp) override;\n    \n    //! Strain energy density\n    double StrainEnergyDensity(FEMaterialPoint& mp) override;\n    \n    //! evaluate referential mass density\n    double Density(FEMaterialPoint& pt) override;\n    \n    //! return fiber modulus\n    double FiberModulus(FEMaterialPoint& pt, double rhor) { return m_E0(pt)*pow(rhor/m_rho0(pt), m_g(pt));}\n    \n    //! Create material point data\n    FEMaterialPointData* CreateMaterialPointData() override;\n    \n    //! update specialize material point data\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic: // --- remodeling interface ---\n\n    //! calculate strain energy density at material point\n    double StrainEnergy(FEMaterialPoint& pt) override;\n\n    //! calculate tangent of strain energy density with mass density\n    double Tangent_SE_Density(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent of stress with mass density\n    mat3ds Tangent_Stress_Density(FEMaterialPoint& pt) override;\n\npublic:\n    FEParamDouble   m_E0;\t\t// fiber modulus E = E0*(rhor/rho0)^gamma\n    FEParamDouble   m_lam0;     // stretch ratio at end of toe region\n    FEParamDouble   m_beta;     // power law exponent in toe region\n    FEParamDouble   m_rho0;     // rho0\n    FEParamDouble   m_g;        // gamma\n    int             m_lsbm;     //!< local id of solid-bound molecule\n\npublic:\n    FEVec3dValuator*    m_fiber;    //!< fiber orientation\n\n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEFixedConcentration.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedConcentration.h\"\n#include <FECore/FEModel.h>\n\nBEGIN_FECORE_CLASS(FEFixedConcentration, FEFixedBC)\n\tADD_PARAMETER(m_dof, \"c_dof\", 0, \"$(dof_list:concentration)\")->setLongName(\"species\");\nEND_FECORE_CLASS();\n\nFEFixedConcentration::FEFixedConcentration(FEModel* fem) : FEFixedBC(fem)\n{\n\tm_dof = -1;\n}\n\nbool FEFixedConcentration::Init()\n{\n\tif (m_dof == -1) return false;\n\tSetDOFList(m_dof);\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioMix/FEFixedConcentration.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEFixedConcentration : public FEFixedBC\n{\npublic:\n\tFEFixedConcentration(FEModel* fem);\n\n\tbool Init() override;\n\nprivate:\n\tint\tm_dof;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEFixedFluidPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFixedFluidPressure.h\"\n\nBEGIN_FECORE_CLASS(FEFixedFluidPressure, FEFixedBC)\nEND_FECORE_CLASS();\n\nFEFixedFluidPressure::FEFixedFluidPressure(FEModel* fem) : FEFixedBC(fem)\n{\n}\n\nbool FEFixedFluidPressure::Init()\n{\n\tvector<int> dofs;\n\tdofs.push_back(GetDOFIndex(\"p\"));\n\tSetDOFList(dofs);\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FEBioMix/FEFixedFluidPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEFixedBC.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEFixedFluidPressure : public FEFixedBC\n{\npublic:\n\tFEFixedFluidPressure(FEModel* fem);\n\tbool Init() override;\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEFluidFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFlux.h\"\n#include \"FECore/FESolver.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FEBioMix.h\"\n#include \"FEBiphasicAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidFlux, FESurfaceLoad)\n\tADD_PARAMETER(m_flux    , \"flux\"   );\n\tADD_PARAMETER(m_blinear , \"linear\" );\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\n\tADD_PARAMETER(m_bmixture, \"mixture\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFluidFlux::FEFluidFlux(FEModel* pfem) : FESurfaceLoad(pfem), m_dofP(pfem), m_dofU(pfem), m_dofV(pfem)\n{ \n\tm_blinear = false; \n\tm_bmixture = false;\n    m_bshellb = false;\n\tm_flux = 1.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFlux::SetSurface(FESurface* ps) \n{ \n\tFESurfaceLoad::SetSurface(ps);\n\tm_flux.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\nbool FEFluidFlux::Init()\n{\n\tFEModel* fem = GetFEModel();\n\n\t// get the degrees of freedom\n\tif (m_bshellb == false)\n\t{\n\t\tm_dofU.AddDof(fem->GetDOFIndex(\"x\"));\n\t\tm_dofU.AddDof(fem->GetDOFIndex(\"y\"));\n\t\tm_dofU.AddDof(fem->GetDOFIndex(\"z\"));\n\n\t\tm_dofV.AddDof(fem->GetDOFIndex(\"vx\"));\n\t\tm_dofV.AddDof(fem->GetDOFIndex(\"vy\"));\n\t\tm_dofV.AddDof(fem->GetDOFIndex(\"vz\"));\n\n\t\tm_dofP.AddDof(fem->GetDOFIndex(\"p\"));\n\t}\n\telse\n\t{\n\t\tm_dofU.AddDof(fem->GetDOFIndex(\"sx\"));\n\t\tm_dofU.AddDof(fem->GetDOFIndex(\"sy\"));\n\t\tm_dofU.AddDof(fem->GetDOFIndex(\"sz\"));\n\n\t\tm_dofV.AddDof(fem->GetDOFIndex(\"svx\"));\n\t\tm_dofV.AddDof(fem->GetDOFIndex(\"svy\"));\n\t\tm_dofV.AddDof(fem->GetDOFIndex(\"svz\"));\n\n\t\tm_dofP.AddDof(fem->GetDOFIndex(\"q\"));\n\t}\n\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFlux::Serialize(DumpStream& ar)\n{\n\tFESurfaceLoad::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_dofU & m_dofV & m_dofP;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvec3d FEFluidFlux::SolidVelocity(FESurfaceMaterialPoint& pt)\n{\n\tFESurfaceElement& el = *pt.SurfaceElement();\n\tint n = pt.m_index;\n\n\tvec3d vt[FEElement::MAX_NODES];\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n\t{\n\t\tFENode& nd = m_psurf->GetMesh()->Node(el.m_node[i]);\n\t\tvt[i] = nd.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t}\n\n\t// shape functions at integration point n\n\tdouble* N = el.H(n);\n\n\t// solid velocity at integration point\n\tvec3d vr(0, 0, 0);\n\tfor (int i = 0; i<neln; ++i)\n\t{\n\t\tvr += vt[i] * N[i];\n\t}\n\n\treturn vr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFlux::LoadVector(FEGlobalVector& R)\n{\n\tFEModel* fem = GetFEModel();\n\n\t// only add the mixture term for transient analysis and when the m_bmixture flag is true\n\tbool bmixture = m_bmixture;\n\tif (fem->GetCurrentStep()->m_nanalysis == FEBiphasicAnalysis::STEADY_STATE) bmixture = false;\n\n\t// get time increment\n\tdouble dt = CurrentTimeIncrement();\n\n\t// integrate over surface\n\tFEFluidFlux* flux = this;\n\tm_psurf->LoadVector(R, m_dofP, m_blinear, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, std::vector<double>& fa) {\n\n\t\t// fluid flux\n\t\tdouble wr = flux->m_flux(mp);\n\t\tif (flux->m_bshellb) wr = -wr;\n\n\t\t// surface normal\n\t\tvec3d dxt = mp.dxr ^ mp.dxs;\n\n\t\t// volumetric flow rate\n\t\tdouble f = dxt.norm()*wr;\n\n\t\t// add optional mixture term\n\t\tif (bmixture)\n\t\t{\n\t\t\t// solid velocity at integration point\n\t\t\tvec3d vr = flux->SolidVelocity(mp);\n\t\t\tf -= vr*dxt;\n\t\t}\n\n\t\t// put it all together\n\t\tdouble H_p = dof_a.shape;\n\t\tfa[0] = H_p * f * dt;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFluidFlux::StiffnessMatrix(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\tdouble dt = CurrentTimeIncrement();\n\n\tFEFluidFlux* flux = this;\n\tbool btransient = (fem.GetCurrentStep()->m_nanalysis != FEBiphasicAnalysis::STEADY_STATE);\n\n\tif (!m_blinear || m_bmixture)\n\t{\n\t\tm_psurf->LoadStiffness(LS, m_dofP, m_dofU, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\t\t// fluid flux\n\t\t\tdouble wr = flux->m_flux(mp);\n\t\t\tif (m_bshellb) wr = -wr;\n\n\t\t\t// calculate surface normal\n\t\t\tvec3d dxt = mp.dxr ^ mp.dxs;\n\n\t\t\t// shape functions and derivatives at integration point\n\t\t\tdouble N_p = dof_a.shape;\n\n\t\t\tdouble N_u = dof_b.shape;\n\t\t\tdouble Gr_u = dof_b.shape_deriv_r;\n\t\t\tdouble Gs_u = dof_b.shape_deriv_s;\n\n\t\t\t// calculate stiffness component\n\t\t\tint i = dof_a.index;\n\t\t\tint j = dof_b.index;\n\n\t\t\tvec3d kab(0, 0, 0);\n\t\t\tif (flux->m_bmixture == false)\n\t\t\t{\n\t\t\t\tvec3d t1 = (dxt / dxt.norm())*wr;\n\t\t\t\tvec3d t2 = mp.dxs*Gr_u - mp.dxr*Gs_u;\n\t\t\t\tkab = (t1^t2)*dt*N_p;\n\t\t\t}\n\t\t\telse if (btransient)\n\t\t\t{\n\t\t\t\tkab = (dxt*N_u)*N_p;\n\t\t\t}\n\n\t\t\tKab[0][0] += kab.x;\n\t\t\tKab[0][1] += kab.y;\n\t\t\tKab[0][2] += kab.z;\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEFluidFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This boundary condition sustains a fluid flux on a surface\n//!\nclass FEBIOMIX_API FEFluidFlux : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n\tFEFluidFlux(FEModel* pfem);\n\n\tvoid SetLinear(bool blinear) { m_blinear = blinear; }\n\n\tvoid SetMixture(bool bmix) { m_bmixture = bmix; }\n\n\t// initialization\n\tbool Init() override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Set the surface to apply the load to\n\tvoid SetSurface(FESurface* ps) override;\n\n\t//! calculate flux stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\nprivate:\n\t// evaluate solid velocity at integration point\n\tvec3d SolidVelocity(FESurfaceMaterialPoint& pt);\n\nprotected:\n\tFEParamDouble\tm_flux;\t\t\t//!< fluid flux\n\tbool\tm_bmixture;\t\t//!< mixture velocity or relative fluid flux\n    bool    m_bshellb;      //!< flag for prescribing flux on shell bottom\n\tbool\tm_blinear;\t\t//!< type (linear or nonlinear)\n\n\t// degrees of freedom\n\tFEDofList\tm_dofP;\t\t// pressure\t\n\tFEDofList\tm_dofU;\t\t// displacement\n\tFEDofList\tm_dofV;\t\t// velocity\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEHydraulicPermeability.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEHydraulicPermeability.h\"\n\n//-----------------------------------------------------------------------------\n// Derivative of permeability w.r.t. solute concentration at material point\n// Set this to zero by default because poroelasticity problems do not require it\nmat3ds FEHydraulicPermeability::Tangent_Permeability_Concentration(FEMaterialPoint& pt, const int isol)\n{\n\treturn mat3ds(0,0,0,0,0,0);\n}\n"
  },
  {
    "path": "FEBioMix/FEHydraulicPermeability.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/tens4d.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for hydraulic permeability of porous materials.\n//! These materials need to define the permeability and tangent permeability functions.\n//!\nclass FEBIOMIX_API FEHydraulicPermeability : public FEMaterialProperty\n{\npublic:\n\tFEHydraulicPermeability(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\tvirtual ~FEHydraulicPermeability(){}\n    \n\t//! hydraulic permeability\n\tvirtual mat3ds Permeability(FEMaterialPoint& pt) = 0;\n    \n\t//! tangent of hydraulic permeability with respect to strain\n\tvirtual tens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) = 0;\n    \n\t//! tangent of hydraulic permeability with respect to concentration\n\tmat3ds Tangent_Permeability_Concentration(FEMaterialPoint& mp, const int isol);\n\n\tFECORE_BASE_CLASS(FEHydraulicPermeability);\n};\n"
  },
  {
    "path": "FEBioMix/FEInitialConcentration.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialConcentration.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialConcentration, FEInitialCondition)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:concentration)\")->setLongName(\"Solute\");\n\tADD_PARAMETER(m_data, \"value\")->setUnits(UNIT_CONCENTRATION);\nEND_FECORE_CLASS();\n\nFEInitialConcentration::FEInitialConcentration(FEModel* fem) : FEInitialDOF(fem)\n{\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialShellConcentration, FEInitialCondition)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:shell_concentration)\")->setLongName(\"Solute\");\n\tADD_PARAMETER(m_data, \"value\");\nEND_FECORE_CLASS();\n\nFEInitialShellConcentration::FEInitialShellConcentration(FEModel* fem) : FEInitialDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioMix/FEInitialConcentration.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialConcentration : public FEInitialDOF\n{\npublic:\n\tFEInitialConcentration(FEModel* fem);\n\tDECLARE_FECORE_CLASS();\n};\n\n\nclass FEInitialShellConcentration : public FEInitialDOF\n{\npublic:\n\tFEInitialShellConcentration(FEModel* fem);\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEInitialEffectiveFluidPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEInitialEffectiveFluidPressure.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialEffectiveFluidPressure, FEInitialCondition)\n\tADD_PARAMETER(m_data, \"value\");\nEND_FECORE_CLASS();\n\nFEInitialEffectiveFluidPressure::FEInitialEffectiveFluidPressure(FEModel* fem) : FEInitialDOF(fem)\n{\n}\n\nbool FEInitialEffectiveFluidPressure::Init()\n{\n\tif (SetDOF(\"p\") == false) return false;\n\treturn FEInitialDOF::Init();\n}\n\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEInitialShellEffectiveFluidPressure, FEInitialCondition)\n\tADD_PARAMETER(m_data, \"value\");\nEND_FECORE_CLASS();\n\nFEInitialShellEffectiveFluidPressure::FEInitialShellEffectiveFluidPressure(FEModel* fem) : FEInitialDOF(fem)\n{\n}\n\nbool FEInitialShellEffectiveFluidPressure::Init()\n{\n\tif (SetDOF(\"q\") == false) return false;\n\treturn FEInitialDOF::Init();\n}\n"
  },
  {
    "path": "FEBioMix/FEInitialEffectiveFluidPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEInitialCondition.h>\n\nclass FEInitialEffectiveFluidPressure : public FEInitialDOF\n{\npublic:\n\tFEInitialEffectiveFluidPressure(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEInitialShellEffectiveFluidPressure : public FEInitialDOF\n{\npublic:\n\tFEInitialShellEffectiveFluidPressure(FEModel* fem);\n\tbool Init() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMassActionForward.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMassActionForward.h\"\n#include \"FESoluteInterface.h\"\n#include \"FEBiphasic.h\"\n\nBEGIN_FECORE_CLASS(FEMassActionForward, FEChemicalReaction)\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMassActionForward::FEMassActionForward(FEModel* pfem) : FEChemicalReaction(pfem) \n{\n    // set material properties\n    ADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionForward::ReactionSupply(FEMaterialPoint& pt)\n{\n    // get reaction rate\n    double kF = m_pFwd->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = kF;\n    \n    // start with contribution from solutes\n\tint nsol = m_psm->Solutes();\n\tfor (int i = 0; i < nsol; ++i) {\n\t\tint vR = m_vR[i];\n\t\tif (vR > 0) {\n\t\t\tdouble c = m_psm->GetActualSoluteConcentration(pt, i);\n\t\t\tzhat *= pow(c, vR);\n\t\t}\n\t}\n\n\t// add contribution of solid-bound molecules\n\tconst int nsbm = m_psm->SBMs();\n\tfor (int i = 0; i < nsbm; ++i) {\n\t\tint vR = m_vR[nsol + i];\n\t\tif (vR > 0) {\n\t\t\tdouble c = m_psm->SBMConcentration(pt, i);\n\t\t\tzhat *= pow(c, vR);\n\t\t}\n\t}\n    \n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\nmat3ds FEMassActionForward::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n\tconst int nsol = m_nsol;\n\tconst int nsbm = (int)m_v.size() - nsol;\n\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    \n \tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\n    double J = ept.m_J;\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(pt);\n        \n    double kF = m_pFwd->ReactionRate(pt);\n    mat3ds dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n    double zhat = ReactionSupply(pt);\n    mat3ds dzhatde = mat3dd(0);\n    if (kF > 0) {\n        dzhatde += dkFde/kF;\n    }\n    mat3ds I = mat3dd(1);\n    for (int isol = 0; isol < nsol; ++isol)\n    {\n        double dkdJ = m_psm->dkdJ(pt, isol);\n        double k = m_psm->GetPartitionCoefficient(pt, isol);\n        dzhatde += I * (m_vR[isol] * dkdJ / k);\n    }\n\n    for (int isbm = 0; isbm<nsbm; ++isbm)\n        dzhatde -= I*(m_vR[nsol+isbm]/(J-phi0));\n        \n    dzhatde *= zhat;\n        \n    return dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMassActionForward::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n\tdouble kF = m_pFwd->ReactionRate(pt);\n\tdouble dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n\tdouble zhat = ReactionSupply(pt);\n\tdouble dzhatdp = 0;\n\tif (kF > 0) {\n\t\tdzhatdp = dkFdp*zhat/kF;\n\t}\n\treturn dzhatdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMassActionForward::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n    const int nsol = m_nsol;\n    \n    // if the derivative is taken with respect to a solid-bound molecule, return 0\n    if (sol >= nsol) {\n        return 0;\n    }\n    \n    double zhat = ReactionSupply(pt);\n    double dzhatdc = 0;\n\tfor (int isol = 0; isol < nsol; ++isol) \n\t{\n\t\tdouble dkdc = m_psm->dkdc(pt, isol, sol);\n\t\tdouble k = m_psm->GetPartitionCoefficient(pt, isol);\n\t\tdouble c = m_psm->GetEffectiveSoluteConcentration(pt, sol);\n\t\tdzhatdc += m_vR[isol]*dkdc/k;\n        if ((isol == sol) && (c > 0))\n            dzhatdc += m_vR[isol]/c;\n    }\n    \n    dzhatdc *= zhat;\n    \n    return dzhatdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMassActionForward.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Law of mass action for forward chemical reaction.\nclass FEBIOMIX_API FEMassActionForward : public FEChemicalReaction\n{\npublic:\n\t//! constructor\n\tFEMassActionForward(FEModel* pfem);\n\t\t\n\t//! molar supply at material point\n\tdouble ReactionSupply(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with strain (J) at material point\n\tmat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective pressure at material point\n\tdouble Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective concentration at material point\n\tdouble Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMassActionForwardEffective.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMassActionForwardEffective.h\"\n#include \"FESoluteInterface.h\"\n#include \"FEBiphasic.h\"\n\nBEGIN_FECORE_CLASS(FEMassActionForwardEffective, FEChemicalReaction)\n    // set material properties\n    ADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//! constructor\n//-----------------------------------------------------------------------------\nFEMassActionForwardEffective::FEMassActionForwardEffective(FEModel* pfem) : FEChemicalReaction(pfem) \n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionForwardEffective::ReactionSupply(FEMaterialPoint& pt)\n{\n    // get reaction rate\n    double kF = m_pFwd->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = kF;\n    \n    // start with contribution from solutes\n    int nsol = m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vR = m_vR[i];\n        if (vR > 0) {\n            double c = m_psm->GetEffectiveSoluteConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    // add contribution of solid-bound molecules\n    const int nsbm = m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vR = m_vR[nsol+i];\n        if (vR > 0) {\n            double c = m_psm->SBMConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\nmat3ds FEMassActionForwardEffective::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double J = ept.m_J;\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(pt);\n        \n    double kF = m_pFwd->ReactionRate(pt);\n    mat3ds dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n    double zhat = ReactionSupply(pt);\n    mat3ds dzhatde = mat3dd(0);\n    if (kF > 0) dzhatde = dkFde*(zhat/kF);\n        \n    return dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMassActionForwardEffective::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n    double zhat = ReactionSupply(pt);\n    double dzhatdp = 0;\n    if (kF > 0) dzhatdp = dkFdp*zhat/kF;\n    return dzhatdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMassActionForwardEffective::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n    const int nsol = m_nsol;\n    \n    // if the derivative is taken with respect to a solid-bound molecule, return 0\n    if (sol >= nsol) return 0;\n    \n    double zhat = ReactionSupply(pt);\n    double dzhatdc = 0;\n    double c = m_psm->GetEffectiveSoluteConcentration(pt, sol);\n    if ((zhat > 0) && (c > 0)) dzhatdc = m_vR[sol]/c*zhat;\n    \n    return dzhatdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMassActionForwardEffective.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Law of mass action for forward chemical reaction, using effective concentrations\nclass FEBIOMIX_API FEMassActionForwardEffective : public FEChemicalReaction\n{\npublic:\n    //! constructor\n    FEMassActionForwardEffective(FEModel* pfem);\n    \n    //! molar supply at material point\n    double ReactionSupply(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with strain (J) at material point\n    mat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with effective pressure at material point\n    double Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with effective concentration at material point\n    double Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMassActionReversible.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMassActionReversible.h\"\n#include \"FESoluteInterface.h\"\n#include \"FEBiphasic.h\"\n\nBEGIN_FECORE_CLASS(FEMassActionReversible, FEChemicalReaction)\n    // set material properties\n    ADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\n    ADD_PROPERTY(m_pRev, \"reverse_rate\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMassActionReversible::FEMassActionReversible(FEModel* pfem) : FEChemicalReaction(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionReversible::FwdReactionSupply(FEMaterialPoint& pt)\n{\n    // get forward reaction rate\n    double k = m_pFwd->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = k;\n    \n    // start with contribution from solutes\n    int nsol = (int) m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vR = m_vR[i];\n        if (vR > 0) {\n            double c = m_psm->GetActualSoluteConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n\n    // add contribution of solid-bound molecules\n    const int nsbm = (int) m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vR = m_vR[nsol+i];\n        if (vR > 0) {\n            double c = m_psm->SBMConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionReversible::RevReactionSupply(FEMaterialPoint& pt)\n{\n    // get forward reaction rate\n    double k = m_pRev->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = k;\n    \n    // start with contribution from solutes\n    int nsol = m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vP = m_vP[i];\n        if (vP > 0) {\n            double c = m_psm->GetActualSoluteConcentration(pt, i);\n            zhat *= pow(c, vP);\n        }\n    }\n\n    // add contribution of solid-bound molecules\n    const int nsbm = m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vP = m_vP[nsol+i];\n        if (vP > 0) {\n            double c = m_psm->SBMConcentration(pt, i);\n            zhat *= pow(c, vP);\n        }\n    }\n    \n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionReversible::ReactionSupply(FEMaterialPoint& pt)\n{\n\tdouble zhatF = FwdReactionSupply(pt);\n\tdouble zhatR = RevReactionSupply(pt);\n\treturn zhatF - zhatR;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\nmat3ds FEMassActionReversible::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\t\n\tconst int nsol = m_nsol;\n\tconst int nsbm = (int)m_v.size() - nsol;\n    double J = ept.m_J;\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(pt);\n\t\n\t// forward reaction\n\tdouble kF = m_pFwd->ReactionRate(pt);\n\tmat3ds dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n\tdouble zhatF = FwdReactionSupply(pt);\n\tmat3ds dzhatFde = mat3dd(0);\n\tif (kF > 0) {\n\t\tdzhatFde += dkFde/kF;\n\t}\n\tmat3ds I = mat3dd(1);\n\tfor (int isol=0; isol<nsol; ++isol)\n    {\n        double dkdJ = m_psm->dkdJ(pt, isol);\n        double k = m_psm->GetPartitionCoefficient(pt, isol);\n        dzhatFde += I*(m_vR[isol]*dkdJ/k);\n    }\n\tfor (int isbm = 0; isbm<nsbm; ++isbm)\n\t\tdzhatFde += I*(m_vR[nsol+isbm]/(J-phi0));\n\t\n\tdzhatFde *= zhatF;\n\t\n\t// reverse reaction\n\tdouble kR = m_pRev->ReactionRate(pt);\n\tmat3ds dkRde = m_pRev->Tangent_ReactionRate_Strain(pt);\n\tdouble zhatR = RevReactionSupply(pt);\n\tmat3ds dzhatRde = mat3dd(0);\n\tif (kR > 0) {\n\t\tdzhatRde += dkRde/kR;\n\t}\n\tfor (int isol=0; isol<nsol; ++isol)\n    {\n        double dkdJ = m_psm->dkdJ(pt, isol);\n        double k = m_psm->GetPartitionCoefficient(pt, isol);\n        dzhatRde += I*(m_vP[isol]*dkdJ/k);\n    }\n\tfor (int isbm = 0; isbm<nsbm; ++isbm)\n\t\tdzhatRde -= I*(m_vP[nsol+isbm]/(J-phi0));\n\t\n\tdzhatRde *= zhatR;\n\t\n\treturn dzhatFde - dzhatRde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMassActionReversible::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n\t// forward reaction\n\tdouble kF = m_pFwd->ReactionRate(pt);\n\tdouble dzhatFdp = 0;\n\tif (kF > 0) {\n\t\tdouble dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n\t\tdouble zhatF = FwdReactionSupply(pt);\n\t\tdzhatFdp = dkFdp*zhatF/kF;\n\t}\n\t\n\t// reverse reaction\n\tdouble kR = m_pRev->ReactionRate(pt);\n\tdouble dzhatRdp = 0;\n\tif (kR > 0) {\n\t\tdouble dkRdp = m_pRev->Tangent_ReactionRate_Pressure(pt);\n\t\tdouble zhatR = RevReactionSupply(pt);\n\t\tdzhatRdp = dkRdp*zhatR/kR;\n\t}\n\t\n\treturn dzhatFdp - dzhatRdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMassActionReversible::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n    const int nsol = m_nsol;\n    \n    // if the derivative is taken with respect to a solid-bound molecule, return 0\n    if (sol >= nsol) {\n        return 0;\n    }\n    \n    // forward reaction\n    double zhatF = FwdReactionSupply(pt);\n    double dzhatFdc = 0;\n    for (int isol=0; isol<nsol; ++isol) {\n        double dkdc = m_psm->dkdc(pt, isol, sol);\n        double k = m_psm->GetPartitionCoefficient(pt, isol);\n        double c = m_psm->GetEffectiveSoluteConcentration(pt, sol);\n        dzhatFdc += m_vR[isol]*dkdc/k;\n        if ((isol == sol) && (c > 0))\n            dzhatFdc += m_vR[isol]/c;\n    }\n    \n    dzhatFdc *= zhatF;\n    \n    // reverse reaction\n    double zhatR = RevReactionSupply(pt);\n    double dzhatRdc = 0;\n    for (int isol=0; isol<nsol; ++isol) {\n        double dkdc = m_psm->dkdc(pt, isol, sol);\n        double k = m_psm->GetPartitionCoefficient(pt, isol);\n        double c = m_psm->GetEffectiveSoluteConcentration(pt, sol);\n\n        dzhatRdc += m_vP[isol]*dkdc/k;\n        if ((isol == sol) && (c > 0))\n            dzhatRdc += m_vP[isol]/c;\n    }\n    \n    dzhatRdc *= zhatR;\n    \n    return dzhatFdc - dzhatRdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMassActionReversible.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Law of mass action for reversible chemical reaction.\nclass FEBIOMIX_API FEMassActionReversible : public FEChemicalReaction\n{\npublic:\n\t//! constructor\n\tFEMassActionReversible(FEModel* pfem);\n\t\t\n\t//! molar supply at material point\n\tdouble ReactionSupply(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with strain (J) at material point\n\tmat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective pressure at material point\n\tdouble Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective concentration at material point\n\tdouble Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n\t\n\t//! molar supply at material point\n\tdouble FwdReactionSupply(FEMaterialPoint& pt);\n\t\n\t//! molar supply at material point\n\tdouble RevReactionSupply(FEMaterialPoint& pt);\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMix/FEMassActionReversibleEffective.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMassActionReversibleEffective.h\"\n#include \"FESoluteInterface.h\"\n#include \"FEBiphasic.h\"\n\nBEGIN_FECORE_CLASS(FEMassActionReversibleEffective, FEChemicalReaction)\n    // set material properties\n    ADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\n    ADD_PROPERTY(m_pRev, \"reverse_rate\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMassActionReversibleEffective::FEMassActionReversibleEffective(FEModel* pfem) : FEChemicalReaction(pfem) \n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionReversibleEffective::FwdReactionSupply(FEMaterialPoint& pt)\n{\n    // get forward reaction rate\n    double k = m_pFwd->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = k;\n    \n    // start with contribution from solutes\n    int nsol = 0;\n    nsol = m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vR = m_vR[i];\n        if (vR > 0) {\n            double c = m_psm->GetEffectiveSoluteConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    // add contribution of solid-bound molecules\n    const int nsbm = m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vR = m_vR[nsol+i];\n        if (vR > 0) {\n            double c = m_psm->SBMConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionReversibleEffective::RevReactionSupply(FEMaterialPoint& pt)\n{\n    // get forward reaction rate\n    double k = m_pRev->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = k;\n    \n    // start with contribution from solutes\n    int nsol = m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vP = m_vP[i];\n        if (vP > 0) {\n            double c = m_psm->GetEffectiveSoluteConcentration(pt, i);\n            zhat *= pow(c, vP);\n        }\n    }\n    \n    // add contribution of solid-bound molecules\n    const int nsbm = m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vP = m_vP[nsol+i];\n        if (vP > 0) {\n            double c = m_psm->SBMConcentration(pt, i);\n            zhat *= pow(c, vP);\n        }\n    }\n    \n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMassActionReversibleEffective::ReactionSupply(FEMaterialPoint& pt)\n{\n    double zhatF = FwdReactionSupply(pt);\n    double zhatR = RevReactionSupply(pt);\n    return zhatF - zhatR;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\nmat3ds FEMassActionReversibleEffective::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    const int nsol = m_nsol;\n    const int nsbm = (int)m_v.size() - nsol;\n    double J = ept.m_J;\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(pt);\n    \n    // forward reaction\n    double kF = m_pFwd->ReactionRate(pt);\n    mat3ds dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n    double zhatF = FwdReactionSupply(pt);\n    mat3ds dzhatFde = mat3dd(0);\n    if (kF > 0) dzhatFde = dkFde*(zhatF/kF);\n    \n    // reverse reaction\n    double kR = m_pRev->ReactionRate(pt);\n    mat3ds dkRde = m_pRev->Tangent_ReactionRate_Strain(pt);\n    double zhatR = RevReactionSupply(pt);\n    mat3ds dzhatRde = mat3dd(0);\n    if (kR > 0) dzhatRde = dkRde*(zhatR/kR);\n    \n    return dzhatFde - dzhatRde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMassActionReversibleEffective::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n    // forward reaction\n    double kF = m_pFwd->ReactionRate(pt);\n    double dzhatFdp = 0;\n    if (kF > 0) {\n        double dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n        double zhatF = FwdReactionSupply(pt);\n        dzhatFdp = dkFdp*zhatF/kF;\n    }\n    \n    // reverse reaction\n    double kR = m_pRev->ReactionRate(pt);\n    double dzhatRdp = 0;\n    if (kR > 0) {\n        double dkRdp = m_pRev->Tangent_ReactionRate_Pressure(pt);\n        double zhatR = RevReactionSupply(pt);\n        dzhatRdp = dkRdp*zhatR/kR;\n    }\n    \n    return dzhatFdp - dzhatRdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMassActionReversibleEffective::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n    const int nsol = m_nsol;\n    \n    // if the derivative is taken with respect to a solid-bound molecule, return 0\n    if (sol >= nsol) {\n        return 0;\n    }\n    \n    // forward reaction\n    double zhatF = FwdReactionSupply(pt);\n    double dzhatFdc = 0;\n    double c = m_psm->GetEffectiveSoluteConcentration(pt, sol);\n    if ((zhatF > 0) && (c > 0)) dzhatFdc = m_vR[sol]*zhatF/c;\n    \n    // reverse reaction\n    double zhatR = RevReactionSupply(pt);\n    double dzhatRdc = 0;\n    if ((zhatR > 0) && (c > 0)) dzhatRdc = m_vP[sol]*zhatR/c;\n\n    return dzhatFdc - dzhatRdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMassActionReversibleEffective.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Law of mass action for reversible chemical reaction\n//! using effective concentrations.\nclass FEBIOMIX_API FEMassActionReversibleEffective : public FEChemicalReaction\n{\npublic:\n    //! constructor\n    FEMassActionReversibleEffective(FEModel* pfem);\n    \n    //! molar supply at material point\n    double ReactionSupply(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with strain (J) at material point\n    mat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with effective pressure at material point\n    double Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with effective concentration at material point\n    double Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n    \n    //! molar supply at material point\n    double FwdReactionSupply(FEMaterialPoint& pt);\n    \n    //! molar supply at material point\n    double RevReactionSupply(FEMaterialPoint& pt);\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMatchingOsmoticCoefficientBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMatchingOsmoticCoefficientBC.h\"\n#include \"FEBioMix.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMatchingOsmoticCoefficientBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_ambp , \"ambient_pressure\");\n    ADD_PARAMETER(m_ambc , \"ambient_osmolarity\");\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMatchingOsmoticCoefficientBC::FEMatchingOsmoticCoefficientBC(FEModel* pfem) : FEPrescribedSurface(pfem)\n{\n    m_ambp = m_ambc = 0.0;\n    m_bshellb = false;\n    \n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    m_dofQ = (pfem ? pfem->GetDOFIndex(\"q\") : -1);\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEMatchingOsmoticCoefficientBC::Activate()\n{\n    FESurface* ps = GetSurface();\n    \n    FEModel* fem = GetFEModel();\n\n    for (int i=0; i<ps->Elements(); ++i) {\n        FESurfaceElement& el = ps->Element(i);\n        // get the element connected to this surface\n        FEElement* elem = el.m_elem[0].pe;\n        FEMaterial* pm = fem->GetMaterial(elem->GetMatID());\n        // get the multihasic material for this element\n        FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pm);\n        // if the material is multiphasic, set prescribable fluid dofs\n        if (pmp) {\n            for (int j=0; j<el.Nodes(); ++j) {\n                FENode& node = ps->Node(el.m_lnode[j]);\n                // mark node as having prescribed DOF\n                if (!m_bshellb) node.set_bc(m_dofP, DOF_PRESCRIBED);\n                else node.set_bc(m_dofQ, DOF_PRESCRIBED);\n            }\n        }\n    }\n\n    // NOTE: Hmmm, not sure about this one. Maybe skip immediate base class?\n//    FEPrescribedSurface::Activate();\n    FESurfaceBC::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the ambient effective fluid pressure, using the multiphasic osmotic coefficient\nvoid FEMatchingOsmoticCoefficientBC::Update()\n{\n    FESurface* ps = GetSurface();\n    \n    FEModel* fem = GetFEModel();\n    \n    vector<double> pn(ps->Nodes(),0);\n    vector<int> cnt(ps->Nodes(),0);\n\n    for (int i=0; i<ps->Elements(); ++i) {\n        FESurfaceElement& el = ps->Element(i);\n        // get the element connected to this surface\n        FEElement* elem = el.m_elem[0].pe;\n        FEMaterial* pm = fem->GetMaterial(elem->GetMatID());\n        // get the multihasic material for this element\n        FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pm);\n        double Phi = 0;\n        if (pmp) {\n            // get average osmotic coefficient in this multiphasic element\n            int nint = elem->GaussPoints();\n            for (int n=0; n<nint; ++n) {\n                FEMaterialPoint& mp = *elem->GetMaterialPoint(n);\n                Phi += pmp->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n            }\n            Phi /= nint;\n            // evaluate effective fluid pressure on the boundary\n            double pe = m_ambp - pmp->m_Rgas*pmp->m_Tabs*Phi*m_ambc;\n            // project to nodes of that face\n            for (int j=0; j<el.Nodes(); ++j) {\n                int n = el.m_lnode[j];\n                pn[n] += pe;\n                ++cnt[n];\n            }\n        }\n    }\n\n    // prescribe the projected nodal values of the effective fluid pressure\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofP] < -1)\n        {\n            assert(cnt[i]);\n            FENode& node = ps->Node(i);\n            // prescribe effective pressure at this node\n            double pe = pn[i]/cnt[i];\n            if (!m_bshellb) node.set(m_dofP, pe);\n            else node.set(m_dofQ, pe);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMatchingOsmoticCoefficientBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMatchingOsmoticCoefficientBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMatchingOsmoticCoefficientBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    if (ar.IsShallow() == false)\n    {\n        ar& m_dofP;\n        ar& m_dofQ;\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FEMatchingOsmoticCoefficientBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include \"FEMultiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! FEMatchingOsmoticCoefficientBC is a surface boundary condition that imposes the same osmotic\n//! coefficient in the bath as in the multiphasic material bounded by tha surface\nclass FEBIOMIX_API FEMatchingOsmoticCoefficientBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEMatchingOsmoticCoefficientBC(FEModel* pfem);\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! activate\n    void Activate() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    double          m_ambc;     //!<ambient osmolarity\n    double          m_ambp;     //!< ambient fluid pressure\n    bool            m_bshellb;  //!< shell bottom flag\nprivate:\n    int             m_dofP, m_dofQ;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMatchingOsmoticCoefficientLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMatchingOsmoticCoefficientLoad.h\"\n#include \"FEBioMix.h\"\n#include <FECore/FEModel.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMatchingOsmoticCoefficientLoad, FESurfaceLoad)\n    ADD_PARAMETER(m_ambp, \"ambient_pressure\");\n    ADD_PARAMETER(m_ambc, \"ambient_osmolarity\");\n    ADD_PARAMETER(m_bshellb, \"shell_bottom\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMatchingOsmoticCoefficientLoad::FEMatchingOsmoticCoefficientLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_ambp = m_ambc = 0.0;\n    m_bshellb = false;\n\n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    m_dofQ = (pfem ? pfem->GetDOFIndex(\"q\") : -1);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEMatchingOsmoticCoefficientLoad::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEMatchingOsmoticCoefficientLoad::Activate()\n{\n    FESurface* ps = &GetSurface();\n\n    FEModel* fem = GetFEModel();\n\n    for (int i = 0; i < ps->Elements(); ++i) {\n        FESurfaceElement& el = ps->Element(i);\n        // get the element connected to this surface\n        FEElement* elem = el.m_elem[0].pe;\n        FEMaterial* pm = fem->GetMaterial(elem->GetMatID());\n        // get the multihasic material for this element\n        FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pm);\n        // if the material is multiphasic, set prescribable fluid dofs\n        if (pmp) {\n            for (int j = 0; j < el.Nodes(); ++j) {\n                FENode& node = ps->Node(el.m_lnode[j]);\n                // mark node as having prescribed DOF\n                if (!m_bshellb) node.set_bc(m_dofP, DOF_PRESCRIBED);\n                else node.set_bc(m_dofQ, DOF_PRESCRIBED);\n            }\n        }\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the ambient effective fluid pressure, using the multiphasic osmotic coefficient\nvoid FEMatchingOsmoticCoefficientLoad::Update()\n{\n    FESurface* ps = &GetSurface();\n\n    FEModel* fem = GetFEModel();\n\n    vector<double> pn(ps->Nodes(), 0);\n    vector<int> cnt(ps->Nodes(), 0);\n\n    for (int i = 0; i < ps->Elements(); ++i) {\n        FESurfaceElement& el = ps->Element(i);\n        // get the element connected to this surface\n        FEElement* elem = el.m_elem[0].pe;\n        FEMaterial* pm = fem->GetMaterial(elem->GetMatID());\n        // get the multihasic material for this element\n        FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pm);\n        double Phi = 0;\n        if (pmp) {\n            // get average osmotic coefficient in this multiphasic element\n            int nint = elem->GaussPoints();\n            for (int n = 0; n < nint; ++n) {\n                FEMaterialPoint& mp = *elem->GetMaterialPoint(n);\n                Phi += pmp->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n            }\n            Phi /= nint;\n            // evaluate effective fluid pressure on the boundary\n            double pe = m_ambp - pmp->m_Rgas * pmp->m_Tabs * Phi * m_ambc;\n            // project to nodes of that face\n            for (int j = 0; j < el.Nodes(); ++j) {\n                int n = el.m_lnode[j];\n                pn[n] += pe;\n                ++cnt[n];\n            }\n        }\n    }\n\n    // prescribe the projected nodal values of the effective fluid pressure\n    for (int i = 0; i < ps->Nodes(); ++i)\n    {\n        if (ps->Node(i).m_ID[m_dofP] < -1)\n        {\n            assert(cnt[i]);\n            FENode& node = ps->Node(i);\n            // prescribe effective pressure at this node\n            double pe = pn[i] / cnt[i];\n            if (!m_bshellb) node.set(m_dofP, pe);\n            else node.set(m_dofQ, pe);\n        }\n    }\n\n    fem->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMatchingOsmoticCoefficientLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMix/FEMatchingOsmoticCoefficientLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"FEMultiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! FEMatchingOsmoticCoefficientBC is a surface boundary condition that imposes the same osmotic\n//! coefficient in the bath as in the multiphasic material bounded by tha surface\nclass FEBIOMIX_API FEMatchingOsmoticCoefficientLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEMatchingOsmoticCoefficientLoad(FEModel* pfem);\n\n    //! calculate traction stiffness (there is none)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n\n    //! calculate load vector\n    void LoadVector(FEGlobalVector& R) override {}\n\n    //! set the dilatation\n    void Update() override;\n\n    //! initialize\n    bool Init() override;\n\n    //! activate\n    void Activate() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\nprivate:\n    double          m_ambc;     //!<ambient osmolarity\n    double          m_ambp;     //!< ambient fluid pressure\n    bool            m_bshellb;  //!< shell bottom flag\nprivate:\n    int             m_dofP, m_dofQ;\n\n    DECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FEBioMix/FEMembraneMassActionForward.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMembraneMassActionForward.h\"\n#include \"FESoluteInterface.h\"\n\nBEGIN_FECORE_CLASS(FEMembraneMassActionForward, FEMembraneReaction)\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMembraneMassActionForward::FEMembraneMassActionForward(FEModel* pfem) : FEMembraneReaction(pfem)\n{\n    // set material properties\n    ADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMembraneMassActionForward::ReactionSupply(FEMaterialPoint& pt)\n{\n    // get reaction rate\n    double kF = m_pFwd->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = kF;\n    \n    // start with contribution from membrane solutes\n    const int nsol = (int) m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vR = m_vR[i];\n        if (vR > 0) {\n            double c = m_psm->GetActualSoluteConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    // add contribution of solid-bound molecules\n    const int nsbm = (int) m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vR = m_vR[nsol+i];\n        if (vR > 0) {\n            double c = m_psm->SBMArealConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    // add contribution from internal and external solutes\n    const int nse = (int) m_psm->SolutesExternal(pt);\n    for (int i=0; i<nse; ++i) {\n        int vRe = m_vRe[m_psm->GetSoluteIDExternal(pt,i)];\n        if (vRe > 0) {\n            // evaluate nodal effective concentrations\n            double c = m_psm->GetEffectiveSoluteConcentrationExternal(pt,i);\n            zhat *= pow(c, vRe);\n        }\n    }\n    const int nsi = (int) m_psm->SolutesInternal(pt);\n    for (int i=0; i<nsi; ++i) {\n        int vRi = m_vRi[m_vRi[m_psm->GetSoluteIDInternal(pt,i)]];\n        if (vRi > 0) {\n            // evaluate nodal effective concentrations\n            double c = m_psm->GetEffectiveSoluteConcentrationInternal(pt,i);\n            zhat *= pow(c, vRi);\n        }\n    }\n\n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n    double zhat = ReactionSupply(pt);\n    double dzhatde = 0;\n    if (kF > 0) dzhatde = dkFde*(zhat/kF);\n    \n    return dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n    double zhat = ReactionSupply(pt);\n    double dzhatdp = 0;\n    if (kF > 0) dzhatdp = dkFdp*zhat/kF;\n    return dzhatdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Pi(FEMaterialPoint& pt)\n{\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdp = m_pFwd->Tangent_ReactionRate_Pi(pt);\n    double zhat = ReactionSupply(pt);\n    double dzhatdp = 0;\n    if (kF > 0) dzhatdp = dkFdp*zhat/kF;\n    return dzhatdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Pe(FEMaterialPoint& pt)\n{\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdp = m_pFwd->Tangent_ReactionRate_Pe(pt);\n    double zhat = ReactionSupply(pt);\n    double dzhatdp = 0;\n    if (kF > 0) dzhatdp = dkFdp*zhat/kF;\n    return dzhatdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n    const int nsol = m_nsol;\n    \n    // if the derivative is taken with respect to a solid-bound molecule, return 0\n    if (sol >= nsol) return 0;\n    \n    double zhat = ReactionSupply(pt);\n    double dzhatdc = 0;\n    double c = m_psm->GetActualSoluteConcentration(pt, sol);\n    if ((zhat > 0) && (c > 0)) dzhatdc = m_vR[sol]/c*zhat;\n    \n    return dzhatdc;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Ci(FEMaterialPoint& pt, const int sol)\n{\n    double zhat = ReactionSupply(pt);\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdci = m_pFwd->Tangent_ReactionRate_Ci(pt, sol);\n    double dzhatdc = 0;\n    if (kF != 0) dzhatdc = dkFdci/kF*zhat;\n    double ci = m_psm->GetEffectiveSoluteConcentrationInternal(pt, sol);\n    int IDi = m_psm->GetSoluteIDInternal(pt, sol);\n    if ((zhat > 0) && (ci > 0)) dzhatdc += m_vRi[IDi]/ci*zhat;\n    \n    return dzhatdc;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMembraneMassActionForward::Tangent_ReactionSupply_Ce(FEMaterialPoint& pt, const int sol)\n{\n    double zhat = ReactionSupply(pt);\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdce = m_pFwd->Tangent_ReactionRate_Ce(pt, sol);\n    double dzhatdc = 0;\n    if (kF != 0) dzhatdc = dkFdce/kF*zhat;\n    double ce = m_psm->GetEffectiveSoluteConcentrationExternal(pt, sol);\n    int IDe = m_psm->GetSoluteIDExternal(pt, sol);\n    if ((zhat > 0) && (ce > 0)) dzhatdc += m_vRe[IDe]/ce*zhat;\n    \n    return dzhatdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMembraneMassActionForward.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMembraneReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Law of mass action for forward membrane reaction (must use effective concentrations)\nclass FEBIOMIX_API FEMembraneMassActionForward : public FEMembraneReaction\n{\npublic:\n    //! constructor\n    FEMembraneMassActionForward(FEModel* pfem);\n    \n    //! molar supply at material point\n    double ReactionSupply(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with strain at material point\n    double Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with effective pressure at material point\n    double Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n    double Tangent_ReactionSupply_Pi(FEMaterialPoint& pt) override;\n    double Tangent_ReactionSupply_Pe(FEMaterialPoint& pt) override;\n\n    //! tangent of molar supply with effective concentration at material point\n    double Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n    double Tangent_ReactionSupply_Ci(FEMaterialPoint& pt, const int sol) override;\n    double Tangent_ReactionSupply_Ce(FEMaterialPoint& pt, const int sol) override;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMembraneMassActionReversible.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEMembraneMassActionReversible.h\"\n#include \"FESoluteInterface.h\"\n\nBEGIN_FECORE_CLASS(FEMembraneMassActionReversible, FEMembraneReaction)\n    // set material properties\n    ADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\n    ADD_PROPERTY(m_pRev, \"reverse_rate\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMembraneMassActionReversible::FEMembraneMassActionReversible(FEModel* pfem) : FEMembraneReaction(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMembraneMassActionReversible::FwdReactionSupply(FEMaterialPoint& pt)\n{\n    // get forward reaction rate\n    double k = m_pFwd->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = k;\n    \n    // start with contribution from solutes\n    const int nsol = (int) m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vR = m_vR[i];\n        if (vR > 0) {\n            double c = m_psm->GetActualSoluteConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    // add contribution of solid-bound molecules\n    const int nsbm = (int) m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vR = m_vR[nsol+i];\n        if (vR > 0) {\n            double c = m_psm->SBMArealConcentration(pt, i);\n            zhat *= pow(c, vR);\n        }\n    }\n    \n    // add contribution from internal and external solutes\n    const int nse = (int) m_psm->SolutesExternal(pt);\n    for (int i=0; i<nse; ++i) {\n        int vRe = m_vRe[m_psm->GetSoluteIDExternal(pt,i)];\n        if (vRe > 0) {\n            // evaluate nodal effective concentrations\n            double c = m_psm->GetEffectiveSoluteConcentrationExternal(pt,i);\n            zhat *= pow(c, vRe);\n        }\n    }\n    const int nsi = (int) m_psm->SolutesInternal(pt);\n    for (int i=0; i<nsi; ++i) {\n        int vRi = m_vRi[m_psm->GetSoluteIDInternal(pt,i)];\n        if (vRi > 0) {\n            // evaluate nodal effective concentrations\n            double c = m_psm->GetEffectiveSoluteConcentrationInternal(pt,i);\n            zhat *= pow(c, vRi);\n        }\n    }\n\n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMembraneMassActionReversible::RevReactionSupply(FEMaterialPoint& pt)\n{\n    // get forward reaction rate\n    double k = m_pRev->ReactionRate(pt);\n    \n    // evaluate the reaction molar supply\n    double zhat = k;\n    \n    // start with contribution from solutes\n    const int nsol = (int) m_psm->Solutes();\n    for (int i=0; i<nsol; ++i) {\n        int vP = m_vP[i];\n        if (vP > 0) {\n            double c = m_psm->GetActualSoluteConcentration(pt, i);\n            zhat *= pow(c, vP);\n        }\n    }\n    \n    // add contribution of solid-bound molecules\n    const int nsbm = (int) m_psm->SBMs();\n    for (int i=0; i<nsbm; ++i) {\n        int vP = m_vP[nsol+i];\n        if (vP > 0) {\n            double c = m_psm->SBMArealConcentration(pt, i);\n            zhat *= pow(c, vP);\n        }\n    }\n    \n    // add contribution from internal and external solutes\n    const int nse = (int) m_psm->SolutesExternal(pt);\n    for (int i=0; i<nse; ++i) {\n        int vPe = m_vPe[m_psm->GetSoluteIDExternal(pt,i)];\n        if (vPe > 0) {\n            // evaluate nodal effective concentrations\n            double c = m_psm->GetEffectiveSoluteConcentrationExternal(pt,i);\n            zhat *= pow(c, vPe);\n        }\n    }\n    const int nsi = (int) m_psm->SolutesInternal(pt);\n    for (int i=0; i<nsi; ++i) {\n        int vPi = m_vPi[m_psm->GetSoluteIDInternal(pt,i)];\n        if (vPi > 0) {\n            // evaluate nodal effective concentrations\n            double c = m_psm->GetEffectiveSoluteConcentrationInternal(pt,i);\n            zhat *= pow(c, vPi);\n        }\n    }\n\n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMembraneMassActionReversible::ReactionSupply(FEMaterialPoint& pt)\n{\n    double zhatF = FwdReactionSupply(pt);\n    double zhatR = RevReactionSupply(pt);\n    return zhatF - zhatR;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n    // forward reaction\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFde = m_pFwd->Tangent_ReactionRate_Strain(pt);\n    double zhatF = FwdReactionSupply(pt);\n    double dzhatFde = 0;\n    if (kF > 0) dzhatFde = dkFde*(zhatF/kF);\n    \n    // reverse reaction\n    double kR = m_pRev->ReactionRate(pt);\n    double dkRde = m_pRev->Tangent_ReactionRate_Strain(pt);\n    double zhatR = RevReactionSupply(pt);\n    double dzhatRde = 0;\n    if (kR > 0) dzhatRde = dkRde*(zhatR/kR);\n    \n    return dzhatFde - dzhatRde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n    // forward reaction\n    double kF = m_pFwd->ReactionRate(pt);\n    double dzhatFdp = 0;\n    if (kF > 0) {\n        double dkFdp = m_pFwd->Tangent_ReactionRate_Pressure(pt);\n        double zhatF = FwdReactionSupply(pt);\n        dzhatFdp = dkFdp*zhatF/kF;\n    }\n    \n    // reverse reaction\n    double kR = m_pRev->ReactionRate(pt);\n    double dzhatRdp = 0;\n    if (kR > 0) {\n        double dkRdp = m_pRev->Tangent_ReactionRate_Pressure(pt);\n        double zhatR = RevReactionSupply(pt);\n        dzhatRdp = dkRdp*zhatR/kR;\n    }\n    \n    return dzhatFdp - dzhatRdp;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Pi(FEMaterialPoint& pt)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Pe(FEMaterialPoint& pt)\n{\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n    const int nsol = m_nsol;\n    \n    // if the derivative is taken with respect to a solid-bound molecule, return 0\n    if (sol >= nsol) {\n        return 0;\n    }\n    \n    // forward reaction\n    double zhatF = FwdReactionSupply(pt);\n    double dzhatFdc = 0;\n    double c = m_psm->GetActualSoluteConcentration(pt, sol);\n    if ((zhatF > 0) && (c > 0)) dzhatFdc = m_vR[sol]*zhatF/c;\n    \n    // reverse reaction\n    double zhatR = RevReactionSupply(pt);\n    double dzhatRdc = 0;\n    if ((zhatR > 0) && (c > 0)) dzhatRdc = m_vP[sol]*zhatR/c;\n    \n    return dzhatFdc - dzhatRdc;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Ci(FEMaterialPoint& pt, const int sol)\n{\n    // forward reaction\n    double zhatF = FwdReactionSupply(pt);\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdci = m_pFwd->Tangent_ReactionRate_Ci(pt, sol);\n    double dzhatFdc = 0;\n    if (kF != 0) dzhatFdc = dkFdci/kF*zhatF;\n    double ci = m_psm->GetEffectiveSoluteConcentrationInternal(pt, sol);\n    int IDi = m_psm->GetSoluteIDInternal(pt, sol);\n    if ((zhatF > 0) && (ci > 0)) dzhatFdc = m_vRi[IDi]*zhatF/ci;\n    \n    // reverse reaction\n    double zhatR = RevReactionSupply(pt);\n    double kR = m_pRev->ReactionRate(pt);\n    double dkRdci = m_pRev->Tangent_ReactionRate_Ci(pt, sol);\n    double dzhatRdc = 0;\n    if (kR != 0) dzhatRdc = dkRdci/kR*zhatR;\n    if ((zhatR > 0) && (ci > 0)) dzhatRdc += m_vPi[IDi]*zhatR/ci;\n    \n    return dzhatFdc - dzhatRdc;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMembraneMassActionReversible::Tangent_ReactionSupply_Ce(FEMaterialPoint& pt, const int sol)\n{\n    // forward reaction\n    double zhatF = FwdReactionSupply(pt);\n    double kF = m_pFwd->ReactionRate(pt);\n    double dkFdce = m_pFwd->Tangent_ReactionRate_Ce(pt, sol);\n    double dzhatFdc = 0;\n    if (kF != 0) dzhatFdc = dkFdce/kF*zhatF;\n    double ce = m_psm->GetEffectiveSoluteConcentrationExternal(pt, sol);\n    int IDe = m_psm->GetSoluteIDExternal(pt, sol);\n    if ((zhatF > 0) && (ce > 0)) dzhatFdc = m_vRe[IDe]*zhatF/ce;\n    \n    // reverse reaction\n    double zhatR = RevReactionSupply(pt);\n    double kR = m_pRev->ReactionRate(pt);\n    double dkRdce = m_pRev->Tangent_ReactionRate_Ce(pt, sol);\n    double dzhatRdc = 0;\n    if (kR != 0) dzhatRdc = dkRdce/kR*zhatR;\n    if ((zhatR > 0) && (ce > 0)) dzhatRdc += m_vPe[IDe]*zhatR/ce;\n    \n    return dzhatFdc - dzhatRdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMembraneMassActionReversible.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMembraneReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Law of mass action for reversible membrane reaction\n//! (must use effective concentrations).\n\nclass FEBIOMIX_API FEMembraneMassActionReversible : public FEMembraneReaction\n{\npublic:\n    //! constructor\n    FEMembraneMassActionReversible(FEModel* pfem);\n    \n    //! molar supply at material point\n    double ReactionSupply(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with strain at material point\n    double Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n    \n    //! tangent of molar supply with effective pressure at material point\n    double Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n    double Tangent_ReactionSupply_Pi(FEMaterialPoint& pt) override;\n    double Tangent_ReactionSupply_Pe(FEMaterialPoint& pt) override;\n\n    //! tangent of molar supply with effective concentration at material point\n    double Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n    double Tangent_ReactionSupply_Ci(FEMaterialPoint& pt, const int sol) override;\n    double Tangent_ReactionSupply_Ce(FEMaterialPoint& pt, const int sol) override;\n    \n    //! molar supply at material point\n    double FwdReactionSupply(FEMaterialPoint& pt);\n    \n    //! molar supply at material point\n    double RevReactionSupply(FEMaterialPoint& pt);\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMembraneReaction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMembraneReaction.h\"\n#include <FECore/FEElementTraits.h>\n#include <FECore/DOFS.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include \"FESoluteInterface.h\"\n#include \"FESolute.h\"\n#include <stdlib.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEInternalReactantSpeciesRef, FEReactionSpeciesRef)\n    ADD_PARAMETER(m_v, \"vRi\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEInternalProductSpeciesRef, FEReactionSpeciesRef)\n    ADD_PARAMETER(m_v, \"vPi\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEExternalReactantSpeciesRef, FEReactionSpeciesRef)\n    ADD_PARAMETER(m_v, \"vRe\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEExternalProductSpeciesRef, FEReactionSpeciesRef)\n    ADD_PARAMETER(m_v, \"vPe\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMembraneReaction, FEReaction)\n    ADD_PARAMETER(m_Vovr, \"override_vbar\")->SetFlags(FE_PARAM_WATCH);\n    ADD_PARAMETER(m_Vbar , \"Vbar\")->SetWatchVariable(&m_Vovr);\n\n    ADD_PROPERTY(m_vRtmp, \"vR\", FEProperty::Optional)->SetLongName(\"Membrane reactants\");\n    ADD_PROPERTY(m_vPtmp, \"vP\", FEProperty::Optional)->SetLongName(\"Membrane products\");\n    ADD_PROPERTY(m_vRitmp, \"vRi\", FEProperty::Optional)->SetLongName(\"Inner membrane reactants\");\n    ADD_PROPERTY(m_vPitmp, \"vPi\", FEProperty::Optional)->SetLongName(\"Inner membrane products\");;\n    ADD_PROPERTY(m_vRetmp, \"vRe\", FEProperty::Optional)->SetLongName(\"Outer membrane reactants\");\n    ADD_PROPERTY(m_vPetmp, \"vPe\", FEProperty::Optional)->SetLongName(\"Outer membrane products\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMembraneReaction::FEMembraneReaction(FEModel* pfem) : FEReaction(pfem)\n{\n    // additional initializations\n    m_Vbar = 0.0;\n    m_Vovr = false;\n    m_nsol = -1;\n\n\tm_pFwd = m_pRev = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Finds the solute id of a solute with given ID nsol.\n// This currently returns either nsol if a solute was found or -1 if not\nFESoluteData* FEMembraneReaction::GetSolute(int nsol)\n{\n    FEModel& fem = *GetFEModel();\n    int N = fem.GlobalDataItems();\n    for (int i=0; i<N; ++i)\n    {\n        FESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n        if (psd)\n        {\n            if (psd->GetID()-1 == nsol) return psd;\n        }\n    }\n    return nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMembraneReaction::Init()\n{\n    // set the parents for the reaction rates\n    if (m_pFwd) m_pFwd->m_pReact = this;\n    if (m_pRev) m_pRev->m_pReact = this;\n    \n    // initialize base class\n    if (FEReaction::Init() == false) return false;\n    \n    //************* reactants and products in multiphasic domain **************\n\n    // create the intmaps\n    for (int i = 0; i < m_vRtmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvr = m_vRtmp[i];\n        if (pvr->IsSolute()) SetStoichiometricCoefficient(m_solR, pvr->m_speciesID - 1, pvr->m_v);\n        if (pvr->IsSBM()   ) SetStoichiometricCoefficient(m_sbmR, pvr->m_speciesID - 1, pvr->m_v);\n    }\n    for (int i = 0; i < m_vPtmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvp = m_vPtmp[i];\n        if (pvp->IsSolute()) SetStoichiometricCoefficient(m_solP, pvp->m_speciesID - 1, pvp->m_v);\n        if (pvp->IsSBM()   ) SetStoichiometricCoefficient(m_sbmP, pvp->m_speciesID - 1, pvp->m_v);\n    }\n    for (int i = 0; i < m_vRitmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvr = m_vRitmp[i];\n        assert(pvr->IsSolute());\n        if (pvr->IsSolute()) SetStoichiometricCoefficient(m_solRi, pvr->m_speciesID - 1, pvr->m_v);\n    }\n    for (int i = 0; i < m_vPitmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvp = m_vPitmp[i];\n        assert(pvp->IsSolute());\n        if (pvp->IsSolute()) SetStoichiometricCoefficient(m_solPi, pvp->m_speciesID - 1, pvp->m_v);\n    }\n    for (int i = 0; i < m_vRetmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvr = m_vRetmp[i];\n        assert(pvr->IsSolute());\n        if (pvr->IsSolute()) SetStoichiometricCoefficient(m_solRe, pvr->m_speciesID - 1, pvr->m_v);\n    }\n    for (int i = 0; i < m_vPetmp.size(); ++i)\n    {\n        FEReactionSpeciesRef* pvp = m_vPetmp[i];\n        assert(pvp->IsSolute());\n        if (pvp->IsSolute()) SetStoichiometricCoefficient(m_solPe, pvp->m_speciesID - 1, pvp->m_v);\n    }\n\n    // initialize the reaction coefficients\n    const int nsol = m_psm->Solutes();\n    const int nsbm = m_psm->SBMs();\n    const int ntot = nsol + nsbm;\n    \n    // initialize the stoichiometric coefficients to zero\n    m_nsol = nsol;\n    m_vR.assign(ntot, 0);\n    m_vP.assign(ntot, 0);\n    m_v.assign(ntot, 0);\n    \n    // cycle through all the solutes in the mixture and determine\n    // if they participate in this reaction\n    itrmap it;\n    intmap solR = m_solR;\n    intmap solP = m_solP;\n    for (int isol = 0; isol<nsol; ++isol) {\n        int sid = m_psm->GetSolute(isol)->GetSoluteID() - 1;\n        it = solR.find(sid);\n        if (it != solR.end()) m_vR[isol] = it->second;\n        it = solP.find(sid);\n        if (it != solP.end()) m_vP[isol] = it->second;\n    }\n    \n    // cycle through all the solid-bound molecules in the mixture\n    // and determine if they participate in this reaction\n    intmap sbmR = m_sbmR;\n    intmap sbmP = m_sbmP;\n    for (int isbm = 0; isbm<nsbm; ++isbm) {\n        int sid = m_psm->GetSBM(isbm)->GetSBMID() - 1;\n        it = sbmR.find(sid);\n        if (it != sbmR.end()) m_vR[nsol + isbm] = it->second;\n        it = sbmP.find(sid);\n        if (it != sbmP.end()) m_vP[nsol + isbm] = it->second;\n    }\n    \n    // evaluate the net stoichiometric coefficient\n    for (int itot = 0; itot<ntot; ++itot) {\n        m_v[itot] = m_vP[itot] - m_vR[itot];\n    }\n    \n    //********* reactants and products on either side of membrane **********\n    // count total number of solutes in model\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_DDOFS = fedofs.GetVariableSize(\"shell concentration\");\n    m_NSOL = MAX_DDOFS;\n    m_z.assign(MAX_DDOFS, 0);\n\n    // initialize the stoichiometric coefficients to zero\n    m_vRi.assign(MAX_DDOFS, 0); m_vRe.assign(MAX_DDOFS, 0);\n    m_vPi.assign(MAX_DDOFS, 0); m_vPe.assign(MAX_DDOFS, 0);\n    m_vi.assign(MAX_DDOFS, 0); m_ve.assign(MAX_DDOFS, 0);\n\n    // cycle through all the solutes in the mixture and determine\n    // if they participate in this reaction\n    for (int ISOL = 0; ISOL<MAX_DDOFS; ++ISOL) {\n        it = m_solRi.find(ISOL);\n        if (it != m_solRi.end()) m_vRi[ISOL] = it->second;\n        it = m_solPi.find(ISOL);\n        if (it != m_solPi.end()) m_vPi[ISOL] = it->second;\n        it = m_solRe.find(ISOL);\n        if (it != m_solRe.end()) m_vRe[ISOL] = it->second;\n        it = m_solPe.find(ISOL);\n        if (it != m_solPe.end()) m_vPe[ISOL] = it->second;\n    }\n\n    // evaluate the net stoichiometric coefficient\n    for (int ISOL = 0; ISOL<MAX_DDOFS; ++ISOL) {\n        m_vi[ISOL] = m_vPi[ISOL] - m_vRi[ISOL];\n        m_ve[ISOL] = m_vPe[ISOL] - m_vRe[ISOL];\n        FESoluteData* sd = GetSolute(ISOL);\n        m_z[ISOL] = sd->m_z;\n    }\n\n    //************** continue with all reactants and products ***************\n    \n    // evaluate the weighted molar volume of reactants and products\n    if (!m_Vovr) {\n        m_Vbar = 0;\n        for (int isol = 0; isol<nsol; ++isol)\n            m_Vbar += m_v[isol] * m_psm->GetSolute(isol)->MolarMass() / m_psm->GetSolute(isol)->Density();\n        for (int isbm = 0; isbm<nsbm; ++isbm)\n            m_Vbar += m_v[nsol + isbm] * m_psm->GetSBM(isbm)->MolarMass() / m_psm->GetSBM(isbm)->Density();\n        for (int ISOL = 0; ISOL<MAX_DDOFS; ++ISOL) {\n            FESoluteData* sd = GetSolute(ISOL);\n            m_Vbar += m_vi[ISOL] * sd->m_M / sd->m_rhoT;\n            m_Vbar += m_ve[ISOL] * sd->m_M / sd->m_rhoT;\n        }\n    }\n    \n    // check that the reaction satisfies electroneutrality\n    int znet = 0;\n    for (int isol = 0; isol<nsol; ++isol)\n        znet += m_v[isol] * m_psm->GetSolute(isol)->ChargeNumber();\n    for (int isbm = 0; isbm<nsbm; ++isbm)\n        znet += m_v[nsol + isbm] * m_psm->GetSBM(isbm)->ChargeNumber();\n    for (int ISOL = 0; ISOL<MAX_DDOFS; ++ISOL) {\n        znet += m_vi[ISOL] * m_z[ISOL];\n        znet += m_ve[ISOL] * m_z[ISOL];\n    }\n\tif (znet != 0) {\n\t\tfeLogError(\"membrane reaction must satisfy electroneutrality\");\n\t\treturn false;\n\t}\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Data serialization\nvoid FEMembraneReaction::Serialize(DumpStream& ar)\n{\n    FEReaction::Serialize(ar);\n    \n    if (ar.IsShallow() == false)\n    {\n        if (ar.IsSaving())\n        {\n            itrmap p;\n            ar << m_nsol << m_vR << m_vP << m_v << m_Vovr << m_NSOL;\n            ar << (int) m_solR.size();\n            for (p = m_solR.begin(); p!=m_solR.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_solP.size();\n            for (p = m_solP.begin(); p!=m_solP.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_sbmR.size();\n            for (p = m_sbmR.begin(); p!=m_sbmR.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_sbmP.size();\n            for (p = m_sbmP.begin(); p!=m_sbmP.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_solRi.size();\n            for (p = m_solRi.begin(); p!=m_solRi.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_solPi.size();\n            for (p = m_solPi.begin(); p!=m_solPi.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_solRe.size();\n            for (p = m_solRe.begin(); p!=m_solRe.end(); ++p) {ar << p->first; ar << p->second;}\n            ar << (int) m_solPe.size();\n            for (p = m_solPe.begin(); p!=m_solPe.end(); ++p) {ar << p->first; ar << p->second;}\n        }\n        else\n        {\n            // restore pointers\n            if (m_pFwd) m_pFwd->m_pReact = this;\n            if (m_pRev) m_pRev->m_pReact = this;\n            \n            ar >> m_nsol >> m_vR >> m_vP >> m_v >> m_Vovr >> m_NSOL;\n            int size, id, vR;\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solR, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solP, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_sbmR, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_sbmP, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solRi, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solPi, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solRe, id, vR);\n            }\n            ar >> size;\n            for (int i=0; i<size; ++i)\n            {\n                ar >> id; ar >> vR;\n                SetStoichiometricCoefficient(m_solPe, id, vR);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FEMembraneReaction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEReaction.h\"\n#include \"febiomix_api.h\"\n#include \"FESolute.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for membrane reaction rates.\n\nclass FEBIOMIX_API FEMembraneReactionRate : public FEMaterialProperty\n{\npublic:\n    //! constructor\n    FEMembraneReactionRate(FEModel* pfem) : FEMaterialProperty(pfem), m_pReact(nullptr) {}\n    \n    //! reaction rate at material point\n    virtual double ReactionRate(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of reaction rate with area strain at material point\n    virtual double Tangent_ReactionRate_Strain(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of reaction rate with effective fluid pressure at material point\n    virtual double Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) = 0;\n    virtual double Tangent_ReactionRate_Pe(FEMaterialPoint& pt) = 0;\n    virtual double Tangent_ReactionRate_Pi(FEMaterialPoint& pt) = 0;\n\n    //! tangent of reaction rate with effective solute concentration at material point\n    virtual double Tangent_ReactionRate_Concentration(FEMaterialPoint& pt, const int isol) = 0;\n    virtual double Tangent_ReactionRate_Ce(FEMaterialPoint& pt, const int isol) = 0;\n    virtual double Tangent_ReactionRate_Ci(FEMaterialPoint& pt, const int isol) = 0;\n    \n    //! reset, initialize and update chemical reaction data in the FESolutesMaterialPoint\n    virtual void ResetElementData(FEMaterialPoint& mp) {}\n    virtual void InitializeElementData(FEMaterialPoint& mp) {}\n    virtual void UpdateElementData(FEMaterialPoint& mp) {}\n    \npublic:\n    FEReaction*    m_pReact;    //!< pointer to parent reaction\n\n    FECORE_BASE_CLASS(FEMembraneReactionRate)\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FEInternalReactantSpeciesRef : public FEReactionSpeciesRef\n{\npublic: FEInternalReactantSpeciesRef(FEModel* fem) : FEReactionSpeciesRef(fem) {}\n      DECLARE_FECORE_CLASS();\n      FECORE_BASE_CLASS(FEInternalReactantSpeciesRef)\n};\n\nclass FEBIOMIX_API FEInternalProductSpeciesRef : public FEReactionSpeciesRef {\npublic: FEInternalProductSpeciesRef(FEModel* fem) : FEReactionSpeciesRef(fem) {}\n      DECLARE_FECORE_CLASS();\n      FECORE_BASE_CLASS(FEInternalProductSpeciesRef)\n};\n\nclass FEBIOMIX_API FEExternalReactantSpeciesRef : public FEReactionSpeciesRef\n{\npublic: FEExternalReactantSpeciesRef(FEModel* fem) : FEReactionSpeciesRef(fem) {}\n      DECLARE_FECORE_CLASS();\n      FECORE_BASE_CLASS(FEExternalReactantSpeciesRef)\n};\n\nclass FEBIOMIX_API FEExternalProductSpeciesRef : public FEReactionSpeciesRef {\npublic: FEExternalProductSpeciesRef(FEModel* fem) : FEReactionSpeciesRef(fem) {}\n      DECLARE_FECORE_CLASS();\n      FECORE_BASE_CLASS(FEExternalProductSpeciesRef)\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for membrane reactions.\nclass FEBIOMIX_API FEMembraneReaction : public FEReaction\n{\npublic:\n    //! constructor\n    FEMembraneReaction(FEModel* pfem);\n    \n    //! get solute (use only during initialization)\n    FESoluteData* GetSolute(int nsol);\n    \n    //! initialization\n    bool Init() override;\n    \npublic:\n    //! set the forward reaction rate\n    void SetForwardReactionRate(FEMembraneReactionRate* pfwd) { m_pFwd = pfwd; }\n    \n    //! set the reverse reaction rate\n    void SetReverseReactionRate(FEMembraneReactionRate* prev) { m_pRev = prev; }\n    \npublic:\n    //! reset, initialize and update optional chemical reaction data in the FESolutesMaterialPoint\n    void ResetElementData(FEMaterialPoint& mp)\n    {\n        if (m_pFwd) m_pFwd->ResetElementData(mp);\n        if (m_pRev) m_pRev->ResetElementData(mp);\n    }\n    void InitializeElementData(FEMaterialPoint& mp)\n    {\n        if (m_pFwd) m_pFwd->InitializeElementData(mp);\n        if (m_pRev) m_pRev->InitializeElementData(mp);\n    }\n    void UpdateElementData(FEMaterialPoint& mp)\n    {\n        if (m_pFwd) m_pFwd->UpdateElementData(mp);\n        if (m_pRev) m_pRev->UpdateElementData(mp);\n    }\n    \npublic:\n    //! molar supply at material point\n    virtual double ReactionSupply(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of molar supply with strain at material point\n    virtual double Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) = 0;\n    \n    //! tangent of molar supply with effective pressure at material point\n    virtual double Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) = 0;\n    virtual double Tangent_ReactionSupply_Pe(FEMaterialPoint& pt) = 0;\n    virtual double Tangent_ReactionSupply_Pi(FEMaterialPoint& pt) = 0;\n\n    //! tangent of molar supply with effective concentration at material point\n    virtual double Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) = 0;\n    virtual double Tangent_ReactionSupply_Ce(FEMaterialPoint& pt, const int sol) = 0;\n    virtual double Tangent_ReactionSupply_Ci(FEMaterialPoint& pt, const int sol) = 0;\n\npublic:\n    //! Serialization\n    void Serialize(DumpStream& ar) override;\n    \npublic:\n    FEMembraneReactionRate*    m_pFwd;        //!< pointer to forward reaction rate\n    FEMembraneReactionRate*    m_pRev;        //!< pointer to reverse reaction rate\n    \n    vector<FEReactantSpeciesRef*> m_vRtmp;\t//!< helper variable for reading in stoichiometric coefficients for reactants\n    vector<FEProductSpeciesRef*> m_vPtmp;\t//!< helper variable for reading in stoichiometric coefficients for products\n    vector<FEInternalReactantSpeciesRef*> m_vRitmp;\t//!< helper variable for reading in stoichiometric coefficients for internal reactants\n    vector<FEInternalProductSpeciesRef*> m_vPitmp;\t//!< helper variable for reading in stoichiometric coefficients for internal products\n    vector<FEExternalReactantSpeciesRef*> m_vRetmp;\t//!< helper variable for reading in stoichiometric coefficients for external reactants\n    vector<FEExternalProductSpeciesRef*> m_vPetmp;\t//!< helper variable for reading in stoichiometric coefficients for external products\n\n    intmap          m_solR;         //!< stoichiometric coefficients of solute reactants\n    intmap          m_solP;         //!< stoichiometric coefficients of solute products\n    intmap          m_sbmR;         //!< stoichiometric coefficients of solid-bound reactants\n    intmap          m_sbmP;         //!< stoichiometric coefficients of solid-bound products\n    intmap          m_solRi;        //!< stoichiometric coefficients of internal solute reactants\n    intmap          m_solPi;        //!< stoichiometric coefficients of internal solute products\n    intmap          m_solRe;        //!< stoichiometric coefficients of external solute reactants\n    intmap          m_solPe;        //!< stoichiometric coefficients of external solute products\n\npublic:\n    int             m_nsol;         //!< number of solutes in the mixture\n    double          m_Vbar;         //!< weighted molar volume of reactants and products\n    bool            m_Vovr;         //!< override flag for m_Vbar\n    vector<int>     m_vR;           //!< stoichiometric coefficients of reactants\n    vector<int>     m_vP;           //!< stoichiometric coefficients of products\n    vector<int>     m_v;            //!< net stoichiometric coefficients of reactants and products\n    int             m_NSOL;         //!< number of solutes in the model\n    vector<int>     m_z;            //!< charge number of all solutes\n    vector<int>     m_vRi;          //!< stoichiometric coefficients of reactants\n    vector<int>     m_vPi;          //!< stoichiometric coefficients of products\n    vector<int>     m_vi;           //!< net stoichiometric coefficients of reactants and products\n    vector<int>     m_vRe;          //!< stoichiometric coefficients of reactants\n    vector<int>     m_vPe;          //!< stoichiometric coefficients of products\n    vector<int>     m_ve;           //!< net stoichiometric coefficients of reactants and products\n\n    DECLARE_FECORE_CLASS();\n    FECORE_BASE_CLASS(FEMembraneReaction)\n};\n"
  },
  {
    "path": "FEBioMix/FEMembraneReactionRateConst.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMembraneReactionRateConst.h\"\n\n// Material parameters for the FEMembraneReactionRateConst material\nBEGIN_FECORE_CLASS(FEMembraneReactionRateConst, FEMembraneReactionRate)\n\tADD_PARAMETER(m_k, FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMix/FEMembraneReactionRateConst.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMultiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! constant membrane reaction rate\n//!\nclass FEBIOMIX_API FEMembraneReactionRateConst : public FEMembraneReactionRate\n{\npublic:\n    //! constructor\n    FEMembraneReactionRateConst(FEModel* pfem) : FEMembraneReactionRate(pfem) { m_k = 0; }\n    \n    //! reaction rate at material point\n    double ReactionRate(FEMaterialPoint& pt) override { return m_k; }\n    \n    //! tangent of reaction rate with strain at material point\n    double Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override { return 0; }\n    \n    //! tangent of reaction rate with effective fluid pressure at material point\n    double Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override {return 0; }\n    double Tangent_ReactionRate_Pe(FEMaterialPoint& pt) override { return 0; }\n    double Tangent_ReactionRate_Pi(FEMaterialPoint& pt) override { return 0; }\n\n    //! tangent of reaction rate with effective solute concentration at material point\n    double Tangent_ReactionRate_Concentration(FEMaterialPoint& pt, const int isol) override {return 0; }\n    double Tangent_ReactionRate_Ce(FEMaterialPoint& pt, const int isol) override { return 0; }\n    double Tangent_ReactionRate_Ci(FEMaterialPoint& pt, const int isol) override { return 0; };\n    \npublic:\n    double    m_k;        //!< reaction rate\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMembraneReactionRateIonChannel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMembraneReactionRateIonChannel.h\"\n#include \"FESoluteInterface.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FESolute.h\"\n#include <FEBioMech/FEElasticMaterialPoint.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\n// Material parameters for the FEMembraneReactionRateConst material\nBEGIN_FECORE_CLASS(FEMembraneReactionRateIonChannel, FEMembraneReactionRate)\n\tADD_PARAMETER(m_g, FE_RANGE_GREATER_OR_EQUAL(0.0), \"g\");\n\tADD_PARAMETER(m_sol, \"sol\");\n    ADD_PARAMETER(m_sbm, \"sbm\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMembraneReactionRateIonChannel::FEMembraneReactionRateIonChannel(FEModel* pfem) : FEMembraneReactionRate(pfem)\n{\n    m_sol = -1;\n    m_sbm = -1;\n    m_lid = -1;\n    m_g = 0;\n    m_z = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMembraneReactionRateIonChannel::Init()\n{\n    if (FEMembraneReactionRate::Init() == false) return false;\n    \n    // do only once\n    if (m_lid == -1) {\n        // get number of DOFS\n        DOFS& fedofs = GetFEModel()->GetDOFS();\n        int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n        // check validity of sol\n        if (m_sol < 1 || m_sol > MAX_CDOFS) {\n            feLogError(\"sol value outside of valid range for solutes\");\n            return false;\n        }\n        \n        FEModel& fem = *GetFEModel();\n        int N = GetFEModel()->GlobalDataItems();\n        for (int i=0; i<N; ++i)\n        {\n            FESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n            if (psd && (psd->GetID() == m_sol)) {\n                m_lid = m_sol - 1;\n                m_z = psd->m_z;\n                break;\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMembraneReactionRateIonChannel::ReactionRate(FEMaterialPoint& pt)\n{\n    FESolutesMaterialPoint& ps = *pt.ExtractData<FESolutesMaterialPoint>();\n    double ci = ps.m_ci[m_lid];\n    double ce = ps.m_ce[m_lid];\n    double R = GetGlobalConstant(\"R\");\n    double T = GetGlobalConstant(\"T\");\n    double Fc = GetGlobalConstant(\"Fc\");\n    FESoluteInterface* psi = m_pReact->m_psm; assert(psi);\n    double ksi = (m_sbm > -1) ? psi->SBMArealConcentration(pt, m_sbm - 1): 1.0;\n\n    double k = 0;\n    if ((ci > 0) && (ce > 0))\n        k = (ci != ce) ? R*T*m_g/ksi/pow(Fc*m_z,2)*log(ci/ce)/(ci-ce) : R*T*m_g/pow(Fc*m_z,2)/ce;\n    \n    return k;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with strain at material point\ndouble FEMembraneReactionRateIonChannel::Tangent_ReactionRate_Strain(FEMaterialPoint& pt)\n{\n    // get the areal strain\n    FEElasticMaterialPoint& pe = *(pt.ExtractData<FEElasticMaterialPoint>());\n    FEShellElement*sel = dynamic_cast<FEShellElement*>(pt.m_elem);\n    assert(sel);\n    double Jg = pe.m_J*sel->Evaluate(sel->m_h0, pt.m_index)/sel->Evaluate(sel->m_ht, pt.m_index);\n    return ReactionRate(pt)/Jg;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMembraneReactionRateIonChannel::Tangent_ReactionRate_Ci(FEMaterialPoint& pt, const int isol)\n{\n    if (isol != m_lid)  return 0;\n    \n    FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n    double ci = ps.m_ci[m_lid];\n    double ce = ps.m_ce[m_lid];\n    double R = GetGlobalConstant(\"R\");\n    double T = GetGlobalConstant(\"T\");\n    double Fc = GetGlobalConstant(\"Fc\");\n    FESoluteInterface* psi = m_pReact->m_psm; assert(psi);\n    double ksi = (m_sbm > -1) ? psi->SBMArealConcentration(pt, m_sbm - 1) : 1.0;\n\n    double dkdc = 0;\n    if ((ci > 0) && (ce > 0))\n        dkdc = (ci != ce) ? R*T/pow(Fc*m_z,2)*m_g/ksi*(ci*(1-log(ci/ce))-ce)/pow(ci-ce,2)/ci : -R*T*m_g/pow(ci*Fc*m_z,2)/2;\n    \n    return dkdc;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMembraneReactionRateIonChannel::Tangent_ReactionRate_Ce(FEMaterialPoint& pt, const int isol)\n{\n    if (isol != m_lid)  return 0;\n    \n    FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n    double ci = ps.m_ci[m_lid];\n    double ce = ps.m_ce[m_lid];\n    double R = GetGlobalConstant(\"R\");\n    double T = GetGlobalConstant(\"T\");\n    double Fc = GetGlobalConstant(\"Fc\");\n    FESoluteInterface* psi = m_pReact->m_psm; assert(psi);\n    double ksi = (m_sbm > -1) ? psi->SBMArealConcentration(pt, m_sbm - 1) : 1.0;\n\n    double dkdc = 0;\n    if ((ci > 0) && (ce > 0))\n        dkdc = (ci != ce) ? R*T*m_g/ksi/pow(Fc*m_z,2)*(ce*(1+log(ci/ce))-ci)/pow(ci-ce,2)/ce : -R*T*m_g/pow(ci*Fc*m_z,2)/2;\n    \n    return dkdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMembraneReactionRateIonChannel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMembraneReaction.h\"\n\nclass FEBIOMIX_API FEMembraneReactionRateIonChannel : public FEMembraneReactionRate\n{\npublic:\n    //! constructor\n    FEMembraneReactionRateIonChannel(FEModel* pfem);\n    \n    // initialization\n    bool Init() override;\n    \n    //! reaction rate at material point\n    double ReactionRate(FEMaterialPoint& pt) override;\n    \n    //! tangent of reaction rate with strain at material point\n    double Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override;\n    \n    //! tangent of reaction rate with effective fluid pressure at material point\n    double Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override {return 0; }\n    double Tangent_ReactionRate_Pe(FEMaterialPoint& pt) override { return 0; }\n    double Tangent_ReactionRate_Pi(FEMaterialPoint& pt) override { return 0; }\n    \n    //! tangent of reaction rate with effective solute concentration at material point\n    double Tangent_ReactionRate_Concentration(FEMaterialPoint& pt, const int isol) override {return 0; }\n    double Tangent_ReactionRate_Ce(FEMaterialPoint& pt, const int isol) override;\n    double Tangent_ReactionRate_Ci(FEMaterialPoint& pt, const int isol) override;\n    \npublic:\n    int     m_sol;      //!< solute id (1-based) for ion\n    int     m_lid;      //!< local id of solute (zero-based)\n    int     m_z;        //!< charge number of channel ion\n    int     m_sbm;      //!< sbm id (1-based) for channel protein\n    double  m_g;        //!< channel conductance\n    \n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMembraneReactionRateVoltageGated.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMembraneReactionRateVoltageGated.h\"\n#include \"FESoluteInterface.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FESolute.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\n// Material parameters for the FEMembraneReactionRateVoltageGated material\nBEGIN_FECORE_CLASS(FEMembraneReactionRateVoltageGated, FEMembraneReactionRate)\n\tADD_PARAMETER(m_a, FE_RANGE_GREATER_OR_EQUAL(0.0), \"a\");\n\tADD_PARAMETER(m_b, FE_RANGE_GREATER_OR_EQUAL(0.0), \"b\");\n\tADD_PARAMETER(m_c, FE_RANGE_GREATER_OR_EQUAL(0.0), \"c\");\n\tADD_PARAMETER(m_d, FE_RANGE_GREATER_OR_EQUAL(0.0), \"d\");\n\tADD_PARAMETER(m_sol, \"sol\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMembraneReactionRateVoltageGated::FEMembraneReactionRateVoltageGated(FEModel* pfem) : FEMembraneReactionRate(pfem)\n{\n    m_a = m_b = m_c = m_d = 0;\n    m_sol = m_lid = -1;\n    m_z = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMembraneReactionRateVoltageGated::Init()\n{\n    if (FEMembraneReactionRate::Init() == false) return false;\n    \n    // do only once\n    if (m_lid == -1) {\n        // get number of DOFS\n        DOFS& fedofs = GetFEModel()->GetDOFS();\n        int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n        // check validity of sol\n        if (m_sol < 1 || m_sol > MAX_CDOFS) {\n            feLogError(\"sol value outside of valid range for solutes\");\n            return false;\n        }\n        \n        FEModel& fem = *GetFEModel();\n        int N = GetFEModel()->GlobalDataItems();\n        for (int i=0; i<N; ++i)\n        {\n            FESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n            if (psd && (psd->GetID() == m_sol)) {\n                m_lid = m_sol - 1;\n                m_z = psd->m_z;\n                break;\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMembraneReactionRateVoltageGated::ReactionRate(FEMaterialPoint& pt)\n{\n    FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n    double ci = ps.m_ci[m_lid];\n    double ce = ps.m_ce[m_lid];\n    if (ce == 0) return 0;\n    double x = ci/ce;\n    \n    double k = (m_c*log(x) + m_d)/(m_a + pow(x,m_b));\n    \n    return k;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMembraneReactionRateVoltageGated::Tangent_ReactionRate_Ci(FEMaterialPoint& pt, const int isol)\n{\n    if (isol != m_lid)  return 0;\n    \n    FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n    double ci = ps.m_ci[m_lid];\n    double ce = ps.m_ce[m_lid];\n    if ((ce == 0) || (ci == 0)) return 0;\n    double x = ci/ce;\n    double xb = pow(x,m_b);\n\n    double dkdc = (m_a*m_c + xb*(m_c-m_b*m_d) - m_b*m_c*log(x))/pow(m_a+xb,2)/ci;\n    \n    return dkdc;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMembraneReactionRateVoltageGated::Tangent_ReactionRate_Ce(FEMaterialPoint& pt, const int isol)\n{\n    if (isol != m_lid)  return 0;\n    \n    FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n    double ci = ps.m_ci[m_lid];\n    double ce = ps.m_ce[m_lid];\n    if (ce == 0) return 0;\n    double x = ci/ce;\n    double xb = pow(x,m_b);\n\n    double dkdc = -(m_a*m_c + xb*(m_c-m_b*m_d) - m_b*m_c*log(x))/pow(m_a+xb,2)/ce;\n\n    \n    return dkdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMembraneReactionRateVoltageGated.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMembraneReaction.h\"\n\nclass FEBIOMIX_API FEMembraneReactionRateVoltageGated : public FEMembraneReactionRate\n{\npublic:\n    //! constructor\n    FEMembraneReactionRateVoltageGated(FEModel* pfem);\n    \n    // initialization\n    bool Init() override;\n    \n    //! reaction rate at material point\n    double ReactionRate(FEMaterialPoint& pt) override;\n    \n    //! tangent of reaction rate with strain at material point\n    double Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override { return 0; }\n    \n    //! tangent of reaction rate with effective fluid pressure at material point\n    double Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override {return 0; }\n    double Tangent_ReactionRate_Pe(FEMaterialPoint& pt) override { return 0; }\n    double Tangent_ReactionRate_Pi(FEMaterialPoint& pt) override { return 0; }\n    \n    //! tangent of reaction rate with effective solute concentration at material point\n    double Tangent_ReactionRate_Concentration(FEMaterialPoint& pt, const int isol) override {return 0; }\n    double Tangent_ReactionRate_Ce(FEMaterialPoint& pt, const int isol) override;\n    double Tangent_ReactionRate_Ci(FEMaterialPoint& pt, const int isol) override;\n    \npublic:\n    int     m_sol;      //!< solute id (1-based)\n    int     m_lid;      //!< local id of solute (zero-based)\n    int     m_z;        //!< charge number of channel ion\n    double  m_a;        //!< coefficient\n    double  m_b;        //!< coefficient\n    double  m_c;        //!< coefficient\n    double  m_d;        //!< coefficient\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMichaelisMenten.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMichaelisMenten.h\"\n#include \"FESoluteInterface.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMichaelisMenten, FEChemicalReaction)\n\tADD_PARAMETER(m_Km, \"Km\");\n\tADD_PARAMETER(m_c0, \"c0\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pFwd, \"forward_rate\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\nFEMichaelisMenten::FEMichaelisMenten(FEModel* pfem) : FEChemicalReaction(pfem) \n{ \n\tm_Rid = m_Pid = -1; \n\tm_Km = m_c0 = 0; \n\tm_Rtype = false; \n}\n\n//-----------------------------------------------------------------------------\n//! data initialization and checking\nbool FEMichaelisMenten::Init()\n{\n    // Initialize base class\n    if (FEChemicalReaction::Init() == false) return false;\n    \n\t// there is only one reactant and one product in a Michaelis-Menten reaction\n\tif (m_solR.size() + m_sbmR.size() > 1) {\n\t\tfeLogError(\"Provide only one vR for this reaction\");\n\t\treturn false;\n\t}\n\n\tif (m_solP.size() + m_sbmP.size() > 1) {\n\t\tfeLogError(\"Provide only one vP for this reaction\");\n\t\treturn false;\n\t}\n\n\tif (m_c0 < 0) {\n\t\tfeLogError(\"c0 must be positive\");\n\t\treturn false;\n\t}\n\t\n\tconst int ntot = (int)m_v.size();\n\tfor (int itot=0; itot<ntot; itot++) {\n\t\tif (m_vR[itot] > 0) m_Rid = itot;\n\t\tif (m_vP[itot] > 0) m_Pid = itot;\n\t}\n\t\n\tif (m_Rid == -1) {\n\t\tfeLogError(\"Provide vR for the reactant\");\n\t\treturn false;\n\t}\n\t\n\t// check if reactant is a solute or a solid-bound molecule\n\tif (m_Rid >= m_nsol) m_Rtype = true;\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! molar supply at material point\ndouble FEMichaelisMenten::ReactionSupply(FEMaterialPoint& pt)\n{\n\t// get reaction rate\n\tdouble Vmax = m_pFwd->ReactionRate(pt);\n\tdouble c = 0.0;\n\tif (m_Rtype) {\n\t\tc = m_psm->SBMConcentration(pt, m_Rid);\n\t}\n\telse {\n\t\tc = m_psm->GetActualSoluteConcentration(pt, m_Rid);\n\t}\n\n\tdouble zhat = 0;\n\tif (c > m_c0) zhat = Vmax*c/(m_Km + c);\n\t\n\treturn zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with strain at material point\nmat3ds FEMichaelisMenten::Tangent_ReactionSupply_Strain(FEMaterialPoint& pt)\n{\n\tdouble ca = 0.0;\n\tdouble dcdJ = 0.0;\n\tif (m_Rtype) {\n\t\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor()); assert(pbm);\n\t\tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\t\tca = m_psm->SBMConcentration(pt, m_Rid);\n\t\tdouble J = ept.m_J;\n        double phi0 = pbm->GetReferentialSolidVolumeFraction(pt);\n\t\tdcdJ = -ca/(J-phi0);\n\t}\n\telse {\n\t\tca = m_psm->GetActualSoluteConcentration(pt, m_Rid);\n\t\tdouble c = m_psm->GetEffectiveSoluteConcentration(pt, m_Rid);\n\t\tdouble dkdJ = m_psm->dkdJ(pt, m_Rid);\n\t\tdcdJ = dkdJ*c;\n\t}\n\t\n\tdouble dzhatdJ = 0;\n\tif (ca > m_c0) {\n        double Vmax = m_pFwd->ReactionRate(pt);\n        dzhatdJ = dcdJ*m_Km*Vmax/SQR(m_Km + ca);\n    }\n\t\n\treturn mat3dd(1)*dzhatdJ;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective pressure at material point\ndouble FEMichaelisMenten::Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of molar supply with effective concentration at material point\ndouble FEMichaelisMenten::Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol)\n{\n\tif (m_Rtype || (m_Rid != sol)) return 0;\n\n\tdouble dzhatdc = 0;\n\tdouble ca = m_psm->GetActualSoluteConcentration(pt, m_Rid);\n\tif (ca > m_c0) {\n        double Vmax = m_pFwd->ReactionRate(pt);\n\t\tdouble k = m_psm->GetPartitionCoefficient(pt, m_Rid);\n\t\tdouble c = m_psm->GetEffectiveSoluteConcentration(pt, m_Rid);\n\t\tdouble dkdc = m_psm->dkdc(pt, m_Rid, m_Rid);\n\t\tdzhatdc = m_Km*Vmax/SQR(m_Km + ca)*(k + dkdc*c);\n    }\n\t\n\treturn dzhatdc;\n}\n"
  },
  {
    "path": "FEBioMix/FEMichaelisMenten.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Forward chemical reaction following Michaelis-Menten kinetics.\n//! The maximum uptake rate is given by the forward reaction rate\n//! which is defined in the parent class FEChemicalReaction.\n//! Optionally, a minimum concentration may be prescribed for the\n//! reactant to trigger the reaction.\n\nclass FEBIOMIX_API FEMichaelisMenten : public FEChemicalReaction\n{\npublic:\n\t//! constructor\n\tFEMichaelisMenten(FEModel* pfem);\n\t\n\t//! data initialization and checking\n\tbool Init() override;\n\t\n\t//! molar supply at material point\n\tdouble ReactionSupply(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with strain at material point\n\tmat3ds Tangent_ReactionSupply_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective pressure at material point\n\tdouble Tangent_ReactionSupply_Pressure(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of molar supply with effective concentration at material point\n\tdouble Tangent_ReactionSupply_Concentration(FEMaterialPoint& pt, const int sol) override;\n\npublic:\n\tdouble\tm_Km;\t\t\t//!< concentration at which half-maximum rate occurs\n\tint\t\tm_Rid;\t\t\t//!< local id of reactant\n\tint\t\tm_Pid;\t\t\t//!< local id of product\n\tbool\tm_Rtype;\t\t//!< flag for reactant type (solute = false, sbm = true)\n\tdouble\tm_c0;\t\t\t//!< minimum reactant concentration to trigger reaction\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\t\n};\n\n"
  },
  {
    "path": "FEBioMix/FEMixDomainFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMixDomainFactory.h\"\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FETriphasic.h\"\n#include \"FEMultiphasic.h\"\n#include \"FEBiphasicSolidDomain.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n#include \"FETriphasicDomain.h\"\n#include \"FEMultiphasicDomain.h\"\n#include <FECore/FEShellDomain.h>\n\n//-----------------------------------------------------------------------------\nFEDomain* FEMixDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tFEModel* pfem = pmat->GetFEModel();\n\tFE_Element_Class eclass = spec.eclass;\n\n\tFEDomain* pd = nullptr;\n\n\tif (eclass == FE_ELEM_SOLID)\n\t{\n\t\tconst char* sztype = 0;\n\t\tif      (dynamic_cast<FEBiphasic*      >(pmat)) sztype = \"biphasic-solid\";\n\t\telse if (dynamic_cast<FEBiphasicSolute*>(pmat)) sztype = \"biphasic-solute-solid\";\n\t\telse if (dynamic_cast<FETriphasic*     >(pmat)) sztype = \"triphasic-solid\";\n\t\telse if (dynamic_cast<FEMultiphasic*   >(pmat)) sztype = \"multiphasic-solid\";\n\n\t\tif (sztype) pd = fecore_new<FESolidDomain>(sztype, pfem);\n\t}\n\telse if (eclass == FE_ELEM_SHELL)\n\t{\n\t\tconst char* sztype = 0;\n\t\tif      (dynamic_cast<FEBiphasic*      >(pmat)) sztype = \"biphasic-shell\";\n\t\telse if (dynamic_cast<FEBiphasicSolute*>(pmat)) sztype = \"biphasic-solute-shell\";\n\t\telse if (dynamic_cast<FEMultiphasic*   >(pmat)) sztype = \"multiphasic-shell\";\n\n\t\tif (sztype) pd = fecore_new<FEShellDomain>(sztype, pfem);\n\t}\n\n\tif (pd) pd->SetMaterial(pmat);\n\treturn pd;\n}\n"
  },
  {
    "path": "FEBioMix/FEMixDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreKernel.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FEMixDomainFactory : public FEDomainFactory\n{\npublic:\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioMix/FEMixtureNormalTraction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMixtureNormalTraction.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMixtureNormalTraction, FESurfaceLoad)\n\tADD_PARAMETER(m_traction  , \"traction\" )->setLongName(\"mixture normal traction\")->setUnits(UNIT_PRESSURE);\n\tADD_PARAMETER(m_blinear   , \"linear\"   );\n    ADD_PARAMETER(m_bshellb   , \"shell_bottom\")->setLongName(\"apply on shell bottom\");\n\tADD_PARAMETER(m_beffective, \"effective\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMixtureNormalTraction::FEMixtureNormalTraction(FEModel* pfem) : FESurfaceLoad(pfem)\n{ \n\tm_traction = 1.0;\n\tm_blinear = false; \n    m_bshellb = false;\n\tm_beffective = false;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEMixtureNormalTraction::SetSurface(FESurface* ps)\n{ \n\tFESurfaceLoad::SetSurface(ps);\n\tm_traction.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\nbool FEMixtureNormalTraction::Init()\n{\n\tif (m_psurf == nullptr) return false;\n\tm_psurf->SetShellBottom(m_bshellb);\n\n\tFEModel* fem = GetFEModel();\n\tm_dof.Clear();\n\tif (m_bshellb == false)\n\t{\n\t\tm_dof.AddDof(\"x\");\n\t\tm_dof.AddDof(\"y\");\n\t\tm_dof.AddDof(\"z\");\n\t\tif (m_dof.AddDof(\"p\") == false) m_dof.AddDof(-1);\n\t}\n\telse\n\t{\n\t\tm_dof.AddDof(fem->GetDOFIndex(\"sx\"));\n\t\tm_dof.AddDof(fem->GetDOFIndex(\"sy\"));\n\t\tm_dof.AddDof(fem->GetDOFIndex(\"sz\"));\n\t\tif (m_dof.AddDof(\"q\") == false) m_dof.AddDof(-1);\n\t}\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMixtureNormalTraction::StiffnessMatrix(FELinearSystem& LS)\n{\n\tif (m_blinear) return;\n\n\tm_psurf->SetShellBottom(m_bshellb);\n\n\tbool bsymm = LS.IsSymmetric();\n\n    FEMixtureNormalTraction* traction = this;\n\tm_psurf->LoadStiffness(LS, m_dof, m_dof, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\t\tdouble H_i  = dof_a.shape;\n\t\t\tdouble Gr_i = dof_a.shape_deriv_r;\n\t\t\tdouble Gs_i = dof_a.shape_deriv_s;\n\n\t\t\tdouble H_j  = dof_b.shape;\n\t\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\t\t// traction at integration point\n\t\t\tdouble tr = traction->Traction(mp);\n\t\t\tif (traction->m_bshellb) tr = -tr;\n\n\t\t\t// calculate stiffness component\n\t\t\tKab.zero();\n\t\t\tif (!bsymm) {\n\t\t\t\t// non-symmetric\n\t\t\t\tvec3d kab = (mp.dxs*Gr_j - mp.dxr*Gs_j)*H_i * tr;\n\n\t\t\t\tKab[0][0] = 0;\n\t\t\t\tKab[0][1] = -kab.z;\n\t\t\t\tKab[0][2] = kab.y;\n\n\t\t\t\tKab[1][0] = kab.z;\n\t\t\t\tKab[1][1] = 0;\n\t\t\t\tKab[1][2] = -kab.x;\n\n\t\t\t\tKab[2][0] = -kab.y;\n\t\t\t\tKab[2][1] = kab.x;\n\t\t\t\tKab[2][2] = 0;\n\n\t\t\t\t// if prescribed traction is effective, add stiffness component\n\t\t\t\tif (traction->m_beffective)\n\t\t\t\t{\n\t\t\t\t\tvec3d kab = (mp.dxr ^ mp.dxs)* H_i * H_j;\n\n\t\t\t\t\tKab[0][3] = kab.x;\n\t\t\t\t\tKab[1][3] = kab.y;\n\t\t\t\t\tKab[2][3] = kab.z;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// symmetric\n\n\t\t\t\tvec3d kab = ((mp.dxs*Gr_j - mp.dxr*Gs_j)*H_i - (mp.dxs*Gr_i - mp.dxr*Gs_i)*H_j)*0.5 * tr;\n\n\t\t\t\tKab[0][0] = 0;\n\t\t\t\tKab[0][1] = -kab.z;\n\t\t\t\tKab[0][2] = kab.y;\n\n\t\t\t\tKab[1][0] = kab.z;\n\t\t\t\tKab[1][1] = 0;\n\t\t\t\tKab[1][2] = -kab.x;\n\n\t\t\t\tKab[2][0] = -kab.y;\n\t\t\t\tKab[2][1] = kab.x;\n\t\t\t\tKab[2][2] = 0;\n\n\t\t\t\t// if prescribed traction is effective, add stiffness component\n\t\t\t\tif (traction->m_beffective)\n\t\t\t\t{\n\t\t\t\t\tvec3d kab = (mp.dxr ^ mp.dxs) * 0.5*H_i * H_j;\n\n\t\t\t\t\tKab[0][3] = kab.x;\n\t\t\t\t\tKab[1][3] = kab.y;\n\t\t\t\t\tKab[2][3] = kab.z;\n\n\t\t\t\t\t// TODO: This is not symmetric!\n\t\t\t\t\tKab[0][3] = kab.x;\n\t\t\t\t\tKab[1][3] = kab.y;\n\t\t\t\t\tKab[2][3] = kab.z;\n\t\t\t\t}\n\t\t}\n\t});\n}\n\n//-----------------------------------------------------------------------------\ndouble FEMixtureNormalTraction::Traction(FESurfaceMaterialPoint& mp)\n{\n\tFESurfaceElement& el = *mp.SurfaceElement();\n\n\t// calculate nodal normal tractions\n\tdouble tr = m_traction(mp);\n\n\t// if the prescribed traction is effective, evaluate the total traction\n\tif (m_beffective)\n\t{\n\t\t// fluid pressure\n\t\ttr -= m_psurf->Evaluate(mp, m_dof[3]);\n\t}\n\n\treturn tr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMixtureNormalTraction::LoadVector(FEGlobalVector& R)\n{\n\tm_psurf->SetShellBottom(m_bshellb);\n\n    FEMixtureNormalTraction* traction = this;\n\tm_psurf->LoadVector(R, m_dof, m_blinear, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, vector<double>& fa) {\n\n\t\t// traction at integration points\n\t\tdouble tr = traction->Traction(mp);\n\t\tif (traction->m_bshellb) tr = -tr;\n\n\t\t// force vector\n\t\tvec3d f = (mp.dxr ^ mp.dxs)*tr;\n\n\t\tdouble H = dof_a.shape;\n\t\tfa[0] = H * f.x;\n\t\tfa[1] = H * f.y;\n\t\tfa[2] = H * f.z;\n\t\tfa[3] = 0.0;\n\t});\n}\n"
  },
  {
    "path": "FEBioMix/FEMixtureNormalTraction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This boundary condition applies a mixture normal traction on a surface\n//!\nclass FEBIOMIX_API FEMixtureNormalTraction : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n    FEMixtureNormalTraction(FEModel* pfem);\n\n\tbool Init() override;\n\n\t//! Set the surface to apply the load to\n\tvoid SetSurface(FESurface* ps) override;\n\n\tvoid SetLinear(bool blinear) { m_blinear = blinear; }\n\n\tvoid SetEffective(bool beff) { m_beffective = beff; }\n\n\t//! calculate pressure stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\nprivate:\n\tdouble Traction(FESurfaceMaterialPoint& mp);\n\nprotected:\n\tFEParamDouble\tm_traction;\t\t//!< traction value\n\tbool\tm_blinear;\t\t//!< linear or not (true is non-follower, false is follower)\n    bool    m_bshellb;      //!< flag for prescribing traction on shell bottom\n\tbool\tm_beffective;\t//!< effective or total normal traction\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/log.h>\n#include <FECore/tens4d.h>\n#include <FECore/tools.h>\n#include <complex>\nusing namespace std;\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEMultiphasic, FEMaterial)\n\tADD_PARAMETER(m_phi0   , FE_RANGE_CLOSED     (0.0, 1.0), \"phi0\"         );\n\tADD_PARAMETER(m_rhoTw  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"fluid_density\");\n\tADD_PARAMETER(m_penalty, FE_RANGE_GREATER_OR_EQUAL(0.0), \"penalty\"      );\n\tADD_PARAMETER(m_cFr    , \"fixed_charge_density\");\n\n\t// define the material properties\n\tADD_PROPERTY(m_pSolid , \"solid\"              , FEProperty::Required | FEProperty::TopLevel);\n\tADD_PROPERTY(m_pPerm  , \"permeability\"       );\n\tADD_PROPERTY(m_pOsmC  , \"osmotic_coefficient\");\n\tADD_PROPERTY(m_pSupp  , \"solvent_supply\"     , FEProperty::Optional);\n\tADD_PROPERTY(m_pSolute, \"solute\"             , FEProperty::Optional);\n\tADD_PROPERTY(m_pSBM   , \"solid_bound\"        , FEProperty::Optional);\n\tADD_PROPERTY(m_pReact , \"reaction\"           , FEProperty::Optional);\n    ADD_PROPERTY(m_pMReact, \"membrane_reaction\"  , FEProperty::Optional);\n\n\tADD_PROPERTY(m_Q, \"mat_axis\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//=============================================================================\n//   FEMultiphasic\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! FEMultiphasic constructor\nFEMultiphasic::FEMultiphasic(FEModel* pfem) : FEMaterial(pfem)\n{\t\n\tm_rhoTw = 0;\n\tm_Rgas = 0; m_Tabs = 0; m_Fc = 0;\n\tm_penalty = 1;\n\n\tm_pSolid = 0;\n\tm_pPerm = 0;\n\tm_pOsmC = 0;\n\tm_pSupp = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasic::AddSolidBoundMolecule(FESolidBoundMolecule* psbm)\n{\n\tm_pSBM.push_back(psbm);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasic::AddChemicalReaction(FEChemicalReaction* pcr)\n{\n\tm_pReact.push_back(pcr);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasic::AddMembraneReaction(FEMembraneReaction* pcr)\n{\n    m_pMReact.push_back(pcr);\n}\n\n//-----------------------------------------------------------------------------\n//! Returns the local ID of the SBM, given the global ID.\n//! \\param nid global ID (one - based)\n//! \\return the local ID (zero-based index) or -1 if not found.\nint FEMultiphasic::FindLocalSBMID(int nid)\n{\n\tint lsbm = -1;\n\tint nsbm = (int) SBMs();\n\tfor (int isbm=0; isbm<nsbm; ++isbm) {\n\t\tif (m_pSBM[isbm]->GetSBMID() == nid) {\n\t\t\tlsbm = isbm;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn lsbm;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasic::Init()\n{\n\t// set the solute IDs first, since they are referenced in FESolute::Init()\n\tfor (int i = 0; i<Solutes(); ++i) {\n\t\tm_pSolute[i]->SetSoluteLocalID(i);\n\t}\n\n    if (m_pSolid->Init() == false) return false;\n    if (m_pPerm->Init() == false) return false;\n    if (m_pOsmC->Init() == false) return false;\n    if (m_pSupp && (m_pSupp->Init() == false)) return false;\n    for (int i=0; i<Solutes(); ++i)\n        if (m_pSolute[i]->Init() == false) return false;\n    for (int i=0; i<SBMs(); ++i)\n        if (m_pSBM[i]->Init() == false) return false;\n    for (int i=0; i<Reactions(); ++i)\n        if (m_pReact[i]->Init() == false) return false;\n    for (int i=0; i<MembraneReactions(); ++i)\n        if (m_pMReact[i]->Init() == false) return false;\n\n\t// call the base class.\n\t// This also initializes all properties\n\tif (FEMaterial::Init() == false) return false;\n\n\t// Determine how to solve for the electric potential psi\n\tint isol;\n\tint zmin = 0, zmax = 0, z;\n\tfor (isol=0; isol<(int)m_pSolute.size(); ++isol) {\n\t\tz = m_pSolute[isol]->ChargeNumber();\n\t\tif (z < zmin) zmin = z;\n\t\tif (z > zmax) zmax = z;\n\t}\n\tm_zmin = zmin;\n\tm_ndeg = zmax - zmin;\t// polynomial degree\n\n\tm_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n\tm_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\tm_Fc   = GetFEModel()->GetGlobalConstant(\"Fc\");\n\t\n\tif (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\");\t return false; }\n\tif ((zmin || zmax) && (m_Fc <= 0)) {\n\t\tfeLogError(\"A positive Faraday constant Fc must be defined in Globals section\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// update specialized material points\nvoid FEMultiphasic::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    m_pSolid->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pPerm->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pOsmC->UpdateSpecializedMaterialPoints(mp, tp);\n    if (m_pSupp) m_pSupp->UpdateSpecializedMaterialPoints(mp, tp);\n    for (int i=0; i<Solutes(); ++i)\n        m_pSolute[i]->UpdateSpecializedMaterialPoints(mp, tp);\n    for (int i=0; i<SBMs(); ++i)\n        m_pSBM[i]->UpdateSpecializedMaterialPoints(mp, tp);\n    for (int i=0; i<Reactions(); ++i)\n        m_pReact[i]->UpdateSpecializedMaterialPoints(mp, tp);\n    for (int i=0; i<MembraneReactions(); ++i)\n        m_pMReact[i]->UpdateSpecializedMaterialPoints(mp, tp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasic::Serialize(DumpStream& ar)\n{\n\tFEMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tar & m_Rgas & m_Tabs & m_Fc;\n\tar & m_zmin & m_ndeg;\n}\n\n//-----------------------------------------------------------------------------\n//! Solid referential apparent density\ndouble FEMultiphasic::SolidReferentialApparentDensity(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& pet = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\t\n\t// evaluate referential apparent density of base solid\n\tdouble density = m_pSolid->Density(pt);\n\tdouble rhosr = pet.m_phi0t*density;\n\n\t// add contribution from solid-bound molecules\n\tfor (int isbm=0; isbm<(int)spt.m_sbmr.size(); ++isbm)\n\t\trhosr += spt.m_sbmr[isbm];\n\t\n\treturn rhosr;\n}\n\n//! Return solid referential apparent density\ndouble FEMultiphasic::GetReferentialSolidVolumeFraction(const FEMaterialPoint& pt)\n{\n    const FEBiphasicMaterialPoint& bt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n    return bt.m_phi0t;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and return solid referential volume fraction\ndouble FEMultiphasic::SolidReferentialVolumeFraction(FEMaterialPoint& pt)\n{\n\t// get referential apparent density of base solid (assumed constant)\n\tdouble phisr = m_phi0(pt);\n    \n    // add contribution from solid-bound 'solutes'\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    double f = 0;\n    for (int isol=0; isol<nsol; ++isol)\n        if (spt.m_bsb[isol]) f += spt.m_ca[isol]*m_pSolute[isol]->MolarMass()/m_pSolute[isol]->Density();\n    phisr = (phisr + et.m_J*f)/(1+f);\n    \n\t// add contribution from solid-bound molecules\n\tfor (int isbm=0; isbm<(int)m_pSBM.size(); ++isbm)\n\t\tphisr += SBMReferentialVolumeFraction(pt, isbm);\n    \n    FEBiphasicMaterialPoint& bt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n    bt.m_phi0t = phisr;\n    \n\treturn phisr;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and return tangent of solid referential volume fraction w.r.t. relative volume\ndouble FEMultiphasic::TangentSRVFStrain(FEMaterialPoint& pt)\n{\n    // get referential apparent density of base solid (assumed constant)\n    double phis0 = m_phi0(pt);\n    double phisr = SolidReferentialVolumeFraction(pt);\n    \n    // add contribution from solid-bound 'solutes'\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    double J = et.m_J;\n    FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    double f = 0;\n    double s = 0;\n    for (int isol=0; isol<nsol; ++isol) {\n        if (spt.m_bsb[isol]) {\n            f += spt.m_ca[isol]*m_pSolute[isol]->MolarMass()/m_pSolute[isol]->Density();\n            s += spt.m_ca[isol]*m_pSolute[isol]->MolarMass()/m_pSolute[isol]->Density()*spt.m_dkdJ[isol]/spt.m_k[isol];\n        }\n    }\n    double d = 1+f;\n    double dphisrdJ = f/d + (J-phis0)*s/(d*d);\n    \n    // add contribution from solid-bound molecules\n    f = s = 0;\n    for (int isbm=0; isbm<(int)m_pSBM.size(); ++isbm) {\n        f += spt.m_sbmr[isbm]/m_pSBM[isbm]->Density();\n    }\n    dphisrdJ += f/(J-phisr + f);\n    \n    return dphisrdJ;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate and return tangent of  solid referential volume fraction w.r.t. to concentration\ndouble FEMultiphasic::TangentSRVFConcentration(FEMaterialPoint& pt, const int sol)\n{\n    // get referential apparent density of base solid (assumed constant)\n    double phis0 = m_phi0(pt);\n    double phisr = SolidReferentialVolumeFraction(pt);\n    \n    // add contribution from solid-bound 'solutes'\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    double J = et.m_J;\n    FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n    const int nsol = (int)m_pSolute.size();\n    double f = 0;\n    if (spt.m_bsb[sol]) {\n        for (int isol=0; isol<nsol; ++isol) {\n            if (spt.m_bsb[isol]) {\n                f += spt.m_ca[isol]*spt.m_dkdc[isol][sol]*m_pSolute[isol]->MolarMass()/m_pSolute[isol]->Density();\n                if (isol == sol) f += spt.m_k[isol]*m_pSolute[isol]->MolarMass()/m_pSolute[isol]->Density();\n            }\n        }\n    }\n    double dphisrdc = pow(J-phisr,2)/(J-phis0)*f;\n    \n    return dphisrdc;\n}\n\n\n//-----------------------------------------------------------------------------\n//! Porosity in current configuration\ndouble FEMultiphasic::Porosity(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& bt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// solid referential volume fraction\n\tdouble phisr = bt.m_phi0t;\n    \n\t// relative volume\n\tdouble J = et.m_J;\n\t\n\tdouble phiw = 1 - phisr/J;\n\t// check for pore collapse\n\t// TODO: throw an error if pores collapse\n\tphiw = (phiw > 0) ? phiw : 0;\n\t\n\treturn phiw;\n}\n\n//-----------------------------------------------------------------------------\n//! Fixed charge density in current configuration\ndouble FEMultiphasic::FixedChargeDensity(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& bt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\tdouble phi0 = bt.m_phi0t;\n\tdouble ce = 0;\n\n\t// add contribution from charged solid-bound molecules\n\tfor (int isbm=0; isbm<(int)m_pSBM.size(); ++isbm)\n\t\tce += SBMChargeNumber(isbm)*spt.m_sbmr[isbm]/SBMMolarMass(isbm);\n    \n    double cFr = m_cFr(pt);\n\tdouble cF = (cFr*(1-bt.m_phi0)+ce)/(J-phi0);\n    \n    // add contribution from solid-bound 'solutes'\n    const int nsol = (int)m_pSolute.size();\n    for (int isol=0; isol<nsol; ++isol)\n        if (spt.m_bsb[isol]) cF += spt.m_ca[isol]*m_pSolute[isol]->ChargeNumber();\n\n\treturn cF;\n}\n\n//-----------------------------------------------------------------------------\n//! Electric potential\ndouble FEMultiphasic::ElectricPotential(FEMaterialPoint& pt, const bool eform)\n{\n\t// check if solution is neutral\n\tif (m_ndeg == 0) {\n\t\tif (eform) return 1.0;\n\t\telse return 0.0;\n\t}\n\t\n\tint i, j;\n\t\n\t// if not neutral, solve electroneutrality polynomial for zeta\n\tFESolutesMaterialPoint& set = *pt.ExtractData<FESolutesMaterialPoint>();\n\tconst int nsol = (int)m_pSolute.size();\n\tdouble cF = FixedChargeDensity(pt);\n\n\tvector<double> c(nsol,0);       // effective concentration\n\tvector<double> khat(nsol,1);\t// solubility\n\tvector<int> z(nsol,0);          // charge number\n\tfor (i=0; i<nsol; ++i) {\n        if (!set.m_bsb[i]) {\n            c[i] = set.m_c[i];\n            khat[i] = m_pSolute[i]->m_pSolub->Solubility(pt);\n            z[i] = m_pSolute[i]->ChargeNumber();\n        }\n\t}\n\t\n\t// evaluate polynomial coefficients\n\tconst int n = m_ndeg;\n\tvector<double> a(n+1,0);\n\tif (m_zmin < 0) {\n\t\tfor (i=0; i<nsol; ++i) {\n\t\t\tj = z[i] - m_zmin;\n\t\t\ta[j] += z[i]*khat[i]*c[i];\n\t\t}\n\t\ta[-m_zmin] = cF;\n\t} else {\n\t\tfor (i=0; i<nsol; ++i) {\n\t\t\tj = z[i];\n\t\t\ta[j] += z[i]*khat[i]*c[i];\n\t\t}\n\t\ta[0] = cF;\n\t}\n\n\t// solve polynomial\n\tdouble psi = set.m_psi;\t\t// use previous solution as initial guess\n\tdouble zeta = exp(-m_Fc*psi/m_Rgas/m_Tabs);\n\tif (!solvepoly(n, a, zeta)) {\n\t\tzeta = 1.0;\n\t}\n\t\n\t// Return exponential (non-dimensional) form if desired\n\tif (eform) return zeta;\n\t\n\t// Otherwise return dimensional value of electric potential\n\tpsi = -m_Rgas*m_Tabs/m_Fc*log(zeta);\n\t\n\treturn psi;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficient\ndouble FEMultiphasic::PartitionCoefficient(FEMaterialPoint& pt, const int sol)\n{\n\t\n\t// solubility\n\tdouble khat = m_pSolute[sol]->m_pSolub->Solubility(pt);\n\t// charge number\n\tint z = m_pSolute[sol]->ChargeNumber();\n\t// electric potential\n\tdouble zeta = ElectricPotential(pt, true);\n\tdouble zz = pow(zeta, z);\n\t// partition coefficient\n\tdouble kappa = zz*khat;\n\t\n\treturn kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficients and their derivatives\nvoid FEMultiphasic::PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                                  vector<double>& dkdJ,\n                                                  vector< vector<double> >& dkdc,\n                                                  vector< vector<double> >& dkdr,\n                                                  vector< vector<double> >& dkdJr,\n                                                  vector< vector< vector<double> > >& dkdrc)\n{\n\tint isol, jsol, ksol;\n    int isbm;\n\t\n\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\tFEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\tFESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n\t\n\tconst int nsol = (int)m_pSolute.size();\n    const int nsbm = (int)m_pSBM.size();\n\t\n\tvector<double> c(nsol);\n\tvector<int> z(nsol);\n\tvector<double> khat(nsol);\n\tvector<double> dkhdJ(nsol);\n\tvector<double> dkhdJJ(nsol);\n\tvector< vector<double> > dkhdc(nsol, vector<double>(nsol));\n\tvector< vector<double> > dkhdJc(nsol, vector<double>(nsol));\n\tvector< vector< vector<double> > > dkhdcc(nsol, dkhdc);\t// use dkhdc to initialize only\n\tvector<double> zz(nsol);\n\tkappa.resize(nsol);\n\n\tdouble den = 0;\n    double num = 0;\n\tdouble zeta = ElectricPotential(mp, true);\n\n\tfor (isol=0; isol<nsol; ++isol) {\n\t\t// get the effective concentration, its gradient and its time derivative\n\t\tc[isol] = spt.m_c[isol];\n\t\t// get the charge number\n\t\tz[isol] = m_pSolute[isol]->ChargeNumber();\n\t\t// evaluate the solubility and its derivatives w.r.t. J and c\n\t\tkhat[isol] = m_pSolute[isol]->m_pSolub->Solubility(mp);\n\t\tdkhdJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain(mp);\n\t\tdkhdJJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Strain(mp);\n\t\tfor (jsol=0; jsol<nsol; ++jsol) {\n\t\t\tdkhdc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration(mp,jsol);\n\t\t\tdkhdJc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Concentration(mp,jsol);\n\t\t\tfor (ksol=0; ksol<nsol; ++ksol) {\n\t\t\t\tdkhdcc[isol][jsol][ksol] = \n\t\t\t\tm_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration_Concentration(mp,jsol,ksol);\n\t\t\t}\n\t\t}\n\t\tzz[isol] = pow(zeta, z[isol]);\n\t\tkappa[isol] = zz[isol]*khat[isol];\n\t\tden += SQR(z[isol])*kappa[isol]*c[isol];\n        num += pow((double)z[isol],3)*kappa[isol]*c[isol];\n\t}\n\t\n\t// get the charge density and its derivatives\n\tdouble J = ept.m_J;\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cF = FixedChargeDensity(mp);\n\tdouble dcFdJ = -cF/(J - phi0);\n\tdouble dcFdJJ = 2*cF/SQR(J-phi0);\n\t\n\t// evaluate electric potential (nondimensional exponential form) and its derivatives\n\t// also evaluate partition coefficients and their derivatives\n\tdouble zidzdJ = 0;\n\tdouble zidzdJJ = 0, zidzdJJ1 = 0, zidzdJJ2 = 0;\n\tvector<double> zidzdc(nsol,0);\n\tvector<double> zidzdJc(nsol,0), zidzdJc1(nsol,0), zidzdJc2(nsol,0);\n\tvector< vector<double> > zidzdcc(nsol, vector<double>(nsol,0));\n\tvector< vector<double> > zidzdcc1(nsol, vector<double>(nsol,0));\n\tvector<double> zidzdcc2(nsol,0);\n\tdouble zidzdcc3 = 0;\n\n\tif (den > 0) {\n\t\t\n\t\tfor (isol=0; isol<nsol; ++isol)\n\t\t\tzidzdJ += z[isol]*zz[isol]*dkhdJ[isol]*c[isol];\n\t\tzidzdJ = -(dcFdJ+zidzdJ)/den;\n\t\t\n\t\tfor (isol=0; isol<nsol; ++isol) {\n\t\t\tfor (jsol=0; jsol<nsol; ++jsol) {\n\t\t\t\tzidzdJJ1 += SQR(z[jsol])*c[jsol]*(z[jsol]*zidzdJ*kappa[jsol]+zz[jsol]*dkhdJ[jsol]);\n\t\t\t\tzidzdJJ2 += z[jsol]*zz[jsol]*c[jsol]*(zidzdJ*z[jsol]*dkhdJ[jsol]+dkhdJJ[jsol]);\n\t\t\t\tzidzdc[isol] += z[jsol]*zz[jsol]*dkhdc[jsol][isol]*c[jsol];\n\t\t\t}\n\t\t\tzidzdc[isol] = -(z[isol]*kappa[isol]+zidzdc[isol])/den;\n\t\t\tzidzdcc3 += pow(double(z[isol]),3)*kappa[isol]*c[isol];\n\t\t}\n\t\tzidzdJJ = zidzdJ*(zidzdJ-zidzdJJ1/den)-(dcFdJJ+zidzdJJ2)/den;\n\t\t\n\t\tfor (isol=0; isol<nsol; ++isol) {\n\t\t\tfor (jsol=0; jsol<nsol; ++jsol) {\n\t\t\t\tzidzdJc1[isol] += SQR(z[jsol])*c[jsol]*(zidzdc[isol]*z[jsol]*kappa[jsol]+zz[jsol]*dkhdc[jsol][isol]);\n\t\t\t\tzidzdJc2[isol] += z[jsol]*zz[jsol]*c[jsol]*(zidzdc[isol]*z[jsol]*dkhdJ[jsol]+dkhdJc[jsol][isol]);\n\t\t\t\tzidzdcc2[isol] += SQR(z[jsol])*zz[jsol]*c[jsol]*dkhdc[jsol][isol];\n\t\t\t\tfor (ksol=0; ksol<nsol; ++ksol)\n\t\t\t\t\tzidzdcc1[isol][jsol] += z[ksol]*zz[ksol]*c[ksol]*dkhdcc[ksol][isol][jsol];\n\t\t\t}\n\t\t\tzidzdJc[isol] = zidzdJ*(zidzdc[isol]-(SQR(z[isol])*kappa[isol] + zidzdJc1[isol])/den)\n\t\t\t-(z[isol]*zz[isol]*dkhdJ[isol] + zidzdJc2[isol])/den;\n\t\t}\n\t\t\n\t\tfor (isol=0; isol<nsol; ++isol) {\n\t\t\tfor (jsol=0; jsol<nsol; ++jsol) {\n\t\t\t\tzidzdcc[isol][jsol] = zidzdc[isol]*zidzdc[jsol]*(1 - zidzdcc3/den)\n\t\t\t\t- zidzdcc1[isol][jsol]/den\n\t\t\t\t- z[isol]*(z[isol]*kappa[isol]*zidzdc[jsol]+zz[isol]*dkhdc[isol][jsol])/den\n\t\t\t\t- z[jsol]*(z[jsol]*kappa[jsol]*zidzdc[isol]+zz[jsol]*dkhdc[jsol][isol])/den\n\t\t\t\t- zidzdc[jsol]*zidzdcc2[isol]/den\n\t\t\t\t- zidzdc[isol]*zidzdcc2[jsol]/den;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tdkdJ.resize(nsol);\n\tdkdc.resize(nsol, vector<double>(nsol,0));\n\t\n\tfor (isol=0; isol<nsol; ++isol) {\n\t\tdkdJ[isol] = zz[isol]*dkhdJ[isol]+z[isol]*kappa[isol]*zidzdJ;\n\t\tfor (jsol=0; jsol<nsol; ++jsol) {\n\t\t\tdkdc[isol][jsol] = zz[isol]*dkhdc[isol][jsol]+z[isol]*kappa[isol]*zidzdc[jsol];\n\t\t}\n\t}\n    vector<double> zidzdr(nsbm,0);\n    vector<double> zidzdJr(nsbm,0);\n\tvector< vector<double> > zidzdrc(nsbm, vector<double>(nsol,0));\n\tdkdr.resize(nsol, vector<double>(nsbm));\n\tdkdJr.resize(nsol, vector<double>(nsbm));\n\tdkdrc.resize(nsol, zidzdrc);\t// use zidzdrc for initialization only\n    \n\tif (den > 0) {\n\t\t\n        for (isbm=0; isbm<nsbm; ++isbm) {\n            zidzdr[isbm] = -(cF/SBMDensity(isbm) + SBMChargeNumber(isbm)/SBMMolarMass(isbm))/(J-phi0)/den;\n            \n            for (isol=0; isol<nsol; ++isol) {\n                zidzdJr[isbm] += SQR(z[isol])*dkdJ[isol]*c[isol];\n            }\n            zidzdJr[isbm] = 1/(J-phi0) + zidzdJr[isbm]/den;\n            zidzdJr[isbm] = (zidzdJr[isbm] + zidzdJ)*zidzdr[isbm];\n            zidzdJr[isbm] += cF/SBMDensity(isbm)/SQR(J-phi0)/den;\n            \n            for (isol=0; isol<nsol; ++isol) {\n                zidzdrc[isbm][isol] = SQR(z[isol])*kappa[isol];\n                for (jsol=0; jsol<nsol; ++jsol)\n                    zidzdrc[isbm][isol] += SQR(z[jsol])*zz[jsol]*c[jsol]*dkhdc[jsol][isol];\n                zidzdrc[isbm][isol] = zidzdr[isbm]*(zidzdc[isol]*(1+num/den) - zidzdrc[isbm][isol]/den);\n            }\n        }\n\t}\n    \n    for (isbm=0; isbm<nsbm; ++isbm) {\n        for (isol=0; isol<nsol; ++isol) {\n            dkdr[isol][isbm] = z[isol]*kappa[isol]*zidzdr[isbm];\n            dkdJr[isol][isbm] = z[isol]*((dkhdJ[isol]*zz[isol]+(z[isol]-1)*kappa[isol]*zidzdJ)*zidzdr[isbm]\n                                         +kappa[isol]*zidzdJr[isbm]);\n            for (jsol=0; jsol<nsol; ++jsol) {\n                dkdrc[isol][isbm][jsol] = z[isol]*(zz[isol]*dkhdc[isol][jsol]*zidzdr[isbm]\n                                                   +(z[isol]-1)*kappa[isol]*zidzdr[isbm]*zidzdc[jsol]\n                                                   +kappa[isol]*zidzdrc[isbm][jsol]);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FEMultiphasic::Concentration(FEMaterialPoint& pt, const int sol)\n{\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// effective concentration\n\tdouble c = spt.m_c[sol];\n\t\n\t// partition coefficient\n\tdouble kappa = PartitionCoefficient(pt, sol);\n\t\n\t// actual concentration\n\tdouble ca = kappa*c;\n\t\n\treturn ca;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a triphasic material is the sum of the fluid pressure\n//! and the elastic stress. Note that this function is declared in the base class\n//! so you do not have to reimplement it in a derived class, unless additional\n//! pressure terms are required.\n\nmat3ds FEMultiphasic::Stress(FEMaterialPoint& mp)\n{\n\t// calculate solid material stress\n\tmat3ds s = m_pSolid->Stress(mp);\n\t\n\t// fluid pressure\n\tdouble p = Pressure(mp);\n\t\n\t// add fluid pressure\n\ts.xx() -= p;\n\ts.yy() -= p;\n\ts.zz() -= p;\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent is the elastic tangent. Note\n//! that this function is declared in the base class, so you don't have to \n//! reimplement it unless additional tangent components are required.\n\ntens4ds FEMultiphasic::Tangent(FEMaterialPoint& mp)\n{\n\tint i;\n\t\n\tFEElasticMaterialPoint& ept = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& bpt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\tconst int nsol = (int)m_pSolute.size();\n\t\n\t// call solid tangent routine\n\ttens4ds C = m_pSolid->Tangent(mp);\n\tdouble D[6][6] = {0};\n\tC.extract(D);\n\t\n\t// relative volume and solid volume fraction\n\tdouble J = ept.m_J;\n\tdouble phi0 = bpt.m_phi0t;\n\t\n\t// get the charge density and its derivatives\n\tdouble cF = FixedChargeDensity(mp);\n\tdouble dcFdJ = -cF/(J - phi0);\n\t\n\t// fluid pressure and solute concentration\n\tdouble p = Pressure(mp);\n\t\n\t// get remaining variables\n\tdouble zeta = ElectricPotential(mp, true);\n\tvector<double> c(nsol);\n\tvector<int> z(nsol);\n\tvector<double> khat(nsol);\n\tvector<double> dkhdJ(nsol);\n\tvector<double> zz(nsol);\n\tvector<double> kappa(nsol);\n\tdouble den = 0;\n\tfor (i=0; i<nsol; ++i) {\n\t\tc[i] = spt.m_c[i];\n\t\tz[i] = m_pSolute[i]->ChargeNumber();\n\t\tkhat[i] = m_pSolute[i]->m_pSolub->Solubility(mp);\n\t\tdkhdJ[i] = m_pSolute[i]->m_pSolub->Tangent_Solubility_Strain(mp);\n\t\tzz[i] = pow(zeta, z[i]);\n\t\tkappa[i] = zz[i]*khat[i];\n\t\tden += SQR(z[i])*kappa[i]*c[i];\n\t}\n\t\n\t// evaluate electric potential (nondimensional exponential form) and its derivatives\n\t// also evaluate partition coefficients and their derivatives\n\tdouble zidzdJ = 0;\n\tif (den > 0) {\n\t\tzidzdJ = dcFdJ;\n\t\tfor (i=0; i<nsol; ++i)\n\t\t\tzidzdJ += z[i]*zz[i]*dkhdJ[i]*c[i];\n\t\tzidzdJ = -zidzdJ/den;\n\t}\n\tvector<double> dkdJ(nsol);\n\tfor (i=0; i<nsol; ++i) dkdJ[i] = zz[i]*dkhdJ[i]+z[i]*kappa[i]*zidzdJ;\n\t\n\t// osmotic coefficient and its derivative w.r.t. strain\n\tdouble osmc = m_pOsmC->OsmoticCoefficient(mp);\n\tdouble dodJ = m_pOsmC->Tangent_OsmoticCoefficient_Strain(mp);\n\t\n\tdouble dp = 0;\n\tfor (i=0; i<nsol; ++i) {\n\t\tdp += c[i]*(osmc*dkdJ[i]+dodJ*kappa[i]);\n\t}\n\tdp *= m_Rgas*m_Tabs*J;\n\t\n\t// adjust tangent for pressures\n\tD[0][0] -= -p + dp;\n\tD[1][1] -= -p + dp;\n\tD[2][2] -= -p + dp;\n\t\n\tD[0][1] -= p + dp; D[1][0] -= p + dp;\n\tD[1][2] -= p + dp; D[2][1] -= p + dp;\n\tD[0][2] -= p + dp; D[2][0] -= p + dp;\n\t\n\tD[3][3] -= -p;\n\tD[4][4] -= -p;\n\tD[5][5] -= -p;\n\t\n\treturn tens4ds(D);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate fluid flux\n\nvec3d FEMultiphasic::FluidFlux(FEMaterialPoint& pt)\n{\n\tint i;\n\t\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\tconst int nsol = (int)m_pSolute.size();\n\tvector<double> c(nsol);\n\tvector<vec3d> gradc(nsol);\n\tvector<mat3ds> D(nsol);\n\tvector<double> D0(nsol);\n\tvector<double> khat(nsol);\n\tvector<int> z(nsol);\n\tvector<double> zz(nsol);\n\tvector<double> kappa(nsol);\n\t\n\t// fluid volume fraction (porosity) in current configuration\n\tdouble phiw = Porosity(pt);\n\t\n\t// pressure gradient\n\tvec3d gradp = ppt.m_gradp;\n\t\n\t// electric potential\n\tdouble zeta = ElectricPotential(pt, true);\n\t\n\t\n\tfor (i=0; i<nsol; ++i) {\n\t\t// concentration\n\t\tc[i] = spt.m_c[i];\n\n\t\t// concentration gradient\n\t\tgradc[i] = spt.m_gradc[i];\n\t\t\n\t\t// solute diffusivity in mixture\n\t\tD[i] = m_pSolute[i]->m_pDiff->Diffusivity(pt);\n\t\t\n\t\t// solute free diffusivity\n\t\tD0[i] = m_pSolute[i]->m_pDiff->Free_Diffusivity(pt);\n\t\t\n\t\t// solubility\n\t\tkhat[i] = m_pSolute[i]->m_pSolub->Solubility(pt);\n\t\tz[i] = m_pSolute[i]->ChargeNumber();\n\t\tzz[i] = pow(zeta, z[i]);\n\t\tkappa[i] = zz[i]*khat[i];\n\t}\n\t\n\t// identity matrix\n\tmat3dd I(1);\n\t\n\t// hydraulic permeability\n\tmat3ds kt = m_pPerm->Permeability(pt);\n\t\n\t// effective hydraulic permeability\n\tmat3ds ke;\n\tke.zero();\n\tfor (i=0; i<nsol; ++i)\n\t\tke += (I-D[i]/D0[i])*(kappa[i]*c[i]/D0[i]);\n\tke = (kt.inverse() + ke*(m_Rgas*m_Tabs/phiw)).inverse();\n\t\n\t// fluid flux w\n\tvec3d w(0,0,0);\n\tfor (i=0; i<nsol; ++i)\n\t\tw += (D[i]*gradc[i])*(kappa[i]/D0[i]);\n\tw = -(ke*(gradp + w*m_Rgas*m_Tabs));\n\t\n\treturn w;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate solute molar flux\n\nvec3d FEMultiphasic::SoluteFlux(FEMaterialPoint& pt, const int sol)\n{\n\tFEBiphasicMaterialPoint& bpt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// fluid volume fraction (porosity) in current configuration\n\tdouble phiw = Porosity(pt);\n\t\n\t// concentration\n\tdouble c = spt.m_c[sol];\n\t\n\t// concentration gradient\n\tvec3d gradc = spt.m_gradc[sol];\n\t\n\t// solute diffusivity in mixture\n\tmat3ds D = m_pSolute[sol]->m_pDiff->Diffusivity(pt);\n\t\n\t// solute free diffusivity\n\tdouble D0 = m_pSolute[sol]->m_pDiff->Free_Diffusivity(pt);\n\t\n\t// solubility\n\tdouble khat = m_pSolute[sol]->m_pSolub->Solubility(pt);\n\tint z = m_pSolute[sol]->ChargeNumber();\n\tdouble zeta = ElectricPotential(pt, true);\n\tdouble zz = pow(zeta, z);\n\tdouble kappa = zz*khat;\n\t\n\t// fluid flux w\n\tvec3d w = bpt.m_w;\n\t\n\t// solute flux j\n\tvec3d j = (D*(w*(c/D0) - gradc*phiw))*kappa;\n\t\n\treturn j;\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure\ndouble FEMultiphasic::Pressure(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n    FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\tconst int nsol = (int)m_pSolute.size();\n\t\n\t// effective pressure\n\tdouble p = ppt.m_p;\n\t\n\t// osmolarity\n    double c = spt.Osmolarity();\n\t\n\t// osmotic coefficient\n\tdouble osmc = m_pOsmC->OsmoticCoefficient(pt);\n\t\n\t// actual pressure\n\tdouble pa = p + m_Rgas*m_Tabs*osmc*c;\n\t\n\treturn pa;\n}\n\n//-----------------------------------------------------------------------------\n//! Current density\nvec3d FEMultiphasic::CurrentDensity(FEMaterialPoint& pt)\n{\n\tint i;\n\tconst int nsol = (int)m_pSolute.size();\n\t\n\tvector<vec3d> j(nsol);\n\tvector<int> z(nsol);\n\tvec3d Ie(0,0,0);\n\tfor (i=0; i<nsol; ++i) {\n\t\tj[i] = SoluteFlux(pt, i);\n\t\tz[i] = m_pSolute[i]->ChargeNumber();\n\t\tIe += j[i]*z[i];\n\t}\n\tIe *= m_Fc;\n\t\n\treturn Ie;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate effective permeability\nmat3ds FEMultiphasic::EffectivePermeability(FEMaterialPoint& pt)\n{\n    // evaluate the hydraulic permeability\n    mat3ds K = GetPermeability()->Permeability(pt);\n\n    const int nsol = Solutes();\n    \n    // if there are no solutes in this mixture, we're done\n    if (nsol == 0) return K;\n\n    // initialize effective permeability\n    mat3ds Ke = K.inverse();\n    \n    // fluid volume fraction (porosity) in current configuration\n    double phiw = Porosity(pt);\n    double tmp = m_Rgas*m_Tabs/phiw;\n    mat3dd I(1.0);\n\n    FESolutesMaterialPoint&  spt = *(pt.ExtractData<FESolutesMaterialPoint >());\n\n    // add solute contributions (but not 'solid-bound' solutes)\n    for (int isol=0; isol<nsol; ++isol) {\n        if (!spt.m_bsb[isol]) {\n            // concentration\n            double ca = spt.m_ca[isol];\n            // solute diffusivity in mixture\n            mat3ds D = m_pSolute[isol]->m_pDiff->Diffusivity(pt);\n            // solute free diffusivity\n            double D0 = m_pSolute[isol]->m_pDiff->Free_Diffusivity(pt);\n            \n            Ke += (I - D/D0)*(tmp*ca/D0);\n        }\n    }\n        \n    return Ke.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate tangent of effective permeability w.r.t. strain\ntens4dmm FEMultiphasic::TangentPermeabilityStrain(FEMaterialPoint& pt, const mat3ds& Ke)\n{\n    // get the hydraulic permeability strain tangent\n    tens4dmm dKdE = GetPermeability()->Tangent_Permeability_Strain(pt);\n    \n    const int nsol = Solutes();\n    \n    // if there are no solutes in this mixture, we're done\n    if (nsol == 0) return dKdE;\n    \n    // evaluate the inverse of the hydraulic permeability\n    mat3ds Ki = GetPermeability()->Permeability(pt).inverse();\n    \n    // fluid volume fraction (porosity) in current configuration\n    double phiw = Porosity(pt);\n    mat3dd I(1.0);\n    \n    FEElasticMaterialPoint&  ept = *(pt.ExtractData<FEElasticMaterialPoint >());\n    FESolutesMaterialPoint&  spt = *(pt.ExtractData<FESolutesMaterialPoint >());\n    \n    tens4dmm dKedE;\n    dKedE.zero();\n    \n    // add solute contributions\n    for (int isol=0; isol<nsol; ++isol) {\n        // concentration\n        double ca = spt.m_ca[isol];\n        // solute free diffusivity\n        double D0 = m_pSolute[isol]->m_pDiff->Free_Diffusivity(pt);\n        // solute diffusivity in mixture, normalized by D0\n        mat3ds D = m_pSolute[isol]->m_pDiff->Diffusivity(pt)/D0;\n        // solute diffusiviety strain tangent, normalized by D0\n        tens4dmm dDdE = m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Strain(pt)/D0;\n        \n        dKedE += (dDdE + (dyad4mm(I, D) + dyad4mm(D,I) - dyad4mm(I,I))*2\n        - dyad1mm(D,I) + dyad1mm(I-D,I)*(1./phiw - ept.m_J*spt.m_dkdJ[isol]/spt.m_k[isol]))*ca/D0;\n    }\n    \n    dKedE = dKedE*(m_Rgas*m_Tabs/phiw) + ddot(dyad2mm(Ki,Ki), dKdE);\n    \n    return ddot(dyad2mm(Ke, Ke), dKedE);\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate tangent of effective permeability w.r.t. concentration\nmat3ds FEMultiphasic::TangentPermeabilityConcentration(FEMaterialPoint& pt, const int sol, const mat3ds& Ke)\n{\n    mat3ds dKedc(0,0,0,0,0,0);\n    \n    const int nsol = Solutes();\n    \n    if (nsol == 0) return dKedc;\n    \n    // fluid volume fraction (porosity) in current configuration\n    double phiw = Porosity(pt);\n    mat3dd I(1.0);\n    \n    FESolutesMaterialPoint&  spt = *(pt.ExtractData<FESolutesMaterialPoint >());\n    \n    // add solute contributions\n    for (int isol=0; isol<nsol; ++isol) {\n        // concentration\n        double ca = spt.m_ca[isol];\n        // solute free diffusivity\n        double D0 = m_pSolute[isol]->m_pDiff->Free_Diffusivity(pt);\n        // solute diffusivity in mixture, normalized by D0\n        mat3ds D = m_pSolute[isol]->m_pDiff->Diffusivity(pt)/D0;\n        // solute free diffusivity concentration tangent\n        double dD0dc = m_pSolute[isol]->m_pDiff->Tangent_Free_Diffusivity_Concentration(pt, sol);\n        // solute diffusivity concentration tangent\n        mat3ds dDdc = m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Concentration(pt,sol);\n        \n        double kd = (isol == sol) ? 1 : 0;\n        dKedc += (I-D)*((spt.m_dkdc[isol][sol]*spt.m_c[isol] + spt.m_k[isol]*kd - dD0dc*ca/D0)/D0)\n        - (dDdc - D*dD0dc)*(ca/D0/D0);\n    }\n    \n    dKedc *= m_Rgas*m_Tabs/phiw;\n    \n    return -(Ke*dKedc*Ke).sym();\n}\n\ndouble FEMultiphasic::GetReferentialFixedChargeDensity(const FEMaterialPoint& mp)\n{\n\tconst FEElasticMaterialPoint* ept = (mp.ExtractData<FEElasticMaterialPoint >());\n\tconst FEBiphasicMaterialPoint* bpt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\tconst FESolutesMaterialPoint* spt = (mp.ExtractData<FESolutesMaterialPoint >());\n\tdouble cf = (ept->m_J - bpt->m_phi0t) * spt->m_cF / (1 - bpt->m_phi0);\n\treturn cf;\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FESolute.h\"\n#include \"FEOsmoticCoefficient.h\"\n#include \"FEChemicalReaction.h\"\n#include \"FEMembraneReaction.h\"\n#include \"FESoluteInterface.h\"\n#include <FECore/FEModelParam.h>\n#include <FECore/FEShellElement.h>\n\n//-----------------------------------------------------------------------------\n//! Base class for multiphasic materials.\n\nclass FEBIOMIX_API FEMultiphasic : public FEMaterial, public FEBiphasicInterface, public FESoluteInterface_T<FESolutesMaterialPoint>\n{\npublic:\n\t//! constructor\n\tFEMultiphasic(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n    \n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// return elastic material component\n\tFEElasticMaterial* GetElasticMaterial() { return m_pSolid; }\n\n    //! Update solid bound molecules\n    virtual void UpdateSolidBoundMolecules(FEMaterialPoint& mp) = 0;\n\npublic:\n\t\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt);\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt);\n\t\n\t//! calculate fluid (solvent) flux\n\tvec3d FluidFlux(FEMaterialPoint& pt);\n\t\n\t//! calculate solute molar flux\n\tvec3d SoluteFlux(FEMaterialPoint& pt, const int sol);\n\t\n\t//! actual fluid pressure (as opposed to effective pressure)\n\tdouble Pressure(FEMaterialPoint& pt);\n\n\t//! partition coefficient\n\tdouble PartitionCoefficient(FEMaterialPoint& pt, const int sol);\n\t\n\t//! partition coefficients and their derivatives\n\tvoid PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n\t\t\t\t\t\t\t\t\t   vector<double>& dkdJ,\n\t\t\t\t\t\t\t\t\t   vector< vector<double> >& dkdc,\n                                       vector< vector<double> >& dkdr,\n                                       vector< vector<double> >& dkdJr,\n                                       vector< vector< vector<double> > >& dkdrc);\n\t\n    //! return solid referential apparent density\n    double GetReferentialSolidVolumeFraction(const FEMaterialPoint& mp) override;\n    \n\t//! evaluate and return solid referential apparent density\n\tdouble SolidReferentialApparentDensity(FEMaterialPoint& pt) override;\n\n\t//! evaluate and return solid referential volume fraction\n\tdouble SolidReferentialVolumeFraction(FEMaterialPoint& pt) override;\n\n    //! evaluate and return tangent of  solid referential volume fraction w.r.t. to J (volume ratio)\n    double TangentSRVFStrain(FEMaterialPoint& pt) override;\n    \n    //! evaluate and return tangent of  solid referential volume fraction w.r.t. to concentration\n    double TangentSRVFConcentration(FEMaterialPoint& pt, const int sol);\n\n    //! actual concentration (as opposed to effective concentration)\n\tdouble Concentration(FEMaterialPoint& pt, const int sol);\n\n\t//! porosity\n\tdouble Porosity(FEMaterialPoint& pt);\n\n\t//! fixed charge density\n\tvirtual double FixedChargeDensity(FEMaterialPoint& pt);\n\n\t//! electric potential\n\tdouble ElectricPotential(FEMaterialPoint& pt, const bool eform = false);\n\n\t//! current density\n\tvec3d CurrentDensity(FEMaterialPoint& pt);\n\n\t//! fluid true density\n\tdouble FluidDensity() { return m_rhoTw; }\n\n\t//! solute density\n\tdouble SoluteDensity(const int sol) { return m_pSolute[sol]->Density(); }\n\n\t//! solute molar mass\n\tdouble SoluteMolarMass(const int sol) { return m_pSolute[sol]->MolarMass(); }\n\n\t//! solute charge number\n\tint SoluteChargeNumber(const int sol) { return m_pSolute[sol]->ChargeNumber(); }\n\n\t//! SBM density\n\tdouble SBMDensity(const int sbm) { return m_pSBM[sbm]->Density(); }\n\n\t//! SBM molar mass\n\tdouble SBMMolarMass(const int sbm) { return m_pSBM[sbm]->MolarMass(); }\n\n\t//! SBM charge number\n\tint SBMChargeNumber(const int sbm) { return m_pSBM[sbm]->ChargeNumber(); }\n\n\t//! SBM actual concentration (molar concentration per fluid volume in current configuration)\n\tdouble SBMConcentration(FEMaterialPoint& pt, const int sbm) override {\n\t\tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\t\tFEBiphasicMaterialPoint& bpt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\treturn spt.m_sbmr[sbm] / (ept.m_J - bpt.m_phi0t) / SBMMolarMass(sbm);\n\t}\n\n\t//! SBM areal concentration (mole per shell area) -- should only be called from shell domains\n\tdouble SBMArealConcentration(FEMaterialPoint& pt, const int sbm) override {\n\t\tFEShellElement* sel = dynamic_cast<FEShellElement*>(pt.m_elem);\n\t\tassert(sel);\n\t\tdouble h = sel->Evaluate(sel->m_ht, pt.m_index);   // shell thickness\n\t\tFEElasticMaterialPoint& ept = *pt.ExtractData<FEElasticMaterialPoint>();\n\t\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\treturn spt.m_sbmr[sbm] / SBMMolarMass(sbm) * h / ept.m_J;\n\t}\n\n    // return the number of solutes on external side\n    int SolutesExternal(FEMaterialPoint& pt) override {\n        FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n        return (int)spt.m_ce.size();\n    }\n    \n    // return the number of solutes on internal side\n    int SolutesInternal(FEMaterialPoint& pt) override {\n        FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n        return (int)spt.m_ci.size();\n    }\n    \n    //! return the solute ID on external side\n    int GetSoluteIDExternal(FEMaterialPoint& mp, int soluteIndex) override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_ide[soluteIndex];\n    }\n    \n    //! return the solute ID on internal side\n    int GetSoluteIDInternal(FEMaterialPoint& mp, int soluteIndex) override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_idi[soluteIndex];\n    }\n    \n    //! return the effective solute concentration on external side\n    double GetEffectiveSoluteConcentrationExternal(FEMaterialPoint& mp, int soluteIndex) override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_ce[soluteIndex];\n    }\n    \n    //! return the effective solute concentration on internal side\n    double GetEffectiveSoluteConcentrationInternal(FEMaterialPoint& mp, int soluteIndex)  override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_ci[soluteIndex];\n    }\n    \n    //! return the effective pressure on external side\n    double GetEffectiveFluidPressureExternal(FEMaterialPoint& mp) override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_pe;\n    }\n    \n    //! return the effective pressure on internal side\n    double GetEffectiveFluidPressureInternal(FEMaterialPoint& mp) override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_pi;\n    }\n    \n    //! return the membrane areal strain\n    double GetMembraneArealStrain(FEMaterialPoint& mp) override {\n        FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n        return spt.m_strain;\n    }\n    \n\t//! SBM referential volume fraction\n\tdouble SBMReferentialVolumeFraction(FEMaterialPoint& pt, const int sbm) {\n\t\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\treturn spt.m_sbmr[sbm] / SBMDensity(sbm);\n\t}\n\n\t//! find local SBM ID from global one\n\tint FindLocalSBMID(int nid);\n\n\t//! Add a solid bound molecule\n\tvoid AddSolidBoundMolecule(FESolidBoundMolecule* psbm);\n\n\t//! Add a chemical reaction\n\tvoid AddChemicalReaction(FEChemicalReaction* pcr);\n\n\t//! Add a membrane reaction\n\tvoid AddMembraneReaction(FEMembraneReaction* pcr);\n\npublic: // solute interface\n\tdouble GetReferentialFixedChargeDensity(const FEMaterialPoint& mp) override;\n\n\tdouble GetFixedChargeDensity(const FEMaterialPoint& mp) override {\n\t\tconst FESolutesMaterialPoint* spt = (mp.ExtractData<FESolutesMaterialPoint>());\n\t\treturn spt->m_cF;\n\t}\n\npublic:\n\t//! Evaluate effective permeability\n\tmat3ds EffectivePermeability(FEMaterialPoint& pt);\n\ttens4dmm TangentPermeabilityStrain(FEMaterialPoint& pt, const mat3ds& Ke);\n\tmat3ds TangentPermeabilityConcentration(FEMaterialPoint& pt, const int sol, const mat3ds& Ke);\n\n\t// solute interface\npublic:\n\tint Solutes() override { return (int)m_pSolute.size(); }\n\tFESolute* GetSolute(int i) override { return m_pSolute[i]; }\n\npublic:\n\tFEElasticMaterial* GetSolid() { return m_pSolid; }\n\tFEHydraulicPermeability* GetPermeability() { return m_pPerm; }\n\tFEOsmoticCoefficient* GetOsmoticCoefficient() override { return m_pOsmC; }\n\tFESolventSupply* GetSolventSupply() { return m_pSupp; }\n\tFESolidBoundMolecule* GetSBM(int i) override { return m_pSBM[i]; }\n\tFEChemicalReaction* GetReaction(int i) { return m_pReact[i]; }\n\tFEMembraneReaction* GetMembraneReaction(int i) { return m_pMReact[i]; }\n\npublic: // From FESoluteInterface\n\tint SBMs() const override  { return (int)m_pSBM.size(); }\n\npublic:\n\tint Reactions() { return (int)m_pReact.size(); }\n\tint MembraneReactions() { return (int)m_pMReact.size(); }\n\npublic: // parameters\n\tFEParamDouble       m_phi0;     //!< solid volume fraction in reference configuration\n\tFEParamDouble       m_cFr;      //!< fixed charge density in reference configurations\n\tdouble              m_rhoTw;    //!< true fluid density\n\tdouble              m_penalty;  //!< penalty for enforcing electroneutrality\n\npublic:\n\tdouble\tm_Rgas;\t\t\t//!< universal gas constant\n\tdouble\tm_Tabs;\t\t\t//!< absolute temperature\n\tdouble\tm_Fc;\t\t\t//!< Faraday's constant\n\tint\t\tm_zmin;\t\t\t//!< minimum charge number in mixture\n\tint\t\tm_ndeg;\t\t\t//!< polynomial degree of zeta in electroneutrality\n\nprotected:\n\t// material properties\n\tFEElasticMaterial* m_pSolid;\t\t//!< pointer to elastic solid material\n\tFEHydraulicPermeability* m_pPerm;\t\t//!< pointer to permeability material\n\tFEOsmoticCoefficient* m_pOsmC;\t\t//!< pointer to osmotic coefficient material\n\tFESolventSupply* m_pSupp;\t\t//!< pointer to solvent supply material\n\tstd::vector<FESolute*>\t\t\t\tm_pSolute;\t\t//!< pointer to solute materials\n\tstd::vector<FESolidBoundMolecule*>\tm_pSBM;\t\t\t//!< pointer to solid-bound molecule materials\n\tstd::vector<FEChemicalReaction*>\tm_pReact;\t\t//!< pointer to chemical reactions\n\tstd::vector<FEMembraneReaction*>    m_pMReact;      //!< pointer to membrane reactions\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEMultiphasicAnalysis.h\"\n\nBEGIN_FECORE_CLASS(FEMultiphasicAnalysis, FEAnalysis)\n\t// The analysis parameter is already defined in the FEAnalysis base class. \n\t// Here, we just need to set the enum values for the analysis parameter.\n\tFindParameterFromData(&m_nanalysis)->setEnums(\"STEADY-STATE\\0TRANSIENT\\0\");\nEND_FECORE_CLASS()\n\nFEMultiphasicAnalysis::FEMultiphasicAnalysis(FEModel* fem) : FEAnalysis(fem)\n{\n\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEAnalysis.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEMultiphasicAnalysis : public FEAnalysis\n{\npublic:\n\tenum MultiphasicAnalysisType {\n\t\tSTEADY_STATE,\n\t\tTRANSIENT\n\t};\n\npublic:\n\tFEMultiphasicAnalysis(FEModel* fem);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicDomain.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFEMultiphasicDomain::FEMultiphasicDomain(FEModel* pfem) : FEElasticDomain(pfem)\n{\n    m_pMat = 0;\n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    m_dofQ = (pfem ? pfem->GetDOFIndex(\"q\") : -1);\n    m_dofC = (pfem ? pfem->GetDOFIndex(\"concentration\", 0) : -1);\n    m_dofD = (pfem ? pfem->GetDOFIndex(\"shell concentration\", 0) : -1);\n    \n    m_breset = false;\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FESolidDomain.h\"\n#include \"FEMultiphasic.h\"\n#include \"FEBioMech/FEElasticDomain.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalVector;\nclass FEBodyForce;\nclass FESolver;\n\n//-----------------------------------------------------------------------------\n//! Abstract interface class for multiphasic domains.\n\n//! A biphasic domain is used by the biphasic solver.\n//! This interface defines the functions that have to be implemented by a\n//! biphasic domain. There are basically two categories: residual functions\n//! that contribute to the global residual vector. And stiffness matrix\n//! function that calculate contributions to the global stiffness matrix.\nclass FEBIOMIX_API FEMultiphasicDomain : public FEElasticDomain\n{\npublic:\n    FEMultiphasicDomain(FEModel* pfem);\n    virtual ~FEMultiphasicDomain(){}\n    \n    // --- R E S I D U A L ---\n    \n    //! internal work for steady-state case\n    virtual void InternalForcesSS(FEGlobalVector& R) = 0;\n    \n    // --- S T I F F N E S S   M A T R I X ---\n    \n    //! calculates the global stiffness matrix for this domain\n    virtual void StiffnessMatrix(FELinearSystem& LS, bool bsymm) = 0;\n    \n    //! calculates the global stiffness matrix (steady-state case)\n    virtual void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) = 0;\n    \nprotected:\n    FEMultiphasic*      m_pMat;\n    int                 m_dofP;\t\t//!< pressure dof index\n    int                 m_dofQ;     //!< shell extra pressure dof index\n    int                 m_dofC;\t\t//!< concentration dof index\n    int                 m_dofD;\t\t//!< shell extra concentration dof index\n    int                 m_dofVX;\n    int                 m_dofVY;\n    int                 m_dofVZ;\n    \nprotected:\n    bool                m_breset;   //! flag for calling reset\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicFluidPressureBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMultiphasicFluidPressureBC.h\"\n#include \"FEBioMix.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESurface.h>\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMultiphasicFluidPressureBC, FEPrescribedSurface)\n    ADD_PARAMETER(m_p, \"pressure\")->setUnits(\"P\")->setLongName(\"fluid pressure\");\n    ADD_PARAMETER(m_bshellb , \"shell_bottom\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMultiphasicFluidPressureBC::FEMultiphasicFluidPressureBC(FEModel* pfem) : FEPrescribedSurface(pfem)\n{\n    m_p = 0;\n    m_dofP = -1;\n    m_dofC = -1;\n    m_Rgas = 0;\n    m_Tabs = 0;\n    m_bshellb = false;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize\nbool FEMultiphasicFluidPressureBC::Init()\n{\n    FEModel* pfem = GetFEModel();\n    m_dofP = m_bshellb ? pfem->GetDOFIndex(\"q\") : pfem->GetDOFIndex(\"p\");\n    \n    m_dofC = m_bshellb ? pfem->GetDOFIndex(\"shell concentration\", 0) : pfem->GetDOFIndex(\"concentration\", 0);\n    SetDOFList(m_dofP);\n\n    if (FEPrescribedSurface::Init() == false) return false;\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n    m_pe.assign(GetSurface()->Nodes(), 0.0);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEMultiphasicFluidPressureBC::Update()\n{\n    // prescribe this dilatation at the nodes\n    FESurface* ps = GetSurface();\n\n    int N = ps->Nodes();\n    std::vector<vector<double>> peNodes(N, vector<double>());\n\n    // Project sum of all kappa and osm_coef values from int points to nodes on surface\n    // All values put into map, including duplicates\n    for (int i=0; i<ps->Elements(); ++i)\n    {\n        FESurfaceElement& el = ps->Element(i);\n        // evaluate average prescribed pressure on this face\n        double p = 0;\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMaterialPoint* pt = el.GetMaterialPoint(j);\n            p += m_p(*pt);\n        }\n        p /= el.GaussPoints();\n        FEElement* e = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(e->GetMatID());\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        FESolidElement* se = dynamic_cast<FESolidElement*>(e);\n        FEShellElement* sh = dynamic_cast<FEShellElement*>(e);\n        if (se) {\n            double peo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0;\n                const int nint = se->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = se->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetPartitionCoefficient(*pt, k);\n                }\n                osc /= nint;\n                for (int k=0; k<nsol; ++k) kappa[k] /= nint;\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    // evaluate effective fluid pressure at this node\n                    peo[j] = p - m_Rgas*m_Tabs*osc*osm;\n                }\n            }\n            else {\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // evaluate effective fluid pressure at this node\n                    peo[j] = p;\n                }\n            }\n            // only keep the dilatations at the nodes of the surface face\n            for (int j=0; j<el.Nodes(); ++j)\n                peNodes[el.m_lnode[j]].push_back(peo[j]);\n        }\n        //If no solid element, insert all 0s\n        else if (sh) {\n            double peo[FEElement::MAX_NODES] = {0};\n            if (psi) {\n                const int nsol = psi->Solutes();\n                std::vector<double> kappa(nsol,0);\n                double osc = 0;\n                const int nint = sh->GaussPoints();\n                // get the average osmotic coefficient and partition coefficients in the solid element\n                for (int j=0; j<nint; ++j) {\n                    FEMaterialPoint* pt = sh->GetMaterialPoint(j);\n                    osc += psi->GetOsmoticCoefficient()->OsmoticCoefficient(*pt);\n                    for (int k=0; k<nsol; ++k)\n                        kappa[k] += psi->GetPartitionCoefficient(*pt, k);\n                }\n                osc /= nint;\n                for (int k=0; k<nsol; ++k) kappa[k] /= nint;\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    double osm = 0;\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // calculate osmolarity at this node, using nodal effective solute concentrations\n                    for (int k=0; k<nsol; ++k)\n                        osm += node.get(m_dofC+psi->GetSolute(k)->GetSoluteID()-1)*kappa[k];\n                    // evaluate effective fluid pressure at this node\n                    peo[j] = p - m_Rgas*m_Tabs*osc*osm;\n                }\n            }\n            else {\n                // loop over face nodes\n                for (int j=0; j<el.Nodes(); ++j) {\n                    FENode& node = ps->Node(el.m_lnode[j]);\n                    // evaluate effective fluid pressure at this node\n                    peo[j] = p;\n                }\n            }\n            // only keep the dilatations at the nodes of the surface face\n            for (int j=0; j<el.Nodes(); ++j)\n                peNodes[el.m_lnode[j]].push_back(peo[j]);\n        }\n    }\n    \n    //For each node, average the nodal pe\n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        double pe = 0;\n        for (int j = 0; j < peNodes[i].size(); ++j)\n            pe += peNodes[i][j];\n        pe /= peNodes[i].size();\n            \n        // store value for now\n        m_pe[i] = pe;\n    }\n \n    FEPrescribedSurface::Update();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicFluidPressureBC::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n    val[0] = m_pe[nodelid];\n}\n\n//-----------------------------------------------------------------------------\n// copy data from another class\nvoid FEMultiphasicFluidPressureBC::CopyFrom(FEBoundaryCondition* pbc)\n{\n    // TODO: implement this\n    assert(false);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMultiphasicFluidPressureBC::Serialize(DumpStream& ar)\n{\n    FEPrescribedSurface::Serialize(ar);\n    ar & m_pe;\n    if (ar.IsShallow()) return;\n    ar & m_dofC & m_dofP;\n    ar & m_Rgas & m_Tabs;\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicFluidPressureBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Prescribe the actual fluid pressure in a multiphasic mixture\n//!\nclass FEBIOMIX_API FEMultiphasicFluidPressureBC : public FEPrescribedSurface\n{\npublic:\n    //! constructor\n    FEMultiphasicFluidPressureBC(FEModel* pfem);\n\n    //! set the dilatation\n    void Update() override;\n\n    //! initialize\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n\npublic:\n    // return the value for node i, dof j\n    void GetNodalValues(int nodelid, std::vector<double>& val) override;\n\n    // copy data from another class\n    void CopyFrom(FEBoundaryCondition* pbc) override;\n\nprivate:\n    FEParamDouble   m_p;        //!< prescribed fluid pressure\n    vector<double>  m_pe;       //!< effective fluid pressure\n\nprivate:\n    double      m_Rgas;\n    double      m_Tabs;\n\n    int         m_dofP;\n    int         m_dofC;\n\npublic:\n    bool        m_bshellb;      //!< flag for prescribing pressure on shell bottom\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicFluidPressureLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicFluidPressureLoad.h\"\n#include \"FESoluteInterface.h\"\n#include \"FEOsmoticCoefficient.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEMaterial.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMultiphasicFluidPressureLoad, FESurfaceLoad)\n    ADD_PARAMETER(m_p0    , \"pressure\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMultiphasicFluidPressureLoad::FEMultiphasicFluidPressureLoad(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_p0 = 0;\n    \n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    \n    m_dof.Clear();\n    m_dof.AddDof(m_dofP);\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasicFluidPressureLoad::Init()\n{\n    if (FESurfaceLoad::Init() == false) return false;\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the degrees of freedom for this BC\nvoid FEMultiphasicFluidPressureLoad::Activate()\n{\n    FESurface* ps = &GetSurface();\n    \n    for (int i=0; i<ps->Nodes(); ++i)\n    {\n        FENode& node = ps->Node(i);\n        // mark node as having prescribed DOF\n        node.set_bc(m_dofP, DOF_PRESCRIBED);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate and prescribe the resistance pressure\nvoid FEMultiphasicFluidPressureLoad::Update()\n{\n    // prescribe this pressure at the nodes\n    FESurface* ps = &GetSurface();\n    int N = ps->Nodes();\n    vector<double> posm(N,0);\n    vector<int> nosm(N,0);\n    for (int i=0; i<ps->Elements(); ++i) {\n        FESurfaceElement& el = m_psurf->Element(i);\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        FESoluteInterface* psi = pm->ExtractProperty<FESoluteInterface>();\n        if (psi) {\n            // calculate average osmotic contribution to pressure in this element\n            double nint = pe->GaussPoints();\n            double p = 0;\n            for (int n=0; n<nint; ++n) {\n                FEMaterialPoint& mp = *pe->GetMaterialPoint(n);\n                double osm = psi->GetOsmolarity(mp);\n                double Phi = psi->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n                p -= m_Rgas*m_Tabs*Phi*osm;\n            }\n            p /= nint;\n            for (int i=0; i<el.Nodes(); ++i) {\n                int inode = el.m_lnode[i];\n                posm[inode] += p;\n                ++nosm[inode];\n            }\n        }\n    }\n\n    // now prescribe the effective fluid pressure at surface nodes\n    for (int i=0; i<N; ++i)\n    {\n        if (nosm[i] > 0) posm[i] /= nosm[i];\n        if (ps->Node(i).m_ID[m_dofP] < -1)\n        {\n            FENode& node = ps->Node(i);\n            // set nodal value\n            node.set(m_dofP, m_p0 + posm[i]);\n        }\n    }\n    \n    GetFEModel()->SetMeshUpdateFlag(true);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FEMultiphasicFluidPressureLoad::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicFluidPressureLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Prescribe the actual fluid pressure in a multiphasic mixture\n\nclass FEBIOMIX_API FEMultiphasicFluidPressureLoad : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEMultiphasicFluidPressureLoad(FEModel* pfem);\n    \n    //! destructor\n    ~FEMultiphasicFluidPressureLoad() {}\n    \n    //! calculate traction stiffness (there is none for this load)\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate load vector (there is none for this load)\n    void LoadVector(FEGlobalVector& R) override {}\n    \n    //! set the dilatation\n    void Update() override;\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \nprotected:\n    int     m_dofP;\n    int     m_Rgas;\n    int     m_Tabs;\n    \npublic:\n    double  m_p0;       // prescribed actual fluid pressure\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicMultigeneration.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicMultigeneration.h\"\n\n// Material parameters for the FEMultiphasicMultigeneration material\nBEGIN_FECORE_CLASS(FEMultiphasicMultigeneration, FEMultiphasic)\n    ADD_PARAMETER(m_gtime   , FE_RANGE_GREATER(0.0), \"gen_time\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FEMultiphasicMultigeneration.h constructor\nFEMultiphasicMultigeneration::FEMultiphasicMultigeneration(FEModel* pfem) : FEMultiphasic(pfem)\n{\n    m_gtime = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMultiphasicMultigeneration::CreateMaterialPointData()\n{\n\treturn new FEMultigenSBMMaterialPoint\n    (this, new FESolutesMaterialPoint\n     (new FEBiphasicMaterialPoint\n      (m_pSolid->CreateMaterialPointData())\n      )\n     );\n}\n\n//--------------------------------------------------------------------------------\n// Check if time t constitutes a new generation and return that generation\nint FEMultiphasicMultigeneration::CheckGeneration(const double t)\n{\n    // each generation has the same length of time\n    // there is always at least one generation\n\tint ngen = (int)(t/m_gtime)+1;\n\treturn ngen;\n}\n\n//--------------------------------------------------------------------------------\n// Return the starting time of a generation\ndouble FEMultiphasicMultigeneration::GetGenerationTime(const int igen)\n{\n\treturn (igen-1)*m_gtime;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicMultigeneration::UpdateSolidBoundMolecules(FEMaterialPoint& mp)\n{\n    double dt = CurrentTimeIncrement();\n    \n    // check if this mixture includes chemical reactions\n    int nreact = (int)Reactions();\n    int mreact = (int)MembraneReactions();\n    if (nreact) {\n        // for chemical reactions involving solid-bound molecules,\n        // update their concentration\n        // multiphasic material point data\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        FEMultigenSBMMaterialPoint& mpt = *(mp.ExtractData<FEMultigenSBMMaterialPoint>());\n        \n        double phi0 = ppt.m_phi0t;\n        int nsbm = SBMs();\n        int nsol = Solutes();\n        int ngen = mpt.m_ngen;\n        \n        for (int isbm=0; isbm<nsbm; ++isbm) {\n            \n            // initialize referential mass density supply\n            spt.m_sbmrhat[isbm] = 0;\n            // initialize referential mass density\n            spt.m_sbmr[isbm] = spt.m_sbmrp[isbm];\n            \n            // evaluate mass fraction of each generation before SBM update\n            vector<double> mf(ngen,0.0);\n            for (int igen=0; igen<ngen; ++igen) {\n                mf[igen] = (spt.m_sbmr[isbm] > 0) ? mpt.m_gsbmr[igen][isbm]/spt.m_sbmr[isbm] : 0;\n                // initialize generational referential mass density\n                mpt.m_gsbmr[igen][isbm] = mpt.m_gsbmrp[igen][isbm];\n            }\n            \n            // for each reaction\n            for (int k=0; k<nreact; ++k) {\n                \n                // evaluate the molar supply for this SBM\n                double zetahat = GetReaction(k)->ReactionSupply(mp);\n                double v = GetReaction(k)->m_v[nsol+isbm];\n                \n                // remember to convert from molar supply to referential mass supply\n                double sbmrhat = (pt.m_J-phi0)*SBMMolarMass(isbm)*v*zetahat;\n                \n                // combine the molar supplies from all the reactions\n                spt.m_sbmrhat[isbm] += sbmrhat;\n                \n                // check if mass is added or removed\n                if (sbmrhat >= 0) {\n                    \n                    // mass is added only to the current generation\n                    // perform the time integration (Euler's method)\n                    double dsbmr = dt*sbmrhat;\n                    \n                    // add this mass increment to the current generation\n                    mpt.m_gsbmr[ngen-1][isbm] += dsbmr;\n                    spt.m_sbmr[isbm] += dsbmr;\n                    \n                    // check bounds\n                    if ((spt.m_sbmrmax[isbm] > 0) && (spt.m_sbmr[isbm] > spt.m_sbmrmax[isbm])) {\n                        dsbmr = spt.m_sbmrmax[isbm] - spt.m_sbmr[isbm];\n                        mpt.m_gsbmr[ngen-1][isbm] += dsbmr;\n                        spt.m_sbmr[isbm] += dsbmr;\n                    }\n                }\n                else\n                {\n                    \n                    // mass is removed from all the generations in proportion to mass fraction\n                    // perform the time integration (Euler's method)\n                    double dsbmr = dt*sbmrhat;\n                    \n                    // add this (negative) weighted mass increment to all generations\n                    for (int igen=0; igen<ngen; ++igen)\n                        mpt.m_gsbmr[igen][isbm] += mf[igen]*dsbmr;\n                    spt.m_sbmr[isbm] += dsbmr;\n                    \n                    // check bounds\n                    if (spt.m_sbmr[isbm] < spt.m_sbmrmin[isbm]) {\n                        dsbmr = spt.m_sbmrmin[isbm] - spt.m_sbmr[isbm];\n                        for (int igen=0; igen<ngen; ++igen)\n                            mpt.m_gsbmr[igen][isbm] += mf[igen]*dsbmr;\n                        spt.m_sbmr[isbm] += dsbmr;\n                    }\n                }\n            }\n            // for each membrane reaction\n            for (int k=0; k<mreact; ++k) {\n                \n                // evaluate the molar supply for this SBM\n                double zetahat = GetMembraneReaction(k)->ReactionSupply(mp);\n                double v = GetMembraneReaction(k)->m_v[nsol+isbm];\n                \n                // remember to convert from molar supply to referential mass supply\n                double sbmrhat = (pt.m_J-phi0)*SBMMolarMass(isbm)*v*zetahat;\n                \n                // combine the molar supplies from all the reactions\n                spt.m_sbmrhat[isbm] += sbmrhat;\n                \n                // check if mass is added or removed\n                if (sbmrhat >= 0) {\n                    \n                    // mass is added only to the current generation\n                    // perform the time integration (Euler's method)\n                    double dsbmr = dt*sbmrhat;\n                    \n                    // add this mass increment to the current generation\n                    mpt.m_gsbmr[ngen-1][isbm] += dsbmr;\n                    spt.m_sbmr[isbm] += dsbmr;\n                    \n                    // check bounds\n                    if ((spt.m_sbmrmax[isbm] > 0) && (spt.m_sbmr[isbm] > spt.m_sbmrmax[isbm])) {\n                        dsbmr = spt.m_sbmrmax[isbm] - spt.m_sbmr[isbm];\n                        mpt.m_gsbmr[ngen-1][isbm] += dsbmr;\n                        spt.m_sbmr[isbm] += dsbmr;\n                    }\n                }\n                else\n                {\n                    \n                    // mass is removed from all the generations in proportion to mass fraction\n                    // perform the time integration (Euler's method)\n                    double dsbmr = dt*sbmrhat;\n                    \n                    // add this (negative) weighted mass increment to all generations\n                    for (int igen=0; igen<ngen; ++igen)\n                        mpt.m_gsbmr[igen][isbm] += mf[igen]*dsbmr;\n                    spt.m_sbmr[isbm] += dsbmr;\n                    \n                    // check bounds\n                    if (spt.m_sbmr[isbm] < spt.m_sbmrmin[isbm]) {\n                        dsbmr = spt.m_sbmrmin[isbm] - spt.m_sbmr[isbm];\n                        for (int igen=0; igen<ngen; ++igen)\n                            mpt.m_gsbmr[igen][isbm] += mf[igen]*dsbmr;\n                        spt.m_sbmr[isbm] += dsbmr;\n                    }\n                }\n            }\n        }\n    }\n}\n\n//=============================================================================\n//   FEMultigenSBMMaterialPoint\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMultigenSBMMaterialPoint::Copy()\n{\n\tFEMultigenSBMMaterialPoint* pt = new FEMultigenSBMMaterialPoint(*this);\n    pt->m_ngen = m_ngen;\n    pt->m_nsbm = m_nsbm;\n\tpt->m_pmat = m_pmat;\n\tpt->m_Fi = m_Fi;\n\tpt->m_Ji = m_Ji;\n    pt->m_gsbmr = m_gsbmr;\n    pt->m_gsbmrp = m_gsbmrp;\n    pt->m_lsbmr = m_lsbmr;\n    pt->m_tgen = m_tgen;\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultigenSBMMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n    ar & m_ngen & m_nsbm & m_tgen;\n\tar & m_Fi;\n\tar & m_Ji;\n\tar & m_gsbmr & m_gsbmrp;\n\tar & m_lsbmr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultigenSBMMaterialPoint::Init()\n{\n\tFEMaterialPointData::Init();\n    \n\tm_Fi.clear();\n\tm_Ji.clear();\n\tm_tgen = 0.0;\n        \n\tFESolutesMaterialPoint& spt = *((*this).ExtractData<FESolutesMaterialPoint>());\n\tm_nsbm = spt.m_nsbm;\n\tm_ngen = 1; // first generation starts at t=0\n\tmat3d Fi(mat3dd(1.0));\n\tdouble Ji = 1;\n\tm_Fi.push_back(Fi);\n\tm_Ji.push_back(Ji);\n\tm_lsbmr.resize(m_nsbm,0.0);\n\tm_gsbmr.push_back(m_lsbmr);\n\tm_gsbmrp.push_back(m_lsbmr);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultigenSBMMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointData::Update(timeInfo);\n\n\t// get the time\n\tdouble t = timeInfo.currentTime;\n        \n\t// Check if this constitutes a new generation\n\tint ngen = m_pmat->CheckGeneration(t);\n\tt = m_pmat->GetGenerationTime(ngen);\n        \n\tif (t>m_tgen) {\n\t\t// new generation\n\t\tFEElasticMaterialPoint& pt = *((*this).ExtractData<FEElasticMaterialPoint>());\n\t\tFESolutesMaterialPoint& spt = *((*this).ExtractData<FESolutesMaterialPoint>());\n            \n\t\t// push back F and J to define relative deformation gradient of this generation\n\t\tmat3d F = pt.m_F;\n\t\tdouble J = pt.m_J;\n\t\tm_Fi.push_back(F.inverse());\n\t\tm_Ji.push_back(1.0/J);\n\t\tm_lsbmr = spt.m_sbmr;           // sbmr content at start of generation\n\t\tvector<double> gsbmr(m_nsbm,0); // incremental sbmr content for this generation\n\t\tm_gsbmr.push_back(gsbmr);\n\t\tm_gsbmrp.push_back(gsbmr);\n\t\tm_tgen = t;\n\t\t++m_ngen;\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicMultigeneration.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMultiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// forward declaration of material class\nclass FEMultiphasicMultigeneration;\n\n//-----------------------------------------------------------------------------\n//! Multigenerational SBM material point.\n//! This material point stores the inverse of the relative deformation gradient,\n//! and the increment in the mass of solid-bound molecular species in\n//! multiple generations.\n\nclass FEBIOMIX_API FEMultigenSBMMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFEMultigenSBMMaterialPoint(FEMultiphasicMultigeneration* pm, FEMaterialPointData* pt) : m_pmat(pm), FEMaterialPointData(pt) { m_tgen = 0.0; }\n    \n\tFEMaterialPointData* Copy();\n    \n\tvoid Serialize(DumpStream& ar);\n    \n\tvoid Init();\n\tvoid Update(const FETimeInfo& timeInfo);\n    \npublic:\n\t// multigenerational material data\n    int                         m_ngen;     //!< number of generations\n\tvector <mat3d>              m_Fi;       //!< inverse of relative deformation gradient\n\tvector <double>             m_Ji;       //!< determinant of Fi (store for efficiency)\n    int                         m_nsbm;     //!< number of solid-bound molecules\n    vector< vector<double> >    m_gsbmr;    //!< sbmr content at each generation\n    vector< vector<double> >    m_gsbmrp;   //!< gsbmr at previous time point\n    vector<double>              m_lsbmr;    //!< last generation sbmr values\n\tdouble                      m_tgen;     //!< last generation time\n    \nprivate:\n\tFEMultiphasicMultigeneration*\tm_pmat;\n};\n\n//-----------------------------------------------------------------------------\n//! Multigeneration multiphasic material.\n\nclass FEMultiphasicMultigeneration : public FEMultiphasic\n{\npublic:\n\t//! constructor\n\tFEMultiphasicMultigeneration(FEModel* pfem);\n    \n    //! returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\t\n    //! Update solid bound molecules\n    void UpdateSolidBoundMolecules(FEMaterialPoint& mp) override;\n\n\tint CheckGeneration(const double t);\n    double GetGenerationTime(const int igen);\n    \npublic:\n\tdouble\tm_gtime;\t//!< time duration of each generation\n    \n    DECLARE_FECORE_CLASS();\n\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicShellDomain.h\"\n#include \"FEMultiphasicMultigeneration.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FEBioMech/FEBioMech.h>\n#include <FECore/FELinearSystem.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\nFEMultiphasicShellDomain::FEMultiphasicShellDomain(FEModel* pfem) : FESSIShellDomain(pfem), FEMultiphasicDomain(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! get the material (overridden from FEDomain)\nFEMaterial* FEMultiphasicShellDomain::GetMaterial()\n{ \n\treturn m_pMat; \n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof\nconst FEDofList& FEMultiphasicShellDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FEMultiphasic*>(pmat);\n    assert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEMultiphasicShellDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    // get nodal DOFS\n    const int nsol = m_pMat->Solutes();\n    \n    int N = el.Nodes();\n    int ndpn = 8+2*nsol;\n    lm.resize(N*ndpn);\n    \n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        FENode& node = m_pMesh->Node(n);\n        \n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[ndpn*i  ] = id[m_dofU[0]];\n        lm[ndpn*i+1] = id[m_dofU[1]];\n        lm[ndpn*i+2] = id[m_dofU[2]];\n        \n        // next the shell dofs\n        lm[ndpn*i+3] = id[m_dofSU[0]];\n        lm[ndpn*i+4] = id[m_dofSU[1]];\n        lm[ndpn*i+5] = id[m_dofSU[2]];\n        \n        // now the pressure dofs\n        lm[ndpn*i+6] = id[m_dofP];\n        lm[ndpn*i+7] = id[m_dofQ];\n        \n        // concentration dofs in shell domain\n        for (int k=0; k<nsol; ++k) {\n            lm[ndpn*i+8+2*k] = id[m_dofC+m_pMat->GetSolute(k)->GetSoluteDOF()];\n            lm[ndpn*i+9+2*k] = id[m_dofD+m_pMat->GetSolute(k)->GetSoluteDOF()];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::UnpackMembraneLM(FEShellElement& el, vector<int>& lm)\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // get the first material point\n    FEMaterialPoint& mp = *el.GetMaterialPoint(0);\n    FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n\n    int nse = (int)ps.m_ide.size();\n    int nsi = (int)ps.m_idi.size();\n    vector<int> ide = ps.m_ide;\n    vector<int> idi = ps.m_idi;\n\n    int neln = el.Nodes();\n    int ndpn = 8 + nse + nsi;\n    lm.resize(neln*ndpn);\n\n    for (int i=0; i<neln; ++i)\n    {\n        int n = el.m_node[i];\n        \n        FENode& node = mesh.Node(n);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[ndpn*i  ] = id[m_dofU[0]];\n        lm[ndpn*i+1] = id[m_dofU[1]];\n        lm[ndpn*i+2] = id[m_dofU[2]];\n        \n        // next the shell dofs\n        lm[ndpn*i+3] = id[m_dofSU[0]];\n        lm[ndpn*i+4] = id[m_dofSU[1]];\n        lm[ndpn*i+5] = id[m_dofSU[2]];\n        \n        // now the pressure dofs\n        lm[ndpn*i+6] = id[m_dofP];\n        lm[ndpn*i+7] = id[m_dofQ];\n        \n        // concentration dofs\n        for (int k=0; k<nse; ++k)\n            lm[ndpn*i+8+k] = id[m_dofC + ide[k]];\n        for (int k=0; k<nsi; ++k)\n            lm[ndpn*i+8+nse+k] = id[m_dofD + idi[k]];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! build connectivity for matrix profile\nvoid FEMultiphasicShellDomain::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n    vector<int> elm;\n    const int NE = Elements();\n    for (int j = 0; j<NE; ++j)\n    {\n        FEElement& el = ElementRef(j);\n        UnpackLM(el, elm);\n        M.build_add(elm);\n    }\n\n    // if we have membrane reactions, build matrix profile for those\n    if (m_pMat->MembraneReactions()) {\n        for (int j = 0; j<NE; ++j)\n        {\n            FEShellElement& el = dynamic_cast<FEShellElement&>(ElementRef(j));\n            UnpackMembraneLM(el, elm);\n            M.build_add(elm);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasicShellDomain::Init()\n{\n    // initialize base class\n\tif (FESSIShellDomain::Init() == false) return false;\n    \n    // extract the initial concentrations of the solid-bound molecules\n    const int nsbm = m_pMat->SBMs();\n    const int nsol = m_pMat->Solutes();\n    \n    for (int i = 0; i<(int)m_Elem.size(); ++i)\n    {\n        UpdateShellMPData(i);\n        \n        // get the solid element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            double h = el.Evaluate(el.m_ht, n);   // shell thickness\n\n            // initialize multiphasic solutes\n            ps.m_nsol = nsol;\n            ps.m_c.assign(nsol,0);\n            ps.m_ca.assign(nsol,0);\n            ps.m_crp.assign(nsol, 0);\n            ps.m_gradc.assign(nsol,vec3d(0,0,0));\n            ps.m_k.assign(nsol, 0);\n            ps.m_dkdJ.assign(nsol, 0);\n            ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n            ps.m_j.assign(nsol,vec3d(0,0,0));\n            ps.m_bsb.assign(nsol, false);\n            ps.m_nsbm = nsbm;\n            ps.m_sbmr.assign(nsbm, 0);\n            ps.m_sbmrp.assign(nsbm, 0);\n            ps.m_sbmrhatp.assign(nsbm, 0);\n            ps.m_sbmrhat.assign(nsbm,0);\n            ps.m_sbmrmin.assign(nsbm,0);\n            ps.m_sbmrmax.assign(nsbm,0);\n\n            // assign bounds on apparent densities of the solid-bound molecules\n            for (int i = 0; i<nsbm; ++i) {\n                ps.m_sbmr[i] = ps.m_sbmrp[i] = m_pMat->GetSBM(i)->m_rho0(mp);\n                ps.m_sbmrmin[i] = m_pMat->GetSBM(i)->m_rhomin;\n                ps.m_sbmrmax[i] = m_pMat->GetSBM(i)->m_rhomax;\n            }\n            \n            // initialize referential solid volume fraction\n            pb.m_phi0 = pb.m_phi0t = m_pMat->SolidReferentialVolumeFraction(mp);\n            if (pb.m_phi0 > 1.0) {\n                feLogError(\"Referential solid volume fraction of multiphasic material cannot exceed unity!\\nCheck ratios of sbm apparent and true densities.\");\n                return false;\n            }\n            \n            // evaluate reaction rates at initial time\n            // check if this mixture includes chemical reactions\n            int nreact = (int)m_pMat->Reactions();\n            if (nreact) {\n                // for chemical reactions involving solid-bound molecules,\n                // update their concentration\n                // multiphasic material point data\n                FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n                \n                double phi0 = pb.m_phi0t;\n                for (int isbm=0; isbm<nsbm; ++isbm) {\n                    // combine the molar supplies from all the reactions\n                    for (int k=0; k<nreact; ++k) {\n                        double zetahat = m_pMat->GetReaction(k)->ReactionSupply(mp);\n                        double v = m_pMat->GetReaction(k)->m_v[nsol+isbm];\n                        // remember to convert from molar supply to referential mass supply\n                        ps.m_sbmrhat[isbm] += (pt.m_J-phi0)*m_pMat->SBMMolarMass(isbm)*v*zetahat;\n                    }\n                }\n            }\n            // check if this mixture includes membrane reactions\n            int mreact = (int)m_pMat->MembraneReactions();\n            if (mreact) {\n                // for membrane reactions involving solid-bound molecules,\n                // update their concentration\n                // multiphasic material point data\n                FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n                \n                for (int isbm=0; isbm<nsbm; ++isbm) {\n                    // combine the molar supplies from all the reactions\n                    for (int k=0; k<mreact; ++k) {\n                        double zetahat = m_pMat->GetMembraneReaction(k)->ReactionSupply(mp);\n                        double v = m_pMat->GetMembraneReaction(k)->m_v[nsol+isbm];\n                        // remember to convert from molar supply to referential mass supply\n                        ps.m_sbmrhat[isbm] += pt.m_J/h*m_pMat->SBMMolarMass(isbm)*v*zetahat;\n                    }\n                }\n            }\n        }\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::Activate()\n{\n    const int nsol = m_pMat->Solutes();\n    \n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n                \n                if (node.HasFlags(FENode::SHELL))\n                {\n                    node.set_active(m_dofSU[0]);\n                    node.set_active(m_dofSU[1]);\n                    node.set_active(m_dofSU[2]);\n                }\n            }\n            \n            node.set_active(m_dofP);\n            for (int l=0; l<nsol; ++l)\n            {\n                int dofc = m_dofC + m_pMat->GetSolute(l)->GetSoluteDOF();\n                node.set_active(dofc);\n            }\n            \n            if (node.HasFlags(FENode::SHELL)) {\n                node.set_active(m_dofQ);\n                for (int l=0; l<nsol; ++l)\n                {\n                    int dofd = m_dofD + m_pMat->GetSolute(l)->GetSoluteDOF();\n                    node.set_active(dofd);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::InitMaterialPoints()\n{\n    const int nsol = m_pMat->Solutes();\n    const int nsbm = m_pMat->SBMs();\n    FEMesh& m = *GetMesh();\n    \n    const int NE = FEElement::MAX_NODES;\n    double p0[NE], q0[NE];\n    vector< vector<double> > c0(nsol, vector<double>(NE));\n    vector< vector<double> > d0(nsol, vector<double>(NE));\n    vector<int> sid(nsol);\n    for (int j = 0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n    \n    for (int i = 0; i<(int)m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the number of nodes\n        int neln = el.Nodes();\n        \n        // get initial values of fluid pressure and solute concentrations\n        for (int i = 0; i<neln; ++i)\n        {\n            p0[i] = m.Node(el.m_node[i]).get(m_dofP);\n            q0[i] = m.Node(el.m_node[i]).get(m_dofQ);\n            for (int isol = 0; isol<nsol; ++isol) {\n                c0[isol][i] = m.Node(el.m_node[i]).get(m_dofC + sid[isol]);\n                d0[isol][i] = m.Node(el.m_node[i]).get(m_dofD + sid[isol]);\n            }\n        }\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEElasticMaterialPoint& pm = *(mp.ExtractData<FEElasticMaterialPoint>());\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            // initialize effective fluid pressure, its gradient\n            pt.m_p = evaluate(el, p0, q0, n);\n            pt.m_gradp = gradient(el, p0, q0, n);\n            \n            // initialize multiphasic solutes\n            ps.m_nsol = nsol;\n            ps.m_nsbm = nsbm;\n            \n            // initialize effective solute concentrations\n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_c[isol] = evaluate(el, c0[isol], d0[isol], n);\n                ps.m_gradc[isol] = gradient(el, c0[isol], d0[isol], n);\n            }\n            \n            // determine if solute is 'solid-bound'\n            for (int isol = 0; isol<nsol; ++isol) {\n                FESolute* soli = m_pMat->GetSolute(isol);\n                if (soli->m_pDiff->Diffusivity(mp).norm() == 0) ps.m_bsb[isol] = true;\n                // initialize solute concentrations\n                ps.m_ca[isol] = m_pMat->Concentration(mp, isol);\n            }\n            \n            // initialize referential solid volume fraction\n            pt.m_phi0t = m_pMat->SolidReferentialVolumeFraction(mp);\n            \n            // initialize electric potential\n            ps.m_psi = m_pMat->ElectricPotential(mp);\n            \n            // initialize fluxes\n            pt.m_w = m_pMat->FluidFlux(mp);\n\n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_j[isol] = m_pMat->SoluteFlux(mp, isol);\n                ps.m_crp[isol] = pm.m_J*m_pMat->Porosity(mp)*ps.m_ca[isol];\n            }\n            \n            pt.m_pa = m_pMat->Pressure(mp);\n            \n            // calculate FCD, current and stress\n            ps.m_cF = m_pMat->FixedChargeDensity(mp);\n            ps.m_Ie = m_pMat->CurrentDensity(mp);\n            pm.m_s = m_pMat->Stress(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::Reset()\n{\n    // reset base class\n    FESSIShellDomain::Reset();\n    \n    const int nsol = m_pMat->Solutes();\n    const int nsbm = m_pMat->SBMs();\n    \n    for (int i=0; i<(int) m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n=0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n\n            // initialize multiphasic solutes\n            ps.m_nsol = nsol;\n            ps.m_c.assign(nsol,0);\n            ps.m_ca.assign(nsol,0);\n            ps.m_crp.assign(nsol, 0);\n            ps.m_gradc.assign(nsol,vec3d(0,0,0));\n            ps.m_k.assign(nsol, 0);\n            ps.m_dkdJ.assign(nsol, 0);\n            ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n            ps.m_j.assign(nsol,vec3d(0,0,0));\n            ps.m_bsb.assign(nsol, false);\n            ps.m_nsbm = nsbm;\n            ps.m_sbmr.assign(nsbm, 0);\n            ps.m_sbmrp.assign(nsbm, 0);\n            ps.m_sbmrhatp.assign(nsbm, 0);\n            ps.m_sbmrhat.assign(nsbm,0);\n            ps.m_sbmrmin.assign(nsbm,0);\n            ps.m_sbmrmax.assign(nsbm,0);\n\n            // assign bounds on apparent densities of the solid-bound molecules\n            for (int i = 0; i<nsbm; ++i) {\n                ps.m_sbmr[i] = ps.m_sbmrp[i] = m_pMat->GetSBM(i)->m_rho0(mp);\n                ps.m_sbmrmin[i] = m_pMat->GetSBM(i)->m_rhomin;\n                ps.m_sbmrmax[i] = m_pMat->GetSBM(i)->m_rhomax;\n            }\n\n            // initialize referential solid volume fraction\n            pb.m_phi0 = pb.m_phi0t = m_pMat->SolidReferentialVolumeFraction(mp);\n\n            // reset chemical reaction element data\n            ps.m_cri.clear();\n            ps.m_crd.clear();\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->ResetElementData(mp);\n            \n            // reset membrane reaction data\n            if (m_pMat->MembraneReactions()) {\n                ps.m_strain = 0;\n                ps.m_pe = ps.m_pi = 0;\n                ps.m_ide.clear();\n                ps.m_idi.clear();\n                ps.m_ce.clear();\n                ps.m_ci.clear();\n                for (int j=0; j<m_pMat->MembraneReactions(); ++j)\n                    m_pMat->GetMembraneReaction(j)->ResetElementData(mp);\n            }\n        }\n    }\n    m_breset = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    FESSIShellDomain::PreSolveUpdate(timeInfo);\n    \n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    FEMesh& m = *GetMesh();\n    \n    for (size_t iel=0; iel<m_Elem.size(); ++iel)\n    {\n        FEShellElement& el = m_Elem[iel];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n            xt[i] = m.Node(el.m_node[i]).m_rt;\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            r0 = el.Evaluate(x0, j);\n            rt = el.Evaluate(xt, j);\n            \n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            FEMultigenSBMMaterialPoint* pmg = mp.ExtractData<FEMultigenSBMMaterialPoint>();\n            \n            mp.m_r0 = r0;\n            mp.m_rt = rt;\n            \n            pe.m_J = defgrad(el, pe.m_F, j);\n            \n            // reset determinant of solid deformation gradient at previous time\n            pt.m_Jp = pe.m_J;\n            \n            // reset referential solid volume fraction at previous time\n            pt.m_phi0p = pt.m_phi0t;\n            \n            // reset referential actual solute concentration at previous time\n            for (int k=0; k<m_pMat->Solutes(); ++k) {\n                ps.m_crp[k] = pe.m_J*m_pMat->Porosity(mp)*ps.m_ca[k];\n            }\n            \n            // reset referential solid-bound molecule concentrations at previous time\n            ps.m_sbmrp = ps.m_sbmr;\n            ps.m_sbmrhatp = ps.m_sbmrhat;\n            // Jansen initialization\n            for (int k=0; k<ps.m_sbmrhat.size(); ++k)\n                ps.m_sbmrhat[k] = - ps.m_sbmrhatp[k];\n            \n            // reset generational referential solid-bound molecule concentrations at previous time\n            if (pmg) {\n                for (int i=0; i<pmg->m_ngen; ++i) {\n                    for (int j=0; j<ps.m_nsbm; ++j) {\n                        pmg->m_gsbmrp[i][j] = pmg->m_gsbmr[i][j];\n                    }\n                }\n            }\n            \n            // reset chemical reaction element data\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->InitializeElementData(mp);\n            \n            // reset membrane reaction element data\n            for (int j=0; j<m_pMat->MembraneReactions(); ++j)\n                m_pMat->GetMembraneReaction(j)->InitializeElementData(mp);\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::InternalForces(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n    \n    // get nodal DOFS\n    int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n#pragma omp parallel for\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n    \n    MembraneReactionFluxes(R);\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEMultiphasicShellDomain::ElementInternalForce(FEShellElement& el, vector<double>& fe)\n{\n    int i, isol, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradM, gradMu, gradMw;\n    double Mu, Mw;\n    mat3ds s;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n    const int nreact = m_pMat->Reactions();\n    const int mreact = m_pMat->MembraneReactions();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    vec3d gcnt[3];\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // next we get the determinant\n        double Jp = bpt.m_Jp;\n        double J = pt.m_J;\n        \n        // and then finally\n        double divv = ((J-Jp)/dt)/J;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        vector<vec3d> j(spt.m_j);\n        vector<int> z(nsol);\n        vector<double> kappa(spt.m_k);\n        vec3d je(0,0,0);\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            je += j[isol]*z[isol];\n        }\n        \n        // evaluate the porosity, its derivative w.r.t. J, and its gradient\n        double phiw = m_pMat->Porosity(mp);\n        vector<double> chat(nsol,0);\n        \n        // get the solvent supply\n        double phiwhat = 0;\n        if (m_pMat->GetSolventSupply()) phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += phiw*pri->m_Vbar*zhat;\n            for (isol=0; isol<nsol; ++isol)\n                chat[isol] += phiw*zhat*pri->m_v[isol];\n        }\n        \n        // membrane reactions\n        for (i=0; i<mreact; ++i) {\n            FEMembraneReaction* pri = m_pMat->GetMembraneReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += phiw*pri->m_Vbar*zhat;\n            for (isol=0; isol<nsol; ++isol)\n                chat[isol] += phiw*zhat*pri->m_v[isol];\n        }\n        \n        for (i=0; i<neln; ++i)\n        {\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu = (1+eta)/2*M[i];\n            Mw = (1-eta)/2*M[i];\n            \n            // calculate internal force\n            vec3d fu = s*gradMu;\n            vec3d fw = s*gradMw;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fu.x*detJt;\n            fe[ndpn*i+1] -= fu.y*detJt;\n            fe[ndpn*i+2] -= fu.z*detJt;\n            fe[ndpn*i+3] -= fw.x*detJt;\n            fe[ndpn*i+4] -= fw.y*detJt;\n            fe[ndpn*i+5] -= fw.z*detJt;\n            fe[ndpn*i+6] -= dt*(w*gradMu + (phiwhat - divv)*Mu)*detJt;\n            fe[ndpn*i+7] -= dt*(w*gradMw + (phiwhat - divv)*Mw)*detJt;\n            for (isol=0; isol<nsol; ++isol) {\n                fe[ndpn*i+8+2*isol] -= dt*(gradMu*(j[isol]+je*m_pMat->m_penalty)\n                                           + Mu*(chat[isol] - (phiw*spt.m_ca[isol] - spt.m_crp[isol]/J)/dt)\n                                           )*detJt;\n                fe[ndpn*i+9+2*isol] -= dt*(gradMw*(j[isol]+je*m_pMat->m_penalty)\n                                           + Mw*(chat[isol] - (phiw*spt.m_ca[isol] - spt.m_crp[isol]/J)/dt)\n                                           )*detJt;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    int NE = (int)m_Elem.size();\n    \n    // get nodal DOFS\n    int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n#pragma omp parallel for\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEMultiphasicShellDomain::ElementInternalForceSS(FEShellElement& el, vector<double>& fe)\n{\n    int i, isol, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradM, gradMu, gradMw;\n    double Mu, Mw;\n    mat3ds s;\n    \n    const double* Mr, *Ms, *M;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n    const int nreact = m_pMat->Reactions();\n    const int mreact = m_pMat->MembraneReactions();\n\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        // get the stress vector for this integration point\n        s = pt.m_s;\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        vector<vec3d> j(spt.m_j);\n        vector<int> z(nsol);\n        vector<double> kappa(spt.m_k);\n        vec3d je(0,0,0);\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            je += j[isol]*z[isol];\n        }\n        \n        // evaluate the porosity, its derivative w.r.t. J, and its gradient\n        double phiw = m_pMat->Porosity(mp);\n        vector<double> chat(nsol,0);\n        \n        // get the solvent supply\n        double phiwhat = 0;\n        if (m_pMat->GetSolventSupply()) phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += phiw*pri->m_Vbar*zhat;\n            for (isol=0; isol<nsol; ++isol)\n                chat[isol] += phiw*zhat*pri->m_v[isol];\n        }\n        \n        // membrane reactions\n        for (i=0; i<mreact; ++i) {\n            FEMembraneReaction* pri = m_pMat->GetMembraneReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += phiw*pri->m_Vbar*zhat;\n            for (isol=0; isol<nsol; ++isol)\n                chat[isol] += phiw*zhat*pri->m_v[isol];\n        }\n        \n        for (i=0; i<neln; ++i)\n        {\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu = (1+eta)/2*M[i];\n            Mw = (1-eta)/2*M[i];\n            \n            // calculate internal force\n            vec3d fu = s*gradMu;\n            vec3d fw = s*gradMw;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fu.x*detJt;\n            fe[ndpn*i+1] -= fu.y*detJt;\n            fe[ndpn*i+2] -= fu.z*detJt;\n            fe[ndpn*i+3] -= fw.x*detJt;\n            fe[ndpn*i+4] -= fw.y*detJt;\n            fe[ndpn*i+5] -= fw.z*detJt;\n            fe[ndpn*i+6] -= dt*(w*gradMu + Mu*phiwhat)*detJt;\n            fe[ndpn*i+7] -= dt*(w*gradMw + Mw*phiwhat)*detJt;\n            for (isol=0; isol<nsol; ++isol) {\n                fe[ndpn*i+8+2*isol] -= dt*(gradMu*(j[isol]+je*m_pMat->m_penalty)\n                                         + Mu*phiw*chat[isol]\n                                         )*detJt;\n                fe[ndpn*i+9+2*isol] -= dt*(gradMw*(j[isol]+je*m_pMat->m_penalty)\n                                           + Mw*phiw*chat[isol]\n                                           )*detJt;\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n    const int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\tint neln = el.Nodes();\n        int ndof = neln*ndpn;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementMultiphasicStiffness(el, ke, bsymm);\n\n\t\t// get lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n    \n    MembraneReactionStiffnessMatrix(LS);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n    const int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n\t\tFEElementMatrix ke(el);\n        int neln = el.Nodes();\n        int ndof = neln*ndpn;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementMultiphasicStiffnessSS(el, ke, bsymm);\n\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FEMultiphasicShellDomain::ElementMultiphasicStiffness(FEShellElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, isol, jsol, n, ireact, isbm;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    const double* Mr, *Ms, *M;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradMu(neln), gradMw(neln);\n    vector<double> Mu(neln), Mw(neln);\n    vec3d gradM;\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n    const int nsbm   = m_pMat->SBMs();\n    const int nreact = m_pMat->Reactions();\n    const int mreact = m_pMat->MembraneReactions();\n\n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Mw[i] = (1-eta)/2*M[i];\n        }\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        vector<double> c(spt.m_c);\n        vector<vec3d> gradc(spt.m_gradc);\n        vector<int> z(nsol);\n        \n        vector<double> kappa(spt.m_k);\n        \n        // get the charge number\n        for (isol=0; isol<nsol; ++isol)\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n        \n        vector<double> dkdJ(spt.m_dkdJ);\n        vector< vector<double> > dkdc(spt.m_dkdc);\n        vector< vector<double> > dkdr(spt.m_dkdr);\n        vector< vector<double> > dkdJr(spt.m_dkdJr);\n        vector< vector< vector<double> > > dkdrc(spt.m_dkdrc);\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phi0 = ppt.m_phi0t;\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the osmotic coefficient\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        \n        // evaluate the permeability\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        \n        vector<mat3ds> dKdc(nsol);\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector< vector<mat3ds> > dDdc(nsol, vector<mat3ds>(nsol));\n        vector<double> D0(nsol);\n        vector< vector<double> > dD0dc(nsol, vector<double>(nsol));\n        vector<double> dodc(nsol);\n        vector<mat3ds> dTdc(nsol);\n        vector<mat3ds> ImD(nsol);\n        mat3dd I(1);\n        \n        // evaluate the solvent supply and its derivatives\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        vector<double> Phic(nsol,0);\n        vector<mat3ds> dchatde(nsol);\n        if (m_pMat->GetSolventSupply()) {\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i)\n            Phie += m_pMat->GetReaction(i)->m_Vbar*(I*m_pMat->GetReaction(i)->ReactionSupply(mp)\n                                                    +m_pMat->GetReaction(i)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n        \n        // membrane reactions\n        for (i=0; i<mreact; ++i)\n            Phie += m_pMat->GetMembraneReaction(i)->m_Vbar*mat3dd(m_pMat->GetMembraneReaction(i)->ReactionSupply(mp)\n                                                    +m_pMat->GetMembraneReaction(i)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // evaluate the permeability derivatives\n            dKdc[isol] = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp,isol);\n            \n            // evaluate the diffusivity tensor and its derivatives\n            D[isol] = m_pMat->GetSolute(isol)->m_pDiff->Diffusivity(mp);\n            dDdE[isol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Diffusivity_Strain(mp);\n            \n            // evaluate the solute free diffusivity\n            D0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            \n            // evaluate the derivative of the osmotic coefficient\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            \n            // evaluate the stress tangent with concentration\n            //\t\t\tdTdc[isol] = pm->GetSolid()->Tangent_Concentration(mp,isol);\n            dTdc[isol] = mat3ds(0,0,0,0,0,0);\n            \n            ImD[isol] = I-D[isol]/D0[isol];\n            \n            for (jsol=0; jsol<nsol; ++jsol) {\n                dDdc[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Diffusivity_Concentration(mp,jsol);\n                dD0dc[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,jsol);\n            }\n            \n            // evaluate the solvent supply tangent with concentration\n            if (m_pMat->GetSolventSupply()) Phic[isol] = m_pMat->GetSolventSupply()->Tangent_Supply_Concentration(mp,isol);\n            \n            // chemical reactions\n            dchatde[isol].zero();\n            for (ireact=0; ireact<nreact; ++ireact) {\n                dchatde[isol] += m_pMat->GetReaction(ireact)->m_v[isol]\n                *(I*m_pMat->GetReaction(ireact)->ReactionSupply(mp)\n                  +m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n                Phic[isol] += phiw*m_pMat->GetReaction(ireact)->m_Vbar\n                *m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Concentration(mp, isol);\n            }\n            \n            // membrane reactions\n            for (ireact=0; ireact<mreact; ++ireact) {\n                dchatde[isol] += m_pMat->GetMembraneReaction(ireact)->m_v[isol]\n                *mat3dd(m_pMat->GetMembraneReaction(ireact)->ReactionSupply(mp)\n                  +m_pMat->GetMembraneReaction(ireact)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n                Phic[isol] += phiw*m_pMat->GetMembraneReaction(ireact)->m_Vbar\n                *m_pMat->GetMembraneReaction(ireact)->Tangent_ReactionSupply_Concentration(mp, isol);\n            }\n        }\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double penalty = m_pMat->m_penalty;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds Ke(0,0,0,0,0,0);\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE);\n        vector<mat3ds> Gc(nsol);\n        vector<mat3ds> dKedc(nsol);\n        for (isol=0; isol<nsol; ++isol) {\n            Ke += ImD[isol]*(kappa[isol]*c[isol]/D0[isol]);\n            G += dyad1(ImD[isol],I)*(R*T*c[isol]*J/D0[isol]/phiw*(dkdJ[isol]-kappa[isol]/phiw*dpdJ))\n            +(dyad1(I,I) - dyad2(I,I)*2 - dDdE[isol]/D0[isol])*(R*T*kappa[isol]*c[isol]/phiw/D0[isol]);\n            Gc[isol] = ImD[isol]*(kappa[isol]/D0[isol]);\n            for (jsol=0; jsol<nsol; ++jsol) {\n                Gc[isol] += ImD[jsol]*(c[jsol]/D0[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol]))\n                -(dDdc[jsol][isol]-D[jsol]*(dD0dc[jsol][isol]/D0[jsol])*(kappa[jsol]*c[jsol]/SQR(D0[jsol])));\n            }\n            Gc[isol] *= R*T/phiw;\n        }\n        Ke = (Ki + Ke*(R*T/phiw)).inverse();\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        for (isol=0; isol<nsol; ++isol)\n            dKedc[isol] = -(Ke*(-Ki*dKdc[isol]*Ki + Gc[isol])*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,qpu, qpw;\n        vector<vec3d> gc(nsol),qcu(nsol),qcw(nsol),wc(nsol),wd(nsol),jce(nsol),jde(nsol);\n        vector< vector<vec3d> > jc(nsol, vector<vec3d>(nsol));\n        vector< vector<vec3d> > jd(nsol, vector<vec3d>(nsol));\n        mat3d wu, ww, jue, jwe;\n        vector<mat3d> ju(nsol), jw(nsol);\n        vector< vector<double> > qcc(nsol, vector<double>(nsol));\n        vector< vector<double> > qcd(nsol, vector<double>(nsol));\n        vector< vector<double> > dchatdc(nsol, vector<double>(nsol));\n        double sum;\n        mat3ds De;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradMu[i]*(s*gradMu[j])) + vdotTdotv(gradMu[i], C, gradMu[j]))*detJ;\n                mat3d Kuw = (mat3dd(gradMu[i]*(s*gradMw[j])) + vdotTdotv(gradMu[i], C, gradMw[j]))*detJ;\n                mat3d Kwu = (mat3dd(gradMw[i]*(s*gradMu[j])) + vdotTdotv(gradMw[i], C, gradMu[j]))*detJ;\n                mat3d Kww = (mat3dd(gradMw[i]*(s*gradMw[j])) + vdotTdotv(gradMw[i], C, gradMw[j]))*detJ;\n\n                ke[ndpn*i  ][ndpn*j  ] += Kuu[0][0]; ke[ndpn*i  ][ndpn*j+1] += Kuu[0][1]; ke[ndpn*i  ][ndpn*j+2] += Kuu[0][2];\n                ke[ndpn*i+1][ndpn*j  ] += Kuu[1][0]; ke[ndpn*i+1][ndpn*j+1] += Kuu[1][1]; ke[ndpn*i+1][ndpn*j+2] += Kuu[1][2];\n                ke[ndpn*i+2][ndpn*j  ] += Kuu[2][0]; ke[ndpn*i+2][ndpn*j+1] += Kuu[2][1]; ke[ndpn*i+2][ndpn*j+2] += Kuu[2][2];\n                \n                ke[ndpn*i  ][ndpn*j+3] += Kuw[0][0]; ke[ndpn*i  ][ndpn*j+4] += Kuw[0][1]; ke[ndpn*i  ][ndpn*j+5] += Kuw[0][2];\n                ke[ndpn*i+1][ndpn*j+3] += Kuw[1][0]; ke[ndpn*i+1][ndpn*j+4] += Kuw[1][1]; ke[ndpn*i+1][ndpn*j+5] += Kuw[1][2];\n                ke[ndpn*i+2][ndpn*j+3] += Kuw[2][0]; ke[ndpn*i+2][ndpn*j+4] += Kuw[2][1]; ke[ndpn*i+2][ndpn*j+5] += Kuw[2][2];\n                \n                ke[ndpn*i+3][ndpn*j  ] += Kwu[0][0]; ke[ndpn*i+3][ndpn*j+1] += Kwu[0][1]; ke[ndpn*i+3][ndpn*j+2] += Kwu[0][2];\n                ke[ndpn*i+4][ndpn*j  ] += Kwu[1][0]; ke[ndpn*i+4][ndpn*j+1] += Kwu[1][1]; ke[ndpn*i+4][ndpn*j+2] += Kwu[1][2];\n                ke[ndpn*i+5][ndpn*j  ] += Kwu[2][0]; ke[ndpn*i+5][ndpn*j+1] += Kwu[2][1]; ke[ndpn*i+5][ndpn*j+2] += Kwu[2][2];\n                \n                ke[ndpn*i+3][ndpn*j+3] += Kww[0][0]; ke[ndpn*i+3][ndpn*j+4] += Kww[0][1]; ke[ndpn*i+3][ndpn*j+5] += Kww[0][2];\n                ke[ndpn*i+4][ndpn*j+3] += Kww[1][0]; ke[ndpn*i+4][ndpn*j+4] += Kww[1][1]; ke[ndpn*i+4][ndpn*j+5] += Kww[1][2];\n                ke[ndpn*i+5][ndpn*j+3] += Kww[2][0]; ke[ndpn*i+5][ndpn*j+4] += Kww[2][1]; ke[ndpn*i+5][ndpn*j+5] += Kww[2][2];\n                \n                // calculate the kpu matrix\n                gp = vec3d(0,0,0);\n                for (isol=0; isol<nsol; ++isol) gp += (D[isol]*gradc[isol])*(kappa[isol]/D0[isol]);\n                gp = gradp+gp*(R*T);\n                wu = vdotTdotv(-gp, dKedE, gradMu[j]);\n                ww = vdotTdotv(-gp, dKedE, gradMw[j]);\n                for (isol=0; isol<nsol; ++isol) {\n                    wu += (((Ke*(D[isol]*gradc[isol])) & gradMu[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradMu[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradMu[j]))*(-kappa[isol]*R*T/D0[isol]);\n                    ww += (((Ke*(D[isol]*gradc[isol])) & gradMw[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradMw[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradMw[j]))*(-kappa[isol]*R*T/D0[isol]);\n                }\n                qpu = -gradMu[j]*(1.0/dt);\n                qpw = -gradMw[j]*(1.0/dt);\n                vec3d kpu = (wu.transpose()*gradMu[i] + (qpu + Phie*gradMu[j])*Mu[i])*(detJ*dt);\n                vec3d kpw = (ww.transpose()*gradMu[i] + (qpw + Phie*gradMw[j])*Mu[i])*(detJ*dt);\n                vec3d kqu = (wu.transpose()*gradMw[i] + (qpu + Phie*gradMu[j])*Mw[i])*(detJ*dt);\n                vec3d kqw = (ww.transpose()*gradMw[i] + (qpw + Phie*gradMw[j])*Mw[i])*(detJ*dt);\n                ke[ndpn*i+6][ndpn*j  ] += kpu.x; ke[ndpn*i+6][ndpn*j+1] += kpu.y; ke[ndpn*i+6][ndpn*j+2] += kpu.z;\n                ke[ndpn*i+6][ndpn*j+3] += kpw.x; ke[ndpn*i+6][ndpn*j+4] += kpw.y; ke[ndpn*i+6][ndpn*j+5] += kpw.z;\n                ke[ndpn*i+7][ndpn*j  ] += kqu.x; ke[ndpn*i+7][ndpn*j+1] += kqu.y; ke[ndpn*i+7][ndpn*j+2] += kqu.z;\n                ke[ndpn*i+7][ndpn*j+3] += kqw.x; ke[ndpn*i+7][ndpn*j+4] += kqw.y; ke[ndpn*i+7][ndpn*j+5] += kqw.z;\n                \n                // calculate the kup matrix\n                vec3d kup = gradMu[i]*(-Mu[j]*detJ);\n                vec3d kuq = gradMu[i]*(-Mw[j]*detJ);\n                vec3d kwp = gradMw[i]*(-Mu[j]*detJ);\n                vec3d kwq = gradMw[i]*(-Mw[j]*detJ);\n\n                ke[ndpn*i  ][ndpn*j+6] += kup.x; ke[ndpn*i  ][ndpn*j+7] += kuq.x;\n                ke[ndpn*i+1][ndpn*j+6] += kup.y; ke[ndpn*i+1][ndpn*j+7] += kuq.y;\n                ke[ndpn*i+2][ndpn*j+6] += kup.z; ke[ndpn*i+2][ndpn*j+7] += kuq.z;\n                \n                ke[ndpn*i+3][ndpn*j+6] += kwp.x; ke[ndpn*i+3][ndpn*j+7] += kwq.x;\n                ke[ndpn*i+4][ndpn*j+6] += kwp.y; ke[ndpn*i+4][ndpn*j+7] += kwq.y;\n                ke[ndpn*i+5][ndpn*j+6] += kwp.z; ke[ndpn*i+5][ndpn*j+7] += kwq.z;\n                \n                // calculate the kpp matrix\n                ke[ndpn*i+6][ndpn*j+6] += (Mu[i]*Mu[j]*Phip - gradMu[i]*(Ke*gradMu[j]))*(detJ*dt);\n                ke[ndpn*i+6][ndpn*j+7] += (Mu[i]*Mw[j]*Phip - gradMu[i]*(Ke*gradMw[j]))*(detJ*dt);\n                ke[ndpn*i+7][ndpn*j+6] += (Mw[i]*Mu[j]*Phip - gradMw[i]*(Ke*gradMu[j]))*(detJ*dt);\n                ke[ndpn*i+7][ndpn*j+7] += (Mw[i]*Mw[j]*Phip - gradMw[i]*(Ke*gradMw[j]))*(detJ*dt);\n                \n                // calculate kcu matrix data\n                jue.zero(); jwe.zero();\n                De.zero();\n                for (isol=0; isol<nsol; ++isol) {\n                    gc[isol] = -gradc[isol]*phiw + w*c[isol]/D0[isol];\n                    ju[isol] = ((D[isol]*gc[isol]) & gradMu[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradMu[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradMu[j])*(-phis)\n                       +(D[isol]*((gradMu[j]*w)*2) - ((D[isol]*w) & gradMu[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*wu*(kappa[isol]*c[isol]/D0[isol]);\n                    jw[isol] = ((D[isol]*gc[isol]) & gradMw[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradMw[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradMw[j])*(-phis)\n                       +(D[isol]*((gradMw[j]*w)*2) - ((D[isol]*w) & gradMw[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*ww*(kappa[isol]*c[isol]/D0[isol]);\n                    jue += ju[isol]*z[isol];\n                    jwe += jw[isol]*z[isol];\n                    De += D[isol]*(z[isol]*kappa[isol]*c[isol]/D0[isol]);\n                    qcu[isol] = qpu*(c[isol]*(kappa[isol]+J*phiw*dkdJ[isol]));\n                    qcw[isol] = qpw*(c[isol]*(kappa[isol]+J*phiw*dkdJ[isol]));\n                    \n                    // chemical reactions\n                    for (ireact=0; ireact<nreact; ++ireact) {\n                        double sum1 = 0;\n                        double sum2 = 0;\n                        for (isbm=0; isbm<nsbm; ++isbm) {\n                            sum1 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetReaction(ireact)->m_v[nsol+isbm]*\n                            ((J-phi0)*dkdr[isol][isbm]-kappa[isol]/m_pMat->SBMDensity(isbm));\n                            sum2 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetReaction(ireact)->m_v[nsol+isbm]*\n                            (dkdr[isol][isbm]+(J-phi0)*dkdJr[isol][isbm]-dkdJ[isol]/m_pMat->SBMDensity(isbm));\n                        }\n                        double zhat = m_pMat->GetReaction(ireact)->ReactionSupply(mp);\n                        mat3dd zhatI(zhat);\n                        mat3ds dzde = m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Strain(mp);\n                        qcu[isol] -= ((zhatI+dzde*(J-phi0))*gradMu[j])*(sum1*c[isol])\n                        +gradMu[j]*(c[isol]*(J-phi0)*sum2*zhat);\n                        qcw[isol] -= ((zhatI+dzde*(J-phi0))*gradMw[j])*(sum1*c[isol])\n                        +gradMw[j]*(c[isol]*(J-phi0)*sum2*zhat);\n                    }\n                    \n                    // membrane reactions\n                    for (ireact=0; ireact<mreact; ++ireact) {\n                        double sum1 = 0;\n                        double sum2 = 0;\n                        for (isbm=0; isbm<nsbm; ++isbm) {\n                            sum1 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetMembraneReaction(ireact)->m_v[nsol+isbm]*\n                            ((J-phi0)*dkdr[isol][isbm]-kappa[isol]/m_pMat->SBMDensity(isbm));\n                            sum2 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetMembraneReaction(ireact)->m_v[nsol+isbm]*\n                            (dkdr[isol][isbm]+(J-phi0)*dkdJr[isol][isbm]-dkdJ[isol]/m_pMat->SBMDensity(isbm));\n                        }\n                        double zhat = m_pMat->GetMembraneReaction(ireact)->ReactionSupply(mp);\n                        mat3dd zhatI(zhat);\n                        mat3ds dzde = mat3dd(m_pMat->GetMembraneReaction(ireact)->Tangent_ReactionSupply_Strain(mp));\n                        qcu[isol] -= ((zhatI+dzde*(J-phi0))*gradMu[j])*(sum1*c[isol])\n                        +gradMu[j]*(c[isol]*(J-phi0)*sum2*zhat);\n                        qcw[isol] -= ((zhatI+dzde*(J-phi0))*gradMw[j])*(sum1*c[isol])\n                        +gradMw[j]*(c[isol]*(J-phi0)*sum2*zhat);\n                    }\n                }\n                \n                for (isol=0; isol<nsol; ++isol) {\n                    \n                    // calculate the kcu matrix\n                    vec3d kcu = ((ju[isol]+jue*penalty).transpose()*gradMu[i]\n                            + (qcu[isol] + dchatde[isol]*gradMu[j])*Mu[i])*(detJ*dt);\n                    vec3d kcw = ((jw[isol]+jwe*penalty).transpose()*gradMu[i]\n                                 + (qcw[isol] + dchatde[isol]*gradMw[j])*Mu[i])*(detJ*dt);\n                    vec3d kdu = ((ju[isol]+jue*penalty).transpose()*gradMw[i]\n                                 + (qcu[isol] + dchatde[isol]*gradMu[j])*Mw[i])*(detJ*dt);\n                    vec3d kdw = ((jw[isol]+jwe*penalty).transpose()*gradMw[i]\n                                 + (qcw[isol] + dchatde[isol]*gradMw[j])*Mw[i])*(detJ*dt);\n                    ke[ndpn*i+8+2*isol][ndpn*j  ] += kcu.x; ke[ndpn*i+8+2*isol][ndpn*j+1] += kcu.y; ke[ndpn*i+8+2*isol][ndpn*j+2] += kcu.z;\n                    ke[ndpn*i+8+2*isol][ndpn*j+3] += kcw.x; ke[ndpn*i+8+2*isol][ndpn*j+4] += kcw.y; ke[ndpn*i+8+2*isol][ndpn*j+5] += kcw.z;\n                    ke[ndpn*i+9+2*isol][ndpn*j  ] += kdu.x; ke[ndpn*i+9+2*isol][ndpn*j+1] += kdu.y; ke[ndpn*i+9+2*isol][ndpn*j+2] += kdu.z;\n                    ke[ndpn*i+9+2*isol][ndpn*j+3] += kdw.x; ke[ndpn*i+9+2*isol][ndpn*j+4] += kdw.y; ke[ndpn*i+9+2*isol][ndpn*j+5] += kdw.z;\n                    \n                    // calculate the kcp matrix\n                    ke[ndpn*i+8+2*isol][ndpn*j+6] -= (gradMu[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMu[j])\n                                                                 ))*(detJ*dt);\n                    ke[ndpn*i+8+2*isol][ndpn*j+7] -= (gradMu[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMw[j])\n                                                                 ))*(detJ*dt);\n                    ke[ndpn*i+9+2*isol][ndpn*j+6] -= (gradMw[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMu[j])\n                                                                 ))*(detJ*dt);\n                    ke[ndpn*i+9+2*isol][ndpn*j+7] -= (gradMw[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMw[j])\n                                                                 ))*(detJ*dt);\n                    \n                    // calculate the kuc matrix\n                    sum = 0;\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        sum += c[jsol]*(dodc[isol]*kappa[jsol]+osmc*dkdc[jsol][isol]);\n                    vec3d kuc = (dTdc[isol]*gradMu[i] - gradMu[i]*(R*T*(osmc*kappa[isol]+sum)))*Mu[j]*detJ;\n                    vec3d kud = (dTdc[isol]*gradMu[i] - gradMu[i]*(R*T*(osmc*kappa[isol]+sum)))*Mw[j]*detJ;\n                    vec3d kwc = (dTdc[isol]*gradMw[i] - gradMw[i]*(R*T*(osmc*kappa[isol]+sum)))*Mu[j]*detJ;\n                    vec3d kwd = (dTdc[isol]*gradMw[i] - gradMw[i]*(R*T*(osmc*kappa[isol]+sum)))*Mw[j]*detJ;\n\n                    ke[ndpn*i  ][ndpn*j+8+2*isol] += kuc.x; ke[ndpn*i  ][ndpn*j+9+2*isol] += kud.x;\n                    ke[ndpn*i+1][ndpn*j+8+2*isol] += kuc.y; ke[ndpn*i+1][ndpn*j+9+2*isol] += kud.y;\n                    ke[ndpn*i+2][ndpn*j+8+2*isol] += kuc.z; ke[ndpn*i+2][ndpn*j+9+2*isol] += kud.z;\n                    \n                    ke[ndpn*i+3][ndpn*j+8+2*isol] += kwc.x; ke[ndpn*i+3][ndpn*j+9+2*isol] += kwd.x;\n                    ke[ndpn*i+4][ndpn*j+8+2*isol] += kwc.y; ke[ndpn*i+4][ndpn*j+9+2*isol] += kwd.y;\n                    ke[ndpn*i+5][ndpn*j+8+2*isol] += kwc.z; ke[ndpn*i+5][ndpn*j+9+2*isol] += kwd.z;\n                    \n                    // calculate the kpc matrix\n                    vtmp = vec3d(0,0,0);\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        vtmp += (D[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol])\n                                 +dDdc[jsol][isol]*kappa[jsol])/D0[jsol]*gradc[jsol];\n                    wc[isol] = (dKedc[isol]*gp)*(-Mu[j])\n                    -Ke*((D[isol]*gradMu[j])*(kappa[isol]/D0[isol])+vtmp*Mu[j])*(R*T);\n                    wd[isol] = (dKedc[isol]*gp)*(-Mw[j])\n                    -Ke*((D[isol]*gradMw[j])*(kappa[isol]/D0[isol])+vtmp*Mw[j])*(R*T);\n\n                    ke[ndpn*i+6][ndpn*j+8+2*isol] += (gradMu[i]*wc[isol])*(detJ*dt);\n                    ke[ndpn*i+6][ndpn*j+9+2*isol] += (gradMu[i]*wd[isol])*(detJ*dt);\n                    ke[ndpn*i+7][ndpn*j+8+2*isol] += (gradMw[i]*wc[isol])*(detJ*dt);\n                    ke[ndpn*i+7][ndpn*j+9+2*isol] += (gradMw[i]*wd[isol])*(detJ*dt);\n                    \n                }\n                \n                // calculate data for the kcc matrix\n                jce.assign(nsol, vec3d(0,0,0));\n                jde.assign(nsol, vec3d(0,0,0));\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        if (jsol != isol) {\n                            jc[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mu[j]\n                            +(D[isol]*(w*(-Mu[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            jd[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mw[j]\n                            +(D[isol]*(w*(-Mw[j]*dD0dc[isol][jsol]/D0[isol])+wd[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            \n                            qcc[isol][jsol] = -Mu[j]*phiw/dt*c[isol]*dkdc[isol][jsol];\n                            qcd[isol][jsol] = -Mw[j]*phiw/dt*c[isol]*dkdc[isol][jsol];\n                        }\n                        else {\n                            jc[isol][jsol] = (D[isol]*(gradMu[j]*(-phiw)+w*(Mu[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mu[j]\n                            +(D[isol]*(w*(-Mu[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            jd[isol][jsol] = (D[isol]*(gradMw[j]*(-phiw)+w*(Mw[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mw[j]\n                            +(D[isol]*(w*(-Mw[j]*dD0dc[isol][jsol]/D0[isol])+wd[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            \n                            qcc[isol][jsol] = -Mu[j]*phiw/dt*(c[isol]*dkdc[isol][jsol] + kappa[isol]);\n                            qcd[isol][jsol] = -Mw[j]*phiw/dt*(c[isol]*dkdc[isol][jsol] + kappa[isol]);\n                        }\n                        jce[jsol] += jc[isol][jsol]*z[isol];\n                        jde[jsol] += jd[isol][jsol]*z[isol];\n                        \n                        // chemical reactions\n                        dchatdc[isol][jsol] = 0;\n                        for (ireact=0; ireact<nreact; ++ireact) {\n                            dchatdc[isol][jsol] += m_pMat->GetReaction(ireact)->m_v[isol]\n                            *m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                            double sum1 = 0;\n                            double sum2 = 0;\n                            for (isbm=0; isbm<nsbm; ++isbm) {\n                                sum1 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetReaction(ireact)->m_v[nsol+isbm]*\n                                ((J-phi0)*dkdr[isol][isbm]-kappa[isol]/m_pMat->SBMDensity(isbm));\n                                sum2 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetReaction(ireact)->m_v[nsol+isbm]*\n                                ((J-phi0)*dkdrc[isol][isbm][jsol]-dkdc[isol][jsol]/m_pMat->SBMDensity(isbm));\n                            }\n                            double zhat = m_pMat->GetReaction(ireact)->ReactionSupply(mp);\n                            double dzdc = m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Concentration(mp, jsol);\n                            if (jsol != isol) {\n                                qcc[isol][jsol] -= Mu[j]*phiw*c[isol]*(dzdc*sum1+zhat*sum2);\n                                qcd[isol][jsol] -= Mw[j]*phiw*c[isol]*(dzdc*sum1+zhat*sum2);\n                            }\n                            else {\n                                qcc[isol][jsol] -= Mu[j]*phiw*((zhat+c[isol]*dzdc)*sum1+c[isol]*zhat*sum2);\n                                qcd[isol][jsol] -= Mw[j]*phiw*((zhat+c[isol]*dzdc)*sum1+c[isol]*zhat*sum2);\n                            }\n                        }\n                        \n                        // membrane reactions\n                        for (ireact=0; ireact<mreact; ++ireact) {\n                            dchatdc[isol][jsol] += m_pMat->GetMembraneReaction(ireact)->m_v[isol]\n                            *m_pMat->GetMembraneReaction(ireact)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                            double sum1 = 0;\n                            double sum2 = 0;\n                            for (isbm=0; isbm<nsbm; ++isbm) {\n                                sum1 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetMembraneReaction(ireact)->m_v[nsol+isbm]*\n                                ((J-phi0)*dkdr[isol][isbm]-kappa[isol]/m_pMat->SBMDensity(isbm));\n                                sum2 += m_pMat->SBMMolarMass(isbm)*m_pMat->GetMembraneReaction(ireact)->m_v[nsol+isbm]*\n                                ((J-phi0)*dkdrc[isol][isbm][jsol]-dkdc[isol][jsol]/m_pMat->SBMDensity(isbm));\n                            }\n                            double zhat = m_pMat->GetMembraneReaction(ireact)->ReactionSupply(mp);\n                            double dzdc = m_pMat->GetMembraneReaction(ireact)->Tangent_ReactionSupply_Concentration(mp, jsol);\n                            if (jsol != isol) {\n                                qcc[isol][jsol] -= Mu[j]*phiw*c[isol]*(dzdc*sum1+zhat*sum2);\n                                qcd[isol][jsol] -= Mw[j]*phiw*c[isol]*(dzdc*sum1+zhat*sum2);\n                            }\n                            else {\n                                qcc[isol][jsol] -= Mu[j]*phiw*((zhat+c[isol]*dzdc)*sum1+c[isol]*zhat*sum2);\n                                qcd[isol][jsol] -= Mw[j]*phiw*((zhat+c[isol]*dzdc)*sum1+c[isol]*zhat*sum2);\n                            }\n                        }\n                    }\n                }\n                \n                // calculate the kcc matrix\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        ke[ndpn*i+8+2*isol][ndpn*j+8+2*jsol] += (gradMu[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                             + Mu[i]*(qcc[isol][jsol]\n                                                                     + Mu[j]*phiw*dchatdc[isol][jsol]))*(detJ*dt);\n                        ke[ndpn*i+8+2*isol][ndpn*j+9+2*jsol] += (gradMu[i]*(jd[isol][jsol]+jde[jsol]*penalty)\n                                                                 + Mu[i]*(qcd[isol][jsol]\n                                                                          + Mw[j]*phiw*dchatdc[isol][jsol]))*(detJ*dt);\n                        ke[ndpn*i+9+2*isol][ndpn*j+8+2*jsol] += (gradMw[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                                 + Mw[i]*(qcc[isol][jsol]\n                                                                          + Mu[j]*phiw*dchatdc[isol][jsol]))*(detJ*dt);\n                        ke[ndpn*i+9+2*isol][ndpn*j+9+2*jsol] += (gradMw[i]*(jd[isol][jsol]+jde[jsol]*penalty)\n                                                                 + Mw[i]*(qcd[isol][jsol]\n                                                                          + Mw[j]*phiw*dchatdc[isol][jsol]))*(detJ*dt);\n                    }\n                }\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<ndpn*neln; ++i)\n            for (j=i+1; j<ndpn*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for steady-state response (zero solid velocity, zero time derivative of\n//! solute concentration)\n//!\nbool FEMultiphasicShellDomain::ElementMultiphasicStiffnessSS(FEShellElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, isol, jsol, n, ireact;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    const double* Mr, *Ms, *M;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradMu(neln), gradMw(neln);\n    vector<double> Mu(neln), Mw(neln);\n    vec3d gradM;\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    double eta;\n    \n    vec3d gcnt[3];\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 2*(4+nsol);\n    \n    const int nreact = m_pMat->Reactions();\n    const int mreact = m_pMat->MembraneReactions();\n\n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        eta = el.gt(n);\n        \n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        M  = el.H(n);\n        \n        ContraBaseVectors(el, n, gcnt);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            gradM = gcnt[0]*Mr[i] + gcnt[1]*Ms[i];\n            gradMu[i] = (gradM*(1+eta) + gcnt[2]*M[i])/2;\n            gradMw[i] = (gradM*(1-eta) - gcnt[2]*M[i])/2;\n            Mu[i] = (1+eta)/2*M[i];\n            Mw[i] = (1-eta)/2*M[i];\n        }\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        vector<double> c(spt.m_c);\n        vector<vec3d> gradc(spt.m_gradc);\n        vector<int> z(nsol);\n        \n        vector<double> zz(nsol);\n        vector<double> kappa(spt.m_k);\n        \n        // get the charge number\n        for (isol=0; isol<nsol; ++isol)\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n        \n        vector<double> dkdJ(spt.m_dkdJ);\n        vector< vector<double> > dkdc(spt.m_dkdc);\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the osmotic coefficient\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        \n        // evaluate the permeability\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        \n        vector<mat3ds> dKdc(nsol);\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector< vector<mat3ds> > dDdc(nsol, vector<mat3ds>(nsol));\n        vector<double> D0(nsol);\n        vector< vector<double> > dD0dc(nsol, vector<double>(nsol));\n        vector<double> dodc(nsol);\n        vector<mat3ds> dTdc(nsol);\n        vector<mat3ds> ImD(nsol);\n        mat3dd I(1);\n        \n        // evaluate the solvent supply and its derivatives\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        vector<double> Phic(nsol,0);\n        if (m_pMat->GetSolventSupply()) {\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i)\n            Phie += m_pMat->GetReaction(i)->m_Vbar*(I*m_pMat->GetReaction(i)->ReactionSupply(mp)\n                                                    +m_pMat->GetReaction(i)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n        \n        // membrane reactions\n        for (i=0; i<mreact; ++i)\n            Phie += m_pMat->GetReaction(i)->m_Vbar*mat3dd(m_pMat->GetMembraneReaction(i)->ReactionSupply(mp)\n                                                    +m_pMat->GetMembraneReaction(i)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // evaluate the permeability derivatives\n            dKdc[isol] = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp,isol);\n            \n            // evaluate the diffusivity tensor and its derivatives\n            D[isol] = m_pMat->GetSolute(isol)->m_pDiff->Diffusivity(mp);\n            dDdE[isol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Diffusivity_Strain(mp);\n            \n            // evaluate the solute free diffusivity\n            D0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            \n            // evaluate the derivative of the osmotic coefficient\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            \n            // evaluate the stress tangent with concentration\n            //\t\t\tdTdc[isol] = pm->GetSolid()->Tangent_Concentration(mp,isol);\n            dTdc[isol] = mat3ds(0,0,0,0,0,0);\n            \n            ImD[isol] = I-D[isol]/D0[isol];\n            \n            for (jsol=0; jsol<nsol; ++jsol) {\n                dDdc[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Diffusivity_Concentration(mp,jsol);\n                dD0dc[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,jsol);\n            }\n            \n            // evaluate the solvent supply tangent with concentration\n            if (m_pMat->GetSolventSupply()) Phic[isol] = m_pMat->GetSolventSupply()->Tangent_Supply_Concentration(mp,isol);\n            \n        }\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double penalty = m_pMat->m_penalty;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds Ke(0,0,0,0,0,0);\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE);\n        vector<mat3ds> Gc(nsol);\n        vector<mat3ds> dKedc(nsol);\n        for (isol=0; isol<nsol; ++isol) {\n            Ke += ImD[isol]*(kappa[isol]*c[isol]/D0[isol]);\n            G += dyad1(ImD[isol],I)*(R*T*c[isol]*J/D0[isol]/phiw*(dkdJ[isol]-kappa[isol]/phiw*dpdJ))\n            +(dyad1(I,I) - dyad2(I,I)*2 - dDdE[isol]/D0[isol])*(R*T*kappa[isol]*c[isol]/phiw/D0[isol]);\n            Gc[isol] = ImD[isol]*(kappa[isol]/D0[isol]);\n            for (jsol=0; jsol<nsol; ++jsol) {\n                Gc[isol] += ImD[jsol]*(c[jsol]/D0[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol]))\n                -(dDdc[jsol][isol]-D[jsol]*(dD0dc[jsol][isol]/D0[jsol])*(kappa[jsol]*c[jsol]/SQR(D0[jsol])));\n            }\n            Gc[isol] *= R*T/phiw;\n        }\n        Ke = (Ki + Ke*(R*T/phiw)).inverse();\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        for (isol=0; isol<nsol; ++isol)\n            dKedc[isol] = -(Ke*(-Ki*dKdc[isol]*Ki + Gc[isol])*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,qpu, qpw;\n        vector<vec3d> gc(nsol),wc(nsol),wd(nsol),jce(nsol),jde(nsol);\n        vector< vector<vec3d> > jc(nsol, vector<vec3d>(nsol));\n        vector< vector<vec3d> > jd(nsol, vector<vec3d>(nsol));\n        mat3d wu, ww, jue, jwe;\n        vector<mat3d> ju(nsol), jw(nsol);\n        vector< vector<double> > dchatdc(nsol, vector<double>(nsol));\n        double sum;\n        mat3ds De;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradMu[i]*(s*gradMu[j])) + vdotTdotv(gradMu[i], C, gradMu[j]))*detJ;\n                mat3d Kuw = (mat3dd(gradMu[i]*(s*gradMw[j])) + vdotTdotv(gradMu[i], C, gradMw[j]))*detJ;\n                mat3d Kwu = (mat3dd(gradMw[i]*(s*gradMu[j])) + vdotTdotv(gradMw[i], C, gradMu[j]))*detJ;\n                mat3d Kww = (mat3dd(gradMw[i]*(s*gradMw[j])) + vdotTdotv(gradMw[i], C, gradMw[j]))*detJ;\n                \n                ke[ndpn*i  ][ndpn*j  ] += Kuu[0][0]; ke[ndpn*i  ][ndpn*j+1] += Kuu[0][1]; ke[ndpn*i  ][ndpn*j+2] += Kuu[0][2];\n                ke[ndpn*i+1][ndpn*j  ] += Kuu[1][0]; ke[ndpn*i+1][ndpn*j+1] += Kuu[1][1]; ke[ndpn*i+1][ndpn*j+2] += Kuu[1][2];\n                ke[ndpn*i+2][ndpn*j  ] += Kuu[2][0]; ke[ndpn*i+2][ndpn*j+1] += Kuu[2][1]; ke[ndpn*i+2][ndpn*j+2] += Kuu[2][2];\n                \n                ke[ndpn*i  ][ndpn*j+3] += Kuw[0][0]; ke[ndpn*i  ][ndpn*j+4] += Kuw[0][1]; ke[ndpn*i  ][ndpn*j+5] += Kuw[0][2];\n                ke[ndpn*i+1][ndpn*j+3] += Kuw[1][0]; ke[ndpn*i+1][ndpn*j+4] += Kuw[1][1]; ke[ndpn*i+1][ndpn*j+5] += Kuw[1][2];\n                ke[ndpn*i+2][ndpn*j+3] += Kuw[2][0]; ke[ndpn*i+2][ndpn*j+4] += Kuw[2][1]; ke[ndpn*i+2][ndpn*j+5] += Kuw[2][2];\n                \n                ke[ndpn*i+3][ndpn*j  ] += Kwu[0][0]; ke[ndpn*i+3][ndpn*j+1] += Kwu[0][1]; ke[ndpn*i+3][ndpn*j+2] += Kwu[0][2];\n                ke[ndpn*i+4][ndpn*j  ] += Kwu[1][0]; ke[ndpn*i+4][ndpn*j+1] += Kwu[1][1]; ke[ndpn*i+4][ndpn*j+2] += Kwu[1][2];\n                ke[ndpn*i+5][ndpn*j  ] += Kwu[2][0]; ke[ndpn*i+5][ndpn*j+1] += Kwu[2][1]; ke[ndpn*i+5][ndpn*j+2] += Kwu[2][2];\n                \n                ke[ndpn*i+3][ndpn*j+3] += Kww[0][0]; ke[ndpn*i+3][ndpn*j+4] += Kww[0][1]; ke[ndpn*i+3][ndpn*j+5] += Kww[0][2];\n                ke[ndpn*i+4][ndpn*j+3] += Kww[1][0]; ke[ndpn*i+4][ndpn*j+4] += Kww[1][1]; ke[ndpn*i+4][ndpn*j+5] += Kww[1][2];\n                ke[ndpn*i+5][ndpn*j+3] += Kww[2][0]; ke[ndpn*i+5][ndpn*j+4] += Kww[2][1]; ke[ndpn*i+5][ndpn*j+5] += Kww[2][2];\n                \n                // calculate the kpu matrix\n                gp = vec3d(0,0,0);\n                for (isol=0; isol<nsol; ++isol) gp += (D[isol]*gradc[isol])*(kappa[isol]/D0[isol]);\n                gp = gradp+gp*(R*T);\n                wu = vdotTdotv(-gp, dKedE, gradMu[j]);\n                ww = vdotTdotv(-gp, dKedE, gradMw[j]);\n                for (isol=0; isol<nsol; ++isol) {\n                    wu += (((Ke*(D[isol]*gradc[isol])) & gradMu[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradMu[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradMu[j]))*(-kappa[isol]*R*T/D0[isol]);\n                    ww += (((Ke*(D[isol]*gradc[isol])) & gradMw[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradMw[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradMw[j]))*(-kappa[isol]*R*T/D0[isol]);\n                }\n                qpu = Phie*gradMu[j];\n                qpw = Phie*gradMw[j];\n                vec3d kpu = (wu.transpose()*gradMu[i] + qpu*Mu[i])*(detJ*dt);\n                vec3d kpw = (ww.transpose()*gradMu[i] + qpw*Mu[i])*(detJ*dt);\n                vec3d kqu = (wu.transpose()*gradMw[i] + qpu*Mw[i])*(detJ*dt);\n                vec3d kqw = (ww.transpose()*gradMw[i] + qpw*Mw[i])*(detJ*dt);\n                ke[ndpn*i+6][ndpn*j  ] += kpu.x; ke[ndpn*i+6][ndpn*j+1] += kpu.y; ke[ndpn*i+6][ndpn*j+2] += kpu.z;\n                ke[ndpn*i+6][ndpn*j+3] += kpw.x; ke[ndpn*i+6][ndpn*j+4] += kpw.y; ke[ndpn*i+6][ndpn*j+5] += kpw.z;\n                ke[ndpn*i+7][ndpn*j  ] += kqu.x; ke[ndpn*i+7][ndpn*j+1] += kqu.y; ke[ndpn*i+7][ndpn*j+2] += kqu.z;\n                ke[ndpn*i+7][ndpn*j+3] += kqw.x; ke[ndpn*i+7][ndpn*j+4] += kqw.y; ke[ndpn*i+7][ndpn*j+5] += kqw.z;\n                \n                // calculate the kup matrix\n                vec3d kup = gradMu[i]*(-Mu[j]*detJ);\n                vec3d kuq = gradMu[i]*(-Mw[j]*detJ);\n                vec3d kwp = gradMw[i]*(-Mu[j]*detJ);\n                vec3d kwq = gradMw[i]*(-Mw[j]*detJ);\n                \n                ke[ndpn*i  ][ndpn*j+6] += kup.x; ke[ndpn*i  ][ndpn*j+7] += kuq.x;\n                ke[ndpn*i+1][ndpn*j+6] += kup.y; ke[ndpn*i+1][ndpn*j+7] += kuq.y;\n                ke[ndpn*i+2][ndpn*j+6] += kup.z; ke[ndpn*i+2][ndpn*j+7] += kuq.z;\n                \n                ke[ndpn*i+3][ndpn*j+6] += kwp.x; ke[ndpn*i+3][ndpn*j+7] += kwq.x;\n                ke[ndpn*i+4][ndpn*j+6] += kwp.y; ke[ndpn*i+4][ndpn*j+7] += kwq.y;\n                ke[ndpn*i+5][ndpn*j+6] += kwp.z; ke[ndpn*i+5][ndpn*j+7] += kwq.z;\n                \n                // calculate the kpp matrix\n                ke[ndpn*i+6][ndpn*j+6] += (Mu[i]*Mu[j]*Phip - gradMu[i]*(Ke*gradMu[j]))*(detJ*dt);\n                ke[ndpn*i+6][ndpn*j+7] += (Mu[i]*Mw[j]*Phip - gradMu[i]*(Ke*gradMw[j]))*(detJ*dt);\n                ke[ndpn*i+7][ndpn*j+6] += (Mw[i]*Mu[j]*Phip - gradMw[i]*(Ke*gradMu[j]))*(detJ*dt);\n                ke[ndpn*i+7][ndpn*j+7] += (Mw[i]*Mw[j]*Phip - gradMw[i]*(Ke*gradMw[j]))*(detJ*dt);\n                \n                // calculate kcu matrix data\n                jue.zero(); jwe.zero();\n                De.zero();\n                for (isol=0; isol<nsol; ++isol) {\n                    gc[isol] = -gradc[isol]*phiw + w*c[isol]/D0[isol];\n                    ju[isol] = ((D[isol]*gc[isol]) & gradMu[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradMu[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradMu[j])*(-phis)\n                       +(D[isol]*((gradMu[j]*w)*2) - ((D[isol]*w) & gradMu[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*wu*(kappa[isol]*c[isol]/D0[isol]);\n                    jw[isol] = ((D[isol]*gc[isol]) & gradMw[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradMw[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradMw[j])*(-phis)\n                       +(D[isol]*((gradMw[j]*w)*2) - ((D[isol]*w) & gradMw[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*ww*(kappa[isol]*c[isol]/D0[isol]);\n                    jue += ju[isol]*z[isol];\n                    jwe += jw[isol]*z[isol];\n                    De += D[isol]*(z[isol]*kappa[isol]*c[isol]/D0[isol]);\n                }\n                \n                for (isol=0; isol<nsol; ++isol) {\n                    \n                    // calculate the kcu matrix\n                    vec3d kcu = ((ju[isol]+jue*penalty).transpose()*gradMu[i])*(detJ*dt);\n                    vec3d kcw = ((jw[isol]+jwe*penalty).transpose()*gradMu[i])*(detJ*dt);\n                    vec3d kdu = ((ju[isol]+jue*penalty).transpose()*gradMw[i])*(detJ*dt);\n                    vec3d kdw = ((jw[isol]+jwe*penalty).transpose()*gradMw[i])*(detJ*dt);\n                    ke[ndpn*i+8+2*isol][ndpn*j  ] += kcu.x; ke[ndpn*i+8+2*isol][ndpn*j+1] += kcu.y; ke[ndpn*i+8+2*isol][ndpn*j+2] += kcu.z;\n                    ke[ndpn*i+8+2*isol][ndpn*j+3] += kcw.x; ke[ndpn*i+8+2*isol][ndpn*j+4] += kcw.y; ke[ndpn*i+8+2*isol][ndpn*j+5] += kcw.z;\n                    ke[ndpn*i+9+2*isol][ndpn*j  ] += kdu.x; ke[ndpn*i+9+2*isol][ndpn*j+1] += kdu.y; ke[ndpn*i+9+2*isol][ndpn*j+2] += kdu.z;\n                    ke[ndpn*i+9+2*isol][ndpn*j+3] += kdw.x; ke[ndpn*i+9+2*isol][ndpn*j+4] += kdw.y; ke[ndpn*i+9+2*isol][ndpn*j+5] += kdw.z;\n                    \n                    // calculate the kcp matrix\n                    ke[ndpn*i+8+2*isol][ndpn*j+6] -= (gradMu[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMu[j])\n                                                                 ))*(detJ*dt);\n                    ke[ndpn*i+8+2*isol][ndpn*j+7] -= (gradMu[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMw[j])\n                                                                 ))*(detJ*dt);\n                    ke[ndpn*i+9+2*isol][ndpn*j+6] -= (gradMw[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMu[j])\n                                                                 ))*(detJ*dt);\n                    ke[ndpn*i+9+2*isol][ndpn*j+7] -= (gradMw[i]*(\n                                                                 (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                                  +De*penalty)\n                                                                 *(Ke*gradMw[j])\n                                                                 ))*(detJ*dt);\n                    \n                    // calculate the kuc matrix\n                    sum = 0;\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        sum += c[jsol]*(dodc[isol]*kappa[jsol]+osmc*dkdc[jsol][isol]);\n                    vec3d kuc = (dTdc[isol]*gradMu[i] - gradMu[i]*(R*T*(osmc*kappa[isol]+sum)))*Mu[j]*detJ;\n                    vec3d kud = (dTdc[isol]*gradMu[i] - gradMu[i]*(R*T*(osmc*kappa[isol]+sum)))*Mw[j]*detJ;\n                    vec3d kwc = (dTdc[isol]*gradMw[i] - gradMw[i]*(R*T*(osmc*kappa[isol]+sum)))*Mu[j]*detJ;\n                    vec3d kwd = (dTdc[isol]*gradMw[i] - gradMw[i]*(R*T*(osmc*kappa[isol]+sum)))*Mw[j]*detJ;\n                    \n                    ke[ndpn*i  ][ndpn*j+8+2*isol] += kuc.x; ke[ndpn*i  ][ndpn*j+9+2*isol] += kud.x;\n                    ke[ndpn*i+1][ndpn*j+8+2*isol] += kuc.y; ke[ndpn*i+1][ndpn*j+9+2*isol] += kud.y;\n                    ke[ndpn*i+2][ndpn*j+8+2*isol] += kuc.z; ke[ndpn*i+2][ndpn*j+9+2*isol] += kud.z;\n                    \n                    ke[ndpn*i+3][ndpn*j+8+2*isol] += kwc.x; ke[ndpn*i+3][ndpn*j+9+2*isol] += kwd.x;\n                    ke[ndpn*i+4][ndpn*j+8+2*isol] += kwc.y; ke[ndpn*i+4][ndpn*j+9+2*isol] += kwd.y;\n                    ke[ndpn*i+5][ndpn*j+8+2*isol] += kwc.z; ke[ndpn*i+5][ndpn*j+9+2*isol] += kwd.z;\n                    \n                    // calculate the kpc matrix\n                    vtmp = vec3d(0,0,0);\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        vtmp += (D[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol])\n                                 +dDdc[jsol][isol]*kappa[jsol])/D0[jsol]*gradc[jsol];\n                    wc[isol] = (dKedc[isol]*gp)*(-Mu[j])\n                    -Ke*((D[isol]*gradMu[j])*(kappa[isol]/D0[isol])+vtmp*Mu[j])*(R*T);\n                    wd[isol] = (dKedc[isol]*gp)*(-Mw[j])\n                    -Ke*((D[isol]*gradMw[j])*(kappa[isol]/D0[isol])+vtmp*Mw[j])*(R*T);\n                    \n                    ke[ndpn*i+6][ndpn*j+8+2*isol] += (gradMu[i]*wc[isol])*(detJ*dt);\n                    ke[ndpn*i+6][ndpn*j+9+2*isol] += (gradMu[i]*wd[isol])*(detJ*dt);\n                    ke[ndpn*i+7][ndpn*j+8+2*isol] += (gradMw[i]*wc[isol])*(detJ*dt);\n                    ke[ndpn*i+7][ndpn*j+9+2*isol] += (gradMw[i]*wd[isol])*(detJ*dt);\n                    \n                }\n                \n                // calculate data for the kcc matrix\n                jce.assign(nsol, vec3d(0,0,0));\n                jde.assign(nsol, vec3d(0,0,0));\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        if (jsol != isol) {\n                            jc[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mu[j]\n                            +(D[isol]*(w*(-Mu[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            jd[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mw[j]\n                            +(D[isol]*(w*(-Mw[j]*dD0dc[isol][jsol]/D0[isol])+wd[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                        }\n                        else {\n                            jc[isol][jsol] = (D[isol]*(gradMu[j]*(-phiw)+w*(Mu[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mu[j]\n                            +(D[isol]*(w*(-Mu[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            jd[isol][jsol] = (D[isol]*(gradMw[j]*(-phiw)+w*(Mw[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*Mw[j]\n                            +(D[isol]*(w*(-Mw[j]*dD0dc[isol][jsol]/D0[isol])+wd[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                        }\n                        jce[jsol] += jc[isol][jsol]*z[isol];\n                        jde[jsol] += jd[isol][jsol]*z[isol];\n                        \n                        // chemical reactions\n                        dchatdc[isol][jsol] = 0;\n                        for (ireact=0; ireact<nreact; ++ireact)\n                            dchatdc[isol][jsol] += m_pMat->GetReaction(ireact)->m_v[isol]\n                            *m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                        \n                        // membrane reactions\n                        for (ireact=0; ireact<mreact; ++ireact)\n                            dchatdc[isol][jsol] += m_pMat->GetMembraneReaction(ireact)->m_v[isol]\n                            *m_pMat->GetMembraneReaction(ireact)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                    }\n                }\n                \n                // calculate the kcc matrix\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        ke[ndpn*i+8+2*isol][ndpn*j+8+2*jsol] += (gradMu[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                                 + Mu[i]*Mu[j]*phiw*dchatdc[isol][jsol])*(detJ*dt);\n                        ke[ndpn*i+8+2*isol][ndpn*j+9+2*jsol] += (gradMu[i]*(jd[isol][jsol]+jde[jsol]*penalty)\n                                                                 + Mu[i]*Mw[j]*phiw*dchatdc[isol][jsol])*(detJ*dt);\n                        ke[ndpn*i+9+2*isol][ndpn*j+8+2*jsol] += (gradMw[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                                 + Mw[i]*Mu[j]*phiw*dchatdc[isol][jsol])*(detJ*dt);\n                        ke[ndpn*i+9+2*isol][ndpn*j+9+2*jsol] += (gradMw[i]*(jd[isol][jsol]+jde[jsol]*penalty)\n                                                                 + Mw[i]*Mw[j]*phiw*dchatdc[isol][jsol])*(detJ*dt);\n                    }\n                }\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<ndpn*neln; ++i)\n            for (j=i+1; j<ndpn*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::MembraneReactionFluxes(FEGlobalVector& R)\n{\n    const int mreact = m_pMat->MembraneReactions();\n    if (mreact == 0) return;\n    \n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FEShellElement& el = m_Elem[i];\n        \n        // calculate internal force vector\n        ElementMembraneReactionFlux(el, fe);\n        \n        // get the element's LM vector\n        UnpackMembraneLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe, true);\n    }\n}\n//-----------------------------------------------------------------------------\n//! element external work of flux generated by membrane reactions\nvoid FEMultiphasicShellDomain::ElementMembraneReactionFlux(FEShellElement& el, vector<double>& fe)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n\n    // get the first material point\n    FEMaterialPoint& mp = *el.GetMaterialPoint(0);\n    FESolutesMaterialPoint& ps = *mp.ExtractData<FESolutesMaterialPoint>();\n    int nse = (int)ps.m_ce.size();\n    int nsi = (int)ps.m_ci.size();\n    int ndpn = 8 + nse + nsi;\n\n    // nr integration points\n    int nint = el.GaussPoints();\n    \n    // nr of element nodes\n    int neln = el.Nodes();\n    \n    fe.resize(neln*ndpn,0);\n    \n    // get the element's nodal positions\n    vec3d re[FEElement::MAX_NODES], ri[FEElement::MAX_NODES];\n    for (int j=0; j<neln; ++j) {\n        FENode& nd = GetFEModel()->GetMesh().Node(el.m_node[j]);\n        // nodal positions at front and back surfaces\n        re[j] = nd.m_rt;\n        ri[j] = nd.st();\n    }\n    \n    double *Mr, *Ms;\n    double *M;\n    double *w  = el.GaussWeights();\n    \n    vec3d dxer, dxes, dxet;\n    vec3d dxir, dxis, dxit;\n\n    // repeat over integration points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FESolutesMaterialPoint& ps = *mp.ExtractData<FESolutesMaterialPoint>();\n        \n        M  = el.H(n);\n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        \n        // evaluate normal solute flux at integration point\n        vector<double> je(nse,0), ji(nsi,0);\n        for (int ireact=0; ireact<m_pMat->MembraneReactions(); ++ireact) {\n            FEMembraneReaction* react = m_pMat->GetMembraneReaction(ireact);\n            FESoluteInterface* psm = react->m_psm;\n            double zbar = react->ReactionSupply(mp);\n            double zve = 0, zvi = 0;\n            for (int k=0; k<nse; ++k) {\n                int id = ps.m_ide[k];\n                zve += react->m_z[id]*react->m_ve[id];\n            }\n            for (int k=0; k<nsi; ++k) {\n                int id = ps.m_idi[k];\n                zvi += react->m_z[id]*react->m_vi[id];\n            }\n            // Divide fluxes by two because we are integrating over the shell volume\n            // but we should only integrate over the shell surface.\n            for (int k=0; k<nse; ++k)\n                je[k] -= zbar*(react->m_ve[ps.m_ide[k]] + zve)/2;\n            for (int k=0; k<nsi; ++k)\n                ji[k] -= zbar*(react->m_vi[ps.m_idi[k]] + zvi)/2;\n        }\n        \n        dxer = dxes = vec3d(0,0,0);\n        dxir = dxis = vec3d(0,0,0);\n        for (int i=0; i<neln; ++i)\n        {\n            dxer += re[i]*Mr[i];\n            dxes += re[i]*Ms[i];\n            dxir += ri[i]*Mr[i];\n            dxis += ri[i]*Ms[i];\n        }\n        double ae = (dxer ^ dxes).norm()*w[n]*dt;\n        double ai = (dxir ^ dxis).norm()*w[n]*dt;\n\n        for (int i=0; i<neln; ++i)\n        {\n            for (int k=0; k<nse; ++k)\n                fe[ndpn*i+8+k] += M[i]*je[k]*ae;\n            for (int k=0; k<nsi; ++k)\n                fe[ndpn*i+8+nse+k] += M[i]*ji[k]*ai;\n        }\n    }\n\n}\n\n\n//-----------------------------------------------------------------------------\n//! calculates the membrane reaction stiffness matrix for this domain\nvoid FEMultiphasicShellDomain::MembraneReactionStiffnessMatrix(FELinearSystem& LS)\n{\n    const int mreact = m_pMat->MembraneReactions();\n    if (mreact == 0) return;\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFEShellElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n\n\t\tvector<int> lm;\n        UnpackMembraneLM(el, lm);\n\t\tke.SetIndices(lm);\n        \n        // calculate the element stiffness matrix\n        ElementMembraneFluxStiffness(el, ke);\n        \n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the element membrane flux stiffness matrix\nbool FEMultiphasicShellDomain::ElementMembraneFluxStiffness(FEShellElement& el, matrix& ke)\n{\n    double dt = GetFEModel()->GetTime().timeIncrement;\n\n    // get the first material point\n    FEMaterialPoint& mp = *el.GetMaterialPoint(0);\n    FESolutesMaterialPoint& ps = *mp.ExtractData<FESolutesMaterialPoint>();\n    int nse = (int)ps.m_ce.size();\n    int nsi = (int)ps.m_ci.size();\n    int ndpn = 8 + nse + nsi;\n\n    // nr integration points\n    int nint = el.GaussPoints();\n    \n    // nr of element nodes\n    int neln = el.Nodes();\n    int ndof = ndpn*neln;\n    \n    // allocate stiffness matrix\n    ke.resize(ndof, ndof);\n    \n    // get the element's nodal coordinates\n    vec3d re[FEElement::MAX_NODES], ri[FEElement::MAX_NODES];\n    for (int j=0; j<neln; ++j) {\n        FENode& nd = GetFEModel()->GetMesh().Node(el.m_node[j]);\n        re[j] = nd.m_rt;\n        ri[j] = nd.st();\n    }\n    \n    double *Mr, *Ms;\n    double *M;\n    double *w  = el.GaussWeights();\n    \n    vec3d dxir, dxis, dxit;\n    vec3d dxer, dxes, dxet;\n    \n    // repeat over integration points\n    ke.zero();\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FESolutesMaterialPoint& ps = *mp.ExtractData<FESolutesMaterialPoint>();\n\n        M  = el.H(n);\n        Mr = el.Hr(n);\n        Ms = el.Hs(n);\n        \n        // evaluate normal solute flux and its derivatives at integration point\n        // take summation over all membrane reactions\n        vector<double> je(nse,0), ji(nsi,0);\n        vector<double> djedJ(nse,0), djidJ(nsi,0);\n        vector<double> djedp(nse,0), djidp(nsi,0);\n        vector< vector<double> > djedc(nse,vector<double>(nse,0));\n        vector< vector<double> > djidc(nsi,vector<double>(nsi,0));\n        for (int ireact=0; ireact<m_pMat->MembraneReactions(); ++ireact) {\n            FEMembraneReaction* react = m_pMat->GetMembraneReaction(ireact);\n            FESoluteInterface* psm = react->m_psm;\n            double zbar = react->ReactionSupply(mp);\n            double dzdJ = react->Tangent_ReactionSupply_Strain(mp);\n            double dzdpe = react->Tangent_ReactionSupply_Pe(mp);\n            double dzdpi = react->Tangent_ReactionSupply_Pi(mp);\n            vector<double> dzdce(nse,0), dzdci(nsi,0);\n            double zve = 0, zvi = 0;\n            for (int k=0; k<nse; ++k) {\n                dzdce[k] = react->Tangent_ReactionSupply_Ce(mp, k);\n                int id = ps.m_ide[k];\n                zve += react->m_z[id]*react->m_ve[id];\n            }\n            for (int k=0; k<nsi; ++k) {\n                dzdci[k] = react->Tangent_ReactionSupply_Ci(mp, k);\n                int id = ps.m_idi[k];\n                zvi += react->m_z[id]*react->m_vi[id];\n            }\n            // Divide fluxes by two because we are integrating over the shell volume\n            // but we should only integrate over the shell surface.\n            for (int k=0; k<nse; ++k) {\n                je[k] -= zbar*(react->m_ve[ps.m_ide[k]] + zve)/2;\n                djedJ[k] -= dzdJ*(react->m_ve[ps.m_ide[k]] + zve)/2;\n                djedp[k] -= dzdpe*(react->m_ve[ps.m_ide[k]] + zve)/2;\n                for (int l=0; l<nse; ++l)\n                    djedc[k][l] -= dzdce[l]*(react->m_ve[ps.m_ide[k]] + zve)/2;\n            }\n            for (int k=0; k<nsi; ++k) {\n                ji[k] -= zbar*(react->m_vi[ps.m_idi[k]] + zvi)/2;\n                djidJ[k] -= dzdJ*(react->m_vi[ps.m_idi[k]] + zvi)/2;\n                djidp[k] -= dzdpi*(react->m_vi[ps.m_idi[k]] + zvi)/2;\n                for (int l=0; l<nsi; ++l)\n                    djidc[k][l] -= dzdci[l]*(react->m_vi[ps.m_idi[k]] + zvi)/2;\n            }\n        }\n\n        dxer = dxes = vec3d(0,0,0);\n        dxir = dxis = vec3d(0,0,0);\n        for (int i=0; i<neln; ++i)\n        {\n            dxer += re[i]*Mr[i];\n            dxes += re[i]*Ms[i];\n            dxir += ri[i]*Mr[i];\n            dxis += ri[i]*Ms[i];\n        }\n        dxet = dxer ^ dxes;\n        dxit = dxir ^ dxis;\n        double Je = (dxer ^ dxes).norm();\n        double Ji = (dxir ^ dxis).norm();\n        vec3d ne = dxet/Je;\n        vec3d ni = dxit/Ji;\n        \n        for (int i=0; i<neln; ++i) {\n            int ir = ndpn*i+8;\n            for (int j=0; j<neln; ++j)\n            {\n                int ic = ndpn*j;\n                // front face\n                vec3d ge = dxes*Mr[j] - dxer*Ms[j];\n                mat3d Ae; Ae.skew(ge);\n                vec3d ve = Ae*ne;\n                for (int k=0; k<nse; ++k) {\n                    vec3d kcu = ve*(je[k] + djedJ[k]*Je)*M[i]*w[n]*dt;\n                    ke[ir+k][ic  ] -= kcu.x;\n                    ke[ir+k][ic+1] -= kcu.y;\n                    ke[ir+k][ic+2] -= kcu.z;\n                    \n                    double kcp = djedp[k]*M[i]*M[j]*Je*w[n]*dt;\n                    ke[ir+k][ic+6] -= kcp;\n                    \n                    for (int l=0; l<nse; ++l) {\n                        double kcc = djedc[k][l]*M[i]*M[j]*Je*w[n]*dt;\n                        ke[ir+k][ic+8+l] -= kcc;\n                    }\n                }\n                // back face\n                vec3d gi = dxis*Mr[j] - dxir*Ms[j];\n                mat3d Ai; Ai.skew(gi);\n                vec3d vi = Ai*ni;\n                for (int k=0; k<nsi; ++k) {\n                    vec3d kdw = vi*(ji[k] + djidJ[k]*Ji)*M[i]*w[n]*dt;\n                    ke[ir+nse+k][ic+3] -= kdw.x;\n                    ke[ir+nse+k][ic+4] -= kdw.y;\n                    ke[ir+nse+k][ic+5] -= kdw.z;\n                    \n                    double kdq = djidp[k]*M[i]*M[j]*Ji*w[n]*dt;\n                    ke[ir+nse+k][ic+7] -= kdq;\n                    \n                    for (int l=0; l<nsi; ++l) {\n                        double kdd = djidc[k][l]*M[i]*M[j]*Ji*w[n]*dt;\n                        ke[ir+nse+k][ic+8+nse+l] -= kdd;\n                    }\n                }\n            }\n        }\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::Update(const FETimeInfo& tp)\n{\n\tFESSIShellDomain::Update(tp);\n\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i, tp);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicShellDomain::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n    double dt = tp.timeIncrement;\n    \n    int j, k, n;\n    int nint, neln;\n    double* gw;\n    vec3d r0[FEElement::MAX_NODES];\n    vec3d rt[FEElement::MAX_NODES];\n    double pn[FEElement::MAX_NODES], qn[FEElement::MAX_NODES];\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    int MAX_DDOFS = fedofs.GetVariableSize(\"shell concentration\");\n\n    FEMesh& mesh = *m_pMesh;\n    \n    // get the multiphasic material\n    FEMultiphasic* pmb = m_pMat;\n    const int nsol = (int)pmb->Solutes();\n    vector<int> sid(nsol);\n    for (j=0; j<nsol; ++j) sid[j] = pmb->GetSolute(j)->GetSoluteDOF();\n    \n    // get the shell element\n    FEShellElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    nint = el.GaussPoints();\n    \n    // get the number of nodes\n    neln = el.Nodes();\n    vector< vector<double> > cn(MAX_CDOFS, vector<double>(neln));\n    vector< vector<double> > dn(MAX_DDOFS, vector<double>(neln));\n\n    // get the integration weights\n    gw = el.GaussWeights();\n    \n    const double *M, *Mr, *Ms;\n    \n    // get the nodal data\n    for (j=0; j<neln; ++j)\n    {\n        r0[j] = mesh.Node(el.m_node[j]).m_r0;\n        rt[j] = mesh.Node(el.m_node[j]).m_rt;\n        pn[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        qn[j] = mesh.Node(el.m_node[j]).get(m_dofQ);\n        for (k=0; k<MAX_CDOFS; ++k)\n            cn[k][j] = mesh.Node(el.m_node[j]).get(m_dofC + k);\n        for (k=0; k<MAX_DDOFS; ++k)\n            dn[k][j] = mesh.Node(el.m_node[j]).get(m_dofD + k);\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //\t\t since the material point coordinates are used by most materials.\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(rt, n);\n        \n        // get the deformation gradient and determinant\n        pt.m_J = defgrad(el, pt.m_F, n);\n        mat3d Fp;\n        defgradp(el, Fp, n);\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (pt.m_F - Fp)*Fi / dt;\n\n        // multiphasic material point data\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // update membrane reaction data if needed\n        if (m_pMat->MembraneReactions()) {\n            int nse = (int)spt.m_ce.size();\n            int nsi = (int)spt.m_ci.size();\n            M = el.H(n);\n            Mr = el.Hr(n);\n            Ms = el.Hs(n);\n            double pe = 0, pi = 0;\n            vector<double> ce(nse,0), ci(nsi,0);\n            vec3d dxr(0,0,0), dxs(0,0,0);\n            vec3d dXr(0,0,0), dXs(0,0,0);\n            for (int j=0; j<neln; ++j) {\n                dxr += rt[j]*Mr[j];\n                dxs += rt[j]*Ms[j];\n                dXr += r0[j]*Mr[j];\n                dXs += r0[j]*Ms[j];\n                pe += pn[j]*M[j];\n                pi += qn[j]*M[j];\n                for (int k=0; k<nse; ++k)\n                    ce[k] += cn[spt.m_ide[k]][j]*M[j];\n                for (int k=0; k<nsi; ++k)\n                    ci[k] += dn[spt.m_idi[k]][j]*M[j];\n            }\n            spt.m_strain = (dxr ^ dxs).norm()/(dXr ^ dXs).norm();\n            spt.m_pe = pe;\n            spt.m_pi = pi;\n            spt.m_ce = ce;\n            spt.m_ci = ci;\n        }\n        \n        for (k=0; k<nsol; ++k) {\n            // evaluate effective solute concentrations at gauss-point\n            spt.m_c[k] = evaluate(el, cn[sid[k]], dn[sid[k]], n);\n            // calculate the gradient of c at gauss-point\n            spt.m_gradc[k] = gradient(el, cn[sid[k]], dn[sid[k]], n);\n        }\n        \n        // update SBM referential densities\n        pmb->UpdateSolidBoundMolecules(mp);\n        \n        // evaluate referential solid volume fraction\n        ppt.m_phi0t = pmb->SolidReferentialVolumeFraction(mp);\n        if (m_breset) ppt.m_phi0 = ppt.m_phi0t;\n        \n        // evaluate fluid pressure at gauss-point\n        ppt.m_p = evaluate(el, pn, qn, n);\n        \n        // calculate the gradient of p at gauss-point\n        ppt.m_gradp = gradient(el, pn, qn, n);\n        \n        // update the fluid and solute fluxes\n        // and evaluate the actual fluid pressure and solute concentration\n        ppt.m_w = pmb->FluidFlux(mp);\n        spt.m_psi = pmb->ElectricPotential(mp);\n        for (k=0; k<nsol; ++k) {\n            spt.m_ca[k] = pmb->Concentration(mp,k);\n            spt.m_j[k] = pmb->SoluteFlux(mp,k);\n        }\n        ppt.m_pa = pmb->Pressure(mp);\n        spt.m_cF = pmb->FixedChargeDensity(mp);\n        spt.m_Ie = pmb->CurrentDensity(mp);\n        pmb->PartitionCoefficientFunctions(mp, spt.m_k, spt.m_dkdJ, spt.m_dkdc,\n                                           spt.m_dkdr, spt.m_dkdJr, spt.m_dkdrc);\n\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = pmb->GetElasticMaterial()->Stress(mp);\n        \n        // evaluate the stress\n        pt.m_s = pmb->Stress(mp);\n        \n        // evaluate the referential solid density\n        spt.m_rhor = pmb->SolidReferentialApparentDensity(mp);\n        \n        // update chemical reaction element data\n        for (int j=0; j<m_pMat->Reactions(); ++j)\n            pmb->GetReaction(j)->UpdateElementData(mp);\n        \n        // update membrane reaction element data\n        for (int j=0; j<m_pMat->MembraneReactions(); ++j)\n            pmb->GetMembraneReaction(j)->UpdateElementData(mp);\n        \n    }\n    if (m_breset) m_breset = false;\n}\n\n//-----------------------------------------------------------------------------\n// Extract the solute DOFs for the solid elements on either side of the shell domain\nvoid FEMultiphasicShellDomain::UpdateShellMPData(int iel)\n{\n    if (m_pMat->MembraneReactions() == 0) return;\n    \n    static bool bfirst = true;\n    \n    if (bfirst) {\n        FEMesh& mesh = *GetMesh();\n\n        // get the shell element\n        FEShellElement& el = m_Elem[iel];\n        FEElement* si = mesh.FindElementFromID(el.m_elem[0]);\n        FEElement* se = mesh.FindElementFromID(el.m_elem[1]);\n        if ((si == nullptr) || (se == nullptr)) return;\n        FEMultiphasic* mpi = dynamic_cast<FEMultiphasic*>(GetFEModel()->GetMaterial(si->GetMatID()));\n        FEMultiphasic* mpe = dynamic_cast<FEMultiphasic*>(GetFEModel()->GetMaterial(se->GetMatID()));\n        if ((mpi == nullptr) || (mpe == nullptr)) return;\n\n        vector<int> idi(mpi->Solutes(),-1);\n        vector<int> ide(mpe->Solutes(),-1);\n        for (int i=0; i<mpi->Solutes(); ++i) idi[i] = mpi->GetSolute(i)->GetSoluteDOF();\n        for (int i=0; i<mpe->Solutes(); ++i) ide[i] = mpe->GetSolute(i)->GetSoluteDOF();\n\n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            ps.m_ide = ide;\n            ps.m_idi = idi;\n            ps.m_ce.resize(ide.size());\n            ps.m_ci.resize(idi.size());\n        }\n        \n        bfirst = false;\n    }\n}\n\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FESSIShellDomain.h>\n#include <FECore/FEDofList.h>\n#include \"FEMultiphasic.h\"\n#include \"FEMultiphasicDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Domain class for multiphasic 3D solid elements\n//! Note that this class inherits from FEElasticSolidDomain since this domain\n//! also needs to calculate elastic stiffness contributions.\n//!\nclass FEBIOMIX_API FEMultiphasicShellDomain : public FESSIShellDomain, public FEMultiphasicDomain\n{\npublic:\n    //! constructor\n    FEMultiphasicShellDomain(FEModel* pfem);\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! get the material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\n\t//! get the total dof\n\tconst FEDofList& GetDOFList() const override;\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n    \n    //! Unpack element data (overridden from FEDomain)\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! initialize elements for this domain\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS, bool bsymm) override;\n    \n    //! calculates the global stiffness matrix for this domain (steady-state case)\n    void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) override;\n    \n    //! calculates the membrane reaction stiffness matrix for this domain\n    void MembraneReactionStiffnessMatrix(FELinearSystem& LS);\n    \n    //! initialize class\n\tbool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! initialize material points in the domain\n    void InitMaterialPoints() override;\n    \n    // update domain data\n    void Update(const FETimeInfo& tp) override;\n    \n    // update element state data\n    void UpdateElementStress(int iel, const FETimeInfo& tp);\n    \n    // update element shell material points if membrane reactions are present\n    void UpdateShellMPData(int iel);\n\n    //! Unpack element data (overridden from FEDomain)\n    void UnpackMembraneLM(FEShellElement& el, vector<int>& lm);\n    \n    //! build connectivity for matrix profile\n    void BuildMatrixProfile(FEGlobalMatrix& M) override;\n    \npublic:\n    \n    // internal work (overridden from FEElasticDomain)\n    void InternalForces(FEGlobalVector& R) override;\n    \n    // internal work (steady-state case)\n    void InternalForcesSS(FEGlobalVector& R) override;\n    \n    // external work of flux generated by membrane reactions\n    void MembraneReactionFluxes(FEGlobalVector& R);\n    \npublic:\n    //! element internal force vector\n    void ElementInternalForce(FEShellElement& el, vector<double>& fe);\n    \n    //! element internal force vector (steady-state case)\n    void ElementInternalForceSS(FEShellElement& el, vector<double>& fe);\n    \n    //! element external work of flux generated by membrane reactions\n    void ElementMembraneReactionFlux(FEShellElement& el, vector<double>& fe);\n    \n    //! calculates the element multiphasic stiffness matrix\n    bool ElementMultiphasicStiffness(FEShellElement& el, matrix& ke, bool bsymm);\n    \n    //! calculates the element multiphasic stiffness matrix\n    bool ElementMultiphasicStiffnessSS(FEShellElement& el, matrix& ke, bool bsymm);\n    \n    //! calculates the element membrane flux stiffness matrix\n    bool ElementMembraneFluxStiffness(FEShellElement& el, matrix& ke);\n    \nprotected: // overridden from FEElasticDomain, but not implemented in this domain\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n    void MassMatrix(FELinearSystem& LS, double scale) override {}\n    \nprotected:\n\tFEDofList\tm_dofSU;\n\tFEDofList\tm_dofR;\n\tFEDofList\tm_dof;\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicSolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicSolidDomain.h\"\n#include \"FEMultiphasicMultigeneration.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/DOFS.h>\n#include <FEBioMech/FEBioMech.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/sys.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\nFEMultiphasicSolidDomain::FEMultiphasicSolidDomain(FEModel* pfem) : FESolidDomain(pfem), FEMultiphasicDomain(pfem), m_dofU(pfem), m_dofSU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n    m_pMat = nullptr;\n\t\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n        m_dofSU.AddVariable(FEBioMech::GetVariableName(FEBioMech::SHELL_DISPLACEMENT));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n    m_pMat = dynamic_cast<FEMultiphasic*>(pmat);\n    assert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n// get total dof list\nconst FEDofList& FEMultiphasicSolidDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data.\nvoid FEMultiphasicSolidDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    // get nodal DOFS\n    const int nsol = m_pMat->Solutes();\n    \n    int N = el.Nodes();\n    int ndpn = 4+nsol;\n    lm.resize(N*(ndpn+3));\n    \n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        FENode& node = m_pMesh->Node(n);\n        \n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[ndpn*i  ] = id[m_dofU[0]];\n        lm[ndpn*i+1] = id[m_dofU[1]];\n        lm[ndpn*i+2] = id[m_dofU[2]];\n        \n        // now the pressure dofs\n        lm[ndpn*i+3] = id[m_dofP];\n        \n        // concentration dofs\n        for (int k=0; k<nsol; ++k)\n            lm[ndpn*i+4+k] = id[m_dofC+m_pMat->GetSolute(k)->GetSoluteDOF()];\n        \n        // rigid rotational dofs\n        // TODO: Do we really need this?\n        lm[ndpn*N + 3*i  ] = id[m_dofR[0]];\n        lm[ndpn*N + 3*i+1] = id[m_dofR[1]];\n        lm[ndpn*N + 3*i+2] = id[m_dofR[2]];\n    }\n    \n    // substitute interface dofs for solid-shell interfaces\n\tFESolidElement& sel = static_cast<FESolidElement&>(el);\n    for (int i=0; i<sel.m_bitfc.size(); ++i)\n    {\n        if (sel.m_bitfc[i]) {\n            FENode& node = m_pMesh->Node(sel.m_node[i]);\n            vector<int>& id = node.m_ID;\n            \n            // first the back-face displacement dofs\n            lm[ndpn*i  ] = id[m_dofSU[0]];\n            lm[ndpn*i+1] = id[m_dofSU[1]];\n            lm[ndpn*i+2] = id[m_dofSU[2]];\n            \n            // now the pressure dof (if the shell has it)\n            if (id[m_dofQ] != -1) lm[ndpn*i+3] = id[m_dofQ];\n            \n            // concentration dofs\n            for (int k=0; k<nsol; ++k) {\n                int dofd = m_dofD+m_pMat->GetSolute(k)->GetSoluteDOF();\n                if (id[dofd] != -1) lm[ndpn*i+4+k] = id[dofd];\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FEMultiphasicSolidDomain::Init()\n{\n    // initialize base class\n\tif (FESolidDomain::Init() == false) return false;\n    \n    // extract the initial concentrations of the solid-bound molecules\n    const int nsbm = m_pMat->SBMs();\n    const int nsol = m_pMat->Solutes();\n    \n    for (int i = 0; i<(int)m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            // initialize multiphasic solutes\n            ps.m_nsol = nsol;\n            ps.m_c.assign(nsol,0);\n            ps.m_ca.assign(nsol,0);\n            ps.m_crp.assign(nsol, 0);\n            ps.m_gradc.assign(nsol,vec3d(0,0,0));\n            ps.m_k.assign(nsol, 0);\n            ps.m_dkdJ.assign(nsol, 0);\n            ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n            ps.m_j.assign(nsol,vec3d(0,0,0));\n            ps.m_bsb.assign(nsol, false);\n            ps.m_nsbm = nsbm;\n            ps.m_sbmr.assign(nsbm,0);\n            ps.m_sbmrp.assign(nsbm,0);\n            ps.m_sbmrhat.assign(nsbm,0);\n            ps.m_sbmrhatp.assign(nsbm,0);\n            ps.m_sbmrmin.assign(nsbm,0);\n            ps.m_sbmrmax.assign(nsbm,0);\n\n            // assign bounds on apparent densities of the solid-bound molecules\n            for (int i = 0; i<nsbm; ++i) {\n                ps.m_sbmr[i] = ps.m_sbmrp[i] = m_pMat->GetSBM(i)->m_rho0(mp);\n                ps.m_sbmrmin[i] = m_pMat->GetSBM(i)->m_rhomin;\n                ps.m_sbmrmax[i] = m_pMat->GetSBM(i)->m_rhomax;\n            }\n\n            // initialize referential solid volume fraction\n            pb.m_phi0 = pb.m_phi0t = m_pMat->SolidReferentialVolumeFraction(mp);\n            if (pb.m_phi0 > 1.0) {\n                feLogError(\"Referential solid volume fraction of multiphasic material cannot exceed unity!\\nCheck ratios of sbm apparent and true densities.\");\n                return false;\n            }\n            \n            // evaluate reaction rates at initial time\n            // check if this mixture includes chemical reactions\n            int nreact = (int)m_pMat->Reactions();\n            if (nreact) {\n                // for chemical reactions involving solid-bound molecules,\n                // update their concentration\n                // multiphasic material point data\n                FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n                \n                double phi0 = pb.m_phi0t;\n                for (int isbm=0; isbm<nsbm; ++isbm) {\n                    // combine the molar supplies from all the reactions\n                    for (int k=0; k<nreact; ++k) {\n                        double zetahat = m_pMat->GetReaction(k)->ReactionSupply(mp);\n                        double v = m_pMat->GetReaction(k)->m_v[nsol+isbm];\n                        // remember to convert from molar supply to referential mass supply\n                        ps.m_sbmrhat[isbm] += (pt.m_J-phi0)*m_pMat->SBMMolarMass(isbm)*v*zetahat;\n                    }\n                }\n            }\n        }\n    }\n\n\t// set the active degrees of freedom list\n\tFEDofList dofs(GetFEModel());\n\tfor (int i=0; i<nsol; ++i)\n\t{\n\t\tint m = m_pMat->GetSolute(i)->GetSoluteDOF();\n\t\tdofs.AddDof(m_dofC + m);\n        dofs.AddDof(m_dofD + m);\n\t}\n\tm_dof = dofs;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::Activate()\n{\n    for (int i=0; i<Nodes(); ++i)\n    {\n        FENode& node = Node(i);\n        if (node.HasFlags(FENode::EXCLUDE) == false)\n        {\n            if (node.m_rid < 0)\n            {\n                node.set_active(m_dofU[0]);\n                node.set_active(m_dofU[1]);\n                node.set_active(m_dofU[2]);\n            }\n        }\n    }\n    \n    const int nsol = m_pMat->Solutes();\n\n    // Activate dof_P and dof_C, except when a solid element is connected to the\n    // back of a shell element, in which case activate dof_Q and dof_D for those nodes.\n    FEMesh& m = *GetMesh();\n    for (int i=0; i<Elements(); ++i) {\n        FESolidElement& el = m_Elem[i];\n        int neln = el.Nodes();\n        for (int j=0; j<neln; ++j)\n        {\n            FENode& node = m.Node(el.m_node[j]);\n            if (el.m_bitfc.size()>0 && el.m_bitfc[j]) {\n                node.set_active(m_dofQ);\n                for (int l=0; l<nsol; ++l)\n                    node.set_active(m_dofD + m_pMat->GetSolute(l)->GetSoluteDOF());\n            }\n            else {\n                node.set_active(m_dofP);\n                for (int l=0; l<nsol; ++l)\n                    node.set_active(m_dofC + m_pMat->GetSolute(l)->GetSoluteDOF());\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::InitMaterialPoints()\n{\n    const int nsol = m_pMat->Solutes();\n    FEMesh& m = *GetMesh();\n\n    // fix initial conditions for solid element nodes that are attached to the back of shells\n    // this is needed because initial conditions for solid elements are prescribed to m_dofC\n    // but we have to use m_dofD degrees of freedom for those solid element nodes\n    for (int i=0; i<Elements(); ++i) {\n        FESolidElement& el = m_Elem[i];\n        // only process solid elements attached to the back of a shell\n        if (el.m_bitfc.size()>0) {\n            int neln = el.Nodes();\n            vector<double> cic(nsol,0);\n            // get the solute concentrations from nodes not attached to shells\n            for (int j=0; j<neln; ++j)\n            {\n                FENode& node = m.Node(el.m_node[j]);\n                if (!el.m_bitfc[j]) {\n                    for (int l=0; l<nsol; ++l)\n                        cic[l] = node.get(m_dofC + m_pMat->GetSolute(l)->GetSoluteDOF());\n                    break;\n                }\n            }\n            // assign those concentrations to nodes attached to shells\n            for (int j=0; j<neln; ++j)\n            {\n                FENode& node = m.Node(el.m_node[j]);\n                if (el.m_bitfc[j]) {\n                    for (int l=0; l<nsol; ++l)\n                        node.set(m_dofD + m_pMat->GetSolute(l)->GetSoluteDOF(), cic[l]);\n                }\n            }\n        }\n    }\n\n    const int nsbm = m_pMat->SBMs();\n    \n    const int NE = FEElement::MAX_NODES;\n    double p0[NE];\n    vector< vector<double> > c0(nsol, vector<double>(NE));\n    vector<int> sid(nsol);\n    for (int j = 0; j<nsol; ++j) sid[j] = m_pMat->GetSolute(j)->GetSoluteDOF();\n    \n    for (int j = 0; j<(int)m_Elem.size(); ++j)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[j];\n        \n        // get the number of nodes\n        int neln = el.Nodes();\n        // get initial values of fluid pressure and solute concentrations\n        if (el.m_bitfc.size() == 0) {\n            for (int i = 0; i<neln; ++i)\n            {\n                FENode& ni = m.Node(el.m_node[i]);\n                p0[i] = ni.get(m_dofP);\n                for (int isol = 0; isol<nsol; ++isol)\n                    c0[isol][i] = ni.get(m_dofC + sid[isol]);\n            }\n        }\n        else {\n            for (int i = 0; i<neln; ++i)\n            {\n                FENode& ni = m.Node(el.m_node[i]);\n                p0[i] = el.m_bitfc[i] ? ni.get(m_dofQ) : ni.get(m_dofP);\n                for (int isol = 0; isol<nsol; ++isol)\n                    c0[isol][i] = (ni.m_ID[m_dofD + isol] != -1) ? ni.get(m_dofD + sid[isol]) : ni.get(m_dofC + sid[isol]);\n            }\n        }\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n = 0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEElasticMaterialPoint& pm = *(mp.ExtractData<FEElasticMaterialPoint>());\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            // initialize effective fluid pressure and its gradient\n            pt.m_p = el.Evaluate(p0, n);\n            pt.m_gradp = gradient(el, p0, n);\n            \n            // initialize multiphasic solutes\n            ps.m_nsol = nsol;\n            ps.m_nsbm = nsbm;\n            \n            // initialize effective solute concentrations\n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_c[isol] = el.Evaluate(c0[isol], n);\n                ps.m_gradc[isol] = gradient(el, c0[isol], n);\n            }\n            \n            // determine if solute is 'solid-bound'\n            for (int isol = 0; isol<nsol; ++isol) {\n                FESolute* soli = m_pMat->GetSolute(isol);\n                if (soli->m_pDiff->Diffusivity(mp).norm() == 0) ps.m_bsb[isol] = true;\n                // initialize solute concentrations\n                ps.m_ca[isol] = m_pMat->Concentration(mp, isol);\n            }\n            \n            // initialize referential solid volume fraction\n            pt.m_phi0t = m_pMat->SolidReferentialVolumeFraction(mp);\n            \n            // initialize electric potential\n            ps.m_psi = m_pMat->ElectricPotential(mp);\n            \n            // initialize fluxes\n            pt.m_w = m_pMat->FluidFlux(mp);\n            \n            for (int isol = 0; isol<nsol; ++isol) {\n                ps.m_j[isol] = m_pMat->SoluteFlux(mp, isol);\n                ps.m_crp[isol] = pm.m_J*m_pMat->Porosity(mp)*ps.m_ca[isol];\n            }\n            \n            pt.m_pa = m_pMat->Pressure(mp);\n            \n            // calculate FCD, current and stress\n            ps.m_cF = m_pMat->FixedChargeDensity(mp);\n            ps.m_Ie = m_pMat->CurrentDensity(mp);\n            pm.m_s = m_pMat->Stress(mp);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::Reset()\n{\n    // reset base class\n    FESolidDomain::Reset();\n    \n    const int nsol = m_pMat->Solutes();\n    const int nsbm = m_pMat->SBMs();\n    \n    for (int i=0; i<(int) m_Elem.size(); ++i)\n    {\n        // get the solid element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the number of integration points\n        int nint = el.GaussPoints();\n        \n        // loop over the integration points\n        for (int n=0; n<nint; ++n)\n        {\n            FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            // initialize multiphasic solutes\n            ps.m_nsol = nsol;\n            ps.m_c.assign(nsol,0);\n            ps.m_ca.assign(nsol,0);\n            ps.m_crp.assign(nsol, 0);\n            ps.m_gradc.assign(nsol,vec3d(0,0,0));\n            ps.m_k.assign(nsol, 0);\n            ps.m_dkdJ.assign(nsol, 0);\n            ps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n            ps.m_j.assign(nsol,vec3d(0,0,0));\n            ps.m_bsb.assign(nsol, false);\n            ps.m_nsbm = nsbm;\n            ps.m_sbmr.assign(nsbm,0);\n            ps.m_sbmrp.assign(nsbm,0);\n            ps.m_sbmrhat.assign(nsbm,0);\n            ps.m_sbmrhatp.assign(nsbm,0);\n            ps.m_sbmrmin.assign(nsbm,0);\n            ps.m_sbmrmax.assign(nsbm,0);\n\n            // assign bounds on apparent densities of the solid-bound molecules\n            for (int i = 0; i<nsbm; ++i) {\n                ps.m_sbmr[i] = ps.m_sbmrp[i] = m_pMat->GetSBM(i)->m_rho0(mp);\n                ps.m_sbmrmin[i] = m_pMat->GetSBM(i)->m_rhomin;\n                ps.m_sbmrmax[i] = m_pMat->GetSBM(i)->m_rhomax;\n            }\n\n            // initialize referential solid volume fraction\n            pt.m_phi0 = pt.m_phi0t = m_pMat->SolidReferentialVolumeFraction(mp);\n\n            // reset chemical reaction element data\n            ps.m_cri.clear();\n            ps.m_crd.clear();\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->ResetElementData(mp);\n        }\n    }\n    m_breset = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n    FESolidDomain::PreSolveUpdate(timeInfo);\n    \n    const int NE = FEElement::MAX_NODES;\n    vec3d x0[NE], xt[NE], r0, rt;\n    FEMesh& m = *GetMesh();\n    for (size_t iel=0; iel<m_Elem.size(); ++iel)\n    {\n        FESolidElement& el = m_Elem[iel];\n        int neln = el.Nodes();\n        for (int i=0; i<neln; ++i)\n        {\n            x0[i] = m.Node(el.m_node[i]).m_r0;\n            xt[i] = m.Node(el.m_node[i]).m_rt;\n        }\n        \n        int n = el.GaussPoints();\n        for (int j=0; j<n; ++j)\n        {\n            r0 = el.Evaluate(x0, j);\n            rt = el.Evaluate(xt, j);\n            \n            FEMaterialPoint& mp = *el.GetMaterialPoint(j);\n            FEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            FEMultigenSBMMaterialPoint* pmg = mp.ExtractData<FEMultigenSBMMaterialPoint>();\n            \n            mp.m_r0 = r0;\n            mp.m_rt = rt;\n            \n            pe.m_J = defgrad(el, pe.m_F, j);\n            \n            // reset determinant of solid deformation gradient at previous time\n            pt.m_Jp = pe.m_J;\n            \n            // reset referential solid volume fraction at previous time\n            pt.m_phi0p = pt.m_phi0t;\n            \n            // reset referential actual solute concentration at previous time\n            for (int j=0; j<m_pMat->Solutes(); ++j) {\n                ps.m_crp[j] = pe.m_J*m_pMat->Porosity(mp)*ps.m_ca[j];\n            }\n            \n            // reset referential solid-bound molecule concentrations at previous time\n            ps.m_sbmrp = ps.m_sbmr;\n            ps.m_sbmrhatp = ps.m_sbmrhat;\n            \n            // reset generational referential solid-bound molecule concentrations at previous time\n            if (pmg) {\n                for (int i=0; i<pmg->m_ngen; ++i) {\n                    for (int j=0; j<ps.m_nsbm; ++j) {\n                        pmg->m_gsbmrp[i][j] = pmg->m_gsbmr[i][j];\n                    }\n                }\n            }\n            \n            // reset chemical reaction element data\n            for (int j=0; j<m_pMat->Reactions(); ++j)\n                m_pMat->GetReaction(j)->InitializeElementData(mp);\n            \n            mp.Update(timeInfo);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::InternalForces(FEGlobalVector& R)\n{\n    size_t NE = m_Elem.size();\n    \n    // get nodal DOFS\n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n#pragma omp parallel for\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForce(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEMultiphasicSolidDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n    int i, isol, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradN;\n    mat3ds s;\n    \n    const double* Gr, *Gs, *Gt, *H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    const int nreact = m_pMat->Reactions();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // next we get the determinant\n        double Jp = bpt.m_Jp;\n        double J = pt.m_J;\n        \n        // and then finally\n        double divv = ((J-Jp)/dt)/J;\n        \n        // get the stress for this integration point\n        s = pt.m_s;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        vector<vec3d> j(spt.m_j);\n        vector<int> z(nsol);\n        vector<double> kappa(spt.m_k);\n        vec3d je(0,0,0);\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            je += j[isol]*z[isol];\n        }\n        \n        // evaluate the porosity, its derivative w.r.t. J, and its gradient\n        double phiw = m_pMat->Porosity(mp);\n        vector<double> chat(nsol,0);\n        \n        // get the solvent supply\n        double phiwhat = 0;\n        if (m_pMat->GetSolventSupply()) phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += phiw*pri->m_Vbar*zhat;\n            for (isol=0; isol<nsol; ++isol)\n                chat[isol] += phiw*zhat*pri->m_v[isol];\n        }\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fu.x*detJt;\n            fe[ndpn*i+1] -= fu.y*detJt;\n            fe[ndpn*i+2] -= fu.z*detJt;\n            fe[ndpn*i+3] -= dt*(w*gradN + (phiwhat - divv)*H[i])*detJt;\n            for (isol=0; isol<nsol; ++isol)\n                fe[ndpn*i+4+isol] -= dt*(gradN*(j[isol]+je*m_pMat->m_penalty)\n                                         + H[i]*(chat[isol] - (phiw*spt.m_ca[isol] - spt.m_crp[isol]/J)/dt)\n                                         )*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    size_t NE = m_Elem.size();\n    \n    // get nodal DOFS\n    int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n#pragma omp parallel for\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = ndpn*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FEMultiphasicSolidDomain::ElementInternalForceSS(FESolidElement& el, vector<double>& fe)\n{\n    int i, isol, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradN;\n    mat3ds s;\n    \n    const double* Gr, *Gs, *Gt, *H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    const int nreact = m_pMat->Reactions();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // get the stress for this integration point\n        s = pt.m_s;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        vector<vec3d> j(spt.m_j);\n        vector<int> z(nsol);\n        vector<double> kappa(spt.m_k);\n        vec3d je(0,0,0);\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // get the charge number\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n            je += j[isol]*z[isol];\n        }\n        \n        // evaluate the porosity, its derivative w.r.t. J, and its gradient\n        double phiw = m_pMat->Porosity(mp);\n        vector<double> chat(nsol,0);\n        \n        // get the solvent supply\n        double phiwhat = 0;\n        if (m_pMat->GetSolventSupply()) phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i) {\n            FEChemicalReaction* pri = m_pMat->GetReaction(i);\n            double zhat = pri->ReactionSupply(mp);\n            phiwhat += phiw*pri->m_Vbar*zhat;\n            for (isol=0; isol<nsol; ++isol)\n                chat[isol] += phiw*zhat*pri->m_v[isol];\n        }\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[ndpn*i  ] -= fu.x*detJt;\n            fe[ndpn*i+1] -= fu.y*detJt;\n            fe[ndpn*i+2] -= fu.z*detJt;\n            fe[ndpn*i+3] -= dt*(w*gradN + H[i]*phiwhat)*detJt;\n            for (isol=0; isol<nsol; ++isol)\n                fe[ndpn*i+4+isol] -= dt*(gradN*(j[isol]+je*m_pMat->m_penalty)\n                                         + H[i]*phiw*chat[isol]\n                                         )*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n\n        // allocate stiffness matrix\n        int neln = el.Nodes();\n        int ndof = neln*ndpn;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementMultiphasicStiffness(el, ke, bsymm);\n\n\t\t// get the lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    // repeat over all solid elements\n    int NE = (int)m_Elem.size();\n    \n#pragma omp parallel for\n    for (int iel=0; iel<NE; ++iel)\n    {\n\t\tFESolidElement& el = m_Elem[iel];\n\n        // element stiffness matrix\n        FEElementMatrix ke(el);\n\n        // allocate stiffness matrix\n        int neln = el.Nodes();\n        int ndof = neln*ndpn;\n        ke.resize(ndof, ndof);\n        \n        // calculate the element stiffness matrix\n        ElementMultiphasicStiffnessSS(el, ke, bsymm);\n\n\t\t// get the lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n        // assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FEMultiphasicSolidDomain::ElementMultiphasicStiffness(FESolidElement& el, matrix& ke, bool bsymm)\n{\n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double *Gr, *Gs, *Gt, *H;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    const int nsbm   = m_pMat->SBMs();\n    const int nreact = m_pMat->Reactions();\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (int n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // calculate global gradient of shape functions\n        for (int i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        vector<double> c(spt.m_c);\n        vector<vec3d> gradc(spt.m_gradc);\n        vector<int> z(nsol);\n        \n        vector<double> kappa(spt.m_k);\n        \n        // get the charge number\n        for (int isol=0; isol<nsol; ++isol)\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n        \n        vector<double> dkdJ(spt.m_dkdJ);\n        vector< vector<double> > dkdc(spt.m_dkdc);\n        vector< vector<double> > dkdr(spt.m_dkdr);\n        vector< vector<double> > dkdJr(spt.m_dkdJr);\n        vector< vector< vector<double> > > dkdrc(spt.m_dkdrc);\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phi0 = ppt.m_phi0t;\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the osmotic coefficient\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        \n        // evaluate the permeability\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        \n        vector<mat3ds> dKdc(nsol);\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector< vector<mat3ds> > dDdc(nsol, vector<mat3ds>(nsol));\n        vector<double> D0(nsol);\n        vector< vector<double> > dD0dc(nsol, vector<double>(nsol));\n        vector<double> dodc(nsol);\n        vector<mat3ds> dTdc(nsol);\n        vector<mat3ds> ImD(nsol);\n        mat3dd I(1);\n        \n        // evaluate the solvent supply and its derivatives\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        vector<double> Phic(nsol,0);\n        vector<mat3ds> dchatde(nsol);\n        if (m_pMat->GetSolventSupply()) {\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // chemical reactions\n\t\tvector<double> reactionSupply(nreact, 0.0);\n\t\tvector<mat3ds> tangentReactionSupplyStrain(nreact);\n\t\tvector< vector<double> > tangentReactionSupplyConcentration(nreact, vector<double>(nsol));\n\t\tfor (int i = 0; i < nreact; ++i)\n\t\t{\n\t\t\tFEChemicalReaction* reacti = m_pMat->GetReaction(i);\n\n\t\t\treactionSupply[i] = reacti->ReactionSupply(mp);\n\t\t\ttangentReactionSupplyStrain[i] = reacti->Tangent_ReactionSupply_Strain(mp);\n\n\t\t\tfor (int isol = 0; isol < nsol; ++isol)\n\t\t\t{\n\t\t\t\ttangentReactionSupplyConcentration[i][isol] = reacti->Tangent_ReactionSupply_Concentration(mp, isol);\n\t\t\t}\n\n\t\t\tPhie += reacti->m_Vbar*(I*reactionSupply[i]\n\t\t\t\t+ tangentReactionSupplyStrain [i]*(J*phiw));\n\n\t\t}\n        \n        for (int isol=0; isol<nsol; ++isol) {\n        \n\t\t\tFESolute* soli = m_pMat->GetSolute(isol);\n\n\t\t\t// evaluate the permeability derivatives\n            dKdc[isol] = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp,isol);\n            \n            // evaluate the diffusivity tensor and its derivatives\n            D[isol] = soli->m_pDiff->Diffusivity(mp);\n            dDdE[isol] = soli->m_pDiff->Tangent_Diffusivity_Strain(mp);\n            \n            // evaluate the solute free diffusivity\n            D0[isol] = soli->m_pDiff->Free_Diffusivity(mp);\n            \n            // evaluate the derivative of the osmotic coefficient\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            \n            // evaluate the stress tangent with concentration\n            //\t\t\tdTdc[isol] = pm->GetSolid()->Tangent_Concentration(mp,isol);\n            dTdc[isol] = mat3ds(0,0,0,0,0,0);\n            \n            ImD[isol] = I-D[isol]/D0[isol];\n            \n            for (int jsol=0; jsol<nsol; ++jsol) {\n                dDdc[isol][jsol] = soli->m_pDiff->Tangent_Diffusivity_Concentration(mp,jsol);\n                dD0dc[isol][jsol] = soli->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,jsol);\n            }\n            \n            // evaluate the solvent supply tangent with concentration\n            if (m_pMat->GetSolventSupply()) Phic[isol] = m_pMat->GetSolventSupply()->Tangent_Supply_Concentration(mp,isol);\n            \n            // chemical reactions\n            dchatde[isol].zero();\n            for (int ireact=0; ireact<nreact; ++ireact) {\n\t\t\t\tFEChemicalReaction* reacti = m_pMat->GetReaction(ireact);\n\n                dchatde[isol] += reacti->m_v[isol]\n\t\t\t\t\t*(I*reactionSupply[ireact]\n\t\t\t\t\t+ tangentReactionSupplyStrain[ireact] *(J*phiw));\n\n                Phic[isol] += phiw* reacti->m_Vbar*tangentReactionSupplyConcentration[ireact][isol];\n            }\n        }\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double penalty = m_pMat->m_penalty;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds Ke(0,0,0,0,0,0);\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE);\n        vector<mat3ds> Gc(nsol);\n        vector<mat3ds> dKedc(nsol);\n        for (int isol=0; isol<nsol; ++isol) {\n            Ke += ImD[isol]*(kappa[isol]*c[isol]/D0[isol]);\n            G += dyad1(ImD[isol],I)*(R*T*c[isol]*J/D0[isol]/phiw*(dkdJ[isol]-kappa[isol]/phiw*dpdJ))\n            +(dyad1(I,I) - dyad2(I,I)*2 - dDdE[isol]/D0[isol])*(R*T*kappa[isol]*c[isol]/phiw/D0[isol]);\n            Gc[isol] = ImD[isol]*(kappa[isol]/D0[isol]);\n            for (int jsol=0; jsol<nsol; ++jsol) {\n                Gc[isol] += ImD[jsol]*(c[jsol]/D0[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol]))\n                -(dDdc[jsol][isol]-D[jsol]*(dD0dc[jsol][isol]/D0[jsol])*(kappa[jsol]*c[jsol]/SQR(D0[jsol])));\n            }\n            Gc[isol] *= R*T/phiw;\n        }\n        Ke = (Ki + Ke*(R*T/phiw)).inverse();\n        tens4d dKedE = (dyad1(Ke,I) - dyad4(Ke,I)*2)*2 - ddot(dyad2(Ke,Ke),G);\n        for (int isol=0; isol<nsol; ++isol)\n            dKedc[isol] = -(Ke*(-Ki*dKdc[isol]*Ki + Gc[isol])*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,qpu;\n        vector<vec3d> gc(nsol),qcu(nsol),wc(nsol),jce(nsol);\n        vector< vector<vec3d> > jc(nsol, vector<vec3d>(nsol));\n        mat3d wu, jue;\n        vector<mat3d> ju(nsol);\n        vector< vector<double> > qcc(nsol, vector<double>(nsol));\n        vector< vector<double> > dchatdc(nsol, vector<double>(nsol));\n        double sum;\n        mat3ds De;\n        for (int i=0; i<neln; ++i)\n        {\n            for (int j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradN[i]*(s*gradN[j])) + vdotTdotv(gradN[i], C, gradN[j]))*detJ;\n                ke[ndpn*i  ][ndpn*j  ] += Kuu[0][0]; ke[ndpn*i  ][ndpn*j+1] += Kuu[0][1]; ke[ndpn*i  ][ndpn*j+2] += Kuu[0][2];\n                ke[ndpn*i+1][ndpn*j  ] += Kuu[1][0]; ke[ndpn*i+1][ndpn*j+1] += Kuu[1][1]; ke[ndpn*i+1][ndpn*j+2] += Kuu[1][2];\n                ke[ndpn*i+2][ndpn*j  ] += Kuu[2][0]; ke[ndpn*i+2][ndpn*j+1] += Kuu[2][1]; ke[ndpn*i+2][ndpn*j+2] += Kuu[2][2];\n                \n                // calculate the kpu matrix\n                gp = vec3d(0,0,0);\n                for (int isol=0; isol<nsol; ++isol) gp += (D[isol]*gradc[isol])*(kappa[isol]/D0[isol]);\n                gp = gradp+gp*(R*T);\n                wu = vdotTdotv(-gp, dKedE, gradN[j]);\n                for (int isol=0; isol<nsol; ++isol) {\n                    wu += (((Ke*(D[isol]*gradc[isol])) & gradN[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradN[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradN[j]))*(-kappa[isol]*R*T/D0[isol]);\n                }\n                qpu = -gradN[j]*(1.0/dt);\n                vtmp = (wu.transpose()*gradN[i] + (qpu + Phie*gradN[j])*H[i])*(detJ*dt);\n                ke[ndpn*i+3][ndpn*j  ] += vtmp.x;\n                ke[ndpn*i+3][ndpn*j+1] += vtmp.y;\n                ke[ndpn*i+3][ndpn*j+2] += vtmp.z;\n                \n                // calculate the kup matrix\n                vtmp = -gradN[i]*H[j]*detJ;\n                ke[ndpn*i  ][ndpn*j+3] += vtmp.x;\n                ke[ndpn*i+1][ndpn*j+3] += vtmp.y;\n                ke[ndpn*i+2][ndpn*j+3] += vtmp.z;\n                \n                // calculate the kpp matrix\n                ke[ndpn*i+3][ndpn*j+3] += (H[i]*H[j]*Phip - gradN[i]*(Ke*gradN[j]))*(detJ*dt);\n                \n                // calculate kcu matrix data\n                jue.zero();\n                De.zero();\n                for (int isol=0; isol<nsol; ++isol) {\n                    gc[isol] = -gradc[isol]*phiw + w*c[isol]/D0[isol];\n                    ju[isol] = ((D[isol]*gc[isol]) & gradN[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradN[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradN[j])*(-phis)\n                       +(D[isol]*((gradN[j]*w)*2) - ((D[isol]*w) & gradN[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*wu*(kappa[isol]*c[isol]/D0[isol]);\n                    jue += ju[isol]*z[isol];\n                    De += D[isol]*(z[isol]*kappa[isol]*c[isol]/D0[isol]);\n                    qcu[isol] = qpu*(c[isol]*(kappa[isol]+J*phiw*dkdJ[isol]));\n                    \n                    // chemical reactions\n                    for (int ireact=0; ireact<nreact; ++ireact) {\n\t\t\t\t\t\tFEChemicalReaction* reacti = m_pMat->GetReaction(ireact);\n\n                        double sum1 = 0;\n                        double sum2 = 0;\n                        for (int isbm=0; isbm<nsbm; ++isbm) {\n                            sum1 += m_pMat->SBMMolarMass(isbm)*reacti->m_v[nsol+isbm]*\n                            ((J-phi0)*dkdr[isol][isbm]-kappa[isol]/m_pMat->SBMDensity(isbm));\n                            sum2 += m_pMat->SBMMolarMass(isbm)*reacti->m_v[nsol+isbm]*\n                            (dkdr[isol][isbm]+(J-phi0)*dkdJr[isol][isbm]-dkdJ[isol]/m_pMat->SBMDensity(isbm));\n                        }\n                        double zhat = reactionSupply[ireact];\n                        mat3dd zhatI(zhat);\n                        mat3ds dzde = tangentReactionSupplyStrain[ireact];\n                        qcu[isol] -= ((zhatI+dzde*(J-phi0))*gradN[j])*(sum1*c[isol])\n                        +gradN[j]*(c[isol]*(J-phi0)*sum2*zhat);\n                    }\n                }\n                \n                for (int isol=0; isol<nsol; ++isol) {\n                    \n                    // calculate the kcu matrix\n                    vtmp = ((ju[isol]+jue*penalty).transpose()*gradN[i]\n                            + (qcu[isol] + dchatde[isol]*gradN[j])*H[i])*(detJ*dt);\n                    ke[ndpn*i+4+isol][ndpn*j  ] += vtmp.x;\n                    ke[ndpn*i+4+isol][ndpn*j+1] += vtmp.y;\n                    ke[ndpn*i+4+isol][ndpn*j+2] += vtmp.z;\n                    \n                    // calculate the kcp matrix\n                    ke[ndpn*i+4+isol][ndpn*j+3] -= (gradN[i]*(\n                                                              (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                               +De*penalty)\n                                                              *(Ke*gradN[j])\n                                                              ))*(detJ*dt);\n                    \n                    // calculate the kuc matrix\n                    sum = 0;\n                    for (int jsol=0; jsol<nsol; ++jsol)\n                        sum += c[jsol]*(dodc[isol]*kappa[jsol]+osmc*dkdc[jsol][isol]);\n                    vtmp = (dTdc[isol]*gradN[i] - gradN[i]*(R*T*(osmc*kappa[isol]+sum)))*H[j]*detJ;\n                    ke[ndpn*i  ][ndpn*j+4+isol] += vtmp.x;\n                    ke[ndpn*i+1][ndpn*j+4+isol] += vtmp.y;\n                    ke[ndpn*i+2][ndpn*j+4+isol] += vtmp.z;\n                    \n                    // calculate the kpc matrix\n                    vtmp = vec3d(0,0,0);\n                    for (int jsol=0; jsol<nsol; ++jsol)\n                        vtmp += (D[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol])\n                                 +dDdc[jsol][isol]*kappa[jsol])/D0[jsol]*gradc[jsol];\n                    wc[isol] = (dKedc[isol]*gp)*(-H[j])\n                    -Ke*((D[isol]*gradN[j])*(kappa[isol]/D0[isol])+vtmp*H[j])*(R*T);\n                    ke[ndpn*i+3][ndpn*j+4+isol] += (gradN[i]*wc[isol]+H[i]*H[j]*Phic[isol])*(detJ*dt);\n                    \n                }\n                \n                // calculate data for the kcc matrix\n                jce.assign(nsol, vec3d(0,0,0));\n                for (int isol=0; isol<nsol; ++isol) {\n                    for (int jsol=0; jsol<nsol; ++jsol) {\n                        if (jsol != isol) {\n                            jc[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            \n                            qcc[isol][jsol] = -H[j]*phiw/dt*c[isol]*dkdc[isol][jsol];\n                        }\n                        else {\n                            jc[isol][jsol] = (D[isol]*(gradN[j]*(-phiw)+w*(H[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            \n                            qcc[isol][jsol] = -H[j]*phiw/dt*(c[isol]*dkdc[isol][jsol] + kappa[isol]);\n                        }\n                        jce[jsol] += jc[isol][jsol]*z[isol];\n                        \n                        // chemical reactions\n                        dchatdc[isol][jsol] = 0;\n                        for (int ireact=0; ireact<nreact; ++ireact) {\n\t\t\t\t\t\t\tFEChemicalReaction* reacti = m_pMat->GetReaction(ireact);\n\n                            dchatdc[isol][jsol] += reacti->m_v[isol]\n                            * tangentReactionSupplyConcentration[ireact][jsol];\n\n                            double sum1 = 0;\n                            double sum2 = 0;\n                            for (int isbm=0; isbm<nsbm; ++isbm) {\n                                sum1 += m_pMat->SBMMolarMass(isbm)*reacti->m_v[nsol+isbm]*\n                                ((J-phi0)*dkdr[isol][isbm]-kappa[isol]/m_pMat->SBMDensity(isbm));\n                                sum2 += m_pMat->SBMMolarMass(isbm)*reacti->m_v[nsol+isbm]*\n                                ((J-phi0)*dkdrc[isol][isbm][jsol]-dkdc[isol][jsol]/m_pMat->SBMDensity(isbm));\n                            }\n                            double zhat = reactionSupply[ireact];\n                            double dzdc = tangentReactionSupplyConcentration[ireact][jsol];\n                            if (jsol != isol) {\n                                qcc[isol][jsol] -= H[j]*phiw*c[isol]*(dzdc*sum1+zhat*sum2);\n                            }\n                            else {\n                                qcc[isol][jsol] -= H[j]*phiw*((zhat+c[isol]*dzdc)*sum1+c[isol]*zhat*sum2);\n                            }\n                        }\n                    }\n                }\n                \n                // calculate the kcc matrix\n                for (int isol=0; isol<nsol; ++isol) {\n                    for (int jsol=0; jsol<nsol; ++jsol) {\n                        ke[ndpn*i+4+isol][ndpn*j+4+jsol] += (gradN[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                             + H[i]*(qcc[isol][jsol]\n                                                                     + H[j]*phiw*dchatdc[isol][jsol]))*(detJ*dt);\n                    }\n                }\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    double tmp;\n    if (bsymm) {\n        for (int i=0; i<ndpn*neln; ++i)\n            for (int j=i+1; j<ndpn*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for steady-state response (zero solid velocity, zero time derivative of\n//! solute concentration)\n//!\nbool FEMultiphasicSolidDomain::ElementMultiphasicStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, isol, jsol, n, ireact;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double *Gr, *Gs, *Gt, *H;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradN(neln);\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    const int nsol = m_pMat->Solutes();\n    int ndpn = 4+nsol;\n    \n    const int nreact = m_pMat->Reactions();\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        vector<double> c(spt.m_c);\n        vector<vec3d> gradc(spt.m_gradc);\n        vector<int> z(nsol);\n        \n        vector<double> zz(nsol);\n        vector<double> kappa(spt.m_k);\n        \n        // get the charge number\n        for (isol=0; isol<nsol; ++isol)\n            z[isol] = m_pMat->GetSolute(isol)->ChargeNumber();\n        \n        vector<double> dkdJ(spt.m_dkdJ);\n        vector< vector<double> > dkdc(spt.m_dkdc);\n        \n        // evaluate the porosity and its derivative\n        double phiw = m_pMat->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the osmotic coefficient\n        double osmc = m_pMat->GetOsmoticCoefficient()->OsmoticCoefficient(mp);\n        \n        // evaluate the permeability\n        mat3ds K = m_pMat->GetPermeability()->Permeability(mp);\n        tens4dmm dKdE = m_pMat->GetPermeability()->Tangent_Permeability_Strain(mp);\n        \n        vector<mat3ds> dKdc(nsol);\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector< vector<mat3ds> > dDdc(nsol, vector<mat3ds>(nsol));\n        vector<double> D0(nsol);\n        vector< vector<double> > dD0dc(nsol, vector<double>(nsol));\n        vector<double> dodc(nsol);\n        vector<mat3ds> dTdc(nsol);\n        vector<mat3ds> ImD(nsol);\n        mat3dd I(1);\n        \n        // evaluate the solvent supply and its derivatives\n        double phiwhat = 0;\n        mat3ds Phie; Phie.zero();\n        double Phip = 0;\n        vector<double> Phic(nsol,0);\n        if (m_pMat->GetSolventSupply()) {\n            phiwhat = m_pMat->GetSolventSupply()->Supply(mp);\n            Phie = m_pMat->GetSolventSupply()->Tangent_Supply_Strain(mp);\n            Phip = m_pMat->GetSolventSupply()->Tangent_Supply_Pressure(mp);\n        }\n        \n        // chemical reactions\n        for (i=0; i<nreact; ++i)\n            Phie += m_pMat->GetReaction(i)->m_Vbar*(I*m_pMat->GetReaction(i)->ReactionSupply(mp)\n                                                    +m_pMat->GetReaction(i)->Tangent_ReactionSupply_Strain(mp)*(J*phiw));\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // evaluate the permeability derivatives\n            dKdc[isol] = m_pMat->GetPermeability()->Tangent_Permeability_Concentration(mp,isol);\n            \n            // evaluate the diffusivity tensor and its derivatives\n            D[isol] = m_pMat->GetSolute(isol)->m_pDiff->Diffusivity(mp);\n            dDdE[isol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Diffusivity_Strain(mp);\n            \n            // evaluate the solute free diffusivity\n            D0[isol] = m_pMat->GetSolute(isol)->m_pDiff->Free_Diffusivity(mp);\n            \n            // evaluate the derivative of the osmotic coefficient\n            dodc[isol] = m_pMat->GetOsmoticCoefficient()->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            \n            // evaluate the stress tangent with concentration\n            //\t\t\tdTdc[isol] = pm->GetSolid()->Tangent_Concentration(mp,isol);\n            dTdc[isol] = mat3ds(0,0,0,0,0,0);\n            \n            ImD[isol] = I-D[isol]/D0[isol];\n            \n            for (jsol=0; jsol<nsol; ++jsol) {\n                dDdc[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Diffusivity_Concentration(mp,jsol);\n                dD0dc[isol][jsol] = m_pMat->GetSolute(isol)->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,jsol);\n            }\n            \n            // evaluate the solvent supply tangent with concentration\n            if (m_pMat->GetSolventSupply()) Phic[isol] = m_pMat->GetSolventSupply()->Tangent_Supply_Concentration(mp,isol);\n            \n        }\n        \n        // Miscellaneous constants\n        double R = m_pMat->m_Rgas;\n        double T = m_pMat->m_Tabs;\n        double penalty = m_pMat->m_penalty;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds Ke(0,0,0,0,0,0);\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE);\n        vector<mat3ds> Gc(nsol);\n        vector<mat3ds> dKedc(nsol);\n        for (isol=0; isol<nsol; ++isol) {\n            Ke += ImD[isol]*(kappa[isol]*c[isol]/D0[isol]);\n            G += dyad1(ImD[isol],I)*(R*T*c[isol]*J/D0[isol]/phiw*(dkdJ[isol]-kappa[isol]/phiw*dpdJ))\n            +(dyad1(I,I) - dyad2(I,I)*2 - dDdE[isol]/D0[isol])*(R*T*kappa[isol]*c[isol]/phiw/D0[isol]);\n            Gc[isol] = ImD[isol]*(kappa[isol]/D0[isol]);\n            for (jsol=0; jsol<nsol; ++jsol) {\n                Gc[isol] += ImD[jsol]*(c[jsol]/D0[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol]))\n                -(dDdc[jsol][isol]-D[jsol]*(dD0dc[jsol][isol]/D0[jsol])*(kappa[jsol]*c[jsol]/SQR(D0[jsol])));\n            }\n            Gc[isol] *= R*T/phiw;\n        }\n        Ke = (Ki + Ke*(R*T/phiw)).inverse();\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        for (isol=0; isol<nsol; ++isol)\n            dKedc[isol] = -(Ke*(-Ki*dKdc[isol]*Ki + Gc[isol])*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,qpu;\n        vector<vec3d> gc(nsol),wc(nsol),jce(nsol);\n        vector< vector<vec3d> > jc(nsol, vector<vec3d>(nsol));\n        mat3d wu, jue;\n        vector<mat3d> ju(nsol);\n        vector< vector<double> > dchatdc(nsol, vector<double>(nsol));\n        double sum;\n        mat3ds De;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradN[i]*(s*gradN[j])) + vdotTdotv(gradN[i], C, gradN[j]))*detJ;\n                ke[ndpn*i  ][ndpn*j  ] += Kuu[0][0]; ke[ndpn*i  ][ndpn*j+1] += Kuu[0][1]; ke[ndpn*i  ][ndpn*j+2] += Kuu[0][2];\n                ke[ndpn*i+1][ndpn*j  ] += Kuu[1][0]; ke[ndpn*i+1][ndpn*j+1] += Kuu[1][1]; ke[ndpn*i+1][ndpn*j+2] += Kuu[1][2];\n                ke[ndpn*i+2][ndpn*j  ] += Kuu[2][0]; ke[ndpn*i+2][ndpn*j+1] += Kuu[2][1]; ke[ndpn*i+2][ndpn*j+2] += Kuu[2][2];\n                \n                // calculate the kpu matrix\n                gp = vec3d(0,0,0);\n                for (isol=0; isol<nsol; ++isol) gp += (D[isol]*gradc[isol])*(kappa[isol]/D0[isol]);\n                gp = gradp+gp*(R*T);\n                wu = vdotTdotv(-gp, dKedE, gradN[j]);\n                for (isol=0; isol<nsol; ++isol) {\n                    wu += (((Ke*(D[isol]*gradc[isol])) & gradN[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradN[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradN[j]))*(-kappa[isol]*R*T/D0[isol]);\n                }\n                qpu = Phie*gradN[j];\n                vtmp = (wu.transpose()*gradN[i] + qpu*H[i])*(detJ*dt);\n                ke[ndpn*i+3][ndpn*j  ] += vtmp.x;\n                ke[ndpn*i+3][ndpn*j+1] += vtmp.y;\n                ke[ndpn*i+3][ndpn*j+2] += vtmp.z;\n                \n                // calculate the kup matrix\n                vtmp = -gradN[i]*H[j]*detJ;\n                ke[ndpn*i  ][ndpn*j+3] += vtmp.x;\n                ke[ndpn*i+1][ndpn*j+3] += vtmp.y;\n                ke[ndpn*i+2][ndpn*j+3] += vtmp.z;\n                \n                // calculate the kpp matrix\n                ke[ndpn*i+3][ndpn*j+3] += (H[i]*H[j]*Phip - gradN[i]*(Ke*gradN[j]))*(detJ*dt);\n                \n                // calculate kcu matrix data\n                jue.zero();\n                De.zero();\n                for (isol=0; isol<nsol; ++isol) {\n                    gc[isol] = -gradc[isol]*phiw + w*c[isol]/D0[isol];\n                    ju[isol] = ((D[isol]*gc[isol]) & gradN[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradN[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradN[j])*(-phis)\n                       +(D[isol]*((gradN[j]*w)*2) - ((D[isol]*w) & gradN[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*wu*(kappa[isol]*c[isol]/D0[isol]);\n                    jue += ju[isol]*z[isol];\n                    De += D[isol]*(z[isol]*kappa[isol]*c[isol]/D0[isol]);\n                }\n                \n                for (isol=0; isol<nsol; ++isol) {\n                    \n                    // calculate the kcu matrix\n                    vtmp = ((ju[isol]+jue*penalty).transpose()*gradN[i])*(detJ*dt);\n                    ke[ndpn*i+4+isol][ndpn*j  ] += vtmp.x;\n                    ke[ndpn*i+4+isol][ndpn*j+1] += vtmp.y;\n                    ke[ndpn*i+4+isol][ndpn*j+2] += vtmp.z;\n                    \n                    // calculate the kcp matrix\n                    ke[ndpn*i+4+isol][ndpn*j+3] -= (gradN[i]*(\n                                                              (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                               +De*penalty)\n                                                              *(Ke*gradN[j])\n                                                              ))*(detJ*dt);\n                    \n                    // calculate the kuc matrix\n                    sum = 0;\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        sum += c[jsol]*(dodc[isol]*kappa[jsol]+osmc*dkdc[jsol][isol]);\n                    vtmp = (dTdc[isol]*gradN[i] - gradN[i]*(R*T*(osmc*kappa[isol]+sum)))*H[j]*detJ;\n                    ke[ndpn*i  ][ndpn*j+4+isol] += vtmp.x;\n                    ke[ndpn*i+1][ndpn*j+4+isol] += vtmp.y;\n                    ke[ndpn*i+2][ndpn*j+4+isol] += vtmp.z;\n                    \n                    // calculate the kpc matrix\n                    vtmp = vec3d(0,0,0);\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        vtmp += (D[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol])\n                                 +dDdc[jsol][isol]*kappa[jsol])/D0[jsol]*gradc[jsol];\n                    wc[isol] = (dKedc[isol]*gp)*(-H[j])\n                    -Ke*((D[isol]*gradN[j])*(kappa[isol]/D0[isol])+vtmp*H[j])*(R*T);\n                    ke[ndpn*i+3][ndpn*j+4+isol] += (gradN[i]*wc[isol]+H[i]*H[j]*Phic[isol])*(detJ*dt);\n                    \n                }\n                \n                // calculate data for the kcc matrix\n                jce.assign(nsol, vec3d(0,0,0));\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        if (jsol != isol) {\n                            jc[isol][jsol] = \n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                        }\n                        else {\n                            jc[isol][jsol] = (D[isol]*(gradN[j]*(-phiw)+w*(H[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                        }\n                        jce[jsol] += jc[isol][jsol]*z[isol];\n                        \n                        // chemical reactions\n                        dchatdc[isol][jsol] = 0;\n                        for (ireact=0; ireact<nreact; ++ireact)\n                            dchatdc[isol][jsol] += m_pMat->GetReaction(ireact)->m_v[isol]\n                            *m_pMat->GetReaction(ireact)->Tangent_ReactionSupply_Concentration(mp,jsol);\n                    }\n                }\n                \n                // calculate the kcc matrix\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        ke[ndpn*i+4+isol][ndpn*j+4+jsol] += (gradN[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                             + H[i]*H[j]*phiw*dchatdc[isol][jsol])*(detJ*dt);\n                    }\n                }\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    double tmp;\n    if (bsymm) {\n        for (i=0; i<ndpn*neln; ++i)\n            for (j=i+1; j<ndpn*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::Update(const FETimeInfo& tp)\n{\n    FEModel& fem = *GetFEModel();\n    bool berr = false;\n    int NE = (int) m_Elem.size();\n    double dt = fem.GetTime().timeIncrement;\n#pragma omp parallel for shared(NE, berr)\n    for (int i=0; i<NE; ++i)\n    {\n        try\n        {\n            UpdateElementStress(i, dt);\n        }\n        catch (NegativeJacobian e)\n        {\n#pragma omp critical\n            {\n                berr = true;\n                if (e.DoOutput()) feLogError(e.what());\n            }\n        }\n    }\n    \n    if (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolidDomain::UpdateElementStress(int iel, double dt)\n{\n    int j, k, n;\n    int nint, neln;\n    double* gw;\n    vec3d r0[FEElement::MAX_NODES];\n    vec3d rt[FEElement::MAX_NODES];\n    double pn[FEElement::MAX_NODES];\n    \n    FEMesh& mesh = *m_pMesh;\n    \n    // get the multiphasic material\n    FEMultiphasic* pmb = m_pMat;\n    const int nsol = (int)pmb->Solutes();\n    vector< vector<double> > ct(nsol, vector<double>(FEElement::MAX_NODES));\n    vector<int> sid(nsol);\n    for (j=0; j<nsol; ++j) sid[j] = pmb->GetSolute(j)->GetSoluteDOF();\n    \n    // get the solid element\n    FESolidElement& el = m_Elem[iel];\n    \n    // get the number of integration points\n    nint = el.GaussPoints();\n    \n    // get the number of nodes\n    neln = el.Nodes();\n    \n    // get the integration weights\n    gw = el.GaussWeights();\n    \n    // get the nodal data\n    for (j=0; j<neln; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j]);\n        r0[j] = node.m_r0;\n        rt[j] = node.m_rt;\n        if (el.m_bitfc.size()>0 && el.m_bitfc[j]) {\n            pn[j] = (node.m_ID[m_dofQ] != -1) ? node.get(m_dofQ) : node.get(m_dofP);\n            for (k=0; k<nsol; ++k)\n                ct[k][j] = (node.m_ID[m_dofD + sid[k]] != -1) ? node.get(m_dofD + sid[k]) : node.get(m_dofC + sid[k]);\n        }\n        else {\n            pn[j] = node.get(m_dofP);\n            for (k=0; k<nsol; ++k)\n                ct[k][j] = node.get(m_dofC + sid[k]);\n        }\n    }\n    \n    // loop over the integration points and calculate\n    // the stress at the integration point\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        \n        // material point coordinates\n        // TODO: I'm not entirly happy with this solution\n        //\t\t since the material point coordinates are used by most materials.\n        mp.m_r0 = el.Evaluate(r0, n);\n        mp.m_rt = el.Evaluate(rt, n);\n        \n        // get the deformation gradient and determinant\n        pt.m_J = defgrad(el, pt.m_F, n);\n        mat3d Fp;\n        defgradp(el, Fp, n);\n        mat3d Fi = pt.m_F.inverse();\n        pt.m_L = (pt.m_F - Fp)*Fi / dt;\n\n        // multiphasic material point data\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        \n        // update SBM referential densities\n        pmb->UpdateSolidBoundMolecules(mp);\n        \n        // evaluate referential solid volume fraction\n        ppt.m_phi0t = pmb->SolidReferentialVolumeFraction(mp);\n        if (m_breset) ppt.m_phi0 = ppt.m_phi0t;\n\n        // evaluate fluid pressure at gauss-point\n        ppt.m_p = el.Evaluate(pn, n);\n        \n        // calculate the gradient of p at gauss-point\n        ppt.m_gradp = gradient(el, pn, n);\n        \n        for (k=0; k<nsol; ++k) {\n            // evaluate effective solute concentrations at gauss-point\n            spt.m_c[k] = el.Evaluate(&ct[k][0], n);\n            // calculate the gradient of c at gauss-point\n            spt.m_gradc[k] = gradient(el, &ct[k][0], n);\n        }\n        \n        // update the fluid and solute fluxes\n        // and evaluate the actual fluid pressure and solute concentration\n        ppt.m_w = pmb->FluidFlux(mp);\n        spt.m_psi = pmb->ElectricPotential(mp);\n        for (k=0; k<nsol; ++k) {\n            spt.m_ca[k] = pmb->Concentration(mp,k);\n            spt.m_j[k] = pmb->SoluteFlux(mp,k);\n        }\n        spt.m_cF = pmb->FixedChargeDensity(mp);\n        ppt.m_pa = pmb->Pressure(mp);\n        spt.m_Ie = pmb->CurrentDensity(mp);\n        pmb->PartitionCoefficientFunctions(mp, spt.m_k, spt.m_dkdJ, spt.m_dkdc,\n                                           spt.m_dkdr, spt.m_dkdJr, spt.m_dkdrc);\n\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = pmb->GetElasticMaterial()->Stress(mp);\n        \n        // evaluate the stress\n        pt.m_s = pmb->Stress(mp);\n        \n        // evaluate the referential solid density\n        spt.m_rhor = pmb->SolidReferentialApparentDensity(mp);\n        \n        // update chemical reaction element data\n        for (int j=0; j<m_pMat->Reactions(); ++j)\n            pmb->GetReaction(j)->UpdateElementData(mp);\n        \n    }\n    if (m_breset) m_breset = false;\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicSolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FESolidDomain.h\"\n#include \"FEMultiphasic.h\"\n#include \"FEMultiphasicDomain.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! Domain class for multiphasic 3D solid elements\n//! Note that this class inherits from FEElasticSolidDomain since this domain\n//! also needs to calculate elastic stiffness contributions.\n//!\nclass FEBIOMIX_API FEMultiphasicSolidDomain : public FESolidDomain, public FEMultiphasicDomain\n{\npublic:\n    //! constructor\n    FEMultiphasicSolidDomain(FEModel* pfem);\n    \n    //! Reset data\n    void Reset() override;\n    \n    //! get the material (overridden from FEDomain)\n    FEMaterial* GetMaterial() override { return m_pMat; }\n    \n    //! set the material\n    void SetMaterial(FEMaterial* pmat) override;\n    \n    //! Unpack solid element data (overridden from FEDomain)\n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n    //! initialize elements for this domain\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! calculates the global stiffness matrix for this domain\n    void StiffnessMatrix(FELinearSystem& LS, bool bsymm) override;\n    \n    //! calculates the global stiffness matrix for this domain (steady-state case)\n    void StiffnessMatrixSS(FELinearSystem& LS, bool bsymm) override;\n    \n    //! initialize class\n\tbool Init() override;\n    \n    //! activate\n    void Activate() override;\n    \n    //! initialize material points in the domain\n    void InitMaterialPoints() override;\n    \n    // update domain data\n    void Update(const FETimeInfo& tp) override;\n    \n    // update element state data\n    void UpdateElementStress(int iel, double dt);\n\n\t// get total dof list\n\tconst FEDofList& GetDOFList() const override;\n    \npublic:\n    \n    // internal work (overridden from FEElasticDomain)\n    void InternalForces(FEGlobalVector& R) override;\n    \n    // internal work (steady-state case)\n    void InternalForcesSS(FEGlobalVector& R) override;\n    \npublic:\n    //! element internal force vector\n    void ElementInternalForce(FESolidElement& el, vector<double>& fe);\n    \n    //! element internal force vector (steady-state case)\n    void ElementInternalForceSS(FESolidElement& el, vector<double>& fe);\n    \n    //! calculates the element triphasic stiffness matrix\n    bool ElementMultiphasicStiffness(FESolidElement& el, matrix& ke, bool bsymm);\n    \n    //! calculates the element triphasic stiffness matrix\n    bool ElementMultiphasicStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm);\n    \nprotected: // overridden from FEElasticDomain, but not implemented in this domain\n    void BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n    void InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    void BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n    void MassMatrix(FELinearSystem& LS, double scale) override {}\n\nprotected:\n\tFEDofList\tm_dofU;\n\tFEDofList\tm_dofSU;\n\tFEDofList\tm_dofR;\n\tFEDofList\tm_dof;\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicSolver.h\"\n#include \"FEBioMech/FEElasticDomain.h\"\n#include \"FEBiphasicDomain.h\"\n#include \"FEBiphasicSoluteDomain.h\"\n#include \"FEMultiphasicDomain.h\"\n#include \"FETriphasicDomain.h\"\n#include \"FESlidingInterface2.h\"\n#include \"FESlidingInterface3.h\"\n#include \"FESlidingInterfaceMP.h\"\n#include \"FESlidingInterfaceBiphasic.h\"\n#include \"FESlidingInterfaceBiphasicMixed.h\"\n#include \"FEBioMech/FEPressureLoad.h\"\n#include \"FEBioMech/FEResidualVector.h\"\n#include <FEBioMech/FESolidLinearSystem.h>\n#include <FEBioMech/FERigidConnector.h>\n#include <FEBioMech/FESlidingElasticInterface.h>\n#include <FEBioMech/FESSIShellDomain.h>\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include \"FECore/sys.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FELinearConstraintManager.h>\n#include \"FEMultiphasicAnalysis.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FEMultiphasicSolver, FENewtonSolver)\n\tBEGIN_PARAM_GROUP(\"Nonlinear solver\");\t// make sure this matches FENewtonSolver. \n\t\tADD_PARAMETER(m_Dtol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"dtol\"        );\n\t\tADD_PARAMETER(m_Etol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"etol\");\n\t\tADD_PARAMETER(m_Rtol      , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rtol\");\n\t\tADD_PARAMETER(m_Ptol, \"ptol\");\n\t\tADD_PARAMETER(m_Ctol, \"ctol\");\n\t\tADD_PARAMETER(m_forcePositive, \"force_positive_concentrations\");\n\tEND_PARAM_GROUP();\n\n\t// obsolete parameters\n\tADD_PARAMETER(m_rhoi     , \"rhoi\"            )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_alpha    , \"alpha\"           )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_beta     , \"beta\"            )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_gamma    , \"gamma\"           )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_logSolve , \"logSolve\"        )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_arcLength, \"arc_length\"      )->SetFlags(FE_PARAM_OBSOLETE);\n\tADD_PARAMETER(m_al_scale , \"arc_length_scale\")->SetFlags(FE_PARAM_OBSOLETE);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMultiphasicSolver::FEMultiphasicSolver(FEModel* pfem) : FENewtonSolver(pfem),\n\tm_dofU(pfem), m_dofV(pfem), m_dofRQ(pfem),\n\tm_dofSU(pfem), m_dofSV(pfem), m_dofSA(pfem),\n\tm_rigidSolver(pfem)\n{\n\t// default values\n\tm_Rtol = 0;\t// deactivate residual convergence \n\tm_Dtol = 0.001;\n\tm_Etol = 0.01;\n\tm_Ptol = 0.01;\n\tm_Ctol = 0.01;\n\tm_Rmin = 1.0e-20;\n\tm_Rmax = 0;\t// not used if zero\n\n\tm_ndeq = 0;\n\tm_npeq = 0;\n\tm_nreq = 0;\n\tm_niter = 0;\n\n\tm_msymm = REAL_UNSYMMETRIC; // assume non-symmetric stiffness matrix by default\n\n    // Preferred strategy is Broyden's method\n    SetDefaultStrategy(QN_BROYDEN);\n    \n\tm_forcePositive = true;\t// force all concentrations to remain positive\n\n\tm_solutionNorm.push_back(ConvergenceInfo());\n\n\tif (pfem)\n\t{\n\t\tm_dofP = pfem->GetDOFIndex(\"p\");\n\t\tm_dofSP = pfem->GetDOFIndex(\"q\");\n\t\tm_dofU.AddVariable(\"displacement\");\n\t\tm_dofRQ.AddVariable(\"rigid rotation\");\n\t\tm_dofV.AddVariable(\"velocity\");\n\t\tm_dofSU.AddVariable(\"shell displacement\");\n\t\tm_dofSV.AddVariable(\"shell velocity\");\n\t\tm_dofSA.AddVariable(\"shell acceleration\");\n\t}\n\tm_dofC = m_dofSC = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Allocates and initializes the data structures.\n//\nbool FEMultiphasicSolver::Init()\n{\n\t// initialize base class\n\tif (FENewtonSolver::Init() == false) return false;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// allocate vectors\n//\tm_Fn.assign(m_neq, 0);\n\tm_Fr.assign(m_neq, 0);\n\tm_Ui.assign(m_neq, 0);\n\tm_Ut.assign(m_neq, 0);\n\tm_Uip.assign(m_neq, 0);\n\n\t// we need to fill the total displacement vector m_Ut\n\tFEMesh& mesh = fem.GetMesh();\n\tgather(m_Ut, mesh, m_dofU[0]);\n\tgather(m_Ut, mesh, m_dofU[1]);\n\tgather(m_Ut, mesh, m_dofU[2]);\n\tgather(m_Ut, mesh, m_dofSU[0]);\n\tgather(m_Ut, mesh, m_dofSU[1]);\n\tgather(m_Ut, mesh, m_dofSU[2]);\n\n\tSolverWarnings();\n\n\t// allocate poro-vectors\n//\tassert((m_ndeq > 0) || (m_npeq > 0));\n\tm_di.assign(m_ndeq, 0);\n\tm_Di.assign(m_ndeq, 0);\n\n\tif (m_npeq > 0) {\n\t\tm_pi.assign(m_npeq, 0);\n\t\tm_Pi.assign(m_npeq, 0);\n\n\t\t// we need to fill the total displacement vector m_Ut\n\t\t// (displacements are already handled in base class)\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tgather(m_Ut, mesh, m_dofP);\n\t\tgather(m_Ut, mesh, m_dofSP);\n\t}\n\n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    int MAX_DDOFS = fedofs.GetVariableSize(\"shell concentration\");\n    \n\t// allocate concentration-vectors\n\tm_ci.assign(MAX_CDOFS,vector<double>(0,0));\n\tm_Ci.assign(MAX_CDOFS,vector<double>(0,0));\n\tfor (int i=0; i<MAX_CDOFS; ++i) {\n\t\tm_ci[i].assign(m_nceq[i], 0);\n\t\tm_Ci[i].assign(m_nceq[i], 0);\n\t}\n\t\n\t// we need to fill the total displacement vector m_Ut\n\tvector<int> dofs;\n\tfor (int j=0; j<(int)m_nceq.size(); ++j) {\n\t\tif (m_nceq[j]) {\n\t\t\tdofs.push_back(m_dofC + j);\t\n\t\t}\n\t}\n    for (int j=0; j<MAX_DDOFS; ++j)\n    {\n        if (m_nceq[j])\n            dofs.push_back(m_dofSC + j);\n    }\n    \n\tgather(m_Ut, mesh, dofs);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize equations\nbool FEMultiphasicSolver::InitEquations()\n{\n\t// First call the base class.\n\t// This will initialize all equation numbers, except the rigid body equation numbers\n\tif (FENewtonSolver::InitEquations() == false) return false;\n\n\t// store the number of equations we currently have\n\tm_nreq = m_neq;\n\n\t// Next, we assign equation numbers to the rigid body degrees of freedom\n\tint neq = m_rigidSolver.InitEquations(m_neq);\n\tif (neq == -1) return false;\n\telse m_neq = neq;\n\n\t// Next, we add any Lagrange Multipliers\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* lmc = fem.NonlinearConstraint(i);\n\t\tif (lmc->IsActive())\n\t\t{\n\t\t\tm_neq += lmc->InitEquations(m_neq);\n\t\t}\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive())\n\t\t{\n\t\t\tm_neq += spc->InitEquations(m_neq);\n\t\t}\n\t}\n\t\n\t// get dofs\n\tm_dofP = fem.GetDOFIndex(\"p\");\n    m_dofSP = fem.GetDOFIndex(\"q\");\n    m_dofC = fem.GetDOFIndex(\"concentration\", 0);\n    m_dofSC = fem.GetDOFIndex(\"shell concentration\", 0);\n    \n\t// determined the nr of pressure and concentration equations\n\tFEMesh& mesh = fem.GetMesh();\n\tm_ndeq = m_npeq = 0;\n\t\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& n = mesh.Node(i);\n\t\tif (n.m_ID[m_dofU[0]] != -1) m_ndeq++;\n\t\tif (n.m_ID[m_dofU[1]] != -1) m_ndeq++;\n\t\tif (n.m_ID[m_dofU[2]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[0]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[1]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofSU[2]] != -1) m_ndeq++;\n        if (n.m_ID[m_dofP] != -1) m_npeq++;\n        if (n.m_ID[m_dofSP] != -1) m_npeq++;\n    }\n\t\n\t// determine the nr of concentration equations\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    m_nceq.assign(MAX_CDOFS, 0);\n\t\n    // get number of DOFS\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& n = mesh.Node(i);\n        for (int j=0; j<MAX_CDOFS; ++j) {\n            if (n.m_ID[m_dofC+j] != -1) m_nceq[j]++;\n            if (n.m_ID[m_dofSC+j] != -1) m_nceq[j]++;\n        }\n    }\n\t\n\treturn true;\n}\n\n//! Generate warnings if needed\nvoid FEMultiphasicSolver::SolverWarnings()\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// Generate warning if rigid connectors are used with symmetric stiffness\n\tif (m_msymm == REAL_SYMMETRIC) {\n\t\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\t\tFERigidConnector* prc = dynamic_cast<FERigidConnector*>(plc);\n\t\t\tif (prc) {\n\t\t\t\tfeLogWarning(\"Rigid connectors require non-symmetric stiffness matrix.\\nSet symmetric_stiffness flag to 0 in Control section.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Generate warning if sliding-elastic contact is used with symmetric stiffness\n\t\tif (fem.SurfacePairConstraints() > 0)\n\t\t{\n\t\t\t// loop over all contact interfaces\n\t\t\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t\t\t{\n\t\t\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\t\t\tFESlidingElasticInterface* pbw = dynamic_cast<FESlidingElasticInterface*>(pci);\n\t\t\t\tif (pbw) {\n\t\t\t\t\tfeLogWarning(\"The sliding-elastic contact algorithm runs better with a non-symmetric stiffness matrix.\\nYou may set symmetric_stiffness flag to 0 in Control section.\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//! Prepares the data for the first QN iteration. \nvoid FEMultiphasicSolver::PrepStep()\n{\n\tfor (int j=0; j<(int)m_nceq.size(); ++j) if (m_nceq[j]) zero(m_Ci[j]);\n\n\tzero(m_Pi);\n\tzero(m_Di);\n\n\t// for concentration nodal loads we need to multiply the time step size\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.ModelLoads(); ++i)\n\t{\n\t\tFENodalDOFLoad* pl = dynamic_cast<FENodalDOFLoad*>(fem.ModelLoad(i));\n\t\tif (pl && pl->IsActive())\n\t\t{\n\t\t\tbool adjust = false;\n\t\t\tint dof = pl->GetDOF();\n\t\t\tif      ((dof == m_dofP) || (dof == m_dofSP)) adjust = true;\n\t\t\telse if ((m_dofC > -1) && (dof == m_dofC)) adjust = true;\n\t\t\telse if ((m_dofSC > -1) && (dof == m_dofSC)) adjust = true;\n\n\t\t\tif (adjust)\n\t\t\t{\n\t\t\t\tpl->SetDtScale(true);\n\t\t\t}\n\t\t}\n\t}\n\n\tFETimeInfo& tp = fem.GetTime();\n\tdouble dt = tp.timeIncrement;\n\ttp.augmentation = 0;\n\n\t// zero total displacements\n\tzero(m_Ui);\n\n\t// store previous mesh state\n\t// we need them for velocity and acceleration calculations\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& ni = mesh.Node(i);\n\t\tvec3d vs = (ni.m_rt - ni.m_rp)/dt;\n\t\tvec3d vq = (ni.m_dt - ni.m_dp)/dt;\n\t\tni.m_rp = ni.m_rt;\n\t\tni.m_vp = ni.get_vec3d(m_dofV[0], m_dofV[1], m_dofV[2]);\n\t\tni.m_dp = ni.m_dt;\n\t\tni.UpdateValues();\n\n\t\tni.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vs);\n\n\t\t// solid shell\n\t\tni.set_vec3d(m_dofSV[0], m_dofSV[1], m_dofSV[2], vs - vq);\n\t}\n\n\t// apply concentrated nodal forces\n\t// since these forces do not depend on the geometry\n\t// we can do this once outside the NR loop.\n//\tvector<double> dummy(m_neq, 0.0);\n//\tzero(m_Fn);\n//\tFEResidualVector Fn(*GetFEModel(), m_Fn, dummy);\n//\tNodalLoads(Fn, tp);\n\n\t// apply boundary conditions\n\t// we save the prescribed displacements increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i = 0; i < nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& dc = *fem.BoundaryCondition(i);\n\t\tif (dc.IsActive()) dc.PrepStep(ui);\n\t}\n\n\t// do the linear constraints\n\tfem.GetLinearConstraintManager().PrepStep();\n\n\t// initialize rigid bodies\n\tm_rigidSolver.PrepStep(tp, ui);\n\n\t// intialize material point data\n\t// NOTE: do this before the stresses are updated\n\t// TODO: does it matter if the stresses are updated before\n\t//       the material point data is initialized\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tif (dom.IsActive()) dom.PreSolveUpdate(tp);\n\t}\n\n\t// update model state\n\tUpdateModel();\n\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc && plc->IsActive()) plc->PrepStep();\n\t}\n\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* psc = fem.SurfacePairConstraint(i);\n\t\tif (psc && psc->IsActive()) psc->PrepStep();\n\t}\n\n\t// see if we need to do contact augmentations\n\tm_baugment = false;\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface& ci = dynamic_cast<FEContactInterface&>(*fem.SurfacePairConstraint(i));\n\t\tif (ci.IsActive() && (ci.m_laugon == FECore::AUGLAG_METHOD)) m_baugment = true;\n\t}\n\n\t// see if we have to do nonlinear constraint augmentations\n\tif (fem.NonlinearConstraints() != 0) m_baugment = true;\n}\n\n//-----------------------------------------------------------------------------\n//! Implements the BFGS algorithm to solve the nonlinear FE equations.\n//! The details of this implementation of the BFGS method can be found in:\n//!   \"Finite Element Procedures\", K.J. Bathe, p759 and following\n//!\nbool FEMultiphasicSolver::Quasin()\n{\n\t// convergence norms\n\tdouble\tnormR1;\t\t// residual norm\n\tdouble\tnormE1;\t\t// energy norm\n\tdouble\tnormD;\t\t// displacement norm\n\tdouble\tnormd;\t\t// displacement increment norm\n\tdouble\tnormRi;\t\t// initial residual norm\n\tdouble\tnormEi;\t\t// initial energy norm\n\tdouble\tnormEm;\t\t// max energy norm\n\tdouble\tnormDi;\t\t// initial displacement norm\n\n\t// poro convergence norms data\n\tdouble\tnormPi;\t\t// initial pressure norm\n\tdouble\tnormP;\t\t// current pressure norm\n\tdouble\tnormp;\t\t// incremement pressure norm\n\n    // get number of DOFS\n\tFEModel& fem = *GetFEModel();\n\tDOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\t// solute convergence data\n\tvector<double>\tnormCi(MAX_CDOFS);\t// initial concentration norm\n\tvector<double>\tnormC(MAX_CDOFS);\t// current concentration norm\n\tvector<double>\tnormc(MAX_CDOFS);\t// incremement concentration norm\n\n\t// prepare for the first iteration\n\tconst FETimeInfo& tp = fem.GetTime();\n\tPrepStep();\n\n\t// init QN method\n\tif (QNInit() == false) return false;\n\n\t// loop until converged or when max nr of reformations reached\n\tbool bconv = false;\t\t// convergence flag\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter+1);\n\n\t\t// assume we'll converge. \n\t\tbconv = true;\n\n\t\t// solve the equations (returns line search; solution stored in m_ui)\n\t\tdouble s = QNSolve();\n\n\t\t// extract the pressure increments\n\t\tGetDisplacementData(m_di, m_ui);\n\n\t\t// set initial convergence norms\n\t\tif (m_niter == 0)\n\t\t{\n\t\t\tnormRi = fabs(m_R0*m_R0);\n\t\t\tnormEi = fabs(m_ui*m_R0);\n\t\t\tnormDi = fabs(m_di*m_di);\n\t\t\tnormEm = normEi;\n\n\t\t\tm_residuNorm.norm0 = normRi;\n\t\t\tm_energyNorm.norm0 = normEi;\n\t\t\tm_solutionNorm[0].norm0 = normDi;\n\t\t}\n\n\t\t// update all degrees of freedom\n\t\tfor (int i = 0; i<m_neq; ++i) m_Ui[i] += s*m_ui[i];\n\n\t\t// update displacements\n\t\tfor (int i = 0; i<m_ndeq; ++i) m_Di[i] += s*m_di[i];\n\n\t\t// calculate norms\n\t\tnormR1 = m_R1*m_R1;\n\t\tnormd  = (m_di*m_di)*(s*s);\n\t\tnormD  = m_Di*m_Di;\n\t\tnormE1 = s*fabs(m_ui*m_R1);\n\n\t\tm_residuNorm.norm = normR1;\n\t\tm_energyNorm.norm = normE1;\n\t\tm_solutionNorm[0].norm = normd;\n\n\t\t// check residual norm\n\t\tif ((m_Rtol > 0) && (normR1 > m_Rtol*normRi)) bconv = false;\t\n\n\t\t// check displacement norm\n\t\tif ((m_Dtol > 0) && (normd  > (m_Dtol*m_Dtol)*normD )) bconv = false;\n\n\t\t// check energy norm\n\t\tif ((m_Etol > 0) && (normE1 > m_Etol*normEi)) bconv = false;\n\n\t\t// check linestep size\n\t\tif ((m_lineSearch->m_LStol > 0) && (s < m_lineSearch->m_LSmin)) bconv = false;\n\n\t\t// check energy divergence\n\t\tif (m_bdivreform)\n\t\t{\n\t\t\tif (normE1 > normEm) bconv = false;\n\t\t}\n\n\t\t// check poroelastic convergence\n\t\t// extract the pressure increments\n\t\tGetPressureData(m_pi, m_ui);\n\n\t\t// set initial norm\n\t\tif (m_niter == 0) normPi = fabs(m_pi*m_pi);\n\n\t\t// update total pressure\n\t\tfor (int i = 0; i<m_npeq; ++i) m_Pi[i] += s*m_pi[i];\n\n\t\t// calculate norms\n\t\tnormP = m_Pi*m_Pi;\n\t\tnormp = (m_pi*m_pi)*(s*s);\n\n\t\t// check convergence\n\t\tif ((m_Ptol > 0) && (normp > (m_Ptol*m_Ptol)*normP)) bconv = false;\n\n\t\t// check solute convergence\n\t\t// extract the concentration increments\n\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j) {\n\t\t\tif (m_nceq[j]) {\n\t\t\t\tGetConcentrationData(m_ci[j], m_ui,j);\n\t\t\t\t\t\n\t\t\t\t// set initial norm\n\t\t\t\tif (m_niter == 0)\n\t\t\t\t\tnormCi[j] = fabs(m_ci[j]*m_ci[j]);\n\t\t\t\t\t\n\t\t\t\t// update total concentration\n\t\t\t\tfor (int i = 0; i<m_nceq[j]; ++i) m_Ci[j][i] += s*m_ci[j][i];\n\t\t\t\t\t\n\t\t\t\t// calculate norms\n\t\t\t\tnormC[j] = m_Ci[j]*m_Ci[j];\n\t\t\t\tnormc[j] = (m_ci[j]*m_ci[j])*(s*s);\n\t\t\t\t\t\n\t\t\t}\n\t\t}\n\t\t\t\n\t\t// check convergence\n\t\tif (m_Ctol > 0) {\n\t\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j)\n\t\t\t\tif (m_nceq[j]) bconv = bconv && (normc[j] <= (m_Ctol*m_Ctol)*normC[j]);\n\t\t}\n\n\t\t// print convergence summary\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", s);\n\t\tfeLog(\"\\tconvergence norms :        INITIAL         CURRENT         REQUIRED\\n\");\n\t\tfeLog(\"\\t residual               %15le %15le %15le\\n\", normRi, normR1, m_Rtol*normRi);\n\t\tfeLog(\"\\t energy                 %15le %15le %15le\\n\", normEi, normE1, m_Etol*normEi);\n\t\tfeLog(\"\\t displacement           %15le %15le %15le\\n\", normDi, normd ,(m_Dtol*m_Dtol)*normD );\n\t\tfeLog(\"\\t fluid pressure         %15le %15le %15le\\n\", normPi, normp ,(m_Ptol*m_Ptol)*normP );\n\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j) {\n\t\t\tif (m_nceq[j])\n\t\t\t\tfeLog(\"\\t solute %d concentration %15le %15le %15le\\n\", j+1, normCi[j], normc[j] ,(m_Ctol*m_Ctol)*normC[j] );\n\t\t}\n\n\t\tif ((bconv == false) && (normR1 < m_Rmin))\n\t\t{\n\t\t\t// check for almost zero-residual on the first iteration\n\t\t\t// this might be an indication that there is no force on the system\n\t\t\tfeLogWarning(\"No force acting on the system.\");\n\t\t\tbconv = true;\n\t\t}\n\n\t\t// check if we have converged. \n\t\t// If not, calculate the BFGS update vectors\n\t\tif (bconv == false)\n\t\t{\n\t\t\tif (s < m_lineSearch->m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\t\t\telse if ((normE1 > normEm) && m_bdivreform)\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n\t\t\t\tnormEm = normE1;\n\t\t\t\tnormEi = normE1;\n\t\t\t\tnormRi = normR1;\n\t\t\t\tnormDi = normd;\n\t\t\t\tnormPi = normp;\n\t\t\t\tfor (int j = 0; j<(int)m_nceq.size(); ++j)\n\t\t\t\t\tif (m_nceq[j]) normCi[j] = normc[j];\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// Do the QN update (This may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// something went wrong with the update, so we'll need to break\n\t\t\tif (bret == false) break;\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// Do augmentations\n\t\t\tbconv = DoAugmentations();\n\t\t}\n\t\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile (bconv == false);\n\n\t// if converged we update the total displacements\n\tif (bconv)\n\t{\n\t\tm_Ut += m_Ui;\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the residual vector\n//! Note that the concentrated nodal forces are not calculated here.\n//! This is because they do not depend on the geometry \n//! so we only calculate them once (in Quasin) and then add them here.\n\nbool FEMultiphasicSolver::Residual(vector<double>& R)\n{\n\tint i;\n\n\t// get the time information\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// initialize residual with concentrated nodal loads\n\tzero(R);\n\n\t// zero nodal reaction forces\n\tzero(m_Fr);\n\n\t// setup global RHS vector\n\tFEResidualVector RHS(fem, R, m_Fr);\n\n\t// zero rigid body reaction forces\n\tm_rigidSolver.Residual();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// internal stress work\n\tfor (i=0; i<mesh.Domains(); ++i)\n\t{\n        FEDomain& dom = mesh.Domain(i);\n        FEElasticDomain* ped = dynamic_cast<FEElasticDomain*>(&dom);\n        FEBiphasicDomain*  pbd = dynamic_cast<FEBiphasicDomain* >(&dom);\n        FEBiphasicSoluteDomain* pbs = dynamic_cast<FEBiphasicSoluteDomain*>(&dom);\n        FETriphasicDomain*      ptd = dynamic_cast<FETriphasicDomain*     >(&dom);\n        FEMultiphasicDomain*    pmd = dynamic_cast<FEMultiphasicDomain*   >(&dom);\n        if (pbd) {\n            if (fem.GetCurrentStep()->m_nanalysis == FEMultiphasicAnalysis::STEADY_STATE)\n                pbd->InternalForcesSS(RHS);\n            else\n                pbd->InternalForces(RHS);\n        }\n        else if (pbs) {\n            if (fem.GetCurrentStep()->m_nanalysis == FEMultiphasicAnalysis::STEADY_STATE)\n                pbs->InternalForcesSS(RHS);\n            else\n                pbs->InternalForces(RHS);\n        }\n        else if (ptd) {\n            if (fem.GetCurrentStep()->m_nanalysis == FEMultiphasicAnalysis::STEADY_STATE)\n                ptd->InternalForcesSS(RHS);\n            else\n                ptd->InternalForces(RHS);\n        }\n        else if (pmd) {\n            if (fem.GetCurrentStep()->m_nanalysis == FEMultiphasicAnalysis::STEADY_STATE)\n                pmd->InternalForcesSS(RHS);\n            else\n                pmd->InternalForces(RHS);\n        }\n        else if (ped)\n            ped->InternalForces(RHS);\n    }\n    \n\t// add model loads\n\tint NML = fem.ModelLoads();\n\tfor (i = 0; i < NML; ++i)\n\t{\n\t\tFEModelLoad& mli = *fem.ModelLoad(i);\n\t\tif (mli.IsActive()) mli.LoadVector(RHS);\n\t}\n\n\t// calculate contact forces\n\tContactForces(RHS);\n\n\t// calculate linear constraint forces\n\t// note that these are the linear constraints\n\t// enforced using the augmented lagrangian\n\tNonLinearConstraintForces(RHS, tp);\n\n\t// set the nodal reaction forces\n\t// TODO: Is this a good place to do this?\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.set_load(m_dofU[0], 0);\n\t\tnode.set_load(m_dofU[1], 0);\n\t\tnode.set_load(m_dofU[2], 0);\n\n\t\tint n;\n\t\tif ((n = -node.m_ID[m_dofU[0]] - 2) >= 0) node.set_load(m_dofU[0], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[1]] - 2) >= 0) node.set_load(m_dofU[1], -m_Fr[n]);\n\t\tif ((n = -node.m_ID[m_dofU[2]] - 2) >= 0) node.set_load(m_dofU[2], -m_Fr[n]);\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates global stiffness matrix.\n\nbool FEMultiphasicSolver::StiffnessMatrix()\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFESolidLinearSystem LS(&fem, &m_rigidSolver, *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC), m_alpha, m_nreq);\n\n\t// calculate the stiffness matrix for each domain\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tbool bsymm = (m_msymm == REAL_SYMMETRIC);\n\tif (pstep->m_nanalysis == FEMultiphasicAnalysis::STEADY_STATE)\n\t{\n\t\tfor (int i=0; i<mesh.Domains(); ++i) \n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tFEElasticDomain*        pde = dynamic_cast<FEElasticDomain*  >(&dom);\n\t\t\tFEBiphasicDomain*       pbd = dynamic_cast<FEBiphasicDomain* >(&dom);\n\t\t\tFEBiphasicSoluteDomain* pbs = dynamic_cast<FEBiphasicSoluteDomain*>(&dom);\n\t\t\tFETriphasicDomain*      ptd = dynamic_cast<FETriphasicDomain*     >(&dom);\n\t\t\tFEMultiphasicDomain*    pmd = dynamic_cast<FEMultiphasicDomain*   >(&dom);\n\n\t\t\tif      (pbd) pbd->StiffnessMatrixSS(LS, bsymm);\n\t\t\telse if (pbs) pbs->StiffnessMatrixSS(LS, bsymm);\n\t\t\telse if (ptd) ptd->StiffnessMatrixSS(LS, bsymm);\n\t\t\telse if (pmd) pmd->StiffnessMatrixSS(LS, bsymm);\n            else if (pde) pde->StiffnessMatrix(LS);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tFEElasticDomain*        pde = dynamic_cast<FEElasticDomain*  >(&dom);\n\t\t\tFEBiphasicDomain*       pbd = dynamic_cast<FEBiphasicDomain* >(&dom);\n\t\t\tFEBiphasicSoluteDomain* pbs = dynamic_cast<FEBiphasicSoluteDomain*>(&dom);\n\t\t\tFETriphasicDomain*      ptd = dynamic_cast<FETriphasicDomain*     >(&dom);\n\t\t\tFEMultiphasicDomain*    pmd = dynamic_cast<FEMultiphasicDomain*   >(&dom);\n\n\t\t\tif      (pbd) pbd->StiffnessMatrix(LS, bsymm);\n\t\t\telse if (pbs) pbs->StiffnessMatrix(LS, bsymm);\n\t\t\telse if (ptd) ptd->StiffnessMatrix(LS, bsymm);\n\t\t\telse if (pmd) pmd->StiffnessMatrix(LS, bsymm);\n            else if (pde) pde->StiffnessMatrix(LS);\n\t\t}\n\t}\n\n\t// calculate contact stiffness\n\tContactStiffness(LS);\n\n\t// calculate stiffness matrices for model loads\n\tint nsl = fem.ModelLoads();\n\tfor (int i = 0; i<nsl; ++i)\n\t{\n\t\tFEModelLoad* pml = fem.ModelLoad(i);\n\t\tif (pml->IsActive()) pml->StiffnessMatrix(LS);\n\t}\n\n\t// calculate nonlinear constraint stiffness\n\t// note that this is the contribution of the \n\t// constrainst enforced with augmented lagrangian\n\tNonLinearConstraintStiffness(LS, tp);\n\n\t// add contributions from rigid bodies\n\tm_rigidSolver.StiffnessMatrix(*m_pK, tp);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolver::GetDisplacementData(vector<double> &di, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.GetMesh().Nodes(), nid, m = 0;\n\tzero(di);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = fem.GetMesh().Node(i);\n\t\tnid = n.m_ID[m_dofU[0]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tdi[m++] = ui[nid];\n\t\t\tassert(m <= (int) di.size());\n\t\t}\n\t\tnid = n.m_ID[m_dofU[1]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tdi[m++] = ui[nid];\n\t\t\tassert(m <= (int) di.size());\n\t\t}\n\t\tnid = n.m_ID[m_dofU[2]];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tdi[m++] = ui[nid];\n\t\t\tassert(m <= (int) di.size());\n\t\t}\n        nid = n.m_ID[m_dofSU[0]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            di[m++] = ui[nid];\n            assert(m <= (int) di.size());\n        }\n        nid = n.m_ID[m_dofSU[1]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            di[m++] = ui[nid];\n            assert(m <= (int) di.size());\n        }\n        nid = n.m_ID[m_dofSU[2]];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            di[m++] = ui[nid];\n            assert(m <= (int) di.size());\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolver::GetPressureData(vector<double> &pi, vector<double> &ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.GetMesh().Nodes(), nid, m = 0;\n\tzero(pi);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = fem.GetMesh().Node(i);\n\t\tnid = n.m_ID[m_dofP];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tpi[m++] = ui[nid];\n\t\t\tassert(m <= (int) pi.size());\n\t\t}\n        nid = n.m_ID[m_dofSP];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            pi[m++] = ui[nid];\n            assert(m <= (int) pi.size());\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicSolver::GetConcentrationData(vector<double> &ci, vector<double> &ui, const int sol)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.GetMesh().Nodes(), nid, m = 0;\n\tzero(ci);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = fem.GetMesh().Node(i);\n\t\tnid = n.m_ID[m_dofC+sol];\n\t\tif (nid != -1)\n\t\t{\n\t\t\tnid = (nid < -1 ? -nid-2 : nid);\n\t\t\tci[m++] = ui[nid];\n\t\t\tassert(m <= (int) ci.size());\n\t\t}\n        nid = n.m_ID[m_dofSC+sol];\n        if (nid != -1)\n        {\n            nid = (nid < -1 ? -nid-2 : nid);\n            ci[m++] = ui[nid];\n            assert(m <= (int) ci.size());\n        }\n    }\n}\n\n\n//! Update EAS\nvoid FEMultiphasicSolver::UpdateEAS(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update EAS on shell domains\n\tfor (int i = 0; i < mesh.Domains(); ++i) {\n\t\tFESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n\t\tif (sdom && sdom->IsActive()) sdom->UpdateEAS(ui);\n\t}\n}\n\n//! Update EAS\nvoid FEMultiphasicSolver::UpdateIncrementsEAS(vector<double>& ui, const bool binc)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update EAS on shell domains\n\tfor (int i = 0; i < mesh.Domains(); ++i) {\n\t\tFESSIShellDomain* sdom = dynamic_cast<FESSIShellDomain*>(&mesh.Domain(i));\n\t\tif (sdom && sdom->IsActive()) sdom->UpdateIncrementsEAS(ui, binc);\n\t}\n}\n\n//! Update the model's kinematic data. This is overriden from FEBiphasicSolver so\n//! that solute data is updated\nvoid FEMultiphasicSolver::UpdateKinematics(vector<double>& ui)\n{\n\t// first update all solid-mechanics kinematics\n\tFEModel& fem = *GetFEModel();\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// update rigid bodies\n\tm_rigidSolver.UpdateRigidBodies(m_Ui, ui);\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tint U_size = (int)U.size();\n#pragma omp parallel for\n\tfor (int i = 0; i < U_size; ++i)\n\t{\n\t\tU[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\t}\n\n\t// update flexible nodes\n\t// translational dofs\n\tscatter3(U, mesh, m_dofU[0], m_dofU[1], m_dofU[2]);\n\t// shell dofs\n\tscatter3(U, mesh, m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\n\t// make sure the boundary conditions are fullfilled\n\tint nbcs = fem.BoundaryConditions();\n\tfor (int i = 0; i < nbcs; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Update();\n\t}\n\n\t// enforce the linear constraints\n\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t// already guarantee that the linear constraints are satisfied?\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints() > 0)\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// Update the spatial nodal positions\n\t// Don't update rigid nodes since they are already updated\n\tint NN = mesh.Nodes();\n#pragma omp parallel\n\t{\n#pragma omp for\n\t\tfor (int i = 0; i < NN; ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tif (node.m_rid == -1) {\n\t\t\t\tnode.m_rt = node.m_r0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2]);\n\t\t\t}\n\t\t\tnode.m_dt = node.m_d0 + node.get_vec3d(m_dofU[0], m_dofU[1], m_dofU[2])\n\t\t\t\t- node.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2]);\n\t\t}\n\t}\n\n\t// update nonlinear constraints (needed for updating Lagrange Multiplier)\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* nlc = fem.NonlinearConstraint(i);\n\t\tif (nlc->IsActive()) nlc->Update(m_Ui, ui);\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* spc = fem.SurfacePairConstraint(i);\n\t\tif (spc->IsActive()) spc->Update(m_Ui, ui);\n\t}\n\n\t// update poroelastic data\n\tUpdatePoro(ui);\n\n\t// update solute-poroelastic data\n\tUpdateSolute(ui);\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the poroelastic data\nvoid FEMultiphasicSolver::UpdatePoro(vector<double>& ui)\n{\n\tint i, n;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tdouble dt = fem.GetTime().timeIncrement;\n\n\t// update poro-elasticity data\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\n\t\t// update nodal pressures\n\t\tn = node.m_ID[m_dofP];\n\t\tif (n >= 0) node.set(m_dofP, 0 + m_Ut[n] + m_Ui[n] + ui[n]);\n        n = node.m_ID[m_dofSP];\n        if (n >= 0) node.set(m_dofSP, 0 + m_Ut[n] + m_Ui[n] + ui[n]);\n    }\n\n\t// update poro-elasticity data\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\n\t\t// update velocities\n\t\tvec3d vt = (node.m_rt - node.m_rp) / dt;\n\t\tnode.set_vec3d(m_dofV[0], m_dofV[1], m_dofV[2], vt);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Updates the solute data\nvoid FEMultiphasicSolver::UpdateSolute(vector<double>& ui)\n{\n\tint i, j, n;\n\t\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tdouble dt = fem.GetTime().timeIncrement;\n\t\n    // get number of DOFS\n    DOFS& fedofs = fem.GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    int MAX_DDOFS = fedofs.GetVariableSize(\"shell concentration\");\n    \n\t// update solute data\n\tfor (i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\t\n\t\t// update nodal concentration\n\t\tfor (j=0; j<MAX_CDOFS; ++j) {\n\t\t\tn = node.m_ID[m_dofC+j];\n\t\t\t// Force the concentrations to remain positive\n\t\t\tif (n >= 0) {\n\t\t\t\tdouble ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n\t\t\t\tif ((ct < 0.0) && m_forcePositive) ct = 0.0;\n\t\t\t\tnode.set(m_dofC + j, ct);\n\t\t\t}\n\t\t}\n        for (int j=0; j<MAX_DDOFS; ++j) {\n            int n = node.m_ID[m_dofSC+j];\n            // Force the concentrations to remain positive\n            if (n >= 0) {\n                double ct = 0 + m_Ut[n] + m_Ui[n] + ui[n];\n                if ((ct < 0) && m_forcePositive) ct = 0.0;\n                node.set(m_dofSC + j, ct);\n            }\n        }\n    }\n}\n\n//! Updates the current state of the model\nvoid FEMultiphasicSolver::Update(vector<double>& ui)\n{\n\tFEModel& fem = *GetFEModel();\n\tFETimeInfo& tp = fem.GetTime();\n\ttp.currentIteration = m_niter;\n\n\t// update EAS\n\tUpdateEAS(ui);\n\tUpdateIncrementsEAS(ui, true);\n\n\t// update kinematics\n\tUpdateKinematics(ui);\n\n\t// update domains \n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.IncrementalUpdate(ui, false);\n\t}\n\n\t// update model state\n\tUpdateModel();\n}\n\nvoid FEMultiphasicSolver::UpdateModel()\n{\n\t// mark all free-draining surfaces\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\n\t\tFESlidingInterface2* psi2 = dynamic_cast<FESlidingInterface2*>(pci);\n\t\tif (psi2) psi2->MarkFreeDraining();\n\t\tFESlidingInterface3* psi3 = dynamic_cast<FESlidingInterface3*>(pci);\n\t\tif (psi3) psi3->MarkAmbient();\n\t\tFESlidingInterfaceMP* psiMP = dynamic_cast<FESlidingInterfaceMP*>(pci);\n\t\tif (psiMP) psiMP->MarkAmbient();\n        FESlidingInterfaceBiphasic* psib = dynamic_cast<FESlidingInterfaceBiphasic*>(pci);\n        if (psib) psib->MarkFreeDraining();\n\t\tFESlidingInterfaceBiphasicMixed* psbm = dynamic_cast<FESlidingInterfaceBiphasicMixed*>(pci);\n\t\tif (psbm) psbm->MarkFreeDraining();\n\t}\n\n\t// Update all contact interfaces\n\tFENewtonSolver::UpdateModel();\n\n\t// set free-draining boundary conditions\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\n\t\tFESlidingInterface2* psi2 = dynamic_cast<FESlidingInterface2*>(pci);\n\t\tif (psi2) psi2->SetFreeDraining();\n\t\tFESlidingInterface3* psi3 = dynamic_cast<FESlidingInterface3*>(pci);\n\t\tif (psi3) psi3->SetAmbient();\n\t\tFESlidingInterfaceMP* psiMP = dynamic_cast<FESlidingInterfaceMP*>(pci);\n\t\tif (psiMP) psiMP->SetAmbient();\n        FESlidingInterfaceBiphasic* psib = dynamic_cast<FESlidingInterfaceBiphasic*>(pci);\n        if (psib) psib->SetFreeDraining();\n\t\tFESlidingInterfaceBiphasicMixed* psbm = dynamic_cast<FESlidingInterfaceBiphasicMixed*>(pci);\n\t\tif (psbm) psbm->SetFreeDraining();\n\t}\n    \n    // make sure the prescribed BCs (fluid and solutes) are fullfilled\n    int nbcs = fem.BoundaryConditions();\n    for (int i = 0; i<nbcs; ++i)\n    {\n        FEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n        if (bc.IsActive()) bc.Repair();\n    }   \n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FEMultiphasicSolver::Serialize(DumpStream& ar)\n{\n\t// Serialize parameters\n\tFENewtonSolver::Serialize(ar);\n\n\tar& m_nrhs;\n\tar& m_niter;\n\tar& m_nref& m_ntotref;\n\tar& m_naug;\n\tar& m_nreq;\n\n\tar& m_Ut& m_Ui;\n\n\tif (ar.IsLoading())\n\t{\n//\t\tm_Fn.assign(m_neq, 0);\n\t\tm_Fr.assign(m_neq, 0);\n//\t\tm_Ui.assign(m_neq, 0);\n\t}\n\n\t// serialize rigid solver\n\tm_rigidSolver.Serialize(ar);\n\t\n\tif (ar.IsShallow()) return;\n\n\tar & m_dofP & m_dofSP & m_dofC & m_dofSC;\n\tar & m_ndeq & m_npeq & m_nceq;\n\tar & m_nceq;\n\n\tar & m_di & m_Di;\n\tar & m_pi & m_Pi;\n\n\tar & m_ci & m_Ci;\n}\n\n//! Calculates the contact forces\nvoid FEMultiphasicSolver::ContactForces(FEGlobalVector& R)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->LoadVector(R, tp);\n\t}\n}\n\n//! This function calculates the contact stiffness matrix\nvoid FEMultiphasicSolver::ContactStiffness(FELinearSystem& LS)\n{\n\tFEModel& fem = *GetFEModel();\n\tconst FETimeInfo& tp = fem.GetTime();\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFEContactInterface* pci = dynamic_cast<FEContactInterface*>(fem.SurfacePairConstraint(i));\n\t\tif (pci->IsActive()) pci->StiffnessMatrix(LS, tp);\n\t}\n}\n\n//! calculate the nonlinear constraint forces \nvoid FEMultiphasicSolver::NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->LoadVector(R, tp);\n\t}\n}\n\n//! Calculate the stiffness contribution due to nonlinear constraints\nvoid FEMultiphasicSolver::NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = fem.NonlinearConstraints();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->StiffnessMatrix(LS, tp);\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FENewtonSolver.h>\n#include <FEBioMech/FERigidSolver.h>\n#include <FECore/FEDofList.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n// This class adds additional functionality to the FESolidSolver2 to solve\n// solute problems. \nclass FEBIOMIX_API FEMultiphasicSolver : public FENewtonSolver\n{\npublic:\n\t//! con/descructor\n\tFEMultiphasicSolver(FEModel* pfem);\n\tvirtual ~FEMultiphasicSolver(){}\n\n\t//! Initialize data structures\n\tbool Init() override;\n\n\t//! Initialize equations\n\tbool InitEquations() override;\n\n\t//! prepares the data for the first QN iteration\n\tvoid PrepStep() override;\n\n\t//! Performs a Newton-Raphson iteration\n\tbool Quasin() override;\n\n\t//! serialize data to/from dump file\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Generate warnings if needed\n\tvoid SolverWarnings();\n\n\t//! preferred matrix type should be unsymmetric.\n\tMatrix_Type PreferredMatrixType() const override { return REAL_UNSYMMETRIC; };\n\npublic:\n\tvoid Update(vector<double>& ui) override;\n\n\t//! update contact\n\tvoid UpdateModel() override;\n\n\t//! Update EAS\n\tvoid UpdateEAS(vector<double>& ui);\n\tvoid UpdateIncrementsEAS(vector<double>& ui, const bool binc);\n\n\t//! update kinematics\n\tvirtual void UpdateKinematics(vector<double>& ui);\n\n\t//! Update poroelastic data\n\tvoid UpdatePoro(vector<double>& ui);\n\n\t//! Update solute data\n\tvoid UpdateSolute(vector<double>& ui);\n\npublic:\n\t//! Calculates residual (overridden from FESolidSolver2)\n\tbool Residual(vector<double>& R) override;\n\n\t//! calculates the global stiffness matrix (overridden from FESolidSolver2)\n\tbool StiffnessMatrix() override;\n\n\tvoid ContactForces(FEGlobalVector& R);\n\n\tvoid ContactStiffness(FELinearSystem& LS);\n\n\tvoid NonLinearConstraintForces(FEGlobalVector& R, const FETimeInfo& tp);\n\n\tvoid NonLinearConstraintStiffness(FELinearSystem& LS, const FETimeInfo& tp);\n\nprotected:\n\tvoid GetDisplacementData(vector<double>& di, vector<double>& ui);\n\tvoid GetPressureData(vector<double>& pi, vector<double>& ui);\n\tvoid GetConcentrationData(vector<double>& ci, vector<double>& ui, const int sol);\n\npublic:\t// Parameters\n\tdouble\tm_Dtol;\t\t\t\t//!< displacement tolerance\n\tdouble\tm_Ctol;\t\t\t\t//!< concentration tolerance\n\tdouble\tm_Ptol;\t\t\t\t//!< pressure tolerance\n\tbool\tm_forcePositive;\t//!< force conentrations to remain positive\n\npublic:\n\t// equation numbers\n\tint\t\tm_ndeq;\t\t\t\t//!< number of equations related to displacement dofs\n\tint\t\tm_npeq;\t\t\t\t//!< number of equations related to pressure dofs\n\tint\t\tm_nreq;\t\t\t//!< start of rigid body equations\n\tvector<int>\t\tm_nceq;\t//!< number of equations related to concentration dofs\n\n\tvector<double> m_Fr;\t//!< nodal reaction forces\n\tvector<double> m_Uip;\t//!< previous converged displacement increment\n\n\t// poro data\n\tvector<double>\tm_di;\t//!< displacement increment vector\n\tvector<double>\tm_Di;\t//!< total displacement vector for iteration\n\tvector<double>\tm_pi;\t//!< pressure increment vector\n\tvector<double>\tm_Pi;\t//!< Total pressure vector for iteration\n\n\t// solute data\n\tvector< vector<double> >\tm_ci;\t//!< concentration increment vector\n\tvector< vector<double> >\tm_Ci;\t//!< Total concentration vector for iteration\n\nprotected:\n\tFEDofList\tm_dofU, m_dofV;\n\tFEDofList\tm_dofRQ;\n\tFEDofList\tm_dofSU, m_dofSV, m_dofSA;\n\tint\tm_dofP;\t\t//!< pressure dof index\n\tint\tm_dofSP;\t//!< shell pressure dof index\n\tint\tm_dofC;\t\t//!< concentration dof index\n\tint\tm_dofSC;\t//!< shell concentration dof\n\n\tFERigidSolverNew\tm_rigidSolver;\n\nprotected:\n\t// obsolete parameters\n\tdouble  m_rhoi = -2;\n\tdouble\tm_alpha = 1;\n\tdouble\tm_beta = 0.25;\n\tdouble\tm_gamma = 0.5;\n\tbool\tm_logSolve = false;\n\tint\t\tm_arcLength = 0;\n\tdouble\tm_al_scale = 0;\n\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicStandard.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicStandard.h\"\n\n//-----------------------------------------------------------------------------\n//! FEMultiphasicStandard constructor\nFEMultiphasicStandard::FEMultiphasicStandard(FEModel* pfem) : FEMultiphasic(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMultiphasicStandard::CreateMaterialPointData()\n{\n\treturn new FESolutesMaterialPoint(new FEBiphasicMaterialPoint(m_pSolid->CreateMaterialPointData()));\n}\n\n//-----------------------------------------------------------------------------\n// call this function from shell domains only\nvoid FEMultiphasicStandard::UpdateSolidBoundMolecules(FEMaterialPoint& mp)\n{\n    double dt = CurrentTimeIncrement();\n    \n    // check if this mixture includes chemical reactions\n    int nreact = (int)Reactions();\n    int mreact = (int)MembraneReactions();\n    if (nreact || mreact) {\n        // for chemical reactions involving solid-bound molecules,\n        // update their concentration\n\t\t// multiphasic material point data\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\tFEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\tFESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n        FEShellElement* sel = dynamic_cast<FEShellElement*>(mp.m_elem);\n        double h = (sel) ? sel->Evaluate(sel->m_ht, mp.m_index) : 0;   // shell thickness\n\n        double phi0 = ppt.m_phi0t;\n        int nsbm = SBMs();\n        int nsol = Solutes();\n        // create a temporary container for spt.m_sbmr so that this variable remains\n        // unchanged as the reaction rates get calculated for each sbm.\n        vector<double> sbmr = spt.m_sbmr;\n        for (int isbm=0; isbm<nsbm; ++isbm) {\n            spt.m_sbmrhat[isbm] = 0;\n            // combine the molar supplies from all the reactions\n            for (int k=0; k<nreact; ++k) {\n                double zetahat = GetReaction(k)->ReactionSupply(mp);\n                double v = GetReaction(k)->m_v[nsol+isbm];\n                // remember to convert from molar supply to referential mass supply\n                spt.m_sbmrhat[isbm] += (pt.m_J-phi0)*SBMMolarMass(isbm)*v*zetahat;\n            }\n            for (int k=0; k<mreact; ++k) {\n                double zetahat = GetMembraneReaction(k)->ReactionSupply(mp);\n                double v = GetMembraneReaction(k)->m_v[nsol+isbm];\n                // remember to convert from molar supply to referential mass supply\n                spt.m_sbmrhat[isbm] += pt.m_J/h*SBMMolarMass(isbm)*v*zetahat;\n            }\n            // perform the time integration (midpoint rule)\n            sbmr[isbm] = spt.m_sbmrp[isbm] + dt*(spt.m_sbmrhat[isbm]+spt.m_sbmrhatp[isbm])/2;\n            // check bounds\n            if (sbmr[isbm] < spt.m_sbmrmin[isbm])\n                sbmr[isbm] = spt.m_sbmrmin[isbm];\n            if ((spt.m_sbmrmax[isbm] > 0) && (sbmr[isbm] > spt.m_sbmrmax[isbm]))\n                sbmr[isbm] = spt.m_sbmrmax[isbm];\n        }\n        // now update spt.m_sbmr\n        spt.m_sbmr = sbmr;\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FEMultiphasicStandard.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMultiphasic.h\"\n\n//-----------------------------------------------------------------------------\n//! Standard multiphasic material.\n\nclass FEBIOMIX_API FEMultiphasicStandard : public FEMultiphasic\n{\npublic:\n\t//! constructor\n\tFEMultiphasicStandard(FEModel* pfem);\n    \n    //! returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData();\n\t\n    //! Update solid bound molecules\n    void UpdateSolidBoundMolecules(FEMaterialPoint& mp);\n};\n"
  },
  {
    "path": "FEBioMix/FENodalFluidFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FENodalFluidFlux.h\"\n#include <FECore/FENodeSet.h>\n#include <FECore/FEMaterialPoint.h>\n#include <FECore/FENode.h>\n#include \"FEBioMix.h\"\n\nBEGIN_FECORE_CLASS(FENodalFluidFlux, FENodalLoad)\n\tADD_PARAMETER(m_w, \"value\");\nEND_FECORE_CLASS();\n\nFENodalFluidFlux::FENodalFluidFlux(FEModel* fem) : FENodalLoad(fem)\n{\n\tm_w = 0.0;\n}\n\nbool FENodalFluidFlux::SetDofList(FEDofList& dofList)\n{\n\treturn dofList.AddVariable(FEBioMix::GetVariableName(FEBioMix::FLUID_PRESSURE));\n}\n\nvoid FENodalFluidFlux::GetNodalValues(int inode, std::vector<double>& val)\n{\n\tassert(val.size() == 1);\n\tconst FENodeSet& nset = *GetNodeSet();\n\tint nid = nset[inode];\n\tconst FENode& node = *nset.Node(inode);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = inode;\n\n\tconst FETimeInfo& tp = GetTimeInfo();\n\n\t// for consistency with evaluation of residual and stiffness matrix\n\tval[0] = m_w(mp)* tp.timeIncrement;\n}\n"
  },
  {
    "path": "FEBioMix/FENodalFluidFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FENodalLoad.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n// Class that implements an equivalent nodal force load.\n// This load will be applied directly to the load vector of the system\nclass FEBIOMIX_API FENodalFluidFlux : public FENodalLoad\n{\npublic:\n\tFENodalFluidFlux(FEModel* fem);\n\nprotected: // required functions of FENodalLoad\n\n\t// Set the dof list\n\tbool SetDofList(FEDofList& dofList) override;\n\n\t// get the nodal values\n\tvoid GetNodalValues(int inode, std::vector<double>& val) override;\n\nprivate:\n\tFEParamDouble\tm_w;\t\t//!< the applied flux\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEOsmCoefConst.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOsmCoefConst.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEOsmCoefConst, FEOsmoticCoefficient)\n\tADD_PARAMETER(m_osmcoef, FE_RANGE_GREATER_OR_EQUAL(0.0), \"osmcoef\")->setLongName(\"osmotic coefficient\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEOsmCoefConst::FEOsmCoefConst(FEModel* pfem) : FEOsmoticCoefficient(pfem)\n{\n\tm_osmcoef = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Osmotic coefficient\ndouble FEOsmCoefConst::OsmoticCoefficient(FEMaterialPoint& mp)\n{\n\t// --- constant osmotic coefficient ---\n\t\n\treturn m_osmcoef;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to strain\ndouble FEOsmCoefConst::Tangent_OsmoticCoefficient_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to concentration\ndouble FEOsmCoefConst::Tangent_OsmoticCoefficient_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEOsmCoefConst.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a constant osmotic coefficient\n\nclass FEBIOMIX_API FEOsmCoefConst :\tpublic FEOsmoticCoefficient\n{\npublic:\n\t//! constructor\n\tFEOsmCoefConst(FEModel* pfem);\n\t\n\t//! osmotic coefficient\n\tdouble OsmoticCoefficient(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of osmotic coefficient with respect to strain (J=detF)\n\tdouble Tangent_OsmoticCoefficient_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of osmotic coefficient with respect to concentration\n\tdouble Tangent_OsmoticCoefficient_Concentration(FEMaterialPoint& mp, const int isol) override;\n\t\npublic:\n\tdouble\tm_osmcoef;\t\t\t//!< osmotic coefficient\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEOsmCoefManning.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOsmCoefManning.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n#include <FECore/FECoreKernel.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEOsmCoefManning, FEOsmoticCoefficient)\n    ADD_PARAMETER(m_ksi , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\"  );\n    ADD_PARAMETER (m_sol , \"co_ion\")->setEnums(\"$(solutes)\");\n    ADD_PROPERTY(m_osmc, \"osmc\")->SetLongName(\"Wells osmotic coefficient\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEOsmCoefManning::FEOsmCoefManning(FEModel* pfem) : FEOsmoticCoefficient(pfem)\n{\n    m_ksi = 1;\n    m_sol = -1;\n    m_lsol = -1;\n    m_osmc = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOsmCoefManning::Init()\n{\n    // get the ancestor material which must be a multiphasic material\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\tif (psm == nullptr) {\n\t\tfeLogError(\"Ancestor material must have solutes\");\n\t\treturn false;\n\t}\n    \n    // extract the local id of the solute from the global id\n    // m_sol must be one-based\n    m_lsol = psm->FindLocalSoluteID(m_sol);\n\tif (m_lsol == -1) {\n\t\tfeLogError(\"Invalid value for sol\");\n\t\treturn false;\n\t}\n\n\tif (m_osmc == nullptr) {\n\t\tfeLogError(\"function for osmc not specified\");\n\t\treturn false;\n\t}\n    \n    return FEOsmoticCoefficient::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Osmotic coefficient\ndouble FEOsmCoefManning::OsmoticCoefficient(FEMaterialPoint& mp)\n{\n    double phiPM = OsmoticCoefficient_Manning(mp);\n    double phiMM = OsmoticCoefficient_Wells(mp);\n    \n    return phiPM + phiMM - 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to strain\ndouble FEOsmCoefManning::Tangent_OsmoticCoefficient_Strain(FEMaterialPoint &mp)\n{\n    double dphiPMdJ = Tangent_OsmoticCoefficient_Strain_Manning(mp);\n    double dphiMMdJ = Tangent_OsmoticCoefficient_Strain_Wells(mp);\n    \n    return dphiPMdJ + dphiMMdJ;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to concentration\ndouble FEOsmCoefManning::Tangent_OsmoticCoefficient_Concentration(FEMaterialPoint &mp, const int isol)\n{\n    double dphiPMdc = Tangent_OsmoticCoefficient_Concentration_Manning(mp,isol);\n    double dphiMMdc = Tangent_OsmoticCoefficient_Concentration_Wells(mp,isol);\n    \n    return dphiPMdc + dphiMMdc;\n}\n\n//-----------------------------------------------------------------------------\n//! Osmotic coefficient\ndouble FEOsmCoefManning::OsmoticCoefficient_Manning(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\n    // evaluate X = FCD/co-ion actual concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double cF = fabs(psm->GetFixedChargeDensity(mp));\n    double X = 0;\n    if (ca > 0) X = cF/ca;\n    \n    // --- Manning osmotic coefficient ---\n    double osmcoef;\n    if (m_ksi <= 1)\n        osmcoef = 1 - 0.5*m_ksi*X/(X+2);\n    else\n        osmcoef = (0.5*X/m_ksi+2)/(X+2);\n\n    assert(osmcoef>0);\n    \n    return osmcoef;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to strain\ndouble FEOsmCoefManning::Tangent_OsmoticCoefficient_Strain_Manning(FEMaterialPoint &mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    \n    // evaluate X = FCD/co-ion actual concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double cF = fabs(psm->GetFixedChargeDensity(mp));\n    double kt = psm->GetPartitionCoefficient(mp, m_lsol);\n    double dktdJ = psm->dkdJ(mp, m_lsol);\n\n    double X = 0;\n    if (ca > 0) X = cF/ca;\n    \n    // evaluate dX/dJ\n    double J = pt.m_J;\n    \n    double phisr = pbm->GetReferentialSolidVolumeFraction(mp);\n    \n    double dXdJ = -(1./(J-phisr)+dktdJ/kt)*X;\n    \n    double dosmdX;\n    if (m_ksi <= 1)\n        dosmdX = -m_ksi/pow(X+2, 2);\n    else\n        dosmdX = (1-2*m_ksi)/m_ksi/pow(X+2, 2);\n    \n    return dosmdX*dXdJ;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to concentration\ndouble FEOsmCoefManning::Tangent_OsmoticCoefficient_Concentration_Manning(FEMaterialPoint &mp, const int isol)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\n    // evaluate X = FCD/co-ion actual concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double cF = fabs(psm->GetFixedChargeDensity(mp));\n    double kta = psm->GetPartitionCoefficient(mp, m_lsol);\n    double kt = psm->GetPartitionCoefficient(mp, isol);\n    int zt = psm->GetSolute(isol)->ChargeNumber();\n    \n    double X = 0;\n    if (ca > 0) X = cF/ca;\n    \n    // evaluate dX/dc\n    double dXdc = -zt*kt/ca;\n    if (isol == m_lsol) dXdc -= kta*X/ca;\n    \n    double dosmdX;\n    if (m_ksi <= 1)\n        dosmdX = -m_ksi/pow(X+2, 2);\n    else\n        dosmdX = (1./m_ksi-2)/pow(X+2, 2);\n    \n    return dosmdX*dXdc;\n}\n\n//-----------------------------------------------------------------------------\n//! Osmotic coefficient\ndouble FEOsmCoefManning::OsmoticCoefficient_Wells(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double osmc = m_osmc->value(ca);\n    \n    assert(osmc>0);\n    \n    return osmc;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to strain\ndouble FEOsmCoefManning::Tangent_OsmoticCoefficient_Strain_Wells(FEMaterialPoint &mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double dkdJ = psm->dkdJ(mp, m_lsol);\n    double f = dkdJ*c;\n    \n    double dosmc = m_osmc->derive(ca);\n    \n    return dosmc*f;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of osmotic coefficient with respect to concentration\ndouble FEOsmCoefManning::Tangent_OsmoticCoefficient_Concentration_Wells(FEMaterialPoint &mp, const int isol)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double k = psm->GetPartitionCoefficient(mp, m_lsol);\n    double dkdc = psm->dkdc(mp, m_lsol, isol);\n    double f = dkdc*c;\n    if (isol == m_lsol) f += k;\n    \n    double dosmc = m_osmc->derive(ca);\n    \n    return dosmc*f;\n}\n"
  },
  {
    "path": "FEBioMix/FEOsmCoefManning.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEOsmoticCoefficient.h\"\n#include <FECore/FEFunction1D.h>\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has an osmotic coefficient behaving\n// according to the Wells-Manning theory.  The Wells correction is provided\n// by a loadcurve.\n\nclass FEBIOMIX_API FEOsmCoefManning : public FEOsmoticCoefficient\n{\npublic:\n    //! constructor\n    FEOsmCoefManning(FEModel* pfem);\n    \n    //! destructor\n    ~FEOsmCoefManning() {}\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! osmotic coefficient\n    double OsmoticCoefficient(FEMaterialPoint& pt) override;\n    \n    //! Tangent of osmotic coefficient with respect to strain (J=detF)\n    double Tangent_OsmoticCoefficient_Strain(FEMaterialPoint& mp) override;\n    \n    //! Tangent of osmotic coefficient with respect to concentration\n    double Tangent_OsmoticCoefficient_Concentration(FEMaterialPoint& mp, const int isol) override;\n\n    //! Manning response\n    double OsmoticCoefficient_Manning(FEMaterialPoint& pt);\n    double Tangent_OsmoticCoefficient_Strain_Manning(FEMaterialPoint& mp);\n    double Tangent_OsmoticCoefficient_Concentration_Manning(FEMaterialPoint& mp, const int isol);\n\n    //! Wells response\n    double OsmoticCoefficient_Wells(FEMaterialPoint& pt);\n    double Tangent_OsmoticCoefficient_Strain_Wells(FEMaterialPoint& mp);\n    double Tangent_OsmoticCoefficient_Concentration_Wells(FEMaterialPoint& mp, const int isol);\n    \npublic:\n    double\t\t\tm_ksi;\t//!< Manning parameter\n    int\t\t\t\tm_sol;\t//!< global id of co-ion\n    int\t\t\t\tm_lsol;\t//!< local id of co-ion\n    FEFunction1D*\tm_osmc;\t//!< osmotic coefficient for Wells correction (mobile ion - mobile interaction)\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEOsmoticCoefficient.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOsmoticCoefficient.h\"\n"
  },
  {
    "path": "FEBioMix/FEOsmoticCoefficient.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for osmotic coefficient.\n//! These materials need to define the osmotic coefficient and tangent functions.\n//!\nclass FEBIOMIX_API FEOsmoticCoefficient : public FEMaterialProperty\n{\npublic:\n\t//! constructor\n\tFEOsmoticCoefficient(FEModel* pfem) : FEMaterialProperty(pfem) {}\n    \n\t//! osmotic coefficient\n\tvirtual double OsmoticCoefficient(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of osmotic coefficient with respect to strain\n\tvirtual double Tangent_OsmoticCoefficient_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of osmotic coefficient with respect to concentration\n\tvirtual double Tangent_OsmoticCoefficient_Concentration(FEMaterialPoint& mp, const int isol) = 0;\n\n\tFECORE_BASE_CLASS(FEOsmoticCoefficient)\n};\n\n"
  },
  {
    "path": "FEBioMix/FEPermConstIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPermConstIso.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPermConstIso, FEHydraulicPermeability)\n\tADD_PARAMETER(m_perm, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm\")->setUnits(UNIT_PERMEABILITY);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEPermConstIso::FEPermConstIso(FEModel* pfem) : FEHydraulicPermeability(pfem)\n{\n\tm_perm = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Permeability tensor.\nmat3ds FEPermConstIso::Permeability(FEMaterialPoint& mp)\n{\n\t// --- constant isotropic permeability ---\n\t\n\treturn mat3dd(m_perm(mp));\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\ntens4dmm FEPermConstIso::Tangent_Permeability_Strain(FEMaterialPoint &mp)\n{\n\ttens4dmm K;\n\tK.zero();\n\treturn K;\n}\n"
  },
  {
    "path": "FEBioMix/FEPermConstIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a poroelastic material that has a constant permeability\n\nclass FEBIOMIX_API FEPermConstIso :\tpublic FEHydraulicPermeability\n{\npublic:\n\t//! constructor\n\tFEPermConstIso(FEModel* pfem);\n\t\t\n\t//! permeability\n\tmat3ds Permeability(FEMaterialPoint& pt) override;\n\t\t\n\t//! Tangent of permeability\n\ttens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) override;\n\t\t\npublic:\n\tFEParamDouble\tm_perm;\t\t\t//!< permeability\n\t\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPermExpIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPermExpIso.h\"\n#include \"FEBiphasic.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPermExpIso, FEHydraulicPermeability)\n    ADD_PARAMETER(m_perm , FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm\" )->setUnits(UNIT_PERMEABILITY);\n    ADD_PARAMETER(m_M    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"M\"    );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFEPermExpIso::FEPermExpIso(FEModel* pfem) : FEHydraulicPermeability(pfem)\n{\n    m_perm = 1;\n    m_M = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Permeability tensor.\nmat3ds FEPermExpIso::Permeability(FEMaterialPoint& mp)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\n    // relative volume\n    double J = et.m_J;\n\n    // referential solid volume fraction\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    // check for potential error\n    if (J <= phi0) feLogError(\"The perm-exp-iso permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\",J,phi0);\n    \n    // --- strain-dependent isotropic permeability ---\n    double k0 = m_perm*exp(m_M*(J-1)/(J-phi0));\n    \n    return mat3dd(k0);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\ntens4dmm FEPermExpIso::Tangent_Permeability_Strain(FEMaterialPoint &mp)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // relative volume\n    double J = et.m_J;\n    \n    // referential solid volume fraction\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    // check for potential error\n    if (J <= phi0) feLogError(\"The perm-exp-iso permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\",J,phi0);\n    \n    mat3dd I(1);    // Identity\n    \n    double k0 = m_perm*exp(m_M*(J-1)/(J-phi0));\n    double k0prime = m_M*(1-phi0)/pow(J-phi0,2)*k0;\n\n    mat3ds k0hat = I*(k0 + J*k0prime);\n    \n    return dyad1mm(I,k0hat)-dyad4s(I)*(2*k0);\n}\n"
  },
  {
    "path": "FEBioMix/FEPermExpIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEHydraulicPermeability.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a poroelastic material that has a strain-dependent\n// permeability which varies exponentially as a function of the\n// volume ratio J, producing zero permeability in the limit as J->phi0.\n\nclass FEBIOMIX_API FEPermExpIso : public FEHydraulicPermeability\n{\npublic:\n    //! constructor\n    FEPermExpIso(FEModel* pfem);\n        \n    //! permeability\n    mat3ds Permeability(FEMaterialPoint& pt) override;\n        \n    //! Tangent of permeability\n    tens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) override;\n        \npublic:\n    double    m_perm;       //!< permeability\n    double    m_M;          //!< exponential coefficient\n        \n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPermHolmesMow.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPermHolmesMow.h\"\n#include <FECore/log.h>\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPermHolmesMow, FEHydraulicPermeability)\n\tADD_PARAMETER(m_perm , FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm\" )->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_M    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"M\"    );\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEPermHolmesMow::FEPermHolmesMow(FEModel* pfem) : FEHydraulicPermeability(pfem)\n{\n\tm_perm = 1;\n\tm_M = m_alpha = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPermHolmesMow::Init()\n{\n\t// make sure the ancestor implements the biphasic interface\n\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\tif (pbm == nullptr)\n\t{\n\t\tfeLogError(\"Parent material needs to be biphasic or multiphasic.\");\n\t\treturn false;\n\t}\n\n\treturn FEHydraulicPermeability::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Permeability tensor.\nmat3ds FEPermHolmesMow::Permeability(FEMaterialPoint& mp)\n{\n\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n\t// relative volume\n\tdouble J = et.m_J;\n\n\t// referential solid volume fraction also check if bfsi\n    double phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\t\n    // check for potential error\n    if (J <= phi0) \n\t{\n\t\tFEElement* pe = mp.m_elem;\n\t\tint id = (pe ? pe->GetID() : -1);\n\t\t// NOTE: This function can be called from a parallel (omp) section\n\t\t//       however, logging should be done in serial. \n#pragma omp critical\n\t\tfeLogError(\"The Holmes-Mow permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g. (element %d)\", J, phi0, id);\n\t}\n\n    // --- strain-dependent isotropic permeability ---\n\tdouble perm = m_perm(mp);\n\treturn mat3dd(perm*pow((J-phi0)/(1.0-phi0),m_alpha)*exp(m_M*(J*J-1.0)/2.0));\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\ntens4dmm FEPermHolmesMow::Tangent_Permeability_Strain(FEMaterialPoint &mp)\n{\n\tFEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\n\t// referential solid volume fraction\n\tdouble phi0 = pbm->GetReferentialSolidVolumeFraction(mp);\n\n    // check for potential error\n\tif (J <= phi0)\n\t{\n\t\tFEElement* pe = mp.m_elem;\n\t\tint id = (pe ? pe->GetID() : -1);\n\t\t// NOTE: This function can be called from a parallel (omp) section\n\t\t//       however, logging should be done in serial. \n#pragma omp critical\n\t\tfeLogError(\"The Holmes-Mow permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g. (element %d)\", J, phi0, id);\n\t}\n\n\tmat3dd I(1);\t// Identity\n\tdouble perm = m_perm(mp);\n\n\tdouble k0 = perm*pow((J-phi0)/(1.0-phi0),m_alpha)*exp(m_M*(J*J-1.0)/2.0);\n\tdouble K0prime = (J*J*m_M+(J*(m_alpha+1)-phi0)/(J-phi0))*k0;\n\tmat3ds k0hat = I*K0prime;\n\t\n\treturn dyad1mm(I,k0hat)-dyad4s(I)*(2*k0);\n}\n"
  },
  {
    "path": "FEBioMix/FEPermHolmesMow.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a poroelastic material that has a strain-dependent\n// permeability according to the constitutive relation of Holmes & Mow (JB 1990)\n\nclass FEBIOMIX_API FEPermHolmesMow :\tpublic FEHydraulicPermeability\n{\npublic:\n\t//! constructor\n\tFEPermHolmesMow(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\t\t\n\t//! permeability\n\tmat3ds Permeability(FEMaterialPoint& pt) override;\n\t\t\n\t//! Tangent of permeability\n\ttens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) override;\n\t\t\npublic:\n\tFEParamDouble\tm_perm;\t\t\t//!< permeability\n\tdouble\tm_M;\t\t\t//!< nonlinear exponential coefficient\n\tdouble\tm_alpha;\t\t//!< nonlinear power exponent\n\t\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPermRefIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <FECore/log.h>\n#include \"FEPermRefIso.h\"\n\n#define INRANGE(x, a, b) ((x)>=(a) && (x)<=(b))\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPermRefIso, FEHydraulicPermeability)\n\tADD_PARAMETER(m_perm0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm0\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm1, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm1\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm2, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm2\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_M    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"M\"    );\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEPermRefIso::FEPermRefIso(FEModel* pfem) : FEHydraulicPermeability(pfem)\n{\n\tm_perm0 = 1;\n\tm_perm1 = 0;\n\tm_perm2 = 0;\n\tm_M = m_alpha = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization. \nbool FEPermRefIso::Validate()\n{\n\tif (FEHydraulicPermeability::Validate() == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Permeability tensor.\nmat3ds FEPermRefIso::Permeability(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// referential solid volume fraction\n\tdouble phisr = pt.m_phi0t;\n\t\n    // check for potential error\n    if (J <= phisr) feLogError(\"The perm-ref-iso permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\",J,phisr);\n    \n\t// --- strain-dependent permeability ---\n\t\n\tdouble f = pow((J-phisr)/(1-pt.m_phi0),m_alpha)*exp(m_M*(J*J-1.0)/2.0);\n\tdouble k0 = m_perm0(mp)*f;\n\tdouble k1 = m_perm1(mp) /(J*J)*f;\n\tdouble k2 = 0.5*m_perm2(mp)/pow(J,4)*f;\n\tmat3ds kt = k0*I+k1*b+2*k2*b.sqr();\n\t\n\treturn kt;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\ntens4dmm FEPermRefIso::Tangent_Permeability_Strain(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// referential solid volume fraction\n\tdouble phisr = pt.m_phi0t;\n    double phi0 = pt.m_phi0;\n\t\n    // check for potential error\n    if (J <= phisr) feLogError(\"The perm-ref-iso permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\",J,phisr);\n    \n\tdouble f = pow((J-phisr)/(1-phi0),m_alpha)*exp(m_M*(J*J-1.0)/2.0);\n\tdouble k0 = m_perm0(mp) *f;\n\tdouble k1 = m_perm1(mp) /(J*J)*f;\n\tdouble k2 = 0.5*m_perm2(mp) /pow(J,4)*f;\n    double K0prime = (1+J*(m_alpha/(J-phi0)+m_M*J))*k0;\n\tdouble K1prime = (J*J*m_M+(J*(m_alpha-1)+phi0)/(J-phisr))*k1;\n\tdouble K2prime = (J*J*m_M+(J*(m_alpha-3)+3*phi0)/(J-phisr))*k2;\n\tmat3ds k0hat = I*K0prime;\n\tmat3ds k1hat = I*K1prime;\n\tmat3ds k2hat = I*K2prime;\n\t\n\ttens4dmm K4 = dyad1mm(I,k0hat)-dyad4s(I)*(2*k0)\n\t+ dyad1mm(b,k1hat)\n\t+ dyad1mm(b.sqr(),k2hat)*2+dyad4s(b)*(4*k2);\n\t\n\treturn K4;\n}\n"
  },
  {
    "path": "FEBioMix/FEPermRefIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a poroelastic material that has a strain-dependent\n// permeability which is isotropic in the reference state, but exhibits\n// strain-induced anisotropy, according to the constitutive relation\n// of Ateshian and Weiss (JBME 2010)\n\nclass FEBIOMIX_API FEPermRefIso :\tpublic FEHydraulicPermeability\n{\npublic:\n\t//! constructor\n\tFEPermRefIso(FEModel* pfem);\n\t\t\n\t//! permeability\n\tmat3ds Permeability(FEMaterialPoint& pt) override;\n\t\t\n\t//! Tangent of permeability\n\ttens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) override;\n\t\t\n\t//! data initialization and checking\n\tbool Validate() override;\n\t\t\npublic:\n\tFEParamDouble m_perm0;\t\t//!< permeability for I term\n\tFEParamDouble m_perm1;\t\t//!< permeability for b term\n\tFEParamDouble m_perm2;\t\t//!< permeability for b^2 term\n\tdouble\tm_M;\t\t\t//!< nonlinear exponential coefficient\n\tdouble\tm_alpha;\t\t//!< nonlinear power exponent\n\t\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPermRefOrtho.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPermRefOrtho.h\"\n#include <FECore/log.h>\n\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPermRefOrtho, FEHydraulicPermeability)\n\tADD_PARAMETER(m_perm0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm0\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_M0    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"M0\");\n\tADD_PARAMETER(m_alpha0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha0\");\n\tADD_PARAMETER(m_perm1, 3, \"perm1\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm2, 3, \"perm2\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_M    , 3, \"M\");\n\tADD_PARAMETER(m_alpha, 3, \"alpha\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEPermRefOrtho::FEPermRefOrtho(FEModel* pfem) : FEHydraulicPermeability(pfem)\n{\n\tm_perm0 = 1;\n\tm_perm1[0] = 0.0; m_perm1[1] = 0.0; m_perm1[2] = 0.0;\n\tm_perm2[0] = 0.0; m_perm2[1] = 0.0; m_perm2[2] = 0.0;\n\n\tm_M0 = 0;\n    m_alpha0 = 0;\n\tm_M[0] = 0.0; m_M[1] = 0.0; m_M[2] = 0.0;\n\tm_alpha[0] = 0.0; m_alpha[1] = 0.0; m_alpha[2] = 0.0;\n}\n\nvoid FEPermRefOrtho::reportError(double J, double phisr)\n{\n\tconst int MAX_ERRORS = 1;\n\tstatic int n = 0;\n\tdouble t = CurrentTime();\n\tstatic double t_last = 0;\n\tif (t != t_last)\n\t{\n\t\tt = t_last;\n\t\tn = 0;\n\t}\n\tif (n < MAX_ERRORS)\n\t{\n\t\tfeLogError(\"The perm-ref-ortho permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\", J, phisr);\n\t\tn++;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Permeability tensor.\nmat3ds FEPermRefOrtho::Permeability(FEMaterialPoint& mp)\n{\n\tint a;\n\tvec3d V;\t\t\t// orthonormal material directions in reference configuration\n\tmat3ds m[3];\t\t// texture tensor in current configuration\n\t\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// deformation gradient\n\tmat3d &F = et.m_F;\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// referential solid volume fraction\n    double phi0 = pt.m_phi0;\n\tdouble phisr = pt.m_phi0t;\n\t\n    // check for potential error\n\tif (J <= phisr) reportError(J, phisr);\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (a=0; a<3; a++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to V\n\t\tV.x = Q[0][a]; V.y = Q[1][a]; V.z = Q[2][a];\n\t\tm[a] = dyad(F*V);\t// Evaluate texture tensor in the current configuration\n\t}\n\t\n\t// --- strain-dependent permeability ---\n\t\n\tdouble f;\n    double M0 = m_M0(mp);\n    double alpha0 = m_alpha0(mp);\n\tdouble k0 = m_perm0(mp)*pow((J-phisr)/(1-phi0),alpha0)*exp(M0*(J*J-1.0)/2.0);\n\tdouble k1[3] = { m_perm1[0](mp), m_perm1[1](mp), m_perm1[2](mp) };\n\tdouble k2[3] = { m_perm2[0](mp), m_perm2[1](mp), m_perm2[2](mp) };\n\tdouble alpha[3] = { m_alpha[0](mp), m_alpha[1](mp), m_alpha[2](mp) };\n\tdouble M[3] = { m_M[0](mp), m_M[1](mp), m_M[2](mp) };\n\tfor (a=0; a<3; a++) {\n\t\tf = pow((J-phisr)/(1-phi0),alpha[a])*exp(M[a]*(J*J-1.0)/2.0);\n\t\tk1[a] *= f/(J*J);\n\t\tk2[a] *= 0.5*f/pow(J,4);\n\t}\n\tmat3ds kt = k0*I\n\t+k1[0]*m[0]+k1[1]*m[1]+k1[2]*m[2]\n\t+(2.0*k2[0])*(m[0]*b).sym()+(2.0*k2[1])*(m[1]*b).sym()+(2.0*k2[2])*(m[2]*b).sym();\n\t\n\treturn kt;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\ntens4dmm FEPermRefOrtho::Tangent_Permeability_Strain(FEMaterialPoint &mp)\n{\n\tint a;\n\tvec3d V;\t\t\t// orthonormal material directions in reference configuration\n\tmat3ds m[3];\t\t// texture tensor in current configuration\n\t\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// deformation gradient\n\tmat3d &F = et.m_F;\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// referential solid volume fraction\n\tdouble phi0 = pt.m_phi0;\n    double phisr = pt.m_phi0t;\n\n    // check for potential error\n    if (J <= phisr) reportError(J, phisr);\n    \n\t// get local coordinates\n\tmat3d Q = GetLocalCS(mp);\n\n\tfor (a=0; a<3; a++) {\t// Perform sum over all three texture directions\n\t\t// Copy the texture direction in the reference configuration to V\n\t\tV.x = Q[0][a]; V.y = Q[1][a]; V.z = Q[2][a];\n\t\tm[a] = dyad(F*V);\t// Evaluate texture tensor in the current configuration\n\t}\n\t\n\tdouble f, k0, K0prime, K1prime, K2prime;\n\tmat3ds k0hat, k1hat, k2hat;\n    double M0 = m_M0(mp);\n    double alpha0 = m_alpha0(mp);\n\tk0 = m_perm0(mp)*pow((J-phisr)/(1-phi0),alpha0)*exp(M0*(J*J-1.0)/2.0);\n\tK0prime = (1+J*(alpha0/(J-phisr)+M0*J))*k0;\n\tk0hat = mat3dd(K0prime);\n\ttens4dmm K4 = dyad1mm(I,k0hat)-dyad4s(I)*(2*k0);\n\tdouble k1[3] = { m_perm1[0](mp), m_perm1[1](mp), m_perm1[2](mp) };\n\tdouble k2[3] = { m_perm2[0](mp), m_perm2[1](mp), m_perm2[2](mp) };\n\tdouble alpha[3] = { m_alpha[0](mp), m_alpha[1](mp), m_alpha[2](mp) };\n\tdouble M[3] = { m_M[0](mp), m_M[1](mp), m_M[2](mp) };\n\tfor (a=0; a<3; a++) {\n\t\tf = pow((J-phisr)/(1-phi0),alpha[a])*exp(M[a]*(J*J-1.0)/2.0);\n\t\tk1[a] *= f/(J*J);\n\t\tk2[a] *= 0.5*f/pow(J,4);\n\t\tK1prime = (J*J*M[a]+(J*(alpha[a]-1)+phi0)/(J-phisr))*k1[a];\n\t\tK2prime = (J*J*M[a]+(J*(alpha[a]-3)+3*phi0)/(J-phisr))*k2[a];\n\t\tk1hat = mat3dd(K1prime);\n\t\tk2hat = mat3dd(K2prime);\n\t\tK4 += dyad1mm(m[a],k1hat) + dyad1mm((m[a]*b).sym()*2.0,k2hat)\n\t\t+dyad4s(m[a],b)*(2.0*k2[a]);\n\t}\n\t\n\treturn K4;\n}\n"
  },
  {
    "path": "FEBioMix/FEPermRefOrtho.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a poroelastic material that has a strain-dependent\n// permeability which is orthotropic in the reference state, but exhibits\n// further strain-induced anisotropy, according to the constitutive relation\n// of Ateshian and Weiss (JBME 2010)\n\nclass FEBIOMIX_API FEPermRefOrtho :\tpublic FEHydraulicPermeability\n{\npublic:\n\t//! constructor\n\tFEPermRefOrtho(FEModel* pfem);\n\t\t\n\t//! permeability\n\tmat3ds Permeability(FEMaterialPoint& pt) override;\n\t\t\n\t//! Tangent of permeability\n\ttens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) override;\n\nprivate:\n\tvoid reportError(double J, double phisr);\n\t\t\npublic:\n\tFEParamDouble\tm_perm0;\t\t//!< permeability for I term\n\tFEParamDouble \tm_perm1[3];\t\t//!< permeability for b term\n\tFEParamDouble\tm_perm2[3];\t\t//!< permeability for b^2 term\n\tFEParamDouble\tm_M0;\t\t\t//!< nonlinear exponential coefficient\n\tFEParamDouble\tm_alpha0;\t\t//!< nonlinear power exponent\n\tFEParamDouble\tm_M[3];\t\t\t//!< nonlinear exponential coefficient\n\tFEParamDouble\tm_alpha[3];\t\t//!< nonlinear power exponent\n\t\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPermRefTransIso.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPermRefTransIso.h\"\n#include <FECore/log.h>\n\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPermRefTransIso, FEHydraulicPermeability)\n\tADD_PARAMETER(m_perm0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm0\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm1T, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm1T\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm1A, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm1A\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm2T, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm2T\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_perm2A, FE_RANGE_GREATER_OR_EQUAL(0.0), \"perm2A\")->setUnits(UNIT_PERMEABILITY);\n\tADD_PARAMETER(m_M0    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"M0\");\n\tADD_PARAMETER(m_MT    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"MT\");\n\tADD_PARAMETER(m_MA    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"MA\");\n\tADD_PARAMETER(m_alpha0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha0\");\n\tADD_PARAMETER(m_alphaT, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alphaT\");\n\tADD_PARAMETER(m_alphaA, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alphaA\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFEPermRefTransIso::FEPermRefTransIso(FEModel* pfem) : FEHydraulicPermeability(pfem)\n{\n\tm_perm0 = 1;\n\tm_perm1T = m_perm1A = 0;\n\tm_perm2T = m_perm2A = 0;\n\tm_M0 = m_MT = m_MA = 0;\n\tm_alpha0 = m_alphaT = m_alphaA = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Permeability tensor.\nmat3ds FEPermRefTransIso::Permeability(FEMaterialPoint& mp)\n{\n\tvec3d V;\t\t\t// axial material directions in reference configuration\n\tmat3ds m;\t\t\t// axial texture tensor in current configuration\n\t\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// deformation gradient\n\tmat3d &F = et.m_F;\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// referential solid volume fraction\n\tdouble phi0 = pt.m_phi0;\n    double phisr = pt.m_phi0t;\n\t\n    // check for potential error\n    if (J <= phisr) feLogError(\"The perm-ref-trans-iso permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\",J,phisr);\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// Copy the texture direction in the reference configuration to V\n\tV.x = Q[0][0]; V.y = Q[1][0]; V.z = Q[2][0];\n\tm = dyad(F*V);\t// Evaluate texture tensor in the current configuration\n\t\n\t// --- strain-dependent permeability ---\n\t\n\tdouble f, k1T, k1A, k2T, k2A;\n\tdouble k0 = m_perm0*pow((J-phisr)/(1-phi0),m_alpha0)*exp(m_M0*(J*J-1.0)/2.0);\n\t// Transverse direction\n\tf = pow((J-phisr)/(1-phi0),m_alphaT)*exp(m_MT*(J*J-1.0)/2.0);\n\tk1T = m_perm1T/(J*J)*f;\n\tk2T = 0.5*m_perm2T/pow(J,4)*f;\n\t// Axial direction\n\tf = pow((J-phisr)/(1-phi0),m_alphaA)*exp(m_MA*(J*J-1.0)/2.0);\n\tk1A = m_perm1A/(J*J)*f;\n\tk2A = 0.5*m_perm2A/pow(J,4)*f;\n\t// Permeability\n\tmat3ds kt = k0*I + k1T*b + (k1A-k1T)*m + 2*k2T*b.sqr() + (m*b).sym()*(2.0*(k2A-k2T));\n\t\n\treturn kt;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of permeability\ntens4dmm FEPermRefTransIso::Tangent_Permeability_Strain(FEMaterialPoint &mp)\n{\n\tvec3d V;\t\t\t// axial material directions in reference configuration\n\tmat3ds m;\t\t\t// axial texture tensor in current configuration\n\t\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// Identity\n\tmat3dd I(1);\n\t\n\t// deformation gradient\n\tmat3d &F = et.m_F;\n\t\n\t// left cauchy-green matrix\n\tmat3ds b = et.LeftCauchyGreen();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// referential solid volume fraction\n\tdouble phi0 = pt.m_phi0;\n    double phisr = pt.m_phi0t;\n\t\n    // check for potential error\n    if (J <= phisr) feLogError(\"The perm-ref-trans-iso permeability calculation failed!\\nThe volume ratio (J=%g) dropped below its theoretical minimum phi0=%g.\",J,phisr);\n    \n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// Copy the texture direction in the reference configuration to V\n\tV.x = Q[0][0]; V.y = Q[1][0]; V.z = Q[2][0];\n\tm = dyad(F*V);\t// Evaluate texture tensor in the current configuration\n\t\n\tdouble f, k0, K0prime;\n\tmat3ds k0hat, k1hat, k2hat;\n\tk0 = m_perm0*pow((J-phisr)/(1-phi0),m_alpha0)*exp(m_M0*(J*J-1.0)/2.0);\n\tK0prime = (1+J*(m_alpha0/(J-phisr)+m_M0*J))*k0;\n\tk0hat = mat3dd(K0prime);\n\ttens4dmm K4 = dyad1mm(I,k0hat)-dyad4s(I)*(2*k0);\n\t// Transverse direction\n\tf = pow((J-phisr)/(1-phi0),m_alphaT)*exp(m_MT*(J*J-1.0)/2.0);\n\tdouble k1T = m_perm1T/(J*J)*f;\n\tdouble k2T = 0.5*m_perm2T/pow(J,4)*f;\n\tmat3ds k1hatT = mat3dd((J*J*m_MT+(J*(m_alphaT-1)+phi0)/(J-phisr))*k1T);\n\tmat3ds k2hatT = mat3dd((J*J*m_MT+(J*(m_alphaT-3)+3*phi0)/(J-phisr))*k2T);\n\t// Axial direction\n\tf = pow((J-phisr)/(1-phi0),m_alphaA)*exp(m_MA*(J*J-1.0)/2.0);\n\tdouble k1A = m_perm1A/(J*J)*f;\n\tdouble k2A = 0.5*m_perm2A/pow(J,4)*f;\n\tmat3ds k1hatA = mat3dd((J*J*m_MA+(J*(m_alphaA-1)+phi0)/(J-phisr))*k1A);\n\tmat3ds k2hatA = mat3dd((J*J*m_MA+(J*(m_alphaA-3)+3*phi0)/(J-phisr))*k2A);\n\t//  Tangent\n\tK4 += dyad1mm(b.sqr(),k2hatT)*2 + dyad4s(b)*(4*k2T) + dyad4s(m,b)*(2*(k2A-k2T))\n\t+ dyad1mm(b,k1hatT) + dyad1mm(m,k1hatA-k1hatT) + dyad1mm((m*b).sym()*2.0,k2hatA-k2hatT);\n\t\n\treturn K4;\n}\n"
  },
  {
    "path": "FEBioMix/FEPermRefTransIso.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a poroelastic material that has a strain-dependent\n// permeability which is orthotropic in the reference state, but exhibits\n// further strain-induced anisotropy, according to the constitutive relation\n// of Ateshian and Weiss (JBME 2010)\n\nclass FEBIOMIX_API FEPermRefTransIso :\tpublic FEHydraulicPermeability\n\t{\n\tpublic:\n\t\t//! constructor\n\t\tFEPermRefTransIso(FEModel* pfem);\n\t\t\n\t\t//! permeability\n\t\tmat3ds Permeability(FEMaterialPoint& pt) override;\n\t\t\n\t\t//! Tangent of permeability\n\t\ttens4dmm Tangent_Permeability_Strain(FEMaterialPoint& mp) override;\n\t\t\n\tpublic:\n\t\tdouble\tm_perm0;\t\t//!< permeability for I term\n\t\tdouble\tm_perm1T;\t\t//!< transverse permeability for b term\n\t\tdouble\tm_perm1A;\t\t//!< axial permeability for b term\n\t\tdouble\tm_perm2T;\t\t//!< transverse permeability for b^2 term\n\t\tdouble\tm_perm2A;\t\t//!< axial permeability for b^2 term\n\t\tdouble\tm_M0;\t\t\t//!< nonlinear exponential coefficient for I term\n\t\tdouble\tm_MT;\t\t\t//!< nonlinear exponential coefficient for transverse direction\n\t\tdouble\tm_MA;\t\t\t//!< nonlinear exponential coefficient for axial direction\n\t\tdouble\tm_alpha0;\t\t//!< nonlinear power exponent for I term\n\t\tdouble\tm_alphaT;\t\t//!< nonlinear power exponent for transverse direction\n\t\tdouble\tm_alphaA;\t\t//!< nonlinear power exponent for axial direction\n\t\t\n\t\t// declare parameter list\n\t\tDECLARE_FECORE_CLASS();\n\t};\n"
  },
  {
    "path": "FEBioMix/FEPorousNeoHookean.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPorousNeoHookean.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FEMultiphasic.h\"\n#include \"FETriphasic.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEPorousNeoHookean, FEElasticMaterial)\n\tADD_PARAMETER(m_E    , FE_RANGE_GREATER   (      0.0), \"E\"       )->setUnits(UNIT_PRESSURE)->setLongName(\"Young's modulus\");\n\tADD_PARAMETER(m_phisr, FE_RANGE_CLOSED    (0.0 , 1.0), \"phi0\"    )->setLongName(\"solid volume fraction\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FEPorousNeoHookean::Init()\n{\n    FECoreBase* ancestor = GetAncestor();\n    FEBiphasic* bparnt = dynamic_cast<FEBiphasic*>(ancestor);\n    FEBiphasicSolute* bsprnt = dynamic_cast<FEBiphasicSolute*>(ancestor);\n    FEMultiphasic* mparnt = dynamic_cast<FEMultiphasic*>(ancestor);\n    if (bparnt) m_phisr = bparnt->m_phi0;\n    else if (bsprnt) m_phisr = bsprnt->m_phi0;\n    else if (mparnt) m_phisr = mparnt->m_phi0;\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FEPorousNeoHookean::Stress(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n    double E = m_E(mp);\n    double lam = m_lam(mp);\n    \n    double phisr = ReferentialSolidVolumeFraction(mp);\n    double phiwr = 1 - phisr;\n    double mu  = E/3*(1+0.5*phiwr*phiwr);\n    double J = pt.m_J;\n    double Jbar = (J-phisr)/phiwr;\n    double lnJbar = log(Jbar);\n    double R = pow(Jbar/J, 2./3.);\n    double mu1 = mu/J;\n\n    // calculate left Cauchy-Green tensor\n    mat3ds b = pt.LeftCauchyGreen();\n    double I1 = b.tr();\n    \n    // Identity\n    mat3dd I(1);\n    \n    // calculate stress\n    mat3ds s = b*(mu1*R) + I*((mu1*(phisr*R*I1/3. - J) + lam*lnJbar)/(J - phisr));\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FEPorousNeoHookean::Tangent(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double E = m_E(mp);\n    double lam = m_lam(mp);\n    \n    double phisr = ReferentialSolidVolumeFraction(mp);\n    double phiwr = 1 - phisr;\n    double mu  = E/3*(1+0.5*phiwr*phiwr);\n    double J = pt.m_J;\n    double Jbar = (J-phisr)/phiwr;\n    double lnJbar = log(Jbar);\n    double R = pow(Jbar/J, 2./3.);\n    double mu1 = mu/J;\n    double lam1 = lam/J;\n    \n    double g = mu1*R*phisr/(J-phisr);\n    double h = (lam1*lnJbar - mu1)*J/(J-phisr);\n    double Jdg = mu1*R*(2*phisr - 3*J)*phisr/3./pow(J - phisr, 2);\n    double Jdh = J*(mu1*phisr + lam1*(J - phisr*lnJbar))/pow(J - phisr, 2);\n\n    // calculate left Cauchy-Green tensor\n    mat3ds b = pt.LeftCauchyGreen();\n    double I1 = b.tr();\n    \n    // Identity\n    mat3dd I(1);\n    \n    tens4ds bIIb = dyad1s(I, b);\n    tens4ds II = dyad1s(I);\n    tens4ds I4 = dyad4s(I);\n    tens4ds c = bIIb*2.*g/3. + II*(Jdg*I1/3 + Jdh) - I4*(2*(g*I1/3 + h));\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPorousNeoHookean::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    double E = m_E(mp);\n    double lam = m_lam(mp);\n    \n    double phisr = ReferentialSolidVolumeFraction(mp);\n    double phiwr = 1 - phisr;\n    double mu  = E/3*(1+0.5*phiwr*phiwr);\n    double J = pt.m_J;\n    double Jbar = (J-phisr)/phiwr;\n    double lnJbar = log(Jbar);\n    \n    // calculate left Cauchy-Green tensor\n    mat3ds b = pt.LeftCauchyGreen();\n    double I1bar = b.tr()*pow(Jbar/J, 2./3.);\n    \n    double sed = mu*((I1bar-3)/2.0 - lnJbar)+lam*lnJbar*lnJbar/2.0;\n    \n    return sed;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPorousNeoHookean::ReferentialSolidVolumeFraction(FEMaterialPoint& mp)\n{\n    return m_phisr(mp);\n}\n"
  },
  {
    "path": "FEBioMix/FEPorousNeoHookean.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Porous Neo Hookean material\n//! This compressible material produces infinite stress as the porosity approaches\n//! zero (i.e., when J = 1 - porosity).  This behavior helps to prevent pore collape.\n\n//! Implementation of a porous neo-Hookean hyperelastic material.\nclass FEBIOMIX_API FEPorousNeoHookean : public FEElasticMaterial\n{\npublic:\n    FEPorousNeoHookean(FEModel* pfem) : FEElasticMaterial(pfem) { m_phisr = 1; m_lam = 0; }\n    \npublic:\n    FEParamDouble   m_E;        //!< Young's modulus\n    FEParamDouble   m_lam;      //!< first Lamé coefficient\n    FEParamDouble   m_phisr;    //!< referential solidity\n    \npublic:\n    //! initialize the material\n    bool Init() override;\n    \n    //! calculate stress at material point\n    mat3ds Stress(FEMaterialPoint& pt) override;\n    \n    //! calculate tangent stiffness at material point\n    tens4ds Tangent(FEMaterialPoint& pt) override;\n    \n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override;\n    \n    //! calculate the referential solid volume fraction at material point\n    double ReferentialSolidVolumeFraction(FEMaterialPoint& pt);\n    \n    // declare the parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPrescribedConcentration.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedConcentration.h\"\n#include <FECore/FEModel.h>\n#include <FECore/DOFS.h>\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedConcentration, FEBoundaryCondition)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list:concentration)\");\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_CONCENTRATION)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedConcentration::FEPrescribedConcentration(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n"
  },
  {
    "path": "FEBioMix/FEPrescribedConcentration.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEPrescribedConcentration : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedConcentration(FEModel* fem);\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPrescribedNodalFluidPressure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPrescribedNodalFluidPressure.h\"\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedNodalFluidPressure, FEBoundaryCondition)\n\tADD_PARAMETER(m_scale, \"value\")->setUnits(UNIT_PRESSURE)->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedNodalFluidPressure::FEPrescribedNodalFluidPressure(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n\nbool FEPrescribedNodalFluidPressure::Init()\n{\n\tif (SetDOF(\"p\") == false) return false;\n\treturn FEPrescribedDOF::Init();\n}\n\n//=======================================================================================\n// NOTE: I'm setting FEBoundaryCondition is the base class since I don't want to pull\n//       in the parameters of FEPrescribedDOF. \nBEGIN_FECORE_CLASS(FEPrescribedShellFluidPressure, FEBoundaryCondition)\n\tADD_PARAMETER(m_scale, \"value\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedShellFluidPressure::FEPrescribedShellFluidPressure(FEModel* fem) : FEPrescribedDOF(fem)\n{\n}\n\nbool FEPrescribedShellFluidPressure::Init()\n{\n\tif (SetDOF(\"q\") == false) return false;\n\treturn FEPrescribedDOF::Init();\n}\n"
  },
  {
    "path": "FEBioMix/FEPrescribedNodalFluidPressure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedDOF.h>\n#include \"febiomix_api.h\"\n\nclass FEBIOMIX_API FEPrescribedNodalFluidPressure : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedNodalFluidPressure(FEModel* fem);\n\tbool Init() override;\n\nprivate:\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEBIOMIX_API FEPrescribedShellFluidPressure : public FEPrescribedDOF\n{\npublic:\n\tFEPrescribedShellFluidPressure(FEModel* fem);\n\tbool Init() override;\n\nprivate:\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEPressureStabilization.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPressureStabilization.h\"\n#include \"FEBiphasic.h\"\n#include \"FECore/FEModel.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPressureStabilization, FESurfaceLoad)\n\tADD_PARAMETER(m_bstab, \"stabilize\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEPressureStabilization::FEPressureStabilization(FEModel* pfem) : FESurfaceLoad(pfem)\n{\n    m_bstab = true;\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FEPressureStabilization::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\nbool FEPressureStabilization::Init()\n{\n    FESurface& ps = GetSurface();\n    ps.Init();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPressureStabilization::Activate()\n{\n    FESurface& ps = GetSurface();\n    \n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // loop over all surface elements\n    for (int i=0; i<ps.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = ps.Element(i);\n        \n        // find the element this face belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        assert(pe);\n        \n        // calculate time constant\n        double tau = TimeConstant(el, ps);\n    }\n    \n    FESurfaceLoad::Activate();\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPressureStabilization::TimeConstant(FESurfaceElement& el, FESurface& s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    double tau = 0;\n    \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe)\n    {\n        // evaluate element surface normal at parametric center\n        vec3d t[2];\n        s.CoBaseVectors0(el, 0, 0, t);\n        vec3d n = t[0] ^ t[1];\n        n.unit();\n        \n        // get the material\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        \n        // see if this is a poro-elastic element\n        FEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n        if (biph)\n        {\n            // get the area of the surface element\n            double A = s.FaceArea(el);\n            \n            // get the volume of the volume element\n            double V = m.ElementVolume(*pe);\n            \n            // calculate the element thickness\n            double h = V/A;\n            \n            // get a material point\n            FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n            FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n            \n            // setup the material point\n            ept.m_F = mat3dd(1.0);\n            ept.m_J = 1;\n            ept.m_s.zero();\n            \n            // get the tangent (stiffness) and it inverse (compliance) at this point\n            tens4dmm C = biph->Tangent(mp);\n            double Ha = n*(vdotTdotv(n, C, n)*n);\n            \n            // if this is a poroelastic element, then get the permeability tensor\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            pt.m_p = 0;\n            pt.m_w = vec3d(0,0,0);\n            \n            mat3ds K = biph->Permeability(mp);\n\n            double k = n*(K*n);\n            \n            tau = h*h/(4*Ha*k);\n            \n            // if time constant not yet set\n            if (biph->m_tau == 0)\n                biph->m_tau = tau;  // set it to calculated value\n            else\n                // pick smallest value\n                biph->m_tau = max(biph->m_tau, tau);\n        }\n    }\n    \n    return tau;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEPressureStabilization.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This pseudo-surface load is used to calculate the pressure stabilization\n//! time constant based on the properties of elements under that surface\n//!\nclass FEBIOMIX_API FEPressureStabilization : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FEPressureStabilization(FEModel* pfem);\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n    \n    //! calculate pressure stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override {}\n    \n    //! calculate residual\n    void LoadVector(FEGlobalVector& R) override {}\n    \n    //! initialize\n    bool Init() override;\n    \n    //! activate\n    void Activate() override;\n\nprotected:\n    double TimeConstant(FESurfaceElement& el, FESurface& s);\n    \nprotected:\n    bool\tm_bstab;\t\t//!< flag for calculating stabilization constant\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEReaction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReaction.h\"\n#include <FECore/log.h>\n#include \"FESoluteInterface.h\"\n#include \"FESolute.h\"\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\nFEReaction::FEReaction(FEModel* pfem) : FEMaterialProperty(pfem)\n{\n    m_psm = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FEReaction::Init()\n{\n    // make sure the parent class is set\n    m_psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    assert(m_psm);\n    if (m_psm == 0) {\n        feLogError(\"Parent class not set or of incorrect type\");\n        return false;\n    }\n    \n\t// TODO: Why does calling the base class cause a crash in cr02???\n\treturn true;// FEMaterialProperty::Init();\n}\n\nvoid FEReaction::Serialize(DumpStream& dmp)\n{\n\tFEMaterialProperty::Serialize(dmp);\n\tif (!dmp.IsShallow())\n\t{\n\t\tif (dmp.IsLoading())\n\t\t{\n\t\t\tm_psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\t\t\tassert(m_psm);\n\t\t}\n\t}\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEReactionSpeciesRef, FEMaterialProperty)\n\tADD_PARAMETER(m_speciesID, \"species\", FE_PARAM_ATTRIBUTE, \"$(species)\");\n\tADD_PARAMETER(m_solId    , \"sol\")->SetFlags(FE_PARAM_ATTRIBUTE | FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_sbmId    , \"sbm\")->SetFlags(FE_PARAM_ATTRIBUTE | FE_PARAM_HIDDEN);\nEND_FECORE_CLASS();\n\nFEReactionSpeciesRef::FEReactionSpeciesRef(FEModel* fem) : FEMaterialProperty(fem) \n{\n    m_speciesID = -1;\n    m_solId = -1;\n    m_sbmId = -1;\n    \n    m_v = 0;\n\n    m_speciesType = UnknownSpecies;\n}\n\nint FEReactionSpeciesRef::GetSpeciesType() const\n{\n    assert(m_speciesType != UnknownSpecies);\n    return m_speciesType;\n}\n\nbool FEReactionSpeciesRef::IsSolute() const\n{\n    return (m_speciesType == SoluteSpecies);\n}\n\nbool FEReactionSpeciesRef::IsSBM() const\n{\n    return (m_speciesType == SBMSpecies);\n}\n\nbool FEReactionSpeciesRef::Init()\n{\n    // make sure the species ID is valid\n    if ((m_speciesID == -1) && (m_solId == -1) && (m_sbmId == -1)) return false;\n\n    // figure out if this is a solute or sbm\n    if (m_speciesType == UnknownSpecies)\n    {\n        FEModel& fem = *GetFEModel();\n        if (m_speciesID != -1)\n        {\n            int id = m_speciesID - 1;\n            int n = 0;\n            for (int i = 0; i < fem.GlobalDataItems(); ++i)\n            {\n                FESoluteData* sol = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n                FESBMData* sbm = dynamic_cast<FESBMData*>(fem.GetGlobalData(i));\n                if (sol || sbm)\n                {\n                    if (id == n)\n                    {\n                        if (sol) { m_speciesType = SoluteSpecies; m_speciesID = sol->GetID(); }\n                        if (sbm) { m_speciesType = SBMSpecies; m_speciesID = sbm->GetID(); }\n\n                        break;\n                    }\n                    else n++;\n                }\n            }\n        }\n        else if (m_solId != -1)\n        {\n            for (int i = 0; i < fem.GlobalDataItems(); ++i)\n            {\n                FESoluteData* sol = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n                if (sol && (sol->GetID() == m_solId))\n                {\n                    m_speciesType = SoluteSpecies;\n                    m_speciesID = sol->GetID(); \n                }\n            }\n        }\n        else if (m_sbmId != -1)\n        {\n            for (int i = 0; i < fem.GlobalDataItems(); ++i)\n            {\n                FESBMData* sbm = dynamic_cast<FESBMData*>(fem.GetGlobalData(i));\n                if (sbm && (sbm->GetID() == m_sbmId))\n                {\n                    m_speciesType = SBMSpecies;\n                    m_speciesID = sbm->GetID();\n                }\n            }\n        }\n\n        assert(m_speciesType != UnknownSpecies);\n        if (m_speciesType == UnknownSpecies) return false;\n    }\n\n    return FEMaterialProperty::Init();\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEReactantSpeciesRef, FEReactionSpeciesRef)\n    ADD_PARAMETER(m_v, \"vR\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEProductSpeciesRef, FEReactionSpeciesRef)\n    ADD_PARAMETER(m_v, \"vP\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMix/FEReaction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiomix_api.h\"\n#include <map>\n\n//-----------------------------------------------------------------------------\nclass FESoluteInterface;\n\n//-----------------------------------------------------------------------------\n//! Base class for reactions.\n\ntypedef std::map<int,int> intmap;\ntypedef std::map<int,int>::iterator itrmap;\n\nclass FEBIOMIX_API FEReaction : public FEMaterialProperty\n{\npublic:\n    //! constructor\n    FEReaction(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& dmp);\n    \npublic:\n    //! set stoichiometric coefficient\n    void SetStoichiometricCoefficient(intmap& RP, int id, int v) { RP.insert(std::pair<int, int>(id, v)); }\n\npublic: //TODO: Make this protected again\n    FESoluteInterface* m_psm;   //!< solute interface to parent class\n};\n\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FEReactionSpeciesRef : public FEMaterialProperty\n{\npublic:\n    enum SpeciesType { UnknownSpecies, SoluteSpecies, SBMSpecies };\n\npublic:\n    FEReactionSpeciesRef(FEModel* fem);\n\n    bool Init() override;\n\n    int GetSpeciesType() const;\n\n    bool IsSolute() const;\n    bool IsSBM() const;\n\npublic:\n    int     m_speciesID;        // the species ID\n    int     m_v;                // stoichiometric coefficient\n\n    // these parameters are mostly for parsing older files that used \"sol\" or \"sbm\"\n    int     m_solId;\n    int     m_sbmId;\n\nprivate:\n    int     m_speciesType;  // solute or sbm?\n\n    DECLARE_FECORE_CLASS();\n};\n\nclass FEBIOMIX_API FEReactantSpeciesRef : public FEReactionSpeciesRef\n{\npublic: FEReactantSpeciesRef(FEModel* fem) : FEReactionSpeciesRef(fem) {}\n      DECLARE_FECORE_CLASS();\n      FECORE_BASE_CLASS(FEReactantSpeciesRef)\n};\n\nclass FEBIOMIX_API FEProductSpeciesRef : public FEReactionSpeciesRef {\npublic: FEProductSpeciesRef(FEModel* fem) : FEReactionSpeciesRef(fem) {}\n      DECLARE_FECORE_CLASS();\n      FECORE_BASE_CLASS(FEProductSpeciesRef)\n};\n"
  },
  {
    "path": "FEBioMix/FEReactionRateConst.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactionRateConst.h\"\n\n// Material parameters for the FEReactionRateConst material\nBEGIN_FECORE_CLASS(FEReactionRateConst, FEReactionRate)\n\tADD_PARAMETER(m_k, FE_RANGE_GREATER_OR_EQUAL(0.0), \"k\");\nEND_FECORE_CLASS();\n"
  },
  {
    "path": "FEBioMix/FEReactionRateConst.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\nclass FEBIOMIX_API FEReactionRateConst : public FEReactionRate\n{\npublic:\n\t//! constructor\n\tFEReactionRateConst(FEModel* pfem) : FEReactionRate(pfem) { m_k = 0; }\n\t\n\t//! reaction rate at material point\n\tdouble ReactionRate(FEMaterialPoint& pt) override { return m_k(pt); }\n\t\n\t//! tangent of reaction rate with strain at material point\n\tmat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override { return mat3ds(0); }\n\t\n\t//! tangent of reaction rate with effective fluid pressure at material point\n\tdouble Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override {return 0; }\n\npublic:\n\tFEParamDouble   m_k;\t\t//!< reaction rate\n\t\n\tDECLARE_FECORE_CLASS();\t\n};\n"
  },
  {
    "path": "FEBioMix/FEReactionRateExpSED.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactionRateExpSED.h\"\n#include \"FEBioMech/FERemodelingElasticMaterial.h\"\n#include \"FEBiphasic.h\"\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEReactionRateExpSED, FEReactionRate)\n\tADD_PARAMETER(m_B   , \"B\");\n\tADD_PARAMETER(m_Psi0, FE_RANGE_NOT_EQUAL(0.0), \"Psi0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEReactionRateExpSED::FEReactionRateExpSED(FEModel* pfem) : FEReactionRate(pfem) \n{ \n    m_B = 0;\n    m_Psi0 = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! reaction rate at material point\ndouble FEReactionRateExpSED::ReactionRate(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n    double phir = pbm->SolidReferentialVolumeFraction(pt);\n    \n    FERemodelingMaterialPoint& rpt = *(pt.ExtractData<FERemodelingMaterialPoint>());\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    double J = et.m_J;\n    double sed = rpt.m_sed;\n    double zhat = m_B(pt)*exp(sed/m_Psi0(pt))/(J-phir);\n    return zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with strain at material point\nmat3ds FEReactionRateExpSED::Tangent_ReactionRate_Strain(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double phir = pbm->SolidReferentialVolumeFraction(pt);\n    double p = pbm->GetActualFluidPressure(pt);\n    \n    double J = et.m_J;\n    double zhat = ReactionRate(pt);\n    mat3dd I(1);\n    mat3ds dzhatde = (I/(phir-J) + (et.m_s+I*p)/m_Psi0(pt))*zhat;\n    return dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with effective fluid pressure at material point\ndouble FEReactionRateExpSED::Tangent_ReactionRate_Pressure(FEMaterialPoint& pt)\n{\n    return 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEReactionRateExpSED.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\nclass FEBIOMIX_API FEReactionRateExpSED : public FEReactionRate\n{\npublic:\n    //! constructor\n    FEReactionRateExpSED(FEModel* pfem);\n    \n    //! reaction rate at material point\n    double ReactionRate(FEMaterialPoint& pt) override;\n    \n    //! tangent of reaction rate with strain at material point\n    mat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override;\n    \n    //! tangent of reaction rate with effective fluid pressure at material point\n    double Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override;\n    \npublic:\n    FEParamDouble   m_B;\t\t\t\t\t//!< mass supply coefficient\n    FEParamDouble   m_Psi0;\t\t\t\t\t//!< scaling strain energy density\n    \n    DECLARE_FECORE_CLASS();\t\n};\n"
  },
  {
    "path": "FEBioMix/FEReactionRateHuiskes.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactionRateHuiskes.h\"\n#include \"FEBiphasic.h\"\n#include \"FEMultiphasic.h\"\n#include <FEBioMech/FERemodelingElasticMaterial.h>\n#include <FEBioMech/FEElasticMixture.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEReactionRateHuiskes, FEReactionRate)\n\tADD_PARAMETER(m_B, \"B\");\n\tADD_PARAMETER(m_psi0, FE_RANGE_GREATER_OR_EQUAL(0.0), \"psi0\")->setUnits(UNIT_SPECIFIC_ENERGY);\n    ADD_PARAMETER(m_D, FE_RANGE_GREATER_OR_EQUAL(0.0), \"D\")->setUnits(UNIT_LENGTH)->setLongName(\"sensor distance\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEReactionRateHuiskes::FEReactionRateHuiskes(FEModel* pfem) : FEReactionRate(pfem) \n{ \n    m_B = 0;\n    m_psi0 = 0;\n    m_D = 0;\n    m_comp = -1;\n    m_binit = false;\n    m_M = 0;\n    m_lsbm = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEReactionRateHuiskes::Init()\n{\n    if (m_binit) return true;\n    FEMultiphasic* pbm = dynamic_cast<FEMultiphasic*>(GetAncestor());\n    if (pbm == nullptr) return true;    // in case material is not multiphasic\n\n    FEChemicalReaction* pcm = dynamic_cast<FEChemicalReaction*>(m_pReact);\n    int sbm = pcm->m_vPtmp[0]->m_speciesID;\n    m_lsbm = pbm->FindLocalSBMID(sbm);\n    m_M = pbm->SBMMolarMass(m_lsbm);\n\n    // get neighboring elements for given proximity\n    if (m_D > 0) {\n        double mult = 4;    //! multiplier of characteristic distance, such that exp(-mult) << 1\n        FEMesh& mesh = GetFEModel()->GetMesh();\n        if (m_topo.Create(&mesh) == false)\n        {\n            feLogError(\"Failed building mesh topo.\");\n            return false;\n        }\n        feLogInfo(\"Evaluating element proximity...\");\n        m_EPL.assign(mesh.Elements(), std::vector<int>());\n        for (int i=0; i< mesh.Elements(); ++i) {\n            std::vector<int> epl = m_topo.ElementProximityList(i, m_D*mult);\n            m_EPL[i] = epl;\n        }\n        feLogInfo(\"Done.\");\n    }\n    m_binit = true;\n\n    FEElasticMaterial* pem = pbm->GetSolid();\n    FEElasticMixture* psm = dynamic_cast<FEElasticMixture*>(pem);\n    if (psm == nullptr) return true;    // in case material is not a solid mixture\n    \n    for (int i=0; i<psm->Materials(); ++i) {\n        pem = psm->GetMaterial(i);\n        // check the types of materials that can employ this reaction rate\n        FERemodelingInterface* pri = dynamic_cast<FERemodelingInterface*>(pem);\n        if (pri) {\n            if (sbm == pri->m_sbm) m_comp = pri->m_comp;\n        }\n    }\n    if (m_comp == -1) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! reaction rate at material point\ndouble FEReactionRateHuiskes::ReactionRate(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    double rhor = pbm->SolidReferentialApparentDensity(pt);\n    double phir = pbm->SolidReferentialVolumeFraction(pt);\n\t\n    FERemodelingMaterialPoint* rpt = nullptr;\n    double J = 1;\n    double sed = 0;\n    if (m_comp == -1) {\n        rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n        FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n        J = et.m_J;\n    }\n    else {\n        FEElasticMixtureMaterialPoint* mp = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        FEMaterialPoint& mpi = *mp->GetPointData(m_comp);\n        rpt = mpi.ExtractData<FERemodelingMaterialPoint>();\n        FEElasticMaterialPoint& et = *mpi.ExtractData<FEElasticMaterialPoint>();\n        J = et.m_J;\n    }\n    sed = rpt->m_sed;\n    double B = m_B(pt);\n    double psi0 = m_psi0(pt);\n\tdouble zhat = B*(sed/rhor - psi0)/(J-phir)/m_M;\n    if (m_D > 0) {\n        FEMesh& mesh = GetFEModel()->GetMesh();\n        int ie = pt.m_elem->GetLocalID();\n        int NEPL = (int)m_EPL[ie].size();\n#pragma omp parallel for shared (NEPL)\n        for (int i=0; i<NEPL; ++i) {\n            int je = m_EPL[ie][i];\n            if (je > -1) {\n                FEElement* el = mesh.Element(je);\n                for (int k=0; k<el->GaussPoints(); ++k) {\n                    FEMaterialPoint& mp = *(el->GetMaterialPoint(k));\n                    double d = (pt.m_rt - mp.m_rt).unit();\n                    if (m_comp == -1) {\n                        rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n                        FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                        J = et.m_J;\n                    }\n                    else {\n                        FEElasticMixtureMaterialPoint* mmp = mp.ExtractData<FEElasticMixtureMaterialPoint>();\n                        FEMaterialPoint& mpi = *mmp->GetPointData(m_comp);\n                        rpt = mpi.ExtractData<FERemodelingMaterialPoint>();\n                    }\n                    sed = rpt->m_sed;\n                    rhor = pbm->SolidReferentialApparentDensity(mp);\n                    phir = pbm->SolidReferentialVolumeFraction(mp);\n                    B = m_B(mp);\n                    psi0 = m_psi0(mp);\n                    zhat += exp(-d/m_D)*B*(sed/rhor - psi0)/(J-phir)/m_M;\n                }\n            }\n        }\n    }\n\treturn zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with strain at material point\nmat3ds FEReactionRateHuiskes::Tangent_ReactionRate_Strain(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double rhor = pbm->SolidReferentialApparentDensity(pt);\n    double phir = pbm->SolidReferentialVolumeFraction(pt);\n    double p = pbm->GetActualFluidPressure(pt);\n\n    double J = 1;\n    if (m_comp == -1) {\n        FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n        J = et.m_J;\n    }\n    else {\n        FEElasticMixtureMaterialPoint* mmp = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        FEMaterialPoint& mpi = *mmp->GetPointData(m_comp);\n        FEElasticMaterialPoint& et = *mpi.ExtractData<FEElasticMaterialPoint>();\n        J = et.m_J;\n    }\n    double zhat = ReactionRate(pt);\n    mat3dd I(1);\n    double B = m_B(pt);\n    mat3ds dzhatde = (I*(-zhat) + (et.m_s+I*p)*(B/rhor/m_M))/(J-phir);\n    if (m_D > 0) {\n        mat3ds s(0);\n        FEMesh& mesh = GetFEModel()->GetMesh();\n        int ie = pt.m_elem->GetLocalID();\n        int NEPL = (int)m_EPL[ie].size();\n#pragma omp parallel for shared (NEPL)\n        for (int i=0; i<NEPL; ++i) {\n            int je = m_EPL[ie][i];\n            if (je > -1) {\n                FEElement* el = mesh.Element(je);\n                for (int k=0; k<el->GaussPoints(); ++k) {\n                    FEMaterialPoint& mp = *(el->GetMaterialPoint(k));\n                    double d = (pt.m_rt - mp.m_rt).unit();\n                    if (m_comp == -1) {\n                        FEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n                        // we just want the effective stress, so subtract the pressure term\n                        p = pbm->GetActualFluidPressure(mp);\n                        s = et.m_s+I*p;\n                    }\n                    else {\n                        FEElasticMixtureMaterialPoint* mmp = mp.ExtractData<FEElasticMixtureMaterialPoint>();\n                        FEMaterialPoint& mpi = *mmp->GetPointData(m_comp);\n                        FEElasticMaterialPoint& et = *mpi.ExtractData<FEElasticMaterialPoint>();\n                        // the stress stored in elastic mixture material points is an effective stress\n                        s = et.m_s;\n                    }\n                    rhor = pbm->SolidReferentialApparentDensity(mp);\n                    B = m_B(mp);\n                    dzhatde += exp(-d/m_D)*s*(B/rhor/m_M)/(J-phir);\n                }\n            }\n        }\n    }\n\treturn dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with effective fluid pressure at material point\ndouble FEReactionRateHuiskes::Tangent_ReactionRate_Pressure(FEMaterialPoint& pt)\n{\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEReactionRateHuiskes.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n#include <FECore/FEMeshTopo.h>\n\nclass FEBIOMIX_API FEReactionRateHuiskes : public FEReactionRate\n{\npublic:\n\t//! constructor\n\tFEReactionRateHuiskes(FEModel* pfem);\n\t\n    //! initialization\n    bool Init() override;\n    \n\t//! reaction rate at material point\n\tdouble ReactionRate(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with strain at material point\n\tmat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with effective fluid pressure at material point\n\tdouble Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override;\n\t\npublic:\n\tFEParamDouble   m_B;\t\t\t\t\t//!< mass supply coefficient\n    FEParamDouble   m_psi0;\t\t\t\t\t//!< specific strain energy at homeostasis\n    double          m_D;                    //!< characteristic sensor distance\n\nprivate:\n    int             m_comp;                 //!< component of solid mixture (if applicable)\n    std::vector<std::vector<int>>    m_EPL; //!< list of element proximity lists\n    FEMeshTopo      m_topo;                 //!< mesh topology;\n    bool            m_binit;                //!< initialization flag\n    double          m_M;                    //!< molar mass of sbm\n    int             m_lsbm;                 //!< local sbm value\n\n\tDECLARE_FECORE_CLASS();\t\n};\n"
  },
  {
    "path": "FEBioMix/FEReactionRateNims.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactionRateNims.h\"\n#include \"FESoluteInterface.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/log.h>\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEReactionRateNims, FEReactionRate)\n    ADD_PARAMETER(m_sol  , \"sol\")->setEnums(\"$(solutes)\");\n\tADD_PARAMETER(m_k0, \"k0\");\n\tADD_PARAMETER(m_kc, \"kc\");\n\tADD_PARAMETER(m_kr, \"kr\");\n\tADD_PARAMETER(m_cc  , FE_RANGE_GREATER         (0.0), \"cc\");\n\tADD_PARAMETER(m_cr  , FE_RANGE_GREATER         (0.0), \"cr\");\n\tADD_PARAMETER(m_trel, FE_RANGE_GREATER_OR_EQUAL(0.0), \"trel\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEReactionRateNims::FEReactionRateNims(FEModel* pfem) : FEReactionRate(pfem)\n{\n    m_lid = m_cmax = -1;\n    m_sol = -1;\n    m_k0 = 0.0;\n    m_cc = 0.0;\n    m_kc = 0.0;\n    m_cr = 0.0;\n    m_kr = 0.0;\n    m_trel = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEReactionRateNims::Init()\n{\n\tif (FEReactionRate::Init() == false) return false;\n\t\n    // do only once\n    if (m_lid == -1) {\n        // get number of DOFS\n        DOFS& fedofs = GetFEModel()->GetDOFS();\n        int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n        // check validity of sol\n\t\tif (m_sol < 1 || m_sol > MAX_CDOFS) {\n\t\t\tfeLogError(\"sol value outside of valid range for solutes\");\n\t\t\treturn false;\n\t\t}\n        \n        // convert global sol value to local id\n        FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n\t\tm_lid = psm->FindLocalSoluteID(m_sol);\n        \n        // check validity of local id\n\t\tif (m_lid == -1) {\n\t\t\tfeLogError(\"sol does not match any solute in multiphasic material\");\n\t\t\treturn false;\n\t\t}\n    }\n\n\treturn true;\n}\n\n#ifndef max\n#define max(a,b) ((a)>(b)?(a):(b))\n#endif\n\n//-----------------------------------------------------------------------------\n//! reaction rate at material point\ndouble FEReactionRateNims::ReactionRate(FEMaterialPoint& pt)\n{\n    // get the time\n\tdouble t = GetFEModel()->GetTime().currentTime;\n    \n    FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n    double c = spt.m_ca[m_lid];\n    double cmax = max(c,spt.m_crd[m_cmax]);\n    \n    double k = m_k0(pt);\n    double kr = m_kr(pt);\n    double kc = m_kc(pt);\n    double trel = m_trel(pt);\n    double cr = m_cr(pt);\n    double cc = m_cc(pt);\n    \n    // if we are past the release time and got exposed to the solute\n    if ((trel > 0) && (t >= trel)) {\n        if (cmax < cr) k += (kr - k)*cmax/cr;\n        else k = kr;\n    }\n    // otherwise\n    else {\n        // evaluate reaction rate\n        if (cmax < cc) k += (kc - k)*cmax/cc;\n        else k = kc;\n    }\n\n\treturn k;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with strain at material point\nmat3ds FEReactionRateNims::Tangent_ReactionRate_Strain(FEMaterialPoint& pt)\n{\n\treturn mat3dd(0);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with effective fluid pressure at material point\ndouble FEReactionRateNims::Tangent_ReactionRate_Pressure(FEMaterialPoint& pt)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! reset, initialize and update chemical reaction data in the FESolutesMaterialPoint\nvoid FEReactionRateNims::ResetElementData(FEMaterialPoint& mp)\n{\n    // store the solute maximum concentration in the optional\n    // chemical reaction data vector m_crd in the solutes material point\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    spt.m_crd.push_back(0);\n    m_cmax = (int)spt.m_crd.size() - 1;\n}\n\nvoid FEReactionRateNims::InitializeElementData(FEMaterialPoint& mp)\n{\n    FESolutesMaterialPoint& pt = *mp.ExtractData<FESolutesMaterialPoint>();\n    double c = pt.m_ca[m_lid];\n    double cmax = pt.m_crd[m_cmax];\n    if (c > cmax) pt.m_crd[m_cmax] = c;\n}\n\nvoid FEReactionRateNims::UpdateElementData(FEMaterialPoint& mp)\n{\n}\n"
  },
  {
    "path": "FEBioMix/FEReactionRateNims.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\n//-----------------------------------------------------------------------------\n//! Concentration-history-dependent reaction rate.\n//! Reaction rate depends on concentration of a solute (e.g., growth factor)\n//! and whether solute has been released (removed) at some release time.\n//! Before release, reaction rate varies linearly with history of maximum solute\n//! concentration cmax, from k0 at cmax=0 to kc at cmax=cc, then holds constant at kc.\n//! After release, reaction rate increases from k0 at cmax=0 to kr at cmax=cr,\n//! then holds constant at kr. Release time is trel.\n\nclass FEBIOMIX_API FEReactionRateNims : public FEReactionRate\n{\npublic:\n\t//! constructor\n\tFEReactionRateNims(FEModel* pfem);\n\t\n\t//! data initialization and checking\n\tbool Init() override;\n\t\n\t//! reaction rate at material point\n\tdouble ReactionRate(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with strain at material point\n\tmat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with effective fluid pressure at material point\n\tdouble Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override;\n\t\n    //! reset, initialize and update chemical reaction data in the FESolutesMaterialPoint\n    void ResetElementData(FEMaterialPoint& mp) override;\n    void InitializeElementData(FEMaterialPoint& mp) override;\n    void UpdateElementData(FEMaterialPoint& mp) override;\n    \npublic:\n    int             m_sol;                  //!< solute id (1-based)\n    int             m_lid;                  //!< local id of solute (zero-based)\n    FEParamDouble   m_k0;                   //!< reaction rate at zero concentration\n    FEParamDouble   m_cc;                   //!< concentration cc\n    FEParamDouble   m_kc;                   //!< reaction rate at cc;\n    FEParamDouble   m_cr;                   //!< concentration cr;\n    FEParamDouble   m_kr;                   //!< reaction rate at cr;\n    FEParamDouble   m_trel;                 //!< release time;\n    int             m_cmax;                 //!< index of entry in m_crd\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FEReactionRateRuberti.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactionRateRuberti.h\"\n#include \"FEBiphasic.h\"\n#include \"FEMultiphasic.h\"\n#include \"FESoluteInterface.h\"\n#include <FEBioMech/FERemodelingElasticMaterial.h>\n#include <FEBioMech/FEElasticMixture.h>\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEReactionRateRuberti, FEReactionRate)\n\tADD_PARAMETER(m_kd, \"kd\");\n\tADD_PARAMETER(m_sig, FE_RANGE_GREATER_OR_EQUAL(0.0), \"sigma\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEReactionRateRuberti::FEReactionRateRuberti(FEModel* pfem) : FEReactionRate(pfem)\n{\n    m_kd = 0;\n    m_sig = 0;\n    m_comp = -1;\n    m_fiber = nullptr;\n    m_M = 0;\n    m_lsbm = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEReactionRateRuberti::Init()\n{\n    FEMultiphasic* pbm = dynamic_cast<FEMultiphasic*>(GetAncestor());\n    if (pbm == nullptr) return true;    // in case material is not multiphasic\n\n    FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    \n    FEChemicalReaction* pcm = dynamic_cast<FEChemicalReaction*>(m_pReact);\n    int sbm = pcm->m_vRtmp[0]->m_speciesID;\n    m_lsbm = pbm->FindLocalSBMID(sbm);\n    m_M = pbm->SBMMolarMass(m_lsbm);\n    \n    FEElasticMaterial* pem = pbm->GetSolid();\n    FEElasticMixture* psm = dynamic_cast<FEElasticMixture*>(pem);\n    if (psm == nullptr) return true;    // in case material is not a solid mixture\n\n    for (int i=0; i<psm->Materials(); ++i) {\n        pem = psm->GetMaterial(i);\n        // check the types of materials that can employ this reaction rate\n        FERemodelingInterface* pri = dynamic_cast<FERemodelingInterface*>(pem);\n        if (pri) {\n            if (sbm == pri->m_sbm) m_comp = pri->m_comp;\n        }\n    }\n    if (m_comp == -1) return false;\n    \n    // get the fiber property (this remodeling rule only works with fibrous materials)\n    m_fiber = dynamic_cast<FEVec3dValuator*>(psm->GetMaterial(m_comp)->GetProperty(\"fiber\"));\n    if (m_fiber == nullptr) return false;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! reaction rate at material point\ndouble FEReactionRateRuberti::ReactionRate(FEMaterialPoint& pt)\n{\n    FEMultiphasic* pbm = dynamic_cast<FEMultiphasic*>(GetAncestor());\n    FEBiphasicInterface* pbi = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    double phir = pbm->SolidReferentialVolumeFraction(pt);\n    double c = psi->SBMConcentration(pt, m_lsbm);\n\n    mat3d Q = pbm->GetLocalCS(pt);\n    vec3d a0 = m_fiber->unitVector(pt);\n    vec3d ar = Q * a0;\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    double lam = (et.m_F*ar).norm();\n    double k = (lam > 1.0) ? m_kd(pt)*exp(-0.5*pow((lam-1)/m_sig(pt),2)) : m_kd(pt);\n    \n\treturn k;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with strain at material point\nmat3ds FEReactionRateRuberti::Tangent_ReactionRate_Strain(FEMaterialPoint& pt)\n{\n    FEMultiphasic* pbm = dynamic_cast<FEMultiphasic*>(GetAncestor());\n    FEBiphasicInterface* pbi = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    double phir = pbm->SolidReferentialVolumeFraction(pt);\n    double c = psi->SBMConcentration(pt, m_lsbm);\n    \n    mat3d Q = pbm->GetLocalCS(pt);\n    vec3d a0 = m_fiber->unitVector(pt);\n    vec3d ar = Q * a0;\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    double lam = (et.m_F*ar).norm();\n    double kd = m_kd(pt);\n    double sig = m_sig(pt);\n    double k = (lam > 1.0) ? kd*exp(-0.5*pow(lam-1,2)/sig) : kd;\n    double dkdlam = (lam > 1.0) ? -(lam-1)/sig*kd*exp(-0.5*pow((lam-1)/sig,2)) : 0;\n\n    mat3ds dzhatde = dyad(et.m_F*ar)*(dkdlam/lam/et.m_J);\n\treturn dzhatde;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with effective fluid pressure at material point\ndouble FEReactionRateRuberti::Tangent_ReactionRate_Pressure(FEMaterialPoint& pt)\n{\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEReactionRateRuberti.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n#include <FECore/FEVec3dValuator.h>\n\nclass FEBIOMIX_API FEReactionRateRuberti : public FEReactionRate\n{\npublic:\n\t//! constructor\n    FEReactionRateRuberti(FEModel* pfem);\n\t\n    //! initialization\n    bool Init() override;\n    \n\t//! reaction rate at material point\n\tdouble ReactionRate(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with strain at material point\n\tmat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with effective fluid pressure at material point\n\tdouble Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override;\n\t\npublic:\n\tFEParamDouble   m_kd;\t\t\t\t\t//!< maximum sbm degradation rate\n    FEParamDouble   m_sig;\t\t\t\t\t//!< stretch standard deviation\n\nprivate:\n    int             m_comp;                 //!< component of solid mixture (if applicable)\n    FEVec3dValuator*    m_fiber;            //!< fiber material\n    double          m_M;                    //!< molar mass of sbm\n    int             m_lsbm;                 //!< local sbm value\n\n\tDECLARE_FECORE_CLASS();\t\n};\n"
  },
  {
    "path": "FEBioMix/FEReactionRateSoluteAsSBM.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEReactionRateSoluteAsSBM.h\"\n#include \"FEBiphasic.h\"\n#include \"FEBioMech/FERemodelingElasticMaterial.h\"\n\n// Material parameters for the FEMultiphasic material\nBEGIN_FECORE_CLASS(FEReactionRateSoluteAsSBM, FEReactionRate)\n\tADD_PARAMETER(m_k0, \"k0\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEReactionRateSoluteAsSBM::FEReactionRateSoluteAsSBM(FEModel* pfem) : FEReactionRate(pfem)\n{\n    m_k0 = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! reaction rate at material point\ndouble FEReactionRateSoluteAsSBM::ReactionRate(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    double phisr = pbm->SolidReferentialVolumeFraction(pt);\n\t\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    double J = et.m_J;\n\tdouble zhat = m_k0(pt)/(J-phisr);\n\treturn zhat;\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with strain at material point\nmat3ds FEReactionRateSoluteAsSBM::Tangent_ReactionRate_Strain(FEMaterialPoint& pt)\n{\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n    double phisr = pbm->SolidReferentialVolumeFraction(pt);\n    double dphisrdJ = pbm->TangentSRVFStrain(pt);\n\n    FEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n    \n    double J = et.m_J;\n    double dzhatdJ = -(1-dphisrdJ)*m_k0(pt)/pow(J-phisr,2);\n\treturn mat3dd(dzhatdJ);\n}\n\n//-----------------------------------------------------------------------------\n//! tangent of reaction rate with effective fluid pressure at material point\ndouble FEReactionRateSoluteAsSBM::Tangent_ReactionRate_Pressure(FEMaterialPoint& pt)\n{\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FEReactionRateSoluteAsSBM.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEChemicalReaction.h\"\n\nclass FEBIOMIX_API FEReactionRateSoluteAsSBM : public FEReactionRate\n{\npublic:\n\t//! constructor\n    FEReactionRateSoluteAsSBM(FEModel* pfem);\n\t\n\t//! reaction rate at material point\n\tdouble ReactionRate(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with strain at material point\n\tmat3ds Tangent_ReactionRate_Strain(FEMaterialPoint& pt) override;\n\t\n\t//! tangent of reaction rate with effective fluid pressure at material point\n\tdouble Tangent_ReactionRate_Pressure(FEMaterialPoint& pt) override;\n\t\npublic:\n\tFEParamDouble   m_k0;\t\t\t\t\t//!< reaction rate constant\n\t\n\tDECLARE_FECORE_CLASS();\t\n};\n"
  },
  {
    "path": "FEBioMix/FERemodelingSolid.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERemodelingSolid.h\"\n#include <FECore/log.h>\n#include <FEBioMech/FEElasticMixture.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FERemodelingSolid, FEElasticMaterial)\n\tADD_PARAMETER(m_sbm , \"sbm\")->setEnums(\"$(sbms)\");\n    ADD_PROPERTY (m_pMat, \"solid\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FERemodelingSolid::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\t\n\t// get the parent material which must be a multiphasic material\n    m_pMP = GetAncestor()->ExtractProperty<FEMultiphasic>();\n\tif (m_pMP == 0) {\n\t\tfeLogError(\"Parent material must be multiphasic\");\n\t\treturn false;\n\t}\n\n\t// extract the local id of the SBM whose density controls Young's modulus from the global id\n\tm_lsbm = m_pMP->FindLocalSBMID(m_sbm);\n\tif (m_lsbm == -1) {\n\t\tfeLogError(\"Invalid value for sbm\");\n\t\treturn false;\n\t}\n\n    FEElasticMaterial* pem = m_pMP->GetSolid();\n    FEElasticMixture* psm = dynamic_cast<FEElasticMixture*>(pem);\n    if (psm == nullptr) {\n        m_comp = -1;    // in case material is not a solid mixture\n        return true;\n    }\n    \n    for (int i=0; i<psm->Materials(); ++i) {\n        pem = psm->GetMaterial(i);\n        if (pem == this) {\n            m_comp = i;\n            break;\n        }\n    }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERemodelingSolid::Serialize(DumpStream& ar)\n{\n\tFEElasticMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_lsbm;\n}\n\n//-----------------------------------------------------------------------------\n//! Create material point data\nFEMaterialPointData* FERemodelingSolid::CreateMaterialPointData()\n{\n\treturn new FERemodelingMaterialPoint(new FEElasticMaterialPoint);\n}\n\n//-----------------------------------------------------------------------------\n//! update specialize material point data\nvoid FERemodelingSolid::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    FESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n    FERemodelingMaterialPoint* rpt = mp.ExtractData<FERemodelingMaterialPoint>();\n    rpt->m_rhor = spt.m_sbmr[m_lsbm];\n    rpt->m_rhorp = spt.m_sbmrp[m_lsbm];\n    rpt->m_sed = StrainEnergyDensity(mp);\n}\n\n//-----------------------------------------------------------------------------\ndouble FERemodelingSolid::StrainEnergy(FEMaterialPoint& mp)\n{\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n    double rhor = spt.m_sbmr[m_lsbm];\n    double rho0 = m_pMP->SBMDensity(m_lsbm);\n    double w = rhor/rho0;\n    double sed = m_pMat->StrainEnergyDensity(mp)*w;\n\t\n\treturn sed;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FERemodelingSolid::Stress(FEMaterialPoint& mp)\n{\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n    double rhor = spt.m_sbmr[m_lsbm];\n    double rho0 = m_pMP->SBMDensity(m_lsbm);\n    double w = rhor/rho0;\n    mat3ds s = m_pMat->Stress(mp)*w;\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FERemodelingSolid::Tangent(FEMaterialPoint& mp)\n{\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n    double rhor = spt.m_sbmr[m_lsbm];\n    double rho0 = m_pMP->SBMDensity(m_lsbm);\n    double w = rhor/rho0;\n    tens4ds c = m_pMat->Tangent(mp)*w;\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! evaluate referential mass density\ndouble FERemodelingSolid::Density(FEMaterialPoint& pt)\n{\n    FERemodelingMaterialPoint* rpt = pt.ExtractData<FERemodelingMaterialPoint>();\n    if (rpt) return rpt->m_rhor;\n    else {\n        FEElasticMixtureMaterialPoint* emp = pt.ExtractData<FEElasticMixtureMaterialPoint>();\n        if (emp) {\n            rpt = emp->GetPointData(m_comp)->ExtractData<FERemodelingMaterialPoint>();\n            if (rpt) return rpt->m_rhor;\n        }\n    }\n    return 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of strain energy density with mass density\ndouble FERemodelingSolid::Tangent_SE_Density(FEMaterialPoint& mp)\n{\n    double rho0 = m_pMP->SBMDensity(m_lsbm);\n    return m_pMat->StrainEnergyDensity(mp)/rho0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate tangent of stress with mass density\nmat3ds FERemodelingSolid::Tangent_Stress_Density(FEMaterialPoint& mp)\n{\n    double rho0 = m_pMP->SBMDensity(m_lsbm);\n    return m_pMat->Stress(mp)/rho0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FERemodelingSolid.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include \"FEBioMech/FERemodelingElasticMaterial.h\"\n#include \"FEMultiphasic.h\"\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This is a container material for an elastic material associated with a SBM. Its strain energy density, stress\n//! and elasticity are scaled by the mass fraction of the SBM, relative to the SBM's true density.\n\nclass FEBIOMIX_API FERemodelingSolid : public FEElasticMaterial, public FERemodelingInterface\n{\npublic:\n    FERemodelingSolid(FEModel* pfem) : FEElasticMaterial(pfem) { m_sbm = -1; m_lsbm = -1; m_pMat = nullptr; m_pMP = nullptr; }\n\t\nprotected:\n\tint\t\tm_lsbm;\t//!< local id of solid-bound molecule\n\npublic:\n\t//! data initialization and checking\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n    //! evaluate referential mass density\n    double Density(FEMaterialPoint& pt) override;\n    \n\t//! Create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\t\n    //! calculate strain energy density at material point\n    double StrainEnergyDensity(FEMaterialPoint& pt) override { return StrainEnergy(pt); }\n    \n    //! update specialize material point data\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n    \npublic: // --- remodeling interface ---\n\n\t//! calculate strain energy density at material point\n\tdouble StrainEnergy(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent of strain energy density with mass density\n\tdouble Tangent_SE_Density(FEMaterialPoint& pt) override;\n\t\n\t//! calculate tangent of stress with mass density\n\tmat3ds Tangent_Stress_Density(FEMaterialPoint& pt) override;\n\npublic:\n    FEElasticMaterial*  m_pMat; //!< elastic material which obeys simple remodeling rule\n\nprivate:\n    FEMultiphasic*      m_pMP;  //!< multiphasic domain containing this material\n\n    // declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESBMPointSource.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESBMPointSource.h\"\n#include \"FEMultiphasic.h\"\n#include \"FESolute.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEElemElemList.h>\n#include <algorithm>\n#include <FEBioMech/FEElasticMaterialPoint.h>\n#include <iostream>\n#include <unordered_set>\n#include <unordered_map>\n#include <limits>\n#include <FECore/FEAnalysis.h>\n\nBEGIN_FECORE_CLASS(FESBMPointSource, FEBodyLoad)\n\tADD_PARAMETER(m_sbmId, \"sbm\");\n\tADD_PARAMETER(m_rate, \"rate\");\n\tADD_PARAMETER(m_pos.x, \"x\");\n\tADD_PARAMETER(m_pos.y, \"y\");\n\tADD_PARAMETER(m_pos.z, \"z\");\n\tADD_PARAMETER(m_weighVolume, \"weigh_volume\");\nEND_FECORE_CLASS();\n\nFESBMPointSource::FESBMPointSource(FEModel* fem) : FEBodyLoad(fem), m_search(fem ? &fem->GetMesh() : nullptr)\n{\n\t//static bool bfirst = true;\n\tm_sbmId = -1;\n\tm_pos = vec3d(0, 0, 0);\n\tm_rate = 0.0;\n\t//m_reset = bfirst;\n\t//m_doReset = true;\n\tm_weighVolume = true;\n\t//bfirst = false;\n}\n\nvec3d FESBMPointSource::GetPosition() const\n{\n\treturn m_pos;\n}\n\nvoid FESBMPointSource::SetPosition(const vec3d& pos)\n{\n\tm_pos = pos;\n}\n\nint FESBMPointSource::GetSBMID() const\n{\n\treturn m_sbmId;\n}\n\n\nvoid FESBMPointSource::SetSBMID(int sbmID)\n{\n\tm_sbmId = sbmID;\n}\n\ndouble FESBMPointSource::GetRate() const\n{\n\treturn m_rate;\n}\n\nvoid FESBMPointSource::SetRate(double rate)\n{\n\tm_rate = rate;\n}\n\ndouble FESBMPointSource::GetdC() const\n{\n\treturn m_dC;\n}\n\ndouble FESBMPointSource::GetdCp() const\n{\n\treturn m_dCp;\n}\n\nvoid FESBMPointSource::SetdC(double dC)\n{\n\tm_dC = dC;\n}\n\n\nvoid FESBMPointSource::SetRadius(double radius)\n{\n\tm_radius = radius;\n\tm_Vc = (4.0 / 3.0) * PI * pow(m_radius, 3.0);\n}\n\nvoid FESBMPointSource::SetAccumulateFlag(bool b)\n{\n\tm_accumulate = b;\n}\n\n//void FESBMPointSource::SetWeighVolume(bool b)\n//{\n//\tm_weighVolume = b;\n//}\n\n//void FESBMPointSource::SetResetFlag(bool b)\n//{\n//\tm_doReset = b;\n//}\n\n\n\nbool FESBMPointSource::Init()\n{\n\tif (m_sbmId == -1) return false;\n\tif (m_search.Init() == false) return false;\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\treturn FEBodyLoad::Init();\n}\n\n// allow species to accumulate at the point source\nvoid FESBMPointSource::Accumulate(double dc) {\n\t// find the element in which the point lies\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tif (m_el == nullptr) return;\n\n\t// make sure this element is part of a multiphasic domain\n\tFEDomain* dom = dynamic_cast<FEDomain*>(m_el->GetMeshPartition());\n\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\tif (mat == nullptr) return;\n\n\t// Make sure the material has the correct solute\n\tint sbmid = -1;\n\tint sbms = mat->SBMs();\n\tfor (int j = 0; j < sbms; ++j)\n\t{\n\t\tint sbmj = mat->GetSBM(j)->GetSBMID();\n\t\tif (sbmj == m_sbmId)\n\t\t{\n\t\t\tsbmid = j;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (sbmid == -1) return;\n\n\tm_rate = dc + m_rate;\n\tm_accumulate = true;\n}\n\nvoid FESBMPointSource::Update()\n{\n\t//if (m_reset && m_doReset) ResetSBM();\n\tif (m_accumulate) {\n\t\tm_dCp = m_dC;\n\t\t// find the element in which the point lies\n\t\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\t\tif (m_el == nullptr) return;\n\n\t\t// make sure this element is part of a multiphasic domain\n\t\tFEDomain* dom = dynamic_cast<FEDomain*>(m_el->GetMeshPartition());\n\t\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\t\tif (mat == nullptr) return;\n\n\t\t// calculate the element volume\n\t\tFEMesh* mesh = dom->GetMesh();\n\t\tdouble Ve = mesh->ElementVolume(*m_el);\n\n\t\t// we prescribe the element average to the integration points\n\t\tconst int nint = m_el->GaussPoints();\n\t\t// Make sure the material has the correct sbm\n\t\tint sbmid = -1;\n\t\tint sbms = mat->SBMs();\n\t\tfor (int j = 0; j < sbms; ++j)\n\t\t{\n\t\t\tint sbmj = mat->GetSBM(j)->GetSBMID();\n\t\t\tif (sbmj == m_sbmId)\n\t\t\t{\n\t\t\t\tsbmid = j;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (sbmid == -1) return;\n\t\t\n\t\tdouble m_dt = mesh->GetFEModel()->GetCurrentStep()->m_dt;\n\t\tstd::vector<FEMaterialPoint*> possible_ints;\n\t\tdouble total_elem = 0;\n\t\tFindIntInRadius(possible_ints, total_elem);\n\t\tdouble total_change = 0.0;\n\t\t// if the cell is not projecting on top of an integration point (i.e. too small) \n\t\t// then project it's concentration to the nodes via the shape functions\n\t\t//SL TODO: Currently assigns just to the current element. Should instead find the ~8 closest integration points\n\t\t//\t\t   regardless of element.\n\t\tif (possible_ints.size() == 0)\n\t\t{\n\t\t\tFindNodesInRadius(possible_ints, total_elem);\n\t\t}\n\t\tif (possible_ints.size() == 0) \n\t\t{\n\t\t\t// set the concentration of all the integration points\n\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\tm_el->shape_fnc(H, m_q[0], m_q[1], m_q[2]);\n\t\t\tfor (int i = 0; i < nint; ++i)\n\t\t\t{\n\t\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\t\tm_el->shape_fnc(H, m_q[0], m_q[1], m_q[2]);\n\t\t\t\tFEMaterialPoint& mp = *m_el->GetMaterialPoint(i);\n\t\t\t\tFESolutesMaterialPoint& pd = *(mp.ExtractData<FESolutesMaterialPoint>());\n\t\t\t\tdouble new_r = H[i] * m_rate * nint / Ve + pd.m_sbmr[sbmid];\n\t\t\t\tpd.m_sbmr[sbmid] = new_r;\n\t\t\t\tpd.m_sbmrp[sbmid] = pd.m_sbmr[sbmid];\n\t\t\t\ttotal_change += m_rate / nint;\n\t\t\t}\n\t\t}\n\t\t// else evenly distribute it among the integration points that the cell is on top of.\n\t\t//SL TODO: currently may lead to a little bias when neighboring elements are smaller resulting in\n\t\t//\t\t   higher density of integration points\n\t\telse {\n\t\t\tint nint_in = possible_ints.size();\n\t\t\tfor (auto iter = possible_ints.begin(); iter != possible_ints.end(); ++iter)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = **iter;\n\t\t\t\tFESolutesMaterialPoint& pd = *(mp.ExtractData<FESolutesMaterialPoint>());\n\t\t\t\t// scale by H so that the total integral over each element is consistent.\n\t\t\t\tdouble H = double(nint) / double(nint_in);\n\t\t\t\tdouble new_r = H * m_rate / Ve + pd.m_sbmr[sbmid];\n\t\t\t\tpd.m_sbmr[sbmid] = new_r;\n\t\t\t\tpd.m_sbmrp[sbmid] = pd.m_sbmr[sbmid];\n\t\t\t\ttotal_change += m_rate / (nint_in);\n\t\t\t}\n\t\t}\t\n\t\tm_dC = -total_change / m_Vc;\n\t\tm_accumulate = false; // don't double count a point source\n\t}\n}\n\nvoid FESBMPointSource::FindIntInRadius(std::vector<FEMaterialPoint*> &possible_ints, double &total_elem) {\n\t\n\t// get element and set up buffers\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tstd::unordered_set<FESolidElement*> visited;\n\tstd::set<FESolidElement*> next;\n\t//std::vector<FEMaterialPoint*> possible_ints;\n\tvisited.reserve(1000);\n\tpossible_ints.reserve(500);\n\n\t//we will need to check the current element first\n\tnext.insert(m_el);\n\t// create the element adjacency list.\n\tFEDomain* dom = dynamic_cast<FEDomain*>(m_el->GetMeshPartition());\n\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\tif (mat == nullptr) return;\n\t// calculate the element volume\n\tauto mesh = dom->GetMesh();\n\t// create the element-element list\n\tFEElemElemList EEL;\n\tEEL.Create(mesh);\n\n\t//while there are still elements to evaluate\n\twhile (next.size()) {\n\t\t// get the element to be evaluated\n\t\tFESolidElement* cur = *next.begin();\n\t\t// remove the current element from the next buffer and add it to the visited buffer\n\t\tnext.erase(next.begin());\n\t\tvisited.insert(cur);\n\t\t// get the current element bounds\n\t\tstd::vector<vec3d> cur_element_bounds;\n\t\t// add integration points within the radius\n\t\tbool int_flag = false;\n\t\tfor (int i = 0; i < cur->GaussPoints(); i++)\n\t\t{\n\t\t\tFEMaterialPoint* mp = cur->GetMaterialPoint(i);\n\t\t\tvec3d disp = mp->m_r0 - m_pos;\n\t\t\tif (disp.norm() <= m_radius) {\n\t\t\t\tpossible_ints.push_back(mp);\n\t\t\t\tint_flag = true;\n\t\t\t}\n\t\t}\n\t\tif (int_flag) \n\t\t{\n\t\t\ttotal_elem++;\n\t\t}\n\n\t\t// Add neighboring element to the next buffer as long as they haven't been visited.\n\t\t// get the global ID of the current element\n\t\tint cur_id = cur->GetID()-1;\n\t\t// for each neighboring element\n\t\tfor (int i = 0; i < EEL.NeighborSize(); i++)\n\t\t{\n\t\t\tif (EEL.Neighbor(cur_id, i))\n\t\t\t{\n\t\t\t\t// if that element has not been visited yet add it to the next list\n\t\t\t\tif (!visited.count(dynamic_cast<FESolidElement*>(EEL.Neighbor(cur_id, i))))\n\t\t\t\t{\n\t\t\t\t\tnext.insert(dynamic_cast<FESolidElement*>(EEL.Neighbor(cur_id, i)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FESBMPointSource::FindNodesInRadius(std::vector<FEMaterialPoint*>& possible_ints, double& total_elem) {\n\n\t// get element and set up buffers\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tstd::unordered_set<FESolidElement*> visited;\n\tstd::set<FESolidElement*> next;\n\t//std::vector<FEMaterialPoint*> possible_ints;\n\tvisited.reserve(1000);\n\tstd::vector<FENode*> possible_nodes;\n\tpossible_nodes.reserve(500);\n\n\t//we will need to check the current element first\n\tnext.insert(m_el);\n\t// create the element adjacency list.\n\tFEDomain* dom = dynamic_cast<FEDomain*>(m_el->GetMeshPartition());\n\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\tif (mat == nullptr) return;\n\t// calculate the element volume\n\tauto mesh = dom->GetMesh();\n\t// create the element-element list\n\tFEElemElemList EEL;\n\tEEL.Create(mesh);\n\n\t//while there are still elements to evaluate\n\twhile (next.size()) {\n\t\t// get the element to be evaluated\n\t\tFESolidElement* cur = *next.begin();\n\t\t// remove the current element from the next buffer and add it to the visited buffer\n\t\tnext.erase(next.begin());\n\t\tvisited.insert(cur);\n\t\t// get the current element bounds\n\t\tstd::vector<vec3d> cur_element_bounds;\n\t\t// add integration points within the radius\n\t\tbool int_flag = false;\n\t\tfor (int i = 0; i < cur->Nodes(); i++)\n\t\t{\n\t\t\tFENode* mn = &(mesh->Node(cur->m_node[i]));\n\t\t\tvec3d disp = mn->m_rt - m_pos;\n\t\t\tFEMaterialPoint* closest;\n\t\t\tif (disp.norm() <= m_radius) {\n\t\t\t\t// find the closest mp in the element\n\t\t\t\tdouble min_d = std::numeric_limits<double>::max();\n\t\t\t\tfor (int j = 0; j < cur->GaussPoints(); j++)\n\t\t\t\t{\n\t\t\t\t\tdouble disp2 = (mn->m_rt - cur->GetMaterialPoint(j)->m_rt).norm();\n\t\t\t\t\tif (disp2 < min_d)\n\t\t\t\t\t{\n\t\t\t\t\t\tmin_d = disp2;\n\t\t\t\t\t\tclosest = cur->GetMaterialPoint(j);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpossible_ints.push_back(closest);\n\t\t\t\tint_flag = true;\n\t\t\t}\n\t\t}\n\t\tif (int_flag)\n\t\t{\n\t\t\ttotal_elem++;\n\t\t}\n\n\t\t// Add neighboring element to the next buffer as long as they haven't been visited.\n\t\t// get the global ID of the current element\n\t\tint cur_id = cur->GetID() - 1;\n\t\t// for each neighboring element\n\t\tfor (int i = 0; i < EEL.NeighborSize(); i++)\n\t\t{\n\t\t\tif (EEL.Neighbor(cur_id, i))\n\t\t\t{\n\t\t\t\t// if that element has not been visited yet add it to the next list\n\t\t\t\tif (!visited.count(dynamic_cast<FESolidElement*>(EEL.Neighbor(cur_id, i))))\n\t\t\t\t{\n\t\t\t\t\tnext.insert(dynamic_cast<FESolidElement*>(EEL.Neighbor(cur_id, i)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//void FESBMPointSource::ResetSBM()\n//{\n//\tFEModel& fem = *GetFEModel();\n//\tFEMesh& mesh = fem.GetMesh();\n//\n//\tfor (int i = 0; i < mesh.Domains(); ++i)\n//\t{\n//\t\tFEDomain& dom = mesh.Domain(i);\n//\t\tint NE = dom.Elements();\n//\n//\t\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom.GetMaterial());\n//\t\tif (mat)\n//\t\t{\n//\t\t\t// Make sure the material has the correct sbm\n//\t\t\tint sbmid = -1;\n//\t\t\tint sbms = mat->SBMs();\n//\t\t\tfor (int j = 0; j<sbms; ++j)\n//\t\t\t{\n//\t\t\t\tint sbmj = mat->GetSBM(j)->GetSBMID();\n//\t\t\t\tif (sbmj == m_sbmId)\n//\t\t\t\t{\n//\t\t\t\t\tsbmid = j;\n//\t\t\t\t\tbreak;\n//\t\t\t\t}\n//\t\t\t}\n//\n//\t\t\tif (sbmid != -1)\n//\t\t\t{\n//\t\t\t\tfor (int j = 0; j < NE; ++j)\n//\t\t\t\t{\n//\t\t\t\t\tFEElement& el = dom.ElementRef(j);\n//\n//\t\t\t\t\t// set the concentration of all the integration points\n//\t\t\t\t\tint nint = el.GaussPoints();\n//\t\t\t\t\tfor (int k = 0; k < nint; ++k)\n//\t\t\t\t\t{\n//\t\t\t\t\t\tFEMaterialPoint* mp = el.GetMaterialPoint(k);\n//\t\t\t\t\t\tFESolutesMaterialPoint& pd = *(mp->ExtractData<FESolutesMaterialPoint>());\n//\t\t\t\t\t\tpd.m_sbmr[sbmid] = 0.0;\n//\t\t\t\t\t\tpd.m_sbmrp[sbmid] = 0.0;\n//\t\t\t\t\t}\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\n//\t}\n//}\n"
  },
  {
    "path": "FEBioMix/FESBMPointSource.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEBodyLoad.h>\n#include <FECore/FEOctreeSearch.h>\n#include <unordered_map>\n#include \"febiomix_api.h\"\n\nclass FESolidElement;\n\nclass FEBIOMIX_API FESBMPointSource : public FEBodyLoad\n{\npublic:\n\tFESBMPointSource(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Update() override;\n\n\tvoid Accumulate(double dc);\n\n\tvoid SetPosition(const vec3d& pos);\n\n\tvec3d GetPosition() const;\n\n\tvoid SetSBMID(int id);\n\n\tint GetSBMID() const;\n\n\tvoid SetRate(double rate);\n\n\tvoid SetRadius(double radius);\n\n\tdouble GetRate() const;\n\n\tdouble GetdC() const;\n\n\tdouble GetdCp() const;\n\n\tvoid SetdC(double dC);\n\n\tvoid SetWeighVolume(bool b);\n\n\tvoid SetResetFlag(bool b);\n\n\tvoid SetAccumulateFlag(bool b);\n\n\t//std::vector<FEMaterialPoint*> FindIntInRadius();\n\tvoid FindIntInRadius(std::vector<FEMaterialPoint*> &possible_ints, double &total_elem);\n\n\t//! return all the elements in the given radius\n\tvoid FindNodesInRadius(std::vector<FEMaterialPoint*>& possible_ints, double& total_elem);\n\nprivate:\n\t//void ResetSBM();\n\nprivate:\n\tint\t\tm_sbmId;\t// The SBM ID that defins the cell's \"concentration\"\n\tvec3d\tm_pos;\t// the position (in reference coordinates)\n\tdouble\tm_rate;\t// density value at point source\n\tdouble\tm_radius;\n\tdouble\tm_Vc;\n\tbool\tm_reset;\n\tbool\tm_doReset;\n\tbool\tm_weighVolume;\n\tbool\tm_accumulate;\t// accumulate species flag for the update\n\tdouble\tm_dC = 0.0;\t\t// total change of a species\n\tdouble\tm_dCp = 0.0;\n\nprivate:\n\tFEOctreeSearch\t\tm_search;\n\tFESolidElement*\t\tm_el;\n\tdouble\t\t\t\tm_q[3];\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESFDSBM.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESFDSBM.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n\n// The following file contains the integration points and weights\n// for the integration over a unit sphere in spherical coordinates\n#include \"FEBioMech/geodesic.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n// we store the cos and sin of the angles here\nint FESFDSBM::m_nres = 0;\ndouble FESFDSBM::m_cth[NSTH];\ndouble FESFDSBM::m_sth[NSTH];\ndouble FESFDSBM::m_cph[NSTH];\ndouble FESFDSBM::m_sph[NSTH];\ndouble FESFDSBM::m_w[NSTH];\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESFDSBM, FEElasticMaterial)\n\tADD_PARAMETER(m_alpha, FE_RANGE_GREATER_OR_EQUAL(0.0), \"alpha\");\n\tADD_PARAMETER(m_beta , FE_RANGE_GREATER_OR_EQUAL(2.0), \"beta\");\n\tADD_PARAMETER(m_ksi0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi0\" );\n\tADD_PARAMETER(m_rho0 , FE_RANGE_GREATER_OR_EQUAL(0.0), \"rho0\" );\n\tADD_PARAMETER(m_g    , FE_RANGE_GREATER_OR_EQUAL(0.0), \"gamma\");\n    ADD_PARAMETER(m_sbm , \"sbm\")->setEnums(\"$(sbms)\");\n\n\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FESphericalFiberDistribution\n//-----------------------------------------------------------------------------\n\nbool FESFDSBM::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\t// get the parent material which must be a multiphasic material\n\tFEMultiphasic* pMP = dynamic_cast<FEMultiphasic*> (GetAncestor());\n\tif (pMP == 0) {\n\t\tfeLogError(\"Parent material must be multiphasic\");\n\t\treturn false;\n\t}\n    \n\t// extract the local id of the SBM whose density controls Young's modulus from the global id\n\tm_lsbm = pMP->FindLocalSBMID(m_sbm);\n\tif (m_lsbm == -1) {\n\t\tfeLogError(\"Invalid value for sbm\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FESFDSBM::Stress(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wl;\n\tconst double eps = 0;\n\tmat3ds s;\n\ts.zero();\n\t\n    // calculate material coefficients\n    double alpha = m_alpha;\n    double beta = m_beta;\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n    double ksi = FiberModulus(rhor);\n    \n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\t\t\n\t\t// --- quadrant 1,1,1 ---\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,1,1 ---\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,-1,1 ---\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant 1,-1,1 ---\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tWl = beta*ksi*pow(In - 1.0, beta-1.0)*exp(alpha*pow(In - 1.0, beta));\n\t\t\t\n\t\t\t// calculate the stress\n\t\t\ts += dyad(nt)*(Wl*wn);\n\t\t}\n\t}\n\t\n\t// we multiply by two to add contribution from other half-sphere\n\treturn s*(4.0/J);\n}\n\n//-----------------------------------------------------------------------------\ntens4ds FESFDSBM::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, Wll;\n\tconst double eps = 0;\n\ttens4ds cf, cfw; cf.zero();\n\tmat3ds N2;\n\ttens4ds N4;\n\ttens4ds c;\n\tc.zero();\n\t\n    // calculate material coefficients\n    double alpha = m_alpha;\n    double beta = m_beta;\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n    double ksi = FiberModulus(rhor);\n    \n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\t\t\n\t\t// --- quadrant 1,1,1 ---\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,1,1 ---\n\t\t\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant -1,-1,1 ---\n\t\t\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t\t\n\t\t// --- quadrant 1,-1,1 ---\n\t\t\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy derivative\n\t\t\tdouble pIn = alpha*pow(In - 1.0,beta);\n\t\t\tWll = beta*ksi*pow(In - 1.0, beta-2.0)*(beta*pIn+beta-1.0)*exp(pIn);\n\t\t\t\n\t\t\tN2 = dyad(nt);\n\t\t\tN4 = dyad1s(N2);\n\t\t\t\n\t\t\tc += N4*(Wll*wn);\n\t\t}\n\t}\n\t\n\t// multiply by two to integrate over other half of sphere\n\treturn c*(2.0*4.0/J);\n}\n\n//-----------------------------------------------------------------------------\ndouble FESFDSBM::StrainEnergyDensity(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// deformation gradient\n\tmat3d &F = pt.m_F;\n\t\n\t// get the local coordinate systems\n\tmat3d Q = GetLocalCS(mp);\n\n\t// loop over all integration points\n\tvec3d n0e, n0a, n0q, nt;\n\tdouble In, W;\n\tconst double eps = 0;\n\tdouble sed = 0.0;\n\t\n    // calculate material coefficients\n    double alpha = m_alpha;\n    double beta = m_beta;\n\tdouble rhor = spt.m_sbmr[m_lsbm];\n    double ksi = FiberModulus(rhor);\n    \n\tconst int nint = 45;\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// set the global fiber direction in material coordinate system\n\t\tn0a.x = XYZ2[n][0];\n\t\tn0a.y = XYZ2[n][1];\n\t\tn0a.z = XYZ2[n][2];\n\t\tdouble wn = XYZ2[n][3];\n\t\t\n\t\t// --- quadrant 1,1,1 ---\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0a;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (m_alpha > 0)\n                W = ksi/m_alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t\t\n\t\t// --- quadrant -1,1,1 ---\n\t\tn0q = vec3d(-n0a.x, n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (m_alpha > 0)\n                W = ksi/m_alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t\t\n\t\t// --- quadrant -1,-1,1 ---\n\t\tn0q = vec3d(-n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (m_alpha > 0)\n                W = ksi/m_alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t\t\n\t\t// --- quadrant 1,-1,1 ---\n\t\tn0q = vec3d(n0a.x, -n0a.y, n0a.z);\n\t\t\n\t\t// rotate to reference configuration\n\t\tn0e = Q*n0q;\n\t\t\n\t\t// get the global spatial fiber direction in current configuration\n\t\tnt = F*n0e;\n\t\t\n\t\t// Calculate In = n0e*C*n0e\n\t\tIn = nt*nt;\n\t\t\n\t\t// only take fibers in tension into consideration\n\t\tif (In > 1. + eps)\n\t\t{\n\t\t\t// calculate strain energy density\n            if (m_alpha > 0)\n                W = ksi/m_alpha*(exp(alpha*pow(In - 1.0, beta))-1);\n            else\n                W = ksi*pow(In - 1.0, beta);\n\t\t\t\n\t\t\t// add to total sed\n\t\t\tsed += W*wn;\n\t\t}\n\t}\n\t\n\t// we multiply by two to add contribution from other half-sphere\n\treturn sed*2.0;\n}\n"
  },
  {
    "path": "FEBioMix/FESFDSBM.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEElasticMaterial.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Material class for the spherical fiber distribution with\n//! fiber modulus dependent on sbm referential density\n\nclass FEBIOMIX_API FESFDSBM : public FEElasticMaterial\n{\npublic:\n    FESFDSBM(FEModel* pfem) : FEElasticMaterial(pfem) { m_alpha = 0; m_sbm = -1; }\n\t\n\t//! Initialization\n\tbool Init() override;\n\t\n\t//! Cauchy stress\n\tmat3ds Stress(FEMaterialPoint& mp) override;\n\t\n\t// Spatial tangent\n\ttens4ds Tangent(FEMaterialPoint& mp) override;\n\t\n\t// Strain energy density\n\tdouble StrainEnergyDensity(FEMaterialPoint& mp) override;\n\t\n\t//! return fiber modulus\n\tdouble FiberModulus(double rhor) { return m_ksi0*pow(rhor/m_rho0, m_g);}\n\t\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n\t\npublic:\n\tdouble\tm_alpha;\t// coefficient of exponential argument\n\tdouble\tm_beta;\t\t// power in power-law relation\n\tdouble\tm_ksi0;\t\t// ksi = ksi0*(rhor/rho0)^gamma\n    double  m_rho0;     // rho0\n    double  m_g;        // gamma\n\tint\t\tm_sbm;      //!< global id of solid-bound molecule\n\tint\t\tm_lsbm;     //!< local id of solid-bound molecule\n\t\n\tstatic int\t\tm_nres;\t// integration rule\n\tstatic double\tm_cth[];\n\tstatic double\tm_sth[];\n\tstatic double\tm_cph[];\n\tstatic double\tm_sph[];\n\tstatic double\tm_w[];\n};\n"
  },
  {
    "path": "FEBioMix/FESlidingInterface2.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingInterface2.h\"\n#include \"FEBiphasic.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingInterface2, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n    ADD_PARAMETER(m_gtol     , \"gaptol\"             )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_ptol     , \"ptol\"               );\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_epsp     , \"pressure_penalty\"   );\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n    ADD_PARAMETER(m_srad     , \"search_radius\"      )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_nsegup   , \"seg_up\"             );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\n\tADD_PARAMETER(m_breloc   , \"node_reloc\"         );\n    ADD_PARAMETER(m_bsmaug   , \"smooth_aug\"         );\n    ADD_PARAMETER(m_bdupr    , \"dual_proj\"          );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FESlidingSurface2\n//-----------------------------------------------------------------------------\n\nFESlidingSurface2::FESlidingSurface2(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{ \n\tm_bporo = false;\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingSurface2::Init()\n{\n\t// initialize surface data first\n\tif (FEBiphasicContactSurface::Init() == false) return false;\n\n\t// allocate node normals and pressures\n\tm_nn.assign(Nodes(), vec3d(0,0,0));\n    m_pn.assign(Nodes(), 0);\n\n\t// determine biphasic status\n\tm_poro.resize(Elements(),false);\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& se = Element(i);\n\t\t\n\t\t// get the element this surface element belongs to\n\t\tFEElement* pe = se.m_elem[0].pe;\n\t\tif (pe)\n\t\t{\n\t\t\t// get the material\n\t\t\tFEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n\t\t\t\n\t\t\t// see if this is a poro-elastic element\n\t\t\tFEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n\t\t\tif (biph) {\n\t\t\t\tm_poro[i] = true;\n\t\t\t\tm_bporo = true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FESlidingSurface2::CreateMaterialPoint()\n{\n\treturn new FEBiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact pressures by averaging values from surrounding\n//! faces.  This function ensures that nodal contact pressures are always\n//! positive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurface2::EvaluateNodalContactPressures()\n{\n    const int N = Nodes();\n\n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact pressures\n    zero(m_pn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n\n        // get the average contact pressure for that face\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_pn[el.m_lnode[j]] += pn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_pn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the \n//! element normals at the node\n\nvoid FESlidingSurface2::UpdateNodeNormals()\n{\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d y[MN];\n\n\t// zero nodal normals\n\tzero(m_nn);\n\n\t// loop over all elements\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\n\t\t// get the nodal coordinates\n\t\tfor (int j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n\n\t\t// calculate the normals\n\t\tfor (int j=0; j<ne; ++j)\n\t\t{\n\t\t\tint jp1 = (j+1)%ne;\n\t\t\tint jm1 = (j+ne-1)%ne;\n\t\t\tvec3d n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n\t\t\tm_nn[el.m_lnode[j]] += n;\n\t\t}\n\t}\n\n\t// normalize all vectors\n\tconst int N = Nodes();\n\tfor (int i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface2::GetContactForce()\n{\n    return m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface2::GetContactForceFromElementStress()\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // initialize contact force\n    vec3d f(0,0,0);\n    \n    // loop over all elements of the surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        \n        mat3ds s(0,0,0,0,0,0);\n        for (int j=0; j<pe->GaussPoints(); ++j) {\n            // get a material point\n            FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n            FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n            s += pt.m_s;\n        }\n        s /= pe->GaussPoints();\n        double sp[3];\n        s.eigen2(sp);\n        \n        int nint = el.GaussPoints();\n        \n        // evaluate the contact force for that element\n        for (int i=0; i<nint; ++i)\n        {\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n            if (data.m_Ln > 0) {\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n                // normal (magnitude = area)\n                vec3d a = g[0] ^ g[1];\n                \n                // gauss weight\n                double w = el.GaussWeights()[i];\n                // contact force\n                f += a*(sp[0]*w);\n            }\n        }\n    }\n    \n    return f;\n    \n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurface2::GetContactArea()\n{\n\t// initialize contact area\n\tdouble area = 0;\n\t\n\t// loop over all elements of the primary surface\n\tfor (int n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// evaluate the contact force for that element\n\t\tfor (int i=0; i<nint; ++i)\n\t\t{\n\t\t\t// get data for this integration point\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\t\t\tif (data.m_Ln > 0)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tCoBaseVectors(el, i, g);\n            \n\t\t\t\t// normal (magnitude = area)\n\t\t\t\tvec3d a = g[0] ^ g[1];\n            \n\t\t\t\t// gauss weight\n\t\t\t\tdouble w = el.GaussWeights()[i];\n            \n\t\t\t\t// contact force\n\t\t\t\tarea += a.norm()*w;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn area;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface2::GetFluidForce()\n{\n    // initialize contact force\n    vec3d f(0,0,0);\n    if (m_dofP < 0) return f;\n    \n    // loop over all elements of the surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        \n        int nint = el.GaussPoints();\n        \n        // evaluate the fluid force for that element\n        for (int i=0; i<nint; ++i)\n        {\n            // get data for this integration point\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\t\t\tif (data.m_Ln > 0) {\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n                // normal (magnitude = area)\n                vec3d a = g[0] ^ g[1];\n                // gauss weight\n                double w = el.GaussWeights()[i];\n                // fluid pressure\n                double p = data.m_p1;\n                // contact force\n                f += a*(w*p);\n            }\n        }\n    }\n    \n    return f;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface2::GetFluidForceFromElementPressure()\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // initialize contact force\n    vec3d f(0,0,0);\n    if (m_dofP < 0) return f;\n    \n    // loop over all elements of the surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        \n        double p = 0;\n        for (int j=0; j<pe->GaussPoints(); ++j) {\n            // get a material point\n            FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            p += pt.m_p;\n        }\n        p /= pe->GaussPoints();\n        \n        int nint = el.GaussPoints();\n        \n        // evaluate the fluid force for that element\n        for (int i=0; i<nint; ++i)\n        {\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n            if (data.m_Ln > 0) {\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n                // normal (magnitude = area)\n                vec3d a = g[0] ^ g[1];\n                // gauss weight\n                double w = el.GaussWeights()[i];\n                // contact force\n                f += a*(w*p);\n            }\n        }\n    }\n    \n    return f;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurface2::GetFluidLoadSupport()\n{\n    double W = GetContactForceFromElementStress().norm();\n    double Wp = GetFluidForceFromElementPressure().norm();\n    if (W == 0) return 0;\n    return Wp/W;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface2::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_bporo;\n\tar & m_poro;\n\tar & m_nn;\n\tar & m_pn;\n\tar & m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface2::GetContactPressure(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_Ln;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface2::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpt -= data.m_nu*data.m_Ln;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface2::GetNodalContactPressure(int nface, double* pg)\n{\n\tFESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        pg[k] = m_pn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface2::GetNodalContactTraction(int nface, vec3d* pt)\n{\n\tFESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        pt[k] = m_nn[el.m_lnode[k]]*(-m_pn[el.m_lnode[k]]);\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingInterface2\n//-----------------------------------------------------------------------------\n\nFESlidingInterface2::FESlidingInterface2(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\t// initial values\n\tm_knmult = 1;\n\tm_atol = 0.1;\n\tm_epsn = 1;\n\tm_epsp = 1;\n\tm_btwo_pass = false;\n\tm_stol = 0.01;\n\tm_bsymm = true;\n\tm_srad = 1.0;\n\tm_gtol = 0;\n\tm_ptol = 0;\n\tm_nsegup = 0;\n\tm_bautopen = false;\n    m_bupdtpen = false;\n\tm_breloc = false;\n    m_bsmaug = false;\n    m_bdupr = true;\n\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n\tm_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFESlidingInterface2::~FESlidingInterface2()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterface2::Init()\n{\n\t// initialize surface data\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_P = fem.GetDOFIndex(\"p\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(7*FEElement::MAX_NODES*2);\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface2& ss = (np == 0? m_ss : m_ms);\n\n\t\tint k, l;\n\t\tfor (int j=0; j<ss.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\t\t\tint nint = se.GaussPoints();\n\t\t\tint* sn = &se.m_node[0];\n\t\t\tfor (k=0; k<nint; ++k)\n\t\t\t{\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(k));\n\t\t\t\tFESurfaceElement* pe = pt.m_pme;\n\t\t\t\tif (pe != 0)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\t\tint* mn = &me.m_node[0];\n\n\t\t\t\t\tassign(lm, -1);\n\n\t\t\t\t\tint nseln = se.Nodes();\n\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(sn[l]).m_ID;\n\t\t\t\t\t\tlm[7*l  ] = id[dof_X];\n\t\t\t\t\t\tlm[7*l+1] = id[dof_Y];\n\t\t\t\t\t\tlm[7*l+2] = id[dof_Z];\n\t\t\t\t\t\tlm[7*l+3] = id[dof_P];\n\t\t\t\t\t\tlm[7*l+4] = id[dof_RU];\n\t\t\t\t\t\tlm[7*l+5] = id[dof_RV];\n\t\t\t\t\t\tlm[7*l+6] = id[dof_RW];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(mn[l]).m_ID;\n\t\t\t\t\t\tlm[7*(l+nseln)  ] = id[dof_X];\n\t\t\t\t\t\tlm[7*(l+nseln)+1] = id[dof_Y];\n\t\t\t\t\t\tlm[7*(l+nseln)+2] = id[dof_Z];\n\t\t\t\t\t\tlm[7*(l+nseln)+3] = id[dof_P];\n\t\t\t\t\t\tlm[7*(l+nseln)+4] = id[dof_RU];\n\t\t\t\t\t\tlm[7*(l+nseln)+5] = id[dof_RV];\n\t\t\t\t\t\tlm[7*(l+nseln)+6] = id[dof_RW];\n\t\t\t\t\t}\n\n\t\t\t\t\tK.build_add(lm);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n        CalcAutoPressurePenalty(m_ss);\n        CalcAutoPressurePenalty(m_ms);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function is called during the initialization\nvoid FESlidingInterface2::Activate()\n{\n\t// don't forget to call base member\n\tFEContactInterface::Activate();\n\n    UpdateAutoPenalty();\n    \n\t// update sliding interface data\n\tUpdate();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::CalcAutoPenalty(FESlidingSurface2& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPenalty(el, s);\n\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::CalcAutoPressurePenalty(FESlidingSurface2& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPressurePenalty(el, s);\n\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingInterface2::AutoPressurePenalty(FESurfaceElement& el, FESlidingSurface2& s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == 0) return 0.0;\n\n\t// get the material\n\tFEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\n\t// see if this is a poro-elastic element\n\tFEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n\tif (biph == 0) return 0.0;\n\n\t// get a material point\n\tFEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t// setup the material point\n\tept.m_F = mat3dd(1.0);\n\tept.m_J = 1;\n\tept.m_s.zero();\n\n\t// if this is a poroelastic element, then get the permeability tensor\n\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\tpt.m_p = 0;\n\tpt.m_w = vec3d(0,0,0);\n\t\t\t\t\t\n\tdouble K[3][3];\n\tbiph->Permeability(K, mp);\n\n\tdouble eps = n.x*(K[0][0]*n.x+K[0][1]*n.y+K[0][2]*n.z)\n\t+n.y*(K[1][0]*n.x+K[1][1]*n.y+K[1][2]*n.z)\n\t+n.z*(K[2][0]*n.x+K[2][1]*n.y+K[2][2]*n.z);\n\n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::ProjectSurface(FESlidingSurface2& ss, FESlidingSurface2& ms, bool bupseg, bool bmove)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tFESurfaceElement* pme;\n\tvec3d r, nu;\n\tdouble rs[2];\n\tdouble Ln;\n\n\tdouble ps[FEElement::MAX_NODES], p1;\n\n    double psf = GetPenaltyScaleFactor();\n    \n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(m_srad);\n\tnp.Init();\n\n\t// if we need to project the nodes onto the secondary surface,\n\t// let's do this first\n\tif (bmove)\n\t{\n\t\tint NN = ss.Nodes();\n\t\tint NE = ss.Elements();\n\t\t// first we need to calculate the node normals\n\t\tvector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = ss.Element(i);\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int j=0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tvec3d r0 = ss.Node(el.m_lnode[ j         ]).m_rt;\n\t\t\t\tvec3d rp = ss.Node(el.m_lnode[(j+   1)%ne]).m_rt;\n\t\t\t\tvec3d rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).m_rt;\n\t\t\t\tvec3d n = (rp - r0)^(rm - r0);\n\t\t\t\tnormal[el.m_lnode[j]] += n;\n\t\t\t}\n\t\t}\n\t\tfor (int i=0; i<NN; ++i) normal[i].unit();\n\n\t\t// loop over all nodes\n\t\tfor (int i=0; i<NN; ++i)\n\t\t{\n\t\t\tFENode& node = ss.Node(i);\n\n\t\t\t// get the spatial nodal coordinates\n\t\t\tvec3d rt = node.m_rt;\n\t\t\tvec3d nu = normal[i];\n\n\t\t\t// project onto the secondary surface\n\t\t\tvec3d q;\n\t\t\tdouble rs[2] = {0,0};\n\t\t\tFESurfaceElement* pme = np.Project(rt, nu, rs);\n\t\t\tif (pme) \n\t\t\t{\n\t\t\t\t// the node could potentially be in contact\n\t\t\t\t// find the global location of the intersection point\n\t\t\t\tvec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n\n\t\t\t\t// calculate the gap function\n\t\t\t\t// NOTE: this has the opposite sign compared\n\t\t\t\t// to Gerard's notes.\n\t\t\t\tdouble gap = nu*(rt - q);\n\n\t\t\t\tif (gap>0) node.m_r0 = node.m_rt = q;\n\t\t\t}\n\t\t}\n\t}\n\n\t// loop over all integration points\n //   #pragma omp parallel for shared(R, bupseg)\n\tfor (int i=0; i<ss.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = ss.Element(i);\n\t\tbool sporo = ss.m_poro[i];\n\n\t\tint ne = el.Nodes();\n\t\tint nint = el.GaussPoints();\n\n\t\t// get the nodal pressures\n\t\tif (sporo)\n\t\t{\n\t\t\tfor (int j=0; j<ne; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n\t\t}\n\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n\t\t\t// get the integration point data\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n\t\t\t// calculate the global position of the integration point\n\t\t\tr = ss.Local2Global(el, j);\n\n\t\t\t// get the pressure at the integration point\n            if (sporo) p1 = el.eval(ps, j);\n\n\t\t\t// calculate the normal at this integration point\n\t\t\tnu = ss.SurfaceNormal(el, j);\n\n\t\t\t// first see if the old intersected face is still good enough\n\t\t\tpme = pt.m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\tdouble g;\n\n\t\t\t\t// see if the ray intersects this element\n\t\t\t\tif (ms.Intersect(*pme, r, nu, rs, g, m_stol))\n\t\t\t\t{\n\t\t\t\t\tpt.m_rs[0] = rs[0];\n\t\t\t\t\tpt.m_rs[1] = rs[1];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpme = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// find the intersection point with the secondary surface\n\t\t\tif (pme == 0 && bupseg) pme = np.Project(r, nu, rs);\n\n\t\t\tpt.m_pme = pme;\n\t\t\tpt.m_nu = nu;\n\t\t\tpt.m_rs[0] = rs[0];\n\t\t\tpt.m_rs[1] = rs[1];\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// the node could potentially be in contact\n\t\t\t\t// find the global location of the intersection point\n\t\t\t\tvec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n\n\t\t\t\t// calculate the gap function\n\t\t\t\t// NOTE: this has the opposite sign compared\n\t\t\t\t// to Gerard's notes.\n\t\t\t\tdouble g = nu*(r - q);\n\n\t\t\t\tdouble eps = m_epsn*pt.m_epsn*psf;\n\n\t\t\t\tLn = pt.m_Lmd + eps*g;\n\n\t\t\t\tpt.m_gap = (g <= m_srad? g : 0);\n\n\t\t\t\tif ((Ln >= 0) && (g <= m_srad))\n\t\t\t\t{\n\n\t\t\t\t\t// calculate the pressure gap function\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\t\tif (sporo) {\n                        pt.m_p1 = p1;\n                        if (mporo) {\n                            double pm[FEElement::MAX_NODES];\n                            for (int k=0; k<pme->Nodes(); ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n                            double p2 = pme->eval(pm, rs[0], rs[1]);\n                            pt.m_pg = p1 - p2;\n                        }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpt.m_Lmd = 0;\n\t\t\t\t\tpt.m_gap = 0;\n\t\t\t\t\tpt.m_pme = 0;\n\t\t\t\t\tif (sporo) {\n\t\t\t\t\t\tpt.m_Lmp = 0;\n\t\t\t\t\t\tpt.m_pg = 0;\n                        pt.m_p1 = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// the node is not in contact\n\t\t\t\tpt.m_Lmd = 0;\n\t\t\t\tpt.m_gap = 0;\n\t\t\t\tif (sporo) {\n\t\t\t\t\tpt.m_Lmp = 0;\n\t\t\t\t\tpt.m_pg = 0;\n                    pt.m_p1 = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface2::Update()\n{\t\n\tdouble rs[2];\n\n\tstatic int naug = 0;\n\tstatic int biter = 0;\n\n\tFEModel& fem = *GetFEModel();\n\t\n\t// get the iteration number\n\t// we need this number to see if we can do segment updates or not\n\t// also reset number of iterations after each augmentation\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tFESolver* psolver = pstep->GetFESolver();\n\tif (psolver->m_niter == 0) {\n\t\tbiter = 0;\n\t\tnaug = psolver->m_naug;\n        // check update of auto-penalty\n        if (m_bupdtpen) UpdateAutoPenalty();\n\t} else if (psolver->m_naug > naug) {\n\t\tbiter = psolver->m_niter;\n\t\tnaug = psolver->m_naug;\n\t}\n\tint niter = psolver->m_niter - biter;\n\tbool bupseg = ((m_nsegup == 0)? true : (niter <= m_nsegup));\n\t// get the logfile\n//\tLogfile& log = GetLogfile();\n//\tlog.printf(\"seg_up iteration # %d\\n\", niter+1);\n\t\n\t// project the surfaces onto each other\n\t// this will update the gap functions as well\n\tstatic bool bfirst = true;\n\tProjectSurface(m_ss, m_ms, bupseg, (m_breloc && bfirst));\n\tif (m_btwo_pass || m_ms.m_bporo) ProjectSurface(m_ms, m_ss, bupseg);\n\tbfirst = false;\n\n\t// Update the net contact pressures\n\tUpdateContactPressures();\n\n    // update node normals\n    m_ss.UpdateNodeNormals();\n    m_ms.UpdateNodeNormals();\n    \n\t// set poro flag\n\tbool bporo = (m_ss.m_bporo || m_ms.m_bporo);\n\n\t// only continue if we are doing a poro-elastic simulation\n\tif (bporo == false) return;\n\n\t// Now that the nodes have been projected, we need to figure out\n\t// if we need to modify the constraints on the pressure dofs.\n\t// If the nodes are not in contact, they must be free\n\t// draining. Since all nodes have been previously marked to be\n\t// free-draining in MarkFreeDraining(), we just need to reverse\n\t// this setting here, for nodes that are in contact.\n\n\t// Next, we loop over each surface, visiting the nodes\n\t// and finding out if that node is in contact or not\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface2& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface2& ms = (np == 0? m_ms : m_ss);\n\n        // loop over all the nodes of the primary surface\n        for (int n=0; n<ss.Nodes(); ++n) {\n            FENode& node = ss.Node(n);\n            int id = node.m_ID[m_dofP];\n            if ((id < -1) && (ss.m_pn[n] > 0))\n            {\n                // mark node as non-free-draining (= pos ID)\n                node.m_ID[m_dofP] = -id-2;\n            }\n        }\n\n\t\t// loop over all nodes of the secondary surface\n\t\t// the secondary surface is trickier since we need\n\t\t// to look at the primary surface's projection\n\t\tif (ms.m_bporo && ((npass == 1) || m_bdupr)) {\n\t\t\tFENormalProjection np(ss);\n\t\t\tnp.SetTolerance(m_stol);\n\t\t\tnp.SetSearchRadius(m_srad);\n\t\t\tnp.Init();\n\n\t\t\tfor (int n=0; n<ms.Nodes(); ++n)\n\t\t\t{\n\t\t\t\t// get the node\n\t\t\t\tFENode& node = ms.Node(n);\n\t\t\t\t\n\t\t\t\t// project it onto the primary surface\n\t\t\t\tFESurfaceElement* pse = np.Project(node.m_rt, ms.m_nn[n], rs);\n\t\t\t\t\n\t\t\t\tif (pse)\n\t\t\t\t{\n\t\t\t\t\t// we found an element, so let's see if it's even remotely close to contact\n\t\t\t\t\t// find the global location of the intersection point\n\t\t\t\t\tvec3d q = ss.Local2Global(*pse, rs[0], rs[1]);\n\t\t\t\t\t\n\t\t\t\t\t// calculate the gap function\n\t\t\t\t\tdouble g = ms.m_nn[n]*(node.m_rt - q);\n\t\t\t\t\t\n\t\t\t\t\tif (fabs(g) <= m_srad)\n\t\t\t\t\t{\n\t\t\t\t\t\t// we found an element so let's calculate the nodal traction values for this element\n\t\t\t\t\t\t// get the normal tractions at the nodes\n                        double tn[FEElement::MAX_NODES];\n                        for (int i=0; i<pse->Nodes(); ++i)\n                            tn[i] = ss.m_pn[pse->m_lnode[i]];\n\t\t\t\t\t\t\n\t\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\t\tdouble tp = pse->eval(tn, rs[0], rs[1]);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// if tp > 0, mark node as non-free-draining. (= pos ID)\n\t\t\t\t\t\tint id = node.m_ID[m_dofP];\n\t\t\t\t\t\tif ((id < -1) && (tp > 0))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// mark as non free-draining\n\t\t\t\t\t\t\tnode.m_ID[m_dofP] = -id-2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint i, j, k;\n\tvector<int> sLM, mLM, LM, en;\n\tvector<double> fe;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN];\n\tdouble N[4*MN*2]; // TODO: is the size correct?\n\n\tFEModel& fem = *GetFEModel();\n\n\t// if we're using the symmetric formulation\n\t// we need to multiply with the timestep\n\tdouble dt = fem.GetTime().timeIncrement;\n\n    double psf = GetPenaltyScaleFactor();\n    \n\tm_ss.m_Ft = vec3d(0, 0, 0);\n\tm_ms.m_Ft = vec3d(0, 0, 0);\n\n\t// loop over the nr of passes\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\t// get primary and secondary surface\n\t\tFESlidingSurface2& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface2& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary surface elements\n\t\tfor (i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the surface element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\n\t\t\tbool sporo = ss.m_poro[i];\n\n\t\t\t// get the nr of nodes and integration points\n\t\t\tint nseln = se.Nodes();\n\t\t\tint nint = se.GaussPoints();\n\n\t\t\t// copy the LM vector; we'll need it later\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tss.CoBaseVectors(se, j, g);\n\n\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\n\t\t\t\t// integration weights\n\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t}\n\n\t\t\t// loop over all integration points\n\t\t\t// note that we are integrating over the current surface\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the integration point data\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\n\t\t\t\t\t// get the nr of element nodes\n\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\t// copy LM vector\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\tint ndof = 3*(nseln + nmeln);\n\n\t\t\t\t\t// build the LM vector\n\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t\t}\n\n\t\t\t\t\t// build the en vector\n\t\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\t\tfor (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\n\t\t\t\t\t// get element shape functions\n\t\t\t\t\tHs = se.H(j);\n\n\t\t\t\t\t// get secondary surface element shape functions\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t\t// get normal vector\n\t\t\t\t\tvec3d nu = pt.m_nu;\n\n\t\t\t\t\t// gap function\n\t\t\t\t\tdouble g = pt.m_gap;\n\t\t\t\t\t\n\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\tdouble Lm = pt.m_Lmd;\n\n\t\t\t\t\t// penalty \n\t\t\t\t\tdouble eps = m_epsn*pt.m_epsn*psf;\n\n\t\t\t\t\t// contact traction\n\t\t\t\t\tdouble tn = Lm + eps*g;\n\t\t\t\t\ttn = MBRACKET(tn);\n\n\t\t\t\t\t// calculate the force vector\n\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\tzero(fe);\n\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[3*k  ] = -Hs[k]*nu.x;\n\t\t\t\t\t\tN[3*k+1] = -Hs[k]*nu.y;\n\t\t\t\t\t\tN[3*k+2] = -Hs[k]*nu.z;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[3*(k+nseln)  ] = Hm[k]*nu.x;\n\t\t\t\t\t\tN[3*(k+nseln)+1] = Hm[k]*nu.y;\n\t\t\t\t\t\tN[3*(k+nseln)+2] = Hm[k]*nu.z;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (k=0; k<ndof; ++k) fe[k] += tn*N[k]*detJ[j]*w[j];\n\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tss.m_Ft += vec3d(fe[k*3], fe[k*3+1], fe[k*3+2]);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (k = 0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tms.m_Ft += vec3d(fe[(k + nseln) * 3], fe[(k + nseln) * 3 + 1], fe[(k + nseln) * 3 + 2]);\n\t\t\t\t\t}\n\n\t\t\t\t\t// assemble the global residual\n\t\t\t\t\tR.Assemble(en, LM, fe);\n\n\t\t\t\t\t// do the biphasic stuff\n\t\t\t\t\tif (sporo && mporo && (tn > 0))\n\t\t\t\t\t{\n\t\t\t\t\t\t// calculate nr of pressure dofs\n\t\t\t\t\t\tint ndof = nseln + nmeln;\n\n\t\t\t\t\t\t// calculate the flow rate\n\t\t\t\t\t\tdouble epsp = m_epsp*pt.m_epsp*psf;\n\n\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\n\t\t\t\t\t\t// fill the LM\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) LM[k        ] = sLM[3*nseln+k];\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) LM[k + nseln] = mLM[3*nmeln+k];\n\n\t\t\t\t\t\t// fill the force array\n\t\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\t\tzero(fe);\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n\n\t\t\t\t\t\tfor (k=0; k<ndof; ++k) fe[k] += dt*wn*N[k]*detJ[j]*w[j];\n\n\t\t\t\t\t\t// assemble residual\n\t\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint i, j, k, l;\n\tvector<int> sLM, mLM, LM, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN], pt[MN], dpr[MN], dps[MN];\n\tdouble N[4*MN*2];\n\tFEElementMatrix ke;\n\n\tFEModel& fem = *GetFEModel();\n\n    double psf = GetPenaltyScaleFactor();\n    \n\t// see how many reformations we've had to do so far\n\tint nref = GetSolver()->m_nref;\n\n\t// set higher order stiffness mutliplier\n\t// NOTE: this algrotihm doesn't really need this\n\t// but I've added this functionality to compare with the other contact \n\t// algorithms and to see the effect of the different stiffness contributions\n\tdouble knmult = m_knmult;\n\tif (m_knmult < 0)\n\t{\n\t\tint ni = int(-m_knmult);\n\t\tif (nref >= ni)\n\t\t{\n\t\t\tknmult = 1; \n\t\t\tfeLog(\"Higher order stiffness terms included.\\n\");\n\t\t}\n\t\telse knmult = 0;\n\t}\n\n\t// do single- or two-pass\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np < npass; ++np)\n\t{\n\t\t// get the primary and secondary surface\n\t\tFESlidingSurface2& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface2& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary surface elements\n\t\tfor (i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the next element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\n\t\t\tbool sporo = ss.m_poro[i];\n\n\t\t\t// get nr of nodes and integration points\n\t\t\tint nseln = se.Nodes();\n\t\t\tint nint = se.GaussPoints();\n\n\t\t\t// nodal pressures\n\t\t\tdouble pn[MN] = {0};\n\t\t\tif (sporo)\n\t\t\t{\n\t\t\t\tfor (j=0; j<nseln; ++j) pn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n\t\t\t}\n\n\t\t\t// copy the LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tss.CoBaseVectors(se, j, g);\n\n\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\n\t\t\t\t// integration weights\n\t\t\t\tw[j] = se.GaussWeights()[j];\n\n\t\t\t\t// pressure\n\t\t\t\tif (sporo)\n\t\t\t\t{\n\t\t\t\t\tpt[j] = se.eval(pn, j);\n\t\t\t\t\tdpr[j] = se.eval_deriv1(pn, j);\n\t\t\t\t\tdps[j] = se.eval_deriv2(pn, j);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// loop over all integration points\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get integration point data\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\n\t\t\t\t\t// get the nr of nodes\n\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\t// nodal pressure\n\t\t\t\t\tdouble pm[MN] = {0};\n\t\t\t\t\tif (sporo && mporo)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) pm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n\t\t\t\t\t}\n\n\t\t\t\t\t// copy the LM vector\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\t\t\t\t\t\n\t\t\t\t\tint ndpn;\t// number of dofs per node\n\t\t\t\t\tint ndof;\t// number of dofs in stiffness matrix\n\n\t\t\t\t\tif (sporo && mporo) {\n\t\t\t\t\t\t// calculate degrees of freedom for biphasic-on-biphasic contact\n\t\t\t\t\t\tndpn = 4;\n\t\t\t\t\t\tndof = ndpn*(nseln+nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[4*k  ] = sLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[4*k+1] = sLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[4*k+2] = sLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[4*k+3] = sLM[3*nseln+k];\t\t// p-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[4*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[4*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[4*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[4*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\telse {\n\t\t\t\t\t\t// calculate degrees of freedom for biphasic-on-elastic or elastic-on-elastic contact\n\t\t\t\t\t\tndpn = 3;\n\t\t\t\t\t\tndof = ndpn*(nseln + nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// build the en vector\n\t\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\t\tfor (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\n\t\t\t\t\t// shape functions\n\t\t\t\t\tHs = se.H(j);\n\n\t\t\t\t\t// secondary surface shape functions\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\n\t\t\t\t\t// get normal vector\n\t\t\t\t\tvec3d nu = pt.m_nu;\n\n\t\t\t\t\t// gap function\n\t\t\t\t\tdouble g = pt.m_gap;\n\t\t\t\t\t\n\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\tdouble Lm = pt.m_Lmd;\n\n\t\t\t\t\t// penalty \n\t\t\t\t\tdouble eps = m_epsn*pt.m_epsn*psf;\n\n\t\t\t\t\t// contact traction\n\t\t\t\t\tdouble tn = Lm + eps*g;\n\t\t\t\t\ttn = MBRACKET(tn);\n\n//\t\t\t\t\tdouble dtn = m_eps*HEAVYSIDE(Lm + eps*g);\n\t\t\t\t\tdouble dtn = (tn > 0.? eps :0.);\n\t\t\t\t\t\n\t\t\t\t\t// create the stiffness matrix\n\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\t\t\n\t\t\t\t\t// --- S O L I D - S O L I D   C O N T A C T ---\n\t\t\t\t\t\n\t\t\t\t\t// a. NxN-term\n\t\t\t\t\t//------------------------------------\n\t\t\t\t\t\n\t\t\t\t\t// calculate the N-vector\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[ndpn*k  ] = Hs[k]*nu.x;\n\t\t\t\t\t\tN[ndpn*k+1] = Hs[k]*nu.y;\n\t\t\t\t\t\tN[ndpn*k+2] = Hs[k]*nu.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[ndpn*(k+nseln)  ] = -Hm[k]*nu.x;\n\t\t\t\t\t\tN[ndpn*(k+nseln)+1] = -Hm[k]*nu.y;\n\t\t\t\t\t\tN[ndpn*(k+nseln)+2] = -Hm[k]*nu.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (ndpn == 4) {\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t\tN[ndpn*k+3] = 0;\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+3] = 0;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<ndof; ++k)\n\t\t\t\t\t\tfor (l=0; l<ndof; ++l) ke[k][l] += dtn*N[k]*N[l]*detJ[j]*w[j];\n\t\t\t\t\t\n\t\t\t\t\t// b. A-term\n\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n\t\t\t\t\t\n\t\t\t\t\tdouble* Gr = se.Gr(j);\n\t\t\t\t\tdouble* Gs = se.Gs(j);\n\t\t\t\t\tvec3d gs[2];\n\t\t\t\t\tss.CoBaseVectors(se, j, gs);\n\t\t\t\t\t\n\t\t\t\t\tmat3d S1, S2;\n\t\t\t\t\tS1.skew(gs[0]);\n\t\t\t\t\tS2.skew(gs[1]);\n\t\t\t\t\tmat3d As[FEElement::MAX_NODES];\n\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\tAs[l] = S2*Gr[l] - S1*Gs[l];\n\t\t\t\t\t\n\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t{\t// non-symmetric\n\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn  ] -= knmult*tn*w[j]*N[k]*As[l][0][0];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+1] -= knmult*tn*w[j]*N[k]*As[l][0][1];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+2] -= knmult*tn*w[j]*N[k]*As[l][0][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn  ] -= knmult*tn*w[j]*N[k]*As[l][1][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+1] -= knmult*tn*w[j]*N[k]*As[l][1][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+2] -= knmult*tn*w[j]*N[k]*As[l][1][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn  ] -= knmult*tn*w[j]*N[k]*As[l][2][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+1] -= knmult*tn*w[j]*N[k]*As[l][2][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+2] -= knmult*tn*w[j]*N[k]*As[l][2][2];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} \n\t\t\t\t\telse \n\t\t\t\t\t{\t// symmetric\n\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][0];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][1];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][k*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][0];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][k*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][1];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][k*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][k*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][0];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][k*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][1];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][k*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][k*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][0];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][k*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][1];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][k*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][2];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// c. M-term\n\t\t\t\t\t//---------------------------------------\n\t\t\t\t\t\n\t\t\t\t\tvec3d Gm[2];\n\t\t\t\t\tms.ContraBaseVectors(me, r, s, Gm);\n\t\t\t\t\t\n\t\t\t\t\t// evaluate secondary surface normal\n\t\t\t\t\tvec3d mnu = Gm[0] ^ Gm[1];\n\t\t\t\t\tmnu.unit();\n\t\t\t\t\t\n\t\t\t\t\tdouble Hmr[FEElement::MAX_NODES], Hms[FEElement::MAX_NODES];\n\t\t\t\t\tme.shape_deriv(Hmr, Hms, r, s);\n\t\t\t\t\tvec3d mm[FEElement::MAX_NODES];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) \n\t\t\t\t\t\tmm[k] = Gm[0]*Hmr[k] + Gm[1]*Hms[k];\n\t\t\t\t\t\n\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t{\t// non-symmetric\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn  ] += tn*knmult*detJ[j]*w[j]*mnu.x*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+1] += tn*knmult*detJ[j]*w[j]*mnu.x*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+2] += tn*knmult*detJ[j]*w[j]*mnu.x*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn  ] += tn*knmult*detJ[j]*w[j]*mnu.y*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+1] += tn*knmult*detJ[j]*w[j]*mnu.y*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+2] += tn*knmult*detJ[j]*w[j]*mnu.y*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn  ] += tn*knmult*detJ[j]*w[j]*mnu.z*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+1] += tn*knmult*detJ[j]*w[j]*mnu.z*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+2] += tn*knmult*detJ[j]*w[j]*mnu.z*mm[k].z*N[l];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t// symmetric\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][(k+nseln)*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][(k+nseln)*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][(k+nseln)*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].x*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][(k+nseln)*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][(k+nseln)*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][(k+nseln)*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].y*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][(k+nseln)*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].z*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][(k+nseln)*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].z*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][(k+nseln)*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].z*N[l];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// --- B I P H A S I C   S T I F F N E S S ---\n\t\t\t\t\tif (sporo && mporo)\n\t\t\t\t\t{\n\t\t\t\t\t\t// the variable dt is either the timestep or one\n\t\t\t\t\t\t// depending on whether we are using the symmetric\n\t\t\t\t\t\t// poro version or not.\n\t\t\t\t\t\tdouble dt = fem.GetTime().timeIncrement;\n\t\t\t\t\t\t\n\t\t\t\t\t\tdouble epsp = (tn > 0) ? m_epsp*pt.m_epsp*psf : 0.;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- S O L I D - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// a. q-term\n\t\t\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble dpmr, dpms;\n\t\t\t\t\t\t\tdpmr = me.eval_deriv1(pm, r, s);\n\t\t\t\t\t\t\tdpms = me.eval_deriv2(pm, r, s);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l  ] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].x + dpms*Gm[1].x);\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+1] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].y + dpms*Gm[1].y);\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+2] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].z + dpms*Gm[1].z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// b. A-term\n\t\t\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l  ] -= dt*w[j]*wn*N[k]*(As[l][0][0]*nu.x + As[l][0][1]*nu.y + As[l][0][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+1] -= dt*w[j]*wn*N[k]*(As[l][1][0]*nu.x + As[l][1][1]*nu.y + As[l][1][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+2] -= dt*w[j]*wn*N[k]*(As[l][2][0]*nu.x + As[l][2][1]*nu.y + As[l][2][2]*nu.z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// c. m-term\n\t\t\t\t\t\t\t//---------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l  ] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].x;\n\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l+1] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].y;\n\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l+2] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].z;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\t// calculate the N-vector\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*k  ] = 0;\n\t\t\t\t\t\t\tN[ndpn*k+1] = 0;\n\t\t\t\t\t\t\tN[ndpn*k+2] = 0;\n\t\t\t\t\t\t\tN[ndpn*k+3] = Hs[k];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)  ] = 0;\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+1] = 0;\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+2] = 0;\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+3] = -Hm[k];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<ndof; ++k)\n\t\t\t\t\t\t\tfor (l=0; l<ndof; ++l) ke[k][l] -= dt*epsp*w[j]*detJ[j]*N[k]*N[l];\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// assemble the global stiffness\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::UpdateContactPressures()\n{\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface2& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface2& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all elements of the primary surface\n\t\tfor (int n=0; n<ss.Elements(); ++n)\n\t\t{\n\t\t\tFESurfaceElement& el = ss.Element(n);\n\t\t\tint nint = el.GaussPoints();\n\t\t\t\n\t\t\t// get the normal tractions at the integration points\n\t\t\tdouble gap, eps;\n\t\t\tfor (int i=0; i<nint; ++i) \n\t\t\t{\n\t\t\t\t// get integration point data\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\n\t\t\t\tgap = pt.m_gap;\n\t\t\t\teps = m_epsn*pt.m_epsn;\n\t\t\t\tpt.m_Ln = MBRACKET(pt.m_Lmd + eps*gap);\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (m_btwo_pass && pme)\n\t\t\t\t{\n\t\t\t\t\tint mint = pme->GaussPoints();\n\t\t\t\t\tdouble ti[FEElement::MAX_NODES];\n\t\t\t\t\tfor (int j=0; j<mint; ++j) {\n                        FEBiphasicContactPoint& mp = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\t\t\t\tgap = mp.m_gap;\n\t\t\t\t\t\teps = m_epsn*mp.m_epsn;\n\t\t\t\t\t\tti[j] = MBRACKET(mp.m_Lmd + m_epsn*mp.m_epsn*mp.m_gap);\n\t\t\t\t\t}\n\t\t\t\t\t// project the data to the nodes\n\t\t\t\t\tdouble tn[FEElement::MAX_NODES];\n\t\t\t\t\tpme->FEElement::project_to_nodes(ti, tn);\n\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\tdouble Ln = pme->eval(tn, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t\tpt.m_Ln += MBRACKET(Ln);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n        ss.EvaluateNodalContactPressures();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterface2::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tdouble Ln, Lp;\n\tbool bconv = true;\n\n\tbool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n\tint NS = m_ss.Elements();\n\tint NM = m_ms.Elements();\n\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\t// a. normal component\n\tdouble normL0 = 0, normP = 0, normDP = 0;\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tfor (int j=0; j<el.GaussPoints(); ++j)\n\t\t{\n            FEBiphasicContactPoint& ds = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += ds.m_Lmd*ds.m_Lmd;\n\t\t}\n\t}\n\tfor (int i=0; i<NM; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FEBiphasicContactPoint& dm = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += dm.m_Lmd*dm.m_Lmd;\n\t\t}\n\t}\n\n\t// b. gap component\n\t// (is calculated during update)\n\tdouble maxgap = 0;\n\tdouble maxpg = 0;\n\n\t// update Lagrange multipliers\n\tdouble normL1 = 0, eps, epsp;\n    for (int i=0; i<m_ss.Elements(); ++i) {\n        FESurfaceElement& el = m_ss.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\t// update Lagrange multipliers on primary surface\n            if (m_bsmaug) {\n                // replace this multiplier with a smoother version\n                Ln = -(tn[j]*data.m_nu);\n                data.m_Lmd = MBRACKET(Ln);\n                if (m_btwo_pass) data.m_Lmd /= 2;\n            }\n            else {\n                eps = m_epsn*data.m_epsn;\n                Ln = data.m_Lmd + eps*data.m_gap;\n                data.m_Lmd = MBRACKET(Ln);\n            }\n            \n            normL1 += data.m_Lmd*data.m_Lmd;\n            \n            if (m_ss.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*data.m_epsp;\n                    Lp = data.m_Lmp + epsp*data.m_pg;\n                    maxpg = max(maxpg,fabs(data.m_pg));\n                    normDP += data.m_pg*data.m_pg;\n                }\n                data.m_Lmp = Lp;\n            }\n            \n            if (Ln > 0) maxgap = max(maxgap,fabs(data.m_gap));\n        }\n    }\n    \n    for (int i=0; i<m_ms.Elements(); ++i) {\n        FESurfaceElement& el = m_ms.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\t// update Lagrange multipliers on secondary surface\n            if (m_bsmaug) {\n                // replace this multiplier with a smoother version\n                Ln = -(tn[j]*data.m_nu);\n                data.m_Lmd = MBRACKET(Ln);\n                if (m_btwo_pass) data.m_Lmd /= 2;\n            }\n            else {\n                eps = m_epsn*data.m_epsn;\n                Ln = data.m_Lmd + eps*data.m_gap;\n                data.m_Lmd = MBRACKET(Ln);\n            }\n            \n            normL1 += data.m_Lmd*data.m_Lmd;\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*data.m_epsp;\n                    Lp = data.m_Lmp + epsp*data.m_pg;\n                    maxpg = max(maxpg,fabs(data.m_pg));\n                    normDP += data.m_pg*data.m_pg;\n                }\n                data.m_Lmp = Lp;\n            }\n            \n            if (Ln > 0) maxgap = max(maxgap,fabs(data.m_gap));\n        }\n    }\n    \n\n    // normP should be a measure of the fluid pressure at the\n    // contact interface.  However, since it could be zero,\n    // use an average measure of the contact traction instead.\n\tnormP = normL1;\n\t\n\t// calculate relative norms\n\tdouble lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0)); \n\tdouble pnorm = (normP != 0 ? (normDP/normP) : normDP); \n\n\t// check convergence\n\tif ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n\tif ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n\n\tif ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n\tif ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tfeLog(\" sliding interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\tif (bporo) { feLog(\"    P gap        : %15le\", pnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\"); }\n\n\tfeLog(\"    maximum gap  : %15le\", maxgap);\n\tif (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n\tif (bporo) {\n\t\tfeLog(\"    maximum pgap : %15le\", maxpg);\n\t\tif (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n\t}\n    \n    if (bconv) UpdateContactPressures();\n    \n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::Serialize(DumpStream &ar)\n{\n\t// serialize contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// serialize contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n\n\t// serialize pointers for deep streaming\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface2::MarkFreeDraining()\n{\t\n\tint i, id, np;\n\n\t// Mark all nodes as free-draining.  This needs to be done for ALL\n\t// contact interfaces prior to executing Update(), where nodes that are\n\t// in contact are subsequently marked as non free-draining.  This ensures\n\t// that for surfaces involved in more than one contact interface, nodes\n\t// that have been marked as non free-draining are not reset to \n\t// free-draining.\n\tfor (np=0; np<2; ++np)\n\t{\n\t\tFESlidingSurface2& s = (np == 0? m_ss : m_ms);\n\t\t\n\t\tif (s.m_bporo) {\n\t\t\t// first, mark all nodes as free-draining (= neg. ID)\n\t\t\t// this is done by setting the dof's equation number\n\t\t\t// to a negative number\n\t\t\tfor (i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tid = s.Node(i).m_ID[m_dofP];\n\t\t\t\tif (id >= 0) \n\t\t\t\t{\n\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t// mark node as free-draining\n\t\t\t\t\tnode.m_ID[m_dofP] = -id-2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface2::SetFreeDraining()\n{\t\n\tint i, np;\n\t\n\t// Set the pressure to zero for the free-draining nodes\n\tfor (np=0; np<2; ++np)\n\t{\n\t\tFESlidingSurface2& s = (np == 0? m_ss : m_ms);\n\t\t\n\t\tif (s.m_bporo) {\n\t\t\t// loop over all nodes\n\t\t\tfor (i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tif (s.Node(i).m_ID[m_dofP] < -1)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t// set the fluid pressure to zero\n\t\t\t\t\tnode.set(m_dofP, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FESlidingInterface2.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBiphasicContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingSurface2 : public FEBiphasicContactSurface\n{\npublic:\n\t//! constructor\n\tFESlidingSurface2(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t// data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! evaluate net contact force\n\tvec3d GetContactForce() override;\n    vec3d GetContactForceFromElementStress();\n\n\t//! evaluate net contact area\n\tdouble GetContactArea() override;\n    \n\t//! evaluate net fluid force\n\tvec3d GetFluidForce() override;\n    vec3d GetFluidForceFromElementPressure();\n    \n    //! evaluate the fluid load support\n    double GetFluidLoadSupport() override;\n\n\t//! calculate the nodal normals\n\tvoid UpdateNodeNormals();\n\n\tvoid SetPoroMode(bool bporo) { m_bporo = bporo; }\n\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt) override;\n\tvoid GetNodalContactPressure(int nface, double* pg) override;\n\tvoid GetNodalContactTraction(int nface, vec3d* pt) override;\n    void EvaluateNodalContactPressures();\n\nprivate:\n\tvoid GetContactPressure(int nface, double& pg);\n\npublic:\n\tbool\tm_bporo;\t//!< set poro-mode\n\n\tvector<bool>\t\tm_poro;\t//!< surface element poro status\n\tvector<vec3d>\t\tm_nn;\t//!< node normals\n    vector<double>      m_pn;   //!< nodal contact pressures\n\n\tvec3d\tm_Ft;\t//!< total contact force (from equivalent nodal forces)\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingInterface2 :\tpublic FEContactInterface\n{\npublic:\n\t//! constructor\n\tFESlidingInterface2(FEModel* pfem);\n\n\t//! destructor\n\t~FESlidingInterface2();\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! calculate contact pressures for file output\n\tvoid UpdateContactPressures();\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! mark free-draining condition \n\tvoid MarkFreeDraining();\n\t\n\t//! set free-draining condition \n\tvoid SetFreeDraining();\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FESlidingSurface2& ss, FESlidingSurface2& ms, bool bupseg, bool bmove = false);\n\n\t//! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n\tvoid CalcAutoPenalty(FESlidingSurface2& s);\n\n\tvoid CalcAutoPressurePenalty(FESlidingSurface2& s);\n\tdouble AutoPressurePenalty(FESurfaceElement& el, FESlidingSurface2& s);\n\npublic:\n\tFESlidingSurface2\tm_ss;\t//!< primary surface\n\tFESlidingSurface2\tm_ms;\t//!< secondary surface\n\n\tint\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n\tbool\t\t\tm_btwo_pass;\t//!< two-pass flag\n\tdouble\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\t\t\tm_gtol;\t\t\t//!< gap tolerance\n\tdouble\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n\tdouble\t\t\tm_stol;\t\t\t//!< search tolerance\n\tbool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n\tdouble\t\t\tm_srad;\t\t\t//!< contact search radius\n\tint\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n\tint\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n\tint\t\t\t\tm_nsegup;\t\t//!< segment update parameter\n\tbool\t\t\tm_breloc;\t\t//!< node relocation on startup\n    bool            m_bsmaug;       //!< smooth augmentation\n    bool            m_bdupr;        //!< dual projection flag for free-draining\n\n\tdouble\t\t\tm_epsn;\t\t//!< normal penalty factor\n\tbool\t\t\tm_bautopen;\t//!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n\n\t// biphasic contact parameters\n\tdouble\tm_epsp;\t\t//!< flow rate penalty\n\nprotected:\n\tint\tm_dofP;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESlidingInterface3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingInterface3.h\"\n#include \"FESlidingInterfaceMP.h\"\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEAnalysis.h>\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingInterface3, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"               )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"            );\n    ADD_PARAMETER(m_gtol     , \"gaptol\"               )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_ptol     , \"ptol\"                 );\n\tADD_PARAMETER(m_ctol     , \"ctol\"                 );\n\tADD_PARAMETER(m_epsn     , \"penalty\"              );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"         );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"             );\n\tADD_PARAMETER(m_knmult   , \"knmult\"               );\n\tADD_PARAMETER(m_stol     , \"search_tol\"           );\n\tADD_PARAMETER(m_epsp     , \"pressure_penalty\"     );\n\tADD_PARAMETER(m_epsc     , \"concentration_penalty\");\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\"  );\n    ADD_PARAMETER(m_srad     , \"search_radius\"        )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_nsegup   , \"seg_up\"               );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"               );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"               );\n    ADD_PARAMETER(m_breloc   , \"node_reloc\"         );\n    ADD_PARAMETER(m_bsmaug   , \"smooth_aug\"         );\n\tADD_PARAMETER(m_ambp     , \"ambient_pressure\"     );\n\tADD_PARAMETER(m_ambc     , \"ambient_concentration\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FESlidingSurface3\n//-----------------------------------------------------------------------------\n\nFESlidingSurface3::FESlidingSurface3(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{ \n\tm_bporo = m_bsolu = false;\n\tm_dofC = -1;\n}\n\n//-----------------------------------------------------------------------------\nFESlidingSurface3::~FESlidingSurface3()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FESlidingSurface3::CreateMaterialPoint()\n{\n\treturn new FEMultiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface3::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    // get nodal DOFS\n    DOFS& dofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = dofs.GetVariableSize(\"concentration\");\n    \n\tint N = el.Nodes();\n\tlm.resize(N*(4+MAX_CDOFS));\n\n\t// pack the equation numbers\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3*i  ] = id[m_dofX];\n\t\tlm[3*i+1] = id[m_dofY];\n\t\tlm[3*i+2] = id[m_dofZ];\n\n\t\t// now the pressure dofs\n\t\tlm[3*N+i] = id[m_dofP];\n\n\t\t// concentration dofs\n\t\tfor (int k=0; k<MAX_CDOFS; ++k)\n\t\t\tlm[(4 + k)*N + i] = id[m_dofC+k];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingSurface3::Init()\n{\n\t// initialize surface data first\n\tif (FEBiphasicContactSurface::Init() == false) return false;\n\t\n\t// store concentration index\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tm_dofC = dofs.GetDOF(\"concentration\", 0);\n\n\t// allocate data structures\n\tm_nn.assign(Nodes(), vec3d(0,0,0));\n    m_pn.assign(Nodes(), 0);\n\t\n\t// determine biphasic and biphasic-solute status\n\tm_poro.resize(Elements(),false);\n\tm_solu.resize(Elements(),-1);\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& se = Element(i);\n\t\t\n\t\t// get the element this surface element belongs to\n\t\tFEElement* pe = se.m_elem[0].pe;\n\t\tif (pe)\n\t\t{\n\t\t\t// get the material\n\t\t\tFEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n\t\t\t\n\t\t\t// see if this is a poro-elastic element\n\t\t\tFEBiphasic* pb = dynamic_cast<FEBiphasic*> (pm);\n\t\t\tFEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n\t\t\tif (pb || pbs) {\n\t\t\t\tm_poro[i] = true;\n\t\t\t\tm_bporo = true;\n\t\t\t}\n\t\t\tif (pbs) {\n\t\t\t\tm_solu[i] = pbs->GetSolute()->GetSoluteID() - 1;\n\t\t\t\tm_bsolu = true;\n\t\t\t}\n\t\t}\n\t}\n    \n    // allocate data structures\n    int NE = Elements();\n    for (int i=0; i<NE; ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int nint = el.GaussPoints();\n        int nsol = m_solu.size();\n        if (nsol) {\n            for (int j=0; j<nint; ++j) {\n                FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n                data.m_Lmc.resize(nsol);\n                data.m_epsc.resize(nsol);\n                data.m_cg.resize(nsol);\n                data.m_c1.resize(nsol);\n                data.m_epsc.assign(nsol,1);\n                data.m_c1.assign(nsol,0.0);\n                data.m_cg.assign(nsol,0.0);\n                data.m_Lmc.assign(nsol,0.0);\n            }\n        }\n    }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact pressures by averaging values from surrounding\n//! faces.  This function ensures that nodal contact pressures are always\n//! positive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurface3::EvaluateNodalContactPressures()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact pressures\n    zero(m_pn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact pressure for that face\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_pn[el.m_lnode[j]] += pn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_pn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the \n//! element normals at the node\n\nvoid FESlidingSurface3::UpdateNodeNormals()\n{\n\tvec3d y[FEElement::MAX_NODES];\n\t\n\t// zero nodal normals\n\tzero(m_nn);\n\t\n\t// loop over all elements\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\t\t\n\t\t// get the nodal coordinates\n\t\tfor (int j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n\t\t\n\t\t// calculate the normals\n\t\tfor (int j=0; j<ne; ++j)\n\t\t{\n\t\t\tint jp1 = (j+1)%ne;\n\t\t\tint jm1 = (j+ne-1)%ne;\n\t\t\tvec3d n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n\t\t\tm_nn[el.m_lnode[j]] += n;\n\t\t}\n\t}\n\t\n\t// normalize all vectors\n\tint N = Nodes();\n\tfor (int i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface3::GetContactForce()\n{\n    return m_Ft;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurface3::GetContactArea()\n{\n\t// initialize contact area\n\tdouble a = 0;\n\t\n\t// loop over all elements of the primary surface\n\tfor (int n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// evaluate the contact force for that element\n\t\tfor (int i=0; i<nint; ++i)\n\t\t{\n\t\t\t// get data for this integration point\n            FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(i));\n            double s = (data.m_Ln > 0) ? 1 : 0;\n            \n\t\t\t// get the base vectors\n\t\t\tvec3d g[2];\n\t\t\tCoBaseVectors(el, i, g);\n            \n\t\t\t// normal (magnitude = area)\n\t\t\tvec3d n = g[0] ^ g[1];\n            \n\t\t\t// gauss weight\n\t\t\tdouble w = el.GaussWeights()[i];\n            \n\t\t\t// contact force\n\t\t\ta += n.norm()*(w*s);\n\t\t}\n\t}\n\t\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurface3::GetFluidForce()\n{\n\tint n, i;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble pn[MN];\n    \n    // get parent contact interface to extract ambient fluid pressure\n    FESlidingInterface3* si3 = dynamic_cast<FESlidingInterface3*>(GetContactInterface());\n    double ambp = si3->m_ambp;\n\t\n\t// initialize contact force\n\tvec3d f(0,0,0);\n\t\n\t// loop over all elements of the surface\n\tfor (n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nseln = el.Nodes();\n\t\t\n\t\t// nodal pressures\n\t\tfor (i=0; i<nseln; ++i) pn[i] = GetMesh()->Node(el.m_node[i]).get(m_dofP);\n\t\t\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// evaluate the fluid force for that element\n\t\tfor (i=0; i<nint; ++i)\n\t\t{\n\t\t\t// get the base vectors\n\t\t\tvec3d g[2];\n\t\t\tCoBaseVectors(el, i, g);\n\t\t\t// normal (magnitude = area)\n\t\t\tvec3d n = g[0] ^ g[1];\n\t\t\t// gauss weight\n\t\t\tdouble w = el.GaussWeights()[i];\n\t\t\t// fluid pressure for fluid load support\n\t\t\tdouble p = el.eval(pn, i) - ambp;\n\t\t\t// contact force\n\t\t\tf += n*(w*p);\n\t\t}\n\t}\n\t\n\treturn f;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface3::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_dofC;\n\tar & m_dofP;\n\tar & m_bporo;\n\tar & m_bsolu;\n\tar & m_nn;\n\tar & m_poro;\n\tar & m_solu;\n\tar & m_pn;\n\tar & m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface3::GetContactPressure(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_Ln;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface3::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpt -= data.m_nu*data.m_Ln;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface3::GetNodalContactPressure(int nface, double* pn)\n{\n\tFESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        pn[k] = m_pn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurface3::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        tn[k] = m_nn[el.m_lnode[k]]*(-m_pn[el.m_lnode[k]]);\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingInterface3\n//-----------------------------------------------------------------------------\n\nFESlidingInterface3::FESlidingInterface3(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\t\n\t// initial values\n\tm_knmult = 1;\n\tm_atol = 0.1;\n\tm_epsn = 1;\n\tm_epsp = 1;\n\tm_epsc = 1;\n\tm_btwo_pass = false;\n\tm_stol = 0.01;\n\tm_bsymm = true;\n\tm_srad = 1.0;\n\tm_gtol = 0;\n\tm_ptol = 0;\n\tm_ctol = 0;\n\tm_ambp = 0;\n\tm_ambc = 0;\n\tm_nsegup = 0;\n\tm_bautopen = false;\n    m_bupdtpen = false;\n    m_breloc = false;\n    m_bsmaug = false;\n\t\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n\tm_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n\tm_dofC = (pfem ? pfem->GetDOFIndex(\"concentration\", 0) : -1);\n\t\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n}\n\n//-----------------------------------------------------------------------------\n\nFESlidingInterface3::~FESlidingInterface3()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterface3::Init()\n{\n\tm_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n\tm_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n\t// initialize surface data\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FESlidingInterface3::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_P = fem.GetDOFIndex(\"p\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\tconst int dof_C = fem.GetDOFIndex(\"concentration\", 0);\n\n\tvector<int> lm(8*FEElement::MAX_NODES*2);\n\t\t\t\t\t\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface3& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface3& ms = (np == 0? m_ms : m_ss);\n\t\t// get the mesh\n\t\tint sid, mid;\n\t\t\t\t\t\t\n\t\tint k, l;\n\t\tfor (int j=0; j<ss.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\t\t\tsid = ss.m_solu[j];\n\t\t\tint nint = se.GaussPoints();\n\t\t\tint* sn = &se.m_node[0];\n\t\t\tfor (k=0; k<nint; ++k)\n\t\t\t{\n                FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(k));\n\n\t\t\t\tFESurfaceElement* pe = data.m_pme;\n\t\t\t\tif (pe != 0)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\t\tmid = ms.m_solu[pe->m_lid];\n\t\t\t\t\tint* mn = &me.m_node[0];\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tassign(lm, -1);\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tint nseln = se.Nodes();\n\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(sn[l]).m_ID;\n\t\t\t\t\t\tlm[8*l  ] = id[dof_X];\n\t\t\t\t\t\tlm[8*l+1] = id[dof_Y];\n\t\t\t\t\t\tlm[8*l+2] = id[dof_Z];\n\t\t\t\t\t\tlm[8*l+3] = id[dof_P];\n\t\t\t\t\t\tlm[8*l+4] = id[dof_RU];\n\t\t\t\t\t\tlm[8*l+5] = id[dof_RV];\n\t\t\t\t\t\tlm[8*l+6] = id[dof_RW];\n\t\t\t\t\t\tlm[8*l+7] = id[dof_C + sid];\n\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(mn[l]).m_ID;\n\t\t\t\t\t\tlm[8*(l+nseln)  ] = id[dof_X];\n\t\t\t\t\t\tlm[8*(l+nseln)+1] = id[dof_Y];\n\t\t\t\t\t\tlm[8*(l+nseln)+2] = id[dof_Z];\n\t\t\t\t\t\tlm[8*(l+nseln)+3] = id[dof_P];\n\t\t\t\t\t\tlm[8*(l+nseln)+4] = id[dof_RU];\n\t\t\t\t\t\tlm[8*(l+nseln)+5] = id[dof_RV];\n\t\t\t\t\t\tlm[8*(l+nseln)+6] = id[dof_RW];\n\t\t\t\t\t\tlm[8*(l+nseln)+7] = id[dof_C + mid];\n\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tK.build_add(lm);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n        if (m_ss.m_bporo) CalcAutoPressurePenalty(m_ss);\n        if (m_ss.m_bsolu) CalcAutoConcentrationPenalty(m_ss);\n        if (m_ms.m_bporo) CalcAutoPressurePenalty(m_ms);\n        if (m_ms.m_bsolu) CalcAutoConcentrationPenalty(m_ms);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n    UpdateAutoPenalty();\n    \n\t// update sliding interface data\n\tUpdate();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::CalcAutoPenalty(FESlidingSurface3& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j) \n\t\t{\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::CalcAutoPressurePenalty(FESlidingSurface3& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPressurePenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j) \n\t\t{\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingInterface3::AutoPressurePenalty(FESurfaceElement& el, FESlidingSurface3& s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == 0) return 0.0;\n\n\t// get the material\n\tFEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\t\t\n\t// see if this is a poro-elastic element\n\tdouble eps = 0.0;\n\tFEBiphasic* bp = dynamic_cast<FEBiphasic*> (pm);\n\tFEBiphasicSolute* bps = dynamic_cast<FEBiphasicSolute*> (pm);\n\tif (bp)\n\t{\n\t\t// get a material point\n\t\tFEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n\t\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\t\n\t\t// setup the material point\n\t\tept.m_F = mat3dd(1.0);\n\t\tept.m_J = 1;\n\t\tept.m_s.zero();\n\t\t\t\n\t\t// if this is a poroelastic element, then get the permeability tensor\n\t\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\tpt.m_p = 0;\n\t\tpt.m_w = vec3d(0,0,0);\n\t\t\t\n\t\tdouble K[3][3];\n\t\tbp->Permeability(K, mp);\n\t\t\t\n\t\teps = n.x*(K[0][0]*n.x+K[0][1]*n.y+K[0][2]*n.z)\n\t\t+n.y*(K[1][0]*n.x+K[1][1]*n.y+K[1][2]*n.z)\n\t\t+n.z*(K[2][0]*n.x+K[2][1]*n.y+K[2][2]*n.z);\n\t}\n\telse if (bps)\n\t{\n\t\t// get a material point\n\t\tFEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n\t\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\t\n\t\t// setup the material point\n\t\tept.m_F = mat3dd(1.0);\n\t\tept.m_J = 1;\n\t\tept.m_s.zero();\n\t\t\t\n\t\t// if this is a biphasic-solute element, then get the permeability tensor\n\t\tFEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\tFESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n\t\tppt.m_p = 0;\n\t\tppt.m_w = vec3d(0,0,0);\n\t\tspt.m_c[0] = 0;\n\t\tspt.m_j[0] = vec3d(0,0,0);\n\t\t\t\n\t\tmat3ds K = bps->GetPermeability()->Permeability(mp);\n\t\t\t\n\t\teps = n*(K*n);\n\t}\n\n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::CalcAutoConcentrationPenalty(FESlidingSurface3& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a modulus\n\t\tdouble eps = AutoConcentrationPenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsc[0] = eps;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingInterface3::AutoConcentrationPenalty(FESurfaceElement& el, FESlidingSurface3& s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n\t\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == 0) return 0.0;\n\n\t// get the material\n\tFEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\t\t\n\t// see if this is a biphasic-solute element\n\tFEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n\tif (pbs == 0) return 0.0;\n\n\t// get a material point\n\tFEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\t\n\t// setup the material point\n\tept.m_F = mat3dd(1.0);\n\tept.m_J = 1;\n\tept.m_s.zero();\n\t\t\t\n\t// if this is a biphasic-solute element, then get the diffusivity tensor\n\tFEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\tFESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n\tppt.m_p = 0;\n\tppt.m_w = vec3d(0,0,0);\n\tspt.m_c[0] = 0;\n\tspt.m_j[0] = vec3d(0,0,0);\n\t\t\t\n\tmat3ds D = pbs->GetSolute()->m_pDiff->Diffusivity(mp)\n\t*(pbs->Porosity(mp)*pbs->GetSolute()->m_pSolub->Solubility(mp));\n\t\t\t\n\tdouble eps = n*(D*n);\n\t\n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::ProjectSurface(FESlidingSurface3& ss, FESlidingSurface3& ms, bool bupseg, bool bmove)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tFESurfaceElement* pme;\n\tvec3d r, nu;\n\tdouble rs[2];\n\tdouble Ln;\n\t\n\tdouble ps[FEElement::MAX_NODES], p1;\n\tdouble cs[FEElement::MAX_NODES], c1;\n\t\n\tdouble R = m_srad*mesh.GetBoundingBox().radius();\n\t\n    double psf = GetPenaltyScaleFactor();\n    \n\t// initialize projection data\n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(m_srad);\n\tnp.Init();\n\n    // if we need to project the nodes onto the secondary surface,\n    // let's do this first\n    if (bmove)\n    {\n        int NN = ss.Nodes();\n        int NE = ss.Elements();\n        // first we need to calculate the node normals\n        vector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n        for (int i=0; i<NE; ++i)\n        {\n            FESurfaceElement& el = ss.Element(i);\n            int ne = el.Nodes();\n            for (int j=0; j<ne; ++j)\n            {\n                vec3d r0 = ss.Node(el.m_lnode[ j         ]).m_rt;\n                vec3d rp = ss.Node(el.m_lnode[(j+   1)%ne]).m_rt;\n                vec3d rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).m_rt;\n                vec3d n = (rp - r0)^(rm - r0);\n                normal[el.m_lnode[j]] += n;\n            }\n        }\n        for (int i=0; i<NN; ++i) normal[i].unit();\n        \n        // loop over all nodes\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = ss.Node(i);\n            \n            // get the spatial nodal coordinates\n            vec3d rt = node.m_rt;\n            vec3d nu = normal[i];\n            \n            // project onto the secondary surface\n            vec3d q;\n            double rs[2] = {0,0};\n            FESurfaceElement* pme = np.Project(rt, nu, rs);\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double gap = nu*(rt - q);\n                \n                if (gap>0) node.m_r0 = node.m_rt = q;\n            }\n        }\n    }\n    \n\t// loop over all integration points\n//    #pragma omp parallel for shared(R, bupseg)\n\tfor (int i=0; i<ss.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = ss.Element(i);\n\n\t\tbool sporo = ss.m_poro[i];\n\t\tint sid = ss.m_solu[i];\n\t\tbool ssolu = (sid > -1) ? true : false;\n\t\t\n\t\tint ne = el.Nodes();\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// get the nodal pressures\n\t\tif (sporo)\n\t\t{\n\t\t\tfor (int j=0; j<ne; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n\t\t}\n\t\t\n\t\t// get the nodal concentrations\n\t\tif (ssolu)\n\t\t{\n\t\t\tfor (int j=0; j<ne; ++j) cs[j] = mesh.Node(el.m_node[j]).get(m_dofC + sid);\n\t\t}\n\t\t\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n\t\t\t// calculate the global position of the integration point\n\t\t\tr = ss.Local2Global(el, j);\n\t\t\t\n\t\t\t// get the pressure at the integration point\n\t\t\tif (sporo) p1 = el.eval(ps, j);\n\t\t\t\n\t\t\t// get the concentration at the integration point\n\t\t\tif (ssolu) c1 = el.eval(cs, j);\n\t\t\t\n\t\t\t// calculate the normal at this integration point\n\t\t\tnu = ss.SurfaceNormal(el, j);\n\t\t\t\n\t\t\t// first see if the old intersected face is still good enough\n\t\t\tpme = pt.m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\tdouble g;\n\t\t\t\t\n\t\t\t\t// see if the ray intersects this element\n\t\t\t\tif (ms.Intersect(*pme, r, nu, rs, g, m_stol))\n\t\t\t\t{\n\t\t\t\t\tpt.m_rs[0] = rs[0];\n\t\t\t\t\tpt.m_rs[1] = rs[1];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpme = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// find the intersection point with the secondary surface\n\t\t\tif (pme == 0 && bupseg) pme = np.Project(r, nu, rs);\n\t\t\t\n\t\t\tpt.m_pme = pme;\n\t\t\tpt.m_nu = nu;\n\t\t\tpt.m_rs[0] = rs[0];\n\t\t\tpt.m_rs[1] = rs[1];\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// the node could potentially be in contact\n\t\t\t\t// find the global location of the intersection point\n\t\t\t\tvec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n\t\t\t\t\n\t\t\t\t// calculate the gap function\n\t\t\t\t// NOTE: this has the opposite sign compared\n\t\t\t\t// to Gerard's notes.\n\t\t\t\tdouble g = nu*(r - q);\n\t\t\t\t\n\t\t\t\tdouble eps = m_epsn*pt.m_epsn*psf;\n\t\t\t\t\n\t\t\t\tLn = pt.m_Lmd + eps*g;\n\t\t\t\t\n\t\t\t\tpt.m_gap = (g <= R? g : 0);\n\t\t\t\t\n\t\t\t\tif ((Ln >= 0) && (g <= R))\n\t\t\t\t{\n\t\t\t\t\t\n\t\t\t\t\t// calculate the pressure gap function\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\t\tint mid = ms.m_solu[pme->m_lid];\n\t\t\t\t\tbool msolu = (mid > -1) ? true : false;\n\t\t\t\t\tif (sporo && mporo) {\n\t\t\t\t\t\tdouble pm[FEElement::MAX_NODES];\n\t\t\t\t\t\tfor (int k=0; k<pme->Nodes(); ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n\t\t\t\t\t\tdouble p2 = pme->eval(pm, rs[0], rs[1]);\n\t\t\t\t\t\tpt.m_pg = p1 - p2;\n\t\t\t\t\t}\n\t\t\t\t\tif (ssolu && msolu && (sid == mid)) {\n\t\t\t\t\t\tdouble cm[FEElement::MAX_NODES];\n\t\t\t\t\t\tfor (int k=0; k<pme->Nodes(); ++k) cm[k] = mesh.Node(pme->m_node[k]).get(m_dofC + sid);\n\t\t\t\t\t\tdouble c2 = pme->eval(cm, rs[0], rs[1]);\n\t\t\t\t\t\tpt.m_cg[0] = c1 - c2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n                    pt.m_Lmd = 0;\n\t\t\t\t\tpt.m_gap = 0;\n\t\t\t\t\tpt.m_pme = 0;\n\t\t\t\t\tif (sporo) {\n                        pt.m_Lmp = 0;\n\t\t\t\t\t\tpt.m_pg = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (ssolu) {\n                        pt.m_Lmc[0] = 0;\n\t\t\t\t\t\tpt.m_cg[0] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// the node is not in contact\n\t\t\t\tpt.m_Lmd = 0;\n\t\t\t\tpt.m_gap = 0;\n\t\t\t\tif (sporo) {\n\t\t\t\t\tpt.m_Lmp = 0;\n\t\t\t\t\tpt.m_pg = 0;\n\t\t\t\t}\n\t\t\t\tif (ssolu) {\n\t\t\t\t\tpt.m_Lmc[0] = 0;\n\t\t\t\t\tpt.m_cg[0] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface3::Update()\n{\t\n\tdouble rs[2];\n\n\tFEModel& fem = *GetFEModel();\n\n    // get number of DOFS\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\tdouble R = m_srad*fem.GetMesh().GetBoundingBox().radius();\n\t\n\tstatic int naug = 0;\n\tstatic int biter = 0;\n\n\t// get the iteration number\n\t// we need this number to see if we can do segment updates or not\n\t// also reset number of iterations after each augmentation\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tFESolver* psolver = pstep->GetFESolver();\n\tif (psolver->m_niter == 0) {\n\t\tbiter = 0;\n\t\tnaug = psolver->m_naug;\n        // check update of auto-penalty\n        if (m_bupdtpen) UpdateAutoPenalty();\n\t} else if (psolver->m_naug > naug) {\n\t\tbiter = psolver->m_niter;\n\t\tnaug = psolver->m_naug;\n\t}\n\tint niter = psolver->m_niter - biter;\n\tbool bupseg = ((m_nsegup == 0)? true : (niter <= m_nsegup));\n\t// get the logfile\n\t//\tLogfile& log = GetLogfile();\n\t//\tlog.printf(\"seg_up iteration # %d\\n\", niter+1);\n\t\n\t// project the surfaces onto each other\n\t// this will update the gap functions as well\n    static bool bfirst = true;\n    ProjectSurface(m_ss, m_ms, bupseg, (m_breloc && bfirst));\n\tif (m_btwo_pass || m_ss.m_bporo) ProjectSurface(m_ms, m_ss, bupseg);\n    bfirst = false;\n\t\n\t// Update the net contact pressures\n\tUpdateContactPressures();\n\n    // update node normals\n    m_ss.UpdateNodeNormals();\n    m_ms.UpdateNodeNormals();\n    \n\t// set poro flag\n\tbool bporo = (m_ss.m_bporo || m_ms.m_bporo);\n\t\n\t// only continue if we are doing a poro-elastic simulation\n\tif (bporo == false) return;\n\t\n\t// Now that the nodes have been projected, we need to figure out\n\t// if we need to modify the constraints on the pressure and concentration dofs.\n\t// If the nodes are not in contact, they must be match ambient\n\t// conditions. Since all nodes have been previously marked to be\n\t// under ambient conditions in MarkAmbient(), we just need to reverse\n\t// this setting here, for nodes that are in contact.\n\t\n\t// Next, we loop over each surface, visiting the nodes\n\t// and finding out if that node is in contact or not\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface3& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface3& ms = (np == 0? m_ms : m_ss);\n\t\t\n        // loop over all the nodes of the primary surface\n        for (int n=0; n<ss.Nodes(); ++n) {\n            if (ss.m_pn[n] > 0) {\n                FENode& node = ss.Node(n);\n                int id = node.m_ID[m_dofP];\n                if (id < -1)\n                    // mark node as non-free-draining (= pos ID)\n                    node.m_ID[m_dofP] = -id-2;\n                for (int j=0; j<MAX_CDOFS; ++j) {\n                    id = node.m_ID[m_dofC+j];\n                    if (id < -1)\n                        // mark node as non-ambient (= pos ID)\n                        node.m_ID[m_dofC+j] = -id-2;\n                }\n            }\n        }\n        \n\t\t// loop over all nodes of the secondary surface\n\t\t// the secondary surface is trickier since we need\n\t\t// to look at the primary's surface projection\n\t\tif (ms.m_bporo) {\n            // initialize projection data\n            FENormalProjection np(ss);\n            np.SetTolerance(m_stol);\n            np.SetSearchRadius(m_srad);\n            np.Init();\n            \n\t\t\tfor (int n = 0; n<ms.Nodes(); ++n)\n\t\t\t{\n\t\t\t\t// get the node\n\t\t\t\tFENode& node = ms.Node(n);\n\t\t\t\t\n\t\t\t\t// project it onto the primary surface\n\t\t\t\tFESurfaceElement* pse = np.Project(node.m_rt, ms.m_nn[n], rs);\n\t\t\t\t\n\t\t\t\tif (pse)\n\t\t\t\t{\n\t\t\t\t\t// we found an element, so let's see if it's even remotely close to contact\n\t\t\t\t\t// find the global location of the intersection point\n\t\t\t\t\tvec3d q = ss.Local2Global(*pse, rs[0], rs[1]);\n\t\t\t\t\t\n\t\t\t\t\t// calculate the gap function\n\t\t\t\t\tdouble g = ms.m_nn[n]*(node.m_rt - q);\n\t\t\t\t\t\n\t\t\t\t\tif (fabs(g) <= R)\n\t\t\t\t\t{\n\t\t\t\t\t\t// we found an element so let's calculate the nodal traction values for this element\n\t\t\t\t\t\t// get the normal tractions at the nodes\n                        double tn[FEElement::MAX_NODES];\n                        for (int i=0; i<pse->Nodes(); ++i)\n                            tn[i] = ss.m_pn[pse->m_lnode[i]];\n\t\t\t\t\t\t\n\t\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\t\tdouble tp = pse->eval(tn, rs[0], rs[1]);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// if tp > 0, mark node as non-ambient. (= pos ID)\n\t\t\t\t\t\tif (tp > 0)  {\n\t\t\t\t\t\t\tint id = node.m_ID[m_dofP];\n\t\t\t\t\t\t\tif (id < -1)\n\t\t\t\t\t\t\t\t// mark as non-ambient\n\t\t\t\t\t\t\t\tnode.m_ID[m_dofP] = -id-2;\n\t\t\t\t\t\t\tfor (int j = 0; j<MAX_CDOFS; ++j) {\n\t\t\t\t\t\t\t\tid = node.m_ID[m_dofC+j];\n\t\t\t\t\t\t\t\tif (id < -1)\n\t\t\t\t\t\t\t\t\t// mark as non-ambient\n\t\t\t\t\t\t\t\t\tnode.m_ID[m_dofC+j] = -id-2;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tvector<int> sLM, mLM, LM, en;\n\tvector<double> fe;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN];\n\tdouble N[10*MN];\n\n\tFEModel& fem = *GetFEModel();\n\n\tdouble dt = fem.GetTime().timeIncrement;\n\t\n    double psf = GetPenaltyScaleFactor();\n    \n    m_ss.m_Ft = vec3d(0, 0, 0);\n    m_ms.m_Ft = vec3d(0, 0, 0);\n    \n\t// loop over the nr of passes\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\t// get primary and secondary surface\n\t\tFESlidingSurface3& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface3& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all primary surface elements\n\t\tfor (int i = 0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the surface element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\n\t\t\tbool sporo = ss.m_poro[i];\n\t\t\tint sid = ss.m_solu[i];\n\t\t\tbool ssolu = (sid > -1) ? true : false;\n\t\t\t\n\t\t\t// get the nr of nodes and integration points\n\t\t\tint nseln = se.Nodes();\n\t\t\tint nint = se.GaussPoints();\n\t\t\t\n\t\t\t// copy the LM vector; we'll need it later\n\t\t\tss.UnpackLM(se, sLM);\n\t\t\t\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tss.CoBaseVectors(se, j, g);\n\t\t\t\t\n\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\t\t\t\t\n\t\t\t\t// integration weights\n\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t}\n\t\t\t\n\t\t\t// loop over all integration points\n\t\t\t// note that we are integrating over the current surface\n\t\t\tfor (int j = 0; j<nint; ++j)\n\t\t\t{\n                FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\t\tint mid = ms.m_solu[pme->m_lid];\n\t\t\t\t\tbool msolu = (mid > -1) ? true : false;\n\t\t\t\t\t\n\t\t\t\t\t// get the nr of secondary surface element nodes\n\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\n\t\t\t\t\t// copy LM vector\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\t\t\t\t\t\n\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\tint ndof = 3*(nseln + nmeln);\n\t\t\t\t\t\n\t\t\t\t\t// build the LM vector\n\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\tfor (int k = 0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (int k = 0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// build the en vector\n\t\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\t\tfor (int k = 0; k<nseln; ++k) en[k] = se.m_node[k];\n\t\t\t\t\tfor (int k = 0; k<nmeln; ++k) en[k + nseln] = me.m_node[k];\n\t\t\t\t\t\n\t\t\t\t\t// get element shape functions\n\t\t\t\t\tHs = se.H(j);\n\t\t\t\t\t\n\t\t\t\t\t// get secondary surface element shape functions\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\t\t\t\t\t\n\t\t\t\t\t// get normal vector\n\t\t\t\t\tvec3d nu = pt.m_nu;\n\t\t\t\t\t\n\t\t\t\t\t// gap function\n\t\t\t\t\tdouble g = pt.m_gap;\n\t\t\t\t\t\n\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\tdouble Lm = pt.m_Lmd;\n\t\t\t\t\t\n\t\t\t\t\t// penalty \n\t\t\t\t\tdouble eps = m_epsn*pt.m_epsn*psf;\n\t\t\t\t\t\n\t\t\t\t\t// contact traction\n\t\t\t\t\tdouble tn = Lm + eps*g;\n\t\t\t\t\ttn = MBRACKET(tn);\n\t\t\t\t\t\n\t\t\t\t\t// calculate the force vector\n\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\tzero(fe);\n\t\t\t\t\t\n\t\t\t\t\tfor (int k = 0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[3*k  ] = -Hs[k]*nu.x;\n\t\t\t\t\t\tN[3*k+1] = -Hs[k]*nu.y;\n\t\t\t\t\t\tN[3*k+2] = -Hs[k]*nu.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (int k = 0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[3*(k+nseln)  ] = Hm[k]*nu.x;\n\t\t\t\t\t\tN[3*(k+nseln)+1] = Hm[k]*nu.y;\n\t\t\t\t\t\tN[3*(k+nseln)+2] = Hm[k]*nu.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (int k = 0; k<ndof; ++k) fe[k] += tn*N[k] * detJ[j] * w[j];\n\t\t\t\t\t\n                    for (int k=0; k<nseln; ++k)\n                    {\n                        ss.m_Ft += vec3d(fe[k*3], fe[k*3+1], fe[k*3+2]);\n                    }\n                    \n                    for (int k = 0; k<nmeln; ++k)\n                    {\n                        ms.m_Ft += vec3d(fe[(k + nseln) * 3], fe[(k + nseln) * 3 + 1], fe[(k + nseln) * 3 + 2]);\n                    }\n                    \n\t\t\t\t\t// assemble the global residual\n\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t\n\t\t\t\t\t// do the biphasic stuff\n\t\t\t\t\tif (tn > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sporo && mporo)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// calculate nr of pressure dofs\n\t\t\t\t\t\t\tint ndof = nseln + nmeln;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// calculate the flow rate\n\t\t\t\t\t\t\tdouble epsp = m_epsp*pt.m_epsp*psf;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// fill the LM\n\t\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\tfor (int k = 0; k<nseln; ++k) LM[k] = sLM[3 * nseln + k];\n\t\t\t\t\t\t\tfor (int k = 0; k<nmeln; ++k) LM[k + nseln] = mLM[3 * nmeln + k];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// fill the force array\n\t\t\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\t\t\tzero(fe);\n\t\t\t\t\t\t\tfor (int k = 0; k<nseln; ++k) N[k] = Hs[k];\n\t\t\t\t\t\t\tfor (int k = 0; k<nmeln; ++k) N[k + nseln] = -Hm[k];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (int k = 0; k<ndof; ++k) fe[k] += dt*wn*N[k] * detJ[j] * w[j];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// assemble residual\n\t\t\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ssolu && msolu && (sid == mid))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// calculate nr of concentration dofs\n\t\t\t\t\t\t\tint ndof = nseln + nmeln;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// calculate the flow rate\n\t\t\t\t\t\t\tdouble epsc = m_epsc*pt.m_epsc[0]*psf;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble jn = pt.m_Lmc[0] + epsc*pt.m_cg[0];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// fill the LM\n\t\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\tfor (int k=0; k<nseln; ++k) LM[k        ] = sLM[(4+sid)*nseln+k];\n\t\t\t\t\t\t\tfor (int k=0; k<nmeln; ++k) LM[k + nseln] = mLM[(4+mid)*nmeln+k];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// fill the force array\n\t\t\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\t\t\tzero(fe);\n\t\t\t\t\t\t\tfor (int k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n\t\t\t\t\t\t\tfor (int k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (int k = 0; k<ndof; ++k) fe[k] += dt*jn*N[k] * detJ[j] * w[j];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// assemble residual\n\t\t\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint i, j, k, l;\n\tvector<int> sLM, mLM, LM, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN];\n\tdouble pt[MN], dpr[MN], dps[MN];\n\tdouble ct[MN], dcr[MN], dcs[MN];\n\tdouble N[10*MN];\n\tFEElementMatrix ke;\n\n\tFEModel& fem = *GetFEModel();\n\n    double psf = GetPenaltyScaleFactor();\n    \n\t// see how many reformations we've had to do so far\n\tint nref = GetSolver()->m_nref;\n\t\n\t// set higher order stiffness mutliplier\n\t// NOTE: this algrotihm doesn't really need this\n\t// but I've added this functionality to compare with the other contact \n\t// algorithms and to see the effect of the different stiffness contributions\n\tdouble knmult = m_knmult;\n\tif (m_knmult < 0)\n\t{\n\t\tint ni = int(-m_knmult);\n\t\tif (nref >= ni)\n\t\t{\n\t\t\tknmult = 1; \n\t\t\tfeLog(\"Higher order stiffness terms included.\\n\");\n\t\t}\n\t\telse knmult = 0;\n\t}\n\t\n\t// do single- or two-pass\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np < npass; ++np)\n\t{\n\t\t// get the primary and secondary surface\n\t\tFESlidingSurface3& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface3& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all primary surface elements\n\t\tfor (i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the next element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\n\t\t\tbool sporo = ss.m_poro[i];\n\t\t\tint sid = ss.m_solu[i];\n\t\t\tbool ssolu = (sid > -1) ? true : false;\n\t\t\t\n\t\t\t// get nr of nodes and integration points\n\t\t\tint nseln = se.Nodes();\n\t\t\tint nint = se.GaussPoints();\n\n\t\t\tdouble pn[FEElement::MAX_NODES], cn[FEElement::MAX_NODES];\n\t\t\tfor (j=0; j<nseln; ++j)\n\t\t\t{\n\t\t\t\tpn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n\t\t\t\tcn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofC + sid);\n\t\t\t}\n\t\t\t\n\t\t\t// copy the LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\t\t\t\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tss.CoBaseVectors(se, j, g);\n\t\t\t\t\n\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\t\t\t\t\n\t\t\t\t// integration weights\n\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t\t\n\t\t\t\t// pressure\n\t\t\t\tif (sporo)\n\t\t\t\t{\n\t\t\t\t\tpt[j] = se.eval(pn, j);\n\t\t\t\t\tdpr[j] = se.eval_deriv1(pn, j);\n\t\t\t\t\tdps[j] = se.eval_deriv2(pn, j);\n\t\t\t\t}\n\t\t\t\t// concentration\n\t\t\t\tif (ssolu)\n\t\t\t\t{\n\t\t\t\t\tct[j] = se.eval(cn, j);\n\t\t\t\t\tdcr[j] = se.eval_deriv1(cn, j);\n\t\t\t\t\tdcs[j] = se.eval_deriv2(cn, j);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// loop over all integration points\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n                FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pme;\n\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\t\tint mid = ms.m_solu[pme->m_lid];\n\t\t\t\t\tbool msolu = (mid > -1) ? true : false;\n\t\t\t\t\t\n\t\t\t\t\t// get the nr of nodes\n\t\t\t\t\tint nmeln = me.Nodes();\n\n\t\t\t\t\t// nodal data\n\t\t\t\t\tdouble pm[FEElement::MAX_NODES], cm[FEElement::MAX_NODES];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tpm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n\t\t\t\t\t\tcm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofC + mid);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// copy the LM vector\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\t\t\t\t\t\n\t\t\t\t\tint ndpn;\t// number of dofs per node\n\t\t\t\t\tint ndof;\t// number of dofs in stiffness matrix\n\t\t\t\t\t\n\t\t\t\t\tif (ssolu && msolu && (sid == mid)) {\n\t\t\t\t\t\t// calculate dofs for biphasic-solute contact\n\t\t\t\t\t\tndpn = 5;\n\t\t\t\t\t\tndof = ndpn*(nseln+nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[ndpn*k  ] = sLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[ndpn*k+1] = sLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[ndpn*k+2] = sLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[ndpn*k+3] = sLM[3*nseln+k];\t\t// p-dof\n\t\t\t\t\t\t\tLM[ndpn*k+4] = sLM[(4+sid)*nseln+k];\t\t// c-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+4] = mLM[(4+mid)*nmeln+k];\t\t// c-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\telse if (sporo && mporo) {\n\t\t\t\t\t\t// calculate dofs for biphasic contact\n\t\t\t\t\t\tndpn = 4;\n\t\t\t\t\t\tndof = ndpn*(nseln+nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[ndpn*k  ] = sLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[ndpn*k+1] = sLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[ndpn*k+2] = sLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[ndpn*k+3] = sLM[3*nseln+k];\t\t// p-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[ndpn*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\telse {\n\t\t\t\t\t\t// calculate dofs for elastic contact\n\t\t\t\t\t\tndpn = 3;\n\t\t\t\t\t\tndof = ndpn*(nseln + nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// build the en vector\n\t\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\t\tfor (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\t\t\t\t\t\n\t\t\t\t\t// shape functions\n\t\t\t\t\tHs = se.H(j);\n\t\t\t\t\t\n\t\t\t\t\t// secondary surface element shape functions\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\t\t\t\t\t\n\t\t\t\t\t// get normal vector\n\t\t\t\t\tvec3d nu = pt.m_nu;\n\t\t\t\t\t\n\t\t\t\t\t// gap function\n\t\t\t\t\tdouble g = pt.m_gap;\n\t\t\t\t\t\n\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\tdouble Lm = pt.m_Lmd;\n\t\t\t\t\t\n\t\t\t\t\t// penalty \n\t\t\t\t\tdouble eps = m_epsn*pt.m_epsn*psf;\n\t\t\t\t\t\n\t\t\t\t\t// contact traction\n\t\t\t\t\tdouble tn = Lm + eps*g;\n\t\t\t\t\ttn = MBRACKET(tn);\n\t\t\t\t\t\n\t\t\t\t\t//\tdouble dtn = m_eps*HEAVYSIDE(Lm + eps*g);\n\t\t\t\t\tdouble dtn = (tn > 0.? eps :0.);\n\t\t\t\t\t\n\t\t\t\t\t// create the stiffness matrix\n\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\t\t\n\t\t\t\t\t// --- S O L I D - S O L I D   C O N T A C T ---\n\t\t\t\t\t\n\t\t\t\t\t// a. NxN-term\n\t\t\t\t\t//------------------------------------\n\t\t\t\t\t\n\t\t\t\t\t// calculate the N-vector\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[ndpn*k  ] = Hs[k]*nu.x;\n\t\t\t\t\t\tN[ndpn*k+1] = Hs[k]*nu.y;\n\t\t\t\t\t\tN[ndpn*k+2] = Hs[k]*nu.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[ndpn*(k+nseln)  ] = -Hm[k]*nu.x;\n\t\t\t\t\t\tN[ndpn*(k+nseln)+1] = -Hm[k]*nu.y;\n\t\t\t\t\t\tN[ndpn*(k+nseln)+2] = -Hm[k]*nu.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (ndpn == 5) {\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*k+3] = 0;\n\t\t\t\t\t\t\tN[ndpn*k+4] = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+3] = 0;\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+4] = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (ndpn == 4) {\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t\tN[ndpn*k+3] = 0;\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+3] = 0;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<ndof; ++k)\n\t\t\t\t\t\tfor (l=0; l<ndof; ++l) ke[k][l] += dtn*N[k]*N[l]*detJ[j]*w[j];\n\t\t\t\t\t\n\t\t\t\t\t// b. A-term\n\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n\t\t\t\t\t\n\t\t\t\t\tdouble* Gr = se.Gr(j);\n\t\t\t\t\tdouble* Gs = se.Gs(j);\n\t\t\t\t\tvec3d gs[2];\n\t\t\t\t\tss.CoBaseVectors(se, j, gs);\n\t\t\t\t\t\n\t\t\t\t\tmat3d S1, S2;\n\t\t\t\t\tS1.skew(gs[0]);\n\t\t\t\t\tS2.skew(gs[1]);\n\t\t\t\t\tmat3d As[FEElement::MAX_NODES];\n\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\tAs[l] = S2*Gr[l] - S1*Gs[l];\n\t\t\t\t\t\n\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t{\t// non-symmetric\n\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn  ] -= knmult*tn*w[j]*N[k]*As[l][0][0];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+1] -= knmult*tn*w[j]*N[k]*As[l][0][1];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+2] -= knmult*tn*w[j]*N[k]*As[l][0][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn  ] -= knmult*tn*w[j]*N[k]*As[l][1][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+1] -= knmult*tn*w[j]*N[k]*As[l][1][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+2] -= knmult*tn*w[j]*N[k]*As[l][1][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn  ] -= knmult*tn*w[j]*N[k]*As[l][2][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+1] -= knmult*tn*w[j]*N[k]*As[l][2][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+2] -= knmult*tn*w[j]*N[k]*As[l][2][2];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} \n\t\t\t\t\telse \n\t\t\t\t\t{\t// symmetric\n\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][0];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][1];\n\t\t\t\t\t\t\t\tke[k*ndpn  ][l*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+1][l*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][0];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][1];\n\t\t\t\t\t\t\t\tke[k*ndpn+2][l*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][k*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][0];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][k*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][1];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][k*ndpn  ] -= 0.5*knmult*tn*w[j]*N[k]*As[l][0][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][k*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][0];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][k*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][1];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][k*ndpn+1] -= 0.5*knmult*tn*w[j]*N[k]*As[l][1][2];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][k*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][0];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][k*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][1];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][k*ndpn+2] -= 0.5*knmult*tn*w[j]*N[k]*As[l][2][2];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// c. M-term\n\t\t\t\t\t//---------------------------------------\n\t\t\t\t\t\n\t\t\t\t\tvec3d Gm[2];\n\t\t\t\t\tms.ContraBaseVectors(me, r, s, Gm);\n\t\t\t\t\t\n\t\t\t\t\t// evaluate secondary surface normal\n\t\t\t\t\tvec3d mnu = Gm[0] ^ Gm[1];\n\t\t\t\t\tmnu.unit();\n\t\t\t\t\t\n\t\t\t\t\tdouble Hmr[FEElement::MAX_NODES], Hms[FEElement::MAX_NODES];\n\t\t\t\t\tme.shape_deriv(Hmr, Hms, r, s);\n\t\t\t\t\tvec3d mm[FEElement::MAX_NODES];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) \n\t\t\t\t\t\tmm[k] = Gm[0]*Hmr[k] + Gm[1]*Hms[k];\n\t\t\t\t\t\n\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t{\t// non-symmetric\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn  ] += tn*knmult*detJ[j]*w[j]*mnu.x*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+1] += tn*knmult*detJ[j]*w[j]*mnu.x*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+2] += tn*knmult*detJ[j]*w[j]*mnu.x*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn  ] += tn*knmult*detJ[j]*w[j]*mnu.y*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+1] += tn*knmult*detJ[j]*w[j]*mnu.y*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+2] += tn*knmult*detJ[j]*w[j]*mnu.y*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn  ] += tn*knmult*detJ[j]*w[j]*mnu.z*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+1] += tn*knmult*detJ[j]*w[j]*mnu.z*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+2] += tn*knmult*detJ[j]*w[j]*mnu.z*mm[k].z*N[l];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t// symmetric\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn  ][l*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+1][l*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[(k+nseln)*ndpn+2][l*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].z*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][(k+nseln)*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][(k+nseln)*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].x*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][(k+nseln)*ndpn  ] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].x*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][(k+nseln)*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][(k+nseln)*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].y*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][(k+nseln)*ndpn+1] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].y*N[l];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[l*ndpn  ][(k+nseln)*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.x*mm[k].z*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+1][(k+nseln)*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.y*mm[k].z*N[l];\n\t\t\t\t\t\t\t\tke[l*ndpn+2][(k+nseln)*ndpn+2] += 0.5*knmult*tn*detJ[j]*w[j]*mnu.z*mm[k].z*N[l];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// --- B I P H A S I C - S O L U T E  S T I F F N E S S ---\n\t\t\t\t\tif (ssolu && msolu && (sid == mid))\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble dt = fem.GetTime().timeIncrement;\n\t\t\t\t\t\t\n\t\t\t\t\t\tdouble epsp = (tn > 0) ? m_epsp*pt.m_epsp*psf : 0.;\n\t\t\t\t\t\tdouble epsc = (tn > 0) ? m_epsc*pt.m_epsc[0]*psf : 0.;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- S O L I D - P R E S S U R E / S O L U T E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// a. q-term\n\t\t\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble dpmr, dpms;\n\t\t\t\t\t\t\tdpmr = me.eval_deriv1(pm, r, s);\n\t\t\t\t\t\t\tdpms = me.eval_deriv2(pm, r, s);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble dcmr, dcms;\n\t\t\t\t\t\t\tdcmr = me.eval_deriv1(cm, r, s);\n\t\t\t\t\t\t\tdcms = me.eval_deriv2(cm, r, s);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l  ] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].x + dpms*Gm[1].x);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+1] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].y + dpms*Gm[1].y);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+2] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].z + dpms*Gm[1].z);\n\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 4][ndpn*l  ] += dt*w[j]*detJ[j]*epsc*N[k]*N[l]*(dcmr*Gm[0].x + dcms*Gm[1].x);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 4][ndpn*l+1] += dt*w[j]*detJ[j]*epsc*N[k]*N[l]*(dcmr*Gm[0].y + dcms*Gm[1].y);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 4][ndpn*l+2] += dt*w[j]*detJ[j]*epsc*N[k]*N[l]*(dcmr*Gm[0].z + dcms*Gm[1].z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\t\t\t\t\t\t\tdouble jn = pt.m_Lmc[0] + epsc*pt.m_cg[0];\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// b. A-term\n\t\t\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l  ] -= dt*w[j]*wn*N[k]*(As[l][0][0]*nu.x + As[l][0][1]*nu.y + As[l][0][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+1] -= dt*w[j]*wn*N[k]*(As[l][1][0]*nu.x + As[l][1][1]*nu.y + As[l][1][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+2] -= dt*w[j]*wn*N[k]*(As[l][2][0]*nu.x + As[l][2][1]*nu.y + As[l][2][2]*nu.z);\n\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 4][ndpn*l  ] -= dt*w[j]*jn*N[k]*(As[l][0][0]*nu.x + As[l][0][1]*nu.y + As[l][0][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 4][ndpn*l+1] -= dt*w[j]*jn*N[k]*(As[l][1][0]*nu.x + As[l][1][1]*nu.y + As[l][1][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 4][ndpn*l+2] -= dt*w[j]*jn*N[k]*(As[l][2][0]*nu.x + As[l][2][1]*nu.y + As[l][2][2]*nu.z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// c. m-term\n\t\t\t\t\t\t\t//---------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 3][ndpn*l  ] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].x;\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 3][ndpn*l+1] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].y;\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 3][ndpn*l+2] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].z;\n\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 4][ndpn*l  ] += dt*w[j]*detJ[j]*jn*N[l]*mm[k].x;\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 4][ndpn*l+1] += dt*w[j]*detJ[j]*jn*N[l]*mm[k].y;\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 4][ndpn*l+2] += dt*w[j]*detJ[j]*jn*N[l]*mm[k].z;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\t// calculate the N-vector\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*k+3] = Hs[k];\n\t\t\t\t\t\t\tN[ndpn*k+4] = Hs[k];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+3] = -Hm[k];\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+4] = -Hm[k];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=3; k<ndof; k+=ndpn)\n\t\t\t\t\t\t\tfor (l=3; l<ndof; l+=ndpn) ke[k][l] -= dt*epsp*w[j]*detJ[j]*N[k]*N[l];\n\n\t\t\t\t\t\t// --- C O N C E N T R A T I O N - C O N C E N T R A T I O N   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=4; k<ndof; k+=ndpn)\n\t\t\t\t\t\t\tfor (l=4; l<ndof; l+=ndpn) ke[k][l] -= dt*epsc*w[j]*detJ[j]*N[k]*N[l];\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// --- B I P H A S I C   S T I F F N E S S ---\n\t\t\t\t\telse if (sporo && mporo)\n\t\t\t\t\t{\n\t\t\t\t\t\t// the variable dt is either the timestep or one\n\t\t\t\t\t\t// depending on whether we are using the symmetric\n\t\t\t\t\t\t// poro version or not.\n\t\t\t\t\t\tdouble dt = fem.GetTime().timeIncrement;\n\t\t\t\t\t\t\n\t\t\t\t\t\tdouble epsp = (tn > 0) ? m_epsp*pt.m_epsp*psf : 0.;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- S O L I D - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// a. q-term\n\t\t\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble dpmr, dpms;\n\t\t\t\t\t\t\tdpmr = me.eval_deriv1(pm, r, s);\n\t\t\t\t\t\t\tdpms = me.eval_deriv2(pm, r, s);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l  ] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].x + dpms*Gm[1].x);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+1] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].y + dpms*Gm[1].y);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+2] += dt*w[j]*detJ[j]*epsp*N[k]*N[l]*(dpmr*Gm[0].z + dpms*Gm[1].z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// b. A-term\n\t\t\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t\tfor (k=0; k<nseln+nmeln; ++k)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l  ] -= dt*w[j]*wn*N[k]*(As[l][0][0]*nu.x + As[l][0][1]*nu.y + As[l][0][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+1] -= dt*w[j]*wn*N[k]*(As[l][1][0]*nu.x + As[l][1][1]*nu.y + As[l][1][2]*nu.z);\n\t\t\t\t\t\t\t\t\tke[ndpn*k + 3][ndpn*l+2] -= dt*w[j]*wn*N[k]*(As[l][2][0]*nu.x + As[l][2][1]*nu.y + As[l][2][2]*nu.z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// c. m-term\n\t\t\t\t\t\t\t//---------------------------------------\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln+nmeln; ++l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 3][ndpn*l  ] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].x;\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 3][ndpn*l+1] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].y;\n\t\t\t\t\t\t\t\t\tke[ndpn*(k+nseln) + 3][ndpn*l+2] += dt*w[j]*detJ[j]*wn*N[l]*mm[k].z;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\t// calculate the N-vector\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*k+3] = Hs[k];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tN[ndpn*(k+nseln)+3] = -Hm[k];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=3; k<ndof; k+=ndpn)\n\t\t\t\t\t\t\tfor (l=3; l<ndof; l+=ndpn) ke[k][l] -= dt*epsp*w[j]*detJ[j]*N[k]*N[l];\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// assemble the global stiffness\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::UpdateContactPressures()\n{\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurface3& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurface3& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all elements of the primary surface\n\t\tfor (int n=0; n<ss.Elements(); ++n)\n\t\t{\n\t\t\tFESurfaceElement& el = ss.Element(n);\n\t\t\tint nint = el.GaussPoints();\n\t\t\t\n\t\t\t// get the normal tractions at the integration points\n\t\t\tdouble gap, eps;\n\t\t\tfor (int i=0; i<nint; ++i) \n\t\t\t{\n                FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\n\t\t\t\tgap = pt.m_gap;\n\t\t\t\teps = m_epsn*pt.m_epsn;\n\t\t\t\tpt.m_Ln = MBRACKET(pt.m_Lmd + eps*gap);\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (m_btwo_pass && pme)\n\t\t\t\t{\n\t\t\t\t\tint mint = pme->GaussPoints();\n\t\t\t\t\tdouble ti[FEElement::MAX_NODES];\n\t\t\t\t\tfor (int j=0; j<mint; ++j) {\n                        FEMultiphasicContactPoint& md = static_cast<FEMultiphasicContactPoint&>(*pme->GetMaterialPoint(j));\n\n\t\t\t\t\t\tgap = md.m_gap;\n\t\t\t\t\t\teps = m_epsn*md.m_epsn;\n\t\t\t\t\t\tti[j] = MBRACKET(md.m_Lmd + m_epsn*md.m_epsn*md.m_gap);\n\t\t\t\t\t}\n\t\t\t\t\t// project the data to the nodes\n\t\t\t\t\tdouble tn[FEElement::MAX_NODES];\n\t\t\t\t\tpme->FEElement::project_to_nodes(ti, tn);\n\t\t\t\t\t// now evaluate the traction at the intersection point\n\t\t\t\t\tdouble Ln = pme->eval(tn, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t\tpt.m_Ln += MBRACKET(Ln);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n        ss.EvaluateNodalContactPressures();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterface3::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tdouble Ln, Lp, Lc;\n\tbool bconv = true;\n\t\n\tbool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n\tbool bsolu = (m_ss.m_bsolu && m_ms.m_bsolu);\n\n\tint NS = m_ss.Elements();\n\tint NM = m_ms.Elements();\n\t\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\t// a. normal component\n\tdouble normL0 = 0, normP = 0, normDP = 0, normC = 0, normDC = 0;\n\tfor (int i=0; i<NS; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tfor (int j=0; j<el.GaussPoints(); ++j)\n\t\t{\n            FEMultiphasicContactPoint& ds = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += ds.m_Lmd*ds.m_Lmd;\n\t\t}\n\t}\n\tfor (int i=0; i<NM; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j=0; j<el.GaussPoints(); ++j)\n\t\t{\n            FEMultiphasicContactPoint& dm = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += dm.m_Lmd*dm.m_Lmd;\n\t\t}\n\t}\n\t\n\t// b. gap component\n\t// (is calculated during update)\n\tdouble maxgap = 0, maxpg = 0, maxcg = 0;\n\t\n\t// update Lagrange multipliers\n\tdouble normL1 = 0, eps, epsp, epsc;\n    for (int i=0; i<m_ss.Elements(); ++i) {\n        FESurfaceElement& el = m_ss.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\t// update Lagrange multipliers on primary surface\n            if (m_bsmaug) {\n                // replace this multiplier with a smoother version\n                Ln = -(tn[j]*data.m_nu);\n                data.m_Lmd = MBRACKET(Ln);\n                if (m_btwo_pass) data.m_Lmd /= 2;\n            }\n            else {\n                eps = m_epsn*data.m_epsn;\n                Ln = data.m_Lmd + eps*data.m_gap;\n                data.m_Lmd = MBRACKET(Ln);\n            }\n            \n            normL1 += data.m_Lmd*data.m_Lmd;\n            \n            if (m_ss.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*data.m_epsp;\n                    Lp = data.m_Lmp + epsp*data.m_pg;\n                    maxpg = max(maxpg,fabs(data.m_pg));\n                    normDP += data.m_pg*data.m_pg;\n                }\n                data.m_Lmp = Lp;\n            }\n            \n            if (m_ss.m_bsolu) {\n                Lc = 0;\n                if (Ln > 0) {\n                    epsc = m_epsc*data.m_epsc[0];\n                    Lc = data.m_Lmc[0] + epsc*data.m_cg[0];\n                    maxcg = max(maxcg,fabs(data.m_cg[0]));\n                    normDC += data.m_cg[0]*data.m_cg[0];\n                }\n                data.m_Lmc[0] = Lc;\n            }\n            \n            if (Ln > 0) maxgap = max(maxgap,fabs(data.m_gap));\n        }\n    }\n    \n    for (int i=0; i<m_ms.Elements(); ++i) {\n        FESurfaceElement& el = m_ms.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\t// update Lagrange multipliers on secondary surface\n            if (m_bsmaug) {\n                // replace this multiplier with a smoother version\n                Ln = -(tn[j]*data.m_nu);\n                data.m_Lmd = MBRACKET(Ln);\n                if (m_btwo_pass) data.m_Lmd /= 2;\n            }\n            else {\n                eps = m_epsn*data.m_epsn;\n                Ln = data.m_Lmd + eps*data.m_gap;\n                data.m_Lmd = MBRACKET(Ln);\n            }\n            \n            normL1 += data.m_Lmd*data.m_Lmd;\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*data.m_epsp;\n                    Lp = data.m_Lmp + epsp*data.m_pg;\n                    maxpg = max(maxpg,fabs(data.m_pg));\n                    normDP += data.m_pg*data.m_pg;\n                }\n                data.m_Lmp = Lp;\n            }\n            \n            if (m_ms.m_bsolu) {\n                Lc = 0;\n                if (Ln > 0) {\n                    epsc = m_epsc*data.m_epsc[0];\n                    Lc = data.m_Lmc[0] + epsc*data.m_cg[0];\n                    maxcg = max(maxcg,fabs(data.m_cg[0]));\n                    normDC += data.m_cg[0]*data.m_cg[0];\n                }\n                data.m_Lmc[0] = Lc;\n            }\n            \n            if (Ln > 0) maxgap = max(maxgap,fabs(data.m_gap));\n        }\n    }\n    \n\t// normP should be a measure of the fluid pressure at the\n\t// contact interface.  However, since it could be zero,\n\t// use an average measure of the contact traction instead.\n\tnormP = normL1;\n\tnormC = normL1/(m_Rgas*m_Tabs);\n\t\n\t// calculate relative norms\n\tdouble lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0)); \n\tdouble pnorm = (normP != 0 ? (normDP/normP) : normDP); \n\tdouble cnorm = (normC != 0 ? (normDC/normC) : normDC); \n\t\n\t// check convergence\n\tif ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n\tif ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n\tif ((m_ctol > 0) && (bsolu && maxcg > m_ctol)) bconv = false;\n\t\n\tif ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n\tif ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n\tif ((m_atol > 0) && (cnorm > m_atol)) bconv = false;\n\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tfeLog(\" sliding interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\tif (bporo) { feLog(\"    P gap        : %15le\", pnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\"); }\n\tif (bsolu) { feLog(\"    C gap        : %15le\", cnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\"); }\n\t\n\tfeLog(\"    maximum gap  : %15le\", maxgap);\n\tif (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n\tif (bporo) {\n\t\tfeLog(\"    maximum pgap : %15le\", maxpg);\n\t\tif (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n\t}\n\tif (bsolu) {\n\t\tfeLog(\"    maximum cgap : %15le\", maxcg);\n\t\tif (m_ctol > 0) feLog(\"%15le\\n\", m_ctol); else feLog(\"       ***\\n\");\n\t}\n\t\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterface3::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n\n\t// serialize element pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface3::MarkAmbient()\n{\t\n\t// Mark all nodes as free-draining.  This needs to be done for ALL\n\t// contact interfaces prior to executing Update(), where nodes that are\n\t// in contact are subsequently marked as non free-draining.  This ensures\n\t// that for surfaces involved in more than one contact interface, nodes\n\t// that have been marked as non free-draining are not reset to \n\t// free-draining.\n\n    // get number of DOFS\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\tfor (int np=0; np<2; ++np)\n\t{\n\t\tFESlidingSurface3& s = (np == 0? m_ss : m_ms);\n\t\t\n\t\tif (s.m_bporo) {\n\t\t\t// first, mark all nodes as free-draining (= neg. ID)\n\t\t\t// this is done by setting the dof's equation number\n\t\t\t// to a negative number\n\t\t\tfor (int i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tint id = s.Node(i).m_ID[m_dofP];\n\t\t\t\tif (id >= 0) \n\t\t\t\t{\n\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t// mark node as free-draining\n\t\t\t\t\tnode.m_ID[m_dofP] = -id-2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (s.m_bsolu) {\n\t\t\t// first, mark all nodes as free-draining (= neg. ID)\n\t\t\t// this is done by setting the dof's equation number\n\t\t\t// to a negative number\n\t\t\tfor (int i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tfor (int j=0; j<MAX_CDOFS; ++j) {\n\t\t\t\t\tint id = s.Node(i).m_ID[m_dofC+j];\n\t\t\t\t\tif (id >= 0) \n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t\t// mark node as free-draining\n\t\t\t\t\t\tnode.m_ID[m_dofC+j] = -id-2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterface3::SetAmbient()\n{\t\n    // get number of DOFS\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\t// Set the pressure to zero for the free-draining nodes\n\tfor (int np=0; np<2; ++np)\n\t{\n\t\tFESlidingSurface3& s = (np == 0? m_ss : m_ms);\n\t\t\n\t\tif (s.m_bporo) {\n\t\t\t// loop over all nodes\n\t\t\tfor (int i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tif (s.Node(i).m_ID[m_dofP] < -1)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t// set the fluid pressure to ambient condition\n\t\t\t\t\tnode.set(m_dofP, m_ambp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (s.m_bsolu) {\n\t\t\t// loop over all nodes\n\t\t\tfor (int i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tfor (int j=0; j<MAX_CDOFS; ++j)\n\t\t\t\t{\n\t\t\t\t\tif (s.Node(i).m_ID[m_dofC+j] < -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t\t// set the fluid pressure to ambient condition\n\t\t\t\t\t\tnode.set(m_dofC + j, m_ambc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FESlidingInterface3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBiphasicContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingSurface3 : public FEBiphasicContactSurface\n{\npublic:\n\t//! constructor\n\tFESlidingSurface3(FEModel* pfem);\n\t\n\t//! destructor\n\t~FESlidingSurface3();\n\t\n\t//! initialization\n\tbool Init() override;\n\t\n\t//! evaluate net contact force\n\tvec3d GetContactForce() override;\n\t\n\t//! evaluate net contact area\n\tdouble GetContactArea() override;\n    \n\t//! evaluate net fluid force\n\tvec3d GetFluidForce() override;\n\t\n\t//! calculate the nodal normals\n\tvoid UpdateNodeNormals();\n\t\n\t// data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\tvoid SetPoroMode(bool bporo) { m_bporo = bporo; }\n\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\npublic:\n    void GetContactTraction(int nface, vec3d& pt) override;\n\tvoid GetNodalContactPressure(int nface, double* pg) override;\n\tvoid GetNodalContactTraction(int nface, vec3d* tn) override;\n    void EvaluateNodalContactPressures();\n\nprivate:\n\tvoid GetContactPressure(int nface, double& pg);\n\t\npublic:\n\tbool\t\t\t\t\t\tm_bporo;\t//!< set poro-mode\n\tbool\t\t\t\t\t\tm_bsolu;\t//!< set solute-mode\n\t\n\tvector<bool>\t\t\t\tm_poro;\t//!< surface element poro status\n\tvector<int>\t\t\t\t\tm_solu;\t//!< surface element solute id\n\n\tvector<vec3d>\t\tm_nn;\t//!< node normals\n    vector<double>      m_pn;   //!< nodal contact pressures\n    \n    vec3d\tm_Ft;\t//!< total contact force (from equivalent nodal forces)\n\nprotected:\n\tint\tm_dofC;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingInterface3 :\tpublic FEContactInterface\n{\npublic:\n\t//! constructor\n\tFESlidingInterface3(FEModel* pfem);\n\t\n\t//! destructor\n\t~FESlidingInterface3();\n\t\n\t//! initialization\n\tbool Init() override;\n\t\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! calculate contact pressures for file output\n\tvoid UpdateContactPressures();\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\t//! mark ambient condition \n\tvoid MarkAmbient();\n\t\n\t//! set ambient condition \n\tvoid SetAmbient();\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FESlidingSurface3& ss, FESlidingSurface3& ms, bool bupseg, bool bmove = false);\n\t\n\t//! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n\tvoid CalcAutoPenalty(FESlidingSurface3& s);\n\t\n\tvoid CalcAutoPressurePenalty(FESlidingSurface3& s);\n\tdouble AutoPressurePenalty(FESurfaceElement& el, FESlidingSurface3& s);\n\t\n\tvoid CalcAutoConcentrationPenalty(FESlidingSurface3& s);\n\tdouble AutoConcentrationPenalty(FESurfaceElement& el, FESlidingSurface3& s);\n\npublic:\n\tFESlidingSurface3\tm_ss;\t//!< primary surface\n\tFESlidingSurface3\tm_ms;\t//!< secondary surface\n\t\n\tint\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n\tbool\t\t\tm_btwo_pass;\t//!< two-pass flag\n\tdouble\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\t\t\tm_gtol;\t\t\t//!< gap tolerance\n\tdouble\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n\tdouble\t\t\tm_ctol;\t\t\t//!< concentration gap tolerance\n\tdouble\t\t\tm_stol;\t\t\t//!< search tolerance\n\tbool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n\tdouble\t\t\tm_srad;\t\t\t//!< contact search radius\n\tint\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n\tint\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n\tint\t\t\t\tm_nsegup;\t\t//!< segment update parameter\n    bool\t\t\tm_breloc;\t\t//!< node relocation on startup\n    bool            m_bsmaug;       //!< smooth augmentation\n\t\n\tdouble\t\t\tm_epsn;\t\t//!< normal penalty factor\n\tbool\t\t\tm_bautopen;\t//!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n\n\t// biphasic-solute contact parameters\n\tdouble\tm_epsp;\t\t//!< fluid volumetric flow rate penalty\n\tdouble\tm_epsc;\t\t//!< solute molar flow rate penalty\n\tdouble\tm_Rgas;\t\t//!< universal gas constant\n\tdouble\tm_Tabs;\t\t//!< absolute temperature\n\tdouble\tm_ambp;\t\t//!< ambient pressure\n\tdouble\tm_ambc;\t\t//!< ambient concentration\n\nprotected:\n\tint\tm_dofP;\n\tint\tm_dofC;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESlidingInterfaceBiphasic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingInterfaceBiphasic.h\"\n#include \"FEBiphasic.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEModel.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingInterfaceBiphasic, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"             )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n    ADD_PARAMETER(m_gtol     , \"gaptol\"             )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_ptol     , \"ptol\"               );\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n\tADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_epsp     , \"pressure_penalty\"   );\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n    ADD_PARAMETER(m_srad     , \"search_radius\"      )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_nsegup   , \"seg_up\"             );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\n\tADD_PARAMETER(m_breloc   , \"node_reloc\"         );\n\tADD_PARAMETER(m_mu       , \"fric_coeff\"         );\n\tADD_PARAMETER(m_phi      , \"contact_frac\"       );\n\tADD_PARAMETER(m_bsmaug   , \"smooth_aug\"         );\n    ADD_PARAMETER(m_bsmfls   , \"smooth_fls\"         );\n    ADD_PARAMETER(m_bflips   , \"flip_primary\"       );\n    ADD_PARAMETER(m_bflipm   , \"flip_secondary\"     );\n    ADD_PARAMETER(m_bshellbs , \"shell_bottom_primary\"  );\n    ADD_PARAMETER(m_bshellbm , \"shell_bottom_secondary\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FESlidingSurfaceBiphasic\n//-----------------------------------------------------------------------------\n\nFESlidingSurfaceBiphasic::FESlidingSurfaceBiphasic(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{\n    m_bporo = false;\n }\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FESlidingSurfaceBiphasic::CreateMaterialPoint()\n{\n\treturn new FEBiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingSurfaceBiphasic::Init()\n{\n    // initialize surface data first\n    if (FEBiphasicContactSurface::Init() == false) return false;\n    \n    // allocate node normals and contact tractions\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    m_tn.assign(Nodes(), vec3d(0,0,0));\n    m_pn.assign(Nodes(), 0);\n    \n    // determine biphasic status\n    m_poro.resize(Elements(),false);\n    for (int i=0; i<Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& se = Element(i);\n        \n        // get the element this surface element belongs to\n        FEElement* pe = se.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n            \n            // see if this is a poro-elastic element\n            FEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n            if (biph) {\n                m_poro[i] = true;\n                m_bporo = true;\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::InitSlidingSurface()\n{\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            // Store current surface projection values as previous\n            data.m_rsp  = data.m_rs;\n\t\t\tdata.m_pmep = data.m_pme;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact pressures by averaging values from surrounding\n//! faces.  This function ensures that nodal contact pressures are always\n//! positive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurfaceBiphasic::EvaluateNodalContactPressures()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact pressures\n    zero(m_pn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact pressure for that face\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_pn[el.m_lnode[j]] += pn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_pn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact tractions by averaging values from surrounding\n//! faces.  This function ensures that nodal contact tractions are always\n//! compressive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurfaceBiphasic::EvaluateNodalContactTractions()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact tractions\n    zero(m_tn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact traction and pressure for that face\n        vec3d tn(0,0,0);\n        GetContactTraction(i, tn);\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_tn[el.m_lnode[j]] += tn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_tn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the\n//! element normals at the node\n\nvoid FESlidingSurfaceBiphasic::UpdateNodeNormals()\n{\n    const int MN = FEElement::MAX_NODES;\n    vec3d y[MN];\n    \n    // zero nodal normals\n    zero(m_nn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the nodal coordinates\n        for (int j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n        \n        // calculate the normals\n        for (int j=0; j<ne; ++j)\n        {\n            int jp1 = (j+1)%ne;\n            int jm1 = (j+ne-1)%ne;\n            vec3d n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n            m_nn[el.m_lnode[j]] += n;\n        }\n    }\n    \n    // normalize all vectors\n    const int N = Nodes();\n    for (int i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurfaceBiphasic::GetContactForce()\n{\n    return m_Ft;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurfaceBiphasic::GetContactArea()\n{\n    // initialize contact area\n    double a = 0;\n    \n    // loop over all elements of the primary surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        int nint = el.GaussPoints();\n        \n        // evaluate the contact force for that element\n        for (int i=0; i<nint; ++i)\n        {\n            // get data for this integration point\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n            if (data.m_Ln > 0)\n            {\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n                \n                // normal (magnitude = area)\n                vec3d n = g[0] ^ g[1];\n                \n                // gauss weight\n                double w = el.GaussWeights()[i];\n                \n                // contact force\n                a += n.norm()*w;\n            }\n        }\n    }\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurfaceBiphasic::GetFluidForce()\n{\n    int n, i;\n    const int MN = FEElement::MAX_NODES;\n    double pn[MN];\n    \n    // initialize contact force\n    vec3d f(0,0,0);\n    \n    // loop over all elements of the surface\n    for (n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        int nseln = el.Nodes();\n        \n        // nodal pressures\n        for (i=0; i<nseln; ++i) pn[i] = GetMesh()->Node(el.m_node[i]).get(m_dofP);\n        \n        int nint = el.GaussPoints();\n        \n        // evaluate the fluid force for that element\n        for (i=0; i<nint; ++i)\n        {\n            // get the base vectors\n            vec3d g[2];\n            CoBaseVectors(el, i, g);\n            // normal (magnitude = area)\n            vec3d n = g[0] ^ g[1];\n            // gauss weight\n            double w = el.GaussWeights()[i];\n            // fluid pressure\n            double p = el.eval(pn, i);\n            // contact force\n            f += n*(w*p);\n        }\n    }\n    \n    return f;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_bporo;\n\tar & m_poro;\n\tar & m_nn;\n\tar & m_pn;\n\tar & m_tn;\n\tar & m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetVectorGap(int nface, vec3d& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_dg;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetContactPressure(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_Ln;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpt += data.m_tr;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetSlipTangent(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tif (!data.m_bstick) pt += data.m_s1;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetMuEffective(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_mueff;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetLocalFLS(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n    for (int k = 0; k < ni; ++k)\n    {\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        pg += data.m_fls;\n    }\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetNodalVectorGap(int nface, vec3d* pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vec3d gi[FEElement::MAX_INTPOINTS];\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tgi[k] = data.m_dg;\n\t}\n    el.project_to_nodes(gi, pg);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetNodalContactPressure(int nface, double* pg)\n{\n    FESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        pg[k] = m_pn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetStickStatus(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tif (data.m_bstick) pg += 1.0;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasic::GetNodalContactTraction(int nface, vec3d* tn)\n{\n    FESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        tn[k] = m_tn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingInterfaceBiphasic\n//-----------------------------------------------------------------------------\n\nFESlidingInterfaceBiphasic::FESlidingInterfaceBiphasic(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n    static int count = 1;\n    SetID(count++);\n    \n    // initial values\n    m_knmult = 0;\n    m_atol = 0.1;\n    m_epsn = 1;\n    m_epsp = 1;\n    m_btwo_pass = false;\n    m_stol = 0.01;\n    m_bsymm = true;\n    m_srad = 1.0;\n    m_gtol = 0;\n    m_ptol = 0;\n    m_nsegup = 0;\n    m_bautopen = false;\n    m_breloc = false;\n    m_bsmaug = false;\n    m_bsmfls = false;\n    m_bupdtpen = false;\n    m_mu = 0.0;\n    m_phi = 0.0;\n    \n    m_naugmin = 0;\n    m_naugmax = 10;\n    \n    m_bfreeze = false;\n    m_bflipm = m_bflips = false;\n    m_bshellbm = m_bshellbs = false;\n\n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    \n    // set parents\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n\n    m_ss.SetSibling(&m_ms);\n    m_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFESlidingInterfaceBiphasic::~FESlidingInterfaceBiphasic()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterfaceBiphasic::Init()\n{\n    // initialize surface data\n    if (m_ss.Init() == false) return false;\n    if (m_ms.Init() == false) return false;\n    \n    // Flip secondary and primary surfaces, if requested.\n    // Note that we turn off those flags because otherwise we keep flipping, each time we get here (e.g. in optimization)\n    // TODO: Of course, we shouldn't get here more than once. I think we also get through the FEModel::Reset, so I'll have\n    //       look into that.\n    if (m_bflips) { m_ss.Invert(); m_bflips = false; }\n    if (m_bflipm) { m_ms.Invert(); m_bflipm = false; }\n    if (m_bshellbs) { m_ss.SetShellBottom(m_bshellbs); m_bshellbs = false; }\n    if (m_bshellbm) { m_ms.SetShellBottom(m_bshellbm); m_bshellbm = false; }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // get the DOFS\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_P = fem.GetDOFIndex(\"p\");\n    const int dof_RU = fem.GetDOFIndex(\"Ru\");\n    const int dof_RV = fem.GetDOFIndex(\"Rv\");\n    const int dof_RW = fem.GetDOFIndex(\"Rw\");\n    \n    vector<int> lm(7*FEElement::MAX_NODES*2);\n    \n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingSurfaceBiphasic& ss = (np == 0? m_ss : m_ms);\n        \n        int k, l;\n        for (int j=0; j<ss.Elements(); ++j)\n        {\n            FESurfaceElement& se = ss.Element(j);\n            int nint = se.GaussPoints();\n            int* sn = &se.m_node[0];\n            for (k=0; k<nint; ++k)\n            {\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(k));\n                FESurfaceElement* pe = pt.m_pme;\n                if (pe != 0)\n                {\n                    FESurfaceElement& me = *pe;\n                    int* mn = &me.m_node[0];\n                    \n                    assign(lm, -1);\n                    \n                    int nseln = se.Nodes();\n                    int nmeln = me.Nodes();\n                    \n                    for (l=0; l<nseln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(sn[l]).m_ID;\n                        lm[7*l  ] = id[dof_X];\n                        lm[7*l+1] = id[dof_Y];\n                        lm[7*l+2] = id[dof_Z];\n                        lm[7*l+3] = id[dof_P];\n                        lm[7*l+4] = id[dof_RU];\n                        lm[7*l+5] = id[dof_RV];\n                        lm[7*l+6] = id[dof_RW];\n                    }\n                    \n                    for (l=0; l<nmeln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(mn[l]).m_ID;\n                        lm[7*(l+nseln)  ] = id[dof_X];\n                        lm[7*(l+nseln)+1] = id[dof_Y];\n                        lm[7*(l+nseln)+2] = id[dof_Z];\n                        lm[7*(l+nseln)+3] = id[dof_P];\n                        lm[7*(l+nseln)+4] = id[dof_RU];\n                        lm[7*(l+nseln)+5] = id[dof_RV];\n                        lm[7*(l+nseln)+6] = id[dof_RW];\n                    }\n                    \n                    K.build_add(lm);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n        CalcAutoPressurePenalty(m_ss);\n        CalcAutoPressurePenalty(m_ms);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function is called during the initialization\nvoid FESlidingInterfaceBiphasic::Activate()\n{\n    // don't forget to call base member\n    FEContactInterface::Activate();\n    \n    UpdateAutoPenalty();\n    \n    // update sliding interface data\n    Update();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::CalcAutoPenalty(FESlidingSurfaceBiphasic& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::CalcAutoPressurePenalty(FESlidingSurfaceBiphasic& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPressurePenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\ndouble FESlidingInterfaceBiphasic::AutoPressurePenalty(FESurfaceElement& el, FESlidingSurfaceBiphasic& s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // evaluate element surface normal at parametric center\n    vec3d t[2];\n    s.CoBaseVectors0(el, 0, 0, t);\n    vec3d n = t[0] ^ t[1];\n    n.unit();\n    \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n\n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        \n    // see if this is a poro-elastic element\n    FEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n    if (biph == 0) return 0.0;\n\n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n    FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n            \n    // setup the material point\n    ept.m_F = mat3dd(1.0);\n    ept.m_J = 1;\n    ept.m_s.zero();\n            \n    // if this is a poroelastic element, then get the permeability tensor\n    FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n    pt.m_p = 0;\n    pt.m_w = vec3d(0,0,0);\n            \n    double K[3][3];\n    biph->Permeability(K, mp);\n            \n    double eps = n.x*(K[0][0]*n.x+K[0][1]*n.y+K[0][2]*n.z)\n    +n.y*(K[1][0]*n.x+K[1][1]*n.y+K[1][2]*n.z)\n    +n.z*(K[2][0]*n.x+K[2][1]*n.y+K[2][2]*n.z);\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n    return eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::ProjectSurface(FESlidingSurfaceBiphasic& ss, FESlidingSurfaceBiphasic& ms, bool bupseg, bool bmove)\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n\n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n    double psf = GetPenaltyScaleFactor();\n    \n    // if we need to project the nodes onto the secondary surface,\n    // let's do this first\n    if (bmove)\n    {\n        int NN = ss.Nodes();\n        int NE = ss.Elements();\n        // first we need to calculate the node normals\n        vector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n        for (int i=0; i<NE; ++i)\n        {\n            FESurfaceElement& el = ss.Element(i);\n            int ne = el.Nodes();\n            for (int j=0; j<ne; ++j)\n            {\n                vec3d r0 = ss.Node(el.m_lnode[ j         ]).m_rt;\n                vec3d rp = ss.Node(el.m_lnode[(j+   1)%ne]).m_rt;\n                vec3d rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).m_rt;\n                vec3d n = (rp - r0)^(rm - r0);\n                normal[el.m_lnode[j]] += n;\n            }\n        }\n        for (int i=0; i<NN; ++i) normal[i].unit();\n        \n        // loop over all nodes\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = ss.Node(i);\n            \n            // get the spatial nodal coordinates\n            vec3d rt = node.m_rt;\n            vec3d nu = normal[i];\n            \n            // project onto the secondary surface\n            vec3d q;\n            double rs[2] = {0,0};\n            FESurfaceElement* pme = np.Project(rt, nu, rs);\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double gap = nu*(rt - q);\n                \n                if (gap>0) node.m_r0 = node.m_rt = q;\n            }\n        }\n    }\n    \n    // loop over all integration points\n#pragma omp parallel for\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        bool sporo = ss.m_poro[i];\n        \n        int ne = el.Nodes();\n        int nint = el.GaussPoints();\n        double ps[FEElement::MAX_INTPOINTS];\n        // get the nodal pressures\n        if (sporo)\n        {\n            for (int j=0; j<ne; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        }\n        \n        for (int j=0; j<nint; ++j)\n        {\n            // get the integration point data\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // calculate the global position of the integration point\n            vec3d r = ss.Local2Global(el, j);\n            \n            // get the pressure at the integration point\n            double p1 = 0;\n            if (sporo) p1 = el.eval(ps, j);\n\n            // calculate the normal at this integration point\n            vec3d nu = ss.SurfaceNormal(el, j);\n            \n            // first see if the old intersected face is still good enough\n            FESurfaceElement* pme = pt.m_pme;\n            double rs[2] = {0,0};\n            if (pme)\n            {\n                double g;\n                \n                // see if the ray intersects this element\n                if (ms.Intersect(*pme, r, nu, rs, g, m_stol))\n                {\n                    pt.m_rs[0] = rs[0];\n                    pt.m_rs[1] = rs[1];\n                }\n                else\n                {\n                    pme = 0;\n                }\n            }\n            \n            // find the intersection point with the secondary surface\n            if (pme == 0 && bupseg) pme = np.Project(r, nu, rs);\n            \n            pt.m_pme = pme;\n            pt.m_nu = nu;\n            pt.m_rs[0] = rs[0];\n            pt.m_rs[1] = rs[1];\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double g = nu*(r - q);\n                \n                double eps = m_epsn*pt.m_epsn*psf;\n                \n                double Ln = pt.m_Lmd + eps*g;\n                \n                pt.m_gap = (g <= m_srad? g : 0);\n                \n                // calculate the pressure gap function\n                bool mporo = ms.m_poro[pme->m_lid];\n                \n                if ((Ln >= 0) && (g <= m_srad))\n                {\n                    \n                    // get the pressure at the projection point\n                    double p2 = 0;\n                    if (mporo) {\n                        double pm[FEElement::MAX_NODES];\n                        for (int k=0; k<pme->Nodes(); ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n                        p2 = pme->eval(pm, rs[0], rs[1]);\n                    }\n                    if (sporo) {\n                        pt.m_p1 = p1;\n                        if (mporo) {\n                            pt.m_pg = p1 - p2;\n                        }\n                    }\n                    else if (mporo) {\n                        pt.m_p1 = p2;\n                    }\n                }\n                else\n                {\n                    pt.m_Lmd = 0;\n                    pt.m_pme = 0;\n                    pt.m_gap = 0;\n                    pt.m_dg = pt.m_Lmt = vec3d(0,0,0);\n                    if (sporo || mporo) {\n                        pt.m_Lmp = 0;\n                        pt.m_pg = 0;\n                        pt.m_p1 = 0;\n                    }\n                }\n            }\n            else\n            {\n                // the node is not in contact\n                pt.m_Lmd = 0;\n                pt.m_gap = 0;\n                pt.m_dg = pt.m_Lmt = vec3d(0,0,0);\n                if (sporo) {\n                    pt.m_Lmp = 0;\n                    pt.m_pg = 0;\n                    pt.m_p1 = 0;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceBiphasic::Update()\n{\n    static int naug = 0;\n    static int biter = 0;\n    \n    FEModel& fem = *GetFEModel();\n    \n    // get the iteration number\n    // we need this number to see if we can do segment updates or not\n    // also reset number of iterations after each augmentation\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolver* psolver = pstep->GetFESolver();\n    if (psolver->m_niter == 0) {\n        biter = 0;\n        naug = psolver->m_naug;\n        // check update of auto-penalty\n        if (m_bupdtpen) UpdateAutoPenalty();\n    } else if (psolver->m_naug > naug) {\n        biter = psolver->m_niter;\n        naug = psolver->m_naug;\n    }\n    int niter = psolver->m_niter - biter;\n    bool bupseg = ((m_nsegup == 0)? true : (niter <= m_nsegup));\n    // get the logfile\n    //\tLogfile& log = GetLogfile();\n    //\tlog.printf(\"seg_up iteration # %d\\n\", niter+1);\n    \n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    static bool bfirst = true;\n    ProjectSurface(m_ss, m_ms, bupseg, (m_breloc && bfirst));\n    if (m_btwo_pass || m_ms.m_bporo) ProjectSurface(m_ms, m_ss, bupseg);\n    bfirst = false;\n    \n    // Call InitSlidingSurface on the first iteration of each time step\n\tint nsolve_iter = psolver->m_niter;\n    if (nsolve_iter == 0)\n    {\n        m_ss.InitSlidingSurface();\n        if (m_btwo_pass) m_ms.InitSlidingSurface();\n        m_bfreeze = false;\n    }\n    \n    // Update the net contact pressures\n    UpdateContactPressures();\n    \n    if (niter == 0) m_bfreeze = false;\n    \n    // set poro flag\n    bool bporo = (m_ss.m_bporo || m_ms.m_bporo);\n    \n    // only continue if we are doing a poro-elastic simulation\n    if (bporo == false) return;\n    \n    // update node normals\n    m_ss.UpdateNodeNormals();\n    if (bporo) m_ms.UpdateNodeNormals();\n    \n    // Now that the nodes have been projected, we need to figure out\n    // if we need to modify the constraints on the pressure dofs.\n    // If the nodes are not in contact, they must be free\n    // draining. Since all nodes have been previously marked to be\n    // free-draining in MarkFreeDraining(), we just need to reverse\n    // this setting here, for nodes that are in contact.\n    \n    // Next, we loop over each surface, visiting the nodes\n    // and finding out if that node is in contact or not\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingSurfaceBiphasic& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceBiphasic& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all the nodes of the primary surface\n        for (int n=0; n<ss.Nodes(); ++n) {\n            FENode& node = ss.Node(n);\n            int id = node.m_ID[m_dofP];\n            if ((id < -1) && (ss.m_pn[n] > 0))\n            {\n                // mark node as non-free-draining (= pos ID)\n                node.m_ID[m_dofP] = -id-2;\n            }\n        }\n        \n        // loop over all nodes of the secondary surface\n        // the secondary surface is trickier since we need\n        // to look at the primary surface's projection\n        if (ms.m_bporo) {\n            FENormalProjection np(ss);\n            np.SetTolerance(m_stol);\n            np.SetSearchRadius(m_srad);\n            np.Init();\n            \n            for (int n=0; n<ms.Nodes(); ++n)\n            {\n                // get the node\n                FENode& node = ms.Node(n);\n                \n                // project it onto the primary surface\n                double rs[2] = {0,0};\n                FESurfaceElement* pse = np.Project(node.m_rt, ms.m_nn[n], rs);\n                \n                if (pse)\n                {\n                    // we found an element, so let's see if it's even remotely close to contact\n                    // find the global location of the intersection point\n                    vec3d q = ss.Local2Global(*pse, rs[0], rs[1]);\n                    \n                    // calculate the gap function\n                    double g = ms.m_nn[n]*(node.m_rt - q);\n                    \n                    if (fabs(g) <= m_srad)\n                    {\n                        // we found an element so let's calculate the nodal traction values for this element\n                        // get the normal tractions at the nodes\n                        \n                        double tn[FEElement::MAX_NODES];\n                        for (int i=0; i<pse->Nodes(); ++i)\n                            tn[i] = ss.m_pn[pse->m_lnode[i]];\n                        \n                        // now evaluate the traction at the intersection point\n                        double tp = pse->eval(tn, rs[0], rs[1]);\n                        \n                        // if tp > 0, mark node as non-free-draining. (= pos ID)\n                        int id = node.m_ID[m_dofP];\n                        if ((id < -1) && (tp > 0))\n                        {\n                            // mark as non free-draining\n                            node.m_ID[m_dofP] = -id-2;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingInterfaceBiphasic::SlipTangent(FESlidingSurfaceBiphasic& ss, const int nel, const int nint, FESlidingSurfaceBiphasic& ms, double& dh, vec3d& r)\n{\n    vec3d s1(0,0,0);\n    dh = 0;\n    r = vec3d(0,0,0);\n    \n    // get primary surface element\n    FESurfaceElement& se = ss.Element(nel);\n    \n    // get integration point data\n    FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(nint));\n\tdouble g = data.m_gap;\n    vec3d nu = data.m_nu;\n    \n    // find secondary surface element\n    FESurfaceElement* pme = data.m_pme;\n    \n    // calculate previous positions\n    vec3d x2p = ms.Local2GlobalP(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d x1p = ss.Local2GlobalP(se, nint);\n    \n    // calculate dx2\n    vec3d x2 = ms.Local2Global(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d dx2 = x2 - x2p;\n    \n    // calculate dx1\n    vec3d x1 = ss.Local2Global(se, nint);\n    vec3d dx1 = x1 - x1p;\n    \n    // get current and previous covariant basis vectors\n    vec3d gscov[2], gscovp[2];\n    ss.CoBaseVectors(se, nint, gscov);\n    ss.CoBaseVectorsP(se, nint, gscovp);\n    \n    // calculate delta gscov\n    vec3d dgscov[2];\n    dgscov[0] = gscov[0] - gscovp[0];\n    dgscov[1] = gscov[1] - gscovp[1];\n    \n    // calculate m, J, Nhat\n    vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n    double detJ = (gscov[0] ^ gscov[1]).norm();\n    mat3d Nhat = (mat3dd(1) - (nu & nu));\n    \n    // calculate c\n    vec3d c = Nhat*m*(1.0/detJ);\n    \n    // calculate slip direction s1\n    double norm = (Nhat*(c*(-g) + dx1 - dx2)).norm();\n    if (norm != 0)\n    {\n        s1 = (Nhat*(c*(-g) + dx1 - dx2))/norm;\n        dh = norm;\n        r = c*(-g) + dx1 - dx2;\n    }\n    \n    return s1;\n    \n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingInterfaceBiphasic::ContactTraction(FESlidingSurfaceBiphasic& ss, const int nel, const int n, FESlidingSurfaceBiphasic& ms, double& pn)\n{\n    vec3d s1(0,0,0);\n    vec3d dr(0,0,0);\n    vec3d t(0,0,0);\n    pn = 0;\n    double tn = 0, ts = 0, mueff = 0;\n    double psf = GetPenaltyScaleFactor();\n    \n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n\n\t// get the primary surface element\n\tFESurfaceElement& se = ss.Element(nel);\n\n    // get the integration point data\n    FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(n));\n\n    // penalty\n    double eps = m_epsn*data.m_epsn*psf;\n    \n    // normal gap\n    double g = data.m_gap;\n    \n    // normal traction Lagrange multiplier\n    double Lm = data.m_Lmd;\n    \n    // vector traction Lagrange multiplier\n    vec3d Lt = data.m_Lmt;\n    \n    // get the normal at this integration point\n    vec3d nu = data.m_nu;\n    \n    // get the fluid pressure at this integration point\n    double p = data.m_p1;\n    \n    // get poro status of primary surface\n    bool sporo = ss.m_poro[nel];\n    \n    // get current and previous secondary elements\n    FESurfaceElement* pme = data.m_pme;\n    FESurfaceElement* pmep = data.m_pmep;\n    \n    // zero the effective friction coefficient\n    data.m_mueff = 0.0;\n    data.m_fls = 0.0;\n    data.m_s1 = vec3d(0,0,0);\n    double flsmax = 1./(1-m_phi);   // theoretical upper bound on fluid load support\n    \n    // get local FLS from element projection\n    double fls = 0;\n    if (m_bsmfls) {\n        double lfls[FEElement::MAX_INTPOINTS];\n        ss.GetGPLocalFLS(nel, lfls);\n        fls = lfls[n];\n    }\n\n    // if we just returned from an augmentation, do not update stick or slip status\n    if (m_bfreeze && pme) {\n        if (data.m_bstick) {\n            // calculate current global position of the integration point\n            vec3d xo = ss.Local2Global(se, n);\n            \n            // calculate current global position of the previous intersection point\n            vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n            \n            // calculate vector gap\n            vec3d dg = xt - xo;\n            \n            // calculate trial stick traction, normal component, shear component\n            t = Lt + dg*eps;\n            tn = t*nu;\n            ts = (t - nu*tn).norm();\n            \n            // contact pressure\n            pn = MBRACKET(-tn);\n            \n            // calculate effective friction coefficient\n            if (pn > 0)\n            {\n                data.m_mueff = ts/pn;\n                data.m_fls = m_bsmfls ? fls : p/pn;\n                if (data.m_fls > flsmax) data.m_fls = flsmax;\n            }\n            \n            // store the previous values as the current\n            data.m_pme = data.m_pmep;\n            data.m_rs = data.m_rsp;\n            \n            // recalculate gap\n            data.m_dg = dg;\n            \n            // recalculate pressure gap\n            bool mporo = ms.m_poro[pme->m_lid];\n            if (sporo && mporo)\n            {\n                double pm[FEElement::MAX_NODES];\n                for (int k=0; k<pme->Nodes(); ++k) pm[k] = m.Node(pme->m_node[k]).get(m_dofP);\n                double p2 = pme->eval(pm, data.m_rs[0], data.m_rs[1]);\n                data.m_pg = p - p2;\n            }\n            \n        }\n        else {\n            // recalculate contact pressure for slip\n            pn = MBRACKET(Lm + eps*g);\n            \n            if (pn != 0)\n            {\n                \n                double dh = 0;\n                \n                // slip direction\n                s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                \n                // calculate effective friction coefficient\n                data.m_fls = m_bsmfls ? fls : p/pn;\n                if (data.m_fls > flsmax) data.m_fls = flsmax;\n                data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                data.m_mueff = MBRACKET(data.m_mueff);\n                \n                // total traction\n                t = nu*(-pn) - s1*pn*data.m_mueff;\n                \n                // reset slip direction\n                data.m_s1 = s1;\n            }\n            else\n            {\n                t = vec3d(0,0,0);\n            }\n        }\n    }\n    // update contact tractions\n    else {\n        data.m_bstick = false;\n        \n        if (pme)\n        {\n            // assume stick and calculate traction\n            if (pmep)\n            {\n                // calculate current global position of the integration point\n                vec3d xo = ss.Local2Global(se, n);\n                \n                // calculate current global position of the previous intersection point\n                vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n                \n                // calculate vector gap\n                vec3d dg = xt - xo;\n                \n                // calculate trial stick traction, normal component, shear component\n                t = Lt + dg*eps;\n                tn = t*nu;\n                ts = (t - nu*tn).norm();\n                \n                // calculate effective friction coefficient\n                if (tn != 0)\n                {\n                    data.m_fls = m_bsmfls ? fls : p/(-tn);\n                    if (data.m_fls > flsmax) data.m_fls = flsmax;\n                    mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                    mueff = MBRACKET(mueff);\n                }\n                \n                // check if stick\n                if ( (tn < 0) && (ts < fabs(tn*mueff)) )\n                {\n                    // set boolean flag for stick\n                    data.m_bstick = true;\n                    \n                    // contact pressure\n                    pn = MBRACKET(-tn);\n                    \n                    // calculate effective friction coefficient\n                    if (pn > 0) {\n                        data.m_mueff = ts/pn;\n                        data.m_fls = m_bsmfls ? fls : p/pn;\n                        if (data.m_fls > flsmax) data.m_fls = flsmax;\n                    }\n                    \n                    // store the previous values as the current\n                    data.m_pme = data.m_pmep;\n                    data.m_rs = data.m_rsp;\n                    \n                    // recalculate gaps\n                    data.m_dg = dg;\n                    \n                    // recalculate pressure gap\n                    bool mporo = ms.m_poro[pme->m_lid];\n                    if (sporo && mporo)\n                    {\n                        double pm[FEElement::MAX_NODES];\n                        for (int k=0; k<pme->Nodes(); ++k) pm[k] = m.Node(pme->m_node[k]).get(m_dofP);\n                        double p2 = pme->eval(pm, data.m_rs[0], data.m_rs[1]);\n                        data.m_pg = p - p2;\n                    }\n                    \n                }\n                else\n                {\n                    // recalculate contact pressure for slip\n                    pn = MBRACKET(Lm + eps*g);\n                    \n                    if (pn != 0)\n                    {\n                        \n                        double dh = 0;\n                        \n                        // slip direction\n                        s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                        \n                        // calculate effective friction coefficient\n                        data.m_fls = p/pn;\n                        data.m_fls = m_bsmfls ? fls : p/pn;\n                        if (data.m_fls > flsmax) data.m_fls = flsmax;\n                        data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                        data.m_mueff = MBRACKET(data.m_mueff);\n\n                        // total traction\n                        t = nu*(-pn) - s1*pn*data.m_mueff;\n                        \n                        // reset slip direction\n                        data.m_s1 = s1;\n                        data.m_bstick = false;\n                    }\n                    else\n                    {\n                        t = vec3d(0,0,0);\n                    }\n                    \n                }\n            }\n            else\n            {\n                // assume slip upon first contact\n                // calculate contact pressure for slip\n                pn = MBRACKET(Lm + eps*g);\n                \n                if (pn != 0)\n                {\n                    \n                    double dh = 0;\n                    \n                    // slip direction\n                    s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                    \n                    // calculate effective friction coefficient\n                    data.m_fls = m_bsmfls ? fls : p/pn;\n                    if (data.m_fls > flsmax) data.m_fls = flsmax;\n                    data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                    data.m_mueff = MBRACKET(data.m_mueff);\n\n                    // total traction\n                    t = nu*(-pn) - s1*pn*data.m_mueff;\n                    \n                    // reset slip direction\n                    data.m_s1 = s1;\n                    data.m_bstick = false;\n                }\n            }\n        }\n    }\n    \n    return t;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    const int MN = FEElement::MAX_NODES;\n    \n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    double N[4*MN*2];\n    \n    m_ss.m_Ft = vec3d(0,0,0);\n    m_ms.m_Ft = vec3d(0,0,0);\n    \n    FEModel& fem = *GetFEModel();\n    \n    // need to multiply biphasic stiffness entries by the timestep\n    double dt = fem.GetTime().timeIncrement;\n    \n    // loop over the nr of passes\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        // get primary and secondary surface\n        FESlidingSurfaceBiphasic& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceBiphasic& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all primary surface elements\n        for (int i=0; i<ss.Elements(); ++i)\n        {\n            // get the surface element\n            FESurfaceElement& se = ss.Element(i);\n            \n            bool sporo = ss.m_poro[i];\n            \n            // get the nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // copy the LM vector; we'll need it later\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (int j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n            }\n            \n            // loop over all integration points\n            // note that we are integrating over the current surface\n            for (int j=0; j<nint; ++j)\n            {\n                // get the integration point data\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n                // calculate contact pressure and account for stick\n                double pn;\n                vec3d t = ContactTraction(ss, i, j, ms, pn);\n                \n                // get the secondary element\n                FESurfaceElement* pme = pt.m_pme;\n                \n                if (pme)\n                {\n                    // get the secondary element\n                    FESurfaceElement& me = *pme;\n                    \n                    bool mporo = ms.m_poro[pme->m_lid];\n                    \n                    // get the nr of secondary element nodes\n                    int nmeln = me.Nodes();\n                    \n                    // copy LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    // calculate degrees of freedom\n                    int ndof = 3*(nseln + nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    for (int k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[3*k  ];\n                        LM[3*k+1] = sLM[3*k+1];\n                        LM[3*k+2] = sLM[3*k+2];\n                    }\n                    \n                    for (int k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[3*k  ];\n                        LM[3*(k+nseln)+1] = mLM[3*k+1];\n                        LM[3*(k+nseln)+2] = mLM[3*k+2];\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // get primary element shape functions\n                    Hs = se.H(j);\n                    \n                    // get secondary element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    if (pn > 0) {\n                        \n                        // calculate the force vector\n                        fe.resize(ndof);\n                        zero(fe);\n                        \n                        for (int k=0; k<nseln; ++k)\n                        {\n                            N[3*k  ] = Hs[k]*t.x;\n                            N[3*k+1] = Hs[k]*t.y;\n                            N[3*k+2] = Hs[k]*t.z;\n                        }\n                        \n                        for (int k=0; k<nmeln; ++k)\n                        {\n                            N[3*(k+nseln)  ] = -Hm[k]*t.x;\n                            N[3*(k+nseln)+1] = -Hm[k]*t.y;\n                            N[3*(k+nseln)+2] = -Hm[k]*t.z;\n                        }\n                        \n                        for (int k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n                        \n                        // calculate contact forces\n                        for (int k=0; k<nseln; ++k)\n                            ss.m_Ft += vec3d(fe[3*k], fe[3*k+1], fe[3*k+2]);\n                        \n                        for (int k = 0; k<nmeln; ++k)\n                            ms.m_Ft += vec3d(fe[3*(k+nseln)], fe[3*(k+nseln)+1], fe[3*(k+nseln)+2]);\n                        \n                        // assemble the global residual\n                        R.Assemble(en, LM, fe);\n                        \n                        // do the biphasic stuff\n                        if (sporo && mporo)\n                        {\n                            // calculate nr of pressure dofs\n                            int ndof = nseln + nmeln;\n                            \n                            // calculate the flow rate\n                            double epsp = m_epsp*pt.m_epsp;\n                            \n                            double wn = pt.m_Lmp + epsp*pt.m_pg;\n                            \n                            // fill the LM\n                            LM.resize(ndof);\n                            for (int k=0; k<nseln; ++k) LM[k        ] = sLM[3*nseln+k];\n                            for (int k=0; k<nmeln; ++k) LM[k + nseln] = mLM[3*nmeln+k];\n                            \n                            // fill the force array\n                            fe.resize(ndof);\n                            zero(fe);\n                            for (int k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                            for (int k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                            for (int k=0; k<ndof; ++k) fe[k] += dt*wn*N[k]*detJ[j]*w[j];\n                            \n                            // assemble residual\n                            R.Assemble(en, LM, fe);\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    // see how many reformations we've had to do so far\n    int nref = GetSolver()->m_nref;\n    \n    const int MN = FEElement::MAX_NODES;\n    \n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    double N[4*MN*2];\n    vector<int> sLM, mLM, LM, en;\n    FEElementMatrix ke;\n    \n    FEModel& fem = *GetFEModel();\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    // do single- or two-pass\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np < npass; ++np)\n    {\n        // get the primary and secondary surface\n        FESlidingSurfaceBiphasic& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceBiphasic& ms = (np == 0? m_ms : m_ss);\n        \n        FEMesh& mesh = *ms.GetMesh();\n        \n        // loop over all primary elements\n        for (int i=0; i<ss.Elements(); ++i)\n        {\n            // get the primary element\n            FESurfaceElement& se = ss.Element(i);\n            \n            bool sporo = ss.m_poro[i];\n            \n            // get nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // nodal pressures\n            double pn[MN] = {0};\n            if (sporo) {\n                for (int j=0; j<nseln; ++j) pn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n            }\n            \n            // copy the LM vector\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (int j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n            }\n            \n            // loop over all integration points\n            for (int j=0; j<nint; ++j)\n            {\n                // get integration point data\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n                // calculate contact pressure and account for stick\n                double pn;\n                vec3d t = ContactTraction(ss, i, j, ms, pn);\n                \n                // get the secondary element\n                FESurfaceElement* pme = pt.m_pme;\n                \n                if (pme)\n                {\n                    FESurfaceElement& me = *pme;\n                    \n                    bool mporo = ms.m_poro[pme->m_lid];\n                    \n                    // get the nr of secondary nodes\n                    int nmeln = me.Nodes();\n                    \n                    // nodal pressure\n                    double pm[MN] = {0};\n                    for (int k=0; k<nmeln; ++k) pm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n                    \n                    // copy the LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    // calculate degrees of freedom\n                    int ndpn;\t// number of dofs per node\n                    int ndof;\t// number of dofs in stiffness matrix\n                    \n                    if (sporo && mporo) {\n                        // calculate degrees of freedom for biphasic-on-biphasic contact\n                        ndpn = 4;\n                        ndof = ndpn*(nseln+nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (int k=0; k<nseln; ++k)\n                        {\n                            LM[4*k  ] = sLM[3*k  ];\t\t\t// x-dof\n                            LM[4*k+1] = sLM[3*k+1];\t\t\t// y-dof\n                            LM[4*k+2] = sLM[3*k+2];\t\t\t// z-dof\n                            LM[4*k+3] = sLM[3*nseln+k];\t\t// p-dof\n                        }\n                        for (int k=0; k<nmeln; ++k)\n                        {\n                            LM[4*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n                            LM[4*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n                            LM[4*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n                            LM[4*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n                        }\n                    }\n                    \n                    else {\n                        // calculate degrees of freedom for biphasic-on-elastic or elastic-on-elastic contact\n                        ndpn = 3;\n                        ndof = ndpn*(nseln + nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (int k=0; k<nseln; ++k)\n                        {\n                            LM[3*k  ] = sLM[3*k  ];\n                            LM[3*k+1] = sLM[3*k+1];\n                            LM[3*k+2] = sLM[3*k+2];\n                        }\n                        \n                        for (int k=0; k<nmeln; ++k)\n                        {\n                            LM[3*(k+nseln)  ] = mLM[3*k  ];\n                            LM[3*(k+nseln)+1] = mLM[3*k+1];\n                            LM[3*(k+nseln)+2] = mLM[3*k+2];\n                        }\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // primary shape functions\n                    Hs = se.H(j);\n                    \n                    // secondary shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // get primary normal vector\n                    vec3d nu = pt.m_nu;\n                    \n                    // gap function\n                    double g = pt.m_gap;\n                    \n                    // penalty\n                    double eps = m_epsn*pt.m_epsn*psf;\n\n                    // only evaluate stiffness matrix if contact traction is non-zero\n                    if (pn > 0)\n                    {\n                        // if stick\n                        if (pt.m_bstick)\n                        {\n                            double dtn = eps;\n                            \n                            // create the stiffness matrix\n                            ke.resize(ndof, ndof); ke.zero();\n                            \n                            // evaluate basis vectors on primary surface\n                            vec3d gscov[2];\n                            ss.CoBaseVectors(se, j, gscov);\n                            \n                            // identity tensor\n                            mat3d I = mat3dd(1);\n                            \n                            // evaluate Mc and Ac and combine them into As\n                            double* Gsr = se.Gr(j);\n                            double* Gss = se.Gs(j);\n                            mat3d Ac[MN], As[MN];\n                            mat3d gscovh[2];\n                            gscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n                            for (int k=0; k<nseln; ++k) {\n                                Ac[k] = (gscovh[1]*Gsr[k] - gscovh[0]*Gss[k])/detJ[j];\n                                As[k] = t & (Ac[k]*nu);\n                            }\n                            \n                            // --- S O L I D - S O L I D   C O N T A C T ---\n                            \n                            // a. I-term\n                            //------------------------------------\n                            \n                            for (int k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                            for (int k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                            double tmp = dtn*detJ[j]*w[j];\n                            for (int l=0; l<nseln+nmeln; ++l)\n                            {\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[k*ndpn  ][l*ndpn  ] -= -tmp*N[k]*N[l]*I[0][0];\n                                    ke[k*ndpn  ][l*ndpn+1] -= -tmp*N[k]*N[l]*I[0][1];\n                                    ke[k*ndpn  ][l*ndpn+2] -= -tmp*N[k]*N[l]*I[0][2];\n                                    \n                                    ke[k*ndpn+1][l*ndpn  ] -= -tmp*N[k]*N[l]*I[1][0];\n                                    ke[k*ndpn+1][l*ndpn+1] -= -tmp*N[k]*N[l]*I[1][1];\n                                    ke[k*ndpn+1][l*ndpn+2] -= -tmp*N[k]*N[l]*I[1][2];\n                                    \n                                    ke[k*ndpn+2][l*ndpn  ] -= -tmp*N[k]*N[l]*I[2][0];\n                                    ke[k*ndpn+2][l*ndpn+1] -= -tmp*N[k]*N[l]*I[2][1];\n                                    ke[k*ndpn+2][l*ndpn+2] -= -tmp*N[k]*N[l]*I[2][2];\n                                }\n                            }\n                            \n                            // b. A-term\n                            //-------------------------------------\n                            \n                            tmp = detJ[j]*w[j];\n                            // non-symmetric\n                            for (int l=0; l<nseln; ++l)\n                            {\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[k*ndpn  ][l*ndpn  ] -= tmp*N[k]*As[l][0][0];\n                                    ke[k*ndpn  ][l*ndpn+1] -= tmp*N[k]*As[l][0][1];\n                                    ke[k*ndpn  ][l*ndpn+2] -= tmp*N[k]*As[l][0][2];\n                                    \n                                    ke[k*ndpn+1][l*ndpn  ] -= tmp*N[k]*As[l][1][0];\n                                    ke[k*ndpn+1][l*ndpn+1] -= tmp*N[k]*As[l][1][1];\n                                    ke[k*ndpn+1][l*ndpn+2] -= tmp*N[k]*As[l][1][2];\n                                    \n                                    ke[k*ndpn+2][l*ndpn  ] -= tmp*N[k]*As[l][2][0];\n                                    ke[k*ndpn+2][l*ndpn+1] -= tmp*N[k]*As[l][2][1];\n                                    ke[k*ndpn+2][l*ndpn+2] -= tmp*N[k]*As[l][2][2];\n                                }\n                            }\n                            \n                            // --- B I P H A S I C   S T I F F N E S S ---\n                            if (sporo && mporo)\n                            {\n                                // need to multiply biphasic stiffness entries by the timestep\n                                double dt = fem.GetTime().timeIncrement;\n                                \n                                double tmp = dt*w[j]*detJ[j];\n                                \n                                double epsp = m_epsp*pt.m_epsp*psf;\n                                \n                                double wn = pt.m_Lmp + epsp*pt.m_pg;\n                                \n                                // --- S O L I D - P R E S S U R E   C O N T A C T ---\n                                \n                                // b. A-term\n                                //-------------------------------------\n                                \n                                for (int l=0; l<nseln; ++l) {\n                                    vec3d Acn = Ac[l]*nu;\n                                    for (int k=0; k<nseln+nmeln; ++k)\n                                    {\n                                        ke[4*k + 3][4*l  ] -= tmp*wn*N[k]*Acn.x;\n                                        ke[4*k + 3][4*l+1] -= tmp*wn*N[k]*Acn.y;\n                                        ke[4*k + 3][4*l+2] -= tmp*wn*N[k]*Acn.z;\n                                    }\n                                }\n                                \n                                // --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n                                \n                                // calculate the N-vector\n                                for (int k=0; k<nseln; ++k)\n                                {\n                                    N[ndpn*k  ] = 0;\n                                    N[ndpn*k+1] = 0;\n                                    N[ndpn*k+2] = 0;\n                                    N[ndpn*k+3] = Hs[k];\n                                }\n                                \n                                for (int k=0; k<nmeln; ++k)\n                                {\n                                    N[ndpn*(k+nseln)  ] = 0;\n                                    N[ndpn*(k+nseln)+1] = 0;\n                                    N[ndpn*(k+nseln)+2] = 0;\n                                    N[ndpn*(k+nseln)+3] = -Hm[k];\n                                }\n                                \n                                for (int k=0; k<ndof; ++k)\n                                    for (int l=0; l<ndof; ++l) ke[k][l] -= tmp*epsp*N[k]*N[l];\n                                \n                            }\n                            // assemble the global stiffness\n\t\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\t\tLS.Assemble(ke);\n                        }\n                        // if slip\n                        else\n                        {\n                            // create the stiffness matrix\n                            ke.resize(ndof, ndof); ke.zero();\n                            \n                            double tn = -pn;\n                            \n                            // obtain the slip direction s1 and inverse of spatial increment dh\n                            double dh = 0, hd = 0;\n                            vec3d dr(0,0,0);\n                            vec3d s1 = SlipTangent(ss, i, j, ms, dh, dr);\n                            \n                            if (dh != 0)\n                            {\n                                hd = 1.0 / dh;\n                            }\n                            \n                            // evaluate basis vectors on both surfaces\n                            vec3d gscov[2], gmcov[2];\n                            ss.CoBaseVectors(se, j, gscov);\n                            ms.CoBaseVectors(me, r, s, gmcov);\n                            mat2d A;\n                            A[0][0] = gscov[0]*gmcov[0]; A[0][1] = gscov[0]*gmcov[1];\n                            A[1][0] = gscov[1]*gmcov[0]; A[1][1] = gscov[1]*gmcov[1];\n                            mat2d a = A.inverse();\n                            \n                            // evaluate covariant basis vectors on primary surface at previous time step\n                            vec3d gscovp[2];\n                            ss.CoBaseVectorsP(se, j, gscovp);\n                            \n                            // calculate delta gscov\n                            vec3d dgscov[2];\n                            dgscov[0] = gscov[0] - gscovp[0];\n                            dgscov[1] = gscov[1] - gscovp[1];\n                            \n                            // evaluate approximate contravariant basis vectors when gap != 0\n                            vec3d gscnt[2], gmcnt[2];\n                            gmcnt[0] = gscov[0]*a[0][0] + gscov[1]*a[0][1];\n                            gmcnt[1] = gscov[0]*a[1][0] + gscov[1]*a[1][1];\n                            gscnt[0] = gmcov[0]*a[0][0] + gmcov[1]*a[1][0];\n                            gscnt[1] = gmcov[0]*a[0][1] + gmcov[1]*a[1][1];\n                            \n                            // evaluate N and S tensors and approximations when gap != 0\n                            mat3ds N1 = dyad(nu);\n                            mat3d Nh1 = mat3dd(1) - (nu & nu);\n                            mat3d Nb1 = mat3dd(1) - (gscov[0] & gscnt[0]) - (gscov[1] & gscnt[1]);\n                            mat3d Nt1 = nu & (Nb1*nu);\n                            mat3d S1 = s1 & nu;\n                            mat3d Sh1 = (mat3dd(1) - (s1 & s1))*hd;\n                            mat3d Sb1 = s1 & (Nb1*nu);\n                            \n                            // evaluate m, c, Mg, and R\n                            // evaluate L1 from Mg and R\n                            // NOTE: Mg has the 1/detJ included in its definition\n                            vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n                            vec3d c = Sh1*Nh1*m*(1/detJ[j]);\n                            mat3d Mg = (mat3dd(1)*(nu * m) + (nu & m))*(1/detJ[j]);\n                            mat3d B = (c & (Nb1*nu)) - Sh1*Nh1;\n                            mat3d R = mat3dd(1)*(nu * dr) + (nu & dr);\n                            mat3d L1 = Sh1*((Nh1*Mg - mat3dd(1))*(-g) + R)*Nh1;\n                            \n                            // evaluate Mc and Ac and combine them into As\n                            // evaluate Fc from Ac_bar (Ab)\n                            // evaluate Jc as L1*Ac-Fc\n                            double* Gsr = se.Gr(j);\n                            double* Gss = se.Gs(j);\n                            mat3d Ac[MN], As[MN], Pc[MN], Jc[MN];\n                            mat3d gscovh[2];\n                            mat3d dgscovh[2];\n                            gscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n                            dgscovh[0].skew(dgscov[0]); dgscovh[1].skew(dgscov[1]);\n                            for (int k=0; k<nseln; ++k) {\n                                vec3d mc = gscnt[0]*Gsr[k] + gscnt[1]*Gss[k];\n                                mat3d Mc = nu & mc;\n                                Ac[k]    = (gscovh[1]*Gsr[k] - gscovh[0]*Gss[k])/detJ[j];\n                                mat3d Ab = (dgscovh[1]*Gsr[k] - dgscovh[0]*Gss[k])/detJ[j];\n                                vec3d hcp = (N1*mc + Ac[k]*nu)*pt.m_mueff*(-g);\n                                vec3d hcm = (N1*mc*m_mu - Ac[k]*nu*pt.m_mueff);\n                                As[k] = (Ac[k] + Mc*N1);\n                                mat3d Jc = (L1*Ac[k]) - Sh1*Nh1*Ab*(-g);\n                                Pc[k] = (s1 & hcm) + (c & hcp) - Jc*pt.m_mueff;\n                            }\n                            \n                            // evaluate mb and Mb\n                            // evaluate s1 dyad mb and combine as Pb\n                            double Gmr[MN], Gms[MN];\n                            me.shape_deriv(Gmr, Gms, r, s);\n                            vec3d mb[MN];\n                            mat3d Pb[MN];\n                            for (int k=0; k<nmeln; ++k) {\n                                mb[k] = gmcnt[0]*Gmr[k] + gmcnt[1]*Gms[k];\n                                Pb[k] = ((-nu) & mb[k]) - (s1 & mb[k])*pt.m_mueff;\n                            }\n                            \n                            // evaluate Gbc\n                            matrix Gbc(nmeln,nseln);\n                            for (int b=0; b<nmeln; ++b) {\n                                for (int c=0; c<nseln; ++c) {\n                                    Gbc(b,c)\n                                    = (a[0][0]*Gmr[b]*Gsr[c]\n                                       + a[0][1]*Gmr[b]*Gss[c]\n                                       + a[1][0]*Gms[b]*Gsr[c]\n                                       + a[1][1]*Gms[b]*Gss[c])*(-g);\n                                }\n                            }\n                            \n                            // define T, Ttb\n                            mat3d T = N1 + S1*pt.m_mueff;\n                            mat3d Ttb = Nt1 + Sb1*m_mu;\n\n                            // --- S O L I D - S O L I D   C O N T A C T ---\n                            \n                            // a. NxN-term\n                            //------------------------------------\n                            \n                            for (int k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                            for (int k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                            double tmp = detJ[j]*w[j];\n                            for (int l=0; l<nseln+nmeln; ++l)\n                            {\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[k*ndpn  ][l*ndpn  ] -= -tmp*N[k]*N[l]*(eps*Ttb[0][0] + pt.m_mueff*tn*B[0][0]);\n                                    ke[k*ndpn  ][l*ndpn+1] -= -tmp*N[k]*N[l]*(eps*Ttb[0][1] + pt.m_mueff*tn*B[0][1]);\n                                    ke[k*ndpn  ][l*ndpn+2] -= -tmp*N[k]*N[l]*(eps*Ttb[0][2] + pt.m_mueff*tn*B[0][2]);\n                                    \n                                    ke[k*ndpn+1][l*ndpn  ] -= -tmp*N[k]*N[l]*(eps*Ttb[1][0] + pt.m_mueff*tn*B[1][0]);\n                                    ke[k*ndpn+1][l*ndpn+1] -= -tmp*N[k]*N[l]*(eps*Ttb[1][1] + pt.m_mueff*tn*B[1][1]);\n                                    ke[k*ndpn+1][l*ndpn+2] -= -tmp*N[k]*N[l]*(eps*Ttb[1][2] + pt.m_mueff*tn*B[1][2]);\n                                    \n                                    ke[k*ndpn+2][l*ndpn  ] -= -tmp*N[k]*N[l]*(eps*Ttb[2][0] + pt.m_mueff*tn*B[2][0]);\n                                    ke[k*ndpn+2][l*ndpn+1] -= -tmp*N[k]*N[l]*(eps*Ttb[2][1] + pt.m_mueff*tn*B[2][1]);\n                                    ke[k*ndpn+2][l*ndpn+2] -= -tmp*N[k]*N[l]*(eps*Ttb[2][2] + pt.m_mueff*tn*B[2][2]);\n                                }\n                            }\n                            \n                            // b. Na,Nb-term\n                            //-------------------------------------\n                            \n                            tmp = detJ[j]*w[j];\n                            // non-symmetric\n                            for (int l=0; l<nseln; ++l)\n                            {\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[k*ndpn  ][l*ndpn  ] -= -tmp*N[k]*(tn*(As[l][0][0] + Pc[l][0][0]));\n                                    ke[k*ndpn  ][l*ndpn+1] -= -tmp*N[k]*(tn*(As[l][0][1] + Pc[l][0][1]));\n                                    ke[k*ndpn  ][l*ndpn+2] -= -tmp*N[k]*(tn*(As[l][0][2] + Pc[l][0][2]));\n                                    \n                                    ke[k*ndpn+1][l*ndpn  ] -= -tmp*N[k]*(tn*(As[l][1][0] + Pc[l][1][0]));\n                                    ke[k*ndpn+1][l*ndpn+1] -= -tmp*N[k]*(tn*(As[l][1][1] + Pc[l][1][1]));\n                                    ke[k*ndpn+1][l*ndpn+2] -= -tmp*N[k]*(tn*(As[l][1][2] + Pc[l][1][2]));\n                                    \n                                    ke[k*ndpn+2][l*ndpn  ] -= -tmp*N[k]*(tn*(As[l][2][0] + Pc[l][2][0]));\n                                    ke[k*ndpn+2][l*ndpn+1] -= -tmp*N[k]*(tn*(As[l][2][1] + Pc[l][2][1]));\n                                    ke[k*ndpn+2][l*ndpn+2] -= -tmp*N[k]*(tn*(As[l][2][2] + Pc[l][2][2]));\n                                }\n                            }\n                            \n                            // c. Nc,Nd-term\n                            //---------------------------------------\n                            \n                            tmp = detJ[j]*w[j];\n                            // non-symmetric\n                            for (int k=0; k<nmeln; ++k)\n                            {\n                                for (int l=0; l<nseln+nmeln; ++l)\n                                {\n                                    ke[(k+nseln)*ndpn  ][l*ndpn  ] -= tmp*N[l]*tn*Pb[k][0][0];\n                                    ke[(k+nseln)*ndpn  ][l*ndpn+1] -= tmp*N[l]*tn*Pb[k][0][1];\n                                    ke[(k+nseln)*ndpn  ][l*ndpn+2] -= tmp*N[l]*tn*Pb[k][0][2];\n                                    \n                                    ke[(k+nseln)*ndpn+1][l*ndpn  ] -= tmp*N[l]*tn*Pb[k][1][0];\n                                    ke[(k+nseln)*ndpn+1][l*ndpn+1] -= tmp*N[l]*tn*Pb[k][1][1];\n                                    ke[(k+nseln)*ndpn+1][l*ndpn+2] -= tmp*N[l]*tn*Pb[k][1][2];\n                                    \n                                    ke[(k+nseln)*ndpn+2][l*ndpn  ] -= tmp*N[l]*tn*Pb[k][2][0];\n                                    ke[(k+nseln)*ndpn+2][l*ndpn+1] -= tmp*N[l]*tn*Pb[k][2][1];\n                                    ke[(k+nseln)*ndpn+2][l*ndpn+2] -= tmp*N[l]*tn*Pb[k][2][2];\n                                }\n                            }\n\n                            // c. Gbc-term\n                            //---------------------------------------\n                            \n                            tmp = tn*detJ[j]*w[j];\n                            for (int k=0; k<nmeln; ++k)\n                            {\n                                for (int l=0; l<nseln; ++l)\n                                {\n                                    mat3d gT = T*(Gbc[k][l]*tmp);\n                                    ke[(k+nseln)*ndpn  ][l*ndpn  ] -= gT[0][0];\n                                    ke[(k+nseln)*ndpn  ][l*ndpn+1] -= gT[0][1];\n                                    ke[(k+nseln)*ndpn  ][l*ndpn+2] -= gT[0][2];\n                                    \n                                    ke[(k+nseln)*ndpn+1][l*ndpn  ] -= gT[1][0];\n                                    ke[(k+nseln)*ndpn+1][l*ndpn+1] -= gT[1][1];\n                                    ke[(k+nseln)*ndpn+1][l*ndpn+2] -= gT[1][2];\n                                    \n                                    ke[(k+nseln)*ndpn+2][l*ndpn  ] -= gT[2][0];\n                                    ke[(k+nseln)*ndpn+2][l*ndpn+1] -= gT[2][1];\n                                    ke[(k+nseln)*ndpn+2][l*ndpn+2] -= gT[2][2];\n                                }\n                            }\n\n                            // --- B I P H A S I C   S T I F F N E S S ---\n                            if (sporo && mporo)\n                            {\n                                // need to multiply biphasic stiffness entries by the timestep\n                                double dt = fem.GetTime().timeIncrement;\n                                \n                                double dpr = 0, dps = 0;\n                                dpr = me.eval_deriv1(pm, r, s);\n                                dps = me.eval_deriv2(pm, r, s);\n                                \n                                vec3d q2 = gmcnt[0]*dpr + gmcnt[1]*dps;\n                                \n                                // evaluate gc\n                                vector<double> gc(nseln);\n                                for (int k=0; k<nseln; ++k) {\n                                    gc[k]\n                                    = (a[0][0]*dpr*Gsr[k]\n                                       + a[0][1]*dpr*Gss[k]\n                                       + a[1][0]*dps*Gsr[k]\n                                       + a[1][1]*dps*Gss[k])*(-g);\n                                }\n                                \n                                tmp = dt*w[j]*detJ[j];\n                                \n                                double epsp = m_epsp*pt.m_epsp*psf;\n                                \n                                vec3d r = s1*m_mu*(1.0 - m_phi);\n                                \n                                \n                                // --- S O L I D - P R E S S U R E   C O N T A C T ---\n                                \n                                // a. q-term\n                                //-------------------------------------\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                    for (int l=0; l<nseln+nmeln; ++l)\n                                    {\n                                        ke[4*k + 3][4*l  ] += tmp*epsp*N[k]*N[l]*q2.x;\n                                        ke[4*k + 3][4*l+1] += tmp*epsp*N[k]*N[l]*q2.y;\n                                        ke[4*k + 3][4*l+2] += tmp*epsp*N[k]*N[l]*q2.z;\n                                    }\n                                \n                                double wn = pt.m_Lmp + epsp*pt.m_pg;\n                                \n                                // b. A-term\n                                //-------------------------------------\n                                \n                                for (int l=0; l<nseln; ++l) {\n                                    vec3d Acn = Ac[l]*nu;\n                                    for (int k=0; k<nseln+nmeln; ++k)\n                                    {\n                                        ke[4*k + 3][4*l  ] -= tmp*wn*N[k]*Acn.x;\n                                        ke[4*k + 3][4*l+1] -= tmp*wn*N[k]*Acn.y;\n                                        ke[4*k + 3][4*l+2] -= tmp*wn*N[k]*Acn.z;\n                                    }\n                                }\n\n                                // c. s-term\n                                //-------------------------------------\n                                \n                                for (int l=0; l<nseln; ++l) {\n                                    for (int k=0; k<nseln+nmeln; ++k)\n                                    {\n                                        ke[4*k    ][4*l+3] -= tmp*N[k]*N[l]*r.x;\n                                        ke[4*k + 1][4*l+3] -= tmp*N[k]*N[l]*r.y;\n                                        ke[4*k + 2][4*l+3] -= tmp*N[k]*N[l]*r.z;\n                                    }\n                                }\n\n                                // d. m-term\n                                //---------------------------------------\n                                \n                                for (int k=0; k<nmeln; ++k) {\n                                    for (int l=0; l<nseln+nmeln; ++l)\n                                    {\n                                        ke[4*(k+nseln) + 3][4*l  ] += tmp*wn*N[l]*mb[k].x;\n                                        ke[4*(k+nseln) + 3][4*l+1] += tmp*wn*N[l]*mb[k].y;\n                                        ke[4*(k+nseln) + 3][4*l+2] += tmp*wn*N[l]*mb[k].z;\n                                    }\n                                }\n\n                                // e. gc-term\n                                //-------------------------------------\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                    for (int l=0; l<nseln; ++l)\n                                    {\n                                        ke[4*k + 3][4*l  ] -= tmp*epsp*N[k]*gc[l]*nu.x;\n                                        ke[4*k + 3][4*l+1] -= tmp*epsp*N[k]*gc[l]*nu.y;\n                                        ke[4*k + 3][4*l+2] -= tmp*epsp*N[k]*gc[l]*nu.z;\n                                    }\n                                \n                                // f. Gbc-term\n                                //---------------------------------------\n                                \n                                for (int k=0; k<nmeln; ++k) {\n                                    for (int l=0; l<nseln; ++l)\n                                    {\n                                        ke[4*(k+nseln) + 3][4*l  ] -= tmp*wn*Gbc[k][l]*nu.x;\n                                        ke[4*(k+nseln) + 3][4*l+1] -= tmp*wn*Gbc[k][l]*nu.y;\n                                        ke[4*(k+nseln) + 3][4*l+2] -= tmp*wn*Gbc[k][l]*nu.z;\n                                    }\n                                }\n\n                                // --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n                                \n                                // calculate the N-vector\n                                for (int k=0; k<nseln; ++k)\n                                {\n                                    N[ndpn*k  ] = 0;\n                                    N[ndpn*k+1] = 0;\n                                    N[ndpn*k+2] = 0;\n                                    N[ndpn*k+3] = Hs[k];\n                                }\n                                \n                                for (int k=0; k<nmeln; ++k)\n                                {\n                                    N[ndpn*(k+nseln)  ] = 0;\n                                    N[ndpn*(k+nseln)+1] = 0;\n                                    N[ndpn*(k+nseln)+2] = 0;\n                                    N[ndpn*(k+nseln)+3] = -Hm[k];\n                                }\n                                \n                                for (int k=0; k<ndof; ++k)\n                                    for (int l=0; l<ndof; ++l) ke[k][l] -= tmp*epsp*N[k]*N[l];\n                                \n                                \n                            }\n                            \n                            // assemble the global stiffness\n\t\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\t\tLS.Assemble(ke);\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::UpdateContactPressures()\n{\n    int npass = (m_btwo_pass?2:1);\n    const int MN = FEElement::MAX_NODES;\n    const int MI = FEElement::MAX_INTPOINTS;\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingSurfaceBiphasic& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceBiphasic& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all elements of the primary surface\n        for (int n=0; n<ss.Elements(); ++n)\n        {\n            FESurfaceElement& el = ss.Element(n);\n            int nint = el.GaussPoints();\n            \n            // get the normal tractions at the integration points\n            for (int i=0; i<nint; ++i)\n            {\n                // get integration point data\n                FEBiphasicContactPoint& sd = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\t\t\t\t// evaluate traction on primary surface\n                double eps = m_epsn*sd.m_epsn*psf;\n                if (sd.m_bstick) {\n                    // if stick, evaluate total traction\n                    sd.m_tr = sd.m_Lmt + sd.m_dg*eps;\n                    // then derive normal component\n                    sd.m_Ln = -sd.m_tr*sd.m_nu;\n                }\n                else {\n                    // if slip, evaluate normal traction\n                    double Ln = sd.m_Lmd + eps*sd.m_gap;\n                    sd.m_Ln = MBRACKET(Ln);\n                    // then derive total traction\n                    sd.m_tr = -(sd.m_nu*sd.m_Ln + sd.m_s1*sd.m_Ln*sd.m_mueff);\n                    \n                }\n                \n                FESurfaceElement* pme = sd.m_pme;\n                \n                if (m_btwo_pass && pme)\n                {\n                    // get secondary element data\n                    int mint = pme->GaussPoints();\n                    double pi[MI];\n                    vec3d ti[MI];\n                    for (int j=0; j<mint; ++j)\n                    {\n                        FEBiphasicContactPoint& md = static_cast<FEBiphasicContactPoint&>(*pme->GetMaterialPoint(j));\n\n                        // evaluate traction on secondary surface\n                        double eps = m_epsn*md.m_epsn*psf;\n                        if (md.m_bstick) {\n                            // if stick, evaluate total traction\n                            ti[j] = md.m_Lmt + md.m_dg*eps;\n                            // then derive normal component\n                            pi[j] = -ti[j]*md.m_nu;\n                        }\n                        else {\n                            // if slip, evaluate normal traction\n                            double Ln = md.m_Lmd + eps*md.m_gap;\n                            pi[j] = MBRACKET(Ln);\n                            // then derive total traction\n                            ti[j] = -(md.m_nu*pi[j] + md.m_s1*md.m_mueff*pi[j]);\n                        }\n                    }\n                    // project the data to the nodes\n                    double pn[MN];\n                    vec3d tn[MN];\n                    pme->FEElement::project_to_nodes(pi, pn);\n                    pme->project_to_nodes(ti, tn);\n                    // now evaluate the traction at the intersection point\n                    double Ln = pme->eval(pn, sd.m_rs[0], sd.m_rs[1]);\n                    vec3d trac = pme->eval(tn, sd.m_rs[0], sd.m_rs[1]);\n                    sd.m_Ln += MBRACKET(Ln);\n                    // tractions on primary-secondary are opposite, so subtract\n                    sd.m_tr -= trac;\n                }\n            }\n        }\n        ss.EvaluateNodalContactPressures();\n        ss.EvaluateNodalContactTractions();\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterfaceBiphasic::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    int i;\n    double Ln, Lp;\n    bool bconv = true;\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    bool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n    int NS = m_ss.Elements();\n    int NM = m_ms.Elements();\n    \n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0, normP = 0, normDP = 0;\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& ds = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tif (ds.m_bstick)\n                normL0 += ds.m_Lmt*ds.m_Lmt;\n            else\n                normL0 += ds.m_Lmd*ds.m_Lmd;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& dm = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tif (dm.m_bstick)\n                normL0 += dm.m_Lmt*dm.m_Lmt;\n            else\n                normL0 += dm.m_Lmd*dm.m_Lmd;\n        }\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0;\n    double maxpg = 0;\n    \n    // update Lagrange multipliers\n    double normL1 = 0, epsp;\n    for (i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& ds = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on primary surface\n            double eps = m_epsn*ds.m_epsn*psf;\n            if (ds.m_bstick) {\n                // if stick, augment total traction\n                if (m_bsmaug) {\n                    ds.m_Lmt = tn[j];\n                    if (m_btwo_pass) ds.m_Lmt /= 2;\n                }\n                else {\n                    ds.m_Lmt += ds.m_dg*eps;\n                }\n                // then derive normal component\n                ds.m_Lmd = -ds.m_Lmt*ds.m_nu;\n                Ln = ds.m_Lmd;\n                normL1 += ds.m_Lmt*ds.m_Lmt;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(ds.m_dg.norm()));\n            }\n            else {\n                // if slip, augment normal traction\n                if (m_bsmaug) {\n                    Ln = -(tn[j]*ds.m_nu);\n                    ds.m_Lmd = MBRACKET(Ln);\n                    if (m_btwo_pass) ds.m_Lmd /= 2;\n                }\n                else {\n                    Ln = ds.m_Lmd + eps*ds.m_gap;\n                    ds.m_Lmd = MBRACKET(Ln);\n                }\n                // then derive total traction\n                double mueff = m_mu*(1.0-(1.0-m_phi)*ds.m_p1/ds.m_Lmd);\n                if ( ds.m_Lmd < (1-m_phi)*ds.m_p1 )\n                {\n                    mueff = 0.0;\n                }\n                ds.m_Lmt = -(ds.m_nu*ds.m_Lmd + ds.m_s1*ds.m_Lmd*mueff);\n                normL1 += ds.m_Lmd*ds.m_Lmd;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(ds.m_gap));\n            }\n            if (m_ss.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*ds.m_epsp*psf;\n                    Lp = ds.m_Lmp + epsp*ds.m_pg;\n                    maxpg = max(maxpg,fabs(ds.m_pg));\n                    normDP += ds.m_pg*ds.m_pg;\n                }\n                ds.m_Lmp = Lp;\n            }\n        }\n    }\n    \n    for (i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& dm = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on secondary surface\n            double eps = m_epsn*dm.m_epsn*psf;\n            if (dm.m_bstick) {\n                // if stick, augment total traction\n                if (m_bsmaug) {\n                    dm.m_Lmt = tn[j];\n                    if (m_btwo_pass) dm.m_Lmt /= 2;\n                }\n                else {\n                    dm.m_Lmt += dm.m_dg*eps;\n                }\n                // then derive normal component\n                dm.m_Lmd = -dm.m_Lmt*dm.m_nu;\n                Ln = dm.m_Lmd;\n                normL1 += dm.m_Lmt*dm.m_Lmt;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(dm.m_dg.norm()));\n            }\n            else {\n                // if slip, augment normal traction\n                if (m_bsmaug) {\n                    Ln = -(tn[j]*dm.m_nu);\n                    dm.m_Lmd = MBRACKET(Ln);\n                    if (m_btwo_pass) dm.m_Lmd /= 2;\n                }\n                else {\n                    Ln = dm.m_Lmd + eps*dm.m_gap;\n                    dm.m_Lmd = MBRACKET(Ln);\n                }\n                // then derive total traction\n                double mueff = m_mu*(1.0-(1.0-m_phi)*dm.m_p1/dm.m_Lmd);\n                if ( dm.m_Lmd < (1-m_phi)*dm.m_p1 )\n                {\n                    mueff = 0.0;\n                }\n                dm.m_Lmt = -(dm.m_nu*dm.m_Lmd + dm.m_s1*dm.m_Lmd*mueff);\n                normL1 += dm.m_Lmd*dm.m_Lmd;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(dm.m_gap));\n            }\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*dm.m_epsp*psf;\n                    Lp = dm.m_Lmp + epsp*dm.m_pg;\n                    maxpg = max(maxpg,fabs(dm.m_pg));\n                    normDP += dm.m_pg*dm.m_pg;\n                }\n                dm.m_Lmp = Lp;\n            }\n        }\n    }\n    \n    // normP should be a measure of the fluid pressure at the\n    // contact interface.  However, since it could be zero,\n    // use an average measure of the contact traction instead.\n    normP = normL1;\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0));\n    double pnorm = (normP != 0 ? (normDP/normP) : normDP);\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    if ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n    \n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    if ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" sliding interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n    if (bporo) { feLog(\"    P gap        : %15le\", pnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\"); }\n    \n    feLog(\"    maximum gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n    if (bporo) {\n        feLog(\"    maximum pgap : %15le\", maxpg);\n        if (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n    }\n    \n    ProjectSurface(m_ss, m_ms, true);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss, true);\n    \n    m_bfreeze = true;\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::Serialize(DumpStream &ar)\n{\n    // serialize contact data\n    FEContactInterface::Serialize(ar);\n    \n    // serialize contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n\n\t// serialize element pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceBiphasic::MarkFreeDraining()\n{\n    int i, id, np;\n    \n    // Mark all nodes as free-draining.  This needs to be done for ALL\n    // contact interfaces prior to executing Update(), where nodes that are\n    // in contact are subsequently marked as non free-draining.  This ensures\n    // that for surfaces involved in more than one contact interface, nodes\n    // that have been marked as non free-draining are not reset to\n    // free-draining.\n    for (np=0; np<2; ++np)\n    {\n        FESlidingSurfaceBiphasic& s = (np == 0? m_ss : m_ms);\n        \n        if (s.m_bporo) {\n            // first, mark all nodes as free-draining (= neg. ID)\n            // this is done by setting the dof's equation number\n            // to a negative number\n            for (i=0; i<s.Nodes(); ++i)\n            {\n                id = s.Node(i).m_ID[m_dofP];\n                if (id >= 0)\n                {\n                    FENode& node = s.Node(i);\n                    // mark node as free-draining\n                    node.m_ID[m_dofP] = -id-2;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasic::SetFreeDraining()\n{\n    int i, np;\n    \n    // Set the pressure to zero for the free-draining nodes\n    for (np=0; np<2; ++np)\n    {\n        FESlidingSurfaceBiphasic& s = (np == 0? m_ss : m_ms);\n        \n        if (s.m_bporo) {\n            // loop over all nodes\n            for (i=0; i<s.Nodes(); ++i)\n            {\n                if (s.Node(i).m_ID[m_dofP] < -1)\n                {\n                    FENode& node = s.Node(i);\n                    // set the fluid pressure to zero\n                    node.set(m_dofP, 0);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FESlidingInterfaceBiphasic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBiphasicContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingSurfaceBiphasic : public FEBiphasicContactSurface\n{\npublic:\n    //! constructor\n    FESlidingSurfaceBiphasic(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    // data serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialize sliding surface and store previous values\n    void InitSlidingSurface();\n    \n    //! evaluate net contact force\n    vec3d GetContactForce() override;\n    \n    //! evaluate net contact area\n    double GetContactArea() override;\n    \n    //! evaluate net fluid force\n    vec3d GetFluidForce() override;\n    \n    //! calculate the nodal normals\n    void UpdateNodeNormals();\n    \n    void SetPoroMode(bool bporo) { m_bporo = bporo; }\n    \n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\npublic:\n    void GetVectorGap      (int nface, vec3d& pg) override;\n    void GetContactTraction(int nface, vec3d& pt) override;\n    void GetSlipTangent    (int nface, vec3d& pt);\n    void GetMuEffective    (int nface, double& pg) override;\n    void GetLocalFLS       (int nface, double& pg) override;\n    void GetNodalVectorGap      (int nface, vec3d* pg) override;\n    void GetNodalContactPressure(int nface, double* pg) override;\n    void GetNodalContactTraction(int nface, vec3d* pt) override;\n    void GetStickStatus(int nface, double& pg) override;\n    void EvaluateNodalContactPressures();\n    void EvaluateNodalContactTractions();\n\nprivate:\n\tvoid GetContactPressure(int nface, double& pg);\n      \npublic:\n    bool\tm_bporo;\t//!< set poro-mode\n    \n    vector<bool>\t\tm_poro;\t//!< surface element poro status\n    vector<vec3d>\t\tm_nn;\t//!< node normals\n    vector<vec3d>       m_tn;   //!< nodal contact tractions\n    vector<double>      m_pn;   //!< nodal contact pressures\n    \n    vec3d    m_Ft;     //!< total contact force (from equivalent nodal forces)\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingInterfaceBiphasic :\tpublic FEContactInterface\n{\npublic:\n    //! constructor\n    FESlidingInterfaceBiphasic(FEModel* pfem);\n    \n    //! destructor\n    ~FESlidingInterfaceBiphasic();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! interface activation\n    void Activate() override;\n    \n    //! calculate the slip direction on the primary surface\n    vec3d SlipTangent(FESlidingSurfaceBiphasic& ss, const int nel, const int nint, FESlidingSurfaceBiphasic& ms, double& dh, vec3d& r);\n    \n    //! calculate contact traction\n    vec3d ContactTraction(FESlidingSurfaceBiphasic& ss, const int nel, const int n, FESlidingSurfaceBiphasic& ms, double& pn);\n    \n    //! calculate contact pressures for file output\n    void UpdateContactPressures();\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! mark free-draining condition\n    void MarkFreeDraining();\n    \n    //! set free-draining condition\n    void SetFreeDraining();\n    \n    //! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n    //! return integration rule class\n    bool UseNodalIntegration() override { return false; }\n    \n    //! build the matrix profile for use in the stiffness matrix\n    void BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n    void ProjectSurface(FESlidingSurfaceBiphasic& ss, FESlidingSurfaceBiphasic& ms, bool bupseg, bool bmove = false);\n    \n    //! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n    void CalcAutoPenalty(FESlidingSurfaceBiphasic& s);\n    \n    void CalcAutoPressurePenalty(FESlidingSurfaceBiphasic& s);\n    double AutoPressurePenalty(FESurfaceElement& el, FESlidingSurfaceBiphasic& s);\n    \npublic:\n\tFESlidingSurfaceBiphasic\tm_ss;\t//!< primary surface\n\tFESlidingSurfaceBiphasic\tm_ms;\t//!< secondary surface\n    \n    int\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n    bool\t\t\tm_btwo_pass;\t//!< two-pass flag\n    double\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n    double\t\t\tm_gtol;\t\t\t//!< gap tolerance\n    double\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n    double\t\t\tm_stol;\t\t\t//!< search tolerance\n    bool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n    double\t\t\tm_srad;\t\t\t//!< contact search radius\n    int\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n    int\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n    int\t\t\t\tm_nsegup;\t\t//!< segment update parameter\n    bool\t\t\tm_breloc;\t\t//!< node relocation on startup\n    bool            m_bsmaug;       //!< smooth augmentation\n    bool            m_bsmfls;       //!< smooth local fluid load support\n\n    double\t\t\tm_epsn;\t\t    //!< normal penalty factor\n    bool\t\t\tm_bautopen;\t    //!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n    \n    double          m_mu;           //!< friction coefficient\n    bool            m_bfreeze;      //!< freeze stick/slip status\n    bool            m_bflips;       //!< flip primary surface normal\n    bool            m_bflipm;       //!< flip secondary surface normal\n    bool            m_bshellbs;     //!< flag for prescribing pressure on shell bottom for primary surface\n    bool            m_bshellbm;     //!< flag for prescribing pressure on shell bottom for secondary surface\n\n    // biphasic contact parameters\n    double\t        m_epsp;\t\t    //!< flow rate penalty\n    double          m_phi;          //!< solid-solid contact fraction\n    \nprotected:\n    int\tm_dofP;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESlidingInterfaceBiphasicMixed.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingInterfaceBiphasicMixed.h\"\n#include \"FEBiphasic.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n#include <FEBioMech/FEBioMech.h>\n#include \"FEBioMix.h\"\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingInterfaceBiphasicMixed, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\")->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n    ADD_PARAMETER(m_gtol     , \"gaptol\"             )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_ptol     , \"ptol\"               );\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n\tADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_epsp     , \"pressure_penalty\"   );\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n    ADD_PARAMETER(m_srad     , \"search_radius\"      )->setUnits(UNIT_LENGTH);;\n\tADD_PARAMETER(m_nsegup   , \"seg_up\"             );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\n\tADD_PARAMETER(m_breloc   , \"node_reloc\"         );\n\tADD_PARAMETER(m_mu       , \"fric_coeff\"         );\n\tADD_PARAMETER(m_phi      , \"contact_frac\"       );\n\tADD_PARAMETER(m_bsmaug   , \"smooth_aug\"         );\n    ADD_PARAMETER(m_bsmfls   , \"smooth_fls\"         );\n    ADD_PARAMETER(m_bflips   , \"flip_primary\"       );\n    ADD_PARAMETER(m_bflipm   , \"flip_secondary\"     );\n    ADD_PARAMETER(m_bshellbs , \"shell_bottom_primary\"  );\n    ADD_PARAMETER(m_bshellbm , \"shell_bottom_secondary\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FESlidingSurfaceBiphasic\n//-----------------------------------------------------------------------------\n\nFESlidingSurfaceBiphasicMixed::FESlidingSurfaceBiphasicMixed(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{\n    m_bporo = false;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FESlidingSurfaceBiphasicMixed::CreateMaterialPoint()\n{\n\treturn new FEBiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingSurfaceBiphasicMixed::Init()\n{\n\t// get the displacement and fluid pressure variable indices.\n\tFEModel* fem = GetFEModel();\n\tm_varU = fem->GetDOFS().GetVariableIndex(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT)); assert(m_varU >= 0);\n\tm_varP = fem->GetDOFS().GetVariableIndex(FEBioMix::GetVariableName(FEBioMix::FLUID_PRESSURE)); assert(m_varP >= 0);\n\n    // initialize surface data first\n    if (FEBiphasicContactSurface::Init() == false) return false;\n    \n    // allocate node normals and contact tractions\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    m_tn.assign(Nodes(), vec3d(0,0,0));\n    m_pn.assign(Nodes(), 0);\n    \n    // determine biphasic status\n    m_poro.resize(Elements(),false);\n    for (int i=0; i<Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& se = Element(i);\n        \n        // get the element this surface element belongs to\n        FEElement* pe = se.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n            \n            // see if this is a poro-elastic element\n            FEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n            if (biph) {\n                m_poro[i] = true;\n                m_bporo = true;\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::InitSlidingSurface()\n{\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            // Store current surface projection values as previous\n            data.m_rsp  = data.m_rs;\n\t\t\tdata.m_pmep = data.m_pme;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact pressures by averaging values from surrounding\n//! faces.  This function ensures that nodal contact pressures are always\n//! positive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurfaceBiphasicMixed::EvaluateNodalContactPressures()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact pressures\n    zero(m_pn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact pressure for that face\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_pn[el.m_lnode[j]] += pn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_pn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact tractions by averaging values from surrounding\n//! faces.  This function ensures that nodal contact tractions are always\n//! compressive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurfaceBiphasicMixed::EvaluateNodalContactTractions()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact tractions\n    zero(m_tn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact traction and pressure for that face\n        vec3d tn(0,0,0);\n        GetContactTraction(i, tn);\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_tn[el.m_lnode[j]] += tn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_tn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the\n//! element normals at the node\n\nvoid FESlidingSurfaceBiphasicMixed::UpdateNodeNormals()\n{\n    const int MN = FEElement::MAX_NODES;\n    vec3d y[MN];\n    \n    // zero nodal normals\n    zero(m_nn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the nodal coordinates\n        for (int j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n        \n        // calculate the normals\n        for (int j=0; j<ne; ++j)\n        {\n            int jp1 = (j+1)%ne;\n            int jm1 = (j+ne-1)%ne;\n            vec3d n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n            m_nn[el.m_lnode[j]] += n;\n        }\n    }\n    \n    // normalize all vectors\n    const int N = Nodes();\n    for (int i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurfaceBiphasicMixed::GetContactForce()\n{\n    return m_Ft;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurfaceBiphasicMixed::GetContactArea()\n{\n    // initialize contact area\n    double a = 0;\n    \n    // loop over all elements of the primary surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        int nint = el.GaussPoints();\n        \n        // evaluate the contact force for that element\n        for (int i=0; i<nint; ++i)\n        {\n            // get data for this integration point\n            FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n            if (data.m_Ln > 0)\n            {\n                // get the base vectors\n                vec3d g[2];\n                CoBaseVectors(el, i, g);\n                \n                // normal (magnitude = area)\n                vec3d n = g[0] ^ g[1];\n                \n                // gauss weight\n                double w = el.GaussWeights()[i];\n                \n                // contact force\n                a += n.norm()*w;\n            }\n        }\n    }\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurfaceBiphasicMixed::GetFluidForce()\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n    const int MN = FEElement::MAX_NODES;\n    double pn[MN];\n    \n    // initialize contact force\n    vec3d f(0,0,0);\n    \n    // loop over all elements of the surface\n    for (int n=0; n<Elements(); ++n)\n    {\n        FESurfaceElement& el = Element(n);\n        int nseln = el.Nodes();\n        \n        // nodal pressures\n\t\tint npdof = el.ShapeFunctions(degree_p);\n        for (int i=0; i<npdof; ++i) pn[i] = GetMesh()->Node(el.m_node[i]).get(m_dofP);\n        \n       \n        // evaluate the fluid force for that element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int i=0; i<nint; ++i)\n        {\n            // get the base vectors\n            vec3d g[2];\n            CoBaseVectors(el, i, g);\n            // normal (magnitude = area)\n            vec3d n = g[0] ^ g[1];\n            // gauss weight\n            double w = el.GaussWeights()[i];\n            // fluid pressure\n            double p = el.eval(degree_p, pn, i);\n            // contact force\n            f += n*(w*p);\n        }\n    }\n    \n    return f;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_bporo;\n\tar & m_poro;\n\tar & m_nn;\n\tar & m_pn;\n\tar & m_tn;\n\tar & m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetVectorGap(int nface, vec3d& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_dg;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetContactPressure(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_Ln;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpt += data.m_tr;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetSlipTangent(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tif (!data.m_bstick) pt += data.m_s1;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetMuEffective(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_mueff;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetLocalFLS(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n    for (int k = 0; k < ni; ++k)\n    {\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        pg += data.m_fls;\n    }\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetNodalVectorGap(int nface, vec3d* pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vec3d gi[FEElement::MAX_INTPOINTS];\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tgi[k] = data.m_dg;\n\t}\n    el.project_to_nodes(gi, pg);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetNodalContactPressure(int nface, double* pg)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n    FESurfaceElement& el = Element(nface);\n\tint npdof = el.ShapeFunctions(degree_p);\n    for (int k=0; k<npdof; ++k)\n        pg[k] = m_pn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetStickStatus(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tif (data.m_bstick) pg += 1.0;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::GetNodalContactTraction(int nface, vec3d* tn)\n{\n    FESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        tn[k] = m_tn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceBiphasicMixed::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_varP);\n\n\t// We should allocate the number of shapefunctions for each variable,\n\t// but for now, we allocate assuming the shape functions equals the nodes.\n\t// We can do this, because the nodes that don't have a pressure dof,\n\t// will have their id set to -1. \n\tint N = el.Nodes();\n\tlm.assign(N * 4, -1);\n\n\t// pack the equation numbers\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3 * i    ] = id[m_dofX];\n\t\tlm[3 * i + 1] = id[m_dofY];\n\t\tlm[3 * i + 2] = id[m_dofZ];\n\n\t\t// now the pressure dofs\n\t\tif (m_dofP >= 0) lm[3 * N + i] = id[m_dofP];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingInterfaceBiphasicMixed\n//-----------------------------------------------------------------------------\n\nFESlidingInterfaceBiphasicMixed::FESlidingInterfaceBiphasicMixed(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n    static int count = 1;\n    SetID(count++);\n    \n    // initial values\n    m_knmult = 0;\n    m_atol = 0.1;\n    m_epsn = 1;\n    m_epsp = 1;\n    m_btwo_pass = false;\n    m_stol = 0.01;\n    m_bsymm = true;\n    m_srad = 1.0;\n    m_gtol = 0;\n    m_ptol = 0;\n    m_nsegup = 0;\n    m_bautopen = false;\n    m_breloc = false;\n    m_bsmaug = false;\n    m_bsmfls = true;\n    m_bupdtpen = false;\n    m_mu = 0.0;\n    m_phi = 0.0;\n    \n    m_naugmin = 0;\n    m_naugmax = 10;\n    \n    m_bfreeze = false;\n    m_bflipm = m_bflips = false;\n    m_bshellbm = m_bshellbs = false;\n\n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    \n    // set parents\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n\n    m_ss.SetSibling(&m_ms);\n    m_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFESlidingInterfaceBiphasicMixed::~FESlidingInterfaceBiphasicMixed()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterfaceBiphasicMixed::Init()\n{\n    // initialize surface data\n    if (m_ss.Init() == false) return false;\n    if (m_ms.Init() == false) return false;\n    \n    // Flip secondary and primary surfaces, if requested.\n    // Note that we turn off those flags because otherwise we keep flipping, each time we get here (e.g. in optimization)\n    // TODO: Of course, we shouldn't get here more than once. I think we also get through the FEModel::Reset, so I'll have\n    //       look into that.\n    if (m_bflips) { m_ss.Invert(); m_bflips = false; }\n    if (m_bflipm) { m_ms.Invert(); m_bflipm = false; }\n    if (m_bshellbs) { m_ss.SetShellBottom(m_bshellbs); m_bshellbs = false; }\n    if (m_bshellbm) { m_ms.SetShellBottom(m_bshellbm); m_bshellbm = false; }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // get the DOFS\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_P = fem.GetDOFIndex(\"p\");\n    const int dof_RU = fem.GetDOFIndex(\"Ru\");\n    const int dof_RV = fem.GetDOFIndex(\"Rv\");\n    const int dof_RW = fem.GetDOFIndex(\"Rw\");\n    \n    vector<int> lm(7*FEElement::MAX_NODES*2);\n    \n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingSurfaceBiphasicMixed& ss = (np == 0? m_ss : m_ms);\n        \n        int k, l;\n        for (int j=0; j<ss.Elements(); ++j)\n        {\n            FESurfaceElement& se = ss.Element(j);\n            int nint = se.GaussPoints();\n            int* sn = &se.m_node[0];\n            for (k=0; k<nint; ++k)\n            {\n                FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(k));\n                FESurfaceElement* pe = pt.m_pme;\n                if (pe != 0)\n                {\n                    FESurfaceElement& me = *pe;\n                    int* mn = &me.m_node[0];\n                    \n                    assign(lm, -1);\n                    \n                    int nseln = se.Nodes();\n                    int nmeln = me.Nodes();\n                    \n                    for (l=0; l<nseln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(sn[l]).m_ID;\n                        lm[7*l  ] = id[dof_X];\n                        lm[7*l+1] = id[dof_Y];\n                        lm[7*l+2] = id[dof_Z];\n                        lm[7*l+3] = id[dof_P];\n                        lm[7*l+4] = id[dof_RU];\n                        lm[7*l+5] = id[dof_RV];\n                        lm[7*l+6] = id[dof_RW];\n                    }\n                    \n                    for (l=0; l<nmeln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(mn[l]).m_ID;\n                        lm[7*(l+nseln)  ] = id[dof_X];\n                        lm[7*(l+nseln)+1] = id[dof_Y];\n                        lm[7*(l+nseln)+2] = id[dof_Z];\n                        lm[7*(l+nseln)+3] = id[dof_P];\n                        lm[7*(l+nseln)+4] = id[dof_RU];\n                        lm[7*(l+nseln)+5] = id[dof_RV];\n                        lm[7*(l+nseln)+6] = id[dof_RW];\n                    }\n                    \n                    K.build_add(lm);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n        CalcAutoPressurePenalty(m_ss);\n        CalcAutoPressurePenalty(m_ms);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function is called during the initialization\nvoid FESlidingInterfaceBiphasicMixed::Activate()\n{\n    // don't forget to call base member\n    FEContactInterface::Activate();\n    \n    UpdateAutoPenalty();\n    \n    // update sliding interface data\n    Update();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::CalcAutoPenalty(FESlidingSurfaceBiphasicMixed& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::CalcAutoPressurePenalty(FESlidingSurfaceBiphasicMixed& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPressurePenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\ndouble FESlidingInterfaceBiphasicMixed::AutoPressurePenalty(FESurfaceElement& el, FESlidingSurfaceBiphasicMixed& s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // evaluate element surface normal at parametric center\n    vec3d t[2];\n    s.CoBaseVectors0(el, 0, 0, t);\n    vec3d n = t[0] ^ t[1];\n    n.unit();\n    \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n\n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        \n    // see if this is a poro-elastic element\n    FEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n    if (biph == 0) return 0.0;\n\n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n    FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n            \n    // setup the material point\n    ept.m_F = mat3dd(1.0);\n    ept.m_J = 1;\n    ept.m_s.zero();\n            \n    // if this is a poroelastic element, then get the permeability tensor\n    FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n    pt.m_p = 0;\n    pt.m_w = vec3d(0,0,0);\n            \n    double K[3][3];\n    biph->Permeability(K, mp);\n            \n    double eps = n.x*(K[0][0]*n.x+K[0][1]*n.y+K[0][2]*n.z)\n    +n.y*(K[1][0]*n.x+K[1][1]*n.y+K[1][2]*n.z)\n    +n.z*(K[2][0]*n.x+K[2][1]*n.y+K[2][2]*n.z);\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n    return eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::ProjectSurface(FESlidingSurfaceBiphasicMixed& ss, FESlidingSurfaceBiphasicMixed& ms, bool bupseg, bool bmove)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(ss.m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(ss.m_varP);\n\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    \n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n    double psf = GetPenaltyScaleFactor();\n\n    // if we need to project the nodes onto the secondary surface,\n    // let's do this first\n    if (bmove)\n    {\n        int NN = ss.Nodes();\n        int NE = ss.Elements();\n        // first we need to calculate the node normals\n        vector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n        for (int i=0; i<NE; ++i)\n        {\n            FESurfaceElement& el = ss.Element(i);\n            int ne = el.Nodes();\n            for (int j=0; j<ne; ++j)\n            {\n                vec3d r0 = ss.Node(el.m_lnode[ j         ]).m_rt;\n                vec3d rp = ss.Node(el.m_lnode[(j+   1)%ne]).m_rt;\n                vec3d rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).m_rt;\n                vec3d n = (rp - r0)^(rm - r0);\n                normal[el.m_lnode[j]] += n;\n            }\n        }\n        for (int i=0; i<NN; ++i) normal[i].unit();\n        \n        // loop over all nodes\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = ss.Node(i);\n            \n            // get the spatial nodal coordinates\n            vec3d rt = node.m_rt;\n            vec3d nu = normal[i];\n            \n            // project onto the secondary surface\n            vec3d q;\n            double rs[2] = {0,0};\n            FESurfaceElement* pme = np.Project(rt, nu, rs);\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double gap = nu*(rt - q);\n                \n                if (gap>0) node.m_r0 = node.m_rt = q;\n            }\n        }\n    }\n    \n    // loop over all integration points\n#pragma omp parallel for\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        bool sporo = ss.m_poro[i];\n        \n        int ne = el.Nodes();\n        int nint = el.GaussPoints();\n        double ps[FEElement::MAX_INTPOINTS];\n        // get the nodal pressures\n        if (sporo)\n        {\n\t\t\tint npd = el.ShapeFunctions(degree_p);\n            for (int j=0; j<npd; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        }\n        \n        for (int j=0; j<nint; ++j)\n        {\n            // get the integration point data\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // calculate the global position of the integration point\n            vec3d r = ss.Local2Global(el, j);\n            \n            // get the pressure at the integration point\n            double p1 = 0;\n            if (sporo) p1 = el.eval(degree_p, ps, j);\n\n            // calculate the normal at this integration point\n            vec3d nu = ss.SurfaceNormal(el, j);\n            \n            // first see if the old intersected face is still good enough\n            FESurfaceElement* pme = pt.m_pme;\n            double rs[2] = {0,0};\n            if (pme)\n            {\n                double g;\n                \n                // see if the ray intersects this element\n                if (ms.Intersect(*pme, r, nu, rs, g, m_stol))\n                {\n                    pt.m_rs[0] = rs[0];\n                    pt.m_rs[1] = rs[1];\n                }\n                else\n                {\n                    pme = 0;\n                }\n            }\n            \n            // find the intersection point with the secondary surface\n            if (pme == 0 && bupseg) pme = np.Project(r, nu, rs);\n            \n            pt.m_pme = pme;\n            pt.m_nu = nu;\n            pt.m_rs[0] = rs[0];\n            pt.m_rs[1] = rs[1];\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double g = nu*(r - q);\n                \n                double eps = m_epsn*pt.m_epsn*psf;\n                \n                double Ln = pt.m_Lmd + eps*g;\n                \n                pt.m_gap = (g <= m_srad? g : 0);\n                \n                // calculate the pressure gap function\n                bool mporo = ms.m_poro[pme->m_lid];\n                \n                if ((Ln >= 0) && (g <= m_srad))\n                {\n                    \n                    // get the pressure at the projection point\n                    double p2 = 0;\n                    if (mporo) {\n                        double pm[FEElement::MAX_NODES];\n\t\t\t\t\t\tint npdof = pme->ShapeFunctions(degree_p);\n                        for (int k=0; k<npdof; ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n                        p2 = pme->eval(degree_p, pm, rs[0], rs[1]);\n                    }\n                    if (sporo) {\n                        pt.m_p1 = p1;\n                        if (mporo) {\n                            pt.m_pg = p1 - p2;\n                        }\n                    }\n                    else if (mporo) {\n                        pt.m_p1 = p2;\n                    }\n                }\n                else\n                {\n                    pt.m_Lmd = 0;\n                    pt.m_pme = 0;\n                    pt.m_gap = 0;\n                    pt.m_dg = pt.m_Lmt = vec3d(0,0,0);\n                    if (sporo || mporo) {\n                        pt.m_Lmp = 0;\n                        pt.m_pg = 0;\n                        pt.m_p1 = 0;\n                    }\n                }\n            }\n            else\n            {\n                // the node is not in contact\n                pt.m_Lmd = 0;\n                pt.m_gap = 0;\n                pt.m_dg = pt.m_Lmt = vec3d(0,0,0);\n                if (sporo) {\n                    pt.m_Lmp = 0;\n                    pt.m_pg = 0;\n                    pt.m_p1 = 0;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceBiphasicMixed::Update()\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_ss.m_varP);\n\n    static int naug = 0;\n    static int biter = 0;\n    \n    FEModel& fem = *GetFEModel();\n    \n    // get the iteration number\n    // we need this number to see if we can do segment updates or not\n    // also reset number of iterations after each augmentation\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolver* psolver = pstep->GetFESolver();\n    if (psolver->m_niter == 0) {\n        biter = 0;\n        naug = psolver->m_naug;\n        // check update of auto-penalty\n        if (m_bupdtpen) UpdateAutoPenalty();\n    } else if (psolver->m_naug > naug) {\n        biter = psolver->m_niter;\n        naug = psolver->m_naug;\n    }\n    int niter = psolver->m_niter - biter;\n    bool bupseg = ((m_nsegup == 0)? true : (niter <= m_nsegup));\n    // get the logfile\n    //\tLogfile& log = GetLogfile();\n    //\tlog.printf(\"seg_up iteration # %d\\n\", niter+1);\n    \n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    static bool bfirst = true;\n    ProjectSurface(m_ss, m_ms, bupseg, (m_breloc && bfirst));\n    if (m_btwo_pass || m_ms.m_bporo) ProjectSurface(m_ms, m_ss, bupseg);\n    bfirst = false;\n    \n    // Call InitSlidingSurface on the first iteration of each time step\n\tint nsolve_iter = psolver->m_niter;\n    if (nsolve_iter == 0)\n    {\n        m_ss.InitSlidingSurface();\n        if (m_btwo_pass) m_ms.InitSlidingSurface();\n        m_bfreeze = false;\n    }\n    \n    // Update the net contact pressures\n    UpdateContactPressures();\n    \n    if (niter == 0) m_bfreeze = false;\n    \n    // set poro flag\n    bool bporo = (m_ss.m_bporo || m_ms.m_bporo);\n    \n    // only continue if we are doing a poro-elastic simulation\n    if (bporo == false) return;\n    \n    // update node normals\n    m_ss.UpdateNodeNormals();\n    if (bporo) m_ms.UpdateNodeNormals();\n    \n    // Now that the nodes have been projected, we need to figure out\n    // if we need to modify the constraints on the pressure dofs.\n    // If the nodes are not in contact, they must be free\n    // draining. Since all nodes have been previously marked to be\n    // free-draining in MarkFreeDraining(), we just need to reverse\n    // this setting here, for nodes that are in contact.\n    \n    // Next, we loop over each surface, visiting the nodes\n    // and finding out if that node is in contact or not\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n\t\tFESlidingSurfaceBiphasicMixed& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurfaceBiphasicMixed& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all the nodes of the primary surface\n        for (int n=0; n<ss.Nodes(); ++n) {\n            FENode& node = ss.Node(n);\n            int id = node.m_ID[m_dofP];\n            if ((id < -1) && (ss.m_pn[n] > 0))\n            {\n                // mark node as non-free-draining (= pos ID)\n                node.m_ID[m_dofP] = -id-2;\n            }\n        }\n        \n        // loop over all nodes of the secondary surface\n        // the secondary surface is trickier since we need\n        // to look at the primary surface's projection\n        if (ms.m_bporo) {\n            FENormalProjection np(ss);\n            np.SetTolerance(m_stol);\n            np.SetSearchRadius(m_srad);\n            np.Init();\n            \n            for (int n=0; n<ms.Nodes(); ++n)\n            {\n                // get the node\n                FENode& node = ms.Node(n);\n                \n                // project it onto the primary surface\n                double rs[2] = {0,0};\n                FESurfaceElement* pse = np.Project(node.m_rt, ms.m_nn[n], rs);\n                \n                if (pse)\n                {\n                    // we found an element, so let's see if it's even remotely close to contact\n                    // find the global location of the intersection point\n                    vec3d q = ss.Local2Global(*pse, rs[0], rs[1]);\n                    \n                    // calculate the gap function\n                    double g = ms.m_nn[n]*(node.m_rt - q);\n                    \n                    if (fabs(g) <= m_srad)\n                    {\n                        // we found an element so let's calculate the nodal traction values for this element\n                        // get the normal tractions at the nodes\n                        \n                        double tn[FEElement::MAX_NODES];\n\t\t\t\t\t\tint N = pse->ShapeFunctions(degree_p);\n                        for (int i=0; i<N; ++i)\n                            tn[i] = ss.m_pn[pse->m_lnode[i]];\n                        \n                        // now evaluate the traction at the intersection point\n                        double tp = pse->eval(degree_p, tn, rs[0], rs[1]);\n                        \n                        // if tp > 0, mark node as non-free-draining. (= pos ID)\n                        int id = node.m_ID[m_dofP];\n                        if ((id < -1) && (tp > 0))\n                        {\n                            // mark as non free-draining\n                            node.m_ID[m_dofP] = -id-2;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingInterfaceBiphasicMixed::SlipTangent(FESlidingSurfaceBiphasicMixed& ss, const int nel, const int nint, FESlidingSurfaceBiphasicMixed& ms, double& dh, vec3d& r)\n{\n    vec3d s1(0,0,0);\n    dh = 0;\n    r = vec3d(0,0,0);\n    \n    // get primary surface element\n    FESurfaceElement& se = ss.Element(nel);\n    \n    // get integration point data\n    FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(nint));\n\tdouble g = data.m_gap;\n    vec3d nu = data.m_nu;\n    \n    // find secondary surface element\n    FESurfaceElement* pme = data.m_pme;\n    \n    // calculate previous positions\n    vec3d x2p = ms.Local2GlobalP(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d x1p = ss.Local2GlobalP(se, nint);\n    \n    // calculate dx2\n    vec3d x2 = ms.Local2Global(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d dx2 = x2 - x2p;\n    \n    // calculate dx1\n    vec3d x1 = ss.Local2Global(se, nint);\n    vec3d dx1 = x1 - x1p;\n    \n    // get current and previous covariant basis vectors\n    vec3d gscov[2], gscovp[2];\n    ss.CoBaseVectors(se, nint, gscov);\n    ss.CoBaseVectorsP(se, nint, gscovp);\n    \n    // calculate delta gscov\n    vec3d dgscov[2];\n    dgscov[0] = gscov[0] - gscovp[0];\n    dgscov[1] = gscov[1] - gscovp[1];\n    \n    // calculate m, J, Nhat\n    vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n    double detJ = (gscov[0] ^ gscov[1]).norm();\n    mat3d Nhat = (mat3dd(1) - (nu & nu));\n    \n    // calculate c\n    vec3d c = Nhat*m*(1.0/detJ);\n    \n    // calculate slip direction s1\n    double norm = (Nhat*(c*(-g) + dx1 - dx2)).norm();\n    if (norm != 0)\n    {\n        s1 = (Nhat*(c*(-g) + dx1 - dx2))/norm;\n        dh = norm;\n        r = c*(-g) + dx1 - dx2;\n    }\n    \n    return s1;\n    \n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingInterfaceBiphasicMixed::ContactTraction(FESlidingSurfaceBiphasicMixed& ss, const int nel, const int n, FESlidingSurfaceBiphasicMixed& ms, double& pn)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(ss.m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(ss.m_varP);\n\n    vec3d s1(0,0,0);\n    vec3d dr(0,0,0);\n    vec3d t(0,0,0);\n    pn = 0;\n    double tn = 0, ts = 0, mueff = 0;\n    double psf = GetPenaltyScaleFactor();\n\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n\n\t// get the primary surface element\n\tFESurfaceElement& se = ss.Element(nel);\n\n    // get the integration point data\n    FEBiphasicContactPoint& data = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(n));\n\n    // penalty\n    double eps = m_epsn*data.m_epsn*psf;\n    \n    // normal gap\n    double g = data.m_gap;\n    \n    // normal traction Lagrange multiplier\n    double Lm = data.m_Lmd;\n    \n    // vector traction Lagrange multiplier\n    vec3d Lt = data.m_Lmt;\n    \n    // get the normal at this integration point\n    vec3d nu = data.m_nu;\n    \n    // get the fluid pressure at this integration point\n    double p = data.m_p1;\n    \n    // get poro status of primary surface\n    bool sporo = ss.m_poro[nel];\n    \n    // get current and previous secondary elements\n    FESurfaceElement* pme = data.m_pme;\n    FESurfaceElement* pmep = data.m_pmep;\n    \n    // zero the effective friction coefficient\n    data.m_mueff = 0.0;\n    data.m_fls = 0.0;\n    data.m_s1 = vec3d(0,0,0);\n    \n    // get local FLS from element projection\n    double fls = 0;\n    if (m_bsmfls) {\n        double lfls[FEElement::MAX_INTPOINTS];\n        ss.GetGPLocalFLS(nel, lfls);\n        fls = lfls[n];\n    }\n    \n    // if we just returned from an augmentation, do not update stick or slip status\n    if (m_bfreeze && pme) {\n        if (data.m_bstick) {\n            // calculate current global position of the integration point\n            vec3d xo = ss.Local2Global(se, n);\n            \n            // calculate current global position of the previous intersection point\n            vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n            \n            // calculate vector gap\n            vec3d dg = xt - xo;\n            \n            // calculate trial stick traction, normal component, shear component\n            t = Lt + dg*eps;\n            tn = t*nu;\n            ts = (t - nu*tn).norm();\n            \n            // contact pressure\n            pn = MBRACKET(-tn);\n            \n            // calculate effective friction coefficient\n            if (pn > 0)\n            {\n                data.m_mueff = ts/pn;\n                data.m_fls = m_bsmfls ? fls : p/pn;\n            }\n\n            // store the previous values as the current\n            data.m_pme = data.m_pmep;\n            data.m_rs = data.m_rsp;\n            \n            // recalculate gap\n            data.m_dg = dg;\n            \n            // recalculate pressure gap\n            bool mporo = ms.m_poro[pme->m_lid];\n            if (sporo && mporo)\n            {\n                double pm[FEElement::MAX_NODES];\n\t\t\t\tint npdof = pme->ShapeFunctions(degree_p);\n                for (int k=0; k<npdof; ++k) pm[k] = m.Node(pme->m_node[k]).get(m_dofP);\n                double p2 = pme->eval(degree_p, pm, data.m_rs[0], data.m_rs[1]);\n                data.m_pg = p - p2;\n            }\n            \n        }\n        else {\n            // recalculate contact pressure for slip\n            pn = MBRACKET(Lm + eps*g);\n            \n            if (pn != 0)\n            {\n                \n                double dh = 0;\n                \n                // slip direction\n                s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                \n                // calculate effective friction coefficient\n                data.m_fls = m_bsmfls ? fls : p/pn;\n                data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                data.m_mueff = MBRACKET(data.m_mueff);\n\n                // total traction\n                t = nu*(-pn) - s1*pn*data.m_mueff;\n                \n                // reset slip direction\n                data.m_s1 = s1;\n            }\n            else\n            {\n                t = vec3d(0,0,0);\n            }\n        }\n    }\n    // update contact tractions\n    else {\n        data.m_bstick = false;\n        \n        if (pme)\n        {\n            // assume stick and calculate traction\n            if (pmep)\n            {\n                // calculate current global position of the integration point\n                vec3d xo = ss.Local2Global(se, n);\n                \n                // calculate current global position of the previous intersection point\n                vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n                \n                // calculate vector gap\n                vec3d dg = xt - xo;\n                \n                // calculate trial stick traction, normal component, shear component\n                t = Lt + dg*eps;\n                tn = t*nu;\n                ts = (t - nu*tn).norm();\n                \n                // calculate effective friction coefficient\n                if (tn != 0)\n                {\n                    data.m_fls = m_bsmfls ? fls : p/(-tn);\n                    mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                    mueff = MBRACKET(mueff);\n                }\n                \n                // check if stick\n                if ( (tn < 0) && (ts < fabs(tn*mueff)) )\n                {\n                    // set boolean flag for stick\n                    data.m_bstick = true;\n                    \n                    // contact pressure\n                    pn = MBRACKET(-tn);\n                    \n                    // calculate effective friction coefficient\n                    if (pn > 0) {\n                        data.m_mueff = ts/pn;\n                        data.m_fls = m_bsmfls ? fls : p/pn;\n                    }\n\n                    // store the previous values as the current\n                    data.m_pme = data.m_pmep;\n                    data.m_rs = data.m_rsp;\n                    \n                    // recalculate gaps\n                    data.m_dg = dg;\n                    \n                    // recalculate pressure gap\n                    bool mporo = ms.m_poro[pme->m_lid];\n                    if (sporo && mporo)\n                    {\n                        double pm[FEElement::MAX_NODES];\n                        for (int k=0; k<pme->Nodes(); ++k) pm[k] = m.Node(pme->m_node[k]).get(m_dofP);\n                        double p2 = pme->eval(pm, data.m_rs[0], data.m_rs[1]);\n                        data.m_pg = p - p2;\n                    }\n                    \n                }\n                else\n                {\n                    // recalculate contact pressure for slip\n                    pn = MBRACKET(Lm + eps*g);\n                    \n                    if (pn != 0)\n                    {\n                        \n                        double dh = 0;\n                        \n                        // slip direction\n                        s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                        \n                        // calculate effective friction coefficient\n                        data.m_fls = m_bsmfls ? fls : p/pn;\n                        data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                        data.m_mueff = MBRACKET(data.m_mueff);\n\n                        // total traction\n                        t = nu*(-pn) - s1*pn*data.m_mueff;\n                        \n                        // reset slip direction\n                        data.m_s1 = s1;\n                        data.m_bstick = false;\n                    }\n                    else\n                    {\n                        t = vec3d(0,0,0);\n                    }\n                    \n                }\n            }\n            else\n            {\n                // assume slip upon first contact\n                // calculate contact pressure for slip\n                pn = MBRACKET(Lm + eps*g);\n                \n                if (pn != 0)\n                {\n                    \n                    double dh = 0;\n                    \n                    // slip direction\n                    s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                    \n                    // calculate effective friction coefficient\n                    data.m_fls = m_bsmfls ? fls : p/pn;\n                    data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                    data.m_mueff = MBRACKET(data.m_mueff);\n\n                    // total traction\n                    t = nu*(-pn) - s1*pn*data.m_mueff;\n                    \n                    // reset slip direction\n                    data.m_s1 = s1;\n                    data.m_bstick = false;\n                }\n            }\n        }\n    }\n    \n    return t;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\t// we will also calculate net contact forces, so zero them here\n\tm_ss.m_Ft = vec3d(0, 0, 0);\n\tm_ms.m_Ft = vec3d(0, 0, 0);\n\n\t// loop over the nr of passes\n\tint npass = (m_btwo_pass ? 2 : 1);\n\tfor (int np = 0; np < npass; ++np)\n\t{\n\t\t// get primary and secondary surface\n\t\tFESlidingSurfaceBiphasicMixed& ss = (np == 0 ? m_ss : m_ms);\n\t\tFESlidingSurfaceBiphasicMixed& ms = (np == 0 ? m_ms : m_ss);\n\n\t\t// assemble the load vector for this pass\n\t\tLoadVector(ss, ms, R, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::LoadVector(FESlidingSurfaceBiphasicMixed& ss, FESlidingSurfaceBiphasicMixed& ms, FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(ss.m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(ss.m_varP);\n\n    const int MN = FEElement::MAX_NODES;\n    \n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    double detJ[MN], w[MN], *Hs, Hm[MN], Hmp[MN];\n    double N[4*MN*2];\n    \n    // need to multiply biphasic force entries by the timestep\n    double dt = tp.timeIncrement;\n    \n    // loop over all primary surface elements\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& se = ss.Element(i);\n            \n\t\t// flag indicating that primary element is poro\n        bool sporo = ss.m_poro[i];\n            \n        // get the nr of nodes and integration points\n        int nseln = se.Nodes();\n        int nint = se.GaussPoints();\n            \n        // copy the LM vector; we'll need it later\n        ss.UnpackLM(se, sLM);\n            \n        // we calculate all the metrics we need before we\n        // calculate the nodal forces\n        for (int j=0; j<nint; ++j)\n        {\n            // get the base vectors\n            vec3d g[2];\n            ss.CoBaseVectors(se, j, g);\n                \n            // jacobians: J = |g0xg1|\n            detJ[j] = (g[0] ^ g[1]).norm();\n                \n            // integration weights\n            w[j] = se.GaussWeights()[j];\n        }\n            \n        // loop over all integration points\n        // note that we are integrating over the current surface\n        for (int j=0; j<nint; ++j)\n        {\n            // get the integration point data\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n            // calculate contact pressure and account for stick\n            double pn;\n            vec3d t = ContactTraction(ss, i, j, ms, pn);\n                \n            // get the secondary element\n            FESurfaceElement* pme = pt.m_pme;\n                \n            if (pme)\n            {\n                // get the secondary element\n                FESurfaceElement& me = *pme;\n                    \n\t\t\t\t// get the secondary element poro status\n                bool mporo = ms.m_poro[pme->m_lid];\n                    \n                // get the nr of secondary element nodes\n                int nmeln = me.Nodes();\n                    \n                // copy LM vector\n                ms.UnpackLM(me, mLM);\n                    \n                // calculate degrees of freedom\n                int ndof = 3*(nseln + nmeln);\n                    \n                // build the LM vector\n                LM.resize(ndof);\n                for (int k=0; k<nseln; ++k)\n                {\n                    LM[3*k  ] = sLM[3*k  ];\n                    LM[3*k+1] = sLM[3*k+1];\n                    LM[3*k+2] = sLM[3*k+2];\n                }\n                    \n                for (int k=0; k<nmeln; ++k)\n                {\n                    LM[3*(k+nseln)  ] = mLM[3*k  ];\n                    LM[3*(k+nseln)+1] = mLM[3*k+1];\n                    LM[3*(k+nseln)+2] = mLM[3*k+2];\n                }\n                    \n                // build the en vector\n                en.resize(nseln+nmeln);\n                for (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                for (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                // get primary element shape functions\n                Hs = se.H(j);\n                    \n                // get secondary element shape functions\n                double r = pt.m_rs[0];\n                double s = pt.m_rs[1];\n                me.shape_fnc(Hm, r, s);\n                    \n                if (pn > 0) {\n                        \n                    // calculate the force vector\n                    fe.resize(ndof);\n                    zero(fe);\n                        \n                    for (int k=0; k<nseln; ++k)\n                    {\n                        N[3*k  ] = Hs[k]*t.x;\n                        N[3*k+1] = Hs[k]*t.y;\n                        N[3*k+2] = Hs[k]*t.z;\n                    }\n                        \n                    for (int k=0; k<nmeln; ++k)\n                    {\n                        N[3*(k+nseln)  ] = -Hm[k]*t.x;\n                        N[3*(k+nseln)+1] = -Hm[k]*t.y;\n                        N[3*(k+nseln)+2] = -Hm[k]*t.z;\n                    }\n                        \n                    for (int k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n                        \n                    // calculate contact forces\n                    for (int k=0; k<nseln; ++k)\n                        ss.m_Ft += vec3d(fe[3*k], fe[3*k+1], fe[3*k+2]);\n                        \n                    for (int k = 0; k<nmeln; ++k)\n                        ms.m_Ft += vec3d(fe[3*(k+nseln)], fe[3*(k+nseln)+1], fe[3*(k+nseln)+2]);\n                        \n                    // assemble the global residual\n                    R.Assemble(en, LM, fe);\n                        \n                    // do the biphasic stuff\n                    if (sporo && mporo)\n                    {\n\t\t\t\t\t\t// get the pressure dofs for each element\n\t\t\t\t\t\tint nspdof = se.ShapeFunctions(degree_p);\n\t\t\t\t\t\tint nmpdof = me.ShapeFunctions(degree_p);\n\t\t\t\t\t\tint npdof = nspdof + nmpdof;\n\n\t\t\t\t\t\t// evaluate shape functions\n\t\t\t\t\t\tdouble* Hsp = se.H(degree_p, j);\n\t\t\t\t\t\tme.shape_fnc(degree_p, Hmp, pt.m_rs[0], pt.m_rs[1]);\n                            \n                        // calculate the flow rate\n                        double epsp = m_epsp*pt.m_epsp;\n                            \n                        double wn = pt.m_Lmp + epsp*pt.m_pg;\n                            \n                        // fill the LM\n                        LM.resize(npdof, -1);\n                        for (int k=0; k<nspdof; ++k) LM[k         ] = sLM[3*nseln+k];\n                        for (int k=0; k<nmpdof; ++k) LM[k + nspdof] = mLM[3*nmeln+k];\n                            \n                        // fill the force array\n                        fe.resize(npdof);\n                        zero(fe);\n                        for (int k=0; k<nspdof; ++k) N[k         ] =  Hsp[k];\n                        for (int k=0; k<nmpdof; ++k) N[k + nspdof] = -Hmp[k];\n                            \n                        for (int k=0; k<npdof; ++k) fe[k] += dt*wn*N[k]*detJ[j]*w[j];\n                            \n\t\t\t\t\t\t// build the en vector\n\t\t\t\t\t\ten.resize(npdof);\n\t\t\t\t\t\tfor (int k = 0; k<nspdof; ++k) en[k         ] = se.m_node[k];\n\t\t\t\t\t\tfor (int k = 0; k<nmpdof; ++k) en[k + nspdof] = me.m_node[k];\n\n                        // assemble residual\n                        R.Assemble(en, LM, fe);\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\t// do single- or two-pass\n\tint npass = (m_btwo_pass ? 2 : 1);\n\tfor (int np = 0; np < npass; ++np)\n\t{\n\t\t// get the primary and secondary surface\n\t\tFESlidingSurfaceBiphasicMixed& ss = (np == 0 ? m_ss : m_ms);\n\t\tFESlidingSurfaceBiphasicMixed& ms = (np == 0 ? m_ms : m_ss);\n\n\t\t// assemble stiffness matrix for this pass\n\t\tStiffnessMatrix(ss, ms, LS, tp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculate contact stiffness\nvoid FESlidingInterfaceBiphasicMixed::StiffnessMatrix(FESlidingSurfaceBiphasicMixed& ss, FESlidingSurfaceBiphasicMixed& ms, FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(ss.m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(ss.m_varP);\n\n    // see how many reformations we've had to do so far\n    int nref = GetSolver()->m_nref;\n    \n    const int MN = FEElement::MAX_NODES;\n    \n    double detJ[MN], w[MN], *Hs, Hm[MN], Hmp[MN];\n    double N[4*MN*2], H[4*MN*2];\n    vector<int> sLM, mLM, LM, en;\n    FEElementMatrix ke;\n    \n    FEModel& fem = *GetFEModel();\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    FEMesh& mesh = *ms.GetMesh();\n        \n    // loop over all primary surface elements\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        // get the next element\n        FESurfaceElement& se = ss.Element(i);\n            \n\t\t// primary element's poro status\n        bool sporo = ss.m_poro[i];\n            \n        // get nr of nodes, integration points, pressure dofs\n        int nseln = se.Nodes();\n        int nint = se.GaussPoints();\n\t\tint nspdof = se.ShapeFunctions(degree_p);\n\n        // nodal pressures of primary element\n        double pn[MN] = {0};\n        if (sporo) {\n            for (int j=0; j<nspdof; ++j) pn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n        }\n            \n        // copy the LM vector\n        ss.UnpackLM(se, sLM);\n\n        // we calculate all the metrics we need before we\n        // calculate the nodal forces\n        for (int j=0; j<nint; ++j)\n        {\n            // get the base vectors\n            vec3d g[2];\n            ss.CoBaseVectors(se, j, g);\n                \n            // jacobians: J = |g0xg1|\n            detJ[j] = (g[0] ^ g[1]).norm();\n                \n            // integration weights\n            w[j] = se.GaussWeights()[j];\n        }\n            \n        // loop over all integration points\n        for (int j=0; j<nint; ++j)\n        {\n            // get integration point data\n            FEBiphasicContactPoint& pt = static_cast<FEBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n            // calculate contact pressure and account for stick\n            double pn;\n            vec3d t = ContactTraction(ss, i, j, ms, pn);\n                \n            // get the secondary element\n            FESurfaceElement* pme = pt.m_pme;\n                \n            if (pme)\n            {\n\t\t\t\t// get secondary element\n                FESurfaceElement& me = *pme;\n                    \n\t\t\t\t// get secondary element's poro status\n                bool mporo = ms.m_poro[pme->m_lid];\n                    \n                // get the nr of secondary nodes\n                int nmeln = me.Nodes();\n\n\t\t\t\t// get secondary pressure dofs\n\t\t\t\tint nmpdof = me.ShapeFunctions(degree_p);\n                    \n                // nodal pressures of secondary nodes\n                double pm[MN] = {0};\n                for (int k=0; k<nmpdof; ++k) pm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n                    \n                // copy the LM vector\n                ms.UnpackLM(me, mLM);\n                    \n                // calculate degrees of freedom\n                int ndpn;\t// number of dofs per node\n                int ndof;\t// number of dofs in stiffness matrix\n                    \n                if (sporo && mporo) {\n                    // calculate degrees of freedom for biphasic-on-biphasic contact\n                    ndpn = 4;\n                    ndof = ndpn*(nseln+nmeln);\n                        \n                    // build the LM vector\n                    LM.resize(ndof);\n                        \n                    for (int k=0; k<nseln; ++k)\n                    {\n                        LM[4*k  ] = sLM[3*k  ];\t\t\t// x-dof\n                        LM[4*k+1] = sLM[3*k+1];\t\t\t// y-dof\n                        LM[4*k+2] = sLM[3*k+2];\t\t\t// z-dof\n                        LM[4*k+3] = sLM[3*nseln+k];\t\t// p-dof\n                    }\n                    for (int k=0; k<nmeln; ++k)\n                    {\n                        LM[4*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n                        LM[4*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n                        LM[4*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n                        LM[4*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n                    }\n                }\n                else {\n                    // calculate degrees of freedom for biphasic-on-elastic or elastic-on-elastic contact\n                    ndpn = 3;\n                    ndof = ndpn*(nseln + nmeln);\n                        \n                    // build the LM vector\n                    LM.resize(ndof);\n                        \n                    for (int k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[3*k  ];\n                        LM[3*k+1] = sLM[3*k+1];\n                        LM[3*k+2] = sLM[3*k+2];\n                    }\n                        \n                    for (int k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[3*k  ];\n                        LM[3*(k+nseln)+1] = mLM[3*k+1];\n                        LM[3*(k+nseln)+2] = mLM[3*k+2];\n                    }\n                }\n                    \n                // build the en vector\n                en.resize(nseln+nmeln);\n                for (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                for (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                // primary shape functions\n                Hs = se.H(j);\n\n\t\t\t\t// get primary pressure shape functions\n\t\t\t\tdouble* Hsp = se.H(degree_p, j);\n\n                // secondary shape functions\n                double r = pt.m_rs[0];\n                double s = pt.m_rs[1];\n                me.shape_fnc(Hm, r, s);\n\n\t\t\t\t// get secondary pressure shape functions\n\t\t\t\tme.shape_fnc(degree_p, Hmp, r, s);\n                    \n                // get primary normal vector\n                vec3d nu = pt.m_nu;\n                    \n                // gap function\n                double g = pt.m_gap;\n                    \n                // penalty\n                double eps = m_epsn*pt.m_epsn*psf;\n\n                // only evaluate stiffness matrix if contact traction is non-zero\n                if (pn > 0)\n                {\n                    // if stick\n                    if (pt.m_bstick)\n                    {\n                        double dtn = eps;\n                            \n                        // create the stiffness matrix\n                        ke.resize(ndof, ndof); ke.zero();\n                            \n                        // evaluate basis vectors on primary surface\n                        vec3d gscov[2];\n                        ss.CoBaseVectors(se, j, gscov);\n                            \n                        // identity tensor\n                        mat3d I = mat3dd(1);\n                            \n                        // evaluate Mc and Ac and combine them into As\n                        double* Gsr = se.Gr(j);\n                        double* Gss = se.Gs(j);\n                        mat3d Ac[MN], As[MN];\n                        mat3d gscovh[2];\n                        gscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n                        for (int k=0; k<nseln; ++k) {\n                            Ac[k] = (gscovh[1]*Gsr[k] - gscovh[0]*Gss[k])/detJ[j];\n                            As[k] = t & (Ac[k]*nu);\n                        }\n                            \n                        // --- S O L I D - S O L I D   C O N T A C T ---\n                            \n                        // a. I-term\n                        //------------------------------------\n                            \n                        for (int k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                        for (int k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                        double tmp = dtn*detJ[j]*w[j];\n                        for (int l=0; l<nseln+nmeln; ++l)\n                        {\n                            for (int k=0; k<nseln+nmeln; ++k)\n                            {\n                                ke[k*ndpn  ][l*ndpn  ] -= -tmp*N[k]*N[l]*I[0][0];\n                                ke[k*ndpn  ][l*ndpn+1] -= -tmp*N[k]*N[l]*I[0][1];\n                                ke[k*ndpn  ][l*ndpn+2] -= -tmp*N[k]*N[l]*I[0][2];\n                                    \n                                ke[k*ndpn+1][l*ndpn  ] -= -tmp*N[k]*N[l]*I[1][0];\n                                ke[k*ndpn+1][l*ndpn+1] -= -tmp*N[k]*N[l]*I[1][1];\n                                ke[k*ndpn+1][l*ndpn+2] -= -tmp*N[k]*N[l]*I[1][2];\n                                    \n                                ke[k*ndpn+2][l*ndpn  ] -= -tmp*N[k]*N[l]*I[2][0];\n                                ke[k*ndpn+2][l*ndpn+1] -= -tmp*N[k]*N[l]*I[2][1];\n                                ke[k*ndpn+2][l*ndpn+2] -= -tmp*N[k]*N[l]*I[2][2];\n                            }\n                        }\n                            \n                        // b. A-term\n                        //-------------------------------------\n                            \n                        tmp = detJ[j]*w[j];\n                        // non-symmetric\n                        for (int l=0; l<nseln; ++l)\n                        {\n                            for (int k=0; k<nseln+nmeln; ++k)\n                            {\n                                ke[k*ndpn  ][l*ndpn  ] -= tmp*N[k]*As[l][0][0];\n                                ke[k*ndpn  ][l*ndpn+1] -= tmp*N[k]*As[l][0][1];\n                                ke[k*ndpn  ][l*ndpn+2] -= tmp*N[k]*As[l][0][2];\n                                    \n                                ke[k*ndpn+1][l*ndpn  ] -= tmp*N[k]*As[l][1][0];\n                                ke[k*ndpn+1][l*ndpn+1] -= tmp*N[k]*As[l][1][1];\n                                ke[k*ndpn+1][l*ndpn+2] -= tmp*N[k]*As[l][1][2];\n                                    \n                                ke[k*ndpn+2][l*ndpn  ] -= tmp*N[k]*As[l][2][0];\n                                ke[k*ndpn+2][l*ndpn+1] -= tmp*N[k]*As[l][2][1];\n                                ke[k*ndpn+2][l*ndpn+2] -= tmp*N[k]*As[l][2][2];\n                            }\n                        }\n                            \n                        // --- B I P H A S I C   S T I F F N E S S ---\n                        if (sporo && mporo)\n                        {\n                            // need to multiply biphasic stiffness entries by the timestep\n                            double dt = fem.GetTime().timeIncrement;\n                                \n                            double tmp = dt*w[j]*detJ[j];\n                                \n                            double epsp = m_epsp*pt.m_epsp*psf;\n                                \n                            double wn = pt.m_Lmp + epsp*pt.m_pg;\n                                \n                            // --- S O L I D - P R E S S U R E   C O N T A C T ---\n                                \n                            // b. A-term\n                            //-------------------------------------\n                                \n                            for (int l=0; l<nseln; ++l) {\n                                vec3d Acn = Ac[l]*nu;\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[4*k + 3][4*l  ] -= tmp*wn*N[k]*Acn.x;\n                                    ke[4*k + 3][4*l+1] -= tmp*wn*N[k]*Acn.y;\n                                    ke[4*k + 3][4*l+2] -= tmp*wn*N[k]*Acn.z;\n                                }\n                            }\n                                \n                            // --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n                                \n                            // calculate the N-vector\n                            for (int k=0; k<nseln; ++k)\n                            {\n                                N[ndpn*k  ] = 0;\n                                N[ndpn*k+1] = 0;\n                                N[ndpn*k+2] = 0;\n                                N[ndpn*k+3] = Hs[k];\n                            }\n                                \n                            for (int k=0; k<nmeln; ++k)\n                            {\n                                N[ndpn*(k+nseln)  ] = 0;\n                                N[ndpn*(k+nseln)+1] = 0;\n                                N[ndpn*(k+nseln)+2] = 0;\n                                N[ndpn*(k+nseln)+3] = -Hm[k];\n                            }\n                                \n                            for (int k=0; k<ndof; ++k)\n                                for (int l=0; l<ndof; ++l) ke[k][l] -= tmp*epsp*N[k]*N[l];\n                                \n                        }\n                        // assemble the global stiffness\n\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\tLS.Assemble(ke);\n                    }\n                    // if slip\n                    else\n                    {\n                        // create the stiffness matrix\n                        ke.resize(ndof, ndof); ke.zero();\n                            \n                        double tn = -pn;\n                            \n                        // obtain the slip direction s1 and inverse of spatial increment dh\n                        double dh = 0, hd = 0;\n                        vec3d dr(0,0,0);\n                        vec3d s1 = SlipTangent(ss, i, j, ms, dh, dr);\n                            \n                        if (dh != 0)\n                        {\n                            hd = 1.0 / dh;\n                        }\n                            \n                        // evaluate basis vectors on both surfaces\n                        vec3d gscov[2], gmcov[2];\n                        ss.CoBaseVectors(se, j, gscov);\n                        ms.CoBaseVectors(me, r, s, gmcov);\n                        mat2d A;\n                        A[0][0] = gscov[0]*gmcov[0]; A[0][1] = gscov[0]*gmcov[1];\n                        A[1][0] = gscov[1]*gmcov[0]; A[1][1] = gscov[1]*gmcov[1];\n                        mat2d a = A.inverse();\n                            \n                        // evaluate covariant basis vectors on primary surface at previous time step\n                        vec3d gscovp[2];\n                        ss.CoBaseVectorsP(se, j, gscovp);\n                            \n                        // calculate delta gscov\n                        vec3d dgscov[2];\n                        dgscov[0] = gscov[0] - gscovp[0];\n                        dgscov[1] = gscov[1] - gscovp[1];\n                            \n                        // evaluate approximate contravariant basis vectors when gap != 0\n                        vec3d gscnt[2], gmcnt[2];\n                        gmcnt[0] = gscov[0]*a[0][0] + gscov[1]*a[0][1];\n                        gmcnt[1] = gscov[0]*a[1][0] + gscov[1]*a[1][1];\n                        gscnt[0] = gmcov[0]*a[0][0] + gmcov[1]*a[1][0];\n                        gscnt[1] = gmcov[0]*a[0][1] + gmcov[1]*a[1][1];\n                            \n                        // evaluate N and S tensors and approximations when gap != 0\n                        mat3ds N1 = dyad(nu);\n                        mat3d Nh1 = mat3dd(1) - (nu & nu);\n                        mat3d Nb1 = mat3dd(1) - (gscov[0] & gscnt[0]) - (gscov[1] & gscnt[1]);\n                        mat3d Nt1 = nu & (Nb1*nu);\n                        mat3d S1 = s1 & nu;\n                        mat3d Sh1 = (mat3dd(1) - (s1 & s1))*hd;\n                        mat3d Sb1 = s1 & (Nb1*nu);\n                            \n                        // evaluate m, c, Mg, and R\n                        // evaluate L1 from Mg and R\n                        // NOTE: Mg has the 1/detJ included in its definition\n                        vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n                        vec3d c = Sh1*Nh1*m*(1/detJ[j]);\n                        mat3d Mg = (mat3dd(1)*(nu * m) + (nu & m))*(1/detJ[j]);\n                        mat3d B = (c & (Nb1*nu)) - Sh1*Nh1;\n                        mat3d R = mat3dd(1)*(nu * dr) + (nu & dr);\n                        mat3d L1 = Sh1*((Nh1*Mg - mat3dd(1))*(-g) + R)*Nh1;\n                            \n                        // evaluate Mc and Ac and combine them into As\n                        // evaluate Fc from Ac_bar (Ab)\n                        // evaluate Jc as L1*Ac-Fc\n                        double* Gsr = se.Gr(j);\n                        double* Gss = se.Gs(j);\n                        mat3d Ac[MN], As[MN], Pc[MN], Jc[MN];\n                        mat3d gscovh[2];\n                        mat3d dgscovh[2];\n                        gscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n                        dgscovh[0].skew(dgscov[0]); dgscovh[1].skew(dgscov[1]);\n                        for (int k=0; k<nseln; ++k) {\n                            vec3d mc = gscnt[0]*Gsr[k] + gscnt[1]*Gss[k];\n                            mat3d Mc = nu & mc;\n                            Ac[k]    = (gscovh[1]*Gsr[k] - gscovh[0]*Gss[k])/detJ[j];\n                            mat3d Ab = (dgscovh[1]*Gsr[k] - dgscovh[0]*Gss[k])/detJ[j];\n                            vec3d hcp = (N1*mc + Ac[k]*nu)*pt.m_mueff*(-g);\n                            vec3d hcm = (N1*mc*m_mu - Ac[k]*nu*pt.m_mueff);\n                            As[k] = (Ac[k] + Mc*N1);\n                            mat3d Jc = (L1*Ac[k]) - Sh1*Nh1*Ab*(-g);\n                            Pc[k] = (s1 & hcm) + (c & hcp) - Jc*pt.m_mueff;\n                        }\n                            \n                        // evaluate mb and Mb\n                        // evaluate s1 dyad mb and combine as Pb\n                        double Gmr[MN], Gms[MN];\n                        me.shape_deriv(Gmr, Gms, r, s);\n                        vec3d mb[MN];\n                        mat3d Pb[MN];\n                        for (int k=0; k<nmeln; ++k) {\n                            mb[k] = gmcnt[0]*Gmr[k] + gmcnt[1]*Gms[k];\n                            Pb[k] = ((-nu) & mb[k]) - (s1 & mb[k])*pt.m_mueff;\n                        }\n                            \n                        // evaluate Gbc\n                        matrix Gbc(nmeln,nseln);\n                        for (int b=0; b<nmeln; ++b) {\n                            for (int c=0; c<nseln; ++c) {\n                                Gbc(b,c)\n                                = (a[0][0]*Gmr[b]*Gsr[c]\n                                    + a[0][1]*Gmr[b]*Gss[c]\n                                    + a[1][0]*Gms[b]*Gsr[c]\n                                    + a[1][1]*Gms[b]*Gss[c])*(-g);\n                            }\n                        }\n                            \n                        // define T, Ttb\n                        mat3d T = N1 + S1*pt.m_mueff;\n                        mat3d Ttb = Nt1 + Sb1*m_mu;\n\n                        // --- S O L I D - S O L I D   C O N T A C T ---\n                            \n                        // a. NxN-term\n                        //------------------------------------\n                            \n                        for (int k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                        for (int k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                        double tmp = detJ[j]*w[j];\n                        for (int l=0; l<nseln+nmeln; ++l)\n                        {\n                            for (int k=0; k<nseln+nmeln; ++k)\n                            {\n                                ke[k*ndpn  ][l*ndpn  ] -= -tmp*N[k]*N[l]*(eps*Ttb[0][0] + pt.m_mueff*tn*B[0][0]);\n                                ke[k*ndpn  ][l*ndpn+1] -= -tmp*N[k]*N[l]*(eps*Ttb[0][1] + pt.m_mueff*tn*B[0][1]);\n                                ke[k*ndpn  ][l*ndpn+2] -= -tmp*N[k]*N[l]*(eps*Ttb[0][2] + pt.m_mueff*tn*B[0][2]);\n                                    \n                                ke[k*ndpn+1][l*ndpn  ] -= -tmp*N[k]*N[l]*(eps*Ttb[1][0] + pt.m_mueff*tn*B[1][0]);\n                                ke[k*ndpn+1][l*ndpn+1] -= -tmp*N[k]*N[l]*(eps*Ttb[1][1] + pt.m_mueff*tn*B[1][1]);\n                                ke[k*ndpn+1][l*ndpn+2] -= -tmp*N[k]*N[l]*(eps*Ttb[1][2] + pt.m_mueff*tn*B[1][2]);\n                                    \n                                ke[k*ndpn+2][l*ndpn  ] -= -tmp*N[k]*N[l]*(eps*Ttb[2][0] + pt.m_mueff*tn*B[2][0]);\n                                ke[k*ndpn+2][l*ndpn+1] -= -tmp*N[k]*N[l]*(eps*Ttb[2][1] + pt.m_mueff*tn*B[2][1]);\n                                ke[k*ndpn+2][l*ndpn+2] -= -tmp*N[k]*N[l]*(eps*Ttb[2][2] + pt.m_mueff*tn*B[2][2]);\n                            }\n                        }\n                            \n                        // b. Na,Nb-term\n                        //-------------------------------------\n                            \n                        tmp = detJ[j]*w[j];\n                        // non-symmetric\n                        for (int l=0; l<nseln; ++l)\n                        {\n                            for (int k=0; k<nseln+nmeln; ++k)\n                            {\n                                ke[k*ndpn  ][l*ndpn  ] -= -tmp*N[k]*(tn*(As[l][0][0] + Pc[l][0][0]));\n                                ke[k*ndpn  ][l*ndpn+1] -= -tmp*N[k]*(tn*(As[l][0][1] + Pc[l][0][1]));\n                                ke[k*ndpn  ][l*ndpn+2] -= -tmp*N[k]*(tn*(As[l][0][2] + Pc[l][0][2]));\n                                    \n                                ke[k*ndpn+1][l*ndpn  ] -= -tmp*N[k]*(tn*(As[l][1][0] + Pc[l][1][0]));\n                                ke[k*ndpn+1][l*ndpn+1] -= -tmp*N[k]*(tn*(As[l][1][1] + Pc[l][1][1]));\n                                ke[k*ndpn+1][l*ndpn+2] -= -tmp*N[k]*(tn*(As[l][1][2] + Pc[l][1][2]));\n                                    \n                                ke[k*ndpn+2][l*ndpn  ] -= -tmp*N[k]*(tn*(As[l][2][0] + Pc[l][2][0]));\n                                ke[k*ndpn+2][l*ndpn+1] -= -tmp*N[k]*(tn*(As[l][2][1] + Pc[l][2][1]));\n                                ke[k*ndpn+2][l*ndpn+2] -= -tmp*N[k]*(tn*(As[l][2][2] + Pc[l][2][2]));\n                            }\n                        }\n                            \n                        // c. Nc,Nd-term\n                        //---------------------------------------\n                            \n                        tmp = detJ[j]*w[j];\n                        // non-symmetric\n                        for (int k=0; k<nmeln; ++k)\n                        {\n                            for (int l=0; l<nseln+nmeln; ++l)\n                            {\n                                ke[(k+nseln)*ndpn  ][l*ndpn  ] -= tmp*N[l]*tn*Pb[k][0][0];\n                                ke[(k+nseln)*ndpn  ][l*ndpn+1] -= tmp*N[l]*tn*Pb[k][0][1];\n                                ke[(k+nseln)*ndpn  ][l*ndpn+2] -= tmp*N[l]*tn*Pb[k][0][2];\n                                    \n                                ke[(k+nseln)*ndpn+1][l*ndpn  ] -= tmp*N[l]*tn*Pb[k][1][0];\n                                ke[(k+nseln)*ndpn+1][l*ndpn+1] -= tmp*N[l]*tn*Pb[k][1][1];\n                                ke[(k+nseln)*ndpn+1][l*ndpn+2] -= tmp*N[l]*tn*Pb[k][1][2];\n                                    \n                                ke[(k+nseln)*ndpn+2][l*ndpn  ] -= tmp*N[l]*tn*Pb[k][2][0];\n                                ke[(k+nseln)*ndpn+2][l*ndpn+1] -= tmp*N[l]*tn*Pb[k][2][1];\n                                ke[(k+nseln)*ndpn+2][l*ndpn+2] -= tmp*N[l]*tn*Pb[k][2][2];\n                            }\n                        }\n\n                        // c. Gbc-term\n                        //---------------------------------------\n                            \n                        tmp = tn*detJ[j]*w[j];\n                        for (int k=0; k<nmeln; ++k)\n                        {\n                            for (int l=0; l<nseln; ++l)\n                            {\n                                mat3d gT = T*(Gbc[k][l]*tmp);\n                                ke[(k+nseln)*ndpn  ][l*ndpn  ] -= gT[0][0];\n                                ke[(k+nseln)*ndpn  ][l*ndpn+1] -= gT[0][1];\n                                ke[(k+nseln)*ndpn  ][l*ndpn+2] -= gT[0][2];\n                                    \n                                ke[(k+nseln)*ndpn+1][l*ndpn  ] -= gT[1][0];\n                                ke[(k+nseln)*ndpn+1][l*ndpn+1] -= gT[1][1];\n                                ke[(k+nseln)*ndpn+1][l*ndpn+2] -= gT[1][2];\n                                    \n                                ke[(k+nseln)*ndpn+2][l*ndpn  ] -= gT[2][0];\n                                ke[(k+nseln)*ndpn+2][l*ndpn+1] -= gT[2][1];\n                                ke[(k+nseln)*ndpn+2][l*ndpn+2] -= gT[2][2];\n                            }\n                        }\n\n                        // --- B I P H A S I C   S T I F F N E S S ---\n                        if (sporo && mporo)\n                        {\n                            // need to multiply biphasic stiffness entries by the timestep\n                            double dt = fem.GetTime().timeIncrement;\n                                \n                            double dpr = 0, dps = 0;\n                            dpr = me.eval_deriv1(degree_p, pm, r, s);\n                            dps = me.eval_deriv2(degree_p, pm, r, s);\n                                \n                            vec3d q2 = gmcnt[0]*dpr + gmcnt[1]*dps;\n                                \n                            // evaluate gc\n                            vector<double> gc(nseln);\n                            for (int k=0; k<nseln; ++k) {\n                                gc[k]\n                                = (a[0][0]*dpr*Gsr[k]\n                                    + a[0][1]*dpr*Gss[k]\n                                    + a[1][0]*dps*Gsr[k]\n                                    + a[1][1]*dps*Gss[k])*(-g);\n                            }\n                                \n                            tmp = dt*w[j]*detJ[j];\n                                \n                            double epsp = m_epsp*pt.m_epsp*psf;\n                                \n\t\t\t\t\t\t\t// pressure shape functions\n\t\t\t\t\t\t\tdouble* Hsp = se.H(degree_p, j);\n\t\t\t\t\t\t\tme.shape_fnc(degree_p, Hmp, r, s);\n\t\t\t\t\t\t\tfor (int i = 0; i < nseln + nmeln; ++i) H[i] = 0;\n\t\t\t\t\t\t\tfor (int i = 0; i < nspdof; ++i) H[i        ] = Hsp[i];\n\t\t\t\t\t\t\tfor (int i = 0; i < nmpdof; ++i) H[i + nseln] = Hmp[i];\n                                \n                            // --- S O L I D - P R E S S U R E   C O N T A C T ---\n                                \n                            // a. q-term\n                            //-------------------------------------\n                            for (int k=0; k<nseln+nmeln; ++k)\n                                for (int l=0; l<nseln+nmeln; ++l)\n                                {\n                                    ke[4*k + 3][4*l  ] += tmp*epsp*H[k]*N[l]*q2.x;\n                                    ke[4*k + 3][4*l+1] += tmp*epsp*H[k]*N[l]*q2.y;\n                                    ke[4*k + 3][4*l+2] += tmp*epsp*H[k]*N[l]*q2.z;\n                                }\n                                \n                            double wn = pt.m_Lmp + epsp*pt.m_pg;\n                                \n                            // b. A-term\n                            //-------------------------------------\n                                \n                            for (int l=0; l<nseln; ++l) {\n                                vec3d Acn = Ac[l]*nu;\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[4*k + 3][4*l  ] -= tmp*wn*H[k]*Acn.x;\n                                    ke[4*k + 3][4*l+1] -= tmp*wn*H[k]*Acn.y;\n                                    ke[4*k + 3][4*l+2] -= tmp*wn*H[k]*Acn.z;\n                                }\n                            }\n\n                            // c. s-term (Frictional term)\n                            //-------------------------------------\n                                \n\t\t\t\t\t\t\tvec3d q = s1*m_mu*(1.0 - m_phi);\n\n                            for (int l=0; l<nseln; ++l) {\n                                for (int k=0; k<nseln+nmeln; ++k)\n                                {\n                                    ke[4*k    ][4*l+3] -= tmp*H[k]*N[l]*q.x;\n                                    ke[4*k + 1][4*l+3] -= tmp*H[k]*N[l]*q.y;\n                                    ke[4*k + 2][4*l+3] -= tmp*H[k]*N[l]*q.z;\n                                }\n                            }\n\n                            // d. m-term\n                            //---------------------------------------\n                                \n\t\t\t\t\t\t\tvec3d mbp[MN];\n\t\t\t\t\t\t\tdouble Gpr[MN], Gps[MN];\n\t\t\t\t\t\t\tme.shape_deriv(degree_p, Gpr, Gps, r, s);\n\t\t\t\t\t\t\tfor (int k = 0; k < nmeln; ++k) mbp[k] = vec3d(0, 0, 0);\n\t\t\t\t\t\t\tfor (int k = 0; k<nmpdof; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmbp[k] = gmcnt[0] * Gpr[k] + gmcnt[1] * Gps[k];\n\t\t\t\t\t\t\t}\n\n                            for (int k=0; k<nmpdof; ++k) {\n                                for (int l=0; l<nseln+nmeln; ++l)\n                                {\n                                    ke[4*(k+nseln) + 3][4*l  ] += tmp*wn*N[l]*mbp[k].x;\n                                    ke[4*(k+nseln) + 3][4*l+1] += tmp*wn*N[l]*mbp[k].y;\n                                    ke[4*(k+nseln) + 3][4*l+2] += tmp*wn*N[l]*mbp[k].z;\n                                }\n                            }\n\n                            // e. gc-term\n                            //-------------------------------------\n                            for (int k=0; k<nseln+nmeln; ++k)\n                                for (int l=0; l<nseln; ++l)\n                                {\n                                    ke[4*k + 3][4*l  ] -= tmp*epsp*H[k]*gc[l]*nu.x;\n                                    ke[4*k + 3][4*l+1] -= tmp*epsp*H[k]*gc[l]*nu.y;\n                                    ke[4*k + 3][4*l+2] -= tmp*epsp*H[k]*gc[l]*nu.z;\n                                }\n                                \n                            // f. Gbc-term (CONVERT!)\n                            //---------------------------------------\n/*                                \n                            for (int k=0; k<nmeln; ++k) {\n                                for (int l=0; l<nseln; ++l)\n                                {\n                                    ke[4*(k+nseln) + 3][4*l  ] -= tmp*wn*Gbc[k][l]*nu.x;\n                                    ke[4*(k+nseln) + 3][4*l+1] -= tmp*wn*Gbc[k][l]*nu.y;\n                                    ke[4*(k+nseln) + 3][4*l+2] -= tmp*wn*Gbc[k][l]*nu.z;\n                                }\n                            }\n*/\n                            // --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n                                \n                            // calculate the N-vector\n\t\t\t\t\t\t\tfor (int k = 0; k < ndof; ++k) N[k] = 0.0;\n                            for (int k=0; k<nspdof; ++k)\n                            {\n                                N[ndpn*k+3] = Hsp[k];\n                            }\n                                \n                            for (int k=0; k<nmpdof; ++k)\n                            {\n                                N[ndpn*(k+nseln)+3] = -Hmp[k];\n                            }\n                                \n                            for (int k=0; k<ndof; ++k)\n                                for (int l=0; l<ndof; ++l) ke[k][l] -= tmp*epsp*N[k]*N[l];\n                        }\n                            \n                        // assemble the global stiffness\n\t\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\t\tLS.Assemble(ke);\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::UpdateContactPressures()\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint degree_d = dofs.GetVariableInterpolationOrder(m_ss.m_varU);\n\tint degree_p = dofs.GetVariableInterpolationOrder(m_ss.m_varP);\n\n    int npass = (m_btwo_pass?2:1);\n    const int MN = FEElement::MAX_NODES;\n    const int MI = FEElement::MAX_INTPOINTS;\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    for (int np=0; np<npass; ++np)\n    {\n\t\tFESlidingSurfaceBiphasicMixed& ss = (np == 0? m_ss : m_ms);\n\t\tFESlidingSurfaceBiphasicMixed& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all elements of the primary surface\n        for (int n=0; n<ss.Elements(); ++n)\n        {\n            FESurfaceElement& el = ss.Element(n);\n            int nint = el.GaussPoints();\n            \n            // get the normal tractions at the integration points\n            for (int i=0; i<nint; ++i)\n            {\n                // get integration point data\n                FEBiphasicContactPoint& sd = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\t\t\t\t// evaluate traction on primary surface\n                double eps = m_epsn*sd.m_epsn*psf;\n                if (sd.m_bstick) {\n                    // if stick, evaluate total traction\n                    sd.m_tr = sd.m_Lmt + sd.m_dg*eps;\n                    // then derive normal component\n                    sd.m_Ln = -sd.m_tr*sd.m_nu;\n                }\n                else {\n                    // if slip, evaluate normal traction\n                    double Ln = sd.m_Lmd + eps*sd.m_gap;\n                    sd.m_Ln = MBRACKET(Ln);\n                    // then derive total traction\n                    sd.m_tr = -(sd.m_nu*sd.m_Ln + sd.m_s1*sd.m_Ln*sd.m_mueff);\n                    \n                }\n                \n                FESurfaceElement* pme = sd.m_pme;\n                \n                if (m_btwo_pass && pme)\n                {\n                    // get secondary element data\n                    int mint = pme->GaussPoints();\n                    double pi[MI];\n                    vec3d ti[MI];\n                    for (int j=0; j<mint; ++j)\n                    {\n                        FEBiphasicContactPoint& md = static_cast<FEBiphasicContactPoint&>(*pme->GetMaterialPoint(j));\n\n                        // evaluate traction on secondary surface\n                        double eps = m_epsn*md.m_epsn*psf;\n                        if (md.m_bstick) {\n                            // if stick, evaluate total traction\n                            ti[j] = md.m_Lmt + md.m_dg*eps;\n                            // then derive normal component\n                            pi[j] = -ti[j]*md.m_nu;\n                        }\n                        else {\n                            // if slip, evaluate normal traction\n                            double Ln = md.m_Lmd + eps*md.m_gap;\n                            pi[j] = MBRACKET(Ln);\n                            // then derive total traction\n                            ti[j] = -(md.m_nu*pi[j] + md.m_s1*md.m_mueff*pi[j]);\n                        }\n                    }\n                    // project the data to the nodes\n                    double pn[MN];\n                    vec3d tn[MN];\n                    pme->FEElement::project_to_nodes(pi, pn);\n                    pme->project_to_nodes(ti, tn);\n                    // now evaluate the traction at the intersection point\n                    double Ln = pme->eval(degree_p, pn, sd.m_rs[0], sd.m_rs[1]);\n                    vec3d trac = pme->eval(tn, sd.m_rs[0], sd.m_rs[1]);\n                    sd.m_Ln += MBRACKET(Ln);\n                    // tractions on primary-secondary are opposite, so subtract\n                    sd.m_tr -= trac;\n                }\n            }\n        }\n        ss.EvaluateNodalContactPressures();\n        ss.EvaluateNodalContactTractions();\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterfaceBiphasicMixed::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    int i;\n    double Ln, Lp;\n    bool bconv = true;\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    bool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n    int NS = m_ss.Elements();\n    int NM = m_ms.Elements();\n    \n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0, normP = 0, normDP = 0;\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& ds = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tif (ds.m_bstick)\n                normL0 += ds.m_Lmt*ds.m_Lmt;\n            else\n                normL0 += ds.m_Lmd*ds.m_Lmd;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& dm = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tif (dm.m_bstick)\n                normL0 += dm.m_Lmt*dm.m_Lmt;\n            else\n                normL0 += dm.m_Lmd*dm.m_Lmd;\n        }\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0;\n    double maxpg = 0;\n    \n    // update Lagrange multipliers\n    double normL1 = 0, epsp;\n    for (i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& ds = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on primary surface\n            double eps = m_epsn*ds.m_epsn*psf;\n            if (ds.m_bstick) {\n                // if stick, augment total traction\n                if (m_bsmaug) {\n                    ds.m_Lmt = tn[j];\n                    if (m_btwo_pass) ds.m_Lmt /= 2;\n                }\n                else {\n                    ds.m_Lmt += ds.m_dg*eps;\n                }\n                // then derive normal component\n                ds.m_Lmd = -ds.m_Lmt*ds.m_nu;\n                Ln = ds.m_Lmd;\n                normL1 += ds.m_Lmt*ds.m_Lmt;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(ds.m_dg.norm()));\n            }\n            else {\n                // if slip, augment normal traction\n                if (m_bsmaug) {\n                    Ln = -(tn[j]*ds.m_nu);\n                    ds.m_Lmd = MBRACKET(Ln);\n                    if (m_btwo_pass) ds.m_Lmd /= 2;\n                }\n                else {\n                    Ln = ds.m_Lmd + eps*ds.m_gap;\n                    ds.m_Lmd = MBRACKET(Ln);\n                }\n                // then derive total traction\n                double mueff = m_mu*(1.0-(1.0-m_phi)*ds.m_p1/ds.m_Lmd);\n                if ( ds.m_Lmd < (1-m_phi)*ds.m_p1 )\n                {\n                    mueff = 0.0;\n                }\n                ds.m_Lmt = -(ds.m_nu*ds.m_Lmd + ds.m_s1*ds.m_Lmd*mueff);\n                normL1 += ds.m_Lmd*ds.m_Lmd;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(ds.m_gap));\n            }\n            if (m_ss.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*ds.m_epsp*psf;\n                    Lp = ds.m_Lmp + epsp*ds.m_pg;\n                    maxpg = max(maxpg,fabs(ds.m_pg));\n                    normDP += ds.m_pg*ds.m_pg;\n                }\n                ds.m_Lmp = Lp;\n            }\n        }\n    }\n    \n    for (i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEBiphasicContactPoint& dm = static_cast<FEBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on secondary surface\n            double eps = m_epsn*dm.m_epsn*psf;\n            if (dm.m_bstick) {\n                // if stick, augment total traction\n                if (m_bsmaug) {\n                    dm.m_Lmt = tn[j];\n                    if (m_btwo_pass) dm.m_Lmt /= 2;\n                }\n                else {\n                    dm.m_Lmt += dm.m_dg*eps;\n                }\n                // then derive normal component\n                dm.m_Lmd = -dm.m_Lmt*dm.m_nu;\n                Ln = dm.m_Lmd;\n                normL1 += dm.m_Lmt*dm.m_Lmt;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(dm.m_dg.norm()));\n            }\n            else {\n                // if slip, augment normal traction\n                if (m_bsmaug) {\n                    Ln = -(tn[j]*dm.m_nu);\n                    dm.m_Lmd = MBRACKET(Ln);\n                    if (m_btwo_pass) dm.m_Lmd /= 2;\n                }\n                else {\n                    Ln = dm.m_Lmd + eps*dm.m_gap;\n                    dm.m_Lmd = MBRACKET(Ln);\n                }\n                // then derive total traction\n                double mueff = m_mu*(1.0-(1.0-m_phi)*dm.m_p1/dm.m_Lmd);\n                if ( dm.m_Lmd < (1-m_phi)*dm.m_p1 )\n                {\n                    mueff = 0.0;\n                }\n                dm.m_Lmt = -(dm.m_nu*dm.m_Lmd + dm.m_s1*dm.m_Lmd*mueff);\n                normL1 += dm.m_Lmd*dm.m_Lmd;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(dm.m_gap));\n            }\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                if (Ln > 0) {\n                    epsp = m_epsp*dm.m_epsp*psf;\n                    Lp = dm.m_Lmp + epsp*dm.m_pg;\n                    maxpg = max(maxpg,fabs(dm.m_pg));\n                    normDP += dm.m_pg*dm.m_pg;\n                }\n                dm.m_Lmp = Lp;\n            }\n        }\n    }\n    \n    // normP should be a measure of the fluid pressure at the\n    // contact interface.  However, since it could be zero,\n    // use an average measure of the contact traction instead.\n    normP = normL1;\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0));\n    double pnorm = (normP != 0 ? (normDP/normP) : normDP);\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    if ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n    \n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    if ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" sliding interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n    if (bporo) { feLog(\"    P gap        : %15le\", pnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\"); }\n    \n    feLog(\"    maximum gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n    if (bporo) {\n        feLog(\"    maximum pgap : %15le\", maxpg);\n        if (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n    }\n    \n    ProjectSurface(m_ss, m_ms, true);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss, true);\n    \n    m_bfreeze = true;\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::Serialize(DumpStream &ar)\n{\n    // serialize contact data\n    FEContactInterface::Serialize(ar);\n    \n    // serialize contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n\n\t// serialize element pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceBiphasicMixed::MarkFreeDraining()\n{\n    // Mark all nodes as free-draining.  This needs to be done for ALL\n    // contact interfaces prior to executing Update(), where nodes that are\n    // in contact are subsequently marked as non free-draining.  This ensures\n    // that for surfaces involved in more than one contact interface, nodes\n    // that have been marked as non free-draining are not reset to\n    // free-draining.\n    for (int np=0; np<2; ++np)\n    {\n\t\tFESlidingSurfaceBiphasicMixed& s = (np == 0? m_ss : m_ms);\n        \n        if (s.m_bporo) {\n            // first, mark all nodes as free-draining (= neg. ID)\n            // this is done by setting the dof's equation number\n            // to a negative number\n            for (int i=0; i<s.Nodes(); ++i)\n            {\n\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\tint id = node.m_ID[m_dofP];\n                if (id >= 0)\n                {\n                    // mark node as free-draining\n                    node.m_ID[m_dofP] = -id-2;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceBiphasicMixed::SetFreeDraining()\n{\n    // Set the pressure to zero for the free-draining nodes\n    for (int np=0; np<2; ++np)\n    {\n        FESlidingSurfaceBiphasicMixed& s = (np == 0? m_ss : m_ms);\n        \n        if (s.m_bporo) {\n            // loop over all nodes\n            for (int i=0; i<s.Nodes(); ++i)\n            {\n\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\tif (node.m_ID[m_dofP] < -1)\n                {\n                    // set the fluid pressure to zero\n                    node.set(m_dofP, 0);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "FEBioMix/FESlidingInterfaceBiphasicMixed.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBiphasicContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingSurfaceBiphasicMixed : public FEBiphasicContactSurface\n{\npublic:\n    //! constructor\n\tFESlidingSurfaceBiphasicMixed(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    // data serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! initialize sliding surface and store previous values\n    void InitSlidingSurface();\n    \n    //! evaluate net contact force\n    vec3d GetContactForce() override;\n    \n    //! evaluate net contact area\n    double GetContactArea() override;\n    \n    //! evaluate net fluid force\n    vec3d GetFluidForce() override;\n    \n    //! calculate the nodal normals\n    void UpdateNodeNormals();\n    \n    void SetPoroMode(bool bporo) { m_bporo = bporo; }\n    \n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\t//! unpack dofs\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\npublic:\n    void GetVectorGap      (int nface, vec3d& pg) override;\n    void GetContactTraction(int nface, vec3d& pt) override;\n    void GetSlipTangent    (int nface, vec3d& pt);\n    void GetMuEffective    (int nface, double& pg) override;\n    void GetLocalFLS       (int nface, double& pg) override;\n    void GetNodalVectorGap      (int nface, vec3d* pg) override;\n    void GetNodalContactPressure(int nface, double* pg) override;\n    void GetNodalContactTraction(int nface, vec3d* pt) override;\n    void GetStickStatus(int nface, double& pg) override;\n    void EvaluateNodalContactPressures();\n    void EvaluateNodalContactTractions();\n\nprivate:\n\tvoid GetContactPressure(int nface, double& pg);\n    \npublic:\n    bool\tm_bporo;\t//!< set poro-mode\n\n\tint\tm_varU, m_varP;\n    \n    vector<bool>\t\tm_poro;\t//!< surface element poro status\n    vector<vec3d>\t\tm_nn;\t//!< node normals\n    vector<vec3d>       m_tn;   //!< nodal contact tractions\n    vector<double>      m_pn;   //!< nodal contact pressures\n    \n    vec3d    m_Ft;     //!< total contact force (from equivalent nodal forces)\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingInterfaceBiphasicMixed : public FEContactInterface\n{\npublic:\n    //! constructor\n\tFESlidingInterfaceBiphasicMixed(FEModel* pfem);\n    \n    //! destructor\n    ~FESlidingInterfaceBiphasicMixed();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! interface activation\n    void Activate() override;\n    \n    //! calculate the slip direction on the primary surface\n    vec3d SlipTangent(FESlidingSurfaceBiphasicMixed& ss, const int nel, const int nint, FESlidingSurfaceBiphasicMixed& ms, double& dh, vec3d& r);\n    \n    //! calculate contact traction\n    vec3d ContactTraction(FESlidingSurfaceBiphasicMixed& ss, const int nel, const int n, FESlidingSurfaceBiphasicMixed& ms, double& pn);\n    \n    //! calculate contact pressures for file output\n    void UpdateContactPressures();\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! mark free-draining condition\n    void MarkFreeDraining();\n    \n    //! set free-draining condition\n    void SetFreeDraining();\n    \n    //! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n    //! return integration rule class\n    bool UseNodalIntegration() override { return false; }\n    \n    //! build the matrix profile for use in the stiffness matrix\n    void BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n    void ProjectSurface(FESlidingSurfaceBiphasicMixed& ss, FESlidingSurfaceBiphasicMixed& ms, bool bupseg, bool bmove = false);\n    \n    //! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n    void CalcAutoPenalty(FESlidingSurfaceBiphasicMixed& s);\n    \n    void CalcAutoPressurePenalty(FESlidingSurfaceBiphasicMixed& s);\n    double AutoPressurePenalty(FESurfaceElement& el, FESlidingSurfaceBiphasicMixed& s);\n\n\t//! calculate contact forces\n\tvoid LoadVector(FESlidingSurfaceBiphasicMixed& ss, FESlidingSurfaceBiphasicMixed& ms, FEGlobalVector& R, const FETimeInfo& tp);\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FESlidingSurfaceBiphasicMixed& ss, FESlidingSurfaceBiphasicMixed& ms, FELinearSystem& LS, const FETimeInfo& tp);\n\npublic:\n\tFESlidingSurfaceBiphasicMixed\tm_ss;\t//!< primary surface\n\tFESlidingSurfaceBiphasicMixed\tm_ms;\t//!< secondary surface\n    \n    int\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n    bool\t\t\tm_btwo_pass;\t//!< two-pass flag\n    double\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n    double\t\t\tm_gtol;\t\t\t//!< gap tolerance\n    double\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n    double\t\t\tm_stol;\t\t\t//!< search tolerance\n    bool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n    double\t\t\tm_srad;\t\t\t//!< contact search radius\n    int\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n    int\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n    int\t\t\t\tm_nsegup;\t\t//!< segment update parameter\n    bool\t\t\tm_breloc;\t\t//!< node relocation on startup\n    bool            m_bsmaug;       //!< smooth augmentation\n    bool            m_bsmfls;       //!< smooth local fluid load support\n\n    double\t\t\tm_epsn;\t\t    //!< normal penalty factor\n    bool\t\t\tm_bautopen;\t    //!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n    \n    double          m_mu;           //!< friction coefficient\n    bool            m_bfreeze;      //!< freeze stick/slip status\n    bool            m_bflips;       //!< flip primary surface normal\n    bool            m_bflipm;       //!< flip secondary surface normal\n    bool            m_bshellbs;     //!< flag for prescribing pressure on shell bottom for primary surface\n    bool            m_bshellbm;     //!< flag for prescribing pressure on shell bottom for secondary surface\n\n    // biphasic contact parameters\n    double\t        m_epsp;\t\t    //!< flow rate penalty\n    double          m_phi;          //!< solid-solid contact fraction\n    \nprotected:\n    int\tm_dofP;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESlidingInterfaceMP.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESlidingInterfaceMP.h\"\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FETriphasic.h\"\n#include \"FEMultiphasic.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include \"FECore/FENormalProjection.h\"\n#include \"FECore/FEAnalysis.h\"\n#include <FECore/FELinearSystem.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEAmbientConcentration, FECoreClass)\n\tADD_PARAMETER(m_sol, \"sol\", FE_PARAM_ATTRIBUTE, \"$(solutes)\");\n\tADD_PARAMETER(m_ambc, \"ambient_concentration\")->MakeVolatile(false);\nEND_FECORE_CLASS();\n\nFEAmbientConcentration::FEAmbientConcentration(FEModel* fem) : FECoreClass(fem) \n{\n\tm_sol = -1;\n\tm_ambc = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FESlidingInterfaceMP, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"               )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"            );\n    ADD_PARAMETER(m_gtol     , \"gaptol\"               )->setUnits(UNIT_LENGTH);;\n    ADD_PARAMETER(m_ptol     , \"ptol\"                 );\n    ADD_PARAMETER(m_ctol     , \"ctol\"                 );\n    ADD_PARAMETER(m_epsn     , \"penalty\"              );\n    ADD_PARAMETER(m_bautopen , \"auto_penalty\"         );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"       );\n    ADD_PARAMETER(m_btwo_pass, \"two_pass\"             );\n    ADD_PARAMETER(m_knmult   , \"knmult\"               );\n    ADD_PARAMETER(m_stol     , \"search_tol\"           );\n    ADD_PARAMETER(m_epsp     , \"pressure_penalty\"     );\n    ADD_PARAMETER(m_epsc     , \"concentration_penalty\");\n    ADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\"  );\n    ADD_PARAMETER(m_srad     , \"search_radius\"        )->setUnits(UNIT_LENGTH);;\n    ADD_PARAMETER(m_nsegup   , \"seg_up\"               );\n    ADD_PARAMETER(m_breloc   , \"node_reloc\"           );\n    ADD_PARAMETER(m_mu       , \"fric_coeff\"           );\n    ADD_PARAMETER(m_phi      , \"contact_frac\"         );\n    ADD_PARAMETER(m_bsmaug   , \"smooth_aug\"           );\n    ADD_PARAMETER(m_bsmfls   , \"smooth_fls\"           );\n    ADD_PARAMETER(m_naugmin  , \"minaug\"               );\n    ADD_PARAMETER(m_naugmax  , \"maxaug\"               );\n    ADD_PARAMETER(m_ambp     , \"ambient_pressure\"     );\n\n\tADD_PROPERTY(m_ambctmp, \"ambient_concentration\",FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FEMultiphasicContactPoint::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactPoint::Serialize(ar);\n    ar & m_Lmc;\n    ar & m_epsc;\n    ar & m_cg;\n    ar & m_c1;\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingSurfaceMP\n//-----------------------------------------------------------------------------\n\nFESlidingSurfaceMP::FESlidingSurfaceMP(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{ \n\tm_bporo = m_bsolu = false;\n\tm_dofC = -1;\n}\n\n//-----------------------------------------------------------------------------\nFESlidingSurfaceMP::~FESlidingSurfaceMP()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FESlidingSurfaceMP::CreateMaterialPoint()\n{\n\treturn new FEMultiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    // get nodal DOFS\n    DOFS& dofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = dofs.GetVariableSize(\"concentration\");\n    \n\tint N = el.Nodes();\n\tlm.resize(N*(4+MAX_CDOFS));\n\n\t// pack the equation numbers\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\n\t\tFENode& node = m_pMesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[3*i  ] = id[m_dofX];\n\t\tlm[3*i+1] = id[m_dofY];\n\t\tlm[3*i+2] = id[m_dofZ];\n\n\t\t// now the pressure dofs\n\t\tlm[3*N+i] = id[m_dofP];\n\n\t\t// concentration dofs\n\t\tfor (int k=0; k<MAX_CDOFS; ++k)\n\t\t\tlm[(4 + k)*N + i] = id[m_dofC+k];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingSurfaceMP::Init()\n{\n\t// initialize surface data first\n\tif (FEBiphasicContactSurface::Init() == false) return false;\n\n\t// store concentration index\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tm_dofC = dofs.GetDOF(\"concentration\", 0);\n\t\n    // allocate node normals and pressures\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    m_tn.assign(Nodes(), vec3d(0,0,0));\n    m_pn.assign(Nodes(), 0);\n\n\t// determine solutes for this surface using the first surface element\n\t// TODO: Check that all elements use the same set of solutes as the first element\n\tint nsol = 0;\n\tif (Elements()) {\n\t\tFESurfaceElement& se = Element(0);\n\t\t// get the element this surface element belongs to\n\t\tFEElement* pe = se.m_elem[0].pe;\n\t\tif (pe)\n\t\t{\n\t\t\t// get the material\n\t\t\tFEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n\t\t\t\n\t\t\t// check type of element\n\t\t\tFEBiphasic* pb = dynamic_cast<FEBiphasic*> (pm);\n\t\t\tFEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n            FETriphasic* ptp = dynamic_cast<FETriphasic*> (pm);\n\t\t\tFEMultiphasic* pmp = dynamic_cast<FEMultiphasic*> (pm);\n\t\t\tif (pb) {\n\t\t\t\tm_bporo = true;\n\t\t\t\tnsol = 0;\n\t\t\t}\n            else if (pbs) {\n\t\t\t\tm_bporo = m_bsolu = true;\n\t\t\t\tnsol = 1;\n\t\t\t\tm_sid.assign(nsol, pbs->GetSolute()->GetSoluteID() - 1);\n\t\t\t}\n            else if (ptp) {\n                m_bporo = m_bsolu = true;\n                nsol = ptp->Solutes();\n                m_sid.resize(nsol);\n                for (int isol=0; isol<nsol; ++isol) {\n                    m_sid[isol] = ptp->GetSolute(isol)->GetSoluteID() - 1;\n                }\n            }\n            else if (pmp) {\n\t\t\t\tm_bporo = m_bsolu = true;\n\t\t\t\tnsol = pmp->Solutes();\n\t\t\t\tm_sid.resize(nsol);\n\t\t\t\tfor (int isol=0; isol<nsol; ++isol) {\n\t\t\t\t\tm_sid[isol] = pmp->GetSolute(isol)->GetSoluteID() - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// allocate data structures\n\tint NE = Elements();\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint nint = el.GaussPoints();\n        if (nsol) {\n            for (int j=0; j<nint; ++j) {\n                FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n                data.m_Lmc.resize(nsol);\n                data.m_epsc.resize(nsol);\n                data.m_cg.resize(nsol);\n                data.m_c1.resize(nsol);\n                data.m_epsc.assign(nsol,1);\n                data.m_c1.assign(nsol,0.0);\n                data.m_cg.assign(nsol,0.0);\n                data.m_Lmc.assign(nsol,0.0);\n            }\n        }\n\t}\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::InitSlidingSurface()\n{\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            // Store current surface projection values as previous\n            FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            data.m_rsp = data.m_rs;\n            data.m_pmep = data.m_pme;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact pressures by averaging values from surrounding\n//! faces.  This function ensures that nodal contact pressures are always\n//! positive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurfaceMP::EvaluateNodalContactPressures()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact pressures\n    zero(m_pn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact pressure for that face\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_pn[el.m_lnode[j]] += pn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_pn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the nodal contact pressures by averaging values from surrounding\n//! faces.  This function ensures that nodal contact pressures are always\n//! positive, so that they can be used to detect free-draining status.\n\nvoid FESlidingSurfaceMP::EvaluateNodalContactTractions()\n{\n    const int N = Nodes();\n    \n    // number of faces with non-zero contact pressure connected to this node\n    vector<int> nfaces(N,0);\n    \n    // zero nodal contact pressures\n    zero(m_tn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the average contact traction and pressure for that face\n        vec3d tn(0,0,0);\n        GetContactTraction(i, tn);\n        double pn = 0;\n        GetContactPressure(i, pn);\n        \n        if (pn > 0) {\n            for (int j=0; j<ne; ++j)\n            {\n                m_tn[el.m_lnode[j]] += tn;\n                ++nfaces[el.m_lnode[j]];\n            }\n        }\n    }\n    \n    // get average over all contacting faces sharing that node\n    for (int i=0; i<N; ++i)\n        if (nfaces[i] > 0) m_tn[i] /= nfaces[i];\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the \n//! element normals at the node\n\nvoid FESlidingSurfaceMP::UpdateNodeNormals()\n{\n\tint N = Nodes(), i, j, ne, jp1, jm1;\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d y[MN], n;\n\t\n\t// zero nodal normals\n\tzero(m_nn);\n\t\n\t// loop over all elements\n\tfor (i=0; i<Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tne = el.Nodes();\n\t\t\n\t\t// get the nodal coordinates\n\t\tfor (j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n\t\t\n\t\t// calculate the normals\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\tjp1 = (j+1)%ne;\n\t\t\tjm1 = (j+ne-1)%ne;\n\t\t\tn = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n\t\t\tm_nn[el.m_lnode[j]] += n;\n\t\t}\n\t}\n\t\n\t// normalize all vectors\n\tfor (i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurfaceMP::GetContactForce()\n{\n    return m_Ft;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESlidingSurfaceMP::GetContactArea()\n{\n\t// initialize contact area\n\tdouble a = 0;\n\t\n\t// loop over all elements of the primary surface\n\tfor (int n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// evaluate the contact force for that element\n\t\tfor (int i=0; i<nint; ++i)\n\t\t{\n\t\t\t// get data for this integration point\n            FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(i));\n\t\t\tdouble s = (data.m_Ln > 0) ? 1 : 0;\n            \n\t\t\t// get the base vectors\n\t\t\tvec3d g[2];\n\t\t\tCoBaseVectors(el, i, g);\n            \n\t\t\t// normal (magnitude = area)\n\t\t\tvec3d n = g[0] ^ g[1];\n            \n\t\t\t// gauss weight\n\t\t\tdouble w = el.GaussWeights()[i];\n            \n\t\t\t// contact force\n\t\t\ta += n.norm()*(w*s);\n\t\t}\n\t}\n\t\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingSurfaceMP::GetFluidForce()\n{\n\tint n, i;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble pn[MN];\n\t\n    // get parent contact interface to extract ambient fluid pressure\n    FESlidingInterfaceMP* simp = dynamic_cast<FESlidingInterfaceMP*>(GetContactInterface());\n    double ambp = simp->m_ambp;\n    \n\t// initialize contact force\n\tvec3d f(0,0,0);\n\t\n\t// loop over all elements of the surface\n\tfor (n=0; n<Elements(); ++n)\n\t{\n\t\tFESurfaceElement& el = Element(n);\n\t\tint nseln = el.Nodes();\n\t\t\n\t\t// nodal pressures\n\t\tfor (i=0; i<nseln; ++i) pn[i] = GetMesh()->Node(el.m_node[i]).get(m_dofP);\n\t\t\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// evaluate the fluid force for that element\n\t\tfor (i=0; i<nint; ++i) \n\t\t{\n\t\t\t// get the base vectors\n\t\t\tvec3d g[2];\n\t\t\tCoBaseVectors(el, i, g);\n\t\t\t// normal (magnitude = area)\n\t\t\tvec3d n = g[0] ^ g[1];\n\t\t\t// gauss weight\n\t\t\tdouble w = el.GaussWeights()[i];\n            // fluid pressure for fluid load support\n            double p = el.eval(pn, i) - ambp;\n\t\t\t// contact force\n\t\t\tf += n*(w*p);\n\t\t}\n\t}\n\t\n\treturn f;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_dofP & m_dofC;\n\tar & m_bporo;\n\tar & m_bsolu;\n\tar & m_nn;\n\tar & m_sid;\n\tar & m_pn;\n    ar & m_tn;\n\tar & m_Ft;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetVectorGap(int nface, vec3d& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = vec3d(0,0,0);\n    for (int k=0; k<ni; ++k)\n    {\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        pg += data.m_dg;\n    }\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetContactPressure(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_Ln;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        pt += data.m_tr;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetSlipTangent(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n    for (int k=0; k<ni; ++k)\n    {\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        if (!data.m_bstick) pt += data.m_s1;\n    }\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetMuEffective(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n    for (int k=0; k<ni; ++k)\n    {\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        pg += data.m_mueff;\n    }\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetLocalFLS(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n    for (int k = 0; k < ni; ++k)\n    {\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        pg += data.m_fls;\n    }\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetNodalVectorGap(int nface, vec3d* pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    vec3d gi[FEElement::MAX_INTPOINTS];\n    for (int k=0; k<ni; ++k)\n    {\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        gi[k] = data.m_dg;\n    }\n    el.project_to_nodes(gi, pg);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetNodalContactPressure(int nface, double* pn)\n{\n\tFESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        pn[k] = m_pn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetStickStatus(int nface, double& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = 0;\n    for (int k=0; k<ni; ++k)\n    {\n        FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(k));\n        if (data.m_bstick) pg += 1.0;\n    }\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingSurfaceMP::GetNodalContactTraction(int nface, vec3d* tn)\n{\n\tFESurfaceElement& el = Element(nface);\n    for (int k=0; k<el.Nodes(); ++k)\n        tn[k] = m_tn[el.m_lnode[k]];\n}\n\n//-----------------------------------------------------------------------------\n// FESlidingInterfaceMP\n//-----------------------------------------------------------------------------\n\nFESlidingInterfaceMP::FESlidingInterfaceMP(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\t\n\t// initial values\n\tm_knmult = 1;\n\tm_atol = 0.1;\n\tm_epsn = 1;\n\tm_epsp = 1;\n\tm_epsc = 1;\n\tm_btwo_pass = false;\n\tm_stol = 0.01;\n\tm_bsymm = true;\n\tm_srad = 1.0;\n\tm_gtol = 0;\n\tm_ptol = 0;\n\tm_ctol = 0;\n\tm_ambp = 0;\n\tm_nsegup = 0;\n\tm_bautopen = false;\n    m_breloc = false;\n    m_bsmaug = false;\n    m_bupdtpen = false;\n    m_mu = 0.0;\n    m_phi = 0.0;\n\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n    m_bfreeze = false;\n\n\tm_dofP = -1;\n\tm_dofC = -1;\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n}\n\n//-----------------------------------------------------------------------------\n\nFESlidingInterfaceMP::~FESlidingInterfaceMP()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterfaceMP::Init()\n{\n\tm_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n\tm_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n\n\t// get number of DOFS\n\tFEModel* fem = GetFEModel();\n\tDOFS& fedofs = fem->GetDOFS();\n\tint nsol = fedofs.GetVariableSize(\"concentration\");\n\tm_ambc.assign(nsol, 0.0);\n\tm_dofP = fem->GetDOFIndex(\"p\");\n\tm_dofC = fem->GetDOFIndex(\"concentration\", 0);\n\t\n\t// initialize surface data\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\t\n\t// determine which solutes are common to both contact surfaces\n    m_sid.clear(); m_ssl.clear(); m_msl.clear(); m_sz.clear();\n\tfor (int is=0; is<m_ss.m_sid.size(); ++is) {\n\t\tfor (int im=0; im<m_ms.m_sid.size(); ++im) {\n\t\t\tif (m_ms.m_sid[im] == m_ss.m_sid[is]) {\n\t\t\t\tm_sid.push_back(m_ss.m_sid[is]);\n\t\t\t\tm_ssl.push_back(is);\n\t\t\t\tm_msl.push_back(im);\n                FESoluteData* sd = FindSoluteData(m_ss.m_sid[is]+1);\n                m_sz.push_back(sd->m_z);\n\t\t\t}\n\t\t}\n\t}\n\n    // cycle through all the solutes and determine ambient concentrations\n\tfor (int i = 0; i < m_ambctmp.size(); ++i)\n\t{\n\t\tFEAmbientConcentration* aci = m_ambctmp[i];\n\t\tint isol = aci->m_sol - 1;\n\t\tassert((isol >= 0) && (isol < nsol));\n\t\tm_ambc[isol] = aci->m_ambc;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FESlidingInterfaceMP::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_P = fem.GetDOFIndex(\"p\");\n\tconst int dof_C = fem.GetDOFIndex(\"concentration\", 0);\n\n    int nsol = (int)m_sid.size();\n    int ndpn = 7 + nsol;\n    \n\tvector<int> lm(ndpn*FEElement::MAX_NODES*2);\n    \n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFESlidingSurfaceMP& ss = (np == 0? m_ss : m_ms);\n        \n\t\tint k, l;\n\t\tfor (int j=0; j<ss.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\t\t\tint nint = se.GaussPoints();\n\t\t\tint* sn = &se.m_node[0];\n\t\t\tfor (k=0; k<nint; ++k)\n\t\t\t{\n                FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(k));\n\n\t\t\t\tFESurfaceElement* pe = data.m_pme;\n\t\t\t\tif (pe != 0)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\t\tint* mn = &me.m_node[0];\n                    \n\t\t\t\t\tassign(lm, -1);\n                    \n\t\t\t\t\tint nseln = se.Nodes();\n\t\t\t\t\tint nmeln = me.Nodes();\n                    \n\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(sn[l]).m_ID;\n\t\t\t\t\t\tlm[ndpn*l  ] = id[dof_X];\n\t\t\t\t\t\tlm[ndpn*l+1] = id[dof_Y];\n\t\t\t\t\t\tlm[ndpn*l+2] = id[dof_Z];\n\t\t\t\t\t\tlm[ndpn*l+3] = id[dof_P];\n                        for (int m=0; m<nsol; ++m) {\n                            lm[ndpn*l+4+m] = id[dof_C + m_sid[m]];\n                        }\n\t\t\t\t\t}\n                    \n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(mn[l]).m_ID;\n\t\t\t\t\t\tlm[ndpn*(l+nseln)  ] = id[dof_X];\n\t\t\t\t\t\tlm[ndpn*(l+nseln)+1] = id[dof_Y];\n\t\t\t\t\t\tlm[ndpn*(l+nseln)+2] = id[dof_Z];\n\t\t\t\t\t\tlm[ndpn*(l+nseln)+3] = id[dof_P];\n                        for (int m=0; m<nsol; ++m) {\n                            lm[ndpn*(l+nseln)+4+m] = id[dof_C + m_sid[m]];\n                        }\n\t\t\t\t\t}\n                    \n\t\t\t\t\tK.build_add(lm);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n    \n}\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n        if (m_ss.m_bporo) CalcAutoPressurePenalty(m_ss);\n        for (int is=0; is<m_ssl.size(); ++is)\n            CalcAutoConcentrationPenalty(m_ss, m_ssl[is]);\n        if (m_ms.m_bporo) CalcAutoPressurePenalty(m_ms);\n        for (int im=0; im<m_msl.size(); ++im)\n            CalcAutoConcentrationPenalty(m_ms, m_msl[im]);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::Activate()\n{\n\t// don't forget to call the base members\n\tFEContactInterface::Activate();\n\t\n    UpdateAutoPenalty();\n    \n\t// update sliding interface data\n\tUpdate();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::CalcAutoPenalty(FESlidingSurfaceMP& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j)\n        {\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::CalcAutoPressurePenalty(FESlidingSurfaceMP& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPressurePenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j)\n        {\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates a contact penalty parameter based on the\n//! material and geometrical properties of the primary and secondary surfaces\n//!\ndouble FESlidingInterfaceMP::AutoPenalty(FESurfaceElement& el, FESurface &s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n\n\ttens4ds S;\n\t// get a material point\n\tFEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        \n\t// extract the material\n\tFEMaterial* pme = GetFEModel()->GetMaterial(pe->GetMatID());\n\tif (pme == 0) return 0.0;\n\n    // get the tangent (stiffness)\n    if (dynamic_cast<FEMultiphasic*>(pme)) {\n        FEMultiphasic* pmm = dynamic_cast<FEMultiphasic*>(pme);\n        S = pmm->Tangent(mp);\n    }\n    else if (dynamic_cast<FETriphasic*>(pme)) {\n        FETriphasic* pms = dynamic_cast<FETriphasic*>(pme);\n        S = pms->Tangent(mp);\n    }\n    else if (dynamic_cast<FEBiphasicSolute*>(pme)) {\n        FEBiphasicSolute* pms = dynamic_cast<FEBiphasicSolute*>(pme);\n        S = pms->Tangent(mp);\n    }\n    else if (dynamic_cast<FEBiphasic*>(pme)) {\n        FEBiphasic* pmb = dynamic_cast<FEBiphasic*>(pme);\n        S = (pmb->Tangent(mp)).supersymm();\n    }\n    else if (dynamic_cast<FEElasticMaterial*>(pme)) {\n        FEElasticMaterial* pm = dynamic_cast<FEElasticMaterial*>(pme);\n        S = pm->Tangent(mp);\n    }\n    // get the inverse (compliance) at this point\n    tens4ds C = S.inverse();\n            \n    // evaluate element surface normal at parametric center\n    vec3d t[2];\n    s.CoBaseVectors0(el, 0, 0, t);\n    vec3d n = t[0] ^ t[1];\n    n.unit();\n            \n    // evaluate normal component of the compliance matrix\n    // (equivalent to inverse of Young's modulus along n)\n    double eps = 1./(n*(vdotTdotv(n, C, n)*n));\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n    return eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\n\ndouble FESlidingInterfaceMP::AutoPressurePenalty(FESurfaceElement& el, FESlidingSurfaceMP& s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\t\n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n\t\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == 0) return 0.0;\n\n\t// get the material\n\tFEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\t\t\n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        \n    mat3ds K;\n        \n\t// check type of element\n\tFEBiphasic* pb = dynamic_cast<FEBiphasic*> (pm);\n\tFEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n    FETriphasic* ptp = dynamic_cast<FETriphasic*> (pm);\n\tFEMultiphasic* pmp = dynamic_cast<FEMultiphasic*> (pm);\n    if (pb)\n        K = pb->GetPermeability()->Permeability(mp);\n\telse if (pbs)\n\t\tK = pbs->GetPermeability()->Permeability(mp);\n    else if (ptp)\n        K = ptp->GetPermeability()->Permeability(mp);\n\telse if (pmp)\n\t\tK = pmp->GetPermeability()->Permeability(mp);\n        \n\tdouble eps = n*(K*n);\n\t\n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::CalcAutoConcentrationPenalty(FESlidingSurfaceMP& s,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst int isol)\n{\n\t// loop over all surface elements\n\tint ni = 0;\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a modulus\n\t\tdouble eps = AutoConcentrationPenalty(el, s, isol);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j, ++ni)\n        {\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsc[isol] = eps;\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\ndouble FESlidingInterfaceMP::AutoConcentrationPenalty(FESurfaceElement& el, \n\t\t\t\t\t\t\t\t\t\t\t\t\t  FESlidingSurfaceMP& s,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  const int isol)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\t\n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n\t\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == 0) return 0.0;\n\n\t// get the material\n\tFEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\t\t\n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        \n    mat3ds D;\n        \n\t// see if this is a biphasic-solute or multiphasic element\n\tFEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n    FETriphasic* ptp = dynamic_cast<FETriphasic*> (pm);\n\tFEMultiphasic* pmp = dynamic_cast<FEMultiphasic*> (pm);\n\tif (pbs)\n\t{\n\t\tD = pbs->GetSolute()->m_pDiff->Diffusivity(mp)\n\t\t*(pbs->Porosity(mp)*pbs->GetSolute()->m_pSolub->Solubility(mp));\n\t}\n    else if (ptp)\n    {\n        D = ptp->GetSolute(isol)->m_pDiff->Diffusivity(mp)\n        *(ptp->Porosity(mp)*ptp->GetSolute(isol)->m_pSolub->Solubility(mp));\n    }\n\telse if (pmp)\n\t{\n\t\tD = pmp->GetSolute(isol)->m_pDiff->Diffusivity(mp)\n\t\t*(pmp->Porosity(mp)*pmp->GetSolute(isol)->m_pSolub->Solubility(mp));\n\t}\n        \n\t// evaluate normal component of diffusivity\n\tdouble eps = n*(D*n);\n\n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\t\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::ProjectSurface(FESlidingSurfaceMP& ss, FESlidingSurfaceMP& ms, bool bupseg, bool bmove)\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    FESurfaceElement* pme;\n    vec3d r, nu;\n    double rs[2] = {0,0};\n    double Ln;\n    \n    const int MN = FEElement::MAX_NODES;\n    int nsol = (int)m_sid.size();\n    double ps[MN], p1 = 0.0;\n    vector< vector<double> > cs(nsol, vector<double>(MN));\n    vector<double> c1(nsol);\n    c1.assign(nsol,0);\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n    \n    // if we need to project the nodes onto the secondary surface,\n    // let's do this first\n    if (bmove)\n    {\n        int NN = ss.Nodes();\n        int NE = ss.Elements();\n        // first we need to calculate the node normals\n        vector<vec3d> normal; normal.assign(NN, vec3d(0,0,0));\n        for (int i=0; i<NE; ++i)\n        {\n            FESurfaceElement& el = ss.Element(i);\n            int ne = el.Nodes();\n            for (int j=0; j<ne; ++j)\n            {\n                vec3d r0 = ss.Node(el.m_lnode[ j         ]).m_rt;\n                vec3d rp = ss.Node(el.m_lnode[(j+   1)%ne]).m_rt;\n                vec3d rm = ss.Node(el.m_lnode[(j+ne-1)%ne]).m_rt;\n                vec3d n = (rp - r0)^(rm - r0);\n                normal[el.m_lnode[j]] += n;\n            }\n        }\n        for (int i=0; i<NN; ++i) normal[i].unit();\n        \n        // loop over all nodes\n        for (int i=0; i<NN; ++i)\n        {\n            FENode& node = ss.Node(i);\n            \n            // get the spatial nodal coordinates\n            vec3d rt = node.m_rt;\n            vec3d nu = normal[i];\n            \n            // project onto the secondary surface\n            vec3d q;\n            double rs[2] = {0,0};\n            FESurfaceElement* pme = np.Project(rt, nu, rs);\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double gap = nu*(rt - q);\n                \n                if (gap>0) node.m_r0 = node.m_rt = q;\n            }\n        }\n    }\n    \n    // loop over all integration points\n    // TODO: commenting this pragma line made my code recover slidingelastic test case results\n//#pragma omp parallel for\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        \n        bool sporo = ss.m_bporo;\n        \n        int ne = el.Nodes();\n        int nint = el.GaussPoints();\n        \n        // get the nodal pressures\n        if (sporo)\n        {\n            for (int j=0; j<ne; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        }\n        \n        // get the nodal concentrations\n        for (int isol=0; isol<nsol; ++isol) {\n            for (int j=0; j<ne; ++j) cs[isol][j] = mesh.Node(el.m_node[j]).get(m_dofC + m_sid[isol]);\n        }\n        \n        for (int j=0; j<nint; ++j)\n        {\n            FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // get the pressure at the integration point\n            if (sporo) p1 = el.eval(ps, j);\n            \n            // get the concentration at the integration point\n            for (int isol=0; isol<nsol; ++isol) c1[isol] = el.eval(&cs[isol][0], j);\n            \n            // calculate the normal at this integration point\n            nu = ss.SurfaceNormal(el, j);\n            \n            // first see if the old intersected face is still good enough\n            pme = pt.m_pme;\n            if (pme)\n            {\n                double g;\n                \n                // see if the ray intersects this element\n                if (ms.Intersect(*pme, r, nu, rs, g, m_stol))\n                {\n                    pt.m_rs[0] = rs[0];\n                    pt.m_rs[1] = rs[1];\n                }\n                else\n                {\n                    pme = 0;\n                }\n            }\n            \n            // find the intersection point with the secondary surface\n            if (pme == 0 && bupseg) pme = np.Project(r, nu, rs);\n            \n            pt.m_pme = pme;\n            pt.m_nu = nu;\n            pt.m_rs[0] = rs[0];\n            pt.m_rs[1] = rs[1];\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                // NOTE: this has the opposite sign compared\n                // to Gerard's notes.\n                double g = nu*(r - q);\n                \n                double eps = m_epsn*pt.m_epsn*psf;\n                \n                Ln = pt.m_Lmd + eps*g;\n                \n                pt.m_gap = (g <= m_srad? g : 0);\n                \n                bool mporo = ms.m_bporo;\n                \n                if ((Ln >= 0) && (g <= m_srad))\n                {\n                    \n                    // get the pressure at the contact point\n                    // account for mixed multiphasic-elastic contact with elastic primary\n                    // calculate the pressure gap function\n                    double p2 = 0;\n                    if (mporo) {\n                        double pm[MN];\n                        for (int k=0; k<pme->Nodes(); ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n                        p2 = pme->eval(pm, rs[0], rs[1]);\n                    }\n                    if (sporo) {\n                        pt.m_p1 = p1;\n                        if (mporo) {\n                            pt.m_pg = p1 - p2;\n                        }\n                    }\n                    else if (mporo) {\n                        pt.m_p1 = p2;\n                    }\n\n                    for (int isol=0; isol<nsol; ++isol) {\n                        int sid = m_sid[isol];\n                        double cm[MN];\n                        for (int k=0; k<pme->Nodes(); ++k) cm[k] = mesh.Node(pme->m_node[k]).get(m_dofC + sid);\n                        double c2 = pme->eval(cm, rs[0], rs[1]);\n                        pt.m_cg[m_ssl[isol]] = c1[isol] - c2;\n                        pt.m_c1[m_ssl[isol]] = c1[isol];\n                    }\n                }\n                else\n                {\n                    pt.m_Lmd = 0;\n                    pt.m_gap = 0;\n                    pt.m_pme = 0;\n                    pt.m_dg = pt.m_Lmt = vec3d(0,0,0);\n                    if (sporo || mporo) {\n                        pt.m_Lmp = 0;\n                        pt.m_pg = 0;\n                        pt.m_p1 = 0;\n                    }\n                    for (int isol=0; isol<nsol; ++isol) {\n                        pt.m_Lmc[m_ssl[isol]] = 0;\n                        pt.m_cg[m_ssl[isol]] = 0;\n                        pt.m_c1[m_ssl[isol]] = 0;\n                    }\n                }\n            }\n            else\n            {\n                // the node is not in contact\n                pt.m_Lmd = 0;\n                pt.m_gap = 0;\n                pt.m_dg = pt.m_Lmt = vec3d(0,0,0);\n                if (sporo) {\n                    pt.m_Lmp = 0;\n                    pt.m_pg = 0;\n                    pt.m_p1 = 0;\n                }\n                for (int isol=0; isol<nsol; ++isol) {\n                    pt.m_Lmc[m_ssl[isol]] = 0;\n                    pt.m_cg[m_ssl[isol]] = 0;\n                    pt.m_c1[m_ssl[isol]] = 0;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceMP::Update()\n{\n    double rs[2]={0,0};\n    \n    FEModel& fem = *GetFEModel();\n    \n    // get number of DOFS\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n    static int naug = 0;\n    static int biter = 0;\n    \n    // get the iteration number\n    // we need this number to see if we can do segment updates or not\n    // also reset number of iterations after each augmentation\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolver* psolver = pstep->GetFESolver();\n    if (psolver->m_niter == 0) {\n        biter = 0;\n        naug = psolver->m_naug;\n        // check update of auto-penalty\n        if (m_bupdtpen) UpdateAutoPenalty();\n    } else if (psolver->m_naug > naug) {\n        biter = psolver->m_niter;\n        naug = psolver->m_naug;\n    }\n    int niter = psolver->m_niter - biter;\n    bool bupseg = ((m_nsegup == 0)? true : (niter <= m_nsegup));\n    \n    // get the logfile\n    //    Logfile& log = GetLogfile();\n    //    log.printf(\"seg_up iteration # %d\\n\", niter+1);\n    \n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    static bool bfirst = true;\n    ProjectSurface(m_ss, m_ms, bupseg, (m_breloc && bfirst));\n    // TODO: there was a bug below - the right part of the OR statement was m_ss.m_bporo\n    if (m_btwo_pass || m_ms.m_bporo) ProjectSurface(m_ms, m_ss, bupseg);\n    bfirst = false;\n    \n    // Call InitSlidingSurface on the first iteration of each time step\n    // TODO: previously had the line below controlling InitSlidingSurface, but SlidingBiphasic uses nsolve_iter\n    //  if (niter == 0)\n    int nsolve_iter = psolver->m_niter;\n    if (nsolve_iter == 0)\n    {\n        m_ss.InitSlidingSurface();\n        if (m_btwo_pass) m_ms.InitSlidingSurface();\n        m_bfreeze = false;\n    }\n    \n    // Update the net contact pressures\n    UpdateContactPressures();\n    \n    if (niter == 0) m_bfreeze = false;\n    \n    // set poro flag\n    bool bporo = (m_ss.m_bporo || m_ms.m_bporo);\n    \n    // only continue if we are doing a poro-elastic simulation\n    if (bporo == false) return;\n\n    // TODO: this line is above the \"only continue if...\" line in SlidingMP\n    // update node normals\n    m_ss.UpdateNodeNormals();\n    m_ms.UpdateNodeNormals();\n    \n    // Now that the nodes have been projected, we need to figure out\n    // if we need to modify the constraints on the pressure and concentration dofs.\n    // If the nodes are not in contact, they must match ambient\n    // conditions. Since all nodes have been previously marked to be\n    // under ambient conditions in MarkAmbient(), we just need to reverse\n    // this setting here, for nodes that are in contact.\n    \n    // Next, we loop over each surface, visiting the nodes\n    // and finding out if that node is in contact or not\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FESlidingSurfaceMP& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceMP& ms = (np == 0? m_ms : m_ss);\n        \n        // initialize projection data\n        FENormalProjection project(ss);\n        project.SetTolerance(m_stol);\n        project.SetSearchRadius(m_srad);\n        project.Init();\n\n        // loop over all the nodes of the primary surface\n        for (int n=0; n<ss.Nodes(); ++n) {\n            if (ss.m_pn[n] > 0)\n            {\n                FENode& node = ss.Node(n);\n                int id = node.m_ID[m_dofP];\n                if (id < -1)\n                    // mark node as non-ambient (= pos ID)\n                    node.m_ID[m_dofP] = -id-2;\n                for (int j=0; j<MAX_CDOFS; ++j) {\n                    id = node.m_ID[m_dofC+j];\n                    if (id < -1)\n                        // mark node as non-ambient (= pos ID)\n                        node.m_ID[m_dofC+j] = -id-2;\n                }\n            }\n        }\n        \n        // loop over all nodes of the secondary surface\n        // the secondary surface is trickier since we need\n        // to look at the primary's surface projection\n        if (ms.m_bporo) {\n            for (int n=0; n<ms.Nodes(); ++n)\n            {\n                // get the node\n                FENode& node = ms.Node(n);\n                \n                // project it onto the primary surface\n                FESurfaceElement* pse = project.Project(node.m_rt, ms.m_nn[n], rs);\n                \n                if (pse)\n                {\n                    // we found an element, so let's see if it's even remotely close to contact\n                    // find the global location of the intersection point\n                    vec3d q = ss.Local2Global(*pse, rs[0], rs[1]);\n                    \n                    // calculate the gap function\n                    double g = ms.m_nn[n]*(node.m_rt - q);\n                    \n                    // TODO: SlidingMP uses R, SlidingBiphasic uses m_srad\n                    if (fabs(g) <= m_srad)\n                    {\n                        // we found an element so let's calculate the nodal traction values for this element\n                        // get the normal tractions at the nodes\n                        double tn[FEElement::MAX_NODES];\n                        for (int i=0; i<pse->Nodes(); ++i)\n                            tn[i] = ss.m_pn[pse->m_lnode[i]];\n                        \n                        // now evaluate the traction at the intersection point\n                        double tp = pse->eval(tn, rs[0], rs[1]);\n                        \n                        // if tp > 0, mark node as non-ambient. (= pos ID)\n                        if (tp > 0)  {\n                            int id = node.m_ID[m_dofP];\n                            if (id < -1)\n                                // mark as non-ambient\n                                node.m_ID[m_dofP] = -id-2;\n                            for (int j=0; j<MAX_CDOFS; ++j) {\n                                id = node.m_ID[m_dofC+j];\n                                if (id < -1)\n                                    // mark as non-ambient\n                                    node.m_ID[m_dofC+j] = -id-2;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingInterfaceMP::SlipTangent(FESlidingSurfaceMP& ss, const int nel, const int nint, FESlidingSurfaceMP& ms, double& dh, vec3d& r)\n{\n    vec3d s1(0,0,0);\n    dh = 0;\n    r = vec3d(0,0,0);\n    \n    // get primary surface element\n    FESurfaceElement& se = ss.Element(nel);\n    \n    // get integration point data\n    FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(nint));\n    double g = data.m_gap;\n    vec3d nu = data.m_nu;\n    \n    // find secondary surface element\n    FESurfaceElement* pme = data.m_pme;\n    \n    // calculate previous positions\n    vec3d x2p = ms.Local2GlobalP(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d x1p = ss.Local2GlobalP(se, nint);\n    \n    // calculate dx2\n    vec3d x2 = ms.Local2Global(*pme, data.m_rs[0], data.m_rs[1]);\n    vec3d dx2 = x2 - x2p;\n    \n    // calculate dx1\n    vec3d x1 = ss.Local2Global(se, nint);\n    vec3d dx1 = x1 - x1p;\n    \n    // get current and previous covariant basis vectors\n    vec3d gscov[2], gscovp[2];\n    ss.CoBaseVectors(se, nint, gscov);\n    ss.CoBaseVectorsP(se, nint, gscovp);\n    \n    // calculate delta gscov\n    vec3d dgscov[2];\n    dgscov[0] = gscov[0] - gscovp[0];\n    dgscov[1] = gscov[1] - gscovp[1];\n    \n    // calculate m, J, Nhat\n    vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n    double detJ = (gscov[0] ^ gscov[1]).norm();\n    mat3d Nhat = (mat3dd(1) - (nu & nu));\n    \n    // calculate c\n    vec3d c = Nhat*m*(1.0/detJ);\n    \n    // calculate slip direction s1\n    double norm = (Nhat*(c*(-g) + dx1 - dx2)).norm();\n    if (norm != 0)\n    {\n        s1 = (Nhat*(c*(-g) + dx1 - dx2))/norm;\n        dh = norm;\n        r = c*(-g) + dx1 - dx2;\n    }\n    \n    return s1;\n    \n}\n\n//-----------------------------------------------------------------------------\nvec3d FESlidingInterfaceMP::ContactTraction(FESlidingSurfaceMP& ss, const int nel, const int n, FESlidingSurfaceMP& ms, double& pn)\n{\n    vec3d s1(0,0,0);\n    vec3d dr(0,0,0);\n    vec3d t(0,0,0);\n    pn = 0;\n    double tn = 0, ts = 0, mueff = 0;\n    double psf = GetPenaltyScaleFactor();\n    \n    int nsol = (int)m_sid.size();\n    vector<double> c1(nsol);\n    c1.assign(nsol,0);\n    \n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // get the primary surface element\n    FESurfaceElement& se = ss.Element(nel);\n    \n    // get the integration point data\n    FEMultiphasicContactPoint& data = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(n));\n    \n    // penalty\n    double eps = m_epsn*data.m_epsn*psf;\n    \n    // normal gap\n    double g = data.m_gap;\n    \n    // normal traction Lagrange multiplier\n    double Lm = data.m_Lmd;\n    \n    // vector traction Lagrange multiplier\n    vec3d Lt = data.m_Lmt;\n    \n    // get the normal at this integration point\n    vec3d nu = data.m_nu;\n    \n    // get the fluid pressure at this integration point\n    double p = data.m_p1;\n    \n    // get the solute concentrations at this integration point\n    for (int isol=0; isol<nsol; ++isol) c1[isol] = data.m_c1[m_ssl[isol]];\n    \n    // get the fluid pressure for load sharing at this integration point\n    double ph = data.m_p1 - m_ambp;\n    \n    // get poro status of primary surface\n    bool sporo = ss.m_bporo;\n    \n    // get current and previous secondary elements\n    FESurfaceElement* pme = data.m_pme;\n    FESurfaceElement* pmep = data.m_pmep;\n    \n    // zero the effective friction coefficient\n    data.m_mueff = 0.0;\n    data.m_fls = 0.0;\n    data.m_s1 = vec3d(0,0,0);\n    \n    // get local FLS from element projection\n    // TODO: Gerard: I added an optional 'pamb' argument to GetGPLocalFLS\n    double fls = 0;\n    if (m_bsmfls) {\n        double lfls[FEElement::MAX_INTPOINTS];\n        ss.GetGPLocalFLS(nel, lfls, m_ambp);\n        fls = lfls[n];\n    }\n    \n    // if we just returned from an augmentation, do not update stick or slip status\n    if (m_bfreeze && pme) {\n        if (data.m_bstick) {\n            // calculate current global position of the integration point\n            vec3d xo = ss.Local2Global(se, n);\n            \n            // calculate current global position of the previous intersection point\n            vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n            \n            // calculate vector gap\n            vec3d dg = xt - xo;\n            \n            // calculate trial stick traction, normal component, shear component\n            t = Lt + dg*eps;\n            tn = t*nu;\n            ts = (t - nu*tn).norm();\n            \n            // contact pressure\n            pn = MBRACKET(-tn);\n            \n            // calculate effective friction coefficient\n            if (pn > 0)\n            {\n                data.m_mueff = ts/pn;\n                data.m_fls = m_bsmfls ? fls : ph/pn;\n            }\n            \n            // store the previous values as the current\n            data.m_pme = data.m_pmep;\n            data.m_rs = data.m_rsp;\n            \n            // recalculate gap\n            data.m_dg = dg;\n            \n            // recalculate pressure gap\n            bool mporo = ms.m_bporo;\n            if (sporo && mporo)\n            {\n                double pm[FEElement::MAX_NODES];\n                for (int k=0; k<pme->Nodes(); ++k) pm[k] = m.Node(pme->m_node[k]).get(m_dofP);\n                double p2 = pme->eval(pm, data.m_rs[0], data.m_rs[1]);\n                data.m_pg = p - p2;\n            }\n            //TODO: what if stick/slip moves from poro to elastic or vice versa? Should I add\n            // an \"else {data.m_pg = 0}\" line?\n            \n            // recalculate concentration gaps\n            for (int isol=0; isol<nsol; ++isol) {\n                int sid = m_sid[isol];\n                double cm[FEElement::MAX_NODES];\n                for (int k=0; k<pme->Nodes(); ++k) cm[k] = m.Node(pme->m_node[k]).get(m_dofC + sid);\n                double c2 = pme->eval(cm, data.m_rs[0], data.m_rs[1]);\n                data.m_cg[m_ssl[isol]] = c1[isol] - c2;\n            }\n        }\n        else {\n            // recalculate contact pressure for slip\n            pn = MBRACKET(Lm + eps*g);\n            \n            if (pn != 0)\n            {\n                double dh = 0;\n                \n                // slip direction\n                s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                \n                // calculate effective friction coefficient\n                data.m_fls = m_bsmfls ? fls : ph/pn;\n                data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                data.m_mueff = MBRACKET(data.m_mueff);\n                \n                // total traction\n                t = (nu + s1*data.m_mueff)*(-pn);\n                \n                // reset slip direction\n                data.m_s1 = s1;\n            }\n            else\n            {\n                t = vec3d(0,0,0);\n            }\n        }\n    }\n    // update contact tractions\n    else {\n        data.m_bstick = false;\n        \n        if (pme)\n        {\n            // assume stick and calculate traction\n            if (pmep)\n            {\n                // calculate current global position of the integration point\n                vec3d xo = ss.Local2Global(se, n);\n                \n                // calculate current global position of the previous intersection point\n                vec3d xt = ms.Local2Global(*pmep, data.m_rsp[0], data.m_rsp[1]);\n                \n                // calculate vector gap\n                vec3d dg = xt - xo;\n                \n                // calculate trial stick traction, normal component, shear component\n                t = Lt + dg*eps;\n                tn = t*nu;\n                ts = (t - nu*tn).norm();\n                \n                // calculate effective friction coefficient\n                if (tn != 0)\n                {\n                    data.m_fls = m_bsmfls ? fls : ph/(-tn);\n                    mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                    mueff = MBRACKET(mueff);\n                }\n                \n                // check if stick\n                if ( (tn < 0) && (ts < fabs(tn*mueff)) )\n                {\n                    // set boolean flag for stick\n                    data.m_bstick = true;\n                    \n                    // contact pressure\n                    pn = MBRACKET(-tn);\n                    \n                    // calculate effective friction coefficient\n                    if (pn > 0) {\n                        data.m_mueff = ts/pn;\n                        data.m_fls = m_bsmfls ? fls : ph/pn;\n                    }\n                    \n                    // store the previous values as the current\n                    data.m_pme = data.m_pmep;\n                    data.m_rs = data.m_rsp;\n                    \n                    // recalculate gaps\n                    data.m_dg = dg;\n                    \n                    // recalculate pressure gap\n                    bool mporo = ms.m_bporo;\n                    if (sporo && mporo)\n                    {\n                        double pm[FEElement::MAX_NODES];\n                        for (int k=0; k<pme->Nodes(); ++k) pm[k] = m.Node(pme->m_node[k]).get(m_dofP);\n                        double p2 = pme->eval(pm, data.m_rs[0], data.m_rs[1]);\n                        data.m_pg = p - p2;\n                    }\n                    //TODO: what if stick/slip moves from poro to elastic or vice versa? Should I add\n                    // an \"else {data.m_pg = 0}\" line?\n                    \n                    // recalculate concentration gaps\n                    for (int isol=0; isol<nsol; ++isol) {\n                        int sid = m_sid[isol];\n                        double cm[FEElement::MAX_NODES];\n                        for (int k=0; k<pme->Nodes(); ++k) cm[k] = m.Node(pme->m_node[k]).get(m_dofC + sid);\n                        double c2 = pme->eval(cm, data.m_rs[0], data.m_rs[1]);\n                        data.m_cg[m_ssl[isol]] = c1[isol] - c2;\n                    }\n                    \n                }\n                else\n                {\n                    // recalculate contact pressure for slip\n                    pn = MBRACKET(Lm + eps*g);\n                    \n                    if (pn != 0)\n                    {\n                        \n                        double dh = 0;\n                        \n                        // slip direction\n                        s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                        \n                        // calculate effective friction coefficient\n                        data.m_fls = m_bsmfls ? fls : ph/pn;\n                        data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                        data.m_mueff = MBRACKET(data.m_mueff);\n                        \n                        // total traction\n                        t = (nu + s1*data.m_mueff)*(-pn);\n                        \n                        // reset slip direction\n                        data.m_s1 = s1;\n                        data.m_bstick = false;\n                    }\n                    else\n                    {\n                        t = vec3d(0,0,0);\n                    }\n                    \n                }\n            }\n            else\n            {\n                // assume slip upon first contact\n                // calculate contact pressure for slip\n                pn = MBRACKET(Lm + eps*g);\n                \n                if (pn != 0)\n                {\n                    \n                    double dh = 0;\n                    \n                    // slip direction\n                    s1 = SlipTangent(ss, nel, n, ms, dh, dr);\n                    \n                    // calculate effective friction coefficient\n                    data.m_fls = m_bsmfls ? fls : ph/pn;\n                    data.m_mueff = m_mu*(1.0-(1.0-m_phi)*data.m_fls);\n                    data.m_mueff = MBRACKET(data.m_mueff);\n                    \n                    // total traction\n                    t = (nu + s1*data.m_mueff)*(-pn);\n                    \n                    // reset slip direction\n                    data.m_s1 = s1;\n                    data.m_bstick = false;\n                }\n            }\n        }\n    }\n    \n    return t;\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    double N[MN*10];\n    int nsol = (int)m_sid.size();\n    \n    FEModel& fem = *GetFEModel();\n    \n    // need to multiply multiphasic stiffness entries by the timestep\n    double dt = fem.GetTime().timeIncrement;\n\n    double psf = GetPenaltyScaleFactor();\n    \n    m_ss.m_Ft = vec3d(0, 0, 0);\n    m_ms.m_Ft = vec3d(0, 0, 0);\n    \n    // loop over the nr of passes\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        // get primary and secondary surface\n        FESlidingSurfaceMP& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceMP& ms = (np == 0? m_ms : m_ss);\n        vector<int>& sl = (np == 0? m_ssl : m_msl);\n        \n        // loop over all primary surface elements\n        for (int i=0; i<ss.Elements(); ++i)\n        {\n            // get the surface element\n            FESurfaceElement& se = ss.Element(i);\n            \n            bool sporo = ss.m_bporo;\n            \n            // get the nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // copy the LM vector; we'll need it later\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (int j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n            }\n            \n            // loop over all integration points\n            // note that we are integrating over the current surface\n            for (int j=0; j<nint; ++j)\n            {\n                // get integration point data\n                FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n                \n                // calculate contact pressure and account for stick\n                double pn;\n                vec3d t = ContactTraction(ss, i, j, ms, pn);\n                \n                // get the secondary surface element\n                FESurfaceElement* pme = pt.m_pme;\n                \n                if (pme)\n                {\n                    // get the secondary surface element\n                    FESurfaceElement& me = *pme;\n                    \n                    bool mporo = ms.m_bporo;\n                    \n                    // get the nr of secondary element nodes\n                    int nmeln = me.Nodes();\n                    \n                    // copy LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    // calculate degrees of freedom\n                    int ndof = 3*(nseln + nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    for (int k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[3*k  ];\n                        LM[3*k+1] = sLM[3*k+1];\n                        LM[3*k+2] = sLM[3*k+2];\n                    }\n                    \n                    for (int k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[3*k  ];\n                        LM[3*(k+nseln)+1] = mLM[3*k+1];\n                        LM[3*(k+nseln)+2] = mLM[3*k+2];\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (int k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (int k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // get primary element shape functions\n                    Hs = se.H(j);\n                    \n                    // get secondary element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    if (pn > 0) {\n                        // calculate the force vector\n                        fe.resize(ndof);\n                        zero(fe);\n                        \n                        for (int k=0; k<nseln; ++k)\n                        {\n                            N[3*k  ] = Hs[k]*t.x;\n                            N[3*k+1] = Hs[k]*t.y;\n                            N[3*k+2] = Hs[k]*t.z;\n                        }\n                        \n                        for (int k=0; k<nmeln; ++k)\n                        {\n                            N[3*(k+nseln)  ] = -Hm[k]*t.x;\n                            N[3*(k+nseln)+1] = -Hm[k]*t.y;\n                            N[3*(k+nseln)+2] = -Hm[k]*t.z;\n                        }\n                        \n                        for (int k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n                        \n                        // calculate contact forces\n                        for (int k=0; k<nseln; ++k)\n                            ss.m_Ft += vec3d(fe[3*k], fe[3*k+1], fe[3*k+2]);\n                        \n                        for (int k = 0; k<nmeln; ++k)\n                            ms.m_Ft += vec3d(fe[3*(k+nseln)], fe[3*(k+nseln)+1], fe[3*(k+nseln)+2]);\n                        \n                        // assemble the global residual\n                        R.Assemble(en, LM, fe);\n                        \n                        // do the biphasic stuff\n                        if (sporo && mporo)\n                        {\n                            // calculate nr of pressure dofs\n                            int ndof = nseln + nmeln;\n                            \n                            // normal fluid flux\n                            double epsp = m_epsp*pt.m_epsp*psf;\n                            double wn = pt.m_Lmp + epsp*pt.m_pg;\n                            \n                            // fill the LM\n                            LM.resize(ndof);\n                            for (int k=0; k<nseln; ++k) LM[k      ] = sLM[3*nseln+k];\n                            for (int k=0; k<nmeln; ++k) LM[k+nseln] = mLM[3*nmeln+k];\n                            \n                            // fill the force array\n                            fe.resize(ndof);\n                            zero(fe);\n                            for (int k = 0; k<nseln; ++k) N[k      ] = Hs[k];\n                            for (int k = 0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                            for (int k = 0; k<ndof; ++k) fe[k] += dt*N[k]*wn*detJ[j]*w[j];\n                            \n                            \n                            // assemble residual\n                            R.Assemble(en, LM, fe);\n                        }\n                        \n                        // do the solute stuff\n                        for (int isol=0; isol<nsol; ++isol)\n                        {\n                            int sid = m_sid[isol];\n                            \n                            // calculate nr of concentration dofs\n                            int ndof = nseln + nmeln;\n                            \n                            // calculate normal effective solute flux\n                            int l = sl[isol];\n                            double epsc = m_epsc*pt.m_epsc[l]*psf;\n                            double jn = pt.m_Lmc[l] + epsc*pt.m_cg[l];\n                            \n                            // fill the LM\n                            LM.resize(ndof);\n                            for (int k = 0; k<nseln; ++k) LM[k      ] = sLM[(4+sid)*nseln+k];\n                            for (int k = 0; k<nmeln; ++k) LM[k+nseln] = mLM[(4+sid)*nmeln+k];\n                            \n                            // fill the force array\n                            fe.resize(ndof);\n                            zero(fe);\n                            for (int k = 0; k<nseln; ++k) N[k] = Hs[k];\n                            for (int k = 0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                            \n                            for (int k = 0; k<ndof; ++k) fe[k] += dt*N[k]*jn*detJ[j]*w[j];\n                            \n                            // assemble residual\n                            R.Assemble(en, LM, fe);\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    int i, j, k, l;\n    vector<int> sLM, mLM, LM, en;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    FEElementMatrix ke;\n    int nsol = (int)m_sid.size();\n    vector<double> jn(nsol);\n    \n    FEModel& fem = *GetFEModel();\n     \n    double psf = GetPenaltyScaleFactor();\n    \n    // see how many reformations we've had to do so far\n    int nref = GetSolver()->m_nref;\n    \n    // set higher order stiffness mutliplier\n    // NOTE: this algorithm doesn't really need this\n    // but I've added this functionality to compare with the other contact\n    // algorithms and to see the effect of the different stiffness contributions\n    double knmult = m_knmult;\n    if (m_knmult < 0)\n    {\n        int ni = int(-m_knmult);\n        if (nref >= ni)\n        {\n            knmult = 1;\n            feLog(\"Higher order stiffness terms included.\\n\");\n        }\n        else knmult = 0;\n    }\n    \n    // do single- or two-pass\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np < npass; ++np)\n    {\n        // get the primary and secondary surface\n        FESlidingSurfaceMP& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceMP& ms = (np == 0? m_ms : m_ss);\n        vector<int>& sl = (np == 0? m_ssl : m_msl);\n        \n        // loop over all primary surface elements\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get the next element\n            FESurfaceElement& se = ss.Element(i);\n            \n            bool sporo = ss.m_bporo;\n            \n            // get nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            double pn[MN] = {0};\n            vector< vector<double> >cn(nsol,vector<double>(MN));\n            if (sporo) {\n                for (j=0; j<nseln; ++j)\n                {\n                    pn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n                    for (int isol=0; isol<nsol; ++isol) {\n                        cn[isol][j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofC + m_sid[isol]);\n                    }\n                }\n            }\n            \n            // copy the LM vector\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n            }\n            \n            // loop over all integration points\n            for (j=0; j<nint; ++j)\n            {\n                // get integration point data\n                FEMultiphasicContactPoint& pt = static_cast<FEMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n                \n                // calculate contact traction and account for stick\n                double pn;\n                vec3d t = ContactTraction(ss, i, j, ms, pn);\n                \n                // get the secondary element\n                FESurfaceElement* pme = pt.m_pme;\n                \n                // calculate normal effective solute flux\n                for (int isol=0; isol<nsol; ++isol)\n                {\n                    int l = sl[isol];\n                    double epsc = m_epsc*pt.m_epsc[l]*psf;\n                    jn[isol] = pt.m_Lmc[l] + epsc*pt.m_cg[l];\n                }\n                \n                // normal fluid flux\n                double epsp = m_epsp*pt.m_epsp*psf;\n                double wn = pt.m_Lmp + epsp*pt.m_pg;\n                \n                if (pme)\n                {\n                    FESurfaceElement& me = *pme;\n                    \n                    bool mporo = ms.m_bporo;\n                    \n                    // get the nr of secondary nodes\n                    int nmeln = me.Nodes();\n                    \n                    // nodal data\n                    double pm[MN] = {0};\n                    vector< vector<double> > cm(nsol,vector<double>(MN));\n                    for (k=0; k<nmeln; ++k)\n                    {\n                        pm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n                        for (int isol=0; isol<nsol; ++isol) {\n                            cm[isol][k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofC + m_sid[isol]);\n                        }\n                    }\n                    \n                    // copy the LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    int ndpn;    // number of dofs per node\n                    int ndof;    // number of dofs in stiffness matrix\n                    \n                    if (nsol) {\n                        // calculate dofs for biphasic-solute contact\n                        ndpn = 4+nsol;\n                        ndof = ndpn*(nseln+nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (k=0; k<nseln; ++k)\n                        {\n                            LM[ndpn*k  ] = sLM[3*k  ];            // x-dof\n                            LM[ndpn*k+1] = sLM[3*k+1];            // y-dof\n                            LM[ndpn*k+2] = sLM[3*k+2];            // z-dof\n                            LM[ndpn*k+3] = sLM[3*nseln+k];        // p-dof\n                            for (int isol=0; isol<nsol; ++isol)\n                                LM[ndpn*k+4+isol] = sLM[(4+m_sid[isol])*nseln+k];        // c-dof\n                        }\n                        for (k=0; k<nmeln; ++k)\n                        {\n                            LM[ndpn*(k+nseln)  ] = mLM[3*k  ];            // x-dof\n                            LM[ndpn*(k+nseln)+1] = mLM[3*k+1];            // y-dof\n                            LM[ndpn*(k+nseln)+2] = mLM[3*k+2];            // z-dof\n                            LM[ndpn*(k+nseln)+3] = mLM[3*nmeln+k];        // p-dof\n                            for (int isol=0; isol<nsol; ++isol)\n                                LM[ndpn*(k+nseln)+4+isol] = mLM[(4+m_sid[isol])*nmeln+k];        // c-dof\n                        }\n                    }\n                    \n                    else if (sporo && mporo) {\n                        // calculate dofs for biphasic contact\n                        ndpn = 4;\n                        ndof = ndpn*(nseln+nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (k=0; k<nseln; ++k)\n                        {\n                            LM[ndpn*k  ] = sLM[3*k  ];            // x-dof\n                            LM[ndpn*k+1] = sLM[3*k+1];            // y-dof\n                            LM[ndpn*k+2] = sLM[3*k+2];            // z-dof\n                            LM[ndpn*k+3] = sLM[3*nseln+k];        // p-dof\n                        }\n                        for (k=0; k<nmeln; ++k)\n                        {\n                            LM[ndpn*(k+nseln)  ] = mLM[3*k  ];            // x-dof\n                            LM[ndpn*(k+nseln)+1] = mLM[3*k+1];            // y-dof\n                            LM[ndpn*(k+nseln)+2] = mLM[3*k+2];            // z-dof\n                            LM[ndpn*(k+nseln)+3] = mLM[3*nmeln+k];        // p-dof\n                        }\n                    }\n                    \n                    else {\n                        // calculate dofs for elastic contact\n                        ndpn = 3;\n                        ndof = ndpn*(nseln + nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (k=0; k<nseln; ++k)\n                        {\n                            LM[3*k  ] = sLM[3*k  ];\n                            LM[3*k+1] = sLM[3*k+1];\n                            LM[3*k+2] = sLM[3*k+2];\n                        }\n                        \n                        for (k=0; k<nmeln; ++k)\n                        {\n                            LM[3*(k+nseln)  ] = mLM[3*k  ];\n                            LM[3*(k+nseln)+1] = mLM[3*k+1];\n                            LM[3*(k+nseln)+2] = mLM[3*k+2];\n                        }\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // primary shape functions\n                    Hs = se.H(j);\n                    \n                    // secondary shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // get primary normal vector\n                    vec3d nu = pt.m_nu;\n                    \n                    // gap function\n                    double g = pt.m_gap;\n                    \n                    // penalty\n                    double eps = m_epsn*pt.m_epsn*psf;\n                    \n                    // only evaluate stiffness matrix if contact traction is non-zero\n                    if (pn > 0) {\n                        // if stick\n                        if (pt.m_bstick) {\n                            \n                            // create the stiffness matrix\n                            ke.resize(ndof, ndof); ke.zero();\n                            \n                            // evaluate basis vectors on primary surface\n                            vec3d gscov[2];\n                            ss.CoBaseVectors(se, j, gscov);\n                            \n                            // identity tensor\n                            mat3d I = mat3dd(1);\n                            \n                            // evaluate Mc and Ac and combine them into As\n                            double* Gr = se.Gr(j);\n                            double* Gs = se.Gs(j);\n                            mat3d Ac[MN], As[MN];\n                            mat3d gscovh[2];\n                            gscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n                            for (int k=0; k<nseln; ++k) {\n                                Ac[k] = (gscovh[1]*Gr[k] - gscovh[0]*Gs[k])/detJ[j];\n                                As[k] = t & (Ac[k]*nu);\n                            }\n                            \n                            // --- S O L I D - S O L I D   C O N T A C T ---\n                            \n                            double tmp = detJ[j]*w[j];\n                            for (int a=0; a<nseln; ++a) {\n                                k = a*ndpn;\n                                for (int c=0; c<nseln; ++c) {\n                                    l = c*ndpn;\n                                    mat3d Kac = (I*Hs[a]*Hs[c]*eps - As[c]*Hs[a])*tmp;\n                                    ke[k  ][l  ] += Kac(0,0); ke[k  ][l+1] += Kac(0,1); ke[k  ][l+2] += Kac(0,2);\n                                    ke[k+1][l  ] += Kac(1,0); ke[k+1][l+1] += Kac(1,1); ke[k+1][l+2] += Kac(1,2);\n                                    ke[k+2][l  ] += Kac(2,0); ke[k+2][l+1] += Kac(2,1); ke[k+2][l+2] += Kac(2,2);\n                                }\n                                for (int d=0; d<nmeln; ++d) {\n                                    l = (nseln+d)*ndpn;\n                                    mat3d Kad = I*(-Hs[a]*Hm[d]*eps)*tmp;\n                                    ke[k  ][l  ] += Kad(0,0); ke[k  ][l+1] += Kad(0,1); ke[k  ][l+2] += Kad(0,2);\n                                    ke[k+1][l  ] += Kad(1,0); ke[k+1][l+1] += Kad(1,1); ke[k+1][l+2] += Kad(1,2);\n                                    ke[k+2][l  ] += Kad(2,0); ke[k+2][l+1] += Kad(2,1); ke[k+2][l+2] += Kad(2,2);\n                                }\n                            }\n                            for (int b=0; b<nmeln; ++b) {\n                                k = (nseln+b)*ndpn;\n                                for (int c=0; c<nseln; ++c) {\n                                    l = c*ndpn;\n                                    mat3d Kbc = (I*(-Hm[b]*Hs[c]*eps) + As[c]*Hm[b])*tmp;\n                                    ke[k  ][l  ] += Kbc(0,0); ke[k  ][l+1] += Kbc(0,1); ke[k  ][l+2] += Kbc(0,2);\n                                    ke[k+1][l  ] += Kbc(1,0); ke[k+1][l+1] += Kbc(1,1); ke[k+1][l+2] += Kbc(1,2);\n                                    ke[k+2][l  ] += Kbc(2,0); ke[k+2][l+1] += Kbc(2,1); ke[k+2][l+2] += Kbc(2,2);\n                                }\n                                for (int d=0; d<nmeln; ++d) {\n                                    l = (nseln+d)*ndpn;\n                                    mat3d Kbd = I*Hm[b]*Hm[d]*eps*tmp;\n                                    ke[k  ][l  ] += Kbd(0,0); ke[k  ][l+1] += Kbd(0,1); ke[k  ][l+2] += Kbd(0,2);\n                                    ke[k+1][l  ] += Kbd(1,0); ke[k+1][l+1] += Kbd(1,1); ke[k+1][l+2] += Kbd(1,2);\n                                    ke[k+2][l  ] += Kbd(2,0); ke[k+2][l+1] += Kbd(2,1); ke[k+2][l+2] += Kbd(2,2);\n                                }\n                            }\n                            \n                            // --- M U L T I P H A S I C   S T I F F N E S S ---\n                            if (sporo && mporo)\n                            {\n                                // need to multiply biphasic stiffness entries by the timestep\n                                double dt = fem.GetTime().timeIncrement;\n                                \n                                // --- S O L I D - P R E S S U R E / S O L U T E   C O N T A C T ---\n                                \n                                for (int a=0; a<nseln; ++a) {\n                                    k = a*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        vec3d gac = (Ac[c]*nu)*(-Hs[a]*wn)*tmp*dt;\n                                        ke[k+3][l  ] += gac.x; ke[k+3][l+1] += gac.y; ke[k+3][l+2] += gac.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            vec3d hac = (Ac[c]*nu)*(-Hs[a]*jn[isol])*tmp*dt;\n                                            ke[k+4+isol][l  ] += hac.x; ke[k+4+isol][l+1] += hac.y; ke[k+4+isol][l+2] += hac.z;\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        vec3d gad = vec3d(0,0,0);\n                                        ke[k+3][l  ] += gad.x; ke[k+3][l+1] += gad.y; ke[k+3][l+2] += gad.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            vec3d had = vec3d(0,0,0);\n                                            ke[k+4+isol][l  ] += had.x; ke[k+4+isol][l+1] += had.y; ke[k+4+isol][l+2] += had.z;\n                                        }\n                                    }\n                                }\n                                for (int b=0; b<nmeln; ++b) {\n                                    k = (nseln+b)*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        vec3d gbc = (Ac[c]*nu)*Hm[b]*wn*tmp*dt;\n                                        ke[k+3][l  ] += gbc.x; ke[k+3][l+1] += gbc.y; ke[k+3][l+2] += gbc.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            vec3d hbc = (Ac[c]*nu)*Hm[b]*jn[isol]*tmp*dt;\n                                            ke[k+4+isol][l  ] += hbc.x; ke[k+4+isol][l+1] += hbc.y; ke[k+4+isol][l+2] += hbc.z;\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        vec3d gbd = vec3d(0,0,0);\n                                        ke[k+3][l  ] += gbd.x; ke[k+3][l+1] += gbd.y; ke[k+3][l+2] += gbd.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            vec3d hbd = vec3d(0,0,0);\n                                            ke[k+4+isol][l  ] += hbd.x; ke[k+4+isol][l+1] += hbd.y; ke[k+4+isol][l+2] += hbd.z;\n                                        }\n                                    }\n                                }\n                                \n                                // --- P R E S S U R E - P R E S S U R E  /  C O N C E N T R A T I O N - C O N C E N T R A T I O N  C O N T A C T ---\n                                \n                                for (int a=0; a<nseln; ++a) {\n                                    k = a*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        double gac = (-Hs[a]*Hs[c]*epsp)*tmp*dt;\n                                        ke[k+3][l+3] += gac;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double hac = (-Hs[a]*Hs[c]*epsc*z)*tmp*dt;\n                                                ke[k+4+isol][l+4+jsol] += hac;\n                                            }\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        double gad = (Hs[a]*Hm[d]*epsp)*tmp*dt;\n                                        ke[k+3][l+3] += gad;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double had = (Hs[a]*Hm[d]*epsc*z)*tmp*dt;\n                                                ke[k+4+isol][l+4+jsol] += had;\n                                            }\n                                        }\n                                    }\n                                }\n                                for (int b=0; b<nmeln; ++b) {\n                                    k = (nseln+b)*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        double gbc = (Hm[b]*Hs[c]*epsp)*tmp*dt;\n                                        ke[k+3][l+3] += gbc;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double hbc = (Hm[b]*Hs[c]*epsc*z)*tmp*dt;\n                                                ke[k+4+isol][l+4+jsol] += hbc;\n                                            }\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        double gbd = (-Hm[b]*Hm[d]*epsp)*tmp*dt;\n                                        ke[k+3][l+3] += gbd;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double hbd = (-Hm[b]*Hm[d]*epsc*z)*tmp*dt;\n                                                ke[k+4+isol][l+4+jsol] += hbd;\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                            \n                            // assemble the global stiffness\n                            ke.SetNodes(en);\n                            ke.SetIndices(LM);\n                            LS.Assemble(ke);\n                        }\n                        // if slip\n                        else {\n                            \n                            // create the stiffness matrix\n                            ke.resize(ndof, ndof); ke.zero();\n                            \n                            double tn = -pn;\n                            \n                            // obtain the slip direction s1 and inverse of spatial increment dh\n                            double dh = 0, hd = 0;\n                            vec3d dr(0,0,0);\n                            vec3d s1 = SlipTangent(ss, i, j, ms, dh, dr);\n                            \n                            if (dh != 0) hd = 1.0 / dh;\n                            \n                            // evaluate basis vectors on both surfaces\n                            vec3d gscov[2], gmcov[2];\n                            ss.CoBaseVectors(se, j, gscov);\n                            ms.CoBaseVectors(me, r, s, gmcov);\n                            mat2d A;\n                            A[0][0] = gscov[0]*gmcov[0]; A[0][1] = gscov[0]*gmcov[1];\n                            A[1][0] = gscov[1]*gmcov[0]; A[1][1] = gscov[1]*gmcov[1];\n                            mat2d a = A.inverse();\n                            \n                            // evaluate covariant basis vectors on primary surface at previous time step\n                            vec3d gscovp[2];\n                            ss.CoBaseVectorsP(se, j, gscovp);\n                            \n                            // calculate delta gscov\n                            vec3d dgscov[2];\n                            dgscov[0] = gscov[0] - gscovp[0];\n                            dgscov[1] = gscov[1] - gscovp[1];\n                            \n                            // evaluate approximate contravariant basis vectors when gap != 0\n                            vec3d gscnt[2], gmcnt[2];\n                            gmcnt[0] = gscov[0]*a[0][0] + gscov[1]*a[0][1];\n                            gmcnt[1] = gscov[0]*a[1][0] + gscov[1]*a[1][1];\n                            gscnt[0] = gmcov[0]*a[0][0] + gmcov[1]*a[1][0];\n                            gscnt[1] = gmcov[0]*a[0][1] + gmcov[1]*a[1][1];\n                            \n                            // evaluate N and S tensors and approximations when gap != 0\n                            mat3ds N1 = dyad(nu);\n                            mat3d Pn = mat3dd(1) - (nu & nu);\n                            mat3d Nb1 = mat3dd(1) - (gscov[0] & gscnt[0]) - (gscov[1] & gscnt[1]);\n                            mat3d Nt1 = nu & (Nb1*nu);\n                            mat3d S1 = s1 & nu;\n                            mat3d Ps = (mat3dd(1) - (s1 & s1))*hd;\n                            mat3d St1 = s1 & (Nb1*nu);\n                            \n                            // evaluate frictional contact vectors and tensors\n                            vec3d m = ((dgscov[0] ^ gscov[1]) + (gscov[0] ^ dgscov[1]));\n                            vec3d c1 = Pn*m*(1/detJ[j]);\n                            mat3d Q1 = (mat3dd(1)*(nu * m) + (nu & m))*(1/detJ[j]);\n                            mat3d B = ((Ps*c1) & (Nb1*nu)) - Ps*Pn;\n                            mat3d R = (mat3dd(1)*(nu * dr) + (nu & dr))/(-g);\n                            mat3d L1 = Ps*(Pn*Q1 + R - mat3dd(1))*Pn*(-g);\n                            \n                            // evaluate Ac, Mc, and combine into As\n                            double* Gr = se.Gr(j);\n                            double* Gs = se.Gs(j);\n                            mat3d gscovh[2];\n                            mat3d dgscovh[2];\n                            gscovh[0].skew(gscov[0]); gscovh[1].skew(gscov[1]);\n                            dgscovh[0].skew(dgscov[0]); dgscovh[1].skew(dgscov[1]);\n                            mat3d Ac[MN];\n                            mat3d As[MN];\n                            mat3d Pc[MN];\n                            for (int c=0; c<nseln; ++c){\n                                vec3d mc = gscnt[0]*Gr[c] + gscnt[1]*Gs[c];\n                                mat3d Mc = nu & mc;\n                                Ac[c] = (gscovh[1]*Gr[c] - gscovh[0]*Gs[c])/detJ[j];\n                                mat3d Acb = (dgscovh[1]*Gr[c] - dgscovh[0]*Gs[c])/detJ[j];\n                                vec3d hcp = (N1*mc + Ac[c]*nu);\n                                vec3d hcmb = (N1*mc*m_mu - Ac[c]*nu*pt.m_mueff);\n                                As[c] = Ac[c]+Mc*N1;\n                                mat3d Jc = (L1*Ac[c]) - Ps*Pn*Acb*(-g);\n                                Pc[c] = (s1 & hcmb) + ((Ps*c1) & hcp)*pt.m_mueff*(-g) - Jc*pt.m_mueff;\n                            }\n                            \n                            // evaluate mb and Mb\n                            double Hmr[MN], Hms[MN];\n                            me.shape_deriv(Hmr, Hms, r, s);\n                            vec3d mb[MN];\n                            mat3d Pb[MN];\n                            for (k=0; k<nmeln; ++k) {\n                                mb[k] = gmcnt[0]*Hmr[k] + gmcnt[1]*Hms[k];\n                                Pb[k] = ((-nu) & mb[k]) - (s1 & mb[k])*pt.m_mueff;\n                            }\n                            \n                            // evaluate Gbc\n                            matrix Gbc(nmeln,nseln);\n                            for (int b=0; b<nmeln; ++b) {\n                                for (int c=0; c<nseln; ++c) {\n                                    Gbc(b,c)\n                                    = (a[0][0]*Hmr[b]*Gr[c]\n                                       + a[0][1]*Hmr[b]*Gs[c]\n                                       + a[1][0]*Hms[b]*Gr[c]\n                                       + a[1][1]*Hms[b]*Gs[c])*(-g);\n                                }\n                            }\n                            \n                            // define Tt, T\n                            mat3d Tt = Nt1 + St1*m_mu;\n                            mat3d T = N1 + S1*pt.m_mueff;\n                            \n                            // --- S O L I D - S O L I D   C O N T A C T ---\n                            \n                            // All Kmn terms have the opposite sign from Brandon's notes\n                            // Does FEBio put the negative sign in the stiffness matrix? K*u = f => f - K*u = 0?\n                            \n                            double tmp = detJ[j]*w[j];\n                            for (int a=0; a<nseln; ++a) {\n                                k = a*ndpn;\n                                for (int c=0; c<nseln; ++c) {\n                                    l = c*ndpn;\n                                    mat3d Kac = ((Tt*eps+B*tn*pt.m_mueff)*Hs[a]*Hs[c]+(As[c]+Pc[c])*Hs[a]*tn)*tmp;\n                                    ke[k  ][l  ] += Kac(0,0); ke[k  ][l+1] += Kac(0,1); ke[k  ][l+2] += Kac(0,2);\n                                    ke[k+1][l  ] += Kac(1,0); ke[k+1][l+1] += Kac(1,1); ke[k+1][l+2] += Kac(1,2);\n                                    ke[k+2][l  ] += Kac(2,0); ke[k+2][l+1] += Kac(2,1); ke[k+2][l+2] += Kac(2,2);\n                                }\n                                for (int d=0; d<nmeln; ++d) {\n                                    l = (nseln+d)*ndpn;\n                                    mat3d Kad = (Tt*eps+B*tn*pt.m_mueff)*(-Hs[a]*Hm[d]*tmp);\n                                    ke[k  ][l  ] += Kad(0,0); ke[k  ][l+1] += Kad(0,1); ke[k  ][l+2] += Kad(0,2);\n                                    ke[k+1][l  ] += Kad(1,0); ke[k+1][l+1] += Kad(1,1); ke[k+1][l+2] += Kad(1,2);\n                                    ke[k+2][l  ] += Kad(2,0); ke[k+2][l+1] += Kad(2,1); ke[k+2][l+2] += Kad(2,2);\n                                }\n                            }\n                            for (int b=0; b<nmeln; ++b) {\n                                k = (nseln+b)*ndpn;\n                                for (int c=0; c<nseln; ++c) {\n                                    l = c*ndpn;\n                                    mat3d Kbc = ((Tt*eps+B*tn*pt.m_mueff)*(-Hm[b]*Hs[c])-(As[c]+Pc[c])*(Hm[b]*tn)-Pb[b]*(Hs[c]*tn)-T*(Gbc[b][c]*tn))*tmp;\n                                    ke[k  ][l  ] += Kbc(0,0); ke[k  ][l+1] += Kbc(0,1); ke[k  ][l+2] += Kbc(0,2);\n                                    ke[k+1][l  ] += Kbc(1,0); ke[k+1][l+1] += Kbc(1,1); ke[k+1][l+2] += Kbc(1,2);\n                                    ke[k+2][l  ] += Kbc(2,0); ke[k+2][l+1] += Kbc(2,1); ke[k+2][l+2] += Kbc(2,2);\n                                }\n                                for (int d=0; d<nmeln; ++d) {\n                                    l = (nseln+d)*ndpn;\n                                    mat3d Kbd = ((Tt*eps+B*tn*pt.m_mueff)*Hm[b]*Hm[d]+Pb[b]*Hm[d]*tn)*tmp;\n                                    ke[k  ][l  ] += Kbd(0,0); ke[k  ][l+1] += Kbd(0,1); ke[k  ][l+2] += Kbd(0,2);\n                                    ke[k+1][l  ] += Kbd(1,0); ke[k+1][l+1] += Kbd(1,1); ke[k+1][l+2] += Kbd(1,2);\n                                    ke[k+2][l  ] += Kbd(2,0); ke[k+2][l+1] += Kbd(2,1); ke[k+2][l+2] += Kbd(2,2);\n                                }\n                            }\n                            \n                            // --- M U L T I P H A S I C   S T I F F N E S S ---\n                            if (sporo && mporo)\n                            {\n                                double dt = fem.GetTime().timeIncrement;\n                                \n                                double epsp = m_epsp*pt.m_epsp*psf;\n                                \n                                // p vector (gradients of effective solute concentrations on master surface)\n                                double dpmr = me.eval_deriv1(pm, r, s);\n                                double dpms = me.eval_deriv2(pm, r, s);\n                                vec3d p = gmcnt[0]*dpmr + gmcnt[1]*dpms;\n                                \n                                // evaluate Pc\n                                double Pc[MN];\n                                for (int k=0; k<nseln; ++k) {\n                                    Pc[k] = (a[0][0]*dpmr*Gr[k]\n                                             + a[0][1]*dpmr*Gs[k]\n                                             + a[1][0]*dpms*Gr[k]\n                                             + a[1][1]*dpms*Gs[k])*(-g);\n                                }\n                                \n                                // q vectors (gradients of effective solute concentrations on master surface)\n                                // Cc scalars\n                                vector<vec3d> q(nsol);\n                                vector< vector<double> > Cc(nsol, vector<double>(MN));\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double dcmr = me.eval_deriv1(&cm[isol][0], r, s);\n                                    double dcms = me.eval_deriv2(&cm[isol][0], r, s);\n                                    q[isol] = gmcnt[0]*dcmr + gmcnt[1]*dcms;\n                                    for (int k=0; k<nseln; ++k) {\n                                        Cc[isol][k] = (a[0][0]*dcmr*Gr[k]\n                                                       + a[0][1]*dcmr*Gs[k]\n                                                       + a[1][0]*dcms*Gr[k]\n                                                       + a[1][1]*dcms*Gs[k])*(-g);\n                                    }\n                                }\n                                \n                                // --- S O L I D - P R E S S U R E / S O L U T E   C O N T A C T ---\n                                \n                                for (int a=0; a<nseln; ++a) {\n                                    k = a*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        vec3d gac = (p*Hs[a]*Hs[c]*epsp-((Ac[c]*nu)*wn+nu*epsp*Pc[c])*Hs[a])*tmp*dt;\n                                        ke[k+3][l  ] += gac.x; ke[k+3][l+1] += gac.y; ke[k+3][l+2] += gac.z;\n                                        vec3d kac = (s1*m_mu*(1.0-m_phi))*(-Hs[a]*Hs[c])*tmp*dt;\n                                        ke[k  ][l+3] += kac.x; ke[k+1][l+3] += kac.y; ke[k+2][l+3] += kac.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                            vec3d hac = (q[isol]*Hs[a]*Hs[c]*epsc-((Ac[c]*nu)*jn[isol]+nu*Cc[isol][c]*epsc)*Hs[a])*tmp*dt;\n                                            ke[k+4+isol][l  ] += hac.x; ke[k+4+isol][l+1] += hac.y; ke[k+4+isol][l+2] += hac.z;\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        vec3d gad = p*(-Hs[a]*Hm[d]*epsp)*tmp*dt;\n                                        ke[k+3][l  ] += gad.x; ke[k+3][l+1] += gad.y; ke[k+3][l+2] += gad.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                            vec3d had = q[isol]*(-Hs[a]*Hm[d]*epsc)*tmp*dt;\n                                            ke[k+4+isol][l  ] += had.x; ke[k+4+isol][l+1] += had.y; ke[k+4+isol][l+2] += had.z;\n                                        }\n                                    }\n                                }\n                                for (int b=0; b<nmeln; ++b) {\n                                    k = (nseln+b)*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        vec3d gbc = (p*(-Hm[b]*Hs[c]*epsp)+((Ac[c]*nu)*wn + nu*Pc[c]*epsp)*Hm[b] + (mb[b]*wn*Hs[c]) - nu*Gbc[b][c]*wn)*tmp*dt;\n                                        ke[k+3][l  ] += gbc.x; ke[k+3][l+1] += gbc.y; ke[k+3][l+2] += gbc.z;\n                                        vec3d kbc = (s1*m_mu*(1.0-m_phi))*(Hm[b]*Hs[c])*tmp*dt;\n                                        ke[k  ][l+3] += kbc.x; ke[k+1][l+3] += kbc.y; ke[k+2][l+3] += kbc.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                            vec3d hbc = (q[isol]*(-Hm[b]*Hs[c]*epsc)+((Ac[c]*nu)*jn[isol]+nu*epsc*Cc[isol][c])*Hm[b]+(mb[b]*jn[isol]*Hs[c])-(nu*Gbc[b][c]*jn[isol]))*tmp*dt;\n                                            ke[k+4+isol][l  ] += hbc.x; ke[k+4+isol][l+1] += hbc.y; ke[k+4+isol][l+2] += hbc.z;\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        vec3d gbd = (p*(Hm[b]*Hm[d]*epsp)-(mb[b]*wn*Hm[d]))*tmp*dt;\n                                        ke[k+3][l  ] += gbd.x; ke[k+3][l+1] += gbd.y; ke[k+3][l+2] += gbd.z;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                            vec3d hbd = (q[isol]*(Hm[b]*Hm[d]*epsc)-(mb[b]*jn[isol]*Hm[d]))*tmp*dt;\n                                            ke[k+4+isol][l  ] += hbd.x; ke[k+4+isol][l+1] += hbd.y; ke[k+4+isol][l+2] += hbd.z;\n                                        }\n                                    }\n                                }\n                                \n                                // --- P R E S S U R E - P R E S S U R E  /  C O N C E N T R A T I O N - C O N C E N T R A T I O N  C O N T A C T ---\n                                \n                                for (int a=0; a<nseln; ++a) {\n                                    k = a*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        double gac = (-Hs[a]*Hs[c]*epsp)*tmp*dt;\n                                        ke[k+3][l+3] += gac;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double hac = (-Hs[a]*Hs[c]*epsc*z)*(dt*detJ[j]*w[j]);\n                                                ke[k+4+isol][l+4+jsol] += hac;\n                                            }\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        double gad = (Hs[a]*Hm[d]*epsp)*(dt*detJ[j]*w[j]);\n                                        ke[k+3][l+3] += gad;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double had = (Hs[a]*Hm[d]*epsc*z)*(dt*detJ[j]*w[j]);\n                                                ke[k+4+isol][l+4+jsol] += had;\n                                            }\n                                        }\n                                    }\n                                }\n                                for (int b=0; b<nmeln; ++b) {\n                                    k = (nseln+b)*ndpn;\n                                    for (int c=0; c<nseln; ++c) {\n                                        l = c*ndpn;\n                                        double gbc = (Hm[b]*Hs[c]*epsp)*(dt*detJ[j]*w[j]);\n                                        ke[k+3][l+3] += gbc;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double hbc = (Hm[b]*Hs[c]*epsc*z)*(dt*detJ[j]*w[j]);\n                                                ke[k+4+isol][l+4+jsol] += hbc;\n                                            }\n                                        }\n                                    }\n                                    for (int d=0; d<nmeln; ++d) {\n                                        l = (nseln+d)*ndpn;\n                                        double gbd = (-Hm[b]*Hm[d]*epsp)*(dt*detJ[j]*w[j]);\n                                        ke[k+3][l+3] += gbd;\n                                        for (int isol=0; isol<nsol; ++isol) {\n                                            for (int jsol=0; jsol<nsol; ++jsol) {\n                                                int z = (isol == jsol? 1.0 : 0.0);\n                                                double epsc = m_epsc*pt.m_epsc[sl[isol]]*psf;\n                                                double hbd = (-Hm[b]*Hm[d]*epsc*z)*(dt*detJ[j]*w[j]);\n                                                ke[k+4+isol][l+4+jsol] += hbd;\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                            // assemble the global stiffness\n                            ke.SetNodes(en);\n                            ke.SetIndices(LM);\n                            LS.Assemble(ke);\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::UpdateContactPressures()\n{\n    int np, n, i, j;\n    const int MN = FEElement::MAX_NODES;\n    const int MI = FEElement::MAX_INTPOINTS;\n    double psf = GetPenaltyScaleFactor();\n    int npass = (m_btwo_pass?2:1);\n    for (np=0; np<npass; ++np)\n    {\n        FESlidingSurfaceMP& ss = (np == 0? m_ss : m_ms);\n        FESlidingSurfaceMP& ms = (np == 0? m_ms : m_ss);\n        \n        // loop over all elements of the primary surface\n        for (n=0; n<ss.Elements(); ++n)\n        {\n            FESurfaceElement& el = ss.Element(n);\n            int nint = el.GaussPoints();\n            \n            // get the normal tractions at the integration points\n            for (i=0; i<nint; ++i)\n            {\n                // get integration point data\n                FEMultiphasicContactPoint& sd = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(i));\n                // evaluate traction on primary surface\n                double eps = m_epsn*sd.m_epsn*psf;\n                if (sd.m_bstick) {\n                    // if stick, evaluate total traction\n                    sd.m_tr = sd.m_Lmt + sd.m_dg*eps;\n                    // then derive normal component\n                    sd.m_Ln = -sd.m_tr*sd.m_nu;\n                }\n                else {\n                    // if slip, evaluate normal traction\n                    double Ln = sd.m_Lmd + eps*sd.m_gap;\n                    sd.m_Ln = MBRACKET(Ln);\n                    // then derive total traction\n                    sd.m_tr = -(sd.m_nu*sd.m_Ln + sd.m_s1*sd.m_Ln*sd.m_mueff);\n                }\n                \n                FESurfaceElement* pme = sd.m_pme;\n                \n                if (m_btwo_pass && pme)\n                {\n                    int mint = pme->GaussPoints();\n                    vec3d ti[MI];\n                    double pi[MI];\n                    for (j=0; j<mint; ++j) {\n                        FEMultiphasicContactPoint& md = static_cast<FEMultiphasicContactPoint&>(*pme->GetMaterialPoint(j));\n                        \n                        // evaluate traction on secondary surface\n                        double eps = m_epsn*md.m_epsn*psf;\n                        if (md.m_bstick) {\n                            // if stick, evaluate total traction\n                            ti[j] = md.m_Lmt + md.m_dg*eps;\n                            // then derive normal component\n                            pi[j] = -ti[j]*md.m_nu;\n                        }\n                        else {\n                            // if slip, evaluate normal traction\n                            double Ln = md.m_Lmd + eps*md.m_gap;\n                            pi[j] = MBRACKET(Ln);\n                            // then derive total traction\n                            ti[j] = -(md.m_nu + md.m_s1*md.m_mueff)*pi[j];\n                        }\n                    }\n                    // project the data to the nodes\n                    vec3d tn[MN];\n                    double pn[MN];\n                    pme->FEElement::project_to_nodes(pi, pn);\n                    pme->project_to_nodes(ti, tn);\n                    // now evaluate the traction at the intersection point\n                    double Ln = pme->eval(pn, sd.m_rs[0], sd.m_rs[1]);\n                    vec3d trac = pme->eval(tn, sd.m_rs[0], sd.m_rs[1]);\n                    sd.m_Ln += MBRACKET(Ln);\n                    // tractions on secondary-primary are opposite, so subtract\n                    sd.m_tr -= trac;\n                }\n            }\n        }\n        ss.EvaluateNodalContactPressures();\n        ss.EvaluateNodalContactTractions();\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESlidingInterfaceMP::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n    if (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    double Ln, Lp;\n    int nsol = (int)m_sid.size();\n    vector<double>Lc(nsol);\n    bool bconv = true;\n    \n    double psf = GetPenaltyScaleFactor();\n    \n    bool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n    bool bsolu = (m_ss.m_bsolu && m_ms.m_bsolu);\n\n    int NS = m_ss.Elements();\n    int NM = m_ms.Elements();\n    \n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0, normP = 0, normDP = 0, normC = 0;\n    vector<double>normDC(nsol,0);\n    for (int i=0; i<NS; ++i)\n    {\n        FESurfaceElement& el = m_ss.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEMultiphasicContactPoint& ds = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            if (ds.m_bstick)\n                normL0 += ds.m_Lmt*ds.m_Lmt;\n            else\n                normL0 += ds.m_Lmd*ds.m_Lmd;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n        FESurfaceElement& el = m_ms.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEMultiphasicContactPoint& dm = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            if (dm.m_bstick)\n                normL0 += dm.m_Lmt*dm.m_Lmt;\n            else\n                normL0 += dm.m_Lmd*dm.m_Lmd;\n        }\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0, maxpg = 0;\n    vector<double> maxcg(nsol,0);\n    \n    // update Lagrange multipliers\n    double normL1 = 0, eps, epsp, epsc;\n    for (int i=0; i<m_ss.Elements(); ++i) {\n        FESurfaceElement& el = m_ss.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ss.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FEMultiphasicContactPoint& ds = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            \n            // update Lagrange multipliers on primary surface\n            eps = m_epsn*ds.m_epsn*psf;\n            if (ds.m_bstick) {\n                // if stick, augment total traction\n                if (m_bsmaug) {\n                    // replace this multiplier with a smoother version\n                    ds.m_Lmt = tn[j];\n                    if (m_btwo_pass) ds.m_Lmt /= 2;\n                }\n                else {\n                    ds.m_Lmt += ds.m_dg*eps;\n                }\n                // then derive normal component\n                ds.m_Lmd = -ds.m_Lmt*ds.m_nu;\n                Ln = ds.m_Lmd;\n                normL1 += ds.m_Lmt*ds.m_Lmt;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(ds.m_dg.norm()));\n            }\n            else {\n                // if slip, augment normal traction\n                if (m_bsmaug) {\n                    // replace this multiplier with a smoother version\n                    Ln = -(tn[j]*ds.m_nu);\n                    ds.m_Lmd = MBRACKET(Ln);\n                    if (m_btwo_pass) ds.m_Lmd /= 2;\n                }\n                else {\n                    Ln = ds.m_Lmd + eps*ds.m_gap;\n                    ds.m_Lmd = MBRACKET(Ln);\n                }\n                // then derive total traction\n                double mueff = m_mu*(1.0-(1.0-m_phi)*(ds.m_p1-m_ambp)/ds.m_Lmd);\n                mueff = MBRACKET(mueff);\n                ds.m_Lmt = -(ds.m_nu*ds.m_Lmd + ds.m_s1*ds.m_Lmd*mueff);\n                normL1 += ds.m_Lmd*ds.m_Lmd;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(ds.m_gap));\n            }\n            \n            if (m_ss.m_bporo) {\n                Lp = 0;\n                Lc.assign(nsol, 0);\n                if (Ln > 0) {\n                    epsp = m_epsp*ds.m_epsp*psf;\n                    Lp = ds.m_Lmp + epsp*ds.m_pg;\n                    maxpg = max(maxpg,fabs(ds.m_pg));\n                    normDP += ds.m_pg*ds.m_pg;\n                    for (int isol=0; isol<nsol; ++isol) {\n                        int l = m_ssl[isol];\n                        epsc = m_epsc*ds.m_epsc[l]*psf;\n                        Lc[isol] = ds.m_Lmc[l] + epsc*ds.m_cg[l];\n                        maxcg[isol] = max(maxcg[isol],fabs(ds.m_cg[l]));\n                        normDC[isol] += ds.m_cg[l]*ds.m_cg[l];\n                    }\n                }\n                ds.m_Lmp = Lp;\n                for (int isol=0; isol<nsol; ++isol) ds.m_Lmc[m_ssl[isol]] = Lc[isol];\n            }\n        }\n    }\n    \n    for (int i=0; i<m_ms.Elements(); ++i) {\n        FESurfaceElement& el = m_ms.Element(i);\n        vec3d tn[FEElement::MAX_INTPOINTS];\n        if (m_bsmaug) m_ms.GetGPSurfaceTraction(i, tn);\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMultiphasicContactPoint& dm = static_cast<FEMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n            \n            // update Lagrange multipliers on master surface\n            double eps = m_epsn*dm.m_epsn*psf;\n            if (dm.m_bstick) {\n                // if stick, augment total traction\n                if (m_bsmaug) {\n                    // replace this multiplier with a smoother version\n                    dm.m_Lmt = tn[j];\n                    if (m_btwo_pass) dm.m_Lmt /= 2;\n                }\n                else {\n                    dm.m_Lmt += dm.m_dg*eps;\n                }\n                // then derive normal component\n                dm.m_Lmd = -dm.m_Lmt*dm.m_nu;\n                Ln = dm.m_Lmd;\n                normL1 += dm.m_Lmt*dm.m_Lmt;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(dm.m_dg.norm()));\n            }\n            else {\n                // if slip, augment normal traction\n                if (m_bsmaug) {\n                    // replace this multiplier with a smoother version\n                    Ln = -(tn[j]*dm.m_nu);\n                    dm.m_Lmd = MBRACKET(Ln);\n                    if (m_btwo_pass) dm.m_Lmd /= 2;\n                }\n                else {\n                    Ln = dm.m_Lmd + eps*dm.m_gap;\n                    dm.m_Lmd = MBRACKET(Ln);\n                }\n                // then derive total traction\n                double mueff = m_mu*(1.0-(1.0-m_phi)*(dm.m_p1-m_ambp)/dm.m_Lmd);\n                mueff = MBRACKET(mueff);\n                dm.m_Lmt = -(dm.m_nu*dm.m_Lmd + dm.m_s1*dm.m_Lmd*mueff);\n                normL1 += dm.m_Lmd*dm.m_Lmd;\n                \n                if (Ln > 0) maxgap = max(maxgap, fabs(dm.m_gap));\n            }\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                Lc.assign(nsol, 0);\n                if (Ln > 0) {\n                    epsp = m_epsp*dm.m_epsp*psf;\n                    Lp = dm.m_Lmp + epsp*dm.m_pg;\n                    maxpg = max(maxpg,fabs(dm.m_pg));\n                    normDP += dm.m_pg*dm.m_pg;\n                    for (int isol=0; isol<nsol; ++isol) {\n                        int l = m_ssl[isol];\n                        epsc = m_epsc*dm.m_epsc[l]*psf;\n                        Lc[isol] = dm.m_Lmc[l] + epsc*dm.m_cg[l];\n                        maxcg[isol] = max(maxcg[isol],fabs(dm.m_cg[l]));\n                        normDC[isol] += dm.m_cg[l]*dm.m_cg[l];\n                    }\n                }\n                dm.m_Lmp = Lp;\n                for (int isol=0; isol<nsol; ++isol) dm.m_Lmc[m_ssl[isol]] = Lc[isol];\n            }\n        }\n    }\n    \n    // normP should be a measure of the fluid pressure at the\n    // contact interface.  However, since it could be zero,\n    // use an average measure of the contact traction instead.\n    normP = normL1;\n    normC = normL1/(m_Rgas*m_Tabs);\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0));\n    double pnorm = (normP != 0 ? (normDP/normP) : normDP);\n    vector<double> cnorm(nsol);\n    for (int isol=0; isol<nsol; ++isol)\n        cnorm[isol] = (normC != 0 ? (normDC[isol]/normC) : normDC[isol]);\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    if ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n    for (int isol=0; isol<nsol; ++isol)\n        if ((m_ctol > 0) && (bsolu && maxcg[isol] > m_ctol)) bconv = false;\n    \n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    if ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n    for (int isol=0; isol<nsol; ++isol)\n        if ((m_atol > 0) && (cnorm[isol] > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" sliding interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    D multiplier : %15le\", lnorm);\n    if (m_atol > 0) feLog(\"%15le\\n\", m_atol);\n    else feLog(\"       ***\\n\");\n    if (bporo) { feLog(\"    P gap       : %15le\", pnorm);\n        if (m_atol > 0) feLog(\"%15le\\n\", m_atol);\n        else feLog(\"       ***\\n\");\n    }\n    for (int isol=0; isol<nsol; ++isol) {\n        feLog(\"    C[%d] gap   : %15le\", m_sid[isol], cnorm[isol]);\n        if (m_atol > 0) feLog(\"%15le\\n\", m_atol);\n        else feLog(\"       ***\\n\");\n    }\n    \n    feLog(\"    maximum gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n    if (bporo) {\n        feLog(\"    maximum pgap : %15le\", maxpg);\n        if (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n    }\n    for (int isol=0; isol<nsol; ++isol) {\n        feLog(\"    maximum cgap[%d] : %15le\", m_sid[isol], maxcg[isol]);\n        if (m_ctol > 0) feLog(\"%15le\\n\", m_ctol); else feLog(\"       ***\\n\");\n    }\n    \n    ProjectSurface(m_ss, m_ms, true);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss, true);\n    \n    m_bfreeze = true;\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESlidingInterfaceMP::Serialize(DumpStream &ar)\n{\n    // serialize contact data\n    FEContactInterface::Serialize(ar);\n    \n    // serialize contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n    \n    \n    ar & m_ambp;\n    ar & m_ambc;\n    ar & m_sid;\n    ar & m_ssl;\n    ar & m_msl;\n    ar & m_sz;\n    \n    // serialize element pointers\n    SerializeElementPointers(m_ss, m_ms, ar);\n    SerializeElementPointers(m_ms, m_ss, ar);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceMP::MarkAmbient()\n{\t\n\tint i, j, id, np;\n\t\n    // get number of DOFS\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\t// Mark all nodes as free-draining.  This needs to be done for ALL\n\t// contact interfaces prior to executing Update(), where nodes that are\n\t// in contact are subsequently marked as non free-draining.  This ensures\n\t// that for surfaces involved in more than one contact interface, nodes\n\t// that have been marked as non free-draining are not reset to \n\t// free-draining.\n\tfor (np=0; np<2; ++np)\n\t{\n\t\tFESlidingSurfaceMP& s = (np == 0? m_ss : m_ms);\n\t\t\n\t\tif (s.m_bporo) {\n\t\t\t// first, mark all nodes as free-draining (= neg. ID)\n\t\t\t// this is done by setting the dof's equation number\n\t\t\t// to a negative number\n\t\t\tfor (i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tid = s.Node(i).m_ID[m_dofP];\n\t\t\t\tif (id >= 0) \n\t\t\t\t{\n\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t// mark node as free-draining\n\t\t\t\t\tnode.m_ID[m_dofP] = -id-2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (s.m_bsolu) {\n\t\t\t// first, mark all nodes as free-draining (= neg. ID)\n\t\t\t// this is done by setting the dof's equation number\n\t\t\t// to a negative number\n\t\t\tfor (i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tfor (j=0; j<MAX_CDOFS; ++j) {\n\t\t\t\t\tid = s.Node(i).m_ID[m_dofC+j];\n\t\t\t\t\tif (id >= 0) \n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t\t// mark node as free-draining\n\t\t\t\t\t\tnode.m_ID[m_dofC+j] = -id-2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESlidingInterfaceMP::SetAmbient()\n{\t\n\tint i, j, np;\n\t\n    // get number of DOFS\n    DOFS& fedofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n    \n\t// Set the pressure to zero for the free-draining nodes\n\tfor (np=0; np<2; ++np)\n\t{\n\t\tFESlidingSurfaceMP& s = (np == 0? m_ss : m_ms);\n\t\t\n\t\tif (s.m_bporo) {\n\t\t\t// loop over all nodes\n\t\t\tfor (i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tif (s.Node(i).m_ID[m_dofP] < -1)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t// set the fluid pressure to ambient condition\n\t\t\t\t\tnode.set(m_dofP, m_ambp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (s.m_bsolu) {\n\t\t\t// loop over all nodes\n\t\t\tfor (i=0; i<s.Nodes(); ++i) \n\t\t\t{\n\t\t\t\tfor (j=0; j<MAX_CDOFS; ++j) {\n\t\t\t\t\tif (s.Node(i).m_ID[m_dofC+j] < -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = s.Node(i);\n\t\t\t\t\t\t// set the fluid pressure to ambient condition\n\t\t\t\t\t\tnode.set(m_dofC + j, m_ambc[j]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFESoluteData* FESlidingInterfaceMP::FindSoluteData(int nid)\n{\n    FEModel& fem = *GetFEModel();\n    int N = GetFEModel()->GlobalDataItems();\n    for (int i=0; i<N; ++i)\n    {\n        FESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n        if (psd && (psd->GetID() == nid)) return psd;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "FEBioMix/FESlidingInterfaceMP.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBiphasicContactSurface.h\"\n#include \"FESolute.h\"\n#include <FECore/FECoreClass.h>\n#include <map>\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FEMultiphasicContactPoint : public FEBiphasicContactPoint\n{\npublic:\n    vector<double>  m_Lmc;  //!< Lagrange multipliers for solute concentrations\n    vector<double>  m_epsc; //!< concentration penalty factors\n    vector<double>  m_cg;   //!< concentration \"gap\"\n    vector<double>  m_c1;   //!< solute concentration\n\n    void Init() override\n    {\n        FEBiphasicContactPoint::Init();\n    }\n    \n    void Serialize(DumpStream& ar) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingSurfaceMP : public FEBiphasicContactSurface\n{\npublic:\n\t//! constructor\n\tFESlidingSurfaceMP(FEModel* pfem);\n\t\n\t//! destructor\n\t~FESlidingSurfaceMP();\n\t\n\t//! initialization\n\tbool Init() override;\n\t\n    //! initialize sliding surface and store previous values\n    void InitSlidingSurface();\n    \n\t//! evaluate net contact force\n\tvec3d GetContactForce() override;\n\t\n\t//! evaluate net contact area\n\tdouble GetContactArea() override;\n    \n\t//! evaluate net fluid force\n\tvec3d GetFluidForce() override;\n\t\n\t//! calculate the nodal normals\n\tvoid UpdateNodeNormals();\n\t\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\tvoid SetPoroMode(bool bporo) { m_bporo = bporo; }\n\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\t\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\npublic:\n    void GetVectorGap           (int nface, vec3d& pg) override;\n    void GetContactTraction     (int nface, vec3d& pt) override;\n    void GetSlipTangent         (int nface, vec3d& pt);\n    void GetMuEffective         (int nface, double& pg) override;\n    void GetLocalFLS            (int nface, double& pg) override;\n    void GetNodalVectorGap      (int nface, vec3d* pg) override;\n    void GetNodalContactPressure(int nface, double* pg) override;\n    void GetNodalContactTraction(int nface, vec3d* tn) override;\n    void GetStickStatus         (int nface, double& pg) override;\n    void EvaluateNodalContactPressures();\n    void EvaluateNodalContactTractions();\n\nprivate:\n\tvoid GetContactPressure(int nface, double& pg);\n\t\npublic:\n\tbool\t\t\t\t\t\tm_bporo;\t//!< set poro-mode\n\tbool\t\t\t\t\t\tm_bsolu;\t//!< set solute-mode\n\t\n\tvector<vec3d>\t\t\t\tm_nn;\t//!< node normals\n    vector<double>              m_pn;   //!< nodal contact pressures\n    vector<vec3d>               m_tn;   //!< nodal contact tractions\n\n\tvector<int>\t\t\t\t\tm_sid;\t//!< list of solute id's for this surface\n    \n    vec3d\tm_Ft;                       //!< total contact force (from equivalent nodal forces)\n\nprotected:\n\tint\tm_dofC;\n};\n\n//-----------------------------------------------------------------------------\n// helper class for reading ambient concentrations\nclass FEAmbientConcentration : public FECoreClass\n{\npublic:\n\tFEAmbientConcentration(FEModel* fem);\n\npublic:\n\tint\t\t\tm_sol;\n\tdouble\t\tm_ambc;\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FEAmbientConcentration);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESlidingInterfaceMP : public FEContactInterface\n{\n\npublic:\n\t//! constructor\n\tFESlidingInterfaceMP(FEModel* pfem);\n\t\n\t//! destructor\n\t~FESlidingInterfaceMP();\n\t\n\t//! initialization\n\tbool Init() override;\n\t\n\t//! interface activation\n\tvoid Activate() override;\n\n    //! calculate the slip direction on the primary surface\n    vec3d SlipTangent(FESlidingSurfaceMP& ss, const int nel, const int nint, FESlidingSurfaceMP& ms, double& dh, vec3d& r);\n\n    //! calculate contact traction\n    vec3d ContactTraction(FESlidingSurfaceMP& ss, const int nel, const int n, FESlidingSurfaceMP& ms, double& pn);\n    \n\t//! calculate contact pressures for file output\n\tvoid UpdateContactPressures();\n\t\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\t//! mark ambient condition \n\tvoid MarkAmbient();\n\t\n\t//! set ambient condition \n\tvoid SetAmbient();\n\t\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return false; }\n    \n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n \n\t//! get solute data\n    FESoluteData* FindSoluteData(int nid);\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FESlidingSurfaceMP& ss, FESlidingSurfaceMP& ms, bool bupseg, bool bmove = false);\n\t\n\t//! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n\tvoid CalcAutoPenalty(FESlidingSurfaceMP& s);\n\t\n\tvoid CalcAutoPressurePenalty(FESlidingSurfaceMP& s);\n\tdouble AutoPressurePenalty(FESurfaceElement& el, FESlidingSurfaceMP& s);\n\t\n\tvoid CalcAutoConcentrationPenalty(FESlidingSurfaceMP& s, const int isol);\n\tdouble AutoConcentrationPenalty(FESurfaceElement& el, FESlidingSurfaceMP& s, const int isol);\n    \n    double AutoPenalty(FESurfaceElement& el, FESurface &s);\n\t\npublic:\n\tFESlidingSurfaceMP\tm_ss;\t//!< primary surface\n\tFESlidingSurfaceMP\tm_ms;\t//!< secondary surface\n\t\n\tint\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n\tbool\t\t\tm_btwo_pass;\t//!< two-pass flag\n\tdouble\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\t\t\tm_gtol;\t\t\t//!< gap tolerance\n\tdouble\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n\tdouble\t\t\tm_ctol;\t\t\t//!< concentration gap tolerance\n\tdouble\t\t\tm_stol;\t\t\t//!< search tolerance\n\tbool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n\tdouble\t\t\tm_srad;\t\t\t//!< contact search radius\n\tint\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n\tint\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n\tint\t\t\t\tm_nsegup;\t\t//!< segment update parameter\n    bool\t\t\tm_breloc;\t\t//!< node relocation on startup\n    bool            m_bsmaug;       //!< smooth augmentation\n    bool            m_bsmfls;       //!< smooth local fluid load support\n\n\tdouble\t\t\tm_epsn;         //!< normal penalty factor\n\tbool\t\t\tm_bautopen;     //!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n    \n    double          m_mu;           //!< friction coefficient\n    bool            m_bfreeze;      //!< freeze stick/slip status\n\n\t// multiphasic contact parameters\n    double  m_phi;                  //!< solid-solid contact fraction\n\tdouble\tm_epsp;\t\t\t\t\t//!< fluid volumetric flow rate penalty\n\tdouble\tm_epsc;\t\t\t\t\t//!< solute molar flow rate penalty\n\tdouble\tm_Rgas;\t\t\t\t\t//!< universal gas constant\n\tdouble\tm_Tabs;\t\t\t\t\t//!< absolute temperature\n\tdouble\tm_ambp;\t\t\t\t\t//!< ambient pressure\n\tvector<double>\tm_ambc;         //!< ambient concentration\n\n\tvector<FEAmbientConcentration*>\tm_ambctmp;\n\tvector<int> m_sid;\t\t\t\t//!< list of solute ids common to both contact surfaces\n\tvector<int> m_ssl;\t\t\t\t//!< list of primary surface solutes common to both contact surfaces\n\tvector<int> m_msl;\t\t\t\t//!< list of secondary surface solutes common to both contact surfaces\n    vector<int> m_sz;               //!< charge number of solutes common to both contact surfaces\n\nprotected:\n\tint\tm_dofP;\n\tint\tm_dofC;\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESolubConst.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolubConst.h\"\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FESolubConst, FESoluteSolubility)\n\tADD_PARAMETER(m_solub, FE_RANGE_GREATER_OR_EQUAL(0.0), \"solub\")->setLongName(\"solubility\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFESolubConst::FESolubConst(FEModel* pfem) : FESoluteSolubility(pfem)\n{\n\tm_solub = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Solubility\ndouble FESolubConst::Solubility(FEMaterialPoint& mp)\n{\n\t// --- constant solubility ---\n\t\n\treturn m_solub;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solubility with respect to strain\ndouble FESolubConst::Tangent_Solubility_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solubility with respect to concentration\ndouble FESolubConst::Tangent_Solubility_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Cross derivative of solubility with respect to strain and concentration\ndouble FESolubConst::Tangent_Solubility_Strain_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Second derivative of solubility with respect to strain\ndouble FESolubConst::Tangent_Solubility_Strain_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Second derivative of solubility with respect to concentration\ndouble FESolubConst::Tangent_Solubility_Concentration_Concentration(FEMaterialPoint &mp, const int isol, const int jsol)\n{\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioMix/FESolubConst.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a constant solute solubility\n\nclass FEBIOMIX_API FESolubConst : public FESoluteSolubility\n{\npublic:\n\t//! constructor\n\tFESolubConst(FEModel* pfem);\n\t\n\t//! solubility\n\tdouble Solubility(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of solubility with respect to strain\n\tdouble Tangent_Solubility_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of solubility with respect to concentration\n\tdouble Tangent_Solubility_Concentration(FEMaterialPoint& mp, const int isol) override;\n\t\n\t//! Cross derivative of solubility with respect to strain and concentration\n\tdouble Tangent_Solubility_Strain_Concentration(FEMaterialPoint& mp, const int isol) override;\n\t\n\t//! Second derivative of solubility with respect to strain\n\tdouble Tangent_Solubility_Strain_Strain(FEMaterialPoint& mp) override;\n\n\t//! Second derivative of solubility with respect to concentration\n\tdouble Tangent_Solubility_Concentration_Concentration(FEMaterialPoint& mp, const int isol, const int jsol) override;\n\npublic:\n\tdouble\tm_solub;\t\t\t//!< solubility\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESolubManning.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolubManning.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FESolubManning, FESoluteSolubility)\n    ADD_PARAMETER(m_ksi  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"ksi\"  );\n    ADD_PARAMETER(m_sol  , \"co_ion\")->setEnums(\"$(solutes)\");\n    ADD_PROPERTY(m_solub, \"solub\" );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor.\nFESolubManning::FESolubManning(FEModel* pfem) : FESoluteSolubility(pfem)\n{\n    m_ksi = 1;\n    m_sol = -1;\n    m_lsol = -1;\n    m_bcoi = false;\n\tm_solub = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FESolubManning::Init()\n{\n    if (FESoluteSolubility::Init() == false) return false;\n    \n    // get the parent which must be a solute material\n    FESolute* m_pSol = dynamic_cast<FESolute*>(GetParent());\n    \n    // set m_bcoion flag\n    if (m_pSol->GetSoluteID() == m_sol)\n        m_bcoi = true;\n    else\n        m_bcoi = false;\n    \n    // get the ancestor material which must be a multiphasic material\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    if (psm == nullptr) {\n        feLogError(\"Ancestor material must have solutes\");\n        return false;\n    }\n    \n    // extract the local id of the solute from the global id\n    // m_sol must be zero-based\n    m_lsol = psm->FindLocalSoluteID(m_sol);\n    if (m_lsol == -1) {\n        feLogError(\"Invalid value for sol\");\n        return false;\n    }\n\n\tif (m_solub == nullptr) {\n\t\tfeLogError(\"Function for solub not assigned\");\n\t\treturn false;\n\t}\n    m_solub->Init();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! Solubility\ndouble FESolubManning::Solubility(FEMaterialPoint& mp)\n{\n    double kPM = Solubility_Manning(mp);\n    double kMM = Solubility_Wells(mp);\n    \n    return kPM*kMM;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solubility with respect to strain\ndouble FESolubManning::Tangent_Solubility_Strain(FEMaterialPoint &mp)\n{\n    double kPM = Solubility_Manning(mp);\n    double kMM = Solubility_Wells(mp);\n    \n    double dkPMdJ = Tangent_Solubility_Strain_Manning(mp);\n    double dkMMdJ = Tangent_Solubility_Strain_Wells(mp);\n    \n    return dkPMdJ*kMM + kPM*dkMMdJ;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solubility with respect to strain\ndouble FESolubManning::Tangent_Solubility_Concentration(FEMaterialPoint& mp, const int isol)\n{\n    double kPM = Solubility_Manning(mp);\n    double kMM = Solubility_Wells(mp);\n    \n    double dkPMdc = Tangent_Solubility_Concentration_Manning(mp,isol);\n    double dkMMdc = Tangent_Solubility_Concentration_Wells(mp,isol);\n    \n    return dkPMdc*kMM + kPM*dkMMdc;\n}\n\n//-----------------------------------------------------------------------------\n//! Cross derivative of solubility with respect to strain and concentration\ndouble FESolubManning::Tangent_Solubility_Strain_Concentration(FEMaterialPoint &mp, const int isol)\n{\n    // assume 0\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Second derivative of solubility with respect to strain\ndouble FESolubManning::Tangent_Solubility_Strain_Strain(FEMaterialPoint &mp)\n{\n    // assume 0\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Second derivative of solubility with respect to concentration\ndouble FESolubManning::Tangent_Solubility_Concentration_Concentration(FEMaterialPoint &mp, const int isol, const int jsol)\n{\n    // assume 0\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solubility\ndouble FESolubManning::Solubility_Manning(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n   \n    // evaluate X = FCD/co-ion actual concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double cF = psm->GetFixedChargeDensity(mp);\n    \n    double X = 0;\n    if (ca > 0) X = cF/ca;\n    \n    // --- Manning activity coefficient ---\n    double kh;\n    if (m_ksi <= 1)\n        kh = exp(0.5*m_ksi*X/(X+2));\n    else\n    {\n        double Y = X/m_ksi;\n        if (m_bcoi)\n            kh = exp(0.5*Y/(Y+2));\n        else\n            kh = (X+1)/(Y+1)*exp(0.5*Y/(Y+2));\n    }\n    \n    assert(kh>0);\n    \n    return kh;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solubility with respect to strain\ndouble FESolubManning::Tangent_Solubility_Strain_Manning(FEMaterialPoint &mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    FEBiphasicInterface* pbm = dynamic_cast<FEBiphasicInterface*>(GetAncestor());\n\n    FEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n    \n    // evaluate X = FCD/co-ion actual concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double cF = fabs(psm->GetFixedChargeDensity(mp));\n    double kt = psm->GetPartitionCoefficient(mp, m_lsol);\n    double dktdJ = psm->dkdJ(mp, m_lsol);\n\n    double X = 0;\n    if (ca > 0) X = cF/ca;\n    \n    // evaluate dX/dJ\n    double J = pt.m_J;\n    double phisr = pbm->GetReferentialSolidVolumeFraction(mp);\n    \n    double dXdJ = -(1./(J-phisr)+dktdJ/kt)*X;\n    \n    // evaluate dkhdX\n    double dkhdX = 0;\n    if (m_ksi <= 1)\n        dkhdX = m_ksi*exp(0.5*m_ksi*X/(X+2))/pow(X+2,2);\n    else\n    {\n        double Y = X/m_ksi;\n        double isk = 1./m_ksi;\n        if (m_bcoi)\n            dkhdX = isk*exp(0.5*Y/(Y+2))/pow(Y+2, 2);\n        else\n            dkhdX = exp(0.5*Y/(Y+2))*(Y*Y*(2-isk)+Y*(5-3*isk)+4-3*isk)/pow((Y+1)*(Y+2), 2);\n    }\n    \n    // evaluate dkhdJ\n    double dkhdJ = dkhdX*dXdJ;\n    \n    return dkhdJ;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solubility with respect to concentration\ndouble FESolubManning::Tangent_Solubility_Concentration_Manning(FEMaterialPoint &mp, const int isol)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    \n    // evaluate X = FCD/co-ion actual concentration\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double cF = fabs(psm->GetFixedChargeDensity(mp));\n    double kta = psm->GetPartitionCoefficient(mp, m_lsol);\n    double kt = psm->GetPartitionCoefficient(mp, isol);\n    int zt = psm->GetSolute(isol)->ChargeNumber();\n\n    double X = 0;\n    if (ca > 0) X = cF/ca;\n    \n    // evaluate dX/dc\n    double dXdc = -zt*kt/ca;\n    if (isol == m_lsol) dXdc -= kta*X/ca;\n    \n    // evaluate dkhdX\n    double dkhdX = 0;\n    if (m_ksi <= 1)\n        dkhdX = m_ksi*exp(0.5*m_ksi*X/(X+2))/pow(X+2,2);\n    else\n    {\n        double Y = X/m_ksi;\n        double isk = 1./m_ksi;\n        if (m_bcoi)\n            dkhdX = isk*exp(0.5*Y/(Y+2))/pow(Y+2, 2);\n        else\n            dkhdX = exp(0.5*Y/(Y+2))*(Y*Y*(2-isk)+Y*(5-3*isk)+4-3*isk)/pow((Y+1)*(Y+2), 2);\n    }\n    \n    // evaluate dkhdc\n    double dkhdc = dkhdX*dXdc;\n    \n    return dkhdc;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolubManning::Solubility_Wells(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double solub = m_solub->value(ca);\n    assert(solub>0);\n    return solub;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolubManning::Tangent_Solubility_Strain_Wells(FEMaterialPoint& mp)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    \n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double dsolub = m_solub->derive(ca);\n    \n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double dkdJ = psm->dkdJ(mp, m_lsol);\n    dsolub *= dkdJ * c;\n    \n    return dsolub;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolubManning::Tangent_Solubility_Concentration_Wells(FEMaterialPoint& mp, const int isol)\n{\n    FESoluteInterface* psm = dynamic_cast<FESoluteInterface*>(GetAncestor());\n    \n    double ca = psm->GetActualSoluteConcentration(mp, m_lsol);\n    double c = psm->GetEffectiveSoluteConcentration(mp, m_lsol);\n    double k = psm->GetPartitionCoefficient(mp, m_lsol);\n    double dkdc = psm->dkdc(mp, m_lsol, isol);\n   \n    double f = dkdc*c;\n    if (isol == m_lsol) f += k;\n    \n    double dsolub = m_solub->derive(ca);\n    dsolub *= f;\n    \n    return dsolub;\n}\n"
  },
  {
    "path": "FEBioMix/FESolubManning.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEFunction1D.h>\n#include \"FESolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a solute solubility that follows\n// the Wells-Manning theory.  The mobile ion-mobile ion correction from Wells\n// is provided by a loadcurve.\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FESolubManning : public FESoluteSolubility\n{\npublic:\n    //! constructor\n    FESolubManning(FEModel* pfem);\n    \n    //! Initialization\n    bool Init() override;\n    \n    //! solubility\n    double Solubility(FEMaterialPoint& pt) override;\n    \n    //! Tangent of solubility with respect to strain\n    double Tangent_Solubility_Strain(FEMaterialPoint& mp) override;\n    \n    //! Tangent of solubility with respect to concentration\n    double Tangent_Solubility_Concentration(FEMaterialPoint& mp, const int isol) override;\n    \n    //! Cross derivative of solubility with respect to strain and concentration\n    double Tangent_Solubility_Strain_Concentration(FEMaterialPoint& mp, const int isol) override;\n    \n    //! Second derivative of solubility with respect to strain\n    double Tangent_Solubility_Strain_Strain(FEMaterialPoint& mp) override;\n    \n    //! Second derivative of solubility with respect to concentration\n    double Tangent_Solubility_Concentration_Concentration(FEMaterialPoint& mp, const int isol, const int jsol) override;\n    \n    //! Manning response\n    double Solubility_Manning(FEMaterialPoint& mp);\n    double Tangent_Solubility_Strain_Manning(FEMaterialPoint& mp);\n    double Tangent_Solubility_Concentration_Manning (FEMaterialPoint& mp, const int isol);\n    \n    //! Wells response\n    double Solubility_Wells(FEMaterialPoint& mp);\n    double Tangent_Solubility_Strain_Wells(FEMaterialPoint& mp);\n    double Tangent_Solubility_Concentration_Wells(FEMaterialPoint& mp, const int isol);\n\npublic:\n    double  m_ksi;              //!< Manning parameter\n    int\t\tm_sol;              //!< global id of co-ion\n    int\t\tm_lsol;             //!< local id of co-ion\n    bool    m_bcoi;             //!< true if this solute is the co-ion\n    FEFunction1D*\tm_solub;    //!< solubility from Wells correction\n\n    // declare parameter list\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESolute.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolute.h\"\n#include <FECore/FEModel.h>\n#include <FECore/DOFS.h>\n#include <FECore/log.h>\n\n//=============================================================================\n// FESoluteData\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Material parameters for FESoluteData\nBEGIN_FECORE_CLASS(FESoluteData, FEGlobalData)\n\tADD_PARAMETER(m_rhoT, \"density\")->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_M, \"molar_mass\")->setUnits(UNIT_MOLAR_MASS);\n\tADD_PARAMETER(m_z, \"charge_number\")->setUnits(UNIT_NONE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESoluteData::FESoluteData(FEModel* pfem) : FEGlobalData(pfem)\n{ \n\tm_rhoT = 1; \n\tm_M = 1; \n\tm_z = 0; \n}\n\n//-----------------------------------------------------------------------------\n// TODO: Maybe I can use the ID to make sure the dof is not duplicated.\nbool FESoluteData::Init()\n{\n\t// for each solute we have to add a concentration degree of freedom\n\tFEModel& fem = *GetFEModel();\n    DOFS& fedofs = fem.GetDOFS();\n\tint varC = fedofs.GetVariableIndex(\"concentration\");\n    int varD = fedofs.GetVariableIndex(\"shell concentration\");\n    int varAC = fedofs.GetVariableIndex(\"concentration tderiv\");\n\tint cdofs = fedofs.GetVariableSize(varC);\n    int ddofs = fedofs.GetVariableSize(varD);\n\tchar sz[8] = {0};\n    int max_len = sizeof sz;\n\tsnprintf(sz, max_len, \"c%d\", cdofs+1);\n\tfedofs.AddDOF(varC, sz);\n    snprintf(sz, max_len, \"d%d\", ddofs+1);\n    fedofs.AddDOF(varD, sz);\n    snprintf(sz, max_len, \"ac%d\", cdofs+1);\n    fedofs.AddDOF(varAC, sz);\n\n\treturn true;\n}\n\n//=============================================================================\n// FESolute\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Material parameters for FESoluteData\nBEGIN_FECORE_CLASS(FESoluteMaterial, FESolute)\n\n\t// These parameters cannot (or should not) be set in the input file since\n\t// they are copied from the FESoluteData class. \n//\tADD_PARAMETER(m_rhoT, \"density\");\n//\tADD_PARAMETER(m_M, \"molar_mass\");\n//\tADD_PARAMETER(m_z, \"charge_number\");\n\n\tADD_PARAMETER(m_ID, \"sol\", FE_PARAM_ATTRIBUTE, \"$(solutes)\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pDiff , \"diffusivity\");\n\tADD_PROPERTY(m_pSolub, \"solubility\");\n\tADD_PROPERTY(m_pSupp , \"supply\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESoluteMaterial::FESoluteMaterial(FEModel* fem) : FESolute(fem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! FESolute constructor\n\nFESolute::FESolute(FEModel* pfem) : FEMaterialProperty(pfem)\n{\n\tm_rhoT = 0;\n\tm_M = 0;\n\tm_z = 0;\n\n\tm_ID = -1;\n\n\tm_pDiff = 0;\n\tm_pSolub = 0;\n\tm_pSupp = 0;\n}\n\n//-----------------------------------------------------------------------------\nFESoluteData* FESolute::FindSoluteData(int nid)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = GetFEModel()->GlobalDataItems();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n\t\tif (psd && (psd->GetID() == nid)) return psd;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FESolute::Init()\n{\n\tif (FEMaterialProperty::Init() == false) return false;\n\n\tFESoluteData* psd = FindSoluteData(m_ID);\n\tif (psd == 0) {\n\t\tfeLogError(\"no match with global solute data\");\n\t\treturn false;\n\t}\n\tm_rhoT = psd->m_rhoT;\n\tm_M = psd->m_M;\n\tm_z = (int) psd->m_z;\n\tSetName(psd->GetName());\n\t\n\tif (m_rhoT < 0) { feLogError(\"density must be positive\"   ); return false; }\n\tif (m_M    < 0) { feLogError(\"molar_mass must be positive\"); return false; }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Data serialization\nvoid FESolute::Serialize(DumpStream& ar)\n{\n\tFEMaterialProperty::Serialize(ar);\n\tar & m_ID & m_LID;\n\tar & m_rhoT& m_M& m_z;\n}\n\n//=============================================================================\n// FESBMData\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Material parameters for FESoluteData\nBEGIN_FECORE_CLASS(FESBMData, FEGlobalData)\n\tADD_PARAMETER(m_rhoT, \"density\"      )->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_M   , \"molar_mass\"   )->setUnits(UNIT_MOLAR_MASS);\n\tADD_PARAMETER(m_z   , \"charge_number\")->setUnits(UNIT_NONE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESBMData::FESBMData(FEModel* pfem) : FEGlobalData(pfem)\n{ \n\tm_rhoT = 1; \n\tm_M = 1; \n\tm_z = 0; \n}\n\n//=============================================================================\n// FESolidBoundMolecule\n//=============================================================================\n\n// Material parameters for the FESolidBoundMolecule material\nBEGIN_FECORE_CLASS(FESolidBoundMolecule, FEMaterialProperty)\n\n\tADD_PARAMETER(m_ID, \"sbm\", FE_PARAM_ATTRIBUTE, \"$(sbms)\");\n\n\tADD_PARAMETER(m_rho0  , \"rho0\"  )->setLongName(\"initial density\")->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_rhomin, \"rhomin\")->setLongName(\"minimum density\")->setUnits(UNIT_DENSITY);\n\tADD_PARAMETER(m_rhomax, \"rhomax\")->setLongName(\"maximum density\")->setUnits(UNIT_DENSITY);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FESolidBoundMolecule constructor\n\nFESolidBoundMolecule::FESolidBoundMolecule(FEModel* pfem) : FEMaterialProperty(pfem)\n{\n\tm_ID = -1;\n\tm_rhoT = 1;\n\tm_M = 1;\n\tm_z = 0;\n\tm_rho0 = 0;\n\tm_rhomin = 0;\n\tm_rhomax = 0;\n}\n\n//-----------------------------------------------------------------------------\nFESBMData* FESolidBoundMolecule::FindSBMData(int nid)\n{\n\tFEModel& fem = *GetFEModel();\n\tint N = GetFEModel()->GlobalDataItems();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFESBMData* psd = dynamic_cast<FESBMData*>(fem.GetGlobalData(i));\n\t\tif (psd && (psd->GetID() == nid)) return psd;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FESolidBoundMolecule::Init()\n{\n\tif (FEMaterialProperty::Init() == false) return false;\n\t\n\tFESBMData* psd = FindSBMData(m_ID);\n\tif (psd == 0) {\n\t\tfeLogError(\"no match with global solid-bound molecule data\");\n\t\treturn false;\n\t}\n\tm_rhoT = psd->m_rhoT;\n\tm_M = psd->m_M;\n\tm_z = psd->m_z;\n\tSetName(psd->GetName());\n\t\n\tif (m_rhoT < 0) { feLogError(\"density must be positive\"   ); return false; }\n\tif (m_M    < 0) { feLogError(\"molar_mass must be positive\"); return false; }\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Data serialization\nvoid FESolidBoundMolecule::Serialize(DumpStream& ar)\n{\n\tFEMaterialProperty::Serialize(ar);\n\tar & m_ID;\n\tar & m_rhoT & m_M & m_z & m_rho0;\n\tar & m_rhomin & m_rhomax;\n}\n"
  },
  {
    "path": "FEBioMix/FESolute.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include <FECore/FEGlobalData.h>\n#include <FECore/tens4d.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for solute diffusivity.\n//! These materials need to define the diffusivity and tangent diffusivity functions.\n//!\nclass FEBIOMIX_API FESoluteDiffusivity : public FEMaterialProperty\n{\npublic:\n\t//! constructor\n\tFESoluteDiffusivity(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\n\t//! solute diffusivity\n\tvirtual mat3ds Diffusivity(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of diffusivity with respect to strain\n\tvirtual tens4dmm Tangent_Diffusivity_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of diffusivity with respect to solute concentration\n\tvirtual mat3ds Tangent_Diffusivity_Concentration(FEMaterialPoint& mp, const int isol) = 0;\n\t\n\t//! solute diffusivity in free solution\n\tvirtual double Free_Diffusivity(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of free diffusivity with respect to solute concentration\n\tvirtual double Tangent_Free_Diffusivity_Concentration(FEMaterialPoint& pt, const int isol) = 0;\n\t\n\t//! set solute ID\n\tvoid SetSoluteID(const int ID) {m_ID = ID;}\n\t\n\t//! set solute ID\n\tint GetSoluteID() { return m_ID;}\n\t\nprivate:\n\tint\tm_ID;\t\t//!< solute ID\n\t\n\tFECORE_BASE_CLASS(FESoluteDiffusivity)\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for solute solubility.\n//! These materials need to define the solubility and tangent solubility functions.\n//!\nclass FEBIOMIX_API FESoluteSolubility : public FEMaterialProperty\n{\npublic:\n\t//! constructor\n\tFESoluteSolubility(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\n\t//! solute solubility\n\tvirtual double Solubility(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of solubility with respect to strain\n\tvirtual double Tangent_Solubility_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of solubility with respect to concentration\n\tvirtual double Tangent_Solubility_Concentration(FEMaterialPoint& mp, const int isol) = 0;\n\t\n\t//! cross derivative of solubility with respect to strain and concentration\n\tvirtual double Tangent_Solubility_Strain_Concentration(FEMaterialPoint& mp, const int isol) = 0;\n\t\n\t//! second derivative of solubility with respect to strain\n\tvirtual double Tangent_Solubility_Strain_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! second derivative of solubility with respect to concentration\n\tvirtual double Tangent_Solubility_Concentration_Concentration(FEMaterialPoint& mp, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  const int isol, const int jsol) = 0;\n\t\n\t//! set solute ID\n\tvoid SetSoluteID(const int ID) {m_ID = ID;}\n\t\n\t//! set solute ID\n\tint GetSoluteID() { return m_ID;}\n\t\nprivate:\n\tint\tm_ID;\t\t//!< solute ID\n\t\n\tFECORE_BASE_CLASS(FESoluteSolubility)\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for solute supply.\n//! These materials need to define the solute supply and tangent supply functions.\n//! The solute supply has units of moles/(referential mixture volume)/time\n//!\nclass FEBIOMIX_API FESoluteSupply : public FEMaterialProperty\n{\npublic:\n\t//! constructor\n\tFESoluteSupply(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\n\t//! solute supply\n\tvirtual double Supply(FEMaterialPoint& pt) = 0;\n\t\n\t//! solute supply under steady-state conditions\n\tvirtual double SupplySS(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of solute supply with respect to strain\n\tvirtual double Tangent_Supply_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of solute supply with respect to solute concentration\n\tvirtual double Tangent_Supply_Concentration(FEMaterialPoint& mp) = 0;\n\t\n\t//! receptor-ligand complex supply\n\tvirtual double ReceptorLigandSupply(FEMaterialPoint& pt) = 0;\n\t\n\t//! receptor-ligand concentration under steady-state conditions\n\tvirtual double ReceptorLigandConcentrationSS(FEMaterialPoint& pt) = 0;\n\t\n\t//! referential solid supply\n\tvirtual double SolidSupply(FEMaterialPoint& pt) = 0;\n\t\n\t//! referential solid concentration under steady-state conditions\n\tvirtual double SolidConcentrationSS(FEMaterialPoint& pt) = 0;\n\n\tFECORE_BASE_CLASS(FESoluteSupply)\n};\n\n//-----------------------------------------------------------------------------\n//! Global solute data\n//! This structure uniquely identifies a solute in multiphasic problems\nclass FESoluteData : public FEGlobalData \n{\npublic:\n\tFESoluteData(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\npublic:\n\tdouble\tm_rhoT;\t\t\t//!< true solute density\n\tdouble\tm_M;\t\t\t//!< solute molecular weight\n\tint\t\tm_z;\t\t\t//!< solute charge number\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for solute materials.\n\nclass FESolute : public FEMaterialProperty\n{\npublic:\n\tFESolute(FEModel* pfem);\n\npublic:\n\tbool Init() override;\n\t\n\t//! solute density\n\tdouble Density() { return m_rhoT; }\n\t\n\t//! solute molecular weight\n\tdouble MolarMass() { return m_M; }\n\t\n\t//! solute charge number\n\tint ChargeNumber() { return m_z; }\n\t\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! set solute ID\n\tvoid SetSoluteID(const int ID) {m_ID = ID;}\n\t\n\t//! get solute ID\n\tint GetSoluteID() {return m_ID;}\n\n\t//! set solute local ID\n\tvoid SetSoluteLocalID(const int LID) {m_LID = LID;}\n\t\n\t//! get solute local ID\n\tint GetSoluteLocalID() {return m_LID;}\n  \n\t//! return the solute's dof\n\tint GetSoluteDOF() const { return m_ID - 1; }\n\nprivate:\n\tFESoluteData* FindSoluteData(int nid);\n\t\nprivate:\n    int                     m_LID;      //!< solute local ID in parent material\n\t\npublic: // material parameters\n\tint\t\t\t\t\t\tm_ID;\t\t//!< solute ID in global table\n\tdouble\t\t\t\t\tm_rhoT;\t\t//!< true solute density\n\tdouble\t\t\t\t\tm_M;\t\t//!< solute molecular weight\n\tint\t\t\t\t\t\tm_z;\t\t//!< charge number of solute\n\npublic: // material properties\n\tFESoluteDiffusivity*\tm_pDiff;\t//!< pointer to diffusivity material\n\tFESoluteSolubility*\t\tm_pSolub;\t//!< pointer to solubility material\n\tFESoluteSupply*\t\t\tm_pSupp;\t//!< pointer to solute supply material\n\n\tFECORE_BASE_CLASS(FESolute)\n};\n\n//-----------------------------------------------------------------------------\n// This class was introduced so that solutes fall in the same paradigm as any other\n// material property.\nclass FESoluteMaterial : public FESolute\n{\npublic:\n\tFESoluteMaterial(FEModel* fem);\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Global solid-bound molecule (SBM) data.\nclass FESBMData : public FEGlobalData\n{\npublic:\n\tFESBMData(FEModel* pfem);\n\npublic:\n\tdouble\tm_rhoT;\t\t\t//!< SBM true density\n\tdouble\tm_M;\t\t\t//!< SBM molar mass\n\tint\t\tm_z;\t\t\t//!< SBM charge number\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for solid-bound molecules.\n\nclass FESolidBoundMolecule : public FEMaterialProperty\n{\npublic:\n\tFESolidBoundMolecule(FEModel* pfem);\n\t\npublic:\n\tbool Init() override;\n\t\n\t//! solute density\n\tdouble Density() { return m_rhoT; }\n\t\n\t//! solute molecular weight\n\tdouble MolarMass() { return m_M; }\n\t\n\t//! solute charge number\n\tint ChargeNumber() { return m_z; }\n\t\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\t//! set solute ID\n\tvoid SetSBMID(const int ID) {m_ID = ID;}\n\t\n\t//! get SBM ID\n\tint GetSBMID() {return m_ID;}\n\t\nprivate:\n\tFESBMData* FindSBMData(int nid);\n\nprivate:\n\tint\t\t\t\t\t\tm_ID;\t\t//!< SBM ID in global table\n\t\npublic:\n\tdouble\t\t\t\t\tm_rhoT;\t\t//!< true SBM density\n\tdouble\t\t\t\t\tm_M;\t\t//!< SBM molar mass\n\tint\t\t\t\t\t\tm_z;\t\t//!< charge number of SBM\n\tFEParamDouble\t\t\tm_rho0;\t\t//!< initial referential (apparent) density of SBM\n\tdouble\t\t\t\t\tm_rhomin;\t//!< minimum referential (apparent) density of SBM\n\tdouble\t\t\t\t\tm_rhomax;\t//!< maximum referential (apparent) density of SBM\n\t\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FESolidBoundMolecule)\n};\n"
  },
  {
    "path": "FEBioMix/FESoluteFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESoluteFlux.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEFacetSet.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FESoluteFlux, FESurfaceLoad)\n\tADD_PARAMETER(m_flux   , \"flux\");\n\tADD_PARAMETER(m_blinear, \"linear\");\n    ADD_PARAMETER(m_bshellb, \"shell_bottom\");\n\tADD_PARAMETER(m_isol   , \"solute_id\")->setEnums(\"$(solutes)\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESoluteFlux::FESoluteFlux(FEModel* pfem) : FESurfaceLoad(pfem), m_dofC(pfem), m_dofU(pfem)\n{ \n\tm_flux = 1.0;\n\tm_blinear = false; \n    m_bshellb = false;\n\tm_isol = -1;\n}\n\t\n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FESoluteFlux::SetSurface(FESurface* ps)\n{ \n\tFESurfaceLoad::SetSurface(ps);\n\tm_flux.SetItemList(ps->GetFacetSet());\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FESoluteFlux::Serialize(DumpStream& ar)\n{\n\tFESurfaceLoad::Serialize(ar);\n\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_dofC & m_dofU;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FESoluteFlux::Init()\n{\n\tif (m_psurf == nullptr) return false;\n\tm_psurf->SetShellBottom(m_bshellb);\n\n\tif (m_isol == -1) return false;\n\n\t// set up the dof lists\n\tm_dofC.Clear();\n\tm_dofU.Clear();\n\tif (m_bshellb == false)\n\t{ \n\t\tm_dofC.AddDof(GetDOFIndex(\"concentration\", m_isol - 1));\n\n\t\tm_dofU.AddDof(GetDOFIndex(\"x\"));\n\t\tm_dofU.AddDof(GetDOFIndex(\"y\"));\n\t\tm_dofU.AddDof(GetDOFIndex(\"z\"));\n\t}\n\telse\n\t{\n\t\tm_dofC.AddDof(GetDOFIndex(\"shell concentration\", m_isol - 1));\n\n\t\tm_dofU.AddDof(GetDOFIndex(\"sx\"));\n\t\tm_dofU.AddDof(GetDOFIndex(\"sy\"));\n\t\tm_dofU.AddDof(GetDOFIndex(\"sz\"));\n\n\t}\n    m_dof.AddDofs(m_dofU);\n    m_dof.AddDofs(m_dofC);\n\treturn FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESoluteFlux::LoadVector(FEGlobalVector& R)\n{\n\tdouble dt = CurrentTimeIncrement();\n\n\tm_psurf->SetShellBottom(m_bshellb);\n\n\tFESoluteFlux* flux = this;\n\tm_psurf->LoadVector(R, m_dofC, m_blinear, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, std::vector<double>& fa) {\n\n\t\tdouble wr = flux->m_flux(mp);\n\t\tif (flux->m_bshellb) wr = -wr;\n\n\t\tvec3d dxt = mp.dxr ^ mp.dxs;\n\n\t\t// volumetric flow rate\n\t\tdouble f = dxt.norm()*wr* dt;\n\n\t\tdouble H_i = dof_a.shape;\n\t\tfa[0] = H_i * f;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FESoluteFlux::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// time increment\n\tdouble dt = CurrentTimeIncrement();\n\n\tm_psurf->SetShellBottom(m_bshellb);\n\t\n\t// evaluate the stiffness contribution\n\tFESoluteFlux* flux = this;\n\tm_psurf->LoadStiffness(LS, m_dofC, m_dofU, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n\t\t// shape functions and derivatives\n\t\tdouble H_i  = dof_a.shape;\n\t\tdouble Gr_j = dof_b.shape_deriv_r;\n\t\tdouble Gs_j = dof_b.shape_deriv_s;\n\n\t\tdouble wr = flux->m_flux(mp);\n\t\tif (flux->m_bshellb) wr = -wr;\n\n\t\t// calculate surface normal\n\t\tvec3d dxt = mp.dxr ^ mp.dxs;\n\n\t\t// calculate stiffness component\n\t\tvec3d t1 = dxt / dxt.norm()*wr;\n\t\tvec3d t2 = mp.dxs*Gr_j - mp.dxr*Gs_j;\n\t\tvec3d kab = (t1 ^ t2)*(H_i)*dt;\n\n\t\tKab[0][0] = kab.x;\n\t\tKab[0][1] = kab.y;\n\t\tKab[0][2] = kab.z;\n\t});\n}\n"
  },
  {
    "path": "FEBioMix/FESoluteFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The flux surface is a surface domain that sustains a solute flux boundary\n//! condition\n//!\nclass FEBIOMIX_API FESoluteFlux : public FESurfaceLoad\n{\npublic:\n\t//! constructor\n\tFESoluteFlux(FEModel* pfem);\n\n\t//! Initialization\n\tbool Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\t//! Set the surface to apply the load to\n\tvoid SetSurface(FESurface* ps) override;\n\n\tvoid SetLinear(bool blinear) { m_blinear = blinear; }\n\n\tvoid SetSolute(int isol) { m_isol = isol; }\n\t\n\t//! calculate flux stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\t\n\t//! calculate residual\n\tvoid LoadVector(FEGlobalVector& R) override;\n\t\nprotected:\n\tFEParamDouble\tm_flux;\t\t//!< flux scale factor magnitude\n\tbool\tm_blinear;\t//!< linear or not (true is non-follower, false is follower)\n    bool    m_bshellb;  //!< flag for prescribing flux on shell bottom\n\tint\t\tm_isol;\t\t//!< solute index\n\nprotected:\n\tFEDofList\tm_dofC;\n\tFEDofList\tm_dofU;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESoluteInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <FECore/FEMaterialPoint.h>\n#include \"FESoluteInterface.h\"\n#include \"FESolute.h\"\n\n//-----------------------------------------------------------------------------\n//! Returns the local solute index given the global ID\nint FESoluteInterface::FindLocalSoluteID(int nid)\n{\n\tint lsid = -1;\n\tfor (int isol = 0; isol<Solutes(); ++isol)\n\t\tif (GetSolute(isol)->GetSoluteID() == nid) {\n\t\t\tlsid = isol;\n\t\t\tbreak;\n\t\t}\n\treturn lsid;\n}\n"
  },
  {
    "path": "FEBioMix/FESoluteInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/fecore_api.h>\n#include \"febiomix_api.h\"\n#include <FECore/vec3d.h>\n\nclass FESolute;\nclass FESolidBoundMolecule;\nclass FEOsmoticCoefficient;\nclass FEMaterialPoint;\n\n//------------------------------------------------------------------------\n// This class should be used by all materials that support solutes\n// TODO: This is a work in progress. The goal is to reduce the dynamic_casts to materials\n//       that support solutes, and instead provide a single consistent interface to features\n//       that need access to solute data (e.g. plot variables).\nclass FEBIOMIX_API FESoluteInterface\n{\npublic:\n\tFESoluteInterface(){}\n\tvirtual ~FESoluteInterface(){}\n\n// derived classes need to implement the following functions\npublic:\n\t// return the number of solutes in the material\n\tvirtual int Solutes() = 0;\n\n\t// return a solute material\n\tvirtual FESolute* GetSolute(int i) = 0;\n\n\t//! return the effective solute concentration\n\tvirtual double GetEffectiveSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n\n\t// return the actual solution concentration at this material point\n\tvirtual double GetActualSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n\n    // return the partition coefficient at this material point\n    virtual double GetFreeDiffusivity(FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n    \n\t// return the partition coefficient at this material point\n\tvirtual double GetPartitionCoefficient(FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n\n\t// return the solute flux at this material point\n\tvirtual vec3d GetSoluteFlux(FEMaterialPoint& mp, int soluteIndex) { return vec3d(0,0,0); }\n\n\t// get the osmotic coefficient\n\tvirtual FEOsmoticCoefficient* GetOsmoticCoefficient() { return nullptr; }\n\n\t// get the osmolarity\n\tvirtual double GetOsmolarity(const FEMaterialPoint& mp) { return 0.0; }\n\n\t// get the electric potential\n\tvirtual double GetElectricPotential(const FEMaterialPoint& mp) { return 0.0; }\n\n\t// get the current density\n\tvirtual vec3d GetCurrentDensity(const FEMaterialPoint& mp) { return vec3d(0,0,0); }\n\n\t// get the fixed charge density\n\tvirtual double GetFixedChargeDensity(const FEMaterialPoint& mp) { return 0.0; }\n\n\t// get the referential fixed charge density\n\tvirtual double GetReferentialFixedChargeDensity(const FEMaterialPoint& mp) { return 0.0; }\n\n\t// get first derivative of k (partition coefficient) w.r.p. concentration\n\tvirtual double dkdc(const FEMaterialPoint& mp, int i, int j) { return 0.0; }\n\n\t// get first derivative of k (partition coefficient) w.r.p. J\n\tvirtual double dkdJ(const FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n\n\t// return the number of solid-bound molecules\n\tvirtual int SBMs() const { return 0; }\n\n\t// return the solid-bound modlecule\n\tvirtual FESolidBoundMolecule* GetSBM(int i) { return nullptr; }\n\n\t//! SBM actual concentration (molar concentration in current configuration)\n\tvirtual double SBMConcentration(FEMaterialPoint& pt, const int sbm) { return 0.0; }\n\n\t//! SBM areal concentration (mole per shell area) -- should only be called from shell domains\n\tvirtual double SBMArealConcentration(FEMaterialPoint& pt, const int sbm) { return 0.0; }\n\n    // return the number of solutes on external side\n    virtual int SolutesExternal(FEMaterialPoint& pt) { return 0; }\n    \n    // return the number of solutes on internal side\n    virtual int SolutesInternal(FEMaterialPoint& pt) { return 0; }\n    \n    //! return the solute ID on external side\n    virtual int GetSoluteIDExternal(FEMaterialPoint& mp, int soluteIndex) { return -1; }\n    \n    //! return the solute ID on internal side\n    virtual int GetSoluteIDInternal(FEMaterialPoint& mp, int soluteIndex) { return -1; }\n    \n    //! return the effective solute concentration on external side\n    virtual double GetEffectiveSoluteConcentrationExternal(FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n    \n    //! return the effective solute concentration on internal side\n    virtual double GetEffectiveSoluteConcentrationInternal(FEMaterialPoint& mp, int soluteIndex) { return 0.0; }\n    \n    //! return the effective pressure on external side\n    virtual double GetEffectiveFluidPressureExternal(FEMaterialPoint& mp) { return 0.0; }\n    \n    //! return the effective pressure on internal side\n    virtual double GetEffectiveFluidPressureInternal(FEMaterialPoint& mp) { return 0.0; }\n\n    //! return the membrane areal strain\n    virtual double GetMembraneArealStrain(FEMaterialPoint& mp) { return 0.0; }\n    \n// additional member functions\npublic:\n\t// return the local index of a global solute ID (or -1, if the solute is not in this material)\n\tint FindLocalSoluteID(int soluteID);\n};\n\n\ntemplate <typename T>\nclass FESoluteInterface_T : public FESoluteInterface\n{\npublic:\n\tdouble GetEffectiveSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) override {\n\t\tT* spt = mp.ExtractData<T>();\n\t\treturn spt->m_c[soluteIndex];\n\t};\n\tdouble GetActualSoluteConcentration(FEMaterialPoint& mp, int soluteIndex) override {\n\t\tT* spt = mp.ExtractData<T>();\n\t\treturn spt->m_ca[soluteIndex];\n\t};\n\tdouble GetPartitionCoefficient(FEMaterialPoint& mp, int soluteIndex) override {\n\t\tT* spt = mp.ExtractData<T>();\n\t\treturn spt->m_k[soluteIndex];\n\t}\n\tvec3d GetSoluteFlux(FEMaterialPoint& mp, int soluteIndex) override {\n\t\tT* spt = mp.ExtractData<T>();\n\t\treturn spt->m_j[soluteIndex];\n\t};\n\tdouble GetOsmolarity(const FEMaterialPoint& mp) override {\n\t\tconst T* spt = mp.ExtractData<T>();\n\t\treturn spt->Osmolarity();\n\t}\n\tdouble GetElectricPotential(const FEMaterialPoint& mp) override {\n\t\tconst T* spt = mp.ExtractData<T>();\n\t\treturn spt->m_psi;\n\t}\n\tvec3d GetCurrentDensity(const FEMaterialPoint& mp) override {\n\t\tconst T* spt = mp.ExtractData<T>();\n\t\treturn spt->m_Ie;\n\t}\n\tdouble dkdc(const FEMaterialPoint& mp, int i, int j) override {\n\t\tconst T* spt = mp.ExtractData<T>();\n\t\treturn spt->m_dkdc[i][j];\n\t}\n\tdouble dkdJ(const FEMaterialPoint& mp, int soluteIndex) override {\n\t\tconst T* spt = mp.ExtractData<T>();\n\t\treturn spt->m_dkdJ[soluteIndex];\n\t}\n};\n"
  },
  {
    "path": "FEBioMix/FESoluteNaturalFlux.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESoluteNaturalFlux.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FESolidDomain.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FESoluteNaturalFlux, FESurfaceLoad)\n    ADD_PARAMETER(m_bshellb, \"shell_bottom\");\n    ADD_PARAMETER(m_isol   , \"solute_id\")->setEnums(\"$(solutes)\");\n    ADD_PARAMETER(m_bup    , \"update\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFESoluteNaturalFlux::FESoluteNaturalFlux(FEModel* pfem) : FESurfaceLoad(pfem), m_dofC(pfem), m_dofU(pfem), m_dofP(pfem)\n{\n    m_bshellb = false;\n    m_isol = -1;\n    m_bup = false;\n}\n    \n//-----------------------------------------------------------------------------\n//! allocate storage\nvoid FESoluteNaturalFlux::SetSurface(FESurface* ps)\n{\n    FESurfaceLoad::SetSurface(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! serialization\nvoid FESoluteNaturalFlux::Serialize(DumpStream& ar)\n{\n    FESurfaceLoad::Serialize(ar);\n\n    if (ar.IsShallow() == false)\n    {\n        ar & m_dofC & m_dofU & m_dofP;\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FESoluteNaturalFlux::Init()\n{\n    if (m_isol <= 0) return false;\n\n    // set up the dof lists\n    FEModel* fem = GetFEModel();\n    m_dofC.Clear();\n    m_dofU.Clear();\n    m_dofP.Clear();\n    if (m_bshellb == false)\n    {\n        m_dofC.AddDof(fem->GetDOFIndex(\"concentration\", m_isol - 1));\n\n        m_dofU.AddDof(fem->GetDOFIndex(\"x\"));\n        m_dofU.AddDof(fem->GetDOFIndex(\"y\"));\n        m_dofU.AddDof(fem->GetDOFIndex(\"z\"));\n        \n        m_dofP.AddDof(fem->GetDOFIndex(\"p\"));\n    }\n    else\n    {\n        m_dofC.AddDof(fem->GetDOFIndex(\"shell concentration\", m_isol - 1));\n\n        m_dofU.AddDof(fem->GetDOFIndex(\"sx\"));\n        m_dofU.AddDof(fem->GetDOFIndex(\"sy\"));\n        m_dofU.AddDof(fem->GetDOFIndex(\"sz\"));\n\n        m_dofP.AddDof(fem->GetDOFIndex(\"q\"));\n    }\n    m_dof.AddDofs(m_dofU);\n    m_dof.AddDofs(m_dofP);\n    m_dof.AddDofs(m_dofC);\n    return FESurfaceLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESoluteNaturalFlux::Update()\n{\n    if (m_bup) {\n        for (int is=0; is<m_psurf->Elements(); ++is)\n        {\n            // get surface element\n            FESurfaceElement& el = m_psurf->Element(is);\n            // get underlying solid element\n            FESolidElement* pe = dynamic_cast<FESolidElement*>(el.m_elem[0].pe);\n            if (pe == nullptr) break;\n            // get element data\n            int neln = pe->Nodes();\n            int nint = pe->GaussPoints();\n            \n            FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n            // get the local solute id\n            FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n            if (psi == nullptr) break;\n            int sid = psi->FindLocalSoluteID(m_isol);\n            if (sid == -1) break;\n            \n            // identify nodes on the surface\n            vector<bool> nsrf(neln,false);\n            for (int j=0; j<neln; ++j) {\n                for (int k=0; k < el.Nodes(); ++k) {\n                    if (el.m_node[k] == pe->m_node[j]) nsrf[j] = true;\n                }\n            }\n            \n            // get average effective concentration of nodes not on surface\n            double cavg = 0;\n            int m = 0;\n            for (int i=0; i<neln; ++i) {\n                if (!nsrf[i]) {\n                    int n = pe->m_node[i];\n                    FENode& node = GetMesh().Node(n);\n                    int dof = m_dofC[m_isol-1];\n                    if (dof != -1) {\n                        cavg += node.get(dof);\n                        ++m;\n                    }\n                }\n            }\n            // assign this average value to surface nodes as initial guess\n            if (m) {\n                cavg /= m;\n                for (int i=0; i<neln; ++i) {\n                    if (nsrf[i]) {\n                        int n = pe->m_node[i];\n                        FENode& node = GetMesh().Node(n);\n                        int dof = m_dofC[m_isol-1];\n                        if (dof != -1) node.set(dof, cavg);\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FESoluteNaturalFlux::LoadVector(FEGlobalVector& R)\n{\n    double dt = CurrentTimeIncrement();\n\n    // element force vector\n    vector<double> fe;\n    vector<int> lm;\n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    const double* Gr, *Gs, *Gt, *H;\n\n    for (int is=0; is<m_psurf->Elements(); ++is)\n    {\n        // get surface element\n        FESurfaceElement& el = m_psurf->Element(is);\n        // get surface normal\n        vec3d nu(0,0,0);\n        for (int n=0; n<el.GaussPoints(); ++n) {\n            FESurfaceMaterialPoint* pt = dynamic_cast<FESurfaceMaterialPoint*>(el.GetMaterialPoint(n));\n            nu += pt->dxr ^ pt->dxs;\n        }\n        nu.unit();\n        // get underlying solid element\n        FESolidElement* pe = dynamic_cast<FESolidElement*>(el.m_elem[0].pe);\n        if (pe == nullptr) break;\n        // determine the solid domain to which this solid element belongs\n        FESolidDomain* sdom = dynamic_cast<FESolidDomain*>(pe->GetMeshPartition());\n        // get element data\n        int nint = pe->GaussPoints();\n        int neln = pe->Nodes();\n        double* gw = pe->GaussWeights();\n        \n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) break;\n        int sid = psi->FindLocalSoluteID(m_isol);\n        if (sid == -1) break;\n        \n        // get the element force vector and initialize it to zero\n        fe.assign(neln, 0);    // 1 concentration dof per node\n        lm.resize(neln);\n        // unpack lm and get nodal effective solute concentrations\n        vector<double> ce(neln,0);\n        for (int i=0; i<neln; ++i) {\n            int n = pe->m_node[i];\n            FENode& node = GetMesh().Node(n);\n            vector<int>& id = node.m_ID;\n            int dof = m_dofC[m_isol-1];\n            if (dof != -1) {\n                lm[i] = id[dof];\n                ce[i] = node.get(dof);\n            }\n        }\n\n        // for each integration point in the solid element\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(pt.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n\n            // calculate the jacobian\n            detJt = sdom->invjact(*pe, Ji, n);\n            detJt *= gw[n]*dt;\n            // get shape functions and their derivatives\n            H = pe->H(n);\n            Gr = pe->Gr(n);\n            Gs = pe->Gs(n);\n            Gt = pe->Gt(n);\n            \n            // get contravariant basis vectors\n            vec3d gcntv[3];\n            sdom->ContraBaseVectors(*pe, n, gcntv);\n            \n            // evaluate gradient of shape function and gradient of effective concentration\n            // (using ps.m_gradc[n] doesn't work, because it doesn't get updated until convergence)\n            vector<vec3d> gradN(neln);\n            vec3d gradc(0,0,0);\n            for (int i=0; i<neln; ++i) {\n                gradN[i] = gcntv[0]*Gr[i] + gcntv[1]*Gs[i] + gcntv[2]*Gt[i];\n                gradc += gradN[i]*ce[i];\n            }\n\n            for (int i=0; i<neln; ++i)\n                fe[i] -= H[i]*(gradc*nu)*detJt;\n        }\n        \n        R.Assemble(pe->m_node, lm, fe);\n    }\n\n    // Now do the surface implementation, the normal way\n    FESoluteNaturalFlux* flux = this;\n    m_psurf->LoadVector(R, m_dofC, false, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, std::vector<double>& fa) {\n\n        // get surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        // get underlying solid element\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) {\n            fa[0] = 0;\n            return;\n        }\n        int sid = psi->FindLocalSoluteID(flux->m_isol);\n        \n        // get element-averaged fluid flux and actual solute concentration\n        vec3d w(0,0,0);\n        double c = 0;\n        int nint = pe->GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(pt.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n            w += pb.m_w;\n            c += ps.m_ca[sid];\n        }\n        w /= nint;\n        c /= nint;\n\n        // evaluate desired natural solute flux\n        vec3d dxt = mp.dxr ^ mp.dxs;\n        double jn = c*(w*dxt);\n        if (flux->m_bshellb) jn = -jn;\n\n        // molar flow rate\n        double f = jn* dt;\n\n        double H_i = dof_a.shape;\n        fa[0] = H_i * f;\n    });\n}\n\n//-----------------------------------------------------------------------------\nvoid FESoluteNaturalFlux::StiffnessMatrix(FELinearSystem& LS)\n{\n    // time increment\n    double dt = CurrentTimeIncrement();\n\n    int ndpn = 4;   // 3 displacement dofs + 1 concentration dof\n    // element stiffness matrix\n    vector<int> lm;\n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    const double* Gr, *Gs, *Gt, *H;\n    \n    for (int is=0; is<m_psurf->Elements(); ++is)\n    {\n        // get surface element\n        FESurfaceElement& el = m_psurf->Element(is);\n        // get surface normal\n        vec3d nu(0,0,0);\n        for (int n=0; n<el.GaussPoints(); ++n) {\n            FESurfaceMaterialPoint* pt = dynamic_cast<FESurfaceMaterialPoint*>(el.GetMaterialPoint(n));\n            nu += pt->dxr ^ pt->dxs;\n        }\n        nu.unit();\n        // get underlying solid element\n        FESolidElement* pe = dynamic_cast<FESolidElement*>(el.m_elem[0].pe);\n        if (pe == nullptr) break;\n        // determine the solid domain to which this solid element belongs\n        FESolidDomain* sdom = dynamic_cast<FESolidDomain*>(pe->GetMeshPartition());\n        // get element data\n        int nint = pe->GaussPoints();\n        int neln = pe->Nodes();\n        double* gw = pe->GaussWeights();\n        int ndof = neln*ndpn;\n        FEElementMatrix ke(*pe);\n        ke.resize(ndof, ndof);\n\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) break;\n        int sid = psi->FindLocalSoluteID(m_isol);\n        if (sid == -1) break;\n        \n        // initialize stiffness matrix it to zero\n        ke.zero();\n        lm.resize(ndof);\n        // unpack lm and get nodal effective solute concentrations\n        vector<double> ce(neln,0);\n        for (int i=0; i<neln; ++i) {\n            int n = pe->m_node[i];\n            FENode& node = GetMesh().Node(n);\n            vector<int>& id = node.m_ID;\n            lm[ndpn*i  ] = id[m_dofU[0]];\n            lm[ndpn*i+1] = id[m_dofU[1]];\n            lm[ndpn*i+2] = id[m_dofU[2]];\n            int dof = m_dofC[m_isol-1];\n            if (dof != -1) {\n                lm[ndpn*i+3] = id[dof];\n                ce[i] = node.get(dof);\n            }\n        }\n        ke.SetIndices(lm);\n        \n        // for each integration point in the solid element\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(pt.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n            \n            // calculate the jacobian\n            detJt = sdom->invjact(*pe, Ji, n);\n            detJt *= gw[n]*dt;\n            // get shape functions and their derivatives\n            H = pe->H(n);\n            Gr = pe->Gr(n);\n            Gs = pe->Gs(n);\n            Gt = pe->Gt(n);\n            \n            // get contravariant basis vectors\n            vec3d gcntv[3];\n            sdom->ContraBaseVectors(*pe, n, gcntv);\n            \n            // evaluate gradient of shape function and gradient of effective concentration\n            // don't use ps.m_gradc[n] as it doesn't get updated until next convergence\n            vector<vec3d> gradN(neln);\n            vec3d gradc(0,0,0);\n            for (int i=0; i<neln; ++i) {\n                gradN[i] = gcntv[0]*Gr[i] + gcntv[1]*Gs[i] + gcntv[2]*Gt[i];\n                gradc += gradN[i]*ce[i];\n            }\n            for (int i=0, in = 0; i<neln; ++i, in += ndpn) {\n                for (int j=0, jn = 0; j<neln; ++j, jn += ndpn) {\n                    vec3d kcu = (gradN[j]*(nu*gradc) - gradc*(gradN[j]*nu))*H[i];\n                    double kcc = H[i]*(gradN[j]*nu);\n                    \n                    ke[in+3][jn  ] += kcu.x*detJt;\n                    ke[in+3][jn+1] += kcu.y*detJt;\n                    ke[in+3][jn+2] += kcu.z*detJt;\n                    ke[in+3][jn+3] += kcc*detJt;\n                }\n            }\n        }\n        \n        LS.Assemble(ke);\n    }\n\n    // Now do the surface implementation, the normal way\n    // evaluate the stiffness contribution\n    FESoluteNaturalFlux* flux = this;\n    m_psurf->LoadStiffness(LS, m_dofC, m_dofU, [=](FESurfaceMaterialPoint& mp, const FESurfaceDofShape& dof_a, const FESurfaceDofShape& dof_b, matrix& Kab) {\n\n        // get surface element\n        FESurfaceElement& el = *mp.SurfaceElement();\n        // get underlying solid element\n        FEElement* pe = el.m_elem[0].pe;\n        FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        // get the local solute id\n        FESoluteInterface* psi = dynamic_cast<FESoluteInterface*>(pm);\n        if (psi == nullptr) return;\n        int sid = psi->FindLocalSoluteID(flux->m_isol);\n        \n        // get element-averaged fluid flux and actual solute concentration\n        vec3d w(0,0,0);\n        double c = 0;\n        int nint = pe->GaussPoints();\n        for (int n=0; n<nint; ++n) {\n            FEMaterialPoint& pt = *pe->GetMaterialPoint(n);\n            FEBiphasicMaterialPoint& pb = *(pt.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(pt.ExtractData<FESolutesMaterialPoint>());\n            w += pb.m_w;\n            c += ps.m_ca[sid];\n        }\n        w /= nint;\n        c /= nint;\n        \n        // shape functions and derivatives\n        double H_i  = dof_a.shape;\n        double Gr_j = dof_b.shape_deriv_r;\n        double Gs_j = dof_b.shape_deriv_s;\n\n        // calculate surface normal\n        vec3d dxt = mp.dxr ^ mp.dxs;\n        vec3d nu = dxt.normalized();\n        double jn = c*(w*nu);\n        if (flux->m_bshellb) jn = -jn;\n\n        // calculate stiffness component\n        vec3d t1 = nu*jn;\n        vec3d t2 = mp.dxs*Gr_j - mp.dxr*Gs_j;\n        vec3d kab = (t1 ^ t2)*(H_i)*dt;\n\n        Kab[0][0] = kab.x;\n        Kab[0][1] = kab.y;\n        Kab[0][2] = kab.z;\n    });\n}\n"
  },
  {
    "path": "FEBioMix/FESoluteNaturalFlux.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2022 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEModelParam.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The flux surface is a surface domain that sustains a solute natural flux boundary\n//! condition\n//!\nclass FEBIOMIX_API FESoluteNaturalFlux : public FESurfaceLoad\n{\npublic:\n    //! constructor\n    FESoluteNaturalFlux(FEModel* pfem);\n\n    //! Initialization\n    bool Init() override;\n\n    //! serialization\n    void Serialize(DumpStream& ar) override;\n    \n    //! Set the surface to apply the load to\n    void SetSurface(FESurface* ps) override;\n\n    void SetSolute(int isol) { m_isol = isol; }\n    \n    //! calculate flux stiffness\n    void StiffnessMatrix(FELinearSystem& LS) override;\n    \n    //! calculate residual\n    void LoadVector(FEGlobalVector& R) override;\n    \n    //! update\n    void Update() override;\n    \nprotected:\n    bool        m_bshellb;      //!< flag for prescribing flux on shell bottom\n    int         m_isol;         //!< solute index\n    bool        m_bup;          //!< flag to call Update function\n\nprotected:\n    FEDofList    m_dofC;\n    FEDofList    m_dofU;\n    FEDofList    m_dofP;\n\n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESolutePointSource.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESolutePointSource.h\"\n#include \"FESolute.h\"\n#include \"FEMultiphasic.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEElemElemList.h>\n#include <algorithm>\n#include <FEBioMech/FEElasticMaterialPoint.h>\n#include <iostream>\n#include <unordered_set>\n#include <unordered_map>\n#include <FECore/FEAnalysis.h>\n\nBEGIN_FECORE_CLASS(FESolutePointSource, FEBodyLoad)\n\tADD_PARAMETER(m_soluteId, \"solute\");\n\tADD_PARAMETER(m_rate, \"rate\");\n\tADD_PARAMETER(m_pos.x, \"x\");\n\tADD_PARAMETER(m_pos.y, \"y\");\n\tADD_PARAMETER(m_pos.z, \"z\");\nEND_FECORE_CLASS();\n\nFESolutePointSource::FESolutePointSource(FEModel* fem) : FEBodyLoad(fem), m_search(fem ? &fem->GetMesh() : nullptr)\n{\n\tm_dofC = -1;\n\tm_soluteId = -1;\n\tm_pos = vec3d(0, 0, 0);\n\tm_rate = 0.0;\n}\n\nvec3d FESolutePointSource::GetPosition() const\n{\n\treturn m_pos;\n}\n\nvoid FESolutePointSource::SetPosition(const vec3d& pos)\n{\n\tm_pos = pos;\n}\n\nint FESolutePointSource::GetSoluteID() const\n{\n\treturn m_soluteId;\n}\n\nvoid FESolutePointSource::SetSoluteID(int soluteID)\n{\n\tm_soluteId = soluteID;\n}\n\ndouble FESolutePointSource::GetRate() const\n{\n\treturn m_rate;\n}\n\nvoid FESolutePointSource::SetRate(double rate)\n{\n\tm_rate = rate;\n}\n\ndouble FESolutePointSource::GetdC() const\n{\n\treturn m_dC;\n}\n\ndouble FESolutePointSource::GetdCp() const\n{\n\treturn m_dCp;\n}\n\nvoid FESolutePointSource::SetdC(double dC)\n{\n\tm_dC = dC;\n}\n\nvoid FESolutePointSource::SetdCp(double dCp)\n{\n\tm_dCp = dCp;\n}\n\nvoid FESolutePointSource::SetRadius(double radius)\n{\n\tm_radius = radius;\n\tm_Vc = (4.0 / 3.0) * PI * pow(m_radius, 3.0);\n}\n\nvoid FESolutePointSource::SetAccumulateFlag(bool b) {\n\tm_accumulate = b;\n}\n\nvoid FESolutePointSource::SetAccumulateCAFlag(bool b) {\n\tm_accumulate_ca = b;\n}\n\n\nbool FESolutePointSource::Init()\n{\n\t// see if the solute exists\n\tFEModel* fem = GetFEModel();\n\n\tbool bfound = false;\n\tint ndata = fem->GlobalDataItems();\n\tfor (int i=0; i<ndata; ++i)\n\t{\n\t\tFESoluteData* soluteData = dynamic_cast<FESoluteData*>(fem->GetGlobalData(i));\n\t\tif (soluteData && (soluteData->GetID() == m_soluteId))\n\t\t{\n\t\t\tbfound = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (bfound == false) return false;\n\n\t// initialize octree search\n\tif (m_search.Init() == false) return false;\n\n\t// get the degree of freedom of the concentration\n\tm_dofC = fem->GetDOFIndex(\"concentration\", m_soluteId - 1);\n\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\n\treturn FEBodyLoad::Init();\n}\n\n// allow species to accumulate at the point source\nvoid FESolutePointSource::Accumulate(double dc) {\n\t// find the element in which the point lies\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tif (m_el == nullptr) return;\n\n\t// make sure this element is part of a multiphasic domain\n\tFEDomain* dom = dynamic_cast<FEDomain*>(m_el->GetMeshPartition());\n\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\tif (mat == nullptr) return;\n\n\t// Make sure the material has the correct solute\n\tint solid = -1;\n\tint sols = mat->Solutes();\n\tfor (int j = 0; j < sols; ++j)\n\t{\n\t\tint solj = mat->GetSolute(j)->GetSoluteID();\n\t\tif (solj == m_soluteId)\n\t\t{\n\t\t\tsolid = j;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (solid == -1) return;\n\n\tm_rate = dc + m_rate;\n\tm_accumulate = true;\n}\n\nvoid FESolutePointSource::Update()\n{\n\tif (m_accumulate) {\n\t\t// find the element in which the point lies\n\t\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\t\tif (m_el == nullptr) return;\n\n\t\t// make sure this element is part of a multiphasic domain\n\t\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(m_el->GetMeshPartition());\n\t\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\t\tif (mat == nullptr) return;\n\n\t\t// calculate the element volume\n\t\tFEMesh* mesh = dom->GetMesh();\n\t\tdouble Ve = mesh->ElementVolume(*m_el);\n\n\t\tstd::cout << \"rate is \" << m_rate << endl;\n\n\t\tint solid = -1;\n\t\tint sols = mat->Solutes();\n\t\tfor (int j = 0; j < sols; ++j) {\n\t\t\tint solj = mat->GetSolute(j)->GetSoluteID();\n\t\t\tif (solj == m_soluteId)\n\t\t\t{\n\t\t\t\tsolid = j;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (solid == -1) return;\n\n\t\t// evaluate the concentration at this point\n\t\tint neln = m_el->Nodes();\n\t\tdouble c[FEElement::MAX_NODES];\n\t\tfor (int i = 0; i < neln; ++i) c[i] = mesh->Node(m_el->m_node[i]).get(m_dofC);\n\t\tdouble cx = m_el->evaluate(c, m_q[0], m_q[1], m_q[2]);\n\t\tdouble dt = mesh->GetFEModel()->GetCurrentStep()->m_dt;\n\t\tdouble cxx = cx * Ve + dt * m_rate;\n\n\t\t// assemble the element load vector\n\t\tvector<double> fe(neln, 0.0);\n\t\tvector<int> lm(neln, -1);\n\t\tstd::vector<FEElement*> possible_nodes;\n\t\tdouble total_elem = 0.0;\n\t\tFindNodesInRadius(possible_nodes, total_elem);\n\t\tdouble total_change = 0.0;\n\t\t//SL: Currently this just assigns within the current element. We will instead want this to assign to the closest integration points.\n\t\tif (possible_nodes.size() == 0) {\n\t\t\t// set the concentration of all nodes via the shape functions for each integration point\n\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\tm_el->shape_fnc(H, m_q[0], m_q[1], m_q[2]);\n\t\t\tint nint = m_el->GaussPoints();\tdouble* w = m_el->GaussWeights();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tdouble* H_int = m_el->H(n);\n\t\t\t\tFEMaterialPoint* mp = m_el->GetMaterialPoint(n);\n\t\t\t\tdouble m_J = mp->m_J0;\n\t\t\t\t// loop over all nodes\n\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t{\n\t\t\t\t\t// only allow internalization if the species won't go negative\n\t\t\t\t\tif (cxx > 0.0) {\n\t\t\t\t\t\ttotal_change += m_rate * H[n] * H_int[j] * dt * w[n];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// set the concentration of all nodes via the shape functions for each integration point\n\t\t\tdouble n_elem = possible_nodes.size();\n\t\t\tdouble H[FEElement::MAX_NODES];\n\t\t\tfor (auto iter = possible_nodes.begin(); iter != possible_nodes.end(); iter++)\n\t\t\t{\n\t\t\t\tFESolidElement* c_el = dynamic_cast<FESolidElement*>(*iter);\n\t\t\t\tdouble r[3];\n\t\t\t\tdom->ProjectToElement(*c_el, m_pos, r);\n\t\t\t\tvec3d r3 = ClampNatC(r);\n\t\t\t\tr[0] = r3.x; r[1] = r3.y; r[2] = r3.z;\n\t\t\t\tint nint = c_el->GaussPoints();\n\t\t\t\tdouble* w = c_el->GaussWeights();\n\t\t\t\tint neln = c_el->Nodes();\n\t\t\t\t// for this element get the shape functions and constants\n\t\t\t\tc_el->shape_fnc(H, r[0], r[1], r[2]);\n\t\t\t\t// for each integration point in this element project to the nodes.\n\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t{\n\t\t\t\t\tdouble* H_int = c_el->H(n);\n\t\t\t\t\tFEMaterialPoint* mp = c_el->GetMaterialPoint(n);\n\t\t\t\t\tdouble m_J = mp->m_J0;\n\t\t\t\t\t// loop over all nodes\n\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\t// only allow internalization if the species won't go negative\n\t\t\t\t\t\tif (cxx > 0.0) {\n\t\t\t\t\t\t\ttotal_change += m_rate * H[n] * H_int[j] * dt * w[n] / n_elem;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tm_dC = -total_change / (m_Vc);\n\t\tm_accumulate = false;\n\t}\n}\n\n//! Evaluate force vector\nvoid FESolutePointSource::LoadVector(FEGlobalVector& R)\n{\n\t// get the domain in which this element resides\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(m_el->GetMeshPartition());\n\tFEMesh* mesh = dom->GetMesh();\n\n\t// get time increment\n\tdouble dt = CurrentTimeIncrement();\n\n\t// evaluate the shape functions at the position\n\tdouble H[FEElement::MAX_NODES];\n\tm_el->shape_fnc(H, m_q[0], m_q[1], m_q[2]);\n\n\t// evaluate the concentration at this point\n\tint neln = m_el->Nodes();\n\tdouble c[FEElement::MAX_NODES];\n\tfor (int i = 0; i < neln; ++i) c[i] = mesh->Node(m_el->m_node[i]).get(m_dofC);\n\tdouble cx = m_el->evaluate(c, m_q[0], m_q[1], m_q[2]);\n\tdouble Ve = mesh->ElementVolume(*m_el);\n\tdouble cxx = cx * Ve + dt * m_rate;\n\tdouble v_rate = m_rate / Ve;\n\n\t// assemble the element load vector\n\tvector<double> fe(neln, 0.0);\n\tvector<int> lm(neln, -1);\n\tstd::vector<FEElement*> possible_nodes;\n\tdouble total_elem = 0;\n\tFindNodesInRadius(possible_nodes, total_elem);\n\t//SL: Currently this just assigns within the current element. We will instead want this to assign to the closest integration points.\n\tif (possible_nodes.size() == 0) {\n\t\tint nint = m_el->GaussPoints();\n\t\tdouble* w = m_el->GaussWeights();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tdouble* H_int = m_el->H(n);\n\t\t\tFEMaterialPoint* mp = m_el->GetMaterialPoint(n);\n\t\t\tdouble m_J = mp->m_J0;\n\t\t\t// loop over all nodes\n\t\t\tfor (int j = 0; j < neln; ++j) fe[j] += -v_rate * m_J * H[n] * H_int[j] * dt * w[n] * neln;\n\t\t}\n\n\t\t//// get the LM vector\n\t\tfor (int i = 0; i < neln; ++i) lm[i] = mesh->Node(m_el->m_node[i]).m_ID[m_dofC];\n\t\tR.Assemble(lm, fe);\n\t}\n\telse\n\t{\n\t\tdouble n_elem = possible_nodes.size();\n\t\t//vec3d global_pos = GetGlobalPos(vec3d(m_q[0],m_q[1],m_q[2]), m_el);\n\t\tfor (auto iter = possible_nodes.begin(); iter != possible_nodes.end(); iter++)\n\t\t{\n\t\t\tFESolidElement* c_el = dynamic_cast<FESolidElement*>(*iter);\n\t\t\t//std::cout << \"element \" << c_el->GetID() << endl;\n\t\t\tdouble r[3];\n\t\t\tdom->ProjectToElement(*c_el, m_pos, r);\n\t\t\tvec3d r3 = ClampNatC(r);\n\t\t\tr[0] = r3.x; r[1] = r3.y; r[2] = r3.z;\n\t\t\t//std::cout << \"r is \" << r[0] << \", \" << r[1] << \", \" << r[2] << endl;\n\t\t\tint nint = c_el->GaussPoints();\n\t\t\tdouble* w = c_el->GaussWeights();\n\t\t\tint neln = c_el->Nodes();\n\t\t\tvector<double> fe(neln, 0.0);\n\t\t\tvector<int> lm(neln, -1);\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tdouble* H_int = c_el->H(n);\n\t\t\t\tc_el->shape_fnc(H, r[0], r[1], r[2]);\n\t\t\t\tFEMaterialPoint* mp = c_el->GetMaterialPoint(n);\n\t\t\t\tdouble m_J = mp->m_J0;\n\t\t\t\tfor (int j = 0; j < neln; ++j) fe[j] += -v_rate * m_J * H[n] * H_int[j] * dt * w[n] * neln / n_elem;\n\t\t\t}\n\t\t\tfor (int i = 0; i < neln; ++i) lm[i] = mesh->Node(c_el->m_node[i]).m_ID[m_dofC];\n\t\t\tR.Assemble(lm, fe);\n\t\t}\n\t}\n}\n\n//! evaluate stiffness matrix\nvoid FESolutePointSource::StiffnessMatrix(FELinearSystem& S)\n{\n\t// get time increment\n\tdouble dt = CurrentTimeIncrement();\n\n\t// get the domain in which this element resides\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(m_el->GetMeshPartition());\n\tFEMesh* mesh = dom->GetMesh();\n\n\t// evaluate the shape functions at the position\n\tdouble H[FEElement::MAX_NODES];\n\tm_el->shape_fnc(H, m_q[0], m_q[1], m_q[2]);\n\n\t// evaluate the concentration at this point\n\tint neln = m_el->Nodes();\n\tint nint = m_el->GaussPoints();\n\n\t// assemble the element load vector\n\tFEElementMatrix ke(neln, neln); ke.zero();\n\tdouble* w = m_el->GaussWeights();\n\tdouble Ve = mesh->ElementVolume(*m_el);\n\tdouble v_rate = m_rate / Ve;\n\n\tdouble c[FEElement::MAX_NODES];\n\tfor (int i = 0; i < neln; ++i) c[i] = mesh->Node(m_el->m_node[i]).get(m_dofC);\n\n\tstd::vector<FEElement*> possible_nodes;\n\tdouble total_elem = 0;\n\tFindNodesInRadius(possible_nodes, total_elem);\n\tif (possible_nodes.size() == 0)\n\t{\n\t\tfor (int k = 0; k < nint; k++) {\n\t\t\tFEMaterialPoint* mp = m_el->GetMaterialPoint(k);\n\t\t\tdouble m_J = mp->m_J0;\n\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tke[i][j] = H[i] * m_J * H[j] * v_rate * w[k] * dt;\n\t\t\t\t}\n\t\t}\n\t\t// get the LM vector\n\t\tvector<int> lm(neln, -1);\n\t\tfor (int i = 0; i < neln; ++i) lm[i] = mesh->Node(m_el->m_node[i]).m_ID[m_dofC];\n\n\t\t// get the nodes\n\t\tvector<int> nodes(neln, -1);\n\t\tfor (int i = 0; i < neln; ++i) nodes[i] = m_el->m_node[i];\n\t\t// assemble into global matrix\n\t\tke.SetIndices(lm);\n\t\tke.SetNodes(nodes);\n\t\tS.Assemble(ke);\n\t}\n\telse\n\t{\n\t\tfor (auto iter = possible_nodes.begin(); iter != possible_nodes.end(); ++iter)\n\t\t{\n\t\t\tFESolidElement* c_el = dynamic_cast<FESolidElement*>(*iter);\n\t\t\tdouble r[3];\n\t\t\tdom->ProjectToElement(*c_el, m_pos, r);\n\t\t\tvec3d r3 = ClampNatC(r);\n\t\t\tr[0] = r3.x; r[1] = r3.y; r[2] = r3.z;\n\t\t\tdouble* w = c_el->GaussWeights();\n\t\t\tc_el->shape_fnc(H, r[0], r[1], r[2]);\n\t\t\tnint = c_el->GaussPoints();\n\t\t\tneln = c_el->Nodes();\n\t\t\tFEElementMatrix ke(neln, neln); ke.zero();\n\t\t\tfor (int k = 0; k < nint; k++) {\n\t\t\t\tFEMaterialPoint* mp = c_el->GetMaterialPoint(k);\n\t\t\t\tdouble m_J = mp->m_J0;\n\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[i][j] = H[i] * m_J * H[j] * v_rate * w[k] * dt;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\t// get the LM vector\n\t\t\tvector<int> lm(neln, -1);\n\t\t\tfor (int i = 0; i < neln; ++i) lm[i] = mesh->Node(c_el->m_node[i]).m_ID[m_dofC];\n\n\t\t\t// get the nodes\n\t\t\tvector<int> nodes(neln, -1);\n\t\t\tfor (int i = 0; i < neln; ++i) nodes[i] = c_el->m_node[i];\n\t\t\t// assemble into global matrix\n\t\t\tke.SetIndices(lm);\n\t\t\tke.SetNodes(nodes);\n\t\t\tS.Assemble(ke);\n\t\t}\n\t}\n}\n\nvoid FESolutePointSource::FindNodesInRadius(std::vector<FEElement*>& possible_nodes, double& total_elem) {\n\n\t// get element and set up buffers\n\tm_el = dynamic_cast<FESolidElement*>(m_search.FindElement(m_pos, m_q));\n\tstd::unordered_set<FESolidElement*> visited;\n\tstd::set<FESolidElement*> next;\n\t//std::vector<FEMaterialPoint*> possible_ints;\n\tvisited.reserve(1000);\n\tpossible_nodes.reserve(500);\n\n\t//we will need to check the current element first\n\tnext.insert(m_el);\n\t// create the element adjacency list.\n\tFEDomain* dom = dynamic_cast<FEDomain*>(m_el->GetMeshPartition());\n\tFEMultiphasic* mat = dynamic_cast<FEMultiphasic*>(dom->GetMaterial());\n\tif (mat == nullptr) return;\n\t// calculate the element volume\n\tauto mesh = dom->GetMesh();\n\t// create the element-element list\n\tFEElemElemList EEL;\n\tEEL.Create(mesh);\n\n\t//while there are still elements to evaluate\n\twhile (next.size()) {\n\t\t// get the element to be evaluated\n\t\tFESolidElement* cur = *next.begin();\n\t\t// remove the current element from the next buffer and add it to the visited buffer\n\t\tnext.erase(next.begin());\n\t\tvisited.insert(cur);\n\t\t// get the current element bounds\n\t\tstd::vector<vec3d> cur_element_bounds;\n\t\t// add integration points within the radius\n\t\tbool int_flag = false;\n\t\tfor (int i = 0; i < cur->Nodes(); i++)\n\t\t{\n\t\t\tFENode* mn = &(mesh->Node(cur->m_node[i]));\n\t\t\tvec3d disp = mn->m_rt - m_pos;\n\t\t\tif (disp.norm() <= m_radius) {\n\t\t\t\tpossible_nodes.push_back(cur);\n\t\t\t\tint_flag = true;\n\t\t\t}\n\t\t}\n\t\tif (int_flag)\n\t\t{\n\t\t\ttotal_elem++;\n\t\t}\n\n\t\t// Add neighboring element to the next buffer as long as they haven't been visited.\n\t\t// get the global ID of the current element\n\t\tint cur_id = cur->GetID() - 1;\n\t\t// for each neighboring element\n\t\tfor (int i = 0; i < EEL.NeighborSize(); i++)\n\t\t{\n\t\t\tif (EEL.Neighbor(cur_id, i))\n\t\t\t{\n\t\t\t\t// if that element has not been visited yet add it to the next list\n\t\t\t\tif (!visited.count(dynamic_cast<FESolidElement*>(EEL.Neighbor(cur_id, i))))\n\t\t\t\t{\n\t\t\t\t\tnext.insert(dynamic_cast<FESolidElement*>(EEL.Neighbor(cur_id, i)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvec3d FESolutePointSource::ClampNatC(double r[3])\n{\n\treturn vec3d(std::max(std::min(1.0, r[0]), -1.0),\n\t\tstd::max(std::min(1.0, r[1]), -1.0),\n\t\tstd::max(std::min(1.0, r[2]), -1.0)\n\t);\n}"
  },
  {
    "path": "FEBioMix/FESolutePointSource.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEBodyLoad.h>\n#include <FECore/FEOctreeSearch.h>\n#include <unordered_map>\n#include \"febiomix_api.h\"\n\nclass FESolidElement;\n\nclass FEBIOMIX_API FESolutePointSource : public FEBodyLoad\n{\npublic:\n\tFESolutePointSource(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Accumulate(double dc);\n\n\tvoid Update() override;\n\n\tvoid SetPosition(const vec3d& v);\n\n\tvec3d GetPosition() const;\n\n\tvoid SetSoluteID(int soluteID);\n\n\tint GetSoluteID() const;\n\n\tvoid SetRate(double rate);\n\n\tvoid SetRadius(double radius);\n\n\tdouble GetRate() const;\n\n\tdouble GetdC() const;\n\n\tdouble GetdCp() const;\n\n\tvoid SetdC(double dC);\n\n\tvoid SetdCp(double dCp);\n\n\tvoid SetAccumulateFlag(bool b);\n\n\tvoid SetAccumulateCAFlag(bool b);\n\n\t//! Evaluate force vector\n\tvoid LoadVector(FEGlobalVector& R) override;\n\n\t//! evaluate stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& S) override;\n\n\t//! return all the elements in the given radius\n\tvoid FindNodesInRadius(std::vector<FEElement*>& possible_nodes, double& total_elem);\n\n\tvec3d ClampNatC(double r[3]);\n\nprivate:\n\tint\t\tm_soluteId;\t//!< solute ID\n\tdouble\tm_rate;\t\t//!< production rate\n\tvec3d\tm_pos;\t\t//!< position of source\n\tbool\tm_accumulate = false; //!< accumulate flag\n\tbool\tm_accumulate_ca; //! < accumulate actual concentration flag\n\tdouble\tm_radius;\n\tdouble\tm_Vc;\n\tdouble\tm_dC = 0.0;\n\tdouble\tm_dCp = 0.0;\n\nprivate:\n\tFEOctreeSearch\t\tm_search;\n\tFESolidElement*\t\tm_el;\n\tdouble\t\t\t\tm_q[3];\n\tint\t\t\t\t\tm_dofC;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESolutesMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FECore/DumpStream.h\"\nusing namespace std;\n\n//=============================================================================\n//   FESolutesMaterialPoint\n//=============================================================================\n\n\n//-----------------------------------------------------------------------------\nFESolutesMaterialPoint::FESolutesMaterialPoint(FEMaterialPointData* ppt) : FEMaterialPointData(ppt) \n{\n\tm_nsol = 0;\n\tm_psi  = 0;\n\tm_cF   = 0;\n\tm_nsbm = 0;\n\tm_rhor = 0;\n\tm_strain = 0;\n\tm_pe = m_pi = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a shallow copy of the material point data\nFEMaterialPointData* FESolutesMaterialPoint::Copy()\n{\n\tFESolutesMaterialPoint* pt = new FESolutesMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize material point data\nvoid FESolutesMaterialPoint::Init()\n{\n\tm_nsol = m_nsbm = 0;\n\tm_psi = m_cF = 0;\n\tm_Ie = vec3d(0,0,0);\n\tm_rhor = 0;\n    m_c.clear();\n    m_gradc.clear();\n    m_j.clear();\n    m_ca.clear();\n    m_crp.clear();\n    m_sbmr.clear();\n    m_sbmrp.clear();\n    m_sbmrhat.clear();\n    m_sbmrhatp.clear();\n    m_sbmrmin.clear();\n    m_sbmrmax.clear();\n    m_k.clear();\n    m_dkdJ.clear();\n    m_dkdJJ.clear();\n    m_dkdc.clear();\n    m_dkdJc.clear();\n    m_dkdcc.clear();\n    m_dkdr.clear();\n    m_dkdJr.clear();\n    m_dkdrc.clear();\n    m_cri.clear();\n    m_crd.clear();\n    m_strain = 0;\n    m_pe = m_pi = 0;\n    m_ce.clear();\n    m_ci.clear();\n    m_ide.clear();\n    m_idi.clear();\n    m_bsb.clear();\n    \n\t// don't forget to initialize the base class\n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Serialize material point data to the archive\nvoid FESolutesMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_nsol & m_psi & m_cF & m_Ie & m_nsbm & m_rhor;\n\tar & m_c & m_gradc & m_j & m_ca & m_crp & m_k & m_dkdJ & m_dkdJJ;\n\tar & m_dkdc & m_dkdJc & m_dkdcc;\n\tar & m_dkdr & m_dkdJr & m_dkdrc;\n\tar & m_sbmr & m_sbmrp & m_sbmrhat & m_sbmrhatp & m_sbmrmin& m_sbmrmax;\n\tar & m_cri;\n\tar & m_crd;\n\tar & m_strain & m_pe & m_pi;\n\tar & m_ce & m_ide;\n\tar & m_ci & m_idi;\n\tar & m_bsb;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolutesMaterialPoint::Osmolarity() const\n{\n    double ew = 0.0;\n    for (int isol = 0; isol < (int)m_ca.size(); ++isol)\n    {\n        // exclude solid-bound 'solutes'\n        if (!m_bsb[isol]) ew += m_ca[isol];\n    }\n    return ew;\n}\n"
  },
  {
    "path": "FEBioMix/FESolutesMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterialPoint.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Class for storing material point data for solute materials\n\nclass FEBIOMIX_API FESolutesMaterialPoint : public FEMaterialPointData\n{\npublic:\n\t//! Constructor\n\tFESolutesMaterialPoint(FEMaterialPointData* ppt);\n\t\n\t//! Create a shallow copy\n\tFEMaterialPointData* Copy();\n\t\n\t//! serialize data\n\tvoid Serialize(DumpStream& ar);\n    \n\t//! Initialize material point data\n\tvoid Init();\n\npublic:\n\tdouble Osmolarity() const;\n\t\npublic:\n\t// solutes material data\n\tint\t\t\t\tm_nsol;\t\t//!< number of solutes\n\tstd::vector<double>\tm_c;\t\t//!< effective solute concentration\n\tstd::vector<vec3d>\tm_gradc;\t//!< spatial gradient of solute concentration\n\tstd::vector<vec3d>\tm_j;\t\t//!< solute molar flux\n\tstd::vector<double>\tm_ca;\t\t//!< actual solute concentration\n    std::vector<double>  m_crp;      //!< referential actual solute concentration at previous time step\n\tdouble\t\t\tm_psi;\t\t//!< electric potential\n\tvec3d\t\t\tm_Ie;\t\t//!< current density\n\tdouble\t\t\tm_cF;\t\t//!< fixed charge density in current configuration\n\tint\t\t\t\tm_nsbm;\t\t//!< number of solid-bound molecules\n\tdouble\t\t\tm_rhor;\t\t//!< current referential mass density\n\tstd::vector<double>\tm_sbmr;\t\t//!< referential mass concentration of solid-bound molecules\n\tstd::vector<double>\tm_sbmrp;\t//!< m_sbmr at previoust time step\n\tstd::vector<double>\tm_sbmrhat;\t//!< referential mass supply of solid-bound molecules\n    std::vector<double>  m_sbmrhatp; //!< referential mass supply of solid-bound molecules at previous time step\n\tstd::vector<double>\tm_sbmrmin;\t//!< minimum value of m_sbmr\n\tstd::vector<double>\tm_sbmrmax;\t//!< maximum value of m_sbmr\n\tstd::vector<double>\tm_k;\t\t//!< solute partition coefficient\n\tstd::vector<double>\tm_dkdJ;\t\t//!< 1st deriv of m_k with strain (J)\n\tstd::vector<double>\tm_dkdJJ;\t//!< 2nd deriv of m_k with strain (J)\n\tstd::vector< std::vector<double> >\tm_dkdc;\t\t\t//!< 1st deriv of m_k with effective concentration\n\tstd::vector< std::vector<double> >\tm_dkdJc;\t\t//!< cross deriv of m_k with J and c\n\tstd::vector< std::vector< std::vector<double> > > m_dkdcc;\t// 2nd deriv of m_k with c\n\tstd::vector< std::vector<double> >\tm_dkdr;\t\t\t//!< 1st deriv of m_k with m_sbmr\n\tstd::vector< std::vector<double> >\tm_dkdJr;\t\t//!< cross deriv of m_k with J and m_sbmr\n\tstd::vector< std::vector< std::vector<double> > > m_dkdrc;\t//!< cross deriv of m_k with m_sbmr and c\n    std::vector<int>     m_cri;      //!< optional integer data needed for chemical reactions\n    std::vector<double>  m_crd;      //!< optional double data needed for chemical reactions\n    double          m_strain;   //!< areal strain\n    double          m_pe;       //!< effective fluid pressure on external side\n    double          m_pi;       //!< effective fluid pressure on internal side\n    std::vector<double>  m_ce;       //!< effective solute concentration on external side\n    std::vector<double>  m_ci;       //!< effective solute concentration on internal side\n    std::vector<int>     m_ide;      //!< solute IDs on external side\n    std::vector<int>     m_idi;      //!< solute IDs on internal side\n    std::vector<bool>   m_bsb;  //!< flag indicating that solute is solid-bound\n};\n\n"
  },
  {
    "path": "FEBioMix/FESolventSupply.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolventSupply.h\"\n\n//-----------------------------------------------------------------------------\n// Derivative of supply w.r.t. solute concentration at material point\n// Set this to zero by default because biphasic problems do not require it\ndouble FESolventSupply::Tangent_Supply_Concentration(FEMaterialPoint& pt, const int isol)\n{\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioMix/FESolventSupply.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMaterial.h>\n#include \"febiomix_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for solvent supply.\n//! These materials need to define the supply and tangent supply functions.\n//!\nclass FEBIOMIX_API FESolventSupply : public FEMaterialProperty\n{\npublic:\n\tFESolventSupply(FEModel* pfem) : FEMaterialProperty(pfem) {}\n\tvirtual ~FESolventSupply(){}\n\t\n\t//! solvent supply\n\tvirtual double Supply(FEMaterialPoint& pt) = 0;\n\t\n\t//! tangent of solvent supply with respect to strain\n\tvirtual mat3ds Tangent_Supply_Strain(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of solvent supply with respect to pressure\n\tvirtual double Tangent_Supply_Pressure(FEMaterialPoint& mp) = 0;\n\t\n\t//! tangent of solvent supply with respect to concentration\n\tdouble Tangent_Supply_Concentration(FEMaterialPoint& mp, const int isol);\n    \n    //! initialization\n    bool Init() override { return FEMaterialProperty::Init(); }\n\n\tFECORE_BASE_CLASS(FESolventSupply)\n};\n\n"
  },
  {
    "path": "FEBioMix/FESolventSupplyStarling.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <sstream>\n#include <iostream>\n#include <cstdlib>\n#include \"FESolventSupplyStarling.h\"\n#include \"FESolutesMaterialPoint.h\"\n#include \"FECore/FEModel.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESolventSupplyStarling, FESolventSupply)\n\tADD_PARAMETER(m_kp, \"kp\")->setLongName(\"hydraulic filtration coefficient, $k_p$\")->setUnits(\"L^2/F.t\");\n\tADD_PARAMETER(m_pv, \"pv\")->setLongName(\"fluid pressure in external source, $p_v$\")->setUnits(UNIT_PRESSURE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFESolventSupplyStarling::FESolventSupplyStarling(FEModel* pfem) : FESolventSupply(pfem)\n{\n\tm_kp = 0;\n\tm_pv = 0;\n\n    // get number of DOFS\n\tif (pfem)\n\t{\n\t\tDOFS& fedofs = pfem->GetDOFS();\n\t\tint MAX_CDOFS = fedofs.GetVariableSize(\"concentration\");\n\n\t\tif (MAX_CDOFS > 0) {\n\t\t\tFEParamDouble tmp;\n\t\t\ttmp = 0;\n\t\t\tm_qc.assign(MAX_CDOFS, tmp);\n\t\t\tm_cv.assign(MAX_CDOFS, tmp);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Solvent supply\ndouble FESolventSupplyStarling::Supply(FEMaterialPoint& mp)\n{\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint* mpt = mp.ExtractData<FESolutesMaterialPoint>();\n\n\t// evaluate solvent supply from pressure drop\n    double kp = m_kp(mp);\n    double pv = m_pv(mp);\n\tdouble phiwhat = kp*(pv - ppt.m_p);\n\t\n\t// evaluate solvent supply from concentration drop\n\tif (mpt) {\n\t\tint nsol = mpt->m_nsol;\n\t\tfor (int isol=0; isol<nsol; ++isol) {\n            double qc = m_qc[isol](mp);\n            double cv = m_cv[isol](mp);\n\t\t\tphiwhat += qc*(cv - mpt->m_c[isol]);\n\t\t}\n\t}\n\t\n\treturn phiwhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solvent supply with respect to strain\nmat3ds FESolventSupplyStarling::Tangent_Supply_Strain(FEMaterialPoint &mp)\n{\n\tmat3dd Phie(Supply(mp));\n\t\n\treturn Phie;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solvent supply with respect to pressure\ndouble FESolventSupplyStarling::Tangent_Supply_Pressure(FEMaterialPoint &mp)\n{\n\treturn -m_kp(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solvent supply with respect to concentration\ndouble FESolventSupplyStarling::Tangent_Supply_Concentration(FEMaterialPoint &mp, const int isol)\n{\n\tFESolutesMaterialPoint& mpt = *mp.ExtractData<FESolutesMaterialPoint>();\n\tif (isol < mpt.m_nsol) {\n\t\treturn -m_qc[isol](mp);\n\t}\n\t\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FESolventSupplyStarling.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBiphasic.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a solvent supply following\n// Starling's equation\nclass FEBIOMIX_API FESolventSupplyStarling :\tpublic FESolventSupply\n{\npublic:\n\t//! constructor\n\tFESolventSupplyStarling(FEModel* pfem);\n\t\n\t//! Solute supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of supply with respect to strain\n\tmat3ds Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to pressure\n\tdouble Tangent_Supply_Pressure(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to concentration\n\tdouble Tangent_Supply_Concentration(FEMaterialPoint& mp, const int isol);\n\n    //! Initialization\n    bool Init() override { return FESolventSupply::Init(); }\n\t\n   \npublic:\n\tFEParamDouble\t\tm_kp;       //!< coefficient of pressure drop\n    FEParamDouble\t\tm_pv;       //!< prescribed (e.g., vascular) pressure\n\tvector<FEParamDouble>\t\tm_qc;       //!< coefficients of concentration drops\n\tvector<FEParamDouble>\t\tm_cv;       //!< prescribed (e.g., vascular) concentrations\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESupplyBinding.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESupplyBinding.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESupplyBinding, FESoluteSupply)\n\tADD_PARAMETER(m_kf , FE_RANGE_GREATER(0.0), \"kf\");\n\tADD_PARAMETER(m_kr , FE_RANGE_GREATER_OR_EQUAL(0.0), \"kr\");\n\tADD_PARAMETER(m_crt, FE_RANGE_GREATER_OR_EQUAL(0.0), \"Rtot\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFESupplyBinding::FESupplyBinding(FEModel* pfem) : FESoluteSupply(pfem)\n{\n\tm_kf = m_kr = m_crt = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply\ndouble FESupplyBinding::Supply(FEMaterialPoint& mp)\n{\n\tdouble crhat = -ReceptorLigandSupply(mp);\n\treturn crhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to strain\ndouble FESupplyBinding::Tangent_Supply_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to referential concentration\ndouble FESupplyBinding::Tangent_Supply_Concentration(FEMaterialPoint &mp)\n{\n\tFESolutesMaterialPoint& pt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble crc = pt.m_sbmr[0];\n\tdouble dcrhatdcr = -m_kf*(m_crt - crc);\n\t\n\treturn dcrhatdcr;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand complex supply\ndouble FESupplyBinding::ReceptorLigandSupply(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble J = et.m_J;\n\tdouble ca = spt.m_ca[0];\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cr = (J-phi0)*ca;\n\tdouble crc = spt.m_sbmr[0];\n\tdouble crchat = m_kf*cr*(m_crt-crc) - m_kr*crc;\n\treturn crchat;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply at steady state\ndouble FESupplyBinding::SupplySS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand concentration at steady-state\ndouble FESupplyBinding::ReceptorLigandConcentrationSS(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble J = et.m_J;\n\tdouble ca = spt.m_ca[0];\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cr = (J-phi0)*ca;\n\tdouble Kd = m_kr/m_kf;\t// dissociation constant\n\tdouble crc = m_crt*cr/(Kd+cr);\n\treturn crc;\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid supply (moles of solid/referential volume/time)\ndouble FESupplyBinding::SolidSupply(FEMaterialPoint& mp)\n{\n\treturn ReceptorLigandSupply(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid concentration (moles of solid/referential volume)\n//! at steady-state\ndouble FESupplyBinding::SolidConcentrationSS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FESupplyBinding.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a solute supply based on\n// receptor-ligand binding kinetics as described by the Langmuir or Hill equation\n\nclass FEBIOMIX_API FESupplyBinding :\tpublic FESoluteSupply\n{\npublic:\n\t//! constructor\n\tFESupplyBinding(FEModel* pfem);\n\t\n\t//! Solute supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of supply with respect to strain\n\tdouble Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to concentration\n\tdouble Tangent_Supply_Concentration(FEMaterialPoint& mp) override;\n\t\n\t//! receptor-ligand complex supply\n\tdouble ReceptorLigandSupply(FEMaterialPoint& mp) override;\n\t\n\t//! Solute supply at steady-state\n\tdouble SupplySS(FEMaterialPoint& pt) override;\n\t\n\t//! receptor-ligand complex concentration at steady-state\n\tdouble ReceptorLigandConcentrationSS(FEMaterialPoint& mp) override;\n\t\n\t//! referential solid supply\n\tdouble SolidSupply(FEMaterialPoint& pt) override;\n\t\n\t//! referential solid volume fraction under steady-state conditions\n\tdouble SolidConcentrationSS(FEMaterialPoint& pt) override;\n\t\npublic:\n\tdouble\tm_kf;\t\t\t//!< forward reaction rate constant\n\tdouble\tm_kr;\t\t\t//!< reverse reaction rate constant\n\tdouble\tm_crt;\t\t\t//!< total receptor concentration (referential)\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESupplyConst.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESupplyConst.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESupplyConst, FESoluteSupply)\n\tADD_PARAMETER(m_supp, \"supp\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFESupplyConst::FESupplyConst(FEModel* pfem) : FESoluteSupply(pfem)\n{\n\tm_supp = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply\ndouble FESupplyConst::Supply(FEMaterialPoint& mp)\n{\n\t// --- constant solubility ---\n\t\n\treturn m_supp;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to strain\ndouble FESupplyConst::Tangent_Supply_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to concentration\ndouble FESupplyConst::Tangent_Supply_Concentration(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand complex supply\ndouble FESupplyConst::ReceptorLigandSupply(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply at steady-state\ndouble FESupplyConst::SupplySS(FEMaterialPoint& mp)\n{\n\t// --- constant solubility ---\n\t\n\treturn m_supp;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand concentration at steady-state\ndouble FESupplyConst::ReceptorLigandConcentrationSS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid supply (moles of solid/referential volume/time)\ndouble FESupplyConst::SolidSupply(FEMaterialPoint& mp)\n{\n\treturn ReceptorLigandSupply(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid concentration (moles of solid/referential volume)\n//! at steady-state\ndouble FESupplyConst::SolidConcentrationSS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n\n"
  },
  {
    "path": "FEBioMix/FESupplyConst.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a constant solute supply\n\nclass FEBIOMIX_API FESupplyConst :\tpublic FESoluteSupply\n{\npublic:\n\t//! constructor\n\tFESupplyConst(FEModel* pfem);\n\t\n\t//! Solute supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of supply with respect to strain\n\tdouble Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to concentration\n\tdouble Tangent_Supply_Concentration(FEMaterialPoint& mp) override;\n\t\n\t//! receptor-ligand complex supply\n\tdouble ReceptorLigandSupply(FEMaterialPoint& mp) override;\n\t\n\t//! Solute supply at steady-state\n\tdouble SupplySS(FEMaterialPoint& pt) override;\n\t\n\t//! receptor-ligand complex concentration at steady-state\n\tdouble ReceptorLigandConcentrationSS(FEMaterialPoint& mp) override;\n\n\t//! referential solid supply\n\tdouble SolidSupply(FEMaterialPoint& pt) override;\n\t\n\t//! referential solid volume fraction under steady-state conditions\n\tdouble SolidConcentrationSS(FEMaterialPoint& pt) override;\n\npublic:\n\tdouble\tm_supp;\t\t\t//!< solute supply\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESupplyMichaelisMenten.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESupplyMichaelisMenten.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESupplyMichaelisMenten, FESoluteSupply)\n\tADD_PARAMETER(m_Vmax, FE_RANGE_GREATER_OR_EQUAL(0.0), \"Vmax\");\n\tADD_PARAMETER(m_Km  , FE_RANGE_GREATER         (0.0), \"Km\"  );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFESupplyMichaelisMenten::FESupplyMichaelisMenten(FEModel* pfem) : FESoluteSupply(pfem)\n{\n\tm_Vmax = m_Km = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply\ndouble FESupplyMichaelisMenten::Supply(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble J = et.m_J;\n\tdouble ca = spt.m_ca[0];\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cr = (J-phi0)*ca;\n\tdouble crhat = -m_Vmax*cr/(m_Km+cr);\n\t\n\treturn crhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to strain\ndouble FESupplyMichaelisMenten::Tangent_Supply_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to referential concentration\ndouble FESupplyMichaelisMenten::Tangent_Supply_Concentration(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble J = et.m_J;\n\tdouble ca = spt.m_ca[0];\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cr = (J-phi0)*ca;\n\tdouble dcrhatdcr = -m_Vmax*m_Km/SQR(m_Km+cr);\n\t\n\treturn dcrhatdcr;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand complex supply\ndouble FESupplyMichaelisMenten::ReceptorLigandSupply(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply at steady state\ndouble FESupplyMichaelisMenten::SupplySS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand concentration at steady-state\ndouble FESupplyMichaelisMenten::ReceptorLigandConcentrationSS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid supply (moles of solid/referential volume/time)\ndouble FESupplyMichaelisMenten::SolidSupply(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid concentration (moles of solid/referential volume)\n//! at steady-state\ndouble FESupplyMichaelisMenten::SolidConcentrationSS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n\n"
  },
  {
    "path": "FEBioMix/FESupplyMichaelisMenten.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a solute supply based on\n// Michaelis-Menten kinetics\n\nclass FEBIOMIX_API FESupplyMichaelisMenten :\tpublic FESoluteSupply\n{\npublic:\n\t//! constructor\n\tFESupplyMichaelisMenten(FEModel* pfem);\n\t\n\t//! Solute supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of supply with respect to strain\n\tdouble Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to concentration\n\tdouble Tangent_Supply_Concentration(FEMaterialPoint& mp) override;\n\t\n\t//! receptor-ligand complex supply\n\tdouble ReceptorLigandSupply(FEMaterialPoint& mp) override;\n\t\n\t//! Solute supply at steady-state\n\tdouble SupplySS(FEMaterialPoint& pt) override;\n\t\n\t//! receptor-ligand complex concentration at steady-state\n\tdouble ReceptorLigandConcentrationSS(FEMaterialPoint& mp) override;\n\t\n\t//! referential solid supply\n\tdouble SolidSupply(FEMaterialPoint& pt) override;\n\t\n\t//! referential solid volume fraction under steady-state conditions\n\tdouble SolidConcentrationSS(FEMaterialPoint& pt) override;\n\t\npublic:\n\tdouble\tm_Vmax;\t\t\t//!< maximum uptake rate\n\tdouble\tm_Km;\t\t\t//!< concentration at which half-maximum rate occurs\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FESupplySynthesisBinding.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESupplySynthesisBinding.h\"\n\n// define the material parameters\nBEGIN_FECORE_CLASS(FESupplySynthesisBinding, FESoluteSupply)\n\tADD_PARAMETER(m_supp, \"supp\");\n\tADD_PARAMETER(m_kf  , FE_RANGE_GREATER         (0.0), \"kf\"  );\n\tADD_PARAMETER(m_kr  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"kr\"  );\n\tADD_PARAMETER(m_crt , FE_RANGE_GREATER_OR_EQUAL(0.0), \"Rtot\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! Constructor. \nFESupplySynthesisBinding::FESupplySynthesisBinding(FEModel* pfem) : FESoluteSupply(pfem)\n{\n\tm_supp = m_kf = m_kr = m_crt = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply\ndouble FESupplySynthesisBinding::Supply(FEMaterialPoint& mp)\n{\n\tdouble crhat = m_supp-ReceptorLigandSupply(mp);\n\treturn crhat;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to strain\ndouble FESupplySynthesisBinding::Tangent_Supply_Strain(FEMaterialPoint &mp)\n{\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Tangent of solute supply with respect to referential concentration\ndouble FESupplySynthesisBinding::Tangent_Supply_Concentration(FEMaterialPoint &mp)\n{\n\tFESolutesMaterialPoint& pt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble crc = pt.m_sbmr[0];\n\tdouble dcrhatdcr = -m_kf*(m_crt - crc);\n\t\n\treturn dcrhatdcr;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand complex supply\ndouble FESupplySynthesisBinding::ReceptorLigandSupply(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble J = et.m_J;\n\tdouble ca = spt.m_ca[0];\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cr = (J-phi0)*ca;\n\tdouble crc = spt.m_sbmr[0];\n\tdouble crchat = m_kf*cr*(m_crt-crc) - m_kr*crc;\n\treturn crchat;\n}\n\n//-----------------------------------------------------------------------------\n//! Solute supply at steady state\ndouble FESupplySynthesisBinding::SupplySS(FEMaterialPoint& mp)\n{\n\treturn m_supp;\n}\n\n//-----------------------------------------------------------------------------\n//! Receptor-ligand concentration at steady-state\ndouble FESupplySynthesisBinding::ReceptorLigandConcentrationSS(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& et = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\tdouble J = et.m_J;\n\tdouble ca = spt.m_ca[0];\n\tdouble phi0 = ppt.m_phi0t;\n\tdouble cr = (J-phi0)*ca;\n\tdouble Kd = m_kr/m_kf;\t// dissociation constant\n\tdouble crc = m_crt*cr/(Kd+cr);\n\treturn crc;\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid supply (moles of solid/referential volume/time)\ndouble FESupplySynthesisBinding::SolidSupply(FEMaterialPoint& mp)\n{\n\treturn ReceptorLigandSupply(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Referential solid concentration (moles of solid/referential volume)\n//! at steady-state\ndouble FESupplySynthesisBinding::SolidConcentrationSS(FEMaterialPoint& mp)\n{\n\treturn 0;\n}\n"
  },
  {
    "path": "FEBioMix/FESupplySynthesisBinding.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBiphasicSolute.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a material that has a solute supply based on\n// receptor-ligand binding kinetics as described by the Langmuir or Hill equation\n// and also includes a constant supply rate (synthesis when positive, degradation\n// when negative).\n\nclass FEBIOMIX_API FESupplySynthesisBinding : public FESoluteSupply\n{\npublic:\n\t//! constructor\n\tFESupplySynthesisBinding(FEModel* pfem);\n\t\n\t//! Solute supply\n\tdouble Supply(FEMaterialPoint& pt) override;\n\t\n\t//! Tangent of supply with respect to strain\n\tdouble Tangent_Supply_Strain(FEMaterialPoint& mp) override;\n\t\n\t//! Tangent of supply with respect to concentration\n\tdouble Tangent_Supply_Concentration(FEMaterialPoint& mp) override;\n\t\n\t//! receptor-ligand complex supply\n\tdouble ReceptorLigandSupply(FEMaterialPoint& mp) override;\n\t\n\t//! Solute supply at steady-state\n\tdouble SupplySS(FEMaterialPoint& pt) override;\n\t\n\t//! receptor-ligand complex concentration at steady-state\n\tdouble ReceptorLigandConcentrationSS(FEMaterialPoint& mp) override;\n\n\t//! referential solid supply\n\tdouble SolidSupply(FEMaterialPoint& pt) override;\n\t\n\t//! referential solid volume fraction under steady-state conditions\n\tdouble SolidConcentrationSS(FEMaterialPoint& pt) override;\n\t\npublic:\n\tdouble\tm_supp;\t\t\t//!< synthesis rate\n\tdouble\tm_kf;\t\t\t//!< forward reaction rate constant\n\tdouble\tm_kr;\t\t\t//!< reverse reaction rate constant\n\tdouble\tm_crt;\t\t\t//!< total receptor concentration (referential)\n\t\n\t// declare parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FETiedBiphasicInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedBiphasicInterface.h\"\n#include \"FEBiphasic.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FETiedBiphasicInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"             )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n\tADD_PARAMETER(m_gtol     , \"gaptol\"             );\n\tADD_PARAMETER(m_ptol     , \"ptol\"               );\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_epsp     , \"pressure_penalty\"   );\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n\tADD_PARAMETER(m_srad     , \"search_radius\"      );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicContactPoint::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactPoint::Serialize(ar);\n\tar & m_Gap;\n\tar & m_dg;\n\tar & m_nu;\n\tar & m_rs;\n\tar & m_Lmd;\n\tar & m_tr;\n\tar & m_epsn;\n\tar & m_epsp;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedBiphasicSurface\n//-----------------------------------------------------------------------------\n\nFETiedBiphasicSurface::FETiedBiphasicSurface(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{ \n\tm_bporo = false;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FETiedBiphasicSurface::CreateMaterialPoint()\n{\n\treturn new FETiedBiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedBiphasicSurface::Init()\n{\n\t// initialize surface data first\n\tif (FEBiphasicContactSurface::Init() == false) return false;\n\t\n    // allocate node normals\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    \n\t// determine biphasic status\n\tm_poro.resize(Elements(),false);\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& se = Element(i);\n\t\t\n\t\t// get the element this surface element belongs to\n\t\tFEElement* pe = se.m_elem[0].pe;\n\t\tif (pe)\n\t\t{\n\t\t\t// get the material\n\t\t\tFEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n\t\t\t\n\t\t\t// see if this is a poro-elastic element\n\t\t\tFEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n\t\t\tif (biph) {\n\t\t\t\tm_poro[i] = true;\n\t\t\t\tm_bporo = true;\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the \n//! element normals at the node\n\nvoid FETiedBiphasicSurface::UpdateNodeNormals()\n{\n\tint N = Nodes(), i, j, ne, jp1, jm1;\n\tvec3d y[FEElement::MAX_NODES], n;\n\t\n\t// zero nodal normals\n\tzero(m_nn);\n\t\n\t// loop over all elements\n\tfor (i=0; i<Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tne = el.Nodes();\n\t\t\n\t\t// get the nodal coordinates\n\t\tfor (j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n\t\t\n\t\t// calculate the normals\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\tjp1 = (j+1)%ne;\n\t\t\tjm1 = (j+ne-1)%ne;\n\t\t\tn = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n\t\t\tm_nn[el.m_lnode[j]] += n;\n\t\t}\n\t}\n\t\n\t// normalize all vectors\n\tfor (i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicSurface::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_bporo;\n\tar & m_poro;\n\tar & m_nn;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicSurface::GetVectorGap(int nface, vec3d& pg)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pg = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FETiedBiphasicContactPoint& data = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpg += data.m_dg;\n\t}\n    pg /= ni;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicSurface::GetContactTraction(int nface, vec3d& pt)\n{\n    FESurfaceElement& el = Element(nface);\n    int ni = el.GaussPoints();\n    pt = vec3d(0,0,0);\n\tfor (int k = 0; k < ni; ++k)\n\t{\n        FETiedBiphasicContactPoint& data = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(k));\n\t\tpt += data.m_tr;\n\t}\n    pt /= ni;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedBiphasicInterface\n//-----------------------------------------------------------------------------\n\nFETiedBiphasicInterface::FETiedBiphasicInterface(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\t\n\t// initial values\n\tm_knmult = 1;\n\tm_atol = 0.1;\n\tm_epsn = 1;\n\tm_epsp = 1;\n\tm_btwo_pass = false;\n\tm_stol = 0.01;\n\tm_bsymm = true;\n\tm_srad = 1.0;\n\tm_gtol = -1;\t// we use augmentation tolerance by default\n\tm_ptol = -1;\t// we use augmentation tolerance by default\n\tm_bautopen = false;\n    m_bupdtpen = false;\n\t\n\tm_naugmin = 0;\n\tm_naugmax = 10;\n\n\tm_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n\t\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFETiedBiphasicInterface::~FETiedBiphasicInterface()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedBiphasicInterface::Init()\n{\n\t// initialize surface data\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FETiedBiphasicInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_P = fem.GetDOFIndex(\"p\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(7*FEElement::MAX_NODES*2);\n\t\t\t\t\t\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFETiedBiphasicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFETiedBiphasicSurface& ms = (np == 0? m_ms : m_ss);\n\t\t\t\t\t\t\n\t\tint ni = 0, k, l;\n\t\tfor (int j=0; j<ss.Elements(); ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\t\t\tint nint = se.GaussPoints();\n\t\t\tint* sn = &se.m_node[0];\n\t\t\tfor (k=0; k<nint; ++k, ++ni)\n\t\t\t{\n                FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*se.GetMaterialPoint(k));\n\t\t\t\tFESurfaceElement* pe = pt.m_pme;\n\t\t\t\tif (pe != 0)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pe;\n\t\t\t\t\tint* mn = &me.m_node[0];\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tassign(lm, -1);\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tint nseln = se.Nodes();\n\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(sn[l]).m_ID;\n\t\t\t\t\t\tlm[7*l  ] = id[dof_X];\n\t\t\t\t\t\tlm[7*l+1] = id[dof_Y];\n\t\t\t\t\t\tlm[7*l+2] = id[dof_Z];\n\t\t\t\t\t\tlm[7*l+3] = id[dof_P];\n\t\t\t\t\t\tlm[7*l+4] = id[dof_RU];\n\t\t\t\t\t\tlm[7*l+5] = id[dof_RV];\n\t\t\t\t\t\tlm[7*l+6] = id[dof_RW];\n\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& id = mesh.Node(mn[l]).m_ID;\n\t\t\t\t\t\tlm[7*(l+nseln)  ] = id[dof_X];\n\t\t\t\t\t\tlm[7*(l+nseln)+1] = id[dof_Y];\n\t\t\t\t\t\tlm[7*(l+nseln)+2] = id[dof_Z];\n\t\t\t\t\t\tlm[7*(l+nseln)+3] = id[dof_P];\n\t\t\t\t\t\tlm[7*(l+nseln)+4] = id[dof_RU];\n\t\t\t\t\t\tlm[7*(l+nseln)+5] = id[dof_RV];\n\t\t\t\t\t\tlm[7*(l+nseln)+6] = id[dof_RW];\n\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\tK.build_add(lm);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        if (m_ss.m_bporo) CalcAutoPressurePenalty(m_ss);\n        if (m_btwo_pass) {\n            CalcAutoPenalty(m_ms);\n            if (m_ms.m_bporo) CalcAutoPressurePenalty(m_ms);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n    UpdateAutoPenalty();\n    \n\t// project the surfaces onto each other\n\t// this will evaluate the gap functions in the reference configuration\n\tInitialProjection(m_ss, m_ms);\n\tif (m_btwo_pass) InitialProjection(m_ms, m_ss);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::CalcAutoPenalty(FETiedBiphasicSurface& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::CalcAutoPressurePenalty(FETiedBiphasicSurface& s)\n{\n\t// loop over all surface elements\n\tfor (int i=0; i<s.Elements(); ++i)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = s.Element(i);\n\t\t\n\t\t// calculate a penalty\n\t\tdouble eps = AutoPressurePenalty(el, s);\n\t\t\n\t\t// assign to integation points of surface element\n\t\tint nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FETiedBiphasicInterface::AutoPressurePenalty(FESurfaceElement& el, FETiedBiphasicSurface& s)\n{\n\t// get the mesh\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n\t\n\t// get the element this surface element belongs to\n\tFEElement* pe = el.m_elem[0].pe;\n\tif (pe == 0) return 0.0;\n\n\t// get the material\n\tFEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n\t\t\n\t// see if this is a poro-elastic element\n\tFEBiphasic* biph = dynamic_cast<FEBiphasic*> (pm);\n\tif (biph == 0) return 0.0;\n\n\t// get a material point\n\tFEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n\tFEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\t\n\t// setup the material point\n\tept.m_F = mat3dd(1.0);\n\tept.m_J = 1;\n\tept.m_s.zero();\n\t\t\t\n\t// if this is a poroelastic element, then get the permeability tensor\n\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\tpt.m_p = 0;\n\tpt.m_w = vec3d(0,0,0);\n\t\t\t\n\tdouble K[3][3];\n\tbiph->Permeability(K, mp);\n\t\t\t\n\tdouble eps = n.x*(K[0][0]*n.x+K[0][1]*n.y+K[0][2]*n.z)\n\t+n.y*(K[1][0]*n.x+K[1][1]*n.y+K[1][2]*n.z)\n\t+n.z*(K[2][0]*n.x+K[2][1]*n.y+K[2][2]*n.z);\n\t\n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n\treturn eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\n// Perform initial projection between tied surfaces in reference configuration\nvoid FETiedBiphasicInterface::InitialProjection(FETiedBiphasicSurface& ss, FETiedBiphasicSurface& ms)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tdouble R = m_srad*mesh.GetBoundingBox().radius();\n\t\n\tFESurfaceElement* pme;\n\tvec3d r, nu;\n\tdouble rs[2];\n\n\t// initialize projection data\n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(m_srad);\n\tnp.Init();\n\t\n\t// loop over all integration points\n\tint n = 0;\n\tfor (int i=0; i<ss.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = ss.Element(i);\n\t\t\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\tfor (int j=0; j<nint; ++j, ++n)\n\t\t{\n\t\t\t// calculate the global position of the integration point\n\t\t\tr = ss.Local2Global(el, j);\n\t\t\t\n\t\t\t// calculate the normal at this integration point\n\t\t\tnu = ss.SurfaceNormal(el, j);\n\t\t\t\n\t\t\t// find the intersection point with the secondary surface\n\t\t\tpme = np.Project2(r, nu, rs);\n\t\t\t\n            FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_pme = pme;\n\t\t\tpt.m_rs[0] = rs[0];\n\t\t\tpt.m_rs[1] = rs[1];\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// the node could potentially be in contact\n\t\t\t\t// find the global location of the intersection point\n\t\t\t\tvec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n\t\t\t\t\n\t\t\t\t// calculate the gap function\n\t\t\t\tpt.m_Gap = q - r;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// the node is not in contact\n\t\t\t\tpt.m_Gap = vec3d(0,0,0);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate gap functions for position and fluid pressure\nvoid FETiedBiphasicInterface::ProjectSurface(FETiedBiphasicSurface& ss, FETiedBiphasicSurface& ms)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tFESurfaceElement* pme;\n\tvec3d r;\n\t\n\tdouble ps[FEElement::MAX_NODES], p1;\n\t\n\t// loop over all integration points\n\tfor (int i=0; i<ss.Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = ss.Element(i);\n\t\tbool sporo = ss.m_poro[i];\n\t\t\n\t\tint ne = el.Nodes();\n\t\tint nint = el.GaussPoints();\n\t\t\n\t\t// get the nodal pressures\n\t\tif (sporo)\n\t\t{\n\t\t\tfor (int j=0; j<ne; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n\t\t}\n\t\t\n\t\tfor (int j=0; j<nint; ++j)\n\t\t{\n            FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n\t\t\t// calculate the global position of the integration point\n\t\t\tr = ss.Local2Global(el, j);\n\t\t\t\n\t\t\t// get the pressure at the integration point\n\t\t\tif (sporo) p1 = el.eval(ps, j);\n\t\t\t\n\t\t\t// calculate the normal at this integration point\n\t\t\tpt.m_nu = ss.SurfaceNormal(el, j);\n\n\t\t\t// if this node is tied, evaluate gap functions\n\t\t\tpme = pt.m_pme;\n\t\t\tif (pme)\n\t\t\t{\n\t\t\t\t// find the global location of the intersection point\n\t\t\t\tvec3d q = ms.Local2Global(*pme, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t\n\t\t\t\t// calculate the gap function\n\t\t\t\tvec3d g = q - r;\n\t\t\t\tpt.m_dg = g - pt.m_Gap;\n//                pt.m_gap = pt.m_dg.unit();\n\t\t\t\t\n\t\t\t\t// calculate the pressure gap function\n\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\tif (sporo && mporo) {\n\t\t\t\t\tdouble pm[FEElement::MAX_NODES];\n\t\t\t\t\tfor (int k=0; k<pme->Nodes(); ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n\t\t\t\t\tdouble p2 = pme->eval(pm, pt.m_rs[0], pt.m_rs[1]);\n\t\t\t\t\tpt.m_pg = p1 - p2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// the node is not tied\n\t\t\t\tpt.m_dg = vec3d(0,0,0);\n//                pt.m_gap = 0;\n\t\t\t\tif (sporo) pt.m_pg = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FETiedBiphasicInterface::Update()\n{\t\n\t// project the surfaces onto each other\n\t// this will update the gap functions as well\n\tProjectSurface(m_ss, m_ms);\n\tif (m_btwo_pass) ProjectSurface(m_ms, m_ss);\n\t\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint i, j, k;\n\tvector<int> sLM, mLM, LM, en;\n\tvector<double> fe;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN];\n\tdouble N[8*MN];\n\n\t// get time step\n\t// if we're using the symmetric formulation\n\t// we need to multiply with the timestep\n    double dt = GetFEModel()->GetTime().timeIncrement;\n\t\n    // Update auto-penalty if requested\n    if (m_bupdtpen && GetFEModel()->GetCurrentStep()->GetFESolver()->m_niter == 0) UpdateAutoPenalty();\n    \n\t// loop over the nr of passes\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\t// get primary and secondary surface\n\t\tFETiedBiphasicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFETiedBiphasicSurface& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all primary surface elements\n\t\tfor (i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the surface element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\t\n\t\t\tbool sporo = ss.m_poro[i];\n\t\t\t\n\t\t\t// get the nr of nodes and integration points\n\t\t\tint nseln = se.Nodes();\n\t\t\tint nint = se.GaussPoints();\n\t\t\t\n\t\t\t// copy the LM vector; we'll need it later\n\t\t\tss.UnpackLM(se, sLM);\n\t\t\t\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tss.CoBaseVectors(se, j, g);\n\t\t\t\t\n\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\t\t\t\t\n\t\t\t\t// integration weights\n\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t}\n\t\t\t\n\t\t\t// loop over all integration points\n\t\t\t// note that we are integrating over the current surface\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n                FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\t// get the secondary surface element\n\t\t\t\t\tFESurfaceElement& me = *pme;\n\t\t\t\t\t\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\t\t\n\t\t\t\t\t// get the nr of secondary surface element nodes\n\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\n\t\t\t\t\t// copy LM vector\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\t\t\t\t\t\n\t\t\t\t\t// calculate degrees of freedom\n\t\t\t\t\tint ndof = 3*(nseln + nmeln);\n\t\t\t\t\t\n\t\t\t\t\t// build the LM vector\n\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// build the en vector\n\t\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\t\tfor (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\t\t\t\t\t\n\t\t\t\t\t// get element shape functions\n\t\t\t\t\tHs = se.H(j);\n\t\t\t\t\t\n\t\t\t\t\t// get secondary surface element shape functions\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\t\t\t\t\t\n\t\t\t\t\t// get normal vector\n\t\t\t\t\tvec3d nu = pt.m_nu;\n\t\t\t\t\t\n\t\t\t\t\t// gap function\n\t\t\t\t\tvec3d dg = pt.m_dg;\n\t\t\t\t\t\n\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\tvec3d Lm = pt.m_Lmd;\n\t\t\t\t\t\n\t\t\t\t\t// penalty \n\t\t\t\t\tdouble eps = m_epsn*pt.m_epsn;\n\t\t\t\t\t\n\t\t\t\t\t// contact traction\n\t\t\t\t\tvec3d t = Lm + dg*eps;\n                    pt.m_tr = t;\n\t\t\t\t\t\n\t\t\t\t\t// calculate the force vector\n\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\tzero(fe);\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[3*k  ] = Hs[k]*t.x;\n\t\t\t\t\t\tN[3*k+1] = Hs[k]*t.y;\n\t\t\t\t\t\tN[3*k+2] = Hs[k]*t.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tN[3*(k+nseln)  ] = -Hm[k]*t.x;\n\t\t\t\t\t\tN[3*(k+nseln)+1] = -Hm[k]*t.y;\n\t\t\t\t\t\tN[3*(k+nseln)+2] = -Hm[k]*t.z;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n\t\t\t\t\t\n\t\t\t\t\t// assemble the global residual\n\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t\n\t\t\t\t\t// do the biphasic stuff\n\t\t\t\t\t// TODO: I should only do this when the node is actually in contact\n\t\t\t\t\tif (sporo && mporo && pt.m_pme)\n\t\t\t\t\t{\n\t\t\t\t\t\t// calculate nr of pressure dofs\n\t\t\t\t\t\tint ndof = nseln + nmeln;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// calculate the flow rate\n\t\t\t\t\t\tdouble epsp = m_epsp*pt.m_epsp;\n\t\t\t\t\t\t\n\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// fill the LM\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) LM[k        ] = sLM[3*nseln+k];\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) LM[k + nseln] = mLM[3*nmeln+k];\n\t\t\t\t\t\t\n\t\t\t\t\t\t// fill the force array\n\t\t\t\t\t\tfe.resize(ndof);\n\t\t\t\t\t\tzero(fe);\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<ndof; ++k) fe[k] += dt*wn*N[k]*detJ[j]*w[j];\n\t\t\t\t\t\t\n\t\t\t\t\t\t// assemble residual\n\t\t\t\t\t\tR.Assemble(en, LM, fe);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint i, j, k, l;\n\tvector<int> sLM, mLM, LM, en;\n\tconst int MN = FEElement::MAX_NODES;\n\tdouble detJ[MN], w[MN], *Hs, Hm[MN], pt[MN], dpr[MN], dps[MN];\n\tFEElementMatrix ke;\n\n\t// get time step\n    double dt = GetFEModel()->GetTime().timeIncrement;\n\t\n\t// get the mesh\n\tFEMesh* pm = m_ss.GetMesh();\n\t\n\t// see how many reformations we've had to do so far\n\tint nref = GetSolver()->m_nref;\n\t\n\t// set higher order stiffness mutliplier\n\t// NOTE: this algrotihm doesn't really need this\n\t// but I've added this functionality to compare with the other contact \n\t// algorithms and to see the effect of the different stiffness contributions\n\tdouble knmult = m_knmult;\n\tif (m_knmult < 0)\n\t{\n\t\tint ni = int(-m_knmult);\n\t\tif (nref >= ni)\n\t\t{\n\t\t\tknmult = 1; \n\t\t\tfeLog(\"Higher order stiffness terms included.\\n\");\n\t\t}\n\t\telse knmult = 0;\n\t}\n\t\n\t// do single- or two-pass\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np < npass; ++np)\n\t{\n\t\t// get the primary and secondary surface\n\t\tFETiedBiphasicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFETiedBiphasicSurface& ms = (np == 0? m_ms : m_ss);\n\t\t\n\t\t// loop over all primary surface elements\n\t\tfor (i=0; i<ss.Elements(); ++i)\n\t\t{\n\t\t\t// get the next element\n\t\t\tFESurfaceElement& se = ss.Element(i);\n\t\t\t\n\t\t\tbool sporo = ss.m_poro[i];\n\t\t\t\n\t\t\t// get nr of nodes and integration points\n\t\t\tint nseln = se.Nodes();\n\t\t\tint nint = se.GaussPoints();\n\t\t\t\n\t\t\t// nodal pressures\n\t\t\tdouble pn[FEElement::MAX_NODES];\n\t\t\tfor (j=0; j<nseln; ++j) pn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n\t\t\t\n\t\t\t// copy the LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\t\t\t\n\t\t\t// we calculate all the metrics we need before we\n\t\t\t// calculate the nodal forces\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n\t\t\t\t// get the base vectors\n\t\t\t\tvec3d g[2];\n\t\t\t\tss.CoBaseVectors(se, j, g);\n\t\t\t\t\n\t\t\t\t// jacobians: J = |g0xg1|\n\t\t\t\tdetJ[j] = (g[0] ^ g[1]).norm();\n\t\t\t\t\n\t\t\t\t// integration weights\n\t\t\t\tw[j] = se.GaussWeights()[j];\n\t\t\t\t\n\t\t\t\t// pressure\n\t\t\t\tif (sporo)\n\t\t\t\t{\n\t\t\t\t\tpt[j] = se.eval(pn, j);\n\t\t\t\t\tdpr[j] = se.eval_deriv1(pn, j);\n\t\t\t\t\tdps[j] = se.eval_deriv2(pn, j);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// loop over all integration points\n\t\t\tfor (j=0; j<nint; ++j)\n\t\t\t{\n                FETiedBiphasicContactPoint& pt = static_cast<FETiedBiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// get the secondary surface element\n\t\t\t\tFESurfaceElement* pme = pt.m_pme;\n\t\t\t\tif (pme)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& me = *pme;\n\t\t\t\t\t\n\t\t\t\t\tbool mporo = ms.m_poro[pme->m_lid];\n\t\t\t\t\t\n\t\t\t\t\t// get the nr of secondary surface nodes\n\t\t\t\t\tint nmeln = me.Nodes();\n\t\t\t\t\t\n\t\t\t\t\t// nodal pressure\n\t\t\t\t\tdouble pm[FEElement::MAX_NODES];\n\t\t\t\t\tif (mporo) for (k=0; k<nmeln; ++k) pm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n\t\t\t\t\t\n\t\t\t\t\t// copy the LM vector\n\t\t\t\t\tms.UnpackLM(me, mLM);\n\t\t\t\t\t\n\t\t\t\t\tint ndpn;\t// number of dofs per node\n\t\t\t\t\tint ndof;\t// number of dofs in stiffness matrix\n\t\t\t\t\t\n\t\t\t\t\tif (sporo && mporo) {\n\t\t\t\t\t\t// calculate degrees of freedom for biphasic-on-biphasic contact\n\t\t\t\t\t\tndpn = 4;\n\t\t\t\t\t\tndof = ndpn*(nseln+nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[4*k  ] = sLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[4*k+1] = sLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[4*k+2] = sLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[4*k+3] = sLM[3*nseln+k];\t\t// p-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[4*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n\t\t\t\t\t\t\tLM[4*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n\t\t\t\t\t\t\tLM[4*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n\t\t\t\t\t\t\tLM[4*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\telse {\n\t\t\t\t\t\t// calculate degrees of freedom for biphasic-on-elastic or elastic-on-elastic contact\n\t\t\t\t\t\tndpn = 3;\n\t\t\t\t\t\tndof = ndpn*(nseln + nmeln);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// build the LM vector\n\t\t\t\t\t\tLM.resize(ndof);\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3*k  ] = sLM[3*k  ];\n\t\t\t\t\t\t\tLM[3*k+1] = sLM[3*k+1];\n\t\t\t\t\t\t\tLM[3*k+2] = sLM[3*k+2];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLM[3*(k+nseln)  ] = mLM[3*k  ];\n\t\t\t\t\t\t\tLM[3*(k+nseln)+1] = mLM[3*k+1];\n\t\t\t\t\t\t\tLM[3*(k+nseln)+2] = mLM[3*k+2];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// build the en vector\n\t\t\t\t\ten.resize(nseln+nmeln);\n\t\t\t\t\tfor (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n\t\t\t\t\t\n\t\t\t\t\t// shape functions\n\t\t\t\t\tHs = se.H(j);\n\t\t\t\t\t\n\t\t\t\t\t// secondary surface element shape functions\n\t\t\t\t\tdouble r = pt.m_rs[0];\n\t\t\t\t\tdouble s = pt.m_rs[1];\n\t\t\t\t\tme.shape_fnc(Hm, r, s);\n\t\t\t\t\t\n\t\t\t\t\t// get normal vector\n\t\t\t\t\tvec3d nu = pt.m_nu;\n\t\t\t\t\t\n\t\t\t\t\t// gap function\n\t\t\t\t\tvec3d dg = pt.m_dg;\n\t\t\t\t\t\n\t\t\t\t\t// lagrange multiplier\n\t\t\t\t\tvec3d Lm = pt.m_Lmd;\n\t\t\t\t\t\n\t\t\t\t\t// penalty \n\t\t\t\t\tdouble eps = m_epsn*pt.m_epsn;\n\t\t\t\t\t\n\t\t\t\t\t// contact traction\n\t\t\t\t\tvec3d t = Lm + dg*eps;\n\t\t\t\t\t\n\t\t\t\t\t// create the stiffness matrix\n\t\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\t\t\n\t\t\t\t\t// --- S O L I D - S O L I D   C O N T A C T ---\n\t\t\t\t\t\n\t\t\t\t\t// a. I-term\n\t\t\t\t\t//------------------------------------\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nseln; ++k) {\n\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l    ] += eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l + 1] += eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l + 2] += eps*Hs[k]*Hs[l]*detJ[j]*w[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*(nseln+l)    ] += -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*(nseln+l) + 1] += -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*(nseln+l) + 2] += -eps*Hs[k]*Hm[l]*detJ[j]*w[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (k=0; k<nmeln; ++k) {\n\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l    ] += -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l + 1] += -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l + 2] += -eps*Hm[k]*Hs[l]*detJ[j]*w[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*(nseln+l)    ] += eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*(nseln+l) + 1] += eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*(nseln+l) + 2] += eps*Hm[k]*Hm[l]*detJ[j]*w[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// b. A-term\n\t\t\t\t\t//-------------------------------------\n\t\t\t\t\t\n\t\t\t\t\tdouble* Gr = se.Gr(j);\n\t\t\t\t\tdouble* Gs = se.Gs(j);\n\t\t\t\t\tvec3d gs[2];\n\t\t\t\t\tss.CoBaseVectors(se, j, gs);\n\t\t\t\t\t\n\t\t\t\t\tvec3d as[FEElement::MAX_NODES];\n\t\t\t\t\tmat3d As[FEElement::MAX_NODES];\n\t\t\t\t\tfor (l=0; l<nseln; ++l) {\n\t\t\t\t\t\tas[l] = nu ^ (gs[1]*Gr[l] - gs[0]*Gs[l]);\n\t\t\t\t\t\tAs[l] = t & as[l];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t{\n\t\t\t\t\t\t// non-symmetric\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l    ] += Hs[k]*As[l](0,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l + 1] += Hs[k]*As[l](0,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l + 2] += Hs[k]*As[l](0,2)*w[j];\n\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l    ] += Hs[k]*As[l](1,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l + 1] += Hs[k]*As[l](1,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l + 2] += Hs[k]*As[l](1,2)*w[j];\n\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l    ] += Hs[k]*As[l](2,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l + 1] += Hs[k]*As[l](2,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l + 2] += Hs[k]*As[l](2,2)*w[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l    ] += -Hm[k]*As[l](0,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l + 1] += -Hm[k]*As[l](0,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l + 2] += -Hm[k]*As[l](0,2)*w[j];\n\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l    ] += -Hm[k]*As[l](1,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l + 1] += -Hm[k]*As[l](1,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l + 2] += -Hm[k]*As[l](1,2)*w[j];\n\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l    ] += -Hm[k]*As[l](2,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l + 1] += -Hm[k]*As[l](2,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l + 2] += -Hm[k]*As[l](2,2)*w[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\telse \n\t\t\t\t\t{\n\t\t\t\t\t\t// symmetric\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l    ] += 0.5*(Hs[k]*As[l](0,0)+Hs[l]*As[k](0,0))*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l + 1] += 0.5*(Hs[k]*As[l](0,1)+Hs[l]*As[k](1,0))*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*l + 2] += 0.5*(Hs[k]*As[l](0,2)+Hs[l]*As[k](2,0))*w[j];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l    ] += 0.5*(Hs[k]*As[l](1,0)+Hs[l]*As[k](0,1))*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l + 1] += 0.5*(Hs[k]*As[l](1,1)+Hs[l]*As[k](1,1))*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*l + 2] += 0.5*(Hs[k]*As[l](1,2)+Hs[l]*As[k](2,1))*w[j];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l    ] += 0.5*(Hs[k]*As[l](2,0)+Hs[l]*As[k](0,2))*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l + 1] += 0.5*(Hs[k]*As[l](2,1)+Hs[l]*As[k](1,2))*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*l + 2] += 0.5*(Hs[k]*As[l](2,2)+Hs[l]*As[k](2,2))*w[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l    ] += -0.5*Hm[k]*As[l](0,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l + 1] += -0.5*Hm[k]*As[l](0,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k)    ][ndpn*l + 2] += -0.5*Hm[k]*As[l](0,2)*w[j];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l    ] += -0.5*Hm[k]*As[l](1,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l + 1] += -0.5*Hm[k]*As[l](1,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 1][ndpn*l + 2] += -0.5*Hm[k]*As[l](1,2)*w[j];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l    ] += -0.5*Hm[k]*As[l](2,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l + 1] += -0.5*Hm[k]*As[l](2,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*(nseln+k) + 2][ndpn*l + 2] += -0.5*Hm[k]*As[l](2,2)*w[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*(nseln+l)    ] += -0.5*Hm[l]*As[k](0,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*(nseln+l) + 1] += -0.5*Hm[l]*As[k](1,0)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k    ][ndpn*(nseln+l) + 2] += -0.5*Hm[l]*As[k](2,0)*w[j];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*(nseln+l)    ] += -0.5*Hm[l]*As[k](0,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*(nseln+l) + 1] += -0.5*Hm[l]*As[k](1,1)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 1][ndpn*(nseln+l) + 2] += -0.5*Hm[l]*As[k](2,1)*w[j];\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*(nseln+l)    ] += -0.5*Hm[l]*As[k](0,2)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*(nseln+l) + 1] += -0.5*Hm[l]*As[k](1,2)*w[j];\n\t\t\t\t\t\t\t\tke[ndpn*k + 2][ndpn*(nseln+l) + 2] += -0.5*Hm[l]*As[k](2,2)*w[j];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t\n\t\t\t\t\t// --- B I P H A S I C   S T I F F N E S S ---\n\t\t\t\t\tif (sporo && mporo)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble epsp = (pt.m_pme) ? m_epsp*pt.m_epsp : 0.;\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- S O L I D - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\t// b. A-term\n\t\t\t\t\t\t//-------------------------------------\n\n\t\t\t\t\t\tdouble wn = pt.m_Lmp + epsp*pt.m_pg;\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!m_bsymm)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// non-symmetric\n\t\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l) {\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l  ] += dt*w[j]*wn*Hs[k]*as[l].x;\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+1] += dt*w[j]*wn*Hs[k]*as[l].y;\n\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+2] += dt*w[j]*wn*Hs[k]*as[l].z;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l) {\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l  ] += -dt*w[j]*wn*Hm[k]*as[l].x;\n\t\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l+1] += -dt*w[j]*wn*Hm[k]*as[l].y;\n\t\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l+2] += -dt*w[j]*wn*Hm[k]*as[l].z;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// symmetric\n\t\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l) {\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l  ] += dt*w[j]*wn*0.5*(Hs[k]*as[l].x+Hs[l]*as[k].x);\n\t\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+1] += dt*w[j]*wn*0.5*(Hs[k]*as[l].y+Hs[l]*as[k].y);\n\t\t\t\t\t\t\t\t\t\tke[4*k + 3][4*l+2] += dt*w[j]*wn*0.5*(Hs[k]*as[l].z+Hs[l]*as[k].z);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l) {\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l  ] += -dt*w[j]*wn*0.5*Hm[k]*as[l].x;\n\t\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l+1] += -dt*w[j]*wn*0.5*Hm[k]*as[l].y;\n\t\t\t\t\t\t\t\t\t\tke[4*(k+nseln) + 3][4*l+2] += -dt*w[j]*wn*0.5*Hm[k]*as[l].z;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t\t\t\t\tfor (l=0; l<nmeln; ++l) {\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tke[4*k + 3][4*(nseln+l)  ] += -dt*w[j]*wn*0.5*Hm[l]*as[k].x;\n\t\t\t\t\t\t\t\t\t\tke[4*k + 3][4*(nseln+l)+1] += -dt*w[j]*wn*0.5*Hm[l]*as[k].y;\n\t\t\t\t\t\t\t\t\t\tke[4*k + 3][4*(nseln+l)+2] += -dt*w[j]*wn*0.5*Hm[l]*as[k].z;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t// --- P R E S S U R E - P R E S S U R E   C O N T A C T ---\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nseln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t\tke[4*k + 3][4*l+3] += -dt*epsp*w[j]*detJ[j]*Hs[k]*Hs[l];\n\t\t\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t\t\t\tke[4*k + 3][4*(nseln+l)+3] += dt*epsp*w[j]*detJ[j]*Hs[k]*Hm[l];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor (k=0; k<nmeln; ++k) {\n\t\t\t\t\t\t\tfor (l=0; l<nseln; ++l)\n\t\t\t\t\t\t\t\tke[4*(nseln+k)+3][4*l + 3] += dt*epsp*w[j]*detJ[j]*Hm[k]*Hs[l];\n\t\t\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t\t\t\tke[4*(nseln+k)+3][4*(nseln+l) + 3] += -dt*epsp*w[j]*detJ[j]*Hm[k]*Hm[l];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// assemble the global stiffness\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\tLS.Assemble(ke);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedBiphasicInterface::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\tvec3d Ln;\n\tdouble Lp;\n\tbool bconv = true;\n\t\n\tbool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n\tint NS = m_ss.Elements();\n\tint NM = m_ms.Elements();\n\t\n\t// --- c a l c u l a t e   i n i t i a l   n o r m s ---\n\t// a. normal component\n\tdouble normL0 = 0, normP = 0, normDP = 0;\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FETiedBiphasicContactPoint& ds = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += ds.m_Lmd*ds.m_Lmd;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FETiedBiphasicContactPoint& dm = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += dm.m_Lmd*dm.m_Lmd;\n        }\n    }\n\t\n\t// b. gap component\n\t// (is calculated during update)\n\tdouble maxgap = 0;\n\tdouble maxpg = 0;\n\t\n\t// update Lagrange multipliers\n\tdouble normL1 = 0, eps, epsp;\n\tfor (int i = 0; i<NS; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FETiedBiphasicContactPoint& ds = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on primary surface\n            eps = m_epsn*ds.m_epsn;\n            ds.m_Lmd = ds.m_Lmd + ds.m_dg*eps;\n            \n            normL1 += ds.m_Lmd*ds.m_Lmd;\n            \n            if (m_ss.m_bporo) {\n                Lp = 0;\n                if (ds.m_pme) {\n                    epsp = m_epsp*ds.m_epsp;\n                    Lp = ds.m_Lmp + epsp*ds.m_pg;\n                    maxpg = max(maxpg,fabs(ds.m_pg));\n                    normDP += ds.m_pg*ds.m_pg;\n                }\n                ds.m_Lmp = Lp;\n            }\n            \n            maxgap = max(maxgap,sqrt(ds.m_dg*ds.m_dg));\n        }\n    }\n\t\n\tfor (i=0; i<NM; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FETiedBiphasicContactPoint& dm = static_cast<FETiedBiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on secondary surface\n            eps = m_epsn*dm.m_epsn;\n            dm.m_Lmd = dm.m_Lmd + dm.m_dg*eps;\n            \n            normL1 += dm.m_Lmd*dm.m_Lmd;\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                if (dm.m_pme) {\n                    epsp = m_epsp*dm.m_epsp;\n                    Lp = dm.m_Lmp + epsp*dm.m_pg;\n                    maxpg = max(maxpg,fabs(dm.m_pg));\n                    normDP += dm.m_pg*dm.m_pg;\n                }\n                dm.m_Lmp = Lp;\n            }\n            \n            maxgap = max(maxgap,sqrt(dm.m_dg*dm.m_dg));\n        }\n    }\n\t\n\t// Ideally normP should be evaluated from the fluid pressure at the\n\t// contact interface (not easily accessible).  The next best thing\n\t// is to use the contact traction.\n\tnormP = normL1;\n\t\n\t// calculate relative norms\n\tdouble lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0)); \n\tdouble pnorm = (normP != 0 ? (normDP/normP) : normDP); \n\t\n\t// check convergence\n\tif ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n\tif ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n\t\n\tif ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n\tif ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n\n\tif (naug < m_naugmin ) bconv = false;\n\tif (naug >= m_naugmax) bconv = true;\n\n\tfeLog(\" tied biphasic interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tfeLog(\"    D multiplier : %15le\", lnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\");\n\tif (bporo) { feLog(\"    P gap        : %15le\", pnorm); if (m_atol > 0) feLog(\"%15le\\n\", m_atol); else feLog(\"       ***\\n\"); }\n\t\n\tfeLog(\"    maximum gap  : %15le\", maxgap);\n\tif (m_gtol > 0) feLog(\"%15le\\n\", m_gtol); else feLog(\"       ***\\n\");\n\tif (bporo) {\n\t\tfeLog(\"    maximum pgap : %15le\", maxpg);\n\t\tif (m_ptol > 0) feLog(\"%15le\\n\", m_ptol); else feLog(\"       ***\\n\");\n\t}\n\t\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicInterface::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n\n\t// serialize pointers\n\tSerializeElementPointers(m_ss, m_ms, ar);\n\tSerializeElementPointers(m_ms, m_ss, ar);\n}\n"
  },
  {
    "path": "FEBioMix/FETiedBiphasicInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FEBioMech/FEContactInterface.h>\n#include \"FEBiphasicContactSurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FETiedBiphasicContactPoint: public FEBiphasicContactPoint\n{\npublic:\n    vec3d   m_Gap;  //!< initial gap in reference configuration\n    vec3d   m_dg;   //!< gap function at integration points\n    vec3d   m_nu;   //!< normal at integration points\n    vec2d   m_rs;   //!< natural coordinates of projection of integration point\n    vec3d   m_Lmd;  //!< lagrange multipliers for displacements\n    vec3d   m_tr;   //!< contact traction\n    double  m_epsn; //!< penalty factors\n    double  m_epsp; //!< pressure penalty factors\n\n    void Init() override\n    {\n        FEBiphasicContactPoint::Init();\n        m_Gap = m_dg = m_nu = m_Lmd = m_tr = vec3d(0,0,0);\n        m_rs = vec2d(0,0);\n        m_epsn = 1.0;\n        m_epsp = 1.0;\n    }\n    \n    void Serialize(DumpStream& ar) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FETiedBiphasicSurface : public FEBiphasicContactSurface\n{\npublic:\n\t//! constructor\n\tFETiedBiphasicSurface(FEModel* pfem);\n\t\n\t//! initialization\n\tbool Init() override;\n\t\n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\n\t//! calculate the nodal normals\n\tvoid UpdateNodeNormals();\n\t\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\tvoid SetPoroMode(bool bporo) { m_bporo = bporo; }\n\t\npublic:\n    void GetVectorGap      (int nface, vec3d& pg) override;\n    void GetContactTraction(int nface, vec3d& pt) override;\n    \npublic:\n\tbool\t\t\t\tm_bporo;\t//!< set poro-mode\n\t\n    vector<bool>\t\t\tm_poro;\t//!< surface element poro status\n    vector<vec3d>\t\t\tm_nn;\t//!< node normals\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FETiedBiphasicInterface :\tpublic FEContactInterface\n{\npublic:\n\t//! constructor\n\tFETiedBiphasicInterface(FEModel* pfem);\n\t\n\t//! destructor\n\t~FETiedBiphasicInterface();\n\t\n\t//! initialization\n\tbool Init() override;\n\t\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return false; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid InitialProjection(FETiedBiphasicSurface& ss, FETiedBiphasicSurface& ms);\n\tvoid ProjectSurface(FETiedBiphasicSurface& ss, FETiedBiphasicSurface& ms);\n\t\n\t//! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n\tvoid CalcAutoPenalty(FETiedBiphasicSurface& s);\n\t\n\tvoid CalcAutoPressurePenalty(FETiedBiphasicSurface& s);\n\tdouble AutoPressurePenalty(FESurfaceElement& el, FETiedBiphasicSurface& s);\n\t\npublic:\n\tFETiedBiphasicSurface\tm_ss;\t//!< primary surface\n\tFETiedBiphasicSurface\tm_ms;\t//!< secondary surface\n\t\n\tint\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n\tbool\t\t\tm_btwo_pass;\t//!< two-pass flag\n\tdouble\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\t\t\tm_gtol;\t\t\t//!< gap tolerance\n\tdouble\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n\tdouble\t\t\tm_stol;\t\t\t//!< search tolerance\n\tbool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n\tdouble\t\t\tm_srad;\t\t\t//!< contact search radius\n\tint\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n\tint\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n\t\n\tdouble\t\t\tm_epsn;\t\t\t//!< normal penalty factor\n\tbool\t\t\tm_bautopen;\t\t//!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n\n\t// biphasic contact parameters\n\tdouble\t\t\tm_epsp;\t\t//!< flow rate penalty\n\nprotected:\n\tint\tm_dofP;\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FETiedMultiphasicInterface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedMultiphasicInterface.h\"\n#include \"FEBiphasic.h\"\n#include \"FEBiphasicSolute.h\"\n#include \"FETriphasic.h\"\n#include \"FEMultiphasic.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FENormalProjection.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FETiedMultiphasicInterface, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"             )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\"          );\n\tADD_PARAMETER(m_gtol     , \"gaptol\"             );\n\tADD_PARAMETER(m_ptol     , \"ptol\"               );\n    ADD_PARAMETER(m_ctol     , \"ctol\"               );\n\tADD_PARAMETER(m_epsn     , \"penalty\"            );\n\tADD_PARAMETER(m_bautopen , \"auto_penalty\"       );\n    ADD_PARAMETER(m_bupdtpen , \"update_penalty\"     );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\"           );\n\tADD_PARAMETER(m_knmult   , \"knmult\"             );\n\tADD_PARAMETER(m_stol     , \"search_tol\"         );\n\tADD_PARAMETER(m_epsp     , \"pressure_penalty\"   );\n\tADD_PARAMETER(m_epsc     , \"concentration_penalty\");\n\tADD_PARAMETER(m_bsymm    , \"symmetric_stiffness\");\n\tADD_PARAMETER(m_srad     , \"search_radius\"      );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"             );\n\tADD_PARAMETER(m_naugmax  , \"maxaug\"             );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicContactPoint::Serialize(DumpStream& ar)\n{\n    FETiedBiphasicContactPoint::Serialize(ar);\n    ar & m_Lmc;\n\tar & m_epsc;\n    ar & m_cg;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedMultiphasicSurface\n//-----------------------------------------------------------------------------\n\nFETiedMultiphasicSurface::FETiedMultiphasicSurface(FEModel* pfem) : FEBiphasicContactSurface(pfem)\n{\n    m_bporo = m_bsolu = false;\n    m_dofC = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! create material point data\nFEMaterialPoint* FETiedMultiphasicSurface::CreateMaterialPoint()\n{\n\treturn new FETiedMultiphasicContactPoint;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicSurface::UnpackLM(FEElement& el, vector<int>& lm)\n{\n    // get nodal DOFS\n    DOFS& dofs = GetFEModel()->GetDOFS();\n    int MAX_CDOFS = dofs.GetVariableSize(\"concentration\");\n    \n    int N = el.Nodes();\n    lm.resize(N*(4+MAX_CDOFS));\n    \n    // pack the equation numbers\n    for (int i=0; i<N; ++i)\n    {\n        int n = el.m_node[i];\n        \n        FENode& node = m_pMesh->Node(n);\n        vector<int>& id = node.m_ID;\n        \n        // first the displacement dofs\n        lm[3*i  ] = id[m_dofX];\n        lm[3*i+1] = id[m_dofY];\n        lm[3*i+2] = id[m_dofZ];\n        \n        // now the pressure dofs\n        lm[3*N+i] = id[m_dofP];\n        \n        // concentration dofs\n        for (int k=0; k<MAX_CDOFS; ++k)\n            lm[(4 + k)*N + i] = id[m_dofC+k];\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedMultiphasicSurface::Init()\n{\n    // initialize surface data first\n    if (FEBiphasicContactSurface::Init() == false) return false;\n    \n    // store concentration index\n    DOFS& dofs = GetFEModel()->GetDOFS();\n    m_dofC = dofs.GetDOF(\"concentration\", 0);\n\tif (m_dofC < 0) return false;\n    \n    // allocate node normals\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    \n    // determine solutes for this surface using the first surface element\n    // TODO: Check that all elements use the same set of solutes as the first element\n    int nsol = 0;\n    if (Elements()) {\n        FESurfaceElement& se = Element(0);\n        // get the element this surface element belongs to\n        FEElement* pe = se.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n            \n            // check type of element\n            FEBiphasic* pb = dynamic_cast<FEBiphasic*> (pm);\n            FEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n            FETriphasic* pt = dynamic_cast<FETriphasic*>(pm);\n            FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*> (pm);\n            if (pb)\n            {\n                m_bporo = true;\n                nsol = 0;\n            }\n            else if (pbs)\n            {\n                m_bporo = m_bsolu = true;\n                nsol = 1;\n                m_sid.assign(nsol, pbs->GetSolute()->GetSoluteID() - 1);\n            }\n            else if (pt)\n            {\n                m_bporo = m_bsolu = true;\n                nsol = 2;\n                m_sid.resize(nsol);\n                m_sid[0] = pt->m_pSolute[0]->GetSoluteID() - 1;\n                m_sid[1] = pt->m_pSolute[1]->GetSoluteID() - 1;\n            }\n            else if (pmp)\n            {\n                m_bporo = m_bsolu = true;\n                nsol = pmp->Solutes();\n                m_sid.resize(nsol);\n                for (int isol=0; isol<nsol; ++isol) {\n                    m_sid[isol] = pmp->GetSolute(isol)->GetSoluteID() - 1;\n                }\n            }\n        }\n    }\n    \n    // allocate data structures\n    int NE = Elements();\n    m_poro.resize(NE,false);\n    for (int i=0; i<NE; ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        // get the element this surface element belongs to\n        FEElement* pe = el.m_elem[0].pe;\n        if (pe)\n        {\n            // get the material\n            FEMaterial* pm = m_pfem->GetMaterial(pe->GetMatID());\n            \n            // see if this is a poro-elastic element\n            FEBiphasic* bp = dynamic_cast<FEBiphasic*> (pm);\n            FEBiphasicSolute* bs = dynamic_cast<FEBiphasicSolute*> (pm);\n            FETriphasic* tp = dynamic_cast<FETriphasic*> (pm);\n            FEMultiphasic* mp = dynamic_cast<FEMultiphasic*> (pm);\n            if (bp || bs || tp || mp) {\n                m_poro[i] = true;\n                m_bporo = true;\n            }\n            if (bs || tp || mp) {\n                m_bsolu = true;\n            }\n        }\n        int nint = el.GaussPoints();\n        if (nsol) {\n            for (int j=0; j<nint; ++j) {\n                FETiedMultiphasicContactPoint& data = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n                data.m_Lmc.resize(nsol);\n                data.m_epsc.resize(nsol);\n                data.m_cg.resize(nsol);\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the\n//! element normals at the node\n\nvoid FETiedMultiphasicSurface::UpdateNodeNormals()\n{\n    int N = Nodes(), i, j, ne, jp1, jm1;\n    vec3d y[FEElement::MAX_NODES], n;\n    \n    // zero nodal normals\n    zero(m_nn);\n    \n    // loop over all elements\n    for (i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        ne = el.Nodes();\n        \n        // get the nodal coordinates\n        for (j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n        \n        // calculate the normals\n        for (j=0; j<ne; ++j)\n        {\n            jp1 = (j+1)%ne;\n            jm1 = (j+ne-1)%ne;\n            n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n            m_nn[el.m_lnode[j]] += n;\n        }\n    }\n    \n    // normalize all vectors\n    for (i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicSurface::Serialize(DumpStream& ar)\n{\n\tFEBiphasicContactSurface::Serialize(ar);\n\tar & m_dofC;\n\tar & m_bporo;\n\tar & m_bsolu;\n\tar & m_poro;\n\tar & m_nn;\n\tar & m_sid;\n}\n\n//-----------------------------------------------------------------------------\n// FETiedMultiphasicInterface\n//-----------------------------------------------------------------------------\n\nFETiedMultiphasicInterface::FETiedMultiphasicInterface(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n    static int count = 1;\n    SetID(count++);\n    \n    // initial values\n    m_knmult = 1;\n    m_atol = 0.1;\n    m_epsn = 1;\n    m_epsp = 1;\n    m_epsc = 1;\n    m_btwo_pass = false;\n    m_stol = 0.01;\n    m_bsymm = true;\n    m_srad = 1.0;\n    m_gtol = 0;\n    m_ptol = 0;\n    m_ctol = 0;\n    m_bautopen = false;\n    m_bupdtpen = false;\n    \n    m_naugmin = 0;\n    m_naugmax = 10;\n    \n    m_dofP = (pfem ? pfem->GetDOFIndex(\"p\") : -1);\n    m_dofC = (pfem ? pfem->GetDOFIndex(\"concentration\", 0) : -1);\n    \n    // set parents\n    m_ss.SetContactInterface(this);\n    m_ms.SetContactInterface(this);\n\n    m_ss.SetSibling(&m_ms);\n    m_ms.SetSibling(&m_ss);\n}\n\n//-----------------------------------------------------------------------------\n\nFETiedMultiphasicInterface::~FETiedMultiphasicInterface()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedMultiphasicInterface::Init()\n{\n    m_Rgas = GetFEModel()->GetGlobalConstant(\"R\");\n    m_Tabs = GetFEModel()->GetGlobalConstant(\"T\");\n    \n    // initialize surface data\n    if (m_ss.Init() == false) return false;\n    if (m_ms.Init() == false) return false;\n    \n    // determine which solutes are common to both contact surfaces\n    m_sid.clear(); m_ssl.clear(); m_msl.clear(); m_sz.clear();\n    for (int is=0; is<m_ss.m_sid.size(); ++is) {\n        for (int im=0; im<m_ms.m_sid.size(); ++im) {\n            if (m_ms.m_sid[im] == m_ss.m_sid[is]) {\n                m_sid.push_back(m_ss.m_sid[is]);\n                m_ssl.push_back(is);\n                m_msl.push_back(im);\n                FESoluteData* sd = FindSoluteData(m_ss.m_sid[is] + 1);\n                m_sz.push_back(sd->m_z);\n            }\n        }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\nvoid FETiedMultiphasicInterface::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // get the DOFS\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_P = fem.GetDOFIndex(\"p\");\n    const int dof_C = fem.GetDOFIndex(\"concentration\", 0);\n    \n    int nsol = (int)m_sid.size();\n    int ndpn = 7 + nsol;\n    \n    vector<int> lm(ndpn*FEElement::MAX_NODES*2);\n    \n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        FETiedMultiphasicSurface& ss = (np == 0? m_ss : m_ms);\n        \n        int k, l;\n        for (int j=0; j<ss.Elements(); ++j)\n        {\n            FESurfaceElement& se = ss.Element(j);\n            int nint = se.GaussPoints();\n            int* sn = &se.m_node[0];\n            for (k=0; k<nint; ++k)\n            {\n                FETiedMultiphasicContactPoint& data = static_cast<FETiedMultiphasicContactPoint&>(*se.GetMaterialPoint(k));\n                FESurfaceElement* pe = data.m_pme;\n                if (pe != 0)\n                {\n                    FESurfaceElement& me = *pe;\n                    int* mn = &me.m_node[0];\n                    \n                    assign(lm, -1);\n                    \n                    int nseln = se.Nodes();\n                    int nmeln = me.Nodes();\n                    \n                    for (l=0; l<nseln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(sn[l]).m_ID;\n                        lm[ndpn*l  ] = id[dof_X];\n                        lm[ndpn*l+1] = id[dof_Y];\n                        lm[ndpn*l+2] = id[dof_Z];\n                        lm[ndpn*l+3] = id[dof_P];\n                        for (int m=0; m<nsol; ++m) {\n                            lm[ndpn*l+4+m] = id[dof_C + m_sid[m]];\n                        }\n                    }\n                    \n                    for (l=0; l<nmeln; ++l)\n                    {\n                        vector<int>& id = mesh.Node(mn[l]).m_ID;\n                        lm[ndpn*(l+nseln)  ] = id[dof_X];\n                        lm[ndpn*(l+nseln)+1] = id[dof_Y];\n                        lm[ndpn*(l+nseln)+2] = id[dof_Z];\n                        lm[ndpn*(l+nseln)+3] = id[dof_P];\n                        for (int m=0; m<nsol; ++m) {\n                            lm[ndpn*(l+nseln)+4+m] = id[dof_C + m_sid[m]];\n                        }\n                    }\n                    \n                    K.build_add(lm);\n                }\n            }\n        }\n    }\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::UpdateAutoPenalty()\n{\n    // calculate the penalty\n    if (m_bautopen)\n    {\n        CalcAutoPenalty(m_ss);\n        CalcAutoPenalty(m_ms);\n        if (m_ss.m_bporo) CalcAutoPressurePenalty(m_ss);\n        for (int is=0; is<m_ssl.size(); ++is)\n            CalcAutoConcentrationPenalty(m_ss, m_ssl[is]);\n        if (m_ms.m_bporo) CalcAutoPressurePenalty(m_ms);\n        for (int im=0; im<m_msl.size(); ++im)\n            CalcAutoConcentrationPenalty(m_ms, m_msl[im]);\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::Activate()\n{\n    // don't forget to call the base class\n    FEContactInterface::Activate();\n    \n    UpdateAutoPenalty();\n    \n    // project the surfaces onto each other\n    // this will evaluate the gap functions in the reference configuration\n    InitialProjection(m_ss, m_ms);\n    if (m_btwo_pass) InitialProjection(m_ms, m_ss);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::CalcAutoPenalty(FETiedMultiphasicSurface& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPenalty(el, s);\n       \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsn = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates a contact penalty parameter based on the\n//! material and geometrical properties of the primary and secondary surfaces\n//!\ndouble FETiedMultiphasicInterface::AutoPenalty(FESurfaceElement& el, FESurface &s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n\n    tens4ds S;\n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        \n    // extract the material\n    FEMaterial* pme = GetFEModel()->GetMaterial(pe->GetMatID());\n\tif (pme == 0) return 0.0;\n        \n\t// get the tangent (stiffness)\n\tif (dynamic_cast<FEMultiphasic*>(pme)) {\n\t\tFEMultiphasic* pmm = dynamic_cast<FEMultiphasic*>(pme);\n\t\tS = pmm->Tangent(mp);\n\t}\n\telse if (dynamic_cast<FETriphasic*>(pme)) {\n\t\tFETriphasic* pmt = dynamic_cast<FETriphasic*>(pme);\n\t\tS = pmt->Tangent(mp);\n\t}\n\telse if (dynamic_cast<FEBiphasicSolute*>(pme)) {\n\t\tFEBiphasicSolute* pms = dynamic_cast<FEBiphasicSolute*>(pme);\n\t\tS = pms->Tangent(mp);\n\t}\n\telse if (dynamic_cast<FEBiphasic*>(pme)) {\n\t\tFEBiphasic* pmb = dynamic_cast<FEBiphasic*>(pme);\n        S = (pmb->Tangent(mp)).supersymm();\n\t}\n\telse if (dynamic_cast<FEElasticMaterial*>(pme)) {\n\t\tFEElasticMaterial* pm = dynamic_cast<FEElasticMaterial*>(pme);\n\t\tS = pm->Tangent(mp);\n\t}\n\t// get the inverse (compliance) at this point\n\ttens4ds C = S.inverse();\n            \n\t// evaluate element surface normal at parametric center\n\tvec3d t[2];\n\ts.CoBaseVectors0(el, 0, 0, t);\n\tvec3d n = t[0] ^ t[1];\n\tn.unit();\n            \n\t// evaluate normal component of the compliance matrix\n\t// (equivalent to inverse of Young's modulus along n)\n\tdouble eps = 1./(n*(vdotTdotv(n, C, n)*n));\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n    return eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::CalcAutoPressurePenalty(FETiedMultiphasicSurface& s)\n{\n    // loop over all surface elements\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoPressurePenalty(el, s);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j)\n        {\n            FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsp = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\ndouble FETiedMultiphasicInterface::AutoPressurePenalty(FESurfaceElement& el, FETiedMultiphasicSurface& s)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // evaluate element surface normal at parametric center\n    vec3d t[2];\n    s.CoBaseVectors0(el, 0, 0, t);\n    vec3d n = t[0] ^ t[1];\n    n.unit();\n    \n   \n    // get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n\n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        \n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        \n    mat3ds K;\n        \n    // check type of element\n    FEBiphasic* pb = dynamic_cast<FEBiphasic*> (pm);\n    FEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n    FETriphasic* ptp = dynamic_cast<FETriphasic*> (pm);\n    FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*> (pm);\n    if (pb) {\n        double k[3][3];\n        pb->Permeability(k, mp);\n        K = mat3ds(k[0][0], k[1][1], k[2][2], k[0][1], k[1][2], k[0][2]);\n    }\n    else if (ptp)\n        K = ptp->GetPermeability()->Permeability(mp);\n    else if (pbs)\n        K = pbs->GetPermeability()->Permeability(mp);\n    else if (pmp)\n        K = pmp->GetPermeability()->Permeability(mp);\n        \n    double eps = n*(K*n);\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n    return eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::CalcAutoConcentrationPenalty(FETiedMultiphasicSurface& s, const int isol)\n{\n    // loop over all surface elements\n    int ni = 0;\n    for (int i=0; i<s.Elements(); ++i)\n    {\n        // get the surface element\n        FESurfaceElement& el = s.Element(i);\n        \n        // calculate a penalty\n        double eps = AutoConcentrationPenalty(el, s, isol);\n        \n        // assign to integation points of surface element\n        int nint = el.GaussPoints();\n        for (int j=0; j<nint; ++j, ++ni)\n        {\n            FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_epsc[isol] = eps;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\ndouble FETiedMultiphasicInterface::AutoConcentrationPenalty(FESurfaceElement& el, FETiedMultiphasicSurface& s, const int isol)\n{\n    // get the mesh\n    FEMesh& m = GetFEModel()->GetMesh();\n    \n    // evaluate element surface normal at parametric center\n    vec3d t[2];\n    s.CoBaseVectors0(el, 0, 0, t);\n    vec3d n = t[0] ^ t[1];\n    n.unit();\n    \n\t// get the element this surface element belongs to\n    FEElement* pe = el.m_elem[0].pe;\n    if (pe == 0) return 0.0;\n\n    // get the material\n    FEMaterial* pm = GetFEModel()->GetMaterial(pe->GetMatID());\n        \n    // get a material point\n    FEMaterialPoint& mp = *pe->GetMaterialPoint(0);\n        \n    mat3ds D;\n        \n    // see if this is a biphasic-solute or multiphasic element\n    FEBiphasicSolute* pbs = dynamic_cast<FEBiphasicSolute*> (pm);\n    FETriphasic* ptp = dynamic_cast<FETriphasic*> (pm);\n    FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*> (pm);\n    if (pbs)\n    {\n        D = pbs->GetSolute()->m_pDiff->Diffusivity(mp)\n        *(pbs->Porosity(mp)*pbs->GetSolute()->m_pSolub->Solubility(mp));\n    }\n    else if (ptp)\n    {\n        D = ptp->GetSolute(isol)->m_pDiff->Diffusivity(mp)\n        *(ptp->Porosity(mp)*ptp->GetSolute(isol)->m_pSolub->Solubility(mp));\n    }\n    else if (pmp)\n    {\n        D = pmp->GetSolute(isol)->m_pDiff->Diffusivity(mp)\n        *(pmp->Porosity(mp)*pmp->GetSolute(isol)->m_pSolub->Solubility(mp));\n    }\n        \n    // evaluate normal component of diffusivity\n\tdouble eps = n*(D*n);\n    \n\t// get the area of the surface element\n\tdouble A = s.FaceArea(el);\n\n\t// get the volume of the volume element\n\tdouble V = m.ElementVolume(*pe);\n\n    return eps*A/V;\n}\n\n//-----------------------------------------------------------------------------\n// Perform initial projection between tied surfaces in reference configuration\nvoid FETiedMultiphasicInterface::InitialProjection(FETiedMultiphasicSurface& ss, FETiedMultiphasicSurface& ms)\n{\n    FESurfaceElement* pme;\n    vec3d r, nu;\n    double rs[2];\n    \n    // initialize projection data\n    FENormalProjection np(ms);\n    np.SetTolerance(m_stol);\n    np.SetSearchRadius(m_srad);\n    np.Init();\n    \n    // loop over all integration points\n    int n = 0;\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        \n        int nint = el.GaussPoints();\n        \n        for (int j=0; j<nint; ++j, ++n)\n        {\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // calculate the normal at this integration point\n            nu = ss.SurfaceNormal(el, j);\n            \n            // find the intersection point with the secondary surface\n            pme = np.Project2(r, nu, rs);\n            \n            FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tpt.m_pme = pme;\n            pt.m_rs[0] = rs[0];\n            pt.m_rs[1] = rs[1];\n            if (pme)\n            {\n                // the node could potentially be in contact\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, rs[0], rs[1]);\n                \n                // calculate the gap function\n                pt.m_Gap = q - r;\n            }\n            else\n            {\n                // the node is not in contact\n                pt.m_Gap = vec3d(0,0,0);\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate gap functions for position and fluid pressure\nvoid FETiedMultiphasicInterface::ProjectSurface(FETiedMultiphasicSurface& ss, FETiedMultiphasicSurface& ms)\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    FESurfaceElement* pme;\n    vec3d r;\n    \n    const int MN = FEElement::MAX_NODES;\n    double ps[MN], p1;\n    int nsol = (int)m_sid.size();\n    vector< vector<double> > cs(nsol, vector<double>(MN));\n    vector<double> c1(nsol);\n    \n    // loop over all integration points\n    for (int i=0; i<ss.Elements(); ++i)\n    {\n        FESurfaceElement& el = ss.Element(i);\n        bool sporo = ss.m_poro[i];\n        \n        int ne = el.Nodes();\n        int nint = el.GaussPoints();\n        \n        // get the nodal pressures\n        p1 = 0;\n        if (sporo)\n        {\n            for (int j=0; j<ne; ++j) ps[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n        }\n        \n        // get the nodal concentrations\n        for (int isol=0; isol<nsol; ++isol) {\n            for (int j=0; j<ne; ++j) cs[isol][j] = mesh.Node(el.m_node[j]).get(m_dofC + m_sid[isol]);\n        }\n        \n        for (int j=0; j<nint; ++j)\n        {\n            FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // calculate the global position of the integration point\n            r = ss.Local2Global(el, j);\n            \n            // get the pressure at the integration point\n            if (sporo) p1 = el.eval(ps, j);\n            \n            // get the concentration at the integration point\n            for (int isol=0; isol<nsol; ++isol) c1[isol] = el.eval(&cs[isol][0], j);\n            \n            // calculate the normal at this integration point\n            pt.m_nu = ss.SurfaceNormal(el, j);\n            \n            // if this node is tied, evaluate gap functions\n            pme = pt.m_pme;\n            if (pme)\n            {\n                // find the global location of the intersection point\n                vec3d q = ms.Local2Global(*pme, pt.m_rs[0], pt.m_rs[1]);\n                \n                // calculate the gap function\n                vec3d g = q - r;\n                pt.m_dg = g - pt.m_Gap;\n                \n                // calculate the pressure gap function\n                bool mporo = ms.m_poro[pme->m_lid];\n                if (sporo && mporo) {\n                    double pm[FEElement::MAX_NODES];\n                    for (int k=0; k<pme->Nodes(); ++k) pm[k] = mesh.Node(pme->m_node[k]).get(m_dofP);\n                    double p2 = pme->eval(pm, pt.m_rs[0], pt.m_rs[1]);\n                    pt.m_pg = p1 - p2;\n                }\n                // calculate the concentration gap functions\n                for (int isol=0; isol<nsol; ++isol) {\n                    int sid = m_sid[isol];\n                    double cm[MN];\n                    for (int k=0; k<pme->Nodes(); ++k) cm[k] = mesh.Node(pme->m_node[k]).get(m_dofC + sid);\n                    double c2 = pme->eval(cm, pt.m_rs[0], pt.m_rs[1]);\n                    pt.m_cg[m_ssl[isol]] = c1[isol] - c2;\n                }\n            }\n            else\n            {\n                // the node is not tied\n                pt.m_dg = vec3d(0,0,0);\n                if (sporo) {\n                    pt.m_pg = 0;\n                    pt.m_Lmp = 0;\n                }\n                for (int isol=0; isol<nsol; ++isol) {\n                    pt.m_Lmc[m_ssl[isol]] = 0;\n                    pt.m_cg[m_ssl[isol]] = 0;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FETiedMultiphasicInterface::Update()\n{\n    // project the surfaces onto each other\n    // this will update the gap functions as well\n    ProjectSurface(m_ss, m_ms);\n    if (m_btwo_pass) ProjectSurface(m_ms, m_ss);\n    \n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n    int i, j, k;\n    vector<int> sLM, mLM, LM, en;\n    vector<double> fe;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    double N[MN*10];\n    int nsol = (int)m_sid.size();\n    vec3d tn[MN];\n    double wn[MN];\n    vector< vector<double> > jn(nsol,vector<double>(MN));\n    \n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolver* psolver = pstep->GetFESolver();\n\n    double dt = fem.GetTime().timeIncrement;\n    \n    // Update auto-penalty if requested\n    if (m_bupdtpen && psolver->m_niter == 0) UpdateAutoPenalty();\n    \n    // loop over the nr of passes\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np<npass; ++np)\n    {\n        // get primary and seconary surface\n        FETiedMultiphasicSurface& ss = (np == 0? m_ss : m_ms);\n        FETiedMultiphasicSurface& ms = (np == 0? m_ms : m_ss);\n        vector<int>& sl = (np == 0? m_ssl : m_msl);\n        \n        // loop over all primary surface elements\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get the surface element\n            FESurfaceElement& se = ss.Element(i);\n            \n            bool sporo = ss.m_bporo;\n            \n            // get the nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            // copy the LM vector; we'll need it later\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n                \n                FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n                // contact traction\n                double eps = m_epsn*pt.m_epsn;      // penalty\n                tn[j] = pt.m_Lmd + pt.m_dg*eps;    // contact traction\n                \n                // normal fluid flux\n                double epsp = m_epsp*pt.m_epsp;\n                wn[j] = pt.m_Lmp + epsp*pt.m_pg;\n                \n                // normal solute flux\n                for (int isol=0; isol<nsol; ++isol)\n                {\n                    int l = sl[isol];\n                    double epsc = m_epsc*pt.m_epsc[l];\n                    jn[isol][j] = pt.m_Lmc[l] + epsc*pt.m_cg[l];\n                }\n            }\n            \n            // loop over all integration points\n            // note that we are integrating over the current surface\n            for (j=0; j<nint; ++j)\n            {\n                FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\t\t\t\t// get the secondary surface element\n                FESurfaceElement* pme = pt.m_pme;\n                if (pme)\n                {\n                    // get the secondary surface element\n                    FESurfaceElement& me = *pme;\n                    \n                    bool mporo = ms.m_bporo;\n                    \n                    // get the nr of secondary surface element nodes\n                    int nmeln = me.Nodes();\n                    \n                    // copy LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    // calculate degrees of freedom\n                    int ndof = 3*(nseln + nmeln);\n                    \n                    // build the LM vector\n                    LM.resize(ndof);\n                    for (k=0; k<nseln; ++k)\n                    {\n                        LM[3*k  ] = sLM[3*k  ];\n                        LM[3*k+1] = sLM[3*k+1];\n                        LM[3*k+2] = sLM[3*k+2];\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        LM[3*(k+nseln)  ] = mLM[3*k  ];\n                        LM[3*(k+nseln)+1] = mLM[3*k+1];\n                        LM[3*(k+nseln)+2] = mLM[3*k+2];\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // get element shape functions\n                    Hs = se.H(j);\n                    \n                    // get secondary surface element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // calculate the force vector\n                    fe.resize(ndof);\n                    zero(fe);\n                    \n                    for (k=0; k<nseln; ++k)\n                    {\n                        N[3*k  ] = Hs[k]*tn[j].x;\n                        N[3*k+1] = Hs[k]*tn[j].y;\n                        N[3*k+2] = Hs[k]*tn[j].z;\n                    }\n                    \n                    for (k=0; k<nmeln; ++k)\n                    {\n                        N[3*(k+nseln)  ] = -Hm[k]*tn[j].x;\n                        N[3*(k+nseln)+1] = -Hm[k]*tn[j].y;\n                        N[3*(k+nseln)+2] = -Hm[k]*tn[j].z;\n                    }\n                    \n                    for (k=0; k<ndof; ++k) fe[k] += N[k]*detJ[j]*w[j];\n                    \n                    // assemble the global residual\n                    R.Assemble(en, LM, fe);\n                    \n                    // do the biphasic stuff\n                    if (sporo && mporo)\n                    {\n                        // calculate nr of pressure dofs\n                        int ndof = nseln + nmeln;\n                        \n                        // fill the LM\n                        LM.resize(ndof);\n                        for (k=0; k<nseln; ++k) LM[k        ] = sLM[3*nseln+k];\n                        for (k=0; k<nmeln; ++k) LM[k + nseln] = mLM[3*nmeln+k];\n                        \n                        // fill the force array\n                        fe.resize(ndof);\n                        zero(fe);\n                        for (k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                        for (k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                        \n                        for (k=0; k<ndof; ++k) fe[k] += dt*N[k]*wn[j]*detJ[j]*w[j];\n                        \n                        // assemble residual\n                        R.Assemble(en, LM, fe);\n                    }\n                    for (int isol=0; isol<nsol; ++isol)\n                    {\n                        int sid = m_sid[isol];\n                        \n                        // calculate nr of concentration dofs\n                        int ndof = nseln + nmeln;\n                        \n                        // fill the LM\n                        LM.resize(ndof);\n                        for (k=0; k<nseln; ++k) LM[k        ] = sLM[(4+sid)*nseln+k];\n                        for (k=0; k<nmeln; ++k) LM[k + nseln] = mLM[(4+sid)*nmeln+k];\n                        \n                        // fill the force array\n                        fe.resize(ndof);\n                        zero(fe);\n                        for (k=0; k<nseln; ++k) N[k      ] =  Hs[k];\n                        for (k=0; k<nmeln; ++k) N[k+nseln] = -Hm[k];\n                        \n                        for (k=0; k<ndof; ++k) fe[k] += dt*N[k]*jn[isol][j]*detJ[j]*w[j];\n                        \n                        // assemble residual\n                        R.Assemble(en, LM, fe);\n                    }\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n    int i, j, k, l;\n    vector<int> sLM, mLM, LM, en;\n    const int MN = FEElement::MAX_NODES;\n    double detJ[MN], w[MN], *Hs, Hm[MN];\n    FEElementMatrix ke;\n    int nsol = (int)m_sid.size();\n    vec3d tn[MN];\n    double wn[MN];\n    vector< vector<double> > jn(nsol,vector<double>(MN));\n    \n    FEModel& fem = *GetFEModel();\n    \n    // see how many reformations we've had to do so far\n    int nref = GetSolver()->m_nref;\n    \n    // set higher order stiffness mutliplier\n    // NOTE: this algorithm doesn't really need this\n    // but I've added this functionality to compare with the other contact\n    // algorithms and to see the effect of the different stiffness contributions\n    double knmult = m_knmult;\n    if (m_knmult < 0)\n    {\n        int ni = int(-m_knmult);\n        if (nref >= ni)\n        {\n            knmult = 1;\n            feLog(\"Higher order stiffness terms included.\\n\");\n        }\n        else knmult = 0;\n    }\n    \n    // do single- or two-pass\n    int npass = (m_btwo_pass?2:1);\n    for (int np=0; np < npass; ++np)\n    {\n        // get the primary and secondary surface\n        FETiedMultiphasicSurface& ss = (np == 0? m_ss : m_ms);\n        FETiedMultiphasicSurface& ms = (np == 0? m_ms : m_ss);\n        vector<int>& sl = (np == 0? m_ssl : m_msl);\n        \n        // loop over all primary surface elements\n        for (i=0; i<ss.Elements(); ++i)\n        {\n            // get the next element\n            FESurfaceElement& se = ss.Element(i);\n            \n            bool sporo = ss.m_bporo;\n            \n            // get nr of nodes and integration points\n            int nseln = se.Nodes();\n            int nint = se.GaussPoints();\n            \n            double pn[MN];\n            vector< vector<double> >cn(nsol,vector<double>(MN));\n            for (j=0; j<nseln; ++j)\n            {\n                pn[j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofP);\n                for (int isol=0; isol<nsol; ++isol) {\n                    cn[isol][j] = ss.GetMesh()->Node(se.m_node[j]).get(m_dofC + m_sid[isol]);\n                }\n            }\n            \n            // copy the LM vector\n            ss.UnpackLM(se, sLM);\n            \n            // we calculate all the metrics we need before we\n            // calculate the nodal forces\n            for (j=0; j<nint; ++j)\n            {\n                // get the base vectors\n                vec3d g[2];\n                ss.CoBaseVectors(se, j, g);\n                \n                // jacobians: J = |g0xg1|\n                detJ[j] = (g[0] ^ g[1]).norm();\n                \n                // integration weights\n                w[j] = se.GaussWeights()[j];\n                \n                FETiedMultiphasicContactPoint& pd = static_cast<FETiedMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n                // contact traction\n                double eps = m_epsn*pd.m_epsn;      // penalty\n                tn[j] = pd.m_Lmd + pd.m_dg*eps;     // contact traction\n                \n                // normal fluid flux\n                double epsp = m_epsp*pd.m_epsp;\n                wn[j] = pd.m_Lmp + epsp*pd.m_pg;\n                \n                // normal solute flux\n                for (int isol=0; isol<nsol; ++isol)\n                {\n                    int l = sl[isol];\n                    double epsc = m_epsc*pd.m_epsc[l];\n                    jn[isol][j] = pd.m_Lmc[l] + epsc*pd.m_cg[l];\n                }\n                \n                // contravariant basis vectors of primary surface\n                vec3d Gs[2];\n                ss.ContraBaseVectors(se, j, Gs);\n            }\n            \n            // loop over all integration points\n            for (j=0; j<nint; ++j)\n            {\n                FETiedMultiphasicContactPoint& pt = static_cast<FETiedMultiphasicContactPoint&>(*se.GetMaterialPoint(j));\n\n\t\t\t\t// get the secondary surface element\n                FESurfaceElement* pme = pt.m_pme;\n                if (pme)\n                {\n                    FESurfaceElement& me = *pme;\n                    \n                    bool mporo = ms.m_bporo;\n                    \n                    // get the nr of secondary surface nodes\n                    int nmeln = me.Nodes();\n                    \n                    // nodal data\n                    double pm[MN];\n                    vector< vector<double> > cm(nsol,vector<double>(MN));\n                    for (k=0; k<nmeln; ++k)\n                    {\n                        pm[k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofP);\n                        for (int isol=0; isol<nsol; ++isol) {\n                            cm[isol][k] = ms.GetMesh()->Node(me.m_node[k]).get(m_dofC + m_sid[isol]);\n                        }\n                    }\n                    \n                    // copy the LM vector\n                    ms.UnpackLM(me, mLM);\n                    \n                    int ndpn;\t// number of dofs per node\n                    int ndof;\t// number of dofs in stiffness matrix\n                    \n                    if (nsol) {\n                        // calculate dofs for biphasic-solute contact\n                        ndpn = 4+nsol;\n                        ndof = ndpn*(nseln+nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (k=0; k<nseln; ++k)\n                        {\n                            LM[ndpn*k  ] = sLM[3*k  ];\t\t\t// x-dof\n                            LM[ndpn*k+1] = sLM[3*k+1];\t\t\t// y-dof\n                            LM[ndpn*k+2] = sLM[3*k+2];\t\t\t// z-dof\n                            LM[ndpn*k+3] = sLM[3*nseln+k];\t\t// p-dof\n                            for (int isol=0; isol<nsol; ++isol)\n                                LM[ndpn*k+4+isol] = sLM[(4+m_sid[isol])*nseln+k];\t\t// c-dof\n                        }\n                        for (k=0; k<nmeln; ++k)\n                        {\n                            LM[ndpn*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n                            LM[ndpn*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n                            LM[ndpn*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n                            LM[ndpn*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n                            for (int isol=0; isol<nsol; ++isol)\n                                LM[ndpn*(k+nseln)+4+isol] = mLM[(4+m_sid[isol])*nmeln+k];\t\t// c-dof\n                        }\n                    }\n                    \n                    else if (sporo && mporo) {\n                        // calculate dofs for biphasic contact\n                        ndpn = 4;\n                        ndof = ndpn*(nseln+nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (k=0; k<nseln; ++k)\n                        {\n                            LM[ndpn*k  ] = sLM[3*k  ];\t\t\t// x-dof\n                            LM[ndpn*k+1] = sLM[3*k+1];\t\t\t// y-dof\n                            LM[ndpn*k+2] = sLM[3*k+2];\t\t\t// z-dof\n                            LM[ndpn*k+3] = sLM[3*nseln+k];\t\t// p-dof\n                        }\n                        for (k=0; k<nmeln; ++k)\n                        {\n                            LM[ndpn*(k+nseln)  ] = mLM[3*k  ];\t\t\t// x-dof\n                            LM[ndpn*(k+nseln)+1] = mLM[3*k+1];\t\t\t// y-dof\n                            LM[ndpn*(k+nseln)+2] = mLM[3*k+2];\t\t\t// z-dof\n                            LM[ndpn*(k+nseln)+3] = mLM[3*nmeln+k];\t\t// p-dof\n                        }\n                    }\n                    \n                    else {\n                        // calculate dofs for elastic contact\n                        ndpn = 3;\n                        ndof = ndpn*(nseln + nmeln);\n                        \n                        // build the LM vector\n                        LM.resize(ndof);\n                        \n                        for (k=0; k<nseln; ++k)\n                        {\n                            LM[3*k  ] = sLM[3*k  ];\n                            LM[3*k+1] = sLM[3*k+1];\n                            LM[3*k+2] = sLM[3*k+2];\n                        }\n                        \n                        for (k=0; k<nmeln; ++k)\n                        {\n                            LM[3*(k+nseln)  ] = mLM[3*k  ];\n                            LM[3*(k+nseln)+1] = mLM[3*k+1];\n                            LM[3*(k+nseln)+2] = mLM[3*k+2];\n                        }\n                    }\n                    \n                    // build the en vector\n                    en.resize(nseln+nmeln);\n                    for (k=0; k<nseln; ++k) en[k      ] = se.m_node[k];\n                    for (k=0; k<nmeln; ++k) en[k+nseln] = me.m_node[k];\n                    \n                    // shape functions\n                    Hs = se.H(j);\n                    \n                    // secondary surface element shape functions\n                    double r = pt.m_rs[0];\n                    double s = pt.m_rs[1];\n                    me.shape_fnc(Hm, r, s);\n                    \n                    // get normal vector\n                    vec3d nu = pt.m_nu;\n                    \n                    // penalty\n                    double eps = m_epsn*pt.m_epsn;\n                    \n                    // create the stiffness matrix\n                    ke.resize(ndof, ndof); ke.zero();\n                    \n                    // --- S O L I D - S O L I D   C O N T A C T ---\n                    \n                    mat3ds Ns = mat3dd(1);\n                    \n                    double* Gr = se.Gr(j);\n                    double* Gs = se.Gs(j);\n                    vec3d gs[2];\n                    ss.CoBaseVectors(se, j, gs);\n                    vec3d as[MN];\n                    mat3d As[MN];\n                    for (int c=0; c<nseln; ++c) {\n                        as[c] = (nu ^ (gs[1]*Gr[c] - gs[0]*Gs[c]))/detJ[j];\n                        As[c] = (tn[j] & as[c]);\n                    }\n                    \n                    for (int a=0; a<nseln; ++a) {\n                        k = a*ndpn;\n                        for (int c=0; c<nseln; ++c) {\n                            l = c*ndpn;\n                            mat3d Kac = (Ns*(Hs[c]*eps)+As[c])*(Hs[a]*detJ[j]*w[j]);\n                            ke[k  ][l  ] += Kac(0,0); ke[k  ][l+1] += Kac(0,1); ke[k  ][l+2] += Kac(0,2);\n                            ke[k+1][l  ] += Kac(1,0); ke[k+1][l+1] += Kac(1,1); ke[k+1][l+2] += Kac(1,2);\n                            ke[k+2][l  ] += Kac(2,0); ke[k+2][l+1] += Kac(2,1); ke[k+2][l+2] += Kac(2,2);\n                        }\n                        for (int d=0; d<nmeln; ++d) {\n                            l = (nseln+d)*ndpn;\n                            mat3d Kad = (Ns*(-Hs[a]*Hm[d]*eps))*(detJ[j]*w[j]);\n                            ke[k  ][l  ] += Kad(0,0); ke[k  ][l+1] += Kad(0,1); ke[k  ][l+2] += Kad(0,2);\n                            ke[k+1][l  ] += Kad(1,0); ke[k+1][l+1] += Kad(1,1); ke[k+1][l+2] += Kad(1,2);\n                            ke[k+2][l  ] += Kad(2,0); ke[k+2][l+1] += Kad(2,1); ke[k+2][l+2] += Kad(2,2);\n                        }\n                    }\n                    for (int b=0; b<nmeln; ++b) {\n                        k = (nseln+b)*ndpn;\n                        for (int c=0; c<nseln; ++c) {\n                            l = c*ndpn;\n                            mat3d Kbc = (Ns*(Hs[c]*eps)+As[c])*(-Hm[b]*detJ[j]*w[j]);\n                            ke[k  ][l  ] += Kbc(0,0); ke[k  ][l+1] += Kbc(0,1); ke[k  ][l+2] += Kbc(0,2);\n                            ke[k+1][l  ] += Kbc(1,0); ke[k+1][l+1] += Kbc(1,1); ke[k+1][l+2] += Kbc(1,2);\n                            ke[k+2][l  ] += Kbc(2,0); ke[k+2][l+1] += Kbc(2,1); ke[k+2][l+2] += Kbc(2,2);\n                        }\n                        for (int d=0; d<nmeln; ++d) {\n                            l = (nseln+d)*ndpn;\n                            mat3d Kbd = Ns*(Hm[b]*Hm[d]*eps*detJ[j]*w[j]);\n                            ke[k  ][l  ] += Kbd(0,0); ke[k  ][l+1] += Kbd(0,1); ke[k  ][l+2] += Kbd(0,2);\n                            ke[k+1][l  ] += Kbd(1,0); ke[k+1][l+1] += Kbd(1,1); ke[k+1][l+2] += Kbd(1,2);\n                            ke[k+2][l  ] += Kbd(2,0); ke[k+2][l+1] += Kbd(2,1); ke[k+2][l+2] += Kbd(2,2);\n                        }\n                    }\n                    \n                    // --- M U L T I P H A S I C   S T I F F N E S S ---\n                    if (sporo && mporo)\n                    {\n                        double dt = fem.GetTime().timeIncrement;\n                        \n                        double epsp = m_epsp*pt.m_epsp;\n                        \n                        // --- S O L I D - P R E S S U R E / S O L U T E   C O N T A C T ---\n                        \n                        for (int a=0; a<nseln; ++a) {\n                            k = a*ndpn;\n                            for (int c=0; c<nseln; ++c) {\n                                l = c*ndpn;\n                                vec3d gac = (as[c]*(Hs[a]*wn[j]))*(dt*detJ[j]*w[j]);\n                                ke[k+3][l  ] += gac.x; ke[k+3][l+1] += gac.y; ke[k+3][l+2] += gac.z;\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double epsc = m_epsc*pt.m_epsc[sl[isol]];\n                                    vec3d hac = (as[c]*(Hs[a]*jn[isol][j])*epsc)*(dt*detJ[j]*w[j]);\n                                    ke[k+4+isol][l  ] += hac.x; ke[k+4+isol][l+1] += hac.y; ke[k+4+isol][l+2] += hac.z;\n                                }\n                            }\n                        }\n                        for (int b=0; b<nmeln; ++b) {\n                            k = (nseln+b)*ndpn;\n                            for (int c=0; c<nseln; ++c) {\n                                l = c*ndpn;\n                                vec3d gbc = (-as[c]*(Hm[b]*wn[j]))*(dt*detJ[j]*w[j]);\n                                ke[k+3][l  ] += gbc.x; ke[k+3][l+1] += gbc.y; ke[k+3][l+2] += gbc.z;\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double epsc = m_epsc*pt.m_epsc[sl[isol]];\n                                    vec3d hbc = (-as[c]*(Hm[b]*jn[isol][j])*epsc)*(dt*detJ[j]*w[j]);\n                                    ke[k+4+isol][l  ] += hbc.x; ke[k+4+isol][l+1] += hbc.y; ke[k+4+isol][l+2] += hbc.z;\n                                }\n                            }\n                        }\n                        \n                        // --- P R E S S U R E - P R E S S U R E  /  C O N C E N T R A T I O N - C O N C E N T R A T I O N  C O N T A C T ---\n                        \n                        for (int a=0; a<nseln; ++a) {\n                            k = a*ndpn;\n                            for (int c=0; c<nseln; ++c) {\n                                l = c*ndpn;\n                                double gac = (-Hs[a]*Hs[c]*epsp)*(dt*detJ[j]*w[j]);\n                                ke[k+3][l+3] += gac;\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double epsc = m_epsc*pt.m_epsc[sl[isol]];\n                                    double hac = (-Hs[a]*Hs[c]*epsc)*(dt*detJ[j]*w[j]);\n                                    ke[k+4+isol][l+4+isol] += hac;\n                                }\n                            }\n                            for (int d=0; d<nmeln; ++d) {\n                                l = (nseln+d)*ndpn;\n                                double gad = (Hs[a]*Hm[d]*epsp)*(dt*detJ[j]*w[j]);\n                                ke[k+3][l+3] += gad;\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double epsc = m_epsc*pt.m_epsc[sl[isol]];\n                                    double had = (Hs[a]*Hm[d]*epsc)*(dt*detJ[j]*w[j]);\n                                    ke[k+4+isol][l+4+isol] += had;\n                                }\n                            }\n                        }\n                        for (int b=0; b<nmeln; ++b) {\n                            k = (nseln+b)*ndpn;\n                            for (int c=0; c<nseln; ++c) {\n                                l = c*ndpn;\n                                double gbc = (Hm[b]*Hs[c]*epsp)*(dt*detJ[j]*w[j]);\n                                ke[k+3][l+3] += gbc;\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double epsc = m_epsc*pt.m_epsc[sl[isol]];\n                                    double hbc = (Hm[b]*Hs[c]*epsc)*(dt*detJ[j]*w[j]);\n                                    ke[k+4+isol][l+4+isol] += hbc;\n                                }\n                            }\n                            for (int d=0; d<nmeln; ++d) {\n                                l = (nseln+d)*ndpn;\n                                double gbd = (-Hm[b]*Hm[d]*epsp)*(dt*detJ[j]*w[j]);\n                                ke[k+3][l+3] += gbd;\n                                for (int isol=0; isol<nsol; ++isol) {\n                                    double epsc = m_epsc*pt.m_epsc[sl[isol]];\n                                    double hbd = (-Hm[b]*Hm[d]*epsc)*(dt*detJ[j]*w[j]);\n                                    ke[k+4+isol][l+4+isol] += hbd;\n                                }\n                            }\n                        }\n                    }\n                    \n                    // assemble the global stiffness\n\t\t\t\t\tke.SetNodes(en);\n\t\t\t\t\tke.SetIndices(LM);\n\t\t\t\t\tLS.Assemble(ke);\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedMultiphasicInterface::Augment(int naug, const FETimeInfo& tp)\n{\n    // make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n    vec3d Ln;\n    double Lp;\n    int nsol = (int)m_sid.size();\n    vector<double>Lc(nsol);\n    bool bconv = true;\n    \n    bool bporo = (m_ss.m_bporo && m_ms.m_bporo);\n    bool bsolu = (m_ss.m_bsolu && m_ms.m_bsolu);\n\n\tint NS = m_ss.Elements();\n\tint NM = m_ms.Elements();\n\n    // --- c a l c u l a t e   i n i t i a l   n o r m s ---\n    // a. normal component\n    double normL0 = 0, normP = 0, normDP = 0, normC = 0;\n    vector<double>normDC(nsol,0);\n    for (int i=0; i<NS; ++i)\n    {\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FETiedMultiphasicContactPoint& ds = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += ds.m_Lmd*ds.m_Lmd;\n        }\n    }\n    for (int i=0; i<NM; ++i)\n    {\n\t\tFESurfaceElement& el = m_ms.Element(i);\n        for (int j=0; j<el.GaussPoints(); ++j)\n        {\n            FETiedMultiphasicContactPoint& dm = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\t\t\tnormL0 += dm.m_Lmd*dm.m_Lmd;\n        }\n    }\n    \n    // b. gap component\n    // (is calculated during update)\n    double maxgap = 0;\n    double maxpg = 0;\n    vector<double> maxcg(nsol,0);\n    \n    // update Lagrange multipliers\n    double normL1 = 0, eps, epsp, epsc;\n\tfor (int i = 0; i<NS; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ss.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FETiedMultiphasicContactPoint& ds = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on primary surface\n            eps = m_epsn*ds.m_epsn;\n            ds.m_Lmd = ds.m_Lmd + ds.m_dg*eps;\n            \n            normL1 += ds.m_Lmd*ds.m_Lmd;\n            \n            if (m_ss.m_bporo) {\n                Lp = 0;\n                Lc.assign(nsol, 0);\n                if (ds.m_pme) {\n                    epsp = m_epsp*ds.m_epsp;\n                    Lp = ds.m_Lmp + epsp*ds.m_pg;\n                    maxpg = max(maxpg,fabs(ds.m_pg));\n                    normDP += ds.m_pg*ds.m_pg;\n                    for (int isol=0; isol<nsol; ++isol) {\n                        int l = m_ssl[isol];\n                        epsc = m_epsc*ds.m_epsc[l];\n                        Lc[isol] = ds.m_Lmc[l] + epsc*ds.m_cg[l];\n                        maxcg[isol] = max(maxcg[isol],fabs(ds.m_cg[l]));\n                        normDC[isol] += ds.m_cg[l]*ds.m_cg[l];\n                    }\n                }\n                ds.m_Lmp = Lp;\n                for (int isol=0; isol<nsol; ++isol)\n                    ds.m_Lmc[m_ssl[isol]] = Lc[isol];\n            }\n            \n            maxgap = max(maxgap,sqrt(ds.m_dg*ds.m_dg));\n        }\n    }\n    \n\tfor (int i = 0; i<NM; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ms.Element(i);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n            FETiedMultiphasicContactPoint& dm = static_cast<FETiedMultiphasicContactPoint&>(*el.GetMaterialPoint(j));\n\n            // update Lagrange multipliers on secondary surface\n            eps = m_epsn*dm.m_epsn;\n            dm.m_Lmd = dm.m_Lmd + dm.m_dg*eps;\n            \n            normL1 += dm.m_Lmd*dm.m_Lmd;\n            \n            if (m_ms.m_bporo) {\n                Lp = 0;\n                Lc.assign(nsol, 0);\n                if (dm.m_pme) {\n                    epsp = m_epsp*dm.m_epsp;\n                    Lp = dm.m_Lmp + epsp*dm.m_pg;\n                    maxpg = max(maxpg,fabs(dm.m_pg));\n                    normDP += dm.m_pg*dm.m_pg;\n                    for (int isol=0; isol<nsol; ++isol) {\n                        int l = m_msl[isol];\n                        epsc = m_epsc*dm.m_epsc[l];\n                        Lc[isol] = dm.m_Lmc[l] + epsc*dm.m_cg[l];\n                        maxcg[isol] = max(maxcg[isol],fabs(dm.m_cg[l]));\n                        normDC[isol] += dm.m_cg[l]*dm.m_cg[l];\n                    }\n                }\n                dm.m_Lmp = Lp;\n                for (int isol=0; isol<nsol; ++isol)\n                    dm.m_Lmc[m_msl[isol]] = Lc[isol];\n            }\n            \n            maxgap = max(maxgap,sqrt(dm.m_dg*dm.m_dg));\n        }\n    }\n    \n    // Ideally normP should be evaluated from the fluid pressure at the\n    // contact interface (not easily accessible).  The next best thing\n    // is to use the contact traction.\n    normP = normL1;\n    normC = normL1/(m_Rgas*m_Tabs);\n    \n    // calculate relative norms\n    double lnorm = (normL1 != 0 ? fabs((normL1 - normL0) / normL1) : fabs(normL1 - normL0)); \n    double pnorm = (normP != 0 ? (normDP/normP) : normDP); \n    vector<double> cnorm(nsol);\n    for (int isol=0; isol<nsol; ++isol)\n        cnorm[isol] = (normC != 0 ? (normDC[isol]/normC) : normDC[isol]);\n    \n    // check convergence\n    if ((m_gtol > 0) && (maxgap > m_gtol)) bconv = false;\n    if ((m_ptol > 0) && (bporo && maxpg > m_ptol)) bconv = false;\n    for (int isol=0; isol<nsol; ++isol)\n        if ((m_ctol > 0) && (bsolu && maxcg[isol] > m_ctol)) bconv = false;\n    \n    if ((m_atol > 0) && (lnorm > m_atol)) bconv = false;\n    if ((m_atol > 0) && (pnorm > m_atol)) bconv = false;\n    for (int isol=0; isol<nsol; ++isol)\n        if ((m_atol > 0) && (cnorm[isol] > m_atol)) bconv = false;\n    \n    if (naug < m_naugmin ) bconv = false;\n    if (naug >= m_naugmax) bconv = true;\n    \n    feLog(\" sliding interface # %d\\n\", GetID());\n    feLog(\"                        CURRENT        REQUIRED\\n\");\n    feLog(\"    D multiplier : %15le\", lnorm);\n    if (m_atol > 0) feLog(\"%15le\\n\", m_atol);\n    else feLog(\"       ***\\n\");\n    if (bporo) {\n        feLog(\"    P gap        : %15le\", pnorm);\n        if (m_atol > 0) feLog(\"%15le\\n\", m_atol);\n        else feLog(\"       ***\\n\"); }\n    for (int isol=0; isol<nsol; ++isol) {\n        feLog(\"    C[%d] gap   : %15le\", m_sid[isol], cnorm[isol]);\n        if (m_atol > 0) feLog(\"%15le\\n\", m_atol);\n        else feLog(\"       ***\\n\");\n    }\n    \n    feLog(\"    maximum gap  : %15le\", maxgap);\n    if (m_gtol > 0) feLog(\"%15le\\n\", m_gtol);\n    else feLog(\"       ***\\n\");\n    if (bporo) {\n        feLog(\"    maximum pgap : %15le\", maxpg);\n        if (m_ptol > 0) feLog(\"%15le\\n\", m_ptol);\n        else feLog(\"       ***\\n\");\n    }\n    for (int isol=0; isol<nsol; ++isol) {\n        feLog(\"    maximum cgap[%d] : %15le\", m_sid[isol], maxcg[isol]);\n        if (m_ctol > 0) feLog(\"%15le\\n\", m_ctol);\n        else feLog(\"       ***\\n\");\n    }\n    \n    return bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedMultiphasicInterface::Serialize(DumpStream &ar)\n{\n    // store base class data\n    FEContactInterface::Serialize(ar);\n    \n    // store contact surface data\n    m_ms.Serialize(ar);\n    m_ss.Serialize(ar);\n\n    // serialize element pointers\n    SerializeElementPointers(m_ss, m_ms, ar);\n    SerializeElementPointers(m_ms, m_ss, ar);\n\n\t// serialize interface data\n\tar & m_epsp;\n\tar & m_epsc;\n\tar & m_sid;\n\tar & m_ssl;\n\tar & m_msl;\n\tar & m_sz;\n    \n    if (ar.IsShallow()) return;\n    ar & m_dofP & m_dofC;\n    ar & m_Rgas & m_Tabs;\n}\n\n//-----------------------------------------------------------------------------\nFESoluteData* FETiedMultiphasicInterface::FindSoluteData(int nid)\n{\n    FEModel& fem = *GetFEModel();\n    int N = GetFEModel()->GlobalDataItems();\n    for (int i=0; i<N; ++i)\n    {\n        FESoluteData* psd = dynamic_cast<FESoluteData*>(fem.GetGlobalData(i));\n        if (psd && (psd->GetID() == nid)) return psd;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "FEBioMix/FETiedMultiphasicInterface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FETiedBiphasicInterface.h\"\n#include \"FESolute.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FETiedMultiphasicContactPoint: public FETiedBiphasicContactPoint\n{\npublic:\n    vector<double>  m_Lmc;  //!< Lagrange multipliers for solute concentrations\n    vector<double>  m_epsc; //!< concentration penatly factors\n    vector<double>  m_cg;   //!< concentration \"gap\"\n\n    void Init() override\n    {\n        FEBiphasicContactPoint::Init();\n        m_Gap = m_dg = m_nu = m_Lmd = m_tr = vec3d(0,0,0);\n        m_rs = vec2d(0,0);\n        m_epsn = 1.0;\n        m_epsp = 1.0;\n    }\n    \n    void Serialize(DumpStream& ar) override;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBIOMIX_API FETiedMultiphasicSurface : public FEBiphasicContactSurface\n{\npublic:\n    //! constructor\n    FETiedMultiphasicSurface(FEModel* pfem);\n    \n    //! initialization\n    bool Init() override;\n    \n    //! calculate the nodal normals\n    void UpdateNodeNormals();\n    \n    void Serialize(DumpStream& ar) override;\n    \n    void SetPoroMode(bool bporo) { m_bporo = bporo; }\n    \n    void UnpackLM(FEElement& el, vector<int>& lm) override;\n    \n\t//! create material point data\n\tFEMaterialPoint* CreateMaterialPoint() override;\n\npublic:\n    bool                    m_bporo;\t//!< set poro-mode\n    bool\t\t\t\t\tm_bsolu;\t//!< set solute-mode\n    \n    vector<bool>\t\t\tm_poro;\t//!< surface element poro status\n    vector<vec3d>\t\t\tm_nn;\t//!< node normals\n    vector<int>\t\t\t\tm_sid;\t//!< list of solute id's for this surface\n    \nprotected:\n    int\tm_dofC;\n};\n\n//-----------------------------------------------------------------------------\nclass FETiedMultiphasicInterface :\tpublic FEContactInterface\n{\npublic:\n    //! constructor\n    FETiedMultiphasicInterface(FEModel* pfem);\n    \n    //! destructor\n    ~FETiedMultiphasicInterface();\n    \n    //! initialization\n    bool Init() override;\n    \n    //! interface activation\n    void Activate() override;\n    \n    //! serialize data to archive\n    void Serialize(DumpStream& ar) override;\n    \n    //! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n    //! return integration rule class\n    bool UseNodalIntegration() override { return false; }\n    \n    //! build the matrix profile for use in the stiffness matrix\n    void BuildMatrixProfile(FEGlobalMatrix& K) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n    void InitialProjection(FETiedMultiphasicSurface& ss, FETiedMultiphasicSurface& ms);\n    void ProjectSurface(FETiedMultiphasicSurface& ss, FETiedMultiphasicSurface& ms);\n    \n    //! calculate penalty factor\n    void UpdateAutoPenalty();\n    \n    void CalcAutoPenalty(FETiedMultiphasicSurface& s);\n    \n    void CalcAutoPressurePenalty(FETiedMultiphasicSurface& s);\n    double AutoPressurePenalty(FESurfaceElement& el, FETiedMultiphasicSurface& s);\n    \n    void CalcAutoConcentrationPenalty(FETiedMultiphasicSurface& s, const int isol);\n    double AutoConcentrationPenalty(FESurfaceElement& el, FETiedMultiphasicSurface& s, const int isol);\n    \n    double AutoPenalty(FESurfaceElement& el, FESurface &s);\n    \n    //! get solute data\n    FESoluteData* FindSoluteData(int nid);\n    \npublic:\n\tFETiedMultiphasicSurface\tm_ss;\t//!< primary surface\n\tFETiedMultiphasicSurface\tm_ms;\t//!< secondary surface\n    \n    int\t\t\t\tm_knmult;\t\t//!< higher order stiffness multiplier\n    bool\t\t\tm_btwo_pass;\t//!< two-pass flag\n    double\t\t\tm_atol;\t\t\t//!< augmentation tolerance\n    double\t\t\tm_gtol;\t\t\t//!< gap tolerance\n    double\t\t\tm_ptol;\t\t\t//!< pressure gap tolerance\n    double\t\t\tm_ctol;\t\t\t//!< concentration gap tolerance\n    double\t\t\tm_stol;\t\t\t//!< search tolerance\n    bool\t\t\tm_bsymm;\t\t//!< use symmetric stiffness components only\n    double\t\t\tm_srad;\t\t\t//!< contact search radius\n    int\t\t\t\tm_naugmax;\t\t//!< maximum nr of augmentations\n    int\t\t\t\tm_naugmin;\t\t//!< minimum nr of augmentations\n    \n    double\t\t\tm_epsn;\t\t\t//!< normal penalty factor\n    bool\t\t\tm_bautopen;\t\t//!< use autopenalty factor\n    bool            m_bupdtpen;     //!< update penalty at each time step\n\n    // multiphasic contact parameters\n    double\t\t\tm_epsp;         //!< fluid flow rate penalty\n    double          m_epsc;\t\t\t//!< solute molar flow rate penalty\n    double          m_Rgas;\t\t\t//!< universal gas constant\n    double          m_Tabs;\t\t\t//!< absolute temperature\n    vector<int> m_sid;\t\t\t\t//!< list of solute ids common to both contact surfaces\n    vector<int> m_ssl;\t\t\t\t//!< list of primary surface solutes common to both contact surfaces\n    vector<int> m_msl;\t\t\t\t//!< list of secondary surface solutes common to both contact surfaces\n    vector<int> m_sz;               //!< charge number of solutes common to both contact surfaces\n    \nprotected:\n    int\tm_dofP;\n    int\tm_dofC;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FETriphasic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETriphasic.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/log.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\n// Material parameters for the FETriphasic material\nBEGIN_FECORE_CLASS(FETriphasic, FEMaterial)\n\tADD_PARAMETER(m_phi0   , FE_RANGE_CLOSED     (0.0, 1.0), \"phi0\");\n\tADD_PARAMETER(m_rhoTw  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"fluid_density\");\n\tADD_PARAMETER(m_penalty, FE_RANGE_GREATER_OR_EQUAL(0.0), \"penalty\");\n\tADD_PARAMETER(m_cFr    , \"fixed_charge_density\");\n\n\t// set material properties\n\tADD_PROPERTY(m_pSolid , \"solid\"              , FEProperty::Required | FEProperty::TopLevel);\n\tADD_PROPERTY(m_pPerm  , \"permeability\"       );\n\tADD_PROPERTY(m_pOsmC  , \"osmotic_coefficient\");\n\tADD_PROPERTY(m_pSolute, \"solute\"             );\n\n    ADD_PROPERTY(m_Q, \"mat_axis\", FEProperty::Optional);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! FETriphasic constructor\n\nFETriphasic::FETriphasic(FEModel* pfem) : FEMaterial(pfem)\n{\t\n\tm_cFr = 0;\n\tm_Rgas = 0; m_Tabs = 0; m_Fc = 0;\n\tm_phi0 = 0;\n\tm_rhoTw = 0;\n\tm_penalty = 1;\n\n\tm_pSolid = 0;\n\tm_pPerm = 0;\n\tm_pOsmC = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasic::AddSolute(FESolute* ps)\n{\n\tm_pSolute.push_back(ps);\n}\n\n//-----------------------------------------------------------------------------\nbool FETriphasic::Init()\n{\n\t// make sure there are exactly two solutes\n\tif (m_pSolute.size() != 2) {\n\t\tfeLogError(\"Exactly two solutes must be specified\");\n\t\treturn false;\n\t}\n\t\n\t// Set the solute IDs since they are referenced in the FESolute::Init() function\n\tm_pSolute[0]->SetSoluteLocalID(0);\n    m_pSolute[1]->SetSoluteLocalID(1);\n\n    if (m_pSolid->Init() == false) return false;\n    if (m_pPerm->Init() == false) return false;\n    if (m_pOsmC->Init() == false) return false;\n    for (int i=0; i<Solutes(); ++i)\n        if (m_pSolute[i]->Init() == false) return false;\n\n\t// Call base class initialization. This will also initialize all properties.\n\tif (FEMaterial::Init() == false) return false;\n\n\t// parameter checking\n\tif ((m_pSolute[0]->ChargeNumber() != 1) && (m_pSolute[0]->ChargeNumber() != -1)) {\n\t\tfeLogError(\"charge_number for first solute must be +1 or -1\");\n\t\treturn false;\n\t}\n\tif ((m_pSolute[1]->ChargeNumber() != 1) && (m_pSolute[1]->ChargeNumber() != -1)) {\n\t\tfeLogError(\"charge_number for second solute must be +1 or -1\");\n\t\treturn false;\n\t}\n\tif (m_pSolute[0]->ChargeNumber() != -m_pSolute[1]->ChargeNumber()) {\n\t\tfeLogError(\"charge_number of solutes must have opposite signs\");\n\t\treturn false;\n\t}\n\t\n\tm_Rgas = GetGlobalConstant(\"R\");\n\tm_Tabs = GetGlobalConstant(\"T\");\n\tm_Fc   = GetGlobalConstant(\"Fc\");\n\t\n\tif (m_Rgas <= 0) { feLogError(\"A positive universal gas constant R must be defined in Globals section\"); return false; }\n\tif (m_Tabs <= 0) { feLogError(\"A positive absolute temperature T must be defined in Globals section\"  ); return false; }\n\tif (m_Fc   <= 0) { feLogError(\"A positive Faraday constant Fc must be defined in Globals section\"     ); return false; }\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// update specialized material points\nvoid FETriphasic::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n    m_pSolid->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pPerm->UpdateSpecializedMaterialPoints(mp, tp);\n    m_pOsmC->UpdateSpecializedMaterialPoints(mp, tp);\n    for (int i=0; i<Solutes(); ++i)\n        m_pSolute[i]->UpdateSpecializedMaterialPoints(mp, tp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasic::Serialize(DumpStream& ar)\n{\n\tFEMaterial::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Rgas & m_Tabs & m_Fc;\n}\n\n//-----------------------------------------------------------------------------\n//! Porosity in current configuration\ndouble FETriphasic::Porosity(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pet = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n\t// porosity\n\t//\tdouble phiw = 1 - m_phi0/J;\n\tdouble phi0 = pet.m_phi0t;\n\tdouble phiw = 1 - phi0/J;\n\t// check for pore collapse\n\t// TODO: throw an error if pores collapse\n\tphiw = (phiw > 0) ? phiw : 0;\n\t\n\treturn phiw;\n}\n\n//-----------------------------------------------------------------------------\n//! Fixed charge density in current configuration\ndouble FETriphasic::FixedChargeDensity(FEMaterialPoint& pt)\n{\n\tFEElasticMaterialPoint& et = *pt.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& pet = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// relative volume\n\tdouble J = et.m_J;\n    double phi0 = pet.m_phi0;\n\tdouble phisr = pet.m_phi0t;\n\tdouble cF = m_cFr(pt)*(1-phi0)/(J-phisr);\n\t\n\treturn cF;\n}\n\n//-----------------------------------------------------------------------------\n//! Electric potential\ndouble FETriphasic::ElectricPotential(FEMaterialPoint& pt, const bool eform)\n{\n\tint i, j;\n\t\n\t// Solve electroneutrality polynomial for zeta\n\tFESolutesMaterialPoint& set = *pt.ExtractData<FESolutesMaterialPoint>();\n\tconst int nsol = 2;\n\tdouble cF = FixedChargeDensity(pt);\n\tdouble c[2];\t\t// effective concentration\n\tdouble khat[2];\t\t// solubility\n\tint z[2];\t\t\t// charge number\n\tfor (i=0; i<nsol; ++i) {\n\t\tc[i] = set.m_c[i];\n\t\tkhat[i] = m_pSolute[i]->m_pSolub->Solubility(pt);\n\t\tz[i] = m_pSolute[i]->ChargeNumber();\n\t}\n\tdouble zeta, psi;\n\t\n\t// evaluate polynomial coefficients\n\tdouble a[3] = {0};\n\tfor (i=0; i<nsol; ++i) {\n\t\tj = z[i] + 1;\n\t\ta[j] += z[i]*khat[i]*c[i];\n\t}\n\ta[1] = cF;\n\t\n\t// solve polynomial\n\tzeta = 1.0;\n\tif (a[2]) {\n\t\tzeta = (-a[1]+sqrt(a[1]*a[1]-4*a[0]*a[2]))/(2*a[2]);\t// quadratic\n\t} else if (a[1]) {\n\t\tzeta = -a[0]/a[1];\t\t\t// linear\n\t}\n\t\n\t// Return exponential (non-dimensional) form if desired\n\tif (eform) return zeta;\n\t\n\t// Otherwise return dimensional value of electric potential\n\tpsi = -m_Rgas*m_Tabs/m_Fc*log(zeta);\n\t\n\treturn psi;\n}\n\n//-----------------------------------------------------------------------------\n//! actual concentration\ndouble FETriphasic::Concentration(FEMaterialPoint& pt, const int ion)\n{\n    FESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n    \n    // effective concentration\n    double c = spt.m_c[ion];\n    \n    // partition coefficient\n    double kappa = PartitionCoefficient(pt, ion);\n    \n    // actual concentration\n    double ca = kappa*c;\n    \n    return ca;\n}\n\n//-----------------------------------------------------------------------------\n//! The stress of a triphasic material is the sum of the fluid pressure\n//! and the elastic stress. Note that this function is declared in the base class\n//! so you do not have to reimplement it in a derived class, unless additional\n//! pressure terms are required.\n\nmat3ds FETriphasic::Stress(FEMaterialPoint& mp)\n{\n\t// calculate solid material stress\n\tmat3ds s = m_pSolid->Stress(mp);\n\t\n\t// fluid pressure\n\tdouble p = Pressure(mp);\n\t\n\t// add fluid pressure\n\ts.xx() -= p;\n\ts.yy() -= p;\n\ts.zz() -= p;\n\t\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! The tangent is the elastic tangent. Note\n//! that this function is declared in the base class, so you don't have to \n//! reimplement it unless additional tangent components are required.\n\ntens4ds FETriphasic::Tangent(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& ept = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEBiphasicMaterialPoint& ppt = *mp.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *mp.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// call solid tangent routine\n\ttens4ds C = m_pSolid->Tangent(mp);\n\t\n\t// relative volume and solid volume fraction\n\tdouble J = ept.m_J;\n\tdouble phi0 = ppt.m_phi0t;\n\t\n\t// get the charge density and its derivatives\n\tdouble cF = FixedChargeDensity(mp);\n\tdouble dcFdJ = -cF/(J - phi0);\n\t\n\t// fluid pressure and solute concentration\n\tdouble p = Pressure(mp);\n\t\n\t// get the effective concentration\n\tdouble c[2] = {spt.m_c[0],spt.m_c[1]};\n\t\n\t// get the charge number\n\tint z[2] = {m_pSolute[0]->ChargeNumber(),m_pSolute[1]->ChargeNumber()};\n\t\n\t// evaluate the solubility and its derivatives w.r.t. J and c\n\tdouble khat[2] = {\n\t\tm_pSolute[0]->m_pSolub->Solubility(mp),\n\t\tm_pSolute[1]->m_pSolub->Solubility(mp)};\n\tdouble dkhdJ[2] = {\n\t\tm_pSolute[0]->m_pSolub->Tangent_Solubility_Strain(mp),\n\t\tm_pSolute[1]->m_pSolub->Tangent_Solubility_Strain(mp)};\n\t\n\t// evaluate electric potential (nondimensional exponential form) and its derivatives\n\t// also evaluate partition coefficients and their derivatives\n\tdouble zeta = ElectricPotential(mp, true);\n\tdouble zz[2] = {pow(zeta, z[0]), pow(zeta, z[1])};\n\tdouble kappa[2] = {zz[0]*khat[0], zz[1]*khat[1]};\n\tdouble den = SQR(z[0])*kappa[0]*c[0]+SQR(z[1])*kappa[1]*c[1];\n\tdouble zidzdJ = 0;\n\tif (den > 0) zidzdJ = -(dcFdJ+z[0]*zz[0]*dkhdJ[0]*c[0]\n\t\t\t\t\t\t\t+z[1]*zz[1]*dkhdJ[1]*c[1])/den;\n\tdouble dkdJ[2] = {\n\t\tzz[0]*dkhdJ[0]+z[0]*kappa[0]*zidzdJ,\n\t\tzz[1]*dkhdJ[1]+z[1]*kappa[1]*zidzdJ};\n\t\n\t// osmotic coefficient and its derivative w.r.t. strain\n\tdouble osmc = m_pOsmC->OsmoticCoefficient(mp);\n\tdouble dodJ = m_pOsmC->Tangent_OsmoticCoefficient_Strain(mp);\n\t\n\tdouble dp = m_Rgas*m_Tabs*J*(c[0]*(osmc*dkdJ[0]+dodJ*kappa[0]) +c[1]*(osmc*dkdJ[1]+dodJ*kappa[1]));\n\t\n\t// adjust tangent for pressures\n\tdouble D[6][6] = {0};\n\tC.extract(D);\n\t\n\tD[0][0] -= -p + dp;\n\tD[1][1] -= -p + dp;\n\tD[2][2] -= -p + dp;\n\t\n\tD[0][1] -= p + dp; D[1][0] -= p + dp;\n\tD[1][2] -= p + dp; D[2][1] -= p + dp;\n\tD[0][2] -= p + dp; D[2][0] -= p + dp;\n\t\n\tD[3][3] -= -p;\n\tD[4][4] -= -p;\n\tD[5][5] -= -p;\n\t\n\treturn tens4ds(D);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate fluid flux\n\nvec3d FETriphasic::FluidFlux(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// fluid volume fraction (porosity) in current configuration\n\tdouble phiw = Porosity(pt);\n\t\n\t// pressure gradient\n\tvec3d gradp = ppt.m_gradp;\n\t\n\t// concentration\n\tdouble c[2] = {spt.m_c[0], spt.m_c[1]};\n\t\n\t// concentration gradient\n\tvec3d gradc[2] = {spt.m_gradc[0], spt.m_gradc[1]};\n\t\n\t// solute diffusivity in mixture\n\tmat3ds D[2] = {\n\t\tm_pSolute[0]->m_pDiff->Diffusivity(pt),\n\t\tm_pSolute[1]->m_pDiff->Diffusivity(pt)};\n\t\n\t// solute free diffusivity\n\tdouble D0[2] = {\n\t\tm_pSolute[0]->m_pDiff->Free_Diffusivity(pt),\n\t\tm_pSolute[1]->m_pDiff->Free_Diffusivity(pt)};\n\t\n\t// solubility\n\tdouble khat[2] = {\n\t\tm_pSolute[0]->m_pSolub->Solubility(pt),\n\t\tm_pSolute[1]->m_pSolub->Solubility(pt)};\n\tint z[2] = {\n\t\tm_pSolute[0]->ChargeNumber(),\n\t\tm_pSolute[1]->ChargeNumber()};\n\tdouble zeta = ElectricPotential(pt, true);\n\tdouble zz[2] = {pow(zeta, z[0]),pow(zeta, z[1])};\n\tdouble kappa[2] = {zz[0]*khat[0],zz[1]*khat[1]};\n\t\n\t// identity matrix\n\tmat3dd I(1);\n\t\n\t// hydraulic permeability\n\tmat3ds kt = m_pPerm->Permeability(pt);\n\t\n\t// effective hydraulic permeability\n\tmat3ds ke = kt.inverse() +\n\t( (I-D[0]/D0[0])*(kappa[0]*c[0]/D0[0])\n\t +(I-D[1]/D0[1])*(kappa[1]*c[1]/D0[1])\n\t )*(m_Rgas*m_Tabs/phiw);\n\tke = ke.inverse();\n\t\n\t// fluid flux w\n\tvec3d w = -(ke*(gradp + ((D[0]*gradc[0])*(kappa[0]/D0[0])\n\t\t\t\t\t+ (D[1]*gradc[1])*(kappa[1]/D0[1]))*m_Rgas*m_Tabs));\n\t\n\treturn w;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate solute molar flux\n\nvec3d FETriphasic::SoluteFlux(FEMaterialPoint& pt, const int ion)\n{\n\tFESolutesMaterialPoint& spt = *pt.ExtractData<FESolutesMaterialPoint>();\n\t\n\t// fluid volume fraction (porosity) in current configuration\n\tdouble phiw = Porosity(pt);\n\t\n\t// concentration\n\tdouble c = spt.m_c[ion];\n\t\n\t// concentration gradient\n\tvec3d gradc = spt.m_gradc[ion];\n\t\n\t// solute diffusivity in mixture\n\tmat3ds D = m_pSolute[ion]->m_pDiff->Diffusivity(pt);\n\t\n\t// solute free diffusivity\n\tdouble D0 = m_pSolute[ion]->m_pDiff->Free_Diffusivity(pt);\n\t\n\t// solubility\n\tdouble khat = m_pSolute[ion]->m_pSolub->Solubility(pt);\n\tint z = m_pSolute[ion]->ChargeNumber();\n\tdouble zeta = ElectricPotential(pt, true);\n\tdouble zz = pow(zeta, z);\n\tdouble kappa = zz*khat;\n\n\t// fluid flux w\n\tvec3d w = FluidFlux(pt);\n\t\n\t// solute flux j\n\tvec3d j = (D*(w*(c/D0) - gradc*phiw))*kappa;\n\t\n\treturn j;\n}\n\n//-----------------------------------------------------------------------------\n//! actual fluid pressure\ndouble FETriphasic::Pressure(FEMaterialPoint& pt)\n{\n\tFEBiphasicMaterialPoint& ppt = *pt.ExtractData<FEBiphasicMaterialPoint>();\n\t\n\t// effective pressure\n\tdouble p = ppt.m_p;\n\t\n\t// effective concentration\n\tdouble ca[2] = {Concentration(pt, 0), Concentration(pt, 1)};\n\t\n\t// osmotic coefficient\n\tdouble osmc = m_pOsmC->OsmoticCoefficient(pt);\n\t\n\t// actual pressure\n\tdouble pa = p + m_Rgas*m_Tabs*osmc*(ca[0]+ca[1]);\n\t\n\treturn pa;\n}\n\n//-----------------------------------------------------------------------------\n//! Current density\nvec3d FETriphasic::CurrentDensity(FEMaterialPoint& pt)\n{\n\tvec3d j[2];\n\tj[0] = SoluteFlux(pt, 0);\n\tj[1] = SoluteFlux(pt, 1);\n\tint z[2] = {m_pSolute[0]->ChargeNumber(), m_pSolute[1]->ChargeNumber()};\n\t\n\tvec3d Ie = (j[0]*z[0] + j[1]*z[1])*m_Fc;\n\t\n\treturn Ie;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficient\ndouble FETriphasic::PartitionCoefficient(FEMaterialPoint& pt, const int sol)\n{\n    \n    // solubility\n    double khat = m_pSolute[sol]->m_pSolub->Solubility(pt);\n    // charge number\n    int z = m_pSolute[sol]->ChargeNumber();\n    // electric potential\n    double zeta = ElectricPotential(pt, true);\n    double zz = pow(zeta, z);\n    // partition coefficient\n    double kappa = zz*khat;\n    \n    return kappa;\n}\n\n//-----------------------------------------------------------------------------\n//! partition coefficients and their derivatives\nvoid FETriphasic::PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                                  vector<double>& dkdJ,\n                                                  vector< vector<double> >& dkdc)\n{\n    int isol, jsol, ksol;\n    \n    FEElasticMaterialPoint& ept = *(mp.ExtractData<FEElasticMaterialPoint>());\n    FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n    FESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n    \n    const int nsol = (int)m_pSolute.size();\n    \n    vector<double> c(nsol);\n    vector<int> z(nsol);\n    vector<double> khat(nsol);\n    vector<double> dkhdJ(nsol);\n    vector<double> dkhdJJ(nsol);\n    vector< vector<double> > dkhdc(nsol, vector<double>(nsol));\n    vector< vector<double> > dkhdJc(nsol, vector<double>(nsol));\n    vector< vector< vector<double> > > dkhdcc(nsol, dkhdc);\t// use dkhdc to initialize only\n    vector<double> zz(nsol);\n    kappa.resize(nsol);\n    \n    double den = 0;\n    double num = 0;\n    double zeta = ElectricPotential(mp, true);\n    \n    for (isol=0; isol<nsol; ++isol) {\n        // get the effective concentration, its gradient and its time derivative\n        c[isol] = spt.m_c[isol];\n        // get the charge number\n        z[isol] = m_pSolute[isol]->ChargeNumber();\n        // evaluate the solubility and its derivatives w.r.t. J and c\n        khat[isol] = m_pSolute[isol]->m_pSolub->Solubility(mp);\n        dkhdJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain(mp);\n        dkhdJJ[isol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Strain(mp);\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkhdc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration(mp,jsol);\n            dkhdJc[isol][jsol] = m_pSolute[isol]->m_pSolub->Tangent_Solubility_Strain_Concentration(mp,jsol);\n            for (ksol=0; ksol<nsol; ++ksol) {\n                dkhdcc[isol][jsol][ksol] =\n                m_pSolute[isol]->m_pSolub->Tangent_Solubility_Concentration_Concentration(mp,jsol,ksol);\n            }\n        }\n        zz[isol] = pow(zeta, z[isol]);\n        kappa[isol] = zz[isol]*khat[isol];\n        den += SQR(z[isol])*kappa[isol]*c[isol];\n        num += pow((double)z[isol],3)*kappa[isol]*c[isol];\n    }\n    \n    // get the charge density and its derivatives\n    double J = ept.m_J;\n    double phi0 = ppt.m_phi0t;\n    double cF = FixedChargeDensity(mp);\n    double dcFdJ = -cF/(J - phi0);\n    double dcFdJJ = 2*cF/SQR(J-phi0);\n    \n    // evaluate electric potential (nondimensional exponential form) and its derivatives\n    // also evaluate partition coefficients and their derivatives\n    double zidzdJ = 0;\n    double zidzdJJ = 0, zidzdJJ1 = 0, zidzdJJ2 = 0;\n    vector<double> zidzdc(nsol,0);\n    vector<double> zidzdJc(nsol,0), zidzdJc1(nsol,0), zidzdJc2(nsol,0);\n    vector< vector<double> > zidzdcc(nsol, vector<double>(nsol,0));\n    vector< vector<double> > zidzdcc1(nsol, vector<double>(nsol,0));\n    vector<double> zidzdcc2(nsol,0);\n    double zidzdcc3 = 0;\n    \n    if (den > 0) {\n        \n        for (isol=0; isol<nsol; ++isol)\n            zidzdJ += z[isol]*zz[isol]*dkhdJ[isol]*c[isol];\n        zidzdJ = -(dcFdJ+zidzdJ)/den;\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdJJ1 += SQR(z[jsol])*c[jsol]*(z[jsol]*zidzdJ*kappa[jsol]+zz[jsol]*dkhdJ[jsol]);\n                zidzdJJ2 += z[jsol]*zz[jsol]*c[jsol]*(zidzdJ*z[jsol]*dkhdJ[jsol]+dkhdJJ[jsol]);\n                zidzdc[isol] += z[jsol]*zz[jsol]*dkhdc[jsol][isol]*c[jsol];\n            }\n            zidzdc[isol] = -(z[isol]*kappa[isol]+zidzdc[isol])/den;\n            zidzdcc3 += pow(double(z[isol]),3)*kappa[isol]*c[isol];\n        }\n        zidzdJJ = zidzdJ*(zidzdJ-zidzdJJ1/den)-(dcFdJJ+zidzdJJ2)/den;\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdJc1[isol] += SQR(z[jsol])*c[jsol]*(zidzdc[isol]*z[jsol]*kappa[jsol]+zz[jsol]*dkhdc[jsol][isol]);\n                zidzdJc2[isol] += z[jsol]*zz[jsol]*c[jsol]*(zidzdc[isol]*z[jsol]*dkhdJ[jsol]+dkhdJc[jsol][isol]);\n                zidzdcc2[isol] += SQR(z[jsol])*zz[jsol]*c[jsol]*dkhdc[jsol][isol];\n                for (ksol=0; ksol<nsol; ++ksol)\n                    zidzdcc1[isol][jsol] += z[ksol]*zz[ksol]*c[ksol]*dkhdcc[ksol][isol][jsol];\n            }\n            zidzdJc[isol] = zidzdJ*(zidzdc[isol]-(SQR(z[isol])*kappa[isol] + zidzdJc1[isol])/den)\n            -(z[isol]*zz[isol]*dkhdJ[isol] + zidzdJc2[isol])/den;\n        }\n        \n        for (isol=0; isol<nsol; ++isol) {\n            for (jsol=0; jsol<nsol; ++jsol) {\n                zidzdcc[isol][jsol] = zidzdc[isol]*zidzdc[jsol]*(1 - zidzdcc3/den)\n                - zidzdcc1[isol][jsol]/den\n                - z[isol]*(z[isol]*kappa[isol]*zidzdc[jsol]+zz[isol]*dkhdc[isol][jsol])/den\n                - z[jsol]*(z[jsol]*kappa[jsol]*zidzdc[isol]+zz[jsol]*dkhdc[jsol][isol])/den\n                - zidzdc[jsol]*zidzdcc2[isol]/den\n                - zidzdc[isol]*zidzdcc2[jsol]/den;\n            }\n        }\n    }\n    \n    dkdJ.resize(nsol);\n    dkdc.resize(nsol, vector<double>(nsol,0));\n    \n    for (isol=0; isol<nsol; ++isol) {\n        dkdJ[isol] = zz[isol]*dkhdJ[isol]+z[isol]*kappa[isol]*zidzdJ;\n        for (jsol=0; jsol<nsol; ++jsol) {\n            dkdc[isol][jsol] = zz[isol]*dkhdc[isol][jsol]+z[isol]*kappa[isol]*zidzdc[jsol];\n        }\n    }\n}\n\ndouble FETriphasic::GetReferentialFixedChargeDensity(const FEMaterialPoint& mp)\n{\n\tconst FEElasticMaterialPoint* ept = (mp.ExtractData<FEElasticMaterialPoint >());\n\tconst FEBiphasicMaterialPoint* bpt = (mp.ExtractData<FEBiphasicMaterialPoint>());\n\tconst FESolutesMaterialPoint* spt = (mp.ExtractData<FESolutesMaterialPoint >());\n\tdouble cf = (ept->m_J - bpt->m_phi0t) * spt->m_cF / (1 - bpt->m_phi0);\n\treturn cf;\n}\n"
  },
  {
    "path": "FEBioMix/FETriphasic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMultiphasic.h\"\n#include \"FESoluteInterface.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for triphasic materials.\n\nclass FEBIOMIX_API FETriphasic : public FEMaterial, public FESoluteInterface_T<FESolutesMaterialPoint>\n{\npublic:\n\tFETriphasic(FEModel* pfem);\n\n\t// initialization\n\tbool Init() override;\n    \n    //! specialized material points\n    void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp) override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\t\n\t// returns a pointer to a new material point object\n\tFEMaterialPointData* CreateMaterialPointData() override\n\t{ \n\t\treturn new FESolutesMaterialPoint(new FEBiphasicMaterialPoint(m_pSolid->CreateMaterialPointData()));\n\t}\n\n\t// Get the elastic component (overridden from FEMaterial)\n\tFEElasticMaterial* GetElasticMaterial() { return m_pSolid; }\n\npublic:\n\n\t//! calculate stress at material point\n\tmat3ds Stress(FEMaterialPoint& pt);\n\t\n\t//! calculate tangent stiffness at material point\n\ttens4ds Tangent(FEMaterialPoint& pt);\n\t\n\t//! calculate fluid (solvent) flux\n\tvec3d FluidFlux(FEMaterialPoint& pt);\n\t\n\t//! calculate solute molar flux\n\tvec3d SoluteFlux(FEMaterialPoint& pt, const int ion);\n\t\n\t//! actual fluid pressure (as opposed to effective pressure)\n\tdouble Pressure(FEMaterialPoint& pt);\n\t\n\t//! actual concentration (as opposed to effective concentration)\n\tdouble Concentration(FEMaterialPoint& pt, const int ion);\n\t\n\t//! porosity\n\tdouble Porosity(FEMaterialPoint& pt);\n\t\n\t//! fixed charge density\n\tdouble FixedChargeDensity(FEMaterialPoint& pt);\n\t\n\t//! electric potential\n\tdouble ElectricPotential(FEMaterialPoint& pt, const bool eform=false);\n\t\n\t//! current density\n\tvec3d CurrentDensity(FEMaterialPoint& pt);\n\t\n    //! partition coefficient\n    double PartitionCoefficient(FEMaterialPoint& pt, const int sol);\n    \n    //! partition coefficient derivatives\n    void PartitionCoefficientFunctions(FEMaterialPoint& mp, vector<double>& kappa,\n                                       vector<double>& dkdJ,\n                                       vector< vector<double> >& dkdc);\n    //! fluid density\n\tdouble FluidDensity() { return m_rhoTw; }\n\t\n\t//! solute density\n\tdouble SoluteDensity(const int ion) { return m_pSolute[ion]->Density(); }\n\t\n\t//! solute molar mass\n\tdouble SoluteMolarMass(const int ion) { return m_pSolute[ion]->MolarMass(); }\n\t\n\t//! solute charge number\n\tint SoluteChargeNumber(const int ion) { return m_pSolute[ion]->ChargeNumber(); }\n\t\n\t//! Add a solute component\n\tvoid AddSolute(FESolute* ps);\n\n// solute interface\npublic:\n\tint Solutes() override { return (int)m_pSolute.size(); }\n\tFESolute* GetSolute(int i) override { return m_pSolute[i]; }\n\tdouble GetReferentialFixedChargeDensity(const FEMaterialPoint& mp) override;\n\tFEOsmoticCoefficient* GetOsmoticCoefficient() override { return m_pOsmC; }\n\tdouble GetFixedChargeDensity(const FEMaterialPoint& mp) override {\n\t\tconst FESolutesMaterialPoint* spt = (mp.ExtractData<FESolutesMaterialPoint>());\n\t\treturn spt->m_cF;\n\t}\n\npublic:\n    FEElasticMaterial*\t\t\tGetSolid()\t\t\t\t{ return m_pSolid; }\n    FEHydraulicPermeability*\tGetPermeability()\t\t{ return m_pPerm;  }\n    \npublic: // material parameters\n\tFEParamDouble\t\t\t\tm_phi0;\t\t\t//!< solid volume fraction in reference configuration\n\tdouble\t\t\t\t\t\tm_rhoTw;\t\t//!< true fluid density\n\tFEParamDouble\t\t\t\tm_cFr;\t\t\t//!< fixed charge density in reference configurations\n\tdouble\t\t\t\t\t\tm_penalty;\t\t//!< penalty for enforcing electroneutrality\n\npublic:\n\tdouble\t\t\t\t\t\tm_Rgas;\t\t\t//!< universal gas constant\n\tdouble\t\t\t\t\t\tm_Tabs;\t\t\t//!< absolute temperature\n\tdouble\t\t\t\t\t\tm_Fc;\t\t\t//!< Faraday's constant\n\npublic: // material properties\n\tFEElasticMaterial*\t\t\tm_pSolid;\t\t//!< pointer to elastic solid material\n\tFEHydraulicPermeability*\tm_pPerm;\t\t//!< pointer to permeability material\n\tFEOsmoticCoefficient*\t\tm_pOsmC;\t\t//!< pointer to osmotic coefficient material\n\tstd::vector<FESolute*>\t\tm_pSolute;\t\t//!< pointer to solute materials\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioMix/FETriphasicDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETriphasicDomain.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/log.h\"\n#include \"FECore/DOFS.h\"\n#include <FEBioMech/FEBioMech.h>\n#include <FECore/FELinearSystem.h>\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n//-----------------------------------------------------------------------------\nFETriphasicDomain::FETriphasicDomain(FEModel* pfem) : FESolidDomain(pfem), FEElasticDomain(pfem), m_dofU(pfem), m_dofR(pfem), m_dof(pfem)\n{\n\tm_pMat = nullptr;\n\n    // TODO: Can this be done in Init, since there is no error checking\n    if (pfem)\n    {\n        m_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n        m_dofR.AddVariable(FEBioMech::GetVariableName(FEBioMech::RIGID_ROTATION));\n        m_dofP = pfem->GetDOFIndex(\"p\");\n        m_dofC = pfem->GetDOFIndex(\"concentration\", 0);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! get the material (overridden from FEDomain)\nFEMaterial* FETriphasicDomain::GetMaterial()\n{\n\treturn m_pMat;\n}\n\n//-----------------------------------------------------------------------------\n//! get the total dof\nconst FEDofList& FETriphasicDomain::GetDOFList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::SetMaterial(FEMaterial* pmat)\n{\n\tFEDomain::SetMaterial(pmat);\n\tm_pMat = dynamic_cast<FETriphasic*>(pmat);\n\tassert(m_pMat);\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the element LM data. \nvoid FETriphasicDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tint dofc0 = m_dofC + m_pMat->m_pSolute[0]->GetSoluteDOF();\n\tint dofc1 = m_dofC + m_pMat->m_pSolute[1]->GetSoluteDOF();\n\n\tint N = el.Nodes();\n\tlm.resize(N*9);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = m_pMesh->Node(n);\n\n\t\tvector<int>& id = node.m_ID;\n\n\t\t// first the displacement dofs\n\t\tlm[6*i  ] = id[m_dofU[0]];\n\t\tlm[6*i+1] = id[m_dofU[1]];\n\t\tlm[6*i+2] = id[m_dofU[2]];\n\n\t\t// now the pressure dofs\n\t\tlm[6*i+3] = id[m_dofP];\n        \n        // concentration dofs\n        lm[6*i + 4] = id[dofc0];\n        lm[6*i + 5] = id[dofc1];\n        \n        // rigid rotational dofs\n\t\t// TODO: Do I really need these?\n\t\tlm[6*N + 3*i  ] = id[m_dofR[0]];\n\t\tlm[6*N + 3*i+1] = id[m_dofR[1]];\n\t\tlm[6*N + 3*i+2] = id[m_dofR[2]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::Activate()\n{\n\tint dofc0 = m_dofC + m_pMat->m_pSolute[0]->GetSoluteDOF();\n\tint dofc1 = m_dofC + m_pMat->m_pSolute[1]->GetSoluteDOF();\n\n\tfor (int i=0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tif (node.m_rid < 0)\n\t\t\t{\n\t\t\t\tnode.set_active(m_dofU[0]);\n\t\t\t\tnode.set_active(m_dofU[1]);\n\t\t\t\tnode.set_active(m_dofU[2]);\n\t\t\t}\n\n\t\t\tnode.set_active(m_dofP);\n\t\t\tnode.set_active(dofc0 );\n\t\t\tnode.set_active(dofc1 );\n\t\t}\n\t}\n\n\t// get the triphasic material\n\tFETriphasic* pmb = m_pMat;\n\tconst int nsol = 2;\n\tconst int nsbm = 0;\n\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble p0[NE];\n\tvector< vector<double> > c0(nsol, vector<double>(NE));\n\tFEMesh& m = *GetMesh();\n\n\tint id[2] = { m_pMat->m_pSolute[0]->GetSoluteID() - 1, m_pMat->m_pSolute[1]->GetSoluteID() - 1 };\n\n\tfor (int i = 0; i<(int)m_Elem.size(); ++i)\n\t{\n\t\t// get the solid element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the number of nodes\n\t\tint neln = el.Nodes();\n\t\t// get initial values of fluid pressure and solute concentrations\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tp0[i] = m.Node(el.m_node[i]).get(m_dofP);\n\t\t\tfor (int isol = 0; isol<nsol; ++isol)\n\t\t\t\tc0[isol][i] = m.Node(el.m_node[i]).get(m_dofC + id[isol]);\n\t\t}\n\n\t\t// get the number of integration points\n\t\tint nint = el.GaussPoints();\n\n\t\t// loop over the integration points\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tFEElasticMaterialPoint& pm = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\t\tFESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n\n\t\t\t// initialize referential solid volume fraction\n\t\t\tpt.m_phi0t = pmb->m_phi0(mp);\n\n\t\t\t// initialize effective fluid pressure, its gradient, and fluid flux\n\t\t\tpt.m_p = el.Evaluate(p0, n);\n\t\t\tpt.m_gradp = gradient(el, p0, n);\n\t\t\tpt.m_w = pmb->FluidFlux(mp);\n\n\n\t\t\t// initialize multiphasic solutes\n\t\t\tps.m_nsol = nsol;\n\t\t\tps.m_nsbm = nsbm;\n\n\t\t\t// initialize effective solute concentrations\n\t\t\tfor (int isol = 0; isol<nsol; ++isol) {\n\t\t\t\tps.m_c[isol] = el.Evaluate(c0[isol], n);\n\t\t\t\tps.m_gradc[isol] = gradient(el, c0[isol], n);\n\t\t\t}\n\n\t\t\tps.m_psi = pmb->ElectricPotential(mp);\n\t\t\tfor (int isol = 0; isol<nsol; ++isol) {\n\t\t\t\tps.m_ca[isol] = pmb->Concentration(mp, isol);\n\t\t\t\tps.m_j[isol] = pmb->SoluteFlux(mp, isol);\n\t\t\t\tps.m_crp[isol] = pm.m_J*m_pMat->Porosity(mp)*ps.m_ca[isol];\n\t\t\t}\n\t\t\tpt.m_pa = pmb->Pressure(mp);\n\t\t\tps.m_cF = pmb->FixedChargeDensity(mp);\n\t\t\tps.m_Ie = pmb->CurrentDensity(mp);\n\n\t\t\tpm.m_s = pmb->Stress(mp);\n\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::Reset()\n{\n\t// reset base class\n\tFESolidDomain::Reset();\n\t\n\t// get the multiphasic material\n\tFETriphasic* pmb = m_pMat;\n\tconst int nsol = 2;\n\tconst int nsbm = 0;\n\t\n\t// loop over all material points\n\tForEachMaterialPoint([=](FEMaterialPoint& mp) \n\t{\n\t\tFEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\tFESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n\t\t\t\n\t\t// initialize referential solid volume fraction\n\t\tpt.m_phi0 = pt.m_phi0t = pmb->m_phi0(mp);\n\t\t\t\n\t\t// initialize multiphasic solutes\n\t\tps.m_nsol = nsol;\n\t\tps.m_c.assign(nsol,0);\n\t\tps.m_ca.assign(nsol,0);\n        ps.m_crp.assign(nsol, 0);\n\t\tps.m_gradc.assign(nsol,vec3d(0,0,0));\n        ps.m_bsb.assign(nsol, false);\n\t\tps.m_k.assign(nsol, 0);\n\t\tps.m_dkdJ.assign(nsol, 0);\n\t\tps.m_dkdc.resize(nsol, vector<double>(nsol,0));\n\t\tps.m_j.assign(nsol,vec3d(0,0,0));\n\t\tps.m_nsbm = nsbm;\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tFESolidDomain::PreSolveUpdate(timeInfo);\n\n\tconst int NE = FEElement::MAX_NODES;\n\tvec3d x0[NE], xt[NE], r0, rt;\n    double pn[NE], p;\n\tFEMesh& m = *GetMesh();\n\tfor (size_t iel=0; iel<m_Elem.size(); ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\t\tint neln = el.Nodes();\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n\t\t\tx0[i] = m.Node(el.m_node[i]).m_r0;\n\t\t\txt[i] = m.Node(el.m_node[i]).m_rt;\n            pn[i] = m.Node(el.m_node[i]).get(m_dofP);\n\t\t}\n\n\t\tint n = el.GaussPoints();\n\t\tfor (int j=0; j<n; ++j) \n\t\t{\n\t\t\tr0 = el.Evaluate(x0, j);\n\t\t\trt = el.Evaluate(xt, j);\n            p = el.Evaluate(pn, j);\n\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tFEElasticMaterialPoint& pe = *mp.ExtractData<FEElasticMaterialPoint>();\n            FEBiphasicMaterialPoint& pt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n            FESolutesMaterialPoint& ps = *(mp.ExtractData<FESolutesMaterialPoint>());\n            \n            mp.m_r0 = r0;\n\t\t\tmp.m_rt = rt;\n\n\t\t\tpe.m_J = defgrad(el, pe.m_F, j);\n\n            // reset referential solid volume fraction at previous time\n            pt.m_phi0p = pt.m_phi0t;\n            \n            // reset determinant of solid deformation gradient at previous time\n            pt.m_Jp = pe.m_J;\n            \n            pt.m_p = p;\n            pt.m_gradp = gradient(el, pn, j);\n            \n            // reset referential actual solute concentration at previous time\n            for (int j=0; j<2; ++j) {\n                ps.m_crp[j] = pe.m_J*m_pMat->Porosity(mp)*ps.m_ca[j];\n            }\n\t\t\tmp.Update(timeInfo);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::InternalForces(FEGlobalVector& R)\n{\n\tsize_t NE = m_Elem.size();\n\t#pragma omp parallel for shared (NE)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// element force vector\n\t\tvector<double> fe;\n\t\tvector<int> lm;\n\t\t\n\t\t// get the element\n\t\tFESolidElement& el = m_Elem[i];\n\n\t\t// get the element force vector and initialize it to zero\n\t\tint ndof = 6*el.Nodes();\n\t\tfe.assign(ndof, 0);\n\n\t\t// calculate internal force vector\n\t\tElementInternalForce(el, fe);\n\n\t\t// get the element's LM vector\n\t\tUnpackLM(el, lm);\n\n\t\t// assemble element 'fe'-vector into global R vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\n\nvoid FETriphasicDomain::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n\tint i, n;\n\n\t// jacobian matrix, inverse jacobian matrix and determinants\n\tdouble Ji[3][3], detJt;\n\n    vec3d gradN;\n    mat3ds s;\n\n\tconst double* Gr, *Gs, *Gt, *H;\n\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\tdouble*\tgw = el.GaussWeights();\n\n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n\tfor (n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(el.GetMaterialPoint(n)->ExtractData<FESolutesMaterialPoint>());\n        \n\t\t// calculate the jacobian\n\t\tdetJt = invjact(el, Ji, n);\n\n\t\tdetJt *= gw[n];\n\n\t\tGr = el.Gr(n);\n\t\tGs = el.Gs(n);\n\t\tGt = el.Gt(n);\n\n        H = el.H(n);\n        \n        // next we get the determinant\n        double Jp = bpt.m_Jp;\n        double J = pt.m_J;\n        \n        // and then finally\n        double divv = ((J-Jp)/dt)/J;\n        \n        // get the stress for this integration point\n        s = pt.m_s;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solute flux\n        vec3d j[2] = {spt.m_j[0],spt.m_j[1]};\n        // get the charge number\n        int z[2] = {m_pMat->m_pSolute[0]->ChargeNumber(), m_pMat->m_pSolute[1]->ChargeNumber()};\n        \n        vec3d je = j[0]*z[0] + j[1]*z[1];\n        \n        // evaluate the porosity, its derivative w.r.t. J, and its gradient\n        double phiw = m_pMat->Porosity(mp);\n        \n        for (i=0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n\n            // the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n            fe[6*i  ] -= fu.x*detJt;\n            fe[6*i+1] -= fu.y*detJt;\n            fe[6*i+2] -= fu.z*detJt;\n            fe[6*i+3] -= dt*(w*gradN - divv*H[i])*detJt;\n            fe[6*i+4] -= dt*(gradN*(j[0]+je*m_pMat->m_penalty)\n                         - H[i]*((phiw*spt.m_ca[0] - spt.m_crp[0]/J)/dt)\n                         )*detJt;\n            fe[6*i+5] -= dt*(gradN*(j[1]+je*m_pMat->m_penalty)\n                             - H[i]*((phiw*spt.m_ca[1] - spt.m_crp[1]/J)/dt)\n                             )*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::InternalForcesSS(FEGlobalVector& R)\n{\n    size_t NE = m_Elem.size();\n#pragma omp parallel for shared (NE)\n    for (int i=0; i<NE; ++i)\n    {\n        // element force vector\n        vector<double> fe;\n        vector<int> lm;\n        \n        // get the element\n        FESolidElement& el = m_Elem[i];\n        \n        // get the element force vector and initialize it to zero\n        int ndof = 6*el.Nodes();\n        fe.assign(ndof, 0);\n        \n        // calculate internal force vector\n        ElementInternalForceSS(el, fe);\n        \n        // get the element's LM vector\n        UnpackLM(el, lm);\n        \n        // assemble element 'fe'-vector into global R vector\n        R.Assemble(el.m_node, lm, fe);\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements (steady-state)\n\nvoid FETriphasicDomain::ElementInternalForceSS(FESolidElement& el, vector<double>& fe)\n{\n    int i, n;\n    \n    // jacobian matrix, inverse jacobian matrix and determinants\n    double Ji[3][3], detJt;\n    \n    vec3d gradN;\n    mat3ds s;\n    \n    const double* Gr, *Gs, *Gt, *H;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double*\tgw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    // repeat for all integration points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n        FEBiphasicMaterialPoint& bpt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint& spt = *(el.GetMaterialPoint(n)->ExtractData<FESolutesMaterialPoint>());\n        \n        // calculate the jacobian\n        detJt = invjact(el, Ji, n);\n        \n        detJt *= gw[n];\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // get the stress for this integration point\n        s = pt.m_s;\n        \n        // get the flux\n        vec3d& w = bpt.m_w;\n        \n        // get the solute flux\n        vec3d j[2] = {spt.m_j[0],spt.m_j[1]};\n        // get the charge number\n        int z[2] = {m_pMat->m_pSolute[0]->ChargeNumber(), m_pMat->m_pSolute[1]->ChargeNumber()};\n        \n        vec3d je = j[0]*z[0] + j[1]*z[1];\n        \n        for (i=0; i<neln; ++i)\n        {\n            // calculate global gradient of shape functions\n            // note that we need the transposed of Ji, not Ji itself !\n            gradN = vec3d(Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i],\n                          Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i],\n                          Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i]);\n            \n            // calculate internal force\n            vec3d fu = s*gradN;\n            \n            // the '-' sign is so that the internal forces get subtracted\n            // from the global residual vector\n            fe[6*i  ] -= fu.x*detJt;\n            fe[6*i+1] -= fu.y*detJt;\n            fe[6*i+2] -= fu.z*detJt;\n            fe[6*i+3] -= dt*(w*gradN)*detJt;\n            fe[6*i+4] -= dt*(gradN*(j[0]+je*m_pMat->m_penalty))*detJt;\n            fe[6*i+5] -= dt*(gradN*(j[1]+je*m_pMat->m_penalty))*detJt;\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::StiffnessMatrix(FELinearSystem& LS, bool bsymm)\n{\n\t// repeat over all solid elements\n\tsize_t NE = m_Elem.size();\n    \n\t#pragma omp parallel for shared(NE)\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\n\t\t// get the lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\t\t\n\t\t// allocate stiffness matrix\n\t\tint neln = el.Nodes();\n\t\tint ndpn = 6;\n\t\tint ndof = neln*ndpn;\n\t\tke.resize(ndof, ndof);\n\t\t\n\t\t// calculate the element stiffness matrix\n\t\tElementTriphasicStiffness(el, ke, bsymm);\n\t\t\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::StiffnessMatrixSS(FELinearSystem& LS, bool bsymm)\n{\n\t// repeat over all solid elements\n\tsize_t NE = m_Elem.size();\n    \n    #pragma omp parallel for shared(NE)\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\n\t\t// allocate stiffness matrix\n\t\tint neln = el.Nodes();\n\t\tint ndpn = 6;\n\t\tint ndof = neln*ndpn;\n\t\tke.resize(ndof, ndof);\n\t\t\n\t\t// calculate the element stiffness matrix\n\t\tElementTriphasicStiffnessSS(el, ke, bsymm);\n\n\t\t//  get the lm vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//!\nbool FETriphasicDomain::ElementTriphasicStiffness(FESolidElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, isol, jsol, n;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double *Gr, *Gs, *Gt, *H;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradN(neln);\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    FETriphasic* pm = m_pMat;\n    const int nsol = 2;\n    int ndpn = 4+nsol;\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        vector<double> c(spt.m_c);\n        vector<vec3d> gradc(spt.m_gradc);\n        vector<int> z(nsol);\n        \n        vector<double> kappa(spt.m_k);\n        \n        // get the charge number\n        for (isol=0; isol<nsol; ++isol)\n            z[isol] = pm->m_pSolute[isol]->ChargeNumber();\n        \n        vector<double> dkdJ(spt.m_dkdJ);\n        vector< vector<double> > dkdc(spt.m_dkdc);\n        vector< vector<double> > dkdr(spt.m_dkdr);\n        vector< vector<double> > dkdJr(spt.m_dkdJr);\n        vector< vector< vector<double> > > dkdrc(spt.m_dkdrc);\n        \n        // evaluate the porosity and its derivative\n        double phiw = pm->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the osmotic coefficient\n        double osmc = pm->m_pOsmC->OsmoticCoefficient(mp);\n        \n        // evaluate the permeability\n        mat3ds K = pm->m_pPerm->Permeability(mp);\n        tens4dmm dKdE = pm->m_pPerm->Tangent_Permeability_Strain(mp);\n        \n        vector<mat3ds> dKdc(nsol);\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector< vector<mat3ds> > dDdc(nsol, vector<mat3ds>(nsol));\n        vector<double> D0(nsol);\n        vector< vector<double> > dD0dc(nsol, vector<double>(nsol));\n        vector<double> dodc(nsol);\n        vector<mat3ds> dTdc(nsol);\n        vector<mat3ds> ImD(nsol);\n        mat3dd I(1);\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // evaluate the permeability derivatives\n            dKdc[isol] = pm->m_pPerm->Tangent_Permeability_Concentration(mp,isol);\n            \n            // evaluate the diffusivity tensor and its derivatives\n            D[isol] = pm->m_pSolute[isol]->m_pDiff->Diffusivity(mp);\n            dDdE[isol] = pm->m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Strain(mp);\n            \n            // evaluate the solute free diffusivity\n            D0[isol] = pm->m_pSolute[isol]->m_pDiff->Free_Diffusivity(mp);\n            \n            // evaluate the derivative of the osmotic coefficient\n            dodc[isol] = pm->m_pOsmC->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            \n            // evaluate the stress tangent with concentration\n            //\t\t\tdTdc[isol] = pm->GetSolid()->Tangent_Concentration(mp,isol);\n            dTdc[isol] = mat3ds(0,0,0,0,0,0);\n            \n            ImD[isol] = I-D[isol]/D0[isol];\n            \n            for (jsol=0; jsol<nsol; ++jsol) {\n                dDdc[isol][jsol] = pm->m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Concentration(mp,jsol);\n                dD0dc[isol][jsol] = pm->m_pSolute[isol]->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,jsol);\n            }\n        }\n        \n        // Miscellaneous constants\n        double R = pm->m_Rgas;\n        double T = pm->m_Tabs;\n        double penalty = pm->m_penalty;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds Ke(0,0,0,0,0,0);\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE);\n        vector<mat3ds> Gc(nsol);\n        vector<mat3ds> dKedc(nsol);\n        for (isol=0; isol<nsol; ++isol) {\n            Ke += ImD[isol]*(kappa[isol]*c[isol]/D0[isol]);\n            G += dyad1(ImD[isol],I)*(R*T*c[isol]*J/D0[isol]/phiw*(dkdJ[isol]-kappa[isol]/phiw*dpdJ))\n            +(dyad1(I,I) - dyad2(I,I)*2 - dDdE[isol]/D0[isol])*(R*T*kappa[isol]*c[isol]/phiw/D0[isol]);\n            Gc[isol] = ImD[isol]*(kappa[isol]/D0[isol]);\n            for (jsol=0; jsol<nsol; ++jsol) {\n                Gc[isol] += ImD[jsol]*(c[jsol]/D0[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol]))\n                -(dDdc[jsol][isol]-D[jsol]*(dD0dc[jsol][isol]/D0[jsol])*(kappa[jsol]*c[jsol]/SQR(D0[jsol])));\n            }\n            Gc[isol] *= R*T/phiw;\n        }\n        Ke = (Ki + Ke*(R*T/phiw)).inverse();\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        for (isol=0; isol<nsol; ++isol)\n            dKedc[isol] = -(Ke*(-Ki*dKdc[isol]*Ki + Gc[isol])*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp,qpu;\n        vector<vec3d> gc(nsol),qcu(nsol),wc(nsol),jce(nsol);\n        vector< vector<vec3d> > jc(nsol, vector<vec3d>(nsol));\n        mat3d wu, jue;\n        vector<mat3d> ju(nsol);\n        vector< vector<double> > qcc(nsol, vector<double>(nsol));\n        double sum;\n        mat3ds De;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradN[i]*(s*gradN[j])) + vdotTdotv(gradN[i], C, gradN[j]))*detJ;\n                ke[6*i  ][6*j  ] += Kuu[0][0]; ke[6*i  ][6*j+1] += Kuu[0][1]; ke[6*i  ][6*j+2] += Kuu[0][2];\n                ke[6*i+1][6*j  ] += Kuu[1][0]; ke[6*i+1][6*j+1] += Kuu[1][1]; ke[6*i+1][6*j+2] += Kuu[1][2];\n                ke[6*i+2][6*j  ] += Kuu[2][0]; ke[6*i+2][6*j+1] += Kuu[2][1]; ke[6*i+2][6*j+2] += Kuu[2][2];\n                \n                // calculate the kpu matrix\n                gp = vec3d(0,0,0);\n                for (isol=0; isol<nsol; ++isol) gp += (D[isol]*gradc[isol])*(kappa[isol]/D0[isol]);\n                gp = gradp+gp*(R*T);\n                wu = vdotTdotv(-gp, dKedE, gradN[j]);\n                for (isol=0; isol<nsol; ++isol) {\n                    wu += (((Ke*(D[isol]*gradc[isol])) & gradN[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradN[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradN[j]))*(-kappa[isol]*R*T/D0[isol]);\n                }\n                qpu = -gradN[j]*(1.0/dt);\n                vtmp = (wu.transpose()*gradN[i] + qpu*H[i])*(detJ*dt);\n                ke[ndpn*i+3][ndpn*j  ] += vtmp.x;\n                ke[ndpn*i+3][ndpn*j+1] += vtmp.y;\n                ke[ndpn*i+3][ndpn*j+2] += vtmp.z;\n                \n                // calculate the kup matrix\n                vtmp = -gradN[i]*H[j]*detJ;\n                ke[ndpn*i  ][ndpn*j+3] += vtmp.x;\n                ke[ndpn*i+1][ndpn*j+3] += vtmp.y;\n                ke[ndpn*i+2][ndpn*j+3] += vtmp.z;\n                \n                // calculate the kpp matrix\n                ke[ndpn*i+3][ndpn*j+3] += (- gradN[i]*(Ke*gradN[j]))*(detJ*dt);\n                \n                // calculate kcu matrix data\n                jue.zero();\n                De.zero();\n                for (isol=0; isol<nsol; ++isol) {\n                    gc[isol] = -gradc[isol]*phiw + w*c[isol]/D0[isol];\n                    ju[isol] = ((D[isol]*gc[isol]) & gradN[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradN[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradN[j])*(-phis)\n                       +(D[isol]*((gradN[j]*w)*2) - ((D[isol]*w) & gradN[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*wu*(kappa[isol]*c[isol]/D0[isol]);\n                    jue += ju[isol]*z[isol];\n                    De += D[isol]*(z[isol]*kappa[isol]*c[isol]/D0[isol]);\n                    qcu[isol] = qpu*(c[isol]*(kappa[isol]+J*phiw*dkdJ[isol]));\n                }\n                \n                for (isol=0; isol<nsol; ++isol) {\n                    \n                    // calculate the kcu matrix\n                    vtmp = ((ju[isol]+jue*penalty).transpose()*gradN[i]\n                            + qcu[isol]*H[i])*(detJ*dt);\n                    ke[ndpn*i+4+isol][ndpn*j  ] += vtmp.x;\n                    ke[ndpn*i+4+isol][ndpn*j+1] += vtmp.y;\n                    ke[ndpn*i+4+isol][ndpn*j+2] += vtmp.z;\n                    \n                    // calculate the kcp matrix\n                    ke[ndpn*i+4+isol][ndpn*j+3] -= (gradN[i]*(\n                                                              (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                               +De*penalty)\n                                                              *(Ke*gradN[j])\n                                                              ))*(detJ*dt);\n                    \n                    // calculate the kuc matrix\n                    sum = 0;\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        sum += c[jsol]*(dodc[isol]*kappa[jsol]+osmc*dkdc[jsol][isol]);\n                    vtmp = (dTdc[isol]*gradN[i] - gradN[i]*(R*T*(osmc*kappa[isol]+sum)))*H[j]*detJ;\n                    ke[ndpn*i  ][ndpn*j+4+isol] += vtmp.x;\n                    ke[ndpn*i+1][ndpn*j+4+isol] += vtmp.y;\n                    ke[ndpn*i+2][ndpn*j+4+isol] += vtmp.z;\n                    \n                    // calculate the kpc matrix\n                    vtmp = vec3d(0,0,0);\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        vtmp += (D[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol])\n                                 +dDdc[jsol][isol]*kappa[jsol])/D0[jsol]*gradc[jsol];\n                    wc[isol] = (dKedc[isol]*gp)*(-H[j])\n                    -Ke*((D[isol]*gradN[j])*(kappa[isol]/D0[isol])+vtmp*H[j])*(R*T);\n                    ke[ndpn*i+3][ndpn*j+4+isol] += (gradN[i]*wc[isol])*(detJ*dt);\n                    \n                }\n                \n                // calculate data for the kcc matrix\n                jce.assign(nsol, vec3d(0,0,0));\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        if (jsol != isol) {\n                            jc[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            \n                            qcc[isol][jsol] = -H[j]*phiw/dt*c[isol]*dkdc[isol][jsol];\n                        }\n                        else {\n                            jc[isol][jsol] = (D[isol]*(gradN[j]*(-phiw)+w*(H[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                            \n                            qcc[isol][jsol] = -H[j]*phiw/dt*(c[isol]*dkdc[isol][jsol] + kappa[isol]);\n                        }\n                        jce[jsol] += jc[isol][jsol]*z[isol];\n                    }\n                }\n                \n                // calculate the kcc matrix\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        ke[ndpn*i+4+isol][ndpn*j+4+jsol] += (gradN[i]*(jc[isol][jsol]+jce[jsol]*penalty)\n                                                             + H[i]*(qcc[isol][jsol]))*(detJ*dt);\n                    }\n                }\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<ndpn*neln; ++i)\n            for (j=i+1; j<ndpn*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculates element stiffness matrix for element iel\n//! for steady-state response (zero solid velocity, zero time derivative of\n//! solute concentration)\n//!\nbool FETriphasicDomain::ElementTriphasicStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm)\n{\n    int i, j, isol, jsol, n;\n    \n    int nint = el.GaussPoints();\n    int neln = el.Nodes();\n    \n    double *Gr, *Gs, *Gt, *H;\n    \n    // jacobian\n    double Ji[3][3], detJ;\n    \n    // Gradient of shape functions\n    vector<vec3d> gradN(neln);\n    double tmp;\n    \n    // gauss-weights\n    double* gw = el.GaussWeights();\n    \n    double dt = GetFEModel()->GetTime().timeIncrement;\n    \n    FETriphasic* pm = m_pMat;\n    const int nsol = 2;\n    int ndpn = 4+nsol;\n    \n    // zero stiffness matrix\n    ke.zero();\n    \n    // loop over gauss-points\n    for (n=0; n<nint; ++n)\n    {\n        FEMaterialPoint& mp = *el.GetMaterialPoint(n);\n        FEElasticMaterialPoint&  ept = *(mp.ExtractData<FEElasticMaterialPoint >());\n        FEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n        FESolutesMaterialPoint&  spt = *(mp.ExtractData<FESolutesMaterialPoint >());\n        \n        // calculate jacobian\n        detJ = invjact(el, Ji, n)*gw[n];\n        \n        vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n        vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n        vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n        \n        Gr = el.Gr(n);\n        Gs = el.Gs(n);\n        Gt = el.Gt(n);\n        \n        H = el.H(n);\n        \n        // calculate global gradient of shape functions\n        for (i=0; i<neln; ++i)\n            gradN[i] = g1*Gr[i] + g2*Gs[i] + g3*Gt[i];\n        \n        // get stress tensor\n        mat3ds s = ept.m_s;\n        \n        // get elasticity tensor\n        tens4ds C = m_pMat->Tangent(mp);\n        \n        // next we get the determinant\n        double J = ept.m_J;\n        \n        // get the fluid flux and pressure gradient\n        vec3d w = ppt.m_w;\n        vec3d gradp = ppt.m_gradp;\n        \n        vector<double> c(spt.m_c);\n        vector<vec3d> gradc(spt.m_gradc);\n        vector<int> z(nsol);\n        \n        vector<double> kappa(spt.m_k);\n        \n        // get the charge number\n        for (isol=0; isol<nsol; ++isol)\n            z[isol] = pm->m_pSolute[isol]->ChargeNumber();\n        \n        vector<double> dkdJ(spt.m_dkdJ);\n        vector< vector<double> > dkdc(spt.m_dkdc);\n        vector< vector<double> > dkdr(spt.m_dkdr);\n        vector< vector<double> > dkdJr(spt.m_dkdJr);\n        vector< vector< vector<double> > > dkdrc(spt.m_dkdrc);\n        \n        // evaluate the porosity and its derivative\n        double phiw = pm->Porosity(mp);\n        double phis = 1. - phiw;\n        double dpdJ = phis/J;\n        \n        // evaluate the osmotic coefficient\n        double osmc = pm->m_pOsmC->OsmoticCoefficient(mp);\n        \n        // evaluate the permeability\n        mat3ds K = pm->m_pPerm->Permeability(mp);\n        tens4dmm dKdE = pm->m_pPerm->Tangent_Permeability_Strain(mp);\n        \n        vector<mat3ds> dKdc(nsol);\n        vector<mat3ds> D(nsol);\n        vector<tens4dmm> dDdE(nsol);\n        vector< vector<mat3ds> > dDdc(nsol, vector<mat3ds>(nsol));\n        vector<double> D0(nsol);\n        vector< vector<double> > dD0dc(nsol, vector<double>(nsol));\n        vector<double> dodc(nsol);\n        vector<mat3ds> dTdc(nsol);\n        vector<mat3ds> ImD(nsol);\n        mat3dd I(1);\n        \n        for (isol=0; isol<nsol; ++isol) {\n            // evaluate the permeability derivatives\n            dKdc[isol] = pm->m_pPerm->Tangent_Permeability_Concentration(mp,isol);\n            \n            // evaluate the diffusivity tensor and its derivatives\n            D[isol] = pm->m_pSolute[isol]->m_pDiff->Diffusivity(mp);\n            dDdE[isol] = pm->m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Strain(mp);\n            \n            // evaluate the solute free diffusivity\n            D0[isol] = pm->m_pSolute[isol]->m_pDiff->Free_Diffusivity(mp);\n            \n            // evaluate the derivative of the osmotic coefficient\n            dodc[isol] = pm->m_pOsmC->Tangent_OsmoticCoefficient_Concentration(mp,isol);\n            \n            // evaluate the stress tangent with concentration\n            //\t\t\tdTdc[isol] = pm->GetSolid()->Tangent_Concentration(mp,isol);\n            dTdc[isol] = mat3ds(0,0,0,0,0,0);\n            \n            ImD[isol] = I-D[isol]/D0[isol];\n            \n            for (jsol=0; jsol<nsol; ++jsol) {\n                dDdc[isol][jsol] = pm->m_pSolute[isol]->m_pDiff->Tangent_Diffusivity_Concentration(mp,jsol);\n                dD0dc[isol][jsol] = pm->m_pSolute[isol]->m_pDiff->Tangent_Free_Diffusivity_Concentration(mp,jsol);\n            }\n        }\n        \n        // Miscellaneous constants\n        double R = pm->m_Rgas;\n        double T = pm->m_Tabs;\n        double penalty = pm->m_penalty;\n        \n        // evaluate the effective permeability and its derivatives\n        mat3ds Ki = K.inverse();\n        mat3ds Ke(0,0,0,0,0,0);\n        tens4d G = (dyad1(Ki,I) - dyad4(Ki,I)*2)*2 - ddot(dyad2(Ki,Ki),dKdE);\n        vector<mat3ds> Gc(nsol);\n        vector<mat3ds> dKedc(nsol);\n        for (isol=0; isol<nsol; ++isol) {\n            Ke += ImD[isol]*(kappa[isol]*c[isol]/D0[isol]);\n            G += dyad1(ImD[isol],I)*(R*T*c[isol]*J/D0[isol]/phiw*(dkdJ[isol]-kappa[isol]/phiw*dpdJ))\n            +(dyad1(I,I) - dyad2(I,I)*2 - dDdE[isol]/D0[isol])*(R*T*kappa[isol]*c[isol]/phiw/D0[isol]);\n            Gc[isol] = ImD[isol]*(kappa[isol]/D0[isol]);\n            for (jsol=0; jsol<nsol; ++jsol) {\n                Gc[isol] += ImD[jsol]*(c[jsol]/D0[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol]))\n                -(dDdc[jsol][isol]-D[jsol]*(dD0dc[jsol][isol]/D0[jsol])*(kappa[jsol]*c[jsol]/SQR(D0[jsol])));\n            }\n            Gc[isol] *= R*T/phiw;\n        }\n        Ke = (Ki + Ke*(R*T/phiw)).inverse();\n        tens4d dKedE = (dyad1(Ke,I) - 2*dyad4(Ke,I))*2 - ddot(dyad2(Ke,Ke),G);\n        for (isol=0; isol<nsol; ++isol)\n            dKedc[isol] = -(Ke*(-Ki*dKdc[isol]*Ki + Gc[isol])*Ke).sym();\n        \n        // calculate all the matrices\n        vec3d vtmp,gp;\n        vector<vec3d> gc(nsol),qcu(nsol),wc(nsol),jce(nsol);\n        vector< vector<vec3d> > jc(nsol, vector<vec3d>(nsol));\n        mat3d wu, jue;\n        vector<mat3d> ju(nsol);\n        double sum;\n        mat3ds De;\n        for (i=0; i<neln; ++i)\n        {\n            for (j=0; j<neln; ++j)\n            {\n                // Kuu matrix\n                mat3d Kuu = (mat3dd(gradN[i]*(s*gradN[j])) + vdotTdotv(gradN[i], C, gradN[j]))*detJ;\n                ke[6*i  ][6*j  ] += Kuu[0][0]; ke[6*i  ][6*j+1] += Kuu[0][1]; ke[6*i  ][6*j+2] += Kuu[0][2];\n                ke[6*i+1][6*j  ] += Kuu[1][0]; ke[6*i+1][6*j+1] += Kuu[1][1]; ke[6*i+1][6*j+2] += Kuu[1][2];\n                ke[6*i+2][6*j  ] += Kuu[2][0]; ke[6*i+2][6*j+1] += Kuu[2][1]; ke[6*i+2][6*j+2] += Kuu[2][2];\n                \n                // calculate the kpu matrix\n                gp = vec3d(0,0,0);\n                for (isol=0; isol<nsol; ++isol) gp += (D[isol]*gradc[isol])*(kappa[isol]/D0[isol]);\n                gp = gradp+gp*(R*T);\n                wu = vdotTdotv(-gp, dKedE, gradN[j]);\n                for (isol=0; isol<nsol; ++isol) {\n                    wu += (((Ke*(D[isol]*gradc[isol])) & gradN[j])*(J*dkdJ[isol] - kappa[isol])\n                           +Ke*(2*kappa[isol]*(gradN[j]*(D[isol]*gradc[isol]))))*(-R*T/D0[isol])\n                    + (Ke*vdotTdotv(gradc[isol], dDdE[isol], gradN[j]))*(-kappa[isol]*R*T/D0[isol]);\n                }\n                vtmp = (wu.transpose()*gradN[i])*(detJ*dt);\n                ke[ndpn*i+3][ndpn*j  ] += vtmp.x;\n                ke[ndpn*i+3][ndpn*j+1] += vtmp.y;\n                ke[ndpn*i+3][ndpn*j+2] += vtmp.z;\n                \n                // calculate the kup matrix\n                vtmp = -gradN[i]*H[j]*detJ;\n                ke[ndpn*i  ][ndpn*j+3] += vtmp.x;\n                ke[ndpn*i+1][ndpn*j+3] += vtmp.y;\n                ke[ndpn*i+2][ndpn*j+3] += vtmp.z;\n                \n                // calculate the kpp matrix\n                ke[ndpn*i+3][ndpn*j+3] += (- gradN[i]*(Ke*gradN[j]))*(detJ*dt);\n                \n                // calculate kcu matrix data\n                jue.zero();\n                De.zero();\n                for (isol=0; isol<nsol; ++isol) {\n                    gc[isol] = -gradc[isol]*phiw + w*c[isol]/D0[isol];\n                    ju[isol] = ((D[isol]*gc[isol]) & gradN[j])*(J*dkdJ[isol])\n                    + vdotTdotv(gc[isol], dDdE[isol], gradN[j])*kappa[isol]\n                    + (((D[isol]*gradc[isol]) & gradN[j])*(-phis)\n                       +(D[isol]*((gradN[j]*w)*2) - ((D[isol]*w) & gradN[j]))*c[isol]/D0[isol]\n                       )*kappa[isol]\n                    +D[isol]*wu*(kappa[isol]*c[isol]/D0[isol]);\n                    jue += ju[isol]*z[isol];\n                    De += D[isol]*(z[isol]*kappa[isol]*c[isol]/D0[isol]);\n                }\n                \n                for (isol=0; isol<nsol; ++isol) {\n                    \n                    // calculate the kcu matrix\n                    vtmp = ((ju[isol]+jue*penalty).transpose()*gradN[i]\n                            + qcu[isol]*H[i])*(detJ*dt);\n                    ke[ndpn*i+4+isol][ndpn*j  ] += vtmp.x;\n                    ke[ndpn*i+4+isol][ndpn*j+1] += vtmp.y;\n                    ke[ndpn*i+4+isol][ndpn*j+2] += vtmp.z;\n                    \n                    // calculate the kcp matrix\n                    ke[ndpn*i+4+isol][ndpn*j+3] -= (gradN[i]*(\n                                                              (D[isol]*(kappa[isol]*c[isol]/D0[isol])\n                                                               +De*penalty)\n                                                              *(Ke*gradN[j])\n                                                              ))*(detJ*dt);\n                    \n                    // calculate the kuc matrix\n                    sum = 0;\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        sum += c[jsol]*(dodc[isol]*kappa[jsol]+osmc*dkdc[jsol][isol]);\n                    vtmp = (dTdc[isol]*gradN[i] - gradN[i]*(R*T*(osmc*kappa[isol]+sum)))*H[j]*detJ;\n                    ke[ndpn*i  ][ndpn*j+4+isol] += vtmp.x;\n                    ke[ndpn*i+1][ndpn*j+4+isol] += vtmp.y;\n                    ke[ndpn*i+2][ndpn*j+4+isol] += vtmp.z;\n                    \n                    // calculate the kpc matrix\n                    vtmp = vec3d(0,0,0);\n                    for (jsol=0; jsol<nsol; ++jsol)\n                        vtmp += (D[jsol]*(dkdc[jsol][isol]-kappa[jsol]/D0[jsol]*dD0dc[jsol][isol])\n                                 +dDdc[jsol][isol]*kappa[jsol])/D0[jsol]*gradc[jsol];\n                    wc[isol] = (dKedc[isol]*gp)*(-H[j])\n                    -Ke*((D[isol]*gradN[j])*(kappa[isol]/D0[isol])+vtmp*H[j])*(R*T);\n                    ke[ndpn*i+3][ndpn*j+4+isol] += (gradN[i]*wc[isol])*(detJ*dt);\n                    \n                }\n                \n                // calculate data for the kcc matrix\n                jce.assign(nsol, vec3d(0,0,0));\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        if (jsol != isol) {\n                            jc[isol][jsol] =\n                            ((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                        }\n                        else {\n                            jc[isol][jsol] = (D[isol]*(gradN[j]*(-phiw)+w*(H[j]/D0[isol])))*kappa[isol]\n                            +((D[isol]*dkdc[isol][jsol]+dDdc[isol][jsol]*kappa[isol])*gc[isol])*H[j]\n                            +(D[isol]*(w*(-H[j]*dD0dc[isol][jsol]/D0[isol])+wc[jsol]))*(kappa[isol]*c[isol]/D0[isol]);\n                        }\n                        jce[jsol] += jc[isol][jsol]*z[isol];\n                    }\n                }\n                \n                // calculate the kcc matrix\n                for (isol=0; isol<nsol; ++isol) {\n                    for (jsol=0; jsol<nsol; ++jsol) {\n                        ke[ndpn*i+4+isol][ndpn*j+4+jsol] += (gradN[i]*(jc[isol][jsol]+jce[jsol]*penalty))*(detJ*dt);\n                    }\n                }\n            }\n        }\n    }\n    \n    // Enforce symmetry by averaging top-right and bottom-left corners of stiffness matrix\n    if (bsymm) {\n        for (i=0; i<ndpn*neln; ++i)\n            for (j=i+1; j<ndpn*neln; ++j) {\n                tmp = 0.5*(ke[i][j]+ke[j][i]);\n                ke[i][j] = ke[j][i] = tmp;\n            }\n    }\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::Update(const FETimeInfo& tp)\n{\n\tbool berr = false;\n\tint NE = (int) m_Elem.size();\n\t#pragma omp parallel for shared(NE, berr)\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\ttry\n\t\t{\n\t\t\tUpdateElementStress(i);\n\t\t}\n\t\tcatch (NegativeJacobian e)\n\t\t{\n\t\t\t#pragma omp critical\n\t\t\t{\n\t\t\t\tberr = true;\n\t\t\t\tif (e.DoOutput()) feLogError(e.what());\n\t\t\t}\n\t\t}\n\t}\n\n\t// if we encountered an error, throw an exception\n\tif (berr) throw NegativeJacobianDetected();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETriphasicDomain::UpdateElementStress(int iel)\n{\n\t// get the solid element\n\tFESolidElement& el = m_Elem[iel];\n\t\t\n\t// get the number of integration points\n\tint nint = el.GaussPoints();\n\t\t\n\t// get the number of nodes\n\tint neln = el.Nodes();\n\t\t\n\t// get the biphasic-solute material\n\tint id0 = m_dofC + m_pMat->m_pSolute[0]->GetSoluteDOF();\n\tint id1 = m_dofC + m_pMat->m_pSolute[1]->GetSoluteDOF();\n\t\t\n\t// get the nodal data\n\tFEMesh& mesh = *m_pMesh;\n\tvec3d r0[FEElement::MAX_NODES];\n\tvec3d rt[FEElement::MAX_NODES];\n\tdouble pn[FEElement::MAX_NODES], ct[2][FEElement::MAX_NODES];\n\tfor (int j=0; j<neln; ++j)\n\t{\n\t\tr0[j] = mesh.Node(el.m_node[j]).m_r0;\n\t\trt[j] = mesh.Node(el.m_node[j]).m_rt;\n\t\tpn[j] = mesh.Node(el.m_node[j]).get(m_dofP);\n\t\tct[0][j] = mesh.Node(el.m_node[j]).get(id0);\n\t\tct[1][j] = mesh.Node(el.m_node[j]).get(id1);\n\t}\n\t\t\n\t// loop over the integration points and calculate\n\t// the stress at the integration point\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\t\t\n\t\t// material point coordinates\n\t\t// TODO: I'm not entirly happy with this solution\n\t\t//\t\t since the material point coordinates are used by most materials.\n\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\tmp.m_rt = el.Evaluate(rt, n);\n\t\t\t\n\t\t// get the deformation gradient and determinant\n\t\tpt.m_J = defgrad(el, pt.m_F, n);\n\t\t\t\n\t\t// solute-poroelastic data\n\t\tFEBiphasicMaterialPoint& ppt = *(mp.ExtractData<FEBiphasicMaterialPoint>());\n\t\tFESolutesMaterialPoint& spt = *(mp.ExtractData<FESolutesMaterialPoint>());\n\t\t\t\n\t\t// evaluate fluid pressure at gauss-point\n\t\tppt.m_p = el.Evaluate(pn, n);\n\t\t\t\n\t\t// calculate the gradient of p at gauss-point\n\t\tppt.m_gradp = gradient(el, pn, n);\n\t\t\t\n\t\t// evaluate effective solute concentration at gauss-point\n\t\tspt.m_c[0] = el.Evaluate(ct[0], n);\n\t\tspt.m_c[1] = el.Evaluate(ct[1], n);\n\t\t\t\n\t\t// calculate the gradient of c at gauss-point\n\t\tspt.m_gradc[0] = gradient(el, ct[0], n);\n\t\tspt.m_gradc[1] = gradient(el, ct[1], n);\n\t\t\t\n\t\t// for biphasic-solute materials also update the porosity, fluid and solute fluxes\n\t\t// and evaluate the actual fluid pressure and solute concentration\n\t\tppt.m_w = m_pMat->FluidFlux(mp);\n\t\tspt.m_psi = m_pMat->ElectricPotential(mp);\n\t\tspt.m_ca[0] = m_pMat->Concentration(mp,0);\n\t\tspt.m_ca[1] = m_pMat->Concentration(mp,1);\n\t\tppt.m_pa = m_pMat->Pressure(mp);\n\t\tspt.m_j[0] = m_pMat->SoluteFlux(mp,0);\n\t\tspt.m_j[1] = m_pMat->SoluteFlux(mp,1);\n\t\tspt.m_cF = m_pMat->FixedChargeDensity(mp);\n\t\tspt.m_Ie = m_pMat->CurrentDensity(mp);\n        m_pMat->PartitionCoefficientFunctions(mp, spt.m_k, spt.m_dkdJ, spt.m_dkdc);\n\t\t\t\n        // update specialized material points\n        m_pMat->UpdateSpecializedMaterialPoints(mp, GetFEModel()->GetTime());\n        \n        // calculate the solid stress at this material point\n        ppt.m_ss = m_pMat->GetElasticMaterial()->Stress(mp);\n        \n\t\tpt.m_s = m_pMat->Stress(mp);\n\t}\n}\n"
  },
  {
    "path": "FEBioMix/FETriphasicDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FESolidDomain.h\"\n#include \"FETriphasic.h\"\n#include \"FEBioMech/FEElasticDomain.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\n//! Domain class for triphasic 3D solid elements\n//! Note that this class inherits from FEElasticSolidDomain since this domain\n//! also needs to calculate elastic stiffness contributions.\n//!\nclass FEBIOMIX_API FETriphasicDomain : public FESolidDomain, public FEElasticDomain\n{\npublic:\n\t//! constructor\n\tFETriphasicDomain(FEModel* pfem);\n\t\n\t//! reset domain data\n\tvoid Reset() override;\n\n\t//! get material (overridden from FEDomain)\n\tFEMaterial* GetMaterial() override;\n\n\t//! get the total dof\n\tconst FEDofList& GetDOFList() const override;\n\n\t//! set the material\n\tvoid SetMaterial(FEMaterial* pmat) override;\n\n\t//! Unpack solid element data (overridden from FEDomain)\n\tvoid UnpackLM(FEElement& el, vector<int>& lm) override;\n\n\t//! activate\n\tvoid Activate() override;\n\n\t//! initialize elements for this domain\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\t\n\t// update domain data\n\tvoid Update(const FETimeInfo& tp) override;\n\n\t//! update element state data\n\tvoid UpdateElementStress(int iel);\n\npublic:\n\t// internal work (overridden from FEElasticDomain)\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n    // internal work (steady-state case)\n    void InternalForcesSS(FEGlobalVector& R);\n    \n\t//! calculates the global stiffness matrix for this domain\n\tvoid StiffnessMatrix(FELinearSystem& LS, bool bsymm);\n\n\t//! calculates the global stiffness matrix for this domain (steady-state case)\n\tvoid StiffnessMatrixSS(FELinearSystem& LS, bool bsymm);\n\nprotected:\n\t//! element internal force vector\n\tvoid ElementInternalForce(FESolidElement& el, vector<double>& fe);\n\n    //! element internal force vector (steady-state case)\n    void ElementInternalForceSS(FESolidElement& el, vector<double>& fe);\n    \n\t//! calculates the element triphasic stiffness matrix\n\tbool ElementTriphasicStiffness(FESolidElement& el, matrix& ke, bool bsymm);\n\t\n\t//! calculates the element triphasic stiffness matrix\n\tbool ElementTriphasicStiffnessSS(FESolidElement& el, matrix& ke, bool bsymm);\n\t\nprotected: // overridden from FEElasticDomain, but not implemented in this domain\n\tvoid BodyForce(FEGlobalVector& R, FEBodyForce& bf) override {}\n\tvoid InertialForces(FEGlobalVector& R, vector<double>& F) override {}\n\tvoid StiffnessMatrix(FELinearSystem& LS) override {}\n\tvoid BodyForceStiffness(FELinearSystem& LS, FEBodyForce& bf) override {}\n\tvoid MassMatrix(FELinearSystem& LS, double scale) override {}\n\t\nprotected:\n\tFETriphasic*\tm_pMat;\n\tFEDofList\t\tm_dofU;\n\tFEDofList\t\tm_dofR;\n\tFEDofList\t\tm_dof;\n\tint\t\t\t\tm_dofP;\t\t//!< pressure dof index\n\tint\t\t\t\tm_dofC;\t\t//!< concentration dof index\n};\n"
  },
  {
    "path": "FEBioMix/febiomix_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febiomix_EXPORTS\n\t\t\t#define FEBIOMIX_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOMIX_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIOMIX_API\n\t#endif\n#else\n\t#define FEBIOMIX_API\n#endif\n"
  },
  {
    "path": "FEBioMix/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <stdlib.h>\n"
  },
  {
    "path": "FEBioOpt/FEBioOpt.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioOpt.h\"\n#include \"FEOptimize.h\"\n#include \"FEParameterSweep.h\"\n#include \"FEBioParamRun.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FELMOptimizeMethod.h\"\n#include \"FEConstrainedLMOptimizeMethod.h\"\n#include \"FEPowellOptimizeMethod.h\"\n#include \"FEScanOptimizeMethod.h\"\n#include \"NLOptOptimizeMethod.h\"\n#ifdef HAVE_LEVMAR\n#include \"levmar.h\"\n#endif\n\n//-----------------------------------------------------------------------------\n//! Initialization of the FEBioOpt module. This function registers all the classes\n//! in this module with the FEBio framework.\nvoid FEBioOpt::InitModule()\n{\n\t// Task classes\n\tREGISTER_FECORE_CLASS(FEOptimize      , \"optimize\");\n\tREGISTER_FECORE_CLASS(FEParameterSweep, \"parameter_sweep\");\n\tREGISTER_FECORE_CLASS(FEBioParamRun   , \"param_run\");\n\n\t// optimization methods\n\tREGISTER_FECORE_CLASS(FELMOptimizeMethod, \"levmar\");\n#ifdef HAVE_LEVMAR\n\tREGISTER_FECORE_CLASS(FEConstrainedLMOptimizeMethod, \"constrained levmar\");\n#endif\n\tREGISTER_FECORE_CLASS(FEPowellOptimizeMethod, \"powell\");\n\tREGISTER_FECORE_CLASS(FEScanOptimizeMethod, \"scan\");\n\n#ifdef HAVE_NLOPT\n\tREGISTER_FECORE_CLASS(FEMMAOptimizeMethod    , \"mma\");\n\tREGISTER_FECORE_CLASS(FENelderMeadOptMethod  , \"nelder-mead\");\n\tREGISTER_FECORE_CLASS(FESubplexOptimizeMethod, \"sbplx\");\n#endif\n}\n\nint FEBioOpt::optimize(\n\tvoid (*func)(double* p, double* hx, int m, int n, void* adata),\n\tdouble* p, double* x, int m, int n, double* lb, double* ub, double* dscl,\n\tint itmax, double* opts, double* info, double* work, double* covar, void* adata)\n{\n#ifdef HAVE_LEVMAR\n\treturn dlevmar_bc_dif(func, p, x, m, n, lb, ub, dscl, itmax, opts, info, work, covar, adata);\n#else\n\treturn -1;\n#endif\n}\n"
  },
  {
    "path": "FEBioOpt/FEBioOpt.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febioopt_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEBioOpt module \n\n//! The FEBioOpt module solves parameter optimization problems with FEBio\n//!\nnamespace FEBioOpt {\n\n\tFEBIOOPT_API void InitModule();\n\n\t// wrapper function for some levmar calls.\n\t// This is only used in FEBio Studio, but don't want to link there with levmar.\n\tFEBIOOPT_API int optimize(\n\t\tvoid (*func)(double* p, double* hx, int m, int n, void* adata),\n\t\tdouble* p, double* x, int m, int n, double* lb, double* ub, double* dscl,\n\t\tint itmax, double* opts, double* info, double* work, double* covar, void* adata);\n}\n"
  },
  {
    "path": "FEBioOpt/FEBioParamRun.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioParamRun.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEShellDomain.h>\n#include <FECore/log.h>\n#include <FEBioXML/XMLReader.h>\n\n//! class constructor\nFEBioParamRun::FEBioParamRun(FEModel* pfem) : FECoreTask(pfem)\n{\n\tm_febioOutput = false;\n}\n\n//! initialization\nbool FEBioParamRun::Init(const char* szfile)\n{\n\tfeLog(\"P A R A M   R U N   M O D U L E\\n\\n\");\n\n\tFEModel* fem = GetFEModel();\n\tif (fem == nullptr) return false;\n\n\t// read the input file\n\tif (Input(szfile) == false) return false;\n\n\t// don't plot anything\n\tif (m_febioOutput == false)\n\t{\n\t\t// NOTE: I need to call GetParameterList to ensure that the parameters are allocated.\n\t\tFEParameterList& pl = fem->GetParameterList();\n\t\tfem->SetParameter(\"log_level\", 0);\n\t\tfor (int i = 0; i < fem->Steps(); ++i)\n\t\t{\n\t\t\tfem->GetStep(i)->SetPlotLevel(FE_PLOT_NEVER);\n\t\t\tfem->GetStep(i)->SetOutputLevel(FE_OUTPUT_NEVER);\n\t\t}\n\t}\n\n\t// do the initialization of the task\n\tif (m_febioOutput == false) GetFEModel()->BlockLog();\n\tif (fem->Init() == false) return false;\n\tif (m_febioOutput == false) GetFEModel()->UnBlockLog();\n\n\t// initialize all parameters\n\tfor (FEModelParameter* v : m_inVar)\n\t{\n\t\tif (v->Init() == false) return false;\n\n\t\t// set the initial value\n\t\tv->SetValue(v->InitValue());\n\t}\n\tfor (FEDataParameter* v : m_outVar)\n\t{\n\t\tif (v->Init() == false) return false;\n\t}\n\n\t// since we can change shell thickness now, we need to reinitialize \n\t// the shell elements with the new thickness\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEShellDomainNew* shellDomain = dynamic_cast<FEShellDomainNew*>(&mesh.Domain(i));\n\t\tif (shellDomain) shellDomain->AssignDefaultShellThickness();\n\t}\n\tif (!fem->InitShells()) return false;\n\n\treturn true;\n}\n\n//! read control file\nbool FEBioParamRun::Input(const char* szfile)\n{\n\tFEModel* fem = GetFEModel();\n\tif (fem == nullptr) return false;\n\n\tXMLReader xml;\n\tif (xml.Open(szfile) == false) return false;\n\n\t// find the root tag\n\tXMLTag tag;\n\tif (xml.FindTag(\"febio_run\", tag) == false) return false;\n\n\t// read the tags\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"Parameters\")\n\t\t{\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"param\")\n\t\t\t\t{\n\t\t\t\t\t// read parameter\n\t\t\t\t\tFEModelParameter* var = new FEModelParameter(fem);\n\n\t\t\t\t\t// get the variable name\n\t\t\t\t\tconst char* sz = tag.AttributeValue(\"name\");\n\t\t\t\t\tvar->SetName(sz);\n\n\t\t\t\t\t// set the value\n\t\t\t\t\tdouble val = 0.0;\n\t\t\t\t\ttag.value(val);\n\t\t\t\t\tvar->InitValue() = val;\n\n\t\t\t\t\tm_inVar.push_back(var);\n\t\t\t\t}\n\t\t\t\telse return false;\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse if (tag == \"Output\")\n\t\t{\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"file\")\n\t\t\t\t{\n\t\t\t\t\ttag.value(m_outFile);\n\t\t\t\t}\n\t\t\t\telse if (tag == \"generate_febio_output\")\n\t\t\t\t{\n\t\t\t\t\ttag.value(m_febioOutput);\n\t\t\t\t}\n\t\t\t\telse if (tag == \"param\")\n\t\t\t\t{\n\t\t\t\t\t// read parameter\n\t\t\t\t\tFEDataParameter* var = new FEDataParameter(fem);\n\n\t\t\t\t\t// get the variable name\n\t\t\t\t\tconst char* sz = tag.AttributeValue(\"name\");\n\t\t\t\t\tvar->SetParameterName(sz);\n\n\t\t\t\t\tm_outVar.push_back(var);\n\t\t\t\t}\n\t\t\t\telse return false;\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\t\t}\n\t\telse return false;\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// cleanup\n\txml.Close();\n\treturn true;\n}\n\n//! Run the task\nbool FEBioParamRun::Run()\n{\n\t// get the model\n\tFEModel* fem = GetFEModel();\n\tif (fem == nullptr) return false;\n\n\t// solve the model\n\tif (m_febioOutput == false) GetFEModel()->BlockLog();\n\tif (fem->Solve() == false) return false;\n\tif (m_febioOutput == false) GetFEModel()->UnBlockLog();\n\n\t// output the values\n\tFILE* fp = fopen(m_outFile.c_str(), \"wt\");\n\tfor (FEDataParameter* p : m_outVar)\n\t{\n\t\tdouble v = p->value();\n\t\tfprintf(fp, \"%lg\\n\", v);\n\t}\n\tfclose(fp);\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioOpt/FEBioParamRun.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FECoreTask.h>\n#include \"FEOptimizeData.h\"\n\n//-----------------------------------------------------------------------------\n// This task runs FEBio, but sets overwrites the values of some parameters\n// of the input model and prints out an output parameter to a file. \nclass FEBioParamRun : public FECoreTask\n{\npublic:\n\t//! class constructor\n\tFEBioParamRun(FEModel* pfem);\n\n\t//! initialization\n\tbool Init(const char* szfile);\n\n\t//! Run the task\n\tbool Run();\n\nprivate:\n\t//! read control file\n\tbool Input(const char* szfile);\n\nprivate:\n\tstd::vector<FEModelParameter*>\t    m_inVar;\n\tstd::vector<FEDataParameter*>\t    m_outVar;\n\tstd::string\tm_outFile;\n\tbool\tm_febioOutput;\t// generate standard FEBio log and plot file output?\n};\n"
  },
  {
    "path": "FEBioOpt/FEConstrainedLMOptimizeMethod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEConstrainedLMOptimizeMethod.h\"\n#include \"FEOptimizeData.h\"\n#include \"FEOptimizeInput.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/log.h\"\n\n#ifdef HAVE_LEVMAR\n#include \"levmar.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEConstrainedLMOptimizeMethod, FEOptimizeMethod)\n\tADD_PARAMETER(m_objtol, \"obj_tol\"     );\n\tADD_PARAMETER(m_tau   , \"tau\"         );\n\tADD_PARAMETER(m_fdiff , \"f_diff_scale\");\n\tADD_PARAMETER(m_nmax  , \"max_iter\"    );\n\tADD_PARAMETER(m_scaleParams, \"scale_parameters\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEConstrainedLMOptimizeMethod::FEConstrainedLMOptimizeMethod(FEModel* fem) : FEOptimizeMethod(fem)\n{\n\tm_pOpt = nullptr;\n\tm_tau = 1e-3;\n\tm_objtol = 0.001;\n\tm_fdiff  = 0.001;\n\tm_nmax   = 100;\n\tm_scaleParams = false;\n\tm_loglevel = LogLevel::LOG_NEVER;\n}\n\n//-----------------------------------------------------------------------------\nbool FEConstrainedLMOptimizeMethod::Solve(FEOptimizeData *pOpt, vector<double>& amin, vector<double>& ymin, double* minObj)\n{\n\tm_pOpt = pOpt;\n\tFEOptimizeData& opt = *pOpt;\n\n\t// get the data\n\tFEObjectiveFunction& obj = opt.GetObjective();\n\tint ndata = obj.Measurements();\n\tvector<double> y(ndata, 0);\n\tobj.GetMeasurements(y);\n\n\t// allocate matrices\n\tint ma = opt.InputParameters();\n\tmatrix covar(ma, ma), alpha(ma, ma);\n\n\topt.m_niter = 0;\n\n\t// return value\n\tdouble fret = 0.0;\n\n\tint niter = 1;\n\n\t// if parameter scaling is not used, just set all scale factors to one\n\t// to retain backward compatibility\n\tif (m_scaleParams == false)\n\t{\n\t\tfor (int i = 0; i < ma; ++i)\n\t\t{\n\t\t\topt.GetInputParameter(i)->ScaleFactor() = 1.0;\n\t\t}\n\t}\n\n\ttry\n\t{\n\t\tvector<double> p(ma);\n\t\tvector<double> s(ma);\n\t\tvector<double> lb(ma);\n\t\tvector<double> ub(ma);\n\t\tfor (int i = 0; i < ma; ++i)\n\t\t{\n\t\t\tFEInputParameter& v = *opt.GetInputParameter(i);\n\t\t\tdouble a = v.GetValue();\n\t\t\tdouble sf = v.ScaleFactor();\n\n\t\t\ts[i] = sf;\n\t\t\tp[i] = a / sf;\n\n\t\t\tlb[i] = v.MinValue() / sf;\n\t\t\tub[i] = v.MaxValue() / sf;\n\t\t}\n\n\t\tvector<double> q(ndata);\n\t\tfor (int i=0; i<ndata; ++i) q[i] = y[i];\n\n\t\tconst double tol = m_objtol;\n\t\tdouble opts[5] = {m_tau, tol, tol, tol, m_fdiff};\n\n\t\tint itmax = m_nmax;\n\t\tif (opt.Constraints() > 0)\n\t\t{\n\t\t\tint NC = opt.Constraints();\n\t\t\tvector<double> A(NC * ma);\n\t\t\tvector<double> b(NC);\n\t\t\tfor (int i=0; i<NC; ++i)\n\t\t\t{\n\t\t\t\tOPT_LIN_CONSTRAINT& con = opt.Constraint(i);\n\t\t\t\tfor (int j=0; j<ma; ++j) A[i*ma + j] = con.a[j] * s[j];\n\t\t\t\tb[i] = con.b;\n\t\t\t}\n\n\t\t\tint ret = dlevmar_blec_dif(objfun, p.data(), q.data(), ma, ndata, lb.data(), ub.data(), A.data(), b.data(), NC, 0, itmax, opts, 0, 0, 0, (void*) this);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint ret = dlevmar_bc_dif(objfun, p.data(), q.data(), ma, ndata, lb.data(), ub.data(), 0, itmax, opts, 0, 0, 0, (void*) this);\n\t\t}\n\n\t\tamin.resize(ma);\n\t\tfor (int i = 0; i < ma; ++i)\n\t\t{\n\t\t\tamin[i] = p[i] * s[i];\n\t\t}\n\n\t\t// store the optimal values\n\t\tfret = obj.Evaluate(m_yopt);\n\t}\n\tcatch (FEErrorTermination)\n\t{\n\t\tFEModel* fem = pOpt->GetFEModel();\n\t\tfeLogErrorEx(fem, \"FEBio error terminated. Parameter optimization cannot continue.\");\n\t\treturn false;\n\t}\n\n\t// return optimal values\n\tymin = m_yopt;\n\tif (minObj) *minObj = fret;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEConstrainedLMOptimizeMethod::ObjFun(double* p, double* hx, int m, int n)\n{\n\t// get the optimization data\n\tFEOptimizeData& opt = *GetOptimizeData();\n\tFEObjectiveFunction& obj = opt.GetObjective();\n\n\t// evaluate at a\n\tvector<double> a(m);\n\tfor (int i = 0; i < m; ++i)\n\t{\n\t\tFEInputParameter& var = *opt.GetInputParameter(i);\n\t\ta[i] = p[i]*var.ScaleFactor();\n\t}\n\n\t// poor man's box constraints\n\tfor (int i = 0; i < opt.InputParameters(); ++i)\n\t{\n\t\tFEInputParameter& var = *opt.GetInputParameter(i);\n\t\tif (a[i] < var.MinValue()) {\n\t\t\tfeLogEx(opt.GetFEModel(), \"Warning: clamping %s to min (was %lg)\\n\", var.GetName().c_str(), a[i]);\n\t\t\ta[i] = var.MinValue();\n\t\t}\n\t\telse if (a[i] >= var.MaxValue()) {\n\t\t\tfeLogEx(opt.GetFEModel(), \"Warning: clamping %s to max (was %lg)\\n\", var.GetName().c_str(), a[i]);\n\t\t\ta[i] = var.MaxValue();\n\t\t}\n\t}\n\n\t// solve the problem\n\tif (opt.FESolve(a) == false) throw FEErrorTermination();\n\n\t// store the measurement vector\n\tvector<double> y(n, 0.0);\n\topt.GetObjective().Evaluate(y);\n\tfor (int i = 0; i < n; ++i) hx[i] = y[i];\n\n\t// store the last calculated values\n\tm_yopt = y;\n}\n\n#endif\n"
  },
  {
    "path": "FEBioOpt/FEConstrainedLMOptimizeMethod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEOptimizeMethod.h\"\n#include <FECore/matrix.h>\n\n//----------------------------------------------------------------------------\n//! Optimization method using contrained Levenberg-Marquardt method\n#ifdef HAVE_LEVMAR\nclass FEConstrainedLMOptimizeMethod : public FEOptimizeMethod\n{\npublic:\n\tFEConstrainedLMOptimizeMethod(FEModel* fem);\n\tbool Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj) override;\n\n\tFEOptimizeData* GetOptimizeData() { return m_pOpt; }\n\nprotected:\n\tFEOptimizeData* m_pOpt;\n\n\tvoid ObjFun(double* p, double* hx, int m, int n);\n\n\tstatic void objfun(double* p, double* hx, int m, int n, void* adata) \n\t{ \n\t\tFEConstrainedLMOptimizeMethod* clm = (FEConstrainedLMOptimizeMethod*)adata;\n\t\treturn clm->ObjFun(p, hx, m , n);\n\t}\n\npublic:\n\tdouble\tm_tau;\t\t// scale factor for mu\n\tdouble\tm_objtol;\t// objective tolerance\n\tdouble\tm_fdiff;\t// forward difference step size\n\tint\t\tm_nmax;\t\t// maximum number of iterations\n    int     m_loglevel; // log file output level\n\tbool\tm_scaleParams;\t// scale parameters flag\n\npublic:\n\tvector<double>\tm_yopt;\t// optimal y-values\n\n\tDECLARE_FECORE_CLASS();\n};\n#endif\n"
  },
  {
    "path": "FEBioOpt/FEDataSource.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <cwctype>\n#include \"FEDataSource.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n#include <FECore/NodeDataRecord.h>\n#include <FECore/ElementDataRecord.h>\n#include <FECore/SurfaceDataRecord.h>\n#include <FECore/DomainDataRecord.h>\n\n//=================================================================================================\nFEDataSource::FEDataSource(FEModel* fem) : m_fem(*fem)\n{\n\t\n}\n\nFEDataSource::~FEDataSource()\n{\n\t\n}\n\nbool FEDataSource::Init()\n{\n\treturn true;\n}\n\nvoid FEDataSource::Reset()\n{\n\t\n}\n\n//=================================================================================================\nbool FEDataParameter::update(FEModel* pmdl, unsigned int nwhen, void* pd)\n{\n\t// get the optimizaton data\n\tFEDataParameter& src = *((FEDataParameter*)pd);\n\tsrc.update();\n\n\treturn true;\n}\n\nvoid FEDataParameter::update()\n{\n\t// get the current time value\n\tdouble time = m_fem.GetTime().currentTime;\n\n\t// evaluate the current reaction force value\n\tdouble x = m_fx();\n\tdouble y = m_fy();\n\n\t// add the data pair to the loadcurve\n\tm_rf.Add(x, y);\n}\n\nFEDataParameter::FEDataParameter(FEModel* fem) : FEDataSource(fem)\n{\n\tm_ord = \"fem.time\";\n}\n\nvoid FEDataParameter::SetParameterName(const std::string& name)\n{\n\tm_param = name;\n}\n\n// set the ordinate name\nvoid FEDataParameter::SetOrdinateName(const std::string& name)\n{\n\tm_ord = name;\n}\n\nbool FEDataParameter::Init()\n{\n\tFEModel* fem = &m_fem;\n\n\t// find all the parameters\n\tFEParamValue val = m_fem.GetParameterValue(ParamString(m_param.c_str()));\n\tif (val.isValid() == false) {\n\n\t\t// see if it's a data parameter\n\t\tif (strstr(m_param.c_str(), \"fem.node_data\"))\n\t\t{\n\t\t\tchar buf[256] = { 0 };\n\t\t\tstrcpy(buf, m_param.c_str());\n\t\t\tchar* sz = buf + 14;\n\t\t\tchar* c1 = strchr(sz, ',');\n\t\t\t*c1++ = 0;\n\n\t\t\tint nid = atoi(c1);\n\n\t\t\tc1 = strrchr(sz, '\\'');\n\t\t\tif (sz[0] == '\\'') sz++;\n\t\t\t*c1 = 0;\n\n\t\t\tFELogNodeData* pd = fecore_new<FELogNodeData>(sz, fem);\n\t\t\tif (pd == nullptr) { feLogErrorEx(fem, \"Invalid parameter name %s\", m_param.c_str()); return false; }\n\n\t\t\tFEMesh& mesh = fem->GetMesh();\n\t\t\tFENode* pn = mesh.FindNodeFromID(nid);\n\t\t\tif (pn == nullptr) { feLogErrorEx(fem, \"Invalid node id\"); return false; }\n\n\t\t\tm_fy = [=]() { return pd->value(*pn); };\n\t\t}\n\t\telse if (strstr(m_param.c_str(), \"fem.element_data\"))\n\t\t{\n\t\t\tchar buf[256] = { 0 };\n\t\t\tstrcpy(buf, m_param.c_str());\n\t\t\tchar* sz = buf + 17;\n\t\t\tchar* c1 = strchr(sz, ',');\n\t\t\t*c1++ = 0;\n\n\t\t\tint eid = atoi(c1);\n\n\t\t\tc1 = strrchr(sz, '\\'');\n\t\t\tif (sz[0] == '\\'') sz++;\n\t\t\t*c1 = 0;\n\n\t\t\tFELogElemData* pd = fecore_new<FELogElemData>(sz, fem);\n\t\t\tif (pd == nullptr) { feLogErrorEx(fem, \"Invalid parameter name %s\", m_param.c_str()); return false; }\n\n\t\t\tFEMesh& mesh = fem->GetMesh();\n\t\t\tFEElement* pe = mesh.FindElementFromID(eid);\n\t\t\tif (pe == nullptr) { feLogErrorEx(fem, \"Invalid element id\"); return false; }\n\n\t\t\tm_fy = [=]() { return pd->value(*pe); };\n\t\t}\n\t\telse if (strstr(m_param.c_str(), \"fem.surface_data\"))\n\t\t{\n\t\t\tchar buf[256] = { 0 };\n\t\t\tstrcpy(buf, m_param.c_str());\n\t\t\tchar* sz = buf + 17;\n\t\t\tchar* c1 = strchr(sz, ',');\n\t\t\t*c1++ = 0;\n\n\t\t\tchar* szsurf = strchr(c1, '\\'');\n\t\t\tif (szsurf == 0) { feLogErrorEx(fem, \"Syntax error in parameter name %s\", m_param.c_str()); return false; }\n\t\t\tszsurf++;\n\n\t\t\tc1 = strrchr(sz, '\\'');\n\t\t\tif (sz[0] == '\\'') sz++;\n\t\t\t*c1 = 0;\n\n\t\t\tc1 = strrchr(szsurf, '\\'');\n\t\t\tif (c1) *c1 = 0;\n\n\t\t\tFELogSurfaceData* pd = fecore_new<FELogSurfaceData>(sz, fem);\n\t\t\tif (pd == nullptr) { feLogErrorEx(fem, \"Invalid parameter name %s\", m_param.c_str()); return false; }\n\n\t\t\tFEMesh& mesh = fem->GetMesh();\n\t\t\tFESurface* surf = mesh.FindSurface(szsurf);\n\t\t\tif (surf == nullptr) { feLogErrorEx(fem, \"Invalid surface name %s\", szsurf); return false; }\n\n\t\t\tm_fy = [=]() { return pd->value(*surf); };\n\t\t}\n\t\telse if (strstr(m_param.c_str(), \"fem.domain_data\"))\n\t\t{\n\t\t\tconst char* c = m_param.c_str() + 15;\n\t\t\tchar buf[256] = { 0 };\n\t\t\tint n = 0;\n\t\t\tbool skipws = true;\n\t\t\twhile (c && *c)\n\t\t\t{\n\t\t\t\tif (*c == '\\'')\n\t\t\t\t{\n\t\t\t\t\tif (skipws) skipws = false; else skipws = true;\n\t\t\t\t}\n\n\t\t\t\tif ((skipws == false) || (iswspace(*c) == 0)) buf[n++] = *c;\n\t\t\t\tc++;\n\t\t\t}\n\t\t\tbuf[n] = 0;\n\n\t\t\tchar* sz = buf;\n\t\t\tint l = strlen(sz);\n\t\t\tif (sz[0  ] != '(') { feLogErrorEx(fem, \"Syntax error in parameter name %s\", m_param.c_str()); return false; }\n\t\t\tif (sz[l-1] != ')') { feLogErrorEx(fem, \"Syntax error in parameter name %s\", m_param.c_str()); return false; }\n\t\t\tsz[l - 1] = 0;\n\t\t\t*sz++ = 0;\n\n\t\t\t// separate string in data variable and domain name\n\t\t\tchar* c1 = strrchr(sz, ',');\n\t\t\t*c1++ = 0;\n\n\t\t\t// process part name\n\t\t\tchar* szdom = strchr(c1, '\\'');\n\t\t\tif (szdom == 0) { feLogErrorEx(fem, \"Syntax error in parameter name %s\", m_param.c_str()); return false; }\n\t\t\tszdom++;\n\n\t\t\tc1 = strrchr(szdom, '\\'');\n\t\t\tif (c1) *c1 = 0; else { feLogErrorEx(fem, \"Syntax error in parameter name %s\", m_param.c_str()); return false; }\n\n\t\t\tFEMesh& mesh = fem->GetMesh();\n\t\t\tFEDomain* dom = mesh.FindDomain(szdom);\n\t\t\tif (dom == nullptr) { feLogErrorEx(fem, \"Invalid domain name %s\", szdom); return false; }\n\n\t\t\t// process data name\n\t\t\tFELogDomainData* pd = nullptr;\n\t\t\tif (sz[0] == '\\'')\n\t\t\t{\n\t\t\t\tsz++;\n\t\t\t\tc1 = strrchr(sz, '\\'');\n\t\t\t\tif (c1 == nullptr) { feLogErrorEx(fem, \"Invalid domain name %s\", szdom); return false; }\n\t\t\t\t*c1 = 0;\n\t\t\t\tpd = fecore_new<FELogDomainData>(sz, fem);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tc1 = strchr(sz, '(');\n\t\t\t\tif (c1 == nullptr) { feLogErrorEx(fem, \"Syntax error\"); return false; }\n\t\t\t\tchar* cvar = c1 + 1;\n\t\t\t\t*c1 = 0;\n\t\t\t\tif ((c1 = strrchr(cvar, ')')) == nullptr) { feLogErrorEx(fem, \"Syntax error\"); return false; }\n\t\t\t\t*c1 = 0;\n\n\t\t\t\tstd::vector<std::string> varList;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (cvar[0] == '\\'')\n\t\t\t\t\t{\n\t\t\t\t\t\t*cvar++ = 0;\n\t\t\t\t\t\tif ((c1 = strchr(cvar, '\\'')) == nullptr) { feLogErrorEx(fem, \"Syntax error\"); return false; }\n\t\t\t\t\t\t*c1 = 0;\n\t\t\t\t\t\tvarList.push_back(cvar);\n\t\t\t\t\t\tcvar = c1 + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse varList.push_back(cvar);\n\t\t\t\t\tcvar = strchr(cvar, ',');\n\t\t\t\t\tif (cvar) cvar++;\n\t\t\t\t} while (cvar && *cvar);\n\n\t\t\t\tpd = fecore_new<FELogDomainData>(sz, fem);\n\t\t\t\tif (pd == nullptr) { feLogErrorEx(fem, \"Syntax error\"); return false; }\n\t\t\t\tif (pd->SetParameters(varList) == false) { feLogErrorEx(fem, \"Syntax error\"); return false; }\n\t\t\t}\n\t\t\tif (pd == nullptr) { feLogErrorEx(fem, \"Invalid parameter name %s\", m_param.c_str()); return false; }\n\n\t\t\tm_fy = [=]() { return pd->value(*dom); };\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfeLogErrorEx(fem, \"Invalid parameter name %s\", m_param.c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (val.type() != FE_PARAM_DOUBLE) {\n\t\t\tfeLogErrorEx(fem, \"Invalid type for parameter %s\", m_param.c_str());\n\t\t\treturn false;\n\t\t}\n\t\tm_fy = [=]() { return val.value<double>(); };\n\t}\n\n\t// find the ordinate\n\tval = m_fem.GetParameterValue(ParamString(m_ord.c_str()));\n\tif (val.isValid() == false) {\n\n\t\t// see if it's a data parameter\n        if (strstr(m_ord.c_str(), \"fem.node_data\"))\n        {\n            char buf[256] = { 0 };\n            strcpy(buf, m_ord.c_str());\n            char* sz = buf + 14;\n            char* c1 = strchr(sz, ',');\n            *c1++ = 0;\n            \n            int nid = atoi(c1);\n            \n            c1 = strrchr(sz, '\\'');\n            if (sz[0] == '\\'') sz++;\n            *c1 = 0;\n            \n            FELogNodeData* pd = fecore_new<FELogNodeData>(sz, fem);\n            if (pd == nullptr) { feLogErrorEx(fem, \"Invalid ordinate name %s\", m_ord.c_str()); return false; }\n            \n            FEMesh& mesh = fem->GetMesh();\n            FENode* pn = mesh.FindNodeFromID(nid);\n            if (pn == nullptr) { feLogErrorEx(fem, \"Invalid node id\"); return false; }\n            \n            m_fx = [=]() { return pd->value(*pn); };\n        }\n\t\telse if (strstr(m_ord.c_str(), \"fem.element_data\"))\n\t\t{\n\t\t\tchar buf[256] = { 0 };\n\t\t\tstrcpy(buf, m_ord.c_str());\n\t\t\tchar* sz = buf + 17;\n\t\t\tchar* c1 = strchr(sz, ',');\n\t\t\t*c1++ = 0;\n\n\t\t\tint eid = atoi(c1);\n\n\t\t\tc1 = strrchr(sz, '\\'');\n\t\t\tif (sz[0] == '\\'') sz++;\n\t\t\t*c1 = 0;\n\n\t\t\tFELogElemData* pd = fecore_new<FELogElemData>(sz, fem);\n\t\t\tif (pd == nullptr) { feLogErrorEx(fem, \"Invalid ordinate name %s\", m_ord.c_str()); return false; }\n\n\t\t\tFEMesh& mesh = fem->GetMesh();\n\t\t\tFEElement* pe = mesh.FindElementFromID(eid);\n\t\t\tif (pe == nullptr) { feLogErrorEx(fem, \"Invalid element id\"); return false; }\n\n\t\t\tm_fx = [=]() { return pd->value(*pe); };\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfeLogErrorEx(fem, \"Invalid ordinate name %s\", m_ord.c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (val.type() != FE_PARAM_DOUBLE) {\n\t\t\tfeLogErrorEx(fem, \"Invalid type for ordinate %s\", m_ord.c_str());\n\t\t\treturn false;\n\t\t}\n\t\tm_fx = [=]() { return val.value<double>(); };\n\t}\n\n\t// register callback\n\tm_fem.AddCallback(update, CB_INIT | CB_MAJOR_ITERS, (void*) this);\n\n\treturn FEDataSource::Init();\n}\n\nvoid FEDataParameter::Reset()\n{\n\t// reset the reaction force load curve\n\tm_rf.Clear();\n\tFEDataSource::Reset();\n}\n\ndouble FEDataParameter::Evaluate(double x)\n{\n\treturn m_rf.value(x);\n}\n\n//=================================================================================================\nFEDataFilterPositive::FEDataFilterPositive(FEModel* fem) : FEDataSource(fem)\n{\n\tm_src = 0;\n}\n\nFEDataFilterPositive::~FEDataFilterPositive()\n{\n\tif (m_src) delete m_src;\n}\n\nvoid FEDataFilterPositive::SetDataSource(FEDataSource* src)\n{\n\tif (m_src) delete m_src;\n\tm_src = src;\n}\n\nbool FEDataFilterPositive::Init()\n{\n\tif (m_src == 0) return false;\n\n\treturn m_src->Init();\n}\n\nvoid FEDataFilterPositive::Reset()\n{\n\tif (m_src) m_src->Reset();\n}\n\ndouble FEDataFilterPositive::Evaluate(double t)\n{\n\tdouble v = m_src->Evaluate(t);\n\treturn (v >= 0.0 ? v : -v);\n}\n\n\n//=================================================================================================\nFENodeDataFilterSum::FENodeDataFilterSum(FEModel* fem) : FEDataSource(fem)\n{\n\tm_data = nullptr;\n\tm_nodeSet = nullptr;\n}\n\nFENodeDataFilterSum::~FENodeDataFilterSum()\n{\n\tdelete m_data;\n}\n\nvoid FENodeDataFilterSum::SetData(FELogNodeData* data, FENodeSet* nodeSet)\n{\n\tm_data = data;\n\tm_nodeSet = nodeSet;\n}\n\n// Initialize data\nbool FENodeDataFilterSum::Init()\n{\n\tif (m_data == nullptr) return false;\n\tif (m_nodeSet == nullptr) return false;\n\n\tif (m_data->Init() == false) return false;\n\n\t// register callback\n\tm_fem.AddCallback(update, CB_MAJOR_ITERS, (void*)this);\n\n\treturn FEDataSource::Init();\n}\n\n// reset data\nvoid FENodeDataFilterSum::Reset()\n{\n\tm_rf.Clear();\n\tm_rf.Add(0, 0);\n}\n\n// evaluate data source at x\ndouble FENodeDataFilterSum::Evaluate(double x)\n{\n\treturn m_rf.value(x);\n}\n\nbool FENodeDataFilterSum::update(FEModel* pmdl, unsigned int nwhen, void* pd)\n{\n\t// get the optimizaton data\n\tFENodeDataFilterSum& src = *((FENodeDataFilterSum*)pd);\n\tsrc.update();\n\n\treturn true;\n}\n\nvoid FENodeDataFilterSum::update()\n{\n\t// get the current time value\n\tdouble time = m_fem.GetTime().currentTime;\n\n\tFEMesh* mesh = m_nodeSet->GetMesh();\n\tFENodeSet& ns = *m_nodeSet;\n\tdouble sum = 0.0;\n\tfor (int i = 0; i < m_nodeSet->Size(); ++i)\n\t{\n\t\tdouble vi = m_data->value(*ns.Node(i));\n\t\tsum += vi;\n\t}\n\n\t// evaluate the current reaction force value\n\tdouble x = time;\n\tdouble y = sum;\n\n\t// add the data pair to the loadcurve\n\tm_rf.Add(x, y);\n}\n\nFEElemDataFilterSum::FEElemDataFilterSum(FEModel* fem) : FEDataSource(fem)\n{\n\tm_data = nullptr;\n\tm_elemSet = nullptr;\n}\n\nFEElemDataFilterSum::~FEElemDataFilterSum()\n{\n\tdelete m_data;\n}\n\nvoid FEElemDataFilterSum::SetData(FELogElemData* data, FEElementSet* elemSet)\n{\n\tm_data = data;\n\tm_elemSet = elemSet;\n}\n\n// Initialize data\nbool FEElemDataFilterSum::Init()\n{\n\tif (m_data == nullptr) return false;\n\tif (m_elemSet == nullptr) return false;\n\n\tif (m_data->Init() == false) return false;\n\n\t// register callback\n\tm_fem.AddCallback(update, CB_MAJOR_ITERS, (void*)this);\n\n\treturn FEDataSource::Init();\n}\n\n// reset data\nvoid FEElemDataFilterSum::Reset()\n{\n\tm_rf.Clear();\n\tm_rf.Add(0, 0);\n}\n\n// evaluate data source at x\ndouble FEElemDataFilterSum::Evaluate(double x)\n{\n\treturn m_rf.value(x);\n}\n\nbool FEElemDataFilterSum::update(FEModel* pmdl, unsigned int nwhen, void* pd)\n{\n\t// get the optimizaton data\n\tFEElemDataFilterSum& src = *((FEElemDataFilterSum*)pd);\n\tsrc.update();\n\n\treturn true;\n}\n\nvoid FEElemDataFilterSum::update()\n{\n\t// get the current time value\n\tdouble time = m_fem.GetTime().currentTime;\n\n\tFEMesh* mesh = m_elemSet->GetMesh();\n\tFEElementSet& eset = *m_elemSet;\n\tdouble sum = 0.0;\n\tfor (int i = 0; i < eset.Elements(); ++i)\n\t{\n\t\tdouble vi = m_data->value(eset.Element(i));\n\t\tsum += vi;\n\t}\n\n\t// evaluate the current reaction force value\n\tdouble x = time;\n\tdouble y = sum;\n\n\t// add the data pair to the loadcurve\n\tm_rf.Add(x, y);\n}\n"
  },
  {
    "path": "FEBioOpt/FEDataSource.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/PointCurve.h>\n#include <functional>\n#include <FECore/NodeDataRecord.h>\n#include <FECore/ElementDataRecord.h>\n\n//-------------------------------------------------------------------------------------------------\n// The FEDataSource class is used by the FEObjectiveFunction to query model data and evaluate it\n// at the requested time point. This is an abstract base class and derived classes must implement\n// the Evaluate function. \nclass FEDataSource\n{\npublic:\n\tFEDataSource(FEModel* fem);\n\tvirtual ~FEDataSource();\n\n\t// Initialize data source\n\tvirtual bool Init();\n\n\t// Reset data source \n\tvirtual void Reset();\n\n\t// Evaluate source at x\n\tvirtual double Evaluate(double x) = 0;\n\nprotected:\n\tFEModel&\t\t\tm_fem;\t//!< reference to model\n};\n\n//-------------------------------------------------------------------------------------------------\n// The FEDataParameter class is a data source the extracts data from a model parameter. The parameter\n// must be set with SetParameterName before calling Init. \nclass FEDataParameter : public FEDataSource\n{\npublic:\n\t// constructor\n\tFEDataParameter(FEModel* fem);\n\t\n\t// Set the model parameter name\n\tvoid SetParameterName(const std::string& name);\n\n\t// set the ordinate name\n\tvoid SetOrdinateName(const std::string& name);\n\n\t// Initialize data\n\tbool Init() override;\n\n\t// Reset data\n\tvoid Reset() override;\n\n\t// Evaluate the model parameter at x\n\tdouble Evaluate(double x) override;\n\n\t// evaluate the current value\n\tdouble value() { return m_fy(); }\n\nprivate:\n\tstatic bool update(FEModel* pmdl, unsigned int nwhen, void* pd);\n\tvoid update();\n\nprivate:\n\tstring\tm_param;\t\t\t//!< name of parameter that generates the function data\n\tstring\tm_ord;\t\t\t\t//!< name of ordinate parameter\n\tstd::function<double()>\tm_fx;\t\t\t\t//!< pointer to ordinate value\n\tstd::function<double()>\tm_fy;\t\t\t\t//!< pointer to variable data\n\tPointCurve\t\tm_rf;\t//!< reaction force data\n};\n\n//-------------------------------------------------------------------------------------------------\n// This data source class evaluates the data using another data source and then applying a filter. \n// In this case, the filter only returns positive values or zero otherwise. The data source must be\n// set before calling Init\nclass FEDataFilterPositive : public FEDataSource\n{\npublic:\n\tFEDataFilterPositive(FEModel* fem);\n\t~FEDataFilterPositive();\n\n\t// Set the data source\n\tvoid SetDataSource(FEDataSource* src);\n\n\t// Initialize data\n\tbool Init() override;\n\n\t// reset data\n\tvoid Reset() override;\n\n\t// evaluate data source at x\n\tdouble Evaluate(double x) override;\n\nprivate:\n\tFEDataSource*\tm_src;\n};\n\nclass FENodeDataFilterSum : public FEDataSource\n{\npublic:\n\tFENodeDataFilterSum(FEModel* fem);\n\t~FENodeDataFilterSum();\n\n\tvoid SetData(FELogNodeData* data, FENodeSet* nodeSet);\n\n\t// Initialize data\n\tbool Init() override;\n\n\t// reset data\n\tvoid Reset() override;\n\n\t// evaluate data source at x\n\tdouble Evaluate(double x) override;\n\n\nprivate:\n\tstatic bool update(FEModel* pmdl, unsigned int nwhen, void* pd);\n\tvoid update();\n\nprivate:\n\tFELogNodeData*\tm_data;\n\tFENodeSet*\t\tm_nodeSet;\n\tPointCurve\t\tm_rf;\n};\n\nclass FEElemDataFilterSum : public FEDataSource\n{\npublic:\n\tFEElemDataFilterSum(FEModel* fem);\n\t~FEElemDataFilterSum();\n\n\tvoid SetData(FELogElemData* data, FEElementSet* elemSet);\n\n\t// Initialize data\n\tbool Init() override;\n\n\t// reset data\n\tvoid Reset() override;\n\n\t// evaluate data source at x\n\tdouble Evaluate(double x) override;\n\nprivate:\n\tstatic bool update(FEModel* pmdl, unsigned int nwhen, void* pd);\n\tvoid update();\n\nprivate:\n\tFELogElemData*\tm_data;\n\tFEElementSet*\tm_elemSet;\n\tPointCurve\t\tm_rf;\n};\n"
  },
  {
    "path": "FEBioOpt/FELMOptimizeMethod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELMOptimizeMethod.h\"\n#include \"FEOptimizeData.h\"\n#include \"FEOptimizeInput.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FELMOptimizeMethod, FEOptimizeMethod)\n\tADD_PARAMETER(m_objtol, \"obj_tol\"     );\n\tADD_PARAMETER(m_fdiff , \"f_diff_scale\");\n\tADD_PARAMETER(m_nmax  , \"max_iter\"    );\n\tADD_PARAMETER(m_bcov  , \"print_cov\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFELMOptimizeMethod* FELMOptimizeMethod::m_pThis = 0;\n\n// forward declarations\nvoid mrqmin(vector<double>& x, \n\t\t\tvector<double>& y, \n\t\t\tvector<double>& sig, \n\t\t\tvector<double>& a, \n\t\t\tmatrix& covar, \n\t\t\tmatrix& alpha, \n\t\t\tvector<double>& oneda,\n\t\t\tvector<double>& atry,\n\t\t\tvector<double>& beta,\n\t\t\tvector<double>& da,\n\t\t\tdouble& chisq,\n\t\t\tvoid funcs(vector<double>& , vector<double>&, vector<double>&, matrix&),\n\t\t\tdouble& alamda);\n\nvoid mrqcof(vector<double>& x, \n\t\t\tvector<double>& y, \n\t\t\tvector<double>& sig, \n\t\t\tvector<double>& a, \n\t\t\tmatrix& alpha,\n\t\t\tvector<double>& beta,\n\t\t\tdouble& chisq,\n\t\t\tvoid funcs(vector<double>& , vector<double>&, vector<double>&, matrix&));\n\n//-----------------------------------------------------------------------------\nFELMOptimizeMethod::FELMOptimizeMethod(FEModel* fem) : FEOptimizeMethod(fem)\n{\n\tm_objtol = 0.001;\n\tm_fdiff  = 0.001;\n\tm_nmax   = 100;\n\tm_bcov   = 0;\n\tm_loglevel = LogLevel::LOG_NEVER;\n}\n\n//-----------------------------------------------------------------------------\nbool FELMOptimizeMethod::Solve(FEOptimizeData *pOpt, vector<double>& amin, vector<double>& ymin, double* minObj)\n{\n\tm_pOpt = pOpt;\n\tFEOptimizeData& opt = *pOpt;\n\n\t// set the variables\n\tint ma = opt.InputParameters();\n\tvector<double> a(ma);\n\tfor (int i=0; i<ma; ++i)\n\t{\n\t\tFEInputParameter& var = *opt.GetInputParameter(i);\n\t\ta[i] = var.GetValue();\n\t}\n\n\t// set the data\n\tFEObjectiveFunction& obj = opt.GetObjective();\n\tint ndata = obj.Measurements();\n\tvector<double> x(ndata), y(ndata);\n\tobj.GetMeasurements(y);\n\n\t// we don't really need x so we create a dummy array\n\tfor (int i = 0; i<ndata; ++i) x[i] = i;\n\n\t// set the sigma's\n\t// for now we set them all to 1\n\tvector<double> sig(ndata);\n\tfor (int i = 0; i<ndata; ++i) sig[i] = 1;\n\n\t// allocate matrices\n\tmatrix covar(ma, ma), alpha(ma, ma);\n\n\t// allocate vectors for mrqmin\n\tvector<double> oneda(ma), atry(ma), beta(ma), da(ma);\n\n\t// set the this pointer\n\tm_pThis = this;\n\n\topt.m_niter = 0;\n\n\t// return value\n\tdouble fret = 0.0;\n\n\tint niter = 1;\n\n\tFEModel* fem = pOpt->GetFEModel();\n\n\ttry\n\t{\n\t\t// do the first call with lamda to intialize the minimization\n\t\tdouble alamda = -1.0;\n\t\tfeLogEx(fem, \"\\n----- Major Iteration: %d -----\\n\", 0);\n\t\tmrqmin(x, y, sig, a, covar, alpha, oneda, atry, beta, da, fret, objfun, alamda);\n\n\t\t// repeat until converged\n\t\tdouble fprev = fret, lam1 = alamda;\n\t\tbool bconv = false;\n\t\tdo\n\t\t{\n\t\t\tfeLogEx(fem, \"\\n----- Major Iteration: %d -----\\n\", niter);\n\t\t\tmrqmin(x, y, sig, a, covar, alpha, oneda, atry, beta, da, fret, objfun, alamda);\n\n\t\t\tif (alamda < lam1)\n\t\t\t{\n\t\t\t\tif (niter != 1)\n\t\t\t\t{\n\t\t\t\t\tdouble df = (fprev - fret)/(fprev + fret + 1);\n\t\t\t\t\tif ( df < m_objtol) bconv = true;\n\t\t\t\t\tfeLogEx(fem, \"objective value: %lg (diff = %lg)\\n\\n\", fret, df);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse feLogEx(fem, \"\\n objective value: %lg\\n\\n\", fret);\n\n\t\t\tfprev = fret;\n\t\t\tlam1 = alamda;\n\n\t\t\t++niter;\n\t\t}\n\t\twhile ((bconv == false) && (niter < m_nmax));\n\n\t\t// do final call with lamda = 0\n\t\talamda = 0.0;\n\t\tmrqmin(x, y, sig, a, covar, alpha, oneda, atry, beta, da, fret, objfun, alamda);\n\n\t}\n\tcatch (FEErrorTermination)\n\t{\n\t\tfeLogErrorEx(fem, \"FEBio error terminated. Parameter optimization cannot continue.\");\n\t\treturn false;\n\t}\n\n\t// store optimal values\n\tamin = a;\n\tymin = m_yopt;\n\tif (minObj) *minObj = fret;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELMOptimizeMethod::ObjFun(vector<double>& x, vector<double>& a, vector<double>& y, matrix& dyda)\n{\n\t// get the optimization data\n\tFEOptimizeData& opt = *m_pOpt;\n\n\t// poor man's box constraints\n\tdouble dir = 1;\t// forward difference by default\n\tfor (int i=0; i<opt.InputParameters(); ++i)\n\t{\n\t\tFEInputParameter& var = *opt.GetInputParameter(i);\n\t\tif (a[i] < var.MinValue()) {\n\t\t\ta[i] = var.MinValue();\n\t\t} else if (a[i] >= var.MaxValue()) {\n\t\t\ta[i] = var.MaxValue();\n\t\t\tdir = -1;\t// use backward difference\n\t\t}\n\t}\n\t\n\t// evaluate at a\n\tif (opt.FESolve(a) == false) throw FEErrorTermination();\n\t\n\topt.GetObjective().Evaluate(y);\n\tm_yopt = y;\n\n\t// now calculate the derivatives using forward differences\n\tint ndata = (int)x.size();\n\tvector<double> a1(a);\n\tvector<double> y1(ndata);\n\tint ma = (int)a.size();\n\tfor (int i=0; i<ma; ++i)\n\t{\n\t\tFEInputParameter& var = *opt.GetInputParameter(i);\n\n\t\tdouble b = var.ScaleFactor();\n\n\t\ta1[i] = a1[i] + dir*m_fdiff*(fabs(b) + fabs(a[i]));\n\t\tassert(a1[i] != a[i]);\n\n\t\tif (opt.FESolve(a1) == false) throw FEErrorTermination();\n\t\topt.GetObjective().Evaluate(y1);\n\t\tfor (int j=0; j<ndata; ++j) dyda[j][i] = (y1[j] - y[j])/(a1[i] - a[i]);\n\t\ta1[i] = a[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid mrqmin(vector<double>& x, \n\t\t\tvector<double>& y, \n\t\t\tvector<double>& sig, \n\t\t\tvector<double>& a, \n\t\t\tmatrix& covar, \n\t\t\tmatrix& alpha, \n\t\t\tvector<double>& oneda,\n\t\t\tvector<double>& atry,\n\t\t\tvector<double>& beta,\n\t\t\tvector<double>& da,\n\t\t\tdouble& chisq,\n\t\t\tvoid funcs(vector<double>& , vector<double>&, vector<double>&, matrix&),\n\t\t\tdouble& alamda)\n{\n\tstatic double ochisq;\n\tint j, k, l;\n\n\tint ma = (int)a.size();\n\tif (alamda < 0)\n\t{\n\t\talamda = 0.001;\n\t\tmrqcof(x, y, sig, a, alpha, beta, chisq, funcs);\n\t\tochisq = chisq;\n\t\tfor (j=0; j<ma; j++) atry[j] = a[j];\n\t}\n\tmatrix temp(ma, ma);\n\tfor (j=0; j<ma; j++)\n\t{\n\t\tfor (k=0; k<ma; k++) covar[j][k] = alpha[j][k];\n\t\tcovar[j][j] = alpha[j][j]*(1.0 + alamda);\n\t\tfor (k=0; k<ma; k++) temp[j][k] = covar[j][k];\n\t\toneda[j] = beta[j];\n\t}\n\tmatrix tempi = temp.inverse();\n\toneda = tempi*oneda;\n\tfor (j=0; j<ma; j++)\n\t{\n\t\tfor (k=0; k<ma; k++) covar[j][k] = tempi[j][k];\n\t\tda[j] = oneda[j];\n\t}\n\n\tif (alamda == 0.0) return;\n\n\tfor (j=0, l=0; l<ma; l++) atry[l] = a[l] + da[j++];\n\tmrqcof(x, y, sig, atry, covar, da, chisq, funcs);\n\tif (chisq < ochisq)\n\t{\n\t\talamda *= 0.1;\n\t\tochisq = chisq;\n\t\tfor (j=0; j<ma; j++)\n\t\t{\n\t\t\tfor (k=0; k<ma; k++) alpha[j][k] = covar[j][k];\n\t\t\tbeta[j] = da[j];\n\t\t}\n\t\tfor (l=0; l<ma; l++) a[l] = atry[l];\n\t}\n\telse\n\t{\n\t\talamda *= 10.0;\n\t\tchisq = ochisq;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid mrqcof(vector<double>& x, \n\t\t\tvector<double>& y, \n\t\t\tvector<double>& sig, \n\t\t\tvector<double>& a, \n\t\t\tmatrix& alpha,\n\t\t\tvector<double>& beta,\n\t\t\tdouble& chisq,\n\t\t\tvoid funcs(vector<double>& , vector<double>&, vector<double>&, matrix&))\n{\n\tint i, j, k, l, m;\n\tdouble wt, sig2i, dy;\n\n\tint ndata = (int)x.size();\n\tint ma = (int)a.size();\n\tfor (j=0; j<ma; j++)\n\t{\n\t\tfor (k=0; k<=j; k++) alpha[j][k] = 0.0;\n\t\tbeta[j] = 0.0;\n\t}\n\n\tvector<double> ymod(ndata);\n\tmatrix dyda(ndata, ma);\n\tfuncs(x, a, ymod, dyda);\n\t\n\tchisq = 0.0;\n\tfor (i=0; i<ndata; i++)\n\t{\n\t\tsig2i = 1.0 / (sig[i]*sig[i]);\n\t\tdy = y[i] - ymod[i];\n\t\tfor (j=0, l=0; l<ma; l++)\n\t\t{\n\t\t\twt = dyda[i][l]*sig2i;\n\t\t\tfor (k=0, m=0; m<l+1; m++) alpha[j][k++] += wt*dyda[i][m];\n\t\t\tbeta[j++] += dy*wt;\n\t\t}\n\t\tchisq += dy*dy*sig2i;\n\t}\n\tfor (j=1; j<ma; j++)\n\t\tfor (k=0; k<j; k++) alpha[k][j] = alpha[j][k];\n}\n"
  },
  {
    "path": "FEBioOpt/FELMOptimizeMethod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEOptimizeMethod.h\"\n#include <FECore/matrix.h>\n#include <vector>\n\n//----------------------------------------------------------------------------\n//! Optimization method using Levenberg-Marquardt method\nclass FELMOptimizeMethod : public FEOptimizeMethod\n{\npublic:\n\tFELMOptimizeMethod(FEModel* fem);\n\tbool Solve(FEOptimizeData* pOpt, std::vector<double>& amin, std::vector<double>& ymin, double* minObj) override;\n\nprotected:\n\tFEOptimizeData* m_pOpt;\n\n\tvoid ObjFun(std::vector<double>& x, std::vector<double>& a, std::vector<double>& y, matrix& dyda);\n\n\tstatic FELMOptimizeMethod* m_pThis;\n\tstatic void objfun(std::vector<double>& x, std::vector<double>& a, std::vector<double>& y, matrix& dyda) { return m_pThis->ObjFun(x, a, y, dyda); }\n\npublic:\n\tdouble\t\t\tm_objtol;\t// objective tolerance\n\tdouble\t\t\tm_fdiff;\t// forward difference step size\n\tint\t\t\t\tm_nmax;\t\t// maximum number of iterations\n\tbool\t\t\tm_bcov;\t\t// flag to print covariant matrix\n\nprotected:\n\tstd::vector<double>\tm_yopt;\t// optimal y-values\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioOpt/FEObjectiveFunction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEObjectiveFunction.h\"\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\n//=============================================================================\n\nFEObjectiveFunction::FEObjectiveFunction(FEModel* fem) : m_fem(fem)\n{\n\tm_verbose = false;\n}\n\nFEObjectiveFunction::~FEObjectiveFunction()\n{\n\t\n}\n\nbool FEObjectiveFunction::Init()\n{\n\t// make sure we have a model\n\tif (m_fem == 0) return false;\n\n\treturn true;\n}\n\nvoid FEObjectiveFunction::Reset()\n{\n}\n\ndouble FEObjectiveFunction::Evaluate()\n{\n\tvector<double> dummy(Measurements());\n\treturn Evaluate(dummy);\n}\n\ndouble FEObjectiveFunction::Evaluate(vector<double>& y)\n{\n\t// get the number of measurements\n\tint ndata = Measurements();\n\ty.resize(ndata);\n\n\t// evaluate the functions\n\tEvaluateFunctions(y);\n\n\t// get the measurement vector\n\tvector<double> y0(ndata);\n\tGetMeasurements(y0);\n\n\tvector<double> x(ndata);\n\tfor (int i = 0; i < ndata; ++i) x[i] = i + 1;\n\tGetXValues(x);\n\n\t// evaluate regression coefficient R^2\n\tdouble rsq = RegressionCoefficient(y0, y);\n\n\tdouble chisq = 0.0;\n\tif (m_verbose) feLog(\"                        CURRENT        REQUIRED      DIFFERENCE\\n\");\n\tfor (int i = 0; i<ndata; ++i)\n\t{\n\t\tdouble dy = (y[i] - y0[i]);\n\t\tchisq += dy*dy;\n\t\tif (m_verbose) feLog(\"%15.10lg %15.10lg %15.10lg %15lg\\n\", x[i], y[i], y0[i], fabs(y[i] - y0[i]));\n\t}\n\tfeLog(\"objective value: %lg\\n\", chisq);\n    feLog(\"regression coef: %lg\\n\", rsq);\n\n\treturn chisq;\n}\n\ndouble FEObjectiveFunction::RegressionCoefficient(const std::vector<double>& y0, const std::vector<double>& y)\n{\n\tint ndata = (int)y0.size();\n\tdouble xb = 0, yb = 0, xyb = 0, x2b = 0, y2b = 0;\n\tfor (int i = 0; i < ndata; ++i)\n\t{\n\t\txb += y0[i]; yb += y[i];\n\t\txyb += y0[i] * y[i];\n\t\tx2b += pow(y0[i], 2); y2b += pow(y[i], 2);\n\t}\n\txb /= ndata; yb /= ndata;\n\txyb /= ndata;\n\tx2b /= ndata; y2b /= ndata;\n\n\tdouble D = (x2b - xb * xb) * (y2b - yb * yb);\n\n\tdouble rsq = (D != 0 ? pow(xyb - xb * yb, 2) / D : 0);\n\treturn rsq;\n}\n\n//=============================================================================\n\n//----------------------------------------------------------------------------\nFEDataFitObjective::FEDataFitObjective(FEModel* fem) : FEObjectiveFunction(fem)\n{\n\tm_src = 0;\n}\n\nFEDataFitObjective::~FEDataFitObjective()\n{\n\tif (m_src) delete m_src;\n\tm_src = 0;\n}\n\n//----------------------------------------------------------------------------\nbool FEDataFitObjective::Init()\n{\n\tif (FEObjectiveFunction::Init() == false) return false;\n\n\t// get the FE model\n\tFEModel& fem = *GetFEModel();\n\n\t// initialize data source\n\tif (m_src == 0) return false;\n\tif (m_src->Init() == false) return false;\n\n\treturn true;\n}\n\n//----------------------------------------------------------------------------\n// set the data source\nvoid FEDataFitObjective::SetDataSource(FEDataSource* src)\n{\n\tif (m_src) delete m_src;\n\tm_src = src;\n}\n\n//----------------------------------------------------------------------------\n// set the data measurements\nvoid FEDataFitObjective::SetMeasurements(const vector<pair<double, double> >& data)\n{\n\tm_lc.Clear();\n\tint n = (int)data.size();\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tconst pair<double,double>& pt = data[i];\n\t\tm_lc.Add(pt.first, pt.second);\n\t}\n}\n\n//----------------------------------------------------------------------------\nvoid FEDataFitObjective::Reset()\n{\n\t// call base class first\n\tFEObjectiveFunction::Reset();\n\n\tm_src->Reset();\n}\n\n//----------------------------------------------------------------------------\n// return the number of measurements. I.e. the size of the measurement vector\nint FEDataFitObjective::Measurements()\n{\n\treturn m_lc.Points();\n}\n\n//----------------------------------------------------------------------------\n// Evaluate the measurement vector and return in y0\nvoid FEDataFitObjective::GetMeasurements(vector<double>& y0)\n{\n\tint ndata = m_lc.Points();\n\ty0.resize(ndata);\n\tfor (int i = 0; i<ndata; ++i) y0[i] = m_lc.Point(i).y();\n}\n\nvoid FEDataFitObjective::GetXValues(std::vector<double>& x)\n{\n\tx = m_x;\n}\n\n//----------------------------------------------------------------------------\nvoid FEDataFitObjective::EvaluateFunctions(vector<double>& f)\n{\n\tint ndata = m_lc.Points();\n\tm_x.resize(ndata);\n\tfor (int i = 0; i<ndata; ++i)\n\t{\n\t\tdouble xi = m_lc.Point(i).x();\n\t\tm_x[i] = xi;\n\t\tf[i] = m_src->Evaluate(xi);\n\t}\n}\n\n//=============================================================================\n\nbool FEMinimizeObjective::ParamFunction::Init()\n{\n\tif (!Function::Init()) return false;\n\n\tFEParamValue val = m_fem->GetParameterValue(ParamString(m_name.c_str()));\n\tif (val.isValid() == false) return false;\n\tif (val.type() != FE_PARAM_DOUBLE) return false;\n\tm_var = (double*)val.data_ptr();\n\tif (m_var == nullptr) return false;\n\treturn true;\n}\n\nFEMinimizeObjective::FilterAvgFunction::FilterAvgFunction(FEModel* fem, FELogElemData* pd, FEElementSet* elemSet, double trg) : Function(fem)\n{\n\tm_pd = pd;\n\tm_elemSet = elemSet;\n\tm_y0 = trg;\n}\n\nbool FEMinimizeObjective::FilterAvgFunction::Init()\n{\n\tif (!Function::Init()) return false;\n\tif (m_pd == nullptr) return false;\n\tif (m_elemSet == nullptr) return false;\n\treturn true;\n}\n\ndouble FEMinimizeObjective::FilterAvgFunction::Value() const\n{\n\tint NE = m_elemSet->Elements();\n\tdouble sum = 0.0;\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tsum += m_pd->value(m_elemSet->Element(i));\n\t}\n\tsum /= (double)NE;\n\n\treturn sum;\n}\n\nFEMinimizeObjective::FEMinimizeObjective(FEModel* fem) : FEObjectiveFunction(fem)\n{\n}\n\nFEMinimizeObjective::~FEMinimizeObjective()\n{\n\tfor (Function* f : m_Func) delete f;\n\tm_Func.clear();\n}\n\nvoid FEMinimizeObjective::AddFunction(FEMinimizeObjective::Function* func)\n{\n\tm_Func.push_back(func);\n}\n\nbool FEMinimizeObjective::Init()\n{\n\tif (FEObjectiveFunction::Init() == false) return false;\n\n\tFEModel* fem = GetFEModel();\n\tif (fem == nullptr) return false;\n\n\tint N = (int) m_Func.size();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFunction& Fi = *m_Func[i];\n\t\tif (Fi.Init() == false) return false;\n\t}\n\n\treturn true;\n}\n\nint FEMinimizeObjective::Measurements()\n{\n\treturn (int) m_Func.size();\n}\n\nvoid FEMinimizeObjective::EvaluateFunctions(vector<double>& f)\n{\n\tint N = (int)m_Func.size();\n\tf.resize(N);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFunction& Fi = *m_Func[i];\n\t\tf[i] = Fi.Value();\n\t}\n}\n\nvoid FEMinimizeObjective::GetMeasurements(vector<double>& y)\n{\n\tint N = (int) m_Func.size();\n\ty.resize(N);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\ty[i] = m_Func[i]->Target();\n\t}\n}\n\n//=============================================================================\nFEElementDataTable::FEElementDataTable(FEModel* fem) : FEObjectiveFunction(fem)\n{\n\tm_var = nullptr;\n}\n\nvoid FEElementDataTable::AddValue(int elemID, double v)\n{\n\tEntry d;\n\td.elemId = elemID;\n\td.target = v;\n\td.pe = nullptr;\n\tm_Data.push_back(d);\n}\n\nvoid FEElementDataTable::SetVariable(FELogElemData* var)\n{\n\tm_var = var;\n}\n\nbool FEElementDataTable::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tint N = (int)m_Data.size();\n\tif (N == 0) return false;\n\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tEntry& di = m_Data[i];\n\t\tFEElement* el = mesh.FindElementFromID(di.elemId);\n\t\tif (el == nullptr) return false;\n\t\tdi.pe = el;\n\t}\n\n\treturn true;\n}\n\n// return number of measurements (i.e. nr of terms in objective function)\nint FEElementDataTable::Measurements()\n{\n\treturn (int)m_Data.size();\n}\n\n// evaluate the function values (i.e. the f_i above)\nvoid FEElementDataTable::EvaluateFunctions(vector<double>& f)\n{\n\tassert(m_var);\n\n\tint N = (int)m_Data.size();\n\tf.resize(N);\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFEElement* pe = m_Data[i].pe;\n\n\t\t// calculate element average measure\n\t\tdouble val = m_var->value(*pe);\n\n\t\t// store result\n\t\tf[i] = val;\n\t}\n}\n\n// get the measurement vector (i.e. the y_i above)\nvoid FEElementDataTable::GetMeasurements(vector<double>& y)\n{\n\tint N = (int)m_Data.size();\n\ty.resize(N);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\ty[i] = m_Data[i].target;\n\t}\n}\n\n\n//=============================================================================\nFENodeDataTable::FENodeDataTable(FEModel* fem) : FEObjectiveFunction(fem)\n{\n\t\n}\n\nbool FENodeDataTable::AddValue(int nodeID, vector<double>& v)\n{\n\tif (v.size() != m_var.size()) return false;\n\n\tfor (int i = 0; i < v.size(); ++i)\n\t{\n\t\tEntry d;\n\t\td.nodeId = nodeID;\n\t\td.target = v[i];\n\t\td.ivar = i;\n\t\td.index = -1;\n\t\tm_Data.push_back(d);\n\t}\n\n\treturn true;\n}\n\nvoid FENodeDataTable::AddVariable(FELogNodeData* var)\n{\n\tm_var.push_back(var);\n}\n\nbool FENodeDataTable::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tint N = (int)m_Data.size();\n\tif (N == 0) return false;\n\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tEntry& di = m_Data[i];\n\t\tFENode* node = mesh.FindNodeFromID(di.nodeId);\n\n\t\tif (node == nullptr) return false;\n\t\tdi.index = di.nodeId - 1;\t// NOTE: This assumes one-based indexing of node IDs. \n\t}\n\n\treturn true;\n}\n\n// return number of measurements (i.e. nr of terms in objective function)\nint FENodeDataTable::Measurements()\n{\n\treturn (int)m_Data.size();\n}\n\n// evaluate the function values (i.e. the f_i above)\nvoid FENodeDataTable::EvaluateFunctions(vector<double>& f)\n{\n\tassert(m_var.size() > 0);\n\n\tint N = (int)m_Data.size();\n\tf.resize(N);\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tint n = m_Data[i].index;\n\t\tint v = m_Data[i].ivar;\n\n\t\t// calculate node value\n\t\tdouble val = m_var[v]->value(mesh.Node(n));\n\n\t\t// store result\n\t\tf[i] = val;\n\t}\n}\n\n// get the measurement vector (i.e. the y_i above)\nvoid FENodeDataTable::GetMeasurements(vector<double>& y)\n{\n\tint N = (int)m_Data.size();\n\ty.resize(N);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\ty[i] = m_Data[i].target;\n\t}\n}\n"
  },
  {
    "path": "FEBioOpt/FEObjectiveFunction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/PointCurve.h>\n#include <vector>\n#include <string>\n#include \"FEDataSource.h\"\n#include <FECore/ElementDataRecord.h>\n#include <FECore/NodeDataRecord.h>\n\nclass FEModel;\nclass FEElement;\n\n//=============================================================================\n//! This class evaluates the objective function, which is defined as the sum\n//! of squares of non-linear functions. \n//            ----\n//             \\                   2\n//  f_obj =    /    [ f_i(a) - y_i]\n//            ----\n//              i\n//\n//! It is an abstract base class and derived classes\n//! need to implement the nonlinear functions.\nclass FEObjectiveFunction\n{\npublic:\n\t// constructor\n\tFEObjectiveFunction(FEModel* fem);\n\tvirtual ~FEObjectiveFunction();\n\n\t// one-time initialization\n\tvirtual bool Init();\n\n\t// This is called before each optimization iteration\n\t// and should be used by derived classes to reset any data\n\tvirtual void Reset();\n\n\t// evaluate objective function\n\t// also returns the function values in f\n\tvirtual double Evaluate(std::vector<double>& f);\n\n\t// evaluate objective function\n\tdouble Evaluate();\n\n\t// print output to screen or not\n\tvoid SetVerbose(bool b) { m_verbose = b; }\n\n\tbool IsVerbose() const { return m_verbose; }\n\n\t// return the FE model\n\tFEModel* GetFEModel() { return m_fem; }\n\npublic:\n\tdouble RegressionCoefficient(const std::vector<double>& y0, const std::vector<double>& y);\n\npublic: // These functions need to be implemented by derived classes\n\n\t// return number of measurements (i.e. nr of terms in objective function)\n\tvirtual int Measurements() = 0;\n\n\t// evaluate the function values (i.e. the f_i above)\n\tvirtual void EvaluateFunctions(std::vector<double>& f) = 0;\n\n\t// get the measurement vector (i.e. the y_i above)\n\tvirtual void GetMeasurements(std::vector<double>& y) = 0;\n\n\t// get the x values (ignore if not applicable)\n\tvirtual void GetXValues(std::vector<double>& x) {}\n\nprivate:\n\tFEModel*\tm_fem;\n\tbool\tm_verbose;\t\t//!< print data flag\n};\n\n//=============================================================================\n// Objective function for fitting value pairs data\n// In this case the nonlinear functions are defined as:\n// f_i(a) = F(t_i; a)\n// where t_i is the time for time step i, and F is the function that will be fitted to the measurement vector.\nclass FEDataFitObjective : public FEObjectiveFunction\n{\npublic:\n\tFEDataFitObjective(FEModel* fem);\n\t~FEDataFitObjective();\n\n\t// one-time initialization\n\tbool Init();\n\n\t// This is called before each optimization iteration\n\t// and should be used by derived classes to reset any data\n\tvoid Reset();\n\n\t// set the data source\n\tvoid SetDataSource(FEDataSource* src);\n\n\t// set the data measurements\n\tvoid SetMeasurements(const std::vector<pair<double, double> >& data);\n\npublic:\n\t// return number of measurements\n\tint Measurements();\n\n\t// evaluate the function values\n\tvoid EvaluateFunctions(std::vector<double>& f);\n\n\t// get the measurement vector\n\tvoid GetMeasurements(std::vector<double>& y);\n\n\tvoid GetXValues(std::vector<double>& x);\n\nprivate:\n\tPointCurve\t\t\tm_lc;\t\t//!< data load curve for evaluating measurements\n\tFEDataSource*\t\tm_src;\t\t//!< source for evaluating functions\n\tstd::vector<double>\tm_x;\n};\n\n//=============================================================================\n// Objective function for minimization of model parameters\nclass FEMinimizeObjective : public FEObjectiveFunction\n{\n\tclass Function\n\t{\n\tpublic:\n\t\tFunction(FEModel* fem) : m_fem(fem) {}\n\t\tvirtual ~Function() {}\n\n\t\tvirtual bool Init() { return (m_fem != nullptr); }\n\n\t\tvirtual double Target() const = 0;\n\t\tvirtual double Value() const = 0;\n\n\tprotected:\n\t\tFEModel* m_fem;\n\t};\n\npublic:\n\tclass ParamFunction : public Function\n\t{\n\tpublic:\n\t\tstring\tm_name;\n\t\tdouble* m_var = nullptr;\n\n\t\tdouble\tm_y0 = 0;\t\t// target value (i.e. \"measurment\")\n\n\t\tParamFunction(FEModel* fem, const string& name, double trg) : Function(fem), m_name(name), m_y0(trg) {}\n\n\tpublic:\n\t\tbool Init() override;\n\t\tdouble Target() const override { return m_y0; }\n\t\tdouble Value() const override { return *m_var; }\n\t};\n\n\tclass FilterAvgFunction : public Function\n\t{\n\tpublic:\n\t\tFELogElemData* m_pd = nullptr;\n\t\tFEElementSet* m_elemSet = nullptr;\n\t\tdouble\tm_y0 = 0;\n\n\t\tFilterAvgFunction(FEModel* fem, FELogElemData* pd, FEElementSet* elemSet, double trg);\n\n\tpublic:\n\t\tbool Init() override;\n\t\tdouble Target() const override { return m_y0; }\n\t\tdouble Value() const override;\n\t};\n\npublic:\n\tFEMinimizeObjective(FEModel* fem);\n\t~FEMinimizeObjective();\n\n\t// one-time initialization\n\tbool Init() override;\n\n\tvoid AddFunction(Function* func);\n\npublic:\n\t// return number of measurements\n\tint Measurements() override;\n\n\t// evaluate the function values\n\tvoid EvaluateFunctions(std::vector<double>& f) override;\n\n\t// get the measurement vector\n\tvoid GetMeasurements(std::vector<double>& y) override;\n\nprivate:\n\tstd::vector<Function*>\tm_Func;\n};\n\n//=============================================================================\n// This objective function evaluates element values with a user-defined\n// table of values. The table stores for each element the required value. \nclass FEElementDataTable : public FEObjectiveFunction\n{\n\tstruct Entry {\n\t\tint\t\t\telemId;\t// the ID of the element\n\t\tdouble\t\ttarget;\t// the target value\n\t\tFEElement*\tpe;\t\t// pointer to element\n\t};\n\npublic:\n\tFEElementDataTable(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid AddValue(int elemID, double v);\n\n\tvoid SetVariable(FELogElemData* var);\n\npublic:\n\t// return number of measurements (i.e. nr of terms in objective function)\n\tint Measurements() override;\n\n\t// evaluate the function values (i.e. the f_i above)\n\tvoid EvaluateFunctions(std::vector<double>& f) override;\n\n\t// get the measurement vector (i.e. the y_i above)\n\tvoid GetMeasurements(std::vector<double>& y) override;\n\nprivate:\n\tstd::vector<Entry>\tm_Data;\n\tFELogElemData*\t\tm_var;\n};\n\n//=============================================================================\n// This objective function evaluates node values with a user-defined\n// table of values. The table stores for each node the required value. \nclass FENodeDataTable : public FEObjectiveFunction\n{\n\tstruct Entry {\n\t\tint\t\t\tnodeId;\t// the ID of the node\n\t\tdouble\t\ttarget;\t// the target value\n\t\tint\t\t\tindex;\t// zero-based node index\n\t\tint\t\t\tivar;  // variable\n\t};\n\npublic:\n\tFENodeDataTable(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool AddValue(int elemID, std::vector<double>& v);\n\n\tvoid AddVariable(FELogNodeData* var);\n\npublic:\n\t// return number of measurements (i.e. nr of terms in objective function)\n\tint Measurements() override;\n\n\t// evaluate the function values (i.e. the f_i above)\n\tvoid EvaluateFunctions(std::vector<double>& f) override;\n\n\t// get the measurement vector (i.e. the y_i above)\n\tvoid GetMeasurements(std::vector<double>& y) override;\n\nprivate:\n\tstd::vector<Entry>\t\t\t\tm_Data;\n\tstd::vector<FELogNodeData*>\t\tm_var;\n};\n"
  },
  {
    "path": "FEBioOpt/FEOptimize.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOptimize.h\"\n#include \"FEOptimizeData.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FECore/log.h\"\n#include \"FECore/Timer.h\"\n\n//-----------------------------------------------------------------------------\n#define VERSION 2\n#define SUB_VERSION\t0\n\n//-----------------------------------------------------------------------------\nFEOptimize::FEOptimize(FEModel* pfem) : FECoreTask(pfem), m_opt(pfem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FEOptimize::Init(const char* szfile)\n{\n\tchar szversion[32] = { 0 };\n\tsnprintf(szversion, sizeof(szversion), \"version %d.%d\", VERSION, SUB_VERSION);\n\tfeLog(\"P A R A M E T E R   O P T I M I Z A T I O N   M O D U L E\\n%s\\n\\n\", szversion);\n\n\t// read the data from the xml input file\n\tif (m_opt.Input(szfile) == false) return false;\n\n\t// do initialization\n\tbool ret = m_opt.Init();\n\n\tif (ret == false)\n\t{\n\t\tfeLogErrorEx(m_opt.GetFEModel(), \"Failed to initialize the optimization data.\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOptimize::Run()\n{\n\tTimer timer(false);\n\ttimer.start();\n\n\t// solve the problem\n\tbool bret = m_opt.Solve();\n\n\ttimer.stop();\n\tdouble elapsedTime = timer.GetTime();\n\tchar sztime[64];\n\tTimer::time_str(elapsedTime, sztime);\n\tfeLog(\"\\n\\tTotal elapsed time : %s (%lg sec)\\n\\n\", sztime, elapsedTime);\n\n\tif (bret)\n\t\tfeLog(\"\\n\\n N O R M A L   T E R M I N A T I O N\\n\\n\");\n\telse \n\t\tfeLog(\"\\n\\n E R R O R   T E R M I N A T I O N\\n\\n\");\n\n\treturn bret;\n}\n"
  },
  {
    "path": "FEBioOpt/FEOptimize.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FECoreTask.h\"\n#include \"FEOptimizeData.h\"\n\n//-----------------------------------------------------------------------------\n// This class defines the FEOptimize task. It allows FEBio to call the optimization\n// module to solve a parameter optimization problem.\nclass FEOptimize : public FECoreTask\n{\npublic:\n\t//! class constructor\n\tFEOptimize(FEModel* pfem);\n\n\t//! initialization\n\tbool Init(const char* szfile);\n\n\t//! Run the optimization module\n\tbool Run();\n\nprivate:\n\tFEOptimizeData\tm_opt;\n};\n"
  },
  {
    "path": "FEBioOpt/FEOptimizeData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOptimizeData.h\"\n#include \"FELMOptimizeMethod.h\"\n#include \"FEOptimizeInput.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEModelParameter::FEModelParameter(FEModel* fem) : FEInputParameter()\n{\n\tm_fem = fem;\n\tm_pd = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModelParameter::Init()\n{\n\t// find the variable\n\tFEModel& fem = *GetFEModel();\n\tstring name = GetName();\n\tFEParamValue val = fem.GetParameterValue(ParamString(name.c_str()));\n\n\t// see if we found the parameter\n\tif (val.isValid() == false)\n\t{\n\t\tfeLogError(\"Cannot find parameter %s\", name.c_str());\n\t\treturn false;\n\t}\n\n\t// Make sure it's a double\n\tif (val.type() == FE_PARAM_DOUBLE)\n\t{\n\t\t// make sure we have a valid data pointer\n\t\tdouble* pd = (double*)val.data_ptr();\n\t\tif (pd == 0)\n\t\t{\n\t\t\tfeLogError(\"Invalid data pointer for parameter %s\", name.c_str());\n\t\t\treturn false;\n\t\t}\n\n\t\t// store the pointer to the parameter\n\t\tm_pd = pd;\n\t}\n\telse\n\t{\n\t\tfeLogError(\"Invalid parameter type for parameter %s\", name.c_str());\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEModelParameter::GetValue()\n{\n\tif (m_pd) return *m_pd;\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModelParameter::SetValue(double newValue)\n{\n\tif (m_pd == 0) return false;\n\t(*m_pd) = newValue;\n\treturn true;\n}\n\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEOptimizeData::FEOptimizeData(FEModel* fem) : m_fem(fem)\n{\n\tm_pSolver = 0;\n\tm_pTask = 0;\n\tm_niter = 0;\n\tm_obj = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEOptimizeData::~FEOptimizeData(void)\n{\n\tdelete m_pSolver;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOptimizeData::Init()\n{\n\t// allocate default optimization solver if none specified in input file\n\tif (m_pSolver == 0) m_pSolver = new FELMOptimizeMethod(GetFEModel());\n\n\t// allocate default solver if none specified in input file\n\tif (m_pTask == 0) m_pTask = fecore_new<FECoreTask>(\"solve\", m_fem);\n\n\t// don't plot anything\n\tfor (int i = 0; i < m_fem->Steps(); ++i)\n\t{\n\t\tm_fem->GetStep(i)->SetPlotLevel(FE_PLOT_NEVER);\n\t}\n\n\t// do the initialization of the task\n\tGetFEModel()->BlockLog();\n\tif (m_pTask->Init(0) == false) return false;\n\tGetFEModel()->UnBlockLog();\n\n\t// initialize all input parameters\n\tfor (int i=0; i<(int)m_Var.size(); ++i)\n\t{\n\t\tFEInputParameter* p = m_Var[i];\n\t\tif ((p==0) || (p->Init() == false)) return false;\n\n\t\t// set the initial value\n\t\tp->SetValue(p->InitValue());\n\t}\n\n\t// initialize the objective function\n\tif (m_obj == 0) return false;\n\tif (m_obj->Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOptimizeData::Solve()\n{\n\t// make sure we have a task that will solve the FE model\n\tif (m_pTask == 0) return false;\n\n\t// go for it!\n\tint NVAR = (int) m_Var.size();\n\tvector<double> amin(NVAR, 0.0);\n\tvector<double> ymin;\n\tdouble minObj = 0.0;\n\tbool bret = m_pSolver->Solve(this, amin, ymin, &minObj);\n\tif (bret)\n\t{\n\t\tfeLog(\"\\nP A R A M E T E R   O P T I M I Z A T I O N   R E S U L T S\\n\\n\");\n\n\t\tvector<double> xmin(ymin.size(), 0);\n\t\tfor (int i = 0; i < (int)ymin.size(); ++i) xmin[i] = i + 1;\n\t\tm_obj->GetXValues(xmin);\n\n\t\tfeLog(\"\\tFunction values:\\n\");\n\t\tfeLog(\"              X            F(X)\\n\");\n\t\tfor (int i=0; i<(int) ymin.size(); ++i)\n\t\t\tfeLog(\"%15lg %15lg\\n\", xmin[i], ymin[i]);\n\n\t\t// evaluate final regression coefficient\n\t\tvector<double> y0;\n\t\tm_obj->GetMeasurements(y0);\n\t\tdouble minR2 = m_obj->RegressionCoefficient(y0, ymin);\n\n\t\tfeLog(\"\\n\\tTotal iterations ........ : %15d\\n\\n\", m_niter);\n\t\tfeLog(\"\\tFinal objective value ... : %15lg\\n\\n\", minObj);\n        feLog(\"\\tFinal regression coef ... : %15lg\\n\\n\", minR2);\n\t\tfeLog(\"\\tOptimal parameters:\\n\\n\");\n\t\t// report the parameters for the minimal value\n\t\tfor (int i = 0; i<NVAR; ++i)\n\t\t{\n\t\t\tFEInputParameter& var = *GetInputParameter(i);\n\t\t\tstring name = var.GetName();\n\t\t\tfeLog(\"\\t\\t%-15s = %.16lg\\n\", name.c_str(), amin[i]);\n\t\t}\n\t}\n\n\treturn bret;\n}\n\n//-----------------------------------------------------------------------------\n//! Read the data from the input file\n//!\nbool FEOptimizeData::Input(const char *szfile)\n{\n\tFEOptimizeInput in;\n\tif (in.Input(szfile, this) == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOptimizeData::RunTask()\n{\n\tif (m_pTask == 0) return false;\n\treturn m_pTask->Run();\n}\n\n//-----------------------------------------------------------------------------\n//! solve the FE problem with a new set of parameters\nbool FEOptimizeData::FESolve(const vector<double>& a)\n{\n\t// increase iterator counter\n\tm_niter++;\n\n\t// reset objective function data\n\tFEObjectiveFunction& obj = GetObjective();\n\tobj.Reset();\n\n\t// set the input parameters\n\tint nvar = InputParameters();\n\tif (nvar != (int)a.size()) return false;\n\tfor (int i = 0; i<nvar; ++i)\n\t{\n\t\tFEInputParameter& var = *GetInputParameter(i);\n\t\tvar.SetValue(a[i]);\n\t}\n\n\t// report the new values\n\tfeLog(\"\\n----- Iteration: %d -----\\n\", m_niter);\n\tfor (int i = 0; i<nvar; ++i)\n\t{\n\t\tFEInputParameter& var = *GetInputParameter(i);\n\t\tstring name = var.GetName();\n\t\tfeLog(\"%-15s = %lg\\n\", name.c_str(), var.GetValue());\n\t}\n\n\t// reset the FEM data\n\tFEModel& fem = *GetFEModel();\n\tfem.BlockLog();\n\tfem.Reset();\n\tfem.UnBlockLog();\n\n\t// solve the FE problem\n\tfem.BlockLog();\n\tbool bret = RunTask();\n\tfem.UnBlockLog();\n\n\treturn bret;\n}\n"
  },
  {
    "path": "FEBioOpt/FEOptimizeData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEModel.h>\n#include <FECore/FECoreTask.h>\n#include \"FEObjectiveFunction.h\"\n#include <vector>\n#include <string>\n\n//-----------------------------------------------------------------------------\nclass FEOptimizeMethod;\n\n//-----------------------------------------------------------------------------\n//! This class represents an input parameter. Input parameters define the parameter \n//! space that will be searched for the optimal parameter value.\nclass FEInputParameter\n{\npublic:\n\tFEInputParameter() { m_min = -1e99; m_max = 1e99; m_scale = 1.0; m_initVal = 0.0; }\n\tvirtual ~FEInputParameter() {}\n\n\t// implement this to initialize the input parameter\n\tvirtual bool Init() { return true; }\n\n\t//! return the current value of the input parameter\n\tvirtual double GetValue() = 0;\n\n\t//! set the current value of the input parameter.\n\t//! should return false is the passed value is invalid\n\tvirtual bool SetValue(double newValue) = 0;\n\n\t//! get/set the initial value\n\tdouble& InitValue() { return m_initVal; }\n\n\t//! get/set the min value\n\tdouble& MinValue() { return m_min; }\n\n\t//! get/set the max value\n\tdouble& MaxValue() { return m_max; }\n\n\t//! get/set scale factor\n\tdouble& ScaleFactor() { return m_scale; }\n\n\t//! set the name\n\tvoid SetName(const string& name) { m_name = name; }\n\n\t//! get the name\n\tstring GetName() { return m_name; }\n\nprivate:\n\tstring\t\tm_name;\t\t\t//!< name of input parameter\n\tdouble\t\tm_initVal;\t\t//!< initial value\n\tdouble\t\tm_min, m_max;\t//!< min, max values for parameter\n\tdouble\t\tm_scale;\t\t//!< scale factor\n};\n\n//-----------------------------------------------------------------------------\n// Class for choosing model parameters as input parameters\nclass FEModelParameter : public FEInputParameter\n{\npublic:\n\tFEModelParameter(FEModel* fem);\n\n\t//! Initialize\n\tbool Init();\n\n\tFEModel* GetFEModel() { return m_fem; }\n\npublic:\n\t//! return the current value of the input parameter\n\tdouble GetValue();\n\n\t//! set the current value of the input parameter.\n\t//! should return false is the passed value is invalid\n\tbool SetValue(double newValue);\n\nprivate:\n\tdouble*\tm_pd;\t\t//!< pointer to variable data\n\tdouble\tm_val;\t\t//!< value\n\tFEModel* m_fem;\t\t//!< pointer to model data\n};\n\n//=============================================================================\n#define OPT_MAX_VAR 64\nstruct OPT_LIN_CONSTRAINT\n{\n\tdouble\ta[OPT_MAX_VAR];\n\tdouble\tb;\n};\n\n//=============================================================================\n//! optimization analyses\n//! \nclass FEOptimizeData\n{\npublic:\n\t//! constructor\n\tFEOptimizeData(FEModel* fem);\n\t~FEOptimizeData(void);\n\n\t//! input function\n\tbool Input(const char* sz);\n\n\t//! Initialize data\n\tbool Init();\n\n\t//! solver the problem\n\tbool Solve();\n\n\t//! return the FE Model\n\tFEModel* GetFEModel() { return m_fem; }\n\n\t//! solve the FE problem with a new set of parameters\n\tbool FESolve(const std::vector<double>& a);\n\npublic:\n\t// return the number of input parameters\n\tint InputParameters() { return (int)m_Var.size(); }\n\n\t//! add an input parameter to optimize\n\tvoid AddInputParameter(FEInputParameter* var) { m_Var.push_back(var); }\n\n\t//! return an input parameter\n\tFEInputParameter* GetInputParameter(int n) { return m_Var[n]; }\n\npublic:\n\t//! add a linear constraint\n\tvoid AddLinearConstraint(OPT_LIN_CONSTRAINT& con) { m_LinCon.push_back(con); }\n\n\t//! return number of constraints\n\tint Constraints() { return (int) m_LinCon.size(); }\n\n\t//! return a linear constraint\n\tOPT_LIN_CONSTRAINT& Constraint(int i) { return m_LinCon[i]; }\n\n\tFEObjectiveFunction& GetObjective() { return *m_obj; }\n\n\tvoid SetObjective(FEObjectiveFunction* obj) { m_obj = obj; }\n\n\tvoid SetSolver(FEOptimizeMethod* po) { m_pSolver = po; }\n\n\tFEOptimizeMethod* GetSolver() { return m_pSolver; }\n\n\tbool RunTask();\n\npublic:\n\tint\tm_niter;\t// nr of minor iterations (i.e. FE solves)\n\n\tFECoreTask* m_pTask;\t// the task that will solve the FE model\n\nprotected:\n\tFEModel*\tm_fem;\n\n\tFEObjectiveFunction*\tm_obj;\t\t//!< the objective function\n\n\tFEOptimizeMethod*\tm_pSolver;\n\n\tstd::vector<FEInputParameter*>\t    m_Var;\n\tstd::vector<OPT_LIN_CONSTRAINT>\t\tm_LinCon;\n};\n"
  },
  {
    "path": "FEBioOpt/FEOptimizeInput.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOptimizeData.h\"\n#include \"FELMOptimizeMethod.h\"\n#include \"FEPowellOptimizeMethod.h\"\n#include \"FEScanOptimizeMethod.h\"\n#include \"FEConstrainedLMOptimizeMethod.h\"\n#include \"FEOptimizeInput.h\"\n#include <FECore/log.h>\n#include <FEBioXML/xmltool.h>\n#include <FECore/FELogElemMath.h>\n#include <FECore/FECoreKernel.h>\n\n//=============================================================================\n// FEOptimizeInput\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! Read the data from the xml input file\n//!\nbool FEOptimizeInput::Input(const char* szfile, FEOptimizeData* pOpt)\n{\n\t// try to open the file\n\tXMLReader xml;\n\tif (xml.Open(szfile) == false)\n\t{\n\t\tfprintf(stderr, \"\\nFATAL ERROR: Failed to load file %s\\n\", szfile);\n\t\treturn false;\n\t}\n\n\t// find the root element\n\tXMLTag tag;\n\tif (xml.FindTag(\"febio_optimize\", tag) == false) return false;\n\n\t// check for the version attribute\n\tconst char* szversion = tag.AttributeValue(\"version\", true);\n\tif ((szversion == 0) || (strcmp(szversion, \"2.0\") != 0))\n\t{\n\t\tfprintf(stderr, \"\\nFATAL ERROR: Invalid version number for febio_optimize!\\n\\n\");\n\t\treturn false;\n\t}\n\n\tm_opt = pOpt;\n\n\t// process the file\n\tbool ret = true;\n\ttry {\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif      (tag == \"Task\"       ) ParseTask(tag);\n\t\t\telse if (tag == \"Options\"    ) ParseOptions(tag);\n\t\t\telse if (tag == \"Parameters\" ) ParseParameters(tag);\n\t\t\telse if (tag == \"Constraints\") ParseConstraints(tag);\n\t\t\telse if (tag == \"Objective\"  ) ParseObjective(tag);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\tcatch (...)\n\t{\n\t\tfprintf(stderr, \"Fatal exception while reading optimization input file.\\n\");\n\t\tret = false;\n\t}\n\n\t// all done\n\txml.Close();\n\n\treturn ret;\n}\n\n//-------------------------------------------------------------------------------------------------\nvoid FEOptimizeInput::ParseTask(XMLTag& tag)\n{\n\tm_opt->m_pTask = fecore_new<FECoreTask>(tag.szvalue(), m_opt->GetFEModel());\n\tif (m_opt->m_pTask == nullptr) throw XMLReader::InvalidValue(tag);\n}\n\n\n//-------------------------------------------------------------------------------------------------\nvoid FEOptimizeInput::ParseOptions(XMLTag& tag)\n{\n\tFEModel* fem = m_opt->GetFEModel();\n\n\tconst char* szt = tag.AttributeValue(\"type\", true);\n\tif (szt == 0) szt = \"levmar\";\n\tFEOptimizeMethod* popt = fecore_new< FEOptimizeMethod>(szt, fem);\n\tif (popt == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\n\t// get the parameter list\n\tFEParameterList& pl = popt->GetParameterList();\n\n\tif (!tag.isleaf())\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (fexml::readParameter(tag, pl) == false)\n\t\t\t{\n\t\t\t\tif (tag == \"log_level\")\n\t\t\t\t{\n\t\t\t\t\tchar szval[256];\n\t\t\t\t\ttag.value(szval);\n\t\t\t\t\tif (strcmp(szval, \"LOG_DEFAULT\") == 0) {} // don't change the plot level\n\t\t\t\t\telse if (strcmp(szval, \"LOG_NEVER\") == 0) popt->m_loglevel = LogLevel::LOG_NEVER;\n\t\t\t\t\telse if (strcmp(szval, \"LOG_FILE_ONLY\") == 0) popt->m_loglevel = LogLevel::LOG_FILE;\n\t\t\t\t\telse if (strcmp(szval, \"LOG_SCREEN_ONLY\") == 0) popt->m_loglevel = LogLevel::LOG_SCREEN;\n\t\t\t\t\telse if (strcmp(szval, \"LOG_FILE_AND_SCREEN\") == 0) popt->m_loglevel = LogLevel::LOG_FILE_AND_SCREEN;\n\t\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t\telse if (tag == \"print_level\")\n\t\t\t\t{\n\t\t\t\t\tchar szval[256];\n\t\t\t\t\ttag.value(szval);\n\t\t\t\t\tif      (strcmp(szval, \"PRINT_ITERATIONS\") == 0) popt->m_print_level = PRINT_ITERATIONS;\n\t\t\t\t\telse if (strcmp(szval, \"PRINT_VERBOSE\"   ) == 0) popt->m_print_level = PRINT_VERBOSE;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint print_level = atoi(szval);\n\t\t\t\t\t\tif ((print_level == PRINT_ITERATIONS) || (print_level == PRINT_VERBOSE))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpopt->m_print_level = print_level;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\n\tm_opt->SetSolver(popt);\n}\n\n\n//-------------------------------------------------------------------------------------------------\nvoid FEOptimizeInput::ParseObjective(XMLTag& tag)\n{\n\tFEModel& fem = *m_opt->GetFEModel();\n\n\t// get the type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tif      (strcmp(sztype, \"data-fit\"    ) == 0) ParseObjectiveDataFit(tag);\n\telse if (strcmp(sztype, \"target\"      ) == 0) ParseObjectiveTarget(tag);\n\telse if (strcmp(sztype, \"element-data\") == 0) ParseObjectiveElementData(tag);\n\telse if (strcmp(sztype, \"node-data\"   ) == 0) ParseObjectiveNodeData(tag);\n\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\tFEOptimizeMethod* solver = m_opt->GetSolver();\n\tif (solver)\n\t{\n\t\tFEObjectiveFunction& obj = m_opt->GetObjective();\n\t\tif (solver->m_print_level == PRINT_ITERATIONS) obj.SetVerbose(false);\n\t\telse obj.SetVerbose(true);\n\t}\n}\n\nvoid FEOptimizeInput::ParseObjectiveDataFit(XMLTag& tag)\n{\n\tFEModel& fem = *m_opt->GetFEModel();\n\tFEDataFitObjective* obj = new FEDataFitObjective(&fem);\n\tm_opt->SetObjective(obj);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"fnc\")\n\t\t{\n\t\t\tFEDataSource* src = ParseDataSource(tag);\n\t\t\tobj->SetDataSource(src);\n\t\t}\n\t\telse if (tag == \"data\")\n\t\t{\n\t\t\tvector<pair<double, double> > data;\n\n\t\t\t// see if the user wants to read the data from a text file\n\t\t\tconst char* szf = tag.AttributeValue(\"import\", true);\n\t\t\tif (szf)\n\t\t\t{\n\t\t\t\t// make sure this tag is a leaf\n\t\t\t\tif ((tag.isempty() == false) || (tag.isleaf() == false)) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t// read the data form a text file\n\t\t\t\tFILE* fp = fopen(szf, \"rt\");\n\t\t\t\tif (fp == 0) throw XMLReader::InvalidAttributeValue(tag, \"import\", szf);\n\t\t\t\tchar szline[256] = { 0 };\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tfgets(szline, 255, fp);\n\t\t\t\t\tdouble t, v;\n\t\t\t\t\tint n = sscanf(szline, \"%lg%lg\", &t, &v);\n\t\t\t\t\tif (n == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tpair<double, double> pt(t, v);\n\t\t\t\t\t\tdata.push_back(pt);\n\t\t\t\t\t}\n\t\t\t\t\telse break;\n\t\t\t\t} while ((feof(fp) == 0) && (ferror(fp) == 0));\n\n\t\t\t\tfclose(fp);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble v[2] = { 0 };\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\ttag.value(v, 2);\n\t\t\t\t\tpair<double, double> p(v[0], v[1]);\n\t\t\t\t\tdata.push_back(p);\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\t\t\t}\n\n\t\t\tobj->SetMeasurements(data);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEOptimizeInput::ParseObjectiveTarget(XMLTag& tag)\n{\n\tFEModel& fem = *m_opt->GetFEModel();\n\tFEMinimizeObjective* obj = new FEMinimizeObjective(&fem);\n\tm_opt->SetObjective(obj);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"var\")\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tdouble trg;\n\t\t\ttag.value(trg);\n\t\t\tobj->AddFunction(new FEMinimizeObjective::ParamFunction(&fem, szname, trg));\n\t\t}\n\t\telse if (tag == \"fnc\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\t\t\tif (strcmp(sztype, \"filter_avg\") == 0)\n\t\t\t{\n\t\t\t\tFEElementSet* elset = nullptr;\n\t\t\t\tFELogElemData* pdata = nullptr;\n\t\t\t\tdouble target = 0;\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"elem_data\")\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char* szdata = tag.AttributeValue(\"data\");\n\t\t\t\t\t\tconst char* szset = tag.AttributeValue(\"elem_set\");\n\n\t\t\t\t\t\tFEMesh& mesh = fem.GetMesh();\n\t\t\t\t\t\telset = mesh.FindElementSet(szset);\n\t\t\t\t\t\tif (elset == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\");\n\n\t\t\t\t\t\t// try to allocate the element data record\n\t\t\t\t\t\tif (szdata && (szdata[0] == '='))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFELogElemMath* logMath = fecore_alloc(FELogElemMath, &fem);\n\t\t\t\t\t\t\tif (logMath)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstring smath(szdata + 1);\n\t\t\t\t\t\t\t\tif (logMath->SetExpression(smath))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpdata = logMath;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpdata = fecore_new<FELogElemData>(szdata, &fem);\n\n\t\t\t\t\t\tif (pdata == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"data\");\n\t\t\t\t\t}\n\t\t\t\t\telse if (tag == \"target\") tag.value(target);\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\n\t\t\t\tobj->AddFunction(new FEMinimizeObjective::FilterAvgFunction(&fem, pdata, elset, target));\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\");\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEOptimizeInput::ParseObjectiveElementData(XMLTag& tag)\n{\n\tFEModel& fem = *m_opt->GetFEModel();\n\tFEElementDataTable* obj = new FEElementDataTable(&fem);\n\tm_opt->SetObjective(obj);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"var\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\t// try to allocate the element data record\n\t\t\tFELogElemData* var = nullptr;\n\t\t\tif (sztype && (sztype[0]=='='))\n\t\t\t{\n\t\t\t\tFELogElemMath* logMath = fecore_alloc(FELogElemMath, &fem);\n\t\t\t\tif (logMath)\n\t\t\t\t{\n\t\t\t\t\tstring smath(sztype + 1);\n\t\t\t\t\tif (logMath->SetExpression(smath))\n\t\t\t\t\t{\n\t\t\t\t\t\tvar = logMath;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tvar = fecore_new<FELogElemData>(sztype, &fem);\n\n\t\t\tif (var == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\tobj->SetVariable(var);\n\t\t}\n\t\telse if (tag == \"data\")\n\t\t{\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"elem\")\n\t\t\t\t{\n\t\t\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\t\t\tint nid = atoi(szid);\n\t\t\t\t\tdouble v = 0.0;\n\t\t\t\t\ttag.value(v);\n\n\t\t\t\t\tobj->AddValue(nid, v);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEOptimizeInput::ParseObjectiveNodeData(XMLTag& tag)\n{\n\tFEModel& fem = *m_opt->GetFEModel();\n\tFENodeDataTable* obj = new FENodeDataTable(&fem);\n\tm_opt->SetObjective(obj);\n\n\tint nvars = 0;\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"var\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\tchar buf[256] = { 0 };\n\t\t\tstrcpy(buf, sztype);\n\t\t\tchar* ch = buf;\n\t\t\tchar* sz = buf;\n\t\t\twhile (*sz)\n\t\t\t{\n\t\t\t\tif ((*ch == 0) || (*ch == ';'))\n\t\t\t\t{\n\t\t\t\t\tchar ch2 = *ch;\n\t\t\t\t\t*ch = 0;\n\n\t\t\t\t\t// try to allocate the element data record\n\t\t\t\t\tFELogNodeData* var = fecore_new<FELogNodeData>(sz, &fem);\n\t\t\t\t\tif (var == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\t\tobj->AddVariable(var);\n\t\t\t\t\tnvars++;\n\n\t\t\t\t\tif (ch2 != 0) ch++;\n\t\t\t\t\tsz = ch;\n\t\t\t\t}\n\t\t\t\telse ch++;\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"data\")\n\t\t{\n\t\t\tif (nvars == 0)\n\t\t\t{\n\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t}\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"node\")\n\t\t\t\t{\n\t\t\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\t\t\tint nid = atoi(szid);\n\t\t\t\t\tvector<double> v(nvars, 0.0);\n\t\t\t\t\tint nread = tag.value(&v[0], nvars);\n\t\t\t\t\tif (nread != nvars) throw XMLReader::InvalidValue(tag);\n\t\t\t\t\tobj->AddValue(nid, v);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nFEDataSource* FEOptimizeInput::ParseDataSource(XMLTag& tag)\n{\n\tFEOptimizeData& opt = *m_opt;\n\n\tFEModel& fem = *opt.GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tif (strcmp(sztype, \"parameter\") == 0)\n\t{\n\t\tFEDataParameter* src = new FEDataParameter(&fem);\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"param\")\n\t\t\t{\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\t\tsrc->SetParameterName(szname);\n\t\t\t}\n\t\t\telse if (tag == \"ordinate\")\n\t\t\t{\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\t\tsrc->SetOrdinateName(szname);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\n\t\treturn src;\n\t}\n\telse if (strcmp(sztype, \"filter_positive_only\") == 0)\n\t{\n\t\tFEDataFilterPositive* src = new FEDataFilterPositive(&fem);\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"source\")\n\t\t\t{\n\t\t\t\tFEDataSource* s = ParseDataSource(tag);\n\t\t\t\tsrc->SetDataSource(s);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\n\t\treturn src;\n\t}\n\telse if (strcmp(sztype, \"filter_sum\") == 0)\n\t{\n\t\tFEDataSource* flt = nullptr;\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"node_data\")\n\t\t\t{\n\t\t\t\tconst char* szdata = tag.AttributeValue(\"data\");\n\t\t\t\tconst char* szset = tag.AttributeValue(\"node_set\");\n\n\t\t\t\tFENodeSet* nodeSet = mesh.FindNodeSet(szset);\n\t\t\t\tif (nodeSet == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t\t\t\tFELogNodeData* nodeData = fecore_new<FELogNodeData>(szdata, &fem);\n\t\t\t\tif (nodeData == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"data\", szdata);\n\n\t\t\t\tFENodeDataFilterSum* dataFlt = new FENodeDataFilterSum(&fem);\n\t\t\t\tdataFlt->SetData(nodeData, nodeSet);\n\t\t\t\tflt = dataFlt;\n\t\t\t}\n\t\t\telse if (tag == \"element_data\")\n\t\t\t{\n\t\t\t\tconst char* szdata = tag.AttributeValue(\"data\");\n\t\t\t\tconst char* szset = tag.AttributeValue(\"elem_set\");\n\n\t\t\t\tFEElementSet* elemSet = mesh.FindElementSet(szset);\n\t\t\t\tif (elemSet == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szset);\n\n\t\t\t\tFELogElemData* elemData = nullptr;\n\t\t\t\tif (szdata && (szdata[0] == '='))\n\t\t\t\t{\n\t\t\t\t\tFELogElemMath* logMath = fecore_alloc(FELogElemMath, &fem);\n\t\t\t\t\tif (logMath)\n\t\t\t\t\t{\n\t\t\t\t\t\tstring smath(szdata + 1);\n\t\t\t\t\t\tif (logMath->SetExpression(smath))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\telemData = logMath;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\telemData = fecore_new<FELogElemData>(szdata, &fem);\n\n\t\t\t\tif (elemData == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"data\", szdata);\n\n\t\t\t\tFEElemDataFilterSum* dataFlt = new FEElemDataFilterSum(&fem);\n\t\t\t\tdataFlt->SetData(elemData, elemSet);\n\t\t\t\tflt = dataFlt;\n\t\t\t}\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\n\t\treturn flt;\n\t}\n\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// we shouldn't be here\n\tassert(false);\n\treturn 0;\n}\n\n//-------------------------------------------------------------------------------------------------\nvoid FEOptimizeInput::ParseParameters(XMLTag& tag)\n{\n\tFEModel& fem = *m_opt->GetFEModel();\n\n\t// read the parameters\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"param\")\n\t\t{\n\t\t\tFEModelParameter* var = new FEModelParameter(&fem);\n\n\t\t\t// get the variable name\n\t\t\tconst char* sz = tag.AttributeValue(\"name\");\n\t\t\tvar->SetName(sz);\n\n\t\t\t// set initial values and bounds\n\t\t\tdouble d[4] = { 0, 0, 0, 1 };\n\t\t\ttag.value(d, 4);\n\t\t\tvar->InitValue() = d[0];\n\t\t\tvar->MinValue() = d[1];\n\t\t\tvar->MaxValue() = d[2];\n\t\t\tvar->ScaleFactor() = d[3];\n\n\t\t\t// add the variable\n\t\t\tm_opt->AddInputParameter(var);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-------------------------------------------------------------------------------------------------\nvoid FEOptimizeInput::ParseConstraints(XMLTag& tag)\n{\n\tint NP = m_opt->InputParameters();\n\tif ((NP > OPT_MAX_VAR) || (NP < 2)) throw XMLReader::InvalidTag(tag);\n\n\tdouble v[OPT_MAX_VAR + 1];\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"constraint\")\n\t\t{\n\t\t\tint m = tag.value(v, OPT_MAX_VAR + 1);\n\t\t\tif (m != NP + 1) throw XMLReader::InvalidValue(tag);\n\n\t\t\tOPT_LIN_CONSTRAINT con;\n\t\t\tfor (int i = 0; i<NP; ++i) con.a[i] = v[i];\n\t\t\tcon.b = v[NP];\n\n\t\t\tm_opt->AddLinearConstraint(con);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioOpt/FEOptimizeInput.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FEBioXML/XMLReader.h>\n\n//-----------------------------------------------------------------------------\n//! FEBio error terminated during the optimization\nclass FEErrorTermination{};\n\n//-----------------------------------------------------------------------------\nclass FEOptimizeData;\n\n//=============================================================================\n//! Class that reads the optimization input file\nclass FEOptimizeInput\n{\npublic:\n\tbool Input(const char* szfile, FEOptimizeData* pOpt);\n\nprivate:\n\tvoid ParseTask(XMLTag& tag);\n\tvoid ParseOptions(XMLTag& tag);\n\tvoid ParseParameters(XMLTag& tag);\n\tvoid ParseConstraints(XMLTag& tag);\n\tvoid ParseObjective(XMLTag& tag);\n\n\tFEDataSource* ParseDataSource(XMLTag& tag);\n\nprivate:\n\tvoid ParseObjectiveDataFit(XMLTag& tag);\n\tvoid ParseObjectiveTarget(XMLTag& tag);\n\tvoid ParseObjectiveElementData(XMLTag& tag);\n\tvoid ParseObjectiveNodeData(XMLTag& tag);\n\nprivate:\n\tFEOptimizeData*\tm_opt;\n};\n"
  },
  {
    "path": "FEBioOpt/FEOptimizeMethod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEOptimizeMethod.h\"\n\nFEOptimizeMethod::FEOptimizeMethod(FEModel* fem) : FECoreClass(fem)\n{ \n\tm_loglevel = LogLevel::LOG_NEVER;\n\tm_print_level = PRINT_ITERATIONS;\n}\n"
  },
  {
    "path": "FEBioOpt/FEOptimizeMethod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreClass.h>\n\n//-----------------------------------------------------------------------------\nclass FEOptimizeData;\n\n//-----------------------------------------------------------------------------\nenum {\n\tPRINT_ITERATIONS,\n\tPRINT_VERBOSE\n};\n\n//-----------------------------------------------------------------------------\nenum LogLevel {\n\tLOG_NEVER,\n\tLOG_FILE,\n\tLOG_SCREEN,\n\tLOG_FILE_AND_SCREEN\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for optimization algorithms.\n//! Derived class implement specific optimization algorithms.\nclass FEOptimizeMethod : public FECoreClass\n{\n\tFECORE_BASE_CLASS(FEOptimizeMethod)\n\npublic:\n\tFEOptimizeMethod(FEModel* fem);\n\n\t// Implement this function for solve an optimization problem\n\t// should return the optimal values for the input parameters in a, the optimal\n\t// values of the measurement vector in ymin and\n\t// the corresponding objective value in obj.\n\t// If this function returns false, something went wrong\n\tvirtual bool Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj) = 0;\n\npublic:\n\tint\t\tm_loglevel;\t\t//!< log file output level\n\tint\t\tm_print_level;\t//!< level of detailed output\n};\n"
  },
  {
    "path": "FEBioOpt/FEParameterSweep.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEParameterSweep.h\"\n#include <FEBioXML/XMLReader.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n\nFESweepParam::FESweepParam()\n{\n\tm_min = m_max = m_step = 0.0;\n\tm_pd = nullptr;\n}\n\nFESweepParam::FESweepParam(const FESweepParam& p)\n{\n\tm_min = p.m_min;\n\tm_max = p.m_max;\n\tm_step = p.m_step;\n\tm_paramName = p.m_paramName;\n\tm_pd = p.m_pd;\n}\n\nvoid FESweepParam::operator = (const FESweepParam& p)\n{\n\tm_min = p.m_min;\n\tm_max = p.m_max;\n\tm_step = p.m_step;\n\tm_paramName = p.m_paramName;\n\tm_pd = p.m_pd;\n}\n\nvoid FESweepParam::SetValue(double v)\n{\n\t*m_pd = v;\n}\n\nFEParameterSweep::FEParameterSweep(FEModel* fem) : FECoreTask(fem)\n{\n\tm_niter = 0;\n}\n\n//! initialization\nbool FEParameterSweep::Init(const char* szfile)\n{\n\t// read the control file\n\tif (Input(szfile) == false) return false;\n\n\t// initialize the model\n\tif (GetFEModel()->Init() == false) return false;\n\n\t// check the parameters\n\tif (InitParams() == false) return false;\n\n\t// don't plot anything\n\tGetFEModel()->GetCurrentStep()->SetPlotHint(FE_PLOT_APPEND);\n\tGetFEModel()->GetCurrentStep()->SetPlotLevel(FE_PLOT_FINAL);\n\n\treturn true;\n}\n\nbool FEParameterSweep::InitParams()\n{\n\t// Make sure we have something to do\n\tif (m_params.empty()) return false;\n\n\t// check the parameters\n\tFEModel& fem = *GetFEModel();\n\tfor (size_t i = 0; i<m_params.size(); ++i)\n\t{\n\t\tFESweepParam& p = m_params[i];\n\n\t\t// find the variable\n\t\tstring name = p.m_paramName;\n\t\tFEParamValue val = fem.GetParameterValue(ParamString(name.c_str()));\n\n\t\t// see if we found the parameter\n\t\tif (val.isValid() == false)\n\t\t{\n\t\t\tfeLogError(\"Cannot find parameter %s\", name.c_str());\n\t\t\treturn false;\n\t\t}\n\n\t\t// see if it's the correct type\n\t\tif (val.type() != FE_PARAM_DOUBLE)\n\t\t{\n\t\t\tfeLogError(\"Invalid parameter type for parameter %s\", name.c_str());\n\t\t\treturn false;\n\t\t}\n\n\t\t// make sure we have a valid data pointer\n\t\tdouble* pd = (double*)val.data_ptr();\n\t\tif (pd == 0)\n\t\t{\n\t\t\tfeLogError(\"Invalid data pointer for parameter %s\", name.c_str());\n\t\t\treturn false;\n\t\t}\n\n\t\t// store the pointer to the parameter\n\t\tp.m_pd = pd;\n\t}\n\n\treturn true;\n}\n\nbool FEParameterSweep::Input(const char* szfile)\n{\n\t// open the xml file\n\tXMLReader xml;\n\tif (xml.Open(szfile) == false) return false;\n\n\t// find the root tag\n\tXMLTag tag;\n\tif (xml.FindTag(\"febio_sweep\", tag) == false) return false;\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"param\")\n\t\t{\n\t\t\tFESweepParam p;\n\n\t\t\t// read the parameter name\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tp.m_paramName = szname;\n\n\t\t\t// read the values\n\t\t\tdouble d[3];\n\t\t\tint n = tag.value(d, 3);\n\t\t\tif (n != 3) throw XMLReader::InvalidValue(tag);\n\n\t\t\t// some error checking\n\t\t\tp.m_min = d[0];\n\t\t\tp.m_max = d[1];\n\t\t\tp.m_step = d[2];\n\t\t\tif (p.m_max < p.m_min) throw XMLReader::InvalidValue(tag);\n\t\t\tif (p.m_step <= 0.0) throw XMLReader::InvalidValue(tag);\n\n\t\t\t// looks good, so throw it on the pile\n\t\t\tm_params.push_back(p);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// all done\n\txml.Close();\n\n\treturn true;\n}\n\n//! Run the optimization module\nbool FEParameterSweep::Run()\n{\n\tsize_t ma = m_params.size();\n\tvector<double> a(ma);\n\tfor (size_t i = 0; i<ma; ++i)\n\t{\n\t\tFESweepParam& pi = m_params[i];\n\t\ta[i] = pi.m_min;\n\t}\n\n\t// run the parameter sweep\n\tbool bdone = false;\n\tdo\n\t{\n\t\t// solve the problem with the new input parameters\n\t\tif (FESolve(a) == false) return false;\n\n\t\t// update indices\n\t\tfor (size_t i = 0; i<ma; ++i)\n\t\t{\n\t\t\tFESweepParam& pi = m_params[i];\n\t\t\ta[i] += pi.m_step;\n\t\t\tif (a[i] <= pi.m_max) break;\n\t\t\telse if (i<ma - 1) a[i] = pi.m_min;\n\t\t\telse { bdone = true; }\n\t\t}\n\t}\n\twhile (!bdone);\n\n\treturn true;\n}\n\nbool FEParameterSweep::FESolve(const vector<double>& a)\n{\n\t++m_niter;\n\tfeLog(\"\\n----- Iteration: %d -----\\n\", m_niter);\n\n\t// set the input parameters\n\tsize_t nvar = m_params.size();\n\tassert(nvar == a.size());\n\tfor (int i = 0; i<nvar; ++i)\n\t{\n\t\tFESweepParam& var = m_params[i];\n\t\tvar.SetValue(a[i]);\n\n\t\tstring name = var.m_paramName;\n\t\tfeLog(\"%-15s = %lg\\n\", name.c_str(), a[i]);\n\t}\n\n\t// reset the FEM data\n\tFEModel& fem = *GetFEModel();\n\tfem.BlockLog();\n\n\t// reset model\n\tfem.Reset();\n\n\t// solve the FE problem\n\tbool bret = fem.Solve();\n\n\tfem.UnBlockLog();\n\n\treturn bret;\n}\n"
  },
  {
    "path": "FEBioOpt/FEParameterSweep.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/FECoreTask.h>\n\n// This class represents a parameter that will be swept\nclass FESweepParam\n{\npublic:\n\tFESweepParam();\n\tFESweepParam(const FESweepParam& p);\n\tvoid operator = (const FESweepParam& p);\n\n\tvoid SetValue(double v);\n\npublic:\n\tstring\tm_paramName;\n\tdouble\tm_min, m_max, m_step;\n\tdouble*\tm_pd;\n};\n\n// This task implements a parameter sweep, where the same model is run similar times,\n// each time with one or more parameters modified.\nclass FEParameterSweep : public FECoreTask\n{\npublic:\n\tFEParameterSweep(FEModel* fem);\n\n\t//! initialization\n\tbool Init(const char* szfile) override;\n\n\t//! Run the optimization module\n\tbool Run() override;\n\nprivate:\n\tbool Input(const char* szfile);\n\tbool InitParams();\n\tbool FESolve(const vector<double>& a);\n\nprivate:\n\tvector<FESweepParam>\tm_params;\n\tint\t\t\t\t\t\tm_niter;\n};\n"
  },
  {
    "path": "FEBioOpt/FEPowellOptimizeMethod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPowellOptimizeMethod.h\"\n#include \"FEOptimizeData.h\"\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/tools.h>\n\nFEPowellOptimizeMethod* FEPowellOptimizeMethod::m_pThis = 0;\n\nFEPowellOptimizeMethod::FEPowellOptimizeMethod(FEModel* fem) : FEOptimizeMethod(fem)\n{\n\n}\n\nbool FEPowellOptimizeMethod::Solve(FEOptimizeData *pOpt, vector<double>& amin, vector<double>& ymin, double* minObj)\n{\n\tm_pOpt = pOpt;\n\tFEOptimizeData& opt = *pOpt;\n\n\tint nvar = opt.InputParameters();\n\n\t// set the initial guess\n\tvector<double> p(nvar);\n\tfor (int i=0; i<nvar; ++i) p[i] = opt.GetInputParameter(i)->GetValue();\n\n\t// set the initial search directions\n\tvector<double> xi(nvar*nvar);\n\tfor (int i=0; i<nvar; ++i)\n\t{\n\t\tfor (int j=0; j<nvar; ++j) xi[i*nvar + j] = 0;\n\t\txi[i*nvar + i] = 0.05*(1.0 + p[i]);\n\t}\n\n\t// don't forget to set this\n\tm_pThis = this;\n\n\t// call the powell routine\n\tint niter = 0;\n\tdouble fret = 0;\n\tpowell(&p[0], &xi[0], nvar, 0.001, &niter, &fret, objfun);\n\n\t// store optimal values\n\tamin = p;\n\tif (minObj) *minObj = fret;\n\n\treturn true;\n}\n\n//------------------------------------------------------------------\n\ndouble FEPowellOptimizeMethod::ObjFun(double *p)\n{\n\t// get the optimization data\n\tFEOptimizeData& opt = *m_pOpt;\n\tFEModel* fem = opt.GetFEModel();\n\n\t// set the input parameters\n\tint nvar = opt.InputParameters();\n\tvector<double> a(nvar);\n\tfor (int i=0; i<nvar; ++i) a[i] = p[i];\n\n\t// solve the FE problem with the new parameters\n\tif (opt.FESolve(a) == false)\n\t{\n\t\tfeLogEx(fem, \"\\n\\n\\nAAAAAAAAARRRRRRRRRGGGGGGGGHHHHHHHHHHH !!!!!!!!!!!!!\\n\\n\\n\\n\");\n\t\treturn 0;\n\t}\n\telse\n\t{\n\t\t// evaluate objective function\n\t\tFEObjectiveFunction& obj = opt.GetObjective();\n\t\tdouble fobj = obj.Evaluate();\n\t\treturn fobj;\n\t}\n}\n"
  },
  {
    "path": "FEBioOpt/FEPowellOptimizeMethod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEOptimizeMethod.h\"\n\n//----------------------------------------------------------------------------\n//! Optimization method using Powell's method\nclass FEPowellOptimizeMethod : public FEOptimizeMethod\n{\npublic:\n\tFEPowellOptimizeMethod(FEModel* fem);\n\n\tbool Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj);\n\nprotected:\n\tdouble ObjFun(double* p);\n\n\tFEOptimizeData*\tm_pOpt;\n\t\n\tstatic FEPowellOptimizeMethod*\tm_pThis;\n\tstatic double objfun(double* p) { return (m_pThis)->ObjFun(p); }\n};\n"
  },
  {
    "path": "FEBioOpt/FEScanOptimizeMethod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEScanOptimizeMethod.h\"\n#include \"FEOptimizeData.h\"\n#include \"FECore/log.h\"\n\nBEGIN_FECORE_CLASS(FEScanOptimizeMethod, FEOptimizeMethod)\n\nEND_FECORE_CLASS();\n\nFEScanOptimizeMethod::FEScanOptimizeMethod(FEModel* fem) : FEOptimizeMethod(fem)\n{\n\n}\n\nbool FEScanOptimizeMethod::Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj)\n{\n\tif (pOpt == 0) return false;\n\tFEOptimizeData& opt = *pOpt;\n\tFEObjectiveFunction& obj = opt.GetObjective();\n\n\t// set the intial values for the variables\n\tint ma = opt.InputParameters();\n\tvector<double> a(ma);\n\tfor (int i=0; i<ma; ++i)\n\t{\n\t\tFEInputParameter* var = opt.GetInputParameter(i);\n\t\ta[i] = var->MinValue();\n\t}\n\n\t// loop until done\n\tvector<double> y(ma, 0.0);\n\tbool bdone = false;\n\tdouble fmin = 0.0;\n\tdo\n\t{\n\t\t// solve the problem with the new input parameters\n\t\tif (opt.FESolve(a) == false) return false;\n\n\t\t// calculate objective function\n\t\tdouble fobj = obj.Evaluate(y);\n\n\t\t// update minimum\n\t\tif ((fmin == 0.0) || (fobj < fmin))\n\t\t{\n\t\t\tfmin = fobj;\n\t\t\tamin = a;\n\t\t\tymin = y;\n\t\t}\n\n\t\t// update indices\n\t\tfor (int i=0; i<ma; ++i)\n\t\t{\n\t\t\tFEInputParameter& vi = *opt.GetInputParameter(i);\n\t\t\ta[i] += vi.ScaleFactor();\n\t\t\tif (a[i] <= vi.MaxValue()) break;\n\t\t\telse if (i<ma-1) a[i] = vi.MinValue();\n\t\t\telse { bdone = true; }\n\t\t}\n\t}\n\twhile (!bdone);\n\n\t// store the optimum data\n\tif (minObj) *minObj = fmin;\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioOpt/FEScanOptimizeMethod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEOptimizeMethod.h\"\n\n//----------------------------------------------------------------------------\n//! Basic method that scans the parameter space for a minimum.\nclass FEScanOptimizeMethod : public FEOptimizeMethod\n{\npublic:\n\tFEScanOptimizeMethod(FEModel* fem);\n\n\t// this implements the solution algorithm.\n\t// returns the optimal parameter values in amin\n\t// returns the optimal measurement vector in ymin\n\t// returns the optimal objective function value in minObj\n\tbool Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj) override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioOpt/NLOptOptimizeMethod.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"NLOptOptimizeMethod.h\"\n#ifdef HAVE_NLOPT\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FESolver.h>\n#include \"FEOptimizeData.h\"\n#include \"FEOptimizeInput.h\" // for FEErrorTermination\n#include <nlopt.h>\n#endif\n\nBEGIN_FECORE_CLASS(NLOptOptimizeMethod, FEOptimizeMethod)\n\tADD_PARAMETER(m_max_iter, \"max_iter\");\n\tADD_PARAMETER(m_ftol_abs, \"ftol_abs\");\n\tADD_PARAMETER(m_ftol_rel, \"ftol_rel\");\nEND_FECORE_CLASS();\n\n#ifdef HAVE_NLOPT\nstatic double myfunc(unsigned int n, const double* x, double* grad, void* my_func_data)\n{\n\tNLOptOptimizeMethod* opt = (NLOptOptimizeMethod*)my_func_data;\n\treturn opt->EvalObjective(n, x, grad);\n}\n\nNLOptOptimizeMethod::NLOptOptimizeMethod(FEModel* fem, int nlopt_algor) : FEOptimizeMethod(fem), m_nlopt_algorithm(nlopt_algor) {}\n\nbool NLOptOptimizeMethod::Solve(FEOptimizeData* optData, vector<double>& amin, vector<double>& ymin, double* minObj)\n{\n\tif (m_nlopt_algorithm == -1) return false;\n\n\tm_optData = optData;\n\tint ma = optData->InputParameters();\n\n\tnlopt_opt opt;\n\topt = nlopt_create((nlopt_algorithm)m_nlopt_algorithm, ma); // algorithm and dimensionality\n\n\t// set nlopt options\n\tvector<double> a(ma), al(ma), au(ma);\n\tfor (int i = 0; i < ma; ++i)\n\t{\n\t\tFEInputParameter* var = optData->GetInputParameter(i);\n\t\ta[i] = var->InitValue();\n\t\tal[i] = var->MinValue();\n\t\tau[i] = var->MaxValue();\n\t}\n\tnlopt_set_lower_bounds(opt, al.data());\n\tnlopt_set_upper_bounds(opt, au.data());\n\n\tnlopt_set_min_objective(opt, myfunc, this);\n\tnlopt_set_maxeval(opt, m_max_iter);\n\tnlopt_set_param(opt, \"verbosity\", m_print_level);\n\n\t//\tnlopt_set_xtol_rel(opt, 1e-9);\n\tif (m_ftol_abs != 0) nlopt_set_ftol_abs(opt, m_ftol_abs);\n\tif (m_ftol_rel != 0) nlopt_set_ftol_rel(opt, m_ftol_rel);\n\n\tdouble minf; //the minimum objective value upon return\n\tint result = 0;\n\tif ((result = nlopt_optimize(opt, a.data(), &minf)) < 0) {\n\t\tprintf(\"nlopt failed!\\n\");\n\t}\n\telse {\n\t\tprintf(\"nlopt finished!\\n\");\n\n\t\tFEObjectiveFunction& obj = optData->GetObjective();\n\t\tdouble f = obj.Evaluate(ymin);\n\t\tamin = a;\n\t}\n\n\tif (minObj) *minObj = minf;\n\n\tnlopt_destroy(opt);\n\treturn (result >= 0);\n}\n\ndouble NLOptOptimizeMethod::EvalObjective(unsigned int n, const double* x, double* grad)\n{\n\tFEOptimizeData& opt = *m_optData;\n\tFEObjectiveFunction& obj = opt.GetObjective();\n\n\t// evaluate at a\n\tvector<double> a(n);\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tFEInputParameter& var = *opt.GetInputParameter(i);\n\t\ta[i] = x[i] * var.ScaleFactor();\n\t}\n\n\t// run the FEBio model with the new parameters\n\tif (opt.FESolve(a) == false) throw FEErrorTermination();\n\n\t// evaluate objective\n\tdouble f = obj.Evaluate();\n\n\t// calculate a finite difference approximation of the gradient if requested\n\tif (grad) {\n\t\tEvalApproxGradient(n, x, grad);\n\t}\n\n\treturn f;\n}\n\nvoid NLOptOptimizeMethod::EvalApproxGradient(unsigned int n, const double* x, double* grad)\n{\n\tFEOptimizeData& opt = *m_optData;\n\tFEObjectiveFunction& obj = opt.GetObjective();\n\n\tconst double eps = 1e-6;\n\tvector<double> x1(n);\n\tvector<double> x2(n);\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\t// perturb up\n\t\tfor (int j = 0; j < n; ++j) x1[j] = x[j];\n\t\tx1[i] += eps;\n\t\t// run the forward model\n\t\tbool b = opt.FESolve(x1);\n\t\tdouble f1 = obj.Evaluate();\n\n\t\t// perturb down\n\t\tfor (int j = 0; j < n; ++j) x2[j] = x[j];\n\t\tx2[i] -= eps;\n\n\t\t// run the forward model\n\t\tb = opt.FESolve(x2);\n\t\tdouble f2 = obj.Evaluate();\n\t\tdouble dfdx = (f1 - f2) / (2 * eps);\n\t\tgrad[i] += dfdx;\n\t}\n}\n\nFEMMAOptimizeMethod::FEMMAOptimizeMethod(FEModel* fem) : NLOptOptimizeMethod(fem, NLOPT_LD_MMA) {}\nFENelderMeadOptMethod::FENelderMeadOptMethod(FEModel* fem) : NLOptOptimizeMethod(fem, NLOPT_LN_NELDERMEAD) {}\nFESubplexOptimizeMethod::FESubplexOptimizeMethod(FEModel* fem) : NLOptOptimizeMethod(fem, NLOPT_LN_SBPLX) {}\n\n#else\nNLOptOptimizeMethod::NLOptOptimizeMethod(FEModel* fem, int algo) : FEOptimizeMethod(fem) {}\nbool NLOptOptimizeMethod::Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj) { return false; }\ndouble NLOptOptimizeMethod::EvalObjective(unsigned int n, const double* x, double* grad) { return 0.0; }\nvoid NLOptOptimizeMethod::EvalApproxGradient(unsigned int n, const double* x, double* grad) {}\n\nFEMMAOptimizeMethod::FEMMAOptimizeMethod(FEModel* fem) : NLOptOptimizeMethod(fem, -1) {}\nFENelderMeadOptMethod::FENelderMeadOptMethod(FEModel* fem) : NLOptOptimizeMethod(fem, -1) {}\nFESubplexOptimizeMethod::FESubplexOptimizeMethod(FEModel* fem) : NLOptOptimizeMethod(fem, -1) {}\n\n#endif\n"
  },
  {
    "path": "FEBioOpt/NLOptOptimizeMethod.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEOptimizeMethod.h\"\n#include \"febioopt_api.h\"\n\n// Base class for optimization methods that use the NLOpt library.\n// This class provides a common implementation of the Solve function that sets up the optimization problem and calls the NLOpt solver. Derived classes can specify the particular NLOpt algorithm to use and can implement the EvalObjective function to evaluate the objective function and its gradient.\nclass FEBIOOPT_API NLOptOptimizeMethod : public FEOptimizeMethod\n{\npublic:\n\tNLOptOptimizeMethod(FEModel* fem, int nlopt_algorithm);\n\tbool Solve(FEOptimizeData* pOpt, vector<double>& amin, vector<double>& ymin, double* minObj) override;\n\n\tdouble EvalObjective(unsigned int n, const double* x, double* grad);\n\nprivate:\n\tvoid EvalApproxGradient(unsigned int n, const double* x, double* grad);\n\nprivate:\n\tint m_nlopt_algorithm = -1;\n\tFEOptimizeData* m_optData = nullptr;\n\tint m_max_iter = 1000;\n\tdouble m_ftol_abs = 0; // use nlopt's default ftol_abs\n\tdouble m_ftol_rel = 0; // use nlopt's default ftol_rel\n\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEBIOOPT_API FEMMAOptimizeMethod : public NLOptOptimizeMethod\n{\npublic:\n\tFEMMAOptimizeMethod(FEModel* fem);\n};\n\nclass FEBIOOPT_API FENelderMeadOptMethod : public NLOptOptimizeMethod\n{\npublic:\n\tFENelderMeadOptMethod(FEModel* fem);\n};\n\nclass FEBIOOPT_API FESubplexOptimizeMethod : public NLOptOptimizeMethod\n{\npublic:\n\tFESubplexOptimizeMethod(FEModel* fem);\n};\n"
  },
  {
    "path": "FEBioOpt/febioopt_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febioopt_EXPORTS\n\t\t\t#define FEBIOOPT_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOOPT_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIOOPT_API\n\t#endif\n#else\n\t#define FEBIOOPT_API\n#endif\n"
  },
  {
    "path": "FEBioOpt/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// Use this file as the precompiler header file\n// TODO: Place frequently included header files here\n"
  },
  {
    "path": "FEBioOpt/targetver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// The following macros define the minimum required platform.  The minimum required platform\n// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run \n// your application.  The macros work by enabling all features available on platform versions up to and \n// including the version specified.\n\n// Modify the following defines if you have to target a platform prior to the ones specified below.\n// Refer to MSDN for the latest info on corresponding values for different platforms.\n#ifndef WINVER                          // Specifies that the minimum required platform is Windows Vista.\n#define WINVER 0x0600           // Change this to the appropriate value to target other versions of Windows.\n#endif\n\n#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows Vista.\n#define _WIN32_WINNT 0x0600     // Change this to the appropriate value to target other versions of Windows.\n#endif\n\n#ifndef _WIN32_WINDOWS          // Specifies that the minimum required platform is Windows 98.\n#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.\n#endif\n\n#ifndef _WIN32_IE                       // Specifies that the minimum required platform is Internet Explorer 7.0.\n#define _WIN32_IE 0x0700        // Change this to the appropriate value to target other versions of IE.\n#endif\n"
  },
  {
    "path": "FEBioPlot/FEBioPlotFile.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioPlotFile.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FECore/FEDataExport.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/FESurface.h>\n#include <FECore/FEEdge.h>\n#include <FECore/FEPlotDataStore.h>\n#include <FECore/log.h>\n#include <FECore/FEPIDController.h>\n#include <sstream>\n\nFEBioPlotFile::DICTIONARY_ITEM::DICTIONARY_ITEM()\n{\n\tm_psave = 0;\n\tm_ntype = 0;\n\tm_nfmt = 0;\n\tm_arraySize = 0;\n\tm_szname[0] = 0;\n\tm_szunit[0] = 0;\n}\n\nFEBioPlotFile::DICTIONARY_ITEM::DICTIONARY_ITEM(const FEBioPlotFile::DICTIONARY_ITEM& item)\n{\n\tm_psave = item.m_psave;\n\tm_ntype = item.m_ntype;\n\tm_nfmt = item.m_nfmt;\n\tm_arraySize = item.m_arraySize;\n\tm_arrayNames = item.m_arrayNames;\n\tm_szname[0] = 0;\n\tm_szunit[0] = 0;\n\tif (item.m_szname[0]) strcpy(m_szname, item.m_szname);\n\tif (item.m_szunit[0]) strcpy(m_szunit, item.m_szunit);\n}\n\nclass FEPlotSurfaceDataExport : public FEPlotData\n{\npublic:\n\tFEPlotSurfaceDataExport(FEModel* fem, const char* szname, Var_Type itype, Storage_Fmt fmt) : FEPlotData(fem, FE_REGION_SURFACE, itype, fmt) { m_szname = szname; }\n\tvoid Save(FEModel& fem, PltArchive& ar)\n\t{\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tint NS = mesh.Surfaces();\n\t\tfor (int i = 0; i<NS; ++i)\n\t\t{\n\t\t\tFESurface& s = mesh.Surface(i);\n\t\t\tint ND = s.DataExports();\n\t\t\tif (ND > 0)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j<ND; ++j)\n\t\t\t\t{\n\t\t\t\t\tFEDataExport* pd = s.GetDataExport(j);\n\t\t\t\t\tif (strcmp(pd->m_szname, m_szname) == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEDataStream d;\n\t\t\t\t\t\tpd->Serialize(d);\n\t\t\t\t\t\tar.WriteData(i + 1, d.data());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\nprivate:\n\tconst char*\t\tm_szname;\n};\n\nclass FEPlotDomainDataExport : public FEPlotData\n{\npublic:\n\tFEPlotDomainDataExport(FEModel* fem, const char* szname, Var_Type itype, Storage_Fmt fmt) : FEPlotData(fem, FE_REGION_DOMAIN, itype, fmt) { m_szname = szname; }\n\tvoid Save(FEModel& fem, PltArchive& ar)\n\t{\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tint NDOMS = mesh.Domains();\n\t\tfor (int i = 0; i<NDOMS; ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tint NDATA = dom.DataExports();\n\t\t\tif (NDATA > 0)\n\t\t\t{\n\t\t\t\tfor (int j = 0; j<NDATA; ++j)\n\t\t\t\t{\n\t\t\t\t\tFEDataExport* pd = dom.GetDataExport(j);\n\t\t\t\t\tif (strcmp(pd->m_szname, m_szname) == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEDataStream d;\n\t\t\t\t\t\tpd->Serialize(d);\n\t\t\t\t\t\tar.WriteData(i + 1, d.data());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\nprivate:\n\tconst char*\t\tm_szname;\n};\n\n\nclass FEBioPlotVariable : public FEPlotNodeData\n{\npublic:\n\tFEBioPlotVariable(FEModel* fem, const char* szname, Var_Type itype, Storage_Fmt fmt) : FEPlotNodeData(fem, itype, fmt) { strcpy(m_szname, szname); }\n\tbool Save(FEMesh& mesh, FEDataStream& str)\n\t{\n\t\t// get the DOFS\n\t\tFEModel& fem = *GetFEModel();\n\t\tDOFS& dofs = fem.GetDOFS();\n\n\t\t// see if this variable exists\n\t\tint nvar = dofs.GetVariableIndex(m_szname);\n\t\tif (nvar < 0) return false;\n\n\t\t// get the size of the variable\n\t\tint n = dofs.GetVariableSize(nvar);\n\t\tif (n == 0) return false;\n\n\t\t// get the start index of the DOFS\n\t\tint ndof = dofs.GetDOF(nvar, 0);\n\t\tif (ndof < 0) return false;\n\n\t\t// store the nodal data\n\t\tint NN = mesh.Nodes();\n\t\tfor (int i = 0; i<NN; ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(i);\n\t\t\tfor (int j = 0; j<n; ++j) str << node.get(ndof + j);\n\t\t}\n\n\t\treturn true;\n\t}\n\nprivate:\n\tchar\tm_szname[256];\n};\n\nclass FEPlotArrayVariable : public FEPlotDomainData\n{\npublic:\n\tFEPlotArrayVariable(FEModel* fem, const char* szname, int index) : FEPlotDomainData(fem, PLT_FLOAT, FMT_NODE) \n\t{ \n\t\tstrcpy(m_szname, szname); \n\t\tm_index = index;\n\t\tif (index == -1)\n\t\t{\n\t\t\t// get the DOFS\n\t\t\tFEModel& fem = *GetFEModel();\n\t\t\tDOFS& dofs = fem.GetDOFS();\n\n\t\t\t// see if this variable exists\n\t\t\tint nvar = dofs.GetVariableIndex(m_szname);\n\t\t\tif (nvar >= 0)\n\t\t\t{\n\t\t\t\t// get the size of the variable\n\t\t\t\tint n = dofs.GetVariableSize(nvar);\n\t\t\t\tif (n > 0)\n\t\t\t\t{\n\t\t\t\t\tSetVarType(PLT_ARRAY);\n\t\t\t\t\tSetArraySize(n);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tbool Save(FEDomain& D, FEDataStream& a)\n\t{\n\t\t// get the DOFS\n\t\tFEModel& fem = *GetFEModel();\n\t\tDOFS& dofs = fem.GetDOFS();\n\n\t\t// see if this variable exists\n\t\tint nvar = dofs.GetVariableIndex(m_szname);\n\t\tif (nvar < 0) return false;\n\n\t\t// get the size of the variable\n\t\tint n = dofs.GetVariableSize(nvar);\n\t\tif (n == 0) return false;\n\n\t\t// get the start index of the DOFS\n\t\tif (m_index >= 0)\n\t\t{\n\t\t\tint ndof = dofs.GetDOF(nvar, m_index);\n\t\t\tif (ndof < 0) return false;\n\n\t\t\t// see if this domain contains this dof\n\t\t\tconst FEDofList& domDofs = D.GetDOFList();\n\t\t\tbool bfound = false;\n\t\t\tfor (int i = 0; i < (int)domDofs.Size(); ++i)\n\t\t\t{\n\t\t\t\tif (domDofs[i] == ndof)\n\t\t\t\t{\n\t\t\t\t\tbfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bfound == false) return false;\n\n\t\t\t// store the nodal data\n\t\t\tint NN = D.Nodes();\n\t\t\tfor (int i = 0; i < NN; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = D.Node(i);\n\t\t\t\ta << node.get(ndof);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst FEDofList& domDofs = D.GetDOFList();\n\n\t\t\tvector<int> dofList(n, -1);\n\t\t\tfor (int dof_i = 0; dof_i < n; dof_i++)\n\t\t\t{\n\t\t\t\tint ndof = dofs.GetDOF(nvar, dof_i);\n\t\t\t\t// see if this domain contains this dof\n\t\t\t\tbool bfound = false;\n\t\t\t\tfor (int i = 0; i < (int)domDofs.Size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tif (domDofs[i] == ndof)\n\t\t\t\t\t{\n\t\t\t\t\t\tbfound = true;\n\t\t\t\t\t\tdofList[dof_i] = i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// store the nodal data\n\t\t\tint NN = D.Nodes();\n\t\t\tfor (int i = 0; i < NN; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = D.Node(i);\n\t\t\t\tfor (int dof_i = 0; dof_i < n; dof_i++)\n\t\t\t\t{\n\t\t\t\t\tint ndof = dofList[dof_i];\n\t\t\t\t\tif (ndof < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ta << 0.0;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// store the nodal data\n\t\t\t\t\t\ta << node.get(ndof);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\nprivate:\n\tchar\tm_szname[256];\n\tint\t\tm_index;\n};\n\n//-----------------------------------------------------------------------------\n//! Adds a variable to the plot file. \n//! \n//! The name of the filter can be composed of three parts and in general takes on\n//! the following format.\n//!\n//! szname = \"field_name[filter]=alias\". \n//!\n//! field_name = This is the actual filter naeme as it is registered with the framework.\n//! filter     = This is a filter that is used to resolve ambiguities.\n//! alias      = This is an alternative name for the field variable.\n//!\n//! The alias is optional but can be used by post-processing software to present an alternative\n//! (often simpler) name for the field variable than the default field_name + filter combo.\n//! \n//! Whether a filter is required depends entirely on the field variable. Most field variables don't\n//! require it, but some do in order to resolve an ambiguity. For instance, the \"parameter\" field\n//! allows users to plot the spatially varying value of a material parameter. The filter is used to\n//! specify the material and parameter name. \n//!\n//! The filter can be a numerical value or a string. If it's a string then it must be enclosed in\n//! single quotes. \n//!\n//! szname = \"field_name[12]\"  \\\\ example of a numerical filter\n//! szname = \"field_name['val'] \\\\ example of a string filter\n//!\n//! The interpretation of these filters is entirely left up to the field variable. \n//!\nbool FEBioPlotFile::Dictionary::AddVariable(FEModel* pfem, const char* szname, vector<int>& item, const char* szdom)\n{\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\tFEPlotFieldDescriptor PD(szname);\n\tif (!PD.isValid()) return false;\n\n\tconst char* szfield = PD.fieldName.c_str();\n\n\t// create the plot variable\n\tFEPlotData* ps = fecore_new<FEPlotData>(szfield, pfem);\n\tif (ps)\n\t{\n\t\t// set the optional item list and filter\n\t\tps->SetItemList(item);\n\t\tif (PD.HasFilter())\n\t\t{\n\t\t\tif (PD.IsStringFilter())\n\t\t\t{\n\t\t\t\tif (ps->SetFilter(PD.strFilter.c_str()) == false) return false;\n\t\t\t}\n\t\t\telse if (PD.IsNumberFilter())\n\t\t\t{\n\t\t\t\tif (ps->SetFilter(PD.numFilter) == false) return false;\n\t\t\t}\n\t\t}\n\n\t\tconst char* sz = PD.alias.c_str();\n\t\t// add the field to the plot file\n\t\tps->SetDomainName(szdom);\n\t\tswitch (ps->RegionType())\n\t\t{\n\t\tcase FE_REGION_GLOBAL : return AddGlobalVariable(ps, sz);\n\t\tcase FE_REGION_NODE   : return AddNodalVariable(ps, sz, item);\n\t\tcase FE_REGION_DOMAIN : return AddDomainVariable(ps, sz, item);\n\t\tcase FE_REGION_SURFACE: return AddSurfaceVariable(ps, sz, item);\n\t\tcase FE_REGION_EDGE   : return AddEdgeVariable(ps, sz, item);\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// If we get here then this variable is not a plot field.\n\t\t// But let's see if it is an export variable from a domain\n\t\t// Check the surfaces first\n\t\tFEMesh& mesh = pfem->GetMesh();\n\t\tfor (int i = 0; i<mesh.Surfaces(); ++i)\n\t\t{\n\t\t\tFESurface& s = mesh.Surface(i);\n\t\t\tint ND = s.DataExports();\n\t\t\tfor (int j = 0; j<ND; ++j)\n\t\t\t{\n\t\t\t\tFEDataExport* pd = s.GetDataExport(j);\n\t\t\t\tif (strcmp(pd->m_szname, szname) == 0)\n\t\t\t\t{\n\t\t\t\t\t// We have a match. Create a plot field for this export\n\t\t\t\t\tps = new FEPlotSurfaceDataExport(pfem, pd->m_szname, pd->m_type, pd->m_fmt);\n\t\t\t\t\treturn AddSurfaceVariable(ps, szname, item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// now the domains.\n\t\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tint ND = dom.DataExports();\n\t\t\tfor (int j = 0; j<ND; ++j)\n\t\t\t{\n\t\t\t\tFEDataExport* pd = dom.GetDataExport(j);\n\t\t\t\tif (strcmp(pd->m_szname, szname) == 0)\n\t\t\t\t{\n\t\t\t\t\t// We have a match. Create a plot field for this export\n\t\t\t\t\tps = new FEPlotDomainDataExport(pfem, pd->m_szname, pd->m_type, pd->m_fmt);\n\t\t\t\t\treturn AddDomainVariable(ps, szname, item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we still didn't find it, maybe it's a model variable.\n\t\tDOFS& dofs = pfem->GetDOFS();\n\t\tint nvar = dofs.GetVariableIndex(szfield);\n\t\tif (nvar >= 0)\n\t\t{\n\t\t\tint vartype = dofs.GetVariableType(nvar);\n\t\t\tif (vartype == VAR_SCALAR)\n\t\t\t{\n\t\t\t\tps = new FEBioPlotVariable(pfem, szfield, PLT_FLOAT, FMT_NODE);\n\t\t\t\treturn AddNodalVariable(ps, szname, item);\n\t\t\t}\n\t\t\telse if (vartype == VAR_VEC3)\n\t\t\t{\n\t\t\t\tps = new FEBioPlotVariable(pfem, szfield, PLT_VEC3F, FMT_NODE);\n\t\t\t\treturn AddNodalVariable(ps, szname, item);\n\t\t\t}\n\t\t\telse if (vartype == VAR_ARRAY)\n\t\t\t{\n\t\t\t\tint ndofs = dofs.GetVariableSize(szfield);\n\t\t\t\tint index = -1;\n\t\t\t\tif (PD.IsNumberFilter()) index = PD.numFilter;\n\t\t\t\telse if (PD.IsStringFilter())\n\t\t\t\t{\n\t\t\t\t\tconst char* szflt = PD.strFilter.c_str();\n\t\t\t\t\tindex = dofs.GetIndex(szfield, szflt);\n\t\t\t\t\tif (index < 0) index = pfem->GetDOFIndex(szflt);\n\t\t\t\t}\n\t\t\t\tif ((index < 0) || (index >= ndofs)) return false;\n\n\t\t\t\tps = new FEPlotArrayVariable(pfem, szfield, index);\n\t\t\t\treturn AddDomainVariable(ps, szname, item);\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Dictionary::AddGlobalVariable(FEPlotData* ps, const char* szname)\n{\n\tassert(ps->RegionType() == FE_REGION_GLOBAL);\n\tif (ps->RegionType() == FE_REGION_GLOBAL)\n\t{\n\t\tDICTIONARY_ITEM it;\n\t\tit.m_ntype = ps->DataType();\n\t\tit.m_nfmt  = ps->StorageFormat();\n\t\tit.m_psave = ps;\n\t\tit.m_arraySize = ps->GetArraysize();\n\t\tit.m_arrayNames = ps->GetArrayNames();\n\t\tstrcpy(it.m_szname, szname);\n\t\tif (ps->GetUnits())\n\t\t{\n\t\t\tstrcpy(it.m_szunit, ps->GetUnits());\n\t\t}\n\t\tm_Glob.push_back(it);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Dictionary::AddMaterialVariable(FEPlotData* ps, const char* szname)\n{\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Dictionary::AddNodalVariable(FEPlotData* ps, const char* szname, vector<int>& item)\n{\n\tassert(ps->RegionType()==FE_REGION_NODE);\n\tif (ps->RegionType()==FE_REGION_NODE)\n\t{\n\t\tDICTIONARY_ITEM it;\n\t\tit.m_ntype = ps->DataType();\n\t\tit.m_nfmt  = ps->StorageFormat();\n\t\tit.m_psave = ps;\n\t\tit.m_arraySize = ps->GetArraysize();\n\t\tit.m_arrayNames = ps->GetArrayNames();\n\t\tstrcpy(it.m_szname, szname);\n\t\tif (ps->GetUnits())\n\t\t{\n\t\t\tstrcpy(it.m_szunit, ps->GetUnits());\n\t\t}\n\t\tm_Node.push_back(it);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Dictionary::AddDomainVariable(FEPlotData* ps, const char* szname, vector<int>& item)\n{\n\tassert(ps->RegionType()==FE_REGION_DOMAIN);\n\tif (ps->RegionType()==FE_REGION_DOMAIN)\n\t{\n\t\tDICTIONARY_ITEM it;\n\t\tit.m_ntype = ps->DataType();\n\t\tit.m_nfmt  = ps->StorageFormat();\n\t\tit.m_psave = ps;\n\t\tit.m_arraySize = ps->GetArraysize();\n\t\tit.m_arrayNames = ps->GetArrayNames();\n\t\tstrcpy(it.m_szname, szname);\n\t\tif (ps->GetUnits())\n\t\t{\n\t\t\tstrcpy(it.m_szunit, ps->GetUnits());\n\t\t}\n\t\tm_Elem.push_back(it);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Dictionary::AddSurfaceVariable(FEPlotData* ps, const char* szname, vector<int>& item)\n{\n\tassert(ps->RegionType()==FE_REGION_SURFACE);\n\tif (ps->RegionType()==FE_REGION_SURFACE)\n\t{\n\t\tDICTIONARY_ITEM it;\n\t\tit.m_ntype = ps->DataType();\n\t\tit.m_nfmt  = ps->StorageFormat();\n\t\tit.m_psave = ps;\n\t\tit.m_arraySize = ps->GetArraysize();\n\t\tit.m_arrayNames = ps->GetArrayNames();\n\t\tstrcpy(it.m_szname, szname);\n\t\tif (ps->GetUnits())\n\t\t{\n\t\t\tstrcpy(it.m_szunit, ps->GetUnits());\n\t\t}\n\t\tm_Face.push_back(it);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool FEBioPlotFile::Dictionary::AddEdgeVariable(FEPlotData* ps, const char* szname, vector<int>& item)\n{\n\tassert(ps->RegionType() == FE_REGION_EDGE);\n\tif (ps->RegionType() == FE_REGION_EDGE)\n\t{\n\t\tDICTIONARY_ITEM it;\n\t\tit.m_ntype = ps->DataType();\n\t\tit.m_nfmt = ps->StorageFormat();\n\t\tit.m_psave = ps;\n\t\tit.m_arraySize = ps->GetArraysize();\n\t\tit.m_arrayNames = ps->GetArrayNames();\n\t\tstrcpy(it.m_szname, szname);\n\t\tif (ps->GetUnits())\n\t\t{\n\t\t\tstrcpy(it.m_szunit, ps->GetUnits());\n\t\t}\n\t\tm_Edge.push_back(it);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::Dictionary::Defaults(FEModel& fem)\n{\n\t// First we build the dictionary\n\t// get the mesh\n\tFEMesh& m = fem.GetMesh();\n\n\t// Define default variables\n\tif (m_Node.empty() && m_Elem.empty() && m_Face.empty())\n\t{\n\t\tvector<int> l; // empty list\n\t\tAddVariable(&fem, \"displacement\", l);\n\t\tAddVariable(&fem, \"stress\", l);\n\t}\n\n\t// Define default global variables\n\tif (m_Glob.empty())\n\t{\n\t\t// add all the PID controllers\n\t\tstd::vector<int> dummy;\n\t\tfor (int i = 0; i < fem.LoadControllers(); ++i)\n\t\t{\n\t\t\tFEPIDController* pid = dynamic_cast<FEPIDController*>(fem.GetLoadController(i));\n\t\t\tif (pid)\n\t\t\t{\n\t\t\t\tstringstream ss;\n\t\t\t\tss << \"pid controller['\" << pid->GetName() << \"']=\" << pid->GetName();\n\t\t\t\tstring s = ss.str();\n\t\t\t\tAddVariable(&fem, s.c_str(), dummy);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::Dictionary::Clear()\n{\n\tlist<DICTIONARY_ITEM>::iterator it = m_Glob.begin();\n\tfor (int i = 0; i < (int)m_Glob.size(); ++i, ++it) delete it->m_psave;\n\tm_Glob.clear();\n\n\tit = m_Mat.begin();\n\tfor (int i = 0; i < (int)m_Mat.size(); ++i, ++it) delete it->m_psave;\n\tm_Mat.clear();\n\n\tit = m_Node.begin();\n\tfor (int i = 0; i < (int)m_Node.size(); ++i, ++it) delete it->m_psave;\n\tm_Node.clear();\n\n\tit = m_Elem.begin();\n\tfor (int i = 0; i < (int)m_Elem.size(); ++i, ++it) delete it->m_psave;\n\tm_Elem.clear();\n\n\tit = m_Face.begin();\n\tfor (int i = 0; i < (int)m_Face.size(); ++i, ++it) delete it->m_psave;\n\tm_Face.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::PlotObject::AddData(const char* szname, Var_Type type, FEPlotData* psave)\n{\n\tDICTIONARY_ITEM item;\n\titem.m_psave = nullptr;\n\n\titem.m_szname[0] = 0;\n\tint l = strlen(szname);\n\tif (l >= STR_SIZE) l = STR_SIZE - 1;\n\tstrncpy(item.m_szname, szname, l);\n\titem.m_szname[l] = 0;\n\n\titem.m_ntype = type;\n\titem.m_nfmt = FMT_ITEM;\n\titem.m_psave = psave;\n\n\tm_data.push_back(item);\n}\n\n\n//=============================================================================\n\nFEBioPlotFile::FEBioPlotFile(FEModel* fem) : PlotFile(fem)\n{\n\tm_ncompress = 0;\n\tm_meshesWritten = 0;\n\tm_exportUnitsFlag = false;\n\tm_exportErodedElements = true;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioPlotFile::PointObjects()\n{\n\treturn (int) m_Points.size();\n}\n\n//-----------------------------------------------------------------------------\nFEBioPlotFile::PointObject* FEBioPlotFile::GetPointObject(int i)\n{\n\tif ((i >= 0) && (i < PointObjects())) return m_Points[i];\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEBioPlotFile::PointObject* FEBioPlotFile::AddPointObject(const std::string& name)\n{\n\tPointObject* po = new PointObject;\n\tm_Points.push_back(po);\n\tpo->m_name = name;\n\tpo->m_id = m_Points.size();\n\treturn po;\n}\n\n//-----------------------------------------------------------------------------\nint FEBioPlotFile::LineObjects()\n{\n\treturn (int)m_Lines.size();\n}\n\n//-----------------------------------------------------------------------------\nFEBioPlotFile::LineObject* FEBioPlotFile::GetLineObject(int i)\n{\n\tif ((i >= 0) && (i < LineObjects())) return m_Lines[i];\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEBioPlotFile::LineObject* FEBioPlotFile::AddLineObject(const std::string& name)\n{\n\tLineObject* po = new LineObject;\n\tm_Lines.push_back(po);\n\tpo->m_name = name;\n\tpo->m_id = m_Lines.size();\n\treturn po;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::SetCompression(int n)\n{\n#ifdef HAVE_ZLIB\n\tm_ncompress = n;\n#else\n\tm_ncompress = 0;\n#endif\n}\n\n//-----------------------------------------------------------------------------\n//! set the version string\nvoid FEBioPlotFile::SetSoftwareString(const std::string& softwareString)\n{\n\tm_softwareString = softwareString;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::IsValid() const\n{\n\treturn m_ar.IsValid();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::Close()\n{\n\tm_ar.Close();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::Clear()\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tdic.Clear();\n\tm_Surf.clear();\n\tfor (PointObject* p : m_Points) delete p; m_Points.clear();\n\tfor (LineObject* l : m_Lines) delete l; m_Lines.clear();\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Open(const char *szfile)\n{\n\tFEModel* fem = GetFEModel();\n\n\tm_meshesWritten = 0;\n\n\t// open the archive\n\tm_ar.Create(szfile);\n\n\t// set compression\n\tFEPlotDataStore& pltData = fem->GetPlotDataStore();\n\tSetCompression(pltData.GetPlotCompression());\n\n\tBuildDictionary();\n\n\ttry\n\t{\n\t\t// write the root element\n\t\tif (WriteRoot(*fem) == false) return false;\n\n\t\t// write the mesh section\n\t\tif (WriteMeshSection(*fem) == false) return false;\n\t}\n\tcatch (...)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::WriteRoot(FEModel& fem)\n{\n\t// write the root element\n\t// (don't compress this section)\n\tm_ar.SetCompression(0);\n\tm_ar.BeginChunk(PLT_ROOT);\n\t{\n\t\t// --- save the header file ---\n\t\tm_ar.BeginChunk(PLT_HEADER);\n\t\t{\n\t\t\tif (WriteHeader(fem) == false) return false;\n\t\t}\n\t\tm_ar.EndChunk();\n\n\t\t// --- save the dictionary ---\n\t\tm_ar.BeginChunk(PLT_DICTIONARY);\n\t\t{\n\t\t\tif (WriteDictionary(fem) == false) return false;\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\tm_ar.EndChunk();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::WriteHeader(FEModel& fem)\n{\n\t// setup the header\n\tunsigned int nversion = PLT_VERSION;\n\n\t// output header\n\tm_ar.WriteChunk(PLT_HDR_VERSION, nversion);\n\n\t// compression flag\n\tm_ar.WriteChunk(PLT_HDR_COMPRESSION, m_ncompress);\n\n\t// software flag\n\tif (m_softwareString.empty() == false)\n\t{\n\t\tconst char* sz = m_softwareString.c_str();\n\t\tm_ar.WriteChunk(PLT_HDR_SOFTWARE, sz);\n\t}\n\n\t// units flag\n\tm_exportUnitsFlag = false;\n\tconst char* szunits = fem.GetUnits();\n\tif (szunits != nullptr)\n\t{\n\t\tm_exportUnitsFlag = true;\n\t\tm_ar.WriteChunk(PLT_HDR_UNITS, szunits);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::WriteDictionary(FEModel& fem)\n{\n\t// setup defaults for the dictionary\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tdic.Defaults(fem);\n\n\t// Next, we save the dictionary\n\t// Global variables\n\tif (dic.GlobalVariables() > 0)\n\t{\n\t\tm_ar.BeginChunk(PLT_DIC_GLOBAL);\n\t\t{\n\t\t\tWriteDicList(dic.GlobalVariableList());\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\t// store nodal variables\n\tif (dic.NodalVariables() > 0)\n\t{\n\t\tm_ar.BeginChunk(PLT_DIC_NODAL);\n\t\t{\n\t\t\tWriteDicList(dic.NodalVariableList());\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\t// store element variables\n\tif (dic.DomainVariables())\n\t{\n\t\tm_ar.BeginChunk(PLT_DIC_DOMAIN);\n\t\t{\n\t\t\tWriteDicList(dic.DomainVariableList());\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\t// store surface data\n\tif (dic.SurfaceVariables())\n\t{\n\t\tm_ar.BeginChunk(PLT_DIC_SURFACE);\n\t\t{\n\t\t\tWriteDicList(dic.SurfaceVariableList());\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\t// store edge variables\n\tif (dic.EdgeVariables())\n\t{\n\t\tm_ar.BeginChunk(PLT_DIC_EDGE);\n\t\t{\n\t\t\tWriteDicList(dic.EdgeVariableList());\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDicList(list<FEBioPlotFile::DICTIONARY_ITEM>& dic)\n{\n\tint N = (int) dic.size();\n\tlist<DICTIONARY_ITEM>::iterator pi = dic.begin();\n\tfor (int i=0; i<N; ++i, ++pi)\n\t{\n\t\tm_ar.BeginChunk(PLT_DIC_ITEM);\n\t\t{\n\t\t\tWriteDictionaryItem(*pi);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDictionaryItem(DICTIONARY_ITEM& it)\n{\n\tm_ar.WriteChunk(PLT_DIC_ITEM_TYPE, it.m_ntype);\n\tm_ar.WriteChunk(PLT_DIC_ITEM_FMT, it.m_nfmt);\n\tm_ar.WriteChunk(PLT_DIC_ITEM_ARRAYSIZE, it.m_arraySize);\n\tif ((it.m_arraySize > 0) && (it.m_arrayNames.size() == it.m_arraySize))\n\t{\n\t\tfor (int i = 0; i < (int)it.m_arraySize; ++i)\n\t\t{\n\t\t\tconst string& si = it.m_arrayNames[i];\n\t\t\tconst char* c = si.c_str();\n\t\t\tm_ar.WriteChunk(PLT_DIC_ITEM_ARRAYNAME, (char*)c, STR_SIZE);\n\t\t}\n\t}\n\tm_ar.WriteChunk(PLT_DIC_ITEM_NAME, it.m_szname, STR_SIZE);\n\n\tif (m_exportUnitsFlag && it.m_szunit && it.m_szunit[0])\n\t{\n\t\tm_ar.WriteChunk(PLT_DIC_ITEM_UNITS, it.m_szunit, STR_SIZE);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::WriteMeshSection(FEModel& fem)\n{\n\t// get the mesh\n\tFEMesh& m = fem.GetMesh();\n\n\tm_ar.BeginChunk(PLT_MESH);\n\t{\n\t\t// node section\n\t\tm_ar.BeginChunk(PLT_NODE_SECTION);\n\t\t{\n\t\t\tWriteNodeSection(m);\n\t\t}\n\t\tm_ar.EndChunk();\n\n\t\t// domain section\n\t\tm_ar.BeginChunk(PLT_DOMAIN_SECTION);\n\t\t{\n\t\t\tWriteDomainSection(m);\n\t\t}\n\t\tm_ar.EndChunk();\n\n\t\t// surface section\n\t\tif (m.FacetSets() > 0)\n\t\t{\n\t\t\tBuildSurfaceTable();\n\t\t\tm_ar.BeginChunk(PLT_SURFACE_SECTION);\n\t\t\t{\n\t\t\t\tWriteSurfaceSection(m);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\n\t\t// edge section\n\t\tif (m.Edges() > 0)\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_EDGE_SECTION);\n\t\t\t{\n\t\t\t\tWriteEdgeSection(m);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\n\t\t// node sets\n\t\tif (m.NodeSets() > 0)\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_NODESET_SECTION);\n\t\t\t{\n\t\t\t\tWriteNodeSetSection(m);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\n\t\t// element sets\n\t\tif (m.ElementSets() > 0)\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_ELEMENTSET_SECTION);\n\t\t\t{\n\t\t\t\tWriteElementSetSection(m);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\n\t\t// facet sets\n\t\tif (m.FacetSets() > 0)\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_FACETSET_SECTION);\n\t\t\t{\n\t\t\t\tWriteFacetSetSection(m);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\t// parts\n\t\t// (we write the materials as parts)\n\t\tif (fem.Materials() > 0)\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_PARTS_SECTION);\n\t\t\t{\n\t\t\t\tWritePartsSection(fem);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\n\t\t// additional objects\n\t\tif (m_meshesWritten == 0)\n\t\t{\n\t\t\tif ((m_Points.size() > 0) || (m_Lines.size() > 0))\n\t\t\t{\n\t\t\t\tm_ar.BeginChunk(PLT_OBJECTS_SECTION);\n\t\t\t\t{\n\t\t\t\t\tWriteObjectsSection();\n\t\t\t\t}\n\t\t\t\tm_ar.EndChunk();\n\t\t\t}\n\t\t}\n\t}\n\tm_ar.EndChunk();\n\n\tm_meshesWritten++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteNodeSection(FEMesh& m)\n{\n\t// write the node header\n\tm_ar.BeginChunk(PLT_NODE_HEADER);\n\t{\n\t\tint NN = m.Nodes();\n\t\tint dim = 3;\n\t\tm_ar.WriteChunk(PLT_NODE_SIZE, NN);\n\t\tm_ar.WriteChunk(PLT_NODE_DIM , dim);\n//\t\tm_ar.WriteChunk(PLT_NODE_NAME, \"AllNodes\");\n\t}\n\tm_ar.EndChunk();\n\n\t// write the reference coordinates\n\tint NN = m.Nodes();\n\tvector<float> X(4*NN);\n\tfor (int i=0; i<m.Nodes(); ++i)\n\t{\n\t\tFENode& node = m.Node(i);\n\t\t// as of 3.3 we store the node IDs. (Previously the node index was stored)\n\t\t*((int*) (&X[0] + 4*i)) = node.GetID();\n\t\tX[4*i+1] = (float) node.m_r0.x;\n\t\tX[4*i+2] = (float) node.m_r0.y;\n\t\tX[4*i+3] = (float) node.m_r0.z;\n\t}\n\tm_ar.WriteChunk(PLT_NODE_COORDS, X);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDomainSection(FEMesh& m)\n{\n\t// write all domains\n\tfor (int nd = 0; nd<m.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = m.Domain(nd);\n\t\tm_ar.BeginChunk(PLT_DOMAIN);\n\t\t{\n\t\t\tswitch (dom.Class())\n\t\t\t{\n\t\t\tcase FE_DOMAIN_SOLID   : WriteSolidDomain   (static_cast<FESolidDomain&   >(dom)); break;\n\t\t\tcase FE_DOMAIN_SHELL   : WriteShellDomain   (static_cast<FEShellDomain&   >(dom)); break;\n\t\t\tcase FE_DOMAIN_BEAM    : WriteBeamDomain    (static_cast<FEBeamDomain&    >(dom)); break;\n\t\t\tcase FE_DOMAIN_DISCRETE: WriteDiscreteDomain(static_cast<FEDiscreteDomain&>(dom)); break;\n            case FE_DOMAIN_2D      : WriteDomain2D      (static_cast<FEDomain2D&      >(dom)); break;\n\t\t\t}\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteSolidDomain(FESolidDomain& dom)\n{\n\tint mid = dom.GetMaterial()->GetID();\n\tassert(mid > 0);\n\tint eshape = dom.GetElementShape();\n\n\tint NE = dom.Elements();\n\tint elementCount = NE;\n\tif (m_exportErodedElements == false)\n\t{\n\t\telementCount = 0;\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(i);\n\t\t\tif (el.isActive()) elementCount++;\n\t\t}\n\t}\n\n\t// figure out element type\n\tint ne = 0;\n\tint dtype = 0;\n\tswitch (eshape)\n\t{\n\t\tcase ET_HEX8   : ne =  8; dtype = PLT_ELEM_HEX; break;\n\t\tcase ET_PENTA6 : ne =  6; dtype = PLT_ELEM_PENTA; break;\n\t\tcase ET_TET4   : ne =  4; dtype = PLT_ELEM_TET4; break;\n\t\tcase ET_TET5   : ne =  5; dtype = PLT_ELEM_TET5; break;\n\t\tcase ET_TET10  : ne = 10; dtype = PLT_ELEM_TET10; break;\n\t\tcase ET_TET15  : ne = 15; dtype = PLT_ELEM_TET15; break;\n\t\tcase ET_HEX20  : ne = 20; dtype = PLT_ELEM_HEX20; break;\n\t\tcase ET_HEX27  : ne = 27; dtype = PLT_ELEM_HEX27; break;\n\t\tcase ET_TET20  : ne = 20; dtype = PLT_ELEM_TET20; break;\n        case ET_PENTA15: ne = 15; dtype = PLT_ELEM_PENTA15; break;\n\t\tcase ET_PYRA5  : ne =  5; dtype = PLT_ELEM_PYRA5; break;\n        case ET_PYRA13 : ne = 13; dtype = PLT_ELEM_PYRA13; break;\n        default:\n\t\t\tassert(false);\n\t}\n\n\t// write the header\n\tm_ar.BeginChunk(PLT_DOMAIN_HDR);\n\t{\n\t\tm_ar.WriteChunk(PLT_DOM_ELEM_TYPE, dtype);\n\t\tm_ar.WriteChunk(PLT_DOM_PART_ID  ,   mid);\n\t\tm_ar.WriteChunk(PLT_DOM_ELEMS    , elementCount);\n\t\tm_ar.WriteChunk(PLT_DOM_NAME     , dom.GetName());\n\t}\n\tm_ar.EndChunk();\n\n\t// write the element list\n\tint n[FEElement::MAX_NODES + 1];\n\tm_ar.BeginChunk(PLT_DOM_ELEM_LIST);\n\t{\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(i);\n\t\t\tif (m_exportErodedElements || el.isActive())\n\t\t\t{\n\t\t\t\tn[0] = el.GetID();\n\t\t\t\tfor (int j = 0; j < ne; ++j) n[j + 1] = el.m_node[j];\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENT, n, ne + 1);\n\t\t\t}\n\t\t}\n\t}\n\tm_ar.EndChunk();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteShellDomain(FEShellDomain& dom)\n{\n\tint mid = dom.GetMaterial()->GetID();\n\tassert(mid > 0);\n\tint etype = dom.GetElementType();\n\n\tint NE = dom.Elements();\n\tint elementCount = NE;\n\tif (m_exportErodedElements == false)\n\t{\n\t\telementCount = 0;\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEShellElement& el = dom.Element(i);\n\t\t\tif (el.isActive()) elementCount++;\n\t\t}\n\t}\n\n\t// figure out element type\n\tint ne = 0;\n\tint dtype = 0;\n\tswitch (etype)\n\t{\n        case FE_SHELL_QUAD4G4 :\n        case FE_SHELL_QUAD4G8 :\n        case FE_SHELL_QUAD4G12 :\n            ne = 4; dtype = PLT_ELEM_QUAD; break;\n        case FE_SHELL_TRI3G3  :\n        case FE_SHELL_TRI3G6  :\n        case FE_SHELL_TRI3G9  :\n            ne = 3; dtype = PLT_ELEM_TRI; break;\n        case FE_SHELL_QUAD8G18:\n        case FE_SHELL_QUAD8G27:\n            ne = 8; dtype = PLT_ELEM_QUAD8; break;\n        case FE_SHELL_TRI6G14 :\n        case FE_SHELL_TRI6G21 :\n            ne = 6; dtype = PLT_ELEM_TRI6; break;\n        default:\n            assert(false);\n\t}\n\n\t// write the header\n\tm_ar.BeginChunk(PLT_DOMAIN_HDR);\n\t{\n\t\tm_ar.WriteChunk(PLT_DOM_ELEM_TYPE, dtype);\n\t\tm_ar.WriteChunk(PLT_DOM_PART_ID  ,   mid);\n\t\tm_ar.WriteChunk(PLT_DOM_ELEMS    , elementCount);\n\t}\n\tm_ar.EndChunk();\n\n\t// write the element list\n\tint n[FEElement::MAX_NODES + 1] = { 0 };\n\tm_ar.BeginChunk(PLT_DOM_ELEM_LIST);\n\t{\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFEShellElement& el = dom.Element(i);\n\t\t\tif (m_exportErodedElements || el.isActive())\n\t\t\t{\n\t\t\t\tn[0] = el.GetID();\n\t\t\t\tfor (int j = 0; j < ne; ++j) n[j + 1] = el.m_node[j];\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENT, n, ne + 1);\n\t\t\t}\n\t\t}\n\t}\n\tm_ar.EndChunk();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteBeamDomain(FEBeamDomain& dom)\n{\n\tint mid = dom.GetMaterial()->GetID();\n\tassert(mid > 0);\n\n\tint NE = dom.Elements();\n\tint elementCount = NE;\n\tif (m_exportErodedElements == false)\n\t{\n\t\telementCount = 0;\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tif (el.isActive()) elementCount++;\n\t\t}\n\t}\n\n\t// figure out element type\n\tint ne = 0;\n\tint dtype = 0;\n\tint etype = dom.GetElementType();\n\tswitch (etype)\n\t{\n\tcase FE_TRUSS  : ne = 2; dtype = PLT_ELEM_LINE2; break;\n\tcase FE_BEAM2G1: ne = 2; dtype = PLT_ELEM_LINE2; break;\n\tcase FE_BEAM2G2: ne = 2; dtype = PLT_ELEM_LINE2; break;\n\tcase FE_BEAM3G2: ne = 3; dtype = PLT_ELEM_LINE3; break;\n\tdefault:\n\t\tassert(false);\n\t\treturn;\n\t}\n\n\t// write the header\n\tm_ar.BeginChunk(PLT_DOMAIN_HDR);\n\t{\n\t\tm_ar.WriteChunk(PLT_DOM_ELEM_TYPE, dtype);\n\t\tm_ar.WriteChunk(PLT_DOM_PART_ID  ,   mid);\n\t\tm_ar.WriteChunk(PLT_DOM_ELEMS    , elementCount);\n\t}\n\tm_ar.EndChunk();\n\n\t// write the element list\n\tint n[5];\n\tm_ar.BeginChunk(PLT_DOM_ELEM_LIST);\n\t{\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tif (m_exportErodedElements || el.isActive())\n\t\t\t{\n\t\t\t\tn[0] = el.GetID();\n\t\t\t\tfor (int j = 0; j < ne; ++j) n[j + 1] = el.m_node[j];\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENT, n, ne + 1);\n\t\t\t}\n\t\t}\n\t}\n\tm_ar.EndChunk();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDiscreteDomain(FEDiscreteDomain& dom)\n{\n\tint mid = dom.GetMaterial()->GetID();\n\tassert(mid > 0);\n\n\tint NE = dom.Elements();\n\tint elementCount = NE;\n\tif (m_exportErodedElements == false)\n\t{\n\t\telementCount = 0;\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tif (el.isActive()) elementCount++;\n\t\t}\n\t}\n\n\t// figure out element type\n\tint ne = 2;\n\tint dtype = PLT_ELEM_LINE2;\n\n\t// write the header\n\tm_ar.BeginChunk(PLT_DOMAIN_HDR);\n\t{\n\t\tm_ar.WriteChunk(PLT_DOM_ELEM_TYPE, dtype);\n\t\tm_ar.WriteChunk(PLT_DOM_PART_ID  ,   mid);\n\t\tm_ar.WriteChunk(PLT_DOM_ELEMS    , elementCount);\n\t}\n\tm_ar.EndChunk();\n\n\t// write the element list\n\tint n[5];\n\tm_ar.BeginChunk(PLT_DOM_ELEM_LIST);\n\t{\n\t\tfor (int i=0; i<NE; ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tif (m_exportErodedElements || el.isActive())\n\t\t\t{\n\t\t\t\tn[0] = el.GetID();\n\t\t\t\tfor (int j = 0; j < ne; ++j) n[j + 1] = el.m_node[j];\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENT, n, ne + 1);\n\t\t\t}\n\t\t}\n\t}\n\tm_ar.EndChunk();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDomain2D(FEDomain2D& dom)\n{\n    int mid = dom.GetMaterial()->GetID();\n    assert(mid > 0);\n    \n\tint NE = dom.Elements();\n\tint elementCount = NE;\n\tif (m_exportErodedElements == false)\n\t{\n\t\telementCount = 0;\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tif (el.isActive()) elementCount++;\n\t\t}\n\t}\n\n    // figure out element type\n    int ne = 0;\n    int dtype = 0;\n\tint etype = dom.GetElementType();\n\tswitch (etype)\n    {\n        case FE2D_TRI3G1 : ne = 3; dtype = PLT_ELEM_TRI; break;\n        case FE2D_TRI6G3 : ne = 6; dtype = PLT_ELEM_TRI6; break;\n        case FE2D_QUAD4G4: ne = 4; dtype = PLT_ELEM_QUAD; break;\n        case FE2D_QUAD8G9: ne = 8; dtype = PLT_ELEM_QUAD8; break;\n        case FE2D_QUAD9G9: ne = 9; dtype = PLT_ELEM_QUAD9; break;\n\t\tdefault:\n\t\t\tassert(false);\n    }\n    \n    // write the header\n    m_ar.BeginChunk(PLT_DOMAIN_HDR);\n    {\n        m_ar.WriteChunk(PLT_DOM_ELEM_TYPE, dtype);\n        m_ar.WriteChunk(PLT_DOM_PART_ID  ,   mid);\n        m_ar.WriteChunk(PLT_DOM_ELEMS    , elementCount);\n    }\n    m_ar.EndChunk();\n    \n    // write the element list\n    int n[10];\n    m_ar.BeginChunk(PLT_DOM_ELEM_LIST);\n    {\n        for (int i=0; i<NE; ++i)\n        {\n            FEElement2D& el = dom.Element(i);\n\t\t\tif (m_exportErodedElements || el.isActive())\n\t\t\t{\n\t\t\t\tn[0] = el.GetID();\n\t\t\t\tfor (int j = 0; j < ne; ++j) n[j + 1] = el.m_node[j];\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENT, n, ne + 1);\n\t\t\t}\n        }\n    }\n    m_ar.EndChunk();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::BuildSurfaceTable()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFEMesh& mesh = fem.GetMesh();\n\tm_Surf.clear();\n\tfor (int ns = 0; ns < mesh.FacetSets(); ++ns)\n\t{\n\t\tFEFacetSet& s = mesh.FacetSet(ns);\n\t\tint NF = s.Faces();\n\n\t\t// find the max nodes\n\t\tint maxNodes = 0;\n\t\tfor (int i = 0; i < NF; ++i)\n\t\t{\n\t\t\tFEFacetSet::FACET& el = s.Face(i);\n\t\t\tif (el.ntype > maxNodes) maxNodes = el.ntype;\n\t\t}\n\n\t\tSurface surf;\n\t\tsurf.maxNodes = maxNodes;\n\t\tsurf.surf = &s;\n\t\tm_Surf.push_back(surf);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteSurfaceSection(FEMesh& m)\n{\n\tfor (int ns = 0; ns< m_Surf.size(); ++ns)\n\t{\n\t\tSurface& surf = m_Surf[ns];\n\t\tFEFacetSet& s = *surf.surf;\n\t\tint NF = s.Faces();\n\t\tint maxNodes = surf.maxNodes;\n\n\t\tm_ar.BeginChunk(PLT_SURFACE);\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_SURFACE_HDR);\n\t\t\t{\n\t\t\t\tint sid = ns+1;\n\t\t\t\tm_ar.WriteChunk(PLT_SURFACE_ID, sid);\n\t\t\t\tm_ar.WriteChunk(PLT_SURFACE_FACES, NF);\n\t\t\t\tm_ar.WriteChunk(PLT_SURFACE_NAME, s.GetName());\n\t\t\t\tm_ar.WriteChunk(PLT_SURFACE_MAX_FACET_NODES, maxNodes);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\n\t\t\tm_ar.BeginChunk(PLT_FACE_LIST);\n\t\t\t{\n\t\t\t\tint n[FEElement::MAX_NODES + 2];\n\t\t\t\tfor (int i=0; i<NF; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEFacetSet::FACET& f = s.Face(i);\n\t\t\t\t\tint nf = f.Nodes();\n\t\t\t\t\tn[0] = i+1;\n\t\t\t\t\tn[1] = nf;\n\t\t\t\t\tfor (int i=0; i<nf; ++i) n[i+2] = f.node[i];\n\t\t\t\t\tm_ar.WriteChunk(PLT_FACE, n, maxNodes + 2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\nvoid FEBioPlotFile::WriteEdgeSection(FEMesh& m)\n{\n\tfor (int i = 0; i < m.Edges(); ++i)\n\t{\n\t\tFEEdge& edge = m.Edge(i);\n\t\tint NE = edge.Elements();\n\t\tint maxNodes = 3;\n\n\t\tm_ar.BeginChunk(PLT_EDGE);\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_EDGE_HDR);\n\t\t\t{\n\t\t\t\tint id = i + 1;\n\t\t\t\tm_ar.WriteChunk(PLT_EDGE_ID, id);\n\t\t\t\tm_ar.WriteChunk(PLT_EDGE_LINES, NE);\n\t\t\t\tm_ar.WriteChunk(PLT_EDGE_NAME, edge.GetName());\n\t\t\t\tm_ar.WriteChunk(PLT_EDGE_MAX_NODES, maxNodes);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\n\t\t\tm_ar.BeginChunk(PLT_EDGE_LIST);\n\t\t\t{\n\t\t\t\tint n[FEElement::MAX_NODES + 2];\n\t\t\t\tfor (int j = 0; j < NE; ++j)\n\t\t\t\t{\n\t\t\t\t\tFELineElement& el = edge.Element(j);\n\t\t\t\t\tint ne = el.Nodes();\n\t\t\t\t\tn[0] = i + 1;\n\t\t\t\t\tn[1] = ne;\n\t\t\t\t\tfor (int i = 0; i < ne; ++i) n[i + 2] = el.m_node[i];\n\t\t\t\t\tm_ar.WriteChunk(PLT_LINE, n, maxNodes + 2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\nvoid FEBioPlotFile::WriteNodeSetSection(FEMesh& m)\n{\n\tfor (int ns = 0; ns < m.NodeSets(); ++ns)\n\t{\n\t\tFENodeSet& l = *m.NodeSet(ns);\n\t\tint nodes = l.Size();\n\t\tm_ar.BeginChunk(PLT_NODESET);\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_NODESET_HDR);\n\t\t\t{\n\t\t\t\tint nid = ns+1;\n\t\t\t\tm_ar.WriteChunk(PLT_NODESET_ID, nid);\n\t\t\t\tm_ar.WriteChunk(PLT_NODESET_SIZE, nodes);\n\t\t\t\tm_ar.WriteChunk(PLT_NODESET_NAME, l.GetName());\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\n\t\t\tstd::vector<int> nodeList(nodes);\n\t\t\tfor (int i = 0; i < nodes; ++i) nodeList[i] = l[i];\n\t\t\tm_ar.WriteChunk(PLT_NODESET_LIST, nodeList);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteElementSetSection(FEMesh& m)\n{\n\tfor (int n = 0; n < m.ElementSets(); ++n)\n\t{\n\t\tFEElementSet& l = m.ElementSet(n);\n\t\tint elems = l.Elements();\n\t\tm_ar.BeginChunk(PLT_ELEMENTSET);\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_ELEMENTSET_HDR);\n\t\t\t{\n\t\t\t\tint nid = n + 1;\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENTSET_ID, nid);\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENTSET_SIZE, elems);\n\t\t\t\tm_ar.WriteChunk(PLT_ELEMENTSET_NAME, l.GetName());\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\n\t\t\tstd::vector<int> elemList(elems);\n\t\t\tfor (int i = 0; i < elems; ++i) elemList[i] = l[i];\n\t\t\tm_ar.WriteChunk(PLT_ELEMENTSET_LIST, elemList);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteFacetSetSection(FEMesh& m)\n{\n\tfor (int ns = 0; ns < m.FacetSets(); ++ns)\n\t{\n\t\tFEFacetSet& surf = m.FacetSet(ns);\n\t\tint NF = surf.Faces();\n\n\t\tm_ar.BeginChunk(PLT_FACETSET);\n\t\t{\n\t\t\tint maxNodes = FEFacetSet::FACET::MAX_NODES;\n\n\t\t\tm_ar.BeginChunk(PLT_FACETSET_HDR);\n\t\t\t{\n\t\t\t\tint sid = ns + 1;\n\t\t\t\tm_ar.WriteChunk(PLT_FACETSET_ID, sid);\n\t\t\t\tm_ar.WriteChunk(PLT_FACETSET_SIZE, NF);\n\t\t\t\tm_ar.WriteChunk(PLT_FACETSET_NAME, surf.GetName());\n\t\t\t\tm_ar.WriteChunk(PLT_FACETSET_MAXNODES, maxNodes);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\n\t\t\tm_ar.BeginChunk(PLT_FACETSET_LIST);\n\t\t\t{\n\t\t\t\tint n[FEElement::MAX_NODES + 2];\n\t\t\t\tfor (int i = 0; i < NF; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEFacetSet::FACET& f = surf.Face(i);\n\t\t\t\t\tint nf = f.ntype;\t// this is the type and the nr. of nodes!\n\t\t\t\t\tn[0] = i + 1;\n\t\t\t\t\tn[1] = nf;\n\t\t\t\t\tfor (int i = 0; i < nf; ++i) n[i + 2] = f.node[i];\n\t\t\t\t\tm_ar.WriteChunk(PLT_FACET, n, maxNodes + 2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WritePartsSection(FEModel& fem)\n{\n\tint NMAT = fem.Materials();\n\tfor (int i=0; i<NMAT; ++i)\n\t{\n\t\tFEMaterial* pm = fem.GetMaterial(i);\n\t\tm_ar.BeginChunk(PLT_PART);\n\t\t{\n\t\t\tunsigned int nid = (unsigned int) pm->GetID();\n\t\t\tchar szname[STR_SIZE] = {0};\n\n\t\t\t// Make sure that the material name fits in the buffer\n\t\t\tstd::string name = pm->GetName();\n\t\t\tconst char* sz = name.c_str();\n\t\t\tint l = (int)strlen(sz);\n\t\t\tif (l >= STR_SIZE) l = STR_SIZE - 1;\n\t\t\tstrncpy(szname, sz, l);\n\n\t\t\t// write the material data\n\t\t\tm_ar.WriteChunk(PLT_PART_ID, nid);\n\t\t\tm_ar.WriteChunk(PLT_PART_NAME, szname, STR_SIZE);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteObjectsSection()\n{\n\tfor (int i = 0; i < m_Points.size(); ++i)\n\t{\n\t\tPointObject* po = GetPointObject(i);\n\t\tm_ar.BeginChunk(PLT_POINT_OBJECT);\n\t\t{\n\t\t\tWriteObject(po);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\tfor (int i = 0; i < m_Lines.size(); ++i)\n\t{\n\t\tLineObject* po = GetLineObject(i);\n\t\tm_ar.BeginChunk(PLT_LINE_OBJECT);\n\t\t{\n\t\t\tWriteObject(po);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteObject(PlotObject* po)\n{\n\tm_ar.WriteChunk(PLT_OBJECT_ID, po->m_id);\n\tm_ar.WriteChunk(PLT_OBJECT_NAME, po->m_name.c_str());\n\tm_ar.WriteChunk(PLT_OBJECT_TAG, po->m_tag);\n\n\tvec3d& r = po->m_pos;\n\tfloat f[3] = { (float)r.x, (float)r.y, (float)r.z };\n\tm_ar.WriteChunk(PLT_OBJECT_POS, f, 3);\n\n\tquatd q = po->m_rot;\n\tfloat a[4] = { (float)q.x, (float)q.y, (float)q.z, (float)q.w };\n\tm_ar.WriteChunk(PLT_OBJECT_ROT, a, 4);\n\n\tif (dynamic_cast<LineObject*>(po))\n\t{\n\t\tLineObject* pl = dynamic_cast<LineObject*>(po);\n\t\tvec3d r1 = pl->m_r1;\n\t\tvec3d r2 = pl->m_r2;\n\t\tfloat c[6] = { (float)r1.x, (float)r1.y, (float)r1.z, (float)r2.x, (float)r2.y, (float)r2.z };\n\t\tm_ar.WriteChunk(PLT_LINE_COORDS, c, 6);\n\t}\n\n\tlist<DICTIONARY_ITEM>::iterator it = po->m_data.begin();\n\tfor (int j = 0; j < po->m_data.size(); ++j, ++it)\n\t{\n\t\tm_ar.BeginChunk(PLT_OBJECT_DATA);\n\t\t{\n\t\t\tWriteDictionaryItem(*it);\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Write(float ftime, int flag)\n{\n\tfeLogDebug(\"writing to plot file; time = %lg; flag = %d\", ftime, flag);\n\n\tFEModel& fem = *GetFEModel();\n\tPlotFile::Dictionary& dic = GetDictionary();\n\n\t// compress these sections if requested\n\tm_ar.SetCompression(m_ncompress);\n\tm_ar.BeginChunk(PLT_STATE);\n\t{\n\t\t// state header\n\t\tm_ar.BeginChunk(PLT_STATE_HEADER);\n\t\t{\n\t\t\tm_ar.WriteChunk(PLT_STATE_HDR_TIME, ftime);\n\t\t\tm_ar.WriteChunk(PLT_STATE_STATUS, flag);\n\t\t}\n\t\tm_ar.EndChunk();\n\n\t\t// write the state flags of the mesh\n\t\tm_ar.BeginChunk(PLT_MESH_STATE);\n\t\t{\n\t\t\tWriteMeshState(fem.GetMesh());\n\t\t}\n\t\tm_ar.EndChunk();\n\n\t\tm_ar.BeginChunk(PLT_STATE_DATA);\n\t\t{\n\t\t\t// Global Data\n\t\t\tif (dic.GlobalVariables() > 0)\n\t\t\t{\n\t\t\t\tm_ar.BeginChunk(PLT_GLOBAL_DATA);\n\t\t\t\t{\n\t\t\t\t\tWriteGlobalData(fem);\n\t\t\t\t}\n\t\t\t\tm_ar.EndChunk();\n\t\t\t}\n\n\t\t\t// Node Data\n\t\t\tif (dic.NodalVariables() > 0)\n\t\t\t{\n\t\t\t\tm_ar.BeginChunk(PLT_NODE_DATA);\n\t\t\t\t{\n\t\t\t\t\tWriteNodeData(fem);\n\t\t\t\t}\n\t\t\t\tm_ar.EndChunk();\n\t\t\t}\n\n\t\t\t// Element Data\n\t\t\tif (dic.DomainVariables() > 0)\n\t\t\t{\n\t\t\t\tm_ar.BeginChunk(PLT_ELEMENT_DATA);\n\t\t\t\t{\n\t\t\t\t\tWriteDomainData(fem);\n\t\t\t\t}\n\t\t\t\tm_ar.EndChunk();\n\t\t\t}\n\n\t\t\t// surface data\n\t\t\tif (dic.SurfaceVariables() > 0)\n\t\t\t{\n\t\t\t\tm_ar.BeginChunk(PLT_FACE_DATA);\n\t\t\t\t{\n\t\t\t\t\tWriteSurfaceData(fem);\n\t\t\t\t}\n\t\t\t\tm_ar.EndChunk();\n\t\t\t}\n\n\t\t\t// edge data\n\t\t\tif (dic.EdgeVariables() > 0)\n\t\t\t{\n\t\t\t\tm_ar.BeginChunk(PLT_EDGE_DATA);\n\t\t\t\t{\n\t\t\t\t\tWriteEdgeData(fem);\n\t\t\t\t}\n\t\t\t\tm_ar.EndChunk();\n\t\t\t}\n\t\t}\n\t\tm_ar.EndChunk();\n\n\t\tif (m_Points.size() > 0)\n\t\t{\n\t\t\tm_ar.BeginChunk(PLT_OBJECTS_STATE);\n\t\t\t{\n\t\t\t\tWriteObjectsState();\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t}\n\tm_ar.EndChunk();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteGlobalData(FEModel& fem)\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tauto& globData = dic.GlobalVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = globData.begin();\n\tfor (int i = 0; i < (int)globData.size(); ++i, ++it)\n\t{\n\t\tm_ar.BeginChunk(PLT_STATE_VARIABLE);\n\t\t{\n\t\t\tunsigned int nid = i + 1;\n\t\t\tm_ar.WriteChunk(PLT_STATE_VAR_ID, nid);\n\t\t\tm_ar.BeginChunk(PLT_STATE_VAR_DATA);\n\t\t\t{\n\t\t\t\tif (it->m_psave) WriteGlobalDataField(fem, it->m_psave);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteNodeData(FEModel& fem)\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tauto& nodeData = dic.NodalVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = nodeData.begin();\n\tfor (int i=0; i<(int)nodeData.size(); ++i, ++it)\n\t{\n\t\tm_ar.BeginChunk(PLT_STATE_VARIABLE);\n\t\t{\n\t\t\tunsigned int nid = i+1;\n\t\t\tm_ar.WriteChunk(PLT_STATE_VAR_ID, nid);\n\t\t\tm_ar.BeginChunk(PLT_STATE_VAR_DATA);\n\t\t\t{\n\t\t\t\tif (it->m_psave) WriteNodeDataField(fem, it->m_psave);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDomainData(FEModel& fem)\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tauto& elemData = dic.DomainVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = elemData.begin();\n\tfor (int i=0; i<(int)elemData.size(); ++i, ++it)\n\t{\n\t\tm_ar.BeginChunk(PLT_STATE_VARIABLE);\n\t\t{\n\t\t\tunsigned int nid = i+1;\n\t\t\tm_ar.WriteChunk(PLT_STATE_VAR_ID, nid);\n\t\t\tm_ar.BeginChunk(PLT_STATE_VAR_DATA);\n\t\t\t{\n\t\t\t\tif (it->m_psave) WriteDomainDataField(fem, it->m_psave);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteSurfaceData(FEModel& fem)\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tauto& surfData = dic.SurfaceVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = surfData.begin();\n\tfor (int i=0; i<(int)surfData.size(); ++i, ++it)\n\t{\n\t\tm_ar.BeginChunk(PLT_STATE_VARIABLE);\n\t\t{\n\t\t\tunsigned int nid = i+1;\n\t\t\tm_ar.WriteChunk(PLT_STATE_VAR_ID, nid);\n\t\t\tm_ar.BeginChunk(PLT_STATE_VAR_DATA);\n\t\t\t{\n\t\t\t\tif (it->m_psave) WriteSurfaceDataField(fem, it->m_psave);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\nvoid FEBioPlotFile::WriteEdgeData(FEModel& fem)\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tauto& edgeData = dic.EdgeVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = edgeData.begin();\n\tfor (int i = 0; i < (int)edgeData.size(); ++i, ++it)\n\t{\n\t\tm_ar.BeginChunk(PLT_STATE_VARIABLE);\n\t\t{\n\t\t\tunsigned int nid = i + 1;\n\t\t\tm_ar.WriteChunk(PLT_STATE_VAR_ID, nid);\n\t\t\tm_ar.BeginChunk(PLT_STATE_VAR_DATA);\n\t\t\t{\n\t\t\t\tif (it->m_psave) WriteEdgeDataField(fem, it->m_psave);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteGlobalDataField(FEModel& fem, FEPlotData* pd)\n{\n\tint ndata = pd->VarSize(pd->DataType());\n\tFEDataStream a; a.reserve(ndata);\n\tif (pd->Save(a))\n\t{\n\t\t// pad mismatches\n\t\tassert(a.size() == ndata);\n\t\tif (a.size() != ndata) a.resize(ndata, 0.f);\n\t\tm_ar.WriteData(0, a.data());\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteNodeDataField(FEModel &fem, FEPlotData* pd)\n{\n\t// loop over all node sets\n\t// right now there is only one, namely the node set of all mesh nodes\n\t// so we just pass the mesh\n\tint ndata = pd->VarSize(pd->DataType());\n\n\tint N = fem.GetMesh().Nodes();\n\tFEDataStream a; a.reserve(ndata*N);\n\tif (pd->Save(fem.GetMesh(), a))\n\t{\n\t\t// pad mismatches\n\t\tassert(a.size() == N*ndata);\n\t\tif (a.size() != N * ndata) a.resize(N*ndata, 0.f);\n\t\tm_ar.WriteData(0, a.data());\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteSurfaceDataField(FEModel& fem, FEPlotData* pd)\n{\n\t// get the domain name (if any)\n\tstring domName;\n\tconst char* szdom = pd->GetDomainName();\n\tif (szdom) domName = szdom;\n\n\t// loop over all surfaces\n\tFEMesh& m = fem.GetMesh();\n\tfor (int i = 0; i<m_Surf.size(); ++i)\n\t{\n\t\tFEFacetSet& facetSet = *m_Surf[i].surf;\n\t\tint maxNodes = m_Surf[i].maxNodes;\n\t\tif (domName.empty() || (domName == facetSet.GetName()))\n\t\t{\n\t\t\t// Find the surface with the same name\n\t\t\tFESurface* surf = m.FindSurface(facetSet.GetName());\n\t\t\tif (surf)\n\t\t\t{\n\t\t\t\tFESurface& S = *surf;\n\n\t\t\t\t// Determine data size.\n\t\t\t\t// Note that for the FMT_MULT case we are \n\t\t\t\t// assuming 9 data entries per facet\n\t\t\t\t// regardless of the nr of nodes a facet really has\n\t\t\t\t// this is because for surfaces, all elements are not\n\t\t\t\t// necessarily of the same type\n\t\t\t\t// TODO: Fix the assumption of the FMT_MULT\n\t\t\t\tint datasize = pd->VarSize(pd->DataType());\n\t\t\t\tint nsize = datasize;\n\t\t\t\tswitch (pd->StorageFormat())\n\t\t\t\t{\n\t\t\t\tcase FMT_NODE: nsize *= S.Nodes(); break;\n\t\t\t\tcase FMT_ITEM: nsize *= S.Elements(); break;\n\t\t\t\tcase FMT_MULT: nsize *= maxNodes * S.Elements(); break;\n\t\t\t\tcase FMT_REGION:\n\t\t\t\t\t// one value per surface so nsize remains unchanged\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// save data\n\t\t\t\tFEDataStream a; a.reserve(nsize);\n\t\t\t\tif (pd->Save(S, a))\n\t\t\t\t{\n\t\t\t\t\t// in FEBio 3.0, the data streams are assumed to have no padding, but for now we still need to pad \n\t\t\t\t\t// the data stream before we write it to the file\n\t\t\t\t\tif (a.size() == nsize)\n\t\t\t\t\t{\n\t\t\t\t\t\t// assumed padding is already there, or not needed\n\t\t\t\t\t\tm_ar.WriteData(i + 1, a.data());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// this is only needed for FMT_MULT storage\n\t\t\t\t\t\tassert(pd->StorageFormat() == FMT_MULT);\n\n\t\t\t\t\t\t// add padding\n\t\t\t\t\t\tconst int M = maxNodes;\n\t\t\t\t\t\tint m = 0;\n\t\t\t\t\t\tFEDataStream b; b.assign(nsize, 0.f);\n\t\t\t\t\t\tfor (int n = 0; n < S.Elements(); ++n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFESurfaceElement& el = S.Element(n);\n\t\t\t\t\t\t\tint ne = el.Nodes();\n\t\t\t\t\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (int k = 0; k < datasize; ++k) b[n * M * datasize + j * datasize + k] = a[m++];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// write the padded data\n\t\t\t\t\t\tm_ar.WriteData(i + 1, b.data());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid FEBioPlotFile::WriteEdgeDataField(FEModel& fem, FEPlotData* pd)\n{\n\t// get the edge name (if any)\n\tstring domName;\n\tconst char* szdom = pd->GetDomainName();\n\tif (szdom) domName = szdom;\n\n\t// loop over all edges\n\tFEMesh& m = fem.GetMesh();\n\tfor (int i = 0; i < m.Edges(); ++i)\n\t{\n\t\tFEEdge& edge = m.Edge(i);\n\t\tint maxNodes = 3;\n\n\t\t// Determine data size.\n\t\t// Note that for the FMT_MULT case we are \n\t\t// assuming 9 data entries per facet\n\t\t// regardless of the nr of nodes a facet really has\n\t\t// this is because for surfaces, all elements are not\n\t\t// necessarily of the same type\n\t\t// TODO: Fix the assumption of the FMT_MULT\n\t\tint datasize = pd->VarSize(pd->DataType());\n\t\tint nsize = datasize;\n\t\tswitch (pd->StorageFormat())\n\t\t{\n\t\tcase FMT_NODE: nsize *= edge.Nodes(); break;\n\t\tcase FMT_ITEM: nsize *= edge.Elements(); break;\n\t\tcase FMT_MULT: nsize *= maxNodes * edge.Elements(); break;\n\t\tcase FMT_REGION:\n\t\t\t// one value per edge so nsize remains unchanged\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\n\t\t// save data\n\t\tFEDataStream a; a.reserve(nsize);\n\t\tif (pd->Save(edge, a))\n\t\t{\n\t\t\t// in FEBio 3.0, the data streams are assumed to have no padding, but for now we still need to pad \n\t\t\t// the data stream before we write it to the file\n\t\t\tif (a.size() == nsize)\n\t\t\t{\n\t\t\t\t// assumed padding is already there, or not needed\n\t\t\t\tm_ar.WriteData(i + 1, a.data());\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// this is only needed for FMT_MULT storage\n\t\t\t\tassert(pd->StorageFormat() == FMT_MULT);\n\n\t\t\t\t// add padding\n\t\t\t\tconst int M = maxNodes;\n\t\t\t\tint m = 0;\n\t\t\t\tFEDataStream b; b.assign(nsize, 0.f);\n\t\t\t\tfor (int n = 0; n < edge.Elements(); ++n)\n\t\t\t\t{\n\t\t\t\t\tFELineElement& el = edge.Element(n);\n\t\t\t\t\tint ne = el.Nodes();\n\t\t\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int k = 0; k < datasize; ++k) b[n * M * datasize + j * datasize + k] = a[m++];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// write the padded data\n\t\t\t\tm_ar.WriteData(i + 1, b.data());\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteDomainDataField(FEModel &fem, FEPlotData* pd)\n{\n\tFEMesh& m = fem.GetMesh();\n\tint ND = m.Domains();\n\n\t// if the item list is empty, store all domains\n\tvector<int> item = pd->GetItemList();\n\tif (item.empty())\n\t{\n\t\tfor (int i = 0; i<ND; ++i) item.push_back(i);\n\t}\n\n\t// allow plot data to prepare for save\n\tif (pd->PreSave() == false)\n\t{\n\t\tassert(false);\n\t\treturn;\n\t}\n\n\t// get the domain name (if any)\n\tstring domName;\n\tconst char* szdom = pd->GetDomainName();\n\tif (szdom) domName = szdom;\n\n\t// loop over all domains in the item list\n\tfor (int i = 0; i<ND; ++i)\n\t{\n\t\t// get the domain\n\t\tFEDomain& D = m.Domain(item[i]);\n\n\t\tif (domName.empty() || (D.GetName() == domName))\n\t\t{\n\t\t\t// calculate the size of the data vector\n\t\t\tint nsize = pd->VarSize(pd->DataType());\n\t\t\tswitch (pd->StorageFormat())\n\t\t\t{\n\t\t\tcase FMT_NODE: nsize *= D.Nodes(); break;\n\t\t\tcase FMT_ITEM: nsize *= D.Elements(); break;\n\t\t\tcase FMT_MULT:\n\t\t\t{\n\t\t\t\t// since all elements have the same type within a domain\n\t\t\t\t// we just grab the number of nodes of the first element \n\t\t\t\t// to figure out how much storage we need\n\t\t\t\tFEElement& e = D.ElementRef(0);\n\t\t\t\tint n = e.Nodes();\n\t\t\t\tnsize *= n * D.Elements();\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FMT_REGION:\n\t\t\t\t// one value for this domain so nsize remains unchanged\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t\tassert(nsize > 0);\n\n\t\t\t// fill data vector and save\n\t\t\tFEDataStream a;\n\t\t\ta.reserve(nsize);\n\t\t\tif (pd->Save(D, a))\n\t\t\t{\n\t\t\t\tassert(a.size() == nsize);\n\t\t\t\tm_ar.WriteData(item[i] + 1, a.data());\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::Append(const char *szfile)\n{\n\t// try to open the file\n\tif (m_ar.Open(szfile) == false) return false;\n\n\tFEModel* fem = GetFEModel();\n\tFEPlotDataStore& pltData = fem->GetPlotDataStore();\n\tSetCompression(pltData.GetPlotCompression());\n\n\t// add plot variables\n\tfor (int n = 0; n < pltData.PlotVariables(); ++n)\n\t{\n\t\tFEPlotVariable& vi = pltData.GetPlotVariable(n);\n\t\tconst std::string& varName = vi.Name();\n\t\tconst std::string& domName = vi.DomainName();\n\n\t\t// add the plot output variable\n\t\tif (AddVariable(varName.c_str(), vi.m_item, domName.c_str()) == false)\n\t\t{\n\t\t\tfeLog(\"FATAL ERROR: Output variable \\\"%s\\\" is not defined\\n\", varName.c_str());\n\t\t\tthrow \"FATAL ERROR\";\n\t\t}\n\t}\n\n\t// NOTE: Reading the dictionary rebuilds the plot variables too, but\n\t//       there is not enough data in the dictionary to do that correctly. \n\t//       So, we should probably rely on the data store, which gets serialized to the dump file.\n\t// open the root element\n\tbool bok = true;\n/*\tm_ar.OpenChunk();\n\tunsigned int nid = m_ar.GetChunkID();\n\tif (nid != PLT_ROOT) return false;\n\n\tbok = false;\n\twhile (m_ar.OpenChunk() == IO_OK)\n\t{\n\t\tnid = m_ar.GetChunkID();\n\t\tif (nid == PLT_DICTIONARY)\n\t\t{\n\t\t\t// read the dictionary\n\t\t\tbok = ReadDictionary();\n\t\t\tbreak;\n\t\t}\n\t\tm_ar.CloseChunk();\n\t}\n*/\n\t// close it again ...\n\tm_ar.Close();\n\n\t// rebuild the surface table\n\tBuildSurfaceTable();\n\n\t// ... and open for appending\n\tif (bok) return m_ar.Append(szfile);\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::ReadDictionary()\n{\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tdic.Clear();\n\n\twhile (m_ar.OpenChunk() == IO_OK)\n\t{\n\t\tunsigned int nid = m_ar.GetChunkID();\n\t\tswitch (nid)\n\t\t{\n\t\tcase PLT_DIC_GLOBAL: assert(false); return false;\n\t\tcase PLT_DIC_NODAL  : ReadDicList(); break;\n\t\tcase PLT_DIC_DOMAIN : ReadDicList(); break;\n\t\tcase PLT_DIC_SURFACE: ReadDicList(); break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\t\tm_ar.CloseChunk();\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioPlotFile::ReadDicList()\n{\n\tvector<int> l; // empty item list\n\twhile (m_ar.OpenChunk() == IO_OK)\n\t{\n\t\tunsigned int nid = m_ar.GetChunkID();\n\t\tif (nid == PLT_DIC_ITEM)\n\t\t{\n\t\t\twhile (m_ar.OpenChunk() == IO_OK)\n\t\t\t{\n\t\t\t\tunsigned int nid = m_ar.GetChunkID();\n\t\t\t\tif (nid == PLT_DIC_ITEM_NAME)\n\t\t\t\t{\n\t\t\t\t\tchar sz[STR_SIZE];\n\t\t\t\t\tm_ar.read(sz, STR_SIZE);\n\t\t\t\t\tAddVariable(sz, l);\n\t\t\t\t}\n\t\t\t\tm_ar.CloseChunk();\n\t\t\t}\n\t\t}\n\t\telse return false;\n\t\tm_ar.CloseChunk();\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteMeshState(FEMesh& mesh)\n{\n\tvector<unsigned int> flags;\n\tflags.reserve(mesh.Elements());\n\tint NDOM = mesh.Domains();\n\tfor (int i = 0; i < NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tif (m_exportErodedElements || el.isActive())\n\t\t\t{\n\t\t\t\tunsigned int status = el.status();\n\t\t\t\tflags.push_back(status);\n\t\t\t}\n\t\t}\n\t}\n\n\tm_ar.WriteChunk(PLT_ELEMENT_STATE, flags);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteObjectsState()\n{\n\tfor (int i = 0; i < PointObjects(); ++i)\n\t{\n\t\tPointObject* po = m_Points[i];\n\t\tm_ar.BeginChunk(PLT_POINT_OBJECT);\n\t\t{\n\t\t\tm_ar.WriteChunk(PLT_OBJECT_ID, po->m_id);\n\n\t\t\tvec3d r = po->m_pos;\n\t\t\tfloat f[3] = { (float)r.x, (float)r.y, (float)r.z };\n\t\t\tm_ar.WriteChunk(PLT_OBJECT_POS, f, 3);\n\n\t\t\tquatd q = po->m_rot;\n\t\t\tfloat a[4] = { (float)q.x, (float)q.y, (float)q.z, (float)q.w };\n\t\t\tm_ar.WriteChunk(PLT_OBJECT_ROT, a, 4);\n\n\t\t\tr = po->m_r;\n\t\t\tfloat c[3] = { (float)r.x, (float)r.y, (float)r.z };\n\t\t\tm_ar.WriteChunk(PLT_POINT_COORD, c, 3);\n\n\t\t\tm_ar.BeginChunk(PLT_OBJECT_DATA);\n\t\t\t{\n\t\t\t\tWriteObjectData(po);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n\n\tfor (int i = 0; i < m_Lines.size(); ++i)\n\t{\n\t\tLineObject* po = GetLineObject(i);\n\t\tm_ar.BeginChunk(PLT_LINE_OBJECT);\n\t\t{\n\t\t\tm_ar.WriteChunk(PLT_OBJECT_ID, po->m_id);\n\n\t\t\tvec3d r = po->m_pos;\n\t\t\tfloat f[3] = { (float)r.x, (float)r.y, (float)r.z };\n\t\t\tm_ar.WriteChunk(PLT_OBJECT_POS, f, 3);\n\n\t\t\tquatd q = po->m_rot;\n\t\t\tfloat a[4] = { (float)q.x, (float)q.y, (float)q.z, (float)q.w };\n\t\t\tm_ar.WriteChunk(PLT_OBJECT_ROT, a, 4);\n\n\t\t\tvec3d r1 = po->m_r1;\n\t\t\tvec3d r2 = po->m_r2;\n\t\t\tfloat c[6] = { (float)r1.x, (float)r1.y, (float)r1.z, (float)r2.x, (float)r2.y, (float)r2.z };\n\t\t\tm_ar.WriteChunk(PLT_LINE_COORDS, c, 6);\n\n\t\t\tm_ar.BeginChunk(PLT_OBJECT_DATA);\n\t\t\t{\n\t\t\t\tWriteObjectData(po);\n\t\t\t}\n\t\t\tm_ar.EndChunk();\n\t\t}\n\t\tm_ar.EndChunk();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioPlotFile::WriteObjectData(PlotObject* po)\n{\n\tlist<DICTIONARY_ITEM>::iterator it = po->m_data.begin();\n\tfor (int j = 0; j < po->m_data.size(); ++j, ++it)\n\t{\n\t\tassert(it->m_psave);\n\n\t\tFEPlotObjectData* pd = dynamic_cast<FEPlotObjectData*>(it->m_psave); assert(pd);\n\n\t\tFEDataStream a;\n\t\tif (pd->Save(po, a))\n\t\t{\n\t\t\tm_ar.WriteData(j, a.data());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioPlot/FEBioPlotFile.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"PlotFile.h\"\n#include \"PltArchive.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEShellDomain.h\"\n#include \"FECore/FEBeamDomain.h\"\n#include \"FECore/FEDiscreteDomain.h\"\n#include \"FECore/FEDomain2D.h\"\n#include <list>\n#include \"febioplot_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements the facilities to export FE data in the FEBio\n//! plot file format (version 3).\n//!\nclass FEBIOPLOT_API FEBioPlotFile : public PlotFile\n{\npublic:\n\t// file version\n\t// 3.2: added PLT_ELEMENTSET_SECTION\n\t// 3.3: node IDs are now stored in Node Section\n\t// 3.4: added PLT_ELEM_LINE3\n\t// 3.5: added PLT_EDGE_DATA\n\tenum { PLT_VERSION = 0x0035 };\n\n\t// file tags\n\tenum { \n\t\tPLT_ROOT\t\t\t\t\t\t= 0x01000000,\n\t\tPLT_HEADER\t\t\t\t\t\t= 0x01010000,\n\t\t\tPLT_HDR_VERSION\t\t\t\t= 0x01010001,\n//\t\t\tPLT_HDR_NODES\t\t\t\t= 0x01010002,\n//\t\t\tPLT_HDR_MAX_FACET_NODES\t\t= 0x01010003,\t// removed (redefined in seach SURFACE section)\n\t\t\tPLT_HDR_COMPRESSION\t\t\t= 0x01010004,\n\t\t\tPLT_HDR_AUTHOR\t\t\t\t= 0x01010005,\t// new in 2.0\n\t\t\tPLT_HDR_SOFTWARE\t\t\t= 0x01010006,\t// new in 2.0\n\t\t\tPLT_HDR_UNITS\t\t\t\t= 0x01010007,\t// new in 4.0\n\t\tPLT_DICTIONARY\t\t\t\t\t= 0x01020000,\n\t\t\tPLT_DIC_ITEM\t\t\t\t= 0x01020001,\n\t\t\tPLT_DIC_ITEM_TYPE\t\t\t= 0x01020002,\n\t\t\tPLT_DIC_ITEM_FMT\t\t\t= 0x01020003,\n\t\t\tPLT_DIC_ITEM_NAME\t\t\t= 0x01020004,\n\t\t\tPLT_DIC_ITEM_ARRAYSIZE\t\t= 0x01020005,\t// added in version 0x05\n\t\t\tPLT_DIC_ITEM_ARRAYNAME\t\t= 0x01020006,\t// added in version 0x05\n\t\t\tPLT_DIC_ITEM_UNITS\t\t\t= 0x01020007,\t// added in version 4.0\n\t\t\tPLT_DIC_GLOBAL\t\t\t\t= 0x01021000,\n//\t\t\tPLT_DIC_MATERIAL\t\t\t= 0x01022000,\t// this was removed\n\t\t\tPLT_DIC_NODAL\t\t\t\t= 0x01023000,\n\t\t\tPLT_DIC_DOMAIN\t\t\t\t= 0x01024000,\n\t\t\tPLT_DIC_SURFACE\t\t\t\t= 0x01025000,\n\t\t\tPLT_DIC_EDGE\t\t\t\t= 0x01026000,\n//\t\tPLT_MATERIALS\t\t\t\t\t= 0x01030000,\t\t// This was removed\n//\t\t\tPLT_MATERIAL\t\t\t\t= 0x01030001,\n//\t\t\tPLT_MAT_ID\t\t\t\t\t= 0x01030002,\n//\t\t\tPLT_MAT_NAME\t\t\t\t= 0x01030003,\n\t\tPLT_MESH\t\t\t\t\t\t= 0x01040000,\t\t// this was PLT_GEOMETRY\n\t\t\tPLT_NODE_SECTION\t\t\t= 0x01041000,\n\t\t\t\tPLT_NODE_HEADER\t\t\t= 0x01041100,\t\t// new in 2.0\n\t\t\t\t\tPLT_NODE_SIZE\t\t= 0x01041101,\t\t// new in 2.0\n\t\t\t\t\tPLT_NODE_DIM\t\t= 0x01041102,\t\t// new in 2.0\n\t\t\t\t\tPLT_NODE_NAME\t\t= 0x01041103,\t\t// new in 2.0\n\t\t\t\tPLT_NODE_COORDS\t\t\t= 0x01041200,\t\t// new in 2.0\n\t\t\tPLT_DOMAIN_SECTION\t\t\t= 0x01042000,\n\t\t\t\tPLT_DOMAIN\t\t\t\t= 0x01042100,\n\t\t\t\tPLT_DOMAIN_HDR\t\t\t= 0x01042101,\n\t\t\t\t\tPLT_DOM_ELEM_TYPE\t= 0x01042102,\n\t\t\t\t\tPLT_DOM_PART_ID\t\t= 0x01042103,\t\t// this was PLT_DOM_MAT_ID\n\t\t\t\t\tPLT_DOM_ELEMS\t\t= 0x01032104,\n\t\t\t\t\tPLT_DOM_NAME\t\t= 0x01032105,\n\t\t\t\tPLT_DOM_ELEM_LIST\t\t= 0x01042200,\n\t\t\t\t\tPLT_ELEMENT\t\t\t= 0x01042201,\n\t\t\tPLT_SURFACE_SECTION\t\t\t= 0x01043000,\n\t\t\t\tPLT_SURFACE\t\t\t\t= 0x01043100,\n\t\t\t\tPLT_SURFACE_HDR\t\t\t= 0x01043101,\n\t\t\t\t\tPLT_SURFACE_ID\t\t= 0x01043102,\n\t\t\t\t\tPLT_SURFACE_FACES\t= 0x01043103,\n\t\t\t\t\tPLT_SURFACE_NAME\t= 0x01043104,\n\t\t\t\t\tPLT_SURFACE_MAX_FACET_NODES = 0x01043105,\t// new in 2.0 (max number of nodes per facet)\n\t\t\t\tPLT_FACE_LIST\t\t\t= 0x01043200,\n\t\t\t\t\tPLT_FACE\t\t\t= 0x01043201,\n\t\t\tPLT_NODESET_SECTION\t\t\t= 0x01044000,\n\t\t\t\tPLT_NODESET\t\t\t\t= 0x01044100,\n\t\t\t\tPLT_NODESET_HDR\t\t\t= 0x01044101,\n\t\t\t\t\tPLT_NODESET_ID\t\t= 0x01044102,\n\t\t\t\t\tPLT_NODESET_NAME\t= 0x01044103,\n\t\t\t\t\tPLT_NODESET_SIZE\t= 0x01044104,\n\t\t\t\tPLT_NODESET_LIST\t\t= 0x01044200,\n\t\t\tPLT_PARTS_SECTION\t\t\t= 0x01045000,\t\t// new in 2.0\n\t\t\t\tPLT_PART\t\t\t\t= 0x01045100,\n\t\t\t\tPLT_PART_ID\t\t\t\t= 0x01045101,\n\t\t\t\tPLT_PART_NAME\t\t\t= 0x01045102,\n\n\t\t\t// element set section was added in 4.1\n\t\t\tPLT_ELEMENTSET_SECTION\t\t= 0x01046000,\n\t\t\t\tPLT_ELEMENTSET\t\t\t= 0x01046100,\n\t\t\t\tPLT_ELEMENTSET_HDR\t\t= 0x01046101,\n\t\t\t\t\tPLT_ELEMENTSET_ID\t= 0x01046102,\n\t\t\t\t\tPLT_ELEMENTSET_NAME\t= 0x01046103,\n\t\t\t\t\tPLT_ELEMENTSET_SIZE\t= 0x01046104,\n\t\t\t\tPLT_ELEMENTSET_LIST\t\t= 0x01046200,\n\n\t\t\t// facet set section was added in 4.1\n\t\t\tPLT_FACETSET_SECTION\t\t\t= 0x01047000,\n\t\t\t\tPLT_FACETSET\t\t\t\t= 0x01047100,\n\t\t\t\tPLT_FACETSET_HDR\t\t\t= 0x01047101,\n\t\t\t\t\tPLT_FACETSET_ID\t\t\t= 0x01047102,\n\t\t\t\t\tPLT_FACETSET_NAME\t\t= 0x01047103,\n\t\t\t\t\tPLT_FACETSET_SIZE\t\t= 0x01047104,\n\t\t\t\t\tPLT_FACETSET_MAXNODES\t= 0x01047105,\n\t\t\t\tPLT_FACETSET_LIST\t\t\t= 0x01047200,\n\t\t\t\t\tPLT_FACET\t\t\t\t= 0x01047201,\n\n\t\t\tPLT_EDGE_SECTION\t\t\t= 0x01048000,\n\t\t\t\tPLT_EDGE\t\t\t\t= 0x01048100,\n\t\t\t\t\tPLT_EDGE_HDR\t\t= 0x01048101,\n\t\t\t\t\tPLT_EDGE_ID\t\t\t= 0x01048102,\n\t\t\t\t\tPLT_EDGE_LINES\t\t= 0x01048103,\n\t\t\t\t\tPLT_EDGE_NAME\t\t= 0x01048104,\n\t\t\t\t\tPLT_EDGE_MAX_NODES\t= 0x01048105,\n\t\t\t\tPLT_EDGE_LIST\t\t\t= 0x01048200,\n\t\t\t\t\tPLT_LINE\t\t\t= 0x01048201,\n\n\t\t\t// plot objects were added in 3.0\n\t\t\tPLT_OBJECTS_SECTION\t\t\t= 0x01050000,\n\t\t\t\t\tPLT_OBJECT_ID\t\t= 0x01050001,\n\t\t\t\t\tPLT_OBJECT_NAME\t\t= 0x01050002,\n\t\t\t\t\tPLT_OBJECT_TAG\t\t= 0x01050003,\n\t\t\t\t\tPLT_OBJECT_POS\t\t= 0x01050004,\n\t\t\t\t\tPLT_OBJECT_ROT\t\t= 0x01050005,\n\t\t\t\t\tPLT_OBJECT_DATA\t\t= 0x01050006,\n\t\t\t\tPLT_POINT_OBJECT\t\t= 0x01051000,\n\t\t\t\t\tPLT_POINT_COORD\t\t= 0x01051001,\n\t\t\t\tPLT_LINE_OBJECT\t\t\t= 0x01052000,\n\t\t\t\t\tPLT_LINE_COORDS\t\t= 0x01052001,\n\n\t\tPLT_STATE\t\t\t\t\t\t= 0x02000000,\n\t\t\tPLT_STATE_HEADER\t\t\t= 0x02010000,\n\t\t\t\tPLT_STATE_HDR_ID\t\t= 0x02010001,\n\t\t\t\tPLT_STATE_HDR_TIME\t\t= 0x02010002,\n\t\t\t\tPLT_STATE_STATUS        = 0x02010003,\t// new in 3.1\n\t\t\tPLT_STATE_DATA\t\t\t\t= 0x02020000,\n\t\t\t\tPLT_STATE_VARIABLE\t\t= 0x02020001,\n\t\t\t\tPLT_STATE_VAR_ID\t\t= 0x02020002,\n\t\t\t\tPLT_STATE_VAR_DATA\t\t= 0x02020003,\n\t\t\t\tPLT_GLOBAL_DATA\t\t\t= 0x02020100,\n//\t\t\t\tPLT_MATERIAL_DATA\t\t= 0x02020200,\t\t// this was removed\n\t\t\t\tPLT_NODE_DATA\t\t\t= 0x02020300,\n\t\t\t\tPLT_ELEMENT_DATA\t\t= 0x02020400,\n\t\t\t\tPLT_FACE_DATA\t\t\t= 0x02020500,\n\t\t\t\tPLT_EDGE_DATA\t\t\t= 0x02020600,\n\t\t\tPLT_MESH_STATE\t\t\t\t= 0x02030000,\n\t\t\t\tPLT_ELEMENT_STATE\t\t= 0x02030001,\n\t\t\tPLT_OBJECTS_STATE\t\t\t= 0x02040000\n\t};\n\t// --- element types ---\n\tenum Elem_Type { \n\t\tPLT_ELEM_HEX, \n\t\tPLT_ELEM_PENTA, \n\t\tPLT_ELEM_TET4, \n\t\tPLT_ELEM_QUAD, \n\t\tPLT_ELEM_TRI, \n\t\tPLT_ELEM_LINE2, \n\t\tPLT_ELEM_HEX20, \n\t\tPLT_ELEM_TET10, \n\t\tPLT_ELEM_TET15, \n\t\tPLT_ELEM_HEX27,\n        PLT_ELEM_TRI6,\n        PLT_ELEM_QUAD8,\n        PLT_ELEM_QUAD9,\n        PLT_ELEM_PENTA15,\n\t\tPLT_ELEM_TET20,\n\t\tPLT_ELEM_TRI10,\n\t\tPLT_ELEM_PYRA5,\n\t\tPLT_ELEM_TET5,\n        PLT_ELEM_PYRA13,\n\t\tPLT_ELEM_LINE3\t\t\t// added in 3.4\n    };\n\n\tstruct Surface\n\t{\n\t\tint\t\t\tmaxNodes;\n\t\tFEFacetSet*\tsurf;\n\t};\n\n\tclass FEBIOPLOT_API PlotObject\n\t{\n\tpublic:\n\t\tPlotObject() {}\n\t\tvirtual ~PlotObject() {}\n\n\t\tvoid AddData(const char* szname, Var_Type type, FEPlotData* psave = nullptr);\n\n\tpublic:\n\t\tint\t\tm_id;\t// object ID\n\t\tint\t\tm_tag;\t// user tag\n\n\t\tvec3d\tm_pos;\t// object's position\n\t\tquatd\tm_rot;\t// object's orientation\n\n\t\tstd::string\tm_name;\t// object's name\n\n\t\tlist<DICTIONARY_ITEM>\tm_data;\n\t};\n\n\tclass PointObject : public PlotObject\n\t{\n\tpublic:\n\t\tPointObject() {}\n\n\tpublic:\n\t\tvec3d\tm_r;\t// point position\n\t};\n\n\tclass LineObject : public PlotObject\n\t{\n\tpublic:\n\t\tLineObject() {}\n\n\tpublic:\n\t\tvec3d\tm_r1;\t// point 1\n\t\tvec3d\tm_r2;\t// point 2\n\t};\n\npublic:\n\tFEBioPlotFile(FEModel* fem);\n\n\t//! Open the plot database\n\tbool Open(const char* szfile) override;\n\n\t//! Close the plot database\n\tvoid Close() override;\n\n\t//! Open for appending\n\tbool Append(const char* szfile) override;\n\n\t//! Write current FE state to plot database\n\tbool Write(float ftime, int flag = 0)  override;\n\n\t//! see if the plot file is valid\n\tbool IsValid() const override;\n\npublic:\n\t//! Set the compression level\n\tvoid SetCompression(int n);\n\n\t// Write a mesh section\n\tbool WriteMeshSection(FEModel& fem);\n\n\t//! set the software variable\n\tvoid SetSoftwareString(const std::string& softwareString);\n\npublic:\n\tint PointObjects();\n\tPointObject* GetPointObject(int i);\n\tPointObject* AddPointObject(const std::string& name);\n\n\tint LineObjects();\n\tLineObject* GetLineObject(int i);\n\tLineObject* AddLineObject(const std::string& name);\n\nprotected:\n\tbool WriteRoot      (FEModel& fem);\n\tbool WriteHeader    (FEModel& fem);\n\tbool WriteDictionary(FEModel& fem);\n\n\tvoid WriteDicList(list<DICTIONARY_ITEM>& dic);\n\tvoid WriteDictionaryItem(DICTIONARY_ITEM& it);\n\n\tvoid WriteNodeSection   (FEMesh& m);\n\tvoid WriteDomainSection (FEMesh& m);\n\tvoid WriteSurfaceSection(FEMesh& m);\n\tvoid WriteEdgeSection(FEMesh& m);\n\tvoid WriteNodeSetSection(FEMesh& m);\n\tvoid WriteElementSetSection(FEMesh& m);\n\tvoid WriteFacetSetSection(FEMesh& m);\n\tvoid WritePartsSection  (FEModel& fem);\n\tvoid WriteObjectsSection();\n\tvoid WriteObject(PlotObject* po);\n\n\tvoid WriteSolidDomain   (FESolidDomain&    dom);\n\tvoid WriteShellDomain   (FEShellDomain&    dom);\n\tvoid WriteBeamDomain    (FEBeamDomain&    dom);\n\tvoid WriteDiscreteDomain(FEDiscreteDomain& dom);\n    void WriteDomain2D      (FEDomain2D&       dom);\n\n\tvoid WriteGlobalData  (FEModel& fem);\n\tvoid WriteNodeData    (FEModel& fem);\n\tvoid WriteDomainData  (FEModel& fem);\n\tvoid WriteSurfaceData (FEModel& fem);\n\tvoid WriteEdgeData    (FEModel& fem);\n\tvoid WriteObjectsState();\n\tvoid WriteObjectData(PlotObject* po);\n\n\tvoid WriteGlobalDataField(FEModel& fem, FEPlotData* pd);\n\tvoid WriteNodeDataField(FEModel& fem, FEPlotData* pd);\n\tvoid WriteDomainDataField(FEModel& fem, FEPlotData* pd);\n\tvoid WriteSurfaceDataField(FEModel& fem, FEPlotData* pd);\n\tvoid WriteEdgeDataField(FEModel& fem, FEPlotData* pd);\n\n\tvoid WriteMeshState(FEMesh& mesh);\n\nprotected:\n\tbool ReadDictionary();\n\tbool ReadDicList();\n\tvoid BuildSurfaceTable();\n\tvoid Clear();\n\nprotected:\n\tPltArchive\tm_ar;\t// the data archive\n\tint\t\t\tm_ncompress;\t// compression level\n\tint\t\t\tm_meshesWritten;\t// nr of meshes written\n\tstring\t\tm_softwareString;\t// the software string\n\tbool\t\tm_exportUnitsFlag;\t// flag that indicates whether to write units\n\tbool\t\tm_exportErodedElements; // export the eroded elements or not \n\n\tstd::vector<Surface>\tm_Surf;\n\n\tstd::vector<PointObject*>\tm_Points;\n\tstd::vector<LineObject*>\t\tm_Lines;\n};\n\n//-----------------------------------------------------------------------------\nclass FEPlotObjectData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotObjectData)\n\npublic:\n\tFEPlotObjectData(FEModel* fem) : FEPlotData(fem) {}\n\n\tvirtual bool Save(FEBioPlotFile::PlotObject* po, FEDataStream& ar) = 0;\n};\n"
  },
  {
    "path": "FEBioPlot/PlotFile.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"PlotFile.h\"\n#include <FECore/FEPlotDataStore.h>\n#include <FECore/FEModel.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nPlotFile::PlotFile(FEModel* fem) : m_pfem(fem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nPlotFile::~PlotFile()\n{\n\tClose();\n\n\t// clear all arrays\n\tlist<DICTIONARY_ITEM>::iterator it = m_dic.m_Glob.begin();\n\tfor (int i = 0; i < (int)m_dic.m_Glob.size(); ++i, ++it) delete it->m_psave;\n\n\tit = m_dic.m_Mat.begin();\n\tfor (int i = 0; i < (int)m_dic.m_Mat.size(); ++i, ++it) delete it->m_psave;\n\n\tit = m_dic.m_Node.begin();\n\tfor (int i = 0; i < (int)m_dic.m_Node.size(); ++i, ++it) delete it->m_psave;\n\n\tit = m_dic.m_Elem.begin();\n\tfor (int i = 0; i < (int)m_dic.m_Elem.size(); ++i, ++it) delete it->m_psave;\n\n\tit = m_dic.m_Face.begin();\n\tfor (int i = 0; i < (int)m_dic.m_Face.size(); ++i, ++it) delete it->m_psave;\n}\n\n//-----------------------------------------------------------------------------\nvoid PlotFile::Close()\n{\n\t\n}\n\n//-----------------------------------------------------------------------------\nbool PlotFile::AddVariable(FEPlotData* ps, const char* szname)\n{\n\tvector<int> dummy;\n\tswitch (ps->RegionType())\n\t{\n\tcase FE_REGION_NODE: return m_dic.AddNodalVariable(ps, szname, dummy);\n\tcase FE_REGION_DOMAIN: return m_dic.AddDomainVariable(ps, szname, dummy);\n\tcase FE_REGION_SURFACE: return m_dic.AddSurfaceVariable(ps, szname, dummy);\n\tdefault:\n\t\tassert(false);\n\t\treturn false;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool PlotFile::AddVariable(const char* sz)\n{\n\tvector<int> dummy;\n\treturn AddVariable(sz, dummy);\n}\n\n//-----------------------------------------------------------------------------\nbool PlotFile::AddVariable(const char* sz, vector<int>& item, const char* szdom)\n{\n\treturn m_dic.AddVariable(GetFEModel(), sz, item, szdom);\n}\n\n//-----------------------------------------------------------------------------\n// build the dictionary\nvoid PlotFile::BuildDictionary()\n{\n\tFEPlotDataStore& pltData = GetFEModel()->GetPlotDataStore();\n\tfor (int n = 0; n < pltData.PlotVariables(); ++n)\n\t{\n\t\tFEPlotVariable& vi = pltData.GetPlotVariable(n);\n\t\tconst std::string& varName = vi.Name();\n\t\tconst std::string& domName = vi.DomainName();\n\n\t\t// add the plot output variable\n\t\tif (AddVariable(varName.c_str(), vi.m_item, domName.c_str()) == false)\n\t\t{\n\t\t\tfeLog(\"FATAL ERROR: Output variable \\\"%s\\\" is not defined\\n\", varName.c_str());\n\t\t\tthrow \"FATAL ERROR\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioPlot/PlotFile.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEMesh.h\"\n#include \"FECore/FEPlotData.h\"\n#include \"febioplot_api.h\"\n//-----------------------------------------------------------------------------\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n//! This class implements the facilities to write to a plot database. \n//!\nclass FEBIOPLOT_API PlotFile\n{\npublic:\n\t// size of name variables\n\tenum { STR_SIZE = 64 };\n\n\t// Dictionary entry\n\tclass DICTIONARY_ITEM\n\t{\n\tpublic:\n\t\tDICTIONARY_ITEM();\n\t\tDICTIONARY_ITEM(const DICTIONARY_ITEM& item);\n\n\tpublic:\n\t\tFEPlotData* m_psave;\n\t\tunsigned int\tm_ntype;\t// data type\n\t\tunsigned int\tm_nfmt;\t\t// storage format\n\t\tunsigned int\tm_arraySize;\t// size of arrays (only used by arrays)\n\t\tstd::vector<string>\tm_arrayNames;\t// names of array components (optional)\n\t\tchar\t\t\tm_szname[STR_SIZE];\n\t\tchar\t\t\tm_szunit[STR_SIZE];\n\t};\n\n\tclass Dictionary\n\t{\n\tpublic:\n\t\tbool AddVariable(FEModel* pfem, const char* szname, std::vector<int>& item, const char* szdom = \"\");\n\n\t\tint GlobalVariables() { return (int)m_Glob.size(); }\n\t\tint NodalVariables() { return (int)m_Node.size(); }\n\t\tint DomainVariables() { return (int)m_Elem.size(); }\n\t\tint SurfaceVariables() { return (int)m_Face.size(); }\n\t\tint EdgeVariables() { return (int)m_Edge.size(); }\n\n\t\tvoid Defaults(FEModel& fem);\n\n\t\tvoid Clear();\n\n\tpublic:\n\t\tlist<DICTIONARY_ITEM>& GlobalVariableList() { return m_Glob; }\n\t\tlist<DICTIONARY_ITEM>& MaterialVariableList() { return m_Mat; }\n\t\tlist<DICTIONARY_ITEM>& NodalVariableList() { return m_Node; }\n\t\tlist<DICTIONARY_ITEM>& DomainVariableList() { return m_Elem; }\n\t\tlist<DICTIONARY_ITEM>& SurfaceVariableList() { return m_Face; }\n\t\tlist<DICTIONARY_ITEM>& EdgeVariableList() { return m_Edge; }\n\n\tprotected:\n\t\tbool AddGlobalVariable(FEPlotData* ps, const char* szname);\n\t\tbool AddMaterialVariable(FEPlotData* ps, const char* szname);\n\t\tbool AddNodalVariable(FEPlotData* ps, const char* szname, std::vector<int>& item);\n\t\tbool AddDomainVariable(FEPlotData* ps, const char* szname, std::vector<int>& item);\n\t\tbool AddSurfaceVariable(FEPlotData* ps, const char* szname, std::vector<int>& item);\n\t\tbool AddEdgeVariable(FEPlotData* ps, const char* szname, std::vector<int>& item);\n\n\tprotected:\n\t\tlist<DICTIONARY_ITEM>\tm_Glob;\t\t// Global variables\n\t\tlist<DICTIONARY_ITEM>\tm_Mat;\t\t// Material variables\n\t\tlist<DICTIONARY_ITEM>\tm_Node;\t\t// Node variables\n\t\tlist<DICTIONARY_ITEM>\tm_Elem;\t\t// Domain variables\n\t\tlist<DICTIONARY_ITEM>\tm_Face;\t\t// Surface variables\n\t\tlist<DICTIONARY_ITEM>\tm_Edge;\t\t// Edge variables\n\n\t\tfriend class PlotFile;\n\t};\n\npublic:\n\t//! constructor\n\tPlotFile(FEModel* fem);\n\n\t//! descructor\n\tvirtual ~PlotFile();\n\n\t//! close the plot database\n\tvirtual void Close();\n\n\t//! Open the plot database\n\tvirtual bool Open(const char* szfile) = 0;\n\n\t//! Open for appending\n\tvirtual bool Append(const char* szfile) = 0;\n\n\t//! Write current FE state to plot database\n\tvirtual bool Write(float ftime, int flag = 0) = 0;\n\n\t//! see if the plot file is valid\n\tvirtual bool IsValid() const = 0;\n\n\tvirtual void Serialize(DumpStream& ar) {}\n\npublic:\n\tDictionary& GetDictionary() { return m_dic; }\n\n\tbool AddVariable(FEPlotData* ps, const char* szname);\n\nprotected:\n\tFEModel* GetFEModel() { return m_pfem; }\n\n\t// build the dictionary\n\tvoid BuildDictionary();\n\n\t//! Add a variable to the dictionary\n\tbool AddVariable(const char* sz);\n\tbool AddVariable(const char* sz, std::vector<int>& item, const char* szdom = \"\");\n\nprivate:\n\tDictionary\tm_dic;\t//!< dictionary\n\tFEModel*\tm_pfem;\t//!< pointer to FE model\n};\n"
  },
  {
    "path": "FEBioPlot/PltArchive.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"PltArchive.h\"\n#include <assert.h>\n\n#ifdef HAVE_ZLIB\n#include \"zlib.h\"\nstatic z_stream strm;\n#endif\n\n//=============================================================================\n// FileStream\n//=============================================================================\nFileStream::FileStream(FILE* fp, bool owner)\n{\n\tm_bufsize = 262144;\t// = 256K\n\tm_current = 0;\n\tm_buf  = new unsigned char[m_bufsize];\n\tm_pout = new unsigned char[m_bufsize];\n\tm_ncompress = 0;\n\tm_fp = fp;\n\tm_fileOwner = owner;\n}\n\nFileStream::~FileStream()\n{\n\tClose();\n\tdelete [] m_buf;\n\tdelete [] m_pout;\n\tm_buf = 0;\n\tm_pout = 0;\n}\n\nbool FileStream::Open(const char* szfile)\n{\n\tm_fp = fopen(szfile, \"rb\");\n\tif (m_fp == 0) return false;\n\treturn true;\n}\n\nbool FileStream::Append(const char* szfile)\n{\n\tm_fp = fopen(szfile, \"a+b\");\n\treturn (m_fp != 0);\n}\n\nbool FileStream::Create(const char* szfile)\n{\n\tm_fp = fopen(szfile, \"wb\");\n\treturn (m_fp != 0);\n}\n\nvoid FileStream::Close()\n{\n\tif (m_fp)\n\t{\n\t\tFlush();\n\t\tif (m_fileOwner) fclose(m_fp);\n\t}\n\tm_fp = 0;\n}\n\nvoid FileStream::BeginStreaming()\n{\n#ifdef HAVE_ZLIB\n\tif (m_ncompress)\n\t{\n\t\tstrm.zalloc = Z_NULL;\n\t\tstrm.zfree = Z_NULL;\n\t\tstrm.opaque = Z_NULL;\n\t\tdeflateInit(&strm, -1);\n\t}\n#endif\n}\n\nvoid FileStream::EndStreaming()\n{\n\tFlush();\n#ifdef HAVE_ZLIB\n\tif (m_ncompress)\n\t{\n\t\tstrm.avail_in = 0;\n\t\tstrm.next_in = 0;\n\n\t\t/* run deflate() on input until output buffer not full, finish\n\t\tcompression if all of source has been read in */\n\t\tdo {\n\t\t\tstrm.avail_out = m_bufsize;\n\t\t\tstrm.next_out = m_pout;\n\t\t\tint ret = deflate(&strm, Z_FINISH);    /* no bad return value */\n\t\t\tassert(ret != Z_STREAM_ERROR);  /* state not clobbered */\n\t\t\tint have = m_bufsize - strm.avail_out;\n\t\t\tfwrite(m_pout, 1, have, m_fp);\n\t\t} while (strm.avail_out == 0);\n\t\tassert(strm.avail_in == 0);     /* all input will be used */\n\n\t\t// all done\n\t\tdeflateEnd(&strm);\n\n\t\tfflush(m_fp);\n\t}\n#endif\n}\n\nvoid FileStream::Write(void* pd, size_t Size, size_t Count)\n{\n\tunsigned char* pdata = (unsigned char*) pd;\n\tsize_t nsize = Size*Count;\n\twhile (nsize > 0)\n\t{\n\t\tif (m_current + nsize < m_bufsize)\n\t\t{\n\t\t\tmemcpy(m_buf + m_current, pdata, nsize);\n\t\t\tm_current += nsize;\n\t\t\tnsize = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint nblock = m_bufsize - m_current;\n\t\t\tif (nblock>0) { memcpy(m_buf + m_current, pdata, nblock); m_current += nblock; }\n\t\t\tFlush();\n\t\t\tpdata += nblock;\n\t\t\tnsize -= nblock;\n\t\t}\n\t}\n}\n\nvoid FileStream::Flush()\n{\n#ifdef HAVE_ZLIB\n\tif (m_ncompress)\n\t{\n\t\tstrm.avail_in = m_current;\n\t\tstrm.next_in = m_buf;\n\n\t\t/* run deflate() on input until output buffer not full, finish\n\t\tcompression if all of source has been read in */\n\t\tdo {\n\t\t\tstrm.avail_out = m_bufsize;\n\t\t\tstrm.next_out = m_pout;\n\t\t\tint ret = deflate(&strm, Z_NO_FLUSH);    /* no bad return value */\n\t\t\tassert(ret != Z_STREAM_ERROR);  /* state not clobbered */\n\t\t\tint have = m_bufsize - strm.avail_out;\n\t\t\tfwrite(m_pout, 1, have, m_fp);\n\t\t} while (strm.avail_out == 0);\n\t\tassert(strm.avail_in == 0);     /* all input will be used */\n\t}\n\telse\n\t{\n\t\tif (m_fp) fwrite(m_buf, m_current, 1, m_fp);\n\t}\n#else\n\tif (m_fp) fwrite(m_buf, m_current, 1, m_fp);\n#endif\n\n\t// flush the file\n\tif (m_fp) fflush(m_fp);\n\n\t// reset current data pointer\n\tm_current = 0;\n}\n\nsize_t FileStream::read(void* pd, size_t Size, size_t Count)\n{\n\treturn fread(pd, Size, Count, m_fp);\n}\n\nlong FileStream::tell()\n{\n\treturn ftell(m_fp);\n}\n\nvoid FileStream::seek(long noff, int norigin)\n{\n\tfseek(m_fp, noff, norigin);\n}\n\n\n//=============================================================================\n// PltArchive\n//=============================================================================\n\nPltArchive::PltArchive()\n{\n\tm_fp = 0;\n\tm_pRoot = 0;\n\tm_pChunk = 0;\n\tm_bSaving = true;\n}\n\nPltArchive::~PltArchive()\n{\n\tClose();\n}\n\nvoid PltArchive::Close()\n{\n\tif (m_bSaving)\n\t{\n\t\tif (m_pRoot) Flush();\n\t}\n\telse \n\t{\n\t\twhile (m_Chunk.empty() == false) CloseChunk();\n\t\tm_bend = true;\n\t}\n\n\t// close the file\n\tif (m_fp)\n\t{\n\t\tm_fp->Close();\n\t\tdelete m_fp;\n\t\tm_fp = 0;\n\t}\n}\n\nvoid PltArchive::SetCompression(int n)\n{\n\tif (m_fp) m_fp->SetCompression(n);\n}\n\nvoid PltArchive::Flush()\n{\n\tif (m_fp && m_pRoot)\n\t{\n\t\tm_fp->BeginStreaming();\n\t\tm_pRoot->Write(m_fp);\n\t\tm_fp->EndStreaming();\n\t}\n\tdelete m_pRoot;\n\tm_pRoot = 0;\n\tm_pChunk = 0;\n}\n\nbool PltArchive::Create(const char* szfile)\n{\n\t// attempt to create the file\n\tassert(m_fp == 0);\n\tm_fp = new FileStream();\n\tif (m_fp->Create(szfile) == false) return false;\n\n\t// write the root tag \n\tunsigned int ntag = 0x00464542;\n\tm_fp->Write(&ntag, sizeof(int), 1);\n\n\tm_bSaving = true;\n\n\treturn true;\n}\n\nvoid PltArchive::BeginChunk(unsigned int id)\n{\n\tif (m_pRoot == 0)\n\t{\n\t\tm_pRoot = new OBranch(id);\n\t\tm_pChunk = m_pRoot;\n\t}\n\telse\n\t{\n\t\t// create a new branch\n\t\tOBranch* pbranch = new OBranch(id);\n\n\t\t// attach it to the current branch\n\t\tm_pChunk->AddChild(pbranch);\n\n\t\t// move the current branch pointer\n\t\tm_pChunk = pbranch;\n\t}\n}\n\nvoid PltArchive::EndChunk()\n{\n\tif (m_pChunk != m_pRoot)\n\t\tm_pChunk = m_pChunk->GetParent();\n\telse \n\t{\n\t\tFlush();\n\t}\n}\n\n\n\n//-----------------------------------------------------------------------------\n\nbool PltArchive::Open(const char* szfile)\n{\n\t// try to open the file\n\tassert(m_fp == 0);\n\tm_fp = new FileStream();\n\tif (m_fp->Open(szfile) == false) return false;\n\n\t// read the root tag\n\tunsigned int ntag;\n\tm_fp->read(&ntag, sizeof(int), 1);\n\tif (ntag != 0x00464542) { Close(); return false; }\n\n\tm_bSaving = false;\n\tm_bend = false;\n\t\n\treturn true;\n}\n\nbool PltArchive::Append(const char* szfile)\n{\n\t// reopen the plot file for appending\n\tassert(m_fp == 0);\n\tm_fp = new FileStream();\n\tif (m_fp->Append(szfile) == false) return false;\n\tm_bSaving = true;\n\treturn true;\n}\n\nint PltArchive::OpenChunk()\n{\n\t// see if the end flag was set\n\t// in that case we first need to clear the flag\n\tif (m_bend)\n\t{\n\t\tm_bend = false;\n\t\treturn IO_END;\n\t}\n\n\t// create a new chunk\n\tCHUNK* pc = new CHUNK;\n\n\t// read the chunk ID\n\tread(pc->id);\n\n\t// read the chunk size\n\tread(pc->nsize);\n\n\tif (pc->nsize == 0) m_bend = true;\n\n\t// record the position\n\tpc->lpos = m_fp->tell();\n\n\t// add it to the stack\n\tm_Chunk.push(pc);\n\n\treturn IO_OK;\n}\n\nvoid PltArchive::CloseChunk()\n{\n\t// pop the last chunk\n\tCHUNK* pc = m_Chunk.top(); m_Chunk.pop();\n\n\t// get the current file position\n\tlong lpos = m_fp->tell();\n\n\t// calculate the offset to the end of the chunk\n\tint noff = pc->nsize - (lpos - pc->lpos);\n\n\t// skip any remaining part in the chunk\n\t// I wonder if this can really happen\n\tif (noff != 0)\n\t{\n\t\tm_fp->seek(noff, SEEK_CUR);\n\t\tlpos = m_fp->tell();\n\t}\n\n\t// delete this chunk\n\tdelete pc;\n\n\t// take a peek at the parent\n\tif (m_Chunk.empty())\n\t{\n\t\t// we just deleted the root chunk\n\t\tm_bend = true;\n\t}\n\telse\n\t{\n\t\tpc = m_Chunk.top();\n\t\tint noff = pc->nsize - (lpos - pc->lpos);\n\t\tif (noff == 0) m_bend = true;\n\t}\n}\n\nunsigned int PltArchive::GetChunkID()\n{\n\tCHUNK* pc = m_Chunk.top();\n\tassert(pc);\n\treturn pc->id;\n}\n"
  },
  {
    "path": "FEBioPlot/PltArchive.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <assert.h>\n#include <string>\n#include <string.h>\n#include <stdio.h>\n#include <list>\n#include <vector>\n#include <stack>\n#include \"febioplot_api.h\"\n\n//-----------------------------------------------------------------------------\nenum IOResult { IO_ERROR, IO_OK, IO_END };\n\n//-----------------------------------------------------------------------------\n//! helper class for writing buffered data to file\nclass FEBIOPLOT_API FileStream\n{\npublic:\n\tFileStream(FILE* fp = nullptr, bool owner = true);\n\t~FileStream();\n\n\tbool Create(const char* szfile);\n\tbool Open(const char* szfile);\n\tbool Append(const char* szfile);\n\tvoid Close();\n\n\tvoid Write(void* pd, size_t Size, size_t Count);\n\n\tvoid Flush();\n\n\t// \\todo temporary reading functions. Needs to be replaced with buffered functions\n\tsize_t read(void* pd, size_t Size, size_t Count);\n\tlong tell();\n\tvoid seek(long noff, int norigin);\n\n\tvoid BeginStreaming();\n\tvoid EndStreaming();\n\n\tvoid SetCompression(int n) { m_ncompress = n; }\n\n\tFILE* FilePtr() { return m_fp; }\n\n\tbool IsValid() { return (m_fp != nullptr); }\n\nprivate:\n\tFILE*\tm_fp;\n\tbool\tm_fileOwner;\n\tsize_t\tm_bufsize;\t\t//!< buffer size\n\tsize_t\tm_current;\t\t//!< current index\n\tunsigned char*\tm_buf;\t//!< buffer\n\tunsigned char*\tm_pout;\t//!< temp buffer when writing\n\tint\t\tm_ncompress;\t//!< compression level\n};\n\nclass OBranch;\n\nclass OChunk\n{\npublic:\n\tOChunk(unsigned int nid) { m_nID = nid; m_pParent = 0; }\n\tvirtual ~OChunk(){}\n\n\tunsigned int GetID() { return m_nID; }\n\n\tvirtual void Write(FileStream* fp) = 0;\n\tvirtual int Size() = 0;\n\n\tvoid SetParent(OBranch* pparent) { m_pParent = pparent; }\n\tOBranch* GetParent() { return m_pParent; }\n\nprotected:\n\tint\t\t\tm_nID;\n\tOBranch*\tm_pParent;\n};\n\nclass OBranch : public OChunk\n{\t\npublic:\n\tOBranch(unsigned int nid) : OChunk(nid) {}\n\t~OBranch()\n\t{\n\t\tstd::list<OChunk*>::iterator pc;\n\t\tfor (pc = m_child.begin(); pc != m_child.end(); ++pc) delete (*pc);\n\t\tm_child.clear();\n\t}\n\n\tint Size()\n\t{\n\t\tint nsize = 0;\n\t\tstd::list<OChunk*>::iterator pc;\n\t\tfor (pc = m_child.begin(); pc != m_child.end(); ++pc) nsize += (*pc)->Size() + 2*sizeof(unsigned int);\n\t\treturn nsize;\n\t}\n\n\tvoid Write(FileStream* fp)\n\t{\n\t\tfp->Write(&m_nID  , sizeof(unsigned int), 1);\n\n\t\tunsigned int nsize = Size();\n\t\tfp->Write(&nsize, sizeof(unsigned int), 1);\n\n\t\tstd::list<OChunk*>::iterator pc;\n\t\tfor (pc = m_child.begin(); pc != m_child.end(); ++pc) (*pc)->Write(fp);\n\t}\n\n\tvoid AddChild(OChunk* pc) { m_child.push_back(pc); pc->SetParent(this); }\n\nprotected:\n\tstd::list<OChunk*>\tm_child;\n};\n\ntemplate <typename T>\nclass OLeaf : public OChunk\n{\npublic:\n\tOLeaf(unsigned int nid, const T& d) : OChunk(nid) { m_d = d; }\n\n\tint Size() { return sizeof(T); }\n\n\tvoid Write(FileStream* fp)\n\t{\n\t\tfp->Write(&m_nID  , sizeof(unsigned int), 1);\n\t\tunsigned int nsize = sizeof(T);\n\t\tfp->Write(&nsize, sizeof(unsigned int), 1);\n\t\tfp->Write(&m_d, sizeof(T), 1);\n\t}\n\nprotected:\n\tT\tm_d;\n};\n\ntemplate <typename T>\nclass OLeaf<T*> : public OChunk\n{\npublic:\n\tOLeaf(unsigned int nid, const T* pd, int nsize) : OChunk(nid)\n\t{\n\t\tassert(nsize > 0);\n\t\tm_pd = new T[nsize];\n\t\tmemcpy(m_pd, pd, sizeof(T)*nsize);\n\t\tm_nsize = nsize;\n\t}\n\t~OLeaf() { delete m_pd; }\n\n\tint Size() { return sizeof(T)*m_nsize; }\n\tvoid Write(FileStream* fp)\n\t{\n\t\tfp->Write(&m_nID , sizeof(unsigned int), 1);\n\t\tunsigned int nsize = Size();\n\t\tfp->Write(&nsize , sizeof(unsigned int), 1);\n\t\tfp->Write(m_pd   , sizeof(T), m_nsize);\n\t}\n\nprotected:\n\tT*\t\tm_pd;\n\tint\t\tm_nsize;\n};\n\ntemplate <>\nclass OLeaf<const char*> : public OChunk\n{\npublic:\n\tOLeaf(unsigned int nid, const char* sz) : OChunk(nid)\n\t{\n\t\tint l = (int)strlen(sz);\n\t\tm_psz = new char[l+1];\n\t\tmemcpy(m_psz, sz, l+1);\n\t}\n\t~OLeaf() { delete m_psz; }\n\n\tint Size() { return (int)strlen(m_psz)+sizeof(int); }\n\tvoid Write(FileStream* fp)\n\t{\n\t\tfp->Write(&m_nID , sizeof(unsigned int), 1);\n\t\tunsigned int nsize = Size();\n\t\tfp->Write(&nsize , sizeof(unsigned int), 1);\n\t\tint l = nsize - sizeof(int);\n\t\tfp->Write(&l, sizeof(int), 1);\n\t\tfp->Write(m_psz, sizeof(char), l);\n\t}\n\nprotected:\n\tchar*\tm_psz;\n};\n\ntemplate <typename T>\nclass OLeaf<std::vector<T> > : public OChunk\n{\npublic:\n\tOLeaf(unsigned int nid, const std::vector<T>& a) : OChunk(nid), m_pd(nullptr)\n\t{\n\t\tm_nsize = (int)a.size();\n\t\tif (m_nsize > 0)\n\t\t{\n\t\t\tm_pd = new T[m_nsize];\n\t\t\tmemcpy(m_pd, &a[0], sizeof(T) * m_nsize);\n\t\t}\n\t}\n\t~OLeaf() { delete m_pd; }\n\n\tint Size() { return sizeof(T)*m_nsize; }\n\tvoid Write(FileStream* fp)\n\t{\n\t\tfp->Write(&m_nID , sizeof(unsigned int), 1);\n\t\tunsigned int nsize = Size();\n\t\tfp->Write(&nsize , sizeof(unsigned int), 1);\n\t\tif (m_pd && (nsize > 0)) fp->Write(m_pd   , sizeof(T), m_nsize);\n\t}\n\nprotected:\n\tT*\t\tm_pd;\n\tint\t\tm_nsize;\n};\n\n//-----------------------------------------------------------------------------\n//! Implementation of an archiving class. Will be used by the FEBioPlotFile class.\nclass FEBIOPLOT_API PltArchive\n{\nprotected:\n\t// CHUNK data structure for reading\n\tstruct CHUNK\n\t{\n\t\tunsigned int\tid;\t\t// chunk ID\n\t\tunsigned int\tlpos;\t// file position\n\t\tunsigned int\tnsize;\t// size of chunk\n\t};\n\npublic:\n\t//! constructor\n\tPltArchive();\n\n\t//! destructor\n\t~PltArchive();\n\n\t// Close archive\n\tvoid Close();\n\n\t// flush data to file\n\tvoid Flush();\n\npublic:\n\t// --- Writing ---\n\n\t// Open for writing\n\tbool Create(const char* szfile);\n\n\t// begin a chunk\n\tvoid BeginChunk(unsigned int id);\n\n\t// end a chunck\n\tvoid EndChunk();\n\n\ttemplate <typename T> void WriteChunk(unsigned int nid, T& o)\n\t{\n\t\tm_pChunk->AddChild(new OLeaf<T>(nid, o));\n\t}\n\n\tvoid WriteChunk(unsigned int nid, const char* sz)\n\t{\n\t\tm_pChunk->AddChild(new OLeaf<const char*>(nid, sz));\n\t}\n\n\tvoid WriteChunk(unsigned int nid, const std::string& s)\n\t{\n\t\tm_pChunk->AddChild(new OLeaf<const char*>(nid, s.c_str()));\n\t}\n\n\ttemplate <typename T> void WriteChunk(unsigned int nid, T* po, int n)\n\t{\n\t\tm_pChunk->AddChild(new OLeaf<T*>(nid, po, n));\n\t}\n\n\ttemplate <typename T> void WriteChunk(unsigned int nid, std::vector<T>& a)\n\t{\n\t\tm_pChunk->AddChild(new OLeaf<std::vector<T> >(nid, a));\n\t}\n\n\tvoid WriteData(int nid, std::vector<float>& data)\n\t{\n\t\tWriteChunk(nid, data);\n\t}\n\npublic:\n\t// --- Reading ---\n\n\t// Open for reading\n\tbool Open(const char* sfile);\n\tbool Append(const char* szfile);\n\n\t// Open a chunk\n\tint OpenChunk();\n\n\t// Get the current chunk ID\n\tunsigned int GetChunkID();\n\n\t// Close a chunk\n\tvoid CloseChunk();\n\n\t// input functions\n\tIOResult read(char&   c) { size_t nr = m_fp->read(&c, sizeof(char  ), 1); if (nr != 1) return IO_ERROR; return IO_OK; }\n\tIOResult read(int&    n) { size_t nr = m_fp->read(&n, sizeof(int   ), 1); if (nr != 1) return IO_ERROR; return IO_OK; }\n\tIOResult read(bool&   b) { size_t nr = m_fp->read(&b, sizeof(bool  ), 1); if (nr != 1) return IO_ERROR; return IO_OK; }\n\tIOResult read(float&  f) { size_t nr = m_fp->read(&f, sizeof(float ), 1); if (nr != 1) return IO_ERROR; return IO_OK; }\n\tIOResult read(double& g) { size_t nr = m_fp->read(&g, sizeof(double), 1); if (nr != 1) return IO_ERROR; return IO_OK; }\n\n\tIOResult read(unsigned int& n) { size_t nr = m_fp->read(&n, sizeof(unsigned int), 1); if (nr != 1) return IO_ERROR; return IO_OK; }\n\n\tIOResult read(char*   pc, int n) { size_t nr = m_fp->read(pc, sizeof(char  ), n); if (nr != n) return IO_ERROR; return IO_OK; }\n\tIOResult read(int*    pi, int n) { size_t nr = m_fp->read(pi, sizeof(int   ), n); if (nr != n) return IO_ERROR; return IO_OK; }\n\tIOResult read(bool*   pb, int n) { size_t nr = m_fp->read(pb, sizeof(bool  ), n); if (nr != n) return IO_ERROR; return IO_OK; }\n\tIOResult read(float*  pf, int n) { size_t nr = m_fp->read(pf, sizeof(float ), n); if (nr != n) return IO_ERROR; return IO_OK; }\n\tIOResult read(double* pg, int n) { size_t nr = m_fp->read(pg, sizeof(double), n); if (nr != n) return IO_ERROR; return IO_OK; }\n\n\tIOResult read(char* sz)\n\t{\n\t\tIOResult ret;\n\t\tint l;\n\t\tret = read(l); if (ret != IO_OK) return ret;\n\t\tsize_t nr = m_fp->read(sz, 1, l); if (nr != l) return IO_ERROR;\n\t\tsz[l] = 0;\n\t\treturn IO_OK;\n\t}\n\n\tvoid SetCompression(int n);\n\n\tbool IsValid() const { return (m_fp != 0); }\n\nprotected:\n\tFileStream*\tm_fp;\t\t// pointer to file stream\n\tbool\t\tm_bSaving;\t// read or write mode?\n\n\t// write data\n\tOBranch*\tm_pRoot;\t// chunk tree root\n\tOBranch*\tm_pChunk;\t// current chunk\n\n\t// read data\n\tbool\t\t\tm_bend;\t\t// chunk end flag\n\tstd::stack<CHUNK*>\tm_Chunk;\n};\n"
  },
  {
    "path": "FEBioPlot/VTKPlotFile.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"VTKPlotFile.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEPlotDataStore.h>\n#include <FECore/FEDomain.h>\n#include <sstream>\n\nenum VTK_CELLTYPE {\n\tVTK_VERTEX = 1,\n\tVTK_POLY_VERTEX = 2,\n\tVTK_LINE = 3,\n\tVTK_POLY_LINE = 4,\n\tVTK_TRIANGLE = 5,\n\tVTK_TRIANGLE_STRIP = 6,\n\tVTK_POLYGON = 7,\n\tVTK_PIXEL = 8,\n\tVTK_QUAD = 9,\n\tVTK_TETRA = 10,\n\tVTK_VOXEL = 11,\n\tVTK_HEXAHEDRON = 12,\n\tVTK_WEDGE = 13,\n\tVTK_PYRAMID = 14,\n\tVTK_QUADRATIC_EDGE = 21,\n\tVTK_QUADRATIC_TRIANGLE = 22,\n\tVTK_QUADRATIC_QUAD = 23,\n\tVTK_QUADRATIC_TETRA = 24,\n\tVTK_QUADRATIC_HEXAHEDRON = 25,\n\tVTK_QUADRATIC_WEDGE = 26,\n\tVTK_QUADRATIC_PYRAMID = 27\n};\n\n\nVTKPlotFile::VTKPlotFile(FEModel* fem) : PlotFile(fem)\n{\n\tm_fp = nullptr;\n\tm_count = 0;\n\tm_valid = false;\n}\n\n//! Open the plot database\nbool VTKPlotFile::Open(const char* szfile)\n{\n\tm_filename = szfile;\n\tsize_t n = m_filename.rfind('.');\n\tif (n != std::string::npos) m_filename.erase(n, std::string::npos);\n\n\tBuildDictionary();\n\tm_valid = true;\n\treturn true;\n}\n\n//! Open for appending\nbool VTKPlotFile::Append(const char* szfile)\n{\n\tm_filename = szfile;\n\tsize_t n = m_filename.rfind('.');\n\tif (n != std::string::npos) m_filename.erase(n, std::string::npos);\n\n\tBuildDictionary();\n\tm_valid = true;\n\treturn true;\n}\n\n//! see if the plot file is valid\nbool VTKPlotFile::IsValid() const\n{\n\treturn m_valid;\n}\n\nvoid VTKPlotFile::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar& m_count;\n}\n\n//! Write current FE state to plot database\nbool VTKPlotFile::Write(float ftime, int flag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tstd::stringstream ss;\n\tss << m_filename << \".\" << m_count++ << \".vtk\";\n\tstring fileName = ss.str();\n\t\n\tm_fp = fopen(fileName.c_str(), \"wt\");\n\tif (m_fp == nullptr) return false;\n\n\tWriteHeader();\n\tWritePoints();\n\tWriteCells();\n\tWritePointData();\n\tWriteCellData();\n\n\tfclose(m_fp);\n\tm_fp = nullptr;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid VTKPlotFile::WriteHeader()\n{\n\tFEModel& fem = *GetFEModel();\n\tfprintf(m_fp, \"%s\\n\", \"# vtk DataFile Version 3.0\");\n\tfprintf(m_fp, \"%s %g\\n\", \"time\", fem.GetCurrentTime());\n\tfprintf(m_fp, \"%s\\n\", \"ASCII\");\n\tfprintf(m_fp, \"%s\\n\", \"DATASET UNSTRUCTURED_GRID\");\n}\n\n//-----------------------------------------------------------------------------\nvoid VTKPlotFile::WritePoints()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\tint nodes = m.Nodes();\n\tfprintf(m_fp, \"POINTS %d float\\n\", nodes);\n\tfor (int j = 0; j < nodes; j += 3)\n\t{\n\t\tfor (int k = 0; k < 3 && j + k < nodes; k++)\n\t\t{\n\t\t\tFENode& nd = m.Node(j + k);\n\t\t\tvec3d& r = nd.m_r0;\n\t\t\tfprintf(m_fp, \"%lg %lg %lg \", r.x, r.y, r.z);\n\t\t}\n\t\tfprintf(m_fp, \"\\n\");\n\t}\n\tfprintf(m_fp, \"%s\\n\", \"\");\n}\n\n//-----------------------------------------------------------------------------\nvoid VTKPlotFile::WriteCells()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\tint NE = m.Elements();\n    int nsize = 0;\n\tfor (int j = 0; j<NE; ++j)\n        nsize += m.Element(j)->Nodes() + 1;\n\n\t// Write CELLS\n    fprintf(m_fp, \"CELLS %d %d\\n\", NE, nsize);\n    for (int j=0; j<NE; ++j)\n    {\n\t\tFEElement& el = *m.Element(j);\n        fprintf(m_fp, \"%d \", el.Nodes());\n        for (int k=0; k<el.Nodes(); ++k) fprintf(m_fp, \"%d \", el.m_node[k]);\n        fprintf(m_fp, \"\\n\");\n    }\n        \n\t// Write CELL_TYPES\n    fprintf(m_fp, \"\\nCELL_TYPES %d\\n\", NE);\n\tfor (int j = 0; j<m.Elements(); ++j)\n    {\n\t\tFEElement& el = *m.Element(j);\n\t\tint vtk_type;\n        switch (el.Shape()) {\n            case ET_HEX8   : vtk_type = VTK_HEXAHEDRON; break;\n            case ET_TET4   : vtk_type = VTK_TETRA; break;\n            case ET_PENTA6 : vtk_type = VTK_WEDGE; break;\n            case ET_PYRA5  : vtk_type = VTK_PYRAMID; break;\n            case ET_QUAD4  : vtk_type = VTK_QUAD; break;\n            case ET_TRI3   : vtk_type = VTK_TRIANGLE; break;\n            case ET_TRUSS2 : vtk_type = VTK_LINE; break;\n            case ET_HEX20  : vtk_type = VTK_QUADRATIC_HEXAHEDRON; break;\n            case ET_QUAD8  : vtk_type = VTK_QUADRATIC_QUAD; break;\n//            case ET_BEAM3  : vtk_type = VTK_QUADRATIC_EDGE; break;\n            case ET_TET10  : vtk_type = VTK_QUADRATIC_TETRA; break;\n            case ET_TET15  : vtk_type = VTK_QUADRATIC_TETRA; break;\n            case ET_PENTA15: vtk_type = VTK_QUADRATIC_WEDGE; break;\n            case ET_HEX27  : vtk_type = VTK_QUADRATIC_HEXAHEDRON; break;\n            case ET_PYRA13 : vtk_type = VTK_QUADRATIC_PYRAMID; break;\n            case ET_TRI6   : vtk_type = VTK_QUADRATIC_TRIANGLE; break;\n            case ET_QUAD9  : vtk_type = VTK_QUADRATIC_QUAD; break;\n            default: vtk_type = -1; break;\n        }\n            \n        fprintf(m_fp, \"%d\\n\", vtk_type);\n    }\n}\n\nvoid VTKPlotFile::WriteScalarData(std::vector<float>& val, const std::string& name)\n{\n\tfprintf(m_fp, \"%s %s %s\\n\", \"SCALARS\", name.c_str(), \"float\");\n\tfprintf(m_fp, \"%s %s\\n\", \"LOOKUP_TABLE\", \"default\");\n\tfor (int i = 0; i < val.size(); ++i) fprintf(m_fp, \"%g\\n\", val[i]);\n}\n\nvoid VTKPlotFile::WriteVectorData(std::vector<float>& val, const std::string& name)\n{\n\tfprintf(m_fp, \"%s %s %s\\n\", \"VECTORS\", name.c_str(), \"float\");\n\tfor (int i = 0; i < val.size(); i += 3) fprintf(m_fp, \"%g %g %g\\n\", val[i], val[i + 1], val[i + 2]);\n}\n\nvoid VTKPlotFile::WriteMat3FData(std::vector<float>& val, const std::string& name)\n{\n\tfprintf(m_fp, \"%s %s %s\\n\", \"TENSORS\", name.c_str(), \"float\");\n\tfor (int i = 0; i < val.size(); i += 9)\n\t\tfprintf(m_fp, \"%g %g %g\\n%g %g %g\\n%g %g %g\\n\\n\",\n\t\t\tval[i    ], val[i + 1], val[i + 2],\n\t\t\tval[i + 3], val[i + 4], val[i + 5],\n\t\t\tval[i + 6], val[i + 7], val[i + 8]);\n}\n\nvoid VTKPlotFile::WriteMat3FSData(std::vector<float>& val, const std::string& name)\n{\n\tfprintf(m_fp, \"%s %s %s\\n\", \"TENSORS\", name.c_str(), \"float\");\n\tfor (int i = 0; i < val.size(); i += 6)\n\t\tfprintf(m_fp, \"%g %g %g\\n%g %g %g\\n%g %g %g\\n\\n\",\n\t\t\tval[i    ], val[i + 3], val[i + 5],\n\t\t\tval[i + 3], val[i + 1], val[i + 4],\n\t\t\tval[i + 5], val[i + 4], val[i + 2]);\n}\n\nvoid VTKPlotFile::WriteMat3FDData(std::vector<float>& val, const std::string& name)\n{\n\tfprintf(m_fp, \"%s %s %s\\n\", \"TENSORS\", name.c_str(), \"float\");\n\tfor (int i = 0; i < val.size(); i += 3)\n\t\tfprintf(m_fp, \"%g %g %g\\n%g %g %g\\n%g %g %g\\n\\n\",\n\t\t\tval[i], 0.f, 0.f,\n\t\t\t0.f, val[i + 1], 0.f,\n\t\t\t0.f, 0.f, val[i + 2]);\n}\n\nstatic void Space2_(string& s)\n{\n\tint n = (int)s.size();\n\tfor (int i = 0; i < n; ++i)\n\t\tif (s[i] == ' ') s[i] = '_';\n}\n\nvoid VTKPlotFile::WriteArrayData(std::vector<float>& val, const std::string& name, FEPlotData* pd)\n{\n\tint arraySize = pd->GetArraysize();\n\tfprintf(m_fp, \"FIELD %s %d\\n\", name.c_str(), arraySize);\n\tstd::vector<string> arrayNames = pd->GetArrayNames();\n\tint NE = val.size() / arraySize;\n\tfor (int j = 0; j < arraySize; ++j)\n\t{\n\t\tstring name = arrayNames[j];\n\t\tSpace2_(name);\n\t\tfprintf(m_fp, \"%s %d %d float\\n\", name.c_str(), 1, NE);\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tfloat f = val[arraySize*i + j];\n\t\t\tfprintf(m_fp, \"%g\\n\", f);\n\t\t}\n\t}\n}\n\nvoid VTKPlotFile::WriteArrayVec3fData(std::vector<float>& val, const std::string& name, FEPlotData* pd)\n{\n\tint arraySize = pd->GetArraysize();\n\tfprintf(m_fp, \"FIELD %s %d\\n\", name.c_str(), arraySize);\n\tstd::vector<string> arrayNames = pd->GetArrayNames();\n\tint NE = val.size() / (3*arraySize);\n\tfor (int j = 0; j < arraySize; ++j)\n\t{\n\t\tstring name = arrayNames[j];\n\t\tSpace2_(name);\n\t\tfprintf(m_fp, \"%s %d %d float\\n\", name.c_str(), 3, NE);\n\t\tfloat f[3];\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tf[0] = val[3*arraySize * i + 3*j    ];\n\t\t\tf[1] = val[3*arraySize * i + 3*j + 1];\n\t\t\tf[2] = val[3*arraySize * i + 3*j + 2];\n\t\t\tfprintf(m_fp, \"%g %g %g\\n\", f[0], f[1], f[2]);\n\t\t}\n\t}\n}\n\nvoid VTKPlotFile::WritePointData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nodes = mesh.Nodes();\n\n\t// we count nodal variables and element variables that use NODE format\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tint nodalVars = dic.NodalVariables();\n\tint domainVars = 0;\n\tauto& domainData = dic.DomainVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = domainData.begin();\n\tfor (int i = 0; i < domainData.size(); ++i, ++it)\n\t{\n\t\tif (it->m_psave)\n\t\t{\n\t\t\tFEPlotData* pd = it->m_psave;\n\t\t\tint format = pd->StorageFormat();\n\t\t\tif ((format == Storage_Fmt::FMT_NODE) ||\n\t\t\t\t(format == Storage_Fmt::FMT_MULT)) domainVars++;\n\t\t}\n\t}\n\tif ((nodalVars + domainVars) == 0) return;\n\n\tfprintf(m_fp, \"\\nPOINT_DATA %d\\n\", nodes);\n\tauto& nodeData = dic.NodalVariableList();\n\tit = nodeData.begin();\n\tfor (int n = 0; n < nodeData.size(); ++n, ++it)\n\t{\n\t\tif (it->m_psave)\n\t\t{\n\t\t\tFEPlotData* pd = it->m_psave;\n\t\t\tint ndata = pd->VarSize(pd->DataType());\n\n\t\t\tint N = fem.GetMesh().Nodes();\n\t\t\tFEDataStream a; a.reserve(ndata * N);\n\t\t\tif (pd->Save(fem.GetMesh(), a))\n\t\t\t{\n\t\t\t\t// pad mismatches\n\t\t\t\tassert(a.size() == N * ndata);\n\t\t\t\tif (a.size() != N * ndata) a.resize(N * ndata, 0.f);\n\n\t\t\t\t// must remove all whitespace\n\t\t\t\tstring dataName = it->m_szname;\n\t\t\t\tfor (size_t i = 0; i < dataName.size(); ++i)\n\t\t\t\t\tif (isspace(dataName[i])) dataName[i] = '_';\n\t\t\t\tconst char* szname = dataName.c_str();\n\n\t\t\t\t// write the value array\n\t\t\t\tstd::vector<float>& val = a.data();\n\t\t\t\tswitch (pd->DataType())\n\t\t\t\t{\n\t\t\t\tcase PLT_FLOAT : WriteScalarData(val, szname); break;\n\t\t\t\tcase PLT_VEC3F : WriteVectorData(val, szname); break;\n\t\t\t\tcase PLT_MAT3FS: WriteMat3FSData(val, szname); break;\n\t\t\t\tcase PLT_MAT3FD: WriteMat3FDData(val, szname); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// export all domain data that uses NODE storage format\n\tit = domainData.begin();\n\tfor (int i = 0; i < domainData.size(); ++i, ++it)\n\t{\n\t\tif (it->m_psave)\n\t\t{\n\t\t\tFEPlotData* pd = it->m_psave;\n\t\t\tint format = pd->StorageFormat();\n\n\t\t\t// must remove all whitespace\n\t\t\tstring dataName = it->m_szname;\n\t\t\tSpace2_(dataName);\n\t\t\tconst char* szname = dataName.c_str();\n\n\t\t\tint ndata = pd->VarSize(pd->DataType());\n\n\t\t\t// For now, we store all data in a global array\n\t\t\tint N = fem.GetMesh().Nodes();\n\t\t\tstd::vector<float> val(ndata * N, 0.f);\n\n\t\t\tif (format == Storage_Fmt::FMT_NODE)\n\t\t\t{\n\t\t\t\t// loop over all domains and fill global val array\n\t\t\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\t\t\tint NN = dom.Nodes();\n\t\t\t\t\tFEDataStream a; a.reserve(ndata * NN);\n\t\t\t\t\tpd->Save(dom, a);\n\n\t\t\t\t\t// pad mismatches\n\t\t\t\t\tif (a.size() != NN * ndata) a.resize(NN * ndata, 0.f);\n\n\t\t\t\t\t// copy to global array\n\t\t\t\t\tfor (int j = 0; j < NN; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tint nj = dom.NodeIndex(j);\n\t\t\t\t\t\tfor (int k = 0; k < ndata; ++k) val[nj * ndata + k] = a[ndata * j + k];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (format == Storage_Fmt::FMT_MULT)\n\t\t\t{\n\t\t\t\tvector<int> tag(N, 0);\n\n\t\t\t\t// loop over all domains and fill global val array\n\t\t\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEDomain& dom = mesh.Domain(i);\n\n\t\t\t\t\tint NN = 0;\n\t\t\t\t\tfor (int j = 0; j < dom.Elements(); ++j) NN += dom.ElementRef(j).Nodes();\n\n\t\t\t\t\tFEDataStream a; a.reserve(ndata * NN);\n\t\t\t\t\tpd->Save(dom, a);\n\n\t\t\t\t\t// pad mismatches\n\t\t\t\t\tif (a.size() != NN * ndata) a.resize(NN * ndata, 0.f);\n\n\t\t\t\t\t// copy to global array\n\t\t\t\t\tNN = 0;\n\t\t\t\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\t\t\tint ne = el.Nodes();\n\t\t\t\t\t\tfor (int k = 0; k < ne; ++k, ++NN)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint nk = el.m_node[k];\n\n\t\t\t\t\t\t\ttag[nk]++;\n\n\t\t\t\t\t\t\tfor (int l = 0; l < ndata; ++l)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tval[nk * ndata + l] += a[NN*ndata + l];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (int i = 0; i < N; ++i)\n\t\t\t\t{\n\t\t\t\t\tif (tag[i] != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat d = 1.f / (float)tag[i];\n\t\t\t\t\t\tfor (int j = 0; j < ndata; ++j) val[i * ndata + j] *= d;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse continue;\n\n\t\t\t// write the value array\n\t\t\tswitch (pd->DataType())\n\t\t\t{\n\t\t\tcase PLT_FLOAT : WriteScalarData(val, szname); break;\n\t\t\tcase PLT_VEC3F : WriteVectorData(val, szname); break;\n\t\t\tcase PLT_MAT3FS: WriteMat3FSData(val, szname); break;\n\t\t\tcase PLT_MAT3FD: WriteMat3FDData(val, szname); break;\n\t\t\tcase PLT_ARRAY : WriteArrayData (val, szname, pd); break;\n\t\t\tcase PLT_ARRAY_VEC3F: WriteArrayVec3fData(val, szname, pd); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid VTKPlotFile::WriteCellData()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint totalElements = mesh.Elements();\n\n\tPlotFile::Dictionary& dic = GetDictionary();\n\tif (dic.DomainVariables() == 0) return;\n\n\t// write cell data\n\tfprintf(m_fp, \"\\nCELL_DATA %d\\n\", totalElements);\n\n\t// write the part IDs first\n\tfprintf(m_fp, \"SCALARS part_id int\\n\");\n\tfprintf(m_fp, \"LOOKUP_TABLE default\\n\");\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int n = 0; n < NE; ++n) fprintf(m_fp, \"%d\\n\", i);\n\t}\n\n\tauto& elemData = dic.DomainVariableList();\n\tlist<DICTIONARY_ITEM>::iterator it = elemData.begin();\n\tfor (int n = 0; n < elemData.size(); ++n, ++it)\n\t{\n\t\tif (it->m_psave)\n\t\t{\n\t\t\tFEPlotData* pd = it->m_psave;\n\n\t\t\t// For now, we can only store FE_REGION_DOMAIN/FMT_ITEM\n\t\t\tint nregion = pd->RegionType();\n\t\t\tint nformat = pd->StorageFormat();\n\t\t\tif ((nregion == FE_REGION_DOMAIN) && (nformat == FMT_ITEM))\n\t\t\t{\n\t\t\t\t// get the number of floats per data value\n\t\t\t\tint ndata = pd->VarSize(pd->DataType());\n\n\t\t\t\t// For now, we store all data in a global array\n\t\t\t\tstd::vector<float> val(ndata * totalElements, 0.f);\n\n\t\t\t\t// loop over all domains and fill global val array\n\t\t\t\tint nc = 0;\n\t\t\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\t\t\tint NE = dom.Elements();\n\t\t\t\t\tFEDataStream a; a.reserve(ndata * NE);\n\t\t\t\t\tpd->Save(dom, a);\n\n\t\t\t\t\t// pad mismatches\n\t\t\t\t\tif (a.size() != NE * ndata) a.resize(NE * ndata, 0.f);\n\n\t\t\t\t\t// copy into global array\n\t\t\t\t\tvector<float>& vi = a.data();\n\t\t\t\t\tfor (int iel = 0; iel < NE; ++iel)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int k = 0; k < ndata; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tval[nc++] = vi[iel * ndata + k];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// must remove all whitespace\n\t\t\t\tstring dataName = it->m_szname;\n\t\t\t\tfor (size_t i = 0; i < dataName.size(); ++i)\n\t\t\t\t\tif (isspace(dataName[i])) dataName[i] = '_';\n\t\t\t\tconst char* szname = dataName.c_str();\n\n\t\t\t\t// write the value array\n\t\t\t\tswitch (pd->DataType())\n\t\t\t\t{\n\t\t\t\tcase PLT_FLOAT : WriteScalarData(val, szname); break;\n\t\t\t\tcase PLT_VEC3F : WriteVectorData(val, szname); break;\n\t\t\t\tcase PLT_MAT3F : WriteMat3FData (val, szname); break;\n\t\t\t\tcase PLT_MAT3FS: WriteMat3FSData(val, szname); break;\n\t\t\t\tcase PLT_MAT3FD: WriteMat3FDData(val, szname); break;\n\t\t\t\tcase PLT_ARRAY : WriteArrayData (val, szname, pd); break;\n\t\t\t\tcase PLT_ARRAY_VEC3F: WriteArrayVec3fData(val, szname, pd); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioPlot/VTKPlotFile.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"PlotFile.h\"\n#include <stdio.h>\n#include \"febioplot_api.h\"\n\n//! This class stores the FEBio results to a family of VTK files. \nclass FEBIOPLOT_API VTKPlotFile : public PlotFile\n{\npublic:\n\tVTKPlotFile(FEModel* fem);\n\n\t//! Open the plot database\n\tbool Open(const char* szfile) override;\n\n\t//! Open for appending\n\tbool Append(const char* szfile) override;\n\n\t//! Write current FE state to plot database\n\tbool Write(float ftime, int flag = 0) override;\n\n\t//! see if the plot file is valid\n\tbool IsValid() const override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tvoid WriteHeader();\n\tvoid WritePoints();\n\tvoid WriteCells();\n\tvoid WritePointData();\n\tvoid WriteCellData();\n\n\tvoid WriteScalarData(std::vector<float>& val, const std::string& szname);\n\tvoid WriteVectorData(std::vector<float>& val, const std::string& szname);\n\tvoid WriteMat3FData (std::vector<float>& val, const std::string& szname);\n\tvoid WriteMat3FSData(std::vector<float>& val, const std::string& szname);\n\tvoid WriteMat3FDData(std::vector<float>& val, const std::string& szname);\n\tvoid WriteArrayData (std::vector<float>& val, const std::string& name, FEPlotData* pd);\n\tvoid WriteArrayVec3fData(std::vector<float>& val, const std::string& name, FEPlotData* pd);\n\nprivate:\n\tFILE*\tm_fp;\n\tint\t\tm_count;\n\tbool\tm_valid;\n\tstd::string\tm_filename;\n};\n"
  },
  {
    "path": "FEBioPlot/febioplot_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febioplot_EXPORTS\n\t\t\t#define FEBIOPLOT_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOPLOT_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIOPLOT_API\n\t#endif\n#else\n\t#define FEBIOPLOT_API\n#endif\n"
  },
  {
    "path": "FEBioPlot/stdafx.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n\n// TODO: reference any additional headers you need in STDAFX.H\n// and not in this file\n"
  },
  {
    "path": "FEBioPlot/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n#include \"targetver.h\"\n\n#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\n\n#endif\n\n// TODO: reference additional headers your program requires here\n"
  },
  {
    "path": "FEBioPlot/targetver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// Including SDKDDKVer.h defines the highest available Windows platform.\n\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\n\n#include <SDKDDKVer.h>\n"
  },
  {
    "path": "FEBioRVE/FE2OMicroConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FE2OMicroConstraint.h\"\n#include <FECore/log.h>\n#include <FEBioMech/FEBioMech.h>\n#include <FECore/FELinearSystem.h>\n#include <FECore/FEMesh.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEMicroFlucSurface::FEMicroFlucSurface(FEModel* fem) : FESurface(fem)\n{\n\tm_Lm.x = 0.; m_Lm.y = 0.; m_Lm.z = 0.;\n\tm_pv.x = 0.; m_pv.y = 0.; m_pv.z = 0.;\n\tm_c.x = 0.;  m_c.y = 0.;  m_c.z = 0.;\n\n\tm_Fm.unit(); m_Gm.zero();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMicroFlucSurface::Init()\n{\n\t// calculate the intial microfluctations across the surface\n\tm_c = SurfMicrofluc();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMicroFlucSurface::CopyFrom(FEMicroFlucSurface& s)\n{\n\tm_Node = s.m_Node;\n\n\t// create elements\n\tint NE = s.Elements();\n\tCreate(NE);\n\tfor (int i=0; i<NE; ++i) Element(i) = s.Element(i);\n\n\t// copy surface data\n\tm_Lm = s.m_Lm;\n\tm_pv = s.m_pv;\n\tm_c  = s.m_c;\n\tm_Fm = s.m_Fm;\n\tm_Gm = s.m_Gm;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the initial volume\nvec3d FEMicroFlucSurface::SurfMicrofluc()\n{\n\t// Integration of microfluctation field across surface\n\tvec3d c;\n\t\n\t// get the mesh\n\tFEMesh& mesh = *GetMesh();\n\n\t// loop over all elements\n\tdouble vol = 0.0;\n\tint NE = Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tvec3d x0[FEElement::MAX_NODES];\n\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& el = Element(i);\n\n\t\t// get the nodal coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j){\n\t\t\tx[j] = mesh.Node(el.m_node[j]).m_rt;\n\t\t\tx0[j] = mesh.Node(el.m_node[j]).m_r0;\n\t\t}\n\n\t\t// loop over integration points\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\tvec3d r = el.eval(x, n);\n\t\t\tvec3d r0 = el.eval(x0, n);\n\n\t\t\tvec3d u = r - r0;\n\t\t\tmat3d I; I.unit();\n\n\t\t\tc += (u - (m_Fm - I)*r0 - m_Gm.contractdyad1(r0)*0.5)*w[n];\n\t\t}\n\t}\n\t\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FE2OMicroConstraint, FESurfaceConstraint);\n\tADD_PARAMETER(m_blaugon, \"laugon\" ); \n\tADD_PARAMETER(m_atol   , \"augtol\" );\n\tADD_PARAMETER(m_eps    , \"penalty\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor. Set default parameter values\nFE2OMicroConstraint::FE2OMicroConstraint(FEModel* pfem) : FESurfaceConstraint(pfem), m_s(pfem), m_dofU(pfem)\n{\n\tm_eps = 0.0;\n\tm_atol = 0.0;\n\tm_blaugon = false;\n\tm_binit = false;\t// will be set to true during activation\n\n\t// TODO: Can this be done in Init, since there is no error checking\n\tif (pfem)\n\t{\n\t\tm_dofU.AddVariable(FEBioMech::GetVariableName(FEBioMech::DISPLACEMENT));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::CopyFrom(FENLConstraint* plc)\n{\n\t// cast to a periodic boundary\n\tFE2OMicroConstraint& mc = dynamic_cast<FE2OMicroConstraint&>(*plc);\n\n\t// copy parameters\n\tGetParameterList() = mc.GetParameterList();\n\n\t// copy nodes\n\tm_s.CopyFrom(mc.m_s);\n}\n\n//-----------------------------------------------------------------------------\n//! Returns the surface\nFESurface* FE2OMicroConstraint::GetSurface()\n{\n\treturn &m_s;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes data structures. \nvoid FE2OMicroConstraint::Activate()\n{\n\t// don't forget to call base class\n\tFENLConstraint::Activate();\n\n\t// initialize the surface\n\tif (m_binit == false) m_s.Init();\n\n\t// set flag that initial volume is calculated\n\tm_binit = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\t// We don't do anything here since the connectivity of a surface\n\t// is implied by the domain to which it is attached.\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tFEMesh& mesh = GetMesh();\n\tint N = el.Nodes();\n\tlm.resize(N*3);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = mesh.Node(n);\n\t\tvector<int>& id = node.m_ID;\n\n\t\tlm[3*i  ] = id[m_dofU[0]];\n\t\tlm[3*i+1] = id[m_dofU[1]];\n\t\tlm[3*i+2] = id[m_dofU[2]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEMesh& mesh = *m_s.GetMesh();\n\n\tvector<double> fe;\n\tvector<int> lm;\n\n\t// get the lagrange \n\tvec3d Lm = m_s.m_Lm;\n\n\t// loop over all elements\n\tint NE = m_s.Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& el = m_s.Element(i);\n\n\t\t// get the nodal coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j) x[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// allocate element residual vector\n\t\tint ndof = 3*neln;\n\t\tfe.resize(ndof);\n\t\tzero(fe);\n\n\t\t// loop over all integration points\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// calculate the tangent vectors\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\t\t\tvec3d dxr(0,0,0), dxs(0,0,0);\n\t\t\tfor (int j=0; j<neln; ++j) \n\t\t\t{\n\t\t\t\tdxr += x[j]*Gr[j];\n\t\t\t\tdxs += x[j]*Gs[j];\n\t\t\t}\n\n\t\t\t// evaluate the \"normal\" vector\n\t\t\tvec3d v = (dxr ^ dxs);\n\t\t\tvec3d f = Lm*w[n]*v.norm();\n\n\t\t\t// evaluate the element forces\n\t\t\tdouble* H = el.H(n);\n\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t{\n\t\t\t\tfe[3*j  ] += H[j]*f.x;\n\t\t\t\tfe[3*j+1] += H[j]*f.y;\n\t\t\t\tfe[3*j+2] += H[j]*f.z;\n\t\t\t}\n\t\t}\n\n\t\t// get the element's LM vector\n\t\tlm.resize(3*neln);\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(el.m_node[j]).m_ID;\n\t\t\tlm[3*j  ] = id[m_dofU[0]];\n\t\t\tlm[3*j+1] = id[m_dofU[1]];\n\t\t\tlm[3*j+2] = id[m_dofU[2]];\n\t\t}\n\n\t\t// add element force vector to global force vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEMesh& mesh = *m_s.GetMesh();\n\n\t// element stiffness matrix\n\tFEElementMatrix ke;\n\tvector<int> lm;\n\tvector<double> fe;\n\n\t// loop over all elements\n\tint NE = m_s.Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tfor (int l=0; l<NE; ++l)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& el = m_s.Element(l);\n\n\t\t// get the nodal coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j) x[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// allocate the stiffness matrix\n\t\tint ndof = 3*neln;\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\t\tfe.resize(ndof);\n\t\tzero(fe);\n\n\t\t// repeat over integration points\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n)\n\t\t{\n\t\t\t// calculate tangent vectors\n\t\t\tdouble* N = el.H(n);\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\t\t\tvec3d dxr(0,0,0), dxs(0,0,0);\n\t\t\tfor (int j=0; j<neln; ++j) \n\t\t\t{\n\t\t\t\tdxr += x[j]*Gr[j];\n\t\t\t\tdxs += x[j]*Gs[j];\n\t\t\t}\n\n\t\t\t// calculate pressure contribution\n\t\t\tvec3d v = (dxr ^ dxs);\n\n\t\t\tdouble vi; \n\t\t\tdouble vj;\n\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tvi = N[i]*v.norm();\n\t\t\t\t\tvj = N[j]*v.norm();\n\t\t\t\t\tke[3*i  ][3*j  ] += m_eps*vi*vj;\n\t\t\t\t\tke[3*i+1][3*j+1] += m_eps*vi*vj;\n\t\t\t\t\tke[3*i+2][3*j+2] += m_eps*vi*vj;\n\t\t\t\t}\n\n\t\t\n\t\t\t// calculate displacement contribution\n\t\t\tvec3d qab;\n\t\t\tfor (int i=0; i<neln; ++i)\n\t\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tqab = (-dxs*Gr[j] + dxr*Gs[j])*(N[i]/(2*(dxr ^ dxs).norm()))*w[n]; \n\n\t\t\t\t\tke[3*i  ][3*j  ] +=      0;\n\t\t\t\t\tke[3*i  ][3*j+1] +=  qab.z;\n\t\t\t\t\tke[3*i  ][3*j+2] += -qab.y;\n\n\t\t\t\t\tke[3*i+1][3*j  ] += -qab.z;\n\t\t\t\t\tke[3*i+1][3*j+1] +=      0;\n\t\t\t\t\tke[3*i+1][3*j+2] +=  qab.x;\n\n\t\t\t\t\tke[3*i+2][3*j  ] +=  qab.y;\n\t\t\t\t\tke[3*i+2][3*j+1] += -qab.x;\n\t\t\t\t\tke[3*i+2][3*j+2] +=      0;\n\t\t\t\t}\n\t\t}\n\n\n\t\t// get the element's LM vector\n\t\tlm.resize(3*neln);\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(el.m_node[j]).m_ID;\n\t\t\tlm[3*j  ] = id[m_dofU[0]];\n\t\t\tlm[3*j+1] = id[m_dofU[1]];\n\t\t\tlm[3*j+2] = id[m_dofU[2]];\n\t\t}\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tke.SetNodes(el.m_node);\n\t\tke.SetIndices(lm);\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FE2OMicroConstraint::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we are augmenting\n\tif ((m_blaugon == false) || (m_atol <= 0.0)) return true;\n\n\tfeLog(\"\\n2O periodic surface microfluctation constraint:\\n\");\n\n\tvec3d Dm = m_s.m_c*m_eps;\n\tvec3d Lm = m_s.m_pv;\n\t\n\tdouble Dnorm = Dm.norm();\n\tdouble Lnorm = Lm.norm();\n\n\tdouble err = Dnorm/Lnorm;\n\n\tif (Lnorm == 0)\n\t\terr = 0;\n\n\tfeLog(\"\\tpressure vect norm: %lg\\n\", Lm.norm());\n\tfeLog(\"\\tnorm : %lg (%lg)\\n\", err, m_atol);\n\tfeLog(\"\\ttotal microfluc norm: %lg\\n\", m_s.m_c.norm());\n\n\t// check convergence\n\tif (err < m_atol) return true;\n\n\t// update Lagrange multiplier (and pressure variable)\n\tm_s.m_Lm = Lm;\n\tm_s.m_pv = Lm + Dm;\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::Serialize(DumpStream& ar)\n{\n\tar & m_s.m_Lm;\n\tar & m_s.m_pv;\n\tar & m_s.m_c;\n\tar & m_s.m_Fm;\n\tar & m_s.m_Gm;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2OMicroConstraint::Reset()\n{\n}\n\n//-----------------------------------------------------------------------------\n// This function is called when the FE model's state needs to be updated.\nvoid FE2OMicroConstraint::Update(const FETimeInfo& tp)\n{\n\t// calculate the current volume\n\tm_s.m_c = m_s.SurfMicrofluc();\n\t\n\t// update pressure variable\n\tm_s.m_pv = m_s.m_Lm - m_s.m_c*m_eps;\n}\n"
  },
  {
    "path": "FEBioRVE/FE2OMicroConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FESurface.h>\n#include <FECore/tens3d.h>\n#include <FEBioMech/FEElasticMaterial.h>\n\n//-----------------------------------------------------------------------------\nclass FEMicroFlucSurface : public FESurface\n{\npublic:\n\t//! constructor\n\tFEMicroFlucSurface(FEModel* fem);\n\n\t//! Initialization\n\tbool Init();\n\n\t//! copy data\n\tvoid CopyFrom(FEMicroFlucSurface& s);\n\npublic:\n\tvec3d SurfMicrofluc();\n\npublic:\n\tvec3d\t\tm_Lm;\t// Lagrange multipler microfluctuation\n\tvec3d\t\tm_pv;\t// \"Pressure\" vector\n\tvec3d\t\tm_c;\t// Microfluction across surface\n\n\tmat3d\t\tm_Fm;\t// Macroscopic deformation gradient\n\ttens3drs\tm_Gm;\t// Macroscopic deformation Hessian\n};\n\n//-----------------------------------------------------------------------------\n// This class implements a constraint that tries to maintain the volume of the \n// enclosed space using an isochoric pressure.\nclass FE2OMicroConstraint : public FESurfaceConstraint\n{\npublic:\n\t//! constructor\n\tFE2OMicroConstraint(FEModel* pfem);\n\n\tvoid Activate() override;\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\tvoid Serialize(DumpStream& ar) override;\n\tvoid CopyFrom(FENLConstraint* plc) override;\n\n\t// update state\n\tvoid Reset() override;\n\tvoid Update(const FETimeInfo& tp);\n\n\tFESurface* GetSurface() override;\n\n\t//! Unpack surface element data\n\tvoid UnpackLM(FEElement& el, vector<int>& lm);\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\npublic:\n\tFEMicroFlucSurface m_s;\t//!< the bounding surface\n\npublic:\n\tdouble\tm_eps;\t\t//!< penalty parameter\n\tdouble\tm_atol;\t\t//!< augmented Lagrangian tolerance\n\tbool\tm_blaugon;\t//!< augmentation flag\n\nprivate:\n\tbool\tm_binit;\t//!< flag indicating whether the constraint is initialized\n\n\tFEDofList\tm_dofU;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEBCPrescribedDeformation2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBCPrescribedDeformation2O.h\"\n#include <FECore/FEMesh.h>\n\nBEGIN_FECORE_CLASS(FEBCPrescribedDeformation2O, FEPrescribedNodeSet)\n\tADD_PARAMETER(m_scale, \"scale\");\n\tADD_PARAMETER(m_F    , \"F\");\n\tADD_PARAMETER(m_G    , \"G\");\n\tADD_PARAMETER(m_refNode, \"reference\");\nEND_FECORE_CLASS();\n\nFEBCPrescribedDeformation2O::FEBCPrescribedDeformation2O(FEModel* pfem) : FEPrescribedNodeSet(pfem)\n{\n\tm_scale = 1.0;\n\tm_F.unit();\n\tm_G.zero();\n\tm_refNode = -1;\n\n\tif (pfem)\n\t{\n\t\tFEDofList dofs(pfem);\n\t\tdofs.AddVariable(\"displacement\");\n\t\tSetDOFList(dofs);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEBCPrescribedDeformation2O::Init()\n{\n\tif (m_refNode < 0) return false;\n\treturn FEPrescribedNodeSet::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Sets the displacement scale factor. An optional load curve index can be given\n// of the load curve that will control the scale factor.\nvoid FEBCPrescribedDeformation2O::SetScale(double s, int lc)\n{\n\tm_scale = s;\n\tif (lc >= 0) AttachLoadController(&m_scale, lc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation2O::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFEBCPrescribedDeformation2O* ps = dynamic_cast<FEBCPrescribedDeformation2O*>(pbc); assert(ps);\n\tm_scale = ps->m_scale;\n\tm_F = ps->m_F;\n\tm_G = ps->m_G;\n\tm_refNode = ps->m_refNode;\n\tCopyParameterListState(ps->GetParameterList());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation2O::SetReferenceNode(int n)\n{\n\tm_refNode = n;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation2O::SetDeformationGradient(const mat3d& F)\n{\n\tm_F = F;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation2O::SetDeformationHessian(const tens3drs& G)\n{\n\tm_G = G;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBCPrescribedDeformation2O::GetNodalValues(int nodelid, std::vector<double>& val)\n{\n\tFEMesh& mesh = GetMesh();\n\tvec3d X1 = mesh.Node(m_refNode).m_r0;\n\n\tvec3d X = GetNodeSet()->Node(nodelid)->m_r0;\n\n\tmat3ds XX = dyad(X);\n\tmat3ds XX1 = dyad(X1);\n\tmat3d U = m_F - mat3dd(1.0);\n\tvec3d u = U*(X - X1) + m_G.contract2s(XX - XX1)*0.5;\n\tu*=m_scale;\n\n\tval[0] = u.x;\n\tval[1] = u.y;\n\tval[2] = u.z;\n}\n"
  },
  {
    "path": "FEBioRVE/FEBCPrescribedDeformation2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/tens3d.h>\n#include \"febiorve_api.h\"\n\nclass FEBIORVE_API FEBCPrescribedDeformation2O : public FEPrescribedNodeSet\n{\npublic:\n\tFEBCPrescribedDeformation2O(FEModel* pfem);\n\n\tvoid SetScale(double s, int lc = -1);\n\n\tvoid SetReferenceNode(int n);\n\n\tbool Init() override;\n\n\tvoid SetDeformationGradient(const mat3d& F);\n\tvoid SetDeformationHessian(const tens3drs& G);\n\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\nprotected:\n\tvoid GetNodalValues(int nodelist, std::vector<double>& val) override;\n\nprotected:\n\tdouble\tm_scale;\n\tmat3d\tm_F;\n\ttens3drs m_G;\n\tint\tm_refNode;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEBioRVE.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioRVE.h\"\n#include \"FEMicroMaterial.h\"\n#include \"FEMicroMaterial2O.h\"\n#include \"FEMindlinElastic2O.h\"\n#include \"FEPeriodicBoundary2O.h\"\n#include \"FEBCPrescribedDeformation2O.h\"\n#include \"FE2OMicroConstraint.h\"\n#include \"FEElasticMultiscaleDomain1O.h\"\n#include \"FEElasticMultiscaleDomain2O.h\"\n#include \"FEMultiscaleDomainFactory.h\"\n#include \"FEBioRVEPlot.h\"\n#include \"FERVEProbe.h\"\n\n//-----------------------------------------------------------------------------\n//! Register all the classes of the FEBioMech module with the FEBio framework.\nvoid FEBioRVE::InitModule()\n{\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tfebio.RegisterDomain(new FEMultiScaleDomainFactory, true);\n\n\t// this module extends the solid module\n\tfebio.SetActiveModule(\"solid\");\n\n\tREGISTER_FECORE_CLASS(FEMicroMaterial, \"micro-material\");\n\tREGISTER_FECORE_CLASS(FEMicroMaterial2O, \"micro-material2O\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEMindlinElastic2O, \"mindlin elastic\", FECORE_EXPERIMENTAL);\n\n\tREGISTER_FECORE_CLASS(FEMicroProbe, \"probe\");\n\n\tREGISTER_FECORE_CLASS(FEElasticMultiscaleDomain1O, \"elastic-mm-solid\");\n\tREGISTER_FECORE_CLASS(FEElasticMultiscaleDomain2O, \"elastic-mm-solid2O\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEElasticSolidDomain2O, \"elastic-solid2O\", FECORE_EXPERIMENTAL);\n\n\tREGISTER_FECORE_CLASS(FEBCPrescribedDeformation2O, \"prescribed deformation 2O\", FECORE_EXPERIMENTAL);\n\n\tREGISTER_FECORE_CLASS(FE2OMicroConstraint, \"2O microfluc\", FECORE_EXPERIMENTAL);\n\n\tREGISTER_FECORE_CLASS(FEPeriodicBoundary1O, \"periodic boundary1O\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEPeriodicBoundary2O, \"periodic boundary2O\", FECORE_EXPERIMENTAL);\n\n\tREGISTER_FECORE_CLASS(FEPlotElementGnorm, \"G norm\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEPlotElementPK1norm, \"PK1 norm\");\n\tREGISTER_FECORE_CLASS(FEPlotElementQK1norm, \"QK1 norm\", FECORE_EXPERIMENTAL);\n\tREGISTER_FECORE_CLASS(FEPlotElementMicroEnergy, \"micro energy\");\n}\n"
  },
  {
    "path": "FEBioRVE/FEBioRVE.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"febiorve_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FEBioRVE module\n//! This module defines classes for dealing with homogenization problems\n\nnamespace FEBioRVE\n{\n\t//! Initialize the FEBioMech module\n\tFEBIORVE_API void InitModule();\n}\n"
  },
  {
    "path": "FEBioRVE/FEBioRVEPlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <FECore/writeplot.h>\n#include \"FEBioRVEPlot.h\"\n#include \"FEMicroMaterial.h\"\n#include \"FEMicroMaterial2O.h\"\n\n//-----------------------------------------------------------------------------\n//! Store the average deformation Hessian (G) for each element. \n\nclass FEMicro2OG\n{\npublic:\n\ttens3drs operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint2O& pt2O = *(mp.ExtractData<FEElasticMaterialPoint2O>());\n\t\treturn pt2O.m_G;\n\t}\n};\n\nbool FEPlotElementGnorm::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticMaterial2O* pme = dom.GetMaterial()->ExtractProperty<FEElasticMaterial2O>();\n\tif (pme == 0) return false;\n\n\twriteAverageElementValue<tens3drs, double>(dom, a, FEMicro2OG(), [](const tens3drs& m) { return m.tripledot(m); });\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Store the norm of the average PK1 stress for each element.\n\nclass FEMicro1OPK1Stress\n{\npublic:\n\tFEMicro1OPK1Stress(FEMicroMaterial* pm) : m_mat(pm) {}\n\tmat3d operator()(const FEMaterialPoint& mp)\n\t{\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\tFEMicroMaterialPoint* mmppt = mp_noconst.ExtractData<FEMicroMaterialPoint>();\n\t\treturn m_mat->AveragedStressPK1(mmppt->m_rve, mp_noconst);\n\t}\n\nprivate:\n\tFEMicroMaterial* m_mat;\n};\n\nclass FEMicro2OPK1Stress\n{\npublic:\n\tmat3d operator()(const FEMaterialPoint& mp)\n\t{\n\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\tFEMicroMaterialPoint2O* mmppt = mp_noconst.ExtractData<FEMicroMaterialPoint2O>();\n\t\treturn mmppt->m_rve.AveragedStressPK1(mp_noconst);\n\t}\n};\n\nbool FEPlotElementPK1norm::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMicroMaterial* pm1O = dynamic_cast<FEMicroMaterial*>(dom.GetMaterial());\n\tif (pm1O)\n\t{\n\t\twriteAverageElementValue<mat3d, double>(dom, a, FEMicro1OPK1Stress(pm1O), [](const mat3d& m) {return m.dotdot(m); });\n\t\treturn true;\n\t}\n\n\tFEMicroMaterial2O* pm2O = dynamic_cast<FEMicroMaterial2O*>(dom.GetMaterial());\n\tif (pm2O)\n\t{\n\t\twriteAverageElementValue<mat3d, double>(dom, a, FEMicro2OPK1Stress(), [](const mat3d& m) {return m.dotdot(m); });\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n//-----------------------------------------------------------------------------\n//! Store the norm of the average PK1 stress moment for each element. \n\nclass FEMicro2OQK1\n{\npublic:\n\ttens3drs operator()(const FEMaterialPoint& mp)\n\t{\n\t\tconst FEElasticMaterialPoint2O& pt2O = *(mp.ExtractData<FEElasticMaterialPoint2O>());\n\t\treturn pt2O.m_Q;\n\t}\n};\n\nbool FEPlotElementQK1norm::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEElasticMaterial2O* pme = dynamic_cast<FEElasticMaterial2O*>(dom.GetMaterial());\n\tif (pme == 0) return false;\n\n\t// write solid element data\n\twriteAverageElementValue<tens3drs, double>(dom, a, FEMicro2OQK1(), [](const tens3drs& m) { return m.tripledot(m); });\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Element macro energy\nbool FEPlotElementMicroEnergy::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEMicroMaterial* pm1O = dynamic_cast<FEMicroMaterial*>(dom.GetMaterial());\n\tif (pm1O)\n\t{\n\t\twriteAverageElementValue<double>(dom, a, [](const FEMaterialPoint& mp) {\n\t\t\tconst FEMicroMaterialPoint& mmpt = *(mp.ExtractData<FEMicroMaterialPoint>());\n\t\t\treturn mmpt.m_micro_energy;\n\t\t\t});\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "FEBioRVE/FEBioRVEPlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEPlotData.h>\n#include <FECore/FEElement.h>\n\n//-----------------------------------------------------------------------------\n//! Element norm for PK1 stress\nclass FEPlotElementPK1norm : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementPK1norm(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element norm for G\nclass FEPlotElementGnorm : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementGnorm(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element norm for PK1 stress moment\nclass FEPlotElementQK1norm : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementQK1norm(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n\n//-----------------------------------------------------------------------------\n//! Element micro energy\nclass FEPlotElementMicroEnergy : public FEPlotDomainData\n{\npublic:\n\tFEPlotElementMicroEnergy(FEModel* pfem) : FEPlotDomainData(pfem, PLT_FLOAT, FMT_ITEM) {}\n\tbool Save(FEDomain& dom, FEDataStream& a);\n};\n"
  },
  {
    "path": "FEBioRVE/FEElasticMaterial2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticMaterial2O.h\"\n#include <FECore/DumpStream.h>\n\n//-----------------------------------------------------------------------------\nFEElasticMaterialPoint2O::FEElasticMaterialPoint2O(FEMaterialPointData* pt) : FEMaterialPointData(pt)\n{\n\tm_PK1.zero();\n\tm_G.zero();\n\tm_Q.zero();\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticMaterialPoint2O::Copy()\n{\n\tFEElasticMaterialPoint2O* pt = new FEElasticMaterialPoint2O(0);\n\tpt->m_PK1 = m_PK1;\n\tpt->m_G   = m_G;\n\tpt->m_Q   = m_Q;\n\tif (m_pNext) pt->SetNext(m_pNext->Copy());\n\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMaterialPoint2O::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_PK1 & m_G & m_Q; \n}\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEElasticMaterial2O, FEElasticMaterial)\n\tADD_PARAMETER(m_beta     , \"beta\"    );\n\tADD_PARAMETER(m_bKDG1    , \"KDG1\"    );\n\tADD_PARAMETER(m_bKDG2    , \"KDG2\"    );\n\tADD_PARAMETER(m_bKDG3    , \"KDG3\"    );\n\tADD_PARAMETER(m_buseJ0   , \"useJ0\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEElasticMaterial2O::FEElasticMaterial2O(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\tm_beta = 10.0;\n\n\tm_bKDG1 = true;\n\tm_bKDG2 = true;\n\tm_bKDG3 = true;\n\tm_buseJ0 = true;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEElasticMaterial2O::CreateMaterialPointData()\n{\n\treturn new FEElasticMaterialPoint2O(new FEElasticMaterialPoint);\n}\n\n//-----------------------------------------------------------------------------\n// The stiffness is evaluated at the same time the stress is evaluated so we \n// can just return it here. Note that this assumes that the stress function \n// is always called prior to the tangent function.\n// Note that this function is not used in the second-order implemenetation\ntens4ds FEElasticMaterial2O::Tangent(FEMaterialPoint &mp)\n{\n\tassert(false);\n\ttens4ds c; c.zero();\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// Note that this function is not used in the second-order implemenetation\nmat3ds FEElasticMaterial2O::Stress(FEMaterialPoint &mp)\n{\n\tassert(false);\n\tmat3ds sa; sa.zero();\n\treturn sa;\n}\n"
  },
  {
    "path": "FEBioRVE/FEElasticMaterial2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include <FECore/tens3d.h>\n#include <FECore/tens4d.h>\n#include <FECore/tens5d.h>\n#include <FECore/tens6d.h>\n\n//-----------------------------------------------------------------------------\n// second order continuum elastic material point\nclass FEElasticMaterialPoint2O : public FEMaterialPointData\n{\npublic:\n\t//! constructor\n\tFEElasticMaterialPoint2O(FEMaterialPointData* pt);\n\n\t//! create a shallow copy\n\tFEMaterialPointData* Copy();\n\n\t//! serialize material point data\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tmat3d\tm_PK1;\t\t\t//!< PK1 stress\n\n\ttens3drs   m_G;\t\t\t//!< gradient of deformation gradient\n\ttens3drs   m_Q;\t\t\t//!< higher-order stress tensor\n};\n\n//-----------------------------------------------------------------------------\n//! This is the base class for second-order continuum elastic material\nclass FEElasticMaterial2O : public FEElasticMaterial\n{\npublic:\n\t//! constructor\n\tFEElasticMaterial2O(FEModel* fem);\n\npublic: // these functions must be implemented by derived classes\n\n\t//! Calculate PK1 stress and higher order stress Q\n\tvirtual void Stress(FEMaterialPoint& mp, mat3d& P, tens3drs& Q) = 0;\n\n\t//! Calculate material tangents\n\t//! C = dP/dF\n\t//! L = dP/dG\n\t//! H = dQ/dF\n\t//! J = dQ/dG\n\tvirtual void Tangent(FEMaterialPoint& mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J) = 0;\n\n\t//! create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic:\n\tdouble\t\t\tm_beta;\t\t\t//!< beta parameter for DG\n\n\t// flags for evaluating effects of stiffness contributions\n\t// TODO: I'll probably delete this when all bugs are found\n\tbool\tm_bKDG1;\n\tbool\tm_bKDG2;\n\tbool\tm_bKDG3;\n\tbool\tm_buseJ0;\n\nprivate:\n\t// We don't need these functions\n\t// TODO: Perhaps we should derive this class directly from FEMaterial so\n\t//       that these functions are not inherited. \n\tmat3ds Stress(FEMaterialPoint& pt) override;\n\ttens4ds Tangent(FEMaterialPoint& pt) override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEElasticMultiscaleDomain1O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticMultiscaleDomain1O.h\"\n#include \"FEMicroMaterial.h\"\n#include \"FECore/mat3d.h\"\n#include \"FECore/tens6d.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEElasticMultiscaleDomain1O::FEElasticMultiscaleDomain1O(FEModel* pfem) : FEElasticSolidDomain(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! intialize domain\nbool FEElasticMultiscaleDomain1O::Init()\n{\n\tif (FEElasticSolidDomain::Init() == false) return false;\n\n\t// get the material\n\tFEModel& fem = *GetFEModel();\n\tFEMicroMaterial* pmat = dynamic_cast<FEMicroMaterial*>(m_pMat);\n\tif (m_pMat == 0) return false;\n\n\t// get the parent RVE\n\tFERVEModel& rve = pmat->m_mrve;\n\n\t// loop over all elements\n\tfor (size_t i=0; i<m_Elem.size(); ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j=0; j<nint; ++j) \n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\tFEMicroMaterialPoint& mmpt = *mp.ExtractData<FEMicroMaterialPoint>();\n\n\t\t\t// create the material point RVEs\n\t\t\tmmpt.m_F_prev = pt.m_F;\t// TODO: I think I can remove this line\n\t\t\tmmpt.m_rve.CopyFrom(rve);\n\t\t\tif (mmpt.m_rve.Init() == false) return false;\n\n\t\t\t// initialize RCI solve\n\t\t\tif (mmpt.m_rve.RCI_Init() == false) return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioRVE/FEElasticMultiscaleDomain1O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include \"FECore/tens3d.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a domain used in an elastic remodeling problem.\n//! It differs from the FEElasticSolidDomain in that it adds a stiffness matrix\n//! due to the deformation dependent density.\nclass FEElasticMultiscaleDomain1O : public FEElasticSolidDomain\n{\npublic:\n\t//! constructor\n\tFEElasticMultiscaleDomain1O(FEModel* pfem);\n\n\t//! initialize class\n\tbool Init();\n};\n"
  },
  {
    "path": "FEBioRVE/FEElasticMultiscaleDomain2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticMultiscaleDomain2O.h\"\n#include \"FEMicroMaterial2O.h\"\n#include \"FERVEProbe.h\"\n#include \"FECore/mat3d.h\"\n#include \"FECore/tens6d.h\"\n#include <FECore/log.h>\n\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEElasticMultiscaleDomain2O::FEElasticMultiscaleDomain2O(FEModel* pfem) : FEElasticSolidDomain2O(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nbool FEElasticMultiscaleDomain2O::Init()\n{\n\t// initialize base class first\n\tif (FEElasticSolidDomain2O::Init() == false) return false;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// initialze RVEs\n\tconst int NE = FEElement::MAX_NODES;\n\tvec3d x0[NE], xt[NE], r0, rt;\n\tFEMesh& m = *GetMesh();\n\t\t\n\tFEMicroMaterial2O* pmat = dynamic_cast<FEMicroMaterial2O*>(m_pMat);\n\tFERVEModel2O& rve = pmat->m_mrve;\n\t\t\t\n\tfor (size_t i=0; i<m_Elem.size(); ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\t\tint neln = el.Nodes();\n\t\tfor (int j=0; j<neln; ++j)\n\t\t{\n\t\t\tx0[j] = m.Node(el.m_node[j]).m_r0;\n\t\t\txt[j] = m.Node(el.m_node[j]).m_rt;\n\t\t}\n\n\t\tint n = el.GaussPoints();\n\t\tfor (int j=0; j<n; ++j) \n\t\t{\n\t\t\tr0 = el.Evaluate(x0, j);\n\t\t\trt = el.Evaluate(xt, j);\n\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\tFEMicroMaterialPoint2O& mmpt2O = *mp.ExtractData<FEMicroMaterialPoint2O>();\n\t\t\tmmpt2O.m_elem_id = el.GetID();\n\t\t\tmmpt2O.m_gpt_id = j;\n\n\t\t\t// initialize the material point RVE\n\t\t\t// This essentially copies the parent RVE to the material point RVE\n\t\t\tif (mmpt2O.m_rve.Init(rve) == false) return false;\n\t\t}\n\t}\n\n\t// initialize surface RVEs\n\tint nnf = 0;\n\tint NF = m_surf.Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& face = m_surf.Element(i);\n\t\tint nint = face.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n, ++nnf)\n\t\t{\n\t\t\tfor (int k=0; k<2; ++k)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *m_surf.GetData(nnf).m_pt[k];\n\t\t\t\tFEMicroMaterialPoint2O& mmpt2O = *mp.ExtractData<FEMicroMaterialPoint2O>();\n\t\t\t\tmmpt2O.m_elem_id = -i-1;\n\t\t\t\tmmpt2O.m_gpt_id = n + k*nint;\n\n\t\t\t\t// Initialize the material point RVE\n\t\t\t\t// This essentially copies the parent RVE model to the material points\n\t\t\t\tmmpt2O.m_rve.Init(rve);\n\t\t\t}\n\t\t}\n\t}\n\n\t\t// TODO: I need to move the code below to FERVEProbe::Init.\n/*\n\t\tif (p.m_neid > 0)\n\t\t{\n\t\t\tFEElement* pel = FindElementFromID(p.m_neid);\n\t\t\tif (pel == 0)\n\t\t\t{\n\t\t\t\tfeLogError(\"Invalid Element ID for micro probe %d in material %d (%s)\", i + 1, m_pMat->GetID(), m_pMat->GetName().c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint nint = pel->GaussPoints();\n\t\t\tint ngp = p.m_ngp - 1;\n\t\t\tif ((ngp>=0)&&(ngp<nint))\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *pel->GetMaterialPoint(ngp);\n\t\t\t\tFEMicroMaterialPoint2O& mmpt = *mp.ExtractData<FEMicroMaterialPoint2O>();\n\t\t\t\tFERVEProbe* prve = new FERVEProbe(fem, mmpt.m_rve, p.m_szfile.c_str());\n\t\t\t\tp.m_probe = prve;\n\t\t\t\tprve->SetDebugFlag(p.m_bdebug);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfeLogError(\"Invalid gausspt number for micro-probe %d in material %d (%s)\", i + 1, m_pMat->GetID(), m_pMat->GetName().c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse \n\t\t{\n\t\t\tint fid = -p.m_neid-1;\n\t\t\tif ((fid < 0) || (fid >= m_surf.Elements()))\n\t\t\t{\n\t\t\t\tfeLogError(\"Invalid surface ID for micro-probe\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tint nnf = 0;\n\t\t\tfor (int j=0; j<fid; ++j)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el = m_surf.Element(j);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tnnf += nint;\n\t\t\t}\n\n\t\t\tFESurfaceElement& face = m_surf.Element(fid);\n\t\t\tint nint = face.GaussPoints();\n\t\t\tint k = (p.m_ngp-1)/nint;\n\t\t\tint gpt = (p.m_ngp-1)%nint;\n\t\t\tnnf += gpt;\n\n\t\t\tFEMaterialPoint& mp = *m_surf.GetData(nnf).m_pt[k];\n\t\t\tFEMicroMaterialPoint2O& mmpt2O = *mp.ExtractData<FEMicroMaterialPoint2O>();\n\t\t\tif (mmpt2O.m_elem_id != p.m_neid ) return false;\n\t\t\tif (mmpt2O.m_gpt_id  != p.m_ngp-1) return false;\n\n\t\t\tFERVEProbe* prve = new FERVEProbe(fem, mmpt2O.m_rve, p.m_szfile.c_str());\n\t\t\tp.m_probe = prve;\n\t\t\tprve->SetDebugFlag(p.m_bdebug);\n\t\t}\n*/\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticMultiscaleDomain2O::Update(const FETimeInfo& timeInfo)\n{\n\ttry\n\t{\n\t\t// call base class\n\t\tFEElasticSolidDomain2O::Update(timeInfo);\n\t}\n\tcatch (FEMultiScaleException)\n\t{\n\t\t// store all the probes\n\t\tFEMicroMaterial2O* pmat = dynamic_cast<FEMicroMaterial2O*>(m_pMat);\n\t\tint NP = pmat->Probes();\n\t\tfor (int i=0; i<NP; ++i)\n\t\t{\n\t\t\tFERVEProbe& p = pmat->Probe(i);\n\t\t\tif (p.GetDebugFlag()) p.Save();\n\t\t}\n\n\t\t// retrhow\n\t\tthrow;\n\t}\n}\n"
  },
  {
    "path": "FEBioRVE/FEElasticMultiscaleDomain2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticSolidDomain2O.h\"\n#include <FECore/tens3d.h>\n#include <FECore/tens5d.h>\n#include <FECore/tens6d.h>\n#include <FECore/FESurface.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements a domain used in an elastic remodeling problem.\n//! It differs from the FEElasticSolidDomain in that it adds a stiffness matrix\n//! due to the deformation dependent density.\nclass FEElasticMultiscaleDomain2O : public FEElasticSolidDomain2O\n{\npublic:\n\t//! constructor\n\tFEElasticMultiscaleDomain2O(FEModel* pfem);\n\t\n\t//! initialize class\n\tbool Init() override;\n\n\t//! Update \n\tvoid Update(const FETimeInfo& timeInfo) override;\n};\n"
  },
  {
    "path": "FEBioRVE/FEElasticSolidDomain2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElasticSolidDomain2O.h\"\n#include \"FEElasticMaterial2O.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEMesh.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\n// helper function for comparing two facets\nbool compare_facets(int* na, int* nb, int nodes)\n{\n\tswitch (nodes)\n\t{\n\tcase 3: // 3-node triangle\n\tcase 6: // 6-node triangle\n\tcase 7: // 7-node triangle\n\t\tif ((na[0]!=nb[0])&&(na[0]!=nb[1])&&(na[0]!=nb[2])) return false;\n\t\tif ((na[1]!=nb[0])&&(na[1]!=nb[1])&&(na[1]!=nb[2])) return false;\n\t\tif ((na[2]!=nb[0])&&(na[2]!=nb[1])&&(na[2]!=nb[2])) return false;\n\t\tbreak;\n\tcase 4: // 4-node quad\n\tcase 8: // 8-node quad\n\tcase 9: // 9-node quad\n\t\tif ((na[0]!=nb[0])&&(na[0]!=nb[1])&&(na[0]!=nb[2])&&(na[0]!=nb[3])) return false;\n\t\tif ((na[1]!=nb[0])&&(na[1]!=nb[1])&&(na[1]!=nb[2])&&(na[1]!=nb[3])) return false;\n\t\tif ((na[2]!=nb[0])&&(na[2]!=nb[1])&&(na[2]!=nb[2])&&(na[2]!=nb[3])) return false;\n\t\tif ((na[3]!=nb[0])&&(na[3]!=nb[1])&&(na[3]!=nb[2])&&(na[3]!=nb[3])) return false;\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ninline bool compare_tri(int* na, int* nb, int i, int j, int k)\n{\n\treturn ((na[0] == nb[i])&&(na[1] == nb[j])&&(na[2] == nb[k]));\n}\n\n//-----------------------------------------------------------------------------\ninline bool compare_quad(int* na, int* nb, int i, int j, int k, int l)\n{\n\treturn ((na[0] == nb[i])&&(na[1] == nb[j])&&(na[2] == nb[k])&&(na[3] == nb[l]));\n}\n\n//-----------------------------------------------------------------------------\n// helper function for mapping surface facet coordinates to element facet coordinates\nvec2d map_facet_to_facet(int* na, int* nb, int nodes, double r, double s)\n{\n\tswitch (nodes)\n\t{\n\t// triangles\n\tcase 3:\n\tcase 6:\n\tcase 7:\n\t\tif (compare_tri(na, nb, 0, 1, 2)) return vec2d(  r  ,     s);\n\t\tif (compare_tri(na, nb, 2, 0, 1)) return vec2d(    s, 1-r-s);\n\t\tif (compare_tri(na, nb, 1, 2, 0)) return vec2d(1-r-s,   r  );\n\t\tif (compare_tri(na, nb, 0, 2, 1)) return vec2d(    s,   r  );\n\t\tif (compare_tri(na, nb, 2, 1, 0)) return vec2d(  r  , 1-r-s);\n\t\tif (compare_tri(na, nb, 1, 0, 2)) return vec2d(1-r-s,     s);\n\t\tbreak;\n\t// quads\n\tcase 4:\n\tcase 8:\n\tcase 9:\n\t\tif (compare_quad(na, nb, 0, 1, 2, 3)) return vec2d(  r,   s);\n\t\tif (compare_quad(na, nb, 3, 0, 1, 2)) return vec2d(  s,  -r);\n\t\tif (compare_quad(na, nb, 2, 3, 0, 1)) return vec2d( -r,  -s);\n\t\tif (compare_quad(na, nb, 1, 2, 3, 0)) return vec2d( -s,   r);\n\t\tif (compare_quad(na, nb, 2, 1, 0, 3)) return vec2d( -s,  -r);\n\t\tif (compare_quad(na, nb, 3, 2, 1, 0)) return vec2d(  r,  -s);\n\t\tif (compare_quad(na, nb, 0, 3, 2, 1)) return vec2d(  s,   r);\n\t\tif (compare_quad(na, nb, 1, 0, 3, 2)) return vec2d( -r,   s);\n\t\tbreak;\n\t}\n\n\t// we shouldn't get here\n\tassert(false);\n\treturn vec2d(0,0);\n}\n\n//-----------------------------------------------------------------------------\n// Notice that this depends on how the facet nodes are numbered (see FEElement::GetFace) \nvec3d map_facet_to_volume_coordinates_tet(int nface, const vec2d& q)\n{\n\tdouble h1 = q.x(), h2 = q.y(), h3 = 1.0 - h1 - h2;\n\tdouble g1, g2, g3;\n\tswitch (nface)\n\t{\n\tcase 0: g1 = h1; g2 = 0.; g3 = h2; break;\n\tcase 1: g1 = h3; g2 = h1; g3 = h2; break;\n\tcase 2: g1 = 0.; g2 = h3; g3 = h2; break;\n\tcase 3: g1 = h1; g2 = h3; g3 = 0.; break;\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn vec3d(g1, g2, g3);\n}\n\n//-----------------------------------------------------------------------------\n// Notice that this depends on how the facet nodes are numbered (see FEElement::GetFace) \nvec3d map_facet_to_volume_coordinates_hex(int nface, const vec2d& q)\n{\n\tdouble h1 = q.x(), h2 = q.y();\n\tdouble g1, g2, g3;\n\tswitch (nface)\n\t{\n\tcase 0: g1 =  h1; g2 = -1.; g3 =  h2; break;\n\tcase 1: g1 =  1.; g2 =  h1; g3 =  h2; break;\n\tcase 2: g1 = -h1; g2 =  1.; g3 =  h2; break;\n\tcase 3: g1 = -1.; g2 = -h1; g3 =  h2; break;\n\tcase 4: g1 =  h2; g2 =  h1; g3 = -1.; break;\n\tcase 5: g1 =  h1; g2 =  h2; g3 =  1.; break;\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn vec3d(g1, g2, g3);\n}\n\n//-----------------------------------------------------------------------------\n// helper function that maps natural coordinates from facets to elements\nvec3d map_facet_to_solid(FEMesh& mesh, FESurfaceElement& face, FESolidElement& el, double r, double s)\n{\n\tint fn[FEElement::MAX_NODES];\n\tint nfaces = el.Faces();\n\tfor (int i=0; i<nfaces; ++i)\n\t{\n\t\tel.GetFace(i, fn);\n\t\tif (compare_facets(&face.m_node[0], fn, face.Nodes()))\n\t\t{\n\t\t\t// map the facet coordinates to the element's facet coordinates\n\t\t\t// (faces can be rotated or inverted w.r.t. the element's face)\n\t\t\tvec2d b = map_facet_to_facet(&face.m_node[0], fn, face.Nodes(), r, s);\n\n\t\t\t// convert facet coordinates to volume coordinates\n\t\t\tswitch (el.Nodes())\n\t\t\t{\n\t\t\t// tets\n\t\t\tcase 4:\n\t\t\tcase 10:\n\t\t\tcase 15: return map_facet_to_volume_coordinates_tet(i, b); break;\n\t\t\t// hexes\n\t\t\tcase 8:\n\t\t\tcase 20:\n\t\t\tcase 27: return map_facet_to_volume_coordinates_hex(i, b); break;\n\t\t\t}\n\n\t\t\tassert(false);\n\t\t}\n\t}\n\n\t// we shouldn't get here\n\tassert(false);\n\treturn vec3d(0,0,0);\n}\n\n//-----------------------------------------------------------------------------\nFEElasticSolidDomain2O::FEInternalSurface2O::FEInternalSurface2O()\n{\n\tm_h = 1.0;\n}\n\nbool FEElasticSolidDomain2O::FEInternalSurface2O::Initialize(FEElasticSolidDomain2O* dom)\n{\n\t// get the material\n\t// We need this for allocating material point data on the internal facets\n\tFEMaterial* pmat = dom->GetMaterial();\n\tassert(pmat);\n\tif (pmat == 0) return false;\n\n\t// build the inside surface\n\tFEMesh& mesh = *dom->GetMesh();\n\tm_ps = mesh.ElementBoundarySurface(false, true);\n\tif (m_ps == 0) return false;\n\n\t// allocate data\n\tint NF = m_ps->Elements();\n\tint nnf = 0;\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ps->Element(i);\n\t\tnnf += el.GaussPoints();\n\t}\n\tm_data.resize(nnf);\n\n\t// allocate material point data\n\tnnf = 0;\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& face = m_ps->Element(i);\n\t\tint nint = face.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n, ++nnf)\n\t\t{\n\t\t\tm_data[nnf].m_pt[0] = new FEMaterialPoint(pmat->CreateMaterialPointData());\n\t\t\tm_data[nnf].m_pt[1] = new FEMaterialPoint(pmat->CreateMaterialPointData());\n\n\t\t\tm_data[nnf].m_pt[0]->Init();\n\t\t\tm_data[nnf].m_pt[1]->Init();\n\n\t\t\t// iso-parametric coordinates in surface element\n\t\t\tdouble h1 = face.gr(n);\n\t\t\tdouble h2 = face.gs(n);\n\n\t\t\tfor (int k=0; k<2; ++k)\n\t\t\t{\n\t\t\t\t// get the adjacent solid element\n\t\t\t\tFESolidElement& ek = static_cast<FESolidElement&>(*face.m_elem[k].pe);\n\n\t\t\t\t// map the iso-parametric coordinates from the facet to the solid element\n\t\t\t\tm_data[nnf].ksi[k] = map_facet_to_solid(mesh, face, ek, h1, h2);\n\t\t\t}\n\n#ifndef NDEBUG\n\t\t\t// This checks that the two integration points coincide physically at the same point\n\t\t\tFESolidElement& ea = static_cast<FESolidElement&>(*face.m_elem[0].pe);\n\t\t\tFESolidElement& eb = static_cast<FESolidElement&>(*face.m_elem[1].pe);\n\t\t\tvec3d xa[FEElement::MAX_NODES];\n\t\t\tvec3d xb[FEElement::MAX_NODES];\n\t\t\tfor (int a=0; a<ea.Nodes(); a++) xa[a] = mesh.Node(ea.m_node[a]).m_r0;\n\t\t\tfor (int b=0; b<eb.Nodes(); b++) xb[b] = mesh.Node(eb.m_node[b]).m_r0;\n\t\t\tvec3d ksia = m_data[nnf].ksi[0];\n\t\t\tvec3d ksib = m_data[nnf].ksi[1];\n\n\t\t\tvec3d ra = ea.evaluate(xa, ksia.x, ksia.y, ksia.z);\n\t\t\tvec3d rb = eb.evaluate(xb, ksib.x, ksib.y, ksib.z);\n\t\t\tvec3d dr = ra - rb;\n\t\t\tdouble Dr = dr.norm();\n\t\t\tassert(Dr <1e-12);\n#endif\n\t\t}\n\t}\n\n\t// calculate the element size\n\tm_h = 0.0;\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& el = m_ps->Element(i);\n\t\tint neln = el.Nodes();\n\t\tFEBoundingBox box(mesh.Node(el.m_node[0]).m_r0);\n\t\tfor (int j=1; j<neln; ++j) box.add(mesh.Node(el.m_node[j]).m_r0);\n\n\t\tdouble R = 0.5*box.radius();\n\t\tif (R > m_h) m_h = R;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEElasticSolidDomain2O::FEElasticSolidDomain2O(FEModel* pfem) : FEElasticSolidDomain(pfem)\n{\n\tm_binitJ0 = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize element data\nbool FEElasticSolidDomain2O::Init()\n{\n\t// initialize base class first\n\tif (FEElasticSolidDomain::Init() == false) return false;\n\n\t// initialize the internal surface data\n\tif (m_surf.Initialize(this) == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\t// call base class first\n\tFEElasticSolidDomain::BuildMatrixProfile(M);\n\n\t// do the interface elements\n\tFEMesh& mesh = *GetMesh();\n\tvector<int> lm;\n\n\t// loop over all internal facets\n\tint NF = m_surf.Elements();\n\tfor (int n=0; n<NF; ++n)\n\t{\n\t\t// get the next surface element\n\t\tFESurfaceElement& el = m_surf.Element(n);\n\n\t\t// get the solid elements from this interface element\n\t\tFESolidElement& ela = static_cast<FESolidElement&>(*el.m_elem[1].pe);\n\t\tFESolidElement& elb = static_cast<FESolidElement&>(*el.m_elem[0].pe);\n\t\tint nelna = ela.Nodes();\n\t\tint nelnb = elb.Nodes();\n\n\t\t// setup the LM vector\n\t\t// TODO: this assumes that the X,Y,Z degrees of freedom are 0,1,2 respectively\n\t\tint ndof = 3*(nelna + nelnb);\n\t\tlm.resize(ndof);\n\t\tfor (int i=0; i<nelna; ++i)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(ela.m_node[i]).m_ID;\n\t\t\tlm[3*i  ] = id[0];\n\t\t\tlm[3*i+1] = id[1];\n\t\t\tlm[3*i+2] = id[2];\n\t\t}\n\t\tfor (int i=0; i<nelnb; ++i)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(elb.m_node[i]).m_ID;\n\t\t\tlm[3*(nelna+i)  ] = id[0];\n\t\t\tlm[3*(nelna+i)+1] = id[1];\n\t\t\tlm[3*(nelna+i)+2] = id[2];\n\t\t}\n\n\t\t// add it to the profile\n\t\tM.build_add(lm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tFEElasticSolidDomain::PreSolveUpdate(timeInfo);\n\n\tint NF = m_surf.Elements(), nd = 0;\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& el = m_surf.Element(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n, ++nd)\n\t\t{\n\t\t\tFEInternalSurface2O::Data& data = m_surf.GetData(nd);\n\t\t\tdata.m_pt[0]->Update(timeInfo);\n\t\t\tdata.m_pt[1]->Update(timeInfo);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::Update(const FETimeInfo& tp)\n{\n\t// call base class first\n\t// (this will call FEElasticMultiscaleDomain2O::UpdateElementStress)\n\tFEElasticSolidDomain::Update(tp);\n\n\t// update internal surfaces\n\tUpdateInternalSurfaceStresses();\n\n\t// update the kinematic variables\n\tUpdateKinematics();\n}\n\n//-----------------------------------------------------------------------------\n// Update some kinematical quantities needed for evaluating the discontinuous-Galerkin terms\nvoid FEElasticSolidDomain2O::UpdateKinematics()\n{\n\tFEMesh& mesh = *GetMesh();\n\n\t// nodal displacements\n\tvec3d ut[FEElement::MAX_NODES];\n\n\t// shape function derivatives\n\tvec3d G[FEElement::MAX_NODES];\n\n\t// loop over all facets\n\tint nd = 0;\n\tint NF = m_surf.Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\t// get the next facet\n\t\tFESurfaceElement& face = m_surf.Element(i);\n\t\tint nfn  = face.Nodes();\n\t\tint nint = face.GaussPoints();\n\n\t\t// calculate the displacement gradient jump across this facet\n\t\tfor (int m=0; m<2; ++m)\n\t\t{\n\t\t\t// evaluate the spatial gradient of shape functions\n\t\t\tFESolidElement& el = static_cast<FESolidElement&>(*face.m_elem[m].pe);\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// get the nodal displacements\n\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(el.m_node[j]);\n\t\t\t\tut[j] = node.m_rt - node.m_r0;\n\t\t\t}\n\n\t\t\t// loop over all integration points\n\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\t// get the integration point data\n\t\t\t\tFEInternalSurface2O::Data& data = m_surf.GetData(nd + n);\n\n\t\t\t\t// evaluate element Jacobian and shape function derivatives\n\t\t\t\t// at this integration point\n\t\t\t\tvec3d& ksi = data.ksi[m];\n\t\t\t\tShapeGradient0(el, ksi.x, ksi.y, ksi.z, G);\n\n\t\t\t\t// calculate displacement gradient\n\t\t\t\tmat3d Gu; Gu.zero();\n\t\t\t\tfor (int j=0; j<neln; ++j) Gu += ut[j] & G[j];\n\n\t\t\t\tif (m == 0) data.DgradU = Gu;\n\t\t\t\telse data.DgradU = Gu - data.DgradU;\n\t\t\t}\n\t\t}\n\n\t\t// don't forget to increment data counter\n\t\tnd += nint;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function evaluates the stresses at either side of the internal surface\n// facets.\nvoid FEElasticSolidDomain2O::UpdateInternalSurfaceStresses()\n{\n\tFEMesh& mesh = *GetMesh();\n\t// calculate the material\n\tFEElasticMaterial2O* pmat = dynamic_cast<FEElasticMaterial2O*>(m_pMat);\n\n\t// loop over all the internal surfaces\n\tint NF = m_surf.Elements(), nd = 0;\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\tFESurfaceElement& face = m_surf.Element(i);\n\t\tint nint = face.GaussPoints();\n\t\tfor (int n=0; n<nint; ++n, ++nd)\n\t\t{\n\t\t\tFEInternalSurface2O::Data& data =  m_surf.GetData(nd);\n\t\t\tdata.Qavg.zero();\n\t\t\t\n\t\t\tif (m_binitJ0 == false) data.J0avg.zero();\n\n\t\t\t// get the deformation gradient and determinant\n\t\t\tfor (int k=0; k<2; ++k)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *data.m_pt[k];\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tFEElasticMaterialPoint2O& pt2O = *mp.ExtractData<FEElasticMaterialPoint2O>();\n\n\t\t\t\tvec3d& ksi = data.ksi[k];\n\n\t\t\t\t// TODO: Is face.m_elem is a local index?\n\t\t\t\tFESolidElement& ek = static_cast<FESolidElement&>(*face.m_elem[k].pe);\n\n\t\t\t\t// evaluate deformation gradient, Jacobian and Hessian for this element\n\t\t\t\tpt.m_J = defgrad(ek, pt.m_F, ksi.x, ksi.y, ksi.z);\n\t\t\t\tdefhess(ek, ksi.x, ksi.y, ksi.z, pt2O.m_G);\n\n\t\t\t\t// evaluate stresses at this integration point\n\t\t\t\tpmat->Stress(mp, pt2O.m_PK1, pt2O.m_Q);\n\n\t\t\t\tdata.Qavg += pt2O.m_Q*0.5;\n\n\t\t\t\t// TODO: Can I evaluate this elsewhere?\n\t\t\t\ttens4d C;\n\t\t\t\ttens5d L, H;\n\t\t\t\ttens6d J;\n\t\t\t\tpmat->Tangent(mp, C, L, H, J);\n\t\t\t\tdata.H[k] = H;\n\t\t\t\tdata.J[k] = J;\n\n\t\t\t\tif (m_binitJ0 == false)\n\t\t\t\t{\n\t\t\t\t\t// we need to evaluate the initial stiffnesses as well\n\t\t\t\t\tdata.J0[k] = J;\n\t\t\t\t\tdata.H0[k] = H;\n\t\t\t\t\tdata.J0avg += J*0.5;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// set flag indicating J0 has been initialized\n\tif (pmat->m_buseJ0) m_binitJ0 = true;\n}\n\n//-----------------------------------------------------------------------------\n//! Update element state data (mostly stresses, but some other stuff as well)\n//! \\todo Remove the remodeling solid stuff\nvoid FEElasticSolidDomain2O::UpdateElementStress(int iel, const FETimeInfo& tp)\n{\n\t// get the solid element\n\tFESolidElement& el = m_Elem[iel];\n\t\n\t// get the number of integration points\n\tint nint = el.GaussPoints();\n\n\t// number of nodes\n\tint neln = el.Nodes();\n\n\t// nodal coordinates\n\tvec3d r0[FEElement::MAX_NODES];\n\tvec3d rt[FEElement::MAX_NODES];\n\tfor (int j=0; j<neln; ++j)\n\t{\n\t\tr0[j] = m_pMesh->Node(el.m_node[j]).m_r0;\n\t\trt[j] = m_pMesh->Node(el.m_node[j]).m_rt;\n\t}\n\n\t// get the integration weights\n\tdouble* gw = el.GaussWeights();\n\n\t// calculate the stress at this material point\n\tFEElasticMaterial2O* pmat = dynamic_cast<FEElasticMaterial2O*>(m_pMat);\n\n\t// loop over the integration points and calculate\n\t// the stress at the integration point\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\t\tFEElasticMaterialPoint2O& pt2O = *(mp.ExtractData<FEElasticMaterialPoint2O>());\n\t\t\n\t\t// material point coordinates\n\t\t// TODO: I'm not entirly happy with this solution\n\t\t//\t\t since the material point coordinates are used by most materials.\n\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\tmp.m_rt = el.Evaluate(rt, n);\n\n\t\t// get the deformation gradient and determinant\n\t\tpt.m_J = defgrad(el, pt.m_F, n);\n\t\tdefhess(el, n, pt2O.m_G);\n\n\t\t// evaluate stresses\n\t\tpmat->Stress(mp, pt2O.m_PK1, pt2O.m_Q);\n\n\t\t// store the Cauchy stress as well\n\t\tdouble J = pt.m_J;\n\t\tconst mat3d Ft = pt.m_F.transpose();\n\t\tpt.m_s = (pt2O.m_PK1*Ft).sym()/J;\t\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::InternalForces(FEGlobalVector& R)\n{\n\t// call base class first\n\tFEElasticSolidDomain::InternalForces(R);\n\n\t// add the discontinuous-Galerkin contribution\n\tInternalForcesDG1(R);\n\tInternalForcesDG2(R);\n\tInternalForcesDG3(R);\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate contribution of discontinuous-Galerkin enforcement of stress flux.\nvoid FEElasticSolidDomain2O::InternalForcesDG1(FEGlobalVector& R)\n{\n\tFEMesh& mesh = *GetMesh();\n\tFESurface& surf = *m_surf.GetSurface();\n\n\tmat3d Ji;\n\tdouble Gr[FEElement::MAX_NODES];\n\tdouble Gs[FEElement::MAX_NODES];\n\tdouble Gt[FEElement::MAX_NODES];\n\n\tvector<double> fe;\n\tvector<int> lm;\n\n\t// loop over all internal surfaces elements\n\tint nd = 0;\n\tint NF = m_surf.Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\t// get the next surface\n\t\tFESurfaceElement& face = m_surf.Element(i);\n\t\tint nfn = face.Nodes();\n\t\tint nint = face.GaussPoints();\n\n\t\t// loop over both sides\n\t\tfor (int m=0; m<2; ++m)\n\t\t{\n\t\t\t// evaluate the spatial gradient of shape functions\n\t\t\tFESolidElement& el = static_cast<FESolidElement&>(*face.m_elem[m].pe);\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// sign\n\t\t\tdouble sgn = (m==0 ? -1.0 : 1.0);\n\n\t\t\t// allocate force vector\n\t\t\tint ndof = neln*3;\n\t\t\tfe.assign(ndof, 0.0);\n\n\t\t\t// loop over all the integration points\n\t\t\tdouble* gw = face.GaussWeights();\n\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\t// get facet data\n\t\t\t\tFEInternalSurface2O::Data& data = m_surf.GetData(nd + n);\n\n\t\t\t\t// average stress across interface\n\t\t\t\ttens3drs& Qavg = data.Qavg;\n\n\t\t\t\t// calculate jacobian and surface normal\n\t\t\t\tvec3d nu(0,0,0);\n\t\t\t\tdouble J = surf.jac0(face, n, nu);\n\n\t\t\t\t// evaluate element Jacobian and shape function derivatives\n\t\t\t\t// at this integration point\n\t\t\t\tvec3d& ksi = data.ksi[m];\n\t\t\t\tinvjac0(el, ksi.x, ksi.y, ksi.z, Ji);\n\t\t\t\tel.shape_deriv(Gr, Gs, Gt, ksi.x, ksi.y, ksi.z);\n\n\t\t\t\t// loop over element nodes\n\t\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\t// calculate global gradient of shape functions\n\t\t\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\t\t\tdouble G[3];\n\t\t\t\t\tG[0] = Ji[0][0]*Gr[j]+Ji[1][0]*Gs[j]+Ji[2][0]*Gt[j];\n\t\t\t\t\tG[1] = Ji[0][1]*Gr[j]+Ji[1][1]*Gs[j]+Ji[2][1]*Gt[j];\n\t\t\t\t\tG[2] = Ji[0][2]*Gr[j]+Ji[1][2]*Gs[j]+Ji[2][2]*Gt[j];\n\n\t\t\t\t\t// put it all together\n\t\t\t\t\tdouble f[3] = {0}, Nu[3] = {nu.x, nu.y, nu.z};\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tf[0] += Qavg(0,k,l)*G[k]*Nu[l];\n\t\t\t\t\t\t\tf[1] += Qavg(1,k,l)*G[k]*Nu[l];\n\t\t\t\t\t\t\tf[2] += Qavg(2,k,l)*G[k]*Nu[l];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t// the negative sign is because we need to subtract the internal forces\n\t\t\t\t\t// from the residual\n\t\t\t\t\tfe[3*j  ] -= f[0]*gw[n]*J*sgn;\n\t\t\t\t\tfe[3*j+1] -= f[1]*gw[n]*J*sgn;\n\t\t\t\t\tfe[3*j+2] -= f[2]*gw[n]*J*sgn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// unpack the LM values\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble \n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n\n\t\t// don't forgot to increment data counter\n\t\tnd += nint;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::InternalForcesDG2(FEGlobalVector& R)\n{\n\tFEMesh& mesh = *GetMesh();\n\tFESurface& surf = *m_surf.GetSurface();\n\n\tvec3d X[FEElement::MAX_NODES];\n\tvec3d G[FEElement::MAX_NODES];\n\tmat3d H[FEElement::MAX_NODES];\n\n\tvector<double> fe;\n\tvector<int> lm;\n\n\t// loop over all internal surface elements\n\tint nd = 0;\n\tint NF = m_surf.Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\t// get the next surface element\n\t\tFESurfaceElement& face = m_surf.Element(i);\n\t\tint nfn = face.Nodes();\n\t\tint nint = face.GaussPoints();\n\n\t\t// loop over both sides\n\t\tfor (int m=0; m<2; ++m)\n\t\t{\n\t\t\tFESolidElement& el = static_cast<FESolidElement&>(*face.m_elem[m].pe);\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// get the initial nodal positions\n\t\t\tfor (int j=0; j<neln; ++j) X[j] = mesh.Node(el.m_node[j]).m_r0;\n\n\t\t\t// allocate force vector\n\t\t\tint ndof = neln*3;\n\t\t\tfe.assign(ndof, 0.0);\n\n\t\t\t// loop over integration points\n\t\t\tdouble* gw = face.GaussWeights();\n\t\t\tfor  (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\t// get facet data\n\t\t\t\tFEInternalSurface2O::Data& data = m_surf.GetData(nd + n);\n\t\t\t\tconst tens6d& J0 = data.J0[m];\n\t\t\t\tconst tens5d& H0 = data.H0[m];\n\t\t\t\tconst mat3d&  Du = data.DgradU;\n\n\t\t\t\t// calculate jacobian and surface normal\n\t\t\t\tvec3d nu(0,0,0);\n\t\t\t\tdouble J = surf.jac0(face, n, nu);\n\t\t\t\tdouble Nu[3] = {nu.x, nu.y, nu.z};\n\n\t\t\t\t// evaluate element shape function derivatives\n\t\t\t\t// at this integration point\n\t\t\t\tvec3d& ksi = data.ksi[m];\n\t\t\t\tShapeGradient0(el, ksi.x, ksi.y, ksi.z, G);\n\t\t\t\tshape_gradient2(el, X, ksi.x, ksi.y, ksi.z, H);\n\n\t\t\t\tfor (int a=0; a<neln; ++a)\n\t\t\t\t{\n\t\t\t\t\tdouble Ax[3][3][3] = {0};\n\t\t\t\t\tdouble Ay[3][3][3] = {0};\n\t\t\t\t\tdouble Az[3][3][3] = {0};\n\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tAx[0][j][k] += J0(0, j, k, 0, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t\tAx[1][j][k] += J0(1, j, k, 0, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t\tAx[2][j][k] += J0(2, j, k, 0, q, r)*H[a](q ,r);\n\n\t\t\t\t\t\t\t\t\tAy[0][j][k] += J0(0, j, k, 1, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t\tAy[1][j][k] += J0(1, j, k, 1, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t\tAy[2][j][k] += J0(2, j, k, 1, q, r)*H[a](q ,r);\n\n\t\t\t\t\t\t\t\t\tAz[0][j][k] += J0(0, j, k, 2, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t\tAz[1][j][k] += J0(1, j, k, 2, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t\tAz[2][j][k] += J0(2, j, k, 2, q, r)*H[a](q ,r);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tAx[0][j][k] += H0(0, j, k, 0, 0)*G[a].x + H0(0, j, k, 0, 1)*G[a].y + H0(0, j, k, 0, 2)*G[a].z;\n\t\t\t\t\t\t\tAx[1][j][k] += H0(1, j, k, 0, 0)*G[a].x + H0(1, j, k, 0, 1)*G[a].y + H0(1, j, k, 0, 2)*G[a].z;\n\t\t\t\t\t\t\tAx[2][j][k] += H0(2, j, k, 0, 0)*G[a].x + H0(2, j, k, 0, 1)*G[a].y + H0(2, j, k, 0, 2)*G[a].z;\n\n\t\t\t\t\t\t\tAy[0][j][k] += H0(0, j, k, 1, 0)*G[a].x + H0(0, j, k, 1, 1)*G[a].y + H0(0, j, k, 1, 2)*G[a].z;\n\t\t\t\t\t\t\tAy[1][j][k] += H0(1, j, k, 1, 0)*G[a].x + H0(1, j, k, 1, 1)*G[a].y + H0(1, j, k, 1, 2)*G[a].z;\n\t\t\t\t\t\t\tAy[2][j][k] += H0(2, j, k, 1, 0)*G[a].x + H0(2, j, k, 1, 1)*G[a].y + H0(2, j, k, 1, 2)*G[a].z;\n\n\t\t\t\t\t\t\tAz[0][j][k] += H0(0, j, k, 2, 0)*G[a].x + H0(0, j, k, 2, 1)*G[a].y + H0(0, j, k, 2, 2)*G[a].z;\n\t\t\t\t\t\t\tAz[1][j][k] += H0(1, j, k, 2, 0)*G[a].x + H0(1, j, k, 2, 1)*G[a].y + H0(1, j, k, 2, 2)*G[a].z;\n\t\t\t\t\t\t\tAz[2][j][k] += H0(2, j, k, 2, 0)*G[a].x + H0(2, j, k, 2, 1)*G[a].y + H0(2, j, k, 2, 2)*G[a].z;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdouble fa[3] = {0};\n\t\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfa[0] += Du(0,j)*Ax[0][j][k]*Nu[k] + Du(1,j)*Ax[1][j][k]*Nu[k] + Du(2,j)*Ax[2][j][k]*Nu[k];\n\t\t\t\t\t\t\t\tfa[1] += Du(0,j)*Ay[0][j][k]*Nu[k] + Du(1,j)*Ay[1][j][k]*Nu[k] + Du(2,j)*Ay[2][j][k]*Nu[k];\n\t\t\t\t\t\t\t\tfa[2] += Du(0,j)*Az[0][j][k]*Nu[k] + Du(1,j)*Az[1][j][k]*Nu[k] + Du(2,j)*Az[2][j][k]*Nu[k];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t// the negative sign is because we need to subtract the internal forces\n\t\t\t\t\t// from the residual\n\t\t\t\t\tfe[3*a  ] -= fa[0]*J*gw[n]*0.5;\n\t\t\t\t\tfe[3*a+1] -= fa[1]*J*gw[n]*0.5;\n\t\t\t\t\tfe[3*a+2] -= fa[2]*J*gw[n]*0.5;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// unpack the LM values\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble \n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n\n\t\t// don't forgot to increment data counter\n\t\tnd += nint;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate contribution of discontinuous-Galerkin enforcement of stress flux.\nvoid FEElasticSolidDomain2O::InternalForcesDG3(FEGlobalVector& R)\n{\n\tFEMesh& mesh = *GetMesh();\n\tFESurface& surf = *m_surf.GetSurface();\n\n\tFEElasticMaterial2O* pmat = dynamic_cast<FEElasticMaterial2O*>(GetMaterial());\n\tassert(pmat);\n\tdouble beta = pmat->m_beta;\n\tdouble h = m_surf.GetElementSize();\n\n\tmat3d Ji;\n\tdouble Gr[FEElement::MAX_NODES];\n\tdouble Gs[FEElement::MAX_NODES];\n\tdouble Gt[FEElement::MAX_NODES];\n\n\tvector<double> fe;\n\tvector<int> lm;\n\n\t// loop over all internal surfaces elements\n\tint nd = 0;\n\tint NF = m_surf.Elements();\n\tfor (int i=0; i<NF; ++i)\n\t{\n\t\t// get the next surface\n\t\tFESurfaceElement& face = m_surf.Element(i);\n\t\tint nfn = face.Nodes();\n\t\tint nint = face.GaussPoints();\n\n\t\t// loop over both sides\n\t\tfor (int m=0; m<2; ++m)\n\t\t{\n\t\t\t// evaluate the spatial gradient of shape functions\n\t\t\tFESolidElement& el = static_cast<FESolidElement&>(*face.m_elem[m].pe);\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// sign\n\t\t\tdouble sgn = (m==0 ? -1.0 : 1.0);\n\n\t\t\t// allocate force vector\n\t\t\tint ndof = neln*3;\n\t\t\tfe.assign(ndof, 0.0);\n\n\t\t\t// loop over all the integration points\n\t\t\tdouble* gw = face.GaussWeights();\n\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\t// get facet data\n\t\t\t\tFEInternalSurface2O::Data& data = m_surf.GetData(nd + n);\n\n\t\t\t\t// average stiffness across interface\n\t\t\t\ttens6d& J0avg = data.J0avg;\n\n\t\t\t\t// displacement gradient jump\n\t\t\t\tconst mat3d& Du = data.DgradU;\n\n\t\t\t\t// calculate jacobian and surface normal\n\t\t\t\tvec3d nu(0,0,0);\n\t\t\t\tdouble J = surf.jac0(face, n, nu);\n\n\t\t\t\t// evaluate element Jacobian and shape function derivatives\n\t\t\t\t// at this integration point\n\t\t\t\tvec3d& ksi = data.ksi[m];\n\t\t\t\tinvjac0(el, ksi.x, ksi.y, ksi.z, Ji);\n\t\t\t\tel.shape_deriv(Gr, Gs, Gt, ksi.x, ksi.y, ksi.z);\n\n\t\t\t\t// loop over element nodes\n\t\t\t\tfor (int j=0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\t// calculate global gradient of shape functions\n\t\t\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\t\t\tdouble G[3];\n\t\t\t\t\tG[0] = Ji[0][0]*Gr[j]+Ji[1][0]*Gs[j]+Ji[2][0]*Gt[j];\n\t\t\t\t\tG[1] = Ji[0][1]*Gr[j]+Ji[1][1]*Gs[j]+Ji[2][1]*Gt[j];\n\t\t\t\t\tG[2] = Ji[0][2]*Gr[j]+Ji[1][2]*Gs[j]+Ji[2][2]*Gt[j];\n\n\t\t\t\t\t// put it all together\n\t\t\t\t\tdouble f[3] = {0}, Nu[3] = {nu.x, nu.y, nu.z};\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\t\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tf[0] += Du(k,l)*Nu[p]*J0avg(k,l,p,0,q,r)*G[q]*Nu[r];\n\t\t\t\t\t\t\t\t\t\tf[1] += Du(k,l)*Nu[p]*J0avg(k,l,p,1,q,r)*G[q]*Nu[r];\n\t\t\t\t\t\t\t\t\t\tf[2] += Du(k,l)*Nu[p]*J0avg(k,l,p,2,q,r)*G[q]*Nu[r];\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t// the negative sign is because we need to subtract the internal forces\n\t\t\t\t\t// from the residual\n\t\t\t\t\tfe[3*j  ] -= f[0]*gw[n]*J*sgn*beta/h;\n\t\t\t\t\tfe[3*j+1] -= f[1]*gw[n]*J*sgn*beta/h;\n\t\t\t\t\tfe[3*j+2] -= f[2]*gw[n]*J*sgn*beta/h;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// unpack the LM values\n\t\t\tUnpackLM(el, lm);\n\n\t\t\t// assemble \n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n\n\t\t// don't forgot to increment data counter\n\t\tnd += nint;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the internal equivalent nodal forces for solid elements\nvoid FEElasticSolidDomain2O::ElementInternalForce(FESolidElement& el, vector<double>& fe)\n{\n\t// contribution from stress\n\tElementInternalForce_PF(el, fe);\n\n\t// contriubtion from higher-order stress\n\tElementInternalForce_QG(el, fe);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::ElementInternalForce_PF(FESolidElement& el, vector<double>& fe)\n{\n\tdouble Ji[3][3];\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\tdouble*\tgw = el.GaussWeights();\n\t\n\t// repeat for all integration points\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint& pt = *(mp.ExtractData<FEElasticMaterialPoint>());\n\n\t\t// calculate the jacobian and its derivative\n\t\t// (and multiply by integration weight)\n\t\tdouble detJt = invjact(el, Ji, n)*gw[n];\n\n\t\t// get the stress vector for this integration point\n\t\tmat3ds& s = pt.m_s;\n\n\t\tdouble* Gr = el.Gr(n);\n\t\tdouble* Gs = el.Gs(n);\n\t\tdouble* Gt = el.Gt(n);\n\n\t\t// --- first-order contribution ---\n\t\tfor (int i=0; i<neln; ++i)\n\t\t{\n\t\t\t// calculate global gradient of shape functions\n\t\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\t\tdouble Gx = Ji[0][0]*Gr[i]+Ji[1][0]*Gs[i]+Ji[2][0]*Gt[i];\n\t\t\tdouble Gy = Ji[0][1]*Gr[i]+Ji[1][1]*Gs[i]+Ji[2][1]*Gt[i];\n\t\t\tdouble Gz = Ji[0][2]*Gr[i]+Ji[1][2]*Gs[i]+Ji[2][2]*Gt[i];\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfe[3*i  ] -=  (Gx*s.xx() + Gy*s.xy() + Gz*s.xz())*detJt;\n\t\t\tfe[3*i+1] -=  (Gx*s.xy() + Gy*s.yy() + Gz*s.yz())*detJt;\n\t\t\tfe[3*i+2] -=  (Gx*s.xz() + Gy*s.yz() + Gz*s.zz())*detJt;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::ElementInternalForce_QG(FESolidElement& el, vector<double>& fe)\n{\n\t// get the nodal positions\n\tvec3d X[FEElement::MAX_NODES];\n\tFEMesh& mesh = *GetMesh();\n\tint neln = el.Nodes();\n\tfor (int i=0; i<neln; ++i) X[i] = mesh.Node(el.m_node[i]).m_r0;\n\n\tmat3d H[FEElement::MAX_NODES];\n\n\t// repeat for all integration points\n\tint nint = el.GaussPoints();\n\tdouble*\tgw = el.GaussWeights();\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\tFEElasticMaterialPoint2O& pt2O = *(mp.ExtractData<FEElasticMaterialPoint2O>());\n\n\t\t// get the higher-order stress\n\t\tconst tens3drs& Q = pt2O.m_Q;\n\n\t\t// we'll evaluate this term in the material frame\n\t\t// so we need the Jacobian with respect to the reference configuration\n\t\tdouble J0 = detJ0(el, n);\n\n\t\t// shape function derivatives\n\t\tshape_gradient2(el, X, n, H);\n\n\t\t// loop over nodes\n\t\tfor (int a=0; a<neln; ++a)\n\t\t{\n\t\t\tmat3d& Ha = H[a];\n\n\t\t\t// calculate internal force\n\t\t\t// the '-' sign is so that the internal forces get subtracted\n\t\t\t// from the global residual vector\n\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t{\n\t\t\t\t\tfe[3*a  ] -=  (Q(0,j,k)*Ha[j][k])*J0*gw[n];\n\t\t\t\t\tfe[3*a+1] -=  (Q(1,j,k)*Ha[j][k])*J0*gw[n];\n\t\t\t\t\tfe[3*a+2] -=  (Q(2,j,k)*Ha[j][k])*J0*gw[n];\n\t\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// repeat over all solid elements\n\tint NE = (int)m_Elem.size();\n\tFETimeInfo tp = GetFEModel()->GetTime();\n\t\n\t#pragma omp parallel for shared (NE)\n\tfor (int iel=0; iel<NE; ++iel)\n\t{\n\t\tFESolidElement& el = m_Elem[iel];\n\n\t\t// element stiffness matrix\n\t\tFEElementMatrix ke(el);\n\t\t\n\t\t// create the element's stiffness matrix\n\t\tint ndof = 3*el.Nodes();\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// calculate element stiffness\n\t\tElementStiffness(tp, iel, ke);\n\n\t\t// get the element's LM vector\n\t\tvector<int> lm;\n\t\tUnpackLM(el, lm);\n\t\tke.SetIndices(lm);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n\n\t// stiffness matrix from discontinuous Galerkin\n\tStiffnessMatrixDG(LS);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::StiffnessMatrixDG(FELinearSystem& LS)\n{\n\tFEElasticMaterial2O* pmat = dynamic_cast<FEElasticMaterial2O*>(GetMaterial());\n\tassert(pmat);\n\n\t// get stiffness flags\n\tbool bKDG1 = pmat->m_bKDG1;\n\tbool bKDG2 = pmat->m_bKDG2;\n\tbool bKDG3 = pmat->m_bKDG3;\n\tif ((bKDG1==false)&&(bKDG2==false)&&(bKDG3==false)) return;\n\n\tFEElementMatrix ke;\n\tvector<int> lm;\n\tvector<int> en;\n\n\tFEMesh& mesh = *GetMesh();\n\n\t// loop over all internal facets\n\tint nd = 0;\n\tint NF = m_surf.Elements();\n\tfor (int n=0; n<NF; ++n)\n\t{\n\t\t// get the next surface element\n\t\tFESurfaceElement& el = m_surf.Element(n);\n\n\t\t// get the solid elements from this interface element\n\t\tFESolidElement& ela = static_cast<FESolidElement&>(*el.m_elem[1].pe);\n\t\tFESolidElement& elb = static_cast<FESolidElement&>(*el.m_elem[0].pe);\n\t\tint nelna = ela.Nodes();\n\t\tint nelnb = elb.Nodes();\n\n\t\t// init element stiffness\n\t\tint ndof = 3*(nelna + nelnb);\n\t\tke.resize(ndof, ndof);\n\t\tke.zero();\n\n\t\t// get the element stiffness matrix\n\t\tif (bKDG1) ElementStiffnessMatrixDG1(el, &m_surf.GetData(nd), ke);\n\t\tif (bKDG2) ElementStiffnessMatrixDG2(el, &m_surf.GetData(nd), ke);\n\t\tif (bKDG3) ElementStiffnessMatrixDG3(el, &m_surf.GetData(nd), ke);\n\n\t\t// setup the LM vector\n\t\t// TODO: this assumes that the X,Y,Z degrees of freedom are 0,1,2 respectively\n\t\tlm.resize(ndof);\n\t\tfor (int i=0; i<nelna; ++i)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(ela.m_node[i]).m_ID;\n\t\t\tlm[3*i  ] = id[0];\n\t\t\tlm[3*i+1] = id[1];\n\t\t\tlm[3*i+2] = id[2];\n\t\t}\n\t\tfor (int i=0; i<nelnb; ++i)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(elb.m_node[i]).m_ID;\n\t\t\tlm[3*(nelna+i)  ] = id[0];\n\t\t\tlm[3*(nelna+i)+1] = id[1];\n\t\t\tlm[3*(nelna+i)+2] = id[2];\n\t\t}\n\t\tke.SetIndices(lm);\n\n\t\t// setup the en vector\n\t\ten.resize(nelna + nelnb);\n\t\tfor (int i=0; i<nelna; i++) en[i        ] = ela.m_node[i];\n\t\tfor (int i=0; i<nelnb; i++) en[i + nelna] = elb.m_node[i];\n\t\tke.SetNodes(en);\n\n\t\t// assemble into global matrix\n\t\tLS.Assemble(ke);\n\n\t\t// don't forget to increment data counter\n\t\tnd += el.GaussPoints();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::ElementStiffnessMatrixDG1(FESurfaceElement& face, FEInternalSurface2O::Data* pdata, matrix& ke)\n{\n\tFESurface& surf = *m_surf.GetSurface();\n\tFEMesh& mesh = *GetMesh();\n\n\t// get the solid elements of this interface element\n\tFESolidElement& ela = static_cast<FESolidElement&>(*face.m_elem[1].pe);\n\tFESolidElement& elb = static_cast<FESolidElement&>(*face.m_elem[0].pe);\n\n\t// get the number nodes of each element\n\tint nelna = ela.Nodes();\n\tint nelnb = elb.Nodes();\n\n\t// initialize the element stiffness matrix\n\tint ndof = 3*(nelna + nelnb);\n\n\t// nodal coordinates\n\tvec3d Xa[FEElement::MAX_NODES];\n\tvec3d Xb[FEElement::MAX_NODES];\n\tfor (int i=0; i<nelna; ++i) Xa[i] = mesh.Node(ela.m_node[i]).m_r0;\n\tfor (int i=0; i<nelnb; ++i) Xb[i] = mesh.Node(elb.m_node[i]).m_r0;\n\n\t// shape function derivatives\n\tvec3d Ga[FEElement::MAX_NODES];\n\tvec3d Gb[FEElement::MAX_NODES];\n\tmat3d Ha[FEElement::MAX_NODES];\n\tmat3d Hb[FEElement::MAX_NODES];\n\n\t// loop over all integration points\n\tint nint = face.GaussPoints();\n\tdouble* gw = face.GaussWeights();\n\tfor (int ng=0; ng<nint; ++ng)\n\t{\n\t\t// get the integration point data\n\t\tFEInternalSurface2O::Data& data = pdata[ng];\n\n\t\t// Jacobian and normal at this integration point\n\t\tvec3d nu;\n\t\tdouble J0 = surf.jac0(face, ng, nu);\n\t\tdouble Nu[3] = {nu.x, nu.y, nu.z};\n\n\t\t// shape function gradients at this integration point\n\t\tShapeGradient0(ela, data.ksi[1].x, data.ksi[1].y, data.ksi[1].z, Ga);\n\t\tShapeGradient0(elb, data.ksi[0].x, data.ksi[0].y, data.ksi[0].z, Gb);\n\n\t\tshape_gradient2(ela, Xa, data.ksi[1].x, data.ksi[1].y, data.ksi[1].z, Ha);\n\t\tshape_gradient2(elb, Xb, data.ksi[0].x, data.ksi[0].y, data.ksi[0].z, Hb);\n\n\t\t// The a-a term\n\t\tfor (int a=0; a<nelna; ++a)\n\t\t\tfor (int b=0; b<nelna; ++b)\n\t\t\t{\n\t\t\t\tvec3d& GA = Ga[a];\n\t\t\t\tvec3d& GB = Ga[b];\n\t\t\t\tmat3d& G2B = Ha[b];\n\n\t\t\t\tconst tens5d& H = data.H[1];\n\t\t\t\tconst tens6d& J = data.J[1];\n\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkik += GA.x*H(i,0,l,k,0)*GB.x + GA.x*H(i,0,l,k,1)*GB.y + GA.x*H(i,0,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.y*H(i,1,l,k,0)*GB.x + GA.y*H(i,1,l,k,1)*GB.y + GA.y*H(i,1,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.z*H(i,2,l,k,0)*GB.x + GA.z*H(i,2,l,k,1)*GB.y + GA.z*H(i,2,l,k,2)*GB.z;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] = kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkik += GA.x*J(i,0,k,l,m,n)*G2B[l][m] + GA.y*J(i,1,k,l,m,n)*G2B[l][m] + GA.z*J(i,2,k,l,m,n)*G2B[l][m];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] += kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\t// add it to the element matrix\n\t\t\t\tke.add(3*a, 3*b, kab);\n\t\t\t}\n\n\t\t// The a-b term\n\t\tfor (int a=0; a<nelna; ++a)\n\t\t\tfor (int b=0; b<nelnb; ++b)\n\t\t\t{\n\t\t\t\tvec3d& GA = Ga[a];\n\t\t\t\tvec3d& GB = Gb[b];\n\t\t\t\tmat3d& G2B = Hb[b];\n\n\t\t\t\tconst tens5d& H = data.H[0];\n\t\t\t\tconst tens6d& J = data.J[0];\n\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkik += GA.x*H(i,0,l,k,0)*GB.x + GA.x*H(i,0,l,k,1)*GB.y + GA.x*H(i,0,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.y*H(i,1,l,k,0)*GB.x + GA.y*H(i,1,l,k,1)*GB.y + GA.y*H(i,1,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.z*H(i,2,l,k,0)*GB.x + GA.z*H(i,2,l,k,1)*GB.y + GA.z*H(i,2,l,k,2)*GB.z;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] = kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkik += GA.x*J(i,0,k,l,m,n)*G2B[l][m] + GA.y*J(i,1,k,l,m,n)*G2B[l][m] + GA.z*J(i,2,k,l,m,n)*G2B[l][m];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] += kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\t// add it to the element matrix\n\t\t\t\tke.add(3*a, 3*(nelna + b), kab);\n\t\t\t}\n\n\t\t// The b-a term\n\t\tfor (int a=0; a<nelnb; ++a)\n\t\t\tfor (int b=0; b<nelna; ++b)\n\t\t\t{\n\t\t\t\tvec3d& GA = Gb[a];\n\t\t\t\tvec3d& GB = Ga[b];\n\t\t\t\tmat3d& G2B = Ha[b];\n\n\t\t\t\tconst tens5d& H = data.H[1];\n\t\t\t\tconst tens6d& J = data.J[1];\n\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkik += GA.x*H(i,0,l,k,0)*GB.x + GA.x*H(i,0,l,k,1)*GB.y + GA.x*H(i,0,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.y*H(i,1,l,k,0)*GB.x + GA.y*H(i,1,l,k,1)*GB.y + GA.y*H(i,1,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.z*H(i,2,l,k,0)*GB.x + GA.z*H(i,2,l,k,1)*GB.y + GA.z*H(i,2,l,k,2)*GB.z;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] = kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkik += GA.x*J(i,0,k,l,m,n)*G2B[l][m] + GA.y*J(i,1,k,l,m,n)*G2B[l][m] + GA.z*J(i,2,k,l,m,n)*G2B[l][m];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] += kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\t// add it to the element matrix\n\t\t\t\tke.sub(3*(nelna + a), 3*b, kab);\n\t\t\t}\n\n\t\t// The b-b term\n\t\tfor (int a=0; a<nelnb; ++a)\n\t\t\tfor (int b=0; b<nelnb; ++b)\n\t\t\t{\n\t\t\t\tvec3d& GA = Gb[a];\n\t\t\t\tvec3d& GB = Gb[b];\n\t\t\t\tmat3d& G2B = Hb[b];\n\n\t\t\t\tconst tens5d& H = data.H[0];\n\t\t\t\tconst tens6d& J = data.J[0];\n\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkik += GA.x*H(i,0,l,k,0)*GB.x + GA.x*H(i,0,l,k,1)*GB.y + GA.x*H(i,0,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.y*H(i,1,l,k,0)*GB.x + GA.y*H(i,1,l,k,1)*GB.y + GA.y*H(i,1,l,k,2)*GB.z;\n\t\t\t\t\t\t\tkik += GA.z*H(i,2,l,k,0)*GB.x + GA.z*H(i,2,l,k,1)*GB.y + GA.z*H(i,2,l,k,2)*GB.z;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] = kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kik = 0.0;\n\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkik += GA.x*J(i,0,k,l,m,n)*G2B[l][m] + GA.y*J(i,1,k,l,m,n)*G2B[l][m] + GA.z*J(i,2,k,l,m,n)*G2B[l][m];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\tkab[i][k] += kik*J0*gw[ng]*0.5;\n\t\t\t\t\t}\n\n\t\t\t\t// add it to the element matrix\n\t\t\t\tke.sub(3*(nelna + a), 3*(nelna + b), kab);\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::ElementStiffnessMatrixDG2(FESurfaceElement& face, FEInternalSurface2O::Data* pdata, matrix& ke)\n{\n\t// get the mesh and surface\n\tFEMesh& mesh = *GetMesh();\n\tFESurface& surf = *m_surf.GetSurface();\n\n\t// get the solid elements of this interface element\n\tFESolidElement& ela = static_cast<FESolidElement&>(*face.m_elem[1].pe);\n\tFESolidElement& elb = static_cast<FESolidElement&>(*face.m_elem[0].pe);\n\n\t// get the number nodes of each element\n\tint nelna = ela.Nodes();\n\tint nelnb = elb.Nodes();\n\n\t// initialize the element stiffness matrix\n\tint ndof = 3*(nelna + nelnb);\n\n\t// shape function derivatives\n\tvec3d G1a[FEElement::MAX_NODES];\n\tvec3d G1b[FEElement::MAX_NODES];\n\tmat3d G2a[FEElement::MAX_NODES];\n\tmat3d G2b[FEElement::MAX_NODES];\n\n\t// nodal coordinates\n\tvec3d Xa[FEElement::MAX_NODES];\n\tvec3d Xb[FEElement::MAX_NODES];\n\tfor (int i=0; i<nelna; ++i) Xa[i] = mesh.Node(ela.m_node[i]).m_r0;\n\tfor (int i=0; i<nelnb; ++i) Xb[i] = mesh.Node(elb.m_node[i]).m_r0;\n\n\t// loop over all integration points\n\tint nint = face.GaussPoints();\n\tdouble* gw = face.GaussWeights();\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// get the integration point data\n\t\tFEInternalSurface2O::Data& data = pdata[n];\n\n\t\t// Jacobian and normal at this integration point\n\t\tvec3d nu;\n\t\tdouble J0 = surf.jac0(face, n, nu);\n\t\tdouble Nu[3] = {nu.x, nu.y, nu.z};\n\n\t\t// shape function gradients at this integration point\n\t\tShapeGradient0(ela, data.ksi[1].x, data.ksi[1].y, data.ksi[1].z, G1a);\n\t\tShapeGradient0(elb, data.ksi[0].x, data.ksi[0].y, data.ksi[0].z, G1b);\n\t\tshape_gradient2(ela, Xa, data.ksi[1].x, data.ksi[1].y, data.ksi[1].z, G2a);\n\t\tshape_gradient2(elb, Xb, data.ksi[0].x, data.ksi[0].y, data.ksi[0].z, G2b);\n\n\t\t// stiffnesses\n\t\tconst tens5d& Ha = data.H0[1];\n\t\tconst tens5d& Hb = data.H0[0];\n\n\t\tconst tens6d& Ja = data.J0[1];\n\t\tconst tens6d& Jb = data.J0[0];\n\n\t\t// The a-a term\n\t\tfor (int a=0; a<nelna; ++a)\n\t\t\tfor (int b=0; b<nelna; ++b)\n\t\t\t{\n\t\t\t\tdouble Ax[3][3][3] = {0};\n\t\t\t\tdouble Ay[3][3][3] = {0};\n\t\t\t\tdouble Az[3][3][3] = {0};\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tAx[i][j][k] += Ja(i, j, k, 0, q, r)*G2a[a](q ,r);\n\t\t\t\t\t\t\t\t\tAy[i][j][k] += Ja(i, j, k, 1, q, r)*G2a[a](q ,r);\n\t\t\t\t\t\t\t\t\tAz[i][j][k] += Ja(i, j, k, 2, q, r)*G2a[a](q ,r);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tAx[i][j][k] += Ha(i, j, k, 0, 0)*G1a[a].x + Ha(i, j, k, 0, 1)*G1a[a].y + Ha(i, j, k, 0, 2)*G1a[a].z;\n\t\t\t\t\t\t\tAy[i][j][k] += Ha(i, j, k, 1, 0)*G1a[a].x + Ha(i, j, k, 1, 1)*G1a[a].y + Ha(i, j, k, 1, 2)*G1a[a].z;\n\t\t\t\t\t\t\tAz[i][j][k] += Ha(i, j, k, 2, 0)*G1a[a].x + Ha(i, j, k, 2, 1)*G1a[a].y + Ha(i, j, k, 2, 2)*G1a[a].z;\n\t\t\t\t\t\t}\n\n\t\t\t\tmat3d kab; kab.zero();\n\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t{\n\t\t\t\t\tkab(0,0) += G1a[b].x*Ax[0][0][k]*Nu[k] + G1a[b].y*Ax[0][1][k]*Nu[k] + G1a[b].z*Ax[0][2][k]*Nu[k];\n\t\t\t\t\tkab(0,1) += G1a[b].x*Ax[1][0][k]*Nu[k] + G1a[b].y*Ax[1][1][k]*Nu[k] + G1a[b].z*Ax[1][2][k]*Nu[k];\n\t\t\t\t\tkab(0,2) += G1a[b].x*Ax[2][0][k]*Nu[k] + G1a[b].y*Ax[2][1][k]*Nu[k] + G1a[b].z*Ax[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(1,0) += G1a[b].x*Ay[0][0][k]*Nu[k] + G1a[b].y*Ay[0][1][k]*Nu[k] + G1a[b].z*Ay[0][2][k]*Nu[k];\n\t\t\t\t\tkab(1,1) += G1a[b].x*Ay[1][0][k]*Nu[k] + G1a[b].y*Ay[1][1][k]*Nu[k] + G1a[b].z*Ay[1][2][k]*Nu[k];\n\t\t\t\t\tkab(1,2) += G1a[b].x*Ay[2][0][k]*Nu[k] + G1a[b].y*Ay[2][1][k]*Nu[k] + G1a[b].z*Ay[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(2,0) += G1a[b].x*Az[0][0][k]*Nu[k] + G1a[b].y*Az[0][1][k]*Nu[k] + G1a[b].z*Az[0][2][k]*Nu[k];\n\t\t\t\t\tkab(2,1) += G1a[b].x*Az[1][0][k]*Nu[k] + G1a[b].y*Az[1][1][k]*Nu[k] + G1a[b].z*Az[1][2][k]*Nu[k];\n\t\t\t\t\tkab(2,2) += G1a[b].x*Az[2][0][k]*Nu[k] + G1a[b].y*Az[2][1][k]*Nu[k] + G1a[b].z*Az[2][2][k]*Nu[k];\n\t\t\t\t}\n\t\t\t\t// multiply by weights\n\t\t\t\tkab *= J0*gw[n]*0.5;\n\n\t\t\t\tke.add(3*a, 3*b, kab);\n\t\t\t}\n\n\t\t// The a-b term\n\t\tfor (int a=0; a<nelna; ++a)\n\t\t\tfor (int b=0; b<nelnb; ++b)\n\t\t\t{\n\t\t\t\tdouble Ax[3][3][3] = {0};\n\t\t\t\tdouble Ay[3][3][3] = {0};\n\t\t\t\tdouble Az[3][3][3] = {0};\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tAx[i][j][k] += Ja(i, j, k, 0, q, r)*G2a[a](q ,r);\n\t\t\t\t\t\t\t\t\tAy[i][j][k] += Ja(i, j, k, 1, q, r)*G2a[a](q ,r);\n\t\t\t\t\t\t\t\t\tAz[i][j][k] += Ja(i, j, k, 2, q, r)*G2a[a](q ,r);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tAx[i][j][k] += Ha(i, j, k, 0, 0)*G1a[a].x + Ha(i, j, k, 0, 1)*G1a[a].y + Ha(i, j, k, 0, 2)*G1a[a].z;\n\t\t\t\t\t\t\tAy[i][j][k] += Ha(i, j, k, 1, 0)*G1a[a].x + Ha(i, j, k, 1, 1)*G1a[a].y + Ha(i, j, k, 1, 2)*G1a[a].z;\n\t\t\t\t\t\t\tAz[i][j][k] += Ha(i, j, k, 2, 0)*G1a[a].x + Ha(i, j, k, 2, 1)*G1a[a].y + Ha(i, j, k, 2, 2)*G1a[a].z;\n\t\t\t\t\t\t}\n\n\t\t\t\tmat3d kab; kab.zero();\n\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t{\n\t\t\t\t\tkab(0,0) += G1b[b].x*Ax[0][0][k]*Nu[k] + G1b[b].y*Ax[0][1][k]*Nu[k] + G1b[b].z*Ax[0][2][k]*Nu[k];\n\t\t\t\t\tkab(0,1) += G1b[b].x*Ax[1][0][k]*Nu[k] + G1b[b].y*Ax[1][1][k]*Nu[k] + G1b[b].z*Ax[1][2][k]*Nu[k];\n\t\t\t\t\tkab(0,2) += G1b[b].x*Ax[2][0][k]*Nu[k] + G1b[b].y*Ax[2][1][k]*Nu[k] + G1b[b].z*Ax[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(1,0) += G1b[b].x*Ay[0][0][k]*Nu[k] + G1b[b].y*Ay[0][1][k]*Nu[k] + G1b[b].z*Ay[0][2][k]*Nu[k];\n\t\t\t\t\tkab(1,1) += G1b[b].x*Ay[1][0][k]*Nu[k] + G1b[b].y*Ay[1][1][k]*Nu[k] + G1b[b].z*Ay[1][2][k]*Nu[k];\n\t\t\t\t\tkab(1,2) += G1b[b].x*Ay[2][0][k]*Nu[k] + G1b[b].y*Ay[2][1][k]*Nu[k] + G1b[b].z*Ay[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(2,0) += G1b[b].x*Az[0][0][k]*Nu[k] + G1b[b].y*Az[0][1][k]*Nu[k] + G1b[b].z*Az[0][2][k]*Nu[k];\n\t\t\t\t\tkab(2,1) += G1b[b].x*Az[1][0][k]*Nu[k] + G1b[b].y*Az[1][1][k]*Nu[k] + G1b[b].z*Az[1][2][k]*Nu[k];\n\t\t\t\t\tkab(2,2) += G1b[b].x*Az[2][0][k]*Nu[k] + G1b[b].y*Az[2][1][k]*Nu[k] + G1b[b].z*Az[2][2][k]*Nu[k];\n\t\t\t\t}\n\t\t\t\t// multiply by weights\n\t\t\t\tkab *= J0*gw[n]*0.5;\n\n\t\t\t\tke.sub(3*a, 3*(nelna + b), kab);\n\t\t\t}\n\n\t\t// The b-a term\n\t\tfor (int a=0; a<nelnb; ++a)\n\t\t\tfor (int b=0; b<nelna; ++b)\n\t\t\t{\n\t\t\t\tdouble Ax[3][3][3] = {0};\n\t\t\t\tdouble Ay[3][3][3] = {0};\n\t\t\t\tdouble Az[3][3][3] = {0};\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tAx[i][j][k] += Jb(i, j, k, 0, q, r)*G2b[a](q ,r);\n\t\t\t\t\t\t\t\t\tAy[i][j][k] += Jb(i, j, k, 1, q, r)*G2b[a](q ,r);\n\t\t\t\t\t\t\t\t\tAz[i][j][k] += Jb(i, j, k, 2, q, r)*G2b[a](q ,r);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tAx[i][j][k] += Hb(i, j, k, 0, 0)*G1b[a].x + Hb(i, j, k, 0, 1)*G1b[a].y + Hb(i, j, k, 0, 2)*G1b[a].z;\n\t\t\t\t\t\t\tAy[i][j][k] += Hb(i, j, k, 1, 0)*G1b[a].x + Hb(i, j, k, 1, 1)*G1b[a].y + Hb(i, j, k, 1, 2)*G1b[a].z;\n\t\t\t\t\t\t\tAz[i][j][k] += Hb(i, j, k, 2, 0)*G1b[a].x + Hb(i, j, k, 2, 1)*G1b[a].y + Hb(i, j, k, 2, 2)*G1b[a].z;\n\t\t\t\t\t\t}\n\n\t\t\t\tmat3d kab; kab.zero();\n\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t{\n\t\t\t\t\tkab(0,0) += G1a[b].x*Ax[0][0][k]*Nu[k] + G1a[b].y*Ax[0][1][k]*Nu[k] + G1a[b].z*Ax[0][2][k]*Nu[k];\n\t\t\t\t\tkab(0,1) += G1a[b].x*Ax[1][0][k]*Nu[k] + G1a[b].y*Ax[1][1][k]*Nu[k] + G1a[b].z*Ax[1][2][k]*Nu[k];\n\t\t\t\t\tkab(0,2) += G1a[b].x*Ax[2][0][k]*Nu[k] + G1a[b].y*Ax[2][1][k]*Nu[k] + G1a[b].z*Ax[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(1,0) += G1a[b].x*Ay[0][0][k]*Nu[k] + G1a[b].y*Ay[0][1][k]*Nu[k] + G1a[b].z*Ay[0][2][k]*Nu[k];\n\t\t\t\t\tkab(1,1) += G1a[b].x*Ay[1][0][k]*Nu[k] + G1a[b].y*Ay[1][1][k]*Nu[k] + G1a[b].z*Ay[1][2][k]*Nu[k];\n\t\t\t\t\tkab(1,2) += G1a[b].x*Ay[2][0][k]*Nu[k] + G1a[b].y*Ay[2][1][k]*Nu[k] + G1a[b].z*Ay[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(2,0) += G1a[b].x*Az[0][0][k]*Nu[k] + G1a[b].y*Az[0][1][k]*Nu[k] + G1a[b].z*Az[0][2][k]*Nu[k];\n\t\t\t\t\tkab(2,1) += G1a[b].x*Az[1][0][k]*Nu[k] + G1a[b].y*Az[1][1][k]*Nu[k] + G1a[b].z*Az[1][2][k]*Nu[k];\n\t\t\t\t\tkab(2,2) += G1a[b].x*Az[2][0][k]*Nu[k] + G1a[b].y*Az[2][1][k]*Nu[k] + G1a[b].z*Az[2][2][k]*Nu[k];\n\t\t\t\t}\n\t\t\t\t// multiply by weights\n\t\t\t\tkab *= J0*gw[n]*0.5;\n\n\t\t\t\tke.add(3*(nelna + a), 3*b, kab);\n\t\t\t}\n\n\t\t// The b-b term\n\t\tfor (int a=0; a<nelnb; ++a)\n\t\t\tfor (int b=0; b<nelnb; ++b)\n\t\t\t{\n\t\t\t\tdouble Ax[3][3][3] = {0};\n\t\t\t\tdouble Ay[3][3][3] = {0};\n\t\t\t\tdouble Az[3][3][3] = {0};\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tAx[i][j][k] += Jb(i, j, k, 0, q, r)*G2b[a](q ,r);\n\t\t\t\t\t\t\t\t\tAy[i][j][k] += Jb(i, j, k, 1, q, r)*G2b[a](q ,r);\n\t\t\t\t\t\t\t\t\tAz[i][j][k] += Jb(i, j, k, 2, q, r)*G2b[a](q ,r);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tAx[i][j][k] += Hb(i, j, k, 0, 0)*G1b[a].x + Hb(i, j, k, 0, 1)*G1b[a].y + Hb(i, j, k, 0, 2)*G1b[a].z;\n\t\t\t\t\t\t\tAy[i][j][k] += Hb(i, j, k, 1, 0)*G1b[a].x + Hb(i, j, k, 1, 1)*G1b[a].y + Hb(i, j, k, 1, 2)*G1b[a].z;\n\t\t\t\t\t\t\tAz[i][j][k] += Hb(i, j, k, 2, 0)*G1b[a].x + Hb(i, j, k, 2, 1)*G1b[a].y + Hb(i, j, k, 2, 2)*G1b[a].z;\n\t\t\t\t\t\t}\n\n\t\t\t\tmat3d kab; kab.zero();\n\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t{\n\t\t\t\t\tkab(0,0) += G1b[b].x*Ax[0][0][k]*Nu[k] + G1b[b].y*Ax[0][1][k]*Nu[k] + G1b[b].z*Ax[0][2][k]*Nu[k];\n\t\t\t\t\tkab(0,1) += G1b[b].x*Ax[1][0][k]*Nu[k] + G1b[b].y*Ax[1][1][k]*Nu[k] + G1b[b].z*Ax[1][2][k]*Nu[k];\n\t\t\t\t\tkab(0,2) += G1b[b].x*Ax[2][0][k]*Nu[k] + G1b[b].y*Ax[2][1][k]*Nu[k] + G1b[b].z*Ax[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(1,0) += G1b[b].x*Ay[0][0][k]*Nu[k] + G1b[b].y*Ay[0][1][k]*Nu[k] + G1b[b].z*Ay[0][2][k]*Nu[k];\n\t\t\t\t\tkab(1,1) += G1b[b].x*Ay[1][0][k]*Nu[k] + G1b[b].y*Ay[1][1][k]*Nu[k] + G1b[b].z*Ay[1][2][k]*Nu[k];\n\t\t\t\t\tkab(1,2) += G1b[b].x*Ay[2][0][k]*Nu[k] + G1b[b].y*Ay[2][1][k]*Nu[k] + G1b[b].z*Ay[2][2][k]*Nu[k];\n\n\t\t\t\t\tkab(2,0) += G1b[b].x*Az[0][0][k]*Nu[k] + G1b[b].y*Az[0][1][k]*Nu[k] + G1b[b].z*Az[0][2][k]*Nu[k];\n\t\t\t\t\tkab(2,1) += G1b[b].x*Az[1][0][k]*Nu[k] + G1b[b].y*Az[1][1][k]*Nu[k] + G1b[b].z*Az[1][2][k]*Nu[k];\n\t\t\t\t\tkab(2,2) += G1b[b].x*Az[2][0][k]*Nu[k] + G1b[b].y*Az[2][1][k]*Nu[k] + G1b[b].z*Az[2][2][k]*Nu[k];\n\t\t\t\t}\n\t\t\t\t// multiply by weights\n\t\t\t\tkab *= J0*gw[n]*0.5;\n\n\t\t\t\tke.sub(3*(nelna + a), 3*(nelna + b), kab);\n\t\t\t}\n\t\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElasticSolidDomain2O::ElementStiffnessMatrixDG3(FESurfaceElement& face, FEInternalSurface2O::Data* pdata, matrix& ke)\n{\n\t// get the beta parameter\n\tFEElasticMaterial2O* pmat = dynamic_cast<FEElasticMaterial2O*>(GetMaterial());\n\tdouble beta = pmat->m_beta;\n\n\tdouble h = m_surf.GetElementSize();\n\n\t// get the surface\n\tFESurface& surf = *m_surf.GetSurface();\n\n\t// get the solid elements of this interface element\n\tFESolidElement& ela = static_cast<FESolidElement&>(*face.m_elem[1].pe);\n\tFESolidElement& elb = static_cast<FESolidElement&>(*face.m_elem[0].pe);\n\n\t// get the number nodes of each element\n\tint nelna = ela.Nodes();\n\tint nelnb = elb.Nodes();\n\n\t// initialize the element stiffness matrix\n\tint ndof = 3*(nelna + nelnb);\n\n\t// shape function derivatives\n\tvec3d Ga[FEElement::MAX_NODES];\n\tvec3d Gb[FEElement::MAX_NODES];\n\n\t// loop over all integration points\n\tint nint = face.GaussPoints();\n\tdouble* gw = face.GaussWeights();\n\tfor (int n=0; n<nint; ++n)\n\t{\n\t\t// get the integration point data\n\t\tFEInternalSurface2O::Data& data = pdata[n];\n\n\t\t// Jacobian and normal at this integration point\n\t\tvec3d nu;\n\t\tdouble J0 = surf.jac0(face, n, nu);\n\t\tdouble Nu[3] = {nu.x, nu.y, nu.z};\n\n\t\t// shape function gradients at this integration point\n\t\tShapeGradient0(ela, data.ksi[1].x, data.ksi[1].y, data.ksi[1].z, Ga);\n\t\tShapeGradient0(elb, data.ksi[0].x, data.ksi[0].y, data.ksi[0].z, Gb);\n\n\t\t// average stiffness \n\t\ttens6d& Javg = data.J0avg;\n\n\t\t// The ++ term\n\t\tfor (int a=0; a<nelna; ++a)\n\t\t\tfor (int b=0; b<nelna; b++)\n\t\t\t{\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kip = 0.0;\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble NN = Nu[k]*Nu[r];\n\t\t\t\t\t\t\t\tkip += Ga[a].x*Ga[b].x*Javg(i,0,k,p,0,r)*NN + Ga[a].x*Ga[b].y*Javg(i,0,k,p,1,r)*NN + Ga[a].x*Ga[b].z*Javg(i,0,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Ga[a].y*Ga[b].x*Javg(i,1,k,p,0,r)*NN + Ga[a].y*Ga[b].y*Javg(i,1,k,p,1,r)*NN + Ga[a].y*Ga[b].z*Javg(i,1,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Ga[a].z*Ga[b].x*Javg(i,2,k,p,0,r)*NN + Ga[a].z*Ga[b].y*Javg(i,2,k,p,1,r)*NN + Ga[a].z*Ga[b].z*Javg(i,2,k,p,2,r)*NN;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkab[i][p] = kip*(beta/h)*J0*gw[n];\n\t\t\t\t\t}\n\n\t\t\t\tke.add(3*a, 3*b, kab);\n\t\t\t}\n\n\t\t// The +- term\n\t\tfor (int a=0; a<nelna; ++a)\n\t\t\tfor (int b=0; b<nelnb; b++)\n\t\t\t{\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kip = 0.0;\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble NN = Nu[k]*Nu[r];\n\t\t\t\t\t\t\t\tkip += Ga[a].x*Gb[b].x*Javg(i,0,k,p,0,r)*NN + Ga[a].x*Gb[b].y*Javg(i,0,k,p,1,r)*NN + Ga[a].x*Gb[b].z*Javg(i,0,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Ga[a].y*Gb[b].x*Javg(i,1,k,p,0,r)*NN + Ga[a].y*Gb[b].y*Javg(i,1,k,p,1,r)*NN + Ga[a].y*Gb[b].z*Javg(i,1,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Ga[a].z*Gb[b].x*Javg(i,2,k,p,0,r)*NN + Ga[a].z*Gb[b].y*Javg(i,2,k,p,1,r)*NN + Ga[a].z*Gb[b].z*Javg(i,2,k,p,2,r)*NN;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkab[i][p] = kip*(beta/h)*J0*gw[n];\n\t\t\t\t\t}\n\n\t\t\t\tke.sub(3*a, 3*(nelna + b), kab);\n\t\t\t}\n\n\t\t// The -+ term\n\t\tfor (int a=0; a<nelnb; ++a)\n\t\t\tfor (int b=0; b<nelna; b++)\n\t\t\t{\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kip = 0.0;\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble NN = Nu[k]*Nu[r];\n\t\t\t\t\t\t\t\tkip += Gb[a].x*Ga[b].x*Javg(i,0,k,p,0,r)*NN + Gb[a].x*Ga[b].y*Javg(i,0,k,p,1,r)*NN + Gb[a].x*Ga[b].z*Javg(i,0,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Gb[a].y*Ga[b].x*Javg(i,1,k,p,0,r)*NN + Gb[a].y*Ga[b].y*Javg(i,1,k,p,1,r)*NN + Gb[a].y*Ga[b].z*Javg(i,1,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Gb[a].z*Ga[b].x*Javg(i,2,k,p,0,r)*NN + Gb[a].z*Ga[b].y*Javg(i,2,k,p,1,r)*NN + Gb[a].z*Ga[b].z*Javg(i,2,k,p,2,r)*NN;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkab[i][p] = kip*(beta/h)*J0*gw[n];\n\t\t\t\t\t}\n\n\t\t\t\tke.sub(3*(a+nelna), 3*b, kab);\n\t\t\t}\n\n\t\t// The -- term\n\t\tfor (int a=0; a<nelnb; ++a)\n\t\t\tfor (int b=0; b<nelnb; b++)\n\t\t\t{\n\t\t\t\tmat3d kab;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble kip = 0.0;\n\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdouble NN = Nu[k]*Nu[r];\n\t\t\t\t\t\t\t\tkip += Gb[a].x*Gb[b].x*Javg(i,0,k,p,0,r)*NN + Gb[a].x*Gb[b].y*Javg(i,0,k,p,1,r)*NN + Gb[a].x*Gb[b].z*Javg(i,0,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Gb[a].y*Gb[b].x*Javg(i,1,k,p,0,r)*NN + Gb[a].y*Gb[b].y*Javg(i,1,k,p,1,r)*NN + Gb[a].y*Gb[b].z*Javg(i,1,k,p,2,r)*NN;\n\t\t\t\t\t\t\t\tkip += Gb[a].z*Gb[b].x*Javg(i,2,k,p,0,r)*NN + Gb[a].z*Gb[b].y*Javg(i,2,k,p,1,r)*NN + Gb[a].z*Gb[b].z*Javg(i,2,k,p,2,r)*NN;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkab[i][p] = kip*(beta/h)*J0*gw[n];\n\t\t\t\t\t}\n\n\t\t\t\tke.add(3*(nelna + a), 3*(nelna + b), kab);\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates element material stiffness element matrix\nvoid FEElasticSolidDomain2O::ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke)\n{\n\tFEElasticMaterial2O* pmat = dynamic_cast<FEElasticMaterial2O*>(m_pMat);\n\n\tFESolidElement& el = Element(iel);\n\n\t// Get the current element's data\n\tconst int neln = el.Nodes();\n\tconst int ndof = 3*neln;\n\n\t// global derivatives of shape functions\n\t// Gx = dN/dx\n\tvec3d G[FEElement::MAX_NODES];\n\n\t// H = d2N/dXdX\n\tmat3d G2[FEElement::MAX_NODES];\n\n\t// get the initial nodal coordinates\n\tvec3d X[FEElement::MAX_NODES];\n\tFEMesh& mesh = *GetMesh();\n\tmesh.GetInitialNodalCoordinates(el, X);\n\n\tke.zero();\n\t\n\t// calculate element stiffness matrix\n\tconst int nint = el.GaussPoints();\n\tconst double *gw = el.GaussWeights();\n\tfor (int ni=0; ni<nint; ++ni)\n\t{\n\t\t// get the material point data\n\t\t// NOTE: deformation gradient and determinant have already been evaluated in the stress routine\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(ni);\n\t\tFEElasticMaterialPoint2O& pt2O = *(mp.ExtractData<FEElasticMaterialPoint2O>());\n\n\t\t// get the material tangents\n\t\ttens4d C;\n\t\ttens5d L;\n\t\ttens5d H;\n\t\ttens6d J;\n\t\tpmat->Tangent(mp, C, L, H, J);\n\n\t\t// get the first derivative of shape functions\n\t\tdouble J0 = ShapeGradient0(el, ni, G);\n\n\t\t// second derivative of shape functions\n\t\tshape_gradient2(el, X, ni, G2);\n\n\t\tfor (int a=0; a<neln; ++a)\n\t\t{\n\t\t\tdouble Ga[3] = {G[a].x, G[a].y, G[a].z};\n\t\t\tmat3d& Ha = G2[a];\n\n\t\t\tfor (int b=0; b<neln; ++b)\n\t\t\t{\n\t\t\t\tdouble Gb[3] = {G[b].x, G[b].y, G[b].z};\n\t\t\t\tmat3d& Hb = G2[b];\n\n\t\t\t\t// gradN*C*gradN\n\t\t\t\tmat3d Kab; Kab.zero();\n\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tKab[0][0] += (Ga[j]*C(0,j,0,l)*Gb[l]);\n\t\t\t\t\t\tKab[0][1] += (Ga[j]*C(0,j,1,l)*Gb[l]);\n\t\t\t\t\t\tKab[0][2] += (Ga[j]*C(0,j,2,l)*Gb[l]);\n\n\t\t\t\t\t\tKab[1][0] += (Ga[j]*C(1,j,0,l)*Gb[l]);\n\t\t\t\t\t\tKab[1][1] += (Ga[j]*C(1,j,1,l)*Gb[l]);\n\t\t\t\t\t\tKab[1][2] += (Ga[j]*C(1,j,2,l)*Gb[l]);\n\n\t\t\t\t\t\tKab[2][0] += (Ga[j]*C(2,j,0,l)*Gb[l]);\n\t\t\t\t\t\tKab[2][1] += (Ga[j]*C(2,j,1,l)*Gb[l]);\n\t\t\t\t\t\tKab[2][2] += (Ga[j]*C(2,j,2,l)*Gb[l]);\n\n\t\t\t\t\t}\n\n\t\t\t\t// gradN*L*grad2N\n\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tKab[0][0] += (Ga[j]*L(0, j, 0, l, m)*Ha(l, m));\n\t\t\t\t\t\t\tKab[0][1] += (Ga[j]*L(0, j, 1, l, m)*Ha(l, m));\n\t\t\t\t\t\t\tKab[0][2] += (Ga[j]*L(0, j, 2, l, m)*Ha(l, m));\n\n\t\t\t\t\t\t\tKab[1][0] += (Ga[j]*L(1, j, 0, l, m)*Ha(l, m));\n\t\t\t\t\t\t\tKab[1][1] += (Ga[j]*L(1, j, 1, l, m)*Ha(l, m));\n\t\t\t\t\t\t\tKab[1][2] += (Ga[j]*L(1, j, 2, l, m)*Ha(l, m));\n\n\t\t\t\t\t\t\tKab[2][0] += (Ga[j]*L(2, j, 0, l, m)*Ha(l, m));\n\t\t\t\t\t\t\tKab[2][1] += (Ga[j]*L(2, j, 1, l, m)*Ha(l, m));\n\t\t\t\t\t\t\tKab[2][2] += (Ga[j]*L(2, j, 2, l, m)*Ha(l, m));\n\t\t\t\t\t\t}\n\n\t\t\t\t// grad2N*H*gradN\n\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tKab[0][0] += (Ha(j,k)*H(0, j, k, 0, m)*Ga[m]);\n\t\t\t\t\t\t\tKab[0][1] += (Ha(j,k)*H(0, j, k, 1, m)*Ga[m]);\n\t\t\t\t\t\t\tKab[0][2] += (Ha(j,k)*H(0, j, k, 2, m)*Ga[m]);\n\n\t\t\t\t\t\t\tKab[1][0] += (Ha(j,k)*H(1, j, k, 0, m)*Ga[m]);\n\t\t\t\t\t\t\tKab[1][1] += (Ha(j,k)*H(1, j, k, 1, m)*Ga[m]);\n\t\t\t\t\t\t\tKab[1][2] += (Ha(j,k)*H(1, j, k, 2, m)*Ga[m]);\n\n\t\t\t\t\t\t\tKab[2][0] += (Ha(j,k)*H(2, j, k, 0, m)*Ga[m]);\n\t\t\t\t\t\t\tKab[2][1] += (Ha(j,k)*H(2, j, k, 1, m)*Ga[m]);\n\t\t\t\t\t\t\tKab[2][2] += (Ha(j,k)*H(2, j, k, 2, m)*Ga[m]);\n\t\t\t\t\t\t}\n\n\t\t\t\t// grad2N*J*grad2N\n\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tKab[0][0] += (Ha(j,k)*J(0, j, k, 0, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t\tKab[0][1] += (Ha(j,k)*J(0, j, k, 1, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t\tKab[0][2] += (Ha(j,k)*J(0, j, k, 2, m, n)*Hb(m,n));\n\n\t\t\t\t\t\t\t\tKab[1][0] += (Ha(j,k)*J(1, j, k, 0, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t\tKab[1][1] += (Ha(j,k)*J(1, j, k, 1, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t\tKab[1][2] += (Ha(j,k)*J(1, j, k, 2, m, n)*Hb(m,n));\n\n\t\t\t\t\t\t\t\tKab[2][0] += (Ha(j,k)*J(2, j, k, 0, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t\tKab[2][1] += (Ha(j,k)*J(2, j, k, 1, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t\tKab[2][2] += (Ha(j,k)*J(2, j, k, 2, m, n)*Hb(m,n));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t// multiply by jacobian and integration weight\n\t\t\t\tKab *= J0*gw[ni];\n\n\t\t\t\t// add it to the element matrix\n\t\t\t\tke.add(3*a, 3*b, Kab);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the gradient of deformation gradient of element el at integration point n.\n//! The gradient of the deformation gradient is returned in G.\nvoid FEElasticSolidDomain2O::defhess(FESolidElement &el, int n, tens3drs &G)\n{\n\tint neln = el.Nodes();\n\n\t// get the nodal positions\n\tvec3d X[FEElement::MAX_NODES];\n\tvec3d x[FEElement::MAX_NODES];\n\tFEMesh& mesh = *GetMesh();\n\tfor (int i=0; i<neln; ++i)\n\t{\n\t\tX[i] = mesh.Node(el.m_node[i]).m_r0;\n\t\tx[i] = mesh.Node(el.m_node[i]).m_rt;\n\t}\n\n\tmat3d H[FEElement::MAX_NODES];\n\tshape_gradient2(el, X, n, H);\n\n\t// loop over nodes\n\tG.zero();\n\tfor (int a=0; a<neln; ++a)\n\t{\n\t\tconst mat3d& Ha = H[a];\n\n\t\t// calculate gradient of deformation gradient\n\t\t// Note that k >= j. Since tensdrs has symmetries this\n\t\t// prevents overwriting of symmetric components\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=j; k<3; ++k)\n\t\t\t{\n\t\t\t\tG(0,j,k) += Ha(j,k)*x[a].x;\n\t\t\t\tG(1,j,k) += Ha(j,k)*x[a].y;\n\t\t\t\tG(2,j,k) += Ha(j,k)*x[a].z;\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the gradient of deformation gradient of element el at integration point n.\n//! The gradient of the deformation gradient is returned in G.\nvoid FEElasticSolidDomain2O::defhess(FESolidElement &el, double r, double s, double t, tens3drs &G)\n{\n\tint neln = el.Nodes();\n\n\t// get the nodal positions\n\tvec3d X[FEElement::MAX_NODES];\n\tvec3d x[FEElement::MAX_NODES];\n\tFEMesh& mesh = *GetMesh();\n\tfor (int i=0; i<neln; ++i)\n\t{\n\t\tX[i] = mesh.Node(el.m_node[i]).m_r0;\n\t\tx[i] = mesh.Node(el.m_node[i]).m_rt;\n\t}\n\n\tmat3d H[FEElement::MAX_NODES];\n\tshape_gradient2(el, X, r, s, t, H);\n\n\t// loop over nodes\n\tG.zero();\n\tfor (int a=0; a<neln; ++a)\n\t{\n\t\tmat3d& Ha = H[a];\n\n\t\t// calculate gradient of deformation gradient\n\t\t// Note that k >= j. Since tensdrs has symmetries this\n\t\t// prevents overwriting of symmetric components\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=j; k<3; ++k)\n\t\t\t{\n\t\t\t\tG(0,j,k) += Ha[j][k]*x[a].x;\n\t\t\t\tG(1,j,k) += Ha[j][k]*x[a].y;\n\t\t\t\tG(2,j,k) += Ha[j][k]*x[a].z;\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the second derivative of shape function N[node] with respect\n//! to the material coordinates.\nvoid FEElasticSolidDomain2O::shape_gradient2(const FESolidElement& el, vec3d* X, int n, mat3d* H)\n{\n\tint neln = el.Nodes();\n\tdouble*\tgw = el.GaussWeights();\n\n\t// inverse of Jacobian matrix\n\tdouble Ji[3][3];\n\n\t// we'll evaluate this term in the material frame\n\t// so we need the Jacobian with respect to the reference configuration\n\tinvjac0(el, Ji, n);\n\n\t// shape function derivatives\n\tdouble* Gr = el.Gr(n);\n\tdouble* Gs = el.Gs(n);\n\tdouble* Gt = el.Gt(n);\n\n\tdouble *Grrn = el.Grr(n); double *Grsn = el.Grs(n); double *Grtn = el.Grt(n);\n\tdouble *Gsrn = el.Gsr(n); double *Gssn = el.Gss(n); double *Gstn = el.Gst(n);\n\tdouble *Gtrn = el.Gtr(n); double *Gtsn = el.Gts(n); double *Gttn = el.Gtt(n);\n\n\t// calculate K = dJ/dr\n\tdouble K[3][3][3] = {0};\n\tfor (int a=0; a<neln; ++a)\n\t{\n\t\t// second derivatives of shape functions\n\t\tdouble G2[3][3];\n\t\tG2[0][0] = Grrn[a]; G2[0][1] = Grsn[a]; G2[0][2] = Grtn[a];\n\t\tG2[1][0] = Gsrn[a]; G2[1][1] = Gssn[a]; G2[1][2] = Gstn[a];\n\t\tG2[2][0] = Gtrn[a]; G2[2][1] = Gtsn[a]; G2[2][2] = Gttn[a];\n\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t{\n\t\t\t\tK[0][j][k] += G2[j][k]*X[a].x;\n\t\t\t\tK[1][j][k] += G2[j][k]*X[a].y;\n\t\t\t\tK[2][j][k] += G2[j][k]*X[a].z;\n\t\t\t}\n\t}\n\n\t// calculate A = -J^-1*dJ/drJ^-1\n\tdouble A[3][3][3] = {0};\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t{\n\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t{\n\t\t\t\t\tA[i][j][0] -= Ji[j][p]*K[p][q][0]*Ji[q][i];\n\t\t\t\t\tA[i][j][1] -= Ji[j][p]*K[p][q][1]*Ji[q][i];\n\t\t\t\t\tA[i][j][2] -= Ji[j][p]*K[p][q][2]*Ji[q][i];\n\t\t\t\t}\n\t\t}\n\n\n\t// first derivative of shape functions\n\tfor (int a=0; a<neln; ++a)\n\t{\n\t\tdouble G1[3];\n\t\tG1[0] = Gr[a];\n\t\tG1[1] = Gs[a];\n\t\tG1[2] = Gt[a];\n\n\t\t// second derivatives of shape functions\n\t\tdouble G2[3][3];\n\t\tG2[0][0] = Grrn[a]; G2[0][1] = Grsn[a]; G2[0][2] = Grtn[a];\n\t\tG2[1][0] = Gsrn[a]; G2[1][1] = Gssn[a]; G2[1][2] = Gstn[a];\n\t\tG2[2][0] = Gtrn[a]; G2[2][1] = Gtsn[a]; G2[2][2] = Gttn[a];\n\n\t\t// calculate dB/dr\n\t\tdouble D[3][3] = {0};\n\t\tfor (int i=0; i<3; ++i)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t{\n\t\t\t\tfor (int j=0; j<3; ++j) D[i][k] += A[i][j][k]*G1[j] + Ji[j][i]*G2[j][k];\n\t\t\t}\n\n\t\t// calculate global gradient of shape functions\n\t\tmat3d& Ha = H[a];\n\t\tfor (int i=0; i<3; ++i)\n\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t{\n\t\t\t\tHa[i][j] = D[i][0]*Ji[0][j] + D[i][1]*Ji[1][j] + D[i][2]*Ji[2][j];\n\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the second derivative of shape function N[node] with respect\n//! to the material coordinates.\nvoid FEElasticSolidDomain2O::shape_gradient2(const FESolidElement& el, vec3d* X, double r, double s, double t, mat3d* H)\n{\n\tint neln = el.Nodes();\n\n\t// we need the Jacobian with respect to the reference configuration\n\tdouble Ji[3][3];\n\tinvjac0(el, Ji, r, s, t);\n\n\t// shape function derivatives\n\tconst int M = FEElement::MAX_NODES;\n\tdouble Gr[M], Gs[M], Gt[M];\n\tel.shape_deriv(Gr, Gs, Gt, r, s, t);\n\n\tdouble Grr[M], Gss[M], Gtt[M], Grs[M], Gst[M], Grt[M];\n\tel.shape_deriv2(Grr, Gss, Gtt, Grs, Gst, Grt, r, s, t);\n\n\t// calculate K = dJ/dr\n\tdouble K[3][3][3] = {0};\n\tfor (int a=0; a<neln; ++a)\n\t{\n\t\t// second derivatives of shape functions\n\t\tdouble G2[3][3];\n\t\tG2[0][0] = Grr[a]; G2[0][1] = Grs[a]; G2[0][2] = Grt[a];\n\t\tG2[1][0] = Grs[a]; G2[1][1] = Gss[a]; G2[1][2] = Gst[a];\n\t\tG2[2][0] = Grt[a]; G2[2][1] = Gst[a]; G2[2][2] = Gtt[a];\n\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t{\n\t\t\t\tK[0][j][k] += G2[j][k]*X[a].x;\n\t\t\t\tK[1][j][k] += G2[j][k]*X[a].y;\n\t\t\t\tK[2][j][k] += G2[j][k]*X[a].z;\n\t\t\t}\n\t}\n\n\t// calculate A = -J^-1*dJ/drJ^-1\n\tdouble A[3][3][3] = {0};\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t{\n\t\t\tfor (int p=0; p<3; ++p)\n\t\t\t\tfor (int q=0; q<3; ++q)\n\t\t\t\t{\n\t\t\t\t\tA[i][j][0] -= Ji[j][p]*K[p][q][0]*Ji[q][i];\n\t\t\t\t\tA[i][j][1] -= Ji[j][p]*K[p][q][1]*Ji[q][i];\n\t\t\t\t\tA[i][j][2] -= Ji[j][p]*K[p][q][2]*Ji[q][i];\n\t\t\t\t}\n\t\t}\n\n\t// first derivative of shape functions\n\tdouble G1[3];\n\tdouble G2[3][3];\n\tfor (int a=0; a<neln; ++a)\n\t{\n\t\tG1[0] = Gr[a];\n\t\tG1[1] = Gs[a];\n\t\tG1[2] = Gt[a];\n\n\t\t// second derivatives of shape functions\n\t\tG2[0][0] = Grr[a]; G2[0][1] = Grs[a]; G2[0][2] = Grt[a];\n\t\tG2[1][0] = Grs[a]; G2[1][1] = Gss[a]; G2[1][2] = Gst[a];\n\t\tG2[2][0] = Grt[a]; G2[2][1] = Gst[a]; G2[2][2] = Gtt[a];\n\n\t\t// calculate dB/dr\n\t\tdouble D[3][3] = {0};\n\t\tfor (int i=0; i<3; ++i)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t{\n\t\t\t\tfor (int j=0; j<3; ++j) D[i][k] += A[i][j][k]*G1[j] + Ji[j][i]*G2[j][k];\n\t\t\t}\n\n\t\t// calculate global gradient of shape functions\n\t\tmat3d& Ha = H[a];\n\t\tfor (int i=0; i<3; ++i)\n\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t{\n\t\t\t\tHa[i][j] = D[i][0]*Ji[0][j] + D[i][1]*Ji[1][j] + D[i][2]*Ji[2][j];\n\t\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEBioRVE/FEElasticSolidDomain2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include <FECore/tens3d.h>\n#include <FECore/tens4d.h>\n#include <FECore/tens5d.h>\n#include <FECore/tens6d.h>\n#include <FECore/FESurface.h>\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FEModel;\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n// This class implements a discontinuous-Galerkin formulation for gradient elasticity\nclass FEElasticSolidDomain2O : public FEElasticSolidDomain\n{\nprotected:\n\t// Helper class for evaluating the discrete-Galerkin contribution\n\t// It stores the data needed for evaluating the integrals over the\n\t// internal surface.\n\tclass FEInternalSurface2O \n\t{\n\tpublic:\n\t\tstruct Data\n\t\t{\n\t\t\tFEMaterialPoint*\tm_pt[2];\t//!< material point data for evaluating stresses\n\t\t\tvec3d\t\t\t\tksi[2];\t\t//!< local element coordinates\n\t\t\ttens3drs\t\t\tQavg;\t\t//!< average stress across interface\n\t\t\ttens5d\t\t\t\tH[2];\t\t//!< H stiffness\n\t\t\ttens6d\t\t\t\tJ[2];\t\t//!< J stiffness\n\t\t\ttens6d\t\t\t\tJ0[2];\t\t//!< initial J stiffness\n\t\t\ttens5d\t\t\t\tH0[2];\t\t//!< initial H stiffness\n\t\t\ttens6d \t\t\t\tJ0avg;\t\t//!< average initial higher order stiffess across interface\n\t\t\tmat3d\t\t\t\tDgradU;\t\t//!< displacement gradient jump across interface\n\t\t};\n\n\tpublic:\n\t\tFEInternalSurface2O ();\n\n\t\t// initialize the data structure\n\t\tbool Initialize(FEElasticSolidDomain2O* dom);\n\n\t\tint Elements() const { return m_ps->Elements(); }\n\n\t\tFESurfaceElement& Element(int i) { return m_ps->Element(i); }\n\n\t\tData& GetData(int i) { return m_data[i]; }\n\n\t\tFESurface* GetSurface() { return m_ps; }\n\n\t\tdouble GetElementSize() const { return m_h; }\n\n\tprivate:\n\t\tFESurface*\t\tm_ps;\n\t\tvector<Data>\tm_data;\n\t\tdouble\t\t\tm_h;\t//!< element size\n\t};\n\npublic:\n\t//! constructor\n\tFEElasticSolidDomain2O(FEModel* pfem);\n\n\t//! initialize class\n\tbool Init() override;\n\n\t//! initialize elements\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo) override;\n\n\t//! build the matrix profile\n\t//! (overridden from FEDomain)\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t//! overridden from FEElasticSolidDomain\n\tvoid Update(const FETimeInfo& tp) override;\n\npublic:\n\t//! internal stress forces\n\tvoid InternalForces(FEGlobalVector& R) override;\n\n\t//! evaluate internal element forces\n\tvoid ElementInternalForce(FESolidElement& el, vector<double>& fe);\n\n\t//! calculates the global stiffness matrix for this domain\n\t//! (overridden from FEElasticSolidDomain)\n\tvoid StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\t// discontinuous-Galerkin contribution to residual\n\tvoid InternalForcesDG1(FEGlobalVector& R);\n\tvoid InternalForcesDG2(FEGlobalVector& R);\n\tvoid InternalForcesDG3(FEGlobalVector& R);\n\n\tvoid ElementInternalForce_PF(FESolidElement& el, vector<double>& fe);\n\tvoid ElementInternalForce_QG(FESolidElement& el, vector<double>& fe);\n\n\t// --- S T I F F N E S S ---\n\t//! calculates the solid element stiffness matrix\n\tvoid ElementStiffness(const FETimeInfo& tp, int iel, matrix& ke) override;\n\n\t//! contributions from discontinuous Galerkin formulation\n\tvoid StiffnessMatrixDG(FELinearSystem& LS);\n\tvoid ElementStiffnessMatrixDG1(FESurfaceElement& el, FEInternalSurface2O::Data* pdata, matrix& ke);\n\tvoid ElementStiffnessMatrixDG2(FESurfaceElement& el, FEInternalSurface2O::Data* pdata, matrix& ke);\n\tvoid ElementStiffnessMatrixDG3(FESurfaceElement& el, FEInternalSurface2O::Data* pdata, matrix& ke);\n\nprivate:\n\tvoid UpdateElementStress(int iel, const FETimeInfo& tp) override;\n\tvoid UpdateInternalSurfaceStresses();\n\tvoid UpdateKinematics();\n\npublic:\n\t// calculate gradient of deformation gradient\n\tvoid defhess(FESolidElement &el, int n, tens3drs &G);\n\tvoid defhess(FESolidElement &el, double r, double s, double t, tens3drs &G);\n\n\t// Calculates second derivative of shape function N[node]\n\tvoid shape_gradient2(const FESolidElement& el, vec3d* X, int n, mat3d* H);\n\tvoid shape_gradient2(const FESolidElement& el, vec3d* X, double r, double s, double t, mat3d* H);\n\nprotected:\n\tFEInternalSurface2O\tm_surf;\n\tbool\tm_binitJ0;\t//!< flag indicating J0 has been initialized\n};\n"
  },
  {
    "path": "FEBioRVE/FEMicroMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMicroMaterial.h\"\n#include \"FECore/FEElemElemList.h\"\n#include \"FECore/log.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FEBioXML/FEBioImport.h\"\n#include <FECore/mat6d.h>\n#include \"FEBioMech/FEBCPrescribedDeformation.h\"\n#include \"FERVEProbe.h\"\n#include <sstream>\n\n//=============================================================================\nFEMicroMaterialPoint::FEMicroMaterialPoint()\n{\n\tm_macro_energy = 0.;\n\tm_micro_energy = 0.;\n\tm_energy_diff = 0.;\n\t\n\tm_macro_energy_inc = 0.;\n\tm_micro_energy_inc = 0.;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize material point data\nvoid FEMicroMaterialPoint::Init()\n{\n\tFEElasticMaterialPoint::Init();\n\tm_F_prev.unit();\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize material point data\nvoid FEMicroMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tFEElasticMaterialPoint::Update(timeInfo);\n\tm_F_prev = m_F;\n\n\t// clear rewind stack so the next rewind won't overwrite current state\n\tm_rve.RCI_ClearRewindStack();\n}\n\n//-----------------------------------------------------------------------------\n//! create a shallow copy\nFEMaterialPointData* FEMicroMaterialPoint::Copy()\n{\n\tFEMicroMaterialPoint* pt = new FEMicroMaterialPoint();\n\tif (m_pNext) pt->SetNext(m_pNext->Copy());\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\n//! serialize material point data\nvoid FEMicroMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_S & m_F_prev;\n\tar & m_macro_energy;\n\tar & m_micro_energy;\n\tar & m_energy_diff;\n\tar & m_macro_energy_inc;\n\tar & m_micro_energy_inc;\n}\n\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMicroMaterial, FEElasticMaterial)\n\tADD_PARAMETER(m_szrve    , \"RVE\"     );\n\tADD_PARAMETER(m_szbc     , \"bc_set\"  );\n\tADD_PARAMETER(m_bctype   , \"rve_type\" );\n\tADD_PARAMETER(m_scale\t , \"scale\"   ); \n\n\tADD_PROPERTY(m_probe, \"probe\", false);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMicroMaterial::FEMicroMaterial(FEModel* pfem) : FEElasticMaterial(pfem)\n{\n\t// initialize parameters\n\tm_szrve[0] = 0;\n\tm_szbc[0] = 0;\n\tm_bctype = FERVEModel::DISPLACEMENT;\t// use displacement BCs by default\n\tm_scale = 1.0;\n}\n\n//-----------------------------------------------------------------------------\nFEMicroMaterial::~FEMicroMaterial(void)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMicroMaterial::CreateMaterialPointData()\n{\n\treturn new FEMicroMaterialPoint;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMicroMaterial::Init()\n{\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\t// load the RVE model\n\tFEBioImport fim;\n\tif (fim.Load(m_mrve, m_szrve.c_str()) == false)\n\t{\n\t\tfeLogError(\"Failed to load RVE model.\");\n\t\treturn false;\n\t}\n\n\t// set the parent FEM\n\tm_mrve.SetParentModel(GetFEModel());\n\n\t// We don't want to output anything from the RVE\n\tm_mrve.BlockLog();\n\n\t// scale the RVE\n\tif (m_scale != 1.0) m_mrve.ScaleGeometry(m_scale);\n\n\t// initialize the RVE model\n\t// This also creates the necessary boundary conditions\n\tbool bret = m_mrve.InitRVE(m_bctype, m_szbc.c_str()); \n\n\tif (bret == false) {\n\t\tfeLogError(\"An error occurred preparing RVE model\"); return false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Note that this function is not used in the first-order implemenetation\nmat3ds FEMicroMaterial::Stress(FEMaterialPoint &mp)\n{\n\t// get the deformation gradient\n\tFEMicroMaterialPoint& pt = *mp.ExtractData<FEMicroMaterialPoint>();\n\tmat3d F = pt.m_F;\n\n\t// calculate the averaged Cauchy stress\n\tmat3ds sa = pt.m_rve.StressAverage(F, mp);\n\t\n\t// calculate the difference between the macro and micro energy for Hill-Mandel condition\n\tpt.m_micro_energy = micro_energy(pt.m_rve);\t\n\t\n\treturn sa;\n}\n\n//-----------------------------------------------------------------------------\n// The stiffness is evaluated at the same time the stress is evaluated so we \n// can just return it here. Note that this assumes that the stress function \n// is always called prior to the tangent function.\ntens4ds FEMicroMaterial::Tangent(FEMaterialPoint &mp)\n{\n\tFEMicroMaterialPoint& mmpt = *mp.ExtractData<FEMicroMaterialPoint>();\n\treturn mmpt.m_rve.StiffnessAverage(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the \"energy\" of the RVE model, i.e. the volume averaged of PK1:F\ndouble FEMicroMaterial::micro_energy(FEModel& rve)\n{\n\tdouble E_avg = 0.0;\n\tdouble V0 = 0.0;\n\tFEMesh& m = rve.GetMesh();\n\tfor (int k=0; k<m.Domains(); ++k)\n\t{\n\t\tFESolidDomain& dom = static_cast<FESolidDomain&>(m.Domain(k));\n\t\tfor (int i=0; i<dom.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\t\n\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\tmat3d& F = pt.m_F;\n\t\t\t\tdouble J = F.det();\n\t\t\t\tmat3ds& s = pt.m_s;\t// Cauchy stress\n\n\t\t\t\t// PK1 stress\n\t\t\t\tmat3d Pk1 = J*s*F.transinv();\n\n\t\t\t\tdouble energy = Pk1.dotdot(F);\n\n\t\t\t\tdouble J0 = dom.detJ0(el, n);\t\t\n\t\t\t\tE_avg += energy*w[n]*J0;\n\n\t\t\t\tV0 += w[n]*J0;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn E_avg/V0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nmat3d FEMicroMaterial::AveragedStressPK1(FEModel& rve, FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get the RVE mesh\n\tFEMesh& m = rve.GetMesh();\n\n\tmat3d PK1; PK1.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\tif (m_bctype == FERVEModel::PERIODIC_AL)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary1O* pbc = dynamic_cast<FEPeriodicBoundary1O*>(rve.SurfacePairConstraint(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_data[i].m_Fr;\n\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tPK1 += (f & node.m_r0)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero). \n\tconst int dof_X = rve.GetDOFIndex(\"x\");\n\tconst int dof_Y = rve.GetDOFIndex(\"y\");\n\tconst int dof_Z = rve.GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = rve.GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\tFEBCPrescribedDeformation& dc = dynamic_cast<FEBCPrescribedDeformation&>(*rve.BoundaryCondition(0));\n\n\tconst FENodeSet& nset = *dc.GetNodeSet();\n\tint nitems = nset.Size();\n\tfor (int i=0; i<nitems; ++i)\n\t{\n\t\tconst FENode& n = *nset.Node(i);\n\t\tvec3d f;\n\t\tf.x = R[-n.m_ID[dof_X]-2];\n\t\tf.y = R[-n.m_ID[dof_Y]-2];\n\t\tf.z = R[-n.m_ID[dof_Z]-2];\n\t\tPK1 += f & n.m_r0;\n\t}\n\n\tdouble V0 = m_mrve.InitialVolume();\n\treturn PK1 / V0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nmat3ds FEMicroMaterial::AveragedStressPK2(FEModel& rve, FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tmat3d Finv = F.inverse();\n\n\t// get the RVE mesh\n\tFEMesh& m = rve.GetMesh();\n\n\tmat3d S; S.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\tif (m_bctype == FERVEModel::PERIODIC_AL)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary1O* pbc = dynamic_cast<FEPeriodicBoundary1O*>(rve.SurfacePairConstraint(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_data[i].m_Fr;\n\t\t\t\tvec3d f0 = Finv*f;\n\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tS += (f0 & node.m_r0)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero). \n\tconst int dof_X = rve.GetDOFIndex(\"x\");\n\tconst int dof_Y = rve.GetDOFIndex(\"y\");\n\tconst int dof_Z = rve.GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = rve.GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\tFEBCPrescribedDeformation& dc = dynamic_cast<FEBCPrescribedDeformation&>(*rve.BoundaryCondition(0));\n\tconst FENodeSet& nset = *dc.GetNodeSet();\n\tint nitems = nset.Size();\n\tfor (int i=0; i<nitems; ++i)\n\t{\n\t\tconst FENode& n = *nset.Node(i);\n\t\tvec3d f;\n\t\tf.x = R[-n.m_ID[dof_X]-2];\n\t\tf.y = R[-n.m_ID[dof_Y]-2];\n\t\tf.z = R[-n.m_ID[dof_Z]-2];\n\t\tvec3d f0 = Finv*f;\n\t\tS += f0 & n.m_r0;\n\t}\n\n\tdouble V0 = m_mrve.InitialVolume();\n\treturn S.sym() / V0;\n}\n"
  },
  {
    "path": "FEBioRVE/FEMicroMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEMaterial.h\"\n#include \"FEPeriodicBoundary1O.h\"\n#include \"FECore/FECallBack.h\"\n#include \"FERVEModel.h\"\n#include \"febiorve_api.h\"\n\nclass FERVEProbe;\n\n//-----------------------------------------------------------------------------\n//! Material point class for the micro-material\nclass FEBIORVE_API FEMicroMaterialPoint : public FEElasticMaterialPoint\n{\npublic:\n\t//! constructor\n\tFEMicroMaterialPoint();\n\n\t//! Initialize material point data\n\tvoid Init();\n\n\t//! Update material point data\n\tvoid Update(const FETimeInfo& timeInfo);\n\n\t//! create a shallow copy\n\tFEMaterialPointData* Copy();\n\n\t//! serialize material point data\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tmat3ds\t\tm_S;\t\t\t\t// 2nd Piola-Kirchhoff stress\n\tmat3d\t\tm_F_prev;\t\t\t// deformation gradient from last time step\n\n\tdouble     m_macro_energy;\t// Macroscopic strain energy\n\tdouble\t   m_micro_energy;\t// Volume-average of strain energy throughout the RVE solution\n\tdouble\t   m_energy_diff;\t// Difference between macro energy and volume averaged energy of RVE (should be zero) \n\n\tdouble\t   m_macro_energy_inc;\t// Macroscopic strain energy increment\n\tdouble\t   m_micro_energy_inc;\t// Microscopic strain energy increment\n\n\tFERVEModel\tm_rve;\t\t\t\t// Local copy of the parent rve\n};\n\n//-----------------------------------------------------------------------------\n//! The micro-material implements material homogenization. The stress and tangents\n//! are calculated by solving a micro-structural RVE problem and return the\n//! averaged stress and condensed tangents.\n//!\nclass FEMicroMaterial :\tpublic FEElasticMaterial\n{\npublic:\n\tFEMicroMaterial(FEModel* pfem);\n\t~FEMicroMaterial(void);\n\npublic:\n\tstd::string\tm_szrve;\t//!< filename for RVE file\n\tstd::string\tm_szbc;\t\t//!< name of nodeset defining boundary\n\tint\t\t\tm_bctype;\t\t//!< periodic bc flag\n\tdouble\t\tm_scale;\t\t//!< RVE scale factor\n\tFERVEModel\tm_mrve;\t\t\t//!< the parent RVE (Representive Volume Element)\n\npublic:\n\t//! calculate stress at material point\n\tvirtual mat3ds Stress(FEMaterialPoint& pt) override;\n\n\t//! calculate tangent stiffness at material point\n\tvirtual tens4ds Tangent(FEMaterialPoint& pt) override;\n\n\t//! data initialization\n\tbool Init() override;\n\n\t//! create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\n\t// calculate the average PK1 stress\n\tmat3d AveragedStressPK1(FEModel& rve, FEMaterialPoint &mp);\n\n\t// calculate the average PK2 stress\n\tmat3ds AveragedStressPK2(FEModel& rve, FEMaterialPoint &mp);\n\n\t// average RVE energy\n\tdouble micro_energy(FEModel& rve);\n\npublic:\n\tint Probes() { return (int) m_probe.size(); }\n\tFERVEProbe& Probe(int i) { return *m_probe[i]; }\n\nprotected:\n\tstd::vector<FERVEProbe*>\tm_probe;\n\npublic:\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEMicroMaterial2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMicroMaterial2O.h\"\n#include \"FECore/log.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include \"FECore/FEAnalysis.h\"\n//#include \"FEBioXML/FEBioImport.h\"\n//#include \"FEBioPlot/FEBioPlotFile.h\"\n#include \"FECore/tens3d.h\"\n#include \"FEPeriodicBoundary2O.h\"\n#include \"FERVEProbe.h\"\n\n//-----------------------------------------------------------------------------\nFEMicroMaterialPoint2O::FEMicroMaterialPoint2O(FEMaterialPointData* mp) : FEMaterialPointData(mp)\n{\n\tm_elem_id = -1;\n\tm_gpt_id = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! create a shallow copy\nFEMaterialPointData* FEMicroMaterialPoint2O::Copy()\n{\n\tFEMicroMaterialPoint2O* pt = new FEMicroMaterialPoint2O(m_pNext?m_pNext->Copy():0);\n\treturn pt;\n}\n\n//-----------------------------------------------------------------------------\n//! serialize material point data\nvoid FEMicroMaterialPoint2O::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n}\n\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// define the material parameters\nBEGIN_FECORE_CLASS(FEMicroMaterial2O, FEElasticMaterial2O)\n\tADD_PARAMETER(m_szrve    , \"RVE\"     );\n\tADD_PARAMETER(m_szbc     , \"bc_set\"  );\n\tADD_PARAMETER(m_rveType  , \"rve_type\" );\n\tADD_PARAMETER(m_scale    , \"scale\");\n\n\tADD_PROPERTY(m_probe, \"probe\", false);\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMicroMaterial2O::FEMicroMaterial2O(FEModel* pfem) : FEElasticMaterial2O(pfem)\n{\n\t// initialize parameters\n\tm_szrve[0] = 0;\n\tm_szbc[0] = 0;\n\tm_rveType = FERVEModel2O::DISPLACEMENT;\n\tm_scale = 1.0;\n}\n\n//-----------------------------------------------------------------------------\nFEMicroMaterial2O::~FEMicroMaterial2O(void)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialPointData* FEMicroMaterial2O::CreateMaterialPointData()\n{\n\treturn new FEMicroMaterialPoint2O(new FEElasticMaterialPoint2O(new FEElasticMaterialPoint()));\n}\n\n//-----------------------------------------------------------------------------\nbool FEMicroMaterial2O::Init()\n{\n\t// initialize base class first\n\tif (FEElasticMaterial::Init() == false) return false;\n\n\t// load the parent RVE model\n/*\tFEBioImport fim;\n\tif (fim.Load(m_mrve, m_szrve.c_str()) == false)\n\t{\n\t\treturn false;\n\t}\n*/\n\t// we don't want to output anything from the RVE\n\tm_mrve.BlockLog();\n\n\t// scale geometry\n\tm_mrve.ScaleGeometry(m_scale);\n\n\t// initialize parent RVE\n\tif (m_mrve.InitRVE(m_rveType, m_szbc.c_str()) == false) {\n\t\tfeLogError(\"An error occurred preparing RVE model\"); return false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMicroMaterial2O::Stress(FEMaterialPoint &mp, mat3d& P, tens3drs& Q)\n{\n\t// get the deformation gradient and its gradient\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEElasticMaterialPoint2O& pt2 = *mp.ExtractData<FEElasticMaterialPoint2O>();\n\tFEMicroMaterialPoint2O& mmpt2O = *mp.ExtractData<FEMicroMaterialPoint2O>();\n\n\t// get the deformation gradient and its gradient\n\tconst mat3d& F = pt.m_F;\n\tconst tens3drs& G = pt2.m_G;\n\n\t// solve the RVE\n\tbool bret = mmpt2O.m_rve.Solve(F, G);\n\n\t// make sure it converged\n\tif (bret == false) throw FEMultiScaleException(mmpt2O.m_elem_id, mmpt2O.m_gpt_id);\n\n\t// calculate the averaged Cauchy stress\n\tmmpt2O.m_rve.AveragedStress2O(P, Q);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMicroMaterial2O::Tangent(FEMaterialPoint& mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J)\n{\n\tFEMicroMaterialPoint2O& mmpt2O = *mp.ExtractData<FEMicroMaterialPoint2O>();\n\t\n\t// calculate the averaged stiffness here\n\tmmpt2O.m_rve.AveragedStiffness(mp, C, L, H, J);\n}\n"
  },
  {
    "path": "FEBioRVE/FEMicroMaterial2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial2O.h\"\n#include <FECore/tens3d.h>\n#include <FECore/tens4d.h>\n#include <FECore/tens5d.h>\n#include <FECore/tens6d.h>\n#include \"FE2OMicroConstraint.h\"\n#include \"FEMicroMaterial.h\"\n#include \"FERVEModel2O.h\"\n\n//-----------------------------------------------------------------------------\n//! Material point class for the micro-material\nclass FEMicroMaterialPoint2O : public FEMaterialPointData\n{\npublic:\n\t//! constructor\n\tFEMicroMaterialPoint2O(FEMaterialPointData* mp);\n\n\t//! create a shallow copy\n\tFEMaterialPointData* Copy();\n\n\t//! serialize material point data\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tFEMicroModel2O m_rve;\t\t\t\t//!< local copy of the rve\t\t\n\tint\t\tm_elem_id;\t\t//!< element ID\n\tint\t\tm_gpt_id;\t\t//!< Gauss point index (0-based)\n};\n\n//-----------------------------------------------------------------------------\n//! The micro-material implements material homogenization. The stress and tangents\n//! are calculated by solving a micro-structural RVE problem and return the\n//! averaged stress and condensed tangents.\n//!\nclass FEMicroMaterial2O : public FEElasticMaterial2O\n{\npublic:\n\tFEMicroMaterial2O(FEModel* pfem);\n\t~FEMicroMaterial2O(void);\n\npublic:\n\tstd::string\t\tm_szrve;\t\t//!< filename for RVE file\n\tstd::string\t\tm_szbc;\t\t\t//!< name of nodeset defining boundary\n\tint\t\t\t\tm_rveType;\t\t//!< RVE type\n\tdouble\t\t\tm_scale;\t\t//!< geometry scale factor\n\tFERVEModel2O\tm_mrve;\t\t\t//!< the parent RVE (Representive Volume Element)\n\npublic:\n\t//! calculate stress at material point\n\tvoid Stress(FEMaterialPoint& mp, mat3d& P, tens3drs& Q) override;\n\n\t//! calculate tangent stiffness at material point\n\tvoid Tangent(FEMaterialPoint &mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J) override;\n\t\n\t//! data initialization\n\tbool Init() override;\n\n\t//! create material point data\n\tFEMaterialPointData* CreateMaterialPointData() override;\n\npublic:\n\tint Probes() { return (int) m_probe.size(); }\n\tFERVEProbe& Probe(int i) { return *m_probe[i]; }\n\nprotected:\n\tstd::vector<FERVEProbe*>\tm_probe;\n\npublic:\n\t// declare the parameter list\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEMindlinElastic2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMindlinElastic2O.h\"\n\nBEGIN_FECORE_CLASS(FEMindlinElastic2O, FEElasticMaterial2O)\n\tADD_PARAMETER(m_lam, \"lam\");\n\tADD_PARAMETER(m_mu , \"mu\" );\n\tADD_PARAMETER(m_a1 , \"a1\" );\n\tADD_PARAMETER(m_a2 , \"a2\" );\n\tADD_PARAMETER(m_a3 , \"a3\" );\n\tADD_PARAMETER(m_a4 , \"a4\" );\n\tADD_PARAMETER(m_a5 , \"a5\" );\nEND_FECORE_CLASS();\n\nFEMindlinElastic2O::FEMindlinElastic2O(FEModel* pfem) : FEElasticMaterial2O(pfem)\n{\n\tm_mu = 0.0;\n\tm_lam = 0.0;\n\tm_a1 = 0.0;\n\tm_a2 = 0.0;\n\tm_a3 = 0.0;\n\tm_a4 = 0.0;\n\tm_a5 = 0.0;\n}\n\n//! Calculate PK1 stress and higher order stress Q\nvoid FEMindlinElastic2O::Stress(FEMaterialPoint& mp, mat3d& P, tens3drs& Q)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEElasticMaterialPoint2O& pt2O = *mp.ExtractData<FEElasticMaterialPoint2O>();\n\n\t// deformation gradient and transpose\n\tconst mat3d& F = pt.m_F;\n\tmat3d Ft = F.transpose();\n\n\t// gradient of deformation gradient\n\tconst tens3drs& G = pt2O.m_G;\n\n\t// identity tensor\n\tmat3dd I(1.0);\n\n\t// Lagrange strain\n\tmat3d E = (Ft*F - I)*0.5;\n\tdouble trE = E.trace();\n\n\t// PK1 stress\n\tP = F*(m_lam*trE) + (F*E)*(2.0*m_mu);\n\n\t// higher order stress\n\tfor (int p=0; p<3; ++p)\n\t\tfor (int q=0; q<3; ++q)\n\t\t\tfor (int r=0; r<3; ++r)\n\t\t\t{\n\t\t\t\tdouble a = 0.0;\n\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t{\n\t\t\t\t\ta += m_a1*(I(p,r)*G(i,q,i) + I(p,q)*G(i,i,r));\n\t\t\t\t\ta += 0.5*m_a2*(2.0*I(q,r)*G(i,p,i) + G(q,i,i)*I(p,r) + G(r,i,i)*I(p,q));\n\t\t\t\t\ta += 2.0*m_a3*G(p,i,i)*I(q,r);\n\t\t\t\t}\n\t\t\t\ta += 2.0*m_a4*G(p,q,r);\n\t\t\t\ta += m_a5*(G(q,p,r) + G(r,p,q));\n\n\t\t\t\tQ(p,q,r) = a;\n\t\t\t}\n}\n\n//! Calculate material tangents\n//! C = dP/dF\n//! L = dP/dG\n//! H = dQ/dF\n//! J = dQ/dG\nvoid FEMindlinElastic2O::Tangent(FEMaterialPoint& mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tFEElasticMaterialPoint2O& pt2O = *mp.ExtractData<FEElasticMaterialPoint2O>();\n\n\t// deformation gradient and transpose\n\tconst mat3d& F = pt.m_F;\n\tmat3d Ft = F.transpose();\n\n\t// identity tensor\n\tmat3dd I(1.0);\n\n\t// Lagrange strain\n\tmat3d E = (Ft*F - I)*0.5;\n\tdouble trE = E.trace();\n\n\tmat3d B = F*Ft;\n\n\t// C-tensor\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t{\n\t\t\t\t\tdouble c = 0.0;\n\t\t\t\t\tc += I(i,k)*(m_lam*trE*I(j,l) + 2.0*m_mu*E(l,j));\n\t\t\t\t\tc += m_lam*F(i,j)*F(k,l);\n\t\t\t\t\tc += m_mu*(F(i,l)*F(k,j) + B(k,i)*I(j,l));\n\n\t\t\t\t\tC(i,j,k,l) = c;\n\t\t\t\t}\n\n\t// L and H are zero\n\tL.zero();\n\tH.zero();\n\n\t// J-tensor\n\tfor (int i=0; i<3; ++i)\n\t\tfor (int j=0; j<3; ++j)\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble a = 0.0;\n\t\t\t\t\t\t\ta += m_a1*(I(i,k)*I(l,n)*I(j,m) + I(i,j)*I(l,m)*I(k,n));\n\t\t\t\t\t\t\ta += 0.5*m_a2*(2.0*I(j,k)*I(l,n)*I(i,m) + I(j,l)*I(m,n)*I(i,k) + I(i,j)*I(k,l)*I(m,n));\n\t\t\t\t\t\t\ta += 2.0*m_a3*I(i,l)*I(m,n)*I(j,k);\n\t\t\t\t\t\t\ta += 2.0*m_a4*I(i,l)*I(j,m)*I(k,n);\n\t\t\t\t\t\t\ta += m_a5*(I(j,l)*I(i,m)*I(k,n) + I(k,l)*I(i,m)*I(j,n));\n\n\t\t\t\t\t\t\tJ(i,j,k,l,m,n) = a;\n\t\t\t\t\t\t}\n\n}\n"
  },
  {
    "path": "FEBioRVE/FEMindlinElastic2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElasticMaterial2O.h\"\n\nclass FEMindlinElastic2O : public FEElasticMaterial2O\n{\npublic:\n\tFEMindlinElastic2O(FEModel* pfem);\n\n\t//! Calculate PK1 stress and higher order stress Q\n\tvoid Stress(FEMaterialPoint& mp, mat3d& P, tens3drs& Q) override;\n\n\t//! Calculate material tangents\n\t//! C = dP/dF\n\t//! L = dP/dG\n\t//! H = dQ/dF\n\t//! J = dQ/dG\n\tvoid Tangent(FEMaterialPoint& mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J) override;\n\npublic:\n\tdouble\tm_lam;\n\tdouble\tm_mu;\n\tdouble\tm_a1;\n\tdouble\tm_a2;\n\tdouble\tm_a3;\n\tdouble\tm_a4;\n\tdouble\tm_a5;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEMultiscaleDomainFactory.cpp",
    "content": "#include \"stdafx.h\"\n#include \"FEMultiscaleDomainFactory.h\"\n#include \"FEMicroMaterial.h\"\n#include \"FEMicroMaterial2O.h\"\n#include \"FEElasticMultiscaleDomain1O.h\"\n#include \"FEElasticMultiscaleDomain2O.h\"\n\n//==========================================================================================\nFEDomain* FEMultiScaleDomainFactory::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tFEModel* pfem = pmat->GetFEModel();\n\tconst char* sztype = 0;\n\tif      (dynamic_cast<FEMicroMaterial*    >(pmat)) sztype = \"elastic-mm-solid\";\n\telse if (dynamic_cast<FEMicroMaterial2O*  >(pmat)) sztype = \"elastic-mm-solid2O\";\n\telse if (dynamic_cast<FEElasticMaterial2O*>(pmat)) sztype = \"elastic-solid2O\";\n\n\tif (sztype)\n\t{\n\t\tFESolidDomain* pd = fecore_new<FESolidDomain>(sztype, pfem);\n\t\tif (pd) pd->SetMaterial(pmat);\n\t\treturn pd;\n\t}\n\telse return 0;\n}\n"
  },
  {
    "path": "FEBioRVE/FEMultiscaleDomainFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECore/FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMultiScaleDomainFactory : public FEDomainFactory\n{\npublic:\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n};\n"
  },
  {
    "path": "FEBioRVE/FEPeriodicBoundary1O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPeriodicBoundary1O.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FENormalProjection.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEPeriodicBoundary1O, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"   )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\");\n\tADD_PARAMETER(m_eps      , \"penalty\"  );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\" );\n\tADD_PARAMETER(m_off      , \"offset\"   );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEPeriodicBoundary\n//-----------------------------------------------------------------------------\n\nFEPeriodicBoundary1O::FEPeriodicBoundary1O(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_stol = 0.01;\n\tm_srad = 1.0;\n\tm_atol = 0;\n\tm_eps = 0;\n\tm_btwo_pass = false;\n\tm_off = vec3d(0,0,0);\n\tm_naugmin = 0;\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n\n\tm_Fmacro.zero();\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicBoundary1O::Init()\n{\n\t// create the surfaces\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary1O::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms, false);\n\tProjectSurface(m_ms, m_ss, false);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary1O::CopyFrom(FESurfacePairConstraint* pci)\n{\n\t// cast to a periodic boundary\n\tFEPeriodicBoundary1O& pb = dynamic_cast<FEPeriodicBoundary1O&>(*pci);\n\n\t// copy parameters\n\tGetParameterList() = pb.GetParameterList();\n\n\t// copy nodes\n\tm_ss.CopyFrom(pb.m_ss);\n\tm_ms.CopyFrom(pb.m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\n// TODO: what if two_pass ??\nvoid FEPeriodicBoundary1O::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6*5);\n\n\tfor (int j=0; j<m_ss.Nodes(); ++j)\n\t{\n\t\tFESurfaceElement& me = *m_ss.m_data[j].m_pme;\n\t\tint* en = &me.m_node[0];\n\n\t\tint n = me.Nodes();\n\t\tif (n == 3)\n\t\t{\n\t\t\tlm[6*(3+1)  ] = -1;\n\t\t\tlm[6*(3+1)+1] = -1;\n\t\t\tlm[6*(3+1)+2] = -1;\n\t\t\tlm[6*(3+1)+3] = -1;\n\t\t\tlm[6*(3+1)+4] = -1;\n\t\t\tlm[6*(3+1)+5] = -1;\n\t\t}\n\n\t\tlm[0] = m_ss.Node(j).m_ID[dof_X];\n\t\tlm[1] = m_ss.Node(j).m_ID[dof_Y];\n\t\tlm[2] = m_ss.Node(j).m_ID[dof_Z];\n\t\tlm[3] = m_ss.Node(j).m_ID[dof_RU];\n\t\tlm[4] = m_ss.Node(j).m_ID[dof_RV];\n\t\tlm[5] = m_ss.Node(j).m_ID[dof_RW];\n\n\t\tfor (int k=0; k<n; ++k)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\tlm[6*(k+1)  ] = id[dof_X];\n\t\t\tlm[6*(k+1)+1] = id[dof_Y];\n\t\t\tlm[6*(k+1)+2] = id[dof_Z];\n\t\t\tlm[6*(k+1)+3] = id[dof_RU];\n\t\t\tlm[6*(k+1)+4] = id[dof_RV];\n\t\t\tlm[6*(k+1)+5] = id[dof_RW];\n\t\t}\n\n\t\tK.build_add(lm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project surface\nvoid FEPeriodicBoundary1O::ProjectSurface(FEPeriodicSurface& ss, FEPeriodicSurface& ms, bool bmove)\n{\n\tint i;\n\tdouble rs[2];\n\n\t// get the primary's center of mass\n\tvec3d cs = ss.CenterOfMass();\n\n\t// get the secondary's center of mass\n\tvec3d cm = ms.CenterOfMass();\n\n\t// get the relative distance\n\tvec3d cr = cs - cm;\n\n\t// unit vector in direction of cr\n\t// this will serve as the projection distance\n\tvec3d cn(cr); cn.unit();\n\n\t// initialize projection data\n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(m_srad);\n\tnp.Init();\n\n\t// loop over all primary nodes\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFENode& node = ss.Node(i);\n\n\t\t// get the nodal position\n\t\tvec3d r0 = node.m_r0;\n\n\t\t// find the intersection with the secondary surface\n\t\tss.m_data[i].m_pme = np.Project3(r0, cn, rs);\n\t\tassert(ss.m_data[i].m_pme);\n\n\t\tss.m_data[i].m_rs[0] = rs[0];\n\t\tss.m_data[i].m_rs[1] = rs[1];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary1O::Update()\n{\n\tint i, j, ne;\n\tFESurfaceElement* pme;\n\n\tFEMesh& mesh = *m_ss.GetMesh();\n\n\t//vec3d us, um;\n\tvec3d ws, wm;\n\t//vec3d umi[FEElement::MAX_NODES];\n\tvec3d wmi[FEElement::MAX_NODES];\n\n\t// update gap functions\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// off-set sign\n\t\tdouble s = (np==0?1.0:-1.0);\n\n\t\tint N = ss.Nodes();\n\n\t\tfor (i=0; i<N; ++i)\n\t\t{\n\t\t\t// calculate the primary displacement\n\t\t\tFENode& node = ss.Node(i);\n\t\t\tws = node.m_rt - m_Fmacro*node.m_r0;\n\n\t\t\t// get the secondary element\n\t\t\tpme = ss.m_data[i].m_pme;\n\n\t\t\t// calculate the secondary displacement\n\t\t\tne = pme->Nodes();\n\t\t\tfor (j=0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = ms.Node(pme->m_lnode[j]);\n\t\t\t\twmi[j] = node.m_rt - m_Fmacro*node.m_r0;\n\t\t\t}\n\t\t\t\n\t\t\twm = pme->eval(wmi, ss.m_data[i].m_rs[0], ss.m_data[i].m_rs[1]);\n\n\t\t\t// calculate gap function\n\t\t\tss.m_data[i].m_gap = ws - wm;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary1O::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint j, k, l, m, n;\n\tint nseln, nmeln;\n\n\tdouble *Gr, *Gs;\n\n\t// jacobian\n\tdouble detJ;\n\n\tvec3d dxr, dxs;\n\tdouble* w;\n\n\t// natural coordinates of primary node in secondary element\n\tdouble r, s;\n\n\t// contact force\n\tvec3d tc;\n\n\t// shape function values\n\tdouble N[FEElement::MAX_NODES];\n\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tvec3d r0[FEElement::MAX_NODES], rt[FEElement::MAX_NODES];\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\tfor (int i=0; i<ss.m_data.size(); ++i) ss.m_data[i].m_Fr = vec3d(0,0,0);\n\n\t\t// loop over all primary facets\n\t\tint ne = ss.Elements();\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\t// get the primary element\n\t\t\tFESurfaceElement& sel = ss.Element(j);\n\t\t\t\n\t\t\t// get the elements LM vector\n\t\t\tss.UnpackLM(sel, sLM);\n\n\t\t\tnseln = sel.Nodes();\n\n\t\t\tfor (int i=0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.GetMesh()->Node(sel.m_node[i]).m_r0;\n\t\t\t\trt[i] = ss.GetMesh()->Node(sel.m_node[i]).m_rt;\n\t\t\t}\n\t\t\tw = sel.GaussWeights();\n\n\t\t\t// loop over primary element nodes (which are the integration points as well)\n\t\t\tfor (n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = sel.Gr(n);\n\t\t\t\tGs = sel.Gs(n);\n\n\t\t\t\tm = sel.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get primary node contact force\n\t\t\t\ttc = ss.m_data[m].m_Lm + ss.m_data[m].m_gap*m_eps;\n\t\t\t\tss.m_data[m].m_Tn = tc;\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& mel = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(mel, mLM);\n\n\t\t\t\tnmeln = mel.Nodes();\n\n\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t// onto the secondary element\n\t\t\t\tr = ss.m_data[m].m_rs[0];\n\t\t\t\ts = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tN[0] = 0.25*(1-r)*(1-s);\n\t\t\t\t\tN[1] = 0.25*(1+r)*(1-s);\n\t\t\t\t\tN[2] = 0.25*(1+r)*(1+s);\n\t\t\t\t\tN[3] = 0.25*(1-r)*(1+s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tN[0] = 1 - r - s;\n\t\t\t\t\tN[1] = r;\n\t\t\t\t\tN[2] = s;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// calculate force vector\n\t\t\t\tfe.resize(3*(nmeln+1));\n\t\t\t\tfe[0] = -detJ*w[n]*tc.x;\n\t\t\t\tfe[1] = -detJ*w[n]*tc.y;\n\t\t\t\tfe[2] = -detJ*w[n]*tc.z;\n\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tfe[3*(l+1)  ] = detJ*w[n]*tc.x*N[l];\n\t\t\t\t\tfe[3*(l+1)+1] = detJ*w[n]*tc.y*N[l];\n\t\t\t\t\tfe[3*(l+1)+2] = detJ*w[n]*tc.z*N[l];\n\t\t\t\t}\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3*(nmeln+1));\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(l+1)  ] = mLM[l*3  ];\n\t\t\t\t\tlm[3*(l+1)+1] = mLM[l*3+1];\n\t\t\t\t\tlm[3*(l+1)+2] = mLM[l*3+2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (l=0; l<nmeln; ++l) en[l+1] = mel.m_node[l];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\n\t\t\t\t// also store in the reaction force vector\n\t\t\t\tvec3d& fr = ss.m_data[m].m_Fr;\n\t\t\t\tfr.x += fe[0];\n\t\t\t\tfr.y += fe[1];\n\t\t\t\tfr.z += fe[2];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary1O::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint j, k, l, n, m;\n\tint nseln, nmeln, ndof;\n\n\tFEElementMatrix ke;\n\n\tconst int MN = FEElement::MAX_NODES;\n\tvector<int> lm(3*(MN+1));\n\tvector<int> en(MN+1);\n\n\tdouble *Gr, *Gs, *w;\n\tvec3d rt[MN], r0[MN];\n\n\tvec3d rtm[MN];\n\n\tdouble detJ, r, s;\n\tvec3d dxr, dxs;\n\tdouble H[MN];\n\n\tvec3d gap, Lm, tc;\n\n\t// curvature tensor K\n\tdouble K[2][2] = {0};\n\n//\tdouble scale = -0.0035*m_fem.GetMesh().GetBoundingBox().radius();\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary elements\n\t\tint ne = ss.Elements();\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\n\t\t\t// get the element's LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\tnseln = se.Nodes();\n\n\t\t\tfor (int i=0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.GetMesh()->Node(se.m_node[i]).m_r0;\n\t\t\t\trt[i] = ss.GetMesh()->Node(se.m_node[i]).m_rt;\n\t\t\t}\n\n\t\t\tw = se.GaussWeights();\n\n\t\t\t// loop over all integration points (that is nodes)\n\t\t\tfor (n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = se.Gr(n);\n\t\t\t\tGs = se.Gs(n);\n\n\t\t\t\tm = se.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\tnmeln = me.Nodes();\n\n\t\t\t\t// get the secondary element node positions\n\t\t\t\tfor (k=0; k<nmeln; ++k) rtm[k] = ms.GetMesh()->Node(me.m_node[k]).m_rt;\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tr = ss.m_data[m].m_rs[0];\n\t\t\t\ts = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get primary node normal force\n\t\t\t\ttc = ss.m_data[m].m_Lm + ss.m_data[m].m_gap*m_eps; //ss.T[m];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tH[0] = 0.25*(1-r)*(1-s);\n\t\t\t\t\tH[1] = 0.25*(1+r)*(1-s);\n\t\t\t\t\tH[2] = 0.25*(1+r)*(1+s);\n\t\t\t\t\tH[3] = 0.25*(1-r)*(1+s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tH[0] = 1 - r - s;\n\t\t\t\t\tH[1] = r;\n\t\t\t\t\tH[2] = s;\n\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// number of degrees of freedom\n\t\t\t\tndof = 3*(1 + nmeln);\n\n\t\t\t\t// fill stiffness matrix\n\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\tke[0][0] = w[n]*detJ*m_eps;\n\t\t\t\tke[1][1] = w[n]*detJ*m_eps;\n\t\t\t\tke[2][2] = w[n]*detJ*m_eps;\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tke[0][3+3*k  ] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[1][3+3*k+1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[2][3+3*k+2] = -w[n]*detJ*m_eps*H[k];\n\n\t\t\t\t\tke[3+3*k  ][0] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+1][1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+2][2] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t}\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3+3*k  ][3+3*l  ] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+1][3+3*l+1] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+2][3+3*l+2] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t}\n\n\t\t\t\t// create lm array\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicBoundary1O::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\n\tdouble g;\n\tvec3d lm;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_data[i].m_Lm + m_ss.m_data[i].m_gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ss.m_data[i].m_gap.norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_data[i].m_Lm + m_ms.m_data[i].m_gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ms.m_data[i].m_gap.norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\tfeLog(\" tied interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\n\t// check convergence of constraints\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\n\t// update Lagrange multipliers if we did not converge\n\tif (bconv == false)\n\t{\n\t\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ss.m_data[i].m_Lm = m_ss.m_data[i].m_Lm + m_ss.m_data[i].m_gap*m_eps;\n\t\t}\n\t\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ms.m_data[i].m_Lm = m_ms.m_data[i].m_Lm + m_ms.m_data[i].m_gap*m_eps;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary1O::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioRVE/FEPeriodicBoundary1O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBioMech/FEPeriodicBoundary.h\"\n#include \"FEBioMech/FEContactSurface.h\"\n#include <FECore/tens3d.h>\n\n//-----------------------------------------------------------------------------\n\nclass FEPeriodicBoundary1O : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEPeriodicBoundary1O(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! create a copy of this interface\n\tvoid CopyFrom(FESurfacePairConstraint* pci) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FEPeriodicSurface& ss, FEPeriodicSurface& ms, bool bmove);\n\npublic:\n\tFEPeriodicSurface\t\tm_ss;\t//!< primary surface\n\tFEPeriodicSurface\t\tm_ms;\t//!< secondary surface\n\n\tdouble\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\tm_eps;\t\t\t//!< penalty scale factor\n\tdouble\tm_stol;\t\t\t//!< search tolerance\n\tdouble  m_srad;\t\t\t//!< search radius (%)\n\tbool\tm_btwo_pass;\t//!< two-pass flag\n\tint\t\tm_naugmin;\t\t//!< minimum number of augmentations\n\tvec3d\tm_off;\t\t\t//!< relative displacement offset\n\n\tmat3d\t\tm_Fmacro;\t\t//!< Macroscopic deformation gradient\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEPeriodicBoundary2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPeriodicBoundary2O.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FENormalProjection.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include <FECore/FELinearSystem.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\n// Define sliding interface parameters\nBEGIN_FECORE_CLASS(FEPeriodicBoundary2O, FEContactInterface)\n\tADD_PARAMETER(m_laugon   , \"laugon\"   )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0\");\n\tADD_PARAMETER(m_atol     , \"tolerance\");\n\tADD_PARAMETER(m_eps      , \"penalty\"  );\n\tADD_PARAMETER(m_btwo_pass, \"two_pass\" );\n\tADD_PARAMETER(m_off      , \"offset\"   );\n\tADD_PARAMETER(m_naugmin  , \"minaug\"   );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n// FEPeriodicBoundary\n//-----------------------------------------------------------------------------\n\nFEPeriodicBoundary2O::FEPeriodicBoundary2O(FEModel* pfem) : FEContactInterface(pfem), m_ss(pfem), m_ms(pfem)\n{\n\tstatic int count = 1;\n\tSetID(count++);\n\n\tm_stol = 0.01;\n\tm_srad = 1.0;\n\tm_atol = 0;\n\tm_eps = 0;\n\tm_btwo_pass = false;\n\tm_off = vec3d(0,0,0);\n\tm_naugmin = 0;\n\n\t// set parents\n\tm_ss.SetContactInterface(this);\n\tm_ms.SetContactInterface(this);\n\n\tm_ss.SetSibling(&m_ms);\n\tm_ms.SetSibling(&m_ss);\n\n\tm_Fmacro.zero();\n\tm_Gmacro.zero();\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicBoundary2O::Init()\n{\n\t// create the surfaces\n\tif (m_ss.Init() == false) return false;\n\tif (m_ms.Init() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary2O::Activate()\n{\n\t// don't forget to call the base class\n\tFEContactInterface::Activate();\n\n\t// project primary surface onto secondary surface\n\tProjectSurface(m_ss, m_ms, false);\n\tProjectSurface(m_ms, m_ss, false);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary2O::CopyFrom(FESurfacePairConstraint* pci)\n{\n\t// cast to a periodic boundary\n\tFEPeriodicBoundary2O& pb = dynamic_cast<FEPeriodicBoundary2O&>(*pci);\n\n\t// copy parameters\n\tGetParameterList() = pb.GetParameterList();\n\n\t// copy data\n\tm_ss.CopyFrom(pb.m_ss);\n\tm_ms.CopyFrom(pb.m_ms);\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile for use in the stiffness matrix\n// TODO: what if two_pass ??\nvoid FEPeriodicBoundary2O::BuildMatrixProfile(FEGlobalMatrix& K)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the DOFS\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_RU = fem.GetDOFIndex(\"Ru\");\n\tconst int dof_RV = fem.GetDOFIndex(\"Rv\");\n\tconst int dof_RW = fem.GetDOFIndex(\"Rw\");\n\n\tvector<int> lm(6*5);\n\n\tfor (int j=0; j<m_ss.Nodes(); ++j)\n\t{\n\t\tFESurfaceElement& me = *m_ss.m_data[j].m_pme;\n\t\tint* en = &me.m_node[0];\n\n\t\tint n = me.Nodes();\n\t\tif (n == 3)\n\t\t{\n\t\t\tlm[6*(3+1)  ] = -1;\n\t\t\tlm[6*(3+1)+1] = -1;\n\t\t\tlm[6*(3+1)+2] = -1;\n\t\t\tlm[6*(3+1)+3] = -1;\n\t\t\tlm[6*(3+1)+4] = -1;\n\t\t\tlm[6*(3+1)+5] = -1;\n\t\t}\n\n\t\tlm[0] = m_ss.Node(j).m_ID[dof_X];\n\t\tlm[1] = m_ss.Node(j).m_ID[dof_Y];\n\t\tlm[2] = m_ss.Node(j).m_ID[dof_Z];\n\t\tlm[3] = m_ss.Node(j).m_ID[dof_RU];\n\t\tlm[4] = m_ss.Node(j).m_ID[dof_RV];\n\t\tlm[5] = m_ss.Node(j).m_ID[dof_RW];\n\n\t\tfor (int k=0; k<n; ++k)\n\t\t{\n\t\t\tvector<int>& id = mesh.Node(en[k]).m_ID;\n\t\t\tlm[6*(k+1)  ] = id[dof_X];\n\t\t\tlm[6*(k+1)+1] = id[dof_Y];\n\t\t\tlm[6*(k+1)+2] = id[dof_Z];\n\t\t\tlm[6*(k+1)+3] = id[dof_RU];\n\t\t\tlm[6*(k+1)+4] = id[dof_RV];\n\t\t\tlm[6*(k+1)+5] = id[dof_RW];\n\t\t}\n\n\t\tK.build_add(lm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project surface\nvoid FEPeriodicBoundary2O::ProjectSurface(FEPeriodicSurface& ss, FEPeriodicSurface& ms, bool bmove)\n{\n\tint i;\n\tdouble rs[2];\n\n\t// get the primary's center of mass\n\tvec3d cs = ss.CenterOfMass();\n\n\t// get the secondary's center of mass\n\tvec3d cm = ms.CenterOfMass();\n\n\t// get the relative distance\n\tvec3d cr = cs - cm;\n\n\t// unit vector in direction of cr\n\t// this will serve as the projection distance\n\tvec3d cn(cr); cn.unit();\n\n\t// initialize projection data\n\tFENormalProjection np(ms);\n\tnp.SetTolerance(m_stol);\n\tnp.SetSearchRadius(m_srad);\n\tnp.Init();\n\n\t// loop over all primary nodes\n\tfor (i=0; i<ss.Nodes(); ++i)\n\t{\n\t\tFENode& node = ss.Node(i);\n\n\t\t// get the nodal position\n\t\tvec3d r0 = node.m_r0;\n\n\t\t// find the intersection with the secondary surface\n\t\tss.m_data[i].m_pme = np.Project3(r0, cn, rs);\n\t\tassert(ss.m_data[i].m_pme);\n\n\t\tss.m_data[i].m_rs[0] = rs[0];\n\t\tss.m_data[i].m_rs[1] = rs[1];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary2O::Update()\n{\n\tint i, j, ne;\n\tFESurfaceElement* pme;\n\n\tFEMesh& mesh = *m_ss.GetMesh();\n\n\t//vec3d us, um;\n\tvec3d ws, wm;\n\t//vec3d umi[FEElement::MAX_NODES];\n\tvec3d wmi[FEElement::MAX_NODES];\n\n\t// update gap functions\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// off-set sign\n\t\tdouble s = (np==0?1.0:-1.0);\n\n\t\tint N = ss.Nodes();\n\n\t\tfor (i=0; i<N; ++i)\n\t\t{\n\t\t\t// calculate the primary displacement\n\t\t\tFENode& node = ss.Node(i);\n\t\t\t//us = node.m_rt - node.m_r0;\n\t\t\tws = node.m_rt - m_Fmacro*node.m_r0 - m_Gmacro.contractdyad1(node.m_r0)*0.5;\n\n\t\t\t// get the secondary element\n\t\t\tpme = ss.m_data[i].m_pme;\n\n\t\t\t// calculate the secondary displacement\n\t\t\tne = pme->Nodes();\n\t\t\tfor (j=0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = ms.Node(pme->m_lnode[j]);\n\t\t\t\twmi[j] = node.m_rt - m_Fmacro*node.m_r0 - m_Gmacro.contractdyad1(node.m_r0)*0.5;\n\t\t\t}\n\t\t\t\n\t\t\twm = pme->eval(wmi, ss.m_data[i].m_rs[0], ss.m_data[i].m_rs[1]);\n\n\t\t\t// calculate gap function\n\t\t\t//ss.m_gap[i] = ws - wm + m_off*s;\n\t\t\tss.m_data[i].m_gap = ws - wm;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary2O::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tint j, k, l, m, n;\n\tint nseln, nmeln;\n\n\tdouble *Gr, *Gs;\n\n\t// jacobian\n\tdouble detJ;\n\n\tvec3d dxr, dxs;\n\tdouble* w;\n\n\t// natural coordinates of primary node in secondary element\n\tdouble r, s;\n\n\t// contact force\n\tvec3d tc;\n\n\t// shape function values\n\tdouble N[FEElement::MAX_NODES];\n\n\t// element contact force vector\n\tvector<double> fe;\n\n\t// the lm array for this force vector\n\tvector<int> lm;\n\n\t// the en array\n\tvector<int> en;\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tvec3d r0[FEElement::MAX_NODES], rt[FEElement::MAX_NODES];\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\tfor (int i=0; i<ss.m_data.size(); ++i) ss.m_data[i].m_Fr = vec3d(0,0,0);\n\n\t\t// loop over all primary facets\n\t\tint ne = ss.Elements();\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\t// get the primary element\n\t\t\tFESurfaceElement& sel = ss.Element(j);\n\t\t\t\n\t\t\t// get the elements LM vector\n\t\t\tss.UnpackLM(sel, sLM);\n\n\t\t\tnseln = sel.Nodes();\n\n\t\t\tfor (int i=0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.GetMesh()->Node(sel.m_node[i]).m_r0;\n\t\t\t\trt[i] = ss.GetMesh()->Node(sel.m_node[i]).m_rt;\n\t\t\t}\n\t\t\tw = sel.GaussWeights();\n\n\t\t\t// loop over primary element nodes (which are the integration points as well)\n\t\t\tfor (n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = sel.Gr(n);\n\t\t\t\tGs = sel.Gs(n);\n\n\t\t\t\tm = sel.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get primary node contact force\n\t\t\t\ttc = ss.m_data[m].m_Lm + ss.m_data[m].m_gap*m_eps;\n\t\t\t\tss.m_data[m].m_Tn = tc;\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& mel = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(mel, mLM);\n\n\t\t\t\tnmeln = mel.Nodes();\n\n\t\t\t\t// isoparametric coordinates of the projected primary node\n\t\t\t\t// onto the secondary element\n\t\t\t\tr = ss.m_data[m].m_rs[0];\n\t\t\t\ts = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tN[0] = 0.25*(1-r)*(1-s);\n\t\t\t\t\tN[1] = 0.25*(1+r)*(1-s);\n\t\t\t\t\tN[2] = 0.25*(1+r)*(1+s);\n\t\t\t\t\tN[3] = 0.25*(1-r)*(1+s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tN[0] = 1 - r - s;\n\t\t\t\t\tN[1] = r;\n\t\t\t\t\tN[2] = s;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// calculate force vector\n\t\t\t\tfe.resize(3*(nmeln+1));\n\t\t\t\tfe[0] = -detJ*w[n]*tc.x;\n\t\t\t\tfe[1] = -detJ*w[n]*tc.y;\n\t\t\t\tfe[2] = -detJ*w[n]*tc.z;\n\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tfe[3*(l+1)  ] = detJ*w[n]*tc.x*N[l];\n\t\t\t\t\tfe[3*(l+1)+1] = detJ*w[n]*tc.y*N[l];\n\t\t\t\t\tfe[3*(l+1)+2] = detJ*w[n]*tc.z*N[l];\n\t\t\t\t}\n\n\t\t\t\t// fill the lm array\n\t\t\t\tlm.resize(3*(nmeln+1));\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(l+1)  ] = mLM[l*3  ];\n\t\t\t\t\tlm[3*(l+1)+1] = mLM[l*3+1];\n\t\t\t\t\tlm[3*(l+1)+2] = mLM[l*3+2];\n\t\t\t\t}\n\n\t\t\t\t// fill the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = sel.m_node[n];\n\t\t\t\tfor (l=0; l<nmeln; ++l) en[l+1] = mel.m_node[l];\n\n\t\t\t\t// assemble into global force vector\n\t\t\t\tR.Assemble(en, lm, fe);\n\n\t\t\t\t// also store in the reaction force vector\n\t\t\t\tvec3d& fr = ss.m_data[m].m_Fr;\n\t\t\t\tfr.x += fe[0];\n\t\t\t\tfr.y += fe[1];\n\t\t\t\tfr.z += fe[2];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary2O::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tint j, k, l, n, m;\n\tint nseln, nmeln, ndof;\n\n\tFEElementMatrix ke;\n\n\tconst int MN = FEElement::MAX_NODES;\n\tvector<int> lm(3*(MN+1));\n\tvector<int> en(MN+1);\n\n\tdouble *Gr, *Gs, *w;\n\tvec3d rt[MN], r0[MN];\n\n\tvec3d rtm[MN];\n\n\tdouble detJ, r, s;\n\tvec3d dxr, dxs;\n\tdouble H[MN];\n\n\tvec3d gap, Lm, tc;\n\n\t// curvature tensor K\n\tdouble K[2][2] = {0};\n\n//\tdouble scale = -0.0035*m_fem.GetMesh().GetBoundingBox().radius();\n\n\tvector<int> sLM;\n\tvector<int> mLM;\n\n\tint npass = (m_btwo_pass?2:1);\n\tfor (int np=0; np<npass; ++np)\n\t{\n\t\tFEPeriodicSurface& ss = (np == 0? m_ss : m_ms);\n\t\tFEPeriodicSurface& ms = (np == 0? m_ms : m_ss);\n\n\t\t// loop over all primary elements\n\t\tint ne = ss.Elements();\n\t\tfor (j=0; j<ne; ++j)\n\t\t{\n\t\t\tFESurfaceElement& se = ss.Element(j);\n\n\t\t\t// get the element's LM vector\n\t\t\tss.UnpackLM(se, sLM);\n\n\t\t\tnseln = se.Nodes();\n\n\t\t\tfor (int i=0; i<nseln; ++i)\n\t\t\t{\n\t\t\t\tr0[i] = ss.GetMesh()->Node(se.m_node[i]).m_r0;\n\t\t\t\trt[i] = ss.GetMesh()->Node(se.m_node[i]).m_rt;\n\t\t\t}\n\n\t\t\tw = se.GaussWeights();\n\n\t\t\t// loop over all integration points (that is nodes)\n\t\t\tfor (n=0; n<nseln; ++n)\n\t\t\t{\n\t\t\t\tGr = se.Gr(n);\n\t\t\t\tGs = se.Gs(n);\n\n\t\t\t\tm = se.m_lnode[n];\n\n\t\t\t\t// calculate jacobian\n\t\t\t\tdxr = dxs = vec3d(0,0,0);\n\t\t\t\tfor (k=0; k<nseln; ++k)\n\t\t\t\t{\n\t\t\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t\t\t}\n\n\t\t\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\t\t\t// get the secondary element\n\t\t\t\tFESurfaceElement& me = *ss.m_data[m].m_pme;\n\t\t\t\tms.UnpackLM(me, mLM);\n\n\t\t\t\tnmeln = me.Nodes();\n\n\t\t\t\t// get the secondary element node positions\n\t\t\t\tfor (k=0; k<nmeln; ++k) rtm[k] = ms.GetMesh()->Node(me.m_node[k]).m_rt;\n\n\t\t\t\t// primary node natural coordinates in secondary element\n\t\t\t\tr = ss.m_data[m].m_rs[0];\n\t\t\t\ts = ss.m_data[m].m_rs[1];\n\n\t\t\t\t// get primary node normal force\n\t\t\t\ttc = ss.m_data[m].m_Lm + ss.m_data[m].m_gap*m_eps; //ss.T[m];\n\n\t\t\t\t// get the secondary shape function values at this primary node\n\t\t\t\tif (nmeln == 4)\n\t\t\t\t{\n\t\t\t\t\t// quadrilateral\n\t\t\t\t\tH[0] = 0.25*(1-r)*(1-s);\n\t\t\t\t\tH[1] = 0.25*(1+r)*(1-s);\n\t\t\t\t\tH[2] = 0.25*(1+r)*(1+s);\n\t\t\t\t\tH[3] = 0.25*(1-r)*(1+s);\n\t\t\t\t}\n\t\t\t\telse if (nmeln == 3)\n\t\t\t\t{\n\t\t\t\t\t// triangle\n\t\t\t\t\tH[0] = 1 - r - s;\n\t\t\t\t\tH[1] = r;\n\t\t\t\t\tH[2] = s;\n\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\t// number of degrees of freedom\n\t\t\t\tndof = 3*(1 + nmeln);\n\n\t\t\t\t// fill stiffness matrix\n\t\t\t\tke.resize(ndof, ndof); ke.zero();\n\t\t\t\tke[0][0] = w[n]*detJ*m_eps;\n\t\t\t\tke[1][1] = w[n]*detJ*m_eps;\n\t\t\t\tke[2][2] = w[n]*detJ*m_eps;\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tke[0][3+3*k  ] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[1][3+3*k+1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[2][3+3*k+2] = -w[n]*detJ*m_eps*H[k];\n\n\t\t\t\t\tke[3+3*k  ][0] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+1][1] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t\tke[3+3*k+2][2] = -w[n]*detJ*m_eps*H[k];\n\t\t\t\t}\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t\tfor (l=0; l<nmeln; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tke[3+3*k  ][3+3*l  ] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+1][3+3*l+1] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t\tke[3+3*k+2][3+3*l+2] = w[n]*detJ*m_eps*H[k]*H[l];\n\t\t\t\t\t}\n\n\t\t\t\t// create lm array\n\t\t\t\tlm[0] = sLM[n*3  ];\n\t\t\t\tlm[1] = sLM[n*3+1];\n\t\t\t\tlm[2] = sLM[n*3+2];\n\n\t\t\t\tfor (k=0; k<nmeln; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[3*(k+1)  ] = mLM[k*3  ];\n\t\t\t\t\tlm[3*(k+1)+1] = mLM[k*3+1];\n\t\t\t\t\tlm[3*(k+1)+2] = mLM[k*3+2];\n\t\t\t\t}\n\n\t\t\t\t// create the en array\n\t\t\t\ten.resize(nmeln+1);\n\t\t\t\ten[0] = se.m_node[n];\n\t\t\t\tfor (k=0; k<nmeln; ++k) en[k+1] = me.m_node[k];\n\n\t\t\t\t// assemble stiffness matrix\n\t\t\t\tke.SetNodes(en);\n\t\t\t\tke.SetIndices(lm);\n\t\t\t\tLS.Assemble(ke);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEPeriodicBoundary2O::Augment(int naug, const FETimeInfo& tp)\n{\n\t// make sure we need to augment\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint i;\n\n\tdouble g;\n\tvec3d lm;\n\n\t// calculate initial norms\n\tdouble normL0 = 0;\n\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_data[i].m_Lm;\n\t\tnormL0 += lm*lm;\n\t}\n\tnormL0 = sqrt(normL0);\n\n\t// update Lagrange multipliers and calculate current norms\n\tdouble normL1 = 0;\n\tdouble normgc = 0;\n\tint N = 0;\n\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t{\n\t\tlm = m_ss.m_data[i].m_Lm + m_ss.m_data[i].m_gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ss.m_data[i].m_gap.norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t{\n\t\tlm = m_ms.m_data[i].m_Lm + m_ms.m_data[i].m_gap*m_eps;\n\n\t\tnormL1 += lm*lm;\n\t\tg = m_ms.m_data[i].m_gap.norm();\n\t\tnormgc += g*g;\n\t\t++N;\n\t}\n\tif (N == 0) N=1;\n\n\tnormL1 = sqrt(normL1);\n\tnormgc = sqrt(normgc / N);\n\n\tfeLog(\" tied interface # %d\\n\", GetID());\n\tfeLog(\"                        CURRENT        REQUIRED\\n\");\n\tdouble pctn = 0;\n\tif (fabs(normL1) > 1e-10) pctn = fabs((normL1 - normL0)/normL1);\n\tfeLog(\"    normal force : %15le %15le\\n\", pctn, m_atol);\n\tfeLog(\"    gap function : %15le       ***\\n\", normgc);\n\n\t// check convergence of constraints\n\tbool bconv = true;\n\tif (pctn >= m_atol) bconv = false;\n\tif (m_naugmin > naug) bconv = false;\n\n\t// update Lagrange multipliers if we did not converge\n\tif (bconv == false)\n\t{\n\t\tfor (i=0; i<m_ss.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ss.m_data[i].m_Lm = m_ss.m_data[i].m_Lm + m_ss.m_data[i].m_gap*m_eps;\n\t\t}\n\t\tfor (i=0; i<m_ms.Nodes(); ++i)\n\t\t{\n\t\t\t// update Lagrange multipliers\n\t\t\tm_ms.m_data[i].m_Lm = m_ms.m_data[i].m_Lm + m_ms.m_data[i].m_gap*m_eps;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPeriodicBoundary2O::Serialize(DumpStream &ar)\n{\n\t// store contact data\n\tFEContactInterface::Serialize(ar);\n\n\t// store contact surface data\n\tm_ms.Serialize(ar);\n\tm_ss.Serialize(ar);\n}\n"
  },
  {
    "path": "FEBioRVE/FEPeriodicBoundary2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FEBioMech/FEContactInterface.h\"\n#include \"FEBioMech/FEPeriodicBoundary.h\"\n#include \"FEBioMech/FEContactSurface.h\"\n#include <FECore/tens3d.h>\n\n//-----------------------------------------------------------------------------\n\nclass FEPeriodicBoundary2O : public FEContactInterface\n{\npublic:\n\t//! constructor\n\tFEPeriodicBoundary2O(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FEPeriodicBoundary2O(void) {}\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! interface activation\n\tvoid Activate() override;\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! return the primary and secondary surface\n\tFESurface* GetPrimarySurface() override { return &m_ss; }\n\tFESurface* GetSecondarySurface() override { return &m_ms; }\n\n\t//! return integration rule class\n\tbool UseNodalIntegration() override { return true; }\n\n\t//! build the matrix profile for use in the stiffness matrix\n\tvoid BuildMatrixProfile(FEGlobalMatrix& K) override;\n\n\t//! create a copy of this interface\n\tvoid CopyFrom(FESurfacePairConstraint* pci) override;\n\npublic:\n\t//! calculate contact forces\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! calculate contact stiffness\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! calculate Lagrangian augmentations\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! update\n\tvoid Update() override;\n\nprotected:\n\tvoid ProjectSurface(FEPeriodicSurface& ss, FEPeriodicSurface& ms, bool bmove);\n\npublic:\n\tFEPeriodicSurface\t\tm_ss;\t//!< primary surface\n\tFEPeriodicSurface\t\tm_ms;\t//!< secondary surface\n\n\tdouble\tm_atol;\t\t\t//!< augmentation tolerance\n\tdouble\tm_eps;\t\t\t//!< penalty scale factor\n\tdouble\tm_stol;\t\t\t//!< search tolerance\n\tdouble  m_srad;\t\t\t//!< search radius (%)\n\tbool\tm_btwo_pass;\t//!< two-pass flag\n\tint\t\tm_naugmin;\t\t//!< minimum number of augmentations\n\tvec3d\tm_off;\t\t\t//!< relative displacement offset\n\n\tmat3d\t\tm_Fmacro;\t\t//!< Macroscopic deformation gradient\n\ttens3drs\tm_Gmacro;\t\t//!< Macroscopic deformation Hessian\n\t\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/FEPeriodicLinearConstraint2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPeriodicLinearConstraint2O.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FELinearConstraint.h>\n#include <FECore/FEMesh.h>\n#include <FECore/FESurface.h>\n\nFEPeriodicLinearConstraint2O::NodeSetSet::NodeSetSet()\n{\n}\n\nFEPeriodicLinearConstraint2O::NodeSetSet::NodeSetSet(const FEPeriodicLinearConstraint2O::NodeSetSet& nss)\n{\n\tsecondary = nss.secondary;\n\tprimary = nss.primary;\n}\n\nvoid FEPeriodicLinearConstraint2O::NodeSetSet::operator = (const FEPeriodicLinearConstraint2O::NodeSetSet& nss)\n{\n\tsecondary = nss.secondary;\n\tprimary = nss.primary;\n}\n\n\nFEPeriodicLinearConstraint2O::FEPeriodicLinearConstraint2O()\n{\n}\n\nFEPeriodicLinearConstraint2O::~FEPeriodicLinearConstraint2O()\n{\n}\n\nvoid FEPeriodicLinearConstraint2O::AddNodeSetPair(const FENodeList& ms, const FENodeList& ss, bool push_back)\n{\n\tNodeSetSet sp;\n\tsp.secondary = ms;\n\tsp.primary = ss;\n\tif (push_back) m_set.push_back(sp); else m_set.insert(m_set.begin(), sp);\n}\n\nint FEPeriodicLinearConstraint2O::closestNode(FEMesh& mesh, const FENodeList& set, const vec3d& r)\n{\n\tint nmin = -1;\n\tdouble Dmin = 0.0;\n\tfor (int i = 0; i<(int)set.Size(); ++i)\n\t{\n\t\tvec3d& ri = mesh.Node(set[i]).m_r0;\n\t\tdouble D = (r - ri)*(r - ri);\n\t\tif ((D < Dmin) || (nmin == -1))\n\t\t{\n\t\t\tDmin = D;\n\t\t\tnmin = i;\n\t\t}\n\t}\n\treturn nmin;\n}\n\nvoid FEPeriodicLinearConstraint2O::addLinearConstraint(FEModel& fem, int parent, int child)\n{\n\t// get the linear constraint manager\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\n\t// do one constraint for x, y, z\n\tfor (int j = 0; j<3; ++j)\n\t{\n\t\tFELinearConstraint* lc = fecore_alloc(FELinearConstraint, &fem);\n\t\tlc->SetParentDof(j, parent);\n\t\tlc->AddChildDof(j, child, 1.0);\n\n\t\tLCM.AddLinearConstraint(lc);\n\t}\n}\n\nbool FEPeriodicLinearConstraint2O::GenerateConstraints(FEModel* fem)\n{\n\t// get the model's mesh\n\tFEMesh& mesh = fem->GetMesh();\n\n\t// make sure there is a list of sets\n\tif (m_set.empty()) return true;\n\n\t// make sure there are three sets\n\tif (m_set.size() != 3) return false;\n\n\t// tag all nodes to identify what they are (edge, face, corner)\n\tint N = mesh.Nodes();\n\tvector<int> tag(N, 0);\n\n\tfor (size_t i = 0; i<m_set.size(); ++i)\n\t{\n\t\tFENodeList& ms = m_set[i].secondary;\n\t\tFENodeList& ss = m_set[i].primary;\n\n\t\tfor (int j = 0; j<(int)ms.Size(); ++j) tag[ms[j]]++;\n\t\tfor (int j = 0; j<(int)ss.Size(); ++j) tag[ss[j]]++;\n\t}\n\n\t// flip signs on primary\n\tfor (size_t i = 0; i<m_set.size(); ++i)\n\t{\n\t\tFENodeList& ss = m_set[i].primary;\n\t\tfor (int j = 0; j<(int)ss.Size(); ++j)\n\t\t{\n\t\t\tint ntag = tag[ss[j]];\n\t\t\tif (ntag > 0) tag[ss[j]] = -ntag;\n\t\t}\n\t}\n\n\t// At this point, the following should hold\n\t// primary nodes: tag < 0, secondary nodes: tag > 0, interior nodes: tag = 0\n\t// only one secondary node should have a value of 3. We make this the reference node\n\tint refNode = -1;\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tif (tag[i] == 3)\n\t\t{\n\t\t\tassert(refNode == -1);\n\t\t\trefNode = i;\n\t\t}\n\t}\n\tassert(refNode != -1);\n\tif (refNode == -1) return false;\n\n\t// extract all 12 edges\n\tvector<FENodeList> surf;\n\tfor (int i = 0; i<(int)m_set.size(); ++i)\n\t{\n\t\tsurf.push_back(m_set[i].secondary);\n\t\tsurf.push_back(m_set[i].primary);\n\t}\n\n\tvector<FENodeList> secondaryEdges;\n\tvector<FENodeList> primaryEdges;\n\tfor (int i = 0; i<surf.size(); ++i)\n\t{\n\t\tFENodeList& s0 = surf[i];\n\t\tfor (int j = i + 1; j<surf.size(); ++j)\n\t\t{\n\t\t\tFENodeList& s1 = surf[j];\n\t\t\tvector<int> tmp(N, 0);\n\t\t\tfor (int k = 0; k<s0.Size(); ++k) tmp[s0[k]]++;\n\t\t\tfor (int k = 0; k<s1.Size(); ++k) tmp[s1[k]]++;\n\n\t\t\tFENodeList edge(&mesh);\n\t\t\tfor (int k = 0; k<N; ++k)\n\t\t\t{\n\t\t\t\tif (tmp[k] == 2) edge.Add(k);\n\t\t\t}\n\n\t\t\tif (edge.Size() != 0)\n\t\t\t{\n\t\t\t\t// see if this is a secondary edge or not\n\t\t\t\t// we assume it's a secondary edge if it connects to the refnode\n\t\t\t\tbool bsecondary = false;\n\t\t\t\tfor (int k = 0; k<edge.Size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tif (edge[k] == refNode)\n\t\t\t\t\t{\n\t\t\t\t\t\tbsecondary = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (bsecondary)\n\t\t\t\t\tsecondaryEdges.push_back(edge);\n\t\t\t\telse\n\t\t\t\t\tprimaryEdges.push_back(edge);\n\t\t\t}\n\t\t}\n\t}\n\n\t// since it is assumed the geometry is a cube, the following must hold\n\tassert(secondaryEdges.size() == 3);\n\tassert(primaryEdges.size() == 9);\n\n\t// find the secondary edge vectors\n\tvec3d Em[3];\n\tfor (int i = 0; i<3; ++i)\n\t{\n\t\tFENodeList& edge = secondaryEdges[i];\n\n\t\t// get the edge vector\n\t\tEm[i] = edge.Node(0)->m_r0 - edge.Node(1)->m_r0; assert(edge[0] != edge[1]);\n\t\tEm[i].unit();\n\t}\n\n\t// setup the constraints for the surfaces\n\tfor (int n=0; n<m_set.size(); ++n)\n\t{\n\t\tFENodeList& ms = m_set[n].secondary;\n\t\tFENodeList& ss = m_set[n].primary;\n\n\t\t// loop over all primary nodes\n\t\tfor (int i=0; i<ss.Size(); ++i)\n\t\t{\n\t\t\tassert(tag[ss[i]] < 0);\n\t\t\tif (tag[ss[i]] == -1)\n\t\t\t{\n\t\t\t\t// get the nodal position\n\t\t\t\tvec3d rs = ss.Node(i)->m_r0;\n\n\t\t\t\t// find the corresponding node on the secondary side\n\t\t\t\tint m = closestNode(mesh, ms, rs);\n\t\t\t\tassert(tag[ms[m]] == 1);\n\n\t\t\t\t// setup the linear constraint\n\t\t\t\taddLinearConstraint(*fem, ss[i], ms[m]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// setup the constraint for the edges\n\tfor (int n = 0; n<(int)primaryEdges.size(); ++n)\n\t{\n\t\tFENodeList& edge = primaryEdges[n];\n\n\t\t// get the edge vector\n\t\tvec3d E = edge.Node(0)->m_r0 - edge.Node(1)->m_r0; assert(edge[0] != edge[1]); E.unit();\n\n\t\t// find the corresponding secondary edge\n\t\tbool bfound = true;\n\t\tfor (int m = 0; m<3; ++m)\n\t\t{\n\t\t\tif (fabs(E*Em[m]) > 0.9999)\n\t\t\t{\n\t\t\t\tFENodeList& medge = secondaryEdges[m];\n\n\t\t\t\tfor (int i = 0; i<(int)edge.Size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tassert(tag[edge[i]] < 0);\n\t\t\t\t\tif (tag[edge[i]] == -2)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3d ri = edge.Node(i)->m_r0;\n\t\t\t\t\t\tint k = closestNode(mesh, medge, ri);\n\n\t\t\t\t\t\taddLinearConstraint(*fem, edge[i], medge[k]);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tassert(bfound);\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioRVE/FEPeriodicLinearConstraint2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMesh.h>\n#include \"febiorve_api.h\"\n#include <vector>\n\nclass FEModel;\n\nclass FEBIORVE_API FEPeriodicLinearConstraint2O\n{\n\tclass NodeSetSet\n\t{\n\tpublic:\n\t\tNodeSetSet();\n\t\tNodeSetSet(const NodeSetSet& nss);\n\t\tvoid operator = (const NodeSetSet& nns);\n\n\tpublic:\n\t\tFENodeList\tprimary;\n\t\tFENodeList\tsecondary;\n\t};\n\npublic:\n\tFEPeriodicLinearConstraint2O();\n\t~FEPeriodicLinearConstraint2O();\n\n\tvoid AddNodeSetPair(const FENodeList& ms, const FENodeList& ss, bool push_back = true);\n\n\tbool GenerateConstraints(FEModel* fem);\n\nprivate:\n\tint closestNode(FEMesh& mesh, const FENodeList& set, const vec3d& r);\n\tvoid addLinearConstraint(FEModel& fem, int parent, int child);\n\nprivate:\n\tstd::vector<NodeSetSet>\tm_set;\t// list of node set pairs\n};\n"
  },
  {
    "path": "FEBioRVE/FERVEModel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERVEModel.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEElemElemList.h\"\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include \"FEPeriodicBoundary1O.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FELoadCurve.h\"\n#include \"FEBioMech/FEBCPrescribedDeformation.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include <FECore/FEPeriodicLinearConstraint.h>\n#include <FECore/FECube.h>\n#include <FECore/FEPointFunction.h>\n#include <FECore/FECoreKernel.h>\n\n//-----------------------------------------------------------------------------\nFERVEModel::FERVEModel()\n{\n\tm_bctype = DISPLACEMENT;\n\n\t// Don't collect function timings for RVE models.\n\tCollectTimings(false);\n}\n\n//-----------------------------------------------------------------------------\nFERVEModel::~FERVEModel()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FERVEModel::SetParentModel(FEModel* fem)\n{\n\tm_parentfem = fem;\n}\n\n//-----------------------------------------------------------------------------\n// copy from the parent RVE\nvoid FERVEModel::CopyFrom(FERVEModel& rve)\n{\n\t// base class does most work\n\tFEModel::CopyFrom(rve);\n\n\t// copy the rest\n\tm_parentfem = rve.m_parentfem;\n\tm_bctype = rve.m_bctype;\n\tm_V0 = rve.m_V0;\n\tm_bb = rve.m_bb;\n\tm_BN = rve.m_BN;\n}\n\n//-----------------------------------------------------------------------------\nbool FERVEModel::Init()\n{\n\t// the RVE should only have one step\n\tif (Steps() != 1) return false;\n\tFEAnalysis* step = GetStep(0);\n\n\t// match the end time with the parent's\n\tFEAnalysis* parentStep = m_parentfem->GetStep(0);\n\tdouble tend = parentStep->m_ntime * parentStep->m_dt0;\n\tstep->m_ntime = parentStep->m_ntime;\n\tstep->m_dt0   = parentStep->m_dt0;\n\tstep->m_tend  = tend;\n\n\treturn FEModel::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes the RVE model and evaluates some useful quantities.\nbool FERVEModel::InitRVE(int rveType, const char* szbc)\n{\n\t// the RVE should only have one step\n\tif (Steps() != 1) return false;\n\tFEAnalysis* step = GetStep(0);\n\n\t// make sure the RVE problem doesn't output anything to a plot file\n\tstep->SetPlotLevel(FE_PLOT_NEVER);\n\n\t// Center the RVE about the origin.\n\t// This also calculates the bounding box\n\tCenterRVE();\n\n\t// generate prescribed BCs\n\t// TODO: Make this part of the RVE definition\n\tm_bctype = rveType;\n\tif (m_bctype == DISPLACEMENT)\n\t{\n\t\t// find the boundary nodes\n\t\tif ((szbc) && (szbc[0] != 0))\n\t\t{\n\t\t\t// get the RVE mesh\n\t\t\tFEMesh& m = GetMesh();\n\n\t\t\t// find the node set that defines the corner nodes\n\t\t\tFENodeSet* pset = m.FindNodeSet(szbc);\n\t\t\tif (pset == 0) return false;\n\n\t\t\t// prep displacement BC's\n\t\t\tif (PrepDisplacementBC(pset) == false) return false;\n\n\t\t\t// tag all boundary nodes\n\t\t\tint NN = m.Nodes();\n\t\t\tm_BN.assign(NN, 0);\n\t\t\tFENodeSet& ns = *pset;\n\t\t\tfor (int i=0; i<pset->Size(); ++i) m_BN[ns[i]] = 1; \n\t\t}\n\t\telse \n\t\t{\n\t\t\t// find all boundary nodes\n\t\t\tFindBoundaryNodes(m_BN);\n\t\t\t\n\t\t\t// create a (temporary) node set from the boundary nodes\n\t\t\tFEMesh& mesh = GetMesh();\n\t\t\tFENodeSet* set = new FENodeSet(this);\n\n\t\t\tint NN = mesh.Nodes();\n\t\t\tfor (int i = 0; i<NN; ++i) if (m_BN[i] == 1) set->Add(i);\n\n\t\t\t// prep the displacement BCs\n\t\t\tif (PrepDisplacementBC(set) == false) return false;\n\t\t}\n\t}\n\telse if (m_bctype == PERIODIC_LC)\n\t{\n\t\tPrepPeriodicLC();\n\t}\n/*\telse if (m_bctype == PERIODIC_AL)\n\t{\n\t\t// prep periodic BC's\n\t\tif (PrepPeriodicBC(szbc) == false) return false;\n\t}\n*/\telse return false;\n\n\t// initialize base class\n\tif (FEModel::Init() == false) return false;\n\n\t// calculate intial RVE volume\n\tEvalInitialVolume();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// This function sets up the periodic linear constraints boundary conditions.\n// It assumes that the geometry is a cube.\nbool FERVEModel::PrepPeriodicLC()\n{\n\t// make sure there no BCs defined\n\tClearBoundaryConditions();\n\n\t// user needs to define corner nodes\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\t// Assuming it's a cube, build the surface, edge, and corner node data\n\tFECube cube;\n\tif (cube.Build(this) == false) return false;\n\n\t// tag all boundary nodes\n\tint NN = m.Nodes();\n\tconst FENodeSet& bs = cube.GetBoundaryNodes();\n\tm_BN.resize(NN, 0);\n\tfor (int i = 0; i<bs.Size(); ++i) m_BN[bs[i]] = 1;\n\n\t// now, build the linear constraints\n\tFEPeriodicLinearConstraint plc(this);\n\tplc.AddNodeSetPair(cube.GetSurface(0)->GetNodeList(), cube.GetSurface(1)->GetNodeList());\n\tplc.AddNodeSetPair(cube.GetSurface(2)->GetNodeList(), cube.GetSurface(3)->GetNodeList());\n\tplc.AddNodeSetPair(cube.GetSurface(4)->GetNodeList(), cube.GetSurface(5)->GetNodeList());\n\tplc.GenerateConstraints(this);\n\n\t// find the node set that defines the corner nodes\n\tFENodeSet* corners = const_cast<FENodeSet*>(&cube.GetCornerNodes());\n\tif (PrepDisplacementBC(corners) == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluates the initial volume of the RVE model.\n//! This is called from FERVEModel::Init.\nvoid FERVEModel::EvalInitialVolume()\n{\n\tm_V0 = 0;\n\tFEMesh& m = GetMesh();\n\tfor (int k=0; k<m.Domains(); ++k)\n\t{\n\t\tFESolidDomain& dom = static_cast<FESolidDomain&>(m.Domain(k));\n\t\tfor (int i=0; i<dom.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tdouble ve = 0;\n\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEElasticMaterialPoint& pt = *el.GetMaterialPoint(n)->ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tdouble J = dom.detJt(el, n);\n\t\t\t\tve += J*w[n];\n\t\t\t}\n\t\t\tm_V0 += ve;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Centers the RVE around the origin.\nvoid FERVEModel::CenterRVE()\n{\n\tFEMesh& mesh = GetMesh();\n\tFENode& node = mesh.Node(0);\n\n\t// setup bounding box\n\tFEBoundingBox box(node.m_r0, node.m_r0);\n\tconst int NN = mesh.Nodes();\n\tfor (int i=1; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tbox.add(node.m_r0);\n\t}\n\n\t// get the center\n\tvec3d c = box.center();\n\t\n\t// recenter the RVE about the origin\n\tfor (int n = 0; n < NN; ++n)\n\t{\n\t\tFENode& node = mesh.Node(n);\n\t\tnode.m_r0 -= c;\n\t\tnode.m_rt = node.m_r0;\n\t}\n\n\t// adjust bounding box\n\tm_bb.translate(-c);\n}\n\n//-----------------------------------------------------------------------------\n//! Find the boundary nodes of the RVE model\nvoid FERVEModel::FindBoundaryNodes(vector<int>& BN)\n{\n\t// first we need to find all the boundary nodes\n\tFEMesh& m = GetMesh();\n\tint NN = m.Nodes();\n\tBN.assign(NN, 0);\n\n\t// create the element-element list\n\tFEElemElemList EEL;\n\tEEL.Create(&m);\n\n\tdouble wx = m_bb.width()*0.5;\n\tdouble wy = m_bb.height()*0.5;\n\tdouble wz = m_bb.depth()*0.5;\n\n\t// use the E-E list to tag all exterior nodes\n\tint fn[FEElement::MAX_NODES], M = 0;\n\tfor (int k=0; k<m.Domains(); ++k)\n\t{\n\t\tFEDomain& dom = m.Domain(k);\n\t\tfor (int i=0; i<dom.Elements(); ++i, ++M)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tint nf = el.Faces();\n\t\t\tfor (int j=0; j<nf; ++j)\n\t\t\t{\n\t\t\t\tif (EEL.Neighbor(M, j) == 0)\n\t\t\t\t{\n\t\t\t\t\t// mark all nodes\n\t\t\t\t\tint nn = el.GetFace(j, fn);\n\t\t\t\t\tfor (int k=0; k<nn; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = m.Node(fn[k]);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (fabs(node.m_r0.x) >= 0.999*wx) BN[fn[k]] = 1;\n\t\t\t\t\t\tif (fabs(node.m_r0.y) >= 0.999*wy) BN[fn[k]] = 1;\n\t\t\t\t\t\tif (fabs(node.m_r0.z) >= 0.999*wz) BN[fn[k]] = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Setup the displacement boundary conditions.\nbool FERVEModel::PrepDisplacementBC(FENodeSet* ns)\n{\n\t// create a load curve\n\tFELoadCurve* plc = fecore_alloc(FELoadCurve, this);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n\tAddLoadController(plc);\n\tint NLC = LoadControllers() - 1;\n\n\t// clear all BCs\n\tClearBoundaryConditions();\n\n\t// we create the prescribed deformation BC\n\tFEBCPrescribedDeformation* pdc = fecore_new<FEBCPrescribedDeformation>(\"prescribed deformation\", this);\n\tAddBoundaryCondition(pdc);\n\n\t// assign the boundary nodes\n\tpdc->SetNodeSet(ns);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FERVEModel::PrepPeriodicBC(const char* szbc)\n{\n\t// make sure the node set is valid\n\tif ((szbc==0)||(szbc[0]==0)) return false;\n\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\t// find the node set that defines the corner nodes\n\tFENodeSet* pset = m.FindNodeSet(szbc);\n\tif (pset == 0) return false;\n\tFENodeSet& ns = *pset;\n\n\t// check the periodic constraints\n\tint nc = SurfacePairConstraints();\n\tif (nc != 3) return false;\n\t\t\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tFEPeriodicBoundary1O* pbc = dynamic_cast<FEPeriodicBoundary1O*>(SurfacePairConstraint(i));\n\t\tif (pbc == 0) return false;\n\t}\n\n\t// create a load curve\n\tFELoadCurve* plc = fecore_alloc(FELoadCurve, this);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n\tAddLoadController(plc);\n\tint NLC = LoadControllers() - 1;\n\n\t// create the DC's\n\tClearBoundaryConditions();\n\tFEBCPrescribedDeformation* pdc = fecore_new<FEBCPrescribedDeformation>(\"prescribed deformation\", this);\n\tAddBoundaryCondition(pdc);\n\n\t// assign nodes to BCs\n\tpdc->SetNodeSet(pset);\n\n\t// create the boundary node flags\n\tm_BN.assign(m.Nodes(), 0);\n\tint N = ns.Size();\n\tfor (int i=0; i<N; ++i) m_BN[ns[i]] = 1;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FERVEModel::Update(const mat3d& F)\n{\n\t// get the mesh\n\tFEMesh& m = GetMesh();\n\n\t// assign new DC's for the boundary nodes\n\tFEBCPrescribedDeformation& dc = dynamic_cast<FEBCPrescribedDeformation&>(*BoundaryCondition(0));\n\tdc.SetDeformationGradient(F);\n\n\tif (m_bctype == FERVEModel::PERIODIC_AL)\n\t{\n\t\t// loop over periodic boundaries\n\t\tfor (int i = 0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary1O* pc = dynamic_cast<FEPeriodicBoundary1O*>(SurfacePairConstraint(i));\n\t\t\tassert(pc);\n\n\t\t\tpc->m_Fmacro = F;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FERVEModel::ScaleGeometry(double scale)\n{\n\t// get the mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// calculate the center of mass first\n\tvec3d rc(0,0,0);\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\trc += mesh.Node(i).m_r0;\n\t}\n\trc /= (double) mesh.Nodes();\n\n\t// scale the nodal positions around the center\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tvec3d r0 = node.m_r0;\n\t\tnode.m_r0 = rc + (r0 - rc)*scale;\n\n\t\tvec3d rt = node.m_rt;\n\t\tnode.m_rt = rc + (rt - rc)*scale;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! return current volume (calculated each time)\ndouble FERVEModel::CurrentVolume()\n{\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tdouble V = 0.0;\n\tfor (int i = 0; i<m.Domains(); ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(m.Domain(i));\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j<NE; ++j)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(j);\n\n\t\t\tint ni = el.GaussPoints();\n\t\t\tint ne = el.Nodes();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n = 0; n<ni; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& ep = *pt.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\t// calculate Jacobian\n\t\t\t\tdouble Jn = dom.detJt(el, n);\n\n\t\t\t\t// add it all up\n\t\t\t\tV += w[n] * Jn;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn V;\n}\n\n//-----------------------------------------------------------------------------\nmat3ds FERVEModel::StressAverage(mat3d& F, FEMaterialPoint& mp)\n{\n\t// rewind the RCI\n\tRCI_Rewind();\n\n\t// update the BC's\n\tUpdate(F);\n\n\t// match the time step\n\tFEModel& parent = *m_parentfem;\n\tFETimeInfo& ti = parent.GetTime();\n\tSetCurrentTimeStep(ti.timeIncrement);\n\n\t// advance the RVE solution\n\tbool bret = RCI_Advance();\n\n\tassert(ti.currentTime == GetCurrentTime());\n\n\t// make sure it converged\n\tif (bret == false) throw FEMultiScaleException(-1, -1);\n\n\t// calculate and return the (Cuachy) stress average\n\treturn StressAverage(mp);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the stress average\nmat3ds FERVEModel::StressAverage(FEMaterialPoint& mp)\n{\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tmat3ds T; T.zero();\n\n\tfor (int i=0; i<m.Domains(); ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(m.Domain(i));\n\t\tint NE = dom.Elements();\n\t\tfor (int j=0; j<NE; ++j)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(j);\n\n\t\t\tint ni = el.GaussPoints();\n\t\t\tint ne = el.Nodes();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n=0; n<ni; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& ep = *pt.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\t// calculate Jacobian\n\t\t\t\tdouble Jn = dom.detJt(el, n);\n\n\t\t\t\t// add it all up\n\t\t\t\tT += ep.m_s*(w[n]*Jn);\n\t\t\t}\n\t\t}\n\t}\n\n/*\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tdouble J = pt.m_J;\n\tdouble V0 = InitialVolume();\n\treturn T / (J*V0);\n*/\n\treturn T / CurrentVolume();\n}\n\n/*\n//-----------------------------------------------------------------------------\n//! Calculate the stress average\nmat3ds FERVEModel::StressAverage(FEMaterialPoint& mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tdouble J = pt.m_J;\n\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tmat3d T; T.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\t// TODO: Figure out a way to store all the reaction forces on the nodes\n\t//       That way, we don't need to do this anymore.\n\tif (m_bctype == FERVEModel::PERIODIC_AL)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i = 0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary1O* pbc = dynamic_cast<FEPeriodicBoundary1O*>(SurfacePairInteraction(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i = 0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_Fr[i];\n\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tT += (f & node.m_rt)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero). \n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\n\tif (m_bctype != FERVEModel::PERIODIC_LC)\n\t{\n\t\t// calculate the averaged stress\n\t\t// TODO: This might be more efficient if we keep a list of boundary nodes\n\t\tint NN = m.Nodes();\n\t\tfor (int j = 0; j<NN; ++j)\n\t\t{\n\t\t\tif (IsBoundaryNode(j))\n\t\t\t{\n\t\t\t\tFENode& n = m.Node(j);\n\t\t\t\tvec3d f;\n\t\t\t\tf.x = R[-n.m_ID[dof_X] - 2];\n\t\t\t\tf.y = R[-n.m_ID[dof_Y] - 2];\n\t\t\t\tf.z = R[-n.m_ID[dof_Z] - 2];\n\t\t\t\tT += f & n.m_rt;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i<m_FN.size(); ++i)\n\t\t{\n\t\t\tFENode& n = m.Node(m_FN[i]);\n\t\t\tvec3d f;\n\t\t\tf.x = R[-n.m_ID[dof_X] - 2];\n\t\t\tf.y = R[-n.m_ID[dof_Y] - 2];\n\t\t\tf.z = R[-n.m_ID[dof_Z] - 2];\n\t\t\tT += f & n.m_rt;\n\t\t}\n\t}\n\n\tdouble V0 = InitialVolume();\n\treturn T.sym() / (J*V0);\n}\n*/\n\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stiffness from the RVE solution.\ntens4ds FERVEModel::StiffnessAverage(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the mesh\n\tFEMesh& m = GetMesh();\n\n\t// the element's stiffness matrix\n\tmatrix ke;\n\n\t// element's residual\n\tvector<double> fe;\n\n\t// calculate the center point\n\tvec3d rc(0, 0, 0);\n\tfor (int k = 0; k<m.Nodes(); ++k) rc += m.Node(k).m_rt;\n\trc /= (double)m.Nodes();\n\n\t// elasticity tensor\n\ttens4ds c(0.);\n\n\t// calculate the stiffness matrix and residual\n\tfor (int k = 0; k<m.Domains(); ++k)\n\t{\n\t\tFEElasticSolidDomain& bd = dynamic_cast<FEElasticSolidDomain&>(m.Domain(k));\n\t\tint NS = bd.Elements();\n\t\tfor (int n = 0; n<NS; ++n)\n\t\t{\n\t\t\tFESolidElement& e = bd.Element(n);\n\n\t\t\t// create the element's stiffness matrix\n\t\t\tint ne = e.Nodes();\n\t\t\tint ndof = 3 * ne;\n\t\t\tke.resize(ndof, ndof);\n\t\t\tke.zero();\n\n\t\t\t// calculate the element's stiffness matrix\n\t\t\tbd.ElementStiffness(GetTime(), n, ke);\n\n\t\t\t// create the element's residual\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// calculate the element's residual\n\t\t\tbd.ElementInternalForce(e, fe);\n\n\t\t\t// loop over the element's nodes\n\t\t\tfor (int i = 0; i<ne; ++i)\n\t\t\t{\n\t\t\t\tFENode& ni = m.Node(e.m_node[i]);\n\t\t\t\tfor (int j = 0; j<ne; ++j)\n\t\t\t\t{\n\t\t\t\t\tFENode& nj = m.Node(e.m_node[j]);\n\t\t\t\t\tif (IsBoundaryNode(e.m_node[i]) && IsBoundaryNode(e.m_node[j]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// both nodes are boundary nodes\n\t\t\t\t\t\t// so grab the element's submatrix\n\t\t\t\t\t\tmat3d K;\n\t\t\t\t\t\tke.get(3 * i, 3 * j, K);\n\n\t\t\t\t\t\t// get the nodal positions\n\t\t\t\t\t\tvec3d ri = ni.m_rt - rc;\n\t\t\t\t\t\tvec3d rj = nj.m_rt - rc;\n\n\t\t\t\t\t\t// create the elasticity tensor\n\t\t\t\t\t\tc += dyad4s(ri, K, rj);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// divide by volume\n/*\tdouble V0 = InitialVolume();\n\tdouble Vi = 1.0 / (pt.m_J * V0);\n*/\n\tdouble Vi = 1.0 / CurrentVolume();\n\tc *= Vi;\n\n\treturn c;\n}\n\n// this function is hidden\nbool FERVEModel::Solve() { assert(false); return false; }\n"
  },
  {
    "path": "FEBioRVE/FERVEModel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEModel.h\"\n#include <FECore/tens4d.h>\n#include \"febiorve_api.h\"\n\n//-----------------------------------------------------------------------------\n// Class describing the RVE model.\n// This is used by the homogenization code.\nclass FEBIORVE_API FERVEModel : public FEModel\n{\npublic:\n\tenum RVE_TYPE\n\t{\n\t\tDISPLACEMENT,\t\t// prescribed displacement\n\t\tPERIODIC_LC,\t\t// periodic, linear constraints\n\t\tPERIODIC_AL\t\t\t// periodic, augmented Lagrangian (NOTE: obsolete, should probably delete)\n\t};\n\npublic:\n\tFERVEModel();\n\t~FERVEModel();\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! one time initialization (only called on the master RVE)\n\tbool InitRVE(int rveType, const char* szbc);\n\n\t//! Return the initial volume (calculated in Init)\n\tdouble InitialVolume() const { return m_V0; }\n\n\t//! return current volume (calculated each time)\n\tdouble CurrentVolume();\n\n\t// scale the geometry\n\tvoid ScaleGeometry(double scale);\n\n\t//! see if node is boundary node\n\tbool IsBoundaryNode(int i) const { return (m_BN[i]==1); }\n\n\t//! Update the RVE (before it is solved)\n\tvoid Update(const mat3d& F);\n\n\t// copy from the master RVE\n\tvoid CopyFrom(FERVEModel& rve);\n\n\t// set the parent FEModel\n\tvoid SetParentModel(FEModel* fem);\n\n\t//! Calculate the stress average\n\tmat3ds StressAverage(mat3d& F, FEMaterialPoint& mp);\n\tmat3ds StressAverage(FEMaterialPoint& mp);\n\n\t//! Calculate the stiffness average\n\ttens4ds StiffnessAverage(FEMaterialPoint &mp);\n\nprotected:\n\t//! Calculate the initial volume\n\tvoid EvalInitialVolume();\n\n\t//! find the list of boundary nodes\n\tvoid FindBoundaryNodes(vector<int>& BN);\n\n\t//! Center the RVE\n\tvoid CenterRVE();\n\n\tbool PrepDisplacementBC(FENodeSet* set);\n\tbool PrepPeriodicBC(const char* szbc);\n\tbool PrepPeriodicLC();\n\nprivate:\n\t// Hide the solve function so we can't call it. \n\t// (We use the RCI solution method)\n\tbool Solve() override;\n\nprotected:\n\tFEModel*\t\tm_parentfem;\t\t//!< parent FEModel\n\tdouble\t\t\tm_V0;\t\t\t\t//!< initial volume\n\tint\t\t\t\tm_bctype;\t\t\t//!< RVE type\n\tFEBoundingBox\tm_bb;\t\t\t\t//!< bounding box of mesh\n\tvector<int>\t\tm_BN;\t\t\t\t//!< boundary node flags\n};\n"
  },
  {
    "path": "FEBioRVE/FERVEModel2O.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERVEModel2O.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEElemElemList.h\"\n#include <FECore/FEPrescribedDOF.h>\n#include \"FEBioMech/FEElasticMaterial.h\"\n#include \"FEPeriodicBoundary2O.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include \"FEBCPrescribedDeformation2O.h\"\n#include \"FEPeriodicLinearConstraint2O.h\"\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FECube.h>\n#include <FECore/FEPointFunction.h>\n#include <FECore/FELoadCurve.h>\n\n//-----------------------------------------------------------------------------\nFERVEModel2O::FERVEModel2O()\n{\n\tm_rveType = FERVEModel2O::DISPLACEMENT;\n}\n\n//-----------------------------------------------------------------------------\nFERVEModel2O::~FERVEModel2O()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FERVEModel2O::ScaleGeometry(double scale)\n{\n\t// get the mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// calculate the center of mass first\n\tvec3d rc(0, 0, 0);\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\trc += mesh.Node(i).m_r0;\n\t}\n\trc /= (double)mesh.Nodes();\n\n\t// scale the nodal positions around the center\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tvec3d r0 = node.m_r0;\n\t\tnode.m_r0 = rc + (r0 - rc)*scale;\n\n\t\tvec3d rt = node.m_rt;\n\t\tnode.m_rt = rc + (rt - rc)*scale;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes the RVE model and evaluates some useful quantities.\nbool FERVEModel2O::InitRVE(int rveType, const char* szbc)\n{\n\t// make sure the RVE problem doesn't output anything to a plot file\n\tGetCurrentStep()->SetPlotLevel(FE_PLOT_NEVER);\n\n\t// Center the RVE about the origin.\n\t// This also calculates the bounding box\n\tCenterRVE();\n\n\t// generate prescribed BCs\n\t// TODO: Make this part of the RVE definition\n\tm_rveType = rveType;\n\tif (rveType == FERVEModel2O::DISPLACEMENT)\n\t{\n\t\t// find the boundary nodes\n\t\tif ((szbc) && (szbc[0] != 0))\n\t\t{\n\t\t\t// get the RVE mesh\n\t\t\tFEMesh& m = GetMesh();\n\n\t\t\t// find the node set that defines the corner nodes\n\t\t\tFENodeSet* pset = m.FindNodeSet(szbc);\n\t\t\tif (pset == 0) return false;\n\n\t\t\tFENodeSet& ns = *pset;\n\n\t\t\tint NN = m.Nodes();\n\t\t\tm_BN.assign(NN, 0);\n\t\t\tfor (int i = 0; i<pset->Size(); ++i) m_BN[ns[i]] = 1;\n\t\t}\n\t\telse FindBoundaryNodes(m_BN);\n\n\t\t// prep displacement BC's\n\t\tif (PrepDisplacementBC() == false) return false;\n\t}\n\telse if (rveType == FERVEModel2O::PERIODIC_LC)\n\t{\n\t\tif (PrepPeriodicLC() == false) return false;\n\t}\n/*\telse if (rveType == FERVEModel2O::PERIODIC_AL)\n\t{\n\t\t// prep periodic BC's\n\t\tif (PrepPeriodicBC(szbc) == false) return false;\n\t}\n*/\telse return false;\n\n\t// initialize base class\n\tif (FEModel::Init() == false) return false;\n\n\t// calculate intial RVE volume\n\tEvalInitialVolume();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluates the initial volume of the RVE model.\n//! This is called from FERVEModel2O::Init.\nvoid FERVEModel2O::EvalInitialVolume()\n{\n\tm_V0 = 0;\n\tFEMesh& m = GetMesh();\n\tfor (int k=0; k<m.Domains(); ++k)\n\t{\n\t\tFESolidDomain& dom = static_cast<FESolidDomain&>(m.Domain(k));\n\t\tfor (int i=0; i<dom.Elements(); ++i)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(i);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tdouble ve = 0;\n\t\t\tfor (int n=0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEElasticMaterialPoint& pt = *el.GetMaterialPoint(n)->ExtractData<FEElasticMaterialPoint>();\n\t\t\t\tdouble J = dom.detJ0(el, n);\n\t\t\t\tve += J*w[n];\n\t\t\t}\n\t\t\tm_V0 += ve;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Centers the RVE around the origin.\nvoid FERVEModel2O::CenterRVE()\n{\n\tFEMesh& mesh = GetMesh();\n\tFENode& node = mesh.Node(0);\n\n\t// setup bounding box\n\tFEBoundingBox box(node.m_r0);\n\tconst int NN = mesh.Nodes();\n\tfor (int i=1; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tbox.add(node.m_r0);\n\t}\n\n\t// get the center\n\tvec3d c = box.center();\n\t\n\t// recenter the RVE about the origin\n\tfor (int n=0; n<NN; ++n)\n\t{\n\t\tFENode& node = mesh.Node(n);\n\t\tnode.m_r0 -= c;\n\t\tnode.m_rt = node.m_r0;\n\t}\n\n\t// adjust bounding box\n\tm_bb.translate(-c);\n}\n\n//-----------------------------------------------------------------------------\n//! Find the boundary nodes of the RVE model\nvoid FERVEModel2O::FindBoundaryNodes(vector<int>& BN)\n{\n\t// first we need to find all the boundary nodes\n\tFEMesh& m = GetMesh();\n\tint NN = m.Nodes();\n\tBN.assign(NN, 0);\n\n\t// create the element-element list\n\tFEElemElemList EEL;\n\tEEL.Create(&m);\n\n\tdouble wx = m_bb.width()*0.5;\n\tdouble wy = m_bb.height()*0.5;\n\tdouble wz = m_bb.depth()*0.5;\n\n\t// use the E-E list to tag all exterior nodes\n\tint fn[FEElement::MAX_NODES], M = 0;\n\tfor (int k=0; k<m.Domains(); ++k)\n\t{\n\t\tFEDomain& dom = m.Domain(k);\n\t\tfor (int i=0; i<dom.Elements(); ++i, ++M)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tint nf = el.Faces();\n\t\t\tfor (int j=0; j<nf; ++j)\n\t\t\t{\n\t\t\t\tif (EEL.Neighbor(M, j) == 0)\n\t\t\t\t{\n\t\t\t\t\t// mark all nodes\n\t\t\t\t\tint nn = el.GetFace(j, fn);\n\t\t\t\t\tfor (int k=0; k<nn; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = m.Node(fn[k]);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (fabs(node.m_r0.x) >= 0.999*wx) BN[fn[k]] = 1;\n\t\t\t\t\t\tif (fabs(node.m_r0.y) >= 0.999*wy) BN[fn[k]] = 1;\n\t\t\t\t\t\tif (fabs(node.m_r0.z) >= 0.999*wz) BN[fn[k]] = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Setup the displacement boundary conditions.\nbool FERVEModel2O::PrepDisplacementBC()\n{\n\tFEMesh& m = GetMesh();\n\tint N = m.Nodes();\n\n\t// count the nr of exterior nodes\n\tint NN = 0, i;\n\tfor (i=0; i<N; ++i) if (m_BN[i] == 1) ++NN;\n\n\tassert(NN > 0);\n\n\t// create a load curve\n\tFELoadCurve* plc = new FELoadCurve(this);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n\tAddLoadController(plc);\n\tint NLC = LoadControllers() - 1;\n\n\t// clear all BCs\n\tClearBoundaryConditions();\n\n\t// we create the prescribed deformation BC\n\tFEBCPrescribedDeformation2O* pdc = fecore_new<FEBCPrescribedDeformation2O>(\"prescribed deformation 2O\", this);\n\tAddBoundaryCondition(pdc);\n\n\t// assign the boundary nodes\n\tFENodeSet* nset = new FENodeSet(this);\n\tint c = -1;\n\tfor (i = 0; i<N; ++i)\n\tif (m_BN[i] == 1)\n\t{\n\t\tif (c == -1) { pdc->SetReferenceNode(m_BN[i]); c = 1; }\n\t\tnset->Add(i);\n\t}\n\tpdc->SetNodeSet(nset);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FERVEModel2O::PrepPeriodicBC(const char* szbc)\n{\n\t// make sure the node set is valid\n\tif ((szbc==0)||(szbc[0]==0)) return false;\n\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\t// find the node set that defines the corner nodes\n\tFENodeSet* pset = m.FindNodeSet(szbc);\n\tif (pset == 0) return false;\n\tFENodeSet& ns = *pset;\n\n\t// check the periodic constraints\n\tint nc = SurfacePairConstraints();\n\tif (nc != 3) return false;\n\t\t\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tFEPeriodicBoundary2O* pbc = dynamic_cast<FEPeriodicBoundary2O*>(SurfacePairConstraint(i));\n\t\tif (pbc == 0) return false;\n\t}\n\n\t// create a load curve\n\tFELoadCurve* plc = new FELoadCurve(this);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n\tAddLoadController(plc);\n\tint NLC = LoadControllers() - 1;\n\n\t// create the DC's\n\tClearBoundaryConditions();\n\tFEPrescribedDOF* pdc[3] = { 0 };\n\tpdc[0] = new FEPrescribedDOF(this); pdc[0]->SetDOF(0); pdc[0]->SetScale(1.0, NLC);\n\tpdc[1] = new FEPrescribedDOF(this); pdc[1]->SetDOF(1); pdc[1]->SetScale(1.0, NLC);\n\tpdc[2] = new FEPrescribedDOF(this); pdc[2]->SetDOF(2); pdc[2]->SetScale(1.0, NLC);\n\n\tAddBoundaryCondition(pdc[0]);\n\tAddBoundaryCondition(pdc[1]);\n\tAddBoundaryCondition(pdc[2]);\n\n\t// assign nodes to BCs\n\tpdc[0]->SetNodeSet(pset);\n\tpdc[1]->SetNodeSet(pset);\n\tpdc[2]->SetNodeSet(pset);\n\n\t// create the boundary node flags\n\tm_BN.assign(m.Nodes(), 0);\n\tint N = ns.Size();\n\tfor (int i=0; i<N; ++i) m_BN[ns[i]] = 1;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FERVEModel2O::PrepPeriodicLC()\n{\n\t// clear all BCs, just to be sure\n\tClearBoundaryConditions();\n\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\t// Assuming this is a cube, build the cube data\n\tFECube cube;\n\tif (cube.Build(this) == false) return false;\n\n\t// setup the linear constraints\n\tFEPeriodicLinearConstraint2O lc;\n\tlc.GenerateConstraints(this);\n\n\tlc.AddNodeSetPair(cube.GetSurface(0)->GetNodeList(), cube.GetSurface(1)->GetNodeList());\n\tlc.AddNodeSetPair(cube.GetSurface(2)->GetNodeList(), cube.GetSurface(3)->GetNodeList());\n\tlc.AddNodeSetPair(cube.GetSurface(4)->GetNodeList(), cube.GetSurface(5)->GetNodeList());\n\n\t// find the node set that defines the corner nodes\n\tconst FENodeSet& set = cube.GetCornerNodes();\n\n\t// create a load curve\n\tFELoadCurve* plc = new FELoadCurve(this);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n\tAddLoadController(plc);\n\tint NLC = LoadControllers() - 1;\n\n\t// we create the prescribed deformation BC\n\tFEBCPrescribedDeformation2O* pdc = fecore_new<FEBCPrescribedDeformation2O>(\"prescribed deformation 2O\", this);\n\tAddBoundaryCondition(pdc);\n\n\t// assign nodes to BCs\n\tpdc->SetReferenceNode(set[0]);\n\tpdc->SetNodeSet(const_cast<FENodeSet*>(&set));\n\tpdc->SetScale(1.0, NLC);\n\n\t// create the boundary node flags\n\tm_BN.assign(m.Nodes(), 0);\n\tint N = set.Size();\n\tfor (int i = 0; i<N; ++i) m_BN[set[i]] = 1;\n\n\treturn true;\n}\n\n//=============================================================================\nFEMicroModel2O::FEMicroModel2O()\n{\n\tm_rveType = FERVEModel2O::DISPLACEMENT;\n\tm_V0 = 0.0;\n\tm_BN = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMicroModel2O::~FEMicroModel2O()\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEMicroModel2O::Init(FERVEModel2O& rve)\n{\n\t// copy the parent RVE\n\tCopyFrom(rve);\n\n\t// initialize model\n\tif (FEModel::Init() == false) return false;\n\n\t// copy some stuff from the parent RVE model\n\tm_V0 = rve.InitialVolume();\n\tm_rveType = rve.RVEType();\n\tm_BN = &rve.BoundaryList();\n\tif (m_BN == 0) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Solve the RVE model\nbool FEMicroModel2O::Solve(const mat3d& F, const tens3drs& G)\n{\n\t// update boundary conditions\n\tUpdateBC(F, G);\n\n\t// solve the model\n\treturn FEModel::Solve();\n}\n\n//-----------------------------------------------------------------------------\n//! Assign the prescribed displacement to the boundary nodes.\nvoid FEMicroModel2O::UpdateBC(const mat3d& F, const tens3drs& G)\n{\n\t// get the mesh\n\tFEMesh& m = GetMesh();\n\n\t// assign new DC's for the boundary nodes\n\tFEBCPrescribedDeformation2O& bc = dynamic_cast<FEBCPrescribedDeformation2O&>(*BoundaryCondition(0));\n\tbc.SetDeformationGradient(F);\n\tbc.SetDeformationHessian(G);\n\n\tif (m_rveType == FERVEModel2O::PERIODIC_AL)\n\t{\n\t\t// get the \"displacement\" component of the deformation gradient\n\t\tmat3d U = F - mat3dd(1);\n\n\t\t// set the offset for the periodic BC's\n\t\tvec3d r[FEElement::MAX_NODES];\n\n\t\t// loop over periodic boundaries\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary2O* pc = dynamic_cast<FEPeriodicBoundary2O*>(SurfacePairConstraint(i));\n\t\t\tassert(pc);\n\t\t\tpc->m_Fmacro = F;\n\t\t\tpc->m_Gmacro = G;\n\t\t}\n\t}\n\telse if (m_rveType == FERVEModel2O::PERIODIC_LC)\n\t{\n\t\t// get the linear constraint manager\n\t\tFELinearConstraintManager& LM = GetLinearConstraintManager();\n\n\t\tmat3d U = F - mat3dd(1.0);\n\n\t\t// loop over all the linear constraints\n\t\tconst int NL = LM.LinearConstraints();\n\t\tfor (int i=0; i<NL; ++i)\n\t\t{\n\t\t\tFELinearConstraint& lc = LM.LinearConstraint(i);\n\n\t\t\tFENode& parentNode = m.Node(lc.GetParentNode());\n\t\t\tFENode& childNode = m.Node(lc.GetChildDof(0).node);\n\n\t\t\tvec3d Xp = parentNode.m_r0;\n\t\t\tvec3d Xm = childNode.m_r0;\n\n\t\t\tmat3ds XXp = dyad(Xp);\n\t\t\tmat3ds XXm = dyad(Xm);\n\n\t\t\tvec3d d = U*(Xp - Xm) + (G.contract2s(XXp - XXm))*0.5;\n\n\t\t\tswitch (lc.GetParentDof())\n\t\t\t{\n\t\t\tcase 0: lc.SetOffset(d.x); break;\n\t\t\tcase 1: lc.SetOffset(d.y); break;\n\t\t\tcase 2: lc.SetOffset(d.z); break;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nmat3d FEMicroModel2O::AveragedStressPK1(FEMaterialPoint &mp)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\t\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tmat3d PK1; PK1.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\tif (m_rveType == FERVEModel2O::PERIODIC_AL)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary2O* pbc = dynamic_cast<FEPeriodicBoundary2O*>(SurfacePairConstraint(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_data[i].m_Fr;\n\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tPK1 += (f & node.m_r0)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero). \n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\tFEBCPrescribedDeformation2O& dc = dynamic_cast<FEBCPrescribedDeformation2O&>(*BoundaryCondition(0));\n\n\tconst FENodeSet& nset = *dc.GetNodeSet();\n\tint nitems = nset.Size();\n\tfor (int i=0; i<nitems; ++i)\n\t{\n\t\tconst FENode& n = *nset.Node(i);\n\t\tvec3d f;\n\t\tf.x = R[-n.m_ID[dof_X]-2];\n\t\tf.y = R[-n.m_ID[dof_Y]-2];\n\t\tf.z = R[-n.m_ID[dof_Z]-2];\n\t\tPK1 += f & n.m_r0;\n\t}\n\n\treturn PK1 / m_V0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nvoid FEMicroModel2O::AveragedStress2O(mat3d& Pa, tens3drs& Qa)\n{\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\t// calculate average PK1 stress\n\tPa.zero();\n\tQa.zero();\n\tfor (int i = 0; i<m.Domains(); ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(m.Domain(i));\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j<NE; ++j)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(j);\n\n\t\t\tint ni = el.GaussPoints();\n\t\t\tint ne = el.Nodes();\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tfor (int n = 0; n<ni; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(n);\n\t\t\t\tFEElasticMaterialPoint& ep = *pt.ExtractData<FEElasticMaterialPoint>();\n\n\t\t\t\t// calculate Jacobian\n\t\t\t\tdouble Jn = dom.detJt(el, n);\n\n\t\t\t\t// get the Cauchy stress \n\t\t\t\tmat3ds& s = ep.m_s;\n\n\t\t\t\t// convert to PK1\n\t\t\t\tmat3d& F = ep.m_F;\n\t\t\t\tdouble J = ep.m_J;\n\n\t\t\t\tmat3d Pn = (s*F.transinv())*J;\n\n\t\t\t\t// add it all up\n\t\t\t\tPa += Pn*(w[n] * Jn);\n\n\t\t\t\t// now do the second order stress\n\t\t\t\tvec3d X = pt.m_r0;\n\n\t\t\t\ttens3drs Qn = dyad3rs(Pn, X);\n\n\t\t\t\t// add it all up\n//\t\t\t\tQa += Qn*(w[n] * Jn);\n\t\t\t}\n\t\t}\n\t}\n\tPa /= m_V0;\n\tQa /= m_V0;\n}\n\n/*\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nvoid FEMicroModel2O::AveragedStress2O(mat3d& Pa, tens3drs& Qa)\n{\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tPa.zero();\n\tQa.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\tif (m_rveType == FERVEModel2O::PERIODIC_AL)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary2O* pbc = dynamic_cast<FEPeriodicBoundary2O*>(SurfacePairInteraction(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_Fr[i];\n\t\t\t\tconst vec3d& X = node.m_r0;\n\t\t\t\t\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tPa += (f & X)*2.0;\n\n\t\t\t\tQa += dyad3rs(f, X)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero).\n\tFEBCPrescribedDeformation2O& dc = dynamic_cast<FEBCPrescribedDeformation2O&>(*PrescribedBC(0));\n\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\n\tint N = dc.Items();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = m.Node(dc.NodeID(i));\n\t\tvec3d f;\n\t\tf.x = R[-n.m_ID[dof_X]-2];\n\t\tf.y = R[-n.m_ID[dof_Y]-2];\n\t\tf.z = R[-n.m_ID[dof_Z]-2];\n\n\t\tconst vec3d& X = n.m_r0;\n\n\t\tPa += (f & X);\n\n\t\tQa += dyad3rs(f, X);\n\t}\n\n\t// divide by volume\n\tPa /= m_V0;\n\tQa /= 2*m_V0;\n}\n*/\n\n/*\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nvoid FEMicroModel2O::AveragedStress2OPK1(FEMaterialPoint &mp, mat3d &PK1a, tens3drs &QK1a)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tmat3d PK1; PK1.zero();\n\ttens3drs QK1; QK1.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\tif (m_bperiodic)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary2O* pbc = dynamic_cast<FEPeriodicBoundary2O*>(SurfacePairInteraction(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_Fr[i];\n\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tPK1 += (f & node.m_r0)*2.0;\n\n\t\t\t\tvec3d X = node.m_r0;\n\t\t\n\t\t\t\tQK1 += dyad3rs(f, X)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero). \n\tFEPrescribedBC& dx = *PrescribedBC(0);\n\tFEPrescribedBC& dy = *PrescribedBC(1);\n\tFEPrescribedBC& dz = *PrescribedBC(2);\n\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\tint N = dx.Items();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = m.Node(dx.NodeID(i));\n\t\tvec3d f;\n\t\tf.x = R[-n.m_ID[dof_X]-2];\n\t\tf.y = R[-n.m_ID[dof_Y]-2];\n\t\tf.z = R[-n.m_ID[dof_Z]-2];\n\t\t\n\t\tPK1 += f & n.m_r0;\n\t\tvec3d X; X = n.m_r0;\n\t\t\n\t\tQK1 += dyad3rs(f, X);\n\t}\n\n\tPK1a = PK1 / m_V0;\n\tQK1a = QK1 / (2*m_V0);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stress from the RVE solution.\nvoid FEMicroModel2O::AveragedStress2OPK2(FEMaterialPoint &mp, mat3ds &Sa, tens3ds &Ta)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\tmat3d F = pt.m_F;\n\tdouble J = pt.m_J;\n\tmat3d Finv = F.inverse();\n\n\t// get the RVE mesh\n\tFEMesh& m = GetMesh();\n\n\tmat3d S; S.zero();\n\ttens3ds T; T.zero();\n\n\t// for periodic BC's we take the reaction forces directly from the periodic constraints\n\tif (m_bperiodic)\n\t{\n\t\t// get the reaction for from the periodic constraints\n\t\tfor (int i=0; i<3; ++i)\n\t\t{\n\t\t\tFEPeriodicBoundary2O* pbc = dynamic_cast<FEPeriodicBoundary2O*>(SurfacePairInteraction(i));\n\t\t\tassert(pbc);\n\t\t\tFEPeriodicSurface& ss = pbc->m_ss;\n\t\t\tint N = ss.Nodes();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tFENode& node = ss.Node(i);\n\t\t\t\tvec3d f = ss.m_Fr[i];\n\t\t\t\tvec3d f0 = Finv*f;\n\n\t\t\t\t// We multiply by two since the reaction forces are only stored at the primary surface \n\t\t\t\t// and we also need to sum over the secondary nodes (NOTE: should I figure out a way to \n\t\t\t\t// store the reaction forces on the secondary nodes as well?)\n\t\t\t\tS += (f0 & node.m_r0)*2.0;\n\n\t\t\t\tvec3d X = node.m_r0;\n\t\t\n\t\t\t\tT += dyad3s(X, f0, X)*2.0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// get the reaction force vector from the solid solver\n\t// (We also need to do this for the periodic BC, since at the prescribed nodes,\n\t// the contact forces will be zero). \n\tFEPrescribedBC& dx = *PrescribedBC(0);\n\tFEPrescribedBC& dy = *PrescribedBC(1);\n\tFEPrescribedBC& dz = *PrescribedBC(2);\n\n\tconst int dof_X = GetDOFIndex(\"x\");\n\tconst int dof_Y = GetDOFIndex(\"y\");\n\tconst int dof_Z = GetDOFIndex(\"z\");\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\tassert(ps);\n\tvector<double>& R = ps->m_Fr;\n\n\tint N = dx.Items();\n\t\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& n = m.Node(dx.NodeID(i));\n\t\tvec3d f;\n\t\tf.x = R[-n.m_ID[dof_X]-2];\n\t\tf.y = R[-n.m_ID[dof_Y]-2];\n\t\tf.z = R[-n.m_ID[dof_Z]-2];\n\t\tvec3d f0 = Finv*f;\n\t\t\n\t\tS += f0 & n.m_r0;\n\n\t\tvec3d X = n.m_r0;\n\t\t\n\t\tT += dyad3s(X, f0, X);\n\t}\n\n\tSa = S.sym() / m_V0;\n\tTa = T / (2*m_V0);\n}\n*/\n\n//-----------------------------------------------------------------------------\n//! Calculate the average stiffness from the RVE solution.\nvoid FEMicroModel2O::AveragedStiffness(FEMaterialPoint &mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J)\n{\n\tFEElasticMaterialPoint& pt = *mp.ExtractData<FEElasticMaterialPoint>();\n\n\t// get the mesh\n\tFEMesh& m = GetMesh();\n\n\t// get the solver\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolidSolver2* ps = dynamic_cast<FESolidSolver2*>(pstep->GetFESolver());\n\n\t// the element's stiffness matrix\n\tmatrix ke;\n\n\t// element's residual\n\tvector<double> fe;\n\n\t// calculate the center point\n\tvec3d rc(0,0,0);\n\tfor (int k=0; k<m.Nodes(); ++k) rc += m.Node(k).m_r0;\n\trc /= (double) m.Nodes();\n\n\t// zero the stiffness components\n\tC.zero();\n\tL.zero();\n\tH.zero();\n\tJ.zero();\n\n\t// calculate the stiffness matrix and residual\n\tfor (int nd=0; nd<m.Domains(); ++nd)\n\t{\n\t\tFEElasticSolidDomain& bd = dynamic_cast<FEElasticSolidDomain&>(m.Domain(nd));\n\t\tint NEL = bd.Elements();\n\t\tfor (int ne=0; ne<NEL; ++ne)\n\t\t{\n\t\t\tFESolidElement& el = bd.Element(ne);\n\n\t\t\t// create the element's stiffness matrix\n\t\t\tint neln = el.Nodes();\n\t\t\tint ndof = 3*neln;\n\t\t\tke.resize(ndof, ndof);\n\t\t\tke.zero();\n\n\t\t\t// calculate the element's stiffness matrix\n\t\t\tbd.ElementStiffness(GetTime(), ne, ke);\n\n\t\t\t// create the element's residual\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// calculate the element's residual\n\t\t\tbd.ElementInternalForce(el, fe);\n\n\t\t\t// loop over the element's nodes\n\t\t\tfor (int a=0; a<neln; ++a)\n\t\t\t{\n\t\t\t\tFENode& na = m.Node(el.m_node[a]);\n\t\t\t\tfor (int b=0; b<neln; ++b)\n\t\t\t\t{\n\t\t\t\t\tFENode& nb = m.Node(el.m_node[b]);\n\t\t\t\t\tif (IsBoundaryNode(el.m_node[a]) && IsBoundaryNode(el.m_node[b]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// both nodes are boundary nodes\n\t\t\t\t\t\t// so grab the element's submatrix\n\t\t\t\t\t\tmat3d K;\n\t\t\t\t\t\tke.get(3*a, 3*b, K);\n\n\t\t\t\t\t\t// get the nodal positions relative to the center\n\t\t\t\t\t\tvec3d ra = na.m_r0 - rc;\n\t\t\t\t\t\tvec3d rb = nb.m_r0 - rc;\n\t\t\t\t\t\t\n\t\t\t\t\t\tdouble Ra[3] = { ra.x, ra.y, ra.z };\n\t\t\t\t\t\tdouble Rb[3] = { rb.x, rb.y, rb.z };\n\n\t\t\t\t\t\t// create the elasticity tensors\n\t\t\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tC(i, j, k, l) += K[i][k]*Rb[l]*Ra[j];\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tL(i, j, k, l, m) += K[i][k]*Rb[l]*Rb[m]*Ra[j];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tH(i, j, k, l, m) += K[i][l]*Rb[m]*Ra[j]*Ra[k];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int i=0; i<3; ++i)\n\t\t\t\t\t\t\tfor (int j=0; j<3; ++j)\n\t\t\t\t\t\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t\t\t\t\t\t\tfor (int l=0; l<3; ++l)\n\t\t\t\t\t\t\t\t\t\tfor (int m=0; m<3; ++m)\n\t\t\t\t\t\t\t\t\t\t\tfor (int n=0; n<3; ++n)\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tJ(i, j, k, l, m, n) += K[i][l]*Rb[m]*Rb[n]*Ra[j]*Ra[k];\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// divide by volume\n\tC /= m_V0;\n\tL /= m_V0;\n\tH /= 2.0*m_V0;\n\tJ /= 2.0*m_V0;\n}\n"
  },
  {
    "path": "FEBioRVE/FERVEModel2O.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEModel.h>\n#include <FECore/mat3d.h>\n#include <FECore/tens3d.h>\n#include <FECore/tens4d.h>\n#include <FECore/tens5d.h>\n#include <FECore/tens6d.h>\n\n//-----------------------------------------------------------------------------\n// Class describing the second-order RVE model.\n// This is used by the second-order homogenization code.\nclass FERVEModel2O : public FEModel\n{\npublic:\n\tenum RVE_TYPE\n\t{\n\t\tDISPLACEMENT,\t\t// prescribed displacement\n\t\tPERIODIC_LC,\t\t// periodic, linear constraints\n\t\tPERIODIC_AL\t\t\t// periodic, augmented Lagrangian (obsolete, should probably delete)\n\t};\n\npublic:\n\tFERVEModel2O();\n\t~FERVEModel2O();\n\n\t//! one time initialization\n\tbool InitRVE(int rveType, const char* szbc);\n\n\t// scale the geometry\n\tvoid ScaleGeometry(double scale);\n\npublic:\n\t//! Return the initial volume (calculated in Init)\n\tdouble InitialVolume() const { return m_V0; }\n\n\t//! periodicity flag\n\tint RVEType() const { return m_rveType; }\n\n\t//! get boundary nodes\n\tconst vector<int>& BoundaryList() const { return m_BN; }\n\nprotected:\n\t//! Calculate the initial volume\n\tvoid EvalInitialVolume();\n\n\t//! find the list of boundary nodes\n\tvoid FindBoundaryNodes(vector<int>& BN);\n\n\t//! Center the RVE\n\tvoid CenterRVE();\n\n\tbool PrepDisplacementBC();\n\tbool PrepPeriodicBC(const char* szbc);\n\tbool PrepPeriodicLC();\n\nprivate:\n\tdouble\t\t\tm_V0;\t\t\t//!< initial volume\n\tint\t\t\t\tm_rveType;\t\t//!< type of RVE\n\tFEBoundingBox\tm_bb;\t\t\t//!< bounding box of mesh\n\tvector<int>\t\tm_BN;\t\t\t//!< boundary node flags\n};\n\n//-----------------------------------------------------------------------------\n//! class representing the RVE models at the material points.\nclass FEMicroModel2O : public FEModel\n{\npublic:\n\tFEMicroModel2O();\n\t~FEMicroModel2O();\n\npublic:\n\t//! Initialize the micro model from the parent RVE model\n\tbool Init(FERVEModel2O& rve);\n\n\t//! Solve the RVE model\n\tbool Solve(const mat3d& F, const tens3drs& G);\n\n\t//! calculate average PK1 stress\n\tmat3d AveragedStressPK1(FEMaterialPoint &mp);\n\n\tvoid AveragedStress2O   (mat3d&  Pa, tens3drs&  Qa);\n//\tvoid AveragedStress2OPK1(FEMaterialPoint &mp, mat3d& PK1a, tens3drs& QK1a);\n//\tvoid AveragedStress2OPK2(FEMaterialPoint &mp, mat3ds&  Sa, tens3ds&    Ta);\n\t\n\tvoid AveragedStiffness(FEMaterialPoint &mp, tens4d& C, tens5d& L, tens5d& H, tens6d& J);\n\nprotected:\n\t//! Update the boundary conditions\n\tvoid UpdateBC(const mat3d& F, const tens3drs& G);\n\n\t//! see if node is boundary node\n\tbool IsBoundaryNode(int i) const { return ((*m_BN)[i]==1); }\n\nprotected:\n\tint\t\tm_rveType;\t\t//!< RVE type flag\n\tdouble\tm_V0;\t\t\t//!< initial RVE volume\n\tconst vector<int>*\tm_BN;\n};\n"
  },
  {
    "path": "FEBioRVE/FERVEProbe.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERVEProbe.h\"\n#include \"FEMicroMaterial.h\"\n#include <FEBioPlot/FEBioPlotFile.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEDomain.h>\n#include <FECore/log.h>\n\nFERVEProbe::FERVEProbe(FEModel* fem) : FECallBack(fem, CB_ALWAYS)\n{\n\tm_rve = nullptr;\n\tm_file = \"rve.xplt\";\n\tm_bdebug = false;\n}\n\nbool FERVEProbe::Init()\n{\n\t// make sure we have an RVE\n\tif (m_rve == nullptr) return false;\n\treturn FECallBack::Init();\n}\n\nbool FERVEProbe::Execute(FEModel& fem, int nwhen)\n{\n\tif (nwhen == CB_INIT)\t// initialize the plot file\n\t{\n\t\tassert(m_rve);\n\t\tif (m_rve == nullptr) return false;\n\n\t\t// create a plot file\n\t\tm_xplt = new FEBioPlotFile(m_rve);\n\t\tif (m_xplt->Open(m_file.c_str()) == false)\n\t\t{\n\t\t\tfeLog(\"Failed creating probe.\\n\\n\");\n\t\t\tdelete m_xplt; m_xplt = 0;\n\t\t}\n\n\t\t// write the initial state\n\t\tSave();\n\t}\n\telse if (nwhen == CB_MINOR_ITERS)\n\t{\n\t\tif (m_bdebug) Save();\n\t}\n\telse if (nwhen == CB_MAJOR_ITERS)\t// store the current state\n\t{\n\t\tSave();\n\t}\n\telse if (nwhen == CB_SOLVED)\t// clean up\n\t{\n\t\tif (m_xplt) delete m_xplt;\n\t\tm_xplt = 0;\n\t}\n\n\treturn true;\n}\n\nvoid FERVEProbe::Save()\n{\n\tassert(m_rve);\n\tif (m_rve)\n\t{\n\t\tif (m_xplt) m_xplt->Write((float)m_rve->GetCurrentTime());\n\t}\n}\n\nvoid FERVEProbe::SetRVEModel(FEModel* rve)\n{\n\tm_rve = rve;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMicroProbe, FERVEProbe)\n\tADD_PARAMETER(m_neid  , \"element_id\");\n\tADD_PARAMETER(m_ngp   , \"gausspt\"   );\n\tADD_PARAMETER(m_file  , \"file\"      );\n\tADD_PARAMETER(m_bdebug, \"debug\"     );\nEND_FECORE_CLASS();\n\nFEMicroProbe::FEMicroProbe(FEModel* fem) : FERVEProbe(fem)\n{\n\tm_neid = -1;\t// invalid element - this must be defined by user\n\tm_ngp = -1;\t\t// invalid gauss point\n}\n\nbool FEMicroProbe::Init()\n{\n\t// get the (parent) model\n\tFEModel& fem = *GetFEModel();\n\n\t// get the micro-material\n\tFEMicroMaterial* mat = dynamic_cast<FEMicroMaterial*>(GetParent());\n\tif (mat == nullptr)\n\t{\n\t\tfeLogError(\"The RVE probe must be a property of a micro-material.\");\n\t\treturn false;\n\t}\n\n\t// find the element from the ID\n\tFEMesh& mesh = fem.GetMesh();\n\tFEElement* pel = mesh.FindElementFromID(m_neid);\n\tif (pel == nullptr)\n\t{\n\t\tfeLogError(\"Invalid Element ID for micro probe %d in material %d (%s)\", m_neid, mat->GetID(), mat->GetName().c_str());\n\t\treturn false;\n\t}\n\n\t// make sure the element's parent domain has this material\n\tFEDomain* dom = dynamic_cast<FEDomain*>(pel->GetMeshPartition());\n\tif ((dom == nullptr) || (dom->GetMaterial() != mat))\n\t{\n\t\tfeLogError(\"The specified element does not belong to this material's domain.\");\n\t\treturn false;\n\t}\n\n\t// get the gauss-point\n\tint nint = pel->GaussPoints();\n\tif ((m_ngp >= 0) && (m_ngp < nint))\n\t{\n\t\tFEMaterialPoint* mp = pel->GetMaterialPoint(m_ngp);\n\t\tFEMicroMaterialPoint* mmp = mp->ExtractData<FEMicroMaterialPoint>();\n\t\tif (mmp == nullptr) return false;\n\t\tSetRVEModel(&mmp->m_rve);\n\t}\n\telse\n\t{\n\t\tfeLogError(\"Invalid gausspt number for micro-probe %d in material %d (%s)\", m_ngp, mat->GetID(), mat->GetName().c_str());\n\t\treturn false;\n\t}\n\n\n\treturn FERVEProbe::Init();\n}\n"
  },
  {
    "path": "FEBioRVE/FERVEProbe.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FECallBack.h>\n#include \"febiorve_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBioPlotFile;\nclass FEMaterialPoint;\n\n//-----------------------------------------------------------------------------\n// Base class for RVE probes\nclass FEBIORVE_API FERVEProbe : public FECallBack\n{\n\tFECORE_BASE_CLASS(FERVEProbe);\n\npublic:\n\t// The first FEModel (fem) is the macro-problem, i.e. the model that will generate the callbacks\n\t// The second FEModel (rve) is the micro-problem that needs to be tracked.\n\tFERVEProbe(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool Execute(FEModel& fem, int nwhen) override;\n\n\tvoid Save();\n\n\tvoid SetDebugFlag(bool b) { m_bdebug = b; }\n\tbool GetDebugFlag() const { return m_bdebug; }\n\n\tvoid SetRVEModel(FEModel* rve);\n\nprotected:\n\tstd::string\tm_file;\t\t\t//!< file name\n\tbool\t\tm_bdebug;\t\t//!< debug flag\n\nprivate:\n\tFEBioPlotFile* m_xplt;\t\t//!< the actual plot file\n\tFEModel* m_rve;\t\t//!< the RVE model\n};\n\n//-----------------------------------------------------------------------------\n// Probe used by FEMicroMaterial\nclass FEMicroProbe : public FERVEProbe\n{\npublic:\n\tFEMicroProbe(FEModel* fem);\n\n\tbool Init() override;\n\nprivate:\n\tint\t\t\tm_neid;\t\t\t//!< element Id\n\tint\t\t\tm_ngp;\t\t\t//!< Gauss-point (one-based!)\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEBioRVE/febiorve_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febiorve_EXPORTS\n\t\t\t#define FEBIORVE_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIORVE_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEBIORVE_API\n\t#endif\n#else\n\t#define FEBIORVE_API\n#endif\n"
  },
  {
    "path": "FEBioRVE/stdafx.h",
    "content": "#pragma once\n"
  },
  {
    "path": "FEBioTest/FEBioDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioDiagnostic.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nbool FEBioDiagnostic::Init(const char *szfile)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// read the diagnostic file\n\t// this will also create a specific diagnostic test\n\tFEDiagnosticImport im;\n\tm_pdia = im.LoadFile(fem, szfile);\n\tif (m_pdia == 0)\n\t{\n\t\tfprintf(stderr, \"Failed reading diagnostic file\\n\");\n\t\treturn false;\n\t}\n\n\t// intialize diagnostic\n\tif (m_pdia->Init() == false)\n\t{\n\t\tfprintf(stderr, \"Diagnostic initialization failed\\n\\n\");\n\t\treturn false;\n\t}\n\n\t// --- initialize FE Model data ---\n\tif (fem.Init() == false)\n\t{\n\t\tfprintf(stderr, \"FE-model data initialized has failed\\n\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioDiagnostic::Run()\n{\n\tif (m_pdia == 0) return false;\n\n\t// --- run the diagnostic ---\n\n\t// the return value will designate the pass/fail result\n\tbool bret = false;\n\ttry\n\t{\n\t\tbret = m_pdia->Run();\n\t}\n\tcatch (...)\n\t{\n\t\tfeLogError(\"Exception thrown. Aborting diagnostic.\\n\");\n\t\tbret = false;\n\t}\n\n\tif (bret) feLog(\"Diagnostic passed\\n\");\n\telse feLog(\"Diagnostic failed\\n\");\n\n\treturn bret;\n}\n"
  },
  {
    "path": "FEBioTest/FEBioDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n#include <FECore/FECoreTask.h>\n\nclass FEBioDiagnostic : public FECoreTask\n{\npublic:\n\tFEBioDiagnostic(FEModel* pfem) : FECoreTask(pfem){ m_pdia = 0; }\n\n\t//! initialization\n\tbool Init(const char* szfile);\n\n\t//! Run the FE model\n\tvirtual bool Run();\n\nprivate:\n\tFEDiagnostic*\tm_pdia;\n};\n"
  },
  {
    "path": "FEBioTest/FEBioTest.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioTest.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEBioDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FERestartDiagnostics.h\"\n#include \"FEJFNKTangentDiagnostic.h\"\n#include \"FEMaterialTest.h\"\n#include \"FEResetTest.h\"\n#include \"FEStiffnessDiagnostic.h\"\n\nnamespace FEBioTest\n{\n\nvoid InitModule()\n{\n\tREGISTER_FECORE_CLASS(FEBioDiagnostic, \"diagnose\");\n\tREGISTER_FECORE_CLASS(FERestartDiagnostic, \"restart_test\");\n\tREGISTER_FECORE_CLASS(FEQuickRestartDiagnostic, \"quick_restart_test\");\n\tREGISTER_FECORE_CLASS(FEJFNKTangentDiagnostic, \"jfnk tangent test\");\n\tREGISTER_FECORE_CLASS(FEResetTest, \"reset_test\");\n\tREGISTER_FECORE_CLASS(FEMaterialTest, \"material test\");\n\tREGISTER_FECORE_CLASS(FEStiffnessDiagnostic, \"stiffness_test\");\n}\n}\n"
  },
  {
    "path": "FEBioTest/FEBioTest.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\nnamespace FEBioTest\n{\n\tvoid InitModule();\n}\n"
  },
  {
    "path": "FEBioTest/FEBiphasicTangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBiphasicTangentDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FEBioMix/FEBiphasicSolver.h\"\n#include \"FEBioMix/FEBiphasicSolidDomain.h\"\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FELoadCurve.h>\n#include \"FECore/log.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEBiphasicTangentUniaxial, FEBiphasicScenario)\n\tADD_PARAMETER(m_strain  , \"solid_strain\"  );\n\tADD_PARAMETER(m_pressure, \"fluid_pressure\");\n\tADD_PARAMETER(m_dt      , \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEBiphasicTangentUniaxial::FEBiphasicTangentUniaxial(FEDiagnostic* pdia) : FEBiphasicScenario(pdia)\n{ \n    m_strain = 0;\n    m_pressure = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Build the uniaxial loading scenario\n// Cube with face displaced along x, with zero fluid pressure on displaced face\n// and impermeable fixed face.\nbool FEBiphasicTangentUniaxial::Init()\n{\n    int i;\n    vec3d r[8] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1)\n    };\n    \n    int BC[8][4] = {\n        {-1,-1,-1, 0},{ 0,-1,-1,-1},{ 0, 0,-1,-1}, {-1, 0,-1, 0},\n        {-1,-1, 0, 0},{ 0,-1, 0,-1},{ 0, 0, 0,-1}, {-1, 0, 0, 0}\n    };\n  \n\tFEModel& fem = *GetDiagnostic()->GetFEModel();\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_x = fem.GetDOFIndex(\"x\");\n\tconst int dof_y = fem.GetDOFIndex(\"y\");\n\tconst int dof_z = fem.GetDOFIndex(\"z\");\n\tconst int dof_p = fem.GetDOFIndex(\"p\");\n\n    // --- create the FE problem ---\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(8);\n\tm.SetDOFS(MAX_DOFS);\n\n\tFENodeSet* nset[4] = { 0 };\n\tfor (int i = 0; i < 4; ++i) nset[i] = new FENodeSet(&fem);\n\n    for (i=0; i<8; ++i)\n    {\n        FENode& n = m.Node(i);\n        n.m_rt = n.m_r0 = r[i];\n        \n        // set displacement BC's\n        if (BC[i][0] == -1) nset[0]->Add(i);\n        if (BC[i][1] == -1) nset[1]->Add(i);\n        if (BC[i][2] == -1) nset[2]->Add(i);\n        if (BC[i][3] == -1) nset[3]->Add(i);\n    }\n    \n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_x, nset[0]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_y, nset[1]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_z, nset[2]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_p, nset[3]));\n\n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    \n    // create a biphasic domain\n    FEBiphasicSolidDomain* pd = new FEBiphasicSolidDomain(&fem);\n\tpd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\tpd->SetMatID(0);\n    m.AddDomain(pd);\n    FESolidElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<8; ++i) el.m_node[i] = i;\n    \n    pd->CreateMaterialPointData();\n    \n    // convert strain to a displacement\n    double d = sqrt(2*m_strain+1) - 1;\n    \n    // Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n    fem.AddLoadController(plc);\n    \n    // Add a prescribed BC\n\tFENodeSet* dc = new FENodeSet(&fem);\n    dc->Add({1, 2, 5, 6});\n    FEPrescribedDOF* pdc = new FEPrescribedDOF(&fem, dof_x, dc);\n    pdc->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdc);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEBiphasicTangentDiagnostic::FEBiphasicTangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\tm_pscn = 0;\n\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"biphasic\");\n\n\tFEAnalysis* pstep = new FEAnalysis(fem);\n\n\t// create a new solver\n\tFESolver* pnew_solver = fecore_new<FESolver>(\"biphasic\", fem);\n\tassert(pnew_solver);\n\tpnew_solver->m_msymm = REAL_UNSYMMETRIC;\n\tpstep->SetFESolver(pnew_solver);\n\n\tfem->AddStep(pstep);\n\tfem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEBiphasicTangentDiagnostic::CreateScenario(const std::string& sname)\n{\n\tif (sname == \"biphasic uni-axial\") return (m_pscn = new FEBiphasicTangentUniaxial(this));\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEBiphasicTangentDiagnostic::Init()\n{\n\tif (m_pscn == 0) return false;\n\n\tif (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEBiphasicTangentDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n    feLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n        feLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n            feLog(\"%15lg \", m[i][j]);\n        }\n    }\n    feLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate\n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEBiphasicTangentDiagnostic::Run()\n{\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    \n    // solve the problem\n\tfem.BlockLog();\n    fem.Solve();\n\tfem.UnBlockLog();\n    \n    FEMesh& mesh = fem.GetMesh();\n    FEBiphasicSolidDomain& bd = static_cast<FEBiphasicSolidDomain&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // set up the element stiffness matrix\n    matrix k0(4*N, 4*N);\n    k0.zero();\n    bd.ElementBiphasicStiffness(el, k0, false);\n    \n    // print the element stiffness matrix\n    feLog(\"\\nActual stiffness matrix:\\n\");\n    print_matrix(k0);\n    \n    // now calculate the derivative of the residual\n    matrix k1;\n    deriv_residual(k1);\n    \n    // print the approximate element stiffness matrix\n    feLog(\"\\nApproximate stiffness matrix:\\n\");\n    print_matrix(k1);\n    \n    // finally calculate the difference matrix\n    feLog(\"\\n\");\n    matrix kd(4*N, 4*N);\n    double kmax = 0, kij;\n    int i0 = -1, j0 = -1, i, j;\n    for (i=0; i<4*N; ++i)\n        for (j=0; j<4*N; ++j)\n        {\n            kd[i][j] = k0[i][j] - k1[i][j];\n            kij = 100.0*fabs(kd[i][j] / k0[0][0]);\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    // print the difference\n    feLog(\"\\ndifference matrix:\\n\");\n    print_matrix(kd);\n    \n    feLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEBiphasicTangentDiagnostic::deriv_residual(matrix& ke)\n{\n    int i, j, nj;\n    \n    // get the solver\n\tFEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n\n\t// get the DOFs\n\tconst int dof_x = fem.GetDOFIndex(\"x\");\n\tconst int dof_y = fem.GetDOFIndex(\"y\");\n\tconst int dof_z = fem.GetDOFIndex(\"z\");\n\tconst int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    FEBiphasicSolidDomain& bd = static_cast<FEBiphasicSolidDomain&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // first calculate the initial residual\n    vector<double> f0(4*N);\n    zero(f0);\n    bd.ElementInternalForce(el, f0);\n    \n    // now calculate the perturbed residuals\n    ke.resize(4*N, 4*N);\n    ke.zero();\n    double dx = 1e-8;\n    vector<double> f1(4*N);\n    for (j=0; j<4*N; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j/4]);\n        nj = j%4;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_x, dx); node.m_rt.x += dx; break;\n            case 1: node.add(dof_y, dx); node.m_rt.y += dx; break;\n            case 2: node.add(dof_z, dx); node.m_rt.z += dx; break;\n            case 3: node.add(dof_p, dx); break;\n        }\n        \n\t\tfem.Update();\n        \n        zero(f1);\n        bd.ElementInternalForce(el, f1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_x, dx); node.m_rt.x -= dx; break;\n            case 1: node.sub(dof_y, dx); node.m_rt.y -= dx; break;\n            case 2: node.sub(dof_z, dx); node.m_rt.z -= dx; break;\n            case 3: node.sub(dof_p, dx); break;\n        }\n        \n\t\tfem.Update();\n        \n        for (i=0; i<4*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n    }\n}\n"
  },
  {
    "path": "FEBioTest/FEBiphasicTangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBiphasicScenario : public FEDiagnosticScenario\n{\npublic:\n\tFEBiphasicScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_dt = 1.0; }\n\npublic:\n\tdouble\tm_dt;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBiphasicTangentUniaxial : public FEBiphasicScenario\n{\npublic:\n\tFEBiphasicTangentUniaxial(FEDiagnostic* pdia);\n\n\tbool Init() override;\n\nprivate:\n    double\t\tm_strain;\n    double      m_pressure;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FEBiphasicTangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FEBiphasicTangentDiagnostic : public FEDiagnostic\n{\npublic:\n    FEBiphasicTangentDiagnostic(FEModel* fem);\n    virtual ~FEBiphasicTangentDiagnostic(){}\n    \n    bool Init();\n    \n    bool Run();\n\n\tFEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void deriv_residual(matrix& ke);\n    \n    void print_matrix(matrix& m);\n    \npublic:\n    FEBiphasicScenario*\tm_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEContactDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEContactDiagnostic.h\"\n#include \"FEBioMech/FENeoHookean.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FESlidingInterface.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include \"FEBioMech/FEResidualVector.h\"\n#include \"FECore/log.h\"\nusing namespace FECore;\n\nvoid FEContactDiagnostic::print_matrix(DenseMatrix& m)\n{\n\tint i, j;\n\tint N = m.Rows();\n\tint M = m.Columns();\n\n\tfeLog(\"\\n    \");\n\tfor (i=0; i<N; ++i) feLog(\"%15d \", i);\n\tfeLog(\"\\n----\");\n\tfor (i=0; i<N; ++i) feLog(\"----------------\", i);\n\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tfeLog(\"\\n%2d: \", i);\n\t\tfor (j=0; j<M; ++j)\n\t\t{\n\t\t\tfeLog(\"%15lg \", m(i,j));\n\t\t}\n\t}\n\tfeLog(\"\\n\");\n}\n\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEContactDiagnostic::FEContactDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"solid\");\n\n\tFEAnalysis* pstep = new FEAnalysis(fem);\n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\nFEContactDiagnostic::~FEContactDiagnostic()\n{\n\n}\n\nbool FEContactDiagnostic::Run()\n{\n\t// get the solver\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tFESolidSolver2& solver = static_cast<FESolidSolver2&>(*pstep->GetFESolver());\n\tsolver.Init();\n\n\t// make sure contact data is up to data\n\tfem.Update();\n\n\t// create the stiffness matrix\n\tsolver.CreateStiffness(true);\n\n\t// get the stiffness matrix\n\tFEGlobalMatrix& K = *solver.GetStiffnessMatrix();\n\tSparseMatrix *pA = (SparseMatrix*)(&K);\n\tDenseMatrix& K0 = static_cast<DenseMatrix&>(*pA);\n\n\t// we need a linear system to evaluate contact\n\tint neq = solver.m_neq;\n\tvector<double> Fd(neq, 0.0);\n\tvector<double> ui(neq, 0.0);\n\tFELinearSystem LS(&fem, K, Fd, ui, true);\n\n\t// build the stiffness matrix\n\tK0.Zero();\n\tsolver.ContactStiffness(LS);\n//\tsolver.StiffnessMatrix();\n\n\tprint_matrix(K0);\n\n\t// calculate the derivative of the residual\n\tDenseMatrix K1;\n\tderiv_residual(K1);\n\n\tprint_matrix(K1);\n\n\t// calculate difference matrix\n\tconst int N = 48;\n\tDenseMatrix Kd; Kd.Create(N, N);\n\tdouble kij, kmax = 0, k0;\n\tint i0=-1, j0=-1;\n\tfor (int i=0; i<N; ++i)\n\t\tfor (int j=0; j<N; ++j)\n\t\t{\n\t\t\tKd(i,j) = K1(i,j) - K0(i,j);\n\t\t\tk0 = fabs(K0(i,j));\n\t\t\tif (k0 < 1e-15) k0 = 1;\n\t\t\tkij = 100.0*fabs(Kd(i,j))/k0;\n\t\t\tif (kij > kmax) \n\t\t\t{\n\t\t\t\tkmax = kij;\n\t\t\t\ti0 = i;\n\t\t\t\tj0 = j;\n\t\t\t}\n\t\t}\n\n\tprint_matrix(Kd);\n\n\tfeLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n\n\treturn (kmax < 1e-3);\n}\n\n//-----------------------------------------------------------------------------\nbool FEContactDiagnostic::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// --- create the geometry ---\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\n\t// currently we simply assume a two-element contact problem\n\t// so we create two elements\n\tconst double eps = 0.5;\n\tmesh.CreateNodes(16);\n\tmesh.SetDOFS(MAX_DOFS);\n\tmesh.Node( 0).m_r0 = vec3d(0,0,0);\n\tmesh.Node( 1).m_r0 = vec3d(1,0,0);\n\tmesh.Node( 2).m_r0 = vec3d(1,1,0);\n\tmesh.Node( 3).m_r0 = vec3d(0,1,0);\n\tmesh.Node( 4).m_r0 = vec3d(0,0,1);\n\tmesh.Node( 5).m_r0 = vec3d(1,0,1);\n\tmesh.Node( 6).m_r0 = vec3d(1,1,1);\n\tmesh.Node( 7).m_r0 = vec3d(0,1,1);\n\tmesh.Node( 8).m_r0 = vec3d(0,0,1-eps);\n\tmesh.Node( 9).m_r0 = vec3d(1,0,1-eps);\n\tmesh.Node(10).m_r0 = vec3d(1,1,1-eps);\n\tmesh.Node(11).m_r0 = vec3d(0,1,1-eps);\n\tmesh.Node(12).m_r0 = vec3d(0,0,2-eps);\n\tmesh.Node(13).m_r0 = vec3d(1,0,2-eps);\n\tmesh.Node(14).m_r0 = vec3d(1,1,2-eps);\n\tmesh.Node(15).m_r0 = vec3d(0,1,2-eps);\n\n\tfor (int i=0; i<16; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.m_rt = node.m_r0;\n\t}\n\n\t// --- create a material ---\n\tFENeoHookean* pm = new FENeoHookean(&fem);\n\tpm->m_E = 1.0;\n\tpm->m_v = 0.45;\n\tfem.AddMaterial(pm);\n\n\t// get the one-and-only domain\n\tFEElasticSolidDomain* pbd = new FEElasticSolidDomain(&fem);\n\tpbd->SetMaterial(pm);\n\tpbd->Create(2, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\tpbd->SetMatID(0);\n\tmesh.AddDomain(pbd);\n\n\tFESolidElement& el0 = pbd->Element(0);\n\tFESolidElement& el1 = pbd->Element(1);\n\n\tel0.SetID(1);\n\tel0.m_node[0] = 0;\n\tel0.m_node[1] = 1;\n\tel0.m_node[2] = 2;\n\tel0.m_node[3] = 3;\n\tel0.m_node[4] = 4;\n\tel0.m_node[5] = 5;\n\tel0.m_node[6] = 6;\n\tel0.m_node[7] = 7;\n\n\tel1.SetID(2);\n\tel1.m_node[0] = 8;\n\tel1.m_node[1] = 9;\n\tel1.m_node[2] = 10;\n\tel1.m_node[3] = 11;\n\tel1.m_node[4] = 12;\n\tel1.m_node[5] = 13;\n\tel1.m_node[6] = 14;\n\tel1.m_node[7] = 15;\n\n\t// --- create the sliding interface ---\n\tFESlidingInterface* ps = new FESlidingInterface(&fem);\n\tps->m_atol = 0.1;\n\tps->m_eps = 1;\n\tps->m_btwo_pass = false;\n\tps->m_nsegup = 0;\n\tFESlidingSurface& ms = ps->m_ms;\n\tms.Create(1, FE_QUAD4NI);\n\tms.Element(0).m_node[0] = 4;\n\tms.Element(0).m_node[1] = 5;\n\tms.Element(0).m_node[2] = 6;\n\tms.Element(0).m_node[3] = 7;\n\tFESlidingSurface& ss = ps->m_ss;\n\tss.Create(1, FE_QUAD4NI);\n\tss.Element(0).m_node[0] = 11;\n\tss.Element(0).m_node[1] = 10;\n\tss.Element(0).m_node[2] = 9;\n\tss.Element(0).m_node[3] = 8;\n\tfem.AddSurfacePairConstraint(ps);\n\n\t// --- set fem data ---\n\t// Make sure we are using the LU solver\n\tFECoreKernel::GetInstance().SetDefaultSolverType(\"LU\");\n\n\treturn FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEContactDiagnostic::deriv_residual(DenseMatrix& K)\n{\n\t// get the solver\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tFESolidSolver2& solver = static_cast<FESolidSolver2&>(*pstep->GetFESolver());\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\tfem.Update();\n\n\t// first calculate the initial residual\n\tvector<double> R0; R0.assign(48, 0);\n\tvector<double> dummy(R0);\n\tFEResidualVector RHS0(fem, R0, dummy);\n\tsolver.ContactForces(RHS0);\n//\tsolver.Residual(RHS);\n\n\t// now calculate the perturbed residuals\n\tK.Create(48, 48);\n\tint i, j, nj;\n\tint N = mesh.Nodes();\n\tdouble dx = 1e-8;\n\tvector<double> R1(48);\n\tfor (j=0; j<3*N; ++j)\n\t{\n\t\tFENode& node = mesh.Node(j/3);\n\t\tnj = j%3;\n\n\t\tswitch (nj)\n\t\t{\n\t\tcase 0: node.m_rt.x += dx; break;\n\t\tcase 1: node.m_rt.y += dx; break;\n\t\tcase 2: node.m_rt.z += dx; break;\n\t\t}\n\n\t\tfem.Update();\n\n\t\tzero(R1);\n\t\tFEResidualVector RHS1(fem, R1, dummy);\n\t\tsolver.ContactForces(RHS1);\n//\t\tsolver.Residual(R1);\n\n\t\tswitch (nj)\n\t\t{\n\t\tcase 0: node.m_rt.x -= dx; break;\n\t\tcase 1: node.m_rt.y -= dx; break;\n\t\tcase 2: node.m_rt.z -= dx; break;\n\t\t}\n\n\t\tfem.Update();\n\n\t\tfor (i=0; i<3*N; ++i) K(i,j) = (1-(R1[i] - R0[i])/dx)-1;\n\t}\n}\n"
  },
  {
    "path": "FEBioTest/FEContactDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n#include <FECore/DenseMatrix.h>\n\nclass FEContactDiagnostic : public FEDiagnostic  \n{\npublic:\n\tFEContactDiagnostic(FEModel* fem);\n\tvirtual ~FEContactDiagnostic();\n\n\tbool Run();\n\n\tbool Init();\n\n\tvoid print_matrix(FECore::DenseMatrix& m);\n\nprotected:\n\tvoid deriv_residual(FECore::DenseMatrix& K);\n};\n"
  },
  {
    "path": "FEBioTest/FEContactDiagnosticBiphasic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEContactDiagnosticBiphasic.h\"\n#include \"FEBioMix/FEBiphasicSolver.h\"\n#include \"FEBioMix/FEBiphasicSolidDomain.h\"\n#include \"FEBioMix/FESlidingInterfaceBiphasic.h\"\n#include \"FEBioMech/FEResidualVector.h\"\n#include <FECore/SparseMatrix.h>\n#include <FECore/log.h>\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEContactDiagnosticBiphasic::FEContactDiagnosticBiphasic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"biphasic\");\n\n    m_pscn = 0;\n    \n    FEAnalysis* pstep = new FEAnalysis(fem);\n    \n    // create a new solver\n    FESolver* pnew_solver = fecore_new<FESolver>(\"biphasic\", fem);\n    assert(pnew_solver);\n    pnew_solver->m_msymm = REAL_UNSYMMETRIC;\n    pstep->SetFESolver(pnew_solver);\n    \n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEContactDiagnosticBiphasic::~FEContactDiagnosticBiphasic()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEContactDiagnosticBiphasic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n    feLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n        feLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n            feLog(\"%15lg \", m[i][j]);\n        }\n    }\n    feLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a sparse matrix\nvoid FEContactDiagnosticBiphasic::print_matrix(SparseMatrix& m)\n{\n    int i, j;\n\tint N = m.Rows();\n    int M = m.Columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n    feLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n        feLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n            feLog(\"%15lg \", m.get(i,j));\n        }\n    }\n    feLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEContactDiagnosticBiphasic::Init()\n{\n    if (m_pscn == 0) return false;\n    \n    if (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\nbool FEContactDiagnosticBiphasic::Run()\n{\n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    pstep->Activate();\n    FEBiphasicSolver& solver = static_cast<FEBiphasicSolver&>(*pstep->GetFESolver());\n    solver.m_msymm = REAL_UNSYMMETRIC;\n    solver.Init();\n    \n    // make sure contact data is up to data\n    fem.Update();\n    \n    // create the stiffness matrix\n    solver.CreateStiffness(true);\n    \n    // get the stiffness matrix\n    FEGlobalMatrix& K = *solver.GetStiffnessMatrix();\n    SparseMatrix& K0 = *K;\n    \n\t// we need a linear system to evaluate contact\n\tint neq = solver.m_neq;\n\tvector<double> Fd(neq, 0.0);\n\tvector<double> ui(neq, 0.0);\n\tFELinearSystem LS(&fem, K, Fd, ui, true);\n\n    // build the stiffness matrix\n    K.Zero();\n    solver.ContactStiffness(LS);\n    \n    print_matrix(K0);\n    \n    // calculate the derivative of the residual\n    matrix K1;\n    deriv_residual(K1);\n    \n    print_matrix(K1);\n    \n    // calculate difference matrix\n    int ndpn = 4;\n    int N = mesh.Nodes();\n    const int ndof = N*ndpn;\n    matrix Kd; Kd.resize(ndof, ndof);\n    double kij, kmax = 0, k0;\n    int i0=-1, j0=-1;\n    for (int i=0; i<ndof; ++i)\n        for (int j=0; j<ndof; ++j)\n        {\n            Kd(i,j) = K1(i,j) - K0.get(i,j);\n            k0 = fabs(K0.get(i,j));\n            if (k0 < 1e-15) k0 = 1;\n            kij = 100.0*fabs(Kd(i,j))/k0;\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    print_matrix(Kd);\n    \n    feLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-3);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEContactDiagnosticBiphasic::deriv_residual(matrix& K)\n{\n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    FEBiphasicSolver& solver = static_cast<FEBiphasicSolver&>(*pstep->GetFESolver());\n    \n    // get the DOFs\n    const int dof_x = fem.GetDOFIndex(\"x\");\n    const int dof_y = fem.GetDOFIndex(\"y\");\n    const int dof_z = fem.GetDOFIndex(\"z\");\n    const int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    int ndpn = 4;\n    int N = mesh.Nodes();\n    int ndof = N*ndpn;\n    \n    fem.Update();\n    \n    // first calculate the initial residual\n    vector<double> R0; R0.assign(ndof, 0);\n    vector<double> dummy(R0);\n    FEResidualVector RHS0(fem, R0, dummy);\n    solver.ContactForces(RHS0);\n    \n    // now calculate the perturbed residuals\n    K.resize(ndof,ndof);\n    int i, j, nj;\n    double dx = 1e-8;\n    vector<double> R1(ndof);\n    for (j=0; j<ndof; ++j)\n    {\n        FENode& node = mesh.Node(j/ndpn);\n        nj = j%ndpn;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_x, dx); node.m_rt.x += dx; break;\n            case 1: node.add(dof_y, dx); node.m_rt.y += dx; break;\n            case 2: node.add(dof_z, dx); node.m_rt.z += dx; break;\n            case 3: node.add(dof_p, dx); break;\n        }\n        \n        fem.Update();\n        \n        zero(R1);\n        FEResidualVector RHS1(fem, R1, dummy);\n        solver.ContactForces(RHS1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_x, dx); node.m_rt.x -= dx; break;\n            case 1: node.sub(dof_y, dx); node.m_rt.y -= dx; break;\n            case 2: node.sub(dof_z, dx); node.m_rt.z -= dx; break;\n            case 3: node.sub(dof_p, dx); break;\n        }\n        \n\t\tfem.Update();\n\n        for (i=0; i<ndof; ++i) K(i,j) = (R0[i] - R1[i])/dx;\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEContactDiagnosticBiphasic::CreateScenario(const std::string& sname)\n{\n    if (sname == \"hex8\") return (m_pscn = new FEContactBiphasicTangentHex8(this));\n    else if (sname == \"hex20\") return (m_pscn = new FEContactBiphasicTangentHex20(this));\n    return 0;\n}\n\n//////////////////////////////////////////////////////////////////////\n// Biphasic Contact Tangent Diagnostic for hex8 Elements\n//////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEContactBiphasicTangentHex8, FEContactBiphasicScenario)\n\tADD_PARAMETER(m_dt, \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEContactBiphasicTangentHex8::FEContactBiphasicTangentHex8(FEDiagnostic* pdia) : FEContactBiphasicScenario(pdia)\n{\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEContactBiphasicTangentHex8::Init()\n{\n    vec3d r[16] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1),\n        vec3d(0,0,2), vec3d(1,0,2), vec3d(1,1,2), vec3d(0,1,2)\n    };\n    \n    double p[16] = {\n        1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // --- create the geometry ---\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n    \n    // get the DOFs\n    const int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // currently we simply assume a two-element contact problem\n    // so we create two elements\n    const double eps = 0.5;\n    mesh.CreateNodes(16);\n    mesh.SetDOFS(MAX_DOFS);\n    vec3d dr(0,0,eps);\n    for (int i=0; i<8; ++i) {\n        mesh.Node(i).m_r0 = r[i];\n        mesh.Node(i).set(dof_p, p[i]);\n    }\n    for (int i=8; i<16; ++i) {\n        mesh.Node(i).m_r0 = r[i] - dr;\n        mesh.Node(i).set(dof_p, p[i]+eps);\n    }\n    \n    for (int i=0; i<16; ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.m_rt = node.m_r0;\n    }\n    \n    // get the material\n    FEMaterial* pm = fem.GetMaterial(0);\n    \n    // get the one-and-only domain\n    FEBiphasicSolidDomain* pbd = new FEBiphasicSolidDomain(&fem);\n    pbd->SetMaterial(pm);\n    pbd->Create(2, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\tpbd->SetMatID(0);\n    mesh.AddDomain(pbd);\n    \n    FESolidElement& el0 = pbd->Element(0);\n    FESolidElement& el1 = pbd->Element(1);\n    \n    el0.SetID(1);\n    el0.m_node[0] = 0;\n    el0.m_node[1] = 1;\n    el0.m_node[2] = 2;\n    el0.m_node[3] = 3;\n    el0.m_node[4] = 4;\n    el0.m_node[5] = 5;\n    el0.m_node[6] = 6;\n    el0.m_node[7] = 7;\n    \n    el1.SetID(2);\n    el1.m_node[0] = 8;\n    el1.m_node[1] = 9;\n    el1.m_node[2] = 10;\n    el1.m_node[3] = 11;\n    el1.m_node[4] = 12;\n    el1.m_node[5] = 13;\n    el1.m_node[6] = 14;\n    el1.m_node[7] = 15;\n    \n    pbd->CreateMaterialPointData();\n    \n    // --- create the sliding interface ---\n    FESlidingInterfaceBiphasic* ps = new FESlidingInterfaceBiphasic(&fem);\n    ps->m_atol = 0.1;\n    ps->m_epsn = 1;\n    ps->m_epsp = 1;\n    ps->m_btwo_pass = false;\n    ps->m_nsegup = 0;\n    ps->m_bautopen = true;\n    ps->m_bsymm = false;\n    ps->m_stol = 0.01;\n    FESlidingSurfaceBiphasic& ms = ps->m_ms;\n    ms.Create(1, FE_QUAD4G4);\n    ms.Element(0).m_node[0] = 4;\n    ms.Element(0).m_node[1] = 5;\n    ms.Element(0).m_node[2] = 6;\n    ms.Element(0).m_node[3] = 7;\n    FESlidingSurfaceBiphasic& ss = ps->m_ss;\n    ss.Create(1, FE_QUAD4G4);\n    ss.Element(0).m_node[0] = 11;\n    ss.Element(0).m_node[1] = 10;\n    ss.Element(0).m_node[2] = 9;\n    ss.Element(0).m_node[3] = 8;\n    fem.AddSurfacePairConstraint(ps);\n    \n    return true;\n}\n\n//////////////////////////////////////////////////////////////////////\n// Biphasic Contact Tangent Diagnostic for hex20 Elements\n//////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEContactBiphasicTangentHex20, FEContactBiphasicScenario)\n\tADD_PARAMETER(m_dt, \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEContactBiphasicTangentHex20::FEContactBiphasicTangentHex20(FEDiagnostic* pdia) : FEContactBiphasicScenario(pdia)\n{\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEContactBiphasicTangentHex20::Init()\n{\n    vec3d r[40] = {\n        vec3d(0,0,0), vec3d(0,0,0.5), vec3d(0,0,1), vec3d(0,0.5,0),\n        vec3d(0,0.5,1), vec3d(0,1,0), vec3d(0,1,0.5), vec3d(0,1,1),\n        vec3d(0.5,0,0), vec3d(0.5,0,1), vec3d(0.5,1,0), vec3d(0.5,1,1),\n        vec3d(1,0,0), vec3d(1,0,0.5), vec3d(1,0,1), vec3d(1,0.5,0),\n        vec3d(1,0.5,1), vec3d(1,1,0), vec3d(1,1,0.5), vec3d(1,1,1),\n        vec3d(0,0,1), vec3d(0,0,1.5), vec3d(0,0,2), vec3d(0,0.5,1),\n        vec3d(0,0.5,2), vec3d(0,1,1), vec3d(0,1,1.5), vec3d(0,1,2),\n        vec3d(0.5,0,1), vec3d(0.5,0,2), vec3d(0.5,1,1), vec3d(0.5,1,2),\n        vec3d(1,0,1), vec3d(1,0,1.5), vec3d(1,0,2), vec3d(1,0.5,1),\n        vec3d(1,0.5,2), vec3d(1,1,1), vec3d(1,1,1.5), vec3d(1,1,2),\n    };\n    \n    double p[40] = {\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n    };\n    \n    int el0n[20] = {\n        1,    13,    18,     6,     3,    15,    20,     8,     9,    16,\n        11,     4,    10,    17,    12,     5,     2,    14,    19,     7\n    };\n    \n    int el1n[20] = {\n        21,    33,    38,    26,    23,    35,    40,    28,    29,    36,\n        31,    24,    30,    37,    32,    25,    22,    34,    39,    27\n    };\n    \n    int msn[8] = {\n        21,    26,    38,    33,    24,    31,    36,    29\n    };\n    \n    int ssn[9] = {\n        3,    15,    20,     8,    10,    17,    12,     5\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // --- create the geometry ---\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n    \n    // get the DOFs\n    const int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // currently we simply assume a two-element contact problem\n    // so we create two elements\n    const double eps = 0.5;\n    mesh.CreateNodes(40);\n    mesh.SetDOFS(MAX_DOFS);\n    vec3d dr(0,0,eps);\n    for (int i=0; i<20; ++i) {\n        mesh.Node(i).m_r0 = r[i];\n        mesh.Node(i).set(dof_p, p[i]);\n    }\n    for (int i=20; i<40; ++i) {\n        mesh.Node(i).m_r0 = r[i] - dr;\n        mesh.Node(i).set(dof_p, p[i]+eps);\n    }\n    \n    for (int i=0; i<40; ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.m_rt = node.m_r0;\n    }\n    \n    // get the material\n    FEMaterial* pm = fem.GetMaterial(0);\n    \n    // get the one-and-only domain\n    FEBiphasicSolidDomain* pbd = new FEBiphasicSolidDomain(&fem);\n    pbd->SetMaterial(pm);\n    pbd->Create(2, FEElementLibrary::GetElementSpecFromType(FE_HEX20G27));\n\tpbd->SetMatID(0);\n    mesh.AddDomain(pbd);\n    \n    FESolidElement& el0 = pbd->Element(0);\n    FESolidElement& el1 = pbd->Element(1);\n    \n    el0.SetID(1);\n    for (int i=0; i<20; ++i) {\n        el0.m_node[i] = el0n[i] - 1;\n    }\n    \n    el1.SetID(2);\n    for (int i=0; i<20; ++i) {\n        el1.m_node[i] = el1n[i] - 1;\n    }\n    \n    pbd->CreateMaterialPointData();\n    \n    // --- create the sliding interface ---\n    FESlidingInterfaceBiphasic* ps = new FESlidingInterfaceBiphasic(&fem);\n    ps->m_atol = 0.1;\n    ps->m_epsn = 1;\n    ps->m_epsp = 1;\n    ps->m_btwo_pass = false;\n    ps->m_nsegup = 0;\n    ps->m_bautopen = true;\n    ps->m_bsymm = false;\n    FESlidingSurfaceBiphasic& ms = ps->m_ms;\n    ms.Create(1, FE_QUAD8G9);\n    for (int i=0; i<8; ++i)\n        ms.Element(0).m_node[i] = msn[i] - 1;\n    FESlidingSurfaceBiphasic& ss = ps->m_ss;\n    ss.Create(1, FE_QUAD8G9);\n    for (int i=0; i<8; ++i)\n        ss.Element(0).m_node[i] = ssn[i] - 1;\n    fem.AddSurfacePairConstraint(ps);\n    \n    return true;\n}\n"
  },
  {
    "path": "FEBioTest/FEContactDiagnosticBiphasic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\nclass SparseMatrix;\n\n//-----------------------------------------------------------------------------\nclass FEContactBiphasicScenario : public FEDiagnosticScenario\n{\npublic:\n    FEContactBiphasicScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_dt = 1.0; }\n    \npublic:\n    double\tm_dt;\n};\n\n//-----------------------------------------------------------------------------\nclass FEContactBiphasicTangentHex8 : public FEContactBiphasicScenario\n{\npublic:\n    FEContactBiphasicTangentHex8(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEContactBiphasicTangentHex20 : public FEContactBiphasicScenario\n{\npublic:\n    FEContactBiphasicTangentHex20(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEContactDiagnosticBiphasic : public FEDiagnostic\n{\npublic:\n    FEContactDiagnosticBiphasic(FEModel* fem);\n    ~FEContactDiagnosticBiphasic();\n    \n    bool Run();\n    \n    bool Init();\n    \n    FEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void print_matrix(matrix& m);\n    void print_matrix(SparseMatrix& m);\n    \n    void deriv_residual(matrix& K);\n    \npublic:\n    FEContactBiphasicScenario*  m_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FEEASShellTangentDiagnostic.h\"\n#include \"FEContactDiagnostic.h\"\n#include \"FEPrintMatrixDiagnostic.h\"\n#include \"FEPrintHBMatrixDiagnostic.h\"\n#include \"FEMemoryDiagnostic.h\"\n#include \"FEBiphasicTangentDiagnostic.h\"\n#include \"FETiedBiphasicDiagnostic.h\"\n#include \"FEMultiphasicTangentDiagnostic.h\"\n#include \"FEFluidTangentDiagnostic.h\"\n#include \"FEFluidFSITangentDiagnostic.h\"\n#include \"FEPolarFluidTangentDiagnostic.h\"\n#include \"FEContactDiagnosticBiphasic.h\"\n#include \"FEMaterialTest.h\"\n#include \"FECore/log.h\"\n#include \"FEBioXML/FEBioControlSection.h\"\n#include \"FEBioXML/FEBioMaterialSection.h\"\n#include \"FEBioXML/FEBioGlobalsSection.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FECore/FESolver.h\"\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEDiagnostic::FEDiagnostic(FEModel* fem) : FECoreClass(fem)\n{\n\n}\n\nFEDiagnostic::~FEDiagnostic()\n{\n\n}\n\nvoid FEDiagnostic::SetFileName(const std::string& fileName)\n{\n\tm_file = fileName;\n}\n\nconst std::string& FEDiagnostic::GetFileName()\n{\n\treturn m_file;\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnostic* FEDiagnosticImport::LoadFile(FEModel& fem, const char* szfile)\n{\n\tm_pdia = 0;\n\n\tm_builder = new FEModelBuilder(fem);\n\n\t// Open the XML file\n\tXMLReader xml;\n\tif (xml.Open(szfile) == false) \n\t{\n\t\terrf(\"FATAL ERROR: Failed opening input file %s\\n\\n\", szfile);\n\t\treturn 0;\n\t}\n\n\t// define file structure\n\tm_map.clear();\n\tm_map[\"Control\" ] = new FEDiagnosticControlSection (this);\n\tm_map[\"Material\"] = new FEBioMaterialSection       (this);\n\tm_map[\"Scenario\"] = new FEDiagnosticScenarioSection(this);\n    m_map[\"Globals\" ] = new FEBioGlobalsSection        (this);\n\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\n\t// loop over all child tags\n\ttry\n\t{\n\t\t// Find the root element\n\t\tXMLTag tag;\n\t\tif (xml.FindTag(\"febio_diagnostic\", tag) == false) return 0;\n\n\t\tXMLAtt& att = tag.m_att[0];\n        if      (att == \"tangent test\"            ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FETangentDiagnostic           (&fem); }\n        else if (att == \"shell tangent test\"      ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FEEASShellTangentDiagnostic   (&fem); }\n        else if (att == \"contact test\"            ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FEContactDiagnostic           (&fem); }\n        else if (att == \"print matrix\"            ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FEPrintMatrixDiagnostic       (&fem); }\n        else if (att == \"print hbmatrix\"          ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FEPrintHBMatrixDiagnostic     (&fem); }\n        else if (att == \"memory test\"             ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FEMemoryDiagnostic            (&fem); }\n        else if (att == \"biphasic tangent test\"   ) { fecore.SetActiveModule(\"biphasic\"   ); m_pdia = new FEBiphasicTangentDiagnostic   (&fem); }\n        else if (att == \"biphasic contact test\"   ) { fecore.SetActiveModule(\"biphasic\"   ); m_pdia = new FEContactDiagnosticBiphasic   (&fem); }\n        else if (att == \"tied biphasic test\"      ) { fecore.SetActiveModule(\"biphasic\"   ); m_pdia = new FETiedBiphasicDiagnostic      (&fem); }\n        else if (att == \"multiphasic tangent test\") { fecore.SetActiveModule(\"multiphasic\"); m_pdia = new FEMultiphasicTangentDiagnostic(&fem); }\n        else if (att == \"fluid tangent test\"      ) { fecore.SetActiveModule(\"fluid\"      ); m_pdia = new FEFluidTangentDiagnostic      (&fem); }\n        else if (att == \"fluid-FSI tangent test\"  ) { fecore.SetActiveModule(\"fluid-FSI\"  ); m_pdia = new FEFluidFSITangentDiagnostic   (&fem); }\n        else if (att == \"polar fluid tangent test\") { fecore.SetActiveModule(\"polar fluid\"); m_pdia = new FEPolarFluidTangentDiagnostic (&fem); }\n        else if (att == \"material test\"           ) { fecore.SetActiveModule(\"solid\"      ); m_pdia = new FEMaterialTest                (&fem); }\n\t\telse\n\t\t{\n\t\t\tfeLog(\"\\nERROR: unknown diagnostic\\n\\n\");\n\t\t\treturn 0;\n\t\t}\n\n        // keep a pointer to the fem object\n\n\t\tfem.SetCurrentStepIndex(0);\n        \n\t\t// parse the file\n\t\tif (ParseFile(tag) == false) return nullptr;\n\t}\n\tcatch (XMLReader::Error& e)\n\t{\n\t\tfeLog(\"FATAL ERROR: %s\\n\", e.what());\n\t\treturn 0;\n\t}\n\tcatch (FEFileException& e)\n\t{\n\t\tfeLog(\"FATAL ERROR: %s (line %d)\\n\", e.GetErrorString(), xml.GetCurrentLine());\n\t\treturn 0;\n\t}\n\tcatch (...)\n\t{\n\t\tfeLog(\"FATAL ERROR: unrecoverable error (line %d)\\n\", xml.GetCurrentLine());\n\t\treturn 0;\n\t}\n\n\t// close the XML file\n\txml.Close();\n\n\tif (m_pdia) m_pdia->SetFileName(szfile);\n\n\t// we're done!\n\treturn m_pdia;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiagnosticControlSection::Parse(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"time_steps\") tag.value(pstep->m_ntime);\n\t\telse if (tag == \"step_size\") { tag.value(pstep->m_dt0); fem.GetTime().timeIncrement = pstep->m_dt0; }\n\t\telse throw XMLReader::InvalidValue(tag);\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiagnosticScenarioSection::Parse(XMLTag &tag)\n{\n\tFEDiagnosticImport& dim = static_cast<FEDiagnosticImport&>(*GetFileReader());\n\n\t// get the diagnostic\n\tFEDiagnostic* pdia = dim.m_pdia;\n\n\t// find the type attribute\n\tXMLAtt& type = tag.Attribute(\"type\");\n\n\t// create the scenario\n\tFEDiagnosticScenario* pscn = pdia->CreateScenario(type.cvalue());\n\tif (pscn == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", type.cvalue());\n\n\t// parse the parameter list\n\tFEParameterList& pl = pscn->GetParameterList();\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false) throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioTest/FEDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECore/FEModel.h\"\n#include \"FEBioXML/FEBioImport.h\"\n\nclass FEDiagnostic;\n\n//-----------------------------------------------------------------------------\nclass FEDiagnosticScenario : public FEParamContainer\n{\npublic:\n\tFEDiagnosticScenario(FEDiagnostic* pdia) : m_pdia(pdia) {};\n\n\tFEDiagnostic* GetDiagnostic() { return m_pdia; }\n\n\tvirtual bool Init() { return true; }\n\nprivate:\n\tFEDiagnostic* m_pdia;\n};\n\n//-----------------------------------------------------------------------------\n//! The FEDiagnostic class is a base class that can be used to create\n//! diagnostic classes to test FEBio's performance.\n\nclass FEDiagnostic : public FECoreClass\n{\npublic:\n\tFECORE_BASE_CLASS(FEDiagnostic)\n\npublic:\n\t//! constructor\n\tFEDiagnostic(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FEDiagnostic();\n\n\t//! initialization\n\tvirtual bool Init() { return true; }\n\n\t//! run the diagnostic. Returns true on pass, false on failure\n\tvirtual bool Run() = 0;\n\n\t//! load data from file\n\tvirtual bool ParseSection(XMLTag& tag) { return false; }\n\n\t//! create a scenario class\n\tvirtual FEDiagnosticScenario* CreateScenario(const std::string& sname) { return 0; }\n\n\tvoid SetFileName(const std::string& fileName);\n\tconst std::string& GetFileName();\n\nprivate:\n\tstd::string\tm_file;\t//!< the input file used\n};\n\n//-----------------------------------------------------------------------------\n// Control Section\nclass FEDiagnosticControlSection : public FEFileSection\n{\npublic:\n\tFEDiagnosticControlSection(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Scenario Section parser\nclass FEDiagnosticScenarioSection : public FEFileSection\n{\npublic:\n\tFEDiagnosticScenarioSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n//! The FEDiagnosticImport class creates a specific diagnostic test. Currently\n//! the only way to create a diagnostic is to load a diagnostic from file\n\nclass FEDiagnosticImport : public FEFileImport\n{\npublic:\n\tFEDiagnostic* LoadFile(FEModel& fem, const char* szfile);\n\nprotected:\n\tFEDiagnostic* m_pdia;\n\n\tfriend class FEDiagnosticScenarioSection;\n};\n"
  },
  {
    "path": "FEBioTest/FEEASShellTangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEASShellTangentDiagnostic.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FEElasticEASShellDomain.h\"\n#include \"FECore/log.h\"\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEEASShellTangentUnloaded, FEDiagnosticScenario)\n\tADD_PARAMETER(m_strain, \"strain\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FEEASShellTangentUnloaded::Init()\n{\n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    const int NELN = 4;\n    \n    int i;\n    vec3d r[NELN] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0)\n    };\n    \n    vec3d D[NELN] = {\n        vec3d(0,0,0.01), vec3d(0,0,0.01), vec3d(0,0,0.01), vec3d(0,0,0.01)\n    };\n\n    \n    // get the degrees of freedom\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\n    // --- create the FE problem ---\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(NELN);\n    m.SetDOFS(MAX_DOFS);\n    for (i=0; i<NELN; ++i)\n    {\n        FENode& n = m.Node(i);\n        n.m_rt = n.m_r0 = r[i];\n        n.m_dt = n.m_d0 = D[i];\n    }\n    \n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    \n    // create a solid domain\n    FEElasticEASShellDomain* pd = new FEElasticEASShellDomain(&fem);\n    pd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_SHELL_QUAD4G8));\n    pd->SetMatID(0);\n    m.AddDomain(pd);\n    FEShellElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<NELN; ++i) {\n        el.m_node[i] = i;\n        el.m_h0[i] = D[i].norm();\n    }\n    \n    pd->CreateMaterialPointData();\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEEASShellTangentDiagnostic::FEEASShellTangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"solid\");\n\n    m_pscn = 0;\n    \n    // create an analysis step\n    FEAnalysis* pstep = new FEAnalysis(fem);\n    \n    // create a new solver\n    FESolver* pnew_solver = fecore_new<FESolver>(\"solid\", fem);\n    assert(pnew_solver);\n    pstep->SetFESolver(pnew_solver);\n    \n    // keep a pointer to the fem object\n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEEASShellTangentDiagnostic::CreateScenario(const std::string& sname)\n{\n    if (sname == \"unloaded\"   ) return (m_pscn = new FEEASShellTangentUnloaded   (this));\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEEASShellTangentDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n    feLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n        feLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n            feLog(\"%15lg \", m[i][j]);\n        }\n    }\n    feLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEEASShellTangentDiagnostic::Init()\n{\n    // make sure we have a scenario\n    if (m_pscn == 0) return false;\n    \n    // initialize the scenario\n    if (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate\n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEEASShellTangentDiagnostic::Run()\n{\n    // solve the problem\n    FEModel& fem = *GetFEModel();\n\tfem.BlockLog();\n    bool bret = fem.Solve();\n\tfem.UnBlockLog();\n    if (bret == false) return false;\n    \n    FEMesh& mesh = fem.GetMesh();\n    FEElasticEASShellDomain& bd = static_cast<FEElasticEASShellDomain&>(mesh.Domain(0));\n    \n    // set up the element stiffness matrix\n    const int NELN = 4;\n    const int NDPN = 6;\n    const int NDOF = NELN*NDPN;\n    matrix k0(NDOF, NDOF);\n    k0.zero();\n    bd.ElementStiffness(0, k0);\n    \n    // print the element stiffness matrix\n    feLog(\"\\nActual stiffness matrix:\\n\");\n    print_matrix(k0);\n    \n    // now calculate the derivative of the residual\n    matrix k1;\n    deriv_residual(k1);\n    \n    // print the approximate element stiffness matrix\n    feLog(\"\\nApproximate stiffness matrix:\\n\");\n    print_matrix(k1);\n    \n    // finally calculate the difference matrix\n    feLog(\"\\n\");\n    matrix kd(NDOF, NDOF);\n    double kmax = 0, kij;\n    int i0 = -1, j0 = -1, i, j;\n    for (i=0; i<NDOF; ++i)\n        for (j=0; j<NDOF; ++j)\n        {\n            kd[i][j] = k0[i][j] - k1[i][j];\n            kij = 100.0*fabs(kd[i][j] / k0[0][0]);\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    // print the difference\n    feLog(\"\\ndifference matrix:\\n\");\n    print_matrix(kd);\n    \n    feLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEEASShellTangentDiagnostic::deriv_residual(matrix& ke)\n{\n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    FESolidSolver2& solver = static_cast<FESolidSolver2&>(*pstep->GetFESolver());\n    \n    // get the degrees of freedom\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_SX = fem.GetDOFIndex(\"sx\");\n    const int dof_SY = fem.GetDOFIndex(\"sy\");\n    const int dof_SZ = fem.GetDOFIndex(\"sz\");\n\n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    FEElasticEASShellDomain& bd = static_cast<FEElasticEASShellDomain&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FEShellElementNew& el = bd.ShellElement(0);\n    \n    // first calculate the initial residual\n    const int NELN = 4;\n    const int NDPN = 6;\n    const int NDOF = NELN*NDPN;\n    vector<double> f0(NDOF);\n    zero(f0);\n    bd.ElementInternalForce(el, f0);\n    \n    // now calculate the perturbed residuals\n    ke.resize(NDOF, NDOF);\n    ke.zero();\n    int i, j, nj;\n    int N = mesh.Nodes();\n    double dx = 1e-8;\n    vector<double> f1(NDOF);\n    vector<double> ui(NDOF,0);\n    for (j=0; j<NDPN*N; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j/NDPN]);\n        nj = j%NDPN;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_X, dx); node.m_rt.x += dx; break;\n            case 1: node.add(dof_Y, dx); node.m_rt.y += dx; break;\n            case 2: node.add(dof_Z, dx); node.m_rt.z += dx; break;\n            case 3: node.add(dof_SX, dx); break;\n            case 4: node.add(dof_SY, dx); break;\n            case 5: node.add(dof_SZ, dx); break;\n        }\n        ui[j] += dx;\n        \n        solver.Update(ui);\n\n        zero(f1);\n        bd.ElementInternalForce(el, f1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_X, dx); node.m_rt.x -= dx; break;\n            case 1: node.sub(dof_Y, dx); node.m_rt.y -= dx; break;\n            case 2: node.sub(dof_Z, dx); node.m_rt.z -= dx; break;\n            case 3: node.sub(dof_SX, dx); break;\n            case 4: node.sub(dof_SY, dx); break;\n            case 5: node.sub(dof_SZ, dx); break;\n        }\n        ui[j] -= dx;\n\n        solver.Update(ui);\n\n        for (i=0; i<NDPN*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n    }\n}\n"
  },
  {
    "path": "FEBioTest/FEEASShellTangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEEASShellTangentUnloaded : public FEDiagnosticScenario\n{\npublic:\n    FEEASShellTangentUnloaded(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_strain = 0.0; }\n    \n    bool Init() override;\n    \nprivate:\n    double    m_strain;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FETangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FEEASShellTangentDiagnostic : public FEDiagnostic\n{\npublic:\n    FEEASShellTangentDiagnostic(FEModel* fem);\n    virtual ~FEEASShellTangentDiagnostic(){}\n    \n    FEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \n    bool Init();\n    \n    bool Run();\n    \nprotected:\n    void deriv_residual(matrix& ke);\n    void print_matrix(matrix& m);\npublic:\n    FEDiagnosticScenario* m_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEFluidFSITangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidFSITangentDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FEBioLib/FEBox.h\"\n#include \"FEBioFluid/FEFluidFSISolver.h\"\n#include \"FEBioFluid/FEFluidFSIDomain3D.h\"\n#include \"FEBioFluid/FEFluidFSIAnalysis.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEFixedBC.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidFSITangentUniaxial, FEFluidFSIScenario)\n\tADD_PARAMETER(m_dilation, \"fluid_dilation\");\n\tADD_PARAMETER(m_dt      , \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFluidFSITangentUniaxial::FEFluidFSITangentUniaxial(FEDiagnostic* pdia) : FEFluidFSIScenario(pdia)\n{\n    m_dilation = 0;\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Build the uniaxial loading scenario\n// Cube with uniaxial velocity prescribed along x on left face and dilatation\n// fixed on right face. Dynamic analysis.\nbool FEFluidFSITangentUniaxial::Init()\n{\n    int i;\n    vec3d r[8] = {\n        vec3d(-5.0000000e-01, -5.0000000e-01,  0.0000000e+00),\n        vec3d( 5.0000000e-01, -5.0000000e-01,  0.0000000e+00),\n        vec3d( 5.0000000e-01,  5.0000000e-01,  0.0000000e+00),\n        vec3d(-5.0000000e-01,  5.0000000e-01,  0.0000000e+00),\n        vec3d(-5.0000000e-01, -5.0000000e-01,  1.0000000e+00),\n        vec3d( 5.0000000e-01, -5.0000000e-01,  1.0000000e+00),\n        vec3d( 5.0000000e-01,  5.0000000e-01,  1.0000000e+00),\n        vec3d(-5.0000000e-01,  5.0000000e-01,  1.0000000e+00)\n    };\n    \n    int BC[8][7] = {\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0},\n        { 0, 0, 0, 0, 0, 0, 0}\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    pstep->m_nanalysis = FEFluidFSIAnalysis::DYNAMIC;\n//    pstep->m_nanalysis = FEFluidFSIAnalysis::STEADY_STATE;\n    \n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_WX = fem.GetDOFIndex(\"wx\");\n    const int dof_WY = fem.GetDOFIndex(\"wy\");\n    const int dof_WZ = fem.GetDOFIndex(\"wz\");\n    const int dof_EF  = fem.GetDOFIndex(\"ef\");\n    \n    // --- create the FE problem ---\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(8);\n    m.SetDOFS(MAX_DOFS);\n\n\tFENodeSet* nset[7] = { 0 };\n\tfor (int i = 0; i < 7; ++i) nset[i] = new FENodeSet(&fem);\n    for (i=0; i<8; ++i)\n    {\n        FENode& n = m.Node(i);\n        n.m_rt = n.m_r0 = r[i];\n        \n        // set displacement BC's\n        if (BC[i][0] == -1) nset[0]->Add(i);\n        if (BC[i][1] == -1) nset[1]->Add(i);\n        if (BC[i][2] == -1) nset[2]->Add(i);\n        if (BC[i][3] == -1) nset[3]->Add(i);\n        if (BC[i][4] == -1) nset[4]->Add(i);\n        if (BC[i][5] == -1) nset[5]->Add(i);\n        if (BC[i][6] == -1) nset[6]->Add(i);\n    }\n    \n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_X , nset[0]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_Y , nset[1]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_Z , nset[2]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WX, nset[3]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WY, nset[4]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WZ, nset[5]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_EF, nset[6]));\n\n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    \n    // create a fluid domain\n    FEFluidFSIDomain3D* pd = new FEFluidFSIDomain3D(&fem);\n    pd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n    pd->SetMatID(0);\n    m.AddDomain(pd);\n    FESolidElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<8; ++i) el.m_node[i] = i;\n    \n    pd->CreateMaterialPointData();\n    \n    // Add a loadcurve\n/*    FELoadCurve* plc = new FELoadCurve(new FELinearFunction(&fem, 1.0, 0.0));\n    fem.AddLoadCurve(plc);\n    \n    // Add a prescribed BC\n    int nd[4] = {0, 4, 3, 7};\n    FEPrescribedDOF* pdc = new FEPrescribedDOF(&fem);\n    fem.AddPrescribedBC(pdc);\n    pdc->SetDOF(dof_EF).SetScale(m_dilation, 0);\n    for (i = 0; i<4; ++i) pdc->AddNode(nd[i]);*/\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEFluidFSITangentDiagnostic::FEFluidFSITangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n    m_pscn = 0;\n\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"fluid-FSI\");\n    \n    FEAnalysis* pstep = new FEAnalysis(fem);\n    \n    // create a new solver\n    FESolver* pnew_solver = fecore_new<FESolver>(\"fluid-FSI\", fem);\n    assert(pnew_solver);\n    pnew_solver->m_msymm = REAL_UNSYMMETRIC;\n    FEFluidFSISolver* fluid_solver = dynamic_cast<FEFluidFSISolver*>(pnew_solver);\n    fluid_solver->m_rhoi = 0;\n    fluid_solver->m_pred = 0;\n    pstep->SetFESolver(pnew_solver);\n    \n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEFluidFSITangentDiagnostic::CreateScenario(const std::string& sname)\n{\n    if (sname == \"fluid uni-axial\") return (m_pscn = new FEFluidFSITangentUniaxial(this));\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEFluidFSITangentDiagnostic::Init()\n{\n    if (m_pscn == 0) return false;\n    \n    if (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEFluidFSITangentDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n    feLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n        feLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n            feLog(\"%15lg \", m[i][j]);\n        }\n    }\n    feLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate\n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEFluidFSITangentDiagnostic::Run()\n{\n    // solve the problem\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    pstep->Activate();\n\tfem.BlockLog();\n    fem.Solve();\n\tfem.UnBlockLog();\n    FETimeInfo tp;\n    tp.timeIncrement = dt;\n    tp.alpha = 1;\n    tp.beta = 0.25;\n    tp.gamma = 0.5;\n    tp.alphaf = 1;\n    tp.alpham = 1;\n    \n    FEMesh& mesh = fem.GetMesh();\n    FEFluidFSIDomain3D& bd = static_cast<FEFluidFSIDomain3D&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // set up the element stiffness matrix\n    const int ndpn = 7;\n    matrix k0(ndpn*N, ndpn*N);\n    k0.zero();\n    bd.ElementStiffness(el, k0);\n    bd.ElementMassMatrix(el,k0);\n    \n    // print the element stiffness matrix\n    feLog(\"\\nActual stiffness matrix:\\n\");\n    print_matrix(k0);\n    \n    // now calculate the derivative of the residual\n    matrix k1;\n    deriv_residual(k1);\n    \n    // print the approximate element stiffness matrix\n    feLog(\"\\nApproximate stiffness matrix:\\n\");\n    print_matrix(k1);\n    \n    // finally calculate the difference matrix\n    feLog(\"\\n\");\n    matrix kd(ndpn*N, ndpn*N);\n    double kmax = 0, kij;\n    int i0 = -1, j0 = -1, i, j;\n    for (i=0; i<ndpn*N; ++i)\n        for (j=0; j<ndpn*N; ++j)\n        {\n            kd[i][j] = k0[i][j] - k1[i][j];\n            kij = 100.0*fabs(kd[i][j] / k0[0][0]);\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    // print the difference\n    feLog(\"\\ndifference matrix:\\n\");\n    print_matrix(kd);\n    \n    feLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEFluidFSITangentDiagnostic::deriv_residual(matrix& ke)\n{\n    int i, j, nj;\n    \n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    FEFluidFSISolver& solver = static_cast<FEFluidFSISolver&>(*pstep->GetFESolver());\n    FETimeInfo& tp = fem.GetTime();\n    tp.alpha = solver.m_alphaf;\n    tp.beta  = solver.m_beta;\n    tp.gamma = solver.m_gamma;\n    tp.alphaf = solver.m_alphaf;\n    tp.alpham  = solver.m_alpham;\n    \n    // get the dof indices\n    const int dof_X = fem.GetDOFIndex(\"x\");\n    const int dof_Y = fem.GetDOFIndex(\"y\");\n    const int dof_Z = fem.GetDOFIndex(\"z\");\n    const int dof_WX = fem.GetDOFIndex(\"wx\");\n    const int dof_WY = fem.GetDOFIndex(\"wy\");\n    const int dof_WZ = fem.GetDOFIndex(\"wz\");\n    const int dof_EF  = fem.GetDOFIndex(\"ef\");\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    FEFluidFSIDomain3D& bd = static_cast<FEFluidFSIDomain3D&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // first calculate the initial residual\n    const int ndpn = 7;\n    vector<double> f0(ndpn*N);\n    zero(f0);\n    bd.ElementInternalForce(el, f0);\n    bd.ElementInertialForce(el, f0);\n    \n    // now calculate the perturbed residuals\n    ke.resize(ndpn*N, ndpn*N);\n    ke.zero();\n    double dx = 1e-8;\n    vector<double> f1(ndpn*N);\n    for (j=0; j<ndpn*N; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j/ndpn]);\n        nj = j%ndpn;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_X, dx); node.m_rt.x += dx; break;\n            case 1: node.add(dof_Y, dx); node.m_rt.y += dx; break;\n            case 2: node.add(dof_Z, dx); node.m_rt.z += dx; break;\n            case 3: node.add(dof_WX, dx); break;\n            case 4: node.add(dof_WY, dx); break;\n            case 5: node.add(dof_WZ, dx); break;\n            case 6: node.add(dof_EF, dx); break;\n        }\n        \n        \n\t\tfem.Update();\n        \n        zero(f1);\n        bd.ElementInternalForce(el, f1);\n        bd.ElementInertialForce(el, f1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_X, dx); node.m_rt.x -= dx; break;\n            case 1: node.sub(dof_Y, dx); node.m_rt.y -= dx; break;\n            case 2: node.sub(dof_Z, dx); node.m_rt.z -= dx; break;\n            case 3: node.sub(dof_WX, dx); break;\n            case 4: node.sub(dof_WY, dx); break;\n            case 5: node.sub(dof_WZ, dx); break;\n            case 6: node.sub(dof_EF, dx); break;\n        }\n        \n\t\tfem.Update();\n        \n        for (i=0; i<ndpn*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n    }\n}\n"
  },
  {
    "path": "FEBioTest/FEFluidFSITangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEFluidFSIScenario : public FEDiagnosticScenario\n{\npublic:\n    FEFluidFSIScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_dt = 1.0; }\n    \npublic:\n    double\tm_dt;\n};\n\n//-----------------------------------------------------------------------------\nclass FEFluidFSITangentUniaxial : public FEFluidFSIScenario\n{\npublic:\n    FEFluidFSITangentUniaxial(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \nprivate:\n    double\t\tm_dilation;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FEFluidFSITangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FEFluidFSITangentDiagnostic : public FEDiagnostic\n{\npublic:\n    FEFluidFSITangentDiagnostic(FEModel* fem);\n    virtual ~FEFluidFSITangentDiagnostic(){}\n    \n    bool Init();\n    \n    bool Run();\n    \n    FEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void deriv_residual(matrix& ke);\n    \n    void print_matrix(matrix& m);\n    \npublic:\n    FEFluidFSIScenario*\tm_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEFluidTangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFluidTangentDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FEBioFluid/FEFluidSolver.h\"\n#include \"FEBioFluid/FEFluidDomain3D.h\"\n#include \"FEBioFluid/FEFluidAnalysis.h\"\n#include \"FECore/log.h\"\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FELoadCurve.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEFluidTangentUniaxial, FEFluidScenario)\n\tADD_PARAMETER(m_velocity, \"fluid_velocity\");\n\tADD_PARAMETER(m_dt      , \"time_step\"     );\nEND_FECORE_CLASS();\n\nBEGIN_FECORE_CLASS(FEFluidTangentUniaxialSS, FEFluidScenario)\n\tADD_PARAMETER(m_velocity, \"fluid_velocity\");\n\tADD_PARAMETER(m_dt      , \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEFluidTangentUniaxial::FEFluidTangentUniaxial(FEDiagnostic* pdia) : FEFluidScenario(pdia)\n{\n    m_velocity = 0;\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Build the uniaxial loading scenario\n// Cube with uniaxial velocity prescribed along x on left face and dilatation\n// fixed on right face. Dynamic analysis.\nbool FEFluidTangentUniaxial::Init()\n{\n    int i;\n    vec3d r[8] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1)\n    };\n    \n    int BC[8][4] = {\n        { 0,-1,-1, 0},{ 0,-1,-1,-1},{ 0,-1,-1,-1}, { 0,-1,-1, 0},\n        { 0,-1,-1, 0},{ 0,-1,-1,-1},{ 0,-1,-1,-1}, { 0,-1,-1, 0}\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    pstep->m_nanalysis = FEFluidAnalysis::DYNAMIC;\n\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_WX = fem.GetDOFIndex(\"wx\");\n\tconst int dof_WY = fem.GetDOFIndex(\"wy\");\n\tconst int dof_WZ = fem.GetDOFIndex(\"wz\");\n\tconst int dof_EF  = fem.GetDOFIndex(\"ef\");\n\n    // --- create the FE problem ---\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(8);\n\tm.SetDOFS(MAX_DOFS);\n\n\tFENodeSet* nset[4] = { 0 };\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\tnset[3] = new FENodeSet(&fem);\n\tfor (i=0; i<8; ++i)\n    {\n        FENode& n = m.Node(i);\n        n.m_rt = n.m_r0 = r[i];\n        \n        // set displacement BC's\n        if (BC[i][0] == -1) nset[0]->Add(i);\n        if (BC[i][1] == -1) nset[1]->Add(i);\n        if (BC[i][2] == -1) nset[2]->Add(i);\n        if (BC[i][3] == -1) nset[3]->Add(i);\n    }\n    \n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WX, nset[0]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WY, nset[1]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WZ, nset[2]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_EF, nset[3]));\n\n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    \n    // create a fluid domain\n    FEFluidDomain3D* pd = new FEFluidDomain3D(&fem);\n    pd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\tpd->SetMatID(0);\n    m.AddDomain(pd);\n    FESolidElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<8; ++i) el.m_node[i] = i;\n    \n    pd->CreateMaterialPointData();\n    \n    // Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n    fem.AddLoadController(plc);\n    \n    // Add a prescribed BC\n\tFENodeSet* dc = new FENodeSet(&fem);\n\tdc->Add({ 0, 3, 4, 7 });\n\tFEPrescribedDOF* pdc = new FEPrescribedDOF(&fem, dof_WX, dc);\n    pdc->SetScale(m_velocity, 0);\n\tfem.AddBoundaryCondition(pdc);\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nFEFluidTangentUniaxialSS::FEFluidTangentUniaxialSS(FEDiagnostic* pdia) : FEFluidScenario(pdia)\n{\n    m_velocity = 0;\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\n// Build the uniaxial loading scenario\n// Cube with uniaxial velocity prescribed along x on left face and dilatation\n// fixed on right face. Steady-state analysis.\nbool FEFluidTangentUniaxialSS::Init()\n{\n    int i;\n    vec3d r[8] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1)\n    };\n    \n/*    int BC[8][4] = {\n        { 0,-1,-1, 0},{ 0,-1,-1,-1},{ 0,-1,-1,-1}, { 0,-1,-1, 0},\n        { 0,-1,-1, 0},{ 0,-1,-1,-1},{ 0,-1,-1,-1}, { 0,-1,-1, 0}\n    };*/\n    int BC[8][4] = {\n        { 0, 0, 0, 0},{ 0, 0, 0, 0},{ 0, 0, 0, 0}, { 0, 0, 0, 0},\n        { 0, 0, 0, 0},{ 0, 0, 0, 0},{ 0, 0, 0, 0}, { 0, 0, 0, 0}\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    pstep->m_nanalysis = FEFluidAnalysis::STEADY_STATE;\n    \n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n    const int dof_WX = fem.GetDOFIndex(\"wx\");\n    const int dof_WY = fem.GetDOFIndex(\"wy\");\n    const int dof_WZ = fem.GetDOFIndex(\"wz\");\n    const int dof_EF  = fem.GetDOFIndex(\"ef\");\n    \n    // --- create the FE problem ---\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(8);\n    m.SetDOFS(MAX_DOFS);\n\tFENodeSet* nset[4] = { 0 };\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\tnset[3] = new FENodeSet(&fem);\n\tfor (i = 0; i<8; ++i)\n\t{\n\t\tFENode& n = m.Node(i);\n\t\tn.m_rt = n.m_r0 = r[i];\n\n\t\t// set displacement BC's\n\t\tif (BC[i][0] == -1) nset[0]->Add(i);\n\t\tif (BC[i][1] == -1) nset[1]->Add(i);\n\t\tif (BC[i][2] == -1) nset[2]->Add(i);\n\t\tif (BC[i][3] == -1) nset[3]->Add(i);\n\t}\n\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WX, nset[0]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WY, nset[1]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WZ, nset[2]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_EF, nset[3]));\n\n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    \n    // create a fluid domain\n    FEFluidDomain3D* pd = new FEFluidDomain3D(&fem);\n    pd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n    pd->SetMatID(0);\n    m.AddDomain(pd);\n    FESolidElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<8; ++i) el.m_node[i] = i;\n    \n    pd->CreateMaterialPointData();\n    \n    // Add a loadcurve\n/*  FELoadCurve* plc = new FELoadCurve(new FELinearFunction(&fem, 1.0, 0.0));\n    fem.AddLoadCurve(plc);\n    \n    // Add a prescribed BC\n    int nd[4] = {0, 3, 4, 7};\n    FEPrescribedDOF* pdc = new FEPrescribedDOF(&fem);\n    fem.AddPrescribedBC(pdc);\n    pdc->SetDOF(dof_WX).SetScale(m_velocity, 0);\n    for (i = 0; i<4; ++i) pdc->AddNode(nd[i]);*/\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEFluidTangentDiagnostic::FEFluidTangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"fluid\");\n\n    m_pscn = 0;\n    \n    FEAnalysis* pstep = new FEAnalysis(fem);\n    \n    // create a new solver\n    FESolver* pnew_solver = fecore_new<FESolver>(\"fluid\", fem);\n    assert(pnew_solver);\n    pnew_solver->m_msymm = REAL_UNSYMMETRIC;\n    pstep->SetFESolver(pnew_solver);\n    \n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEFluidTangentDiagnostic::CreateScenario(const std::string& sname)\n{\n    if (sname == \"fluid uni-axial\") return (m_pscn = new FEFluidTangentUniaxial(this));\n    else if (sname == \"fluid uni-axial-SS\") return (m_pscn = new FEFluidTangentUniaxialSS(this));\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEFluidTangentDiagnostic::Init()\n{\n    if (m_pscn == 0) return false;\n    \n    if (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEFluidTangentDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n\tfeLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n\tfeLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n\t\tfeLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n\t\t\tfeLog(\"%15lg \", m[i][j]);\n        }\n    }\n\tfeLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate\n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEFluidTangentDiagnostic::Run()\n{\n    // solve the problem\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    pstep->Activate();\n\tfem.BlockLog();\n    fem.Solve();\n\tfem.UnBlockLog();\n    FETimeInfo tp;\n    tp.timeIncrement = dt;\n    tp.alpha = 1;\n    tp.beta = 0.25;\n    tp.gamma = 0.5;\n    tp.alphaf = 1;\n    tp.alpham = 1;\n    \n    FEMesh& mesh = fem.GetMesh();\n    FEFluidDomain3D& bd = dynamic_cast<FEFluidDomain3D&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // set up the element stiffness matrix\n    matrix k0(4*N, 4*N);\n    k0.zero();\n    bd.ElementStiffness(el, k0);\n    bd.ElementMassMatrix(el,k0);\n    \n    // print the element stiffness matrix\n\tfeLog(\"\\nActual stiffness matrix:\\n\");\n    print_matrix(k0);\n    \n    // now calculate the derivative of the residual\n    matrix k1;\n    deriv_residual(k1);\n    \n    // print the approximate element stiffness matrix\n\tfeLog(\"\\nApproximate stiffness matrix:\\n\");\n    print_matrix(k1);\n    \n    // finally calculate the difference matrix\n\tfeLog(\"\\n\");\n    matrix kd(4*N, 4*N);\n    double kmax = 0, kij;\n    int i0 = -1, j0 = -1, i, j;\n    for (i=0; i<4*N; ++i)\n        for (j=0; j<4*N; ++j)\n        {\n            kd[i][j] = k0[i][j] - k1[i][j];\n            kij = 100.0*fabs(kd[i][j] / k0[0][0]);\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    // print the difference\n\tfeLog(\"\\ndifference matrix:\\n\");\n    print_matrix(kd);\n    \n\tfeLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEFluidTangentDiagnostic::deriv_residual(matrix& ke)\n{\n    int i, j, nj;\n    \n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    FEFluidSolver& solver = static_cast<FEFluidSolver&>(*pstep->GetFESolver());\n    FETimeInfo tp;\n    tp.timeIncrement = dt;\n    tp.alpha = 1;\n    tp.beta = 0.25;\n    tp.gamma = 0.5;\n    tp.alphaf = 1;\n    tp.alpham = 1;\n\n\t// get the dof indices\n\tconst int dof_WX = fem.GetDOFIndex(\"wx\");\n\tconst int dof_WY = fem.GetDOFIndex(\"wy\");\n\tconst int dof_WZ = fem.GetDOFIndex(\"wz\");\n\tconst int dof_EF  = fem.GetDOFIndex(\"ef\");\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    FEFluidDomain3D& bd = dynamic_cast<FEFluidDomain3D&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // first calculate the initial residual\n    vector<double> f0(4*N);\n    zero(f0);\n    bd.ElementInternalForce(el, f0);\n    bd.ElementInertialForce(el, f0);\n    \n    // now calculate the perturbed residuals\n    ke.resize(4*N, 4*N);\n    ke.zero();\n    double dx = 1e-8;\n    vector<double> f1(4*N);\n    for (j=0; j<4*N; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j/4]);\n        nj = j%4;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_WX, dx); break;\n            case 1: node.add(dof_WY, dx); break;\n            case 2: node.add(dof_WZ, dx); break;\n            case 3: node.add(dof_EF, dx); break;\n        }\n        \n        \n\t\tfem.Update();\n        \n        zero(f1);\n        bd.ElementInternalForce(el, f1);\n        bd.ElementInertialForce(el, f1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_WX, dx); break;\n            case 1: node.sub(dof_WY, dx); break;\n            case 2: node.sub(dof_WZ, dx); break;\n            case 3: node.sub(dof_EF, dx); break;\n        }\n        \n\t\tfem.Update();\n        \n        for (i=0; i<4*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n    }\n}\n"
  },
  {
    "path": "FEBioTest/FEFluidTangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEFluidScenario : public FEDiagnosticScenario\n{\npublic:\n    FEFluidScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_dt = 1.0; }\n    \npublic:\n    double\tm_dt;\n};\n\n//-----------------------------------------------------------------------------\nclass FEFluidTangentUniaxial : public FEFluidScenario\n{\npublic:\n    FEFluidTangentUniaxial(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \nprivate:\n    double\t\tm_velocity;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FEFluidTangentUniaxialSS : public FEFluidScenario\n{\npublic:\n    FEFluidTangentUniaxialSS(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \nprivate:\n    double\t\tm_velocity;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FEBiphasicTangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FEFluidTangentDiagnostic : public FEDiagnostic\n{\npublic:\n    FEFluidTangentDiagnostic(FEModel* fem);\n    virtual ~FEFluidTangentDiagnostic(){}\n    \n    bool Init();\n    \n    bool Run();\n    \n    FEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void deriv_residual(matrix& ke);\n    \n    void print_matrix(matrix& m);\n    \npublic:\n    FEFluidScenario*\tm_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEJFNKTangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEJFNKTangentDiagnostic.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FENewtonSolver.h>\n#include <FECore/JFNKMatrix.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/log.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FEBioXML/XMLReader.h>\n\nFEJFNKTangentDiagnostic::FEJFNKTangentDiagnostic(FEModel* fem) : FECoreTask(fem)\n{\n\tm_max_err = 0.0;\n\tm_sum_err = 0.0;\n\tm_max_err_K = 0.0;\n\tm_max_err_A = 0.0;\n\tm_max_err_i = -1;\n\tm_max_err_j = -1;\n\tm_evals = 0;\n\tm_max_K = 0.0;\n\n\tm_jfnk_eps = 1e-7;\n\tm_small_val = 1e-9;\n\tm_nstep = -1;\n\n\tm_nprint = 0;\n}\n\nbool FEJFNKTangentDiagnostic::Init(const char* szfile)\n{\n\tXMLReader xml;\n\tif (xml.Open(szfile) == false)\n\t{\n\t\tfprintf(stderr, \"\\nERROR: Failed to open %s\\n\\n\", szfile);\n\t\treturn false;\n\t}\n\n\tXMLTag tag;\n\tif (xml.FindTag(\"jfnk_diagnostic_spec\", tag) == false)\n\t{\n\t\tfprintf(stderr, \"\\nERROR: Failed to read %s\\n\\n\", szfile);\n\t\treturn false;\n\t}\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"jfnk_eps\" ) tag.value(m_jfnk_eps);\n\t\telse if (tag == \"small_value\") tag.value(m_small_val);\n\t\telse if (tag == \"time_step\") tag.value(m_nstep);\n\t\telse if (tag == \"print_level\") tag.value(m_nprint);\n\t\telse\n\t\t{\n\t\t\tfprintf(stderr, \"ERROR: Failed to read %s\\n\\n\", szfile);\n\t\t\treturn false;\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tprintf(\"JFNK Diagnostic parameters:\\n\");\n\tprintf(\"\\tJFNK eps    = %lg\\n\", m_jfnk_eps);\n\tprintf(\"\\tSmall value = %lg\\n\", m_small_val);\n\tprintf(\"\\ttime step   = %d\\n\", m_nstep);\n\tprintf(\"\\n\");\n\n\txml.Close();\n\n\treturn GetFEModel()->Init();\n}\n\nbool cb_diagnose(FEModel* fem, unsigned int nwhen, void* pd)\n{\n\treturn ((FEJFNKTangentDiagnostic*)pd)->Diagnose();\n}\n\nbool FEJFNKTangentDiagnostic::Run()\n{\n\tFEModel& fem = *GetFEModel();\n\tfem.AddCallback(cb_diagnose, CB_MAJOR_ITERS, this);\n\n//\tfem.GetLogFile().SetMode(Logfile::LOG_FILE);\n\tprintf(\"Running model ...\");\n\tbool ret = fem.Solve();\n\tprintf(\"done!\\n\");\n\n\tprintf(\"\\nmax K    : %lg\\n\", m_max_K);\n\n\tprintf(\"\\nmax error: %lg\\n\", m_max_err);\n\tprintf(\"\\tK_exact[%d, %d] = %lg\\n\", m_max_err_i, m_max_err_j, m_max_err_K);\n\tprintf(\"\\tK_jfnk [%d, %d] = %lg\\n\", m_max_err_i, m_max_err_j, m_max_err_A);\n\n\tdouble avg_err = m_sum_err / m_evals;\n\tprintf(\"\\navg error: %lg\\n\\n\", avg_err);\n\n\treturn true;\n}\n\nbool FEJFNKTangentDiagnostic::Diagnose()\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// only analyze the requested step\n\tint timeSteps = fem.GetCurrentStep()->m_ntimesteps;\n\tif ((m_nstep != -1) && (timeSteps != m_nstep)) return true;\n\n\t// get the solver\n\tFENewtonSolver* fesolver = dynamic_cast<FENewtonSolver*>(fem.GetCurrentStep()->GetFESolver());\n\tif (fesolver == nullptr) return false;\n\n\t// Force a reform of the stiffness matrix first\n\tfesolver->ReformStiffness();\n\n\t// get the actual stiffness matrix\n\tFEGlobalMatrix* G = fesolver->GetStiffnessMatrix();\n\tif (G == nullptr) return false;\n\n\tSparseMatrix* K = G->GetSparseMatrixPtr();\n\tif (K == nullptr) return false;\n\n\t// setup the JFNK matrix\n\tJFNKMatrix A(fesolver);\n\tA.SetEpsilon(m_jfnk_eps);\n\n\t// get number of equations\n\tint neq = K->Rows();\n\tvector<double> ej(neq, 0.0), aj(neq, 0.0), kj(neq, 0.0);\n\tvector<bool> cj(neq, false);\n\n\tif (m_nprint)\n\t{\n\t\tFILE* fp = fopen(\"out_exact.txt\", \"wt\");\n\t\t// print the transpose of the matrix \n\t\tfor (int j = 0; j < neq; ++j)\n\t\t{\n\t\t\tfor (int i = 0; i < neq; ++i)\n\t\t\t{\n\t\t\t\tdouble kij = K->get(i, j);\n\t\t\t\tfprintf(fp, \"%13.7lg\", kij);\n\t\t\t\tif (i != neq - 1) fprintf(fp, \",\");\n\t\t\t}\n\t\t\tfprintf(fp, \"\\n\");\n\t\t}\n\t\tfclose(fp);\n\t}\n\n\tFILE* out = NULL;\n\tif (m_nprint)\n\t{\n\t\tout = fopen(\"out_jfnk.txt\", \"wt\");\n\t}\n\n\t// re-evaluate residual\n\tvector<double> R(neq, 0.0);\n\tfesolver->Residual(R);\n\tA.SetReferenceResidual(R);\n\n\t// loop over all columns\n\tfor (int j = 0; j < neq; ++j)\n\t{\n\t\t// create e_i vector\n\t\tej[j] = 1.0;\n\n\t\t// multiply with JFNK matrix\n\t\tA.mult_vector(&ej[0], &aj[0]);\n\n\t\t// get column of K\n\t\tfor (int i = 0; i < neq; ++i)\n\t\t{\n\t\t\tcj[i] = K->check(i, j);\n\t\t\tdouble kij = K->get(i, j);\n\t\t\tkj[i] = kij;\n\t\t\tif (fabs(kij) > m_max_K) m_max_K = fabs(kij);\n\t\t}\n\n\t\t// compare difference\n\t\tfor (int i = 0; i < neq; ++i)\n\t\t{\n\t\t\tdouble aij = aj[i];\n\t\t\tdouble kij = kj[i];\n\n\t\t\tif (m_nprint)\n\t\t\t{\n\t\t\t\tfprintf(out, \"%13.7lg\", aij);\n\t\t\t\tif (i != neq - 1) fprintf(out, \",\");\n\t\t\t}\n\n\t\t\tdouble eij = 0;\n\t\t\tif (fabs(kij) <= m_small_val)\n\t\t\t{\n\t\t\t\teij = fabs(kij - aij);\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\tdouble D = 0.5*(fabs(kij) + fabs(aij));\n\t\t\t\tif (D != 0.0)\n\t\t\t\t{\n\t\t\t\t\teij = fabs(kij - aij) / D;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (eij != 0.0)\n\t\t\t{\n\t\t\t\tm_sum_err += eij;\n\t\t\t\tm_evals++;\n\n\t\t\t\tif (eij > m_max_err)\n\t\t\t\t{\n\t\t\t\t\tm_max_err = eij;\n\t\t\t\t\tm_max_err_K = kij;\n\t\t\t\t\tm_max_err_A = aij;\n\t\t\t\t\tm_max_err_i = i;\n\t\t\t\t\tm_max_err_j = j;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (m_nprint)\n\t\t{\n\t\t\tfprintf(out, \"\\n\");\n\t\t}\n\t\t\n\t\t// reset e_j vector\n\t\tej[j] = 0.0;\n\t}\n\n\tif (m_nprint) fclose(out);\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioTest/FEJFNKTangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FECoreTask.h>\n\nclass FEJFNKTangentDiagnostic : public FECoreTask\n{\npublic:\n\tFEJFNKTangentDiagnostic(FEModel* fem);\n\n\tbool Init(const char* szfile) override;\n\n\tbool Run() override;\n\npublic:\n\tbool Diagnose();\n\nprivate:\n\tdouble\tm_max_err;\n\tdouble\tm_max_err_K, m_max_err_A;\n\tint\t\tm_max_err_i, m_max_err_j;\n\tdouble\tm_sum_err;\n\tdouble\tm_max_K;\n\tint\t\tm_evals;\n\n\tdouble\tm_jfnk_eps;\n\tdouble\tm_small_val;\n\tint\t\tm_nstep;\n\n\tint\t\tm_nprint;\n};\n"
  },
  {
    "path": "FEBioTest/FEMaterialTest.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMaterialTest.h\"\n#include \"FETangentDiagnostic.h\"\n#include <FECore/log.h>\n#include <FECore/DataRecord.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEDomain.h>\n\nFEMaterialTest::FEMaterialTest(FEModel* fem) : FEDiagnostic(fem)\n{\n\tm_szoutfile = \"stress.txt\";\n\n\tm_xout = \"Ex\";\n\tm_yout = \"sx\";\n\n\tm_pscn = nullptr;\n\tm_strain = nullptr;\n\tm_stress = nullptr;\n\n\t// make sure the correct module is active\n\tif (fem)\n\t{\n\t\tfem->SetActiveModule(\"solid\");\n\n\t\t// create an analysis step\n\t\tFEAnalysis* pstep = new FEAnalysis(fem);\n\n\t\t// create a new solver\n\t\tFESolver* pnew_solver = fecore_new<FESolver>(\"solid\", fem);\n\t\tassert(pnew_solver);\n\t\tpstep->SetFESolver(pnew_solver);\n\n\t\t// keep a pointer to the fem object\n\t\tfem->AddStep(pstep);\n\t\tfem->SetCurrentStep(pstep);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEMaterialTest::~FEMaterialTest()\n{\n\tdelete m_strain;\n\tdelete m_stress;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterialTest::SetOutputFileName(const char* szfilename)\n{\n\tm_szoutfile = szfilename;\n}\n\nvoid FEMaterialTest::SetOutputVariables(const std::string& xout, const std::string& yout)\n{\n\tm_xout = xout;\n\tm_yout = yout;\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEMaterialTest::CreateScenario(const std::string& sname)\n{\n\tif (sname == \"uni-axial\")\n\t{\n\t\tm_pscn = new FETangentUniaxial(this);\n\t\tSetOutputVariables(\"Ex\", \"sx\");\n\t}\n\telse if (sname == \"biaxial\")\n\t{\n\t\tm_pscn = new FETangentBiaxial(this);\n\t\tSetOutputVariables(\"Ex\", \"sx\");\n\t}\n\telse if (sname == \"triaxial\")\n\t{\n\t\tm_pscn = new FETangentTriaxial(this);\n\t\tSetOutputVariables(\"Ex\", \"sx\");\n\t}\n\telse if (sname == \"simple shear\")\n\t{\n\t\tm_pscn = new FETangentSimpleShear(this);\n\t\tSetOutputVariables(\"Exz\", \"sx\");\n\t}\n\treturn m_pscn;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEMaterialTest::Init()\n{\n\t// make sure we have a scenario\n\tif (m_pscn == 0) return false;\n\n\t// initialize the scenario\n\tif (m_pscn->Init() == false) return false;\n\n\tFEModel* fem = GetFEModel();\n\tfem->AddCallback(cb, CB_INIT | CB_MAJOR_ITERS, this);\n\n\tm_strain = fecore_new<FELogElemData>(m_xout.c_str(), fem); assert(m_strain);\n\tm_stress = fecore_new<FELogElemData>(m_yout.c_str(), fem); assert(m_stress);\n\n\t// add the stress output \n\tif (m_szoutfile)\n\t{\n\t\tDataRecord* pdr = fecore_new<DataRecord>(\"element_data\", fem);\n\t\tFEDomain& dom = fem->GetMesh().Domain(0);\n\t\tFEElementSet* es = new FEElementSet(fem);\n\t\tes->Create(&dom);\n\t\tstd::vector<int> dummy;\n\t\tpdr->SetItemList(es, dummy);\n\t\tpdr->SetData(\"Exy;sx\");\n\t\tpdr->SetComments(false);\n\n\t\tpdr->SetFileName(m_szoutfile);\n\n\t\tDataStore& ds = fem->GetDataStore();\n\t\tds.AddRecord(pdr);\n\t}\n\n\treturn FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMaterialTest::cb()\n{\n\tFEModel* fem = GetFEModel();\n\tFEDomain& dom= fem->GetMesh().Domain(0);\n\tFEElement& el = dom.ElementRef(0);\n\t\n\tdouble E = (m_strain ? m_strain->value(el) : 0);\n\tdouble s = (m_stress ? m_stress->value(el) : 0);\n\n\tm_data.push_back(pair<double, double>(E, s));\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate \n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEMaterialTest::Run()\n{\n\t// solve the problem\n\tFEModel& fem = *GetFEModel();\n\tfem.BlockLog();\n\tbool bret = fem.Solve();\n\tfem.UnBlockLog();\n\tif (bret == false)\n\t{\n\t\tfeLogError(\"FEBio error terminated. Aborting diagnostic.\\n\");\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\tfeLog(\"Material test completed.\");\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioTest/FEMaterialTest.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDiagnostic.h\"\n#include <FECore/ElementDataRecord.h>\n\n//-----------------------------------------------------------------------------\n//! The material test diagnostics evaluates the stress-strain of a material. \nclass FEMaterialTest : public FEDiagnostic\n{\npublic:\n\tFEMaterialTest(FEModel* fem);\n\t~FEMaterialTest();\n\n\tFEDiagnosticScenario* CreateScenario(const std::string& sname);\n\n\tbool Init();\n\n\tbool Run();\n\n\tvoid SetOutputFileName(const char* szfilename);\n\n\tvoid SetOutputVariables(const std::string& xout, const std::string& yout);\n\n\tstd::vector<std::pair<double, double> > GetOutputData() { return m_data; }\n\nprivate:\n\tstatic bool cb(FEModel* fem, unsigned int when, void* pd) { return ((FEMaterialTest*)pd)->cb(); }\n\tbool cb();\n\nprivate:\n\tFEDiagnosticScenario* m_pscn;\n\t\n\tconst char* m_szoutfile;\n\n\tstd::string m_xout, m_yout;\n\n\tFELogElemData* m_strain;\n\tFELogElemData* m_stress;\n\n\tstd::vector<std::pair<double, double> >\tm_data;\n};\n"
  },
  {
    "path": "FEBioTest/FEMemoryDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMemoryDiagnostic.h\"\n#include \"FECore/log.h\"\n\nFEMemoryDiagnostic::FEMemoryDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\tm_szfile[0] = 0;\n\tm_iters = 1;\n\n\tFEAnalysis* pstep = new FEAnalysis(fem);\n\tfem->AddStep(pstep);\n\tfem->SetCurrentStep(pstep);\n}\n\nFEMemoryDiagnostic::~FEMemoryDiagnostic(void)\n{\n\n}\n\nbool FEMemoryDiagnostic::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// try to open the file\n\tFEBioImport im;\n\tif (im.Load(fem, m_szfile) == false)\n\t{\n\t\treturn false;\n\t}\n\n\t// make sure the iters is a positive number\n\tif (m_iters <= 0) return false;\n\n\t// turn off all output\n\tfem.GetCurrentStep()->SetPlotLevel(FE_PLOT_NEVER);\n\n\treturn true;\n}\n\nbool FEMemoryDiagnostic::Run()\n{\n\t// run the problem\n\tFEModel& fem = *GetFEModel();\n\tfor (int i=0; i<m_iters; ++i)\n\t{\n\t\tfprintf(stderr, \"%d/%d: ...\", i+1, m_iters);\n\t\tfem.Reset();\n\t\tbool b = fem.Solve();\n//\t\tsystem(\"ps -C febio.test -o rss,vsize h\");\n\t\tfprintf(stderr, \"%s\\n\", (b?\"NT\" : \"ET\"));\n\t}\n\n\treturn true;\n}\n\nbool FEMemoryDiagnostic::ParseSection(XMLTag &tag)\n{\n\tif (tag == \"file\") tag.value(m_szfile);\n\telse if (tag == \"iters\") tag.value(m_iters);\n\telse return false;\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioTest/FEMemoryDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\nclass FEMemoryDiagnostic :\tpublic FEDiagnostic\n{\npublic:\n\tFEMemoryDiagnostic(FEModel* fem);\n\t~FEMemoryDiagnostic();\n\n\tbool Init();\n\n\tbool Run();\n\n\tbool ParseSection(XMLTag& tag);\n\nprotected:\n\tchar\tm_szfile[512];\n\tint\t\tm_iters;\n};\n"
  },
  {
    "path": "FEBioTest/FEMultiphasicTangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMultiphasicTangentDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FEBioMix/FEMultiphasicSolver.h\"\n#include \"FEBioMix/FEMultiphasicSolidDomain.h\"\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEFixedBC.h>\n#include \"FECore/FEInitialCondition.h\"\n#include <FECore/FELoadCurve.h>\n#include \"FECore/log.h\"\n\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMultiphasicTangentUniaxial, FEDiagnosticScenario)\n\tADD_PARAMETER(m_strain       , \"solid_strain\"  );\n\tADD_PARAMETER(m_pressure     , \"fluid_pressure\");\n\tADD_PARAMETER(m_dt           , \"time_step\"     );\n\tADD_PARAMETER(m_concentration, \"solute_concentration\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMultiphasicTangentUniaxial::FEMultiphasicTangentUniaxial(FEDiagnostic* pdia) : FEMultiphasicScenario(pdia)\n{ \n\tm_strain = 0;\n    m_pressure = 0;\n    m_concentration = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Build the uniaxial loading scenario\n// Cube with face displaced along x, with zero fluid pressure on displaced face\n// and impermeable fixed face.\nbool FEMultiphasicTangentUniaxial::Init()\n{\n    int i, isol;\n    vec3d r[8] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1)\n    };\n    \n    int BC[8][3] = {\n        {-1,-1,-1},{ 0,-1,-1},{ 0, 0,-1}, {-1, 0,-1},\n        {-1,-1, 0},{ 0,-1, 0},{ 0, 0, 0}, {-1, 0, 0}\n    };\n    \n    // get the material\n\tFEModel& fem = *GetDiagnostic()->GetFEModel();\n\n    FEMaterial* pmat = fem.GetMaterial(0);\n    FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pmat);\n    assert(pmp);\n    int nsol = pmp->Solutes();\n    double osm = nsol*m_concentration;\n    double pe = -pmp->m_Rgas*pmp->m_Tabs*osm;\n    \n    // --- create the FE problem ---\n\t// get the degrees of freedom\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_x = fem.GetDOFIndex(\"x\");\n\tconst int dof_y = fem.GetDOFIndex(\"y\");\n\tconst int dof_z = fem.GetDOFIndex(\"z\");\n\tconst int dof_p = fem.GetDOFIndex(\"p\");\n\tconst int dof_c = fem.GetDOFIndex(\"concentration\", 0);\n\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(8);\n\tm.SetDOFS(MAX_DOFS);\n\n\t// create a node set\n\tFENodeSet* iset = new FENodeSet(&fem);\n\tfor (int i = 0; i < 8; ++i) iset->Add(i);\n\n\t// add initial conditions\n\tfor (isol = 0; isol<nsol; ++isol) {\n\n\t\tFEInitialDOF* pic = new FEInitialDOF(&fem, dof_c + isol, iset);\n\t\tpic->SetValue(m_concentration);\n\n\t\tfem.AddInitialCondition(pic);\n\t}\n\n\tFEInitialDOF* pip = new FEInitialDOF(&fem, dof_p, iset);\n\tpip->SetValue(pe);\n\tfem.AddInitialCondition(pip);\n\n\t// add boundary conditions\n\tFENodeSet* nset[3];\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\tfor (i=0; i<8; ++i)\n    {\n        FENode& n = m.Node(i);\n        n.m_rt = n.m_rp = n.m_r0 = r[i];\n        \n        // set displacement BC's\n        if (BC[i][0] == -1) nset[0]->Add(i);\n        if (BC[i][1] == -1) nset[1]->Add(i);\n        if (BC[i][2] == -1) nset[2]->Add(i);\n    }\n\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_x, nset[0]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_y, nset[1]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_z, nset[2]));\n\n    // create a multiphasic domain\n    FEMultiphasicSolidDomain* pd = new FEMultiphasicSolidDomain(&fem);\n\tpd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\tpd->SetMatID(0);\n    m.AddDomain(pd);\n    FESolidElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<8; ++i) el.m_node[i] = i;\n    \n    pd->CreateMaterialPointData();\n    \n    // convert strain to a displacement\n    double d = sqrt(2*m_strain+1) - 1;\n    \n    // Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n\tfem.AddLoadController(plc);\n\n    // Add a prescribed displacement BC along X\n\tFENodeSet* dc = new FENodeSet(&fem);\n\tdc->Add({ 1, 2, 5, 6 });\n\n\tFEPrescribedDOF* pdc = new FEPrescribedDOF(&fem, dof_x, dc);\n\tpdc->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdc);\n\t\n    // Add a prescribed fluid pressure BC\n\tFEPrescribedDOF* ppc = new FEPrescribedDOF(&fem, dof_p, dc);\n\tppc->SetScale(pe, 0);\n\tfem.AddBoundaryCondition(ppc);\n\n    // Add prescribed solute concentration BC\n    for (i=0; i<nsol; ++i) {\n\t\tFEPrescribedDOF* psc = new FEPrescribedDOF(&fem, dof_c + i, dc);\n\t\tpsc->SetScale(m_concentration, 0);\n\t\tfem.AddBoundaryCondition(psc);\n\t}\n\n\treturn true;\n}\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEMultiphasicTangentDiagnostic::FEMultiphasicTangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"multiphasic\");\n\n    m_pscn = 0;\n\n\tFEAnalysis* pstep = new FEAnalysis(fem);\n\n\t// create a new solver\n\tFESolver* pnew_solver = fecore_new<FESolver>(\"multiphasic\", fem);\n\tassert(pnew_solver);\n\tpnew_solver->m_msymm = REAL_UNSYMMETRIC;\n\tpstep->SetFESolver(pnew_solver);\n\n\tfem->AddStep(pstep);\n\tfem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEMultiphasicTangentDiagnostic::CreateScenario(const std::string& sname)\n{\n\tif (sname == \"multiphasic uni-axial\") return (m_pscn = new FEMultiphasicTangentUniaxial(this));\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEMultiphasicTangentDiagnostic::Init()\n{\n\tif (m_pscn == 0) return false;\n\n\tif (m_pscn->Init() == false) return false;\n\n\treturn FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEMultiphasicTangentDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n\tfeLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n\t\tfeLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n\t\t\tfeLog(\"%15lg \", m[i][j]);\n        }\n    }\n\tfeLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate\n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEMultiphasicTangentDiagnostic::Run()\n{\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n\n    // solve the problem\n\tfem.BlockLog();\n    if (fem.Solve() == false)\n\t{\n\t\tfem.UnBlockLog();\n\t\treturn false;\n\t}\n\tfem.UnBlockLog();\n    \n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pmat);\n    assert(pmp);\n    int nsol = pmp->Solutes();\n    int ndpn = 4+nsol;\n    \n    FEMesh& mesh = fem.GetMesh();\n    FEMultiphasicSolidDomain& md = static_cast<FEMultiphasicSolidDomain&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = md.Element(0);\n    int N = mesh.Nodes();\n    \n    // set up the element stiffness matrix\n    matrix k0(ndpn*N, ndpn*N);\n    k0.zero();\n    md.ElementMultiphasicStiffness(el, k0, false);\n    double k0max = 0;\n    for (int i=0; i<ndpn*N; ++i)\n        for (int j=0; j<ndpn*N; ++j)\n            if (fabs(k0[i][j]) > k0max) k0max = fabs(k0[i][j]);\n    \n    // print the element stiffness matrix\n\tfeLog(\"\\nActual stiffness matrix:\\n\");\n    print_matrix(k0);\n    \n    // now calculate the derivative of the residual\n    matrix k1;\n    deriv_residual(k1);\n    \n    // print the approximate element stiffness matrix\n\tfeLog(\"\\nApproximate stiffness matrix:\\n\");\n    print_matrix(k1);\n    \n    // finally calculate the difference matrix\n\tfeLog(\"\\n\");\n    matrix kd(ndpn*N, ndpn*N);\n    double kmax = 0, kij;\n    int i0 = -1, j0 = -1, i, j;\n    for (i=0; i<ndpn*N; ++i)\n        for (j=0; j<ndpn*N; ++j)\n        {\n            kd[i][j] = k0[i][j] - k1[i][j];\n            kij = 100.0*fabs(kd[i][j] / k0max);\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    // print the difference\n\tfeLog(\"\\ndifference matrix:\\n\");\n    print_matrix(kd);\n    \n\tfeLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEMultiphasicTangentDiagnostic::deriv_residual(matrix& ke)\n{\n    int i, j, nj;\n    \n    // get the solver\n\tFEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n\tFEMultiphasicSolver& solver = static_cast<FEMultiphasicSolver&>(*pstep->GetFESolver());\n    \n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    FEMultiphasic* pmp = dynamic_cast<FEMultiphasic*>(pmat);\n    assert(pmp);\n    int nsol = pmp->Solutes();\n    int ndpn = 4+nsol;\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    const int dof_x = fem.GetDOFIndex(\"x\");\n    const int dof_y = fem.GetDOFIndex(\"y\");\n    const int dof_z = fem.GetDOFIndex(\"z\");\n\tconst int dof_p = fem.GetDOFIndex(\"p\");\n\tconst int dof_c = fem.GetDOFIndex(\"concentration\", 0);\n    \n    FEMultiphasicSolidDomain& md = static_cast<FEMultiphasicSolidDomain&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = md.Element(0);\n    int N = mesh.Nodes();\n    \n    // first calculate the initial residual\n    vector<double> f0(ndpn*N);\n    vector< vector<double> > f0c(nsol,vector<double>(N));\n    zero(f0);\n    md.ElementInternalForce(el, f0);\n    \n    // now calculate the perturbed residuals\n    ke.resize(ndpn*N, ndpn*N);\n    ke.zero();\n    double dx = 1e-8;\n    vector<double> f1(ndpn*N);\n    for (j=0; j<ndpn*N; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j/ndpn]);\n        nj = j%ndpn;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_x, dx); node.m_rt.x += dx; break;\n            case 1: node.add(dof_y, dx); node.m_rt.y += dx; break;\n            case 2: node.add(dof_z, dx); node.m_rt.z += dx; break;\n            case 3: node.add(dof_p, dx); break;\n            default: node.add(dof_c + nj-4, dx); break;\n        }\n        \n        \n\t\tfem.Update();\n        \n        zero(f1);\n        md.ElementInternalForce(el, f1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_x, dx); node.m_rt.x -= dx; break;\n            case 1: node.sub(dof_y, dx); node.m_rt.y -= dx; break;\n            case 2: node.sub(dof_z, dx); node.m_rt.z -= dx; break;\n            case 3: node.sub(dof_p, dx); break;\n            default: node.sub(dof_c + nj-4, dx); break;\n        }\n        \n\t\tfem.Update();\n        \n        for (i=0; i<ndpn*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n    }\n}\n"
  },
  {
    "path": "FEBioTest/FEMultiphasicTangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMultiphasicScenario : public FEDiagnosticScenario\n{\npublic:\n\tFEMultiphasicScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_dt = 1.0; }\n\npublic:\n\tdouble\tm_dt;\n};\n\n//-----------------------------------------------------------------------------\nclass FEMultiphasicTangentUniaxial : public FEMultiphasicScenario\n{\npublic:\n\tFEMultiphasicTangentUniaxial(FEDiagnostic* pdia);\n\n\tbool Init() override;\n\nprivate:\n    double\t\tm_strain;\n    double      m_pressure;\n    double      m_concentration;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FEMultiphasicTangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FEMultiphasicTangentDiagnostic : public FEDiagnostic\n{\npublic:\n    FEMultiphasicTangentDiagnostic(FEModel* fem);\n    \n    bool Init();\n    \n    bool Run();\n\n\tFEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void deriv_residual(matrix& ke);\n    \n    void print_matrix(matrix& m);\n    \npublic:\n    FEMultiphasicScenario*\tm_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEPolarFluidTangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPolarFluidTangentDiagnostic.h\"\n#include \"FETangentDiagnostic.h\"\n#include <FEBioFluid/FEPolarFluidSolver.h>\n#include <FEBioFluid/FEPolarFluidDomain3D.h>\n#include <FEBioFluid/FEPolarFluidAnalysis.h>\n#include <FECore/log.h>\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FELoadCurve.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPolarFluidTangentUniaxial, FEPolarFluidScenario)\n\tADD_PARAMETER(m_velocity, \"fluid_velocity\");\n    ADD_PARAMETER(m_angular_velocity, \"fluid_angular_velocity\");\n\tADD_PARAMETER(m_dt      , \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPolarFluidTangentUniaxial::FEPolarFluidTangentUniaxial(FEDiagnostic* pdia) : FEPolarFluidScenario(pdia)\n{\n    m_velocity = 0;\n    m_angular_velocity = 0;\n    m_dt = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Build the uniaxial loading scenario\n// Cube with uniaxial velocity prescribed along x on left face and dilatation\n// fixed on right face. Dynamic analysis.\nbool FEPolarFluidTangentUniaxial::Init()\n{\n    int i;\n    vec3d r[8] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1)\n    };\n    \n    int BC[8][7] = {\n        { 0, 0, 0, 0, 0, 0,-1},\n        {-1,-1,-1, 0, 0, 0, 0},\n        {-1,-1,-1, 0, 0, 1,-1},\n        { 1, 0, 0, 0, 0, 1,-1},\n        { 0, 0, 0,-1,-1,-1,-1},\n        {-1,-1,-1,-1,-1,-1, 0},\n        {-1,-1,-1,-1,-1,-1, 0},\n        { 1, 0, 0,-1,-1,-1,-1}\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n//    pstep->m_nanalysis = FEPolarFluidAnalysis::DYNAMIC;\n    pstep->m_nanalysis = FEPolarFluidAnalysis::STEADY_STATE;\n\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_WX = fem.GetDOFIndex(\"wx\");\n\tconst int dof_WY = fem.GetDOFIndex(\"wy\");\n\tconst int dof_WZ = fem.GetDOFIndex(\"wz\");\n    const int dof_GX = fem.GetDOFIndex(\"gx\");\n    const int dof_GY = fem.GetDOFIndex(\"gy\");\n    const int dof_GZ = fem.GetDOFIndex(\"gz\");\n\tconst int dof_EF  = fem.GetDOFIndex(\"ef\");\n\n    // --- create the FE problem ---\n    // create the mesh\n    FEMesh& m = fem.GetMesh();\n    m.CreateNodes(8);\n\tm.SetDOFS(MAX_DOFS);\n\n\tFENodeSet* nfset[7] = { 0 };\n    for (int i=0; i<7; ++i)\tnfset[i] = new FENodeSet(&fem);\n\tfor (i=0; i<8; ++i)\n    {\n        FENode& n = m.Node(i);\n        n.m_rt = n.m_r0 = r[i];\n        \n        // set displacement BC's\n        for (int j=0; j<7; ++j)\n            if (BC[i][j] == 0) nfset[j]->Add(i);\n    }\n    \n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WX, nfset[0]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WY, nfset[1]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_WZ, nfset[2]));\n    fem.AddBoundaryCondition(new FEFixedBC(&fem, dof_GX, nfset[3]));\n    fem.AddBoundaryCondition(new FEFixedBC(&fem, dof_GY, nfset[4]));\n    fem.AddBoundaryCondition(new FEFixedBC(&fem, dof_GZ, nfset[5]));\n\tfem.AddBoundaryCondition(new FEFixedBC(&fem, dof_EF, nfset[6]));\n\n    // get the material\n    FEMaterial* pmat = fem.GetMaterial(0);\n    \n    // create a fluid domain\n    FEPolarFluidDomain3D* pd = new FEPolarFluidDomain3D(&fem);\n    pd->SetMaterial(pmat);\n    pd->Create(1, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n\tpd->SetMatID(0);\n    m.AddDomain(pd);\n    FESolidElement& el = pd->Element(0);\n    el.SetID(1);\n    for (i=0; i<8; ++i) el.m_node[i] = i;\n    \n    pd->CreateMaterialPointData();\n    \n    // Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(1.0, 1.0);\n    fem.AddLoadController(plc);\n    \n    // Add a prescribed velocity BC\n\tFENodeSet* dcv = new FENodeSet(&fem);\n\tdcv->Add({ 3, 7 });\n\tFEPrescribedDOF* pdcv = new FEPrescribedDOF(&fem, dof_WX, dcv);\n    pdcv->SetScale(m_velocity, 0);\n\tfem.AddBoundaryCondition(pdcv);\n\n    // Add a prescribed angular velocity BC\n    FENodeSet* dcg = new FENodeSet(&fem);\n    dcg->Add({ 2, 3 });\n    FEPrescribedDOF* pdcg = new FEPrescribedDOF(&fem, dof_GZ, dcg);\n    pdcg->SetScale(m_angular_velocity, 0);\n    fem.AddBoundaryCondition(pdcg);\n    \n    m_time_increment = m_dt;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// Constructor\nFEPolarFluidTangentDiagnostic::FEPolarFluidTangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n    m_pscn = 0;\n    \n    // make sure the correct module is active\n    fem->SetActiveModule(\"polar fluid\");\n    \n    FEAnalysis* pstep = new FEAnalysis(fem);\n    \n    // create a new solver\n    FESolver* pnew_solver = fecore_new<FESolver>(\"polar fluid\", fem);\n    assert(pnew_solver);\n    pnew_solver->m_msymm = REAL_UNSYMMETRIC;\n    pstep->SetFESolver(pnew_solver);\n    \n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FEPolarFluidTangentDiagnostic::CreateScenario(const std::string& sname)\n{\n    if (sname == \"polar fluid uni-axial\") return (m_pscn = new FEPolarFluidTangentUniaxial(this));\n    return 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEPolarFluidTangentDiagnostic::Init()\n{\n    if (m_pscn == 0) return false;\n    \n    if (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FEPolarFluidTangentDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n\tfeLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n\tfeLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n\t\tfeLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n\t\t\tfeLog(\"%15lg \", m[i][j]);\n        }\n    }\n\tfeLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate\n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEPolarFluidTangentDiagnostic::Run()\n{\n    // solve the problem\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_time_increment;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    pstep->Activate();\n\tfem.BlockLog();\n    fem.Solve();\n\tfem.UnBlockLog();\n    FETimeInfo tp;\n    tp.timeIncrement = dt;\n    tp.alpha = 0.5;\n    tp.beta = 0.25;\n    tp.gamma = 0.5;\n    tp.alphaf = 0.5;\n    tp.alpham = 0.5;\n    \n    FEMesh& mesh = fem.GetMesh();\n    FEPolarFluidDomain3D& bd = dynamic_cast<FEPolarFluidDomain3D&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // set up the element stiffness matrix\n    const int ndpn = 7;\n    matrix k0(ndpn*N, ndpn*N);\n    k0.zero();\n    bd.ElementStiffness(el, k0);\n    bd.ElementMassMatrix(el,k0);\n    \n    // print the element stiffness matrix\n\tfeLog(\"\\nActual stiffness matrix:\\n\");\n    print_matrix(k0);\n    \n    // now calculate the derivative of the residual\n    matrix k1(ndpn*N, ndpn*N);\n    deriv_residual(k1);\n    \n    // print the approximate element stiffness matrix\n\tfeLog(\"\\nApproximate stiffness matrix:\\n\");\n    print_matrix(k1);\n    \n    // finally calculate the difference matrix\n\tfeLog(\"\\n\");\n    matrix kd(ndpn*N, ndpn*N);\n    double kmax = 0, kij;\n    int i0 = -1, j0 = -1, i, j;\n    for (i=0; i<ndpn*N; ++i)\n        for (j=0; j<ndpn*N; ++j)\n        {\n            kd[i][j] = k0[i][j] - k1[i][j];\n            kij = 100.0*fabs(kd[i][j] / k0[0][0]);\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    // print the difference\n\tfeLog(\"\\ndifference matrix:\\n\");\n    print_matrix(kd);\n    \n\tfeLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEPolarFluidTangentDiagnostic::deriv_residual(matrix& ke)\n{\n    int i, j, nj;\n    \n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_time_increment;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    FEPolarFluidSolver& solver = static_cast<FEPolarFluidSolver&>(*pstep->GetFESolver());\n    FETimeInfo tp;\n    tp.timeIncrement = dt;\n    tp.alpha = 1;\n    tp.beta = 0.25;\n    tp.gamma = 0.5;\n    tp.alphaf = 1;\n    tp.alpham = 1;\n\n\t// get the dof indices\n\tconst int dof_WX = fem.GetDOFIndex(\"wx\");\n\tconst int dof_WY = fem.GetDOFIndex(\"wy\");\n\tconst int dof_WZ = fem.GetDOFIndex(\"wz\");\n    const int dof_GX = fem.GetDOFIndex(\"gx\");\n    const int dof_GY = fem.GetDOFIndex(\"gy\");\n    const int dof_GZ = fem.GetDOFIndex(\"gz\");\n\tconst int dof_EF  = fem.GetDOFIndex(\"ef\");\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    \n    FEPolarFluidDomain3D& bd = dynamic_cast<FEPolarFluidDomain3D&>(mesh.Domain(0));\n    \n    // get the one and only element\n    FESolidElement& el = bd.Element(0);\n    int N = mesh.Nodes();\n    \n    // first calculate the initial residual\n    const int ndpn = 7;\n    vector<double> f0(ndpn*N);\n    zero(f0);\n    bd.ElementInternalForce(el, f0);\n    bd.ElementInertialForce(el, f0);\n    \n    // now calculate the perturbed residuals\n    ke.zero();\n    double dx = 1e-8;\n    vector<double> f1(ndpn*N);\n    for (j=0; j<ndpn*N; ++j)\n    {\n        FENode& node = mesh.Node(el.m_node[j/ndpn]);\n        nj = j%ndpn;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_WX, dx); break;\n            case 1: node.add(dof_WY, dx); break;\n            case 2: node.add(dof_WZ, dx); break;\n            case 3: node.add(dof_GX, dx); break;\n            case 4: node.add(dof_GY, dx); break;\n            case 5: node.add(dof_GZ, dx); break;\n            case 6: node.add(dof_EF, dx); break;\n        }\n        \n        \n\t\tfem.Update();\n        \n        zero(f1);\n        bd.ElementInternalForce(el, f1);\n        bd.ElementInertialForce(el, f1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_WX, dx); break;\n            case 1: node.sub(dof_WY, dx); break;\n            case 2: node.sub(dof_WZ, dx); break;\n            case 3: node.sub(dof_GX, dx); break;\n            case 4: node.sub(dof_GY, dx); break;\n            case 5: node.sub(dof_GZ, dx); break;\n            case 6: node.sub(dof_EF, dx); break;\n        }\n        \n\t\tfem.Update();\n        \n        for (i=0; i<ndpn*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n    }\n}\n"
  },
  {
    "path": "FEBioTest/FEPolarFluidTangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FEPolarFluidScenario : public FEDiagnosticScenario\n{\npublic:\n    FEPolarFluidScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_time_increment = 0.0; }\n\npublic:\n    double m_time_increment;\n};\n\n//-----------------------------------------------------------------------------\nclass FEPolarFluidTangentUniaxial : public FEPolarFluidScenario\n{\npublic:\n    FEPolarFluidTangentUniaxial(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \nprivate:\n    double\t\tm_velocity;\n    double      m_angular_velocity;\n    double      m_dt;\n\n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FEBiphasicTangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FEPolarFluidTangentDiagnostic : public FEDiagnostic\n{\npublic:\n    FEPolarFluidTangentDiagnostic(FEModel* fem);\n    \n    bool Init();\n    \n    bool Run();\n    \n    FEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void deriv_residual(matrix& ke);\n    \n    void print_matrix(matrix& m);\n    \npublic:\n    FEPolarFluidScenario*\tm_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FEPrintHBMatrixDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrintHBMatrixDiagnostic.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include <FECore/CompactMatrix.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <NumCore/MatrixTools.h>\n\n//-----------------------------------------------------------------------------\nFEPrintHBMatrixDiagnostic::FEPrintHBMatrixDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEPrintHBMatrixDiagnostic::~FEPrintHBMatrixDiagnostic(void)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrintHBMatrixDiagnostic::ParseSection(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tif (tag == \"input\")\n\t{\n\t\t// get the input file name\n\t\tconst char* szfile = tag.szvalue();\n\n\t\t// try to read the file\n\t\tFEBioImport im;\n\t\tif (im.Load(fem, szfile) == false)\n\t\t{\n\t\t\tchar szerr[256];\n\t\t\tim.GetErrorMessage(szerr);\n\t\t\tfprintf(stderr, \"%s\", szerr);\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn  false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrintHBMatrixDiagnostic::Run()\n{\n\t// Get the current step\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t// initialize the step\n\tpstep->Activate();\n\n\t// get and initialize the FE solver\n\tFENewtonSolver* solver = dynamic_cast<FENewtonSolver*>(pstep->GetFESolver());\n\tif (solver == 0) return false;\n\n\t// do initialization\n\tFETimeInfo& tp = fem.GetTime();\n\ttp.currentTime = pstep->m_dt0;\n\ttp.timeIncrement = pstep->m_dt0;\n\tsolver->InitStep(pstep->m_dt0);\n\tsolver->PrepStep();\n\n\t// reshape the stiffness matrix\n\tif (!solver->ReformStiffness()) return false;\n\n\t// get the matrix\n\tSparseMatrix* psm = solver->GetStiffnessMatrix()->GetSparseMatrixPtr();\n\tCompactMatrix* pcm = dynamic_cast<CompactMatrix*>(psm);\n\tif (pcm == 0) return false;\n\n\t// print the matrix to file\n\tif (NumCore::write_hb(*pcm, \"hb_matrix.out\") == false)\n\t{\n\t\tfprintf(stderr, \"Failed writing sparse matrix.\\n\\n\");\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioTest/FEPrintHBMatrixDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//! Harwell-Boeing Matrix Print Diagnostic\n\n//! Class to run a diagnostic to print the initial matrix in\n//! Harwell-Boeing matrix format\n\nclass FEPrintHBMatrixDiagnostic :\tpublic FEDiagnostic\n{\npublic:\n\tFEPrintHBMatrixDiagnostic(FEModel* fem);\n\t~FEPrintHBMatrixDiagnostic(void);\n\n\tbool ParseSection(XMLTag& tag);\n\n\tbool Run();\n\n};\n"
  },
  {
    "path": "FEBioTest/FEPrintMatrixDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrintMatrixDiagnostic.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FECore/FEGlobalMatrix.h\"\n#include <stdio.h>\n\n//-----------------------------------------------------------------------------\n//! Print a block from a sparse matrix to file\nvoid print(SparseMatrix& m, FILE* fp, int i0, int j0, int i1, int j1)\n{\n\tint nr = m.Rows();\n\tint nc = m.Columns();\n\tif ((i1 < 0) || (i1 >= nr)) i1 = nr-1;\n\tif ((j1 < 0) || (j1 >= nc)) j1 = nc-1;\n\n\tfor (int i=i0; i<=i1; ++i)\n\t{\n\t\tfor (int j=j0; j<=j1; ++j)\n\t\t{\n\t\t\tfprintf(fp, \"%10.3g\", m.get(i,j));\n\t\t}\n\t\tfprintf(fp, \"\\n\");\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEPrintMatrixDiagnostic::FEPrintMatrixDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\tm_szout[0] = 0;\n\tm_rng[0] = m_rng[1] = 0;\n\tm_rng[2] = m_rng[3] = -1;\n\n\tFEAnalysis* pstep = new FEAnalysis(fem);\n\tfem->AddStep(pstep);\n\tfem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEPrintMatrixDiagnostic::~FEPrintMatrixDiagnostic(void)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrintMatrixDiagnostic::ParseSection(XMLTag &tag)\n{\n\tif (tag == \"input\")\n\t{\n\t\t// get the input file name\n\t\tconst char* szfile = tag.szvalue();\n\n\t\t// try to read the file\n\t\tFEBioImport im;\n\t\tFEModel& fem = *GetFEModel();\n\t\tif (im.Load(fem, szfile) == false)\n\t\t{\n\t\t\tchar szerr[256];\n\t\t\tim.GetErrorMessage(szerr);\n\t\t\tfprintf(stderr, \"%s\", szerr);\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\telse if (tag == \"output\")\n\t{\n\t\tstrcpy(m_szout, tag.szvalue());\n\t\treturn true;\n\t}\n\telse if (tag == \"range\")\n\t{\n\t\ttag.value(m_rng, 4);\n\t\treturn true;\n\t}\n\n\treturn  false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrintMatrixDiagnostic::Run()\n{\n\t// get and initialize the first step\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetStep(0);\n\tpstep->Init();\n\tpstep->Activate();\n\n\t// get and initialize the solver\n\tFESolidSolver2& solver = static_cast<FESolidSolver2&>(*pstep->GetFESolver());\n\tsolver.Init();\n\n\t// build the stiffness matrix\n\t// recalculate the shape of the stiffness matrix if necessary\n\tfem.Update();\n\n\t// reshape the stiffness matrix\n\tif (!solver.CreateStiffness(true)) return false;\n\n\t// calculate the stiffness matrices\n\tsolver.StiffnessMatrix();\n\n\t// print the matrix\n\tFILE* fp = fopen(m_szout, \"wt\");\n\tif (fp == 0) { fprintf(stderr, \"Failed creating output file.\"); return false; }\n\tint* n = m_rng;\n\tprint(*solver.GetStiffnessMatrix()->GetSparseMatrixPtr(), fp, n[0], n[1], n[2], n[3]);\n\tfclose(fp);\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioTest/FEPrintMatrixDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\nclass FEPrintMatrixDiagnostic :\tpublic FEDiagnostic\n{\npublic:\n\tFEPrintMatrixDiagnostic(FEModel* fem);\n\t~FEPrintMatrixDiagnostic(void);\n\n\tbool ParseSection(XMLTag& tag);\n\n\tbool Run();\n\nprotected:\n\tchar\tm_szout[1024];\n\tint\t\tm_rng[4];\n};\n"
  },
  {
    "path": "FEBioTest/FEResetTest.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEResetTest.h\"\n#include <FEBioLib/FEBioModel.h>\n#include <FECore/log.h>\n#include <FEBioLib/Logfile.h>\n#include <FECore/FELogSolutionNorm.h>\n#include <iostream>\n#include <iomanip>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nFEResetTest::FEResetTest(FEModel*pfem) : FECoreTask(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\n// initialize the diagnostic\nbool FEResetTest::Init(const char* sz)\n{\n\tFEBioModel& fem = dynamic_cast<FEBioModel&>(*GetFEModel());\n\n\tLogfile& log = fem.GetLogFile();\n\tlog.SetMode(Logfile::MODE::LOG_FILE);\n\n\t// do the FE initialization\n\treturn fem.Init();\n}\n\n//-----------------------------------------------------------------------------\n// run the diagnostic\nbool FEResetTest::Run()\n{\n\tFEBioModel* fem = dynamic_cast<FEBioModel*>(GetFEModel());\n\n\tFELogSolutionNorm sn(fem);\n\n\t// try to run the model\n\tcerr << \"Running model for the first time.\\n\";\n\tif (fem->Solve() == false)\n\t{\n\t\tcerr << \"Failed to run model.\\nTest aborted.\\n\\n\";\n\t\treturn false;\n\t}\n\n\t// collect results\n\tModelStats stats1 = fem->GetModelStats();\n\tdouble norm1 = sn.value();\n\tcerr << \"time steps    = \" << stats1.ntimeSteps    << endl;\n\tcerr << \"total iters   = \" << stats1.ntotalIters   << endl;\n\tcerr << \"total reforms = \" << stats1.ntotalReforms << endl;\n\tcerr << \"total rhs     = \" << stats1.ntotalRHS     << endl;\n\tcerr << \"solution norm = \" << std::setprecision(15) << norm1 << endl;\n\n\t// change the name of the logfile so we can compare\n\tstd::string logfileName = fem->GetLogfileName();\n\tsize_t n = logfileName.rfind('.');\n\tstring extension;\n\tstring filebase;\n\tif (n != std::string::npos)\n\t{\n\t\textension = logfileName.substr(n);\n\t\tfilebase = logfileName.substr(0, n);\n\t}\n\tfilebase += \"_2\";\n\tlogfileName = filebase + extension;\n\tfem->SetLogFilename(logfileName);\n\n\t// reset the model\n\tstd::cerr << \"Resetting model.\\n\";\n\tif (fem->Reset() == false)\n\t{\n\t\tcerr << \"Failed to reset model.\\nTest aborted.\\n\\n\";\n\t\treturn false;\n\t}\n\n\t// try to run it again\n\tstd::cerr << \"Running model for the second time.\\n\";\n\tif (fem->Solve() == false)\n\t{\n\t\tcerr << \"Failed to run model second time.\\nTest aborted.\\n\\n\";\n\t\treturn false;\n\t}\n\n\t// get model stats\n\tModelStats stats2 = fem->GetModelStats();\n\tdouble norm2 = sn.value();\n\tcerr << \"time steps    = \" << stats2.ntimeSteps    << endl;\n\tcerr << \"total iters   = \" << stats2.ntotalIters   << endl;\n\tcerr << \"total reforms = \" << stats2.ntotalReforms << endl;\n\tcerr << \"total rhs     = \" << stats2.ntotalRHS     << endl;\n\tcerr << \"solution norm = \" << norm2 << endl;\n\n\tbool success = true;\n\tif (stats1.ntimeSteps    != stats2.ntimeSteps   ) success = false;\n\tif (stats1.ntotalIters   != stats2.ntotalIters  ) success = false;\n\tif (stats1.ntotalReforms != stats2.ntotalReforms) success = false;\n\tif (stats1.ntotalRHS     != stats2.ntotalRHS    ) success = false;\n\tif (norm1 != norm2) success = false;\n\tcerr << \" --> Reset test \" << (success ? \"PASSED\" : \"FAILED\") << endl;\n\n\treturn success;\n}\n"
  },
  {
    "path": "FEBioTest/FEResetTest.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreTask.h>\n#include <FECore/DumpMemStream.h>\n\n//-----------------------------------------------------------------------------\nclass FEResetTest : public FECoreTask\n{\npublic:\n\t// constructor\n\tFEResetTest(FEModel* pfem);\n\n\t// initialize the diagnostic\n\tbool Init(const char* sz) override;\n\n\t// run the diagnostic\n\tbool Run() override;\n};\n"
  },
  {
    "path": "FEBioTest/FERestartDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FERestartDiagnostics.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/DumpFile.h>\n#include <FECore/log.h>\n\nFERestartDiagnostic::FERestartDiagnostic(FEModel*pfem) : FECoreTask(pfem), m_dmp(*pfem)\n{\n\tm_bok = false;\n\tm_bfile = false;\n\tstrcpy(m_szdmp, \"out.dmp\");\n}\n\n//-----------------------------------------------------------------------------\nbool restart_test_cb(FEModel* pfem, unsigned int nwen, void* pd)\n{\n\tFERestartDiagnostic* ptask = (FERestartDiagnostic*) pd;\n\n\t// try to create archive\n\tif (ptask->m_bfile)\n\t{\n\t\tDumpFile ar(*pfem);\n\t\tif (ar.Create(ptask->m_szdmp) == false)\n\t\t{\n\t\t\tfeLogErrorEx(pfem, \"FAILED CREATING RESTART DUMP FILE.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// serialize the data\n\t\tpfem->Serialize(ar);\n\n\t\t// close the dump file.\n\t\tar.Close();\n\t}\n\telse\n\t{\n\t\tptask->m_dmp.Open(true, false);\n\t\tpfem->Serialize(ptask->m_dmp);\n\t}\n\n\t// set the ok flag to tell the diagnostic that the restart \n\t// file was created successfully.\n\tptask->m_bok = true;\n\n\t// suppress the error output\n\tpfem->BlockLog();\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// initialize the diagnostic\nbool FERestartDiagnostic::Init(const char* sz)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// copy the file name (if any)\n\tif (sz && (sz[0] != 0)) strcpy(m_szdmp, sz);\n\n\t// Make sure that restart flag is off.\n\t// This is because we are hijacking restart and we don't\n\t// want regular restart to interfere.\n\t// TODO: is this really necessary? This creates a circular link between FEBioTest and FEBioLib\n//\tfem.SetDumpLevel(FE_DUMP_NEVER);\n\n\t// Add the restart callback\n\tfem.AddCallback(restart_test_cb, CB_MAJOR_ITERS, this);\n\n\t// do the FE initialization\n\treturn fem.Init();\n}\n\n//-----------------------------------------------------------------------------\n// run the diagnostic\nbool FERestartDiagnostic::Run()\n{\n\tFEModel* fem = GetFEModel();\n\n\twhile (fem->Solve() == false)\n\t{\n\t\t// reset output mode (it was turned off in restart_test_cb)\n\t\tfem->UnBlockLog();\n\n\t\tif (m_bok)\n\t\t{\n\t\t\tfeLogEx(fem, \"Reading restart file...\");\n\t\t\tif (m_bfile)\n\t\t\t{\n\t\t\t\t// reopen the dump file for readin\n\t\t\t\tDumpFile ar(*fem);\n\t\t\t\tif (ar.Open(m_szdmp) == false)\n\t\t\t\t{\n\t\t\t\t\tfeLogErrorEx(fem, \"FAILED OPENING RESTART DUMP FILE.\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tfem->Serialize(ar);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_dmp.Open(false, false);\n\t\t\t\tfem->Serialize(m_dmp);\n\t\t\t}\n\t\t\tm_bok = false;\n\t\t\tfeLogEx(fem, \"done!\\n\");\n\n\t\t\t// reopen the log file for appending\n/*\t\t\tconst char* szlog = fem.GetLogfileName();\n\t\t\tif (felog.append(szlog) == false)\n\t\t\t{\n\t\t\t\tprintf(\"WARNING: Could not reopen log file. A new log file is created\\n\");\n\t\t\t\tfelog.open(szlog);\n\t\t\t}\n*/\n\t\t}\n\t\telse return false;\n\t}\n\n\treturn true;\n}\n\nFEQuickRestartDiagnostic::FEQuickRestartDiagnostic(FEModel* pfem) : FECoreTask(pfem)\n{\n\tstrcpy(m_szdmp, \"out.dmp\");\n}\n\n// initialize the diagnostic\nbool FEQuickRestartDiagnostic::Init(const char* sz)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// copy the file name (if any)\n\tif (sz && (sz[0] != 0)) strcpy(m_szdmp, sz);\n\n\t// do the FE initialization\n\treturn fem.Init();\n}\n\n// run the diagnostic\nbool FEQuickRestartDiagnostic::Run()\n{\n\tFEModel* fem = GetFEModel();\n\n\t// write the restart file\n\tDumpFile out(*fem);\n\tif (out.Create(m_szdmp) == false)\n\t{\n\t\tfeLogErrorEx(fem, \"Failed creating dump file!\");\n\t\treturn false;\n\t}\n\tfeLogEx(fem, \"Writing dump file...\");\n\tfem->Serialize(out);\n\tout.Close();\n\tfeLogEx(fem, \"done!\\n\");\n\n\t// clear the model\n\tfem->Clear();\n\n\t// reopen the dump file for reading\n\tDumpFile in(*fem);\n\tif (in.Open(m_szdmp) == false)\n\t{\n\t\tfeLogErrorEx(fem, \"failed opening restart dump file.\\n\");\n\t\treturn false;\n\t}\n\tfeLogEx(fem, \"Reading dump file...\");\n\tfem->Serialize(in);\n\tin.Close();\n\tfeLogEx(fem, \"done!\\n\");\n\n\tfeLogEx(fem, \"All is well!\\n\\n\");\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioTest/FERestartDiagnostics.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FECoreTask.h>\n#include <FECore/DumpMemStream.h>\n\n// This diagnostics tests the running and cold restart features\nclass FERestartDiagnostic : public FECoreTask\n{\npublic:\n\t// constructor\n\tFERestartDiagnostic(FEModel* pfem);\n\n\t// initialize the diagnostic\n\tbool Init(const char* sz) override;\n\n\t// run the diagnostic\n\tbool Run() override;\n\npublic:\n\tbool\tm_bok;\n\tbool\tm_bfile;\t\t// file or memory stream?\n\tchar\tm_szdmp[256];\t// restart file name\n\tDumpMemStream\tm_dmp;\n};\n\n// This diagnostics tests simple calls serialize after init\nclass FEQuickRestartDiagnostic : public FECoreTask\n{\npublic:\n\t// constructor\n\tFEQuickRestartDiagnostic(FEModel* pfem);\n\n\t// initialize the diagnostic\n\tbool Init(const char* sz) override;\n\n\t// run the diagnostic\n\tbool Run() override;\n\npublic:\n\tchar\tm_szdmp[256];\t// restart file name\n};\n"
  },
  {
    "path": "FEBioTest/FEStiffnessDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEStiffnessDiagnostic.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FENewtonSolver.h>\n#include <FECore/FEGlobalMatrix.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/log.h>\n#include <FEBioMech/FEMechModel.h>\n#include <FEBioMech/FERigidBody.h>\n#include <FEBioMech/FESolidSolver2.h>\n#include <iostream>\n\n//-----------------------------------------------------------------------------\nFEStiffnessDiagnostic::FEStiffnessDiagnostic(FEModel* fem) : FECoreTask(fem)\n{\n\tm_fp = nullptr;\n\tm_writeMatrix = false;\n\tm_nmax = -1;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FEStiffnessDiagnostic::Init(const char* szarg)\n{\n\tif (szarg && szarg[0])\n\t{\n\t\tif (strcmp(szarg, \"v\") == 0) m_writeMatrix = true;\n\t\telse { m_nmax = atoi(szarg); m_writeMatrix = true; }\n\t}\n\treturn GetFEModel()->Init();\n}\n\n//-----------------------------------------------------------------------------\nbool stiffness_diagnostic_cb(FEModel* fem, unsigned int when, void* pd)\n{\n\tFEStiffnessDiagnostic* diagnostic = (FEStiffnessDiagnostic*)pd;\n\treturn diagnostic->Diagnose();\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate \n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FEStiffnessDiagnostic::Run()\n{\n\t// solve the problem\n\tFEModel& fem = *GetFEModel();\n\n//\tfem.AddCallback(stiffness_diagnostic_cb, CB_MATRIX_REFORM, (void*)this);\n\tfem.AddCallback(stiffness_diagnostic_cb, CB_QUASIN_CONVERGED, (void*)this);\n\n\t// create a file name for the log file\n\tstring logfile(\"diagnostic.log\");\n\tm_fp = fopen(logfile.c_str(), \"wt\");\n\tfprintf(m_fp, \"FEBio Stiffness Diagnostics:\\n\");\n\tfprintf(m_fp, \"============================\\n\");\n\n\tfem.BlockLog();\n\tbool bret = fem.Solve();\n\tfem.UnBlockLog();\n\tif (bret == false)\n\t{\n\t\tfeLogError(\"FEBio error terminated. Aborting diagnostic.\\n\");\n\t\treturn false;\n\t}\n\n\tfprintf(m_fp, \"diagnostic completed.\\n\");\n\n\tfclose(m_fp);\n\tm_fp = nullptr;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEStiffnessDiagnostic::Diagnose()\n{\n\tFEModel* fem = GetFEModel();\n\tFEMechModel* mech = dynamic_cast<FEMechModel*>(fem);\n\n\tFEAnalysis* step = fem->GetCurrentStep();\n\tif (step == nullptr) return false;\n\n\tFESolidSolver2* solver = dynamic_cast<FESolidSolver2*>(step->GetFESolver());\n\tFENewtonSolver* nlsolve = dynamic_cast<FENewtonSolver*>(solver);\n\tif (nlsolve == nullptr) return false;\n\n\tSparseMatrix* pA = nlsolve->m_pK->GetSparseMatrixPtr();\n\tif (pA == nullptr) return false;\n\n\tconst double eps = 1e-8;\n\tint neq = pA->Rows();\n\n\t// need to know which dofs are prescribed\n\t// 0 == fixed, 1 == free\n\tvector<int> bc(neq, 0);\n\tint nmax = -1;\n\tFEMesh& mesh = fem->GetMesh();\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid < 0)\n\t\t{\n\t\t\tfor (int j = 0; j < node.m_ID.size(); ++j)\n\t\t\t{\n\t\t\t\tint n = node.m_ID[j];\n\t\t\t\tif (n >= 0) bc[n] = 1;\n\t\t\t\tif (n > nmax) nmax = n;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (int j = 0; j < node.m_ID.size(); ++j)\n\t\t\t{\n\t\t\t\tint n = -node.m_ID[j]-2;\n\t\t\t\tif (n >= 0) bc[n] = 1;\n\t\t\t\tif (n > nmax) nmax = n;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (mech)\n\t{\n\t\tfor (int i = 0; i < mech->RigidBodies(); ++i)\n\t\t{\n\t\t\tFERigidBody& rb = *mech->GetRigidBody(i);\n\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t{\n\t\t\t\tint n = rb.m_LM[j];\n\t\t\t\tif (n >= 0) bc[n] = 1;\n\t\t\t\tif (n > nmax) nmax = n;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (nmax < neq)\n\t{\n\t\t// these are probably lagrange multiplier dofs\n\t\tfor (int i = nmax + 1; i < neq; ++i) bc[i] = 1;\n\t}\n\n\tstd::vector<double> R0(neq, 0);\n\tnlsolve->Residual(R0);\n\tdouble max_val = 0, max_err = 0.0;\n\tint i_max = -1, j_max = -1;\n\tstd::cerr << \"\\nstarting diagnostic:\\nprogress:\";\n\tint pct = 0;\n\n\tint nreq = (m_nmax <= 0 ? neq : m_nmax);\n\tif (nreq > neq) nreq = neq;\n\n\tfor (int j = 0; j < nreq; ++j)\n\t{\n\t\tstd::vector<double> u(neq, 0);\n\t\tstd::vector<double> R(neq, 0);\n\t\tu[j] = eps;\n\t\tnlsolve->Update(u);\n\t\tnlsolve->Residual(R);\n\n\t\tint new_pct = (100 * j) / neq;\n\t\tif (pct != new_pct) {\n\t\t\tif ((new_pct % 10) == 0)\n\t\t\t\tstd::cerr << \"+\"; \n\t\t\telse\n\t\t\t\tstd::cerr << \"-\"; \n\t\t\tpct = new_pct;\n\t\t}\n\n\t\tfor (int i = 0; i < nreq; ++i)\n\t\t{\n\t\t\t// note that we flip the sign on ka.\n\t\t\t// this is because febio actually calculates the negative of the residual\n\t\t\tdouble ka_ij = 0;\n\t\t\tif ((bc[i] == 0) || (bc[j] == 0))\n\t\t\t{\n\t\t\t\tif (i == j) ka_ij = 1;\n\t\t\t\telse ka_ij = 0;\n\t\t\t}\n\t\t\telse ka_ij = -(R[i] - R0[i]) / eps;\n\n\t\t\tdouble kt_ij = pA->get(i, j);\n\n\t\t\tif (fabs(kt_ij) > max_val) max_val = fabs(kt_ij);\n\n\t\t\tdouble err = fabs(kt_ij - ka_ij);\n\t\t\tif (err > max_err)\n\t\t\t{\n\t\t\t\tmax_err = err;\n\t\t\t\ti_max = i;\n\t\t\t\tj_max = j;\n\t\t\t}\n\n\t\t\tif (m_writeMatrix)\n\t\t\t{\n\t\t\t\tfprintf(m_fp, \"%d, %d : %lg, %lg (%lg)\\n\", i, j, kt_ij, ka_ij, err);\n\t\t\t}\n\t\t}\n\t}\n\tstd::cerr << \"\\n\";\n\n\t// let's make sure we leave the model in a consistent state\n\tstd::vector<double> u(neq, 0);\n\tstd::vector<double> R(neq, 0);\n\tnlsolve->Update(u);\n\tnlsolve->Residual(R);\n\n\tprintf(\"Max abs. value: %lg\\n\", max_val);\n\tfprintf(m_fp, \"Max abs. value: %lg\\n\", max_val);\n\tif (max_val == 0) max_val = 1;\n\n\tprintf(\"Max error: %lg (%d, %d)\\n\", max_err, i_max, j_max);\n\tprintf(\"Max rel. error: %lg (%d, %d)\\n\", max_err / max_val, i_max, j_max);\n\tfprintf(m_fp, \"Max error: %lg (%d, %d)\\n\", max_err, i_max, j_max);\n\tfprintf(m_fp, \"Max rel. error: %lg (%d, %d)\\n\", max_err / max_val, i_max, j_max);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FEStiffnessDiagnostic::deriv_residual(matrix& ke)\n{\n/*\t// get the solver\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tFESolidSolver2& solver = static_cast<FESolidSolver2&>(*pstep->GetFESolver());\n\n\t// get the degrees of freedom\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEElasticSolidDomain& bd = static_cast<FEElasticSolidDomain&>(mesh.Domain(0));\n\n\t// get the one and only element\n\tFESolidElement& el = bd.Element(0);\n\n\t// first calculate the initial residual\n\tvector<double> f0(24);\n\tzero(f0);\n\tbd.ElementInternalForce(el, f0);\n\n\t// now calculate the perturbed residuals\n\tke.resize(24, 24);\n\tke.zero();\n\tint i, j, nj;\n\tint N = mesh.Nodes();\n\tdouble dx = 1e-8;\n\tvector<double> f1(24);\n\tfor (j = 0; j < 3 * N; ++j)\n\t{\n\t\tFENode& node = mesh.Node(el.m_node[j / 3]);\n\t\tnj = j % 3;\n\n\t\tswitch (nj)\n\t\t{\n\t\tcase 0: node.add(dof_X, dx); node.m_rt.x += dx; break;\n\t\tcase 1: node.add(dof_Y, dx); node.m_rt.y += dx; break;\n\t\tcase 2: node.add(dof_Z, dx); node.m_rt.z += dx; break;\n\t\t}\n\n\n\t\tfem.Update();\n\n\t\tzero(f1);\n\t\tbd.ElementInternalForce(el, f1);\n\n\t\tswitch (nj)\n\t\t{\n\t\tcase 0: node.sub(dof_X, dx); node.m_rt.x -= dx; break;\n\t\tcase 1: node.sub(dof_Y, dx); node.m_rt.y -= dx; break;\n\t\tcase 2: node.sub(dof_Z, dx); node.m_rt.z -= dx; break;\n\t\t}\n\n\t\tfem.Update();\n\n\t\tfor (i = 0; i < 3 * N; ++i) ke[i][j] = -(f1[i] - f0[i]) / dx;\n\t}\n*/\n}\n"
  },
  {
    "path": "FEBioTest/FEStiffnessDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FECoreTask.h>\n#include <string>\n\n//-----------------------------------------------------------------------------\nclass FEStiffnessDiagnostic : public FECoreTask\n{\npublic:\n\tFEStiffnessDiagnostic(FEModel* fem);\n\n\tbool Init(const char* szfile) override;\n\n\tbool Run() override;\n\n\tbool Diagnose();\n\nprotected:\n\tvoid deriv_residual(matrix& ke);\n\nprivate:\n\tFILE*\tm_fp;\n\tbool\tm_writeMatrix;\n\tint\t\tm_nmax;\n};\n"
  },
  {
    "path": "FEBioTest/FETangentDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETangentDiagnostic.h\"\n#include \"FEBioMech/FESolidSolver2.h\"\n#include \"FEBioMech/FEElasticSolidDomain.h\"\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FELoadCurve.h>\n#include \"FECore/log.h\"\n#include <FECore/FECoreKernel.h>\n\n// Helper function to print a matrix\nvoid print_matrix(FILE* fp, matrix& m)\n{\n\tint N = m.rows();\n\tint M = m.columns();\n\n\tfprintf(fp, \"\\n    \");\n\tfor (int i=0; i<N; ++i) fprintf(fp, \"%15d \", i);\n\tfprintf(fp, \"\\n----\");\n\tfor (int i=0; i<N; ++i) fprintf(fp, \"----------------\");\n\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tfprintf(fp, \"\\n%2d: \", i);\n\t\tfor (int j=0; j<M; ++j)\n\t\t{\n\t\t\tfprintf(fp, \"%15lg \", m[i][j]);\n\t\t}\n\t}\n\tfprintf(fp, \"\\n\");\n}\n\n// helper function for creating a box mesh\nvoid BuildBoxMesh(FEMesh& mesh, FEMaterial* mat)\n{\n\tconstexpr int N = 8;\n\tvec3d r[N] = {\n\t\tvec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n\t\tvec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1)\n\t};\n\n\tmesh.CreateNodes(N);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tnode.m_rt = node.m_r0 = r[i];\n\t}\n\n\tFE_Element_Spec es;\n\tes.eclass = FE_Element_Class::FE_ELEM_SOLID;\n\tes.eshape = FE_Element_Shape::ET_HEX8;\n\tes.etype = FE_Element_Type::FE_HEX8G8;\n\n\t// create a solid domain\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tFEElasticSolidDomain* pd = dynamic_cast<FEElasticSolidDomain*>(fecore.CreateDomain(es, &mesh, mat));\n\tpd->Create(1, es);\n\tpd->SetMatID(0);\n\tmesh.AddDomain(pd);\n\tFESolidElement& el = pd->Element(0);\n\tel.SetID(1);\n\tfor (int i = 0; i < 8; ++i) el.m_node[i] = i;\n\n\tpd->CreateMaterialPointData();\n}\n\n//////////////////////////////////////////////////////////////////////\n// FETangentUniaxial\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETangentUniaxial, FEDiagnosticScenario)\n\tADD_PARAMETER(m_strain, \"strain\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FETangentUniaxial::Init()\n{\n\tFEModel& fem = *GetDiagnostic()->GetFEModel();\n\n\tint BC[8][3] = {\n\t\t{-1,-1,-1},{ 0,-1,-1},{ 0, 0,-1}, {-1, 0,-1},\n\t\t{-1,-1, 0},{ 0,-1, 0},{ 0, 0, 0}, {-1, 0, 0}\n\t};\n\n\t// --- create the FE problem ---\n\t// get the material (must already be defined)\n\tif (fem.Materials() == 0) return false;\n\tFEMaterial* pmat = fem.GetMaterial(0);\n\n\t// create the mesh\n\tFEMesh& m = fem.GetMesh();\n\tBuildBoxMesh(m, pmat);\n\n\t// build boundary conditions\n\tFENodeSet* nset[3];\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\n\t// get the degrees of freedom\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\n\tm.SetDOFS(MAX_DOFS);\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\tFENode& n = m.Node(i);\n\t\t// set displacement BC's\n\t\tif (BC[i][0] == -1) nset[0]->Add(i);\n\t\tif (BC[i][1] == -1) nset[1]->Add(i);\n\t\tif (BC[i][2] == -1) nset[2]->Add(i);\n\t}\n\n\tFEFixedBC* bc_x = new FEFixedBC(&fem, dof_X, nset[0]); fem.AddBoundaryCondition(bc_x);\n\tFEFixedBC* bc_y = new FEFixedBC(&fem, dof_Y, nset[1]); fem.AddBoundaryCondition(bc_y);\n\tFEFixedBC* bc_z = new FEFixedBC(&fem, dof_Z, nset[2]); fem.AddBoundaryCondition(bc_z);\n\n\t// get the simulation time\n\tFEAnalysis* step = fem.GetStep(0);\n\tdouble tend = step->m_ntime * step->m_dt0;\n\n\t// Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(tend, 1.0);\n\tfem.AddLoadController(plc);\n\n\t// Add a prescribed BC\n\tstd::vector<int> prescribedNodes = {1, 2, 5, 6};\n\tFENodeSet* dc = new FENodeSet(&fem);\n\tdc->Add(prescribedNodes);\n\n\t// convert strain to a displacement\n\tdouble d = sqrt(2 * m_strain + 1) - 1;\n\n\tFEPrescribedDOF* pdc = new FEPrescribedDOF(&fem, dof_X, dc);\n\tpdc->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdc);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETangentSimpleShear, FEDiagnosticScenario)\n\tADD_PARAMETER(m_strain, \"strain\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FETangentSimpleShear::Init()\n{\n\tFEModel& fem = *GetDiagnostic()->GetFEModel();\n\n\tint BC[8][3] = {\n\t\t{-1,-1,-1},{-1,-1,-1},{-1, 0,-1}, {-1, 0,-1},\n\t\t{ 0,-1,-1},{ 0,-1,-1},{ 0, 0,-1}, { 0, 0,-1}\n\t};\n\n\t// get the degrees of freedom\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\n\t// --- create the FE problem ---\n\t// get the material (must already be defined)\n\tif (fem.Materials() == 0) return false;\n\tFEMaterial* pmat = fem.GetMaterial(0);\n\t \n\t// create the mesh\n\tFEMesh& m = fem.GetMesh();\n\tBuildBoxMesh(m, pmat);\n\n\tm.SetDOFS(MAX_DOFS);\n\tFENodeSet* nset[3];\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\n\tfor (int i = 0; i<8; ++i)\n\t{\n\t\tFENode& n = m.Node(i);\n\t\t// set displacement BC's\n\t\tif (BC[i][0] == -1) nset[0]->Add(i);\n\t\tif (BC[i][1] == -1) nset[1]->Add(i);\n\t\tif (BC[i][2] == -1) nset[2]->Add(i);\n\t}\n\n\tFEFixedBC* bc_x = new FEFixedBC(&fem, dof_X, nset[0]); fem.AddBoundaryCondition(bc_x);\n\tFEFixedBC* bc_y = new FEFixedBC(&fem, dof_Y, nset[1]); fem.AddBoundaryCondition(bc_y);\n\tFEFixedBC* bc_z = new FEFixedBC(&fem, dof_Z, nset[2]); fem.AddBoundaryCondition(bc_z);\n\n\t// get the simulation time\n\tFEAnalysis* step = fem.GetStep(0);\n\tdouble tend = step->m_ntime * step->m_dt0;\n\n\t// Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(tend, 1.0);\n\tfem.AddLoadController(plc);\n\n\t// Add a prescribed BC\n\tstd::vector<int> precribedNodes = { 4, 5, 6, 7 };\n\tFENodeSet* dc = new FENodeSet(&fem);\n\tdc->Add(precribedNodes);\n\n\t// convert strain to a displacement\n\tdouble d = 2 * m_strain;\n\n\tFEPrescribedDOF* pdc = new FEPrescribedDOF(&fem, dof_X, dc);\n\tpdc->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdc);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETangentBiaxial, FEDiagnosticScenario)\n\tADD_PARAMETER(m_strain, \"strain\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nbool FETangentBiaxial::Init()\n{\n\tFEModel& fem = *GetDiagnostic()->GetFEModel();\n\n\tint BC[8][3] = {\n\t\t{-1,-1,-1},{ 0,-1,-1},{ 0, 0,-1}, {-1, 0,-1},\n\t\t{-1,-1, 0},{ 0,-1, 0},{ 0, 0, 0}, {-1, 0, 0}\n\t};\n\n\t// --- create the FE problem ---\n\t// get the material (must already be defined)\n\tif (fem.Materials() == 0) return false;\n\tFEMaterial* pmat = fem.GetMaterial(0);\n\n\t// create the mesh\n\tFEMesh& m = fem.GetMesh();\n\tBuildBoxMesh(m, pmat);\n\n\t// build boundary conditions\n\tFENodeSet* nset[3];\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\n\t// get the degrees of freedom\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\n\tm.SetDOFS(MAX_DOFS);\n\tfor (int i = 0; i < 8; ++i)\n\t{\n\t\tFENode& n = m.Node(i);\n\t\t// set displacement BC's\n\t\tif (BC[i][0] == -1) nset[0]->Add(i);\n\t\tif (BC[i][1] == -1) nset[1]->Add(i);\n\t\tif (BC[i][2] == -1) nset[2]->Add(i);\n\t}\n\n\tFEFixedBC* bc_x = new FEFixedBC(&fem, dof_X, nset[0]); fem.AddBoundaryCondition(bc_x);\n\tFEFixedBC* bc_y = new FEFixedBC(&fem, dof_Y, nset[1]); fem.AddBoundaryCondition(bc_y);\n\tFEFixedBC* bc_z = new FEFixedBC(&fem, dof_Z, nset[2]); fem.AddBoundaryCondition(bc_z);\n\n\t// get the simulation time\n\tFEAnalysis* step = fem.GetStep(0);\n\tdouble tend = step->m_ntime * step->m_dt0;\n\n\t// Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(tend, 1.0);\n\tfem.AddLoadController(plc);\n\n\t// Add two prescribed BCs, one for X and one for Y\n\tFENodeSet* dcx = new FENodeSet(&fem);\n\tdcx->Add({ 1, 2, 5, 6 });\n\n\tFENodeSet* dcy = new FENodeSet(&fem);\n\tdcy->Add({ 2, 3, 6, 7 });\n\n\t// convert strain to a displacement\n\tdouble d = sqrt(2 * m_strain + 1) - 1;\n\n\tFEPrescribedDOF* pdcx = new FEPrescribedDOF(&fem, dof_X, dcx);\n\tpdcx->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdcx);\n\n\tFEPrescribedDOF* pdcy = new FEPrescribedDOF(&fem, dof_Y, dcy);\n\tpdcy->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdcy);\n\n\treturn true;\n}\n\nBEGIN_FECORE_CLASS(FETangentTriaxial, FEDiagnosticScenario)\n\tADD_PARAMETER(m_strain, \"strain\");\nEND_FECORE_CLASS();\n\nbool FETangentTriaxial::Init()\n{\n\tFEModel& fem = *GetDiagnostic()->GetFEModel();\n\n\tint BC[8][3] = {\n\t\t{-1,-1,-1},{ 0,-1,-1},{ 0, 0,-1}, {-1, 0,-1},\n\t\t{-1,-1, 0},{ 0,-1, 0},{ 0, 0, 0}, {-1, 0, 0}\n\t};\n\n\t// --- create the FE problem ---\n\t// get the material (must already be defined)\n\tif (fem.Materials() == 0) return false;\n\tFEMaterial* pmat = fem.GetMaterial(0);\n\n\t// create the mesh\n\tFEMesh& m = fem.GetMesh();\n\tBuildBoxMesh(m, pmat);\n\n\t// build boundary conditions\n\tFENodeSet* nset[3];\n\tnset[0] = new FENodeSet(&fem);\n\tnset[1] = new FENodeSet(&fem);\n\tnset[2] = new FENodeSet(&fem);\n\n\t// get the degrees of freedom\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\n\tm.SetDOFS(MAX_DOFS);\n\tfor (int i = 0; i < 8; ++i)\n\t{\n\t\tFENode& n = m.Node(i);\n\t\t// set displacement BC's\n\t\tif (BC[i][0] == -1) nset[0]->Add(i);\n\t\tif (BC[i][1] == -1) nset[1]->Add(i);\n\t\tif (BC[i][2] == -1) nset[2]->Add(i);\n\t}\n\n\tFEFixedBC* bc_x = new FEFixedBC(&fem, dof_X, nset[0]); fem.AddBoundaryCondition(bc_x);\n\tFEFixedBC* bc_y = new FEFixedBC(&fem, dof_Y, nset[1]); fem.AddBoundaryCondition(bc_y);\n\tFEFixedBC* bc_z = new FEFixedBC(&fem, dof_Z, nset[2]); fem.AddBoundaryCondition(bc_z);\n\n\t// get the simulation time\n\tFEAnalysis* step = fem.GetStep(0);\n\tdouble tend = step->m_ntime * step->m_dt0;\n\n\t// Add a loadcurve\n\tFELoadCurve* plc = new FELoadCurve(&fem);\n\tplc->Add(0.0, 0.0);\n\tplc->Add(tend, 1.0);\n\tfem.AddLoadController(plc);\n\n\t// Add 3 prescribed BCs, one for X, Y, and Z\n\tFENodeSet* dcx = new FENodeSet(&fem);\n\tdcx->Add({ 1, 2, 5, 6 });\n\n\tFENodeSet* dcy = new FENodeSet(&fem);\n\tdcy->Add({ 2, 3, 6, 7 });\n\n\tFENodeSet* dcz = new FENodeSet(&fem);\n\tdcz->Add({ 4, 5, 6, 7 });\n\n\t// convert strain to a displacement\n\tdouble d = sqrt(2 * m_strain + 1) - 1;\n\n\tFEPrescribedDOF* pdcx = new FEPrescribedDOF(&fem, dof_X, dcx);\n\tpdcx->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdcx);\n\n\tFEPrescribedDOF* pdcy = new FEPrescribedDOF(&fem, dof_Y, dcy);\n\tpdcy->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdcy);\n\n\tFEPrescribedDOF* pdcz = new FEPrescribedDOF(&fem, dof_Z, dcz);\n\tpdcz->SetScale(d, 0);\n\tfem.AddBoundaryCondition(pdcz);\n\n\treturn true;\n}\n\nFETangentDiagnostic::FETangentDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"solid\");\n\n\tm_pscn = 0;\n\n\t// create an analysis step\n\tFEAnalysis* pstep = new FEAnalysis(fem);\n\n\t// create a new solver\n\tFENewtonSolver* pnew_solver = dynamic_cast<FENewtonSolver*>(fecore_new<FESolver>(\"solid\", fem));\n\tassert(pnew_solver);\n\tpnew_solver->SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"full Newton\", fem));\n\t\n\tpstep->SetFESolver(pnew_solver);\n\n\t// keep a pointer to the fem object\n    fem->AddStep(pstep);\n    fem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FETangentDiagnostic::CreateScenario(const std::string& sname)\n{\n\tif (sname == \"uni-axial\"   ) return (m_pscn = new FETangentUniaxial   (this));\n\tif (sname == \"simple shear\") return (m_pscn = new FETangentSimpleShear(this));\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FETangentDiagnostic::Init()\n{\n\t// make sure we have a scenario\n\tif (m_pscn == 0) return false;\n\n\t// initialize the scenario\n\tif (m_pscn->Init() == false) return false;\n\n\treturn FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\n// Run the tangent diagnostic. After we run the FE model, we calculate \n// the element stiffness matrix and compare that to a finite difference\n// of the element residual.\nbool FETangentDiagnostic::Run()\n{\n\t// solve the problem\n\tFEModel& fem = *GetFEModel();\n\tfem.BlockLog();\n\tbool bret = fem.Solve();\n\tfem.UnBlockLog();\n\tif (bret == false)\n\t{\n\t\tfeLogError(\"FEBio error terminated. Aborting diagnostic.\\n\");\n\t\treturn false;\n\t}\n\n\tFEMesh& mesh = fem.GetMesh();\n\tFEElasticSolidDomain& bd = static_cast<FEElasticSolidDomain&>(mesh.Domain(0));\n\n\t// get the one and only element\n\tFESolidElement& el = bd.Element(0);\n\n\t// set up the element stiffness matrix\n\tmatrix k0(24, 24);\n\tk0.zero();\n\tbd.ElementStiffness(fem.GetTime(), 0, k0);\n\n\t// create a file name for the tangent log file\n\tstd::string febFile = GetFileName();\n\tstring fileName = febFile;\n\tsize_t n = fileName.rfind('.');\n\tif (n != string::npos) fileName.erase(n);\n\tfileName.append(\"_out.log\");\n\n\tFILE* fp = fopen(fileName.c_str(), \"wt\");\n\n\tfprintf(fp, \"FEBio Tangent Diagnostics Results:\\n\");\n\tfprintf(fp, \"==================================\\n\");\n\tfprintf(fp, \"Diagnostics file: %s\\n\\n\", febFile.c_str());\n\n\t// print the element stiffness matrix\n\tfprintf(fp, \"\\nActual stiffness matrix:\\n\");\n\tprint_matrix(fp, k0);\n\n\t// now calculate the derivative of the residual\n\tmatrix k1;\n\tderiv_residual(k1);\n\n\t// print the approximate element stiffness matrix\n\tfprintf(fp, \"\\nApproximate stiffness matrix:\\n\");\n\tprint_matrix(fp, k1);\n\n\t// finally calculate the difference matrix\n\tfprintf(fp, \"\\n\");\n\tmatrix kd(24, 24);\n\tdouble kmax = 0, kij;\n\tint i0 = -1, j0 = -1, i, j;\n\tfor (i=0; i<24; ++i)\n\t\tfor (j=0; j<24; ++j)\n\t\t{\n\t\t\tkd[i][j] = k0[i][j] - k1[i][j];\n\t\t\tkij = 100.0*fabs(kd[i][j] / k0[0][0]);\n\t\t\tif (kij > kmax) \n\t\t\t{\n\t\t\t\tkmax = kij;\n\t\t\t\ti0 = i;\n\t\t\t\tj0 = j;\n\t\t\t}\n\t\t}\n\n\t// print the difference\n\tfprintf(fp, \"\\ndifference matrix:\\n\");\n\tprint_matrix(fp, kd);\n\n\tfprintf(fp, \"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n\tfprintf(stderr, \"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n\n\tfclose(fp);\n\n\treturn (kmax < 1e-4);\n}\n\n//-----------------------------------------------------------------------------\n// Calculate a finite difference approximation of the derivative of the\n// element residual.\nvoid FETangentDiagnostic::deriv_residual(matrix& ke)\n{\n\t// get the solver\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\tFESolidSolver2& solver = static_cast<FESolidSolver2&>(*pstep->GetFESolver());\n\n\t// get the degrees of freedom\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\n\t// get the mesh\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEElasticSolidDomain& bd = static_cast<FEElasticSolidDomain&>(mesh.Domain(0));\n\n\t// get the one and only element\n\tFESolidElement& el = bd.Element(0);\n\n\t// first calculate the initial residual\n\tvector<double> f0(24);\n\tzero(f0);\n\tbd.ElementInternalForce(el, f0);\n\n\t// now calculate the perturbed residuals\n\tke.resize(24, 24);\n\tke.zero();\n\tint i, j, nj;\n\tint N = mesh.Nodes();\n\tdouble dx = 1e-8;\n\tvector<double> f1(24);\n\tfor (j=0; j<3*N; ++j)\n\t{\n\t\tFENode& node = mesh.Node(el.m_node[j/3]);\n\t\tnj = j%3;\n\n\t\tswitch (nj)\n\t\t{\n\t\tcase 0: node.add(dof_X, dx); node.m_rt.x += dx; break;\n\t\tcase 1: node.add(dof_Y, dx); node.m_rt.y += dx; break;\n\t\tcase 2: node.add(dof_Z, dx); node.m_rt.z += dx; break;\n\t\t}\n\n\n\t\tfem.Update();\n\n\t\tzero(f1);\n\t\tbd.ElementInternalForce(el, f1);\n\n\t\tswitch (nj)\n\t\t{\n\t\tcase 0: node.sub(dof_X, dx); node.m_rt.x -= dx; break;\n\t\tcase 1: node.sub(dof_Y, dx); node.m_rt.y -= dx; break;\n\t\tcase 2: node.sub(dof_Z, dx); node.m_rt.z -= dx; break;\n\t\t}\n\n\t\tfem.Update();\n\n\t\tfor (i=0; i<3*N; ++i) ke[i][j] = -(f1[i] - f0[i])/dx;\n\t}\n}\n"
  },
  {
    "path": "FEBioTest/FETangentDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDiagnostic.h\"\n\n//-----------------------------------------------------------------------------\nclass FETangentUniaxial : public FEDiagnosticScenario\n{\npublic:\n\tFETangentUniaxial(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_strain = 0.0; }\n\n\tbool Init() override;\n\nprivate:\n\tdouble\tm_strain;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FETangentSimpleShear : public FEDiagnosticScenario\n{\npublic:\n\tFETangentSimpleShear(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_strain = 0.0; }\n\n\tbool Init() override;\n\nprivate:\n\tdouble\tm_strain;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FETangentBiaxial : public FEDiagnosticScenario\n{\npublic:\n\tFETangentBiaxial(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_strain = 0.0; }\n\n\tbool Init() override;\n\nprivate:\n\tdouble\tm_strain;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FETangentTriaxial : public FEDiagnosticScenario\n{\npublic:\n\tFETangentTriaxial(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_strain = 0.0; }\n\n\tbool Init() override;\n\nprivate:\n\tdouble\tm_strain;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! The FETangentDiagnostic class tests the stiffness matrix implementation\n//! by comparing it to a numerical approximating of the derivative of the\n//! residual.\n\nclass FETangentDiagnostic : public FEDiagnostic  \n{\npublic:\n\tFETangentDiagnostic(FEModel* fem);\n\n\tFEDiagnosticScenario* CreateScenario(const std::string& sname);\n\n\tbool Init();\n\n\tbool Run();\n\nprotected:\n\tvoid deriv_residual(matrix& ke);\n\npublic:\n\tFEDiagnosticScenario* m_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/FETiedBiphasicDiagnostic.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETiedBiphasicDiagnostic.h\"\n#include \"FEBioMix/FEBiphasicSolver.h\"\n#include \"FEBioMix/FEBiphasicSolidDomain.h\"\n#include \"FEBioMix/FETiedBiphasicInterface.h\"\n#include \"FEBioMech/FEResidualVector.h\"\n#include <FECore/log.h>\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFETiedBiphasicDiagnostic::FETiedBiphasicDiagnostic(FEModel* fem) : FEDiagnostic(fem)\n{\n\t// make sure the correct module is active\n\tfem->SetActiveModule(\"biphasic\");\n\n    m_pscn = 0;\n    \n    FEAnalysis* pstep = new FEAnalysis(fem);\n    \n    // create a new solver\n    FESolver* pnew_solver = fecore_new<FESolver>(\"biphasic\", fem);\n    assert(pnew_solver);\n\tpnew_solver->m_msymm = REAL_UNSYMMETRIC;\n\tpstep->SetFESolver(pnew_solver);\n\n\tfem->AddStep(pstep);\n\tfem->SetCurrentStep(pstep);\n}\n\n//-----------------------------------------------------------------------------\nFETiedBiphasicDiagnostic::~FETiedBiphasicDiagnostic()\n{\n    \n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a matrix\nvoid FETiedBiphasicDiagnostic::print_matrix(matrix& m)\n{\n    int i, j;\n    int N = m.rows();\n    int M = m.columns();\n    \n    feLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n\tfeLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n\t\tfeLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n\t\t\tfeLog(\"%15lg \", m[i][j]);\n        }\n    }\n\tfeLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Helper function to print a sparse matrix\nvoid FETiedBiphasicDiagnostic::print_matrix(SparseMatrix& m)\n{\n    int i, j;\n\tint N = m.Rows();\n    int M = m.Columns();\n    \n\tfeLog(\"\\n    \");\n    for (i=0; i<N; ++i) feLog(\"%15d \", i);\n\tfeLog(\"\\n----\");\n    for (i=0; i<N; ++i) feLog(\"----------------\", i);\n    \n    for (i=0; i<N; ++i)\n    {\n\t\tfeLog(\"\\n%2d: \", i);\n        for (j=0; j<M; ++j)\n        {\n\t\t\tfeLog(\"%15lg \", m.get(i,j));\n        }\n    }\n\tfeLog(\"\\n\");\n}\n\n//-----------------------------------------------------------------------------\n// Initialize the diagnostic. In this function we build the FE model depending\n// on the scenario.\nbool FETiedBiphasicDiagnostic::Init()\n{\n    if (m_pscn == 0) return false;\n    \n    if (m_pscn->Init() == false) return false;\n    \n    return FEDiagnostic::Init();\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedBiphasicDiagnostic::Run()\n{\n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n    fem.GetTime().timeIncrement = pstep->m_dt0 =dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    pstep->Activate();\n    FEBiphasicSolver& solver = static_cast<FEBiphasicSolver&>(*pstep->GetFESolver());\n    solver.m_msymm = REAL_UNSYMMETRIC;\n    solver.Init();\n    \n    // make sure contact data is up to data\n    fem.Update();\n    \n    // create the stiffness matrix\n    solver.CreateStiffness(true);\n    \n    // get the stiffness matrix\n    FEGlobalMatrix& K = *solver.GetStiffnessMatrix();\n    SparseMatrix& K0 = *K;\n    \n    // build the stiffness matrix\n    K.Zero();\n\n    print_matrix(K0);\n    \n    // calculate the derivative of the residual\n    matrix K1;\n    deriv_residual(K1);\n    \n    print_matrix(K1);\n    \n    // calculate difference matrix\n    int ndpn = 4;\n    int N = mesh.Nodes();\n    const int ndof = N*ndpn;\n    matrix Kd; Kd.resize(ndof, ndof);\n    double kij, kmax = 0, k0;\n    int i0=-1, j0=-1;\n    for (int i=0; i<ndof; ++i)\n        for (int j=0; j<ndof; ++j)\n        {\n            Kd(i,j) = K1(i,j) - K0.get(i,j);\n            k0 = fabs(K0.get(i,j));\n            if (k0 < 1e-15) k0 = 1;\n            kij = 100.0*fabs(Kd(i,j))/k0;\n            if (kij > kmax)\n            {\n                kmax = kij;\n                i0 = i;\n                j0 = j;\n            }\n        }\n    \n    print_matrix(Kd);\n    \n\tfeLog(\"\\nMaximum difference: %lg%% (at (%d,%d))\\n\", kmax, i0, j0);\n    \n    return (kmax < 1e-3);\n}\n\n//-----------------------------------------------------------------------------\nvoid FETiedBiphasicDiagnostic::deriv_residual(matrix& K)\n{\n    // get the solver\n    FEModel& fem = *GetFEModel();\n    FEAnalysis* pstep = fem.GetCurrentStep();\n    double dt = m_pscn->m_dt;\n\tfem.GetTime().timeIncrement = pstep->m_dt0 = dt;\n    pstep->m_tstart = 0;\n    pstep->m_tend = dt;\n    pstep->m_final_time = dt;\n    FEBiphasicSolver& solver = static_cast<FEBiphasicSolver&>(*pstep->GetFESolver());\n    \n    // get the DOFs\n    const int dof_x = fem.GetDOFIndex(\"x\");\n    const int dof_y = fem.GetDOFIndex(\"y\");\n    const int dof_z = fem.GetDOFIndex(\"z\");\n    const int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // get the mesh\n    FEMesh& mesh = fem.GetMesh();\n    int ndpn = 4;\n    int N = mesh.Nodes();\n    int ndof = N*ndpn;\n    \n    solver.UpdateModel();\n    \n    // first calculate the initial residual\n    vector<double> R0; R0.assign(ndof, 0);\n    vector<double> dummy(R0);\n    FEResidualVector RHS0(fem, R0, dummy);\n    solver.ContactForces(RHS0);\n    \n    // now calculate the perturbed residuals\n    K.resize(ndof,ndof);\n    int i, j, nj;\n    double dx = 1e-8;\n    vector<double> R1(ndof);\n    for (j=0; j<ndof; ++j)\n    {\n        FENode& node = mesh.Node(j/ndpn);\n        nj = j%ndpn;\n        \n        switch (nj)\n        {\n            case 0: node.add(dof_x, dx); node.m_rt.x += dx; break;\n            case 1: node.add(dof_y, dx); node.m_rt.y += dx; break;\n            case 2: node.add(dof_z, dx); node.m_rt.z += dx; break;\n            case 3: node.add(dof_p, dx); break;\n        }\n        \n        solver.UpdateModel();\n        \n        zero(R1);\n        FEResidualVector RHS1(fem, R1, dummy);\n        solver.ContactForces(RHS1);\n        \n        switch (nj)\n        {\n            case 0: node.sub(dof_x, dx); node.m_rt.x -= dx; break;\n            case 1: node.sub(dof_y, dx); node.m_rt.y -= dx; break;\n            case 2: node.sub(dof_z, dx); node.m_rt.z -= dx; break;\n            case 3: node.sub(dof_p, dx); break;\n        }\n        \n        solver.UpdateModel();\n        \n        for (i=0; i<ndof; ++i) K(i,j) = (R0[i] - R1[i])/dx;\n    }\n}\n\n//-----------------------------------------------------------------------------\nFEDiagnosticScenario* FETiedBiphasicDiagnostic::CreateScenario(const std::string& sname)\n{\n    if (sname == \"hex8\") return (m_pscn = new FETiedBiphasicTangentHex8(this));\n    else if (sname == \"hex20\") return (m_pscn = new FETiedBiphasicTangentHex20(this));\n    return 0;\n}\n\n//////////////////////////////////////////////////////////////////////\n// Biphasic Contact Tangent Diagnostic for hex8 Elements\n//////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETiedBiphasicTangentHex8, FETiedBiphasicScenario)\n\tADD_PARAMETER(m_dt, \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETiedBiphasicTangentHex8::FETiedBiphasicTangentHex8(FEDiagnostic* pdia) : FETiedBiphasicScenario(pdia)\n{\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedBiphasicTangentHex8::Init()\n{\n    vec3d r[16] = {\n        vec3d(0,0,0), vec3d(1,0,0), vec3d(1,1,0), vec3d(0,1,0),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1),\n        vec3d(0,0,1), vec3d(1,0,1), vec3d(1,1,1), vec3d(0,1,1),\n        vec3d(0,0,2), vec3d(1,0,2), vec3d(1,1,2), vec3d(0,1,2)\n    };\n    \n    double p[16] = {\n        1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // --- create the geometry ---\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n    \n    // get the DOFs\n    const int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // currently we simply assume a two-element tied interface problem\n    // so we create two elements\n    mesh.CreateNodes(16);\n    mesh.SetDOFS(MAX_DOFS);\n    for (int i=0; i<16; ++i) {\n        mesh.Node(i).m_r0 = r[i];\n        mesh.Node(i).set(dof_p, p[i]);\n    }\n    \n    for (int i=0; i<16; ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.m_rt = node.m_r0;\n    }\n    \n    // get the material\n    FEMaterial* pm = fem.GetMaterial(0);\n    \n    // get the one-and-only domain\n    FEBiphasicSolidDomain* pbd = new FEBiphasicSolidDomain(&fem);\n    pbd->SetMaterial(pm);\n    pbd->Create(2, FEElementLibrary::GetElementSpecFromType(FE_HEX8G8));\n    pbd->SetMatID(0);\n    mesh.AddDomain(pbd);\n    \n    FESolidElement& el0 = pbd->Element(0);\n    FESolidElement& el1 = pbd->Element(1);\n    \n    el0.SetID(1);\n    el0.m_node[0] = 0;\n    el0.m_node[1] = 1;\n    el0.m_node[2] = 2;\n    el0.m_node[3] = 3;\n    el0.m_node[4] = 4;\n    el0.m_node[5] = 5;\n    el0.m_node[6] = 6;\n    el0.m_node[7] = 7;\n    \n    el1.SetID(2);\n    el1.m_node[0] = 8;\n    el1.m_node[1] = 9;\n    el1.m_node[2] = 10;\n    el1.m_node[3] = 11;\n    el1.m_node[4] = 12;\n    el1.m_node[5] = 13;\n    el1.m_node[6] = 14;\n    el1.m_node[7] = 15;\n    \n    pbd->CreateMaterialPointData();\n    \n    // --- create the tied interface ---\n    FETiedBiphasicInterface* ps = new FETiedBiphasicInterface(&fem);\n    ps->m_atol = 0.1;\n    ps->m_epsn = 1;\n    ps->m_epsp = 1;\n    ps->m_btwo_pass = false;\n    ps->m_bautopen = true;\n    ps->m_bsymm = false;\n    ps->m_stol = 0.01;\n    FETiedBiphasicSurface& ms = ps->m_ms;\n    ms.Create(1, FE_QUAD4G4);\n    ms.Element(0).m_node[0] = 4;\n    ms.Element(0).m_node[1] = 5;\n    ms.Element(0).m_node[2] = 6;\n    ms.Element(0).m_node[3] = 7;\n    FETiedBiphasicSurface& ss = ps->m_ss;\n    ss.Create(1, FE_QUAD4G4);\n    ss.Element(0).m_node[0] = 11;\n    ss.Element(0).m_node[1] = 10;\n    ss.Element(0).m_node[2] = 9;\n    ss.Element(0).m_node[3] = 8;\n    fem.AddSurfacePairConstraint(ps);\n    \n    return true;\n}\n\n//////////////////////////////////////////////////////////////////////\n// Tied Biphasic Interface Tangent Diagnostic for hex20 Elements\n//////////////////////////////////////////////////////////////////////\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETiedBiphasicTangentHex20, FETiedBiphasicScenario)\n\tADD_PARAMETER(m_dt, \"time_step\"     );\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETiedBiphasicTangentHex20::FETiedBiphasicTangentHex20(FEDiagnostic* pdia) : FETiedBiphasicScenario(pdia)\n{\n    m_dt = 1;\n}\n\n//-----------------------------------------------------------------------------\nbool FETiedBiphasicTangentHex20::Init()\n{\n    vec3d r[40] = {\n        vec3d(0,0,0), vec3d(0,0,0.5), vec3d(0,0,1), vec3d(0,0.5,0),\n        vec3d(0,0.5,1), vec3d(0,1,0), vec3d(0,1,0.5), vec3d(0,1,1),\n        vec3d(0.5,0,0), vec3d(0.5,0,1), vec3d(0.5,1,0), vec3d(0.5,1,1),\n        vec3d(1,0,0), vec3d(1,0,0.5), vec3d(1,0,1), vec3d(1,0.5,0),\n        vec3d(1,0.5,1), vec3d(1,1,0), vec3d(1,1,0.5), vec3d(1,1,1),\n        vec3d(0,0,1), vec3d(0,0,1.5), vec3d(0,0,2), vec3d(0,0.5,1),\n        vec3d(0,0.5,2), vec3d(0,1,1), vec3d(0,1,1.5), vec3d(0,1,2),\n        vec3d(0.5,0,1), vec3d(0.5,0,2), vec3d(0.5,1,1), vec3d(0.5,1,2),\n        vec3d(1,0,1), vec3d(1,0,1.5), vec3d(1,0,2), vec3d(1,0.5,1),\n        vec3d(1,0.5,2), vec3d(1,1,1), vec3d(1,1,1.5), vec3d(1,1,2),\n    };\n    \n    double p[40] = {\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n    };\n    \n    int el0n[20] = {\n        1,    13,    18,     6,     3,    15,    20,     8,     9,    16,\n        11,     4,    10,    17,    12,     5,     2,    14,    19,     7\n    };\n    \n    int el1n[20] = {\n        21,    33,    38,    26,    23,    35,    40,    28,    29,    36,\n        31,    24,    30,    37,    32,    25,    22,    34,    39,    27\n    };\n    \n    int msn[8] = {\n        21,    26,    38,    33,    24,    31,    36,    29\n    };\n    \n    int ssn[9] = {\n        3,    15,    20,     8,    10,    17,    12,     5\n    };\n    \n    FEModel& fem = *GetDiagnostic()->GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n    \n    // --- create the geometry ---\n    int MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n    \n    // get the DOFs\n    const int dof_p = fem.GetDOFIndex(\"p\");\n    \n    // currently we simply assume a two-element tied interface problem\n    // so we create two elements\n    mesh.CreateNodes(40);\n    mesh.SetDOFS(MAX_DOFS);\n    for (int i=0; i<40; ++i) {\n        mesh.Node(i).m_r0 = r[i];\n        mesh.Node(i).set(dof_p, p[i]);\n    }\n    \n    for (int i=0; i<40; ++i)\n    {\n        FENode& node = mesh.Node(i);\n        node.m_rt = node.m_r0;\n    }\n    \n    // get the material\n    FEMaterial* pm = fem.GetMaterial(0);\n    \n    // get the one-and-only domain\n    FEBiphasicSolidDomain* pbd = new FEBiphasicSolidDomain(&fem);\n    pbd->SetMaterial(pm);\n    pbd->Create(2, FEElementLibrary::GetElementSpecFromType(FE_HEX20G27));\n    pbd->SetMatID(0);\n    mesh.AddDomain(pbd);\n    \n    FESolidElement& el0 = pbd->Element(0);\n    FESolidElement& el1 = pbd->Element(1);\n    \n    el0.SetID(1);\n    for (int i=0; i<20; ++i) {\n        el0.m_node[i] = el0n[i] - 1;\n    }\n    \n    el1.SetID(2);\n    for (int i=0; i<20; ++i) {\n        el1.m_node[i] = el1n[i] - 1;\n    }\n    \n    pbd->CreateMaterialPointData();\n    \n    // --- create the tied interface ---\n    FETiedBiphasicInterface* ps = new FETiedBiphasicInterface(&fem);\n    ps->m_atol = 0.1;\n    ps->m_epsn = 1;\n    ps->m_epsp = 1;\n    ps->m_btwo_pass = false;\n    ps->m_bautopen = true;\n    ps->m_bsymm = false;\n    FETiedBiphasicSurface& ms = ps->m_ms;\n    ms.Create(1, FE_QUAD8G9);\n    for (int i=0; i<8; ++i)\n        ms.Element(0).m_node[i] = msn[i] - 1;\n    FETiedBiphasicSurface& ss = ps->m_ss;\n    ss.Create(1, FE_QUAD8G9);\n    for (int i=0; i<8; ++i)\n        ss.Element(0).m_node[i] = ssn[i] - 1;\n    fem.AddSurfacePairConstraint(ps);\n    \n    return true;\n}\n"
  },
  {
    "path": "FEBioTest/FETiedBiphasicDiagnostic.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDiagnostic.h\"\n\nclass SparseMatrix;\n\n//-----------------------------------------------------------------------------\nclass FETiedBiphasicScenario : public FEDiagnosticScenario\n{\npublic:\n    FETiedBiphasicScenario(FEDiagnostic* pdia) : FEDiagnosticScenario(pdia) { m_dt = 1.0; }\n    \npublic:\n    double\tm_dt;\n};\n\n//-----------------------------------------------------------------------------\nclass FETiedBiphasicTangentHex8 : public FETiedBiphasicScenario\n{\npublic:\n    FETiedBiphasicTangentHex8(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FETiedBiphasicTangentHex20 : public FETiedBiphasicScenario\n{\npublic:\n    FETiedBiphasicTangentHex20(FEDiagnostic* pdia);\n    \n    bool Init() override;\n    \n    DECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FETiedBiphasicDiagnostic : public FEDiagnostic\n{\npublic:\n    FETiedBiphasicDiagnostic(FEModel* fem);\n    ~FETiedBiphasicDiagnostic();\n    \n    bool Run();\n    \n    bool Init();\n    \n    FEDiagnosticScenario* CreateScenario(const std::string& sname);\n    \nprotected:\n    void print_matrix(matrix& m);\n    void print_matrix(SparseMatrix& m);\n    \n    void deriv_residual(matrix& K);\n    \npublic:\n    FETiedBiphasicScenario*  m_pscn;\n};\n"
  },
  {
    "path": "FEBioTest/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n"
  },
  {
    "path": "FEBioXML/FEBModel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBModel.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEDomain.h>\n#include <FECore/FEShellDomain.h>\n#include <FECore/log.h>\nusing namespace std;\n\n//=============================================================================\nFEBModel::NodeSet::NodeSet() {}\n\nFEBModel::NodeSet::NodeSet(const FEBModel::NodeSet& set)\n{\n\tm_name = set.m_name;\n\tm_node = set.m_node;\n}\n\nFEBModel::NodeSet::NodeSet(const string& name) : m_name(name) {}\n\nvoid FEBModel::NodeSet::SetName(const string& name) { m_name = name; }\n\nconst string& FEBModel::NodeSet::Name() const { return m_name; }\n\nvoid FEBModel::NodeSet::SetNodeList(const vector<int>& node) { m_node = node; }\n\nconst vector<int>& FEBModel::NodeSet::NodeList() const { return m_node; }\n\n//=============================================================================\nFEBModel::EdgeSet::EdgeSet() {}\n\nFEBModel::EdgeSet::EdgeSet(const FEBModel::EdgeSet& set)\n{\n\tm_name = set.m_name;\n\tm_edge = set.m_edge;\n}\n\nFEBModel::EdgeSet::EdgeSet(const string& name) : m_name(name) {}\n\nvoid FEBModel::EdgeSet::SetName(const string& name) { m_name = name; }\n\nconst string& FEBModel::EdgeSet::Name() const { return m_name; }\n\nvoid FEBModel::EdgeSet::SetEdgeList(const vector<EDGE>& edge) { m_edge = edge; }\n\nconst vector<FEBModel::EDGE>& FEBModel::EdgeSet::EdgeList() const { return m_edge; }\n\n//=============================================================================\nFEBModel::ElementSet::ElementSet() {}\n\nFEBModel::ElementSet::ElementSet(const FEBModel::ElementSet& set)\n{\n\tm_name = set.m_name;\n\tm_elem = set.m_elem;\n}\n\nFEBModel::ElementSet::ElementSet(const string& name) : m_name(name) {}\n\nvoid FEBModel::ElementSet::SetName(const string& name) { m_name = name; }\n\nconst string& FEBModel::ElementSet::Name() const { return m_name; }\n\nvoid FEBModel::ElementSet::SetElementList(const vector<int>& elem) { m_elem = elem; }\n\nconst vector<int>& FEBModel::ElementSet::ElementList() const { return m_elem; }\n\n//=============================================================================\nFEBModel::PartList::PartList() {}\nFEBModel::PartList::PartList(const std::string& name) : m_name(name) {}\n\nvoid FEBModel::PartList::SetName(const std::string& name) { m_name = name; }\nconst std::string& FEBModel::PartList::Name() const { return m_name; }\n\nvoid FEBModel::PartList::SetPartList(const std::vector<std::string>& parts) { m_parts = parts; }\nconst std::vector<std::string>& FEBModel::PartList::GetPartList() const { return m_parts; }\n\nFEBModel::PartList* FEBModel::Part::FindPartList(const string& name)\n{\n\tfor (PartList* ps  : m_PList) {\n\t\tif (ps->Name() == name) return ps;\n\t}\n\treturn nullptr;\n}\n\n//=============================================================================\nFEBModel::SurfacePair::SurfacePair() {}\nFEBModel::SurfacePair::SurfacePair(const SurfacePair& surfPair)\n{\n\tm_name = surfPair.m_name;\n\tm_primary = surfPair.m_primary;\n\tm_secondary = surfPair.m_secondary;\n}\nconst string& FEBModel::SurfacePair::Name() const { return m_name; }\n\n//=============================================================================\nFEBModel::Domain::Domain()\n{\n\tm_defaultShellThickness = 0.0;\n}\n\nFEBModel::Domain::Domain(const FEBModel::Domain& dom)\n{\n\tm_spec = dom.m_spec;\n\tm_name = dom.m_name;\n\tm_matName = dom.m_matName;\n\tm_Elem = dom.m_Elem;\n\tm_defaultShellThickness = dom.m_defaultShellThickness;\n}\n\nFEBModel::Domain::Domain(const FE_Element_Spec& spec) : m_spec(spec) \n{\n\tm_defaultShellThickness = 0.0;\n}\n\nconst string& FEBModel::Domain::Name() const { return m_name; }\n\nvoid FEBModel::Domain::SetName(const string& name) { m_name = name; }\n\nvoid FEBModel::Domain::SetMaterialName(const string& name) { m_matName = name; }\n\nconst string& FEBModel::Domain::MaterialName() const { return m_matName; }\n\nvoid FEBModel::Domain::SetElementList(const vector<ELEMENT>& el) { m_Elem = el; }\n\nconst vector<FEBModel::ELEMENT>& FEBModel::Domain::ElementList() const { return m_Elem; }\n\n//=============================================================================\nFEBModel::Surface::Surface() { m_partList = nullptr; }\n\nFEBModel::Surface::Surface(const FEBModel::Surface& surf)\n{\n\tm_name = surf.m_name;\n\tm_Face = surf.m_Face;\n\tm_partList = surf.m_partList;\n}\n\nFEBModel::Surface::Surface(const string& name) : m_name(name), m_partList(nullptr) {}\n\nFEBModel::Surface::Surface(const string& name, PartList* partList) : m_name(name), m_partList(partList) {}\n\nconst string& FEBModel::Surface::Name() const { return m_name; }\n\nvoid FEBModel::Surface::SetName(const string& name) { m_name = name; }\n\nvoid FEBModel::Surface::SetFacetList(const vector<FEBModel::FACET>& face) { m_Face = face; }\n\nconst vector<FEBModel::FACET>& FEBModel::Surface::FacetList() const { return m_Face; }\n\n//=============================================================================\nFEBModel::DiscreteSet::DiscreteSet() {}\nFEBModel::DiscreteSet::DiscreteSet(const FEBModel::DiscreteSet& set)\n{\n\tm_name = set.m_name;\n\tm_elem = set.m_elem;\n}\n\nvoid FEBModel::DiscreteSet::SetName(const string& name) { m_name = name; }\nconst string& FEBModel::DiscreteSet::Name() const { return m_name; }\n\nvoid FEBModel::DiscreteSet::AddElement(int n0, int n1) { m_elem.push_back(ELEM{ n0, n1 } ); }\nconst vector<FEBModel::DiscreteSet::ELEM>& FEBModel::DiscreteSet::ElementList() const { return m_elem; }\n\n//=============================================================================\nFEBModel::Part::Part() {}\n\nFEBModel::Part::Part(const std::string& name) : m_name(name) {}\n\nFEBModel::Part::Part(const FEBModel::Part& part)\n{\n\tm_name = part.m_name;\n\tm_Node = part.m_Node;\n\tfor (size_t i=0; i<part.m_Dom.size() ; ++i) AddDomain (new Domain (*part.m_Dom[i]));\n\tfor (size_t i=0; i<part.m_Surf.size(); ++i) AddSurface(new Surface(*part.m_Surf[i]));\n\tfor (size_t i=0; i<part.m_NSet.size(); ++i) AddNodeSet(new NodeSet(*part.m_NSet[i]));\n\tfor (size_t i=0; i<part.m_ESet.size(); ++i) AddElementSet(new ElementSet(*part.m_ESet[i]));\n\tfor (size_t i = 0; i < part.m_SurfPair.size(); ++i) AddSurfacePair(new SurfacePair(*part.m_SurfPair[i]));\n\tfor (size_t i = 0; i < part.m_DiscSet.size(); ++i) AddDiscreteSet(new DiscreteSet(*part.m_DiscSet[i]));\n}\n\nFEBModel::Part::~Part()\n{\n\tfor (size_t i=0; i<m_NSet.size(); ++i) delete m_NSet[i];\n\tfor (size_t i=0; i<m_Dom.size(); ++i) delete m_Dom[i];\n\tfor (size_t i = 0; i<m_Surf.size(); ++i) delete m_Surf[i];\n\tfor (size_t i = 0; i < m_SurfPair.size(); ++i) delete m_SurfPair[i];\n\tfor (size_t i = 0; i < m_DiscSet.size(); ++i) delete m_DiscSet[i];\n}\n\nvoid FEBModel::Part::SetName(const std::string& name) {\tm_name = name; }\n\nconst string& FEBModel::Part::Name() const { return m_name; }\n\nvoid FEBModel::Part::AddNodes(const std::vector<NODE>& nodes)\n{\n\tsize_t N0 = m_Node.size();\n\tsize_t N = nodes.size();\n\tif (N > 0)\n\t{\n\t\tm_Node.resize(N0 + N);\n\t\tfor (int i=0; i<N; ++i)\n\t\t{\n\t\t\tm_Node[N0 + i] = nodes[i];\n\t\t}\n\t}\n}\n\nFEBModel::Domain* FEBModel::Part::FindDomain(const string& name)\n{\n\tfor (size_t i = 0; i<m_Dom.size(); ++i)\n\t{\n\t\tDomain* dom = m_Dom[i];\n\t\tif (dom->Name() == name) return dom;\n\t}\n\treturn 0;\n}\n\nvoid FEBModel::Part::AddDomain(FEBModel::Domain* dom) { m_Dom.push_back(dom); }\n\nvoid FEBModel::Part::AddSurface(FEBModel::Surface* surf) { m_Surf.push_back(surf); }\n\nFEBModel::Surface* FEBModel::Part::FindSurface(const string& name)\n{\n\tif (name.compare(0, 11, \"@part_list:\") == 0)\n\t{\n\t\t// make sure this part list exists\n\t\tstring partListName = name.substr(11);\n\t\tPartList* partList = FindPartList(partListName);\n\t\tif (partList == nullptr) return nullptr;\n\n\t\t// see if a surface already exists\n\t\tSurface* surf = FindSurface(partListName);\n\t\tif (surf) return surf;\n\n\t\t// create the new surface\n\t\tsurf = new Surface(partListName, partList);\n\t\tAddSurface(surf);\n\n\t\treturn surf;\n\t}\n\telse\n\t{\n\t\tfor (size_t i = 0; i < m_Surf.size(); ++i)\n\t\t{\n\t\t\tSurface* surf = m_Surf[i];\n\t\t\tif (surf->Name() == name) return surf;\n\t\t}\n\t}\n\treturn nullptr;\n}\n\nFEBModel::NodeSet* FEBModel::Part::FindNodeSet(const string& name)\n{\n\tfor (size_t i = 0; i < m_NSet.size(); ++i)\n\t{\n\t\tNodeSet* nset = m_NSet[i];\n\t\tif (nset->Name() == name) return nset;\n\t}\n\treturn nullptr;\n}\n\nFEBModel::EdgeSet* FEBModel::Part::FindEdgeSet(const string& name)\n{\n\tfor (size_t i = 0; i < m_LSet.size(); ++i)\n\t{\n\t\tEdgeSet* lset = m_LSet[i];\n\t\tif (lset->Name() == name) return lset;\n\t}\n\treturn nullptr;\n}\n\nFEBModel::ElementSet* FEBModel::Part::FindElementSet(const string& name)\n{\n\tfor (size_t i = 0; i < m_ESet.size(); ++i)\n\t{\n\t\tElementSet* eset = m_ESet[i];\n\t\tif (eset->Name() == name) return eset;\n\t}\n\treturn nullptr;\n}\n\n//=============================================================================\nFEBModel::FEBModel()\n{\n}\n\nFEBModel::~FEBModel()\n{\n\tfor (size_t i=0; i<m_Part.size(); ++i) delete m_Part[i];\n\tm_Part.clear();\n}\n\nsize_t FEBModel::Parts() const\n{\n\treturn m_Part.size();\n}\n\nFEBModel::Part* FEBModel::GetPart(int i)\n{\n\treturn m_Part[i];\n}\n\nFEBModel::Part* FEBModel::AddPart(const std::string& name)\n{\n\tPart* part = new Part(name);\n\tm_Part.push_back(part);\n\treturn part;\n}\n\nvoid FEBModel::AddPart(FEBModel::Part* part)\n{\n\tm_Part.push_back(part);\n}\n\nFEBModel::Part* FEBModel::FindPart(const string& name)\n{\n\tfor (size_t i=0; i<m_Part.size(); ++i)\n\t{\n\t\tPart* p = m_Part[i];\n\t\tif (p->Name() == name) return p;\n\t}\n\n\treturn 0;\n}\n\nbool FEBModel::BuildPart(FEModel& fem, Part& part, bool buildDomains, const Transform& T)\n{\n\t// we'll need the kernel for creating domains\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// build node-index lookup table\n\tint noff = -1, maxID = 0;\n\tint N0 = mesh.Nodes();\n\tint NN = part.Nodes();\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tint nid = part.GetNode(i).id;\n\t\tif ((noff < 0) || (nid < noff)) noff = nid;\n\t\tif (nid > maxID) maxID = nid;\n\t}\n\tvector<int> NLT(maxID - noff + 1, -1);\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tint nid = part.GetNode(i).id - noff;\n\t\tNLT[nid] = i + N0;\n\t}\n\n\t// build element-index lookup table\n\tint eoff = -1; maxID = 0;\n\tint E0 = mesh.Elements();\n\tint NDOM = part.Domains();\n\tfor (int i=0; i<NDOM; ++i)\n\t{\n\t\tconst Domain& dom = part.GetDomain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j=0; j<NE; ++j)\n\t\t{\n\t\t\tint eid = dom.GetElement(j).id;\n\t\t\tif ((eoff < 0) || (eid < eoff)) eoff = eid;\n\t\t\tif (eid > maxID) maxID = eid;\n\t\t}\n\t}\n\tvector<int> ELT(maxID - eoff + 1, -1);\n\tint ecount = E0;\n\tfor (int i = 0; i<NDOM; ++i)\n\t{\n\t\tconst Domain& dom = part.GetDomain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j<NE; ++j)\n\t\t{\n\t\t\tint eid = dom.GetElement(j).id - eoff;\n\t\t\tELT[eid] = ecount++;\n\t\t}\n\t}\n\n\t// create the nodes\n\tint nid = N0;\n\tmesh.AddNodes(NN);\n\tint n = 0;\n\tfor (int j = 0; j<NN; ++j)\n\t{\n\t\tNODE& partNode = part.GetNode(j);\n\t\tFENode& meshNode = mesh.Node(N0 + n++);\n\n\t\t// TODO: This is going to break multi-part models\n\t\tmeshNode.SetID(partNode.id);\n\n\t\tmeshNode.m_r0 = T.Apply(partNode.r);\n\t\tmeshNode.m_rt = meshNode.m_r0;\n\t}\n\tassert(n == NN);\n\n\t// get the part name\n\tstring partName = part.Name();\n\tif (partName.empty() == false) partName += \".\";\n\n\t// process domains\n\tif (buildDomains)\n\t{\n\t\tint eid = E0;\n\t\tfor (int i = 0; i < NDOM; ++i)\n\t\t{\n\t\t\tconst Domain& partDomain = part.GetDomain(i);\n\n\t\t\t// element count\n\t\t\tint elems = partDomain.Elements();\n\n\t\t\t// get the element spect\n\t\t\tFE_Element_Spec spec = partDomain.ElementSpec();\n\n\t\t\t// get the material\n\t\t\tstring matName = partDomain.MaterialName();\n\t\t\tFEMaterial* mat = fem.FindMaterial(matName.c_str());\n\t\t\tif (mat == 0) return false;\n\n\t\t\t// create the domain\n\t\t\tFEDomain* dom = febio.CreateDomain(spec, &mesh, mat);\n\t\t\tif (dom == 0) return false;\n\n\t\t\tif (dom->Create(elems, spec) == false)\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdom->SetMatID(mat->GetID() - 1);\n\n\t\t\tstring domName = partName + partDomain.Name();\n\t\t\tdom->SetName(domName);\n\n\t\t\t// process element data\n\t\t\tfor (int j = 0; j < elems; ++j)\n\t\t\t{\n\t\t\t\tconst ELEMENT& domElement = partDomain.GetElement(j);\n\n\t\t\t\tFEElement& el = dom->ElementRef(j);\n\n\t\t\t\t// TODO: This is going to break multi-part models\n\t\t\t\tel.SetID(domElement.id);\n\n\t\t\t\tint ne = el.Nodes();\n\t\t\t\tfor (int n = 0; n < ne; ++n) el.m_node[n] = NLT[domElement.node[n] - noff];\n\t\t\t}\n\n\t\t\tif (partDomain.m_defaultShellThickness != 0.0)\n\t\t\t{\n\t\t\t\tdouble h0 = partDomain.m_defaultShellThickness;\n\t\t\t\tFEShellDomain* shellDomain = dynamic_cast<FEShellDomain*>(dom);\n\t\t\t\tif (shellDomain)\n\t\t\t\t{\n\t\t\t\t\tint ne = shellDomain->Elements();\n\t\t\t\t\tfor (int n = 0; n < ne; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEShellElement& el = shellDomain->Element(n);\n\t\t\t\t\t\tfor (int k = 0; k < el.Nodes(); ++k) el.m_h0[k] = h0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFEModel* pfem = &fem;\n\t\t\t\t\tfeLogWarningEx(pfem, \"Shell thickness assigned on non-shell part %s\", partDomain.Name().c_str());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// add the domain to the mesh\n\t\t\tmesh.AddDomain(dom);\n\n\t\t\t// initialize material point data\n\t\t\tdom->CreateMaterialPointData();\n\t\t}\n\t}\n\n\t// create node sets\n\tint NSets = part.NodeSets();\n\tfor (int i = 0; i<NSets; ++i)\n\t{\n\t\tNodeSet* set = part.GetNodeSet(i);\n\n\t\t// create a new node set\n\t\tFENodeSet* feset = new FENodeSet(&fem);\n\n\t\t// add the name\n\t\tstring name = partName + set->Name();\n\t\tfeset->SetName(name.c_str());\n\n\t\t// copy indices\n\t\tvector<int> nodeList = set->NodeList();\n\t\tint nn = (int)nodeList.size();\n\t\tfor (int j=0; j<nn; ++j) nodeList[j] = NLT[nodeList[j] - noff];\n\t\tfeset->Add(nodeList);\n\n\t\t// add it to the mesh\n\t\tmesh.AddNodeSet(feset);\n\t}\n\n\t// create edges\n\tint Edges = part.EdgeSets();\n\tfor (int i = 0; i < Edges; ++i)\n\t{\n\t\tEdgeSet* edgeSet = part.GetEdgeSet(i);\n\t\tint N = edgeSet->Edges();\n\n\t\t// create a new segment set\n\t\tFESegmentSet* segSet = new FESegmentSet(&fem);\n\t\tstring name = partName + edgeSet->Name();\n\t\tsegSet->SetName(name.c_str());\n\n\t\t// copy data\n\t\tsegSet->Create(N);\n\t\tfor (int j = 0; j < N; ++j)\n\t\t{\n\t\t\tEDGE& edge = edgeSet->Edge(j);\n\t\t\tFESegmentSet::SEGMENT& seg = segSet->Segment(j);\n\n\t\t\tseg.ntype = edge.ntype;\n\t\t\tint nn = edge.ntype;\t// we assume that the type also identifies the number of nodes\n\t\t\tfor (int n = 0; n < nn; ++n)\n\t\t\t{\n\t\t\t\tint nid = edge.node[n] - noff;\n\t\t\t\tif (nid < 0 || nid >= NLT.size() || NLT[nid] < 0)\n\t\t\t\t{\n\t\t\t\t\tfeLogErrorEx(&fem, \"Invalid node index %d in edge set %s\", edge.node[n], edgeSet->Name().c_str());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tseg.node[n] = NLT[nid];\n\t\t\t}\n\t\t}\n\n\t\t// add it to the mesh\n\t\tmesh.AddSegmentSet(segSet);\n\t}\n\n\t// create surfaces\n\tint Surfs = part.Surfaces();\n\tfor (int i=0; i<Surfs; ++i)\n\t{\n\t\tSurface* surf = part.GetSurface(i);\n\t\tint faces = surf->Facets();\n\n\t\tFEFacetSet* fset = nullptr;\n\t\tif ((faces == 0) && surf->GetPartList())\n\t\t{\n\t\t\t// build the domain list\n\t\t\tFEBModel::PartList* partList = surf->GetPartList();\n\t\t\tstd::vector<string> partNames = partList->GetPartList();\n\t\t\tstd::vector<FEDomain*> domList;\n\t\t\tfor (string s : partNames)\n\t\t\t{\n\t\t\t\tFEDomain* dom = mesh.FindDomain(s);\n\t\t\t\tassert(dom);\n\t\t\t\tif (dom == nullptr) return false;\n\t\t\t\tdomList.push_back(dom);\n\t\t\t}\n\n\t\t\t// we need to extract the surface from a list of parts\n\t\t\tfset = mesh.DomainBoundary(domList);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// create a new facet set\n\t\t\tfset = new FEFacetSet(&fem);\n\n\t\t\t// copy data\n\t\t\tfset->Create(faces);\n\t\t\tfor (int j = 0; j < faces; ++j)\n\t\t\t{\n\t\t\t\tFACET& srcFacet = surf->GetFacet(j);\n\t\t\t\tFEFacetSet::FACET& face = fset->Face(j);\n\n\t\t\t\tface.ntype = srcFacet.ntype;\n\t\t\t\tint nf = srcFacet.ntype;\t// we assume that the type also identifies the number of nodes\n\t\t\t\tfor (int n = 0; n < nf; ++n) face.node[n] = NLT[srcFacet.node[n] - noff];\n\t\t\t}\n\t\t}\n\n\t\t// add it to the mesh\n\t\tassert(fset);\n\t\tstring name = partName + surf->Name();\n\t\tfset->SetName(name.c_str());\n\t\tmesh.AddFacetSet(fset);\n\t}\n\n\t// create element sets\n\tint ESets = part.ElementSets();\n\tfor (int i=0; i<ESets; ++i)\n\t{\n\t\tElementSet& eset = *part.GetElementSet(i);\n\t\tvector<int> elist = eset.ElementList();\n\n\t\tint ne = (int) elist.size();\n\t\tFEElementSet* feset = new FEElementSet(&fem);\n\t\tstring name = partName + eset.Name();\n\t\tfeset->SetName(name);\n\n\t\t// If a domain exists with the same name, we assume\n\t\t// that this element set refers to the that domain (TODO: should actually check this!)\n\t\tFEDomain* dom = mesh.FindDomain(name);\n\t\tif (dom) feset->Create(dom);\n\t\telse\n\t\t{\n\t\t\t// A domain with the same name is not found, but it is possible that this \n\t\t\t// set still coincides with a domain, so let's see if we can find it. \n\t\t\t// see if all elements belong to the same domain\n\t\t\tbool oneDomain = true;\n\t\t\tFEElement* el = mesh.FindElementFromID(elist[0]); assert(el);\n\t\t\tFEDomain* dom = dynamic_cast<FEDomain*>(el->GetMeshPartition());\n\t\t\tfor (int i = 1; i < elist.size(); ++i)\n\t\t\t{\n\t\t\t\tFEElement* el_i = mesh.FindElementFromID(elist[i]); assert(el);\n\t\t\t\tFEDomain* dom_i = dynamic_cast<FEDomain*>(el_i->GetMeshPartition());\n\n\t\t\t\tif (dom != dom_i)\n\t\t\t\t{\n\t\t\t\t\toneDomain = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// assign indices to element set\n\t\t\tif (oneDomain && (dom->Elements() == elist.size()))\n\t\t\t\tfeset->Create(dom, elist);\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Couldn't find a single domain.\n\t\t\t\t// But maybe this set encompasses the entire mesh? \n\t\t\t\tif (elist.size() == mesh.Elements())\n\t\t\t\t{\n\t\t\t\t\tFEDomainList allDomains;\n\t\t\t\t\tfor (int i = 0; i < mesh.Domains(); ++i) allDomains.AddDomain(&mesh.Domain(i));\n\t\t\t\t\tfeset->Create(allDomains);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfeset->Create(elist);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tmesh.AddElementSet(feset);\n\t}\n\n\t// create surface pairs\n\tint SPairs = part.SurfacePairs();\n\tfor (int i = 0; i < SPairs; ++i)\n\t{\n\t\tSurfacePair& spair = *part.GetSurfacePair(i);\n\t\tstring name = partName + spair.Name();\n\n\t\tFESurfacePair* fesurfPair = new FESurfacePair(&mesh);\n\t\tmesh.AddSurfacePair(fesurfPair);\n\t\tfesurfPair->SetName(name);\n\n\t\tFEFacetSet* surf1 = mesh.FindFacetSet(spair.m_primary);\n\t\tif (surf1 == nullptr) return false;\n\t\tfesurfPair->SetPrimarySurface(surf1);\n\n\t\tFEFacetSet* surf2 = mesh.FindFacetSet(spair.m_secondary);\n\t\tif (surf2 == nullptr) return false;\n\t\tfesurfPair->SetSecondarySurface(surf2);\n\t}\n\n\t// create domain lists\n\tfor (int i = 0; i < part.PartLists(); ++i)\n\t{\n\t\tFEBModel::PartList* partList = part.GetPartList(i);\n\n\t\tFEDomainList* domList = new FEDomainList();\n\t\tdomList->SetName(partList->Name());\n\t\tfor (int j = 0; j < partList->Parts(); ++j)\n\t\t{\n\t\t\tFEDomain* dom = mesh.FindDomain(partList->PartName(j)); assert(dom);\n\t\t\tif (dom) domList->AddDomain(dom);\n\t\t}\n\n\t\tmesh.AddDomainList(domList);\n\t}\n\n\t// create discrete element sets\n\tint DSets = part.DiscreteSets();\n\tfor (int i = 0; i < DSets; ++i)\n\t{\n\t\tDiscreteSet& dset = *part.GetDiscreteSet(i);\n\t\tstring name = partName + dset.Name();\n\n\t\tFEDiscreteSet* fedset = new FEDiscreteSet(&mesh);\n\t\tmesh.AddDiscreteSet(fedset);\n\t\tfedset->SetName(name);\n\n\t\tconst std::vector<DiscreteSet::ELEM>& elemList = dset.ElementList();\n\n\t\tfor (int j = 0; j < elemList.size(); ++j)\n\t\t{\n\t\t\tint n0 = NLT[elemList[j].node[0] - noff];\n\t\t\tint n1 = NLT[elemList[j].node[1] - noff];\n\n\t\t\tfedset->add(n0, n1);\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioXML/FEBModel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/vec3d.h>\n#include <vector>\n#include <string>\n#include <FECore/FEElement.h>\n#include <FECore/FETransform.h>\n\n//-----------------------------------------------------------------------------\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// This is a helper class for parsing the FEBio input file. It manages the geometry\n// of the model as composed of parts. After the Geometry section is read in, call the \n// BuildMesh function to generate the mesh for the model.\nclass FEBModel\n{\npublic:\n\tstruct NODE\n\t{\n\t\tint\t\tid;\t// Nodal ID\n\t\tvec3d\tr;\t// Node position\n\t};\n\n\tstruct ELEMENT\n\t{\n\t\tint\tid;\n\t\tint\tnode[FEElement::MAX_NODES];\n\t};\n\n\tstruct FACET\n\t{\n\t\tint id;\n\t\tint node[FEElement::MAX_NODES];\n\t\tint ntype;\n\t};\n\n\tstruct EDGE\n\t{\n\t\tint id;\n\t\tint node[FEElement::MAX_NODES];\n\t\tint ntype;\n\t};\n\n\tclass Domain\n\t{\n\tpublic:\n\t\tDomain();\n\t\tDomain(const Domain& dom);\n\t\tDomain(const FE_Element_Spec& spec);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid SetMaterialName(const std::string& name);\n\t\tconst std::string& MaterialName() const;\n\n\t\tvoid SetElementList(const std::vector<ELEMENT>& el);\n\t\tconst std::vector<ELEMENT>& ElementList() const;\n\n\t\tint Elements() const { return (int) m_Elem.size(); }\n\n\t\tFE_Element_Spec ElementSpec() const { return m_spec; }\n\t\tvoid SetElementSpec(FE_Element_Spec spec) { m_spec = spec; }\n\t\t\n\t\tvoid Create(int nsize) { m_Elem.resize(nsize); }\n\t\tvoid Reserve(int nsize) { m_Elem.reserve(nsize); }\n\t\tconst ELEMENT& GetElement(int i) const { return m_Elem[i]; }\n\t\tELEMENT& GetElement(int i) { return m_Elem[i]; }\n\t\tvoid AddElement(const ELEMENT& el) { m_Elem.push_back(el); }\n\n\tprivate:\n\t\tFE_Element_Spec\t\tm_spec;\n\t\tstd::string\t\t\tm_name;\n\t\tstd::string\t\t\tm_matName;\n\t\tstd::vector<ELEMENT>\tm_Elem;\n\n\tpublic:\n\t\tdouble\tm_defaultShellThickness;\n\t};\n\n\tclass PartList;\n\n\t// a surface can be defined explicitly (using a list of facets)\n\t// or implicitly via a part list (in this case, the facet list will be empty \n\t// and the part list can be found via GetPartList()\n\tclass Surface\n\t{\n\tpublic:\n\t\tSurface();\n\t\tSurface(const Surface& surf);\n\t\tSurface(const std::string& name);\n\t\tSurface(const std::string& name, PartList* partList);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid SetFacetList(const std::vector<FACET>& el);\n\t\tconst std::vector<FACET>& FacetList() const;\n\n\t\tvoid Create(int n) { m_Face.resize(n); }\n\n\t\tint Facets() const { return (int) m_Face.size(); }\n\t\tFACET& GetFacet(int i) { return m_Face[i]; }\n\n\t\tPartList* GetPartList() { return m_partList; }\n\n\tprivate:\n\t\tstd::string\tm_name;\n\t\tstd::vector<FACET>\tm_Face;\n\t\tPartList* m_partList;\n\t};\n\n\tclass NodeSet\n\t{\n\tpublic:\n\t\tNodeSet();\n\t\tNodeSet(const NodeSet& set);\n\t\tNodeSet(const std::string& name);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid SetNodeList(const std::vector<int>& node);\n\t\tconst std::vector<int>& NodeList() const;\n\n\tprivate:\n\t\tstd::string\t\tm_name;\n\t\tstd::vector<int>\tm_node;\n\t};\n\n\tclass EdgeSet\n\t{\n\tpublic:\n\t\tEdgeSet();\n\t\tEdgeSet(const EdgeSet& set);\n\t\tEdgeSet(const std::string& name);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid SetEdgeList(const std::vector<EDGE>& edge);\n\t\tconst std::vector<EDGE>& EdgeList() const;\n\n\t\tint Edges() const { return (int)m_edge.size(); }\n\t\tEDGE& Edge(int i) { return m_edge[i]; }\n\n\tprivate:\n\t\tstd::string\t\t\tm_name;\n\t\tstd::vector<EDGE>\tm_edge;\n\t};\n\n\tclass ElementSet\n\t{\n\tpublic:\n\t\tElementSet();\n\t\tElementSet(const ElementSet& set);\n\t\tElementSet(const std::string& name);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid SetElementList(const std::vector<int>& elem);\n\t\tconst std::vector<int>& ElementList() const;\n\n\tprivate:\n\t\tstd::string\t\t\tm_name;\n\t\tstd::vector<int>\tm_elem;\n\t};\n\n\tclass PartList\n\t{\n\tpublic:\n\t\tPartList();\n\t\tPartList(const std::string& name);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid SetPartList(const std::vector<std::string>& parts);\n\t\tconst std::vector<std::string>& GetPartList() const;\n\n\t\tsize_t Parts() const { return m_parts.size(); }\n\t\tconst std::string& PartName(size_t n) const { return m_parts[n]; }\n\n\tprivate:\n\t\tstd::string\t\t\t\t\tm_name;\n\t\tstd::vector<std::string>\tm_parts;\n\t};\n\n\tclass SurfacePair\n\t{\n\tpublic:\n\t\tSurfacePair();\n\t\tSurfacePair(const SurfacePair& surfPair);\n\n\t\tconst std::string& Name() const;\n\n\tpublic:\n\t\tstd::string\tm_name;\n\t\tstd::string\tm_primary;\n\t\tstd::string\tm_secondary;\t\t\n\t};\n\n\tclass DiscreteSet\n\t{\n\tpublic:\n\t\tstruct ELEM\n\t\t{\n\t\t\tint\tnode[2];\n\t\t};\n\n\tpublic:\n\t\tDiscreteSet();\n\t\tDiscreteSet(const DiscreteSet& set);\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid AddElement(int n0, int n1);\n\t\tconst std::vector<ELEM>& ElementList() const;\n\n\tprivate:\n\t\tstd::string\t\t\tm_name;\n\t\tstd::vector<ELEM>\tm_elem;\n\t};\n\n\tclass Part\n\t{\n\tpublic:\n\t\tPart();\n\t\tPart(const std::string& name);\n\t\tPart(const Part& part);\n\t\t~Part();\n\n\t\tvoid SetName(const std::string& name);\n\t\tconst std::string& Name() const;\n\n\t\tvoid AddNodes(const std::vector<NODE>& nodes);\n\n\t\tint Domains() const { return (int)m_Dom.size(); }\n\t\tvoid AddDomain(Domain* dom);\n\t\tconst Domain& GetDomain(int i) const { return *m_Dom[i]; }\n\t\tDomain* FindDomain(const std::string& name);\n\n\t\tint Surfaces() const { return (int) m_Surf.size(); }\n\t\tvoid AddSurface(Surface* surf);\n\t\tSurface* GetSurface(int i) { return m_Surf[i]; }\n\t\tSurface* FindSurface(const std::string& name);\n\n\t\tint NodeSets() const { return (int) m_NSet.size(); }\n\t\tvoid AddNodeSet(NodeSet* nset) { m_NSet.push_back(nset); }\n\t\tNodeSet* GetNodeSet(int i) { return m_NSet[i]; }\n\t\tNodeSet* FindNodeSet(const std::string& name);\n\n\t\tint EdgeSets() const { return (int)m_LSet.size(); }\n\t\tvoid AddEdgeSet(EdgeSet* cset) { m_LSet.push_back(cset); }\n\t\tEdgeSet* GetEdgeSet(int i) { return m_LSet[i]; }\n\t\tEdgeSet* FindEdgeSet(const std::string& name);\n\n\t\tint ElementSets() const { return (int) m_ESet.size(); }\n\t\tvoid AddElementSet(ElementSet* eset) { m_ESet.push_back(eset); }\n\t\tElementSet* GetElementSet(int i) { return m_ESet[i]; }\n\t\tElementSet* FindElementSet(const std::string& name);\n\n\t\tint PartLists() const { return (int)m_PList.size(); }\n\t\tvoid AddPartList(PartList* plist) { m_PList.push_back(plist); }\n\t\tPartList* GetPartList(int i) { return m_PList[i]; }\n\t\tPartList* FindPartList(const std::string& name);\n\n\t\tint SurfacePairs() const { return (int)m_SurfPair.size(); }\n\t\tvoid AddSurfacePair(SurfacePair* sp) { m_SurfPair.push_back(sp); }\n\t\tSurfacePair* GetSurfacePair(int i) { return m_SurfPair[i]; }\n\n\t\tint DiscreteSets() const { return (int)m_DiscSet.size(); }\n\t\tvoid AddDiscreteSet(DiscreteSet* sp) { m_DiscSet.push_back(sp); }\n\t\tDiscreteSet* GetDiscreteSet(int i) { return m_DiscSet[i]; }\n\n\t\tint Nodes() const { return (int) m_Node.size(); }\n\n\t\tNODE& GetNode(int i) { return m_Node[i]; }\n\n\tprivate:\n\t\tstd::string\t\t\t\t\tm_name;\n\t\tstd::vector<NODE>\t\t\tm_Node;\n\t\tstd::vector<Domain*>\t\tm_Dom;\n\t\tstd::vector<Surface*>\t\tm_Surf;\n\t\tstd::vector<NodeSet*>\t\tm_NSet;\n\t\tstd::vector<EdgeSet*>\t\tm_LSet;\n\t\tstd::vector<ElementSet*>\tm_ESet;\n\t\tstd::vector<PartList*>\t\tm_PList;\n\t\tstd::vector<SurfacePair*>\tm_SurfPair;\n\t\tstd::vector<DiscreteSet*>\tm_DiscSet;\n\t};\n\npublic:\n\tFEBModel();\n\t~FEBModel();\n\n\tsize_t Parts() const;\n\tPart* GetPart(int i);\n\tPart* AddPart(const std::string& name);\n\tvoid AddPart(Part* part);\n\n\tPart* FindPart(const std::string& name);\n\n\tbool BuildPart(FEModel& fem, Part& part, bool buildDomains = true, const Transform& T = Transform());\n\nprivate:\n\tstd::vector<Part*>\tm_Part;\n};\n"
  },
  {
    "path": "FEBioXML/FEBioBoundarySection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioBoundarySection.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FEDiscreteMaterial.h>\n#include <FECore/FEDiscreteDomain.h>\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FEPrescribedDOF.h>\n#include <FECore/FEFixedBC.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FEPeriodicLinearConstraint.h>\n#include <FECore/FEMergedConstraint.h>\n#include <FECore/FEFacetSet.h>\n#include <FECore/log.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEInitialCondition.h>\n\n//---------------------------------------------------------------------------------\nvoid FEBioBoundarySection::BuildNodeSetMap()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\tm_NodeSet.clear();\n\tfor (int i = 0; i<m.NodeSets(); ++i)\n\t{\n\t\tFENodeSet* nsi = m.NodeSet(i);\n\t\tm_NodeSet[nsi->GetName()] = nsi;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection1x::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"fix\"              ) ParseBCFix         (tag);\n\t\telse if (tag == \"prescribe\"        ) ParseBCPrescribe   (tag);\n\t\telse if (tag == \"contact\"          ) ParseContactSection(tag);\n\t\telse if (tag == \"linear_constraint\") ParseConstraints   (tag);\n\t\telse if (tag == \"spring\"           ) ParseSpringSection (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection2::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"fix\"              ) ParseBCFix      (tag);\n\t\telse if (tag == \"prescribe\"        ) ParseBCPrescribe(tag);\n\t\telse if (tag == \"linear_constraint\") ParseConstraints(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection25::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t// build the node set map for faster lookup\n\tBuildNodeSetMap();\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"fix\"                          ) ParseBCFix                     (tag);\n\t\telse if (tag == \"prescribe\"                    ) ParseBCPrescribe               (tag);\n\t\telse if (tag == \"bc\"                           ) ParseBC                        (tag);\n\t\telse if (tag == \"rigid\"                        ) ParseBCRigid                   (tag);\n\t\telse if (tag == \"rigid_body\"                   ) ParseRigidBody                 (tag);\n\t\telse if (tag == \"linear_constraint\"            ) ParseConstraints               (tag);\n\t\telse if (tag == \"periodic_linear_constraint\"   ) ParsePeriodicLinearConstraint  (tag);\n\t\telse if (tag == \"periodic_linear_constraint_2O\") ParsePeriodicLinearConstraint2O(tag);\n\t\telse if (tag == \"merge\"                        ) ParseMergeConstraint           (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//---------------------------------------------------------------------------------\n// parse a surface section for contact definitions\n//\nbool FEBioBoundarySection::ParseSurfaceSection(XMLTag &tag, FESurface& s, int nfmt, bool bnodal)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\tint NN = m.Nodes();\n\n\tint N, nf[9];\n\n\t// count nr of faces\n\tint faces = tag.children();\n\n\t// allocate storage for faces\n\ts.Create(faces);\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t// read faces\n\t++tag;\n\tfor (int i=0; i<faces; ++i)\n\t{\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// set the element type/integration rule\n\t\tif (bnodal)\n\t\t{\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4NI);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(FE_TRI3NI );\n\t\t\telse if (tag == \"tri6\" ) el.SetType(FE_TRI6NI );\n            else if (tag == \"quad8\" ) el.SetType(FE_QUAD8NI);\n            else if (tag == \"quad9\" ) el.SetType(FE_QUAD9NI);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4G4);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(feb->m_ntri3);\n\t\t\telse if (tag == \"tri6\" ) el.SetType(feb->m_ntri6);\n\t\t\telse if (tag == \"tri7\" ) el.SetType(feb->m_ntri7);\n\t\t\telse if (tag == \"tri10\") el.SetType(feb->m_ntri10);\n\t\t\telse if (tag == \"quad8\") el.SetType(FE_QUAD8G9);\n\t\t\telse if (tag == \"quad9\") el.SetType(FE_QUAD9G9);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\n\t\tN = el.Nodes();\n\n\t\tif (nfmt == 0)\n\t\t{\n\t\t\ttag.value(nf, N);\n\t\t\tfor (int j=0; j<N; ++j) \n\t\t\t{\n\t\t\t\tint nid = nf[j]-1;\n\t\t\t\tif ((nid<0)||(nid>= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\t\tel.m_node[j] = nid;\n\t\t\t}\n\t\t}\n\t\telse if (nfmt == 1)\n\t\t{\n\t\t\ttag.value(nf, 2);\n\t\t\tFEElement* pe = m.FindElementFromID(nf[0]);\n\t\t\tif (pe)\n\t\t\t{\n\t\t\t\tint ne[9];\n\t\t\t\tint nn = pe->GetFace(nf[1]-1, ne);\n\t\t\t\tif (nn != N) throw XMLReader::InvalidValue(tag);\n\t\t\t\tfor (int j=0; j<N; ++j) el.m_node[j] = ne[j];\n\t\t\t\tel.m_elem[0].pe = pe;\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t}\n\n\t\t++tag;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection::AddFixedBC(FENodeSet* set, int dof)\n{\n\tFEModel* fem = GetFEModel();\n\tFEFixedBC* bc = new FEFixedBC(fem);\n\tbc->SetDOFList(dof);\n\tbc->SetNodeSet(set);\n\tfem->AddBoundaryCondition(bc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection::ParseBCFix(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// get the DOF indices\n\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\tconst int dof_U = fem.GetDOFIndex(\"u\");\n\tconst int dof_V = fem.GetDOFIndex(\"v\");\n\tconst int dof_W = fem.GetDOFIndex(\"w\");\n    const int dof_SX = fem.GetDOFIndex(\"sx\");\n    const int dof_SY = fem.GetDOFIndex(\"sy\");\n    const int dof_SZ = fem.GetDOFIndex(\"sz\");\n    const int dof_WX = fem.GetDOFIndex(\"wx\");\n    const int dof_WY = fem.GetDOFIndex(\"wy\");\n    const int dof_WZ = fem.GetDOFIndex(\"wz\");\n    const int dof_GX = fem.GetDOFIndex(\"gx\");\n    const int dof_GY = fem.GetDOFIndex(\"gy\");\n    const int dof_GZ = fem.GetDOFIndex(\"gz\");\n\n\t// see if a set is defined\n\tconst char* szset = tag.AttributeValue(\"set\", true);\n\tif (szset)\n\t{\n\t\t// read the set\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tFENodeSet* ps = mesh.FindNodeSet(szset);\n\t\tif (ps == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\n\t\t// get the bc attribute\n\t\tconst char* sz = tag.AttributeValue(\"bc\");\n\n\t\t// Make sure this is a leaf\n\t\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// loop over all nodes in the nodeset\n\t\tint ndof = dofs.GetDOF(sz);\n\t\tif (ndof >= 0) AddFixedBC(ps, ndof);\n\t\telse\n\t\t{\n\t\t\t// The supported fixed BC strings don't quite follow the dof naming convention.\n\t\t\t// For now, we'll check these BC explicitly, but I want to get rid of this in the future.\n\t\t\tif      (strcmp(sz, \"xy\"  ) == 0) { AddFixedBC(ps, dof_X ); AddFixedBC(ps, dof_Y); }\n\t\t\telse if (strcmp(sz, \"yz\"  ) == 0) { AddFixedBC(ps, dof_Y ); AddFixedBC(ps, dof_Z); }\n\t\t\telse if (strcmp(sz, \"xz\"  ) == 0) { AddFixedBC(ps, dof_X ); AddFixedBC(ps, dof_Z); }\n\t\t\telse if (strcmp(sz, \"xyz\" ) == 0) { AddFixedBC(ps, dof_X ); AddFixedBC(ps, dof_Y); AddFixedBC(ps, dof_Z); }\n\t\t\telse if (strcmp(sz, \"uv\"  ) == 0) { AddFixedBC(ps, dof_U ); AddFixedBC(ps, dof_V); }\n\t\t\telse if (strcmp(sz, \"vw\"  ) == 0) { AddFixedBC(ps, dof_V ); AddFixedBC(ps, dof_W); }\n\t\t\telse if (strcmp(sz, \"uw\"  ) == 0) { AddFixedBC(ps, dof_U ); AddFixedBC(ps, dof_W); }\n\t\t\telse if (strcmp(sz, \"uvw\" ) == 0) { AddFixedBC(ps, dof_U ); AddFixedBC(ps, dof_V); AddFixedBC(ps, dof_W); }\n            else if (strcmp(sz, \"sxy\" ) == 0) { AddFixedBC(ps, dof_SX); AddFixedBC(ps, dof_SY); }\n            else if (strcmp(sz, \"syz\" ) == 0) { AddFixedBC(ps, dof_SY); AddFixedBC(ps, dof_SZ); }\n            else if (strcmp(sz, \"sxz\" ) == 0) { AddFixedBC(ps, dof_SX); AddFixedBC(ps, dof_SZ); }\n            else if (strcmp(sz, \"sxyz\") == 0) { AddFixedBC(ps, dof_SX); AddFixedBC(ps, dof_SY); AddFixedBC(ps, dof_SZ); }\n            else if (strcmp(sz, \"wxy\" ) == 0) { AddFixedBC(ps, dof_WX); AddFixedBC(ps, dof_WY); }\n            else if (strcmp(sz, \"wyz\" ) == 0) { AddFixedBC(ps, dof_WY); AddFixedBC(ps, dof_WZ); }\n            else if (strcmp(sz, \"wxz\" ) == 0) { AddFixedBC(ps, dof_WX); AddFixedBC(ps, dof_WZ); }\n            else if (strcmp(sz, \"wxyz\") == 0) { AddFixedBC(ps, dof_WX); AddFixedBC(ps, dof_WY); AddFixedBC(ps, dof_WZ); }\n            else if (strcmp(sz, \"gxy\" ) == 0) { AddFixedBC(ps, dof_GX); AddFixedBC(ps, dof_GY); }\n            else if (strcmp(sz, \"gyz\" ) == 0) { AddFixedBC(ps, dof_GY); AddFixedBC(ps, dof_GZ); }\n            else if (strcmp(sz, \"gxz\" ) == 0) { AddFixedBC(ps, dof_GX); AddFixedBC(ps, dof_GZ); }\n            else if (strcmp(sz, \"gxyz\") == 0) { AddFixedBC(ps, dof_GX); AddFixedBC(ps, dof_GY); AddFixedBC(ps, dof_GZ); }\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// The format where the bc can be defined on each line is no longer supported.\n\t\tthrow XMLReader::MissingAttribute(tag, \"set\");\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection2::ParseBCFix(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tDOFS& dofs = fem.GetDOFS();\n\tFEMesh& mesh = fem.GetMesh();\n\tint NN = mesh.Nodes();\n\n\t// get the required bc attribute\n\tchar szbc[32] = { 0 };\n\tstrcpy(szbc, tag.AttributeValue(\"bc\"));\n\n\t// process the bc string\n\tvector<int> bc;\n\n\tint ndof = dofs.GetDOF(szbc);\n\tif (ndof >= 0) bc.push_back(ndof);\n\telse\n\t{\n\t\t// get the DOF indices\n\t\tconst int dof_X = fem.GetDOFIndex(\"x\");\n\t\tconst int dof_Y = fem.GetDOFIndex(\"y\");\n\t\tconst int dof_Z = fem.GetDOFIndex(\"z\");\n\t\tconst int dof_U = fem.GetDOFIndex(\"u\");\n\t\tconst int dof_V = fem.GetDOFIndex(\"v\");\n\t\tconst int dof_W = fem.GetDOFIndex(\"w\");\n\t\tconst int dof_SX = fem.GetDOFIndex(\"sx\");\n\t\tconst int dof_SY = fem.GetDOFIndex(\"sy\");\n\t\tconst int dof_SZ = fem.GetDOFIndex(\"sz\");\n        const int dof_WX = fem.GetDOFIndex(\"wx\");\n        const int dof_WY = fem.GetDOFIndex(\"wy\");\n        const int dof_WZ = fem.GetDOFIndex(\"wz\");\n        const int dof_GX = fem.GetDOFIndex(\"gx\");\n        const int dof_GY = fem.GetDOFIndex(\"gy\");\n        const int dof_GZ = fem.GetDOFIndex(\"gz\");\n\n\t\t// The supported fixed BC strings don't quite follow the dof naming convention.\n\t\t// For now, we'll check these BC explicitly, but I want to get rid of this in the future.\n\t\tif      (strcmp(szbc, \"x\"   ) == 0) { bc.push_back(dof_X ); }\n\t\telse if (strcmp(szbc, \"y\"   ) == 0) { bc.push_back(dof_Y ); }\n\t\telse if (strcmp(szbc, \"z\"   ) == 0) { bc.push_back(dof_Z ); }\n\t\telse if (strcmp(szbc, \"xy\"  ) == 0) { bc.push_back(dof_X ); bc.push_back(dof_Y); }\n\t\telse if (strcmp(szbc, \"yz\"  ) == 0) { bc.push_back(dof_Y ); bc.push_back(dof_Z); }\n\t\telse if (strcmp(szbc, \"xz\"  ) == 0) { bc.push_back(dof_X ); bc.push_back(dof_Z); }\n\t\telse if (strcmp(szbc, \"xyz\" ) == 0) { bc.push_back(dof_X ); bc.push_back(dof_Y); bc.push_back(dof_Z); }\n\t\telse if (strcmp(szbc, \"uv\"  ) == 0) { bc.push_back(dof_U ); bc.push_back(dof_V); }\n\t\telse if (strcmp(szbc, \"vw\"  ) == 0) { bc.push_back(dof_V ); bc.push_back(dof_W); }\n\t\telse if (strcmp(szbc, \"uw\"  ) == 0) { bc.push_back(dof_U ); bc.push_back(dof_W); }\n\t\telse if (strcmp(szbc, \"uvw\" ) == 0) { bc.push_back(dof_U ); bc.push_back(dof_V); bc.push_back(dof_W); }\n\t    else if (strcmp(szbc, \"sxy\" ) == 0) { bc.push_back(dof_SX); bc.push_back(dof_SY); }\n\t\telse if (strcmp(szbc, \"syz\" ) == 0) { bc.push_back(dof_SY); bc.push_back(dof_SZ); }\n\t\telse if (strcmp(szbc, \"sxz\" ) == 0) { bc.push_back(dof_SX); bc.push_back(dof_SZ); }\n\t\telse if (strcmp(szbc, \"sxyz\") == 0) { bc.push_back(dof_SX); bc.push_back(dof_SY); bc.push_back(dof_SZ); }\n        else if (strcmp(szbc, \"wxy\" ) == 0) { bc.push_back(dof_WX); bc.push_back(dof_WY); }\n        else if (strcmp(szbc, \"wyz\" ) == 0) { bc.push_back(dof_WY); bc.push_back(dof_WZ); }\n        else if (strcmp(szbc, \"wxz\" ) == 0) { bc.push_back(dof_WX); bc.push_back(dof_WZ); }\n        else if (strcmp(szbc, \"wxyz\") == 0) { bc.push_back(dof_WX); bc.push_back(dof_WY); bc.push_back(dof_WZ); }\n        else if (strcmp(szbc, \"gxy\" ) == 0) { bc.push_back(dof_GX); bc.push_back(dof_GY); }\n        else if (strcmp(szbc, \"gyz\" ) == 0) { bc.push_back(dof_GY); bc.push_back(dof_GZ); }\n        else if (strcmp(szbc, \"gxz\" ) == 0) { bc.push_back(dof_GX); bc.push_back(dof_GZ); }\n        else if (strcmp(szbc, \"gxyz\") == 0) { bc.push_back(dof_GX); bc.push_back(dof_GY); bc.push_back(dof_GZ); }\n\t\telse if (strcmp(szbc, \"xyzuvw\") == 0)\n\t\t{\n\t\t\tbc.push_back(dof_X); bc.push_back(dof_Y); bc.push_back(dof_Z);\n\t\t\tbc.push_back(dof_U); bc.push_back(dof_V); bc.push_back(dof_W);\n\t\t}\n        else if (strcmp(szbc, \"xyzsxyz\") == 0)\n        {\n            bc.push_back(dof_X); bc.push_back(dof_Y); bc.push_back(dof_Z);\n            bc.push_back(dof_SX); bc.push_back(dof_SY); bc.push_back(dof_SZ);\n        }\n\t\telse\n\t\t{\n\t\t\t// see if this is a comma seperated list\n\t\t\tif (dofs.ParseDOFString(szbc, bc) == false)\n\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\t\t}\n\t}\n\n\tif (bc.empty()) throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t// create the fixed BC's\n\tFEFixedDOF* pbc = dynamic_cast<FEFixedDOF*>(fecore_new<FEBoundaryCondition>(\"fix\", &fem));\n\tpbc->SetDOFS(bc);\n\n\t// add it to the model\n\tGetBuilder()->AddBC(pbc);\n\n\t// see if the set attribute is defined\n\tconst char* szset = tag.AttributeValue(\"set\", true);\n\tif (szset)\n\t{\n\t\t// make sure the tag is a leaf\n\t\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// process the node set\n\t\tFENodeSet* pns = mesh.FindNodeSet(szset);\n\t\tif (pns == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\n\t\tpbc->SetNodeSet(pns);\n\t}\n\telse\n\t{\n\t\tFENodeSet* nset = new FENodeSet(&fem);\n\t\tfem.GetMesh().AddNodeSet(nset);\n\n\t\t// Read the fixed nodes\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tint n = ReadNodeID(tag);\n\t\t\tif ((n<0) || (n >= NN)) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\t\t\tnset->Add(n);\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\n\t\tpbc->SetNodeSet(nset);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection25::ParseBCFix(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tDOFS& dofs = fem.GetDOFS();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// make sure the tag is a leaf\n\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t// get the required bc attribute\n\tchar szbc[64] = {0};\n\tstrcpy(szbc, tag.AttributeValue(\"bc\"));\n\n\t// process the bc string\n\tvector<int> bc;\n\tdofs.ParseDOFString(szbc, bc);\n\n\t// check the list\n\tif (bc.empty()) throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\tint nbc = (int)bc.size();\n\tfor (int i=0; i<nbc; ++i) if (bc[i] == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t// get the nodeset\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tmap<string, FENodeSet*>::iterator nset = m_NodeSet.find(szset);\n\tif (nset == m_NodeSet.end()) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\tFENodeSet* nodeSet = (*nset).second;\n\n\t// create the fixed BC\n\tFEFixedDOF* pbc = dynamic_cast<FEFixedDOF*>(fecore_new<FEBoundaryCondition>(\"fix\", &fem));\n\tpbc->SetDOFS(bc);\n\tpbc->SetNodeSet(nodeSet);\n\n\t// add it to the model\n\tGetBuilder()->AddBC(pbc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection::ParseBCPrescribe(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// see if this tag defines a set\n\tconst char* szset = tag.AttributeValue(\"set\", true);\n\tif (szset)\n\t{\n\t\t// Find the set\n\t\tFENodeSet* ps = mesh.FindNodeSet(szset);\n\t\tif (ps == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\n\t\t// get the bc attribute\n\t\tconst char* sz = tag.AttributeValue(\"bc\");\n\n\t\t// find the dof index from its symbol\n\t\tint bc = dofs.GetDOF(sz);\n\t\tif (bc == -1) \n\t\t{\n\t\t\t// the temperature degree of freedom was renamed\n\t\t\t// for backward compatibility we need to check for it\n\t\t\tif (strcmp(sz, \"t\") == 0) bc = dofs.GetDOF(\"T\");\n\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\t\t}\n\n\t\t// get the lc attribute\n\t\tsz = tag.AttributeValue(\"lc\");\n\t\tint lc = atoi(sz);\n\n\t\t// make sure this tag is a leaf\n\t\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// get the scale factor\n\t\tdouble s = 1;\n\t\tvalue(tag, s);\n\n\t\t// create the bc\n\t\tFEPrescribedDOF* pdc = dynamic_cast<FEPrescribedDOF*>(fecore_new<FEBoundaryCondition>(\"prescribe\", &fem));\n\t\tpdc->SetScale(s).SetDOF(bc);\n\n\t\tif (lc >= 0)\n\t\t{\n\t\t\tFEParam* p = pdc->GetParameter(\"scale\");\n\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\tfem.AttachLoadController(p, lc);\n\t\t}\n\n\t\t// add this boundary condition to the current step\n\t\tGetBuilder()->AddBC(pdc);\n\n\t\t// add nodes in the nodeset\n\t\tpdc->SetNodeSet(ps);\n\t}\n\telse\n\t{\n\t\t// count how many prescibed nodes there are\n\t\tint ndis = tag.children();\n\n\t\t// determine whether prescribed BC is relative or absolute\n\t\tbool br = false;\n\t\tconst char* sztype = tag.AttributeValue(\"type\",true);\n\t\tif (sztype && strcmp(sztype, \"relative\") == 0) br = true;\n\n\t\t// read the prescribed data\n\t\t++tag;\n\t\tfor (int i=0; i<ndis; ++i)\n\t\t{\n\t\t\tint n = ReadNodeID(tag);\n\t\t\tconst char* sz = tag.AttributeValue(\"bc\");\n\n\t\t\t// get the dof index from its symbol\n\t\t\tint bc = dofs.GetDOF(sz);\n\t\t\tif (bc == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\n\t\t\tsz = tag.AttributeValue(\"lc\");\n\t\t\tint lc = atoi(sz)-1;\n\n\t\t\tdouble scale;\n\t\t\ttag.value(scale);\n\n\t\t\tFEPrescribedDOF* pdc = dynamic_cast<FEPrescribedDOF*>(fecore_new<FEBoundaryCondition>(\"prescribe\", &fem));\n\t\t\tpdc->SetDOF(bc);\n\t\t\tpdc->SetScale(scale);\n\t\t\tpdc->SetRelativeFlag(br);\n\n\t\t\tFENodeSet* ps = new FENodeSet(&fem);\n\t\t\tps->Add(n);\n\t\t\tpdc->SetNodeSet(ps);\n\n\t\t\tif (lc >= 0)\n\t\t\t{\n\t\t\t\tFEParam* p = pdc->GetParameter(\"scale\");\n\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\tfem.AttachLoadController(p, lc);\n\t\t\t}\n\n\t\t\t// add this boundary condition to the current step\n\t\t\tGetBuilder()->AddBC(pdc);\n\n\t\t\t// next tag\n\t\t\t++tag;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection2::ParseBCPrescribe(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\tint NN = mesh.Nodes();\n\n\t// count how many prescibed nodes there are\n\tint ndis = tag.children();\n\n\t// determine whether prescribed BC is relative or absolute\n\tbool br = false;\n\tconst char* sztype = tag.AttributeValue(\"type\",true);\n\tif (sztype && strcmp(sztype, \"relative\") == 0) br = true;\n\n\t// get the BC\n\tconst char* sz = tag.AttributeValue(\"bc\");\n\tint bc = dofs.GetDOF(sz);\n\tif (bc == -1) \n\t{\n\t\t// the temperature degree of freedom was renamed\n\t\t// for backward compatibility we need to check for it\n\t\tif (strcmp(sz, \"t\") == 0) bc = dofs.GetDOF(\"T\");\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\t}\n\n\t// get the load curve number\n\tint lc = -1;\n\tsz = tag.AttributeValue(\"lc\", true);\n\tif (sz) { lc = atoi(sz) - 1; }\n\n\t// see if the scale attribute is defined\n\tdouble scale = 1.0;\n\ttag.AttributeValue(\"scale\", scale, true);\n\n\t// if the value is a leaf, the scale factor is defined as the value\n\tif (tag.isleaf())\n\t{\n\t\ttag.value(scale);\n\t}\n\n\t// create a prescribed bc\n\tFEPrescribedDOF* pdc = dynamic_cast<FEPrescribedDOF*>(fecore_new<FEBoundaryCondition>(\"prescribe\", &fem));\n\tpdc->SetDOF(bc);\n\tpdc->SetScale(scale);\n\tpdc->SetRelativeFlag(br);\n\n\tif (lc >= 0)\n\t{\n\t\tFEParam* p = pdc->GetParameter(\"scale\");\n\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\tfem.AttachLoadController(p, lc);\n\t}\n\n\t// add this boundary condition to the current step\n\tGetBuilder()->AddBC(pdc);\n\n\t// see if there is a set defined\n\tconst char* szset = tag.AttributeValue(\"set\", true);\n\tif (szset)\n\t{\n\t\t// make sure this is a leaf tag\n\t\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// find the node set\n\t\tFENodeSet* pns = mesh.FindNodeSet(szset);\n\t\tif (pns == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\n\t\t// add the nodes\n\t\tpdc->SetNodeSet(pns);\n\t}\n\telse\n\t{\n\t\tFENodeSet* nset = new FENodeSet(&fem);\n\t\tfem.GetMesh().AddNodeSet(nset);\n\n\t\t// read the prescribed data\n\t\t++tag;\n\t\tfor (int i=0; i<ndis; ++i)\n\t\t{\n\t\t\t// get the node ID\n\t\t\tint n = ReadNodeID(tag);\n\t\t\tvalue(tag, scale);\n\n\t\t\t// TODO: I need to create a data map for this BC and assign the values\n\t\t\t// to that data map\n\n\t\t\tnset->Add(n);\n\t\t\t++tag;\n\t\t}\n\n\t\tpdc->SetNodeSet(nset);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! In version 2.5 all prescribed boundary conditions use a node set to define\n//! the list of nodes to which the bc is applied.\nvoid FEBioBoundarySection25::ParseBCPrescribe(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\tint NN = mesh.Nodes();\n\n\t// get the BC\n\tconst char* sz = tag.AttributeValue(\"bc\");\n\tint bc = dofs.GetDOF(sz);\n\tif (bc == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\n\t// Boundary conditions can be applied to node sets or surfaces\n\t// depending on whether the node_set or surface attribute is defined\n\tFENodeSet* nodeSet = nullptr;\n\tFEFacetSet* facetSet = nullptr;\n\n\t// get the node set or surface\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tif (szset) {\n\t\tmap<string, FENodeSet*>::iterator nset = m_NodeSet.find(szset);\n\t\tif (nset == m_NodeSet.end()) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\t\tnodeSet = (*nset).second;\n\t}\n\telse\n\t{\n\t\tconst char* szset = tag.AttributeValue(\"surface\");\n\t\tif (szset) {\n\t\t\tfacetSet = mesh.FindFacetSet(szset);\n\t\t\tif (facetSet == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szset);\n\t\t}\n\t\telse throw XMLReader::MissingAttribute(tag, \"node_set\");\n\t}\n\n\t// create a prescribed bc\n\tFEPrescribedDOF* pdc = dynamic_cast<FEPrescribedDOF*>(fecore_new<FEBoundaryCondition>(\"prescribe\", &fem));\n\tpdc->SetDOF(bc);\n\n\t// Apply either the node set or the surface\n\tif (nodeSet) pdc->SetNodeSet(nodeSet);\n\telse if (facetSet)\n\t{\n\t\tFENodeSet* set = new FENodeSet(&fem);\n\t\tset->Add(facetSet->GetNodeList());\n\t\tpdc->SetNodeSet(set);\n\t}\n\n\t// add this boundary condition to the current step\n\tGetBuilder()->AddBC(pdc);\n\n\t// Read the parameter list\n\tFEParameterList& pl = pdc->GetParameterList();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl, 0, 0) == false)\n\t\t{\n\t\t\tif (tag == \"value\")\n\t\t\t{\n\t\t\t\tfeLogWarningEx((&fem), \"The value parameter of the prescribed bc is deprecated.\");\n\n\t\t\t\t// NOTE: This will only work if the scale was set to 1!!\n\t\t\t\tconst char* sznodedata = tag.AttributeValue(\"node_data\", true);\n\t\t\t\tif (sznodedata)\n\t\t\t\t{\n\t\t\t\t\tFEParam* pp = pdc->GetParameter(\"scale\"); assert(pp);\n\t\t\t\t\tGetBuilder()->AddMappedParameter(pp, pdc, sznodedata);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdouble v;\n\t\t\t\t\ttag.value(v);\n\t\t\t\t\tpdc->SetScale(v);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection25::ParseBC(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the type string\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t// create the boundary condition\n\tFEBoundaryCondition* pdc = fecore_new<FEBoundaryCondition>(sztype, &fem);\n\tif (pdc == 0) throw XMLReader::InvalidTag(tag);\n\n\t// get the selection\n\tconst char* szset = tag.AttributeValue(\"node_set\", true);\n\tif (dynamic_cast<FENodalBC*>(pdc))\n\t{\n\t\tFENodalBC* pnbc = dynamic_cast<FENodalBC*>(pdc);\n\t\tFENodeSet* nodeSet = mesh.FindNodeSet(szset);\n\t\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t\t// Set the node set\n\t\tpnbc->SetNodeSet(nodeSet);\n\n\t\t// Read the parameter list\n\t\tFEParameterList& pl = pdc->GetParameterList();\n\t\tReadParameterList(tag, pl);\n\t}\n\telse if (dynamic_cast<FESurfaceBC*>(pdc))\n\t{\n\t\tFESurfaceBC* sbc = dynamic_cast<FESurfaceBC*>(pdc);\n\n\t\t// if a node set is not defined, see if a surface is defined\n\t\tszset = tag.AttributeValue(\"surface\");\n\t\tFEFacetSet* set = mesh.FindFacetSet(szset);\n\n\t\t// Read the parameter list (before setting the surface)\n\t\tFEParameterList& pl = pdc->GetParameterList();\n\t\tReadParameterList(tag, pl);\n\n\t\t// add the surface\n\t\tFESurface* surf = fecore_alloc(FESurface, GetFEModel());\n\t\tsurf->Create(*set);\n\t\tmesh.AddSurface(surf);\n\n\t\tsbc->SetSurface(surf);\n\t}\n\n\t// add this boundary condition to the current step\n\tGetBuilder()->AddBC(pdc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection::ParseSpringSection(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// determine the spring type\n\tconst char* szt = tag.AttributeValue(\"type\", true);\n\tif (szt == 0) szt = \"linear\";\n\tFEDiscreteMaterial* pm = dynamic_cast<FEDiscreteMaterial*>(fecore_new<FEMaterial>(szt, &fem));\n\tif (pm == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\n\t// create a new spring \"domain\"\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tFE_Element_Spec spec;\n\tspec.eclass = FE_ELEM_TRUSS;\n\tspec.eshape = ET_TRUSS2;\n\tspec.etype  = FE_DISCRETE;\n\tFEDiscreteDomain* pd = dynamic_cast<FEDiscreteDomain*>(febio.CreateDomain(spec, &mesh, pm));\n\tmesh.AddDomain(pd);\n\n\tpd->Create(1, spec);\n\tFEDiscreteElement& de = pd->Element(0);\n\tde.SetID(++GetBuilder()->m_maxid);\n\t\n\t// add a new material for each spring\n\tfem.AddMaterial(pm);\n\tpm->SetID(fem.Materials());\n\tpd->SetMatID(fem.Materials()-1);\n\n\t// read spring discrete elements\n\t++tag;\n\tdo\n\t{\n\t\t// read the required node tag\n\t\tif (tag == \"node\")\n\t\t{\n\t\t\tint n[2];\n\t\t\ttag.value(n, 2);\n\t\t\tde.m_node[0] = n[0]-1;\n\t\t\tde.m_node[1] = n[1]-1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// read the actual spring material parameters\n\t\t\tFEParameterList& pl = pm->GetParameterList();\n\t\t\tif (ReadParameter(tag, pl) == 0)\n\t\t\t{\n\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tpd->CreateMaterialPointData();\n}\n\n//-----------------------------------------------------------------------------\n//! Parse the linear constraints section of the xml input file\n//! This section is a subsection of the Boundary section\n\nvoid FEBioBoundarySection::ParseConstraints(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// make sure there is a constraint defined\n\tif (tag.isleaf()) return;\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t// read the parent node\n\tint nodeID;\n\ttag.AttributeValue(\"node\", nodeID);\n\tint parentNode = feb->FindNodeFromID(nodeID);\n\n\t// get the dofs\n\tconst char* szbc = tag.AttributeValue(\"bc\");\n\tvector<int> dofList;\n\tdofs.ParseDOFString(szbc, dofList);\n\tint ndofs = (int) dofList.size();\n\n\t// allocate linear constraints\n\tvector<FELinearConstraint*> LC;\n\tfor (int i=0; i<ndofs; ++i)\n\t{\n\t\tint dof = dofList[i];\n\t\tif (dof < 0) throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\tLC[i] = fecore_alloc(FELinearConstraint, &fem);\n\t\tLC[i]->SetParentDof(dof, parentNode);\n\t}\n\n\t// read the child nodes\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"node\")\n\t\t{\n\t\t\t// get the node\n\t\t\tint childNode = ReadNodeID(tag);\n\n\t\t\t// get the dof\n\t\t\t// (if ommitted we take the parent dof)\n\t\t\tint childDOF = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\", true);\n\t\t\tif (szbc)\n\t\t\t{\n\t\t\t\tchildDOF = dofs.GetDOF(szbc);\n\t\t\t\tif (childDOF < 0) throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\t\t\t}\n\n\t\t\t// get the coefficient\n\t\t\tdouble val;\n\t\t\ttag.value(val);\n\n\t\t\t// add it to the list\n\t\t\tfor (int i=0; i<ndofs; ++i)\n\t\t\t{\n\t\t\t\tint ndof = (childDOF < 0 ? LC[i]->GetParentDof() : childDOF);\n\t\t\t\tLC[i]->AddChildDof(ndof, childNode, val);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// add the linear constraint to the system\n\tfor (int i=0; i<ndofs; ++i)\n\t{\n\t\tfem.GetLinearConstraintManager().AddLinearConstraint(LC[i]);\n\t\tGetBuilder()->AddComponent(LC[i]);\n\t}\n}\n\nvoid FEBioBoundarySection25::ParseMergeConstraint(XMLTag& tag)\n{\n\t// make sure this is an empty tag\n\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t// get the dofs\n\tconst char* szbc = tag.AttributeValue(\"bc\");\n\n\t// get the surface pair name\n\tconst char* szsp = tag.AttributeValue(\"surface_pair\");\n\n\t// get the dof list\n\tFEModel& fem = *GetFEModel();\n\tDOFS& dof = fem.GetDOFS();\n\tvector<int> dofs;\n\tdof.ParseDOFString(szbc, dofs);\n\tif (dofs.empty()) throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t// get the surfaces\n\tFEMesh& mesh = fem.GetMesh();\n\tFESurfacePair* sp = mesh.FindSurfacePair(szsp);\n\tif (sp == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szsp);\n\n\t// merge the interfaces\n\tFEMergedConstraint merge(fem);\n\tif (merge.Merge(sp->GetSecondarySurface(), sp->GetPrimarySurface(), dofs) == false)\n\t\tthrow XMLReader::InvalidTag(tag);\n}\n\nvoid FEBioBoundarySection25::ParsePeriodicLinearConstraint(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\tFEPeriodicLinearConstraint plc(fem);\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"constrain\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"surface_pair\", true);\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tFESurfacePair* spair = mesh.FindSurfacePair(sz);\n\t\t\t\tif (spair == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", sz);\n\n\t\t\t\tFESurface* surf2 = fecore_alloc(FESurface, fem); feb->BuildSurface(*surf2, *spair->GetSecondarySurface());\n\t\t\t\tFESurface* surf1 = fecore_alloc(FESurface, fem); feb->BuildSurface(*surf1, *spair->GetPrimarySurface());\n\t\t\t\tplc.AddNodeSetPair(surf2->GetNodeList(), surf1->GetNodeList());\n\t\t\t}\n\t\t\telse throw XMLReader::MissingAttribute(tag, \"surface_pair\");\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// generate the linear constraints\n\tplc.GenerateConstraints(fem);\n\n\t// don't forget to activate\n\n}\n\nvoid FEBioBoundarySection25::ParsePeriodicLinearConstraint2O(XMLTag& tag)\n{\n\tassert(false);\n\tthrow XMLReader::InvalidTag(tag);\n\n/*\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\tFEPeriodicLinearConstraint2O plc;\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"constrain\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"surface_pair\");\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tFESurfacePair* spair = mesh.FindSurfacePair(sz);\n\t\t\t\tif (spair == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", sz);\n\n\t\t\t\tFESurface* surf2 = fecore_alloc(FESurface, fem); feb->BuildSurface(*surf2, *spair->GetSecondarySurface());\n\t\t\t\tFESurface* surf1 = fecore_alloc(FESurface, fem); feb->BuildSurface(*surf1, *spair->GetPrimarySurface());\n\t\t\t\tplc.AddNodeSetPair(surf2->GetNodeList(), surf1->GetNodeList());\n\t\t\t}\n\t\t\telse throw XMLReader::MissingAttribute(tag, \"surface_pair\");\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// generate the linear constraints\n\tif (plc.GenerateConstraints(fem) == false)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\t*/\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection::ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\t// get the parameter list\n\tFEParameterList& pl = pci->GetParameterList();\n\n\t// read the parameters\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false)\n\t\t{\n\t\t\tif (tag == \"surface\")\n\t\t\t{\n\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\t\t\t\tint ntype = 0;\n\t\t\t\tif (strcmp(sztype, \"master\") == 0) ntype = 1;\n\t\t\t\telse if (strcmp(sztype, \"slave\") == 0) ntype = 2;\n\n\t\t\t\tFESurface& s = *(ntype == 1? pci->GetSecondarySurface() : pci->GetPrimarySurface());\n\t\t\t\tm.AddSurface(&s);\n\n\t\t\t\tint nfmt = 0;\n\t\t\t\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\t\t\t\tif (szfmt)\n\t\t\t\t{\n\t\t\t\t\tif (strcmp(szfmt, \"face nodes\") == 0) nfmt = 0;\n\t\t\t\t\telse if (strcmp(szfmt, \"element face\") == 0) nfmt = 1;\n\t\t\t\t}\n\n\t\t\t\t// read the surface section\n\t\t\t\tParseSurfaceSection(tag, s, nfmt, pci->UseNodalIntegration());\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n\n//-----------------------------------------------------------------------------\n//! Parses the contact section of the xml input file\n//! The contact section is a subsection of the boundary section\n\nvoid FEBioBoundarySection::ParseContactSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t// get the type attribute\n\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t// Not all contact interfaces can be parsed automatically.\n\t// First, check all these special cases.\n\tif (strcmp(szt, \"rigid_wall\") == 0)\n\t{\n\t\t// --- R I G I D   W A L L   I N T E R F A C E ---\n\n\t\tFESurfacePairConstraint* ps = fecore_new<FESurfacePairConstraint>(szt, GetFEModel());\n\t\tif (ps)\n\t\t{\n\t\t\tfem.AddSurfacePairConstraint(ps);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (ReadParameter(tag, ps) == false)\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"surface\")\n\t\t\t\t\t{\n\t\t\t\t\t\tFESurface& s = *ps->GetPrimarySurface();\n\n\t\t\t\t\t\tint nfmt = 0;\n\t\t\t\t\t\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\t\t\t\t\t\tif (szfmt)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (strcmp(szfmt, \"face nodes\") == 0) nfmt = 0;\n\t\t\t\t\t\t\telse if (strcmp(szfmt, \"element face\") == 0) nfmt = 1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// read the surface section\n\t\t\t\t\t\tParseSurfaceSection(tag, s, nfmt, true);\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\t\t}\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\t}\n\telse if (strcmp(szt, \"rigid\") == 0)\n\t{\n\t\t// --- R I G I D   B O D Y   I N T E R F A C E ---\n\n\t\t// count how many rigid nodes there are\n\t\tint nrn= 0;\n\t\tXMLTag t(tag); ++t;\n\t\twhile (!t.isend()) { nrn++; ++t; }\n\n\t\t++tag;\n\t\tint id, rb, rbp = -1;\n\t\tFENodalBC* prn = 0;\n\t\tFENodeSet* ns = 0;\n\t\tfor (int i=0; i<nrn; ++i)\n\t\t{\n\t\t\tid = atoi(tag.AttributeValue(\"id\"))-1;\n\t\t\trb = atoi(tag.AttributeValue(\"rb\"));\n\n\t\t\tif ((prn == 0) || (rb != rbp))\n\t\t\t{\n\t\t\t\tprn = fecore_new_class<FENodalBC>(\"FERigidNodeSet\", &fem);\n\n\t\t\t\tprn->SetParameter(\"rb\", rb);\n\n\t\t\t\tns = new FENodeSet(&fem);\n\t\t\t\tprn->SetNodeSet(ns);\n\n\t\t\t\t// the default shell bc depends on the shell formulation\n\t\t\t\t// hinged shell = 0\n\t\t\t\t// clamped shell = 1\n\t\t\t\tprn->SetParameter(\"clamp_shells\", feb->m_default_shell == OLD_SHELL ? 0 : 1);\n\n\t\t\t\tfeb->AddBC(prn);\n\t\t\t\trbp = rb;\n\t\t\t}\n\t\t\tns->Add(id);\n\n\t\t\t++tag;\n\t\t}\n\t}\n\telse if (strcmp(szt, \"linear constraint\") == 0)\n\t{\n\t\tFEModel& fem = *GetFEModel();\n\n\t\t// make sure there is a constraint defined\n\t\tif (tag.isleaf()) return;\n\n\t\t// create a new linear constraint manager\n\t\tFELinearConstraintSet* pLCS = dynamic_cast<FELinearConstraintSet*>(fecore_new<FENLConstraint>(szt, GetFEModel()));\n\t\tfem.AddNonlinearConstraint(pLCS);\n\n\t\t// read the linear constraints\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"linear_constraint\")\n\t\t\t{\n\t\t\t\tFEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, &fem);\n\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tint node, bc;\n\t\t\t\t\tdouble val;\n\t\t\t\t\tif (tag == \"node\")\n\t\t\t\t\t{\n\t\t\t\t\t\ttag.value(val);\n\n\t\t\t\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\t\t\t\tnode = atoi(szid);\n\n\t\t\t\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\t\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\t\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\t\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t\t\t\tpLC->AddDOF(node, bc, val);\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t++tag;\n\t\t\t\t}\n\t\t\t\twhile (!tag.isend());\n\n\t\t\t\t// add the linear constraint to the system\n\t\t\t\tpLCS->add(pLC);\n\t\t\t}\n\t\t\telse if (ReadParameter(tag, pLCS) == false)\n\t\t\t{\n\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\telse\n\t{\n\t\t// If we get here, we try to create a contact interface\n\t\t// using the FEBio kernel. \n\t\tFESurfacePairConstraint* pci = fecore_new<FESurfacePairConstraint>(szt, GetFEModel());\n\t\tif (pci)\n\t\t{\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddContactInterface(pci);\n\n\t\t\t// parse the interface\n\t\t\tParseContactInterface(tag, pci);\n\t\t}\n\t\telse \n\t\t{\n\t\t\t// some nonlinear constraints are also defined in the Contact section, so let's try that next.\n\t\t\t// TODO: These are mostly rigid constraints. Therefore, I would like to move this elsewhere (maybe in the new Rigid section?)\n\t\t\tFENLConstraint* pnlc = fecore_new<FENLConstraint>(szt, GetFEModel());\n\t\t\tif (pnlc)\n\t\t\t{\n\t\t\t\tReadParameterList(tag, pnlc);\n\t\t\t\tfem.AddNonlinearConstraint(pnlc);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Rigid node sets are defined in the Boundary section since version 2.5\n// (Used to be defined in the Contact section)\nvoid FEBioBoundarySection25::ParseBCRigid(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tFEModelBuilder* feb = GetBuilder();\n\tint NMAT = fem.Materials();\n\n\t// get the rigid body material ID\n\tint rb = -1;\n\ttag.AttributeValue(\"rb\", rb);\n\n\t// make sure we have a valid rigid body reference\n\tif ((rb <= 0)||(rb>NMAT)) throw XMLReader::InvalidAttributeValue(tag, \"rb\", tag.AttributeValue(\"rb\"));\n\n\t// get the nodeset\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nodeSet = mesh.FindNodeSet(szset);\n\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// create new rigid node set\n\tFENodalBC* prn = fecore_new_class<FENodalBC>(\"FERigidNodeSet\", &fem);\n\n\t// the default shell bc depends on the shell formulation\n\tprn->SetParameter(\"clamp_shells\", feb->m_default_shell == OLD_SHELL ? 0 : 1);\n\tprn->SetParameter(\"rb\", rb);\n\tprn->SetNodeSet(nodeSet);\n\n\t// add it to the model\n\tfeb->AddBC(prn);\n\t\n\t// read the parameter list\n\tReadParameterList(tag, prn);\n}\n\n//-----------------------------------------------------------------------------\n// The rigid body \"constraints\" are moved to the Boundary section in 2.5\nvoid FEBioBoundarySection25::ParseRigidBody(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEModelBuilder& feb = *GetBuilder();\n\n\tconst char* szm = tag.AttributeValue(\"mat\");\n\tassert(szm);\n\n\t// get the material ID\n\tint nmat = atoi(szm);\n\tif ((nmat <= 0) || (nmat > fem.Materials())) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szm);\n\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"prescribed\")\n\t\t{\n\t\t\t// get the dof\n\t\t\tint bc = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\telse if (strcmp(szbc, \"Rx\") == 0) bc = 3;\n\t\t\telse if (strcmp(szbc, \"Ry\") == 0) bc = 4;\n\t\t\telse if (strcmp(szbc, \"Rz\") == 0) bc = 5;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t// get the loadcurve\n\t\t\tconst char* szlc = tag.AttributeValue(\"lc\");\n\t\t\tint lc = atoi(szlc) - 1;\n\n\t\t\t// get the (optional) type attribute\n\t\t\tbool brel = false;\n\t\t\tconst char* szrel = tag.AttributeValue(\"type\", true);\n\t\t\tif (szrel)\n\t\t\t{\n\t\t\t\tif      (strcmp(szrel, \"relative\" ) == 0) brel = true;\n\t\t\t\telse if (strcmp(szrel, \"absolute\" ) == 0) brel = false;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szrel);\n\t\t\t}\n\n\t\t\tdouble val = 0.0;\n\t\t\tvalue(tag, val);\n\n\t\t\t// create the rigid displacement constraint\n\t\t\tFEStepComponent* pDC = fecore_new_class<FEBoundaryCondition>(\"FERigidPrescribedOld\", &fem);\n\t\t\tfeb.AddRigidComponent(pDC);\n\n\t\t\tpDC->SetParameter(\"rb\", nmat);\n\t\t\tpDC->SetParameter(\"dof\", bc);\n\t\t\tpDC->SetParameter(\"relative\", brel);\n\t\t\tpDC->SetParameter(\"value\", val);\n\n\t\t\t// assign a load curve\n\t\t\tif (lc >= 0)\n\t\t\t{\n\t\t\t\tFEParam* p = pDC->GetParameter(\"value\");\n\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"force\")\n\t\t{\n\t\t\t// get the dof\n\t\t\tint bc = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\telse if (strcmp(szbc, \"Rx\") == 0) bc = 3;\n\t\t\telse if (strcmp(szbc, \"Ry\") == 0) bc = 4;\n\t\t\telse if (strcmp(szbc, \"Rz\") == 0) bc = 5;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t// get the type\n\t\t\tint ntype = 0; // FERigidBodyForce::FORCE_LOAD;\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype)\n\t\t\t{\n\t\t\t\tif      (strcmp(sztype, \"ramp\"  ) == 0) ntype = 2; //FERigidBodyForce::FORCE_TARGET;\n\t\t\t\telse if (strcmp(sztype, \"follow\") == 0) ntype = 1; //FERigidBodyForce::FORCE_FOLLOW;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t}\n\n\t\t\t// get the loadcurve\n\t\t\tconst char* szlc = tag.AttributeValue(\"lc\", true);\n\t\t\tint lc = -1;\n\t\t\tif (szlc) lc = atoi(szlc) - 1;\n\n\t\t\t// make sure there is a loadcurve for type=0 forces\n\t\t\tif ((ntype == 0)&&(lc==-1)) throw XMLReader::MissingAttribute(tag, \"lc\");\n\n\t\t\tdouble val = 0.0;\n\t\t\tvalue(tag, val);\n\n\t\t\t// create the rigid body force/moment\n\t\t\tFEModelLoad* pFC = nullptr;\n\t\t\tif (bc < 3)\n\t\t\t{\n\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_force\", &fem);\n\n\t\t\t\tpFC->SetParameter(\"load_type\", ntype);\n\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\tpFC->SetParameter(\"dof\", bc);\n\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_moment\", &fem);\n\n\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\tpFC->SetParameter(\"dof\", bc - 3);\n\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t}\n\n\t\t\tfeb.AddModelLoad(pFC);\n\n\t\t\tif (lc >= 0)\n\t\t\t{\n\t\t\t\tFEParam* p = pFC->GetParameter(\"value\");\n\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"fixed\")\n\t\t{\n\t\t\t// get the dof\n\t\t\tint bc = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\telse if (strcmp(szbc, \"Rx\") == 0) bc = 3;\n\t\t\telse if (strcmp(szbc, \"Ry\") == 0) bc = 4;\n\t\t\telse if (strcmp(szbc, \"Rz\") == 0) bc = 5;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t// create the fixed dof\n\t\t\tFEBoundaryCondition* pBC = fecore_new_class<FEBoundaryCondition>(\"FERigidFixedBCOld\",  &fem);\n\t\t\tfeb.AddRigidComponent(pBC);\n\n\t\t\tpBC->SetParameter(\"rb\", nmat);\n\n\t\t\tvector<int> dofs; dofs.push_back(bc);\n\t\t\tpBC->SetParameter(\"dofs\", dofs);\n\n\t\t}\n\t\telse if (tag == \"initial_velocity\")\n\t\t{\n\t\t\t// get the initial velocity\n\t\t\tvec3d v;\n\t\t\tvalue(tag, v);\n\n\t\t\t// create the initial condition\n\t\t\tFEStepComponent* pic = fecore_new_class<FEInitialCondition>(\"FERigidBodyVelocity\", &fem);\n\t\t\tpic->SetParameter(\"rb\", nmat);\n\t\t\tpic->SetParameter(\"value\", v);\n\n\t\t\t// add to model\n\t\t\tfeb.AddRigidComponent(pic);\n\t\t}\n\t\telse if (tag == \"initial_angular_velocity\")\n\t\t{\n\t\t\t// get the initial angular velocity\n\t\t\tvec3d w;\n\t\t\tvalue(tag, w);\n\n\t\t\t// create the initial condition\n\t\t\tFEStepComponent* pic = fecore_new_class<FEInitialCondition>(\"FERigidBodyAngularVelocity\", &fem);\n\t\t\tpic->SetParameter(\"rb\", nmat);\n\t\t\tpic->SetParameter(\"value\", w);\n\n\t\t\t// add to model\n\t\t\tfeb.AddRigidComponent(pic);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioBoundarySection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n#include \"FECore/FESurfacePairConstraint.h\"\n#include <map>\n\n//-----------------------------------------------------------------------------\n// Boundary Section\nclass FEBioBoundarySection : public FEFileSection\n{\npublic:\n\tFEBioBoundarySection(FEFileImport* pim) : FEFileSection(pim){}\n\nprotected:\n\tvoid ParseBCFix         (XMLTag& tag);\n\tvoid ParseBCPrescribe   (XMLTag& tag);\n\tvoid ParseContactSection(XMLTag& tag);\n\tvoid ParseConstraints   (XMLTag& tag);\n\tvoid ParseSpringSection (XMLTag& tag);\n\nprotected:\n\tvoid ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* psi);\n\tbool ParseSurfaceSection  (XMLTag& tag, FESurface& s, int nfmt, bool bnodal);\n\nprotected:\n\tvoid ParseRigidJoint      (XMLTag& tag);\n\tvoid ParseLinearConstraint(XMLTag& tag);\n\tvoid ParseRigidWall       (XMLTag& tag);\n\tvoid ParseRigidContact    (XMLTag& tag);\n\nprotected:\n\tvoid BuildNodeSetMap();\n\n\tvoid AddFixedBC(FENodeSet* set, int bc);\n\nprotected:\n\tstd::map<std::string, FENodeSet*>\tm_NodeSet;\t// map for faster lookup of node sets\n};\n\n//-----------------------------------------------------------------------------\n// older formats (1.2)\nclass FEBioBoundarySection1x : public FEBioBoundarySection\n{\npublic:\n\tFEBioBoundarySection1x(FEFileImport* imp) : FEBioBoundarySection(imp){}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// version 2.0\nclass FEBioBoundarySection2 : public FEBioBoundarySection\n{\npublic:\n\tFEBioBoundarySection2(FEFileImport* imp) : FEBioBoundarySection(imp){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseBCFix      (XMLTag& tag);\n\tvoid ParseBCPrescribe(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// version 2.5\nclass FEBioBoundarySection25 : public FEBioBoundarySection\n{\npublic:\n\tFEBioBoundarySection25(FEFileImport* imp) : FEBioBoundarySection(imp){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseBCFix      (XMLTag& tag);\n\tvoid ParseBCPrescribe(XMLTag& tag);\n\tvoid ParseBCRigid    (XMLTag& tag);\n\tvoid ParseRigidBody  (XMLTag& tag);\n\tvoid ParseBC         (XMLTag& tag);\n\n\tvoid ParsePeriodicLinearConstraint  (XMLTag& tag); // version 2.5 (temporary construction)\n\tvoid ParsePeriodicLinearConstraint2O(XMLTag& tag); // version 2.5 (temporary construction)\n\tvoid ParseMergeConstraint           (XMLTag& tag); // version 2.5\n};\n"
  },
  {
    "path": "FEBioXML/FEBioBoundarySection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioBoundarySection3.h\"\n#include <FECore/FENodalBC.h>\n#include <FECore/FESurfaceBC.h>\n#include <FECore/FEModel.h>\n#include <FECore/FELinearConstraintManager.h>\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection3::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t// build the node set map for faster lookup\n\tBuildNodeSetMap();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"bc\") ParseBC(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioBoundarySection3::ParseBC(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\n\t// get the type string\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t// handle \"rigid\" bc separately\n\tif (strcmp(sztype, \"rigid\") == 0)\n\t{\n\t\tParseBCRigid(tag);\n\t\treturn;\n\t}\n\n\t// handle linar constraints\n\tif (strcmp(sztype, \"linear constraint\") == 0)\n\t{\n\t\tParseLinearConstraint(tag);\n\t\treturn;\n\t}\n\n\t// create the boundary condition\n\tFEBoundaryCondition* pbc = fecore_new<FEBoundaryCondition>(sztype, fem);\n\tif (pbc == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\");\n\n\t// read the (optional) name \n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) pbc->SetName(szname);\n\n\t// get the node set\n\tif (dynamic_cast<FENodalBC*>(pbc))\n\t{\n\t\tFENodalBC* nbc = dynamic_cast<FENodalBC*>(pbc); assert(nbc);\n\n\t\t// read required node_set attribute\n\t\tconst char* szset = tag.AttributeValue(\"node_set\");\n\t\tFENodeSet* nodeSet = GetBuilder()->FindNodeSet(szset);\n\t\tif (nodeSet == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t\tnbc->SetNodeSet(nodeSet);\n\t}\n\n\t// get the surface\n\tif (dynamic_cast<FESurfaceBC*>(pbc))\n\t{\n\t\tFESurfaceBC* sbc = dynamic_cast<FESurfaceBC*>(pbc);\n\n\t\t// read required surface attribute\n\t\tconst char* surfaceName = tag.AttributeValue(\"surface\");\n\t\tFEFacetSet* pface = mesh.FindFacetSet(surfaceName);\n\t\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", surfaceName);\n\n\t\t// create a surface from this facet set\n\t\tFESurface* psurf = fecore_alloc(FESurface, fem);\n\t\tGetBuilder()->BuildSurface(*psurf, *pface);\n\n\t\t// assign it\n\t\tmesh.AddSurface(psurf);\n\t\tsbc->SetSurface(psurf);\n\t}\n\n\t// add this boundary condition to the current step\n\tGetBuilder()->AddBC(pbc);\n\n\t// Read the parameter list\n\tReadParameterList(tag, pbc);\n}\n\n//-----------------------------------------------------------------------------\n// Rigid node sets are defined in the Boundary section since version 2.5\n// (Used to be defined in the Contact section)\nvoid FEBioBoundarySection3::ParseBCRigid(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tFEModelBuilder* feb = GetBuilder();\n\tint NMAT = fem.Materials();\n\n\t// get the nodeset\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nodeSet = GetBuilder()->FindNodeSet(szset);\n\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// create new rigid node set\n\tFENodalBC* prn = fecore_new_class<FENodalBC>(\"FERigidNodeSet\", &fem);\n\n\tprn->SetNodeSet(nodeSet);\n\n\t// the default shell bc depends on the shell formulation\n\t// hinged shell = 0\n\t// clamped shell = 1\n\tprn->SetParameter(\"clamp_shells\", feb->m_default_shell == OLD_SHELL ? 0 : 1);\n\n\tfeb->AddBC(prn);\n\n\t// read the parameter list\n\tReadParameterList(tag, prn);\n}\n\n\n//-----------------------------------------------------------------------------\n//! Parse the linear constraints section of the xml input file\n//! This section is a subsection of the Boundary section\n\nvoid FEBioBoundarySection3::ParseLinearConstraint(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// make sure there is a constraint defined\n\tif (tag.isleaf()) return;\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\tFELinearConstraint* lc = fecore_alloc(FELinearConstraint, &fem);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"node\")\n\t\t{\n\t\t\tint nodeId;\n\t\t\ttag.value(nodeId);\n\t\t\tlc->SetParentNode(feb->FindNodeFromID(nodeId));\n\t\t}\n\t\telse if (tag == \"dof\")\n\t\t{\n\t\t\tlc->SetParentDof(dofs.GetDOF(tag.szvalue()));\n\t\t}\n\t\telse if (tag == \"offset\")\n\t\t{\n\t\t\tdouble d = 0.0;\n\t\t\ttag.value(d);\n\t\t\tlc->SetOffset(d);\n\n\t\t\tconst char* szlc = tag.AttributeValue(\"lc\", true);\n\t\t\tif (szlc)\n\t\t\t{\n\t\t\t\tint l = atoi(szlc) - 1;\n\t\t\t\tFEParam* pp = lc->FindParameter(\"offset\"); assert(pp);\n\t\t\t\tif (pp) fem.AttachLoadController(pp, l);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"child_dof\")\n\t\t{\n\t\t\tFELinearConstraintDOF* dof = new FELinearConstraintDOF(&fem);\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"node\")\n\t\t\t\t{\n\t\t\t\t\tint nodeId;\n\t\t\t\t\ttag.value(nodeId);\n\t\t\t\t\tdof->node = feb->FindNodeFromID(nodeId);\n\t\t\t\t}\n\t\t\t\telse if (tag == \"dof\")\n\t\t\t\t{\n\t\t\t\t\tdof->dof = dofs.GetDOF(tag.szvalue());\n\t\t\t\t}\n\t\t\t\telse if (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tdouble v;\n\t\t\t\t\ttag.value(v);\n\t\t\t\t\tdof->val = v;\n\n\t\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\", true);\n\t\t\t\t\tif (szlc)\n\t\t\t\t\t{\n\t\t\t\t\t\tint lc = atoi(szlc) - 1;\n\t\t\t\t\t\tFEParam* pp = dof->FindParameter(\"value\"); assert(pp);\n\t\t\t\t\t\tif (pp) fem.AttachLoadController(pp, lc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} \n\t\t\twhile (!tag.isend());\n\n\t\t\tlc->AddChildDof(dof);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// add the linear constraint to the system\n\tfem.GetLinearConstraintManager().AddLinearConstraint(lc);\n\tGetBuilder()->AddComponent(lc);\n}"
  },
  {
    "path": "FEBioXML/FEBioBoundarySection3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioBoundarySection.h\"\n\n//-----------------------------------------------------------------------------\n// version 3.0\nclass FEBioBoundarySection3 : public FEBioBoundarySection\n{\npublic:\n\tFEBioBoundarySection3(FEFileImport* imp) : FEBioBoundarySection(imp) {}\n\tvoid Parse(XMLTag& tag);\n\tvoid ParseBC(XMLTag& tag);\n\tvoid ParseBCRigid(XMLTag& tag);\t// read rigid contact section\n\tvoid ParseLinearConstraint(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioCodeSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioCodeSection.h\"\n#include \"FECore/FECallBack.h\"\n#include \"FECore/FECoreKernel.h\"\n\nvoid FEBioCodeSection::Parse(XMLTag& tag)\n{\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"callback\")\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tFECallBack* pcb = fecore_new<FECallBack>(szname, GetFEModel());\n\n\t\t\t// TODO: The constructor of FECallBack already registered the callback class, so\n\t\t\t// we don't need to do anything else here. Of course, the question is who\n\t\t\t// is going to cleanup pcb?\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n"
  },
  {
    "path": "FEBioXML/FEBioCodeSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// This is an experimental feature. \n// This section allows users to define callbacks from the input file. \nclass FEBioCodeSection : public FEFileSection\n{\npublic:\n\tFEBioCodeSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioConstraintsSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioConstraintsSection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FEBodyConstraint.h>\n#include <FECore/FENodeSetConstraint.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FEMesh.h>\n#include <FECore/FESurface.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FEInitialCondition.h>\n\nvoid FEBioConstraintsSection1x::Parse(XMLTag &tag)\n{\n\t// make sure there is something to read\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"rigid_body\")\n\t\t{\n\t\t\tParseRigidConstraint(tag);\n\t\t}\n\t\telse if (tag == \"constraint\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype == 0)\n\t\t\t{\n\t\t\t\t// check the name attribute\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\t\tif (szname == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", \"(unknown)\");\n\n\t\t\t\t// make sure this is a leaf\n\t\t\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t// see if we can find this constraint\n\t\t\t\tFEModel& fem = *GetFEModel();\n\t\t\t\tint NLC = fem.NonlinearConstraints();\n\t\t\t\tFENLConstraint* plc = 0;\n\t\t\t\tfor (int i = 0; i<NLC; ++i)\n\t\t\t\t{\n\t\t\t\t\tFENLConstraint* pci = fem.NonlinearConstraint(i);\n\t\t\t\t\tif (pci->GetName() == szname) { plc = pci; }\n\t\t\t\t}\n\t\t\t\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t\t\t\t// add this boundary condition to the current step\n\t\t\t\tGetBuilder()->AddComponent(plc);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFENLConstraint* plc = fecore_new<FENLConstraint>(sztype, GetFEModel());\n\t\t\t\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\t\t\tif (szname) plc->SetName(szname);\n\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (ReadParameter(tag, plc) == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tag == \"surface\")\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFESurfaceConstraint* psc = dynamic_cast<FESurfaceConstraint*>(plc);\n\t\t\t\t\t\t\tif (psc == 0) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\t\t\t\t\tFESurface* psurf = psc->GetSurface();\n\t\t\t\t\t\t\tif (psurf == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\t\t\t\tm.AddSurface(psurf);\n\n\t\t\t\t\t\t\t// see if the set attribute is defined\n\t\t\t\t\t\t\tconst char* szset = tag.AttributeValue(\"set\", true);\n\t\t\t\t\t\t\tif (szset)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// make sure this tag does not have any children\n\t\t\t\t\t\t\t\tif (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t\t\t\t// see if we can find the facet set\n\t\t\t\t\t\t\t\tFEFacetSet* pset = m.FindFacetSet(szset);\n\n\t\t\t\t\t\t\t\t// create a surface from the facet set\n\t\t\t\t\t\t\t\tif (pset)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (GetBuilder()->BuildSurface(*psurf, *pset, true) == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse ParseSurfaceSection(tag, *psurf, 0, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t}\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\n\t\t\t\t// add this boundary condition to the current step\n\t\t\t\tGetBuilder()->AddNonlinearConstraint(plc);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEBioConstraintsSection2::Parse(XMLTag &tag)\n{\n\t// make sure there is something to read\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"rigid_body\") \n\t\t{\n\t\t\tParseRigidConstraint20(tag);\n\t\t}\n\t\telse if (tag == \"constraint\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype == 0)\n\t\t\t{\n\t\t\t\t// check the name attribute\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\t\tif (szname == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", \"(unknown)\");\n\n\t\t\t\t// make sure this is a leaf\n\t\t\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t// see if we can find this constraint\n\t\t\t\tFEModel& fem = *GetFEModel();\n\t\t\t\tint NLC = fem.NonlinearConstraints();\n\t\t\t\tFENLConstraint* plc = 0;\n\t\t\t\tfor (int i=0; i<NLC; ++i)\n\t\t\t\t{\n\t\t\t\t\tFENLConstraint* pci = fem.NonlinearConstraint(i);\n\t\t\t\t\tif (pci->GetName() == szname) { plc = pci; }\n\t\t\t\t}\n\t\t\t\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t\t\t\t// add this boundary condition to the current step\n\t\t\t\tGetBuilder()->AddComponent(plc);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFENLConstraint* plc = fecore_new<FENLConstraint>(sztype, GetFEModel());\n\t\t\t\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\t\t\tif (szname) plc->SetName(szname);\n\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (ReadParameter(tag, plc) == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tag == \"surface\")\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFESurfaceConstraint* psc = dynamic_cast<FESurfaceConstraint*>(plc);\n\t\t\t\t\t\t\tif (psc == 0) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t\t\tFESurface* psurf = psc->GetSurface();\n\t\t\t\t\t\t\tif (psurf == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\t\t\t\tm.AddSurface(psurf);\n\n\t\t\t\t\t\t\t// see if the set attribute is defined\n\t\t\t\t\t\t\tconst char* szset = tag.AttributeValue(\"set\", true);\n\t\t\t\t\t\t\tif (szset)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// make sure this tag does not have any children\n\t\t\t\t\t\t\t\tif (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t\t\t\t// see if we can find the facet set\n\t\t\t\t\t\t\t\tFEFacetSet* pset = m.FindFacetSet(szset);\n\n\t\t\t\t\t\t\t\t// create a surface from the facet set\n\t\t\t\t\t\t\t\tif (pset)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (GetBuilder()->BuildSurface(*psurf, *pset, true) == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse ParseSurfaceSection(tag, *psurf, 0, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t}\n\t\t\t\t\t++tag;\n\t\t\t\t}\n\t\t\t\twhile (!tag.isend());\n\n\t\t\t\t// add this boundary condition to the current step\n\t\t\t\tGetBuilder()->AddNonlinearConstraint(plc);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\nvoid FEBioConstraintsSection25::Parse(XMLTag &tag)\n{\n\t// make sure there is something to read\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"constraint\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype == 0)\n\t\t\t{\n\t\t\t\t// check the name attribute\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\t\tif (szname == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", \"(unknown)\");\n\n\t\t\t\t// make sure this is a leaf\n\t\t\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t// see if we can find this constraint\n\t\t\t\tFEModel& fem = *GetFEModel();\n\t\t\t\tint NLC = fem.NonlinearConstraints();\n\t\t\t\tFENLConstraint* plc = 0;\n\t\t\t\tfor (int i=0; i<NLC; ++i)\n\t\t\t\t{\n\t\t\t\t\tFENLConstraint* pci = fem.NonlinearConstraint(i);\n\t\t\t\t\tif (pci->GetName() == szname) { plc = pci; }\n\t\t\t\t}\n\t\t\t\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t\t\t\t// add this boundary condition to the current step\n\t\t\t\tGetBuilder()->AddComponent(plc);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFENLConstraint* plc = fecore_new<FENLConstraint>(sztype, GetFEModel());\n\t\t\t\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\t\t\tif (szname) plc->SetName(szname);\n\n\t\t\t\t// get the surface\n\t\t\t\t// Note that not all constraints define a surface\n\t\t\t\tFESurfaceConstraint* psc = dynamic_cast<FESurfaceConstraint*>(plc);\n\t\t\t\tif (psc && psc->GetSurface())\n\t\t\t\t{\n\t\t\t\t\tFESurface* psurf = psc->GetSurface();\n\t\t\t\t\tmesh.AddSurface(psurf);\n\t\t\t\t\tconst char* szsurf = tag.AttributeValue(\"surface\");\n\t\t\t\t\tFEFacetSet* pface = mesh.FindFacetSet(szsurf);\n\t\t\t\t\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szsurf);\n\t\t\t\t\tif (GetBuilder()->BuildSurface(*psurf, *pface, psc->UseNodalIntegration()) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szsurf);\n\t\t\t\t}\n\n\t\t\t\t// get the element set\n\t\t\t\tFEBodyConstraint* pbc = dynamic_cast<FEBodyConstraint*>(plc);\n\t\t\t\tif (pbc)\n\t\t\t\t{\n\t\t\t\t\t// see if a specific domain was referenced\n\t\t\t\t\tconst char* szpart = tag.AttributeValue(\"elem_set\", true);\n\t\t\t\t\tif (szpart)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMesh& mesh = fem.GetMesh();\n\t\t\t\t\t\tFEElementSet* elset = mesh.FindElementSet(szpart);\n\t\t\t\t\t\tif (elset == 0) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szpart);\n\t\t\t\t\t\tpbc->SetDomainList(elset);\n\t\t\t\t\t}\n\t\t\t\t}\n\n                // get the nodeset for other constraints\n                // Note that not all constraints define a nodeset\n                FENodeSetConstraint* pns = dynamic_cast<FENodeSetConstraint*>(plc);\n                if (pns && pns->GetNodeSet())\n                {\n                    FENodeSet* pnset = pns->GetNodeSet();\n                    const char* sznset = tag.AttributeValue(\"node_set\");\n                    FENodeSet* pset = mesh.FindNodeSet(sznset);\n                    if (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sznset);\n                    pnset->Add(pset->GetNodeList());\n                }\n\n\t\t\t\t// read the parameter list\n\t\t\t\tReadParameterList(tag, plc);\n\n\t\t\t\t// add this constraint to the current step\n\t\t\t\tGetBuilder()->AddNonlinearConstraint(plc);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioConstraintsSection1x::ParseRigidConstraint(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEModelBuilder& feb = *GetBuilder();\n\n\tconst char* szm = tag.AttributeValue(\"mat\");\n\tassert(szm);\n\n\t// get the material ID\n\tint nmat = atoi(szm);\n\tif ((nmat <= 0) || (nmat > fem.Materials())) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szm);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (strncmp(tag.Name(), \"trans_\", 6) == 0)\n\t\t{\n\t\t\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t\t\tint bc = -1;\n\t\t\tif      (tag.Name()[6] == 'x') bc = 0;\n\t\t\telse if (tag.Name()[6] == 'y') bc = 1;\n\t\t\telse if (tag.Name()[6] == 'z') bc = 2;\n\t\t\tassert(bc >= 0);\n\t\t\t\n\t\t\tif (strcmp(szt, \"prescribed\") == 0)\n\t\t\t{\n\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\");\n\t\t\t\tint lc = atoi(szlc) - 1;\n\n\t\t\t\tbool brel = false;\n\t\t\t\tconst char* szrel = tag.AttributeValue(\"relative\", true);\n\t\t\t\tif (szrel)\n\t\t\t\t{\n\t\t\t\t\tif      (strcmp(szrel, \"true\" ) == 0) brel = true;\n\t\t\t\t\telse if (strcmp(szrel, \"false\") == 0) brel = false;\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"relative\", szrel);\n\t\t\t\t}\n\n\t\t\t\tdouble val = 0.0;\n\t\t\t\tvalue(tag, val);\n\n\t\t\t\tFEStepComponent* pDC = fecore_new_class<FEBoundaryCondition>(\"FERigidPrescribedOld\", &fem);\n\t\t\t\tfeb.AddRigidComponent(pDC);\n\n\t\t\t\tpDC->SetParameter(\"rb\", nmat);\n\t\t\t\tpDC->SetParameter(\"dof\", bc);\n\t\t\t\tpDC->SetParameter(\"relative\", brel);\n\t\t\t\tpDC->SetParameter(\"value\", val);\n\n\t\t\t\t// assign a load curve\n\t\t\t\tif (lc >= 0)\n\t\t\t\t{\n\t\t\t\t\tFEParam* p = pDC->GetParameter(\"value\");\n\t\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\tfem.AttachLoadController(p, lc);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (strcmp(szt, \"force\") == 0)\n\t\t\t{\n\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\");\n\t\t\t\tint lc = atoi(szlc) - 1;\n\n\t\t\t\tdouble val = 0.0;\n\t\t\t\tvalue(tag, val);\n\n\t\t\t\tFEModelLoad* pFC = nullptr;\n\t\t\t\t\n\t\t\t\tif (bc < 3)\n\t\t\t\t{\n\t\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_force\", &fem);\n\n\t\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\t\tpFC->SetParameter(\"dof\", bc);\n\t\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_moment\", &fem);\n\n\t\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\t\tpFC->SetParameter(\"dof\", bc - 3);\n\t\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t\t}\n\t\t\t\tfeb.AddModelLoad(pFC);\n\n\t\t\t\tif (lc >= 0)\n\t\t\t\t{\n\t\t\t\t\tFEParam* p = pFC->GetParameter(\"value\");\n\t\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (strcmp(szt, \"fixed\") == 0)\n\t\t\t{\n\t\t\t\tFEStepComponent* pBC = fecore_new_class<FEBoundaryCondition>(\"FERigidFixedBCOld\", &fem);\n\t\t\t\tfeb.AddRigidComponent(pBC);\n\n\t\t\t\tpBC->SetParameter(\"rb\", nmat);\n\n\t\t\t\tvector<int> dofs; dofs.push_back(bc);\n\t\t\t\tpBC->SetParameter(\"dofs\", dofs);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\t\t}\n\t\telse if (strncmp(tag.Name(), \"rot_\", 4) == 0)\n\t\t{\n\t\t\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t\t\tint bc = -1;\n\t\t\tif      (tag.Name()[4] == 'x') bc = 3;\n\t\t\telse if (tag.Name()[4] == 'y') bc = 4;\n\t\t\telse if (tag.Name()[4] == 'z') bc = 5;\n\t\t\tassert(bc >= 0);\n\n\t\t\tif (strcmp(szt, \"prescribed\") == 0)\n\t\t\t{\n\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\");\n\t\t\t\tint lc = atoi(szlc) - 1;\n\n\t\t\t\tdouble val = 0.0;\n\t\t\t\tvalue(tag, val);\n\n\t\t\t\tFEStepComponent* pDC = fecore_new_class<FEBoundaryCondition>(\"FERigidPrescribedOld\", &fem);\n\t\t\t\tfeb.AddRigidComponent(pDC);\n\n\t\t\t\tpDC->SetParameter(\"rb\", nmat);\n\t\t\t\tpDC->SetParameter(\"dof\", bc);\n\t\t\t\tpDC->SetParameter(\"value\", val);\n\t\t\t}\n\t\t\telse if (strcmp(szt, \"force\") == 0)\n\t\t\t{\n\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\");\n\t\t\t\tint lc = atoi(szlc) - 1;\n\n\t\t\t\tdouble val = 0.0;\n\t\t\t\tvalue(tag, val);\n\n\t\t\t\tFEModelLoad* pFC = nullptr;\n\n\t\t\t\tif (bc < 3)\n\t\t\t\t{\n\t\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_force\", &fem);\n\n\t\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\t\tpFC->SetParameter(\"dof\", bc);\n\t\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_moment\", &fem);\n\n\t\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\t\tpFC->SetParameter(\"dof\", bc - 3);\n\t\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t\t}\n\t\t\t\tfeb.AddModelLoad(pFC);\n\n\t\t\t\tif (lc >= 0)\n\t\t\t\t{\n\t\t\t\t\tFEParam* p = pFC->GetParameter(\"value\");\n\t\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (strcmp(szt, \"fixed\") == 0)\n\t\t\t{\n\t\t\t\tFEStepComponent* pBC = fecore_new_class<FEBoundaryCondition>(\"FERigidFixedBCOld\", &fem);\n\t\t\t\tfeb.AddRigidComponent(pBC);\n\n\t\t\t\tpBC->SetParameter(\"rb\", nmat);\n\n\t\t\t\tvector<int> dofs; dofs.push_back(bc);\n\t\t\t\tpBC->SetParameter(\"dofs\", dofs);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioConstraintsSection2::ParseRigidConstraint20(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEModelBuilder& feb = *GetBuilder();\n\n\tconst char* szm = tag.AttributeValue(\"mat\");\n\tassert(szm);\n\n\t// get the material ID\n\tint nmat = atoi(szm);\n\tif ((nmat <= 0) || (nmat > fem.Materials())) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szm);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"prescribed\")\n\t\t{\n\t\t\t// get the dof\n\t\t\tint bc = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\telse if (strcmp(szbc, \"Rx\") == 0) bc = 3;\n\t\t\telse if (strcmp(szbc, \"Ry\") == 0) bc = 4;\n\t\t\telse if (strcmp(szbc, \"Rz\") == 0) bc = 5;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t// get the loadcurve\n\t\t\tconst char* szlc = tag.AttributeValue(\"lc\");\n\t\t\tint lc = atoi(szlc) - 1;\n\n\t\t\t// get the (optional) type attribute\n\t\t\tbool brel = false;\n\t\t\tconst char* szrel = tag.AttributeValue(\"type\", true);\n\t\t\tif (szrel)\n\t\t\t{\n\t\t\t\tif      (strcmp(szrel, \"relative\" ) == 0) brel = true;\n\t\t\t\telse if (strcmp(szrel, \"absolute\" ) == 0) brel = false;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szrel);\n\t\t\t}\n\n\t\t\tdouble val = 0.0;\n\t\t\tvalue(tag, val);\n\n\t\t\tFEStepComponent* pDC = fecore_new_class<FEBoundaryCondition>(\"FERigidPrescribedOld\", &fem);\n\t\t\tfeb.AddRigidComponent(pDC);\n\n\t\t\tpDC->SetParameter(\"rb\", nmat);\n\t\t\tpDC->SetParameter(\"dof\", bc);\n\t\t\tpDC->SetParameter(\"relative\", brel);\n\t\t\tpDC->SetParameter(\"value\", val);\n\n\t\t\t// assign a load curve\n\t\t\tif (lc >= 0)\n\t\t\t{\n\t\t\t\tFEParam* p = pDC->GetParameter(\"value\");\n\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"force\")\n\t\t{\n\t\t\t// get the dof\n\t\t\tint bc = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\telse if (strcmp(szbc, \"Rx\") == 0) bc = 3;\n\t\t\telse if (strcmp(szbc, \"Ry\") == 0) bc = 4;\n\t\t\telse if (strcmp(szbc, \"Rz\") == 0) bc = 5;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t// get the type\n\t\t\tint ntype = 0; // FERigidBodyForce::FORCE_LOAD;\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype)\n\t\t\t{\n\t\t\t\tif      (strcmp(sztype, \"ramp\"  ) == 0) ntype = 2; //FERigidBodyForce::FORCE_TARGET;\n\t\t\t\telse if (strcmp(sztype, \"follow\") == 0) ntype = 1; //FERigidBodyForce::FORCE_FOLLOW;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t}\n\n\t\t\t// get the loadcurve\n\t\t\tconst char* szlc = tag.AttributeValue(\"lc\", true);\n\t\t\tint lc = -1;\n\t\t\tif (szlc) lc = atoi(szlc) - 1;\n\n\t\t\t// make sure there is a loadcurve for type=0 forces\n\t\t\tif ((ntype == 0)&&(lc==-1)) throw XMLReader::MissingAttribute(tag, \"lc\");\n\n\t\t\tdouble val = 0.0;\n\t\t\tvalue(tag, val);\n\n\t\t\t// create the rigid body force\n\t\t\tFEModelLoad* pFC = nullptr;\n\n\t\t\tif (bc < 3)\n\t\t\t{\n\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_force\", &fem);\n\n\t\t\t\tpFC->SetParameter(\"load_type\", ntype);\n\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\tpFC->SetParameter(\"dof\", bc);\n\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpFC = fecore_new<FEModelLoad>(\"rigid_moment\", &fem);\n\n\t\t\t\tpFC->SetParameter(\"rb\", nmat);\n\t\t\t\tpFC->SetParameter(\"dof\", bc - 3);\n\t\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\t}\n\t\t\tfeb.AddModelLoad(pFC);\n\n\t\t\tif (lc >= 0)\n\t\t\t{\n\t\t\t\tFEParam* p = pFC->GetParameter(\"value\");\n\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"fixed\")\n\t\t{\n\t\t\t// get the dof\n\t\t\tint bc = -1;\n\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n\t\t\tif      (strcmp(szbc, \"x\") == 0) bc = 0;\n\t\t\telse if (strcmp(szbc, \"y\") == 0) bc = 1;\n\t\t\telse if (strcmp(szbc, \"z\") == 0) bc = 2;\n\t\t\telse if (strcmp(szbc, \"Rx\") == 0) bc = 3;\n\t\t\telse if (strcmp(szbc, \"Ry\") == 0) bc = 4;\n\t\t\telse if (strcmp(szbc, \"Rz\") == 0) bc = 5;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\tFEStepComponent* pBC = fecore_new_class<FEBoundaryCondition>(\"FERigidFixedBCOld\", &fem);\n\t\t\tfeb.AddRigidComponent(pBC);\n\n\t\t\tpBC->SetParameter(\"rb\", nmat);\n\n\t\t\tvector<int> dofs; dofs.push_back(bc);\n\t\t\tpBC->SetParameter(\"dofs\", dofs);\n\t\t}\n\t\telse if (tag == \"initial_velocity\")\n\t\t{\n\t\t\t// get the initial velocity\n\t\t\tvec3d v;\n\t\t\tvalue(tag, v);\n\n\t\t\t// create the initial condition\n\t\t\tFEStepComponent* pic = fecore_new_class<FEInitialCondition>(\"FERigidBodyVelocity\", &fem);\n\t\t\tpic->SetParameter(\"rb\", nmat);\n\t\t\tpic->SetParameter(\"value\", v);\n\n\t\t\t// add this initial condition to the current step\n\t\t\tfeb.AddRigidComponent(pic);\n\t\t}\n\t\telse if (tag == \"initial_angular_velocity\")\n\t\t{\n\t\t\t// get the initial angular velocity\n\t\t\tvec3d w;\n\t\t\tvalue(tag, w);\n\n\t\t\t// create the initial condition\n\t\t\tFEStepComponent* pic = fecore_new_class<FEInitialCondition>(\"FERigidBodyAngularVelocity\", &fem);\n\t\t\tpic->SetParameter(\"rb\", nmat);\n\t\t\tpic->SetParameter(\"value\", w);\n\n\t\t\t// add to model\n\t\t\tfeb.AddRigidComponent(pic);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//---------------------------------------------------------------------------------\n// parse a surface section for contact definitions\n//\nbool FEBioConstraintsSection::ParseSurfaceSection(XMLTag &tag, FESurface& s, int nfmt, bool bnodal)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\tint NN = m.Nodes();\n\n\t// count nr of faces\n\tint faces = 0, N, nf[9];\n\tXMLTag t(tag); ++t;\n\twhile (!t.isend()) { faces++; ++t; }\n\n\t// allocate storage for faces\n\ts.Create(faces);\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t// read faces\n\t++tag;\n\tfor (int i=0; i<faces; ++i)\n\t{\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// set the element type/integration rule\n\t\tif (bnodal)\n\t\t{\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4NI);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(FE_TRI3NI );\n\t\t\telse if (tag == \"tri6\" ) el.SetType(FE_TRI6NI);\n            else if (tag == \"quad8\" ) el.SetType(FE_QUAD8NI);\n            else if (tag == \"quad9\" ) el.SetType(FE_QUAD9NI);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4G4);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(feb->m_ntri3);\n\t\t\telse if (tag == \"tri6\" ) el.SetType(feb->m_ntri6);\n\t\t\telse if (tag == \"tri7\" ) el.SetType(feb->m_ntri7);\n\t\t\telse if (tag == \"tri10\") el.SetType(feb->m_ntri10);\n\t\t\telse if (tag == \"quad8\") el.SetType(FE_QUAD8G9);\n\t\t\telse if (tag == \"quad9\") el.SetType(FE_QUAD9G9);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\n\t\tN = el.Nodes();\n\n\t\tif (nfmt == 0)\n\t\t{\n\t\t\ttag.value(nf, N);\n\t\t\tfor (int j=0; j<N; ++j) \n\t\t\t{\n\t\t\t\tint nid = nf[j]-1;\n\t\t\t\tif ((nid<0)||(nid>= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\t\tel.m_node[j] = nid;\n\t\t\t}\n\t\t}\n\t\telse if (nfmt == 1)\n\t\t{\n\t\t\ttag.value(nf, 2);\n\t\t\tFEElement* pe = m.FindElementFromID(nf[0]);\n\t\t\tif (pe)\n\t\t\t{\n\t\t\t\tint ne[9];\n\t\t\t\tint nn = pe->GetFace(nf[1]-1, ne);\n\t\t\t\tif (nn != N) throw XMLReader::InvalidValue(tag);\n\t\t\t\tfor (int j=0; j<N; ++j) el.m_node[j] = ne[j];\n\t\t\t\tel.m_elem[0].pe = pe;\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t}\n\n\t\t++tag;\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioXML/FEBioConstraintsSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\nclass FEFacetSet;\n\n//-----------------------------------------------------------------------------\n// Constraints Section\n// (Base class. Don't use this directly!)\nclass FEBioConstraintsSection : public FEFileSection\n{\npublic:\n\tFEBioConstraintsSection(FEFileImport* pim) : FEFileSection(pim){}\n\nprotected:\n\tbool ParseSurfaceSection(XMLTag& tag, FESurface& s, int nfmt, bool bnodal);\n};\n\n//-----------------------------------------------------------------------------\n// Constraints Section (format 1.x)\nclass FEBioConstraintsSection1x : public FEBioConstraintsSection\n{\npublic:\n\tFEBioConstraintsSection1x(FEFileImport* pim) : FEBioConstraintsSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseRigidConstraint(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Constraints Section (format 2.0)\nclass FEBioConstraintsSection2 : public FEBioConstraintsSection\n{\npublic:\n\tFEBioConstraintsSection2(FEFileImport* pim) : FEBioConstraintsSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\t\nprotected:\n\tvoid ParseRigidConstraint20(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Constraints Section (format 2.5)\nclass FEBioConstraintsSection25 : public FEBioConstraintsSection\n{\npublic:\n\tFEBioConstraintsSection25(FEFileImport* pim) : FEBioConstraintsSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioContactSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioContactSection.h\"\n#include <FECore/FEAugLagLinearConstraint.h>\n#include <FECore/FENodalBC.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModel.h>\n\n//-----------------------------------------------------------------------------\nFEBioContactSection::MissingPrimarySurface::MissingPrimarySurface()\n{\n\tSetErrorString(\"Missing contact primary surface\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioContactSection::MissingSecondarySurface::MissingSecondarySurface()\n{\n\tSetErrorString(\"Missing contact secondary surface\");\n}\n\n//-----------------------------------------------------------------------------\n//! Parse the Contact section (new in version 2.0)\nvoid FEBioContactSection2::Parse(XMLTag& tag)\n{\n\t// make sure there are children\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// loop over tags\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"contact\")\n\t\t{\n\t\t\t// get the contact type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\t// Not all contact interfaces can be automated, so we first handle these special cases\n\t\t\tif      (strcmp(sztype, \"rigid_wall\"       ) == 0) ParseRigidWall       (tag);\n\t\t\telse if (strcmp(sztype, \"rigid\"            ) == 0) ParseRigidInterface  (tag);\n\t\t\telse if (strcmp(sztype, \"linear constraint\") == 0) ParseLinearConstraint(tag);\n\t\t\telse \n\t\t\t{\n\t\t\t\t// If we get here, we try to create a contact interface\n\t\t\t\t// using the FEBio kernel. \n\t\t\t\tFESurfacePairConstraint* pci = fecore_new<FESurfacePairConstraint>(sztype, &fem);\n\t\t\t\tif (pci)\n\t\t\t\t{\n\t\t\t\t\tGetBuilder()->AddContactInterface(pci);\n\t\t\t\t\tParseContactInterface(tag, pci);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFEModel& fem = *GetFEModel();\n\n\t\t\t\t\t// Some constraints were initially defined in the Contact section, although\n\t\t\t\t\t// now it is preferred that they are defined in the Constraints section. For backward\n\t\t\t\t\t// compatibility we still allow constraints to be defined in this section. \n\t\t\t\t\tFENLConstraint* pc = fecore_new<FENLConstraint>(sztype, &fem);\n\t\t\t\t\tif (pc)\n\t\t\t\t\t{\n\t\t\t\t\t\tReadParameterList(tag, pc);\n\t\t\t\t\t\tGetBuilder()->AddNonlinearConstraint(pc);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// check for some older obsolete names\n\t\t\t\t\t\tif      (strcmp(sztype, \"facet-to-facet sliding\") == 0) pci = fecore_new_class<FESurfacePairConstraint>(\"FEFacet2FacetSliding\", &fem);\n\t\t\t\t\t\telse if (strcmp(sztype, \"sliding_with_gaps\"     ) == 0) pci = fecore_new_class<FESurfacePairConstraint>(\"FESlidingInterface\", &fem);\n\n\t\t\t\t\t\tif (pci)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tGetBuilder()->AddContactInterface(pci);\n\t\t\t\t\t\t\tParseContactInterface(tag, pci);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\n//! Parse the Contact section (new in version 2.0)\nvoid FEBioContactSection25::Parse(XMLTag& tag)\n{\n\t// make sure there are children\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// loop over tags\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"contact\")\n\t\t{\n\t\t\t// get the contact type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\t// Not all contact interfaces can be automated, so we first handle these special cases\n\t\t\tif      (strcmp(sztype, \"rigid_wall\"       ) == 0) ParseRigidWall       (tag);\n\t\t\telse if (strcmp(sztype, \"rigid sliding\"    ) == 0) ParseRigidSliding    (tag);\n\t\t\telse if (strcmp(sztype, \"linear constraint\") == 0) ParseLinearConstraint(tag);\n\t\t\telse\n\t\t\t{\n\t\t\t\t// If we get here, we try to create a contact interface\n\t\t\t\t// using the FEBio kernel. \n\t\t\t\tFESurfacePairConstraint* pci = fecore_new<FESurfacePairConstraint>(sztype, &fem);\n\n\t\t\t\tif (pci == nullptr)\n\t\t\t\t{\n\t\t\t\t\t// check for some older obsolete names\n\t\t\t\t\tif (strcmp(sztype, \"facet-to-facet sliding\") == 0) pci = fecore_new_class<FESurfacePairConstraint>(\"FEFacet2FacetSliding\", &fem);\n\t\t\t\t\telse if (strcmp(sztype, \"sliding_with_gaps\") == 0) pci = fecore_new_class<FESurfacePairConstraint>(\"FESlidingInterface\", &fem);\n\t\t\t\t}\n\n\t\t\t\tif (pci)\n\t\t\t\t{\n\t\t\t\t\tGetBuilder()->AddContactInterface(pci);\n\t\t\t\t\tParseContactInterface(tag, pci);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioContactSection2::ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\t// get the parameter list\n\tFEParameterList& pl = pci->GetParameterList();\n\n\t// read the parameters\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false)\n\t\t{\n\t\t\tif (tag == \"surface\")\n\t\t\t{\n\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\t\t\t\tint ntype = 0;\n\t\t\t\tif (strcmp(sztype, \"master\") == 0) ntype = 1;\n\t\t\t\telse if (strcmp(sztype, \"slave\") == 0) ntype = 2;\n\n\t\t\t\tFESurface& s = *(ntype == 1? pci->GetSecondarySurface() : pci->GetPrimarySurface());\n\t\t\t\tm.AddSurface(&s);\n\n\t\t\t\tint nfmt = 0;\n\t\t\t\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\t\t\t\tif (szfmt)\n\t\t\t\t{\n\t\t\t\t\tif (strcmp(szfmt, \"face nodes\") == 0) nfmt = 0;\n\t\t\t\t\telse if (strcmp(szfmt, \"element face\") == 0) nfmt = 1;\n\t\t\t\t}\n\n\t\t\t\t// see if the set attribute is defined\n\t\t\t\tconst char* szset = tag.AttributeValue(\"set\", true);\n\t\t\t\tif (szset)\n\t\t\t\t{\n\t\t\t\t\t// make sure this tag does not have any children\n\t\t\t\t\tif (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t// see if we can find the facet set\n\t\t\t\t\tFEFacetSet* ps = m.FindFacetSet(szset);\n\n\t\t\t\t\t// create a surface from the facet set\n\t\t\t\t\tif (ps)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (GetBuilder()->BuildSurface(s, *ps, pci->UseNodalIntegration()) == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\t// read the surface section\n\t\t\t\t\tif (ParseSurfaceSection(tag, s, nfmt, pci->UseNodalIntegration()) == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// Make sure we have a primary and secondary interface\n\tFESurface* pss = pci->GetPrimarySurface (); if ((pss == 0) || (pss->Elements()==0)) throw MissingPrimarySurface ();\n\tFESurface* pms = pci->GetSecondarySurface(); if ((pms == 0) || (pms->Elements()==0)) throw MissingSecondarySurface();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioContactSection25::ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\t// get the surface pair\n\tconst char* szpair = tag.AttributeValue(\"surface_pair\");\n\tFESurfacePair* surfacePair =m.FindSurfacePair(szpair);\n\tif (surfacePair == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szpair);\n\n\t// build the surfaces\n\tif (GetBuilder()->BuildSurface(*pci->GetSecondarySurface(), *surfacePair->GetSecondarySurface(), pci->UseNodalIntegration()) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szpair);\n\tif (GetBuilder()->BuildSurface(*pci->GetPrimarySurface(), *surfacePair->GetPrimarySurface(), pci->UseNodalIntegration()) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szpair);\n\n\t// get the parameter list\n\tReadParameterList(tag, pci);\n\n\t// Make sure we have both surfaces\n\tFESurface* pss = pci->GetPrimarySurface (); if ((pss == 0) || (pss->Elements()==0)) throw MissingPrimarySurface ();\n\tm.AddSurface(pss);\n\tFESurface* pms = pci->GetSecondarySurface(); if ((pms == 0) || (pms->Elements()==0)) throw MissingSecondarySurface();\n\tm.AddSurface(pms);\n}\n\n//-----------------------------------------------------------------------------\n// --- R I G I D   W A L L   I N T E R F A C E ---\nvoid FEBioContactSection2::ParseRigidWall(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFESurfacePairConstraint* ps = fecore_new<FESurfacePairConstraint>(\"rigid_wall\", GetFEModel());\n\tfem.AddSurfacePairConstraint(ps);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, ps) == false)\n\t\t{\n\t\t\tif (tag == \"surface\")\n\t\t\t{\n\t\t\t\tFESurface& s = *ps->GetPrimarySurface();\n\n\t\t\t\tint nfmt = 0;\n\t\t\t\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\t\t\t\tif (szfmt)\n\t\t\t\t{\n\t\t\t\t\tif      (strcmp(szfmt, \"face nodes\"  ) == 0) nfmt = 0;\n\t\t\t\t\telse if (strcmp(szfmt, \"element face\") == 0) nfmt = 1;\n\t\t\t\t}\n\n\t\t\t\t// read the surface section\n\t\t\t\tParseSurfaceSection(tag, s, nfmt, true);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n\n//-----------------------------------------------------------------------------\n// --- R I G I D   W A L L   I N T E R F A C E ---\nvoid FEBioContactSection25::ParseRigidWall(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFESurfaceConstraint* ps = fecore_new<FESurfaceConstraint>(\"rigid_wall\", GetFEModel());\n\tfem.AddNonlinearConstraint(ps);\n\n\t// get and build the surface\n\tconst char* sz = tag.AttributeValue(\"surface\");\n\tFEFacetSet* pface = mesh.FindFacetSet(sz);\n\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\tif (GetBuilder()->BuildSurface(*ps->GetSurface(), *pface, true) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\n\tmesh.AddSurface(ps->GetSurface());\n\n\tReadParameterList(tag, ps);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioContactSection25::ParseRigidSliding(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFESurfacePairConstraint* ps = fecore_new<FESurfacePairConstraint>(\"rigid sliding\", GetFEModel());\n\tfem.AddSurfacePairConstraint(ps);\n\n\t// get and build the surface\n\tconst char* sz = tag.AttributeValue(\"surface\");\n\tFEFacetSet* pface = mesh.FindFacetSet(sz);\n\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\tif (GetBuilder()->BuildSurface(*ps->GetPrimarySurface(), *pface, false) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\tmesh.AddSurface(ps->GetPrimarySurface());\n\n\tReadParameterList(tag, ps);\n}\n\n//-----------------------------------------------------------------------------\n// --- R I G I D   B O D Y   I N T E R F A C E ---\nvoid FEBioContactSection2::ParseRigidInterface(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEModelBuilder* feb = GetBuilder();\n\n\tint NMAT = fem.Materials();\n\n\t// count how many rigid nodes there are\n\tint nrn= 0;\n\tXMLTag t(tag); ++t;\n\twhile (!t.isend()) { nrn++; ++t; }\n\n\t++tag;\n\tint id, rb, rbp = -1;\n\tFENodalBC* prn = 0;\n\tFENodeSet* ns = nullptr;\n\tfor (int i=0; i<nrn; ++i)\n\t{\n\t\tid = atoi(tag.AttributeValue(\"id\"))-1;\n\t\trb = atoi(tag.AttributeValue(\"rb\"));\n\n\t\t// make sure we have a valid rigid body reference\n\t\tif ((rb <= 0)||(rb>NMAT)) throw XMLReader::InvalidAttributeValue(tag, \"rb\", tag.AttributeValue(\"rb\"));\n\n\t\tif ((prn == 0) || (rb != rbp))\n\t\t{\n\t\t\tprn = fecore_new_class<FENodalBC>(\"FERigidNodeSet\", &fem);\n\t\t\tns = new FENodeSet(&fem);\n\t\t\tprn->SetNodeSet(ns);\n\n\t\t\t// the default shell bc depends on the shell formulation\n\t\t\t// hinged shell = 0\n\t\t\t// clamped shell = 1\n\t\t\tprn->SetParameter(\"clamp_shells\", feb->m_default_shell == OLD_SHELL ? 0 : 1);\n\n\t\t\tprn->SetParameter(\"rb\", rb);\n\n\t\t\tGetBuilder()->AddBC(prn);\n\t\t\trbp = rb;\n\t\t}\n\t\tif (ns) ns->Add(id);\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// --- L I N E A R   C O N S T R A I N T ---\nvoid FEBioContactSection::ParseLinearConstraint(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n    DOFS& dofs = fem.GetDOFS();\n\tFEMesh& m = fem.GetMesh();\n\n\t// make sure there is a constraint defined\n\tif (tag.isleaf()) return;\n\n\t// create a new linear constraint manager\n\tFELinearConstraintSet* pLCS = dynamic_cast<FELinearConstraintSet*>(fecore_new<FENLConstraint>(\"linear constraint\", GetFEModel()));\n\tfem.AddNonlinearConstraint(pLCS);\n\n\t// read the linear constraints\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"linear_constraint\")\n\t\t{\n\t\t\tFEAugLagLinearConstraint* pLC = fecore_alloc(FEAugLagLinearConstraint, &fem);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tint node, bc;\n\t\t\t\tdouble val;\n\t\t\t\tif (tag == \"node\")\n\t\t\t\t{\n\t\t\t\t\ttag.value(val);\n\t\t\t\t\t\n\t\t\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\t\t\tnode = atoi(szid);\n\n\t\t\t\t\tconst char* szbc = tag.AttributeValue(\"bc\");\n                    int ndof = dofs.GetDOF(szbc);\n                    if (ndof >= 0) bc = ndof;\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"bc\", szbc);\n\n\t\t\t\t\tpLC->AddDOF(node, bc, val);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\n\t\t\t// add the linear constraint to the system\n\t\t\tpLCS->add(pLC);\n\t\t}\n\t\telse if (ReadParameter(tag, pLCS) == false)\n\t\t{\n\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//---------------------------------------------------------------------------------\n// parse a surface section for contact definitions\n//\nbool FEBioContactSection::ParseSurfaceSection(XMLTag &tag, FESurface& s, int nfmt, bool bnodal)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\tint NN = m.Nodes();\n\n\tint N, nf[9];\n\n\t// count nr of faces\n\tint faces = tag.children();\n\n\t// allocate storage for faces\n\ts.Create(faces);\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t// read faces\n\t++tag;\n\tfor (int i=0; i<faces; ++i)\n\t{\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// set the element type/integration rule\n\t\tif (bnodal)\n\t\t{\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4NI);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(FE_TRI3NI );\n\t\t\telse if (tag == \"tri6\" ) el.SetType(FE_TRI6NI );\n            else if (tag == \"quad8\" ) el.SetType(FE_QUAD8NI);\n            else if (tag == \"quad9\" ) el.SetType(FE_QUAD9NI);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4G4);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(feb->m_ntri3);\n\t\t\telse if (tag == \"tri6\" ) el.SetType(feb->m_ntri6);\n\t\t\telse if (tag == \"tri7\" ) el.SetType(feb->m_ntri7);\n\t\t\telse if (tag == \"tri10\") el.SetType(feb->m_ntri10);\n\t\t\telse if (tag == \"quad8\") el.SetType(FE_QUAD8G9);\n\t\t\telse if (tag == \"quad9\") el.SetType(FE_QUAD9G9);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\n\t\tN = el.Nodes();\n\n\t\tif (nfmt == 0)\n\t\t{\n\t\t\ttag.value(nf, N);\n\t\t\tfor (int j=0; j<N; ++j) \n\t\t\t{\n\t\t\t\tint nid = nf[j]-1;\n\t\t\t\tif ((nid<0)||(nid>= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\t\tel.m_node[j] = nid;\n\t\t\t}\n\t\t}\n\t\telse if (nfmt == 1)\n\t\t{\n\t\t\ttag.value(nf, 2);\n\t\t\tFEElement* pe = m.FindElementFromID(nf[0]);\n\t\t\tif (pe)\n\t\t\t{\n\t\t\t\tint ne[4];\n\t\t\t\tint nn = pe->GetFace(nf[1]-1, ne);\n\t\t\t\tif (nn != N) throw XMLReader::InvalidValue(tag);\n\t\t\t\tfor (int j=0; j<N; ++j) el.m_node[j] = ne[j];\n\t\t\t\tel.m_elem[0].pe = pe;\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t}\n\n\t\t++tag;\n\t}\n\n\ts.InitSurface();\n\ts.CreateMaterialPointData();\n\n\treturn true;\n}\n//-----------------------------------------------------------------------------\n//! Parse the Contact section (new in version 2.0)\nvoid FEBioContactSection4::Parse(XMLTag& tag)\n{\n\t// make sure there are children\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// loop over tags\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"contact\")\n\t\t{\n\t\t\t// get the contact type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\t// Try to create a contact interface using the FEBio kernel. \n\t\t\tFESurfacePairConstraint* pci = fecore_new<FESurfacePairConstraint>(sztype, &fem);\n\t\t\tif (pci == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\tGetBuilder()->AddContactInterface(pci);\n\t\t\tParseContactInterface(tag, pci);\n\t\t}\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioContactSection4::ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& m = fem.GetMesh();\n\n\t// get the surface pair\n\tconst char* szpair = tag.AttributeValue(\"surface_pair\");\n\tFESurfacePair* surfacePair = m.FindSurfacePair(szpair);\n\tif (surfacePair == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szpair);\n\n\t// get the parameter list\n\tReadParameterList(tag, pci);\n\n\t// build the surfaces\n\tif (GetBuilder()->BuildSurface(*pci->GetSecondarySurface(), *surfacePair->GetSecondarySurface(), pci->UseNodalIntegration()) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szpair);\n\tif (GetBuilder()->BuildSurface(*pci->GetPrimarySurface(), *surfacePair->GetPrimarySurface(), pci->UseNodalIntegration()) == false) throw XMLReader::InvalidAttributeValue(tag, \"surface_pair\", szpair);\n\n\t// Make sure we have both surfaces\n\tFESurface* pss = pci->GetPrimarySurface(); if ((pss == 0) || (pss->Elements() == 0)) throw MissingPrimarySurface();\n\tm.AddSurface(pss);\n\tFESurface* pms = pci->GetSecondarySurface(); if ((pms == 0) || (pms->Elements() == 0)) throw MissingSecondarySurface();\n\tm.AddSurface(pms);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioContactSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FileImport.h\"\n#include <FECore/FESurfacePairConstraint.h>\n\n//-----------------------------------------------------------------------------\n// Contact section (new in version 2.0)\nclass FEBioContactSection : public FEFileSection\n{\nprotected:\n\t//! missing primary surface\n\tclass MissingPrimarySurface : public FEFileException\n\t{\n\tpublic:\n\t\tMissingPrimarySurface();\n\t};\n\n\t//! missing secondary surface\n\tclass MissingSecondarySurface : public FEFileException\n\t{\n\tpublic:\n\t\tMissingSecondarySurface();\n\t};\n\npublic:\n\tFEBioContactSection(FEFileImport* pim) : FEFileSection(pim){}\n\nprotected:\n\tvoid ParseLinearConstraint     (XMLTag& tag);\n\nprotected:\n\tbool ParseSurfaceSection  (XMLTag& tag, FESurface& s, int nfmt, bool bnodal);\n};\n\n//-----------------------------------------------------------------------------\n// Version 2.0\nclass FEBioContactSection2 : public FEBioContactSection\n{\npublic:\n\tFEBioContactSection2(FEFileImport* im) : FEBioContactSection(im){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseRigidInterface(XMLTag& tag);\n\tvoid ParseRigidWall(XMLTag& tag);\n\tvoid ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci);\n};\n\n//-----------------------------------------------------------------------------\n// Version 2.5\nclass FEBioContactSection25 : public FEBioContactSection\n{\npublic:\n\tFEBioContactSection25(FEFileImport* im) : FEBioContactSection(im){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseRigidWall(XMLTag& tag);\n\tvoid ParseRigidSliding(XMLTag& tag);\n\tvoid ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci);\n};\n\n\n//-----------------------------------------------------------------------------\n// Version 4.0\nclass FEBioContactSection4 : public FEBioContactSection\n{\npublic:\n\tFEBioContactSection4(FEFileImport* im) : FEBioContactSection(im) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseContactInterface(XMLTag& tag, FESurfacePairConstraint* pci);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioControlSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioControlSection.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FETimeStepController.h>\n#include <FECore/log.h>\n#include <FECore/FENewtonSolver.h>\n\n#ifndef WIN32\n#define strnicmp strncasecmp\n#endif\n\nclass FEObsoleteParamHandler25 : public FEObsoleteParamHandler\n{\npublic:\n\tFEObsoleteParamHandler25(XMLTag& tag, FEAnalysis* step, FEModelBuilder* feb) : m_step(step), m_feb(feb), FEObsoleteParamHandler(tag, step)\n\t{\n\t\tAddParam(\"max_ups\", \"solver.qn_method.max_ups\", FE_PARAM_INT);\n\t}\n\n\tbool ProcessTag(XMLTag& tag) override\n\t{\n\t\tif (tag == \"qnmethod\")\n\t\t{\n\t\t\tconst char* szv = tag.szvalue();\n\t\t\tint l = (int)strlen(szv);\n\t\t\tif      ((strnicmp(szv, \"BFGS\"   , l) == 0) || (strnicmp(szv, \"0\", l) == 0)) m_qnmethod = QN_BFGS;\n\t\t\telse if ((strnicmp(szv, \"BROYDEN\", l) == 0) || (strnicmp(szv, \"1\", l) == 0)) m_qnmethod = QN_BROYDEN;\n\t\t\telse if ((strnicmp(szv, \"JFNK\"   , l) == 0) || (strnicmp(szv, \"2\", l) == 0)) m_qnmethod = QN_JFNK;\n\t\t\telse return false;\n\n\t\t\treturn true;\n\t\t}\n\t\telse if (tag == \"shell_formulation\")\n\t\t{\n\t\t\tint nshell = 0;\n\t\t\ttag.value(nshell);\n\t\t\tswitch (nshell)\n\t\t\t{\n\t\t\tcase 0: m_feb->m_default_shell = OLD_SHELL; break;\n\t\t\tcase 1: m_feb->m_default_shell = NEW_SHELL; break;\n\t\t\tcase 2: m_feb->m_default_shell = EAS_SHELL; break;\n\t\t\tcase 3: m_feb->m_default_shell = ANS_SHELL; break;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse return FEObsoleteParamHandler::ProcessTag(tag);\n\t}\n\n\tvoid MapParameters() override\n\t{\n\t\tFEModel* fem = m_step->GetFEModel();\n\n\t\t// first, make sure that the QN method is allocated\n\t\tif (m_qnmethod != -1)\n\t\t{\n\t\t\tFENewtonSolver& solver = dynamic_cast<FENewtonSolver&>(*m_step->GetFESolver());\n\t\t\tFEProperty& qn = *solver.FindProperty(\"qn_method\");\n\t\t\tswitch (m_qnmethod)\n\t\t\t{\n\t\t\tcase QN_BFGS   : solver.SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"BFGS\"   , fem)); break;\n\t\t\tcase QN_BROYDEN: solver.SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"Broyden\", fem)); break;\n\t\t\tcase QN_JFNK   : solver.SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"JFNK\"   , fem)); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\n\t\t// now, process the rest\n\t\tFEObsoleteParamHandler::MapParameters();\n\t}\n\nprivate:\n\tint\tm_qnmethod = 0;\n\tFEAnalysis* m_step;\n\tFEModelBuilder* m_feb;\n};\n\n//-----------------------------------------------------------------------------\nvoid FEBioControlSection::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = GetBuilder()->GetStep();\n\tif (pstep == 0)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// Get the solver\n\tFESolver* psolver = pstep->GetFESolver();\n\tif (psolver == 0) \n\t{\n\t\tstring m = GetBuilder()->GetModuleName();\n\t\tthrow FEBioImport::FailedAllocatingSolver(m.c_str());\n\t}\n\n\tFEObsoleteParamHandler25 ctrlParams(tag, pstep, GetBuilder());\n\n\t++tag;\n\tdo\n\t{\n\t\t// first parse common control parameters\n\t\tif (ParseCommonParams(tag) == false)\n\t\t{\n\t\t\t// next, check the solver parameters\n\t\t\tif (ReadParameter(tag, psolver->GetParameterList()) == false)\n\t\t\t{\n\t\t\t\tif (ctrlParams.ProcessTag(tag) == false)\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tctrlParams.MapParameters();\n}\n\n//-----------------------------------------------------------------------------\n// Parse control parameters common to all solvers/modules\nbool FEBioControlSection::ParseCommonParams(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\n\tFEBioImport* imp = GetFEBioImport();\n\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = GetBuilder()->GetStep();\n\n\tFEParameterList& modelParams = fem.GetParameterList();\n\tFEParameterList& stepParams = pstep->GetParameterList();\n\n\t// The \"analysis\" parameter is now an actual parameter of FEAnalysis. \n\t// This means the old format, using the type attribute, is not recognized\n\t// and we have to make sure that this parameter gets processed before the step parameters.\n\tif (tag == \"analysis\")\n\t{\n\t\tXMLAtt& att = tag.Attribute(\"type\");\n\t\tif      (att == \"static\"      ) pstep->m_nanalysis = 0;\n\t\telse if (att == \"dynamic\"     ) pstep->m_nanalysis = 1;\n\t\telse if (att == \"steady-state\") pstep->m_nanalysis = 0;\n\t\telse if (att == \"transient\"   ) pstep->m_nanalysis = 1;\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", att.cvalue());\n\t}\n\telse if (ReadParameter(tag, modelParams) == false)\n\t{\n\t\tif (ReadParameter(tag, stepParams) == false)\n\t\t{\n\t\t\tif (tag == \"time_stepper\")\n\t\t\t{\n\t\t\t\tif (pstep->m_timeController == nullptr) pstep->m_timeController = fecore_alloc(FETimeStepController, &fem);\n\t\t\t\tFETimeStepController& tc = *pstep->m_timeController;\n\t\t\t\tFEParameterList& pl = tc.GetParameterList();\n\t\t\t\tReadParameterList(tag, pl);\n\t\t\t}\n\t\t\telse if (tag == \"use_three_field_hex\") tag.value(feb->m_b3field_hex);\n\t\t\telse if (tag == \"use_three_field_tet\") tag.value(feb->m_b3field_tet);\n\t\t\telse if (tag == \"use_three_field_shell\") tag.value(feb->m_b3field_shell);\n            else if (tag == \"use_three_field_quad\") tag.value(feb->m_b3field_quad);\n            else if (tag == \"use_three_field_tri\") tag.value(feb->m_b3field_tri);\n\t\t\telse if (tag == \"shell_formulation\")\n\t\t\t{\n\t\t\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\t\t\tint nshell = 0;\n\t\t\t\ttag.value(nshell);\n\t\t\t\tswitch (nshell)\n\t\t\t\t{\n\t\t\t\tcase 0: feb->m_default_shell = OLD_SHELL; break;\n\t\t\t\tcase 1: feb->m_default_shell = NEW_SHELL; break;\n\t\t\t\tcase 2: feb->m_default_shell = EAS_SHELL; break;\n\t\t\t\tcase 3: feb->m_default_shell = ANS_SHELL; break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t}\n            else if (tag == \"shell_normal_nodal\") tag.value(feb->m_shell_norm_nodal);\n\t\t\telse if (tag == \"integration\") ParseIntegrationRules(tag);\n\t\t\telse if (tag == \"print_level\")\n\t\t\t{\n\t\t\t\tfeLogWarningEx((&fem), \"The print_level parameter is no longer supported and will be ignored.\");\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioControlSection::ParseIntegrationRules(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\tFEModel& fem = *GetFEModel();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"rule\")\n\t\t{\n\t\t\tXMLAtt& elem = tag.Attribute(\"elem\");\n\t\t\tconst char* szv = tag.szvalue();\n\n\t\t\tif (elem == \"hex8\")\n\t\t\t{\n\t\t\t\tif (tag.isleaf())\n\t\t\t\t{\n\t\t\t\t\tif      (strcmp(szv, \"GAUSS8\") == 0) feb->m_nhex8 = FE_HEX8G8;\n\t\t\t\t\telse if (strcmp(szv, \"POINT6\") == 0) feb->m_nhex8 = FE_HEX8RI;\n\t\t\t\t\telse if (strcmp(szv, \"UDG\"   ) == 0) feb->m_nhex8 = FE_HEX8G1;\n\t\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconst char* szt = tag.AttributeValue(\"type\");\n\t\t\t\t\tif      (strcmp(szt, \"GAUSS8\") == 0) feb->m_nhex8 = FE_HEX8G8;\n\t\t\t\t\telse if (strcmp(szt, \"POINT6\") == 0) feb->m_nhex8 = FE_HEX8RI;\n\t\t\t\t\telse if (strcmp(szt, \"UDG\"   ) == 0) feb->m_nhex8 = FE_HEX8G1;\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\n\t\t\t\t\t++tag;\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tag == \"hourglass\") tag.value(feb->m_udghex_hg);\n\t\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t\t++tag;\n\t\t\t\t\t}\n\t\t\t\t\twhile (!tag.isend());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (elem == \"tet10\")\n\t\t\t{\n\t\t\t\tif      (strcmp(szv, \"GAUSS1\"   ) == 0) feb->m_ntet10 = FE_TET10G1;\n\t\t\t\telse if (strcmp(szv, \"GAUSS4\"   ) == 0) feb->m_ntet10 = FE_TET10G4;\n\t\t\t\telse if (strcmp(szv, \"GAUSS8\"   ) == 0) feb->m_ntet10 = FE_TET10G8;\n\t\t\t\telse if (strcmp(szv, \"LOBATTO11\") == 0) feb->m_ntet10 = FE_TET10GL11;\n\t\t\t\telse if (strcmp(szv, \"GAUSS4RI1\") == 0) feb->m_ntet10 = FE_TET10G4RI1;\n\t\t\t\telse if (strcmp(szv, \"GAUSS8RI4\") == 0) feb->m_ntet10 = FE_TET10G8RI4;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tet15\")\n\t\t\t{\n\t\t\t\tif      (strcmp(szv, \"GAUSS8\"    ) == 0) feb->m_ntet15 = FE_TET15G8;\n\t\t\t\telse if (strcmp(szv, \"GAUSS11\"   ) == 0) feb->m_ntet15 = FE_TET15G11;\n\t\t\t\telse if (strcmp(szv, \"GAUSS15\"   ) == 0) feb->m_ntet15 = FE_TET15G15;\n\t\t\t\telse if (strcmp(szv, \"GAUSS15RI4\") == 0) feb->m_ntet10 = FE_TET15G15RI4;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tet20\")\n\t\t\t{\n\t\t\t\tif (strcmp(szv, \"GAUSS15\") == 0) feb->m_ntet20 = FE_TET20G15;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tri3\")\n\t\t\t{\n\t\t\t\tif      (strcmp(szv, \"GAUSS1\") == 0) feb->m_ntri3 = FE_TRI3G1;\n\t\t\t\telse if (strcmp(szv, \"GAUSS3\") == 0) feb->m_ntri3 = FE_TRI3G3;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tri6\")\n\t\t\t{\n\t\t\t\tif      (strcmp(szv, \"GAUSS3\"    ) == 0) feb->m_ntri6 = FE_TRI6G3;\n\t\t\t\telse if (strcmp(szv, \"GAUSS6\"    ) == 0) feb->m_ntri6 = FE_TRI6NI;\n\t\t\t\telse if (strcmp(szv, \"GAUSS4\"    ) == 0) feb->m_ntri6 = FE_TRI6G4;\n\t\t\t\telse if (strcmp(szv, \"GAUSS7\"    ) == 0) feb->m_ntri6 = FE_TRI6G7;\n\t\t\t\telse if (strcmp(szv, \"LOBATTO7\"  ) == 0) feb->m_ntri6 = FE_TRI6GL7;\n//\t\t\t\telse if (strcmp(szv, \"MOD_GAUSS7\") == 0) feb->m_ntri6 = FE_TRI6MG7;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tri7\")\n\t\t\t{\n\t\t\t\tif      (strcmp(szv, \"GAUSS3\"  ) == 0) feb->m_ntri7 = FE_TRI7G3;\n\t\t\t\telse if (strcmp(szv, \"GAUSS4\"  ) == 0) feb->m_ntri7 = FE_TRI7G4;\n\t\t\t\telse if (strcmp(szv, \"GAUSS7\"  ) == 0) feb->m_ntri7 = FE_TRI7G7;\n\t\t\t\telse if (strcmp(szv, \"LOBATTO7\") == 0) feb->m_ntri7 = FE_TRI7GL7;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tri10\")\n\t\t\t{\n\t\t\t\tif      (strcmp(szv, \"GAUSS7\" ) == 0) feb->m_ntri10 = FE_TRI10G7;\n\t\t\t\telse if (strcmp(szv, \"GAUSS12\") == 0) feb->m_ntri10 = FE_TRI10G12;\n\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse if (elem == \"tet4\")\n\t\t\t{\n\t\t\t\tif (tag.isleaf())\n\t\t\t\t{\n\t\t\t\t\tif      (strcmp(szv, \"GAUSS4\") == 0) feb->m_ntet4 = FE_TET4G4;\n\t\t\t\t\telse if (strcmp(szv, \"GAUSS1\") == 0) feb->m_ntet4 = FE_TET4G1;\n\t\t\t\t\telse if (strcmp(szv, \"UT4\"   ) == 0) feb->m_but4 = true;\n\t\t\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconst char* szt = tag.AttributeValue(\"type\");\n\t\t\t\t\tif      (strcmp(szt, \"GAUSS4\") == 0) feb->m_ntet4 = FE_TET4G4;\n\t\t\t\t\telse if (strcmp(szt, \"GAUSS1\") == 0) feb->m_ntet4 = FE_TET4G1;\n\t\t\t\t\telse if (strcmp(szt, \"UT4\"   ) == 0) feb->m_but4 = true;\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", szv);\n\n\t\t\t\t\t++tag;\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\tif      (tag == \"alpha\"   ) tag.value(feb->m_ut4_alpha);\n\t\t\t\t\t\telse if (tag == \"iso_stab\") tag.value(feb->m_ut4_bdev);\n\t\t\t\t\t\telse if (tag == \"stab_int\")\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst char* sz = tag.szvalue();\n\t\t\t\t\t\t\tif      (strcmp(sz, \"GAUSS4\") == 0) feb->m_ntet4 = FE_TET4G4;\n\t\t\t\t\t\t\telse if (strcmp(sz, \"GAUSS1\") == 0) feb->m_ntet4 = FE_TET4G1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t\t++tag;\n\t\t\t\t\t}\n\t\t\t\t\twhile (!tag.isend());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"elem\", elem.cvalue());\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStepControlSection::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = GetBuilder()->GetStep();\n\n\t// Get the solver\n\tFESolver* psolver = pstep->GetFESolver();\n\tif (psolver == 0)\n\t{\n\t\tstring m = GetBuilder()->GetModuleName();\n\t\tthrow FEBioImport::FailedAllocatingSolver(m.c_str());\n\t}\n\n\tFEObsoleteParamHandler25 ctrlParams(tag, pstep, GetBuilder());\n\n\t++tag;\n\tdo\n\t{\n\t\t// first parse common control parameters\n\t\tif (ParseCommonParams(tag) == false)\n\t\t{\n\t\t\t// next, check the solver parameters\n\t\t\tif (ReadParameter(tag, psolver->GetParameterList()) == false)\n\t\t\t{\n\t\t\t\tif (ctrlParams.ProcessTag(tag) == false)\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\n// Parse control parameters common to all solvers/modules\nbool FEStepControlSection::ParseCommonParams(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = GetBuilder()->GetStep();\n\n\tFEParameterList& modelParams = fem.GetParameterList();\n\tFEParameterList& stepParams = pstep->GetParameterList();\n\n\t// NOTE: The \"analysis\" parameter is now an actual parameter of the FEAnalysis class.\n\t//       This implies that the old format cannot be read anymore, and we have to make \n\t//       to make sure that this parameter is processed before any FEAnalysis parameters.\n\tif (tag == \"analysis\")\n\t{\n\t\tXMLAtt& att = tag.Attribute(\"type\");\n\t\tif      (att == \"static\"      ) pstep->m_nanalysis = 0;\n\t\telse if (att == \"dynamic\"     ) pstep->m_nanalysis = 1;\n\t\telse if (att == \"steady-state\") pstep->m_nanalysis = 0;\n\t\telse if (att == \"transient\"   ) pstep->m_nanalysis = 1;\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", att.cvalue());\n\t}\n\telse if (tag == \"shell_normal_nodal\") tag.value(feb->m_shell_norm_nodal);\n\telse if (ReadParameter(tag, modelParams) == false)\n\t{\n\t\tif (ReadParameter(tag, stepParams) == false)\n\t\t{\n\t\t\tif (tag == \"time_stepper\")\n\t\t\t{\n\t\t\t\tif (pstep->m_timeController == nullptr) pstep->m_timeController = fecore_alloc(FETimeStepController, &fem);\n\t\t\t\tFETimeStepController& tc = *pstep->m_timeController;\n\t\t\t\tFEParameterList& pl = tc.GetParameterList();\n\t\t\t\tReadParameterList(tag, pl);\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FEBioXML/FEBioControlSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Control Section\nclass FEBioControlSection : public FEBioFileSection\n{\npublic:\n\tFEBioControlSection(FEBioImport* pim) : FEBioFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tbool ParseCommonParams(XMLTag& tag);\n\tvoid ParseIntegrationRules(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Control Section for steps\nclass FEStepControlSection : public FEFileSection\n{\npublic:\n\tFEStepControlSection(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tbool ParseCommonParams(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioControlSection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioControlSection3.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FECoreKernel.h\"\n#include \"FECore/FENewtonSolver.h\"\n\n#ifndef WIN32\n#define strnicmp strncasecmp\n#endif\n\nclass FEObsoleteStepParamHandler : public FEObsoleteParamHandler\n{\npublic:\n\tFEObsoleteStepParamHandler(XMLTag& tag, FEAnalysis* step, FEModelBuilder* feb) : m_step(step), m_feb(feb), FEObsoleteParamHandler(tag, step)\n\t{\n\t\tAddParam(\"solver.max_ups\"           , \"solver.qn_method.max_ups\"        , FE_PARAM_INT);\n\t\tAddParam(\"solver.qn_max_buffer_size\", \"solver.qn_method.max_buffer_size\", FE_PARAM_INT);\n\t\tAddParam(\"solver.qn_cycle_buffer\"   , \"solver.qn_method.cycle_buffer\"   , FE_PARAM_BOOL);\n\t\tAddParam(\"solver.cmax\"              , \"solver.qn_method.cmax\"           , FE_PARAM_DOUBLE);\n\t}\n\n\tbool ProcessTag(XMLTag& tag) override\n\t{\n\t\tif (tag == \"qnmethod\")\n\t\t{\n\t\t\tconst char* szv = tag.szvalue();\n\t\t\tint l = strlen(szv);\n\t\t\tif      ((strnicmp(szv, \"BFGS\"   , l) == 0) || (strnicmp(szv, \"0\", l) == 0)) m_qnmethod = QN_BFGS;\n\t\t\telse if ((strnicmp(szv, \"BROYDEN\", l) == 0) || (strnicmp(szv, \"1\", l) == 0)) m_qnmethod = QN_BROYDEN;\n\t\t\telse if ((strnicmp(szv, \"JFNK\"   , l) == 0) || (strnicmp(szv, \"2\", l) == 0)) m_qnmethod = QN_JFNK;\n\t\t\telse return false;\n\n\t\t\treturn true;\n\t\t}\n\t\telse if (tag == \"analysis\")\n\t\t{\n\t\t\tconst char* szval = tag.szvalue();\n\t\t\tFEParam* p = m_step->GetParameter(\"analysis\");\n\t\t\tif      (strcmp(szval, \"STEADY_STATE\") == 0) p->value<int>() = 0;\n\t\t\telse if (strcmp(szval, \"STEADY-STATE\") == 0) p->value<int>() = 0;\n\t\t\telse if (strcmp(szval, \"STATIC\"      ) == 0) p->value<int>() = 0;\n\t\t\telse if (strcmp(szval, \"TRANSIENT\"   ) == 0) p->value<int>() = 1;\n\t\t\telse if (strcmp(szval, \"DYNAMIC\"     ) == 0) p->value<int>() = 1;\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert(false);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse if (tag == \"shell_formulation\")\n\t\t{\n\t\t\tint nshell = 0;\n\t\t\ttag.value(nshell);\n\t\t\tswitch (nshell)\n\t\t\t{\n\t\t\tcase 0: m_feb->m_default_shell = OLD_SHELL; break;\n\t\t\tcase 1: m_feb->m_default_shell = NEW_SHELL; break;\n\t\t\tcase 2: m_feb->m_default_shell = EAS_SHELL; break;\n\t\t\tcase 3: m_feb->m_default_shell = ANS_SHELL; break;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse return FEObsoleteParamHandler::ProcessTag(tag);\n\t}\n\n\tvoid MapParameters() override\n\t{\n\t\tFEModel* fem = m_step->GetFEModel();\n\n\t\t// first, make sure that the QN method is allocated\n\t\tif (m_qnmethod != -1)\n\t\t{\n\t\t\tFENewtonSolver& solver = dynamic_cast<FENewtonSolver&>(*m_step->GetFESolver());\n\t\t\tFEProperty& qn = *solver.FindProperty(\"qn_method\");\n\t\t\tswitch (m_qnmethod)\n\t\t\t{\n\t\t\tcase QN_BFGS   : solver.SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"BFGS\"   , fem)); break;\n\t\t\tcase QN_BROYDEN: solver.SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"Broyden\", fem)); break;\n\t\t\tcase QN_JFNK   : solver.SetSolutionStrategy(fecore_new<FENewtonStrategy>(\"JFNK\"   , fem)); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\n\t\t// now, process the rest\n\t\tFEObsoleteParamHandler::MapParameters();\n\t}\n\nprivate:\n\tint\tm_qnmethod = -1;\n\tFEAnalysis* m_step;\n\tFEModelBuilder* m_feb;\n};\n\n//-----------------------------------------------------------------------------\nFEBioControlSection3::FEBioControlSection3(FEFileImport* pim) : FEFileSection(pim)\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioControlSection3::Parse(XMLTag& tag)\n{\n\t// get the step\n\tFEAnalysis* pstep = GetBuilder()->GetStep();\n\tif (pstep == 0)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// Get the solver\n\tFESolver* psolver = pstep->GetFESolver();\n\tif (psolver == 0) \n\t{\n\t\tstring m = GetBuilder()->GetModuleName();\n\t\tthrow FEBioImport::FailedAllocatingSolver(m.c_str());\n\t}\n\n\t// prepare obsolete parameter mapping.\n\tFEObsoleteStepParamHandler stepParamHandler(tag, pstep, GetBuilder());\n\n\t// read the step parameters\n\tSetInvalidTagHandler(&stepParamHandler);\n\tReadParameterList(tag, pstep);\n\n\tstepParamHandler.MapParameters();\n}\n"
  },
  {
    "path": "FEBioXML/FEBioControlSection3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Control Section\nclass FEBioControlSection3 : public FEFileSection\n{\npublic:\n\tFEBioControlSection3(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioControlSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioControlSection4.h\"\n#include \"FECore/FEAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nFEBioControlSection4::FEBioControlSection4(FEFileImport* pim) : FEFileSection(pim)\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioControlSection4::Parse(XMLTag& tag)\n{\n\t// get the step (don't allocate solver)\n\tFEAnalysis* pstep = GetBuilder()->GetStep(false);\n\tif (pstep == 0)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// read the step parameters\n\tReadParameterList(tag, pstep);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioControlSection4.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Control Section\nclass FEBioControlSection4 : public FEFileSection\n{\npublic:\n\tFEBioControlSection4(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioDiscreteSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioDiscreteSection.h\"\n#include \"FECore/FEDiscreteMaterial.h\"\n#include \"FECore/FEDiscreteDomain.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEModelLoad.h\"\n#include \"FECore/FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\nvoid FEBioDiscreteSection::Parse(XMLTag& tag)\n{\n\t// make sure this tag has children\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"spring\"           ) ParseSpringSection  (tag);\n\t\telse if (tag == \"rigid_axial_force\") ParseRigidAxialForce(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioDiscreteSection::ParseSpringSection(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// determine the spring type\n\tconst char* szt = tag.AttributeValue(\"type\", true);\n\n\t// in 2.5 the names of the spring materials were changed so\n\t// we have to map the type string to the new names.\n\tif ((szt == 0) || (strcmp(szt, \"linear\") == 0)) szt = \"linear spring\";\n\telse if (strcmp(szt, \"tension-only linear\") == 0) szt = \"tension-only linear spring\";\n\telse if (strcmp(szt, \"nonlinear\") == 0) szt = \"nonlinear spring\";\n\n\tFEDiscreteMaterial* pm = dynamic_cast<FEDiscreteMaterial*>(fecore_new<FEMaterial>(szt, &fem));\n\tif (pm == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\n\tfem.AddMaterial(pm);\n\tpm->SetID(fem.Materials());\n\n\t// create a new spring \"domain\"\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tFE_Element_Spec spec;\n\tspec.eclass = FE_ELEM_DISCRETE;\n\tspec.eshape = ET_DISCRETE;\n\tspec.etype  = FE_DISCRETE;\n\tFEDiscreteDomain* pd = dynamic_cast<FEDiscreteDomain*>(febio.CreateDomain(spec, &mesh, pm));\n\tassert(pd);\n\tif (pd == 0) throw FEBioImport::InvalidDomainType();\n\tmesh.AddDomain(pd);\n\n\tint elems = fem.GetMesh().Elements();\n\tint maxid = elems+1;\n\n\t// read spring discrete elements\n\t++tag;\n\tdo\n\t{\n\t\t// read the required node tag\n\t\tif (tag == \"node\")\n\t\t{\n\t\t\tint n[2];\n\t\t\ttag.value(n, 2);\n\t\t\tn[0] -= 1;\n\t\t\tn[1] -= 1;\n\t\t\tpd->AddElement(++maxid, n);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// first read the domain paramters\n\t\t\tFEParameterList& pl = pd->GetParameterList();\n\t\t\tif (ReadParameter(tag, pl) == 0)\n\t\t\t{\n\t\t\t\t// read the actual spring material parameters\n\t\t\t\tFEParameterList& pl = pm->GetParameterList();\n\t\t\t\tif (ReadParameter(tag, pl) == 0)\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tpd->CreateMaterialPointData();\n}\n\n//---------------------------------------------------------------------------------\nvoid FEBioDiscreteSection::ParseRigidAxialForce(XMLTag& tag)\n{\n\t// create a new rigid constraint\n\tFEModelLoad* paf = fecore_new<FEModelLoad>(tag.Name(), GetFEModel());\n\n\t// read the parameters\n\tFEParameterList& pl = paf->GetParameterList();\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false) throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// add it to the model\n\tFEModel& fem = *GetFEModel();\n\tfem.AddModelLoad(paf);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioDiscreteSection25::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tvector<FEDiscreteMaterial*> dmat;\n\n\t// figure out the max element ID\n\t// todo: probably should store that when reading elements\n\tint maxid = 0;\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j)\n\t\t{\n\t\t\tint eid = dom.ElementRef(j).GetID();\n\t\t\tif (eid > maxid) maxid = eid;\n\t\t}\n\t}\n\n\tvector<FEDomain*> discreteDomains;\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"discrete_material\")\n\t\t{\n\t\t\t// determine the discrete material type\n\t\t\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t\t\t// get the optional name\n\t\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t\t\t// create the discrete material\n\t\t\tFEDiscreteMaterial* pm = fecore_new<FEDiscreteMaterial>(szt, &fem);\n\t\t\tif (pm == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\n\t\t\t// set the optional name\n\t\t\tif (szname) pm->SetName(szname);\n\n\t\t\t// add it to the model\n\t\t\tfem.AddMaterial(pm);\n\n\t\t\tdmat.push_back(pm);\n\n\t\t\t// read the parameter list\n\t\t\tReadParameterList(tag, pm);\n\n\t\t\t// The id in the file is one-based for discrete materials, but we want to have \n\t\t\t// a global material ID here.\n\t\t\tpm->SetID(fem.Materials());\n\t\t}\n\t\telse if (tag == \"discrete\")\n\t\t{\n\t\t\t// determine the material\n\t\t\tint mid;\n\t\t\ttag.AttributeValue(\"dmat\", mid);\n\t\t\tif ((mid < 1) || (mid > (int) dmat.size())) throw XMLReader::InvalidAttributeValue(tag, \"dmat\");\n\n\t\t\t// create a new spring \"domain\"\n\t\t\tFE_Element_Spec spec;\n\t\t\tspec.eclass = FE_ELEM_DISCRETE;\n\t\t\tspec.eshape = ET_TRUSS2;\n\t\t\tspec.etype  = FE_DISCRETE;\n\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype)\n\t\t\t{\n\t\t\t\tif (strcmp(sztype, \"wire\") == 0)\n\t\t\t\t{\n\t\t\t\t\tspec.eclass = FE_ELEM_WIRE;\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse if (strcmp(sztype, \"spring\") == 0)\n\t\t\t\t{\n\t\t\t\t\tspec.eclass = FE_ELEM_DISCRETE;\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t}\n\n\t\t\tFEDiscreteDomain* pd = dynamic_cast<FEDiscreteDomain*>(febio.CreateDomain(spec, &mesh, dmat[mid - 1]));\n\t\t\tmesh.AddDomain(pd);\n\n\t\t\t// get the discrete set\n\t\t\tconst char* szset = tag.AttributeValue(\"discrete_set\");\n\t\t\tFEDiscreteSet* pset = mesh.FindDiscreteSet(szset);\n\t\t\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"discrete_set\", szset);\n\n\t\t\t// set the name of the domain\n\t\t\tpd->SetName(szset);\n\n\t\t\t// build the springs\n\t\t\tint N = pset->size();\n\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t{\n\t\t\t\tconst FEDiscreteSet::NodePair& np = pset->Element(i);\n\t\t\t\tint n[2] = {np.n0, np.n1};\n\t\t\t\tpd->AddElement(++maxid, n);\n\t\t\t}\n\n\t\t\t// get the domain parameters\n\t\t\tFEParameterList& pl = pd->GetParameterList();\n\t\t\tReadParameterList(tag, pl);\n\n\t\t\tdiscreteDomains.push_back(pd);\n\t\t}\n\t\telse if (tag == \"rigid_axial_force\") ParseRigidAxialForce(tag);\n\t\telse if (tag == \"rigid_cable\") ParseRigidCable(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// initialize the discrete domains\n\tfor (FEDomain* dom : discreteDomains)\n\t{\n\t\t// create an element set for this domain\n\t\tFEElementSet* elset = new FEElementSet(&fem);\n\t\telset->Create(dom);\n\t\telset->SetName(dom->GetName());\n\t\tmesh.AddElementSet(elset);\n\n\t\t// create material point data for this domain\n\t\tdom->CreateMaterialPointData();\n\t}\n}\n\n//---------------------------------------------------------------------------------\nvoid FEBioDiscreteSection25::ParseRigidAxialForce(XMLTag& tag)\n{\n\t// create a new rigid constraint\n\tFEModelLoad* paf = fecore_new<FEModelLoad>(tag.Name(), GetFEModel());\n\n\t// read the parameters\n\tFEParameterList& pl = paf->GetParameterList();\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false) throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// add it to the model\n\tFEModel& fem = *GetFEModel();\n\tfem.AddModelLoad(paf);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioDiscreteSection25::ParseRigidCable(XMLTag& tag)\n{\n\t// create a new rigid constraint\n\tFEModelLoad* pml = fecore_new<FEModelLoad>(tag.Name(), GetFEModel());\n\tif (pml == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// read all parameters and properties\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pml) == false) throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// add it to the model\n\tFEModel& fem = *GetFEModel();\n\tfem.AddModelLoad(pml);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioDiscreteSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\nclass FEBioDiscreteSection : public FEFileSection\n{\npublic:\n\tFEBioDiscreteSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseSpringSection  (XMLTag& tag);\n\tvoid ParseRigidAxialForce(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioDiscreteSection25 : public FEFileSection\n{\npublic:\n\tFEBioDiscreteSection25(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseRigidAxialForce(XMLTag& tag);\n\tvoid ParseRigidCable(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioGeometrySection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioGeometrySection.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEMaterial.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <FECore/FENodeNodeList.h>\n\n//-----------------------------------------------------------------------------\nbool FEBioGeometrySection::ReadElement(XMLTag &tag, FEElement& el, int nid)\n{\n\tel.SetID(nid);\n\tint n[FEElement::MAX_NODES];\n\tint m = tag.value(n, el.Nodes());\n\tif (m != el.Nodes()) return false;\n\tGetBuilder()->GlobalToLocalID(n, el.Nodes(), el.m_node);\n\treturn true;\n}\n\n//=============================================================================\n// FEBioGeometrySection1x\n//============================================================================= \n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection1x::Parse(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\tfeb->m_maxid = 0;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParseNodeSection(tag);\n\t\telse if (tag == \"Elements\"   ) ParseElementSection(tag);\n\t\telse if (tag == \"ElementData\") ParseElementDataSection(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} \n\twhile (!tag.isend());\n\n\t// At this point the mesh is completely read in.\n\t// Now we can allocate the degrees of freedom.\n\t// NOTE: We do this here since the mesh no longer automatically allocates the dofs.\n\t//       At some point I want to be able to read the mesh before deciding any physics.\n\t//       When that happens I'll have to move this elsewhere.\n\tFEModel& fem = *GetFEModel();\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tfem.GetMesh().SetDOFS(MAX_DOFS);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioGeometrySection1x::ParseNodeSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// get the largest nodal ID\n\t// (It is assumed that nodes are sorted by ID so the last node should have the largest ID)\n\tint max_id = 0;\n\tif (N0 > 0) max_id = mesh.Node(N0 - 1).GetID();\n\n\t// first we need to figure out how many nodes there are\n\tXMLTag t(tag);\n\tint nodes = tag.children();\n\n\t// see if this list defines a set\n\tconst char* szl = tag.AttributeValue(\"set\", true);\n\tFENodeSet* ps = 0;\n\tif (szl)\n\t{\n\t\tps = new FENodeSet(&fem);\n\n\t\tps->SetName(szl);\n\t\tmesh.AddNodeSet(ps);\n\t}\n\n\t// resize node's array\n\tmesh.AddNodes(nodes);\n\n\t// read nodal coordinates\n\t++tag;\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tFENode& node = mesh.Node(N0 + i);\n\t\tvalue(tag, node.m_r0);\n\t\tnode.m_rt = node.m_r0;\n\n\t\t// get the nodal ID\n\t\tint nid = -1;\n\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t// Make sure it is valid\n\t\tif (nid <= max_id) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// set the ID\n\t\tnode.SetID(nid);\n\t\tmax_id = nid;\n\n\t\t// go on to the next node\n\t\t++tag;\n\t}\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps)\n\t{\n\t\tfor (int i = 0; i<nodes; ++i) ps->Add(N0 + i);\n\t}\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioGeometrySection1x::ParseElementSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// first we need to figure out how many elements \n\t// and how many domains there are\n\tvector<FEDOMAIN> dom;\n\tvector<int>\tED; ED.reserve(1000);\n\tXMLTag t(tag); ++t;\n\tint elems = 0, i;\n\twhile (!t.isend())\n\t{\n\t\t// get the material ID\n\t\tconst char* szmat = t.AttributeValue(\"mat\");\n\t\tint nmat = atoi(szmat) - 1;\n\t\tif ((nmat < 0) || (nmat >= fem.Materials())) throw FEBioImport::InvalidMaterial(elems + 1);\n\n\t\t// get the element type\n\t\tFE_Element_Spec spec = GetBuilder()->ElementSpec(t.Name());\n\t\tif (FEElementLibrary::IsValid(spec) == false) throw FEBioImport::InvalidElementType();\n\n\t\t// get the element ID\n\t\tint nid = -1;\n\t\tt.AttributeValue(\"id\", nid);\n\n\t\t// keep track of the largest element ID\n\t\tif (nid > GetBuilder()->m_maxid) GetBuilder()->m_maxid = nid;\n\n\t\t// find a domain for this element\n\t\tint ndom = -1;\n\t\tfor (i = 0; i<(int)dom.size(); ++i)\n\t\t{\n\t\t\tFEDOMAIN& d = dom[i];\n\t\t\tif ((d.mat == nmat) && (d.elem.eshape == spec.eshape))\n\t\t\t{\n\t\t\t\tndom = i;\n\t\t\t\td.nel++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (ndom == -1)\n\t\t{\n\t\t\tFEDOMAIN d;\n\t\t\td.mat = nmat;\n\t\t\td.elem = spec;\n\t\t\td.nel = 1;\n\t\t\tndom = (int)dom.size();\n\t\t\tdom.push_back(d);\n\t\t}\n\n\t\tED.push_back(ndom);\n\t\telems++;\n\t\t++t;\n\t}\n\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// create the domains\n\tfor (i = 0; i<(int)dom.size(); ++i)\n\t{\n\t\tFEDOMAIN& d = dom[i];\n\n\t\t// get material class\n\t\tFEMaterial* pmat = fem.GetMaterial(d.mat);\n\n\t\t// create the new domain\n\t\tFEDomain* pdom = GetBuilder()->CreateDomain(d.elem, pmat);\n\t\tif (pdom == 0) throw FEBioImport::FailedCreatingDomain();\n\n\t\t// add it to the mesh\n\t\tassert(d.nel);\n\t\tpdom->Create(d.nel, d.elem);\n\t\tmesh.AddDomain(pdom);\n\n\t\t// we reset the nr of elements since we'll be using \n\t\t// that variable is a counter in the next loop\n\t\td.nel = 0;\n\t}\n\n\t// read element data\n\t++tag;\n\tint nid = 1;\n\tfor (i = 0; i<elems; ++i, ++nid)\n\t{\n\t\tint nd = ED[i];\n\t\tint ne = dom[nd].nel++;\n\n\t\t// get the domain to which this element belongs\n\t\tFEDomain& domi = mesh.Domain(nd);\n\n\t\t// get the material ID\n\t\tint nmat = atoi(tag.AttributeValue(\"mat\")) - 1;\n\t\tdomi.SetMatID(nmat);\n\n\t\t// get the material class\n\t\tFEMaterial* pmat = fem.GetMaterial(nmat);\n\t\tassert(pmat == domi.GetMaterial());\n\n\t\t// determine element shape\n\t\tFE_Element_Spec espec = GetBuilder()->ElementSpec(tag.Name());\n\t\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t\tif (espec.etype != dom[ED[i]].elem.etype) throw XMLReader::InvalidTag(tag);\n\t\tif (ReadElement(tag, domi.ElementRef(ne), nid) == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// go to next tag\n\t\t++tag;\n\t}\n\n\t// assign material point data\n\tfor (i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& d = mesh.Domain(i);\n\t\td.CreateMaterialPointData();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid set_element_fiber(FEElement& el, const vec3d& v, int ncomp)\n{\n\t// normalize fiber\n\tvec3d a = v;\n\ta.unit();\n\n\t// set up a orthonormal coordinate system\n\tvec3d b(0, 1, 0);\n\tif (fabs(fabs(a*b) - 1) < 1e-7) b = vec3d(0, 0, 1);\n\tvec3d c = a^b;\n\tb = c^a;\n\n\t// make sure they are unit vectors\n\tb.unit();\n\tc.unit();\n\n\tfor (int i = 0; i<el.GaussPoints(); ++i)\n\t{\n\t\tFEMaterialPoint* mp = (ncomp == -1) ? el.GetMaterialPoint(i) : el.GetMaterialPoint(i)->GetPointData(ncomp);\n/*\n\t\tFEElasticMaterialPoint& pt = *mp->ExtractData<FEElasticMaterialPoint>();\n\t\tmat3d& m = pt.m_Q;\n\t\tm.zero();\n\t\tm[0][0] = a.x; m[0][1] = b.x; m[0][2] = c.x;\n\t\tm[1][0] = a.y; m[1][1] = b.y; m[1][2] = c.y;\n\t\tm[2][0] = a.z; m[2][1] = b.z; m[2][2] = c.z;\n*/\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid set_element_mat_axis(FEElement& el, const vec3d& v1, const vec3d& v2, int ncomp)\n{\n\tvec3d a = v1;\n\tvec3d d = v2;\n\n\tvec3d c = a^d;\n\tvec3d b = c^a;\n\n\t// normalize\n\ta.unit();\n\tb.unit();\n\tc.unit();\n\n\t// assign to element\n\tfor (int i = 0; i<el.GaussPoints(); ++i)\n\t{\n        FEMaterialPoint* mp = (ncomp == -1) ? el.GetMaterialPoint(i) : el.GetMaterialPoint(i)->GetPointData(ncomp);\n\n//\t\tFEElasticMaterialPoint& pt = *mp->ExtractData<FEElasticMaterialPoint>();\n//\t\tpt.m_Q = mat3d(a, b, c);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection1x::ParseElementData(FEElement& el, XMLTag& tag)\n{\n\tvec3d a, d;\n\tif (tag == \"fiber\")\n\t{\n\t\t// read the fiber direction\n\t\tvalue(tag, a);\n\t\tset_element_fiber(el, a, 0);\n\t}\n\telse if (tag == \"mat_axis\")\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"a\") value(tag, a);\n\t\t\telse if (tag == \"d\") value(tag, d);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t\tset_element_mat_axis(el, a, d, -1);\n\t}\n\telse if (tag == \"thickness\")\n\t{\n\t\tif (el.Class() != FE_ELEM_SHELL) throw XMLReader::InvalidTag(tag);\n\t\tFEShellElement& shell = static_cast<FEShellElement&> (el);\n\n\t\t// read shell thickness\n\t\ttag.value(&shell.m_h0[0], shell.Nodes());\n\t}\n\telse if (tag == \"area\")\n\t{\n\t\tif (el.Class() != FE_ELEM_TRUSS) throw XMLReader::InvalidTag(tag);\n\t\tFETrussElement& truss = static_cast<FETrussElement&>(el);\n\n\t\t// read truss area\n\t\tvalue(tag, truss.m_a0);\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i<el.GaussPoints(); ++i)\n\t\t{\n\t\t\tFEMaterialPoint* pt = el.GetMaterialPoint(i);\n\t\t\t// TODO: Material point parameters are no longer supported so I need to reimplement this\n/*\t\t\twhile (pt)\n\t\t\t{\n\t\t\t\tFEParameterList& pl = pt->GetParameterList();\n\t\t\t\tif (ReadParameter(tag, pl)) break;\n\n\t\t\t\tbool tagFound = false;\n\t\t\t\tfor (int i = 0; i<pt->Components(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEParameterList& pl = pt->GetPointData(i)->GetParameterList();\n\t\t\t\t\tif (ReadParameter(tag, pl))\n\t\t\t\t\t{\n\t\t\t\t\t\ttagFound = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\tif (tagFound) break;\n\n\t\t\t\tpt = pt->Next();\n\t\t\t\tif (pt == 0) throw XMLReader::InvalidTag(tag);\n\t\t\t}\n*/\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the ElementData section from the FEBio input file\n\nvoid FEBioGeometrySection1x::ParseElementDataSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the total nr of elements\n\tint nelems = mesh.Elements();\n\n\t//make sure we've read the element section\n\tif (nelems == 0) throw XMLReader::InvalidTag(tag);\n\n\t// create the pelem array\n\tvector<FEElement*> pelem;\n\tpelem.assign(nelems, static_cast<FEElement*>(0));\n\n\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& d = mesh.Domain(nd);\n\t\tfor (int i = 0; i<d.Elements(); ++i)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(i);\n\t\t\tassert(pelem[el.GetID() - 1] == 0);\n\t\t\tpelem[el.GetID() - 1] = &el;\n\t\t}\n\t}\n\n\t// read additional element data\n\t++tag;\n\tdo\n\t{\n\t\t// make sure this is an \"element\" tag\n\t\tif (tag == \"element\")\n\t\t{\n\t\t\t// get the element number\n\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\tint n = atoi(szid) - 1;\n\n\t\t\t// make sure the number is valid\n\t\t\tif ((n<0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"id\", szid);\n\n\t\t\t// get a pointer to the element\n\t\t\tFEElement* pe = pelem[n];\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tParseElementData(*pe, tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse if (tag == \"elset\")\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"set\");\n\t\t\t// find domain with this name\n\t\t\tFEElementSet* pset = mesh.FindElementSet(szname);\n\t\t\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szname);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tint n = pset->Elements();\n\t\t\t\tfor (int i = 0; i<n; ++i)\n\t\t\t\t{\n\t\t\t\t\t// get a pointer to the element\n\t\t\t\t\tint nid = (*pset)[i] - 1;\n\t\t\t\t\tFEElement* pe = pelem[nid];\n\t\t\t\t\tParseElementData(*pe, tag);\n\t\t\t\t}\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection1x::ParseNodeSetSection(XMLTag& tag)\n{\n\t// read the node set\n\tFENodeSet* pns = GetFEBioImport()->ParseNodeSet(tag, \"set\");\n\tif (pns == 0) throw XMLReader::InvalidTag(tag);\n}\n\n//=============================================================================\n// FEBioGeometrySection2\n//============================================================================= \n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection2::Parse(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\tfeb->m_maxid = 0;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParseNodeSection       (tag);\n\t\telse if (tag == \"Elements\"   ) ParseElementSection    (tag);\n\t\telse if (tag == \"NodeSet\"    ) ParseNodeSetSection    (tag);\n\t\telse if (tag == \"Surface\"    ) ParseSurfaceSection    (tag);\n\t\telse if (tag == \"Edge\"       ) ParseEdgeSection       (tag);\n\t\telse if (tag == \"ElementSet\" ) ParseElementSetSection (tag);\n\t\telse if (tag == \"ElementData\") ParseElementDataSection(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// At this point the mesh is completely read in.\n\t// Now we can allocate the degrees of freedom.\n\t// NOTE: We do this here since the mesh no longer automatically allocates the dofs.\n\t//       At some point I want to be able to read the mesh before deciding any physics.\n\t//       When that happens I'll have to move this elsewhere.\n\tFEModel& fem = *GetFEModel();\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tfem.GetMesh().SetDOFS(MAX_DOFS);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioGeometrySection2::ParseNodeSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// get the largest nodal ID\n\t// (It is assumed that nodes are sorted by ID so the last node should have the largest ID)\n\tint max_id = 0;\n\tif (N0 > 0) max_id = mesh.Node(N0 - 1).GetID();\n\n\t// first we need to figure out how many nodes there are\n\tXMLTag t(tag);\n\tint nodes = tag.children();\n\n\t// see if this list defines a set\n\tconst char* szl = tag.AttributeValue(\"set\", true);\n\tFENodeSet* ps = 0;\n\tif (szl)\n\t{\n\t\tps = new FENodeSet(&fem);\n\n\t\tps->SetName(szl);\n\t\tmesh.AddNodeSet(ps);\n\t}\n\n\t// resize node's array\n\tmesh.AddNodes(nodes);\n\n\t// read nodal coordinates\n\t++tag;\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tFENode& node = mesh.Node(N0 + i);\n\t\tvalue(tag, node.m_r0);\n\t\tnode.m_rt = node.m_r0;\n\n\t\t// get the nodal ID\n\t\tint nid = -1;\n\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t// Make sure it is valid\n\t\tif (nid <= max_id) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// set the ID\n\t\tnode.SetID(nid);\n\t\tmax_id = nid;\n\n\t\t// go on to the next node\n\t\t++tag;\n\t}\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps)\n\t{\n\t\tfor (int i = 0; i<nodes; ++i) ps->Add(N0 + i);\n\t}\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioGeometrySection2::ParseElementSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the material ID\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\n\t// as of 2.5, we allow materials to be referenced by name, so try this first\n\tFEMaterial* pmat = fem.FindMaterial(szmat);\n\tif (pmat == 0)\n\t{\n\t\t// if we didn't find the material we assume that the material tag uses an ID\n\t\tint nmat = atoi(szmat) - 1;\n\t\tif ((nmat < 0) || (nmat >= fem.Materials())) throw FEBioImport::InvalidDomainMaterial();\n\n\t\t// get the domain's material class\n\t\tpmat = fem.GetMaterial(nmat);\n\t}\n\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"elset\", true);\n\tif (szname == 0) szname = \"_unnamed\";\n\n\t// get the element type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// create the new domain\n\tFEDomain* pdom = GetBuilder()->CreateDomain(espec, pmat);\n\tif (pdom == 0) throw FEBioImport::FailedCreatingDomain();\n\tFEDomain& dom = *pdom;\n\tdom.SetName(szname);\n\n\t// count elements\n\tint elems = tag.children();\n\tassert(elems);\n\n\t// add domain it to the mesh\n\tpdom->Create(elems, espec);\n\tpdom->SetMatID(pmat->GetID() - 1);\n\tmesh.AddDomain(pdom);\n\n\t// for named domains, we'll also create an element set\n\tFEElementSet* pg = 0;\n\tif (szname)\n\t{\n\t\tpg = new FEElementSet(&fem);\n\t\tpg->SetName(szname);\n\t\tmesh.AddElementSet(pg);\n\t}\n\n\t// read element data\n\t++tag;\n\tfor (int i = 0; i<elems; ++i)\n\t{\n\t\tif ((tag == \"elem\") == false) throw XMLReader::InvalidTag(tag);\n\n\t\t// get the element ID\n\t\tint nid;\n\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t// Make sure element IDs increase\n\t\t//\t\tif (nid <= m_pim->m_maxid) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// keep track of the largest element ID\n\t\t// (which by assumption is the ID that was just read in)\n\t\tGetBuilder()->m_maxid = nid;\n\n\t\t// read the element data\n\t\tif (ReadElement(tag, dom.ElementRef(i), nid) == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// go to next tag\n\t\t++tag;\n\t}\n\n\t// create the element set\n\tif (pg) pg->Create(pdom);\n\n\t// assign material point data\n\tdom.CreateMaterialPointData();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection2::ParseElementData(FEElement& el, XMLTag& tag)\n{\n\tvec3d a, d;\n\tif (tag == \"fiber\")\n\t{\n\t\t// read the fiber direction\n\t\tvalue(tag, a);\n\t\tset_element_fiber(el, a, -1);\n\t}\n\telse if (tag == \"mat_axis\")\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"a\") value(tag, a);\n\t\t\telse if (tag == \"d\") value(tag, d);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t\tset_element_mat_axis(el, a, d, -1);\n\t}\n\telse if (tag == \"thickness\")\n\t{\n\t\tif (el.Class() != FE_ELEM_SHELL) throw XMLReader::InvalidTag(tag);\n\t\tFEShellElement& shell = static_cast<FEShellElement&> (el);\n\n\t\t// read shell thickness\n\t\ttag.value(&shell.m_h0[0], shell.Nodes());\n\t}\n\telse if (tag == \"area\")\n\t{\n\t\tif (el.Class() != FE_ELEM_TRUSS) throw XMLReader::InvalidTag(tag);\n\t\tFETrussElement& truss = static_cast<FETrussElement&>(el);\n\n\t\t// read truss area\n\t\tvalue(tag, truss.m_a0);\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i<el.GaussPoints(); ++i)\n\t\t{\n\t\t\tFEMaterialPoint* pt = el.GetMaterialPoint(i);\n\t\t\t// TODO: material point parameters are no longer supported so I need to reimplement this.\n/*\t\t\twhile (pt)\n\t\t\t{\n\t\t\t\tFEParameterList& pl = pt->GetParameterList();\n\t\t\t\tif (ReadParameter(tag, pl)) break;\n\n\t\t\t\tbool tagFound = false;\n\t\t\t\tif (pt->Components() > 1)\n\t\t\t\t{\n\t\t\t\t\tfor (int i = 0; i<pt->Components(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParameterList& pl = pt->GetPointData(i)->GetParameterList();\n\t\t\t\t\t\tif (ReadParameter(tag, pl))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttagFound = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (tagFound) break;\n\n\t\t\t\tpt = pt->Next();\n\t\t\t\tif (pt == 0) throw XMLReader::InvalidTag(tag);\n\t\t\t}\n*/\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the ElementData section from the FEBio input file\n\nvoid FEBioGeometrySection2::ParseElementDataSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the total nr of elements\n\tint nelems = mesh.Elements();\n\n\t//make sure we've read the element section\n\tif (nelems == 0) throw XMLReader::InvalidTag(tag);\n\n\t// create the pelem array\n\tvector<FEElement*> pelem;\n\tpelem.assign(nelems, static_cast<FEElement*>(0));\n\n\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& d = mesh.Domain(nd);\n\t\tfor (int i = 0; i<d.Elements(); ++i)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(i);\n\t\t\tassert(pelem[el.GetID() - 1] == 0);\n\t\t\tpelem[el.GetID() - 1] = &el;\n\t\t}\n\t}\n\n\t// read additional element data\n\t++tag;\n\tdo\n\t{\n\t\t// make sure this is an \"element\" tag\n\t\tif (tag == \"element\")\n\t\t{\n\t\t\t// get the element number\n\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\tint n = atoi(szid) - 1;\n\n\t\t\t// make sure the number is valid\n\t\t\tif ((n<0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"id\", szid);\n\n\t\t\t// get a pointer to the element\n\t\t\tFEElement* pe = pelem[n];\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tParseElementData(*pe, tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse if (tag == \"elset\")\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"set\");\n\t\t\t// find domain with this name\n\t\t\tFEElementSet* pset = mesh.FindElementSet(szname);\n\t\t\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szname);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tint n = pset->Elements();\n\t\t\t\tfor (int i = 0; i<n; ++i)\n\t\t\t\t{\n\t\t\t\t\t// get a pointer to the element\n\t\t\t\t\tint nid = (*pset)[i] - 1;\n\t\t\t\t\tFEElement* pe = pelem[nid];\n\t\t\t\t\tParseElementData(*pe, tag);\n\t\t\t\t}\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection2::ParseNodeSetSection(XMLTag& tag)\n{\n\t// read the node set\n\tFENodeSet* pns = GetFEBioImport()->ParseNodeSet(tag, \"set\");\n\tif (pns == 0) throw XMLReader::InvalidTag(tag);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Edge section.\nvoid FEBioGeometrySection2::ParseEdgeSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the number of nodes\n\t// (we use this for checking the node indices of the facets)\n\tint NN = mesh.Nodes();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// count nr of segments\n\tint nsegs = tag.children();\n\n\t// allocate storage for segments\n\tFESegmentSet* ps = new FESegmentSet(&fem);\n\tps->Create(nsegs);\n\tps->SetName(szname);\n\n\t// add it to the mesh\n\tmesh.AddSegmentSet(ps);\n\n\t// read segments\n\t++tag;\n\tint nf[FEElement::MAX_NODES];\n\tfor (int i = 0; i<nsegs; ++i)\n\t{\n\t\tFESegmentSet::SEGMENT& line = ps->Segment(i);\n\n\t\t// set the facet type\n\t\tif (tag == \"line2\") line.ntype = 2;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the segment type also defines the number of nodes\n\t\tint N = line.ntype;\n\t\ttag.value(nf, N);\n\t\tfor (int j = 0; j<N; ++j)\n\t\t{\n\t\t\tint nid = nf[j] - 1;\n\t\t\tif ((nid<0) || (nid >= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\tline.node[j] = nid;\n\t\t}\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Surface section.\nvoid FEBioGeometrySection2::ParseSurfaceSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the number of nodes\n\t// (we use this for checking the node indices of the facets)\n\tint NN = mesh.Nodes();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// count nr of faces\n\tint faces = tag.children();\n\n\t// allocate storage for faces\n\tFEFacetSet* ps = new FEFacetSet(&fem);\n\tps->Create(faces);\n\tps->SetName(szname);\n\n\t// add it to the mesh\n\tmesh.AddFacetSet(ps);\n\n\t// read faces\n\t++tag;\n\tint nf[FEElement::MAX_NODES];\n\tfor (int i = 0; i<faces; ++i)\n\t{\n\t\tFEFacetSet::FACET& face = ps->Face(i);\n\n\t\t// set the facet type\n\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\telse if (tag == \"tri10\") face.ntype = 10;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the facet type also defines the number of nodes\n\t\tint N = face.ntype;\n\t\ttag.value(nf, N);\n\t\tfor (int j = 0; j<N; ++j)\n\t\t{\n\t\t\tint nid = nf[j] - 1;\n\t\t\tif ((nid<0) || (nid >= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\tface.node[j] = nid;\n\t\t}\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection2::ParseElementSetSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create a new element set\n\tFEElementSet* pg = new FEElementSet(&fem);\n\tpg->SetName(szname);\n\n\tvector<int> l;\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"elem\")\n\t\t{\n\t\t\tint nid = -1;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\t\t\tl.push_back(nid);\n\t\t}\n\t\telse { delete pg; throw XMLReader::InvalidTag(tag); }\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// only add non-empty element sets\n\tif (l.empty() == false)\n\t{\n\t\t// assign indices to element set\n\t\tpg->Create(l);\n\n\t\t// add the element set to the mesh\n\t\tmesh.AddElementSet(pg);\n\t}\n\telse delete pg;\n}\n\n//=============================================================================\n// FEBioGeometrySection25\n//============================================================================= \n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::Parse(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\tfeb->m_maxid = 0;\n\n\t// read all sections\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParseNodeSection       (tag);\n\t\telse if (tag == \"Elements\"   ) ParseElementSection    (tag);\n\t\telse if (tag == \"NodeSet\"    ) ParseNodeSetSection    (tag);\n\t\telse if (tag == \"Surface\"    ) ParseSurfaceSection    (tag);\n\t\telse if (tag == \"Edge\"       ) ParseEdgeSection       (tag);\n\t\telse if (tag == \"ElementSet\" ) ParseElementSetSection (tag);\n\t\telse if (tag == \"DiscreteSet\") ParseDiscreteSetSection(tag);\n\t\telse if (tag == \"SurfacePair\") ParseSurfacePairSection(tag);\n\t\telse if (tag == \"NodeSetPair\") ParseNodeSetPairSection(tag);\n\t\telse if (tag == \"NodeSetSet\" ) ParseNodeSetSetSection (tag);\n\t\telse if (tag == \"Part\"       ) ParsePartSection       (tag);\n\t\telse if (tag == \"Instance\"   ) ParseInstanceSection   (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// At this point the mesh is completely read in.\n\t// Now we can allocate the degrees of freedom.\n\t// NOTE: We do this here since the mesh no longer automatically allocates the dofs.\n\t//       At some point I want to be able to read the mesh before deciding any physics.\n\t//       When that happens I'll have to move this elsewhere.\n\tFEModel& fem = *GetFEModel();\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tfem.GetMesh().SetDOFS(MAX_DOFS);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParsePartSection(XMLTag& tag)\n{\n\t// get the part name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// Create a new part\n\tFEBModel::Part* part = m_feb.AddPart(szname);\n\n\t// get the type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\tif (sztype == 0) sztype = \"template\";\n\n\t// check the value\n\tbool binstance = false;\n\tif      (strcmp(sztype, \"instance\") == 0) binstance = true;\n\telse if (strcmp(sztype, \"template\") == 0) binstance = false;\n\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// see if the from attribute is defined\n\tconst char* szfrom = tag.AttributeValue(\"from\", true);\n\tif (szfrom)\n\t{\n\t\t// make sure this is an empty leaf\n\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// redirect input to another file\n\t\tchar xpath[256] = {0};\n\t\tsnprintf(xpath, sizeof(xpath), \"febio_spec/Geometry/Part[@name=%s]\", szname);\n\t\tXMLReader xml;\n\t\tif (xml.Open(szfrom) == false) throw XMLReader::InvalidAttributeValue(tag, \"from\", szfrom);\n\t\tXMLTag tag2;\n\t\tif (xml.FindTag(xpath, tag2) == false) throw XMLReader::InvalidAttributeValue(tag, \"from\", szfrom);\n\n\t\t// read the part\n\t\tParsePart(tag2, part);\n\t}\n\telse ParsePart(tag, part);\n\n\t// instantiate the part\n\tif (binstance) \n\t{\n\t\tif (m_feb.BuildPart(*GetFEModel(), *part) == false) throw FEBioImport::FailedBuildingPart(part->Name());\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParsePart(XMLTag& tag, FEBModel::Part* part)\n{\n\t// read the part's sections\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParsePartNodeSection   (tag, part);\n\t\telse if (tag == \"Elements\"   ) ParsePartElementSection(tag, part);\n\t\telse if (tag == \"NodeSet\"    ) ParsePartNodeSetSection(tag, part);\n\t\telse if (tag == \"Surface\"    ) ParsePartSurfaceSection(tag, part);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseInstanceSection(XMLTag& tag)\n{\n\t// get the name and part tags\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tconst char* szpart = tag.AttributeValue(\"part\");\n\tif (szname == 0) szname = szpart;\n\n\t// find the part\n\tFEBModel::Part* oldPart = m_feb.FindPart(szpart);\n\tif (oldPart == 0) throw XMLReader::InvalidAttributeValue(tag, \"part\", szpart);\n\n\t// copy the part\n\tFEBModel::Part* newPart = new FEBModel::Part(*oldPart);\n\tm_feb.AddPart(newPart);\n\n\t// rename the part\n\tnewPart->SetName(szname);\n\n\t// parse any child tags\n\tTransform transform;\n\tif (tag.isleaf() == false)\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"translate\")\n\t\t\t{\n\t\t\t\tdouble r[3];\n\t\t\t\ttag.value(r, 3);\n\t\t\t\ttransform.SetPosition(vec3d(r[0], r[1], r[2]));\n\t\t\t}\n\t\t\telse if (tag == \"rotate\")\n\t\t\t{\n\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\t\tif (sztype == 0) sztype = \"quaternion\";\n\n\t\t\t\tif (strcmp(sztype, \"quaternion\") == 0)\n\t\t\t\t{\n\t\t\t\t\tdouble v[4];\n\t\t\t\t\ttag.value(v, 4);\n\t\t\t\t\tquatd q(v[0], v[1], v[2], v[3]);\n\t\t\t\t\ttransform.SetRotation(q);\n\t\t\t\t}\n\t\t\t\telse if (strcmp(sztype, \"vector\") == 0)\n\t\t\t\t{\n\t\t\t\t\tdouble v[3];\n\t\t\t\t\ttag.value(v, 3);\n\t\t\t\t\ttransform.SetRotation(vec3d(v[0], v[1], v[2]));\n\t\t\t\t}\n\t\t\t\telse if (strcmp(sztype, \"Euler\") == 0)\n\t\t\t\t{\n\t\t\t\t\tdouble v[3];\n\t\t\t\t\ttag.value(v, 3);\n\t\t\t\t\ttransform.SetRotation(v[0], v[1], v[2]);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t}\n\t\t\telse if (tag == \"scale\")\n\t\t\t{\n\t\t\t\tdouble s[3];\n\t\t\t\ttag.value(s, 3);\n\t\t\t\ttransform.SetScale(s[0], s[1], s[2]);\n\t\t\t}\n\t\t\telse if (tag == \"Elements\")\n\t\t\t{\n\t\t\t\tconst char* szdom = tag.AttributeValue(\"name\");\n\t\t\t\tconst char* szmat = tag.AttributeValue(\"mat\");\n\t\t\t\t\n\t\t\t\tFEBModel::Domain* dom = newPart->FindDomain(szdom);\n\t\t\t\tif (dom == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", szdom);\n\n\t\t\t\t// make sure the material is defined\n\t\t\t\tFEModel& fem = *GetFEModel();\n\t\t\t\tFEMaterial* mat = fem.FindMaterial(szmat);\n\t\t\t\tif (mat == 0) throw FEBioImport::InvalidDomainMaterial();\n\n\t\t\t\tdom->SetMaterialName(szmat);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\n\t// build this part\n\tif (m_feb.BuildPart(*GetFEModel(), *newPart, true, transform) == false) throw FEBioImport::FailedBuildingPart(newPart->Name());\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioGeometrySection25::ParseNodeSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// get the largest nodal ID\n\t// (It is assumed that nodes are sorted by ID so the last node should have the largest ID)\n\tint max_id = 0;\n\tif (N0 > 0) max_id = mesh.Node(N0 - 1).GetID();\n\n\t// first we need to figure out how many nodes there are\n\tXMLTag t(tag);\n\tint nodes = tag.children();\n\n\t// see if this list defines a set\n\tconst char* szl = tag.AttributeValue(\"name\", true);\n\tFENodeSet* ps = 0;\n\tif (szl)\n\t{\n\t\tps = new FENodeSet(&fem);\n\n\t\tps->SetName(szl);\n\t\tmesh.AddNodeSet(ps);\n\t}\n\n\t// resize node's array\n\tmesh.AddNodes(nodes);\n\n\t// read nodal coordinates\n\t++tag;\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tFENode& node = mesh.Node(N0 + i);\n\t\tvalue(tag, node.m_r0);\n\t\tnode.m_rt = node.m_r0;\n\n\t\t// get the nodal ID\n\t\tint nid = -1;\n\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t// Make sure it is valid\n\t\tif (nid <= max_id) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// set the ID\n\t\tnode.SetID(nid);\n\t\tmax_id = nid;\n\n\t\t// go on to the next node\n\t\t++tag;\n\t}\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps)\n\t{\n\t\tfor (int i = 0; i<nodes; ++i) ps->Add(N0 + i);\n\t}\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioGeometrySection25::ParsePartNodeSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// first we need to figure out how many nodes there are\n\tXMLTag t(tag);\n\tint nodes = tag.children();\n\n\t// see if this list defines a set\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tFEBModel::NodeSet* ps = 0;\n\tif (szname)\n\t{\n\t\tps = new FEBModel::NodeSet(szname);\n\t\tpart->AddNodeSet(ps);\n\t}\n\n\t// allocate node\n\tvector<FEBModel::NODE> node(nodes);\n\tvector<int> nodeList(nodes);\n\n\t// read nodal coordinates\n\t++tag;\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tFEBModel::NODE& nd = node[i];\n\t\tvalue(tag, nd.r);\n\n\t\t// get the nodal ID\n\t\ttag.AttributeValue(\"id\", nd.id);\n\t\tnodeList[i] = nd.id;\n\n\t\t// go on to the next node\n\t\t++tag;\n\t}\n\n\t// add nodes to the part\n\tpart->AddNodes(node);\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps) ps->SetNodeList(nodeList);\n}\n\n\n\n\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioGeometrySection25::ParseElementSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the material ID\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\n\t// as of 2.5, we allow materials to be referenced by name, so try this first\n\tFEMaterial* pmat = fem.FindMaterial(szmat);\n\tif (pmat == 0)\n\t{\n\t\t// if we didn't find the material we assume that the material tag uses an ID\n\t\tint nmat = atoi(szmat) - 1;\n\t\tif ((nmat < 0) || (nmat >= fem.Materials())) throw FEBioImport::InvalidDomainMaterial();\n\n\t\t// get the domain's material class\n\t\tpmat = fem.GetMaterial(nmat);\n\t}\n\n\t// get the name\n\tstring name;\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) name = szname;\n\n\t// get the element type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// create the new domain\n\tFEDomain* pdom = GetBuilder()->CreateDomain(espec, pmat);\n\tif (pdom == 0) throw FEBioImport::FailedCreatingDomain();\n\tFEDomain& dom = *pdom;\n\tdom.SetName(name);\n\n\t// active flag\n\tconst char* szactive = tag.AttributeValue(\"active\", true);\n\tif (szactive)\n\t{\n\t\tif (strcmp(szactive, \"false\") == 0) pdom->SetActive(false);\n\t}\n\n\t// count elements\n\tvector<FEModelBuilder::ELEMENT> elemList; elemList.reserve(512000);\n\t++tag;\n\tdo\n\t{\n\t\tif ((tag == \"elem\") == false) throw XMLReader::InvalidTag(tag);\n\n\t\t// get the element ID\n\t\tFEModelBuilder::ELEMENT el;\n\t\ttag.AttributeValue(\"id\", el.nid);\n\n\t\tel.nodes = tag.value(el.node, FEElement::MAX_NODES);\n\t\telemList.push_back(el);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tint elems = (int) elemList.size();\n\tassert(elems);\n\n\t// add domain it to the mesh\n\tpdom->Create(elems, espec);\n\tpdom->SetMatID(pmat->GetID() - 1);\n\tmesh.AddDomain(pdom);\n\n\t// read element data\n\tfor (int i = 0; i<elems; ++i)\n\t{\n\t\tFEModelBuilder::ELEMENT& elem = elemList[i];\n\t\t\n\t\t// get the element ID\n\t\tint nid = elem.nid;\n\n\t\t// Make sure element IDs increase\n\t\t//\t\tif (nid <= m_pim->m_maxid) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// keep track of the largest element ID\n\t\t// (which by assumption is the ID that was just read in)\n\t\tGetBuilder()->m_maxid = nid;\n\n\t\t// process the element data\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tel.SetID(nid);\n\t\tif (elem.nodes != el.Nodes()) throw XMLReader::InvalidTag(tag);\n\t\tGetBuilder()->GlobalToLocalID(elem.node, el.Nodes(), el.m_node);\n\t}\n\n\t// for named domains, we'll also create an element set\n\tif (!name.empty())\n\t{\n\t\tFEElementSet* pg = new FEElementSet(&fem);\n\t\tpg->SetName(name);\n\t\tmesh.AddElementSet(pg);\n\t\tpg->Create(pdom);\n\t}\n\n\t// assign material point data\n\tdom.CreateMaterialPointData();\n}\n\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioGeometrySection25::ParsePartElementSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the material ID\n\tconst char* szmat = tag.AttributeValue(\"mat\", true);\n\n\t// get the (optional) name\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t// get the element type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// create the new domain\n\tFEBModel::Domain* dom = new FEBModel::Domain(espec);\n\tif (szname) dom->SetName(szname);\n\tif (szmat) dom->SetMaterialName(szmat);\n\n\t// count elements\n\tint elems = tag.children();\n\tassert(elems);\n\n\t// add domain it to the mesh\n\tdom->Create(elems);\n\tpart->AddDomain(dom);\n\n\t// for named domains, we'll also create an element set\n\tFEBModel::ElementSet* pg = 0;\n\tif (szname)\n\t{\n\t\tFEBModel::ElementSet* pg = new FEBModel::ElementSet(szname);\n\t\tpart->AddElementSet(pg);\n\t}\n\n\tvector<int> elemList(elems);\n\n\t// read element data\n\t++tag;\n\tfor (int i = 0; i<elems; ++i)\n\t{\n\t\tif ((tag == \"elem\") == false) throw XMLReader::InvalidTag(tag);\n\n\t\tFEBModel::ELEMENT& el = dom->GetElement(i);\n\n\t\t// get the element ID\n\t\ttag.AttributeValue(\"id\", el.id);\n\t\telemList[i] = el.id;\n\n\t\t// read the element data\n\t\ttag.value(el.node, FEElement::MAX_NODES);\n\n\t\t// go to next tag\n\t\t++tag;\n\t}\n\n\t// set the element list\n\tif (pg) pg->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseNodeSetSection(XMLTag& tag)\n{\n\t// read the node set\n\tFENodeSet* pns = GetFEBioImport()->ParseNodeSet(tag, \"node_set\");\n\tif (pns == 0) throw XMLReader::InvalidTag(tag);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Geometry::Groups section of the FEBio input file\nvoid FEBioGeometrySection25::ParsePartNodeSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create the node set\n\tFEBModel::NodeSet* set = new FEBModel::NodeSet(szname);\n\tpart->AddNodeSet(set);\n\n\tint nodes = tag.children();\n\tvector<int> nodeList(nodes);\n\n\t++tag;\n\tfor (int i=0; i<nodes; ++i)\n\t{\n\t\tif (tag == \"node\")\n\t\t{\n\t\t\t// get the ID\n\t\t\tint nid;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\t\t\tnodeList[i] = nid;\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\n\t\t++tag;\n\t}\n\n\t// add nodes to the list\n\tset->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseDiscreteSetSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create the discrete element set\n\tFEDiscreteSet* ps = new FEDiscreteSet(&mesh);\n\tps->SetName(szname);\n\tmesh.AddDiscreteSet(ps);\n\n\t// see if the generate attribute was defined\n\tconst char* szgen = tag.AttributeValue(\"generate\", true);\n\tif (szgen == 0)\n\t{\n\t\t// read the node pairs\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"delem\")\n\t\t\t{\n\t\t\t\tint n[2];\n\t\t\t\ttag.value(n, 2);\n\t\t\t\tn[0] -= 1; n[1] -= 1;\n\t\t\t\tps->add(n[0], n[1]);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\telse\n\t{\n\t\tassert(tag.isempty());\n\n\t\t// generate all the springs from the mesh\n\t\tFENodeNodeList NNL;\n\t\tNNL.Create(mesh);\n\n\t\t// generate the springs\n\t\tfor (int i = 0; i<NNL.Size(); ++i)\n\t\t{\n\t\t\tint nval = NNL.Valence(i);\n\t\t\tfor (int j = 0; j<nval; ++j)\n\t\t\t{\n\t\t\t\tint n0 = i;\n\t\t\t\tint nj = NNL.NodeList(i)[j];\n\t\t\t\tif (n0 < nj)\n\t\t\t\t{\n\t\t\t\t\tps->add(n0, nj);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Edge section.\nvoid FEBioGeometrySection25::ParseEdgeSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the number of nodes\n\t// (we use this for checking the node indices of the facets)\n\tint NN = mesh.Nodes();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// count nr of segments\n\tint nsegs = tag.children();\n\n\t// allocate storage for segments\n\tFESegmentSet* ps = new FESegmentSet(&fem);\n\tps->Create(nsegs);\n\tps->SetName(szname);\n\n\t// add it to the mesh\n\tmesh.AddSegmentSet(ps);\n\n\t// read segments\n\t++tag;\n\tint nf[FEElement::MAX_NODES];\n\tfor (int i = 0; i<nsegs; ++i)\n\t{\n\t\tFESegmentSet::SEGMENT& line = ps->Segment(i);\n\n\t\t// set the facet type\n\t\tif (tag == \"line2\") line.ntype = 2;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the segment type also defines the number of nodes\n\t\tint N = line.ntype;\n\t\ttag.value(nf, N);\n\t\tfor (int j = 0; j<N; ++j)\n\t\t{\n\t\t\tint nid = nf[j] - 1;\n\t\t\tif ((nid<0) || (nid >= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\tline.node[j] = nid;\n\t\t}\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseSurfacePairSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create new surface pair\n\tFESurfacePair* p = new FESurfacePair(&mesh);\n\n\t// set its name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tp->SetName(szname);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"master\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"surface\");\n\t\t\tp->SetSecondarySurface(mesh.FindFacetSet(sz));\n\t\t\tif (p->GetSecondarySurface() == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\t\t}\n\t\telse if (tag == \"slave\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"surface\");\n\t\t\tp->SetPrimarySurface(mesh.FindFacetSet(sz));\n\t\t\tif (p->GetPrimarySurface() == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// add it to the mesh\n\tmesh.AddSurfacePair(p);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseNodeSetPairSection(XMLTag& tag)\n{\n\tFEModelBuilder::NodeSetPair p;\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tstrcpy(p.szname, szname);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"master\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"node_set\");\n\t\t\tp.set2 = mesh.FindNodeSet(sz);\n\t\t\tif (p.set2 == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sz);\n\t\t}\n\t\telse if (tag == \"slave\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"node_set\");\n\t\t\tp.set1 = mesh.FindNodeSet(sz);\n\t\t\tif (p.set1 == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sz);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n\n\tGetBuilder()->AddNodeSetPair(p);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseNodeSetSetSection(XMLTag& tag)\n{\n\tFEModelBuilder::NodeSetSet p;\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tstrcpy(p.szname, szname);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"node_set\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"node_set\");\n\t\t\tFENodeSet* ns = mesh.FindNodeSet(sz);\n\t\t\tif (ns == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sz);\n\t\t\tp.add(ns);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tGetBuilder()->AddNodeSetSet(p);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Surface section.\nvoid FEBioGeometrySection25::ParseSurfaceSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the number of nodes\n\t// (we use this for checking the node indices of the facets)\n\tint NN = mesh.Nodes();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// if parts are defined we use the new format\n\tif (m_feb.Parts() > 0)\n\t{\n\t\tFEFacetSet* ps = new FEFacetSet(&fem);\n\t\tps->SetName(szname);\n\n\t\t// add it to the mesh\n\t\tmesh.AddFacetSet(ps);\n\n\t\t// read the child surfaces\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"Surface\")\n\t\t\t{\n\t\t\t\tconst char* szatt = tag.AttributeValue(\"surface\");\n\t\t\t\tFEFacetSet* pf = mesh.FindFacetSet(szatt);\n\t\t\t\tif (pf == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szatt);\n\n\t\t\t\tps->Add(pf);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\n\t}\n\telse\n\t{\n\t\t// count nr of faces\n\t\tint faces = tag.children();\n\n\t\t// allocate storage for faces\n\t\tFEFacetSet* ps = new FEFacetSet(&fem);\n\t\tps->Create(faces);\n\t\tps->SetName(szname);\n\n\t\t// add it to the mesh\n\t\tmesh.AddFacetSet(ps);\n\n\t\t// read faces\n\t\t++tag;\n\t\tint nf[FEElement::MAX_NODES];\n\t\tfor (int i = 0; i<faces; ++i)\n\t\t{\n\t\t\tFEFacetSet::FACET& face = ps->Face(i);\n\n\t\t\t// set the facet type\n\t\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\t\telse if (tag == \"tri10\") face.ntype = 10;\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t// we assume that the facet type also defines the number of nodes\n\t\t\tint N = face.ntype;\n\t\t\tint nread = tag.value(nf, N);\n\t\t\tif (nread != face.ntype) throw XMLReader::InvalidValue(tag);\n\t\t\tfor (int j = 0; j<N; ++j)\n\t\t\t{\n\t\t\t\tint nid = nf[j] - 1;\n\t\t\t\tif ((nid<0) || (nid >= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\t\tface.node[j] = nid;\n\t\t\t}\n\n\t\t\t++tag;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Surface section.\nvoid FEBioGeometrySection25::ParsePartSurfaceSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// count nr of faces\n\tint faces = tag.children();\n\n\t// allocate storage for faces\n\tFEBModel::Surface* ps = new FEBModel::Surface(szname);\n\tpart->AddSurface(ps);\n\tps->Create(faces);\n\n\t// read faces\n\t++tag;\n\tfor (int i = 0; i<faces; ++i)\n\t{\n\t\tFEBModel::FACET& face = ps->GetFacet(i);\n\n\t\t// get the ID (although we don't really use this)\n\t\ttag.AttributeValue(\"id\", face.id);\n\n\t\t// set the facet type\n\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the facet type also defines the number of nodes\n\t\tint N = face.ntype;\n\t\ttag.value(face.node, N);\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection25::ParseElementSetSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create a new element set\n\tFEElementSet* pg = new FEElementSet(&fem);\n\tpg->SetName(szname);\n\n\tvector<int> l;\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"elem\")\n\t\t{\n\t\t\tint nid = -1;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\t\t\tl.push_back(nid);\n\t\t}\n\t\telse { delete pg; throw XMLReader::InvalidTag(tag); }\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// only add non-empty element sets\n\tif (l.empty() == false)\n\t{\n\t\t// see if all elements belong to the same domain\n\t\tbool oneDomain = true;\n\t\tFEElement* el = mesh.FindElementFromID(l[0]); assert(el);\n\t\tFEDomain* dom = dynamic_cast<FEDomain*>(el->GetMeshPartition());\n\t\tfor (int i = 1; i < l.size(); ++i)\n\t\t{\n\t\t\tFEElement* el_i = mesh.FindElementFromID(l[i]); assert(el);\n\t\t\tFEDomain* dom_i = dynamic_cast<FEDomain*>(el_i->GetMeshPartition());\n\n\t\t\tif (dom != dom_i)\n\t\t\t{\n\t\t\t\toneDomain = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// assign indices to element set\n\t\tif (oneDomain)\n\t\t\tpg->Create(dom, l);\n\t\telse\n\t\t\tpg->Create(l);\n\n\t\t// add the element set to the mesh\n\t\tmesh.AddElementSet(pg);\n\t}\n\telse delete pg;\n}\n"
  },
  {
    "path": "FEBioXML/FEBioGeometrySection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n#include \"FEBModel.h\"\n\n//-----------------------------------------------------------------------------\n// Geometry Section (base class)\nclass FEBioGeometrySection : public FEBioFileSection\n{\npublic:\n\tFEBioGeometrySection(FEBioImport* pim) : FEBioFileSection(pim) {}\n\nprotected:\n\tbool ReadElement(XMLTag& tag, FEElement& el, int nid);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioGeometrySection1x : public FEBioGeometrySection\n{\nprotected:\n\tstruct FEDOMAIN\n\t{\n\t\tFE_Element_Spec\t\telem;\t// element type\n\t\tint\t\t\t\t\tmat;\t// material ID\n\t\tint\t\t\t\t\tnel;\t// number of elements\n\t};\n\npublic:\n\tFEBioGeometrySection1x(FEBioImport* pim) : FEBioGeometrySection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodeSection(XMLTag& tag);\n\tvoid ParseNodeSetSection(XMLTag& tag);\n\tvoid ParseElementSection(XMLTag& tag);\n\tvoid ParseElementDataSection(XMLTag& tag);\n\tvoid ParseElementData(FEElement& el, XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioGeometrySection2 : public FEBioGeometrySection\n{\npublic:\n\tFEBioGeometrySection2(FEBioImport* pim) : FEBioGeometrySection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodeSection(XMLTag& tag);\n\tvoid ParseEdgeSection(XMLTag& tag);\n\tvoid ParseSurfaceSection   (XMLTag& tag);\n\tvoid ParseElementSection   (XMLTag& tag);\n\tvoid ParseNodeSetSection   (XMLTag& tag);\n\tvoid ParseElementSetSection(XMLTag& tag);\n\tvoid ParseElementDataSection(XMLTag& tag);\n\tvoid ParseElementData(FEElement& el, XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioGeometrySection25 : public FEBioGeometrySection\n{\npublic:\n\tFEBioGeometrySection25(FEBioImport* pim) : FEBioGeometrySection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodeSection       (XMLTag& tag);\n\tvoid ParseDiscreteSetSection(XMLTag& tag);\n\tvoid ParseSurfacePairSection(XMLTag& tag);\n\tvoid ParseNodeSetPairSection(XMLTag& tag);\n\tvoid ParseNodeSetSetSection (XMLTag& tag);\n\tvoid ParsePartSection       (XMLTag& tag);\n\tvoid ParseInstanceSection   (XMLTag& tag);\n\tvoid ParseSurfaceSection    (XMLTag& tag);\n\tvoid ParseElementSection    (XMLTag& tag);\n\tvoid ParseNodeSetSection    (XMLTag& tag);\n\tvoid ParseEdgeSection       (XMLTag& tag);\n\tvoid ParseElementSetSection (XMLTag& tag);\n\n\t// New functions for parsing parts\n\tvoid ParsePart(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartNodeSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartElementSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartNodeSetSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartSurfaceSection(XMLTag& tag, FEBModel::Part* part);\n\nprotected:\n\tFEBModel\t\t\tm_feb;\n};\n\n//-----------------------------------------------------------------------------\nclass FEBioGeometrySection3 : public FEBioGeometrySection\n{\npublic:\n\tFEBioGeometrySection3(FEBioImport* pim) : FEBioGeometrySection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodeSection       (XMLTag& tag);\n\tvoid ParseDiscreteSetSection(XMLTag& tag);\n\tvoid ParseSurfacePairSection(XMLTag& tag);\n\tvoid ParseNodeSetPairSection(XMLTag& tag);\n\tvoid ParseNodeSetSetSection (XMLTag& tag);\n\tvoid ParsePartSection       (XMLTag& tag);\n\tvoid ParseInstanceSection   (XMLTag& tag);\n\tvoid ParseSurfaceSection    (XMLTag& tag);\n\tvoid ParseElementSection    (XMLTag& tag);\n\tvoid ParseNodeSetSection    (XMLTag& tag);\n\tvoid ParseEdgeSection       (XMLTag& tag);\n\tvoid ParseElementSetSection (XMLTag& tag);\n\n\t// New functions for parsing parts\n\tvoid ParsePart(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartNodeSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartElementSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartNodeSetSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartSurfaceSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartElementSetSection(XMLTag& tag, FEBModel::Part* part);\n\nprotected:\n\tFEBModel\t\t\tm_feb;\n};\n"
  },
  {
    "path": "FEBioXML/FEBioGeometrySection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioGeometrySection.h\"\n#include \"FECore/FESolidDomain.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/FENodeNodeList.h>\n\n//-----------------------------------------------------------------------------\n// functions defined in FEBioGeometrySection\nvoid set_element_fiber(FEElement& el, const vec3d& v, int ncomp);\nvoid set_element_mat_axis(FEElement& el, const vec3d& v1, const vec3d& v2, int ncomp);\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::Parse(XMLTag& tag)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\tfeb->m_maxid = 0;\n\n\t// read all sections\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParseNodeSection       (tag);\n\t\telse if (tag == \"Elements\"   ) ParseElementSection    (tag);\n\t\telse if (tag == \"NodeSet\"    ) ParseNodeSetSection    (tag);\n\t\telse if (tag == \"Surface\"    ) ParseSurfaceSection    (tag);\n\t\telse if (tag == \"Edge\"       ) ParseEdgeSection       (tag);\n\t\telse if (tag == \"ElementSet\" ) ParseElementSetSection (tag);\n\t\telse if (tag == \"DiscreteSet\") ParseDiscreteSetSection(tag);\n\t\telse if (tag == \"SurfacePair\") ParseSurfacePairSection(tag);\n\t\telse if (tag == \"NodeSetPair\") ParseNodeSetPairSection(tag);\n\t\telse if (tag == \"NodeSetSet\" ) ParseNodeSetSetSection (tag);\n\t\telse if (tag == \"Part\"       ) ParsePartSection       (tag);\n\t\telse if (tag == \"Instance\"   ) ParseInstanceSection   (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// At this point the mesh is completely read in.\n\t// Now we can allocate the degrees of freedom.\n\t// NOTE: We do this here since the mesh no longer automatically allocates the dofs.\n\t//       At some point I want to be able to read the mesh before deciding any physics.\n\t//       When that happens I'll have to move this elsewhere.\n\tFEModel& fem = *GetFEModel();\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tfem.GetMesh().SetDOFS(MAX_DOFS);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParsePartSection(XMLTag& tag)\n{\n\t// get the part name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// Create a new part\n\tFEBModel::Part* part = m_feb.AddPart(szname);\n\n\t// get the type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\tif (sztype == 0) sztype = \"template\";\n\n\t// check the value\n\tbool binstance = false;\n\tif      (strcmp(sztype, \"instance\") == 0) binstance = true;\n\telse if (strcmp(sztype, \"template\") == 0) binstance = false;\n\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// see if the from attribute is defined\n\tconst char* szfrom = tag.AttributeValue(\"from\", true);\n\tif (szfrom)\n\t{\n\t\t// make sure this is an empty leaf\n\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// redirect input to another file\n\t\tchar xpath[256] = {0};\n\t\tsnprintf(xpath, sizeof(xpath), \"febio_spec/Geometry/Part[@name=%s]\", szname);\n\t\tXMLReader xml;\n\t\tif (xml.Open(szfrom) == false) throw XMLReader::InvalidAttributeValue(tag, \"from\", szfrom);\n\t\tXMLTag tag2;\n\t\tif (xml.FindTag(xpath, tag2) == false) throw XMLReader::InvalidAttributeValue(tag, \"from\", szfrom);\n\n\t\t// read the part\n\t\tParsePart(tag2, part);\n\t}\n\telse ParsePart(tag, part);\n\n\t// instantiate the part\n\tif (binstance) \n\t{\n\t\tif (m_feb.BuildPart(*GetFEModel(), *part) == false) throw FEBioImport::FailedBuildingPart(part->Name());\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParsePart(XMLTag& tag, FEBModel::Part* part)\n{\n\t// read the part's sections\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParsePartNodeSection   (tag, part);\n\t\telse if (tag == \"Elements\"   ) ParsePartElementSection(tag, part);\n\t\telse if (tag == \"NodeSet\"    ) ParsePartNodeSetSection(tag, part);\n\t\telse if (tag == \"Surface\"    ) ParsePartSurfaceSection(tag, part);\n\t\telse if (tag == \"ElementSet\" ) ParsePartElementSetSection(tag, part);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseInstanceSection(XMLTag& tag)\n{\n\t// get the name and part tags\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tconst char* szpart = tag.AttributeValue(\"part\");\n\tif (szname == 0) szname = szpart;\n\n\t// find the part\n\tFEBModel::Part* oldPart = m_feb.FindPart(szpart);\n\tif (oldPart == 0) throw XMLReader::InvalidAttributeValue(tag, \"part\", szpart);\n\n\t// copy the part\n\tFEBModel::Part* newPart = new FEBModel::Part(*oldPart);\n\tm_feb.AddPart(newPart);\n\n\t// rename the part\n\tnewPart->SetName(szname);\n\n\t// parse any child tags\n\tTransform transform;\n\tif (tag.isleaf() == false)\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"transform\")\n\t\t\t{\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"translate\")\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble r[3];\n\t\t\t\t\t\ttag.value(r, 3);\n\t\t\t\t\t\ttransform.SetPosition(vec3d(r[0], r[1], r[2]));\n\t\t\t\t\t}\n\t\t\t\t\telse if (tag == \"rotate\")\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\t\t\t\tif (sztype == 0) sztype = \"quaternion\";\n\n\t\t\t\t\t\tif (strcmp(sztype, \"quaternion\") == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble v[4];\n\t\t\t\t\t\t\ttag.value(v, 4);\n\t\t\t\t\t\t\tquatd q(v[0], v[1], v[2], v[3]);\n\t\t\t\t\t\t\ttransform.SetRotation(q);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (strcmp(sztype, \"vector\") == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble v[3];\n\t\t\t\t\t\t\ttag.value(v, 3);\n\t\t\t\t\t\t\ttransform.SetRotation(vec3d(v[0], v[1], v[2]));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (strcmp(sztype, \"Euler\") == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble v[3];\n\t\t\t\t\t\t\ttag.value(v, 3);\n\t\t\t\t\t\t\ttransform.SetRotation(v[0], v[1], v[2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t\t\t}\n\t\t\t\t\telse if (tag == \"scale\")\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble s[3];\n\t\t\t\t\t\ttag.value(s, 3);\n\t\t\t\t\t\ttransform.SetScale(s[0], s[1], s[2]);\n\t\t\t\t\t}\n\n\t\t\t\t\t++tag;\n\t\t\t\t}\n\t\t\t\twhile (!tag.isend());\n\t\t\t}\n\t\t\telse if (tag == \"domain\")\n\t\t\t{\n\t\t\t\tconst char* szdom = tag.AttributeValue(\"name\");\n\t\t\t\tconst char* szmat = tag.AttributeValue(\"mat\");\n\t\t\t\t\n\t\t\t\tFEBModel::Domain* dom = newPart->FindDomain(szdom);\n\t\t\t\tif (dom == 0) throw XMLReader::InvalidAttributeValue(tag, \"name\", szdom);\n\n\t\t\t\t// make sure the material is defined\n\t\t\t\tFEModel& fem = *GetFEModel();\n\t\t\t\tFEMaterial* mat = fem.FindMaterial(szmat);\n\t\t\t\tif (mat == 0) throw FEBioImport::InvalidDomainMaterial();\n\n\t\t\t\tdom->SetMaterialName(szmat);\n\n\t\t\t\t// any additional parameters?\n\t\t\t\tif (tag.isleaf() == false)\n\t\t\t\t{\n\t\t\t\t\t++tag;\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tag == \"shell_thickness\")\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble h = 0.0;\n\t\t\t\t\t\t\ttag.value(h);\n\t\t\t\t\t\t\tdom->m_defaultShellThickness = h;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t++tag;\n\t\t\t\t\t} \n\t\t\t\t\twhile (!tag.isend());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\n\t// build this part\n\tif (m_feb.BuildPart(*GetFEModel(), *newPart, true, transform) == false) throw FEBioImport::FailedBuildingPart(newPart->Name());\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioGeometrySection3::ParseNodeSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// get the largest nodal ID\n\t// (It is assumed that nodes are sorted by ID so the last node should have the largest ID)\n\tint max_id = 0;\n\tif (N0 > 0) max_id = mesh.Node(N0 - 1).GetID();\n\n\t// first we need to figure out how many nodes there are\n\tXMLTag t(tag);\n\tint nodes = tag.children();\n\n\t// see if this list defines a set\n\tconst char* szl = tag.AttributeValue(\"name\", true);\n\tFENodeSet* ps = 0;\n\tif (szl)\n\t{\n\t\tps = new FENodeSet(&fem);\n\n\t\tps->SetName(szl);\n\t\tmesh.AddNodeSet(ps);\n\t}\n\n\t// resize node's array\n\tmesh.AddNodes(nodes);\n\n\t// read nodal coordinates\n\t++tag;\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tFENode& node = mesh.Node(N0 + i);\n\t\tvalue(tag, node.m_r0);\n\t\tnode.m_rt = node.m_r0;\n\n\t\t// get the nodal ID\n\t\tint nid = -1;\n\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t// Make sure it is valid\n\t\tif (nid <= max_id) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// set the ID\n\t\tnode.SetID(nid);\n\t\tmax_id = nid;\n\n\t\t// go on to the next node\n\t\t++tag;\n\t}\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps)\n\t{\n\t\tfor (int i = 0; i<nodes; ++i) ps->Add(N0 + i);\n\t}\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioGeometrySection3::ParsePartNodeSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// first we need to figure out how many nodes there are\n\tXMLTag t(tag);\n\tint nodes = tag.children();\n\n\t// see if this list defines a set\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tFEBModel::NodeSet* ps = 0;\n\tif (szname)\n\t{\n\t\tps = new FEBModel::NodeSet(szname);\n\t\tpart->AddNodeSet(ps);\n\t}\n\n\t// allocate node\n\tvector<FEBModel::NODE> node(nodes);\n\tvector<int> nodeList(nodes);\n\n\t// read nodal coordinates\n\t++tag;\n\tfor (int i = 0; i<nodes; ++i)\n\t{\n\t\tFEBModel::NODE& nd = node[i];\n\t\tvalue(tag, nd.r);\n\n\t\t// get the nodal ID\n\t\ttag.AttributeValue(\"id\", nd.id);\n\t\tnodeList[i] = nd.id;\n\n\t\t// go on to the next node\n\t\t++tag;\n\t}\n\n\t// add nodes to the part\n\tpart->AddNodes(node);\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps) ps->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioGeometrySection3::ParseElementSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the material ID\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\n\t// as of 2.5, we allow materials to be referenced by name, so try this first\n\tFEMaterial* pmat = fem.FindMaterial(szmat);\n\tif (pmat == 0)\n\t{\n\t\t// if we didn't find the material we assume that the material tag uses an ID\n\t\tint nmat = atoi(szmat) - 1;\n\t\tif ((nmat < 0) || (nmat >= fem.Materials())) throw FEBioImport::InvalidDomainMaterial();\n\n\t\t// get the domain's material class\n\t\tpmat = fem.GetMaterial(nmat);\n\t}\n\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname == 0) szname = \"_unnamed\";\n\n\t// get the element type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// create the new domain\n\tFEDomain* pdom = GetBuilder()->CreateDomain(espec, pmat);\n\tif (pdom == 0) throw FEBioImport::FailedCreatingDomain();\n\tFEDomain& dom = *pdom;\n\tdom.SetName(szname);\n\n\t// active flag\n\tconst char* szactive = tag.AttributeValue(\"active\", true);\n\tif (szactive)\n\t{\n\t\tif (strcmp(szactive, \"false\") == 0) pdom->SetActive(false);\n\t}\n\n\t// count elements\n\tint elems = tag.children();\n\tassert(elems);\n\n\t// add domain it to the mesh\n\tpdom->Create(elems, espec);\n\tpdom->SetMatID(pmat->GetID() - 1);\n\tmesh.AddDomain(pdom);\n\n\t// for named domains, we'll also create an element set\n\tFEElementSet* pg = 0;\n\tif (szname)\n\t{\n\t\tpg = new FEElementSet(&fem);\n\t\tpg->SetName(szname);\n\t\tmesh.AddElementSet(pg);\n\t}\n\n\t// read element data\n\t++tag;\n\tfor (int i = 0; i<elems; ++i)\n\t{\n\t\t// get the element ID\n\t\tint nid;\n\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t// Make sure element IDs increase\n\t\t//\t\tif (nid <= m_pim->m_maxid) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// keep track of the largest element ID\n\t\t// (which by assumption is the ID that was just read in)\n\t\tGetBuilder()->m_maxid = nid;\n\n\t\t// read the element data\n\t\tif (ReadElement(tag, dom.ElementRef(i), nid) == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// go to next tag\n\t\t++tag;\n\t}\n\n\t// create the element set\n\tif (pg) pg->Create(pdom);\n\n\t// assign material point data\n\tdom.CreateMaterialPointData();\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioGeometrySection3::ParsePartElementSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the material ID\n\tconst char* szmat = tag.AttributeValue(\"mat\", true);\n\n\t// get the (optional) name\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t// get the element type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// create the new domain\n\tFEBModel::Domain* dom = new FEBModel::Domain(espec);\n\tif (szname) dom->SetName(szname);\n\tif (szmat) dom->SetMaterialName(szmat);\n\n\t// count elements\n\tint elems = tag.children();\n\tassert(elems);\n\n\t// add domain it to the mesh\n\tdom->Create(elems);\n\tpart->AddDomain(dom);\n\n\t// for named domains, we'll also create an element set\n\tFEBModel::ElementSet* pg = 0;\n\tif (szname)\n\t{\n\t\tpg = new FEBModel::ElementSet(szname);\n\t\tpart->AddElementSet(pg);\n\t}\n\n\tvector<int> elemList(elems);\n\n\t// read element data\n\t++tag;\n\tfor (int i = 0; i<elems; ++i)\n\t{\n\t\tFEBModel::ELEMENT& el = dom->GetElement(i);\n\n\t\t// get the element ID\n\t\ttag.AttributeValue(\"id\", el.id);\n\t\telemList[i] = el.id;\n\n\t\t// read the element data\n\t\ttag.value(el.node, FEElement::MAX_NODES);\n\n\t\t// go to next tag\n\t\t++tag;\n\t}\n\n\t// set the element list\n\tif (pg) pg->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseNodeSetSection(XMLTag& tag)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create a new node set\n\tFENodeSet* pns = new FENodeSet(GetFEModel());\n\tpns->SetName(szname);\n\n\t// add the nodeset to the mesh\n\tmesh.AddNodeSet(pns);\n\n\t++tag;\n\tdo\n\t{\n\t\tif ((tag == \"n\") || (tag == \"node\"))\n\t\t{\n\t\t\tint nid = -1;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t\tnid = GetBuilder()->FindNodeFromID(nid);\n\t\t\tpns->Add(nid);\n\t\t}\n\t\telse if (tag == \"node_set\")\n\t\t{\n\t\t\tconst char* szset = tag.szvalue();\n\n\t\t\t// find the node set\n\t\t\tFENodeSet* ps = mesh.FindNodeSet(szset);\n\t\t\tif (ps == 0) throw XMLReader::InvalidValue(tag);\n\n\t\t\t// add the node set\n\t\t\tpns->Add(ps->GetNodeList());\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Geometry::Groups section of the FEBio input file\nvoid FEBioGeometrySection3::ParsePartNodeSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create the node set\n\tFEBModel::NodeSet* set = new FEBModel::NodeSet(szname);\n\tpart->AddNodeSet(set);\n\n\tint nodes = tag.children();\n\tvector<int> nodeList(nodes);\n\n\t++tag;\n\tfor (int i=0; i<nodes; ++i)\n\t{\n\t\t// get the ID\n\t\tint nid;\n\t\ttag.AttributeValue(\"id\", nid);\n\t\tnodeList[i] = nid;\n\t\t++tag;\n\t}\n\n\t// add nodes to the list\n\tset->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseDiscreteSetSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create the discrete element set\n\tFEDiscreteSet* ps = new FEDiscreteSet(&mesh);\n\tps->SetName(szname);\n\tmesh.AddDiscreteSet(ps);\n\n\t// see if the generate attribute was defined\n\tconst char* szgen = tag.AttributeValue(\"generate\", true);\n\tif (szgen == 0)\n\t{\n\t\t// read the node pairs\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"delem\")\n\t\t\t{\n\t\t\t\tint n[2];\n\t\t\t\ttag.value(n, 2);\n\t\t\t\tn[0] -= 1; n[1] -= 1;\n\t\t\t\tps->add(n[0], n[1]);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\telse\n\t{\n\t\tassert(tag.isempty());\n\n\t\t// generate all the springs from the mesh\n\t\tFENodeNodeList NNL;\n\t\tNNL.Create(mesh);\n\n\t\t// generate the springs\n\t\tfor (int i = 0; i<NNL.Size(); ++i)\n\t\t{\n\t\t\tint nval = NNL.Valence(i);\n\t\t\tfor (int j = 0; j<nval; ++j)\n\t\t\t{\n\t\t\t\tint n0 = i;\n\t\t\t\tint nj = NNL.NodeList(i)[j];\n\t\t\t\tif (n0 < nj)\n\t\t\t\t{\n\t\t\t\t\tps->add(n0, nj);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Edge section.\nvoid FEBioGeometrySection3::ParseEdgeSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the number of nodes\n\t// (we use this for checking the node indices of the facets)\n\tint NN = mesh.Nodes();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// count nr of segments\n\tint nsegs = tag.children();\n\n\t// allocate storage for segments\n\tFESegmentSet* ps = new FESegmentSet(&fem);\n\tps->Create(nsegs);\n\tps->SetName(szname);\n\n\t// add it to the mesh\n\tmesh.AddSegmentSet(ps);\n\n\t// read segments\n\t++tag;\n\tint nf[FEElement::MAX_NODES];\n\tfor (int i = 0; i<nsegs; ++i)\n\t{\n\t\tFESegmentSet::SEGMENT& line = ps->Segment(i);\n\n\t\t// set the facet type\n\t\tif (tag == \"line2\") line.ntype = 2;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the segment type also defines the number of nodes\n\t\tint N = line.ntype;\n\t\ttag.value(nf, N);\n\t\tfor (int j = 0; j<N; ++j)\n\t\t{\n\t\t\tint nid = nf[j] - 1;\n\t\t\tif ((nid<0) || (nid >= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\tline.node[j] = nid;\n\t\t}\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseSurfacePairSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create new surface pair\n\tFESurfacePair* p = new FESurfacePair(&mesh);\n\n\t// set its name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tp->SetName(szname);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"primary\")\n\t\t{\n\t\t\tconst char* sz = tag.szvalue();\n\t\t\tp->SetPrimarySurface(mesh.FindFacetSet(sz));\n\t\t\tif (p->GetPrimarySurface() == 0) throw XMLReader::InvalidValue(tag);\n\t\t}\n\t\telse if (tag == \"secondary\")\n\t\t{\n\t\t\tconst char* sz = tag.szvalue();\n\t\t\tp->SetSecondarySurface(mesh.FindFacetSet(sz));\n\t\t\tif (p->GetSecondarySurface() == 0) throw XMLReader::InvalidValue(tag);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// add it to the mesh\n\tmesh.AddSurfacePair(p);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseNodeSetPairSection(XMLTag& tag)\n{\n\tFEModelBuilder::NodeSetPair p;\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tstrcpy(p.szname, szname);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"primary\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"node_set\");\n\t\t\tp.set1 = mesh.FindNodeSet(sz);\n\t\t\tif (p.set1 == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sz);\n\t\t}\n\t\telse if (tag == \"secondary\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"node_set\");\n\t\t\tp.set2 = mesh.FindNodeSet(sz);\n\t\t\tif (p.set2 == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sz);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n\n\tGetBuilder()->AddNodeSetPair(p);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseNodeSetSetSection(XMLTag& tag)\n{\n\tFEModelBuilder::NodeSetSet p;\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tstrcpy(p.szname, szname);\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"node_set\")\n\t\t{\n\t\t\tconst char* sz = tag.AttributeValue(\"node_set\");\n\t\t\tFENodeSet* ns = mesh.FindNodeSet(sz);\n\t\t\tif (ns == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", sz);\n\t\t\tp.add(ns);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\tGetBuilder()->AddNodeSetSet(p);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Surface section.\nvoid FEBioGeometrySection3::ParseSurfaceSection(XMLTag& tag)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the number of nodes\n\t// (we use this for checking the node indices of the facets)\n\tint NN = mesh.Nodes();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// if parts are defined we use the new format\n\tif (m_feb.Parts() > 0)\n\t{\n\t\tFEFacetSet* ps = new FEFacetSet(&fem);\n\t\tps->SetName(szname);\n\n\t\t// add it to the mesh\n\t\tmesh.AddFacetSet(ps);\n\n\t\t// read the child surfaces\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"surface\")\n\t\t\t{\n\t\t\t\tFEFacetSet* pf = mesh.FindFacetSet(tag.szvalue());\n\t\t\t\tif (pf == 0) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\tps->Add(pf);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\n\t}\n\telse\n\t{\n\t\t// count nr of faces\n\t\tint faces = tag.children();\n\n\t\t// allocate storage for faces\n\t\tFEFacetSet* ps = new FEFacetSet(&fem);\n\t\tps->Create(faces);\n\t\tps->SetName(szname);\n\n\t\t// add it to the mesh\n\t\tmesh.AddFacetSet(ps);\n\n\t\t// read faces\n\t\t++tag;\n\t\tint nf[FEElement::MAX_NODES];\n\t\tfor (int i = 0; i<faces; ++i)\n\t\t{\n\t\t\tFEFacetSet::FACET& face = ps->Face(i);\n\n\t\t\t// set the facet type\n\t\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\t\telse if (tag == \"tri10\") face.ntype = 10;\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t// we assume that the facet type also defines the number of nodes\n\t\t\tint N = face.ntype;\n\t\t\tint nread = tag.value(nf, N);\n\t\t\tif (nread != face.ntype) throw XMLReader::InvalidValue(tag);\n\t\t\tfor (int j = 0; j<N; ++j)\n\t\t\t{\n\t\t\t\tint nid = nf[j] - 1;\n\t\t\t\tif ((nid<0) || (nid >= NN)) throw XMLReader::InvalidValue(tag);\n\t\t\t\tface.node[j] = nid;\n\t\t\t}\n\n\t\t\t++tag;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Reads a Geometry\\Surface section.\nvoid FEBioGeometrySection3::ParsePartSurfaceSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// count nr of faces\n\tint faces = tag.children();\n\n\t// allocate storage for faces\n\tFEBModel::Surface* ps = new FEBModel::Surface(szname);\n\tpart->AddSurface(ps);\n\tps->Create(faces);\n\n\t// read faces\n\t++tag;\n\tfor (int i = 0; i<faces; ++i)\n\t{\n\t\tFEBModel::FACET& face = ps->GetFacet(i);\n\n\t\t// get the ID (although we don't really use this)\n\t\ttag.AttributeValue(\"id\", face.id);\n\n\t\t// set the facet type\n\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the facet type also defines the number of nodes\n\t\tint N = face.ntype;\n\t\ttag.value(face.node, N);\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParsePartElementSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// allocate storage for faces\n\tFEBModel::ElementSet* ps = new FEBModel::ElementSet(szname);\n\tpart->AddElementSet(ps);\n\n\t// read elements\n\tvector<int> elemList;\n\t++tag;\n\tdo\n\t{\n\t\t// get the ID\n\t\tint id;\n\t\ttag.AttributeValue(\"id\", id);\n\t\telemList.push_back(id);\n\n\t\t++tag;\n\t} \n\twhile (!tag.isend());\n\n\tps->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGeometrySection3::ParseElementSetSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// create a new element set\n\tFEElementSet* pg = new FEElementSet(&fem);\n\tpg->SetName(szname);\n\n\t++tag;\n\tif (tag == \"elem_set\")\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"elem_set\")\n\t\t\t{\n\t\t\t\tconst char* sz = tag.szvalue();\n\t\t\t\tFEElementSet* eset = mesh.FindElementSet(sz);\n\t\t\t\tif (eset == 0) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\tpg->Add(*eset);\n\t\t\t}\n\t\t\telse { delete pg; throw XMLReader::InvalidTag(tag); }\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\telse\n\t{\n\t\tvector<int> l;\n\t\tdo\n\t\t{\n\t\t\tint nid = -1;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\t\t\tl.push_back(nid);\n\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\n\t\t// see if all elements belong to the same domain\n\t\tbool oneDomain = true;\n\t\tFEElement* el = mesh.FindElementFromID(l[0]); assert(el);\n\t\tFEDomain* dom = dynamic_cast<FEDomain*>(el->GetMeshPartition());\n\t\tfor (int i = 1; i < l.size(); ++i)\n\t\t{\n\t\t\tFEElement* el_i = mesh.FindElementFromID(l[i]); assert(el);\n\t\t\tFEDomain* dom_i = dynamic_cast<FEDomain*>(el_i->GetMeshPartition());\n\n\t\t\tif (dom != dom_i)\n\t\t\t{\n\t\t\t\toneDomain = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// assign indices to element set\n\t\tif (oneDomain)\n\t\t\tpg->Create(dom, l);\n\t\telse\n\t\t\tpg->Create(l);\n\t}\n\n\t// only add non-empty element sets\n\tif (pg->Elements())\n\t{\n\t\t// add the element set to the mesh\n\t\tmesh.AddElementSet(pg);\n\t}\n\telse delete pg;\n}\n"
  },
  {
    "path": "FEBioXML/FEBioGlobalsSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioGlobalsSection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEGlobalData.h\"\n#include \"FECore/FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\n//!  This function reads the global variables from the xml file\n//!\nvoid FEBioGlobalsSection::Parse(XMLTag& tag)\n{\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Constants\"          ) ParseConstants(tag);\n\t\telse if (tag == \"Solutes\"            ) ParseGlobalData(tag);\n\t\telse if (tag == \"SolidBoundMolecules\") ParseGlobalData(tag);\n\t\telse if (tag == \"Variables\"          ) ParseVariables(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGlobalsSection::ParseConstants(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\t++tag;\n\tstring s;\n\tdouble v;\n\tdo\n\t{\n\t\ts = string(tag.Name());\n\t\ttag.value(v);\n\t\tfem.SetGlobalConstant(s, v);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGlobalsSection::ParseGlobalData(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\t\n\t// read the global solute data\n\t++tag;\n\tdo\n\t{\n\t\t// create new global data\n\t\tFEGlobalData* pgd = fecore_new<FEGlobalData>(tag.Name(), &fem);\n\t\tif (pgd == 0) throw XMLReader::InvalidTag(tag);\n\t\tfem.AddGlobalData(pgd);\n\n\t\t// TODO: We have to call the Init member here because solute data \n\t\t//       allocates the concentration dofs and they have to be allocated before \n\t\t//       materials are read in. I'd like to move this to FEModel::Init but not sure\n\t\t//       yet how. \n\t\tpgd->Init();\n\n\t\t// read solute properties\n\t\tReadParameterList(tag, pgd);\n\t\t\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioGlobalsSection::ParseVariables(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tfem.GetParameterList();\n\tif (tag.isleaf()) return;\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"var\")\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tdouble v = 0; tag.value(v);\n\t\t\tfem.AddGlobalVariable(szname, v);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioGlobalsSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FileImport.h\"\n\n//-----------------------------------------------------------------------------\n//! Globals Section\nclass FEBIOXML_API FEBioGlobalsSection : public FEFileSection\n{\npublic:\n\tFEBioGlobalsSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseConstants   (XMLTag& tag);\n\tvoid ParseGlobalData  (XMLTag& tag);\n\tvoid ParseVariables   (XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioImport.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioImport.h\"\n#include \"FEBioIncludeSection.h\"\n#include \"FEBioModuleSection.h\"\n#include \"FEBioControlSection.h\"\n#include \"FEBioControlSection3.h\"\n#include \"FEBioControlSection4.h\"\n#include \"FEBioGlobalsSection.h\"\n#include \"FEBioMaterialSection.h\"\n#include \"FEBioGeometrySection.h\"\n#include \"FEBioBoundarySection.h\"\n#include \"FEBioBoundarySection3.h\"\n#include \"FEBioLoadsSection.h\"\n#include \"FEBioContactSection.h\"\n#include \"FEBioConstraintsSection.h\"\n#include \"FEBioInitialSection.h\"\n#include \"FEBioInitialSection3.h\"\n#include \"FEBioLoadDataSection.h\"\n#include \"FEBioOutputSection.h\"\n#include \"FEBioStepSection.h\"\n#include \"FEBioStepSection4.h\"\n#include \"FEBioDiscreteSection.h\"\n#include \"FEBioMeshDataSection.h\"\n#include \"FEBioCodeSection.h\"\n#include \"FEBioRigidSection.h\"\n#include \"FEBioRigidSection4.h\"\n#include \"FEBioMeshAdaptorSection.h\"\n#include \"FEBioMeshSection.h\"\n#include \"FEBioMeshSection4.h\"\n#include \"FEBioMeshDomainsSection4.h\"\n#include \"FEBioStepSection3.h\"\n#include \"FEBioScriptsSection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FECoreKernel.h\"\n#include <string.h>\n#include \"xmltool.h\"\n\nFEBioFileSection::FEBioFileSection(FEBioImport* feb) : FEFileSection(feb) {}\n\nFEBioImport* FEBioFileSection::GetFEBioImport() { return static_cast<FEBioImport*>(GetFileReader()); }\n\n//-----------------------------------------------------------------------------\nFEBioImport::InvalidVersion::InvalidVersion()\n{\n\tSetErrorString(\"Invalid version\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::InvalidMaterial::InvalidMaterial(int nel)\n{\n\tSetErrorString(\"Element %d has an invalid material type\", nel);\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::InvalidDomainType::InvalidDomainType()\t\n{\n\tSetErrorString(\"Invalid domain type\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::InvalidDomainMaterial::InvalidDomainMaterial()\n{\n\tSetErrorString(\"Invalid domain material\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::FailedCreatingDomain::FailedCreatingDomain()\n{\n\tSetErrorString(\"Failed creating domain\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::InvalidElementType::InvalidElementType()\n{\n\tSetErrorString(\"Invalid element type\\n\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::FailedLoadingPlugin::FailedLoadingPlugin(const char* szfile)\n{\n\tSetErrorString(\"failed loading plugin %s\\n\", szfile);\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::DuplicateMaterialSection::DuplicateMaterialSection()\n{\n\tSetErrorString(\"Material section has already been defined\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::MissingProperty::MissingProperty(const std::string& matName, const char* szprop)\n{\n\tSetErrorString(\"Component \\\"%s\\\" needs to have property \\\"%s\\\" defined\", matName.c_str(), szprop);\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::FailedAllocatingSolver::FailedAllocatingSolver(const char* sztype)\n{\n\tSetErrorString(\"Failed allocating solver \\\"%s\\\"\", sztype);\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::DataGeneratorError::DataGeneratorError()\n{\n\tSetErrorString(\"Error in data generation\");\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::FailedBuildingPart::FailedBuildingPart(const std::string& partName)\n{\n\tSetErrorString(\"Failed building part %s\", partName.c_str());\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::MeshDataError::MeshDataError()\n{\n\tSetErrorString(\"An error occurred processing mesh_data section.\");\n}\n\nFEBioImport::RepeatedNodeSet::RepeatedNodeSet(const std::string& name)\n{\n\tSetErrorString(\"A nodeset with name \\\"%s\\\" was already defined.\", name.c_str());\n}\n\nFEBioImport::RepeatedSurface::RepeatedSurface(const std::string& name)\n{\n\tSetErrorString(\"A surface with name \\\"%s\\\" was already defined.\", name.c_str());\n}\n\nFEBioImport::RepeatedEdgeSet::RepeatedEdgeSet(const std::string& name)\n{\n\tSetErrorString(\"An edge with name \\\"%s\\\" was already defined.\", name.c_str());\n}\n\nFEBioImport::RepeatedElementSet::RepeatedElementSet(const std::string& name)\n{\n\tSetErrorString(\"An element set with name \\\"%s\\\" was already defined.\", name.c_str());\n}\n\nFEBioImport::RepeatedPartList::RepeatedPartList(const std::string& name)\n{\n\tSetErrorString(\"An part list with name \\\"%s\\\" was already defined.\", name.c_str());\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::FEBioImport()\n{\n}\n\n//-----------------------------------------------------------------------------\nFEBioImport::~FEBioImport()\n{\n}\n\n//-----------------------------------------------------------------------------\n// Build the file section map based on the version number\nvoid FEBioImport::BuildFileSectionMap(int nversion)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tif (nversion < 0x0400) fecore.ShowDeprecationWarnings(false);\n\n\t// define the file structure\n\tm_map[\"Module\"     ] = new FEBioModuleSection     (this);\n\tm_map[\"Globals\"    ] = new FEBioGlobalsSection    (this);\n\tm_map[\"Output\"     ] = new FEBioOutputSection     (this);\n\n\t// older formats\n\tif (nversion < 0x0200)\n\t{\n\t\tm_map[\"Control\"    ] = new FEBioControlSection      (this);\n\t\tm_map[\"Material\"   ] = new FEBioMaterialSection     (this);\n\t    m_map[\"Geometry\"   ] = new FEBioGeometrySection1x   (this);\n\t\tm_map[\"Boundary\"   ] = new FEBioBoundarySection1x   (this);\n\t\tm_map[\"Loads\"      ] = new FEBioLoadsSection1x      (this);\n\t\tm_map[\"Constraints\"] = new FEBioConstraintsSection1x(this);\n\t\tm_map[\"Step\"       ] = new FEBioStepSection         (this);\n\t\tm_map[\"Initial\"    ] = new FEBioInitialSection      (this);\n\t\tm_map[\"LoadData\"   ] = new FEBioLoadDataSection     (this);\n\t}\n\n\t// version 2.0\n\tif (nversion == 0x0200)\n\t{\n\t\tm_map[\"Control\"    ] = new FEBioControlSection     (this);\n\t\tm_map[\"Material\"   ] = new FEBioMaterialSection    (this);\n\t    m_map[\"Geometry\"   ] = new FEBioGeometrySection2   (this);\n\t\tm_map[\"Initial\"    ] = new FEBioInitialSection     (this);\n\t\tm_map[\"Boundary\"   ] = new FEBioBoundarySection2   (this);\n\t\tm_map[\"Loads\"      ] = new FEBioLoadsSection2      (this);\n\t\tm_map[\"Include\"    ] = new FEBioIncludeSection     (this);\n\t\tm_map[\"Contact\"    ] = new FEBioContactSection2    (this);\n\t\tm_map[\"Discrete\"   ] = new FEBioDiscreteSection    (this);\n\t\tm_map[\"Code\"       ] = new FEBioCodeSection        (this); // added in FEBio 2.4 (experimental feature!)\n\t\tm_map[\"Constraints\"] = new FEBioConstraintsSection2(this);\n\t\tm_map[\"Step\"       ] = new FEBioStepSection2       (this);\n\t\tm_map[\"LoadData\"   ] = new FEBioLoadDataSection    (this);\n\t}\n\n\t// version 2.5\n\tif (nversion == 0x0205)\n\t{\n\t\tm_map[\"Control\"    ] = new FEBioControlSection      (this);\n\t\tm_map[\"Material\"   ] = new FEBioMaterialSection     (this);\n\t    m_map[\"Geometry\"   ] = new FEBioGeometrySection25   (this);\n\t\tm_map[\"Include\"    ] = new FEBioIncludeSection      (this);\n\t\tm_map[\"Initial\"    ] = new FEBioInitialSection25    (this);\n\t\tm_map[\"Boundary\"   ] = new FEBioBoundarySection25   (this);\n\t\tm_map[\"Loads\"      ] = new FEBioLoadsSection25      (this);\n\t\tm_map[\"Contact\"    ] = new FEBioContactSection25    (this);\n\t\tm_map[\"Discrete\"   ] = new FEBioDiscreteSection25   (this);\n\t\tm_map[\"Constraints\"] = new FEBioConstraintsSection25(this);\n\t\tm_map[\"Code\"       ] = new FEBioCodeSection         (this); // added in FEBio 2.4 (experimental feature!)\n\t\tm_map[\"LoadData\"   ] = new FEBioLoadDataSection     (this);\n\t\tm_map[\"MeshData\"   ] = new FEBioMeshDataSection     (this);\n\t\tm_map[\"Step\"       ] = new FEBioStepSection25       (this);\n\t\tm_map[\"MeshAdaptor\"] = new FEBioMeshAdaptorSection  (this);\t// added in FEBio 3.0\n\t}\n\n\t// version 3.0\n\tif (nversion == 0x0300)\n\t{\n\t\t// we no longer allow unknown attributes\n\t\tSetStopOnUnknownAttribute(true);\n\n\t\tm_map[\"Control\"    ] = new FEBioControlSection3     (this);\n\t\tm_map[\"Material\"   ] = new FEBioMaterialSection3    (this);\n\t\tm_map[\"Geometry\"   ] = new FEBioGeometrySection3    (this);\n\t\tm_map[\"Mesh\"       ] = new FEBioMeshSection         (this);\n\t\tm_map[\"MeshDomains\"] = new FEBioMeshDomainsSection  (this);\n\t\tm_map[\"Include\"    ] = new FEBioIncludeSection      (this);\n\t\tm_map[\"Initial\"    ] = new FEBioInitialSection3     (this);\n\t\tm_map[\"Boundary\"   ] = new FEBioBoundarySection3    (this);\n\t\tm_map[\"Loads\"      ] = new FEBioLoadsSection3       (this);\n\t\tm_map[\"Contact\"    ] = new FEBioContactSection25    (this);\n\t\tm_map[\"Discrete\"   ] = new FEBioDiscreteSection25   (this);\n\t\tm_map[\"Constraints\"] = new FEBioConstraintsSection25(this);\n\t\tm_map[\"Code\"       ] = new FEBioCodeSection         (this); // added in FEBio 2.4 (experimental feature!)\n\t\tm_map[\"MeshData\"   ] = new FEBioMeshDataSection3    (this);\n\t\tm_map[\"LoadData\"   ] = new FEBioLoadDataSection3    (this);\n\t\tm_map[\"Rigid\"      ] = new FEBioRigidSection        (this); // added in FEBio 3.0\n\t\tm_map[\"Step\"       ] = new FEBioStepSection3        (this);\n\t\tm_map[\"MeshAdaptor\"] = new FEBioMeshAdaptorSection  (this);\t// added in FEBio 3.0\n\t}\n\n\t// version 4.0\n\tif (nversion == 0x0400)\n\t{\n\t\t// we no longer allow unknown attributes\n\t\tSetStopOnUnknownAttribute(true);\n\n\t\tm_map[\"Control\"    ] = new FEBioControlSection4     (this);\n\t\tm_map[\"Material\"   ] = new FEBioMaterialSection3    (this);\n\t\tm_map[\"Mesh\"       ] = new FEBioMeshSection4        (this);\n\t\tm_map[\"MeshDomains\"] = new FEBioMeshDomainsSection4 (this);\n\t\tm_map[\"Include\"    ] = new FEBioIncludeSection      (this);\n\t\tm_map[\"Initial\"    ] = new FEBioInitialSection3     (this);\n\t\tm_map[\"Boundary\"   ] = new FEBioBoundarySection3    (this);\n\t\tm_map[\"Loads\"      ] = new FEBioLoadsSection4       (this);\n\t\tm_map[\"Contact\"    ] = new FEBioContactSection4     (this);\n\t\tm_map[\"Discrete\"   ] = new FEBioDiscreteSection25   (this);\n\t\tm_map[\"Constraints\"] = new FEBioConstraintsSection25(this);\n\t\tm_map[\"Code\"       ] = new FEBioCodeSection         (this); // added in FEBio 2.4 (experimental feature!)\n\t\tm_map[\"MeshData\"   ] = new FEBioMeshDataSection4    (this);\t// added in febio4\n\t\tm_map[\"LoadData\"   ] = new FEBioLoadDataSection3    (this);\n\t\tm_map[\"Rigid\"      ] = new FEBioRigidSection4       (this); // added in FEBio 4.0\n\t\tm_map[\"Scripts\"    ] = new FEBioScriptsSection      (this);\n\t\tm_map[\"Step\"       ] = new FEBioStepSection4        (this);\n\t\tm_map[\"MeshAdaptor\"] = new FEBioMeshAdaptorSection  (this);\t// added in FEBio 3.0\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEBioImport::Load(FEModel& fem, const char* szfile)\n{\n\tif (m_builder == nullptr)\n\t{\n\t\tm_builder = new FEModelBuilder(fem);\n\t}\n\n\t// intialize some variables\n\tm_szdmp[0] = 0;\n\tm_szlog[0] = 0;\n\tm_szplt[0] = 0;\n\n\tm_data.clear();\n\n\t// extract the path\n\tstrcpy(m_szpath, szfile);\n\tchar* ch = strrchr(m_szpath, '\\\\');\n\tif (ch==0) ch = strrchr(m_szpath, '/');\n\tif (ch==0) m_szpath[0] = 0; else *(ch+1)=0;\n\n\t// clean up\n\tfem.GetMesh().ClearDataMaps();\n\n\t// read the file\n\tif (ReadFile(szfile) == false) return false;\n\n\t// finish building\n\ttry {\n\t\tbool b = m_builder->Finish();\n\t\tif (b == false) return errf(\"FAILED building FEBio model.\");\n\t}\n\tcatch (std::exception e)\n\t{\n\t\tconst char* szerr = e.what();\n\t\tif (szerr == nullptr) szerr = \"(unknown exception)\";\n\t\treturn errf(\"%s\", e.what());\n\t}\t\n\tcatch (...)\n\t{\n\t\treturn errf(\"unknown exception.\");\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// This function parses the XML input file. The broot parameter is used to indicate\n// if this is the main control file or an included file. \nbool FEBioImport::ReadFile(const char* szfile, bool broot)\n{\n\t// Open the XML file\n\tXMLReader xml;\n\tif (xml.Open(szfile) == false) return errf(\"FATAL ERROR: Failed opening input file %s\\n\\n\", szfile);\n\n\t// Find the root element\n\tXMLTag tag;\n\ttry\n\t{\n\t\tif (xml.FindTag(\"febio_spec\", tag) == false) return errf(\"FATAL ERROR: febio_spec tag was not found. This is not a valid input file.\\n\\n\");\n\t}\n\tcatch (...)\n\t{\n\t\treturn errf(\"An error occured while finding the febio_spec tag.\\nIs this a valid FEBio input file?\\n\\n\");\n\t}\n\n\t// parse the file\n\ttry\n\t{\n\t\t// get the version number\n\t\tParseVersion(tag);\n\n\t\t// FEBio4 only supports file version 1.2, 2.0, 2.5, 3.0, and 4.0\n\t\tint nversion = GetFileVersion();\n\t\tif ((nversion != 0x0102) && \n\t\t\t(nversion != 0x0200) && \n\t\t\t(nversion != 0x0205) && \n\t\t\t(nversion != 0x0300) && \n\t\t\t(nversion != 0x0400)) throw InvalidVersion();\n\n\t\t// build the file section map based on the version number\n\t\tBuildFileSectionMap(nversion);\n\n\t\t// For versions before 2.5 we need to allocate all the degrees of freedom beforehand. \n\t\t// This is necessary because the Module section doesn't have to defined until a Control section appears.\n\t\t// That means that model components that depend on DOFs can be defined before the Module tag (e.g. in multi-step analyses) and this leads to problems.\n\t\t// In 2.5 this is solved by requiring that the Module tag is defined at the top of the file. \n\t\tif (broot && (nversion < 0x0205))\n\t\t{\n\t\t\t// We need to define a default Module type since before 2.5 this tag is optional for structural mechanics model definitions.\n\t\t\tGetBuilder()->SetActiveModule(\"solid\");\n\n\t\t\t// set default variables for older files.\n\t\t\tGetBuilder()->SetDefaultVariables();\n\t\t}\n\n\t\t// parse the file\n\t\t++tag;\n\n\t\t// From version 2.5 and up the first tag of the (main control) file has to be the Module tag.\n\t\tif (broot && (nversion >= 0x0205))\n\t\t{\n\t\t\tif (tag != \"Module\")\n\t\t\t{\n\t\t\t\treturn errf(\"First tag must be the Module section.\\n\\n\");\n\t\t\t}\n\n\t\t\t// try to find a section parser\n\t\t\tFEFileSectionMap::iterator is = m_map.find(tag.Name());\n\n\t\t\t// make sure we found a section reader\n\t\t\tif (is == m_map.end()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t// parse the module tag\n\t\t\tis->second->Parse(tag);\n\n\t\t\t// Now that the Module tag is read in, we'll want to create an analysis step.\n\t\t\t// Creating an analysis step will allocate a solver class (based on the module) \n\t\t\t// and this in turn will allocate the degrees of freedom.\n\t\t\t// TODO: This is kind of a round-about way and I really want to find a better solution.\n\t\t\t// NOTE: For version 4.0 we do not allocate the solver by default\n\t\t\tGetBuilder()->GetStep(nversion >= 0x0400 ? false : true);\n\n\t\t\t// let's get the next tag\n\t\t\t++tag;\n\t\t}\n\n\t\tdo\n\t\t{\n\t\t\t// try to find a section parser\n\t\t\tFEFileSectionMap::iterator is = m_map.find(tag.Name());\n\n\t\t\t// make sure we found a section reader\n\t\t\tif (is == m_map.end()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t// see if the file has the \"from\" attribute (for version 2.0 and up)\n\t\t\tif (nversion >= 0x0200)\n\t\t\t{\n\t\t\t\tconst char* szinc = tag.AttributeValue(\"from\", true);\n\t\t\t\tif (szinc)\n\t\t\t\t{\n\t\t\t\t\t// make sure this is a leaf\n\t\t\t\t\tif (tag.isleaf() == false) return errf(\"FATAL ERROR: included sections may not have child sections.\\n\\n\");\n\n\t\t\t\t\t// read this section from an included file.\n\t\t\t\t\tXMLReader xml2;\n\t\t\t\t\tif (xml2.Open(szinc) == false) return errf(\"FATAL ERROR: failed opening input file %s\\n\\n\", szinc);\n\n\t\t\t\t\t// find the febio_spec tag\n\t\t\t\t\tXMLTag tag2;\n\t\t\t\t\tif (xml2.FindTag(\"febio_spec\", tag2) == false) return errf(\"FATAL ERROR: febio_spec tag was not found. This is not a valid input file.\\n\\n\");\n\n\t\t\t\t\t// find the section we are looking for\n\t\t\t\t\tchar sz[512] = {0};\n\t\t\t\t\tsnprintf(sz, sizeof(sz), \"febio_spec/%s\", tag.Name());\n\t\t\t\t\tif (xml2.FindTag(sz, tag2) == false) return errf(\"FATAL ERROR: Couldn't find %s section in file %s.\\n\\n\", tag.Name(), szinc);\n\n\t\t\t\t\t// parse the section\n\t\t\t\t\tis->second->Parse(tag2);\n\t\t\t\t}\n\t\t\t\telse is->second->Parse(tag);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// parse the section\n\t\t\t\tis->second->Parse(tag);\n\t\t\t}\n\n\t\t\t// go to the next tag\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\t// --- XML Reader Exceptions ---\n\tcatch (XMLReader::Error& e)\n\t{\n\t\treturn errf(\"%s\", e.what());\n\t}\n\t// --- FEBioImport Exceptions ---\n\tcatch (FEFileException& e)\n\t{\n\t\treturn errf(\"%s (line %d)\\n\", e.GetErrorString(), xml.GetCurrentLine());\n\t}\n\t// --- Exception from DataStore ---\n\tcatch (UnknownDataField& e)\n\t{\n\t\treturn errf(\"\\\"%s\\\" is not a valid field variable name (line %d)\\n\", e.what(), xml.GetCurrentLine()-1);\n\t}\n\t// std::exception\n\tcatch (std::exception e)\n\t{\n\t\tconst char* szerr = e.what();\n\t\tif (szerr == nullptr) szerr = \"(unknown exception)\";\n\t\treturn errf(\"%s\", szerr);\n\t}\n\t// --- Unknown exceptions ---\n\tcatch (...)\n\t{\n\t\treturn errf(\"unrecoverable error (line %d)\\n\", xml.GetCurrentLine());\n\t\treturn false;\n\t}\n\n\t// close the XML file\n\txml.Close();\n\n\t// we're done!\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function parses the febio_spec tag for the version number\nvoid FEBioImport::ParseVersion(XMLTag &tag)\n{\n\tconst char* szv = tag.AttributeValue(\"version\");\n\tassert(szv);\n\tint n1, n2;\n\tint nr = sscanf(szv, \"%d.%d\", &n1, &n2);\n\tif (nr != 2) throw InvalidVersion();\n\tif ((n1 < 1) || (n1 > 0xFF)) throw InvalidVersion();\n\tif ((n2 < 0) || (n2 > 0xFF)) throw InvalidVersion();\n\tint nversion = (n1 << 8) + n2;\n\tSetFileVerion(nversion);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioImport::SetDumpfileName(const char* sz) { snprintf(m_szdmp, sizeof(m_szdmp), \"%s\", sz); }\nvoid FEBioImport::SetLogfileName (const char* sz) { snprintf(m_szlog, sizeof(m_szlog), \"%s\", sz); }\nvoid FEBioImport::SetPlotfileName(const char* sz) { snprintf(m_szplt, sizeof(m_szplt), \"%s\", sz); }\n\n//-----------------------------------------------------------------------------\nvoid FEBioImport::AddDataRecord(DataRecord* pd)\n{\n\tm_data.push_back(pd);\n}\n\n//-----------------------------------------------------------------------------\n// This tag parses a node set.\nFENodeSet* FEBioImport::ParseNodeSet(XMLTag& tag, const char* szatt)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tFENodeSet* pns = 0;\n\n\t// see if the set attribute is defined\n\tconst char* szset = tag.AttributeValue(szatt, true);\n\tif (szset)\n\t{\n\t\t// Make sure this is an empty tag\n\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// find the node set\n\t\tpns = mesh.FindNodeSet(szset);\n\t\tif (pns == 0) throw XMLReader::InvalidAttributeValue(tag, szatt, szset);\n\t}\n\telse\n\t{\n\t\t// This defines a node set, so we need a name tag\n\t\t// (For now this name is optional)\n\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\tif (szname == 0) szname = \"_unnamed\";\n\n\t\t// create a new node set\n\t\tpns = new FENodeSet(GetFEModel());\n\t\tpns->SetName(szname);\n\n\t\t// add the nodeset to the mesh\n\t\tmesh.AddNodeSet(pns);\n\n\t\t// read the nodes\n\t\tif (tag.isleaf())\n\t\t{\n\t\t\t// This format is deprecated\n\t\t\tvector<int> l;\n\t\t\tfexml::readList(tag, l);\n\t\t\tfor (int i=0; i<l.size(); ++i) pns->Add(GetBuilder()->FindNodeFromID(l[i]));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// read the nodes\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif ((tag == \"n\") || (tag == \"node\"))\n\t\t\t\t{\n\t\t\t\t\tint nid = -1;\n\t\t\t\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t\t\t\tnid = GetBuilder()->FindNodeFromID(nid);\n\t\t\t\t\tpns->Add(nid);\n\t\t\t\t}\n\t\t\t\telse if (tag == \"NodeSet\")\n\t\t\t\t{\n\t\t\t\t\tconst char* szset = tag.AttributeValue(szatt);\n\t\t\t\t\t\n\t\t\t\t\t// Make sure this is an empty tag\n\t\t\t\t\tif (tag.isempty() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\t// find the node set\n\t\t\t\t\tFENodeSet* ps = mesh.FindNodeSet(szset);\n\t\t\t\t\tif (ps == 0) throw XMLReader::InvalidAttributeValue(tag, szatt, szset);\n\n\t\t\t\t\t// add the node set\n\t\t\t\t\tpns->Add(ps->GetNodeList());\n\t\t\t\t}\n\t\t\t\telse if (tag == \"node_list\")\n\t\t\t\t{\n\t\t\t\t\tvector<int> nl;\n\t\t\t\t\tfexml::readList(tag, nl);\n\t\t\t\t\tfor (int i = 0; i<nl.size(); ++i) pns->Add(GetBuilder()->FindNodeFromID(nl[i]));\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\t\t}\n\t}\n\n\treturn pns;\n}\n\n//-----------------------------------------------------------------------------\nFESurface* FEBioImport::ParseSurface(XMLTag& tag, const char* szatt)\n{\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// create new surface\n\tFESurface* psurf = fecore_alloc(FESurface, GetFEModel());\n\n\t// see if the surface is referenced by a set of defined explicitly\n\tconst char* szset = tag.AttributeValue(szatt, true);\n\tif (szset)\n\t{\n\t\t// make sure this tag does not have any children\n\t\tif (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n\n\t\t// see if we can find the facet set\n\t\tFEMesh& m = GetFEModel()->GetMesh();\n\t\tFEFacetSet* ps = m.FindFacetSet(szset);\n\n\t\t// create a surface from the facet set\n\t\tif (ps)\n\t\t{\n\t\t\tif (GetBuilder()->BuildSurface(*psurf, *ps) == false) throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\t}\n\telse\n\t{\n\t\t// count how many pressure cards there are\n\t\tint npr = tag.children();\n\t\tpsurf->Create(npr);\n\n\t\tFEModelBuilder* feb = GetBuilder();\n\n\t\t++tag;\n\t\tint nf[FEElement::MAX_NODES ], N;\n\t\tfor (int i=0; i<npr; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = psurf->Element(i);\n\n\t\t\tif      (tag == \"quad4\") el.SetType(FE_QUAD4G4);\n\t\t\telse if (tag == \"tri3\" ) el.SetType(feb->m_ntri3);\n\t\t\telse if (tag == \"tri6\" ) el.SetType(feb->m_ntri6);\n\t\t\telse if (tag == \"tri7\" ) el.SetType(feb->m_ntri7);\n\t\t\telse if (tag == \"tri10\") el.SetType(feb->m_ntri10);\n\t\t\telse if (tag == \"quad8\") el.SetType(FE_QUAD8G9);\n\t\t\telse if (tag == \"quad9\") el.SetType(FE_QUAD9G9);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\tN = el.Nodes();\n\t\t\ttag.value(nf, N);\n\t\t\tfor (int j=0; j<N; ++j) el.m_node[j] = nf[j]-1;\n\n\t\t\t++tag;\n\t\t}\n\t}\n\n\treturn psurf;\n}\n"
  },
  {
    "path": "FEBioXML/FEBioImport.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FileImport.h\"\n#include <FEBioXML/XMLReader.h>\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FESolver.h\"\n#include \"FECore/DataStore.h\"\n#include <FECore/FEMesh.h>\n#include <FECore/FESurfaceMap.h>\n#include <FECore/tens3d.h>\n#include <string>\n\nclass FENodeSet;\nclass FEBioImport;\n\n//-----------------------------------------------------------------------------\n//! Base class for FEBio (feb) file sections.\nclass FEBioFileSection : public FEFileSection\n{\npublic:\n\tFEBioFileSection(FEBioImport* feb);\n\n\tFEBioImport* GetFEBioImport();\n};\n\n//=============================================================================\n//! Implements a class to import FEBio input files\n//!\nclass FEBIOXML_API FEBioImport : public FEFileImport\n{\npublic:\n\t// invalid version\n\tclass InvalidVersion : public FEFileException\n\t{\n\tpublic: InvalidVersion();\n\t};\n\n\t// invalid material defintion\n\tclass InvalidMaterial : public FEFileException\n\t{ \n\tpublic: InvalidMaterial(int nel);\n\t};\n\n\t// invalid domain type\n\tclass InvalidDomainType : public FEFileException\n\t{\n\tpublic: InvalidDomainType();\n\t};\n\n\t// cannot create domain\n\tclass FailedCreatingDomain : public FEFileException\n\t{\n\tpublic: FailedCreatingDomain();\n\t};\n\n\t// invalid element type\n\tclass InvalidElementType : public FEFileException\n\t{\n\tpublic: InvalidElementType();\n\t};\n\n\t// failed loading plugin\n\tclass FailedLoadingPlugin : public FEFileException\n\t{\n\tpublic: FailedLoadingPlugin(const char* sz);\n\t};\n\n\t// duplicate material section\n\tclass DuplicateMaterialSection : public FEFileException\n\t{\n\tpublic: DuplicateMaterialSection();\n\t};\n\n\t// invalid domain material\n\tclass InvalidDomainMaterial : public FEFileException\n\t{ \n\tpublic: InvalidDomainMaterial();\n\t};\n\n\t// missing property\n\tclass MissingProperty : public FEFileException\n\t{\n\tpublic: MissingProperty(const std::string& matName, const char* szprop);\n\t};\n\n\t//! Failed allocating solver\n\tclass FailedAllocatingSolver : public FEFileException\n\t{\n\tpublic: FailedAllocatingSolver(const char* sztype);\n\t};\n\n\t//! error in data generation\n\tclass DataGeneratorError : public FEFileException\n\t{\n\tpublic: \n\t\tDataGeneratorError();\n\t};\n\n\t//! failed building a part\n\tclass FailedBuildingPart : public FEFileException\n\t{\n\tpublic:\n\t\tFailedBuildingPart(const std::string& partName);\n\t};\n\n\t// Error while reading mesh data section\n\tclass MeshDataError : public FEFileException\n\t{\n\tpublic:\n\t\tMeshDataError();\n\t};\n\n\t// repeated node set\n\tclass RepeatedNodeSet : public FEFileException\n\t{\n\tpublic: RepeatedNodeSet(const std::string& name);\n\t};\n\n\t// repeated surface\n\tclass RepeatedSurface : public FEFileException\n\t{\n\tpublic: RepeatedSurface(const std::string& name);\n\t};\n\n\t// repeated edge set\n\tclass RepeatedEdgeSet : public FEFileException\n\t{\n\tpublic: RepeatedEdgeSet(const std::string& name);\n\t};\n\n\t// repeated element set\n\tclass RepeatedElementSet : public FEFileException\n\t{\n\tpublic: RepeatedElementSet(const std::string& name);\n\t};\n\n\t// repeated part list\n\tclass RepeatedPartList : public FEFileException\n\t{\n\tpublic: RepeatedPartList(const std::string& name);\n\t};\n\npublic:\n\t//! constructor\n\tFEBioImport();\n\n\t//! destructor\n\t~FEBioImport();\n\n\t//! open the file\n\tbool Load(FEModel& fem, const char* szfile);\n\n\t//! read the contents of a file\n\tbool ReadFile(const char* szfile, bool broot = true);\n\npublic:\n\tvoid SetDumpfileName(const char* sz);\n\tvoid SetLogfileName (const char* sz);\n\tvoid SetPlotfileName(const char* sz);\n\n\tvoid AddDataRecord(DataRecord* pd);\n\npublic:\n\t// Helper functions for reading node sets, surfaces, etc.\n\tFENodeSet* ParseNodeSet(XMLTag& tag, const char* szatt = \"set\");\n\tFESurface* ParseSurface(XMLTag& tag, const char* szatt = \"surf\");\n\nprotected:\n\tvoid ParseVersion(XMLTag& tag);\n\n\tvoid BuildFileSectionMap(int nversion);\n\npublic:\n\tchar\tm_szdmp[512];\n\tchar\tm_szlog[512];\n\tchar\tm_szplt[512];\n\npublic:\n\tstd::vector<DataRecord*>\t\tm_data;\n};\n"
  },
  {
    "path": "FEBioXML/FEBioIncludeSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioIncludeSection.h\"\n\n//-----------------------------------------------------------------------------\n//! Parse the Include section (new in version 2.0)\n//! This section includes the contents of another FEB file.\nvoid FEBioIncludeSection::Parse(XMLTag& tag)\n{\n\t// see if we need to pre-pend a path\n\tchar szin[512];\n\tstrcpy(szin, tag.szvalue());\n\tchar* ch = strrchr(szin, '\\\\');\n\tif (ch==0) ch = strrchr(szin, '/');\n\tif (ch==0)\n\t{\n\t\t// pre-pend the name with the input path\n\t\tsnprintf(szin, sizeof(szin), \"%s%s\", GetFileReader()->GetFilePath(), tag.szvalue());\n\t}\n\n\t// read the file\n\tif (GetFEBioImport()->ReadFile(szin, false) == false)\n\t\tthrow XMLReader::InvalidValue(tag);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioIncludeSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Include section (new in version 2.0)\nclass FEBioIncludeSection : public FEBioFileSection\n{\npublic:\n\tFEBioIncludeSection(FEBioImport* pim) : FEBioFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioInitialSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioInitialSection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/DOFS.h\"\n#include <FECore/FEInitialCondition.h>\n#include <FECore/FECoreKernel.h>\n\n//-----------------------------------------------------------------------------\nvoid FEBioInitialSection::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// make sure we've read the nodes section\n\tif (mesh.Nodes() == 0) throw XMLReader::InvalidTag(tag);\n\n\t// read nodal data\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"velocity\")\n\t\t{\n\t\t\tFENodalIC* pic = fecore_new<FENodalIC>(\"velocity\", &fem);\n\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddInitialCondition(pic);\n\n\t\t\t// create a node set\n\t\t\tFENodeSet* nset = new FENodeSet(&fem);\n\t\t\tfem.GetMesh().AddNodeSet(nset);\n\t\t\tpic->SetNodeSet(nset);\n\n\t\t\tstd::vector<vec3d> values;\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"node\")\n\t\t\t\t{\n\t\t\t\t\tint nid = ReadNodeID(tag);\n\t\t\t\t\tvec3d v;\n\t\t\t\t\tvalue(tag, v);\n\t\t\t\t\tnset->Add(nid);\n\t\t\t\t\tvalues.push_back(v);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\n\t\t\t// TODO: Fix this! I need to add a mechanism again for setting mapped data.\n\t\t\tFEParam* param = pic->GetParameter(\"value\"); assert(param);\n\t\t\tFEParamVec3& val = param->value<FEParamVec3>();\n\t\t\tval = values[0];\n//\t\t\tfor (int i = 0; i < values.size(); ++i) pic->SetValue(i, values[i]);\n\n\t\t}\n\t\telse if (tag == \"ic\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\t\t\tFEInitialCondition* pic = fecore_new<FEInitialCondition>(sztype, &fem);\n\n\t\t\tif (tag.isleaf() == false)\n\t\t\t{\n\t\t\t\tFEParameterList& pl = pic->GetParameterList();\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (ReadParameter(tag, pl) == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t++tag;\n\t\t\t\t}\n\t\t\t\twhile (!tag.isend());\n\t\t\t}\n\t\t\t\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddInitialCondition(pic);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Get the degree of freedom\n\t\t\tint ndof = -1;\n\t\t\tif      (tag == \"temperature\"   ) ndof = dofs.GetDOF(\"T\");\n\t\t\telse if (tag == \"fluid_pressure\") ndof = dofs.GetDOF(\"p\");\n            else if (tag == \"shell_fluid_pressure\") ndof = dofs.GetDOF(\"q\");\n            else if (tag == \"dilatation\"    ) ndof = dofs.GetDOF(\"ef\");\n\t\t\telse if (tag == \"concentration\" )\n\t\t\t{\n\t\t\t\t// TODO: Add a check to make sure that a solute with this ID exists\n\t\t\t\tint isol = 0;\n\t\t\t\tconst char* sz = tag.AttributeValue(\"sol\", true);\n\t\t\t\tif (sz) isol = atoi(sz) - 1;\n\n\t\t\t\tndof = dofs.GetDOF(\"concentration\", isol);\n\t\t\t}\n            else if (tag == \"shell_concentration\" )\n            {\n                // TODO: Add a check to make sure that a solute with this ID exists\n                int isol = 0;\n                const char* sz = tag.AttributeValue(\"sol\", true);\n                if (sz) isol = atoi(sz) - 1;\n                \n                ndof = dofs.GetDOF(\"shell concentration\", isol);\n            }\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\tif (ndof == -1) throw XMLReader::InvalidTag(tag);\n\n\t\t\t// allocate initial condition\n\t\t\tFEInitialDOF* pic = dynamic_cast<FEInitialDOF*>(fecore_new<FEInitialCondition>(\"init_dof\", &fem));\n\t\t\tpic->SetDOF(ndof);\n\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddInitialCondition(pic);\n\n\t\t\t// create a node set\n\t\t\tFENodeSet* nset = new FENodeSet(&fem);\n\t\t\tfem.GetMesh().AddNodeSet(nset);\n\t\t\tpic->SetNodeSet(nset);\n\n\t\t\tstd::vector<double> vals;\n\n\t\t\t// read the node list and values\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"node\")\n\t\t\t\t{\n\t\t\t\t\tint nid = ReadNodeID(tag);\n\t\t\t\t\tdouble p;\n\t\t\t\t\tvalue(tag, p);\n\t\t\t\t\tnset->Add(nid);\n\t\t\t\t\tvals.push_back(p);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\n\t\t\t// TODO: Fix this! I need to add a mechanism again for setting mapped data.\n\t\t\tpic->SetValue(vals[0]);\n//\t\t\tfor (int i = 0; i < vals.size(); ++i) pic->SetValue(i, vals[i]);\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioInitialSection25::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// make sure we've read the nodes section\n\tif (mesh.Nodes() == 0) throw XMLReader::InvalidTag(tag);\n\n\t// read nodal data\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"init\")\n\t\t{\n\t\t\t// get the BC\n\t\t\tconst char* sz = tag.AttributeValue(\"bc\");\n\t\t\tint ndof = dofs.GetDOF(sz);\n\t\t\tif (ndof == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\n\t\t\t// get the node set\n\t\t\tconst char* szset = tag.AttributeValue(\"node_set\");\n\t\t\tFENodeSet* pns = mesh.FindNodeSet(szset);\n\t\t\tif (pns == 0) throw XMLReader::InvalidTag(tag);\n\n\t\t\t// allocate initial condition\n\t\t\tFEInitialDOF* pic = dynamic_cast<FEInitialDOF*>(fecore_new<FEInitialCondition>(\"init_dof\", &fem));\n\t\t\tpic->SetDOF(ndof);\n\t\t\tpic->SetNodeSet(pns);\n\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddInitialCondition(pic);\n\n\t\t\t// read parameters\n\t\t\tReadParameterList(tag, pic);\n\t\t}\n\t\telse if (tag == \"ic\")\n\t\t{\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\t\t\tFEInitialCondition* pic = fecore_new<FEInitialCondition>(sztype, &fem);\n\n\t\t\tFENodalIC* nic = dynamic_cast<FENodalIC*>(pic);\n\t\t\tif (nic)\n\t\t\t{\n\t\t\t\t// get the node set\n\t\t\t\tconst char* szset = tag.AttributeValue(\"node_set\");\n\t\t\t\tFENodeSet* pns = mesh.FindNodeSet(szset);\n\t\t\t\tif (pns == 0) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\tnic->SetNodeSet(pns);\n\t\t\t}\n\n\t\t\tReadParameterList(tag, pic);\n\t\t\t\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddInitialCondition(pic);\n\t\t}\n\t\telse if (tag == \"rigid_body\")\n\t\t{\n\t\t\t// get the material ID\n\t\t\tconst char* szm = tag.AttributeValue(\"mat\");\n\t\t\tint nmat = atoi(szm);\n\t\t\tif ((nmat <= 0) || (nmat > fem.Materials())) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szm);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"initial_velocity\")\n\t\t\t\t{\n\t\t\t\t\t// get the initial velocity\n\t\t\t\t\tvec3d v;\n\t\t\t\t\tvalue(tag, v);\n\n\t\t\t\t\t// create the initial condition\n\t\t\t\t\tFEStepComponent* pic = fecore_new_class<FEInitialCondition>(\"FERigidBodyVelocity\", &fem);\n\t\t\t\t\tpic->SetParameter(\"rb\", nmat);\n\t\t\t\t\tpic->SetParameter(\"value\", v);\n\n\t\t\t\t\t// add this initial condition to the current step\n\t\t\t\t\tGetBuilder()->AddRigidComponent(pic);\n\t\t\t\t}\n\t\t\t\telse if (tag == \"initial_angular_velocity\")\n\t\t\t\t{\n\t\t\t\t\t// get the initial angular velocity\n\t\t\t\t\tvec3d w;\n\t\t\t\t\tvalue(tag, w);\n\n\t\t\t\t\t// create the initial condition\n\t\t\t\t\tFEStepComponent* pic = fecore_new_class<FEInitialCondition>(\"FERigidBodyAngularVelocity\", &fem);\n\t\t\t\t\tpic->SetParameter(\"rb\", nmat);\n\t\t\t\t\tpic->SetParameter(\"value\", w);\n\n\t\t\t\t\t// add this initial condition to the current step\n\t\t\t\t\tGetBuilder()->AddRigidComponent(pic);\n\t\t\t\t}\n\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\t\t}\n\t\telse\n\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioInitialSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Initial Section\nclass FEBioInitialSection : public FEFileSection\n{\npublic:\n\tFEBioInitialSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Initial Section\nclass FEBioInitialSection25 : public FEFileSection\n{\npublic:\n\tFEBioInitialSection25(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioInitialSection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioInitialSection3.h\"\n#include <FECore/FEInitialCondition.h>\n\nFEBioInitialSection3::FEBioInitialSection3(FEFileImport* pim) : FEFileSection(pim) \n{\n}\n\nvoid FEBioInitialSection3::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"ic\") ParseIC(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\nvoid FEBioInitialSection3::ParseIC(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\n\t// read the type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t// try to allocate the initial condition\n\tFEInitialCondition* pic = fecore_new<FEInitialCondition>(sztype, fem);\n\tif (pic == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// add it to the model\n\tGetBuilder()->AddInitialCondition(pic);\n\n\tFENodalIC* nic = dynamic_cast<FENodalIC*>(pic);\n\tif (nic)\n\t{\n\t\t// read required node_set attribute\n\t\tconst char* szset = tag.AttributeValue(\"node_set\");\n\t\tFENodeSet* nodeSet = GetBuilder()->FindNodeSet(szset);\n\t\tif (nodeSet == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\t\tnic->SetNodeSet(nodeSet);\n\t}\n\n\t// Read the parameter list\n\tReadParameterList(tag, pic);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioInitialSection3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Initial Section\nclass FEBioInitialSection3 : public FEFileSection\n{\npublic:\n\tFEBioInitialSection3(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n\tvoid ParseIC(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioLoadDataSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioLoadDataSection.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELoadCurve.h>\n\n//-----------------------------------------------------------------------------\nFEBioLoadDataSection::FEBioLoadDataSection(FEFileImport* pim) : FEFileSection(pim) \n{\n\tm_redefineCurves = false;\n}\n\n//-----------------------------------------------------------------------------\n//!  This function reads the load data section from the xml file\n//!\nvoid FEBioLoadDataSection::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\t\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"loadcurve\")\n\t\t{\n\t\t\t// load curve ID\n\t\t\tint nid;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t\t// default type and extend mode\n\t\t\tPointCurve::INTFUNC ntype = PointCurve::LINEAR;\n\t\t\tPointCurve::EXTMODE nextm = PointCurve::CONSTANT;\n\n\t\t\t// get the (optional) type\n\t\t\tXMLAtt* patt = tag.Attribute(\"type\", true);\n\t\t\tif (patt)\n\t\t\t{\n\t\t\t\tXMLAtt& type = *patt;\n\t\t\t\tif      (type == \"step\"  ) ntype = PointCurve::STEP;\n\t\t\t\telse if (type == \"linear\") ntype = PointCurve::LINEAR;\n\t\t\t\telse if (type == \"smooth\") ntype = PointCurve::SMOOTH;\n                else if (type == \"cubic spline\") ntype = PointCurve::CSPLINE;\n                else if (type == \"control points\") ntype = PointCurve::CPOINTS;\n                else if (type == \"approximation\" ) ntype = PointCurve::APPROX;\n                else if (type == \"smooth step\"   ) ntype = PointCurve::SMOOTH_STEP;\n                else if (type == \"C2-smooth\" ) ntype = PointCurve::C2SMOOTH;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", type.cvalue());\n\t\t\t}\n\n\t\t\t// get the optional extend mode\n\t\t\tpatt = tag.Attribute(\"extend\", true);\n\t\t\tif (patt)\n\t\t\t{\n\t\t\t\tXMLAtt& ext = *patt;\n\t\t\t\tif      (ext == \"constant\"     ) nextm = PointCurve::CONSTANT;\n\t\t\t\telse if (ext == \"extrapolate\"  ) nextm = PointCurve::EXTRAPOLATE;\n\t\t\t\telse if (ext == \"repeat\"       ) nextm = PointCurve::REPEAT;\n\t\t\t\telse if (ext == \"repeat offset\") nextm = PointCurve::REPEAT_OFFSET;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"extend\", ext.cvalue());\n\t\t\t}\n\n\t\t\t// get the number of load curves\n\t\t\tint nlc = fem.LoadControllers();\n\n\t\t\t// find or create the load curve\n\t\t\tFELoadCurve* plc = 0;\n\n\t\t\t// see if this refers to a valid curve\n\t\t\tif (m_redefineCurves)\n\t\t\t{\n\t\t\t\tif ((nid > 0) && (nid <= nlc)) \n\t\t\t\t{\n\t\t\t\t\tplc = dynamic_cast<FELoadCurve*>(fem.GetLoadController(nid - 1));\n\t\t\t\t\tassert(plc);\n\n\t\t\t\t\t// clear the curve since we're about to read in new data points\n\t\t\t\t\tplc->Clear();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if the ID does not refer to an existing curve, make sure it defines the next curve\n\t\t\tif (plc == 0)\n\t\t\t{\n\t\t\t\t// check that the ID is one more than the number of load curves defined\n\t\t\t\t// This is to make sure that the ID's are in numerical order and no values are skipped.\n\t\t\t\tif (nid != nlc + 1) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t\t\t// create the loadcurve\n\t\t\t\tplc = fecore_new<FELoadCurve>(\"loadcurve\", &fem); assert(plc);\n\t\t\t\tplc->SetID(nid - 1);\n\n\t\t\t\t// add the loadcurve\n\t\t\t\tfem.AddLoadController(plc);\n\t\t\t}\n\n\t\t\t// set the load curve attributes\n\t\t\tplc->SetInterpolation(ntype);\n\t\t\tplc->SetExtendMode(nextm);\n\n\t\t\t// read the data points\n\t\t\tdouble d[2];\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\ttag.value(d, 2);\n\t\t\t\tplc->Add(d[0], d[1]);\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\n\t\t\tif (m_redefineCurves)\n\t\t\t{\n\t\t\t\tplc->Init();\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioLoadDataSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// LoadData Section\nclass FEBioLoadDataSection : public FEFileSection\n{\npublic:\n\tFEBioLoadDataSection(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n\n\t// Set the redefine curves flag.\n\t// When this flag is set, curves can be redefined by using an existing ID\n\tvoid SetRedefineCurvesFlag(bool b) { m_redefineCurves = b; }\n\nprotected:\n\tbool\tm_redefineCurves;\t// flag to allow redefining curves\n};\n\n//-----------------------------------------------------------------------------\n// LoadData Section\nclass FEBioLoadDataSection3 : public FEFileSection\n{\npublic:\n\tFEBioLoadDataSection3(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n\n\t// Set the redefine curves flag.\n\t// When this flag is set, curves can be redefined by using an existing ID\n\tvoid SetRedefineCurvesFlag(bool b) { m_redefineCurves = b; }\n\nprotected:\n\tbool\tm_redefineCurves;\t// flag to allow redefining curves\n};\n"
  },
  {
    "path": "FEBioXML/FEBioLoadDataSection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioLoadDataSection.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELoadController.h>\n#include <sstream>\n\n//-----------------------------------------------------------------------------\nFEBioLoadDataSection3::FEBioLoadDataSection3(FEFileImport* pim) : FEFileSection(pim) \n{\n\tm_redefineCurves = false;\n}\n\n//-----------------------------------------------------------------------------\n//!  This function reads the load data section from the xml file\n//!\nvoid FEBioLoadDataSection3::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\t\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"load_controller\")\n\t\t{\n\t\t\t// load curve ID\n\t\t\tint nid;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\n\t\t\t// get the type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\t// get the number of load curves\n\t\t\tint nlc = fem.LoadControllers();\n\n\t\t\t// create the controller\n\t\t\tFELoadController* plc = fecore_new<FELoadController>(sztype, &fem);\n\t\t\tif (plc == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\");\n\n\t\t\t// set the ID\n\t\t\tplc->SetID(nid - 1);\n\n\t\t\t// see if this refers to a valid curve\n\t\t\tif (m_redefineCurves && ((nid > 0) && (nid <= nlc)))\n\t\t\t{\n\t\t\t\tfem.ReplaceLoadController(nid - 1, plc);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// check that the ID is one more than the number of load curves defined\n\t\t\t\t// This is to make sure that the ID's are in numerical order and no values are skipped.\n\t\t\t\tif (nid != nlc + 1)\n\t\t\t\t{\n\t\t\t\t\tdelete plc;\n\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"id\");\n\t\t\t\t}\n\n\t\t\t\t// add the controller\n\t\t\t\tfem.AddLoadController(plc);\n\t\t\t}\n\n\t\t\t// read the parameter list\n\t\t\tReadParameterList(tag, plc);\n\n\t\t\t// make sure we have a name\n\t\t\tstring name = plc->GetName();\n\t\t\tif (name.empty())\n\t\t\t{\n\t\t\t\tstringstream ss;\n\t\t\t\tss << \"lc\" << plc->GetID() + 1;\n\t\t\t\tname = ss.str();\n\t\t\t\tplc->SetName(name);\n\t\t\t}\n\n\t\t\tif (m_redefineCurves)\n\t\t\t{\n\t\t\t\t// We only get here during restart, in which case the new load controllers\n\t\t\t\t// will not get a chance to be initialized, so we'll do it here.\n\t\t\t\tplc->Init();\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioLoadsSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioLoadsSection.h\"\n#include <FECore/FEBodyLoad.h>\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEEdgeLoad.h>\n#include <FECore/FEEdge.h>\n#include <FECore/FEPrescribedBC.h>\n#include <FECore/log.h>\n\n//=============================================================================\n// FEBioLoadsSection1x\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection1x::Parse(XMLTag& tag)\n{\n\t// make sure this tag has children\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"force\"      ) ParseNodalLoad(tag);\n\t\telse if (tag == \"body_force\" ) ParseBodyForce(tag);\n\t\telse if (tag == \"heat_source\") ParseBodyLoad (tag);\n\t\telse ParseSurfaceLoad(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n\n//-----------------------------------------------------------------------------\n// NOTE: note that this section used to be in the Globals section (version 1.1)\nvoid FEBioLoadsSection1x::ParseBodyForce(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tconst char* szt = tag.AttributeValue(\"type\", true);\n\tif (szt == 0) szt = \"const\";\n\n\t// create the body force\n\tFEBodyLoad* pf = fecore_new<FEBodyLoad>(szt, &fem);\n\tif (pf == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", szt);\n\n\t// add it to the model\n\tfem.AddModelLoad(pf);\n\n\t// read the parameter list\n\tReadParameterList(tag, pf);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection1x::ParseBodyLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEBodyLoad* pbl = fecore_new<FEBodyLoad>(tag.Name(), &fem);\n\tif (pbl == 0) throw XMLReader::InvalidTag(tag);\n\tReadParameterList(tag, pbl);\n\tfem.AddModelLoad(pbl);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection1x::ParseNodalLoad(XMLTag &tag)\n{\n\t// No longer supported\n\tthrow XMLReader::InvalidTag(tag);\n/*\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// count how many nodal forces there are\n\tint ncnf = tag.children();\n\n\t// read the prescribed data\n\t++tag;\n\tfor (int i = 0; i<ncnf; ++i)\n\t{\n\t\tint n = atoi(tag.AttributeValue(\"id\")) - 1, bc;\n\t\tconst char* sz = tag.AttributeValue(\"bc\");\n\n\t\tbc = dofs.GetDOF(sz);\n\t\tif (bc == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\n\t\tsz = tag.AttributeValue(\"lc\");\n\t\tint lc = atoi(sz) - 1;\n\n\t\tdouble scale = 0.0;\n\t\tvalue(tag, scale);\n\n\t\tFENodalLoad* pfc = dynamic_cast<FENodalLoad*>(fecore_new<FEBoundaryCondition>(\"nodal_load\", &fem));\n\t\tpfc->SetDOF(bc);\n\t\tpfc->SetLoad(scale);\n\t\tpfc->AddNode(n);\n\n\t\tif (lc >= 0)\n\t\t{\n\t\t\tFEParam* p = pfc->GetParameter(\"scale\");\n\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\tfem.AttachLoadController(p, lc);\n\t\t}\n\n\t\t// add it to the model\n\t\tGetBuilder()->AddNodalLoad(pfc);\n\n\t\t++tag;\n\t}\n*/\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection1x::ParseSurfaceLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// count how many pressure cards there are\n\tint npr = tag.children();\n\n\t// create a new surface\n\tFESurface* psurf = fecore_alloc(FESurface, &fem);\n\tpsurf->Create(npr);\n\tfem.GetMesh().AddSurface(psurf);\n\n\t// create surface load\n\tFESurfaceLoad* ps = fecore_new<FESurfaceLoad>(tag.Name(), &fem);\n\tif (ps == 0) throw XMLReader::InvalidTag(tag);\n\n\tFEModelBuilder* feb = GetBuilder();\n\n\t// read the pressure data\n\t++tag;\n\tint nf[FEElement::MAX_NODES], N;\n\tfor (int i = 0; i<npr; ++i)\n\t{\n\t\tFESurfaceElement& el = psurf->Element(i);\n\n\t\tif (tag == \"quad4\") el.SetType(FE_QUAD4G4);\n\t\telse if (tag == \"tri3\") el.SetType(feb->m_ntri3);\n\t\telse if (tag == \"tri6\") el.SetType(feb->m_ntri6);\n\t\telse if (tag == \"tri7\") el.SetType(feb->m_ntri7);\n\t\telse if (tag == \"tri10\") el.SetType(feb->m_ntri10);\n\t\telse if (tag == \"quad8\") el.SetType(FE_QUAD8G9);\n\t\telse if (tag == \"quad9\") el.SetType(FE_QUAD9G9);\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\tN = el.Nodes();\n\t\ttag.value(nf, N);\n\t\tfor (int j = 0; j<N; ++j) el.m_node[j] = nf[j] - 1;\n\n\t\t++tag;\n\t}\n\n\tps->SetSurface(psurf);\n\n\t// add it to the model\n\tGetBuilder()->AddSurfaceLoad(ps);\n}\n\n//=============================================================================\n// FEBioLoadsSection2\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection2::Parse(XMLTag& tag)\n{\n\t// make sure this tag has children\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"nodal_load\"  ) ParseNodalLoad(tag);\n\t\telse if (tag == \"surface_load\") ParseSurfaceLoad(tag);\n\t\telse if (tag == \"edge_load\"   ) ParseEdgeLoad(tag);\n\t\telse if (tag == \"body_load\"   ) ParseBodyLoad(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection2::ParseBodyLoad(XMLTag& tag)\n{\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEModel& fem = *GetFEModel();\n\tFEBodyLoad* pbl = fecore_new<FEBodyLoad>(sztype, &fem);\n\tif (pbl == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\tReadParameterList(tag, pbl);\n\tfem.AddModelLoad(pbl);\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection2::ParseNodalLoad(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// count how many nodal forces there are\n\tint ncnf = tag.children();\n\n\t// get the bc\n\tconst char* sz = tag.AttributeValue(\"bc\");\n\tint bc = dofs.GetDOF(sz);\n\tif (bc == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\n\t// get the load curve\n\tsz = tag.AttributeValue(\"lc\");\n\tint lc = atoi(sz) - 1;\n\n\t// see if there is a set defined\n\tconst char* szset = tag.AttributeValue(\"set\", true);\n\tif (szset)\n\t{\n\t\t// make sure this is a leaf tag\n\t\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t// find the node set\n\t\tFENodeSet* pns = mesh.FindNodeSet(szset);\n\t\tif (pns == 0) throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\n\t\t// see if the scale attribute is defined\n\t\tdouble scale = 1.0;\n\t\ttag.AttributeValue(\"scale\", scale, true);\n\n\t\t// create new nodal force\n\t\tFENodalDOFLoad* pfc = fecore_alloc(FENodalDOFLoad, &fem);\n\t\tpfc->SetDOF(bc);\n\t\tpfc->SetLoad(scale);\n\t\tpfc->SetNodeSet(pns);\n\n\t\tif (lc >= 0)\n\t\t{\n\t\t\tFEParam* p = pfc->GetParameter(\"scale\");\n\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\tfem.AttachLoadController(p, lc);\n\t\t}\n\n\t\t// add it to the model\n\t\tGetBuilder()->AddNodalLoad(pfc);\n\t}\n\telse\n\t{\n\t\t// NOTE: no longer supported!\n\t\tthrow XMLReader::InvalidTag(tag);\n/*\n\t\t// read the prescribed data\n\t\t++tag;\n\t\tfor (int i = 0; i<ncnf; ++i)\n\t\t{\n\t\t\t// get the nodal ID\n\t\t\tint n = atoi(tag.AttributeValue(\"id\")) - 1;\n\n\t\t\t// get the load scale factor\n\t\t\tdouble scale = 0.0;\n\t\t\tvalue(tag, scale);\n\n\t\t\t// create new nodal force\n\t\t\tFENodalDOFLoad* pfc = fecore_alloc(FENodalDOFLoad, &fem);\n\t\t\tpfc->SetDOF(bc);\n\t\t\tpfc->SetLoad(scale);\n\t\t\tpfc->AddNode(n);\n\n\t\t\tif (lc >= 0)\n\t\t\t{\n\t\t\t\tFEParam* p = pfc->GetParameter(\"scale\");\n\t\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\t\tfem.AttachLoadController(p, lc);\n\t\t\t}\n\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->AddNodalLoad(pfc);\n\n\t\t\t++tag;\n\t\t}\n*/\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection2::ParseSurfaceLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// create surface load\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFESurfaceLoad* psl = fecore_new<FESurfaceLoad>(sztype, &fem);\n\tif (psl == 0) throw XMLReader::InvalidTag(tag);\n\n\t// read name attribute\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) psl->SetName(szname);\n\n\t// create a new surface\n\tFESurface* psurf = fecore_alloc(FESurface, &fem);\n\tfem.GetMesh().AddSurface(psurf);\n\n\t// we need to find the surface tag first\n\tParseSurfaceLoadSurface(tag, psurf);\n\tpsl->SetSurface(psurf);\n\n\t// read the parameters\n\tFEParameterList& pl = psl->GetParameterList();\n\n\t// read the pressure data\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false)\n\t\t{\n\t\t\tif (tag == \"surface\")\n\t\t\t{\n\t\t\t\t// skip it, we already processed it\n\t\t\t\ttag.m_preader->SkipTag(tag);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\t++tag;\n\t} \n\twhile (!tag.isend());\n\n\t// add it to the model\n\tGetBuilder()->AddSurfaceLoad(psl);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection2::ParseSurfaceLoadSurface(XMLTag& tag, FESurface* psurf)\n{\n\tFEModelBuilder* feb = GetBuilder();\n\tXMLTag tag2(tag);\n\t++tag2;\n\tdo\n\t{\n\t\tif (tag2 == \"surface\")\n\t\t{\n\t\t\t// see if the surface is referenced by a set of defined explicitly\n\t\t\tconst char* szset = tag2.AttributeValue(\"set\", true);\n\t\t\tif (szset)\n\t\t\t{\n\t\t\t\t// make sure this tag does not have any children\n\t\t\t\tif (!tag2.isleaf()) throw XMLReader::InvalidTag(tag2);\n\n\t\t\t\t// see if we can find the facet set\n\t\t\t\tFEMesh& m = GetFEModel()->GetMesh();\n\t\t\t\tFEFacetSet* ps = m.FindFacetSet(szset);\n\n\t\t\t\t// create a surface from the facet set\n\t\t\t\tif (ps)\n\t\t\t\t{\n\t\t\t\t\tif (feb->BuildSurface(*psurf, *ps) == false) throw XMLReader::InvalidTag(tag2);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag2, \"set\", szset);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// count how many pressure cards there are\n\t\t\t\tint npr = tag2.children();\n\t\t\t\tpsurf->Create(npr);\n\n\t\t\t\t// read surface\n\t\t\t\t++tag2;\n\t\t\t\tint nf[FEElement::MAX_NODES], N;\n\t\t\t\tfor (int i = 0; i<npr; ++i)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& el = psurf->Element(i);\n\n\t\t\t\t\tif      (tag2 == \"quad4\") el.SetType(FE_QUAD4G4);\n\t\t\t\t\telse if (tag2 == \"tri3\" ) el.SetType(feb->m_ntri3);\n\t\t\t\t\telse if (tag2 == \"tri6\" ) el.SetType(feb->m_ntri6);\n\t\t\t\t\telse if (tag2 == \"tri7\" ) el.SetType(feb->m_ntri7);\n\t\t\t\t\telse if (tag2 == \"tri10\") el.SetType(feb->m_ntri10);\n\t\t\t\t\telse if (tag2 == \"quad8\") el.SetType(FE_QUAD8G9);\n\t\t\t\t\telse if (tag2 == \"quad9\") el.SetType(FE_QUAD9G9);\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag2);\n\n\t\t\t\t\tN = el.Nodes();\n\t\t\t\t\ttag2.value(nf, N);\n\t\t\t\t\tfor (int j = 0; j<N; ++j) el.m_node[j] = nf[j] - 1;\n\n\t\t\t\t\t++tag2;\n\t\t\t\t}\n\n\t\t\t\tpsurf->CreateMaterialPointData();\n\t\t\t}\n\t\t}\n\t\telse tag2.skip();\n\t\t++tag2;\n\t}\n\twhile (!tag2.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection2::ParseEdgeLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// create edge load\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEEdgeLoad* pel = fecore_new<FEEdgeLoad>(sztype, &fem);\n\tif (pel == 0) throw XMLReader::InvalidTag(tag);\n\n\t// create a new edge\n\tFEEdge* pedge = new FEEdge(&fem);\n\tfem.GetMesh().AddEdge(pedge);\n\tpel->SetEdge(pedge);\n\n\t// read the parameters\n\tFEParameterList& pl = pel->GetParameterList();\n\n\t// read the pressure data\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl) == false)\n\t\t{\n\t\t\tif (tag == \"edge\")\n\t\t\t{\n\t\t\t\t// see if the surface is referenced by a set of defined explicitly\n\t\t\t\tconst char* szset = tag.AttributeValue(\"set\", true);\n\t\t\t\tif (szset)\n\t\t\t\t{\n\t\t\t\t\t// make sure this tag does not have any children\n\t\t\t\t\tif (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t// see if we can find the facet set\n\t\t\t\t\tFEMesh& m = GetFEModel()->GetMesh();\n\t\t\t\t\tFESegmentSet* ps = m.FindSegmentSet(szset);\n\n\t\t\t\t\t// create an edge from the segment set\n\t\t\t\t\tif (ps)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (GetBuilder()->BuildEdge(*pedge, *ps) == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"set\", szset);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// count how many load cards there are\n\t\t\t\t\tint npr = tag.children();\n\t\t\t\t\tpedge->Create(npr);\n\n\t\t\t\t\t++tag;\n\t\t\t\t\tint nf[FEElement::MAX_NODES], N;\n\t\t\t\t\tfor (int i = 0; i<npr; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFELineElement& el = pedge->Element(i);\n\n\t\t\t\t\t\tif (tag == \"line2\") el.SetType(FE_LINE2G1);\n\t\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t\tN = el.Nodes();\n\t\t\t\t\t\ttag.value(nf, N);\n\t\t\t\t\t\tfor (int j = 0; j<N; ++j) el.m_node[j] = nf[j] - 1;\n\n\t\t\t\t\t\t++tag;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// add edge load to model\n\tGetBuilder()->AddEdgeLoad(pel);\n}\n\n//=============================================================================\n// FEBioLoadsSection25\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection25::Parse(XMLTag& tag)\n{\n\t// make sure this tag has children\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"nodal_load\"  ) ParseNodalLoad  (tag);\n\t\telse if (tag == \"surface_load\") ParseSurfaceLoad(tag);\n\t\telse if (tag == \"edge_load\"   ) ParseEdgeLoad   (tag);\n\t\telse if (tag == \"body_load\"   ) ParseBodyLoad   (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection25::ParseBodyLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEBodyLoad* pbl = fecore_new<FEBodyLoad>(sztype, &fem);\n\tif (pbl == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// see if a name was defined\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) pbl->SetName(szname);\n\n\t// see if a specific domain was referenced\n\tconst char* szpart = tag.AttributeValue(\"elem_set\", true);\n\tif (szpart)\n\t{\n\t\tFEElementSet* elset = mesh.FindElementSet(szpart);\n\t\tif (elset == 0) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szpart);\n\n\t\tpbl->SetDomainList(elset);\n\t}\n\n\tReadParameterList(tag, pbl);\n\tfem.AddModelLoad(pbl);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection25::ParseNodalLoad(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& dofs = fem.GetDOFS();\n\n\t// get the bc\n\tconst char* sz = tag.AttributeValue(\"bc\");\n\tint bc = dofs.GetDOF(sz);\n\tif (bc == -1) throw XMLReader::InvalidAttributeValue(tag, \"bc\", sz);\n\n\t// get the node set\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nodeSet = mesh.FindNodeSet(szset);\n\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// create nodal load\n\tFENodalDOFLoad* pfc = fecore_alloc(FENodalDOFLoad, &fem);\n\tpfc->SetDOF(bc);\n\tpfc->SetNodeSet(nodeSet);\n\n\t// add it to the model\n\tGetBuilder()->AddNodalLoad(pfc);\n\n\t// read parameters\n\tReadParameterList(tag, pfc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection25::ParseSurfaceLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create surface load\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFESurfaceLoad* psl = fecore_new<FESurfaceLoad>(sztype, &fem);\n\tif (psl == 0)\n\t{\n\t\t// there are some obsolete loads that have been moved elsewhere,\n\t\t// so let's check for those first;\n\t\tParseObsoleteLoad(tag);\n\t}\n\telse\n\t{\n\n\t\t// read name attribute\n\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\tif (szname) psl->SetName(szname);\n\n\t\t// get the surface\n\t\tconst char* szset = tag.AttributeValue(\"surface\");\n\t\tFEFacetSet* pface = mesh.FindFacetSet(szset);\n\t\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szset);\n\n\t\tFESurface* psurf = fecore_alloc(FESurface, &fem);\n\t\tGetBuilder()->BuildSurface(*psurf, *pface);\n\n\t\tmesh.AddSurface(psurf);\n\t\tpsl->SetSurface(psurf);\n\n\t\t// read the parameters\n\t\tif (!tag.isleaf())\n\t\t{\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (ReadParameter(tag, psl) == false)\n\t\t\t\t{\n\t\t\t\t\tif ((tag == \"value\") && (strcmp(psl->GetTypeStr(), \"pressure\") == 0))\n\t\t\t\t\t{\n\t\t\t\t\t\tfeLogWarningEx((&fem), \"The value parameter of the pressure load is deprecated.\");\n\n\t\t\t\t\t\tFEParam* pp = psl->GetParameter(\"pressure\"); assert(pp);\n\t\t\t\t\t\tFEParamDouble& val = pp->value<FEParamDouble>();\n\n\t\t\t\t\t\t// NOTE: This will only work if the pressure was set to 1!!\n\t\t\t\t\t\tconst char* szsurfdata = tag.AttributeValue(\"surface_data\", true);\n\t\t\t\t\t\tif (szsurfdata)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tGetBuilder()->AddMappedParameter(pp, psl, szsurfdata);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble v;\n\t\t\t\t\t\t\ttag.value(v);\n\t\t\t\t\t\t\tval = v;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\n\t\t// add it to the model\n\t\tGetBuilder()->AddSurfaceLoad(psl);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection25::ParseEdgeLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create edge load\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEEdgeLoad* pel = fecore_new<FEEdgeLoad>(sztype, &fem);\n\tif (pel == 0) throw XMLReader::InvalidTag(tag);\n\n\t// create a new edge\n\tFEEdge* pedge = new FEEdge(&fem);\n\tmesh.AddEdge(pedge);\n\tpel->SetEdge(pedge);\n\n\t// get the segment set\n\tconst char* szedge = tag.AttributeValue(\"edge\");\n\tFESegmentSet* pset = mesh.FindSegmentSet(szedge);\n\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"edge\", szedge);\n\tif (GetBuilder()->BuildEdge(*pedge, *pset) == false) throw XMLReader::InvalidTag(tag);\n\n\t// read the parameters\n\tReadParameterList(tag, pel);\n\n\t// add edge load to model\n\tGetBuilder()->AddEdgeLoad(pel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection25::ParseObsoleteLoad(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t// this \"load\" was moved to the boundary section\n\tif (strcmp(sztype, \"fluid rotational velocity\") == 0)\n\t{\n\t\tFEPrescribedNodeSet* pbc = fecore_new<FEPrescribedNodeSet>(sztype, fem);\n\t\tif (pbc == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t// get the surface\n\t\tconst char* szset = tag.AttributeValue(\"surface\");\n\t\tFEFacetSet* pface = mesh.FindFacetSet(szset);\n\t\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szset);\n\n\t\t// extract the nodeset from the surface\n\t\tFENodeList nodeList = pface->GetNodeList();\n\t\tFENodeSet* nodeSet = new FENodeSet(fem);\n\t\tnodeSet->SetName(pface->GetName());\n\t\tnodeSet->Add(nodeList);\n\t\tmesh.AddNodeSet(nodeSet);\n\n\t\t// assign the node set\n\t\tpbc->SetNodeSet(nodeSet);\n\n\t\t// Read the parameter list\n\t\tReadParameterList(tag, pbc);\n\n\t\t// Add the boundary condition\n\t\tGetBuilder()->AddBC(pbc);\n\t}\n\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioLoadsSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Version 1.2\nclass FEBioLoadsSection1x : public FEFileSection\n{\npublic:\n\tFEBioLoadsSection1x(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalLoad  (XMLTag& tag);\n\tvoid ParseBodyForce  (XMLTag& tag);\n\tvoid ParseBodyLoad   (XMLTag& tag);\n\tvoid ParseSurfaceLoad(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Version 2.0\nclass FEBioLoadsSection2 : public FEFileSection\n{\npublic:\n\tFEBioLoadsSection2(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalLoad  (XMLTag& tag);\n\tvoid ParseBodyLoad   (XMLTag& tag);\n\tvoid ParseEdgeLoad   (XMLTag& tag);\n\tvoid ParseSurfaceLoad(XMLTag& tag);\n\tvoid ParseSurfaceLoadSurface(XMLTag& tag, FESurface* psurf);\n};\n\n//-----------------------------------------------------------------------------\n// Version 2.5\nclass FEBioLoadsSection25 : public FEFileSection\n{\npublic:\n\tFEBioLoadsSection25(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalLoad  (XMLTag& tag);\n\tvoid ParseEdgeLoad   (XMLTag& tag);\n\tvoid ParseSurfaceLoad(XMLTag& tag);\n\tvoid ParseBodyLoad   (XMLTag& tag);\n\nprotected:\n\tvoid ParseObsoleteLoad(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Version 3.0\nclass FEBioLoadsSection3 : public FEFileSection\n{\npublic:\n\tFEBioLoadsSection3(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalLoad  (XMLTag& tag);\n\tvoid ParseEdgeLoad   (XMLTag& tag);\n\tvoid ParseSurfaceLoad(XMLTag& tag);\n\tvoid ParseBodyLoad   (XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Version 4.0\nclass FEBioLoadsSection4 : public FEFileSection\n{\npublic:\n\tFEBioLoadsSection4(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalLoad(XMLTag& tag);\n\tvoid ParseEdgeLoad(XMLTag& tag);\n\tvoid ParseSurfaceLoad(XMLTag& tag);\n\tvoid ParseBodyLoad(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioLoadsSection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioLoadsSection.h\"\n#include <FECore/FEBodyLoad.h>\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEEdgeLoad.h>\n#include <FECore/FEEdge.h>\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection3::Parse(XMLTag& tag)\n{\n\t// make sure this tag has children\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"nodal_load\"  ) ParseNodalLoad  (tag);\n\t\telse if (tag == \"surface_load\") ParseSurfaceLoad(tag);\n\t\telse if (tag == \"edge_load\"   ) ParseEdgeLoad   (tag);\n\t\telse if (tag == \"body_load\"   ) ParseBodyLoad   (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection3::ParseBodyLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEBodyLoad* pbl = fecore_new<FEBodyLoad>(sztype, &fem);\n\tif (pbl == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// see if a name was defined\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) pbl->SetName(szname);\n\n\t// see if a specific domain was referenced\n\tconst char* szpart = tag.AttributeValue(\"elem_set\", true);\n\tif (szpart)\n\t{\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tFEElementSet* elset = mesh.FindElementSet(szpart);\n\t\tif (elset == 0) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szpart);\n\t\tpbl->SetDomainList(elset);\n\t}\n\n\tReadParameterList(tag, pbl);\n\tGetBuilder()->AddModelLoad(pbl);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection3::ParseNodalLoad(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t// create nodal load\n\tFENodalLoad* pfc = fecore_new<FENodalLoad>(sztype, GetFEModel());\n\tif (pfc == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\");\n\n\t// read (optional) name attribute\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) pfc->SetName(szname);\n\n\t// get the node set\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nodeSet = GetBuilder()->FindNodeSet(szset);\n\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// assign the node set\n\tpfc->SetNodeSet(nodeSet);\n\n\t// add it to the model\n\tGetBuilder()->AddNodalLoad(pfc);\n\n\t// read parameters\n\tReadParameterList(tag, pfc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection3::ParseSurfaceLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create surface load\n\tstring typeString = tag.AttributeValue(\"type\");\n\tFESurfaceLoad* psl = fecore_new<FESurfaceLoad>(typeString.c_str(), &fem);\n\tif (psl == 0) throw XMLReader::InvalidTag(tag);\n\n\t// read (optional) name attribute\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) psl->SetName(szname);\n\n\t// read required surface attribute\n\tconst char* surfaceName = tag.AttributeValue(\"surface\");\n\tFEFacetSet* pface = mesh.FindFacetSet(surfaceName);\n\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", surfaceName);\n\n\t// create a surface from this facet set\n\tFESurface* psurf = fecore_alloc(FESurface, &fem);\n\tGetBuilder()->BuildSurface(*psurf, *pface);\n\n\t// assign it\n\tmesh.AddSurface(psurf);\n\tpsl->SetSurface(psurf);\n\n\t// add it to the model\n\tGetBuilder()->AddSurfaceLoad(psl);\n\n\t// read the parameters\n\tif (!tag.isleaf())\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (ReadParameter(tag, psl) == false)\n\t\t\t{\n\t\t\t\t// some special handling for fluidnormalvelocity\n\t\t\t\tif ((tag == \"value\") && (strcmp(typeString.c_str(), \"fluid normal velocity\") == 0))\n\t\t\t\t{\n\t\t\t\t\t// The value parameter no longer exists, but for backward compatibility\n\t\t\t\t\t// we map it to the \"velocity\" parameter.\n\n\t\t\t\t\t// get the surface data attribute\n\t\t\t\t\tconst char* szsurfdata = tag.AttributeValue(\"surface_data\");\n\n\t\t\t\t\t// find the surface map\n\t\t\t\t\tFEDataMap* surfMap = mesh.FindDataMap(szsurfdata);\n\t\t\t\t\tif (surfMap == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"surface_data\");\n\n\t\t\t\t\t// get the velocity parameter\n\t\t\t\t\tFEParam* p = psl->GetParameter(\"velocity\");\n\t\t\t\t\tFEParamDouble& v = p->value<FEParamDouble>();\n\n\t\t\t\t\t// we'll map the velocity value to the map's scale factor\n\t\t\t\t\tdouble s = 1.0;\n\t\t\t\t\tif (v.isConst()) s = v.constValue(); else throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t// create map and assign\n\t\t\t\t\tFEMappedValue* map = new FEMappedValue(&fem);\n\t\t\t\t\tmap->setDataMap(surfMap);\n\t\t\t\t\tmap->setScaleFactor(s);\n\t\t\t\t\tv.setValuator(map);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t\t++tag;\n\t\t} \n\t\twhile (!tag.isend());\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioLoadsSection3::ParseEdgeLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create edge load\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEEdgeLoad* pel = fecore_new<FEEdgeLoad>(sztype, &fem);\n\tif (pel == 0) throw XMLReader::InvalidTag(tag);\n\n\t// create a new edge\n\tFEEdge* pedge = new FEEdge(&fem);\n\tmesh.AddEdge(pedge);\n\tpel->SetEdge(pedge);\n\n\t// get the segment set\n\tconst char* szedge = tag.AttributeValue(\"edge\");\n\tFESegmentSet* pset = mesh.FindSegmentSet(szedge);\n\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"edge\", szedge);\n\tif (GetBuilder()->BuildEdge(*pedge, *pset) == false) throw XMLReader::InvalidTag(tag);\n\n\t// add edge load to model\n\tGetBuilder()->AddEdgeLoad(pel);\n\n\t// read the parameters\n\tReadParameterList(tag, pel);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioLoadsSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioLoadsSection.h\"\n#include <FECore/FEBodyLoad.h>\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEEdgeLoad.h>\n#include <FECore/FEEdge.h>\n\nvoid FEBioLoadsSection4::Parse(XMLTag& tag)\n{\n\t// make sure this tag has children\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"nodal_load\"  ) ParseNodalLoad  (tag);\n\t\telse if (tag == \"surface_load\") ParseSurfaceLoad(tag);\n\t\telse if (tag == \"edge_load\"   ) ParseEdgeLoad   (tag);\n\t\telse if (tag == \"body_load\"   ) ParseBodyLoad   (tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\nvoid FEBioLoadsSection4::ParseBodyLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n    FEMesh& mesh = fem.GetMesh();\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEBodyLoad* pbl = fecore_new<FEBodyLoad>(sztype, &fem);\n\tif (pbl == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// see if a name was defined\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) pbl->SetName(szname);\n\n\t// see if a specific domain was referenced\n\tconst char* szpart = tag.AttributeValue(\"elem_set\", true);\n\tif (szpart)\n\t{\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tFEElementSet* elset = mesh.FindElementSet(szpart);\n\t\tif (elset == 0) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szpart);\n\t\tpbl->SetDomainList(elset);\n\t}\n\n\tReadParameterList(tag, pbl);\n\tGetBuilder()->AddModelLoad(pbl);\n}\n\nvoid FEBioLoadsSection4::ParseNodalLoad(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t// create nodal load\n\tFENodalLoad* pfc = fecore_new<FENodalLoad>(sztype, GetFEModel());\n\tif (pfc == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\");\n\n\t// read (optional) name attribute\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) pfc->SetName(szname);\n\n\t// get the node set\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nodeSet = GetBuilder()->FindNodeSet(szset);\n\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// assign the node set\n\tpfc->SetNodeSet(nodeSet);\n\n\t// add it to the model\n\tGetBuilder()->AddNodalLoad(pfc);\n\n\t// read parameters\n\tReadParameterList(tag, pfc);\n}\n\nvoid FEBioLoadsSection4::ParseSurfaceLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create surface load\n\tstring typeString = tag.AttributeValue(\"type\");\n\tFESurfaceLoad* psl = fecore_new<FESurfaceLoad>(typeString.c_str(), &fem);\n\tif (psl == 0) throw XMLReader::InvalidTag(tag);\n\n\t// read (optional) name attribute\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) psl->SetName(szname);\n\n\t// read required surface attribute\n\tconst char* surfaceName = tag.AttributeValue(\"surface\");\n\tFEFacetSet* pface = mesh.FindFacetSet(surfaceName);\n\tif (pface == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", surfaceName);\n\n\t// create a surface from this facet set\n\tFESurface* psurf = fecore_alloc(FESurface, &fem);\n\tGetBuilder()->BuildSurface(*psurf, *pface);\n\n\t// assign it\n\tmesh.AddSurface(psurf);\n\tpsl->SetSurface(psurf);\n\n\t// add it to the model\n\tGetBuilder()->AddSurfaceLoad(psl);\n\n\t// read the parameters\n\tif (!tag.isleaf())\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (ReadParameter(tag, psl) == false)\n\t\t\t{\n\t\t\t\t// some special handling for fluidnormalvelocity\n\t\t\t\tif ((tag == \"value\") && (strcmp(typeString.c_str(), \"fluid normal velocity\") == 0))\n\t\t\t\t{\n\t\t\t\t\t// The value parameter no longer exists, but for backward compatibility\n\t\t\t\t\t// we map it to the \"velocity\" parameter.\n\n\t\t\t\t\t// get the surface data attribute\n\t\t\t\t\tconst char* szsurfdata = tag.AttributeValue(\"surface_data\");\n\n\t\t\t\t\t// find the surface map\n\t\t\t\t\tFEDataMap* surfMap = mesh.FindDataMap(szsurfdata);\n\t\t\t\t\tif (surfMap == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"surface_data\");\n\n\t\t\t\t\t// get the velocity parameter\n\t\t\t\t\tFEParam* p = psl->GetParameter(\"velocity\");\n\t\t\t\t\tFEParamDouble& v = p->value<FEParamDouble>();\n\n\t\t\t\t\t// we'll map the velocity value to the map's scale factor\n\t\t\t\t\tdouble s = 1.0;\n\t\t\t\t\tif (v.isConst()) s = v.constValue(); else throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t// create map and assign\n\t\t\t\t\tFEMappedValue* map = new FEMappedValue(&fem);\n\t\t\t\t\tmap->setDataMap(surfMap);\n\t\t\t\t\tmap->setScaleFactor(s);\n\t\t\t\t\tv.setValuator(map);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t\t++tag;\n\t\t} \n\t\twhile (!tag.isend());\n\t}\n}\n\nvoid FEBioLoadsSection4::ParseEdgeLoad(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// create edge load\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEEdgeLoad* pel = fecore_new<FEEdgeLoad>(sztype, &fem);\n\tif (pel == 0) throw XMLReader::InvalidTag(tag);\n\n\t// create a new edge\n\tFEEdge* pedge = new FEEdge(&fem);\n\tmesh.AddEdge(pedge);\n\tpel->SetEdge(pedge);\n\n\t// get the segment set\n\tconst char* szedge = tag.AttributeValue(\"edge\");\n\tFESegmentSet* pset = mesh.FindSegmentSet(szedge);\n\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"edge\", szedge);\n\tif (GetBuilder()->BuildEdge(*pedge, *pset) == false) throw XMLReader::InvalidTag(tag);\n\n\t// add edge load to model\n\tGetBuilder()->AddEdgeLoad(pel);\n\n\t// read the parameters\n\tReadParameterList(tag, pel);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMaterialSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMaterialSection.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/FEMaterial.h\"\n#include <FECore/log.h>\n#include <sstream>\n\n//-----------------------------------------------------------------------------\n//! This function creates a material by checking the type attribute against\n//! registered materials. Also, if the tag defines attributes (other than\n//! type and name), the material is offered a chance to process the attributes.\nFEMaterial* FEBioMaterialSection::CreateMaterial(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// get the material type\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\n\t// in some case, a type is not defined (e.g. for solutes)\n\t// in that case, we use the tag name as the type\n\tif (sztype == 0) sztype = tag.Name();\n\n\t// create a new material of this type\n\tFEMaterial* pmat = GetBuilder()->CreateMaterial(sztype);\n\tif (pmat == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\treturn pmat;\n}\n\n//-----------------------------------------------------------------------------\n//! Parse the Materials section. \nvoid FEBioMaterialSection::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// Make sure no materials are defined\n\tif (fem.Materials() != 0) throw FEBioImport::DuplicateMaterialSection();\n\n\t// reset material counter\n\tm_nmat = 0;\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"material\")\n\t\t{\n\t\t\t// check that the ID attribute is defined and that it \n\t\t\t// equals the number of materials + 1.\n\t\t\tint nid = -1;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\t\t\tint nmat = fem.Materials();\n\t\t\tif (nid != nmat+1) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t\t// make sure that the name is unique\n\t\t\tstd::string name;\n\t\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\t\tif (szname == nullptr)\n\t\t\t{\n\t\t\t\tstringstream ss;\n\t\t\t\tss << \"Material\" << nid;\n\t\t\t\tname = ss.str();\n\n\t\t\t\tfeLogWarningEx((&fem), \"Material %d has no name.\\nIt was given the name %s.\", nid, name.c_str());\n\t\t\t}\n\t\t\telse name = szname;\n\n\t\t\tFEMaterial* mat = fem.FindMaterial(name);\n\t\t\tif (mat)\n\t\t\t{\n\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"name\");\n\t\t\t}\n\n\t\t\t// create a material from this tag\n\t\t\tFEMaterial* pmat = CreateMaterial(tag); assert(pmat);\n\t\t\tpmat->SetName(name);\n\n\t\t\t// set the material's ID\n\t\t\t++m_nmat;\n\t\t\tpmat->SetID(m_nmat);\n\n\t\t\t// parse the material parameters\n\t\t\tReadParameterList(tag, pmat);\n\n\t\t\t// add the material\n\t\t\tGetBuilder()->AddMaterial(pmat);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// read next tag\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//===============================================================================\n\n//-----------------------------------------------------------------------------\n//! This function creates a material by checking the type attribute against\n//! registered materials. Also, if the tag defines attributes (other than\n//! type and name), the material is offered a chance to process the attributes.\nFEMaterial* FEBioMaterialSection3::CreateMaterial(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// get the material type\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t// in some case, a type is not defined (e.g. for solutes)\n\t// in that case, we use the tag name as the type\n\tif (sztype == 0) sztype = tag.Name();\n\n\t// create a new material of this type\n\tFEMaterial* pmat = GetBuilder()->CreateMaterial(sztype);\n\tif (pmat == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\treturn pmat;\n}\n\n//-----------------------------------------------------------------------------\n//! Parse the Materials section. \nvoid FEBioMaterialSection3::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"material\")\n\t\t{\n\t\t\t// check that the ID attribute is defined and that it \n\t\t\t// equals the number of materials + 1.\n\t\t\tint nid = -1;\n\t\t\ttag.AttributeValue(\"id\", nid);\n\t\t\tint nmat = fem.Materials();\n\t\t\tif (nid != nmat + 1) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t\t// make sure that the name is unique\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tFEMaterial* mat = fem.FindMaterial(szname);\n\t\t\tif (mat)\n\t\t\t{\n\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"name\");\n\t\t\t}\n\n\t\t\t// create a material from this tag\n\t\t\tFEMaterial* pmat = CreateMaterial(tag); assert(pmat);\n\t\t\tpmat->SetName(szname);\n\n\t\t\t// add the material\n\t\t\tfem.AddMaterial(pmat);\n\n\t\t\t// set the material's ID\n\t\t\tpmat->SetID(nid);\n\n\t\t\t// parse the material parameters\n\t\t\tReadParameterList(tag, pmat);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// read next tag\n\t\t++tag;\n\t} while (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMaterialSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Material Section\nclass FEBIOXML_API FEBioMaterialSection : public FEFileSection\n{\npublic:\n\tFEBioMaterialSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tFEMaterial* CreateMaterial(XMLTag& tag);\n\nprotected:\n\tint\tm_nmat;\n};\n\n//-----------------------------------------------------------------------------\n// Material Section\nclass FEBIOXML_API FEBioMaterialSection3 : public FEFileSection\n{\npublic:\n\tFEBioMaterialSection3(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tFEMaterial* CreateMaterial(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioMeshAdaptorSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMeshAdaptorSection.h\"\n#include <FECore/FEMeshAdaptor.h>\n\nvoid FEBioMeshAdaptorSection::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"mesh_adaptor\")\n\t\t{\n\t\t\tParseMeshAdaptor(tag);\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\nvoid FEBioMeshAdaptorSection::ParseMeshAdaptor(XMLTag& tag)\n{\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tFEModel* fem = GetFEModel();\n\n\tFEMeshAdaptor* meshAdaptor = fecore_new<FEMeshAdaptor>(sztype, fem);\n\tif (meshAdaptor == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t// get the optional element set\n\tconst char* set = tag.AttributeValue(\"elem_set\", true);\n\tif (set)\n\t{\n\t\tFEMesh& mesh = fem->GetMesh();\n\t\tFEElementSet* elemSet = mesh.FindElementSet(set);\n\t\tif (elemSet == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", set);\n\n\t\tmeshAdaptor->SetElementSet(elemSet);\n\t}\n\n\tfem->AddMeshAdaptor(meshAdaptor);\n\tGetBuilder()->AddComponent(meshAdaptor);\n\n\tReadParameterList(tag, meshAdaptor);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshAdaptorSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\nclass FEBioMeshAdaptorSection : public FEFileSection\n{\npublic:\n\tFEBioMeshAdaptorSection(FEFileImport* pim) : FEFileSection(pim) {}\n\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseMeshAdaptor(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioMeshDataSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMeshDataSection.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FEDataGenerator.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEDataMathGenerator.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEModelParam.h>\n#include <FECore/FEDomainMap.h>\n#include <FECore/FEConstValueVec3.h>\n#include <sstream>\n\n// in FEBioMeshDataSection3.cpp\nFEDataType str2datatype(const char* szdataType);\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\n#define szcmp    _stricmp\n#else\n#define szcmp    strcmp\n#endif\n\nvoid FEBioMeshDataSection::Parse(XMLTag& tag)\n{\n\t// Make sure there is something in this tag\n\tif (tag.isleaf()) return;\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the total nr of elements\n\tint nelems = mesh.Elements();\n\n\t//make sure we've read the element section\n\tif (nelems == 0) throw XMLReader::InvalidTag(tag);\n\n\t// get the largest ID\n\tint max_id = 0;\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& d = mesh.Domain(i);\n\t\tfor (int j = 0; j<d.Elements(); ++j)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(j);\n\t\t\tif (el.GetID() > max_id) max_id = el.GetID();\n\t\t}\n\t}\n\n\t// create the pelem array\n\tm_pelem.assign(max_id, static_cast<FEElement*>(0));\n\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& d = mesh.Domain(nd);\n\t\tfor (int i = 0; i<d.Elements(); ++i)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(i);\n\t\t\tassert(m_pelem[el.GetID() - 1] == 0);\n\t\t\tm_pelem[el.GetID() - 1] = &el;\n\t\t}\n\t}\n\n\tFEBioImport* feb = GetFEBioImport();\n\n\t// loop over all mesh data\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"ElementData\") ParseElementData(tag);\n\t\telse if (tag == \"SurfaceData\") ParseSurfaceData(tag);\n\t\telse if (tag == \"EdgeData\"   ) ParseEdgeData(tag);\n\t\telse if (tag == \"NodeData\"   ) ParseNodeData(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEBioMeshDataSection::ParseNodeData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nodeSet = mesh.FindNodeSet(szset);\n\tif (nodeSet == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\tconst char* sztype = tag.AttributeValue(\"data_type\", true);\n\tif (sztype == 0) sztype = \"scalar\";\n\n\tFEDataType dataType;\n\tif (strcmp(sztype, \"scalar\") == 0) dataType = FE_DOUBLE;\n\telse if (strcmp(sztype, \"vec2\") == 0) dataType = FE_VEC2D;\n\telse if (strcmp(sztype, \"vec3\") == 0) dataType = FE_VEC3D;\n\telse throw XMLReader::InvalidAttributeValue(tag, \"data_type\", sztype);\n\n\tstring sname = tag.AttributeValue(\"name\");\n\n\tFENodeDataMap* pdata = nullptr;\n\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen)\n\t{\n\t\tif (dataType != FE_DOUBLE) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"math\")\n\t\t\t{\n\t\t\t\tFEDataMathGenerator gen(&fem);\n\t\t\t\tgen.setExpression(tag.szvalue());\n\t\t\t\tif (gen.Init() == false)  throw XMLReader::InvalidValue(tag);\n\n\t\t\t\tpdata = dynamic_cast<FENodeDataMap*>(gen.Generate());\n\t\t\t\tif (pdata == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\telse\n\t{\n\t\tpdata = new FENodeDataMap(dataType);\n\t\tpdata->Create(nodeSet);\n\t\tParseDataArray(tag, *pdata, \"node\");\n\t}\n\n\tif (pdata)\n\t{\n\t\tpdata->SetName(sname);\n\t\tmesh.AddDataMap(pdata);\n\t}\n}\n\nvoid FEBioMeshDataSection::ParseEdgeData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tconst char* szedge = tag.AttributeValue(\"edge\");\n\tFESegmentSet* pset = mesh.FindSegmentSet(szedge);\n\tif (pset == 0) throw XMLReader::InvalidAttributeValue(tag, \"edge\", szedge);\n\n\tconst char* sztype = tag.AttributeValue(\"data_type\", true);\n\tif (sztype == 0) sztype = \"scalar\";\n\n\tint dataType = -1;\n\tif (strcmp(sztype, \"scalar\") == 0) dataType = FE_DOUBLE;\n\telse if (strcmp(sztype, \"vec2\") == 0) dataType = FE_VEC2D;\n\telse if (strcmp(sztype, \"vec3\") == 0) dataType = FE_VEC3D;\n\tif (dataType == -1) throw XMLReader::InvalidAttributeValue(tag, \"data_type\", sztype);\n\t/*\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEDataArray* pdata = new FEDataArray(dataType);\n\tfem.AddDataArray(szname, pdata);\n\n\tpdata->resize(pset->Segments());\n\tParseDataArray(tag, *pdata, \"edge\");\n\t*/\n}\n\nvoid FEBioMeshDataSection::ParseSurfaceData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tconst char* szsurf = tag.AttributeValue(\"surface\");\n\tFEFacetSet* psurf = mesh.FindFacetSet(szsurf);\n\tif (psurf == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szsurf);\n\n\tconst char* sztype = tag.AttributeValue(\"data_type\", true);\n\tif (sztype == 0) sztype = \"scalar\";\n\n\tFEDataType dataType;\n\tif      (strcmp(sztype, \"scalar\") == 0) dataType = FE_DOUBLE;\n\telse if (strcmp(sztype, \"vec2\"  ) == 0) dataType = FE_VEC2D;\n\telse if (strcmp(sztype, \"vec3\"  ) == 0) dataType = FE_VEC3D;\n\telse throw XMLReader::InvalidAttributeValue(tag, \"data_type\", sztype);\n\n\tstring sname = tag.AttributeValue(\"name\");\n\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen)\n\t{\n\t\tFEFaceDataGenerator* gen = fecore_new<FEFaceDataGenerator>(szgen, &fem);\n\t\tif (gen == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\tReadParameterList(tag, gen);\n\n\t\tFEDataMap* pdata = gen->Generate();\n\t\tpdata->SetName(sname);\n\t\tmesh.AddDataMap(pdata);\n\t}\n\telse\n\t{\n\t\tFESurfaceMap* pdata = new FESurfaceMap(dataType);\n\t\tpdata->SetName(sname);\n\t\tpdata->Create(psurf);\n\n\t\tmesh.AddDataMap(pdata);\n\n\t\tParseDataArray(tag, *pdata, \"face\");\n\t}\n}\n\nvoid FEBioMeshDataSection::ParseElementData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tconst char* szset = tag.AttributeValue(\"elem_set\");\n\tFEElementSet* part = mesh.FindElementSet(szset);\n\tif (part == 0) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szset);\n\n\t// see if the data will be generated or tabulated\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen == nullptr)\n\t{\n\t\t// data is tabulated and mapped directly to a variable.\n\t\tconst char* szvar = tag.AttributeValue(\"var\", true);\n\t\tif (szvar)\n\t\t{\n\t\t\tif      (strcmp(szvar, \"shell thickness\") == 0) ParseShellThickness(tag, *part);\n\t\t\telse if (strcmp(szvar, \"fiber\"          ) == 0) ParseMaterialFibers(tag, *part);\n\t\t\telse if (strstr(szvar, \".fiber\"         ) != 0) ParseMaterialFiberProperty(tag, *part);\n\t\t\telse if (strcmp(szvar, \"mat_axis\"       ) == 0) ParseElementMaterialAxes(tag, *part);\n\t\t\telse if (strstr(szvar, \".mat_axis\"      ) != 0) ParseMaterialAxesProperty(tag, *part);\n\t\t\telse ParseMaterialData(tag, *part, szvar);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tstring name = szname;\n\n\t\t\tconst char* szdatatype = tag.AttributeValue(\"datatype\");\n\t\t\tFEDataType dataType = str2datatype(szdatatype);\n\t\t\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"datatype\", szdatatype);\n\n\t\t\t// default format\n\t\t\tStorage_Fmt fmt = (((dataType == FE_MAT3D) || (dataType == FE_MAT3DS)) ? Storage_Fmt::FMT_ITEM : Storage_Fmt::FMT_MULT);\n\n\t\t\t// format overrider?\n\t\t\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\t\t\tif (szfmt)\n\t\t\t{\n\t\t\t\tif (szcmp(szfmt, \"MAT_POINTS\") == 0) fmt = Storage_Fmt::FMT_MATPOINTS;\n\t\t\t\telse if (szcmp(szfmt, \"ITEM\") == 0) fmt = Storage_Fmt::FMT_ITEM;\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\t\t\t}\n\n\t\t\tFEDomainMap* map = new FEDomainMap(dataType, fmt);\n\t\t\tmap->Create(part);\n\n\t\t\t// parse the data\n\t\t\tParseElementData(tag, *map);\n\n\t\t\t// see if this map already exsits \n\t\t\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh.FindDataMap(name));\n\t\t\tif (oldMap)\n\t\t\t{\n\t\t\t\toldMap->Merge(*map);\n\t\t\t\tdelete map;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmap->SetName(name);\n\t\t\t\tmesh.AddDataMap(map);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// data will be generated\n\t\tFEElemDataGenerator* gen = fecore_new<FEElemDataGenerator>(szgen, &fem);\n\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t// get the variable or name\n\t\tstring mapName;\n\t\tconst char* szvar = tag.AttributeValue(\"var\", true);\n\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\tif (szvar) mapName = szvar;\n\t\telse if (szname) mapName = szname;\n\t\telse { assert(false); }\n\n\t\t// make sure the parameter is valid\n\t\tFEParamDouble* pp = nullptr;\n\t\tif (szvar)\n\t\t{\n\t\t\t// find the variable\n\t\t\tParamString paramName(szvar);\n\t\t\tFEParam* pv = fem.FindParameter(paramName);\n\t\t\tif (pv == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"var\", szvar);\n\n\t\t\t// make sure it's a mapped parameter\n\t\t\tif (pv->type() != FE_PARAM_DOUBLE_MAPPED)throw XMLReader::InvalidAttributeValue(tag, \"var\", szvar);\n\n\t\t\t// if it's an array parameter, get the right index\n\t\t\tif (pv->dim() > 1)\n\t\t\t{\n\t\t\t\tParamString l = paramName.last();\n\t\t\t\tint m = l.Index();\n\t\t\t\tassert(m >= 0);\n\t\t\t\tpp = &(pv->value<FEParamDouble>(m));\n\t\t\t}\n\t\t\telse pp = &(pv->value<FEParamDouble>());\n\t\t}\n\n\t\t// create a new domain map (only scalars for now!)\n\t\tFEDomainMap* map = new FEDomainMap(FE_DOUBLE);\n\t\tmap->Create(part);\n\t\tmap->SetName(mapName);\n\n\t\t// read the parameters of the generator\n\t\tReadParameterList(tag, gen);\n\t\tgen->SetElementSet(part);\n\n\t\t// Add it to the list (will be evaluated later)\n\t\tGetBuilder()->AddMeshDataGenerator(gen, map, pp);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseShellThickness(XMLTag& tag, FEElementSet& set)\n{\n\tif (tag.isleaf())\n\t{\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tdouble h[FEElement::MAX_NODES];\n\t\tint nval = tag.value(h, FEElement::MAX_NODES);\n\n\t\tfor (int i = 0; i<set.Elements(); ++i)\n\t\t{\n\t\t\tFEShellElement* pel = dynamic_cast<FEShellElement*>(m_pelem[set[i] - 1]);\n\t\t\tif (pel == 0) throw XMLReader::InvalidValue(tag);\n\n\t\t\tif (pel->Nodes() != nval) throw XMLReader::InvalidValue(tag);\n\t\t\tfor (int j = 0; j<nval; ++j) pel->m_h0[j] = h[j];\n\t\t}\n\t}\n\telse\n\t{\n\t\tvector<ELEMENT_DATA> data;\n\t\tParseElementData(tag, set, data, FEElement::MAX_NODES);\n\t\tfor (int i = 0; i<(int)data.size(); ++i)\n\t\t{\n\t\t\tELEMENT_DATA& di = data[i];\n\t\t\tif (di.nval > 0)\n\t\t\t{\n\t\t\t\tFEElement& el = *m_pelem[set[i] - 1];\n\n\t\t\t\tif (el.Class() != FE_ELEM_SHELL) throw XMLReader::InvalidTag(tag);\n\t\t\t\tFEShellElement& shell = static_cast<FEShellElement&> (el);\n\n\t\t\t\tint ne = shell.Nodes();\n\t\t\t\tif (ne != di.nval) throw XMLReader::InvalidTag(tag);\n\t\t\t\tfor (int j = 0; j<ne; ++j) shell.m_h0[j] = di.val[j];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Defined in FEBioGeometrySection.cpp\nvoid set_element_fiber(FEElement& el, const vec3d& v, int ncomp);\nvoid set_element_mat_axis(FEElement& el, const vec3d& v1, const vec3d& v2, int ncomp);\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseMaterialFibers(XMLTag& tag, FEElementSet& set)\n{\n\t// find the domain with the same name\n\tstring name = set.GetName();\n\n\tFEMesh* mesh = const_cast<FEMesh*>(set.GetMesh());\n\tFEDomain* dom = mesh->FindDomain(name);\n\tif (dom == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// get the material\n\tFEMaterial* mat = dom->GetMaterial();\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// get the fiber property\n\tFEProperty* fiber = mat->FindProperty(\"fiber\");\n\tif (fiber == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\tif (fiber->GetSuperClassID() != FEVEC3DVALUATOR_ID) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// create a domain map\n\tFEDomainMap* map = new FEDomainMap(FE_VEC3D, FMT_ITEM);\n\tmap->Create(&set);\n\tFEMappedValueVec3* val = fecore_new<FEMappedValueVec3>(\"map\", GetFEModel());\n\tval->setDataMap(map);\n\tfiber->SetProperty(val);\n\n\tvector<ELEMENT_DATA> data;\n\tParseElementData(tag, set, data, 3);\n\tfor (int i = 0; i<(int)data.size(); ++i)\n\t{\n\t\tELEMENT_DATA& di = data[i];\n\t\tif (di.nval > 0)\n\t\t{\n\t\t\tFEElement& el = *m_pelem[set[i] - 1];\n\n\t\t\tif (di.nval != 3) throw XMLReader::InvalidTag(tag);\n\t\t\tvec3d v(di.val[0], di.val[1], di.val[2]);\n\t\t\tv.unit();\n\t\t\tmap->set<vec3d>(i, v);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseMaterialFiberProperty(XMLTag& tag, FEElementSet& set)\n{\n\tconst char* szvar = tag.AttributeValue(\"var\");\n\tchar szbuf[256] = { 0 };\n\tstrcpy(szbuf, szvar);\n\tchar* ch = strstr(szbuf, \".fiber\");\n\tif (ch == 0) return;\n\t*ch = 0;\n\tch = strrchr(szbuf, ']');\n\tif (ch == 0) return;\n\t*ch = 0;\n\tch = strchr(szbuf, '[');\n\tif (ch == 0) return;\n\t*ch++ = 0;\n\tint nindex = atoi(ch);\n\n\tvector<ELEMENT_DATA> data;\n\tParseElementData(tag, set, data, 3);\n\tfor (int i = 0; i<(int)data.size(); ++i)\n\t{\n\t\tELEMENT_DATA& di = data[i];\n\t\tif (di.nval > 0)\n\t\t{\n\t\t\tFEElement& el = *m_pelem[set[i] - 1];\n\n\t\t\tif (di.nval != 3) throw XMLReader::InvalidTag(tag);\n\t\t\tvec3d v(di.val[0], di.val[1], di.val[2]);\n\n\t\t\tset_element_fiber(el, v, nindex);\n\t\t}\n\t}\n}\n\nvoid FEBioMeshDataSection::ParseElementMaterialAxes(XMLTag& tag, FEElementSet& set)\n{\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the domain\n\tstring domName = set.GetName();\n\tFEDomainList& DL = set.GetDomainList();\n\tif (DL.Domains() != 1)\n\t{\n\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_set\", domName.c_str());\n\t}\n\tFEDomain* dom = DL.GetDomain(0);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"elem\")\n\t\t{\n\t\t\t// get the local element number\n\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\tint lid = atoi(szlid) - 1;\n\n\t\t\t// make sure the number is valid\n\t\t\tif ((lid < 0) || (lid >= set.Elements())) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t// get the element\n\t\t\tFEElement* el = mesh.FindElementFromID(set[lid]);\n\t\t\tif (el == 0) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t// read parameters\n\t\t\tdouble a[3] = { 0 };\n\t\t\tdouble d[3] = { 0 };\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"a\") tag.value(a, 3);\n\t\t\t\telse if (tag == \"d\") tag.value(d, 3);\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\n\t\t\tvec3d v1(a[0], a[1], a[2]);\n\t\t\tvec3d v2(d[0], d[1], d[2]);\n\n\t\t\tvec3d e1(v1);\n\t\t\tvec3d e3 = v1 ^ v2;\n\t\t\tvec3d e2 = e3 ^ e1;\n\n\t\t\t// normalize\n\t\t\te1.unit();\n\t\t\te2.unit();\n\t\t\te3.unit();\n\n\t\t\t// set the value\n\t\t\tmat3d Q(e1, e2, e3);\n\n\t\t\tfor (int i = 0; i < el->GaussPoints(); ++i)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el->GetMaterialPoint(i);\n\t\t\t\tmp.m_Q = Q;\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEBioMeshDataSection::ParseMaterialAxesProperty(XMLTag& tag, FEElementSet& set)\n{\n\tconst char* szvar = tag.AttributeValue(\"var\");\n\tchar szbuf[256] = { 0 };\n\tstrcpy(szbuf, szvar);\n\tchar* ch = strstr(szbuf, \".mat_axis\");\n\tif (ch) *ch = 0;\n\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the domain\n\tstring domName = set.GetName();\n\tFEDomainList& DL = set.GetDomainList();\n\tif (DL.Domains() != 1)\n\t{\n\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_set\", domName.c_str());\n\t}\n\tFEDomain* dom = DL.GetDomain(0);\n\n\t// get its material\n\tFEMaterial* domMat = dom->GetMaterial();\n\n\t// find the material property \n\tFEMaterial* mat = domMat;\n\tif (ch)\n\t{\n\t\tmat = dynamic_cast<FEMaterial*>(domMat->GetProperty(szbuf));\n\t\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"var\", szvar);\n\t}\n\n\tFEProperty* pQ = mat->FindProperty(\"mat_axis\"); assert(pQ);\n\tif (pQ == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"var\", szvar);\n\n\t// create the map's name.\n\tstringstream ss;\n\tif (ch)\n\t\tss << \"material\" << domMat->GetID() << \".\" << szbuf << \".mat_axis\";\n\telse\n\t\tss << \"material\" << domMat->GetID() << \".mat_axis\";\n\n\tstring mapName = ss.str();\n\n\t// create a domain map\n\tFEDomainMap* map = new FEDomainMap(FE_MAT3D, FMT_ITEM);\n\tmap->SetName(mapName);\n\tmap->Create(&set);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"elem\")\n\t\t{\n\t\t\t// get the local element number\n\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\tint lid = atoi(szlid) - 1;\n\n\t\t\t// make sure the number is valid\n\t\t\tif ((lid < 0) || (lid >= set.Elements())) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t// get the element\n\t\t\tFEElement* el = mesh.FindElementFromID(set[lid]);\n\t\t\tif (el == 0) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t// read parameters\n\t\t\tdouble a[3] = { 0 };\n\t\t\tdouble d[3] = { 0 };\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"a\") tag.value(a, 3);\n\t\t\t\telse if (tag == \"d\") tag.value(d, 3);\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\n\t\t\tvec3d v1(a[0], a[1], a[2]);\n\t\t\tvec3d v2(d[0], d[1], d[2]);\n\n\t\t\tvec3d e1(v1);\n\t\t\tvec3d e3 = v1 ^ v2;\n\t\t\tvec3d e2 = e3 ^ e1;\n\n\t\t\t// normalize\n\t\t\te1.unit();\n\t\t\te2.unit();\n\t\t\te3.unit();\n\n\t\t\t// set the value\n\t\t\tmat3d Q(e1, e2, e3);\n\t\t\tmap->set<mat3d>(lid, Q);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// see if this map already exists\n\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh.FindDataMap(mapName));\n\tif (oldMap)\n\t{\n\t\t// It does, so merge it\n\t\toldMap->Merge(*map);\n\t\tdelete map;\n\t}\n\telse\n\t{\n\t\t// It does not, so add it\n\t\tFEMappedValueMat3d* val = fecore_alloc(FEMappedValueMat3d, GetFEModel());\n\t\tval->setDataMap(map);\n\t\tpQ->SetProperty(val);\n\t\tmesh.AddDataMap(map);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseMaterialData(XMLTag& tag, FEElementSet& set, const string& pname)\n{\n\t// get the (optional) scale factor\n\tdouble scale = 1.0;\n\tconst char* szscale = tag.AttributeValue(\"scale\", true);\n\tif (szscale) scale = atof(szscale);\n\n\t// find the parameter\n\tFEModel& fem = *GetFEModel();\n\tParamString PS(pname.c_str());\n\tFEParam* p = fem.FindParameter(PS);\n\tif (p == nullptr)\n\t{\n\t\tprintf(\"Can't find parameter %s\\n\", pname.c_str());\n\t\tthrow XMLReader::InvalidAttributeValue(tag, \"var\", pname.c_str());\n\t}\n\n\t// get the format\n\tStorage_Fmt fmt = FMT_ITEM;\n\tconst char* szfmt = tag.AttributeValue(\"data_format\", true);\n\tif (szfmt == nullptr) fmt = FMT_ITEM;\n\telse if (strcmp(szfmt, \"node\") == 0) fmt = FMT_NODE;\n\telse if (strcmp(szfmt, \"item\") == 0) fmt = FMT_ITEM;\n\telse if (strcmp(szfmt, \"mult\") == 0) fmt = FMT_MULT;\n\telse if (strcmp(szfmt, \"region\") == 0) fmt = FMT_REGION;\n\telse throw XMLReader::InvalidAttributeValue(tag, \"data_format\", szfmt);\n\n\tif (p->type() == FE_PARAM_DOUBLE_MAPPED)\n\t{\n\t\tvector<ELEMENT_DATA> data;\n\t\tParseElementData(tag, set, data, 1);\n\n\t\tFEParamDouble& param = p->value<FEParamDouble>();\n\t\tparam.SetItemList(&set);\n\n\t\tFEMappedValue* val = fecore_alloc(FEMappedValue, &fem);\n\t\tif (val == nullptr)\n\t\t{\n\t\t\tprintf(\"Something went horribly wrong.\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (fmt != FMT_ITEM) throw XMLReader::InvalidAttributeValue(tag, \"data_format\", szfmt);\n\n\t\tFEDomainMap* map = new FEDomainMap(FEDataType::FE_DOUBLE, FMT_ITEM);\n\t\tmap->Create(&set);\n\t\tval->setDataMap(map);\n\n\t\tfor (int i = 0; i < data.size(); ++i) map->set<double>(i, data[i].val[0] * scale);\n\n\t\tparam.setValuator(val);\n\t}\n\telse if (p->type() == FE_PARAM_MAT3DS_MAPPED)\n\t{\n\t\tFEParamMat3ds& param = p->value<FEParamMat3ds>();\n\t\tparam.SetItemList(&set);\n\n\t\tFEMappedValueMat3ds* val = fecore_alloc(FEMappedValueMat3ds, &fem);\n\t\tif (val == nullptr)\n\t\t{\n\t\t\tprintf(\"Something went horribly wrong.\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (fmt == FMT_ITEM)\n\t\t{\n\t\t\tvector<ELEMENT_DATA> data;\n\t\t\tParseElementData(tag, set, data, 6);\n\n\t\t\tFEDomainMap* map = new FEDomainMap(FEDataType::FE_MAT3DS, FMT_ITEM);\n\t\t\tmap->Create(&set);\n\t\t\tval->setDataMap(map);\n\n\t\t\tfor (int i = 0; i < data.size(); ++i)\n\t\t\t{\n\t\t\t\tdouble* d = data[i].val;\n\t\t\t\tmat3ds m(d[0], d[1], d[2], d[3], d[4], d[5]);\n\t\t\t\tmap->set<mat3ds>(i, m * scale);\n\t\t\t}\n\n\t\t\tparam.setValuator(val);\n\t\t}\n\t\telse if (fmt == FMT_NODE)\n\t\t{\n\t\t\t// create a node set from an element set\n\t\t\tFENodeList nodeList = set.GetNodeList();\n\n\t\t\t// create domain map\n\t\t\tFEDomainMap* map = new FEDomainMap(FEDataType::FE_MAT3DS, FMT_NODE);\n\t\t\tmap->Create(&set);\n\t\t\tval->setDataMap(map);\n\n\t\t\t// read values\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"node\")\n\t\t\t\t{\n\t\t\t\t\tconst char* szid = tag.AttributeValue(\"id\");\n\t\t\t\t\tint nid = atoi(szid) - 1;\n\n\t\t\t\t\t// convert global ID into local one\n\t\t\t\t\tint lid = nodeList.GlobalToLocalID(nid);\n\t\t\t\t\tif (lid < 0) throw XMLReader::InvalidAttributeValue(tag, \"id\", szid);\n\n\t\t\t\t\t// read the value\n\t\t\t\t\tdouble d[6];\n\t\t\t\t\ttag.value(d, 6);\n\t\t\t\t\tmat3ds m(d[0], d[1], d[2], d[3], d[4], d[5]);\n\t\t\t\t\tmap->set<mat3ds>(lid, m*scale);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} \n\t\t\twhile (!tag.isend());\n\n\t\t\tparam.setValuator(val);\n\t\t}\n\t}\n\telse\n\t{\n\t\tprintf(\"A mesh data map cannot be assigned to this parameter.\");\n\t\tthrow XMLReader::InvalidAttribute(tag, \"var\");\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseElementData(XMLTag& tag, FEElementSet& set, vector<ELEMENT_DATA>& values, int nvalues)\n{\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set.Elements();\n\n\t// resize the array\n\tvalues.resize(nelems);\n\tfor (int i = 0; i<nelems; ++i) values[i].nval = 0;\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"elem\")\n\t\t{\n\t\t\t// get the local element number\n\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\tint n = atoi(szlid) - 1;\n\n\t\t\t// make sure the number is valid\n\t\t\tif ((n<0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\tELEMENT_DATA& data = values[n];\n\t\t\tdata.nval = tag.value(data.val, nvalues);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseElementData(XMLTag& tag, FEDomainMap& map)\n{\n\tconst FEElementSet* set = map.GetElementSet();\n\tif (set == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set->Elements();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tint m = map.MaxNodes();\n\tdouble data[3 * FEElement::MAX_NODES]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t// TODO: For vec3d values, I sometimes need to normalize the vectors (e.g. for fibers). How can I do this?\n\n\tint ncount = 0;\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, m*dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tdouble* v = data;\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, v[0]); break;\n\t\t\tcase FE_VEC2D:\tmap.setValue(n, vec2d(v[0], v[1])); break;\n\t\t\tcase FE_VEC3D:\tmap.setValue(n, vec3d(v[0], v[1], v[2])); break;\n\t\t\tcase FE_MAT3D:  map.setValue(n, mat3d(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8])); break;\n            case FE_MAT3DS: map.setValue(n, mat3ds(v[0], v[1], v[2], v[3], v[4], v[5])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse if (nread == m * dataSize)\n\t\t{\n\t\t\tdouble* v = data;\n\t\t\tfor (int i = 0; i < m; ++i, v += dataSize)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FE_DOUBLE:\tmap.setValue(n, i, v[0]); break;\n\t\t\t\tcase FE_VEC2D:\tmap.setValue(n, i, vec2d(v[0], v[1])); break;\n\t\t\t\tcase FE_VEC3D:\tmap.setValue(n, i, vec3d(v[0], v[1], v[2])); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\n\t\tncount++;\n\t} while (!tag.isend());\n\n\tif (ncount != nelems) throw FEBioImport::MeshDataError();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection::ParseDataArray(XMLTag& tag, FEDataArray& map, const char* sztag)\n{\n    int dataType = map.DataType();\n\n\tif (dataType == FE_DOUBLE)\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == sztag)\n\t\t\t{\n\t\t\t\tint nid;\n\t\t\t\ttag.AttributeValue(\"lid\", nid);\n\n\t\t\t\tdouble v;\n\t\t\t\ttag.value(v);\n\n\t\t\t\tmap.setValue(nid - 1, v);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\telse if (dataType == FE_VEC3D)\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == sztag)\n\t\t\t{\n\t\t\t\tint nid;\n\t\t\t\ttag.AttributeValue(\"lid\", nid);\n\n\t\t\t\tdouble v[3];\n\t\t\t\ttag.value(v, 3);\n\n\t\t\t\tmap.setValue(nid - 1, vec3d(v[0], v[1], v[2]));\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshDataSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\nclass FEDomainMap;\nclass FESurfaceMap;\nclass FENodeDataMap;\n\n//-----------------------------------------------------------------------------\n// MeshData Section (introduced in febio_spec 2.5)\nclass FEBioMeshDataSection : public FEBioFileSection\n{\n\tstruct ELEMENT_DATA\n\t{\n\t\tint\t\tnval;\t// number of values read\n\t\tdouble\tval[FEElement::MAX_NODES];\t// scalar value\n\t};\n\npublic:\n\tFEBioMeshDataSection(FEBioImport* pim) : FEBioFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseShellThickness(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialFibers(XMLTag& tag, FEElementSet& set);\n\tvoid ParseElementMaterialAxes(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialAxesProperty(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialData  (XMLTag& tag, FEElementSet& set, const string& name);\n\tvoid ParseMaterialFiberProperty(XMLTag& tag, FEElementSet& set);\n\nprivate:\n\tvoid ParseNodeData(XMLTag& tag);\n\tvoid ParseEdgeData(XMLTag& tag);\n\tvoid ParseSurfaceData(XMLTag& tag);\n\tvoid ParseElementData(XMLTag& tag);\n\n\tvoid ParseElementData(XMLTag& tag, FEElementSet& set, vector<ELEMENT_DATA>& values, int nvalues);\n\tvoid ParseElementData(XMLTag& tag, FEDomainMap& map);\n\tvoid ParseDataArray(XMLTag& tag, FEDataArray& map, const char* sztag);\n\tFEElement* ParseElement(XMLTag& tag, FEElementSet& set);\n\nprivate:\n\tvector<FEElement*> m_pelem;\n};\n\n//-----------------------------------------------------------------------------\n// MeshData Section for febio_spec 3.0\nclass FEBioMeshDataSection3 : public FEBioFileSection\n{\n\tstruct ELEMENT_DATA\n\t{\n\t\tint\t\tnval;\t// number of values read\n\t\tdouble\tval[FEElement::MAX_NODES];\t// scalar value\n\t};\n\npublic:\n\tFEBioMeshDataSection3(FEBioImport* pim) : FEBioFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalData(XMLTag& tag);\n\tvoid ParseSurfaceData(XMLTag& tag);\n\tvoid ParseElementData(XMLTag& tag);\n\nprotected:\n//\tvoid ParseModelParameter(XMLTag& tag, FEParamValue param);\n//\tvoid ParseMaterialPointData(XMLTag& tag, FEParamValue param);\n\nprotected:\n\tvoid ParseShellThickness(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialFibers(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialAxes(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialAxesProperty(XMLTag& tag, FEElementSet& set);\n\nprivate:\n\tvoid ParseElementData(XMLTag& tag, FEElementSet& set, vector<ELEMENT_DATA>& values, int nvalues);\n\tvoid ParseElementData(XMLTag& tag, FEDomainMap& map);\n\tvoid ParseSurfaceData(XMLTag& tag, FESurfaceMap& map);\n\tvoid ParseNodeData   (XMLTag& tag, FENodeDataMap& map);\n};\n\n//-----------------------------------------------------------------------------\n// MeshData Section for febio_spec 4.0\nclass FEBioMeshDataSection4: public FEBioFileSection\n{\n\tstruct ELEMENT_DATA\n\t{\n\t\tint\t\tnval;\t// number of values read\n\t\tdouble\tval[FEElement::MAX_NODES];\t// scalar value\n\t};\n\npublic:\n\tFEBioMeshDataSection4(FEBioImport* pim) : FEBioFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodalData(XMLTag& tag);\n\tvoid ParseSurfaceData(XMLTag& tag);\n\tvoid ParseElementData(XMLTag& tag);\n\nprivate:\n\tvoid ParseNodeData(XMLTag& tag, FENodeDataMap& map);\n\tvoid ParseSurfaceData(XMLTag& tag, FESurfaceMap& map);\n\tvoid ParseElementData(XMLTag& tag, FEElementSet& set, vector<ELEMENT_DATA>& values, int nvalues);\n\tvoid ParseElementData(XMLTag& tag, FEDomainMap& map);\n\tvoid ParseShellThickness(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialAxes(XMLTag& tag, FEElementSet& set);\n\tvoid ParseMaterialFibers(XMLTag& tag, FEElementSet& set);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioMeshDataSection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMeshDataSection.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FEDataGenerator.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEDataMathGenerator.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEModelParam.h>\n#include <FECore/FEDomainMap.h>\n#include <FECore/FEConstValueVec3.h>\n#include <sstream>\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\n#define szcmp    _stricmp\n#else\n#define szcmp    strcmp\n#endif\n\n//-----------------------------------------------------------------------------\n// helper function for converting a datatype attribute to FEDataType\nFEDataType str2datatype(const char* szdataType)\n{\n\tif (szdataType == nullptr) return FEDataType::FE_DOUBLE;\n\tFEDataType dataType = FEDataType::FE_INVALID_TYPE;\n\tif      (strcmp(szdataType, \"scalar\") == 0) dataType = FEDataType::FE_DOUBLE;\n\telse if (strcmp(szdataType, \"vec2\"  ) == 0) dataType = FEDataType::FE_VEC2D;\n\telse if (strcmp(szdataType, \"vec3\"  ) == 0) dataType = FEDataType::FE_VEC3D;\n\telse if (strcmp(szdataType, \"mat3\"  ) == 0) dataType = FEDataType::FE_MAT3D;\n    else if (strcmp(szdataType, \"mat3s\" ) == 0) dataType = FEDataType::FE_MAT3DS;\n\treturn dataType;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::Parse(XMLTag& tag)\n{\n\t// Make sure there is something in this tag\n\tif (tag.isleaf()) return;\n\n\t// make sure the MeshDomain section was processed. \n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tif (mesh.Domains() == 0)\n\t{\n\t\tthrow FEFileException(\"MeshData must appear after MeshDomain section.\");\n\t}\n\n\t// loop over all mesh data section\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"NodeData\"   ) ParseNodalData  (tag);\n\t\telse if (tag == \"SurfaceData\") ParseSurfaceData(tag);\n\t\telse if (tag == \"ElementData\") ParseElementData(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseNodalData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the node set\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\tFENodeSet* nset = GetBuilder()->FindNodeSet(szset);\n\tif (nset == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// get the data type\n\tconst char* szdataType = tag.AttributeValue(\"datatype\", true);\n\tFEDataType dataType = str2datatype(szdataType);\n\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"datatype\", szdataType);\n\n\t// get the name (required!)\n\tstring sname = tag.AttributeValue(\"name\");\n\n\tFENodeDataMap* map = nullptr;\n\n\t// see if there is a generator\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen)\n\t{\n\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t{\n\t\t\tmap = new FENodeDataMap(dataType);\n\n\t\t\t++tag;\n\t\t\tdo {\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tswitch (dataType)\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_DOUBLE: { double v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC2D : { vec2d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC3D : { vec3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3D : { mat3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3DS: { mat3ds v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"type\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tdelete map;\n\t\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse \n\t\t{\n\t\t\tFENodeDataGenerator* gen = nullptr;\n\t\t\tgen = fecore_new<FENodeDataGenerator>(szgen, &fem);\n\t\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t\tgen->SetNodeSet(nset);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// initialize the generator\n\t\t\tif (gen->Init() == false) throw FEBioImport::DataGeneratorError();\n\n\t\t\t// generate the data\n\t\t\tmap = dynamic_cast<FENodeDataMap*>(gen->Generate());\n\t\t\tif (map == nullptr) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t}\n\telse\n\t{\n\t\t// create the data map\n\t\tmap = new FENodeDataMap(dataType);\n\t\tmap->Create(nset);\n\n\t\t// read the data\n\t\tParseNodeData(tag, *map);\n\t}\n\n\t// add it to the mesh\n\tif (map)\n\t{\n\t\tmap->SetName(sname);\n\t\tmesh.AddDataMap(map);\n\t}\n}\n\nvoid FEBioMeshDataSection3::ParseSurfaceData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the surface in the mesh\n\tconst char* szset = tag.AttributeValue(\"surface\");\n\tFEFacetSet* surf = mesh.FindFacetSet(szset);\n\tif (surf == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szset);\n\n\t// get the data type\n\tconst char* szdataType = tag.AttributeValue(\"datatype\", true);\n\tif (szdataType == nullptr) szdataType = \"scalar\";\n\tFEDataType dataType = str2datatype(szdataType);\n\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"datatype\", szdataType);\n\n\t// get the name (required!)\n\tstring sname = tag.AttributeValue(\"name\");\n\n\tFESurfaceMap* map = nullptr;\n\n\t// see if there is a generator\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen)\n\t{\n\t\t// treat const separately\n\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t{\n\t\t\tmap = new FESurfaceMap(dataType);\n\t\t\tmap->Create(surf);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tswitch (dataType)\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_DOUBLE: { double v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC2D : { vec3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC3D : { vec3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3D : { mat3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3DS: { mat3ds v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"type\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEFaceDataGenerator* gen = fecore_new<FEFaceDataGenerator>(szgen, &fem);\n\t\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// initialize the generator\n\t\t\tif (gen->Init() == false) throw FEBioImport::DataGeneratorError();\n\n\t\t\t// generate the data\n\t\t\tmap = dynamic_cast<FESurfaceMap*>(gen->Generate());\n\t\t\tif (map == nullptr) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t}\n\telse\n\t{\n\t\tmap = new FESurfaceMap(dataType);\n\t\tmap->Create(surf);\n\t\tParseSurfaceData(tag, *map);\n\t}\n\n\tif (map)\n\t{\n\t\tmap->SetName(sname);\n\t\tmesh.AddDataMap(map);\n\t}\n}\n\nvoid FEBioMeshDataSection3::ParseElementData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the element set in the mesh\n\tconst char* szset = tag.AttributeValue(\"elem_set\");\n\tFEElementSet* elset = mesh.FindElementSet(szset);\n\tif (elset == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szset);\n\n\t// get the data type\n\tconst char* szdataType = tag.AttributeValue(\"datatype\", true);\n\tif (szdataType == nullptr) szdataType = \"scalar\";\n\tFEDataType dataType = str2datatype(szdataType);\n\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"datatype\", szdataType);\n\n\t// see if a generator is defined\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\n\t// get the name or var (required!)\n\tconst char* szvar = tag.AttributeValue(\"var\", true);\n\tconst char* szname = (szvar == nullptr ? tag.AttributeValue(\"name\") : nullptr);\n\n\tstring sname = (szname ? szname : \"\");\n\n\tbool isVar = (szvar != nullptr);\n\tstring mapName = (isVar ? szvar : szname);\n\n\t// process some special variables\n\tif ((szname == nullptr) && (szgen == nullptr))\n\t{\n\t\tif      (strcmp(szvar, \"shell thickness\") == 0) ParseShellThickness(tag, *elset);\n\t\telse if (strcmp(szvar, \"fiber\"          ) == 0) ParseMaterialFibers(tag, *elset);\n\t\telse if (strcmp(szvar, \"mat_axis\"       ) == 0) ParseMaterialAxes(tag, *elset);\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"var\");\n\t\treturn;\n\t}\n\n\t// default format\n\tStorage_Fmt fmt = (((dataType == FE_MAT3D) || (dataType == FE_MAT3DS)) ? Storage_Fmt::FMT_ITEM : Storage_Fmt::FMT_MULT);\n\n\t// format overrider?\n\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\tif (szfmt)\n\t{\n\t\tif (szcmp(szfmt, \"MAT_POINTS\") == 0) fmt = Storage_Fmt::FMT_MATPOINTS;\n\t\telse if (szcmp(szfmt, \"ITEM\") == 0) fmt = Storage_Fmt::FMT_ITEM;\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\t}\n\n\t// see if there is a generator\n\tif (szgen)\n\t{\n\t\t// make sure the parameter is valid\n\t\tFEParamDouble* pp = nullptr;\n\t\tif (isVar)\n\t\t{\n\t\t\t// find the variable\n\t\t\tParamString paramName(szvar);\n\t\t\tFEParam* pv = fem.FindParameter(paramName);\n\t\t\tif (pv == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"var\", szvar);\n\n\t\t\t// make sure it's a mapped parameter\n\t\t\tif (pv->type() != FE_PARAM_DOUBLE_MAPPED)throw XMLReader::InvalidAttributeValue(tag, \"var\", szvar);\n\n\t\t\t// if it's an array parameter, get the right index\n\t\t\tif (pv->dim() > 1)\n\t\t\t{\n\t\t\t\tParamString l = paramName.last();\n\t\t\t\tint m = l.Index();\n\t\t\t\tassert(m >= 0);\n\t\t\t\tpp = &(pv->value<FEParamDouble>(m));\n\t\t\t}\n\t\t\telse pp = &(pv->value<FEParamDouble>());\n\t\t}\n\n\t\tFEElemDataGenerator* gen = nullptr;\t\t// data will be generated\n\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t{\n\t\t\tFEDomainMap* map = new FEDomainMap(dataType, fmt);\n\t\t\tmap->Create(elset);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tswitch (dataType)\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_DOUBLE: { double v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC2D : { vec2d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC3D : { vec3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3D : { mat3d  v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3DS: { mat3ds v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"type\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\n\t\t\t// see if this map already exists\n\t\t\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh.FindDataMap(sname));\n\t\t\tif (oldMap)\n\t\t\t{\n\t\t\t\toldMap->Merge(*map);\n\t\t\t\tdelete map;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmap->SetName(sname);\n\t\t\t\tmesh.AddDataMap(map);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tgen = fecore_new<FEElemDataGenerator>(szgen, &fem);\n\t\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// Add it to the list (will be applied after the rest of the model was read in)\n\t\t\tGetBuilder()->AddMeshDataGenerator(gen, nullptr, pp);\n\t\t}\n\t}\n\telse\n\t{\n\t\tFEDomainMap* map = new FEDomainMap(dataType, fmt);\n\t\tmap->Create(elset);\n\n\t\t// read the data\n\t\tParseElementData(tag, *map);\n\n\t\t// see if this map already exists\n\t\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh.FindDataMap(sname));\n\t\tif (oldMap)\n\t\t{\n\t\t\toldMap->Merge(*map);\n\t\t\tdelete map;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmap->SetName(sname);\n\t\t\tmesh.AddDataMap(map);\n\t\t}\n\t}\n}\n/*\nvoid FEBioMeshDataSection3::ParseModelParameter(XMLTag& tag, FEParamValue param)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the parameter name\n\tconst char* szparam = tag.AttributeValue(\"param\");\n\n\t// see if the data will be generated or tabulated\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\n\tFEParam* pp = param.param();\n\tif (pp->type() == FE_PARAM_MATERIALPOINT)\n\t{\n\t\tParseMaterialPointData(tag, param);\n\t\treturn;\n\t}\n\n\t// make sure it is a mapped param\n\tif ((pp->type() != FE_PARAM_DOUBLE_MAPPED) &&\n\t\t(pp->type() != FE_PARAM_VEC3D_MAPPED) && \n\t\t(pp->type() != FE_PARAM_MAT3D_MAPPED)) throw XMLReader::InvalidAttributeValue(tag, \"param\", szparam);\n\n\tFEDataType dataType = FE_DOUBLE;\n\tif (pp->type() == FE_PARAM_VEC3D_MAPPED) dataType = FE_VEC3D;\n\tif (pp->type() == FE_PARAM_MAT3D_MAPPED) dataType = FE_MAT3D;\n\n\t// get the parent\n\tFECoreBase* pc = dynamic_cast<FECoreBase*>(pp->parent());\n\tif (pc == 0) throw XMLReader::InvalidAttributeValue(tag, \"param\", szparam);\n\n\t// data generator\n\t// TODO: Make this a shared pointer so it will be deleted properly\n\tFEDataGenerator* gen = 0;\n\tif (szgen)\n\t{\n\t\t// data will be generated\n\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t{\n\t\t\tif      (dataType == FE_DOUBLE) gen = new FEConstDataGenerator<double>(&fem);\n\t\t\telse if (dataType == FE_VEC3D ) gen = new FEConstDataGenerator<vec3d>(&fem);\n\t\t\telse if (dataType == FE_MAT3D ) gen = new FEConstDataGenerator<mat3d>(&fem);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tgen = fecore_new<FEDataGenerator>(szgen, &fem);\n\t\t}\n\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t// read the parameters\n\t\tReadParameterList(tag, gen);\n\n\t\t// initialize the generator\n\t\tif (gen->Init() == false) throw FEBioImport::DataGeneratorError();\n\t}\n\n\tif (dynamic_cast<FEMaterial*>(pc))\n\t{\n\t\tFEMaterial* mat = dynamic_cast<FEMaterial*>(pc->GetAncestor());\n\t\tif (mat == 0) throw XMLReader::InvalidAttributeValue(tag, \"param\", szparam);\n\n\t\tFEDomainList& DL = mat->GetDomainList();\n\t\tFEElementSet* set = new FEElementSet(&fem);\n\t\tset->Create(DL);\n\t\tmesh.AddElementSet(set);\n\n\t\t// create a new domain map\n\t\tFEDomainMap* map = nullptr;\n\t\tswitch (dataType)\n\t\t{\n\t\tcase FE_DOUBLE: map = new FEDomainMap(dataType); break;\n\t\tcase FE_VEC3D : map = new FEDomainMap(dataType); break;\n\t\tcase FE_MAT3D : map = new FEDomainMap(dataType, FMT_ITEM); break;\n\t\t}\n\n\t\tmap->Create(set);\n\t\tmap->SetName(szparam);\n\t\tmesh.AddDataArray(szparam, map);\n\n\t\tif (gen)\n\t\t{\n\t\t\tif (gen->Generate(*map, *set) == false) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tParseElementData(tag, *map);\n\t\t}\n\n\t\tif (dataType == FE_DOUBLE)\n\t\t{\n\t\t\tFEParamDouble& p = pp->value<FEParamDouble>();\n\t\t\tif (p.isConst() == false) throw FEBioImport::DataGeneratorError();\n\t\t\tFEMappedValue* val = new FEMappedValue(&fem);\n\t\t\tval->setDataMap(map, p.constValue());\n\t\t\tp.setValuator(val);\n\t\t}\n\t\telse if (dataType == FE_VEC3D)\n\t\t{\n\t\t\tFEParamVec3& p = pp->value<FEParamVec3>();\n\t\t\tFEMappedValueVec3* val = new FEMappedValueVec3(&fem);\n\t\t\tval->setDataMap(map);\n\t\t\tp.setValuator(val);\n\t\t}\n\t\telse if (dataType == FE_MAT3D)\n\t\t{\n\t\t\tFEParamMat3d& p = pp->value<FEParamMat3d>();\n\t\t\tFEMappedValueMat3d* val = fecore_alloc(FEMappedValueMat3d, &fem);\n\t\t\tval->setDataMap(map);\n\t\t\tp.setValuator(val);\n\t\t}\n\n\t\tint a = 0;\n\t}\n\telse if (dynamic_cast<FEBodyLoad*>(pc))\n\t{\n\t\tFEBodyLoad* pbl = dynamic_cast<FEBodyLoad*>(pc);\n\n\t\tFEDomainList& DL = pbl->GetDomainList();\n\t\tFEElementSet* set = new FEElementSet(&fem);\n\t\tset->Create(DL);\n\t\tmesh.AddElementSet(set);\n\n\t\t// create a new domain map\n\t\tFEDomainMap* map = new FEDomainMap(dataType);\n\t\tmap->Create(set);\n\t\tmap->SetName(szparam);\n\t\tmesh.AddDataArray(szparam, map);\n\n\t\tif (gen)\n\t\t{\n\t\t\tif (gen->Generate(*map, *set) == false) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tParseElementData(tag, *map);\n\t\t}\n\n\t\tif (dataType == FE_DOUBLE)\n\t\t{\n\t\t\tFEParamDouble& p = pp->value<FEParamDouble>();\n\t\t\tif (p.isConst() == false) throw FEBioImport::DataGeneratorError();\n\t\t\tFEMappedValue* val = new FEMappedValue(&fem);\n\t\t\tval->setDataMap(map, p.constValue());\n\t\t\tp.setValuator(val);\n\t\t}\n\t\telse if (dataType == FE_VEC3D)\n\t\t{\n\t\t\tFEParamVec3& p = pp->value<FEParamVec3>();\n\t\t\tif (p.isConst() == false) throw FEBioImport::DataGeneratorError();\n\t\t\tFEMappedValueVec3* val = new FEMappedValueVec3(&fem);\n\t\t\tval->setDataMap(map, p.constValue());\n\t\t\tp.setValuator(val);\n\t\t}\n\t}\n\telse if (dynamic_cast<FESurfaceLoad*>(pc))\n\t{\n\t\tFESurfaceLoad* psl = dynamic_cast<FESurfaceLoad*>(pc);\n\n\t\tFESurface* set = &psl->GetSurface();\n\n\t\t// create a new domain map\n\t\tFESurfaceMap* map = new FESurfaceMap(dataType);\n\t\tmap->Create(set);\n\t\tmap->SetName(szparam);\n\t\tmesh.AddDataArray(szparam, map);\n\n\t\tif (gen)\n\t\t{\n\t\t\tif (gen->Generate(*map, *set->GetFacetSet()) == false) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tParseSurfaceData(tag, *map);\n\t\t}\n\n\t\tif (dataType == FE_DOUBLE)\n\t\t{\n\t\t\tFEParamDouble& p = pp->value<FEParamDouble>();\n\t\t\tif (p.isConst() == false) throw FEBioImport::DataGeneratorError();\n\t\t\tFEMappedValue* val = new FEMappedValue(&fem);\n\t\t\tval->setDataMap(map, p.constValue());\n\t\t\tp.setValuator(val);\n\t\t}\n\t\telse if (dataType == FE_VEC3D)\n\t\t{\n\t\t\tFEParamVec3& p = pp->value<FEParamVec3>();\n\t\t\tif (p.isConst() == false) throw FEBioImport::DataGeneratorError();\n\t\t\tFEMappedValueVec3* val = new FEMappedValueVec3(&fem);\n\t\t\tval->setDataMap(map, p.constValue());\n\t\t\tp.setValuator(val);\n\t\t}\n\t}\n\telse if (dynamic_cast<FEPrescribedDOF*>(pc))\n\t{\n\t\tFEPrescribedDOF* bc = dynamic_cast<FEPrescribedDOF*>(pc);\n\t\t// create node set\n\t\tconst FENodeSet& bc_set = *bc->GetNodeSet();\n\t\tint nsize = bc_set.Size();\n\t\tFENodeSet* set = new FENodeSet(&fem);\n\t\tfor (int i = 0; i < nsize; ++i) set->Add(bc_set[i]);\n\n\t\tFENodeDataMap* map = new FENodeDataMap(FE_DOUBLE);\n\t\tmesh.AddDataArray(szparam, map);\n\n\t\tif (gen)\n\t\t{\n\t\t\tif (gen->Generate(*map, *set) == false) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmap->Create(set->Size());\n\t\t\tParseNodeData(tag, *map);\n\t\t}\n\n\t\tif (dataType == FE_DOUBLE)\n\t\t{\n\t\t\tFEParamDouble& p = pp->value<FEParamDouble>();\n\t\t\tif (p.isConst() == false) throw FEBioImport::DataGeneratorError();\n\t\t\tFENodeMappedValue* val = new FENodeMappedValue(&fem);\n\t\t\tval->setDataMap(map, p.constValue());\n\t\t\tp.setValuator(val);\n\t\t}\n\t}\n\telse throw XMLReader::InvalidAttributeValue(tag, \"param\", szparam);\n}\n\n//-----------------------------------------------------------------------------\n// Helper function for setting material point member data\ntemplate <class T> void setMaterialPointData(FEElement& el, FEMaterialPointProperty& d, const T& v)\n{\n\tint nint = el.GaussPoints();\n\tfor (int j = 0; j < nint; ++j)\n\t{\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\td.set(mp, v);\n\t}\n}\n\nvoid FEBioMeshDataSection3::ParseMaterialPointData(XMLTag& tag, FEParamValue param)\n{\n\tif (param.type() != FE_PARAM_MATERIALPOINT) throw XMLReader::InvalidAttributeValue(tag, \"param\");\n\n\tFEParam* pp = param.param();\n\tFEMaterialPointProperty& matProp = pp->value<FEMaterialPointProperty>();\n\tFECoreBase* pc = dynamic_cast<FECoreBase*>(pp->parent());\n\tif (pc == 0) throw XMLReader::InvalidAttributeValue(tag, \"param\");\n\n\tFEDataType dataType = matProp.dataType();\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the parameter name\n\tconst char* szparam = tag.AttributeValue(\"param\");\n\n\t// see if the data will be generated or tabulated\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\n\tFEMaterial* mat = dynamic_cast<FEMaterial*>(pc->GetAncestor());\n\tif (mat)\n\t{\n\t\tFEDomainList& DL = mat->GetDomainList();\n\t\tFEElementSet* set = new FEElementSet(&fem);\n\t\tset->Create(DL);\n\n\t\tif (szgen)\n\t\t{\n\t\t\tFEDataGenerator* gen = 0;\n\t\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t\t{\n\t\t\t\tif      (dataType == FE_DOUBLE) gen = new FEConstDataGenerator<double>(&fem);\n\t\t\t\telse if (dataType == FE_VEC2D ) gen = new FEConstDataGenerator<vec2d >(&fem);\n\t\t\t\telse if (dataType == FE_VEC3D ) gen = new FEConstDataGenerator<vec3d >(&fem);\n\t\t\t\telse if (dataType == FE_MAT3D ) gen = new FEConstDataGenerator<mat3d >(&fem);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tgen = fecore_new<FEDataGenerator>(szgen, &fem);\n\t\t\t}\n\t\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// initialize the generator\n\t\t\tif (gen->Init() == false) throw FEBioImport::DataGeneratorError();\n\n\t\t\tfor (int i = 0; i < set->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = set->Element(i);\n\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\tvec3d rn = mp.m_r0;\n\t\t\t\t\tswitch (dataType)\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_DOUBLE: { double d; gen->value(rn, d); matProp.set(mp, d); } break;\n\t\t\t\t\tcase FE_VEC2D : { vec2d  d; gen->value(rn, d); matProp.set(mp, d); } break;\n\t\t\t\t\tcase FE_VEC3D : { vec3d  d; gen->value(rn, d); matProp.set(mp, d); } break;\n\t\t\t\t\tcase FE_MAT3D : { mat3d  d; gen->value(rn, d); matProp.set(mp, d); } break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvector<ELEMENT_DATA> data;\n\t\t\tParseElementData(tag, *set, data, fecore_data_size(matProp.dataType()));\n\n\t\t\tfor (int i = 0; i<set->Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = set->Element(i);\n\n\t\t\t\tELEMENT_DATA& di = data[i];\n\n\t\t\t\t// make sure the correct number of values were read in\n\t\t\t\tif (di.nval != fecore_data_size(matProp.dataType()))\n\t\t\t\t{\n\t\t\t\t\tthrow FEBioImport::MeshDataError();\n\t\t\t\t}\n\n\t\t\t\tdouble* v = di.val;\n\t\t\t\tswitch (matProp.dataType())\n\t\t\t\t{\n\t\t\t\tcase FE_DOUBLE: setMaterialPointData(el, matProp, v[0]); break;\n\t\t\t\tcase FE_VEC2D : setMaterialPointData(el, matProp, vec2d(v[0], v[1])); break;\n\t\t\t\tcase FE_VEC3D : setMaterialPointData(el, matProp, vec3d(v[0], v[1], v[2])); break;\n\t\t\t\tcase FE_MAT3D : setMaterialPointData(el, matProp, mat3d(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8])); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdelete set;\n\n\t\treturn;\n\t}\n\t\n\tthrow XMLReader::InvalidAttributeValue(tag, \"param\", szparam);\n}\n*/\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseShellThickness(XMLTag& tag, FEElementSet& set)\n{\n\tif (tag.isleaf())\n\t{\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tdouble h[FEElement::MAX_NODES];\n\t\tint nval = tag.value(h, FEElement::MAX_NODES);\n\n\t\tfor (int i=0; i<set.Elements(); ++i)\n\t\t{\n\t\t\tFEShellElement* pel = dynamic_cast<FEShellElement*>(&set.Element(i));\n\t\t\tif (pel == 0) throw XMLReader::InvalidValue(tag);\n\n\t\t\tif (pel->Nodes() != nval) throw XMLReader::InvalidValue(tag);\n\t\t\tfor (int j=0; j<nval; ++j) pel->m_h0[j] = h[j];\n\t\t}\n\t}\n\telse\n\t{\n\t\tvector<ELEMENT_DATA> data;\n\t\tParseElementData(tag, set, data, FEElement::MAX_NODES);\n\t\tfor (int i=0; i<(int)data.size(); ++i)\n\t\t{\n\t\t\tELEMENT_DATA& di = data[i];\n\t\t\tif (di.nval > 0)\n\t\t\t{\n\t\t\t\tFEElement& el = set.Element(i);\n\n\t\t\t\tif (el.Class() != FE_ELEM_SHELL) throw XMLReader::InvalidTag(tag);\n\t\t\t\tFEShellElement& shell = static_cast<FEShellElement&> (el);\n\t\t\n\t\t\t\tint ne = shell.Nodes();\n\t\t\t\tif (ne != di.nval) throw XMLReader::InvalidTag(tag);\n\t\t\t\tfor (int j=0; j<ne; ++j) shell.m_h0[j] = di.val[j];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseMaterialFibers(XMLTag& tag, FEElementSet& set)\n{\n\t// find the domain with the same name\n\tstring name = set.GetName();\n\n\tFEMesh* mesh = const_cast<FEMesh*>(set.GetMesh());\n\tFEDomain* dom = mesh->FindDomain(name);\n\tif (dom == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// get the material\n\tFEMaterial* mat = dom->GetMaterial();\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// get the fiber property\n\tFEProperty* fiber = mat->FindProperty(\"fiber\");\n\tif (fiber == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\tif (fiber->GetSuperClassID() != FEVEC3DVALUATOR_ID) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// create a domain map\n\tFEDomainMap* map = new FEDomainMap(FE_VEC3D, FMT_ITEM);\n\tmap->Create(&set);\n\tFEMappedValueVec3* val = fecore_new<FEMappedValueVec3>(\"map\", GetFEModel());\n\tval->setDataMap(map);\n\tfiber->SetProperty(val);\n\n\tvector<ELEMENT_DATA> data;\n\tParseElementData(tag, set, data, 3);\n\tfor (int i = 0; i < (int)data.size(); ++i)\n\t{\n\t\tELEMENT_DATA& di = data[i];\n\t\tif (di.nval > 0)\n\t\t{\n\t\t\tFEElement& el = set.Element(i);\n\n\t\t\tif (di.nval != 3) throw XMLReader::InvalidTag(tag);\n\t\t\tvec3d v(di.val[0], di.val[1], di.val[2]);\n\t\t\tv.unit();\n\t\t\tmap->set<vec3d>(i, v);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseMaterialAxes(XMLTag& tag, FEElementSet& set)\n{\n\t// find the domain with the same name\n\tstring name = set.GetName();\n\tconst char* szname = name.c_str();\n\n\tFEMesh* mesh = const_cast<FEMesh*>(set.GetMesh());\n\n\t// find the domain\n\tstring domName = set.GetName();\n\tFEDomainList& DL = set.GetDomainList();\n\tif (DL.Domains() != 1)\n\t{\n\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_set\", domName.c_str());\n\t}\n\tFEDomain* dom = DL.GetDomain(0);\n\n\t// get the material\n\tFEMaterial* mat = dom->GetMaterial();\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szname);\n\n\tStorage_Fmt fmt = FMT_ITEM;\n\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\tif (szfmt)\n\t{\n\t\tif (szcmp(szfmt, \"mat_points\") == 0) fmt = FMT_MATPOINTS;\n\t}\n\n\t// get the mat_axis property\n\tFEProperty* pQ = mat->FindProperty(\"mat_axis\", true);\n\tif (pQ == nullptr)\n\t{\n\t\t// if the material does not have the mat_axis property, we'll assign it directly to the material points\n\t\t// This only works for ITEM storage\n\t\tif (fmt != FMT_ITEM) throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif ((tag == \"e\") || (tag == \"elem\"))\n\t\t\t{\n\t\t\t\t// get the local element number\n\t\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\t\tint lid = atoi(szlid) - 1;\n\n\t\t\t\t// make sure the number is valid\n\t\t\t\tif ((lid < 0) || (lid >= set.Elements())) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// get the element\n\t\t\t\tFEElement* el = mesh->FindElementFromID(set[lid]);\n\t\t\t\tif (el == 0) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// read parameters\n\t\t\t\tdouble a[3] = { 0 };\n\t\t\t\tdouble d[3] = { 0 };\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"a\") tag.value(a, 3);\n\t\t\t\t\telse if (tag == \"d\") tag.value(d, 3);\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\n\t\t\t\tvec3d v1(a[0], a[1], a[2]);\n\t\t\t\tvec3d v2(d[0], d[1], d[2]);\n\n\t\t\t\tvec3d e1(v1);\n\t\t\t\tvec3d e3 = v1 ^ v2;\n\t\t\t\tvec3d e2 = e3 ^ e1;\n\n\t\t\t\t// normalize\n\t\t\t\te1.unit();\n\t\t\t\te2.unit();\n\t\t\t\te3.unit();\n\n\t\t\t\t// set the value\n\t\t\t\tmat3d A(e1, e2, e3);\n\n\t\t\t\t// convert to quaternion\n\t\t\t\tquatd Q(A);\n\n\t\t\t\t// assign to all material points\n\t\t\t\tint ni = el->GaussPoints();\n\t\t\t\tfor (int n = 0; n < ni; ++n)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint* mp = el->GetMaterialPoint(n);\n\t\t\t\t\tmp->m_Q = Q;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t\treturn;\n\t}\n\n\tif (pQ->GetSuperClassID() != FEMAT3DVALUATOR_ID) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szname);\n\n\t// create the map's name: material_name.mat_axis\n\tstringstream ss;\n\tss << \"material\" << mat->GetID() << \".mat_axis\";\n\tstring mapName = ss.str();\n\n\t// the domain map we're about to create\n\tFEDomainMap* map = nullptr;\n\n\t// see if the generator is defined\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen)\n\t{\n\t\t// create a domain map\n\t\tmap = new FEDomainMap(FE_MAT3D, fmt);\n\t\tmap->SetName(mapName);\n\t\tmap->Create(&set);\n\n\t\t// data will be generated\n\t\tFEModel* fem = GetFEModel();\n\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t{\n\t\t\tmap = new FEDomainMap(FE_MAT3D, fmt);\n\t\t\t++tag;\n\t\t\tdo {\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tmat3d v; tag.value(v); map->fillValue(v);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEElemDataGenerator* gen = fecore_new<FEElemDataGenerator>(szgen, fem);\n\t\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// initialize the generator\n\t\t\tif (gen->Init() == false) throw FEBioImport::DataGeneratorError();\n\n\t\t\t// generate the data\n\t\t\tmap = dynamic_cast<FEDomainMap*>(gen->Generate());\n\t\t\tif (map == nullptr) throw FEBioImport::DataGeneratorError();\n\t\t}\n\t}\n\telse\n\t{\n\t\t// This only works for ITEM storage\n\t\tif (fmt != FMT_ITEM) throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\n\t\t// create a domain map\n\t\tmap = new FEDomainMap(FE_MAT3D, FMT_ITEM);\n\t\tmap->SetName(mapName);\n\t\tmap->Create(&set);\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif ((tag == \"e\") || (tag == \"elem\"))\n\t\t\t{\n\t\t\t\t// get the local element number\n\t\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\t\tint lid = atoi(szlid) - 1;\n\n\t\t\t\t// make sure the number is valid\n\t\t\t\tif ((lid < 0) || (lid >= set.Elements())) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// get the element\n\t\t\t\tFEElement* el = mesh->FindElementFromID(set[lid]);\n\t\t\t\tif (el == 0) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// read parameters\n\t\t\t\tdouble a[3] = { 0 };\n\t\t\t\tdouble d[3] = { 0 };\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"a\") tag.value(a, 3);\n\t\t\t\t\telse if (tag == \"d\") tag.value(d, 3);\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\n\t\t\t\tvec3d v1(a[0], a[1], a[2]);\n\t\t\t\tvec3d v2(d[0], d[1], d[2]);\n\n\t\t\t\tvec3d e1(v1);\n\t\t\t\tvec3d e3 = v1 ^ v2;\n\t\t\t\tvec3d e2 = e3 ^ e1;\n\n\t\t\t\t// normalize\n\t\t\t\te1.unit();\n\t\t\t\te2.unit();\n\t\t\t\te3.unit();\n\n\t\t\t\t// set the value\n\t\t\t\tmat3d Q(e1, e2, e3);\n\t\t\t\tmap->setValue(lid, Q);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\tassert(map);\n\n\t// see if this map already exists\n\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh->FindDataMap(mapName));\n\tif (oldMap)\n\t{\n\t\t// It does, so merge it\n\t\toldMap->Merge(*map);\n\t\tdelete map;\n\t}\n\telse\n\t{\n\t\t// It does not, so add it\n\t\tFEMappedValueMat3d* val = fecore_alloc(FEMappedValueMat3d, GetFEModel());\n\t\tval->setDataMap(map);\n\t\tpQ->SetProperty(val);\n\t\tmesh->AddDataMap(map);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseNodeData(XMLTag& tag, FENodeDataMap& map)\n{\n\t// get the total nr of nodes\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nodes = map.DataCount();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tdouble data[3]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nodes)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, data[0]); break;\n\t\t\tcase FE_VEC2D:\tmap.setValue(n, vec2d(data[0], data[1])); break;\n\t\t\tcase FE_VEC3D:\tmap.setValue(n, vec3d(data[0], data[1], data[2])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseSurfaceData(XMLTag& tag, FESurfaceMap& map)\n{\n\tconst FEFacetSet* set = map.GetFacetSet();\n\tif (set == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set->Faces();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tint m = map.MaxNodes();\n\tdouble data[3 * FEElement::MAX_NODES]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, m*dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, data[0]); break;\n\t\t\tcase FE_VEC2D:\tmap.setValue(n, vec2d(data[0], data[1])); break;\n\t\t\tcase FE_VEC3D:\tmap.setValue(n, vec3d(data[0], data[1], data[2])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse if (nread == m*dataSize)\n\t\t{\n\t\t\tdouble* pd = data;\n\t\t\tfor (int i = 0; i < m; ++i, pd += dataSize)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FE_DOUBLE:\tmap.setValue(n, i, pd[0]); break;\n\t\t\t\tcase FE_VEC2D:\tmap.setValue(n, i, vec2d(pd[0], pd[1])); break;\n\t\t\t\tcase FE_VEC3D:\tmap.setValue(n, i, vec3d(pd[0], pd[1], pd[2])); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseElementData(XMLTag& tag, FEDomainMap& map)\n{\n\tif (tag.isleaf())\n\t{\n\t\tif (map.DataType() == FE_DOUBLE)\n\t\t{\n\t\t\tdouble v = 0.0;\n\t\t\ttag.value(v);\n\t\t\tmap.set(v);\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t}\n\n\tconst FEElementSet* set = map.GetElementSet();\n\tif (set == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set->Elements();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tint m = map.MaxNodes();\n\tdouble data[3 * FEElement::MAX_NODES]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t// TODO: For vec3d values, I sometimes need to normalize the vectors (e.g. for fibers). How can I do this?\n\n\tint ncount = 0;\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, m*dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tdouble* v = data;\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, v[0]); break;\n\t\t\tcase FE_VEC2D :\tmap.setValue(n, vec2d(v[0], v[1])); break;\n\t\t\tcase FE_VEC3D :\tmap.setValue(n, vec3d(v[0], v[1], v[2])); break;\n\t\t\tcase FE_MAT3D : map.setValue(n, mat3d(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8])); break;\n            case FE_MAT3DS: map.setValue(n, mat3ds(v[0], v[1], v[2], v[3], v[4], v[5])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse if (nread == m*dataSize)\n\t\t{\n\t\t\tdouble* v = data;\n\t\t\tfor (int i = 0; i < m; ++i, v += dataSize)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FE_DOUBLE:\tmap.setValue(n, i, v[0]); break;\n\t\t\t\tcase FE_VEC2D:\tmap.setValue(n, i, vec2d(v[0], v[1])); break;\n\t\t\t\tcase FE_VEC3D:\tmap.setValue(n, i, vec3d(v[0], v[1], v[2])); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\n\t\tncount++;\n\t}\n\twhile (!tag.isend());\n\n\tif (ncount != nelems) throw FEBioImport::MeshDataError();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection3::ParseElementData(XMLTag& tag, FEElementSet& set, vector<ELEMENT_DATA>& values, int nvalues)\n{\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set.Elements();\n\n\t// resize the array\n\tvalues.resize(nelems);\n\tfor (int i=0; i<nelems; ++i) values[i].nval = 0;\n\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid)-1;\n\n\t\t// make sure the number is valid\n\t\tif ((n<0) || (n>=nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tELEMENT_DATA& data = values[n];\n\t\tdata.nval = tag.value(data.val, nvalues);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshDataSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMeshDataSection.h\"\n#include \"FECore/FEModel.h\"\n#include <FECore/FEDataGenerator.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEDomainMap.h>\n#include <FECore/FEConstValueVec3.h>\n#include <sstream>\n\n// defined in FEBioMeshDataSection3.cpp\nextern FEDataType str2datatype(const char* szdataType);\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\n#define szcmp    _stricmp\n#else\n#define szcmp    strcmp\n#endif\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::Parse(XMLTag& tag)\n{\n\t// Make sure there is something in this tag\n\tif (tag.isleaf()) return;\n\n\t// make sure the MeshDomain section was processed. \n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tif (mesh.Domains() == 0)\n\t{\n\t\tthrow FEFileException(\"MeshData must appear after MeshDomain section.\");\n\t}\n\n\t// loop over all mesh data section\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"NodeData\"   ) ParseNodalData  (tag);\n\t\telse if (tag == \"SurfaceData\") ParseSurfaceData(tag);\n\t\telse if (tag == \"ElementData\") ParseElementData(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseNodalData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the element set\n\tconst char* szset = tag.AttributeValue(\"node_set\");\n\n\t// find the element set in the mesh\n\tFENodeSet* nset = GetBuilder()->FindNodeSet(szset);\n\tif (nset == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"node_set\", szset);\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t// get the name (required!)\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if there is a generator\n\tif (sztype)\n\t{\n\t\t// allocate the data generator\n\t\tFENodeDataGenerator* gen = dynamic_cast<FENodeDataGenerator*>(fecore_new<FEMeshDataGenerator>(sztype, &fem));\n\t\tif (gen == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\tgen->SetName(szname);\n\t\tgen->SetNodeSet(nset);\n\n\t\tGetBuilder()->GetFEModel().AddMeshDataGenerator(gen);\n\n\t\t// read the parameters\n\t\tReadParameterList(tag, gen);\n\n\t\t// Add it to the list (will be applied after the rest of the model was read in)\n\t\tGetBuilder()->AddMeshDataGenerator(gen, nullptr, nullptr);\n\t}\n\telse\n\t{\n\t\t// get the data type\n\t\tconst char* szdataType = tag.AttributeValue(\"data_type\", true);\n\t\tif (szdataType == nullptr) szdataType = \"scalar\";\n\t\tFEDataType dataType = str2datatype(szdataType);\n\t\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"data_type\", szdataType);\n\n\t\t// create the data map\n\t\tFENodeDataMap* map = new FENodeDataMap(dataType);\n\t\tmap->Create(nset);\n\t\tmap->SetName(szname);\n\n\t\t// add it to the mesh\n\t\tmesh.AddDataMap(map);\n\n\t\t// read the data\n\t\tParseNodeData(tag, *map);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseNodeData(XMLTag& tag, FENodeDataMap& map)\n{\n\t// get the total nr of nodes\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nodes = map.DataCount();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tdouble data[3]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nodes)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, data[0]); break;\n\t\t\tcase FE_VEC2D:\tmap.setValue(n, vec2d(data[0], data[1])); break;\n\t\t\tcase FE_VEC3D:\tmap.setValue(n, vec3d(data[0], data[1], data[2])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\nvoid FEBioMeshDataSection4::ParseSurfaceData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// find the element set\n\tconst char* szset = tag.AttributeValue(\"surface\");\n\n\t// find the element set in the mesh\n\tFEFacetSet* surf = mesh.FindFacetSet(szset);\n\tif (surf == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"surface\", szset);\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t// get the name (required!)\n\tstring sname = tag.AttributeValue(\"name\");\n\n\t// see if there is a generator\n\tif (sztype)\n\t{\n\t\tif (strcmp(sztype, \"const\") == 0)\n\t\t{\n\t\t\t// get the data type\n\t\t\tconst char* szdataType = tag.AttributeValue(\"data_type\", true);\n\t\t\tif (szdataType == nullptr) szdataType = \"scalar\";\n\t\t\tFEDataType dataType = str2datatype(szdataType);\n\t\t\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"datatype\", szdataType);\n\n\t\t\tFESurfaceMap* map = new FESurfaceMap(dataType);\n\t\t\tmap->Create(surf);\n\t\t\tmap->SetName(sname);\n\t\t\tmesh.AddDataMap(map);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tswitch (dataType)\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_DOUBLE: { double v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC2D : { vec2d  v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC3D : { vec3d  v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3D : { mat3d  v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3DS: { mat3ds v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"type\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEFaceDataGenerator* gen = fecore_new<FEFaceDataGenerator>(sztype, &fem);\n\t\t\tif (gen == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\tGetBuilder()->GetFEModel().AddMeshDataGenerator(gen);\n\n\t\t\tgen->SetFacetSet(surf);\n\t\t\tgen->SetName(sname);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// Add it to the list (will be applied after the rest of the model was read in)\n\t\t\tGetBuilder()->AddMeshDataGenerator(gen, nullptr, nullptr);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// get the data type\n\t\tconst char* szdataType = tag.AttributeValue(\"data_type\", true);\n\t\tif (szdataType == nullptr) szdataType = \"scalar\";\n\t\tFEDataType dataType = str2datatype(szdataType);\n\t\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"data_type\", szdataType);\n\n\t\t// create the data map\n\t\tFESurfaceMap* map = new FESurfaceMap(dataType);\n\t\tmap->Create(surf);\n\t\tmap->SetName(sname);\n\n\t\t// add it to the mesh\n\t\tmesh.AddDataMap(map);\n\n\t\t// read the data\n\t\tParseSurfaceData(tag, *map);\n\t}\n}\n\nvoid FEBioMeshDataSection4::ParseElementData(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// process the attributes\n\tconst char* szset  = nullptr;\n\tconst char* sztype = nullptr;\n\tconst char* szname = nullptr;\n\tconst char* szdataType = nullptr;\n\tconst char* szfmt = nullptr;\n\tfor (XMLAtt& att : tag.m_att)\n\t{\n\t\tif (strcmp(att.name(), \"elem_set\") == 0) { szset = att.cvalue(); att.m_bvisited = true; }\n\t\telse if (strcmp(att.name(), \"type\"     ) == 0) { sztype = att.cvalue(); att.m_bvisited = true;}\n\t\telse if (strcmp(att.name(), \"name\"     ) == 0) { szname = att.cvalue();  att.m_bvisited = true;}\n\t\telse if (strcmp(att.name(), \"data_type\") == 0) { szdataType = att.cvalue(); att.m_bvisited = true;}\n\t\telse if (strcmp(att.name(), \"format\"   ) == 0) { szfmt  = att.cvalue(); att.m_bvisited = true;}\n\t\telse throw XMLReader::InvalidAttribute(tag, att.name());\n\t}\n\n\t// find the element set in the mesh\n\tif (szset == nullptr) throw XMLReader::MissingAttribute(tag, \"elem_set\");\n\tFEElementSet* elset = mesh.FindElementSet(szset);\n\tif (elset == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szset);\n\n\tif (sztype)\n\t{\n\t\tif      (strcmp(sztype, \"shell thickness\") == 0) ParseShellThickness(tag, *elset);\n\t\telse if (strcmp(sztype, \"mat_axis\"       ) == 0) ParseMaterialAxes  (tag, *elset);\n\t\telse if (strcmp(sztype, \"fiber\"          ) == 0) ParseMaterialFibers(tag, *elset);\n\t\telse if (strstr(sztype, \".fiber\"         )) ParseMaterialFibers(tag, *elset);\n\t\telse if (strcmp(sztype, \"const\") == 0)\n\t\t{\n\t\t\t// get the data type\n\t\t\tconst char* szdataType = tag.AttributeValue(\"data_type\", true);\n\t\t\tif (szdataType == nullptr) szdataType = \"scalar\";\n\t\t\tFEDataType dataType = str2datatype(szdataType);\n\t\t\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"datatype\", szdataType);\n\n\t\t\t// create the data map\n\t\t\tFEDomainMap* map = new FEDomainMap(dataType, Storage_Fmt::FMT_ITEM);\n\t\t\tmap->Create(elset);\n\t\t\tmap->SetName(szname);\n\t\t\tmesh.AddDataMap(map);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tswitch (dataType)\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_DOUBLE: { double v; tag.value(v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC2D: { vec2d  v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_VEC3D: { vec3d  v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3D: { mat3d  v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tcase FE_MAT3DS: { mat3ds v; value(tag, v); map->fillValue(v); } break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"type\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// allocate generator\n\t\t\tFEElemDataGenerator* gen = dynamic_cast<FEElemDataGenerator*>(fecore_new<FEMeshDataGenerator>(sztype, &fem));\n\t\t\tif (gen == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t// set the name and element set\n\t\t\tgen->SetElementSet(elset);\n\t\t\tgen->SetName(szname);\n\n\t\t\t// add it to the model\n\t\t\tGetBuilder()->GetFEModel().AddMeshDataGenerator(gen);\n\n\t\t\t// read the parameters\n\t\t\tReadParameterList(tag, gen);\n\n\t\t\t// Add it to the list (will be applied after the rest of the model was read in)\n\t\t\tGetBuilder()->AddMeshDataGenerator(gen, nullptr, nullptr);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// name is required\n\t\tif (szname == nullptr) throw XMLReader::MissingAttribute(tag, \"name\");\n\n\t\t// copy the name\n\t\tstring name = szname;\n\n\t\t// get the data type\n\t\tif (szdataType == nullptr) szdataType = \"scalar\";\n\t\tFEDataType dataType = str2datatype(szdataType);\n\t\tif (dataType == FEDataType::FE_INVALID_TYPE) throw XMLReader::InvalidAttributeValue(tag, \"data_type\", szdataType);\n\n\t\t// default format\n\t\tStorage_Fmt fmt = (((dataType == FE_MAT3D) || (dataType == FE_MAT3DS)) ? Storage_Fmt::FMT_ITEM : Storage_Fmt::FMT_MULT);\n\n\t\t// format overrider?\n\t\tif (szfmt)\n\t\t{\n\t\t\tif      (szcmp(szfmt, \"MAT_POINTS\") == 0) fmt = Storage_Fmt::FMT_MATPOINTS;\n\t\t\telse if (szcmp(szfmt, \"ITEM\"      ) == 0) fmt = Storage_Fmt::FMT_ITEM;\n\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\t\t}\n\n\t\t// create the data map\n\t\tFEDomainMap* map = new FEDomainMap(dataType, fmt);\n\t\tmap->Create(elset);\n\t\tmap->SetName(name);\n\n\t\t// read the data\n\t\tParseElementData(tag, *map);\n\n\t\t// see if this map already exsits \n\t\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh.FindDataMap(name));\n\t\tif (oldMap)\n\t\t{\n\t\t\toldMap->Merge(*map);\n\t\t\tdelete map;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmap->SetName(name);\n\t\t\tmesh.AddDataMap(map);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseSurfaceData(XMLTag& tag, FESurfaceMap& map)\n{\n\tconst FEFacetSet* set = map.GetFacetSet();\n\tif (set == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set->Faces();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tint m = map.MaxNodes();\n\tdouble data[3 * FEElement::MAX_NODES]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, m * dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, data[0]); break;\n\t\t\tcase FE_VEC2D:\tmap.setValue(n, vec2d(data[0], data[1])); break;\n\t\t\tcase FE_VEC3D:\tmap.setValue(n, vec3d(data[0], data[1], data[2])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse if (nread == m * dataSize)\n\t\t{\n\t\t\tdouble* pd = data;\n\t\t\tfor (int i = 0; i < m; ++i, pd += dataSize)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FE_DOUBLE:\tmap.setValue(n, i, pd[0]); break;\n\t\t\t\tcase FE_VEC2D:\tmap.setValue(n, i, vec2d(pd[0], pd[1])); break;\n\t\t\t\tcase FE_VEC3D:\tmap.setValue(n, i, vec3d(pd[0], pd[1], pd[2])); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseShellThickness(XMLTag& tag, FEElementSet& set)\n{\n\tif (tag.isleaf())\n\t{\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tdouble h[FEElement::MAX_NODES];\n\t\tint nval = tag.value(h, FEElement::MAX_NODES);\n\n\t\tfor (int i = 0; i < set.Elements(); ++i)\n\t\t{\n\t\t\tFEShellElement* pel = dynamic_cast<FEShellElement*>(&set.Element(i));\n\t\t\tif (pel == 0) throw XMLReader::InvalidValue(tag);\n\n\t\t\tif (pel->Nodes() != nval) throw XMLReader::InvalidValue(tag);\n\t\t\tfor (int j = 0; j < nval; ++j) pel->m_h0[j] = h[j];\n\t\t}\n\t}\n\telse\n\t{\n\t\tvector<ELEMENT_DATA> data;\n\t\tParseElementData(tag, set, data, FEElement::MAX_NODES);\n\t\tfor (int i = 0; i < (int)data.size(); ++i)\n\t\t{\n\t\t\tELEMENT_DATA& di = data[i];\n\t\t\tif (di.nval > 0)\n\t\t\t{\n\t\t\t\tFEElement& el = set.Element(i);\n\n\t\t\t\tif (el.Class() != FE_ELEM_SHELL) throw XMLReader::InvalidTag(tag);\n\t\t\t\tFEShellElement& shell = static_cast<FEShellElement&> (el);\n\n\t\t\t\tint ne = shell.Nodes();\n\t\t\t\tif (ne != di.nval) throw XMLReader::InvalidTag(tag);\n\t\t\t\tfor (int j = 0; j < ne; ++j) shell.m_h0[j] = di.val[j];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseElementData(XMLTag& tag, FEElementSet& set, vector<ELEMENT_DATA>& values, int nvalues)\n{\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set.Elements();\n\n\t// resize the array\n\tvalues.resize(nelems);\n\tfor (int i = 0; i < nelems; ++i) values[i].nval = 0;\n\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tELEMENT_DATA& data = values[n];\n\t\tdata.nval = tag.value(data.val, nvalues);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseElementData(XMLTag& tag, FEDomainMap& map)\n{\n\tconst FEElementSet* set = map.GetElementSet();\n\tif (set == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the total nr of elements\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint nelems = set->Elements();\n\n\tFEDataType dataType = map.DataType();\n\tint dataSize = map.DataSize();\n\tint m = map.MaxNodes();\n\tdouble data[3 * FEElement::MAX_NODES]; // make sure this array is large enough to store any data map type (current 3 for FE_VEC3D)\n\n\t// TODO: For vec3d values, I sometimes need to normalize the vectors (e.g. for fibers). How can I do this?\n\n\tint ncount = 0;\n\t++tag;\n\tdo\n\t{\n\t\t// get the local element number\n\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\tint n = atoi(szlid) - 1;\n\n\t\t// make sure the number is valid\n\t\tif ((n < 0) || (n >= nelems)) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\tint nread = tag.value(data, m * dataSize);\n\t\tif (nread == dataSize)\n\t\t{\n\t\t\tdouble* v = data;\n\t\t\tswitch (dataType)\n\t\t\t{\n\t\t\tcase FE_DOUBLE:\tmap.setValue(n, v[0]); break;\n\t\t\tcase FE_VEC2D:\tmap.setValue(n, vec2d(v[0], v[1])); break;\n\t\t\tcase FE_VEC3D:\tmap.setValue(n, vec3d(v[0], v[1], v[2])); break;\n\t\t\tcase FE_MAT3D: map.setValue(n, mat3d(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8])); break;\n\t\t\tcase FE_MAT3DS: map.setValue(n, mat3ds(v[0], v[1], v[2], v[3], v[4], v[5])); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse if (nread == m * dataSize)\n\t\t{\n\t\t\tdouble* v = data;\n\t\t\tfor (int i = 0; i < m; ++i, v += dataSize)\n\t\t\t{\n\t\t\t\tswitch (dataType)\n\t\t\t\t{\n\t\t\t\tcase FE_DOUBLE:\tmap.setValue(n, i, v[0]); break;\n\t\t\t\tcase FE_VEC2D:\tmap.setValue(n, i, vec2d(v[0], v[1])); break;\n\t\t\t\tcase FE_VEC3D:\tmap.setValue(n, i, vec3d(v[0], v[1], v[2])); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse throw XMLReader::InvalidValue(tag);\n\t\t++tag;\n\n\t\tncount++;\n\t} while (!tag.isend());\n\n\tif (ncount != nelems) throw FEBioImport::MeshDataError();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseMaterialFibers(XMLTag& tag, FEElementSet& set)\n{\n\t// find the domain with the same name\n\tstring name = set.GetName();\n\n\tFEMesh* mesh = const_cast<FEMesh*>(set.GetMesh());\n\tFEDomain* dom = mesh->FindDomain(name);\n\tif (dom == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// get the material\n\tFEMaterial* mat = dom->GetMaterial();\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", name.c_str());\n\n\t// get the fiber property\n\tconst char* sztype = tag.Attribute(\"type\").cvalue();\n\tParamString ps(sztype);\n\tFEProperty* fiber = mat->FindProperty(ps);\n\tif (fiber == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", \"fiber\");\n\tif (fiber->GetSuperClassID() != FEVEC3DVALUATOR_ID) throw XMLReader::InvalidAttributeValue(tag, \"type\", \"fiber\");\n\n\t// create a domain map\n\tFEDomainMap* map = new FEDomainMap(FE_VEC3D, FMT_ITEM);\n\tmap->Create(&set);\n\tFEMappedValueVec3* val = fecore_new<FEMappedValueVec3>(\"map\", GetFEModel());\n\tval->setDataMap(map);\n\tfiber->SetProperty(val);\n\n\tvector<ELEMENT_DATA> data;\n\tParseElementData(tag, set, data, 3);\n\tfor (int i = 0; i < (int)data.size(); ++i)\n\t{\n\t\tELEMENT_DATA& di = data[i];\n\t\tif (di.nval > 0)\n\t\t{\n\t\t\tFEElement& el = set.Element(i);\n\n\t\t\tif (di.nval != 3) throw XMLReader::InvalidTag(tag);\n\t\t\tvec3d v(di.val[0], di.val[1], di.val[2]);\n\t\t\tv.unit();\n\t\t\tmap->set<vec3d>(i, v);\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshDataSection4::ParseMaterialAxes(XMLTag& tag, FEElementSet& set)\n{\n\t// find the domain with the same name\n\tstring name = set.GetName();\n\tconst char* szname = name.c_str();\n\n\tFEMesh* mesh = const_cast<FEMesh*>(set.GetMesh());\n\n\t// find the domain\n\tstring domName = set.GetName();\n\tFEDomainList& DL = set.GetDomainList();\n\tif (DL.Domains() != 1)\n\t{\n\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_set\", domName.c_str());\n\t}\n\tFEDomain* dom = DL.GetDomain(0);\n\n\t// get the material\n\tFEMaterial* mat = dom->GetMaterial();\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szname);\n\n\tStorage_Fmt fmt = FMT_ITEM;\n\tconst char* szfmt = tag.AttributeValue(\"format\", true);\n\tif (szfmt)\n\t{\n\t\tif (szcmp(szfmt, \"mat_points\") == 0) fmt = FMT_MATPOINTS;\n\t}\n\n\t// get the mat_axis property\n\tFEProperty* pQ = mat->FindProperty(\"mat_axis\", true);\n\tif (pQ == nullptr)\n\t{\n\t\t// if the material does not have the mat_axis property, we'll assign it directly to the material points\n\t\t// This only works for ITEM storage\n\t\tif (fmt != FMT_ITEM) throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif ((tag == \"e\") || (tag == \"elem\"))\n\t\t\t{\n\t\t\t\t// get the local element number\n\t\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\t\tint lid = atoi(szlid) - 1;\n\n\t\t\t\t// make sure the number is valid\n\t\t\t\tif ((lid < 0) || (lid >= set.Elements())) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// get the element\n\t\t\t\tFEElement* el = mesh->FindElementFromID(set[lid]);\n\t\t\t\tif (el == 0) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// read parameters\n\t\t\t\tvec3d a, d;\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"a\") value(tag, a);\n\t\t\t\t\telse if (tag == \"d\") value(tag, d);\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\n\t\t\t\t// convert to quaternion\n\t\t\t\tmat3d A(a, d);\n\t\t\t\tquatd Q(A);\n\n\t\t\t\t// assign to all material points\n\t\t\t\tint ni = el->GaussPoints();\n\t\t\t\tfor (int n=0; n<ni; ++n)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint* mp = el->GetMaterialPoint(n);\n\t\t\t\t\tmp->m_Q = Q;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t\treturn;\n\t}\n\n\tif (pQ->GetSuperClassID() != FEMAT3DVALUATOR_ID) throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szname);\n\n\t// create the map's name: material_name.mat_axis\n\tstringstream ss;\n\tss << \"material\" << mat->GetID() << \".mat_axis\";\n\tstring mapName = ss.str();\n\n\t// the domain map we're about to create\n\tFEDomainMap* map = nullptr;\n\n\t// see if the generator is defined\n\tconst char* szgen = tag.AttributeValue(\"generator\", true);\n\tif (szgen)\n\t{\n\t\t// create a domain map\n\t\tmap = new FEDomainMap(FE_MAT3D, fmt);\n\t\tmap->SetName(mapName);\n\t\tmap->Create(&set);\n\n\t\t// data will be generated\n\t\tFEModel* fem = GetFEModel();\n\t\tFEElemDataGenerator* gen = 0;\n\t\tif (strcmp(szgen, \"const\") == 0)\n\t\t{\n\t\t\t++tag;\n\t\t\tdo {\n\t\t\t\tif (tag == \"value\")\n\t\t\t\t{\n\t\t\t\t\tmat3d v;\n\t\t\t\t\ttag.value(v);\n\t\t\t\t\tmap->fillValue(v);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"generator\", szgen);\n\t}\n\telse\n\t{\n\t\t// This only works for ITEM storage\n\t\tif (fmt != FMT_ITEM) throw XMLReader::InvalidAttributeValue(tag, \"format\", szfmt);\n\n\t\t// create a domain map\n\t\tmap = new FEDomainMap(FE_MAT3D, FMT_ITEM);\n\t\tmap->SetName(mapName);\n\t\tmap->Create(&set);\n\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif ((tag == \"e\") || (tag == \"elem\"))\n\t\t\t{\n\t\t\t\t// get the local element number\n\t\t\t\tconst char* szlid = tag.AttributeValue(\"lid\");\n\t\t\t\tint lid = atoi(szlid) - 1;\n\n\t\t\t\t// make sure the number is valid\n\t\t\t\tif ((lid < 0) || (lid >= set.Elements())) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// get the element\n\t\t\t\tFEElement* el = mesh->FindElementFromID(set[lid]);\n\t\t\t\tif (el == 0) throw XMLReader::InvalidAttributeValue(tag, \"lid\", szlid);\n\n\t\t\t\t// read parameters\n\t\t\t\tvec3d a, d;\n\t\t\t\t++tag;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (tag == \"a\") value(tag, a);\n\t\t\t\t\telse if (tag == \"d\") value(tag, d);\n\t\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t++tag;\n\t\t\t\t} while (!tag.isend());\n\n\t\t\t\t// set the value\n\t\t\t\tmat3d Q(a, d);\n\t\t\t\tmap->setValue(lid, Q);\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\tassert(map);\n\n\t// see if this map already exists\n\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh->FindDataMap(mapName));\n\tif (oldMap)\n\t{\n\t\t// It does, so merge it\n\t\toldMap->Merge(*map);\n\t\tdelete map;\n\t}\n\telse\n\t{\n\t\t// It does not, so add it\n\t\tFEMappedValueMat3d* val = fecore_alloc(FEMappedValueMat3d, GetFEModel());\n\t\tval->setDataMap(map);\n\t\tpQ->SetProperty(val);\n\t\tmesh->AddDataMap(map);\n\t}\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshDomainsSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioMeshDomainsSection4.h\"\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEShellDomain.h>\n#include <FECore/FETrussDomain.h>\n#include <FECore/FEDomain2D.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FECoreKernel.h>\n#include <sstream>\n\nFEBioMeshDomainsSection4::FEBioMeshDomainsSection4(FEBioImport* pim) : FEBioFileSection(pim) \n{\n\tm_noff = 0;\n}\n\nvoid FEBioMeshDomainsSection4::Parse(XMLTag& tag)\n{\n\t// build the node ID lookup table\n\tBuildNLT();\n\t\n\t// read all sections\n\tif (tag.isleaf() == false)\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif      (tag == \"SolidDomain\") ParseSolidDomainSection(tag);\n\t\t\telse if (tag == \"ShellDomain\") ParseShellDomainSection(tag);\n\t\t\telse if (tag == \"BeamDomain\" ) ParseBeamDomainSection(tag);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\n\t// let's build the part\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (feb.BuildPart(*GetFEModel(), *part, false) == false)\n\t{\n\t\tthrow XMLReader::Error(\"Failed building parts.\");\n\t}\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n\n\t// At this point the mesh is completely read in.\n\t// allocate material point data\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.CreateMaterialPointData();\n\t}\n\n\t// Now we can allocate the degrees of freedom.\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tfem.GetMesh().SetDOFS(MAX_DOFS);\n}\n\nvoid FEBioMeshDomainsSection4::BuildNLT()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (part == nullptr) return;\n\n\t// build node-index lookup table\n\tint noff = -1, maxID = 0;\n\tint N0 = mesh.Nodes();\n\tint NN = part->Nodes();\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tint nid = part->GetNode(i).id;\n\t\tif ((noff < 0) || (nid < noff)) noff = nid;\n\t\tif (nid > maxID) maxID = nid;\n\t}\n\tm_NLT.assign(maxID - noff + 1, -1);\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tint nid = part->GetNode(i).id - noff;\n\t\tm_NLT[nid] = i + N0;\n\t}\n\n\tm_noff = noff;\n}\n\nvoid FEBioMeshDomainsSection4::ParseSolidDomainSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (part == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the domain name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::Domain* partDomain = part->FindDomain(szname);\n\tif (partDomain == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t// get the material name\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\tFEMaterial* mat = fem.FindMaterial(szmat);\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szmat);\n\n\t// set the material name\n\tpartDomain->SetMaterialName(szmat);\n\n\t// see if the element type is specified\n\tconst char* szelem = tag.AttributeValue(\"elem_type\", true);\n\tif (szelem && (strcmp(szelem, \"default\") != 0))\n\t{\n\t\tFE_Element_Spec elemSpec = partDomain->ElementSpec();\n\t\tFE_Element_Spec newSpec = GetBuilder()->ElementSpec(szelem);\n\n\t\t// make sure it's valid\n\t\tif ((FEElementLibrary::IsValid(newSpec) == false) || (elemSpec.eshape != newSpec.eshape))\n\t\t{\n\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_type\", szelem);\n\t\t}\n\n\t\tpartDomain->SetElementSpec(newSpec);\n\t}\n\n\t// get the (optional) type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t// --- build the domain --- \n\t// we'll need the kernel for creating domains\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// element count\n\tint elems = partDomain->Elements();\n\n\t// get the element spect\n\tFE_Element_Spec spec = partDomain->ElementSpec();\n\n\t// create the domain\n\tFEDomain* dom = nullptr;\n\tif (sztype)\n\t{\n\t\t// if the type attribute is defined, try to allocate the domain class directly. \n\t\tdom = febio.CreateDomainExplicit(FESOLIDDOMAIN_ID, sztype, &fem);\n\t\tif (dom == nullptr) throw XMLReader::InvalidAttributeValue(tag, sztype);\n\t\tdom->SetMaterial(mat);\n\t}\n\telse\n\t{\n\t\t// if not, then use \"old\" logic, which tries to match the domain using the \n\t\t// domain factories. \n\t\tdom = febio.CreateDomain(spec, &mesh, mat);\n\t\tif (dom == 0) throw XMLReader::InvalidTag(tag);\n\t}\n\n\t// add it to the mesh\n\tmesh.AddDomain(dom);\n\n\t// Allocate elements\n\tif (dom->Create(elems, spec) == false)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// assign the material\n\tdom->SetMatID(mat->GetID() - 1);\n\n\t// get the part name\n\tstring partName = part->Name();\n\tif (partName.empty() == false) partName += \".\";\n\n\tstring domName = partName + partDomain->Name();\n\tdom->SetName(domName);\n\n\t// read additional parameters\n\tif (tag.isleaf() == false)\n\t{\n\t\tReadParameterList(tag, dom);\n\t}\n\n\t// process element data\n\tfor (int j = 0; j < elems; ++j)\n\t{\n\t\tconst FEBModel::ELEMENT& domElement = partDomain->GetElement(j);\n\n\t\tFEElement& el = dom->ElementRef(j);\n\t\tel.SetID(domElement.id);\n\n\t\tint ne = el.Nodes();\n\t\tfor (int n = 0; n < ne; ++n) el.m_node[n] = m_NLT[domElement.node[n] - m_noff];\n\t}\n}\n\nvoid FEBioMeshDomainsSection4::ParseShellDomainSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (part == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the domain name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::Domain* partDomain = part->FindDomain(szname);\n\tif (partDomain == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t// get the material name\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\tFEMaterial* mat = fem.FindMaterial(szmat);\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szmat);\n\n\t// set the material name\n\tpartDomain->SetMaterialName(szmat);\n\n\t// see if the element type is specified\n\tconst char* szelem = tag.AttributeValue(\"elem_type\", true);\n\tif (szelem)\n\t{\n\t\tFE_Element_Spec elemSpec = partDomain->ElementSpec();\n\t\tFE_Element_Spec newSpec = GetBuilder()->ElementSpec(szelem);\n\n\t\t// make sure it's valid\n\t\tif ((FEElementLibrary::IsValid(newSpec) == false) || (elemSpec.eshape != newSpec.eshape))\n\t\t{\n\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_type\", szelem);\n\t\t}\n\n\t\tpartDomain->SetElementSpec(newSpec);\n\t}\n\n\t// get the (optional) type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t// --- build the domain --- \n\t// we'll need the kernel for creating domains\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// element count\n\tint elems = partDomain->Elements();\n\n\t// get the element spect\n\tFE_Element_Spec spec = partDomain->ElementSpec();\n\n\t// create the domain\n\tFEDomain* dom = nullptr;\n\tif (sztype)\n\t{\n\t\t// if the type attribute is defined, try to allocate the domain class directly. \n\t\tdom = febio.CreateDomainExplicit(FESHELLDOMAIN_ID, sztype, &fem);\n\t\tif (dom == nullptr) throw XMLReader::InvalidAttributeValue(tag, sztype);\n\t\tdom->SetMaterial(mat);\n\t}\n\telse\n\t{\n\t\t// if not, then use \"old\" logic, which tries to match the domain using the \n\t\t// domain factories. \n\t\tdom = febio.CreateDomain(spec, &mesh, mat);\n\t\tif (dom == 0) throw XMLReader::InvalidTag(tag);\n\t}\n\n\tmesh.AddDomain(dom);\n\n\tif (dom->Create(elems, spec) == false)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// assign the material\n\tdom->SetMatID(mat->GetID() - 1);\n\n\t// get the part name\n\tstring partName = part->Name();\n\tif (partName.empty() == false) partName += \".\";\n\n\tstring domName = partName + partDomain->Name();\n\tdom->SetName(domName);\n\n\t// read additional parameters\n\tif (tag.isleaf() == false)\n\t{\n\t\tReadParameterList(tag, dom);\n\t}\n\n\t// process element data\n\tFEShellDomain* shellDomain = dynamic_cast<FEShellDomain*>(dom);\n\tif (shellDomain)\n\t{\n\t\tfor (int j = 0; j < elems; ++j)\n\t\t{\n\t\t\tconst FEBModel::ELEMENT& domElement = partDomain->GetElement(j);\n\n\t\t\tFEShellElement& el = shellDomain->Element(j);\n\t\t\tel.SetID(domElement.id);\n\n\t\t\tint ne = el.Nodes();\n\t\t\tfor (int n = 0; n < ne; ++n)\n\t\t\t{\n\t\t\t\tel.m_node[n] = m_NLT[domElement.node[n] - m_noff];\n\t\t\t\tel.m_h0[n] = 0.0;\n\t\t\t}\n\t\t}\n\n\t\tshellDomain->AssignDefaultShellThickness();\n\t}\n}\n\nvoid FEBioMeshDomainsSection4::ParseBeamDomainSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (part == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the domain name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::Domain* partDomain = part->FindDomain(szname);\n\tif (partDomain == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t// get the material name\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\tFEMaterial* mat = fem.FindMaterial(szmat);\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szmat);\n\n\t// set the material name\n\tpartDomain->SetMaterialName(szmat);\n\n\t// see if the element type is specified\n\tconst char* szelem = tag.AttributeValue(\"elem_type\", true);\n\tif (szelem)\n\t{\n\t\tFE_Element_Spec elemSpec = partDomain->ElementSpec();\n\t\tFE_Element_Spec newSpec = GetBuilder()->ElementSpec(szelem);\n\n\t\t// make sure it's valid\n\t\tif ((FEElementLibrary::IsValid(newSpec) == false) || (elemSpec.eshape != newSpec.eshape))\n\t\t{\n\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_type\", szelem);\n\t\t}\n\n\t\tpartDomain->SetElementSpec(newSpec);\n\t}\n\n\t// get the (optional) type attribute\n\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t// --- build the domain --- \n\t// we'll need the kernel for creating domains\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// element count\n\tint elems = partDomain->Elements();\n\n\t// get the element spect\n\tFE_Element_Spec spec = partDomain->ElementSpec();\n\n\t// create the domain\n\tFEDomain* dom = nullptr;\n\tif (sztype)\n\t{\n\t\t// if the type attribute is defined, try to allocate the domain class directly. \n\t\tdom = febio.CreateDomainExplicit(FEBEAMDOMAIN_ID, sztype, &fem);\n\t\tif (dom == nullptr) throw XMLReader::InvalidAttributeValue(tag, sztype);\n\t\tdom->SetMaterial(mat);\n\t}\n\telse\n\t{\n\t\t// if not, then use \"old\" logic, which tries to match the domain using the \n\t\t// domain factories. \n\t\tdom = febio.CreateDomain(spec, &mesh, mat);\n\t\tif (dom == 0) throw XMLReader::InvalidTag(tag);\n\t}\n\n\t// add it to the mesh\n\tmesh.AddDomain(dom);\n\n\t// Allocate elements\n\tif (dom->Create(elems, spec) == false)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// assign the material\n\tdom->SetMatID(mat->GetID() - 1);\n\n\t// get the part name\n\tstring partName = part->Name();\n\tif (partName.empty() == false) partName += \".\";\n\n\tstring domName = partName + partDomain->Name();\n\tdom->SetName(domName);\n\n\t// read additional parameters\n\tif (tag.isleaf() == false)\n\t{\n\t\tReadParameterList(tag, dom);\n\t}\n\n\t// process element data\n\tfor (int j = 0; j < elems; ++j)\n\t{\n\t\tconst FEBModel::ELEMENT& domElement = partDomain->GetElement(j);\n\n\t\tFEElement& el = dom->ElementRef(j);\n\t\tel.SetID(domElement.id);\n\n\t\t// TODO: This assumes one-based indexing of all nodes!\n\t\tint ne = el.Nodes();\n\t\tfor (int n = 0; n < ne; ++n) el.m_node[n] = m_NLT[domElement.node[n] - m_noff];\n\t}\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshDomainsSection4.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBioImport.h\"\n#include \"FEBModel.h\"\n\n//-----------------------------------------------------------------------------\n// MeshDomains section\nclass FEBioMeshDomainsSection4 : public FEBioFileSection\n{\npublic:\n\tFEBioMeshDomainsSection4(FEBioImport* pim);\n\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseSolidDomainSection(XMLTag& tag);\n\tvoid ParseShellDomainSection(XMLTag& tag);\n\tvoid ParseBeamDomainSection(XMLTag& tag);\n\nprivate:\n\tvoid BuildNLT();\n\nprivate:\n\tstd::vector<int>\tm_NLT;\n\tint\t\t\t\t\tm_noff;\n};\n"
  },
  {
    "path": "FEBioXML/FEBioMeshSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMeshSection.h\"\n#include <FECore/FEDomain.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FECoreKernel.h>\n#include <sstream>\n\n//-----------------------------------------------------------------------------\nFEBioMeshSection::FEBioMeshSection(FEBioImport* pim) : FEBioFileSection(pim) {}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection::Parse(XMLTag& tag)\n{\n\tFEModelBuilder* builder = GetBuilder();\n\tbuilder->m_maxid = 0;\n\n\t// create a default part\n\t// NOTE: Do not specify a name for the part, otherwise\n\t//       all lists will be given the name: partname.listname\n\tFEBModel& feb = builder->GetFEBModel();\n\tassert(feb.Parts() == 0);\n\tFEBModel::Part* part = feb.AddPart(\"\");\n\n\t// read all sections\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParseNodeSection       (tag, part);\n\t\telse if (tag == \"Elements\"   ) ParseElementSection    (tag, part);\n\t\telse if (tag == \"NodeSet\"    ) ParseNodeSetSection    (tag, part);\n\t\telse if (tag == \"Surface\"    ) ParseSurfaceSection    (tag, part);\n//\t\telse if (tag == \"Edge\"       ) ParseEdgeSection       (tag, part);\n\t\telse if (tag == \"ElementSet\" ) ParseElementSetSection (tag, part);\n\t\telse if (tag == \"SurfacePair\") ParseSurfacePairSection(tag, part);\n\t\telse if (tag == \"DiscreteSet\") ParseDiscreteSetSection(tag, part);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioMeshSection::ParseNodeSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// see if this list defines a set\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tFEBModel::NodeSet* ps = 0;\n\tif (szname)\n\t{\n\t\tps = new FEBModel::NodeSet(szname);\n\t\tpart->AddNodeSet(ps);\n\t}\n\n\t// allocate node\n\n\tvector<FEBModel::NODE> node; node.reserve(10000);\n\tvector<int> nodeList; nodeList.reserve(10000);\n\n\t// read nodal coordinates\n\t++tag;\n\tdo {\n\t\t// nodal coordinates\n\t\tFEBModel::NODE nd;\n\t\tvalue(tag, nd.r);\n\n\t\t// get the nodal ID\n\t\ttag.AttributeValue(\"id\", nd.id);\n\n\t\t// add it to the pile\n\t\tnode.push_back(nd);\n\t\tnodeList.push_back(nd.id);\n\n\t\t// go on to the next node\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// add nodes to the part\n\tpart->AddNodes(node);\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps) ps->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioMeshSection::ParseElementSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the (required!) name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// get the element spec\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// make sure the domain does not exist yet\n\tFEBModel::Domain* dom = part->FindDomain(szname);\n\tif (dom)\n\t{\n\t\tstringstream ss;\n\t\tss << \"Duplicate part name found : \" << szname;\n\t\tthrow std::runtime_error(ss.str());\n\t}\n\n\t// create the new domain\n\tdom = new FEBModel::Domain(espec);\n\tif (szname) dom->SetName(szname);\n\n\t// add domain it to the mesh\n\tpart->AddDomain(dom);\n\n\t// for named domains, we'll also create an element set\n\tFEBModel::ElementSet* pg = 0;\n\tif (szname)\n\t{\n\t\tpg = new FEBModel::ElementSet(szname);\n\t\tpart->AddElementSet(pg);\n\t}\n\n\tdom->Reserve(10000);\n\tvector<int> elemList; elemList.reserve(10000);\n\n\t// read element data\n\t++tag;\n\tdo\n\t{\n\t\tFEBModel::ELEMENT el;\n\n\t\t// get the element ID\n\t\ttag.AttributeValue(\"id\", el.id);\n\n\t\t// read the element data\n\t\ttag.value(el.node, FEElement::MAX_NODES);\n\n\t\tdom->AddElement(el);\n\t\telemList.push_back(el.id);\n\n\t\t// go to next tag\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// set the element list\n\tif (pg) pg->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Geometry::Groups section of the FEBio input file\nvoid FEBioMeshSection::ParseNodeSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if a nodeset with this name already exists\n\tFEBModel::NodeSet* set = part->FindNodeSet(szname);\n\tif (set) throw FEBioImport::RepeatedNodeSet(szname);\n\n\t// create the node set\n\tset = new FEBModel::NodeSet(szname);\n\tpart->AddNodeSet(set);\n\n\tint nodes = tag.children();\n\tvector<int> nodeList(nodes);\n\n\t++tag;\n\tfor (int i = 0; i < nodes; ++i)\n\t{\n\t\t// get the ID\n\t\tint nid;\n\t\ttag.AttributeValue(\"id\", nid);\n\t\tnodeList[i] = nid;\n\t\t++tag;\n\t}\n\n\t// add nodes to the list\n\tset->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection::ParseSurfaceSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if this surface was already defined\n\tFEBModel::Surface* ps = part->FindSurface(szname);\n\tif (ps) throw FEBioImport::RepeatedSurface(szname);\n\n\t// count nr of faces\n\tint faces = tag.children();\n\n\t// allocate storage for faces\n\tps = new FEBModel::Surface(szname);\n\tpart->AddSurface(ps);\n\tps->Create(faces);\n\n\t// read faces\n\t++tag;\n\tfor (int i = 0; i < faces; ++i)\n\t{\n\t\tFEBModel::FACET& face = ps->GetFacet(i);\n\n\t\t// get the ID (although we don't really use this)\n\t\ttag.AttributeValue(\"id\", face.id);\n\n\t\t// set the facet type\n\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\telse if (tag == \"tri10\") face.ntype = 10;\n\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the facet type also defines the number of nodes\n\t\tint N = face.ntype;\n\t\ttag.value(face.node, N);\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection::ParseElementSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if this element set was already defined\n\tFEBModel::ElementSet* ps = part->FindElementSet(szname);\n\tif (ps) throw FEBioImport::RepeatedElementSet(szname);\n\n\t// allocate storage for faces\n\tps = new FEBModel::ElementSet(szname);\n\tpart->AddElementSet(ps);\n\n\t// read elements\n\tvector<int> elemList;\n\t++tag;\n\tdo\n\t{\n\t\t// get the ID\n\t\tint id;\n\t\ttag.AttributeValue(\"id\", id);\n\t\telemList.push_back(id);\n\n\t\t++tag;\n\t} while (!tag.isend());\n\n\tif (elemList.empty()) throw XMLReader::InvalidTag(tag);\n\n\tps->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection::ParseEdgeSection(XMLTag& tag, FEBModel::Part* part)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection::ParseSurfacePairSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::SurfacePair* surfPair = new FEBModel::SurfacePair;\n\tsurfPair->m_name = szname;\n\tpart->AddSurfacePair(surfPair);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"primary\")\n\t\t{\n\t\t\tconst char* sz = tag.szvalue();\n\t\t\tFEBModel::Surface* surf = part->FindSurface(sz);\n\t\t\tif (surf == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\tsurfPair->m_primary = sz;\n\t\t}\n\t\telse if (tag == \"secondary\")\n\t\t{\n\t\t\tconst char* sz = tag.szvalue();\n\t\t\tFEBModel::Surface* surf = part->FindSurface(sz);\n\t\t\tif (surf == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\tsurfPair->m_secondary = sz;\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} \n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection::ParseDiscreteSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::DiscreteSet* dset = new FEBModel::DiscreteSet;\n\tdset->SetName(szname);\n\tpart->AddDiscreteSet(dset);\n\n\tint n[2];\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"delem\")\n\t\t{\n\t\t\ttag.value(n, 2);\n\t\t\tdset->AddElement(n[0], n[1]);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\n//=============================================================================\nFEBioMeshDomainsSection::FEBioMeshDomainsSection(FEBioImport* pim) : FEBioFileSection(pim) {}\n\nvoid FEBioMeshDomainsSection::Parse(XMLTag& tag)\n{\n\t// read all sections\n\tif (tag.isleaf() == false)\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif      (tag == \"SolidDomain\") ParseSolidDomainSection(tag);\n\t\t\telse if (tag == \"ShellDomain\") ParseShellDomainSection(tag);\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\n\t// let's build the part\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (feb.BuildPart(*GetFEModel(), *part, false) == false)\n\t{\n\t\tthrow XMLReader::Error(\"Failed building parts.\");\n\t}\n\n\t// tell the file reader to rebuild the node ID table\n\tGetBuilder()->BuildNodeList();\n\n\t// At this point the mesh is completely read in.\n\t// allocate material point data\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.CreateMaterialPointData();\n\t}\n\n\t// Now we can allocate the degrees of freedom.\n\tint MAX_DOFS = fem.GetDOFS().GetTotalDOFS();\n\tfem.GetMesh().SetDOFS(MAX_DOFS);\n}\n\nvoid FEBioMeshDomainsSection::ParseSolidDomainSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (part == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the domain name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::Domain* partDomain = part->FindDomain(szname);\n\tif (partDomain == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t// get the material name\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\tFEMaterial* mat = fem.FindMaterial(szmat);\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szmat);\n\n\t// set the material name\n\tpartDomain->SetMaterialName(szmat);\n\n\t// see if the element type is specified\n\tconst char* szelem = tag.AttributeValue(\"elem_type\", true);\n\tif (szelem)\n\t{\n\t\tFE_Element_Spec elemSpec = partDomain->ElementSpec();\n\t\tFE_Element_Spec newSpec = GetBuilder()->ElementSpec(szelem);\n\n\t\t// make sure it's valid\n\t\tif ((FEElementLibrary::IsValid(newSpec) == false) || (elemSpec.eshape != newSpec.eshape))\n\t\t{\n\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"elem_type\", szelem);\n\t\t}\n\n\t\tpartDomain->SetElementSpec(newSpec);\n\t}\n\n\t// see if the three_field flag is defined\n\tconst char* sz3field = tag.AttributeValue(\"three_field\", true);\n\tif (sz3field)\n\t{\n\t\tif (strcmp(sz3field, \"on\") == 0)\n\t\t{\n\t\t\tFE_Element_Spec espec = partDomain->ElementSpec();\n\t\t\tespec.m_bthree_field = true;\n\t\t\tpartDomain->SetElementSpec(espec);\n\t\t}\n\t\telse if (strcmp(sz3field, \"off\") == 0)\n\t\t{\n\t\t\tFE_Element_Spec espec = partDomain->ElementSpec();\n\t\t\tespec.m_bthree_field = false;\n\t\t\tpartDomain->SetElementSpec(espec);\n\t\t}\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"three_field\", sz3field);\n\t}\n\n\t// --- build the domain --- \n\t// we'll need the kernel for creating domains\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// element count\n\tint elems = partDomain->Elements();\n\n\t// get the element spect\n\tFE_Element_Spec spec = partDomain->ElementSpec();\n\n\t// create the domain\n\tFEDomain* dom = febio.CreateDomain(spec, &mesh, mat);\n\tif (dom == 0) throw XMLReader::InvalidTag(tag);\n\n\tmesh.AddDomain(dom);\n\n\t// TODO: Should the domain parameters be read in before\n\t//       the domain is created? For UT4 domains, this is necessary\n\t//       since the initial ut4 parameters are copied from the element spec.\n\t//       but they can be overridden by the parameters.\n\tif (dom->Create(elems, spec) == false)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// assign the material\n\tdom->SetMatID(mat->GetID() - 1);\n\n\t// get the part name\n\tstring partName = part->Name();\n\tif (partName.empty() == false) partName += \".\";\n\n\tstring domName = partName + partDomain->Name();\n\tdom->SetName(domName);\n\n\t// process element data\n\tfor (int j = 0; j < elems; ++j)\n\t{\n\t\tconst FEBModel::ELEMENT& domElement = partDomain->GetElement(j);\n\n\t\tFEElement& el = dom->ElementRef(j);\n\t\tel.SetID(domElement.id);\n\n\t\t// TODO: This assumes one-based indexing of all nodes!\n\t\tint ne = el.Nodes();\n\t\tfor (int n = 0; n < ne; ++n) el.m_node[n] = domElement.node[n] - 1;\n\t}\n\n\t// read additional parameters\n\tif (tag.isleaf() == false)\n\t{\n\t\tReadParameterList(tag, dom);\n\t}\n}\n\nvoid FEBioMeshDomainsSection::ParseShellDomainSection(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFEBModel& feb = GetBuilder()->GetFEBModel();\n\tFEBModel::Part* part = feb.GetPart(0); assert(part);\n\tif (part == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t// get the domain name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::Domain* partDomain = part->FindDomain(szname);\n\tif (partDomain == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"name\", szname);\n\n\t// get the material name\n\tconst char* szmat = tag.AttributeValue(\"mat\");\n\tFEMaterial* mat = fem.FindMaterial(szmat);\n\tif (mat == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"mat\", szmat);\n\n\t// set the material name\n\tpartDomain->SetMaterialName(szmat);\n\n    // see if the element type is specified\n    const char* szelem = tag.AttributeValue(\"elem_type\", true);\n    if (szelem)\n    {\n        FE_Element_Spec elemSpec = partDomain->ElementSpec();\n        FE_Element_Spec newSpec = GetBuilder()->ElementSpec(szelem);\n        \n        // make sure it's valid\n        if ((FEElementLibrary::IsValid(newSpec) == false) || (elemSpec.eshape != newSpec.eshape))\n        {\n            throw XMLReader::InvalidAttributeValue(tag, \"elem_type\", szelem);\n        }\n        \n        partDomain->SetElementSpec(newSpec);\n    }\n    \n\t// see if the three_field flag is defined\n\tconst char* sz3field = tag.AttributeValue(\"three_field\", true);\n\tif (sz3field)\n\t{\n\t\tif (strcmp(sz3field, \"on\") == 0)\n\t\t{\n\t\t\tFE_Element_Spec espec = partDomain->ElementSpec();\n\t\t\tespec.m_bthree_field = true;\n\t\t\tpartDomain->SetElementSpec(espec);\n\t\t}\n\t\telse if (strcmp(sz3field, \"off\") == 0)\n\t\t{\n\t\t\tFE_Element_Spec espec = partDomain->ElementSpec();\n\t\t\tespec.m_bthree_field = false;\n\t\t\tpartDomain->SetElementSpec(espec);\n\t\t}\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"three_field\", sz3field);\n\t}\n\n\t// --- build the domain --- \n\t// we'll need the kernel for creating domains\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\n\t// element count\n\tint elems = partDomain->Elements();\n\n\t// get the element spect\n\tFE_Element_Spec spec = partDomain->ElementSpec();\n\n\t// create the domain\n\tFEDomain* dom = febio.CreateDomain(spec, &mesh, mat);\n\tif (dom == 0) throw XMLReader::InvalidTag(tag);\n\n\tmesh.AddDomain(dom);\n\n\tif (dom->Create(elems, spec) == false)\n\t{\n\t\tthrow XMLReader::InvalidTag(tag);\n\t}\n\n\t// assign the material\n\tdom->SetMatID(mat->GetID() - 1);\n\n\t// get the part name\n\tstring partName = part->Name();\n\tif (partName.empty() == false) partName += \".\";\n\n\tstring domName = partName + partDomain->Name();\n\tdom->SetName(domName);\n\n\t// process element data\n\tfor (int j = 0; j < elems; ++j)\n\t{\n\t\tconst FEBModel::ELEMENT& domElement = partDomain->GetElement(j);\n\n\t\tFEElement& el = dom->ElementRef(j);\n\t\tel.SetID(domElement.id);\n\n\t\t// TODO: This assumes one-based indexing of all nodes!\n\t\tint ne = el.Nodes();\n\t\tfor (int n = 0; n < ne; ++n) el.m_node[n] = domElement.node[n] - 1;\n\t}\n\n\t// read additional parameters\n\tif (tag.isleaf() == false)\n\t{\n\t\tReadParameterList(tag, dom);\n\t}\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n#include \"FEBModel.h\"\n\n//-----------------------------------------------------------------------------\n// Mesh section\nclass FEBioMeshSection : public FEBioFileSection\n{\npublic:\n\tFEBioMeshSection(FEBioImport* pim);\n\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodeSection       (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseSurfaceSection    (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseElementSection    (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseNodeSetSection    (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseElementSetSection (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseEdgeSection       (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseSurfacePairSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseDiscreteSetSection(XMLTag& tag, FEBModel::Part* part);\n};\n\n//-----------------------------------------------------------------------------\n// MeshDomains section\nclass FEBioMeshDomainsSection : public FEBioFileSection\n{\npublic:\n\tFEBioMeshDomainsSection(FEBioImport* pim);\n\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseSolidDomainSection(XMLTag& tag);\n\tvoid ParseShellDomainSection(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioMeshSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioMeshSection4.h\"\n#include <FECore/FEModel.h>\n#include <sstream>\n\n//-----------------------------------------------------------------------------\nFEBioMeshSection4::FEBioMeshSection4(FEBioImport* pim) : FEBioFileSection(pim) \n{\n\tm_maxNodeId = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::Parse(XMLTag& tag)\n{\n\tFEModelBuilder* builder = GetBuilder();\n\tbuilder->m_maxid = 0;\n\tm_maxNodeId = 0;\n\n\t// create a default part\n\t// NOTE: Do not specify a name for the part, otherwise\n\t//       all lists will be given the name: partname.listname\n\tFEBModel& feb = builder->GetFEBModel();\n\tassert(feb.Parts() == 0);\n\tFEBModel::Part* part = feb.AddPart(\"\");\n\n\t// read all sections\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"Nodes\"      ) ParseNodeSection       (tag, part);\n\t\telse if (tag == \"Elements\"   ) ParseElementSection    (tag, part);\n\t\telse if (tag == \"NodeSet\"    ) ParseNodeSetSection    (tag, part);\n\t\telse if (tag == \"Surface\"    ) ParseSurfaceSection    (tag, part);\n\t\telse if (tag == \"Edge\"       ) ParseEdgeSection       (tag, part);\n\t\telse if (tag == \"ElementSet\" ) ParseElementSetSection (tag, part);\n\t\telse if (tag == \"PartList\"   ) ParsePartListSection   (tag, part);\n\t\telse if (tag == \"SurfacePair\") ParseSurfacePairSection(tag, part);\n\t\telse if (tag == \"DiscreteSet\") ParseDiscreteSetSection(tag, part);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Nodes section of the FEBio input file\nvoid FEBioMeshSection4::ParseNodeSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tint N0 = mesh.Nodes();\n\n\t// see if this list defines a set\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tFEBModel::NodeSet* ps = 0;\n\tif (szname)\n\t{\n\t\tps = new FEBModel::NodeSet(szname);\n\t\tpart->AddNodeSet(ps);\n\t}\n\n\t// allocate node\n\n\tvector<FEBModel::NODE> node; node.reserve(10000);\n\tvector<int> nodeList; nodeList.reserve(10000);\n\n\t// read nodal coordinates\n\t++tag;\n\tdo {\n\t\t// nodal coordinates\n\t\tFEBModel::NODE nd;\n\t\tvalue(tag, nd.r);\n\n\t\t// get the nodal ID\n\t\ttag.AttributeValue(\"id\", nd.id);\n\n\t\t// make sure node IDs are incrementing\n\t\tif (nd.id <= m_maxNodeId) throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\t\tm_maxNodeId = nd.id;\n\n\t\t// add it to the pile\n\t\tnode.push_back(nd);\n\t\tnodeList.push_back(nd.id);\n\n\t\t// go on to the next node\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// add nodes to the part\n\tpart->AddNodes(node);\n\n\t// If a node set is defined add these nodes to the node-set\n\tif (ps) ps->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads the Element section from the FEBio input file. It also\n//! creates the domain classes which store the element data. A domain is defined\n//! by the module (structural, poro, heat, etc), the element type (solid, shell,\n//! etc.) and the material. \n//!\nvoid FEBioMeshSection4::ParseElementSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the (required!) name\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// get the element spec\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFE_Element_Spec espec = GetBuilder()->ElementSpec(sztype);\n\tif (FEElementLibrary::IsValid(espec) == false) throw FEBioImport::InvalidElementType();\n\n\t// make sure the domain does not exist yet\n\tFEBModel::Domain* dom = part->FindDomain(szname);\n\tif (dom)\n\t{\n\t\tstringstream ss;\n\t\tss << \"Duplicate part name found : \" << szname;\n\t\tthrow std::runtime_error(ss.str());\n\t}\n\n\t// create the new domain\n\tdom = new FEBModel::Domain(espec);\n\tif (szname) dom->SetName(szname);\n\n\t// add domain it to the mesh\n\tpart->AddDomain(dom);\n\n\t// for named domains, we'll also create an element set\n\tFEBModel::ElementSet* pg = 0;\n\tif (szname)\n\t{\n\t\tpg = new FEBModel::ElementSet(szname);\n\t\tpart->AddElementSet(pg);\n\t}\n\n\tdom->Reserve(10000);\n\tvector<int> elemList; elemList.reserve(10000);\n\n\t// keep track of largest ID\n\t// we need to enforce that element IDs are increasing\n\t// (This is currently only done for each domain. Need to modify this\n\t// so it's done on the whole model.)\n\tint maxID = -1;\n\n\t// read element data\n\t++tag;\n\tdo\n\t{\n\t\tFEBModel::ELEMENT el;\n\n\t\t// get the element ID\n\t\ttag.AttributeValue(\"id\", el.id);\n\n\t\tif ((maxID == -1) || (el.id > maxID)) maxID = el.id;\n\t\telse throw XMLReader::InvalidAttributeValue(tag, \"id\");\n\n\t\t// read the element data\n\t\ttag.value(el.node, FEElement::MAX_NODES);\n\n\t\tdom->AddElement(el);\n\t\telemList.push_back(el.id);\n\n\t\t// go to next tag\n\t\t++tag;\n\t} while (!tag.isend());\n\n\t// set the element list\n\tif (pg) pg->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\n//! Reads the Geometry::Groups section of the FEBio input file\nvoid FEBioMeshSection4::ParseNodeSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// make sure it's a leaf\n\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t// see if a nodeset with this name already exists\n\tFEBModel::NodeSet* set = part->FindNodeSet(szname);\n\tif (set) throw FEBioImport::RepeatedNodeSet(szname);\n\n\t// read the node IDs\n\tvector<int> nodeList;\n\ttag.value(nodeList);\n\n\t// create the node set\n\tset = new FEBModel::NodeSet(szname);\n\tpart->AddNodeSet(set);\n\n\t// add nodes to the list\n\tset->SetNodeList(nodeList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::ParseSurfaceSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if this surface was already defined\n\tFEBModel::Surface* ps = part->FindSurface(szname);\n\tif (ps) throw FEBioImport::RepeatedSurface(szname);\n\n\t// allocate storage for faces\n\tps = new FEBModel::Surface(szname);\n\tpart->AddSurface(ps);\n\n\tif (tag.isleaf() || tag.isempty()) return;\n\n\t// count nr of faces\n\tint faces = tag.children();\n\tps->Create(faces);\n\n\t// read faces\n\t++tag;\n\tfor (int i = 0; i < faces; ++i)\n\t{\n\t\tFEBModel::FACET& face = ps->GetFacet(i);\n\n\t\t// get the ID (although we don't really use this)\n\t\ttag.AttributeValue(\"id\", face.id);\n\n\t\t// set the facet type\n\t\tif (tag == \"quad4\") face.ntype = 4;\n\t\telse if (tag == \"tri3\") face.ntype = 3;\n\t\telse if (tag == \"tri6\") face.ntype = 6;\n\t\telse if (tag == \"tri7\") face.ntype = 7;\n\t\telse if (tag == \"quad8\") face.ntype = 8;\n\t\telse if (tag == \"quad9\") face.ntype = 9;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the facet type also defines the number of nodes\n\t\tint N = face.ntype;\n\t\ttag.value(face.node, N);\n\n\t\t++tag;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::ParseElementSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if this element set was already defined\n\tFEBModel::ElementSet* ps = part->FindElementSet(szname);\n\tif (ps) throw FEBioImport::RepeatedElementSet(szname);\n\n\t// allocate storage for faces\n\tps = new FEBModel::ElementSet(szname);\n\tpart->AddElementSet(ps);\n\n\t// read elements\n\tvector<int> elemList;\n\ttag.value(elemList);\n\n\tif (elemList.empty()) throw XMLReader::InvalidTag(tag);\n\n\tps->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::ParsePartListSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if this part list was already defined\n\tFEBModel::PartList* ps = part->FindPartList(szname);\n\tif (ps) throw FEBioImport::RepeatedPartList(szname);\n\n\t// create a new part list\n\tps = new FEBModel::PartList(szname);\n\tpart->AddPartList(ps);\n\n\t// read the part names\n\tvector<string> partList;\n\ttag.value(partList);\n\tif (partList.empty()) throw XMLReader::InvalidTag(tag);\n\n\tps->SetPartList(partList);\n\n\t// we'll also create an element set of this\n\tFEBModel::ElementSet* es = new FEBModel::ElementSet(\"@part_list:\" + string(szname));\n\tpart->AddElementSet(es);\n\n\tvector<int> elemList;\n\tfor (string s : partList)\n\t{\n\t\tFEBModel::ElementSet* a = part->FindElementSet(s);\n\t\tif (a == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\n\t\tconst vector<int>& aList = a->ElementList();\n\t\telemList.insert(elemList.end(), aList.begin(), aList.end());\n\t}\n\tes->SetElementList(elemList);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::ParseEdgeSection(XMLTag& tag, FEBModel::Part* part)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// get the required name attribute\n\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t// see if this edge was already defined\n\tFEBModel::EdgeSet* ps = part->FindEdgeSet(szname);\n\tif (ps) throw FEBioImport::RepeatedEdgeSet(szname);\n\n\t// count nr of edges\n\tint edges = tag.children();\n\n\t// allocate storage for edges\n\tps = new FEBModel::EdgeSet(szname);\n\tpart->AddEdgeSet(ps);\n\t\n\n\t// read edges\n\tvector<FEBModel::EDGE> edgeSet(edges);\n\t++tag;\n\tfor (int i = 0; i < edges; ++i)\n\t{\n\t\tFEBModel::EDGE& edge = edgeSet[i];\n\n\t\t// get the ID (although we don't really use this)\n\t\ttag.AttributeValue(\"id\", edge.id);\n\n\t\t// set the facet type\n\t\tif      (tag == \"line2\") edge.ntype = 2;\n\t\telse if (tag == \"line3\") edge.ntype = 3;\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t// we assume that the facet type also defines the number of nodes\n\t\tint N = edge.ntype;\n\t\ttag.value(edge.node, N);\n\n\t\t++tag;\n\t}\n\n\tps->SetEdgeList(edgeSet);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::ParseSurfacePairSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::SurfacePair* surfPair = new FEBModel::SurfacePair;\n\tsurfPair->m_name = szname;\n\tpart->AddSurfacePair(surfPair);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"primary\")\n\t\t{\n\t\t\tconst char* sz = tag.szvalue();\n\t\t\tFEBModel::Surface* surf = part->FindSurface(sz);\n\t\t\tif (surf == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\tsurfPair->m_primary = surf->Name();\n\t\t}\n\t\telse if (tag == \"secondary\")\n\t\t{\n\t\t\tconst char* sz = tag.szvalue();\n\t\t\tFEBModel::Surface* surf = part->FindSurface(sz);\n\t\t\tif (surf == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\tsurfPair->m_secondary = surf->Name();\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} \n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioMeshSection4::ParseDiscreteSetSection(XMLTag& tag, FEBModel::Part* part)\n{\n\tconst char* szname = tag.AttributeValue(\"name\");\n\tFEBModel::DiscreteSet* dset = new FEBModel::DiscreteSet;\n\tdset->SetName(szname);\n\tpart->AddDiscreteSet(dset);\n\n\tint n[2];\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"delem\")\n\t\t{\n\t\t\ttag.value(n, 2);\n\t\t\tdset->AddElement(n[0], n[1]);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioMeshSection4.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n#include \"FEBModel.h\"\n\n//-----------------------------------------------------------------------------\n// Mesh section\nclass FEBioMeshSection4 : public FEBioFileSection\n{\npublic:\n\tFEBioMeshSection4(FEBioImport* pim);\n\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseNodeSection       (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseSurfaceSection    (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseElementSection    (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseNodeSetSection    (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseElementSetSection (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParsePartListSection   (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseEdgeSection       (XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseSurfacePairSection(XMLTag& tag, FEBModel::Part* part);\n\tvoid ParseDiscreteSetSection(XMLTag& tag, FEBModel::Part* part);\n\nprivate:\n\tint m_maxNodeId;\n};\n"
  },
  {
    "path": "FEBioXML/FEBioModuleSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioModuleSection.h\"\n\n//-----------------------------------------------------------------------------\n//! This function parses the Module section.\n//! The Module defines the type of problem the user wants to solve (solid, heat, ...)\n//! It is currently only used to allocate the FESolver.\nvoid FEBioModuleSection::Parse(XMLTag &tag)\n{\n\tint nversion = GetFileReader()->GetFileVersion();\n\n\t// For version 2.5 and up this tag can only be read in once and we \n\t// determine that by inspecting the m_szmod member.\n\tif (nversion >= 0x0205)\n\t{\n\t\tstd::string moduleName = GetBuilder()->GetModuleName();\n\t\tif (moduleName.empty() == false) throw XMLReader::InvalidTag(tag);\n\t}\n\n\t// get the type attribute\n\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t// some special case\n\tif (strcmp(szt, \"explicit-solid\") == 0)\n\t{\n\t\tszt = \"solid\";\n\t\tGetBuilder()->SetDefaultSolver(\"explicit-solid\");\n\t}\n\telse if (strcmp(szt, \"CG-solid\") == 0)\n\t{\n\t\tszt = \"solid\";\n\t\tGetBuilder()->SetDefaultSolver(\"CG-solid\");\n\t}\n\n\tGetBuilder()->SetActiveModule(szt);\n\n\tif (tag.isempty() || tag.isleaf()) return;\n\n\t++tag;\n\tdo {\n\t\tif (tag == \"units\")\n\t\t{\n\t\t\tconst char* szunits = tag.szvalue();\n\t\t\tGetFEModel()->SetUnits(szunits);\n\t\t}\n\t\t\n\t\t++tag;\n\t} while (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioModuleSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// FEBio Module Section\nclass FEBioModuleSection : public FEBioFileSection\n{\npublic:\n\tFEBioModuleSection(FEBioImport* pim) : FEBioFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioOutputSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioOutputSection.h\"\n#include <FECore/SurfaceDataRecord.h>\n#include <FECore/DomainDataRecord.h>\n#include <FECore/FEModelDataRecord.h>\n#include <FECore/FEModel.h>\n#include <FECore/FSPath.h>\n#include <FECore/FEPlotDataStore.h>\n#include <FECore/FESurface.h>\n\nbool string_to_int_vector(const char* szlist, std::vector<int>& list)\n{\n\tlist.clear();\n\tif (szlist == nullptr) return false;\n\tif (szlist[0] == 0) return true;\n\n\tchar* ch = nullptr;\n\tchar* sz = (char*)szlist;\n\tdo\n\t{\n\t\tch = strchr(sz, ',');\n\t\tif (ch) *ch = 0;\n\t\tint n0, n1, nn;\n\t\tint nread = sscanf(sz, \"%d:%d:%d\", &n0, &n1, &nn);\n\t\tswitch (nread)\n\t\t{\n\t\tcase 0: return false;\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tn1 = n0;\n\t\t\tnn = 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tnn = 1;\n\t\t}\n\n\t\tfor (int i = n0; i <= n1; i += nn) list.push_back(i);\n\n\t\tif (ch) *ch = ',';\n\t\tsz = ch + 1;\n\t}\n\twhile (ch != 0);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioOutputSection::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"logfile\" ) ParseLogfile(tag);\n\t\telse if (tag == \"plotfile\") ParsePlotfile(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioOutputSection::ParseLogfile(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// Get the feb file path\n\tconst char* szpath = GetFileReader()->GetFilePath();\n\n\t// see if the log file has any attributes\n\tconst char* szlog = tag.AttributeValue(\"file\", true);\n\n\t// If the log filename is not a path, but just a filename, make the filename\n\t// relative to the input file, otherwise, leave the path alone.\n\tif(szlog)\n\t{\n\t\tif(!FSPath::isPath(szlog))\n\t\t{\n\t\t\tchar szfile[1024] = {0};\n\t\t\tsnprintf(szfile, sizeof(szfile), \"%s%s\", szpath, szlog);\n\t\t\tGetFEBioImport()->SetLogfileName(szfile);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tGetFEBioImport()->SetLogfileName(szlog);\n\t\t}\n\n\t}\n\n\tif (tag.isleaf()) return;\n\t++tag;\n\tdo\n\t{\n\t\t// get the (optional) file attribute\n\t\tchar szfilename[1024] = { 0 };\n\t\tconst char* szfile = tag.AttributeValue(\"file\", true);\n\t\tif (szfile)\n\t\t{\n\t\t\t// if we have a path, prepend the path's name\n\t\t\tif (szpath && szpath[0])\n\t\t\t{\n\t\t\t\tsnprintf(szfilename, sizeof(szfilename), \"%s%s\", szpath, szfile);\n\t\t\t}\n\t\t\telse strcpy(szfilename, szfile);\n\t\t\tszfile = szfilename;\n\t\t}\n\n\t\t// get other attributes\n\t\tconst char* szdelim = tag.AttributeValue(\"delim\", true);\n\t\tconst char* szformat = tag.AttributeValue(\"format\", true);\n\n\t\tbool bcomment = true;\n\t\tconst char* szcomment = tag.AttributeValue(\"comments\", true);\n\t\tif (szcomment != 0)\n\t\t{\n\t\t\tif (strcmp(szcomment, \"on\") == 0) bcomment = true;\n\t\t\telse if (strcmp(szcomment, \"off\") == 0) bcomment = false;\n\t\t}\n\n\t\t// get the data attribute\n\t\tconst char* szdata = tag.AttributeValue(\"data\");\n\n\t\t// get the name attribute\n\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t\t// allocate data record\n\t\tDataRecord* pdr = nullptr;\n\t\tif (tag == \"node_data\")\n\t\t{\n\t\t\tpdr = fecore_new<DataRecord>(\"node_data\", &fem);\n\n\t\t\tconst char* sztmp = \"set\";\n\t\t\tif (GetFileReader()->GetFileVersion() >= 0x0205) sztmp = \"node_set\";\n\t\t\tconst char* sz = tag.AttributeValue(sztmp, true);\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tFENodeSet* pns = mesh.FindNodeSet(sz);\n\t\t\t\tif (pns == 0) throw XMLReader::InvalidAttributeValue(tag, sztmp, sz);\n\n\t\t\t\tvector<int> items;\n\t\t\t\tint n = pns->Size();\n\t\t\t\tassert(n);\n\t\t\t\titems.resize(n);\n\t\t\t\tfor (int i = 0; i < n; ++i) items[i] = (*pns)[i] + 1;\n\n\t\t\t\tpdr->SetItemList(items);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstd::vector<int> items;\n\t\t\t\tstring_to_int_vector(tag.szvalue(), items);\n\t\t\t\tpdr->SetItemList(items);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"face_data\")\n\t\t{\n\t\t\tpdr = fecore_new<DataRecord>(\"face_data\", &fem);\n\n\t\t\tconst char* sz = tag.AttributeValue(\"surface\");\n\t\t\tFESurface* surf = mesh.FindSurface(sz);\n\t\t\tif (surf == nullptr)\n\t\t\t{\n\t\t\t\tFEFacetSet* pfs = mesh.FindFacetSet(sz);\n\t\t\t\tif (pfs == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\n\t\t\t\tsurf = new FESurface(&fem);\n\t\t\t\tsurf->Create(*pfs);\n\t\t\t\tsurf->SetName(sz);\n\t\t\t\tsurf->Init();\n\t\t\t\tmesh.AddSurface(surf);\n\t\t\t}\n\n\t\t\tstd::vector<int> items;\n\t\t\tstring_to_int_vector(tag.szvalue(), items);\n\n\t\t\t// TODO: This is a bit of a hack, because the face data record needs an FEItemList, but FESurface is derived from that.\n\t\t\tFEFacetSet* fset = surf->GetFacetSet();\n\t\t\tfset->SetSurface(surf);\n\t\t\tpdr->SetItemList(fset, items);\n\t\t}\n\t\telse if (tag == \"element_data\")\n\t\t{\n\t\t\tpdr = fecore_new<DataRecord>(\"element_data\", &fem);\n\n\t\t\tconst char* sztmp = \"elset\";\n\t\t\tif (GetFileReader()->GetFileVersion() >= 0x0205) sztmp = \"elem_set\";\n\n\t\t\tconst char* sz = tag.AttributeValue(sztmp, true);\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tvector<int> dummy;\n\t\t\t\tFEElementSet* pes = mesh.FindElementSet(sz);\n\t\t\t\tif (pes == 0) throw XMLReader::InvalidAttributeValue(tag, sztmp, sz);\n\t\t\t\tpdr->SetItemList(pes, dummy);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstd::vector<int> items;\n\t\t\t\tstring_to_int_vector(tag.szvalue(), items);\n\t\t\t\tpdr->SetItemList(items);\n\t\t\t}\n\t\t}\n\t\telse if (tag == \"rigid_body_data\")\n\t\t{\n\t\t\tpdr = fecore_new<DataRecord>(\"rigid_body_data\", &fem);\n\t\t\tstd::vector<int> items;\n\t\t\tstring_to_int_vector(tag.szvalue(), items);\n\t\t\tpdr->SetItemList(items);\n\t\t}\n        else if (tag == \"rigid_connector_data\")\n        {\n\t\t\tpdr = fecore_new<DataRecord>(\"rigid_connector_data\", &fem);\n\t\t\tstd::vector<int> items;\n\t\t\tstring_to_int_vector(tag.szvalue(), items);\n\t\t\tpdr->SetItemList(items);\n        }\n        else if (tag == \"surface_data\")\n        {\n            FESurfaceDataRecord* prec = new FESurfaceDataRecord(&fem);\n\t\t\tpdr = prec;\n\t\t\tconst char* sz = tag.AttributeValue(\"surface\");\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tint surfIndex = mesh.FindSurfaceIndex(sz);\n\t\t\t\tif (surfIndex == -1) throw XMLReader::InvalidAttributeValue(tag, \"surface\", sz);\n\t\t\t\tprec->SetSurface(surfIndex);\n\t\t\t}\n        }\n        else if (tag == \"domain_data\")\n        {\n            FEDomainDataRecord* prec = new FEDomainDataRecord(&fem);\n\t\t\tpdr = prec;\n\t\t\tconst char* sz = tag.AttributeValue(\"domain\", true);\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tint domainIndex = mesh.FindDomainIndex(sz);\n\t\t\t\tif (domainIndex == -1) throw XMLReader::InvalidAttributeValue(tag, \"domain\", sz);\n\t\t\t\tprec->SetDomain(domainIndex);\n\t\t\t}\n        }\n        else if (tag == \"model_data\")\n        {\n            pdr = new FEModelDataRecord(&fem);\n        }\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\tif (pdr)\n\t\t{\n\t\t\tpdr->SetData(szdata);\n\t\t\tif (szname != 0) pdr->SetName(szname); else pdr->SetName(szdata);\n\t\t\tif (szfile) pdr->SetFileName(szfile);\n\t\t\tif (szdelim != 0) pdr->SetDelim(szdelim);\n\t\t\tif (szformat != 0) pdr->SetFormat(szformat);\n\t\t\tpdr->SetComments(bcomment);\n\t\t\tGetFEBioImport()->AddDataRecord(pdr);\n\t\t}\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioOutputSection::ParsePlotfile(XMLTag &tag)\n{\n\tFEModel& fem = *GetFEModel();\n\n\tFEPlotDataStore& plotData = fem.GetPlotDataStore();\n\n\t// get the plot file type. Must be \"febio\"!\n\tconst char* sz = tag.AttributeValue(\"type\", true);\n\tif (sz)\n\t{\n\t\tif ((strcmp(sz, \"febio\" ) != 0) && \n\t\t\t(strcmp(sz, \"vtk\"   ) != 0)) throw XMLReader::InvalidAttributeValue(tag, \"type\", sz);\n\t}\n\telse sz = \"febio\";\n\tplotData.SetPlotFileType(sz);\n\n\t// get the optional plot file name\n\tconst char* szplt = tag.AttributeValue(\"file\", true);\n\n\t// If the plot filename is not a path, but just a filename, make the filename\n\t// relative to the input file, otherwise, leave the path alone.\n\tif(szplt)\n\t{\n\t\tif(!FSPath::isPath(szplt))\n\t\t{\n\t\t\t// Get the feb file path\n\t\t\tconst char* szpath = GetFileReader()->GetFilePath();\n\n\t\t\tchar szfile[1024] = {0};\n\t\t\tsnprintf(szfile, sizeof(szfile), \"%s%s\", szpath, szplt);\n\t\t\tGetFEBioImport()->SetPlotfileName(szfile);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tGetFEBioImport()->SetPlotfileName(szplt);\n\t\t}\n\n\t}\n\n\t// read and store the plot variables\n\tif (!tag.isleaf())\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (tag == \"var\")\n\t\t\t{\n\t\t\t\t// get the variable name\n\t\t\t\tconst char* szt = tag.AttributeValue(\"type\");\n\n\t\t\t\t// get the item list\n\t\t\t\tvector<int> item;\n\t\t\t\tif (tag.isempty() == false) tag.value(item);\n\n                // see if a surface is referenced\n                const char* szsurf = tag.AttributeValue(\"surface\", true);\n                const char* szeset = tag.AttributeValue(\"elem_set\", true);\n                if (szsurf)\n                {\n                    // make sure this tag does not have any children\n                    if (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n                    \n                    // see if we can find the facet set\n                    FEMesh& m = GetFEModel()->GetMesh();\n\t\t\t\t\tFEFacetSet* ps = m.FindFacetSet(szsurf);\n                    \n                    // create a surface from the facet set\n                    if (ps)\n                    {\n                        // create a new surface\n                        FESurface* psurf = fecore_alloc(FESurface, &fem);\n                        fem.GetMesh().AddSurface(psurf);\n                        if (GetBuilder()->BuildSurface(*psurf, *ps) == false) throw XMLReader::InvalidTag(tag);\n\n                        // Add the plot variable\n                        const std::string& surfName = psurf->GetName();\n\t\t\t\t\t\tplotData.AddPlotVariable(szt, item, surfName.c_str());\n                    }\n                    else throw XMLReader::InvalidAttributeValue(tag, \"surface\", szsurf);\n                }\n\t\t\t\telse if (szeset)\n\t\t\t\t{\n\t\t\t\t\t// make sure this tag does not have any children\n\t\t\t\t\tif (!tag.isleaf()) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\t// see if we can find the facet set\n\t\t\t\t\tFEMesh& m = GetFEModel()->GetMesh();\n\t\t\t\t\tFEElementSet* ps = m.FindElementSet(szeset);\n\n\t\t\t\t\t// create a surface from the facet set\n\t\t\t\t\tif (ps)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Add the plot variable\n\t\t\t\t\t\tplotData.AddPlotVariable(szt, item, szeset);\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"elem_set\", szeset);\n\t\t\t\t}\n                else\n                {\n                    // Add the plot variable\n\t\t\t\t\tplotData.AddPlotVariable(szt, item);\n                }\n\t\t\t}\n\t\t\telse if (tag==\"compression\")\n\t\t\t{\n\t\t\t\tint ncomp;\n\t\t\t\ttag.value(ncomp);\n\t\t\t\tplotData.SetPlotCompression(ncomp);\n\t\t\t}\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n}\n"
  },
  {
    "path": "FEBioXML/FEBioOutputSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\nclass FEFacetSet;\n\n//-----------------------------------------------------------------------------\n// Output Section\nclass FEBioOutputSection : public FEBioFileSection\n{\npublic:\n\tFEBioOutputSection(FEBioImport* pim) : FEBioFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseLogfile    (XMLTag& tag);\n\tvoid ParsePlotfile   (XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioRigidSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioRigidSection.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FEBoundaryCondition.h>\n\nvoid FEBioRigidSection::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"rigid_constraint\") ParseRigidBC(tag);\n\t\telse if (tag == \"rigid_connector\" ) ParseRigidConnector(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} \n\twhile (!tag.isend());\n}\n\nvoid FEBioRigidSection::ParseRigidBC(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\tFEModelBuilder& feb = *GetBuilder();\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tif (strcmp(sztype, \"fix\") == 0)\n\t{\n\t\t// create the fixed dof\n\t\tFEBoundaryCondition* pBC = fecore_new_class<FEBoundaryCondition>(\"FERigidFixedBCOld\", fem);\n\t\tfeb.AddRigidComponent(pBC);\n\t\tReadParameterList(tag, pBC);\n\t}\n\telse if (strcmp(sztype, \"prescribe\") == 0)\n\t{\n\t\t// create the rigid displacement constraint\n\t\tFEBoundaryCondition* pDC = fecore_new_class<FEBoundaryCondition>(\"FERigidPrescribedOld\", fem);\n\t\tfeb.AddRigidComponent(pDC);\n\t\tReadParameterList(tag, pDC);\n\t}\n\telse if (strcmp(sztype, \"force\") == 0)\n\t{\n\t\tstring name;\n\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\tif (szname) name = szname;\n\n\t\t// we need to decide whether we want to apply a force or a moment, since these\n\t\t// are now two separate classes. Unfortunately, this means we first need to \n\t\t// read all parameters, before we can allocate the correct class. \n\t\tint rb = -1;\n\t\tint ntype = 0;\n\t\tint bc = -1;\n\t\tdouble val = 0.;\n\t\tbool brel = false;\n\t\tint lc = -1;\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif      (tag == \"rb\"       ) tag.value(rb);\n\t\t\telse if (tag == \"value\")\n\t\t\t{\n\t\t\t\ttag.value(val);\n\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\", true);\n\t\t\t\tif (szlc) lc = atoi(szlc) - 1;\n\t\t\t}\n\t\t\telse if (tag == \"load_type\") tag.value(ntype);\n\t\t\telse if (tag == \"relative\" ) tag.value(brel);\n\t\t\telse if (tag == \"dof\"      )\n\t\t\t{\n\t\t\t\tconst char* sz = tag.szvalue();\n\t\t\t\tif ((strcmp(sz, \"Rx\") == 0) || (strcmp(sz, \"0\") == 0)) bc = 0;\n\t\t\t\tif ((strcmp(sz, \"Ry\") == 0) || (strcmp(sz, \"1\") == 0)) bc = 1;\n\t\t\t\tif ((strcmp(sz, \"Rz\") == 0) || (strcmp(sz, \"2\") == 0)) bc = 2;\n\t\t\t\tif ((strcmp(sz, \"Ru\") == 0) || (strcmp(sz, \"3\") == 0)) bc = 3;\n\t\t\t\tif ((strcmp(sz, \"Rv\") == 0) || (strcmp(sz, \"4\") == 0)) bc = 4;\n\t\t\t\tif ((strcmp(sz, \"Rw\") == 0) || (strcmp(sz, \"5\") == 0)) bc = 5;\n\n\t\t\t\tif (bc < 0)\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\n\t\tFEModelLoad* pFC = nullptr;\n\t\tif (bc < 3)\n\t\t{\n\t\t\tpFC = fecore_new_class<FEModelLoad>(\"FERigidBodyForce\", fem);\n\t\t\tpFC->SetParameter(\"load_type\", ntype);\n\t\t\tpFC->SetParameter(\"rb\", rb);\n\t\t\tpFC->SetParameter(\"dof\", bc);\n\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\tpFC->SetParameter(\"relative\", brel);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpFC = fecore_new_class<FEModelLoad>(\"FERigidBodyMoment\", fem);\n\t\t\tpFC->SetParameter(\"rb\", rb);\n\t\t\tpFC->SetParameter(\"dof\", bc - 3);\n\t\t\tpFC->SetParameter(\"value\", val);\n\t\t\tpFC->SetParameter(\"relative\", brel);\n\t\t}\n\n\t\tif (lc >= 0)\n\t\t{\n\t\t\tFEParam* p = pFC->GetParameter(\"value\");\n\t\t\tif (p == nullptr) throw XMLReader::InvalidTag(tag);\n\t\t\tGetFEModel()->AttachLoadController(p, lc);\n\t\t}\n\n\t\tif (name.empty() == false)\n\t\t{\n\t\t\tpFC->SetName(name);\n\t\t}\n\n\t\tfeb.AddModelLoad(pFC);\n\t}\n\telse if (strcmp(sztype, \"initial_rigid_velocity\") == 0)\n\t{\n\t\tFEBoundaryCondition* pic = fecore_new_class<FEBoundaryCondition>(\"FERigidBodyVelocity\", fem);\n\t\tfeb.AddRigidComponent(pic);\n\t\tReadParameterList(tag, pic);\n\t}\n\telse if (strcmp(sztype, \"initial_rigid_angular_velocity\") == 0)\n\t{\n\t\tFEBoundaryCondition* pic = fecore_new_class<FEBoundaryCondition>(\"FERigidBodyAngularVelocity\", fem);\n\t\tfeb.AddRigidComponent(pic);\n\t\tReadParameterList(tag, pic);\n\t}\n    else if (strcmp(sztype, \"follower force\") == 0)\n    {\n        FEModelLoad* rc = fecore_new_class<FEModelLoad>(\"FERigidFollowerForce\", fem);\n        feb.AddModelLoad(rc);\n        ReadParameterList(tag, rc);\n    }\n    else if (strcmp(sztype, \"follower moment\") == 0)\n    {\n\t\tFEModelLoad* rc = fecore_new_class<FEModelLoad>(\"FERigidFollowerMoment\", fem);\n        feb.AddModelLoad(rc);\n        ReadParameterList(tag, rc);\n    }\n\telse\n\t{\n\t\t// create the rigid constraint\n\t\tFEBoundaryCondition* pBC = fecore_new<FEBoundaryCondition>(sztype, fem);\n\t\tif (pBC == nullptr)  throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\tfeb.AddRigidComponent(pBC);\n\t\tReadParameterList(tag, pBC);\n\t}\n}\n\nvoid FEBioRigidSection::ParseRigidConnector(XMLTag& tag)\n{\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tFENLConstraint* plc = fecore_new<FENLConstraint>(sztype, GetFEModel());\n\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) plc->SetName(szname);\n\n\t// read the parameter list\n\tReadParameterList(tag, plc);\n\n\t// add this constraint to the current step\n\tGetBuilder()->AddNonlinearConstraint(plc);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioRigidSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\nclass FEBioRigidSection : public FEFileSection\n{\npublic:\n\tFEBioRigidSection(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseRigidBC(XMLTag& tag);\n\tvoid ParseRigidConnector(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioRigidSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioRigidSection4.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FENLConstraint.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FEInitialCondition.h>\n\nvoid FEBioRigidSection4::Parse(XMLTag& tag)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"rigid_bc\"       ) ParseRigidBC(tag);\n\t\telse if (tag == \"rigid_ic\"       ) ParseRigidIC(tag);\n\t\telse if (tag == \"rigid_load\"     ) ParseRigidLoad(tag);\n\t\telse if (tag == \"rigid_connector\") ParseRigidConnector(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n\nvoid FEBioRigidSection4::ParseRigidBC(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\tFEModelBuilder& feb = *GetBuilder();\n\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tFEBoundaryCondition* bc = fecore_new<FEBoundaryCondition>(sztype, fem);\n\tif (szname) bc->SetName(szname);\n\n\tGetBuilder()->AddRigidComponent(bc);\n\tReadParameterList(tag, bc);\n}\n\nvoid FEBioRigidSection4::ParseRigidIC(XMLTag& tag)\n{\n\tFEModel* fem = GetFEModel();\n\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tFEInitialCondition* ic = fecore_new<FEInitialCondition>(sztype, fem);\n\tif (szname) ic->SetName(szname);\n\n\tGetBuilder()->AddRigidComponent(ic);\n\tReadParameterList(tag, ic);\n}\n\nvoid FEBioRigidSection4::ParseRigidLoad(XMLTag& tag)\n{\n\t// get the name\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\n\t// get the type\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tFEModelLoad* ml = fecore_new<FEModelLoad>(sztype, GetFEModel());\n\tif (szname) ml->SetName(szname);\n\n\tGetBuilder()->AddRigidComponent(ml);\n\tReadParameterList(tag, ml);\n}\n\nvoid FEBioRigidSection4::ParseRigidConnector(XMLTag& tag)\n{\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\tFENLConstraint* plc = fecore_new<FENLConstraint>(sztype, GetFEModel());\n\tif (plc == 0) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\tconst char* szname = tag.AttributeValue(\"name\", true);\n\tif (szname) plc->SetName(szname);\n\n\t// read the parameter list\n\tReadParameterList(tag, plc);\n\n\t// add this constraint to the current step\n\tGetBuilder()->AddNonlinearConstraint(plc);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioRigidSection4.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBioImport.h\"\n\nclass FEBioRigidSection4 : public FEFileSection\n{\npublic:\n\tFEBioRigidSection4(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n\nprotected:\n\tvoid ParseRigidBC(XMLTag& tag);\n\tvoid ParseRigidIC(XMLTag& tag);\n\tvoid ParseRigidLoad(XMLTag& tag);\n\tvoid ParseRigidConnector(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioScriptsSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioScriptsSection.h\"\n\nvoid FEBioScriptsSection::Parse(XMLTag& tag)\n{\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"script\")\n\t\t{\n\t\t\tconst char* szname = tag.AttributeValue(\"name\");\n\t\t\tconst char* szcode = tag.szvalue();\n\n\t\t\tif (GetFEModel()->AddScript(szname, szcode) == false)\n\t\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t} while (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioScriptsSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBioImport.h\"\n\nclass FEBioScriptsSection : public FEFileSection\n{\npublic:\n\tFEBioScriptsSection(FEFileImport* pim) : FEFileSection(pim) {}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioStepSection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioStepSection.h\"\n#include \"FEBioModuleSection.h\"\n#include \"FEBioControlSection.h\"\n#include \"FEBioConstraintsSection.h\"\n#include \"FEBioBoundarySection.h\"\n#include \"FEBioLoadsSection.h\"\n#include \"FEBioContactSection.h\"\n#include \"FEBioInitialSection.h\"\n\n//-----------------------------------------------------------------------------\nvoid FEBioStepSection::Parse(XMLTag& tag)\n{\n\t// create next step\n\tFEBioImport* feb = GetFEBioImport();\n\tfeb->GetBuilder()->NextStep();\n\n\tFEFileSectionMap Map;\n\tMap[\"Module\"     ] = new FEBioModuleSection       (feb);\n\tMap[\"Control\"    ] = new FEBioControlSection      (feb);\n\tMap[\"Constraints\"] = new FEBioConstraintsSection1x(feb);\n\tMap[\"Boundary\"   ] = new FEBioBoundarySection1x   (feb);\n\tMap[\"Loads\"      ] = new FEBioLoadsSection1x      (feb);\n\tMap[\"Initial\"    ] = new FEBioInitialSection      (feb);\n\n\t// parse the file sections\n\tMap.Parse(tag);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioStepSection2::Parse(XMLTag& tag)\n{\n\t// create next step\n\tFEBioImport* feb = GetFEBioImport();\n\tfeb->GetBuilder()->NextStep();\n\n\tFEFileSectionMap Map;\n\tMap[\"Module\"     ] = new FEBioModuleSection      (feb);\n\tMap[\"Control\"    ] = new FEBioControlSection     (feb);\n\tMap[\"Constraints\"] = new FEBioConstraintsSection2(feb);\n\tMap[\"Boundary\"   ] = new FEBioBoundarySection2   (feb);\n\tMap[\"Loads\"      ] = new FEBioLoadsSection2      (feb);\n\tMap[\"Initial\"    ] = new FEBioInitialSection     (feb);\n\tMap[\"Contact\"    ] = new FEBioContactSection2    (feb);\n\n\t// parse the file sections\n\tMap.Parse(tag);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBioStepSection25::Parse(XMLTag& tag)\n{\n\t// create next step\n\tGetBuilder()->NextStep();\n\n\tFEFileImport* imp = GetFileReader();\n\tFEFileSectionMap Map;\n\tMap[\"Control\"    ] = new FEStepControlSection     (imp);\n\tMap[\"Constraints\"] = new FEBioConstraintsSection25(imp);\n\tMap[\"Boundary\"   ] = new FEBioBoundarySection25   (imp);\n\tMap[\"Loads\"      ] = new FEBioLoadsSection25      (imp);\n\tMap[\"Initial\"    ] = new FEBioInitialSection25    (imp);\n\tMap[\"Contact\"    ] = new FEBioContactSection25    (imp);\n\n\t// parse the file sections\n\tMap.Parse(tag);\n}\n"
  },
  {
    "path": "FEBioXML/FEBioStepSection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Step Section (old format)\nclass FEBioStepSection : public FEBioFileSection\n{\npublic:\n\tFEBioStepSection(FEBioImport* pim) : FEBioFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Step Section (2.0 format)\nclass FEBioStepSection2 : public FEBioFileSection\n{\npublic:\n\tFEBioStepSection2(FEBioImport* pim) : FEBioFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n// Step Section (2.5 format)\nclass FEBioStepSection25 : public FEFileSection\n{\npublic:\n\tFEBioStepSection25(FEFileImport* pim) : FEFileSection(pim){}\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioStepSection3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBioStepSection3.h\"\n#include \"FEBioControlSection3.h\"\n#include \"FEBioInitialSection3.h\"\n#include \"FEBioBoundarySection3.h\"\n#include \"FEBioLoadsSection.h\"\n#include \"FEBioConstraintsSection.h\"\n#include \"FEBioContactSection.h\"\n#include \"FEBioRigidSection.h\"\n#include \"FEBioMeshAdaptorSection.h\"\n\n//-----------------------------------------------------------------------------\nFEBioStepSection3::FEBioStepSection3(FEFileImport* pim) : FEFileSection(pim) {}\n\n//-----------------------------------------------------------------------------\nvoid FEBioStepSection3::Parse(XMLTag& tag)\n{\n\t// Build the file section map\n\tFEFileImport* imp = GetFileReader();\n\tFEFileSectionMap Map;\n\tMap[\"Control\"    ] = new FEBioControlSection3(imp);\n\tMap[\"Initial\"    ] = new FEBioInitialSection3(imp);\n\tMap[\"Boundary\"   ] = new FEBioBoundarySection3(imp);\n\tMap[\"Loads\"      ] = new FEBioLoadsSection3(imp);\n\tMap[\"Constraints\"] = new FEBioConstraintsSection25(imp);\n\tMap[\"Contact\"    ] = new FEBioContactSection25(imp);\n\tMap[\"Rigid\"      ] = new FEBioRigidSection(imp);\n\tMap[\"MeshAdaptor\"] = new FEBioMeshAdaptorSection(imp);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"step\")\n\t\t{\n\t\t\t// create next step\n\t\t\tGetBuilder()->NextStep();\n\n\t\t\t// parse the file sections\n\t\t\tMap.Parse(tag);\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioStepSection3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Step Section (3.0 format)\nclass FEBioStepSection3 : public FEFileSection\n{\npublic:\n\tFEBioStepSection3(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEBioStepSection4.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBioStepSection4.h\"\n#include \"FEBioControlSection4.h\"\n#include \"FEBioInitialSection3.h\"\n#include \"FEBioBoundarySection3.h\"\n#include \"FEBioLoadsSection.h\"\n#include \"FEBioConstraintsSection.h\"\n#include \"FEBioContactSection.h\"\n#include \"FEBioRigidSection4.h\"\n#include \"FEBioMeshAdaptorSection.h\"\n\n//-----------------------------------------------------------------------------\nFEBioStepSection4::FEBioStepSection4(FEFileImport* pim) : FEFileSection(pim) {}\n\n//-----------------------------------------------------------------------------\nvoid FEBioStepSection4::Parse(XMLTag& tag)\n{\n\t// Build the file section map\n\tFEFileImport* imp = GetFileReader();\n\tFEFileSectionMap Map;\n\tMap[\"Control\"    ] = new FEBioControlSection4(imp);\n\tMap[\"Initial\"    ] = new FEBioInitialSection3(imp);\n\tMap[\"Boundary\"   ] = new FEBioBoundarySection3(imp);\n\tMap[\"Loads\"      ] = new FEBioLoadsSection4(imp);\n\tMap[\"Constraints\"] = new FEBioConstraintsSection25(imp);\n\tMap[\"Contact\"    ] = new FEBioContactSection25(imp);\n\tMap[\"Rigid\"      ] = new FEBioRigidSection4(imp);\n\tMap[\"MeshAdaptor\"] = new FEBioMeshAdaptorSection(imp);\n\n\t++tag;\n\tdo\n\t{\n\t\tif (tag == \"step\")\n\t\t{\n\t\t\t// create next step\n\t\t\tGetBuilder()->NextStep();\n\n\t\t\t// parse the file sections\n\t\t\tMap.Parse(tag);\n\t\t}\n\t\telse throw XMLReader::MissingTag(tag, \"step\");\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n"
  },
  {
    "path": "FEBioXML/FEBioStepSection4.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEBioImport.h\"\n\n//-----------------------------------------------------------------------------\n// Step Section (4.0 format)\nclass FEBioStepSection4 : public FEFileSection\n{\npublic:\n\tFEBioStepSection4(FEFileImport* pim);\n\tvoid Parse(XMLTag& tag);\n};\n"
  },
  {
    "path": "FEBioXML/FEModelBuilder.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEModelBuilder.h\"\n#include <FECore/FEMaterial.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/FEBoundaryCondition.h>\n#include <FECore/FENodalLoad.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEEdgeLoad.h>\n#include <FECore/FEInitialCondition.h>\n#include <FECore/FEModelLoad.h>\n#include <FECore/FELoadCurve.h>\n#include <FECore/FESurfaceMap.h>\n#include <FECore/FEDomainMap.h>\n#include <FECore/FEEdge.h>\n#include <FECore/FEConstValueVec3.h>\n#include <FECore/FEDataGenerator.h>\n#include <FECore/FEPointFunction.h>\n#include <FECore/FESurfacePairConstraint.h>\n#include <FECore/FENLConstraint.h>\n#include <sstream>\n\n//-----------------------------------------------------------------------------\nFEModelBuilder::FEModelBuilder(FEModel& fem) : m_fem(fem)\n{\n\tm_pStep = 0;\t// zero step pointer\n\tm_nsteps = 0;\t// reset step section counter\n\n\t// default element type\n\tm_ntet4  = FE_TET4G1;\n\tm_nhex8  = FE_HEX8G8;\n\tm_ntet10 = FE_TET10G8;\n\tm_ntet15 = FE_TET15G15;\n\tm_ntet20 = FE_TET20G15;\n\tm_ntri6  = FE_TRI6G7;\n\tm_ntri3  = FE_TRI3G3;\n\tm_ntri7  = FE_TRI7G7;\n\tm_ntri10 = FE_TRI10G7;\n\n\tm_nquad4 = FE_QUAD4G4;\n\tm_nquad8 = FE_QUAD8G9;\n\tm_nquad9 = FE_QUAD9G9;\n\n\t// 3-field formulation flags\n\tm_b3field_hex = true;\n\tm_b3field_tet = false;\n    m_b3field_shell = false;\n    m_b3field_quad = true;\n    m_b3field_tri = false;\n\n\t// shell formulation\n\tm_default_shell = NEW_SHELL;\n    m_shell_norm_nodal = true;\n\n\t// UT4 formulation off by default\n\tm_but4 = false;\n\tm_ut4_alpha = 0.05;\n\tm_ut4_bdev = false;\n\n\t// UDG hourglass parameter\n\tm_udghex_hg = 1.0;\n}\n\n//-----------------------------------------------------------------------------\nFEModelBuilder::~FEModelBuilder() \n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::SetActiveModule(const std::string& moduleName)\n{\n\tm_fem.SetActiveModule(moduleName);\n}\n\n//-----------------------------------------------------------------------------\n//! Get the module name\nstd::string FEModelBuilder::GetModuleName() const\n{\n\treturn m_fem.GetModuleName();\n}\n\n//-----------------------------------------------------------------------------\nFEAnalysis* FEModelBuilder::CreateNewStep(bool allocSolver)\n{\n\t// default analysis type should match module name\n\tstd::string modName = GetModuleName();\n\tFEAnalysis* pstep = fecore_new<FEAnalysis>(modName.c_str(), &m_fem);\n\n\t// make sure we have a solver defined\n\tFESolver* psolver = pstep->GetFESolver();\n\tif ((psolver == 0) && allocSolver)\n\t{\n\t\tpsolver = BuildSolver(m_fem);\n\t\tif (psolver == 0) return 0;\n\t\tpstep->SetFESolver(psolver);\n\t}\n\treturn pstep;\n}\n\n//-----------------------------------------------------------------------------\n// create a material\nFEMaterial* FEModelBuilder::CreateMaterial(const char* sztype)\n{\n\tFEMaterial* pmat = fecore_new<FEMaterial>(sztype, &m_fem);\n\treturn pmat;\n}\n\n//-----------------------------------------------------------------------------\nFESolver* FEModelBuilder::BuildSolver(FEModel& fem)\n{\n\tstring moduleName = fem.GetModuleName();\n\tconst char* sztype = moduleName.c_str();\n\tif (m_defaultSolver.empty() == false) sztype = m_defaultSolver.c_str();\n\tFESolver* ps = fecore_new<FESolver>(sztype, &fem);\n\treturn ps;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::NextStep()\n{\n\t// reset the step pointer\n\tif (m_nsteps != 0) m_pStep = nullptr;\n\n\t// increase the step section counter\n\t++m_nsteps;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the mesh\nFEMesh& FEModelBuilder::GetMesh()\n{\n\treturn m_fem.GetMesh();\n}\n\n//-----------------------------------------------------------------------------\n//! get the FE model\nFEModel& FEModelBuilder::GetFEModel()\n{\n\treturn m_fem;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a domain\nFEDomain* FEModelBuilder::CreateDomain(FE_Element_Spec espec, FEMaterial* mat)\n{\n\tFECoreKernel& febio = FECoreKernel::GetInstance();\n\tFEDomain* pdom = febio.CreateDomain(espec, &m_fem.GetMesh(), mat);\n\treturn pdom;\n}\n\n//-----------------------------------------------------------------------------\nFEAnalysis* FEModelBuilder::GetStep(bool allocSolver)\n{\n\tif (m_pStep == 0)\n\t{\n\t\tm_pStep = CreateNewStep(allocSolver);\n\t\tm_fem.AddStep(m_pStep);\n\t\tif (m_fem.Steps() == 1)\n\t\t{\n\t\t\tm_fem.SetCurrentStep(m_pStep);\n\t\t\tm_fem.SetCurrentStepIndex(0);\n\t\t}\n\t}\n\treturn m_pStep;\n}\n\nvoid FEModelBuilder::AddMaterial(FEMaterial* pmat)\n{\n\tm_fem.AddMaterial(pmat);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddComponent(FEStepComponent* pmc)\n{\n\tif (m_nsteps > 0)\n\t{\n\t\tGetStep()->AddStepComponent(pmc);\n\t\tpmc->Deactivate();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddBC(FEBoundaryCondition* pbc)\n{\n\tm_fem.AddBoundaryCondition(pbc);\n\tAddComponent(pbc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddNodalLoad(FENodalLoad* pfc)\n{\n\tm_fem.AddModelLoad(pfc);\n\tAddComponent(pfc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddSurfaceLoad(FESurfaceLoad* psl)\n{\n\tm_fem.AddModelLoad(psl);\n\tAddComponent(psl);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddEdgeLoad(FEEdgeLoad* pel)\n{\n\tm_fem.AddModelLoad(pel);\n\tAddComponent(pel);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddInitialCondition(FEInitialCondition* pic)\n{\n\tm_fem.AddInitialCondition(pic);\n\tAddComponent(pic);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddContactInterface(FESurfacePairConstraint* pci)\n{\n\tm_fem.AddSurfacePairConstraint(pci);\n\tAddComponent(pci);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddModelLoad(FEModelLoad* pml)\n{\n\tm_fem.AddModelLoad(pml);\n\tAddComponent(pml);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddNonlinearConstraint(FENLConstraint* pnc)\n{\n\tm_fem.AddNonlinearConstraint(pnc);\n\tAddComponent(pnc);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::AddRigidComponent(FEStepComponent* pmc) { assert(false); }\n\n//---------------------------------------------------------------------------------\n// parse a surface section for contact definitions\n//\nbool FEModelBuilder::BuildSurface(FESurface& s, FEFacetSet& fs, bool bnodal)\n{\n\tFEMesh& m = m_fem.GetMesh();\n\tint NN = m.Nodes();\n\n\t// count nr of faces\n\tint faces = fs.Faces();\n\n\t// allocate storage for faces\n\ts.Create(fs);\n\n\t// read faces\n\tfor (int i = 0; i<faces; ++i)\n\t{\n\t\tFESurfaceElement& el = s.Element(i);\n\t\tFEFacetSet::FACET& fi = fs.Face(i);\n\n\t\t// set the element type/integration rule\n\t\tif (bnodal)\n\t\t{\n\t\t\tif      (fi.ntype == 4) el.SetType(FE_QUAD4NI);\n\t\t\telse if (fi.ntype == 3) el.SetType(FE_TRI3NI);\n\t\t\telse if (fi.ntype == 6) el.SetType(FE_TRI6NI);\n\t\t\telse if (fi.ntype == 8) el.SetType(FE_QUAD8NI);\n\t\t\telse if (fi.ntype == 9) el.SetType(FE_QUAD9NI);\n\t\t\telse return false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (fi.ntype ==  3) el.SetType(m_ntri3);\n\t\t\telse if (fi.ntype ==  4) el.SetType(m_nquad4);\n\t\t\telse if (fi.ntype ==  6) el.SetType(m_ntri6);\n\t\t\telse if (fi.ntype ==  7) el.SetType(m_ntri7);\n\t\t\telse if (fi.ntype == 10) el.SetType(m_ntri10);\n\t\t\telse if (fi.ntype ==  8) el.SetType(m_nquad8);\n\t\t\telse if (fi.ntype ==  9) el.SetType(m_nquad9);\n\t\t\telse return false;\n\t\t}\n\n\t\tint N = el.Nodes(); assert(N == fi.ntype);\n\t\tfor (int j = 0; j<N; ++j) el.m_node[j] = fi.node[j];\n\t}\n\n    // copy the name\n    s.SetName(fs.GetName());\n\n\t// finish building the surface\n\ts.InitSurface();\n\n\t// allocate material point data\n\ts.CreateMaterialPointData();\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModelBuilder::BuildEdge(FEEdge& e, FESegmentSet& es)\n{\n\t// copy the name\n\te.SetName(es.GetName());\n\n\t// create the edge\n\treturn e.Create(es);\n}\n\n//-----------------------------------------------------------------------------\nFEModelBuilder::NodeSetPair* FEModelBuilder::FindNodeSetPair(const char* szname)\n{\n\tfor (int i = 0; i<m_nsetPair.size(); ++i)\n\t\tif (strcmp(m_nsetPair[i].szname, szname) == 0) return &m_nsetPair[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nFEModelBuilder::NodeSetSet* FEModelBuilder::FindNodeSetSet(const char* szname)\n{\n\tfor (int i = 0; i<m_nsetSet.size(); ++i)\n\t\tif (strcmp(m_nsetSet[i].szname, szname) == 0) return &m_nsetSet[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::BuildNodeList()\n{\n\t// find the min, max ID\n\t// (We assume that they are given by the first and last node)\n\tFEMesh& mesh = m_fem.GetMesh();\n\tint NN = mesh.Nodes();\n\tint nmin = mesh.Node(0).GetID();\n\tint nmax = mesh.Node(NN - 1).GetID();\n\tassert(nmax >= nmin);\n\n\t// get the range\n\tint nn = nmax - nmin + 1;\n\n\t// allocate list\n\tm_node_off = nmin;\n\tm_node_list.assign(nn, -1);\n\n\t// build the list\n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\tint nid = mesh.Node(i).GetID();\n\t\tm_node_list[nid - m_node_off] = i;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nint FEModelBuilder::FindNodeFromID(int nid)\n{\n\tint N = (int)m_node_list.size();\n\tif (N > 0)\n\t{\n\t\tint n = nid - m_node_off;\n\t\tif ((n >= 0) && (n<N)) return m_node_list[n];\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelBuilder::GlobalToLocalID(int* l, int n, vector<int>& m)\n{\n\tassert((int)m.size() == n);\n\tfor (int i = 0; i<n; ++i)\n\t{\n\t\tm[i] = FindNodeFromID(l[i]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Call this to initialize default variables when reading older files.\nvoid FEModelBuilder::SetDefaultVariables()\n{\n\t// Reset degrees of\n\tFEModel& fem = m_fem;\n\tDOFS& dofs = fem.GetDOFS();\n\tdofs.Reset();\n\n\t// Add the default variables and degrees of freedom\n\tint varD = dofs.AddVariable(\"displacement\", VAR_VEC3);\n\tdofs.SetDOFName(varD, 0, \"x\");\n\tdofs.SetDOFName(varD, 1, \"y\");\n\tdofs.SetDOFName(varD, 2, \"z\");\n\tint varQ = dofs.AddVariable(\"rotation\", VAR_VEC3);\n\tdofs.SetDOFName(varQ, 0, \"u\");\n\tdofs.SetDOFName(varQ, 1, \"v\");\n\tdofs.SetDOFName(varQ, 2, \"w\");\n\tint varSD = dofs.AddVariable(\"shell displacement\", VAR_VEC3);\n\tdofs.SetDOFName(varSD, 0, \"sx\");\n\tdofs.SetDOFName(varSD, 1, \"sy\");\n\tdofs.SetDOFName(varSD, 2, \"sz\");\n\tint varP = dofs.AddVariable(\"fluid pressure\");\n\tdofs.SetDOFName(varP, 0, \"p\");\n\tint varSP = dofs.AddVariable(\"shell fluid pressure\");\n\tdofs.SetDOFName(varSP, 0, \"q\");\n\tint varQR = dofs.AddVariable(\"rigid rotation\", VAR_VEC3);\n\tdofs.SetDOFName(varQR, 0, \"Ru\");\n\tdofs.SetDOFName(varQR, 1, \"Rv\");\n\tdofs.SetDOFName(varQR, 2, \"Rw\");\n\tint varV = dofs.AddVariable(\"velocity\", VAR_VEC3);\n\tdofs.SetDOFName(varV, 0, \"vx\");\n\tdofs.SetDOFName(varV, 1, \"vy\");\n\tdofs.SetDOFName(varV, 2, \"vz\");\n\tint varW = dofs.AddVariable(\"relative fluid velocity\", VAR_VEC3);\n\tdofs.SetDOFName(varW, 0, \"wx\");\n\tdofs.SetDOFName(varW, 1, \"wy\");\n\tdofs.SetDOFName(varW, 2, \"wz\");\n\tint varAW = dofs.AddVariable(\"relative fluid acceleration\", VAR_VEC3);\n\tdofs.SetDOFName(varAW, 0, \"awx\");\n\tdofs.SetDOFName(varAW, 1, \"awy\");\n\tdofs.SetDOFName(varAW, 2, \"awz\");\n\tint varVF = dofs.AddVariable(\"fluid velocity\", VAR_VEC3);\n\tdofs.SetDOFName(varVF, 0, \"vfx\");\n\tdofs.SetDOFName(varVF, 1, \"vfy\");\n\tdofs.SetDOFName(varVF, 2, \"vfz\");\n\tint varAF = dofs.AddVariable(\"fluid acceleration\", VAR_VEC3);\n\tdofs.SetDOFName(varAF, 0, \"afx\");\n\tdofs.SetDOFName(varAF, 1, \"afy\");\n\tdofs.SetDOFName(varAF, 2, \"afz\");\n\tint varEF = dofs.AddVariable(\"fluid dilatation\");\n\tdofs.SetDOFName(varEF, 0, \"ef\");\n\tint varAEF = dofs.AddVariable(\"fluid dilatation tderiv\");\n\tdofs.SetDOFName(varAEF, 0, \"aef\");\n\tint varQV = dofs.AddVariable(\"shell velocity\", VAR_VEC3);\n\tdofs.SetDOFName(varQV, 0, \"svx\");\n\tdofs.SetDOFName(varQV, 1, \"svy\");\n\tdofs.SetDOFName(varQV, 2, \"svz\");\n\tint varQA = dofs.AddVariable(\"shell acceleration\", VAR_VEC3);\n\tdofs.SetDOFName(varQA, 0, \"sax\");\n\tdofs.SetDOFName(varQA, 1, \"say\");\n\tdofs.SetDOFName(varQA, 2, \"saz\");\n    int varT = dofs.AddVariable(\"temperature\");\n    dofs.SetDOFName(varT, 0, \"T\");\n\t// must be last variable definition!!\n\tint varC = dofs.AddVariable(\"concentration\", VAR_ARRAY); // we start with zero concentrations\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t // must be last variable definition!!\n\tint varSC = dofs.AddVariable(\"shell concentration\", VAR_ARRAY); // we start with zero concentrations\n    int varAC = dofs.AddVariable(\"concentration tderiv\", VAR_ARRAY); // we start with zero concentrations\n    // must be last variable definition!!\n}\n\n//-----------------------------------------------------------------------------\n//! Get the element type from a XML tag\nFE_Element_Spec FEModelBuilder::ElementSpec(const char* sztype)\n{\n    FEMesh& mesh = m_fem.GetMesh();\n    \n\t// determine the element shape\n\tFE_Element_Shape eshape = FE_ELEM_INVALID_SHAPE;\n\n\t// for shells, don't overwrite m_pim->m_ntri3/6 or m_nquad4/8, since they are needed for surface definitions\n\tFE_Element_Type stype = FE_ELEM_INVALID_TYPE;\n\tif      (strcmp(sztype, \"hex8\"   ) == 0) eshape = ET_HEX8;\n\telse if (strcmp(sztype, \"hex20\"  ) == 0) eshape = ET_HEX20;\n\telse if (strcmp(sztype, \"hex27\"  ) == 0) eshape = ET_HEX27;\n\telse if (strcmp(sztype, \"penta6\" ) == 0) eshape = ET_PENTA6;\n\telse if (strcmp(sztype, \"penta15\") == 0) eshape = ET_PENTA15;\n\telse if (strcmp(sztype, \"pyra5\"  ) == 0) eshape = ET_PYRA5;\n    else if (strcmp(sztype, \"pyra13\" ) == 0) eshape = ET_PYRA13;\n\telse if (strcmp(sztype, \"tet4\"   ) == 0) eshape = ET_TET4;\n\telse if (strcmp(sztype, \"tet5\"   ) == 0) eshape = ET_TET5;\n\telse if (strcmp(sztype, \"tet10\"  ) == 0) eshape = ET_TET10;\n\telse if (strcmp(sztype, \"tet15\"  ) == 0) eshape = ET_TET15;\n\telse if (strcmp(sztype, \"tet20\"  ) == 0) eshape = ET_TET20;\n\telse if (strcmp(sztype, \"quad4\"  ) == 0) { eshape = ET_QUAD4; stype = FE_SHELL_QUAD4G8; }   // default shell type for quad4\n\telse if (strcmp(sztype, \"quad8\"  ) == 0) { eshape = ET_QUAD8; stype = FE_SHELL_QUAD8G18; }   // default shell type for quad8\n\telse if (strcmp(sztype, \"quad9\"  ) == 0) eshape = ET_QUAD9;\n\telse if (strcmp(sztype, \"tri3\"   ) == 0) { eshape = ET_TRI3; stype = FE_SHELL_TRI3G6; }     // default shell type for tri3\n\telse if (strcmp(sztype, \"tri3s\"  ) == 0) { eshape = ET_TRI3; stype = FE_SHELL_TRI3G3; }     // should only be used for rigid shells\n\telse if (strcmp(sztype, \"tri6\"   ) == 0) { eshape = ET_TRI6; stype = FE_SHELL_TRI6G14; }     // default shell type for tri6\n\telse if (strcmp(sztype, \"q4eas\"  ) == 0) { eshape = ET_QUAD4; stype = FE_SHELL_QUAD4G8; m_default_shell = EAS_SHELL; }   // default shell type for q4eas\n\telse if (strcmp(sztype, \"q4ans\"  ) == 0) { eshape = ET_QUAD4; stype = FE_SHELL_QUAD4G8; m_default_shell = ANS_SHELL; }   // default shell type for q4ans\n\telse if (strcmp(sztype, \"q4s\"    ) == 0) { eshape = ET_QUAD4; stype = FE_SHELL_QUAD4G4; m_default_shell = -1; } // should only be used for rigid shells\n\telse if (strcmp(sztype, \"truss2\" ) == 0) eshape = ET_TRUSS2;\n\telse if (strcmp(sztype, \"line2\"  ) == 0) eshape = ET_TRUSS2;\n\telse if (strcmp(sztype, \"line3\"  ) == 0) eshape = ET_LINE3;\n\telse if (strcmp(sztype, \"ut4\"    ) == 0) { eshape = ET_TET4; m_but4 = true; }\n\telse\n\t{\n\t\t// new way for defining element type and integration rule at the same time\n\t\t// this is useful for multi-step analyses where the geometry is read in before the control section.\n\t\tif      (strcmp(sztype, \"TET4G4\"      ) == 0) { eshape = ET_TET4 ; m_ntet4  = FE_TET4G4; }\n\t\telse if (strcmp(sztype, \"TET10G4\"     ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G4; }\n\t\telse if (strcmp(sztype, \"TET10G8\"     ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G8; }\n\t\telse if (strcmp(sztype, \"TET10GL11\"   ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10GL11; }\n\t\telse if (strcmp(sztype, \"TET10G4_S3\"  ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G4;   m_ntri6 = FE_TRI6G3; }\n\t\telse if (strcmp(sztype, \"TET10G8_S3\"  ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G8;   m_ntri6 = FE_TRI6G3; }\n\t\telse if (strcmp(sztype, \"TET10GL11_S3\") == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10GL11; m_ntri6 = FE_TRI6G3; }\n\t\telse if (strcmp(sztype, \"TET10G4_S4\"  ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G4;   m_ntri6 = FE_TRI6G4; }\n\t\telse if (strcmp(sztype, \"TET10G8_S4\"  ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G8;   m_ntri6 = FE_TRI6G4; }\n\t\telse if (strcmp(sztype, \"TET10GL11_S4\") == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10GL11; m_ntri6 = FE_TRI6G4; }\n\t\telse if (strcmp(sztype, \"TET10G4_S7\"  ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G4;   m_ntri6 = FE_TRI6G7; }\n\t\telse if (strcmp(sztype, \"TET10G8_S7\"  ) == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10G8;   m_ntri6 = FE_TRI6G7; }\n\t\telse if (strcmp(sztype, \"TET10GL11_S7\") == 0) { eshape = ET_TET10; m_ntet10 = FE_TET10GL11; m_ntri6 = FE_TRI6G7; }\n\t\telse if (strcmp(sztype, \"TET15G8\"     ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G8; }\n\t\telse if (strcmp(sztype, \"TET15G11\"    ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G11; }\n\t\telse if (strcmp(sztype, \"TET15G15\"    ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G15; }\n\t\telse if (strcmp(sztype, \"TET15G8_S3\"  ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G8;  m_ntri7 = FE_TRI7G3; }\n\t\telse if (strcmp(sztype, \"TET15G11_S3\" ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G11; m_ntri7 = FE_TRI7G3; }\n\t\telse if (strcmp(sztype, \"TET15G15_S3\" ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G15; m_ntri7 = FE_TRI7G3; }\n\t\telse if (strcmp(sztype, \"TET15G8_S4\"  ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G8;  m_ntri7 = FE_TRI7G4; }\n\t\telse if (strcmp(sztype, \"TET15G11_S4\" ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G11; m_ntri7 = FE_TRI7G4; }\n\t\telse if (strcmp(sztype, \"TET15G15_S4\" ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G15; m_ntri7 = FE_TRI7G4; }\n\t\telse if (strcmp(sztype, \"TET15G8_S7\"  ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G8;  m_ntri7 = FE_TRI7G7; }\n\t\telse if (strcmp(sztype, \"TET15G11_S7\" ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G11; m_ntri7 = FE_TRI7G7; }\n\t\telse if (strcmp(sztype, \"TET15G15_S7\" ) == 0) { eshape = ET_TET15; m_ntet15 = FE_TET15G15; m_ntri7 = FE_TRI7G7; }\n\t\telse if (strcmp(sztype, \"PENTA15G8\"   ) == 0) { eshape = ET_PENTA15; stype = FE_PENTA15G8; }\n\t\telse if (strcmp(sztype, \"HEX20G8\"     ) == 0) { eshape = ET_HEX20; stype = FE_HEX20G8; }\n\t\telse if (strcmp(sztype, \"QUAD4G8\"     ) == 0) { eshape = ET_QUAD4; stype = FE_SHELL_QUAD4G8; }\n\t\telse if (strcmp(sztype, \"QUAD4G12\"    ) == 0) { eshape = ET_QUAD4; stype = FE_SHELL_QUAD4G12; }\n\t\telse if (strcmp(sztype, \"QUAD8G18\"    ) == 0) { eshape = ET_QUAD8; stype = FE_SHELL_QUAD8G18; }\n\t\telse if (strcmp(sztype, \"QUAD8G27\"    ) == 0) { eshape = ET_QUAD8; stype = FE_SHELL_QUAD8G27; }\n\t\telse if (strcmp(sztype, \"TRI3G6\"      ) == 0) { eshape = ET_TRI3; stype = FE_SHELL_TRI3G6; }\n\t\telse if (strcmp(sztype, \"TRI3G9\"      ) == 0) { eshape = ET_TRI3; stype = FE_SHELL_TRI3G9; }\n\t\telse if (strcmp(sztype, \"TRI6G14\"     ) == 0) { eshape = ET_TRI6; stype = FE_SHELL_TRI6G14; }\n\t\telse if (strcmp(sztype, \"TRI6G21\"     ) == 0) { eshape = ET_TRI6; stype = FE_SHELL_TRI6G21; }\n\t\telse if (strcmp(sztype, \"HEX8G1\"      ) == 0) { eshape = ET_HEX8; m_nhex8 = FE_HEX8G1; }\n\t\telse if (strcmp(sztype, \"HEX8G8\"      ) == 0) { eshape = ET_HEX8; m_nhex8 = FE_HEX8G8; }\n\t\telse if (strcmp(sztype, \"HEX8G6\"      ) == 0) { eshape = ET_HEX8; m_nhex8 = FE_HEX8RI; }\n\t\telse\n\t\t{\n\t\t\tassert(false);\n\t\t}\n\t}\n\n\t// this is a hack to choose between 2D elements and shell elements.\n\t// NOTE: This is only used by quad/tri elements.\n\t// TODO: find a better way\n\tint NDIM = 3;\n\tif (GetModuleName() == \"fluid\") NDIM = 2;\n\n\t// determine the element type\n\tFE_Element_Type etype = FE_ELEM_INVALID_TYPE;\n\tswitch (eshape)\n\t{\n\tcase ET_HEX8   : etype = m_nhex8; break;\n\tcase ET_PENTA6 : etype = FE_PENTA6G6; break;\n\tcase ET_PENTA15: etype = FE_PENTA15G21; break;\n\tcase ET_PYRA5  : etype = FE_PYRA5G8; break;\n    case ET_PYRA13 : etype = FE_PYRA13G8; break;\n\tcase ET_TET4   : etype = m_ntet4; break;\n\tcase ET_TET5   : etype = FE_TET5G4; break;\n\tcase ET_TET10  : etype = m_ntet10; break;\n\tcase ET_TET15  : etype = m_ntet15; break;\n\tcase ET_TET20  : etype = m_ntet20; break;\n\tcase ET_HEX20  : etype = (stype == FE_HEX20G8 ? FE_HEX20G8 : FE_HEX20G27); break;\n\tcase ET_HEX27  : etype = FE_HEX27G27; break;\n\tcase ET_QUAD4  : etype = (NDIM == 3 ? stype : FE2D_QUAD4G4); break;\n\tcase ET_TRI3   : etype = (NDIM == 3 ? stype : FE2D_TRI3G1); break;\n\tcase ET_TRI6   : etype = (NDIM == 3 ? stype : FE2D_TRI6G3); break;\n\tcase ET_QUAD8  : etype = (NDIM == 3 ? stype : FE2D_QUAD8G9); break;\n\tcase ET_QUAD9  : etype = FE2D_QUAD9G9; break;\n\tcase ET_TRUSS2 : etype = FE_TRUSS; break;\n\tcase ET_LINE3  : etype = FE_BEAM3G2; break;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\t// determine the element class\n\tFE_Element_Class eclass = FEElementLibrary::GetElementClass(etype);\n\n\t// return the spec\n\tFE_Element_Spec spec;\n\tspec.eclass = eclass;\n\tspec.eshape = eshape;\n\tspec.etype = etype;\n\n\t// set three-field flag\n\tswitch (eshape)\n\t{\n\tcase ET_HEX8   : spec.m_bthree_field = m_b3field_hex; break;\n\tcase ET_PENTA6 : spec.m_bthree_field = m_b3field_hex; break;\n\tcase ET_PYRA5  : spec.m_bthree_field = m_b3field_hex; break;\n\tcase ET_TET4   : spec.m_bthree_field = m_b3field_tet; break;\n\tcase ET_TET5   : spec.m_bthree_field = m_b3field_tet; break;\n\tcase ET_TET10  : spec.m_bthree_field = m_b3field_tet; break;\n\tcase ET_TET15  : spec.m_bthree_field = m_b3field_tet; break;\n\tcase ET_TET20  : spec.m_bthree_field = m_b3field_tet; break;\n\tcase ET_QUAD4  : spec.m_bthree_field = m_b3field_shell || m_b3field_quad; break;\n\tcase ET_TRI3   : spec.m_bthree_field = m_b3field_shell || m_b3field_tri; break;\n\tcase ET_TRI6   : spec.m_bthree_field = m_b3field_shell; break;\n\tcase ET_QUAD8  : spec.m_bthree_field = m_b3field_shell; break;\n\tcase ET_QUAD9  : spec.m_bthree_field = m_b3field_shell; break;\n\t}\n\n\tspec.m_but4 = m_but4;\n\tspec.m_ut4_alpha = m_ut4_alpha;\n\tspec.m_ut4_bdev = m_ut4_bdev;\n\tspec.m_shell_formulation = m_default_shell;\n    spec.m_shell_norm_nodal = m_shell_norm_nodal;\n\n\t// Make sure this is a valid element specification\n\tassert(FEElementLibrary::IsValid(spec));\n\n\treturn spec;\n}\n\nvoid FEModelBuilder::AddMappedParameter(FEParam* p, FECoreBase* parent, const char* szmap, int index)\n{\n\tMappedParameter mp;\n\tmp.pp = p;\n\tmp.pc = parent;\n\tmp.szname = strdup(szmap);\n\tmp.index = index;\n\n\tm_mappedParams.push_back(mp);\n}\n\nvoid FEModelBuilder::AddMeshDataGenerator(FEMeshDataGenerator* gen, FEDataMap* pmap, FEParamDouble* pp)\n{\n\tm_mapgen.push_back(DataGen{ gen, pmap, pp });\n}\n\nvoid FEModelBuilder::ApplyParameterMaps()\n{\n\tFEMesh& mesh = m_fem.GetMesh();\n\tfor (int i = 0; i < m_mappedParams.size(); ++i)\n\t{\n\t\tMappedParameter& mp = m_mappedParams[i];\n\t\tFEParam& p = *mp.pp;\n\n\t\tFEDataMap* data = (FEDataMap*)mesh.FindDataMap(mp.szname);\n\t\tif (data == nullptr)\n\t\t{\n\t\t\tstringstream ss;\n\t\t\tss << \"Can't find map \\\"\" << mp.szname << \"\\\" for parameter \\\"\" << p.name() << \"\\\"\";\n\t\t\tthrow std::runtime_error(ss.str());\n\t\t}\n\n\t\tFEItemList* itemList = data->GetItemList();\n\n\t\t// find the map of this parameter\n\t\tif (p.type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t{\n\t\t\tFEParamDouble& v = p.value<FEParamDouble>(mp.index);\n\t\t\tFEMappedValue* map = fecore_alloc(FEMappedValue, &m_fem);\n\t\t\tif (data->DataType() != FE_DOUBLE)\n\t\t\t{\n\t\t\t\tstd::stringstream ss;\n\t\t\t\tss << \"Cannot assign map \\\"\" << data->GetName() << \"\\\" to parameter \\\"\" << p.name() << \"\\\" : bad data type\";\n\t\t\t\tstring err = ss.str();\n\t\t\t\tthrow std::runtime_error(err.c_str());\n\t\t\t}\n\t\t\tmap->setDataMap(data);\n\t\t\tv.setValuator(map);\n\t\t\tv.SetItemList(itemList);\n\t\t}\n\t\telse if (p.type() == FE_PARAM_VEC3D_MAPPED)\n\t\t{\n\t\t\tFEParamVec3& v = p.value<FEParamVec3>();\n\t\t\tFEMappedValueVec3* map = fecore_alloc(FEMappedValueVec3, &m_fem);\n\t\t\tif (data->DataType() != FE_VEC3D)\n\t\t\t{\n\t\t\t\tstd::stringstream ss;\n\t\t\t\tss << \"Cannot assign map \\\"\" << data->GetName() << \"\\\" to parameter \\\"\" << p.name() << \"\\\" : bad data type\";\n\t\t\t\tstring err = ss.str();\n\t\t\t\tthrow std::runtime_error(err.c_str());\n\t\t\t}\n\t\t\tmap->setDataMap(data);\n\t\t\tv.setValuator(map);\n\t\t\tv.SetItemList(itemList);\n\t\t}\n\t\telse if (p.type() == FE_PARAM_MAT3D_MAPPED)\n\t\t{\n\t\t\tFEParamMat3d& v = p.value<FEParamMat3d>();\n\t\t\tFEMappedValueMat3d* map = fecore_alloc(FEMappedValueMat3d, &m_fem);\n\t\t\tif (data->DataType() != FE_MAT3D)\n\t\t\t{\n\t\t\t\tstd::stringstream ss;\n\t\t\t\tss << \"Cannot assign map \\\"\" << data->GetName() << \"\\\" to parameter \\\"\" << p.name() << \"\\\" : bad data type\";\n\t\t\t\tstring err = ss.str();\n\t\t\t\tthrow std::runtime_error(err.c_str());\n\t\t\t}\n\t\t\tmap->setDataMap(data);\n\t\t\tv.setValuator(map);\n\t\t\tv.SetItemList(itemList);\n\t\t}\n        else if (p.type() == FE_PARAM_MAT3DS_MAPPED)\n        {\n            FEParamMat3ds& v = p.value<FEParamMat3ds>();\n            FEMappedValueMat3ds* map = fecore_alloc(FEMappedValueMat3ds, &m_fem);\n            if (data->DataType() != FE_MAT3DS)\n            {\n                std::stringstream ss;\n                ss << \"Cannot assign map \\\"\" << data->GetName() << \"\\\" to parameter \\\"\" << p.name() << \"\\\" : bad data type\";\n                string err = ss.str();\n                throw std::runtime_error(err.c_str());\n            }\n            map->setDataMap(data);\n            v.setValuator(map);\n            v.SetItemList(itemList);\n        }\n\t\telse\n\t\t{\n\t\t\tassert(false);\n\t\t}\n\t}\n}\n\nFENodeSet* FEModelBuilder::FindNodeSet(const string& setName)\n{\n\tFEMesh& mesh = m_fem.GetMesh();\n\n\tFENodeSet* nodeSet = nullptr;\n\n\tif (setName.compare(0, 9, \"@surface:\") == 0)\n\t{\n\t\t// see if we can find a surface\n\t\tstring surfName = setName.substr(9);\n\t\tFEFacetSet* surf = mesh.FindFacetSet(surfName);\n\t\tif (surf == nullptr) return nullptr;\n\n\t\t// we might have been here before. If so, we already create a nodeset\n\t\t// with the same name as the surface, so look for that first.\n\t\tnodeSet = mesh.FindNodeSet(surfName);\n\t\tif (nodeSet) return nodeSet;\n\n\t\t// okay, first time here, so let's create a node set from this surface\n\t\tFENodeList nodeList = surf->GetNodeList();\n\t\tnodeSet = new FENodeSet(&m_fem);\n\t\tnodeSet->Add(nodeList);\n\t\tnodeSet->SetName(surfName);\n\t\tmesh.AddNodeSet(nodeSet);\n\t}\n\telse if (setName.compare(0, 6, \"@edge:\") == 0)\n\t{\n\t\t// see if we can find an edge\n\t\tstring edgeName = setName.substr(6);\n\t\tFESegmentSet* edge = mesh.FindSegmentSet(edgeName);\n\t\tif (edge == nullptr) return nullptr;\n\n\t\t// we might have been here before. If so, we already create a nodeset\n\t\t// with the same name as the edge, so look for that first.\n\t\tnodeSet = mesh.FindNodeSet(edgeName);\n\t\tif (nodeSet) return nodeSet;\n\n\t\t// okay, first time here, so let's create a node set from this surface\n\t\tFENodeList nodeList = edge->GetNodeList();\n\t\tnodeSet = new FENodeSet(&m_fem);\n\t\tnodeSet->Add(nodeList);\n\t\tnodeSet->SetName(edgeName);\n\t\tmesh.AddNodeSet(nodeSet);\n\t}\n\telse if (setName.compare(0, 10, \"@elem_set:\") == 0)\n\t{\n\t\t// see if we can find an element set\n\t\tstring esetName = setName.substr(10);\n\t\tFEElementSet* part = mesh.FindElementSet(esetName);\n\t\tif (part == nullptr) return nullptr;\n\n\t\t// we might have been here before. If so, we already create a nodeset\n\t\t// with the same name as the surface, so look for that first.\n\t\tnodeSet = mesh.FindNodeSet(esetName);\n\t\tif (nodeSet) return nodeSet;\n\n\t\t// okay, first time here, so let's create a node set from this element set\n\t\tFENodeList nodeList = part->GetNodeList();\n\t\tnodeSet = new FENodeSet(&m_fem);\n\t\tnodeSet->Add(nodeList);\n\t\tnodeSet->SetName(esetName);\n\t\tmesh.AddNodeSet(nodeSet);\n\t}\n\telse if (setName.compare(0, 11, \"@part_list:\") == 0)\n\t{\n\t\t// see if we can find an element set\n\t\tFEElementSet* part = mesh.FindElementSet(setName);\n\t\tif (part == nullptr) return nullptr;\n\n\t\t// we might have been here before. If so, we already create a nodeset\n\t\t// with the same name as the surface, so look for that first.\n\t\tnodeSet = mesh.FindNodeSet(setName);\n\t\tif (nodeSet) return nodeSet;\n\n\t\t// okay, first time here, so let's create a node set from this element set\n\t\tFENodeList nodeList = part->GetNodeList();\n\t\tnodeSet = new FENodeSet(&m_fem);\n\t\tnodeSet->Add(nodeList);\n\t\tnodeSet->SetName(setName);\n\t\tmesh.AddNodeSet(nodeSet);\n\t}\n\telse nodeSet = mesh.FindNodeSet(setName);\n\n\treturn nodeSet;\n}\n\nvoid FEModelBuilder::MapLoadCurveToFunction(FEPointFunction* pf, int lc, double scale)\n{\n\tMapLCToFunction m = { lc, scale, pf };\n\tm_lc2fnc.push_back(m);\n}\n\nvoid FEModelBuilder::ApplyLoadcurvesToFunctions()\n{\n\tFEModel& fem = m_fem;\n\tfor (int i = 0; i < m_lc2fnc.size(); ++i)\n\t{\n\t\tMapLCToFunction& m = m_lc2fnc[i];\n\n\t\tFELoadController* plc = fem.GetLoadController(m.lc); assert(plc);\n\t\tFELoadCurve* lc = dynamic_cast<FELoadCurve*>(plc);\n\n\t\tm.pf->SetInterpolation(lc->GetInterpolation());\n\t\tm.pf->SetExtendMode(lc->GetExtendMode());\n\t\tm.pf->SetPoints(lc->GetPoints());\n\t\tif (m.scale != 1.0) m.pf->Scale(m.scale);\n\t}\n}\n\nbool FEModelBuilder::GenerateMeshDataMaps()\n{\n\tFEModel& fem = GetFEModel();\n\tFEMesh& mesh = GetMesh();\n\tfor (int i = 0; i < m_mapgen.size(); ++i)\n\t{\n\t\tFEMeshDataGenerator* gen = m_mapgen[i].gen;\n\n\t\t// try to initialize the generator\n\t\tif (gen->Init() == false) return false;\n\n\t\tFENodeDataGenerator* ngen = dynamic_cast<FENodeDataGenerator*>(gen);\n\t\tif (ngen)\n\t\t{\n\t\t\t// see if this map is already defined\n\t\t\tstring mapName = ngen->GetName();\n\t\t\tFENodeDataMap* oldMap = dynamic_cast<FENodeDataMap*>(mesh.FindDataMap(mapName));\n\t\t\tif (oldMap) return false;\n\n\t\t\t// generate the node data map\n\t\t\tif (ngen->Init() == false) return false;\n\t\t\tFEDataMap* map = ngen->Generate();\n\t\t\tif (map == nullptr) return false;\n\t\t\tmap->SetName(mapName);\n\t\t\tmesh.AddDataMap(map);\n\t\t}\n\n\t\tFEFaceDataGenerator* fgen = dynamic_cast<FEFaceDataGenerator*>(gen);\n\t\tif (fgen)\n\t\t{\n\t\t\t// see if this map is already defined\n\t\t\tstring mapName = fgen->GetName();\n\t\t\tFESurfaceMap* oldMap = dynamic_cast<FESurfaceMap*>(mesh.FindDataMap(mapName));\n\t\t\tif (oldMap) return false;\n\n\t\t\t// generate data\n\t\t\tif (fgen->Init() == false) return false;\n\t\t\tFEDataMap* map = fgen->Generate();\n\t\t\tif (map == nullptr) return false;\n\t\t\tmap->SetName(mapName);\n\t\t\tmesh.AddDataMap(map);\n\t\t}\n\n\t\tFEElemDataGenerator* egen = dynamic_cast<FEElemDataGenerator*>(gen);\n\t\tif (egen)\n\t\t{\n\t\t\tif (egen->Init() == false) return false;\n\n\t\t\t// generate the data\n\t\t\tFEDomainMap* map = dynamic_cast<FEDomainMap*>(egen->Generate());\n\t\t\tif (map == nullptr) return false;\n\n\t\t\t// see if this map is already defined\n\t\t\tstring mapName = gen->GetName();\n\t\t\tFEDomainMap* oldMap = dynamic_cast<FEDomainMap*>(mesh.FindDataMap(mapName));\n\t\t\tif (oldMap)\n\t\t\t{\n\t\t\t\t// it is, so merge it\n\t\t\t\toldMap->Merge(*map);\n\n\t\t\t\t// we can now delete this map\n\t\t\t\tdelete map;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// nope, so add it\n\t\t\t\tmap->SetName(mapName);\n\t\t\t\tmesh.AddDataMap(map);\n\n\t\t\t\t// apply the map\n\t\t\t\tFEParamDouble* pp = m_mapgen[i].pp;\n\t\t\t\tif (pp)\n\t\t\t\t{\n\t\t\t\t\tFEMappedValue* val = fecore_alloc(FEMappedValue, &fem);\n\t\t\t\t\tval->setDataMap(map);\n\t\t\t\t\tpp->setValuator(val);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n// finish the build process\nbool FEModelBuilder::Finish()\n{\n\tApplyLoadcurvesToFunctions();\n\tif (GenerateMeshDataMaps() == false) return false;\n\tApplyParameterMaps();\n\treturn true;\n}\n\nFEBModel& FEModelBuilder::GetFEBModel()\n{\n\treturn m_feb;\n}\n"
  },
  {
    "path": "FEBioXML/FEModelBuilder.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEModel.h>\n#include \"febioxml_api.h\"\n#include \"FEBModel.h\"\n#include <string>\n\nclass FESolver;\nclass FEPointFunction;\nclass FEMeshDataGenerator;\nclass FEDomainMap;\nclass FENodalLoad;\nclass FEEdgeLoad;\nclass FESurfaceLoad;\nclass FEBodyLoad;\n\n// This is a helper class for building the FEModel from file input. \nclass FEBIOXML_API FEModelBuilder\n{\npublic:\n\tstruct ELEMENT\n\t{\n\t\tint\tnid;\n\t\tint nodes;\n\t\tint\tnode[FEElement::MAX_NODES];\n\t};\n\n\tstruct FEBIOXML_API NodeSetPair\n\t{\n\t\tchar\t\tszname[256];\n\t\tFENodeSet*\tset1;\n\t\tFENodeSet*\tset2;\n\t};\n\n\tstruct FEBIOXML_API NodeSetSet\n\t{\n\t\tNodeSetSet() { count = 0; }\n\t\tenum { MAX_SETS = 32 };\n\t\tchar\t\tszname[256];\n\t\tFENodeSet*\tset[MAX_SETS];\n\t\tint\t\t\tcount;\n\n\t\tvoid add(FENodeSet* ps) { set[count++] = ps; }\n\t};\n\n\tstruct FEBIOXML_API MappedParameter\n\t{\n\t\tFEParam*\tpp;\n\t\tFECoreBase*\tpc;\n\t\tconst char*\tszname;\n\t\tint\t\t\tindex;\n\t};\n\n\tstruct FEBIOXML_API MapLCToFunction\n\t{\n\t\tint\tlc;\n\t\tdouble scale;\n\t\tFEPointFunction*\tpf;\n\t};\n\n\tstruct FEBIOXML_API DataGen\n\t{\n\t\tFEMeshDataGenerator*\tgen;\t// the data generator\n\t\tFEDataMap*\t\t\t\tmap;\t// the destination map \n\t\tFEParamDouble*\t\t\tpp;\t\t// the param to which to apply the map (or null)\n\t};\n\npublic:\n\t//! constructor\n\tFEModelBuilder(FEModel& fem);\n\tvirtual ~FEModelBuilder();\n\n\t//! set the active module\n\tvoid SetActiveModule(const std::string& moduleName);\n\n\t//! Get the module name\n\tstd::string GetModuleName() const;\n\n\t// create a new analysis step\n\tFEAnalysis* CreateNewStep(bool allocSolver = true);\n\n\t// create a material\n\tFEMaterial* CreateMaterial(const char* sztype);\n\n\t// get the current step (will create a new one if no step was defined yet)\n\tFEAnalysis*\tGetStep(bool allocSolver = true);\n\n\t// add component to current step\n\tvoid AddComponent(FEStepComponent* mc);\n\n\t// reset some data for reading next step\n\tvoid NextStep();\n\n\t//! Create a domain\n\tvirtual FEDomain* CreateDomain(FE_Element_Spec espec, FEMaterial* mat);\n\n\t//! Get the mesh\n\tFEMesh& GetMesh();\n\n\t//! get the FE model\n\tFEModel& GetFEModel();\n\npublic:\n\tbool BuildSurface(FESurface& s, FEFacetSet& f, bool bnodal = false);\n\n\tbool BuildEdge(FEEdge& s, FESegmentSet& f);\n\n\tFE_Element_Spec ElementSpec(const char* sz);\n\n\t// Call this to initialize default variables when reading older files.\n\tvoid SetDefaultVariables();\n\npublic:\n\tvirtual void AddMaterial(FEMaterial* pmat);\n\n\tvoid AddBC(FEBoundaryCondition* pbc);\n\tvoid AddNodalLoad(FENodalLoad* pfc);\n\tvoid AddEdgeLoad(FEEdgeLoad* pel);\n\tvoid AddSurfaceLoad(FESurfaceLoad* psl);\n\tvoid AddInitialCondition(FEInitialCondition* pic);\n\tvoid AddContactInterface(FESurfacePairConstraint* pci);\n\tvoid AddModelLoad(FEModelLoad* pml);\n\tvoid AddNonlinearConstraint(FENLConstraint* pnc);\n\n\t// TODO: Try to remove these\n\tvirtual void AddRigidComponent(FEStepComponent* prc);\n\npublic:\n\tvoid AddNodeSetPair(NodeSetPair& p) { m_nsetPair.push_back(p); }\n\tNodeSetPair* FindNodeSetPair(const char* szname);\n\n\tvoid AddNodeSetSet(NodeSetSet& p) { m_nsetSet.push_back(p); }\n\tNodeSetSet* FindNodeSetSet(const char* szname);\n\n\tFENodeSet* FindNodeSet(const string& setName);\n\npublic:\n\tvoid MapLoadCurveToFunction(FEPointFunction* pf, int lc, double scale = 1.0);\n\nprotected:\n\tFESolver* BuildSolver(FEModel& fem);\n\npublic:\n\t// Build the node ID table\n\tvoid BuildNodeList();\n\n\t// find a node index from its ID\n\tint FindNodeFromID(int nid);\n\n\t// convert an array of nodal ID to nodal indices\n\tvoid GlobalToLocalID(int* l, int n, vector<int>& m);\n\npublic:\n\tvoid AddMappedParameter(FEParam* p, FECoreBase* parent, const char* szmap, int index = 0);\n\n\tvoid AddMeshDataGenerator(FEMeshDataGenerator* gen, FEDataMap* map, FEParamDouble* pp);\n\n\t// This will associate all mapped parameters to their assigned maps.\n\tvoid ApplyParameterMaps();\n\n\tvoid ApplyLoadcurvesToFunctions();\n\n\tbool GenerateMeshDataMaps();\n\n\t// finish the build process\n\tbool Finish();\n\n\tFEBModel& GetFEBModel();\n\n\tvoid SetDefaultSolver(const std::string& s) { m_defaultSolver = s; }\n\nprivate:\n\tFEModel&\t\tm_fem;\t\t\t\t//!< model that is being constructed\n\tFEAnalysis*\t\tm_pStep;\t\t\t//!< pointer to current analysis step\n\tint\t\t\t\tm_nsteps;\t\t\t//!< nr of step sections read\n\n\tFEBModel\tm_feb;\n\n\tstd::string\tm_defaultSolver;\t\t//!< default solver\n\npublic:\n\tint\t\tm_maxid;\t\t//!< max element ID\n\n\tbool\tm_b3field_hex;\t    //!< three-field element flag for hex (and wedge elements)\n\tbool\tm_b3field_tet;\t    //!< three-field element flag for quadratic tets\n    bool    m_b3field_shell;    //!< three-field element flag for shells\n    bool    m_b3field_quad;     //!< three-field element flag for quad shells\n    bool    m_b3field_tri;      //!< three-field element flag for tri shells\n\tbool\tm_but4;\t\t\t\t//!< use UT4 formulation flag\n\tint\t\tm_default_shell;\t//!< shell formulation\n    bool    m_shell_norm_nodal; //!< shell normal flag (nodal or face)\n\tdouble\tm_ut4_alpha;\t\t//!< UT4 integration alpha value\n\tbool\tm_ut4_bdev;\t\t\t//!< UT4 integration deviatoric formulation flag\n\tdouble\tm_udghex_hg;\t\t//!< hourglass parameter for UDGhex integration\n\tFE_Element_Type\t\tm_nhex8;\t//!< hex integration rule\n\tFE_Element_Type\t\tm_ntet4;\t//!< tet4 integration rule\n\tFE_Element_Type\t\tm_ntet10;\t//!< tet10 integration rule\n\tFE_Element_Type\t\tm_ntet15;\t//!< tet15 integration rule\n\tFE_Element_Type\t\tm_ntet20;\t//!< tet20 integration rule\n\tFE_Element_Type\t\tm_ntri3;\t//!< tri3 integration rule\n\tFE_Element_Type\t\tm_ntri6;\t//!< tri6 integration rule\n\tFE_Element_Type\t\tm_ntri7;\t//!< tri7 integration rule\n\tFE_Element_Type\t\tm_ntri10;\t//!< tri10 integration rule\n\tFE_Element_Type\t\tm_nquad4;\t//!< quad4 integration rule\n\tFE_Element_Type\t\tm_nquad8;\t//!< quad8 integration rule\n\tFE_Element_Type\t\tm_nquad9;\t//!< quad9 integration rule\n\nprotected:\n\tvector<NodeSetPair>\t\tm_nsetPair;\n\tvector<NodeSetSet>\t\tm_nsetSet;\n\tvector<MappedParameter>\tm_mappedParams;\n\tvector<MapLCToFunction>\tm_lc2fnc;\n\tvector<DataGen>\t\t\tm_mapgen;\n\nprotected:\n\tint\t\t\tm_node_off;\t\t//!< node offset (i.e. lowest node ID)\n\tvector<int>\tm_node_list;\t//!< map node ID's to their nodes.\n};\n"
  },
  {
    "path": "FEBioXML/FERestartImport.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FERestartImport.h\"\n#include \"FECore/FESolver.h\"\n#include \"FECore/FEAnalysis.h\"\n#include \"FECore/FEModel.h\"\n#include \"FECore/DumpFile.h\"\n#include <FECore/FETimeStepController.h>\n#include \"FEBioLoadDataSection.h\"\n#include \"FEBioStepSection.h\"\n#include \"FEBioStepSection3.h\"\n#include \"FEBioStepSection4.h\"\n\nvoid FERestartControlSection::Parse(XMLTag& tag)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t++tag;\n\tdo\n\t{\n\t\tif      (tag == \"time_steps\"        ) tag.value(pstep->m_ntime);\n\t\telse if (tag == \"final_time\"        ) tag.value(pstep->m_final_time);\n\t\telse if (tag == \"step_size\"         ) tag.value(pstep->m_dt0);\n\t\telse if (tag == \"time_stepper\")\n\t\t{\n\t\t\tif (pstep->m_timeController == nullptr) pstep->m_timeController = fecore_alloc(FETimeStepController, &fem);\n\t\t\tFETimeStepController& tc = *pstep->m_timeController;\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif      (tag == \"max_retries\") tag.value(tc.m_maxretries);\n\t\t\t\telse if (tag == \"opt_iter\"   ) tag.value(tc.m_iteopt);\n\t\t\t\telse if (tag == \"dtmin\"      ) tag.value(tc.m_dtmin);\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\t\t}\n\t\telse if (tag == \"plot_level\")\n\t\t{\n\t\t\tchar szval[256];\n\t\t\ttag.value(szval);\n\t\t\tif      (strcmp(szval, \"PLOT_NEVER\"        ) == 0) pstep->SetPlotLevel(FE_PLOT_NEVER);\n\t\t\telse if (strcmp(szval, \"PLOT_MAJOR_ITRS\"   ) == 0) pstep->SetPlotLevel(FE_PLOT_MAJOR_ITRS);\n\t\t\telse if (strcmp(szval, \"PLOT_MINOR_ITRS\"   ) == 0) pstep->SetPlotLevel(FE_PLOT_MINOR_ITRS);\n\t\t\telse if (strcmp(szval, \"PLOT_MUST_POINTS\"  ) == 0) pstep->SetPlotLevel(FE_PLOT_MUST_POINTS);\n\t\t\telse if (strcmp(szval, \"PLOT_FINAL\"        ) == 0) pstep->SetPlotLevel(FE_PLOT_FINAL);\n\t\t\telse if (strcmp(szval, \"PLOT_STEP_FINAL\"   ) == 0) pstep->SetPlotLevel(FE_PLOT_STEP_FINAL);\n\t\t\telse if (strcmp(szval, \"PLOT_AUGMENTATIONS\") == 0) pstep->SetPlotLevel(FE_PLOT_AUGMENTATIONS);\n\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t}\n\t\telse if (tag == \"plot_stride\")\n\t\t{\n\t\t\tint n = 1;\n\t\t\ttag.value(n);\n\t\t\tif (n < 1) throw XMLReader::InvalidValue(tag);\n\t\t\tpstep->SetPlotStride(n);\n\t\t}\n\t\telse if (tag == \"solver\")\n\t\t{\n\t\t\tFEAnalysis* step = fem.GetCurrentStep();\n\t\t\tFESolver* solver = step->GetFESolver();\n\t\t\tif (solver == nullptr) throw XMLReader::InvalidTag(tag);\n\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (ReadParameter(tag, solver) == false)\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t\t++tag;\n\t\t\t} while (!tag.isend());\n\t\t}\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n\n\t// we need to reevaluate the time step size and end time\n\tpstep->m_dt = pstep->m_dt0;\n//\tpstep->m_tend = pstep->m_tstart = pstep->m_ntime*pstep->m_dt0;\n\n}\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFERestartImport::FERestartImport()\n{\n\tm_newSteps = 0;\n}\n\nFERestartImport::~FERestartImport()\n{\n\t\n}\n\nint FERestartImport::StepsAdded() const\n{\n\treturn m_newSteps;\n}\n\n//-----------------------------------------------------------------------------\nbool FERestartImport::Load(FEModel& fem, const char* szfile)\n{\n\t// open the XML file\n\tif (m_xml.Open(szfile) == false) return errf(\"FATAL ERROR: Failed opening restart file %s\\n\", szfile);\n\n\tif (m_builder == nullptr) SetModelBuilder(new FEModelBuilder(fem));\n\n\tm_szdmp[0] = 0;\n\tm_newSteps = 0;\n\n\tm_map[\"Control\" ] = new FERestartControlSection(this);\n\n\t// loop over child tags\n\tbool ret = true;\n\ttry\n\t{\n\t\t// find the root element\n\t\tXMLTag tag;\n\t\tif (m_xml.FindTag(\"febio_restart\", tag) == false) return errf(\"FATAL ERROR: File does not contain restart data.\\n\");\n\n\t\t// check the version number\n\t\tconst char* szversion = tag.AttributeValue(\"version\");\n\t\tint nversion = -1;\n\t\tif      (strcmp(szversion, \"1.0\") == 0) nversion = 1;\n\t\telse if (strcmp(szversion, \"2.0\") == 0) nversion = 2;\n\t\telse if (strcmp(szversion, \"3.0\") == 0) nversion = 3;\n\t\telse if (strcmp(szversion, \"4.0\") == 0) nversion = 4;\n\n\t\tif (nversion == -1) return errf(\"FATAL ERROR: Incorrect restart file version\\n\");\n\n\t\t// Add the Step section for version 2\n\t\tif (nversion == 2)\n\t\t{\n\t\t\t// set the file version to make sure we are using the correct format\n\t\t\tSetFileVerion(0x0205);\n\n\t\t\t// make sure we can redefine curves in the LoadData section\n\t\t\tFEBioLoadDataSection* lcSection = new FEBioLoadDataSection(this);\n\t\t\tlcSection->SetRedefineCurvesFlag(true);\n\t\t\tm_map[\"LoadData\"] = lcSection;\n\n\t\t\tm_map[\"Step\"] = new FEBioStepSection25(this);\n\t\t}\n\n\t\t// Add the Step section for version 3\n\t\tif (nversion == 3)\n\t\t{\n\t\t\t// set the file version to make sure we are using the correct format\n\t\t\tSetFileVerion(0x0300);\n\n\t\t\t// make sure we can redefine curves in the LoadData section\n\t\t\tFEBioLoadDataSection3* lcSection = new FEBioLoadDataSection3(this);\n\t\t\tlcSection->SetRedefineCurvesFlag(true);\n\t\t\tm_map[\"LoadData\"] = lcSection;\n\n\t\t\tm_map[\"Step\"] = new FEBioStepSection3(this);\n\t\t}\n\n\t\t// Add the Step section for version 4\n\t\tif (nversion == 4)\n\t\t{\n\t\t\t// set the file version to make sure we are using the correct format\n\t\t\tSetFileVerion(0x0400);\n\n\t\t\t// make sure we can redefine curves in the LoadData section\n\t\t\tFEBioLoadDataSection3* lcSection = new FEBioLoadDataSection3(this);\n\t\t\tlcSection->SetRedefineCurvesFlag(true);\n\t\t\tm_map[\"LoadData\"] = lcSection;\n\n\t\t\tm_map[\"Step\"] = new FEBioStepSection4(this);\n\t\t}\n\n\t\t// the first section has to be the archive\n\t\t++tag;\n\t\tif (tag != \"Archive\") return errf(\"FATAL ERROR: The first element must be the archive name\\n\");\n\t\tchar szar[256];\n\t\ttag.value(szar);\n\n\t\t// open the archive\n\t\tDumpFile ar(fem);\n\t\tif (ar.Open(szar) == false) return errf(\"FATAL ERROR: failed opening restart archive\\n\");\n\n\t\t// read the archive\n\t\tfem.Serialize(ar);\n\n\t\t// set the module name\n\t\tGetBuilder()->SetActiveModule(fem.GetModuleName());\n\n\t\t// keep track of the number of steps\n\t\tint steps0 = fem.Steps();\n\n\t\t// read the rest of the restart input file\n\t\tret = ParseFile(tag);\n\n\t\t// count nr of newly added steps\n\t\tint steps1 = fem.Steps();\n\t\tm_newSteps = steps1 - steps0;\n\t}\n\tcatch (XMLReader::Error& e)\n\t{\n\t\tfprintf(stderr, \"FATAL ERROR: %s\\n\", e.what());\n\t\tret = false;\n\t}\n\tcatch (...)\n\t{\n\t\tfprintf(stderr, \"FATAL ERROR: unrecoverable error (line %d)\\n\", m_xml.GetCurrentLine());\n\t\tret = false;\n\t}\n\n\t// close the XML file\n\tm_xml.Close();\n\n\t// we're done!\n\treturn ret;\n}\n"
  },
  {
    "path": "FEBioXML/FERestartImport.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FileImport.h\"\n#include <FEBioXML/XMLReader.h>\n\n//-----------------------------------------------------------------------------\nclass FERestartControlSection : public FEFileSection\n{\npublic:\n\tFERestartControlSection(FEFileImport* reader) : FEFileSection(reader) {}\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n//! Restart input file reader.\nclass FEBIOXML_API FERestartImport : public FEFileImport\n{\npublic:\n\tFERestartImport();\n\tvirtual ~FERestartImport();\n\n\tbool Load(FEModel& fem, const char* szfile);\n\n\tint StepsAdded() const;\n\npublic:\n\tchar\t\tm_szdmp[256];\t// user defined restart file name\n\nprotected:\n\tXMLReader\tm_xml;\t\t\t// the file reader\n\tint\t\tm_newSteps;\t\t// nr of new steps added\n};\n"
  },
  {
    "path": "FEBioXML/FileImport.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FileImport.h\"\n#include <FECore/FENodeDataMap.h>\n#include <FECore/FESurfaceMap.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEMaterial.h>\n#include <FECore/FEModelParam.h>\n#include <FECore/FESurface.h>\n#include <FECore/FEEdge.h>\n#include <FECore/FESurfaceLoad.h>\n#include <FECore/FEPointFunction.h>\n#include <FECore/FEGlobalData.h>\n#include <FECore/FEScriptedBehavior.h>\n#include <FECore/log.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdarg.h>\n#include <sstream>\n#include <iostream>\n#include \"FEBioImport.h\"\n\n#ifndef WIN32\n#define strnicmp strncasecmp\n#endif\n\n//-----------------------------------------------------------------------------\n// helper function to see if a string is a number\nbool is_number(const char* sz)\n{\n\tchar* cend;\n\tdouble tmp = strtod(sz, &cend);\n\treturn ((cend == nullptr) || (cend[0] == 0));\n}\n\n//-----------------------------------------------------------------------------\nFEObsoleteParamHandler::FEObsoleteParamHandler(XMLTag& tag, FECoreBase* pc) : m_pc(pc) \n{\n\tm_root = tag.Name();\n}\n\nvoid FEObsoleteParamHandler::AddParam(const char* oldName, const char* newName, FEParamType paramType)\n{\n\tFEObsoleteParam p = { oldName, newName, paramType };\n\tm_param.push_back(p);\n}\n\nbool FEObsoleteParamHandler::ProcessTag(XMLTag& tag)\n{\n\tstd::string path = tag.relpath(m_root.c_str());\n\tfor (int i = 0; i < m_param.size(); ++i)\n\t{\n\t\tif (path == m_param[i].oldName)\n\t\t{\n\t\t\tm_param[i].readIn = true;\n\n\t\t\tswitch (m_param[i].paramType)\n\t\t\t{\n\t\t\tcase FE_PARAM_INVALID: break; // parameter will be ignored\n\t\t\tcase FE_PARAM_BOOL   : tag.value(m_param[i].bVal); break;\n\t\t\tcase FE_PARAM_INT    : tag.value(m_param[i].iVal); break;\n\t\t\tcase FE_PARAM_DOUBLE : tag.value(m_param[i].gVal); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid FEObsoleteParamHandler::MapParameters()\n{\n\tFEModel* fem = m_pc->GetFEModel();\n\tfeLogEx(fem, \"\\n\");\n\tfor (FEObsoleteParam& p : m_param)\n\t{\n\t\tif (p.readIn)\n\t\t{\n\t\t\tif (p.newName == nullptr)\n\t\t\t{\n\t\t\t\tfeLogWarningEx(fem, \"Obsolete parameter %s is ignored!\", p.oldName);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tParamString ps(p.newName);\n\t\t\t\tFEParam* pp = m_pc->FindParameter(ps);\n\t\t\t\tif (pp == nullptr)\n\t\t\t\t{\n\t\t\t\t\tfeLogErrorEx(fem, \"Failed to map obsolete parameter %s. Could not find new parameter %s\", p.oldName, p.newName);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (pp->type() == p.paramType)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (pp->type())\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase FE_PARAM_BOOL  : pp->value<bool>  () = p.bVal; break;\n\t\t\t\t\t\tcase FE_PARAM_INT   : pp->value<int>   () = p.iVal; break;\n\t\t\t\t\t\tcase FE_PARAM_DOUBLE: pp->value<double>() = p.gVal; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfeLogEx(fem, \"Successfully mapped obsolete parameter %s to %s\\n\", p.oldName, p.newName);\n\t\t\t\t\t}\n\t\t\t\t\telse if ((pp->type() == FE_PARAM_DOUBLE_MAPPED) && (p.paramType == FE_PARAM_DOUBLE))\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamDouble& v = pp->value<FEParamDouble>();\n\t\t\t\t\t\tv = p.gVal;\n\t\t\t\t\t\tfeLogEx(fem, \"Successfully mapped obsolete parameter %s to %s\\n\", p.oldName, p.newName);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfeLogErrorEx(fem, \"Failed to map obsolete parameter %s. New parameter %s has different type.\", p.oldName, p.newName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEFileException::FEFileException()\n{\n\tm_szerr[0] = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEFileException::FEFileException(const char* sz, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tva_start(args, sz);\n\tvsnprintf(m_szerr, sizeof(m_szerr), sz, args);\n\tva_end(args);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileException::SetErrorString(const char* sz, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tva_start(args, sz);\n\tvsnprintf(m_szerr, sizeof(m_szerr), sz, args);\n\tva_end(args);\n}\n\n//-----------------------------------------------------------------------------\nFEModel* FEFileSection::GetFEModel() { return &GetBuilder()->GetFEModel(); }\n\n//-----------------------------------------------------------------------------\nFEModelBuilder* FEFileSection::GetBuilder() { return m_pim->GetBuilder(); }\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::SetInvalidTagHandler(FEInvalidTagHandler* ith)\n{\n\tm_ith = ith;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, int& n)\n{\n\tconst char* val = tag.szvalue();\n\tif (is_number(val) == false) throw XMLReader::InvalidValue(tag);\n\tn = atoi(val);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, double& g)\n{\n\tg = atof(tag.szvalue());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, bool& b)\n{\n\tconst char* sz = tag.szvalue();\n\tint n = 0;\n\tsscanf(sz, \"%d\", &n);\n\tb = (n != 0);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, vec2d& v)\n{\n\tconst char* sz = tag.szvalue();\n\tint n = sscanf(sz, \"%lg,%lg\", &v.r[0], &v.r[1]);\n\tif (n != 3) throw XMLReader::XMLSyntaxError(tag.m_nstart_line);\n}\n\nvoid FEFileSection::value(XMLTag& tag, vec3d& v)\n{\n\tconst char* sz = tag.szvalue();\n\tint n = sscanf(sz, \"%lg,%lg,%lg\", &v.x, &v.y, &v.z);\n\tif (n != 3) throw XMLReader::XMLSyntaxError(tag.m_nstart_line);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, mat3d& m)\n{\n\tconst char* sz = tag.szvalue();\n\tdouble xx, xy, xz, yx, yy, yz, zx, zy, zz;\n\tint n = sscanf(sz, \"%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg\", &xx, &xy, &xz, &yx, &yy, &yz, &zx, &zy, &zz);\n\tif (n != 9) throw XMLReader::XMLSyntaxError(tag.m_nstart_line);\n\tm = mat3d(xx, xy, xz, yx, yy, yz, zx, zy, zz);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, mat3ds& m)\n{\n\tconst char* sz = tag.szvalue();\n\tdouble x, y, z, xy, yz, xz;\n\tint n = sscanf(sz, \"%lg,%lg,%lg,%lg,%lg,%lg\", &x, &y, &z, &xy, &yz, &xz);\n\tif (n != 6) throw XMLReader::XMLSyntaxError(tag.m_nstart_line);\n\tm = mat3ds(x, y, z, xy, yz, xz);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, tens3drs& m)\n{\n\tdouble v[18];\n\tint n = value(tag, v, 18);\n\tif (n != 18) throw XMLReader::InvalidValue(tag);\n\tfor (int i = 0; i<18; ++i) m.d[i] = v[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, char* szstr)\n{\n\tconst char* sz = tag.szvalue();\n\tstrcpy(szstr, sz);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, std::string& v)\n{\n\tv = tag.szvalue();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, std::vector<int>& v)\n{\n\tv.clear();\n\tconst char* sz = tag.szvalue();\n\tdo\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tint d = atoi(sz);\n\t\tv.push_back(d);\n\n\t\tif (sze) sz = sze + 1;\n\t\telse sz = nullptr;\n\n\t}\n\twhile (sz);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::value(XMLTag& tag, std::vector<double>& v)\n{\n\tv.clear();\n\tconst char* sz = tag.szvalue();\n\tdo\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tconst char* szc = strchr(sz, ':');\n\t\tif ((szc == nullptr) || (sze && (szc > sze)))\n\t\t{\n\t\t\tdouble d = atof(sz);\n\t\t\tv.push_back(d);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble d0 = atof(sz);\n\t\t\tdouble d1 = atof(szc + 1);\n\t\t\tdouble di = 1.0;\n\t\t\t// see if there is a second colon\n\t\t\tconst char* szc2 = strchr(szc + 1, ':');\n\t\t\tif (szc2 && ((sze == nullptr) || (szc2 < sze))) di = atof(szc2 + 1);\n\t\t\tif (di > 0)\n\t\t\t{\n\t\t\t\tconst double eps = 1e-12;\n\t\t\t\tfor (double d = d0; d <= d1 + eps; d += di)\n\t\t\t\t{\n\t\t\t\t\tv.push_back(d);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse throw XMLReader::InvalidValue(tag);\n\t\t}\n\n\t\tif (sze) sz = sze + 1;\n\t\telse sz = nullptr;\n\t} \n\twhile (sz);\n}\n\n//-----------------------------------------------------------------------------\nint FEFileSection::value(XMLTag& tag, int* pi, int n)\n{\n\tconst char* sz = tag.szvalue();\n\tint nr = 0;\n\tfor (int i = 0; i<n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tpi[i] = atoi(sz);\nnr++;\n\nif (sze) sz = sze + 1;\nelse break;\n\t}\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\nint FEFileSection::value(XMLTag& tag, double* pf, int n)\n{\n\tconst char* sz = tag.szvalue();\n\tint nr = 0;\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tpf[i] = atof(sz);\n\t\tnr++;\n\n\t\tif (sze) sz = sze + 1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\nint FEFileSection::ReadNodeID(XMLTag& tag)\n{\n\tint n = atoi(tag.AttributeValue(\"id\"));\n\tint node = GetBuilder()->FindNodeFromID(n);\n\tif (node == -1) throw FEFileException(\"Invalid node ID\");\n\treturn node;\n}\n\n//-----------------------------------------------------------------------------\nint enumValue(const char* val, const char* szenum)\n{\n\tif ((val == nullptr) || (szenum == nullptr)) return -1;\n\n\t// get the string's length. \n\t// there could be a comma, so correct for that.\n\tsize_t L = strlen(val);\n\tconst char* c = strchr(val, ',');\n\tif (c) L = c - val;\n\n\tconst char* ch = szenum;\n\n\tint n = 0;\n\twhile (ch && *ch)\n\t{\n\t\tsize_t l = strlen(ch);\n\t\tint nval = n;\n\t\t// see if the value of the enum is overridden\n\t\tconst char* ce = strrchr(ch, '=');\n\t\tif (ce)\n\t\t{\n\t\t\tl = ce - ch;\n\t\t\tnval = atoi(ce + 1);\n\t\t}\n\n\t\tif ((L==l) && (strnicmp(ch, val, l) == 0))\n\t\t{\n\t\t\treturn nval;\n\t\t}\n\t\tch = strchr(ch, '\\0');\n\t\tif (ch) ch++;\n\t\tn++;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFileSection::parseEnumParam(FEParam* pp, const char* val)\n{\n\t// get the enums\n\tconst char* szenums = pp->enums();\n\tif (szenums == nullptr) return false;\n\n\t// special cases\n\tif (szenums[0] == '$')\n\t{\n\t\tFEModel* fem = GetFEModel();\n\n\t\tchar var[256] = { 0 };\n\t\tconst char* chl = strchr(szenums, '('); assert(chl);\n\t\tconst char* chr = strchr(szenums, ')'); assert(chr);\n\t\tstrncpy(var, chl + 1, chr - chl - 1);\n\n\t\tif (strncmp(var, \"dof_list\", 8) == 0)\n\t\t{\n\t\t\tDOFS& dofs = GetFEModel()->GetDOFS();\n\t\t\tconst char* szvar = nullptr;\n\t\t\tif (var[8] == ':') szvar = var + 9;\n\n\t\t\tif (pp->type() == FE_PARAM_INT)\n\t\t\t{\n\t\t\t\tint ndof = dofs.GetDOF(val, szvar);\n\t\t\t\tif (ndof < 0) return false;\n\t\t\t\tpp->value<int>() = ndof;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if (pp->type() == FE_PARAM_STD_VECTOR_INT)\n\t\t\t{\n\t\t\t\tstd::vector<int>& v = pp->value<std::vector<int> >();\n\t\t\t\treturn dofs.ParseDOFString(val, v, szvar);\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\t\telse if (strcmp(var, \"solutes\") == 0)\n\t\t{\n\t\t\tint n = -1;\n\t\t\tif (is_number(val)) n = atoi(val);\n\t\t\telse\n\t\t\t{\n\t\t\t\tFEGlobalData* pd = fem->FindGlobalData(val);\n\t\t\t\tif (pd == nullptr) return false;\n\t\t\t\tn = pd->GetID(); assert(n > 0);\n\t\t\t}\n\t\t\t\t\n\t\t\tpp->value<int>() = n;\n\t\t\treturn true;\n\t\t}\n\t\telse if (strcmp(var, \"sbms\") == 0)\n\t\t{\n\t\t\tint n = -1;\n\t\t\tif (is_number(val)) n = atoi(val);\n\t\t\telse\n\t\t\t{\n\t\t\t\tFEGlobalData* pd = fem->FindGlobalData(val);\n\t\t\t\tif (pd == nullptr) return false;\n\t\t\t\tn = pd->GetID(); assert(n > 0);\n\t\t\t}\n\n\t\t\tpp->value<int>() = n;\n\t\t\treturn true;\n\t\t}\n\t\telse if (strcmp(var, \"species\") == 0)\n\t\t{\n\t\t\tint n = -1;\n\t\t\tif (is_number(val)) n = atoi(val);\n\t\t\telse\n\t\t\t{\n\t\t\t\t// NOTE: This assumes that the solutes are defined before the SBMS!\n\t\t\t\tint m = fem->FindGlobalDataIndex(val);\n\t\t\t\tif (m == -1) return false;\n\t\t\t\tn = m + 1;\n\t\t\t}\n\t\t\tpp->value<int>() = n;\n\t\t\treturn true;\n\t\t}\n\t\telse if (strcmp(var, \"rigid_materials\") == 0)\n\t\t{\n\t\t\tif (is_number(val))\n\t\t\t{\n\t\t\t\tint n = atoi(val);\n\t\t\t\tpp->value<int>() = n;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFEMaterial* mat = fem->FindMaterial(val);\n\t\t\t\tif (mat == nullptr) return false;\n\t\t\t\tint n = mat->GetID();\n\t\t\t\tpp->value<int>() = n;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse return false;\n\t}\n\telse if (strncmp(szenums, \"@factory_list\", 13) == 0)\n\t{\n\t\tint classID = atoi(szenums + 14);\n\n\t\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\t\tfor (int i = 0; i < fecore.FactoryClasses(); ++i)\n\t\t{\n\t\t\tconst FECoreFactory* fac = fecore.GetFactoryClass(i);\n\t\t\tif (fac->GetSuperClassID() == classID)\n\t\t\t{\n\t\t\t\tif (strcmp(fac->GetTypeStr(), val) == 0)\n\t\t\t\t{\n\t\t\t\t\tpp->value<int>() = i;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tswitch (pp->type())\n\t{\n\tcase FE_PARAM_INT:\n\t{\n\t\tint n = enumValue(val, szenums);\n\t\tif (n != -1) pp->value<int>() = n;\n\t\telse\n\t\t{\n\t\t\t// see if the value is an actual number\n\t\t\tif (is_number(val))\n\t\t\t{\n\t\t\t\tn = atoi(val);\n\t\t\t\tpp->value<int>() = n;\n\t\t\t}\n\t\t}\n\t\treturn (n != -1);\n\t}\n\tbreak;\n\tcase FE_PARAM_STD_VECTOR_INT:\n\t{\n\t\tstd::vector<int>& v = pp->value<std::vector<int> >();\n\t\tv.clear();\n\t\tconst char* tmp = val;\n\t\twhile (tmp)\n\t\t{\n\t\t\tint n = enumValue(tmp, szenums);\n\t\t\tv.push_back(n);\n\t\t\ttmp = strchr(tmp, ',');\n\t\t\tif (tmp) tmp++;\n\t\t}\n\n\t\treturn true;\n\t}\n\tbreak;\n\t};\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nstd::vector<std::string> split_string(const std::string& s, char delim)\n{\n\tstd::stringstream ss(s);\n\tstd::string tmp;\n\tstd::vector<std::string> vs;\n\twhile (std::getline(ss, tmp, delim))\n\t{\n\t\tvs.push_back(tmp);\n\t}\n\treturn vs;\n}\n\nstd::string trim_string(const std::string& s) {\n\tauto start = std::find_if_not(s.begin(), s.end(), ::isspace);\n\tauto end = std::find_if_not(s.rbegin(), s.rend(), ::isspace).base();\n\tif (start >= end) return \"\";  // All spaces or empty string\n\treturn std::string(start, end);\n}\n\n//-----------------------------------------------------------------------------\n//! This function parses a parameter list\nbool FEFileSection::ReadParameter(XMLTag& tag, FEParameterList& pl, const char* szparam, FECoreBase* pc, bool parseAttributes)\n{\n\tFEParam* pp = nullptr;\n\tconst char* szparamName = (szparam == 0 ? tag.Name() : szparam);\n\n\t// Read a user-parameter. \n\t// Note that we don't process this tag when parseAttributes is false. This is a bit of a hack to get around\n\t// an issue with reading valuators. \n\tif ((tag == \"add_param\") && parseAttributes)\n\t{\n\t\t// get the name and value\n\t\tconst char* szname = tag.AttributeValue(\"name\");\n\n\t\tconst char* sztype = tag.AttributeValue(\"data_type\", true);\n\n\t\tbool allowMappedParams = true;\n\t\tbool allowVolatileParams = true;\n\n\t\tif (auto scripted = dynamic_cast<FEScriptedBehavior*>(pc))\n\t\t{\n\t\t\tScriptContext sc = scripted->GetScriptContext();\n\t\t\tallowMappedParams = sc.allowMappedInputs;\n\t\t\tallowVolatileParams = sc.allowVolatileInputs;\n\t\t}\n\n\t\t// make sure this parameter does not exist yet\n\t\tpp = pl.FindFromName(szname);\n\t\tif (pp) throw XMLReader::InvalidTag(tag);\n\n\t\t// add a new user parameter\n\t\tif ((sztype == nullptr) || strcmp(sztype, \"double\") == 0)\n\t\t{\n\t\t\tdouble v = 0.0; tag.value(v);\n\t\t\tif (allowMappedParams)\n\t\t\t\tpp = pl.AddParameter(new FEParamDouble(v), FE_PARAM_DOUBLE_MAPPED, 1, strdup(szname));\n\t\t\telse\n\t\t\t\tpp = pl.AddParameter(new FEParamDouble(v), FE_PARAM_DOUBLE, 1, strdup(szname));\n\n\t\t\tpp->MakeVolatile(allowVolatileParams);\n\t\t}\n\t\telse if (strcmp(sztype, \"vec3\") == 0)\n\t\t{\n\t\t\tvec3d v(0, 0, 0); value(tag, v);\n\t\t\tif (allowMappedParams)\n\t\t\t\tpp = pl.AddParameter(new FEParamVec3(v), FE_PARAM_VEC3D_MAPPED, 1, strdup(szname));\n\t\t\telse\n\t\t\t\tpp = pl.AddParameter(new FEParamVec3(v), FE_PARAM_VEC3D, 1, strdup(szname));\n\n\t\t\tpp->MakeVolatile(allowVolatileParams);\n\t\t}\n\t\telse if (strcmp(sztype, \"mat3\") == 0)\n\t\t{\n\t\t\tmat3d m; value(tag, m);\n\t\t\tif (allowMappedParams)\n\t\t\t\tpp = pl.AddParameter(new FEParamMat3d(m), FE_PARAM_MAT3D_MAPPED, 1, strdup(szname));\n\t\t\telse\n\t\t\t\tpp = pl.AddParameter(new FEParamMat3d(m), FE_PARAM_MAT3D, 1, strdup(szname));\n\n\t\t\tpp->MakeVolatile(allowVolatileParams);\n\t\t}\n\t\telse if (strcmp(sztype, \"int\") == 0)\n\t\t{\n\t\t\tint v = 0; tag.value(v);\n\t\t\tpp = pl.AddParameter(new int(v), FE_PARAM_INT, 1, strdup(szname));\n\t\t}\n\t\telse if (strcmp(sztype, \"bool\") == 0)\n\t\t{\n\t\t\tbool v = false; tag.value(v);\n\t\t\tpp = pl.AddParameter(new bool(v), FE_PARAM_BOOL, 1, strdup(szname));\n\t\t}\n\t\telse\n\t\t\tthrow XMLReader::InvalidAttributeValue(tag, \"data_type\", sztype);\n\n\t\t// add the user parameter flag\n\t\tpp->SetFlags(pp->GetFlags() | FEParamFlag::FE_PARAM_USER);\n\t}\n\telse\n\t{\n\t\t// see if we can find this parameter\n\t\tpp = pl.FindFromName(szparamName);\n\t}\n\tif (pp == 0) return false;\n\n\tif (pp->IsObsolete())\n\t{\n\t\tfeLogWarning(\"parameter '%s' is obsolete.\", szparamName);\n\t}\n\n\tif (pp->dim() == 1)\n\t{\n\t\tswitch (pp->type())\n\t\t{\n\t\t\tcase FE_PARAM_DOUBLE: \n\t\t\t{\n\t\t\t\t// make sure the type attribute is not defined \n\t\t\t\t// This most likely means a user thinks this parameter can be mapped\n\t\t\t\t// but the corresponding parameter is not a FEModelParam\n\t\t\t\tXMLAtt* att = tag.Attribute(\"type\", true);\n\t\t\t\tif (att)\n\t\t\t\t\tthrow XMLReader::InvalidAttribute(tag, \"type\");\n\n\t\t\t\tvalue(tag, pp->value<double  >());\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_INT: \n\t\t\t{\n\t\t\t\tconst char* szenum = pp->enums();\n\t\t\t\tif (szenum == 0)\n\t\t\t\t{\n\t\t\t\t\tvalue(tag, pp->value<int>());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbool bfound = parseEnumParam(pp, tag.szvalue());\n\t\t\t\t\tif (bfound == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((m_ith == nullptr) || (m_ith->ProcessTag(tag) == false)) {\n\t\t\t\t\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_STD_VECTOR_INT:\n\t\t\t{\n\t\t\t\tconst char* szenum = pp->enums();\n\t\t\t\tif (szenum == 0)\n\t\t\t\t{\n\t\t\t\t\tstd::vector<int>& v = pp->value< std::vector<int> >();\n\t\t\t\t\tvalue(tag, v);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbool bfound = parseEnumParam(pp, tag.szvalue());\n\t\t\t\t\tif (bfound == false) throw XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_STD_VECTOR_DOUBLE:\n\t\t\t{\n\t\t\t\tstd::vector<double>& v = pp->value< std::vector<double> >();\n\t\t\t\tvalue(tag, v);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_BOOL: value(tag, pp->value<bool    >()); break;\n\t\tcase FE_PARAM_VEC3D: \n\t\t\t{\n\t\t\t\t// make sure the type attribute is not defined \n\t\t\t\t// This most likely means a user thinks this parameter can be mapped\n\t\t\t\t// but the corresponding parameter is not a FEModelParam\n//\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n//\t\t\t\tif (sztype) throw XMLReader::InvalidAttribute(tag, \"type\");\n\n\t\t\t\tvalue(tag, pp->value<vec3d   >());\n\t\t\t}\n\t\t\tbreak;\n\t\tcase FE_PARAM_MAT3D: value(tag, pp->value<mat3d   >()); break;\n\t\tcase FE_PARAM_MAT3DS: value(tag, pp->value<mat3ds  >()); break;\n\t\tcase FE_PARAM_TENS3DRS: value(tag, pp->value<tens3drs>()); break;\n\t\tcase FE_PARAM_STRING: value(tag, pp->cvalue()); break;\n\t\tcase FE_PARAM_STD_STRING: value(tag, pp->value<string>()); break;\n\t\tcase FE_PARAM_DATA_ARRAY:\n\t\t{\n\t\t\t// get the surface map\n\t\t\tFEDataArray& map = pp->value<FEDataArray>();\n\n\t\t\t// Make sure that the tag is a leaf\n\t\t\tif (!tag.isleaf()) throw XMLReader::InvalidValue(tag);\n\n\t\t\t// read the surface map data\n\t\t\tconst char* szmap = tag.AttributeValue(\"surface_data\", true);\n\t\t\tif (szmap)\n\t\t\t{\n\t\t\t\tFESurfaceMap* pmap = dynamic_cast<FESurfaceMap*>(&map);\n\t\t\t\tif (pmap == 0) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\tFESurfaceMap* pdata = dynamic_cast<FESurfaceMap*>(GetFEModel()->GetMesh().FindDataMap(szmap));\n\t\t\t\tif (pdata == 0) throw XMLReader::InvalidAttributeValue(tag, \"surface_data\");\n\n\t\t\t\t// make sure the types match\n\t\t\t\tif (map.DataSize() != pdata->DataSize()) throw XMLReader::InvalidAttributeValue(tag, \"surface_data\", szmap);\n\n\t\t\t\t// copy data\n\t\t\t\t*pmap = *pdata;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconst char* szmap = tag.AttributeValue(\"node_data\", true);\n\t\t\t\tif (szmap)\n\t\t\t\t{\n\t\t\t\t\tFENodeDataMap* pmap = dynamic_cast<FENodeDataMap*>(&map);\n\t\t\t\t\tif (pmap == 0) throw XMLReader::InvalidTag(tag);\n\n\t\t\t\t\tFENodeDataMap* pdata = dynamic_cast<FENodeDataMap*>(GetFEModel()->GetMesh().FindDataMap(szmap));\n\t\t\t\t\tif (pdata == 0) throw XMLReader::InvalidAttributeValue(tag, \"node_data\");\n\n\t\t\t\t\t// make sure the types match\n\t\t\t\t\tif (map.DataSize() != pdata->DataSize()) throw XMLReader::InvalidAttributeValue(tag, \"node_data\", szmap);\n\n\t\t\t\t\t// copy data\n\t\t\t\t\t*pmap = *pdata;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFEDataType dataType = map.DataType();\n\t\t\t\t\tif (dataType == FE_DOUBLE)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble v;\n\t\t\t\t\t\ttag.value(v);\n\t\t\t\t\t\tmap.fillValue(v);\n\t\t\t\t\t}\n\t\t\t\t\telse if (dataType == FE_VEC2D)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble v[2] = { 0 };\n\t\t\t\t\t\ttag.value(v, 2);\n\t\t\t\t\t\tmap.fillValue(vec2d(v[0], v[1]));\n\t\t\t\t\t}\n\t\t\t\t\telse if (dataType == FE_VEC3D)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble v[3] = { 0 };\n\t\t\t\t\t\ttag.value(v, 3);\n\t\t\t\t\t\tmap.fillValue(vec3d(v[0], v[1], v[2]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tbreak;\n\t\tcase FE_PARAM_STD_VECTOR_VEC2D:\n\t\t{\n\t\t\tstd::vector<vec2d>& data = pp->value< std::vector<vec2d> >();\n\t\t\tdata.clear();\n\n\t\t\tdouble d[2];\n\t\t\t++tag;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tint nread = tag.value(d, 2);\n\t\t\t\tif (nread != 2) throw XMLReader::InvalidValue(tag);\n\t\t\t\tdata.push_back(vec2d(d[0], d[1]));\n\t\t\t\t++tag;\n\t\t\t}\n\t\t\twhile (!tag.isend());\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_STD_VECTOR_STRING:\n\t\t{\n\t\t\t// make sure this is leaf\n\t\t\tif (tag.isempty()) throw XMLReader::InvalidValue(tag);\n\n\t\t\tstd::vector<string>& data = pp->value< std::vector<string> >();\n\n\t\t\t// Note that this parameter is read in item per item, not all at once!\n\t\t\tstring s;\n\t\t\ttag.value(s);\n\t\t\tdata.push_back(s);\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t{\n\t\t\t// get the model parameter\n\t\t\tFEParamDouble& p = pp->value<FEParamDouble>();\n\n\t\t\t// get the type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t\t\t// if the type is not specified, we'll try to determine if \n\t\t\t// it's a math expression or a const\n\t\t\tif (sztype == 0)\n\t\t\t{\n\t\t\t\tconst char* szval = tag.szvalue();\n\t\t\t\tsztype = (is_number(szval) ? \"const\" : \"math\");\n\t\t\t}\n\n\t\t\t// allocate valuator\n\t\t\tFEScalarValuator* val = fecore_new<FEScalarValuator>(sztype, GetFEModel());\n\t\t\tif (val == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\tval->SetParent(pc);\n\n\t\t\t// Figure out the item list\n\t\t\tFEItemList* itemList = nullptr;\n\t\t\tif (dynamic_cast<FESurfaceLoad*>(pc))\n\t\t\t{\n\t\t\t\tFESurfaceLoad* psl = dynamic_cast<FESurfaceLoad*>(pc);\n\t\t\t\titemList = psl->GetSurface().GetFacetSet();\n\t\t\t}\n\n\t\t\tp.SetItemList(itemList);\n\n\t\t\t// mapped values require special treatment\n\t\t\t// The value is just the name of the map, but the problem is that \n\t\t\t// these maps may not be defined yet.\n\t\t\t// So, we add them to the FEBioModel, which will process mapped \n\t\t\t// parameters after the rest of the file is processed\n\t\t\tif (strcmp(sztype, \"map\") == 0)\n\t\t\t{\n\t\t\t\tstring mapName = tag.szvalue();\n\t\t\t\tstring trimmedName = trim_string(mapName);\n\t\t\t\tif (trimmedName.empty()) throw XMLReader::InvalidValue(tag);\n\t\t\t\tGetBuilder()->AddMappedParameter(pp, pc, trimmedName.c_str());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// read the parameter list\n\t\t\t\tReadParameterList(tag, val);\n\t\t\t}\n\n\t\t\t// assign the valuator to the parameter\n\t\t\tp.setValuator(val);\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_VEC3D_MAPPED:\n\t\t{\n\t\t\t// get the model parameter\n\t\t\tFEParamVec3& p = pp->value<FEParamVec3>();\n\n\t\t\t// get the type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t\t\t// ignore \"user\" types\n\t\t\tif (sztype && strcmp(sztype, \"user\") == 0)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (sztype == 0) sztype = \"vector\";\n\n\t\t\t// allocate valuator\n\t\t\tFEVec3dValuator* val = fecore_new<FEVec3dValuator>(sztype, GetFEModel());\n\t\t\tif (val == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t// mapped values require special treatment\n\t\t\t// The value is just the name of the map, but the problem is that \n\t\t\t// these maps may not be defined yet.\n\t\t\t// So, we add them to the FEBioModel, which will process mapped \n\t\t\t// parameters after the rest of the file is processed\n\t\t\tif (strcmp(sztype, \"map\") == 0)\n\t\t\t{\n\t\t\t\tstring mapName = tag.szvalue();\n\t\t\t\tstring trimmedName = trim_string(mapName);\n\t\t\t\tif (trimmedName.empty()) throw XMLReader::InvalidValue(tag);\n\t\t\t\tGetBuilder()->AddMappedParameter(pp, pc, trimmedName.c_str());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// read the parameter list\n\t\t\t\tReadParameterList(tag, val);\n\t\t\t}\n\n\t\t\t// assign the valuator to the parameter\n\t\t\tp.setValuator(val);\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_MAT3D_MAPPED:\n\t\t{\n\t\t\t// get the model parameter\n\t\t\tFEParamMat3d& p = pp->value<FEParamMat3d>();\n\n\t\t\t// get the type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype == 0) sztype = \"const\";\n\n\t\t\t// ignore user type\n\t\t\tif (sztype && (strcmp(sztype, \"user\") == 0)) return true;\n\n\t\t\t// allocate valuator\n\t\t\tFEMat3dValuator* val = fecore_new<FEMat3dValuator>(sztype, GetFEModel());\n\t\t\tif (val == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t// mapped values require special treatment\n\t\t\t// The value is just the name of the map, but the problem is that \n\t\t\t// these maps may not be defined yet.\n\t\t\t// So, we add them to the FEBioModel, which will process mapped \n\t\t\t// parameters after the rest of the file is processed\n\t\t\tif (strcmp(sztype, \"map\") == 0)\n\t\t\t{\n\t\t\t\tstring mapName = tag.szvalue();\n\t\t\t\tstring trimmedName = trim_string(mapName);\n\t\t\t\tif (trimmedName.empty()) throw XMLReader::InvalidValue(tag);\n\t\t\t\tGetBuilder()->AddMappedParameter(pp, pc, trimmedName.c_str());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// read the parameter list\n\t\t\t\tReadParameterList(tag, val);\n\t\t\t}\n\n\t\t\t// assign the valuator to the parameter\n\t\t\tp.setValuator(val);\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_MAT3DS_MAPPED:\n\t\t{\n\t\t\t// get the model parameter\n\t\t\tFEParamMat3ds& p = pp->value<FEParamMat3ds>();\n\n\t\t\t// get the type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype == 0) sztype = \"const\";\n\n\t\t\t// allocate valuator\n\t\t\tFEMat3dsValuator* val = fecore_new<FEMat3dsValuator>(sztype, GetFEModel());\n\t\t\tif (val == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t// mapped values require special treatment\n\t\t\t// The value is just the name of the map, but the problem is that \n\t\t\t// these maps may not be defined yet.\n\t\t\t// So, we add them to the FEBioModel, which will process mapped \n\t\t\t// parameters after the rest of the file is processed\n\t\t\tif (strcmp(sztype, \"map\") == 0)\n\t\t\t{\n\t\t\t\tstring mapName = tag.szvalue();\n\t\t\t\tstring trimmedName = trim_string(mapName);\n\t\t\t\tif (trimmedName.empty()) throw XMLReader::InvalidValue(tag);\n\t\t\t\tGetBuilder()->AddMappedParameter(pp, pc, trimmedName.c_str());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// read the parameter list\n\t\t\t\tReadParameterList(tag, val);\n\t\t\t}\n\n\t\t\t// assign the valuator to the parameter\n\t\t\tp.setValuator(val);\n\n\t\t\t// do the initialization.\n\t\t\t// TODO: Is this a good place to do this?\n\t\t\tif (val->Init() == false) throw XMLReader::InvalidTag(tag);\n\t\t}\n\t\tbreak;\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tswitch (pp->type())\n\t\t{\n\t\tcase FE_PARAM_INT: value(tag, pp->pvalue<int   >(), pp->dim()); break;\n\t\tcase FE_PARAM_DOUBLE: value(tag, pp->pvalue<double>(), pp->dim()); break;\n\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t{\n\t\t\tif (tag.isleaf())\n\t\t\t{\n\t\t\t\t// find the type attribute\n\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\t\tif (sztype == nullptr) sztype = \"const\";\n\n\t\t\t\tif (strcmp(sztype, \"const\") == 0)\n\t\t\t\t{\n\t\t\t\t\tstd::vector<double> v(pp->dim());\n\t\t\t\t\tint m = tag.value(&v[0], pp->dim());\n\t\t\t\t\tif (m != pp->dim()) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\tfor (int i = 0; i < pp->dim(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamDouble& pi = pp->value<FEParamDouble>(i);\n\t\t\t\t\t\tpi = v[i];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (strcmp(sztype, \"math\") == 0)\n\t\t\t\t{\n\t\t\t\t\tstring sval = tag.szvalue();\n\n\t\t\t\t\tvector<string> s = split_string(sval, ',');\n\t\t\t\t\tif (s.size() != pp->dim()) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\tfor (int i = 0; i < pp->dim(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamDouble& pi = pp->value<FEParamDouble>(i);\n\t\t\t\t\t\tFEMathValue* v = fecore_alloc(FEMathValue, GetFEModel());\n\t\t\t\t\t\tv->setMathString(s[i]);\n\t\t\t\t\t\tpi.setValuator(v);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (strcmp(sztype, \"map\") == 0)\n\t\t\t\t{ \n\t\t\t\t\tstring sval = tag.szvalue();\n\n\t\t\t\t\tvector<string> s = split_string(sval, ',');\n\t\t\t\t\tif (s.size() != pp->dim()) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\tfor (int i = 0; i < pp->dim(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamDouble& pi = pp->value<FEParamDouble>(i);\n\n\t\t\t\t\t\t// allocate valuator\n\t\t\t\t\t\tFEScalarValuator* val = fecore_alloc(FEMappedValue, GetFEModel());\n\n\t\t\t\t\t\t// Figure out the item list\n\t\t\t\t\t\tFEItemList* itemList = nullptr;\n\t\t\t\t\t\tif (dynamic_cast<FESurfaceLoad*>(pc))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFESurfaceLoad* psl = dynamic_cast<FESurfaceLoad*>(pc);\n\t\t\t\t\t\t\titemList = psl->GetSurface().GetFacetSet();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpi.SetItemList(itemList);\n\n\t\t\t\t\t\t// trim the string\n\t\t\t\t\t\tstd::string trimmedName = trim_string(s[i]);\n\n\t\t\t\t\t\t// mapped values require special treatment\n\t\t\t\t\t\t// The value is just the name of the map, but the problem is that \n\t\t\t\t\t\t// these maps may not be defined yet.\n\t\t\t\t\t\t// So, we add them to the FEBioModel, which will process mapped \n\t\t\t\t\t\t// parameters after the rest of the file is processed\n\t\t\t\t\t\tGetBuilder()->AddMappedParameter(pp, pc, trimmedName.c_str(), i);\n\n\t\t\t\t\t\t// assign the valuator to the parameter\n\t\t\t\t\t\tpi.setValuator(val);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint n = 0;\n\t\t\t\t++tag;\n\t\t\t\tdo {\n\t\t\t\t\t// find the type attribute\n\t\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\t\t\tif (sztype == nullptr)\n\t\t\t\t\t{\n\t\t\t\t\t\t// if the type is not specified, we'll try to determine if \n\t\t\t\t\t\t// it's a math expression or a const\n\t\t\t\t\t\tconst char* szval = tag.szvalue();\n\t\t\t\t\t\tsztype = (is_number(szval) ? \"const\" : \"math\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (strcmp(sztype, \"const\") == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble v = 0.0;\n\t\t\t\t\t\ttag.value(v);\n\t\t\t\t\t\tFEParamDouble& pi = pp->value<FEParamDouble>(n);\n\t\t\t\t\t\tpi = v;\n\t\t\t\t\t}\n\t\t\t\t\telse if (strcmp(sztype, \"math\") == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tstring sval = tag.szvalue();\n\n\t\t\t\t\t\tFEParamDouble& pi = pp->value<FEParamDouble>(n);\n\t\t\t\t\t\tFEMathValue* v = fecore_alloc(FEMathValue, GetFEModel());\n\t\t\t\t\t\tv->setMathString(sval);\n\t\t\t\t\t\tpi.setValuator(v);\n\n\t\t\t\t\t\t// do the initialization.\n\t\t\t\t\t\t// TODO: Is this a good place to do this?\n\t\t\t\t\t\tif (v->Init() == false) throw XMLReader::InvalidTag(tag);\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\t\t\t\t\t++tag;\n\t\t\t\t\tn++;\n\t\t\t\t\tif (n > pp->dim()) throw XMLReader::InvalidTag(tag);\n\t\t\t\t}\n\t\t\t\twhile (!tag.isend());\n\t\t\t}\n\t\t}\n\t\tbreak;\n        default: \n\t\t\tassert(false);\n\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (parseAttributes)\n\t{\n\t\tfor (XMLAtt& att : tag.m_att)\n\t\t{\n\t\t\tconst char* szat = att.m_name.c_str();\n\t\t\t// If we get here, the container did not understand the attribute.\n\t\t\t// If the attribute is a \"lc\", we interpret it as a load curve\n\t\t\tif (strcmp(szat, \"lc\") == 0)\n\t\t\t{\n\t\t\t\tint lc = atoi(att.m_val.c_str()) - 1;\n\t\t\t\tif (lc < 0) throw XMLReader::InvalidAttributeValue(tag, szat, att.m_val.c_str());\n\n\t\t\t\t// make sure the parameter is volatile\n\t\t\t\tif (pp->IsVolatile() == false)\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidAttribute(tag, szat);\n\t\t\t\t}\n\n\t\t\t\tGetFEModel()->AttachLoadController(pp, lc);\n\t\t\t}\n/*\t\t\telse\n\t\t\t{\n\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, szat, tag.m_att[i].m_szatv);\n\t\t\t}\n*/\n\t\t\t// This is not true. Parameters can have attributes that are used for other purposes. E.g. The local fiber option.\n\t\t\t//\t\telse felog.printf(\"WARNING: attribute \\\"%s\\\" of parameter \\\"%s\\\" ignored (line %d)\\n\", szat, tag.Name(), tag.m_ncurrent_line-1);\n\t\t}\n\t}\n\n\t// Set the watch flag since the parameter was read in successfully\n\t// (This requires that the parameter was declared with a watch variable)\n\tpp->SetWatchFlag(true);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::ReadAttributes(XMLTag& tag, FECoreBase* pc)\n{\n\tFEParameterList& pl = pc->GetParameterList();\n\n\t// process all the other attributes\n\tfor (XMLAtt& att : tag.m_att)\n\t{\n\t\tconst char* szatt = att.name();\n\t\tconst char* szval = att.cvalue();\n\t\tif ((att.m_bvisited == false) && szatt && szval)\n\t\t{\n\t\t\tFEParam* param = pl.FindFromName(szatt);\n\t\t\tif (param && (param->GetFlags() & FE_PARAM_ATTRIBUTE))\n\t\t\t{\n\t\t\t\tswitch (param->type())\n\t\t\t\t{\n\t\t\t\tcase FE_PARAM_INT:\n\t\t\t\t{\n\t\t\t\t\tif (param->enums() == nullptr)\n\t\t\t\t\t\tparam->value<int>() = atoi(szval);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (parseEnumParam(param, szval) == false) throw XMLReader::InvalidAttributeValue(tag, szatt, szval);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase FE_PARAM_DOUBLE: param->value<double>() = atof(szval); break;\n\t\t\t\tcase FE_PARAM_STD_STRING: param->value<std::string>() = szval; break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, szatt, szval);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (strcmp(\"name\", szatt) == 0) pc->SetName(szval);\n\t\t\telse if (strcmp(\"id\"  , szatt) == 0) pc->SetID(atoi(szval));\n\t\t\telse if (strcmp(\"lc\"  , szatt) == 0) { /* don't do anything. Loadcurves are processed elsewhere. */ }\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (m_pim->StopOnUnknownAttribute())\n\t\t\t\t{\n\t\t\t\t\tthrow XMLReader::InvalidAttribute(tag, szatt);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfprintf(stderr, \"WARNING: Unknown attribute %s in tag %s\\n\", szatt, tag.Name());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function parses a parameter list\nbool FEFileSection::ReadParameter(XMLTag& tag, FECoreBase* pc, const char* szparam, bool parseAttributes)\n{\n\t// get the parameter list\n\tFEParameterList& pl = pc->GetParameterList();\n\n\t// see if we can find this parameter\n\tif (ReadParameter(tag, pl, szparam, pc, parseAttributes) == false)\n\t{\n\t\t// if we get here, the parameter is not found.\n\t\t// See if the parameter container has defined a property of this name\n\t\tint n = pc->FindPropertyIndex(tag.Name());\n\t\tif (n >= 0)\n\t\t{\n\t\t\tFEProperty* prop = pc->PropertyClass(n);\n\n\t\t\tif (prop->IsReference())\n\t\t\t{\n\t\t\t\t// get the reference. It is either defined by the ref attribute\n\t\t\t\t// or the value of the tag.\n\t\t\t\tif (tag.isleaf() == false) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\tconst char* szref = tag.AttributeValue(\"ref\", true);\n\t\t\t\tif (szref == nullptr) szref = tag.szvalue();\n\n\t\t\t\tconst char* sztag = tag.Name();\n\n\t\t\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\t\t\t\t// This property should reference an existing class\n\t\t\t\tSUPER_CLASS_ID classID = prop->GetSuperClassID();\n/*\t\t\t\tif (classID == FEITEMLIST_ID)\n\t\t\t\t{\n\t\t\t\t\tFENodeSet* nodeSet = mesh.FindNodeSet(szref);\n\t\t\t\t\tif (nodeSet == nullptr) throw XMLReader::InvalidValue(tag);\n\t\t\t\t\tprop->SetProperty(nodeSet);\n\t\t\t\t}\n\t\t\t\telse */if (classID == FESURFACE_ID)\n\t\t\t\t{\n\t\t\t\t\tFEModelBuilder* builder = GetBuilder();\n\t\t\t\t\tFEFacetSet* facetSet = mesh.FindFacetSet(szref);\n\t\t\t\t\tif (facetSet == nullptr) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\tFESurface* surface = fecore_alloc(FESurface, GetFEModel());\n\t\t\t\t\tGetBuilder()->BuildSurface(*surface, *facetSet);\n\t\t\t\t\tmesh.AddSurface(surface);\n\n\t\t\t\t\tprop->SetProperty(surface);\n\t\t\t\t}\n\t\t\t\telse if (classID == FEEDGE_ID)\n\t\t\t\t{\n\t\t\t\t\tFEModelBuilder* builder = GetBuilder();\n\t\t\t\t\tFESegmentSet* edgeList = mesh.FindSegmentSet(szref);\n\t\t\t\t\tif (edgeList == nullptr) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\tFEEdge* edge = dynamic_cast<FEEdge*>(prop->get(0));\n\t\t\t\t\tif (edge == nullptr) throw XMLReader::InvalidValue(tag);\n\n\t\t\t\t\tif (GetBuilder()->BuildEdge(*edge, *edgeList))\n\t\t\t\t\t\tmesh.AddEdge(edge);\n\t\t\t\t\telse\n\t\t\t\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t\t\t}\n\t\t\t\telse throw XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// see if the property is already allocated\n\t\t\t\tif ((prop->IsArray() == false) && (prop->get(0)))\n\t\t\t\t{\n\t\t\t\t\t// If so, let's just read the parameters\n\t\t\t\t\tFECoreBase* pc = prop->get(0);\n\n\t\t\t\t\tconst char* szname = tag.AttributeValue(\"name\", true);\n\t\t\t\t\tif (szname) pc->SetName(szname);\n\n\t\t\t\t\tif (tag.isleaf() == false) ReadParameterList(tag, pc);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t\t\t\t\t// If the type attribute is omitted we try the property's default type,\n\t\t\t\t\t// otherwise assume the tag's name is the default type\n\t\t\t\t\tif (sztype == nullptr)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (prop->GetDefaultType()) sztype = prop->GetDefaultType();\n\t\t\t\t\t\telse sztype = tag.Name();\n\t\t\t\t\t}\n\n\t\t\t\t\t// HACK for getting passed the old \"user\" fiber type.\n\t\t\t\t\tif (strcmp(sztype, \"user\") == 0) sztype = \"map\";\n\n\t\t\t\t\t// HACK for mapping load curves to FEFunction1D\n\t\t\t\t\tconst char* szlc = tag.AttributeValue(\"lc\", true);\n\t\t\t\t\tif (szlc && (tag.m_att.size() == 1) && (prop->GetSuperClassID() == FEFUNCTION1D_ID))\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble v = 1;\n\t\t\t\t\t\ttag.value(v);\n\t\t\t\t\t\tFEPointFunction* f = fecore_alloc(FEPointFunction, GetFEModel()); assert(f);\n\t\t\t\t\t\tprop->SetProperty(f);\n\n\t\t\t\t\t\tint lc = atoi(szlc) - 1;\n\t\t\t\t\t\tGetBuilder()->MapLoadCurveToFunction(f, lc, v);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// try to allocate the class\n\t\t\t\t\t\tFECoreBase* pp = fecore_new<FECoreBase>(prop->GetSuperClassID(), sztype, GetFEModel());\n\t\t\t\t\t\tif (pp == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\t\t\tprop->SetProperty(pp);\n\n\t\t\t\t\t\t// read the property data\n\t\t\t\t\t\tif (tag.isleaf() == false)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tReadParameterList(tag, pp);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (tag.isempty() == false)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((tag.szvalue() != nullptr) && (tag.szvalue()[0] != 0))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// parse attributes first\n\t\t\t\t\t\t\t\tReadAttributes(tag, pp);\n\n\t\t\t\t\t\t\t\t// There should be a parameter with the same name as the type\n\t\t\t\t\t\t\t\tif (ReadParameter(tag, pp->GetParameterList(), sztype, pp) == false)\n\t\t\t\t\t\t\t\t\tthrow XMLReader::InvalidValue(tag);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// parse attributes first\n\t\t\t\t\t\t\tReadAttributes(tag, pp);\n\n\t\t\t\t\t\t\t// we get here if the property was defined with an empty tag.\n\t\t\t\t\t\t\t// We should still validate it.\n\t\t\t\t\t\t\tint NP = pp->PropertyClasses();\n\t\t\t\t\t\t\tfor (int i = 0; i < NP; ++i)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFEProperty* pi = pp->PropertyClass(i);\n\t\t\t\t\t\t\t\tbool a = pi->IsRequired();\n\t\t\t\t\t\t\t\tbool b = (pi->size() == 0);\n\t\t\t\t\t\t\t\tif (a && b)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstd::string name = pp->GetName();\n\t\t\t\t\t\t\t\t\tif (name.empty()) name = prop->GetName();\n\t\t\t\t\t\t\t\t\tthrow FEBioImport::MissingProperty(name, pi->GetName());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// backward compatibility hack for older formats (< v 3.0)\n/*\t\t\tif (strcmp(tag.Name(), \"fiber\") == 0)\n\t\t\t{\n\t\t\t\treturn ReadParameter(tag, pc, \"mat_axis\", parseAttributes);\n\t\t\t}\n\t\t\telse return false;\n*/\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::ReadParameterList(XMLTag& tag, FEParameterList& pl)\n{\n\t// Make sure there is something to read\n\tif (tag.isleaf() || tag.isempty()) return;\n\n\t// parse the child tags\n\t++tag;\n\tdo\n\t{\n\t\tif (ReadParameter(tag, pl, 0, 0) == false) throw XMLReader::InvalidTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSection::ReadParameterList(XMLTag& tag, FECoreBase* pc)\n{\n\t// parse attributes first\n\tReadAttributes(tag, pc);\n\n\t// process the parameter lists\n\tif (!tag.isleaf())\n\t{\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (ReadParameter(tag, pc) == false)\n\t\t\t{\n\t\t\t\tif ((m_ith == nullptr) || (m_ith->ProcessTag(tag) == false))\n\t\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t\t}\n\t\t\t++tag;\n\t\t}\n\t\twhile (!tag.isend());\n\t}\n\telse if ((tag.isempty() == false) && (pc->Parameters() > 0))\n\t{\n\t\t// there should be one parameter with the same name as the tag\n\t\t// Notice that we don't process attributes, since this situation should be synonymous \n\t\t// to an additional tag that defines the parameter.\n\t\tif (ReadParameter(tag, pc, 0, false) == false)\n\t\t{\n\t\t\t// try a parameter with the type string as name\n\t\t\tif (ReadParameter(tag, pc, pc->GetTypeStr(), false) == false)\n\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t}\n\t}\n\n\t// validate the class\n\tint NP = pc->PropertyClasses();\n\tfor (int i = 0; i<NP; ++i)\n\t{\n\t\tFEProperty* pi = pc->PropertyClass(i);\n\t\tbool a = pi->IsRequired();\n\t\tbool b = (pi->size() == 0);\n\t\tif (a && b)\n\t\t{\n\t\t\tthrow FEBioImport::MissingProperty(pc->GetName(), pi->GetName());\n\t\t}\n\t}\n\n}\n\n//-----------------------------------------------------------------------------\nFEFileSectionMap::~FEFileSectionMap()\n{\n\tClear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSectionMap::Clear()\n{\n\t// clear the map\n\tFEFileSectionMap::iterator is;\n\tfor (is = begin(); is != end(); ++is)\n\t{\n\t\tFEFileSection* ps = is->second; delete ps;\n\t}\n\tclear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileSectionMap::Parse(XMLTag& tag)\n{\n\t++tag;\n\twhile (!tag.isend())\n\t{\n\t\tstd::map<string, FEFileSection*>::iterator is = find(tag.Name());\n\t\tif (is != end()) is->second->Parse(tag);\n\t\telse throw XMLReader::InvalidTag(tag);\n\n\t\t++tag;\n\t};\n}\n\n//-----------------------------------------------------------------------------\n//! class constructor\nFEFileImport::FEFileImport()\n{\n\tm_fp = 0;\n\tm_szerr[0] = 0;\n\tm_builder = 0;\n\tm_nversion = 0;\n\n\tm_stopOnUnknownAttribute = false;\n}\n\n//-----------------------------------------------------------------------------\n// class destructor. Closes file on call.\nFEFileImport::~FEFileImport()\n{\n\t// make sure to close the file\n\tClose();\n}\n\n//-----------------------------------------------------------------------------\n//! Open a file and store the file name and file pointer.\nbool FEFileImport::Open(const char* szfile, const char* szmode)\n{\n\tm_fp = fopen(szfile, szmode);\n\tif (m_fp == 0) return false;\n\n\tstrcpy(m_szfile, szfile);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Close the file\nvoid FEFileImport::Close()\n{\n\tif (m_fp)\n\t{\n\t\tfclose(m_fp);\n\t\tm_fp = 0;\n\t}\n\n\tdelete m_builder;\n\tm_builder = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! parse the file\nbool FEFileImport::ParseFile(XMLTag& tag)\n{\n\t// parse the file\n\ttry\n\t{\n\t\tm_map.Parse(tag);\n\t}\n\tcatch (XMLReader::Error& e)\n\t{\n\t\tfprintf(stderr, \"FATAL ERROR: %s (line %d)\\n\", e.what(), tag.m_ncurrent_line);\n\t\treturn false;\n\t}\n\tcatch (FEBioImport::MissingProperty e)\n\t{\n\t\tfprintf(stderr, \"FATAL ERROR: %s\\n\\n\", e.GetErrorString());\n\t\treturn false;\n\t}\n\tcatch (...)\n\t{\n\t\tfprintf(stderr, \"FATAL ERROR: unknown exception occured while parsing file.\\n\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEModel* FEFileImport::GetFEModel()\n{\n\treturn &m_builder->GetFEModel();\n}\n\n//-----------------------------------------------------------------------------\n//! set a custom model builder \nvoid FEFileImport::SetModelBuilder(FEModelBuilder* modelBuilder)\n{\n\tdelete m_builder;\n\tm_builder = modelBuilder;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the model builder\nFEModelBuilder* FEFileImport::GetBuilder()\n{\n\treturn m_builder;\n}\n\n//-----------------------------------------------------------------------------\n// return the file path\nconst char* FEFileImport::GetFilePath() { return m_szpath; }\n\n//-----------------------------------------------------------------------------\n// set file version\nvoid FEFileImport::SetFileVerion(int nversion)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tfecore.SetSpecID(nversion);\n\tm_nversion = nversion;\n}\n\n//-----------------------------------------------------------------------------\n// get file version\nint FEFileImport::GetFileVersion() const\n{\n\treturn m_nversion;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFileImport::SetStopOnUnknownAttribute(bool b)\n{\n\tm_stopOnUnknownAttribute = b;\n}\n\n//-----------------------------------------------------------------------------\n// throw exception if an unknown attribute is found\nbool FEFileImport::StopOnUnknownAttribute() const\n{\n\treturn m_stopOnUnknownAttribute;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the error message. Errors message are stored when calling the errf function.\nvoid FEFileImport::GetErrorMessage(char* szerr)\n{\n\tstrcpy(szerr, m_szerr);\n}\n\n//-----------------------------------------------------------------------------\n//! Call this function to report an error message. The user can retrieve the \n//! error message with the GetErrorMessage member function.\nbool FEFileImport::errf(const char* szerr, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// copy to string\n\tva_start(args, szerr);\n\tvsnprintf(m_szerr, sizeof(m_szerr), szerr, args);\n\tva_end(args);\n\n\t// close the file\n\tClose();\n\n\treturn false;\n}\n"
  },
  {
    "path": "FEBioXML/FileImport.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <stdio.h>\n#include <FEBioXML/XMLReader.h>\n#include <FECore/vec3d.h>\n#include <FECore/mat3d.h>\n#include <FECore/tens3d.h>\n#include <FECore/FECoreBase.h>\n#include \"FEModelBuilder.h\"\n#include \"febioxml_api.h\"\n#include <map>\n\n//-----------------------------------------------------------------------------\n// Forward declarations\nclass FEModel;\nclass FEFileImport;\n\n//-----------------------------------------------------------------------------\n// Base class for FEBio import exceptions\n// Derived classes should set the error string in their constructor\nclass FEBIOXML_API FEFileException\n{\npublic:\n\tenum { MAX_ERR_STRING = 1024 };\n\n\tFEFileException();\n\n\tFEFileException(const char* sz, ...);\n\npublic:\n\t// retrieve the error string\n\tconst char* GetErrorString() const { return m_szerr; }\n\nprotected:\n\t// set the error string (used by derived classes)\n\tvoid SetErrorString(const char* sz, ...);\n\nprotected:\n\tchar\tm_szerr[MAX_ERR_STRING];\n};\n\n//-----------------------------------------------------------------------------\n// Class for handling unrecognized tags. \n// Use FEFileSection::SetInvalidTagHandler to set the handler for unrecognized parameters. \nclass FEInvalidTagHandler\n{\npublic:\n\tFEInvalidTagHandler() {}\n\tvirtual ~FEInvalidTagHandler() {}\n\n\tvirtual bool ProcessTag(XMLTag& tag) { return false; }\n};\n\n//-----------------------------------------------------------------------------\n// The FEObsoleteParamHandler class tries to map an unrecognized tag to \n// a model parameter. Obsolete parameters are added with the AddParam function. \nclass FEObsoleteParamHandler : public FEInvalidTagHandler\n{\n\tstruct FEObsoleteParam\n\t{\n\t\tconst char* oldName = nullptr;\n\t\tconst char* newName = nullptr;\n\t\tint paramType = FE_PARAM_INVALID;\n\t\tbool readIn = false;\n\t\tunion {\n\t\t\tbool\t\tbVal;\n\t\t\tint\t\t\tiVal;\n\t\t\tdouble\t\tgVal;\n\t\t};\n\t};\n\npublic:\n\tFEObsoleteParamHandler(XMLTag& tag, FECoreBase* pc);\n\n\t// Add an obsolete parameter.\n\t// The oldname is a relative path w.r.t. the XMLTag passed in the constructor.\n\t// the newName is a ParamString, relative to the pc parameter. \n\t// To mark a parameter as ignored, set newName to nullptr, and paramType to FE_PARAM_INVALID.\n\tvoid AddParam(const char* oldName, const char* newName, FEParamType paramType);\n\n\t// This will try to find an entry in the m_param list. If a match is not find, this function returns false.\n\tbool ProcessTag(XMLTag& tag) override;\n\n\t// This function will try to map the obsolete parameters to model parameters. \n\t// Or it will print a warning if the obsolete parameter will be ignored. \n\tvirtual void MapParameters();\n\nprivate:\n\tstd::string\tm_root;\n\tFECoreBase* m_pc;\n\tvector<FEObsoleteParam>\tm_param;\n};\n\n//-----------------------------------------------------------------------------\n// Base class for XML sections parsers\nclass FEBIOXML_API FEFileSection\n{\npublic:\n\tFEFileSection(FEFileImport* pim) { m_pim = pim; }\n    virtual ~FEFileSection() {}\n\n\tvirtual void Parse(XMLTag& tag) = 0;\n\n\tFEFileImport* GetFileReader() { return m_pim; }\n\n\tFEModel* GetFEModel();\n\n\tFEModelBuilder* GetBuilder();\n\n\t// Set the handler for unrecognized tags\n\tvoid SetInvalidTagHandler(FEInvalidTagHandler* ith);\n\npublic:\n\t//! read a nodal ID\n\t//! This assumes the node ID is defined via the \"id\" attribute\n\tint ReadNodeID(XMLTag& tag);\n\npublic:\n\tbool ReadParameter(XMLTag& tag, FEParameterList& pl, const char* szparam = 0, FECoreBase* pc = 0, bool parseAttributes = true);\n\tbool ReadParameter(XMLTag& tag, FECoreBase* pc, const char* szparam = 0, bool parseAttributes = true);\n\tvoid ReadParameterList(XMLTag& tag, FEParameterList& pl);\n\tvoid ReadParameterList(XMLTag& tag, FECoreBase* pc);\n\tvoid ReadAttributes(XMLTag& tag, FECoreBase* pc);\n\npublic:\n\tvoid value(XMLTag& tag, int&    n);\n\tvoid value(XMLTag& tag, double& g);\n\tvoid value(XMLTag& tag, bool&   b);\n\tvoid value(XMLTag& tag, vec2d&  v);\n\tvoid value(XMLTag& tag, vec3d&  v);\n\tvoid value(XMLTag& tag, mat3d&  m);\n\tvoid value(XMLTag& tag, mat3ds& m);\n\tvoid value(XMLTag& tag, tens3drs& m);\n\tvoid value(XMLTag& tag, char* szstr);\n\tint value(XMLTag& tag, int* pi, int n);\n\tint value(XMLTag& tag, double* pf, int n);\n\tvoid value(XMLTag& tag, std::string& v);\n\tvoid value(XMLTag& tag, std::vector<int>& v);\n\tvoid value(XMLTag& tag, std::vector<double>& v);\n\nprotected:\n\tbool parseEnumParam(FEParam* pp, const char* val);\n\nprivate:\n\tFEFileImport*\tm_pim;\n\tFEInvalidTagHandler* m_ith = nullptr;\n};\n\n//-----------------------------------------------------------------------------\n// class that manages file section parsers\nclass FEBIOXML_API FEFileSectionMap : public std::map<string, FEFileSection*>\n{\npublic:\n\t~FEFileSectionMap();\n\tvoid Clear();\n\n\tvoid Parse(XMLTag& tag);\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for file import classes. \n//! FEBio import files are XML formatted files, where each major section (children of root) is represented\n//! by an FEFileSection. \n//! This class also offers a simple error reporting mechanism and manages the FILE* pointer. \n//! This class also manages \"xml parameters\". This is a feature of FEBio files that allow users to use parameters\n//! as values for xml tag. A parameter is defined by a name-value pair and referenced in the input file using the $(parameter_name) syntax.\n\nclass FEBIOXML_API FEFileImport\n{\npublic:\n\t//! constructor\n\tFEFileImport();\n\n\t//! destructor\n\tvirtual ~FEFileImport();\n\n\t//! get the error message\n\tvoid GetErrorMessage(char* szerr);\n\n\t//! Get the current FE model that is being processed\n\tFEModel* GetFEModel();\n\n\t//! set a custom model builder (takes ownership of modelBuilder)\n\tvoid SetModelBuilder(FEModelBuilder* modelBuilder);\n\n\t//! Get the model builder\n\tFEModelBuilder* GetBuilder();\n\n\t// return the file path\n\tconst char* GetFilePath();\n\n\t// set file version\n\tvoid SetFileVerion(int nversion);\n\n\t// get file version\n\tint GetFileVersion() const;\n\n\t// throw exception if an unknown attribute is found\n\tvoid SetStopOnUnknownAttribute(bool b);\n\tbool StopOnUnknownAttribute() const;\n\nprotected:\n\t//! open a file\n\tbool Open(const char* szfile, const char* szmode);\n\n\t//! close the file\n\tvoid Close();\n\n\t//! helper function for reporting errors\n\tbool errf(const char* szerr, ...);\n\n\t//! parse the file\n\tbool ParseFile(XMLTag& tag);\n\nprotected:\n\tFILE*\tm_fp;\t\t\t//!< file pointer\n\tchar\tm_szfile[256];\t//!< file name\n\tchar\tm_szerr[256];\t//!< error message\n\tchar\tm_szpath[512];\t//!< file path\n\nprotected:\n\tFEFileSectionMap\tm_map;\n\tFEModelBuilder*\t\tm_builder;\n\tbool\t\t\t\tm_stopOnUnknownAttribute;\n\nprivate:\n\tint\tm_nversion;\t// version of file\n};\n"
  },
  {
    "path": "FEBioXML/XMLReader.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"XMLReader.h\"\n#include <assert.h>\n#include <stdarg.h>\n#include <fstream>\n#include <sstream>\nusing namespace std;\n\n//=============================================================================\n// XMLAtt\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! The constructor sets the name and value to zero strings\nXMLAtt::XMLAtt()\n{\n\tm_bvisited = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Returns true if the attribute's value is the same as the string that is passed.\n//! Note that the comparison is case-sensitive \nbool XMLAtt::operator == (const char* sz)\n{\n\treturn (strcmp(m_val.c_str(), sz) == 0);\n}\n\nbool XMLAtt::operator != (const char* szval) \n{ \n\treturn (strcmp(szval, m_val.c_str()) != 0); \n}\n\nint XMLAtt::value(int* v, int n)\n{\n\tconst char* sz = m_val.c_str();\n\tint nr = 0;\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\t\tv[i] = atoi(sz);\n\t\tnr++;\n\t\tif (sze) sz = sze + 1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\nint XMLAtt::value(double* pf, int n)\n{\n\tconst char* sz = m_val.c_str();\n\tint nr = 0;\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tpf[i] = atof(sz);\n\t\tnr++;\n\n\t\tif (sze) sz = sze + 1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\n//=============================================================================\n// XMLTag\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nXMLTag::XMLTag()\n{\n\tm_preader = 0;\n\tm_bend = false;\n\tm_bleaf = true;\n\tm_bempty = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Clear tag data\nvoid XMLTag::clear()\n{\n\tm_sztag.clear();\n\tm_szval.clear();\n\tm_att.clear();\n//\tm_path.clear(); // NOTE: Do not clear the path!\n\tm_bend = false;\n\tm_bleaf = true;\n\tm_bempty = false;\n}\n\n//-----------------------------------------------------------------------------\nstd::string XMLTag::relpath(const char* szroot) const\n{\n\tstd::string s;\n\tint m = -1;\n\tif (szroot)\n\t{\n\t\tfor (int i = 0; i < m_path.size(); ++i)\n\t\t{\n\t\t\tif (strcmp(szroot, m_path[i].c_str()) == 0)\n\t\t\t{\n\t\t\t\tm = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (m != -1)\n\t{\n\t\tfor (int i = m+1; i < m_path.size(); i++)\n\t\t{\n\t\t\tif (i != m+1) s += \".\";\n\t\t\ts += m_path[i];\n\t\t}\n\t}\n\n\tif (s.empty() == false) s += \".\";\n\ts += m_sztag;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads in a comma delimited list of doubles. The function reads\n//! in a maximum of n values. The actual number of values that are read is returned.\n//!\nint XMLTag::value(double* pf, int n)\n{\n\tconst char* sz = m_szval.c_str();\n\tint nr = 0;\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tpf[i] = atof(sz);\n\t\tnr++;\n\n\t\tif (sze) sz = sze+1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads in a comma delimited list of floats. The function reads\n//! in a maximum of n values. The actual number of values that are read is returned.\n//!\nint XMLTag::value(float* pf, int n)\n{\n\tconst char* sz = m_szval.c_str();\n\tint nr = 0;\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tpf[i] = (float) atof(sz);\n\t\tnr++;\n\n\t\tif (sze) sz = sze+1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\n//! This function reads in a comma delimited list of ints. The function reads\n//! in a maximum of n values. The actual number of values that are read is returned.\n//!\nint XMLTag::value(int* pi, int n)\n{\n\tconst char* sz = m_szval.c_str();\n\tint nr = 0;\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\n\t\tpi[i] = atoi(sz);\n\t\tnr++;\n\n\t\tif (sze) sz = sze+1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\nint XMLTag::value(std::vector<string>& stringList, int n)\n{\n\tstringList.clear();\n\n\tchar tmp[256] = { 0 };\n\n\tconst char* sz = m_szval.c_str();\n\tint nr = 0;\n\tfor (int i = 0; i<n; ++i)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\t\tif (sze)\n\t\t{\n\t\t\tint l = (int)(sze - sz);\n\t\t\tif (l > 0) strncpy(tmp, sz, l);\n\t\t\ttmp[l] = 0;\n\n\t\t\tstringList.push_back(tmp);\n\t\t}\n\t\telse stringList.push_back(sz);\n\t\tnr++;\n\n\t\tif (sze) sz = sze + 1;\n\t\telse break;\n\t}\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLTag::value(std::vector<string>& stringList)\n{\n\tstringList.clear();\n\n\tchar tmp[256] = { 0 };\n\n\tconst char* sz = m_szval.c_str();\n\twhile (sz && *sz)\n\t{\n\t\tconst char* sze = strchr(sz, ',');\n\t\tif (sze)\n\t\t{\n\t\t\tint l = (int)(sze - sz);\n\t\t\tif (l > 0) strncpy(tmp, sz, l);\n\t\t\ttmp[l] = 0;\n\n\t\t\tstringList.push_back(tmp);\n\t\t}\n\t\telse stringList.push_back(sz);\n\n\t\tif (sze) sz = sze + 1;\n\t\telse break;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLTag::value(bool& val)\n{ \n\tint n=0; \n\tsscanf(m_szval.c_str(), \"%d\", &n); \n\tval = (n != 0); \n}\n\n//-----------------------------------------------------------------------------\nvoid XMLTag::value(char* szstr)\n{\n\tstrcpy(szstr, m_szval.c_str()); \n}\n\n//-----------------------------------------------------------------------------\nvoid XMLTag::value(std::string& val)\n{\n\tval = m_szval;\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLTag::value(vector<int>& l)\n{\n\tint i, n = 0, n0, n1, nn;\n\tchar* szval = strdup(m_szval.c_str());\n\tchar* ch;\n\tchar* sz = szval;\n\tint nread;\n\tdo\n\t{\n\t\tch = strchr(sz, ',');\n\t\tif (ch) *ch = 0;\n\t\tnread = sscanf(sz, \"%d:%d:%d\", &n0, &n1, &nn);\n\t\tswitch (nread)\n\t\t{\n\t\tcase 1:\n\t\t\tn1 = n0;\n\t\t\tnn = 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tnn = 1;\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tn0 = 0;\n\t\t\tn1 = -1;\n\t\t\tnn = 1;\n\t\t}\n\n\t\tfor (i=n0; i<=n1; i += nn) ++n;\n\n\t\tif (ch) *ch = ',';\n\t\tsz = ch+1;\n\t}\n\twhile (ch != 0);\n\n\tif (n != 0)\n\t{\n\t\tl.resize(n);\n\n\t\tsz = szval;\n\t\tn = 0;\n\t\tdo\n\t\t{\n\t\t\tch = strchr(sz, ',');\n\t\t\tif (ch) *ch = 0;\n\t\t\tnread = sscanf(sz, \"%d:%d:%d\", &n0, &n1, &nn);\n\t\t\tswitch (nread)\n\t\t\t{\n\t\t\tcase 1:\n\t\t\t\tn1 = n0;\n\t\t\t\tnn = 1;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tnn = 1;\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tn0 = 0;\n\t\t\t\tn1 = -1;\n\t\t\t\tnn = 1;\n\t\t\t}\n\n\t\t\tfor (i=n0; i<=n1; i += nn) l[n++] = i;\n\t\t\tassert(n <= (int) l.size());\n\n\t\t\tif (ch) *ch = ',';\n\t\t\tsz = ch+1;\n\t\t}\n\t\twhile (ch != 0);\n\t}\n\n\tfree(szval);\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLTag::value(vector<double>& l)\n{\n\tl.clear();\n\tconst char *sz = m_szval.c_str();\n\twhile (sz && *sz)\n\t{\n\t\t// skip space\n\t\twhile (isspace(*sz)) ++sz;\n\n\t\t// read the value\n\t\tif (sz && *sz)\n\t\t{\n\t\t\tdouble v = atof(sz);\n\t\t\tl.push_back(v);\n\n\t\t\t// find next space or comma\n\t\t\twhile (*sz && (*sz != ' ') && (*sz != ',')) sz++;\n\t\t\tif (*sz == ',') sz++;\n\t\t}\n\t}\n}\n\nvoid XMLTag::value2(std::vector<int>& l)\n{\n\tl.clear();\n\tconst char* sz = m_szval.c_str();\n\twhile (sz && *sz)\n\t{\n\t\t// skip space\n\t\twhile (isspace(*sz)) ++sz;\n\n\t\t// read the value\n\t\tif (sz && *sz)\n\t\t{\n\t\t\tint v = (int)atoi(sz);\n\t\t\tl.push_back(v);\n\n\t\t\t// find next space or comma\n\t\t\twhile (*sz && (*sz != ' ') && (*sz != ',')) sz++;\n\t\t\tif (*sz == ',') sz++;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Return the number of children of a tag\nint XMLTag::children()\n{\n\tXMLTag tag(*this); ++tag;\n\tint ncount = 0;\n\twhile (!tag.isend()) { ncount++; tag.skip(); ++tag; }\n\treturn ncount;\n}\n\n//-----------------------------------------------------------------------------\n//! return the string value of an attribute\nconst char* XMLTag::AttributeValue(const char* szat, bool bopt)\n{\n\t// find the attribute\n\tfor (XMLAtt& att : m_att)\n\t{\n\t\tif (strcmp(att.m_name.c_str(), szat) == 0)\n\t\t{\n\t\t\tatt.m_bvisited = true;\n\t\t\treturn att.m_val.c_str();\n\t\t}\n\t}\n\n\t// If the attribute was not optional, we throw a fit\n\tif (!bopt) throw XMLReader::MissingAttribute(*this, szat);\n\n\t// we didn't find it\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nXMLAtt* XMLTag::AttributePtr(const char* szat)\n{\n\treturn Attribute(szat, true);\n}\n\n//-----------------------------------------------------------------------------\n//! return the attribute\nXMLAtt* XMLTag::Attribute(const char* szat, bool bopt)\n{\n\t// find the attribute\n\tfor (XMLAtt& att : m_att)\n\t{\n\t\tif (strcmp(att.m_name.c_str(), szat) == 0)\n\t\t{\n\t\t\tatt.m_bvisited = true;\n\t\t\treturn &(att);\n\t\t}\n\t}\n\n\t// If the attribute was not optional, we throw a fit\n\tif (!bopt) throw XMLReader::MissingAttribute(*this, szat);\n\n\t// we didn't find it\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! return the attribute\nXMLAtt& XMLTag::Attribute(const char* szat)\n{\n\t// find the attribute\n\tfor (XMLAtt& att : m_att)\n\t{\n\t\tif (strcmp(att.m_name.c_str(), szat) == 0)\n\t\t{\n\t\t\tatt.m_bvisited = true;\n\t\t\treturn att;\n\t\t}\n\t}\n\n\t// throw a fit\n\tthrow XMLReader::MissingAttribute(*this, szat);\n}\n\n//-----------------------------------------------------------------------------\nbool XMLTag::AttributeValue(const char* szat, double& d, bool bopt)\n{\n\tconst char* szv = AttributeValue(szat, bopt);\n\tif (szv == 0) return false;\n\n\td = atof(szv);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool XMLTag::AttributeValue(const char* szat, int& n, bool bopt)\n{\n\tconst char* szv = AttributeValue(szat, bopt);\n\tif (szv == 0) return false;\n\n\tn = atoi(szv);\n\n\treturn true;\n}\n\n//=============================================================================\n// XMLReader - Exceptions\n//=============================================================================\n\n// helper function for formatting a string\nstring format_string(const char* sz, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar szbuf[512];\n\tva_start(args, sz);\n\tvsnprintf(szbuf, sizeof(szbuf), sz, args);\n\tva_end(args);\n\n\treturn szbuf;\n}\n\n//-----------------------------------------------------------------------------\nXMLReader::Error::Error(XMLTag& tag, const std::string& err) : \\\nstd::runtime_error(format_string(\"tag \\\"%s\\\" (line %d) : \", tag.Name(), tag.m_nstart_line) + err) {}\n\n//-----------------------------------------------------------------------------\nXMLReader::XMLSyntaxError::XMLSyntaxError(int line_number) : \\\nXMLReader::Error(format_string(\"syntax error (line %d)\", line_number)) {}\n\n//-----------------------------------------------------------------------------\nXMLReader::UnmatchedEndTag::UnmatchedEndTag(XMLTag& tag) : \\\nXMLReader::Error(tag, \"unmatched end tag\") {}\n\n//-----------------------------------------------------------------------------\nXMLReader::InvalidTag::InvalidTag(XMLTag& tag) : \\\nXMLReader::Error(tag, \"unrecognized tag\") {}\n\n//-----------------------------------------------------------------------------\nXMLReader::InvalidValue::InvalidValue(XMLTag& tag) : \\\nXMLReader::Error(tag, format_string(\"invalid value: %s\", tag.isleaf() ? tag.szvalue() : \"\")) {}\n\n//-----------------------------------------------------------------------------\nXMLReader::InvalidAttributeValue::InvalidAttributeValue(XMLTag& tag, const char* sza, const char* szv) : \\\nXMLReader::Error(tag, format_string(\"invalid value for attribute \\\"%s\\\"\", sza)) {}\n\nXMLReader::InvalidAttributeValue::InvalidAttributeValue(XMLTag& tag, XMLAtt& att) : \\\nXMLReader::Error(tag, format_string(\"invalid value for attribute \\\"%s\\\"\", att.cvalue())) {}\n\n//-----------------------------------------------------------------------------\nXMLReader::InvalidAttribute::InvalidAttribute(XMLTag& tag, const char* sza) :\\\nXMLReader::Error(tag, format_string(\"invalid attribute \\\"%s\\\"\", sza)) {}\n\n//-----------------------------------------------------------------------------\nXMLReader::MissingAttribute::MissingAttribute(XMLTag& tag, const char* sza) : \\\nXMLReader::Error(tag, format_string(\"missing attribute \\\"%s\\\"\", sza)) {}\n\n//-----------------------------------------------------------------------------\nXMLReader::MissingTag::MissingTag(XMLTag& tag, const char* sza) : \\\nXMLReader::Error(tag, format_string(\"missing tag \\\"%s\\\"\", sza)) {}\n\n//=============================================================================\n// XMLReader\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nXMLReader::XMLReader()\n{\n    m_stream = nullptr;\n\tm_nline = 0;\n\tm_bufIndex = 0;\n\tm_bufSize = 0;\n\tm_eof = false;\n\tm_currentPos = 0;\n\tm_buf = new char[BUF_SIZE];\n}\n\n//-----------------------------------------------------------------------------\nXMLReader::~XMLReader()\n{\n\tClose();\n\tdelete[] m_buf;\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLReader::Close()\n{\n    if(m_stream)\n    {\n        ifstream* fileStream = dynamic_cast<ifstream*>(m_stream);\n        if(fileStream)\n        {\n            fileStream->close();\n        }\n\n        delete m_stream;\n        m_stream = nullptr;\n    }\n\n\tm_nline = 0;\n\tm_bufIndex = 0;\n\tm_bufSize = 0;\n\tm_eof = false;\n\tm_currentPos = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool XMLReader::Open(const char* szfile, bool checkForXMLTag)\n{\n\t// make sure this reader has not been attached to a file yet\n    if(m_stream) return false;\n\n\t// open the file\n    m_stream = new ifstream;\n    static_cast<ifstream*>(m_stream)->open(szfile, ifstream::in|ifstream::binary);\n    if(m_stream->fail()) return false;\n\n\n\t// read the first line\n\tif (checkForXMLTag)\n\t{\n\t\tchar szline[256] = { 0 };\n\t\t// fgets(szline, 255, m_fp);\n        m_stream->get(szline, 255);\n\n\t\t// make sure it is correct\n\t\tif (strncmp(szline, \"<?xml\", 5) != 0)\n\t\t{\n\t\t\t// This file is not an XML file\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tm_currentPos = 0;\n\n\t// This file is ready to be processed\n\treturn true;\n}\n\nbool XMLReader::OpenString(std::string& xml, bool checkForXMLTag)\n{\n    // make sure this we don't already have a stream\n    if(m_stream) return false;\n\n    // create string stream\n    m_stream = new std::istringstream(xml);\n    if(m_stream->fail()) return false;\n\n    // read the first line\n\tif (checkForXMLTag)\n\t{\n\t\tchar szline[256] = { 0 };\n\t\t// fgets(szline, 255, m_fp);\n        m_stream->get(szline, 255);\n\n\t\t// make sure it is correct\n\t\tif (strncmp(szline, \"<?xml\", 5) != 0)\n\t\t{\n\t\t\t// This file is not an XML file\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tm_currentPos = 0;\n\n\t// This file is ready to be processed\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n\nclass XMLPath\n{\n\tstruct TAG\n\t{\n\t\tchar*\ttag;\n\t\tchar*\tatt;\n\t\tchar*\tatv;\n\t};\n\npublic:\n\tXMLPath(const char* xpath)\n\t{\n\t\tint l = (int)strlen(xpath);\n\t\tm_path = new char[l+1];\n\t\tstrncpy(m_path, xpath, l);\n\t\tm_path[l] = 0;\n\t\tm_index = 0;\n\t\tparse();\n\t}\n\n\t~XMLPath()\n\t{\n\t\tdelete [] m_path;\n\t}\n\n\tvoid next() { m_index++; }\n\n\tbool match(XMLTag& tag)\n\t{\n\t\t// make sure index is valid\n\t\tif ((m_index < 0) || (m_index >= (int) m_tag.size())) return false;\n\n\t\t// get current tag\n\t\tTAG& t = m_tag[m_index];\n\n\t\t// do tag name compare?\n\t\tif (strcmp(t.tag, tag.Name()) != 0) return false;\n\n\t\t// if tag requires attribute, make sure it exist\n\t\tif (t.att)\n\t\t{\n\t\t\tconst char* szatt = tag.AttributeValue(t.att, true);\n\t\t\tif (szatt)\n\t\t\t{\n\t\t\t\t// if the value is specified, make sure it matches too\n\t\t\t\tif (t.atv)\n\t\t\t\t{\n\t\t\t\t\tif (strcmp(t.atv, szatt) == 0) return true;\n\t\t\t\t\telse return false;\n\t\t\t\t}\n\t\t\t\telse return false;\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\t\telse return true;\n\t}\n\n\tbool valid() { return ((m_index >= 0) && (m_index < m_tag.size())); }\n\nprivate:\n\tvoid parse()\n\t{\n\t\tchar* sz = m_path;\n\t\tchar* ch = strchr(m_path, '/');\n\t\tdo\n\t\t{\n\t\t\tTAG tag;\n\t\t\ttag.tag = sz;\n\t\t\ttag.att = 0;\n\t\t\ttag.atv = 0;\n\n\t\t\tif (ch) *ch = 0;\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\tchar* cl = strchr(sz, '[');\n\t\t\t\tif (cl)\n\t\t\t\t{\n\t\t\t\t\tchar* cr = strchr(cl+1, ']');\n\t\t\t\t\tif (cr) \n\t\t\t\t\t{\n\t\t\t\t\t\t*cl++ = 0;\n\t\t\t\t\t\tif (cl[0]=='@') cl++;\n\t\t\t\t\t\ttag.att = cl;\n\n\t\t\t\t\t\t*cr = 0;\n\t\t\t\t\t\tcr = strchr(cl, '=');\n\t\t\t\t\t\tif (cr)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*cr++ = 0;\n\t\t\t\t\t\t\ttag.atv = cr;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsz = (ch ? ch + 1 : 0);\n\t\t\tif (ch) ch = strchr(sz, '/');\n\n\t\t\tm_tag.push_back(tag);\n\t\t}\n\t\twhile (sz);\n\t}\n\nprivate:\n\tchar*\tm_path;\n\tvector<TAG>\tm_tag;\n\tint\t\t\tm_index;\n};\n\nbool XMLReader::FindTag(const char* xpath, XMLTag& tag)\n{\n\t// go to the beginning of the file\n    m_stream->seekg(0, ios_base::beg);\n\tm_bufIndex = m_bufSize = 0;\n\tm_currentPos = 0;\n\tm_eof = false;\n\n\t// set the first tag\n\ttag.m_preader = this;\n\ttag.m_ncurrent_line = 1;\n\ttag.m_fpos = currentPos();\n\n\tXMLPath path(xpath);\n\n\t// find the correct tag\n\tbool bfound = false;\n\ttry\n\t{\n\t\t// get the next tag\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\t// check for match\n\t\t\tif (path.match(tag))\n\t\t\t{\n\t\t\t\tpath.next();\n\t\t\t\tif (path.valid()) ++tag;\n\t\t\t\tbfound = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttag.skip();\n\t\t\t\t++tag;\n\t\t\t\tbfound = false;\n\t\t\t}\n\t\t}\n\t\twhile (path.valid());\n\t}\n\tcatch (...)\n\t{\n\t\t// an error has occured (or the end-of-file was reached)\n\t\treturn false;\n\t}\n\n\treturn bfound;\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLReader::NextTag(XMLTag& tag)\n{\n\tassert( tag.m_preader == this);\n\n\t// set current line number\n\tm_nline = tag.m_ncurrent_line;\n\n\t// set the current file position\n\tif (m_currentPos != tag.m_fpos)\n\t{\n        m_stream->seekg(tag.m_fpos, ios_base::beg);\n\t\tm_currentPos = tag.m_fpos;\n\t\tm_bufSize = m_bufIndex = 0;\n\t\tm_eof = false;\n\t}\n\n\t// update the path\n\tif (!tag.isend() && !tag.isempty() && !tag.isleaf())\n\t{\n\t\t// keep a copy of the name\n\t\ttag.m_path.push_back(tag.m_sztag);\n\t}\n\telse if (tag.isend())\n\t{\n\t\t// make sure the name is the same as the root\n\t\tif (strcmp(tag.m_sztag.c_str(), tag.m_path.back().c_str()) != 0) throw UnmatchedEndTag(tag);\n\n\t\ttag.m_path.erase(--tag.m_path.end());\n\t}\n\n\t// clear tag's content\n\ttag.clear();\n\n\t// read the start tag\n\tReadTag(tag);\n\n\t// read value and end tag if tag is not empty\n\tif (!tag.isempty() && !tag.isend())\n\t{\n\t\t// read the value\n\t\tReadValue(tag);\n\n\t\t// read the end tag\n\t\tReadEndTag(tag);\n\t}\n\n\t// store current line number\n\ttag.m_ncurrent_line = m_nline;\n\n\t// store start file pos for next element\n\ttag.m_fpos = currentPos();\n}\n\n//-----------------------------------------------------------------------------\ninline bool isvalid(char c)\n{\n\treturn (isalnum(c) || (c=='_') || (c=='.') || (c=='-') || (c==':'));\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLReader::ReadTag(XMLTag& tag)\n{\n\t// find the start token\n\tchar ch;\n\twhile (true)\n\t{\n\t\twhile ((ch=GetChar())!='<') \n\t\t\tif (!isspace(ch)) \n\t\t\t{\n\t\t\t\tthrow XMLSyntaxError(m_nline);\n\t\t\t}\n\n\t\tch = GetChar();\n\t\tif (ch == '?')\n\t\t{\n\t\t\t// parse the xml header tag\n\t\t\twhile ((ch = GetChar()) != '?');\n\t\t\tch = GetChar();\n\t\t\tif (ch != '>') throw XMLSyntaxError(m_nline);\n\t\t}\n\t\telse break;\n\t}\n\n\t// record the startline\n\ttag.m_nstart_line = m_nline;\n\n\tif (ch == '/')\n\t{\n\t\ttag.m_bend = true;\n        m_comment.clear();\n\t\tch = GetChar();\n\t}\n\n\t// skip whitespace\n\twhile (isspace(ch)) ch = GetChar();\n\n\t// read the tag name\n\tif (!isvalid(ch)) throw XMLSyntaxError(m_nline);\n\ttag.m_sztag.clear();\n\ttag.m_sztag.push_back(ch);\n\twhile (isvalid(ch=GetChar())) tag.m_sztag.push_back(ch);\n\n\t// read attributes\n\ttag.m_att.clear();\n\tint n = 0;\n\twhile (true)\n\t{\n\t\t// skip whitespace\n\t\twhile (isspace(ch)) ch = GetChar();\n\t\tif (ch == '/')\n\t\t{\n\t\t\ttag.m_bempty = true;\n\t\t\tch = GetChar();\n\t\t\tif (ch != '>') throw XMLSyntaxError(m_nline);\n\t\t\tbreak;\n\t\t}\n\t\telse if (ch == '>') break;\n\n\t\t// read the attribute's name\n\t\tXMLAtt att;\n\t\tstd::string& name = att.m_name;\n\t\tname.clear();\n\t\tif (!isvalid(ch)) throw XMLSyntaxError(m_nline);\n\t\tname.push_back(ch);\n\t\twhile (isvalid(ch=GetChar())) name.push_back(ch);\n\n\t\t// skip whitespace\n\t\twhile (isspace(ch)) ch=GetChar();\n\t\tif (ch != '=') throw XMLSyntaxError(m_nline);\n\n\t\t// skip whitespace\n\t\twhile (isspace(ch=GetChar()));\n\n\t\t// make sure the attribute's value starts with an apostrophe or quotation mark\n\t\tif ((ch != '\"')&&(ch != '\\'')) throw XMLSyntaxError(m_nline);\n\t\tchar quot = ch;\n\n\t\t// read the value\n\t\tstd::string& val = att.m_val;\n\t\twhile ((ch=GetChar())!=quot) val.push_back(ch);\n\t\tch=GetChar();\n\n\t\t// mark tag as unvisited\n\t\tatt.m_bvisited = false;\n\n\t\t++n;\n\t\ttag.m_att.push_back(att);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLReader::ReadValue(XMLTag& tag)\n{\n\tchar ch;\n\tif (!tag.isend())\n\t{\n\t\ttag.m_szval.clear();\n\t\ttag.m_szval.reserve(256);\n\t\twhile (true) \n\t\t{\n\t\t\tch = GetChar();\n\n\t\t\t// look for CDATA section\n\t\t\tif (ch == '<')\n\t\t\t{\n\t\t\t\tchar ch2 = readNextChar();\n\t\t\t\tif (ch2 == '!')\n\t\t\t\t{\n\t\t\t\t\t// this is a CDATA section\n\t\t\t\t\tchar szbuf[10] = { 0 };\n\t\t\t\t\tszbuf[0] = '<';\n\t\t\t\t\tszbuf[1] = '!';\n\t\t\t\t\tint i = 2;\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\tch = readNextChar();\n\t\t\t\t\t\tszbuf[i++] = ch;\n\t\t\t\t\t} while (i < 9);\n\t\t\t\t\tif (strcmp(szbuf, \"<![CDATA[\") == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\twhile (true)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tch = readNextChar();\n\t\t\t\t\t\t\tif (ch == ']')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar ch3 = readNextChar();\n\t\t\t\t\t\t\t\tif (ch3 == ']')\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tchar ch4 = readNextChar();\n\t\t\t\t\t\t\t\t\tif (ch4 == '>')\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tch = '\\0';// set to null to avoid skipping over next char\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse tag.m_szval.push_back(ch);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse tag.m_szval.push_back(ch);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse tag.m_szval.push_back(ch);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse throw XMLSyntaxError(m_nline);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trewind(1);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// read entity references\n\t\t\tif (ch == '&')\n\t\t\t{\n\t\t\t\tchar szbuf[16] = { 0 };\n\t\t\t\tszbuf[0] = '&';\n\t\t\t\tint i = 1;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tch = readNextChar();\n\t\t\t\t\tszbuf[i++] = ch;\n\t\t\t\t} while ((i < 16) && (ch != ';'));\n\t\t\t\tif (ch != ';') throw XMLSyntaxError(m_nline);\n\n\t\t\t\tif (strcmp(szbuf, \"&lt;\") == 0) ch = '<';\n\t\t\t\telse if (strcmp(szbuf, \"&gt;\") == 0) ch = '>';\n\t\t\t\telse if (strcmp(szbuf, \"&amp;\") == 0) ch = '&';\n\t\t\t\telse if (strcmp(szbuf, \"&apos;\") == 0) ch = '\\'';\n\t\t\t\telse if (strcmp(szbuf, \"&quot;\") == 0) ch = '\"';\n\t\t\t\telse if ((strcmp(szbuf, \"&#9;\") == 0) || (strcmp(szbuf, \"&#x9;\") == 0)) ch = '\\t';\n\t\t\t\telse if ((strcmp(szbuf, \"&#10;\") == 0) || (strcmp(szbuf, \"&#xA;\") == 0)) ch = '\\n';\n\t\t\t\telse if ((strcmp(szbuf, \"&#13;\") == 0) || (strcmp(szbuf, \"&#xD;\") == 0)) ch = '\\r';\n\t\t\t\telse throw XMLSyntaxError(m_nline);\n\t\t\t}\n\n\t\t\tif (ch != '\\0') tag.m_szval.push_back(ch);\n\t\t}\n\t}\n\telse while ((ch=GetChar())!='<');\n}\n\n//-----------------------------------------------------------------------------\nvoid XMLReader::ReadEndTag(XMLTag& tag)\n{\n\tchar ch;\n\tconst char* sz = tag.m_sztag.c_str();\n\tif (!tag.isend())\n\t{\n\t\tch = GetChar();\n\t\tif (ch == '/')\n\t\t{\n\t\t\t// this is the end tag\n\n\t\t\t// skip whitespace\n\t\t\twhile (isspace(ch=GetChar()));\n\n\t\t\tint n = 0;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (ch != *sz++) throw UnmatchedEndTag(tag);\n\t\t\t\tch = GetChar();\n\t\t\t\t++n;\n\t\t\t}\n\t\t\twhile (!isspace(ch) && (ch!='>'));\n\t\t\tif (n != (int) tag.m_sztag.size()) throw UnmatchedEndTag(tag);\n\n\t\t\t// skip whitespace\n\t\t\twhile (isspace(ch)) ch=GetChar();\n\t\t\tif (ch != '>') throw XMLSyntaxError(m_nline);\n\n\t\t\t// find the start of the next tag\n\t\t\tif (tag.m_path.empty() == false)\n\t\t\t{\n\t\t\t\twhile (isspace(ch=GetChar()));\n\t\t\t\tif (ch != '<') throw XMLSyntaxError(m_nline);\n\t\t\t\trewind(1);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// this element has child elements\n\t\t\t// and therefor is not a leaf\n\n\t\t\ttag.m_bleaf = false;\n\t\t\trewind(2);\n\t\t}\n\t}\n\telse\n\t{\n\t\trewind(1);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nchar XMLReader::readNextChar()\n{\n\tif (m_bufIndex >= m_bufSize)\n\t{\n\t\tif (m_eof) \n        {\n            throw UnexpectedEOF();\n        }\n\n        m_stream->read(m_buf, BUF_SIZE);\n        \n        // clear eofbit and failbit if eof is hit. Unless we do this, seekg doesn't work\n        // we handle eof manually, so we can clear it without issue. \n        m_stream->clear();\n        \n        m_bufSize = m_stream->gcount();\n        m_bufIndex = 0;\n        m_eof = (m_bufSize != BUF_SIZE);\n\t}\n\tm_currentPos++;\n\tchar ch = m_buf[m_bufIndex++];\n\tif (ch == '\\n') m_nline++;\n\treturn ch;\n}\n\n//-----------------------------------------------------------------------------\nint64_t XMLReader::currentPos()\n{\n\treturn m_currentPos;\n}\n\n//-----------------------------------------------------------------------------\n//! move the file pointer\nvoid XMLReader::rewind(int64_t nstep)\n{\n\t// NOTE: What if we rewind past a newline? Won't that mess up the line index?\n\tm_bufIndex -= nstep;\n\tm_currentPos -= nstep;\n\n\tif (m_bufIndex < 0)\n\t{\n\t\tm_stream->seekg(m_bufIndex - m_bufSize, ios_base::cur);\n\t\tm_bufIndex = m_bufSize = 0;\n\t\tm_eof = false;\n\t}\n}\n\n// clean the string by removing whitespace at the front and back\nvoid clean_string(string& s)\n{\n\tif (s.empty()) return;\n\n\tsize_t n = s.size();\n\tchar* tmp = new char[n + 1];\n\tstrncpy(tmp, s.c_str(), n);\n\ttmp[n] = 0;\n\n\tchar* cl = tmp;\n\twhile (*cl && isspace(*cl)) cl++;\n\tn = strlen(cl);\n\tif (n > 0)\n\t{\n\t\tchar* cr = &cl[n - 1];\n\t\twhile ((cr > cl) && isspace(*cr)) *cr-- = 0;\n\t}\n\n\ts = cl;\n\tdelete[] tmp;\n}\n\n//-----------------------------------------------------------------------------\n//! Read the next character in the file (skipping over comments).\nchar XMLReader::GetChar()\n{\n\tchar ch = readNextChar();\n\twhile (ch == '\\r') ch = readNextChar();\n\tif ((ch == '\\t') || (ch == '\\n')) ch = ' ';\n\n\t// check for comments\n\tif (ch == '<')\n\t{\n\t\tchar tmp = readNextChar();\n\t\tif (tmp == '!')\n\t\t{\n\t\t\tif (readNextChar() == '[')\n\t\t\t{\n\t\t\t\t// this is a CDATA section, not a comment\n\t\t\t\trewind(2);\n\t\t\t\treturn ch;\n\t\t\t}\n\t\t\telse rewind(1);\n\n\t\t\tm_comment.clear();\n\t\t\t// parse the comment\n\t\t\tch = readNextChar(); if (ch != '-') throw XMLSyntaxError(m_nline);\n\t\t\tch = readNextChar(); if (ch != '-') throw XMLSyntaxError(m_nline);\n\n\t\t\t// find the end of the comment\n\t\t\tint n = 0;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tch = readNextChar();\n\t\t\t\tif (ch == '-') n++;\n\t\t\t\telse if ((ch == '>') && (n >= 2)) break;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (n > 0) m_comment += '-';\n\t\t\t\t\tif (n > 1) m_comment += '-';\n\t\t\t\t\tif (ch != '\\r') m_comment += ch; // don't append \\r\n\t\t\t\t\tn = 0;\n\t\t\t\t}\n\t\t\t} while (1);\n\n\t\t\t// eat whitespace at the start and end of the comment\n\t\t\tclean_string(m_comment);\n\n\t\t\tch = readNextChar();\n\t\t}\n\t\telse\n\t\t{\n\t\t\trewind(1);\n\t\t}\n\t}\n\treturn ch;\n}\n\n//-----------------------------------------------------------------------------\n//! Skip a tag\nvoid XMLReader::SkipTag(XMLTag& tag)\n{\n\t// if this tag is a leaf we just return\n\tif (tag.isleaf()) { return; }\n\n\t// if it is not a leaf we have to loop over all \n\t// the children, skipping each child in turn\n\t++tag;\n\tdo\n\t{\n\t\tSkipTag(tag);\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n//-----------------------------------------------------------------------------\nchar XMLReader::GetNextChar()\n{\n\tchar ch;\n\tdo\n\t{\n\t\tch = readNextChar();\n\t\tif (ch == '\\n') ++m_nline;\n\t} while (ch == '\\r');\n\treturn ch;\n}\n\nifstream* XMLReader::GetFileStream()\n{\n    return dynamic_cast<ifstream*>(m_stream);\n}\n\n//! return the current line\nint XMLReader::GetCurrentLine() { return m_nline; }\n\n\nconst std::string& XMLReader::GetLastComment()\n{\n    // Get rid of starting and trailing new lines in comments\n\tif (m_comment.empty() == false)\n\t{\n\t\tif (*m_comment.begin() == '\\n')\n\t\t{\n\t\t\tm_comment.erase(m_comment.begin());\n\t\t}\n\t\tif (*m_comment.rbegin() == '\\n')\n\t\t{\n\t\t\tm_comment.erase(m_comment.end());\n\t\t}\n\t}\n\n    return m_comment;\n}\n"
  },
  {
    "path": "FEBioXML/XMLReader.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <string>\n#include <vector>\n#include <stdexcept>\n#include <assert.h>\n#include \"febioxml_api.h\"\n\n//-------------------------------------------------------------------------\n// forward declaration\nclass XMLReader;\n\n//-------------------------------------------------------------------------\n//! This class represents a xml-attribute\nclass FEBIOXML_API XMLAtt\n{\npublic:\n\t//! constructor\n\tXMLAtt();\n\n\t//! comparison operators\n\tbool operator == (const char* sz);\n\tbool operator != (const char* szval);\n\n\t//! Get the attribute name\n\tconst char* name() { return m_name.c_str(); }\n\n\t//! Get the attribute value\n\tconst char* cvalue() { return m_val.c_str(); }\n\npublic:\n\tvoid value(int& val) { val = atoi(m_val.c_str()); }\n\tint value(int* v, int n);\n\tint value(double* v, int n);\n\ttemplate <typename T> T value() { return T(0); }\n\npublic:\n\tstd::string\tm_name;\n\tstd::string m_val;\n\tbool\tm_bvisited;\t\t\t//!< was the attribute processed or not?\n};\n\ntemplate <> inline int XMLAtt::value<int>() { return atoi(m_val.c_str()); }\ntemplate <> inline double XMLAtt::value<double>() { return atof(m_val.c_str()); }\ntemplate <> inline std::string XMLAtt::value<std::string>() { return m_val; }\n\n//-------------------------------------------------------------------------\n//! This class implements a xml-tag. The value and attributes of this tag\n//! can be queried.\n//! \\todo I would like to get rid of the m_szroot element and replace it with a \n//!       parent tag. The root element can then be identified by the tag that \n//!       does not have a parent\nclass FEBIOXML_API XMLTag\n{\npublic:\n\tstd::string\tm_sztag;\t\t\t// tag name\n\tstd::string m_szval;\t\t\t\t// tag value\n\n\tstd::vector<XMLAtt>\tm_att;\t\t\t\t// attribute list\n\n\tstd::vector<std::string> m_path;\t// current path (name tags of parents)\n\n\tXMLReader*\tm_preader;\t\t\t// pointer to reader\n    int64_t\t\tm_fpos;\t\t\t\t// file position of next tag\n\tint\t\tm_nstart_line;\t\t// line number at beginning of tag\n\tint\t\tm_ncurrent_line;\t// current line number\n\n\tbool\tm_bend;\t\t// end tag flag\n\tbool\tm_bleaf;\t// this is a leaf (i.e. has no child elements)\n\tbool\tm_bempty;\t// empty tag (i.e. no value)\n\npublic:\n\tXMLTag();\n\n\tvoid clear();\n\n\tint currentLine() const { return m_ncurrent_line; }\n\n\tbool operator == (const char* sztag) { return (strcmp(sztag, m_sztag.c_str()) == 0); }\n\tbool operator != (const char* sztag) { return (strcmp(sztag, m_sztag.c_str()) != 0); }\n\tvoid operator ++ ();\n\n\tvoid skip();\n\n\tbool isend() { return m_bend; }\n\tbool isleaf() { return m_bleaf; }\n\tbool isempty() { return m_bempty; }\n\n\t// count the number of children\n\tint children();\n\n\tconst char* AttributeValue(const char* szat, bool bopt = false);\n\tXMLAtt* AttributePtr(const char* szat);\n\tXMLAtt* Attribute(const char* szat, bool bopt);\n\tXMLAtt& Attribute(const char* szat);\n\n\ttemplate <typename T> T AttributeValue(const char* szatt, const T& def_val) { return def_val; }\n\n\tbool AttributeValue(const char* szat, int&    n, bool bopt = false);\n\tbool AttributeValue(const char* szat, double& d, bool bopt = false);\n\t\t\n\tconst char* Name() { return m_sztag.c_str(); }\n\n\tvoid value(double& val) { val = atof(m_szval.c_str()); } \n\tvoid value(float& val)  { val = (float) atof(m_szval.c_str()); }\n\tvoid value(int& val) { val = atoi(m_szval.c_str()); }\n\tvoid value(long& val) { val = (long) atoi(m_szval.c_str()); }\n\tvoid value(short& val) { val = (short) atoi(m_szval.c_str()); }\n\tint value(double* pf, int n);\n\tint value(float* pf, int n);\n\tint value(int* pi, int n);\n\tint value(std::vector<std::string>& stringList, int n);\n\tvoid value(std::vector<std::string>& stringList);\n\tvoid value(bool& val);\n\tvoid value(char* szstr);\n\tvoid value(std::string& val);\n\tvoid value(std::vector<int>& l);\n\tvoid value2(std::vector<int>& l);\n\tvoid value(std::vector<double>& l);\n\n\ttemplate <class T> void value(T& v);\n\n\tconst char* szvalue() { return m_szval.c_str(); }\n\n\tstd::string relpath(const char* szroot) const;\n\n\tconst std::string& comment();\n    const void clearComment();\n};\n\n\ntemplate <> inline int XMLTag::AttributeValue<int>(const char* szatt, const int& def_val)\n{\n\tXMLAtt* pa = AttributePtr(szatt);\n\tif (pa) return pa->value<int>();\n\telse return def_val;\n}\n\ntemplate <> inline double XMLTag::AttributeValue<double >(const char* szatt, const double& def_val)\n{\n\tXMLAtt* pa = AttributePtr(szatt);\n\tif (pa) return pa->value<double>();\n\telse return def_val;\n}\n\ntemplate <> inline std::string XMLTag::AttributeValue<std::string>(const char* szatt, const std::string& def_val)\n{\n\tXMLAtt* pa = AttributePtr(szatt);\n\tif (pa) return pa->value<std::string>();\n\telse return def_val;\n}\n\n//-----------------------------------------------------------------------------\n//! This class implements a reader for XML files\nclass FEBIOXML_API XMLReader\n{\npublic:\n\tenum {BUF_SIZE = 32768};\n\npublic:\n\t// Base class for Exceptions\n\tclass Error : public std::runtime_error\n\t{\n\tpublic:\n\t\tError(const std::string& err) : std::runtime_error(err) {}\n\t\tError(XMLTag& tag, const std::string& err);\n\t};\n\n\t// the end of file was detected unexpectedly.\n\tclass UnexpectedEOF : public Error {\n\tpublic:\n\t\tUnexpectedEOF() : Error(\"Unexpected end of file\") {}\n\t};\n\n\t// A syntax error was found\n\tclass FEBIOXML_API XMLSyntaxError : public Error\n\t{\n\tpublic:\n\t\tXMLSyntaxError(int line_number = -1);\n\t};\n\n\t// an end tag was not matched\n\tclass FEBIOXML_API UnmatchedEndTag : public Error\n\t{\n\tpublic:\n\t\tUnmatchedEndTag(XMLTag& t);\n\t};\n\n\t// an unknown tag was encountered \n\tclass FEBIOXML_API InvalidTag : public Error\n\t{\n\tpublic:\n\t\tInvalidTag(XMLTag& t);\n\t};\n\n\t// The value of a tag was invald \n\tclass FEBIOXML_API InvalidValue : public Error\n\t{\n\tpublic:\n\t\tInvalidValue(XMLTag& t);\n\t};\n\n\t// the value of an attribute was invalid \n\tclass FEBIOXML_API InvalidAttributeValue : public Error\n\t{\n\tpublic:\n\t\tInvalidAttributeValue(XMLTag& t, const char* sza, const char* szv = 0);\n\t\tInvalidAttributeValue(XMLTag& t, XMLAtt& att);\n\t};\n\n\t// an attribute is invalid\n\tclass FEBIOXML_API InvalidAttribute : public Error\n\t{\n\tpublic:\n\t\tInvalidAttribute(XMLTag& t, const char* sza);\n\t};\n\n\t// an attribute was missing\n\tclass FEBIOXML_API MissingAttribute : public Error\n\t{\n\tpublic:\n\t\tMissingAttribute(XMLTag& t, const char* sza);\n\t};\n\n\tclass FEBIOXML_API MissingTag : public Error\n\t{\n\tpublic:\n\t\tMissingTag(XMLTag& t, const char* szt);\n\t};\n\t//------------------------\n\npublic:\n\t//! constructor/destructor\n\tXMLReader();\n\tvirtual ~XMLReader();\n\n    std::ifstream* GetFileStream();\n\n\t//! Open the xml file\n\tbool Open(const char* szfile, bool checkForXMLTag = true);\n\n    //! Pass xml formatted string to reader\n    bool OpenString(std::string& xml, bool checkForXMLTag = true);\n\n\t//! Close the xml file\n\tvoid Close();\n\n\t//! Find a tag\n\tbool FindTag(const char* xpath, XMLTag& tag);\n\n\t//! Get the next tag\n\tvoid NextTag(XMLTag& tag);\n\n\t//! return the current line\n\tint GetCurrentLine();\n\n\t//! Skip a tag\n\tvoid SkipTag(XMLTag& tag);\n\n\tconst std::string& GetLastComment();\n\nprotected: // helper functions\n\n\t//! Get the next character in the file\n\tchar GetChar();\n\n\t//! Read a tag\n\tvoid ReadTag(XMLTag& tag);\n\n\t//! Read the value of a tag\n\tvoid ReadValue(XMLTag& tag);\n\n\t//! process end tag\n\tvoid ReadEndTag(XMLTag& tag);\n\n\t//! read the next character of the buffer\n\tchar readNextChar();\n\n\t//! get the current position\n    int64_t currentPos();\n\n\t//! move the file pointer\n    void rewind(int64_t nstep);\n\n\t// only used for processing comments\n\tchar GetNextChar();\n\t\nprotected:\n    std::istream* m_stream;\n\n\tint\t\tm_nline;\t\t//!< current line (used only as temp storage)\n    int64_t\tm_currentPos;\t//!< current file position\n\n\tstd::string\tm_comment;\t//!< last comment that was read\n\n\tchar*\t\tm_buf;\n    int64_t    m_bufIndex, m_bufSize;\n\tbool\t\tm_eof;\n};\n\n//-----------------------------------------------------------------------------\n// some inline functions\ninline void XMLTag::operator ++ () { m_preader->NextTag(*this); }\n\ninline void XMLTag::skip() { m_preader->SkipTag(*this); }\n\ninline const std::string& XMLTag::comment() { return m_preader->GetLastComment(); }\n\n//-----------------------------------------------------------------------------\n// mechanism for using custom types with XMLReader. \ntemplate <class T> void string_to_type(const std::string& s, T& v) { assert(false); }\ntemplate <class T> void XMLTag::value(T& v) { string_to_type(m_szval, v); }\n"
  },
  {
    "path": "FEBioXML/XMLWriter.cpp",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio-Studio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n// XMLWriter.cpp: implementation of the XMLWriter class.\n//\n//////////////////////////////////////////////////////////////////////\n\n#include \"XMLWriter.h\"\n#include <sstream>\n#include <fstream>\n#include <iomanip>\nusing namespace std;\n\nvoid XMLElement::value(int    n) { stringstream ss; ss << n; m_val = ss.str(); }\nvoid XMLElement::value(bool   b) { int n = (b ? 1 : 0); stringstream ss; ss << n; m_val = ss.str(); }\nvoid XMLElement::value(double g) { stringstream ss; ss << std::setprecision(7) << g; m_val = ss.str(); }\n\nvoid XMLElement::value(int* pi, int n)\n{\n\tm_val.clear();\n\tif (n==0) return;\n\n\tstringstream ss;\n\tss << pi[0];\n\tfor (int i=1; i<n; ++i)\n\t{\n\t\tss << \",\" << pi[i];\n\t}\n\tm_val = ss.str();\n}\n\nvoid XMLElement::value(double* pg, int n)\n{\n\tm_val.clear();\n\tif (n==0) return;\n\tstringstream ss;\n\tss << pg[0];\n\tfor (int i=1; i<n; ++i)\n\t{\n\t\tss << \",\" << pg[i];\n\t}\n\tm_val = ss.str();\n}\n/*\nvoid XMLElement::value(const vec3f& r)\n{\n\tstringstream ss;\n\tss << r.x << \",\" << r.y << \",\" << r.z;\n\tm_val = ss.str();\n}\n\nvoid XMLElement::value(const vec3d& r)\n{ \n\tstringstream ss;\n\tss << r.x << \",\" << r.y << \",\" << r.z;\n\tm_val = ss.str();\n}\n\nvoid XMLElement::value(const vec2i& r)\n{\n\tstringstream ss;\n\tss << r.x << \",\" << r.y;\n\tm_val = ss.str();\n}\n\nvoid XMLElement::value(const mat3d& a)\n{\n\tstringstream ss;\n\tss << a(0,0) << \",\" << a(0,1) << \",\" << a(0,2) << \",\";\n\tss << a(1,0) << \",\" << a(1,1) << \",\" << a(1,2) << \",\";\n\tss << a(2,0) << \",\" << a(2,1) << \",\" << a(2,2);\n\tm_val = ss.str();\n}\n*/\n\nvoid XMLElement::value(const std::vector<int>& v)\n{\n\tm_val.clear();\n\tif (v.empty()) return;\n\n\tstringstream ss;\n\tss << v[0];\n\tfor (int i = 1; i < v.size(); ++i)\n\t{\n\t\tss << \",\" << v[i];\n\t}\n\tm_val = ss.str();\n}\n\nvoid XMLElement::value(const std::vector<double>& v)\n{\n\tm_val.clear();\n\tif (v.empty()) return;\n\n\tstringstream ss;\n\tss << v[0];\n\tfor (int i = 1; i < v.size(); ++i)\n\t{\n\t\tss << \",\" << v[i];\n\t}\n\tm_val = ss.str();\n}\n\nint XMLElement::add_attribute(const char* szn, const char* szv)\n{\n\tm_att.push_back(XMLAtt(szn, szv));\n\treturn (int) m_att.size()-1;\n}\n\nint XMLElement::add_attribute(const char* szn, int n)\n{\n\tstringstream ss; ss << n;\n\tm_att.push_back(XMLAtt(szn, ss.str()));\n\treturn (int)m_att.size() - 1;\n}\n\nint XMLElement::add_attribute(const char* szn, bool b)\n{\n\tint n = (b ? 1 : 0);\n\tstringstream ss; ss << n;\n\tm_att.push_back(XMLAtt(szn, ss.str()));\n\treturn (int)m_att.size() - 1;\n}\n\nint XMLElement::add_attribute(const char* szn, double g)\n{\n\tstringstream ss; ss << g;\n\tm_att.push_back(XMLAtt(szn, ss.str()));\n\treturn (int)m_att.size() - 1;\n}\n\nint XMLElement::add_attribute(const char* szn, const std::string& s)\n{\n\tm_att.push_back(XMLAtt(szn, s));\n\treturn (int)m_att.size() - 1;\n}\n\nvoid XMLElement::set_attribute(int nid, const char* szv)\n{\n\tm_att[nid].m_val = szv;\n}\n\nvoid XMLElement::set_attribute(int nid, int n)\n{\n\tstringstream ss; ss << n;\n\tm_att[nid].m_val = ss.str();\n}\n\nvoid XMLElement::set_attribute(int nid, bool b)\n{\n\tstringstream ss; ss << (b?(int)1:(int)0);\n\tm_att[nid].m_val = ss.str();\n}\n\nvoid XMLElement::set_attribute(int nid, double g)\n{\n\tstringstream ss; ss << g;\n\tm_att[nid].m_val = ss.str();\n}\n\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nXMLWriter::XMLFloatFormat\tXMLWriter::m_floatFormat = XMLWriter::FixedFormat;\n\nvoid XMLWriter::SetFloatFormat(XMLFloatFormat fmt)\n{\n\tm_floatFormat = fmt;\n}\n\nXMLWriter::XMLFloatFormat XMLWriter::GetFloatFormat()\n{\n\treturn m_floatFormat;\n}\n\nXMLWriter::XMLWriter()\n{\n    m_stream = nullptr;\n\tm_level = 0;\n\n\tm_sztab[0] = 0;\n}\n\nXMLWriter::~XMLWriter()\n{\n\tclose();\n}\n\nbool XMLWriter::open(const char* szfile)\n{\n    if (m_stream) return false;\n\tif (szfile == nullptr) return false;\n\n    m_stream = new ofstream(szfile, std::ios_base::out);\n\tif (m_stream && m_stream->fail()) return false;\n\n\t// write the first line\n    if(m_stream)\n    {\n        init();\n    }\n\t\n    return (m_stream != nullptr);\n}\n\nbool XMLWriter::setStringstream(std::ostringstream* stream)\n{\n    if (m_stream) return false;\n    if (!stream) return false;\n\n    m_stream = stream;\n\n    init();\n\n    return true;\n}\n\nvoid XMLWriter::init()\n{\n    m_stream->precision(12);\n    *m_stream << \"<?xml version=\\\"1.0\\\" encoding=\\\"ISO-8859-1\\\"?>\\n\";\n}\n\nvoid XMLWriter::close()\n{\n    ofstream* fstrm = dynamic_cast<ofstream*>(m_stream);\n    if (fstrm) \n    {\n        fstrm->close();\n\n        delete m_stream;\n        m_stream = nullptr;\n    }\n}\n\nvoid XMLWriter::inc_level()\n{\n\t++m_level;\n\n\tfor (int i=0; i<m_level; ++i) \n\t\tm_sztab[i] = '\\t';\n\tm_sztab[m_level] = 0;\n}\n\nvoid XMLWriter::dec_level()\n{\n\tif (m_level <= 0) return;\n\n\t--m_level;\n\n\tfor (int i = 0; i < m_level; ++i)\n\t\tm_sztab[i] = '\\t';\n\tm_sztab[m_level] = 0;\n}\n\n\nvoid XMLWriter::add_branch(XMLElement& el, bool bclear)\n{\n    *m_stream << m_sztab << \"<\" << el.m_tag;\n\n\tfor (int i=0; i<el.attributes(); ++i)\n\t{\n\t\tconst XMLElement::XMLAtt& att = el.attribute(i);\n\n        *m_stream << \" \" << att.name() << \"=\\\"\" << att.value() << \"\\\"\";\n\t}\n\n    *m_stream << \">\" << el.m_val << \"\\n\";\n\n\tstrcpy(m_tag[m_level], el.m_tag.c_str());\n\n\tinc_level();\n\n\tif (bclear) el.clear();\n}\n\nvoid XMLWriter::add_branch(const char* sz)\n{\n    *m_stream << m_sztab << \"<\" << sz << \">\\n\";\n\n\tstrcpy(m_tag[m_level], sz);\n\tinc_level();\n}\n\nvoid XMLWriter::add_empty(XMLElement& el, bool bclear)\n{\n    *m_stream << m_sztab << \"<\" << el.m_tag;\n\n\tfor (int i=0; i<el.attributes(); ++i)\n\t{\n\t\tconst XMLElement::XMLAtt& att = el.attribute(i);\n\n        *m_stream << \" \" << att.name() << \"=\\\"\" << att.value() << \"\\\"\";\n\t}\n\n    *m_stream << \"/>\\n\";\n\n\n\tif (bclear) el.clear();\n}\n\nvoid XMLWriter::add_leaf(XMLElement& el, bool bclear)\n{\n    *m_stream << m_sztab << \"<\" << el.m_tag;\n\n\tfor (int i=0; i<el.attributes(); ++i)\n\t{\n\t\tconst XMLElement::XMLAtt& att = el.attribute(i);\n        *m_stream << \" \" << att.name() << \"=\\\"\" << att.value() << \"\\\"\";\n\t}\n\n    *m_stream << \">\" << el.m_val << \"</\" << el.m_tag << \">\\n\";\n\n\tif (bclear) el.clear();\n}\n\nvoid XMLWriter::write_leaf(const char* sztag, const char* szval)\n{\n    *m_stream << m_sztab << \"<\" << sztag;\n\t*m_stream << \">\";\n\n\tif (m_encodeControlChars)\n\t{\n\t\tconst char* ch = szval;\n\t\twhile (ch && *ch)\n\t\t{\n\t\t\tif      (*ch == '\\t') *m_stream << \"&#x9;\";\n\t\t\telse if (*ch == '\\n') *m_stream << \"&#xA;\";\n\t\t\telse if (*ch == '\\r') *m_stream << \"&#xD;\";\n\t\t\telse *m_stream << *ch;\n\t\t\t++ch;\n\t\t}\n\t}\n\telse\n\t{\n\t\t*m_stream << szval;\n\t}\n\n\t*m_stream << \"</\" << sztag << \">\\n\";\n}\n\nvoid XMLWriter::add_leaf(const char* szn, const char* szv)\n{\n\twrite_leaf(szn, szv);\n}\n\nvoid XMLWriter::add_leaf(const char* szn, const std::string& s)\n{\n\twrite_leaf(szn, s.c_str());\n}\n\nvoid XMLWriter::add_leaf(const char* szn, double* pg, int n)\n{\n    *m_stream << m_sztab << \"<\" << szn << \">\";\n\n\tif (n>0)\n\t{\n        *m_stream << pg[0];\n        for (int i=1; i<n; ++i) *m_stream << \",\" << pg[i];\n\t}\n\n    *m_stream << \"</\" << szn << \">\\n\";\n}\n\nvoid XMLWriter::add_leaf(const char* szn, float* pg, int n)\n{\n    *m_stream << m_sztab << \"<\" << szn << \">\";\n\n\tif (n>0)\n\t{\n        *m_stream << pg[0];\n        for (int i=1; i<n; ++i) *m_stream << \",\" << pg[i];\n\t}\n\n    *m_stream << \"</\" << szn << \">\\n\";\n}\n\n\nvoid XMLWriter::add_leaf(const char* szn, int* pi, int n)\n{\n    *m_stream << m_sztab << \"<\" << szn << \">\";\n\n\tif (n>0)\n\t{\n        *m_stream << pi[0];\n        for (int i=1; i<n; ++i) *m_stream << \",\" << pi[i];\n\t}\n\n    *m_stream << \"</\" << szn << \">\\n\";\n}\n\nvoid XMLWriter::add_leaf(XMLElement& el, const std::vector<int>& A)\n{\n    *m_stream << m_sztab << \"<\" << el.m_tag;\n\n\tfor (int i=0; i<el.attributes(); ++i)\n\t{\n\t\tconst XMLElement::XMLAtt& att = el.attribute(i);\n        *m_stream << \" \" << att.name() << \"=\\\"\" << att.value() << \"\\\"\";\n\t}\n\n\tinc_level();\n\n    *m_stream << \">\\n\" << m_sztab;\n\n\tint n = (int) A.size(), l = 0;\n    streampos start;\n\tfor (int i=0; i<n; ++i)\n\t{\n\n        start = m_stream->tellp();\n        *m_stream << A[i];\n        l += m_stream->tellp() - start;\n\n\t\tif (i < n-1)\n\t\t{\n            if ((i+1) % 8 == 0) { *m_stream << \",\\n\" << m_sztab; l=0; }\n\t\t\telse *m_stream << \", \";\n\t\t}\n\t}\n\tdec_level();\n    *m_stream << \"\\n\" << m_sztab << \"</\" << el.m_tag << \">\\n\";\n}\n\n\nvoid XMLWriter::close_branch()\n{\n\tif (m_level > 0)\n\t{\n\t\tdec_level();\n\n\t\tchar szformat[256] = {0};\n\t\tsnprintf(szformat, sizeof(szformat), \"%s</%%s>\\n\", m_sztab);\n\n        *m_stream << m_sztab << \"</\" << m_tag[m_level] << \">\\n\";\n\t}\n}\n\nvoid XMLWriter::add_comment(const std::string& s, bool singleLine)\n{\n\tif (s.empty()) return;\n\n\tif (singleLine)\n\t{\n        *m_stream << \"<!-- \" << s << \" -->\\n\";\n\t}\n\telse\n\t{\n        *m_stream << \"<!--\\n\" << s << \"\\n-->\\n\";\n\t}\n}\n"
  },
  {
    "path": "FEBioXML/XMLWriter.h",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio-Studio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <stdio.h>\n#include <string.h>\n#include <vector>\n#include <string>\n#include <assert.h>\n#include \"febioxml_api.h\"\nclass XMLWriter;\n\nclass FEBIOXML_API XMLElement\n{\npublic:\n\tclass XMLAtt\n\t{\n\tpublic:\n\t\tXMLAtt() {}\n\t\tXMLAtt(const std::string& name, const std::string& val) : m_name(name), m_val(val) {}\n\t\tXMLAtt(const XMLAtt& att) : m_name(att.m_name), m_val(att.m_val) {}\n\t\tvoid operator = (const XMLAtt& att) { m_name = att.m_name; m_val = att.m_val; }\n\n\t\tconst char* name() const { return m_name.c_str(); }\n\t\tconst char* value() const { return m_val.c_str(); }\n\n\tpublic:\n\t\tstd::string m_name;\n\t\tstd::string m_val;\n\t};\n\npublic:\n\tXMLElement(const char* szname = 0)\n\t{\n\t\tclear();\n\t\tif (szname) m_tag = szname;\n\t}\n\n\tvoid clear()\n\t{\n\t\tm_att.clear();\n\t\tm_tag.clear();\n\t\tm_val.clear();\n\t}\n\n\tvoid name(const char* sz) { if (sz) m_tag = sz; else m_tag.clear(); }\n\tconst char* name() const { return m_tag.c_str(); }\n\n\tvoid value(const char* sz) { if (sz) m_val = sz; else m_val.clear(); }\n\tvoid value(int    n);\n\tvoid value(int* pi, int n);\n\tvoid value(bool   b);\n\tvoid value(double g);\n\tvoid value(double* pg, int n);\n\tvoid value(const std::vector<int>& v);\n\tvoid value(const std::vector<double>& v);\n\n\ttemplate <class T> void value(const T& v);\n\n\tint add_attribute(const char* szn, const char* szv);\n\tint add_attribute(const char* szn, int n);\n\tint add_attribute(const char* szn, bool b);\n\tint add_attribute(const char* szn, double g);\n\tint add_attribute(const char* szn, const std::string& s);\n\n\tvoid set_attribute(int nid, const char* szv);\n\tvoid set_attribute(int nid, int n);\n\tvoid set_attribute(int nid, bool b);\n\tvoid set_attribute(int nid, double g);\n\n\tint attributes() const { return (int) m_att.size(); }\n\tconst XMLAtt& attribute(int i) const { return m_att[i]; }\n\nprotected:\n\tstd::string\tm_tag;\t\t// element name\n\tstd::string\tm_val;\t\t// element value\n\n\tstd::vector<XMLAtt> m_att; // attributes\n\npublic:\n\tfriend class XMLWriter;\n};\n\nclass FEBIOXML_API XMLWriter\n{\n\tenum {MAX_TAGS = 32};\n\npublic:\n\tenum XMLFloatFormat {\n\t\tScientificFormat,\n\t\tFixedFormat\n\t};\n\npublic:\n\tXMLWriter();\n\tvirtual ~XMLWriter();\n\t\n\tbool open(const char* szfile);\n    bool setStringstream(std::ostringstream* stream);\n    void init();\n\n\tvoid close();\n\n\tvoid add_branch(XMLElement& el, bool bclear = true);\n\tvoid add_branch(const char* szname);\n\n\tvoid add_empty(XMLElement& el, bool bclear = true);\n\n\tvoid add_leaf  (XMLElement& el, bool bclear = true);\n\n\tvoid add_leaf(const char* szn, const char* szv);\n\tvoid add_leaf(const char* szn, const std::string& s);\n\n\tvoid add_leaf(const char* szn, int    n){ char szv[256]; snprintf(szv, sizeof(szv), \"%d\" , n); write_leaf(szn, szv); }\n\tvoid add_leaf(const char* szn, bool   b){ char szv[256]; snprintf(szv, sizeof(szv), \"%d\" , b); write_leaf(szn, szv); }\n\tvoid add_leaf(const char* szn, double g){ char szv[256]; snprintf(szv, sizeof(szv), \"%lg\", g); write_leaf(szn, szv); }\n\tvoid add_leaf(const char* szn, float  g){ char szv[256]; snprintf(szv, sizeof(szv), \"%g\" , g); write_leaf(szn, szv); }\n\tvoid add_leaf(const char* szn, int *pi, int n);\n\tvoid add_leaf(const char* szn, float* pg, int n);\n\tvoid add_leaf(const char* szn, double* pg, int n);\n\tvoid add_leaf(XMLElement& el, const std::vector<int>& A);\n\n\ttemplate <class T> void add_leaf(const char* szname, const T& v);\n\n\tvoid close_branch();\n\n\tvoid add_comment(const std::string& s, bool singleLine = false);\n\npublic:\n\tstatic void SetFloatFormat(XMLFloatFormat fmt);\n\tstatic XMLFloatFormat GetFloatFormat();\n\n\tvoid SetEncodeControlChars(bool b) { m_encodeControlChars = b; }\n\tbool EncodeControlChars() const { return m_encodeControlChars; }\n\nprotected:\n\tvoid inc_level();\n\tvoid dec_level();\n\n\tvoid write_leaf(const char* sztag, const char* szval);\n\nprotected:\n    std::ostream* m_stream;\n\tint\t\tm_level;\n\n\tchar\tm_tag[MAX_TAGS][256];\n\tchar\tm_sztab[256];\n\n\tbool\tm_encodeControlChars = false;\n\n\tstatic XMLFloatFormat\tm_floatFormat;\n};\n\ntemplate <class T> std::string type_to_string(const T& v)\n{\n\treturn std::string(v);\n}\n\ntemplate <class T> void XMLElement::value(const T& v)\n{\n\tstd::string s = type_to_string<T>(v);\n\tm_val = s;\n}\n\ntemplate <class T> void XMLWriter::add_leaf(const char* szname, const T& v)\n{\n\tstd::string s = type_to_string<T>(v);\n\tadd_leaf(szname, s);\n}\n"
  },
  {
    "path": "FEBioXML/febioxml_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef febioxml_EXPORTS\n\t\t\t#define FEBIOXML_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEBIOXML_API __declspec(dllimport)\n\t\t#endif\n\t#else\n#define FEBIOXML_API\n\t#endif\n#else\n\t#define FEBIOXML_API\n#endif\n"
  },
  {
    "path": "FEBioXML/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// this file is just here in case I decide to use precompiled headers for the library.\n"
  },
  {
    "path": "FEBioXML/xmltool.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"xmltool.h\"\n#include <FECore/FECoreKernel.h>\n#include <FECore/FEScalarValuator.h>\n#include <FECore/FEModelParam.h>\n\nint enumValue(const char* val, const char* szenum);\nbool is_number(const char* sz);\n\n//-----------------------------------------------------------------------------\nbool parseEnumParam(FEParam* pp, const char* val)\n{\n\t// get the enums\n\tconst char* ch = pp->enums();\n\tif (ch == nullptr) return false;\n\n\t// special cases\n\tif (strncmp(ch, \"@factory_list\", 13) == 0)\n\t{\n\t\tint classID = atoi(ch + 14);\n\n\t\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\t\tfor (int i = 0, n = 0; i < fecore.FactoryClasses(); ++i)\n\t\t{\n\t\t\tconst FECoreFactory* fac = fecore.GetFactoryClass(i);\n\t\t\tif (fac->GetSuperClassID() == classID)\n\t\t\t{\n\t\t\t\tif (strcmp(fac->GetTypeStr(), val) == 0)\n\t\t\t\t{\n\t\t\t\t\tpp->value<int>() = n;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tn++;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tint n = enumValue(val, ch);\n\tif (n != -1) pp->value<int>() = n;\n\telse\n\t{\n\t\t// see if the value is an actual number\n\t\tif (is_number(val))\n\t\t{\n\t\t\tn = atoi(val);\n\t\t\tpp->value<int>() = n;\n\t\t}\n\t}\n\n\treturn (n != -1);\n}\n\n//-----------------------------------------------------------------------------\n//! This function parses a parameter list\nbool fexml::readParameter(XMLTag& tag, FEParameterList& paramList, const char* paramName, FECoreBase* parent)\n{\n\t// see if we can find this parameter\n\tFEParam* pp = paramList.FindFromName((paramName == 0 ? tag.Name() : paramName));\n\tif (pp == 0) return false;\n\t\n\tif (pp->dim() == 1)\n\t{\n\t\tswitch (pp->type())\n\t\t{\n\t\tcase FE_PARAM_DOUBLE  : { double d; tag.value(d); pp->value<double  >() = d; } break;\n\t\tcase FE_PARAM_INT     : \n\t\t{ \n\t\t\tconst char* szenum = pp->enums();\n\t\t\tif (szenum == 0)\n\t\t\t{\n\t\t\t\tint n;\n\t\t\t\ttag.value(n); pp->value<int>() = n;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbool bfound = parseEnumParam(pp, tag.szvalue());\n\t\t\t\tif (bfound == false) throw XMLReader::InvalidValue(tag);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t\tcase FE_PARAM_BOOL    : { bool b; tag.value(b); pp->value<bool>() = b; } break;\n\t\tcase FE_PARAM_STD_STRING: { std::string s; tag.value(s); pp->value<std::string>() = s; } break;\n\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t{\n\t\t\tif (parent == nullptr) return false;\n\n\t\t\t// get the model parameter\n\t\t\tFEParamDouble& p = pp->value<FEParamDouble>();\n\n\t\t\t// get the type\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t\t\t// if the type is not specified, we'll try to determine if \n\t\t\t// it's a math expression or a const\n\t\t\tif (sztype == 0)\n\t\t\t{\n\t\t\t\tconst char* szval = tag.szvalue();\n\t\t\t\tsztype = (is_number(szval) ? \"const\" : \"math\");\n\t\t\t}\n\n\t\t\t// allocate valuator\n\t\t\tFEScalarValuator* val = fecore_new<FEScalarValuator>(sztype, parent->GetFEModel());\n\t\t\tif (val == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t// read the parameter list\n\t\t\treadParameterList(tag, val);\n\n\t\t\t// assign the valuator to the parameter\n\t\t\tp.setValuator(val);\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tswitch (pp->type())\n\t\t{\n\t\tcase FE_PARAM_INT   : { \n\t\t\tvector<int> d(pp->dim()); \n\t\t\ttag.value(&d[0], pp->dim()); \n\t\t\tfor (int i=0; i<pp->dim(); ++i) \n\t\t\t\t*(pp->pvalue<int>()+i) = d[i]; \n\t\t} \n\t\tbreak;\n\t\tcase FE_PARAM_DOUBLE: \n\t\t{ \n\t\t\tvector<double> d(pp->dim()); \n\t\t\ttag.value(&d[0], pp->dim()); \n\t\t\tfor (int i = 0; i < pp->dim(); ++i) \n\t\t\t\t*(pp->pvalue<double>() + i) = d[i];\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid fexml::readList(XMLTag& tag, vector<int>& l)\n{\n\t// make sure the list is empty\n\tl.clear();\n\n\t// get a pointer to the value\n\tconst char* sz = tag.szvalue();\n\n\t// parse the string\n\tconst char* ch;\n\tdo\n\t{\n\t\tint n0, n1, nn;\n\t\tint nread = sscanf(sz, \"%d:%d:%d\", &n0, &n1, &nn);\n\t\tswitch (nread)\n\t\t{\n\t\tcase 1:\n\t\t\tn1 = n0;\n\t\t\tnn = 1;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tnn = 1;\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (int i = n0; i <= n1; i += nn) l.push_back(i);\n\n\t\tch = strchr(sz, ',');\n\t\tif (ch) sz = ch + 1;\n\t}\n\twhile (ch != 0);\n}\n\nbool fexml::readParameterList(XMLTag& tag, FECoreBase* pc)\n{\n\t// read attribute parameters\n\tif (!readAttributeParams(tag, pc)) return false;\n\n\t// make sure this tag has children\n\tif (!tag.isleaf())\n\t{\n\t\t// process the parameter lists\n\t\t++tag;\n\t\tdo\n\t\t{\n\t\t\tif (readParameter(tag, pc) == false) return false;\n\t\t\t++tag;\n\t\t} while (!tag.isend());\n\t}\n\telse if ((tag.isempty() == false) && (pc->Parameters() > 0))\n\t{\n\t\t// there should be one parameter with the same name as the tag\n\t\tif (readParameter(tag, pc) == false)\n\t\t{\n\t\t\t// try a parameter with the type string as name\n\t\t\tif (readParameter(tag, pc, pc->GetTypeStr()) == false)\n\t\t\t\tthrow XMLReader::InvalidTag(tag);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool fexml::readAttributeParams(XMLTag& tag, FECoreBase* pc)\n{\n\tFEParameterList& pl = pc->GetParameterList();\n\n\t// process all the other attributes\n\tfor (XMLAtt& att : tag.m_att)\n\t{\n\t\tconst char* szatt = att.name();\n\t\tconst char* szval = att.cvalue();\n\t\tif ((att.m_bvisited == false) && szatt && szval)\n\t\t{\n\t\t\tFEParam* param = pl.FindFromName(szatt);\n\t\t\tif (param && (param->GetFlags() & FE_PARAM_ATTRIBUTE))\n\t\t\t{\n\t\t\t\tswitch (param->type())\n\t\t\t\t{\n\t\t\t\tcase FE_PARAM_INT:\n\t\t\t\t{\n\t\t\t\t\tif (param->enums() == nullptr)\n\t\t\t\t\t\tparam->value<int>() = atoi(szval);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (parseEnumParam(param, szval) == false) throw XMLReader::InvalidAttributeValue(tag, szatt, szval);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase FE_PARAM_DOUBLE: param->value<double>() = atof(szval); break;\n\t\t\t\tcase FE_PARAM_STD_STRING: param->value<std::string>() = szval; break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow XMLReader::InvalidAttributeValue(tag, szatt, szval);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (strcmp(\"name\", szatt) == 0) pc->SetName(szval);\n\t\t\telse if (strcmp(\"id\", szatt) == 0) pc->SetID(atoi(szval));\n\t\t\telse if (strcmp(\"type\", szatt) == 0) { /* don't do anything here */ }\n\t\t\telse if (strcmp(\"lc\", szatt) == 0) { /* don't do anything. Loadcurves are processed elsewhere. */ }\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert(false);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool fexml::readParameter(XMLTag& tag, FECoreBase* pc, const char* szparam)\n{\n\tFEParameterList& PL = pc->GetParameterList();\n\tif (readParameter(tag, PL, szparam, pc) == false)\n\t{\n\t\t// see if this is a property\n\t\t// if we get here, the parameter is not found.\n\t\t// See if the parameter container has defined a property of this name\n\t\tint n = pc->FindPropertyIndex(tag.Name());\n\t\tif (n >= 0)\n\t\t{\n\t\t\tFEProperty* prop = pc->PropertyClass(n);\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\n\t\t\tif (sztype)\n\t\t\t{\n\t\t\t\t// try to allocate the class\n\t\t\t\tFECoreBase* pp = fecore_new<FECoreBase>(prop->GetSuperClassID(), sztype, pc->GetFEModel());\n\t\t\t\tif (pp == nullptr) throw XMLReader::InvalidAttributeValue(tag, \"type\", sztype);\n\n\t\t\t\tprop->SetProperty(pp);\n\n\t\t\t\t// read the property data\n\t\t\t\treadParameterList(tag, pp);\n\t\t\t}\n\t\t\telse if (prop->Flags() & FEProperty::Fixed)\n\t\t\t{\n\t\t\t\tif (prop->IsArray())\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pc = prop->get(prop->size());\n\t\t\t\t\treadParameterList(tag, pc);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pc = prop->get(0);\n\t\t\t\t\treadParameterList(tag, pc);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tthrow XMLReader::MissingAttribute(tag, \"type\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid readClassVariable(XMLTag& tag, FEClassDescriptor::ClassVariable* vars)\n{\n\tif (tag.isleaf()) return;\n\n\t++tag;\n\tdo {\n\t\tif (tag.isleaf())\n\t\t{\n\t\t\tconst char* szname = tag.Name();\n\n\t\t\t// see if the type attribute is defined\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\", true);\n\t\t\tif (sztype)\n\t\t\t{\n\t\t\t\tFEClassDescriptor::ClassVariable* child = new FEClassDescriptor::ClassVariable(szname, sztype);\n\t\t\t\tvars->AddVariable(child);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconst char* szval = tag.szvalue();\n\t\t\t\tFEClassDescriptor::SimpleVariable* var = new FEClassDescriptor::SimpleVariable(szname, szval);\n\t\t\t\tvars->AddVariable(var);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char* szname = tag.Name();\n\t\t\tconst char* sztype = tag.AttributeValue(\"type\");\n\n\t\t\tFEClassDescriptor::ClassVariable* child = new FEClassDescriptor::ClassVariable(szname, sztype);\n\t\t\tvars->AddVariable(child);\n\t\t\treadClassVariable(tag, child);\n\t\t}\n\t\t++tag;\n\t}\n\twhile (!tag.isend());\n}\n\n// create a class descriptor from the current tag\nFEClassDescriptor* fexml::readParameterList(XMLTag& tag)\n{\n\tconst char* sztype = tag.AttributeValue(\"type\");\n\tFEClassDescriptor* cd = new FEClassDescriptor(sztype);\n\n\tFEClassDescriptor::ClassVariable* root = cd->Root();\n\treadClassVariable(tag, root);\n\n\treturn cd;\n}\n"
  },
  {
    "path": "FEBioXML/xmltool.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FECoreBase.h>\n#include <FECore/ClassDescriptor.h>\n#include \"XMLReader.h\"\n#include \"febioxml_api.h\"\n\n//This namespace defines some helper functions that facilitate processing the FEBio xml formatted files.\nnamespace fexml\n{\n//---------------------------------------------------------------------------------------\n// Reads the value of a parameter.\n// if paramName is zero, the tag's name will be used as the parameter name.\nbool FEBIOXML_API readParameter(XMLTag& tag, FEParameterList& paramList, const char* paramName = 0, FECoreBase* parent = nullptr);\n\n//---------------------------------------------------------------------------------------\nbool FEBIOXML_API readParameter(XMLTag& tag, FECoreBase* pc, const char* szparam = nullptr);\n\n//---------------------------------------------------------------------------------------\n// reads the parameters and properties of a FECore class\nbool FEBIOXML_API readParameterList(XMLTag& tag, FECoreBase* pc);\n\n// read parameters listed as attributes in the tag\nbool FEBIOXML_API readAttributeParams(XMLTag& tag, FECoreBase* pc);\n\n//---------------------------------------------------------------------------------------\n// read a list of integers\nvoid FEBIOXML_API readList(XMLTag& tag, vector<int>& l);\n\n//---------------------------------------------------------------------------------------\n// create a class descriptor from the current tag\nFEBIOXML_API FEClassDescriptor* readParameterList(XMLTag& tag);\n\n}\n"
  },
  {
    "path": "FECore/AkimaSpline.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n\n#include \"AkimaSpline.h\"\n#include <limits>\n\n//--------------------------------------------------------------------------------\nstruct AkimaSpline::Impl\n{\n    int                 ncoef;    //! number of B-spline coefficients\n    std::vector<double> xknot;    //! knot sequence\n    std::vector<double> acoef;    //! Akima spline coefficient a\n    std::vector<double> bcoef;    //! Akima spline coefficient b\n    std::vector<double> ccoef;    //! Akima spline coefficient c\n    std::vector<double> dcoef;    //! Akima spline coefficient d\n};\n\n//--------------------------------------------------------------------------------\nAkimaSpline::AkimaSpline() : im(new AkimaSpline::Impl)\n{\n    im->ncoef = 0;\n}\n\n//--------------------------------------------------------------------------------\n// destructor\nAkimaSpline::~AkimaSpline()\n{\n    delete im;\n    im = nullptr;\n}\n\n//--------------------------------------------------------------------------------\n// copy constructor\nAkimaSpline::AkimaSpline(const AkimaSpline& as) : im(new AkimaSpline::Impl)\n{\n    im->ncoef  = as.im->ncoef;\n    im->xknot  = as.im->xknot;\n    im->acoef  = as.im->acoef;\n    im->bcoef  = as.im->bcoef;\n    im->ccoef  = as.im->ccoef;\n    im->dcoef  = as.im->dcoef;\n}\n\n//--------------------------------------------------------------------------------\nvoid AkimaSpline::operator = (const AkimaSpline& as)\n{\n    im->ncoef  = as.im->ncoef;\n    im->xknot  = as.im->xknot;\n    im->acoef  = as.im->acoef;\n    im->bcoef  = as.im->bcoef;\n    im->ccoef  = as.im->ccoef;\n    im->dcoef  = as.im->dcoef;\n}\n\n//--------------------------------------------------------------------------------\n// initialize B-spline, using p as control points\nbool AkimaSpline::init(const std::vector<vec2d>& p)\n{\n    int ncoef = (int)p.size();\n\tif (ncoef < 2) return false;\n\n    im->ncoef = ncoef;\n    im->xknot.resize(ncoef);\n    im->acoef.resize(ncoef-1);\n    im->bcoef.resize(ncoef-1);\n    im->ccoef.resize(ncoef-1);\n    im->dcoef.resize(ncoef-1);\n\n    // extract m sequence\n    std::vector<double> m(ncoef-1);\n    for (int i=0; i< ncoef-1; ++i)\n        m[i] = (p[i+1].y()-p[i].y())/(p[i+1].x()-p[i].x());\n    \n    // extract slopes s\n    const double eps = 10*std::numeric_limits<double>::epsilon();\n    std::vector<double> s(ncoef);\n    s[0] = m[0];\n    s[1] = (m[0]+m[1])/2;\n    if (ncoef > 2) {\n        s[ncoef-2] = (m[ncoef-3]+m[ncoef-2])/2;\n        s[ncoef-1] = m[ncoef-2];\n        for (int i=2; i<ncoef-2; ++i) {\n            double d = fabs(m[i+1]-m[i]) + fabs(m[i-1]-m[i-2]);\n            s[i] = (fabs(d) > eps) ? (fabs(m[i+1]-m[i])*m[i-1]+fabs(m[i-1]-m[i-2])*m[i])/d : (m[i-1]+m[i])/2;\n        }\n    }\n\n    // evaluate knots and coefficients\n    if (ncoef == 2) {\n        double dx = p[1].x()-p[0].x();\n        if (fabs(dx) <= eps) return false;\n        im->xknot[0] = p[0].x();\n        im->xknot[1] = p[1].x();\n        im->acoef[0] = p[0].y();\n        im->bcoef[0] = s[0];\n        im->ccoef[0] = 0;\n        im->dcoef[0] = 0;\n    }\n    else {\n        for (int i=0; i<ncoef-1; ++i) {\n            double dx = p[i+1].x()-p[i].x();\n            if (fabs(dx) <= eps) return false;\n            im->xknot[i] = p[i].x();\n            im->acoef[i] = p[i].y();\n            im->bcoef[i] = s[i];\n            im->ccoef[i] = (3*m[i] - 2*s[i] - s[i+1])/dx;\n            im->dcoef[i] = (s[i] + s[i+1] - 2*m[i])/(dx*dx);\n        }\n        im->xknot[ncoef-1] = p[ncoef-1].x();\n    }\n    \n    return true;\n}\n\n//--------------------------------------------------------------------------------\n// evaluate Akima spline at x\ndouble AkimaSpline::eval(double x) const\n{\n    // perform binary search to locate knot interval that encloses x\n    int j = 0, jh = im->ncoef-1;\n    while (jh - j > 1) {\n        int jm = (j+jh)/2;\n        if ((im->xknot[j] <= x) && (x < im->xknot[jm]))\n            jh = jm;\n        else\n            j = jm;\n    }\n    \n    double dx = x - im->xknot[j];\n    double y = im->acoef[j] + dx*(im->bcoef[j] + dx*(im->ccoef[j] + dx*im->dcoef[j]));\n    \n    return y;\n}\n\n//--------------------------------------------------------------------------------\ndouble AkimaSpline::eval_deriv(double x) const\n{\n    // perform binary search to locate knot interval that encloses x\n    int j = 0, jh = im->ncoef-1;\n    while (jh - j > 1) {\n        int jm = (j+jh)/2;\n        if ((im->xknot[j] <= x) && (x < im->xknot[jm]))\n            jh = jm;\n        else\n            j = jm;\n    }\n    \n    double dx = x - im->xknot[j];\n    double dy = im->bcoef[j] + dx*(2*im->ccoef[j] + 3*dx*im->dcoef[j]);\n    \n    return dy;\n}\n\n//--------------------------------------------------------------------------------\ndouble AkimaSpline::eval_deriv2(double x) const\n{\n    // perform binary search to locate knot interval that encloses x\n    int j = 0, jh = im->ncoef-1;\n    while (jh - j > 1) {\n        int jm = (j+jh)/2;\n        if ((im->xknot[j] <= x) && (x < im->xknot[jm]))\n            jh = jm;\n        else\n            j = jm;\n    }\n    \n    double dx = x - im->xknot[j];\n    double d2y = 2*(im->ccoef[j] + 3*dx*im->dcoef[j]);\n    \n    return d2y;\n}\n"
  },
  {
    "path": "FECore/AkimaSpline.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2023 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"vec2d.h\"\n#include <vector>\n\nclass FECORE_API AkimaSpline\n{\n    struct Impl;\n\npublic:\n    // constructor\n    AkimaSpline();\n\n    // copy constructor\n    AkimaSpline(const AkimaSpline& as);\n\n    // destructor\n    ~AkimaSpline();\n\n    // assignment operator\n    void operator = (const AkimaSpline& as);\n\npublic:\n    // Akima spline function evaluations\n    double eval(double x) const;\n    double eval_deriv(double x) const;\n    double eval_deriv2(double x) const;\n    \npublic:\n    // initializations\n    // use given points p as control points\n    bool init(const std::vector<vec2d>& p);\n\nprivate:\n    Impl* im;\n};\n"
  },
  {
    "path": "FECore/BFGSSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"BFGSSolver.h\"\n#include \"FESolver.h\"\n#include \"FEException.h\"\n#include \"FENewtonSolver.h\"\n#include \"log.h\"\n\n//-----------------------------------------------------------------------------\n// BFGSSolver\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(BFGSSolver, FENewtonStrategy)\n\tADD_PARAMETER(m_maxups, \"max_ups\");\n\tADD_PARAMETER(m_max_buf_size, FE_RANGE_GREATER_OR_EQUAL(0), \"max_buffer_size\"); \n\tADD_PARAMETER(m_cycle_buffer, \"cycle_buffer\");\n\tADD_PARAMETER(m_cmax, FE_RANGE_GREATER_OR_EQUAL(0.0), \"cmax\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBFGSSolver::BFGSSolver(FEModel* fem) : FENewtonStrategy(fem)\n{\n\tm_neq = 0;\n\n\t// pointer to linear solver\n\tm_plinsolve = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Initialization method\nbool BFGSSolver::Init()\n{\n\tif (m_pns == nullptr) return false;\n\n\tif (m_max_buf_size <= 0) m_max_buf_size = m_maxups;\n\n\tint neq = m_pns->m_neq;\n\n\t// allocate storage for BFGS update vectors\n\tm_V.resize(m_max_buf_size, neq);\n\tm_W.resize(m_max_buf_size, neq);\n\n\tm_D.resize(neq);\n\tm_G.resize(neq);\n\tm_H.resize(neq);\n\n\tm_neq = neq;\n\tm_nups = 0;\n\n\tm_plinsolve = m_pns->GetLinearSolver();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function performs a BFGS stiffness update.\n//! The last line search step is input to this function.\n//! This function performs the update assuming the stiffness matrix\n//! is positive definite. In the case that this is not the case\n//! the function returns false. The function also returns false if \n//! the condition number is too big. A too big condition number might\n//! be an indication of an ill-conditioned matrix and the update should\n//! not be performed.\n\nbool BFGSSolver::Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1)\n{\n\t// for full-Newton, we skip QN update\n\tif (m_maxups == 0) return false;\n\n\t// make sure we didn't reach max updates\n\tif (m_nups >= m_maxups - 1)\n\t{\n\t\tfeLogWarning(\"Max nr of iterations reached.\\nStiffness matrix will now be reformed.\");\n\t\treturn false;\n\t}\n\n\t// calculate the BFGS update vectors\n\tint neq = m_neq;\n\tfor (int i = 0; i<neq; ++i)\n\t{\n\t\tm_D[i] = s*ui[i];\n\t\tm_G[i] = R0[i] - R1[i];\n\t\tm_H[i] = R0[i]*s;\n\t}\n\n\tdouble dg = m_D*m_G;\n\tdouble dh = m_D*m_H;\n\tdouble dgi = 1.0 / dg;\n\tdouble r = dg / dh;\n\n\t// check to see if this is still a pos definite update\n//\tif (r <= 0) \n//\t{\n//\t\tfeLogWarning(\"The QN update has failed.\\nStiffness matrix will now be reformed.\");\n//\t\treturn false;\n//\t}\n\n\t// calculate the condition number\n//\tc = sqrt(r);\n\tdouble c = sqrt(fabs(r));\n\n\t// make sure c is less than the the maximum.\n\tif (c > m_cmax) return false;\n\n\t// get the correct buffer to fill\n\tint n = (m_nups % m_max_buf_size);\n\n\t// do the update only when allowed\n\tif ((m_nups < m_max_buf_size) || (m_cycle_buffer == true))\n\t{\n\t\tdouble* vn = m_V[n];\n\t\tdouble* wn = m_W[n];\n\n\t\tfor (int i=0; i<neq; ++i)\t\n\t\t{\n\t\t\tvn[i] = -m_H[i]*c - m_G[i];\n\t\t\twn[i] = m_D[i]*dgi;\n\t\t}\n\t}\n\n\t// increment update counter\n\t++m_nups;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// This function solves a system of equations using the BFGS update vectors\n// The variable m_nups keeps track of how many updates have been made so far.\n// \n\nvoid BFGSSolver::SolveEquations(vector<double>& x, vector<double>& b)\n{\n\t// make sure we need to do work\n\tif (m_neq ==0) return;\n\n\t// create temporary storage\n\ttmp = b;\n\n\t// number of updates can be larger than buffer size, so clamp it\n\tint nups = (m_nups> m_max_buf_size ? m_max_buf_size : m_nups);\n\n\t// get the \"0\" buffer index\n\tint n0 = 0;\n\tif ((m_nups > m_max_buf_size) && (m_cycle_buffer == true))\n\t{\n\t\tn0 = m_nups % m_max_buf_size;\n\t}\n\n\t// loop over all update vectors\n\tfor (int i=nups-1; i>=0; --i)\n\t{\n\t\tint n = (n0 + i) % m_max_buf_size;\n\n\t\tdouble* vi = m_V[n];\n\t\tdouble* wi = m_W[n];\n\n\t\tdouble wr = 0;\n\t\tfor (int j = 0; j<m_neq; j++) wr += wi[j] * tmp[j];\n\n\t\tfor (int j = 0; j<m_neq; j++) tmp[j] += vi[j] * wr;\n\t}\n\n\t// perform a backsubstitution\n\tif (m_plinsolve->BackSolve(x, tmp) == false)\n\t{\n\t\tthrow LinearSolverFailed();\n\t}\n\n\t// loop again over all update vectors\n\tfor (int i = 0; i<nups; ++i)\n\t{\n\t\tint n = (n0 + i) % m_max_buf_size;\n\n\t\tdouble* vi = m_V[n];\n\t\tdouble* wi = m_W[n];\n\n\t\tdouble vr = 0;\n\t\tfor (int j = 0; j<m_neq; ++j) vr += vi[j] * x[j];\n\n\t\tfor (int j = 0; j<m_neq; ++j) x[j] += wi[j] * vr;\n\t}\n}\n"
  },
  {
    "path": "FECore/BFGSSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"matrix.h\"\n#include \"vector.h\"\n#include \"LinearSolver.h\"\n#include \"FENewtonStrategy.h\"\n\n//-----------------------------------------------------------------------------\n//! The BFGSSolver solves a nonlinear system of equations using the BFGS method.\n//! It depends on the NonLinearSystem to evaluate the function and its jacobian.\n\nclass FECORE_API BFGSSolver : public FENewtonStrategy\n{\npublic:\n\t//! constructor\n\tBFGSSolver(FEModel* fem);\n\n\t//! New initialization method\n\tbool Init() override;\n\n\t//! perform a BFGS udpate\n\tbool Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1) override;\n\n\t//! solve the equations\n\tvoid SolveEquations(vector<double>& x, vector<double>& b) override;\n\npublic:\n\t// keep a pointer to the linear solver\n\tLinearSolver*\tm_plinsolve;\t//!< pointer to linear solver\n\tint\t\t\t\tm_neq;\t\t//!< number of equations\n\n\t// BFGS update vectors\n\tmatrix\t\t\tm_V;\t\t//!< BFGS update vector\n\tmatrix\t\t\tm_W;\t\t//!< BFGS update vector\n\tvector<double>\tm_D, m_G, m_H;\t//!< temp vectors for calculating BFGS update vectors\n\n\tvector<double>\ttmp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/BSpline.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n\n#include \"BSpline.h\"\n#include \"matrix.h\"\n#include <limits>\n\n//--------------------------------------------------------------------------------\nstruct BSpline::Impl \n{\n    int                 korder;   //! B-spline order\n    int                 ncoef;    //! number of B-spline coefficients\n    std::vector<double> xknot;    //! knot sequence\n    std::vector<double> coeff;    //! B-spline coefficients\n};\n\n//--------------------------------------------------------------------------------\nBSpline::BSpline() : im(new BSpline::Impl)\n{\n    im->korder = 0;\n    im->ncoef = 0;\n    m_deriv = false;\n}\n\n//--------------------------------------------------------------------------------\n// destructor\nBSpline::~BSpline()\n{\n    delete im;\n    im = nullptr;\n}\n\n//--------------------------------------------------------------------------------\n// copy constructor\nBSpline::BSpline(const BSpline& bs) : im(new BSpline::Impl)\n{\n    im->korder = bs.im->korder;\n    im->ncoef  = bs.im->ncoef;\n    im->xknot  = bs.im->xknot;\n    im->coeff  = bs.im->coeff;\n}\n\n//--------------------------------------------------------------------------------\nvoid BSpline::operator = (const BSpline& bs)\n{\n    im->korder = bs.im->korder;\n    im->ncoef  = bs.im->ncoef;\n    im->xknot  = bs.im->xknot;\n    im->coeff  = bs.im->coeff;\n}\n\n//--------------------------------------------------------------------------------\n// initialize B-spline, using p as control points\nbool BSpline::init(int korder, const std::vector<vec2d>& p)\n{\n    int ncoef = (int)p.size();\n\tif (ncoef < 2) return false;\n\n    im->korder = korder;\n    im->ncoef = ncoef;\n    if (ncoef < korder) return false;\n    im->coeff.resize(ncoef);\n    \n    // extract breakpoint sequence and spline coefficients\n    std::vector<double> bp(ncoef);\n    for (int i=0; i< ncoef; ++i) {\n        bp[i] = p[i].x();\n        im->coeff[i] = p[i].y();\n    }\n    \n    // evaluate knot sequence from breakpoint sequence\n    im->xknot.resize(korder + ncoef);\n    int khalf;\n    const double eps = 10*std::numeric_limits<double>::epsilon();\n    double e = eps*(bp[ncoef-1] - bp[ncoef-2]);\n    \n    if ((korder % 2) == 0) {\n        khalf = korder/2;\n        for (int i=0; i< korder; ++i)\n            im->xknot[i] = bp.front();\n        for (int i= korder; i< ncoef; ++i)\n            im->xknot[i] = bp[i-khalf];\n        for (int i=ncoef; i<ncoef+korder; ++i)\n            im->xknot[i]  = bp.back() + e;\n    }\n    else {\n        khalf = (korder-1)/2;\n        for (int i=0; i<korder; ++i)\n            im->xknot[i] = bp.front();\n        for (int i=korder; i<ncoef; ++i)\n            im->xknot[i] = (bp[i-khalf] + bp[i-1-khalf])/2;\n        for (int i=ncoef; i<ncoef+korder; ++i)\n            im->xknot[i]  = bp.back() + e;\n    }\n\n    return true;\n}\n\n//--------------------------------------------------------------------------------\n// evaluate B-spline at x using de Boor algorithm (de Boor 1986)\ndouble BSpline::eval(double x, int korder, const std::vector<double>& xknot,\n            int ncoef, const std::vector<double>& coeff) const\n{\n    // perform binary search to locate knot interval that encloses x\n    int j = korder-1, jh = ncoef;\n    while (jh - j > 1) {\n        int jm = (j+jh)/2;\n        if ((xknot[j] <= x) && (x < xknot[jm]))\n            jh = jm;\n        else\n            j = jm;\n    }\n    \n    std::vector<double> c = coeff;\n    double w;\n    for (int r=0; r<korder-1; ++r) {\n        for (int i=j; i>j-korder+r+1; --i) {\n            if (xknot[i] != xknot[i+korder-r-1])\n                w = (x - xknot[i])/(xknot[i+korder-r-1] - xknot[i]);\n            else\n                w = 0;\n            c[i] = (1-w)*c[i-1] + w*c[i];\n        }\n    }\n    return c[j];\n}\n\n//--------------------------------------------------------------------------------\n// evaluate B-spline at x using de Boor algorithm (de Boor 1986)\ndouble BSpline::eval(double x) const\n{\n    return eval(x, im->korder, im->xknot, im->ncoef, im->coeff);\n}\n\n//--------------------------------------------------------------------------------\ndouble BSpline::eval_deriv(double x) const {\n    if (m_deriv) return eval(x);\n    else return eval_nderiv(x, 1);\n}\n\n//--------------------------------------------------------------------------------\ndouble BSpline::eval_deriv2(double x) const {\n    if (m_deriv) return eval_nderiv(x, 1);\n    else return eval_nderiv(x, 2);\n}\n\n//--------------------------------------------------------------------------------\n// evaluate B-spline n-th derivative at x using de Boor algorithm (de Boor 1986)\ndouble BSpline::eval_nderiv(double x, int n) const\n{\n    double deriv = 0;\n\n    int korder = im->korder;\n    int ncoef = im->ncoef;\n    if (n >= korder) return deriv;\n    \n    std::vector<double> coeff = im->coeff;\n\n    for (int k=1; k<=n; ++k) {\n        for (int i= im->ncoef-1; i>=k; --i) {\n            if (im->xknot[i+korder-k] > im->xknot[i]) {\n                coeff[i] = (korder - k)*(coeff[i] - coeff[i-1])/\n                (im->xknot[i+korder-k] - im->xknot[i]);\n            }\n        }\n    }\n\n    // extract portion of vectors\n    std::vector<double> xknot(im->xknot.begin() + n, im->xknot.end());\n    std::vector<double> doeff(coeff.begin() + n,coeff.end());\n\n    deriv = eval(x,korder-n, xknot, ncoef-n, doeff);\n    \n    return deriv;\n}\n\n//--------------------------------------------------------------------------------\n// evaluate B-spline blending functions at x\nstd::vector<double> BSpline::blending_functions(double x) const\n{\n    int korder = im->korder;\n    int ncoef  = im->ncoef;\n\n    std::vector<double> bsbldg(ncoef);\n    std::vector<std::vector<double> > d(ncoef, std::vector<double>(korder));\n    \n    for (int i=0; i<ncoef; ++i) {\n        if ((im->xknot[i] <= x) && (x < im->xknot[i+1]))\n            d[i][0] = 1;\n        else\n            d[i][0] = 0;\n    }\n\n    double r1, r2;\n    for (int k=2; k<=korder; ++k) {\n        for (int i=0; i<ncoef; ++i) {\n            if (im->xknot[i+k-1] == im->xknot[i])\n                r1 = 0;\n            else\n                r1 = (x- im->xknot[i])*d[i][k-2]/(im->xknot[i+k-1]-im->xknot[i]);\n            if (im->xknot[i+k] == im->xknot[i+1])\n                r2 = 0;\n            else\n                r2 = (im->xknot[i+k]-x)*d[i+1][k-2]/(im->xknot[i+k]- im->xknot[i+1]);\n            d[i][k-1] = r1 + r2;\n        }\n    }\n    \n    for (int i=0; i<ncoef; ++i)\n        bsbldg[i] = d[i][korder-1];\n    \n    return bsbldg;\n}\n\n//--------------------------------------------------------------------------------\n// fit a B-spline of order korder, with ncoef coefficients, to the points p\nbool BSpline::fit(int korder, int ncoef, const std::vector<vec2d>& p)\n{\n    // check for valid spline order\n    if (korder <1) return false;\n    \n    // number of points to fit\n    int np = (int)p.size();\n    if (np < korder) return false;\n    \n    // for an interpolation, use p.x as breakpoints to generate knot sequence\n    bool binit = true;\n    if (ncoef == np) binit = init(korder, p);\n    // otherwise, generate breakpoints uniformly over range of x\n    else {\n        std::vector<vec2d> q(ncoef,vec2d(0, 0));\n        double dx = (p[np-1].x() - p[0].x())/(ncoef-1);\n        for (int i=0; i<ncoef; ++i)\n            q[i].x() = p[0].x() + i*dx;\n        binit = init(korder, q);\n    }\n    if (binit == false) return binit;\n    \n    // evaluate B-spline blending functions at p.x using this knot sequence\n    matrix wk1(np,ncoef);\n    for (int j=0; j<np; ++j) {\n        std::vector<double> bsbldg = blending_functions(p[j].x());\n        for (int i=0; i<ncoef; ++i) wk1(j,i) = bsbldg[i];\n    }\n    \n    // evaluate the coefficient matrix\n    matrix wk2 = wk1.transpose()*wk1;\n    \n    // evaluate the right-hand-side\n    std::vector<double> rhs(ncoef,0);\n    for (int k=0; k<ncoef; ++k)\n        for (int j=0; j<np; ++j)\n            rhs[k] += p[j].y()*wk1(j,k);\n\n    // solve the system of equations\n    wk2.solve(im->coeff, rhs);\n    \n    return true;\n}\n\n//--------------------------------------------------------------------------------\n// use given points as interpolation points\nbool BSpline::init_interpolation(int korder, const std::vector<vec2d>& p)\n{\n    int ncoef = (int)p.size();\n    return fit(korder, ncoef, p);\n}\n\n//--------------------------------------------------------------------------------\n// perform spline approximation over points p, using ncoef coefficients\nbool BSpline::init_approximation(int korder, int ncoef, const std::vector<vec2d>& p)\n{\n    return fit(korder, ncoef, p);\n}\n"
  },
  {
    "path": "FECore/BSpline.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"vec2d.h\"\n#include <vector>\n\nclass FECORE_API BSpline\n{\n    struct Impl;\n\npublic:\n    // constructor\n    BSpline();\n\n    // copy constructor\n    BSpline(const BSpline& bs);\n\n    // destructor\n    ~BSpline();\n\n    // assignment operator\n    void operator = (const BSpline& bs);\n\npublic:\n    // spline function evaluations\n    double eval(double x, int korder, const std::vector<double>& xknot,\n                int ncoef, const std::vector<double>& coeff) const;\n    double eval(double x) const;\n    double eval_nderiv(double x, int n) const;\n    double eval_deriv(double x) const;\n    double eval_deriv2(double x) const;\n    \npublic:\n    // spline fitting\n    std::vector<double> blending_functions(double x) const;\n    bool fit(int korder, int ncoef, const std::vector<vec2d>& p);\n    \npublic:\n    // initializations\n    // use given points p as control points\n    bool init(int korder, const std::vector<vec2d>& p);\n    // use given points p as interpolation points\n    bool init_interpolation(int korder, const std::vector<vec2d>& p);\n    // perform spline approximation over points p, using ncoef coefficients\n    bool init_approximation(int korder, int ncoef, const std::vector<vec2d>& p);\n\n    bool    m_deriv;    //!< flag indicating derivative status\n\nprivate:\n    Impl* im;\n};\n"
  },
  {
    "path": "FECore/CSRMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"CSRMatrix.h\"\n#include <assert.h>\n\nCSRMatrix::CSRMatrix() : m_nr(0), m_nc(0), m_offset(0)\n{\n}\n\n// create a matrix of given size\nCSRMatrix::CSRMatrix(int rows, int cols, int noffset) : m_nr(rows), m_nc(cols), m_offset(noffset)\n{\n\tm_rowIndex.resize(rows+1, m_offset);\n}\n\n// Create matrix\nvoid CSRMatrix::create(int nr, int nc, int noffset)\n{\n\tm_nr = nr;\n\tm_nc = nc;\n\tm_offset = noffset;\n\tm_rowIndex.resize(nr+1, noffset);\n\tm_columns.clear();\n\tm_values.clear();\n}\n\n// copy constructor\nCSRMatrix::CSRMatrix(const CSRMatrix& A)\n{\n\tm_nr = A.m_nr;\n\tm_nc = A.m_nc;\n\tm_offset = A.m_offset;\n\tm_rowIndex = A.m_rowIndex;\n\tm_columns = A.m_columns;\n\tm_values = A.m_values;\n}\n\n// assignment operator\nvoid CSRMatrix::operator = (const CSRMatrix& A)\n{\n\tm_nr = A.m_nr;\n\tm_nc = A.m_nc;\n\tm_offset = A.m_offset;\n\tm_rowIndex = A.m_rowIndex;\n\tm_columns = A.m_columns;\n\tm_values = A.m_values;\n}\n\n// sets a value, inserting it if necessary\nvoid CSRMatrix::set(int i, int j, double val)\n{\n\tassert((i >= 0) && (i < m_nr));\n\tassert((j >= 0) && (j < m_nc));\n\n\t// get the start column index for the given row and the non-zero count for that row\n\tint col = m_rowIndex[i] - m_offset;\n\tint count = m_rowIndex[i+1] - m_rowIndex[i];\n\n\t// see if the row is empty\n\tif (count == 0)\n\t{\n\t\tm_columns.insert(m_columns.begin() + col, j);\n\t\tm_values.insert(m_values.begin() + col, val);\n\t}\n\telse\n\t{\n\t\t// see if this column would be before the first entry\n\t\tif (j < m_columns[col] - m_offset)\n\t\t{\n\t\t\tm_columns.insert(m_columns.begin() + col, j);\n\t\t\tm_values.insert(m_values.begin() + col, val);\n\t\t}\n\t\t// see if this column would be past the last entry\n\t\telse if (j > m_columns[col + count - 1] - m_offset)\n\t\t{\n\t\t\tm_columns.insert(m_columns.begin() + col + count, j);\n\t\t\tm_values.insert(m_values.begin() + col + count, val);\n\t\t}\n\t\telse {\n\t\t\t// find the column index\n\t\t\tfor (int n=0; n<count; ++n)\n\t\t\t{\n\t\t\t\t// see if it alreay exists\n\t\t\t\tif (m_columns[col + n] - m_offset == j)\n\t\t\t\t{\n\t\t\t\t\t// if so, replace the value and return\n\t\t\t\t\tm_values[col+n] = val;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse if (m_columns[col + n] - m_offset > j)\n\t\t\t\t{\n\t\t\t\t\t// we found an index that is bigger so insert this value before this one\n\t\t\t\t\tm_columns.insert(m_columns.begin() + col + n, j);\n\t\t\t\t\tm_values.insert(m_values.begin() + col + n, val);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// increase row counts\n\tfor (int n = i + 1; n <= m_nr; ++n) m_rowIndex[n]++;\n\tassert(m_rowIndex[m_nr] == m_values.size());\n\tassert(m_columns.size() == m_values.size());\n}\n\n// get a value\ndouble CSRMatrix::operator () (int i, int j) const\n{\n\tassert((i >= 0) && (i < m_nr));\n\tassert((j >= 0) && (j < m_nc));\n\n\tint col = m_rowIndex[i] - m_offset;\n\tint count = m_rowIndex[i + 1] - m_rowIndex[i];\n\n\tif (count == 0) return 0.0;\n\tif (j < m_columns[col] - m_offset) return 0.0;\n\tif (j > m_columns[col + count - 1] - m_offset) return 0.0;\n\tfor (int n=0; n<count; ++n)\n\t{\n\t\tif (m_columns[col + n] - m_offset == j) return m_values[col + n];\n\t}\n\treturn 0.0;\n}\n\n// see if a matrix entry was allocated\nbool CSRMatrix::isAlloc(int i, int j) const\n{\n\tassert((i >= 0) && (i < m_nr));\n\tassert((j >= 0) && (j < m_nc));\n\n\tint col = m_rowIndex[i] - m_offset;\n\tint count = m_rowIndex[i + 1] - m_rowIndex[i];\n\n\tif (count == 0) return false;\n\tif (j < m_columns[col] - m_offset) return false;\n\tif (j > m_columns[col + count - 1] - m_offset) return false;\n\tfor (int n = 0; n<count; ++n)\n\t{\n\t\tif (m_columns[col + n] - m_offset == j) return true;\n\t}\n\treturn false;\n}\n\nvoid CSRMatrix::multv(const std::vector<double>& x, std::vector<double>& r)\n{\n\tmultv(&x[0], &r[0]);\n}\n\nvoid CSRMatrix::multv(const double* x, double* r)\n{\n\tconst int nr = rows();\n\tconst int nc = cols();\n\n\t// loop over all rows\n\tfor (int i = 0; i<nr; ++i)\n\t{\n\t\tint col = m_rowIndex[i] - m_offset;\n\t\tint count = m_rowIndex[i + 1] - m_rowIndex[i];\n\n\t\tdouble* pv = &m_values[col];\n\t\tint* pi = &m_columns[col];\n\t\tdouble ri = 0.0;\n\t\tfor (int j = 0; j<count; ++j) ri += pv[j] * x[pi[j] - m_offset];\n\t\tr[i] = ri;\n\t}\n}\n"
  },
  {
    "path": "FECore/CSRMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n\n// This class represents a sparse matrix in the row-compressed format (3-array format)\nclass FECORE_API CSRMatrix\n{\npublic:\n\t// default constructor\n\tCSRMatrix();\n\n\t// create a matrix of given size\n\tCSRMatrix(int rows, int cols, int noffset = 0);\n\n\t// copy constructor\n\tCSRMatrix(const CSRMatrix& A);\n\n\t// Create matrix\n\tvoid create(int nr, int nc, int noffset = 0);\n\n\t// assignment operator\n\tvoid operator = (const CSRMatrix& A);\n\n\t// return row count\n\tint rows() const { return m_nr; }\n\n\t// return columns count\n\tint cols() const { return m_nc; }\n\n\t// return number of nonzeroes\n\tint nonzeroes() const { return (int) m_values.size(); }\n\n\t// set the value, inserting it if necessary\n\tvoid set(int i, int j, double val);\n\n\t// get a value\n\tdouble operator () (int i, int j) const;\n\n\t// see if a matrix entry was allocated\n\tbool isAlloc(int i, int j) const;\n\npublic:\n\t// matrix-vector multiplication: A.x = r\n\tvoid multv(const std::vector<double>& x, std::vector<double>& r);\n\tvoid multv(const double* x, double* r);\n\npublic:\n\tstd::vector<double>& values() { return m_values; }\n\tstd::vector<int>& indices() { return m_columns; }\n\tstd::vector<int>& pointers() { return m_rowIndex; }\n\nprivate:\n\tint\t\tm_nr;\t\t// number of rows\n\tint\t\tm_nc;\t\t// number of columns\n\tint\t\tm_offset;\t// offset (0 or 1)\n\tstd::vector<int>\tm_rowIndex;\t\t// start of row in columns array\n\tstd::vector<int>\tm_columns;\t\t// columns of non-zero entries\n\tstd::vector<double>\tm_values;\t\t// values of matrix\n};\n"
  },
  {
    "path": "FECore/Callback.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"Callback.h\"\n\n//-----------------------------------------------------------------------------\nCallbackHandler::CallbackHandler()\n{\n\tm_event = 0;\n}\n\n//-----------------------------------------------------------------------------\nCallbackHandler::~CallbackHandler()\n{\n}\n\n//-----------------------------------------------------------------------------\n//! This function adds a callback routine\n//\nvoid CallbackHandler::AddCallback(FECORE_CB_FNC pcb, unsigned int nwhen, void *pd, CBInsertPolicy insert)\n{\n\tFECORE_CALLBACK cb;\n\tcb.m_pcb = pcb;\n\tcb.m_pd = pd;\n\tcb.m_nwhen = nwhen;\n\n\tif (insert == CBInsertPolicy::CB_ADD_END)\n\t\tm_pcb.push_back(cb);\n\telse\n\t\tm_pcb.push_front(cb);\n}\n\n//-----------------------------------------------------------------------------\n//! Call the callback functions.\n//\nbool CallbackHandler::DoCallback(FEModel* fem, unsigned int nevent)\n{\n\tm_event = nevent;\n\tstd::list<FECORE_CALLBACK>::iterator it = m_pcb.begin();\n\tfor (int i = 0; i<(int)m_pcb.size(); ++i, ++it)\n\t{\n\t\t// call the callback function\n\t\tif (it->m_nwhen & nevent)\n\t\t{\n\t\t\tbool bret = (it->m_pcb)(fem, nevent, it->m_pd);\n\t\t\tif (bret == false) return false;\n\t\t}\n\t}\n\tm_event = 0;\n\n\treturn true;\n}\n\n//! Get the current callback reason (or zero if not inside DoCallback)\nunsigned int CallbackHandler::CurrentEvent() const\n{\n\treturn m_event;\n}\n"
  },
  {
    "path": "FECore/Callback.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <list>\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\n// Forward declarations.\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// callback events\n#define CB_ALWAYS\t\t\t0x0FFFFFFF\t\t//!< Call for all reasons\n#define CB_INIT\t\t\t\t0x00000001\t\t//!< Call after model initialization (i.e. FEModel::Init())\n#define CB_STEP_ACTIVE\t\t0x00000002\t\t//!< call after step was activated (i.e. \n#define CB_MAJOR_ITERS\t\t0x00000004\t\t//!< Call at the end of each major converged iteration\n#define CB_MINOR_ITERS\t\t0x00000008\t\t//!< Call for each minor iteration\n#define CB_SOLVED\t\t\t0x00000010\t\t//!< Call at the end of FEModel::Solve\n#define CB_UPDATE_TIME\t\t0x00000020\t\t//!< Call when time is updated and right before time step is solved (in FEAnalysis::Solve)\n#define CB_AUGMENT\t\t\t0x00000040\t\t//!< The model is entering augmentations (called before Augment)\n#define CB_STEP_SOLVED\t\t0x00000080\t\t//!< The step was solved\n#define CB_MATRIX_REFORM\t0x00000100\t\t//!< stiffness matrix was reformed\n#define CB_REMESH\t\t\t0x00000200\t\t//!< Called after remesh\n#define CB_PRE_MATRIX_SOLVE\t0x00000400\t\t//!< Called right before matrix solve\n#define CB_RESET            0x00000800\t\t//!< Called after FEModel::Reset\n#define CB_MODEL_UPDATE\t\t0x00001000\t\t//!< Called at the end of FEModel::Update\n#define CB_TIMESTEP_SOLVED\t0x00002000\t\t//!< Called at FEAnalysis::SolveTimeStep after the solver returns.\n#define CB_SERIALIZE_SAVE\t0x00004000\t\t//!< Called at the end of FEModel::Serialize when saving\n#define CB_SERIALIZE_LOAD\t0x00008000\t\t//!< Called at the end of FEModel::Serialize when loading\n#define CB_TIMESTEP_FAILED\t0x00010000\t\t//!< Called when the time step failed\n#define CB_QUASIN_CONVERGED 0x00020000\t\t//!< Called when Quasin is done (but before it finishes up)\n#define CB_USER1\t\t\t0x01000000\t\t//!< can be used by users\n\ntypedef unsigned int FECORE_CB_WHEN;\ntypedef bool(*FECORE_CB_FNC)(FEModel*, unsigned int, void*);\n\n//-----------------------------------------------------------------------------\n// callback structure\nstruct FECORE_CALLBACK {\n\tFECORE_CB_FNC\tm_pcb;\t\t// pointer to callback function\n\tvoid*\t\t\tm_pd;\t\t// pointer to user data\n\tFECORE_CB_WHEN\tm_nwhen;\t// when to call function\n};\n\n//-----------------------------------------------------------------------------\n// class that handles callbacks\nclass FECORE_API CallbackHandler\n{\npublic:\n\tenum CBInsertPolicy\n\t{\n\t\tCB_ADD_FRONT,\n\t\tCB_ADD_END\n\t};\n\npublic:\n\tCallbackHandler();\n\tvirtual ~CallbackHandler();\n\n\t//! set callback function\n\tvoid AddCallback(FECORE_CB_FNC pcb, unsigned int nwhen, void* pd, CBInsertPolicy insert = CBInsertPolicy::CB_ADD_END);\n\n\t//! call the callback function\n\t//! This function returns false if the run is to be aborted\n\tbool DoCallback(FEModel* fem, unsigned int nevent);\n\n\t//! Get the current callback reason (or zero if not inside DoCallback)\n\tunsigned int CurrentEvent() const;\n\nprivate:\n\tstd::list<FECORE_CALLBACK>\tm_pcb;\t//!< pointer to callback function\n\tunsigned int\tm_event;\t\t\t//!< reason for current callback (or zero)\n};\n"
  },
  {
    "path": "FECore/ClassDescriptor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"ClassDescriptor.h\"\n\nFEClassDescriptor::SimpleVariable* FEClassDescriptor::FindParameter(const char* szparam)\n{\n\tif (szparam == nullptr) return nullptr;\n\tfor (int i = 0; i < m_var.Count(); ++i)\n\t{\n\t\tVariable* vi = m_var.GetVariable(i);\n\t\tif (vi->m_name == szparam) return dynamic_cast<SimpleVariable*>(vi);\n\t}\n\treturn nullptr;\n}\n"
  },
  {
    "path": "FECore/ClassDescriptor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <string>\n#include <vector>\n#include \"fecore_api.h\"\n\nclass FECORE_API FEClassDescriptor\n{\npublic:\n\tclass Variable\n\t{\n\tpublic:\n\t\tVariable(const std::string& name) : m_name(name) {}\n\t\tvirtual ~Variable() {}\n\n\t\tvirtual size_t Count() const = 0;\n\n\tpublic:\n\t\tstd::string\tm_name;\n\t};\n\n\tclass SimpleVariable : public Variable\n\t{\n\tpublic:\n\t\tSimpleVariable(const std::string& name, const std::string& value) : Variable(name), m_val(value) {}\n\n\t\tsize_t Count() const override { return 1; }\n\n\tpublic:\n\t\tstd::string\tm_val;\n\t};\n\n\tclass ClassVariable : public Variable\n\t{\n\tpublic:\n\t\tClassVariable(const std::string& name, const std::string& type) : Variable(name), m_type(type) {}\n\t\tsize_t Count() const override { return m_var.size(); }\n\n\t\tvoid AddVariable(Variable* v) { m_var.push_back(v); }\n\n\t\tVariable* GetVariable(int i) { return m_var[i]; }\n\t\tconst Variable* GetVariable(int i) const { return m_var[i]; }\n\n\tpublic:\n\t\tstd::vector<Variable*> m_var;\n\t\tstd::string\tm_type;\n\t};\n\npublic:\n\tFEClassDescriptor(const std::string& classType) : m_var(\"root\", classType) {}\n\n\tvoid AddVariable(Variable* v) { m_var.AddVariable(v); }\n\n\tClassVariable* Root() { return &m_var; }\n\tconst ClassVariable* Root() const { return &m_var; }\n\n\tSimpleVariable* FindParameter(const char* szparam);\n\n\tconst std::string& ClassType() const { return m_var.m_type; }\n\npublic:\n\tClassVariable\tm_var;\n};\n"
  },
  {
    "path": "FECore/CompactMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"CompactMatrix.h\"\n#include <assert.h>\n\n//=============================================================================\n// CompactMatrix\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nCompactMatrix::CompactMatrix(int offset)\n{\n\tm_pd = 0;\n\tm_pindices = 0;\n\tm_ppointers = 0;\n\tm_offset = offset;\n\n\tm_bdel = false;\n}\n\n\n//-----------------------------------------------------------------------------\nCompactMatrix::~CompactMatrix()\n{ \n\tClear(); \n}\n\n//-----------------------------------------------------------------------------\nvoid CompactMatrix::Zero()\n{\n\tmemset(m_pd, 0, m_nsize*sizeof(double)); \n}\n\n//-----------------------------------------------------------------------------\nvoid CompactMatrix::Clear()\n{\n\tif (m_bdel)\n\t{\n\t\tif (m_pd) delete [] m_pd;\n\t\tif (m_pindices) delete [] m_pindices;\n\t\tif (m_ppointers) delete [] m_ppointers;\n\t}\n\n\tm_pd = 0;\n\tm_pindices = 0;\n\tm_ppointers = 0;\n\n\tSparseMatrix::Clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid CompactMatrix::alloc(int nr, int nc, int nz, double* pv, int* pi, int* pp, bool bdel)\n{\n\tClear();\n\tm_pd = pv;\n\tm_pindices = pi;\n\tm_ppointers = pp;\n\n\tm_bdel = bdel;\n\n\tm_nrow = nr;\n\tm_ncol = nc;\n\tm_nsize = nz;\n\n\tint nn = (isRowBased() ? nr : nc) + 1;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate bandwidth of matrix\nint CompactMatrix::bandWidth()\n{\n\tint NR = Rows();\n\tint NC = Columns();\n\tint kmax = 0;\n\tif (isRowBased())\n\t{\n\t\tfor (int i = 0; i < NR; ++i)\n\t\t{\n\t\t\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\t\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tint j = pi[k] - m_offset;\n\t\t\t\tif (abs(j - i) > kmax) kmax = abs(j - i);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int j = 0; j < NC; ++j)\n\t\t{\n\t\t\tint* pj = m_pindices + (m_ppointers[j] - m_offset);\n\t\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tint i = pj[k] - m_offset;\n\t\t\t\tif (abs(j - i) > kmax) kmax = abs(j - i);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn kmax;\n}\n\nsize_t CompactMatrix::actualNonZeroes()\n{\n\tsize_t NNZ = NonZeroes();\n\tsize_t nnz = 0;\n\tdouble* v = Values();\n\tfor (size_t i = 0; i < NNZ; ++i)\n\t{\n\t\tif (v[i] != 0.0) nnz++;\n\t}\n\treturn nnz;\n}\n"
  },
  {
    "path": "FECore/CompactMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"SparseMatrix.h\"\n#include \"CSRMatrix.h\"\n\n//=============================================================================\n//! This class stores a sparse matrix in Harwell-Boeing format.\n\n//! This is the base class for the symmetric and unsymmetric classes\n\nclass FECORE_API CompactMatrix : public SparseMatrix\n{\npublic:\n\t//! constructor\n\tCompactMatrix( int offset );\n\n\t//! destructor\n\tvirtual ~CompactMatrix();\n\n\t//! zero matrix elements\n\tvoid Zero() override;\n\n\t//! Clear \n\tvoid Clear() override;\n\npublic:\n\t//! Pointer to matrix values\n\tdouble* Values  () override { return m_pd;   }\n\n\t//! Pointer to matrix indices\n\tint*    Indices() override { return m_pindices; }\n\n\t//! pointer to matrix row pointers\n\tint*    Pointers() override { return m_ppointers; }\n\n\t//! return the index offset (is 0 or 1)\n\tint     Offset() const override { return m_offset; }\n\npublic:\n\t//! Create the matrix\n\tvoid alloc(int nr, int nc, int nz, double* pv, int *pi, int* pp, bool bdel = true);\n\n\t//! is the matrix symmetric or not\n\tvirtual bool isSymmetric() = 0;\n\n\t//! is this a row-based format or not\n\tvirtual bool isRowBased() = 0;\n\npublic:\n\t//! Calculate the infinity norm\n\tvirtual double infNorm() const = 0;\n\n\t//! calculate the one norm\n\tvirtual double oneNorm() const = 0;\n\n\t//! calculate bandwidth of matrix\n\tint bandWidth();\n\n\t//! count the actual nr. of nonzeroes\n\tsize_t actualNonZeroes();\n\nprotected:\n\tdouble*\tm_pd;\t\t\t//!< matrix values\n\tint*\tm_pindices;\t\t//!< indices\n\tint*\tm_ppointers;\t//!< pointers\n\tint\t\tm_offset;\t\t//!< adjust array indices for fortran arrays\n\tbool\tm_bdel;\t\t\t//!< delete data arrays in destructor\n\nprotected:\n\tstd::vector<int>\tP;\n};\n"
  },
  {
    "path": "FECore/CompactSymmMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"CompactSymmMatrix.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n//! constructor\nCompactSymmMatrix::CompactSymmMatrix(int offset) : CompactMatrix(offset) \n{\n\t\n}\n\n//-----------------------------------------------------------------------------\nbool CompactSymmMatrix::mult_vector(double* x, double* r)\n{\n\t// get row count\n\tint N = Rows();\n\tint M = Columns();\n\n\t// zero result vector\n\tfor (int j = 0; j<N; ++j) r[j] = 0.0;\n\n\t// loop over all columns\n\tfor (int j = 0; j<M; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pi = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\t// add off-diagonal elements\n\t\tfor (int i = 1; i<n - 7; i += 8)\n\t\t{\n\t\t\t// add lower triangular element\n\t\t\tr[pi[i    ] - m_offset] += pv[i    ] * x[j];\n\t\t\tr[pi[i + 1] - m_offset] += pv[i + 1] * x[j];\n\t\t\tr[pi[i + 2] - m_offset] += pv[i + 2] * x[j];\n\t\t\tr[pi[i + 3] - m_offset] += pv[i + 3] * x[j];\n\t\t\tr[pi[i + 4] - m_offset] += pv[i + 4] * x[j];\n\t\t\tr[pi[i + 5] - m_offset] += pv[i + 5] * x[j];\n\t\t\tr[pi[i + 6] - m_offset] += pv[i + 6] * x[j];\n\t\t\tr[pi[i + 7] - m_offset] += pv[i + 7] * x[j];\n\t\t}\n\t\tfor (int i = 0; i<(n - 1) % 8; ++i)\n\t\t\tr[pi[n - 1 - i] - m_offset] += pv[n - 1 - i] * x[j];\n\n\t\t// add diagonal element\n\t\tdouble rj = pv[0] * x[j];\n\n\t\t// add upper-triangular elements\n\t\tfor (int i = 1; i<n - 7; i += 8)\n\t\t{\n\t\t\t// add upper triangular element\n\t\t\trj += pv[i    ] * x[pi[i    ] - m_offset];\n\t\t\trj += pv[i + 1] * x[pi[i + 1] - m_offset];\n\t\t\trj += pv[i + 2] * x[pi[i + 2] - m_offset];\n\t\t\trj += pv[i + 3] * x[pi[i + 3] - m_offset];\n\t\t\trj += pv[i + 4] * x[pi[i + 4] - m_offset];\n\t\t\trj += pv[i + 5] * x[pi[i + 5] - m_offset];\n\t\t\trj += pv[i + 6] * x[pi[i + 6] - m_offset];\n\t\t\trj += pv[i + 7] * x[pi[i + 7] - m_offset];\n\t\t}\n\t\tfor (int i = 0; i<(n - 1) % 8; ++i)\n\t\t\trj += pv[n - 1 - i] * x[pi[n - 1 - i] - m_offset];\n\n\t\tr[j] += rj;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid CompactSymmMatrix::Create(SparseMatrixProfile& mp)\n{\n\t// TODO: we should probably enforce that the matrix is square\n\tint nr = mp.Rows();\n\tint nc = mp.Columns();\n\tassert(nr==nc);\n\n\t// allocate pointers to column offsets\n\tint* pointers = new int[nc + 1];\n\tfor (int i = 0; i <= nc; ++i) pointers[i] = 0;\n\n\tint nsize = 0;\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tint n = (int)a.size();\n\t\tfor (int j = 0; j<n; j++)\n\t\t{\n\t\t\tint a0 = a[j].start;\n\t\t\tint a1 = a[j].end;\n\n\t\t\t// only grab lower-triangular\n\t\t\tif (a1 >= i)\n\t\t\t{\n\t\t\t\tif (a0 < i) a0 = i;\n\t\t\t\tint asize = a1 - a0 + 1;\n\t\t\t\tnsize += asize;\n\t\t\t\tpointers[i] += asize;\n\t\t\t}\n\t\t}\n\t}\n\n\t// allocate indices which store row index for each matrix element\n\tint* pindices = new int[nsize];\n\tint m = 0;\n\tfor (int i = 0; i <= nc; ++i)\n\t{\n\t\tint n = pointers[i];\n\t\tpointers[i] = m;\n\t\tm += n;\n\t}\n\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tint n = (int)a.size();\n\t\tint nval = 0;\n\t\tfor (int j = 0; j<n; j++)\n\t\t{\n\t\t\tint a0 = a[j].start;\n\t\t\tint a1 = a[j].end;\n\n\t\t\t// only grab lower-triangular\n\t\t\tif (a1 >= i)\n\t\t\t{\n\t\t\t\tif (a0 < i) a0 = i;\n\t\t\t\tfor (int k = a0; k <= a1; ++k)\n\t\t\t\t{\n\t\t\t\t\tpindices[pointers[i] + nval] = k;\n\t\t\t\t\t++nval;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// offset the indicies for fortran arrays\n\tif (Offset())\n\t{\n\t\tfor (int i = 0; i <= nc; ++i) pointers[i]++;\n\t\tfor (int i = 0; i<nsize; ++i) pindices[i]++;\n\t}\n\n\t// create the values array\n\tdouble* pvalues = new double[nsize];\n\n\t// create the stiffness matrix\n\tCompactMatrix::alloc(nr, nc, nsize, pvalues, pindices, pointers);\n}\n\n//-----------------------------------------------------------------------------\n// this sort function is defined in qsort.cpp\nvoid qsort(int n, const int* arr, int* indx);\n\n//-----------------------------------------------------------------------------\n//! This function assembles the local stiffness matrix\n//! into the global stiffness matrix which is in compact column storage\n//!\nvoid CompactSymmMatrix::Assemble(const matrix& ke, const vector<int>& LM)\n{\n\t// get the number of degrees of freedom\n\tconst int N = ke.rows();\n\n\t// find the permutation array that sorts LM in ascending order\n\t// we can use this to speed up the row search (i.e. loop over n below)\n\tP.resize(N);\n\tqsort(N, &LM[0], &P[0]);\n\n\t// get the data pointers \n\tint* indices = Indices();\n\tint* pointers = Pointers();\n\tdouble* pd = Values();\n\tint offset = Offset();\n\n\t// find the starting index\n\tint N0 = 0;\n\twhile ((N0<N) && (LM[P[N0]]<0)) ++N0;\n\n\t// assemble element stiffness\n\tfor (int m = N0; m<N; ++m)\n\t{\n\t\tint j = P[m];\n\t\tint J = LM[j];\n\t\tint n = 0;\n\t\tdouble* pm = pd + (pointers[J] - offset);\n\t\tint* pi = indices + (pointers[J] - offset);\n\t\tint l = pointers[J + 1] - pointers[J];\n\t\tint M0 = m;\n\t\twhile ((M0>N0) && (LM[P[M0 - 1]] == J)) M0--;\n\t\tfor (int k = M0; k<N; ++k)\n\t\t{\n\t\t\tint i = P[k];\n\t\t\tint I = LM[i] + offset;\n\t\t\tfor (; n<l; ++n)\n\t\t\t\tif (pi[n] == I)\n\t\t\t\t{\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tpm[n] += ke[i][j];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid CompactSymmMatrix::Assemble(const matrix& ke, const vector<int>& LMi, const vector<int>& LMj)\n{\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tint* indices = Indices();\n\tint* pointers = Pointers();\n\tdouble* values = Values();\n\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tint I = LMi[i];\n\n\t\tfor (int j = 0; j<M; ++j)\n\t\t{\n\t\t\tint J = LMj[j];\n\n\t\t\t// only add values to lower-diagonal part of stiffness matrix\n\t\t\tif ((I >= J) && (J >= 0))\n\t\t\t{\n\t\t\t\tdouble* pv = values + (pointers[J] - m_offset);\n\t\t\t\tint* pi = indices + (pointers[J] - m_offset);\n\t\t\t\tint l = pointers[J + 1] - pointers[J];\n\t\t\t\tfor (int n = 0; n<l; ++n) \n\t\t\t\t\tif (pi[n] - m_offset == I)\n\t\t\t\t\t{\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tpv[n] += ke[i][j];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! add a matrix item\nvoid CompactSymmMatrix::add(int i, int j, double v)\n{\n\t// only add to lower triangular part\n\t// since FEBio only works with the upper triangular part\n\t// we have to swap the indices\n\ti ^= j; j ^= i; i ^= j;\n\n\tif (j <= i)\n\t{\n\t\tdouble* pd = m_pd + (m_ppointers[j] - m_offset);\n\t\tint* pi = m_pindices + m_ppointers[j];\n\t\tpi -= m_offset;\n\t\ti += m_offset;\n\t\tint n1 = m_ppointers[j + 1] - m_ppointers[j] - 1;\n\t\tint n0 = 0;\n\t\tint n = n1 / 2;\n\t\tdo\n\t\t{\n\t\t\tint m = pi[n];\n\t\t\tif (m == i)\n\t\t\t{\n\t\t\t\t#pragma omp atomic\n\t\t\t\tpd[n] += v;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (m < i)\n\t\t\t{\n\t\t\t\tn0 = n;\n\t\t\t\tn = (n0 + n1 + 1) >> 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tn1 = n;\n\t\t\t\tn = (n0 + n1) >> 1;\n\t\t\t}\n\t\t} while (n0 != n1);\n\n\t\tassert(false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! check fo a matrix item\nbool CompactSymmMatrix::check(int i, int j)\n{\n\t// only the lower-triangular part is stored, so swap indices if necessary\n\tif (i<j) { i ^= j; j ^= i; i ^= j; }\n\n\t// only add to lower triangular part\n\tif (j <= i)\n\t{\n\t\tint* pi = m_pindices + m_ppointers[j];\n\t\tpi -= m_offset;\n\t\tint l = m_ppointers[j + 1] - m_ppointers[j];\n\t\tfor (int n = 0; n<l; ++n)\n\t\t\tif (pi[n] == i + m_offset)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t}\n\n\treturn false;\n}\n\n\n//-----------------------------------------------------------------------------\n//! set matrix item\nvoid CompactSymmMatrix::set(int i, int j, double v)\n{\n\tif (j <= i)\n\t{\n\t\tint* pi = m_pindices + m_ppointers[j];\n\t\tpi -= m_offset;\n\t\tint l = m_ppointers[j + 1] - m_ppointers[j];\n\t\tfor (int n = 0; n<l; ++n)\n\t\t\tif (pi[n] == i + m_offset)\n\t\t\t{\n\t\t\t\tint k = m_ppointers[j] + n;\n\t\t\t\tk -= m_offset;\n#pragma omp critical (CSM_set)\n\t\t\t\tm_pd[k] = v;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\tassert(false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get a matrix item\ndouble CompactSymmMatrix::get(int i, int j)\n{\n\t// only the lower-triangular part is stored, so swap indices if necessary\n\tif (i<j) { i ^= j; j ^= i; i ^= j; }\n\n\tint *pi = m_pindices + m_ppointers[j], k;\n\tpi -= m_offset;\n\tint l = m_ppointers[j + 1] - m_ppointers[j];\n\tfor (int n = 0; n<l; ++n)\n\t\tif (pi[n] == i + m_offset)\n\t\t{\n\t\t\tk = m_ppointers[j] + n;\n\t\t\tk -= m_offset;\n\t\t\treturn m_pd[k];\n\t\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\ndouble CompactSymmMatrix::infNorm() const\n{\n\t// get the matrix size\n\tconst int N = Rows();\n\n\t// keep track of row sums\n\tvector<double> rowSums(N, 0.0);\n\n\t// loop over all columns\n\tfor (int j = 0; j<N; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pr = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\tdouble ri = 0.0;\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tint irow = pr[i] - m_offset;\n\t\t\tdouble vij = fabs(pv[i]);\n\t\t\tri += vij;\n\t\t\tif (irow != j) rowSums[irow] += vij;\n\t\t}\n\n\t\trowSums[j] += ri;\n\t}\n\n\t// find the largest row sum\n\tdouble rmax = rowSums[0];\n\tfor (int i = 1; i < N; ++i)\n\t{\n\t\tif (rowSums[i] > rmax) rmax = rowSums[i];\n\t}\n\n\treturn rmax;\n}\n\n//-----------------------------------------------------------------------------\ndouble CompactSymmMatrix::oneNorm() const\n{\n\t// get the matrix size\n\tconst int NR = Rows();\n\tconst int NC = Columns();\n\n\t// keep track of col sums\n\tvector<double> colSums(NC, 0.0);\n\n\t// loop over all columns\n\tfor (int j = 0; j<NC; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pr = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\tdouble cj = 0.0;\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tint irow = pr[i] - m_offset;\n\t\t\tdouble vij = fabs(pv[i]);\n\t\t\tcj += vij;\n\t\t\tif (irow != j) colSums[irow] += vij;\n\t\t}\n\n\t\tcolSums[j] += cj;\n\t}\n\n\t// find the largest row sum\n\tdouble rmax = colSums[0];\n\tfor (int i = 1; i < NC; ++i)\n\t{\n\t\tif (colSums[i] > rmax) rmax = colSums[i];\n\t}\n\n\treturn rmax;\n}\n\n//-----------------------------------------------------------------------------\nvoid CompactSymmMatrix::scale(const vector<double>& L, const vector<double>& R)\n{\n\t// get the matrix size\n\tconst int N = Columns();\n\n\t// loop over all columns\n\tfor (int j = 0; j < N; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pr = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tpv[i] *= L[pr[i] - m_offset] * R[j];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/CompactSymmMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"CompactMatrix.h\"\n#include \"fecore_api.h\"\n\n//=============================================================================\n//! This class stores a sparse matrix in Harwell-Boeing format (i.e. column major, lower triangular compact).\n\n//! This class also assumes the matrix is symmetric and therefor only stores\n//! the lower triangular matrix\n\nclass FECORE_API CompactSymmMatrix : public CompactMatrix\n{\npublic:\n\t//! class constructor\n\tCompactSymmMatrix(int offset = 0);\n\n\t//! Create the matrix structure from the SparseMatrixProfile.\n\tvoid Create(SparseMatrixProfile& mp) override;\n\n\t//! Assemble an element matrix into the global matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\t//! add a matrix item\n\tvoid add(int i, int j, double v) override;\n\n\t//! set matrix item\n\tvoid set(int i, int j, double v) override;\n\n\t//! get a matrix item\n\tdouble get(int i, int j) override;\n\n\t// alternative access\n\tdouble operator ()(int i, int j) { return get(i, j); }\n\n\t//! return the diagonal component\n\tdouble diag(int i) override { return m_pd[m_ppointers[i] - m_offset]; }\n\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override;\n\n\t//! see if a matrix element is defined\n\tbool check(int i, int j) override;\n\n\t//! is the matrix symmetric or not\n\tbool isSymmetric() override { return true; }\n\n\t//! this is a column based format\n\tbool isRowBased() override { return false; }\n\n\t//! calculate the inf norm\n\tdouble infNorm() const override;\n\n\t//! calculate the one norm\n\tdouble oneNorm() const override;\n\n\t//! do row (L) and column (R) scaling\n\tvoid scale(const std::vector<double>& L, const std::vector<double>& R) override;\n};\n"
  },
  {
    "path": "FECore/CompactSymmMatrix64.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"CompactSymmMatrix64.h\"\nusing namespace std;\n\nCompactSymmMatrix64::CompactSymmMatrix64(int offset) : m_offset(offset)\n{\n\tm_pvalues   = nullptr;\n\tm_pindices  = nullptr;\n\tm_ppointers = nullptr;\n}\n\nCompactSymmMatrix64::~CompactSymmMatrix64()\n{\n\tClear();\n}\n\nvoid CompactSymmMatrix64::Clear()\n{\n\tdelete[] m_pvalues; m_pvalues = nullptr;\n\tdelete[] m_pindices; m_pindices = nullptr;\n\tdelete[] m_ppointers; m_ppointers = nullptr;\n}\n\nvoid CompactSymmMatrix64::Create(SparseMatrixProfile& mp)\n{\n\t// TODO: we should probably enforce that the matrix is square\n\tm_nrow = mp.Rows();\n\tm_ncol = mp.Columns();\n\tsize_t nr = m_nrow;\n\tsize_t nc = m_ncol;\n\tassert(nr == nc);\n\n\tClear();\n\n\t// allocate pointers to column offsets\n\tm_ppointers = new long long[nc + 1];\n\tfor (int i = 0; i <= nc; ++i) m_ppointers[i] = 0;\n\n\tsize_t nsize = 0;\n\tfor (size_t i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tsize_t n = a.size();\n\t\tfor (size_t j = 0; j<n; j++)\n\t\t{\n\t\t\tsize_t a0 = a[j].start;\n\t\t\tsize_t a1 = a[j].end;\n\n\t\t\t// only grab lower-triangular\n\t\t\tif (a1 >= i)\n\t\t\t{\n\t\t\t\tif (a0 < i) a0 = i;\n\t\t\t\tsize_t asize = a1 - a0 + 1;\n\t\t\t\tnsize += asize;\n\t\t\t\tm_ppointers[i] += asize;\n\t\t\t}\n\t\t}\n\t}\n\n\t// allocate indices which store row index for each matrix element\n\tm_pindices = new long long[nsize];\n\tsize_t m = 0;\n\tfor (size_t i = 0; i <= nc; ++i)\n\t{\n\t\tlong long n = m_ppointers[i];\n\t\tm_ppointers[i] = m;\n\t\tm += n;\n\t}\n\n\tfor (size_t i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tsize_t n = a.size();\n\t\tsize_t nval = 0;\n\t\tfor (size_t j = 0; j<n; j++)\n\t\t{\n\t\t\tsize_t a0 = a[j].start;\n\t\t\tsize_t a1 = a[j].end;\n\n\t\t\t// only grab lower-triangular\n\t\t\tif (a1 >= i)\n\t\t\t{\n\t\t\t\tif (a0 < i) a0 = i;\n\t\t\t\tfor (size_t k = a0; k <= a1; ++k)\n\t\t\t\t{\n\t\t\t\t\tm_pindices[m_ppointers[i] + nval] = (long long)k;\n\t\t\t\t\t++nval;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// offset the indicies for fortran arrays\n\tif (Offset())\n\t{\n\t\tfor (size_t i = 0; i <= nc; ++i) m_ppointers[i]++;\n\t\tfor (size_t i = 0; i<nsize; ++i) m_pindices[i]++;\n\t}\n\n\t// create the values array\n\tm_nsize = nsize;\n\tm_pvalues = new double[nsize];\n}\n\n//-----------------------------------------------------------------------------\n// this sort function is defined in qsort.cpp\nvoid qsort(int n, const int* arr, int* indx);\n\n//-----------------------------------------------------------------------------\n//! This function assembles the local stiffness matrix\n//! into the global stiffness matrix which is in compact column storage\n//!\nvoid CompactSymmMatrix64::Assemble(const matrix& ke, const vector<int>& LM)\n{\n\t// get the number of degrees of freedom\n\tconst int N = ke.rows();\n\n\t// find the permutation array that sorts LM in ascending order\n\t// we can use this to speed up the row search (i.e. loop over n below)\n\tstd::vector<int> P(N);\n\tqsort(N, &LM[0], &P[0]);\n\n\t// get the data pointers \n\tlong long* indices = Indices64();\n\tlong long* pointers = Pointers64();\n\tdouble* pd = Values();\n\n\t// find the starting index\n\tsize_t N0 = 0;\n\twhile ((N0<N) && (LM[P[N0]]<0)) ++N0;\n\n\t// assemble element stiffness\n\tfor (size_t m = N0; m<N; ++m)\n\t{\n\t\tsize_t j = P[m];\n\t\tsize_t J = LM[j];\n\t\tsize_t n = 0;\n\t\tdouble* pm = pd + (pointers[J] - m_offset);\n\t\tlong long* pi = indices + (pointers[J] - m_offset);\n\t\tlong long l = pointers[J + 1] - pointers[J];\n\t\tsize_t M0 = m;\n\t\twhile ((M0>N0) && (LM[P[M0 - 1]] == J)) M0--;\n\t\tfor (size_t k = M0; k<N; ++k)\n\t\t{\n\t\t\tsize_t i = P[k];\n\t\t\tsize_t I = LM[i] + (size_t)m_offset;\n\t\t\tfor (; n<l; ++n)\n\t\t\t\tif (pi[n] == I)\n\t\t\t\t{\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tpm[n] += ke[i][j];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CompactSymmMatrix64::Assemble(const matrix& ke, const vector<int>& LMi, const vector<int>& LMj)\n{\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tlong long* indices = Indices64();\n\tlong long* pointers = Pointers64();\n\tdouble* values = Values();\n\n\tfor (size_t i = 0; i<N; ++i)\n\t{\n\t\tint I = LMi[i];\n\n\t\tfor (size_t j = 0; j<M; ++j)\n\t\t{\n\t\t\tint J = LMj[j];\n\n\t\t\t// only add values to lower-diagonal part of stiffness matrix\n\t\t\tif ((I >= J) && (J >= 0))\n\t\t\t{\n\t\t\t\tdouble* pv = values + (pointers[J] - m_offset);\n\t\t\t\tlong long* pi = indices + (pointers[J] - m_offset);\n\t\t\t\tlong long l = pointers[J + 1] - pointers[J];\n\t\t\t\tfor (size_t n = 0; n<l; ++n) \n\t\t\t\t\tif (pi[n] - m_offset == I)\n\t\t\t\t\t{\n\t\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\t\tpv[n] += ke[i][j];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CompactSymmMatrix64::add(int i, int j, double v)\n{\n\t// only add to lower triangular part\n\t// since FEBio only works with the upper triangular part\n\t// we have to swap the indices\n\ti ^= j; j ^= i; i ^= j;\n\n\tif (j <= i)\n\t{\n\t\tdouble* pd = m_pvalues + (m_ppointers[j] - m_offset);\n\t\tlong long* pi = m_pindices + m_ppointers[j];\n\t\tpi -= m_offset;\n\t\ti += m_offset;\n\t\tlong long n1 = m_ppointers[j + 1] - m_ppointers[j] - 1;\n\t\tlong long n0 = 0;\n\t\tlong long n = n1 / 2;\n\t\tdo\n\t\t{\n\t\t\tlong long m = pi[n];\n\t\t\tif (m == i)\n\t\t\t{\n\t\t\t\t#pragma omp atomic\n\t\t\t\tpd[n] += v;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (m < i)\n\t\t\t{\n\t\t\t\tn0 = n;\n\t\t\t\tn = (n0 + n1 + 1) >> 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tn1 = n;\n\t\t\t\tn = (n0 + n1) >> 1;\n\t\t\t}\n\t\t} while (n0 != n1);\n\n\t\tassert(false);\n\t}\n}\n\n//! check fo a matrix item\nbool CompactSymmMatrix64::check(int i, int j)\n{\n\t// only the lower-triangular part is stored, so swap indices if necessary\n\tif (i<j) { i ^= j; j ^= i; i ^= j; }\n\n\t// only add to lower triangular part\n\tif (j <= i)\n\t{\n\t\tlong long* pi = m_pindices + m_ppointers[j];\n\t\tpi -= m_offset;\n\t\tlong long l = m_ppointers[j + 1] - m_ppointers[j];\n\t\tfor (long long n = 0; n<l; ++n)\n\t\t\tif (pi[n] == i + m_offset)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t}\n\n\treturn false;\n}\n\n//! set matrix item\nvoid CompactSymmMatrix64::set(int i, int j, double v)\n{\n\tif (j <= i)\n\t{\n\t\tlong long* pi = m_pindices + m_ppointers[j];\n\t\tpi -= m_offset;\n\t\tlong long l = m_ppointers[j + 1] - m_ppointers[j];\n\t\tfor (long long n = 0; n<l; ++n)\n\t\t\tif (pi[n] == i + m_offset)\n\t\t\t{\n\t\t\t\tlong long k = m_ppointers[j] + n;\n\t\t\t\tk -= m_offset;\n#pragma omp critical (CSM_set)\n\t\t\t\tm_pvalues[k] = v;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\tassert(false);\n\t}\n}\n\n//! get a matrix item\ndouble CompactSymmMatrix64::get(int i, int j)\n{\n\t// only the lower-triangular part is stored, so swap indices if necessary\n\tif (i<j) { i ^= j; j ^= i; i ^= j; }\n\n\tlong long *pi = m_pindices + m_ppointers[j], k;\n\tpi -= m_offset;\n\tlong long l = m_ppointers[j + 1] - m_ppointers[j];\n\tfor (long long n = 0; n<l; ++n)\n\t\tif (pi[n] == i + m_offset)\n\t\t{\n\t\t\tk = m_ppointers[j] + n;\n\t\t\tk -= m_offset;\n\t\t\treturn m_pvalues[k];\n\t\t}\n\treturn 0;\n}\n\nvoid CompactSymmMatrix64::Zero()\n{\n\tfor (size_t i = 0; i < m_nsize; ++i) m_pvalues[i] = 0.0;\n}\n"
  },
  {
    "path": "FECore/CompactSymmMatrix64.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"SparseMatrix.h\"\n#include \"fecore_api.h\"\n\n//! This class stores a sparse matrix in Harwell-Boeing format (i.e. column major, lower triangular compact).\n\n//! This class also assumes the matrix is symmetric and therefor only stores\n//! the lower triangular matrix\n\nclass FECORE_API CompactSymmMatrix64 : public SparseMatrix\n{\npublic:\n\t//! class constructor\n\tCompactSymmMatrix64(int offset = 0);\n\t~CompactSymmMatrix64();\n\n\t//! Create the matrix structure from the SparseMatrixProfile.\n\tvoid Create(SparseMatrixProfile& mp) override;\n\n\t//! Assemble an element matrix into the global matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\t//! add a matrix item\n\tvoid add(int i, int j, double v) override;\n\n\t//! set matrix item\n\tvoid set(int i, int j, double v) override;\n\n\t//! get a matrix item\n\tdouble get(int i, int j) override;\n\n\t// alternative access\n\tdouble operator ()(int i, int j) { return get(i, j); }\n\n\t//! return the diagonal component\n\tdouble diag(int i) override { return m_pvalues[m_ppointers[i] - m_offset]; }\n\n\t//! multiply with vector\n\n\t//! see if a matrix element is defined\n\tbool check(int i, int j) override;\n\n\tvoid Zero() override;\n\npublic:\n\tdouble* Values() override { return m_pvalues; }\n\tlong long* Indices64() { return m_pindices; }\n\tlong long* Pointers64() { return m_ppointers; }\n\tint Offset() const override { return (int)m_offset; }\n\nprivate:\n\tint* Indices() override { return nullptr; }\n\tint* Pointers() override { return nullptr; }\n\tbool mult_vector(double* x, double* r) override { return false; }\n\n\tvoid Clear();\n\nprivate:\n\tdouble* m_pvalues;\t\t\t//!< matrix values\n\tlong long* m_pindices;\t\t//!< row indices\n\tlong long* m_ppointers;\t//!< pointers\n\tlong long m_offset;\t\t//!< adjust array indices for fortran arrays\n};\n"
  },
  {
    "path": "FECore/CompactUnSymmMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"CompactUnSymmMatrix.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n// this sort function is defined in qsort.cpp\nvoid qsort(int n, const int* arr, int* indx);\n\n//=================================================================================================\nCRSSparseMatrix::Iterator::Iterator(CRSSparseMatrix* A) : m_A(A)\n{\n\treset();\n}\n\nbool CRSSparseMatrix::Iterator::valid()\n{\n\treturn (n != -1);\n}\n\nvoid CRSSparseMatrix::Iterator::next()\n{\n\tif (valid())\n\t{\n\t\tint* pr = m_A->Pointers();\n\t\tint l = pr[r+1] - pr[r];\n\t\tif (n < l-1) n++;\n\t\telse\n\t\t{\n\t\t\tr++;\n\t\t\tif (r >= m_A->Rows()) n = -1;\n\t\t\telse n = 0;\n\t\t}\n\t}\n\telse assert(false);\n}\n\nvoid CRSSparseMatrix::Iterator::reset()\n{\n\tr = 0;\n\tn = 0;\n\tif (m_A == nullptr) n = -1;\n}\n\nMatrixItem CRSSparseMatrix::Iterator::get()\n{\n\tassert(valid());\n\tint* pr = m_A->Pointers();\n\tint* pi = m_A->Indices() + (pr[r] - m_A->Offset());\n\tdouble* pv = m_A->Values() + (pr[r] - m_A->Offset());\n\n\tMatrixItem m;\n\tm.row = r;\n\tm.col = pi[n] - m_A->Offset();\n\tm.val = pv[n];\n\n\treturn m;\n}\n\nvoid CRSSparseMatrix::Iterator::set(double v)\n{\n\tassert(valid());\n\tint* pr = m_A->Pointers();\n\tdouble* pv = m_A->Values() + (pr[r] - m_A->Offset());\n\tpv[n] = v;\n}\n\n\n//=================================================================================================\n// CRSSparseMatrix\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\n//! Constructor for CRSSparseMatrix class \nCRSSparseMatrix::CRSSparseMatrix(int offset) : CompactMatrix(offset)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nCRSSparseMatrix::CRSSparseMatrix(const CRSSparseMatrix& A) : CompactMatrix(A.m_offset)\n{\n\tm_nrow = A.m_nrow;\n\tm_ncol = A.m_ncol;\n\tm_nsize = A.m_nsize;\n\n\tm_ppointers = new int[m_nrow + 1];\n\tm_pindices = new int[m_nsize];\n\tm_pd = new double[m_nsize];\n\n\tfor (int i = 0; i <= m_nrow; ++i) m_ppointers[i] = A.m_ppointers[i];\n\tfor (int i = 0; i<m_nsize; ++i) m_pindices[i] = A.m_pindices[i];\n\tfor (int i = 0; i<m_nsize; ++i) m_pd[i] = A.m_pd[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid CRSSparseMatrix::Create(SparseMatrixProfile& mp)\n{\n\tint nr = mp.Rows();\n\tint nc = mp.Columns();\n\n\tint* pointers = new int[nr + 1];\n\tfor (int i = 0; i <= nr; ++i) pointers[i] = 0;\n\n\tint nsize = 0;\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tint n = (int)a.size();\n\t\tfor (int j = 0; j<n; j++)\n\t\t{\n\t\t\tint asize = a[j].end - a[j].start + 1;\n\t\t\tnsize += asize;\n\t\t\tfor (int k = a[j].start; k <= a[j].end; ++k) pointers[k]++;\n\t\t}\n\t}\n\n\tint* pindices = new int[nsize];\n\tint m = 0;\n\tfor (int i = 0; i <= nr; ++i)\n\t{\n\t\tint n = pointers[i];\n\t\tpointers[i] = m;\n\t\tm += n;\n\t}\n\tassert(pointers[nr] == nsize);\n\n\tvector<int> pval(nr, 0);\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tint n = (int)a.size();\n\t\tfor (int j = 0; j<n; j++)\n\t\t{\n\t\t\tfor (int k = a[j].start; k <= a[j].end; ++k)\n\t\t\t{\n\t\t\t\tpindices[pointers[k] + pval[k]] = i;\n\t\t\t\t++pval[k];\n\t\t\t}\n\t\t}\n\t}\n\n\t// offset the indicies for fortran arrays\n\tif (Offset())\n\t{\n\t\tfor (int i = 0; i <= nr; ++i) pointers[i]++;\n\t\tfor (int i = 0; i<nsize; ++i) pindices[i]++;\n\t}\n\n\t// create the values array\n\tdouble* pvalues = new double[nsize];\n\n\t// create the stiffness matrix\n\tCompactMatrix::alloc(nr, nc, nsize, pvalues, pindices, pointers);\n\n\t// calculate and print matrix bandwidth\n//\tfeLog(\"\\tMatrix bandwidth .......................... : %d\\n\", bandWidth());\n}\n\nvoid CRSSparseMatrix::Assemble(const matrix& ke, const vector<int>& LM)\n{\n\t// get the number of degrees of freedom\n\tconst int N = ke.rows();\n\n\t// find the permutation array that sorts LM in ascending order\n\t// we can use this to speed up the row search (i.e. loop over n below)\n\tP.resize(N);\n\tqsort(N, &LM[0], &P[0]);\n\n\t// get the data pointers \n\tint* indices = Indices();\n\tint* pointers = Pointers();\n\tdouble* pd = Values();\n\tint offset = Offset();\n\n\t// find the starting index\n\tint N0 = 0;\n\twhile ((N0<N) && (LM[P[N0]]<0)) ++N0;\n\n\t// assemble element stiffness\n\tfor (int k = N0; k<N; ++k)\n\t{\n\t\tint i = P[k];\n\t\tint I = LM[i];\n\t\tint n = 0;\n\n\t\tdouble* pm = pd + (pointers[I] - offset);\n\t\tint* pi = indices + (pointers[I] - offset);\n\t\tint l = pointers[I + 1] - pointers[I];\n\n\t\tfor (int m = N0; m<N; ++m)\n\t\t{\n\t\t\tint j = P[m];\n\t\t\tint J = LM[j] + offset;\n\t\t\tdouble kij = ke[i][j];\n\t\t\tfor (; n<l; ++n)\n\t\t\t\tif (pi[n] == J)\n\t\t\t\t{\n#pragma omp atomic\n\t\t\t\t\tpm[n] += kij;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid CRSSparseMatrix::Assemble(const matrix& ke, const vector<int>& LMi, const vector<int>& LMj)\n{\n\tint I, J;\n\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tif ((I = LMi[i]) >= 0)\n\t\t{\n\t\t\tfor (int j = 0; j<M; ++j)\n\t\t\t{\n\t\t\t\tif ((J = LMj[j]) >= 0) add(I, J, ke[i][j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This algorithm uses a binary search for locating the correct row index\n// This assumes that the indices are ordered!\nvoid CRSSparseMatrix::add(int i, int j, double v)\n{\n\tassert((i >= 0) && (i<m_nrow));\n\tassert((j >= 0) && (j<m_ncol));\n\n\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\tdouble* pd = m_pd + (m_ppointers[i] - m_offset);\n\tint n1 = m_ppointers[i + 1] - m_ppointers[i] - 1;\n\tint n0 = 0;\n\tint n = n1 / 2;\n\tj += m_offset;\n\tdo\n\t{\n\t\tint m = pi[n];\n\t\tif (m == j)\n\t\t{\n#pragma omp atomic\n\t\t\tpd[n] += v;\n\t\t\treturn;\n\t\t}\n\t\telse if (m < j)\n\t\t{\n\t\t\tn0 = n;\n\t\t\tn = (n0 + n1 + 1) >> 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tn1 = n;\n\t\t\tn = (n0 + n1) >> 1;\n\t\t}\n\t} while (n0 != n1);\n\tassert(false);\n}\n\n\n//-----------------------------------------------------------------------------\nvoid CRSSparseMatrix::set(int i, int j, double v)\n{\n\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\tint l = m_ppointers[i + 1] - m_ppointers[i];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == j + m_offset)\n\t\t{\n#pragma omp critical (CM_set)\n\t\t\tm_pd[m_ppointers[i] + n - m_offset] = v;\n\t\t\treturn;\n\t\t}\n\t}\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\ndouble CRSSparseMatrix::get(int i, int j)\n{\n\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\tint l = m_ppointers[i + 1] - m_ppointers[i];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == j + m_offset) return m_pd[m_ppointers[i] + n - m_offset];\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool CRSSparseMatrix::check(int i, int j)\n{\n\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\tint l = m_ppointers[i + 1] - m_ppointers[i];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == j + m_offset) return true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\ndouble CRSSparseMatrix::diag(int i)\n{\n\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\tint l = m_ppointers[i + 1] - m_ppointers[i];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == i + m_offset)\n\t\t{\n\t\t\treturn m_pd[m_ppointers[i] + n - m_offset];\n\t\t}\n\t}\n\tassert(false);\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool CRSSparseMatrix::mult_vector(double* x, double* r)\n{\n\t// get the matrix size\n\tconst int N = Rows();\n\n\t// loop over all rows\n\t#pragma omp parallel for schedule(guided)\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tconst double* pv = m_pd + (m_ppointers[i] - m_offset);\n\t\tconst int* pi = m_pindices + (m_ppointers[i] - m_offset);\n\t\tconst int n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tr[i] = 0.0;\n\t\tfor (int j = 0; j < n; j ++)\n\t\t{\n\t\t\tr[i] += (*pv++) * x[*pi++ - m_offset];\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//! calculate the abs row sum \ndouble CRSSparseMatrix::infNorm() const\n{\n\t// get the matrix size\n\tconst int N = Rows();\n\n\tdouble norm = 0.0;\n\t// loop over all rows\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tdouble ri = 0.0;\n\t\tdouble* pv = m_pd + m_ppointers[i] - m_offset;\n\t\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j<n; ++j) ri += fabs(pv[j]);\n\n\t\tif (ri > norm) norm = ri;\n\t}\n\n\treturn norm;\n}\n\n//! calculate the one norm\ndouble CRSSparseMatrix::oneNorm() const\n{\n\t// get the matrix size\n\tconst int NR = Rows();\n\tconst int NC = Columns();\n\n\tvector<double> colNorms(NC, 0.0);\n\n\t// loop over all rows\n\tfor (int i = 0; i<NR; ++i)\n\t{\n\t\tdouble ri = 0.0;\n\t\tdouble* pv = m_pd + m_ppointers[i] - m_offset;\n\t\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j<n; ++j) colNorms[pi[j]-m_offset] += fabs(pv[j]);\n\t}\n\n\t// find max value\n\tdouble rmax = 0;\n\tfor (int i = 0; i < NC; ++i)\n\t{\n\t\tif (colNorms[i] > rmax) rmax = colNorms[i];\n\t}\n\n\treturn rmax;\n}\n\n//! make the matrix a unit matrix (retains sparsity pattern)\nvoid CRSSparseMatrix::makeUnit()\n{\n\t// loop over all rows\n\tconst int N = Rows();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[i] - m_offset;\n\t\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j < n; ++j)\n\t\t{\n\t\t\tif (pi[j] - m_offset == i) pv[j] = 1.0;\n\t\t\telse pv[j] = 0.0;\n\t\t}\n\t}\n}\n\nvoid CRSSparseMatrix::scale(double s)\n{\n\tint N = NonZeroes();\n\tfor (int i = 0; i < N; ++i) m_pd[i] *= s;\n}\n\nvoid CRSSparseMatrix::scale(const vector<double>& L, const vector<double>& R)\n{\n\tconst int N = Rows();\n\tassert(L.size() == Rows());\n\tassert(R.size() == Columns());\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[i] - m_offset;\n\t\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j<n; ++j)\n\t\t{\n\t\t\tpv[j] *= L[i] * R[pi[j] - m_offset];\n\t\t}\n\t}\n}\n\n//! extract a block of this matrix\nvoid CRSSparseMatrix::get(int i0, int j0, int nr, int nc, CSRMatrix& M)\n{\n\t// create the matrix\n\tM.create(nr, nc, m_offset);\n\n\tvector<double>& val = M.values();\n\tvector<int>& ind = M.indices();\n\tvector<int>& pnt = M.pointers(); assert(pnt.size() == nr + 1);\n\n\t// count how many values we'll need to copy\n\tint nnz = 0;\n\tfor (int i = i0; i<i0 + nr; ++i)\n\t{\n\t\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j<n; ++j)\n\t\t{\n\t\t\tint colj = pi[j] - m_offset;\n\t\t\tif ((colj >= j0) && (colj < j0 + nc)) nnz++;\n\t\t}\n\t\tpnt[i - i0 + 1] = nnz + m_offset;\n\t}\n\n\t// allocate arrays\n\tval.resize(nnz);\n\tind.resize(nnz);\n\n\t// copy data\n\tnnz = 0;\n\tfor (int i = i0; i<i0 + nr; ++i)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[i] - m_offset;\n\t\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j<n; ++j)\n\t\t{\n\t\t\tint colj = pi[j] - m_offset;\n\t\t\tif ((colj >= j0) && (colj < j0 + nc))\n\t\t\t{\n\t\t\t\tval[nnz] = pv[j];\n\t\t\t\tind[nnz] = colj - j0 + m_offset;\n\t\t\t\tnnz++;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//=================================================================================================\n// CCSSparseMatrix\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\n//! Constructor for CCSSparseMatrix class \nCCSSparseMatrix::CCSSparseMatrix(int offset) : CompactMatrix(offset)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nCCSSparseMatrix::CCSSparseMatrix(const CCSSparseMatrix& A) : CompactMatrix(A.m_offset)\n{\n\tm_nrow = A.m_nrow;\n\tm_ncol = A.m_ncol;\n\tm_nsize = A.m_nsize;\n\n\tm_ppointers = new int[m_ncol + 1];\n\tm_pindices = new int[m_nsize];\n\tm_pd = new double[m_nsize];\n\n\tfor (int i = 0; i <= m_ncol; ++i) m_ppointers[i] = A.m_ppointers[i];\n\tfor (int i = 0; i<m_nsize; ++i) m_pindices[i] = A.m_pindices[i];\n\tfor (int i = 0; i<m_nsize; ++i) m_pd[i] = A.m_pd[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid CCSSparseMatrix::Create(SparseMatrixProfile& mp)\n{\n\tint nr = mp.Rows();\n\tint nc = mp.Columns();\n\n\tint* pointers = new int[nc + 1];\n\tfor (int i = 0; i <= nc; ++i) pointers[i] = 0;\n\n\tint nsize = 0;\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tint n = (int)a.size();\n\t\tfor (int j = 0; j<n; j++)\n\t\t{\n\t\t\tint asize = a[j].end - a[j].start + 1;\n\t\t\tnsize += asize;\n\t\t\tpointers[i] += asize;\n\t\t}\n\t}\n\n\tint* pindices = new int[nsize];\n\tint m = 0;\n\tfor (int i = 0; i <= nc; ++i)\n\t{\n\t\tint n = pointers[i];\n\t\tpointers[i] = m;\n\t\tm += n;\n\t}\n\tassert(pointers[nc] == nsize);\n\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i);\n\t\tint n = (int)a.size();\n\t\tint nval = 0;\n\t\tfor (int j = 0; j<n; j++)\n\t\t{\n\t\t\tfor (int k = a[j].start; k <= a[j].end; ++k)\n\t\t\t{\n\t\t\t\tpindices[pointers[i] + nval] = k;\n\t\t\t\tnval++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// offset the indicies for fortran arrays\n\tif (Offset())\n\t{\n\t\tfor (int i = 0; i <= nc; ++i) pointers[i]++;\n\t\tfor (int i = 0; i<nsize; ++i) pindices[i]++;\n\t}\n\n\t// create the values array\n\tdouble* pvalues = new double[nsize];\n\n\t// create the stiffness matrix\n\tCompactMatrix::alloc(nr, nc, nsize, pvalues, pindices, pointers);\n}\n\n//-----------------------------------------------------------------------------\nvoid CCSSparseMatrix::Assemble(const matrix& ke, const vector<int>& LM)\n{\n\t// get the number of degrees of freedom\n\tconst int N = ke.rows();\n\n\t// find the permutation array that sorts LM in ascending order\n\t// we can use this to speed up the row search (i.e. loop over n below)\n\tP.resize(N);\n\tqsort(N, &LM[0], &P[0]);\n\n\t// get the data pointers \n\tint* indices = Indices();\n\tint* pointers = Pointers();\n\tdouble* pd = Values();\n\tint offset = Offset();\n\n\t// find the starting index\n\tint N0 = 0;\n\twhile ((N0<N) && (LM[P[N0]]<0)) ++N0;\n\n\tfor (int m = N0; m<N; ++m)\n\t{\n\t\tint j = P[m];\n\t\tint J = LM[j];\n\t\tint n = 0;\n\t\tdouble* pm = pd + (pointers[J] - offset);\n\t\tint* pi = indices + (pointers[J] - offset);\n\t\tint l = pointers[J + 1] - pointers[J];\n\t\tfor (int k = N0; k<N; ++k)\n\t\t{\n\t\t\tint i = P[k];\n\t\t\tint I = LM[i] + offset;\n\t\t\tfor (; n<l; ++n)\n\t\t\t\tif (pi[n] == I)\n\t\t\t\t{\n#pragma omp atomic\n\t\t\t\t\tpm[n] += ke[i][j];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid CCSSparseMatrix::Assemble(const matrix& ke, const vector<int>& LMi, const vector<int>& LMj)\n{\n\tint I, J;\n\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tif ((I = LMi[i]) >= 0)\n\t\t{\n\t\t\tfor (int j = 0; j<M; ++j)\n\t\t\t{\n\t\t\t\tif ((J = LMj[j]) >= 0) add(I, J, ke[i][j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This algorithm uses a binary search for locating the correct row index\n// This assumes that the indices are ordered!\nvoid CCSSparseMatrix::add(int i, int j, double v)\n{\n\tassert((i >= 0) && (i<m_nrow));\n\tassert((j >= 0) && (j<m_ncol));\n\n\tint* pi = m_pindices + (m_ppointers[j] - m_offset);\n\ti += m_offset;\n\tdouble* pd = m_pd + (m_ppointers[j] - m_offset);\n\tint n1 = m_ppointers[j + 1] - m_ppointers[j] - 1;\n\tint n0 = 0;\n\tint n = n1 / 2;\n\tdo\n\t{\n\t\tint m = pi[n];\n\t\tif (m == i)\n\t\t{\n#pragma omp atomic\n\t\t\tpd[n] += v;\n\t\t\treturn;\n\t\t}\n\t\telse if (m < i)\n\t\t{\n\t\t\tn0 = n;\n\t\t\tn = (n0 + n1 + 1) >> 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tn1 = n;\n\t\t\tn = (n0 + n1) >> 1;\n\t\t}\n\t} while (n0 != n1);\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\nvoid CCSSparseMatrix::set(int i, int j, double v)\n{\n\tint* pi = m_pindices + (m_ppointers[j] - m_offset);\n\tint l = m_ppointers[j + 1] - m_ppointers[j];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == i + m_offset)\n\t\t{\n#pragma omp critical (CC_set)\n\t\t\tm_pd[m_ppointers[j] + n - m_offset] = v;\n\t\t\treturn;\n\t\t}\n\t}\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\ndouble CCSSparseMatrix::get(int i, int j)\n{\n\tint* pi = m_pindices + (m_ppointers[j] - m_offset);\n\tint l = m_ppointers[j + 1] - m_ppointers[j];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == i + m_offset) return m_pd[m_ppointers[j] + n - m_offset];\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool CCSSparseMatrix::check(int i, int j)\n{\n\tint* pi = m_pindices + m_ppointers[j] - m_offset;\n\tint l = m_ppointers[j + 1] - m_ppointers[j];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == i + m_offset) return true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\ndouble CCSSparseMatrix::diag(int i)\n{\n\tint* pi = m_pindices + m_ppointers[i] - m_offset;\n\tint l = m_ppointers[i + 1] - m_ppointers[i];\n\tfor (int n = 0; n<l; ++n)\n\t{\n\t\tif (pi[n] == i + m_offset)\n\t\t{\n\t\t\treturn m_pd[m_ppointers[i] + n - m_offset];\n\t\t}\n\t}\n\tassert(false);\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool CCSSparseMatrix::mult_vector(double* x, double* r)\n{\n\t// get the matrix size\n\tconst int N = Rows();\n\tconst int M = Columns();\n\n\t// zero r\n\tfor (int i=0; i<N; ++i) r[i] = 0.0;\n\n\t// loop over all columns\n\tfor (int i = 0; i<M; ++i)\n\t{\n\t\tdouble* pv = m_pd + (m_ppointers[i] - m_offset);\n\t\tint* pi = m_pindices + (m_ppointers[i] - m_offset);\n\t\tint n = m_ppointers[i + 1] - m_ppointers[i];\n\t\tfor (int j = 0; j<n; j++)  r[pi[j] - m_offset] += pv[j] * x[i];\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the inf norm\ndouble CCSSparseMatrix::infNorm() const\n{\n\t// get the matrix size\n\tconst int NR = Rows();\n\tconst int NC = Columns();\n\n\t// keep track of row sums\n\tvector<double> rowSums(NR, 0.0);\n\n\t// loop over all columns\n\tfor (int j = 0; j<NC; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pr = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tint irow = pr[i] - m_offset;\n\t\t\tdouble vij = fabs(pv[i]);\n\t\t\trowSums[irow] += vij;\n\t\t}\n\t}\n\n\t// find the largest row sum\n\tdouble rmax = rowSums[0];\n\tfor (int i = 1; i < NR; ++i)\n\t{\n\t\tif (rowSums[i] > rmax) rmax = rowSums[i];\n\t}\n\n\treturn rmax;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the one norm\ndouble CCSSparseMatrix::oneNorm() const\n{\n\t// get the matrix size\n\tconst int NR = Rows();\n\tconst int NC = Columns();\n\n\t// max col sum\n\tdouble cmax = 0.0;\n\n\t// loop over all columns\n\tfor (int j = 0; j<NC; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pr = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\tdouble cj = 0.0;\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tdouble vij = fabs(pv[i]);\n\t\t\tcj += vij;\n\t\t}\n\n\t\tif (cj > cmax) cmax = cj;\n\t}\n\n\treturn cmax;\n}\n\n//-----------------------------------------------------------------------------\nvoid CCSSparseMatrix::scale(const vector<double>& L, const vector<double>& R)\n{\n\t// get the matrix size\n\tconst int N = Columns();\n\n\t// loop over all columns\n\tfor (int j = 0; j < N; ++j)\n\t{\n\t\tdouble* pv = m_pd + m_ppointers[j] - m_offset;\n\t\tint* pr = m_pindices + m_ppointers[j] - m_offset;\n\t\tint n = m_ppointers[j + 1] - m_ppointers[j];\n\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tpv[i] *= L[pr[i] - m_offset] * R[j];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Create a copy of the matrix (does not copy values)\nCRSSparseMatrix* CRSSparseMatrix::Copy(int offset)\n{\n\tCRSSparseMatrix* A = new CRSSparseMatrix(offset);\n\n\tint nnz = NonZeroes();\n\tint nrow = Rows();\n\tint ncol = Columns();\n\n\tint nn = (isRowBased() ? nrow : ncol);\n\n\t// allocate memory for copy\n\tdouble* vd = new double[nnz];\n\tint* id = new int[nnz];\n\tint* pd = new int[nn + 1];\n\tA->alloc(nrow, ncol, nnz, vd, id, pd);\n\n\tint offs = Offset();\n\n\t// copy indices\n\tint* is = Indices();\n\tfor (int i = 0; i < nnz; ++i) id[i] = (is[i] - offs) + offset;\n\n\t// copy pointers\n\tint* ps = Pointers();\n\tfor (int i = 0; i < nn + 1; ++i) pd[i] = (ps[i] - offs) + offset;\n\n\treturn A;\n}\n\n//-----------------------------------------------------------------------------\n//! Copy the values from another matrix\nvoid CRSSparseMatrix::CopyValues(CompactMatrix* A)\n{\n\tassert(NonZeroes() == A->NonZeroes());\n\tmemcpy(Values(), A->Values(), sizeof(double)*NonZeroes());\n}\n\n//-----------------------------------------------------------------------------\n//! convert to another format (currently only offset can be changed)\nbool CRSSparseMatrix::Convert(int newOffset)\n{\n\tif (newOffset == m_offset) return true;\n\n\tint d_off = newOffset - m_offset;\n\n\tint nn = Rows();\n\tint nnz = NonZeroes();\n\tm_offset = newOffset;\n\n\t// copy indices\n\tint* is = Indices();\n\tfor (int i = 0; i < nnz; ++i) is[i] += d_off;\n\n\t// copy pointers\n\tint* ps = Pointers();\n\tfor (int i = 0; i < nn + 1; ++i) ps[i] += d_off;\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/CompactUnSymmMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"CompactMatrix.h\"\n#include \"fecore_api.h\"\n\nstruct MatrixItem\n{\n\tint\trow, col;\n\tdouble\tval;\n};\n\n//=============================================================================\n//! This class stores a general, sparse matrix in Compact Row Storage format\nclass FECORE_API CRSSparseMatrix : public CompactMatrix\n{\npublic:\n\tclass Iterator\n\t{\n\tpublic:\n\t\tIterator(CRSSparseMatrix* A);\n\t\tbool valid();\n\t\tvoid next();\n\t\tvoid reset();\n\n\t\tMatrixItem get();\n\t\tvoid set(double v);\n\n\tprivate:\n\t\tint\tr, n;\n\t\tCRSSparseMatrix*\tm_A;\n\t};\n\npublic:\n\t//! constructor\n\tCRSSparseMatrix(int offset = 0);\n\n\t//! copy constructor\n\tCRSSparseMatrix(const CRSSparseMatrix& A);\n\n\t//! Create the matrix structure from the SparseMatrixProfile\n\tvoid Create(SparseMatrixProfile& mp) override;\n\n\t//! Assemble the element matrix into the global matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\t//! add a value to the matrix item\n\tvoid add(int i, int j, double v) override;\n\n\t//! set the matrix item\n\tvoid set(int i, int j, double v) override;\n\n\t//! get a matrix item\n\tdouble get(int i, int j) override;\n\n\t//! return the diagonal value\n\tdouble diag(int i) override;\n\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override;\n\n\t//! see if a matrix element is defined\n\tbool check(int i, int j) override;\n\n\t// scale matrix \n\tvoid scale(double s);\n\tvoid scale(const std::vector<double>& L, const std::vector<double>& R) override;\n\n\t//! extract a block of this matrix\n\tvoid get(int i0, int j0, int nr, int nc, CSRMatrix& M);\n\n\t//! is the matrix symmetric or not\n\tbool isSymmetric() override { return false; }\n\n\t//! is this a row-based format or not\n\tbool isRowBased() override { return true; }\n\n\t//! calculate the inf norm\n\tdouble infNorm() const override;\n\n\t//! calculate the one norm\n\tdouble oneNorm() const override;\n\n\t//! make the matrix a unit matrix (retains sparsity pattern)\n\tvoid makeUnit();\n\n\t//! Create a copy of the matrix (does not copy values)\n\tCRSSparseMatrix* Copy(int offset);\n\n\t//! Copy the values from another matrix\n\tvoid CopyValues(CompactMatrix* A);\n\n\t//! convert to another format (currently only offset can be changed)\n\tbool Convert(int newOffset);\n};\n\n//=============================================================================\n//! This class stores a sparse matrix in Compact Column Storage format\n\nclass FECORE_API CCSSparseMatrix : public CompactMatrix\n{\npublic:\n\t//! constructor\n\tCCSSparseMatrix(int offset = 0);\n\n\t//! copy constructor\n\tCCSSparseMatrix(const CCSSparseMatrix& A);\n\n\t//! Create the matrix structure from the SparseMatrixProfile\n\tvoid Create(SparseMatrixProfile& mp) override;\n\n\t//! Assemble the element matrix into the global matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\t//! add a value to the matrix item\n\tvoid add(int i, int j, double v) override;\n\n\t//! set the matrix item\n\tvoid set(int i, int j, double v) override;\n\n\t//! get a matrix item\n\tdouble get(int i, int j) override;\n\n\t//! return the diagonal value\n\tdouble diag(int i) override;\n\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override;\n\n\t//! see if a matrix element is defined\n\tbool check(int i, int j) override;\n\n\t//! is the matrix symmetric or not\n\tbool isSymmetric() override { return false; }\n\n\t//! is this a row-based format or not\n\tbool isRowBased() override { return false; }\n\n\t//! calculate the inf norm\n\tdouble infNorm() const override;\n\n\t//! calculate the one norm\n\tdouble oneNorm() const override;\n\n\t//! do row (L) and column (R) scaling\n\tvoid scale(const std::vector<double>& L, const std::vector<double>& R) override;\n};\n"
  },
  {
    "path": "FECore/DOFS.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"DOFS.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n#include \"DumpStream.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nDOFS::DOF_ITEM::DOF_ITEM()\n{\n\tsz[0] = 0;\n\tndof = -1;\n\tnvar = -1;\n}\n\n//-----------------------------------------------------------------------------\nDOFS::DOF_ITEM::DOF_ITEM(const char* sz)\n{\n\tSetName(sz);\n\tndof = -1;\n\tnvar = -1;\n}\n\n//-----------------------------------------------------------------------------\nDOFS::DOF_ITEM::DOF_ITEM(const DOFS::DOF_ITEM& d)\n{\n\tSetName(d.sz);\n\tndof = d.ndof;\n\tnvar = d.nvar;\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::DOF_ITEM::operator = (const DOFS::DOF_ITEM& d)\n{\n\tSetName(d.sz);\n\tndof = d.ndof;\n\tnvar = d.nvar;\n}\n\n//-----------------------------------------------------------------------------\nDOFS::~DOFS()\n{\n\t\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::DOF_ITEM::SetName(const char* szdof)\n{\n\tstrcpy(sz, szdof);\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::DOF_ITEM::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & sz;\n\tar & ndof & nvar;\n}\n\n//-----------------------------------------------------------------------------\n// constructor for the DOFS class\nDOFS::DOFS()\n{\n\tm_maxdofs = 0;\n}\n\n//-----------------------------------------------------------------------------\nDOFS::DOFS(const DOFS& dofs)\n{\n\tm_var = dofs.m_var;\n\tm_maxdofs = dofs.m_maxdofs;\n}\n\n//-----------------------------------------------------------------------------\nDOFS& DOFS::operator = (const DOFS& dofs)\n{\n\tm_var = dofs.m_var;\n\tm_maxdofs = dofs.m_maxdofs;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\n// destructor for the DOFS class\n//\nDOFS::DOF_ITEM::~DOF_ITEM()\n{\n}\n\n//-----------------------------------------------------------------------------\nDOFS::Var::Var()\n{\n\tm_ntype = -1;\t// invalid\n\tm_order = -1;\t// assumed full interpolation order as implied by element nodes\n}\n\n//-----------------------------------------------------------------------------\nDOFS::Var::Var(const DOFS::Var& v) \n{ \n\tm_ntype = v.m_ntype;\n\tm_order = v.m_order;\n\tm_dof = v.m_dof;\n\tm_name = v.m_name;\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::Var::operator = (const DOFS::Var& v)\n{ \n\tm_ntype = v.m_ntype;\n\tm_order = v.m_order;\n\tm_dof = v.m_dof;\n\tm_name = v.m_name;\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::Var::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_ntype & m_order;\n\tar & m_name;\n\tar & m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::Reset()\n{\n\t// clear the DOFS\n\tif (m_var.empty() == false) m_var.clear();\n\tm_maxdofs = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Define a variable.\n//! This creates an empty variable. Add DOFs to this variable using one of the \n//! DOFS::AddDOF functions.\nint DOFS::AddVariable(const char* szvar, int ntype)\n{\n\t// Make sure szvar is a valid symbol\n\tif (szvar    == 0) return -1;\t// cannot be null\n\tif (szvar[0] == 0) return -1;\t// must have non-zero length\n\n\t// Make sure the variable does not exist yet\n\tint nvar = GetVariableIndex(szvar);\n\tif (nvar >= 0) return -1;\n\n\t// Okay, add the variable\n\tVar var;\n\tvar.m_name = szvar;\n\tvar.m_ntype = ntype;\n\n\t// allocate degrees of freedom\n\tint ndof = 0;\n\tif      (ntype == VAR_SCALAR) ndof = 1;\n\telse if (ntype == VAR_VEC2  ) ndof = 2;\n\telse if (ntype == VAR_VEC3  ) ndof = 3;\n\telse if (ntype == VAR_ARRAY ) ndof = 0;\t\t// for array we start with no dofs predefined (use AddDOF to a dofs to an array variable)\n\telse { assert(false); return -1; }\n\n\tif (ndof > 0) var.m_dof.resize(ndof);\n\tm_var.push_back(var);\n\n\tUpdate();\n\n\t// return the index to this variable\n\treturn (int) m_var.size() - 1;\n}\n\n//-----------------------------------------------------------------------------\n//! Get number of variables\nint DOFS::Variables() const\n{\n\treturn (int) m_var.size();\n}\n\n//-----------------------------------------------------------------------------\nDOFS::Var* DOFS::GetVariable(const char* szvar)\n{\n\tif (m_var.empty()) return 0;\n\tint NVAR = (int) m_var.size();\n\tfor (int i=0; i<NVAR; ++i)\n\t{\n\t\tVar& var = m_var[i];\n\t\tif (var.m_name == szvar) return &var;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint DOFS::GetVariableIndex(const char* szvar)\n{\n\tif (m_var.empty()) return -1;\n\tint NVAR = (int) m_var.size();\n\tfor (int i=0; i<NVAR; ++i)\n\t{\n\t\tVar& var = m_var[i];\n\t\tif (var.m_name == szvar) return i;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the variable name\nstd::string DOFS::GetVariableName(int n) const\n{\n\tstd::string varName;\n\tif ((n >= 0) && (n < Variables()))\n\t{\n\t\tconst Var& var = m_var[n];\n\t\tvarName = var.m_name;\n\t}\n\treturn varName;\n}\n\n//-----------------------------------------------------------------------------\n//! Add a degree of freedom to a variable.\n// This only works on array variables\n//! Returns -1 if the degree of freedom exists or if the symbol is invalid\n//! \\sa DOFS::GetDOF\nint DOFS::AddDOF(const char* szvar, const char* sz)\n{\n\t// Make sure sz is a valid symbol\n\tif (sz    == 0) return -1;\t// cannot be null\n\tif (sz[0] == 0) return -1;\t// must have non-zero length\n\n\t// Make sure the symbol does not exist yet\n\tint ndof = GetDOF(sz);\n\tif (ndof >= 0) return -1;\n\n\t// Make sure the variable is valid\n\tVar* pvar = GetVariable(szvar);\n\tif (pvar && (pvar->m_ntype == VAR_ARRAY))\n\t{\n\t\tVar& var = *pvar;\n\n\t\tDOF_ITEM it(sz);\n\t\tvar.m_dof.push_back(it);\n\n\t\t// update all dofs\n\t\tUpdate();\n\n\t\t// return a nonnegative number\n\t\treturn 0;\n\t}\n\n\t// if we get here, the variable does not exist\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Add a degree of freedom to a variable.\n//! Returns -1 if the degree of freedom exists or if the symbol is invalid\n//! \\sa DOFS::GetDOF\nint DOFS::AddDOF(int nvar, const char* sz)\n{\n\t// Make sure sz is a valid symbol\n\tif (sz    == 0) return -1;\t// cannot be null\n\tif (sz[0] == 0) return -1;\t// must have non-zero length\n\n\t// Make sure the symbol is not defined yet\n\tint ndof = GetDOF(sz);\n\tif (ndof >= 0) return -1;\n\n\t// Make sure the variable index is valid\n\tif (nvar < 0) return -1;\n\tif (nvar >= (int) m_var.size()) return -1; \n\n\tVar& var = m_var[nvar];\n\tif (var.m_ntype == VAR_ARRAY)\n\t{\n\t\t// Add the DOF\n\t\tDOF_ITEM it(sz);\n\t\tm_var[nvar].m_dof.push_back(it);\n\n\t\t// update all dofs\n\t\tUpdate();\n\n\t\t// return a nonnegative number\n\t\treturn 0;\n\t}\n\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nint DOFS::GetIndex(const char* varName, const char* szdof)\n{\n\tVar* var = GetVariable(varName);\n\tif (var == 0) return -1;\n\tfor (int i=0; i<(int)var->m_dof.size(); ++i)\n\t{\n\t\tif (strcmp(var->m_dof[i].sz, szdof) == 0) return i;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the DOF index from a variable and an index into the variable's dof array.\n//! This index is used in the FENode::get(), FENode::set() functions to set \n//! the values of nodal values. This index is also used in the FENode::m_ID and FENode::m_BC arrays.\nint DOFS::GetDOF(const char* szvar, int n)\n{\n\tVar* pvar = GetVariable(szvar);\n\tif (pvar)\n\t{\n//\t\tassert((n >= 0) && (n<(int)pvar->m_dof.size()));\n\t\tif ((n >= 0) && (n<(int)pvar->m_dof.size()))\n\t\t{\n\t\t\tDOF_ITEM& it = pvar->m_dof[n];\n\t\t\treturn it.ndof;\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the DOF index from a variable and an index into the variable's dof array.\n//! This index is used in the FENode::get(), FENode::set() functions to set \n//! the values of nodal values. This index is also used in the FENode::m_ID and FENode::m_BC arrays.\nint DOFS::GetDOF(int nvar, int n)\n{\n\tassert((nvar>=0)&&(nvar<(int)m_var.size()));\n\tVar& var = m_var[nvar];\n\tassert((n>=0)&&(n<(int)var.m_dof.size()));\n\tDOF_ITEM& it = var.m_dof[n];\n\treturn it.ndof;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the DOF index from a dof symbol.\n//! This index is used in the FENode::get(), FENode::set() functions to set \n//! the values of nodal values. This index is also used in the FENode::m_ID and FENode::m_BC arrays.\nint DOFS::GetDOF(const char* szdof, const char* szvarName)\n{\n\tconst int NVAR = (int)m_var.size();\n\tfor (int i=0; i<NVAR; ++i)\n\t{\n\t\tVar& var = m_var[i];\n\t\tif ((szvarName == nullptr) || (var.m_name == std::string(szvarName)))\n\t\t{\n\t\t\tint ndof = (int)var.m_dof.size();\n\t\t\t\tfor (int j = 0; j < ndof; ++j)\n\t\t\t\t{\n\t\t\t\t\tDOF_ITEM& it = var.m_dof[j];\n\t\t\t\t\t\tif (strcmp(it.sz, szdof) == 0) return it.ndof;\n\t\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n//! Returns a list of DOF indices for a variable. \n//! The returned list will be empty if the variable is not known\nvoid DOFS::GetDOFList(const char* varName, std::vector<int>& dofs)\n{\n\t// make sure we start with an empty list\n\tdofs.clear();\n\n\t// get the variable\n\tVar* var = GetVariable(varName);\n\tif (var == 0) return;\n\n\t// fill the dof list\n\tint n = (int) var->m_dof.size();\n\tif (n==0) return;\n\tdofs.resize(n);\n\tfor (int i=0; i<n; ++i) dofs[i] = var->m_dof[i].ndof;\n}\n\n//-----------------------------------------------------------------------------\n//! Returns a list of DOF indices for a variable. \n//! The returned list will be empty if the variable is not known\nvoid DOFS::GetDOFList(int nvar, std::vector<int>& dofs)\n{\n\t// make sure we start with an empty list\n\tdofs.clear();\n\n\t// get the variable\n\tVar& var = m_var[nvar];\n\n\t// fill the dof list\n\tint n = (int)var.m_dof.size();\n\tif (n == 0) return;\n\tdofs.resize(n);\n\tfor (int i = 0; i<n; ++i) dofs[i] = var.m_dof[i].ndof;\n}\n\n//-----------------------------------------------------------------------------\nbool DOFS::ParseDOFString(const char* sz, std::vector<int>& dofs, const char* szvar)\n{\n\tconst char* ch = sz;\n\tchar szdof[8] = {0}, *c = szdof;\n\tdo\n\t{\n\t\tif ((*ch==',')||(*ch==0))\n\t\t{\n\t\t\t*c = 0;\n\t\t\tint ndof = GetDOF(szdof, szvar);\n\t\t\tif (ndof != -1) dofs.push_back(ndof); else return false;\n\t\t\tc = szdof;\n\t\t\tif (*ch != 0) ch++; else ch = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (isspace(*ch) == 0) *c++ = *ch;\n\t\t\tch++;\n\t\t}\n\t}\n\twhile (ch);\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! get the size of the dof array of a variable\nint DOFS::GetVariableSize(const char* szvar)\n{\n\tVar* pvar = GetVariable(szvar);\n\tif (pvar) return (int)pvar->m_dof.size();\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n//! get the size of the dof array of a variable\nint DOFS::GetVariableSize(int nvar)\n{\n\tif ((nvar < 0) || (nvar >= (int) m_var.size())) return -1;\n\treturn (int)m_var[nvar].m_dof.size();\n}\n\n//-----------------------------------------------------------------------------\n//! get the size of the dof array of a variable\nint DOFS::GetVariableType(int nvar)\n{\n\tif ((nvar < 0) || (nvar >= (int) m_var.size())) return -1;\n\treturn m_var[nvar].m_ntype;\n}\n\n//-----------------------------------------------------------------------------\n//! return the total number of degrees of freedom\nint DOFS::GetTotalDOFS() const { return m_maxdofs; }\n\n//-----------------------------------------------------------------------------\n// Updates the DOF indices. \n// This is called after a dof is added. \nvoid DOFS::Update()\n{\n\tm_maxdofs = 0;\n\tint NVAR = (int) m_var.size();\n\tfor (int i=0; i<NVAR; ++i)\n\t{\n\t\tVar& var = m_var[i];\n\t\tint NDOF = (int) var.m_dof.size();\n\t\tfor (int j=0; j<NDOF; ++j)\n\t\t{\n\t\t\tDOF_ITEM& it = var.m_dof[j];\n\t\t\tit.ndof = m_maxdofs++;\n\t\t\tit.nvar = i;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nDOFS::DOF_ITEM* DOFS::GetDOFPtr(const char* szdof)\n{\n\tconst int NVAR = (int)m_var.size();\n\tfor (int i=0; i<NVAR; ++i)\n\t{\n\t\tVar& var = m_var[i];\n\t\tint ndof = (int)var.m_dof.size();\n\t\tfor (int j=0; j<ndof; ++j)\n\t\t{\n\t\t\tDOF_ITEM& it = var.m_dof[j];\n\t\t\tif (strcmp(it.sz, szdof) == 0) return &it;\n\t\t}\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::SetDOFName(const char* szvar, int n, const char* szname)\n{\n\tVar* pvar = GetVariable(szvar);\n\tif (pvar)\n\t{\n\t\tint nsize = (int) pvar->m_dof.size();\n\t\tif ((n>=0)&&(n<nsize))\n\t\t{\n\t\t\tDOF_ITEM& it = pvar->m_dof[n];\n\t\t\tit.SetName(szname);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::SetDOFName(int nvar, int n, const char* szname)\n{\n\tif ((nvar>=0)&&(nvar<(int)m_var.size()))\n\t{\n\t\tVar& var = m_var[nvar];\n\t\tint nsize = (int) var.m_dof.size();\n\t\tif ((n>=0)&&(n<nsize))\n\t\t{\n\t\t\tDOF_ITEM& it = var.m_dof[n];\n\t\t\tit.SetName(szname);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nconst char* DOFS::GetDOFName(int nvar, int n)\n{\n\tif ((nvar >= 0) && (nvar<(int)m_var.size()))\n\t{\n\t\tVar& var = m_var[nvar];\n\t\tint nsize = (int)var.m_dof.size();\n\t\tif ((n >= 0) && (n<nsize))\n\t\t{\n\t\t\tDOF_ITEM& it = var.m_dof[n];\n\t\t\treturn it.sz;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nconst char* DOFS::GetDOFName(int ndof)\n{\n\tint n = 0;\n\tfor (int i = 0; i < m_var.size(); ++i)\n\t{\n\t\tVar& var = m_var[i];\n\t\tfor (int j = 0; j < var.m_dof.size(); ++j)\n\t\t{\n\t\t\tDOF_ITEM& dof = var.m_dof[j];\n\t\t\tif (dof.ndof == ndof)\n\t\t\t{\n\t\t\t\treturn dof.sz;\n\t\t\t}\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid DOFS::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_maxdofs;\n\tar & m_var;\n}\n\n//-----------------------------------------------------------------------------\n//! set the interpolation order for a variable\nvoid DOFS::SetVariableInterpolationOrder(int nvar, int order)\n{\n\tm_var[nvar].m_order = order;\n}\n\n//-----------------------------------------------------------------------------\n// return the interpolation order of a variable\nint DOFS::GetVariableInterpolationOrder(int nvar)\n{\n\treturn m_var[nvar].m_order;\n}\n\n//-----------------------------------------------------------------------------\n// Find the variable from a dof\nint DOFS::FindVariableFromDOF(int ndof)\n{\n\tfor (int i=0; i<Variables(); ++i)\n\t{\n\t\tVar& v = m_var[i];\n\t\tsize_t dofs = v.m_dof.size();\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tif (v.m_dof[j].ndof == ndof) return i;\n\t\t}\n\t}\n\tassert(false);\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// return the interpolation order for a degree of freedom\nint DOFS::GetDOFInterpolationOrder(int ndof)\n{\n\t// find the variable for this dof\n\tint nvar = FindVariableFromDOF(ndof);\n\tassert(nvar >= 0);\n\n\treturn m_var[nvar].m_order;\n}\n"
  },
  {
    "path": "FECore/DOFS.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <string>\n#include \"fecore_api.h\"\n\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n// Degree of freedom types\n#define DOF_OPEN\t\t 0\t\t// the dof is open and will be given an equation number\n#define DOF_FIXED\t\t 1\t\t// the dof is fixed and will not be given an equation number\n#define DOF_PRESCRIBED\t 2\t\t// the dof is prescribed. It will be given a negative equation number (equation = - index - 2)\n\n//-----------------------------------------------------------------------------\n// Types of variables\n#define VAR_SCALAR\t0\t\t// scalar variable\n#define VAR_VEC2\t1\t\t// 2D vector\n#define VAR_VEC3\t2\t\t// 3D vector\n#define VAR_ARRAY\t3\t\t// a variable array of scalars\n\n//-----------------------------------------------------------------------------\n//! Class that manages the variables and degrees of freedoms.\nclass FECORE_API DOFS\n{\n\t// Class representing an individual degree of freedom\n\tclass DOF_ITEM\n\t{\n\tpublic:\n\t\tenum { MAX_DOF_NAME = 8 };\n\n\tpublic:\n\t\tDOF_ITEM();\n\t\tDOF_ITEM(const char* sz);\n\t\t~DOF_ITEM();\n\t\tDOF_ITEM(const DOF_ITEM& d);\n\t\tvoid operator = (const DOF_ITEM& d);\n\n\t\tvoid SetName(const char* szdof);\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\tpublic:\n\t\tchar\tsz[MAX_DOF_NAME];\t//!< symbol of variable\n\t\tint\t\tndof;\t\t\t\t//!< index of degree of freedom\n\t\tint\t\tnvar;\t\t\t\t//!< variable associated with this dof\n\t};\n\npublic:\n\t// A Variable is a logical grouping of degrees of freedoms.\n\t// (e.g. a displacement variable in 3D has 3 dofs.)\n\t// The order variable is the interpolation order that should be\n\t// assumed for this variable. By default, it is set to -1, which \n\t// should be interpreted as the interpolation order implied by the \n\t// nodes of the element type.\n\tclass Var\n\t{\n\tpublic:\n\t\tVar();\n\t\tVar(const Var& v);\n\t\tvoid operator = (const Var& v);\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\tpublic:\n\t\tint\t\tm_ntype;\t\t\t\t// type of variable\n\t\tint\t\tm_order;\t\t\t\t// interpolation order\n\t\tstd::string\t\t\t\tm_name;\t// variable name\n\t\tstd::vector<DOF_ITEM>\tm_dof;\t// list of dofs for this variable\n\t};\n\npublic:\n\t// constructors\n\tDOFS();\n\tDOFS(const DOFS& dofs);\n\tDOFS& operator = (const DOFS& dofs);\n    \n\t//! destructor\n\t~DOFS();\n\n\t//! Reset dofs\n\tvoid Reset();\n\npublic:\n\t//! Add a (empty) variable\n\t//! returns the new variable's index (or -1 if the variable already exists)\n\tint AddVariable(const char* szname, int ntype = VAR_SCALAR);\n\n\t//! Get number of variables\n\tint Variables() const;\n\n\t//! Get the index of a variable (returns -1 if the variable does not exist)\n\tint GetVariableIndex(const char* szname);\n\n\t//! Get the variable name\n\tstd::string GetVariableName(int n) const;\n\n\t// Add a degree of freedom\n\t// Can only be used on array variables.\n\t// returns >= 0 on success and -1 on failure (e.g. if the dof is already defined)\n\tint AddDOF(const char* szvar, const char* sz);\n\n\t// Add a degree of freedom\n\t// Can only be used on array variables.\n\t// returns >= 0 on success and -1 on failure (e.g. if the dof is already defined)\n\tint AddDOF(int nvar, const char* sz);\n\n\t//! return a dof index from the dof symbol\n\t//! this function returns -1 if the symbol is not recognized\n\tint GetDOF(const char* szdof, const char* varName = nullptr);\n\n\t//! return a list of dofs for a variable\n\tvoid GetDOFList(const char* varName, std::vector<int>& dofs);\n\n\t//! return a list of dofs for a variable\n\tvoid GetDOFList(int nvar, std::vector<int>& dofs);\n\n\t//! return a list of dofs from comma seperated list dof symbols\n\tbool ParseDOFString(const char* sz, std::vector<int>& dofs, const char* varName = nullptr);\n\n\t//! return a dof index from a variable\n\t//! this function returns -1 if the symbol is not recognized\n\tint GetDOF(const char* szvar, int n);\n\n\t//! return the index into the variable's dof list\n\tint GetIndex(const char* varName, const char* szdof);\n\n\t//! return a dof index from a variable index\n\t//! this function returns -1 if the symbol is not recognized\n\tint GetDOF(int nvar, int n);\n\n\t//! return the size of a variable\n\t//! (returns -1 if the variable is not defined)\n\tint GetVariableSize(const char* sz);\n\n\t//! return the size of a variable\n\t//! (returns -1 if the variable is not defined)\n\tint GetVariableSize(int nvar);\n\n\t//! return the type of a variable\n\tint GetVariableType(int nvar);\n\n\t//! return the total number of degrees of freedom\n\tint GetTotalDOFS() const;\n\n\t//! Set the name of a DOF\n\tvoid SetDOFName(const char* szvar, int n, const char* szname);\n\tvoid SetDOFName(int nvar, int n, const char* szname);\n\n\t//! Get a dof name\n\tconst char* GetDOFName(int nvar, int n);\n\tconst char* GetDOFName(int ndof);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar);\n\n\t//! set the interpolation order for a variable\n\tvoid SetVariableInterpolationOrder(int nvar, int order);\n\n\t// return the interpolation order of a variable\n\tint GetVariableInterpolationOrder(int nvar);\n\n\t// Find the variable from a dof\n\tint FindVariableFromDOF(int ndof);\n\n\t// return the interpolation order for a degree of freedom\n\tint GetDOFInterpolationOrder(int ndof);\n\nprivate:\n\tvoid Update();\n\tVar* GetVariable(const char* szvar);\n\tDOF_ITEM* GetDOFPtr(const char* szdof);\n    \nprivate:\n\tstd::vector<Var>\tm_var;\t\t//!< array of variables\n    int\t\t\t\t\tm_maxdofs;  //!< total number of nodal DOFS (i.e. size of FENode::m_val array)\n};\n\n"
  },
  {
    "path": "FECore/DataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"DataRecord.h\"\n#include \"DumpStream.h\"\n#include \"FEModel.h\"\n#include \"FEAnalysis.h\"\n#include \"log.h\"\n#include <sstream>\n\n//-----------------------------------------------------------------------------\nUnknownDataField::UnknownDataField(const char* sz) : std::runtime_error(sz)\n{\n}\n\n//-----------------------------------------------------------------------------\nDataRecord::DataRecord(FEModel* pfem, int ntype) : FECoreBase(pfem), m_type(ntype)\n{\n\tm_nid = 0;\n\tm_szname[0] = 0;\n\tm_szdata[0] = 0;\n\tm_szfmt[0] = 0;\n\n\tstrcpy(m_szdelim, \" \");\n\t\n\tm_bcomm = true;\n\n\tm_fp = 0;\n\tm_szfile[0] = 0;\n\n}\n\n//-----------------------------------------------------------------------------\nbool DataRecord::SetFileName(const char* szfile)\n{\n\tif (szfile == nullptr) return false;\n\n\tstrcpy(m_szfile, szfile);\n\tm_fp = fopen(szfile, \"wt\");\n\tif (m_fp == 0)\n\t{\n\t\tfeLogError(\"FAILED CREATING DATA FILE %s\\n\\n\", szfile);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nDataRecord::~DataRecord()\n{\n\tif (m_fp)\n\t{\n\t\tfclose(m_fp);\n\t\tm_fp = 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid DataRecord::SetName(const char* sz)\n{\n\tstrcpy(m_szname, sz);\n}\n\n//-----------------------------------------------------------------------------\nvoid DataRecord::SetDelim(const char* sz)\n{\n\tstrcpy(m_szdelim, sz);\n}\n\n//-----------------------------------------------------------------------------\nvoid DataRecord::SetFormat(const char* sz)\n{\n\tstrcpy(m_szfmt, sz);\n}\n\n//-----------------------------------------------------------------------------\nbool DataRecord::Initialize()\n{\n\tif (m_item.empty()) SelectAllItems();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nstd::string DataRecord::printToString(int i)\n{\n\tstd::stringstream ss;\n\tss.precision(12);\n\n\tss << m_item[i] << m_szdelim;\n\tint nd = Size();\n\tfor (int j = 0; j<nd; ++j)\n\t{\n\t\tdouble val = Evaluate(m_item[i], j);\n\t\tss << val;\n\t\tif (j != nd - 1) ss << m_szdelim;\n\t\telse ss << \"\\n\";\n\t}\n\n\treturn ss.str();\n}\n\n//-----------------------------------------------------------------------------\nstd::string DataRecord::printToFormatString(int i)\n{\n\tint ndata = Size();\n\tchar szfmt[MAX_STRING];\n\tstrcpy(szfmt, m_szfmt);\n\n\tstd::stringstream ss;\n\n\tint nitem = m_item[i];\n\tchar* sz = szfmt, *ch = 0;\n\tint j = 0;\n\tdo\n\t{\n\t\tch = strchr(sz, '%');\n\t\tif (ch)\n\t\t{\n\t\t\tif (ch[1] == 'i')\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tss << sz;\n\t\t\t\t*ch = '%'; sz = ch + 2;\n\t\t\t\tss << nitem;\n\t\t\t}\n\t\t\telse if (ch[1] == 'l')\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tss << sz;\n\t\t\t\t*ch = '%'; sz = ch + 2;\n\t\t\t\tss << (int)i + 1;\n\t\t\t}\n\t\t\telse if (ch[1] == 'g')\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tss << sz;\n\t\t\t\t*ch = '%'; sz = ch + 2;\n\t\t\t\tif (j<ndata)\n\t\t\t\t{\n\t\t\t\t\tdouble val = Evaluate(nitem, j++);\n\t\t\t\t\tss << val;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (ch[1] == 't')\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tss << sz;\n\t\t\t\t*ch = '%'; sz = ch + 2;\n\t\t\t\tss << \"\\t\";\n\t\t\t}\n\t\t\telse if (ch[1] == 'n')\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tss << \"%s\";\n\t\t\t\t*ch = '%'; sz = ch + 2;\n\t\t\t\tss << \"\\n\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*ch = 0;\n\t\t\t\tss << sz;\n\t\t\t\t*ch = '%'; sz = ch + 1;\n\t\t\t}\n\t\t}\n\t\telse { ss << sz; break; }\n\t} while (*sz);\n\tss << \"\\n\";\n\n\treturn ss.str();\n}\n\n//-----------------------------------------------------------------------------\nbool DataRecord::Write()\n{\n\tFEModel* fem = GetFEModel();\n\tint nstep = fem->GetCurrentStep()->m_ntimesteps;\n\tdouble ftime = fem->GetCurrentTime();\n\n\t// make a note in the log file\n\tfeLog(\"\\nData Record #%d\\n\", m_nid);\n\tfeLog(\"===========================================================================\\n\");\n\tfeLog(\"Step = %d\\n\", nstep);\n\tfeLog(\"Time = %.9lg\\n\", ftime);\n\tfeLog(\"Data = %s\\n\", m_szname);\n\n\t// write some comments\n\tFILE* fp = m_fp;\n\tif (fp && m_bcomm)\n\t{\n\t\t// we save the data in a seperate file\n\t\tfeLog(\"File = %s\\n\", m_szfile);\n\n\t\t// make a note in the data file\n\t\tfprintf(fp,\"*Step  = %d\\n\", nstep);\n\t\tfprintf(fp,\"*Time  = %.9lg\\n\", ftime);\n\t\tfprintf(fp,\"*Data  = %s\\n\", m_szname);\n\t}\n\n\t// save the data\n\tif (m_szfmt[0]==0)\n\t{\n\t\tfor (size_t i=0; i<m_item.size(); ++i)\n\t\t{\n\t\t\tstd::string out = printToString((int)i);\n\n\t\t\tif (fp) fprintf(fp, \"%s\", out.c_str());\n\t\t\telse feLog(out.c_str(),\"\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t// print using the format string\n\t\tfor (size_t i=0; i<m_item.size(); ++i)\n\t\t{\n\t\t\tstd::string out = printToFormatString((int)i);\n\n\t\t\tif (fp) fprintf(fp, \"%s\", out.c_str());\n\t\t\telse feLog(out.c_str(),\"\");\n\t\t}\n\t}\n\n\tif (fp) fflush(fp);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid DataRecord::SetItemList(const std::vector<int>& items)\n{\n\tm_item = items;\n}\n\n//-----------------------------------------------------------------------------\nvoid DataRecord::SetItemList(FEItemList* items, const std::vector<int>& selection)\n{\n\t// derived classes should override this\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\n\nvoid DataRecord::Serialize(DumpStream &ar)\n{\n\tif (ar.IsShallow()) return;\n\n\t// serialize data\n\tar & m_nid;\n\tar & m_szname;\n\tar & m_szdelim;\n\tar & m_szfile;\n\tar & m_bcomm;\n\tar & m_item;\n\tar & m_szdata;\n\n\t// when we're loading we need to reinitialize the file\n\tif (ar.IsLoading())\n\t{\n\t\tSetData(m_szdata);\n\n\t\tif (m_fp) fclose(m_fp);\n\t\tm_fp = 0;\n\t\tif (m_szfile[0] != 0)\n\t\t{\n\t\t\t// reopen data file for appending\n\t\t\tm_fp = fopen(m_szfile, \"a+\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/DataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <stdio.h>\n#include <string>\n#include <vector>\n#include <stdexcept>\n#include \"FECoreBase.h\"\n#include \"FELogData.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\n// forward declaration\nclass FEModel;\nclass DumpStream;\nclass FEItemList;\n\n//-----------------------------------------------------------------------------\nenum FEDataRecordType {\n\tFE_DATA_NODE = 0x01,\n\tFE_DATA_FACE,\n\tFE_DATA_ELEM,\n\tFE_DATA_RB,\n\tFE_DATA_NLC,\n\tFE_DATA_SURFACE,\n\tFE_DATA_DOMAIN,\n\tFE_DATA_MODEL\n};\n\n//-----------------------------------------------------------------------------\n// Exception thrown when parsing fails\nclass FECORE_API UnknownDataField : public std::runtime_error\n{\npublic:\n\tUnknownDataField(const char* sz);\n};\n\n//-----------------------------------------------------------------------------\n\nclass FECORE_API DataRecord : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FEDATARECORD_ID)\n\tFECORE_BASE_CLASS(DataRecord)\n\npublic:\n\tenum {MAX_DELIM=16, MAX_STRING=1024};\npublic:\n\tDataRecord(FEModel* pfem, int ntype);\n\tvirtual ~DataRecord();\n\n\tbool SetFileName(const char* szfile);\n\n\tbool Write();\n\n\tvoid SetItemList(const std::vector<int>& items);\n\tvirtual void SetItemList(FEItemList* itemList, const std::vector<int>& selection);\n\n\tvoid SetName(const char* sz);\n\tvoid SetDelim(const char* sz);\n\tvoid SetFormat(const char* sz);\n\tvoid SetComments(bool b) { m_bcomm = b; }\n\npublic:\n\tvirtual bool Initialize();\n\tvirtual double Evaluate(int item, int ndata) = 0;\n\tvirtual void SelectAllItems() = 0;\n\tvirtual void Serialize(DumpStream& ar);\n\tvirtual void SetData(const char* sz) = 0;\n\tvirtual int Size() const = 0;\n\nprivate:\n\tstd::string printToString(int i);\n\tstd::string printToFormatString(int i);\n\npublic:\n\tint\t\t\t\t\tm_nid;\t\t//!< ID of data record\n\tstd::vector<int>\tm_item;\t\t//!< item list\n\tint\t\t\t\t\tm_type;\t\t//!< type of data record\n\nprotected:\n\tbool\tm_bcomm;\t\t\t\t//!< export comments or not\n\tchar\tm_szname[MAX_STRING];\t//!< name of expression\n\tchar\tm_szdelim[MAX_DELIM];\t//!< data delimitor\n\tchar\tm_szdata[MAX_STRING];\t//!< data expression\n\tchar\tm_szfmt[MAX_STRING];\t//!< max format string\n\nprotected:\n\tchar\tm_szfile[MAX_STRING];\t//!< file name of data record\n\tFILE*\t\tm_fp;\n};\n"
  },
  {
    "path": "FECore/DataStore.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"DataStore.h\"\n#include \"log.h\"\n#include \"FEModel.h\"\n#include \"FEAnalysis.h\"\n\n//-----------------------------------------------------------------------------\nDataStore::DataStore()\n{\n}\n\n//-----------------------------------------------------------------------------\nDataStore::~DataStore()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid DataStore::Clear()\n{\n\tfor (size_t i=0; i<m_data.size(); ++i) delete m_data[i];\n\tm_data.clear();\n}\n\n//-----------------------------------------------------------------------------\n\nvoid DataStore::Write()\n{\n\tfor (size_t i=0; i<m_data.size(); ++i)\n\t{\n\t\tDataRecord& DR = *m_data[i];\n\t\tDR.Write();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nvoid DataStore::AddRecord(DataRecord* prec)\n{\n\tprec->m_nid = (int) m_data.size() + 1;\n\tm_data.push_back(prec);\n}\n"
  },
  {
    "path": "FECore/DataStore.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"DataRecord.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FECORE_API DataStore\n{\npublic:\n\tDataStore();\n\tvirtual ~DataStore();\n\n\tvoid Clear();\n\n\tvoid Write();\n\n\tvoid AddRecord(DataRecord* prec);\n\n\tint Size() { return (int) m_data.size(); }\n\n\tDataRecord* GetDataRecord(int i) { return m_data[i]; }\n\nprotected:\n\tstd::vector<DataRecord*>\tm_data;\t//!< the data records\n};\n"
  },
  {
    "path": "FECore/DenseMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"DenseMatrix.h\"\nusing namespace FECore;\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nDenseMatrix::DenseMatrix()\n{\n\tm_pr = 0;\n\tm_pd = 0;\n}\n\n//-----------------------------------------------------------------------------\nDenseMatrix::~DenseMatrix()\n{\n\tdelete [] m_pd; m_pd = 0;\n\tdelete [] m_pr; m_pr = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid DenseMatrix::Zero()\n{\n\tmemset(m_pd, 0, m_nsize*sizeof(double)); \n}\n\n//-----------------------------------------------------------------------------\nvoid DenseMatrix::Clear()\n{\n\tif (m_pd) delete [] m_pd; m_pd = 0;\n\tif (m_pr) delete [] m_pr; m_pr = 0;\n\n\tSparseMatrix::Clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid DenseMatrix::Create(SparseMatrixProfile& mp)\n{\n\tCreate(mp.Rows(), mp.Columns()); \n}\n\n//-----------------------------------------------------------------------------\n// Creat a dense matrix of size N x N\nvoid DenseMatrix::Create(int rows, int cols)\n{\n\tif ((rows != m_nrow) || (cols != m_ncol))\n\t{\n\t\tif (m_pd) delete [] m_pd;\n\t\tif (m_pr) delete [] m_pr;\n\n\t\tm_pd = new double[rows*cols];\n\t\tm_pr = new double*[rows];\n\n\t\tfor (int i=0; i<rows; ++i) m_pr[i] = m_pd + i*cols;\n\n\t\tm_nrow = rows;\n\t\tm_ncol = cols;\n\t\tm_nsize = rows*cols;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function assembles the local stiffness matrix\n//! into the global stiffness matrix which is in dense format\n//!\nvoid DenseMatrix::Assemble(const matrix& ke, const vector<int>& lm)\n{\n\tint I, J;\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tif ((I = lm[i])>=0)\n\t\t{\n\t\t\tfor (int j=0; j<M; ++j)\n\t\t\t{\n\t\t\t\tif ((J = lm[j]) >= 0) m_pr[I][J] += ke[i][j];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid DenseMatrix::Assemble(const matrix& ke, const vector<int>& LMi, const vector<int>& LMj)\n{\n\tint I, J;\n\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tif ((I = LMi[i])>=0)\n\t\t{\n\t\t\tfor (int j=0; j<M; ++j)\n\t\t\t{\n\t\t\t\tif ((J = LMj[j]) >= 0) m_pr[I][J] += ke[i][j];\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/DenseMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"SparseMatrix.h\"\n\nnamespace FECore {\n\n//=============================================================================\n//! This class implements a full matrix\n//! that is a matrix that stores all its elements.\n\nclass FECORE_API DenseMatrix : public SparseMatrix\n{\npublic:\n\t// con/de-structor\n\tDenseMatrix();\n\t~DenseMatrix();\n\n\t// create a matrix of particular size\n\tvoid Create(int rows, int cols);\n\n\t// retrieve matrix data\n\tdouble& operator () (int i, int j) { return m_pr[i][j]; }\n\npublic:\n\t// zero matrix elements\n\tvoid Zero() override;\n\n\t// create a matrix from a spares matrix profile\n\tvoid Create(SparseMatrixProfile& mp) override;\n\n\t// assemble matrix into sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\t// clear all data\n\tvoid Clear() override;\n\n\tbool check(int i, int j) override { return true; }\n\tvoid add(int i, int j, double v) override { m_pr[i][j] += v; }\n\tvoid set(int i, int j, double v) override { m_pr[i][j] = v; }\n\tdouble diag(int i) override { return m_pr[i][i]; }\n\nprotected:\n\tdouble*\t\tm_pd;\t//!< matrix values\n\tdouble**\tm_pr;\t//!< pointers to rows\n};\n\n}\n"
  },
  {
    "path": "FECore/DomainDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"DomainDataRecord.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModel.h\"\n#include \"FEDomain.h\"\n\n//-----------------------------------------------------------------------------\nvoid FEDomainDataRecord::SetData(const char* szexpr)\n{\n    char szcopy[MAX_STRING] = { 0 };\n    strcpy(szcopy, szexpr);\n    char* sz = szcopy, * ch;\n    m_Data.clear();\n    strcpy(m_szdata, szexpr);\n    do\n    {\n\t\tconst char* szparam = nullptr;\n        ch = strchr(sz, ';');\n        if (ch) *ch++ = 0;\n\n\t\t// see if parameters are defined\n\t\tchar* cl = strchr(sz, '(');\n\t\tif (cl)\n\t\t{\n\t\t\tchar* cr = strrchr(sz, ')');\n\t\t\tif (cr == nullptr) throw UnknownDataField(sz);\n\n\t\t\t*cl++ = 0;\n\t\t\t*cr = 0;\n\n\t\t\tcl = strchr (cl, '\\''); if (cl == nullptr) throw UnknownDataField(sz);\n\t\t\tcr = strrchr(cl, '\\''); if (cr == nullptr) throw UnknownDataField(sz);\n\n\t\t\t*cl++ = 0;\n\t\t\t*cr = 0;\n\n\t\t\tszparam = cl;\n\t\t}\n\n        FELogDomainData* pdata = fecore_new<FELogDomainData>(sz, GetFEModel());\n\t\tif (pdata)\n\t\t{\n\t\t\tm_Data.push_back(pdata);\n\t\t\tif (szparam)\n\t\t\t{\n\t\t\t\tvector<string> params; params.push_back(szparam);\n\t\t\t\tif (pdata->SetParameters(params) == false) throw UnknownDataField(sz);\n\t\t\t}\n\t\t}\n        else throw UnknownDataField(sz);\n        sz = ch;\n    } while (ch);\n}\n\n//-----------------------------------------------------------------------------\nFEDomainDataRecord::FEDomainDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_DOMAIN) {}\n\n//-----------------------------------------------------------------------------\nint FEDomainDataRecord::Size() const { return (int)m_Data.size(); }\n\n//-----------------------------------------------------------------------------\ndouble FEDomainDataRecord::Evaluate(int item, int ndata)\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    int nd = item - 1;\n    if ((nd < 0) || (nd >= mesh.Domains())) return 0;\n\n    FEDomain& dom = mesh.Domain(nd);\n    return m_Data[ndata]->value(dom);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainDataRecord::SetDomain(int domainIndex)\n{\n    m_item.clear();\n    m_item.push_back(domainIndex + 1);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainDataRecord::SelectAllItems()\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    int n = mesh.Domains();\n    m_item.resize(n);\n    for (int i = 0; i < n; ++i) m_item[i] = i + 1;\n}\n\n//============================================================================\nFELogAvgDomainData::FELogAvgDomainData(FEModel* pfem) : FELogDomainData(pfem) \n{\n    m_elemData = nullptr;\n}\n\nFELogAvgDomainData::~FELogAvgDomainData()\n{\n    if (m_elemData) delete m_elemData;\n    m_elemData = nullptr;\n}\n\nbool FELogAvgDomainData::SetParameters(std::vector<std::string>& params)\n{\n    if (params.size() != 1) return false;\n    std::string& v1 = params[0];\n    if (v1.empty()) return false;\n\n    m_elemData = fecore_new<FELogElemData>(v1.c_str(), GetFEModel());\n    if (m_elemData == nullptr) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogAvgDomainData::value(FEDomain& dom)\n{\n    if (m_elemData == nullptr) return 0.0;\n\n    double avg = 0.0;\n    const int NE = dom.Elements();\n    for (int i = 0; i < dom.Elements(); ++i)\n    {\n        FEElement& el = dom.ElementRef(i);\n        double eval = m_elemData->value(el);\n        avg += eval;\n    }\n    avg /= (double)NE;\n    return avg;\n}\n\n//============================================================================\nFELogPctDomainData::FELogPctDomainData(FEModel* pfem) : FELogDomainData(pfem)\n{\n    m_pct = 0.0;\n    m_elemData = nullptr;\n}\n\nFELogPctDomainData::~FELogPctDomainData()\n{\n    if (m_elemData) delete m_elemData;\n    m_elemData = nullptr;\n}\n\nbool FELogPctDomainData::SetParameters(std::vector<std::string>& params)\n{\n    if (params.size() != 2) return false;\n    std::string& v1 = params[0];\n    std::string& v2 = params[1];\n    if (v1.empty() || v2.empty()) return false;\n\n    m_elemData = fecore_new<FELogElemData>(v1.c_str(), GetFEModel());\n    if (m_elemData == nullptr) return false;\n\n    m_pct = atof(v2.c_str());\n    if ((m_pct < 0.0) || (m_pct > 1.0)) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogPctDomainData::value(FEDomain& dom)\n{\n    if (m_elemData == nullptr) return 0.0;\n\n    const int NE = dom.Elements();\n    vector<double> val(NE, 0.0);\n    for (int i = 0; i < NE; ++i)\n    {\n        FEElement& el = dom.ElementRef(i);\n        val[i] = m_elemData->value(el);\n    }\n\n    std::sort(val.begin(), val.end());\n\n    int n = (int) (m_pct * ((double)val.size() - 1.0));\n    return val[n];\n}\n\n\n//============================================================================\nFELogIntegralDomainData::FELogIntegralDomainData(FEModel* pfem) : FELogDomainData(pfem)\n{\n\tm_elemData = nullptr;\n}\n\nFELogIntegralDomainData::~FELogIntegralDomainData()\n{\n\tif (m_elemData) delete m_elemData;\n\tm_elemData = nullptr;\n}\n\nbool FELogIntegralDomainData::SetParameters(std::vector<std::string>& params)\n{\n\tif (params.size() != 1) return false;\n\tstd::string& v1 = params[0];\n\tif (v1.empty()) return false;\n\n\tm_elemData = fecore_new<FELogElemData>(v1.c_str(), GetFEModel());\n\tif (m_elemData == nullptr) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FELogIntegralDomainData::value(FEDomain& dom)\n{\n\tif (m_elemData == nullptr) return 0.0;\n\n\tFEMesh* mesh = dom.GetMesh();\n\n\tdouble sum = 0.0;\n\tconst int NE = dom.Elements();\n\tfor (int i = 0; i < dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tdouble eval = m_elemData->value(el);\n\t\tdouble vol = mesh->ElementVolume(el);\n\t\tsum += eval*vol;\n\t}\n\treturn sum;\n}\n"
  },
  {
    "path": "FECore/DomainDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n#include \"ElementDataRecord.h\"\n\nclass FEDomain;\n\n//-----------------------------------------------------------------------------\n//! Base class for domain log data\nclass FECORE_API FELogDomainData : public FELogData\n{\n    FECORE_SUPER_CLASS(FELOGDOMAINDATA_ID)\n    FECORE_BASE_CLASS(FELogDomainData)\n\npublic:\n    FELogDomainData(FEModel* fem) : FELogData(fem) {}\n    virtual ~FELogDomainData() {}\n    virtual double value(FEDomain& rc) = 0;\n\n    virtual bool SetParameters(std::vector<std::string>& params) { return false; }\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEDomainDataRecord : public DataRecord\n{\npublic:\n    FEDomainDataRecord(FEModel* pfem);\n    double Evaluate(int item, int ndata) override;\n    void SetData(const char* sz) override;\n    void SelectAllItems() override;\n    void SetDomain(int domainIndex);\n    int Size() const override;\n\nprivate:\n    vector<FELogDomainData*>\tm_Data;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FELogAvgDomainData : public FELogDomainData\n{\npublic:\n    FELogAvgDomainData(FEModel* pfem);\n    ~FELogAvgDomainData();\n    double value(FEDomain& rc) override;\n\n    bool SetParameters(std::vector<std::string>& params) override;\n\nprivate:\n    FELogElemData* m_elemData;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FELogPctDomainData : public FELogDomainData\n{\npublic:\n    FELogPctDomainData(FEModel* pfem);\n    ~FELogPctDomainData();\n    double value(FEDomain& rc) override;\n\n    bool SetParameters(std::vector<std::string>& params) override;\n\nprivate:\n    double          m_pct;\n    FELogElemData* m_elemData;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FELogIntegralDomainData : public FELogDomainData\n{\npublic:\n\tFELogIntegralDomainData(FEModel* pfem);\n\t~FELogIntegralDomainData();\n\tdouble value(FEDomain& rc) override;\n\n\tbool SetParameters(std::vector<std::string>& params) override;\n\nprivate:\n\tFELogElemData* m_elemData;\n};\n"
  },
  {
    "path": "FECore/DumpFile.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"DumpFile.h\"\n\nDumpFile::DumpFile(FEModel& fem) : DumpStream(fem)\n{\n\tm_fp = 0;\n\tm_size = 0;\n}\n\nDumpFile::~DumpFile()\n{\n\tClose();\n}\n\nbool DumpFile::Open(const char* szfile)\n{\n\tm_fp = fopen(szfile, \"rb\");\n\tif (m_fp == 0) return false;\n\n\tDumpStream::Open(false, false);\n\n\treturn true;\n}\n\nbool DumpFile::Create(const char* szfile)\n{\n\tm_fp = fopen(szfile, \"wb\");\n\tif (m_fp == 0) return false;\n\n\tDumpStream::Open(true, false);\n\n\treturn true;\n}\n\nbool DumpFile::Append(const char* szfile)\n{\n\tm_fp = fopen(szfile, \"a+b\");\n\tif (m_fp == 0) return false;\n\n\tDumpStream::Open(true, false);\n\n\treturn true;\n}\n\nvoid DumpFile::Close()\n{\n\tif (m_fp) fclose(m_fp); \n\tm_fp = 0;\n}\n\n//! write buffer to archive\nsize_t DumpFile::write(const void* pd, size_t size, size_t count)\n{\n\tassert(IsSaving());\n\tsize_t elemsWritten = fwrite(pd, size, count, m_fp);\n\tm_size += size * elemsWritten;\n\treturn size * elemsWritten;\n}\n\n//! read buffer from archive\nsize_t DumpFile::read(void* pd, size_t size, size_t count)\n{\n\tassert(IsLoading());\n\tsize_t elemsRead = fread(pd, size, count, m_fp);\n\treturn size * elemsRead;\n}\n\nbool DumpFile::EndOfStream() const\n{\n\treturn (feof(m_fp) != 0);\n}\n"
  },
  {
    "path": "FECore/DumpFile.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <stdio.h>\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\n//! Class for serializing data to a binary archive.\n\n//! This class is used to read data from or write\n//! data to a binary file. The class defines several operators to \n//! simplify in- and output.\n//! \\sa FEM::Serialize()\n\nclass FECORE_API DumpFile : public DumpStream\n{\npublic:\n\t// overloaded from DumpStream\n\tsize_t write(const void* pd, size_t size, size_t count) override;\n\tsize_t read(void* pd, size_t size, size_t count) override;\n\tvoid clear() override {}\n\tbool EndOfStream() const override;\n\npublic:\n\tDumpFile(FEModel& fem);\n\tvirtual ~DumpFile();\n\n\t//! Open archive for reading\n\tbool Open(const char* szfile);\n\n\t//! Open archive for writing\n\tbool Create(const char* szfile);\n\n\t//! Open archive for appending\n\tbool Append(const char* szfile);\n\n\t//! Close archive\n\tvoid Close();\n\n\t//! See if the archive is valid\n\tbool IsValid() { return (m_fp != 0); }\n\n\t//! Flush the archive\n\tvoid Flush() { fflush(m_fp); }\n\n\tsize_t Size() { return m_size; }\n\nprotected:\n\tFILE*\t\tm_fp;\t\t//!< The actual file pointer\n\tsize_t\t\tm_size;\n};\n"
  },
  {
    "path": "FECore/DumpMemStream.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"DumpMemStream.h\"\n#include <assert.h>\n#include <memory.h>\n\n//-----------------------------------------------------------------------------\nDumpMemStream::DumpMemStream(FEModel& fem) : DumpStream(fem)\n{\n\tm_pb = 0;\n\tm_pd = 0;\n\tm_nsize = 0;\n\tm_nreserved = 0;\n\tm_growsize = 16777216;\n\tm_growcounter = 0;\n\n\tOpen(true, true);\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpMemStream::clear()\n{\n\tm_pd = m_pb;\n\tm_nsize = 0;\n\n\t// Since we can't read from an empty stream\n\t// we restore write mode.\n\tOpen(true, true);\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpMemStream::Open(bool bsave, bool bshallow)\n{\n\tDumpStream::Open(bsave, bshallow);\n\tif (m_pb) set_position(0);\n}\n\n//-----------------------------------------------------------------------------\nbool DumpMemStream::EndOfStream() const\n{\n\treturn (bytesSerialized() >= m_nsize);\n}\n\n//-----------------------------------------------------------------------------\nDumpMemStream::~DumpMemStream()\n{\n\tdelete[] m_pb;\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpMemStream::set_position(size_t l)\n{\n\tassert((l >= 0) && (l < m_nreserved));\n\tm_pd = m_pb + l;\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpMemStream::grow_buffer(size_t l)\n{\n\tif (l <= 0) return;\n\n\tif (l > m_growsize) m_growsize = l;\n\n\tsize_t newSize = m_nreserved + m_growsize;\n\n\tchar* pnew = new char[newSize];\n\tif (m_pb)\n\t{\n\t\tmemcpy(pnew, m_pb, m_nreserved);\n\t\tdelete [] m_pb;\n\t}\n\tm_pb = pnew;\n\tm_pd = m_pb + m_nsize;\n\tm_nreserved = newSize;\n\tm_growsize = m_nreserved / 2;\n\n\tm_growcounter++;\n}\n\n//-----------------------------------------------------------------------------\nsize_t DumpMemStream::write(const void* pd, size_t size, size_t count)\n{\n\tassert(IsSaving());\n\tsize_t nsize = count*size;\n\tsize_t lpos = (size_t)(m_pd - m_pb);\n\tif (lpos + nsize > m_nreserved) grow_buffer(nsize);\n\tmemcpy(m_pd, pd, nsize);\n\n\tm_pd += nsize;\n\tlpos += nsize;\n\tif (lpos > m_nsize) m_nsize = lpos;\n\n\treturn nsize;\n}\n\n//-----------------------------------------------------------------------------\nsize_t DumpMemStream::read(void* pd, size_t size, size_t count)\n{\n\tassert(IsSaving()==false);\n\tsize_t nsize = count*size;\n\tmemcpy(pd, m_pd, nsize);\n\tm_pd += nsize;\n\treturn nsize;\n}\n"
  },
  {
    "path": "FECore/DumpMemStream.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\n//! The dump stream allows a class to record its internal state to a memory object\n//! so that it can be restored later.\n//! This can be used for storing the FEModel state during running restarts\nclass FECORE_API DumpMemStream : public DumpStream\n{\npublic:\n\tDumpMemStream(FEModel& fem);\n\t~DumpMemStream();\n\npublic: // overloaded from base class\n\tsize_t write(const void* pd, size_t size, size_t count);\n\tsize_t read(void* pd, size_t size, size_t count);\n\tvoid clear();\n\tvoid Open(bool bsave, bool bshallow);\n\n\tsize_t size() const { return m_nsize; }\n\tsize_t reserved() const { return m_nreserved; }\n\tbool EndOfStream() const;\n\nprotected:\n\tvoid grow_buffer(size_t l);\n\tvoid set_position(size_t l);\n\nprivate:\n\tchar*\tm_pb;\t\t\t//!< pointer to buffer\n\tchar*\tm_pd;\t\t\t//!< position to insert a new value\n\tsize_t\tm_nsize;\t\t//!< size of stream\n\tsize_t\tm_nreserved;\t//!< size of reserved buffer\n\tsize_t\tm_growsize;\t\t//!< size to grow the buffer\n\tsize_t\tm_growcounter;\t//!< number of times we had to grow the buffer\n};\n"
  },
  {
    "path": "FECore/DumpStream.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"DumpStream.h\"\n#include \"matrix.h\"\n\n//-----------------------------------------------------------------------------\nDumpStream::DumpStream(FEModel& fem) : m_fem(fem)\n{\n\tm_bsave = false;\n\tm_bshallow = false;\n\tm_bytes_serialized = 0;\n\tm_ptr_lock = false;\n\n#ifndef NDEBUG\n\tm_btypeInfo = false;\n#else\n\tm_btypeInfo = false;\n#endif\n}\n\n//-----------------------------------------------------------------------------\n//! See if the stream is used for input or output\nbool DumpStream::IsSaving() const { return m_bsave; }\n\n//-----------------------------------------------------------------------------\n//! See if the stream is used for input\nbool DumpStream::IsLoading() const { return !m_bsave; }\n\n//-----------------------------------------------------------------------------\n//! See if shallow flag is set\nbool DumpStream::IsShallow() const { return m_bshallow; }\n\n//-----------------------------------------------------------------------------\nDumpStream::~DumpStream()\n{\n\tm_ptrOut.clear();\n\tm_ptrIn.clear();\n\tm_bytes_serialized = 0;\n}\n\n//-----------------------------------------------------------------------------\n// set the write type info flag\nvoid DumpStream::WriteTypeInfo(bool b)\n{\n\tm_btypeInfo = b;\n}\n\n//-----------------------------------------------------------------------------\n// see if the stream has type info\nbool DumpStream::HasTypeInfo() const\n{\n\treturn m_btypeInfo;\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpStream::Open(bool bsave, bool bshallow)\n{\n\tm_bsave = bsave;\n\tm_bshallow = bshallow;\n\tm_bytes_serialized = 0;\n\tm_ptr_lock = false;\n\n\t// add the \"null\" pointer\n\tif (bsave)\n\t{\n\t\tm_ptrOut.clear();\n\t\tm_ptrOut[nullptr] = 0;\n\t}\n\telse\n\t{\n\t\tm_ptrIn.clear();\n\t\tm_ptrIn.push_back(nullptr);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpStream::check()\n{\n\tif (IsSaving())\n\t{\n\t\tm_bytes_serialized += write(&m_bytes_serialized, sizeof(m_bytes_serialized), 1);\n\t}\n\telse\n\t{\n\t\tsize_t nsize;\n\t\tsize_t tmp = read(&nsize, sizeof(m_bytes_serialized), 1);\n\t\tassert(m_bytes_serialized == nsize);\n\t\tif (m_bytes_serialized != nsize) throw DumpStream::ReadError();\n\t\tm_bytes_serialized += tmp;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpStream::LockPointerTable()\n{\n\tm_ptr_lock = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpStream::UnlockPointerTable()\n{\n\tm_ptr_lock = false;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator << (const char* sz) \n{ \n\tint n = (sz ? (int)strlen(sz) : 0);\n\tm_bytes_serialized += write(&n, sizeof(int), 1);\n\tif (sz && (n > 0)) m_bytes_serialized += write(sz, sizeof(char), n);\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator << (char* sz) \n{ \n\tint n = (sz ? (int)strlen(sz) : 0); \n\tm_bytes_serialized += write(&n, sizeof(int), 1);\n\tif (sz && (n > 0)) m_bytes_serialized += write(sz, sizeof(char), n);\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator<<(std::string& s)\n{\n\tconst char* sz = s.c_str();\n\tthis->operator<<(sz);\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator<<(const std::string& s)\n{\n\tconst char* sz = s.c_str();\n\tthis->operator<<(sz);\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator << (bool b)\n{\n\tint n = (b ? 1 : 0);\n\tm_bytes_serialized += write(&n, sizeof(n), 1);\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator << (int n)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_INT);\n\tm_bytes_serialized += write(&n, sizeof(int), 1);\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator << (const double a[3][3])\n{\n\tm_bytes_serialized += write(a, sizeof(double), 9);\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator >> (char* sz) \n{ \n\tint n;\n\tm_bytes_serialized += read(&n, sizeof(int), 1);\n\tif (n>0) m_bytes_serialized += read(sz, sizeof(char), n);\n\tsz[n] = 0;\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator >> (std::string& s)\n{\n\tint n;\n\tm_bytes_serialized += read(&n, sizeof(int), 1);\n\tchar* tmp = new char[n + 1];\n\tif (n > 0) m_bytes_serialized += read(tmp, sizeof(char), n);\n\ttmp[n] = 0;\n\ts = std::string(tmp);\n\tdelete [] tmp;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator >> (bool& b)\n{\n\tint n;\n\tm_bytes_serialized += read(&n, sizeof(int), 1);\n\tb = (n == 1);\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::operator >> (double a[3][3])\n{\n\tm_bytes_serialized += read(a, sizeof(double), 9);\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nint DumpStream::FindPointer(void* p)\n{\n\tassert(IsSaving());\n\tauto it = m_ptrOut.find(p);\n\tif (it != m_ptrOut.end()) return it->second;\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nvoid DumpStream::AddPointer(void* p)\n{\n\tif (m_ptr_lock) return;\n\tif (p == nullptr) { assert(false); return;\t}\n\n\tif (IsSaving())\n\t{\n\t\tassert(FindPointer(p) == -1);\n\t\tint id = (int)m_ptrOut.size();\n\t\tm_ptrOut[p] = id;\n\t}\n\telse\n\t{\n\t\tm_ptrIn.push_back(p);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::write_matrix(matrix& o)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_MATRIX);\n\n\t// don't write type info for all components\n\tbool oldTypeFlag = m_btypeInfo;\n\tm_btypeInfo = false;\n\n\tDumpStream& ar = *this;\n\tint nr = o.rows();\n\tint nc = o.columns();\n\tar << nr << nc;\n\tint nsize = nr*nc;\n\tif (nsize > 0)\n\t{\n\t\tvector<double> data;\n\t\tdata.reserve(nr*nc);\n\t\tfor (int i = 0; i < nr; ++i)\n\t\t\tfor (int j = 0; j < nc; ++j) data.push_back(o(i, j));\n\n\t\tar << data;\n\t}\n\n\tm_btypeInfo = oldTypeFlag;\n\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nDumpStream& DumpStream::read_matrix(matrix& o)\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_MATRIX);\n\n\t// don't read type info for all components\n\tbool oldTypeFlag = m_btypeInfo;\n\tm_btypeInfo = false;\n\n\tDumpStream& ar = *this;\n\tint nr = 0, nc = 0;\n\tar >> nr >> nc;\n\tint nsize = nr*nc;\n\tif (nsize > 0)\n\t{\n\t\to.resize(nr, nc);\n\t\tvector<double> data;\n\t\tar >> data;\n\t\tint n = 0;\n\t\tfor (int i = 0; i < nr; ++i)\n\t\t\tfor (int j = 0; j < nc; ++j) o(i, j) = data[n++];\n\t}\n\n\tm_btypeInfo = oldTypeFlag;\n\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\n// read the next block\nbool DumpStream::readBlock(DataBlock& d)\n{\n\t// make sure we have type info\n\tif (m_btypeInfo == false) return false;\n\n\t// see if we have reached the end of the stream\n\tif (EndOfStream()) return false;\n\n\t// read the data type\n\td.m_type = readType();\n\n\t// turn off type flag since we already read it\n\tm_btypeInfo = false;\n\n\t// read/allocate data\n\tswitch (d.m_type)\n\t{\n\tcase TypeID::TYPE_INT     : { int          v; read_raw(v); d.m_pd = new int         (v); } break;\n\tcase TypeID::TYPE_UINT    : { unsigned int v; read_raw(v); d.m_pd = new unsigned int(v); } break;\n\tcase TypeID::TYPE_FLOAT   : { float        v; read_raw(v); d.m_pd = new float       (v); } break;\n\tcase TypeID::TYPE_DOUBLE  : { double       v; read_raw(v); d.m_pd = new double      (v); } break;\n\tcase TypeID::TYPE_VEC2D   : { vec2d        v; read_raw(v); d.m_pd = new vec2d       (v); } break;\n\tcase TypeID::TYPE_VEC3D   : { vec3d        v; read_raw(v); d.m_pd = new vec3d       (v); } break;\n\tcase TypeID::TYPE_MAT2D   : { mat2d        v; read_raw(v); d.m_pd = new mat2d       (v); } break;\n\tcase TypeID::TYPE_MAT3D   : { mat3d        v; read_raw(v); d.m_pd = new mat3d       (v); } break;\n\tcase TypeID::TYPE_MAT3DD  : { mat3dd       v; read_raw(v); d.m_pd = new mat3dd      (v); } break;\n\tcase TypeID::TYPE_MAT3DS  : { mat3ds       v; read_raw(v); d.m_pd = new mat3ds      (v); } break;\n\tcase TypeID::TYPE_MAT3DA  : { mat3da       v; read_raw(v); d.m_pd = new mat3da      (v); } break;\n\tcase TypeID::TYPE_QUATD   : { quatd        v; read_raw(v); d.m_pd = new quatd       (v); } break;\n\tcase TypeID::TYPE_TENS3DS : { tens3ds      v; read_raw(v); d.m_pd = new tens3ds     (v); } break;\n\tcase TypeID::TYPE_TENS3DRS: { tens3drs     v; read_raw(v); d.m_pd = new tens3drs    (v); } break;\n\tcase TypeID::TYPE_MATRIX  : { matrix       v; read_raw(v); d.m_pd = new matrix      (v); } break;\n\tdefault:\n\t\tassert(false);\n\t\tm_btypeInfo = true;\n\t\treturn false;\n\t}\n\n\t// turn the type info flag back on\n\tm_btypeInfo = true;\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/DumpStream.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <string>\n#include <map>\n#include <string.h>\n#include \"vec3d.h\"\n#include \"mat3d.h\"\n#include \"quatd.h\"\n#include \"tens3d.h\"\n#include \"tens4d.h\"\n#include \"fecore_api.h\"\n#include \"FECoreKernel.h\"\n#include \"matrix.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass matrix;\n\n\n//-----------------------------------------------------------------------------\nenum TypeID\n{\n\tTYPE_UNKNOWN,\n\tTYPE_INT,\n\tTYPE_UINT,\n\tTYPE_FLOAT,\n\tTYPE_DOUBLE,\n\tTYPE_VEC2D,\n\tTYPE_VEC3D,\n\tTYPE_MAT2D,\n\tTYPE_MAT3D,\n\tTYPE_MAT3DD,\n\tTYPE_MAT3DS,\n\tTYPE_MAT3DA,\n\tTYPE_QUATD,\n\tTYPE_TENS3DS,\n\tTYPE_TENS3DRS,\n\tTYPE_MATRIX,\n    TYPE_TENS4D,\n    TYPE_TENS4DS,\n    TYPE_TENS4DMM\n};\n\ntypedef unsigned char uchar;\n\n//-----------------------------------------------------------------------------\n//! A dump stream is used to serialize data to and from a data stream.\n//! This is used in FEBio for running and cold restarts. \n//! This is just an abstract base class. Classes must be derived from this\n//! to implement the actual storage mechanism.\nclass FECORE_API DumpStream\n{\npublic: \n\tclass DataBlock\n\t{\n\tpublic:\n\t\tDataBlock() { m_type = TypeID::TYPE_UNKNOWN; m_pd = nullptr; }\n        ~DataBlock() {\n            if (m_pd) {\n                switch (m_type) {\n                    case TypeID::TYPE_INT: delete (int*) m_pd; break;\n                    case TypeID::TYPE_UINT: delete (unsigned int*) m_pd; break;\n                    case TypeID::TYPE_FLOAT: delete (float*) m_pd; break;\n                    case TypeID::TYPE_DOUBLE: delete (double*) m_pd; break;\n                    case TypeID::TYPE_VEC2D: delete (vec2d*) m_pd; break;\n                    case TypeID::TYPE_VEC3D: delete (vec3d*) m_pd; break;\n                    case TypeID::TYPE_MAT2D: delete (mat2d*) m_pd; break;\n                    case TypeID::TYPE_MAT3D: delete (mat3d*) m_pd; break;\n                    case TypeID::TYPE_MAT3DD: delete (mat3dd*) m_pd; break;\n                    case TypeID::TYPE_MAT3DS: delete (mat3ds*) m_pd; break;\n                    case TypeID::TYPE_MAT3DA: delete (mat3da*) m_pd; break;\n                    case TypeID::TYPE_QUATD: delete (quatd*) m_pd; break;\n                    case TypeID::TYPE_TENS3DS: delete (tens3ds*) m_pd; break;\n                    case TypeID::TYPE_TENS3DRS: delete (tens3drs*) m_pd; break;\n                    case TypeID::TYPE_MATRIX: delete (matrix*) m_pd; break;\n                    case TypeID::TYPE_TENS4D: delete (tens4d*) m_pd; break;\n                    case TypeID::TYPE_TENS4DS: delete (tens4ds*) m_pd; break;\n                    case TypeID::TYPE_TENS4DMM: delete (tens4dmm*) m_pd; break;\n                    default: break;\n                }\n            }\n            m_pd = nullptr;\n        }\n\n\t\tint dataType() const { return m_type; }\n\n\t\ttemplate <typename T> T value() { return *((T*)(m_pd)); }\n\n\tprivate:\n\t\tint\t\tm_type;\n\t\tvoid*\tm_pd;\n\n\t\tfriend class DumpStream;\n\t};\n\npublic:\n\t// This class is thrown when an error occurs reading the dumpfile\n\tclass ReadError{};\n\npublic:\n\t//! constructor\n\tDumpStream(FEModel& fem);\n\n\t//! destructor\n\tvirtual ~DumpStream();\n\n\t//! See if the stream is used for input or output\n\tbool IsSaving() const;\n\n\t//! See if the stream is used for input\n\tbool IsLoading() const;\n\n\t//! See if shallow flag is set\n\tbool IsShallow() const;\n\n\t// open the stream\n\tvirtual void Open(bool bsave, bool bshallow);\n\n\t// get the FE model\n\tFEModel& GetFEModel() { return m_fem; }\n\n\t// set the write type info flag\n\tvoid WriteTypeInfo(bool b);\n\n\t// see if the stream has type info\n\tbool HasTypeInfo() const;\n\n\t// return total nr of bytes that was serialized\n\tsize_t bytesSerialized() const { return m_bytes_serialized; }\n\npublic:\n\t// read the next block\n\tbool readBlock(DataBlock& d);\n\npublic:\n\t// These functions must be overloaded by derived classes\n\t// and should return the number of bytes serialized\n\tvirtual size_t write(const void* pd, size_t size, size_t count) = 0;\n\tvirtual size_t read(void* pd, size_t size, size_t count) = 0;\n\n\t// override function to indicate the end of stream was reached (while reading)\n\tvirtual bool EndOfStream() const = 0;\n\n\t// additional function that need to be overridden\n\tvirtual void clear() = 0;\n\n\tvoid check();\n\n\tvoid LockPointerTable();\n\tvoid UnlockPointerTable();\n\npublic:\n\t// input-output operators (will call correct operator depending on input or output mode)\n\ttemplate <typename T> DumpStream& operator & (T& o);\n\npublic: // output operators\n\tDumpStream& operator << (const char* sz);\n\tDumpStream& operator << (char* sz);\n\tDumpStream& operator << (const double a[3][3]);\n\tDumpStream& operator << (std::string& s);\n\tDumpStream& operator << (const std::string& s);\n\tDumpStream& operator << (bool b);\n\tDumpStream& operator << (int n);\n\ttemplate <typename T> DumpStream& operator << (T& o);\n\ttemplate <typename T> DumpStream& operator << (std::vector<T>& o);\n\ttemplate <typename A, typename B> DumpStream& operator << (std::map<A, B>& o);\n\ttemplate <typename T, std::size_t N> DumpStream& operator << (T(&a)[N]);\n\ttemplate <typename T> DumpStream& operator << (T* &a);\n\ttemplate <typename T> DumpStream& operator << (std::vector<T*>& o);\n\n\ttemplate <typename T> DumpStream& write_raw(const T& o);\n\npublic: // input operators\n\tDumpStream& operator >> (char* sz);\n\tDumpStream& operator >> (double a[3][3]);\n\tDumpStream& operator >> (std::string& s);\n\tDumpStream& operator >> (bool& b);\n\ttemplate <typename T> DumpStream& operator >> (T& o);\n\ttemplate <typename T> DumpStream& operator >> (std::vector<T>& o);\n\ttemplate <typename A, typename B> DumpStream& operator >> (std::map<A, B>& o);\n\ttemplate <typename T, std::size_t N> DumpStream& operator >> (T(&a)[N]);\n\ttemplate <typename T> DumpStream& operator >> (T* &a);\n\ttemplate <typename T> DumpStream& operator >> (std::vector<T*>& o);\n\n\ttemplate <typename T> DumpStream& read_raw(T& o);\n\nprivate:\n\tint FindPointer(void* p);\n\tvoid AddPointer(void* p);\n\n\tDumpStream& write_matrix(matrix& o);\n\tDumpStream& read_matrix(matrix& o);\n\n\tvoid writeType(uchar type)\n\t{\n\t\tm_bytes_serialized += write(&type, sizeof(type), 1);\n\t}\n\tuchar readType()\n\t{\n\t\tuchar type;\n\t\tm_bytes_serialized += read(&type, sizeof(type), 1);\n\t\treturn type;\n\t}\n\tbool readType(uchar type)\n\t{\n\t\tuchar typeRead = readType();\n\t\tassert(type == typeRead);\n\t\treturn (type == typeRead);\n\t}\n\nprivate:\n\tbool\t\tm_bsave;\t//!< true if output stream, false for input stream\n\tbool\t\tm_bshallow;\t//!< if true only shallow data needs to be serialized\n\tbool\t\tm_btypeInfo;\t//!< write/read type info\n\tFEModel&\tm_fem;\t\t//!< the FE Model that is being serialized\n\n\tsize_t\tm_bytes_serialized;\t//!< number or bytes serialized\n\n\tbool\t\t\t\t\tm_ptr_lock;\n\tstd::map<void*, int>\tm_ptrOut;\t// used for writing\n\tstd::vector<void*>\t\tm_ptrIn;\t// user for reading\n};\n\ntemplate <typename T> class typeInfo {};\ntemplate <> class typeInfo<int>          { public: static uchar typeId() { return (uchar)TypeID::TYPE_INT;     }};\ntemplate <> class typeInfo<unsigned int> { public: static uchar typeId() { return (uchar)TypeID::TYPE_UINT;    }};\ntemplate <> class typeInfo<float>        { public: static uchar typeId() { return (uchar)TypeID::TYPE_FLOAT;   }};\ntemplate <> class typeInfo<double>       { public: static uchar typeId() { return (uchar)TypeID::TYPE_DOUBLE;  }};\ntemplate <> class typeInfo<vec2d>        { public: static uchar typeId() { return (uchar)TypeID::TYPE_VEC2D;   }};\ntemplate <> class typeInfo<vec3d>        { public: static uchar typeId() { return (uchar)TypeID::TYPE_VEC3D;   }};\ntemplate <> class typeInfo<mat2d>        { public: static uchar typeId() { return (uchar)TypeID::TYPE_MAT3D;   }};\ntemplate <> class typeInfo<mat3d>        { public: static uchar typeId() { return (uchar)TypeID::TYPE_MAT3D;   }};\ntemplate <> class typeInfo<mat3dd>       { public: static uchar typeId() { return (uchar)TypeID::TYPE_MAT3DD;  }};\ntemplate <> class typeInfo<mat3ds>       { public: static uchar typeId() { return (uchar)TypeID::TYPE_MAT3DS;  }};\ntemplate <> class typeInfo<mat3da>       { public: static uchar typeId() { return (uchar)TypeID::TYPE_MAT3DA;  }};\ntemplate <> class typeInfo<quatd>        { public: static uchar typeId() { return (uchar)TypeID::TYPE_QUATD;   }};\ntemplate <> class typeInfo<tens3ds>      { public: static uchar typeId() { return (uchar)TypeID::TYPE_TENS3DS; }};\ntemplate <> class typeInfo<tens3drs>     { public: static uchar typeId() { return (uchar)TypeID::TYPE_TENS3DRS;}};\ntemplate <> class typeInfo<matrix>       { public: static uchar typeId() { return (uchar)TypeID::TYPE_MATRIX;  }};\ntemplate <> class typeInfo<tens4d>       { public: static uchar typeId() { return (uchar)TypeID::TYPE_TENS4D;  }};\ntemplate <> class typeInfo<tens4ds>      { public: static uchar typeId() { return (uchar)TypeID::TYPE_TENS4DS; }};\ntemplate <> class typeInfo<tens4dmm>     { public: static uchar typeId() { return (uchar)TypeID::TYPE_TENS4DMM;}};\n\ntemplate <typename T> DumpStream& DumpStream::write_raw(const T& o)\n{\n\tif (m_btypeInfo) writeType(typeInfo<T>::typeId());\n\tm_bytes_serialized += write(&o, sizeof(T), 1);\n\treturn *this;\n}\n\ntemplate <typename T> DumpStream& DumpStream::read_raw(T& o)\n{\n\tif (m_btypeInfo) readType(typeInfo<T>::typeId());\n\tm_bytes_serialized += read(&o, sizeof(T), 1);\n\treturn *this;\n}\n\ntemplate <typename T> inline DumpStream& DumpStream::operator & (T& o)\n{\n\tif (IsSaving()) (*this) << o; else (*this) >> o;\n\treturn *this;\n}\n\ntemplate <> inline DumpStream& DumpStream::operator << (int&          o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (unsigned int& o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (double&   o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (vec2d&    o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (vec3d&    o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (quatd&    o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (mat2d&    o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (mat3d&    o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (mat3ds&   o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (mat3dd&   o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (mat3da&   o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (tens3ds&  o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (tens3drs& o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (matrix&   o) { return write_matrix(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (tens4d&   o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (tens4ds&  o) { return write_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator << (tens4dmm& o) { return write_raw(o); }\n\ntemplate <> inline DumpStream& DumpStream::operator >> (int&          o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (unsigned int& o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (double&   o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (vec2d&    o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (vec3d&    o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (quatd&    o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (mat2d&    o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (mat3d&    o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (mat3ds&   o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (mat3dd&   o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (mat3da&   o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (tens3ds&  o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (tens3drs& o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (matrix&   o) { return read_matrix(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (tens4d&   o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (tens4ds&  o) { return read_raw(o); }\ntemplate <> inline DumpStream& DumpStream::operator >> (tens4dmm& o) { return read_raw(o); }\n\ntemplate <typename T> inline DumpStream& DumpStream::operator << (T& o)\n{\n\tAddPointer((void*)&o);\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\to.Serialize(*this);\n\tcheck();\n\treturn *this;\n}\n\ntemplate <typename T> inline DumpStream& DumpStream::operator >> (T& o)\n{\n\tAddPointer((void*)&o);\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\to.Serialize(*this);\n\tcheck();\n\treturn *this;\n}\n\ntemplate <typename T> inline DumpStream& DumpStream::operator << (std::vector<T>& o)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\tint N = (int) o.size();\n\tm_bytes_serialized += write(&N, sizeof(int), 1);\n\tfor (int i=0; i<N; ++i) (*this) << o[i];\n\treturn *this;\n}\n\ntemplate <typename T> inline DumpStream& DumpStream::operator >> (std::vector<T>& o)\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\tDumpStream& This = *this;\n\tint N = 0;\n\tm_bytes_serialized += read(&N, sizeof(int), 1);\n\tif (N > 0)\n\t{\n\t\to.resize(N);\n\t\tfor (int i = 0; i<N; ++i) (*this) >> o[i];\n\t}\n\treturn This;\n}\n\ntemplate <> inline DumpStream& DumpStream::operator << (std::vector<double>& o)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\tint N = (int)o.size();\n\tm_bytes_serialized += write(&N, sizeof(int), 1);\n\twrite(o.data(), sizeof(double), N);\n\treturn *this;\n}\n\ntemplate <> inline DumpStream& DumpStream::operator >> (std::vector<double>& o)\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\tDumpStream& This = *this;\n\tint N = 0;\n\tm_bytes_serialized += read(&N, sizeof(int), 1);\n\tif (N > 0)\n\t{\n\t\to.resize(N);\n\t\tread(o.data(), sizeof(double), N);\n\t}\n\treturn This;\n}\n\ntemplate <> inline DumpStream& DumpStream::operator << (std::vector<bool>& o)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\tDumpStream& This = *this;\n\tint N = (int) o.size();\n\tm_bytes_serialized += write(&N, sizeof(int), 1);\n\tfor (int i=0; i<N; ++i) \n\t{\n\t\tbool b = o[i];\n\t\tThis << b;\n\t}\n\treturn This;\n}\n\ntemplate <> inline DumpStream& DumpStream::operator >> (std::vector<bool>& o)\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\tDumpStream& This = *this;\n\tint N;\n\tm_bytes_serialized += read(&N, sizeof(int), 1);\n\tif (N > 0)\n\t{\n\t\to.resize(N);\n\t\tfor (int i=0; i<N; ++i) \n\t\t{\n\t\t\tbool b;\n\t\t\tThis >> b;\n\t\t\to[i] = b;\n\t\t}\n\t}\n\treturn This;\n}\n\ntemplate <typename A, typename B> DumpStream& DumpStream::operator << (std::map<A, B>& o)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\tDumpStream& ar = *this;\n\tint N = (int)o.size();\n\tar << N;\n    for (typename std::map<A, B>::iterator it = o.begin(); it != o.end(); ++it)\n\t{\n\t\tconst A& a = it->first;\n\t\tB& b = it->second;\n\t\tar << a << b;\n\t}\n\treturn ar;\n}\n\ntemplate <typename A, typename B> DumpStream& DumpStream::operator >> (std::map<A, B>& o)\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\tDumpStream& ar = *this;\n\tint N = 0;\n\tar >> N;\n\to.clear();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tA a;\n\t\tB b;\n\t\tar >> a >> b;\n\t\to[a] = b;\n\t}\n\treturn ar;\n}\n\ntemplate <typename T, std::size_t N> DumpStream& DumpStream::operator << (T(&a)[N])\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\tfor (int i = 0; i < N; ++i) (*this) << a[i];\n\treturn *this;\n}\n\ntemplate <typename T, std::size_t N> DumpStream& DumpStream::operator >> (T(&a)[N])\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\tfor (int i = 0; i < N; ++i) (*this) >> a[i];\n\treturn *this;\n}\n\ntemplate <typename T> DumpStream& DumpStream::operator << (T* &a)\n{\n\tDumpStream& ar = *this;\n\n\t// see if we already stored this pointer \n\tint pid = FindPointer((void*)a);\n\tar << pid;\n\tif (pid != -1) return ar;\n\n\t// store the pointer in the table\n\tAddPointer((void*)a);\n\n\t// If we are storing a deep copy we need to store class info \n\t// so that we can reinstantiate the class\n\tif (ar.IsShallow() == false)\n\t{\n\t\t// store the class info\n\t\tT::SaveClass(*this, a);\n\t}\n\n\t// serialize the object (assuming it has a Serialize member)\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\ta->Serialize(*this);\n\n\treturn *this;\n}\n\ntemplate <typename T> DumpStream& DumpStream::operator << (std::vector<T*>& o)\n{\n\tif (m_btypeInfo) writeType(TypeID::TYPE_UNKNOWN);\n\tsize_t N = o.size();\n\tm_bytes_serialized += write(&N, sizeof(size_t), 1);\n\tfor (size_t i = 0; i < N; ++i)\n\t{\n\t\t(*this) << o[i];\n\t}\n\treturn *this;\n}\n\ntemplate <typename T> DumpStream& DumpStream::operator >> (T* &a)\n{\n\tDumpStream& ar = *this;\n\n\t// get the pointer id\n\tint pid;\n\tar >> pid;\n\tif (pid != -1)\n\t{\n\t\ta = (T*)(m_ptrIn[pid]);\n\t\treturn ar;\n\t}\n\n\t// read class identifier and instatiate class\n\tif (ar.IsShallow() == false)\n\t{\n\t\ta = dynamic_cast<T*>(T::LoadClass(ar, a));\n\t}\n\n\t// store the pointer\n\tAddPointer((void*)a);\n\n\t// serialize the object\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\ta->Serialize(*this);\n\n\treturn *this;\n}\n\ntemplate <typename T> DumpStream& DumpStream::operator >> (std::vector<T*>& o)\n{\n\tif (m_btypeInfo) readType(TypeID::TYPE_UNKNOWN);\n\tsize_t N = 0;\n\tm_bytes_serialized += read(&N, sizeof(size_t), 1);\n\tif (N > 0)\n\t{\n\t\to.resize(N);\n\t\tfor (size_t i = 0; i < N; ++i)\n\t\t{\n\t\t\t(*this) >> o[i];\n\t\t}\n\t}\n\treturn *this;\n}\n"
  },
  {
    "path": "FECore/EigenSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"EigenSolver.h\"\n#include \"fecore_enum.h\"\n\nEigenSolver::EigenSolver(FEModel* fem) : FECoreBase(fem)\n{\n\n}\n\nbool EigenSolver::Init()\n{\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/EigenSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include <vector>\n#include \"matrix.h\"\n\nclass SparseMatrix;\n\nclass FECORE_API EigenSolver : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FEEIGENSOLVER_ID)\n\tFECORE_BASE_CLASS(EigenSolver)\n\npublic:\n\tEigenSolver(FEModel* fem);\n\n\tvirtual bool Init();\n\n\tvirtual bool EigenSolve(SparseMatrix* A, SparseMatrix* B, std::vector<double>& eigenValues, matrix& eigenVectors) = 0;\n};\n"
  },
  {
    "path": "FECore/ElementDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"ElementDataRecord.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModel.h\"\n#include \"FEDomain.h\"\n#include \"FELogElemMath.h\"\n\n//-----------------------------------------------------------------------------\nElementDataRecord::ElementDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_ELEM)\n{\n\tm_offset = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid ElementDataRecord::SetData(const char *szexpr)\n{\n\tchar szcopy[MAX_STRING] = {0};\n\tstrcpy(szcopy, szexpr);\n\tchar* sz = szcopy, *ch;\n\tm_Data.clear();\n\tstrcpy(m_szdata, szexpr);\n\tdo\n\t{\n\t\tch = strchr(sz, ';');\n\t\tif (ch) *ch++ = 0;\n\t\tFELogElemData* pdata = nullptr;\n\t\tif (sz && sz[0] == '=')\n\t\t{\n\t\t\tFELogElemMath* logMath = fecore_alloc(FELogElemMath, GetFEModel());\n\t\t\tif (logMath)\n\t\t\t{\n\t\t\t\tstring smath(sz + 1);\n\t\t\t\tif (logMath->SetExpression(smath))\n\t\t\t\t{\n\t\t\t\t\tpdata = logMath;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tpdata = fecore_new<FELogElemData>(sz, GetFEModel());\n\t\tif (pdata) m_Data.push_back(pdata);\n\t\telse throw UnknownDataField(sz);\n\t\tsz = ch;\n\t}\n\twhile (ch);\n}\n\n//-----------------------------------------------------------------------------\ndouble ElementDataRecord::Evaluate(int item, int ndata)\n{\n\t// make sure we have an ELT\n\tif (m_ELT.empty()) BuildELT();\n\n\t// find the element\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tint index = item - m_offset;\n\tif ((index >= 0) && (index < m_ELT.size()))\n\t{\n\t\tELEMREF& e = m_ELT[index];\n\t\tassert((e.ndom != -1) && (e.nid != -1));\n\t\tFEElement* pe = &mesh.Domain(e.ndom).ElementRef(e.nid); assert(pe);\n\t\tassert(pe->GetID() == item);\n\n\t\t// get the element value\n\t\treturn m_Data[ndata]->value(*pe);\n\t}\n\telse return 0.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid ElementDataRecord::BuildELT()\n{\n\tm_ELT.clear();\n\tFEMesh& m = GetFEModel()->GetMesh();\n\n\t// find the min, max ID\n\tint minID = -1, maxID = 0;\n\tfor (int i = 0; i<m.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = m.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j<NE; ++j)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tint id = el.GetID(); assert(id > 0);\n\n\t\t\tif ((minID < 0) || (id < minID)) minID = id;\n\t\t\tif (id > maxID) maxID = id;\n\t\t}\n\t}\n\n\t// allocate lookup table\n\tint nsize = maxID - minID + 1;\n\tm_ELT.resize(nsize);\n\tfor (int i=0; i<nsize; ++i) \n\t{\n\t\tm_ELT[i].ndom = -1;\n\t\tm_ELT[i].nid  = -1;\n\t}\n\n\t// build lookup table\n\tm_offset = minID;\n\tfor (int i=0; i<m.Domains(); ++i)\n\t{\n\t\tFEDomain& d = m.Domain(i);\n\t\tint ne = d.Elements();\n\t\tfor (int j=0; j<ne; ++j)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(j);\n\t\t\tint id = el.GetID() - minID;\n\t\t\tm_ELT[id].ndom = i;\n\t\t\tm_ELT[id].nid  = j;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nint ElementDataRecord::Size() const\n{ \n\treturn (int)m_Data.size(); \n}\n\n//-----------------------------------------------------------------------------\nvoid ElementDataRecord::SelectAllItems()\n{\n\tFEMesh& m = GetFEModel()->GetMesh();\n\tint n = m.Elements();\n\tm_item.resize(n);\n\tn = 0;\n\tfor (int i=0; i<m.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = m.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j=0; j<NE; ++j, n++)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tm_item[n] = el.GetID();\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This sets the item list based on a element set.\nvoid ElementDataRecord::SetElementSet(FEElementSet* pg)\n{\n\tint n = pg->Elements();\n\tassert(n);\n\tm_item.resize(n);\n\tfor (int i=0; i<n; ++i) m_item[i] = (*pg)[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid ElementDataRecord::SetItemList(FEItemList* itemList, const vector<int>& selection)\n{\n\tFEElementSet* pg = dynamic_cast<FEElementSet*>(itemList);\n\tassert(selection.empty());\n\tSetElementSet(pg);\n}\n"
  },
  {
    "path": "FECore/ElementDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n#include \"FELogElemData.h\"\n\nclass FEElement;\nclass FEElementSet;\n\nclass FECORE_API ElementDataRecord : public DataRecord\n{\n\tstruct ELEMREF\n\t{\n\t\tint\tndom;\n\t\tint\tnid;\n\t};\n\npublic:\n\tElementDataRecord(FEModel* pfem);\n\tdouble Evaluate(int item, int ndata) override;\n\tvoid SetData(const char* sz) override;\n\tvoid SelectAllItems() override;\n\tint Size() const override;\n\tvoid SetElementSet(FEElementSet* pg);\n\n\tvoid SetItemList(FEItemList* itemList, const vector<int>& selection) override;\n\n\tusing DataRecord::SetItemList;\n\nprotected:\n\tvoid BuildELT();\n\nprotected:\n\tvector<ELEMREF>\tm_ELT;\n\tint\t\t\t\tm_offset;\n\tvector<FELogElemData*>\tm_Data;\n};\n"
  },
  {
    "path": "FECore/FEAnalysis.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEAnalysis.h\"\n#include \"FEModel.h\"\n#include \"FECoreKernel.h\"\n#include \"log.h\"\n#include \"DOFS.h\"\n#include \"MatrixProfile.h\"\n#include \"FEBoundaryCondition.h\"\n#include \"DumpMemStream.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"FEShellDomain.h\"\n#include \"FEMeshAdaptor.h\"\n#include \"FETimeStepController.h\"\n#include \"FEModule.h\"\n\n//---------------------------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEAnalysis, FECoreBase)\n\n\tBEGIN_PARAM_GROUP(\"Analysis\");\n\t\tADD_PARAMETER(m_nanalysis, \"analysis\");// , 0, \"STATIC\\0DYNAMIC\\0STEADY-STATE\\0TRANSIENT=1\\0\");\n\tEND_PARAM_GROUP();\n\n\tBEGIN_PARAM_GROUP(\"Time stepping\");\n\t\tADD_PARAMETER(m_ntime       , FE_RANGE_GREATER_OR_EQUAL(-1) , \"time_steps\");\n\t\tADD_PARAMETER(m_dt0         , FE_RANGE_GREATER_OR_EQUAL(0.0), \"step_size\")->setUnits(UNIT_TIME)->SetFlags(0);\n\t\tADD_PARAMETER(m_final_time  , FE_RANGE_GREATER_OR_EQUAL(0.0), \"final_time\")->SetFlags(FE_PARAM_HIDDEN);\n\tEND_PARAM_GROUP();\n\n\tBEGIN_PARAM_GROUP(\"Output\");\n\t\tADD_PARAMETER(m_bplotZero, \"plot_zero_state\");\n\t\tADD_PARAMETER(m_nplotRange, 2, \"plot_range\");\n\t\tADD_PARAMETER(m_nplot, \"plot_level\", 0, \"PLOT_NEVER\\0PLOT_MAJOR_ITRS\\0PLOT_MINOR_ITRS\\0PLOT_MUST_POINTS\\0PLOT_FINAL\\0PLOT_AUGMENTATIONS\\0PLOT_STEP_FINAL\\0\");\n\t\tADD_PARAMETER(m_noutput, \"output_level\", 0, \"OUTPUT_NEVER\\0OUTPUT_MAJOR_ITRS\\0OUTPUT_MINOR_ITRS\\0OUTPUT_MUST_POINTS\\0OUTPUT_FINAL\\0\");\n\t\tADD_PARAMETER(m_nplot_stride, \"plot_stride\");\n\t\tADD_PARAMETER(m_noutput_stride, \"output_stride\");\n\tEND_PARAM_GROUP();\n\n\tBEGIN_PARAM_GROUP(\"Advanced settings\");\n\t\tADD_PARAMETER(m_badaptorReSolve, \"adaptor_re_solve\")->setLongName(\"re-solve after adaptation\");\n\tEND_PARAM_GROUP();\n\n\tADD_PROPERTY(m_timeController, \"time_stepper\", FEProperty::Preferred)->SetDefaultType(\"default\").SetLongName(\"Auto time stepper\");\n\tFEProperty* solver = ADD_PROPERTY(m_psolver, \"solver\");\n\n\t// the default type of the solver should match the active module's name\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tFEModule* mod = fecore.GetActiveModule();\n\tif (mod)\n\t{\n\t\tconst char* szmod = mod->GetName();\n\t\tif (szmod)\n\t\t{\n\t\t\tsolver->SetDefaultType(szmod);\n\t\t}\n\t}\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEAnalysis::FEAnalysis(FEModel* fem) : FECoreBase(fem)\n{\n\tm_psolver = nullptr;\n\tm_tend = 0.0;\n\n\tm_timeController = nullptr;\n\n\t// --- Analysis data ---\n\tm_nanalysis = 0;\n\tm_badaptorReSolve = true;\n\n\t// --- Time Step Data ---\n\tm_ntime = 10;\n\tm_final_time = 0.0;\n\tm_dt0 = 0.1;\n\tm_dt = 0;\n\n\t// initialize counters\n\tm_ntotref    = 0;\t\t// total nr of stiffness reformations\n\tm_ntotiter   = 0;\t\t// total nr of non-linear iterations\n\tm_ntimesteps = 0;\t\t// time steps completed\n\tm_ntotrhs    = 0;\t\t// total nr of right hand side evaluations\n\n\t// --- I/O Data ---\n\tm_nplot   = FE_PLOT_MAJOR_ITRS;\n\tm_noutput = FE_OUTPUT_MAJOR_ITRS;\n\tm_nplot_stride = 1;\n\tm_noutput_stride = 1;\n\tm_nplotRange[0] = 0; // by default, will store step zero.\n\tm_nplotRange[1] = -1; // by default, store last time step\n\tm_bplotZero = false; // don't force plotting step zero.\n\tm_plotHint = 0;\n\n\tm_bactive = false;\n}\n\n//-----------------------------------------------------------------------------\nFEAnalysis::~FEAnalysis()\n{\n\tif (m_psolver) delete m_psolver;\n}\n\n//-----------------------------------------------------------------------------\n//! copy data from another analysis\nvoid FEAnalysis::CopyFrom(FEAnalysis* step)\n{\n\tm_nanalysis = step->m_nanalysis;\n\n\tm_ntime      = step->m_ntime;\n\tm_final_time = step->m_final_time;\n\tm_dt         = step->m_dt;\n\tm_dt0        = step->m_dt0;\n\tm_tstart     = step->m_tstart;\n\tm_tend       = step->m_tend;\n\n\tif (step->m_timeController)\n\t{\n\t\tm_timeController = new FETimeStepController(GetFEModel());\n\t\tm_timeController->SetAnalysis(this);\n\t\tm_timeController->CopyFrom(step->m_timeController);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Return a domain\nFEDomain* FEAnalysis::Domain(int i)\n{\n\treturn &(GetFEModel()->GetMesh().Domain(m_Dom[i])); \n}\n\n//-----------------------------------------------------------------------------\nvoid FEAnalysis::AddStepComponent(FEStepComponent* pmc)\n{\n\tif (pmc) m_MC.push_back(pmc);\n}\n\n//-----------------------------------------------------------------------------\nint FEAnalysis::StepComponents() const\n{\n\treturn (int) m_MC.size();\n}\n\n//-----------------------------------------------------------------------------\n//! get a model component\nFEStepComponent* FEAnalysis::GetStepComponent(int i)\n{\n\treturn m_MC[i];\n}\n\n//-----------------------------------------------------------------------------\n//! sets the plot level\nvoid FEAnalysis::SetPlotLevel(int n) { m_nplot = n; }\n\n//-----------------------------------------------------------------------------\n//! sets the plot stride\nvoid FEAnalysis::SetPlotStride(int n) { m_nplot_stride = n; }\n\n//-----------------------------------------------------------------------------\n//! sets the plot range\nvoid FEAnalysis::SetPlotRange(int n0, int n1)\n{\n\tm_nplotRange[0] = n0;\n\tm_nplotRange[1] = n1;\n}\n\n//-----------------------------------------------------------------------------\n//! sets the zero-state plot flag\nvoid FEAnalysis::SetPlotZeroState(bool b)\n{\n\tm_bplotZero = b;\n}\n\n//-----------------------------------------------------------------------------\n//! sets the plot hint\nvoid FEAnalysis::SetPlotHint(int plotHint)\n{\n\tm_plotHint = plotHint;\n}\n\n//-----------------------------------------------------------------------------\n//! get the plot hint\nint FEAnalysis::GetPlotHint() const\n{\n\treturn m_plotHint;\n}\n\n//-----------------------------------------------------------------------------\n//! get the plot level\nint FEAnalysis::GetPlotLevel() { return m_nplot; }\n\n//! Set the output level\nvoid FEAnalysis::SetOutputLevel(int n) { m_noutput = n; }\n\n//! Get the output level\nint FEAnalysis::GetOutputLevel() { return m_noutput; }\n\n//-----------------------------------------------------------------------------\nvoid FEAnalysis::Reset()\n{\n\tm_ntotref    = 0;\t\t// total nr of stiffness reformations\n\tm_ntotiter   = 0;\t\t// total nr of non-linear iterations\n\tm_ntimesteps = 0;\t\t// time steps completed\n\tm_ntotrhs    = 0;\t\t// total nr of right hand side evaluations\n\n\tm_dt = m_dt0;\n\n\tif (m_timeController) m_timeController->Reset();\n\n\t// Deactivate the step\n\tDeactivate();\n\n\tif (m_psolver) m_psolver->Reset();\n}\n\n//-----------------------------------------------------------------------------\nFESolver* FEAnalysis::GetFESolver() \n{ \n\treturn m_psolver; \n}\n\n//-----------------------------------------------------------------------------\nvoid FEAnalysis::SetFESolver(FESolver* psolver)\n{\n\tif (m_psolver) delete m_psolver;\n\tm_psolver = psolver;\n}\n\n//-----------------------------------------------------------------------------\n//! Data initialization and data checking.\nbool FEAnalysis::Init()\n{\n\tm_dt = m_dt0;\n\n\tif (m_timeController)\n\t{\n\t\tm_timeController->SetAnalysis(this);\n\t\tif (m_timeController->Init() == false) return false;\n\t}\n\tif (m_nplot_stride <= 0) return false;\n\tif (m_noutput_stride <= 0) return false;\n\treturn Validate();\n}\n\n//-----------------------------------------------------------------------------\n//! See if this step is active\nbool FEAnalysis::IsActive()\n{\n\treturn m_bactive;\n}\n\n//-----------------------------------------------------------------------------\n//! This function gets called right before the step needs to be solved.\nbool FEAnalysis::Activate()\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// Make sure we are not activated yet\n\t// This can happen after a restart during FEModel::Solve\n\tif (m_bactive) return true;\n\n\t// activate the time step\n\tm_bactive = true;\n\n\t// set first time step\n\t// We can't do this since it will mess up the value from a restart\n//\tm_dt = m_dt0;\n\n\t// determine the end time\n\tdouble Dt;\n\tif (m_ntime == -1) Dt = m_final_time; else Dt = m_dt0*m_ntime;\n\tm_tstart = fem.GetStartTime();\n\tm_tend = m_tstart + Dt;\n\n\t// For now, add all domains to the analysis step\n\tFEMesh& mesh = fem.GetMesh();\n\tint ndom = mesh.Domains();\n\tClearDomains();\n\tfor (int i=0; i<ndom; ++i) AddDomain(i);\n\n\t// activate the model components assigned to this step\n\t// NOTE: This currently does not ensure that initial conditions are\n\t// applied first. This is important since relative prescribed displacements must \n\t// be applied after initial conditions.\n\tfor (int i=0; i<(int) m_MC.size(); ++i) m_MC[i]->Activate();\n\n\t// Next, we need to determine which degrees of freedom are active. \n\t// We start by resetting all nodal degrees of freedom.\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tfor (int j=0; j<(int)node.dofs(); ++j) node.set_inactive(j);\n\t}\n\n    // Then, we activate the domains.\n    // This will activate the relevant degrees of freedom\n    // NOTE: this must be done after the model components are activated.\n    // This is to make sure that all initial and prescribed values are applied.\n    // Activate all domains\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.Class() != FE_DOMAIN_SHELL)\n            dom.Activate();\n    }\n    // but activate shell domains last (to deal with sandwiched shells)\n    for (int i=0; i<mesh.Domains(); ++i)\n    {\n        FEDomain& dom = mesh.Domain(i);\n        if (dom.Class() == FE_DOMAIN_SHELL)\n            dom.Activate();\n    }\n\n\t// active the linear constraints\n\tfem.GetLinearConstraintManager().Activate();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function deactivates all boundary conditions and contact interfaces.\n//! It also gives the linear solver to clean its data.\n//! This is called at the completion of an analysis step.\nvoid FEAnalysis::Deactivate()\n{\n\t// deactivate the model components\n\tfor (size_t i=0; i<(int) m_MC.size(); ++i) m_MC[i]->Deactivate();\n\n\t// clean up solver data (i.e. destroy linear solver)\n\tFESolver* solver = GetFESolver();\n\tif (solver) solver->Clean();\n\n\t// deactivate the time step\n\tm_bactive = false;\n}\n\n//-----------------------------------------------------------------------------\n// initialize the solver\nbool FEAnalysis::InitSolver()\n{\n\tTRACK_TIME(TimerID::Timer_Init);\n\tFEModel& fem = *GetFEModel();\n\n\t// initialize equations\n\tFESolver* psolver = GetFESolver();\n\tif (psolver == nullptr) return false;\n\n\tif (psolver->InitEquations() == false) return false;\n\n\t// do initialization of solver data\n\tif (psolver->Init() == false) return false;\n\n\t// initialize linear constraints\n\t// Must be done after equations are initialized\n\tif (fem.GetLinearConstraintManager().Initialize() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEAnalysis::Solve()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tfem.GetTime().timeIncrement = m_dt0;\n\n\t// Initialize the solver\n\tif (InitSolver() == false) return false;\n\n\t// convergence flag\n\t// we initialize it to true so that when a restart is performed after \n\t// the last time step we terminate normally.\n\tbool bconv = true;\n\n\t// calculate end time value\n\tdouble starttime = fem.GetStartTime();\n//\tdouble endtime = fem.m_ftime0 + m_ntime*m_dt0;\n\tdouble endtime = m_tend;\n\tconst double eps = endtime*1e-7;\n\n\t// if we restarted we need to update the timestep\n\t// before continuing\n\tif (m_ntimesteps != 0)\n\t{\n\t\t// update time step\n\t\tif (m_timeController && (fem.GetCurrentTime() + eps < endtime)) m_timeController->AutoTimeStep(GetFESolver()->m_niter);\n\t}\n\telse\n\t{\n\t\t// make sure that the timestep is at least the min time step size\n\t\tif (m_timeController) m_timeController->AutoTimeStep(0);\n\t}\n\n\t// dump stream for running restarts\n\tDumpMemStream dmp(fem);\n\n\t// repeat for all timesteps\n\tif (m_timeController) m_timeController->m_nretries = 0;\n\twhile (endtime - fem.GetCurrentTime() > eps)\n\t{\n\t\t// keep a copy of the current state, in case\n\t\t// we need to retry this time step\n\t\tif (m_timeController && (m_timeController->m_maxretries > 0))\n\t\t{ \n\t\t\tdmp.clear();\n\t\t\tfem.Serialize(dmp); \n\t\t}\n\n\t\t// Inform that the time is about to change. (Plugins can use \n\t\t// this callback to modify time step)\n\t\tif (fem.DoCallback(CB_UPDATE_TIME) == false) return false;\n\n\t\t// update time\n\t\tFETimeInfo& tp = fem.GetTime();\n\t\tdouble newTime = tp.currentTime + m_dt;\n\t\tif (newTime > endtime)\n\t\t{\n\t\t\ttp.timeIncrement = endtime - tp.currentTime;\n\t\t\ttp.currentTime = endtime;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttp.currentTime = newTime;\n\t\t\ttp.timeIncrement = m_dt;\n\t\t}\n\t\ttp.timeStep = m_ntimesteps;\n\t\tfeLog(\"\\n===== beginning time step %d : %lg =====\\n\", m_ntimesteps + 1, newTime);\n\n\t\t// initialize the solver step\n\t\t// (This basically evaluates all the parameter lists, but let's the solver\n\t\t//  customize this process to the specific needs of the solver)\n\t\tif (GetFESolver()->InitStep(newTime) == false)\n\t\t{\n\t\t\tbconv = false;\n\t\t\tbreak;\n\t\t}\n\n\t\t// Solve the time step\n\t\tint ierr = SolveTimeStep();\n\n\t\t// see if we want to abort\n\t\tif (ierr == 2) \n\t\t{\n\t\t\tbconv = false;\n\t\t\tbreak;\n\t\t}\n\n\t\t// update counters\n\t\tFESolver* psolver = GetFESolver();\n\t\tm_ntotref  += psolver->m_ntotref;\n\t\tm_ntotiter += psolver->m_niter;\n\t\tm_ntotrhs  += psolver->m_nrhs;\n\n\t\t// see if we have converged\n\t\tif (ierr == 0)\n\t\t{\n\t\t\tbconv = true;\n\n\t\t\t// Yes! We have converged!\n\t\t\tfeLog(\"\\n------- converged at time : %lg\\n\\n\", fem.GetCurrentTime());\n\n\t\t\t// update nr of completed timesteps\n\t\t\tm_ntimesteps++;\n\n\t\t\t// call callback function\n\t\t\tif (fem.DoCallback(CB_MAJOR_ITERS) == false)\n\t\t\t{\n\t\t\t\tbconv = false;\n\t\t\t\tfeLogWarning(\"Early termination on user's request\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// reset retry counter\n\t\t\tif (m_timeController) m_timeController->m_nretries = 0;\n\n\t\t\t// update time step\n\t\t\tif (m_timeController && (fem.GetCurrentTime() + eps < endtime)) m_timeController->AutoTimeStep(psolver->m_niter);\n\t\t}\n\t\telse \n\t\t{\n\t\t\t// We failed to converge. \n\t\t\tbconv = false;\n\n\t\t\t// Report the sad news to the user.\n\t\t\tfeLog(\"\\n\\n------- failed to converge at time : %lg\\n\\n\", fem.GetCurrentTime());\n\n\t\t\t// This will allow states that have negative Jacobians to be stored\n\t\t\tfem.DoCallback(CB_TIMESTEP_FAILED);\n\n\t\t\t// If we have auto time stepping, decrease time step and let's retry\n\t\t\tif (m_timeController && (m_timeController->m_nretries < m_timeController->m_maxretries))\n\t\t\t{\n\t\t\t\t// restore the previous state\n\t\t\t\tdmp.Open(false, true);\n\t\t\t\tfem.Serialize(dmp);\n\t\t\t\t\n\t\t\t\t// let's try again\n\t\t\t\tm_timeController->Retry();\n\n\t\t\t\t// rewind the solver\n\t\t\t\tGetFESolver()->Rewind();\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\t// can't retry, so abort\n\t\t\t\tif (m_timeController && (m_timeController->m_nretries >= m_timeController->m_maxretries))\n\t\t\t\t\tfeLog(\"Max. nr of retries reached.\\n\\n\");\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// TODO: Why is this here?\n\tfem.SetStartTime(fem.GetCurrentTime());\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n// This function calls the FE Solver for solving this analysis and also handles\n// all the exceptions. \nint FEAnalysis::SolveTimeStep()\n{\n\tint nerr = 0;\n\ttry\n\t{\n\t\t// solve this timestep,\n\t\tint niter = 0;\n\t\tbool bconv = false;\n\t\twhile (bconv == false) {\n\t\t\t\n\t\t\t// solve the time step\n\t\t\tbconv = GetFESolver()->SolveStep();\n\n\t\t\t// Apply any mesh adaptors\n\t\t\tif (bconv)\n\t\t\t{\n\t\t\t\tFEModel& fem = *GetFEModel();\n\n\t\t\t\tif (fem.DoCallback(CB_TIMESTEP_SOLVED) == false)\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (fem.MeshAdaptors())\n\t\t\t\t{\n\t\t\t\t\tfem.GetTime().augmentation = niter;\n\t\t\t\t\tfeLog(\"\\n=== Applying mesh adaptors: iteration %d\\n\", niter + 1);\n\t\t\t\t\tfor (int i = 0; i < fem.MeshAdaptors(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMeshAdaptor* meshAdaptor = fem.MeshAdaptor(i);\n\t\t\t\t\t\tif (meshAdaptor->IsActive())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfeLog(\"*mesh adaptor %d (%s):\\n\", i + 1, meshAdaptor->GetTypeStr());\n\n\t\t\t\t\t\t\t// Apply the mesh adaptor. \n\t\t\t\t\t\t\t// It will return true if the mesh was modified. \n\t\t\t\t\t\t\tbool meshModified = meshAdaptor->Apply(niter);\n\n\t\t\t\t\t\t\tbconv = ((meshModified == false) && bconv);\n\t\t\t\t\t\t\tfeLog(\"\\n\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tniter++;\n\n\t\t\t\t\tif (bconv == false)\n\t\t\t\t\t{\n\t\t\t\t\t\t// we need to clear the FE solver and then reinitialize it again\n\t\t\t\t\t\tFESolver* solver = GetFESolver();\n\t\t\t\t\t\tsolver->Clean();\n\n\t\t\t\t\t\t// reinitialize it\n\t\t\t\t\t\tInitSolver();\n\n\t\t\t\t\t\t// inform listeners that the mesh was remeshed\n\t\t\t\t\t\tfem.DoCallback(CB_REMESH);\n\t\t\t\t\t}\n\t\t\t\t\tfeLog(\"\\n\");\n\n\t\t\t\t\tif (m_badaptorReSolve == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tbconv = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse break;\n\t\t}\n\t\tnerr = (bconv ? 0 : 1);\n\t}\n\tcatch (LinearSolverFailed e)\n\t{\n\t\tfeLogError(e.what());\n\t\tnerr = 2;\n\t}\n\tcatch (FactorizationError e)\n\t{\n\t\tfeLogError(e.what());\n\t\tnerr = 2;\n\t}\n\tcatch (NANInResidualDetected e)\n\t{\n\t\tfeLogError(e.what());\n\t\tnerr = 1;\t// don't abort, instead let's retry the step\n\t}\t\n\tcatch (NANInSolutionDetected e)\n\t{\n\t\tfeLogError(e.what());\n\t\tnerr = 1;\t// don't abort, instead let's retry the step\n\t}\n\tcatch (FEMultiScaleException)\n\t{\n\t\tfeLogError(\"The RVE problem has failed. Aborting macro run.\");\n\t\tnerr = 2;\n\t}\n\tcatch (std::bad_alloc e)\n\t{\n\t\tfeLogError(\"A memory allocation failure has occured.\\nThe program will now be terminated.\");\n\t\tnerr = 2;\n\t}\n\tcatch (std::exception e)\n\t{\n\t\tfeLogError(\"Exception detected: %s\\n\", e.what());\n\t\tnerr = 2;\n\t}\n\tcatch (...)\n\t{\n\t\tnerr = 2;\n\t}\n\n\treturn nerr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEAnalysis::Serialize(DumpStream& ar)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// --- analysis data ---\n\tar & m_nanalysis;\n\tar & m_bactive;\n\n\t// --- Time Step Data ---\n\tar & m_ntime;\n\tar & m_final_time;\n\tar & m_dt0 & m_dt;\n\tar & m_tstart & m_tend;\n\tar & m_ntotrhs;\n\tar & m_ntotref;\n\tar & m_ntotiter;\n\tar & m_ntimesteps;\n\n\t// --- I/O Data ---\n\tar & m_nplot;\n\tar & m_noutput;\n\tar & m_nplotRange;\n\tar & m_bplotZero;\n\n\t// Serialize solver data\n\tar & m_psolver;\n\n\t// don't serialize for shallow copies\n\tif (ar.IsShallow()) return;\n\n\t// Serialize model components\n\tar & m_MC;\n\n\tif (ar.IsSaving() == false)\n\t{\n\t\t// For now, add all domains to the analysis step\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tint ndom = mesh.Domains();\n\t\tClearDomains();\n\t\tfor (int i = 0; i<ndom; ++i) AddDomain(i);\n\t}\n\n\t// serialize time controller\n\tar & m_timeController;\n}\n"
  },
  {
    "path": "FECore/FEAnalysis.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n#include \"FECoreClass.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FESolver;\nclass FEDomain;\nclass DumpStream;\nclass FEStepComponent;\nclass FETimeStepController;\n\n//-----------------------------------------------------------------------------\n//! Base class for finite element analysis\nclass FECORE_API FEAnalysis : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FEANALYSIS_ID)\n\tFECORE_BASE_CLASS(FEAnalysis);\n\npublic:\n\t//! constructor\n\tFEAnalysis(FEModel* pfem);\n\n\t//! destructor\n\tvirtual ~FEAnalysis();\n\n\t//! Initialization\n\tvirtual bool Init() override;\n\n\t//! Activation\n\tvirtual bool Activate();\n\n\t//! See if this step is active\n\tbool IsActive();\n\n\t//! Reset analysis data\n\tvirtual void Reset();\n\n\t//! Solve the analysis step\n\tvirtual bool Solve();\n\n\t//! wrap it up\n\tvirtual void Deactivate();\n\n\t//! Serialize data from and to a binary archive\n\tvirtual void Serialize(DumpStream& ar) override;\n\n\t//! copy data from another analysis\n\tvoid CopyFrom(FEAnalysis* step);\n\npublic:\n\tvoid SetFESolver(FESolver* psolver);\n\n\tFESolver* GetFESolver();\n\npublic:\n\t//! Get active domains\n\tint Domains() { return (int)m_Dom.size(); }\n\n\t//! Get active domain\n\tFEDomain* Domain(int i);\n\n\t//! Add a domain\n\tvoid AddDomain(int i) { m_Dom.push_back(i); }\n\n\t//! clear all domains\n\tvoid ClearDomains() { m_Dom.clear(); }\n\npublic:\n\t//! add a step component\n\tvoid AddStepComponent(FEStepComponent* pmc);\n\n\t//! return number of model components\n\tint StepComponents() const;\n\n\t//! get a step component\n\tFEStepComponent* GetStepComponent(int i);\n\npublic:\n\t//! sets the plot level\n\tvoid SetPlotLevel(int n);\n\n\t//! sets the plot stride\n\tvoid SetPlotStride(int n);\n\n\t//! sets the plot range\n\tvoid SetPlotRange(int n0, int n1);\n\n\t//! sets the zero-state plot flag\n\tvoid SetPlotZeroState(bool b);\n\n\t//! sets the plot hint\n\tvoid SetPlotHint(int plotHint);\n\n\t//! get the plot hint\n\tint GetPlotHint() const;\n\n\t//! get the plot level\n\tint GetPlotLevel();\n\n\t//! Set the output level\n\tvoid SetOutputLevel(int n);\n\n\t//! Get the output level\n\tint GetOutputLevel();\n\n\t// initialize the solver\n\tbool InitSolver();\n\n\t// Call the FE Solver to solve the time step\n\t// Returns an error code\n\t// 0 = all is well, continue\n\t// 1 = solver has failed, but try auto-time step\n\t// 2 = abort\n\tint SolveTimeStep();\n\npublic:\n\t// --- Control Data ---\n\t//{\n\t\tint\t\tm_nanalysis;\t\t//!< analysis type\n\t\tbool\tm_badaptorReSolve;\t//!< resolve analysis after mesh adaptor phase\n\t//}\n\n\t// --- Time Step Data ---\n\t//{\n\t\tint\t\tm_ntime;\t\t//!< nr of timesteps\n\t\tdouble\tm_final_time;\t//!< end time for this time step\n\t\tdouble\tm_dt;\t\t\t//!< current time step \n\t\tdouble\tm_dt0;\t\t\t//!< initial time step size\n\t\tdouble\tm_tstart;\t\t//!< start time\n\t\tdouble\tm_tend;\t\t\t//!< end time\n\n\t\tFETimeStepController* m_timeController;\n\t//}\n\n\t// --- Quasi-Newton Solver Variables ---\n\t//{\n\t\tint\t\tm_ntotrhs;\t\t//!< total nr of right hand side evaluations\n\t\tint\t\tm_ntotref;\t\t//!< total nr of stiffness reformations\n\t\tint\t\tm_ntotiter;\t\t//!< total nr of non-linear iterations\n\t\tint\t\tm_ntimesteps;\t//!< time steps completed\n\t//}\n\n\t// --- I/O Data ---\n\t//{\n\t\tint\t\tm_nplot;\t\t//!< plot level\n\t\tint\t\tm_noutput;\t\t//!< data output level\n\t\tint\t\tm_nplot_stride;\t//!< stride for plotting\n\t\tint\t\tm_noutput_stride;\t//!< stride for data output\n\t\tint\t\tm_nplotRange[2];\t//!< plot range\n\t\tbool\tm_bplotZero;\t\t//!< Force plotting of time step \"zero\"\n\t\tint\t\tm_plotHint;\t\t\t//!< the plot mode\n\t//}\n\nprivate:\n\t// the FE solver\n\tFESolver*\tm_psolver;\t//!< pointer to solver class that will solve this step.\n\tbool\t\tm_bactive;\t//!< activation flag\n\nprotected:\n\tstd::vector<int>\t\t\t\tm_Dom;\t//!< list of active domains for this analysis\n\tstd::vector<FEStepComponent*>\tm_MC;\t//!< array of model components active during this step\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEAugLagLinearConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEAugLagLinearConstraint.h\"\n#include \"FEModel.h\"\n#include \"FEMesh.h\"\n#include \"log.h\"\n#include \"FELinearSystem.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEAugLagLinearConstraintDOF, FECoreClass)\n\tADD_PARAMETER(m_nodeid, \"id\", FE_PARAM_ATTRIBUTE, 0);\n\tADD_PARAMETER(m_bc, \"bc\", FE_PARAM_ATTRIBUTE, \"$(dof_list)\");\n\tADD_PARAMETER(m_val, \"node\")->setLongName(\"Value\");\nEND_FECORE_CLASS();\n\nFEAugLagLinearConstraintDOF::FEAugLagLinearConstraintDOF(FEModel* fem) : FECoreClass(fem)\n{\n\tm_nodeid = m_bc = 0;\n\tm_val = 0.0;\n}\n\nFENode& FEAugLagLinearConstraintDOF::Node()\n{\n\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\treturn *(mesh.FindNodeFromID(m_nodeid));\n}\n\nint FEAugLagLinearConstraintDOF::NodeIndex()\n{\n\tFEModel* fem = GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\treturn mesh.FindNodeIndexFromID(m_nodeid);\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEAugLagLinearConstraint, FECoreClass)\n\tADD_PROPERTY(m_dof, \"node\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nvoid FEAugLagLinearConstraint::ClearDOFs()\n{\n\tfor (int i = 0; i < m_dof.size(); ++i) delete m_dof[i];\n\tm_dof.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEAugLagLinearConstraint::AddDOF(int node, int bc, double val)\n{\n\tFEAugLagLinearConstraintDOF* dof = fecore_alloc(FEAugLagLinearConstraintDOF, GetFEModel());\n\tdof->m_nodeid = node;\n\tdof->m_bc = bc;\n\tdof->m_val = val;\n\tm_dof.push_back(dof);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEAugLagLinearConstraint::Serialize(DumpStream& ar)\n{\n    FECoreClass::Serialize(ar);\n    ar & m_dof & m_lam;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FELinearConstraintSet, FENLConstraint)\n    ADD_PARAMETER(m_laugon , \"laugon\"        )->setLongName(\"Enforcement method\")->setEnums(\"PENALTY\\0AUGLAG\\0LAGMULT\\0\");\n\tADD_PARAMETER(m_tol    , \"tol\");\n\tADD_PARAMETER(m_eps    , \"penalty\");\n    ADD_PARAMETER(m_rhs    , \"rhs\");\n\tADD_PARAMETER(m_naugmin, \"minaug\");\n\tADD_PARAMETER(m_naugmax, \"maxaug\");\n\n\tADD_PROPERTY(m_LC, \"linear_constraint\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFELinearConstraintSet::FELinearConstraintSet(FEModel* pfem) : FENLConstraint(pfem)\n{\n\tstatic int nc = 1;\n\tm_nID = nc++;\n\n\tm_laugon = FECore::AUGLAG_METHOD;\n\tm_eps = 1;\n\tm_tol = 0.1;\n    m_rhs = 0;\n\tm_naugmax = 50;\n\tm_naugmin = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintSet::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tFEMesh& mesh = GetMesh();\n\tvector<FEAugLagLinearConstraint*>& LC = m_LC;\n\tvector<int> lm;\n\tint N = (int)LC.size();\n\tvector<FEAugLagLinearConstraint*>::iterator it = LC.begin();\n    if (m_laugon > FECore::AUGLAG_METHOD) {\n        for (int i=0; i<N; ++i, ++it)\n        {\n            int n = (int)(*it)->m_dof.size();\n            lm.resize(n+1);\n            FEAugLagLinearConstraint::Iterator is = (*it)->m_dof.begin();\n            for (int j = 0; j<n; ++j, ++is) lm[j] = (*is)->Node().m_ID[(*is)->m_bc];\n            lm[n] = m_EQ[i];\n            M.build_add(lm);\n        }\n    }\n    else {\n        for (int i=0; i<N; ++i, ++it)\n        {\n            int n = (int)(*it)->m_dof.size();\n            lm.resize(n);\n            FEAugLagLinearConstraint::Iterator is = (*it)->m_dof.begin();\n            for (int j = 0; j<n; ++j, ++is) lm[j] = (*is)->Node().m_ID[(*is)->m_bc];\n            M.build_add(lm);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the current value of the constraint.\n\ndouble FELinearConstraintSet::constraint(FEAugLagLinearConstraint& LC)\n{\n\tint n = (int)LC.m_dof.size();\n\tdouble c = 0;\n\tvector<FEAugLagLinearConstraintDOF*>::iterator it = LC.m_dof.begin();\n\tdouble u;\n\tFEMesh& mesh = GetMesh();\n\tfor (int i=0; i<n; ++i, ++it) \n\t{\n\t\tFENode& node = (*it)->Node();\n\t\tswitch ((*it)->m_bc)\n\t\t{\n\t\tcase 0: u = node.m_rt.x - node.m_r0.x; break;\n\t\tcase 1: u = node.m_rt.y - node.m_r0.y; break;\n\t\tcase 2: u = node.m_rt.z - node.m_r0.z; break;\n\t\tdefault:\n                u = node.get((*it)->m_bc);\n\t\t}\n\t\tc += (*it)->m_val*u;\n\t}\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n//! This function performs an augmentation, if the Lagrange multiplier \n//! has not converged\n\nbool FELinearConstraintSet::Augment(int naug, const FETimeInfo& tp)\n{\t\n\tif (m_laugon != FECore::AUGLAG_METHOD) return true;\n\n\tint M = (int)m_LC.size(), i;\n\tvector<FEAugLagLinearConstraint*>::iterator im = m_LC.begin();\n\n\t// calculate lag multipliers\n\tdouble L0 = 0, L1 = 0;\n\tfor (i=0; i<M; ++i, ++im)\n\t{\n\t\tFEAugLagLinearConstraint& LC = *(*im);\n\t\tdouble c = constraint(LC) - m_rhs;\n\t\tdouble lam = LC.m_lam + m_eps*c;\n\n\t\tL0 += LC.m_lam*LC.m_lam;\n\t\tL1 += lam*lam;\n\t}\n\n\tL0 = sqrt(L0);\n\tL1 = sqrt(L1);\n\n\tdouble p;\n\tif (L1 != 0)\n\t\tp = fabs((L1 - L0)/L1);\n\telse p = fabs(L1 - L0);\n\n\tfeLog(\"linear constraint set %d: %15.7lg %15.7lg %15.7lg\\n\", m_nID, L0, fabs(L1 - L0), fabs(m_tol*L1));\n\n\tbool bconv = false;\n\tif (p <= m_tol) bconv = true;\n\tif ((m_naugmax >= 0) && (naug >= m_naugmax)) bconv = true;\n\tif (naug < m_naugmin) bconv = false;\n\n\tif (bconv == false)\n\t{\n\t\tim = m_LC.begin();\n\t\tfor (i=0; i<M; ++i, ++im)\n\t\t{\n\t\t\tFEAugLagLinearConstraint& LC = *(*im);\n\t\t\tdouble c = constraint(LC) - m_rhs;\n\t\t\tLC.m_lam += m_eps*c;\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contribution to the residual.\n\nvoid FELinearConstraintSet::LoadVector(FEGlobalVector& R, const FETimeInfo& tp)\n{\n\tFEMesh& mesh = GetMesh();\n\n\tint M = (int)m_LC.size();\n\tvector<FEAugLagLinearConstraint*>::iterator  im = m_LC.begin();\n    if (m_laugon == FECore::LAGMULT_METHOD) {\n        double alpha = tp.alphaf;\n        \n        for (int m=0; m<M; ++m, ++im)\n        {\n            FEAugLagLinearConstraint& LC = *(*im);\n            int n = (int)LC.m_dof.size();\n            double f = constraint(LC) - m_rhs;\n            double lam = m_Lm[m]*alpha + m_Lmp[m]*(1-alpha);\n            FEAugLagLinearConstraint::Iterator it = LC.m_dof.begin();\n            for (int i=0; i<n; ++i, ++it)\n            {\n                int neq = (*it)->Node().m_ID[(*it)->m_bc];\n                if (neq >= 0)\n                    R[neq] -= lam * (*it)->m_val;\n            }\n            R[m_EQ[m]] -= f;\n        }\n    }\n    else {\n        for (int m=0; m<M; ++m, ++im)\n        {\n            FEAugLagLinearConstraint& LC = *(*im);\n            int n = (int)LC.m_dof.size();\n            double c = constraint(LC);\n            FEAugLagLinearConstraint::Iterator it = LC.m_dof.begin();\n            for (int i=0; i<n; ++i, ++it)\n            {\n                int neq = (*it)->Node().m_ID[(*it)->m_bc];\n                if (neq >= 0)\n                {\n                    R[neq] -= (LC.m_lam+m_eps*c)* (*it)->m_val;\n                }\n            }\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contribution to the stiffness matrix.\n\nvoid FELinearConstraintSet::StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp)\n{\n\tFEMesh& mesh = GetMesh();\n\n\tvector<int> en;\n\tvector<int> elm;\n\tFEElementMatrix ke;\n\n\tint M = (int)m_LC.size();\n\tvector<FEAugLagLinearConstraint*>::iterator im = m_LC.begin();\n    if (m_laugon == FECore::LAGMULT_METHOD) {\n        double alpha = tp.alphaf;\n        for (int m=0; m<M; ++m, ++im)\n        {\n            FEAugLagLinearConstraint& LC = *(*im);\n            int n = (int)LC.m_dof.size();\n            double lam = m_Lm[m]*alpha + m_Lmp[m]*(1-alpha);\n            double f = constraint(LC) - m_rhs;\n            ke.resize(n+1, n+1);\n            ke.zero();\n            FEAugLagLinearConstraint::Iterator it = LC.m_dof.begin(), jt;\n            for (int i=0; i<n; ++i, ++it)\n            {\n                ke[n][i] = (*it)->m_val;\n                ke[i][n] = (*it)->m_val;\n            }\n            ke[n][n] = 0;\n            \n            en.resize(n+1);\n            elm.resize(n+1);\n            it = LC.m_dof.begin();\n            for (int i=0; i<n; ++i, ++it)\n            {\n                en[i] = (*it)->NodeIndex();\n                int neq = (*it)->Node().m_ID[(*it)->m_bc];\n                elm[i] = neq;\n            }\n            en[n] = -1;\n            elm[n] = m_EQ[m];\n            ke.SetNodes(en);\n            ke.SetIndices(elm);\n            LS.Assemble(ke);\n        }\n    }\n    else {\n        for (int m=0; m<M; ++m, ++im)\n        {\n            FEAugLagLinearConstraint& LC = *(*im);\n            int n = (int)LC.m_dof.size();\n            ke.resize(n, n);\n            FEAugLagLinearConstraint::Iterator it = LC.m_dof.begin(), jt;\n            for (int i=0; i<n; ++i, ++it)\n            {\n                jt = LC.m_dof.begin();\n                for (int j=0; j<n; ++j, ++jt)\n                {\n                    ke[i][j] = m_eps* (*it)->m_val*(*jt)->m_val;\n                }\n            }\n            \n            en.resize(n);\n            elm.resize(n);\n            it = LC.m_dof.begin();\n            for (int i=0; i<n; ++i, ++it)\n            {\n                en[i] = (*it)->NodeIndex();\n                int neq = (*it)->Node().m_ID[(*it)->m_bc];\n                elm[i] = neq;\n            }\n            ke.SetNodes(en);\n            ke.SetIndices(elm);\n            LS.Assemble(ke);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintSet::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_LC;\n        if (m_laugon > FECore::AUGLAG_METHOD)\n            ar & m_EQ & m_Lm & m_Lmp;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FELinearConstraintSet::Init()\n{\n    if (FENLConstraint::Init() == false) return false;\n    \n    int M = (int)m_LC.size();\n\n    if (m_laugon == FECore::LAGMULT_METHOD) {\n        m_EQ.resize(M, -1);\n        m_Lm.resize(M, 0.0);\n        m_Lmp.resize(M, 0.0);\n    }\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// allocate equations\nint FELinearConstraintSet::InitEquations(int neq)\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return 0;\n    int n = neq;\n    for (int i = 0; i < (int)m_LC.size(); ++i) m_EQ[i] = n++;\n    \n    return n - neq;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintSet::Update(const std::vector<double>& Ui, const std::vector<double>& ui)\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return;\n    for (int i = 0; i < (int)m_LC.size(); ++i)\n    {\n        if (m_EQ[i] != -1) m_Lm[i] = m_Lmp[i] + Ui[m_EQ[i]] + ui[m_EQ[i]];\n    }\n}\n\nvoid FELinearConstraintSet::PrepStep()\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return;\n    for (int i = 0; i < (int)m_LC.size(); ++i)\n    {\n        m_Lmp[i] = m_Lm[i];\n    }\n}\n\nvoid FELinearConstraintSet::UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui)\n{\n    if (m_laugon < FECore::LAGMULT_METHOD) return;\n    for (int i = 0; i < (int)m_LC.size(); ++i)\n    {\n        if (m_EQ[i] != -1) Ui[m_EQ[i]] += ui[m_EQ[i]];\n    }\n}\n\n"
  },
  {
    "path": "FECore/FEAugLagLinearConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <FECore/vector.h>\n#include <FECore/matrix.h>\n#include <FECore/FESurfaceConstraint.h>\n#include <FECore/FENodeSetConstraint.h>\n#include <FECore/FECoreClass.h>\n#include \"fecore_api.h\"\n#include <list>\n\nclass FENode;\n\n//-----------------------------------------------------------------------------\n//! linear constraint enforced using augmented lagrangian\n\nclass FECORE_API FEAugLagLinearConstraintDOF : public FECoreClass\n{\npublic:\n\tFEAugLagLinearConstraintDOF(FEModel* fem);\n\n\tFENode& Node();\n\tint NodeIndex();\n\npublic:\n\tint\tm_nodeid;\t\t// the ID of the node to which this dof belongs to\n\tint\tm_bc;\t\t\t// the degree of freedom\n\tdouble\tm_val;\t// coefficient value\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FEAugLagLinearConstraintDOF);\n};\n\nclass FECORE_API FEAugLagLinearConstraint : public FECoreClass\n{\npublic:\n\ttypedef std::vector<FEAugLagLinearConstraintDOF*>::iterator Iterator;\n\npublic:\n\t//! constructor\n\tFEAugLagLinearConstraint(FEModel* fem) : FECoreClass(fem) { m_lam = 0; }\n\n\t//! serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\tvoid ClearDOFs();\n\n\tvoid AddDOF(int node, int bc, double val);\n\npublic:\n\tstd::vector<FEAugLagLinearConstraintDOF*>\tm_dof;\t//!< list of participating dofs\n\tdouble\t\t\t\t\t\t\t\t\tm_lam;\t//!< lagrange multiplier\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FEAugLagLinearConstraint);\n};\n\n//-----------------------------------------------------------------------------\n//! This class manages a group of linear constraints\n\nclass FECORE_API FELinearConstraintSet : public FENLConstraint\n{\npublic:\n\t//! constructor\n\tFELinearConstraintSet(FEModel* pfem);\n\n\t//! add a linear constraint to the list\n\tvoid add(FEAugLagLinearConstraint* plc) { m_LC.push_back(plc); }\n\npublic:\n\t//! add the linear constraint contributions to the residual\n\tvoid LoadVector(FEGlobalVector& R, const FETimeInfo& tp) override;\n\n\t//! add the linear constraint contributions to the stiffness matrix\n\tvoid StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) override;\n\n\t//! do the augmentation\n\tbool Augment(int naug, const FETimeInfo& tp) override;\n\n\t//! build connectivity for matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& M) override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n    //! initialize\n    bool Init() override;\n    \n    // allocate equations\n    int InitEquations(int neq) override;\n    \n    void Update(const std::vector<double>& Ui, const std::vector<double>& ui) override;\n    void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) override;\n    \n    void PrepStep() override;\n    \nprotected:\n\t//! calculate the constraint value\n\tdouble constraint(FEAugLagLinearConstraint& LC);\n\npublic:\n\tstd::vector<FEAugLagLinearConstraint*>\tm_LC;\t//!< list of linear constraints\n\npublic:\n\tint \tm_laugon;\t//!< contact enforcement method\n\tdouble\tm_tol;\t//!< augmentation tolerance\n\tdouble\tm_eps;\t//!< penalty factor\n    double  m_rhs;  //!< right-hand-side of linear constraint equation\n\tint\t\tm_naugmax;\t//!< max nr of augmentations\n\tint\t\tm_naugmin;\t//!< min nf of augmentations\n\n\tint\tm_nID;\t\t//!< ID of manager\n\nprotected:\n    vector<int>     m_EQ;\n    vector<double>  m_Lm, m_Lmp;\n    \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEBeamDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBeamDomain.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFEBeamDomain::FEBeamDomain(FEModel* fem) : FEDomain(FE_DOMAIN_BEAM, fem)\n{\n\n}\n"
  },
  {
    "path": "FECore/FEBeamDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for beam domains\nclass FECORE_API FEBeamDomain : public FEDomain\n{\n\tFECORE_SUPER_CLASS(FEBEAMDOMAIN_ID)\n\tFECORE_BASE_CLASS(FEBeamDomain)\n\n\t// get the element type (TODO: Move to FEDomain class?)\n\tint GetElementType() { return ElementRef(0).Type(); };\n\npublic:\n\tFEBeamDomain(FEModel* pm);\n};\n"
  },
  {
    "path": "FECore/FEBodyConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBodyConstraint.h\"\n#include \"FEElementSet.h\"\n#include \"FEModelParam.h\"\n#include \"FEMesh.h\"\n\nFEBodyConstraint::FEBodyConstraint(FEModel* fem) : FENLConstraint(fem)\n{\n}\n\nbool FEBodyConstraint::Init()\n{\n\t// If the domain list is empty, add all the domains\n\tif (m_dom.IsEmpty())\n\t{\n\t\tFEMesh& mesh = GetMesh();\n\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain* dom = &mesh.Domain(i);\n\t\t\tm_dom.AddDomain(dom);\n\t\t}\n\t}\n\treturn FENLConstraint::Init();\n}\n\nint FEBodyConstraint::Domains() const\n{\n\treturn m_dom.Domains();\n}\n\nFEDomain* FEBodyConstraint::Domain(int i)\n{\n\treturn m_dom.GetDomain(i);\n}\n\nvoid FEBodyConstraint::SetDomainList(FEElementSet* elset)\n{\n\tm_dom = elset->GetDomainList();\n\n\t// add it to all the mapped parameters\n\tFEParameterList& PL = GetParameterList();\n\tFEParamIterator it = PL.first();\n\tfor (int i = 0; i < PL.Parameters(); ++i, ++it)\n\t{\n\t\tFEParam& pi = *it;\n\t\tif (pi.type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t{\n\t\t\tFEParamDouble& param = pi.value<FEParamDouble>();\n\t\t\tparam.SetItemList(elset);\n\t\t}\n\t\telse if (pi.type() == FE_PARAM_VEC3D_MAPPED)\n\t\t{\n\t\t\tFEParamVec3& param = pi.value<FEParamVec3>();\n\t\t\tparam.SetItemList(elset);\n\t\t}\n\t\telse if (pi.type() == FE_PARAM_MAT3D_MAPPED)\n\t\t{\n\t\t\tFEParamMat3d& param = pi.value<FEParamMat3d>();\n\t\t\tparam.SetItemList(elset);\n\t\t}\n\t}\n}\n\n// get the domain list\nFEDomainList& FEBodyConstraint::GetDomainList()\n{\n\treturn m_dom;\n}\n\n//! Serialization\nvoid FEBodyConstraint::Serialize(DumpStream& ar)\n{\n\tFENLConstraint::Serialize(ar);\n\tm_dom.Serialize(ar);\n}\n"
  },
  {
    "path": "FECore/FEBodyConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FENLConstraint.h\"\n#include \"FEDomain.h\"\n#include \"FEDomainList.h\"\n\n// Base class for nonlinear constraints that are defined using a surface.\nclass FECORE_API FEBodyConstraint : public FENLConstraint\n{\n\tFECORE_BASE_CLASS(FEBodyConstraint)\n\npublic:\n\tFEBodyConstraint(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t//! return number of domains this load is applied to\n\tint Domains() const;\n\n\t//! return a domain \n\tFEDomain* Domain(int i);\n\n\t//! add a domain to which to apply this load\n\tvoid SetDomainList(FEElementSet* elset);\n\n\t//! get the domain list\n\tFEDomainList& GetDomainList();\n\nprivate:\n\tFEDomainList\tm_dom;\t//!< list of domains to which to apply the body load\n};\n"
  },
  {
    "path": "FECore/FEBodyLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBodyLoad.h\"\n#include \"FEMesh.h\"\n#include \"FEModelParam.h\"\n\n//-----------------------------------------------------------------------------\nFEBodyLoad::FEBodyLoad(FEModel* pfem) : FEModelLoad(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEBodyLoad::~FEBodyLoad()\n{\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEBodyLoad::Init()\n{\n\t// If the domain list is empty, add all the domains\n\tif (m_dom.IsEmpty())\n\t{\n\t\tFEMesh& mesh = GetMesh();\n\t\tfor (int i=0; i<mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain* dom = &mesh.Domain(i);\n\t\t\tm_dom.AddDomain(dom);\n\t\t}\n\t}\n\treturn FEModelLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\nint FEBodyLoad::Domains() const\n{\n\treturn m_dom.Domains();\n}\n\n//-----------------------------------------------------------------------------\nFEDomain* FEBodyLoad::Domain(int i)\n{\n\treturn m_dom.GetDomain(i);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBodyLoad::SetDomainList(FEElementSet* elset)\n{\n\tm_dom = elset->GetDomainList();\n\n\t// add it to all the mapped parameters\n\tFEParameterList& PL = GetParameterList();\n\tFEParamIterator it = PL.first();\n\tfor (int i = 0; i < PL.Parameters(); ++i, ++it)\n\t{\n\t\tFEParam& pi = *it;\n\t\tif (pi.type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t{\n\t\t\tFEParamDouble& param = pi.value<FEParamDouble>();\n\t\t\tparam.SetItemList(elset);\n\t\t}\n\t\telse if (pi.type() == FE_PARAM_VEC3D_MAPPED)\n\t\t{\n\t\t\tFEParamVec3& param = pi.value<FEParamVec3>();\n\t\t\tparam.SetItemList(elset);\n\t\t}\n\t\telse if (pi.type() == FE_PARAM_MAT3D_MAPPED)\n\t\t{\n\t\t\tFEParamMat3d& param = pi.value<FEParamMat3d>();\n\t\t\tparam.SetItemList(elset);\n\t\t}\n\t}\n}\n\n// get the domain list\nFEDomainList& FEBodyLoad::GetDomainList()\n{ \n\treturn m_dom; \n}\n\n//! Serialization\nvoid FEBodyLoad::Serialize(DumpStream& ar)\n{\n\tFEModelLoad::Serialize(ar);\n\tm_dom.Serialize(ar);\n}\n"
  },
  {
    "path": "FECore/FEBodyLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelLoad.h\"\n#include \"FEDomain.h\"\n#include \"FEDomainList.h\"\n#include \"FESurface.h\"\n\n//-----------------------------------------------------------------------------\n// forward declaration of FEModel class\nclass FEModel;\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! Base class for body-loads\nclass FECORE_API FEBodyLoad : public FEModelLoad\n{\n\tFECORE_BASE_CLASS(FEBodyLoad)\n\npublic:\n\tFEBodyLoad(FEModel* pfem);\n\tvirtual ~FEBodyLoad();\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t//! return number of domains this load is applied to\n\tint Domains() const;\n\n\t//! return a domain \n\tFEDomain* Domain(int i);\n\n\t//! add a domain to which to apply this load\n\tvoid SetDomainList(FEElementSet* elset);\n\n\t//! get the domain list\n\tFEDomainList& GetDomainList();\n    \nprivate:\n\tFEDomainList\tm_dom;\t//!< list of domains to which to apply the body load\n};\n"
  },
  {
    "path": "FECore/FEBoundaryCondition.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBoundaryCondition.h\"\n#include \"FEFacetSet.h\"\n\n//-----------------------------------------------------------------------------\nFEBoundaryCondition::FEBoundaryCondition(FEModel* pfem) : FEStepComponent(pfem), m_dof(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEBoundaryCondition::~FEBoundaryCondition()\n{\n}\n\n//-----------------------------------------------------------------------------\n//! fill the prescribed values\nvoid FEBoundaryCondition::PrepStep(std::vector<double>& u, bool brel)\n{\n\n}\n\nvoid FEBoundaryCondition::Serialize(DumpStream& ar)\n{\n\tFEStepComponent::Serialize(ar);\n\tif (ar.IsShallow() == false) ar & m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBoundaryCondition::SetDOFList(int ndof)\n{\n\tm_dof.Clear();\n\tm_dof.AddDof(ndof);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBoundaryCondition::SetDOFList(const std::vector<int>& dofs)\n{\n\tm_dof = dofs;\n}\n\nvoid FEBoundaryCondition::SetDOFList(const FEDofList& dofs)\n{\n\tm_dof = dofs;\n}\n"
  },
  {
    "path": "FECore/FEBoundaryCondition.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEStepComponent.h\"\n#include \"FENodeSet.h\"\n#include \"FEDofList.h\"\n\n//-----------------------------------------------------------------------------\nclass FEFacetSet;\n\n//-----------------------------------------------------------------------------\n//! This class is the base class of boundary conditions.\n\n//! Boundary conditions set the \"bc\" state of nodes. The bc-state determines\n//! whether or not the dofs of the node will be assigned an equation number. \n//! Currently, there are two boundary conditions: a fixed (FEFixedBC) and a\n//! prescribed (FEPrescribedBC) boundary condition. \nclass FECORE_API FEBoundaryCondition : public FEStepComponent\n{\n\tFECORE_SUPER_CLASS(FEBC_ID)\n\tFECORE_BASE_CLASS(FEBoundaryCondition);\n\npublic:\n\t//! constructor\n\tFEBoundaryCondition(FEModel* pfem);\n\n\t//! desctructor\n\t~FEBoundaryCondition();\n\n\t//! fill the prescribed values\n\tvirtual void PrepStep(std::vector<double>& u, bool brel = true);\n\n\t// copy data from another class\n\tvirtual void CopyFrom(FEBoundaryCondition* pbc) = 0;\n    \n    // repair BC if needed\n    virtual void Repair() {}\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// TODO: Temporary construction to update some special boundary conditions in FEModel::Update\n\t//       Will likely remove this at some point.\n\tvirtual void UpdateModel() {}\n\n\npublic:\n\t// set the dof list\n\tvoid SetDOFList(int ndof);\n\tvoid SetDOFList(const std::vector<int>& dofs);\n\tvoid SetDOFList(const FEDofList& dofs);\n\n\tconst FEDofList& GetDofList() const { return m_dof; }\n\nprotected:\n\tFEDofList\tm_dof;\t// the dof list for the BC\n};\n"
  },
  {
    "path": "FECore/FEBoundingBox.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n\n//-----------------------------------------------------------------------------\n//  This class stores the coordinates of a bounding box\n//\nclass FECORE_API FEBoundingBox\n{\npublic:\n\tFEBoundingBox() {}\n\tFEBoundingBox(const vec3d& x) : r0(x), r1(x) {}\n\tFEBoundingBox(const vec3d& x0, const vec3d& x1) : r0(x0), r1(x1) {}\n\n\t// center of box\n\tvec3d center() const { return (r0 + r1)*0.5; }\n\n\t// dimensions of box\n\tdouble width() const { return (r1.x - r0.x); }\n\tdouble height() const { return (r1.y - r0.y); }\n\tdouble depth() const { return (r1.z - r0.z); }\n\n\t// max dimension\n\tdouble radius() const\n\t{\n\t\tdouble w = width();\n\t\tdouble h = height();\n\t\tdouble d = depth();\n\n\t\tif ((w >= d) && (w >= h)) return w;\n\t\tif ((h >= w) && (h >= d)) return h;\n\n\t\treturn d;\n\t}\n\n\t// add a point and grow the box if necessary\n\tvoid add(const vec3d& r)\n\t{\n\t\tif (r.x < r0.x) r0.x = r.x;\n\t\tif (r.y < r0.y) r0.y = r.y;\n\t\tif (r.z < r0.z) r0.z = r.z;\n\t\tif (r.x > r1.x) r1.x = r.x;\n\t\tif (r.y > r1.y) r1.y = r.y;\n\t\tif (r.z > r1.z) r1.z = r.z;\n\t}\n\n\t// inflate the box\n\tvoid inflate(double dx, double dy, double dz)\n\t{\n\t\tr0.x -= dx; r1.x += dx;\n\t\tr0.y -= dy; r1.y += dy;\n\t\tr0.z -= dz; r1.z += dz;\n\t}\n\n\t// translate the box\n\tvoid translate(const vec3d& t)\n\t{\n\t\tr0 += t;\n\t\tr1 += t;\n\t}\n\n\t// check whether a point is inside or not\n\tbool IsInside(const vec3d& r) const\n\t{\n\t\treturn ((r.x >= r0.x) && (r.y >= r0.y) && (r.z >= r0.z) && (r.x <= r1.x) && (r.y <= r1.y) && (r.z <= r1.z));\n\t}\n\nprivate:\n\tvec3d\tr0, r1; // coordinates of opposite corners\n};\n"
  },
  {
    "path": "FECore/FEBox.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEBox.h\"\n#include \"FEMesh.h\"\n#include \"FEMeshPartition.h\"\n\nFEBox::FEBox()\n{\n}\n\nFEBox::FEBox(const vec3d& r0, const vec3d& r1) : m_r0(r0), m_r1(r1)\n{\n}\n\nFEBox::FEBox(const FEMesh& mesh)\n{\n\tm_r0 = m_r1 = vec3d(0,0,0);\n\tint N = mesh.Nodes();\n\tif (N > 0)\n\t{\n\t\tm_r0 = m_r1 = mesh.Node(0).m_rt;\n\t\tfor (int i=1; i<N; ++i)\n\t\t{\n\t\t\tconst FENode& ni = mesh.Node(i);\n\t\t\tadd(ni.m_rt);\n\t\t}\n\t}\n}\n\nFEBox::FEBox(const FEMeshPartition& dom)\n{\n\tm_r0 = m_r1 = vec3d(0,0,0);\n\tint N = dom.Nodes();\n\tif (N > 0)\n\t{\n\t\tm_r0 = m_r1 = dom.Node(0).m_rt;\n\t\tfor (int i=1; i<N; ++i)\n\t\t{\n\t\t\tconst FENode& ni = dom.Node(i);\n\t\t\tadd(ni.m_rt);\n\t\t}\n\t}\n}\n\ndouble FEBox::maxsize()\n{\n\tdouble dx = fabs(width());\n\tdouble dy = fabs(height());\n\tdouble dz = fabs(depth());\n\n\tdouble D = dx;\n\tif (dy > D) D = dy;\n\tif (dz > D) D = dz;\n\n\treturn D;\n}\n\nvoid FEBox::add(const vec3d& r)\n{\n\tif (r.x < m_r0.x) m_r0.x = r.x; if (r.x > m_r1.x) m_r1.x = r.x;\n\tif (r.y < m_r0.y) m_r0.y = r.y; if (r.y > m_r1.y) m_r1.y = r.y;\n\tif (r.z < m_r0.z) m_r0.z = r.z; if (r.z > m_r1.z) m_r1.z = r.z;\n}\n"
  },
  {
    "path": "FECore/FEBox.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMesh;\nclass FEMeshPartition;\n\n//-----------------------------------------------------------------------------\n// Helper class for finding bounding boxes.\nclass FECORE_API FEBox\n{\npublic:\n\tFEBox();\n\tFEBox(const vec3d& r0, const vec3d& r1);\n\n\tFEBox(const FEMesh& mesh);\n\tFEBox(const FEMeshPartition& dom);\n\n\tvec3d center() { return (m_r0 + m_r1)*0.5; }\n\t\n\tdouble width () { return m_r1.x - m_r0.x; };\t//!< x-size\n\tdouble height() { return m_r1.y - m_r0.y; };\t//!< y-size\n\tdouble depth () { return m_r1.z - m_r0.z; };\t//!< z-size\n\n\t// the maximum size\n\tdouble maxsize();\n\n\t// union of box and node\n\tvoid add(const vec3d& r);\n\nprivate:\n\tvec3d\tm_r0, m_r1;\n};\n"
  },
  {
    "path": "FECore/FEBroydenStrategy.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEBroydenStrategy.h\"\n#include \"LinearSolver.h\"\n#include \"FEException.h\"\n#include \"FENewtonSolver.h\"\n#include \"log.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEBroydenStrategy, FENewtonStrategy)\n\tADD_PARAMETER(m_maxups, \"max_ups\");\n\tADD_PARAMETER(m_max_buf_size, FE_RANGE_GREATER_OR_EQUAL(0), \"max_buffer_size\"); \n\tADD_PARAMETER(m_cycle_buffer, \"cycle_buffer\");\n\tADD_PARAMETER(m_cmax, FE_RANGE_GREATER_OR_EQUAL(0.0), \"cmax\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEBroydenStrategy::FEBroydenStrategy(FEModel* fem) : FENewtonStrategy(fem)\n{\n\tm_neq = 0;\n\tm_plinsolve = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization\nbool FEBroydenStrategy::Init()\n{\n\tif (m_pns == nullptr) return false;\n\n\tif (m_max_buf_size <= 0) m_max_buf_size = m_maxups;\n\n\tint neq = m_pns->m_neq;\n\n\t// allocate storage for Broyden update vectors\n\tm_R.resize(m_max_buf_size, neq);\n\tm_D.resize(m_max_buf_size, neq);\n\tm_rho.resize(m_max_buf_size);\n\tm_q.resize(neq, 0.0);\n\n\tm_neq = neq;\n\tm_nups = 0;\n\n\tm_plinsolve = m_pns->GetLinearSolver();\n\n\tm_bnewStep = true;\n\n\treturn true;\n}\n\n//! Presolve update\nvoid FEBroydenStrategy::PreSolveUpdate()\n{\n\tm_bnewStep = true;\n}\n\n//-----------------------------------------------------------------------------\n//! perform a quasi-Newton udpate\nbool FEBroydenStrategy::Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1)\n{\n\t// for full-Newton, we skip QN update\n\tif (m_maxups == 0) return false;\n\n\t// make sure we didn't reach max updates\n\tif (m_nups >= m_maxups - 1)\n\t{\n\t\tfeLogWarning(\"Max nr of iterations reached.\\nStiffness matrix will now be reformed.\");\n\t\treturn false;\n\t}\n\n\t// calculate q1\n\tm_q.assign(m_neq, 0.0);\n\tif (m_plinsolve->BackSolve(m_q, R1) == false)\n\t\tthrow LinearSolverFailed();\n\n\t// only update when allowed\n\tif ((m_nups < m_max_buf_size) || (m_cycle_buffer == true))\n\t{\n\t\t// since the number of updates can be larger than buffer size we need to clamp\n\t\tint nups = (m_nups >= m_max_buf_size ? m_max_buf_size - 1 : m_nups);\n\n\t\t// get the \"0\" buffer index\n\t\tint n0 = (m_nups >= m_max_buf_size ? (m_nups + 1) % m_max_buf_size : 0);\n\t\tint n1 = (m_nups >= m_max_buf_size ? (m_nups) % m_max_buf_size : m_nups);\n\n\t\t// loop over update vectors\n\t\tfor (int j = 0; j<nups; ++j)\n\t\t{\n\t\t\tint n = (n0 + j) % m_max_buf_size;\n\n\t\t\tdouble w = 0.0;\n\t\t\tfor (int i = 0; i<m_neq; ++i) w += (m_D[n][i] * m_q[i]);\n\n\t\t\tdouble g = m_rho[n] * w;\n\t\t\tfor (int i = 0; i<m_neq; ++i) m_q[i] += g*(m_D[n][i] - m_R[n][i]);\n\t\t}\n\n\t\t// form and store the next update vector\n\t\tdouble rhoi = 0.0;\n\t\tfor (int i = 0; i<m_neq; ++i)\n\t\t{\n\t\t\tdouble ri = m_q[i] - ui[i];\n\t\t\tdouble di = -s*ui[i];\n\t\t\tm_R[n1][i] = ri;\n\t\t\tm_D[n1][i] = di;\n\n\t\t\trhoi += di*ri;\n\t\t}\n\t\tm_rho[n1] = 1.0 / (rhoi);\n\t}\n\n\tm_nups++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! solve the equations\nvoid FEBroydenStrategy::SolveEquations(vector<double>& x, vector<double>& b)\n{\n\t// loop over update vectors\n\tif (m_nups == 0)\n\t{\n\t\tif (m_plinsolve->BackSolve(x, b) == false)\n\t\t\tthrow LinearSolverFailed();\n\n\t\tm_bnewStep = false;\n\t}\n\telse\n\t{\n\t\t// since the number of updates can be larger than buffer size we need to clamp\n\t\tint nups = (m_nups > m_max_buf_size ? m_max_buf_size : m_nups);\n\n\t\t// get the first and last buffer index\n\t\tint n0 = 0;\n\t\tint n1 = nups - 1;\n\t\tif ((m_nups > m_max_buf_size) && (m_cycle_buffer == true))\n\t\t{\n\t\t\tn0 = m_nups % m_max_buf_size;\n\t\t\tn1 = (m_nups - 1) % m_max_buf_size;\n\t\t}\n\n\t\tif (m_bnewStep)\n\t\t{\n\t\t\tm_q = x;\n\t\t\tif (m_plinsolve->BackSolve(m_q, b) == false)\n\t\t\t\tthrow LinearSolverFailed();\n\n\t\t\tfor (int j = 0; j<nups - 1; ++j)\n\t\t\t{\n\t\t\t\tint n = (n0 + j) % m_max_buf_size;\n\n\t\t\t\tdouble w = 0.0;\n\t\t\t\tfor (int i = 0; i<m_neq; ++i) w += (m_D[n][i] * m_q[i]);\n\n\t\t\t\tdouble g = m_rho[n] * w;\n\t\t\t\tfor (int i = 0; i<m_neq; ++i) m_q[i] += g*(m_D[n][i] - m_R[n][i]);\n\t\t\t}\n\n\t\t\tm_bnewStep = false;\n\t\t}\n\n\t\t// calculate solution\n\t\tdouble rho = 0.0;\n\t\tfor (int i = 0; i<m_neq; ++i) rho += m_D[n1][i] * m_q[i];\n\t\trho *= m_rho[n1];\n\n\t\tfor (int i = 0; i<m_neq; ++i)\n\t\t{\n\t\t\tx[i] = m_q[i] + rho*(m_D[n1][i] - m_R[n1][i]);\n\t\t}\n\t}\n}\n\n/*\n//-----------------------------------------------------------------------------\n//! perform a quasi-Newton udpate\nbool FEBroydenStrategy::Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1)\n{\n\t// calculate q1\n\t// TODO: q is not initialized. Don't the iterative solvers use this as an initial guess?\n\t// TODO: Can I use ui as an initial guess for q?\n\tvector<double> q(m_neq);\n\tif (m_plinsolve->BackSolve(q, R1) == false)\n\t\tthrow LinearSolverFailed();\n\n\t// only update when allowed\n\tif ((m_nups < m_max_buf_size) || (m_cycle_buffer == true))\n\t{\n\t\t// since the number of updates can be larger than buffer size we need to clamp\n\t\tint nups = (m_nups >= m_max_buf_size ? m_max_buf_size - 1: m_nups);\n\n\t\t// get the \"0\" buffer index\n\t\tint n0 = (m_nups >= m_max_buf_size ? (m_nups + 1) % m_max_buf_size : 0);\n\t\tint n1 = (m_nups >= m_max_buf_size ? (m_nups    ) % m_max_buf_size : m_nups);\n\n\t\t// loop over update vectors\n\t\tfor (int j=0; j<nups; ++j)\n\t\t{\n\t\t\tint n = (n0 + j) % m_max_buf_size;\n\n\t\t\tdouble w = 0.0;\n\t\t\tfor (int i=0; i<m_neq; ++i) w += (m_D[n][i]*q[i]);\n\n\t\t\tdouble g = m_rho[n]*w;\n\t\t\tfor (int i=0; i<m_neq; ++i) q[i] += g*(m_D[n][i] - m_R[n][i]);\n\t\t}\n\n\t\t// form and store the next update vector\n\t\tdouble rhoi = 0.0;\n\t\tfor (int i=0; i<m_neq; ++i)\n\t\t{\n\t\t\tdouble ri = q[i] - ui[i];\n\t\t\tdouble di = -s*ui[i];\n\t\t\tm_R[n1][i] = ri;\n\t\t\tm_D[n1][i] = di;\n\n\t\t\trhoi += di*ri;\n\t\t}\n\t\tm_rho[n1] = 1.0 / (rhoi);\n\t}\n\n\tm_nups++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! solve the equations\nvoid FEBroydenStrategy::SolveEquations(vector<double>& x, vector<double>& b)\n{\n\t// loop over update vectors\n\tif (m_nups == 0)\n\t{\n\t\tif (m_plinsolve->BackSolve(x, b) == false)\n\t\t\tthrow LinearSolverFailed();\n\t}\n\telse\n\t{\n\t\t// calculate q1\n\t\tvector<double> q(m_neq);\n\t\tif (m_plinsolve->BackSolve(q, b) == false)\n\t\t\tthrow LinearSolverFailed();\n\n\t\t// since the number of updates can be larger than buffer size we need to clamp\n\t\tint nups = (m_nups > m_max_buf_size ? m_max_buf_size : m_nups);\n\n\t\t// get the first and last buffer index\n\t\tint n0 = 0;\n\t\tint n1 = nups - 1;\n\t\tif ((m_nups > m_max_buf_size) && (m_cycle_buffer == true))\n\t\t{\n\t\t\tn0 = m_nups % m_max_buf_size;\n\t\t\tn1 = (m_nups - 1) % m_max_buf_size;\n\t\t}\n\n\t\tfor (int j=0; j<nups-1; ++j)\n\t\t{\n\t\t\tint n = (n0 + j) % m_max_buf_size;\n\n\t\t\tdouble w = 0.0;\n\t\t\tfor (int i=0; i<m_neq; ++i) w += (m_D[n][i]*q[i]);\n\n\t\t\tdouble g = m_rho[n]*w;\n\t\t\tfor (int i=0; i<m_neq; ++i) q[i] += g*(m_D[n][i] - m_R[n][i]);\n\t\t}\n\n\t\t// calculate solution\n\t\tdouble rho = 0.0;\n\t\tfor (int i=0; i<m_neq; ++i) rho += m_D[n1][i]*q[i];\n\t\trho *= m_rho[n1];\n\t\n\t\tfor (int i=0; i<m_neq; ++i)\n\t\t{\n\t\t\tx[i] = q[i] + rho*(m_D[n1][i] - m_R[n1][i]);\n\t\t}\n\t}\n}\n*/\n"
  },
  {
    "path": "FECore/FEBroydenStrategy.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"matrix.h\"\n#include \"FENewtonStrategy.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements the Broyden quasi-newton strategy. \nclass FECORE_API FEBroydenStrategy : public FENewtonStrategy\n{\npublic:\n\t//! constructor\n\tFEBroydenStrategy(FEModel* fem);\n\n\t//! Initialization\n\tbool Init() override;\n\n\t//! perform a quasi-Newton udpate\n\tbool Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1) override;\n\n\t//! solve the equations\n\tvoid SolveEquations(vector<double>& x, vector<double>& b) override;\n\n\t//! Presolve update\n\tvirtual void PreSolveUpdate() override;\n\nprivate:\n\t// keep a pointer to the linear solver\n\tLinearSolver*\tm_plinsolve;\t//!< pointer to linear solver\n\tint\t\t\t\tm_neq;\t\t\t//!< number of equations\n\n\tbool\t\tm_bnewStep;\n\n\t// Broyden update vectors\n\tmatrix\t\t\tm_R;\t\t//!< Broyden update vector \"r\"\n\tmatrix\t\t\tm_D;\t\t//!< Broydeb update vector \"delta\"\n\tvector<double>\tm_rho;\t\t//!< temp vectors for calculating Broyden update vectors\n\tvector<double>\tm_q;\t\t//!< temp storage for q\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FECallBack.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelComponent.h\"\n\n//-----------------------------------------------------------------------------\n// Forward declaration of the FEModel class.\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// This class implements a mechanism for defining callbacks from within plugins.\nclass FECORE_API FECallBack : public FEModelComponent\n{\n\tFECORE_SUPER_CLASS(FECALLBACK_ID)\n\tFECORE_BASE_CLASS(FECallBack)\n\npublic:\n\t// constructor requires the WHEN parameter (defined in FEModel.h)\n\tFECallBack(FEModel* pfem, int when);\n\n\t// Override this function in the derived class.\n\tvirtual bool Execute(FEModel& fem, int nwhen) = 0;\n};\n"
  },
  {
    "path": "FECore/FECallback.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECallBack.h\"\n#include \"FEModel.h\"\n\nbool do_FECallBack_cb(FEModel* pfem, unsigned int nwhen, void* pd)\n{\n\tFECallBack* pCB = (FECallBack*) (pd);\n\treturn pCB->Execute(*pfem, nwhen);\n}\n\nFECallBack::FECallBack(FEModel* fem, int when) : FEModelComponent(fem)\n{\n\tif (fem) fem->AddCallback(do_FECallBack_cb, when, this);\n}\n"
  },
  {
    "path": "FECore/FEClosestPointProjection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEClosestPointProjection.h\"\n#include \"FEElemElemList.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\n// constructor\nFEClosestPointProjection::FEClosestPointProjection(FESurface& s) : m_surf(s)\n{\n\t// set default options\n\tm_tol = 0.01;\n\tm_rad = 0.0;\t// 0 means don't use search radius\n\tm_bspecial = false;\n\tm_projectBoundary = false;\n\n\t// calculate node-element list\n\tm_NEL.Create(m_surf);\n\tm_EEL.Create(&m_surf);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialization of data structures\nbool FEClosestPointProjection::Init()\n{\n\t// initialize the nearest neighbor search\n\tm_SNQ.Attach(&m_surf);\n\tm_SNQ.Init();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// helper function for projecting a point onto an edge\nbool Project2Edge(const vec3d& p0, const vec3d& p1, const vec3d& x, vec3d& q)\n{\n\tvec3d e = p1 - p0;\n\tdouble D = e*e;\n\tif (D == 0.0) return false;\n\n\tdouble l = (e*(x - p0))/D;\n\n\tq = p0 + e*l;\n\n\treturn (l>=0.0)&&(l<=1.0);\n}\n\n\n//-----------------------------------------------------------------------------\nclass FEPatch\n{\npublic:\n\tFEPatch(FESurface* surf, FEElement** ppe, int n)\n\t{\n\t\tm_surf = surf;\n\t\tm_ppe = ppe;\n\t\tm_nval = n;\n\t}\n\n\tbool HasNode(int n)\n\t{\n\t\tfor (int i = 0; i < m_nval; ++i)\n\t\t{\n\t\t\t// get the element\n\t\t\tFESurfaceElement& el = static_cast<FESurfaceElement&> (*m_ppe[i]);\n\t\t\tif (el.HasNode(n))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tbool Contains(FESurfaceElement& el)\n\t{\n\t\tfor (int j = 0; j < m_nval; ++j)\n\t\t{\n\t\t\tFESurfaceElement& ei = *Element(j);\n\t\t\tif ((el.Type() == ei.Type()) && (el.Nodes() == ei.Nodes()))\n\t\t\t{\n\t\t\t\tif (ei.HasNodes(&(el.m_node)[0], el.Nodes()) != 0)\n\t\t\t\t{\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tFESurface* GetSurface() { return m_surf; }\n\n\tint Size() const { return m_nval; }\n\tFESurfaceElement* Element(int n) { return dynamic_cast<FESurfaceElement*>(m_ppe[n]); }\n\nprivate:\n\tFESurface*\tm_surf;\n\tFEElement**\tm_ppe;\n\tint\t\t\tm_nval;\n};\n\n// project a point to a patch\nFESurfaceElement* projectToPatch(FEPatch& patch, const vec3d& x, vec3d& q, double& r, double& s, double tol)\n{\n\tFESurfaceElement* pemin = nullptr;\n\tint nval = patch.Size();\n\tFESurface* surf = patch.GetSurface();\n\tdouble d2min = 0;\n\tfor (int j = 0; j < nval; ++j)\n\t{\n\t\t// get the element\n\t\tFESurfaceElement& el = *patch.Element(j);\n\t\tif (el.isActive())\n\t\t{\n\t\t\tint N = el.Nodes();\n\n\t\t\t// project the node on the element\n\t\t\tdouble p[2] = { 0, 0 };\n\t\t\tvec3d qj = surf->ProjectToSurface(el, x, p[0], p[1]);\n\t\t\tdouble d2 = (qj - x).norm2();\n\t\t\tif (surf->IsInsideElement(el, p[0], p[1], tol))\n\t\t\t{\n\t\t\t\tif ((pemin == nullptr) || (d2 < d2min))\n\t\t\t\t{\n\t\t\t\t\td2min = d2;\n\t\t\t\t\tq = qj;\n\t\t\t\t\tr = p[0];\n\t\t\t\t\ts = p[1];\n\t\t\t\t\tpemin = &el;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pemin;\n}\n\n//-----------------------------------------------------------------------------\n//! Project the node on the surface. This function returns a pointer to the\n//! surface element if the projection is succesful, otherwise null. The natural\n//! coordinates of the projection is return in r and the spatial coordinates in q.\n//! \nFESurfaceElement* FEClosestPointProjection::Project(const vec3d& x, vec3d& q, vec2d& r)\n{\n\t// get the mesh\n\tFEMesh& mesh = *m_surf.GetMesh();\n\n\t// let's find the closest node\n\tint mn = m_SNQ.Find(x);\n\tif (mn < 0) return nullptr;\n\n\t// make sure it is within the search radius\n\tvec3d rm = m_surf.Node(mn).m_rt;\n\tdouble d2 = (x - rm)*(x - rm);\n\tif ((m_rad > 0) && (d2 > m_rad))\n\t{\n\t\treturn nullptr;\n\t}\n\n\t// now that we found the closest node, lets see if we can find \n\t// the best element\n\tFEPatch patch(&m_surf, m_NEL.ElementList(mn), m_NEL.Valence(mn));\n\tFESurfaceElement* pe = projectToPatch(patch, x, q, r[0], r[1], m_tol);\n\tif (pe) return pe;\n\n\t// If we get here, we did not find a facet.\n\t// There are a couple of reasons why the search could fail:\n\t// -1. the point cannot be projected onto the surface. For contact this implies the node is not in contact.\n\t// -2. the projection falls outside the set of elements surrounding the closest point.\n\t// -3. the projection falls on an edge of two faces whos normals are pointing away.\n\t// -4. the closest node is in fact the closest point and no closer projection on face or edge can be found\n\t//\n\tif (m_bspecial)\n\t{\n\t\treturn ProjectSpecial(mn, x, q, r);\n\t}\n\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! This is slightly modified version of the ClosestPointProjection where the\n//! node to be projected is part of this surface. This is used in some contact algorithms\n//! that support self-contact. The algorithm is modified in two ways. First, a search_radius\n//! limits the max distance for consideration of closest point. Second, the search point cannot\n//! be part of the star of the closest point.\nFESurfaceElement* FEClosestPointProjection::Project(int nodeIndex, vec3d& q, vec2d& r)\n{\n\t// get the mesh\n\tFEMesh& mesh = *m_surf.GetMesh();\n\n\t// get the node's position\n\tvec3d x = mesh.Node(nodeIndex).m_rt;\n\n\t// Find the closest surface node to x that:\n\t// 1. is within the search radius\n\t// 2. its star does not contain n\n\tint mn = -1;\t// local index of closest node\n\tdouble d2min;\t// min squared distance\n\tint N = m_surf.Nodes();\n\tdouble R2 = m_rad * m_rad;\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tif (m_surf.NodeIndex(i) != nodeIndex)\n\t\t{\n\t\t\tvec3d r = m_surf.Node(i).m_rt;\n\t\t\tdouble d2 = (r - x)*(r - x);\n\n\t\t\t// make sure the node lies within the search radius\n\t\t\tif ((m_rad == 0) || (d2 <= R2))\n\t\t\t{\n\t\t\t\tbool bok = true;\n\n\t\t\t\t// The node cannot be part of the star of the closest point\n\t\t\t\tFEPatch patch(&m_surf, m_NEL.ElementList(i), m_NEL.Valence(i));\n\t\t\t\tif (patch.HasNode(nodeIndex))\n\t\t\t\t{\n\t\t\t\t\tbok = false;\n\t\t\t\t}\n\n\t\t\t\t// make sure the point is closer than the last one\n\t\t\t\tif (bok && ((mn == -1) || (d2 < d2min)))\n\t\t\t\t{\n\t\t\t\t\td2min = d2;\n\t\t\t\t\tmn = i;\n\t\t\t\t\tq = r;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (mn == -1) return nullptr;\n\n\t// now that we found the closest node, lets see if we can find \n\t// the best element\n\tFEPatch patch(&m_surf, m_NEL.ElementList(mn), m_NEL.Valence(mn));\n\tFESurfaceElement* pemin = projectToPatch(patch, x, q, r[0], r[1], m_tol);\n\tif (pemin) return pemin;\n\n\t// If we get here, we did not find a facet.\n\t// There are a couple of reasons why the search has failed:\n\t// -1. the point cannot be projected onto the surface. For contact this implies the node is not in contact.\n\t// -2. the projection falls outside the set of elements surrounding the closest point.\n\t// -3. the projection falls on an edge of two faces whos normals are pointing away.\n\t// -4. the closest node is in fact the closest point and no closer projection on face or edge can be found\n\t//\n\tif (m_bspecial)\n\t{\n\t\treturn ProjectSpecial(mn, x, q, r);\n\t}\n\n\treturn nullptr;\n}\n\n//! Project a point of a surface element onto a surface\nFESurfaceElement* FEClosestPointProjection::Project(FESurfaceElement* pse, int intgrPoint, vec3d& q, vec2d& r)\n{\n\tassert(pse);\n\tif (pse == nullptr) return nullptr;\n\n\t// get the mesh\n\tFEMesh& mesh = *m_surf.GetMesh();\n\n\t// get the node's position\n\tint nn = pse->Nodes();\n\tvec3d re[FEElement::MAX_NODES];\n\tfor (int l = 0; l < nn; ++l) re[l] = mesh.Node(pse->m_node[l]).m_rt;\n\tvec3d x = pse->eval(re, intgrPoint);\n\n\t// if the element belongs to this surface\n\t// we want to prevent this element and its immediate neigbors\n\t// from being the closest.\n\tbool check_self_projection = false;\n\tif (ContainsElement(pse))\n\t{\n\t\tcheck_self_projection = true;\n\t}\n\n\t// find the closest point\n\tint mn = -1;\n\tdouble d2min;\n\tdouble R2 = m_rad * m_rad;\n\tint N = m_surf.Nodes();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tvec3d ri = m_surf.Node(i).m_rt;\n\t\tdouble d2 = (ri - x)*(ri - x);\n\n\t\t// make sure the node lies within the search radius\n\t\tif ((R2 == 0) || (d2 <= R2))\n\t\t{\n\t\t\tbool bok = true;\n\t\t\tif (check_self_projection)\n\t\t\t{\n\t\t\t\t// The pse element cannot be part of the star of the closest point\n\t\t\t\tFEPatch patch(&m_surf, m_NEL.ElementList(i), m_NEL.Valence(i));\n\t\t\t\tif (patch.Contains(*pse))\n\t\t\t\t{\n\t\t\t\t\tbok = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (bok && ((mn == -1) || (d2 < d2min)))\n\t\t\t{\n\t\t\t\td2min = d2;\n\t\t\t\tmn = i;\n\t\t\t\tq = ri;\n\t\t\t}\n\t\t}\n\t}\n\tif (mn == -1) return nullptr;\n\n\t// mn is a local index, so get the global node number too\n\tint m = m_surf.NodeIndex(mn);\n\n\t// get the nodal position\n\tvec3d r0 = mesh.Node(m).m_rt;\n\n\t// check the distance\n\tdouble D = (x - r0).norm();\n\tif ((m_rad > 0) && (D > m_rad)) return 0;\n\n\t// now that we found the closest node, lets see if we can find \n\t// the best element\n\tFEPatch patch(&m_surf, m_NEL.ElementList(mn), m_NEL.Valence(mn));\n\tFESurfaceElement* pe = projectToPatch(patch, x, q, r[0], r[1], m_tol);\n\tif (pe) return pe;\n\n\t// If we get here, we did not find a facet.\n\t// handle special cases\n\tif (m_bspecial)\n\t{\n\t\treturn ProjectSpecial(mn, x, q, r);\n\t}\n\n\treturn nullptr;\n}\n\nbool FEClosestPointProjection::ContainsElement(FESurfaceElement* el)\n{\n\tif (el == nullptr) return false;\n\n\tint n = el->m_lnode[0];\n\tif ((n < 0) || (n >= m_surf.Nodes())) return false;\n\n\tint nval = m_NEL.Valence(n);\n\tFEElement** pe = m_NEL.ElementList(n);\n\tfor (int i = 0; i < nval; ++i)\n\t{\n\t\tFESurfaceElement& ei = dynamic_cast<FESurfaceElement&>(*(pe[i]));\n\n\t\tint m = el->Nodes();\n\t\tif ((el->Type() == ei.Type()) && (ei.Nodes() == m))\n\t\t{\n\t\t\tif (el->HasNodes(&ei.m_node[0], m) != 0)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n//-------------------------------------------------------------------------------------------\n// This function handles special cases for closest-point searches\n// -1. the point cannot be projected onto the surface. For contact this implies the node is not in contact.\n// -2. the projection falls outside the set of elements surrounding the closest point.\n// -3. the projection falls on an edge of two faces whos normals are pointing away.\n// -4. the closest node is in fact the closest point and no closer projection on face or edge can be found\n//\nFESurfaceElement* FEClosestPointProjection::ProjectSpecial(int closestPoint, const vec3d& x, vec3d& q, vec2d& r)\n{\n\tFEMesh& mesh = *m_surf.GetMesh();\n\tint nval = m_NEL.Valence(closestPoint);\n\tFEElement** pe = m_NEL.ElementList(closestPoint);\n\tint* eil = m_NEL.ElementIndexList(closestPoint);\n\tint m = m_surf.NodeIndex(closestPoint);\n\n\t// get position of closest point\n\tvec3d rm = m_surf.Node(closestPoint).m_rt;\n\n\tFESurfaceElement* pemin = nullptr;\n\tdouble D2min = (x - rm).norm2();\n\n\t// First, let's redo the search but with a larger search radius\n\t// NOTE: This is highly inefficient since multiple nodes and faces are visited multiple times\n\tfor (int i = 0; i < nval; ++i)\n\t{\n\t\t// get the element\n\t\tFESurfaceElement& el = static_cast<FESurfaceElement&> (*pe[i]);\n\t\tint N = el.Nodes();\n\t\tfor (int j = 0; j < N; ++j)\n\t\t{\n\t\t\tint mj = el.m_lnode[j];\n\t\t\tint nj = m_NEL.Valence(mj);\n\t\t\tFEElement** pej = m_NEL.ElementList(mj);\n\t\t\tfor (int k = 0; k < nj; ++k)\n\t\t\t{\n\t\t\t\tFESurfaceElement& ek = static_cast<FESurfaceElement&> (*pej[k]);\n\n\t\t\t\t// project the node on the element\n\t\t\t\tdouble p[2] = { 0, 0 };\n\t\t\t\tvec3d qk = m_surf.ProjectToSurface(ek, x, p[0], p[1]);\n\t\t\t\tif (m_surf.IsInsideElement(ek, p[0], p[1], m_tol))\n\t\t\t\t{\n\t\t\t\t\tdouble D2 = (x - qk).norm2();\n\t\t\t\t\tif (D2 < D2min)\n\t\t\t\t\t{\n\t\t\t\t\t\tpemin = &ek;\n\t\t\t\t\t\tq = qk;\n\t\t\t\t\t\tr = vec2d(p[0], p[1]);\n\t\t\t\t\t\tD2min = D2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (pemin) return pemin;\n\n\t// This tries to handle some of the special cases. \n\t// First we try to find an edge this point projects on\n\tfor (int j = 0; j < nval; ++j)\n\t{\n\t\t// get an element\n\t\tFESurfaceElement& el = static_cast<FESurfaceElement&> (*pe[j]);\n\n\t\t// loop over facet edges\n\t\tint N = el.facet_edges();\n\t\tfor (int k = 0; k < N; ++k)\n\t\t{\n\t\t\t// if projection on boundary edges are not allowed\n\t\t\t// make sure the element has a neighbor\n\t\t\tif (m_projectBoundary || m_EEL.Neighbor(eil[j], k))\n\t\t\t{\n\t\t\t\t// get the two edge node indices\n\t\t\t\tint en[3];\n\t\t\t\tel.facet_edge(k, en);\n\t\t\t\tint nk1 = en[0];\n\t\t\t\tint nk2 = en[1];\n\n\t\t\t\t// make sure one of them is our closest point\n\t\t\t\tif ((nk1 == closestPoint) || (nk2 == closestPoint))\n\t\t\t\t{\n\t\t\t\t\t// try to project it on the edge\n\t\t\t\t\tvec3d p0 = m_surf.Node(nk1).m_rt;\n\t\t\t\t\tvec3d p1 = m_surf.Node(nk2).m_rt;\n\t\t\t\t\tvec3d qk;\n\t\t\t\t\tif (Project2Edge(p0, p1, x, qk))\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble p[2] = { 0, 0 };\n\t\t\t\t\t\tvec3d qp = m_surf.ProjectToSurface(el, qk, p[0], p[1]);\n\t\t\t\t\t\tif (m_surf.IsInsideElement(el, p[0], p[1], m_tol))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// see if this is a closer projection\n\t\t\t\t\t\t\tdouble D2 = (x - qp).norm2();\n\t\t\t\t\t\t\tif (D2 < D2min)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpemin = &el;\n\t\t\t\t\t\t\t\tq = qp;\n\t\t\t\t\t\t\t\tD2min = D2;\n\t\t\t\t\t\t\t\tr = vec2d(p[0], p[1]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (pemin) return pemin;\n\n\t// if we get here then no edge was found.\n\t// This can imply that the projection is in fact the closest node\n\n\t// first we want to make sure that the node does not lie on the boundary if\n\t// boundary projects are not allowed\n\tif (m_projectBoundary == false)\n\t{\n\t\tfor (int j = 0; j < nval; ++j)\n\t\t{\n\t\t\t// get an element\n\t\t\tFESurfaceElement& el = static_cast<FESurfaceElement&> (*pe[j]);\n\n\t\t\t// make sure that the node does not lie on a boundary edge\n\t\t\tint N = el.facet_edges();\n\t\t\tfor (int k = 0; k < N; ++k)\n\t\t\t{\n\t\t\t\tif (m_EEL.Neighbor(eil[j], k) == 0)\n\t\t\t\t{\n\t\t\t\t\tint n0 = el.m_node[k];\n\t\t\t\t\tint n1 = el.m_node[(k + 1) % N];\n\t\t\t\t\tif ((n0 == m) || (n1 == m)) return 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Any element can now be used but just to be safe, we loop over all\n\t// and make sure the element actually contains the closest point.\n\tfor (int j = 0; j < nval; ++j)\n\t{\n\t\t// get an element\n\t\tFESurfaceElement& el = static_cast<FESurfaceElement&> (*pe[j]);\n\n\t\t// make sure that the node does not lie on a boundary edge\n\t\tint N = el.facet_edges();\n\t\tif (m_projectBoundary == false)\n\t\t{\n\t\t\tfor (int k = 0; k < N; ++k)\n\t\t\t{\n\t\t\t\tif (m_EEL.Neighbor(eil[j], k) == 0)\n\t\t\t\t{\n\t\t\t\t\tint n0 = el.m_node[k];\n\t\t\t\t\tint n1 = el.m_node[(k + 1) % N];\n\t\t\t\t\tif ((n0 == m) || (n1 == m)) return 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// make sure one of them is our closest point\n\t\tif (el.HasNode(m))\n\t\t{\n\t\t\tq = rm;\n\t\t\treturn &el;\n\t\t}\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "FECore/FEClosestPointProjection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESurface.h\"\n#include \"FENNQuery.h\"\n#include \"FEElemElemList.h\"\n#include \"FENodeElemList.h\"\n\n//-----------------------------------------------------------------------------\n// This class can be used to find the closest point projection of a point\n// onto a surface.\nclass FECORE_API FEClosestPointProjection\n{\npublic:\n\t//! constructor\n\tFEClosestPointProjection(FESurface& s);\n\n\t//! Initialization\n\tbool Init();\n\n\t//! Project a point onto surface\n\tFESurfaceElement* Project(const vec3d& x, vec3d& q, vec2d& r);\n\n\t//! Project a node onto a surface\n\tFESurfaceElement* Project(int n, vec3d& q, vec2d& r);\n\n\t//! Project a point of a surface element onto a surface\n\tFESurfaceElement* Project(FESurfaceElement* pse, int intgrPoint, vec3d& q, vec2d& r);\n\npublic:\n\t//! Set the projection tolerance\n\tvoid SetTolerance(double t) { m_tol = t; }\n\n\t//! get the projection tolerance\n\tdouble GetTolerance() { return m_tol; }\n\n\t//! Set the search radius (used in self-projection)\n\tvoid SetSearchRadius(double s) { m_rad = s; }\n\n\t//! set if the projection should handle special cases\n\tvoid HandleSpecialCases(bool b) { m_bspecial = b; }\n\n\t//! set if boundary projections are allowed\n\tvoid AllowBoundaryProjections(bool b) { m_projectBoundary = b; }\n\nprivate:\n\tbool ContainsElement(FESurfaceElement* el);\n\tFESurfaceElement* ProjectSpecial(int closestPoint, const vec3d& x, vec3d& q, vec2d& r);\n\nprotected:\n\tdouble\tm_tol;\t//!< projection tolerance\n\tdouble\tm_rad;\t//!< search radius\n\tbool\tm_bspecial;\t//!< try to handle special cases\n\tbool\tm_projectBoundary;\t//!< allow boundary projections\n\nprotected:\n\tFESurface&\t\tm_surf;\t\t//!< reference to surface\n\tFENNQuery\t\tm_SNQ;\t\t//!< used to find the nearest neighbour\n\tFENodeElemList\tm_NEL;\t\t//!< node-element tree\n\tFEElemElemList\tm_EEL;\t\t//!< element neighbor list\n};\n"
  },
  {
    "path": "FECore/FEConstValueVec3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEModel.h\"\n#include \"FEConstValueVec3.h\"\n#include \"FEMaterialPoint.h\"\n#include \"FEMeshPartition.h\"\n#include \"FENode.h\"\n#include \"quatd.h\"\n#include <assert.h>\n\n//==================================================================================\nBEGIN_FECORE_CLASS(FEConstValueVec3, FEVec3dValuator)\n\tADD_PARAMETER(m_val, \"vector\");\nEND_FECORE_CLASS();\n\nFEConstValueVec3::FEConstValueVec3(FEModel* fem) : FEVec3dValuator(fem) {}\n\nFEVec3dValuator* FEConstValueVec3::copy()\n{\n\tFEConstValueVec3* val = fecore_alloc(FEConstValueVec3, GetFEModel());\n\tval->m_val = m_val;\n\treturn val;\n}\n\n//==================================================================================\nBEGIN_FECORE_CLASS(FEMathValueVec3, FEVec3dValuator)\n\tADD_PARAMETER(m_expr, \"math\");\nEND_FECORE_CLASS();\n\nFEMathValueVec3::FEMathValueVec3(FEModel* fem) : FEVec3dValuator(fem)\n{\n\tm_expr = \"0,0,0\";\n\tInit();\n}\n\n//---------------------------------------------------------------------------------------\nbool FEMathValueVec3::Init()\n{\n\tif (m_expr.empty()) return false;\n\tstring s[3];\n\n\tconst char* c = m_expr.c_str();\n\tint n = 0;\n\tint l = 0;\n\twhile (*c)\n\t{\n\t\tchar ch = *c;\n\t\tif ((ch == ',') && (l == 0))\n\t\t{\n\t\t\tn++;\n\t\t\tif (n > 2) return false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((ch == '(') || (ch == '[') || (ch == '{')) l++;\n\t\t\tif ((ch == ')') || (ch == ']') || (ch == '}')) l--;\n\t\t\ts[n].push_back(ch);\n\t\t}\n\t\tc++;\n\t}\n\tif (n != 2) return false;\n\n\treturn create(s[0], s[1], s[2]);\n}\n\n//---------------------------------------------------------------------------------------\nbool FEMathValueVec3::create(const std::string& sx, const std::string& sy, const std::string& sz)\n{\n\tFECoreBase* pc = nullptr;\n\tif (pc == nullptr)\n\t{\n\t\t// try to find the owner of this parameter\n\t\t// First, we need the model parameter\n\t\tFEModelParam* param = GetModelParam();\n\t\tif (param == nullptr) return false;\n\n\t\t// we'll need the model for this\n\t\tFEModel* fem = GetFEModel();\n\t\tif (fem == nullptr) return false;\n\n\t\t// Now try to find the owner of this parameter\n\t\tpc = fem->FindParameterOwner(param);\n\t}\n\n\tif (m_math[0].Init(sx, pc) == false) return false;\n\tif (m_math[1].Init(sy, pc) == false) return false;\n\tif (m_math[2].Init(sz, pc) == false) return false;\n\n\treturn true;\n}\n\nbool FEMathValueVec3::UpdateParams()\n{\n\treturn Init();\n}\n\nvec3d FEMathValueVec3::operator()(const FEMaterialPoint& pt)\n{\n\tdouble vx = m_math[0].value(GetFEModel(), pt);\n\tdouble vy = m_math[1].value(GetFEModel(), pt);\n\tdouble vz = m_math[2].value(GetFEModel(), pt);\n\treturn vec3d(vx, vy, vz);\n}\n\nvoid FEMathValueVec3::Serialize(DumpStream& ar)\n{\n\tFEVec3dValuator::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsLoading())\n\t{\n\t\tInit();\n\t}\n}\n\n//---------------------------------------------------------------------------------------\nFEVec3dValuator* FEMathValueVec3::copy()\n{\n\tFEMathValueVec3* newVal = fecore_alloc(FEMathValueVec3, GetFEModel());\n\tnewVal->m_math[0] = m_math[0];\n\tnewVal->m_math[1] = m_math[1];\n\tnewVal->m_math[2] = m_math[2];\n\treturn newVal;\n}\n\n//---------------------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEMappedValueVec3, FEVec3dValuator)\n\tADD_PARAMETER(m_mapName, \"map\");\nEND_FECORE_CLASS();\n\nFEMappedValueVec3::FEMappedValueVec3(FEModel* fem) : FEVec3dValuator(fem)\n{\n\tm_val = nullptr;\n}\n\nvoid FEMappedValueVec3::setDataMap(FEDataMap* val, vec3d scl)\n{\n\tm_val = val;\n}\n\nvec3d FEMappedValueVec3::operator()(const FEMaterialPoint& pt)\n{\n\tvec3d r = m_val->valueVec3d(pt);\n\treturn vec3d(r.x, r.y, r.z);\n}\n\nFEVec3dValuator* FEMappedValueVec3::copy()\n{\n\tFEMappedValueVec3* map = fecore_alloc(FEMappedValueVec3, GetFEModel());\n\tmap->m_val = m_val;\n\treturn map;\n}\n\nvoid FEMappedValueVec3::Serialize(DumpStream& ar)\n{\n\tFEVec3dValuator::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_val;\n}\n\nbool FEMappedValueVec3::Init()\n{\n\tif (m_val == nullptr)\n\t{\n\t\tFEModel& fem = *GetFEModel();\n\t\tFEMesh& mesh = fem.GetMesh();\n\t\tFEDataMap* map = mesh.FindDataMap(m_mapName);\n\t\tif (map == nullptr) return false;\n\t\tsetDataMap(map);\n\t}\n\treturn FEVec3dValuator::Init();\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FELocalVectorGenerator, FEVec3dValuator)\n\tADD_PARAMETER(m_n, 2, \"local\");\nEND_FECORE_CLASS();\n\nFELocalVectorGenerator::FELocalVectorGenerator(FEModel* fem) : FEVec3dValuator(fem)\n{\n\tm_n[0] = m_n[1] = 0;\n}\n\nbool FELocalVectorGenerator::Init()\n{\n\tif ((m_n[0] <= 0) && (m_n[1] <= 0))\n\t{\n\t\tm_n[0] = 1;\n\t\tm_n[1] = 2;\n\t}\n\tif ((m_n[0] <= 0) || (m_n[1] <= 0)) return false;\n\treturn FEVec3dValuator::Init();\n}\n\nvec3d FELocalVectorGenerator::operator () (const FEMaterialPoint& mp)\n{\n\tFEElement* el = mp.m_elem; assert(el);\n\n\tFEMeshPartition* dom = el->GetMeshPartition();\n\tvec3d r0 = dom->Node(el->m_lnode[m_n[0]-1]).m_r0;\n\tvec3d r1 = dom->Node(el->m_lnode[m_n[1]-1]).m_r0;\n\n\tvec3d n = r1 - r0;\n\tn.unit();\n\n\treturn n;\n}\n\nFEVec3dValuator* FELocalVectorGenerator::copy()\n{\n\tFELocalVectorGenerator* map = fecore_alloc(FELocalVectorGenerator, GetFEModel());\n\tmap->m_n[0] = m_n[0];\n\tmap->m_n[1] = m_n[1];\n\treturn map;\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FESphericalVectorGenerator, FEVec3dValuator)\n\tADD_PARAMETER(m_center, \"center\");\n\tADD_PARAMETER(m_vector, \"vector\");\nEND_FECORE_CLASS();\n\nFESphericalVectorGenerator::FESphericalVectorGenerator(FEModel* fem) : FEVec3dValuator(fem)\n{\n\tm_center = vec3d(0, 0, 0);\n\tm_vector = vec3d(1, 0, 0);\n}\n\nbool FESphericalVectorGenerator::Init()\n{\n\t// Make sure the vector is a unit vector\n\tm_vector.unit();\n\treturn true;\n}\n\nFEVec3dValuator* FESphericalVectorGenerator::copy()\n{\n\tFESphericalVectorGenerator* map = fecore_alloc(FESphericalVectorGenerator, GetFEModel());\n\tmap->m_center = m_center;\n\tmap->m_vector = m_vector;\n\treturn map;\n}\n\nvec3d FESphericalVectorGenerator::operator () (const FEMaterialPoint& mp)\n{\n\tvec3d a = mp.m_r0 - m_center;\n\ta.unit();\n\n\t// setup the rotation\n\tvec3d e1(1, 0, 0);\n\tquatd q(e1, a);\n\n\tvec3d v = m_vector;\n\t//\tv.unit();\t\n\tq.RotateVector(v);\n\n\treturn v;\n}\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FECylindricalVectorGenerator, FEVec3dValuator)\n\tADD_PARAMETER(m_center, \"center\");\n\tADD_PARAMETER(m_axis, \"axis\");\n\tADD_PARAMETER(m_vector, \"vector\");\nEND_FECORE_CLASS();\n\nFECylindricalVectorGenerator::FECylindricalVectorGenerator(FEModel* fem) : FEVec3dValuator(fem)\n{\n\tm_center = vec3d(0, 0, 0);\n\tm_axis = vec3d(0, 0, 1);\n\tm_vector = vec3d(1, 0, 0);\n}\n\nbool FECylindricalVectorGenerator::Init()\n{\n\t// Make sure the axis and vector are unit vectors\n\tm_axis.unit();\n\tm_vector.unit();\n\treturn true;\n}\n\nvec3d FECylindricalVectorGenerator::operator () (const FEMaterialPoint& mp)\n{\n\tvec3d p = mp.m_r0 - m_center;\n\n\t// find the vector to the axis\n\tvec3d a = m_axis; a.unit();\n\tvec3d b = p - a * (a*p);\n\tb.unit();\n\n\t// setup the rotation\n\tvec3d e1(1, 0, 0);\n\tquatd qz(vec3d(0, 0, 1), a);\n\tqz.RotateVector(e1);\n\tquatd q(e1, b);\n\n\tvec3d r = m_vector;\n\tr.unit();\t\n\tq.RotateVector(r);\n\n\treturn r;\n}\n\nFEVec3dValuator* FECylindricalVectorGenerator::copy()\n{\n\tFECylindricalVectorGenerator* map = fecore_alloc(FECylindricalVectorGenerator, GetFEModel());\n\tmap->m_center = m_center;\n\tmap->m_axis = m_axis;\n\tmap->m_vector = m_vector;\n\treturn map;\n}\n\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FESphericalAnglesVectorGenerator, FEVec3dValuator)\n\tADD_PARAMETER(m_theta, \"theta\");\n\tADD_PARAMETER(m_phi, \"phi\");\nEND_FECORE_CLASS();\n\nFESphericalAnglesVectorGenerator::FESphericalAnglesVectorGenerator(FEModel* fem) : FEVec3dValuator(fem)\n{\n\t// equal to x-axis (1,0,0)\n\tm_theta = 0.0;\n\tm_phi = 90.0;\n}\n\nvec3d FESphericalAnglesVectorGenerator::operator () (const FEMaterialPoint& mp)\n{\n\t// convert from degress to radians\n\tconst double the = m_theta(mp)* PI / 180.;\n\tconst double phi = m_phi(mp)* PI / 180.;\n\n\t// the fiber vector\n\tvec3d a;\n\ta.x = cos(the)*sin(phi);\n\ta.y = sin(the)*sin(phi);\n\ta.z = cos(phi);\n\n\treturn a;\n}\n\nFEVec3dValuator* FESphericalAnglesVectorGenerator::copy()\n{\n\tFESphericalAnglesVectorGenerator* v = fecore_alloc(FESphericalAnglesVectorGenerator, GetFEModel());\n\tv->m_theta = m_theta;\n\tv->m_phi = m_phi;\n\treturn v;\n}\n\n\n//=================================================================================================\nBEGIN_FECORE_CLASS(FEUserVectorGenerator, FEVec3dValuator)\nEND_FECORE_CLASS();\n\nFEUserVectorGenerator::FEUserVectorGenerator(FEModel* fem) : FEVec3dValuator(fem)\n{\n}\n\nvec3d FEUserVectorGenerator::operator () (const FEMaterialPoint& mp)\n{\n\tassert(false);\n\treturn vec3d(0, 0, 0);\n}\n\nFEVec3dValuator* FEUserVectorGenerator::copy()\n{\n\tassert(false);\n\treturn fecore_alloc(FEUserVectorGenerator, GetFEModel());\n}\n"
  },
  {
    "path": "FECore/FEConstValueVec3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include \"FEVec3dValuator.h\"\n#include \"FEModelParam.h\"\n\n//---------------------------------------------------------------------------------------\n// A constant valuator\nclass FECORE_API FEConstValueVec3 : public FEVec3dValuator\n{\npublic:\n\tFEConstValueVec3(FEModel* fem);\n\n\tFEVec3dValuator* copy() override;\n\n\tvec3d operator()(const FEMaterialPoint& pt) override { return m_val; }\n\n\t// is this a const value\n\tbool isConst() override { return true; }\n\n\t// get the const value (returns 0 if param is not const)\n\tvec3d* constValue() override { return &m_val; }\n\n\tvec3d& value() { return m_val; }\n\n\tvoid setConstValue(const vec3d& v) { m_val = v; }\n\nprivate:\n\tvec3d\tm_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------------------\n// The value is calculated using a mathematical expression\nclass FECORE_API FEMathValueVec3 : public FEVec3dValuator\n{\npublic:\n\tFEMathValueVec3(FEModel* fem);\n\tvec3d operator()(const FEMaterialPoint& pt) override;\n\n\tbool Init() override;\n\n\tbool create(const std::string& sx, const std::string& sy, const std::string& sz);\n\n\tFEVec3dValuator* copy() override;\n\n\tbool UpdateParams() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tstd::string\t\t\tm_expr;\n\tFEMathExpression\tm_math[3];\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------------------\n// The value is determined by a data map\nclass FECORE_API FEMappedValueVec3 : public FEVec3dValuator\n{\npublic:\n\tFEMappedValueVec3(FEModel* fem);\n\n\tvoid setDataMap(FEDataMap* val, vec3d scl = vec3d(1, 1, 1));\n\n\tvec3d operator()(const FEMaterialPoint& pt) override;\n\n\tFEVec3dValuator* copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tbool Init() override;\n\nprivate:\n\tstd::string\t\tm_mapName;\n\nprivate:\n\tFEDataMap*\t\tm_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n// This class calculates a vector based on local element node numbering\nclass FECORE_API FELocalVectorGenerator : public FEVec3dValuator\n{\npublic:\n\tFELocalVectorGenerator(FEModel* fem);\n\n\tbool Init() override;\n\n\tvec3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEVec3dValuator* copy() override;\n\nprotected:\n\tint\tm_n[2];\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FECylindricalVectorGenerator : public FEVec3dValuator\n{\npublic:\n\tFECylindricalVectorGenerator(FEModel* fem);\n\n\tbool Init() override;\n\n\tvec3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEVec3dValuator* copy() override;\n\nprotected:\n\tvec3d\tm_center;\t\t// center of map\n\tvec3d\tm_axis;\t\t\t// cylinder axis\n\tvec3d\tm_vector;\t\t// reference direction\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FESphericalVectorGenerator : public FEVec3dValuator\n{\npublic:\n\tFESphericalVectorGenerator(FEModel* fem);\n\n\tbool Init() override;\n\n\tvec3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEVec3dValuator* copy() override;\n\nprotected:\n\tvec3d\tm_center;\n\tvec3d\tm_vector;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FESphericalAnglesVectorGenerator : public FEVec3dValuator\n{\npublic:\n\tFESphericalAnglesVectorGenerator(FEModel* fem);\n\n\tvec3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEVec3dValuator* copy() override;\n\nprotected:\n\tFEParamDouble\tm_theta;\t// in-plane (x,y) angle from x-axis\n\tFEParamDouble\tm_phi;\t\t// angle from z-axis\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// This class is mostly to support some older formats that used the \"user\" fiber\n// generator option. It actually doesn't generate any vectors and should not be used. \nclass FECORE_API FEUserVectorGenerator : public FEVec3dValuator\n{\npublic:\n\tFEUserVectorGenerator(FEModel* fem);\n\n\tvec3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEVec3dValuator* copy() override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FECore.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECore.h\"\n#include \"FECoreKernel.h\"\n#include \"FEPrescribedDOF.h\"\n#include \"FENodalLoad.h\"\n#include \"FEFixedBC.h\"\n#include \"FELinearConstraint.h\"\n#include \"FEInitialCondition.h\"\n#include \"FECorePlot.h\"\n#include \"FESurfaceToSurfaceMap.h\"\n#include \"FESurfaceToSurfaceVectorMap.h\"\n#include \"FEParabolicMap.h\"\n#include \"FEDataMathGenerator.h\"\n#include \"FEPointFunction.h\"\n#include \"FELoadCurve.h\"\n#include \"FEMathController.h\"\n#include \"FEMathIntervalController.h\"\n#include \"FEPIDController.h\"\n#include \"FEScriptedLoadController.h\"\n#include \"Preconditioner.h\"\n#include \"FEMat3dValuator.h\"\n#include \"FEMat3dSphericalAngleMap.h\"\n#include \"FEAnalysis.h\"\n#include \"BFGSSolver.h\"\n#include \"FEBroydenStrategy.h\"\n#include \"JFNKStrategy.h\"\n#include \"FENodeSet.h\"\n#include \"FEFacetSet.h\"\n#include \"FEElementSet.h\"\n#include \"FEConstValueVec3.h\"\n#include \"NodeDataRecord.h\"\n#include \"FaceDataRecord.h\"\n#include \"ElementDataRecord.h\"\n#include \"NLConstraintDataRecord.h\"\n#include \"FEAugLagLinearConstraint.h\"\n#include \"SurfaceDataRecord.h\"\n#include \"FELogEnclosedVolume.h\"\n#include \"FELogElementVolume.h\"\n#include \"FELogDomainVolume.h\"\n#include \"FELogSolutionNorm.h\"\n#include \"FELogElemMath.h\"\n#include \"LUSolver.h\"\n#include \"FETimeStepController.h\"\n#include \"FEModifiedNewtonStrategy.h\"\n#include \"FEFullNewtonStrategy.h\"\n#include \"SkylineSolver.h\"\n#include \"FEScriptedBehavior.h\"\n\n#define FECORE_VERSION\t\t0\n#define FECORE_SUBVERSION\t1\n\n//-----------------------------------------------------------------------------\n// Get the version info\nvoid FECore::get_version(int& version, int& subversion)\n{\n\tversion = FECORE_VERSION;\n\tsubversion = FECORE_SUBVERSION;\n}\n\n//-----------------------------------------------------------------------------\n// get the version string\nconst char* FECore::get_version_string()\n{\n\tstatic const char fecore_str[4] = {'0'+FECORE_VERSION, '.', '0'+FECORE_SUBVERSION };\n\treturn fecore_str;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECore::InitModule()\n{\n\t// initialize the element librar\n\tFEElementLibrary::Initialize();\n\n// analysis class\n//REGISTER_FECORE_CLASS(FEAnalysis, \"analysis\");\n\n// time controller\nREGISTER_FECORE_CLASS(FETimeStepController, \"default\");\n\n// boundary conditions\nREGISTER_FECORE_CLASS(FEFixedDOF     , \"fix\"      , FECORE_DEPRECATED);\t// obsolete in 4.0\nREGISTER_FECORE_CLASS(FEPrescribedDOF, \"prescribe\", FECORE_DEPRECATED);\t// obsolete in 4.0\nREGISTER_FECORE_CLASS(FELinearConstraint, \"linear constraint\");\nREGISTER_FECORE_CLASS(FELinearConstraintDOF, \"child_dof\");\n\n// nodal loads\nREGISTER_FECORE_CLASS(FENodalDOFLoad, \"nodal_load\");\n\n// initial conditions\nREGISTER_FECORE_CLASS(FEInitialDOF     , \"init_dof\"     , FECORE_DEPRECATED);\t// obsolete in 4.0\n\n// (augmented lagrangian) linear constraints\nREGISTER_FECORE_CLASS(FELinearConstraintSet, \"linear constraint\");\nREGISTER_FECORE_CLASS(FEAugLagLinearConstraint, \"linear_constraint\");\nREGISTER_FECORE_CLASS(FEAugLagLinearConstraintDOF, \"node\");\n\n// plot field\nREGISTER_FECORE_CLASS(FEPlotParameter, \"parameter\");\nREGISTER_FECORE_CLASS(FEPlotPIDController, \"pid controller\");\nREGISTER_FECORE_CLASS(FEPlotMeshData, \"mesh_data\");\nREGISTER_FECORE_CLASS(FEPlotFieldVariable, \"field\");\n\n// 1D functions\nREGISTER_FECORE_CLASS(FEPointFunction , \"point\");\nREGISTER_FECORE_CLASS(FEConstFunction, \"const\");\nREGISTER_FECORE_CLASS(FEScaleFunction, \"scale\");\nREGISTER_FECORE_CLASS(FELinearFunction, \"linear ramp\");\nREGISTER_FECORE_CLASS(FEStepFunction  , \"step\");\nREGISTER_FECORE_CLASS(FEMathFunction  , \"math\");\n\n// data generators\nREGISTER_FECORE_CLASS(FEDataMathGenerator  , \"math\");\nREGISTER_FECORE_CLASS(FESurfaceToSurfaceMap, \"surface-to-surface map\");\nREGISTER_FECORE_CLASS(FESurfaceToSurfaceVectorMap, \"surface-to-surface vector\");\nREGISTER_FECORE_CLASS(FEParabolicMap       , \"parabolic map\");\n\n// scalar valuators\nREGISTER_FECORE_CLASS(FEConstValue  , \"const\");\nREGISTER_FECORE_CLASS(FEMathValue   , \"math\" );\nREGISTER_FECORE_CLASS(FEMappedValue , \"map\"  );\n\n//  vector generators\nREGISTER_FECORE_CLASS(FELocalVectorGenerator          , \"local\");\nREGISTER_FECORE_CLASS(FEConstValueVec3                , \"vector\");\nREGISTER_FECORE_CLASS(FEMathValueVec3                 , \"math\");\nREGISTER_FECORE_CLASS(FESphericalVectorGenerator      , \"spherical\");\nREGISTER_FECORE_CLASS(FECylindricalVectorGenerator    , \"cylindrical\");\nREGISTER_FECORE_CLASS(FESphericalAnglesVectorGenerator, \"angles\");\nREGISTER_FECORE_CLASS(FEMappedValueVec3               , \"map\");\nREGISTER_FECORE_CLASS(FEUserVectorGenerator           , \"user\");\n\n// mat3d generators\nREGISTER_FECORE_CLASS(FEConstValueMat3d       , \"const\"      );\nREGISTER_FECORE_CLASS(FEMat3dLocalElementMap  , \"local\"      );\nREGISTER_FECORE_CLASS(FEMat3dSphericalMap     , \"spherical\"  );\nREGISTER_FECORE_CLASS(FEMat3dCylindricalMap   , \"cylindrical\");\nREGISTER_FECORE_CLASS(FEMat3dVectorMap        , \"vector\"     );\nREGISTER_FECORE_CLASS(FEMat3dSphericalAngleMap, \"angles\"     );\nREGISTER_FECORE_CLASS(FEMat3dPolarMap         , \"polar\"      );\nREGISTER_FECORE_CLASS(FEMappedValueMat3d      , \"map\"        );\n\n// mat3ds generators\nREGISTER_FECORE_CLASS(FEConstValueMat3ds , \"const\");\nREGISTER_FECORE_CLASS(FEMappedValueMat3ds, \"map\");\n\n// load controllers\nREGISTER_FECORE_CLASS(FELoadCurve             , \"loadcurve\");\nREGISTER_FECORE_CLASS(FEMathController        , \"math\");\nREGISTER_FECORE_CLASS(FEMathIntervalController, \"math-interval\");\nREGISTER_FECORE_CLASS(FEPIDController         , \"PID\");\nREGISTER_FECORE_CLASS(FEScriptedLoadController, \"load controller script\", FECORE_EXPERIMENTAL);\n\n// Newton strategies\nREGISTER_FECORE_CLASS(BFGSSolver       , \"BFGS\");\nREGISTER_FECORE_CLASS(FEBroydenStrategy, \"Broyden\");\nREGISTER_FECORE_CLASS(JFNKStrategy     , \"JFNK\");\nREGISTER_FECORE_CLASS(FEModifiedNewtonStrategy, \"modified Newton\");\nREGISTER_FECORE_CLASS(FEFullNewtonStrategy    , \"full Newton\");\n\n// preconditioners\nREGISTER_FECORE_CLASS(DiagonalPreconditioner, \"diagonal\");\n\nREGISTER_FECORE_CLASS(FESurface, \"surface\");\n\n// data records\nREGISTER_FECORE_CLASS(NodeDataRecord, \"node_data\");\nREGISTER_FECORE_CLASS(FaceDataRecord, \"face_data\");\nREGISTER_FECORE_CLASS(ElementDataRecord, \"element_data\");\nREGISTER_FECORE_CLASS(NLConstraintDataRecord, \"rigid_connector_data\");\n\n// log classes\nREGISTER_FECORE_CLASS(FELogEnclosedVolume, \"volume\");\nREGISTER_FECORE_CLASS(FELogEnclosedVolumeChange, \"volume change\");\nREGISTER_FECORE_CLASS(FELogElementVolume, \"V\");\nREGISTER_FECORE_CLASS(FELogDomainVolume, \"volume\");\nREGISTER_FECORE_CLASS(FELogAvgDomainData, \"avg\");\nREGISTER_FECORE_CLASS(FELogPctDomainData, \"pct\");\nREGISTER_FECORE_CLASS(FELogIntegralDomainData, \"integrate\");\nREGISTER_FECORE_CLASS(FELogSolutionNorm, \"solution_norm\");\nREGISTER_FECORE_CLASS(FELogFaceArea    , \"facet area\");\nREGISTER_FECORE_CLASS(FELogElemMath    , \"_math\", FECORE_EXPERIMENTAL);\n\n// linear solvers\nREGISTER_FECORE_CLASS(LUSolver, \"LU\");\nREGISTER_FECORE_CLASS(SkylineSolver, \"skyline\");\n\n// scripts\nREGISTER_FECORE_CLASS(FEScriptedBehavior, \"script\", FECORE_EXPERIMENTAL);\n\n}\n"
  },
  {
    "path": "FECore/FECore.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\n//! The FECore namespace encapsulates all classes that belong to the FECore library\nnamespace FECore\n{\n\t// retrieve version numbers\n\tFECORE_API void get_version(int& version, int& subversion);\n\n\t// retrieve version number string\n\tFECORE_API const char* get_version_string();\n\n\t// initialize the module\n\tFECORE_API void InitModule();\n\n} // namespace FECore\n"
  },
  {
    "path": "FECore/FECoreBase.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoreBase.h\"\n#include \"DumpStream.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModelParam.h\"\n#include \"log.h\"\n#include <sstream>\n\n//-----------------------------------------------------------------------------\n//! The constructor takes one argument, namely the SUPER_CLASS_ID which\n//! defines the type of class this is. (The SUPER_CLASS_ID was introduced to\n//! eliminate a lot of akward dynamic_casts.)\nFECoreBase::FECoreBase(FEModel* fem) : m_fem(fem)\n{ \n\tm_nID = -1;\n\tm_pParent = 0;\n\tm_fac = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! destructor does nothing for now.\nFECoreBase::~FECoreBase()\n{\n\tClearProperties();\n}\n\n//-----------------------------------------------------------------------------\n//! return the super class id\nSUPER_CLASS_ID FECoreBase::GetSuperClassID() { return (m_fac ? m_fac->GetSuperClassID() : FEINVALID_ID); }\n\n//-----------------------------------------------------------------------------\n//! return a (unique) string describing the type of this class\n//! This string is used in object creation\nconst char* FECoreBase::GetTypeStr() { return (m_fac ? m_fac->GetTypeStr() : nullptr); }\n\n//-----------------------------------------------------------------------------\n//! Set the factory class\nvoid FECoreBase::SetFactoryClass(const FECoreFactory* fac)\n{\n\tm_fac = fac;\n}\n\n//-----------------------------------------------------------------------------\nconst FECoreFactory* FECoreBase::GetFactoryClass() const\n{\n\treturn m_fac;\n}\n\n//-----------------------------------------------------------------------------\n//! Sets the user defined name of the component\nvoid FECoreBase::SetName(const std::string& name)\n{ \n\tm_name = name;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the name\nconst std::string& FECoreBase::GetName() const\n{ \n\treturn m_name; \n}\n\n//-----------------------------------------------------------------------------\n//! Get the parent of this object (zero if none)\nFECoreBase* FECoreBase::GetParent()\n{ \n\treturn m_pParent; \n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FECoreBase::GetAncestor()\n{\n\tFECoreBase* mp = GetParent(); \n\treturn (mp ? mp->GetAncestor() : this); \n}\n\n//-----------------------------------------------------------------------------\n//! Set the parent of this class\nvoid FECoreBase::SetParent(FECoreBase* parent) { m_pParent = parent; }\n\n//-----------------------------------------------------------------------------\n//! return the component ID\nint FECoreBase::GetID() const { return m_nID; }\n\n//-----------------------------------------------------------------------------\n//! set the component ID\nvoid FECoreBase::SetID(int nid) { m_nID = nid; }\n\n//-----------------------------------------------------------------------------\n//! Get the FE model\nFEModel* FECoreBase::GetFEModel() const { return m_fem; }\n\n//-----------------------------------------------------------------------------\nvoid FECoreBase::SetFEModel(FEModel* fem) { m_fem = fem; }\n\n//-----------------------------------------------------------------------------\nvoid setParamValue(FEParam& pi, const std::string& val)\n{\n\tif (val.empty()) return;\n\tconst char* sz = val.c_str();\n\tswitch (pi.type())\n\t{\n\tcase FE_PARAM_INT: pi.value<int>() = atoi(sz); break;\n\tcase FE_PARAM_BOOL: pi.value<bool>() = (atoi(sz) == 0 ? false : true); break;\n\tcase FE_PARAM_DOUBLE: pi.value<double>() = atof(sz); break;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// set parameters through a class descriptor\nbool FECoreBase::SetParameters(const FEClassDescriptor& cd)\n{\n\tconst FEClassDescriptor::ClassVariable* root = cd.Root();\n\treturn SetParameters(*cd.Root());\n}\n\n//-----------------------------------------------------------------------------\n// set parameters through a class descriptor\nbool FECoreBase::SetParameters(const FEClassDescriptor::ClassVariable& cv)\n{\n\tFEParameterList& PL = GetParameterList();\n\tfor (int i=0; i<cv.Count(); ++i)\n\t{\n\t\t// get the next variable\n\t\tconst FEClassDescriptor::Variable* vari = cv.GetVariable(i);\n\n\t\t// see if this parameter is defined\n\t\tFEParam* pi = PL.FindFromName(vari->m_name.c_str());\n\t\tif (pi)\n\t\t{\n\t\t\tconst FEClassDescriptor::SimpleVariable* vi = dynamic_cast<const FEClassDescriptor::SimpleVariable*>(vari);\n\t\t\tassert(vi);\n\t\t\tif (vi == nullptr) return false;\n\n\t\t\t// set the value\n\t\t\tsetParamValue(*pi, vi->m_val);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// could be a property\n\t\t\tconst FEClassDescriptor::ClassVariable* ci = dynamic_cast<const FEClassDescriptor::ClassVariable*>(vari);\n\t\t\tassert(ci);\n\n\t\t\t// find the property\n\t\t\tFEProperty* prop = FindProperty(ci->m_name.c_str()); assert(prop);\n\t\t\tif (prop == nullptr) return false;\n\n\t\t\t// allocate a new child class\n\t\t\tFECoreBase* pc = fecore_new<FECoreBase>(prop->GetSuperClassID(), ci->m_type.c_str(), GetFEModel()); assert(pc);\n\t\t\tif (pc == nullptr) return false;\n\n\t\t\t// assign the property\n\t\t\tprop->SetProperty(pc);\n\n\t\t\t// set the property's parameters\n\t\t\tpc->SetParameters(*ci);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreBase::Serialize(DumpStream& ar)\n{\n\t// do base class first\n\tFEParamContainer::Serialize(ar);\n\n\t// serialize name\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_name;\n\t\tar & m_nID;\n\t}\n\n\tif (ar.IsShallow() == false) ar & m_pParent;\n\n\t// serialize all the properties\n\tint NP = (int)m_Prop.size();\n\tfor (int i = 0; i<NP; ++i)\n\t{\n\t\tFEProperty* prop = m_Prop[i];\n\t\tprop->SetParent(this);\n\t\tprop->Serialize(ar);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreBase::SaveClass(DumpStream& ar, FECoreBase* a)\n{\n\tassert(ar.IsSaving());\n\tint classID = 0;\n\tif (a == nullptr) { ar << classID; return; }\n\tclassID = a->GetSuperClassID();\n\tassert(classID != FEINVALID_ID);\n\tconst char* sztype = a->GetTypeStr();\n\tar << classID;\n\tar << sztype;\n}\n\nFECoreBase* FECoreBase::LoadClass(DumpStream& ar, FECoreBase* a)\n{\n\tassert(ar.IsLoading());\n\n\tint classID = 0;\n\tar >> classID;\n\tif (classID == FEINVALID_ID) return nullptr;\n\n\tchar sztype[256] = { 0 };\n\tar >> sztype;\n\n\t// instantiate the class\n\ta = fecore_new<FECoreBase>(classID, sztype, &ar.GetFEModel());\n\tassert(a);\n\n\tif (a == nullptr) throw DumpStream::ReadError();\n\n\treturn a;\n}\n\nbool FECoreBase::ValidateParameters()\n{\n\tFEParameterList& pl = GetParameterList();\n\tint N = pl.Parameters();\n\tlist<FEParam>::iterator pi = pl.first();\n\tfor (int i = 0; i < N; ++i, pi++)\n\t{\n\t\tFEParam& p = *pi;\n\t\tif (p.is_valid() == false)\n\t\t{\n\t\t\tstringstream ss;\n\t\t\tss << GetName() << \".\" << p.name();\n\t\t\tstring paramName = ss.str();\n\t\t\tfeLogError(\"Invalid value for parameter: %s\", paramName.c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nbool FECoreBase::Validate()\n{\n\t// validate parameters\n\tif (!ValidateParameters()) return false;\n\n\t// check properties\n\tconst int nprop = (int)m_Prop.size();\n\tfor (int i = 0; i<nprop; ++i)\n\t{\n\t\tFEProperty* pi = m_Prop[i];\n\t\tif (pi)\n\t\t{\n\t\t\tif (pi->Validate() == false) return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreBase::Init()\n{\n\t// call init on model parameters\n\tFEParameterList& PL = GetParameterList();\n\tFEParamIterator it = PL.first();\n\tfor (int i = 0; i < PL.Parameters(); ++i, ++it)\n\t{\n\t\tFEParam& pi = *it;\n\t\tif (pi.type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t{\n\t\t\tfor (int j = 0; j < pi.dim(); ++j)\n\t\t\t{\n\t\t\t\tFEParamDouble& pd = pi.value<FEParamDouble>(j);\n\t\t\t\tif (pd.Init() == false)\n\t\t\t\t{\n\t\t\t\t\tfeLogError(\"Failed to initialize parameter %s\", pi.name());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (pi.type() == FE_PARAM_VEC3D_MAPPED)\n\t\t{\n\t\t\tfor (int j = 0; j < pi.dim(); ++j)\n\t\t\t{\n\t\t\t\tFEParamVec3& pd = pi.value<FEParamVec3>(j);\n\t\t\t\tif (pd.Init() == false)\n\t\t\t\t{\n\t\t\t\t\tfeLogError(\"Failed to initialize parameter %s\", pi.name());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (pi.type() == FE_PARAM_MAT3D_MAPPED)\n\t\t{\n\t\t\tfor (int j = 0; j < pi.dim(); ++j)\n\t\t\t{\n\t\t\t\tFEParamMat3d& pd = pi.value<FEParamMat3d>(j);\n\t\t\t\tif (pd.Init() == false)\n\t\t\t\t{\n\t\t\t\t\tfeLogError(\"Failed to initialize parameter %s\", pi.name());\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// check the parameter ranges\n\tif (ValidateParameters() == false) return false;\n\n\t// initialize properties\n\tconst int nprop = (int)m_Prop.size();\n\tfor (int i = 0; i<nprop; ++i)\n\t{\n\t\tFEProperty* pi = m_Prop[i];\n\t\tif (pi)\n\t\t{\n\t\t\tif (pi->Init() == false)\n\t\t\t{\n\t\t\t\tfeLogError(\"The property \\\"%s\\\" failed to initialize or was not defined.\", pi->GetName());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfeLogError(\"A nullptr was set for property i\");\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreBase::AddProperty(FEProperty* pp, const char* sz, unsigned int flags)\n{\n\tpp->SetName(sz);\n\tpp->SetLongName(sz);\n\tpp->SetFlags(flags);\n\tpp->SetParent(this);\n\tm_Prop.push_back(pp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreBase::RemoveProperty(int i)\n{\n\tm_Prop[i] = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreBase::ClearProperties()\n{\n\tfor (int i = 0; i < m_Prop.size(); ++i)\n\t{\n\t\tdelete m_Prop[i];\n\t}\n\tm_Prop.clear();\n}\n\n//-----------------------------------------------------------------------------\nint FECoreBase::Properties()\n{\n\tint N = (int)m_Prop.size();\n\tint n = 0;\n\tfor (int i = 0; i<N; ++i) n += m_Prop[i]->size();\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\nint FECoreBase::FindPropertyIndex(const char* sz)\n{\n\tint NP = (int)m_Prop.size();\n\tfor (int i = 0; i<NP; ++i)\n\t{\n\t\tconst FEProperty* pm = m_Prop[i];\n\t\tif (pm && (strcmp(pm->GetName(), sz) == 0)) return i;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nFEProperty* FECoreBase::FindProperty(const char* sz, bool searchChildren)\n{\n\t// first, search the class' properties\n\tint NP = (int)m_Prop.size();\n\tfor (int i = 0; i<NP; ++i)\n\t{\n\t\tFEProperty* pm = m_Prop[i];\n\t\tif (pm && (strcmp(pm->GetName(), sz) == 0)) return pm;\n\t}\n\n\t// the property, wasn't found so look into the properties' properties\n\tif (searchChildren)\n\t{\n\t\tfor (int i = 0; i < NP; ++i)\n\t\t{\n\t\t\tFEProperty* pm = m_Prop[i];\n\t\t\tif (pm)\n\t\t\t{\n\t\t\t\tint m = pm->size();\n\t\t\t\tfor (int j = 0; j < m; ++j)\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pcj = pm->get(j);\n\t\t\t\t\tif (pcj)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Note: we don't search children's children!\n\t\t\t\t\t\tFEProperty* pj = pcj->FindProperty(sz);\n\t\t\t\t\t\tif (pj) return pj;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FECoreBase::GetProperty(int n)\n{\n\tint N = (int)m_Prop.size();\n\tint m = 0;\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFEProperty* pm = m_Prop[i];\n\t\tint l = pm->size();\n\t\tif (m + l > n) return pm->get(n - m);\n\t\tm += l;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreBase::SetProperty(int i, FECoreBase* pb)\n{\n\tFEProperty* pm = m_Prop[i];\n\tif (pm->IsType(pb))\n\t{\n\t\tpm->SetProperty(pb);\n\t\tif (pb) pb->SetParent(this);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! Set a property via name\nbool FECoreBase::SetProperty(const char* sz, FECoreBase* pb)\n{\n\tFEProperty* prop = FindProperty(sz);\n\tif (prop == nullptr) return false;\n\n\tif (prop->IsType(pb))\n\t{\n\t\tprop->SetProperty(pb);\n\t\tif (pb) pb->SetParent(this);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! number of parameters\nint FECoreBase::Parameters() const\n{\n\treturn GetParameterList().Parameters();\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FECoreBase::FindParameter(const ParamString& s)\n{\n\t// first search the parameter list\n\tFEParam* p = FEParamContainer::FindParameter(s);\n\tif (p) return p;\n\n\t// next, let's try the property list\n\tint NP = (int)m_Prop.size();\n\tfor (int i = 0; i<NP; ++i)\n\t{\n\t\t// get the property\n\t\tFEProperty* mp = m_Prop[i];\n\n\t\t// see if matches\n\t\tif (s == mp->GetName())\n\t\t{\n\t\t\tif (mp->IsArray())\n\t\t\t{\n\t\t\t\t// get the number of items in this property\n\t\t\t\tint nsize = mp->size();\n\t\t\t\tint index = s.Index();\n\t\t\t\tif ((index >= 0) && (index < nsize))\n\t\t\t\t{\n\t\t\t\t\treturn mp->get(index)->FindParameter(s.next());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint nid = s.ID();\n\t\t\t\t\tif (nid != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* pc = mp->getFromID(nid);\n\t\t\t\t\t\tif (pc) return pc->FindParameter(s.next());\n\t\t\t\t\t}\n\t\t\t\t\telse if (s.IDString())\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* c = mp->get(s.IDString());\n\t\t\t\t\t\tif (c) return c->FindParameter(s.next());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFECoreBase* pc = mp->get(0);\n\t\t\t\treturn (pc ? pc->FindParameter(s.next()) : nullptr);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\nFEParamValue FECoreBase::GetParameterValue(const ParamString& s)\n{\n\tFEParam* p = FEParamContainer::FindParameter(s);\n\tif (p)\n\t{\n\t\tFEParamValue paramVal;\n\t\tif (p->type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t{\n\t\t\tFEParamDouble& v = p->value<FEParamDouble>();\n\t\t\tif (v.isConst()) paramVal = FEParamValue(p, &v.constValue(), FE_PARAM_DOUBLE);\n\t\t\telse paramVal = FEParamValue(p, p->data_ptr(), p->type());\n\t\t}\n\t\telse paramVal = FEParamValue(p, p->data_ptr(), p->type());\n\n\t\tif (s.Index() >= 0)\n\t\t{\n\t\t\tparamVal = GetParameterComponent(paramVal, s.Index());\n\t\t}\n\n\t\tParamString comp = s.next();\n\t\tif (comp.isValid())\n\t\t{\n\t\t\tparamVal = GetParameterComponent(paramVal, comp.c_str());\n\t\t}\n\n\t\treturn paramVal;\n\t}\n\n\t// next, let's try the property list\n\tint NP = (int)m_Prop.size();\n\tfor (int i = 0; i < NP; ++i)\n\t{\n\t\t// get the property\n\t\tFEProperty* mp = m_Prop[i];\n\n\t\t// see if matches\n\t\tif (s == mp->GetName())\n\t\t{\n\t\t\tif (mp->IsArray())\n\t\t\t{\n\t\t\t\t// get the number of items in this property\n\t\t\t\tint nsize = mp->size();\n\t\t\t\tint index = s.Index();\n\t\t\t\tif ((index >= 0) && (index < nsize))\n\t\t\t\t{\n\t\t\t\t\treturn mp->get(index)->GetParameterValue(s.next());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint nid = s.ID();\n\t\t\t\t\tif (nid != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* pc = mp->getFromID(nid);\n\t\t\t\t\t\tif (pc) return pc->GetParameterValue(s.next());\n\t\t\t\t\t}\n\t\t\t\t\telse if (s.IDString())\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* c = mp->get(s.IDString());\n\t\t\t\t\t\tif (c) return c->GetParameterValue(s.next());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFECoreBase* pc = mp->get(0);\n\t\t\t\treturn (pc ? pc->GetParameterValue(s.next()) : FEParamValue());\n\t\t\t}\n\t\t}\n\t}\n\n\treturn FEParamValue();\n}\n\n//-----------------------------------------------------------------------------\n//! return the property (or this) that owns a parameter\nFECoreBase* FECoreBase::FindParameterOwner(void* pd)\n{\n\t// see if this class is the owner of the data pointer\n\tFEParam* p = FindParameterFromData(pd);\n\tif (p) return this;\n\n\t// it's not se let's check the properties\n\tint NP = PropertyClasses();\n\tfor (int i = 0; i < NP; ++i)\n\t{\n\t\tFEProperty* pi = PropertyClass(i);\n\t\tint n = pi->size();\n\t\tfor (int j = 0; j < n; ++j)\n\t\t{\n\t\t\tFECoreBase* pcj = pi->get(j);\n\t\t\tif (pcj)\n\t\t\t{\n\t\t\t\tFECoreBase* pc = pcj->FindParameterOwner(pd);\n\t\t\t\tif (pc) return pc;\n\t\t\t}\n\t\t}\n\t}\n\n\t// sorry, no luck\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! return the number of properties defined\nint FECoreBase::PropertyClasses() const \n{ \n\treturn (int)m_Prop.size(); \n}\n\n//! return a property\nFEProperty* FECoreBase::PropertyClass(int i)\n{ \n\treturn m_Prop[i]; \n}\n\n//-----------------------------------------------------------------------------\nFEProperty* FECoreBase::FindProperty(const ParamString& prop)\n{\n\tint NP = (int)m_Prop.size();\n\tfor (int i = 0; i < NP; ++i)\n\t{\n\t\tFEProperty* mp = m_Prop[i];\n\n\t\tif (prop == mp->GetName())\n\t\t{\n\t\t\tif (mp->IsArray())\n\t\t\t{\n\t\t\t\t// get the number of items in this property\n\t\t\t\tint nsize = mp->size();\n\t\t\t\tint index = prop.Index();\n\t\t\t\tif ((index >= 0) && (index < nsize))\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pc = mp->get(index);\n\t\t\t\t\tif (pc)\n\t\t\t\t\t{\n\t\t\t\t\t\tParamString next = prop.next();\n\t\t\t\t\t\tif (next.count() == 0) return nullptr;\n\t\t\t\t\t\telse return pc->FindProperty(next);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint nid = prop.ID();\n\t\t\t\t\tif (nid != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* pc = mp->getFromID(nid);\n\t\t\t\t\t\t// TODO: What to do here? \n\t\t\t\t\t\tassert(false);\n\t\t\t\t\t}\n\t\t\t\t\telse if (prop.IDString())\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* pc = mp->get(prop.IDString());\n\t\t\t\t\t\tif (pc)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tParamString next = prop.next();\n\t\t\t\t\t\t\tif (next.count() == 0) return nullptr;\n\t\t\t\t\t\t\telse return pc->FindProperty(next);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tParamString next = prop.next();\n\t\t\t\tif (next.count() == 0) return mp;\n\t\t\t\telse return FindProperty(next);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FECoreBase::GetProperty(const ParamString& prop)\n{\n\tint NP = (int) m_Prop.size();\n\tfor (int i=0; i<NP; ++i)\n\t{\t\n\t\tFEProperty* mp = m_Prop[i];\n\n\t\tif (prop == mp->GetName())\n\t\t{\n\t\t\tif (mp->IsArray())\n\t\t\t{\n\t\t\t\t// get the number of items in this property\n\t\t\t\tint nsize = mp->size();\n\t\t\t\tint index = prop.Index();\n\t\t\t\tif ((index >= 0) && (index < nsize))\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pc = mp->get(index);\n\t\t\t\t\tif (pc)\n\t\t\t\t\t{\n\t\t\t\t\t\tParamString next = prop.next();\n\t\t\t\t\t\tif (next.count() == 0) return pc;\n\t\t\t\t\t\telse return pc->GetProperty(next);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint nid = prop.ID();\n\t\t\t\t\tif (nid != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* pc = mp->getFromID(nid);\n\t\t\t\t\t}\n\t\t\t\t\telse if (prop.IDString())\n\t\t\t\t\t{\n\t\t\t\t\t\tFECoreBase* pc = mp->get(prop.IDString());\n\t\t\t\t\t\tif (pc)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tParamString next = prop.next();\n\t\t\t\t\t\t\tif (next.count() == 0) return pc;\n\t\t\t\t\t\t\telse return pc->GetProperty(next);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFECoreBase* pc = mp->get(0);\n\t\t\t\tParamString next = prop.next();\n\t\t\t\tif (next.count() == 0) return pc;\n\t\t\t\telse return pc->GetProperty(next);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nFEProperty* FECoreBase::FindProperty(FECoreBase* pc)\n{\n\tfor (int i = 0; i < PropertyClasses(); ++i)\n\t{\n\t\tFEProperty* prop = PropertyClass(i);\n\t\tif (prop)\n\t\t{\n\t\t\tint N = prop->size();\n\t\t\tfor (int j = 0; j < N; ++j)\n\t\t\t{\n\t\t\t\tif (prop->get(j) == pc) return prop;\n\t\t\t}\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreBase::BuildClass()\n{\n\tGetParameterList();\n\n\tfor (int i = 0; i < PropertyClasses(); ++i)\n\t{\n\t\tFEProperty* pp = PropertyClass(i);\n\t\tint m = pp->size();\n\t\tfor (int j = 0; j < m; ++j)\n\t\t{\n\t\t\tFECoreBase* pj = pp->get(j);\n\t\t\tif (pj) pj->BuildClass();\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreBase::UpdateParams()\n{\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FECoreBase.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEParameterList.h\"\n#include \"fecore_enum.h\"\n#include \"FEProperty.h\"\n#include \"ClassDescriptor.h\"\n#include <string>\n\n//-----------------------------------------------------------------------------\nclass FECoreFactory;\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n//! Base class for most classes in FECore library and the base class for all \n//! classes that can be registered with the framework.\nclass FECORE_API FECoreBase : public FEParamContainer\n{\npublic:\n\t//! constructor\n\tFECoreBase(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FECoreBase();\n\n\t//! set class name\n\tvoid SetName(const std::string& name);\n\n\t//! get the class' name\n\tconst std::string& GetName() const;\n\n\t//! data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Initialization\n\tvirtual bool Init();\n\n\t//! validates all properties and parameters recursively\n\tbool Validate() override;\n\n\t//! validate parameters only\n\tbool ValidateParameters();\n\n\t//! call this after the parameters are changed\n\tvirtual bool UpdateParams();\n\npublic:\n\t//! return the super class id\n\tSUPER_CLASS_ID GetSuperClassID();\n\n\t//! return a (unique) string describing the type of this class\n\t//! This string is used in object creation\n\tconst char* GetTypeStr();\n\n\t// Build the class' parameter and property list\n\tbool BuildClass();\n\npublic:\n\t//! number of parameters\n\tint Parameters() const;\n\n\t//! return a parameter\n\tvirtual FEParam* FindParameter(const ParamString& s) override;\n\n\tvirtual FEParamValue GetParameterValue(const ParamString& paramString);\n\n\t//! return the property (or this) that owns a parameter\n\tFECoreBase* FindParameterOwner(void* pd);\n\npublic: // interface for getting/setting properties\n\n\t//! get the number of properties\n\tint Properties();\n\n\t//! Set a property\n\tbool SetProperty(int nid, FECoreBase* pm);\n\n\t//! Set a property via name\n\tbool SetProperty(const char* sz, FECoreBase* pm);\n\n\t//! return a property\n\tvirtual FECoreBase* GetProperty(int i);\n\n\t//! find a property index ( returns <0 for error)\n\tint FindPropertyIndex(const char* szname);\n\n\t//! return a property (class)\n\tFEProperty* FindProperty(const char* sz, bool searchChildren = false);\n\tFEProperty* FindProperty(const ParamString& prop);\n\tFEProperty* FindProperty(FECoreBase* pc);\n\n\t//! return a property from a paramstring\n\tFECoreBase* GetProperty(const ParamString& prop);\n\n\t//! return the number of properties defined\n\tint PropertyClasses() const;\n\n\t//! return a property\n\tFEProperty* PropertyClass(int i);\n\npublic:\n\t//! Get the parent of this object (zero if none)\n\tFECoreBase* GetParent();\n\n\t//! Get the ancestor of this class (this if none)\n\tFECoreBase* GetAncestor();\n\n\t//! Set the parent of this class\n\tvoid SetParent(FECoreBase* parent);\n\n\t//! return the component ID\n\tint GetID() const;\n\n\t//! set the component ID\n\tvoid SetID(int nid);\n\n\t//! Get the FE model\n\tFEModel* GetFEModel() const;\n\n\t//! set the FEModel of this class (use with caution!)\n\tvoid SetFEModel(FEModel* fem);\n\n\tstatic void SaveClass(DumpStream& ar, FECoreBase* p);\n\tstatic FECoreBase* LoadClass(DumpStream& ar, FECoreBase* p);\n\n\t// set parameters through a class descriptor\n\tbool SetParameters(const FEClassDescriptor& cd);\n\tbool SetParameters(const FEClassDescriptor::ClassVariable& cv);\n\npublic:\n\t//! Add a property\n\t//! Call this in the constructor of derived classes to \n\t//! build the property list\n\tvoid AddProperty(FEProperty* pp, const char* sz, unsigned int flags = FEProperty::Required);\n\n\tvoid RemoveProperty(int i);\n\n\tvoid ClearProperties();\n\npublic:\n\ttemplate <class T> T* ExtractProperty(bool extractSelf = true);\n\nprotected:\n\t// Set the factory class\n\tvoid SetFactoryClass(const FECoreFactory* fac);\n\npublic:\n\tconst FECoreFactory* GetFactoryClass() const;\n\nprivate:\n\tstd::string\t\tm_name;\t\t\t//!< user defined name of component\n\tFECoreBase*\t\tm_pParent;\t\t//!< pointer to \"parent\" object (if any) (NOTE: only used by materials)\n\tFEModel*\t\tm_fem;\t\t\t//!< the model this class belongs to\n\n\tvector<FEProperty*>\tm_Prop;\t\t//!< list of properties\n\nprivate:\n\tint\t\tm_nID;\t\t\t//!< component ID\n\n\tconst FECoreFactory*\tm_fac;\t//!< factory class that instantiated this class\n\n\tfriend class FECoreFactory;\n};\n\n// include template property definitions\n#include \"FEPropertyT.h\"\n\ntemplate <class T>\tFEProperty* AddClassProperty(FECoreBase* pc, T* pp, const char* sz)\n{\n\tFEFixedPropertyT<T>* prop = new FEFixedPropertyT<T>(pp);\n\tprop->SetDefaultType(sz);\n\tpc->AddProperty(prop, sz, FEProperty::Fixed);\n\treturn prop;\n}\n\ntemplate <class T>\tFEProperty* AddClassProperty(FECoreBase* pc, std::vector<T>* pp, const char* sz)\n{\n\tFEFixedVecPropertyT<T>* prop = new FEFixedVecPropertyT<T>(pp);\n\tprop->SetDefaultType(sz);\n\tpc->AddProperty(prop, sz, FEProperty::Fixed);\n\treturn prop;\n}\n\ntemplate <class T> FEProperty* AddClassProperty(FECoreBase* pc, T** pp, const char* sz, unsigned int flags = FEProperty::Required)\n{\n\tFEPropertyT<T>* prop = new FEPropertyT<T>(pp);\n\tif (prop->GetSuperClassID() == FECLASS_ID) prop->SetDefaultType(sz);\n\tpc->AddProperty(prop, sz, flags);\n\treturn prop;\n}\n\ntemplate <class T>\tFEProperty* AddClassProperty(FECoreBase* pc, std::vector<T*>* pp, const char* sz, unsigned int flags = FEProperty::Required)\n{\n\tFEVecPropertyT<T>* prop = new FEVecPropertyT<T>(pp);\n\tif (prop->GetSuperClassID() == FECLASS_ID) prop->SetDefaultType(sz);\n\tpc->AddProperty(prop, sz, flags);\n\treturn prop;\n}\n\n#define ADD_PROPERTY(theProp, ...) AddClassProperty(this, &theProp, __VA_ARGS__)\n\n#define FECORE_SUPER_CLASS(a) public: static SUPER_CLASS_ID superClassID() { return a; }\n//#define REGISTER_SUPER_CLASS(theClass, a) SUPER_CLASS_ID theClass::superClassID() { return a;}\n\ntemplate <class T> T* FECoreBase::ExtractProperty(bool extractSelf)\n{\n\tif (extractSelf)\n\t{\n\t\tif (dynamic_cast<T*>(this)) return dynamic_cast<T*>(this);\n\t}\n\n\tint NC = Properties();\n\tfor (int i = 0; i < NC; i++)\n\t{\n\t\tFECoreBase* pci = GetProperty(i);\n\t\tif (pci)\n\t\t{\n\t\t\tT* pc = pci->ExtractProperty<T>();\n\t\t\tif (pc) return pc;\n\t\t}\n\t}\n\treturn nullptr;\n}\n"
  },
  {
    "path": "FECore/FECoreClass.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FECoreClass.h\"\n\nBEGIN_FECORE_CLASS(FECoreClass, FECoreBase)\nEND_FECORE_CLASS();\n\nFECoreClass::FECoreClass(FEModel* fem) : FECoreBase(fem)\n{\n\n}\n"
  },
  {
    "path": "FECore/FECoreClass.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"fecore_api.h\"\n\n// Generic base class for classes that don't fit in the super-class structure\nclass FECORE_API FECoreClass : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FECLASS_ID)\n\npublic:\n\tFECoreClass(FEModel* fem = nullptr);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FECoreFactory.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoreFactory.h\"\n#include \"FECoreBase.h\"\n#include \"log.h\"\n#include <assert.h>\n\n//-----------------------------------------------------------------------------\n//! constructor\nFECoreFactory::FECoreFactory(SUPER_CLASS_ID scid, const char* szclass, const char* szbase, const char* szalias, int nspec) : m_scid(scid)\n{ \n\tm_szclass = szclass;\n\tm_szalias = szalias;\n\tm_szbase = szbase;\n\tm_module = 0; \n\tm_spec = nspec;\n\tm_alloc_id = -1;\n}\n\n//-----------------------------------------------------------------------------\n//! virtual constructor\nFECoreFactory::~FECoreFactory(){}\n\n//-----------------------------------------------------------------------------\n//! set the module ID\nvoid FECoreFactory::SetModuleID(unsigned int mid)\n{\n\tm_module = mid;\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FECoreFactory::CreateInstance(FEModel* pfem) const\n{\n\t// create a new instance of this class\n\tFECoreBase* pclass = Create(pfem); assert(pclass);\n\tif (pclass == 0) return 0;\n\n\t// store the factory that created the class\n\tpclass->SetFactoryClass(this);\n\n\t// build the class descriptor\n\tif (pclass->BuildClass() == false) return 0;\n\n\t// return the pointer\n\treturn pclass;\n}\n"
  },
  {
    "path": "FECore/FECoreFactory.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_enum.h\"\n#include \"FEParameterList.h\"\n\n//-----------------------------------------------------------------------------\n//! Forward declaration of the FEModel class. All classes that register\n//! with the framework take a pointer to FEModel as their constructor parameter.\nclass FEModel;\nclass FECoreBase;\n\n//-----------------------------------------------------------------------------\n//! The factory class contains the mechanism for instantiating a class.\nclass FECORE_API FECoreFactory : public FEParamContainer\n{\npublic:\n\t//! constructor\n\tFECoreFactory(SUPER_CLASS_ID scid, const char* szclass, const char* szbase, const char* szalias, int nspec = -1);\n\n\t//! virtual constructor\n\tvirtual ~FECoreFactory();\n\n\t//! This is the function that the kernel will use to intantiate an object\n\tFECoreBase* CreateInstance(FEModel* pfem) const;\n\npublic:\n\t// return the class name\n\tconst char* GetClassName() const { return m_szclass; }\n\n\t// return the base class name\n\tconst char* GetBaseClassName() const { return m_szbase; }\n\n\t// return the type string identifier\n\tconst char* GetTypeStr() const { return m_szalias; }\n\n\t//! return the super-class ID\n\tSUPER_CLASS_ID GetSuperClassID() const { return m_scid; }\n\n\t//! return the module name\n\tunsigned int GetModuleID() const { return m_module; }\n\n\t//! set the module name\n\tvoid SetModuleID(unsigned int nid);\n\n\t//! Get the spec number\n\tint GetSpecID() const { return m_spec; }\n\n\t//! Set the allocator ID\n\tvoid SetAllocatorID(int alloc) { m_alloc_id = alloc; }\n\n\t//! Get the allocator ID\n\tint GetAllocatorID() const { return m_alloc_id; }\n\t\npublic:\n\t//! derived classes implement this to create an instance of a class\n\tvirtual FECoreBase* Create(FEModel*) const = 0;\n\nprivate:\n\tconst char*\t\tm_szclass;\t//!< class name\n\tconst char*\t\tm_szbase;\t//!< base class name\n\tconst char*\t\tm_szalias;\t//!< class alias string\n\tint\t\t\t\tm_spec;\t\t//!< The max spec number for which this feature is defined (-1 is don't care)\n\tunsigned int\tm_module;\t//!< ID of module this class belongs to\n\tSUPER_CLASS_ID\tm_scid;\t\t//!< the super-class ID\n\tint\t\t\t\tm_alloc_id;\t//!< allocator ID\n};\n\n//-----------------------------------------------------------------------------\n//! Forward declarations of classes used by the domain factory\nclass FEDomain;\nclass FEMesh;\nclass FEMaterial;\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n//! Creation of domains are a little more elaborate and deviate from the usual\n//! factory methods.\nclass FECORE_API FEDomainFactory\n{\npublic:\n\tFEDomainFactory(){}\n\tvirtual ~FEDomainFactory(){}\n\n\tvirtual FEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat) = 0;\n};\n\n#define FECORE_SPEC(major, minor) ((major << 8) + minor)\n#define FECORE_SPEC_MAJOR(n) ((n) >> 8)\n#define FECORE_SPEC_MINOR(n) ((n) & 0x0F)\n\n// macro for tagging a feature as experimental (i.e. in development)\n#define FECORE_EXPERIMENTAL\tint(0xFFFF)\n#define FECORE_DEPRECATED int(0x300)\n"
  },
  {
    "path": "FECore/FECoreKernel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoreKernel.h\"\n#include \"LinearSolver.h\"\n#include \"Timer.h\"\n#include \"FEModule.h\"\n#include <stdarg.h>\n#include \"log.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nFECoreKernel* FECoreKernel::m_pKernel = 0;\n\n//-----------------------------------------------------------------------------\nFECoreKernel& FECoreKernel::GetInstance()\n{\n\tif (m_pKernel == 0) m_pKernel = new FECoreKernel;\n\treturn *m_pKernel;\n}\n\n//-----------------------------------------------------------------------------\n// This function is used by plugins to make sure that the plugin and the executable\n// are using the same kernel\nvoid FECoreKernel::SetInstance(FECoreKernel* pkernel)\n{\n\tm_pKernel = pkernel;\n}\n\n//-----------------------------------------------------------------------------\nconst char* FECoreKernel::SuperClassString(unsigned int sid)\n{\n\tFECoreKernel& fecore = GetInstance();\n\tif (fecore.m_sidMap.find(sid) != fecore.m_sidMap.end())\n\t\treturn fecore.m_sidMap[sid];\n\telse\n\t\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nstd::map<unsigned int, const char*>\tFECoreKernel::GetSuperClassMap()\n{\n\tFECoreKernel& fecore = GetInstance();\n\treturn fecore.m_sidMap;\n}\n\n//-----------------------------------------------------------------------------\n\n#define ADD_SUPER_CLASS(a) m_sidMap[a] = #a\nFECoreKernel::FECoreKernel()\n{\n\tm_activeModule = -1;\n\tm_alloc_id = 0;\n\tm_next_alloc_id = 1;\n\tm_nspec = -1;\n\tm_default_solver = nullptr;\n\tm_bshowDeprecationWarning = true;\n\n\t// build the super class ID table\n\tADD_SUPER_CLASS(FEINVALID_ID);\n\tADD_SUPER_CLASS(FEOBJECT_ID);\n\tADD_SUPER_CLASS(FETASK_ID);\n\tADD_SUPER_CLASS(FESOLVER_ID);\n\tADD_SUPER_CLASS(FEMATERIAL_ID);\n\tADD_SUPER_CLASS(FEMATERIALPROP_ID);\n\tADD_SUPER_CLASS(FEDISCRETEMATERIAL_ID);\n\tADD_SUPER_CLASS(FELOAD_ID);\n\tADD_SUPER_CLASS(FENLCONSTRAINT_ID);\n\tADD_SUPER_CLASS(FEPLOTDATA_ID);\n\tADD_SUPER_CLASS(FEANALYSIS_ID);\n\tADD_SUPER_CLASS(FESURFACEINTERACTION_ID);\n\tADD_SUPER_CLASS(FELOGNODEDATA_ID);\n\tADD_SUPER_CLASS(FELOGFACEDATA_ID);\n\tADD_SUPER_CLASS(FELOGELEMDATA_ID);\n\tADD_SUPER_CLASS(FELOGOBJECTDATA_ID);\n\tADD_SUPER_CLASS(FELOGDOMAINDATA_ID);\n\tADD_SUPER_CLASS(FELOGNLCONSTRAINTDATA_ID);\n\tADD_SUPER_CLASS(FELOGSURFACEDATA_ID);\n\tADD_SUPER_CLASS(FELOGMODELDATA_ID);\n\tADD_SUPER_CLASS(FEBC_ID);\n\tADD_SUPER_CLASS(FEGLOBALDATA_ID);\n\tADD_SUPER_CLASS(FECALLBACK_ID);\n\tADD_SUPER_CLASS(FESOLIDDOMAIN_ID);\n\tADD_SUPER_CLASS(FESHELLDOMAIN_ID);\n\tADD_SUPER_CLASS(FEBEAMDOMAIN_ID);\n\tADD_SUPER_CLASS(FEDISCRETEDOMAIN_ID);\n\tADD_SUPER_CLASS(FEDOMAIN2D_ID);\n\tADD_SUPER_CLASS(FESURFACE_ID);\n\tADD_SUPER_CLASS(FEEDGE_ID);\n\tADD_SUPER_CLASS(FEIC_ID);\n\tADD_SUPER_CLASS(FEMESHDATAGENERATOR_ID);\n\tADD_SUPER_CLASS(FELOADCONTROLLER_ID);\n\tADD_SUPER_CLASS(FEMODEL_ID);\n\tADD_SUPER_CLASS(FESCALARVALUATOR_ID);\n\tADD_SUPER_CLASS(FEVEC3DVALUATOR_ID);\n\tADD_SUPER_CLASS(FEMAT3DVALUATOR_ID);\n\tADD_SUPER_CLASS(FEMAT3DSVALUATOR_ID);\n\tADD_SUPER_CLASS(FEFUNCTION1D_ID);\n\tADD_SUPER_CLASS(FELINEARSOLVER_ID);\n\tADD_SUPER_CLASS(FEMESHADAPTOR_ID);\n\tADD_SUPER_CLASS(FEMESHADAPTORCRITERION_ID);\n\tADD_SUPER_CLASS(FENEWTONSTRATEGY_ID);\n\tADD_SUPER_CLASS(FETIMECONTROLLER_ID);\n\tADD_SUPER_CLASS(FEEIGENSOLVER_ID);\n\tADD_SUPER_CLASS(FEDATARECORD_ID);\n\tADD_SUPER_CLASS(FECLASS_ID);\n\tADD_SUPER_CLASS(FESCRIPT_ID);\n}\n\n//-----------------------------------------------------------------------------\n// Generate a allocator ID\nint FECoreKernel::GenerateAllocatorID()\n{\n\treturn m_next_alloc_id++;\n}\n\n//-----------------------------------------------------------------------------\nFECoreFactory* FECoreKernel::SetDefaultSolverType(const char* sztype)\n{\n\tFECoreFactory* fac = FindFactoryClass(FELINEARSOLVER_ID, sztype);\n\tif (fac) m_default_solver_type = sztype;\n\treturn fac;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreKernel::SetDefaultSolver(FEClassDescriptor* linsolve)\n{\n\tdelete m_default_solver;\n\tm_default_solver = linsolve;\n\n\tif (linsolve)\n\t{\n\t\tm_default_solver_type = linsolve->ClassType();\n\t}\n\telse\n\t{\n\t\tm_default_solver_type.clear();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the linear solver type\nconst char* FECoreKernel::GetLinearSolverType() const\n{\n\treturn m_default_solver_type.c_str();\n}\n\n//-----------------------------------------------------------------------------\nLinearSolver* FECoreKernel::CreateDefaultLinearSolver(FEModel* fem)\n{\n\tif (m_default_solver == nullptr)\n\t{\n\t\tconst char* sztype = m_default_solver_type.c_str();\n\t\tFECoreFactory* fac = FindFactoryClass(FELINEARSOLVER_ID, sztype);\n\t\treturn (LinearSolver*)CreateInstance(fac, fem);\n\t}\n\telse\n\t{\n\t\treturn (LinearSolver*)Create(FELINEARSOLVER_ID, fem, *m_default_solver);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreKernel::RegisterFactory(FECoreFactory* ptf)\n{\n\tunsigned int activeID = 0;\n\tif (m_activeModule != -1)\n\t{\n\t\tFEModule& activeModule = *m_modules[m_activeModule];\n\t\tactiveID = activeModule.GetModuleID();\n\t}\n\n\t// see if the name already exists\n\tfor (int i=0; i<m_Fac.size(); ++i)\n\t{\n\t\tFECoreFactory* pfi = m_Fac[i];\n\n\t\tif ((pfi->GetSuperClassID() == ptf->GetSuperClassID()) && \n\t\t\t(strcmp(pfi->GetTypeStr(), ptf->GetTypeStr()) == 0))\n\t\t{\n\t\t\t// A feature with the same is already registered. \n\t\t\t// We need to check the module to see if this would create an ambiguity\n\t\t\tunsigned int modId = pfi->GetModuleID();\n\n\t\t\t// If the same feature is defined in the active module,\n\t\t\t// then this feature will replace the existing one. \n\t\t\tif ((modId == activeID) && (pfi->GetSpecID() == ptf->GetSpecID()))\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"WARNING: \\\"%s\\\" feature is redefined\\n\", ptf->GetTypeStr());\n\t\t\t\tm_Fac[i] = ptf;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t// it doesn't so add it\n\tptf->SetModuleID(activeID);\n\tptf->SetAllocatorID(m_alloc_id);\n\tm_Fac.push_back(ptf);\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreKernel::UnregisterFactory(FECoreFactory* ptf)\n{\n\tfor (vector<FECoreFactory*>::iterator it = m_Fac.begin(); it != m_Fac.end(); ++it)\n\t{\n\t\tFECoreFactory* pfi = *it;\n\t\tif (pfi == ptf)\n\t\t{\n\t\t\tm_Fac.erase(it);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! unregister factories from allocator\nvoid FECoreKernel::UnregisterFactories(int alloc_id)\n{\n\tfor (vector<FECoreFactory*>::iterator it = m_Fac.begin(); it != m_Fac.end();)\n\t{\n\t\tFECoreFactory* pfi = *it;\n\t\tif (pfi->GetAllocatorID() == alloc_id)\n\t\t{\n\t\t\tit = m_Fac.erase(it);\n\t\t}\n\t\telse ++it;\n\t}\n}\n\n//! unregister modules from allocator\nvoid FECoreKernel::UnregisterModules(int alloc_id)\n{\n    for (vector<FEModule*>::iterator it = m_modules.begin(); it != m_modules.end();)\n\t{\n\t\tFEModule* pfi = *it;\n        int alloc = pfi->GetAllocID();\n\t\tif (pfi->GetAllocID() == alloc_id)\n\t\t{\n\t\t\tit = m_modules.erase(it);\n\t\t}\n\t\telse ++it;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! set the current allocator ID\nvoid FECoreKernel::SetAllocatorID(int alloc_id)\n{\n\tm_alloc_id = alloc_id;\n}\n\n//-----------------------------------------------------------------------------\n//! Create an object. An object is created by specifying the super-class id\n//! and the type-string. \nFECoreBase* FECoreKernel::Create(int superClassID, const char* sztype, FEModel* pfem)\n{\n\tFECoreFactory* fac = FindFactoryClass(superClassID, sztype);\n\tif (fac == nullptr) return nullptr;\n\treturn CreateInstance(fac, pfem);\n}\n\n//-----------------------------------------------------------------------------\n//! Create an object. An object is created by specifying the super-class id\n//! and the type-string. \nFECoreBase* FECoreKernel::Create(int superClassID, const char* sztype, const char* szmod, FEModel* pfem)\n{\n\tFECoreFactory* fac = FindFactoryClass(superClassID, sztype, szmod);\n\tif (fac == nullptr) return nullptr;\n\treturn CreateInstance(fac, pfem);\n}\n\n//-----------------------------------------------------------------------------\n//! Create an object. An object is created by specifying the super-class id\n//! and the type-string. \nFECoreBase* FECoreKernel::Create(const char* szbase, const char* sztype, FEModel* pfem)\n{\n\tif (szbase == nullptr) return nullptr;\n\tif (sztype == nullptr) return nullptr;\n\n\tunsigned int activeID = 0;\n\tvector<int> moduleDepends;\n\tif (m_activeModule != -1)\n\t{\n\t\tFEModule& activeModule = *m_modules[m_activeModule];\n\t\tactiveID = activeModule.GetModuleID();\n\t\tmoduleDepends = activeModule.GetDependencies();\n\t}\n\n\t// first check active module\n\tif ((activeID > 0) || (activeID == 0))\n\t{\n\t\tstd::vector<FECoreFactory*>::iterator pf;\n\t\tfor (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n\t\t{\n\t\t\tFECoreFactory* pfac = *pf;\n\t\t\tif (strcmp(pfac->GetBaseClassName(), szbase) == 0) {\n\n\t\t\t\t// see if we can match module first\n\t\t\t\tunsigned int mid = pfac->GetModuleID();\n\t\t\t\tif ((mid == activeID) || (mid == 0))\n\t\t\t\t{\n\t\t\t\t\t// see if the type name matches\n\t\t\t\t\tif ((strcmp(pfac->GetTypeStr(), sztype) == 0))\n\t\t\t\t\t{\n\t\t\t\t\t\t// check the spec (TODO: What is this for?)\n\t\t\t\t\t\tint nspec = pfac->GetSpecID();\n\t\t\t\t\t\tif ((nspec == -1) || (m_nspec <= nspec))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn CreateInstance(pfac, pfem);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// check dependencies in order in which they are defined\n\tstd::vector<FECoreFactory*>::iterator pf;\n\tfor (int i = 0; i < moduleDepends.size(); ++i)\n\t{\n\t\tunsigned modId = moduleDepends[i];\n\t\tfor (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n\t\t{\n\t\t\tFECoreFactory* pfac = *pf;\n\t\t\tif (strcmp(pfac->GetBaseClassName(), szbase) == 0) {\n\n\t\t\t\t// see if we can match module first\n\t\t\t\tunsigned int mid = pfac->GetModuleID();\n\t\t\t\tif ((mid == 0) || (mid == modId))\n\t\t\t\t{\n\t\t\t\t\t// see if the type name matches\n\t\t\t\t\tif ((strcmp(pfac->GetTypeStr(), sztype) == 0))\n\t\t\t\t\t{\n\t\t\t\t\t\t// check the spec (TODO: What is this for?)\n\t\t\t\t\t\tint nspec = pfac->GetSpecID();\n\t\t\t\t\t\tif ((nspec == -1) || (m_nspec <= nspec))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn CreateInstance(pfac, pfem);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a specific class\nFECoreBase* FECoreKernel::CreateClass(const char* szclassName, FEModel* fem)\n{\n\tstd::vector<FECoreFactory*>::iterator pf;\n\tfor (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n\t{\n\t\tFECoreFactory* pfac = *pf;\n\t\tconst char* szfacName = pfac->GetClassName();\n\t\tif (szfacName && (strcmp(szfacName, szclassName) == 0))\n\t\t{\n\t\t\treturn CreateInstance(pfac, fem);\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a class from a class descriptor\nFECoreBase* FECoreKernel::Create(int superClassID, FEModel* pfem, const FEClassDescriptor& cd)\n{\n\tconst FEClassDescriptor::ClassVariable* root = cd.Root();\n\tFECoreBase* pc = (FECoreBase*)Create(superClassID, root->m_type.c_str(), pfem);\n\tif (pc == nullptr) return nullptr;\n\tpc->SetParameters(cd);\n\treturn pc;\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreKernel::IsModuleActive(int moduleID)\n{\n\tif (moduleID <= 0) return true;\n\tif (m_activeModule < 0) return false;\n\n\tFEModule* mod = GetActiveModule();\n\tif (mod == nullptr) return false;\n\n\tif (mod->GetModuleID() == moduleID) return true;\n\n\tstd::vector<int> deps = mod->GetDependencies();\n\tfor (int i = 0; i < deps.size(); ++i)\n\t{\n\t\tif (deps[i] == moduleID) return true;\n\t}\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FECoreKernel::CreateInstance(const FECoreFactory* fac, FEModel* fem)\n{\n\tFECoreBase* pc = fac->CreateInstance(fem);\n\n\tint nspec = fac->GetSpecID();\n\tif (m_bshowDeprecationWarning && (nspec != -1) && fem)\n\t{\n\t\tif (nspec == FECORE_EXPERIMENTAL)\n\t\t{\n\t\t\tfeLogWarningEx(fem, \"\\\"%s\\\" is considered experimental!\", fac->GetTypeStr());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint n1 = FECORE_SPEC_MAJOR(nspec);\n\t\t\tint n2 = FECORE_SPEC_MINOR(nspec);\n\t\t\tfeLogWarningEx(fem, \"\\\"%s\\\" is deprecated in spec %d.%d!\", fac->GetTypeStr(), n1, n2);\n\t\t}\n\t}\n\n\treturn pc;\n}\n\n//-----------------------------------------------------------------------------\nint FECoreKernel::Count(SUPER_CLASS_ID sid)\n{\n\tint N = 0;\n\tstd::vector<FECoreFactory*>::iterator pf;\n\tfor (pf=m_Fac.begin(); pf!= m_Fac.end(); ++pf)\n\t{\n\t\tFECoreFactory* pfac = *pf;\n\t\tif (pfac->GetSuperClassID() == sid) N++;\n\t}\n\treturn N;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreKernel::List(SUPER_CLASS_ID sid)\n{\n  std::vector<FECoreFactory*>::iterator pf;\n  for (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n    {\n      FECoreFactory* pfac = *pf;\n      if (pfac->GetSuperClassID() == sid) fprintf(stdout, \"%s\\n\", pfac->GetTypeStr());\n    }\n}\n\n//-----------------------------------------------------------------------------\nint FECoreKernel::FactoryClasses()\n{\n\treturn (int) m_Fac.size();\n}\n\n//-----------------------------------------------------------------------------\nconst FECoreFactory* FECoreKernel::GetFactoryClass(int i)\n{\n\tif ((i < 0) || (i >= m_Fac.size())) return nullptr;\n\telse return m_Fac[i];\n}\n\n//-----------------------------------------------------------------------------\n//! return a factory class\nconst FECoreFactory* FECoreKernel::GetFactoryClass(int classID, int i)\n{\n\tint n = 0;\n\tfor (int j = 0; j < m_Fac.size(); ++j)\n\t{\n\t\tFECoreFactory* fac = m_Fac[j];\n\t\tif (fac->GetSuperClassID() == classID)\n\t\t{\n\t\t\tif (i == n) return fac;\n\t\t\tn++;\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! return a factory class\nint FECoreKernel::GetFactoryIndex(int superClassId, const char* sztype)\n{\n\tFEModule* mod = GetActiveModule();\n\tif (mod == nullptr) return -1;\n\tfor (int j = 0; j < m_Fac.size(); ++j)\n\t{\n\t\tFECoreFactory* fac = m_Fac[j];\n\t\tint modId = fac->GetModuleID();\n\n\t\t// check the super-class first\n\t\tif (fac->GetSuperClassID() == superClassId)\n\t\t{\n\t\t\t// check the string name \n\t\t\tif (strcmp(sztype, fac->GetTypeStr()) == 0)\n\t\t\t{\n\t\t\t\t// make sure it's part of the active module\n\t\t\t\tif ((modId == 0) || (mod->HasDependent(modId))) return j;\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\n\n//-----------------------------------------------------------------------------\nFECoreFactory* FECoreKernel::FindFactoryClass(int superID, const char* sztype)\n{\n\tif (sztype == nullptr) return nullptr;\n\n\tunsigned int activeID = 0;\n\tvector<int> moduleDepends;\n\tif (m_activeModule != -1)\n\t{\n\t\tFEModule& activeModule = *m_modules[m_activeModule];\n\t\tactiveID = activeModule.GetModuleID();\n\t\tmoduleDepends = activeModule.GetDependencies();\n\t}\n\n\t// first check active module\n\tif ((activeID > 0) || (activeID == 0))\n\t{\n\t\tstd::vector<FECoreFactory*>::iterator pf;\n\t\tfor (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n\t\t{\n\t\t\tFECoreFactory* pfac = *pf;\n\t\t\tif (pfac->GetSuperClassID() == superID) {\n\n\t\t\t\t// see if we can match module first\n\t\t\t\tunsigned int mid = pfac->GetModuleID();\n\t\t\t\tif ((mid == activeID) || (mid == 0))\n\t\t\t\t{\n\t\t\t\t\t// see if the type name matches\n\t\t\t\t\tif ((strcmp(pfac->GetTypeStr(), sztype) == 0))\n\t\t\t\t\t{\n\t\t\t\t\t\t// check the spec (TODO: What is this for?)\n\t\t\t\t\t\tint nspec = pfac->GetSpecID();\n\t\t\t\t\t\tif ((nspec == -1) || (m_nspec <= nspec))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn pfac;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// check dependencies in order in which they are defined\n\tstd::vector<FECoreFactory*>::iterator pf;\n\tfor (int i = 0; i < moduleDepends.size(); ++i)\n\t{\n\t\tunsigned modId = moduleDepends[i];\n\t\tfor (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n\t\t{\n\t\t\tFECoreFactory* pfac = *pf;\n\t\t\tif (pfac->GetSuperClassID() == superID) {\n\n\t\t\t\t// see if we can match module first\n\t\t\t\tunsigned int mid = pfac->GetModuleID();\n\t\t\t\tif ((mid == 0) || (mid == modId))\n\t\t\t\t{\n\t\t\t\t\t// see if the type name matches\n\t\t\t\t\tif ((strcmp(pfac->GetTypeStr(), sztype) == 0))\n\t\t\t\t\t{\n\t\t\t\t\t\t// check the spec (TODO: What is this for?)\n\t\t\t\t\t\tint nspec = pfac->GetSpecID();\n\t\t\t\t\t\tif ((nspec == -1) || (m_nspec <= nspec))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn pfac;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFECoreFactory* FECoreKernel::FindFactoryClass(int superID, const char* sztype, const char* szmod)\n{\n\tif (sztype == nullptr) return nullptr;\n\n\tint modId = FindModuleID(szmod);\n\tif (modId == -1) return nullptr;\t\n\n\tstd::vector<FECoreFactory*>::iterator pf;\n\tfor (pf = m_Fac.begin(); pf != m_Fac.end(); ++pf)\n\t{\n\t\tFECoreFactory* pfac = *pf;\n\t\tif (pfac->GetSuperClassID() == superID) {\n\n\t\t\t// see if we can match module first\n\t\t\tunsigned int mid = pfac->GetModuleID();\n\t\t\tif (mid == modId)\n\t\t\t{\n\t\t\t\t// see if the type name matches\n\t\t\t\tif ((strcmp(pfac->GetTypeStr(), sztype) == 0))\n\t\t\t\t{\n\t\t\t\t\t// check the spec (TODO: What is this for?)\n\t\t\t\t\tint nspec = pfac->GetSpecID();\n\t\t\t\t\tif ((nspec == -1) || (m_nspec <= nspec))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn pfac;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! set the active module\nbool FECoreKernel::SetActiveModule(const char* szmod)\n{\n\t// See if user want to deactivate modules\n\tif (szmod == 0)\n\t{\n\t\tm_activeModule = -1;\n\t\treturn true;\n\t}\n\n\t// see if the module exists or not\n\tfor (size_t i=0; i<m_modules.size(); ++i) \n\t{\n\t\tFEModule& mi = *m_modules[i];\n\t\tif (strcmp(mi.GetName(), szmod) == 0)\n\t\t{\n\t\t\tm_activeModule = (int) i;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// couldn't find it\n\tm_activeModule = -1;\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FECoreKernel::SetActiveModule(int moduleId)\n{\n\tif (moduleId < 0) return false;\n\tif (GetActiveModuleID() == moduleId) return true;\n\n\t// see if the module exists or not\n\tfor (size_t i = 0; i < m_modules.size(); ++i)\n\t{\n\t\tFEModule& mi = *m_modules[i];\n\t\tif (mi.GetModuleID() == moduleId)\n\t\t{\n\t\t\tm_activeModule = (int)i;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// couldn't find it\n\tm_activeModule = -1;\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// return the active module's ID\nint FECoreKernel::GetActiveModuleID()\n{\n\tif (m_activeModule == -1) return -1;\n\treturn m_modules[m_activeModule]->GetModuleID();\n}\n\n//-----------------------------------------------------------------------------\nFEModule* FECoreKernel::GetActiveModule()\n{\n\tif (m_activeModule == -1) return nullptr;\n\treturn m_modules[m_activeModule];\n}\n\n//-----------------------------------------------------------------------------\n//! count modules\nint FECoreKernel::Modules() const\n{\n\treturn (int)m_modules.size();\n}\n\n//-----------------------------------------------------------------------------\n//! create a module\nbool FECoreKernel::CreateModule(const char* szmod, const char* description)\n{\n\tFEModule* mod = new FEModule();\n\treturn CreateModule(mod, szmod, description);\n}\n\n//-----------------------------------------------------------------------------\n//! create a module\nbool FECoreKernel::CreateModule(FEModule* pmodule, const char* szmod, const char* description)\n{\n\tassert(pmodule);\n\tif (pmodule == nullptr) return false;\n\n\tm_activeModule = -1;\n\tif (szmod == 0) return false;\n\n\t// see if this module already exist\n\tif (SetActiveModule(szmod) == false)\n\t{\n\t\t// The module does not exist, so let's add it.\n\t\tunsigned int newID = (unsigned int)m_modules.size() + 1;\n\n\t\tpmodule->SetName(szmod);\n\t\tpmodule->SetID(newID);\n\t\tpmodule->SetDescription(description);\n        pmodule->SetAllocID(m_alloc_id);\n\t\tm_modules.push_back(pmodule);\n\n\t\t// make this the active module\n\t\tm_activeModule = (int)m_modules.size() - 1;\n\t}\n\telse return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Get a module\nconst char* FECoreKernel::GetModuleName(int i) const\n{\n\tif ((i<0) || (i >= m_modules.size())) return nullptr;\n\treturn m_modules[i]->GetName();\n}\n\nconst char* FECoreKernel::GetModuleDescription(int i) const\n{\n\tif ((i < 0) || (i >= m_modules.size())) return nullptr;\n\treturn m_modules[i]->GetDescription();\n}\n\nint FECoreKernel::GetModuleStatus(int i) const\n{\n\tif ((i < 0) || (i >= m_modules.size())) return -1;\n\treturn m_modules[i]->GetStatus();\n}\n\nint FECoreKernel::GetModuleAllocatorID(int i) const\n{\n    if ((i < 0) || (i >= m_modules.size())) return -1;\n    return m_modules[i]->GetAllocID();\n}\n\nint FECoreKernel::FindModuleID(const char* szmodule) const\n{\n\tif (szmodule == 0) return -1;\n\tfor (size_t i = 0; i<m_modules.size(); ++i)\n\t{\n\t\tFEModule& mi = *m_modules[i];\n\t\tif (strcmp(mi.GetName(), szmodule) == 0) return mi.GetModuleID();\n\t}\n\treturn -1;\n}\n\n//! Get a module\nconst char* FECoreKernel::GetModuleNameFromId(int id) const\n{\n\tfor (size_t n = 0; n < m_modules.size(); ++n)\n\t{\n\t\tconst FEModule& mod = *m_modules[n];\n\t\tif (mod.GetModuleID() == id) return mod.GetName();\n\t}\n\treturn 0;\n}\n\n//! Get a module's dependencies\nvector<int> FECoreKernel::GetModuleDependencies(int i) const\n{\n\tvector<int> md;\n\tif ((i >= 0) && (i < m_modules.size()))\n\t{\n\t\tmd = m_modules[i]->GetDependencies();\n\t}\n\treturn md;\n}\n\n\n//-----------------------------------------------------------------------------\n//! set the spec ID. Features with a matching spec ID will be preferred\n//! set spec ID to -1 to stop caring\nvoid FECoreKernel::SetSpecID(int nspec)\n{\n\tm_nspec = nspec;\n}\n\n//-----------------------------------------------------------------------------\n//! set a dependency on a module\nbool FECoreKernel::AddModuleDependency(const char* szmodule)\n{\n\tif (m_activeModule == -1) return false;\n\tFEModule& activeModule = *m_modules[m_activeModule];\n\n\tif (szmodule == 0)\n\t{\n\t\t// clear dependencies\n\t\tactiveModule.ClearDependencies();\n\t\treturn true;\n\t}\n\n\t// find the module\n\tfor (size_t i = 0; i<m_modules.size(); ++i)\n\t{\n\t\tFEModule& mi = *m_modules[i];\n\t\tif (strcmp(mi.GetName(), szmodule) == 0)\n\t\t{\n\t\t\t// add the module to the active module's dependency list\n\t\t\tactiveModule.AddDependency(mi);\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// oh, oh, couldn't find it\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! Register a new domain class\nvoid FECoreKernel::RegisterDomain(FEDomainFactory* pf, bool pushFront)\n{\n\tif (pushFront)\n\t\tm_Dom.insert(m_Dom.begin(), pf);\n\telse\n\t\tm_Dom.push_back(pf); \n}\n\n//-----------------------------------------------------------------------------\nFEDomain* FECoreKernel::CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat)\n{\n\tfor (int i=0; i<(int)m_Dom.size(); ++i)\n\t{\n\t\tFEDomain* pdom = m_Dom[i]->CreateDomain(spec, pm, pmat);\n\t\tif (pdom != 0) return pdom;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nFEDomain* FECoreKernel::CreateDomainExplicit(int superClass, const char* sztype, FEModel* fem)\n{\n\tFEDomain* domain = (FEDomain*)Create(superClass, sztype, fem);\n\treturn domain;\n}\n\n//-----------------------------------------------------------------------------\nvoid FECoreKernel::ShowDeprecationWarnings(bool b)\n{\n\tm_bshowDeprecationWarning = b;\n}\n"
  },
  {
    "path": "FECore/FECoreKernel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreFactory.h\"\n#include \"ClassDescriptor.h\"\n#include <vector>\n#include <map>\n#include <string.h>\n#include <stdio.h>\n#include \"version.h\"\n\n//-----------------------------------------------------------------------------\n// Forward declarations\nclass FEModel;\nclass LinearSolver;\nclass FEModule;\n\n//-----------------------------------------------------------------------------\n//! This is the FECore kernel class that manages the interactions between the \n//! different modules. In particular, it manages the factory classes\n//! which are responsible for the creation of different classes that are registered\n//! with the kernel.\nclass FECORE_API FECoreKernel\n{\npublic:\n\t// Do not call this function from a plugin as it will not return the correct\n\t// instance. Instead, use the FECoreKernel object that is passed in the PluginInitialize method\n\tstatic FECoreKernel& GetInstance();\n\n\t// set the instance of the kernel\n\tstatic void SetInstance(FECoreKernel* pkernel);\n\npublic:\n\tstatic const char* SuperClassString(unsigned int sid);\n\n\tstatic std::map<unsigned int, const char*>\tGetSuperClassMap();\n\npublic:\n\t//! Register a class with the framework\n\tvoid RegisterFactory(FECoreFactory* ptf);\n\n\t//! Create a specific using a superclass ID and an alias\n\tFECoreBase* Create(int superClassID, const char* szalias, FEModel* pfem);\n\n\t//! Creat a specific class using a superclass ID, an alias and a module name\n\tFECoreBase* Create(int superClassID, const char* szalias, const char* szmod, FEModel* pfem);\n\n\t//! Create a class from its base class name and type string\n\tFECoreBase* Create(const char* baseClassName, const char* typeStr, FEModel* pfem);\n\n\t//! Create a specific class\n\tFECoreBase* CreateClass(const char* szclassName, FEModel* fem);\n\n\t//! Create a class from a class descriptor\n\tFECoreBase* Create(int superClassID, FEModel* pfem, const FEClassDescriptor& cd);\n\n\t//! count the number of registered classes with a given super-class id\n\tint Count(SUPER_CLASS_ID sid);\n\n\t//! List the registered classes with a given super-class id\n\tvoid List(SUPER_CLASS_ID sid);\n\n\t//! Get the number of registered factory classes\n\tint FactoryClasses();\n\n\t//! return a factory class\n\tconst FECoreFactory* GetFactoryClass(int i);\n\n\t//! return a factory class\n\tconst FECoreFactory* GetFactoryClass(int superClassID, int i);\n\n\t//! Get the index of a class factory (NOTE: this is a slow function!)\n\tint GetFactoryIndex(int superClassId, const char* sztype);\n\n\t//! find a factory class\n\tFECoreFactory* FindFactoryClass(int classID, const char* sztype);\n\n\t//! find a factory class (also match module name)\n\tFECoreFactory* FindFactoryClass(int classID, const char* sztype, const char* szmod);\n\n\t//! remove a factory class\n\tbool UnregisterFactory(FECoreFactory* ptf);\n\n\t//! unregister factories from allocator\n\tvoid UnregisterFactories(int alloc_id);\n\n    //! unregister modules from allocator\n\tvoid UnregisterModules(int alloc_id);\n\n\t//! set the current allocator ID\n\tvoid SetAllocatorID(int alloc_id);\n\n\t//! generate a allocator ID\n\tint GenerateAllocatorID();\n\n\tFECoreBase* CreateInstance(const FECoreFactory* fac, FEModel* fem);\n\n\tbool IsModuleActive(int moduleID);\n\npublic: // Modules\n\n\t//! count modules\n\tint Modules() const;\n\n\t//! Create a module (also makes it the active module)\n\tbool CreateModule(const char* szmodule, const char* description = nullptr);\n\tbool CreateModule(FEModule* pmodule, const char* szmodule, const char* description = nullptr);\n\n\t//! set the active module\n\tbool SetActiveModule(const char* szmodule);\n\tbool SetActiveModule(int moduleId);\n\n\t// return the active module's ID\n\tint GetActiveModuleID();\n\tFEModule* GetActiveModule();\n\n\t//! add module dependency to the active module\n\tbool AddModuleDependency(const char* szdep);\n\n\t//! Get a module's name\n\tconst char* GetModuleName(int i) const;\n\tconst char* GetModuleNameFromId(int id) const;\n\tconst char* GetModuleDescription(int i) const;\n\tint GetModuleStatus(int i) const;\n    int GetModuleAllocatorID(int i) const;\n\n\tint FindModuleID(const char* szmodule) const;\n\n\t//! Get a module's dependencies\n\tvector<int> GetModuleDependencies(int i) const;\n\n\t//! set the spec ID. Features with a matching spec ID will be preferred\n\t//! set spec ID to -1 to stop caring\n\tvoid SetSpecID(int nspec);\n\npublic:\n\t//! Register a new domain class\n\tvoid RegisterDomain(FEDomainFactory* pf, bool pushFront = false);\n\n\t//! Create a domain of a certain type (this uses the domain factories)\n\tFEDomain* CreateDomain(const FE_Element_Spec& spec, FEMesh* pm, FEMaterial* pmat);\n\n\t//! Create a domain of a certain type (this does not use the domain factories)\n\tFEDomain* CreateDomainExplicit(int superClass, const char* sztype, FEModel* fem);\n\npublic:\n\t//! set the default linear solver\n\tFECoreFactory* SetDefaultSolverType(const char* sztype);\n\n\tvoid SetDefaultSolver(FEClassDescriptor* linsolve);\n\n\t//! get the linear solver type\n\tconst char* GetLinearSolverType() const;\n\n\t//! Get a linear solver\n\tLinearSolver* CreateDefaultLinearSolver(FEModel* fem);\n\npublic:\n\tvoid ShowDeprecationWarnings(bool b);\n\nprivate:\n\tstd::vector<FECoreFactory*>\t\t\tm_Fac;\t// list of registered factory classes\n\tstd::vector<FEDomainFactory*>\t\tm_Dom;\t// list of domain factory classes\n\n\tbool\tm_bshowDeprecationWarning;\n\n\tstd::map<unsigned int, const char*>\tm_sidMap;\t// super class ID map\n\n\tstd::string\t\t\tm_default_solver_type;\t// default linear solver\n\tFEClassDescriptor*\tm_default_solver;\n\n\t// module list\n\tvector<FEModule*>\tm_modules;\n\tint\t\t\t\t\tm_activeModule;\n\n\tint\t\t\t\tm_nspec;\n\n\tint\t\tm_alloc_id;\t\t\t//!< current allocator ID\n\tint\t\tm_next_alloc_id;\t//!< next allocator ID\n\nprivate: // make singleton\n\tFECoreKernel();\n\tFECoreKernel(const FECoreKernel&){}\n\tvoid operator = (const FECoreKernel&){}\n\nprivate:\n\tstatic FECoreKernel* m_pKernel;\t// the one-and-only kernel object\n};\n\n//-----------------------------------------------------------------------------\n//! This class helps with the registration of a class with the framework\ntemplate <typename T> class FERegisterClass_T : public FECoreFactory\n{\npublic:\n\tFERegisterClass_T(SUPER_CLASS_ID sid, const char* szclass, const char* szbase, const char* szalias, int spec = -1) : FECoreFactory(sid, szclass, szbase, szalias, spec)\n\t{\n\t\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\t\tfecore.RegisterFactory(this);\n\t}\n\tFECoreBase* Create(FEModel* pfem) const { return new T(pfem); }\n};\n\n//-----------------------------------------------------------------------------\n// Register a factory class\n#define REGISTER_FECORE_FACTORY(theFactory) \\\n\tstatic theFactory _##theFactory##_rc;\n\n//-----------------------------------------------------------------------------\n// Register a class using default creation parameters\n#define REGISTER_FECORE_CLASS(theClass, ...) \\\n\tstatic FERegisterClass_T<theClass> _##theClass##_rc(theClass::superClassID(), #theClass, theClass::BaseClassName(), __VA_ARGS__);\n\n//-----------------------------------------------------------------------------\n// Register a class using default creation parameters\n#define REGISTER_FECORE_CLASS_EXPLICIT(theClass, theID, ...) \\\n\tstatic FERegisterClass_T<theClass> _##theClass##_rc(theID, #theClass, theClass::BaseClassName(), __VA_ARGS__);\n\n//-----------------------------------------------------------------------------\n// version for classes that require template arguments\n#define REGISTER_FECORE_CLASS_T(theClass, theArg, theName) \\\n\tstatic FERegisterClass_T<theClass<theArg> > _##theClass##theArg##_rc(theClass<theArg>::superClassID(), #theClass, theClass<theArg>::BaseClassName(), theName);\n\n//-----------------------------------------------------------------------------\n// version for classes that require template arguments\n#define REGISTER_FECORE_CLASS_T2(theClass, theArg1, theArg2, theName) \\\n\tstatic FERegisterClass_T<theClass<theArg1, theArg2> > _##theClass##theArg1##theArg2##_rc(theClass<theArg1, theArg2>::superClassID(), #theClass, theClass<theArg1, theArg2>::BaseClassName(), theName);\n\n//-----------------------------------------------------------------------------\n// Create an instance of a class.\n// This assumes that TBase is derived from FECoreBase and defines a class ID. \ntemplate <typename TBase> inline TBase* fecore_new(const char* sztype, FEModel* pfem)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\treturn static_cast<TBase*>(fecore.Create(TBase::superClassID(), sztype, pfem));\n//\treturn static_cast<TBase*>(fecore.Create(TBase::BaseClassName(), sztype, pfem));\n}\n\n//-----------------------------------------------------------------------------\n// Create an instance of a class in a particulare module.\n// This assumes that TBase is derived from FECoreBase and defines a class ID. \ntemplate <typename TBase> inline TBase* fecore_new_ex(const char* sztype, const char* szmod, FEModel* pfem)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\treturn static_cast<TBase*>(fecore.Create(TBase::superClassID(), sztype, szmod, pfem));\n}\n\n//-----------------------------------------------------------------------------\n// Create an instance of a class.\n// This assumes that TBase is derived from FECoreBase and defines a class ID. \ntemplate <typename TBase> inline TBase* fecore_new(int classIndex, FEModel* pfem)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tconst FECoreFactory* f = fecore.GetFactoryClass(TBase::superClassID(), classIndex);\n\tif (f) return static_cast<TBase*>(f->Create(pfem));\n\telse return nullptr;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <typename TClass> inline TClass* fecore_new_class(const char* szclass, FEModel* fem)\n{\n\tint superId = TClass::superClassID();\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\treturn static_cast<TClass*>(fecore.CreateClass(szclass, fem));\n}\n\n//-----------------------------------------------------------------------------\n#define fecore_alloc(theClass, fem) fecore_new_class<theClass>(#theClass, fem)\n\n//-----------------------------------------------------------------------------\n// Three-parameter form of the fecore_new function for situations where the base class does not \n// define the classID value.\ntemplate <typename TBase> inline TBase* fecore_new(int sid, const char* sztype, FEModel* pfem)\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\treturn static_cast<TBase*>(fecore.Create(sid, sztype, pfem));\n}\n\n//=============================================================================\n// TODO: Move all this stuff to sdk.h\n\n//-----------------------------------------------------------------------------\n// Template class for factory classes for plugins\ntemplate <typename T, SUPER_CLASS_ID sid> class FEPluginFactory_T : public FECoreFactory\n{\npublic:\n\tFEPluginFactory_T(const char* sz) : FECoreFactory(sid, nullptr, sz, nullptr){}\n\tFECoreBase* Create(FEModel* pfem) const { return new T(pfem); }\n};\n\n//------------------------------------------------------------------------------\n// This is for functions exported from a plugin\n#ifdef WIN32\n#define FECORE_EXPORT extern \"C\" __declspec(dllexport)\n#else\n#define FECORE_EXPORT extern \"C\"\n#endif\n"
  },
  {
    "path": "FECore/FECorePlot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECorePlot.h\"\n#include \"FEMaterial.h\"\n#include \"FESolidDomain.h\"\n#include \"FEModelParam.h\"\n#include \"FEBodyLoad.h\"\n#include \"FENodalLoad.h\"\n#include \"FEPlotData.h\"\n#include \"FESurface.h\"\n#include \"FEMaterialPointProperty.h\"\n#include \"writeplot.h\"\n#include \"FESurfaceLoad.h\"\n#include \"FEDomainMap.h\"\n#include \"FEModel.h\"\n#include \"FEPIDController.h\"\n#include \"FEDataMap.h\"\n\n//-----------------------------------------------------------------------------\nFEPlotParameter::FEPlotParameter(FEModel* pfem) : FEPlotData(pfem)\n{\n\tm_index = 0; \n\tSetStorageFormat(FMT_MULT);\n\n\tm_mat = 0;\n\tm_dom = 0;\n\tm_surf = 0;\n}\n\n//-----------------------------------------------------------------------------\n// This plot field requires a filter which defines the material name and \n// the material parameter in the format [materialname.parametername].\nbool FEPlotParameter::SetFilter(const char* sz)\n{\n\t// store the filter for serialization\n\tm_filter = sz;\n\n\t// find the parameter\n\tParamString ps(sz);\n\tm_param = GetFEModel()->GetParameterValue(ps);\n\tif (m_param.isValid() == false) return false;\n\n\tFEParam* param = m_param.param();\n\tif (param == 0) return false;\n\n\tFECoreBase* pc = dynamic_cast<FECoreBase*>(param->parent());\n\tif (pc == nullptr) return false;\n\n\tswitch (m_param.type())\n\t{\n\tcase FE_PARAM_DOUBLE_MAPPED:\n\t{\n\t\tFEParamDouble& p = m_param.value<FEParamDouble>();\n\n\t\t// check for materials first\n\t\tif (dynamic_cast<FEMaterial*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_mat = dynamic_cast<FEMaterial*>(pc); m_mat = dynamic_cast<FEMaterial*>(m_mat->GetAncestor()); }\n\t\telse \n\t\t{\n\t\t\tFEItemList* itemList = p.GetItemList();\n\t\t\tif (itemList == 0)\n\t\t\t{\n\t\t\t\t// for some classes, the item list can be empty\n\t\t\t\tif (dynamic_cast<FEBodyLoad*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEBodyLoad*>(pc))->GetDomainList(); }\n\t//\t\t\telse if (dynamic_cast<FESurfaceLoad*>(pc)) { SetRegionType(FE_REGION_SURFACE); m_surf = dynamic_cast<FESurfaceLoad*>(pc)->GetSurface().GetFacetSet(); }\n\t\t\t\telse if (dynamic_cast<FENodalLoad*>(pc)) SetRegionType(FE_REGION_NODE);\n\t\t\t\telse return false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif      (dynamic_cast<FENodeSet*>(itemList)) SetRegionType(FE_REGION_NODE);\n\t\t\t\telse if (dynamic_cast<FEFacetSet*>(itemList)) { SetRegionType(FE_REGION_SURFACE); m_surf = dynamic_cast<FEFacetSet*>(itemList); }\n\t\t\t\telse if (dynamic_cast<FEElementSet*>(itemList)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEElementSet*>(itemList))->GetDomainList(); }\n\t\t\t\telse return false;\n\t\t\t}\n\t\t}\n\n\t\tSetVarType(PLT_FLOAT);\n\n\t\tFEMappedValue* mapval = dynamic_cast<FEMappedValue*>(p.valuator());\n\t\tif (mapval)\n\t\t{\n\t\t\tFEDomainMap* map = dynamic_cast<FEDomainMap*>(mapval->dataMap()); assert(map);\n\t\t\tif (map->StorageFormat() == FMT_ITEM) SetStorageFormat(FMT_ITEM);\n\t\t}\n\t}\n\tbreak;\n\tcase FE_PARAM_VEC3D_MAPPED:\n\t{\n\t\tFEParamVec3& p = m_param.value<FEParamVec3>();\n\n\t\tFEItemList* itemList = p.GetItemList();\n\t\tif (itemList == 0)\n\t\t{\n\t\t\t// for material parameters, the item list can be empty\n\t\t\tif      (dynamic_cast<FEMaterial*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_mat = dynamic_cast<FEMaterial*>(pc); m_mat = dynamic_cast<FEMaterial*>(m_mat->GetAncestor()); }\n\t\t\telse if (dynamic_cast<FEBodyLoad*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEBodyLoad*>(pc))->GetDomainList(); }\n\t\t\telse return false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (dynamic_cast<FENodeSet*>(itemList)) SetRegionType(FE_REGION_NODE);\n\t\t\telse if (dynamic_cast<FEFacetSet*>(itemList)) { SetRegionType(FE_REGION_SURFACE); m_surf = dynamic_cast<FEFacetSet*>(itemList); }\n\t\t\telse if (dynamic_cast<FEElementSet*>(itemList)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEElementSet*>(itemList))->GetDomainList(); }\n\t\t\telse return false;\n\t\t}\n\n\t\tSetVarType(PLT_VEC3F);\n\t}\n\tbreak;\n\tcase FE_PARAM_MAT3D_MAPPED:\n\t{\n\t\tFEParamMat3d& p = m_param.value<FEParamMat3d>();\n\n\t\tFEItemList* itemList = p.GetItemList();\n\t\tif (itemList == 0)\n\t\t{\n\t\t\t// for material parameters, the item list can be empty\n\t\t\tif      (dynamic_cast<FEMaterial*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_mat = dynamic_cast<FEMaterial*>(pc); m_mat = dynamic_cast<FEMaterial*>(m_mat->GetAncestor()); }\n\t\t\telse if (dynamic_cast<FEBodyLoad*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEBodyLoad*>(pc))->GetDomainList(); }\n\t\t\telse return false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (dynamic_cast<FENodeSet*>(itemList)) SetRegionType(FE_REGION_NODE);\n\t\t\telse if (dynamic_cast<FEFacetSet*>(itemList)) { SetRegionType(FE_REGION_SURFACE); m_surf = dynamic_cast<FEFacetSet*>(itemList); }\n\t\t\telse if (dynamic_cast<FEElementSet*>(itemList)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEElementSet*>(itemList))->GetDomainList(); }\n\t\t\telse return false;\n\t\t}\n\n\t\tSetStorageFormat(FMT_ITEM);\n\t\tSetVarType(PLT_MAT3F);\n\t}\n\tbreak;\n\tcase FE_PARAM_MAT3DS_MAPPED:\n\t{\n\t\tFEParamMat3ds& p = m_param.value<FEParamMat3ds>();\n\n\t\tFEItemList* itemList = p.GetItemList();\n\t\tif (itemList == 0)\n\t\t{\n\t\t\t// for material parameters, the item list can be empty\n\t\t\tif      (dynamic_cast<FEMaterial*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_mat = dynamic_cast<FEMaterial*>(pc); m_mat = dynamic_cast<FEMaterial*>(m_mat->GetAncestor()); }\n\t\t\telse if (dynamic_cast<FEBodyLoad*>(pc)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEBodyLoad*>(pc))->GetDomainList(); }\n\t\t\telse return false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif      (dynamic_cast<FENodeSet*>(itemList)) SetRegionType(FE_REGION_NODE);\n\t\t\telse if (dynamic_cast<FEFacetSet*>(itemList)) { SetRegionType(FE_REGION_SURFACE); m_surf = dynamic_cast<FEFacetSet*>(itemList); }\n\t\t\telse if (dynamic_cast<FEElementSet*>(itemList)) { SetRegionType(FE_REGION_DOMAIN); m_dom = &(dynamic_cast<FEElementSet*>(itemList))->GetDomainList(); }\n\t\t\telse return false;\n\t\t}\n\n\t\tSetStorageFormat(FMT_ITEM);\n\t\tSetVarType(PLT_MAT3FS);\n\t}\n\tbreak;\n\tcase FE_PARAM_DOUBLE:\n\t{\n\t\tif (dynamic_cast<FEMaterial*>(pc))\n\t\t{\n\t\t\tm_mat = dynamic_cast<FEMaterial*>(pc->GetAncestor());\n\t\t\tSetRegionType(FE_REGION_DOMAIN);\n\t\t\tSetStorageFormat(FMT_REGION);\n\t\t}\n\t\telse if (dynamic_cast<FESurfaceLoad*>(pc))\n\t\t{\n\t\t\tFESurfaceLoad* psl = dynamic_cast<FESurfaceLoad*>(pc);\n\t\t\tSetRegionType(FE_REGION_SURFACE);\n\t\t\tSetStorageFormat(FMT_REGION);\n\t\t\tm_surf = psl->GetSurface().GetFacetSet();\n\t\t}\n\t\telse return false;\n\t}\n\tbreak;\n\tcase FE_PARAM_VEC3D:\n\t{\n\t\tif (dynamic_cast<FEMaterial*>(pc))\n\t\t{\n\t\t\tm_mat = dynamic_cast<FEMaterial*>(pc->GetAncestor());\n\t\t\tSetRegionType(FE_REGION_DOMAIN);\n\t\t\tSetStorageFormat(FMT_REGION);\n\t\t}\n\t\telse if (dynamic_cast<FEBodyLoad*>(pc))\n\t\t{\n\t\t\tSetRegionType(FE_REGION_DOMAIN);\n\t\t\tSetStorageFormat(FMT_REGION);\n\t\t\tm_dom = &(dynamic_cast<FEBodyLoad*>(pc))->GetDomainList();\n\t\t}\n\t\telse if (dynamic_cast<FESurfaceLoad*>(pc))\n\t\t{\n\t\t\tFESurfaceLoad* psl = dynamic_cast<FESurfaceLoad*>(pc);\n\t\t\tSetRegionType(FE_REGION_SURFACE);\n\t\t\tSetStorageFormat(FMT_REGION);\n\t\t\tm_surf = psl->GetSurface().GetFacetSet();\n\t\t}\n\t\telse return false;\n\n\t\tSetVarType(PLT_VEC3F);\n\t}\n\tbreak;\n\tcase FE_PARAM_MATERIALPOINT:\n\t{\n\t\tFEMaterialPointProperty& prop = m_param.value<FEMaterialPointProperty>();\n\t\tm_mat = dynamic_cast<FEMaterial*>(pc->GetAncestor());\n\t\tif (m_mat == nullptr) return false;\n\n\t\tSetRegionType(FE_REGION_DOMAIN);\n\n\t\tswitch (prop.dataType())\n\t\t{\n\t\tcase FE_DOUBLE: SetVarType(PLT_FLOAT); break;\n\t\tcase FE_VEC3D: SetVarType(PLT_VEC3F); break;\n\t\tcase FE_MAT3D: SetVarType(PLT_MAT3F); break;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t}\n\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\treturn false;\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotParameter::Serialize(DumpStream& ar)\n{\n\tFEPlotData::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t\tar << m_filter;\n\telse\n\t{\n\t\tstring filter;\n\t\tar >> filter;\n\t\tSetFilter(filter.c_str());\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// The Save function stores the material parameter data to the plot file.\nbool FEPlotParameter::Save(FEDomain& dom, FEDataStream& a)\n{\n\tif (m_param.isValid() == false) return false;\n\n\tFEParam* param = m_param.param();\n\n\tif ((m_param.type() == FE_PARAM_DOUBLE_MAPPED) ||\n\t\t(m_param.type() == FE_PARAM_VEC3D_MAPPED ) ||\n\t\t(m_param.type() == FE_PARAM_MAT3D_MAPPED ) ||\n\t\t(m_param.type() == FE_PARAM_MAT3DS_MAPPED))\n\t{\n\t\tFEModelParam& map = m_param.value<FEModelParam>();\n\n\t\tif (m_dom == nullptr)\n\t\t{\n\t\t\tif ((m_mat == nullptr) || (dom.GetMaterial() != m_mat))\n\t\t\t\treturn false;\n\t\t}\n\t\telse if (m_dom->IsMember(&dom) == false) return false;\n\n\t\tif (m_param.type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t{\n\t\t\tFEParamDouble& mapDouble = dynamic_cast<FEParamDouble&>(map);\n\n\t\t\tFEMappedValue* val = dynamic_cast<FEMappedValue*>(mapDouble.valuator());\n\t\t\tif (val)\n\t\t\t{\n\t\t\t\tFEDomainMap* map = dynamic_cast<FEDomainMap*>(val->dataMap()); assert(map);\n\t\t\t\tif (map->StorageFormat() == FMT_MULT)\n\t\t\t\t{\n\t\t\t\t\t// loop over all elements\n\t\t\t\t\tint NE = dom.Elements();\n\t\t\t\t\tfor (int i = 0; i < NE; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEElement& e = dom.ElementRef(i);\n\t\t\t\t\t\tint ne = e.Nodes();\n\n\t\t\t\t\t\tconst FEElementSet* elset = map->GetElementSet();\n\t\t\t\t\t\tint n = elset->GetLocalIndex(e);\n\t\t\t\t\t\tvector<double> sn(ne, 0.0);\n\t\t\t\t\t\tif (n >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsn[j] = map->value<double>(n, j);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// push data to archive\n\t\t\t\t\t\tfor (int j = 0; j < ne; ++j) a << sn[j];\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\telse if (map->StorageFormat() == FMT_ITEM)\n\t\t\t\t{\n\t\t\t\t\tassert(StorageFormat() == FMT_ITEM);\n\t\t\t\t\t// loop over all elements\n\t\t\t\t\tint NE = dom.Elements();\n\t\t\t\t\tconst FEElementSet* elset = map->GetElementSet();\n\t\t\t\t\tfor (int i = 0; i < NE; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\t\tint n = elset->GetLocalIndex(el);\n\t\t\t\t\t\tif (n < 0) a << 0.0;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\ta << map->get<double>(n);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\twriteNodalProjectedElementValues(dom, a, mapDouble);\n\t\t}\n\t\telse if (m_param.type() == FE_PARAM_VEC3D_MAPPED)\n\t\t{\n\t\t\tFEParamVec3& mapVec3 = dynamic_cast<FEParamVec3&>(map);\n\t\t\twriteNodalProjectedElementValues<vec3d>(dom, a, mapVec3);\n\t\t}\n\t\telse if (m_param.type() == FE_PARAM_MAT3D_MAPPED)\n\t\t{\n\t\t\tFEParamMat3d& mapMat3 = dynamic_cast<FEParamMat3d&>(map);\n\t\t\twriteElementValue<mat3d>(dom, a, mapMat3);\n\t\t}\n\t\telse if (m_param.type() == FE_PARAM_MAT3DS_MAPPED)\n\t\t{\n\t\t\tFEParamMat3ds& mapMat3 = dynamic_cast<FEParamMat3ds&>(map);\n\t\t\twriteElementValue<mat3ds>(dom, a, mapMat3);\n\t\t}\n\t\telse return false;\n\n\t\treturn true;\n\t}\n\telse if (m_param.type() == FE_PARAM_DOUBLE)\n\t{\n\t\tif (dom.GetMaterial() == m_mat)\n\t\t{\n\t\t\tdouble val = m_param.value<double>();\n\t\t\ta << val;\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (m_param.type() == FE_PARAM_VEC3D)\n\t{\n\t\tif (m_dom && (m_dom->IsMember(&dom)))\n\t\t{\n\t\t\tvec3d val = m_param.value<vec3d>();\n\t\t\ta << val;\n\t\t\treturn true;\n\t\t}\n\t\telse if (dom.GetMaterial() == m_mat)\n\t\t{\n\t\t\tvec3d val = m_param.value<vec3d>();\n\t\t\ta << val;\n\t\t\treturn true;\n\t\t}\n\t\telse return false;\n\t}\n\telse if (m_param.type() == FE_PARAM_MATERIALPOINT)\n\t{\n\t\tif (dom.GetMaterial() == m_mat)\n\t\t{\n\t\t\tFEMaterialPointProperty& prop = m_param.value<FEMaterialPointProperty>();\n\n\t\t\tswitch (prop.dataType())\n\t\t\t{\n\t\t\tcase FE_DOUBLE: writeNodalProjectedElementValues<double>(dom, a, [&](const FEMaterialPoint& mp) {\n\t\t\t\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\t\t\t\tdouble d;\n\t\t\t\t\tprop.get(mp_noconst, d);\n\t\t\t\t\treturn d;\n\t\t\t\t});\t\n\t\t\t\tbreak;\n\t\t\tcase FE_VEC3D: writeNodalProjectedElementValues<vec3d>(dom, a, [&](const FEMaterialPoint& mp) {\n\t\t\t\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\t\t\t\tvec3d d;\n\t\t\t\t\tprop.get(mp_noconst, d);\n\t\t\t\t\treturn d;\n\t\t\t\t});\t\n\t\t\t\tbreak;\n\t\t\tcase FE_MAT3D: writeNodalProjectedElementValues<mat3d>(dom, a, [&](const FEMaterialPoint& mp) {\n\t\t\t\t\tFEMaterialPoint& mp_noconst = const_cast<FEMaterialPoint&>(mp);\n\t\t\t\t\tmat3d d;\n\t\t\t\t\tprop.get(mp_noconst, d);\n\t\t\t\t\treturn d;\n\t\t\t\t});\t\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// The Save function stores the material parameter data to the plot file.\nbool FEPlotParameter::Save(FESurface& dom, FEDataStream& a)\n{\n\tif (m_param.isValid() == false) return false;\n\n\tFEFacetSet* surf = dom.GetFacetSet();\n\tif (m_surf != surf) return false;\n\n\tif (m_param.type() == FE_PARAM_DOUBLE_MAPPED)\n\t{\n\t\tFEParamDouble& map = m_param.value<FEParamDouble>();\n\t\twriteNodalProjectedElementValues<double>(dom, a, map);\n\t}\n\telse if (m_param.type() == FE_PARAM_VEC3D_MAPPED)\n\t{\n\t\tFEParamVec3& map = m_param.value<FEParamVec3>();\n\t\twriteNodalProjectedElementValues<vec3d>(dom, a, map);\n\t}\n\telse if (m_param.type() == FE_PARAM_DOUBLE)\n\t{\n\t\ta << m_param.value<double>();\n\t}\n\telse if (m_param.type() == FE_PARAM_VEC3D)\n\t{\n\t\ta << m_param.value<vec3d>();\n\t}\n\telse return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotParameter::Save(FEMesh& mesh, FEDataStream& a)\n{\n\tif (m_param.isValid() == false) return false;\n\n\tif (m_param.type() == FE_PARAM_DOUBLE_MAPPED)\n\t{\n\t\tFEParamDouble& map = m_param.value<FEParamDouble>();\n\t\tFENodeSet* nset = dynamic_cast<FENodeSet*>(map.GetItemList());\n\t\tif (nset)\n\t\t\twriteNodalValues<double>(*nset, a, map);\n\t\telse\n\t\t{\n\t\t\twriteNodalValues<double>(mesh, a, [&](const FENode& node) {\n\t\t\t\tFEMaterialPoint mp;\n\t\t\t\tmp.m_r0 = node.m_r0;\n\t\t\t\tmp.m_index = -1;\n\t\t\t\tdouble v = map(mp);\n\t\t\t\treturn v;\n\t\t\t});\n\t\t}\n\n\t\treturn true;\n\t}\n\telse if (m_param.type() == FE_PARAM_VEC3D_MAPPED)\n\t{\n\t\tFEParamVec3& map = m_param.value<FEParamVec3>();\n\t\tFENodeSet* nset = dynamic_cast<FENodeSet*>(map.GetItemList());\n\t\tif (nset == 0) return false;\n\n\t\t// write the nodal values\n\t\twriteNodalValues<vec3d>(*nset, a, map);\n\n\t\treturn true;\n\t}\n\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nFEPlotPIDController::FEPlotPIDController(FEModel* pfem) : FEPlotGlobalData(pfem, PLT_ARRAY)\n{\n\tm_pid = nullptr;\n\tSetArraySize(3);\n\tstd::vector<std::string> names;\n\tnames.push_back(\"measurement\");\n\tnames.push_back(\"error\");\n\tnames.push_back(\"output\");\n\tSetArrayNames(names);\n}\n\nbool FEPlotPIDController::SetFilter(const char* sz)\n{\n\tif (sz == nullptr) return false;\n\tFEModel* fem = GetFEModel(); assert(fem);\n\tif (fem == nullptr) return false;\n\n\tfor (int i = 0; i < fem->LoadControllers(); ++i)\n\t{\n\t\tFEPIDController* pid = dynamic_cast<FEPIDController*>(fem->GetLoadController(i));\n\t\tif (pid && (pid->GetName() == string(sz)))\n\t\t{\n\t\t\tm_pid = pid;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tm_pid = nullptr;\n\treturn false;\n}\n\nbool FEPlotPIDController::Save(FEDataStream& a)\n{\n\tif (m_pid == nullptr) return false;\n\ta << m_pid->GetParameterValue();\n\ta << m_pid->GetError();\n\ta << m_pid->Value();\n\treturn true;\n}\n\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEPlotMeshData::FEPlotMeshData(FEModel* pfem) : FEPlotData(pfem)\n{\n\tm_map = nullptr;\n\tm_dom = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// This plot field requires a filter which defines the material name and \n// the material parameter in the format [materialname.parametername].\nbool FEPlotMeshData::SetFilter(const char* sz)\n{\n\t// store the filter for serialization\n\tm_filter = sz;\n\n\t// find the map\n\tm_map = GetFEModel()->GetMesh().FindDataMap(sz);\n\tif (m_map == nullptr) return false;\n\n\tswitch (m_map->DataMapType())\n\t{\n\tcase FE_DOMAIN_MAP:\n\t{\n\t\tFEDomainMap* map = dynamic_cast<FEDomainMap*>(m_map); assert(map);\n\t\tFEDataType dataType = map->DataType();\n\n\t\tSetRegionType(FE_REGION_DOMAIN);\n\t\tSetStorageFormat((Storage_Fmt)map->StorageFormat());\n\n\t\tif (map->StorageFormat() == FMT_MATPOINTS) SetStorageFormat(FMT_MULT);\n\t\t\n\t\tswitch (dataType)\n\t\t{\n\t\tcase FE_DOUBLE: SetVarType(PLT_FLOAT); break;\n\t\tcase FE_VEC3D : SetVarType(PLT_VEC3F); break;\n\t\tcase FE_MAT3D : SetVarType(PLT_MAT3F); break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn false;\n\t\t}\n\n\t\tconst FEElementSet* set = map->GetElementSet();\n\t\tif (set == nullptr) return false;\n\n\t\tm_dom = &(set->GetDomainList());\n\t}\n\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotMeshData::Serialize(DumpStream& ar)\n{\n\tFEPlotData::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t\tar << m_filter;\n\telse\n\t{\n\t\tstring filter;\n\t\tar >> filter;\n\t\tSetFilter(filter.c_str());\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// The Save function stores the material parameter data to the plot file.\nbool FEPlotMeshData::Save(FEDomain& dom, FEDataStream& a)\n{\n\tFEDomainMap* map = dynamic_cast<FEDomainMap*>(m_map);\n\tif (map == nullptr) return false;\n\tif (m_dom == nullptr) return false;\n\n\tif (m_dom->IsMember(&dom))\n\t{\n\t\tif (StorageFormat() == FMT_NODE)\n\t\t{\n\t\t\tif (DataType() == PLT_FLOAT)\n\t\t\t{\n\t\t\t\tint n = dom.Nodes();\n\t\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t\t{\n\t\t\t\t\tint m = dom.NodeIndex(i);\n\t\t\t\t\tdouble v = map->NodalValue(m);\n\t\t\t\t\ta << v;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if (StorageFormat() == FMT_ITEM)\n\t\t{\n\t\t\tif (DataType() == PLT_FLOAT)\n\t\t\t{\n\t\t\t\tint n = dom.Elements();\n\t\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tFEMaterialPoint mp;\n\t\t\t\t\tmp.m_elem = &el;\n\t\t\t\t\tmp.m_index = 0;\n\t\t\t\t\tdouble v = map->value(mp);\n\t\t\t\t\ta << v;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if (StorageFormat() == FMT_MULT)\n\t\t{\n\t\t\tif (DataType() == PLT_FLOAT)\n\t\t\t{\n\t\t\t\tint n = dom.Elements();\n\t\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble v = map->value<double>(i, j);\n\t\t\t\t\t\ta << v;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if (DataType() == PLT_VEC3F)\n\t\t\t{\n\t\t\t\tint n = dom.Elements();\n\t\t\t\tfor (int i = 0; i < n; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3d v = map->value<vec3d>(i, j);\n\t\t\t\t\t\ta << v;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if (map->StorageFormat() == FMT_MATPOINTS)\n\t\t{\n\t\t\t// Note that for this map type, the plot format was changed to FMT_MULT\n\t\t\tif (DataType() == PLT_FLOAT)\n\t\t\t{\n\t\t\t\tint NE = dom.Elements();\n\t\t\t\tdouble vi[FEElement::MAX_INTPOINTS] = { 0 };\n\t\t\t\tdouble vn[FEElement::MAX_NODES] = { 0 };\n\t\t\t\tfor (int i = 0; i < NE; ++i)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint mp;\n\t\t\t\t\t\tmp.m_elem = &el;\n\t\t\t\t\t\tmp.m_index = j;\n\t\t\t\t\t\tvi[j] = map->value(mp);\n\t\t\t\t\t}\n\n\t\t\t\t\tel.project_to_nodes(vi, vn);\n\t\t\t\t\tfor (int j=0; j<el.Nodes(); ++j)\n\t\t\t\t\t\ta << vn[j];\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// The Save function stores the material parameter data to the plot file.\nbool FEPlotMeshData::Save(FESurface& dom, FEDataStream& a)\n{\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPlotMeshData::Save(FEMesh& mesh, FEDataStream& a)\n{\n\treturn false;\n}\n\nFEPlotFieldVariable::FEPlotFieldVariable(FEModel* pfem) : FEPlotNodeData(pfem, Var_Type::PLT_FLOAT, Storage_Fmt::FMT_ITEM)\n{\n\n}\n\nbool FEPlotFieldVariable::SetFilter(const char* sz)\n{\n\tDOFS& dofs = GetFEModel()->GetDOFS();\n\tint nvar = dofs.GetVariableIndex(sz);\n\tif (nvar == -1) return false;\n\tdofs.GetDOFList(sz, m_dofs);\n\tint vartype = dofs.GetVariableType(nvar);\n\tswitch (vartype)\n\t{\n\tcase VAR_SCALAR: SetVarType(PLT_FLOAT); break;\n\tcase VAR_VEC3  : SetVarType(PLT_VEC3F); break;\n\tcase VAR_ARRAY :\n\t{\n\t\tSetVarType(Var_Type::PLT_ARRAY);\n\t\tSetArraySize((int)m_dofs.size());\n\t\tvector<string> dofNames;\n\t\tfor (int i = 0; i < m_dofs.size(); ++i) dofNames.push_back(dofs.GetDOFName(nvar, i));\n\t\tSetArrayNames(dofNames);\n\t}\n\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nbool FEPlotFieldVariable::Save(FEMesh& mesh, FEDataStream& a)\n{\n\tif (m_dofs.empty()) return false;\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tfor (int j = 0; j < m_dofs.size(); ++j)\n\t\t{\n\t\t\ta << node.get(m_dofs[j]);\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid FEPlotFieldVariable::Serialize(DumpStream& ar)\n{\n\tFEPlotNodeData::Serialize(ar);\n\tar & m_dofs;\n}\n"
  },
  {
    "path": "FECore/FECorePlot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEPlotData.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMaterial;\nclass FEDomainList;\nclass FEFacetSet;\nclass FEDataMap;\n\n//-----------------------------------------------------------------------------\n// class for exporting element specific material parameters to plot file\nclass FECORE_API FEPlotParameter : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotParameter)\n\npublic:\n\tFEPlotParameter(FEModel* pfem);\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\n\tbool Save(FESurface& dom, FEDataStream& a) override;\n\tbool Save(FEMesh& mesh, FEDataStream& a) override;\n\n\tbool SetFilter(const char* sz) override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tFEParamValue\tm_param;\t//!< parameter\n\tint\t\t\t\tm_index;\t//!< index for array parameters\n\nprivate:\n\tFEMaterial*\t\t\tm_mat;\n\tFEDomainList*\t\tm_dom;\n\tFEFacetSet*\t\t\tm_surf;\n\n\tstd::string\t\tm_filter;\n};\n\n//-----------------------------------------------------------------------------\nclass FEPIDController;\n\nclass FECORE_API FEPlotPIDController : public FEPlotGlobalData\n{\npublic:\n\tFEPlotPIDController(FEModel* pfem);\n\tbool SetFilter(const char* sz) override;\n\tbool Save(FEDataStream& a) override;\n\nprivate:\n\tFEPIDController* m_pid;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEPlotMeshData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotMeshData)\n\npublic:\n\tFEPlotMeshData(FEModel* pfem);\n\tbool Save(FEDomain& dom, FEDataStream& a) override;\n\tbool Save(FESurface& dom, FEDataStream& a) override;\n\tbool Save(FEMesh& mesh, FEDataStream& a) override;\n\n\tbool SetFilter(const char* sz) override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tFEDataMap*\tm_map;\t\t//!< parameter\n\n\tconst FEDomainList*\tm_dom;\n\tstd::string\t\tm_filter;\n};\n\n//! Plot variable for field variables\nclass FECORE_API FEPlotFieldVariable : public FEPlotNodeData\n{\npublic:\n\tFEPlotFieldVariable(FEModel* pfem);\n\tbool Save(FEMesh& mesh, FEDataStream& a) override;\n\n\tbool SetFilter(const char* sz) override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tstd::vector<int>\tm_dofs;\n};\n"
  },
  {
    "path": "FECore/FECoreTask.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECoreTask.h\"\n\n//-----------------------------------------------------------------------------\nFECoreTask::FECoreTask(FEModel* fem) : FECoreBase(fem) \n{\n}\n\n//-----------------------------------------------------------------------------\nFECoreTask::~FECoreTask(void)\n{\n}\n"
  },
  {
    "path": "FECore/FECoreTask.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// The FECoreTask class is the base class for all tasks.\n// A task is simply the highest level module which defines what the code will do.\nclass FECORE_API FECoreTask : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FETASK_ID)\n\tFECORE_BASE_CLASS(FECoreTask)\n\npublic:\n\tFECoreTask(FEModel* fem);\n\tvirtual ~FECoreTask(void);\n\n\t//! initialize the task\n\t//! make sure to call FEModel::Init at some point\n\tvirtual bool Init(const char* szfile) = 0;\n\n\t//! Run the task.\n\tvirtual bool Run() = 0;\n};\n"
  },
  {
    "path": "FECore/FECube.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FECube.h\"\n#include \"FESurface.h\"\n#include \"FEModel.h\"\n\nFECube::FECube() : m_mesh(0)\n{\n}\n\nFECube::~FECube()\n{\n\tfor (int i = 0; i<6; ++i) \n\t{\n\t\tdelete m_surf[i];\n\t\tm_surf[i] = 0;\n\t}\n}\n\n// Get a surface\nFESurface* FECube::GetSurface(int i)\n{\n\treturn m_surf[i];\n}\n\n// get the node set of the corner nodes\nconst FENodeSet& FECube::GetCornerNodes() const\n{\n\treturn *m_corners;\n}\n\n// get the node set of boundary nodes\nconst FENodeSet& FECube::GetBoundaryNodes() const\n{\n\treturn *m_boundary;\n}\n\n// get the mesh of this cube\nFEMesh* FECube::GetMesh()\n{\n\treturn m_mesh;\n}\n\nbool FECube::Build(FEModel* fem)\n{\n\t// make sure we have a mesh\n\tm_mesh = &fem->GetMesh();\n\n\tFEMesh& m = *m_mesh;\n\tint NN = m.Nodes();\n\t\n\t// first, get the outside surface\n\tFESurface* boundary = m.ElementBoundarySurface();\n\n\t// get the boundary node set\n\tm_boundary = new FENodeSet(fem);\n\tm_boundary->Add(boundary->GetNodeList());\n\n\t// Next, split it up in 6 surfaces\n\t// We divide the surface by comparing normals to the 6 surface normals of a cube\n\tvec3d fn[6] = { { 1, 0, 0 }, { -1, 0, 0 }, { 0, 1, 0 }, { 0, -1, 0 }, { 0, 0, 1 }, { 0, 0, -1 } };\n\tfor (int n = 0; n<6; ++n)\n\t{\n\t\t// create the surface\n\t\tm_surf[n] = fecore_alloc(FESurface, m_mesh->GetFEModel());\n\n\t\t// get the normal for this face\n\t\tvec3d N = fn[n];\n\n\t\tint faces = 0;\n\t\tfor (int i = 0; i<boundary->Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& face = boundary->Element(i);\n\t\t\tvec3d Ni = boundary->SurfaceNormal(face, 0, 0);\n\t\t\tif (Ni*N > 0.9999) faces++;\n\t\t}\n\t\tm_surf[n]->Create(faces);\n\n\t\tfaces = 0;\n\t\tfor (int i = 0; i<boundary->Elements(); ++i)\n\t\t{\n\t\t\tFESurfaceElement& face = boundary->Element(i);\n\t\t\tvec3d Ni = boundary->SurfaceNormal(face, 0, 0);\n\t\t\tif (Ni*N > 0.9999)\n\t\t\t{\n\t\t\t\tFESurfaceElement& newFace = m_surf[n]->Element(faces++);\n\t\t\t\tnewFace = face;\n\t\t\t}\n\t\t}\n\n\t\tm_surf[n]->Init();\n\t}\n\n\t// we also need to find the 8 corner nodes\n\tvector<int> tag(NN, 0);\n\tfor (int n = 0; n<6; ++n)\n\t{\n\t\tFESurface& sn = *m_surf[n];\n\t\tFENodeList ns = sn.GetNodeList();\n\t\tfor (int i = 0; i<ns.Size(); ++i) tag[ns[i]]++;\n\t}\n\n\tm_corners = new FENodeSet(fem);\n\tfor (int i = 0; i<NN; ++i) if (tag[i] == 3) m_corners->Add(i);\n\tassert(m_corners->Size() == 8);\n\n\t// don't forget to cleanup\n\tdelete boundary;\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FECube.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\n// This class tries to identify surfaces, edges, and corner nodes on a mesh, \n// assuming that it is a cube.\n// The surfaces are ordered as follows:\n// 1: +X, 2: -X, 3: +Y, 4: -Y, 5: +Z, 6: -Z\nclass FECORE_API FECube\n{\npublic:\n\t// constructor\n\tFECube();\n\n\t// destructor\n\t~FECube();\n\n\t// build the cube data\n\tbool Build(FEModel* fem);\n\n\t// get the mesh of this cube\n\tFEMesh* GetMesh();\n\npublic:\n\t// Get a surface\n\tFESurface* GetSurface(int i);\n\n\t// get the node set of the corner nodes\n\tconst FENodeSet& GetCornerNodes() const;\n\n\t// get the node set of boundary nodes\n\tconst FENodeSet& GetBoundaryNodes() const;\n\nprivate:\n\tFEMesh*\tm_mesh;\n\n\tFESurface*\tm_surf[6];\t\t// the six boundary surfaces\n\tFENodeSet*\tm_corners;\t\t// the eight corner nodes\n\tFENodeSet*\tm_boundary;\t\t// set of all boundary nodes\n};\n"
  },
  {
    "path": "FECore/FEDataArray.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDataArray.h\"\n#include \"DumpStream.h\"\n#include \"fecore_type.h\"\n#include \"FENodeDataMap.h\"\n#include \"FEDomainMap.h\"\n#include \"FESurfaceMap.h\"\n\n//-----------------------------------------------------------------------------\nFEDataArray::FEDataArray(FEDataMapType mapType, FEDataType dataType) : m_mapType(mapType), m_dataType(dataType), m_dataCount(0)\n{\n\tm_dataSize = fecore_data_size(dataType);\n}\n\n//-----------------------------------------------------------------------------\nFEDataArray::~FEDataArray()\n{\n}\n\n//-----------------------------------------------------------------------------\nFEDataArray::FEDataArray(const FEDataArray& map)\n{\n\tm_dataType = map.m_dataType;\n\tm_dataSize = map.m_dataSize;\n\tm_dataCount = map.m_dataCount;\n\tm_val = map.m_val;\n}\n\n//-----------------------------------------------------------------------------\nFEDataArray& FEDataArray::operator = (const FEDataArray& map)\n{\n\tm_dataType = map.m_dataType;\n\tm_dataSize = map.m_dataSize;\n\tm_dataCount = map.m_dataCount;\n\tm_val = map.m_val;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nbool FEDataArray::resize(int n, double val)\n{\n\tif (n < 0) return false;\n\tm_val.resize(n*DataSize(), val);\n\tm_dataCount = n;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEDataArray::realloc(int n)\n{\n\tif (n < 0) return false;\n\tm_val.resize(n*DataSize());\n\tm_dataCount = n;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! set the data sized\nvoid FEDataArray::SetDataSize(int dataSize)\n{\n\tm_dataSize = dataSize;\n\tif (m_val.empty() == false)\n\t{\n\t\tm_val.resize(m_dataSize*m_dataCount);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n\nFEDataType intToDataType(int i)\n{\n\tswitch (i)\n\t{\n\tcase FE_DOUBLE: return FE_DOUBLE; break;\n\tcase FE_VEC2D : return FE_VEC2D; break;\n\tcase FE_VEC3D : return FE_VEC3D; break;\n\tcase FE_MAT3D : return FE_MAT3D; break;\n    case FE_MAT3DS: return FE_MAT3DS; break;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n\treturn FE_INVALID_TYPE;\n}\n\nvoid FEDataArray::Serialize(DumpStream& ar)\n{\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_dataSize;\n\t\tar << m_dataCount;\n\t\tar << (int)m_dataType;\n\t\tar << m_val;\n\t}\n\telse\n\t{\n\t\tint ntype = 0;\n\t\tar >> m_dataSize;\n\t\tar >> m_dataCount;\n\t\tar >> ntype;\n\t\tar >> m_val;\n\n\t\tm_dataType = intToDataType(ntype);\n\t\tassert(m_val.size() == m_dataSize*m_dataCount);\n\t}\n}\n\nvoid FEDataArray::SaveClass(DumpStream& ar, FEDataArray* p)\n{\n\tint ntype = (int) p->DataMapType();\n\tar << ntype;\n}\n\nFEDataArray* FEDataArray::LoadClass(DumpStream& ar, FEDataArray* p)\n{\n\tint ntype;\n\tar >> ntype;\n\tp = nullptr;\n\tswitch (ntype)\n\t{\n\tcase FE_NODE_DATA_MAP: p = new FENodeDataMap; break;\n\tcase FE_DOMAIN_MAP   : p = new FEDomainMap; break;\n\tcase FE_SURFACE_MAP  : p = new FESurfaceMap; break;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn p;\n}\n"
  },
  {
    "path": "FECore/FEDataArray.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <assert.h>\n#include <string>\n#include \"vec3d.h\"\n#include \"vec2d.h\"\n#include \"mat3d.h\"\n#include \"fecore_api.h\"\n#include \"fecore_enum.h\"\n#include \"fecore_type.h\"\n\n//-----------------------------------------------------------------------------\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEDataArray\n{\npublic:\n\t//! default constructor\n\tFEDataArray(FEDataMapType mapType, FEDataType dataType);\n\n\tvirtual ~FEDataArray();\n\npublic:\n\tvirtual void setValue(int n, double v) = 0;\n\tvirtual void setValue(int n, const vec2d& v) = 0;\n\tvirtual void setValue(int n, const vec3d& v) = 0;\n\tvirtual void setValue(int n, const mat3d& v) = 0;\n\tvirtual void setValue(int n, const mat3ds& v) = 0;\n\n\tvirtual void fillValue(double v) = 0;\n\tvirtual void fillValue(const vec2d& v) = 0;\n\tvirtual void fillValue(const vec3d& v) = 0;\n\tvirtual void fillValue(const mat3d& v) = 0;\n\tvirtual void fillValue(const mat3ds& v) = 0;\n\npublic:\n\t//! get the value for a given facet index\n\ttemplate <class T> T get(int n) const;\n\n\t//! set the value\n\ttemplate <class T> bool set(const T& v);\n\n\t//! set the value\n\ttemplate <class T> bool set(int n, const T& v);\n\n\t//! add a value\n\ttemplate <class T> void push_back(const T& v);\n\n\t//! allocate data\n\tbool resize(int nsize, double val = 0.0);\n\tbool realloc(int nsize);\n\n\t//! set the data sized\n\tvoid SetDataSize(int dataSize);\n\npublic:\n\t//! get the data type\n\tFEDataType DataType() const { return m_dataType; }\n\n\t//! get the map type\n\tFEDataMapType DataMapType() const { return m_mapType; }\n\n\t//! get data size\n\tint DataSize() const { return m_dataSize; }\n\n\t// number of data items\n\tint DataCount() const { return m_dataCount; }\n\n\t//! return the buffer size (actual number of doubles)\n\tint BufferSize() const { return (int) m_val.size(); }\n\npublic:\n\t//! serialization\n\tvirtual void Serialize(DumpStream& ar);\n\n\tstatic void SaveClass(DumpStream& ar, FEDataArray* p);\n\tstatic FEDataArray* LoadClass(DumpStream& ar, FEDataArray* p);\n\nprotected:\n\t//! copy constructor\n\tFEDataArray(const FEDataArray& map);\n\n\t//! assignment operator\n\tFEDataArray& operator = (const FEDataArray& map);\n\nprivate:\n\tFEDataMapType\tm_mapType;\t//!< the map type\n\tFEDataType\t\tm_dataType;\t//!< the data type\n\tint\tm_dataSize;\t\t\t\t//!< size of each data item\n\tint\tm_dataCount;\t\t\t//!< number of data items\n\n\tstd::vector<double>\tm_val;\t//!< data values\n};\n\ntemplate <> inline double FEDataArray::get<double>(int n) const\n{\n\tassert(m_dataSize == fecoreType<double>::size());\n\treturn\tm_val[n];\n}\n\ntemplate <> inline vec2d FEDataArray::get<vec2d>(int n) const\n{\n\tassert(m_dataSize == fecoreType<vec2d>::size());\n\treturn\tvec2d(m_val[2*n], m_val[2*n+1]);\n}\n\ntemplate <> inline vec3d FEDataArray::get<vec3d>(int n) const\n{\n\tassert(m_dataSize == fecoreType<vec3d>::size());\n\treturn\tvec3d(m_val[3*n], m_val[3*n + 1], m_val[3*n+2]);\n}\n\ntemplate <> inline mat3d FEDataArray::get<mat3d>(int n) const\n{\n\tassert(m_dataSize == fecoreType<mat3d>::size());\n\tconst double* v = &(m_val[9*n]);\n\treturn\tmat3d(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);\n}\n\ntemplate <> inline mat3ds FEDataArray::get<mat3ds>(int n) const\n{\n\tassert(m_dataSize == fecoreType<mat3ds>::size());\n\tconst double* v = &(m_val[6 * n]);\n\treturn\tmat3ds(v[0], v[1], v[2], v[3], v[4], v[5]);\n}\n\n\ntemplate <> inline void FEDataArray::push_back<double>(const double& v)\n{\n\tassert(m_dataSize == fecoreType<double>::size());\n\tm_val.push_back(v);\n\tm_dataCount++;\n}\n\ntemplate <> inline void FEDataArray::push_back<vec2d>(const vec2d& v)\n{\n\tassert(m_dataSize == fecoreType<vec2d>::size());\n\tm_val.push_back(v.x());\n\tm_val.push_back(v.y());\n\tm_dataCount++;\n}\n\ntemplate <> inline void FEDataArray::push_back<vec3d>(const vec3d& v)\n{\n\tassert(m_dataSize == fecoreType<vec3d>::size());\n\tm_val.push_back(v.x);\n\tm_val.push_back(v.y);\n\tm_val.push_back(v.z);\n\tm_dataCount++;\n}\n\ntemplate <> inline void FEDataArray::push_back<mat3d>(const mat3d& v)\n{\n\tassert(m_dataSize == fecoreType<mat3d>::size());\n\tm_val.push_back(v[0][0]); m_val.push_back(v[0][1]); m_val.push_back(v[0][2]);\n\tm_val.push_back(v[1][0]); m_val.push_back(v[1][1]); m_val.push_back(v[1][2]);\n\tm_val.push_back(v[2][0]); m_val.push_back(v[2][1]); m_val.push_back(v[2][2]);\n\tm_dataCount++;\n}\n\ntemplate <> inline void FEDataArray::push_back<mat3ds>(const mat3ds& v)\n{\n\tassert(m_dataSize == fecoreType<mat3ds>::size());\n\tm_val.push_back(v.xx());\n\tm_val.push_back(v.yy());\n\tm_val.push_back(v.zz());\n\tm_val.push_back(v.xy());\n\tm_val.push_back(v.yz());\n\tm_val.push_back(v.xz());\n\tm_dataCount++;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<double>(int n, const double& v)\n{\n\tassert(m_dataSize == fecoreType<double>::size());\n\tm_val[n] = v;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<vec2d>(int n, const vec2d& v)\n{\n\tassert(m_dataSize == fecoreType<vec2d>::size());\n\tm_val[2 * n] = v.x();\n\tm_val[2 * n + 1] = v.y();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<vec3d>(int n, const vec3d& v)\n{\n\tassert(m_dataSize == fecoreType<vec3d>::size());\n\tm_val[3 * n] = v.x;\n\tm_val[3 * n + 1] = v.y;\n\tm_val[3 * n + 2] = v.z;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<mat3d>(int n, const mat3d& v)\n{\n\tassert(m_dataSize == fecoreType<mat3d>::size());\n\tdouble* d = &(m_val[9 * n]);\n\td[0] = v[0][0]; d[1] = v[0][1]; d[2] = v[0][2];\n\td[3] = v[1][0]; d[4] = v[1][1]; d[5] = v[1][2];\n\td[6] = v[2][0]; d[7] = v[2][1]; d[8] = v[2][2];\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<mat3ds>(int n, const mat3ds& v)\n{\n\tassert(m_dataSize == fecoreType<mat3ds>::size());\n\tdouble* d = &(m_val[6 * n]);\n\td[0] = v.xx();\n\td[1] = v.yy();\n\td[2] = v.zz();\n\td[3] = v.xy();\n\td[4] = v.yz();\n\td[5] = v.xz();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<double>(const double& v)\n{\n\tassert(m_dataSize == fecoreType<double>::size());\n\tfor (int i = 0; i<(int)m_val.size(); ++i) m_val[i] = v;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<vec2d>(const vec2d& v)\n{\n\tassert(m_dataSize == fecoreType<vec2d>::size());\n\tfor (int i = 0; i<(int)m_val.size(); i += 2)\n\t{\n\t\tm_val[i] = v.x();\n\t\tm_val[i + 1] = v.y();\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<vec3d>(const vec3d& v)\n{\n\tassert(m_dataSize == fecoreType<vec3d>::size());\n\tfor (int i = 0; i<(int)m_val.size(); i += 3)\n\t{\n\t\tm_val[i] = v.x;\n\t\tm_val[i + 1] = v.y;\n\t\tm_val[i + 2] = v.z;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<mat3d>(const mat3d& v)\n{\n\tassert(m_dataSize == fecoreType<mat3d>::size());\n\tfor (int i = 0; i<(int)m_val.size(); i += 9)\n\t{\n\t\tdouble* d = &m_val[i];\n\t\td[0] = v[0][0]; d[1] = v[0][1]; d[2] = v[0][2];\n\t\td[3] = v[1][0]; d[4] = v[1][1]; d[5] = v[1][2];\n\t\td[6] = v[2][0]; d[7] = v[2][1]; d[8] = v[2][2];\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <> inline bool FEDataArray::set<mat3ds>(const mat3ds& v)\n{\n\tassert(m_dataSize == fecoreType<mat3ds>::size());\n\tfor (int i = 0; i < (int)m_val.size(); i += 6)\n\t{\n\t\tdouble* d = &m_val[i];\n\t\td[0] = v.xx();\n\t\td[1] = v.yy();\n\t\td[2] = v.zz();\n\t\td[3] = v.xy();\n\t\td[4] = v.yz();\n\t\td[5] = v.xz();\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FEDataExport.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDataExport.h\"\n#include \"vec3d.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nvoid FEDataExport::Serialize(FEDataStream& ar)\n{\n\tif ((m_type == PLT_VEC3F)&&(m_fmt == FMT_NODE))\n\t{\n\t\tvector<vec3d>& v = *(static_cast<vector<vec3d>*>(m_pd));\n\n\t\tint n = (int) v.size();\n\t\tfor (int i=0; i<n; ++i) ar << v[i];\n\t}\n\telse if ((m_type == PLT_FLOAT)&&(m_fmt == FMT_REGION))\n\t{\n\t\tdouble& d = *(static_cast<double*>(m_pd));\n\t\tar << d;\n\t}\n\telse if ((m_type == PLT_FLOAT) && (m_fmt == FMT_NODE))\n\t{\n\t\tvector<double>& v = *(static_cast<vector<double>*>(m_pd));\n\n\t\tint n = (int)v.size();\n\t\tfor (int i = 0; i<n; ++i) ar << v[i];\n\t}\n\telse\n\t{\n\t\tassert(false);\n\t}\n}\n"
  },
  {
    "path": "FECore/FEDataExport.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_enum.h\"\n#include \"FEDataStream.h\"\n\n//-----------------------------------------------------------------------------\n// This class is used by domain classes to define their data exports.\n// This is part of an experimental feature that allows domain classes to handle\n// the data that they want to export. \nclass FECORE_API FEDataExport\n{\npublic:\n\tFEDataExport(Var_Type itype, Storage_Fmt ifmt, void* pd, const char* szname)\n\t{\n\t\tm_pd = pd;\n\t\tm_type = itype;\n\t\tm_fmt = ifmt;\n\t\tm_szname = szname;\n\t}\n\n\tvirtual ~FEDataExport(){}\n\n\tvirtual void Serialize(FEDataStream& d);\n\npublic:\n\tVar_Type\tm_type;\n\tStorage_Fmt\tm_fmt;\n\tvoid*\t\tm_pd;\t\t//!< pointer to data field\n\tconst char*\tm_szname;\n};\n\n#define EXPORT_DATA(iType, iFmt, pVar, Name) AddDataExport(new FEDataExport(iType, iFmt, pVar, Name));"
  },
  {
    "path": "FECore/FEDataGenerator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDataGenerator.h\"\n#include \"FEMesh.h\"\n#include \"FENodeDataMap.h\"\n#include \"FEDomainMap.h\"\n#include \"FEElementSet.h\"\n#include \"log.h\"\n\nFEMeshDataGenerator::FEMeshDataGenerator(FEModel* fem) : FEModelComponent(fem)\n{\n}\n\nFEMeshDataGenerator::~FEMeshDataGenerator()\n{\n}\n\nbool FEMeshDataGenerator::Init()\n{\n\treturn true;\n}\n\nvoid FEMeshDataGenerator::Evaluate(double time)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nFENodeDataGenerator::FENodeDataGenerator(FEModel* fem) : FEMeshDataGenerator(fem)\n{\n\tm_nodeSet = nullptr;\n}\n\nvoid FENodeDataGenerator::SetNodeSet(FENodeSet* nodeSet)\n{\n\tm_nodeSet = nodeSet;\n}\n\nFENodeSet* FENodeDataGenerator::GetNodeSet()\n{\n\treturn m_nodeSet;\n}\n\n//-----------------------------------------------------------------------------\nFEEdgeDataGenerator::FEEdgeDataGenerator(FEModel* fem) : FEMeshDataGenerator(fem)\n{\n\tm_edgeList = nullptr;\n}\n\nvoid FEEdgeDataGenerator::SetEdgeList(FEEdgeList* edgeSet)\n{\n\tm_edgeList = edgeSet;\n}\n\nFEEdgeList* FEEdgeDataGenerator::GetEdgeList()\n{\n\treturn m_edgeList;\n}\n\n//-----------------------------------------------------------------------------\nFEFaceDataGenerator::FEFaceDataGenerator(FEModel* fem) : FEMeshDataGenerator(fem)\n{\n\tm_surf = nullptr;\n}\n\nvoid FEFaceDataGenerator::SetFacetSet(FEFacetSet* surf)\n{\n\tm_surf = surf;\n}\n\nFEFacetSet* FEFaceDataGenerator::GetFacetSet()\n{\n\treturn m_surf;\n}\n\n//-----------------------------------------------------------------------------\nFEElemDataGenerator::FEElemDataGenerator(FEModel* fem) : FEMeshDataGenerator(fem)\n{\n\tm_elemSet = nullptr;\n}\n\nvoid FEElemDataGenerator::SetElementSet(FEElementSet* elset) { m_elemSet = elset; }\nFEElementSet* FEElemDataGenerator::GetElementSet() { return m_elemSet; }\n"
  },
  {
    "path": "FECore/FEDataGenerator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include \"FEModelComponent.h\"\n\nclass FENodeSet;\nclass FEEdgeList;\nclass FEFacetSet;\nclass FEElementSet;\nclass FEDataMap;\n\n// Data generators are used to generate mesh data sections algorithmically\nclass FECORE_API FEMeshDataGenerator : public FEModelComponent\n{\n\tFECORE_SUPER_CLASS(FEMESHDATAGENERATOR_ID)\n\tFECORE_BASE_CLASS(FEMeshDataGenerator)\n\npublic:\n\tFEMeshDataGenerator(FEModel* fem);\n\tvirtual ~FEMeshDataGenerator();\n\n\t// this function gives the data generator a chance to initialize itself\n\t// and check for any input problems.\n\tvirtual bool Init();\n\n\t// evaluate the data at specific time\n\tvirtual void Evaluate(double time);\n\n\t// generate the mesh data section\n\tvirtual FEDataMap* Generate() = 0;\n};\n\n// class for generating data on node sets\nclass FECORE_API FENodeDataGenerator : public FEMeshDataGenerator\n{\n\tFECORE_BASE_CLASS(FENodeDataGenerator)\n\npublic:\n\tFENodeDataGenerator(FEModel* fem);\n\n\tvoid SetNodeSet(FENodeSet* nodeSet);\n\n\tFENodeSet* GetNodeSet();\n\nprotected:\n\tFENodeSet* m_nodeSet;\n};\n\n// class for generating data on edges\nclass FECORE_API FEEdgeDataGenerator : public FEMeshDataGenerator\n{\n\tFECORE_BASE_CLASS(FEEdgeDataGenerator)\n\npublic:\n\tFEEdgeDataGenerator(FEModel* fem);\n\n\tvoid SetEdgeList(FEEdgeList* edgeSet);\n\n\tFEEdgeList* GetEdgeList();\n\nprotected:\n\tFEEdgeList* m_edgeList;\n};\n\n// class for generating data on surfaces\nclass FECORE_API FEFaceDataGenerator : public FEMeshDataGenerator\n{\n\tFECORE_BASE_CLASS(FEFaceDataGenerator)\n\npublic:\n\tFEFaceDataGenerator(FEModel* fem);\n\n\tvoid SetFacetSet(FEFacetSet* surf);\n\tFEFacetSet* GetFacetSet();\n\nprivate:\n\tFEFacetSet* m_surf;\n};\n\n// class for generating data on element sets\nclass FECORE_API FEElemDataGenerator : public FEMeshDataGenerator\n{\n\tFECORE_BASE_CLASS(FEElemDataGenerator)\n\npublic:\n\tFEElemDataGenerator(FEModel* fem);\n\n\tvoid SetElementSet(FEElementSet* elset);\n\tFEElementSet* GetElementSet();\n\nprivate:\n\tFEElementSet* m_elemSet;\n};\n"
  },
  {
    "path": "FECore/FEDataMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDataMap.h\"\n#include \"fecore_type.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nFEDataMap::FEDataMap(FEDataMapType mapType, FEDataType dataType) : FEDataArray(mapType, dataType) {}\n\n//-----------------------------------------------------------------------------\nFEDataMap::FEDataMap(const FEDataMap& map) : FEDataArray(map) {}\n\n//-----------------------------------------------------------------------------\nvoid FEDataMap::SetName(const std::string& name)\n{\n\tm_name = name;\n}\n\n//-----------------------------------------------------------------------------\nconst std::string& FEDataMap::GetName() const { return m_name; }\n\n//-----------------------------------------------------------------------------\nvoid FEDataMap::Serialize(DumpStream& ar)\n{\n\tFEDataArray::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_name;\n\t}\n}\n"
  },
  {
    "path": "FECore/FEDataMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDataArray.h\"\n\nclass FEMaterialPoint;\nclass FEItemList;\n\n//-----------------------------------------------------------------------------\n// Base class for all data maps. A data map needs to be able to evaluate data across a domain\n// TODO: This is a work in progress. \n// This class was added to create a base for FESurfaceMap and FEDomainMap so that both could be used in \n// FEMappedValue. \nclass FECORE_API FEDataMap : public FEDataArray\n{\npublic:\n\tFEDataMap(FEDataMapType mapType, FEDataType dataType = FE_INVALID_TYPE);\n\tFEDataMap(const FEDataMap& map);\n\n\t//! set the name\n\tvoid SetName(const std::string& name);\n\n\t//! get the name\n\tconst std::string& GetName() const;\n\npublic:\n\t// This function needs to be overridden by derived classes\n\tvirtual double value(const FEMaterialPoint& mp) = 0;\n\tvirtual vec3d valueVec3d(const FEMaterialPoint& mp) = 0;\n\tvirtual mat3d valueMat3d(const FEMaterialPoint& mp) = 0;\n\tvirtual mat3ds valueMat3ds(const FEMaterialPoint& mp) = 0;\n\n\t// return the item list associated with this map\n\tvirtual FEItemList* GetItemList() = 0;\n\npublic:\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tstd::string\tm_name;\t\t\t\t\t// name of data map TODO: Move to base class?\n};\n"
  },
  {
    "path": "FECore/FEDataMathGenerator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDataMathGenerator.h\"\n#include \"FENodeDataMap.h\"\n#include \"MathObject.h\"\n#include \"MObjBuilder.h\"\n#include \"FEMesh.h\"\nusing namespace std;\n\nBEGIN_FECORE_CLASS(FEDataMathGenerator, FENodeDataGenerator)\n\tADD_PARAMETER(m_math, \"math\");\nEND_FECORE_CLASS();\n\nFEDataMathGenerator::FEDataMathGenerator(FEModel* fem) : FENodeDataGenerator(fem)\n{\n}\n\n// set the math expression\nvoid FEDataMathGenerator::setExpression(const std::string& math)\n{\n\tm_math = math;\n}\n\nbool FEDataMathGenerator::Init()\n{\n\tstring tmp = m_math;\n\n\t// split the math string at ','\n\tvector<string> strings;\n\tsize_t pos = 0;\n\twhile ((pos = tmp.find(',')) != string::npos)\n\t{\n\t\tstring t = tmp.substr(0, pos);\n\t\tstrings.push_back(t);\n\t\ttmp.erase(0, pos + 1);\n\t}\n\tstrings.push_back(tmp);\n\n\tif ((strings.size() == 0) || (strings.size() > 3)) return false;\n\n\tfor (size_t i = 0; i < strings.size(); ++i)\n\t{\n\t\tMSimpleExpression val; \n\t\tMVariable* var_x = val.AddVariable(\"X\");\n\t\tMVariable* var_y = val.AddVariable(\"Y\");\n\t\tMVariable* var_z = val.AddVariable(\"Z\");\n\t\tif (val.Create(strings[i]) == false) return false;\n\t\tm_val.push_back(val);\n\t}\n\n\treturn true;\n}\n\nFEDataMap* FEDataMathGenerator::Generate()\n{\n\tassert(m_nodeSet);\n\tif (m_nodeSet == nullptr) return nullptr;\n\tFENodeDataMap* map = new FENodeDataMap(FE_DOUBLE);\n\tmap->Create(m_nodeSet);\n\tint N = m_nodeSet->Size();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode* node = m_nodeSet->Node(i);\n\t\tvec3d r = node->m_r0;\n\t\tvector<double> p{ r.x, r.y, r.z };\n\t\tdouble v = m_val[0].value_s(p);\n\t\tmap->setValue(i, v);\n\t}\n\treturn map;\n}\n"
  },
  {
    "path": "FECore/FEDataMathGenerator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDataGenerator.h\"\n#include <string>\n#include \"MathObject.h\"\n\nclass FENodeSet;\nclass FEFacetSet;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEDataMathGenerator : public FENodeDataGenerator\n{\npublic:\n\tFEDataMathGenerator(FEModel* fem);\n\n\tbool Init() override;\n\n\t// set the math expression\n\tvoid setExpression(const std::string& math);\n\n\tFEDataMap* Generate() override;\n\nprivate:\n\tstd::string\t\t\tm_math;\n\tvector<MSimpleExpression>\tm_val;\n\n\tDECLARE_FECORE_CLASS()\n};\n"
  },
  {
    "path": "FECore/FEDataStream.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include \"mat3d.h\"\n#include \"tens4d.h\"\n\n//-----------------------------------------------------------------------------\n// This class can be used to serialize data.\n// This is part of a new experimental feature that allows domain classes to define\n// data exports. This in turn will eliminate the need for many of the plot classes. \n// TODO: This looks a lot like a FEDataArray. Perhaps combine?\nclass FEDataStream\n{\npublic:\n\tFEDataStream(){}\n\n\tvoid clear() { m_a.clear(); }\n\n\tFEDataStream& operator << (const double& f) { m_a.push_back((float) f); return *this; }\n\tFEDataStream& operator << (const vec3d& v) \n\t{\n\t\tm_a.push_back((float) v.x);\n\t\tm_a.push_back((float) v.y);\n\t\tm_a.push_back((float) v.z);\n\t\treturn *this;\n\t}\n\tFEDataStream& operator << (const mat3ds& m) \n\t{\n\t\tm_a.push_back((float) m.xx());\n\t\tm_a.push_back((float) m.yy());\n\t\tm_a.push_back((float) m.zz());\n\t\tm_a.push_back((float) m.xy());\n\t\tm_a.push_back((float) m.yz());\n\t\tm_a.push_back((float) m.xz());\n\t\treturn *this;\n\t}\n\tFEDataStream& operator << (const mat3d& m)\n\t{\n\t\tm_a.push_back((float)m(0, 0));\n\t\tm_a.push_back((float)m(0, 1));\n\t\tm_a.push_back((float)m(0, 2));\n\t\tm_a.push_back((float)m(1, 0));\n\t\tm_a.push_back((float)m(1, 1));\n\t\tm_a.push_back((float)m(1, 2));\n\t\tm_a.push_back((float)m(2, 0));\n\t\tm_a.push_back((float)m(2, 1));\n\t\tm_a.push_back((float)m(2, 2));\n\t\treturn *this;\n\t}\n\tFEDataStream& operator << (const tens4ds& a)\n\t{\n        for (int k=0; k<21; ++k) m_a.push_back((float) a.d[k]);\n\t\treturn *this;\n\t}\n\n\tFEDataStream& operator << (const std::vector<double>& a)\n\t{\n\t\tfor (double ai : a) m_a.push_back((float)ai);\n\t\treturn *this;\n\t}\n\n\tFEDataStream& operator << (const std::vector<vec3d>& a)\n\t{\n\t\tfor (vec3d ai : a)\n\t\t{\n\t\t\tm_a.push_back((float)ai.x);\n\t\t\tm_a.push_back((float)ai.y);\n\t\t\tm_a.push_back((float)ai.z);\n\t\t}\n\t\treturn *this;\n\t}\n\n\tvoid assign(size_t count, float f) { m_a.assign(count, f); }\n\tvoid resize(size_t count, float f) { m_a.resize(count, f); }\n\tvoid reserve(size_t count) { m_a.reserve(count); }\n\tvoid push_back(const float& f) { m_a.push_back(f); }\n\tsize_t size() const { return m_a.size(); }\n\n\tfloat& operator [] (int i) { return m_a[i]; }\n\n\tstd::vector<float>& data() { return m_a; }\n\n\ttemplate <class T> T get(int i);\n\nprivate:\n\tstd::vector<float>\tm_a;\n};\n\ntemplate <class T> inline T FEDataStream::get(int i) { return T(0.0);  }\n\ntemplate <> inline float  FEDataStream::get<float >(int i) { return m_a[i]; }\ntemplate <> inline double FEDataStream::get<double>(int i) { return (double) m_a[i]; }\ntemplate <> inline vec3d  FEDataStream::get<vec3d >(int i) { return vec3d(m_a[3*i], m_a[3*i+1], m_a[3*i+2]); }\ntemplate <> inline vec3f  FEDataStream::get<vec3f >(int i) { return vec3f(m_a[3*i], m_a[3*i+1], m_a[3*i+2]); }\ntemplate <> inline mat3fs FEDataStream::get<mat3fs>(int i) { float* v = &m_a[6*i]; return mat3fs(v[0],v[1],v[2],v[3],v[4],v[5]); }\ntemplate <> inline mat3f  FEDataStream::get<mat3f >(int i) { float* v = &m_a[9*i]; return mat3f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8]); }\n"
  },
  {
    "path": "FECore/FEDataValue.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEDataValue.h\"\n#include \"FELogNodeData.h\"\n#include \"FELogElemData.h\"\n#include \"FEModel.h\"\n#include \"FEMesh.h\"\n\nFEDataValue::FEDataValue()\n{\n\n}\n\nbool FEDataValue::IsValid() const\n{\n\treturn (m_logData != nullptr);\n}\n\nvoid FEDataValue::SetLogData(FELogData* logData)\n{\n\tm_logData = logData;\n}\n\nbool FEDataValue::GetValues(const FEItemList* itemList, std::vector<double>& val)\n{\n\tif (m_logData == nullptr) return false;\n\tif (itemList == nullptr) return false;\n\n\tFELogNodeData* nodeData = dynamic_cast<FELogNodeData*>(m_logData);\n\tif (nodeData)\n\t{\n\t\tconst FENodeSet* nset = dynamic_cast<const FENodeSet*>(itemList);\n\t\tif (nset == nullptr) return false;\n\n\t\tFEModel* fem = nodeData->GetFEModel();\n\t\tFEMesh& mesh = fem->GetMesh();\n\n\t\tint n = nset->Size();\n\t\tval.resize(n);\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tint nid = (*nset)[i];\n\t\t\tFENode* node = mesh.FindNodeFromID(nid);\n\t\t\tif (node == nullptr) return false;\n\t\t\tval[i] = nodeData->value(*node);\n\t\t}\n\t\treturn true;\n\t}\n\n\tFELogElemData* elemData = dynamic_cast<FELogElemData*>(m_logData);\n\tif (elemData)\n\t{\n\t\tconst FEElementSet* eset = dynamic_cast<const FEElementSet*>(itemList);\n\t\tif (eset == nullptr) return false;\n\n\t\tFEModel* fem = elemData->GetFEModel();\n\t\tFEMesh& mesh = fem->GetMesh();\n\n\t\tint n = eset->Elements();\n\t\tval.resize(n);\n\t\tfor (int i = 0; i < n; ++i)\n\t\t{\n\t\t\tint eid = (*eset)[i];\n\t\t\tFEElement* elem = mesh.FindElementFromID(eid);\n\t\t\tif (elem == nullptr) return false;\n\t\t\tval[i] = elemData->value(*elem);\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "FECore/FEDataValue.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FELogData.h\"\n#include \"FEItemList.h\"\n#include <memory>\n\nclass FECORE_API FEDataValue\n{\npublic:\n\tFEDataValue();\n\n\tbool IsValid() const;\n\n\tvoid SetLogData(FELogData* logData);\n\n\tbool GetValues(const FEItemList* itemList, std::vector<double>& val);\n\nprivate:\n\tFELogData* m_logData = nullptr;\n};\n"
  },
  {
    "path": "FECore/FEDiscreteDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiscreteDomain.h\"\n#include \"FEMesh.h\"\n#include \"FEMaterial.h\"\n\n//-----------------------------------------------------------------------------\nbool FEDiscreteDomain::Create(int nelems, FE_Element_Spec espec)\n{ \n\tm_Elem.resize(nelems); \n\tfor (int i = 0; i < nelems; ++i)\n\t{\n\t\tFEDiscreteElement& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (espec.etype != FE_ELEM_INVALID_TYPE)\n\t\tfor (int i=0; i<nelems; ++i) m_Elem[i].SetType(espec.etype);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteDomain::Reset()\n{\n\tfor (auto& el : m_Elem) el.setActive();\n}\n\n//-----------------------------------------------------------------------------\nbool FEDiscreteDomain::Init()\n{\n\tif (FEDomain::Init() == false) return false;\n\n\tFEMaterial* pmat = GetMaterial();\n\tif (pmat) SetMatID(pmat->GetID() - 1);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteDomain::CopyFrom(FEMeshPartition* pd)\n{\n\tFEDomain::CopyFrom(pd);\n\tFEDiscreteDomain* psd = dynamic_cast<FEDiscreteDomain*>(pd);\n\tm_Elem = psd->m_Elem;\n\tForEachElement([=](FEElement& el) { el.SetMeshPartition(this); });\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteDomain::AddElement(int eid, int n[2])\n{\n\tFEDiscreteElement el;\n\tel.SetType(FE_DISCRETE);\n\tel.m_node[0] = n[0];\n\tel.m_node[1] = n[1];\n\tel.SetID(eid);\n\tm_Elem.push_back(el);\n}\n"
  },
  {
    "path": "FECore/FEDiscreteDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! domain for discrete elements\nclass FECORE_API FEDiscreteDomain : public FEDomain\n{\n\tFECORE_SUPER_CLASS(FEDISCRETEDOMAIN_ID)\n\tFECORE_BASE_CLASS(FEDiscreteDomain)\n\npublic:\n\tFEDiscreteDomain(FEModel* fem) : FEDomain(FE_DOMAIN_DISCRETE, fem) {}\n\n\tbool Create(int nsize, FE_Element_Spec espec) override;\n\tint Elements() const override { return (int) m_Elem.size(); }\n\tFEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\n\tFEDiscreteElement& Element(int n) { return m_Elem[n]; }\n\n\tbool Init() override;\n\n\tvoid Reset() override;\n\n\t//! copy data from another domain (overridden from FEDomain)\n\tvoid CopyFrom(FEMeshPartition* pd) override;\n\npublic:\n\tvoid AddElement(int eid, int n[2]);\n\nprotected:\n\tvector<FEDiscreteElement>\tm_Elem;\n};\n"
  },
  {
    "path": "FECore/FEDiscreteMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiscreteMaterial.h\"\n\n//=============================================================================\nFEMaterialPointData* FEDiscreteMaterialPoint::Copy()\n{\n\tFEDiscreteMaterialPoint* pt = new FEDiscreteMaterialPoint(*this);\n\tif (m_pNext) pt->m_pNext = m_pNext->Copy();\n\treturn pt;\n}\n\nvoid FEDiscreteMaterialPoint::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tar & m_dr0 & m_drp & m_drt & m_dvt;\n}\n\n\n//=============================================================================\n\nFEDiscreteMaterial::FEDiscreteMaterial(FEModel* pfem) : FEMaterial(pfem) {}\n"
  },
  {
    "path": "FECore/FEDiscreteMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMaterial.h\"\n\n//-----------------------------------------------------------------------------\n// Material point data for discrete materials.\nclass FECORE_API FEDiscreteMaterialPoint : public FEMaterialPointData\n{\npublic:\n\tFEMaterialPointData* Copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tvec3d\tm_dr0;\t// initial relative position\n\tvec3d\tm_drp;\t// previous relative position\n\tvec3d\tm_drt;\t// relative position r_b - r_a\n\tvec3d\tm_dvt;\t// relative velocity v_b - v_a\n};\n\n//-----------------------------------------------------------------------------\n//! material class for discrete elements\nclass FECORE_API FEDiscreteMaterial : public FEMaterial\n{\n\tFECORE_SUPER_CLASS(FEDISCRETEMATERIAL_ID)\n\npublic:\n\tFEDiscreteMaterial(FEModel* pfem);\n};\n"
  },
  {
    "path": "FECore/FEDiscreteSet.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDiscreteSet.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteSet::NodePair::Serialize(DumpStream& ar)\n{\n\tar & n0 & n1;\n}\n\n//-----------------------------------------------------------------------------\nFEDiscreteSet::FEDiscreteSet(FEMesh* pm) : m_pmesh(pm)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteSet::create(int n)\n{\n\tm_pair.resize(n);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteSet::add(int n0, int n1)\n{\n\tNodePair p = { n0, n1 };\n\tm_pair.push_back(p);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteSet::SetName(const std::string& name)\n{\n\tm_name = name;\n}\n\n//-----------------------------------------------------------------------------\nconst std::string& FEDiscreteSet::GetName() const\n{\n\treturn m_name;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDiscreteSet::Serialize(DumpStream& ar)\n{\n\tar & m_name;\n\tar & m_pair;\n}\n"
  },
  {
    "path": "FECore/FEDiscreteSet.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n#include <string>\n\n//-----------------------------------------------------------------------------\n// Forward declarations\nclass FEMesh;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! Defines a discrete element set (i.e. node-pairs)\nclass FECORE_API FEDiscreteSet\n{\npublic:\n\tstruct NodePair\n\t{\n\t\tint\tn0, n1;\n\n\t\tvoid Serialize(DumpStream& ar);\n\t};\n\npublic:\n\tFEDiscreteSet(FEMesh* pm);\n\tvoid create(int n);\n\tint size() const { return (int)m_pair.size(); }\n\n\tvoid add(int n0, int n1);\n\n\tvoid SetName(const std::string& name);\n\tconst std::string& GetName() const;\n\n\tconst NodePair& Element(int i) const { return m_pair[i]; }\n\n\tvoid Serialize(DumpStream& ar);\n\nprivate:\n\tFEMesh*\t\t\t\t\tm_pmesh;\n\tstd::vector<NodePair>\tm_pair;\t\t//!< list of discrete elements\n\tstd::string\t\t\t\tm_name;\n};\n"
  },
  {
    "path": "FECore/FEDofList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FEDofList.h\"\n#include \"FEModel.h\"\n\nFEDofList::FEDofList(FEModel* fem)\n{\n\tm_fem = fem;\n}\n\nFEDofList::FEDofList(const FEDofList& dofs)\n{\n\tm_fem = dofs.m_fem;\n\tm_dofList = dofs.m_dofList;\n}\n\nvoid FEDofList::operator = (const FEDofList& dofs)\n{\n\tassert(m_fem == dofs.m_fem);\n\tm_dofList = dofs.m_dofList;\n}\n\nvoid FEDofList::Clear()\n{\n\tm_dofList.clear();\n}\n\nbool FEDofList::AddDof(const char* szdof)\n{\n\tint dof = m_fem->GetDOFIndex(szdof);\n\tif (dof == -1) return false;\n\tm_dofList.push_back(dof);\n\treturn true;\n}\n\nvoid FEDofList::operator = (const std::vector<int>& dofs)\n{\n\tm_dofList = dofs;\n}\n\nbool FEDofList::AddDof(int ndof)\n{\n\tm_dofList.push_back(ndof);\n\treturn true;\n}\n\nbool FEDofList::AddVariable(const char* szvar)\n{\n\tDOFS& Dofs = m_fem->GetDOFS();\n\tstd::vector<int> dofList;\n\tDofs.GetDOFList(szvar, dofList);\n\tif (dofList.empty()) { return false; }\n\n\tm_dofList.insert(m_dofList.end(), dofList.begin(), dofList.end());\n\treturn true;\n}\n\n// Add all the dofs a variable\nbool FEDofList::AddVariable(int nvar)\n{\n\tDOFS& Dofs = m_fem->GetDOFS();\n\tstd::vector<int> dofList;\n\tDofs.GetDOFList(nvar, dofList);\n\tif (dofList.empty()) return false;\n\n\tm_dofList.insert(m_dofList.end(), dofList.begin(), dofList.end());\n\treturn true;\n}\n\n// Add degrees of freedom\nbool FEDofList::AddDofs(const FEDofList& dofs)\n{\n\tfor (int i = 0; i < dofs.Size(); ++i) {\n\t\tif (AddDof(dofs[i]) == false) return false;\n\t}\n\treturn true;\n}\n\nbool FEDofList::IsEmpty() const\n{\n\treturn m_dofList.empty();\n}\n\nint FEDofList::Size() const\n{\n\treturn (int)m_dofList.size();\n}\n\nint FEDofList::operator [] (int n) const\n{\n\treturn m_dofList[n];\n}\n\nvoid FEDofList::Serialize(DumpStream& ar)\n{\n\tar & m_dofList;\n}\n\nbool FEDofList::Contains(int dof)\n{\n\tfor (size_t i = 0; i < m_dofList.size(); ++i)\n\t{\n\t\tif (dof == m_dofList[i]) return true;\n\t}\n\treturn false;\n}\n\nbool FEDofList::Contains(const FEDofList& dof)\n{\n\t// see if we have all the dofs in dof\n\tfor (int i = 0; i < dof.Size(); ++i)\n\t{\n\t\tint dof_i = dof[i];\n\t\tif (Contains(dof_i) == false) return false;\n\t}\n\n\t// we can only get here if all dofs are accounted for\n\treturn true;\n}\n\nint FEDofList::InterpolationOrder(int index) const\n{\n\treturn m_fem->GetDOFS().GetDOFInterpolationOrder(m_dofList[index]);\n}\n"
  },
  {
    "path": "FECore/FEDofList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n// Convenience class for creating a list of degrees of freedom, without the \n// need to go through the FEModel class;\n\nclass FECORE_API FEDofList\n{\npublic:\n\tFEDofList(FEModel* fem);\n\tFEDofList(const FEDofList& dofs);\n\n\t// assignment operator\n\tvoid operator = (const FEDofList& dofs);\n\tvoid operator = (const std::vector<int>& dofs);\n\n\t// clear the list\n\tvoid Clear();\n\n\t// Add a degree of freedom\n\tbool AddDof(const char* szdof);\n\n\t// Add a degree of freedom\n\tbool AddDof(int ndof);\n\n\t// Add all the dofs of a variable\n\tbool AddVariable(const char* szvar);\n\n\t// Add all the dofs a variable\n\tbool AddVariable(int nvar);\n\n\t// Add degrees of freedom\n\tbool AddDofs(const FEDofList& dofs);\n\n\t// is the list empty\n\tbool IsEmpty() const;\n\n\t// number of dofs in the list\n\tint Size() const;\n\n\t// access a dof\n\tint operator [] (int n) const;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar);\n\n\t// see if this dof list contains all the dofs of a FEDofList\n\tbool Contains(int dof);\n\tbool Contains(const FEDofList& dof);\n\n\t// Get the interpolation order \n\tint InterpolationOrder(int index) const;\n\nprivate:\n\tFEModel*\t\t\tm_fem;\n\tstd::vector<int>\tm_dofList;\n};\n"
  },
  {
    "path": "FECore/FEDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEDomain.h\"\n#include \"FEMaterial.h\"\n#include \"DumpStream.h\"\n#include \"FEMesh.h\"\n#include \"FEGlobalMatrix.h\"\n\n//-----------------------------------------------------------------------------\nFEDomain::FEDomain(int nclass, FEModel* fem) : FEMeshPartition(nclass, fem)\n{\n\tm_matAxis = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain::SetMaterial(FEMaterial* pm)\n{\n\tassert(pm);\n\tif (pm) pm->AddDomain(this);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain::SetMatID(int mid)\n{\n\tForEachElement([=](FEElement& el) { el.SetMatID(mid); });\n}\n\n//-----------------------------------------------------------------------------\n// This routine allocates the material point data for the element's integration points.\n// Currently, this has to be called after the elements have been assigned a type (since this\n// determines how many integration points an element gets). \nvoid FEDomain::CreateMaterialPointData()\n{\n\t// This function is called before Init is called, so we need to do the \n\t// initialization of the mat_axis here.\n\tif (m_matAxis) m_matAxis->Init();\n\n\tFEMaterial* pmat = GetMaterial();\n\tFEMesh* mesh = GetMesh();\n\tif (pmat) ForEachElement([=](FEElement& el) {\n\n\t\tvec3d r[FEElement::MAX_NODES];\n\t\tint ne = el.Nodes();\n\t\tfor (int i = 0; i < ne; ++i) r[i] = mesh->Node(el.m_node[i]).m_r0;\n\n\t\tfor (int k = 0; k < el.GaussPoints(); ++k)\n\t\t{\n\t\t\tFEMaterialPoint* mp = new FEMaterialPoint(pmat->CreateMaterialPointData());\n\t\t\tmp->m_Q = (m_matAxis ? m_matAxis->operator()(*mp) : mat3d::identity());\n\t\t\tmp->m_r0 = el.Evaluate(r, k);\n\t\t\tmp->m_rt = mp->m_r0;\n\t\t\tmp->m_index = k;\n\t\t\tel.SetMaterialPointData(mp, k);\n\t\t}\n\t});\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FEDomain::Serialize(DumpStream& ar)\n{\n\tFEMeshPartition::Serialize(ar);\n\n\tif (ar.IsShallow())\n\t{\n\t\tint NEL = Elements();\n\t\tfor (int i = 0; i < NEL; ++i)\n\t\t{\n\t\t\tFEElement& el = ElementRef(i);\n\t\t\tel.Serialize(ar);\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int j = 0; j < nint; ++j) el.GetMaterialPoint(j)->Serialize(ar);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tFEMaterial* mat = GetMaterial();\n\t\t\tar << mat;\n\n\t\t\tint NEL = Elements();\n\t\t\tar << NEL;\n\t\t\tfor (int i = 0; i < NEL; ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = ElementRef(i);\n\t\t\t\tel.Serialize(ar);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int j = 0; j < nint; ++j) el.GetMaterialPoint(j)->Serialize(ar);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEMaterial* pmat = 0;\n\t\t\tar >> pmat;\n\t\t\tSetMaterial(pmat);\n\n\t\t\tFE_Element_Spec espec; // invalid element spec!\n\n\t\t\tint NEL = 0;\n\t\t\tar >> NEL;\n\t\t\tCreate(NEL, espec);\n\t\t\tfor (int i = 0; i < NEL; ++i)\n\t\t\t{\n\t\t\t\tFEElement& el = ElementRef(i);\n\t\t\t\tel.Serialize(ar);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int j = 0; j < nint; ++j)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint* mp = new FEMaterialPoint(pmat->CreateMaterialPointData());\n\t\t\t\t\tel.SetMaterialPointData(mp, j);\n\t\t\t\t\tel.GetMaterialPoint(j)->Serialize(ar);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Unpack the LM data for an element of this domain\nvoid FEDomain::UnpackLM(FEElement& el, vector<int>& lm)\n{\n\tUnpackLM(el, GetDOFList(), lm);\n}\n\n//-----------------------------------------------------------------------------\n//! Activate the domain\nvoid FEDomain::Activate()\n{\n\tActivate(GetDOFList());\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain::IncrementalUpdate(std::vector<double>& ui, bool finalFlag)\n{\n\n}\n\n//-----------------------------------------------------------------------------\n// This is the default packing method. \n// It stores all the degrees of freedom for the first node in the order defined\n// by the DOF array, then for the second node, and so on. \nvoid FEDomain::UnpackLM(FEElement& el, const FEDofList& dof, vector<int>& lm)\n{\n\tFEMesh* mesh = GetMesh();\n\tint N = el.Nodes();\n\tint ndofs = dof.Size();\n\tlm.resize(N*ndofs);\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tint n = el.m_node[i];\n\t\tFENode& node = mesh->Node(n);\n\t\tvector<int>& id = node.m_ID;\n\t\tfor (int j = 0; j<ndofs; ++j) lm[i*ndofs + j] = id[dof[j]];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain::BuildMatrixProfile(FEGlobalMatrix& M)\n{\n\tvector<int> elm;\n\tconst int NE = Elements();\n\tfor (int j = 0; j<NE; ++j)\n\t{\n\t\tFEElement& el = ElementRef(j);\n\t\tUnpackLM(el, elm);\n\t\tM.build_add(elm);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain::Activate(const FEDofList& dof)\n{\n\t// get the number of degrees of freedom for this domain.\n\tconst int ndofs = dof.Size();\n\n\t// activate all the degrees of freedom of this domain\n\tfor (int i = 0; i<Nodes(); ++i)\n\t{\n\t\tFENode& node = Node(i);\n\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t{\n\t\t\tfor (int j = 0; j<ndofs; ++j)\n\t\t\t\tif (dof[j] >= 0) node.set_active(dof[j]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FEDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMeshPartition.h\"\n#include \"FEMat3dValuator.h\"\n\n// forward declaration of material class\nclass FEMaterial;\n\n// Base class for solid and shell parts. Domains can also have materials assigned.\nclass FECORE_API FEDomain : public FEMeshPartition\n{\npublic:\n\tFEDomain(int nclass, FEModel* fem);\n\n\t//! get the material of this domain\n\tvirtual FEMaterial* GetMaterial() { return 0; }\n\n\t// assign a material to this domain\n\tvirtual void SetMaterial(FEMaterial* pm);\n\n\t//! set the material ID of all elements\n\tvoid SetMatID(int mid);\n\n\t//! Allocate material point data for the elements\n\t//! This is called after elements get read in from the input file.\n\t//! And must be called before material point data can be accessed.\n\t//! \\todo Perhaps I can make this part of the \"creation\" routine\n\tvoid CreateMaterialPointData();\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! augmentation\n\t// NOTE: This is here so that the FESolver can do the augmentations\n\t// for the 3-field hex/shell domains.\n\tvirtual bool Augment(int naug) { return true; }\n\n\t// create function\n\tvirtual bool Create(int elements, FE_Element_Spec espec) = 0;\n\npublic:\n\t//! Get the list of dofs on this domain\n\tvirtual const FEDofList& GetDOFList() const = 0;\n\n\t//! Unpack the LM data for an element of this domain\n\tvirtual void UnpackLM(FEElement& el, vector<int>& lm);\n\n\t//! build the matrix profile\n\tvirtual void BuildMatrixProfile(FEGlobalMatrix& M);\n\n\t//! Activate the domain\n\tvirtual void Activate();\n\n\t//! Gives domains a chance to update any data that depends on the displacement increments.\n\t//! This function is called during the line search as well. The finalFlag\n\t//! indicates whether it is safe to commit the updates.\n\tvirtual void IncrementalUpdate(std::vector<double>& ui, bool finalFlag);\n\nprotected:\n\t// helper function for activating dof lists\n\tvoid Activate(const FEDofList& dof);\n\n\t// helper function for unpacking element dofs\n\tvoid UnpackLM(FEElement& el, const FEDofList& dof, vector<int>& lm);\n\nprotected:\n\tFEMat3dValuator* m_matAxis; // initial material axis\n};\n"
  },
  {
    "path": "FECore/FEDomain2D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDomain2D.h\"\n#include \"FEMesh.h\"\n#include \"FEMaterial.h\"\n\n//-----------------------------------------------------------------------------\nbool FEDomain2D::Create(int nelems, FE_Element_Spec espec)\n{\n\tm_Elem.resize(nelems);\n\tfor (int i = 0; i < nelems; ++i)\n\t{\n\t\tFEElement2D& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (espec.etype != FE_ELEM_INVALID_TYPE) \n\t\tfor (int i=0; i<nelems; ++i) m_Elem[i].SetType(espec.etype);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain2D::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tForEachMaterialPoint([&](FEMaterialPoint& mp) {\n\t\tmp.Update(timeInfo);\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomain2D::Reset()\n{\n\tForEachMaterialPoint([](FEMaterialPoint& mp) {\n\t\tmp.Init();\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the reference frame at\n//! integration point n. The inverse jacobian is return in Ji. The return value\n//! is the determinant of the jacobian (not the inverse!)\ndouble FEDomain2D::invjac0(FEElement2D& el, double Ji[2][2], int n)\n{\n    // initial nodal coordinates\n    int neln = el.Nodes();\n    double x[FEElement::MAX_NODES];\n    double y[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n    {\n        vec3d& ri = m_pMesh->Node(el.m_node[i]).m_r0;\n\t\tx[i] = ri.x;\n\t\ty[i] = ri.y;\n    }\n    \n\t// calculate Jacobian\n\tdouble J[2][2] = {0};\n\tfor (int i=0; i<neln; ++i)\n\t{\n\t\tconst double& Gri = el.Hr(n)[i];\n\t\tconst double& Gsi = el.Hs(n)[i];\n\t\t\n\t\tJ[0][0] += Gri*x[i]; J[0][1] += Gsi*x[i];\n\t\tJ[1][0] += Gri*y[i]; J[1][1] += Gsi*y[i];\n\t}\n\t\t\n\t// calculate the determinant\n\tdouble det =  J[0][0]*J[1][1] - J[0][1]*J[1][0];\n\t\t\n\t// make sure the determinant is positive\n\tif (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n\n\t// calculate the inverse jacobian\n\tdouble deti = 1.0 / det;\n\tJi[0][0] =  deti*J[1][1];\n\tJi[1][0] = -deti*J[1][0];\n\tJi[0][1] = -deti*J[0][1];\n\tJi[1][1] =  deti*J[0][0];\n\n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the reference frame at\n//! integration point n. The inverse jacobian is return in Ji. The return value\n//! is the determinant of the jacobian (not the inverse!)\ndouble FEDomain2D::invjact(FEElement2D& el, double Ji[2][2], int n)\n{\n    // number of nodes\n    int neln = el.Nodes();\n    \n    // nodal coordinates\n    double x[FEElement::MAX_NODES];\n    double y[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n    {\n        vec3d& ri = m_pMesh->Node(el.m_node[i]).m_rt;\n\t\tx[i] = ri.x;\n\t\ty[i] = ri.y;\n    }\n    \n\t// calculate Jacobian\n\tdouble J[2][2] = {0};\n\tfor (int i=0; i<neln; ++i)\n\t{\n\t\tconst double& Gri = el.Hr(n)[i];\n\t\tconst double& Gsi = el.Hs(n)[i];\n\t\t\n\t\tJ[0][0] += Gri*x[i]; J[0][1] += Gsi*x[i];\n\t\tJ[1][0] += Gri*y[i]; J[1][1] += Gsi*y[i];\n\t}\n\t\t\n\t// calculate the determinant\n\tdouble det =  J[0][0]*J[1][1] - J[0][1]*J[1][0];\n\t\t\n\t// make sure the determinant is positive\n\tif (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n\n\t// calculate the inverse jacobian\n\tdouble deti = 1.0 / det;\n\tJi[0][0] =  deti*J[1][1];\n\tJi[1][0] = -deti*J[1][0];\n\tJi[0][1] = -deti*J[0][1];\n\tJi[1][1] =  deti*J[0][0];    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate gradient of function at integration points\n//! A 2D element is assumed to have no variation through the thickness.\nvec2d FEDomain2D::gradient(FEElement2D& el, double* fn, int n)\n{\n    double Ji[2][2];\n    invjact(el, Ji, n);\n\t\t\t\t\n    double* Grn = el.Hr(n);\n    double* Gsn = el.Hs(n);\n    \n    double Gx, Gy;\n    \n    vec2d gradf(0,0);\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i];\n        Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i];\n        \n        // calculate gradient\n        gradf.x() += Gx*fn[i];\n        gradf.y() += Gy*fn[i];\n    }\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate gradient of function at integration points\n//! A 2D element is assumed to have no variation through the thickness.\nvec2d FEDomain2D::gradient(FEElement2D& el, vector<double>& fn, int n)\n{\n    double Ji[2][2];\n    invjact(el, Ji, n);\n\t\t\t\t\n    double* Grn = el.Hr(n);\n    double* Gsn = el.Hs(n);\n    \n    vec2d gradf(0,0);\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i];\n        double Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i];\n        \n        // calculate pressure gradient\n        gradf.x() += Gx*fn[i];\n        gradf.y() += Gy*fn[i];\n    }\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points\nmat2d FEDomain2D::gradient(FEElement2D& el, vec2d* fn, int n)\n{\n    double Ji[2][2];\n    invjact(el, Ji, n);\n\t\t\t\t\n    vec2d g1(Ji[0][0],Ji[0][1]);\n    vec2d g2(Ji[1][0],Ji[1][1]);\n    \n    double* Gr = el.Hr(n);\n    double* Gs = el.Hs(n);\n    \n    mat2d gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        vec2d tmp = g1*Gr[i] + g2*Gs[i];\n        gradf += dyad(fn[i], tmp);\n    }\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points\n//! A 2D element is assumed to have no variation through the thickness.\nmat3d FEDomain2D::gradient(FEElement2D& el, vec3d* fn, int n)\n{\n    double Ji[2][2];\n    invjact(el, Ji, n);\n\t\t\t\t\n    vec3d g1(Ji[0][0],Ji[0][1],0.0);\n    vec3d g2(Ji[1][0],Ji[1][1],0.0);\n    \n    double* Gr = el.Hr(n);\n    double* Gs = el.Hs(n);\n    \n    mat3d gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gradf += fn[i] & (g1*Gr[i] + g2*Gs[i]);\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to reference frame\ndouble FEDomain2D::detJ0(FEElement2D &el, int n)\n{\n    // initial nodal coordinates\n    int neln = el.Nodes();\n    double x[FEElement::MAX_NODES];\n    double y[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n    {\n        vec3d& ri = m_pMesh->Node(el.m_node[i]).m_r0;\n\t\tx[i] = ri.x;\n\t\ty[i] = ri.y;\n    }\n    \n\t// calculate Jacobian\n\tdouble J[2][2] = {0};\n\tfor (int i=0; i<neln; ++i)\n\t{\n\t\tconst double& Gri = el.Hr(n)[i];\n\t\tconst double& Gsi = el.Hs(n)[i];\n\t\tJ[0][0] += Gri*x[i]; J[0][1] += Gsi*x[i];\n\t\tJ[1][0] += Gri*y[i]; J[1][1] += Gsi*y[i];\n\t}\n\t\t\n\t// calculate the determinant\n\tdouble det =  J[0][0]*J[1][1] - J[0][1]*J[1][0];\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to current frame\ndouble FEDomain2D::detJt(FEElement2D &el, int n)\n{\n    // initial nodal coordinates\n    int neln = el.Nodes();\n    double x[FEElement::MAX_NODES];\n    double y[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i)\n    {\n        vec3d& ri = m_pMesh->Node(el.m_node[i]).m_rt;\n        x[i] = ri.x;\n        y[i] = ri.y;\n    }\n    \n    // calculate Jacobian\n    double J[2][2] = {0};\n    for (int i=0; i<neln; ++i)\n    {\n        const double& Gri = el.Hr(n)[i];\n        const double& Gsi = el.Hs(n)[i];\n        J[0][0] += Gri*x[i]; J[0][1] += Gsi*x[i];\n        J[1][0] += Gri*y[i]; J[1][1] += Gsi*y[i];\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*J[1][1] - J[0][1]*J[1][0];\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant basis vectors of a 2D element\n//! at an integration point\n\nvoid FEDomain2D::CoBaseVectors(FEElement2D& el, int j, vec2d g[2])\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hr = el.Hr(j);\n    double* Hs = el.Hs(j);\n    \n    g[0] = g[1] = vec2d(0,0);\n    for (int i=0; i<n; ++i)\n    {\n        vec2d rt = vec2d(m.Node(el.m_node[i]).m_rt.x,m.Node(el.m_node[i]).m_rt.y);\n        g[0] += rt*Hr[i];\n        g[1] += rt*Hs[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contravariant basis vectors of a 2D element\n//! at an integration point\n\nvoid FEDomain2D::ContraBaseVectors(FEElement2D& el, int j, vec2d gcnt[2])\n{\n    vec2d gcov[2];\n    CoBaseVectors(el, j, gcov);\n    \n    mat2d J = mat2d(gcov[0].x(), gcov[1].x(),\n                    gcov[0].y(), gcov[1].y());\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec2d(Ji(0,0),Ji(0,1));\n    gcnt[1] = vec2d(Ji(1,0),Ji(1,1));\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of covariant basis\n//! vectors of a 2D element at an integration point\n\nvoid FEDomain2D::CoBaseVectorDerivatives(FEElement2D& el, int j, vec2d dg[2][2])\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hrr = el.Hrr(j); double* Hrs = el.Hrs(j);\n    double* Hsr = el.Hsr(j); double* Hss = el.Hss(j);\n    \n    dg[0][0] = dg[0][1] = vec2d(0,0);  // derivatives of g[0]\n    dg[1][0] = dg[1][1] = vec2d(0,0);  // derivatives of g[1]\n    \n    for (int i=0; i<n; ++i)\n    {\n        vec2d rt = vec2d(m.Node(el.m_node[i]).m_rt.x,m.Node(el.m_node[i]).m_rt.y);\n        dg[0][0] += rt*Hrr[i]; dg[0][1] += rt*Hsr[i];\n        dg[1][0] += rt*Hrs[i]; dg[1][1] += rt*Hss[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of contravariant basis\n//! vectors of a solid element at an integration point\n\nvoid FEDomain2D::ContraBaseVectorDerivatives(FEElement2D& el, int j, vec2d dgcnt[2][2])\n{\n    vec2d gcnt[2];\n    vec2d dgcov[2][2];\n    ContraBaseVectors(el, j, gcnt);\n    CoBaseVectorDerivatives(el, j, dgcov);\n    \n    // derivatives of gcnt[0]\n    dgcnt[0][0] = -gcnt[0]*(gcnt[0]*dgcov[0][0])-gcnt[1]*(gcnt[0]*dgcov[1][0]);\n    dgcnt[0][1] = -gcnt[0]*(gcnt[0]*dgcov[0][1])-gcnt[1]*(gcnt[0]*dgcov[1][1]);\n    \n    // derivatives of gcnt[1]\n    dgcnt[1][0] = -gcnt[0]*(gcnt[1]*dgcov[0][0])-gcnt[1]*(gcnt[1]*dgcov[1][0]);\n    dgcnt[1][1] = -gcnt[0]*(gcnt[1]*dgcov[0][1])-gcnt[1]*(gcnt[1]*dgcov[1][1]);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the laplacian of a vector function at an integration point\nvec2d FEDomain2D::lapvec(FEElement2D& el, vec2d* fn, int n)\n{\n    vec2d gcnt[2];\n    vec2d dgcnt[2][2];\n    ContraBaseVectors(el, n, gcnt);\n    ContraBaseVectorDerivatives(el, n, dgcnt);\n\t\t\t\t\n    double* Gr = el.Hr(n);\n    double* Gs = el.Hs(n);\n    \n    // get the shape function derivatives\n    double* Hrr = el.Hrr(n); double* Hrs = el.Hrs(n);\n    double* Hsr = el.Hsr(n); double* Hss = el.Hss(n);\n    \n    vec2d lapv(0,0);\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        lapv += fn[i]*((gcnt[0]*Hrr[i]+dgcnt[0][0]*Gr[i])*gcnt[0]+\n                       (gcnt[0]*Hrs[i]+dgcnt[0][1]*Gr[i])*gcnt[1]+\n                       (gcnt[1]*Hsr[i]+dgcnt[1][0]*Gs[i])*gcnt[0]+\n                       (gcnt[1]*Hss[i]+dgcnt[1][1]*Gs[i])*gcnt[1]);\n    \n    return lapv;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the gradient of the divergence of a vector function at an integration point\nvec2d FEDomain2D::gradivec(FEElement2D& el, vec2d* fn, int n)\n{\n    vec2d gcnt[2];\n    vec2d dgcnt[2][2];\n    ContraBaseVectors(el, n, gcnt);\n    ContraBaseVectorDerivatives(el, n, dgcnt);\n\t\t\t\t\n    double* Gr = el.Hr(n);\n    double* Gs = el.Hs(n);\n    \n    // get the shape function derivatives\n    double* Hrr = el.Hrr(n); double* Hrs = el.Hrs(n);\n    double* Hsr = el.Hsr(n); double* Hss = el.Hss(n);\n    \n    vec2d gdv(0,0);\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gdv +=\n        gcnt[0]*((gcnt[0]*Hrr[i]+dgcnt[0][0]*Gr[i])*fn[i])+\n        gcnt[1]*((gcnt[0]*Hrs[i]+dgcnt[0][1]*Gr[i])*fn[i])+\n        gcnt[0]*((gcnt[1]*Hsr[i]+dgcnt[1][0]*Gs[i])*fn[i])+\n        gcnt[1]*((gcnt[1]*Hss[i]+dgcnt[1][1]*Gs[i])*fn[i]);\n    \n    return gdv;\n}\n"
  },
  {
    "path": "FECore/FEDomain2D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for shell elements\nclass FECORE_API FEDomain2D : public FEDomain\n{\n    FECORE_SUPER_CLASS(FEDOMAIN2D_ID)\n    FECORE_BASE_CLASS(FEDomain2D)\n\npublic:\n    //! constructor\n    FEDomain2D(FEModel* fem) : FEDomain(FE_DOMAIN_2D, fem) {}\n    \n    //! create storage for elements\n\tbool Create(int nsize, FE_Element_Spec espec) override;\n\n    //! return nr of elements\n    int Elements() const override { return (int)m_Elem.size(); }\n    \n    //! element access\n    FEElement2D& Element(int n) { return m_Elem[n]; }\n    FEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\n    int GetElementType() { return m_Elem[0].Type(); }\n    \n    //! Initialize elements\n    void PreSolveUpdate(const FETimeInfo& timeInfo) override;\n    \n    //! Reset element data\n    void Reset() override;\n    \n    // inverse jacobian with respect to reference frame\n    double invjac0(FEElement2D& el, double J[2][2], int n);\n    \n    // inverse jacobian with respect to current frame\n    double invjact(FEElement2D& el, double J[2][2], int n);\n    \n    //! calculate in-plane gradient of function at integration points\n    vec2d gradient(FEElement2D& el, double* fn, int n);\n    \n    //! calculate in-plane gradient of function at integration points\n    vec2d gradient(FEElement2D& el, vector<double>& fn, int n);\n\n    //! calculate in-plane gradient of vector function at integration points\n    mat2d gradient(FEElement2D& el, vec2d* fn, int n);\n    \n    //! calculate in-plane gradient of vector function at integration points\n    mat3d gradient(FEElement2D& el, vec3d* fn, int n);\n    \n    // jacobian with respect to reference frame\n    double detJ0(FEElement2D& el, int n);\n    \n    //! calculate jacobian in current frame\n    double detJt(FEElement2D& el, int n);\n    \n    //! calculates covariant basis vectors at an integration point\n    void CoBaseVectors(FEElement2D& el, int j, vec2d g[2]);\n    \n    //! calculates contravariant basis vectors at an integration point\n    void ContraBaseVectors(FEElement2D& el, int j, vec2d g[2]);\n    \n    //! calculates parametric derivatives of covariant basis vectors at an integration point\n    void CoBaseVectorDerivatives(FEElement2D& el, int j, vec2d dg[2][2]);\n    \n    //! calculates parametric derivatives of contravariant basis vectors at an integration point\n    void ContraBaseVectorDerivatives(FEElement2D& el, int j, vec2d dg[2][2]);\n    \n    //! calculate the laplacian of a vector function at an integration point\n    vec2d lapvec(FEElement2D& el, vec2d* fn, int n);\n    \n    //! calculate the gradient of the divergence of a vector function at an integration point\n    vec2d gradivec(FEElement2D& el, vec2d* fn, int n);\n    \nprotected:\n    vector<FEElement2D>\tm_Elem;\t//!< array of elements\n};\n"
  },
  {
    "path": "FECore/FEDomainList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDomainList.h\"\n#include \"DumpStream.h\"\n#include \"FEDomain.h\"\n#include \"FEMesh.h\"\n#include <assert.h>\nusing namespace std;\n\nFEDomainList::FEDomainList()\n{\n\n}\n\nFEDomainList::FEDomainList(FEDomainList& domList)\n{\n\tm_dom = domList.m_dom;\n}\n\nFEDomainList::FEDomainList(std::vector<FEDomain*> domList)\n{\n\tm_dom = domList;\n}\n\n//! Clear the domain list\nvoid FEDomainList::Clear()\n{\n\tm_dom.clear();\n}\n\nvoid FEDomainList::AddDomain(FEDomain* dom)\n{\n\t// see if this domain is already a member of this list\n\tif (IsMember(dom))\n\t{\n//\t\tassert(false);\n\t\treturn;\n\t}\n\n\t// it's not, so let's add it\n\tm_dom.push_back(dom);\n}\n\n//! Add a domain list\nvoid FEDomainList::AddDomainList(const FEDomainList& domList)\n{\n\tfor (int i = 0; i < domList.Domains(); ++i)\n\t{\n\t\tFEDomain* d = const_cast<FEDomain*>(domList.GetDomain(i));\n\t\tAddDomain(d);\n\t}\n}\n\nbool FEDomainList::IsMember(const FEDomain* dom) const\n{\n\t// loop over all the domains\n\tfor (size_t i = 0; i < m_dom.size(); ++i)\n\t{\n\t\tif (m_dom[i] == dom)\n\t\t{\n\t\t\t// found it!\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// better luck next time!\n\treturn false;\n}\n\n//! serialization\nvoid FEDomainList::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_dom;\n}\n"
  },
  {
    "path": "FECore/FEDomainList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <vector>\n#include <string>\n#include \"fecore_api.h\"\n\nclass FEDomain;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n// Class that represents a list of domains. \nclass FECORE_API FEDomainList\n{\npublic:\n\t//! constructor\n\tFEDomainList();\n\tFEDomainList(FEDomainList& domList);\n\tFEDomainList(std::vector<FEDomain*> domList);\n\n\t//! Clear the domain list\n\tvoid Clear();\n\n\t//! Add a domain to the list\n\tvoid AddDomain(FEDomain* dom);\n\n\t//! Add a domain list\n\tvoid AddDomainList(const FEDomainList& domList);\n\n\t//! See if a domain is a member of this list\n\tbool IsMember(const FEDomain* dom) const;\n\n\t//! See if the list is empty\n\tbool IsEmpty() const { return m_dom.empty(); }\n\n\t//! Return number of domains in list\n\tint Domains() const { return (int) m_dom.size();  }\n\n\t//! Return a domain\n\tFEDomain* GetDomain(int i) { return m_dom[i]; }\n\tconst FEDomain* GetDomain(int i) const { return m_dom[i]; }\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar);\n\n\tsize_t size() const { return m_dom.size(); }\n\tFEDomain* operator [] (size_t n) { return m_dom[n]; }\n\npublic:\n\tvoid SetName(const std::string& name) { m_name = name; }\n\tconst std::string& GetName() const { return m_name; }\n\nprivate:\n\tstd::string m_name;\n\tstd::vector<FEDomain*>\tm_dom;\t\t// the actual list of domains\n};\n"
  },
  {
    "path": "FECore/FEDomainMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEDomainMap.h\"\n#include \"FEMesh.h\"\n#include \"DumpStream.h\"\n#include \"mathalg.h\"\n\n//-----------------------------------------------------------------------------\nFEDomainMap::FEDomainMap() : FEDataMap(FE_DOMAIN_MAP)\n{\n\tm_maxElemNodes = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEDomainMap::FEDomainMap(FEDataType dataType, Storage_Fmt format) : FEDataMap(FE_DOMAIN_MAP, dataType)\n{\n\tm_fmt = format;\n\tm_maxElemNodes = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEDomainMap::FEDomainMap(const FEDomainMap& map) : FEDataMap(map)\n{\n\tm_name = map.m_name;\n\tm_maxElemNodes = map.m_maxElemNodes;\n}\n\n//-----------------------------------------------------------------------------\nFEDomainMap& FEDomainMap::operator = (const FEDomainMap& map)\n{\n\tFEDataArray::operator=(map);\n\tm_name = map.m_name;\n\tm_maxElemNodes = map.m_maxElemNodes;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\n// return the item list associated with this map\nFEItemList* FEDomainMap::GetItemList()\n{\n\treturn m_elset;\n}\n\n//-----------------------------------------------------------------------------\nbool FEDomainMap::Create(FEElementSet* ps, double val)\n{\n\tm_elset = ps;\n\tint NE = ps->Elements();\n\tFEMesh* mesh = ps->GetMesh();\n\tm_maxElemNodes = 0;\n\n\tif (m_fmt == FMT_MULT)\n\t{\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = *mesh->FindElementFromID((*ps)[i]);\n\t\t\tint ne = el.Nodes();\n\t\t\tif (ne > m_maxElemNodes) m_maxElemNodes = ne;\n\t\t}\n\t\treturn resize(NE*m_maxElemNodes, val);\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tm_maxElemNodes = 1;\n\t\treturn resize(NE, val);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\tFENodeList nodeList = ps->GetNodeList();\n\t\tint nodes = nodeList.Size();\n\n\t\t// find min, max index\n\t\tm_imin = mesh->Nodes();\n\t\tint imax = -1;\n\t\tfor (int i = 0; i < nodes; ++i)\n\t\t{\n\t\t\tint nid = nodeList[i];\n\t\t\tif (nid < m_imin) m_imin = nid;\n\t\t\tif (nid > imax) imax = nid;\n\t\t}\n\n\t\t// build lookup table\n\t\tm_NLT.resize(imax - m_imin + 1, -1);\n\t\tfor (int i = 0; i < nodes; ++i)\n\t\t{\n\t\t\tint nid = nodeList[i];\n\t\t\tm_NLT[nid - m_imin] = i;\n\t\t}\n\t\t\n\t\treturn resize(nodes, val);\n\t}\n\telse if (m_fmt == FMT_MATPOINTS)\n\t{\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = *mesh->FindElementFromID((*ps)[i]);\n\t\t\tint ne = el.GaussPoints();\n\t\t\tif (ne > m_maxElemNodes) m_maxElemNodes = ne;\n\t\t}\n\t\treturn resize(NE*m_maxElemNodes, val);\n\t}\n\telse return false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::setValue(int n, double v)\n{\n\tif (m_fmt == FMT_MULT)\n\t{\n\t\tint index = n*m_maxElemNodes;\n\t\tfor (int i = 0; i < m_maxElemNodes; ++i) set<double>(index + i, v);\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tset<double>(n, v);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\tset<double>(n, v);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::setValue(int n, const vec2d& v)\n{\n\tif (m_fmt == FMT_MULT)\n\t{\n\t\tint index = n*m_maxElemNodes;\n\t\tfor (int i = 0; i < m_maxElemNodes; ++i) set<vec2d>(index + i, v);\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tset<vec2d>(n, v);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\tset<vec2d>(n, v);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::setValue(int n, const vec3d& v)\n{\n\tif ((m_fmt == FMT_MULT) || (m_fmt == FMT_MATPOINTS))\n\t{\n\t\tint index = n*m_maxElemNodes;\n\t\tfor (int i = 0; i < m_maxElemNodes; ++i) set<vec3d>(index + i, v);\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tset<vec3d>(n, v);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\tset<vec3d>(n, v);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::setValue(int n, const mat3d& v)\n{\n\tif ((m_fmt == FMT_MULT) || (m_fmt == FMT_MATPOINTS))\n\t{\n\t\tint index = n*m_maxElemNodes;\n\t\tfor (int i = 0; i < m_maxElemNodes; ++i) set<mat3d>(index + i, v);\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tset<mat3d>(n, v);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\tset<mat3d>(n, v);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::setValue(int n, const mat3ds& v)\n{\n\tif ((m_fmt == FMT_MULT) || (m_fmt == FMT_MATPOINTS))\n\t{\n\t\tint index = n * m_maxElemNodes;\n\t\tfor (int i = 0; i < m_maxElemNodes; ++i) set<mat3ds>(index + i, v);\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tset<mat3ds>(n, v);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\tset<mat3ds>(n, v);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::fillValue(double v)\n{\n\tset<double>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::fillValue(const vec2d& v)\n{\n\tset<vec2d>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::fillValue(const vec3d& v)\n{\n\tset<vec3d>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::fillValue(const mat3d& v)\n{\n\tset<mat3d>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::fillValue(const mat3ds& v)\n{\n\tset<mat3ds>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::Serialize(DumpStream& ar)\n{\n\tFEDataArray::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_maxElemNodes;\n\tar & m_name;\n\tar & m_elset;\n\tar & m_fmt;\n\tar & m_NLT;\n\tar & m_imin;\n}\n\n//-----------------------------------------------------------------------------\n//! get the value at a material point\ndouble FEDomainMap::value(const FEMaterialPoint& pt)\n{\n\t// get the element this material point is in\n\tFEElement* pe = pt.m_elem;\n\tassert(pe);\n\n\t// see if this element belongs to the element set\n\tassert(m_elset);\n\tint lid = m_elset->GetLocalIndex(*pe);\n\tassert((lid >= 0));\n\n\tdouble v = 0.0;\n\tif (m_fmt == FMT_MULT)\n\t{\n\t\t// get shape functions\n\t\tdouble* H = pe->H(pt.m_index);\n\n\t\tint ne = pe->Nodes();\n\t\tfor (int i = 0; i < ne; ++i)\n\t\t{\n\t\t\tdouble vi = value<double>(lid, i);\n\t\t\tv += vi*H[i];\n\t\t}\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\t// get shape functions\n\t\tdouble* H = pe->H(pt.m_index);\n\n\t\tint ne = pe->Nodes();\n\t\tfor (int i = 0; i < ne; ++i)\n\t\t{\n\t\t\tint n = pe->m_node[i];\n\t\t\tint m = m_NLT[n - m_imin];\n\t\t\tdouble vi = value<double>(0, m);\n\t\t\tv += vi * H[i];\n\t\t}\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\tv = get<double>(lid);\n\t}\n\telse if (m_fmt == FMT_MATPOINTS)\n\t{\n\t\tv = value<double>(lid, pt.m_index);\n\t}\n\telse { assert(false); }\n\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\n//! get the value at a material point\nvec3d FEDomainMap::valueVec3d(const FEMaterialPoint& pt)\n{\n\t// get the element this material point is in\n\tFEElement* pe = pt.m_elem;\n\tassert(pe);\n\n\t// see if this element belongs to the element set\n\tassert(m_elset);\n\tint lid = m_elset->GetLocalIndex(*pe);\n\tassert((lid >= 0));\n\n\tvec3d v(0, 0, 0);\n\tif (m_fmt == FMT_MULT)\n\t{\n\t\t// get shape functions\n\t\tdouble* H = pe->H(pt.m_index);\n\t\tint ne = pe->Nodes();\n\t\tfor (int i = 0; i < ne; ++i)\n\t\t{\n\t\t\tvec3d vi = value<vec3d>(lid, i);\n\t\t\tv += vi*H[i];\n\t\t}\n\t}\n\telse if (m_fmt == FMT_ITEM)\n\t{\n\t\treturn get<vec3d>(lid);\n\t}\n\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\n//! get the value at a material point\nmat3d FEDomainMap::valueMat3d(const FEMaterialPoint& pt)\n{\n\tassert(DataType() == FEDataType::FE_MAT3D);\n\t// get the element this material point is in\n\tFEElement* pe = pt.m_elem;\n\tassert(pe);\n\n\t// see if this element belongs to the element set\n\tassert(m_elset);\n\tint lid = m_elset->GetLocalIndex(*pe);\n\tassert((lid >= 0));\n\n\tmat3d Q;\n\tif (m_fmt == FMT_ITEM)\n\t{\n\t\tQ = get<mat3d>(lid);\n\t}\n\telse if (m_fmt == FMT_MATPOINTS)\n\t{\n\t\tQ = value<mat3d>(lid, pt.m_index);\n\t}\n\telse { assert(false); }\n\n\treturn Q;\n}\n\n//-----------------------------------------------------------------------------\n//! get the value at a material point\nmat3ds FEDomainMap::valueMat3ds(const FEMaterialPoint& pt)\n{\n\tassert(DataType() == FEDataType::FE_MAT3DS);\n\t// get the element this material point is in\n\tFEElement* pe = pt.m_elem;\n\tassert(pe);\n\n\t// see if this element belongs to the element set\n\tassert(m_elset);\n\tint lid = m_elset->GetLocalIndex(*pe);\n\tassert((lid >= 0));\n\n\tmat3ds Q;\n\tif (m_fmt == FMT_ITEM)\n\t{\n\t\tQ = get<mat3ds>(lid);\n\t}\n\telse if (m_fmt == FMT_NODE)\n\t{\n\t\t// calculate shape function values\n\t\tdouble* w = pe->H(pt.m_index);\n\t\tmat3ds Qi[FEElement::MAX_NODES];\n\t\tint ne = pe->Nodes();\n\t\tfor (int i = 0; i < ne; ++i)\n\t\t{\n\t\t\tint nid = pe->m_node[i];\n\n\t\t\tint lid = m_NLT[nid] - m_imin; \n\t\t\tassert((lid >= 0) && (lid < DataCount()));\n\n\t\t\tQi[i] = get<mat3ds>(lid);\n\t\t}\n\n\t\t// weighted average\n\t\tQ = weightedAverageStructureTensor(Qi, w, ne);\n\t}\n\telse if (m_fmt == FMT_MATPOINTS)\n\t{\n\t\treturn value<mat3ds>(lid, pt.m_index);\n\t}\n\n\treturn Q;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEDomainMap::NodalValue(int nid)\n{\n\tif (StorageFormat() != FMT_NODE) { assert(false); return 0.0; }\n\tint m = m_NLT[nid - m_imin];\n\tdouble v = value<double>(0, m);\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\n// merge with another map\nbool FEDomainMap::Merge(FEDomainMap& map)\n{\n\t// make sure the type and format are the same\n\tif (map.DataType() != DataType()) return false;\n\tif (map.StorageFormat() != map.StorageFormat()) return false;\n\n\t// get the two element sets\n\tFEElementSet* set1 = m_elset;\n\tconst FEElementSet* set2 = map.GetElementSet();\n\tassert(set1->GetMesh() == set2->GetMesh());\n\n\t// create a new element set\n\t// TODO: should we add it to the mesh? I think we probably have to for remeshing\n\tFEElementSet* elset = new FEElementSet(set1->GetMesh());\n\telset->Add(*set1);\n\telset->Add(*set2);\n\n\t// reallocate the data array\n\tint oldElems = set1->Elements();\n\tint newElems = elset->Elements();\n\n\tif (StorageFormat() == FMT_MULT)\n\t{\n\t\tif (DataType() == FE_DOUBLE)\n\t\t{\n\t\t\t// get the max element nodes\n\t\t\tint maxElemNodes = m_maxElemNodes;\n\t\t\tif (map.m_maxElemNodes > maxElemNodes) maxElemNodes = map.m_maxElemNodes;\n\t\t\tRealloc(newElems, maxElemNodes);\n\n\t\t\t// set the new values of the map\n\t\t\tfor (int i = 0; i < set2->Elements(); ++i)\n\t\t\t{\n\t\t\t\tint n = set2->Element(i).Nodes();\n\t\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t\t{\n\t\t\t\t\tdouble v = map.value<double>(i, j);\n\t\t\t\t\tsetValue<double>(oldElems + i, j, v);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (DataType() == FE_VEC3D)\n\t\t{\n\t\t\t// get the max element nodes\n\t\t\tint maxElemNodes = m_maxElemNodes;\n\t\t\tif (map.m_maxElemNodes > maxElemNodes) maxElemNodes = map.m_maxElemNodes;\n\t\t\tRealloc(newElems, maxElemNodes);\n\n\t\t\t// set the new values of the map\n\t\t\tfor (int i = 0; i < set2->Elements(); ++i)\n\t\t\t{\n\t\t\t\tint n = set2->Element(i).Nodes();\n\t\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t\t{\n\t\t\t\t\tvec3d v = map.value<vec3d>(i, j);\n\t\t\t\t\tsetValue<vec3d>(oldElems + i, j, v);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse assert(false);\n\t}\n\telse if (StorageFormat() == FMT_MATPOINTS)\n\t{\n\t\tassert(DataType() == FE_DOUBLE);\n\n\t\t// get the max element nodes\n\t\tint maxElemNodes = m_maxElemNodes;\n\t\tif (map.m_maxElemNodes > maxElemNodes) maxElemNodes = map.m_maxElemNodes;\n\t\tRealloc(newElems, maxElemNodes);\n\n\t\t// set the new values of the map\n\t\tfor (int i = 0; i < set2->Elements(); ++i)\n\t\t{\n\t\t\tint n = set2->Element(i).GaussPoints();\n\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t{\n\t\t\t\tdouble v = map.value<double>(i, j);\n\t\t\t\tsetValue<double>(oldElems + i, j, v);\n\t\t\t}\n\t\t}\n\t}\n\telse if (StorageFormat() == FMT_ITEM)\n\t{\n\t\trealloc(newElems);\n\n\t\tswitch (DataType())\n\t\t{\n\t\tcase FE_DOUBLE:\n\t\t{\n\t\t\t// set the new values of the map\n\t\t\tfor (int i = 0; i < set2->Elements(); ++i)\n\t\t\t{\n\t\t\t\tdouble v = map.get<double>(i);\n\t\t\t\tset<double>(oldElems + i, v);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t\tcase FE_VEC3D:\n\t\t{\n\t\t\t// set the new values of the map\n\t\t\tfor (int i = 0; i < set2->Elements(); ++i)\n\t\t\t{\n\t\t\t\tvec3d v = map.get<vec3d>(i);\n\t\t\t\tset<vec3d>(oldElems + i, v);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t\tcase FE_MAT3D:\n\t\t{\n\t\t\t// set the new values of the map\n\t\t\tfor (int i = 0; i < set2->Elements(); ++i)\n\t\t\t{\n\t\t\t\tmat3d v = map.get<mat3d>(i);\n\t\t\t\tset<mat3d>(oldElems + i, v);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t}\n\n\tm_elset = elset;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDomainMap::Realloc(int newElemSize, int newMaxElemNodes)\n{\n\tint oldMaxElemNodes = m_maxElemNodes;\n\tassert(newMaxElemNodes >= oldMaxElemNodes);\n\tif (newMaxElemNodes == oldMaxElemNodes) realloc(newElemSize*newMaxElemNodes);\n\telse\n\t{\n\t\tFEDomainMap oldData(*this);\n\t\tm_maxElemNodes = newMaxElemNodes;\n\t\trealloc(newElemSize*newMaxElemNodes);\n\t\tFEElementSet* set = m_elset;\n\t\tfor (int i = 0; i < set->Elements(); ++i)\n\t\t{\n\t\t\tint n = set->Element(i).Nodes();\n\t\t\tif (StorageFormat() == FMT_MATPOINTS) n = set->Element(i).GaussPoints();\n\n\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t{\n\t\t\t\tdouble v = oldData.get<double>(i*oldMaxElemNodes + j);\n\t\t\t\tsetValue<double>(i, j, v);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FEDomainMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDataMap.h\"\n#include \"FEDomain.h\"\n#include \"FEElementSet.h\"\n#include \"FEMaterialPoint.h\"\n\nclass FECORE_API FEDomainMap : public FEDataMap\n{\npublic:\n\t//! default constructor\n\tFEDomainMap();\n\tFEDomainMap(FEDataType dataType, Storage_Fmt format = FMT_MULT);\n\n\t//! copy constructor\n\tFEDomainMap(const FEDomainMap& map);\n\n\t//! assignment operator\n\tFEDomainMap& operator = (const FEDomainMap& map);\n\n\t//! Create a surface data map for this surface\n\tbool Create(FEElementSet* ps, double val = 0.0);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! get the value at a material point\n\tdouble value(const FEMaterialPoint& pt) override;\n\tvec3d valueVec3d(const FEMaterialPoint& pt) override;\n\tmat3d valueMat3d(const FEMaterialPoint& pt) override;\n\tmat3ds valueMat3ds(const FEMaterialPoint& pt) override;\n\n\t//! get the value at a node (only works with FMT_NODE!!)\n\tdouble NodalValue(int nid);\n\n\t//! Get the element set\n\tconst FEElementSet* GetElementSet() const { return m_elset; }\n\n\t//! return max nr of nodes\n\tint MaxNodes() const { return m_maxElemNodes; }\n\n\t//! return storage format\n\tint\tStorageFormat() const { return m_fmt; }\n\n\t// return the item list associated with this map\n\tFEItemList* GetItemList() override;\n\n\t// merge with another map\n\tbool Merge(FEDomainMap& map);\n\npublic:\n\ttemplate <typename T> T value(int nelem, int node)\n\t{\n\t\treturn get<T>(nelem*m_maxElemNodes + node);\n\t}\n\n\ttemplate <typename T> void setValue(int nelem, int node, const T& v)\n\t{\n\t\tset<T>(nelem*m_maxElemNodes + node, v);\n\t}\n\n\tvoid setValue(int n, double v) override;\n\tvoid setValue(int n, const vec2d& v) override;\n\tvoid setValue(int n, const vec3d& v) override;\n\tvoid setValue(int n, const mat3d& v) override;\n\tvoid setValue(int n, const mat3ds& v) override;\n\n\tvoid fillValue(double v) override;\n\tvoid fillValue(const vec2d& v) override;\n\tvoid fillValue(const vec3d& v) override;\n\tvoid fillValue(const mat3d& v) override;\n\tvoid fillValue(const mat3ds& v) override;\n\nprivate:\n\tvoid Realloc(int newElemSize, int newMaxElemNodes);\n\nprivate:\n\tint\t\t\t\t\tm_fmt;\t\t\t\t//!< storage format\n\tint\t\t\t\t\tm_maxElemNodes;\t\t//!< max number of nodes for each element\n\tFEElementSet*\t\tm_elset;\t\t\t//!< the element set on which this map is defined\n\n\tvector<int>\t\tm_NLT;\t\t//!< node index lookup table for FMT_NODE\n\tint\t\t\t\tm_imin;\t\t//!< min index for lookup for FMT_NODE\n};\n"
  },
  {
    "path": "FECore/FEDomainParameter.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEDomainParameter.h\"\n\nFEDomainParameter::FEDomainParameter(const std::string& name) : m_name(name)\n{\n\n}\n\nFEDomainParameter::~FEDomainParameter()\n{\n\n}\n\nvoid FEDomainParameter::setName(const std::string& name)\n{\n\tm_name = name;\n}\n\nconst std::string& FEDomainParameter::name() const\n{\n\treturn m_name;\n}\n"
  },
  {
    "path": "FECore/FEDomainParameter.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEParam.h\"\n#include \"FEMaterialPoint.h\"\n\n//! The domain parameter is a mechanism for accessing material point data\n//! indirectly through the domain. \n//! Domains keep lists of domain parameters that can be queried.\n//! Notice that these classes return FEParamValue so that they can be used in optimization\nclass FECORE_API FEDomainParameter\n{\npublic:\n\tFEDomainParameter(const std::string& name);\n\tvirtual ~FEDomainParameter();\n\n\tvoid setName(const std::string& name);\n\tconst std::string& name() const;\n\n\t//! derived classes must override this function\n\tvirtual FEParamValue value(FEMaterialPoint& mp) = 0;\n\nprivate:\n\tstd::string\tm_name;\n};\n"
  },
  {
    "path": "FECore/FEEdge.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEdge.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFEEdge::FEEdge(FEModel* fem) : FEMeshPartition(FE_DOMAIN_EDGE, fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEEdge::~FEEdge()\n{\n}\n\n//-----------------------------------------------------------------------------\nFENodeList FEEdge::GetNodeList()\n{\n\tFEMesh* pm = GetMesh();\n\tFENodeList set(pm);\n\n\tvector<int> tag(pm->Nodes(), 0);\n\tfor (int i = 0; i<Elements(); ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j<ne; ++j)\n\t\t{\n\t\t\tif (tag[el.m_node[j]] == 0)\n\t\t\t{\n\t\t\t\tset.Add(el.m_node[j]);\n\t\t\t\ttag[el.m_node[j]] = 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn set;\n}\n\n//-----------------------------------------------------------------------------\nbool FEEdge::Init()\n{\n\t// make sure that there is an edge defined\n\tif (Elements() == 0) return false;\n\n\t// get the mesh to which this edge belongs\n\tFEMesh& mesh = *GetMesh();\n\n\t// This array is used to keep tags on each node\n\tvector<int> tag; tag.assign(mesh.Nodes(), -1);\n\n\t// let's find all nodes the edge needs\n\tint nn = 0;\n\tint ne = Elements();\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\t\tel.SetLocalID(i);\n\n\t\tfor (int j=0; j<el.Nodes(); ++j)\n\t\t{\n\t\t\t// get the global node number\n\t\t\tint m = el.m_node[j];\n\t\t\n\t\t\t// create a local node number\n\t\t\tif (tag[m] == -1) tag[m] = nn++;\n\n\t\t\t// set the local node number\n\t\t\tel.m_lnode[j] = tag[m];\n\t\t}\n\t}\n\n\t// allocate node index table\n\tm_Node.resize(nn);\n\n\t// fill the node index table\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tif (tag[i] >= 0)\n\t\t{\n\t\t\tm_Node[tag[i]] = i;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEdge::Create(int nelems, int elemType)\n{ \n\tm_Elem.resize(nelems); \n\tfor (int i = 0; i < nelems; ++i)\n\t{\n\t\tFELineElement& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (elemType != -1)\n\t{\n\t\tfor (int i = 0; i < nelems; ++i) m_Elem[i].SetType(elemType);\n\t\tCreateMaterialPointData();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEEdge::Create(FESegmentSet& es)\n{\n\treturn Create(es, FE_LINE2G1);\n}\n\n//-----------------------------------------------------------------------------\nbool FEEdge::Create(FESegmentSet& es, int elemType)\n{\n\tFEMesh& m = *GetMesh();\n\tint NN = m.Nodes();\n\n\t// count nr of segments\n\tint nsegs = es.Segments();\n\n\t// allocate storage for faces\n\tCreate(nsegs);\n\n\t// read segments\n\tfor (int i = 0; i < nsegs; ++i)\n\t{\n\t\tFELineElement& el = Element(i);\n\t\tFESegmentSet::SEGMENT& si = es.Segment(i);\n\n\t\tif (si.ntype == 2) el.SetType(elemType);\n\t\telse return false;\n\n\t\tint N = el.Nodes(); assert(N == si.ntype);\n\t\tfor (int j = 0; j < N; ++j) el.m_node[j] = si.node[j];\n\t}\n\n\tCreateMaterialPointData();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEdge::CreateMaterialPointData()\n{\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFELineElement& el = m_Elem[i];\n\t\tint nint = el.GaussPoints();\n\t\tel.ClearData();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFELineMaterialPoint* pt = dynamic_cast<FELineMaterialPoint*>(CreateMaterialPoint());\n\t\t\tassert(pt);\n\t\t\tel.SetMaterialPointData(pt, n);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Create material point data for this surface\nFEMaterialPoint* FEEdge::CreateMaterialPoint()\n{\n\treturn new FELineMaterialPoint;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEdge::GetNodalCoordinates(FELineElement& el, vec3d* rt)\n{\n\tFEMesh& mesh = *GetMesh();\n\tint neln = el.Nodes();\n\tfor (int j = 0; j < neln; ++j) rt[j] = mesh.Node(el.m_node[j]).m_rt;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEEdge::GetReferenceNodalCoordinates(FELineElement& el, vec3d* rt)\n{\n\tFEMesh& mesh = *GetMesh();\n\tint neln = el.Nodes();\n\tfor (int j = 0; j < neln; ++j) rt[j] = mesh.Node(el.m_node[j]).m_r0;\n}\n"
  },
  {
    "path": "FECore/FEEdge.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMeshPartition.h\"\n#include \"FENodeList.h\"\n#include \"FESegmentSet.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FELineMaterialPoint : public FEMaterialPoint\n{\npublic:\n\t// return the surface element\n\tFELineElement* LineElement() { return (FELineElement*)m_elem; }\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tFEMaterialPoint::Serialize(ar);\n\t}\n};\n\n//-----------------------------------------------------------------------------\n// This class represents an edge of a domain.\nclass FECORE_API FEEdge : public FEMeshPartition\n{\npublic:\n\tFECORE_SUPER_CLASS(FEEDGE_ID)\n\tFECORE_BASE_CLASS(FEEdge)\n\npublic:\n\t//! constructor\n\tFEEdge(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FEEdge();\n\n\t//! initialize edge data structure\n\tvirtual bool Init() override;\n\n\t//! creates edge\n\tvoid Create(int nelems, int elemType = -1);\n\n\t//! create from edge set\n\tvirtual bool Create(FESegmentSet& es);\n\n\t//! extract node set\n\tFENodeList GetNodeList();\n\npublic:\n\n\t//! return number of edge elements\n\tint Elements() const override { return (int)m_Elem.size(); }\n\n\t//! return an element of the edge\n\tFELineElement& Element(int i) { return m_Elem[i]; }\n\n\t//! returns reference to element\n\tFEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\n\t// Create material point data for this surface\n\tvirtual FEMaterialPoint* CreateMaterialPoint();\n\n\tvoid CreateMaterialPointData();\n\npublic:\n\t// Get current coordinates\n\tvoid GetNodalCoordinates(FELineElement& el, vec3d* rt);\n\n\t// Get reference coordinates\n\tvoid GetReferenceNodalCoordinates(FELineElement& el, vec3d* rt);\n\nprotected:\n\tbool Create(FESegmentSet& es, int elemType);\n\nprotected:\n\tstd::vector<FELineElement>\tm_Elem;\n};\n"
  },
  {
    "path": "FECore/FEEdgeList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEdgeList.h\"\n#include \"FEMesh.h\"\n#include \"FENodeNodeList.h\"\n#include \"FEDomain.h\"\n#include \"FEElementList.h\"\n#include <set>\nusing namespace std;\n\nFEEdgeList::FEEdgeList() : m_mesh(nullptr)\n{\n\n}\n\nint FEEdgeList::Edges() const\n{\n\treturn (int) m_edgeList.size();\n}\n\nconst FEEdgeList::EDGE& FEEdgeList::operator[] (int i)\n{\n\treturn m_edgeList[i];\n}\n\nconst FEEdgeList::EDGE& FEEdgeList::Edge(int i) const\n{\n\treturn m_edgeList[i];\n}\n\nFEMesh* FEEdgeList::GetMesh()\n{\n\treturn m_mesh;\n}\n\nstruct edge_less\n{\n\tbool operator ()(const pair<int, int>& lhs, const pair<int, int>& rhs) const\n\t{\n\t\tint na0 = lhs.first;\n\t\tint na1 = lhs.second;\n\t\tif (na0 > na1) { int tmp = na0; na0 = na1; na1 = tmp; }\n\n\t\tint nb0 = rhs.first;\n\t\tint nb1 = rhs.second;\n\t\tif (nb0 > nb1) { int tmp = nb0; nb0 = nb1; nb1 = tmp; }\n\n\t\tif (na0 != nb0) return na0 < nb0;\n\t\telse return na1 < nb1;\n\t}\n};\n\nstruct EDGE_less\n{\n\tbool operator ()(const FEEdgeList::EDGE& lhs, const FEEdgeList::EDGE& rhs) const\n\t{\n\t\tint na0 = lhs.node[0];\n\t\tint na1 = lhs.node[1];\n\t\tif (na0 > na1) { int tmp = na0; na0 = na1; na1 = tmp; }\n\n\t\tint nb0 = rhs.node[0];\n\t\tint nb1 = rhs.node[1];\n\t\tif (nb0 > nb1) { int tmp = nb0; nb0 = nb1; nb1 = tmp; }\n\n\t\tif (na0 != nb0) return na0 < nb0;\n\t\telse return na1 < nb1;\n\t}\n};\n\n\nbool FEEdgeList::Create(FEMesh* pmesh)\n{\n\tif (pmesh == nullptr) return false;\n\tm_mesh = pmesh;\n\tFEMesh& mesh = *pmesh;\n\n\t// create the node-node list\n\tFEElementList elemList(mesh);\n\n\tset<pair<int, int>, edge_less> edgeSet;\n\n\tconst int ETET[ 6][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 0, 3 },{ 1, 3 },{ 2, 3 } };\n\tconst int EHEX[12][2] = { { 0, 1 },{ 1, 2 },{ 2, 3 },{ 3, 0 },{ 4, 5 },{ 5, 6 },{ 6, 7 },{ 7, 4 },{ 0, 4 },{ 1, 5 },{ 2, 6 },{ 3, 7 } };\n\tconst int EPEN[ 9][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 3, 4 },{ 4, 5 },{ 5, 3 },{ 0, 3 },{ 1, 4 },{ 2, 5 } };\n\n\tfor (FEElementList::iterator it = elemList.begin(); it != elemList.end(); ++it)\n\t{\n\t\tFEElement& el = *it;\n\n\t\tif ((el.Shape() == ET_TET4) || (el.Shape() == ET_TET5))\n\t\t{\n\t\t\tfor (int i = 0; i < 6; ++i)\n\t\t\t{\n\t\t\t\tpair<int, int> edge;\n\t\t\t\tedge.first = el.m_node[ETET[i][0]];\n\t\t\t\tedge.second = el.m_node[ETET[i][1]];\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == ET_HEX8)\n\t\t{\n\t\t\tfor (int i = 0; i < 12; ++i)\n\t\t\t{\n\t\t\t\tpair<int, int> edge;\n\t\t\t\tedge.first = el.m_node[EHEX[i][0]];\n\t\t\t\tedge.second = el.m_node[EHEX[i][1]];\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == ET_PENTA6)\n\t\t{\n\t\t\tfor (int i = 0; i < 9; ++i)\n\t\t\t{\n\t\t\t\tpair<int, int> edge;\n\t\t\t\tedge.first  = el.m_node[EPEN[i][0]];\n\t\t\t\tedge.second = el.m_node[EPEN[i][1]];\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t}\n\n\t// copy set into a vector\n\tsize_t edges = edgeSet.size();\n\tm_edgeList.resize(edges);\n\tset< pair<int, int>, edge_less>::iterator edgeIter = edgeSet.begin();\n\tfor (size_t i = 0; i < edges; ++i, ++edgeIter)\n\t{\n\t\tconst pair<int, int>& edge = *edgeIter;\n\t\tEDGE& Edge = m_edgeList[i];\n\t\tEdge.ntype = 2;\n\t\tint n0 = edge.first;\n\t\tint n1 = edge.second;\n\t\tif (n0 > n1) { int tmp = n0; n0 = n1; n1 = tmp; }\n\t\tEdge.node[0] = n0;\n\t\tEdge.node[1] = n1;\n\t}\n\n\treturn true;\n}\n\nbool FEEdgeList::Create(FEDomain* dom)\n{\n\tif (dom == nullptr) return false;\n\tm_mesh = dom->GetMesh();\n\tFEMesh& mesh = *m_mesh;\n\n\tset<EDGE, EDGE_less> edgeSet;\n\n\tconst int ETET[6][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 0, 3 },{ 1, 3 },{ 2, 3 } };\n\tconst int ETET10[6][3] = { { 0, 1, 4 },{ 1, 2, 5 },{ 2, 0, 6 },{ 0, 3, 7 },{ 1, 3, 8 },{ 2, 3, 9 } };\n\tconst int EHEX[12][2] = { { 0, 1 },{ 1, 2 },{ 2, 3 },{ 3, 0 },{ 4, 5 },{ 5, 6 },{ 6, 7 },{ 7, 4 },{ 0, 4 },{ 1, 5 },{ 2, 6 },{ 3, 7 } };\n\tconst int EHEX20[12][3] = { { 0, 1, 8 },{ 1, 2, 9 },{ 2, 3, 10 },{ 3, 0, 11 },{ 4, 5, 12 },{ 5, 6, 13 },{ 6, 7, 14 },{ 7, 4, 15 },{ 0, 4, 16 },{ 1, 5, 17 },{ 2, 6, 18 },{ 3, 7, 19 } };\n\tconst int EPEN[9][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 3, 4 },{ 4, 5 },{ 5, 3 },{ 0, 3 },{ 1, 4 },{ 2, 5 } };\n\n\tfor (int i = 0; i<dom->Elements(); ++i)\n\t{\n\t\tFEElement& el = dom->ElementRef(i);\n\n\t\tif ((el.Shape() == ET_TET4) || (el.Shape() == ET_TET5))\n\t\t{\n\t\t\tfor (int i = 0; i < 6; ++i)\n\t\t\t{\n\t\t\t\tEDGE edge;\n\t\t\t\tedge.ntype = 2;\n\t\t\t\tedge.node[0] = el.m_lnode[ETET[i][0]];\n\t\t\t\tedge.node[1] = el.m_lnode[ETET[i][1]];\n\t\t\t\tedge.node[2] = -1;\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == ET_PENTA6)\n\t\t{\n\t\t\tfor (int i = 0; i < 9; ++i)\n\t\t\t{\n\t\t\t\tEDGE edge;\n\t\t\t\tedge.ntype = 2;\n\t\t\t\tedge.node[0] = el.m_lnode[EPEN[i][0]];\n\t\t\t\tedge.node[1] = el.m_lnode[EPEN[i][1]];\n\t\t\t\tedge.node[2] = -1;\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == ET_HEX8)\n\t\t{\n\t\t\tfor (int i = 0; i < 12; ++i)\n\t\t\t{\n\t\t\t\tEDGE edge;\n\t\t\t\tedge.ntype = 2;\n\t\t\t\tedge.node[0] = el.m_lnode[EHEX[i][0]];\n\t\t\t\tedge.node[1] = el.m_lnode[EHEX[i][1]];\n\t\t\t\tedge.node[2] = -1;\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == ET_HEX20)\n\t\t{\n\t\t\tfor (int i = 0; i < 12; ++i)\n\t\t\t{\n\t\t\t\tEDGE edge;\n\t\t\t\tedge.ntype = 3;\n\t\t\t\tedge.node[0] = el.m_lnode[EHEX20[i][0]];\n\t\t\t\tedge.node[1] = el.m_lnode[EHEX20[i][1]];\n\t\t\t\tedge.node[2] = el.m_lnode[EHEX20[i][2]];\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == ET_TET10)\n\t\t{\n\t\t\tfor (int i = 0; i < 6; ++i)\n\t\t\t{\n\t\t\t\tEDGE edge;\n\t\t\t\tedge.ntype = 3;\n\t\t\t\tedge.node[0] = el.m_lnode[ETET10[i][0]];\n\t\t\t\tedge.node[1] = el.m_lnode[ETET10[i][1]];\n\t\t\t\tedge.node[2] = el.m_lnode[ETET10[i][2]];\n\n\t\t\t\tedgeSet.insert(edge);\n\t\t\t}\n\t\t}\n\t}\n\n\t// copy set into a vector\n\tsize_t edges = edgeSet.size();\n\tm_edgeList.resize(edges);\n\tset< EDGE, EDGE_less>::iterator edgeIter = edgeSet.begin();\n\tfor (size_t i = 0; i < edges; ++i, ++edgeIter)\n\t{\n\t\tm_edgeList[i] = *edgeIter;\n\t}\n\n\treturn true;\n}\n\nint FEEdgeList::FindEdge(int a, int b)\n{\n\tfor (int i = 0; i < (int)m_edgeList.size(); ++i)\n\t{\n\t\tEDGE& edge = m_edgeList[i];\n\t\tif ((edge.node[0] == a) && (edge.node[1] == b)) return i;\n\t\tif ((edge.node[0] == b) && (edge.node[1] == a)) return i;\n\t}\n\treturn -1;\n}\n\n//=============================================================================\n\nFEElementEdgeList::FEElementEdgeList()\n{\n\n}\n\nint FEElementEdgeList::Edges(int elem) const\n{\n\treturn (int)m_EEL[elem].size();\n}\n\nconst std::vector<int>& FEElementEdgeList::EdgeList(int elem) const\n{\n\treturn m_EEL[elem];\n}\n\n// NOTE: This only works for TET4 and HEX8 elements!\nbool FEElementEdgeList::Create(FEElementList& elemList, FEEdgeList& edgeList)\n{\n\tFEMesh& mesh = *edgeList.GetMesh();\n\n\tconst int ETET[6][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 0, 3 },{ 1, 3 },{ 2, 3 } };\n\tconst int EHEX[12][2] = { { 0, 1 },{ 1, 2 },{ 2, 3 },{ 3, 0 },{ 4, 5 },{ 5, 6 },{ 6, 7 },{ 7, 4 },{ 0, 4 },{ 1, 5 },{ 2, 6 },{ 3, 7 } };\n\tconst int EPENTA[9][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 3, 4 },{ 4, 5 },{ 5, 3 },{ 0, 3 },{ 1, 4 },{ 2, 5 } };\n\n\tint NN = mesh.Nodes();\n\tvector<pair<int, int> > NI;\n\tNI.resize(NN);\n\tfor (int i = 0; i<NN; ++i) NI[i].second = 0;\n\tfor (int i = edgeList.Edges() - 1; i >= 0; --i)\n\t{\n\t\tconst FEEdgeList::EDGE& et = edgeList.Edge(i);\n\t\tNI[et.node[0]].first = i;\n\t\tNI[et.node[0]].second++;\n\t}\n\n\tint NE = mesh.Elements();\n\tm_EEL.resize(NE);\n\tint i = 0;\n\tfor (FEElementList::iterator it = elemList.begin(); it != elemList.end(); ++it, ++i)\n\t{\n\t\tconst FEElement& el = *it;\n\t\tvector<int>& EELi = m_EEL[i];\n\t\tif ((el.Shape() == ET_TET4) || (el.Shape() == ET_TET5))\n\t\t{\n\t\t\tEELi.resize(6);\n\t\t\tfor (int j = 0; j<6; ++j)\n\t\t\t{\n\t\t\t\tint n0 = el.m_node[ETET[j][0]];\n\t\t\t\tint n1 = el.m_node[ETET[j][1]];\n\n\t\t\t\tif (n1 < n0) { int nt = n1; n1 = n0; n0 = nt; }\n\n\t\t\t\tint l0 = NI[n0].first;\n\t\t\t\tint ln = NI[n0].second;\n\t\t\t\tfor (int l = 0; l<ln; ++l)\n\t\t\t\t{\n\t\t\t\t\tassert(edgeList[l0 + l].node[0] == n0);\n\t\t\t\t\tif (edgeList[l0 + l].node[1] == n1)\n\t\t\t\t\t{\n\t\t\t\t\t\tEELi[j] = l0 + l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_HEX8)\n\t\t{\n\t\t\tEELi.resize(12);\n\t\t\tfor (int j = 0; j<12; ++j)\n\t\t\t{\n\t\t\t\tint n0 = el.m_node[EHEX[j][0]];\n\t\t\t\tint n1 = el.m_node[EHEX[j][1]];\n\n\t\t\t\tif (n1 < n0) { int nt = n1; n1 = n0; n0 = nt; }\n\n\t\t\t\tint l0 = NI[n0].first;\n\t\t\t\tint ln = NI[n0].second;\n\t\t\t\tfor (int l = 0; l<ln; ++l)\n\t\t\t\t{\n\t\t\t\t\tassert(edgeList[l0 + l].node[0] == n0);\n\t\t\t\t\tif (edgeList[l0 + l].node[1] == n1)\n\t\t\t\t\t{\n\t\t\t\t\t\tEELi[j] = l0 + l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_PENTA6)\n\t\t{\n\t\t\tEELi.resize(9);\n\t\t\tfor (int j = 0; j < 9; ++j)\n\t\t\t{\n\t\t\t\tint n0 = el.m_node[EPENTA[j][0]];\n\t\t\t\tint n1 = el.m_node[EPENTA[j][1]];\n\n\t\t\t\tif (n1 < n0) { int nt = n1; n1 = n0; n0 = nt; }\n\n\t\t\t\tint l0 = NI[n0].first;\n\t\t\t\tint ln = NI[n0].second;\n\t\t\t\tfor (int l = 0; l < ln; ++l)\n\t\t\t\t{\n\t\t\t\t\tassert(edgeList[l0 + l].node[0] == n0);\n\t\t\t\t\tif (edgeList[l0 + l].node[1] == n1)\n\t\t\t\t\t{\n\t\t\t\t\t\tEELi[j] = l0 + l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\n// NOTE: This only works for TET4 and HEX8 elements!\nbool FEElementEdgeList::Create(FEDomain& domain, FEEdgeList& edgeList)\n{\n\tFEMesh& mesh = *edgeList.GetMesh();\n\n\tconst int ETET[6][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 0, 3 },{ 1, 3 },{ 2, 3 } };\n\tconst int EHEX[12][2] = { { 0, 1 },{ 1, 2 },{ 2, 3 },{ 3, 0 },{ 4, 5 },{ 5, 6 },{ 6, 7 },{ 7, 4 },{ 0, 4 },{ 1, 5 },{ 2, 6 },{ 3, 7 } };\n\tconst int EPEN[9][2] = { { 0, 1 },{ 1, 2 },{ 2, 0 },{ 3, 4 },{ 4, 5 },{ 5, 3 },{ 0, 3 },{ 1, 4 },{ 2, 5 } };\n\n\tint NN = mesh.Nodes();\n\tvector<pair<int, int> > NI;\n\tNI.resize(NN);\n\tfor (int i = 0; i < NN; ++i) NI[i].second = 0;\n\tfor (int i = edgeList.Edges() - 1; i >= 0; --i)\n\t{\n\t\tconst FEEdgeList::EDGE& et = edgeList.Edge(i);\n\t\tNI[et.node[0]].first = i;\n\t\tNI[et.node[0]].second++;\n\t}\n\n\tint NE = domain.Elements();\n\tm_EEL.resize(NE);\n\tfor (int i=0; i < NE; ++i)\n\t{\n\t\tconst FEElement& el = domain.ElementRef(i);\n\t\tvector<int>& EELi = m_EEL[i];\n\t\tif ((el.Shape() == ET_TET4) || (el.Shape() == ET_TET5))\n\t\t{\n\t\t\tEELi.resize(6);\n\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t{\n\t\t\t\tint n0 = el.m_node[ETET[j][0]];\n\t\t\t\tint n1 = el.m_node[ETET[j][1]];\n\n\t\t\t\tif (n1 < n0) { int nt = n1; n1 = n0; n0 = nt; }\n\n\t\t\t\tint l0 = NI[n0].first;\n\t\t\t\tint ln = NI[n0].second;\n\t\t\t\tfor (int l = 0; l < ln; ++l)\n\t\t\t\t{\n\t\t\t\t\tassert(edgeList[l0 + l].node[0] == n0);\n\t\t\t\t\tif (edgeList[l0 + l].node[1] == n1)\n\t\t\t\t\t{\n\t\t\t\t\t\tEELi[j] = l0 + l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_HEX8)\n\t\t{\n\t\t\tEELi.resize(12);\n\t\t\tfor (int j = 0; j < 12; ++j)\n\t\t\t{\n\t\t\t\tint n0 = el.m_node[EHEX[j][0]];\n\t\t\t\tint n1 = el.m_node[EHEX[j][1]];\n\n\t\t\t\tif (n1 < n0) { int nt = n1; n1 = n0; n0 = nt; }\n\n\t\t\t\tint l0 = NI[n0].first;\n\t\t\t\tint ln = NI[n0].second;\n\t\t\t\tfor (int l = 0; l < ln; ++l)\n\t\t\t\t{\n\t\t\t\t\tassert(edgeList[l0 + l].node[0] == n0);\n\t\t\t\t\tif (edgeList[l0 + l].node[1] == n1)\n\t\t\t\t\t{\n\t\t\t\t\t\tEELi[j] = l0 + l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_PENTA6)\n\t\t{\n\t\t\tEELi.resize(9);\n\t\t\tfor (int j = 0; j < 9; ++j)\n\t\t\t{\n\t\t\t\tint n0 = el.m_node[EPEN[j][0]];\n\t\t\t\tint n1 = el.m_node[EPEN[j][1]];\n\n\t\t\t\tif (n1 < n0) { int nt = n1; n1 = n0; n0 = nt; }\n\n\t\t\t\tint l0 = NI[n0].first;\n\t\t\t\tint ln = NI[n0].second;\n\t\t\t\tfor (int l = 0; l < ln; ++l)\n\t\t\t\t{\n\t\t\t\t\tassert(edgeList[l0 + l].node[0] == n0);\n\t\t\t\t\tif (edgeList[l0 + l].node[1] == n1)\n\t\t\t\t\t{\n\t\t\t\t\t\tEELi[j] = l0 + l;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FEEdgeList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n\nclass FEMesh;\nclass FEElementList;\nclass FEDomain;\n\nclass FECORE_API FEEdgeList\n{\npublic:\n\tstruct EDGE\n\t{\n\t\tint ntype;\t\t// 2 = linear, 3 = quadratic\n\t\tint\tnode[3];\n\t};\n\npublic:\n\tFEEdgeList();\n\n\tbool Create(FEMesh* mesh);\n\n\tbool Create(FEDomain* dom);\n\n\tint Edges() const;\n\n\tconst EDGE& operator[] (int i);\n\tconst EDGE& Edge(int i) const;\n\n\tFEMesh* GetMesh();\n\n\tint FindEdge(int a, int b);\n\nprivate:\n\tFEMesh*\t\t\t\tm_mesh;\n\tstd::vector<EDGE>\tm_edgeList;\n};\n\nclass FECORE_API FEElementEdgeList\n{\npublic:\n\tFEElementEdgeList();\n\n\tbool Create(FEElementList& elemList, FEEdgeList& edgeList);\n\n\t// Create the element edge list of a domain\n\tbool Create(FEDomain& domain, FEEdgeList& edgeList);\n\n\tint Edges(int elem) const;\n\tconst std::vector<int>& EdgeList(int elem) const;\n\nprivate:\n\tstd::vector<std::vector<int> >\tm_EEL;\n};\n"
  },
  {
    "path": "FECore/FEEdgeLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEEdgeLoad.h\"\n#include \"FEEdge.h\"\n\n//-----------------------------------------------------------------------------\nFEEdgeLoad::FEEdgeLoad(FEModel* pfem) : FEModelLoad(pfem)\n{\n\tm_pedge = nullptr;\n}\n\nFEEdgeLoad::~FEEdgeLoad(void)\n{\n}\n\n//! Set the edge to apply the load to\nvoid FEEdgeLoad::SetEdge(FEEdge* pe) \n{ \n\tm_pedge = pe; \n}\n\n//! Get the edge\nFEEdge& FEEdgeLoad::Edge() \n{ \n\treturn *m_pedge; \n}\n\nvoid FEEdgeLoad::Serialize(DumpStream& ar)\n{\n\tFEModelLoad::Serialize(ar);\n\tFEEdge& e = Edge();\n\te.Serialize(ar);\n}\n"
  },
  {
    "path": "FECore/FEEdgeLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelLoad.h\"\n\n//-----------------------------------------------------------------------------\nclass FEEdge;\nclass FEModel;\nclass FELinearSystem;\nclass FEGlobalVector;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEEdgeLoad : public FEModelLoad\n{\n\tFECORE_BASE_CLASS(FEEdgeLoad)\n\npublic:\n\tFEEdgeLoad(FEModel* pfem);\n\tvirtual ~FEEdgeLoad(void);\n\n\t//! Set the edge to apply the load to\n\tvoid SetEdge(FEEdge* pe);\n\n\t//! Get the edge\n\tFEEdge& Edge();\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tFEEdge*\tm_pedge;\n};\n"
  },
  {
    "path": "FECore/FEElemElemList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElemElemList.h\"\n#include \"FENodeElemList.h\"\n#include \"FESolidDomain.h\"\n#include \"FESurface.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFEElemElemList::FEElemElemList(void)\n{\n\tm_pmesh = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEElemElemList::~FEElemElemList(void)\n{\n}\n\nbool FEElemElemList::IsValid() const\n{\n\treturn (m_pmesh != nullptr);\n}\n\nvoid FEElemElemList::Clear()\n{\n\tm_pmesh = nullptr;\n\tm_ref.clear();\n\tm_pel.clear();\n\tm_peli.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElemElemList::Init()\n{\n\tFEMesh& m = *m_pmesh;\n\n\t// allocate storage\n\tint NE = m.Elements();\n\tm_ref.resize(NE);\n\n\t// count nr of neighbors\n\tint NN = 0, n = 0;\n\tfor (int i=0; i<m.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = m.Domain(i);\n\t\tfor (int j=0; j<dom.Elements(); ++j, ++n)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tint nf = el.Faces();\n\t\t\tm_ref[n] = NN;\n\t\t\tNN += nf;\n\t\t}\n\t}\n\n\tm_pel.resize(NN);\n\tm_peli.resize(NN);\n\n\t// TODO: do this for shells as well (if we have to)\n}\n\n//-----------------------------------------------------------------------------\nbool FEElemElemList::Create(FEMesh* pmesh)\n{\n\t// store a pointer to the mesh\n\tm_pmesh = pmesh;\n\tFEMesh& m = *m_pmesh;\n\n\t// initialize data structures\n\tInit();\n\n\t// create the node element list\n\tFENodeElemList& NEL = pmesh->NodeElementList();\n\n\t// loop over all solid elements first\n\tint en0[FEElement::MAX_NODES], en1[FEElement::MAX_NODES], n0, n1, M = 0;\n\tint nf0, nf1;\n\tfor (int nd=0; nd<m.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = m.Domain(nd);\n\t\tfor (int i=0; i<dom.Elements(); ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\t\n\t\t\t// get the number of neighbors\n\t\t\tnf0 = el.Faces();\n\n\t\t\t// loop over all neighbors\n\t\t\tfor (int j=0; j<nf0; ++j, ++M)\n\t\t\t{\n\t\t\t\t// get the face nodes\n\t\t\t\tn0 = el.GetFace(j, en0);\n\n\t\t\t\t// find the neighbor element\n\t\t\t\tm_pel[M] = 0;\n\t\t\t\tm_peli[M] = -1;\n\n\t\t\t\t// loop over all possible candidates\n\t\t\t\tint nval = NEL.Valence(en0[0]);\n\t\t\t\tFEElement** pne = NEL.ElementList(en0[0]);\n\t\t\t\tint* pnei = NEL.ElementIndexList(en0[0]);\n\t\t\t\tfor (int k=0; k<nval; ++k)\n\t\t\t\t{\n\t\t\t\t\t// make sure we don't compare the current element\n\t\t\t\t\tif (pne[k] != &el)\n\t\t\t\t\t{\n\t\t\t\t\t\t// get the number of faces\n\t\t\t\t\t\tnf1 = pne[k]->Faces();\n\n\t\t\t\t\t\t// see if any of these faces match en0\n\t\t\t\t\t\tfor (int l=0; l<nf1; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tn1 = pne[k]->GetFace(l, en1);\n\n\t\t\t\t\t\t\t// make sure the faces have the same nr of nodes\n\t\t\t\t\t\t\tif (n1 == n0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// check triangles\n\t\t\t\t\t\t\t\tif ((n0 == 3) || (n0 == 6) || (n0 ==7))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (((en0[0] == en1[0]) || (en0[0] == en1[1]) || (en0[0] == en1[2])) &&\n\t\t\t\t\t\t\t\t\t\t((en0[1] == en1[0]) || (en0[1] == en1[1]) || (en0[1] == en1[2])) &&\n\t\t\t\t\t\t\t\t\t\t((en0[2] == en1[0]) || (en0[2] == en1[1]) || (en0[2] == en1[2])))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t// found it!\n\t\t\t\t\t\t\t\t\t\tm_pel[M] = pne[k];\n\t\t\t\t\t\t\t\t\t\tm_peli[M] = pnei[k];\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// check quads\n\t\t\t\t\t\t\t\telse if ((n0 == 4) || (n0 == 8) || (n0 == 9))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (((en0[0] == en1[0]) || (en0[0] == en1[1]) || (en0[0] == en1[2]) || (en0[0] == en1[3])) &&\n\t\t\t\t\t\t\t\t\t\t((en0[1] == en1[0]) || (en0[1] == en1[1]) || (en0[1] == en1[2]) || (en0[1] == en1[3])) &&\n\t\t\t\t\t\t\t\t\t\t((en0[2] == en1[0]) || (en0[2] == en1[1]) || (en0[2] == en1[2]) || (en0[2] == en1[3])) &&\n\t\t\t\t\t\t\t\t\t\t((en0[3] == en1[0]) || (en0[3] == en1[1]) || (en0[3] == en1[2]) || (en0[3] == en1[3])))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t// found it!\n\t\t\t\t\t\t\t\t\t\tm_pel[M] = pne[k];\n\t\t\t\t\t\t\t\t\t\tm_peli[M] = pnei[k];\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (m_pel[M] != 0) break;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// TODO: do the same for shells\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Find the element neighbors for a surface. In this case, the elements are\n//! surface elements (i.e. FESurfaceElement).\nbool FEElemElemList::Create(const FESurface* psurf)\n{\n\t// allocate storage\n\tint NE = psurf->Elements();\n\tm_ref.resize(NE);\n\n\t// count nr of neighbors\n\tint NN = 0;\n\tm_ref[0] = 0;\n\tfor (int j=0; j<NE; ++j)\n\t{\n\t\tconst FESurfaceElement& el = psurf->Element(j);\n\n\t\tint nf = el.facet_edges();\n\n\t\tif (j != NE-1) m_ref[j+1] = m_ref[j] + nf;\n\t\tNN += nf;\n\t}\n\n\tm_pel.resize(NN);\n\n\t// create the node element list\n\tFENodeElemList NEL;\n\tNEL.Create(*psurf);\n\n\t// loop over all facets\n\tint en0[3], en1[3], M = 0;\n\tint nf0, nf1;\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\tconst FESurfaceElement& el = psurf->Element(i);\n\t\t\t\n\t\t// get the number of neighbors\n\t\tnf0 = el.facet_edges();\n\n\t\t// loop over all neighbors\n\t\tfor (int j=0; j<nf0; ++j, ++M)\n\t\t{\n\t\t\t// get the edge nodes\n\t\t\tel.facet_edge(j, en0);\n\n\t\t\t// find the neighbor element\n\t\t\tm_pel[M] = 0;\n\n\t\t\t// loop over all possible candidates\n\t\t\tint nval = NEL.Valence(en0[0]);\n\t\t\tFEElement** pne = NEL.ElementList(en0[0]);\n\t\t\tfor (int k=0; k<nval; ++k)\n\t\t\t{\n\t\t\t\t// make sure we don't compare the current element\n\t\t\t\tif (pne[k] != &el)\n\t\t\t\t{\n\t\t\t\t\t// get the number of edges\n\t\t\t\t\tFESurfaceElement& me = dynamic_cast<FESurfaceElement&>(*pne[k]);\n\t\t\t\t\tnf1 = me.facet_edges();\n\n\t\t\t\t\t// see if any of these edges match en0\n\t\t\t\t\tfor (int l=0; l<nf1; ++l)\n\t\t\t\t\t{\n\t\t\t\t\t\tme.facet_edge(l, en1);\n\n\t\t\t\t\t\tif (((en0[0] == en1[0]) || (en0[0] == en1[1])) &&\n\t\t\t\t\t\t\t((en0[1] == en1[0]) || (en0[1] == en1[1])))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t// found it!\n\t\t\t\t\t\t\t\tm_pel[M] = pne[k];\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (m_pel[M] != 0) break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FEElemElemList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMesh;\nclass FESurface;\nclass FEElement;\n\n//-----------------------------------------------------------------------------\n//! This class finds for each element the neighbouring elements\n//!\nclass FECORE_API FEElemElemList\n{\npublic:\n\t//! constructor\n\tFEElemElemList(void);\n\n\t//! destructor\n\t~FEElemElemList(void);\n\n\tbool IsValid() const;\n\n\tvoid Clear();\n\n\t//! create the element-element list\n\tbool Create(FEMesh* pmesh);\n\n\t//! create the element-element list for a surface\n\tbool Create(const FESurface* psurf);\n\n\t//! Find the j-th neighbor element of element n\n\tFEElement* Neighbor(int n, int j) { return m_pel[ m_ref[n] + j]; }\n\n\t//! Find the j-th neighbor element of element n\n\tint NeighborIndex(int n, int j) { return m_peli[m_ref[n] + j]; }\n\n\t//! Return the size of the neighbor vector\n\tint NeighborSize() { return (int)(m_pel.size()/m_ref.size()); }\n\nprotected:\n\t//! Initialization\n\tvoid Init();\n\nprotected:\n\tstd::vector<int>\t\tm_ref;\t\t//!< start index into pel and peli array\n\tstd::vector<FEElement*>\tm_pel;\t//!< list of all neighbouring elements (or 0 if no neighbor)\n\tstd::vector<int>\t\tm_peli;\t//!< indices of neighbor elems (or -1 if no neighbor)\n\tFEMesh*\tm_pmesh;\t\t\t\t//!< pointer to mesh that created this list\n};\n"
  },
  {
    "path": "FECore/FEElement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElement.h\"\n#include \"DumpStream.h\"\n#include <math.h>\n\n//-----------------------------------------------------------------------------\nFEElementState::FEElementState(const FEElementState& s)\n{\n\tm_data.resize( s.m_data.size() );\n\tfor (size_t i=0; i<m_data.size(); ++i) \n\t{\n\t\tif (s.m_data[i]) m_data[i] = s.m_data[i]->Copy(); else m_data[i] = 0;\n\t}\n}\n\nFEElementState& FEElementState::operator = (const FEElementState& s)\n{\n\tClear();\n\tm_data.resize( s.m_data.size() );\n\tfor (size_t i=0; i<m_data.size(); ++i) \n\t{\n\t\tif (s.m_data[i]) m_data[i] = s.m_data[i]->Copy(); else m_data[i] = 0;\n\t}\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\n//! clear material point data\nvoid FEElement::ClearData()\n{\n\tint nint = GaussPoints();\n\tfor (int i=0; i<nint; ++i)\n\t{\n\t\tFEMaterialPoint* mp = GetMaterialPoint(i);\n\t\tdelete mp;\n\t\tm_State[i] = nullptr;\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElement::Evaluate(double* fn, int n)\n{\n\tdouble* Hn = H(n);\n\tdouble f = 0;\n\tconst int N = Nodes();\n\tfor (int i=0; i<N; ++i) f += Hn[i]*fn[i];\n\treturn f;\n}\n\ndouble FEElement::Evaluate(int order, double* fn, int n)\n{\n\tdouble* Hn = H(order, n);\n\tdouble f = 0;\n\tconst int N = ShapeFunctions(order);\n\tfor (int i = 0; i<N; ++i) f += Hn[i] * fn[i];\n\treturn f;\n}\n\ndouble FEElement::Evaluate(vector<double>& fn, int n)\n{\n\tdouble* Hn = H(n);\n\tdouble f = 0;\n\tconst int N = Nodes();\n\tfor (int i=0; i<N; ++i) f += Hn[i]*fn[i];\n\treturn f;\n}\n\nvec2d FEElement::Evaluate(vec2d* vn, int n)\n{\n\tdouble* Hn = H(n);\n\tvec2d v(0,0);\n\tconst int N = Nodes();\n\tfor (int i=0; i<N; ++i) v += vn[i]*Hn[i];\n\treturn v;\n}\n\nvec3d FEElement::Evaluate(vec3d* vn, int n)\n{\n\tdouble* Hn = H(n);\n\tvec3d v;\n\tconst int N = Nodes();\n\tfor (int i=0; i<N; ++i) v += vn[i]*Hn[i];\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEElement::Evaluate(double* fn, int order, int n)\n{\n\tdouble* Hn = H(order, n);\n\tdouble f = 0;\n\tconst int N = ShapeFunctions(order);\n\tfor (int i = 0; i<N; ++i) f += Hn[i] * fn[i];\n\treturn f;\n}\n\ndouble* FEElement::H(int order, int n)\n{\n\tif (order == -1) return m_pT->m_H[n];\n\telse return m_pT->m_Hp[order][n];\n}\n\nint FEElement::ShapeFunctions(int order)\n{\n\treturn (order == -1 ? Nodes() : m_pT->ShapeFunctions(order));\n}\n\n//-----------------------------------------------------------------------------\nbool FEElement::HasNode(int n) const\n{\n\tint l = Nodes();\n\tfor (int i = 0; i<l; ++i)\n\t\tif (m_node[i] == n) return true;\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nint FEElement::FindNode(int n) const\n{\n\tint l = Nodes();\n\tfor (int i = 0; i<l; ++i)\n\t\tif (m_node[i] == n) return i;\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nFEElement::FEElement() : m_pT(0) \n{ \n\tstatic int n = 1;\n\tm_nID = n++;\n\tm_lm = -1;\n\tm_val = 0.0;\n\tm_lid = -1;\n\tm_part = nullptr;\n\tm_status = ACTIVE;\n}\n\n//! get the element ID\nint FEElement::GetID() const { return m_nID; }\n\n//! set the element ID\nvoid FEElement::SetID(int n) { m_nID = n; }\n\n//! Get the element's material ID\nint FEElement::GetMatID() const { return m_mat; }\n\n//! Set the element's material ID\nvoid FEElement::SetMatID(int id) { m_mat = id; }\n\n//-----------------------------------------------------------------------------\nvoid FEElement::SetTraits(FEElementTraits* ptraits)\n{\n\tm_pT = ptraits;\n\tm_node.resize(Nodes());\n\tm_lnode.resize(Nodes());\n\tm_State.Create(GaussPoints());\n}\n\n//! serialize\nvoid FEElement::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tint type = Type();\n\t\tar << type;\n\t\tar << m_nID << m_lid << m_mat;\n\t\tar << m_node;\n\t\tar << m_lnode;\n\t\tar << m_lm << m_val;\n\t\tar << m_status;\n\t}\n\telse\n\t{\n\t\tint ntype;\n\t\tar >> ntype; SetType(ntype);\n\t\tar >> m_nID >> m_lid >> m_mat;\n\t\tar >> m_node;\n\t\tar >> m_lnode;\t\t\n\t\tar >> m_lm >> m_val;\n\t\tar >> m_status;\n\t}\n}\n\n//! return the nodes of the face\nint FEElement::GetFace(int nface, int* nf) const\n{\n\tint nn = -1;\n\tconst int* en = &(m_node[0]);\n\tswitch (Shape())\n\t{\n\tcase ET_HEX8:\n\t\tnn = 4;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[5]; nf[3] = en[4]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[6]; nf[3] = en[5]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[3]; nf[2] = en[7]; nf[3] = en[6]; break;\n\t\tcase 3: nf[0] = en[3]; nf[1] = en[0]; nf[2] = en[4]; nf[3] = en[7]; break;\n\t\tcase 4: nf[0] = en[0]; nf[1] = en[3]; nf[2] = en[2]; nf[3] = en[1]; break;\n\t\tcase 5: nf[0] = en[4]; nf[1] = en[5]; nf[2] = en[6]; nf[3] = en[7]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_PENTA6:\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nn = 4; nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[4]; nf[3] = en[3]; break;\n\t\tcase 1: nn = 4; nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[5]; nf[3] = en[4]; break;\n\t\tcase 2: nn = 4; nf[0] = en[0]; nf[1] = en[3]; nf[2] = en[5]; nf[3] = en[2]; break;\n\t\tcase 3: nn = 3; nf[0] = en[0]; nf[1] = en[2]; nf[2] = en[1]; nf[3] = en[1]; break;\n\t\tcase 4: nn = 3; nf[0] = en[3]; nf[1] = en[4]; nf[2] = en[5]; nf[3] = en[5]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_PENTA15:\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nn = 8; nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[4]; nf[3] = en[3]; nf[4] = en[6]; nf[5] = en[13]; nf[6] = en[9]; nf[7] = en[12]; break;\n\t\tcase 1: nn = 8; nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[5]; nf[3] = en[4]; nf[4] = en[7]; nf[5] = en[14]; nf[6] = en[10]; nf[7] = en[13]; break;\n\t\tcase 2: nn = 8; nf[0] = en[0]; nf[1] = en[3]; nf[2] = en[5]; nf[3] = en[2]; nf[4] = en[12]; nf[5] = en[11]; nf[6] = en[14]; nf[7] = en[8]; break;\n\t\tcase 3: nn = 6; nf[0] = en[0]; nf[1] = en[2]; nf[2] = en[1]; nf[3] = en[8]; nf[4] = en[7]; nf[5] = en[6]; break;\n\t\tcase 4: nn = 6; nf[0] = en[3]; nf[1] = en[4]; nf[2] = en[5]; nf[3] = en[9]; nf[4] = en[10]; nf[5] = en[11]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_PYRA5:\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nn = 3; nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[4]; break;\n\t\tcase 1: nn = 3; nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[4]; break;\n\t\tcase 2: nn = 3; nf[0] = en[2]; nf[1] = en[3]; nf[2] = en[4]; break;\n\t\tcase 3: nn = 3; nf[0] = en[3]; nf[1] = en[0]; nf[2] = en[4]; break;\n\t\tcase 4: nn = 4; nf[0] = en[3]; nf[1] = en[2]; nf[2] = en[1]; nf[3] = en[0]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_TET4:\n\tcase ET_TET5:\n\t\tnn = 3;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = nf[3] = en[3]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = nf[3] = en[3]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[0]; nf[2] = nf[3] = en[3]; break;\n\t\tcase 3: nf[0] = en[2]; nf[1] = en[1]; nf[2] = nf[3] = en[0]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_TET10:\n\t\tnn = 6;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[3]; nf[3] = en[4]; nf[4] = en[8]; nf[5] = en[7]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[3]; nf[3] = en[5]; nf[4] = en[9]; nf[5] = en[8]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[0]; nf[2] = en[3]; nf[3] = en[6]; nf[4] = en[7]; nf[5] = en[9]; break;\n\t\tcase 3: nf[0] = en[2]; nf[1] = en[1]; nf[2] = en[0]; nf[3] = en[5]; nf[4] = en[4]; nf[5] = en[6]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_TET15:\n\t\tnn = 7;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[3]; nf[3] = en[4]; nf[4] = en[8]; nf[5] = en[7]; nf[6] = en[11]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[3]; nf[3] = en[5]; nf[4] = en[9]; nf[5] = en[8]; nf[6] = en[12]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[0]; nf[2] = en[3]; nf[3] = en[6]; nf[4] = en[7]; nf[5] = en[9]; nf[6] = en[13]; break;\n\t\tcase 3: nf[0] = en[2]; nf[1] = en[1]; nf[2] = en[0]; nf[3] = en[5]; nf[4] = en[4]; nf[5] = en[6]; nf[6] = en[10]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_TET20:\n\t\tnn = 10;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[3]; nf[3] = en[4]; nf[4] = en[5]; nf[5] = en[12]; nf[6] = en[13]; nf[7] = en[10]; nf[8] = en[11]; nf[9] = en[16]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[3]; nf[3] = en[6]; nf[4] = en[7]; nf[5] = en[14]; nf[6] = en[15]; nf[7] = en[13]; nf[8] = en[14]; nf[9] = en[17]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[0]; nf[2] = en[3]; nf[3] = en[9]; nf[4] = en[8]; nf[5] = en[10]; nf[6] = en[11]; nf[7] = en[14]; nf[8] = en[15]; nf[9] = en[18]; break;\n\t\tcase 3: nf[0] = en[2]; nf[1] = en[1]; nf[2] = en[0]; nf[3] = en[7]; nf[4] = en[6]; nf[5] = en[5]; nf[6] = en[4]; nf[7] = en[10]; nf[8] = en[8]; nf[9] = en[19]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_HEX20:\n\t\tnn = 8;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[5]; nf[3] = en[4]; nf[4] = en[8]; nf[5] = en[17]; nf[6] = en[12]; nf[7] = en[16]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[6]; nf[3] = en[5]; nf[4] = en[9]; nf[5] = en[18]; nf[6] = en[13]; nf[7] = en[17]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[3]; nf[2] = en[7]; nf[3] = en[6]; nf[4] = en[10]; nf[5] = en[19]; nf[6] = en[14]; nf[7] = en[18]; break;\n\t\tcase 3: nf[0] = en[3]; nf[1] = en[0]; nf[2] = en[4]; nf[3] = en[7]; nf[4] = en[11]; nf[5] = en[16]; nf[6] = en[15]; nf[7] = en[19]; break;\n\t\tcase 4: nf[0] = en[0]; nf[1] = en[3]; nf[2] = en[2]; nf[3] = en[1]; nf[4] = en[11]; nf[5] = en[10]; nf[6] = en[9]; nf[7] = en[8]; break;\n\t\tcase 5: nf[0] = en[4]; nf[1] = en[5]; nf[2] = en[6]; nf[3] = en[7]; nf[4] = en[12]; nf[5] = en[13]; nf[6] = en[14]; nf[7] = en[15]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_HEX27:\n\t\tnn = 9;\n\t\tswitch (nface)\n\t\t{\n\t\tcase 0: nf[0] = en[0]; nf[1] = en[1]; nf[2] = en[5]; nf[3] = en[4]; nf[4] = en[8]; nf[5] = en[17]; nf[6] = en[12]; nf[7] = en[16]; nf[8] = en[20]; break;\n\t\tcase 1: nf[0] = en[1]; nf[1] = en[2]; nf[2] = en[6]; nf[3] = en[5]; nf[4] = en[9]; nf[5] = en[18]; nf[6] = en[13]; nf[7] = en[17]; nf[8] = en[21]; break;\n\t\tcase 2: nf[0] = en[2]; nf[1] = en[3]; nf[2] = en[7]; nf[3] = en[6]; nf[4] = en[10]; nf[5] = en[19]; nf[6] = en[14]; nf[7] = en[18]; nf[8] = en[22]; break;\n\t\tcase 3: nf[0] = en[3]; nf[1] = en[0]; nf[2] = en[4]; nf[3] = en[7]; nf[4] = en[11]; nf[5] = en[16]; nf[6] = en[15]; nf[7] = en[19]; nf[8] = en[23]; break;\n\t\tcase 4: nf[0] = en[0]; nf[1] = en[3]; nf[2] = en[2]; nf[3] = en[1]; nf[4] = en[11]; nf[5] = en[10]; nf[6] = en[9]; nf[7] = en[8]; nf[8] = en[24]; break;\n\t\tcase 5: nf[0] = en[4]; nf[1] = en[5]; nf[2] = en[6]; nf[3] = en[7]; nf[4] = en[12]; nf[5] = en[13]; nf[6] = en[14]; nf[7] = en[15]; nf[8] = en[25]; break;\n\t\t}\n\t\tbreak;\n\tcase ET_QUAD4:\n\t\tnn = 4;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2]; nf[3] = en[3];\n\t\tbreak;\n\tcase ET_QUAD8:\n\t\tnn = 8;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2]; nf[3] = en[3]; nf[4] = en[4]; nf[5] = en[5]; nf[6] = en[6]; nf[7] = en[7];\n\t\tbreak;\n\tcase ET_QUAD9:\n\t\tnn = 9;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2]; nf[3] = en[3]; nf[4] = en[4]; nf[5] = en[5]; nf[6] = en[6]; nf[7] = en[7]; nf[8] = en[8];\n\t\tbreak;\n\tcase ET_TRI3:\n\t\tnn = 3;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2];\n\t\tbreak;\n\tcase ET_TRI6:\n\t\tnn = 6;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2]; nf[3] = en[3]; nf[4] = en[4]; nf[5] = en[5];\n\t\tbreak;\n\tcase ET_TRI7:\n\t\tnn = 7;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2]; nf[3] = en[3]; nf[4] = en[4]; nf[5] = en[5]; nf[6] = en[6];\n\t\tbreak;\n\tcase ET_TRI10:\n\t\tnn = 7;\n\t\tnf[0] = en[0]; nf[1] = en[1]; nf[2] = en[2]; nf[3] = en[3]; nf[4] = en[4]; nf[5] = en[5]; nf[6] = en[6]; nf[7] = en[7]; nf[8] = en[8]; nf[9] = en[9];\n\t\tbreak;\n\t}\n\n\treturn nn;\n}\n//=================================================================================================\n// FETrussElement\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFETrussElement::FETrussElement()\n{\n\tm_a0 = 0.0;\n\tm_lam = 1.0;\n\tm_tau = 0.0;\n\tm_L0 = 0.0;\n}\n\nFETrussElement::FETrussElement(const FETrussElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\t// truss data\n\tm_a0 = el.m_a0;\n\tm_L0 = el.m_L0;\n\tm_lam = el.m_lam;\n\tm_tau = el.m_tau;\n}\n\nFETrussElement& FETrussElement::operator = (const FETrussElement& el) \n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\t// copy truss data\n\tm_a0 = el.m_a0;\n\tm_L0 = el.m_L0;\n\tm_lam = el.m_lam;\n\tm_tau = el.m_tau;\n\n\treturn (*this); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETrussElement::Serialize(DumpStream& ar)\n{\n\tFEElement::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_a0 & m_L0 & m_lam & m_tau;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEDiscreteElement::FEDiscreteElement(const FEDiscreteElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n}\n\nFEDiscreteElement& FEDiscreteElement::operator =(const FEDiscreteElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nFEElement2D::FEElement2D(const FEElement2D& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n}\n\nFEElement2D& FEElement2D::operator = (const FEElement2D& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nFELineElement::FELineElement()\n{\n\tm_lid = -1;\n}\n\nFELineElement::FELineElement(const FELineElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy data\n\tm_lid = el.m_lid;\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n}\n\nFELineElement& FELineElement::operator = (const FELineElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy data\n\tm_lid = el.m_lid;\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\treturn (*this);\n}\n\nvoid FELineElement::SetTraits(FEElementTraits* pt)\n{\n\tm_pT = pt;\n\tm_node.resize(Nodes());\n\tm_lnode.resize(Nodes());\n\tm_State.Create(GaussPoints());\n}\n\n//=============================================================================\nFEBeamElement::FEBeamElement()\n{\n\tm_lid = -1;\n\tm_L0 = 0.0;\n}\n\nFEBeamElement::FEBeamElement(const FEBeamElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy data\n\tm_lid = el.m_lid;\n\tm_L0 = el.m_L0;\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n}\n\nFEBeamElement& FEBeamElement::operator = (const FEBeamElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy data\n\tm_lid = el.m_lid;\n\tm_L0 = el.m_L0;\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\treturn (*this);\n}\n"
  },
  {
    "path": "FECore/FEElement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElementLibrary.h\"\n#include \"FEElementTraits.h\"\n#include \"FEMaterialPoint.h\"\n#include \"fecore_enum.h\"\n#include \"FEException.h\"\n\nclass FEMesh;\n//-----------------------------------------------------------------------------\nclass FEElementTraits;\nclass FEMeshPartition;\n\n//-----------------------------------------------------------------------------\n//! The FEElementState class stores the element state data. The state is defined\n//! by a material point class for each of the integration points.\nclass FECORE_API FEElementState\n{\npublic:\n\t//! default constructor\n\tFEElementState() {}\n\n\t//! destructor\n\t~FEElementState() { Clear(); }\n\n\t//! copy constructor\n\tFEElementState(const FEElementState& s);\n\n\t//! assignment operator\n\tFEElementState& operator = (const FEElementState& s);\n\n\t//! clear state data\n\tvoid Clear() { for (size_t i=0; i<m_data.size(); ++i) delete m_data[i]; m_data.clear(); }\n\n\t//! create \n\tvoid Create(int n) { Clear(); m_data.assign(n, static_cast<FEMaterialPoint*>(0)); }\n\n\t//! operator for easy access to element data\n\tFEMaterialPoint*& operator [] (int n) { return m_data[n]; }\n\nprivate:\n\tstd::vector<FEMaterialPoint*>\tm_data;\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for all element classes\n\n//! From this class the different element classes are derived.\n\nclass FECORE_API FEElement\n{\npublic:\n\tenum {MAX_NODES     = 27};\t// max nr of nodes\n\tenum {MAX_INTPOINTS = 27};\t// max nr of integration points\n\n\t// Status flags. \n\tenum Status {\n\t\tACTIVE = 0x01\n\t};\n\npublic:\n\t//! default constructor\n\tFEElement();\n\n\t//! destructor\n\tvirtual ~FEElement() {}\n\n\t//! get the element ID\n\tint GetID() const;\n\n\t//! set the element ID\n\tvoid SetID(int n);\n\n\t//! Get the element's material ID\n\tint GetMatID() const;\n\n\t//! Set the element's material ID\n\tvoid SetMatID(int id);\n\n\t//Get the mesh partition that contains this element\n\tFEMeshPartition * GetMeshPartition() const { return m_part; }\n\n\t//Set the mesh partition that contains this element\n\tvoid SetMeshPartition(FEMeshPartition* part){ m_part = part; }\n\n\t//! Set the Local ID\n\tvoid SetLocalID(int lid) { m_lid = lid; }\n\n\t//! Get the local ID\n\tint GetLocalID() const { return m_lid; }\n\n\t//! clear material point data\n\tvoid ClearData();\n\npublic:\n\t//! Set the type of the element\n\tvoid SetType(int ntype) { FEElementLibrary::SetElementTraits(*this, ntype); }\n\n\t//! Set the traits of an element\n\tvirtual void SetTraits(FEElementTraits* ptraits);\n\n\t//! Get the element traits\n\tFEElementTraits* GetTraits() { return m_pT; }\n\n\t//! return number of nodes\n\tint Nodes() const { return m_pT->m_neln; }\n\n\t//! return the element class\n\tint Class() const { return m_pT->Class(); }\n\n\t//! return the element shape\n\tint Shape() const { return m_pT->Shape(); }\n\n\t//! return the type of element\n\tint Type() const { return m_pT->Type(); }\n\n\t//! return number of integration points\n\tint GaussPoints() const { return m_pT->m_nint; }\n\n\t//! shape function values\n\tdouble* H(int n) { return m_pT->m_H[n]; }\n\tconst double* H(int n) const { return m_pT->m_H[n]; }\n\n\t//! return number of faces\n\tint Faces() const { return m_pT->Faces(); }\n\n\t//! return the nodes of the face\n\tint GetFace(int nface, int* nodeList) const;\n\npublic:\n\t//! Get the material point data\n\tFEMaterialPoint* GetMaterialPoint(int n) { return m_State[n]; }\n\n\t//! set the material point data\n\tvoid SetMaterialPointData(FEMaterialPoint* pmp, int n)\n\t{ \n\t\tpmp->m_elem = this;\n\t\tpmp->m_index = n;\n\t\tif (m_State[n] != nullptr) delete m_State[n];\n\t\tm_State[n] = pmp; \n\t}\n\n\t//! serialize\n\t//! NOTE: state data is not serialized by the element. This has to be done by the domains.\n\tvirtual void Serialize(DumpStream& ar);\n\npublic:\n\t//! evaluate scalar field at integration point\n\tdouble Evaluate(double* fn, int n);\n\tdouble Evaluate(int order, double* fn, int n);\n\n\t//! evaluate scale field at integration point\n\tdouble Evaluate(std::vector<double>& fn, int n);\n\n\t//! evaluate vector field at integration point\n\tvec2d Evaluate(vec2d* vn, int n);\n\n\t//! evaluate vector field at integration point\n\tvec3d Evaluate(vec3d* vn, int n);\n\n\t// see if this element has the node n\n    bool HasNode(int n) const;\n\n\t// find local element index of node n\n    int FindNode(int n) const;\n\n\t// project data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const { m_pT->project_to_nodes(ai, ao); }\n\tvoid project_to_nodes(vec3d*  ai, vec3d*  ao) const { m_pT->project_to_nodes(ai, ao); }\n\tvoid project_to_nodes(mat3ds* ai, mat3ds* ao) const { m_pT->project_to_nodes(ai, ao); }\n\tvoid project_to_nodes(mat3d*  ai, mat3d*  ao) const { m_pT->project_to_nodes(ai, ao); }\n\n\t// evaluate scalar field at integration point using specific interpolation order\n\tdouble Evaluate(double* fn, int order, int n);\n\n\tint ShapeFunctions(int order);\n\tdouble* H(int order, int n);\n\npublic:\n\tvoid setStatus(unsigned int n) { m_status = n; }\n\tunsigned int status() const { return m_status; }\n\tbool isActive() const { return (m_status & ACTIVE); }\n\tvoid setActive() { m_status |= ACTIVE; }\n\tvoid setInactive() { m_status &= ~ACTIVE; }\n\nprotected:\n\tint\t\tm_nID;\t\t//!< element ID\n\tint\t\tm_lid;\t\t//!< local ID\n\tint\t\tm_mat;\t\t//!< material index\n\tunsigned int\tm_status;\t//!< element status\n\tFEMeshPartition * m_part;\t//!< parent mesh partition\n\npublic:\n\tstd::vector<int>\t\tm_node;\t\t//!< connectivity\n\n\t// This array stores the local node numbers, that is the node numbers\n\t// into the node list of a domain.\n\tstd::vector<int>\t\tm_lnode;\t//!< local connectivity\n\npublic: \n\t// NOTE: Work in progress\n\t// Elements can now also have degrees of freedom, only currently just one.\n\t// Like with nodes, a degree of freedom needs an equation number and a value\n\t// The equation number is in m_lm and the value is in m_val\n\tint\t\tm_lm;\t//!< equation number of element degree of freedom\n\tdouble\tm_val;\t//!< solution value of element degree of freedom\n\nprotected:\n\tFEElementState\t\tm_State;\t//!< element state data\n\tFEElementTraits*\tm_pT;\t\t//!< pointer to element traits\n};\n\n//-----------------------------------------------------------------------------\n\nclass FECORE_API FETrussElement : public FEElement\n{\npublic:\n\tFETrussElement();\n\n\tFETrussElement(const FETrussElement& el);\n\n\tFETrussElement& operator = (const FETrussElement& el);\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tdouble* GaussWeights() const { return &((FETrussElementTraits*)(m_pT))->gw[0]; }\n\npublic:\n\tdouble\tm_a0;\t// cross-sectional area\n\tdouble\tm_lam;\t// current stretch ratio\n\tdouble\tm_tau;\t// Kirchoff stress\n\tdouble\tm_L0;\t// initial length\n\tdouble\tm_Lt;\t// current length\n};\n\n//-----------------------------------------------------------------------------\n//! Discrete element class\n\nclass FECORE_API FEDiscreteElement : public FEElement\n{\npublic:\n\tFEDiscreteElement(){}\n\tFEDiscreteElement(const FEDiscreteElement& e);\n\tFEDiscreteElement& operator = (const FEDiscreteElement& e);\n};\n\n//-----------------------------------------------------------------------------\n//!  This class defines a 2D element\nclass FECORE_API FEElement2D : public FEElement\n{\npublic:\n\t//! default constructor\n\tFEElement2D(){}\n\n\t//! copy constructor\n\tFEElement2D(const FEElement2D& el);\n\n\t//! assignment operator\n\tFEElement2D& operator = (const FEElement2D& el);\n\n\tdouble* GaussWeights() { return &((FE2DElementTraits*)(m_pT))->gw[0]; }\t\t\t// weights of integration points\n\n\tdouble* Hr(int n) { return ((FE2DElementTraits*)(m_pT))->Gr[n]; }\t// shape function derivative to r\n\tdouble* Hs(int n) { return ((FE2DElementTraits*)(m_pT))->Gs[n]; }\t// shape function derivative to s\n\n    double* Hrr(int n) { return ((FE2DElementTraits*)(m_pT))->Grr[n]; }\t// shape function 2nd derivative to rr\n    double* Hsr(int n) { return ((FE2DElementTraits*)(m_pT))->Gsr[n]; }\t// shape function 2nd derivative to sr\n    \n    double* Hrs(int n) { return ((FE2DElementTraits*)(m_pT))->Grs[n]; }\t// shape function 2nd derivative to rs\n    double* Hss(int n) { return ((FE2DElementTraits*)(m_pT))->Gss[n]; }\t// shape function 2nd derivative to ss\n    \n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) { ((FE2DElementTraits*)(m_pT))->shape(H, r, s); }\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) { ((FE2DElementTraits*)(m_pT))->shape_deriv(Hr, Hs, r, s); }\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FELineElement : public FEElement\n{\npublic:\n\tFELineElement();\n\n\tFELineElement(const FELineElement& el);\n\n\tFELineElement& operator = (const FELineElement& el);\n\n\tvoid SetTraits(FEElementTraits* pt);\n\n\tdouble* GaussWeights() { return &((FELineElementTraits*)(m_pT))->gw[0]; }\t\t\t// weights of integration points\n\n\tdouble* Gr(int n) const { return ((FELineElementTraits*)(m_pT))->Gr[n]; }\t// shape function derivative to r\n\n\tvoid shape(double* H, double r) { return ((FELineElementTraits*)(m_pT))->shape(H, r); }\n\n\tvoid shape_deriv(double* Hr, double r) { return ((FELineElementTraits*)(m_pT))->shape_deriv(Hr, r); }\n\n\tvoid shape_deriv2(double* Hrr, double r) { return ((FELineElementTraits*)(m_pT))->shape_deriv2(Hrr, r); }\n\n\tvec3d eval(vec3d* d, int n)\n\t{\n\t\tint ne = Nodes();\n\t\tdouble* N = H(n);\n\t\tvec3d a(0, 0, 0);\n\t\tfor (int i = 0; i < ne; ++i) a += d[i] * N[i];\n\t\treturn a;\n\t}\n\n\tvec3d eval_deriv(vec3d* d, int j)\n\t{\n\t\tdouble* Hr = Gr(j);\n\t\tint n = Nodes();\n\t\tvec3d v(0, 0, 0);\n\t\tfor (int i = 0; i < n; ++i) v += d[i] * Hr[i];\n\t\treturn v;\n\t}\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEBeamElement : public FEElement\n{\npublic:\n\tFEBeamElement();\n\n\tFEBeamElement(const FEBeamElement& el);\n\n\tFEBeamElement& operator = (const FEBeamElement& el);\n\n\tdouble* GaussWeights() { return &((FEBeamElementTraits*)(m_pT))->gw[0]; }\n\tdouble* Hr(int n) { return ((FEBeamElementTraits*)(m_pT))->Gr[n]; }\n\npublic:\n\tdouble\tm_L0;\t// initial length of beam\n\tmat3d\tm_E;\t// columns are local beam orientation\n};\n"
  },
  {
    "path": "FECore/FEElementLibrary.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElementLibrary.h\"\n#include \"FEElement.h\"\n#include \"FESolidElementShape.h\"\n#include \"FESurfaceElementShape.h\"\n\nFEElementLibrary* FEElementLibrary::m_pThis = 0;\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\n//! initialize library\nvoid FEElementLibrary::Initialize()\n{\n\t// Calling GetInstance will initialize the static pointer\n\tif (m_pThis == 0) GetInstance();\n}\n\nFEElementLibrary* FEElementLibrary::GetInstance()\n{\n\tif (m_pThis == 0)\n\t{\n\t\tm_pThis = new FEElementLibrary;\n\n\t\tint n;\n\t\t// register element shapes (must be done before types!)\n\t\tn = m_pThis->RegisterShape(new FETet4   ); assert(n == ET_TET4   );\n\t\tn = m_pThis->RegisterShape(new FETet5   ); assert(n == ET_TET5   );\n\t\tn = m_pThis->RegisterShape(new FETet10  ); assert(n == ET_TET10  );\n\t\tn = m_pThis->RegisterShape(new FETet15  ); assert(n == ET_TET15  );\n\t\tn = m_pThis->RegisterShape(new FETet20  ); assert(n == ET_TET20  );\n\t\tn = m_pThis->RegisterShape(new FEPenta6 ); assert(n == ET_PENTA6 );\n\t\tn = m_pThis->RegisterShape(new FEPenta15); assert(n == ET_PENTA15);\n\t\tn = m_pThis->RegisterShape(new FEHex8   ); assert(n == ET_HEX8   );\n\t\tn = m_pThis->RegisterShape(new FEHex20  ); assert(n == ET_HEX20  );\n\t\tn = m_pThis->RegisterShape(new FEHex27  ); assert(n == ET_HEX27  );\n\t\tn = m_pThis->RegisterShape(new FEPyra5  ); assert(n == ET_PYRA5  );\n        n = m_pThis->RegisterShape(new FEPyra13 ); assert(n == ET_PYRA13 );\n\t\tn = m_pThis->RegisterShape(new FEQuad4  ); assert(n == ET_QUAD4  );\n\t\tn = m_pThis->RegisterShape(new FEQuad8  ); assert(n == ET_QUAD8  );\n\t\tn = m_pThis->RegisterShape(new FEQuad9  ); assert(n == ET_QUAD9  );\n\t\tn = m_pThis->RegisterShape(new FETri3   ); assert(n == ET_TRI3   );\n\t\tn = m_pThis->RegisterShape(new FETri6   ); assert(n == ET_TRI6   );\n\t\tn = m_pThis->RegisterShape(new FETri7   ); assert(n == ET_TRI7   );\n\t\tn = m_pThis->RegisterShape(new FETri10  ); assert(n == ET_TRI10  );\n\n\t\t// register element types\n\t\tn = m_pThis->RegisterTraits(new FEHex8G8    ); assert(n==FE_HEX8G8   );\n\t\tn = m_pThis->RegisterTraits(new FEHex8RI    ); assert(n==FE_HEX8RI   );\n\t\tn = m_pThis->RegisterTraits(new FEHex8G1    ); assert(n==FE_HEX8G1   );\n\t\tn = m_pThis->RegisterTraits(new FETet4G1    ); assert(n==FE_TET4G1   );\n\t\tn = m_pThis->RegisterTraits(new FETet4G4    ); assert(n==FE_TET4G4   );\n\t\tn = m_pThis->RegisterTraits(new FETet5G4    ); assert(n==FE_TET5G4   );\n\t\tn = m_pThis->RegisterTraits(new FEPenta6G6  ); assert(n==FE_PENTA6G6 );\n        n = m_pThis->RegisterTraits(new FETet10G1   ); assert(n==FE_TET10G1  );\n\t\tn = m_pThis->RegisterTraits(new FETet10G4   ); assert(n==FE_TET10G4  );\n\t\tn = m_pThis->RegisterTraits(new FETet10G8   ); assert(n==FE_TET10G8  );\n\t\tn = m_pThis->RegisterTraits(new FETet10GL11 ); assert(n==FE_TET10GL11);\n\t\tn = m_pThis->RegisterTraits(new FETet10G4RI1); assert(n==FE_TET10G4RI1);\n\t\tn = m_pThis->RegisterTraits(new FETet10G8RI4); assert(n==FE_TET10G8RI4); \n\t\tn = m_pThis->RegisterTraits(new FETet15G4   ); assert(n==FE_TET15G4  );\n\t\tn = m_pThis->RegisterTraits(new FETet15G8   ); assert(n==FE_TET15G8  );\n\t\tn = m_pThis->RegisterTraits(new FETet15G11  ); assert(n==FE_TET15G11 );\n\t\tn = m_pThis->RegisterTraits(new FETet15G15  ); assert(n==FE_TET15G15 );\n\t\tn = m_pThis->RegisterTraits(new FETet15G15RI4); assert(n==FE_TET15G15RI4);\n\t\tn = m_pThis->RegisterTraits(new FETet20G15  ); assert(n==FE_TET20G15 );\n        n = m_pThis->RegisterTraits(new FEHex20G8   ); assert(n==FE_HEX20G8  );\n\t\tn = m_pThis->RegisterTraits(new FEHex20G27  ); assert(n==FE_HEX20G27 );\n\t\tn = m_pThis->RegisterTraits(new FEHex27G27  ); assert(n==FE_HEX27G27 );\n        n = m_pThis->RegisterTraits(new FEPenta15G8 ); assert(n==FE_PENTA15G8);\n        n = m_pThis->RegisterTraits(new FEPenta15G21); assert(n==FE_PENTA15G21);\n\t\tn = m_pThis->RegisterTraits(new FEPyra5G8   ); assert(n==FE_PYRA5G8  );\n        n = m_pThis->RegisterTraits(new FEPyra13G8  ); assert(n==FE_PYRA13G8 );\n\t\tn = m_pThis->RegisterTraits(new FEQuad4G4   ); assert(n==FE_QUAD4G4  );\n\t\tn = m_pThis->RegisterTraits(new FEQuad4G16  ); assert(n==FE_QUAD4G16 );\n\t\tn = m_pThis->RegisterTraits(new FEQuad4NI   ); assert(n==FE_QUAD4NI  );\n\t\tn = m_pThis->RegisterTraits(new FETri3G1    ); assert(n==FE_TRI3G1   );\n\t\tn = m_pThis->RegisterTraits(new FETri3G3    ); assert(n==FE_TRI3G3   );\n\t\tn = m_pThis->RegisterTraits(new FETri3G7    ); assert(n==FE_TRI3G7   );\n\t\tn = m_pThis->RegisterTraits(new FETri3NI    ); assert(n==FE_TRI3NI   );\n\t\tn = m_pThis->RegisterTraits(new FETri6G3    ); assert(n==FE_TRI6G3   );\n\t\tn = m_pThis->RegisterTraits(new FETri6G4    ); assert(n==FE_TRI6G4   );\n\t\tn = m_pThis->RegisterTraits(new FETri6G7    ); assert(n==FE_TRI6G7   );\n//\t\tn = m_pThis->RegisterTraits(new FETri6mG7   ); assert(n==FE_TRI6MG7  );\n\t\tn = m_pThis->RegisterTraits(new FETri6GL7   ); assert(n==FE_TRI6GL7  );\n\t\tn = m_pThis->RegisterTraits(new FETri6NI    ); assert(n==FE_TRI6NI   );\n\t\tn = m_pThis->RegisterTraits(new FETri7G3    ); assert(n==FE_TRI7G3   );\n\t\tn = m_pThis->RegisterTraits(new FETri7G4    ); assert(n==FE_TRI7G4   );\n\t\tn = m_pThis->RegisterTraits(new FETri7G7    ); assert(n==FE_TRI7G7   );\n\t\tn = m_pThis->RegisterTraits(new FETri7GL7   ); assert(n==FE_TRI7GL7  );\n\t\tn = m_pThis->RegisterTraits(new FETri10G7   ); assert(n==FE_TRI10G7  );\n\t\tn = m_pThis->RegisterTraits(new FETri10G12  ); assert(n==FE_TRI10G12 );\n\t\tn = m_pThis->RegisterTraits(new FEQuad8G9   ); assert(n==FE_QUAD8G9  );\n        n = m_pThis->RegisterTraits(new FEQuad8NI   ); assert(n==FE_QUAD8NI  );\n\t\tn = m_pThis->RegisterTraits(new FEQuad9G9   ); assert(n==FE_QUAD9G9  );\n        n = m_pThis->RegisterTraits(new FEQuad9NI   ); assert(n==FE_QUAD9NI  );\n        n = m_pThis->RegisterTraits(new FEShellQuad4G4  ); assert(n==FE_SHELL_QUAD4G4 );\n        n = m_pThis->RegisterTraits(new FEShellQuad4G8  ); assert(n==FE_SHELL_QUAD4G8 );\n        n = m_pThis->RegisterTraits(new FEShellQuad4G12 ); assert(n==FE_SHELL_QUAD4G12);\n        n = m_pThis->RegisterTraits(new FEShellQuad8G18 ); assert(n==FE_SHELL_QUAD8G18);\n        n = m_pThis->RegisterTraits(new FEShellQuad8G27 ); assert(n==FE_SHELL_QUAD8G27);\n        n = m_pThis->RegisterTraits(new FEShellTri3G3   ); assert(n==FE_SHELL_TRI3G3);\n        n = m_pThis->RegisterTraits(new FEShellTri3G6   ); assert(n==FE_SHELL_TRI3G6);\n        n = m_pThis->RegisterTraits(new FEShellTri3G9   ); assert(n==FE_SHELL_TRI3G9);\n        n = m_pThis->RegisterTraits(new FEShellTri6G14  ); assert(n==FE_SHELL_TRI6G14);\n        n = m_pThis->RegisterTraits(new FEShellTri6G21  ); assert(n==FE_SHELL_TRI6G21);\n        n = m_pThis->RegisterTraits(new FETrussElementTraits      ); assert(n==FE_TRUSS);\n\t\tn = m_pThis->RegisterTraits(new FEDiscreteElementTraits   ); assert(n==FE_DISCRETE);\n\t\tn = m_pThis->RegisterTraits(new FE2DTri3G1  ); assert(n==FE2D_TRI3G1 );\n\t\tn = m_pThis->RegisterTraits(new FE2DTri6G3  ); assert(n==FE2D_TRI6G3 );\n\t\tn = m_pThis->RegisterTraits(new FE2DQuad4G4 ); assert(n==FE2D_QUAD4G4);\n\t\tn = m_pThis->RegisterTraits(new FE2DQuad8G9 ); assert(n==FE2D_QUAD8G9);\n\t\tn = m_pThis->RegisterTraits(new FE2DQuad9G9 ); assert(n==FE2D_QUAD9G9);\n\t\tn = m_pThis->RegisterTraits(new FELine2G1   ); assert(n==FE_LINE2G1);\n\t\tn = m_pThis->RegisterTraits(new FELine2NI   ); assert(n==FE_LINE2NI);\n\t\tn = m_pThis->RegisterTraits(new FEBeam2G1   ); assert(n==FE_BEAM2G1);\n\t\tn = m_pThis->RegisterTraits(new FEBeam2G2   ); assert(n==FE_BEAM2G2);\n\t\tn = m_pThis->RegisterTraits(new FEBeam3G2   ); assert(n==FE_BEAM3G2);\n\t}\n\treturn m_pThis;\n}\n\nFEElementLibrary::~FEElementLibrary()\n{\n\tfor (size_t i=0; i<m_Traits.size(); ++i) delete m_Traits[i];\n\tm_Traits.clear();\n}\n\nint FEElementLibrary::RegisterShape(FEElementShape* pshape)\n{\n\tm_Shape.push_back(pshape);\n\treturn ((int)m_Shape.size() - 1);\n}\n\nint FEElementLibrary::RegisterTraits(FEElementTraits* ptrait) \n{ \n\tm_Traits.push_back(ptrait); \n\treturn ((int)m_Traits.size()-1);\n}\n\nvoid FEElementLibrary::SetElementTraits(FEElement& el, int nid)\n{\n\tel.SetTraits( GetInstance()->m_Traits[nid] );\n}\n\n//! return element shape class\nFEElementShape* FEElementLibrary::GetElementShapeClass(FE_Element_Shape eshape)\n{\n\treturn GetInstance()->m_Shape[eshape];\n}\n\nFEElementTraits* FEElementLibrary::GetElementTraits(int ntype)\n{ \n\treturn GetInstance()->m_Traits[ntype]; \n}\n\nFE_Element_Shape FEElementLibrary::GetElementShape(int ntype)\n{\n\tFEElementLibrary* p = GetInstance();\n\tif ((ntype < 0)||(ntype >= p->m_Traits.size())) return FE_ELEM_INVALID_SHAPE;\n\treturn p->m_Traits[ntype]->Shape();\n}\n\n//! return the element class of a given element type\nFE_Element_Class FEElementLibrary::GetElementClass(int ntype)\n{\n\tFEElementLibrary* p = GetInstance();\n\tif ((ntype < 0) || (ntype >= p->m_Traits.size())) return FE_ELEM_INVALID_CLASS;\n\treturn p->m_Traits[ntype]->Class();\n}\n\nbool FEElementLibrary::IsValid(const FE_Element_Spec& c)\n{\n\tif (c.eclass == FE_ELEM_INVALID_CLASS) return false;\n\tif (c.eshape == FE_ELEM_INVALID_SHAPE) return false;\n\tif (c.etype  == FE_ELEM_INVALID_TYPE ) return false;\n\tif (c.eclass != GetElementClass(c.etype)) return false;\n\tif (c.eshape != GetElementShape(c.etype)) return false;\n\treturn true;\n}\n\n//! get the element spec from the type\nFE_Element_Spec FEElementLibrary::GetElementSpecFromType(FE_Element_Type elemType)\n{\n\tFE_Element_Spec espec;\n\tespec.etype = elemType;\n\tif (elemType != FE_ELEM_INVALID_TYPE)\n\t{\n\t\tespec.eclass = GetElementClass(elemType);\n\t\tespec.eshape = GetElementShape(elemType);\n\t}\n\treturn espec;\n}\n"
  },
  {
    "path": "FECore/FEElementLibrary.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_enum.h\"\n#include \"fecore_api.h\"\n\nclass FEElement;\nclass FEElementTraits;\nclass FEElementShape;\n\n//-----------------------------------------------------------------------------\n//! This class stores the different element traits classes\n\n//! The purpose of this class is to store all the different element traits classes.\n//! It are these traits classes that define the different element types. All different\n//! traits must be registered before they can be assigned to elements.\n\nclass FECORE_API FEElementLibrary\n{\npublic:\n\t//! destructor\n\t~FEElementLibrary();\n\n\t//! return the element library\n\tstatic FEElementLibrary* GetInstance();\n\n\t//! Assign a traits class to an element\n\tstatic void SetElementTraits(FEElement& el, int id);\n\n\t//! return element traits data\n\tstatic FEElementTraits* GetElementTraits(int ntype);\n\n\t//! return element shape class\n\tstatic FEElementShape* GetElementShapeClass(FE_Element_Shape eshape);\n\n\t//! return the element shape of a given element type\n\tstatic FE_Element_Shape GetElementShape(int ntype);\n\n\t//! return the element class of a given element type\n\tstatic FE_Element_Class GetElementClass(int ntype);\n\n\t//! checks if the element spec is valid\n\tstatic bool IsValid(const FE_Element_Spec& c);\n\n\t//! initialize library\n\tstatic void Initialize();\n\n\t//! get the element spec from the type\n\tstatic FE_Element_Spec GetElementSpecFromType(FE_Element_Type elemType);\n\nprivate:\n\t//! constructor\n\tFEElementLibrary(){}\n\tFEElementLibrary(const FEElementLibrary&){}\n\n\t//! Function to register an element shape class\n\tint RegisterShape(FEElementShape* pshape);\n\n\t//! Function to register an element traits class\n\tint RegisterTraits(FEElementTraits* ptrait);\n\nprivate:\n\tstd::vector<FEElementTraits*>\tm_Traits;\t//!< pointer to registered element traits\n\tstd::vector<FEElementShape*>\tm_Shape;\t//!< pointer to registered element shapes\n\tstatic FEElementLibrary*\tm_pThis;\n};\n"
  },
  {
    "path": "FECore/FEElementList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElementList.h\"\n#include \"FEMesh.h\"\n#include \"FEDomain.h\"\n\nFEElement& FEElementList::iterator::operator*()\n{ \n\treturn m_pmesh->Domain(m_ndom).ElementRef(m_nel); \n}\n\nFEElement* FEElementList::iterator::operator->()\n{\n\treturn &m_pmesh->Domain(m_ndom).ElementRef(m_nel);\n}\n\nFECORE_API FEElementList::iterator::operator FEElement* ()\n{\n\treturn &m_pmesh->Domain(m_ndom).ElementRef(m_nel);\n}\n\nvoid FEElementList::iterator::operator ++ ()\n{\n\tif (m_pmesh && (m_ndom >= 0) && (m_nel >= 0))\n\t{\n\t\tm_nel++;\n\t\tif (m_nel >= m_pmesh->Domain(m_ndom).Elements())\n\t\t{\n\t\t\tm_ndom++;\n\t\t\tm_nel = 0;\n\t\t\tif (m_ndom >= m_pmesh->Domains())\n\t\t\t{\n\t\t\t\tm_ndom = -1;\n\t\t\t\tm_nel = -1;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FEElementList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n\nclass FEMesh;\nclass FEElement;\n\n//-----------------------------------------------------------------------------\n//! utitlity class for accessing all elements without having to go throug the domains\nclass FEElementList\n{\npublic:\n\tclass iterator\n\t{\n\tpublic:\n\t\titerator() { m_pmesh = 0; m_ndom = -1; m_nel = -1; }\n\t\titerator(FEMesh* pm) { m_pmesh = pm; m_ndom = 0; m_nel = 0; }\n\n\t\tFECORE_API FEElement& operator*();\n\n\t\tFECORE_API FEElement* operator->();\n\n\t\tFECORE_API operator FEElement* ();\n\n\t\tFECORE_API void operator ++ ();\n\n\t\tbool operator != (const iterator& it)\n\t\t{\n\t\t\treturn ((m_ndom!=it.m_ndom)||(m_nel != it.m_nel));\n\t\t}\n\n\tpublic:\n\t\tFEMesh*\t\tm_pmesh;\t// pointer to mesh\n\t\tint\t\t\tm_ndom;\t\t// domain index\n\t\tint\t\t\tm_nel;\t\t// element index\n\t};\n\npublic:\n\tFEElementList(FEMesh& m) : m_mesh(m){}\n\n\titerator begin() { return iterator(&m_mesh); }\n\titerator end() { return iterator(); }\n\nprivate:\n\tFEMesh&\tm_mesh;\n};\n"
  },
  {
    "path": "FECore/FEElementSet.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElementSet.h\"\n#include \"FEElement.h\"\n#include \"FEMesh.h\"\n#include \"FEDomain.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nFEElementSet::FEElementSet(FEModel* fem) : FEItemList(fem, FEItemList::FE_ELEMENT_SET)\n{\n\tm_minID = -1;\n\tm_maxID = -1;\n}\n\nFEElementSet::FEElementSet(FEMesh* mesh) : FEItemList(mesh, FEItemList::FE_ELEMENT_SET)\n{\n\tm_minID = -1;\n\tm_maxID = -1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::Create(const std::vector<int>& elemList)\n{\n\tm_dom.Clear();\n\tm_Elem = elemList;\n\tBuildLUT();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::Create(FEDomain* dom, const std::vector<int>& elemList)\n{\n\tm_dom.Clear();\n\tm_dom.AddDomain(dom);\n\tm_Elem = elemList;\n\tBuildLUT();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::CopyFrom(FEElementSet& eset)\n{\n\tSetName(eset.GetName());\n\n\tm_Elem = eset.m_Elem;\n\n\tFEMesh* mesh = GetMesh(); assert(mesh);\n\n\tm_dom.Clear();\n\tFEDomainList& dl = eset.GetDomainList();\n\tfor (int i = 0; i < dl.Domains(); ++i)\n\t{\n\t\tFEDomain* di = dl.GetDomain(i);\n\t\tFEDomain* newdi = mesh->FindDomain(di->GetName()); assert(newdi);\n\t\tm_dom.AddDomain(newdi);\n\t}\n\n\tBuildLUT();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::Create(FEDomain* dom)\n{\n\tm_dom.Clear();\n\tm_dom.AddDomain(dom);\n\tSetMesh(dom->GetMesh());\n\n\tint NE = dom->Elements();\n\tm_Elem.resize(NE, -1);\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom->ElementRef(i);\n\t\tm_Elem[i] = el.GetID();\n\t}\n\n\tBuildLUT();\n}\n\n//-----------------------------------------------------------------------------\n// add another element set\nvoid FEElementSet::Add(const FEElementSet& set)\n{\n\t// add the domain list\n\tm_dom.AddDomainList(set.GetDomainList());\n\n\t// add the elements\n\tm_Elem.insert(m_Elem.end(), set.m_Elem.begin(), set.m_Elem.end());\n\n\tBuildLUT();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::Create(FEDomainList& domList)\n{\n\tint NT = 0;\n\tm_dom.Clear();\n\tfor (int n = 0; n < domList.Domains(); ++n)\n\t{\n\t\tFEDomain* dom = domList.GetDomain(n);\n\t\tm_dom.AddDomain(dom);\n\t\tNT += dom->Elements();\n\t}\n\n\tm_Elem.resize(NT, -1);\n\tNT = 0;\n\tfor (int n = 0; n < domList.Domains(); ++n)\n\t{\n\t\tFEDomain* dom = domList.GetDomain(n);\n\t\tint NE = dom->Elements();\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEElement& el = dom->ElementRef(i);\n\t\t\tm_Elem[NT + i] = el.GetID();\n\t\t}\n\t\tNT += NE;\n\t}\n\n\tBuildLUT();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::BuildLUT()\n{\n\tFEMesh* mesh = GetMesh();\n\tint N = (int)m_Elem.size();\n\tm_minID = m_maxID = -1;\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFEElement* pe = mesh->FindElementFromID(m_Elem[i]);\n\t\tint id = pe->GetID();\n\n\t\tif ((id < m_minID) || (m_minID == -1)) m_minID = id;\n\t\tif ((id > m_maxID) || (m_maxID == -1)) m_maxID = id;\n\t}\n\n\tint lutSize = m_maxID - m_minID + 1;\n\tm_LUT.resize(lutSize, -1);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFEElement* pe = mesh->FindElementFromID(m_Elem[i]);\n\t\tint id = pe->GetID() - m_minID;\n\t\tm_LUT[id] = i;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEElement& FEElementSet::Element(int i)\n{\n\tFEMesh* mesh = GetMesh();\n\treturn *mesh->FindElementFromID(m_Elem[i]);\n}\n\n//-----------------------------------------------------------------------------\nconst FEElement& FEElementSet::Element(int i) const\n{\n\tFEMesh* mesh = GetMesh();\n\treturn *mesh->FindElementFromID(m_Elem[i]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEElementSet::Serialize(DumpStream& ar)\n{\n\tFEItemList::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Elem;\n\tar & m_LUT;\n\tar & m_minID & m_maxID;\n}\n\nvoid FEElementSet::SaveClass(DumpStream& ar, FEElementSet* p) {}\nFEElementSet* FEElementSet::LoadClass(DumpStream& ar, FEElementSet* p)\n{\n\tp = new FEElementSet(&ar.GetFEModel());\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\n// create node list from this element set\nFENodeList FEElementSet::GetNodeList() const\n{\n\tFEMesh* mesh = GetMesh();\n\tFENodeList set(mesh);\n\tvector<int> tag(mesh->Nodes(), 0);\n\tfor (int i = 0; i<Elements(); ++i)\n\t{\n\t\tconst FEElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j<ne; ++j)\n\t\t{\n\t\t\tif (tag[el.m_node[j]] == 0)\n\t\t\t{\n\t\t\t\tset.Add(el.m_node[j]);\n\t\t\t\ttag[el.m_node[j]] = 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn set;\n}\n"
  },
  {
    "path": "FECore/FEElementSet.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"FEItemList.h\"\n#include \"FEElement.h\"\n#include \"FEDomainList.h\"\n#include \"FENodeList.h\"\n#include <vector>\n#include <string>\n\n//-----------------------------------------------------------------------------\nclass FEMesh;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n// This class defines a set of elements\nclass FECORE_API FEElementSet : public FEItemList\n{\npublic:\n\t//! constructor\n\tFEElementSet(FEModel* fem);\t// TODO: remove!\n\tFEElementSet(FEMesh* fem);\n\n\t// Create the element set\n\tvoid Create(const std::vector<int>& elemList);\n\tvoid Create(FEDomain* dom, const std::vector<int>& elemList);\n\n\tvoid CopyFrom(FEElementSet& eset);\n\n\t// add another element set\n\tvoid Add(const FEElementSet& set);\n\n\t// Create the element set from a domain\n\tvoid Create(FEDomain* dom);\n\n\t// Create the element set from a domain\n\tvoid Create(FEDomainList& dom);\n\n\t// Return number of elements in the set\n\tint Elements() const { return (int)m_Elem.size(); }\n\n\tint operator [] (int i) const { return m_Elem[i]; }\n\n\t// return the local index of an element into the element set\n\t// returns -1 if the element is not part of element set\n\tint GetLocalIndex(const FEElement& el) const;\n\tbool Contains(const FEElement& el) const;\n\n\t// Get the element ID list\n\tconst std::vector<int>& GetElementIDList() const { return m_Elem; }\n\n\t// get the domain list that generated the element set\n\tFEDomainList& GetDomainList() { return m_dom; }\n\tconst FEDomainList& GetDomainList() const { return m_dom; }\n\n\t// Get an element\n\tFEElement& Element(int i);\n\tconst FEElement& Element(int i) const;\n\n\t// create node list from this element set\n\tFENodeList GetNodeList() const;\n\npublic:\n\tvoid Serialize(DumpStream& ar) override;\n\n\tstatic void SaveClass(DumpStream& ar, FEElementSet* p);\n\tstatic FEElementSet* LoadClass(DumpStream& ar, FEElementSet* p);\n\nprivate:\n\t// Build the lookup table\n\tvoid BuildLUT();\n\nprotected:\n\tstd::vector<int>\tm_Elem;\t\t//!< list of elements' global ID\n\n\tFEDomainList\t\tm_dom;\t//!< domain list that generated the element set\n\n\t// used for fast lookup in GetLocalIndex\n\tstd::vector<int>\tm_LUT;\n\tint\t\t\t\t\tm_minID, m_maxID;\n};\n\ninline int FEElementSet::GetLocalIndex(const FEElement& el) const\n{\n\tint eid = el.GetID();\n\tif ((eid < m_minID) || (eid > m_maxID)) return -1;\n\telse return m_LUT[el.GetID() - m_minID];\n}\n\ninline bool FEElementSet::Contains(const FEElement& el) const\n{\n\treturn (GetLocalIndex(el) != -1);\n}\n"
  },
  {
    "path": "FECore/FEElementShape.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEElementShape.h\"\n\nFEElementShape::FEElementShape(FE_Element_Shape eshape, int nodes) : m_shape(eshape), m_nodes(nodes)\n{\n}\n\nFEElementShape::~FEElementShape() \n{\n}\n"
  },
  {
    "path": "FECore/FEElementShape.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_enum.h\"\n\n//=============================================================================\n// Base class for defining element shape classes.\n// The element shape classes are used for evaluating shape functions and their derivatives.\nclass FEElementShape\n{\npublic:\n\tFEElementShape(FE_Element_Shape eshape, int nodes);\n\tvirtual ~FEElementShape();\n\n\tFE_Element_Shape shape() const { return m_shape; }\n\tint nodes() const { return m_nodes; }\n\nprivate:\n\tFE_Element_Shape\tm_shape;\n\tint\t\t\t\t\tm_nodes;\n};\n"
  },
  {
    "path": "FECore/FEElementTraits.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEElementTraits.h\"\n#include \"FEElement.h\"\n#include \"FEException.h\"\n#include \"FESolidElementShape.h\"\n#include \"FESurfaceElementShape.h\"\nusing namespace std;\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\nFEElementTraits::FEElementTraits(int ni, int ne, FE_Element_Class c, FE_Element_Shape s, FE_Element_Type t)\n{\n\tm_neln = ne;\n\tm_nint = ni;\n\tm_faces = 0;\n\tm_spec.eclass = c;\n\tm_spec.eshape = s;\n\tm_spec.etype  = t;\n\tm_H.resize(ni, ne);\n}\n\n//-----------------------------------------------------------------------------\n//! project mat3ds integration point data to nodes\nvoid FEElementTraits::project_to_nodes(mat3ds* si, mat3ds* so) const\n{\n\tdouble ai[FEElement::MAX_INTPOINTS];\n\tdouble ao[FEElement::MAX_NODES];\n\tfor (int i = 0; i<3; ++i) {\n\t\tfor (int j = i; j<3; ++j) {\n\t\t\tfor (int n = 0; n<m_nint; ++n) ai[n] = si[n](i, j);\n\t\t\tproject_to_nodes(ai, ao);\n\t\t\tfor (int n = 0; n<m_neln; ++n) so[n](i, j) = ao[n];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project mat3d integration point data to nodes\nvoid FEElementTraits::project_to_nodes(mat3d* si, mat3d* so) const\n{\n\tdouble ai[FEElement::MAX_INTPOINTS];\n\tdouble ao[FEElement::MAX_NODES];\n\tfor (int i = 0; i<3; ++i) {\n\t\tfor (int j = 0; j<3; ++j) {\n\t\t\tfor (int n = 0; n<m_nint; ++n) ai[n] = si[n](i, j);\n\t\t\tproject_to_nodes(ai, ao);\n\t\t\tfor (int n = 0; n<m_neln; ++n) so[n](i, j) = ao[n];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! project vec3d integration point data to nodes\nvoid FEElementTraits::project_to_nodes(vec3d* si, vec3d* so) const\n{\n\tdouble ai[FEElement::MAX_INTPOINTS];\n\tdouble ao[FEElement::MAX_NODES];\n\n\tfor (int n = 0; n<m_nint; ++n) ai[n] = si[n].x; project_to_nodes(ai, ao); for (int n = 0; n<m_neln; ++n) so[n].x = ao[n];\n\tfor (int n = 0; n<m_nint; ++n) ai[n] = si[n].y; project_to_nodes(ai, ao); for (int n = 0; n<m_neln; ++n) so[n].y = ao[n];\n\tfor (int n = 0; n<m_nint; ++n) ai[n] = si[n].z; project_to_nodes(ai, ao); for (int n = 0; n<m_neln; ++n) so[n].z = ao[n];\n}\n\n//=============================================================================\nFESolidElementTraits::FESolidElementTraits(int ni, int ne, FE_Element_Shape eshape, FE_Element_Type etype) : FEElementTraits(ni, ne, FE_ELEM_SOLID, eshape, etype) \n{\n\tm_shape = nullptr;\n\n\tgr.resize(ni);\n\tgs.resize(ni);\n\tgt.resize(ni);\n\tgw.resize(ni);\n\n\tm_Gr.resize(ni, ne);\n\tm_Gs.resize(ni, ne);\n\tm_Gt.resize(ni, ne);\n\n\tGrr.resize(ni, ne);\n\tGsr.resize(ni, ne);\n\tGtr.resize(ni, ne);\n\t\t\n\tGrs.resize(ni, ne);\n\tGss.resize(ni, ne);\n\tGts.resize(ni, ne);\n\t\t\n\tGrt.resize(ni, ne);\n\tGst.resize(ni, ne);\n\tGtt.resize(ni, ne);\n\n\t// TODO: Move this to the individual classes?\n\tm_faces = 0;\n\tswitch (eshape)\n\t{\n\tcase ET_TET4:\n\tcase ET_TET5:\n\tcase ET_TET10:\n\tcase ET_TET15:\n\tcase ET_TET20: \n\t\tm_faces = 4;\n\t\tbreak;\n\tcase ET_PENTA6:\n\tcase ET_PENTA15:\n\t\tm_faces = 5;\n\t\tbreak;\n\tcase ET_HEX8:\n\tcase ET_HEX20:\n\tcase ET_HEX27:\n\t\tm_faces = 6;\n\t\tbreak;\n\tcase ET_PYRA5:\n    case ET_PYRA13:\n\t\tm_faces = 5;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nint FESolidElementTraits::ShapeFunctions(int order)\n{\n\tFESolidElementShape* shape = m_shapeP[order];\n\treturn (shape ? shape->nodes(): 0);\n}\n\n//-----------------------------------------------------------------------------\n//! initialize element traits data\nvoid FESolidElementTraits::init()\n{\n\tassert(m_nint > 0);\n\tassert(m_neln > 0);\n\tconst int NELN = FEElement::MAX_NODES;\n\n\t// get shape class\n\tm_shape = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(m_spec.eshape));\n\tassert(m_shape && (m_shape->shape() == m_spec.eshape));\n\n\t// calculate shape function values at gauss points\n\tdouble N[NELN];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tm_shape->shape_fnc(N, gr[n], gs[n], gt[n]);\n\t\tfor (int i=0; i<m_neln; ++i) m_H[n][i] = N[i];\n\t}\n\n\t// calculate local derivatives of shape functions at gauss points\n\tdouble Hr[NELN], Hs[NELN], Ht[NELN];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tm_shape->shape_deriv(Hr, Hs, Ht, gr[n], gs[n], gt[n]);\n\t\tfor (int i=0; i<m_neln; ++i)\n\t\t{\n\t\t\tm_Gr[n][i] = Hr[i];\n\t\t\tm_Gs[n][i] = Hs[i];\n\t\t\tm_Gt[n][i] = Ht[i];\n\t\t}\n\t}\n\t\n\t// calculate local second derivatives of shape functions at gauss points\n\tdouble Hrr[NELN], Hss[NELN], Htt[NELN], Hrs[NELN], Hst[NELN], Hrt[NELN];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tm_shape->shape_deriv2(Hrr, Hss, Htt, Hrs, Hst, Hrt, gr[n], gs[n], gt[n]);\n\t\tfor (int i=0; i<m_neln; ++i)\n\t\t{\n\t\t\tGrr[n][i] = Hrr[i]; Grs[n][i] = Hrs[i]; Grt[n][i] = Hrt[i]; \n\t\t\tGsr[n][i] = Hrs[i]; Gss[n][i] = Hss[i]; Gst[n][i] = Hst[i]; \n\t\t\tGtr[n][i] = Hrt[i]; Gts[n][i] = Hst[i]; Gtt[n][i] = Htt[i]; \n\t\t}\n\t}\n\n\t// NOTE: Below, is a new interface for dealing with mixed element formulations.\n\t//       This is still a work in progress.\n\n\t// Get the max interpolation order\n\tconst int maxOrder = (int) m_shapeP.size() - 1;\n\tm_Hp.resize(maxOrder + 1);\n\tm_Gr_p.resize(maxOrder + 1);\n\tm_Gs_p.resize(maxOrder + 1);\n\tm_Gt_p.resize(maxOrder + 1);\n\tfor (int i = 0; i <= maxOrder; ++i)\n\t{\n\t\tFESolidElementShape* shape = m_shapeP[i];\n\t\tmatrix& H = m_Hp[i];\n\t\tmatrix& Gr = m_Gr_p[i];\n\t\tmatrix& Gs = m_Gs_p[i];\n\t\tmatrix& Gt = m_Gt_p[i];\n\t\tif (i == 0)\n\t\t{\n\t\t\tH.resize(m_nint, 1);\n\t\t\tGr.resize(m_nint, 1);\n\t\t\tGs.resize(m_nint, 1);\n\t\t\tGt.resize(m_nint, 1);\n\t\t\tfor (int n = 0; n < m_nint; ++n)\n\t\t\t{\n\t\t\t\tH[n][0] = 1.0;\n\t\t\t\tGr[n][0] = Gs[n][0] = Gt[n][0] = 0.0;\n\t\t\t}\n\t\t}\n\t\telse if (m_shapeP[i])\n\t\t{\n\t\t\t// get the nodes\n\t\t\tint neln = shape->nodes();\n\n\t\t\t// shape function values\n\t\t\tH.resize(m_nint, neln);\n\t\t\tfor (int n = 0; n<m_nint; ++n)\n\t\t\t{\n\t\t\t\tm_shapeP[i]->shape_fnc(N, gr[n], gs[n], gt[n]);\n\t\t\t\tfor (int j = 0; j<neln; ++j) H[n][j] = N[j];\n\t\t\t}\n\n\t\t\t// calculate local derivatives of shape functions at gauss points\n\t\t\tGr.resize(m_nint, neln);\n\t\t\tGs.resize(m_nint, neln);\n\t\t\tGt.resize(m_nint, neln);\n\t\t\tfor (int n = 0; n<m_nint; ++n)\n\t\t\t{\n\t\t\t\tshape->shape_deriv(Hr, Hs, Ht, gr[n], gs[n], gt[n]);\n\t\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tGr[n][j] = Hr[j];\n\t\t\t\t\tGs[n][j] = Hs[j];\n\t\t\t\t\tGt[n][j] = Ht[j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//! values of shape functions\nvoid FESolidElementTraits::shape_fnc(double* H, double r, double s, double t)\n{\n\treturn m_shape->shape_fnc(H, r, s, t); \n}\n\n//! values of shape function derivatives\nvoid FESolidElementTraits::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\treturn m_shape->shape_deriv(Hr, Hs, Ht, r, s, t);\n}\n\n//! values of shape function second derivatives\nvoid FESolidElementTraits::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\treturn m_shape->shape_deriv2(Hrr, Hss, Htt, Hrs, Hst, Hrt, r, s, t);\n}\n\n//=============================================================================\n//                                F E H E X 8\n//=============================================================================\n\nvoid FEHex8_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(2);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_HEX8));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n\n//*****************************************************************************\n//                          H E X 8 G 8 \n//*****************************************************************************\n\nFEHex8G8::FEHex8G8() : FEHex8_(NINT, FE_HEX8G8)\n{\n\t// integration point coordinates\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gs[0] = -a; gt[0] = -a; gw[0] = 1;\n\tgr[1] =  a; gs[1] = -a; gt[1] = -a; gw[1] = 1;\n\tgr[2] =  a; gs[2] =  a; gt[2] = -a; gw[2] = 1;\n\tgr[3] = -a; gs[3] =  a; gt[3] = -a; gw[3] = 1;\n\tgr[4] = -a; gs[4] = -a; gt[4] =  a; gw[4] = 1;\n\tgr[5] =  a; gs[5] = -a; gt[5] =  a; gw[5] = 1;\n\tgr[6] =  a; gs[6] =  a; gt[6] =  a; gw[6] = 1;\n\tgr[7] = -a; gs[7] =  a; gt[7] =  a; gw[7] = 1;\n\tinit();\n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEHex8G8::project_to_nodes(double* ai, double* ao) const\n{\n\tfor (int j=0; j<NELN; ++j)\n\t{\n\t\tao[j] = 0;\n\t\tfor (int k=0; k<NINT; ++k) \n\t\t{\n\t\t\tao[j] += m_Hi[j][k]*ai[k];\n\t\t}\n\t}\n}\n\n//*****************************************************************************\n//                          F E H E X R I \n//*****************************************************************************\n\nFEHex8RI::FEHex8RI(): FEHex8_(NINT, FE_HEX8RI)\n{\n\t// This is for a six point integration rule\n\t// integration point coordinates\n\tconst double a = 8.0 / 6.0;\n\tgr[0] = -1; gs[0] = 0; gt[0] = 0; gw[0] = a;\n\tgr[1] =  1; gs[1] = 0; gt[1] = 0; gw[1] = a;\n\tgr[2] =  0; gs[2] =-1; gt[2] = 0; gw[2] = a;\n\tgr[3] =  0; gs[3] = 1; gt[3] = 0; gw[3] = a;\n\tgr[4] =  0; gs[4] = 0; gt[4] =-1; gw[4] = a;\n\tgr[5] =  0; gs[5] = 0; gt[5] = 1; gw[5] = a;\n\t\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement this\nvoid FEHex8RI::project_to_nodes(double* ai, double* ao) const\n{\n\t\n}\n\n//*****************************************************************************\n//                          F E H E X G 1\n//*****************************************************************************\n\nFEHex8G1::FEHex8G1() : FEHex8_(NINT, FE_HEX8G1)\n{\n\t// single gauss-point integration rule\n\tgr[0] = 0; gs[0] = 0; gt[0] = 0; gw[0] = 8.0;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEHex8G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n\tao[2] = ai[0];\n\tao[3] = ai[0];\n\tao[4] = ai[0];\n\tao[5] = ai[0];\n\tao[6] = ai[0];\n\tao[7] = ai[0];\n}\n\n//=============================================================================\n//                              F E T E T 4\n//=============================================================================\n\n//! initialize element traits data\nvoid FETet4_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(2);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TET4));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n//=============================================================================\n//                          T E T 4\n//=============================================================================\n\nFETet4G4::FETet4G4() : FETet4_(NINT, FE_TET4G4)\n{\n\t// gaussian integration for tetrahedral elements\n\tconst double a = 0.58541020;\n\tconst double b = 0.13819660;\n\tconst double w = 1.0 / 24.0;\n\t\n\tgr[0] = b; gs[0] = b; gt[0] = b; gw[0] = w;\n\tgr[1] = a; gs[1] = b; gt[1] = b; gw[1] = w;\n\tgr[2] = b; gs[2] = a; gt[2] = b; gw[2] = w;\n\tgr[3] = b; gs[3] = b; gt[3] = a; gw[3] = w;\n\n\tinit();\n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet4G4::project_to_nodes(double* ai, double* ao) const\n{\n\tfor (int j=0; j<NELN; ++j)\n\t{\n\t\tao[j] = 0;\n\t\tfor (int k=0; k<NINT; ++k) \n\t\t{\n\t\t\tao[j] += m_Hi[j][k]*ai[k];\n\t\t}\n\t}\n}\n\n//=============================================================================\n//                              F E T E T 5\n//=============================================================================\n\n//! initialize element traits data\nvoid FETet5_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TET4));\n\tm_shapeP[2] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TET5));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n//=============================================================================\n//                          T E T 5 G 4\n//=============================================================================\n\nFETet5G4::FETet5G4() : FETet5_(NINT, FE_TET5G4)\n{\n\t// gaussian integration for tetrahedral elements\n\tconst double a = 0.58541020;\n\tconst double b = 0.13819660;\n\tconst double w = 1.0 / 24.0;\n\n\tgr[0] = b; gs[0] = b; gt[0] = b; gw[0] = w;\n\tgr[1] = a; gs[1] = b; gt[1] = b; gw[1] = w;\n\tgr[2] = b; gs[2] = a; gt[2] = b; gw[2] = w;\n\tgr[3] = b; gs[3] = b; gt[3] = a; gw[3] = w;\n\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet5G4::project_to_nodes(double* ai, double* ao) const\n{\n\t// TODO: implement this\n\tassert(false);\n}\n\n\n//=============================================================================\n//                          F E G 1 T E T E L E M E N T\n//=============================================================================\n\nFETet4G1::FETet4G1() : FETet4_(NINT, FE_TET4G1)\n{\n\t// gaussian integration for tetrahedral elements\n\tconst double a = 0.25;\n\tconst double w = 1.0 / 6.0;\n\t\n\tgr[0] = a; gs[0] = a; gt[0] = a; gw[0] = w;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet4G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n\tao[2] = ai[0];\n\tao[3] = ai[0];\n}\n\n//=============================================================================\n//                       P E N T A 6\n//=============================================================================\n\nvoid FEPenta6_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(2);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_PENTA6));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n//=============================================================================\n//                         P E N T A 6 G 6\n//=============================================================================\n\nFEPenta6G6::FEPenta6G6(): FEPenta6_(NINT, FE_PENTA6G6)\n{\n\t//gauss intergration points\n\tconst double a = 1.0/6.0;\n\tconst double b = 2.0/3.0;\n\tconst double c = 1.0 / sqrt(3.0);\n\tconst double w = 1.0 / 6.0;\n\t\n\tgr[0] = a; gs[0] = a; gt[0] = -c; gw[0] = w;\n\tgr[1] = b; gs[1] = a; gt[1] = -c; gw[1] = w;\n\tgr[2] = a; gs[2] = b; gt[2] = -c; gw[2] = w;\n\tgr[3] = a; gs[3] = a; gt[3] =  c; gw[3] = w;\n\tgr[4] = b; gs[4] = a; gt[4] =  c; gw[4] = w;\n\tgr[5] = a; gs[5] = b; gt[5] =  c; gw[5] = w;\n\n\tinit();\n\n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPenta6G6::project_to_nodes(double* ai, double* ao) const\n{\n\tfor (int j=0; j<NELN; ++j)\n\t{\n\t\tao[j] = 0;\n\t\tfor (int k=0; k<NINT; ++k) \n\t\t{\n\t\t\tao[j] += m_Hi[j][k]*ai[k];\n\t\t}\n\t}\n}\n\n//=============================================================================\n//                       P E N T A 1 5\n//=============================================================================\n\n//=============================================================================\n//                          F E P E N T A 1 5 G 8\n//=============================================================================\n\nint FEPenta15G8::ni[NELN] = {};\n\nFEPenta15G8::FEPenta15G8() : FEPenta15_(NINT, FE_PENTA15G8)\n{\n    const double a = 1.0/3.0;\n    const double b = 1.0/5.0;\n    const double c = 3.0/5.0;\n    const double d = sqrt(a);\n    gr[0] = a; gs[0] = a; gt[0] = -d; gw[0] = -27.0/96.0;\n    gr[1] = c; gs[1] = b; gt[1] = -d; gw[1] =  25.0/96.0;\n    gr[2] = b; gs[2] = b; gt[2] = -d; gw[2] =  25.0/96.0;\n    gr[3] = b; gs[3] = c; gt[3] = -d; gw[3] =  25.0/96.0;\n    gr[4] = a; gs[4] = a; gt[4] =  d; gw[4] = -27.0/96.0;\n    gr[5] = c; gs[5] = b; gt[5] =  d; gw[5] =  25.0/96.0;\n    gr[6] = b; gs[6] = b; gt[6] =  d; gw[6] =  25.0/96.0;\n    gr[7] = b; gs[7] = c; gt[7] =  d; gw[7] =  25.0/96.0;\n    \n    init();\n    \n\tm_MT.resize(NELN, NINT);\n    for (int i=0; i<NINT; ++i)\n        for (int n=0; n<NELN; ++n)\n\t\t\tm_MT(n,i) = m_H(i,n);\n    \n\tm_Hi.resize(NELN, NELN);\n\tm_Hi = m_MT*m_MT.transpose();\n\tm_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\n//! Use least-squares extrapolation\nvoid FEPenta15G8::project_to_nodes(double* ai, double* ao) const\n{\n    double v[NELN];\n    for (int n=0; n<NELN; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<NELN; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<NELN; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//                          F E P E N T A 1 5 G 2 1\n//=============================================================================\n\nint FEPenta15G21::ni[NELN] = { 1, 2, 3, 4, 5, 6, 8, 9, 10, 15, 16, 17, 18, 19, 20 };\n\nFEPenta15G21::FEPenta15G21() : FEPenta15_(NINT, FE_PENTA15G21)\n{\n    const double w = 1.0/2.0;\n    const double a = 0.774596669241483;\n    const double w1 = 5.0 / 9.0;\n    const double w2 = 8.0 / 9.0;\n    gr[ 0] = 0.333333333333333; gs[ 0] = 0.333333333333333; gt[ 0] = -a; gw[ 0] = w*w1*0.225000000000000;\n    gr[ 1] = 0.797426985353087; gs[ 1] = 0.101286507323456; gt[ 1] = -a; gw[ 1] = w*w1*0.125939180544827;\n    gr[ 2] = 0.101286507323456; gs[ 2] = 0.797426985353087; gt[ 2] = -a; gw[ 2] = w*w1*0.125939180544827;\n    gr[ 3] = 0.101286507323456; gs[ 3] = 0.101286507323456; gt[ 3] = -a; gw[ 3] = w*w1*0.125939180544827;\n    gr[ 4] = 0.470142064105115; gs[ 4] = 0.470142064105115; gt[ 4] = -a; gw[ 4] = w*w1*0.132394152788506;\n    gr[ 5] = 0.470142064105115; gs[ 5] = 0.059715871789770; gt[ 5] = -a; gw[ 5] = w*w1*0.132394152788506;\n    gr[ 6] = 0.059715871789770; gs[ 6] = 0.470142064105115; gt[ 6] = -a; gw[ 6] = w*w1*0.132394152788506;\n    gr[ 7] = 0.333333333333333; gs[ 7] = 0.333333333333333; gt[ 7] =  0; gw[ 7] = w*w2*0.225000000000000;\n    gr[ 8] = 0.797426985353087; gs[ 8] = 0.101286507323456; gt[ 8] =  0; gw[ 8] = w*w2*0.125939180544827;\n    gr[ 9] = 0.101286507323456; gs[ 9] = 0.797426985353087; gt[ 9] =  0; gw[ 9] = w*w2*0.125939180544827;\n    gr[10] = 0.101286507323456; gs[10] = 0.101286507323456; gt[10] =  0; gw[10] = w*w2*0.125939180544827;\n    gr[11] = 0.470142064105115; gs[11] = 0.470142064105115; gt[11] =  0; gw[11] = w*w2*0.132394152788506;\n    gr[12] = 0.470142064105115; gs[12] = 0.059715871789770; gt[12] =  0; gw[12] = w*w2*0.132394152788506;\n    gr[13] = 0.059715871789770; gs[13] = 0.470142064105115; gt[13] =  0; gw[13] = w*w2*0.132394152788506;\n    gr[14] = 0.333333333333333; gs[14] = 0.333333333333333; gt[14] =  a; gw[14] = w*w1*0.225000000000000;\n    gr[15] = 0.797426985353087; gs[15] = 0.101286507323456; gt[15] =  a; gw[15] = w*w1*0.125939180544827;\n    gr[16] = 0.101286507323456; gs[16] = 0.797426985353087; gt[16] =  a; gw[16] = w*w1*0.125939180544827;\n    gr[17] = 0.101286507323456; gs[17] = 0.101286507323456; gt[17] =  a; gw[17] = w*w1*0.125939180544827;\n    gr[18] = 0.470142064105115; gs[18] = 0.470142064105115; gt[18] =  a; gw[18] = w*w1*0.132394152788506;\n    gr[19] = 0.470142064105115; gs[19] = 0.059715871789770; gt[19] =  a; gw[19] = w*w1*0.132394152788506;\n    gr[20] = 0.059715871789770; gs[20] = 0.470142064105115; gt[20] =  a; gw[20] = w*w1*0.132394152788506;\n\n    init();\n    \n    m_MT.resize(NELN, NINT);\n    for (int i=0; i<NINT; ++i)\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n);\n    \n    m_Hi.resize(NELN, NELN);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEPenta15G21::project_to_nodes(double* ai, double* ao) const\n{\n    double v[NELN];\n    for (int n=0; n<NELN; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<NELN; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<NELN; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//                           T E T 1 0\n//=============================================================================\n\n//! initialize element traits data\nvoid FETet10_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TET4));\n\tm_shapeP[2] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TET10));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n//=============================================================================\n//                          T E T 1 0 G 1\n//=============================================================================\n\nFETet10G1::FETet10G1() : FETet10_(NINT, FE_TET10G1)\n{\n\t// gaussian integration for tetrahedral elements\n\tconst double a = 0.25;\n\tconst double w = 1.0 / 6.0;\n\t\n\tgr[0] = a; gs[0] = a; gt[0] = a; gw[0] = w;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet10G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n\tao[2] = ai[0];\n\tao[3] = ai[0];\n\tao[4] = ai[0];\n\tao[5] = ai[0];\n\tao[6] = ai[0];\n\tao[7] = ai[0];\n\tao[8] = ai[0];\n\tao[9] = ai[0];\n}\n\n//*****************************************************************************\n//                          F E T E T 1 0 E L E M E N T\n//*****************************************************************************\n// I think this assumes that the tetrahedron in natural space is essentially\n// a constant metric tet with the edge nodes at the center of the edges.\nFETet10G4::FETet10G4() : FETet10_(NINT, FE_TET10G4)\n{\n\t// integration point coordinates\n\tconst double a = 0.58541020;\n\tconst double b = 0.13819660;\n\tconst double w = 0.25 / 6.0;\n\tgr[ 0] = a; gs[ 0] = b; gt[ 0] = b; gw[ 0] = w;\n\tgr[ 1] = b; gs[ 1] = a; gt[ 1] = b; gw[ 1] = w;\n\tgr[ 2] = b; gs[ 2] = b; gt[ 2] = a; gw[ 2] = w;\n\tgr[ 3] = b; gs[ 3] = b; gt[ 3] = b; gw[ 3] = w;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tmatrix A(4,4);\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tdouble r = gr[i];\n\t\tdouble s = gs[i];\n\t\tdouble t = gt[i];\n\n\t\tA[i][0] = 1 - r - s - t;\n\t\tA[i][1] = r;\n\t\tA[i][2] = s;\n\t\tA[i][3] = t;\n\t}\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet10G4::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = Ai[0][0]*ai[0] + Ai[0][1]*ai[1] + Ai[0][2]*ai[2] + Ai[0][3]*ai[3];\n\tao[1] = Ai[1][0]*ai[0] + Ai[1][1]*ai[1] + Ai[1][2]*ai[2] + Ai[1][3]*ai[3];\n\tao[2] = Ai[2][0]*ai[0] + Ai[2][1]*ai[1] + Ai[2][2]*ai[2] + Ai[2][3]*ai[3];\n\tao[3] = Ai[3][0]*ai[0] + Ai[3][1]*ai[1] + Ai[3][2]*ai[2] + Ai[3][3]*ai[3];\n\n\tao[4] = 0.5*(ao[0] + ao[1]);\n\tao[5] = 0.5*(ao[1] + ao[2]);\n\tao[6] = 0.5*(ao[2] + ao[0]);\n\tao[7] = 0.5*(ao[0] + ao[3]);\n\tao[8] = 0.5*(ao[1] + ao[3]);\n\tao[9] = 0.5*(ao[2] + ao[3]);\n}\n\n//=============================================================================\n//                          T E T 1 0 G 8\n//=============================================================================\n// I think this assumes that the tetrahedron in natural space is essentially\n// a constant metric tet with the edge nodes at the center of the edges.\nFETet10G8::FETet10G8() : FETet10_(NINT, FE_TET10G8)\n{\n\tconst double w = 1.0/6.0;\n    gr[0] = 0.0158359099; gs[0] = 0.3280546970; gt[0] = 0.3280546970; gw[0] = 0.138527967*w;\n    gr[1] = 0.3280546970; gs[1] = 0.0158359099; gt[1] = 0.3280546970; gw[1] = 0.138527967*w;\n    gr[2] = 0.3280546970; gs[2] = 0.3280546970; gt[2] = 0.0158359099; gw[2] = 0.138527967*w;\n    gr[3] = 0.3280546970; gs[3] = 0.3280546970; gt[3] = 0.3280546970; gw[3] = 0.138527967*w;\n    gr[4] = 0.6791431780; gs[4] = 0.1069522740; gt[4] = 0.1069522740; gw[4] = 0.111472033*w;\n    gr[5] = 0.1069522740; gs[5] = 0.6791431780; gt[5] = 0.1069522740; gw[5] = 0.111472033*w;\n    gr[6] = 0.1069522740; gs[6] = 0.1069522740; gt[6] = 0.6791431780; gw[6] = 0.111472033*w;\n    gr[7] = 0.1069522740; gs[7] = 0.1069522740; gt[7] = 0.1069522740; gw[7] = 0.111472033*w;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tN.resize(8, 4);\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\tN[i][0] = 1.0 - gr[i] - gs[i] - gt[i];\n\t\tN[i][1] = gr[i];\n\t\tN[i][2] = gs[i];\n\t\tN[i][3] = gt[i];\n\t}\n\n\tmatrix A(4, 4);\n\tA = N.transpose()*N;\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet10G8::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(4);\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += N[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tao[i] = 0.0;\n\t\tfor (int j=0; j<4; ++j) ao[i] += Ai[i][j]*b[j];\n\t}\n\n\tao[4] = 0.5*(ao[0] + ao[1]);\n\tao[5] = 0.5*(ao[1] + ao[2]);\n\tao[6] = 0.5*(ao[2] + ao[0]);\n\tao[7] = 0.5*(ao[0] + ao[3]);\n\tao[8] = 0.5*(ao[1] + ao[3]);\n\tao[9] = 0.5*(ao[2] + ao[3]);\n}\n\n//=============================================================================\n//                          T E T 1 0 G 4 R I 1\n//=============================================================================\n\nFETet10G4RI1::FETet10G4RI1()\n{\n\tm_pTRI = new FETet10G1;\n}\n\n//=============================================================================\n//                          T E T 1 0 G 8 R I 4\n//=============================================================================\n\nFETet10G8RI4::FETet10G8RI4()\n{\n\tm_pTRI = new FETet10G4;\n}\n\n//=============================================================================\n//                             T E T 1 0 G L 1 1\n//=============================================================================\n// I think this assumes that the tetrahedron in natural space is essentially\n// a constant metric tet with the edge nodes at the center of the edges.\nFETet10GL11::FETet10GL11() : FETet10_(NINT, FE_TET10GL11)\n{\n\tconst double w = 1.0/6.0;\n\tconst double a = w*1.0/60.0;\n\tconst double b = w*4.0/60.0;\n\tgr[ 0] = 0.0; gs[ 0] = 0.0; gt[ 0] = 0.0; gw[ 0] = a;\n\tgr[ 1] = 1.0; gs[ 1] = 0.0; gt[ 1] = 0.0; gw[ 1] = a;\n\tgr[ 2] = 0.0; gs[ 2] = 1.0; gt[ 2] = 0.0; gw[ 2] = a;\n\tgr[ 3] = 0.0; gs[ 3] = 0.0; gt[ 3] = 1.0; gw[ 3] = a;\n\tgr[ 4] = 0.5; gs[ 4] = 0.0; gt[ 4] = 0.0; gw[ 4] = b;\n\tgr[ 5] = 0.5; gs[ 5] = 0.5; gt[ 5] = 0.0; gw[ 5] = b;\n\tgr[ 6] = 0.0; gs[ 6] = 0.5; gt[ 6] = 0.0; gw[ 6] = b;\n\tgr[ 7] = 0.0; gs[ 7] = 0.0; gt[ 7] = 0.5; gw[ 7] = b;\n\tgr[ 8] = 0.5; gs[ 8] = 0.0; gt[ 8] = 0.5; gw[ 8] = b;\n\tgr[ 9] = 0.0; gs[ 9] = 0.5; gt[ 9] = 0.5; gw[ 9] = b;\n\tgr[10] = 0.25; gs[10] = 0.25; gt[10] = 0.25; gw[10] = 32*a;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet10GL11::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0]; ao[1] = ai[1]; ao[2] = ai[2]; ao[3] = ai[3];\n\tao[4] = ai[4]; ao[5] = ai[5]; ao[6] = ai[6]; ao[7] = ai[7]; ao[8] = ai[8]; ao[9] = ai[9];\n}\n\n//=============================================================================\n//                           T E T 1 5\n//=============================================================================\n\n//=============================================================================\n//                          T E T 1 5 G 4\n//=============================================================================\nFETet15G4::FETet15G4() : FETet15_(NINT, FE_TET15G4)\n{\n\t// integration point coordinates\n\tconst double a = 0.58541020;\n\tconst double b = 0.13819660;\n\tconst double w = 0.25 / 6.0;\n\tgr[ 0] = a; gs[ 0] = b; gt[ 0] = b; gw[ 0] = w;\n\tgr[ 1] = b; gs[ 1] = a; gt[ 1] = b; gw[ 1] = w;\n\tgr[ 2] = b; gs[ 2] = b; gt[ 2] = a; gw[ 2] = w;\n\tgr[ 3] = b; gs[ 3] = b; gt[ 3] = b; gw[ 3] = w;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tmatrix A(4,4);\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tdouble r = gr[i];\n\t\tdouble s = gs[i];\n\t\tdouble t = gt[i];\n\n\t\tA[i][0] = 1 - r - s - t;\n\t\tA[i][1] = r;\n\t\tA[i][2] = s;\n\t\tA[i][3] = t;\n\t}\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet15G4::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = Ai[0][0]*ai[0] + Ai[0][1]*ai[1] + Ai[0][2]*ai[2] + Ai[0][3]*ai[3];\n\tao[1] = Ai[1][0]*ai[0] + Ai[1][1]*ai[1] + Ai[1][2]*ai[2] + Ai[1][3]*ai[3];\n\tao[2] = Ai[2][0]*ai[0] + Ai[2][1]*ai[1] + Ai[2][2]*ai[2] + Ai[2][3]*ai[3];\n\tao[3] = Ai[3][0]*ai[0] + Ai[3][1]*ai[1] + Ai[3][2]*ai[2] + Ai[3][3]*ai[3];\n\n\tao[4] = 0.5*(ao[0] + ao[1]);\n\tao[5] = 0.5*(ao[1] + ao[2]);\n\tao[6] = 0.5*(ao[2] + ao[0]);\n\tao[7] = 0.5*(ao[0] + ao[3]);\n\tao[8] = 0.5*(ao[1] + ao[3]);\n\tao[9] = 0.5*(ao[2] + ao[3]);\n\n\tao[10] = (ao[0] + ao[1] + ao[2])/3.0;\n\tao[11] = (ao[0] + ao[1] + ao[3])/3.0;\n\tao[12] = (ao[1] + ao[2] + ao[3])/3.0;\n\tao[13] = (ao[0] + ao[2] + ao[3])/3.0;\n\n\tao[14] = 0.25*(ao[0] + ao[1] + ao[2] + ao[3]);\n}\n\n\n//=============================================================================\n//                          T E T 1 5 G 8\n//=============================================================================\n// I think this assumes that the tetrahedron in natural space is essentially\n// a constant metric tet with the edge nodes at the center of the edges.\nFETet15G8::FETet15G8() : FETet15_(NINT, FE_TET15G8)\n{\n\tconst double w = 1.0/6.0;\n    gr[0] = 0.0158359099; gs[0] = 0.3280546970; gt[0] = 0.3280546970; gw[0] = 0.138527967*w;\n    gr[1] = 0.3280546970; gs[1] = 0.0158359099; gt[1] = 0.3280546970; gw[1] = 0.138527967*w;\n    gr[2] = 0.3280546970; gs[2] = 0.3280546970; gt[2] = 0.0158359099; gw[2] = 0.138527967*w;\n    gr[3] = 0.3280546970; gs[3] = 0.3280546970; gt[3] = 0.3280546970; gw[3] = 0.138527967*w;\n    gr[4] = 0.6791431780; gs[4] = 0.1069522740; gt[4] = 0.1069522740; gw[4] = 0.111472033*w;\n    gr[5] = 0.1069522740; gs[5] = 0.6791431780; gt[5] = 0.1069522740; gw[5] = 0.111472033*w;\n    gr[6] = 0.1069522740; gs[6] = 0.1069522740; gt[6] = 0.6791431780; gw[6] = 0.111472033*w;\n    gr[7] = 0.1069522740; gs[7] = 0.1069522740; gt[7] = 0.1069522740; gw[7] = 0.111472033*w;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tN.resize(8, 4);\n\tfor (int i=0; i<8; ++i)\n\t{\n\t\tN[i][0] = 1.0 - gr[i] - gs[i] - gt[i];\n\t\tN[i][1] = gr[i];\n\t\tN[i][2] = gs[i];\n\t\tN[i][3] = gt[i];\n\t}\n\n\tmatrix A(4, 4);\n\tA = N.transpose()*N;\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet15G8::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(4);\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += N[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tao[i] = 0.0;\n\t\tfor (int j=0; j<4; ++j) ao[i] += Ai[i][j]*b[j];\n\t}\n\n\tao[4] = 0.5*(ao[0] + ao[1]);\n\tao[5] = 0.5*(ao[1] + ao[2]);\n\tao[6] = 0.5*(ao[2] + ao[0]);\n\tao[7] = 0.5*(ao[0] + ao[3]);\n\tao[8] = 0.5*(ao[1] + ao[3]);\n\tao[9] = 0.5*(ao[2] + ao[3]);\n\n\tao[10] = (ao[0] + ao[1] + ao[2])/3.0;\n\tao[11] = (ao[0] + ao[1] + ao[3])/3.0;\n\tao[12] = (ao[1] + ao[2] + ao[3])/3.0;\n\tao[13] = (ao[0] + ao[2] + ao[3])/3.0;\n\n\tao[14] = (ao[0] + ao[1] + ao[2] + ao[3])*0.25;\n}\n\n//=============================================================================\n//                          T E T 1 5 G 1 1\n//=============================================================================\nFETet15G11::FETet15G11() : FETet15_(NINT, FE_TET15G11)\n{\n    gr[0] = 0.25; gs[0] = 0.25; gt[0] = 0.25; gw[0] = -0.01315555556;\n\n    gr[1] = 0.071428571428571; gs[1] = 0.071428571428571; gt[1] = 0.071428571428571; gw[1] = 0.007622222222;\n    gr[2] = 0.785714285714286; gs[2] = 0.071428571428571; gt[2] = 0.071428571428571; gw[2] = 0.007622222222;\n    gr[3] = 0.071428571428571; gs[3] = 0.785714285714286; gt[3] = 0.071428571428571; gw[3] = 0.007622222222;\n    gr[4] = 0.071428571428571; gs[4] = 0.071428571428571; gt[4] = 0.785714285714286; gw[4] = 0.007622222222;\n\n    gr[ 5] = 0.399403576166799; gs[ 5] = 0.100596423833201; gt[ 5] = 0.100596423833201; gw[ 5] = 0.024888888889;\n    gr[ 6] = 0.100596423833201; gs[ 6] = 0.399403576166799; gt[ 6] = 0.100596423833201; gw[ 6] = 0.024888888889;\n    gr[ 7] = 0.100596423833201; gs[ 7] = 0.100596423833201; gt[ 7] = 0.399403576166799; gw[ 7] = 0.024888888889;\n    gr[ 8] = 0.399403576166799; gs[ 8] = 0.399403576166799; gt[ 8] = 0.100596423833201; gw[ 8] = 0.024888888889;\n    gr[ 9] = 0.399403576166799; gs[ 9] = 0.100596423833201; gt[ 9] = 0.399403576166799; gw[ 9] = 0.024888888889;\n    gr[10] = 0.100596423833201; gs[10] = 0.399403576166799; gt[10] = 0.399403576166799; gw[10] = 0.024888888889;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tN.resize(11, 4);\n\tfor (int i=0; i<11; ++i)\n\t{\n\t\tN[i][0] = 1.0 - gr[i] - gs[i] - gt[i];\n\t\tN[i][1] = gr[i];\n\t\tN[i][2] = gs[i];\n\t\tN[i][3] = gt[i];\n\t}\n\n\tmatrix A(4, 4);\n\tA = N.transpose()*N;\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet15G11::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(4);\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += N[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tao[i] = 0.0;\n\t\tfor (int j=0; j<4; ++j) ao[i] += Ai[i][j]*b[j];\n\t}\n\n\tao[4] = 0.5*(ao[0] + ao[1]);\n\tao[5] = 0.5*(ao[1] + ao[2]);\n\tao[6] = 0.5*(ao[2] + ao[0]);\n\tao[7] = 0.5*(ao[0] + ao[3]);\n\tao[8] = 0.5*(ao[1] + ao[3]);\n\tao[9] = 0.5*(ao[2] + ao[3]);\n\n\tao[10] = (ao[0] + ao[1] + ao[2])/3.0;\n\tao[11] = (ao[0] + ao[1] + ao[3])/3.0;\n\tao[12] = (ao[1] + ao[2] + ao[3])/3.0;\n\tao[13] = (ao[0] + ao[2] + ao[3])/3.0;\n\n\tao[14] = (ao[0] + ao[1] + ao[2] + ao[3])*0.25;\n}\n\n//=============================================================================\n//                          T E T 1 5 G 1 5\n//=============================================================================\nFETet15G15::FETet15G15() : FETet15_(NINT, FE_TET15G15)\n{\n    gr[0] = 0.25; gs[0] = 0.25; gt[0] = 0.25; gw[0] = 0.030283678097089;\n\n    gr[1] = 0.333333333333333; gs[1] = 0.333333333333333; gt[1] = 0.333333333333333; gw[1] = 0.006026785714286;\n    gr[2] = 0.000000000000000; gs[2] = 0.333333333333333; gt[2] = 0.333333333333333; gw[2] = 0.006026785714286;\n    gr[3] = 0.333333333333333; gs[3] = 0.000000000000000; gt[3] = 0.333333333333333; gw[3] = 0.006026785714286;\n    gr[4] = 0.333333333333333; gs[4] = 0.333333333333333; gt[4] = 0.000000000000000; gw[4] = 0.006026785714286;\n\n    gr[ 5] = 0.090909090909091; gs[ 5] = 0.090909090909091; gt[ 5] = 0.090909090909091; gw[ 5] = 0.011645249086029;\n    gr[ 6] = 0.727272727272727; gs[ 6] = 0.090909090909091; gt[ 6] = 0.090909090909091; gw[ 6] = 0.011645249086029;\n    gr[ 7] = 0.090909090909091; gs[ 7] = 0.727272727272727; gt[ 7] = 0.090909090909091; gw[ 7] = 0.011645249086029;\n    gr[ 8] = 0.090909090909091; gs[ 8] = 0.090909090909091; gt[ 8] = 0.727272727272727; gw[ 8] = 0.011645249086029;\n\n    gr[ 9] = 0.433449846426336; gs[ 9] = 0.066550153573664; gt[ 9] = 0.066550153573664; gw[ 9] = 0.010949141561386;\n    gr[10] = 0.066550153573664; gs[10] = 0.433449846426336; gt[10] = 0.066550153573664; gw[10] = 0.010949141561386;\n    gr[11] = 0.066550153573664; gs[11] = 0.066550153573664; gt[11] = 0.433449846426336; gw[11] = 0.010949141561386;\n    gr[12] = 0.066550153573664; gs[12] = 0.433449846426336; gt[12] = 0.433449846426336; gw[12] = 0.010949141561386;\n    gr[13] = 0.433449846426336; gs[13] = 0.066550153573664; gt[13] = 0.433449846426336; gw[13] = 0.010949141561386;\n    gr[14] = 0.433449846426336; gs[14] = 0.433449846426336; gt[14] = 0.066550153573664; gw[14] = 0.010949141561386;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tN.resize(NINT, 4);\n\tfor (int i=0; i<NINT; ++i)\n\t{\n\t\tN[i][0] = 1.0 - gr[i] - gs[i] - gt[i];\n\t\tN[i][1] = gr[i];\n\t\tN[i][2] = gs[i];\n\t\tN[i][3] = gt[i];\n\t}\n\n\tmatrix A(4, 4);\n\tA = N.transpose()*N;\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet15G15::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(4);\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += N[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<4; ++i)\n\t{\n\t\tao[i] = 0.0;\n\t\tfor (int j=0; j<4; ++j) ao[i] += Ai[i][j]*b[j];\n\t}\n\n\tao[4] = 0.5*(ao[0] + ao[1]);\n\tao[5] = 0.5*(ao[1] + ao[2]);\n\tao[6] = 0.5*(ao[2] + ao[0]);\n\tao[7] = 0.5*(ao[0] + ao[3]);\n\tao[8] = 0.5*(ao[1] + ao[3]);\n\tao[9] = 0.5*(ao[2] + ao[3]);\n\n\tao[10] = (ao[0] + ao[1] + ao[2])/3.0;\n\tao[11] = (ao[0] + ao[1] + ao[3])/3.0;\n\tao[12] = (ao[1] + ao[2] + ao[3])/3.0;\n\tao[13] = (ao[0] + ao[2] + ao[3])/3.0;\n\n\tao[14] = (ao[0] + ao[1] + ao[2] + ao[3])*0.25;\n}\n\n//=============================================================================\n//                          T E T 1 5 G 1 5 R I 4\n//=============================================================================\n\nFETet15G15RI4::FETet15G15RI4()\n{\n\tm_pTRI = new FETet15G4;\n}\n\n\n//=============================================================================\n//                           T E T 2 0\n//=============================================================================\n\n//=============================================================================\n//                          T E T 2 0 G 1 5\n//=============================================================================\nFETet20G15::FETet20G15() : FETet20_(NINT, FE_TET20G15)\n{\n\tgr[0] = 0.25; gs[0] = 0.25; gt[0] = 0.25; gw[0] = 0.030283678097089;\n\n\tgr[1] = 0.333333333333333; gs[1] = 0.333333333333333; gt[1] = 0.333333333333333; gw[1] = 0.006026785714286;\n\tgr[2] = 0.000000000000000; gs[2] = 0.333333333333333; gt[2] = 0.333333333333333; gw[2] = 0.006026785714286;\n\tgr[3] = 0.333333333333333; gs[3] = 0.000000000000000; gt[3] = 0.333333333333333; gw[3] = 0.006026785714286;\n\tgr[4] = 0.333333333333333; gs[4] = 0.333333333333333; gt[4] = 0.000000000000000; gw[4] = 0.006026785714286;\n\n\tgr[5] = 0.090909090909091; gs[5] = 0.090909090909091; gt[5] = 0.090909090909091; gw[5] = 0.011645249086029;\n\tgr[6] = 0.727272727272727; gs[6] = 0.090909090909091; gt[6] = 0.090909090909091; gw[6] = 0.011645249086029;\n\tgr[7] = 0.090909090909091; gs[7] = 0.727272727272727; gt[7] = 0.090909090909091; gw[7] = 0.011645249086029;\n\tgr[8] = 0.090909090909091; gs[8] = 0.090909090909091; gt[8] = 0.727272727272727; gw[8] = 0.011645249086029;\n\n\tgr[9] = 0.433449846426336; gs[9] = 0.066550153573664; gt[9] = 0.066550153573664; gw[9] = 0.010949141561386;\n\tgr[10] = 0.066550153573664; gs[10] = 0.433449846426336; gt[10] = 0.066550153573664; gw[10] = 0.010949141561386;\n\tgr[11] = 0.066550153573664; gs[11] = 0.066550153573664; gt[11] = 0.433449846426336; gw[11] = 0.010949141561386;\n\tgr[12] = 0.066550153573664; gs[12] = 0.433449846426336; gt[12] = 0.433449846426336; gw[12] = 0.010949141561386;\n\tgr[13] = 0.433449846426336; gs[13] = 0.066550153573664; gt[13] = 0.433449846426336; gw[13] = 0.010949141561386;\n\tgr[14] = 0.433449846426336; gs[14] = 0.433449846426336; gt[14] = 0.066550153573664; gw[14] = 0.010949141561386;\n\n\tinit();\n\n\t// setup the shape function matrix\n\tN.resize(15, 4);\n\tfor (int i = 0; i<15; ++i)\n\t{\n\t\tN[i][0] = 1.0 - gr[i] - gs[i] - gt[i];\n\t\tN[i][1] = gr[i];\n\t\tN[i][2] = gs[i];\n\t\tN[i][3] = gt[i];\n\t}\n\n\tmatrix A(4, 4);\n\tA = N.transpose()*N;\n\n\t// calculate inverse matrix\n\tAi.resize(4, 4);\n\tAi = A.inverse();\n}\n\nvoid FETet20G15::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(4);\n\tfor (int i = 0; i<4; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j = 0; j<NINT; ++j) b[i] += N[j][i] * ai[j];\n\t}\n\n\tfor (int i = 0; i<4; ++i)\n\t{\n\t\tao[i] = 0.0;\n\t\tfor (int j = 0; j<4; ++j) ao[i] += Ai[i][j] * b[j];\n\t}\n\n\tconst double w1 = 1.0 / 3.0;\n\tconst double w2 = 2.0 / 3.0;\n\n\tao[ 4] = ao[0] * w2 + ao[1] * w1;\n\tao[ 5] = ao[0] * w1 + ao[1] * w2;\n\tao[ 6] = ao[1] * w2 + ao[2] * w1;\n\tao[ 7] = ao[1] * w1 + ao[2] * w2;\n\tao[ 8] = ao[0] * w2 + ao[2] * w1;\n\tao[ 9] = ao[0] * w1 + ao[2] * w2;\n\tao[10] = ao[0] * w2 + ao[3] * w1;\n\tao[11] = ao[0] * w1 + ao[3] * w2;\n\tao[12] = ao[1] * w2 + ao[3] * w1;\n\tao[13] = ao[1] * w1 + ao[3] * w2;\n\tao[14] = ao[2] * w2 + ao[3] * w1;\n\tao[15] = ao[2] * w1 + ao[3] * w2;\n\n\tao[16] = (ao[0] + ao[1] + ao[3]) / 3.0;\n\tao[17] = (ao[1] + ao[2] + ao[3]) / 3.0;\n\tao[18] = (ao[2] + ao[0] + ao[3]) / 3.0;\n\tao[19] = (ao[0] + ao[1] + ao[2]) / 3.0;\n}\n\n//=============================================================================\n//              H E X 2 0\n//=============================================================================\n\nint FEHex20_::Nodes(int order)\n{\n\tswitch (order)\n\t{\n\tcase 2: return 20; break;\n\tcase 1: return 8; break;\n\tcase 0: return 1; break;\n\tdefault:\n\t\tassert(false);\n\t\treturn 20;\n\t}\n}\n\nvoid FEHex20_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_HEX8));\n\tm_shapeP[2] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_HEX20));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n//=============================================================================\n//              H E X 2 0 G 8\n//=============================================================================\n\nint FEHex20G8::ni[NELN] = {};\n\nFEHex20G8::FEHex20G8() : FEHex20_(NINT, FE_HEX20G8)\n{\n    // integration point coordinates\n    const double a = 1.0 / sqrt(3.0);\n    gr[0] = -a; gs[0] = -a; gt[0] = -a; gw[0] = 1;\n    gr[1] =  a; gs[1] = -a; gt[1] = -a; gw[1] = 1;\n    gr[2] =  a; gs[2] =  a; gt[2] = -a; gw[2] = 1;\n    gr[3] = -a; gs[3] =  a; gt[3] = -a; gw[3] = 1;\n    gr[4] = -a; gs[4] = -a; gt[4] =  a; gw[4] = 1;\n    gr[5] =  a; gs[5] = -a; gt[5] =  a; gw[5] = 1;\n    gr[6] =  a; gs[6] =  a; gt[6] =  a; gw[6] = 1;\n    gr[7] = -a; gs[7] =  a; gt[7] =  a; gw[7] = 1;\n    \n    init();\n    \n    MT.resize(NELN, NINT);\n    for (int i=0; i<NINT; ++i)\n        for (int n=0; n<NELN; ++n)\n            MT(n,i) = m_H(i,n);\n    \n    Hi.resize(NELN, NELN);\n    Hi = MT*MT.transpose();\n    Hi = Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! Use least-squares extrapolation\nvoid FEHex20G8::project_to_nodes(double* ai, double* ao) const\n{\n    double v[NELN];\n    for (int n=0; n<NELN; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<NELN; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<NELN; ++k)\n        {\n            ao[j] += Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//              H E X 2 0 G 2 7\n//=============================================================================\n\nint FEHex20G27::ni[NELN] = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 15, 17, 18, 19, 20, 21, 23, 24, 25, 26 };\n\nFEHex20G27::FEHex20G27() : FEHex20_(NINT, FE_HEX20G27)\n{\n\t// integration point coordinates\n\tconst double a = 0.774596669241483;\n\tconst double w1 = 5.0 / 9.0;\n\tconst double w2 = 8.0 / 9.0;\n\tgr[ 0] = -a; gs[ 0] = -a; gt[ 0] = -a; gw[ 0] = w1*w1*w1;\n\tgr[ 1] =  0; gs[ 1] = -a; gt[ 1] = -a; gw[ 1] = w2*w1*w1;\n\tgr[ 2] =  a; gs[ 2] = -a; gt[ 2] = -a; gw[ 2] = w1*w1*w1;\n\tgr[ 3] = -a; gs[ 3] =  0; gt[ 3] = -a; gw[ 3] = w1*w2*w1;\n\tgr[ 4] =  0; gs[ 4] =  0; gt[ 4] = -a; gw[ 4] = w2*w2*w1;\n\tgr[ 5] =  a; gs[ 5] =  0; gt[ 5] = -a; gw[ 5] = w1*w2*w1;\n\tgr[ 6] = -a; gs[ 6] =  a; gt[ 6] = -a; gw[ 6] = w1*w1*w1;\n\tgr[ 7] =  0; gs[ 7] =  a; gt[ 7] = -a; gw[ 7] = w2*w1*w1;\n\tgr[ 8] =  a; gs[ 8] =  a; gt[ 8] = -a; gw[ 8] = w1*w1*w1;\n\tgr[ 9] = -a; gs[ 9] = -a; gt[ 9] =  0; gw[ 9] = w1*w1*w2;\n\tgr[10] =  0; gs[10] = -a; gt[10] =  0; gw[10] = w2*w1*w2;\n\tgr[11] =  a; gs[11] = -a; gt[11] =  0; gw[11] = w1*w1*w2;\n\tgr[12] = -a; gs[12] =  0; gt[12] =  0; gw[12] = w1*w2*w2;\n\tgr[13] =  0; gs[13] =  0; gt[13] =  0; gw[13] = w2*w2*w2;\n\tgr[14] =  a; gs[14] =  0; gt[14] =  0; gw[14] = w1*w2*w2;\n\tgr[15] = -a; gs[15] =  a; gt[15] =  0; gw[15] = w1*w1*w2;\n\tgr[16] =  0; gs[16] =  a; gt[16] =  0; gw[16] = w2*w1*w2;\n\tgr[17] =  a; gs[17] =  a; gt[17] =  0; gw[17] = w1*w1*w2;\n\tgr[18] = -a; gs[18] = -a; gt[18] =  a; gw[18] = w1*w1*w1;\n\tgr[19] =  0; gs[19] = -a; gt[19] =  a; gw[19] = w2*w1*w1;\n\tgr[20] =  a; gs[20] = -a; gt[20] =  a; gw[20] = w1*w1*w1;\n\tgr[21] = -a; gs[21] =  0; gt[21] =  a; gw[21] = w1*w2*w1;\n\tgr[22] =  0; gs[22] =  0; gt[22] =  a; gw[22] = w2*w2*w1;\n\tgr[23] =  a; gs[23] =  0; gt[23] =  a; gw[23] = w1*w2*w1;\n\tgr[24] = -a; gs[24] =  a; gt[24] =  a; gw[24] = w1*w1*w1;\n\tgr[25] =  0; gs[25] =  a; gt[25] =  a; gw[25] = w2*w1*w1;\n\tgr[26] =  a; gs[26] =  a; gt[26] =  a; gw[26] = w1*w1*w1;\n\n\tinit();\n    \n    Hi.resize(NELN, NELN);\n    for (int i=0; i<NELN; ++i)\n        for (int n=0; n<NELN; ++n)\n            Hi(i,n) = m_H(ni[i],n);\n    Hi = Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement this\nvoid FEHex20G27::project_to_nodes(double* ai, double* ao) const\n{\n    for (int j=0; j<NELN; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<NELN; ++k)\n        {\n            ao[j] += Hi[j][k]*ai[ni[k]];\n        }\n    }\n}\n\n//=============================================================================\n//              H E X 2 7\n//=============================================================================\n\nvoid FEHex27_::init()\n{\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_HEX8));\n\tm_shapeP[2] = dynamic_cast<FESolidElementShape*>(FEElementLibrary::GetElementShapeClass(ET_HEX27));\n\n\t// initialize base class\n\tFESolidElementTraits::init();\n}\n\n//=============================================================================\n//              H E X 2 7 G 2 7\n//=============================================================================\n\nFEHex27G27::FEHex27G27() : FEHex27_(NINT, FE_HEX27G27)\n{\n\t// integration point coordinates\n\tconst double a = 0.774596669241483;\n\tconst double w1 = 5.0 / 9.0;\n\tconst double w2 = 8.0 / 9.0;\n\tgr[ 0] = -a; gs[ 0] = -a; gt[ 0] = -a; gw[ 0] = w1*w1*w1;\n\tgr[ 1] =  0; gs[ 1] = -a; gt[ 1] = -a; gw[ 1] = w2*w1*w1;\n\tgr[ 2] =  a; gs[ 2] = -a; gt[ 2] = -a; gw[ 2] = w1*w1*w1;\n\tgr[ 3] = -a; gs[ 3] =  0; gt[ 3] = -a; gw[ 3] = w1*w2*w1;\n\tgr[ 4] =  0; gs[ 4] =  0; gt[ 4] = -a; gw[ 4] = w2*w2*w1;\n\tgr[ 5] =  a; gs[ 5] =  0; gt[ 5] = -a; gw[ 5] = w1*w2*w1;\n\tgr[ 6] = -a; gs[ 6] =  a; gt[ 6] = -a; gw[ 6] = w1*w1*w1;\n\tgr[ 7] =  0; gs[ 7] =  a; gt[ 7] = -a; gw[ 7] = w2*w1*w1;\n\tgr[ 8] =  a; gs[ 8] =  a; gt[ 8] = -a; gw[ 8] = w1*w1*w1;\n\tgr[ 9] = -a; gs[ 9] = -a; gt[ 9] =  0; gw[ 9] = w1*w1*w2;\n\tgr[10] =  0; gs[10] = -a; gt[10] =  0; gw[10] = w2*w1*w2;\n\tgr[11] =  a; gs[11] = -a; gt[11] =  0; gw[11] = w1*w1*w2;\n\tgr[12] = -a; gs[12] =  0; gt[12] =  0; gw[12] = w1*w2*w2;\n\tgr[13] =  0; gs[13] =  0; gt[13] =  0; gw[13] = w2*w2*w2;\n\tgr[14] =  a; gs[14] =  0; gt[14] =  0; gw[14] = w1*w2*w2;\n\tgr[15] = -a; gs[15] =  a; gt[15] =  0; gw[15] = w1*w1*w2;\n\tgr[16] =  0; gs[16] =  a; gt[16] =  0; gw[16] = w2*w1*w2;\n\tgr[17] =  a; gs[17] =  a; gt[17] =  0; gw[17] = w1*w1*w2;\n\tgr[18] = -a; gs[18] = -a; gt[18] =  a; gw[18] = w1*w1*w1;\n\tgr[19] =  0; gs[19] = -a; gt[19] =  a; gw[19] = w2*w1*w1;\n\tgr[20] =  a; gs[20] = -a; gt[20] =  a; gw[20] = w1*w1*w1;\n\tgr[21] = -a; gs[21] =  0; gt[21] =  a; gw[21] = w1*w2*w1;\n\tgr[22] =  0; gs[22] =  0; gt[22] =  a; gw[22] = w2*w2*w1;\n\tgr[23] =  a; gs[23] =  0; gt[23] =  a; gw[23] = w1*w2*w1;\n\tgr[24] = -a; gs[24] =  a; gt[24] =  a; gw[24] = w1*w1*w1;\n\tgr[25] =  0; gs[25] =  a; gt[25] =  a; gw[25] = w2*w1*w1;\n\tgr[26] =  a; gs[26] =  a; gt[26] =  a; gw[26] = w1*w1*w1;\n\n\tinit();\n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement this\nvoid FEHex27G27::project_to_nodes(double* ai, double* ao) const\n{\n    for (int j=0; j<NELN; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<NINT; ++k)\n        {\n            ao[j] += m_Hi[j][k]*ai[k];\n        }\n    }\n}\n\n//=============================================================================\n//              P Y R A 5\n//=============================================================================\n\n//=============================================================================\n//              P Y R A 5 G 8\n//=============================================================================\n\nFEPyra5G8::FEPyra5G8() : FEPyra5_(NINT, FE_PYRA5G8)\n{\n\t// integration point coordinates\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gs[0] = -a; gt[0] = -a; gw[0] = 1;\n\tgr[1] = a; gs[1] = -a; gt[1] = -a; gw[1] = 1;\n\tgr[2] = a; gs[2] = a; gt[2] = -a; gw[2] = 1;\n\tgr[3] = -a; gs[3] = a; gt[3] = -a; gw[3] = 1;\n\tgr[4] = -a; gs[4] = -a; gt[4] = a; gw[4] = 1;\n\tgr[5] = a; gs[5] = -a; gt[5] = a; gw[5] = 1;\n\tgr[6] = a; gs[6] = a; gt[6] = a; gw[6] = 1;\n\tgr[7] = -a; gs[7] = a; gt[7] = a; gw[7] = 1;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN, NELN);\n\tm_Ai.resize(NELN, NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\nvoid FEPyra5G8::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i = 0; i<NELN; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j = 0; j<NINT; ++j) b[i] += m_H[j][i] * ai[j];\n\t}\n\n\tfor (int i = 0; i<NELN; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j = 0; j<NELN; ++j) ao[i] += m_Ai[i][j] * b[j];\n\t}\n}\n\n\n//=============================================================================\n//              P Y R A 1 3\n//=============================================================================\n\n//=============================================================================\n//              P Y R A 1 3 G 8\n//=============================================================================\n\nFEPyra13G8::FEPyra13G8() : FEPyra13_(NINT, FE_PYRA13G8)\n{\n    // integration point coordinates\n    const double a = 1.0 / sqrt(3.0);\n    gr[0] = -a; gs[0] = -a; gt[0] = -a; gw[0] = 1;\n    gr[1] = a; gs[1] = -a; gt[1] = -a; gw[1] = 1;\n    gr[2] = a; gs[2] = a; gt[2] = -a; gw[2] = 1;\n    gr[3] = -a; gs[3] = a; gt[3] = -a; gw[3] = 1;\n    gr[4] = -a; gs[4] = -a; gt[4] = a; gw[4] = 1;\n    gr[5] = a; gs[5] = -a; gt[5] = a; gw[5] = 1;\n    gr[6] = a; gs[6] = a; gt[6] = a; gw[6] = 1;\n    gr[7] = -a; gs[7] = a; gt[7] = a; gw[7] = 1;\n    init();\n    \n    // we need Ai to project integration point data to the nodes\n    matrix A(NELN, NELN);\n    m_Ai.resize(NELN, NELN);\n    A = m_H.transpose()*m_H;\n    m_Ai = A.inverse();\n}\n\nvoid FEPyra13G8::project_to_nodes(double* ai, double* ao) const\n{\n    vector<double> b(NELN);\n    for (int i = 0; i<NELN; ++i)\n    {\n        b[i] = 0;\n        for (int j = 0; j<NINT; ++j) b[i] += m_H[j][i] * ai[j];\n    }\n    \n    for (int i = 0; i<NELN; ++i)\n    {\n        ao[i] = 0;\n        for (int j = 0; j<NELN; ++j) ao[i] += m_Ai[i][j] * b[j];\n    }\n}\n\n\n//=============================================================================\n//\n//                  S U R F A C E   E L E M E N T S\n//\n//=============================================================================\n\nFESurfaceElementTraits::FESurfaceElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et) : FEElementTraits(ni, ne, FE_ELEM_SURFACE, es, et)\n{\n\tgr.resize(ni);\n\tgs.resize(ni);\n\tgw.resize(ni);\n\n\tGr.resize(ni, ne);\n\tGs.resize(ni, ne);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize the surface element traits data variables.\n//\nvoid FESurfaceElementTraits::init()\n{\n\tassert(m_nint > 0);\n\tassert(m_neln > 0);\n\n\t// get shape class\n\tm_shape = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(m_spec.eshape));\n\tassert(m_shape && (m_shape->shape() == m_spec.eshape));\n\n\t// evaluate shape functions\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble N[NE];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tshape_fnc(N, gr[n], gs[n]);\n\t\tfor (int i=0; i<m_neln; ++i) m_H[n][i] = N[i];\n\t}\n\t\n\t// evaluate shape function derivatives\n\tdouble Nr[NE], Ns[NE];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tshape_deriv(Nr, Ns, gr[n], gs[n]);\n\t\tfor (int i=0; i<m_neln; ++i)\n\t\t{\n\t\t\tGr[n][i] = Nr[i];\n\t\t\tGs[n][i] = Ns[i];\n\t\t}\n\t}\n\n\t// NOTE: Below, is a new interface for dealing with mixed element formulations.\n\t//       This is still a work in progress.\n\n\t// Get the max interpolation order\n\tconst int maxOrder = (int)m_shapeP.size() - 1;\n\tm_Hp.resize(maxOrder + 1);\n\tGr_p.resize(maxOrder + 1);\n\tGs_p.resize(maxOrder + 1);\n\tfor (int i = 0; i <= maxOrder; ++i)\n\t{\n\t\tFESurfaceElementShape* shape = m_shapeP[i];\n\t\tmatrix& H = m_Hp[i];\n\t\tmatrix& Gr = Gr_p[i];\n\t\tmatrix& Gs = Gs_p[i];\n\t\tif (i == 0)\n\t\t{\n\t\t\tH.resize(m_nint, 1);\n\t\t\tGr.resize(m_nint, 1);\n\t\t\tGs.resize(m_nint, 1);\n\t\t\tfor (int n = 0; n < m_nint; ++n)\n\t\t\t{\n\t\t\t\tH[n][0] = 1.0;\n\t\t\t\tGr[n][0] = Gs[n][0] = 0.0;\n\t\t\t}\n\t\t}\n\t\telse if (m_shapeP[i])\n\t\t{\n\t\t\t// get the nodes\n\t\t\tint neln = shape->nodes();\n\n\t\t\t// shape function values\n\t\t\tH.resize(m_nint, neln);\n\t\t\tfor (int n = 0; n<m_nint; ++n)\n\t\t\t{\n\t\t\t\tm_shapeP[i]->shape_fnc(N, gr[n], gs[n]);\n\t\t\t\tfor (int j = 0; j<neln; ++j) H[n][j] = N[j];\n\t\t\t}\n\n\t\t\t// calculate local derivatives of shape functions at gauss points\n\t\t\tGr.resize(m_nint, neln);\n\t\t\tGs.resize(m_nint, neln);\n\t\t\tfor (int n = 0; n<m_nint; ++n)\n\t\t\t{\n\t\t\t\tshape->shape_deriv(Nr, Ns, gr[n], gs[n]);\n\t\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\tGr[n][j] = Nr[j];\n\t\t\t\t\tGs[n][j] = Ns[j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// shape functions at (r,s)\nvoid FESurfaceElementTraits::shape_fnc(double* H, double r, double s)\n{\n\tm_shape->shape_fnc(H, r, s);\n}\n\n// shape function derivatives at (r,s)\nvoid FESurfaceElementTraits::shape_deriv(double* Gr, double* Gs, double r, double s)\n{\n\tm_shape->shape_deriv(Gr, Gs, r, s);\n}\n\n// shape function derivatives at (r,s)\nvoid FESurfaceElementTraits::shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s)\n{\n\tm_shape->shape_deriv2(Grr, Gss, Grs, r, s);\n}\n\n// shape functions at (r,s)\nvoid FESurfaceElementTraits::shape_fnc(int order, double* H, double r, double s)\n{\n\tif (order == -1) m_shape->shape_fnc(H, r, s);\n\telse m_shapeP[order]->shape_fnc(H, r, s);\n}\n\n// shape function derivatives at (r,s)\nvoid FESurfaceElementTraits::shape_deriv(int order, double* Gr, double* Gs, double r, double s)\n{\n\tif (order == -1) shape_deriv(Gr, Gs, r, s);\n\telse m_shapeP[order]->shape_deriv(Gr, Gs, r, s);\n}\n\n//=============================================================================\n//                          F E Q U A D 4\n//=============================================================================\n\nvoid FEQuad4_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(2);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_QUAD4));\n\n    // centroid coordinates\n    cr = cs = 0;\n\n    // initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//                          F E Q U A D G 4 \n//=============================================================================\n\nFEQuad4G4::FEQuad4G4() : FEQuad4_(NINT, FE_QUAD4G4) \n{\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gs[0] = -a; gw[0] = 1;\n\tgr[1] =  a; gs[1] = -a; gw[1] = 1;\n\tgr[2] =  a; gs[2] =  a; gw[2] = 1;\n\tgr[3] = -a; gs[3] =  a; gw[3] = 1;\n\tinit(); \n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad4G4::project_to_nodes(double* ai, double* ao) const\n{\n\tint ni = NINT;\n\tint ne = NELN;\n\tassert(ni == ne);\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<ni; ++j) ao[i] += m_Hi[i][j]*ai[j];\n\t}\n}\n\n//=============================================================================\n//                          F E Q U A D G 16\n//=============================================================================\n\nFEQuad4G16::FEQuad4G16() : FEQuad4_(NINT, FE_QUAD4G16)\n{\n\tconst double a = 0.339981;\n\tconst double b = 0.861136;\n\n\tconst double wa = 0.652145;\n\tconst double wb = 0.347855;\n\n\tgr[ 0] = -b; gs[ 0] = -b; gw[ 0] = wb * wb;\n\tgr[ 1] = -a; gs[ 1] = -b; gw[ 1] = wa * wb;\n\tgr[ 2] =  a; gs[ 2] = -b; gw[ 2] = wa * wb;\n\tgr[ 3] =  b; gs[ 3] = -b; gw[ 3] = wb * wb;\n\n\tgr[ 4] = -b; gs[ 4] = -a; gw[ 4] = wa * wb;\n\tgr[ 5] = -a; gs[ 5] = -a; gw[ 5] = wa * wa;\n\tgr[ 6] =  a; gs[ 6] = -a; gw[ 6] = wa * wa;\n\tgr[ 7] =  b; gs[ 7] = -a; gw[ 7] = wa * wb;\n\n\tgr[ 8] = -b; gs[ 8] =  a; gw[ 8] = wa * wb;\n\tgr[ 9] = -a; gs[ 9] =  a; gw[ 9] = wa * wa;\n\tgr[10] =  a; gs[10] =  a; gw[10] = wa * wa;\n\tgr[11] =  b; gs[11] =  a; gw[11] = wa * wb;\n\n\tgr[12] = -b; gs[12] =  b; gw[12] = wb * wb;\n\tgr[13] = -a; gs[13] =  b; gw[13] = wa * wb;\n\tgr[14] =  a; gs[14] =  b; gw[14] = wa * wb;\n\tgr[15] =  b; gs[15] =  b; gw[15] = wb * wb;\n\n\tinit();\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN, NELN);\n\tm_Ai.resize(NELN, NELN);\n\tA = m_H.transpose() * m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad4G16::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i = 0; i < NELN; ++i)\n\t{\n\t\tb[i] = 0;\n\t\tfor (int j = 0; j < NINT; ++j) b[i] += m_H[j][i] * ai[j];\n\t}\n\n\tfor (int i = 0; i < NELN; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j = 0; j < NELN; ++j) ao[i] += m_Ai[i][j] * b[j];\n\t}\n}\n\n//=============================================================================\n//                          F E Q U A D N I\n//=============================================================================\n\nFEQuad4NI::FEQuad4NI() : FEQuad4_(NINT, FE_QUAD4NI) \n{\n\tgr[0] = -1; gs[0] = -1; gw[0] = 1;\n\tgr[1] =  1; gs[1] = -1; gw[1] = 1;\n\tgr[2] =  1; gs[2] =  1; gw[2] = 1;\n\tgr[3] = -1; gs[3] =  1; gw[3] = 1;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad4NI::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[1];\n\tao[2] = ai[2];\n\tao[3] = ai[3];\n}\n\n//=============================================================================\n//                          F E T R I \n//=============================================================================\n\nvoid FETri3_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(2);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI3));\n\n    // centroid coordinates\n    cr = cs = 1.0/3.0;\n    \n\t// initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//                          F E T R I G 1 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFETri3G1::FETri3G1() : FETri3_(NINT, FE_TRI3G1)\n{\n\tconst double a = 1.0/3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = 0.5;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri3G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n\tao[2] = ai[0];\n}\n\n//=============================================================================\n//                          F E T R I G 3 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFETri3G3::FETri3G3() : FETri3_(NINT, FE_TRI3G3)\n{\n\tconst double a = 1.0 / 6.0;\n\tconst double b = 2.0 / 3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = a;\n\tgr[1] = b; gs[1] = a; gw[1] = a;\n\tgr[2] = a; gs[2] = b; gw[2] = a;\n\tinit(); \n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri3G3::project_to_nodes(double* ai, double* ao) const\n{\n\tassert(NINT == NELN);\n\tfor (int i=0; i<NELN; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) ao[i] += m_Hi[i][j]*ai[j];\n\t}\n}\n\n//=============================================================================\n//                          F E T R I G 7\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFETri3G7::FETri3G7() : FETri3_(NINT, FE_TRI3G7) \n{ \n\tconst double w = 1.0/2.0;\n\tgr[0] = 0.333333333333333; gs[0] = 0.333333333333333; gw[0] = w*0.225000000000000;\n\tgr[1] = 0.797426985353087; gs[1] = 0.101286507323456; gw[1] = w*0.125939180544827;\n\tgr[2] = 0.101286507323456; gs[2] = 0.797426985353087; gw[2] = w*0.125939180544827;\n\tgr[3] = 0.101286507323456; gs[3] = 0.101286507323456; gw[3] = w*0.125939180544827;\n\tgr[4] = 0.470142064105115; gs[4] = 0.470142064105115; gw[4] = w*0.132394152788506;\n\tgr[5] = 0.470142064105115; gs[5] = 0.059715871789770; gw[5] = w*0.132394152788506;\n\tgr[6] = 0.059715871789770; gs[6] = 0.470142064105115; gw[6] = w*0.132394152788506;\n\tinit(); \n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri3G7::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n//=============================================================================\n//                          F E T R I N I\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFETri3NI::FETri3NI() : FETri3_(NINT, FE_TRI3NI)\n{ \n\tconst double a = 1.0 / 6.0;\n\tgr[0] = 0; gs[0] = 0; gw[0] = a;\n\tgr[1] = 1; gs[1] = 0; gw[1] = a;\n\tgr[2] = 0; gs[2] = 1; gw[2] = a;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri3NI::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[1];\n\tao[2] = ai[2];\n}\n\n//============================================================================\n//                             F E T R I 6\n//============================================================================\n\nvoid FETri6_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI3));\n\tm_shapeP[2] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI6));\n\n    // centroid coordinates\n    cr = cs = 1.0/3.0;\n    \n\t// initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//                          F E T R I 6 G 3\n//=============================================================================\n\nFETri6G3::FETri6G3() : FETri6_(NINT, FE_TRI6G3) \n{ \n\tconst double a = 1.0 / 6.0;\n\tconst double b = 2.0 / 3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = a;\n\tgr[1] = b; gs[1] = a; gw[1] = a;\n\tgr[2] = a; gs[2] = b; gw[2] = a;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6G3::project_to_nodes(double* ai, double* ao) const\n{\n\tmatrix H(3, 3);\n\tfor (int n=0; n<3; ++n)\n\t{\n\t\tH[n][0] = 1.0 - gr[n] - gs[n];\n\t\tH[n][1] = gr[n];\n\t\tH[n][2] = gs[n];\n\t}\n\tH.inverse();\n\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<3; ++j) ao[i] += H[i][j]*ai[j];\n\t}\n\n\tao[3] = 0.5*(ao[0] + ao[1]);\n\tao[4] = 0.5*(ao[1] + ao[2]);\n\tao[5] = 0.5*(ao[2] + ao[0]);\n}\n\n//=============================================================================\n//                          F E T R I 6 G 4\n//=============================================================================\n\nFETri6G4::FETri6G4() : FETri6_(NINT, FE_TRI6G4) \n{ \n\tconst double a = 1.0/3.0;\n\tconst double b = 1.0/5.0;\n\tconst double c = 3.0/5.0;\n\tgr[0] = a; gs[0] = a; gw[0] = -27.0/96.0;\n\tgr[1] = c; gs[1] = b; gw[1] =  25.0/96.0;\n\tgr[2] = b; gs[2] = b; gw[2] =  25.0/96.0;\n\tgr[3] = b; gs[3] = c; gw[3] =  25.0/96.0;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement this\nvoid FETri6G4::project_to_nodes(double* ai, double* ao) const\n{\n\t\n}\n\n//=============================================================================\n//                          F E T R I 6 G 7\n//=============================================================================\n\nFETri6G7::FETri6G7() : FETri6_(NINT, FE_TRI6G7) \n{ \n\tconst double w = 1.0/2.0;\n\tgr[0] = 0.333333333333333; gs[0] = 0.333333333333333; gw[0] = w*0.225000000000000;\n\tgr[1] = 0.797426985353087; gs[1] = 0.101286507323456; gw[1] = w*0.125939180544827;\n\tgr[2] = 0.101286507323456; gs[2] = 0.797426985353087; gw[2] = w*0.125939180544827;\n\tgr[3] = 0.101286507323456; gs[3] = 0.101286507323456; gw[3] = w*0.125939180544827;\n\tgr[4] = 0.470142064105115; gs[4] = 0.470142064105115; gw[4] = w*0.132394152788506;\n\tgr[5] = 0.470142064105115; gs[5] = 0.059715871789770; gw[5] = w*0.132394152788506;\n\tgr[6] = 0.059715871789770; gs[6] = 0.470142064105115; gw[6] = w*0.132394152788506;\n\tinit(); \n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6G7::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n//=============================================================================\n//                          T R I 6 G L 7\n//=============================================================================\n\nFETri6GL7::FETri6GL7() : FETri6_(NINT, FE_TRI6GL7) \n{ \n\tconst double a = 1.0/40.0;\n\tconst double b = 1.0/15.0;\n\tgr[0] = 0.0; gs[0] = 0.0; gw[0] = a;\n\tgr[1] = 1.0; gs[1] = 0.0; gw[1] = a;\n\tgr[2] = 0.0; gs[2] = 1.0; gw[2] = a;\n\tgr[3] = 0.5; gs[3] = 0.0; gw[3] = b;\n\tgr[4] = 0.5; gs[4] = 0.5; gw[4] = b;\n\tgr[5] = 0.0; gs[5] = 0.5; gw[5] = b;\n\tgr[6] = 1.0/3.0; gs[6] = 1.0/3.0; gw[6] = 9.0*a;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6GL7::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0]; ao[1] = ai[1]; ao[2] = ai[2];\n\tao[3] = ai[3]; ao[4] = ai[4]; ao[5] = ai[5];\n}\n\n//=============================================================================\n//                          F E T R I 6 N I\n//=============================================================================\n\nFETri6NI::FETri6NI() : FETri6_(NINT, FE_TRI6NI)\n{ \n\tconst double a = 0.0;\n\tconst double b = 1.0/6.0;\n\tgr[0] = 0.0; gs[0] = 0.0; gw[0] = a;\n\tgr[1] = 1.0; gs[1] = 0.0; gw[1] = a;\n\tgr[2] = 0.0; gs[2] = 1.0; gw[2] = a;\n\tgr[3] = 0.5; gs[3] = 0.0; gw[3] = b;\n\tgr[4] = 0.5; gs[4] = 0.5; gw[4] = b;\n\tgr[5] = 0.0; gs[5] = 0.5; gw[5] = b;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6NI::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[1];\n\tao[2] = ai[2];\n\tao[3] = ai[3];\n\tao[4] = ai[4];\n\tao[5] = ai[5];\n}\n\n//============================================================================\n//                             F E T R I 6 M\n//============================================================================\n/*\n// parameter used in the tri6m (6-node triangle with modified shape functions)\nconst double fetri6m_alpha = 0.2;\n\n//-----------------------------------------------------------------------------\nvoid FETri6m_::shape(double* H, double r, double s)\n{\n\tdouble r1 = 1.0 - r - s;\n\tdouble r2 = r;\n\tdouble r3 = s;\n\n\tdouble N[6];\n\tN[0] = r1*(2.0*r1 - 1.0);\n\tN[1] = r2*(2.0*r2 - 1.0);\n\tN[2] = r3*(2.0*r3 - 1.0);\n\tN[3] = 4.0*r1*r2;\n\tN[4] = 4.0*r2*r3;\n\tN[5] = 4.0*r3*r1;\n\n\tconst double a = fetri6m_alpha;\n\tconst double b = 1.0 - 2.0*a;\n\tH[0] = N[0] + a*(N[3] + N[5]);\n\tH[1] = N[1] + a*(N[3] + N[4]);\n\tH[2] = N[2] + a*(N[4] + N[5]);\n\tH[3] = b*N[3];\n\tH[4] = b*N[4];\n\tH[5] = b*N[5];\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6m_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tdouble Nr[6], Ns[6];\n\tNr[0] = -3.0 + 4.0*r + 4.0*s;\n\tNr[1] =  4.0*r - 1.0;\n\tNr[2] =  0.0;\n\tNr[3] =  4.0 - 8.0*r - 4.0*s;\n\tNr[4] =  4.0*s;\n\tNr[5] = -4.0*s;\n\n\tNs[0] = -3.0 + 4.0*s + 4.0*r;\n\tNs[1] =  0.0;\n\tNs[2] =  4.0*s - 1.0;\n\tNs[3] = -4.0*r;\n\tNs[4] =  4.0*r;\n\tNs[5] =  4.0 - 8.0*s - 4.0*r;\n\n\tconst double a = fetri6m_alpha;\n\tconst double b = 1.0 - 2.0*a;\n\tHr[0] = Nr[0] + a*(Nr[3] + Nr[5]);\n\tHr[1] = Nr[1] + a*(Nr[3] + Nr[4]);\n\tHr[2] = Nr[2] + a*(Nr[4] + Nr[5]);\n\tHr[3] = b*Nr[3];\n\tHr[4] = b*Nr[4];\n\tHr[5] = b*Nr[5];\n\n\tHs[0] = Ns[0] + a*(Ns[3] + Ns[5]);\n\tHs[1] = Ns[1] + a*(Ns[3] + Ns[4]);\n\tHs[2] = Ns[2] + a*(Ns[4] + Ns[5]);\n\tHs[3] = b*Ns[3];\n\tHs[4] = b*Ns[4];\n\tHs[5] = b*Ns[5];\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6m_::shape_deriv2(double* Hrr, double* Hrs, double* Hss, double r, double s)\n{\n\tdouble Nrr[6], Nrs[6], Nss[6];\n\tNrr[0] =  4.0; Nrs[0] =  4.0; Nss[0] =  4.0;\n\tNrr[1] =  4.0; Nrs[1] =  0.0; Nss[1] =  0.0;\n\tNrr[2] =  0.0; Nrs[2] =  0.0; Nss[2] =  4.0;\n\tNrr[3] = -8.0; Nrs[3] = -4.0; Nss[3] =  0.0;\n\tNrr[4] =  0.0; Nrs[4] =  4.0; Nss[4] =  0.0;\n\tNrr[5] =  0.0; Nrs[5] = -4.0; Nss[5] = -8.0;\n\n\tconst double a = fetri6m_alpha;\n\tconst double b = 1.0 - 2.0*a;\n\tHrr[0] = Nrr[0] + a*(Nrr[3] + Nrr[5]);\n\tHrr[1] = Nrr[1] + a*(Nrr[3] + Nrr[4]);\n\tHrr[2] = Nrr[2] + a*(Nrr[4] + Nrr[5]);\n\tHrr[3] = b*Nrr[3];\n\tHrr[4] = b*Nrr[4];\n\tHrr[5] = b*Nrr[5];\n\n\tHrs[0] = Nrs[0] + a*(Nrs[3] + Nrs[5]);\n\tHrs[1] = Nrs[1] + a*(Nrs[3] + Nrs[4]);\n\tHrs[2] = Nrs[2] + a*(Nrs[4] + Nrs[5]);\n\tHrs[3] = b*Nrs[3];\n\tHrs[4] = b*Nrs[4];\n\tHrs[5] = b*Nrs[5];\n\n\tHss[0] = Nss[0] + a*(Nss[3] + Nss[5]);\n\tHss[1] = Nss[1] + a*(Nss[3] + Nss[4]);\n\tHss[2] = Nss[2] + a*(Nss[4] + Nss[5]);\n\tHss[3] = b*Nss[3];\n\tHss[4] = b*Nss[4];\n\tHss[5] = b*Nss[5];\n}\n\n//=============================================================================\n//                          F E T R I 6 M G 7\n//=============================================================================\n\nFETri6mG7::FETri6mG7() : FETri6m_(NINT, FE_TRI6MG7) \n{ \n\tconst double w = 1.0/2.0;\n\tgr[0] = 0.333333333333333; gs[0] = 0.333333333333333; gw[0] = w*0.225000000000000;\n\tgr[1] = 0.797426985353087; gs[1] = 0.101286507323456; gw[1] = w*0.125939180544827;\n\tgr[2] = 0.101286507323456; gs[2] = 0.797426985353087; gw[2] = w*0.125939180544827;\n\tgr[3] = 0.101286507323456; gs[3] = 0.101286507323456; gw[3] = w*0.125939180544827;\n\tgr[4] = 0.470142064105115; gs[4] = 0.470142064105115; gw[4] = w*0.132394152788506;\n\tgr[5] = 0.470142064105115; gs[5] = 0.059715871789770; gw[5] = w*0.132394152788506;\n\tgr[6] = 0.059715871789770; gs[6] = 0.470142064105115; gw[6] = w*0.132394152788506;\n\tinit(); \n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6mG7::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n*/\n\n//=============================================================================\n//                          F E T R I 7 G 3\n//=============================================================================\n\nFETri7G3::FETri7G3() : FETri7_(NINT, FE_TRI7G3) \n{ \n\tconst double a = 1.0 / 6.0;\n\tconst double b = 2.0 / 3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = a;\n\tgr[1] = b; gs[1] = a; gw[1] = a;\n\tgr[2] = a; gs[2] = b; gw[2] = a;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri7G3::project_to_nodes(double* ai, double* ao) const\n{\n\tmatrix H(3, 3);\n\tfor (int n=0; n<3; ++n)\n\t{\n\t\tH[n][0] = 1.0 - gr[n] - gs[n];\n\t\tH[n][1] = gr[n];\n\t\tH[n][2] = gs[n];\n\t}\n\tH.inverse();\n\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<3; ++j) ao[i] += H[i][j]*ai[j];\n\t}\n\n\tao[3] = 0.5*(ao[0] + ao[1]);\n\tao[4] = 0.5*(ao[1] + ao[2]);\n\tao[5] = 0.5*(ao[2] + ao[0]);\n\tao[6] = (ao[0]+ao[1]+ao[2])/3.0;\n}\n\n//=============================================================================\n//                          F E T R I 6 G 4\n//=============================================================================\n\nFETri7G4::FETri7G4() : FETri7_(NINT, FE_TRI7G4) \n{ \n\tconst double a = 1.0/3.0;\n\tconst double b = 1.0/5.0;\n\tconst double c = 3.0/5.0;\n\tgr[0] = a; gs[0] = a; gw[0] = -27.0/96.0;\n\tgr[1] = c; gs[1] = b; gw[1] =  25.0/96.0;\n\tgr[2] = b; gs[2] = b; gw[2] =  25.0/96.0;\n\tgr[3] = b; gs[3] = c; gw[3] =  25.0/96.0;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement this\nvoid FETri7G4::project_to_nodes(double* ai, double* ao) const\n{\n\t\n}\n\n//============================================================================\n//                             F E T R I 7\n//============================================================================\n\nvoid FETri7_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI3));\n\tm_shapeP[2] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI7));\n\n    // centroid coordinates\n    cr = cs = 1.0/3.0;\n    \n\t// initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//                          F E T R I 7 G 7\n//=============================================================================\n\nFETri7G7::FETri7G7() : FETri7_(NINT, FE_TRI7G7) \n{ \n\tconst double w = 1.0/2.0;\n\tgr[0] = 0.333333333333333; gs[0] = 0.333333333333333; gw[0] = w*0.225000000000000;\n\tgr[1] = 0.797426985353087; gs[1] = 0.101286507323456; gw[1] = w*0.125939180544827;\n\tgr[2] = 0.101286507323456; gs[2] = 0.797426985353087; gw[2] = w*0.125939180544827;\n\tgr[3] = 0.101286507323456; gs[3] = 0.101286507323456; gw[3] = w*0.125939180544827;\n\tgr[4] = 0.470142064105115; gs[4] = 0.470142064105115; gw[4] = w*0.132394152788506;\n\tgr[5] = 0.470142064105115; gs[5] = 0.059715871789770; gw[5] = w*0.132394152788506;\n\tgr[6] = 0.059715871789770; gs[6] = 0.470142064105115; gw[6] = w*0.132394152788506;\n\tinit(); \n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri7G7::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n\n//=============================================================================\n//                          F E T R I 7 G L 7\n//=============================================================================\n\nFETri7GL7::FETri7GL7() : FETri7_(NINT, FE_TRI7GL7) \n{ \n\tconst double a = 1.0/40.0;\n\tconst double b = 1.0/15.0;\n\tgr[0] = 0.0; gs[0] = 0.0; gw[0] = a;\n\tgr[1] = 1.0; gs[1] = 0.0; gw[1] = a;\n\tgr[2] = 0.0; gs[2] = 1.0; gw[2] = a;\n\tgr[3] = 0.5; gs[3] = 0.0; gw[3] = b;\n\tgr[4] = 0.5; gs[4] = 0.5; gw[4] = b;\n\tgr[5] = 0.0; gs[5] = 0.5; gw[5] = b;\n\tgr[6] = 1.0/3.0; gs[6] = 1.0/3.0; gw[6] = 9.0*a;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FETri7GL7::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0]; ao[1] = ai[1]; ao[2] = ai[2];\n\tao[3] = ai[3]; ao[4] = ai[4]; ao[5] = ai[5];\n\tao[6] = ai[6];\n}\n\n//============================================================================\n//                             F E T R I 1 0\n//============================================================================\n\nvoid FETri10_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(4);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI3));\n\tm_shapeP[2] = 0; // this element cannot be used for quadratic interpolation!\n\tm_shapeP[3] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_TRI10));\n\n    // centroid coordinates\n    cr = cs = 1.0/3.0;\n    \n\t// initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//                          F E T R I 1 0 G 7\n//=============================================================================\n\nFETri10G7::FETri10G7() : FETri10_(NINT, FE_TRI10G7)\n{\n\tconst double w = 1.0 / 2.0;\n\tgr[0] = 0.333333333333333; gs[0] = 0.333333333333333; gw[0] = w*0.225000000000000;\n\tgr[1] = 0.797426985353087; gs[1] = 0.101286507323456; gw[1] = w*0.125939180544827;\n\tgr[2] = 0.101286507323456; gs[2] = 0.797426985353087; gw[2] = w*0.125939180544827;\n\tgr[3] = 0.101286507323456; gs[3] = 0.101286507323456; gw[3] = w*0.125939180544827;\n\tgr[4] = 0.470142064105115; gs[4] = 0.470142064105115; gw[4] = w*0.132394152788506;\n\tgr[5] = 0.470142064105115; gs[5] = 0.059715871789770; gw[5] = w*0.132394152788506;\n\tgr[6] = 0.059715871789770; gs[6] = 0.470142064105115; gw[6] = w*0.132394152788506;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN, NELN);\n\tm_Ai.resize(NELN, NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri10G7::project_to_nodes(double* ai, double* ao) const\n{\n\t// TODO: Implement this\n}\n\n\n//=============================================================================\n//                          F E T R I 1 0 G 1 2\n//=============================================================================\n\nFETri10G12::FETri10G12() : FETri10_(NINT, FE_TRI10G12)\n{\n\tgr[ 0] = 0.063089014; gs[ 0] = 0.873821971;\tgw[ 0] = 0.025422453;\n\tgr[ 1] = 0.873821971; gs[ 1] = 0.063089014;\tgw[ 1] = 0.025422453;\n\tgr[ 2] = 0.063089014; gs[ 2] = 0.063089014;\tgw[ 2] = 0.025422453;\n\tgr[ 3] = 0.249286745; gs[ 3] = 0.501426510;\tgw[ 3] = 0.058393138;\n\tgr[ 4] = 0.501426510; gs[ 4] = 0.249286745;\tgw[ 4] = 0.058393138;\n\tgr[ 5] = 0.249286745; gs[ 5] = 0.249286745;\tgw[ 5] = 0.058393138;\n\tgr[ 6] = 0.053145050; gs[ 6] = 0.636502499;\tgw[ 6] = 0.041425538;\n\tgr[ 7] = 0.636502499; gs[ 7] = 0.053145050;\tgw[ 7] = 0.041425538;\n\tgr[ 8] = 0.310352451; gs[ 8] = 0.636502499;\tgw[ 8] = 0.041425538;\n\tgr[ 9] = 0.636502499; gs[ 9] = 0.310352451;\tgw[ 9] = 0.041425538;\n\tgr[10] = 0.310352451; gs[10] = 0.053145050;\tgw[10] = 0.041425538;\n\tgr[11] = 0.053145050; gs[11] = 0.310352451;\tgw[11] = 0.041425538;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN, NELN);\n\tm_Ai.resize(NELN, NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri10G12::project_to_nodes(double* ai, double* ao) const\n{\n\t// TODO: Implement this\n}\n\n//=============================================================================\n//          F E Q U A D 8 \n//=============================================================================\n\nvoid FEQuad8_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_QUAD4));\n\tm_shapeP[2] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_QUAD8));\n\n    // centroid coordinates\n    cr = cs = 0;\n    \n\t// initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//       F E Q U A D 8 G 9\n//=============================================================================\n\nFEQuad8G9::FEQuad8G9() : FEQuad8_(NINT, FE_QUAD8G9)\n{\n\t// integration point coordinates\n\tconst double a = sqrt(0.6);\n\tconst double w1 = 25.0/81.0;\n\tconst double w2 = 40.0/81.0;\n\tconst double w3 = 64.0/81.0;\n\tgr[ 0] = -a; gs[ 0] = -a;  gw[ 0] = w1;\n\tgr[ 1] =  0; gs[ 1] = -a;  gw[ 1] = w2;\n\tgr[ 2] =  a; gs[ 2] = -a;  gw[ 2] = w1;\n\tgr[ 3] = -a; gs[ 3] =  0;  gw[ 3] = w2;\n\tgr[ 4] =  0; gs[ 4] =  0;  gw[ 4] = w3;\n\tgr[ 5] =  a; gs[ 5] =  0;  gw[ 5] = w2;\n\tgr[ 6] = -a; gs[ 6] =  a;  gw[ 6] = w1;\n\tgr[ 7] =  0; gs[ 7] =  a;  gw[ 7] = w2;\n\tgr[ 8] =  a; gs[ 8] =  a;  gw[ 8] = w1;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad8G9::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n\n//=============================================================================\n//                          F E Q U A D 8 N I\n//=============================================================================\n\nFEQuad8NI::FEQuad8NI() : FEQuad8_(NINT, FE_QUAD8NI)\n{\n    double w = 1./9.;\n    gr[0] = -1; gs[0] = -1; gw[0] = w;\n    gr[1] =  1; gs[1] = -1; gw[1] = w;\n    gr[2] =  1; gs[2] =  1; gw[2] = w;\n    gr[3] = -1; gs[3] =  1; gw[3] = w;\n    gr[4] =  0; gs[4] = -1; gw[0] = 4*w;\n    gr[5] =  1; gs[5] =  0; gw[1] = 4*w;\n    gr[6] =  0; gs[6] =  1; gw[2] = 4*w;\n    gr[7] = -1; gs[7] =  0; gw[3] = 4*w;\n    init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad8NI::project_to_nodes(double* ai, double* ao) const\n{\n    ao[0] = ai[0];\n    ao[1] = ai[1];\n    ao[2] = ai[2];\n    ao[3] = ai[3];\n    ao[4] = ai[4];\n    ao[5] = ai[5];\n    ao[6] = ai[6];\n    ao[7] = ai[7];\n}\n\n//=============================================================================\n//          F E Q U A D 9 \n//=============================================================================\n\nvoid FEQuad9_::init()\n{\n\t// allocate shape classes\n\tm_shapeP.resize(3);\n\tm_shapeP[0] = 0;\n\tm_shapeP[1] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_QUAD4));\n\tm_shapeP[2] = dynamic_cast<FESurfaceElementShape*>(FEElementLibrary::GetElementShapeClass(ET_QUAD9));\n\n    // centroid coordinates\n    cr = cs = 0;\n    \n\t// initialize base class\n\tFESurfaceElementTraits::init();\n}\n\n//=============================================================================\n//       F E Q U A D 9 G 9\n//=============================================================================\n\nFEQuad9G9::FEQuad9G9() : FEQuad9_(NINT, FE_QUAD9G9)\n{\n\t// integration point coordinates\n\tconst double a = sqrt(0.6);\n\tconst double w1 = 25.0/81.0;\n\tconst double w2 = 40.0/81.0;\n\tconst double w3 = 64.0/81.0;\n\tgr[ 0] = -a; gs[ 0] = -a;  gw[ 0] = w1;\n\tgr[ 1] =  0; gs[ 1] = -a;  gw[ 1] = w2;\n\tgr[ 2] =  a; gs[ 2] = -a;  gw[ 2] = w1;\n\tgr[ 3] = -a; gs[ 3] =  0;  gw[ 3] = w2;\n\tgr[ 4] =  0; gs[ 4] =  0;  gw[ 4] = w3;\n\tgr[ 5] =  a; gs[ 5] =  0;  gw[ 5] = w2;\n\tgr[ 6] = -a; gs[ 6] =  a;  gw[ 6] = w1;\n\tgr[ 7] =  0; gs[ 7] =  a;  gw[ 7] = w2;\n\tgr[ 8] =  a; gs[ 8] =  a;  gw[ 8] = w1;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad9G9::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n//=============================================================================\n//                          F E Q U A D 9 N I\n//=============================================================================\n\nFEQuad9NI::FEQuad9NI() : FEQuad9_(NINT, FE_QUAD9NI)\n{\n    double w = 1./9.;\n    gr[0] = -1; gs[0] = -1; gw[0] = w;\n    gr[1] =  1; gs[1] = -1; gw[1] = w;\n    gr[2] =  1; gs[2] =  1; gw[2] = w;\n    gr[3] = -1; gs[3] =  1; gw[3] = w;\n    gr[4] =  0; gs[4] = -1; gw[0] = 4*w;\n    gr[5] =  1; gs[5] =  0; gw[1] = 4*w;\n    gr[6] =  0; gs[6] =  1; gw[2] = 4*w;\n    gr[7] = -1; gs[7] =  0; gw[3] = 4*w;\n    gr[8] =  0; gs[8] =  0; gw[3] = 16*w;\n    init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad9NI::project_to_nodes(double* ai, double* ao) const\n{\n    ao[0] = ai[0];\n    ao[1] = ai[1];\n    ao[2] = ai[2];\n    ao[3] = ai[3];\n    ao[4] = ai[4];\n    ao[5] = ai[5];\n    ao[6] = ai[6];\n    ao[7] = ai[7];\n    ao[8] = ai[8];\n}\n\n//=============================================================================\n//\n//                      S H E L L   E L E M E N T S\n//\n//=============================================================================\n\nFEShellElementTraits::FEShellElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et) : FEElementTraits(ni, ne, FE_ELEM_SHELL, es, et)\n{\n\tm_nvln = ne;\n\n\tgr.resize(ni);\n\tgs.resize(ni);\n\tgt.resize(ni);\n\tgw.resize(ni);\n\n\tHr.resize(ni, ne);\n\tHs.resize(ni, ne);\n\n\tm_faces = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize element traits data\nvoid FEShellElementTraits::init()\n{\n    assert(m_nint > 0);\n    assert(m_neln > 0);\n    const int NELN = FEElement::MAX_NODES;\n    \n    // calculate shape function values at gauss points\n    double N[NELN];\n    for (int n=0; n<m_nint; ++n)\n    {\n        shape_fnc(N, gr[n], gs[n]);\n        for (int i=0; i<m_neln; ++i) m_H[n][i] = N[i];\n    }\n    \n    // calculate local derivatives of shape functions at gauss points\n    double Nr[NELN], Ns[NELN];\n    for (int n=0; n<m_nint; ++n)\n    {\n        shape_deriv(Nr, Ns, gr[n], gs[n]);\n        for (int i=0; i<m_neln; ++i)\n        {\n            Hr[n][i] = Nr[i];\n            Hs[n][i] = Ns[i];\n        }\n    }\n}\n\n//! project mat3ds integration point data to nodes\nvoid FEShellElementTraits::project_to_nodes(mat3ds* si, mat3ds* so) const\n{\n\tdouble ai[FEElement::MAX_INTPOINTS];\n\tdouble ao[FEElement::MAX_NODES];\n\tfor (int i = 0; i < 3; ++i) {\n\t\tfor (int j = i; j < 3; ++j) {\n\t\t\tfor (int n = 0; n < m_nint; ++n) ai[n] = si[n](i, j);\n\t\t\tproject_to_nodes(ai, ao);\n\t\t\tfor (int n = 0; n < m_nvln; ++n) so[n](i, j) = ao[n];\n\t\t}\n\t}\n}\n\n//=============================================================================\n//                                F E S H E L L Q U A D 4\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEShellQuad4_::shape_fnc(double* H, double r, double s)\n{\n    H[0] = 0.25*(1-r)*(1-s);\n    H[1] = 0.25*(1+r)*(1-s);\n    H[2] = 0.25*(1+r)*(1+s);\n    H[3] = 0.25*(1-r)*(1+s);\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEShellQuad4_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n    Hr[0] = -0.25*(1-s);\n    Hr[1] =  0.25*(1-s);\n    Hr[2] =  0.25*(1+s);\n    Hr[3] = -0.25*(1+s);\n    \n    Hs[0] = -0.25*(1-r);\n    Hs[1] = -0.25*(1+r);\n    Hs[2] =  0.25*(1+r);\n    Hs[3] =  0.25*(1-r);\n}\n\n//*****************************************************************************\n//                          S H E L L Q U A D 4 G 4\n//*****************************************************************************\n\nFEShellQuad4G4::FEShellQuad4G4() : FEShellQuad4_(NINT, FE_SHELL_QUAD4G4)\n{\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gs[0] = -a; gw[0] = 1;\n\tgr[1] =  a; gs[1] = -a; gw[1] = 1;\n\tgr[2] =  a; gs[2] =  a; gw[2] = 1;\n\tgr[3] = -a; gs[3] =  a; gw[3] = 1;\n\tinit(); \n\tm_Hi = m_H.inverse();\n}\n\nvoid FEShellQuad4G4::project_to_nodes(double* ai, double* ao) const\n{\n\tint ni = NINT;\n\tint ne = NELN;\n\tassert(ni == ne);\n\tfor (int i = 0; i < ne; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j = 0; j < ni; ++j) ao[i] += m_Hi[i][j] * ai[j];\n\t}\n}\n\n//*****************************************************************************\n//                          S H E L L Q U A D 4 G 8\n//*****************************************************************************\n\nint FEShellQuad4G8::ni[NELN] = { 4, 5, 6, 7 };\n\nFEShellQuad4G8::FEShellQuad4G8() : FEShellQuad4_(NINT, FE_SHELL_QUAD4G8)\n{\n    m_nvln = 8;\n    const double a = 1.0 / sqrt(3.0);\n    const double w = 1.0;\n    \n    gr[ 0] = -a; gs[ 0] = -a; gt[ 0] = -a; gw[ 0] = w;\n    gr[ 1] =  a; gs[ 1] = -a; gt[ 1] = -a; gw[ 1] = w;\n    gr[ 2] =  a; gs[ 2] =  a; gt[ 2] = -a; gw[ 2] = w;\n    gr[ 3] = -a; gs[ 3] =  a; gt[ 3] = -a; gw[ 3] = w;\n    \n    gr[ 4] = -a; gs[ 4] = -a; gt[ 4] =  a; gw[ 4] = w;\n    gr[ 5] =  a; gs[ 5] = -a; gt[ 5] =  a; gw[ 5] = w;\n    gr[ 6] =  a; gs[ 6] =  a; gt[ 6] =  a; gw[ 6] = w;\n    gr[ 7] = -a; gs[ 7] =  a; gt[ 7] =  a; gw[ 7] = w;\n    \n    init();\n\n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellQuad4G8::project_to_nodes(double* ai, double* ao) const\n{\n    std::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//*****************************************************************************\n//                          S H E L L Q U A D 4 G 1 2\n//*****************************************************************************\n\nint FEShellQuad4G12::ni[NELN] = { 8, 9, 10, 11 };\n\nFEShellQuad4G12::FEShellQuad4G12() : FEShellQuad4_(NINT, FE_SHELL_QUAD4G12)\n{\n    m_nvln = 8;\n    const double a = 1.0 / sqrt(3.0);\n    const double b = sqrt(3.0/5.0);\n    const double w = 5.0 / 9.0;\n    \n    gr[ 0] = -a; gs[ 0] = -a; gt[ 0] = -b; gw[ 0] = w;\n    gr[ 1] =  a; gs[ 1] = -a; gt[ 1] = -b; gw[ 1] = w;\n    gr[ 2] =  a; gs[ 2] =  a; gt[ 2] = -b; gw[ 2] = w;\n    gr[ 3] = -a; gs[ 3] =  a; gt[ 3] = -b; gw[ 3] = w;\n    \n    gr[ 4] = -a; gs[ 4] = -a; gt[ 4] =  0; gw[ 4] = 8.0/9.0;\n    gr[ 5] =  a; gs[ 5] = -a; gt[ 5] =  0; gw[ 5] = 8.0/9.0;\n    gr[ 6] =  a; gs[ 6] =  a; gt[ 6] =  0; gw[ 6] = 8.0/9.0;\n    gr[ 7] = -a; gs[ 7] =  a; gt[ 7] =  0; gw[ 7] = 8.0/9.0;\n    \n    gr[ 8] = -a; gs[ 8] = -a; gt[ 8] =  b; gw[ 8] = w;\n    gr[ 9] =  a; gs[ 9] = -a; gt[ 9] =  b; gw[ 9] = w;\n    gr[10] =  a; gs[10] =  a; gt[10] =  b; gw[10] = w;\n    gr[11] = -a; gs[11] =  a; gt[11] =  b; gw[11] = w;\n    \n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellQuad4G12::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//                                F E S H E L L T R I 3\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEShellTri3_::shape_fnc(double* H, double r, double s)\n{\n    H[0] = 1-r-s;\n    H[1] = r;\n    H[2] = s;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEShellTri3_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n    Hr[0] = -1;\n    Hr[1] =  1;\n    Hr[2] =  0;\n    \n    Hs[0] = -1;\n    Hs[1] =  0;\n    Hs[2] =  1;\n}\n\n//*****************************************************************************\n//                          F E S H E L L T R I G 3 \n//*****************************************************************************\n\n//-----------------------------------------------------------------------------\nFEShellTri3G3::FEShellTri3G3() : FEShellTri3_(NINT, FE_SHELL_TRI3G3)\n{\n\tconst double a = 1.0 / 6.0;\n\tconst double b = 2.0 / 3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = a;\n\tgr[1] = b; gs[1] = a; gw[1] = a;\n\tgr[2] = a; gs[2] = b; gw[2] = a;\n\tinit();\n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEShellTri3G3::project_to_nodes(double* ai, double* ao) const\n{\n\tassert(NINT == NELN);\n\tfor (int i = 0; i < NELN; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j = 0; j < NINT; ++j) ao[i] += m_Hi[i][j] * ai[j];\n\t}\n}\n\n//*****************************************************************************\n//                          S H E L L T R I 3 G 6\n//*****************************************************************************\n\nint FEShellTri3G6::ni[NELN] = { 3, 4, 5 };\n\nFEShellTri3G6::FEShellTri3G6() : FEShellTri3_(NINT, FE_SHELL_TRI3G6)\n{\n    m_nvln = 6;\n    //gauss intergration points\n    const double a = 1.0/6.0;\n    const double b = 2.0/3.0;\n    const double c = 1.0 / sqrt(3.0);\n    \n    gr[0] = a; gs[0] = a; gt[0] = -c; gw[0] = a;\n    gr[1] = b; gs[1] = a; gt[1] = -c; gw[1] = a;\n    gr[2] = a; gs[2] = b; gt[2] = -c; gw[2] = a;\n    gr[3] = a; gs[3] = a; gt[3] =  c; gw[3] = a;\n    gr[4] = b; gs[4] = a; gt[4] =  c; gw[4] = a;\n    gr[5] = a; gs[5] = b; gt[5] =  c; gw[5] = a;\n\n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellTri3G6::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//*****************************************************************************\n//                          S H E L L T R I 3 G 9\n//*****************************************************************************\n\nint FEShellTri3G9::ni[NELN] = { 6, 7, 8 };\n\nFEShellTri3G9::FEShellTri3G9() : FEShellTri3_(NINT, FE_SHELL_TRI3G9)\n{\n    m_nvln = 6;\n    const double a = 1.0 / 6.0;\n    const double b = 2.0 / 3.0;\n    const double w1 = 5.0 / 9.0;\n    const double w2 = 8.0 / 9.0;\n    \n    gr[0] = a; gs[0] = a; gt[0] = -b; gw[0] = a*w1;\n    gr[1] = b; gs[1] = a; gt[1] = -b; gw[1] = a*w1;\n    gr[2] = a; gs[2] = b; gt[2] = -b; gw[2] = a*w1;\n    \n    gr[3] = a; gs[3] = a; gt[3] =  0; gw[3] = a*w2;\n    gr[4] = b; gs[4] = a; gt[4] =  0; gw[4] = a*w2;\n    gr[5] = a; gs[5] = b; gt[5] =  0; gw[5] = a*w2;\n    \n    gr[6] = a; gs[6] = a; gt[6] =  b; gw[6] = a*w1;\n    gr[7] = b; gs[7] = a; gt[7] =  b; gw[7] = a*w1;\n    gr[8] = a; gs[8] = b; gt[8] =  b; gw[8] = a*w1;\n    \n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellTri3G9::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//                                F E S H E L L Q U A D 8\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEShellQuad8_::shape_fnc(double* H, double r, double s)\n{\n    H[4] = 0.5*(1 - r*r)*(1 - s);\n    H[5] = 0.5*(1 - s*s)*(1 + r);\n    H[6] = 0.5*(1 - r*r)*(1 + s);\n    H[7] = 0.5*(1 - s*s)*(1 - r);\n    \n    H[0] = 0.25*(1 - r)*(1 - s) - 0.5*(H[4] + H[7]);\n    H[1] = 0.25*(1 + r)*(1 - s) - 0.5*(H[4] + H[5]);\n    H[2] = 0.25*(1 + r)*(1 + s) - 0.5*(H[5] + H[6]);\n    H[3] = 0.25*(1 - r)*(1 + s) - 0.5*(H[6] + H[7]);\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEShellQuad8_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n    Hr[4] = -r*(1 - s);\n    Hr[5] = 0.5*(1 - s*s);\n    Hr[6] = -r*(1 + s);\n    Hr[7] = -0.5*(1 - s*s);\n    \n    Hr[0] = -0.25*(1 - s) - 0.5*(Hr[4] + Hr[7]);\n    Hr[1] =  0.25*(1 - s) - 0.5*(Hr[4] + Hr[5]);\n    Hr[2] =  0.25*(1 + s) - 0.5*(Hr[5] + Hr[6]);\n    Hr[3] = -0.25*(1 + s) - 0.5*(Hr[6] + Hr[7]);\n    \n    Hs[4] = -0.5*(1 - r*r);\n    Hs[5] = -s*(1 + r);\n    Hs[6] = 0.5*(1 - r*r);\n    Hs[7] = -s*(1 - r);\n    \n    Hs[0] = -0.25*(1 - r) - 0.5*(Hs[4] + Hs[7]);\n    Hs[1] = -0.25*(1 + r) - 0.5*(Hs[4] + Hs[5]);\n    Hs[2] =  0.25*(1 + r) - 0.5*(Hs[5] + Hs[6]);\n    Hs[3] =  0.25*(1 - r) - 0.5*(Hs[6] + Hs[7]);\n}\n\n//*****************************************************************************\n//                          S H E L L Q U A D 8 G 1 8\n//*****************************************************************************\n\nint FEShellQuad8G18::ni[NELN] = { 9, 10, 11, 12, 14, 15, 16, 17 };\n\nFEShellQuad8G18::FEShellQuad8G18() : FEShellQuad8_(NINT, FE_SHELL_QUAD8G18)\n{\n    m_nvln = 16;\n\n    // integration point coordinates\n    const double a = 0.774596669241483;\n    const double c = 0.577350269189626;\n    const double w1 = 5.0 / 9.0;\n    const double w2 = 8.0 / 9.0;\n    gr[ 0] = -a; gs[ 0] = -a; gt[ 0] = -c; gw[ 0] = w1*w1;\n    gr[ 1] =  0; gs[ 1] = -a; gt[ 1] = -c; gw[ 1] = w2*w1;\n    gr[ 2] =  a; gs[ 2] = -a; gt[ 2] = -c; gw[ 2] = w1*w1;\n    gr[ 3] = -a; gs[ 3] =  0; gt[ 3] = -c; gw[ 3] = w1*w2;\n    gr[ 4] =  0; gs[ 4] =  0; gt[ 4] = -c; gw[ 4] = w2*w2;\n    gr[ 5] =  a; gs[ 5] =  0; gt[ 5] = -c; gw[ 5] = w1*w2;\n    gr[ 6] = -a; gs[ 6] =  a; gt[ 6] = -c; gw[ 6] = w1*w1;\n    gr[ 7] =  0; gs[ 7] =  a; gt[ 7] = -c; gw[ 7] = w2*w1;\n    gr[ 8] =  a; gs[ 8] =  a; gt[ 8] = -c; gw[ 8] = w1*w1;\n    gr[ 9] = -a; gs[ 9] = -a; gt[ 9] =  c; gw[ 9] = w1*w1;\n    gr[10] =  0; gs[10] = -a; gt[10] =  c; gw[10] = w2*w1;\n    gr[11] =  a; gs[11] = -a; gt[11] =  c; gw[11] = w1*w1;\n    gr[12] = -a; gs[12] =  0; gt[12] =  c; gw[12] = w1*w2;\n    gr[13] =  0; gs[13] =  0; gt[13] =  c; gw[13] = w2*w2;\n    gr[14] =  a; gs[14] =  0; gt[14] =  c; gw[14] = w1*w2;\n    gr[15] = -a; gs[15] =  a; gt[15] =  c; gw[15] = w1*w1;\n    gr[16] =  0; gs[16] =  a; gt[16] =  c; gw[16] = w2*w1;\n    gr[17] =  a; gs[17] =  a; gt[17] =  c; gw[17] = w1*w1;\n    \n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellQuad8G18::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//*****************************************************************************\n//                          S H E L L Q U A D 8 G 2 7\n//*****************************************************************************\n\nint FEShellQuad8G27::ni[NELN] = { 18, 19, 20, 21, 23, 24, 25, 26 };\n\nFEShellQuad8G27::FEShellQuad8G27() : FEShellQuad8_(NINT, FE_SHELL_QUAD8G27)\n{\n    m_nvln = 16;\n\n    // integration point coordinates\n    const double a = 0.774596669241483;\n    const double w1 = 5.0 / 9.0;\n    const double w2 = 8.0 / 9.0;\n    gr[ 0] = -a; gs[ 0] = -a; gt[ 0] = -a; gw[ 0] = w1*w1*w1;\n    gr[ 1] =  0; gs[ 1] = -a; gt[ 1] = -a; gw[ 1] = w2*w1*w1;\n    gr[ 2] =  a; gs[ 2] = -a; gt[ 2] = -a; gw[ 2] = w1*w1*w1;\n    gr[ 3] = -a; gs[ 3] =  0; gt[ 3] = -a; gw[ 3] = w1*w2*w1;\n    gr[ 4] =  0; gs[ 4] =  0; gt[ 4] = -a; gw[ 4] = w2*w2*w1;\n    gr[ 5] =  a; gs[ 5] =  0; gt[ 5] = -a; gw[ 5] = w1*w2*w1;\n    gr[ 6] = -a; gs[ 6] =  a; gt[ 6] = -a; gw[ 6] = w1*w1*w1;\n    gr[ 7] =  0; gs[ 7] =  a; gt[ 7] = -a; gw[ 7] = w2*w1*w1;\n    gr[ 8] =  a; gs[ 8] =  a; gt[ 8] = -a; gw[ 8] = w1*w1*w1;\n    gr[ 9] = -a; gs[ 9] = -a; gt[ 9] =  0; gw[ 9] = w1*w1*w2;\n    gr[10] =  0; gs[10] = -a; gt[10] =  0; gw[10] = w2*w1*w2;\n    gr[11] =  a; gs[11] = -a; gt[11] =  0; gw[11] = w1*w1*w2;\n    gr[12] = -a; gs[12] =  0; gt[12] =  0; gw[12] = w1*w2*w2;\n    gr[13] =  0; gs[13] =  0; gt[13] =  0; gw[13] = w2*w2*w2;\n    gr[14] =  a; gs[14] =  0; gt[14] =  0; gw[14] = w1*w2*w2;\n    gr[15] = -a; gs[15] =  a; gt[15] =  0; gw[15] = w1*w1*w2;\n    gr[16] =  0; gs[16] =  a; gt[16] =  0; gw[16] = w2*w1*w2;\n    gr[17] =  a; gs[17] =  a; gt[17] =  0; gw[17] = w1*w1*w2;\n    gr[18] = -a; gs[18] = -a; gt[18] =  a; gw[18] = w1*w1*w1;\n    gr[19] =  0; gs[19] = -a; gt[19] =  a; gw[19] = w2*w1*w1;\n    gr[20] =  a; gs[20] = -a; gt[20] =  a; gw[20] = w1*w1*w1;\n    gr[21] = -a; gs[21] =  0; gt[21] =  a; gw[21] = w1*w2*w1;\n    gr[22] =  0; gs[22] =  0; gt[22] =  a; gw[22] = w2*w2*w1;\n    gr[23] =  a; gs[23] =  0; gt[23] =  a; gw[23] = w1*w2*w1;\n    gr[24] = -a; gs[24] =  a; gt[24] =  a; gw[24] = w1*w1*w1;\n    gr[25] =  0; gs[25] =  a; gt[25] =  a; gw[25] = w2*w1*w1;\n    gr[26] =  a; gs[26] =  a; gt[26] =  a; gw[26] = w1*w1*w1;\n    \n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellQuad8G27::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//                                F E S H E L L T R I 6\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEShellTri6_::shape_fnc(double* H, double r, double s)\n{\n    double r1 = 1.0 - r - s;\n    double r2 = r;\n    double r3 = s;\n    \n    H[0] = r1*(2.0*r1 - 1.0);\n    H[1] = r2*(2.0*r2 - 1.0);\n    H[2] = r3*(2.0*r3 - 1.0);\n    H[3] = 4.0*r1*r2;\n    H[4] = 4.0*r2*r3;\n    H[5] = 4.0*r3*r1;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEShellTri6_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n    Hr[0] = -3.0 + 4.0*r + 4.0*s;\n    Hr[1] =  4.0*r - 1.0;\n    Hr[2] =  0.0;\n    Hr[3] =  4.0 - 8.0*r - 4.0*s;\n    Hr[4] =  4.0*s;\n    Hr[5] = -4.0*s;\n    \n    Hs[0] = -3.0 + 4.0*s + 4.0*r;\n    Hs[1] =  0.0;\n    Hs[2] =  4.0*s - 1.0;\n    Hs[3] = -4.0*r;\n    Hs[4] =  4.0*r;\n    Hs[5] =  4.0 - 8.0*s - 4.0*r;\n}\n\n//*****************************************************************************\n//                          S H E L L T R I 6 G 1 4\n//*****************************************************************************\n\nint FEShellTri6G14::ni[NELN] = { 8, 9, 10, 11, 12, 13 };\n\nFEShellTri6G14::FEShellTri6G14() : FEShellTri6_(NINT, FE_SHELL_TRI6G14)\n{\n    m_nvln = 12;\n\n    const double a = 0.774596669241483;\n    const double c = 0.577350269189626;\n    const double w = 1.0/2.0;\n    \n    gr[ 0] = 0.333333333333333; gs[ 0] = 0.333333333333333; gt[ 0] = -c; gw[ 0] = w*0.225000000000000;\n    gr[ 1] = 0.797426985353087; gs[ 1] = 0.101286507323456; gt[ 1] = -c; gw[ 1] = w*0.125939180544827;\n    gr[ 2] = 0.101286507323456; gs[ 2] = 0.797426985353087; gt[ 2] = -c; gw[ 2] = w*0.125939180544827;\n    gr[ 3] = 0.101286507323456; gs[ 3] = 0.101286507323456; gt[ 3] = -c; gw[ 3] = w*0.125939180544827;\n    gr[ 4] = 0.470142064105115; gs[ 4] = 0.470142064105115; gt[ 4] = -c; gw[ 4] = w*0.132394152788506;\n    gr[ 5] = 0.470142064105115; gs[ 5] = 0.059715871789770; gt[ 5] = -c; gw[ 5] = w*0.132394152788506;\n    gr[ 6] = 0.059715871789770; gs[ 6] = 0.470142064105115; gt[ 6] = -c; gw[ 6] = w*0.132394152788506;\n    \n    gr[ 7] = 0.333333333333333; gs[ 7] = 0.333333333333333; gt[ 7] =  c; gw[ 7] = w*0.225000000000000;\n    gr[ 8] = 0.797426985353087; gs[ 8] = 0.101286507323456; gt[ 8] =  c; gw[ 8] = w*0.125939180544827;\n    gr[ 9] = 0.101286507323456; gs[ 9] = 0.797426985353087; gt[ 9] =  c; gw[ 9] = w*0.125939180544827;\n    gr[10] = 0.101286507323456; gs[10] = 0.101286507323456; gt[10] =  c; gw[10] = w*0.125939180544827;\n    gr[11] = 0.470142064105115; gs[11] = 0.470142064105115; gt[11] =  c; gw[11] = w*0.132394152788506;\n    gr[12] = 0.470142064105115; gs[12] = 0.059715871789770; gt[12] =  c; gw[12] = w*0.132394152788506;\n    gr[13] = 0.059715871789770; gs[13] = 0.470142064105115; gt[13] =  c; gw[13] = w*0.132394152788506;\n    \n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellTri6G14::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//*****************************************************************************\n//                          S H E L L T R I 6 G 2 1\n//*****************************************************************************\n\nint FEShellTri6G21::ni[NELN] = { 15, 16, 17, 18, 19, 20 };\n\nFEShellTri6G21::FEShellTri6G21() : FEShellTri6_(NINT, FE_SHELL_TRI6G21)\n{\n    m_nvln = 12;\n\n    const double a = 0.774596669241483;\n    const double w = 1.0/2.0;\n    const double w1 = 5.0 / 9.0;\n    const double w2 = 8.0 / 9.0;\n    \n    gr[ 0] = 0.333333333333333; gs[ 0] = 0.333333333333333; gt[ 0] = -a; gw[ 0] = w*w1*0.225000000000000;\n    gr[ 1] = 0.797426985353087; gs[ 1] = 0.101286507323456; gt[ 1] = -a; gw[ 1] = w*w1*0.125939180544827;\n    gr[ 2] = 0.101286507323456; gs[ 2] = 0.797426985353087; gt[ 2] = -a; gw[ 2] = w*w1*0.125939180544827;\n    gr[ 3] = 0.101286507323456; gs[ 3] = 0.101286507323456; gt[ 3] = -a; gw[ 3] = w*w1*0.125939180544827;\n    gr[ 4] = 0.470142064105115; gs[ 4] = 0.470142064105115; gt[ 4] = -a; gw[ 4] = w*w1*0.132394152788506;\n    gr[ 5] = 0.470142064105115; gs[ 5] = 0.059715871789770; gt[ 5] = -a; gw[ 5] = w*w1*0.132394152788506;\n    gr[ 6] = 0.059715871789770; gs[ 6] = 0.470142064105115; gt[ 6] = -a; gw[ 6] = w*w1*0.132394152788506;\n    \n    gr[ 7] = 0.333333333333333; gs[ 7] = 0.333333333333333; gt[ 7] =  0; gw[ 7] = w*w2*0.225000000000000;\n    gr[ 8] = 0.797426985353087; gs[ 8] = 0.101286507323456; gt[ 8] =  0; gw[ 8] = w*w2*0.125939180544827;\n    gr[ 9] = 0.101286507323456; gs[ 9] = 0.797426985353087; gt[ 9] =  0; gw[ 9] = w*w2*0.125939180544827;\n    gr[10] = 0.101286507323456; gs[10] = 0.101286507323456; gt[10] =  0; gw[10] = w*w2*0.125939180544827;\n    gr[11] = 0.470142064105115; gs[11] = 0.470142064105115; gt[11] =  0; gw[11] = w*w2*0.132394152788506;\n    gr[12] = 0.470142064105115; gs[12] = 0.059715871789770; gt[12] =  0; gw[12] = w*w2*0.132394152788506;\n    gr[13] = 0.059715871789770; gs[13] = 0.470142064105115; gt[13] =  0; gw[13] = w*w2*0.132394152788506;\n    \n    gr[14] = 0.333333333333333; gs[14] = 0.333333333333333; gt[14] =  a; gw[14] = w*w1*0.225000000000000;\n    gr[15] = 0.797426985353087; gs[15] = 0.101286507323456; gt[15] =  a; gw[15] = w*w1*0.125939180544827;\n    gr[16] = 0.101286507323456; gs[16] = 0.797426985353087; gt[16] =  a; gw[16] = w*w1*0.125939180544827;\n    gr[17] = 0.101286507323456; gs[17] = 0.101286507323456; gt[17] =  a; gw[17] = w*w1*0.125939180544827;\n    gr[18] = 0.470142064105115; gs[18] = 0.470142064105115; gt[18] =  a; gw[18] = w*w1*0.132394152788506;\n    gr[19] = 0.470142064105115; gs[19] = 0.059715871789770; gt[19] =  a; gw[19] = w*w1*0.132394152788506;\n    gr[20] = 0.059715871789770; gs[20] = 0.470142064105115; gt[20] =  a; gw[20] = w*w1*0.132394152788506;\n    \n    init();\n    \n    m_MT.resize(m_nvln, NINT);\n    for (int i=0; i<NINT; ++i) {\n        for (int n=0; n<NELN; ++n)\n            m_MT(n,i) = m_H(i,n)*(1-gt[i])/2;\n        for (int n=NELN; n<m_nvln; ++n)\n            m_MT(n,i) = m_H(i,n-NELN)*(1+gt[i])/2;\n    }\n    \n    m_Hi.resize(m_nvln, m_nvln);\n    m_Hi = m_MT*m_MT.transpose();\n    m_Hi = m_Hi.inverse();\n}\n\n//-----------------------------------------------------------------------------\n//! project to nodes\nvoid FEShellTri6G21::project_to_nodes(double* ai, double* ao) const\n{\n\tstd::vector<double> v(m_nvln);\n    for (int n=0; n<m_nvln; ++n) {\n        v[n] = 0;\n        for (int i=0; i<NINT; ++i) {\n            v[n] += m_MT(n,i)*ai[i];\n        }\n    }\n    for (int j=0; j<m_nvln; ++j)\n    {\n        ao[j] = 0;\n        for (int k=0; k<m_nvln; ++k)\n        {\n            ao[j] += m_Hi[j][k]*v[k];\n        }\n    }\n}\n\n//=============================================================================\n//                          F E T R U S S E L E M E N T\n//=============================================================================\n\nFETrussElementTraits::FETrussElementTraits() : FEElementTraits(NINT, NELN, FE_ELEM_TRUSS, ET_TRUSS2, FE_TRUSS) \n{ \n\tgr.resize(NINT, 0);\n\tgw.resize(NINT, 0);\n\n\tinit(); \n}\n\nvoid FETrussElementTraits::init()\n{\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gr[1] =  a;\n\tgw[0] = gw[1] = 1;\n\tassert(m_nint > 0);\n\tassert(m_neln > 0);\n\n\t// evaluate shape functions\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble N[NE];\n\tfor (int n = 0; n < m_nint; ++n)\n\t{\n\t\tshape(N, gr[n]);\n\t\tfor (int i = 0; i < m_neln; ++i) m_H[n][i] = N[i];\n\t}\n}\n\nvoid FETrussElementTraits::shape(double* H, double r)\n{\n\tH[0] = 0.5 * (1.0 - r);\n\tH[1] = 0.5 * (1.0 + r);\n}\n\n//=============================================================================\n//\n//                  2 D   E L E M E N T S\n//\n//=============================================================================\n\nFE2DElementTraits::FE2DElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et) : FEElementTraits(ni, ne, FE_ELEM_2D, es, et)\n{\n\tgr.resize(ni);\n\tgs.resize(ni);\n\tgw.resize(ni);\n\n\tGr.resize(ni, ne);\n\tGs.resize(ni, ne);\n    \n    Grr.resize(ni, ne);\n    Gsr.resize(ni, ne);\n    \n    Grs.resize(ni, ne);\n    Gss.resize(ni, ne);\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize the 2D element traits data variables.\n//\nvoid FE2DElementTraits::init()\n{\n\tassert(m_nint > 0);\n\tassert(m_neln > 0);\n\n\t// evaluate shape functions\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble N[NE];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tshape(N, gr[n], gs[n]);\n\t\tfor (int i=0; i<m_neln; ++i) m_H[n][i] = N[i];\n\t}\n\t\n\t// evaluate shape function derivatives\n\tdouble Nr[NE], Ns[NE];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tshape_deriv(Nr, Ns, gr[n], gs[n]);\n\t\tfor (int i=0; i<m_neln; ++i)\n\t\t{\n\t\t\tGr[n][i] = Nr[i];\n\t\t\tGs[n][i] = Ns[i];\n\t\t}\n\t}\n}\n\n//=============================================================================\n//                          F E 2 D T R I \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri3_::shape(double* H, double r, double s)\n{\n\tH[0] = 1.0 - r - s;\n\tH[1] = r;\n\tH[2] = s;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri3_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[0] = -1; Hs[0] = -1;\n\tHr[1] =  1; Hs[1] =  0;\n\tHr[2] =  0; Hs[2] =  1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri3_::shape_deriv2(double* Hrr, double* Hrs, double* Hss, double r, double s)\n{\n\tHrr[0] = 0; Hrs[0] = 0; Hss[0] = 0;\n\tHrr[1] = 0; Hrs[1] = 0; Hss[1] = 0;\n\tHrr[2] = 0; Hrs[2] = 0; Hss[2] = 0;\n}\n\n//=============================================================================\n//                          F E 2 D T R I G 1 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFE2DTri3G1::FE2DTri3G1() : FE2DTri3_(NINT, FE2D_TRI3G1)\n{\n\tconst double a = 1.0/3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = 0.5;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri3G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n\tao[2] = ai[0];\n}\n\n//============================================================================\n//                             F E 2 D T R I 6\n//============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri6_::shape(double* H, double r, double s)\n{\n\tdouble r1 = 1.0 - r - s;\n\tdouble r2 = r;\n\tdouble r3 = s;\n\n\tH[0] = r1*(2.0*r1 - 1.0);\n\tH[1] = r2*(2.0*r2 - 1.0);\n\tH[2] = r3*(2.0*r3 - 1.0);\n\tH[3] = 4.0*r1*r2;\n\tH[4] = 4.0*r2*r3;\n\tH[5] = 4.0*r3*r1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri6_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[0] = -3.0 + 4.0*r + 4.0*s;\n\tHr[1] =  4.0*r - 1.0;\n\tHr[2] =  0.0;\n\tHr[3] =  4.0 - 8.0*r - 4.0*s;\n\tHr[4] =  4.0*s;\n\tHr[5] = -4.0*s;\n\n\tHs[0] = -3.0 + 4.0*s + 4.0*r;\n\tHs[1] =  0.0;\n\tHs[2] =  4.0*s - 1.0;\n\tHs[3] = -4.0*r;\n\tHs[4] =  4.0*r;\n\tHs[5] =  4.0 - 8.0*s - 4.0*r;\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri6_::shape_deriv2(double* Hrr, double* Hrs, double* Hss, double r, double s)\n{\n\tHrr[0] =  4.0; Hrs[0] =  4.0; Hss[0] =  4.0;\n\tHrr[1] =  4.0; Hrs[1] =  0.0; Hss[1] =  0.0;\n\tHrr[2] =  0.0; Hrs[2] =  0.0; Hss[2] =  4.0;\n\tHrr[3] = -8.0; Hrs[3] = -4.0; Hss[3] =  0.0;\n\tHrr[4] =  0.0; Hrs[4] =  4.0; Hss[4] =  0.0;\n\tHrr[5] =  0.0; Hrs[5] = -4.0; Hss[5] = -8.0;\n}\n\n//=============================================================================\n//                          F E 2 D T R I 6 G 3\n//=============================================================================\n\nFE2DTri6G3::FE2DTri6G3() : FE2DTri6_(NINT, FE2D_TRI6G3)\n{ \n\tconst double a = 1.0 / 6.0;\n\tconst double b = 2.0 / 3.0;\n\tgr[0] = a; gs[0] = a; gw[0] = a;\n\tgr[1] = b; gs[1] = a; gw[1] = a;\n\tgr[2] = a; gs[2] = b; gw[2] = a;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DTri6G3::project_to_nodes(double* ai, double* ao) const\n{\n\tmatrix H(3, 3);\n\tfor (int n=0; n<3; ++n)\n\t{\n\t\tH[n][0] = 1.0 - gr[n] - gs[n];\n\t\tH[n][1] = gr[n];\n\t\tH[n][2] = gs[n];\n\t}\n\tH.inverse();\n\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<3; ++j) ao[i] += H[i][j]*ai[j];\n\t}\n\n\tao[3] = 0.5*(ao[0] + ao[1]);\n\tao[4] = 0.5*(ao[1] + ao[2]);\n\tao[5] = 0.5*(ao[2] + ao[0]);\n}\n\n//=============================================================================\n//                          F E 2 D Q U A D 4\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FE2DQuad4_::shape(double* H, double r, double s)\n{\n\tH[0] = 0.25*(1-r)*(1-s);\n\tH[1] = 0.25*(1+r)*(1-s);\n\tH[2] = 0.25*(1+r)*(1+s);\n\tH[3] = 0.25*(1-r)*(1+s);\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DQuad4_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[0] = -0.25*(1-s); Hs[0] = -0.25*(1-r);\n\tHr[1] =  0.25*(1-s); Hs[1] = -0.25*(1+r);\n\tHr[2] =  0.25*(1+s); Hs[2] =  0.25*(1+r);\n\tHr[3] = -0.25*(1+s); Hs[3] =  0.25*(1-r);\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DQuad4_::shape_deriv2(double* Hrr, double* Hrs, double* Hss, double r, double s)\n{\n\tHrr[0] = 0; Hrs[0] =  0.25; Hss[0] = 0;\n\tHrr[1] = 0; Hrs[1] = -0.25; Hss[1] = 0;\n\tHrr[2] = 0; Hrs[2] =  0.25; Hss[2] = 0;\n\tHrr[3] = 0; Hrs[3] = -0.25; Hss[3] = 0;\n}\n\n//=============================================================================\n//                          F E 2 D Q U A D G 4 \n//=============================================================================\n\nFE2DQuad4G4::FE2DQuad4G4() : FE2DQuad4_(NINT, FE2D_QUAD4G4) \n{\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gs[0] = -a; gw[0] = 1;\n\tgr[1] =  a; gs[1] = -a; gw[1] = 1;\n\tgr[2] =  a; gs[2] =  a; gw[2] = 1;\n\tgr[3] = -a; gs[3] =  a; gw[3] = 1;\n\tinit(); \n\tm_Hi = m_H.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DQuad4G4::project_to_nodes(double* ai, double* ao) const\n{\n\tint ni = NINT;\n\tint ne = NELN;\n\tassert(ni == ne);\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<ni; ++j) ao[i] += m_Hi[i][j]*ai[j];\n\t}\n}\n\n//=============================================================================\n//          F E 2 D Q U A D 8 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// shape function at (r,s)\nvoid FE2DQuad8_::shape(double* H, double r, double s)\n{\n\tH[4] = 0.5*(1 - r*r)*(1 - s);\n\tH[5] = 0.5*(1 - s*s)*(1 + r);\n\tH[6] = 0.5*(1 - r*r)*(1 + s);\n\tH[7] = 0.5*(1 - s*s)*(1 - r);\n\n\tH[0] = 0.25*(1 - r)*(1 - s) - 0.5*(H[4] + H[7]);\n\tH[1] = 0.25*(1 + r)*(1 - s) - 0.5*(H[4] + H[5]);\n\tH[2] = 0.25*(1 + r)*(1 + s) - 0.5*(H[5] + H[6]);\n\tH[3] = 0.25*(1 - r)*(1 + s) - 0.5*(H[6] + H[7]);\n}\n\n//-----------------------------------------------------------------------------\n// shape function derivatives at (r,s)\nvoid FE2DQuad8_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[4] = -r*(1 - s);\n\tHr[5] = 0.5*(1 - s*s);\n\tHr[6] = -r*(1 + s);\n\tHr[7] = -0.5*(1 - s*s);\n\n\tHr[0] = -0.25*(1 - s) - 0.5*(Hr[4] + Hr[7]);\n\tHr[1] =  0.25*(1 - s) - 0.5*(Hr[4] + Hr[5]);\n\tHr[2] =  0.25*(1 + s) - 0.5*(Hr[5] + Hr[6]);\n\tHr[3] = -0.25*(1 + s) - 0.5*(Hr[6] + Hr[7]);\n\n\tHs[4] = -0.5*(1 - r*r);\n\tHs[5] = -s*(1 + r);\n\tHs[6] = 0.5*(1 - r*r);\n\tHs[7] = -s*(1 - r);\n\n\tHs[0] = -0.25*(1 - r) - 0.5*(Hs[4] + Hs[7]);\n\tHs[1] = -0.25*(1 + r) - 0.5*(Hs[4] + Hs[5]);\n\tHs[2] =  0.25*(1 + r) - 0.5*(Hs[5] + Hs[6]);\n\tHs[3] =  0.25*(1 - r) - 0.5*(Hs[6] + Hs[7]);\n}\n\n//-----------------------------------------------------------------------------\n//! shape function derivatives at (r,s)\nvoid FE2DQuad8_::shape_deriv2(double* Hrr, double* Hrs, double* Hss, double r, double s)\n{\n\tHrr[4] = -(1 - s);\n\tHrr[5] = 0.0;\n\tHrr[6] = -(1 + s);\n\tHrr[7] = 0.0;\n\n\tHrs[4] = r;\n\tHrs[5] = -s;\n\tHrs[6] = -r;\n\tHrs[7] = s;\n\n\tHss[4] = 0.0;\n\tHss[5] = -(1 + r);\n\tHss[6] = 0.0;\n\tHss[7] = -(1 - r);\n\n\tHrr[0] = - 0.5*(Hrr[4] + Hrr[7]);\n\tHrr[1] = - 0.5*(Hrr[4] + Hrr[5]);\n\tHrr[2] = - 0.5*(Hrr[5] + Hrr[6]);\n\tHrr[3] = - 0.5*(Hrr[6] + Hrr[7]);\n\n\tHrs[0] =  0.25 - 0.5*(Hrs[4] + Hrs[7]);\n\tHrs[1] = -0.25 - 0.5*(Hrs[4] + Hrs[5]);\n\tHrs[2] =  0.25 - 0.5*(Hrs[5] + Hrs[6]);\n\tHrs[3] = -0.25 - 0.5*(Hrs[6] + Hrs[7]);\n\n\tHss[0] = - 0.5*(Hss[4] + Hss[7]);\n\tHss[1] = - 0.5*(Hss[4] + Hss[5]);\n\tHss[2] = - 0.5*(Hss[5] + Hss[6]);\n\tHss[3] = - 0.5*(Hss[6] + Hss[7]);\n}\n\n//=============================================================================\n//       F E 2 D Q U A D 8 G 9\n//=============================================================================\n\nFE2DQuad8G9::FE2DQuad8G9() : FE2DQuad8_(NINT, FE2D_QUAD8G9)\n{\n\t// integration point coordinates\n\tconst double a = sqrt(0.6);\n\tconst double w1 = 25.0/81.0;\n\tconst double w2 = 40.0/81.0;\n\tconst double w3 = 64.0/81.0;\n\tgr[ 0] = -a; gs[ 0] = -a;  gw[ 0] = w1;\n\tgr[ 1] =  0; gs[ 1] = -a;  gw[ 1] = w2;\n\tgr[ 2] =  a; gs[ 2] = -a;  gw[ 2] = w1;\n\tgr[ 3] = -a; gs[ 3] =  0;  gw[ 3] = w2;\n\tgr[ 4] =  0; gs[ 4] =  0;  gw[ 4] = w3;\n\tgr[ 5] =  a; gs[ 5] =  0;  gw[ 5] = w2;\n\tgr[ 6] = -a; gs[ 6] =  a;  gw[ 6] = w1;\n\tgr[ 7] =  0; gs[ 7] =  a;  gw[ 7] = w2;\n\tgr[ 8] =  a; gs[ 8] =  a;  gw[ 8] = w1;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DQuad8G9::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n//=============================================================================\n//          F E 2 D Q U A D 9 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// shape function at (r,s)\nvoid FE2DQuad9_::shape(double* H, double r, double s)\n{\n\tdouble R[3] = {0.5*r*(r-1.0), 0.5*r*(r+1.0), 1.0 - r*r};\n\tdouble S[3] = {0.5*s*(s-1.0), 0.5*s*(s+1.0), 1.0 - s*s};\n\n\tH[0] = R[0]*S[0];\n\tH[1] = R[1]*S[0];\n\tH[2] = R[1]*S[1];\n\tH[3] = R[0]*S[1];\n\tH[4] = R[2]*S[0];\n\tH[5] = R[1]*S[2];\n\tH[6] = R[2]*S[1];\n\tH[7] = R[0]*S[2];\n\tH[8] = R[2]*S[2];\n}\n\n//-----------------------------------------------------------------------------\n// shape function derivatives at (r,s)\nvoid FE2DQuad9_::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tdouble R[3] = {0.5*r*(r-1.0), 0.5*r*(r+1.0), 1.0 - r*r};\n\tdouble S[3] = {0.5*s*(s-1.0), 0.5*s*(s+1.0), 1.0 - s*s};\n\tdouble DR[3] = {r-0.5, r+0.5, -2.0*r};\n\tdouble DS[3] = {s-0.5, s+0.5, -2.0*s};\n\n\tHr[0] = DR[0]*S[0];\n\tHr[1] = DR[1]*S[0];\n\tHr[2] = DR[1]*S[1];\n\tHr[3] = DR[0]*S[1];\n\tHr[4] = DR[2]*S[0];\n\tHr[5] = DR[1]*S[2];\n\tHr[6] = DR[2]*S[1];\n\tHr[7] = DR[0]*S[2];\n\tHr[8] = DR[2]*S[2];\n\n\tHs[0] = R[0]*DS[0];\n\tHs[1] = R[1]*DS[0];\n\tHs[2] = R[1]*DS[1];\n\tHs[3] = R[0]*DS[1];\n\tHs[4] = R[2]*DS[0];\n\tHs[5] = R[1]*DS[2];\n\tHs[6] = R[2]*DS[1];\n\tHs[7] = R[0]*DS[2];\n\tHs[8] = R[2]*DS[2];\n}\n\n//-----------------------------------------------------------------------------\n//! shape function derivatives at (r,s)\nvoid FE2DQuad9_::shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s)\n{\n\tdouble R[3] = {0.5*r*(r-1.0), 0.5*r*(r+1.0), 1.0 - r*r};\n\tdouble S[3] = {0.5*s*(s-1.0), 0.5*s*(s+1.0), 1.0 - s*s};\n\tdouble DR[3] = {r-0.5, r+0.5, -2.0*r};\n\tdouble DS[3] = {s-0.5, s+0.5, -2.0*s};\n\tdouble DDR[3] = {1.0, 1.0, -2.0};\n\tdouble DDS[3] = {1.0, 1.0, -2.0};\n\n\tGrr[0] = DDR[0]*S[0]; Grs[0] = DR[0]*DS[0]; Gss[0] = R[0]*DDS[0];\n\tGrr[1] = DDR[1]*S[0]; Grs[1] = DR[1]*DS[0]; Gss[1] = R[1]*DDS[0];\n\tGrr[2] = DDR[1]*S[1]; Grs[2] = DR[1]*DS[1]; Gss[2] = R[1]*DDS[1];\n\tGrr[3] = DDR[0]*S[1]; Grs[3] = DR[0]*DS[1]; Gss[3] = R[0]*DDS[1];\n\tGrr[4] = DDR[2]*S[0]; Grs[4] = DR[2]*DS[0]; Gss[4] = R[2]*DDS[0];\n\tGrr[5] = DDR[1]*S[2]; Grs[5] = DR[1]*DS[2]; Gss[5] = R[1]*DDS[2];\n\tGrr[6] = DDR[2]*S[1]; Grs[6] = DR[2]*DS[1]; Gss[6] = R[2]*DDS[1];\n\tGrr[7] = DDR[0]*S[2]; Grs[7] = DR[0]*DS[2]; Gss[7] = R[0]*DDS[2];\n\tGrr[8] = DDR[2]*S[2]; Grs[8] = DR[2]*DS[2]; Gss[8] = R[2]*DDS[2];\t\t\n}\n\n//=============================================================================\n//       F E 2 D Q U A D 9 G 9\n//=============================================================================\n\nFE2DQuad9G9::FE2DQuad9G9() : FE2DQuad9_(NINT, FE2D_QUAD9G9)\n{\n\t// integration point coordinates\n\tconst double a = sqrt(0.6);\n\tconst double w1 = 25.0/81.0;\n\tconst double w2 = 40.0/81.0;\n\tconst double w3 = 64.0/81.0;\n\tgr[ 0] = -a; gs[ 0] = -a;  gw[ 0] = w1;\n\tgr[ 1] =  0; gs[ 1] = -a;  gw[ 1] = w2;\n\tgr[ 2] =  a; gs[ 2] = -a;  gw[ 2] = w1;\n\tgr[ 3] = -a; gs[ 3] =  0;  gw[ 3] = w2;\n\tgr[ 4] =  0; gs[ 4] =  0;  gw[ 4] = w3;\n\tgr[ 5] =  a; gs[ 5] =  0;  gw[ 5] = w2;\n\tgr[ 6] = -a; gs[ 6] =  a;  gw[ 6] = w1;\n\tgr[ 7] =  0; gs[ 7] =  a;  gw[ 7] = w2;\n\tgr[ 8] =  a; gs[ 8] =  a;  gw[ 8] = w1;\n\tinit();\n\n\t// we need Ai to project integration point data to the nodes\n\tmatrix A(NELN,NELN);\n\tm_Ai.resize(NELN,NELN);\n\tA = m_H.transpose()*m_H;\n\tm_Ai = A.inverse();\n}\n\n//-----------------------------------------------------------------------------\nvoid FE2DQuad9G9::project_to_nodes(double* ai, double* ao) const\n{\n\tvector<double> b(NELN);\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tb[i] = 0;\n\t\tfor (int j=0; j<NINT; ++j) b[i] += m_H[j][i]*ai[j];\n\t}\n\n\tfor (int i=0; i<NELN; ++i) \n\t{\n\t\tao[i] = 0;\n\t\tfor (int j=0; j<NELN; ++j) ao[i] += m_Ai[i][j]*b[j];\n\t}\n}\n\n//=============================================================================\n//\n//                  L I N E    E L E M E N T S\n//\n//=============================================================================\n\nFELineElementTraits::FELineElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et) : FEElementTraits(ni, ne, FE_ELEM_EDGE, es, et)\n{\n\tgr.resize(ni);\n\tgw.resize(ni);\n\tGr.resize(ni, ne);\n    Grr.resize(ni, ne);\n}\n\n//-----------------------------------------------------------------------------\nvoid FELineElementTraits::init()\n{\n\tassert(m_nint > 0);\n\tassert(m_neln > 0);\n\n\t// evaluate shape functions\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble N[NE];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tshape(N, gr[n]);\n\t\tfor (int i=0; i<m_neln; ++i) m_H[n][i] = N[i];\n\t}\n\t\n\t// evaluate shape function derivatives\n\tdouble Nr[NE];\n\tfor (int n=0; n<m_nint; ++n)\n\t{\n\t\tshape_deriv(Nr, gr[n]);\n\t\tfor (int i=0; i<m_neln; ++i)\n\t\t{\n\t\t\tGr[n][i] = Nr[i];\n\t\t}\n\t}\n}\n\n//=============================================================================\n//                         FELine2_\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FELine2_::shape(double* H, double r)\n{\n\tH[0] = 0.5*(1.0 - r);\n\tH[1] = 0.5*(1.0 + r);\n}\n\n//-----------------------------------------------------------------------------\nvoid FELine2_::shape_deriv(double* Hr, double r)\n{\n\tHr[0] = -0.5;\n\tHr[1] =  0.5;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELine2_::shape_deriv2(double* Hrr, double r)\n{\n\tHrr[0] = 0;\n\tHrr[1] = 0;\n}\n\n//=============================================================================\n//                          FELine2G1 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFELine2G1::FELine2G1() : FELine2_(NINT, FE_LINE2G1)\n{\n\tgr[0] = 0.0; gw[0] = 2.0;\n\tinit(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FELine2G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n}\n\n//=============================================================================\n//                          FELine2NI \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFELine2NI::FELine2NI() : FELine2_(NINT, FE_LINE2NI)\n{\n\tgr[0] = -1; gr[1] = 1;\n\tgw[0] = gw[1] = 1.0;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FELine2NI::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[1];\n}\n\n//=============================================================================\n//\n//                  B E A M    E L E M E N T S\n//\n//=============================================================================\n\nFEBeamElementTraits::FEBeamElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et) : FEElementTraits(ni, ne, FE_ELEM_EDGE, es, et)\n{\n\tgr.resize(ni);\n\tgw.resize(ni);\n\tGr.resize(ni, ne);\n\tGrr.resize(ni, ne);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeamElementTraits::init()\n{\n\tassert(m_nint > 0);\n\tassert(m_neln > 0);\n\n\t// evaluate shape functions\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble N[NE];\n\tfor (int n = 0; n < m_nint; ++n)\n\t{\n\t\tshape(N, gr[n]);\n\t\tfor (int i = 0; i < m_neln; ++i) m_H[n][i] = N[i];\n\t}\n\n\t// evaluate shape function derivatives\n\tdouble Nr[NE];\n\tfor (int n = 0; n < m_nint; ++n)\n\t{\n\t\tshape_deriv(Nr, gr[n]);\n\t\tfor (int i = 0; i < m_neln; ++i)\n\t\t{\n\t\t\tGr[n][i] = Nr[i];\n\t\t}\n\t}\n}\n\n//=============================================================================\n//                         FEBeam2_\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEBeam2_::shape(double* H, double r)\n{\n\tH[0] = 0.5 * (1.0 - r);\n\tH[1] = 0.5 * (1.0 + r);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam2_::shape_deriv(double* Hr, double r)\n{\n\tHr[0] = -0.5;\n\tHr[1] = 0.5;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam2_::shape_deriv2(double* Hrr, double r)\n{\n\tHrr[0] = 0;\n\tHrr[1] = 0;\n}\n\n//=============================================================================\n//                          FEBeam2G1 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEBeam2G1::FEBeam2G1() : FEBeam2_(NINT, FE_BEAM2G1)\n{\n\tgr[0] = 0.0; gw[0] = 2.0;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam2G1::project_to_nodes(double* ai, double* ao) const\n{\n\tao[0] = ai[0];\n\tao[1] = ai[0];\n}\n\n//=============================================================================\n//                          FEBeam2G2 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEBeam2G2::FEBeam2G2() : FEBeam2_(NINT, FE_BEAM2G2)\n{\n\tconst double a = 1.0 / sqrt(3.0);\n\tgr[0] = -a; gw[0] = 1.0;\n\tgr[1] =  a; gw[1] = 1.0;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam2G2::project_to_nodes(double* ai, double* ao) const\n{\n\t// TODO: implement better!\n\tao[0] = ai[0];\n\tao[1] = ai[1];\n}\n\n\n//=============================================================================\n//                         FEBeam3_\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEBeam3_::shape(double* H, double r)\n{\n\tH[0] = 0.5 * r * (r - 1.0);\n\tH[1] = 0.5 * r * (r + 1.0);\n\tH[2] = 1.0 - r*r;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam3_::shape_deriv(double* Hr, double r)\n{\n\tHr[0] = 0.5 * (2.0*r - 1.0);\n\tHr[1] = 0.5 * (2.0*r + 1.0);\n\tHr[2] = -2.0*r;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam3_::shape_deriv2(double* Hrr, double r)\n{\n\tHrr[0] =  1.0;\n\tHrr[1] =  1.0;\n\tHrr[2] = -2.0;\n}\n\n//=============================================================================\n//                          FEBeam3G2 \n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEBeam3G2::FEBeam3G2() : FEBeam3_(NINT, FE_BEAM3G2)\n{\n\tconst double a = 1.0/sqrt(3.0);\n\tgr[0] = -a; gw[0] = 1.0;\n\tgr[1] =  a; gw[1] = 1.0;\n\tinit();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEBeam3G2::project_to_nodes(double* ai, double* ao) const\n{\n\t// TODO: implement this better\n\tassert(false);\n\tao[0] = ai[0];\n\tao[1] = ai[1];\n\tao[2] = 0.5*(ai[0] + ai[1]);\n}\n"
  },
  {
    "path": "FECore/FEElementTraits.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"matrix.h\"\n#include \"vec3d.h\"\n#include \"mat3d.h\"\n#include \"fecore_enum.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n// Forward declaration of the FEElement class\nclass FEElement;\nclass FESolidElementShape;\nclass FESurfaceElementShape;\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for all element trait's classes\n\nclass FECORE_API FEElementTraits\n{\npublic:\n\t//! constructor\n\tFEElementTraits(int ni, int ne, FE_Element_Class c, FE_Element_Shape s, FE_Element_Type t);\n\n\t//! destructor\n\tvirtual ~FEElementTraits(){}\n\n\t//! return the element class\n\tFE_Element_Class Class() const { return m_spec.eclass; }\n\n\t//! return the element shape\n\tFE_Element_Shape Shape() const { return m_spec.eshape; }\n\n\t//! return the element type\n\tFE_Element_Type Type() const { return m_spec.etype; }\n\n\t// project integration point data to nodes\n\tvirtual void project_to_nodes(double* ai, double* ao) const {}\n\tvirtual void project_to_nodes(vec3d*  ai, vec3d*  ao) const;\n\tvirtual void project_to_nodes(mat3ds* ai, mat3ds* ao) const;\n\tvirtual void project_to_nodes(mat3d*  ai, mat3d*  ao) const;\n\n\tvirtual int ShapeFunctions(int order) { return m_neln; }\n\n\tint Faces() const { return m_faces; }\n\npublic:\n\tint m_nint;\t//!< number of integration points\n\tint\tm_neln;\t//!< number of element nodes\n\n\tmatrix m_H;\t//!< shape function values at gausspoints.\n\t\t\t\t//!< The first index refers to the gauss-point,\n\t\t\t\t//!< the second index to the shape function\n\n\tstd::vector<matrix> m_Hp;\t//!< shape function values at gausspoints.\n\t\t\t\t\t\t\t//!< The first index refers to the gauss-point,\n\t\t\t\t\t\t\t//!< the second index to the shape function\n\n\tFE_Element_Spec\tm_spec;\t//!< element specs\n\nprotected:\n\t// number of faces of element\n\tint\tm_faces;\n\n\t//! function to allocate storage for integration point data\n\tvirtual void init() = 0;\n};\n\n//=============================================================================\n//      S O L I D   E L E M E N T \n//\n// This section defines a set of solid element formulation used in 3D finite\n// element models.\n//=============================================================================\n\n//=============================================================================\n//! This class defines the specific traits for solid elements and serves as\n//! a base class for specific solid element formulations\n//\nclass FECORE_API FESolidElementTraits : public FEElementTraits\n{\npublic:\n\t//! constructor\n\tFESolidElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et);\n\n\t//! initialize element traits data\n\tvoid init() override;\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n\n\tint ShapeFunctions(int order) override;\n\npublic:\n\t// gauss-point coordinates and weights\n\tstd::vector<double> gr;\n\tstd::vector<double> gs;\n\tstd::vector<double> gt;\n\tstd::vector<double> gw;\n\n\t// element shape class\n\tFESolidElementShape*\t\t\t\tm_shape;\n\tstd::vector<FESolidElementShape*>\tm_shapeP; // shape classes for different order (some orders can by null)\n\n\t// local derivatives of shape functions at gauss points\n\tmatrix m_Gr, m_Gs, m_Gt;\n\tstd::vector<matrix>\tm_Gr_p;\n\tstd::vector<matrix>\tm_Gs_p;\n\tstd::vector<matrix>\tm_Gt_p;\n\n\t// local second derivatives of shape functions at gauss points\n\tmatrix Grr, Gsr, Gtr, Grs, Gss, Gts, Grt, Gst, Gtt;\n};\n\n//=============================================================================\nclass FECORE_API FESRISolidElementTraits\n{\npublic:\n\tFESRISolidElementTraits() : m_pTRI(0) {}\n\t~FESRISolidElementTraits() { if (m_pTRI) delete m_pTRI; }\n\tFESolidElementTraits*\tm_pTRI;\n};\n\n//=============================================================================\n//! Base class for 8-node hexahedral elements\nclass FEHex8_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 8 };\n\n\t//! initialize element traits data\n\tvoid init();\n\npublic:\n\tFEHex8_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_HEX8, et) {}\n};\n\n//=============================================================================\n// 8-node hexahedral elements with 8-point gaussian quadrature\n//\nclass FEHex8G8 : public FEHex8_\n{\npublic:\n\tenum { NINT = 8 };\n\npublic:\n\tFEHex8G8();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n// 8-node hexahedral elements with 6-point reduced integration rule\n//\nclass FEHex8RI : public FEHex8_\n{\npublic:\n\tenum { NINT = 6 };\n\npublic:\n\tFEHex8RI();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n// 8-node hexahedral element with uniform deformation gradient\n\nclass FEHex8G1 : public FEHex8_\n{\npublic:\n\tenum { NINT = 1 };\n\npublic:\n\tFEHex8G1();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//! Base class for 4-node linear tetrahedrons\nclass FETet4_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 4 };\n\n\t//! initialize element traits data\n\tvoid init();\n\npublic:\n\tFETet4_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_TET4, et) {}\n};\n\n//=============================================================================\n// single Gauss point integrated tet element\nclass FETet4G1 : public FETet4_\n{\npublic:\n\tenum { NINT = 1};\n\npublic:\n\tFETet4G1();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n// 4-node tetrahedral element using a 4-node Gaussian integration rule\nclass FETet4G4 : public FETet4_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFETet4G4();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n//! Base class for 5-node linear tetrahedrons\nclass FETet5_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 5 };\n\n\t//! initialize element traits data\n\tvoid init();\n\npublic:\n\tFETet5_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_TET5, et) {}\n};\n\n//=============================================================================\n// 5-node tetrahedral element using a 4-node Gaussian integration rule\nclass FETet5G4 : public FETet5_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFETet5G4();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FEPenta6\n//\n//=============================================================================\n\n//=============================================================================\n//! Base class for 6-node pentahedral \"wedge\" elements\nclass FEPenta6_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 6 };\n\n\t//! initialize element traits data\n\tvoid init();\n\npublic:\n\tFEPenta6_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_PENTA6, et){}\n};\n\n//=============================================================================\n// 6-node pentahedral elements with 6-point gaussian quadrature \nclass FEPenta6G6 : public FEPenta6_\n{\npublic:\n\tenum { NINT = 6 };\n\npublic:\n\tFEPenta6G6();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n//\n//   FEPenta15\n//\n//=============================================================================\n\n//=============================================================================\n//! Base class for 15-node quadratic pentahedral \"wedge\" elements\nclass FEPenta15_ : public FESolidElementTraits\n{\npublic:\n    enum { NELN = 15 };\n    \npublic:\n    FEPenta15_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_PENTA15, et){}\n};\n\n//=============================================================================\n// 15-node pentahedral elements with 8-point gaussian quadrature\nclass FEPenta15G8 : public FEPenta15_\n{\npublic:\n    enum { NINT = 8 };\n    \npublic:\n    FEPenta15G8();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n    \n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 15-node pentahedral elements with 21-point gaussian quadrature\nclass FEPenta15G21 : public FEPenta15_\n{\npublic:\n    enum { NINT = 21 };\n    \npublic:\n    FEPenta15G21();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n//\n//   FETet10\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 10-node quadratic tetrahedral elements\nclass FETet10_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 10 };\n\n\t//! initialize element traits data\n\tvoid init();\n\npublic:\n\tFETet10_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_TET10, et){}\n};\n\n//=============================================================================\n// 10-node tetrahedral element using a 4-node Gaussian integration rule\nclass FETet10G1 : public FETet10_\n{\npublic:\n\tenum { NINT = 1 };\n\npublic:\n\tFETet10G1();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix Ai;\n};\n\n//=============================================================================\n// 10-node tetrahedral element using a 4-node Gaussian integration rule\nclass FETet10G4 : public FETet10_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFETet10G4();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix Ai;\n};\n\n//=============================================================================\n// 10-node tetrahedral element using a 8-node Gaussian integration rule\nclass FETet10G8 : public FETet10_\n{\npublic:\n\tenum { NINT = 8 };\n\npublic:\n\tFETet10G8();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix N;\n\tmatrix Ai;\n};\n\n//=============================================================================\nclass FETet10G4RI1 : public FETet10G4, public FESRISolidElementTraits\n{\npublic:\n\tFETet10G4RI1();\n};\n\n//=============================================================================\nclass FETet10G8RI4 : public FETet10G8, public FESRISolidElementTraits\n{\npublic:\n\tFETet10G8RI4();\n};\n\n//=============================================================================\n// 10-node tetrahedral element using a 11-node Gauss-Lobatto integration rule\nclass FETet10GL11 : public FETet10_\n{\npublic:\n\tenum { NINT = 11 };\n\npublic:\n\tFETet10GL11();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FETet15\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 15-node quadratic tetrahedral elements\nclass FETet15_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 15 };\n\npublic:\n\tFETet15_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_TET15, et){}\n};\n\n//=============================================================================\n// 15-node tetrahedral element using a 8-node Gaussian integration rule\nclass FETet15G4 : public FETet15_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFETet15G4();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix N;\n\tmatrix Ai;\n};\n\n//=============================================================================\n// 15-node tetrahedral element using a 8-node Gaussian integration rule\nclass FETet15G8 : public FETet15_\n{\npublic:\n\tenum { NINT = 8 };\n\npublic:\n\tFETet15G8();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix N;\n\tmatrix Ai;\n};\n\n//=============================================================================\n// 15-node tetrahedral element using a 11-point Gaussian integration rule\nclass FETet15G11 : public FETet15_\n{\npublic:\n\tenum { NINT = 11 };\n\npublic:\n\tFETet15G11();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix N;\n\tmatrix Ai;\n};\n\n//=============================================================================\n// 15-node tetrahedral element using a 15-point Gaussian integration rule\nclass FETet15G15 : public FETet15_\n{\npublic:\n\tenum { NINT = 15 };\n\npublic:\n\tFETet15G15();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix N;\n\tmatrix Ai;\n};\n\n//=============================================================================\nclass FETet15G15RI4 : public FETet15G15, public FESRISolidElementTraits\n{\npublic:\n\tFETet15G15RI4();\n};\n\n//=============================================================================\n//\n//   FETet20\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 20-node cubic tetrahedral elements\nclass FETet20_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 20 };\n\npublic:\n\tFETet20_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_TET20, et){}\n};\n\n//=============================================================================\n// 20-node tetrahedral element using a 15-point Gaussian integration rule\nclass FETet20G15 : public FETet20_\n{\npublic:\n\tenum { NINT = 15 };\n\npublic:\n\tFETet20G15();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix N;\n\tmatrix Ai;\n};\n\n//=============================================================================\n//\n//   FEHex20\n//   \n//=============================================================================\n\n\n//=============================================================================\n//! Base class for 20-node quadratic hexahedral element\nclass FEHex20_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 20 };\n\npublic:\n\tFEHex20_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_HEX20, et){}\n\n\t//! initialize element traits data\n\tvoid init();\n\n\tint Nodes(int order);\n};\n\n//=============================================================================\n// 20-node hexahedral element using a 2x2x2 Gaussian integration rule\nclass FEHex20G8 : public FEHex20_\n{\npublic:\n    enum { NINT = 8 };\n    \npublic:\n    FEHex20G8();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n    \n    matrix Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix MT;\n};\n\n//=============================================================================\n// 20-node hexahedral element using a 3x3x3 Gaussian integration rule\nclass FEHex20G27 : public FEHex20_\n{\npublic:\n\tenum { NINT = 27 };\n\npublic:\n\tFEHex20G27();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n};\n\n//=============================================================================\n//\n//   FEHex27\n//   \n//=============================================================================\n\n\n//=============================================================================\n//! Base class for 27-node quadratic hexahedral element\nclass FEHex27_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 27 };\n\npublic:\n\tFEHex27_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_HEX27, et){}\n    \n\t//! initialize element traits data\n\tvoid init();\n\nprotected:\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n};\n\n//=============================================================================\n// 27-node hexahedral element using a 3x3x3 Gaussian integration rule\nclass FEHex27G27 : public FEHex27_\n{\npublic:\n\tenum { NINT = 27 };\n\npublic:\n\tFEHex27G27();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n// \n// FEPyra5\n//\n//=============================================================================\n\n//=============================================================================\n//! Base class for 5-node pyramid element\nclass FEPyra5_ : public FESolidElementTraits\n{\npublic:\n\tenum { NELN = 5 };\n\npublic:\n\tFEPyra5_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_PYRA5, et){}\n};\n\n//=============================================================================\n// 5-node pyramid element using a 2x2x2 Gaussian integration rule\nclass FEPyra5G8: public FEPyra5_\n{\npublic:\n\tenum { NINT = 8 };\n\npublic:\n\tFEPyra5G8();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//\n// FEPyra13\n//\n//=============================================================================\n\n//=============================================================================\n//! Base class for 13-node pyramid element\nclass FEPyra13_ : public FESolidElementTraits\n{\npublic:\n    enum { NELN = 13 };\n    \npublic:\n    FEPyra13_(int ni, FE_Element_Type et) : FESolidElementTraits(ni, NELN, ET_PYRA13, et){}\n};\n\n//=============================================================================\n// 13-node pyramid element using a 2x2x2 Gaussian integration rule\nclass FEPyra13G8: public FEPyra13_\n{\npublic:\n    enum { NINT = 8 };\n    \npublic:\n    FEPyra13G8();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    matrix    m_Ai;\n};\n\n//=============================================================================\n//    S U R F A C E   E L E M E N T S\n//\n// This section defines a set of surface element formulations for use in 3D\n// finite element models. For specific, these elements are used to define\n// the surface of 3D volume models.\n//=============================================================================\n\n//=============================================================================\n// This class defines the traits for surface elements and serves as a\n// base class for the specific surface element formulations.\nclass FECORE_API FESurfaceElementTraits : public FEElementTraits\n{\npublic:\n\tFESurfaceElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et);\n\n\t// initialization\n\tvoid init();\n\n\t// shape functions at (r,s)\n\tvoid shape_fnc(double* H, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n\n\t// shape functions at (r,s)\n\tvoid shape_fnc(int order, double* H, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv(int order, double* Gr, double* Gs, double r, double s);\n\npublic:\n\t// gauss-point coordinates and weights\n\tstd::vector<double> gr;\n\tstd::vector<double> gs;\n\tstd::vector<double> gw;\n\n\t// element shape class\n\tFESurfaceElementShape*\t\t\t\tm_shape;\n\tstd::vector<FESurfaceElementShape*>\tm_shapeP; // shape classes for different order (some orders can by null)\n\n\t// local derivatives of shape functions at gauss points\n\tmatrix Gr, Gs;\n\n\t// local derivatives of shape functions at gauss points, for different interpolation order\n\tstd::vector<matrix>\tGr_p;\n\tstd::vector<matrix>\tGs_p;\n    \n    // parametric coordinates of element center\n    double cr;\n    double cs;\n};\n\n//=============================================================================\n//\n//   FEQuad4\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 4-node bilinear quadrilaterals\n//\nclass FEQuad4_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 4 };\n\n\tvoid init() override;\n\npublic:\n\t//! constructor\n\tFEQuad4_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_QUAD4, et){}\n};\n\n//=============================================================================\n// 4-node quadrilateral elements with 4-point gaussian quadrature \nclass FEQuad4G4 : public FEQuad4_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFEQuad4G4();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n// 16-node quadrilateral elements with 16-point gaussian quadrature \nclass FEQuad4G16 : public FEQuad4_\n{\npublic:\n\tenum { NINT = 16 };\n\npublic:\n\tFEQuad4G16();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Ai;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n// 4-node quadrilateral elements with nodal quadrature \nclass FEQuad4NI : public FEQuad4_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\t//! constructor\n\tFEQuad4NI();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FETri3\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for linear triangles\nclass FETri3_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 3 };\n\npublic:\n\t//! constructor\n\tFETri3_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_TRI3, et){}\n\n\t// initialization \n\tvoid init() override;\n};\n\n//=============================================================================\n//!  3-node triangular element with 1-point gaussian quadrature\nclass FETri3G1 : public FETri3_\n{\npublic:\n\tenum { NINT = 1 };\n\npublic:\n\t//! constructor\n\tFETri3G1();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//!  3-node triangular element with 3-point gaussian quadrature\nclass FETri3G3 : public FETri3_\n{\npublic:\n\tenum { NINT = 3 };\n\npublic:\n\t//! constructor\n\tFETri3G3();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n//!  3-node triangular element with 7-point gaussian quadrature\nclass FETri3G7 : public FETri3_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t//! constructor\n\tFETri3G7();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//  3-node triangular element with nodal quadrature\nclass FETri3NI : public FETri3_\n{\npublic:\n\tenum { NINT = 3 };\n\npublic:\n\t// constructor\n\tFETri3NI();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FETri6\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 6-noded quadratic triangles\nclass FETri6_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 6 };\n\npublic:\n\tFETri6_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_TRI6, et){}\n\n\t// initialization \n\tvoid init() override;\n};\n\n//=============================================================================\n//  6-node triangular element with 3-point gaussian quadrature\n//\nclass FETri6G3 : public FETri6_\n{\npublic:\n\tenum { NINT = 3 };\n\npublic:\n\t// constructor\n\tFETri6G3();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//  6-node triangular element with 4-point gaussian quadrature\n//\nclass FETri6G4 : public FETri6_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\t// constructor\n\tFETri6G4();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//  6-node triangular element with 7-point gaussian quadrature\n//\nclass FETri6G7 : public FETri6_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t// constructor\n\tFETri6G7();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//  6-node triangular element with 7-point Gauss-Lobatto quadrature\n//\nclass FETri6GL7 : public FETri6_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t// constructor\n\tFETri6GL7();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//  6-node triangular element with 6-point nodal quadrature\n//\nclass FETri6NI : public FETri6_\n{\npublic:\n\tenum { NINT = 6 };\n\npublic:\n\t// constructor\n\tFETri6NI();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FETri6m\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 6-noded quadratic triangles with modified shape functions\n/*\nclass FETri6m_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 6 };\n\npublic:\n\tFETri6m_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_TRI6, et){}\n\n\t// shape function at (r,s)\n\tvoid shape(double* H, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n};\n\n\n//=============================================================================\n// 6-node triangular element (with modified shape functions)\n// with 7-point gaussian quadrature\n//\nclass FETri6mG7 : public FETri6m_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t// constructor\n\tFETri6mG7();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n*/\n\n//=============================================================================\n//\n//   FETri7\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 7-noded quadratic triangles\nclass FETri7_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 7 };\n\npublic:\n\tFETri7_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_TRI7, et){}\n\n\t// initialization\n\tvoid init() override;\n};\n\n//=============================================================================\n//  7-node triangular element with 3-point gaussian quadrature\n//\nclass FETri7G3 : public FETri7_\n{\npublic:\n\tenum { NINT = 3 };\n\npublic:\n\t// constructor\n\tFETri7G3();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tAi;\n};\n\n//=============================================================================\n//  7-node triangular element with 4-point gaussian quadrature\n//\nclass FETri7G4 : public FETri7_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\t// constructor\n\tFETri7G4();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tAi;\n};\n\n\n//=============================================================================\n//  7-node triangular element with 7-point gaussian quadrature\n//\nclass FETri7G7 : public FETri7_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t// constructor\n\tFETri7G7();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//  7-node triangular element with 7-point Gauss-Lobatto quadrature\n//\nclass FETri7GL7 : public FETri7_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t// constructor\n\tFETri7GL7();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tAi;\n};\n\n//=============================================================================\n//\n//   FETri10\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 10-noded cubic triangles\nclass FETri10_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 10 };\n\npublic:\n\tFETri10_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_TRI10, et){}\n\n\t// initialization\n\tvoid init() override;\n};\n\n//=============================================================================\n//  10-node triangular element with 7-point gaussian quadrature\n//\nclass FETri10G7 : public FETri10_\n{\npublic:\n\tenum { NINT = 7 };\n\npublic:\n\t// constructor\n\tFETri10G7();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n\n//=============================================================================\n//  10-node triangular element with 12-point gaussian quadrature\n//\nclass FETri10G12 : public FETri10_\n{\npublic:\n\tenum { NINT = 12 };\n\npublic:\n\t// constructor\n\tFETri10G12();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//\n//   FEQuad8\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 8-node quadratic quadrilaterals\n//\nclass FEQuad8_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 8 };\n\npublic:\n\tFEQuad8_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_QUAD8, et) {}\n\n\t// initialization\n\tvoid init() override;\n};\n\n//=============================================================================\n//! class implementing 8-node quad quadrilateral with 9 integration points\n//\nclass FEQuad8G9 : public FEQuad8_\n{\npublic:\n\tenum { NINT = 9 };\n\n\t// constructor\n\tFEQuad8G9();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//! class implementing 8-node quad quadrilateral with 8 nodal integration points\n//\nclass FEQuad8NI : public FEQuad8_\n{\npublic:\n    enum { NINT = 8 };\n    \n    // constructor\n    FEQuad8NI();\n    \n    // project integration point data to nodes\n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprivate:\n    matrix\tm_Ai;\n};\n\n//=============================================================================\n//\n//   FEQuad9\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 9-node quadratic quadrilaterals\n//\nclass FEQuad9_ : public FESurfaceElementTraits\n{\npublic:\n\tenum { NELN = 9 };\n\npublic:\n\tFEQuad9_(int ni, FE_Element_Type et) : FESurfaceElementTraits(ni, NELN, ET_QUAD9, et) {}\n\n\t// initialization\n\tvoid init() override;\n};\n\n//=============================================================================\n//! class implementing 9-node quad quadrilateral with 9 integration points\n//\nclass FEQuad9G9 : public FEQuad9_\n{\npublic:\n\tenum { NINT = 9 };\n\n\t// constructor\n\tFEQuad9G9();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//! class implementing 9-node quad quadrilateral with 9 nodal integration points\n//\nclass FEQuad9NI : public FEQuad9_\n{\npublic:\n    enum { NINT = 9 };\n    \n    // constructor\n    FEQuad9NI();\n    \n    // project integration point data to nodes\n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprivate:\n    matrix\tm_Ai;\n};\n\n//=============================================================================\n//     S H E L L   E L E M E N T S\n//\n// This section defines several shell formulations for use in 3D finite element\n// analysis. \n//=============================================================================\n\n//=============================================================================\n// This class defines the specific for shell elements and serves as a base class\n// for specific shell formulations\n//\nclass FEShellElementTraits : public FEElementTraits\n{\npublic:\n\tFEShellElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et);\n\n    void init();\n    \n    //! values of shape functions\n    virtual void shape_fnc(double* H, double r, double s) = 0;\n    \n    //! values of shape function derivatives\n    virtual void shape_deriv(double* Hr, double* Hs, double r, double s) = 0;\n    \n\tusing FEElementTraits::project_to_nodes;\n\tvirtual void project_to_nodes(mat3ds* ai, mat3ds* ao) const;\n\npublic:\n\tint m_nvln; //!< number of element nodes including virtual nodes (e.g., in shell elements)\n\n\t// gauss-point coordinates and weights\n\tstd::vector<double> gr;\n\tstd::vector<double> gs;\n\tstd::vector<double> gt;\n\tstd::vector<double> gw;\n\n\t// local derivatives of shape functions at gauss points\n\tmatrix Hr, Hs;\n    \n};\n\n//=============================================================================\n// 4-node quadrilateral elements\n//\nclass FEShellQuad4_ : public FEShellElementTraits\n{\npublic:\n    enum { NELN = 4 };\n    \npublic:\n    FEShellQuad4_(int ni, FE_Element_Type et) : FEShellElementTraits(ni, NELN, ET_QUAD4, et) {}\n    \npublic:\n    //! values of shape functions\n    void shape_fnc(double* H, double r, double s);\n    \n    //! values of shape function derivatives\n    void shape_deriv(double* Hr, double* Hs, double r, double s);\n    \n};\n\n\n//=============================================================================\n// 4-node quadrilateral elements with 4 point (membrane) gaussian quadrature\nclass FEShellQuad4G4 : public FEShellQuad4_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFEShellQuad4G4();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n};\n\n//=============================================================================\n// 4-node quadrilateral elements with 4*2-point gaussian quadrature\n//\nclass FEShellQuad4G8 : public FEShellQuad4_\n{\npublic:\n    enum { NINT = 8 };\n    \npublic:\n    FEShellQuad4G8();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 4-node quadrilateral elements with 4*3-point gaussian quadrature\n//\nclass FEShellQuad4G12 : public FEShellQuad4_\n{\npublic:\n    enum { NINT = 12 };\n    \npublic:\n    FEShellQuad4G12();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 3-node triangular elements\n//\nclass FEShellTri3_ : public FEShellElementTraits\n{\npublic:\n    enum { NELN = 3 };\n    \npublic:\n    FEShellTri3_(int ni, FE_Element_Type et) : FEShellElementTraits(ni, NELN, ET_TRI3, et) {}\n    \npublic:\n    //! values of shape functions\n    void shape_fnc(double* H, double r, double s);\n    \n    //! values of shape function derivatives\n    void shape_deriv(double* Hr, double* Hs, double r, double s);\n    \n};\n\n//=============================================================================\n// 3-node triangular elements with 1-point (membrane) gaussian quadrature\n//\nclass FEShellTri3G3 : public FEShellTri3_\n{\npublic:\n\tenum { NINT = 3 };\n\npublic:\n\tFEShellTri3G3();\n\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n};\n\n//=============================================================================\n// 3-node triangular elements with 3*2-point gaussian quadrature\n//\nclass FEShellTri3G6 : public FEShellTri3_\n{\npublic:\n    enum { NINT = 6 };\n    \npublic:\n    FEShellTri3G6();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 3-node triangular elements with 3*3-point gaussian quadrature\n//\nclass FEShellTri3G9 : public FEShellTri3_\n{\npublic:\n    enum { NINT = 9 };\n    \npublic:\n    FEShellTri3G9();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 8-node quadrilateral elements\n//\nclass FEShellQuad8_ : public FEShellElementTraits\n{\npublic:\n    enum { NELN = 8 };\n    \npublic:\n    FEShellQuad8_(int ni, FE_Element_Type et) : FEShellElementTraits(ni, NELN, ET_QUAD8, et) {}\n    \npublic:\n    //! values of shape functions\n    void shape_fnc(double* H, double r, double s);\n    \n    //! values of shape function derivatives\n    void shape_deriv(double* Hr, double* Hs, double r, double s);\n    \n};\n\n//=============================================================================\n// 8-node quadrilateral elements with 9*2-point gaussian quadrature\n//\nclass FEShellQuad8G18 : public FEShellQuad8_\n{\npublic:\n    enum { NINT = 18 };\n    \npublic:\n    FEShellQuad8G18();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 8-node quadrilateral elements with 9*3-point gaussian quadrature\n//\nclass FEShellQuad8G27 : public FEShellQuad8_\n{\npublic:\n    enum { NINT = 27 };\n    \npublic:\n    FEShellQuad8G27();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 6-node triangular elements\n//\nclass FEShellTri6_ : public FEShellElementTraits\n{\npublic:\n    enum { NELN = 6 };\n    \npublic:\n    FEShellTri6_(int ni, FE_Element_Type et) : FEShellElementTraits(ni, NELN, ET_TRI6, et) {}\n    \npublic:\n    //! values of shape functions\n    void shape_fnc(double* H, double r, double s);\n    \n    //! values of shape function derivatives\n    void shape_deriv(double* Hr, double* Hs, double r, double s);\n    \n};\n\n//=============================================================================\n// 6-node triangular elements with 7*2-point gaussian quadrature\n//\nclass FEShellTri6G14 : public FEShellTri6_\n{\npublic:\n    enum { NINT = 14 };\n    \npublic:\n    FEShellTri6G14();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n// 6-node triangular elements with 7*3-point gaussian quadrature\n//\nclass FEShellTri6G21 : public FEShellTri6_\n{\npublic:\n    enum { NINT = 21 };\n    \npublic:\n    FEShellTri6G21();\n    \n    void project_to_nodes(double* ai, double* ao) const override;\n    \nprotected:\n    // use these integration points to project to nodes\n    static int ni[NELN];\n\n    matrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data\n    matrix m_MT;\n};\n\n//=============================================================================\n//          T R U S S    E L E M E N T S\n//\n// This section defines truss elements for 3D analysis\n//=============================================================================\n\n//=============================================================================\nclass FETrussElementTraits : public FEElementTraits\n{\npublic:\n\tenum { NINT = 2 };\n\tenum { NELN = 2 };\n\npublic:\n\tFETrussElementTraits();\n\n\tvoid init();\n\n\t//! shape function at (r)\n\tvoid shape(double* H, double r);\n\npublic:\n\t// gauss-point coordinates and weights\n\tstd::vector<double> gr;\n\tstd::vector<double> gw;\n};\n\n//=============================================================================\n//          D I S C R E T E    E L E M E N T S\n//\n// This section defines discrete elements for 3D analysis\n//=============================================================================\n\n//=============================================================================\nclass FEDiscreteElementTraits : public FEElementTraits\n{\npublic:\n\tenum { NINT = 1 };\n\tenum { NELN = 2 };\n\npublic:\n\tFEDiscreteElementTraits() : FEElementTraits(NINT, NELN, FE_ELEM_DISCRETE, ET_DISCRETE, FE_DISCRETE) { init(); }\n\n\tvoid init() {}\n};\n\n//=============================================================================\n//                      2 D   E L E M E N T S\n//\n// This section defines a set of solid element formulation used in 3D finite\n// element models.\n//=============================================================================\n\n//=============================================================================\n// This class defines the traits for 2D elements and serves as a\n// base class for the specific 2D element formulations.\nclass FE2DElementTraits : public FEElementTraits\n{\npublic:\n\tFE2DElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et);\n\n\t// initialization\n\tvoid init();\n\n\t// shape functions at (r,s)\n\tvirtual void shape(double* H, double r, double s) = 0;\n\n\t// shape function derivatives at (r,s)\n\tvirtual void shape_deriv(double* Gr, double* Gs, double r, double s) = 0;\n\n\t// shape function derivatives at (r,s)\n\tvirtual void shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s) = 0;\n\npublic:\n\t// gauss-point coordinates and weights\n\tstd::vector<double> gr;\n\tstd::vector<double> gs;\n\tstd::vector<double> gw;\n\n\t// local derivatives of shape functions at gauss points\n\tmatrix Gr, Gs;\n    \n    // local second derivatives of shape functions at gauss points\n    matrix Grr, Gsr, Grs, Gss;\n};\n\n//=============================================================================\n//\n//   FE2DTri3\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for linear triangles\nclass FE2DTri3_ : public FE2DElementTraits\n{\npublic:\n\tenum { NELN = 3 };\n\npublic:\n\t//! constructor\n\tFE2DTri3_(int ni, FE_Element_Type et) : FE2DElementTraits(ni, NELN, ET_TRI3, et){}\n\n\t//! shape function at (r,s)\n\tvoid shape(double* H, double r, double s);\n\n\t//! shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t//! shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n};\n\n//=============================================================================\n//!  3-node triangular element with 1-point gaussian quadrature\nclass FE2DTri3G1 : public FE2DTri3_\n{\npublic:\n\tenum { NINT = 1 };\n\npublic:\n\t//! constructor\n\tFE2DTri3G1();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FE2DTri6\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 6-noded quadratic triangles\nclass FE2DTri6_ : public FE2DElementTraits\n{\npublic:\n\tenum { NELN = 6 };\n\npublic:\n\tFE2DTri6_(int ni, FE_Element_Type et) : FE2DElementTraits(ni, NELN, ET_TRI6, et){}\n\n\t// shape function at (r,s)\n\tvoid shape(double* H, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n};\n\n//=============================================================================\n//  6-node triangular element with 3-point gaussian quadrature\n//\nclass FE2DTri6G3 : public FE2DTri6_\n{\npublic:\n\tenum { NINT = 3 };\n\npublic:\n\t// constructor\n\tFE2DTri6G3();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FE2DQuad4\n//   \n//=============================================================================\n\n//=============================================================================\n// Base class for 4-node bilinear quadrilaterals\n//\nclass FE2DQuad4_ : public FE2DElementTraits\n{\npublic:\n\tenum { NELN = 4 };\n\npublic:\n\t//! constructor\n\tFE2DQuad4_(int ni, FE_Element_Type et) : FE2DElementTraits(ni, NELN, ET_QUAD4, et){}\n\n\t//! shape functions at (r,s)\n\tvoid shape(double* H, double r, double s);\n\n\t//! shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t//! shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n};\n\n//=============================================================================\n// 4-node quadrilateral elements with 4-point gaussian quadrature \nclass FE2DQuad4G4 : public FE2DQuad4_\n{\npublic:\n\tenum { NINT = 4 };\n\npublic:\n\tFE2DQuad4G4();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprotected:\n\tmatrix m_Hi;\t//!< inverse of H; useful for projection integr. point data to nodal data \n};\n\n//=============================================================================\n//\n//   FE2DQuad8\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 8-node quadratic quadrilaterals\n//\nclass FE2DQuad8_ : public FE2DElementTraits\n{\npublic:\n\tenum { NELN = 8 };\n\npublic:\n\tFE2DQuad8_(int ni, FE_Element_Type et) : FE2DElementTraits(ni, NELN, ET_QUAD8, et) {}\n\n\t// shape function at (r,s)\n\tvoid shape(double* H, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n};\n\n//=============================================================================\n//! class implementing 8-node quad quadrilateral with 9 integration points\n//\nclass FE2DQuad8G9 : public FE2DQuad8_\n{\npublic:\n\tenum { NINT = 9 };\n\n\t// constructor\n\tFE2DQuad8G9();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//\n//   FE2DQuad9\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for 9-node quadratic quadrilaterals\n//\nclass FE2DQuad9_ : public FE2DElementTraits\n{\npublic:\n\tenum { NELN = 9 };\n\npublic:\n\tFE2DQuad9_(int ni, FE_Element_Type et) : FE2DElementTraits(ni, NELN, ET_QUAD9, et) {}\n\n\t// shape function at (r,s)\n\tvoid shape(double* H, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s);\n\n\t// shape function derivatives at (r,s)\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s);\n};\n\n//=============================================================================\n//! class implementing 9-node quad quadrilateral with 9 integration points\n//\nclass FE2DQuad9G9 : public FE2DQuad9_\n{\npublic:\n\tenum { NINT = 9 };\n\n\t// constructor\n\tFE2DQuad9G9();\n\n\t// project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n\nprivate:\n\tmatrix\tm_Ai;\n};\n\n//=============================================================================\n//                      L I N E   E L E M E N T S\n//\n// This section defines a set of element formulations used to describe edges. \n//=============================================================================\n\n//=============================================================================\nclass FELineElementTraits : public FEElementTraits\n{\npublic:\n\tFELineElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et);\n\n\t// initialization\n\tvoid init();\n\n\t// shape functions at r\n\tvirtual void shape(double* H, double r) = 0;\n\n\t// shape function derivatives at (r)\n\tvirtual void shape_deriv(double* Gr, double r) = 0;\n\n\t// shape function second derivatives at (r)\n\tvirtual void shape_deriv2(double* Grr, double r) = 0;\n\npublic:\n\tstd::vector<double> gr;\t//!< integration point coordinates\n\tstd::vector<double> gw;\t//!< integration point weights\n\n\t// local derivatives of shape functions at gauss points\n\tmatrix Gr;\n    \n    // local second derivatives of shape functions at gauss points\n    matrix Grr;\n};\n\n//=============================================================================\n//\n//   FELine2_\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for two-point lines\nclass FELine2_ : public FELineElementTraits\n{\npublic:\n\tenum { NELN = 2 };\n\npublic:\n\t//! constructor\n\tFELine2_(int ni, FE_Element_Type et) : FELineElementTraits(ni, NELN, ET_LINE2, et){}\n\n\t//! shape function at (r)\n\tvoid shape(double* H, double r);\n\n\t//! shape function derivatives at (r)\n\tvoid shape_deriv(double* Gr, double r);\n\n\t//! shape function derivatives at (r)\n\tvoid shape_deriv2(double* Grr, double r);\n};\n\n//=============================================================================\nclass FELine2G1 : public FELine2_\n{\npublic:\n\tenum { NINT = 1 };\n\npublic:\n\t//! constructor\n\tFELine2G1();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\nclass FELine2NI : public FELine2_\n{\npublic:\n\tenum { NINT = 2 };\n\npublic:\n\t//! constructor\n\tFELine2NI();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//                      B E A M   E L E M E N T S\n//\n// This section defines a set of element formulations used to describe beams.\n//=============================================================================\n\n//=============================================================================\nclass FEBeamElementTraits : public FEElementTraits\n{\npublic:\n\tFEBeamElementTraits(int ni, int ne, FE_Element_Shape es, FE_Element_Type et);\n\n\t// initialization\n\tvoid init();\n\n\t// shape functions at r\n\tvirtual void shape(double* H, double r) = 0;\n\n\t// shape function derivatives at (r)\n\tvirtual void shape_deriv(double* Gr, double r) = 0;\n\n\t// shape function second derivatives at (r)\n\tvirtual void shape_deriv2(double* Grr, double r) = 0;\n\npublic:\n\tstd::vector<double> gr;\t//!< integration point coordinates\n\tstd::vector<double> gw;\t//!< integration point weights\n\n\t// local derivatives of shape functions at gauss points\n\tmatrix Gr;\n\n\t// local second derivatives of shape functions at gauss points\n\tmatrix Grr;\n};\n\n//=============================================================================\n//\n//   FEBeam2_\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for two-point beam\nclass FEBeam2_ : public FEBeamElementTraits\n{\npublic:\n\tenum { NELN = 2 };\n\npublic:\n\t//! constructor\n\tFEBeam2_(int ni, FE_Element_Type et) : FEBeamElementTraits(ni, NELN, ET_LINE2, et) {}\n\n\t//! shape function at (r)\n\tvoid shape(double* H, double r);\n\n\t//! shape function derivatives at (r)\n\tvoid shape_deriv(double* Gr, double r);\n\n\t//! shape function derivatives at (r)\n\tvoid shape_deriv2(double* Grr, double r);\n};\n\n//=============================================================================\nclass FEBeam2G1 : public FEBeam2_\n{\npublic:\n\tenum { NINT = 1 };\n\npublic:\n\t//! constructor\n\tFEBeam2G1();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\nclass FEBeam2G2 : public FEBeam2_\n{\npublic:\n\tenum { NINT = 2 };\n\npublic:\n\t//! constructor\n\tFEBeam2G2();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n\n//=============================================================================\n//\n//   FEBeam3_\n//   \n//=============================================================================\n\n//=============================================================================\n//! Base class for three-point beam\nclass FEBeam3_ : public FEBeamElementTraits\n{\npublic:\n\tenum { NELN = 3 };\n\npublic:\n\t//! constructor\n\tFEBeam3_(int ni, FE_Element_Type et) : FEBeamElementTraits(ni, NELN, ET_LINE3, et) {}\n\n\t//! shape function at (r)\n\tvoid shape(double* H, double r);\n\n\t//! shape function derivatives at (r)\n\tvoid shape_deriv(double* Gr, double r);\n\n\t//! shape function derivatives at (r)\n\tvoid shape_deriv2(double* Grr, double r);\n};\n\n//=============================================================================\nclass FEBeam3G2 : public FEBeam3_\n{\npublic:\n\tenum { NINT = 2 };\n\npublic:\n\t//! constructor\n\tFEBeam3G2();\n\n\t//! project integration point data to nodes\n\tvoid project_to_nodes(double* ai, double* ao) const override;\n};\n"
  },
  {
    "path": "FECore/FEException.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEException.h\"\n#include \"FESolver.h\" // for FENodalDofInfo\n#include <stdarg.h>\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEException::FEException(const char* msg, int level)\n{\n\tif (msg) m_what = msg;\n\tm_level = level;\n}\n\nFEException::~FEException()\n{\n\n}\n\nconst char* FEException::what()\n{\n\treturn m_what.c_str();\n}\n\nvoid FEException::what(const char* msg, ...)\n{\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar sztxt[1024] = { 0 };\n\tva_start(args, msg);\n\tvsnprintf(sztxt, sizeof(sztxt), msg, args);\n\tva_end(args);\n\n\tm_what = sztxt;\n}\n\n//-----------------------------------------------------------------------------\nint FEException::level() const\n{\n\treturn m_level;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement error handling\nZeroDiagonal::ZeroDiagonal(int node, int ndof)\n{\n\twhat(\"Zero diagonal detected. Aborting run.\");\n}\n\n/*\nZeroDiagonal::ZeroDiagonal(vector<int>& l, FEM& fem)\n{\n\t// let's find what dof this equation belonges to\n\tFEMesh& m = fem.GetMesh();\n\n\tint nz = (int) l.size();\n\tint i, j, id;\n\tfor (i=0; i<fem.m_nrb; ++i)\n\t{\n\t\tFERigidBody& rb = fem.m_RB[i];\n\t\tfor (j=0; j<6; ++j)\n\t\t{\n\t\t\tid = rb.m_LM[j];\n\t\t\tif (id < -1) id = -id-2;\n\n\t\t\tfor (int k=0; k<nz; ++k)\n\t\t\t{\n\t\t\t\tint n = l[k];\n\t\t\t\tif (id == n)\n\t\t\t\t{\n\t\t\t\t\tfelog.printf(\"Zero diagonal on row %d.\\nThis dof belongs to rigid body %d (dof %d)\\n\", n, i+1, j+1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvector<EQUATION> EQT(fem.m_neq);\n\tfor (i=0; i<fem.m_neq; ++i)\n\t{\n\t\tEQUATION& q = EQT[i];\n\t\tq.node = -1;\n\t\tq.dof = -1;\n\t}\n\n\tfor (i=0; i<m.Nodes(); ++i)\n\t{\n\t\tFENode& node = m.Node(i);\n\t\tfor (j=0; j<MAX_NDOFS; ++j)\n\t\t{\n\t\t\tid = node.m_ID[j];\n\t\t\tif (id < -1) id = -id-2;\n\t\t\tif (id != -1)\n\t\t\t{\n\t\t\t\tassert(id < fem.m_neq);\n\t\t\t\tEQT[id].node = i;\n\t\t\t\tEQT[id].dof = j;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst int NMAX = 128;\n\tint N = (nz < NMAX? nz : NMAX), n;\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tn = l[i];\n\t\tEQUATION& q = EQT[n];\n\t\tfelog.printf(\"Zero diagonal on row %d. (node %d, dof %d)\\n\", n+1, q.node+1, q.dof+1);\n\t}\n\tif (nz > NMAX) felog.printf(\"(%d out of %d printed)\\n\", NMAX, nz);\n\n\t// print error message\n\tsprintf(m_szerr, \"FATAL ERROR: %d zero(s) found on diagonal.\", l.size());\n\n}\n*/\n//=============================================================================\nbool NegativeJacobian::m_bthrown = false;\nint NegativeJacobian::m_maxout = 0; // output off by default\nint NegativeJacobian::m_count = 0;\n\n//-----------------------------------------------------------------------------\nNegativeJacobian::NegativeJacobian(int iel, int ng, double vol, FEElement* pe)\n{\n\tm_iel = iel;\n\tm_ng = ng;\n\tm_vol = vol;\n\tm_pel = pe;\n\tm_bthrown = true;\n\twhat(\"Negative jacobian was detected at element %d at gauss point %d\\njacobian = %lg\\n\", m_iel, m_ng + 1, m_vol);\n}\n\n//-----------------------------------------------------------------------------\nbool NegativeJacobian::DoOutput()\n{\n\tm_count++; // we do this here because this function is called from critical sections.\n\tbool b = (m_maxout < 0) || (m_count <= m_maxout);\n\treturn b;\n}\n\n//-----------------------------------------------------------------------------\nvoid NegativeJacobian::clearFlag()\n{\n\tm_bthrown = false;\n}\n\n//-----------------------------------------------------------------------------\nbool NegativeJacobian::IsThrown()\n{\n\treturn m_bthrown;\n}\n\nint NegativeJacobian::Count()\n{\n\treturn m_count;\n}\n\nvoid NegativeJacobian::ResetCount()\n{\n\tm_count = 0;\n}\n\n//-----------------------------------------------------------------------------\nNANInResidualDetected::NANInResidualDetected(const FENodalDofInfo& ndi)\n{\n\twhat(\"NAN detected in residual vector at index %d.\\nNode id = %d, dof = %d ('%s')\", ndi.m_eq, ndi.m_node, ndi.m_dof, ndi.szdof);\n}\n\n//-----------------------------------------------------------------------------\nNANInSolutionDetected::NANInSolutionDetected(const FENodalDofInfo& ndi)\n{\n\twhat(\"NAN detected in solution vector at index %d.\\nNode id = %d, dof = %d ('%s')\", ndi.m_eq, ndi.m_node, ndi.m_dof, ndi.szdof);\n}\n\n\n//-----------------------------------------------------------------------------\nFEMultiScaleException::FEMultiScaleException(int eid, int gpt)\n{\n\twhat(\"The RVE problem has failed at element %d, gauss point %d.\\nAborting macro run.\", eid, gpt + 1);\n}\n\n//-----------------------------------------------------------------------------\nConcentrationChangeDetected::ConcentrationChangeDetected(const FENodalDofInfo& ndi)\n{\n    what(\"Sudden concentration change detected.\\n\");\n}\n\n"
  },
  {
    "path": "FECore/FEException.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <string>\n\nclass FEElement;\n\nclass FECORE_API FEException\n{\npublic:\n\tenum {\n\t\tError = 0,\n\t\tWarning = 1\n\t};\n\npublic:\n\t// level = 0, error\n\t// level = 1, warning\n\tFEException(const char* msg = nullptr, int level = 0);\n\tvirtual ~FEException();\n\n\tconst char* what();\n\n\tvoid what(const char* msg, ...);\n\n\tint level() const;\n\nprivate:\n\tstd::string\tm_what;\n\tint\tm_level;\n};\n\nclass FECORE_API NegativeJacobian : public FEException\n{\npublic:\n\tNegativeJacobian(int iel, int ng, double vol, FEElement* pe = 0);\n\n\tint\t\tm_iel;\t// element where the jacobian was negative\n\tint\t\tm_ng;\t// integration point\n\tdouble\tm_vol;\t// volume\n\tFEElement*\tm_pel;\t// pointer to element\n\n\tstatic bool DoOutput();\n\n\tstatic void clearFlag();\n\tstatic bool IsThrown();\n\n\tstatic int Count();\n\tstatic void ResetCount();\n\npublic:\n\tstatic bool m_bthrown;\n\tstatic int\tm_maxout; // max nr of negative jacobians reported (< 0 for unlimited, 0 = off, > 0 sets limit)\n\tstatic int m_count;\n};\n\nclass FECORE_API ZeroDiagonal : public FEException\n{\nprivate:\n\tstruct EQUATION\n\t{\n\t\tint\tnode;\t// node\n\t\tint\tdof;\t// degree of node\n\t};\n\npublic:\n\tZeroDiagonal(int node, int dof);\n};\n\nclass FECORE_API EnergyDiverging : public FEException {\npublic: EnergyDiverging() : FEException(\"Problem diverging uncontrollably.\") {}\n};\n\nclass FECORE_API MaxStiffnessReformations : public FEException {\npublic: MaxStiffnessReformations() : FEException(\"Max nr of reformations reached.\") {}\n};\n\nclass FECORE_API ZeroLinestepSize : public FEException {\npublic: ZeroLinestepSize() : FEException(\"Zero line step size.\") {}\n};\n\nclass FECORE_API ForceConversion : public FEException {\npublic: ForceConversion() : FEException(\"User forced conversion.\\nSolution might not be stable.\", FEException::Warning) {}\n};\n\nclass FECORE_API IterationFailure : public FEException {\npublic: IterationFailure() : FEException(\"User forced iteration failure.\", FEException::Warning) {}\n};\n\nclass FECORE_API MaxResidualError : public FEException {\npublic: MaxResidualError() : FEException(\"Maximum residual exceeded.\", FEException::Warning) {}\n};\n\nstruct FECORE_API FENodalDofInfo;\n\nclass FECORE_API NANInResidualDetected : public FEException {\npublic: \n\tNANInResidualDetected() : FEException(\"NAN detected\") {}\n\tNANInResidualDetected(const FENodalDofInfo& ndi);\n};\n\nclass FECORE_API NANInSolutionDetected : public FEException {\npublic:\n\tNANInSolutionDetected() : FEException(\"NAN detected\") {}\n\tNANInSolutionDetected(const FENodalDofInfo& ndi);\n};\n\nclass FECORE_API FatalError : public FEException{\npublic: FatalError() : FEException(\"Fatal error\") {}\n};\n\nclass FECORE_API DoRunningRestart : public FEException {\npublic: DoRunningRestart() : FEException(\"Running restart requested\", FEException::Warning) {}\n};\n\nclass FECORE_API FEMultiScaleException : public FEException\n{\npublic:\n\tFEMultiScaleException(int eid, int gpt);\n};\n\nclass FECORE_API LinearSolverFailed : public FEException {\npublic: LinearSolverFailed() : FEException(\"Linear solver failed to find solution. Aborting run.\") {}\n};\n\nclass FECORE_API FactorizationError : public FEException{\npublic: FactorizationError() : FEException(\"Fatal error in factorization of stiffness matrix. Aborting run.\") {}\n};\n\nclass FECORE_API NegativeJacobianDetected : public FEException {\npublic: NegativeJacobianDetected() {\n\t\tint n = NegativeJacobian::Count();\n\t\tif (n > 1)\n\t\t\twhat(\"%d negative jacobians detected.\", n);\n\t\telse\n\t\t\twhat(\"Negative jacobian detected.\");\n\t\tNegativeJacobian::ResetCount();\n\t}\n};\n\nclass FECORE_API ConcentrationChangeDetected : public FEException {\npublic:\n    ConcentrationChangeDetected() : FEException(\"Concentration change detected\") {}\n    ConcentrationChangeDetected(const FENodalDofInfo& ndi);\n};\n\n"
  },
  {
    "path": "FECore/FEFaceList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFaceList.h\"\n#include \"FEMesh.h\"\n#include \"FEDomain.h\"\n#include \"FEElemElemList.h\"\n#include \"FEElementList.h\"\n#include \"FEEdgeList.h\"\n\nbool FEFaceList::FACE::IsEqual(int* n) const\n{\n\tif (ntype == 3)\n\t{\n\t\tif ((node[0] != n[0]) && (node[0] != n[1]) && (node[0] != n[2])) return false;\n\t\tif ((node[1] != n[0]) && (node[1] != n[1]) && (node[1] != n[2])) return false;\n\t\tif ((node[2] != n[0]) && (node[2] != n[1]) && (node[2] != n[2])) return false;\n\t}\n\telse if (ntype == 4)\n\t{\n\t\tif ((node[0] != n[0]) && (node[0] != n[1]) && (node[0] != n[2]) && (node[0] != n[3])) return false;\n\t\tif ((node[1] != n[0]) && (node[1] != n[1]) && (node[1] != n[2]) && (node[1] != n[3])) return false;\n\t\tif ((node[2] != n[0]) && (node[2] != n[1]) && (node[2] != n[2]) && (node[2] != n[3])) return false;\n\t\tif ((node[3] != n[0]) && (node[3] != n[1]) && (node[3] != n[2]) && (node[3] != n[3])) return false;\n\t}\n\telse \n\t{\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool FEFaceList::FACE::HasEdge(int a, int b) const\n{\n\tconst int* n = node;\n\tif (ntype == 3)\n\t{\n\t\tif (((n[0] == a) && (n[1] == b)) || ((n[0] == b) && (n[1] == a))) return true;\n\t\tif (((n[1] == a) && (n[2] == b)) || ((n[1] == b) && (n[2] == a))) return true;\n\t\tif (((n[2] == a) && (n[0] == b)) || ((n[2] == b) && (n[0] == a))) return true;\n\t}\n\telse if (ntype == 4)\n\t{\n\t\tif (((n[0] == a) && (n[1] == b)) || ((n[0] == b) && (n[1] == a))) return true;\n\t\tif (((n[1] == a) && (n[2] == b)) || ((n[1] == b) && (n[2] == a))) return true;\n\t\tif (((n[2] == a) && (n[3] == b)) || ((n[2] == b) && (n[3] == a))) return true;\n\t\tif (((n[3] == a) && (n[0] == b)) || ((n[3] == b) && (n[0] == a))) return true;\n\t}\n\telse\n\t{\n\t\tassert(false);\n\t}\n\n\treturn false;\n}\n\nFEFaceList::FEFaceList() : m_mesh(nullptr)\n{\n\n}\n\nFEFaceList::FEFaceList(const FEFaceList& faceList)\n{\n\tm_mesh = faceList.m_mesh;\n\tm_faceList = faceList.m_faceList;\n}\n\nint FEFaceList::Faces() const\n{\n\treturn (int) m_faceList.size();\n}\n\nconst FEFaceList::FACE& FEFaceList::operator [] (int i) const\n{ \n\treturn m_faceList[i];\n}\n\nconst FEFaceList::FACE& FEFaceList::Face(int i) const\n{\n\treturn m_faceList[i];\n}\n\nFEMesh* FEFaceList::GetMesh()\n{\n\treturn m_mesh;\n}\n\nint FEElementFaceList::Faces(int elem) const\n{\n\treturn (int)m_EFL[elem].size();\n}\n\nconst std::vector<int>& FEElementFaceList::FaceList(int elem) const\n{\n\treturn m_EFL[elem];\n}\n\n// Extract the surface only\nFEFaceList FEFaceList::GetSurface() const\n{\n\tFEFaceList surface;\n\tsurface.m_mesh = m_mesh;\n\n\tint faces = Faces();\n\tfor (int i = 0; i < faces; ++i)\n\t{\n\t\tconst FEFaceList::FACE& f = m_faceList[i];\n\t\tif (f.nsurf == 1) surface.m_faceList.push_back(f);\n\t}\n\n\treturn surface;\n}\n\nbool FEFaceList::Create(FEMesh& mesh, FEElemElemList& EEL)\n{\n\tm_mesh = &mesh;\n\n\t// get the number of elements in this mesh\n\tint NE = mesh.Elements();\n\n\t// count the number of facets we have to create\n\tint NF = 0;\n\tFEElementList EL(mesh);\n\tFEElementList::iterator it = EL.begin();\n\tfor (int i = 0; i<NE; ++i, ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tint nf = el.Faces();\n\t\tfor (int j = 0; j<nf; ++j)\n\t\t{\n\t\t\tFEElement* pen = EEL.Neighbor(i, j);\n\t\t\tif (pen == 0) ++NF;\n\t\t\tif ((pen != 0) && (el.GetID() < pen->GetID())) ++NF;\n\t\t}\n\t}\n\n\t// create the facet list\n\tm_faceList.resize(NF);\n\n\t// build the facets\n\tint face[FEElement::MAX_NODES];\n\tNF = 0;\n\tit = EL.begin();\n\tfor (int i = 0; i<NE; ++i, ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tif (el.Class() == FE_ELEM_SOLID)\n\t\t{\n\t\t\tint nf = el.Faces();\n\t\t\tfor (int j = 0; j < nf; ++j)\n\t\t\t{\n\t\t\t\tFEElement* pen = EEL.Neighbor(i, j);\n\t\t\t\tif ((pen == 0) || ((pen != 0) && (el.GetID() < pen->GetID())))\n\t\t\t\t{\n\t\t\t\t\tFACE& se = m_faceList[NF++];\n\t\t\t\t\tint nn = el.GetFace(j, face);\n\t\t\t\t\tse.ntype = nn;\n\t\t\t\t\tfor (int k = 0; k < nn; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tse.node[k] = face[k];\n\t\t\t\t\t}\n\n\t\t\t\t\t// The facet is a surface facet if the element neighbor is null\n\t\t\t\t\tse.nsurf = (pen == 0 ? 1 : 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n// build the neighbor list\nvoid FEFaceList::BuildNeighbors()\n{\n\t// build a node-face list\n\tFENodeFaceList NFL;\n\tNFL.Create(*this);\n\n\tfor (int i = 0; i < Faces(); ++i)\n\t{\n\t\tFACE& f = m_faceList[i];\n\t\tf.nbr[0] = f.nbr[1] = f.nbr[2] = f.nbr[3] = -1;\n\n\t\tif (f.nsurf == 1)\n\t\t{\n\t\t\tint fn = f.ntype;\n\t\t\tfor (int j = 0; j < fn; ++j)\n\t\t\t{\n\t\t\t\tint n0 = f.node[j];\n\t\t\t\tint n1 = f.node[(j + 1) % fn];\n\n\t\t\t\tint nval = NFL.Faces(n0);\n\t\t\t\tstd::vector<int> fl = NFL.FaceList(n0);\n\t\t\t\tfor (int k = 0; k < nval; ++k)\n\t\t\t\t{\n\t\t\t\t\tif (fl[k] != i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFACE& fk = m_faceList[fl[k]];\n\t\t\t\t\t\tif ((fk.nsurf == 1) && fk.HasEdge(n0, n1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tf.nbr[j] = fl[k];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//=============================================================================\nFENodeFaceList::FENodeFaceList()\n{\n\n}\n\nint FENodeFaceList::Faces(int node) const\n{\n\treturn (int)m_NFL[node].size();\n}\n\nconst std::vector<int>& FENodeFaceList::FaceList(int node) const\n{\n\treturn m_NFL[node];\n}\n\nbool FENodeFaceList::Create(FEFaceList& FL)\n{\n\tFEMesh& mesh = *FL.GetMesh();\n\n\tint NN = mesh.Nodes();\n\tm_NFL.resize(NN);\n\n\tint NF = FL.Faces();\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tconst FEFaceList::FACE& face = FL[i];\n\t\tfor (int j = 0; j < face.ntype; ++j)\n\t\t{\n\t\t\tm_NFL[face.node[j]].push_back(i);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//=============================================================================\n\nFEElementFaceList::FEElementFaceList()\n{\n\n}\n\n//NOTE: only works for hex elements\nbool FEElementFaceList::Create(FEElementList& elemList, FEFaceList& faceList)\n{\n\tFEMesh& mesh = *faceList.GetMesh();\n\n\tconst int FTET[4][3] = {\n\t\t{ 0, 1, 3},\n\t\t{ 1, 2, 3},\n\t\t{ 2, 0, 3},\n\t\t{ 2, 1, 0}\n\t};\n\n\tconst int FHEX[6][4] = { \n\t\t{ 0, 1, 5, 4 },\n\t\t{ 1, 2, 6, 5 },\n\t\t{ 2, 3, 7, 6 },\n\t\t{ 3, 0, 4, 7 },\n\t\t{ 3, 2, 1, 0 },\n\t\t{ 4, 5, 6, 7 }};\n\n\tconst int FPENTA[5][4] = {\n\t\t{ 0, 1, 4, 3 },\n\t\t{ 1, 2, 5, 4 },\n\t\t{ 0, 3, 5, 2 },\n\t\t{ 0, 2, 1, 1 },\n\t\t{ 3, 4, 5, 5 }};\n\n\t// build a node face table for FT to facilitate searching\n\tint NN = mesh.Nodes();\n\tvector<vector<int> > NFT; NFT.resize(NN);\n\tfor (int i = 0; i<faceList.Faces(); ++i)\n\t{\n\t\tconst FEFaceList::FACE& f = faceList.Face(i);\n\t\tNFT[f.node[0]].push_back(i);\n\t\tNFT[f.node[1]].push_back(i);\n\t\tNFT[f.node[2]].push_back(i);\n\t\tif (f.ntype == 4) NFT[f.node[3]].push_back(i);\n\t}\n\n\tint NE = mesh.Elements();\n\tm_EFL.resize(NE);\n\tint i = 0;\n\tfor (FEElementList::iterator it = elemList.begin(); it != elemList.end(); ++it, ++i)\n\t{\n\t\tconst FEElement& el = *it;\n\t\tvector<int>& EFLi = m_EFL[i];\n\t\tif ((el.Shape() == ET_TET4) || (el.Shape() == ET_TET5))\n\t\t{\n\t\t\tEFLi.resize(4);\n\t\t\tfor (int j = 0; j < 4; ++j)\n\t\t\t{\n\t\t\t\tint fj[3] = { el.m_node[FTET[j][0]], el.m_node[FTET[j][1]], el.m_node[FTET[j][2]] };\n\t\t\t\tEFLi[j] = -1;\n\t\t\t\tvector<int>& nfi = NFT[fj[0]];\n\t\t\t\tfor (int k = 0; k<(int)nfi.size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tconst FEFaceList::FACE& fk = faceList.Face(nfi[k]);\n\t\t\t\t\tif (fk.IsEqual(fj))\n\t\t\t\t\t{\n\t\t\t\t\t\tEFLi[j] = nfi[k];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_HEX8)\n\t\t{\n\t\t\tEFLi.resize(6);\n\t\t\tfor (int j = 0; j < 6; ++j)\n\t\t\t{\n\t\t\t\tint fj[4] = { el.m_node[FHEX[j][0]], el.m_node[FHEX[j][1]], el.m_node[FHEX[j][2]], el.m_node[FHEX[j][3]] };\n\t\t\t\tEFLi[j] = -1;\n\t\t\t\tvector<int>& nfi = NFT[fj[0]];\n\t\t\t\tfor (int k = 0; k<(int)nfi.size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tconst FEFaceList::FACE& fk = faceList.Face(nfi[k]);\n\t\t\t\t\tif (fk.IsEqual(fj))\n\t\t\t\t\t{\n\t\t\t\t\t\tEFLi[j] = nfi[k];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_PENTA6)\n\t\t{\n\t\t\tEFLi.resize(5);\n\t\t\tfor (int j = 0; j < 5; ++j)\n\t\t\t{\n\t\t\t\tint fj[4] = { el.m_node[FPENTA[j][0]], el.m_node[FPENTA[j][1]], el.m_node[FPENTA[j][2]], el.m_node[FPENTA[j][3]] };\n\t\t\t\tEFLi[j] = -1;\n\t\t\t\tvector<int>& nfi = NFT[fj[0]];\n\t\t\t\tfor (int k = 0; k < (int)nfi.size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tconst FEFaceList::FACE& fk = faceList.Face(nfi[k]);\n\t\t\t\t\tif (fk.IsEqual(fj))\n\t\t\t\t\t{\n\t\t\t\t\t\tEFLi[j] = nfi[k];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_QUAD4)\n\t\t{\n\t\t}\n\t\telse if (el.Shape() == FE_Element_Shape::ET_TRI3)\n\t\t{\n\t\t}\n\t\telse return false;\n\t}\n\treturn true;\n}\n\n//=============================================================================\nFEFaceEdgeList::FEFaceEdgeList()\n{\n\n}\n\nbool FEFaceEdgeList::Create(FEFaceList& faceList, FEEdgeList& edgeList)\n{\n\tFENodeEdgeList NEL;\n\tNEL.Create(edgeList);\n\n\tint faces = faceList.Faces();\n\tm_FEL.resize(faces);\n\tfor (int i = 0; i < faces; ++i)\n\t{\n\t\tvector<int>& edges = m_FEL[i];\n\t\tedges.clear();\n\n\t\tFEFaceList::FACE face = faceList.Face(i);\n\n\t\t// find the corresponding edges\n\t\tint n = face.ntype;\n\t\tfor (int j = 0; j < n; ++j)\n\t\t{\n\t\t\tint a = face.node[j];\n\t\t\tint b = face.node[(j + 1) % n];\n\n\t\t\tint edge = -1;\n\t\t\tconst std::vector<int>& a_edges = NEL.EdgeList(a);\n\t\t\tfor (int k = 0; k < a_edges.size(); ++k)\n\t\t\t{\n\t\t\t\tconst FEEdgeList::EDGE& ek = edgeList[a_edges[k]];\n\t\t\t\tif (((ek.node[0] == a) && (ek.node[1] == b)) ||\n\t\t\t\t\t((ek.node[1] == a) && (ek.node[0] == b)))\n\t\t\t\t{\n\t\t\t\t\tedge = a_edges[k];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassert(edge >= 0);\n\t\t\tif (edge == -1) return false;\n\t\t\tedges.push_back(edge);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nint FEFaceEdgeList::Edges(int nface)\n{\n\treturn (int)m_FEL[nface].size();\n}\n\nconst std::vector<int>& FEFaceEdgeList::EdgeList(int nface) const\n{\n\treturn m_FEL[nface];\n}\n\n//=============================================================================\nFENodeEdgeList::FENodeEdgeList()\n{\n\n}\n\nbool FENodeEdgeList::Create(FEEdgeList& edgeList)\n{\n\tm_NEL.clear();\n\tFEMesh* mesh = edgeList.GetMesh();\n\tint N = mesh->Nodes();\n\tm_NEL.resize(N);\n\n\tint NE = edgeList.Edges();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tconst FEEdgeList::EDGE& edge = edgeList[i];\n\t\tm_NEL[edge.node[0]].push_back(i);\n\t\tm_NEL[edge.node[1]].push_back(i);\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FEFaceList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n\nclass FEMesh;\nclass FEElementList;\nclass FEElemElemList;\nclass FEEdgeList;\n\nclass FECORE_API FEFaceList\n{\npublic:\n\tstruct FECORE_API FACE\n\t{\n\t\tint\t\tntype;\t\t// 3 = triangle, 4 = quad\n\t\tint\t\tnode[4];\n\t\tint\t\tnsurf;\t\t// 1 if facet is on surface \n\t\tint\t\tnbr[4];\t\t// neighbor list\n\n\t\tbool IsEqual(int* n) const;\n\n\t\tbool HasEdge(int a, int b) const;\n\t};\n\npublic:\n\tFEFaceList();\n\tFEFaceList(const FEFaceList& faceList);\n\n\tbool Create(FEMesh& mesh, FEElemElemList& ENL);\n\n\tint Faces() const;\n\n\tconst FACE& operator [] (int i) const;\n\tconst FACE& Face(int i) const;\n\n\tFEMesh* GetMesh();\n\n\t// Extract the surface only\n\tFEFaceList GetSurface() const;\n\n\t// build the neighbor list\n\tvoid BuildNeighbors();\n\nprotected:\n\tFEMesh*\t\t\t\tm_mesh;\n\tstd::vector<FACE>\tm_faceList;\n};\n\nclass FECORE_API FENodeFaceList\n{\npublic:\n\tFENodeFaceList();\n\n\tbool Create(FEFaceList& FL);\n\n\tint Faces(int node) const;\n\tconst std::vector<int>& FaceList(int node) const;\n\nprivate:\n\tstd::vector<std::vector<int> >\tm_NFL;\n};\n\nclass FECORE_API FEElementFaceList\n{\npublic:\n\tFEElementFaceList();\n\n\tbool Create(FEElementList& elemList, FEFaceList& faceList);\n\n\tint Faces(int elem) const;\n\tconst std::vector<int>& FaceList(int elem) const;\n\nprivate:\n\tstd::vector<std::vector<int> >\tm_EFL;\n};\n\nclass FECORE_API FEFaceEdgeList\n{\npublic:\n\tFEFaceEdgeList();\n\n\tbool Create(FEFaceList& faceList, FEEdgeList& edgeList);\n\n\tint Edges(int nface);\n\tconst std::vector<int>& EdgeList(int nface) const;\n\nprivate:\n\tstd::vector<std::vector<int> > m_FEL;\n};\n\nclass FECORE_API FENodeEdgeList\n{\npublic:\n\tFENodeEdgeList();\n\n\tbool Create(FEEdgeList& edgeList);\n\n\tconst std::vector<int>& EdgeList(int node) const { return m_NEL[node]; }\n\nprivate:\n\tstd::vector<std::vector<int> > m_NEL;\n};\n"
  },
  {
    "path": "FECore/FEFacetSet.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFacetSet.h\"\n#include \"FEMesh.h\"\n#include \"DumpStream.h\"\n#include \"FESurface.h\"\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSet::FACET::Serialize(DumpStream& ar)\n{\n\tar & node;\n\tar & ntype;\n}\n\n//-----------------------------------------------------------------------------\nFEFacetSet::FEFacetSet(FEModel* fem) : FEItemList(fem, FEItemList::FE_ELEMENT_SET)\n{\n\tm_surface = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSet::SetSurface(FESurface* surf) { m_surface = surf; }\nFESurface* FEFacetSet::GetSurface() { return m_surface; }\n\nvoid FEFacetSet::Clear()\n{\n\tm_Face.clear();\n}\n\n//-----------------------------------------------------------------------------\nint FEFacetSet::Faces() const { return (int)m_Face.size(); }\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSet::Create(int n)\n{\n\tm_Face.resize(n);\n}\n\n//-----------------------------------------------------------------------------\n// create from a surface\nvoid FEFacetSet::Create(const FESurface& surf)\n{\n\tint NE = surf.Elements();\n\tm_Face.resize(NE);\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tconst FESurfaceElement& el = surf.Element(i);\n\t\tFACET& face = m_Face[i];\n\t\tswitch (el.Shape())\n\t\t{\n\t\tcase ET_TRI3 : face.ntype = 3; break;\n\t\tcase ET_QUAD4: face.ntype = 4; break;\n\t\tcase ET_TRI6 : face.ntype = 6; break;\n\t\tcase ET_TRI7 : face.ntype = 7; break;\n\t\tcase ET_QUAD8: face.ntype = 8; break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\n\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t{\n\t\t\tface.node[j] = el.m_node[j];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEFacetSet::FACET& FEFacetSet::Face(int i)\n{\n\treturn m_Face[i];\n}\n\n//-----------------------------------------------------------------------------\nconst FEFacetSet::FACET& FEFacetSet::Face(int i) const\n{\n\treturn m_Face[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSet::Add(FEFacetSet* pf)\n{\n\tm_Face.insert(m_Face.end(), pf->m_Face.begin(), pf->m_Face.end());\n}\n\n//-----------------------------------------------------------------------------\nFENodeList FEFacetSet::GetNodeList() const\n{\n\tFEMesh* mesh = GetMesh();\n\tFENodeList set(mesh);\n\tvector<int> tag(mesh->Nodes(), 0);\n\tfor (int i = 0; i<Faces(); ++i)\n\t{\n\t\tconst FACET& el = m_Face[i];\n\t\tint ne = el.ntype;\n\t\tfor (int j = 0; j<ne; ++j)\n\t\t{\n\t\t\tif (tag[el.node[j]] == 0)\n\t\t\t{\n\t\t\t\tset.Add(el.node[j]);\n\t\t\t\ttag[el.node[j]] = 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn set;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFacetSet::Serialize(DumpStream& ar)\n{\n\tFEItemList::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Face;\n}\n\nvoid FEFacetSet::SaveClass(DumpStream& ar, FEFacetSet* p) {}\nFEFacetSet* FEFacetSet::LoadClass(DumpStream& ar, FEFacetSet* p)\n{\n\tp = new FEFacetSet(&ar.GetFEModel());\n\treturn p;\n}\n"
  },
  {
    "path": "FECore/FEFacetSet.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"FEItemList.h\"\n#include \"FEElement.h\"\n#include \"FENodeSet.h\"\n#include <vector>\n#include <string>\n\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n//! This class defines a set of facets. This can be used in the creation of\n//! surfaces.\nclass FECORE_API FEFacetSet : public FEItemList\n{\npublic:\n\tstruct FACET\n\t{\n\t\t// max nr of nodes for each facet\n\t\tenum { MAX_NODES = 10};\n\n\t\t// different facet types\n\t\tenum FacetType {\n\t\t\tINVALID = 0,\n\t\t\tTRI3  = 3,\n\t\t\tQUAD4 = 4,\n\t\t\tTRI6  = 6,\n\t\t\tTRI7  = 7,\n\t\t\tQUAD8 = 8,\n\t\t\tQUAD9 = 9,\n\t\t\tTRI10 = 10\n\t\t};\n\n\t\tint\tnode[FACET::MAX_NODES];\n\t\tint\tntype;\t//\t3=tri3, 4=quad4, 6=tri6, 7=tri7, 8=quad8, 9=quad9\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\t\tint Nodes() const { return ntype; }\n\n\t\tFACET() { ntype = FACET::INVALID; }\n\t};\n\npublic:\n\t// Constructor\n\tFEFacetSet(FEModel* fem);\n\n\t// Allocate facets \n\tvoid Create(int n);\n\n\t// create from a surface\n\tvoid Create(const FESurface& surf);\n\n\tvoid Clear();\n\n\t// return the size of the facet ste\n\tint Faces() const;\n\n\t// return a facet\n\tFACET& Face(int i);\n\tconst FACET& Face(int i) const;\n\n\t// add a facet set\n\tvoid Add(FEFacetSet* pf);\n\n\t// extract the node set from the facet set\n\tFENodeList GetNodeList() const;\n\npublic:\n\t// serialize\n\tvoid Serialize(DumpStream& ar);\n\n\tstatic void SaveClass(DumpStream& ar, FEFacetSet* p);\n\tstatic FEFacetSet* LoadClass(DumpStream& ar, FEFacetSet* p);\n\n\t// TODO: This is a hack used to convert between a surface and a facet set when reading face data records\n\tvoid SetSurface(FESurface* surf);\n\tFESurface* GetSurface();\n\nprivate:\n\tstd::vector<FACET>\tm_Face;\t// the list of facets\n\tFESurface*\t\t\tm_surface;\t\t// the surface this facet list refers to (can be zero!)\n};\n"
  },
  {
    "path": "FECore/FEFixedBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFixedBC.h\"\n#include \"FENodeSet.h\"\n#include \"FENode.h\"\n#include \"DumpStream.h\"\n#include \"log.h\"\n\n//-----------------------------------------------------------------------------\nFEFixedBC::FEFixedBC(FEModel* pfem) : FENodalBC(pfem)\n{\n}\n\nFEFixedBC::FEFixedBC(FEModel* pfem, int dof, FENodeSet* ps) : FENodalBC(pfem)\n{\n\tSetDOFList(dof);\n\tSetNodeSet(ps);\n}\n\n//-----------------------------------------------------------------------------\n//! initialization\nbool FEFixedBC::Init()\n{\n\tif (GetNodeSet() == nullptr) return false;\n\tif (GetDofList().Size() == 0)\n\t{\n\t\tfeLogError(\"No degrees of freedom are constrained in %s\", GetName().c_str());\n\t\treturn false;\n\t}\n\n\treturn FENodalBC::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFixedBC::Activate()\n{\n\tFENodalBC::Activate();\n\n\tFENodeSet& nset = *GetNodeSet();\n\tint n = nset.Size();\n\tint dofs = m_dof.Size();\n\tfor (int i = 0; i<n; ++i)\n\t{\n\t\t// make sure we only activate open dof's\n\t\tFENode& node = *nset.Node(i);\n\t\tfor (int j=0; j<dofs; ++j)\n\t\t{\n\t\t\tint dofj = m_dof[j];\n\t\t\tif (node.get_bc(dofj) == DOF_OPEN)\n\t\t\t{\n\t\t\t\tnode.set_bc(dofj, DOF_FIXED);\n\t\t\t\tnode.set(dofj, 0.0);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFixedBC::Deactivate()\n{\n\tFENodalBC::Deactivate();\n\tFENodeSet* pns = GetNodeSet();\n\tint N = pns->Size();\n\tsize_t dofs = m_dof.Size();\n\tfor (size_t i = 0; i<N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *pns->Node(i);\n\n\t\t// set the dof to open\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tnode.set_bc(m_dof[j], DOF_OPEN);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFixedBC::CopyFrom(FEBoundaryCondition* bc)\n{\n\tFEFixedBC* fbc = dynamic_cast<FEFixedBC*>(bc);\n\tm_dof = fbc->GetDofList();\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEFixedDOF, FEFixedBC)\n\tADD_PARAMETER(m_dofs, \"dofs\", 0, \"$(dof_list)\");\nEND_FECORE_CLASS();\n\nFEFixedDOF::FEFixedDOF(FEModel* fem) : FEFixedBC(fem)\n{\n\n}\n\nvoid FEFixedDOF::SetDOFS(const std::vector<int>& dofs)\n{\n\tm_dofs = dofs;\n}\n\nbool FEFixedDOF::Init()\n{\n\tSetDOFList(m_dofs);\n\treturn FEFixedBC::Init();\n}\n"
  },
  {
    "path": "FECore/FEFixedBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENodalBC.h\"\n\n//-----------------------------------------------------------------------------\n//! This class represents a fixed degree of freedom\n//! This boundary conditions sets the BC attribute of the nodes in the nodeset\n//! to DOF_FIXED when activated.\nclass FECORE_API FEFixedBC : public FENodalBC\n{\npublic:\n\t//! constructors\n\tFEFixedBC(FEModel* pfem);\n\tFEFixedBC(FEModel* pfem, int dof, FENodeSet* ps);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! activation\n\tvoid Activate() override;\n\n\t//! deactivation\n\tvoid Deactivate() override;\n\n\tvoid CopyFrom(FEBoundaryCondition* bc) override;\n};\n\n//-----------------------------------------------------------------------------\n// This class is obsolete, but provides a direct parameterization of the base class.\n// This is maintained for backward compatibility with older feb files.\nclass FECORE_API FEFixedDOF : public FEFixedBC\n{\n\tDECLARE_FECORE_CLASS();\n\npublic:\n\tFEFixedDOF(FEModel* fem);\n\n\tvoid SetDOFS(const std::vector<int>& dofs);\n\n\tbool Init() override;\n\nprotected:\n\tstd::vector<int>\tm_dofs;\n};\n"
  },
  {
    "path": "FECore/FEFullNewtonStrategy.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEFullNewtonStrategy.h\"\n#include \"FESolver.h\"\n#include \"FEException.h\"\n#include \"FENewtonSolver.h\"\n\n//-----------------------------------------------------------------------------\nFEFullNewtonStrategy::FEFullNewtonStrategy(FEModel* fem) : FENewtonStrategy(fem)\n{\n\tm_plinsolve = nullptr;\n\tm_maxups = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFullNewtonStrategy::Init()\n{\n\tif (m_pns == nullptr) return false;\n\tm_plinsolve = m_pns->GetLinearSolver();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEFullNewtonStrategy::Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1)\n{\n\t// always return false to force a matrix reformation\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEFullNewtonStrategy::SolveEquations(vector<double>& x, vector<double>& b)\n{\n\t// perform a backsubstitution\n\tif (m_plinsolve->BackSolve(x, b) == false)\n\t{\n\t\tthrow LinearSolverFailed();\n\t}\n}\n"
  },
  {
    "path": "FECore/FEFullNewtonStrategy.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include \"matrix.h\"\n#include \"vector.h\"\n#include \"LinearSolver.h\"\n#include \"FENewtonStrategy.h\"\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEFullNewtonStrategy : public FENewtonStrategy\n{\npublic:\n\t//! constructor\n\tFEFullNewtonStrategy(FEModel* fem);\n\n\t//! New initialization method\n\tbool Init() override;\n\n\t//! perform a BFGS udpate\n\tbool Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1) override;\n\n\t//! solve the equations\n\tvoid SolveEquations(vector<double>& x, vector<double>& b) override;\n\npublic:\n\t// keep a pointer to the linear solver\n\tLinearSolver* m_plinsolve;\t//!< pointer to linear solver\n};\n"
  },
  {
    "path": "FECore/FEFunction1D.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEFunction1D.h\"\n#include \"FEModel.h\"\n#include \"DumpStream.h\"\n#include \"MMath.h\"\n#include \"MObj2String.h\"\n#include \"log.h\"\n\nFEFunction1D::FEFunction1D(FEModel* fem) : FECoreBase(fem)\n{\n}\n\ndouble FEFunction1D::derive(double x) const\n{\n\tconst double eps = 1e-6;\n\treturn (value(x + eps) - value(x))/eps;\n}\n\ndouble FEFunction1D::integrate(double a, double b) const\n{\n\treturn (b-a)*((value(a) + value(b))/2);\n}\n\nvoid FEFunction1D::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\n\tif (ar.IsSaving())\n\t{\n\t}\n\telse\n\t{\n\t}\n}\n\n// Invert the function f(x) = f0: given f0, return x.\n// On input, x is the initial guess.\n// On output, x is the solution.\n// Function returns true if solution has converged, false otherwise.\n// Uses Newton's method.\nbool FEFunction1D::invert(const double f0, double &x)\n{\n    const int maxiter = 100;\n    const double errabs = 1e-15;\n    const double errrel = 1e-6;\n    \n    bool cnvgd = false;\n    bool done = false;\n    int iter = 0;\n    double dx;\n    while (!done) {\n        double f = value(x) - f0;\n        double df = derive(x);\n        // check if slope is zero\n        if (fabs(df) <= errabs) {\n            if (fabs(f) > errabs) {\n                // if function not zero, this is a local minimum\n                done = true;\n            }\n            else {\n                // if function is zero, this is a valid solution\n                done = cnvgd = true;\n            }\n        }\n        // if slope is not zero proceed with iterative scheme\n        else {\n            dx = -f/df;\n            x += dx;\n            // check for relative or absolute convergence\n            if ((fabs(dx) <= errrel*fabs(x)) || (fabs(f) <= errabs)) {\n                done = cnvgd = true;\n            }\n        }\n        ++iter;\n        if (iter > maxiter) done = true;\n    }\n    \n    return cnvgd;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEConstFunction, FEFunction1D)\n\tADD_PARAMETER(m_value, \"value\");\nEND_FECORE_CLASS();\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEScaleFunction, FEFunction1D)\n\tADD_PARAMETER(m_scale, \"scale\");\nEND_FECORE_CLASS();\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FELinearFunction, FEFunction1D)\n\tADD_PARAMETER(m_slope, \"slope\");\n\tADD_PARAMETER(m_intercept, \"intercept\");\nEND_FECORE_CLASS();\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEStepFunction, FEFunction1D)\n\tADD_PARAMETER(m_x0, \"x0\");\n\tADD_PARAMETER(m_leftVal , \"left_val\");\n\tADD_PARAMETER(m_rightVal, \"right_val\");\nEND_FECORE_CLASS();\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMathFunction, FEFunction1D)\n\tADD_PARAMETER(m_s, \"math\");\nEND_FECORE_CLASS();\n\nFEMathFunction::FEMathFunction(FEModel* fem) : FEFunction1D(fem)\n{\n\tm_s = \"0\";\n}\n\nvoid FEMathFunction::SetMathString(const std::string& s)\n{\n\tm_s = s;\n}\n\nbool FEMathFunction::Init()\n{\n\tif (BuildMathExpressions() == false) return false;\n\treturn FEFunction1D::Init();\n}\n\nbool FEMathFunction::BuildMathExpressions()\n{\n\t// process the string\n\tm_exp.Clear();\n\tif (m_exp.Create(m_s, true) == false) return false;\n\n\t// match variables to model parameters.\n\tm_var.clear();\n\tm_ix = -1;\n\tFEModel* fem = GetFEModel();\n\tfor (int i=0; i<m_exp.Variables(); ++i)\n\t{\n\t\tMVariable* v = m_exp.Variable(i);\n\t\t\n\t\tParamString ps(v->Name().c_str());\n\t\tFEParamValue param = fem->GetParameterValue(ps);\n\t\tif (param.isValid() == false)\n\t\t{\n\t\t\t// let's assume this is the independent parameter\n\t\t\tif (m_ix == -1)\n\t\t\t{\n\t\t\t\t// push a dummy param value\n\t\t\t\tm_var.push_back(FEParamValue());\n\t\t\t\tm_ix = i;\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (param.type() != FE_PARAM_DOUBLE) return false;\n\t\t\tm_var.push_back(param);\n\t\t}\n\t}\n\n\t// copy variables to derived expressions\n\tm_dexp.Clear();\n\tm_d2exp.Clear();\n\tfor (int i = 0; i < m_exp.Variables(); ++i)\n\t{\n\t\tm_dexp.AddVariable(m_exp.Variable(i)->Name());\n\t\tm_d2exp.AddVariable(m_exp.Variable(i)->Name());\n\t}\n\n\t// evaluate first derivative\n    if (m_ix != -1) {\n        MITEM mi = MDerive(m_exp.GetExpression(), *m_exp.Variable(m_ix));\n\t\tm_dexp.SetExpression(mi);\n    }\n\telse\n\t\tm_dexp.Create(\"0\");\n\n\t// evaluate second derivative\n    if (m_ix != -1) {\n        MITEM mi = MDerive(m_dexp.GetExpression(), *m_dexp.Variable(m_ix));\n        m_d2exp.SetExpression(mi);\n    }\n    else\n        m_d2exp.Create(\"0\");\n\n\treturn true;\n}\n\nvoid FEMathFunction::Serialize(DumpStream& ar)\n{\n\tFEFunction1D::Serialize(ar);\n\tif ((ar.IsShallow() == false) && (ar.IsLoading()))\n\t{\n\t\tbool b = BuildMathExpressions();\n\t\tassert(b);\n\t}\n}\n\nFEFunction1D* FEMathFunction::copy()\n{\n\tFEMathFunction* m = new FEMathFunction(GetFEModel());\n\tm->m_s = m_s;\n\tm->m_ix = m_ix;\n\tm->m_var = m_var;\n\n\tm->m_exp = m_exp;\n\tm->m_dexp = m_dexp;\n    m->m_d2exp = m_d2exp;\n\treturn m;\n}\n\nvoid FEMathFunction::evalParams(std::vector<double>& val, double t) const\n{\n\tval.resize(m_var.size());\n\tfor (int i = 0; i < m_var.size(); ++i)\n\t{\n\t\tif (i == m_ix) val[i] = t;\n\t\telse val[i] = m_var[i].value<double>();\n\t}\n}\n\ndouble FEMathFunction::value(double t) const\n{\n\tvector<double> v;\n\tevalParams(v, t);\n\treturn m_exp.value_s(v);\n}\n\ndouble FEMathFunction::derive(double t) const\n{\n\tvector<double> v;\n\tevalParams(v, t);\n\treturn m_dexp.value_s(v);\n}\n\ndouble FEMathFunction::deriv2(double t) const\n{\n\tvector<double> v;\n\tevalParams(v, t);\n\treturn m_d2exp.value_s(v);\n}\n\n"
  },
  {
    "path": "FECore/FEFunction1D.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"FECoreBase.h\"\n#include \"MathObject.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n// Class that represents a 1D function\n// Derived classes need to implement:\n//   - value(double): This evaluates the function at a particular point\n//   - copy()       : Create a copy of the class\n//   - derive(double) : Calculate the derivative. This is optional, but allows implementation of more efficient algorithm, since default implements forward difference\n//\nclass FECORE_API FEFunction1D : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FEFUNCTION1D_ID)\n\tFECORE_BASE_CLASS(FEFunction1D);\n\npublic:\n\tFEFunction1D(FEModel* pfem);\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar);\n\n\t// this class requires a copy member\n\tvirtual FEFunction1D* copy() = 0;\n\n\t// evaluate the function at x\n\t// must be defined by derived classes\n\tvirtual double value(double x) const = 0;\n\n\t// value of first derivative of function at x\n\t// can be overridden by derived classes.\n\t// default implementation is a forward-difference\n\tvirtual double derive(double x) const;\n\n    virtual double deriv2(double x) const = 0;\n\n\t// value of first definite integral of funciton from a to b\n\t// can be overridden by derived classes.\n\t// default implementation is trapezoidal rule\n\tvirtual double integrate(double a, double b) const;\n    \n\tvirtual void Clear() {}\n    \n    // invert function\n    virtual bool invert(const double f0, double &x);\n};\n\n//-----------------------------------------------------------------------------\n// A constant function\nclass FECORE_API FEConstFunction : public FEFunction1D\n{\npublic:\n\tFEConstFunction(FEModel* fem) : FEFunction1D(fem), m_value(0.0) {}\n\tFEFunction1D* copy() override { return new FEConstFunction(GetFEModel(), m_value); }\n\n\tdouble value(double t) const override { return m_value;\t}\n\tdouble derive(double t) const override { return 0.0; }\n\tdouble deriv2(double t) const override { return 0.0; }\n\nprotected:\n\tFEConstFunction(FEModel* fem, double val) : FEFunction1D(fem), m_value(val) {}\n\nprivate:\n\tdouble\tm_value;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n// A scale function\nclass FECORE_API FEScaleFunction : public FEFunction1D\n{\npublic:\n\tFEScaleFunction(FEModel* fem) : FEFunction1D(fem), m_scale(1.0) {}\n\tFEScaleFunction(FEModel* fem, double s) : FEFunction1D(fem), m_scale(s) {}\n\tFEFunction1D* copy() override { return new FEScaleFunction(GetFEModel(), m_scale); }\n\n\tdouble value(double t) const override\n\t{\n\t\treturn m_scale * t;\n\t}\n\n\tdouble derive(double t) const override\n\t{\n\t\treturn m_scale;\n\t}\n\n\tdouble deriv2(double t) const override\n\t{\n\t\treturn 0;\n\t}\n\nprivate:\n\tdouble\tm_scale;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// A linear function\nclass FECORE_API FELinearFunction : public FEFunction1D\n{\npublic:\n\tFELinearFunction(FEModel* fem) : FEFunction1D(fem), m_slope(0.0), m_intercept(0.0) {}\n\tFELinearFunction(FEModel* fem, double m, double y0) : FEFunction1D(fem), m_slope(m), m_intercept(y0) {}\n\tFEFunction1D* copy() override { return new FELinearFunction(GetFEModel(), m_slope, m_intercept); }\n\n\tdouble value(double t) const override\n\t{\n\t\treturn m_slope*t + m_intercept;\n\t}\n\n\tdouble derive(double t) const override\n\t{\n\t\treturn m_slope;\n\t}\n\n    double deriv2(double t) const override\n    {\n        return 0;\n    }\n\nprivate:\n\tdouble\tm_slope;\n\tdouble\tm_intercept;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// A step function\nclass FECORE_API FEStepFunction : public FEFunction1D\n{\npublic:\n\tFEStepFunction(FEModel* fem) : FEFunction1D(fem), m_x0(0.0), m_leftVal(0.0), m_rightVal(1.0) {}\n\tFEStepFunction(FEModel* fem, double x0, double lv, double rv) : FEFunction1D(fem), m_x0(x0), m_leftVal(lv), m_rightVal(rv) {}\n\tFEFunction1D* copy() override { return new FEStepFunction(GetFEModel(), m_x0, m_leftVal, m_rightVal); }\n\n\tdouble value(double t) const override\n\t{\n\t\treturn (t < m_x0 ? m_leftVal : m_rightVal);\n\t}\n\n\tdouble derive(double t) const override\n\t{\n\t\treturn 0.0;\n\t}\n\n\tdouble deriv2(double t) const override\n\t{\n\t\treturn 0.0;\n\t}\n\n    // invert function has no unique solution\n    bool invert(const double f0, double &x) override { return false; }\n    \nprivate:\n\tdouble\tm_x0;\n\tdouble\tm_leftVal;\n\tdouble\tm_rightVal;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n//! function defined via math expression\nclass FECORE_API FEMathFunction : public FEFunction1D\n{\npublic:\n\tFEMathFunction(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tFEFunction1D* copy() override;\n\n\tdouble value(double t) const override;\n\n\tdouble derive(double t) const override;\n\n    double deriv2(double t) const override;\n\n\tvoid SetMathString(const std::string& s);\n\nprivate:\n\tvoid evalParams(std::vector<double>& val, double t) const;\n\n\tbool BuildMathExpressions();\n\nprivate:\n\tstd::string\t\t\tm_s;\n\tint\t\t\t\t\tm_ix;\t\t\t// index of independent variable\n\tstd::vector<FEParamValue>\tm_var;\t// list of model parameters that are used as variables in expression.\n\n\tMSimpleExpression\tm_exp;\n\tMSimpleExpression\tm_dexp;\n    MSimpleExpression   m_d2exp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEGlobalData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEGlobalData.h\"\n\n//-----------------------------------------------------------------------------\nFEGlobalData::FEGlobalData(FEModel* fem) : FEModelComponent(fem)\n{\n\n}\n"
  },
  {
    "path": "FECore/FEGlobalData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelComponent.h\"\n\n//-----------------------------------------------------------------------------\n//! This class can be used to define global model data and will be placed in the\n//! global date section of the FEModel class\nclass FECORE_API FEGlobalData : public FEModelComponent\n{\n\tFECORE_SUPER_CLASS(FEGLOBALDATA_ID)\n\tFECORE_BASE_CLASS(FEGlobalData)\n\npublic:\n\t//! constructor\n\tFEGlobalData(FEModel* fem);\n};\n"
  },
  {
    "path": "FECore/FEGlobalMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEGlobalMatrix.h\"\n#include \"FEModel.h\"\n#include \"FEDomain.h\"\n#include \"FESurface.h\"\n\n//-----------------------------------------------------------------------------\nFEElementMatrix::FEElementMatrix(const FEElement& el)\n{\n\tm_node = el.m_node;\n}\n\n//-----------------------------------------------------------------------------\nFEElementMatrix::FEElementMatrix(const FEElementMatrix& ke) : matrix(ke)\n{\n\tm_node = ke.m_node;\n\tm_lmi = ke.m_lmi;\n\tm_lmj = ke.m_lmj;\n}\n\n//-----------------------------------------------------------------------------\nFEElementMatrix::FEElementMatrix(const FEElementMatrix& ke, double scale)\n{\n\tm_node = ke.m_node;\n\tm_lmi = ke.m_lmi;\n\tm_lmj = ke.m_lmj;\n\tmatrix& T = *this;\n\tconst matrix& K = ke;\n\tT = (scale == 1.0 ? K : K*scale);\n}\n\n//-----------------------------------------------------------------------------\nFEElementMatrix::FEElementMatrix(const FEElement& el, const vector<int>& lmi) : matrix((int)lmi.size(), (int)lmi.size())\n{\n\tm_node = el.m_node;\n\tm_lmi = lmi;\n\tm_lmj = lmi;\n}\n\n//-----------------------------------------------------------------------------\nFEElementMatrix::FEElementMatrix(const FEElement& el, vector<int>& lmi, vector<int>& lmj) : matrix((int)lmi.size(), (int)lmj.size())\n{\n\tm_node = el.m_node;\n\tm_lmi = lmi;\n\tm_lmj = lmj;\n};\n\n//-----------------------------------------------------------------------------\n// assignment operator\nvoid FEElementMatrix::operator = (const matrix& ke)\n{\n\tmatrix::operator=(ke);\n}\n\n\n//-----------------------------------------------------------------------------\n//! Takes a SparseMatrix structure that defines the structure of the global matrix.\nFEGlobalMatrix::FEGlobalMatrix(SparseMatrix* pK, bool del)\n{\n\tm_pA = pK;\n\tm_LM.resize(MAX_LM_SIZE);\n\tm_pMP = 0;\n\tm_nlm = 0;\n\tm_delA = del;\n}\n\n//-----------------------------------------------------------------------------\n//! Deletes the SparseMatrix variable.\nFEGlobalMatrix::~FEGlobalMatrix()\n{\n\tif (m_delA) delete m_pA;\n\tm_pA = 0;\n\tif (m_pMP) delete m_pMP;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGlobalMatrix::Clear()\n{ \n\tif (m_pA) m_pA->Clear(); \n}\n\n//-----------------------------------------------------------------------------\n//! Start building the profile. That is delete the old profile (if there was one)\n//! and create a new one. \nvoid FEGlobalMatrix::build_begin(int neq)\n{\n\tif (m_pMP) delete m_pMP;\n\tm_pMP = new SparseMatrixProfile(neq, neq);\n\n\t// initialize it to a diagonal matrix\n\t// TODO: Is this necessary?\n\tm_pMP->CreateDiagonal();\n\n\tm_nlm = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Add an \"element\" to the matrix profile. The definition of an element is quite\n//! general at this point. Any two or more degrees of freedom that are connected \n//! somehow are considered an element. This function takes one argument, namely a\n//! list of degrees of freedom that are part of such an \"element\". Elements are \n//! first copied into a local LM array.  When this array is full it is flushed and\n//! all elements in this array are added to the matrix profile.\nvoid FEGlobalMatrix::build_add(vector<int>& lm)\n{\n\tif (lm.empty() == false)\n\t{\n\t\tm_LM[m_nlm++] = lm;\n\t\tif (m_nlm >= MAX_LM_SIZE) build_flush();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Flush the LM array. The LM array stores a buffer of elements that have to be\n//! added to the profile. When this buffer is full it needs to be flushed. This\n//! flushin operation causes the actual update of the matrix profile.\nvoid FEGlobalMatrix::build_flush()\n{\n\tint i, j, n, *lm;\n\n\t// Since prescribed dofs have an equation number of < -1 we need to modify that\n\t// otherwise no storage will be allocated for these dofs (even not diagonal elements!).\n\tfor (i=0; i<m_nlm; ++i)\n\t{\n\t\tn = (int)m_LM[i].size();\n\t\tif (n > 0)\n\t\t{\n\t\t\tlm = &(m_LM[i])[0];\n\t\t\tfor (j=0; j<n; ++j) if (lm[j] < -1) lm[j] = -lm[j]-2;\n\t\t}\n\t}\n\n\tm_pMP->UpdateProfile(m_LM, m_nlm);\n\tm_nlm = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! This function makes sure the LM buffer is flushed and creates the actual\n//! sparse matrix from the matrix profile.\nvoid FEGlobalMatrix::build_end()\n{\n\tif (m_nlm > 0) build_flush();\n\tm_pA->Create(*m_pMP);\n}\n\n//-----------------------------------------------------------------------------\nbool FEGlobalMatrix::Create(FEModel* pfem, int neq, bool breset)\n{\n\t// The first time we come here we build the \"static\" profile.\n\t// This static profile stores the contribution to the matrix profile\n\t// of the \"elements\" that do not change. Most elements are static except\n\t// for instance contact elements which can change connectivity in between\n\t// calls to the Create() function. Storing the static profile instead of\n\t// reconstructing it every time we come here saves us a lot of time. The \n\t// static profile is stored in the variable m_MPs.\n\n\t// begin building the profile\n\tbuild_begin(neq);\n\t{\n\t\t// The first time we are here we construct the \"static\"\n\t\t// profile. This profile contains the contribution from\n\t\t// all static elements. A static element is defined as\n\t\t// an element that never changes its connectity. This \n\t\t// static profile is stored in the MP object. Next time\n\t\t// we come here we simply copy the MP object in stead\n\t\t// of building it from scratch.\n\t\tif (breset)\n\t\t{\n\t\t\tm_MPs.Clear();\n\n\t\t\t// build the matrix profile\n\t\t\tpfem->BuildMatrixProfile(*this, true);\n\n\t\t\t// copy the static profile to the MP object\n\t\t\t// Make sure the LM buffer is flushed first.\n\t\t\tbuild_flush();\n\t\t\tm_MPs = *m_pMP;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// copy the old static profile\n\t\t\t*m_pMP = m_MPs;\n\t\t}\n\n\t\t// Add the \"dynamic\" profile\n\t\tpfem->BuildMatrixProfile(*this, false);\n\t}\n\t// All done! We can now finish building the profile and create \n\t// the actual sparse matrix. This is done in the following function\n\tbuild_end();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Constructs the stiffness matrix from a FEMesh object. \nbool FEGlobalMatrix::Create(FEMesh& mesh, int neq)\n{\n\t// begin building the profile\n\tbuild_begin(neq);\n\t{\n\t\t// Add all elements to the profile\n\t\t// Loop over all active domains\n\t\tfor (int nd=0; nd<mesh.Domains(); ++nd)\n\t\t{\n\t\t\tFEDomain& d = mesh.Domain(nd);\n\t\t\td.BuildMatrixProfile(*this);\n\t\t}\n\t}\n\t// All done! We can now finish building the profile and create \n\t// the actual sparse matrix. This is done in the following function\n\tbuild_end();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! construct the stiffness matrix from a mesh\nbool FEGlobalMatrix::Create(FEMesh& mesh, int nstart, int nend)\n{\n\tif (nstart > nend) return false;\n\tint neq = nend - nstart + 1;\n\n\t// begin building the profile\n\tbuild_begin(neq);\n\t{\n\t\t// Add all elements to the profile\n\t\t// Loop over all active domains\n\t\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t\t{\n\t\t\tFEDomain& d = mesh.Domain(nd);\n\n\t\t\tvector<int> elm, lm;\n\t\t\tconst int NE = d.Elements();\n\t\t\tfor (int j = 0; j<NE; ++j)\n\t\t\t{\n\t\t\t\tFEElement& el = d.ElementRef(j);\n\t\t\t\td.UnpackLM(el, elm);\n\n\t\t\t\tlm.clear();\n\t\t\t\tfor (int k = 0; k < elm.size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tint nk = elm[k];\n\t\t\t\t\tif (nk < -1) nk = -nk - 2;\n\n\t\t\t\t\tif ((nk >= nstart) && (nk <= nend))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (elm[k] < -1) lm.push_back(elm[k] + nstart);\n\t\t\t\t\t\telse lm.push_back(elm[k] - nstart);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbuild_add(lm);\n\t\t\t}\n\t\t}\n\t}\n\t// All done! We can now finish building the profile and create \n\t// the actual sparse matrix. This is done in the following function\n\tbuild_end();\n\n\treturn true;\n}\n\n//! construct a stiffness matrix from a surface\nbool FEGlobalMatrix::Create(const FESurface& surf, const std::vector<int>& equationIDs)\n{\n\tint N = surf.Nodes();\n\tif ((int)equationIDs.size() != N) return false;\n\n\t// count equations\n\tint neq = 0;\n\tfor (int i = 0; i < N; ++i) if (equationIDs[i] != -1) neq++;\n\tif (neq == 0) return false;\n\n\t// build the matrix, assuming one degree of freedom per node\n\tbuild_begin(neq);\n\tfor (int i = 0; i<surf.Elements(); ++i) {\n\t\tconst FESurfaceElement& el = surf.Element(i);\n\t\tvector<int> elm(el.Nodes(), -1);\n\t\tfor (int j = 0; j<el.Nodes(); ++j)\n\t\t\telm[j] = equationIDs[el.m_lnode[j]];\n\t\tbuild_add(elm);\n\t}\n\tbuild_end();\n\n\treturn true;\n}\n\nvoid FEGlobalMatrix::Assemble(const FEElementMatrix& ke)\n{\n\tm_pA->Assemble(ke, ke.RowIndices(), ke.ColumnsIndices());\n}\n"
  },
  {
    "path": "FECore/FEGlobalMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"SparseMatrix.h\"\n#include \"FESolver.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEMesh;\nclass FESurface;\nclass FEElement;\n\n//-----------------------------------------------------------------------------\n//! This class represents an element matrix, i.e. a matrix of values and the row and\n//! column indices of the corresponding matrix elements in the global matrix. \nclass FECORE_API FEElementMatrix : public matrix\n{\npublic:\n\t// default constructor\n\tFEElementMatrix(){}\n\tFEElementMatrix(int nr, int nc) : matrix(nr, nc) {}\n\tFEElementMatrix(const FEElement& el);\n\n\t// constructor for symmetric matrices\n\tFEElementMatrix(const FEElement& el, const vector<int>& lmi);\n\n\t// constructor\n\tFEElementMatrix(const FEElement& el, vector<int>& lmi, vector<int>& lmj);\n\n\t// copy constructor\n\tFEElementMatrix(const FEElementMatrix& ke);\n\tFEElementMatrix(const FEElementMatrix& ke, double scale);\n\n\t// assignment operator\n\tvoid operator = (const matrix& ke);\n\n\t// row indices\n\tstd::vector<int>& RowIndices() { return m_lmi; }\n\tconst std::vector<int>& RowIndices() const { return m_lmi; }\n\n\t// column indices\n\tstd::vector<int>& ColumnsIndices() { return m_lmj; }\n\tconst std::vector<int>& ColumnsIndices() const { return m_lmj; }\n\n\t// set the row and columnd indices (assuming they are the same)\n\tvoid SetIndices(const std::vector<int>& lm) { m_lmi = m_lmj = lm; }\n\n\t// set the row and columnd indices\n\tvoid SetIndices(const std::vector<int>& lmr, const std::vector<int>& lmc) { m_lmi = lmr; m_lmj = lmc; }\n\n\t// Set the node indices\n\tvoid SetNodes(const std::vector<int>& en) { m_node = en; }\n\n\t// get the nodes\n\tconst std::vector<int>& Nodes() const { return m_node; }\n\nprivate:\n\tstd::vector<int>\tm_node;\t//!< node indices\n\tstd::vector<int>\tm_lmi;\t//!< row indices\n\tstd::vector<int>\tm_lmj;\t//!< column indices\n};\n\n//-----------------------------------------------------------------------------\n//! This class implements a global system matrix.\n\n//! The global system matrix is usually created by the discretization of the FE \n//! equations into a linear system of equations. The structure of it depends greatly\n//! on the element connectivity and usually results in a sparse matrix structure. \n//! Several sparse matrix structures are supported (Compact, Skyline, etc.) and to \n//! simplify the creation of the specific matrix structure, the FEGlobalMatrix offers\n//! functionality to create the global matrix structure without the need to know \n//! what particular sparse matrix format is used by the linear solver.\n\n//! \\todo I think the SparseMatrixProfile can handle all of the build functions.\n\nclass FECORE_API FEGlobalMatrix\n{\nprotected:\n\tenum { MAX_LM_SIZE = 64000 };\n\npublic:\n\t//! constructor\n\tFEGlobalMatrix(SparseMatrix* pK, bool del = true);\n\n\t//! destructor\n\tvirtual ~FEGlobalMatrix();\n\n\t//! construct the stiffness matrix from a FEM object\n\tbool Create(FEModel* pfem, int neq, bool breset);\n\n\t//! construct the stiffness matrix from a mesh\n\tbool Create(FEMesh& mesh, int neq);\n\n\t//! construct the stiffness matrix from a mesh\n\tbool Create(FEMesh& mesh, int nstart, int nend);\n\n\t//! construct a stiffness matrix from a surface\n\t//! The boundary array is a list of equation numbers.\n\tbool Create(const FESurface& surf, const std::vector<int>& equationIDs);\n\n\t//! clears the sparse matrix that stores the stiffness matrix\n\tvoid Clear();\n\n\t//! Assembly routine\n\tvirtual void Assemble(const FEElementMatrix& ke);\n\n\t//! return the nonzeroes in the sparse matrix\n\tsize_t NonZeroes() { return m_pA->NonZeroes(); }\n\n\t//! return the number of rows\n\tint Rows() { return m_pA->Rows(); }\n\n\t//! converts a FEGlobalMatrix to a SparseMatrix\n\toperator SparseMatrix* () { return m_pA; }\n\n\t//! converts a FEGlobalMatrix to a SparseMatrix\n\toperator SparseMatrix& () { return *m_pA;}\n\n\t//! return a pointer to the sparse matrix\n\tSparseMatrix* GetSparseMatrixPtr() { return m_pA; }\n\n\t//! zero the sparse matrix\n\tvoid Zero() { m_pA->Zero(); }\n\n\t//! get the sparse matrix profile\n\tSparseMatrixProfile* GetSparseMatrixProfile() { return m_pMP; }\n\npublic:\n\tvoid build_begin(int neq);\n\tvoid build_add(std::vector<int>& lm);\n\tvoid build_end();\n\tvoid build_flush();\n\nprotected:\n\tSparseMatrix*\tm_pA;\t//!< the actual global stiffness matrix\n\tbool\t\t\tm_delA;\t//!< delete A in destructor\n\n\t// The following data structures are used to incrementally\n\t// build the profile of the sparse matrix\n\n\tSparseMatrixProfile*\tm_pMP;\t\t//!< profile of sparse matrix\n\tSparseMatrixProfile\t\tm_MPs;\t\t//!< the \"static\" part of the matrix profile\n\tvector< vector<int> >\tm_LM;\t\t//!< used for building the stiffness matrix\n\tint\tm_nlm;\t\t\t\t//!< nr of elements in m_LM array\n};\n"
  },
  {
    "path": "FECore/FEGlobalVector.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEGlobalVector.h\"\n#include \"vec3d.h\"\n#include \"FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFEGlobalVector::FEGlobalVector(FEModel& fem, vector<double>& R, vector<double>& Fr) : m_fem(fem), m_R(R), m_Fr(Fr)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEGlobalVector::~FEGlobalVector()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEGlobalVector::Assemble(vector<int>& en, vector<int>& elm, vector<double>& fe, bool bdom)\n{\n\tvector<double>& R = m_R;\n\n\t// assemble the element residual into the global residual\n\tint ndof = (int)fe.size();\n\tfor (int i=0; i<ndof; ++i)\n\t{\n\t\tint I = elm[i];\n\t\tif ( I >= 0) {\n#pragma omp atomic\n\t\t\tR[I] += fe[i];\n\t\t}\n// TODO: Find another way to store reaction forces\n\t\telse if (-I-2 >= 0) {\n#pragma omp atomic\n\t\t\tm_Fr[-I-2] -= fe[i];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo This function does not add to m_Fr. Is this a problem?\nvoid FEGlobalVector::Assemble(vector<int>& lm, vector<double>& fe)\n{\n\tvector<double>& R = m_R;\n\tconst int n = (int) lm.size();\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tint nid = lm[i];\n\t\tif (nid >= 0) {\n#pragma omp atomic\n\t\t\tR[nid] += fe[i];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! assemble a nodel value\nvoid FEGlobalVector::Assemble(int nodeId, int dof, double f)\n{\n\t// get the equation number\n\tFENode& node = m_fem.GetMesh().Node(nodeId);\n\tint n = node.m_ID[dof];\n\n\t// assemble into global vector\n\tif (n >= 0) {\n#pragma omp atomic\n\t\tm_R[n] += f;\n\t}\n}\n"
  },
  {
    "path": "FECore/FEGlobalVector.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n//! This class represents a global system array. It provides functions to assemble\n//! local (element) vectors into this array\n//! TODO: remove FEModel dependency!\nclass FECORE_API FEGlobalVector\n{\npublic:\n\t//! constructor\n\tFEGlobalVector(FEModel& fem, std::vector<double>& R, std::vector<double>& Fr);\n\n\t//! destructor\n\tvirtual ~FEGlobalVector();\n\n\t//! Assemble the element vector into this global vector\n\tvirtual void Assemble(std::vector<int>& en, std::vector<int>& elm, std::vector<double>& fe, bool bdom = false);\n\n\t//! Assemble into this global vector\n\tvirtual void Assemble(std::vector<int>& lm, std::vector<double>& fe);\n\n\t//! assemble a nodel value\n\tvirtual void Assemble(int node, int dof, double f);\n    \n\t//! access operator\n\tdouble& operator [] (int i) { return m_R[i]; }\n\n\t//! Get the FE model\n\tFEModel& GetFEModel() { return m_fem; }\n\n\t//! get the size of the vector\n\tint Size() const { return (int) m_R.size(); }\n\n\toperator std::vector<double>& () { return m_R; }\n\nprotected:\n\tFEModel&\t\t\tm_fem;\t//!< model\n\tstd::vector<double>&\t\tm_R;\t//!< residual\n\tstd::vector<double>&\t\tm_Fr;\t//!< nodal reaction forces \\todo I want to remove this\n};\n"
  },
  {
    "path": "FECore/FEInitialCondition.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEInitialCondition.h\"\n#include \"DumpStream.h\"\n#include \"FEMaterialPoint.h\"\n#include \"FENode.h\"\n#include \"FEModel.h\"\n\nFEInitialCondition::FEInitialCondition(FEModel* pfem) : FEStepComponent(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FENodalIC, FEInitialCondition)\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENodalIC::FENodalIC(FEModel* fem) : FEInitialCondition(fem), m_dofs(fem)\n{\n\tm_nodeSet = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// set the nodeset for this component\nvoid FENodalIC::SetNodeSet(FENodeSet* nset)\n{\n\tm_nodeSet = nset;\n}\n\n//-----------------------------------------------------------------------------\n// get the node set\nFENodeSet* FENodalIC::GetNodeSet()\n{\n\treturn m_nodeSet;\n}\n\n//-----------------------------------------------------------------------------\n// set the list of degrees of freedom\nvoid FENodalIC::SetDOFList(const FEDofList& dofList)\n{\n\tm_dofs = dofList;\n}\n\n//-----------------------------------------------------------------------------\nbool FENodalIC::Init()\n{\n\tif (m_nodeSet == nullptr) return false;\n\treturn FEInitialCondition::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalIC::Activate()\n{\n\tFEStepComponent::Activate();\n\tif (m_dofs.IsEmpty()) return;\n\n\tint dofs = (int)m_dofs.Size();\n\tstd::vector<double> val(dofs, 0.0);\n\n\tint N = (int)m_nodeSet->Size();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tFENode& node = *m_nodeSet->Node(i);\n\n\t\t// get the nodal values\n\t\tGetNodalValues(i, val);\n\t\t\n\t\tfor (int j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tnode.set(m_dofs[j], val[j]);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FENodalIC::Serialize(DumpStream& ar)\n{\n\tFEStepComponent::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tar & m_dofs;\n\tar & m_nodeSet;\n}\n\n//======================================================================================\nBEGIN_FECORE_CLASS(FEInitialDOF, FENodalIC)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list)\");\n\tADD_PARAMETER(m_data, \"value\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEInitialDOF::FEInitialDOF(FEModel* pfem) : FENodalIC(pfem)\n{\n\tm_dof = -1;\n\tm_data = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nFEInitialDOF::FEInitialDOF(FEModel* fem, int ndof, FENodeSet* nset) : FENodalIC(fem)\n{\n\tSetDOF(ndof);\n\tSetNodeSet(nset);\n\tm_data = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialDOF::SetDOF(int ndof) { m_dof = ndof; }\n\n//-----------------------------------------------------------------------------\nbool FEInitialDOF::SetDOF(const char* szdof)\n{\n\tFEModel* fem = GetFEModel();\n\tint ndof = fem->GetDOFIndex(szdof);\n\tassert(ndof >= 0);\n\tif (ndof < 0) return false;\n\tSetDOF(ndof);\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEInitialDOF::Init()\n{\n\tif (FENodalIC::Init() == false) return false;\n\tif (m_dof == -1) return false;\n\tFEDofList dofs(GetFEModel());\n\tif (dofs.AddDof(m_dof) == false) return false;\n\tSetDOFList(dofs);\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialDOF::Serialize(DumpStream& ar)\n{\n\tFEInitialCondition::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEInitialDOF::SetValue(double v)\n{\n\tm_data = v;\n}\n\n//-----------------------------------------------------------------------------\n// return the values for node i\nvoid FEInitialDOF::GetNodalValues(int inode, std::vector<double>& val)\n{\n\tassert(val.size() == 1);\n\tconst FENodeSet& nset = *GetNodeSet();\n\tint nid = nset[inode];\n\tconst FENode& node = *nset.Node(inode);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = inode;\n\n\tval[0] = m_data(mp);\n}\n"
  },
  {
    "path": "FECore/FEInitialCondition.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEStepComponent.h\"\n#include <vector>\n#include \"FENodeDataMap.h\"\n#include \"FENodeSet.h\"\n#include \"FEDofList.h\"\n#include \"FEModelParam.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for defining initial conditions.\n//! Initial conditions can be used to set the initial state of the model in an analysis. \nclass FECORE_API FEInitialCondition : public FEStepComponent\n{\n\tFECORE_SUPER_CLASS(FEIC_ID)\n\tFECORE_BASE_CLASS(FEInitialCondition);\n\npublic:\n\tFEInitialCondition(FEModel* pfem);\n};\n\n//-----------------------------------------------------------------------------\n// Base class for initial conditions applied to node sets\nclass FECORE_API FENodalIC : public FEInitialCondition\n{\npublic:\n\tFENodalIC(FEModel* fem);\n\n\t// set the nodeset for this component\n\tvoid SetNodeSet(FENodeSet* nset);\n\n\t// get the node set\n\tFENodeSet* GetNodeSet();\n\n\t// set the list of degrees of freedom\n\tvoid SetDOFList(const FEDofList& dofList);\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// return the values for node i\n\tvirtual void GetNodalValues(int inode, std::vector<double>& values) = 0;\n\npublic:\n\tvoid Activate() override;\n\n\tbool Init() override;\n\nprotected:\n\tFEDofList\t\tm_dofs;\n\tFENodeSet*\t\tm_nodeSet;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Class representing an initial condition on a degree of freedom\nclass FECORE_API FEInitialDOF : public FENodalIC\n{\npublic:\n\tFEInitialDOF(FEModel* pfem);\n\tFEInitialDOF(FEModel* fem, int ndof, FENodeSet* nset);\n\n\tvoid SetDOF(int ndof);\n\tbool SetDOF(const char* szdof);\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tbool Init() override;\n\n\t// return the values for node i\n\tvoid GetNodalValues(int inode, std::vector<double>& values) override;\n\n\tvoid SetValue(double v);\n\nprotected:\n\tint\t\t\t\tm_dof;\t\t//!< degree of freedom\n\tFEParamDouble\tm_data;\t\t//!< nodal values\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEItemList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEItemList.h\"\n#include \"DumpStream.h\"\n#include \"FEModel.h\"\n#include \"FENodeSet.h\"\n#include \"FEFacetSet.h\"\n#include \"FEElementSet.h\"\n#include \"FESegmentSet.h\"\n\nFEItemList::FEItemList(FEModel* fem, FEItemType type) : m_type(type)\n{\n\tm_mesh = nullptr;\n\tif (fem)\n\t{\n\t\tm_mesh = &fem->GetMesh();\n\t}\n}\n\nFEItemList::FEItemList(FEMesh* mesh, FEItemType type) : m_type(type)\n{\n\tm_mesh = mesh;\n}\n\nFEItemList::~FEItemList() {}\n\nvoid FEItemList::Serialize(DumpStream& ar)\n{\n\tar & m_name;\n}\n\n// get the mesh\nFEMesh* FEItemList::GetMesh() const\n{\n\treturn m_mesh;\n}\n\nvoid FEItemList::SetMesh(FEMesh* mesh)\n{\n\tm_mesh = mesh;\n}\n\nconst std::string& FEItemList::GetName() const\n{\n\treturn m_name;\n}\n\nvoid FEItemList::SetName(const std::string& name)\n{\n\tm_name = name;\n}\n\nFEItemList* FEItemList::LoadClass(DumpStream& ar, FEItemList* p)\n{\n\tint ntype = -1;\n\tar >> ntype;\n\tFEItemList* pi = nullptr;\n\tFEModel* fem = &ar.GetFEModel();\n\tswitch (ntype)\n\t{\n\tcase FE_NODE_SET   : pi = new FENodeSet   (fem); break;\n\tcase FE_FACET_SET  : pi = new FEFacetSet  (fem); break;\n\tcase FE_ELEMENT_SET: pi = new FEElementSet(fem); break;\n\tcase FE_SEGMENT_SET: pi = new FESegmentSet(fem); break;\n\tdefault:\n\t\tassert(false);\n\t\tbreak;\n\t}\n\t\n\treturn pi;\n}\n\nvoid FEItemList::SaveClass(DumpStream& ar, FEItemList* p)\n{\n\tint ntype = (int) p->Type();\n\tar << (int)ntype;\n}\n"
  },
  {
    "path": "FECore/FEItemList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <string>\n#include \"fecore_api.h\"\n#include \"FECoreBase.h\"\n\nclass FEMesh;\n\nclass FECORE_API FEItemList// : public FECoreBase\n{\npublic:\n\tenum FEItemType {\n\t\tFE_NODE_SET,\n\t\tFE_ELEMENT_SET,\n\t\tFE_FACET_SET,\n\t\tFE_SEGMENT_SET\n\t};\n\npublic:\n\tFEItemList(FEModel* fem, FEItemType type);\t// TODO: remove\n\tFEItemList(FEMesh* mesh, FEItemType type);\n\tvirtual ~FEItemList();\n\n\t// get the mesh\n\tFEMesh* GetMesh() const;\n\n\tvoid SetMesh(FEMesh* mesh);\n\n\tconst std::string& GetName() const;\n\tvoid SetName(const std::string& name);\n\n\tFEItemType Type() const { return m_type; }\n\npublic:\n\tvirtual void Serialize(DumpStream& ar);\n\n\tstatic FEItemList* LoadClass(DumpStream& ar, FEItemList* p);\n\tstatic void SaveClass(DumpStream& ar, FEItemList* p);\n\nprotected:\n\tFEMesh*\t\tm_mesh;\n\tFEItemType\tm_type;\n\n\tstd::string\tm_name;\n};\n"
  },
  {
    "path": "FECore/FELevelStructure.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELevelStructure.h\"\n#include <stdlib.h>\n#include <queue>\n#include <stack>\n#include \"vector.h\"\n#include <assert.h>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n\nFELevelStructure::FELevelStructure()\n{\n\tm_pNL = 0;\n}\n\n//-----------------------------------------------------------------------------\n\nFELevelStructure::~FELevelStructure()\n{\n\n}\n\nFELevelStructure* FELevelStructure::m_pthis = 0;\n\n//-----------------------------------------------------------------------------\n//! the compare function compares the degree of two nodes. This function is used\n//! in the FELevelStructure::SortLevels function. Note the use of the static\n//! this pointer to identify the level structure that called this function.\n\nint FELevelStructure::compare(const void* e1, const void* e2)\n{\n\tint n1 = *((int*) e1);\n\tint n2 = *((int*) e2);\n\n\tFENodeNodeList& L = *(m_pthis->m_pNL);\n\n\treturn (L.Valence(n1) - L.Valence(n2));\n}\n\n//-----------------------------------------------------------------------------\n//! This function creates a level structure rooted at node nroot of the graph L\n//! The root is placed in level 0 and then all nodes adjacent to nodes that have\n//! been placed in the level structure are placed in the next level.\n\nvoid FELevelStructure::Create(FENodeNodeList& L, int nroot)\n{\n\tint i, n, m, *pn;\n\n\tint N = L.Size();\n\tm_pNL = &L;\n\n\t// create the node array. This array will store\n\t// for each node to which level it belongs. We initialize\n\t// the array with -1 to indicate that no node has been\n\t// assigned to a level\n\tm_node.assign(N, -1);\n\n\t// place all nodes in a level. We use a queue for this\n\t// since we want nodes to be processed in a first come\n\t// first serve way. The root node is placed in level 0\n\t// and then all nodes adjacent to processed nodes are\n\t// placed in the next level.\n\tqueue<int> NQ;\n\tint node = nroot;\n\tNQ.push(node);\n\tm_node[nroot] = 0;\n\tint level;\n\twhile (NQ.size() > 0)\n\t{\n\t\t// get a node from the queue\n\t\tnode = NQ.front(); NQ.pop();\n\n\t\t// get the next level index\n\t\tlevel = m_node[node] + 1;\n\n\t\t// now we have to place all neighbours on the queue\n\t\tn = L.Valence(node);\n\t\tpn = L.NodeList(node);\n\t\tfor (i=0; i<n; ++i) \n\t\t{\n\t\t\t// get an adjacent node\n\t\t\tm = pn[i];\n\n\t\t\t// make sure the node has not been assigned a level yet\n\t\t\tif (m_node[m] < 0)\n\t\t\t{\n\t\t\t\t// assign the node to a  level\n\t\t\t\tm_node[m] = level;\n\n\t\t\t\t// push the node on the queue\n\t\t\t\tNQ.push(m);\n\t\t\t}\n\t\t}\n\t}\n\n\t// determine the total nr of levels we generated\n\tint nlevels = 0;\n\tfor (i=0; i<N; ++i)\n\t\tif (m_node[i] > nlevels) nlevels = m_node[i];\n\t++nlevels;\n\n\t// allocate levels data\n\tm_lval.assign(nlevels, 0);\n\tm_pl.resize(nlevels);\n\tm_nref.resize(N);\n\n\t// At this point it is important to realize that\n\t// there still may be nodes that are not assigned to \n\t// a level. The reason is that these nodes are not connected\n\t// to the component that the root is part of. When looping\n\t// over all nodes (as below) it is important to explicitly\n\t// check whether the node belongs to the level structure or not.\n\t// Nodes that are not part of it have m_node[i] < 0\n\n\t// count the width of each level\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tif (m_node[i] >= 0) ++m_lval[ m_node[i] ];\n\t}\n\n\t// set nref pointers\n\tm_pl[0] = 0;\n\tm_nwidth = m_lval[0];\n\tfor (i=1; i<nlevels; ++i)\n\t{\n\t\tm_pl[i] = m_pl[i-1] + m_lval[i-1];\n\t\tif (m_lval[i] > m_nwidth) m_nwidth = m_lval[i];\n\t}\n\n\t// reset the valence\n\tzero(m_lval);\n\n\t// fill the nref list and recount the width of each level\n\t// remember to see if the node is part of the level structure\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tn = m_node[i];\n\t\tif (n >= 0)\n\t\t{\n\t\t\tm_nref[ m_pl[n] + m_lval[n] ] = i;\n\t\t\t++m_lval[n];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function sorts the nodes in each level from level l0 to l1 in order\n//! of increasing degree. Note that the static this pointer gets set since the\n//! compare function has to be static and therefore will not receive the this function\n//! from the calling class member\n\nvoid FELevelStructure::SortLevels(int l0, int l1)\n{\n\tint n, *pn;\n\tm_pthis = this;\n\tfor (int i=l0; i<=l1; ++i)\n\t{\n\t\tn = Valence(i);\n\t\tpn = NodeList(i);\n\t\tqsort(pn, n, sizeof(int), compare);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function merges two level structures into one. The merging algorithm below\n//! usually returns a level structure that has a smaller width than either L1 or L2.\n//! The swap variable will be set to true if second index of the node-pair array G \n//! was used of the first component.\n\nvoid FELevelStructure::Merge(FELevelStructure& L1, FELevelStructure& L2, bool& bswap)\n{\n\tint i, j, m, l1, l2;\n\n\t// get the node list that was used\n\t// to generate L1 and L2\n\tFENodeNodeList& NL = *L1.m_pNL;\n\n\t// store the node list for later use\n\tm_pNL = L1.m_pNL;\n\n\t// get the level depth of L1 and \n\t// make sure that it is the same as L2's\n\tint k = L1.Depth();\n\tassert( k == L2.Depth()); \n\n\t// get the level widths of L1 and L2\n\tint w1 = L1.Width();\n\tint w2 = L2.Width();\n\n\t// get the number of nodes\n\tint N = NL.Size();\n\n\t// create the valence array\n\tm_lval.assign(k, 0);\n\n\t// create the nodal array\n\tm_node.assign(N, -1);\n\n\t// In a moment nodes of the graph NL will be marked\n\t// as \"removed\". This will split the graph into \n\t// several disconnected components. The comp array\n\t// will store for each node the component to which\n\t// it belongs.\n\n\t// create the component array\n\t// this array will store for each node\n\t// the index of the component\n\t// the nodes in the \"0\" component are the ones\n\t// that are marked as \"removed\" from the graph\n\tstd::vector<int> comp; \n\tcomp.assign(N, -1);\n\n\t// fill the level pair array G will store for each\n\t// node the index of the level that it has in L1 and\n\t// the reversed index of level L2.\n\t// Note that l1 < 0 for nodes that are not part of L1\n\t// and similar for l2.\n\tvector<int>\tG1(N), G2(N);\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tl1 = L1.m_node[i];\n\t\tl2 = L2.m_node[i];\n\n\t\tif ((l1>=0) && (l2>=0))\n\t\t{\n\t\t\tG1[i] = l1;\n\t\t\tG2[i] = k-1-l2;\n\n\t\t\tif (G1[i] == G2[i]) \n\t\t\t{\n\t\t\t\tm_node[i] = l1;\n\t\t\t\t++m_lval[l1];\n\t\t\t\tcomp[i] = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// node i does not belong to L1 or L2\n\t\t\tG1[i] = -1;\n\t\t\tG2[i] = -1;\n\t\t}\n\t}\n\n\t// Next, we find all connected components. A component\n\t// is created by finding an unprocessed node and attaching all\n\t// nodes that can be reached from this node. Remember that nodes\n\t// which are assigned to component 0 are considered as removed\n\t// from the graph\n\tint nc = 1;\n\tstack<int> NS;\n\tdo\n\t{\n\t\t// find a node that has not been assigned to a component\n\t\t// (and that belongs to the graph (ie. node[i]>0))\n\t\tint node = -1;\n\t\tfor (i=0; i<N; ++i)\n\t\t{\n\t\t\tif ((comp[i] == -1) && (L1.m_node[i] >= 0))\n\t\t\t{\n\t\t\t\tnode = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// if we didn't find one, we can stop\n\t\tif (node == -1) break;\n\n\t\t// assign the node and all adjacent nodes to a component\n\t\tNS.push(node);\n\t\tcomp[node] = nc;\n\t\twhile (!NS.empty())\n\t\t{\n\t\t\tnode = NS.top(); NS.pop();\n\n\t\t\tint n = NL.Valence(node);\n\t\t\tint* pn = NL.NodeList(node);\n\t\t\tfor (i=0; i<n; ++i)\n\t\t\t\tif (comp[pn[i]] == -1) \n\t\t\t\t{\n\t\t\t\t\tcomp[pn[i]] = nc;\n\t\t\t\t\tNS.push(pn[i]);\n\t\t\t\t}\n\t\t}\n\n\t\t// increase the component counter\n\t\t++nc;\n\t}\n\twhile (true);\n\n\t// if we found more than 1 component than we'll have the process\n\t// each component seperatly. Note that we will only have one component\n\t// if all node-pairs of G are in the form (i,i). In that case all nodes\n\t// are placed in level 0, ie. are considered as \"removed\"\n\tif (nc>1)\n\t{\n\t\t// the vectors ni, hi and li will help us determine where to place\n\t\t// a node of a component\n\t\tstd::vector<int> ni(k);\n\t\tstd::vector<int> hi(k);\n\t\tstd::vector<int> li(k);\n\n\t\t// Next, we have to create the components explicitly. The Comp arrays\n\t\t// will store for each component a list of nodes that belongs to this\n\t\t// component. We do this in two steps. First, we determine the sizes\n\t\t// of each component so that we can allocate the Comp array. Next, we\n\t\t// fill the Comp array by looping over all nodes and placing each node\n\t\t// in the correct component.\n\n\t\t// count the elements of the components\n\t\t// and determing the maximum component size\n\t\tstd::vector<int> cc;\n\t\tcc.assign(nc, 0);\n\t\tint ncmax = 0;\n\t\tfor (i=0; i<N; ++i) \n\t\t{\n\t\t\t// make sure the node belongs to the graph L\n\t\t\tif (comp[i] >= 0)\n\t\t\t{\n\t\t\t\t++cc[ comp[i] ];\n\t\t\t\tif (cc[ comp[i] ] > ncmax) ncmax = cc[comp[i]];\n\t\t\t}\n\t\t}\n\n\t\t// create the component arrays\n\t\tstd::vector< std::vector<int> > Comp(nc);\n\t\tfor (i=0; i<nc; ++i) \n\t\t{\n\t\t\tComp[i].resize(cc[i]);\n\t\t\tcc[i] = 0;\n\t\t}\n\n\t\t// fill the components\n\t\tfor (i=0; i<N; ++i)\n\t\t{\n\t\t\tm = comp[i];\n\t\t\tif (m>=0)\n\t\t\t{\n\t\t\t\tComp[m][cc[m]] = i;\n\t\t\t\t++cc[m];\n\t\t\t}\n\t\t}\n\n\t\t// Next, we loop over all components and place each node of the \n\t\t// component in a level. The level is determined by the node-pair\n\t\t// graph G. We can use either G[i][0] or G[i][1] for node i. Which\n\t\t// one gets picked determines on some criteria described below.\n\t\t// Note that we skip the \"0\" component since the nodes of this component\n\t\t// have already been assigned to a level.\n\t\tint ns = -1, nsm;\n\t\tfor (i=1; i<nc; ++i)\n\t\t{\n\t\t\t// we need to loop over all components in order of increasing size.\n\t\t\t// In stead of actually sorting the components we just find the next\n\t\t\t// smallest component by looping again over all components. At the bottom\n\t\t\t// we set cc[i] of the smallest component to -1 so that it can't get\n\t\t\t// picked anymore.\n\t\t\tnsm = ncmax;\n\t\t\tint ncomp = -1;\n\t\t\tfor (j=1; j<nc; ++j)\n\t\t\t{\n\t\t\t\tif ((cc[j] >= ns) && (cc[j] <= nsm))\n\t\t\t\t{\n\t\t\t\t\tncomp = j;\n\t\t\t\t\tnsm = cc[j];\n\t\t\t\t}\n\t\t\t}\n\t\t\tns = nsm;\n\t\t\tassert(ncomp > 0);\n\n\t\t\t// calculate the vectors ni, hi, li\n\t\t\t// ni[i] = number of nodes in level i\n\t\t\t// li[i] = ni + nodes that would be put in level i based on G[node][0]\n\t\t\t// hi[i] = ni + nodes that would be put in level i based on G[node][1]\n\t\t\tfor (j=0; j<k; ++j)\n\t\t\t{\n\t\t\t\tli[j] = hi[j] = ni[j] = m_lval[j];\n\t\t\t}\n\n\t\t\tstd::vector<int>& pn = Comp[ncomp];\n\t\t\tfor (j=0; j<cc[ncomp]; ++j)\n\t\t\t{\n\t\t\t\tm = pn[j];\n\t\t\t\t++hi[ G1[m] ];\n\t\t\t\t++li[ G2[m] ];\n\t\t\t}\n\n\t\t\t// next, we determint h0 and l0, where\n\t\t\t// h0 = max{hi[i] : hi[i] - ni[i] > 0} and similar for l0\n\t\t\tint h0 = -1, l0 = -1;\n\t\t\tfor (j=0; j<k; ++j)\n\t\t\t{\n\t\t\t\tif ((hi[j] > h0) && (hi[j] > ni[j])) h0 = hi[j];\n\t\t\t\tif ((li[j] > l0) && (li[j] > ni[j])) l0 = li[j];\n\t\t\t}\n\t\t\tassert(h0 >= 0);\n\t\t\tassert(l0 >= 0);\n\n\t\t\t// Next we can assign the nodes of the component to a level.\n\t\t\t// the criteria are:\n\t\t\t// if (h0 < l0) place the node in level G[i][0]\n\t\t\t// if (l0 < h0) place the node in level G[i][1]\n\t\t\t// if (l0 == h0) use the level of the rooted level structure \n\t\t\t//   with smallest width\n\t\t\tif ((h0 < l0) || ((h0 == l0) && (w1 <= w2)))\n\t\t\t{\n\t\t\t\tstd::vector<int>& pn = Comp[ncomp];\n\t\t\t\tfor (j=0; j<cc[ncomp]; ++j)\n\t\t\t\t{\n\t\t\t\t\tm = pn[j];\n\t\t\t\t\tm_node[m] = G1[m];\n\t\t\t\t\t++m_lval[ G1[m] ];\n\t\t\t\t}\n\t\t\t\tif (i==1) bswap = false;\n\t\t\t}\n\t\t\telse if ((l0 < h0) || ((h0 == l0) && (w1 > w2)))\n\t\t\t{\n\t\t\t\tstd::vector<int>& pn = Comp[ncomp];\n\t\t\t\tfor (j=0; j<cc[ncomp]; ++j)\n\t\t\t\t{\n\t\t\t\t\tm = pn[j];\n\t\t\t\t\tm_node[m] = G2[m];\n\t\t\t\t\t++m_lval[ G2[m] ];\n\t\t\t\t}\n\t\t\t\tif (i==1) bswap = true;\n\t\t\t}\n\n\t\t\t// set cc[ncomp] to an impossible value so that it won't get picked any more\n\t\t\t// in the search for the next smallest component\n\t\t\tcc[ncomp] = -1;\n\t\t}\n\t}\n\n\t// All nodes are now placed in a level and we now the size of each level\n\t// We can now finish allocating and initializing the rest of the level structure data\n\n\t// allocate levels data\n\tm_pl.resize(k);\n\tm_nref.resize(N);\n\n\t// set nref pointers\n\tm_pl[0] = 0;\n\tm_nwidth = m_lval[0];\n\tfor (i=1; i<k; ++i)\n\t{\n\t\tm_pl[i] = m_pl[i-1] + m_lval[i-1];\n\t\tif (m_lval[i] > m_nwidth) m_nwidth = m_lval[i];\n\t}\n\n\t// reset the valence\n\tzero(m_lval);\n\n\t// fill the nref list\n\t// Remember to check to see if the node belongs to the graph\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tm = m_node[i];\n\t\tif (m>=0)\n\t\t{\n\t\t\tm_nref[ m_pl[m] + m_lval[m] ] = i;\n\t\t\t++m_lval[m];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FELevelStructure.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENodeNodeList.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n//! This class implements the idea of a level structure\n\n//! A level structures groups the nodes per level. The basic notion is that \n//! (1) each node in level 1 is adjacent to a node in level 1 and/or level 2. \n//! (2) each node in level k is adjacent to a node in level k and/or level k-1\n//! (3) each node in level 1<i<k is adjacent to a node in level i-1, i, or i+1\n//! The width of the level structure is the largest width of any level.\n//!\n//! The level structure is used by the node - reorder algorithm implemented in\n//! the FENodeReorder class.\n\nclass FECORE_API FELevelStructure\n{\npublic:\n\t//! default constructor\n\tFELevelStructure();\n\n\t//! destructor\n\tvirtual ~FELevelStructure();\n\n\t//! copy constructor\n\tFELevelStructure(FELevelStructure& L)\n\t{\n\t\tm_lval = L.m_lval;\n\t\tm_nref = L.m_nref;\n\t\tm_pl = L.m_pl;\n\t\tm_node = L.m_node;\n\t\tm_nwidth = L.m_nwidth;\n\t\tm_pNL = L.m_pNL;\n\t}\n\n\t//! assignment operator\n\tFELevelStructure& operator = (FELevelStructure& L)\n\t{\n\t\tm_lval = L.m_lval;\n\t\tm_nref = L.m_nref;\n\t\tm_pl = L.m_pl;\n\t\tm_node = L.m_node;\n\t\tm_nwidth = L.m_nwidth;\n\t\tm_pNL = L.m_pNL;\n\n\t\treturn (*this);\n\t}\n\n\t//! Combine level structures L1 and L2 into one level structure\n\tvoid Merge(FELevelStructure& L1, FELevelStructure& L2, bool& bswap);\n\n\t//! Create a rooted level structure, starting at node nroot\n\tvoid Create(FENodeNodeList& L, int nroot);\n\n\t//! return the depth, ie. the number of levels.\n\tint Depth() const { return (int) m_lval.size(); }\n\n\t//! return the width of the level structure\n\tint Width() { return m_nwidth; }\n\n\t//! return the number of nodes in level l\n\tint Valence(int l) { return m_lval[l]; }\n\n\t//! return a list of nodes in level l\n\tint* NodeList(int l) { return &m_nref[0] + m_pl[l]; }\n\n\t//! return the level that node n is in\n\tint NodeLevel(int n) { return m_node[n]; }\n\n\t//! sort all nodes in levels l0 to l1 in order of increasing degree\n\tvoid SortLevels(int l0, int l1);\n\nprotected:\n\tstd::vector<int>\tm_lval;\t//!< the level valence\n\tstd::vector<int>\tm_nref;\t//!< the nodes in the level\n\tstd::vector<int>\tm_pl;\t//!< start of each level\n\tstd::vector<int>\tm_node;\t//!< the levels to which a nodes belongs\n\n\tFENodeNodeList*\tm_pNL;\t//!< The nodelist that generated the level structure\n\n\tint\tm_nwidth;\t//!< width of level structure\n\n\tstatic FELevelStructure*\tm_pthis;\t//!< this pointer used in static compare function\n\n\t//!< used in sorting the nodes of a level\n\tstatic int compare(const void*, const void*);\n};\n"
  },
  {
    "path": "FECore/FELineSearch.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELineSearch.h\"\n#include \"FENewtonSolver.h\"\n#include \"FEException.h\"\n#include \"DumpStream.h\"\n#include \"log.h\"\n#include <vector>\nusing namespace std;\n\nFELineSearch::FELineSearch(FENewtonSolver* pns) : m_pns(pns)\n{\n\tm_LSmin = 0.01;\n\tm_LStol = 0.9;\n\tm_LSiter = 5;\n\tm_checkJacobians = false;\n}\n\n// serialization\nvoid FELineSearch::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_LSmin & m_LStol & m_LSiter & m_checkJacobians;\n}\n\n//! Performs a linesearch on a NR iteration\n//! The description of this method can be found in:\n//!    \"Nonlinear Continuum Mechanics for Finite Element Analysis\", \tBonet & Wood.\n//\n//! \\todo Find a different way to update the deformation based on the ls.\n//! For instance, define a di so that ui = s*di. Also, define the \n//! position of the nodes at the previous iteration.\ndouble FELineSearch::DoLineSearch(double s)\n{\n\tassert(m_pns);\n\n\tdouble smin = s;\n\n\tdouble a, A, B, D;\n\tdouble r0, r1, r;\n\n\t// max nr of line search iterations\n\tint nmax = m_LSiter;\n\tint n = 0;\n\n\t// vectors\n\tvector<double>& ui = m_pns->m_ui;\n\tvector<double>& R0 = m_pns->m_R0;\n\tvector<double>& R1 = m_pns->m_R1;\n\n\t// initial energy\n\tr0 = ui*R0;\n\n\tdouble rmin = fabs(r0);\n\n\tFENewtonStrategy* ns = m_pns->m_qnstrategy;\n\n\t// ul = ls*ui\n\tvector<double> ul(ui.size());\n\tdo\n\t{\n\t\t// Update geometry\n\t\ts = UpdateModel(ul, ui, s);\n\n\t\t// calculate residual at this point\n\t\tns->Residual(R1, false);\n\n\t\t// make sure we are still in a valid range\n\t\t// (When the check-jacobian flag is on, it's very possible that \n\t\t//  the current line search scale factor drops below the minimum, \n\t\t//  so we don't check the min value when the flag is on.)\n\t\tif ((s < m_LSmin) && !m_checkJacobians)\n\t\t{\n\t\t\t// it appears that we are not converging\n\t\t\t// I found in the NIKE3D code that when this happens,\n\t\t\t// the line search step is simply set to 0.5.\n\t\t\t// so let's try it here too\n\t\t\ts = 0.5;\n\n\t\t\t// reupdate  \n\t\t\tvcopys(ul, ui, s);\n\t\t\tm_pns->Update(ul);\n\n\t\t\t// recalculate residual at this point\n\t\t\tns->Residual(R1, false);\n\n\t\t\t// return and hope for the best\n\t\t\tbreak;\n\t\t}\n\n\t\t// calculate energies\n\t\tr1 = ui*R1;\n\n\t\tif ((n == 0) || (fabs(r1) < rmin))\n\t\t{\n\t\t\tsmin = s;\n\t\t\trmin = fabs(r1);\n\t\t}\n\n\t\t// make sure that r1 does not happen to be really close to zero,\n\t\t// since in that case we won't find any better solution.\n\t\tif (fabs(r1) < 1.e-17) r = 0;\n\t\telse r = fabs(r1 / r0);\n\n\t\tif (r > m_LStol)\n\t\t{\n\t\t\t// calculate the line search step\n\t\t\ta = r0 / r1;\n\n\t\t\tA = 1 + a*(s - 1);\n\t\t\tB = a*(s*s);\n\t\t\tD = B*B - 4 * A*B;\n\n\t\t\t// update the line step\n\t\t\tif (D >= 0)\n\t\t\t{\n\t\t\t\ts = (B + sqrt(D)) / (2 * A);\n\t\t\t\tif (s < 0) s = (B - sqrt(D)) / (2 * A);\n\t\t\t\tif (s < 0) s = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ts = 0.5*B / A;\n\t\t\t}\n\n\t\t\t++n;\n\t\t}\n\t} while ((r > m_LStol) && (n < nmax));\n\n\tif (n >= nmax)\n\t{\n\t\t// max nr of iterations reached.\n\t\t// we choose the line step that reached the smallest energy\n\t\ts = smin;\n\t\tvcopys(ul, ui, s);\n\t\tm_pns->Update(ul);\n\t\tns->Residual(R1, false);\n\t}\n\treturn s;\n}\n\ndouble FELineSearch::UpdateModel(std::vector<double>& ul, std::vector<double>& ui, double s)\n{\n\tif (m_checkJacobians)\n\t{\n\t\tint nmax = m_LSiter;\n\t\tint n = 0;\n\t\tbool negJacFree = true;\n\t\tdo {\n\t\t\tnegJacFree = true;\n\t\t\tvcopys(ul, ui, s);\n\t\t\ttry {\n\t\t\t\tm_pns->Update(ul);\n\t\t\t}\n\t\t\tcatch (NegativeJacobianDetected e)\n\t\t\t{\n\t\t\t\tnegJacFree = false;\n\t\t\t\ts *= 0.5;\n\t\t\t\tfeLogDebugEx(m_pns->GetFEModel(), \"line search cutback : %lg\", s);\n\t\t\t\tn++;\n\t\t\t\tif (n > nmax) throw;\n\t\t\t}\n\t\t} while (!negJacFree);\n\t}\n\telse\n\t{\n\t\tvcopys(ul, ui, s);\n\t\tm_pns->Update(ul);\n\t}\n\treturn s;\n}\n"
  },
  {
    "path": "FECore/FELineSearch.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <vector>\n\nclass FENewtonSolver;\nclass DumpStream;\n\nclass FELineSearch\n{\npublic:\n\tFELineSearch(FENewtonSolver* pns);\n\n\t// Do a line search. ls is the initial search step\n\tdouble DoLineSearch(double ls = 1.0);\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar);\n\nprivate:\n\tdouble UpdateModel(std::vector<double>& ul, std::vector<double>& ui, double s);\n\npublic:\n\tdouble\tm_LSmin;\t\t//!< minimum line search step\n\tdouble\tm_LStol;\t\t//!< line search tolerance\n\tint\t\tm_LSiter;\t\t//!< max nr of line search iterations\n\tbool\tm_checkJacobians;//!< check for negative jacobians\n\nprivate:\n\tFENewtonSolver*\tm_pns;\n};\n"
  },
  {
    "path": "FECore/FELinearConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELinearConstraint.h\"\n#include \"FEMesh.h\"\n#include \"FEModel.h\"\n#include \"DumpStream.h\"\n#include \"log.h\"\n\nBEGIN_FECORE_CLASS(FELinearConstraintDOF, FECoreClass)\n\tADD_PARAMETER(dof, \"dof\", 0, \"$(dof_list)\");\n\tADD_PARAMETER(node, \"node\");\n\tADD_PARAMETER(val, \"value\");\nEND_FECORE_CLASS();\n\nFELinearConstraintDOF::FELinearConstraintDOF(FEModel* fem) : FECoreClass(fem)\n{\n\tnode = dof = -1;\n\tval = 1.0;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FELinearConstraint, FEBoundaryCondition)\n\tADD_PARAMETER(m_parentDof->dof, \"dof\", 0, \"$(dof_list)\");\n\tADD_PARAMETER(m_parentDof->node, \"node\");\n\tADD_PARAMETER(m_off, \"offset\");\n\n\tADD_PROPERTY(m_childDof, \"child_dof\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFELinearConstraint::FELinearConstraint() : FEBoundaryCondition(nullptr)\n{\n\tm_parentDof = nullptr;\n\tm_off = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nFELinearConstraint::FELinearConstraint(FEModel* pfem) : FEBoundaryCondition(pfem)\n{\n\tm_parentDof = new FELinearConstraintDOF(pfem);\n\tm_parentDof->GetParameterList(); // we need to call this to make sure that the parameter list is created.\n\tm_off = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nFELinearConstraint::~FELinearConstraint()\n{\n\tClear();\n}\n\n//-----------------------------------------------------------------------------\n// return offset\ndouble FELinearConstraint::GetOffset() const\n{\n\treturn m_off;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::Clear()\n{\n\tif (m_parentDof) delete m_parentDof; m_parentDof = nullptr;\n\tfor (size_t i = 0; i < m_childDof.size(); ++i) delete m_childDof[i];\n\tm_childDof.clear();\n}\n\n//-----------------------------------------------------------------------------\nFELinearConstraint::FELinearConstraint(const FELinearConstraint& LC) : FEBoundaryCondition(LC.GetFEModel())\n{\n\tm_parentDof = nullptr;\n\tCopyFrom(&(const_cast<FELinearConstraint&>(LC)));\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFELinearConstraint& LC = dynamic_cast<FELinearConstraint&>(*pbc);\n\n\tClear();\n\tif (LC.m_parentDof)\n\t{\n\t\tm_parentDof = new FELinearConstraintDOF(GetFEModel());\n\t\tm_parentDof->GetParameterList(); // NOTE: we need to call this to make sure that the parameter list is created.\n\t\tm_parentDof->node = LC.m_parentDof->node;\n\t\tm_parentDof->dof  = LC.m_parentDof->dof;\n\t\tm_parentDof->val  = LC.m_parentDof->val;\n\t}\n\tm_off = LC.m_off;\n\tint n = (int)LC.m_childDof.size();\n\tvector<FELinearConstraintDOF*>::const_iterator it = LC.m_childDof.begin();\n\tfor (int i = 0; i < n; ++i, ++it)\n\t{\n\t\tFELinearConstraintDOF* d = new FELinearConstraintDOF(GetFEModel());\n\t\td->GetParameterList(); // NOTE: we need to call this to make sure that the parameter list is created.\n\t\td->node = (*it)->node;\n\t\td->dof  = (*it)->dof;\n\t\td->val  = (*it)->val;\n\t\tm_childDof.push_back(d);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::SetParentDof(int dof, int node)\n{\n\tif (m_parentDof == nullptr) {\n\t\tm_parentDof = new FELinearConstraintDOF(GetFEModel());\n\t\tm_parentDof->GetParameterList(); // NOTE: we need to call this to make sure that the parameter list is created.\n\t}\n\tm_parentDof->dof = dof;\n\tm_parentDof->node = node;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::SetParentNode(int node)\n{\n\tif (m_parentDof == nullptr) {\n\t\tm_parentDof = new FELinearConstraintDOF(GetFEModel());\n\t\tm_parentDof->GetParameterList(); // NOTE: we need to call this to make sure that the parameter list is created.\n\t}\n\tm_parentDof->node = node;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::SetParentDof(int dof)\n{\n\tif (m_parentDof == nullptr) {\n\t\tm_parentDof = new FELinearConstraintDOF(GetFEModel());\n\t\tm_parentDof->GetParameterList(); // NOTE: we need to call this to make sure that the parameter list is created.\n\t}\n\tm_parentDof->dof = dof;\n}\n\n//-----------------------------------------------------------------------------\n// get the parent dof\nint FELinearConstraint::GetParentDof() const\n{\n\treturn m_parentDof->dof;\n}\n\n//-----------------------------------------------------------------------------\nint FELinearConstraint::GetParentNode() const\n{\n\treturn m_parentDof->node;\n}\n\n//-----------------------------------------------------------------------------\n// get the child DOF\nconst FELinearConstraintDOF& FELinearConstraint::GetChildDof(int n) const\n{\n\treturn *m_childDof[n];\n}\n\n//-----------------------------------------------------------------------------\nsize_t FELinearConstraint::Size() const\n{\n\treturn m_childDof.size();\n}\n\n//-----------------------------------------------------------------------------\nFELinearConstraint::dof_iterator FELinearConstraint::begin()\n{\n\treturn m_childDof.begin();\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::AddChildDof(int dof, int node, double v)\n{\n\tFELinearConstraintDOF* d = new FELinearConstraintDOF(GetFEModel());\n\td->GetParameterList();\t// we need to call this to make sure that the parameter list is created.\n\td->dof = dof;\n\td->node = node;\n\td->val = v;\n\tm_childDof.push_back(d);\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::AddChildDof(FELinearConstraintDOF* dof)\n{\n\tdof->GetParameterList(); \t// we need to call this to make sure that the parameter list is created.\n\tm_childDof.push_back(dof);\n}\n\n//-----------------------------------------------------------------------------\n// Initialization.\n// Make sure the parent dof does not appear as a child dof\nbool FELinearConstraint::Init()\n{\n\tif (m_parentDof == nullptr) return false;\n\tif (m_parentDof->node < 0)\n\t{\n\t\tfeLogError(\"Invalid node ID for parent node of linear constraint.\");\n\t\treturn false;\n\t}\n\n\tint n = (int)m_childDof.size();\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tFELinearConstraintDOF& childNode = *m_childDof[i];\n\t\tif ((childNode.node == m_parentDof->node) && (childNode.dof == m_parentDof->dof)) return false;\n\t\tif (childNode.node < 0)\n\t\t{\n\t\t\tfeLogError(\"Invalid node ID for child node  of linear constraint.\");\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// This is called during model activation (i.e. at the start of an analysis step)\n// The parent dof is fixed in order to make sure that they are not assigned an equation number.\nvoid FELinearConstraint::Activate()\n{\n\tFEStepComponent::Activate();\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\t// we need the parent node to be fixed so that no equation is allocated\n\tFENode& node = mesh.Node(m_parentDof->node);\n\tnode.set_bc(m_parentDof->dof, DOF_FIXED);\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::Deactivate()\n{\n\tFEStepComponent::Deactivate();\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tFENode& node = mesh.Node(m_parentDof->node);\n\tnode.set_bc(m_parentDof->dof, DOF_OPEN);\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraint::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tm_parentDof->Serialize(ar);\n\t\tint n = (int)m_childDof.size();\n\t\tar << n;\n\t\tvector<FELinearConstraintDOF*>::iterator it = m_childDof.begin();\n\t\tfor (int i = 0; i < n; ++i, ++it) (*it)->Serialize(ar);\n\t}\n\telse\n\t{\n\t\tm_childDof.clear();\n\t\tif (m_parentDof == nullptr) {\n\t\t\tm_parentDof = new FELinearConstraintDOF(GetFEModel());\n\t\t\tm_parentDof->GetParameterList(); // we need to call this to make sure that the parameter list is created.\n\t\t}\n\t\tm_parentDof->Serialize(ar);\n\t\tint n;\n\t\tar >> n;\n\t\tfor (int i=0; i<n; ++i)\n\t\t{\n\t\t\tFELinearConstraintDOF* dof = new FELinearConstraintDOF(GetFEModel());\n\t\t\tdof->GetParameterList(); // we need to call this to make sure that the parameter list is created.\n\t\t\tdof->Serialize(ar);\n\t\t\tm_childDof.push_back(dof);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FELinearConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBoundaryCondition.h\"\n#include \"FECoreClass.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n//! linear constraint\nclass FECORE_API FELinearConstraintDOF : public FECoreClass\n{\npublic:\n\tFELinearConstraintDOF(FEModel* fem);\n\n\tpublic:\n\t\tint\t\tnode;\t// node number\n\t\tint\t\tdof;\t// degree of freedom\n\t\tdouble\tval;\t// coefficient value (ignored for parent dof)\n\nprivate:\n\tFELinearConstraintDOF(const FELinearConstraintDOF&) : FECoreClass(nullptr) {}\n\tvoid operator = (const FELinearConstraintDOF&) {}\n\n\tDECLARE_FECORE_CLASS();\n\tFECORE_BASE_CLASS(FELinearConstraintDOF);\n};\n\nclass FECORE_API FELinearConstraint : public FEBoundaryCondition\n{\npublic:\n\ttypedef std::vector<FELinearConstraintDOF*>::iterator dof_iterator;\n\npublic:\n\t// constructors\n\tFELinearConstraint();\n\tFELinearConstraint(FEModel* pfem);\n\tFELinearConstraint(const FELinearConstraint& LC);\n\n\t~FELinearConstraint();\n\n\tvoid Clear();\n\n\t// copy data\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// initialize the linear constraint\n\tbool Init() override;\n\n\t// make the constraint active\n\tvoid Activate() override;\n\tvoid Deactivate() override;\n\n\t// set the parent degree of freedom\n\tvoid SetParentDof(int dof, int node);\n\tvoid SetParentNode(int node);\n\tvoid SetParentDof(int dof);\n\n\t// get the parent dof\n\tint GetParentDof() const;\n\tint GetParentNode() const;\n\n\t// add a child degree of freedom\n\tvoid AddChildDof(int dof, int node, double v);\n\tvoid AddChildDof(FELinearConstraintDOF* dof);\n\n\t// set the linear constraint offset\n\tvoid SetOffset(double d) { m_off = d; }\n\n\t// return offset\n\tdouble GetOffset() const;\n\n\t// get the child DOF\n\tconst FELinearConstraintDOF& GetChildDof(int n) const;\n\n\tsize_t Size() const;\n\n\tdof_iterator begin();\n\nprotected:\n\tFELinearConstraintDOF*\t\t\t\tm_parentDof;\t// parent degree of freedom\n\tstd::vector<FELinearConstraintDOF*>\tm_childDof;\t\t// list of child dofs\n\tdouble\t\t\tm_off;\t\t\t// offset value\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FELinearConstraintManager.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"FEMesh.h\"\n#include \"FEModel.h\"\n#include \"FEAnalysis.h\"\n#include \"DumpStream.h\"\n#include \"FEDomain.h\"\n#include \"FEGlobalMatrix.h\"\n\n//-----------------------------------------------------------------------------\nFELinearConstraintManager::FELinearConstraintManager(FEModel* fem) : m_fem(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::Clear()\n{\n\tfor (size_t i = 0; i < m_LinC.size(); ++i) delete m_LinC[i];\n\tm_LinC.clear();\n}\n\n//-----------------------------------------------------------------------------\nFELinearConstraintManager::~FELinearConstraintManager()\n{\n\tClear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::CopyFrom(const FELinearConstraintManager& lcm)\n{\n\tClear();\n\tfor (int i=0; i<lcm.LinearConstraints(); ++i)\n\t{\n\t\tFELinearConstraint* lc = fecore_alloc(FELinearConstraint, m_fem);\n\t\tlc->CopyFrom(&(const_cast<FELinearConstraint&>(lcm.LinearConstraint(i))));\n\t\tm_LinC.push_back(lc);\n\t}\n\tInitTable();\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::AddLinearConstraint(FELinearConstraint* lc)\n{\n\tm_LinC.push_back(lc);\n}\n\n//-----------------------------------------------------------------------------\nint FELinearConstraintManager::LinearConstraints() const\n{\n\treturn (int)m_LinC.size();\n}\n\n//-----------------------------------------------------------------------------\nconst FELinearConstraint& FELinearConstraintManager::LinearConstraint(int i) const\n{\n\treturn *m_LinC[i];\n}\n\n//-----------------------------------------------------------------------------\nFELinearConstraint& FELinearConstraintManager::LinearConstraint(int i)\n{\n\treturn *m_LinC[i];\n}\n\n//-----------------------------------------------------------------------------\n//! remove a linear constraint\nvoid FELinearConstraintManager::RemoveLinearConstraint(int i)\n{\n\tFELinearConstraint& lc = *m_LinC[i];\n\tif (lc.IsActive()) lc.Deactivate();\n\tm_LinC.erase(m_LinC.begin() + i);\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_LinC;\n\n\t\tint nr = m_LCT.rows();\n\t\tint nc = m_LCT.columns();\n\t\tar << nr << nc;\n\t\tif (nr*nc > 0)\n\t\t{\n\t\t\tar.write(&m_LCT(0,0), sizeof(int), nr*nc);\n\t\t}\n\t}\n\telse\n\t{\n\t\tFEModel& fem = ar.GetFEModel();\n\n\t\t// linear constraints\n\t\tar >> m_LinC;\n\n\t\tint nr, nc;\n\t\tar >> nr >> nc;\n\t\tif (nr*nc > 0)\n\t\t{\n\t\t\tm_LCT.resize(nr, nc);\n\t\t\tar.read(&m_LCT(0,0), sizeof(int), nr*nc);\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::BuildMatrixProfile(FEGlobalMatrix& G)\n{\n\tint nlin = (int)m_LinC.size();\n\tif (nlin == 0) return;\n\n\tFEAnalysis* pstep = m_fem->GetCurrentStep();\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\t// Add linear constraints to the profile\n\t// TODO: we need to add a function build_add(lmi, lmj) for\n\t// this type of \"elements\". Now we allocate too much memory\n\tvector<int> lm, elm;\n\n\t// do the cross-term\n\t// TODO: I have to make this easier. For instance,\n\t// keep a list that stores for each node the list of\n\t// elements connected to that node.\n\t// loop over all solid elements\n\tfor (int nd = 0; nd<pstep->Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = *pstep->Domain(nd);\n\t\tfor (int i = 0; i<dom.Elements(); ++i)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(i);\n\t\t\tdom.UnpackLM(el, elm);\n\t\t\tint ne = (int)elm.size();\n\n\t\t\t// keep a list of the constraints this element connects to\n\t\t\tvector<int> constraintList;\n\n\t\t\t// see if this element connects to the \n\t\t\t// parent node of a linear constraint ...\n\t\t\tint m = el.Nodes();\n\t\t\tfor (int j = 0; j<m; ++j)\n\t\t\t{\t\n\t\t\t\tint ncols = m_LCT.columns();\n\t\t\t\tfor (int k = 0; k<ncols; ++k)\n\t\t\t\t{\n\t\t\t\t\tint n = m_LCT(el.m_node[j], k);\n\t\t\t\t\tif (n >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t// ... it does so we need to connect the \n\t\t\t\t\t\t// element to the linear constraint\n\t\t\t\t\t\tFELinearConstraint* plc = m_LinC[n];\n\t\t\t\t\t\tconstraintList.push_back(n);\n\t\t\t\t\t\t\n\t\t\t\t\t\tint ns = (int)plc->Size();\n\n\t\t\t\t\t\tlm.resize(ne + ns);\n\t\t\t\t\t\tfor (int l = 0; l<ne; ++l) lm[l] = elm[l];\n\n\t\t\t\t\t\tFELinearConstraint::dof_iterator is = plc->begin();\n\t\t\t\t\t\tfor (int l = ne; l<ne + ns; ++l, ++is) \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint neq = mesh.Node((*is)->node).m_ID[(*is)->dof];\n\t\t\t\t\t\t\tlm[l] = neq;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tG.build_add(lm);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This replaces the commented out section below, which sets up the connectivity \n\t\t\t// for the constraint block of the stiffness matrix. \n\t\t\t// The problem is that this will only work for linear constraints that are connected\n\t\t\t// the element, so not for constraints connected to contact \"elements\". Howerver, I\n\t\t\t// don't think this was working anyway, so this is okay for now.\n\t\t\t// TODO: Replace this with a more generic algorithm that looks at the matrix profile \n\t\t\t// directly instead of element by element. \n\t\t\tif (constraintList.empty() == false)\n\t\t\t{\n\t\t\t\t// do the constraint term\n\t\t\t\tint n = 0;\n\t\t\t\tfor (int i = 0; i<constraintList.size(); ++i) n += (int)m_LinC[constraintList[i]]->Size();\n\t\t\t\tlm.resize(n);\n\t\t\t\tn = 0;\n\t\t\t\tfor (int i = 0; i<constraintList.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFELinearConstraint& lc = *m_LinC[constraintList[i]];\n\t\t\t\t\tint ni = (int)lc.Size();\n\t\t\t\t\tfor (int j = 0; j<ni; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst FELinearConstraintDOF& sj = lc.GetChildDof(j);\n\t\t\t\t\t\tint neq = mesh.Node(sj.node).m_ID[sj.dof];\n\t\t\t\t\t\tlm[n++] = neq;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tG.build_add(lm);\n\t\t\t}\n\t\t}\n\t}\n\n\t// do the constraint term\n\t// NOTE: This block was replaced by the section above which reduces the size\n\t// of the stiffness matrix, but might be less generic (altough not entirely sure\n\t// about that). \n/*\tvector<FELinearConstraint>::iterator ic = m_LinC.begin();\n\tint n = 0;\n\tfor (int i = 0; i<nlin; ++i, ++ic) n += (int) ic->m_childDof.size();\n\tlm.resize(n);\n\tic = m_LinC.begin();\n\tn = 0;\n\tfor (int i = 0; i<nlin; ++i, ++ic)\n\t{\n\t\tint ni = (int)ic->m_childDof.size();\n\t\tvector<FELinearConstraintDOF>::iterator is = ic->m_childDof.begin();\n\t\tfor (int j = 0; j<ni; ++j, ++is) \n\t\t{\n\t\t\tint neq = mesh.Node(is->node).m_ID[is->dof];\n\t\t\tlm[n++] = neq;\n\t\t}\n\t}\n\tG.build_add(lm);\n*/\n}\n\n//-----------------------------------------------------------------------------\n//! This function initializes the linear constraint table (LCT). This table\n//! contains for each dof the linear constraint it belongs to. (or -1 if it is\n//! not constraint)\nbool FELinearConstraintManager::Initialize()\n{\n\tint nlin = LinearConstraints();\n\tif (nlin == 0) return true;\n\n\tfor (int i = 0; i < nlin; ++i)\n\t{\n\t\tFELinearConstraint& lci = *m_LinC[i];\n\t\tif (lci.Init() == false) return false;\n\t}\n\n\treturn true;\n}\n\nvoid FELinearConstraintManager::PrepStep()\n{\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\tfor (int i=0; i<m_LinC.size(); ++i)\n\t{\n\t\tFELinearConstraint& lc = *m_LinC[i];\n\t\tif (lc.IsActive())\n\t\t{\n\t\t\tFENode& node = mesh.Node(lc.GetParentNode());\n\t\t\tdouble u = node.get(lc.GetParentDof());\n\n\t\t\tdouble v = 0;\n\t\t\tfor (int j = 0; j < lc.Size(); ++j)\n\t\t\t{\n\t\t\t\tconst FELinearConstraintDOF& dofj = lc.GetChildDof(j);\n\t\t\t\tFENode& nj = mesh.Node(dofj.node);\n\t\t\t\tv += dofj.val * nj.get(dofj.dof);\n\t\t\t}\n\n\t\t\tm_up[i] = v + lc.GetOffset() - u;\n\t\t}\n\t}\n}\n\nvoid FELinearConstraintManager::InitTable()\n{\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\t// create the linear constraint table\n\tDOFS& fedofs = m_fem->GetDOFS();\n\tint MAX_NDOFS = fedofs.GetTotalDOFS();\n\tm_LCT.resize(mesh.Nodes(), MAX_NDOFS, -1);\n\tm_LCT.set(-1);\n\n\tvector<FELinearConstraint*>::iterator ic = m_LinC.begin();\n\tint nlin = LinearConstraints();\n\tfor (int i = 0; i<nlin; ++i, ++ic)\n\t{\n\t\tFELinearConstraint& lc = *(*ic);\n\t\tif (lc.IsActive())\n\t\t{\n\t\t\tint n = lc.GetParentNode();\n\t\t\tint m = lc.GetParentDof();\n\n\t\t\tm_LCT(n, m) = i;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This gets called during model activation, i.e. activation of permanent model components.\nbool FELinearConstraintManager::Activate()\n{\n\tint nlin = LinearConstraints();\n\tif (nlin == 0) return true;\n\n\t// initialize the lookup table\n\tInitTable();\n\n\t// ensure that none of the parent nodes are child nodes in any of the active linear constraints\n\tfor (int i = 0; i<nlin; ++i)\n\t{\n\t\tFELinearConstraint& lci = *m_LinC[i];\n\t\tif (lci.IsActive())\n\t\t{\n\t\t\tint n = (int)lci.Size();\n\t\t\tfor (int k = 0; k<n; ++k)\n\t\t\t{\n\t\t\t\tconst FELinearConstraintDOF& childDOF = lci.GetChildDof(k);\n\t\t\t\tint n = m_LCT(childDOF.node, childDOF.dof);\n\t\t\t\tif (n != -1)\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// set the prescribed value array\n\tm_up.assign(m_LinC.size(), 0.0);\n\tif (m_LinC.size())\n\t{\n\t\tvector<FELinearConstraint*>::iterator il = m_LinC.begin();\n\t\tfor (int l = 0; l<(int)m_LinC.size(); ++l, ++il) \n\t\t{\n\t\t\tif ((*il)->IsActive()) (*il)->Activate();\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::AssembleResidual(vector<double>& R, vector<int>& en, vector<int>& elm, vector<double>& fe)\n{\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\tint ndof = (int)fe.size();\n\tint ndn = ndof / (int)en.size();\n\tconst int nodes = (int)en.size();\n\n\t// loop over all degrees of freedom of this element\n\tfor (int i = 0; i<ndof; ++i)\n\t{\n\t\tint nodei = i / ndn;\n\t\tif (nodei < nodes) {\n\t\t\t// see if this dof belongs to a linear constraint\n\t\t\tint l = m_LCT(en[nodei], i%ndn);\n\t\t\tif (l >= 0)\n\t\t\t{\n\t\t\t\t// if so, get the linear constraint\n\t\t\t\tFELinearConstraint& lc = *m_LinC[l];\n\t\t\t\tassert(elm[i] == -1);\n\n\t\t\t\t// now loop over all child nodes and\n\t\t\t\t// add the contribution to the residual\n\t\t\t\tint ns = (int)lc.Size();\n\t\t\t\tFELinearConstraint::dof_iterator is = lc.begin();\n\t\t\t\tfor (int j = 0; j < ns; ++j, ++is)\n\t\t\t\t{\n\t\t\t\t\tint I = mesh.Node((*is)->node).m_ID[(*is)->dof];\n\t\t\t\t\tif (I >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble A = (*is)->val;\n#pragma omp atomic\n\t\t\t\t\t\tR[I] += A*fe[i];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearConstraintManager::AssembleStiffness(FEGlobalMatrix& G, vector<double>& R, vector<double>& ui, const vector<int>& en, const vector<int>& lmi, const vector<int>& lmj, const matrix& ke)\n{\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\t// make sure we have a node list\n\t// (rigid matrices will not have the node list set and therefore should be ignored, since\n\t// you cannot use rigid nodes in linear constraints)\n\tif (en.size() == 0) return;\n\n\tint ndof = ke.rows();\n\tint ndn = ndof / (int)en.size();\n\tconst int nodes = (int)en.size();\n\n\tSparseMatrix& K = *(&G);\n\n\t// loop over all stiffness components \n\t// and correct for linear constraints\n\tfor (int i = 0; i<ndof; ++i)\n\t{\n\t\tint nodei = i / ndn;\n\t\tint li = (nodei < nodes ? m_LCT(en[nodei], i%ndn) : -1);\n\t\tfor (int j = 0; j < ndof; ++j)\n\t\t{\n\t\t\tint nodej = j / ndn;\n\t\t\tint lj = (nodej < nodes ? m_LCT(en[nodej], j%ndn) : -1);\n\t\t\tif ((li >= 0) && (lj < 0))\n\t\t\t{\n\t\t\t\t// dof i is constrained\n\t\t\t\tFELinearConstraint& Li = *m_LinC[li];\n\n\t\t\t\tassert(lmi[i] == -1);\n\n\t\t\t\tFELinearConstraint::dof_iterator is = Li.begin();\n\t\t\t\tfor (int k = 0; k < (int)Li.Size(); ++k, ++is)\n\t\t\t\t{\n\t\t\t\t\tint I = mesh.Node((*is)->node).m_ID[(*is)->dof];\n\t\t\t\t\tint J = lmj[j];\n\t\t\t\t\tdouble kij = (*is)->val*ke[i][j];\n\t\t\t\t\tif ((J >= 0) && (I >= 0)) K.add(I, J, kij);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// adjust for prescribed dofs\n\t\t\t\t\t\tJ = -J - 2;\n\t\t\t\t\t\tif ((J >= 0) && (I >= 0)) R[I] -= kij*ui[J];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((lj >= 0) && (li < 0))\n\t\t\t{\n\t\t\t\t// dof j is constrained\n\t\t\t\tFELinearConstraint& Lj = *m_LinC[lj];\n\n\t\t\t\tassert(lmj[j] == -1);\n\n\t\t\t\tFELinearConstraint::dof_iterator js = Lj.begin();\n\t\t\t\tfor (int k = 0; k < (int)Lj.Size(); ++k, ++js)\n\t\t\t\t{\n\t\t\t\t\tint I = lmi[i];\n\t\t\t\t\tint J = mesh.Node((*js)->node).m_ID[(*js)->dof];\n\t\t\t\t\tdouble kij = (*js)->val*ke[i][j];\n\t\t\t\t\tif ((J >= 0) && (I >= 0)) K.add(I, J, kij);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// adjust for prescribed dofs\n\t\t\t\t\t\tJ = -J - 2;\n\t\t\t\t\t\tif ((J >= 0) && (I >= 0)) R[I] -= kij*ui[J];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// adjust right-hand side for inhomogeneous linear constraints\n\t\t\t\tif (Lj.GetOffset() != 0.0)\n\t\t\t\t{\n\t\t\t\t\tdouble ri = ke[i][j] * m_up[lj];\n\t\t\t\t\tint I = lmi[i];\n\t\t\t\t\tif (I >= 0) R[I] -= ri;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((li >= 0) && (lj >= 0))\n\t\t\t{\n\t\t\t\t// both dof i and j are constrained\n\t\t\t\tFELinearConstraint& Li = *m_LinC[li];\n\t\t\t\tFELinearConstraint& Lj = *m_LinC[lj];\n\n\t\t\t\tFELinearConstraint::dof_iterator is = Li.begin();\n\t\t\t\tFELinearConstraint::dof_iterator js = Lj.begin();\n\n\t\t\t\tassert(lmi[i] == -1);\n\t\t\t\tassert(lmj[j] == -1);\n\n\t\t\t\tfor (int k = 0; k < (int)Li.Size(); ++k, ++is)\n\t\t\t\t{\n\t\t\t\t\tjs = Lj.begin();\n\t\t\t\t\tfor (int l = 0; l < (int)Lj.Size(); ++l, ++js)\n\t\t\t\t\t{\n\t\t\t\t\t\tint I = mesh.Node((*is)->node).m_ID[(*is)->dof];\n\t\t\t\t\t\tint J = mesh.Node((*js)->node).m_ID[(*js)->dof];;\n\t\t\t\t\t\tdouble kij = ke[i][j] * (*is)->val*(*js)->val;\n\n\t\t\t\t\t\tif ((J >= 0) && (I >= 0)) K.add(I, J, kij);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// adjust for prescribed dofs\n\t\t\t\t\t\t\tJ = -J - 2;\n\t\t\t\t\t\t\tif ((J >= 0) && (I >= 0)) R[I] -= kij*ui[J];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// adjust for inhomogeneous linear constraints\n\t\t\t\tif (Lj.GetOffset() != 0.0)\n\t\t\t\t{\n\t\t\t\t\tis = Li.begin();\n\t\t\t\t\tfor (int k = 0; k < (int)Li.Size(); ++k, ++is)\n\t\t\t\t\t{\n\t\t\t\t\t\tint I = mesh.Node((*is)->node).m_ID[(*is)->dof];\n\t\t\t\t\t\tdouble ri = (*is)->val * ke[i][j] * m_up[lj];\n\t\t\t\t\t\tif (I >= 0) R[I] -= ri;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This updates the nodal degrees of freedom of the parent nodes.\nvoid FELinearConstraintManager::Update()\n{\n\tFEMesh& mesh = m_fem->GetMesh();\n\n\tint nlin = LinearConstraints();\n\tfor (int n = 0; n<nlin; ++n)\n\t{\n\t\tFELinearConstraint& lc = LinearConstraint(n);\n\t\tif (lc.IsActive())\n\t\t{\n\t\t\t// evaluate the linear constraint\n\t\t\tdouble d = 0;\n\t\t\tint ns = (int)lc.Size();\n\t\t\tFELinearConstraint::dof_iterator si = lc.begin();\n\t\t\tfor (int i = 0; i < ns; ++i, ++si)\n\t\t\t{\n\t\t\t\tFENode& childNode = mesh.Node((*si)->node);\n\t\t\t\td += (*si)->val * childNode.get((*si)->dof);\n\t\t\t}\n\n\t\t\t// assign to parent node\n\t\t\tFENode& parentNode = mesh.Node(lc.GetParentNode());\n\t\t\tparentNode.set(lc.GetParentDof(), d + lc.GetOffset());\n\t\t}\n\t}\n\n\tm_up.assign(m_LinC.size(), 0.0);\n}\n"
  },
  {
    "path": "FECore/FELinearConstraintManager.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FELinearConstraint.h\"\n#include \"table.h\"\n\nclass FEGlobalMatrix;\nclass matrix;\n\n//-----------------------------------------------------------------------------\n// This class helps manage all the linear constraints\nclass FECORE_API FELinearConstraintManager\n{\npublic:\n\tFELinearConstraintManager(FEModel* fem);\n\t~FELinearConstraintManager();\n\n\t// Clear all constraints\n\tvoid Clear();\n\n\t// copy data\n\tvoid CopyFrom(const FELinearConstraintManager& lcm);\n\n\t// serialize linear constraints\n\tvoid Serialize(DumpStream& ar);\n\n\t// build the matrix profile\n\tvoid BuildMatrixProfile(FEGlobalMatrix& G);\n\n\t// add the linear constraint\n\tvoid AddLinearConstraint(FELinearConstraint* lc);\n\n\t// return number of linear constraints\n\tint LinearConstraints() const;\n\n\t// return linear constraint\n\tconst FELinearConstraint& LinearConstraint(int i) const;\n\n\t// return linear constraint\n\tFELinearConstraint& LinearConstraint(int i);\n\n\t//! remove a linear constraint\n\tvoid RemoveLinearConstraint(int i);\n\npublic:\n\t// one-time initialization\n\tbool Initialize();\n\n\t// activation\n\tbool Activate();\n\n\t// assemble element residual into global residual\n\tvoid AssembleResidual(vector<double>& R, vector<int>& en, vector<int>& elm, vector<double>& fe);\n\n\t// assemble element matrix into (reduced) global matrix\n\tvoid AssembleStiffness(FEGlobalMatrix& K, vector<double>& R, vector<double>& ui, const vector<int>& en, const vector<int>& lmi, const vector<int>& lmj, const matrix& ke);\n\n\t// called before the first reformation for each time step\n\tvoid PrepStep();\n\n\t// update nodal variables\n\tvoid Update();\n\nprotected:\n\tvoid InitTable();\n\nprivate:\n\tFEModel* m_fem;\n\tvector<FELinearConstraint*>\tm_LinC;\t\t//!< linear constraints data\n\ttable<int>\t\t\t\t\tm_LCT;\t\t//!< linear constraint table\n\tvector<double>\t\t\t\tm_up;\t\t//!< the inhomogenous component of the linear constraint\n};\n"
  },
  {
    "path": "FECore/FELinearSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELinearSolver.h\"\n#include \"FEModel.h\"\n#include \"LinearSolver.h\"\n#include \"FEGlobalMatrix.h\"\n#include \"log.h\"\n#include \"FENodeReorder.h\"\n#include \"FELinearSystem.h\"\n#include \"FEBoundaryCondition.h\"\n#include \"FEGlobalVector.h\"\n#include \"FEDomain.h\"\n#include \"FENodalLoad.h\"\n#include \"FESurfaceLoad.h\"\n#include \"FEBodyLoad.h\"\n#include \"DumpStream.h\"\n#include \"FELinearConstraintManager.h\"\n\n//-----------------------------------------------------------------------------\n//! constructor\nFELinearSolver::FELinearSolver(FEModel* pfem) : FESolver(pfem)\n{\n\tm_pls = 0;\n\tm_pK = 0;\n\tm_neq = 0;\n\tm_breform = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearSolver::Clean()\n{\n\tif (m_pls) m_pls->Destroy();\n}\n\n//-----------------------------------------------------------------------------\n//! This function sets the degrees of freedom that will be used by this solver.\n//! This is used in the InitEquations method that initializes the equation numbers\n//! and the Update method which maps the solution of the linear system to the nodal\n//! data.\nvoid FELinearSolver::SetDOF(vector<int>& dof)\n{\n\tm_dof = dof;\n}\n\n//-----------------------------------------------------------------------------\nint FELinearSolver::NumberOfEquations() const \n{\n\treturn m_neq;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the linear solver\nLinearSolver* FELinearSolver::GetLinearSolver()\n{\n\treturn m_pls;\n}\n\n//-----------------------------------------------------------------------------\nbool FELinearSolver::Init()\n{\n\tif (FESolver::Init() == false) return false;\n\n\t// Now that we have determined the equation numbers we can continue\n\t// with creating the stiffness matrix. First we select the linear solver\n\t// The stiffness matrix is created in CreateStiffness\n\t// Note that if a particular solver was requested in the input file\n\t// then the solver might already be allocated. That's way we need to check it.\n\tif (m_pls == 0)\n\t{\n\t\tFEModel* fem = GetFEModel();\n\t\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\t\tm_pls = fecore.CreateDefaultLinearSolver(fem);\n\t\tif (m_pls == 0)\n\t\t{\n\t\t\tfeLogError(\"Unknown solver type selected\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tif (m_part.empty() == false)\n\t\t{\n\t\t\tm_pls->SetPartitions(m_part);\n\t\t}\n\t}\n\n\t// allocate storage for the sparse matrix that will hold the stiffness matrix data\n\t// we let the solver allocate the correct type of matrix format\n\tMatrix_Type mtype = MatrixType();\n\tSparseMatrix* pS = m_pls->CreateSparseMatrix(mtype);\n\tif ((pS == 0) && (mtype == REAL_SYMMETRIC))\n\t{\n\t\t// oh, oh, something went wrong. It's probably because the user requested a symmetric matrix for a \n\t\t// solver that wants a non-symmetric. If so, let's force a non-symmetric format.\n\t\tpS = m_pls->CreateSparseMatrix(REAL_UNSYMMETRIC);\n\n\t\tif (pS)\n\t\t{\n\t\t\t// Problem solved! Let's inform the user.\n\t\t\tm_msymm = REAL_UNSYMMETRIC;\n\t\t\tfeLogWarning(\"The matrix format was changed to non-symmetric since the selected linear solver does not support a symmetric format. \\n\");\n\t\t}\n\t}\n\n\tif (pS == 0)\n\t{\n\t\tfeLogError(\"The selected linear solver does not support the requested matrix format.\\nPlease select a different linear solver.\\n\");\n\t\treturn false;\n\t}\n\n\t// clean up the stiffness matrix if we have one\n\tif (m_pK) delete m_pK; m_pK = 0;\n\n\t// Create the stiffness matrix.\n\t// Note that this does not construct the stiffness matrix. This\n\t// is done later in the StiffnessMatrix routine.\n\tm_pK = new FEGlobalMatrix(pS);\n\tif (m_pK == 0)\n\t{\n\t\tfeLogError(\"Failed allocating stiffness matrix\\n\\n\");\n\t\treturn false;\n\t}\n\n\t// Set the matrix formation flag\n\tm_breform = true;\n\n\t// get number of equations\n\tint neq = m_neq;\n\n\t// allocate data structures\n\tm_R.resize(neq);\n\tm_u.resize(neq);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Solve an analysis step\nbool FELinearSolver::SolveStep()\n{\n\t// Make sure we have a linear solver and a stiffness matrix\n\tif (m_pls == 0) return false;\n\tif (m_pK == 0) return false;\n\n\t// reset counters\n\tm_niter = 0;\n\tm_nrhs = 0;\n\tm_nref = 0;\n\tm_ntotref = 0;\n\n\tFEModel& fem = *GetFEModel();\n\n\t// Set up the prescribed dof vector\n\t// The stiffness matrix assembler uses this to update the RHS vector\n\t// for prescribed dofs.\n\tzero(m_u);\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i=0; i<nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *fem.BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.PrepStep(m_u, false);\n\t}\n\n\t// build the right-hand side\n\t// (Is done by the derived class)\n\tzero(m_R);\n\tvector<double> F(m_neq);\n\tFEGlobalVector rhs(fem, m_R, F);\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Residual);\n\t\tForceVector(rhs);\n\t}\n\n\t// increase RHS counter\n\tm_nrhs++;\n\n\t// build the stiffness matrix\n\tReformStiffness();\n\n\t// solve the equations\n\tvector<double> u(m_neq);\n\t{\n\t\tTRACK_TIME(TimerID::Timer_LinSol_Backsolve);\n\t\tif (m_pls->BackSolve(u, m_R) == false)\n\t\t\tthrow LinearSolverFailed();\n\t}\n\n\t// print norms\n\tdouble rnorm = sqrt(m_R*m_R);\n\tdouble unorm = sqrt(u*u);\n\tfeLog(\"\\trhs norm      :  %15le\\n\", rnorm);\n\tfeLog(\"\\tsolution norm :  %15le\\n\", unorm);\n\n\t// the prescribed values are stored in m_u, so let's add it\n\tu += m_u;\n\n\t// update solution\n\tUpdate(u);\n\n\t// increase iteration count\n\tm_niter++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FELinearSolver::ReformStiffness()\n{\n\t// recalculate the shape of the stiffness matrix if necessary\n\tif (m_breform)\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Reform);\n\n\t\tif (!CreateStiffness()) return false;\n\t\t\n\t\t// since it's not likely that the matrix form changes\n\t\t// in a linear analysis, we don't recalculate the form\n\t\tm_breform = false;\n\t}\n\n\t// Make sure it is all set to zero\n\tm_pK->Zero();\n\n\t// calculate the stiffness matrix\n\t// (This is done by the derived class)\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Stiffness);\n\n\t\tFELinearSystem K(GetFEModel(), *m_pK, m_R, m_u, (m_msymm == REAL_SYMMETRIC));\n\t\tif (!StiffnessMatrix(K)) return false;\n\n\t\t// do call back\n\t\tFEModel& fem = *GetFEModel();\n\t\tfem.DoCallback(CB_MATRIX_REFORM);\n\t}\n\n\t// factorize the stiffness matrix\n\t{\n\t\tTRACK_TIME(TimerID::Timer_LinSol_Factor);\n\t\tm_pls->Factor();\n\t}\n\n\t// increase total nr of reformations\n\tm_nref++;\n\tm_ntotref++;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FELinearSolver::CreateStiffness()\n{\n\t// clean up the solver\n\tif (m_pK->NonZeroes()) m_pls->Destroy();\n\n\t// clean up the stiffness matrix\n\tm_pK->Clear();\n\n\t// create the stiffness matrix\n\tfeLog(\"===== reforming stiffness matrix:\\n\");\n\tif (m_pK->Create(GetFEModel(), m_neq, true) == false) \n\t{\n\t\tfeLogError(\"An error occured while building the stiffness matrix\\n\\n\");\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\t// output some information about the direct linear solver\n\t\tint neq = m_pK->Rows();\n\t\tint nnz = m_pK->NonZeroes();\n\t\tfeLog(\"\\tNr of equations ........................... : %d\\n\", neq);\n\t\tfeLog(\"\\tNr of nonzeroes in stiffness matrix ....... : %d\\n\", nnz);\n\t}\n\n\t// Do the preprocessing of the solver\n\t{\n\t\tif (!m_pls->PreProcess()) throw FatalError();\n\t}\n\n\t// done!\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEGlobalMatrix* FELinearSolver::GetStiffnessMatrix()\n{\n\treturn m_pK;\n}\n\n//-----------------------------------------------------------------------------\n//! get the RHS\nstd::vector<double>\tFELinearSolver::GetLoadVector()\n{\n\treturn m_R;\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearSolver::Serialize(DumpStream& ar)\n{\n\tFESolver::Serialize(ar);\n\n\tar & m_breform & m_neq;\n\n\tif (ar.IsSaving() == false)\n\t{\n\t\t// We need to rebuild the stiffness matrix at some point.\n\t\t// Currently this is done during Activation, but we don't\n\t\t// call FEAnalysis::Activate after restart so for now,\n\t\t// I'll just do it here.\n\t\t// TODO: Find a better way.\n\t\tFELinearSolver::Init();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the right-hand side \"force\" vector\nvoid FELinearSolver::ForceVector(FEGlobalVector& R)\n{\n\t// get the modal and mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\tFETimeInfo& tp = fem.GetTime();\n\n\t// loop over model loads\n\tint nml = fem.ModelLoads();\n\tfor (int i = 0; i < nml; ++i)\n\t{\n\t\tFEModelLoad& ml = *fem.ModelLoad(i);\n\t\tif (ml.IsActive()) ml.LoadVector(R);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluate the stiffness matrix\nbool FELinearSolver::StiffnessMatrix(FELinearSystem& K) \n{ \n\treturn false; \n}\n\n//-----------------------------------------------------------------------------\n// This function copies the solution back to the nodal variables\n// and class the FEDomain::Update to give domains a chance to update\n// their local data.\n// TODO: Instead of calling Update on all domains, perhaps I should introduce\n//       a mechanism for solvers only update the domains that are relevant.\nvoid FELinearSolver::Update(vector<double>& u)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// update nodal variables\n\tfor (int i=0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tfor (int j=0; j<m_dof.size(); ++j)\n\t\t{\n\t\t\tint n = node.m_ID[m_dof[j]];\n\t\t\tif (n >= 0) node.set(m_dof[j], u[n]);\n\t\t\telse if (-n-2 >= 0) node.set(m_dof[j], u[-n-2]);\n\t\t}\n\t}\n\n\t// make sure linear constraints are satisfied\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\tif (LCM.LinearConstraints())\n\t{\n\t\tLCM.Update();\n\t}\n\n\t// update the domains\n\tfor (int i=0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tdom.Update(tp);\n\t}\n\n\tfem.IncrementUpdateCounter();\n}\n"
  },
  {
    "path": "FECore/FELinearSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESolver.h\"\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FEGlobalVector;\nclass FEGlobalMatrix;\nclass FELinearSystem;\nclass LinearSolver;\n\n//-----------------------------------------------------------------------------\n//! Abstract Base class for finite element solution algorithms (i.e. \"FE solvers\") that require the solution\n//! of a linear system of equations.\nclass FECORE_API FELinearSolver : public FESolver\n{\npublic:\n\t//! constructor\n\tFELinearSolver(FEModel* pfem);\n\n\t//! Set the degrees of freedom\n\tvoid SetDOF(vector<int>& dof);\n\n\t//! Get the number of equations\n\tint NumberOfEquations() const;\n\n\t//! Get the linear solver\n\tLinearSolver* GetLinearSolver() override;\n\npublic: // from FESolver\n\n\t//! solve the step\n\tbool SolveStep() override;\n\n\t//! Initialize and allocate data\n\tbool Init() override;\n\n\t//! Clean up data\n\tvoid Clean() override;\n\n\t//! Serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic: // these functions need to be implemented by the derived class\n\n\t//! Evaluate the right-hand side \"force\" vector\n\tvirtual void ForceVector(FEGlobalVector& R);\n\n\t//! Evaluate the stiffness matrix\n\tvirtual bool StiffnessMatrix(FELinearSystem& K);\n\n\t//! Update the model state\n\tvirtual void Update(vector<double>& u) override;\n\nprotected: // some helper functions\n\n\t//! Reform the stiffness matrix\n\tbool ReformStiffness();\n\n\t//! Create and evaluate the stiffness matrix\n\tbool CreateStiffness();\n\n\t//! get the stiffness matrix\n\tFEGlobalMatrix* GetStiffnessMatrix() override;\n\n\t//! get the RHS\n\tstd::vector<double>\tGetLoadVector() override;\n\nprotected:\n\tvector<double>\t\tm_R;\t//!< RHS vector\n\tvector<double>\t\tm_u;\t//!< vector containing prescribed values\n\nprivate:\n\tLinearSolver*\t\tm_pls;\t\t//!< The linear equation solver\n\tFEGlobalMatrix*\t\tm_pK;\t\t//!< The global stiffness matrix\n\n\tvector<int>\t\tm_dof;\t//!< list of active degrees of freedom\n\tbool\t\t\tm_breform;\t//!< matrix reformation flag\n};\n"
  },
  {
    "path": "FECore/FELinearSystem.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELinearSystem.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"FEModel.h\"\n\n//-----------------------------------------------------------------------------\nFELinearSystem::FELinearSystem(FEModel* fem, FEGlobalMatrix& K, vector<double>& F, vector<double>& u, bool bsymm) : m_K(K), m_F(F), m_u(u), m_fem(fem)\n{\n\tm_bsymm = bsymm;\n}\n\n//-----------------------------------------------------------------------------\nFELinearSystem::~FELinearSystem()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n// get symmetry flag\nbool FELinearSystem::IsSymmetric() const\n{\n\treturn m_bsymm;\n}\n\n//! assemble global stiffness matrix\nvoid FELinearSystem::Assemble(const FEElementMatrix& ke)\n{\n\tif ((ke.rows() == 0) || (ke.columns() == 0)) return;\n\n\t// assemble into the global stiffness\n\tm_K.Assemble(ke);\n\n\t// check the prescribed contributions\n\tSparseMatrix& K = m_K;\n\tint N = ke.rows();\n\tint neq = m_K.Rows();\n\n\t// loop over columns\n\tconst vector<int>& lmi = ke.RowIndices();\n\tconst vector<int>& lmj = ke.ColumnsIndices();\n\tfor (int j = 0; j<N; ++j)\n\t{\n\t\tint J = -lmj[j] - 2;\n\t\tif ((J >= 0) && (J<neq))\n\t\t{\n\t\t\t// dof j is a prescribed degree of freedom\n\n\t\t\t// loop over rows\n\t\t\tfor (int i = 0; i<N; ++i)\n\t\t\t{\n\t\t\t\tint I = lmi[i];\n\t\t\t\tif (I >= 0)\n\t\t\t\t{\n\t\t\t\t\t// dof i is not a prescribed degree of freedom\n#pragma omp atomic\n\t\t\t\t\tm_F[I] -= ke[i][j] * m_u[J];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// set the diagonal element of K to 1\n\t\t\tK.set(J, J, 1);\n\t\t}\n\t}\n\n\tif (m_fem) \n\t{\n\t\tFELinearConstraintManager& LCM = m_fem->GetLinearConstraintManager();\n\t\tif (LCM.LinearConstraints())\n\t\t{\n\t\t\tconst vector<int>& en = ke.Nodes();\n\t\t\t#pragma omp critical (LC_assemble)\n\t\t\tLCM.AssembleStiffness(m_K, m_F, m_u, en, lmi, lmj, ke);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearSystem::AssembleRHS(vector<int>& lm, matrix& ke, vector<double>& U)\n{\n\tint ne = (int)lm.size();\n\tfor (int j = 0; j<ne; ++j)\n\t{\n\t\tif (lm[j] >= 0)\n\t\t{\n\t\t\tdouble q = 0;\n\t\t\tfor (int k = 0; k<ne; ++k)\n\t\t\t{\n\t\t\t\tif (lm[k] >= 0) q += ke[j][k] * U[lm[k]];\n\t\t\t\telse if (-lm[k] - 2 >= 0) q += ke[j][k] * U[-lm[k] - 2];\n\t\t\t}\n\t\t\tm_F[lm[j]] += q;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FELinearSystem::AssembleRHS(vector<int>& lm, vector<double>& fe)\n{\n\tconst int n = (int)lm.size();\n\tfor (int i = 0; i<n; ++i)\n\t{\n\t\tint nid = lm[i];\n\t\tif (nid >= 0) \n\t\t{\n\t\t\tm_F[nid] += fe[i];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FELinearSystem.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEGlobalMatrix.h\"\n#include \"matrix.h\"\n#include <vector>\n\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// Experimental class to see if all the assembly operations can be moved to a class\n// and out of the solver class.\nclass FECORE_API FELinearSystem\n{\npublic:\n\t// Constructor\n\t// Takes a FEGlobalMatrix class K that will store the actual stiffness matrix\n\t// and a vector F which contains the assembled contribution of the prescribed \n\t// degrees of freedom. The F vector must be added to the \"force\" vector. The u \n\t// vector contains the nodal values of the prescribed degrees of freedom.\n\tFELinearSystem(FEModel* fem, FEGlobalMatrix& K, std::vector<double>& F, std::vector<double>& u, bool bsymm);\n\n\t// virtual destructor\n\tvirtual ~FELinearSystem();\n\n\t// get symmetry flag\n\tbool IsSymmetric() const;\n\npublic:\n\t// Assembly routine\n\t// This assembles the element stiffness matrix ke into the global matrix.\n\t// The contributions of prescribed degrees of freedom will be stored in m_F\n\tvirtual void Assemble(const FEElementMatrix& ke);\n\n\t// This assembles a matrix to the RHS by pre-multiplying the matrix with the \n\t// prescribed value array U and then adding it to F\n\tvoid AssembleRHS(std::vector<int>& lm, matrix& ke, std::vector<double>& U);\n\n\t// This assembles a vetor to the RHS\n\tvoid AssembleRHS(std::vector<int>& lm, std::vector<double>& fe);\n\nprotected:\n\tbool\t\t\t\t\tm_bsymm;\t//!< symmetry flag\n\tFEModel*\t\t\t\tm_fem;\n\tFEGlobalMatrix&\t\t\tm_K;\t//!< The global stiffness matrix\n\tstd::vector<double>&\tm_F;\t//!< Contributions from prescribed degrees of freedom\n\tstd::vector<double>&\tm_u;\t//!< the array with prescribed values\n};\n"
  },
  {
    "path": "FECore/FELoadController.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELoadController.h\"\n#include \"DumpStream.h\"\n\nFELoadController::FELoadController(FEModel* fem) : FEModelComponent(fem)\n{\n\tm_value = 0.0;\n}\n\nvoid FELoadController::Evaluate(double time)\n{\n\tm_value = GetValue(time);\n}\n\nvoid FELoadController::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\tar & m_value;\n}\n\nvoid FELoadController::Reset()\n{\n\tm_value = 0.0;\n}\n"
  },
  {
    "path": "FECore/FELoadController.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelComponent.h\"\n\n//-----------------------------------------------------------------------------\n// Class that describes a load controller. A load controller can modify the value\n// of model parameters during the analysis. \nclass FECORE_API FELoadController : public FEModelComponent\n{\n\tFECORE_SUPER_CLASS(FELOADCONTROLLER_ID)\n\tFECORE_BASE_CLASS(FELoadController);\n\npublic:\n\tFELoadController(FEModel* fem);\n\n\t//! evaluate the load controller \n\tvoid Evaluate(double time);\n\n\t//! return the last calculated value\n\tdouble Value() const { return m_value; }\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\tvirtual void Reset();\n\nprotected:\n\t// This must be implemented by derived classes\n\tvirtual double GetValue(double time) = 0;\n\nprivate:\n\tdouble\tm_value;\t//!< last calculated value\n};\n"
  },
  {
    "path": "FECore/FELoadCurve.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FELoadCurve.h\"\n#include \"DumpStream.h\"\n#include \"FECoreKernel.h\"\n#include \"FEFunction1D.h\"\n#include \"log.h\"\n\nBEGIN_FECORE_CLASS(FELoadCurve, FELoadController)\n\tADD_PARAMETER(m_int, \"interpolate\", 0, \"LINEAR\\0STEP\\0SMOOTH\\0CUBIC SPLINE\\0CONTROL POINTS\\0APPROXIMATION\\0SMOOTH STEP\\0C2-SMOOTH\\0\");\n\tADD_PARAMETER(m_ext, \"extend\"     , 0, \"CONSTANT\\0EXTRAPOLATE\\0REPEAT\\0REPEAT OFFSET\\0\");\n\tADD_PARAMETER(m_points, \"points\");\nEND_FECORE_CLASS();\n\nFELoadCurve::FELoadCurve(FEModel* fem) : FELoadController(fem)\n{\n\tm_int = PointCurve::LINEAR;\n\tm_ext = PointCurve::CONSTANT;\n}\n\nFELoadCurve::FELoadCurve(const FELoadCurve& lc) : FELoadController(lc)\n{\n\tm_fnc = lc.m_fnc;\n\tm_int = lc.m_int;\n\tm_ext = lc.m_ext;\n}\n\nvoid FELoadCurve::operator = (const FELoadCurve& lc)\n{\n\tm_fnc = lc.m_fnc;\n\tm_int = lc.m_int;\n\tm_ext = lc.m_ext;\n}\n\nFELoadCurve::~FELoadCurve()\n{\n\t\n}\n\nbool FELoadCurve::Init()\n{\n\tm_fnc.SetInterpolator(m_int);\n\tm_fnc.SetExtendMode(m_ext);\n\tm_fnc.SetPoints(m_points);\n\n\t// check points\n\tif (m_fnc.Points() > 1)\n\t{\n\t\tfor (int i = 1; i < m_points.size(); ++i)\n\t\t{\n\t\t\tdouble t0 = m_points[i - 1].x();\n\t\t\tdouble t1 = m_points[i    ].x();\n\t\t\tif (t0 == t1) feLogWarning(\"Repeated time coordinate in load controller %d\", GetID() + 1);\n\t\t}\n\t}\n\n\tif (m_fnc.Update() == false) return false;\n\treturn FELoadController::Init();\n}\n\nvoid FELoadCurve::Reset()\n{\n\tFELoadController::Reset();\n\tm_fnc.SetInterpolator(m_int);\n\tm_fnc.SetExtendMode(m_ext);\n\tm_fnc.SetPoints(m_points);\n\tm_fnc.Update();\n}\n\nvoid FELoadCurve::Serialize(DumpStream& ar)\n{\n\tFELoadController::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_int << m_ext;\n\t\tar << m_points;\n\t}\n\telse\n\t{\n\t\tar >> m_int >> m_ext;\n\t\tar >> m_points;\n\n\t\tm_fnc.Clear();\n\t\tm_fnc.SetInterpolator(m_int);\n\t\tm_fnc.SetExtendMode(m_ext);\n\t\tm_fnc.SetPoints(m_points);\n\t\tm_fnc.Update();\n\t}\n}\n\n//! evaluates the loadcurve at time\ndouble FELoadCurve::GetValue(double time)\n{\n\treturn m_fnc.value(time);\n}\n\nbool FELoadCurve::CopyFrom(FELoadCurve* lc)\n{\n\tm_int = lc->m_int;\n\tm_ext = lc->m_ext;\n\tm_points = lc->m_points;\n\n\tm_fnc = lc->m_fnc;\n\treturn true;\n}\n\nvoid FELoadCurve::Add(double time, double value)\n{\n//\tm_fnc.Add(time, value);\n\tm_points.push_back(vec2d(time, value));\n}\n\nvoid FELoadCurve::Clear()\n{\n\tm_fnc.Clear();\n}\n\nvoid FELoadCurve::SetInterpolation(PointCurve::INTFUNC f)\n{\n\tm_int = f;\n//\tm_fnc.SetInterpolator(f);\n}\n\nvoid FELoadCurve::SetExtendMode(PointCurve::EXTMODE f)\n{\n\tm_ext = f;\n//\tm_fnc.SetExtendMode(f);\n}\n"
  },
  {
    "path": "FECore/FELoadCurve.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FELoadController.h\"\n#include \"PointCurve.h\"\n\n//-----------------------------------------------------------------------------\n// Base class for load curves.\n// Load curves are used to manipulate the time dependency of model parameters.\nclass FECORE_API FELoadCurve : public FELoadController\n{\npublic:\n\t// constructor\n\tFELoadCurve(FEModel* fem);\n\tFELoadCurve(const FELoadCurve& lc);\n\n\tvoid operator = (const FELoadCurve& lc);\n\n\t// destructor\n\tvirtual ~FELoadCurve();\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tbool CopyFrom(FELoadCurve* lc);\n\n\tvoid Add(double time, double value);\n\n\tvoid Clear();\n    \n    bool Init() override;\n\n\tvoid Reset() override;\n\n\tPointCurve& GetFunction() { return m_fnc; }\n\n\tint GetInterpolation() const { return m_int; }\n\tvoid SetInterpolation(PointCurve::INTFUNC f);\n\n\tint GetExtendMode() const { return m_ext; }\n\tvoid SetExtendMode(PointCurve::EXTMODE f);\n\n\tstd::vector<vec2d> GetPoints() const { return m_points; }\n\n\tdouble GetValue(double time) override;\n\nprivate:\n\tint\t\tm_int;\n\tint\t\tm_ext;\n\tstd::vector<vec2d>\tm_points;\n\nprivate:\n\tPointCurve\tm_fnc;\t//!< function to evaluate\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FELogData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FELogData.h\"\n\nFELogData::FELogData(FEModel* fem) : FECoreBase(fem)\n{\n\n}\n"
  },
  {
    "path": "FECore/FELogData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"fecore_api.h\"\n\n// Super class for log data classes. \nclass FECORE_API FELogData : public FECoreBase\n{\npublic:\n\tFELogData(FEModel* fem);\n};\n"
  },
  {
    "path": "FECore/FELogDomainVolume.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FELogDomainVolume.h\"\n#include \"FESolidDomain.h\"\n#include \"FEShellDomain.h\"\n\ndouble FELogDomainVolume::value(FEDomain& dom)\n{\n\tdouble vol = 0.0;\n\tint NE = dom.Elements();\n\n\tFESolidDomain* solidDomain = dynamic_cast<FESolidDomain*>(&dom);\n\tif (solidDomain)\n\t{\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFESolidElement& el = solidDomain->Element(i);\n\t\t\tdouble Ve = solidDomain->CurrentVolume(el);\n\t\t\tvol += Ve;\n\t\t}\n\t}\n\n\tFEShellDomain* shellDomain = dynamic_cast<FEShellDomain*>(&dom);\n\tif (shellDomain)\n\t{\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFEShellElement& el = shellDomain->Element(i);\n\t\t\tdouble Ve = shellDomain->CurrentVolume(el);\n\t\t\tvol += Ve;\n\t\t}\n\t}\n\n\treturn vol;\n}\n"
  },
  {
    "path": "FECore/FELogDomainVolume.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"DomainDataRecord.h\"\n\nclass FECORE_API FELogDomainVolume : public FELogDomainData\n{\npublic:\n    FELogDomainVolume(FEModel* fem) : FELogDomainData(fem) {}\n    double value(FEDomain& dom) override;\n};\n"
  },
  {
    "path": "FECore/FELogElemData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FELogElemData.h\"\n\nFELogElemData::FELogElemData(FEModel* fem) : FELogData(fem) {}\n\nFELogElemData::~FELogElemData() {}\n"
  },
  {
    "path": "FECore/FELogElemData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FELogData.h\"\n\nclass FEElement;\n\n//! Base class for element log data\nclass FECORE_API FELogElemData : public FELogData\n{\n\tFECORE_SUPER_CLASS(FELOGELEMDATA_ID)\n\tFECORE_BASE_CLASS(FELogElemData)\n\npublic:\n\tFELogElemData(FEModel* fem);\n\tvirtual ~FELogElemData();\n\tvirtual double value(FEElement& el) = 0;\n};\n"
  },
  {
    "path": "FECore/FELogElemMath.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FELogElemMath.h\"\n#include \"MObjBuilder.h\"\n\nFELogElemMath::FELogElemMath(FEModel* pfem) : FELogElemData(pfem) \n{\n\n}\n\nFELogElemMath::~FELogElemMath()\n{\n\tClear();\n}\n\nvoid FELogElemMath::Clear()\n{\n\tfor (FELogElemData* d : m_data) delete d;\n\tm_data.clear();\n}\n\ndouble FELogElemMath::value(FEElement& el)\n{\n\tstd::vector<double> val(m_data.size());\n\tfor (size_t i = 0; i < m_data.size(); ++i) val[i] = m_data[i]->value(el);\n\treturn m.value_s(val);\n}\n\nbool FELogElemMath::SetExpression(const std::string& smath)\n{\n\tClear();\n\n\tMObjBuilder o;\n\to.setAutoVars(true);\n\tif (!o.Create(&m, smath, false)) return false;\n\n\tint nvar = m.Variables();\n\tfor (int i = 0; i < nvar; ++i)\n\t{\n\t\tMVariable& var = *m.Variable(i);\n\t\tstring varName = var.Name();\n\t\tFELogElemData* pd = fecore_new<FELogElemData>(varName.c_str(), GetFEModel());\n\t\tif (pd == nullptr) return false;\n\t\tm_data.push_back(pd);\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FELogElemMath.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"ElementDataRecord.h\"\n#include \"MathObject.h\"\n#include <string>\n#include \"fecore_api.h\"\n\nclass FECORE_API FELogElemMath : public FELogElemData\n{\npublic:\n\tFELogElemMath(FEModel* pfem);\n\t~FELogElemMath();\n\n\tdouble value(FEElement& el);\n\n\tbool SetExpression(const std::string& smath);\n\nprivate:\n\tvoid Clear();\n\nprivate:\n\tMSimpleExpression m;\n\tstd::vector<FELogElemData*> m_data;\n};\n\n"
  },
  {
    "path": "FECore/FELogElementVolume.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FELogElementVolume.h\"\n#include \"FEModel.h\"\n#include \"FESurface.h\"\n\nFELogElementVolume::FELogElementVolume(FEModel* fem) : FELogElemData(fem)\n{\n\n}\n\ndouble FELogElementVolume::value(FEElement& el)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\treturn mesh.CurrentElementVolume(el);\n}\n\nFELogFaceArea::FELogFaceArea(FEModel* fem) : FELogFaceData(fem) {}\n\ndouble FELogFaceArea::value(FESurfaceElement& el)\n{\n\tFESurface* surface = dynamic_cast<FESurface*>(el.GetMeshPartition());\n\tif (surface == nullptr) return 0.0;\n\treturn surface->CurrentFaceArea(el);\n}\n"
  },
  {
    "path": "FECore/FELogElementVolume.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"ElementDataRecord.h\"\n#include \"FaceDataRecord.h\"\n\nclass FELogElementVolume : public FELogElemData\n{\npublic:\n\tFELogElementVolume(FEModel* fem);\n\tdouble value(FEElement& el) override;\n};\n\nclass FELogFaceArea : public FELogFaceData\n{\npublic:\n\tFELogFaceArea(FEModel* fem);\n\tdouble value(FESurfaceElement& el) override;\n};\n"
  },
  {
    "path": "FECore/FELogEnclosedVolume.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FELogEnclosedVolume.h\"\n#include \"FESurface.h\"\n#include \"FENode.h\"\n\nBEGIN_FECORE_CLASS(FELogEnclosedVolume, FELogSurfaceData)\nEND_FECORE_CLASS();\n\ndouble FELogEnclosedVolume::value(FESurface& surface)\n{\n\tdouble vol = 0.0;\n\tint NE = surface.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESurfaceElement& el = surface.Element(i);\n\t\tdouble* w = el.GaussWeights();\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tvec3d xi = surface.Local2Global(el, n);\n\t\t\tvec3d g[2];\n\t\t\tsurface.CoBaseVectors(el, n, g);\n\t\t\tdouble DVj = xi * (g[0] ^ g[1]) / 3;\n\t\t\tvol += DVj * w[n];\n\t\t}\n\t}\n\treturn vol;\n}\n\ndouble FELogEnclosedVolumeChange::value(FESurface& surface)\n{\n\tdouble DV = 0.0;\n\tint NE = surface.Elements();\n\tfor (int i = 0; i < NE; ++i) {\n\t\tFESurfaceElement& el = surface.Element(i);\n\t\tdouble* w = el.GaussWeights();\n\t\tfor (int n = 0; n < el.GaussPoints(); ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\tvec3d xi = surface.Local2Global(el, n);\n\t\t\tvec3d g[2];\n\t\t\tsurface.CoBaseVectors(el, n, g);\n\t\t\tvec3d Xi = surface.Local2Global0(el, n);\n\t\t\tvec3d G[2];\n\t\t\tsurface.CoBaseVectors0(el, n, G);\n\t\t\tdouble DVj = (xi * (g[0] ^ g[1]) - Xi * (G[0] ^ G[1])) / 3;\n\t\t\tDV += DVj * w[n];\n\t\t}\n\t}\n\treturn DV;\n}\n"
  },
  {
    "path": "FECore/FELogEnclosedVolume.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"SurfaceDataRecord.h\"\n\nclass FECORE_API FELogEnclosedVolume : public FELogSurfaceData\n{\npublic:\n    FELogEnclosedVolume(FEModel* fem) : FELogSurfaceData(fem) {}\n    double value(FESurface& surface) override;\n\n    DECLARE_FECORE_CLASS();\n};\n\nclass FELogEnclosedVolumeChange : public FELogSurfaceData\n{\npublic:\n\tFELogEnclosedVolumeChange(FEModel* fem) : FELogSurfaceData(fem) {}\n\tdouble value(FESurface& surface) override;\n};\n"
  },
  {
    "path": "FECore/FELogNodeData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2025 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FELogNodeData.h\"\n\nFELogNodeData::FELogNodeData(FEModel* fem) : FELogData(fem) {}\n\nFELogNodeData::~FELogNodeData() {}\n"
  },
  {
    "path": "FECore/FELogNodeData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2025 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FELogData.h\"\n\nclass FENode;\n\n//! This is the base class for a node data value.\nclass FECORE_API FELogNodeData : public FELogData\n{\n\tFECORE_SUPER_CLASS(FELOGNODEDATA_ID)\n\tFECORE_BASE_CLASS(FELogNodeData)\n\npublic:\n\tFELogNodeData(FEModel* fem);\n\tvirtual ~FELogNodeData();\n\tvirtual double value(const FENode& node) = 0;\n};\n"
  },
  {
    "path": "FECore/FELogSolutionNorm.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FELogSolutionNorm.h\"\n#include \"FEModel.h\"\n#include \"FEAnalysis.h\"\n#include \"FESolver.h\"\n\n//================================================================================\nFELogSolutionNorm::FELogSolutionNorm(FEModel* fem) : FEModelLogData(fem) {}\ndouble FELogSolutionNorm::value()\n{\n\tFEModel* fem = GetFEModel(); assert(fem);\n\tif (fem == nullptr) return 0.0;\n\n\tFEAnalysis* step = fem->GetCurrentStep();\n\tif (step == nullptr) return 0.0;\n\n\tFESolver* solver = step->GetFESolver();\n\tif (solver == nullptr) return 0.0;\n\n\tstd::vector<double> u = solver->GetSolutionVector();\n\tdouble unorm = l2_norm(u);\n\n\treturn unorm;\n}\n"
  },
  {
    "path": "FECore/FELogSolutionNorm.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEModelDataRecord.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FELogSolutionNorm : public FEModelLogData\n{\npublic:\n\tFELogSolutionNorm(FEModel* fem);\n\tdouble value() override;\n};\n"
  },
  {
    "path": "FECore/FEMat3dSphericalAngleMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMat3dSphericalAngleMap.h\"\n\nBEGIN_FECORE_CLASS(FEMat3dSphericalAngleMap, FEMat3dValuator)\n\tADD_PARAMETER(m_theta, \"theta\");\n\tADD_PARAMETER(m_phi, \"phi\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMat3dSphericalAngleMap::FEMat3dSphericalAngleMap(FEModel* pfem) : FEMat3dValuator(pfem)\n{\n\tm_theta = 0.0;\n\tm_phi = 90.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMat3dSphericalAngleMap::Init()\n{\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMat3dSphericalAngleMap::SetAngles(double theta, double phi)\n{\n\tm_theta = theta;\n\tm_phi = phi;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEMat3dSphericalAngleMap::operator () (const FEMaterialPoint& mp)\n{\n\t// convert from degress to radians\n\tconst double the = m_theta(mp)*PI / 180.;\n\tconst double phi = m_phi(mp)*PI / 180.;\n\n\t// define the first axis (i.e. the fiber vector)\n\tvec3d a;\n\ta.x = cos(the)*sin(phi);\n\ta.y = sin(the)*sin(phi);\n\ta.z = cos(phi);\n    \n    vec3d b;\n    b.x = -sin(the);\n    b.y = cos(the);\n    b.z = 0;\n\n\tvec3d c;\n    c.x = -cos(the)*cos(phi);\n    c.y = -sin(the)*cos(phi);\n    c.z = sin(phi);\n\n\t// setup the rotation matrix\n\tmat3d Q;\n\tQ[0][0] = a.x; Q[0][1] = b.x; Q[0][2] = c.x;\n\tQ[1][0] = a.y; Q[1][1] = b.y; Q[1][2] = c.y;\n\tQ[2][0] = a.z; Q[2][1] = b.z; Q[2][2] = c.z;\n\n    return Q;\n}\n\n//-----------------------------------------------------------------------------\nFEMat3dValuator* FEMat3dSphericalAngleMap::copy()\n{\n\tFEMat3dSphericalAngleMap* map = new FEMat3dSphericalAngleMap(GetFEModel());\n\tmap->m_theta = m_theta;\n\tmap->m_phi = m_phi;\n\treturn map;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMat3dSphericalAngleMap::Serialize(DumpStream &ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_theta & m_phi;\n}\n"
  },
  {
    "path": "FECore/FEMat3dSphericalAngleMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEMat3dValuator.h\"\n#include \"FEModelParam.h\"\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMat3dSphericalAngleMap : public FEMat3dValuator\n{\npublic:\n\tFEMat3dSphericalAngleMap(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid SetAngles(double theta, double phi);\n\n\tmat3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEMat3dValuator* copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFEParamDouble m_theta;\n\tFEParamDouble m_phi;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FECore/FEMat3dValuator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMat3dValuator.h\"\n#include \"FEModel.h\"\n#include \"FEMesh.h\"\n#include \"FEDataMap.h\"\n#include \"DumpStream.h\"\n\n//=============================================================================\n// FELocalMap\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEConstValueMat3d, FEMat3dValuator)\n\tADD_PARAMETER(m_val, \"const\");\nEND_FECORE_CLASS();\n\nFEConstValueMat3d::FEConstValueMat3d(FEModel* fem) : FEMat3dValuator(fem)\n{\n\tm_val.zero();\n}\n\nFEMat3dValuator* FEConstValueMat3d::copy()\n{\n\tFEConstValueMat3d* map = fecore_alloc(FEConstValueMat3d, GetFEModel());\n\tmap->m_val = m_val;\n\treturn map;\n}\n\n//=============================================================================\n// FELocalMap\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMat3dLocalElementMap, FEMat3dValuator)\n\tADD_PARAMETER(m_n, 3, \"local\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMat3dLocalElementMap::FEMat3dLocalElementMap(FEModel* pfem) : FEMat3dValuator(pfem)\n{\n\tm_n[0] = 0;\n\tm_n[1] = 0;\n\tm_n[2] = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMat3dLocalElementMap::Init()\n{\n\t// check values\n\tif ((m_n[0] <= 0) && (m_n[1] <= 0) && (m_n[2] <= 0)) { m_n[0] = 1; m_n[1] = 2; m_n[2] = 4; }\n\tif (m_n[2] <= 0) m_n[2] = m_n[1];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEMat3dLocalElementMap::operator () (const FEMaterialPoint& mp)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\n\tFEElement& el = *mp.m_elem;\n\n\tvec3d r0[FEElement::MAX_NODES];\n\tfor (int i=0; i<el.Nodes(); ++i) r0[i] = mesh.Node(el.m_node[i]).m_r0;\n\n\tvec3d a, b, c, d;\n\tmat3d Q;\n\n\ta = r0[m_n[1] - 1] - r0[m_n[0] - 1];\n\ta.unit();\n\n\tif (m_n[2] != m_n[1])\n\t{\n\t\td = r0[m_n[2] - 1] - r0[m_n[0] - 1];\n\t}\n\telse\n\t{\n\t\td = vec3d(0,1,0);\n\t\tif (fabs(d*a) > 0.999) d = vec3d(1,0,0);\n\t}\n\n\tc = a^d;\n\tb = c^a;\n\n\tb.unit();\n\tc.unit();\n\n\tQ[0][0] = a.x; Q[0][1] = b.x; Q[0][2] = c.x;\n\tQ[1][0] = a.y; Q[1][1] = b.y; Q[1][2] = c.y;\n\tQ[2][0] = a.z; Q[2][1] = b.z; Q[2][2] = c.z;\n\n\treturn Q;\n}\n\n//-----------------------------------------------------------------------------\nFEMat3dValuator* FEMat3dLocalElementMap::copy()\n{\n\tFEMat3dLocalElementMap* map = new FEMat3dLocalElementMap(GetFEModel());\n\tmap->m_n[0] = m_n[0];\n\tmap->m_n[1] = m_n[1];\n\tmap->m_n[2] = m_n[2];\n\treturn map;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMat3dLocalElementMap::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_n;\n}\n\n//=============================================================================\n// FESphericalMap\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMat3dSphericalMap, FEMat3dValuator)\n\tADD_PARAMETER(m_c, \"center\");\n\tADD_PARAMETER(m_r, \"vector\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMat3dSphericalMap::FEMat3dSphericalMap(FEModel* pfem): FEMat3dValuator(pfem)\n{\n\tm_c = vec3d(0,0,0);\n\tm_r = vec3d(1,0,0);\n}\n\n//-----------------------------------------------------------------------------\nbool FEMat3dSphericalMap::Init()\n{\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEMat3dSphericalMap::operator () (const FEMaterialPoint& mp)\n{\n\tvec3d a = mp.m_r0;\n\ta -= m_c;\n\ta.unit();\n\n\t// setup the rotation vector\n\tvec3d x_unit(1,0,0);\n\tquatd q(x_unit, a);\n\n\tvec3d v = m_r;\n\tv.unit();\n\tq.RotateVector(v);\n\ta = v;\n\n\tvec3d d(0,1,0);\n\td.unit();\n\tif (fabs(a*d) > .99) \n\t{\n\t\td = vec3d(0,0,1);\n\t\td.unit();\n\t}\n\n\tvec3d c = a^d;\n\tvec3d b = c^a;\n\n\ta.unit();\n\tb.unit();\n\tc.unit();\n\n\tmat3d Q;\n\tQ[0][0] = a.x; Q[0][1] = b.x; Q[0][2] = c.x;\n\tQ[1][0] = a.y; Q[1][1] = b.y; Q[1][2] = c.y;\n\tQ[2][0] = a.z; Q[2][1] = b.z; Q[2][2] = c.z;\n\n\treturn Q;\n}\n\n//-----------------------------------------------------------------------------\nFEMat3dValuator* FEMat3dSphericalMap::copy()\n{\n\tFEMat3dSphericalMap* map = new FEMat3dSphericalMap(GetFEModel());\n\tmap->m_c = m_c;\n\tmap->m_r = m_r;\n\treturn map;\n}\n\n//=============================================================================\n// FECylindricalMap\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMat3dCylindricalMap, FEMat3dValuator)\n\tADD_PARAMETER(m_c, \"center\");\n\tADD_PARAMETER(m_a, \"axis\"  );\n\tADD_PARAMETER(m_r, \"vector\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMat3dCylindricalMap::FEMat3dCylindricalMap(FEModel* pfem) : FEMat3dValuator(pfem)\n{\n\tm_c = vec3d(0,0,0);\n\tm_a = vec3d(0,0,1);\n\tm_r = vec3d(1,0,0);\n}\n\n//-----------------------------------------------------------------------------\nbool FEMat3dCylindricalMap::Init()\n{\n\tm_a.unit();\n\tm_r.unit();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEMat3dCylindricalMap::operator () (const FEMaterialPoint& mp)\n{\n\t// get the position of the material point\n\tvec3d p = mp.m_r0;\n\n\t// find the vector to the axis\n\tvec3d b = (p - m_c) - m_a*(m_a*(p - m_c)); b.unit();\n\n\t// setup the rotation vector\n\tvec3d x_unit(vec3d(1,0,0));\n\tquatd q(x_unit, b);\n\n\t// rotate the reference vector\n\tvec3d r(m_r); r.unit();\n\tq.RotateVector(r);\n\n\t// setup a local coordinate system with r as the x-axis\n\tvec3d d(0,1,0);\n\tq.RotateVector(d);\n\tif (fabs(d*r) > 0.99)\n\t{\n\t\td = vec3d(0,0,1);\n\t\tq.RotateVector(d);\n\t}\n\n\t// find basis vectors\n\tvec3d e1 = r;\n\tvec3d e3 = (e1 ^ d); e3.unit();\n\tvec3d e2 = e3 ^ e1;\n\n\t// setup rotation matrix\n\tmat3d Q;\n\tQ[0][0] = e1.x; Q[0][1] = e2.x; Q[0][2] = e3.x;\n\tQ[1][0] = e1.y; Q[1][1] = e2.y; Q[1][2] = e3.y;\n\tQ[2][0] = e1.z; Q[2][1] = e2.z; Q[2][2] = e3.z;\n\n\treturn Q;\n}\n\n//-----------------------------------------------------------------------------\nFEMat3dValuator* FEMat3dCylindricalMap::copy()\n{\n\tFEMat3dCylindricalMap* val = new FEMat3dCylindricalMap(GetFEModel());\n\tval->m_c = m_c;\n\tval->m_a = m_a;\n\tval->m_r = m_r;\n\treturn val;\n}\n\n//=============================================================================\n// FEPolarMap\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMat3dPolarMap, FEMat3dValuator)\n\tADD_PARAMETER(m_c, \"center\");\n\tADD_PARAMETER(m_a, \"axis\"  );\n\tADD_PARAMETER(m_d0, \"vector1\");\n\tADD_PARAMETER(m_d1, \"vector2\");\n\tADD_PARAMETER(m_R0, \"radius1\");\n\tADD_PARAMETER(m_R1, \"radius2\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMat3dPolarMap::FEMat3dPolarMap(FEModel* pfem) : FEMat3dValuator(pfem)\n{\n\tm_c = vec3d(0,0,0);\n\tm_a = vec3d(0,0,1);\n\tm_d0 = m_d1 = vec3d(1,0,0);\n\tm_R0 = 0; \n\tm_R1 = 1;\n}\n\n//-----------------------------------------------------------------------------\nbool FEMat3dPolarMap::Init()\n{\n\tm_a.unit();\n\tm_d0.unit();\n\tm_d1.unit();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FEMat3dPolarMap::operator () (const FEMaterialPoint& mp)\n{\n\t// get the nodal position of material point\n\tvec3d p = mp.m_r0;\n\n\t// find the vector to the axis and its length\n\tvec3d b = (p - m_c) - m_a*(m_a*(p - m_c)); \n\tdouble R = b.unit();\n\n\t// get the relative radius\n\tdouble R0 = m_R0;\n\tdouble R1 = m_R1;\n\tif (R1 == R0) R1 += 1;\n\tdouble w = (R - R0)/(R1 - R0);\n\n\t// get the fiber vectors\n\tvec3d v0 = m_d0;\n\tvec3d v1 = m_d1;\n\tquatd Q0(0,vec3d(0,0,1)), Q1(v0,v1);\n\tquatd Qw = quatd::slerp(Q0, Q1, w);\n\tvec3d v = v0; Qw.RotateVector(v);\n\n\t// setup the rotation vector\n\tvec3d x_unit(vec3d(1,0,0));\n\tquatd q(x_unit, b);\n\n\t// rotate the reference vector\n\tq.RotateVector(v);\n\n\t// setup a local coordinate system with r as the x-axis\n\tvec3d d(vec3d(0,1,0));\n\tq.RotateVector(d);\n\tif (fabs(d*v) > 0.99)\n\t{\n\t\td = vec3d(0,0,1);\n\t\tq.RotateVector(d);\n\t}\n\n\t// find basis vectors\n\tvec3d e1 = v;\n\tvec3d e3 = (e1 ^ d); e3.unit();\n\tvec3d e2 = e3 ^ e1;\n\n\t// setup rotation matrix\n\tmat3d Q;\n\tQ[0][0] = e1.x; Q[0][1] = e2.x; Q[0][2] = e3.x;\n\tQ[1][0] = e1.y; Q[1][1] = e2.y; Q[1][2] = e3.y;\n\tQ[2][0] = e1.z; Q[2][1] = e2.z; Q[2][2] = e3.z;\n\n\treturn Q;\n}\n\n//-----------------------------------------------------------------------------\nFEMat3dValuator* FEMat3dPolarMap::copy()\n{\n\tFEMat3dPolarMap* map = new FEMat3dPolarMap(GetFEModel());\n\tmap->m_c = m_c;\n\tmap->m_a = m_a;\n\tmap->m_d0 = m_d0;\n\tmap->m_d1 = m_d1;\n\tmap->m_R0 = m_R0;\n\tmap->m_R1 = m_R1;\n\treturn map;\n}\n\n//=============================================================================\n// FEVectorMap\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMat3dVectorMap, FEMat3dValuator)\n\tADD_PARAMETER(m_a, \"a\");\n\tADD_PARAMETER(m_d, \"d\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMat3dVectorMap::FEMat3dVectorMap(FEModel* pfem) : FEMat3dValuator(pfem)\n{\n\tm_a = vec3d(1,0,0);\n\tm_d = vec3d(0,1,0);\n\tm_Q.unit();\n}\n\n//-----------------------------------------------------------------------------\nbool FEMat3dVectorMap::Init()\n{\n\t// generators have to be unit vectors\n\tm_a.unit();\n\tm_d.unit();\n\n\t// make sure the vectors are not 0-vectors\n\tif ((m_a.norm2() == 0.0) || (m_d.norm2() == 0.0)) return false;\n\n\t// make sure that a, d are not aligned\n\tif (fabs(m_a*m_d) > 0.999)\n\t{\n\t\t// if they are, find a different value for d\n\t\t// Note: If this is used for a mat_axis parameter, then this\n\t\t// would modify the user specified value. \n\t\tm_d = vec3d(1,0,0);\n\t\tif (fabs(m_a*m_d) > 0.999) m_d = vec3d(0,1,0);\n\t}\n\n\tvec3d a = m_a;\n\tvec3d d = m_d;\n\n\tvec3d c = a^d;\n\tvec3d b = c^a;\n\n\ta.unit();\n\tb.unit();\n\tc.unit();\n\n\tm_Q[0][0] = a.x; m_Q[0][1] = b.x; m_Q[0][2] = c.x;\n\tm_Q[1][0] = a.y; m_Q[1][1] = b.y; m_Q[1][2] = c.y;\n\tm_Q[2][0] = a.z; m_Q[2][1] = b.z; m_Q[2][2] = c.z;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMat3dVectorMap::SetVectors(vec3d a, vec3d d)\n{ \n\tm_a = a; \n\tm_d = d; \n}\n\n//-----------------------------------------------------------------------------\nmat3d FEMat3dVectorMap::operator () (const FEMaterialPoint& mp)\n{\n\treturn m_Q;\n}\n\n//-----------------------------------------------------------------------------\nFEMat3dValuator* FEMat3dVectorMap::copy()\n{\n\tFEMat3dVectorMap* map = new FEMat3dVectorMap(GetFEModel());\n\tmap->m_a = m_a;\n\tmap->m_d = m_d;\n\tmap->m_Q = m_Q;\n\treturn map;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMat3dVectorMap::Serialize(DumpStream &ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_a & m_d & m_Q;\n}\n\n//=============================================================================\n// FEMappedValueMat3d\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMappedValueMat3d, FEMat3dValuator)\n\tADD_PARAMETER(m_mapName, \"map\");\nEND_FECORE_CLASS();\n\n\nFEMappedValueMat3d::FEMappedValueMat3d(FEModel* fem) : FEMat3dValuator(fem)\n{\n\tm_val = nullptr;\n}\n\nvoid FEMappedValueMat3d::setDataMap(FEDataMap* val)\n{\n\tm_val = val;\n}\n\nFEDataMap* FEMappedValueMat3d::dataMap()\n{\n\treturn m_val;\n}\n\nmat3d FEMappedValueMat3d::operator()(const FEMaterialPoint& pt)\n{\n\tassert(m_val);\n\treturn (m_val ? m_val->valueMat3d(pt) : mat3d::identity());\n}\n\nFEMat3dValuator* FEMappedValueMat3d::copy()\n{\n\tFEMappedValueMat3d* map = fecore_alloc(FEMappedValueMat3d, GetFEModel());\n\tmap->m_val = m_val;\n\treturn map;\n}\n\nvoid FEMappedValueMat3d::Serialize(DumpStream& ar)\n{\n\tFEMat3dValuator::Serialize(ar);\n\tar & m_val;\n}\n"
  },
  {
    "path": "FECore/FEMat3dValuator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEValuator.h\"\n\nclass FEDataMap;\n\n//---------------------------------------------------------------------------------------\n// Base class for evaluating vec3d parameters\nclass FECORE_API FEMat3dValuator : public FEValuator\n{\n\tFECORE_SUPER_CLASS(FEMAT3DVALUATOR_ID)\n\tFECORE_BASE_CLASS(FEMat3dValuator)\n\npublic:\n\tFEMat3dValuator(FEModel* fem) : FEValuator(fem) {};\n\n\tvirtual mat3d operator()(const FEMaterialPoint& pt) = 0;\n\n\tvirtual FEMat3dValuator* copy() = 0;\n\n\tvirtual bool isConst() { return false; }\n\n\tvirtual mat3d* constValue() { return nullptr; }\n};\n\n//-----------------------------------------------------------------------------\n// A constant valuator\nclass FECORE_API FEConstValueMat3d : public FEMat3dValuator\n{\npublic:\n\tFEConstValueMat3d(FEModel* fem);\n\n\tFEMat3dValuator* copy() override;\n\n\tmat3d operator()(const FEMaterialPoint& pt) override { return m_val; }\n\n\t// is this a const value\n\tbool isConst() override { return true; }\n\n\t// get the const value (returns 0 if param is not const)\n\tmat3d* constValue() override { return &m_val; }\n\n\tmat3d& value() { return m_val; }\n\nprivate:\n\tmat3d\tm_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n\n//-----------------------------------------------------------------------------\n//! This class generates a material axes based on the local element node numbering.\nclass FECORE_API FEMat3dLocalElementMap : public FEMat3dValuator\n{\npublic:\n\tFEMat3dLocalElementMap(FEModel* pfem);\n\n\tbool Init() override;\n\n\tmat3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEMat3dValuator* copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tint\t\t\tm_n[3];\t// local element nodes\n\nprotected:\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n//! This class generates material axes based on a spherical map. \nclass FECORE_API FEMat3dSphericalMap : public FEMat3dValuator\n{\npublic:\n\tFEMat3dSphericalMap(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid SetSphereCenter(const vec3d& c) { m_c = c; }\n\n\tvoid SetSphereVector(const vec3d& r) { m_r = r; }\n\n\tmat3d operator() (const FEMaterialPoint& mp) override;\n\n\tFEMat3dValuator* copy() override;\n\npublic:\n\tvec3d\t\tm_c;\t// center of map\n\tvec3d\t\tm_r;\t// vector for parallel transport\n\nprotected:\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMat3dCylindricalMap : public FEMat3dValuator\n{\npublic:\n\tFEMat3dCylindricalMap(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid SetCylinderCenter(vec3d c) { m_c = c; }\n\n\tvoid SetCylinderAxis(vec3d a) { m_a = a; m_a.unit(); }\n\n\tvoid SetCylinderRef(vec3d r) { m_r = r; m_r.unit(); }\n\n\tmat3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEMat3dValuator* copy() override;\n\npublic:\n\tvec3d\t\tm_c;\t// center of map\n\tvec3d\t\tm_a;\t// axis\n\tvec3d\t\tm_r;\t// reference direction\n\nprotected:\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMat3dPolarMap : public FEMat3dValuator\n{\npublic:\n\tFEMat3dPolarMap(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid SetCenter(vec3d c) { m_c = c; }\n\n\tvoid SetAxis(vec3d a) { m_a = a; m_a.unit(); }\n\n\tvoid SetVector0(vec3d r) { m_d0 = r; m_d0.unit(); }\n\tvoid SetVector1(vec3d r) { m_d1 = r; m_d1.unit(); }\n\n\tvoid SetRadius0(double r) { m_R0 = r; }\n\tvoid SetRadius1(double r) { m_R1 = r; }\n\n\tmat3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEMat3dValuator* copy() override;\n\npublic:\n\tvec3d\t\tm_c;\t\t// center of map\n\tvec3d\t\tm_a;\t\t// axis\n\tvec3d\t\tm_d0, m_d1;\t// reference direction\n\tdouble\t\tm_R0, m_R1;\n\nprotected:\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMat3dVectorMap : public FEMat3dValuator\n{\npublic:\n\tFEMat3dVectorMap(FEModel* pfem);\n\n\tbool Init() override;\n\n\tvoid SetVectors(vec3d a, vec3d d);\n\n\tmat3d operator () (const FEMaterialPoint& mp) override;\n\n\tFEMat3dValuator* copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tvec3d\tm_a, m_d;\n\tmat3d\tm_Q;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEMappedValueMat3d : public FEMat3dValuator\n{\npublic:\n\tFEMappedValueMat3d(FEModel* fem);\n\n\tvoid setDataMap(FEDataMap* val);\n\n\tFEDataMap* dataMap();\n\n\tmat3d operator()(const FEMaterialPoint& pt) override;\n\n\tFEMat3dValuator* copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tstd::string\tm_mapName;\n\nprivate:\n\tFEDataMap* m_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEMat3dsValuator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMat3dsValuator.h\"\n#include \"FEModel.h\"\n#include \"FEMesh.h\"\n#include \"FEDataMap.h\"\n#include \"DumpStream.h\"\n\n//=============================================================================\n// FEConstValueMat3ds\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEConstValueMat3ds, FEMat3dsValuator)\n\tADD_PARAMETER(m_val, \"const\");\nEND_FECORE_CLASS();\n\nFEConstValueMat3ds::FEConstValueMat3ds(FEModel* fem) : FEMat3dsValuator(fem)\n{\n\tm_val.zero();\n}\n\nFEMat3dsValuator* FEConstValueMat3ds::copy()\n{\n\tFEConstValueMat3ds* map = fecore_alloc(FEConstValueMat3ds, GetFEModel());\n\tmap->m_val = m_val;\n\treturn map;\n}\n\n//=============================================================================\n// FEMappedValueMat3ds\n//-----------------------------------------------------------------------------\n\nBEGIN_FECORE_CLASS(FEMappedValueMat3ds, FEMat3dsValuator)\n\tADD_PARAMETER(m_mapName, \"map\");\nEND_FECORE_CLASS();\n\nFEMappedValueMat3ds::FEMappedValueMat3ds(FEModel* fem) : FEMat3dsValuator(fem)\n{\n\tm_val = nullptr;\n}\n\nvoid FEMappedValueMat3ds::setDataMap(FEDataMap* val)\n{\n\tm_val = val;\n}\n\nmat3ds FEMappedValueMat3ds::operator()(const FEMaterialPoint& pt)\n{\n\treturn m_val->valueMat3ds(pt);\n}\n\nFEMat3dsValuator* FEMappedValueMat3ds::copy()\n{\n\tFEMappedValueMat3ds* map = fecore_alloc(FEMappedValueMat3ds, GetFEModel());\n\tmap->m_val = m_val;\n\treturn map;\n}\n\nvoid FEMappedValueMat3ds::Serialize(DumpStream& ar)\n{\n\tFEMat3dsValuator::Serialize(ar);\n\tar & m_val;\n}\n"
  },
  {
    "path": "FECore/FEMat3dsValuator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEValuator.h\"\n\nclass FEDataMap;\n\n//---------------------------------------------------------------------------------------\n// Base class for evaluating vec3d parameters\nclass FECORE_API FEMat3dsValuator : public FEValuator\n{\n\tFECORE_SUPER_CLASS(FEMAT3DSVALUATOR_ID)\n\tFECORE_BASE_CLASS(FEMat3dsValuator)\n\npublic:\n\tFEMat3dsValuator(FEModel* fem) : FEValuator(fem) {};\n\n\tvirtual mat3ds operator()(const FEMaterialPoint& pt) = 0;\n\n\tvirtual FEMat3dsValuator* copy() = 0;\n\n\tvirtual bool isConst() { return false; }\n\n\tvirtual mat3ds* constValue() { return nullptr; }\n};\n\n//-----------------------------------------------------------------------------\n// A constant valuator\nclass FECORE_API FEConstValueMat3ds : public FEMat3dsValuator\n{\npublic:\n\tFEConstValueMat3ds(FEModel* fem);\n\n\tFEMat3dsValuator* copy() override;\n\n\tmat3ds operator()(const FEMaterialPoint& pt) override { return m_val; }\n\n\t// is this a const value\n\tbool isConst() override { return true; }\n\n\t// get the const value (returns 0 if param is not const)\n\tmat3ds* constValue() override { return &m_val; }\n\n\tmat3ds& value() { return m_val; }\n\nprivate:\n\tmat3ds\tm_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEMappedValueMat3ds : public FEMat3dsValuator\n{\npublic:\n\tFEMappedValueMat3ds(FEModel* fem);\n\n\tvoid setDataMap(FEDataMap* val);\n\n\tmat3ds operator()(const FEMaterialPoint& pt) override;\n\n\tFEMat3dsValuator* copy() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tstd::string\tm_mapName;\n\nprivate:\n\tFEDataMap*\tm_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEMaterial.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMaterial.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nFEMaterialBase::FEMaterialBase(FEModel* fem) : FEModelComponent(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! returns a pointer to a new material point object\nFEMaterialPointData* FEMaterialBase::CreateMaterialPointData() { return nullptr; };\n\n//-----------------------------------------------------------------------------\n//! Update specialized material points at each iteration\nvoid FEMaterialBase::UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp)\n{\n\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMaterial, FEMaterialBase)\n//\tADD_PROPERTY(m_Q, \"mat_axis\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEMaterial::FEMaterial(FEModel* fem) : FEMaterialBase(fem)\n{\n\tm_Q = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterial::~FEMaterial()\n{\n\tfor (size_t i = 0; i < m_param.size(); ++i) delete m_param[i];\n\tm_param.clear();\n}\n\n//-----------------------------------------------------------------------------\n// evaluate local coordinate system at material point\nmat3d FEMaterial::GetLocalCS(const FEMaterialPoint& mp)\n{\n\tmat3d Q = (m_Q ? m_Q->operator()(mp) : mat3d::identity());\n\tFEMaterial* parent = dynamic_cast<FEMaterial*>(GetParent());\n\tif (parent) \n\t{\n\t\tmat3d Qp = parent->GetLocalCS(mp);\n\t\treturn Qp*Q;\n\t}\n\telse\n\t{\n\t\tmat3d A = mp.m_Q.RotationMatrix();\n\t\treturn A*Q;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// set the (local) material axis valuator\nvoid FEMaterial::SetMaterialAxis(FEMat3dValuator* val)\n{\n\tif (m_Q) delete m_Q;\n\tm_Q = val;\n}\n\n//-----------------------------------------------------------------------------\n//! Initial material.\nbool FEMaterial::Init()\n{\n\t// initialize base class\n\treturn FECoreBase::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterial::AddDomain(FEDomain* dom)\n{\n\tm_domList.AddDomain(dom);\n}\n\n//-----------------------------------------------------------------------------\nFEDomainParameter* FEMaterial::FindDomainParameter(const std::string& paramName)\n{\n\tfor (int i = 0; i < m_param.size(); ++i)\n\t{\n\t\tFEDomainParameter* pi = m_param[i];\n\t\tif (pi->name() == paramName) return pi;\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterial::AddDomainParameter(FEDomainParameter* p)\n{\n\tassert(p);\n\tm_param.push_back(p);\n}\n\n//==============================================================================\nBEGIN_FECORE_CLASS(FEMaterialProperty, FEMaterialBase)\nEND_FECORE_CLASS();\n\nFEMaterialProperty::FEMaterialProperty(FEModel* fem) : FEMaterialBase(fem)\n{\n\n}\n\n//-----------------------------------------------------------------------------\n// Since properties don't have local coordinate system,\n// we return the parent's \nmat3d FEMaterialProperty::GetLocalCS(const FEMaterialPoint& mp)\n{\n\tFEMaterialBase* parent = dynamic_cast<FEMaterialBase*>(GetParent()); assert(parent);\n\treturn parent->GetLocalCS(mp);\n}\n"
  },
  {
    "path": "FECore/FEMaterial.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelComponent.h\"\n#include \"FEMaterialPoint.h\"\n#include \"FEModelParam.h\"\n#include \"FEDomainList.h\"\n#include \"FEDomainParameter.h\"\n\n//-----------------------------------------------------------------------------\n// forward declaration of some classes\nclass FEDomain;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMaterialBase : public FEModelComponent\n{\npublic:\n\tFEMaterialBase(FEModel* fem);\n\n\t//! returns a pointer to a new material point object\n\tvirtual FEMaterialPointData* CreateMaterialPointData();\n\n\t//! Update specialized material points at each iteration\n\tvirtual void UpdateSpecializedMaterialPoints(FEMaterialPoint& mp, const FETimeInfo& tp);\n\n\t// evaluate local coordinate system at material point\n\tvirtual mat3d GetLocalCS(const FEMaterialPoint& mp) = 0;\n};\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for material types\n//! From this class all other material classes are derived.\n\nclass FECORE_API FEMaterial : public FEMaterialBase\n{\n\tFECORE_SUPER_CLASS(FEMATERIAL_ID)\n\tFECORE_BASE_CLASS(FEMaterial)\n\npublic:\n\tFEMaterial(FEModel* fem);\n\tvirtual ~FEMaterial();\n\n\t//! performs initialization\n\tbool Init() override;\n\n\t//! get a domain parameter\n\tFEDomainParameter* FindDomainParameter(const std::string& paramName);\n\n\t// evaluate local coordinate system at material point\n\tmat3d GetLocalCS(const FEMaterialPoint& mp) override;\n\n\t// set the (local) material axis valuator\n\tvoid SetMaterialAxis(FEMat3dValuator* val);\n\nprotected:\n\tFEMat3dValuator*\tm_Q;\t\t\t//!< local material coordinate system\n\npublic:\n\t//! Assign a domain to this material\n\tvoid AddDomain(FEDomain* dom);\n\n\t//! get the domaint list\n\tFEDomainList& GetDomainList() { return m_domList; }\n\nprotected:\n\tvoid AddDomainParameter(FEDomainParameter* p);\n\nprivate:\n\tFEDomainList\tm_domList;\t\t//!< list of domains that use this material\n\n\tstd::vector<FEDomainParameter*>\tm_param;\t//!< list of domain variables\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Material properties are classes that can only be defined as properties of other materials\nclass FECORE_API FEMaterialProperty : public FEMaterialBase\n{\n\tFECORE_SUPER_CLASS(FEMATERIALPROP_ID)\n\npublic:\n\tFEMaterialProperty(FEModel* fem);\n\n\t// evaluate local coordinate system at material point\n\tmat3d GetLocalCS(const FEMaterialPoint& mp) override;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEMaterialPoint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMaterialPoint.h\"\n#include \"DumpStream.h\"\n#include <string.h>\n\nFEMaterialPointData::FEMaterialPointData(FEMaterialPointData* ppt)\n{\n\tm_pPrev = 0;\n\tm_pNext = ppt;\n\tif (ppt) ppt->m_pPrev = this;\n}\n\nFEMaterialPointData::~FEMaterialPointData()\n{ \n\tif (m_pNext) delete m_pNext;\n\tm_pNext = m_pPrev = 0;\n}\n\nvoid FEMaterialPointData::SetPrev(FEMaterialPointData* pt)\n{\n\tm_pPrev = pt;\n}\n\nvoid FEMaterialPointData::SetNext(FEMaterialPointData* pt)\n{\n\tm_pNext = pt;\n\tif (pt) pt->m_pPrev = this;\n}\n\nvoid FEMaterialPointData::Append(FEMaterialPointData* pt)\n{\n\tif (pt == nullptr) return;\n\tif (m_pNext) m_pNext->Append(pt);\n\telse SetNext(pt);\n}\n\nvoid FEMaterialPointData::Init()\n{\n\tif (m_pNext) m_pNext->Init();\n}\n\nvoid FEMaterialPointData::Update(const FETimeInfo& timeInfo)\n{\n\tif (m_pNext) m_pNext->Update(timeInfo);\n}\n\nvoid FEMaterialPointData::Serialize(DumpStream& ar)\n{\n\tif (m_pNext) m_pNext->Serialize(ar);\n}\n\n//=================================================================================================\nFEMaterialPoint::FEMaterialPoint(FEMaterialPointData* data)\n{\n\tm_data = data;\n\tm_elem = nullptr;\n\tm_index = -1;\n\tm_shape = nullptr;\n\tm_J0 = m_Jt = 1;\n}\n\nFEMaterialPoint::~FEMaterialPoint()\n{\n\tdelete m_data;\n}\n\n// NOTE: We need a copy constructor to create vectors of FEMaterialPoint,\n// but we should not actually need to copy anything\nFEMaterialPoint::FEMaterialPoint(const FEMaterialPoint&)\n{\n\tm_elem = nullptr;\n\tm_shape = nullptr;\n\tm_data = nullptr;\n\tm_J0 = m_Jt = 1;\n\tm_index = -1;\n}\n\nFEMaterialPoint& FEMaterialPoint::operator = (const FEMaterialPoint&)\n{\n\tm_elem = nullptr;\n\tm_shape = nullptr;\n\tm_data = nullptr;\n\tm_J0 = m_Jt = 1;\n\tm_index = -1;\n\treturn *this;\n}\n\nvoid FEMaterialPoint::Init()\n{\n\tif (m_data) m_data->Init();\n}\n\nFEMaterialPoint* FEMaterialPoint::Copy()\n{\n\tFEMaterialPoint* mp = new FEMaterialPoint(*this);\n\tif (m_data) mp->m_data = m_data->Copy();\n\treturn mp;\n}\n\nvoid FEMaterialPoint::Update(const FETimeInfo& timeInfo)\n{\n\tif (m_data) m_data->Update(timeInfo);\n}\n\nvoid FEMaterialPoint::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_r0 & m_J0 & m_Jt;\n\t}\n\tif (m_data) m_data->Serialize(ar);\n}\n\nvoid FEMaterialPoint::Append(FEMaterialPointData* pt)\n{\n\tif (pt == nullptr) return;\n\tassert(m_data);\n\tif (m_data) m_data->Append(pt);\n}\n\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFEMaterialPointArray::FEMaterialPointArray(FEMaterialPointData* ppt) : FEMaterialPointData(ppt)\n{\n\t\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterialPointArray::AddMaterialPoint(FEMaterialPoint* pt)\n{\n\tm_mp.push_back(pt);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterialPointArray::Init()\n{\n\tfor (int i = 0; i<(int)m_mp.size(); ++i) m_mp[i]->Init();\n\tFEMaterialPointData::Init();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterialPointArray::Serialize(DumpStream& ar)\n{\n\tFEMaterialPointData::Serialize(ar);\n\tfor (int i = 0; i<(int)m_mp.size(); ++i) m_mp[i]->Serialize(ar);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMaterialPointArray::Update(const FETimeInfo& timeInfo)\n{\n\tFEMaterialPointData::Update(timeInfo);\n\tfor (int i = 0; i<(int)m_mp.size(); ++i) m_mp[i]->Update(timeInfo);\n}\n"
  },
  {
    "path": "FECore/FEMaterialPoint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"mat3d.h\"\n#include \"quatd.h\"\n#include \"FETimeInfo.h\"\n#include <vector>\n\nclass FEElement;\nclass FEMaterialPoint;\n\n//-----------------------------------------------------------------------------\n//! Material point class\n\n//! This class implements the concept of a material point. This point carries\n//! with it not only information about its location, both in the reference and  \n//! current configuration but also about the local deformation. In addition\n//! it contains the state information that is associated with the current\n//! point.\n//! \n\nclass FECORE_API FEMaterialPointData\n{\npublic:\n\tFEMaterialPointData(FEMaterialPointData* ppt = 0);\n\tvirtual ~FEMaterialPointData();\n\npublic:\n\t//! The init function is used to intialize data\n\tvirtual void Init();\n\n\t//! The Update function is used to update material point data\n\t//! Note that this gets called at the start of the time step during PreSolveUpdate\n\tvirtual void Update(const FETimeInfo& timeInfo);\n\n\t//! copy material point data (for running restarts) \\todo Is this still used?\n\tvirtual FEMaterialPointData* Copy() { return 0; }\n\n\t// serialization\n\tvirtual void Serialize(DumpStream& ar);\n\npublic:\n\t//! Get the next material point data\n\tFEMaterialPointData* Next() { return m_pNext; }\n\n\t//! Get the previous (parent) material point data\n\tFEMaterialPointData* Prev() { return m_pPrev; }\n    \n\t// assign the previous pointer\n\tvoid SetPrev(FEMaterialPointData* pt);\n\n\t//! assign the next pointer\n\t//! this also sets the prev pointer of the passed pointer\n\t//! in other words, it makes this the parent of the passed pointer\n\tvoid SetNext(FEMaterialPointData* pt);\n\n\t//! append a material point\n\tvoid Append(FEMaterialPointData* pt);\n\nprotected:\n\tvirtual int Components() const { return 0; }\n\tvirtual FEMaterialPoint* GetPointData(int i) { return nullptr; }\n\npublic:\n\t//! Extract data (\\todo Is it safe for a plugin to use this function?)\n\ttemplate <class T> T* ExtractData();\n\ttemplate <class T> const T* ExtractData() const;\n\nprotected:\n\tFEMaterialPointData*\tm_pNext;    //!< next data in the list\n\tFEMaterialPointData*\tm_pPrev;    //!< previous data in the list\n\n\tfriend class FEMaterialPoint;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMaterialPoint\n{\npublic:\n\tFEMaterialPoint(FEMaterialPointData* data = nullptr);\n\tvirtual ~FEMaterialPoint();\n\n\t// TODO: These functions copy  nothing! They are only included because we need them to create vectors!\n\t//       I would like to delete these functions, but this means they cannot be used in vectors anymore.\n\tFEMaterialPoint(const FEMaterialPoint&);\n\tFEMaterialPoint& operator = (const FEMaterialPoint&);\n\n\t//! The init function is used to intialize data\n\tvirtual void Init();\n\n\tvirtual FEMaterialPoint* Copy();\n\n\t//! The Update function is used to update material point data\n\t//! Note that this gets called at the start of the time step during PreSolveUpdate\n\tvirtual void Update(const FETimeInfo& timeInfo);\n\n\tvirtual void Serialize(DumpStream& ar);\n\n\tvoid Append(FEMaterialPointData* pt);\n\npublic:\n\tint Components() const { return (m_data ? m_data->Components() : 0); }\n\n\tFEMaterialPoint* GetPointData(int i = 0)\n\t{\n\t\tif (m_data == nullptr) return this;\n\t\tFEMaterialPoint* mp = m_data->GetPointData(i);\n\t\tif (mp == nullptr) mp = this;\n\t\treturn mp;\n\t}\n\npublic:\n\t//! Extract data (\\todo Is it safe for a plugin to use this function?)\n\ttemplate <class T> T* ExtractData();\n\ttemplate <class T> const T* ExtractData() const;\n\npublic:\n\tvec3d\t\tm_r0;\t\t//!< material point position\n\tvec3d\t\tm_rt;\t\t//!< current point position\n\tdouble\t\tm_J0;\t\t//!< reference Jacobian\n\tdouble\t\tm_Jt;\t\t//!< current Jacobian\n\tquatd\t\tm_Q;\t\t//!< local coordinates\n\tFEElement* m_elem;\t\t//!< Element where this material point is\n\tint\t\t\tm_index;\t//!< local integration point index \n\n\t// pointer to element's shape function values\n\tdouble* m_shape;\n\nprotected:\n\tFEMaterialPointData* m_data;\n};\n\n//-----------------------------------------------------------------------------\ntemplate <class T> inline T* FEMaterialPointData::ExtractData()\n{\n\t// first see if this is the correct type\n\tT* p = dynamic_cast<T*>(this);\n\tif (p) return p;\n\n\t// check all the child classes \n\tFEMaterialPointData* pt = this;\n\twhile (pt->m_pNext)\n\t{\n\t\tpt = pt->m_pNext;\n\t\tp = dynamic_cast<T*>(pt);\n\t\tif (p) return p;\n\t}\n\n\t// search up\n\tpt = this;\n\twhile (pt->m_pPrev)\n\t{\n\t\tpt = pt->m_pPrev;\n\t\tp = dynamic_cast<T*>(pt);\n\t\tif (p) return p;\n\t}\n\n\tif (Components() > 0)\n\t{\n\t\tfor (int i = 0; i < Components(); ++i)\n\t\t{\n\t\t\tFEMaterialPoint* mpi = GetPointData(i);\n\t\t\tp = mpi->ExtractData<T>();\n\t\t\tif (p) return p;\n\t\t}\n\t}\n\n\t// Everything has failed. Material point data can not be found\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <class T> inline const T* FEMaterialPointData::ExtractData() const\n{\n\t// first see if this is the correct type\n\tconst T* p = dynamic_cast<const T*>(this);\n\tif (p) return p;\n\n\t// check all the child classes \n\tconst FEMaterialPointData* pt = this;\n\twhile (pt->m_pNext)\n\t{\n\t\tpt = pt->m_pNext;\n\t\tp = dynamic_cast<const T*>(pt);\n\t\tif (p) return p;\n\t}\n\n\t// search up\n\tpt = this;\n\twhile (pt->m_pPrev)\n\t{\n\t\tpt = pt->m_pPrev;\n\t\tp = dynamic_cast<const T*>(pt);\n\t\tif (p) return p;\n\t}\n\n\t// Everything has failed. Material point data can not be found\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\ntemplate <class T> inline T* FEMaterialPoint::ExtractData()\n{\n\treturn (m_data ? m_data->ExtractData<T>() : nullptr);\n}\n\n//-----------------------------------------------------------------------------\ntemplate <class T> inline const T* FEMaterialPoint::ExtractData() const\n{\n\treturn (m_data ? m_data->ExtractData<T>() : nullptr);\n}\n\n//-----------------------------------------------------------------------------\n// Material point base class for materials that define vector properties\nclass FECORE_API FEMaterialPointArray : public FEMaterialPointData\n{\npublic:\n\tFEMaterialPointArray(FEMaterialPointData* ppt = nullptr);\n\n\t//! initialization\n\tvoid Init() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! material point update\n\tvoid Update(const FETimeInfo& timeInfo) override;\n\npublic:\n\t//! Add a child material point\n\tvoid AddMaterialPoint(FEMaterialPoint* pt);\n\n\t//! get the number of material point components\n\tint Components() const override { return (int)m_mp.size(); }\n\n\t//! retrieve point data\n\tFEMaterialPoint* GetPointData(int i) override { return m_mp[i]; }\n\nprotected:\n\tstd::vector<FEMaterialPoint*>\tm_mp;\t//!< material point data for indidivual properties\n};\n"
  },
  {
    "path": "FECore/FEMaterialPointProperty.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n#include \"FEMaterialPoint.h\"\n#include \"fecore_type.h\"\n\n//--------------------------------------------------------------------\n// This class defines a material point property. Material point properties\n// are used to move data in and out of material points.\nclass FEMaterialPointProperty\n{\npublic:\n\tFEMaterialPointProperty(FEDataType dataType) : m_type(dataType) {}\n\tvirtual ~FEMaterialPointProperty() {}\n\n\tFEDataType dataType() const { return m_type; }\n\n\tvirtual void set(FEMaterialPoint& mp, const double& v) {}\n\tvirtual void set(FEMaterialPoint& mp, const vec3d&  v) {}\n\tvirtual void set(FEMaterialPoint& mp, const mat3d&  v) {}\n\n\tvirtual void get(FEMaterialPoint& mp, double& v) {}\n\tvirtual void get(FEMaterialPoint& mp, vec3d&  v) {}\n\tvirtual void get(FEMaterialPoint& mp, mat3d&  v) {}\n\nprivate:\n\tFEDataType\tm_type;\n};\n\n//--------------------------------------------------------------------\n// Template class for defining material point property classes. \n// Derived classes need to override the data member. \ntemplate <class T, class A> class FEMaterialPointProperty_T : public FEMaterialPointProperty\n{\npublic:\n\tFEMaterialPointProperty_T() : FEMaterialPointProperty(fecoreType<A>::type()) {}\n\tvoid set(FEMaterialPoint& mp, const A& Q)\n\t{\n\t\tT& pt = *mp.ExtractData<T>();\n\t\tdata(pt) = Q;\n\t}\n\n\tvoid get(FEMaterialPoint& mp, A& Q)\n\t{\n\t\tT& pt = *mp.ExtractData<T>();\n\t\tQ = data(pt);\n\t}\n\n\tvirtual A& data(T& pt) = 0;\n};\n"
  },
  {
    "path": "FECore/FEMathController.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMathController.h\"\n#include \"FEModel.h\"\n\nBEGIN_FECORE_CLASS(FEMathController, FELoadController)\n\tADD_PARAMETER(m_var, \"var\");\n\tADD_PARAMETER(m_math, \"math\");\nEND_FECORE_CLASS();\n\nFEMathController::FEMathController(FEModel* fem) : FELoadController(fem)\n{\n}\n\nbool FEMathController::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\tm_val.AddVariable(\"t\");\n\tchar sz[64] = { 0 };\n\tfor (int i = 0; i < (int)m_var.size(); ++i)\n\t{\n\t\tParamString ps(m_var[i].c_str());\n\t\tFEParamValue param = fem.GetParameterValue(ps);\n\t\tif (param.isValid() == false) return false;\n\t\tif (param.type() != FE_PARAM_DOUBLE) return false;\n\n\t\tm_param.push_back(param);\n\n\t\tsnprintf(sz, sizeof(sz), \"var_%d\", i);\n\t\tm_val.AddVariable(sz);\n\t}\n\n\tif (m_val.Create(m_math) == false) return false;\n\n\treturn FELoadController::Init();\n}\n\ndouble FEMathController::GetValue(double time)\n{\n\tvector<double> p(1 + m_param.size());\n\tp[0] = time;\n\tfor (int i = 0; i < m_param.size(); ++i) p[1 + i] = m_param[i].value<double>();\n\treturn m_val.value_s(p);\n}\n"
  },
  {
    "path": "FECore/FEMathController.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FELoadController.h\"\n#include \"MathObject.h\"\n#include <vector>\n#include <string>\n\nclass FEMathController : public FELoadController\n{\npublic:\n\tFEMathController(FEModel* fem);\n\n\tbool Init() override;\n\nprotected:\n\tdouble GetValue(double time) override;\n\nprivate:\n\tstd::vector<std::string>\tm_var;\n\tstd::string\t\t\t\t\tm_math;\n\n\tMSimpleExpression\t\t\tm_val;\n\tstd::vector<FEParamValue>\tm_param;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEMathIntervalController.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEMathIntervalController.h\"\n#include \"FEModel.h\"\n\nBEGIN_FECORE_CLASS(FEMathIntervalController, FELoadController)\n\tADD_PARAMETER(m_rng, 2, \"interval\");\n\tADD_PARAMETER(m_leftExtend , \"left_extend\", 0, \"zero\\0constant\\0repeat\\0\");\n\tADD_PARAMETER(m_rightExtend, \"right_extend\", 0, \"zero\\0constant\\0repeat\\0\");\n\tADD_PARAMETER(m_var, \"var\");\n\tADD_PARAMETER(m_math, \"math\");\nEND_FECORE_CLASS();\n\nFEMathIntervalController::FEMathIntervalController(FEModel* fem) : FELoadController(fem)\n{\n\tm_rng[0] = 0.0;\n\tm_rng[1] = 1.0;\n\tm_leftExtend = ExtendMode::CONSTANT;\n\tm_rightExtend = ExtendMode::CONSTANT;\n}\n\nbool FEMathIntervalController::Init()\n{\n\tdouble Dt = m_rng[1] - m_rng[0];\n\tif (Dt <= 0.0) return false;\n\n\tFEModel& fem = *GetFEModel();\n\tm_val.AddVariable(\"t\");\n\tchar sz[64] = { 0 };\n\tfor (int i = 0; i < (int)m_var.size(); ++i)\n\t{\n\t\tParamString ps(m_var[i].c_str());\n\t\tFEParamValue param = fem.GetParameterValue(ps);\n\t\tif (param.isValid() == false) return false;\n\t\tif (param.type() != FE_PARAM_DOUBLE) return false;\n\n\t\tm_param.push_back(param);\n\n\t\tsnprintf(sz, sizeof(sz), \"var_%d\", i);\n\t\tm_val.AddVariable(sz);\n\t}\n\n\tif (m_val.Create(m_math) == false) return false;\n\n\treturn FELoadController::Init();\n}\n\ndouble FEMathIntervalController::GetValue(double time)\n{\n\tif (time <= m_rng[0])\n\t{\n\t\tswitch (m_leftExtend)\n\t\t{\n\t\tcase ExtendMode::ZERO: return 0.0; break;\n\t\tcase ExtendMode::CONSTANT: time = m_rng[0]; break;\n\t\tcase ExtendMode::REPEAT:\n\t\t{\n\t\t\tdouble Dt = m_rng[1] - m_rng[0];\n\t\t\ttime = m_rng[1] - fmod(m_rng[0] - time, Dt);\n\t\t}\n\t\tbreak;\n\t\t}\n\t}\n\telse if (time >= m_rng[1])\n\t{\n\t\tswitch (m_rightExtend)\n\t\t{\n\t\tcase ExtendMode::ZERO: return 0.0; break;\n\t\tcase ExtendMode::CONSTANT: time = m_rng[1]; break;\n\t\tcase ExtendMode::REPEAT:\n\t\t{\n\t\t\tdouble Dt = m_rng[1] - m_rng[0];\n\t\t\ttime = m_rng[0] + fmod(time - m_rng[0], Dt);\n\t\t}\n\t\tbreak;\n\t\t}\n\t}\n\n\tvector<double> p(1 + m_param.size());\n\tp[0] = time;\n\tfor (int i = 0; i < m_param.size(); ++i) p[1 + i] = m_param[i].value<double>();\n\treturn m_val.value_s(p);\n}\n"
  },
  {
    "path": "FECore/FEMathIntervalController.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FELoadController.h\"\n#include \"MathObject.h\"\n#include <vector>\n#include <string>\n\nclass FEMathIntervalController : public FELoadController\n{\npublic:\n\t//! Extend mode\n\tenum ExtendMode { ZERO, CONSTANT, REPEAT};\n\npublic:\n\tFEMathIntervalController(FEModel* fem);\n\n\tbool Init() override;\n\nprotected:\n\tdouble GetValue(double time) override;\n\nprivate:\n\tdouble\tm_rng[2];\t\t// range of interval\n\tint\t\tm_leftExtend;\t// left extend mode\n\tint\t\tm_rightExtend;\t// right extend mode\n\n\tstd::vector<std::string>\tm_var;\n\tstd::string\t\t\t\t\tm_math;\n\n\tMSimpleExpression\t\t\tm_val;\n\tstd::vector<FEParamValue>\tm_param;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "FECore/FEMergedConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMergedConstraint.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FELinearConstraintManager.h>\n#include <FECore/FELinearConstraint.h>\n\nFEMergedConstraint::FEMergedConstraint(FEModel& fem) : m_fem(fem)\n{\n}\n\nFEMergedConstraint::~FEMergedConstraint()\n{\n}\n\nbool FEMergedConstraint::Merge(FEFacetSet* surf1, FEFacetSet* surf2, const vector<int>& dofList)\n{\n\t// extract the nodes from the surfaces\n\tFENodeList set1 = surf1->GetNodeList();\n\tFENodeList set2 = surf2->GetNodeList();\n\n\t// find for each node on surface1 a corresponding node on surface 2 within tolerance\n\t// First, make sure that set2 is larger than set1\n\tif (set1.Size() > set2.Size()) return false;\n\tint N1 = set1.Size();\n\tint N2 = set2.Size();\n\n\t// make sure there is something to do\n\tif (N1 == 0) return true;\n\tif (dofList.size() == 0) return true;\n\n\t// alright, let's get going\n\tvector<int> tag(N1, -1);\n\tfor (int i=0; i<N1; ++i)\n\t{\n\t\t// get the node position\n\t\tvec3d ri = set1.Node(i)->m_rt;\n\n\t\t// find the closest node\n\t\tint n = 0;\n\t\tdouble Dmin = (set2.Node(0)->m_rt - ri).norm2();\n\t\tfor (int j=1; j<N2; ++j)\n\t\t{\n\t\t\tvec3d rj = set2.Node(j)->m_rt;\n\t\t\tdouble D2 = (ri - rj).norm2();\n\t\t\tif (D2 < Dmin)\n\t\t\t{\n\t\t\t\tDmin = D2;\n\t\t\t\tn = j;\n\t\t\t}\n\t\t}\n\n\t\t// since this interface type assumes that the nodes match identically,\n\t\t// we check that the min distance is indeed very small\n\t\tif (Dmin > 1e-9) { \n\t\t\treturn false;\n\t\t}\n\n\t\t// store this node\n\t\ttag[i] = n;\n\t}\n\n\t// next, create the linear constraints \n\t// get the linear constraint manager\n\tint ndofs = (int) dofList.size();\n\tFELinearConstraintManager& LCM = m_fem.GetLinearConstraintManager();\n\tfor (int i=0; i<N1; ++i)\n\t{\n\t\tfor (int j=0; j<ndofs; ++j)\n\t\t{\n\t\t\tint dof = dofList[j];\n\t\t\tFELinearConstraint* lc = fecore_alloc(FELinearConstraint, &m_fem);\n\t\t\tlc->SetParentDof(dof, set1[i]);\n\t\t\tlc->AddChildDof(dof, set2[tag[i]], 1.0);\n\n\t\t\tLCM.AddLinearConstraint(lc);\n\t\t}\n\t}\n\n\t// all done\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/FEMergedConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/FEMesh.h>\n#include \"fecore_api.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n// Forward declaration of FEModel class\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// A merged constraint takes two surfaces and merges them by matching each node of one surface\n// to the corresponding node on the other surface and then generates a linear constraint \n// between the two nodes that essentially matches the degrees of freedom.\nclass FECORE_API FEMergedConstraint\n{\npublic:\n\tFEMergedConstraint(FEModel& fem);\n\t~FEMergedConstraint();\n\n\tbool Merge(FEFacetSet* surf1, FEFacetSet* surf2, const std::vector<int>& dofList);\n\nprivate:\n\tFEModel&\tm_fem;\n};\n"
  },
  {
    "path": "FECore/FEMesh.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEMesh.h\"\n#include \"FEException.h\"\n#include \"FEDiscreteDomain.h\"\n#include \"FETrussDomain.h\"\n#include \"FEShellDomain.h\"\n#include \"FESolidDomain.h\"\n#include \"FEDomain2D.h\"\n#include \"DOFS.h\"\n#include \"FEElemElemList.h\"\n#include \"FEElementList.h\"\n#include \"FESurface.h\"\n#include \"FEDataArray.h\"\n#include \"FEDomainMap.h\"\n#include \"FESurfaceMap.h\"\n#include \"FENodeDataMap.h\"\n#include \"DumpStream.h\"\n#include \"FECoreKernel.h\"\n#include <algorithm>\n\n//-----------------------------------------------------------------------------\nFEDataMap* CreateDataMap(int mapType)\n{\n\tFEDataMap* map = nullptr;\n\tswitch (mapType)\n\t{\n\tcase FE_NODE_DATA_MAP: map = new FENodeDataMap; break;\n\tcase FE_DOMAIN_MAP   : map = new FEDomainMap; break;\n\tcase FE_SURFACE_MAP  : map = new FESurfaceMap; break;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn map;\n}\n\n//=============================================================================\n// FEMesh\n//-----------------------------------------------------------------------------\nFEMesh::FEMesh(FEModel* fem) : m_fem(fem)\n{\n\tm_ELT = nullptr;\n\tm_NLT = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEMesh::~FEMesh()\n{\n\tClear();\n}\n\n//-----------------------------------------------------------------------------\n//! return number of nodes\nint FEMesh::Nodes() const \n{ \n\treturn (int)m_Node.size(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::Serialize(DumpStream& ar)\n{\n\t// clear the mesh if we are loading from an archive\n\tif ((ar.IsShallow() == false) && (ar.IsLoading())) Clear();\n\n\t// we don't want to store pointers to all the nodes\n\t// mostly for efficiency, so we tell the archive not to store the pointers\n\tar.LockPointerTable();\n\t{\n\t\t// store the node list\n\t\tar & m_Node;\n\t}\n\tar.UnlockPointerTable();\n\n\t// if this is a shallow archive, we're done\n\tif (ar.IsShallow()) return;\n\n\t// serialize node sets\n\tar & m_NodeSet;\n\n\tif (ar.IsSaving())\n\t{\n\t\t// write segment sets\n\t\tint ssets = SegmentSets();\n\t\tar << ssets;\n\t\tfor (int i=0; i<ssets; ++i)\n\t\t{\n\t\t\tFESegmentSet& sset = SegmentSet(i);\n\t\t\tsset.Serialize(ar);\n\t\t}\n\n\t\t// write element sets\n\t\tar << m_ElemSet;\n\n\t\t// write facet sets\n\t\tar << m_FaceSet;\n\n\t\t// write surface pairs\n\t\tint surfPairs = (int)m_SurfPair.size();\n\t\tar << surfPairs;\n\t\tfor (int i = 0; i < surfPairs; ++i)\n\t\t{\n\t\t\tFESurfacePair& sp = *m_SurfPair[i];\n\t\t\tar << sp.GetName();\n\t\t\tar << sp.GetPrimarySurface()->GetName();\n\t\t\tar << sp.GetSecondarySurface()->GetName();\n\t\t}\n\n\t\t// write discrete sets\n\t\tint dsets = DiscreteSets();\n\t\tar << dsets;\n\t\tfor (int i=0; i<dsets; ++i)\n\t\t{\n\t\t\tFEDiscreteSet& dset = DiscreteSet(i);\n\t\t\tdset.Serialize(ar);\n\t\t}\n\n\t\t// write data maps\n\t\tint maps = DataMaps();\n\t\tar << maps;\n\t\tfor (int i = 0; i < maps; ++i)\n\t\t{\n\t\t\tFEDataMap* map = GetDataMap(i);\n\t\t\tar << map;\n\t\t}\n\t}\n\telse\n\t{\n\t\tFEModel* fem = &ar.GetFEModel();\n\n\t\t// read segment sets\n\t\tint ssets = 0;\n\t\tar >> ssets;\n\t\tfor (int i=0; i<ssets; ++i)\n\t\t{\n\t\t\tFESegmentSet* sset = new FESegmentSet(fem);\n\t\t\tAddSegmentSet(sset);\n\t\t\tsset->Serialize(ar);\n\t\t}\n\n\t\t// read element sets\n\t\tar >> m_ElemSet;\n\n\t\t// read facet sets\n\t\tar >> m_FaceSet;\n\n\t\t// read surface pairs\n\t\tint surfPairs = 0;\n\t\tar >> surfPairs;\n\t\tfor (int i = 0; i < surfPairs; ++i)\n\t\t{\n\t\t\tFESurfacePair* sp = new FESurfacePair(this);\n\t\t\tstd::string name;\n\t\t\tar >> name; \n\t\t\tsp->SetName(name);\n\n\t\t\tar >> name;\n\t\t\tFEFacetSet* ps = FindFacetSet(name);\n\t\t\tsp->SetPrimarySurface(ps);\n\n\t\t\tar >> name;\n\t\t\tps = FindFacetSet(name);\n\t\t\tsp->SetSecondarySurface(ps);\n\n\t\t\tAddSurfacePair(sp);\n\t\t}\n\n\t\t// read discrete sets\n\t\tint dsets = 0;\n\t\tar >> dsets;\n\t\tfor (int i=0; i<dsets; ++i)\n\t\t{\n\t\t\tFEDiscreteSet* dset = new FEDiscreteSet(this);\n\t\t\tAddDiscreteSet(dset);\n\t\t\tdset->Serialize(ar);\n\t\t}\n\n\t\t// write data maps\n\t\tClearDataMaps();\n\t\tint maps = 0;\n\t\tar >> maps;\n\t\tfor (int i = 0; i < maps; ++i)\n\t\t{\n\t\t\tFEDataMap* map = nullptr;\n\t\t\tar >> map;\n\t\t\tAddDataMap(map);\n\t\t}\n\n\t\tUpdateBox();\n\t}\n}\n\nvoid FEMesh::SerializeDomains(DumpStream& ar)\n{\n\t// stream domain data\n\tar & m_Domain;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::SaveClass(DumpStream& ar, FEMesh* p)\n{\n\t// we should never get here\n\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\nFEMesh* FEMesh::LoadClass(DumpStream& ar, FEMesh* p)\n{\n\t// we should never get here\n\tassert(false);\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//  Allocates storage for mesh data.\n//\nvoid FEMesh::CreateNodes(int nodes)\n{\n\tassert(nodes);\n\tm_Node.resize(nodes);\n\n\t// set the default node IDs\n\tfor (int i=0; i<nodes; ++i) Node(i).SetID(i+1);\n\n\tm_NEL.Clear();\n\tm_EEL.Clear();\n\n\tdelete m_ELT; m_ELT = nullptr;\n\tdelete m_NLT; m_NLT = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n// Make more room for nodes\nvoid FEMesh::AddNodes(int nodes)\n{\n\tassert(nodes);\n\tint N0 = (int) m_Node.size();\n\n\t// get the ID of the last node\n\t// (It is assumed that nodes are sorted according their ID\n\t//  so the last node should have the highest ID)\n\tint n0 = 1;\n\tif (N0 > 0) n0 = m_Node[N0-1].GetID() + 1;\n\n\tm_Node.resize(N0 + nodes);\n\tfor (int i=0; i<nodes; ++i) m_Node[i+N0].SetID(n0+i);\n\n\tdelete m_ELT; m_ELT = nullptr;\n\tdelete m_NLT; m_NLT = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::SetDOFS(int n)\n{\n\tint NN = Nodes();\n#pragma omp parallel for\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tm_Node[i].SetDOFS(n);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Return the total number elements\nint FEMesh::Elements() const\n{\n\tint N = 0;\n\tfor (int i=0; i<(int) m_Domain.size(); ++i) \n\t{\n\t\tN += m_Domain[i]->Elements();\n\t}\n\treturn N;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the total number of elements of a specific domain type\nint FEMesh::Elements(int ndom_type) const\n{\n\tint N = 0;\n\tfor (int i=0; i<(int) m_Domain.size(); ++i) \n\t{\n\t\tFEDomain& dom = *m_Domain[i];\n\t\tif (dom.Class() == ndom_type) N += m_Domain[i]->Elements();\n\t}\n\treturn N;\n}\n\n//-----------------------------------------------------------------------------\n//! return reference to a node\nFENode& FEMesh::Node(int i) { return m_Node[i]; }\nconst FENode& FEMesh::Node(int i) const { return m_Node[i]; }\n\n//-----------------------------------------------------------------------------\n//  Updates the bounding box of the mesh (using current coordinates)\n//\nvoid FEMesh::UpdateBox()\n{\n\tif (Nodes() > 0)\n\t{\n\t\tm_box = FEBoundingBox(Node(0).m_rt);\n\t\tfor (int i=1; i<Nodes(); ++i)\n\t\t{\n\t\t\tm_box.add(Node(i).m_rt);\n\t\t}\n\t}\n\telse m_box = FEBoundingBox(vec3d(0,0,0));\n}\n\n//-----------------------------------------------------------------------------\n//  Counts the number of shell elements in the mesh\n//\nint FEMesh::RemoveIsolatedVertices()\n{\n\tint i, j, k, N = Nodes(), n;\n\n\t// create a valence array\n\tvector<int> val; val.assign(N, 0);\n\n\t// count the nodal valences\n\tfor (i=0; i<(int) m_Domain.size(); ++i)\n\t{\n\t\tFEDomain& d = Domain(i);\n\t\tfor (j=0; j<d.Elements(); ++j)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(j);\n\t\t\tn = el.Nodes();\n\t\t\tfor (k=0; k<n; ++k) ++val[el.m_node[k]];\n\t\t}\n\t}\n\n\t// See if there are any isolated nodes\n\t// Exclude them from the analysis\n\tint ni = 0;\n\tfor (i=0; i<N; ++i)\n\t\tif (val[i] == 0)\n\t\t{\n\t\t\t++ni;\n\t\t\tFENode& node = Node(i);\n\t\t\tnode.SetFlags(FENode::EXCLUDE);\n\t\t}\n\n\treturn ni;\n}\n\n//-----------------------------------------------------------------------------\n//! Does one-time initialization of the Mesh material point data.\nvoid FEMesh::InitMaterialPoints()\n{\n    for (int i = 0; i<Domains(); ++i)\n    {\n        FEDomain& dom = Domain(i);\n        dom.InitMaterialPoints();\n    }\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::Clear()\n{\n\tm_Node.clear();\n\tfor (size_t i=0; i<m_Domain.size (); ++i) delete m_Domain [i];\n\n\t// TODO: Surfaces and edges are currently managed by the classes that use them so don't delete them\n//\tfor (size_t i=0; i<m_Surf.size   (); ++i) delete m_Surf[i];\n//\tfor (size_t i=0; i<m_Edge.size   (); ++i) delete m_Edge[i];\n\n\tfor (size_t i=0; i<m_NodeSet.size (); ++i) delete m_NodeSet [i];\n\tfor (size_t i=0; i<m_LineSet.size (); ++i) delete m_LineSet [i];\n\tfor (size_t i=0; i<m_ElemSet.size (); ++i) delete m_ElemSet [i];\n\tfor (size_t i=0; i<m_DiscSet.size (); ++i) delete m_DiscSet [i];\n\tfor (size_t i=0; i<m_FaceSet.size (); ++i) delete m_FaceSet [i];\n\tfor (size_t i=0; i<m_SurfPair.size(); ++i) delete m_SurfPair[i];\n\tfor (size_t i=0; i<m_DomList.size (); ++i) delete m_DomList [i];\n\n\tm_Domain.clear();\n\tm_Surf.clear();\n\tm_NodeSet.clear();\n\tm_LineSet.clear();\n\tm_ElemSet.clear();\n\tm_DiscSet.clear();\n\tm_FaceSet.clear();\n\tm_SurfPair.clear();\n\tm_DomList.clear();\n\n\tm_NEL.Clear();\n\tm_EEL.Clear();\n\tif (m_ELT) delete m_ELT; m_ELT = nullptr;\n\tif (m_NLT) delete m_NLT; m_NLT = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Reset the mesh data. Return nodes to their intial position, reset their \n//! attributes and zero all element stresses.\n\nvoid FEMesh::Reset()\n{\n\t// reset nodal data\n\tfor (int i=0; i<Nodes(); ++i) \n\t{\n\t\tFENode& node = Node(i);\n\n\t\tnode.m_rp = node.m_rt = node.m_r0;\n\t\tnode.m_vp = vec3d(0,0,0);\n\t\tnode.m_ap = node.m_at = vec3d(0,0,0);\n        node.m_dp = node.m_dt = node.m_d0;\n\n\t\t// reset ID arrays\n\t\tint ndof = (int)node.dofs();\n\t\tfor (int i=0; i<ndof; ++i) \n\t\t{\n\t\t\tnode.set_inactive(i);\n\t\t\tnode.set_bc(i, DOF_OPEN);\n\t\t\tnode.set(i, 0.0);\n\t\t\tnode.set_load(i, 0.0);\n\t\t}\n\t}\n\n\t// update the mesh\n\tUpdateBox();\n\n\t// reset domain data\n\tfor (int n=0; n<(int) m_Domain.size(); ++n) m_Domain[n]->Reset();\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the (initial) volume of an element. In some case, the volume\n//! may only be approximate.\ndouble FEMesh::ElementVolume(FEElement &el)\n{\n\tdouble V = 0;\n\tswitch (el.Class())\n\t{\n\tcase FE_ELEM_SOLID:\n\t{\n\t\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(el.GetMeshPartition()); assert(dom);\n\t\tif (dom) V = dom->Volume(static_cast<FESolidElement&>(el));\n\t}\n\tbreak;\n\tcase FE_ELEM_SHELL: \n\t{\n\t\tFEShellDomain* dom = dynamic_cast<FEShellDomain*>(el.GetMeshPartition()); assert(dom);\n\t\tif (dom) V = dom->Volume(static_cast<FEShellElement&>(el));\n\t}\n\tbreak;\n\t}\n\treturn V;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the (initial) volume of an element. In some case, the volume\n//! may only be approximate.\ndouble FEMesh::CurrentElementVolume(FEElement& el)\n{\n\tdouble V = 0;\n\tswitch (el.Class())\n\t{\n\tcase FE_ELEM_SOLID:\n\t{\n\t\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(el.GetMeshPartition()); assert(dom);\n\t\tif (dom) V =dom->CurrentVolume(static_cast<FESolidElement&>(el));\n\t}\n\tbreak;\n\tcase FE_ELEM_SHELL:\n\t{\n\t\tFEShellDomain* dom = dynamic_cast<FEShellDomain*>(el.GetMeshPartition()); assert(dom);\n\t\tif (dom) return dom->CurrentVolume(static_cast<FEShellElement&>(el));\n\t}\n\tbreak;\n\t}\n\treturn V;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a nodeset by name\n\nFENodeSet* FEMesh::FindNodeSet(const std::string& name)\n{\n\tfor (size_t i=0; i<m_NodeSet.size(); ++i) if (m_NodeSet[i]->GetName() == name) return m_NodeSet[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a segment set set by name\n\nFESegmentSet* FEMesh::FindSegmentSet(const std::string& name)\n{\n\tfor (size_t i=0; i<m_LineSet.size(); ++i) if (m_LineSet[i]->GetName() ==  name) return m_LineSet[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a surface set set by name\n\nFESurface* FEMesh::FindSurface(const std::string& name)\n{\n\tfor (size_t i=0; i<m_Surf.size(); ++i) if (m_Surf[i]->GetName() == name) return m_Surf[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a surface set set by name and returns its index\n\nint FEMesh::FindSurfaceIndex(const std::string& name)\n{\n\tfor (size_t i = 0; i < m_Surf.size(); ++i) if (m_Surf[i]->GetName() == name) return i;\n\treturn -1;\n}\n\nFESurface* FEMesh::CreateSurface(FEFacetSet& facetSet)\n{\n\tFESurface* surf = fecore_alloc(FESurface, m_fem);\n\tsurf->Create(facetSet);\n\tAddSurface(surf);\n\treturn surf;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a discrete element set set by name\n\nFEDiscreteSet* FEMesh::FindDiscreteSet(const std::string& name)\n{\n\tfor (size_t i=0; i<m_DiscSet.size(); ++i) if (m_DiscSet[i]->GetName() == name) return m_DiscSet[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a element set by name\n\nFEElementSet* FEMesh::FindElementSet(const std::string& name)\n{\n\tfor (size_t i=0; i<m_ElemSet.size(); ++i) if (m_ElemSet[i]->GetName() == name) return m_ElemSet[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nFEFacetSet* FEMesh::FindFacetSet(const std::string& name)\n{\n\tfor (size_t i = 0; i<(int)m_FaceSet.size(); ++i) if (m_FaceSet[i]->GetName() == name) return m_FaceSet[i];\n\treturn 0;\n}\n\nFESurfacePair* FEMesh::FindSurfacePair(const std::string& name)\n{\n\tfor (FESurfacePair* sp : m_SurfPair) if (sp->GetName() == name) return sp;\n\treturn nullptr;\n}\n\nFEDomainList* FEMesh::FindDomainList(const std::string& name)\n{\n\tfor (FEDomainList* dom : m_DomList) \n\t\tif (dom->GetName() == name) return dom;\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nint FEMesh::Domains() { return (int)m_Domain.size(); }\n\n//-----------------------------------------------------------------------------\nFEDomain& FEMesh::Domain(int n) { return *m_Domain[n]; }\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::AddDomain(FEDomain* pd)\n{ \n\tint N = (int)m_Domain.size();\n\tpd->SetID(N);\n\tm_Domain.push_back(pd); \n\tif (m_ELT) delete m_ELT; m_ELT = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a domain\n\nFEDomain* FEMesh::FindDomain(const std::string& name)\n{\n\tfor (size_t i = 0; i<m_Domain.size(); ++i) if (m_Domain[i]->GetName() == name) return m_Domain[i];\n\treturn 0;\n}\n\nint FEMesh::FindDomainIndex(const std::string& name)\n{\n\tfor (size_t i = 0; i < m_Domain.size(); ++i) if (m_Domain[i]->GetName() == name) return i;\n\treturn -1;\n}\n\nFEDomain* FEMesh::FindDomain(int domId)\n{\n\tfor (size_t i = 0; i<m_Domain.size(); ++i) if (m_Domain[i]->GetID() == domId) return m_Domain[i];\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! return an element\nFEElement* FEMesh::Element(int n)\n{\n\tif (n < 0) return nullptr;\n\tfor (int i = 0; i < Domains(); ++i)\n\t{\n\t\tFEDomain& dom = Domain(i);\n\t\tint NEL = dom.Elements();\n\t\tif (n < NEL) return &dom.ElementRef(n); \n\t\telse n -= NEL;\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Find a node from a given ID. return 0 if the node cannot be found.\n\nFENode* FEMesh::FindNodeFromID(int nid)\n{\n\tif (m_NLT == nullptr) m_NLT = new FENodeLUT(*this);\n\treturn m_NLT->Find(nid);\n}\n\nint FEMesh::FindNodeIndexFromID(int nid)\n{\n\tif (m_NLT == nullptr) m_NLT = new FENodeLUT(*this);\n\treturn m_NLT->FindIndex(nid);\n}\n\n//-----------------------------------------------------------------------------\n//! Find an element from a given ID. return 0 if the element cannot be found.\n\nFEElement* FEMesh::FindElementFromID(int elemID)\n{\n\tif (m_ELT == nullptr) m_ELT = new FEElementLUT(*this);\n\treturn m_ELT->Find(elemID);\n}\n\nint FEMesh::FindElementIndexFromID(int elemID)\n{\n\tif (m_ELT == nullptr) m_ELT = new FEElementLUT(*this);\n\treturn m_ELT->FindIndex(elemID);\n}\n\n/*\nFEElement* FEMesh::FindElementFromID(int nid)\n{\n\tFEElement* pe = 0;\n\n\tfor (int i=0; i<Domains(); ++i)\n\t{\n\t\tFEDomain& d = Domain(i);\n\t\tpe = d.FindElementFromID(nid);\n\t\tif (pe) return pe;\n\t}\n\n\treturn pe;\n}\n*/\n\n\n//-----------------------------------------------------------------------------\n//! See if all elements are of a particular shape\nbool FEMesh::IsType(FE_Element_Shape eshape)\n{\n\tFEElementList elemList(*this);\n\tfor (FEElementList::iterator it = elemList.begin(); it != elemList.end(); ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tif (el.Shape() != eshape) return false;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Find the element in which point y lies\nFESolidElement* FEMesh::FindSolidElement(vec3d y, double r[3])\n{\n\tint ND = (int) m_Domain.size();\n\tfor (int i=0; i<ND; ++i)\n\t{\n\t\tif (m_Domain[i]->Class() == FE_DOMAIN_SOLID)\n\t\t{\n\t\t\tFESolidDomain& bd = static_cast<FESolidDomain&>(*m_Domain[i]);\n\t\t\tFESolidElement* pe = bd.FindElement(y, r);\n\t\t\tif (pe) return pe;\n\t\t}\n\t}\n\treturn 0;\n}\n\nFENodeElemList& FEMesh::NodeElementList()\n{\n\tif (m_NEL.Size() != m_Node.size()) m_NEL.Create(*this);\n\treturn m_NEL;\n}\n\nFEElemElemList& FEMesh::ElementElementList()\n{\n\tif (!m_EEL.IsValid()) m_EEL.Create(this);\n\treturn m_EEL;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::ClearDomains()\n{\n\tint N = Domains();\n\tfor (int i = 0; i < N; ++i) delete m_Domain[i];\n\tm_Domain.clear();\n\tif (m_ELT) delete m_ELT; m_ELT = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Rebuild the LUT\nvoid FEMesh::RebuildLUT()\n{\n\tif (m_ELT) delete m_ELT;\n\tm_ELT = new FEElementLUT(*this);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the surface representing the element boundaries\n//! boutside : include all exterior facets\n//! binside  : include all interior facets\nFESurface* FEMesh::ElementBoundarySurface(bool boutside, bool binside)\n{\n\tif ((boutside == false) && (binside == false)) return 0;\n\n\t// create the element neighbor list\n\tFEElemElemList EEL;\n\tEEL.Create(this);\n\n\t// get the number of elements in this mesh\n\tint NE = Elements();\n\n\t// count the number of facets we have to create\n\tint NF = 0;\n\tFEElementList EL(*this);\n\tFEElementList::iterator it = EL.begin();\n\tfor (int i=0; i<NE; ++i, ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tint nf = el.Faces();\n\t\tfor (int j=0; j<nf; ++j)\n\t\t{\n\t\t\tFEElement* pen = EEL.Neighbor(i, j);\n\t\t\tif ((pen == 0) && boutside) ++NF;\n\t\t\tif ((pen != 0) && (el.GetID() < pen->GetID()) && binside ) ++NF;\n\t\t}\n\t}\n\t// create the surface\n\tFESurface* ps = fecore_alloc(FESurface, GetFEModel());\n\tif (NF == 0) return ps;\n\tps->Create(NF);\n\n\t// build the surface elements\n\tint face[FEElement::MAX_NODES];\n\tNF = 0;\n\tit = EL.begin();\n\tfor (int i=0; i<NE; ++i, ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tint nf = el.Faces();\n\t\tfor (int j=0; j<nf; ++j)\n\t\t{\n\t\t\tFEElement* pen = EEL.Neighbor(i, j);\n\t\t\tif (((pen == 0) && boutside)||\n\t\t\t\t((pen != 0) && (el.GetID() < pen->GetID()) && binside ))\n\t\t\t{\n\t\t\t\tFESurfaceElement& se = ps->Element(NF++);\n\t\t\t\tint faceNodes = el.GetFace(j, face);\n\n\t\t\t\tswitch (faceNodes)\n\t\t\t\t{\n\t\t\t\tcase 4: se.SetType(FE_QUAD4G4); break;\n\t\t\t\tcase 8: se.SetType(FE_QUAD8G9); break;\n\t\t\t\tcase 9: se.SetType(FE_QUAD9G9); break;\n\t\t\t\tcase 3: se.SetType(FE_TRI3G1 ); break;\n\t\t\t\tcase 6: se.SetType(FE_TRI6G7 ); break;\n\t\t\t\tcase 7: se.SetType(FE_TRI7G7); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tse.m_elem[0].pe = &el;\n\t\t\t\tif (pen) se.m_elem[1].pe = pen;\n\t\t\t\t\n\t\t\t\tint nn = se.Nodes();\n\t\t\t\tfor (int k=0; k<nn; ++k)\n\t\t\t\t{\n\t\t\t\t\tse.m_node[k] = face[k];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// initialize the surface. \n\t// This will set the local surface element ID's and also set the m_nelem IDs.\n\tps->Init();\n\n\t// all done\n\treturn ps;\n}\n\nFESurface* FEMesh::ElementBoundarySurface(std::vector<FEDomain*> domains, bool boutside, bool binside)\n{\n\tif ((boutside == false) && (binside == false)) return nullptr;\n\n\t// create the element neighbor list\n\tFEElemElemList EEL;\n\tEEL.Create(this);\n\n\t// get the number of elements in this mesh\n\tint NE = Elements();\n\n\t// count the number of facets we have to create\n\tint NF = 0;\n\n\tfor (int i = 0; i < domains.size(); i++)\n\t{\n\t\tfor (int j = 0; j < domains[i]->Elements(); j++)\n\t\t{\n\t\t\tFEElement& el = domains[i]->ElementRef(j);\n\t\t\tint nf = el.Faces();\n\t\t\tfor (int k = 0; k<nf; ++k)\n\t\t\t{\n\t\t\t\tFEElement* pen = EEL.Neighbor(el.GetID()-1, k);\n\t\t\t\tif ((pen == nullptr) && boutside) ++NF;\n\t\t\t\telse if (pen && (std::find(domains.begin(), domains.end(), pen->GetMeshPartition()) == domains.end()) && boutside) ++NF;\n\t\t\t\tif ((pen != nullptr) && (el.GetID() < pen->GetID()) && binside && (std::find(domains.begin(), domains.end(), pen->GetMeshPartition()) != domains.end())) ++NF;\n\t\t\t}\n\t\t}\n\t}\n\n\t// create the surface\n\tFESurface* ps = fecore_alloc(FESurface, GetFEModel());\n\tif (NF == 0) return ps;\n\tps->Create(NF);\n\n\t// build the surface elements\n\tint face[FEElement::MAX_NODES];\n\tNF = 0;\n\tfor (int i = 0; i < domains.size(); i++)\n\t{\n\t\tfor (int j = 0; j < domains[i]->Elements(); j++)\n\t\t{\n\t\t\tFEElement& el = domains[i]->ElementRef(j);\n\t\t\tint nf = el.Faces();\n\t\t\tfor (int k = 0; k < nf; ++k)\n\t\t\t{\n\t\t\t\tFEElement* pen = EEL.Neighbor(el.GetID()-1, k);\n\t\t\t\tif (((pen == nullptr) && boutside) ||\n\t\t\t\t\t(pen && (std::find(domains.begin(), domains.end(), pen->GetMeshPartition()) == domains.end()) && boutside) ||\n\t\t\t\t\t((pen != nullptr) && (el.GetID() < pen->GetID()) && binside && (std::find(domains.begin(), domains.end(), pen->GetMeshPartition()) != domains.end())))\n\t\t\t\t{\n\t\t\t\t\tFESurfaceElement& se = ps->Element(NF++);\n\t\t\t\t\tint faceNodes = el.GetFace(k, face);\n\n\t\t\t\t\tswitch (faceNodes)\n\t\t\t\t\t{\n\t\t\t\t\tcase 4: se.SetType(FE_QUAD4G4); break;\n\t\t\t\t\tcase 8: se.SetType(FE_QUAD8G9); break;\n\t\t\t\t\tcase 9: se.SetType(FE_QUAD9G9); break;\n\t\t\t\t\tcase 3: se.SetType(FE_TRI3G1 ); break;\n\t\t\t\t\tcase 6: se.SetType(FE_TRI6G7 ); break;\n\t\t\t\t\tcase 7: se.SetType(FE_TRI7G7 ); break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tassert(false);\n\t\t\t\t\t}\n\n\t\t\t\t\tse.m_elem[0].pe = &el;\n\t\t\t\t\tif (pen) se.m_elem[1].pe = pen;\n\n\t\t\t\t\tint nn = se.Nodes();\n\t\t\t\t\tfor (int p = 0; p < nn; ++p)\n\t\t\t\t\t{\n\t\t\t\t\t\tse.m_node[p] = face[p];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// initialize the surface. \n\t// This will set the local surface element ID's and also set the m_nelem IDs.\n\tps->Init();\n\n\t// all done\n\treturn ps;\n}\n\nFEFacetSet* FEMesh::DomainBoundary(std::vector<FEDomain*> domains, bool boutside, bool binside)\n{\n\tFEDomainList tmp(domains);\n\treturn DomainBoundary(tmp, boutside, binside);\n}\n\nFEFacetSet* FEMesh::DomainBoundary(FEDomainList& domains, bool boutside, bool binside)\n{\n\tif ((boutside == false) && (binside == false)) return nullptr;\n\n\t// get the element neighbor list\n\tFEElemElemList& EEL = ElementElementList();\n\n\t// get the number of elements in this mesh\n\tint NE = Elements();\n\n\t// count the number of facets we have to create\n\tint NF = 0;\n\n\tfor (int i = 0; i < domains.size(); i++)\n\t{\n\t\tfor (int j = 0; j < domains[i]->Elements(); j++)\n\t\t{\n\t\t\tFEElement& el = domains[i]->ElementRef(j);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint index = FindElementIndexFromID(el.GetID());\n\t\t\t\tint nf = el.Faces();\n\t\t\t\tfor (int k = 0; k < nf; ++k)\n\t\t\t\t{\n\t\t\t\t\tFEElement* pen = EEL.Neighbor(index, k);\n\t\t\t\t\tif ((pen == nullptr) && boutside) ++NF;\n\t\t\t\t\telse if (pen && !pen->isActive() && boutside) ++NF;\n\t\t\t\t\telse if (pen)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEDomain* domk = dynamic_cast<FEDomain*>(pen->GetMeshPartition()); assert(domk);\n\t\t\t\t\t\tif (boutside && !domains.IsMember(domk)) ++NF;\n\t\t\t\t\t\telse if (binside && domains.IsMember(domk) && (el.GetID() < pen->GetID())) ++NF;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// create the surface\n\tFEFacetSet* ps = new FEFacetSet(GetFEModel());\n\tif (NF == 0) return ps;\n\tps->Create(NF);\n\n\t// build the surface elements\n\tint faceNodes[FEElement::MAX_NODES];\n\tNF = 0;\n\tfor (int i = 0; i < domains.size(); i++)\n\t{\n\t\tfor (int j = 0; j < domains[i]->Elements(); j++)\n\t\t{\n\t\t\tFEElement& el = domains[i]->ElementRef(j);\n\t\t\tif (el.isActive())\n\t\t\t{\n\t\t\t\tint index = FindElementIndexFromID(el.GetID());\n\t\t\t\tint nf = el.Faces();\n\t\t\t\tfor (int k = 0; k < nf; ++k)\n\t\t\t\t{\n\t\t\t\t\tFEElement* pen = EEL.Neighbor(index, k);\n\t\t\t\t\tbool addFace = false;\n\n\t\t\t\t\tif ((pen == nullptr) && boutside) addFace = true;\n\t\t\t\t\telse if (pen && !pen->isActive() && boutside) addFace = true;\n\t\t\t\t\telse if (pen)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEDomain* domk = dynamic_cast<FEDomain*>(pen->GetMeshPartition()); assert(domk);\n\t\t\t\t\t\tif (boutside && !domains.IsMember(domk)) addFace = true;\n\t\t\t\t\t\telse if (binside && domains.IsMember(domk) && (el.GetID() < pen->GetID())) addFace = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (addFace)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEFacetSet::FACET& f = ps->Face(NF++);\n\t\t\t\t\t\tint fn = el.GetFace(k, faceNodes);\n\n\t\t\t\t\t\tswitch (fn)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 4: f.ntype = FEFacetSet::FACET::QUAD4; break;\n\t\t\t\t\t\tcase 8: f.ntype = FEFacetSet::FACET::QUAD8; break;\n\t\t\t\t\t\tcase 9: f.ntype = FEFacetSet::FACET::QUAD9; break;\n\t\t\t\t\t\tcase 3: f.ntype = FEFacetSet::FACET::TRI3; break;\n\t\t\t\t\t\tcase 6: f.ntype = FEFacetSet::FACET::TRI6; break;\n\t\t\t\t\t\tcase 7: f.ntype = FEFacetSet::FACET::TRI7; break;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tassert(false);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int p = 0; p < fn; ++p)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tf.node[p] = faceNodes[p];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// all done\n\treturn ps;\n}\n\n//-----------------------------------------------------------------------------\n//! Retrieve the nodal coordinates of an element in the reference configuration.\nvoid FEMesh::GetInitialNodalCoordinates(const FEElement& el, vec3d* node)\n{\n\tconst int neln = el.Nodes();\n\tfor (int i=0; i<neln; ++i) node[i] = Node(el.m_node[i]).m_r0;\n}\n\n//-----------------------------------------------------------------------------\n//! Retrieve the nodal coordinates of an element in the current configuration.\nvoid FEMesh::GetNodalCoordinates(const FEElement& el, vec3d* node)\n{\n\tconst int neln = el.Nodes();\n\tfor (int i=0; i<neln; ++i) node[i] = Node(el.m_node[i]).m_rt;\n}\n\n//=============================================================================\nFENodeLUT::FENodeLUT(FEMesh& mesh)\n{\n\tm_mesh = &mesh;\n\n\t// get the ID ranges\n\tm_minID = -1;\n\tm_maxID = -1;\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tint nid = node.GetID();\n\t\tif ((nid < m_minID) || (m_minID == -1)) m_minID = nid;\n\t\tif ((nid > m_maxID) || (m_maxID == -1)) m_maxID = nid;\n\t}\n\n\t// allocate size\n\tint nsize = m_maxID - m_minID + 1;\n\tm_node.resize(nsize, -1);\n\n\t// fill the table\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tint nid = node.GetID();\n\t\tm_node[nid - m_minID] = i;\n\t}\n}\n\n// Find an element from its ID\nFENode* FENodeLUT::Find(int nodeID) const\n{\n\tif ((nodeID < m_minID) || (nodeID > m_maxID)) return nullptr;\n\treturn &m_mesh->Node(m_node[nodeID - m_minID]);\n}\n\nint FENodeLUT::FindIndex(int nodeID) const\n{\n\tif ((nodeID < m_minID) || (nodeID > m_maxID)) return -1;\n\treturn m_node[nodeID - m_minID];\n}\n\n//=============================================================================\nFEElementLUT::FEElementLUT(FEMesh& mesh)\n{\n\t// get the ID ranges\n\tm_minID = -1;\n\tm_maxID = -1;\n\tint NDOM = mesh.Domains();\n\tfor (int i=0; i<NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j=0; j<NE; ++j)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tint eid = el.GetID();\n\t\t\tif ((eid < m_minID) || (m_minID == -1)) m_minID = eid;\n\t\t\tif ((eid > m_maxID) || (m_maxID == -1)) m_maxID = eid;\n\t\t}\n\t}\n\n\t// allocate size\n\tint nsize = m_maxID - m_minID + 1;\n\tm_elem.resize(nsize, (FEElement*) 0);\n\tm_elid.resize(nsize, -1);\n\n\t// fill the table\n\tint index = 0;\n\tfor (int i = 0; i<NDOM; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j<NE; ++j, ++index)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tint eid = el.GetID();\n\t\t\tm_elem[eid - m_minID] = &el;\n\t\t\tm_elid[eid - m_minID] = index;\n\t\t}\n\t}\n}\n\n// Find an element from its ID\nFEElement* FEElementLUT::Find(int elemID) const\n{\n\tif ((elemID < m_minID) || (elemID > m_maxID)) return nullptr;\n\treturn m_elem[elemID - m_minID];\n}\n\nint FEElementLUT::FindIndex(int elemID) const\n{\n\tif ((elemID < m_minID) || (elemID > m_maxID)) return -1;\n\treturn m_elid[elemID - m_minID];\n}\n\n// update the domains of the mesh\nvoid FEMesh::Update(const FETimeInfo& tp)\n{\n\tfor (int i = 0; i<Domains(); ++i)\n\t{\n\t\tFEDomain& dom = Domain(i);\n\t\tif (dom.IsActive()) dom.Update(tp);\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::ClearDataMaps()\n{\n\tfor (int i = 0; i<(int)m_DataMap.size(); ++i) delete m_DataMap[i];\n\tm_DataMap.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMesh::AddDataMap(FEDataMap* map)\n{\n\tm_DataMap.push_back(map);\n}\n\n//-----------------------------------------------------------------------------\nFEDataMap* FEMesh::FindDataMap(const std::string& mapName)\n{\n\tfor (int i = 0; i<(int)m_DataMap.size(); ++i)\n\t{\n\t\tif (m_DataMap[i]->GetName() == mapName) return m_DataMap[i];\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint FEMesh::DataMaps() const\n{\n\treturn (int)m_DataMap.size();\n}\n\n//-----------------------------------------------------------------------------\nFEDataMap* FEMesh::GetDataMap(int i)\n{\n\treturn m_DataMap[i];\n}\n\n//==============================================================================\nFEElementIterator::FEElementIterator(FEMesh* mesh, FEElementSet* elemSet) : m_mesh(mesh), m_eset(elemSet)\n{\n\treset();\n}\n\nvoid FEElementIterator::reset()\n{\n\tassert(m_mesh);\n\tm_el = nullptr;\n\tm_dom = -1;\n\tm_index = -1;\n\tif (m_eset)\n\t{\n\t\tif (m_eset->Elements())\n\t\t{\n\t\t\tm_index = 0;\n\t\t\tm_el = &m_eset->Element(0);\n\t\t}\n\t}\n\telse if (m_mesh && (m_mesh->Domains() > 0))\n\t{\n\t\tFEDomain& dom = m_mesh->Domain(0);\n\t\tif (dom.Elements())\n\t\t{\n\t\t\tm_dom = 0;\n\t\t\tm_index = 0;\n\t\t\tm_el = &dom.ElementRef(0);\n\t\t}\n\t}\n}\n\nvoid FEElementIterator::operator++()\n{\n\tif (m_el == nullptr)\n\t{\n\t\tassert(false);\n\t\treturn;\n\t}\n\n\tif (m_eset)\n\t{\n\t\tm_index++;\n\t\tif (m_index < m_eset->Elements())\n\t\t{\n\t\t\tm_el = &m_eset->Element(m_index);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_el = nullptr;\n\t\t}\n\t}\n\telse if (m_mesh)\n\t{\n\t\tm_index++;\n\t\tFEDomain& dom = m_mesh->Domain(m_dom);\n\t\tif (m_index >= dom.Elements())\n\t\t{\n\t\t\tm_dom++;\n\t\t\tif (m_dom < m_mesh->Domains())\n\t\t\t{\n\t\t\t\tFEDomain& dom2 = m_mesh->Domain(m_dom);\n\t\t\t\tif (dom2.Elements())\n\t\t\t\t{\n\t\t\t\t\tm_index = 0;\n\t\t\t\t\tm_el = &dom2.ElementRef(0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tm_el = nullptr;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_el = nullptr;\n\t\t\t}\n\t\t}\n\t\telse m_el = &dom.ElementRef(m_index);\n\t}\n\telse\n\t{\n\t\tassert(false);\n\t\tm_el = nullptr;\n\t}\n}\n\n// create a copy of this mesh\nvoid FEMesh::CopyFrom(FEMesh& mesh)\n{\n\tClear();\n\n\tint N0 = mesh.Nodes();\n\tCreateNodes(N0);\n\tfor (int i = 0; i < N0; ++i)\n\t{\n\t\tNode(i) = mesh.Node(i);\n\t}\n\n\t// now allocate domains\n\tClearDomains();\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tconst char* sz = dom.GetTypeStr();\n\n\t\t// create a new domain\n\t\t// create a new domain\n\t\tFEDomain* pd = nullptr;\n\t\tswitch (dom.Class())\n\t\t{\n\t\tcase FE_DOMAIN_SOLID   : pd = fecore_new<FESolidDomain   >(sz, nullptr); break;\n\t\tcase FE_DOMAIN_SHELL   : pd = fecore_new<FEShellDomain   >(sz, nullptr); break;\n\t\tcase FE_DOMAIN_BEAM    : pd = fecore_new<FEBeamDomain    >(sz, nullptr); break;\n\t\tcase FE_DOMAIN_2D      : pd = fecore_new<FEDomain2D      >(sz, nullptr); break;\n\t\tcase FE_DOMAIN_DISCRETE: pd = fecore_new<FEDiscreteDomain>(sz, nullptr); break;\n\t\t}\n\t\tassert(pd);\n\t\tpd->SetMesh(this);\n\n\t\t// copy domain data\n\t\tpd->CopyFrom(&dom);\n\n\t\t// add it to the mesh\n\t\tAddDomain(pd);\n\t}\n\tRebuildLUT();\n\n\t// copy element sets\n\tfor (int i = 0; i < mesh.ElementSets(); ++i)\n\t{\n\t\tFEElementSet& eset = mesh.ElementSet(i);\n\t\tFEElementSet* pset = new FEElementSet(GetFEModel());\n\t\tpset->SetMesh(this);\n\t\tpset->CopyFrom(eset);\n\t\tAddElementSet(pset);\n\t}\n}\n"
  },
  {
    "path": "FECore/FEMesh.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENode.h\"\n#include \"FENodeElemList.h\"\n#include \"FEElemElemList.h\"\n#include \"FENodeSet.h\"\n#include \"FEFacetSet.h\"\n#include \"FEDiscreteSet.h\"\n#include \"FESegmentSet.h\"\n#include \"FEElementSet.h\"\n#include \"FESurfacePair.h\"\n#include \"FEBoundingBox.h\"\n#include \"FESolidElement.h\"\n#include \"FEShellElement.h\"\n#include \"FEDomainList.h\"\n\n//-----------------------------------------------------------------------------\nclass FEEdge;\nclass FESurface;\nclass FEDomain;\nclass FEModel;\nclass FETimeInfo;\nclass FEDataMap;\nclass DumpStream;\n\n//---------------------------------------------------------------------------------------\n// Helper class for faster lookup of nodes based on their ID \nclass FECORE_API FENodeLUT\n{\npublic:\n\tFENodeLUT(FEMesh& mesh);\n\n\t// Find an element from its ID\n\tFENode* Find(int nodeID) const;\n\n\t// return an element's zero-based index\n\tint FindIndex(int nodeID) const;\n\nprivate:\n\tvector<int>\t\tm_node;\n\tint\t\t\t\tm_minID, m_maxID;\n\tFEMesh*\t\t\tm_mesh;\n};\n\n//---------------------------------------------------------------------------------------\n// Helper class for faster lookup of elements based on their ID \nclass FECORE_API FEElementLUT\n{\npublic:\n\tFEElementLUT(FEMesh& mesh);\n\n\t// Find an element from its ID\n\tFEElement* Find(int elemID) const;\n\n\t// return an element's zero-based index\n\tint FindIndex(int elemID) const;\n\nprivate:\n\tvector<FEElement*>\tm_elem;\n\tvector<int>\t\t\tm_elid;\n\tint\t\t\t\t\tm_minID, m_maxID;\n};\n\n//-----------------------------------------------------------------------------\n//! Defines a finite element mesh\n\n//! All the geometry data is stored in this class. \n\nclass FECORE_API FEMesh\n{\npublic:\n\t//! constructor\n\tFEMesh(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FEMesh();\n\n    //! initialize material points in mesh\n    void InitMaterialPoints();\n    \n\t//! clear the mesh\n\tvoid Clear();\n\n\t//! allocate storage for mesh data\n\tvoid CreateNodes(int nodes);\n\tvoid AddNodes(int nodes);\n\n\t//! return number of nodes\n\tint Nodes() const;\n\n\t//! return total nr of elements\n\tint Elements() const;\n\n\t//! return the nr of elements of a specific domain type\n\tint Elements(int ndom_type) const;\n\n\t//! return reference to a node\n\tFENode& Node(int i);\n\tconst FENode& Node(int i) const;\n\n\t//! Set the number of degrees of freedom on this mesh\n\tvoid SetDOFS(int n);\n\n\t//! update bounding box\n\tvoid UpdateBox();\n\n\t//! retrieve the bounding box\n\tFEBoundingBox& GetBoundingBox() { return m_box; }\n\n\t//! remove isolated vertices\n\tint RemoveIsolatedVertices();\n\n\t//! Reset the mesh data\n\tvoid Reset();\n\n\t//! Calculates an elements volume in reference configuration\n\tdouble ElementVolume(FEElement& el);\n\n\t//! calculates element volume in current configuration\n\tdouble CurrentElementVolume(FEElement& el);\n\n\t//! Finds a node from a given ID\n\tFENode* FindNodeFromID(int nid);\n\n\t//! Finds node index from a given ID\n\tint FindNodeIndexFromID(int nid);\n\n\t//! return an element (expensive way!)\n\tFEElement* Element(int i);\n\n\t//! Finds an element from a given ID\n\tFEElement* FindElementFromID(int elemID);\n\t\n\tint FindElementIndexFromID(int elemID);\n\n\t//! Finds the solid element in which y lies\n\tFESolidElement* FindSolidElement(vec3d y, double r[3]);\n\n\tFENodeElemList& NodeElementList();\n\n\tFEElemElemList& ElementElementList();\n\n\t//! See if all elements are of a particular shape\n\tbool IsType(FE_Element_Shape eshape);\n\n\t// --- NODESETS ---\n\t//! adds a node set to the mesh\n\tvoid AddNodeSet(FENodeSet* pns) { m_NodeSet.push_back(pns); }\n\n\t//! number of nodesets\n\tint NodeSets() { return (int) m_NodeSet.size(); }\n\n\t//! return a node set\n\tFENodeSet* NodeSet(int i) { return m_NodeSet[i]; }\n\n\t//! Find a nodeset by name\n\tFENodeSet* FindNodeSet(const std::string& name);\n\n\t// --- ELEMENT SETS ---\n\tint ElementSets() { return (int) m_ElemSet.size(); }\n\tFEElementSet& ElementSet(int n) { return *m_ElemSet[n]; }\n\tvoid AddElementSet(FEElementSet* pg) { m_ElemSet.push_back(pg); }\n\n\t//! Find a element set by name\n\tFEElementSet* FindElementSet(const std::string& name);\n\n\t// --- DOMAINS ---\n\tint Domains();\n\tFEDomain& Domain(int n);\n\n\tvoid AddDomain(FEDomain* pd);\n\n\tFEDomain* FindDomain(const std::string& name);\n\tint FindDomainIndex(const std::string& name);\n\tFEDomain* FindDomain(int domId);\n\n\t//! clear all domains\n\tvoid ClearDomains();\n\n\t//! Rebuild the LUT\n\tvoid RebuildLUT();\n\n\t// --- SURFACES ---\n\tint Surfaces() { return (int) m_Surf.size(); }\n\tFESurface& Surface(int n) { return *m_Surf[n]; }\n\tvoid AddSurface(FESurface* ps) { m_Surf.push_back(ps); }\n\tFESurface* FindSurface(const std::string& name);\n\tint FindSurfaceIndex(const std::string& name);\n\n\t// create a surface from a facet set\n\tFESurface* CreateSurface(FEFacetSet& facetSet);\n\n\t// --- EDGES ---\n\tint Edges() { return (int) m_Edge.size(); }\n\tFEEdge& Edge(int n) { return *m_Edge[n]; }\n\tvoid AddEdge(FEEdge* ps) { m_Edge.push_back(ps); }\n\n\t// --- Segment Sets ---\n\tint SegmentSets() { return (int) m_LineSet.size(); }\n\tFESegmentSet& SegmentSet(int n) { return *m_LineSet[n]; }\n\tvoid AddSegmentSet(FESegmentSet* ps) { m_LineSet.push_back(ps); }\n\tFESegmentSet* FindSegmentSet(const std::string& name);\n\n\t// --- Discrete Element Sets ---\n\tint DiscreteSets() { return (int) m_DiscSet.size(); }\n\tFEDiscreteSet& DiscreteSet(int n) { return *m_DiscSet[n]; }\n\tvoid AddDiscreteSet(FEDiscreteSet* ps) { m_DiscSet.push_back(ps); }\n\tFEDiscreteSet* FindDiscreteSet(const std::string& name);\n\n\t// --- FACETSETS ---\n\tint FacetSets() { return (int)m_FaceSet.size(); }\n\tFEFacetSet& FacetSet(int n) { return *m_FaceSet[n]; }\n\tvoid AddFacetSet(FEFacetSet* ps) { m_FaceSet.push_back(ps); }\n\tFEFacetSet* FindFacetSet(const std::string& name);\n\n\t// --- surface pairs ---\n\tint SurfacePairs() { return (int)m_SurfPair.size(); }\n\tFESurfacePair& SurfacePair(int n) { return *m_SurfPair[n]; }\n\tvoid AddSurfacePair(FESurfacePair* ps) { m_SurfPair.push_back(ps); }\n\tFESurfacePair* FindSurfacePair(const std::string& name);\n\npublic:\n\tsize_t DomainLists() const { return m_DomList.size(); }\n\tFEDomainList* DomainList(size_t n) { return m_DomList[n]; }\n\tvoid AddDomainList(FEDomainList* dl) { m_DomList.push_back(dl); }\n\tFEDomainList* FindDomainList(const std::string& name);\n\npublic:\n\t//! stream mesh data\n\tvoid Serialize(DumpStream& dmp);\n\n\tvoid SerializeDomains(DumpStream& ar);\n\n\tstatic void SaveClass(DumpStream& ar, FEMesh* p);\n\tstatic FEMesh* LoadClass(DumpStream& ar, FEMesh* p);\n\n\t// create a copy of this mesh\n\tvoid CopyFrom(FEMesh& mesh);\n\npublic:\n\t//! Calculate the surface representing the element boundaries\n\t//! boutside : include all exterior facets\n\t//! binside  : include all interior facets\n\tFESurface* ElementBoundarySurface(bool boutside = true, bool binside = false);\n\n\t//! Calculate the surface representing the element boundaries\n\t//! domains  : a list of which domains to create the surface from \n\t//! boutside : include all exterior facets\n\t//! binside  : include all interior facets\n\tFESurface* ElementBoundarySurface(std::vector<FEDomain*> domains, bool boutside = true, bool binside = false);\n\tFEFacetSet* DomainBoundary(std::vector<FEDomain*> domains, bool boutside = true, bool binside = false);\n\tFEFacetSet* DomainBoundary(FEDomainList& domainList, bool boutside = true, bool binside = false);\n\n\t//! get the nodal coordinates in reference configuration\n\tvoid GetInitialNodalCoordinates(const FEElement& el, vec3d* node);\n\n\t//! get the nodal coordinates in current configuration\n\tvoid GetNodalCoordinates(const FEElement& el, vec3d* node);\n\n\t// Get the FE model\n\tFEModel* GetFEModel() const { return m_fem; }\n\n\t// update the domains of the mesh\n\tvoid Update(const FETimeInfo& tp);\n\npublic: // data maps\n\tvoid ClearDataMaps();\n\tvoid AddDataMap(FEDataMap* map);\n\tFEDataMap* FindDataMap(const std::string& map);\n\n\tint DataMaps() const;\n\tFEDataMap* GetDataMap(int i);\n\nprivate:\n\tvector<FENode>\t\tm_Node;\t\t//!< nodes\n\tvector<FEDomain*>\tm_Domain;\t//!< list of domains\n\tvector<FESurface*>\tm_Surf;\t\t//!< surfaces\n\tvector<FEEdge*>\t\tm_Edge;\t\t//!< Edges\n\n\tvector<FENodeSet*>\t\tm_NodeSet;\t//!< node sets\n\tvector<FESegmentSet*>\tm_LineSet;\t//!< segment sets\n\tvector<FEElementSet*>\tm_ElemSet;\t//!< element sets\n\tvector<FEDiscreteSet*>\tm_DiscSet;\t//!< discrete element sets\n\tvector<FEFacetSet*>\t\tm_FaceSet;\t//!< facet sets\n\tvector<FESurfacePair*>\tm_SurfPair;\t//!< facet set pairs\n\tvector<FEDomainList*>\tm_DomList;\t//!< named domain lists\n\n\tvector<FEDataMap*>\t\tm_DataMap;\t//!< all data maps\n\n\tFEBoundingBox\t\tm_box;\t//!< bounding box\n\n\tFENodeElemList\tm_NEL;\n\tFEElementLUT*\tm_ELT;\n\tFENodeLUT*\t\tm_NLT;\n\n\tFEElemElemList\tm_EEL;\n\n\tFEModel*\tm_fem;\nprivate:\n\t//! hide the copy constructor\n\tFEMesh(FEMesh& m){}\n\n\t//! hide assignment operator\n\tvoid operator =(FEMesh& m) {}\n};\n\nclass FECORE_API FEElementIterator\n{\npublic:\n\tFEElementIterator(FEMesh* mesh, FEElementSet* elemSet = nullptr);\n\n\tFEElement& operator *() { return *m_el; }\n\n\tbool isValid() { return (m_el != nullptr); }\n\n\tvoid operator++();\n\n\tvoid reset();\n\nprivate:\n\tFEElement*\tm_el;\n\tFEMesh*\tm_mesh;\n\tFEElementSet*\tm_eset;\n\tint\tm_index;\n\tint\tm_dom;\n};\n"
  },
  {
    "path": "FECore/FEMeshAdaptor.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMeshAdaptor.h\"\n#include \"FESolidDomain.h\"\n#include \"FEElementList.h\"\n#include \"FEModel.h\"\n\nFEMeshAdaptor::FEMeshAdaptor(FEModel* fem) : FEStepComponent(fem)\n{\n\tm_elemSet = nullptr;\n}\n\nvoid FEMeshAdaptor::SetElementSet(FEElementSet* elemSet)\n{\n\tm_elemSet = elemSet;\n}\n\nFEElementSet* FEMeshAdaptor::GetElementSet()\n{\n\treturn m_elemSet;\n}\n\nvoid FEMeshAdaptor::UpdateModel()\n{\n\tFEModel& fem = *GetFEModel();\n\tfem.Reactivate();\n}\n\n// helper function for projecting integration point data to nodes\nvoid projectToNodes(FEMesh& mesh, std::vector<double>& nodeVals, std::function<double(FEMaterialPoint& mp)> f)\n{\n\t// temp storage \n\tdouble si[FEElement::MAX_INTPOINTS];\n\tdouble sn[FEElement::MAX_NODES];\n\n\t// allocate nodeVals and create valence array (tag)\n\tint NN = mesh.Nodes();\n\tvector<int> tag(NN, 0);\n\tnodeVals.assign(NN, 0.0);\n\n\t// loop over all elements\n\tint NE = mesh.Elements();\n\tFEElementList EL(mesh);\n\tFEElementList::iterator it = EL.begin();\n\tfor (int i = 0; i < NE; ++i, ++it)\n\t{\n\t\tFEElement& e = *it;\n\t\tint ne = e.Nodes();\n\t\tint ni = e.GaussPoints();\n\n\t\t// get the integration point values\n\t\tfor (int k = 0; k < ni; ++k)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(k);\n\t\t\tsi[k] = f(mp);\n\t\t}\n\n\t\t// project to nodes\n\t\te.project_to_nodes(si, sn);\n\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tnodeVals[e.m_node[j]] += sn[j];\n\t\t\ttag[e.m_node[j]]++;\n\t\t}\n\t}\n\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tif (tag[i] > 0) nodeVals[i] /= (double)tag[i];\n\t}\n}\n\n// helper function for projecting integration point data to nodes\nvoid projectToNodes(FEDomain& dom, std::vector<double>& nodeVals, std::function<double(FEMaterialPoint& mp)> f)\n{\n\t// temp storage \n\tdouble si[FEElement::MAX_INTPOINTS];\n\tdouble sn[FEElement::MAX_NODES];\n\n\t// allocate nodeVals and create valence array (tag)\n\tint NN = dom.Nodes();\n\tvector<int> tag(NN, 0);\n\tnodeVals.assign(NN, 0.0);\n\n\t// loop over all elements\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint ne = e.Nodes();\n\t\tint ni = e.GaussPoints();\n\n\t\t// get the integration point values\n\t\tfor (int k = 0; k < ni; ++k)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(k);\n\t\t\tsi[k] = f(mp);\n\t\t}\n\n\t\t// project to nodes\n\t\te.project_to_nodes(si, sn);\n\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tnodeVals[e.m_lnode[j]] += sn[j];\n\t\t\ttag[e.m_node[j]]++;\n\t\t}\n\t}\n\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tif (tag[i] > 0) nodeVals[i] /= (double)tag[i];\n\t}\n}\n\n"
  },
  {
    "path": "FECore/FEMeshAdaptor.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEStepComponent.h\"\n#include <functional>\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FEModel;\nclass FEElement;\nclass FEMaterialPoint;\nclass FEElementSet;\nclass FEMeshAdaptorCriterion;\n\n//-----------------------------------------------------------------------------\n// Base class for all mesh adaptors\nclass FECORE_API FEMeshAdaptor : public FEStepComponent\n{\n\tFECORE_SUPER_CLASS(FEMESHADAPTOR_ID)\n\tFECORE_BASE_CLASS(FEMeshAdaptor);\n\npublic:\n\tFEMeshAdaptor(FEModel* fem);\n\n\tvoid SetElementSet(FEElementSet* elemSet);\n\tFEElementSet* GetElementSet();\n\n\t// The mesh adaptor should return true if the mesh was modified. \n\t// otherwise, it should return false.\n\t// iteration is the iteration number of the mesh adaptation loop\n\tvirtual bool Apply(int iteration) = 0;\n\nprotected:\n\t// call this after the model was updated\n\tvoid UpdateModel();\n\nprivate:\n\tFEElementSet*\tm_elemSet;\n};\n\n// helper function for projecting integration point data to nodes\nvoid FECORE_API projectToNodes(FEMesh& mesh, std::vector<double>& nodeVals, std::function<double (FEMaterialPoint& mp)> f);\nvoid FECORE_API projectToNodes(FEDomain& dom, std::vector<double>& nodeVals, std::function<double(FEMaterialPoint& mp)> f);\n"
  },
  {
    "path": "FECore/FEMeshAdaptorCriterion.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMeshAdaptorCriterion.h\"\n#include \"FEMeshAdaptor.h\"\n#include \"FEModel.h\"\n#include \"FEMesh.h\"\n#include <algorithm>\n\n//=============================================================================\nvoid FEMeshAdaptorSelection::Sort(FEMeshAdaptorSelection::SortFlag sortFlag)\n{\n\tif (sortFlag == SORT_DECREASING)\n\t{\n\t\tstd::sort(m_itemList.begin(), m_itemList.end(), [](Item& e1, Item& e2) {\n\t\t\treturn (e1.m_elemValue > e2.m_elemValue);\n\t\t});\n\t}\n\telse\n\t{\n\t\tstd::sort(m_itemList.begin(), m_itemList.end(), [](Item& e1, Item& e2) {\n\t\t\treturn (e1.m_elemValue < e2.m_elemValue);\n\t\t});\n\t}\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEMeshAdaptorCriterion, FEModelComponent)\nEND_FECORE_CLASS();\n\nFEMeshAdaptorCriterion::FEMeshAdaptorCriterion(FEModel* fem) : FEModelComponent(fem)\n{\n}\n\n// return a list of elements that satisfy the criterion\nFEMeshAdaptorSelection FEMeshAdaptorCriterion::GetElementSelection(FEElementSet* elemSet)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// allocate iterator\n\tFEElementIterator it(&mesh, elemSet);\n\n\t// loop over elements\n\tFEMeshAdaptorSelection selectedElements;\n\tfor (;it.isValid(); ++it)\n\t{\n\t\tFEElement& el = *it;\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// evaluate element average\n\t\t\tdouble elemVal = 0;\n\t\t\tbool bvalid = GetElementValue(el, elemVal);\n\n\t\t\tif (bvalid)\n\t\t\t{\n\t\t\t\tint nid = el.GetID();\n\t\t\t\tselectedElements.push_back(nid, elemVal);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn selectedElements;\n}\n\nbool FEMeshAdaptorCriterion::GetElementValue(FEElement& el, double& value)\n{\n\tvalue = 0.0;\n\tint ni = el.GaussPoints(), nv = 0;\n\tfor (int i = 0; i < ni; ++i)\n\t{\n\t\tdouble vali = 0.0;\n\t\tbool b = GetMaterialPointValue(*el.GetMaterialPoint(i), vali);\n\t\tif (b) { value += vali; nv++; }\n\t}\n\tif (nv > 0)\n\t{\n\t\tvalue /= (double)nv;\n\t\treturn true;\n\t}\n\telse return false;\n}\n\nbool FEMeshAdaptorCriterion::GetMaterialPointValue(FEMaterialPoint& mp, double& elemVal)\n{\n\treturn false;\n}\n"
  },
  {
    "path": "FECore/FEMeshAdaptorCriterion.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n#include \"FEModelComponent.h\"\n#include <FECore/FEMaterialPoint.h>\n\nclass FEElementSet;\nclass FEElement;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEMeshAdaptorSelection\n{\npublic:\n\tenum SortFlag\n\t{\n\t\tSORT_INCREASING,\n\t\tSORT_DECREASING\n\t};\n\npublic:\n\tstruct Item {\n\t\tint\t\tm_elementId;\n\t\tdouble\tm_elemValue;\n\t};\n\npublic:\n\tFEMeshAdaptorSelection() {}\n\tFEMeshAdaptorSelection(size_t size) : m_itemList(size) {}\n\n\tvoid resize(size_t newSize) { m_itemList.resize(newSize); }\n\tItem& operator [] (size_t item) { return m_itemList[item]; }\n\tconst Item& operator [] (size_t item) const { return m_itemList[item]; }\n\tbool empty() const { return m_itemList.empty(); }\n\tsize_t size() const { return m_itemList.size(); }\n\tvoid push_back(int elemIndex, double scale) { m_itemList.push_back(Item{ elemIndex, scale }); }\n\tvoid push_back(const Item& item) { m_itemList.push_back(item); }\n\npublic:\n\tvoid Sort(SortFlag sortFlag);\n\nprivate:\n\tstd::vector<Item>\tm_itemList;\n};\n\n//-----------------------------------------------------------------------------\n// This class is a helper class for use in the mesh adaptors. Its purpose is to assign\n// values based on some criterion. This element list is then usually passed to the \n// mesh adaptor for further processing.\nclass FECORE_API FEMeshAdaptorCriterion : public FEModelComponent\n{\n\tFECORE_SUPER_CLASS(FEMESHADAPTORCRITERION_ID)\n\tFECORE_BASE_CLASS(FEMeshAdaptorCriterion)\n\npublic:\n\t// Constructor\n\tFEMeshAdaptorCriterion(FEModel* fem);\n\n\t// return a list of elements and associated values. \n\t// The elements will be taken from the element set. If nullptr is passed\n\t// for the element set, the entire mesh will be processed\n\tvirtual FEMeshAdaptorSelection GetElementSelection(FEElementSet* elset);\n\n\t// evaluate an element. This can be overriden by derived classes. By default,\n\t// it will evaluate the integration point average by calling GetMaterialPointValue\n\tvirtual bool GetElementValue(FEElement& el, double& value);\n\n\t// This function needs to be overridden in order to set the element's value.  \n\t// Return false if the element cannot be evaluated. Otherwise return true.\n\tvirtual bool GetMaterialPointValue(FEMaterialPoint& mp, double& value);\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEMeshPartition.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMeshPartition.h\"\n#include \"FEMaterial.h\"\n#include \"FEDataExport.h\"\n#include \"FEMesh.h\"\n#include \"DOFS.h\"\n#include <string.h>\n#include \"FEModel.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nFEMeshPartition::FEMeshPartition(int nclass, FEModel* fem) : FECoreBase(fem), m_nclass(nclass)\n{\n\tm_pMesh = nullptr;\n\tif (fem) m_pMesh = &fem->GetMesh();\n\tm_bactive = true;\n}\n\n//-----------------------------------------------------------------------------\nFEMeshPartition::~FEMeshPartition()\n{\n\t// delete all data export classes\n\tif (m_Data.empty() == false)\n\t{\n\t\tsize_t ND = m_Data.size();\n\t\tfor (size_t i = 0; i<ND; ++i) delete m_Data[i];\n\t\tm_Data.clear();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMeshPartition::AddDataExport(FEDataExport* pd)\n{\n\tif (pd) m_Data.push_back(pd);\n}\n\n//-----------------------------------------------------------------------------\nFEElement* FEMeshPartition::FindElementFromID(int nid)\n{\n\tfor (int i = 0; i<Elements(); ++i)\n\t{\n\t\tFEElement& el = ElementRef(i);\n\t\tif (el.GetID() == nid) return &el;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMeshPartition::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Node;\n\tar & m_nclass;\n\tar & m_bactive;\n//\tar & m_Data;\n}\n\n//-----------------------------------------------------------------------------\n//! return a specific node\nFENode& FEMeshPartition::Node(int i)\n{\n\treturn m_pMesh->Node(m_Node[i]);\n}\n\n//-----------------------------------------------------------------------------\n//! return a specific node\nconst FENode& FEMeshPartition::Node(int i) const\n{\n\treturn m_pMesh->Node(m_Node[i]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMeshPartition::CopyFrom(FEMeshPartition* pd)\n{\n\tm_Node = pd->m_Node;\n\tSetName(pd->GetName());\n}\n\n//-----------------------------------------------------------------------------\nbool FEMeshPartition::Init()\n{\n\t// base class first\n\tif (FECoreBase::Init() == false) return false;\n\n\t// make sure that there are elements in this domain\n\tif (Elements() == 0) return false;\n\n\t// get the mesh to which this domain belongs\n\tFEMesh& mesh = *GetMesh();\n\n\t// This array is used to keep tags on each node\n\tint NN = mesh.Nodes();\n\tvector<int> tag; tag.assign(NN, -1);\n\n\t// let's find all nodes the domain needs\n\tint nn = 0;\n\tint NE = Elements();\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFEElement& el = ElementRef(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j<ne; ++j)\n\t\t{\n\t\t\t// get the global node number\n\t\t\tint m = el.m_node[j];\n\n\t\t\t// create a local node number\n\t\t\tif (tag[m] == -1) tag[m] = nn++;\n\n\t\t\t// set the local node number\n\t\t\tel.m_lnode[j] = tag[m];\n\t\t}\n\t}\n\n\t// allocate node index table\n\tm_Node.assign(nn, -1);\n\n\t// fill the node index table\n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\tif (tag[i] >= 0)\n\t\t{\n\t\t\tm_Node[tag[i]] = i;\n\t\t}\n\t}\n\n#ifndef NDEBUG\n\t// make sure all nodes are assigned a local index\n\tfor (int i = 0; i<nn; ++i)\n\t{\n\t\tassert(m_Node[i] >= 0);\n\t}\n#endif\n\n\treturn true;\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FEMeshPartition::ForEachMaterialPoint(std::function<void(FEMaterialPoint& mp)> f)\n{\n\tint NE = Elements();\n#pragma omp parallel for shared(f)\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = ElementRef(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n) f(*el.GetMaterialPoint(n));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEMeshPartition::ForEachElement(std::function<void(FEElement& el)> f)\n{\n\tint NE = Elements();\n#pragma omp parallel for shared(f)\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tf(ElementRef(i));\n\t}\n}\n"
  },
  {
    "path": "FECore/FEMeshPartition.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEElement.h\"\n#include \"fecore_enum.h\"\n#include \"FESolver.h\"\n#include \"FEGlobalVector.h\"\n#include \"FETimeInfo.h\"\n#include <functional>\n\n//-----------------------------------------------------------------------------\n// forward declaration of classes\nclass FEModel;\nclass FENode;\nclass FEMesh;\nclass FEDataExport;\nclass FEGlobalMatrix;\nclass FEElementSet;\n\n//-----------------------------------------------------------------------------\n//! This class describes a mesh partition, that is, a group of elements that represent\n//! a part of the mesh.\nclass FECORE_API FEMeshPartition : public FECoreBase\n{\npublic:\n\t//! constructor\n\tFEMeshPartition(int nclass, FEModel* fem);\n\n\t//! virtual destructor\n\tvirtual ~FEMeshPartition();\n\n\t//! return domain class\n\tint Class() { return m_nclass; }\n\n\t//! set the mesh of this domain\n\tvoid SetMesh(FEMesh* pm) { m_pMesh = pm; }\n\n\t//! get the mesh of this domain\n\tFEMesh* GetMesh() { return m_pMesh; }\n\tconst FEMesh* GetMesh() const { return m_pMesh; }\n\n\t//! find the element with a specific ID\n\tFEElement* FindElementFromID(int nid);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t//! return number of nodes\n\tint Nodes() const { return (int)m_Node.size(); }\n\n\t//! return a specific node\n\tFENode& Node(int i);\n\tconst FENode& Node(int i) const;\n\n\t//! return the global node index from a local index\n\tint NodeIndex(int i) const { return m_Node[i]; }\n\npublic: // interface for derived classes\n\n\t//! return number of elements\n\tvirtual int Elements() const = 0;\n\n\t//! return a reference to an element \\todo this is not the preferred interface but I've added it for now\n\tvirtual FEElement& ElementRef(int i) = 0;\n\tvirtual const FEElement& ElementRef(int i) const = 0;\n\npublic: // optional functions to overload\n\n\t//! reset the domain\n\tvirtual void Reset() {}\n\n\t//! create a copy of this domain\n\tvirtual void CopyFrom(FEMeshPartition* pd);\n\n\t//! initialize domain\n\t//! one-time initialization, called during model initialization\n\tbool Init() override;\n\n\t//! This function is called at the start of a solution step.\n\t//! Domain classes can use this to update time dependant quantities\n\t//! \\todo replace this by a version of Update that takes a flag that indicates\n\t//! whether the update is final or not\n\tvirtual void PreSolveUpdate(const FETimeInfo& timeInfo) {}\n\n\t//! Update domain data.\n\t//! This is called when the model state needs to be updated (i.e. at the end of each Newton iteration)\n\tvirtual void Update(const FETimeInfo& tp) {}\n\npublic:\n\t//! Initialize material points in the domain (optional)\n\tvirtual void InitMaterialPoints() {}\n\n\t// Loop over all material points\n\tvoid ForEachMaterialPoint(std::function<void(FEMaterialPoint& mp)> f);\n\n\t// Loop over all elements\n\tvoid ForEachElement(std::function<void(FEElement& el)> f);\n\npublic:\n\t// This is an experimental feature.\n\t// The idea is to let the class define what data it wants to export\n\t// The hope is to eliminate the need for special plot and log classes\n\t// and to automate the I/O completely.\n\tvoid AddDataExport(FEDataExport* pd);\n\tint DataExports() const { return (int)m_Data.size(); }\n\tFEDataExport* GetDataExport(int i) { return m_Data[i]; }\n\npublic:\n\tbool IsActive() const { return m_bactive; }\n\tvoid SetActive(bool b) { m_bactive = b; }\n\nprotected:\n\tFEMesh*\t\tm_pMesh;\t//!< the mesh that this domain is a part of\n\tvector<int>\tm_Node;\t\t//!< list of nodes in this domain\n\nprotected:\n\tint\tm_nclass;\t\t\t//!< domain class\n\n\tbool\tm_bactive;\n\nprivate:\n\tvector<FEDataExport*>\tm_Data;\t//!< list of data export classes\n};\n"
  },
  {
    "path": "FECore/FEMeshTopo.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEMeshTopo.h\"\n#include \"FEElementList.h\"\n#include \"FEMesh.h\"\n#include \"FEDomain.h\"\n#include \"FEElemElemList.h\"\n#include \"FESurface.h\"\n#include <stack>\n\nclass FEMeshTopo::MeshTopoImp\n{\npublic:\n\tMeshTopoImp()\n\t{\n\t\tm_minId = -1;\n\t\tm_mesh = nullptr;\n\t}\n\npublic:\n\tFEMesh*\t\t\t\tm_mesh;\t\t\t// the mesh\n\tFEEdgeList\t\t\tm_edgeList;\t\t// the edge list\n\tFEElementEdgeList\tm_EEL;\t\t\t// the element-edge list\n\tFEFaceList\t\t\tm_faceList;\t\t// the face list (all faces)\n\tFEElementFaceList\tm_EFL;\t\t\t// the element-face list\n\tFEElemElemList\t\tm_ENL;\t\t\t// the element neighbor list\n\tFEFaceList\t\t\tm_surface;\t\t// only surface facets\n\tFEElementFaceList\tm_ESL;\t\t\t// element-surface facet list\n\tFEFaceEdgeList\t\tm_FEL;\t\t\t// face-edge list\n\n\tstd::vector<FEElement*>\tm_elem;\t\t// element list\n\n\tstd::vector<int>\tm_lut;\n\tint\t\t\t\t\tm_minId;\n};\n\nFEMeshTopo::FEMeshTopo() : imp(new FEMeshTopo::MeshTopoImp)\n{\n}\n\nFEMeshTopo::~FEMeshTopo()\n{\n\tdelete imp;\n}\n\n// get the mesh\nFEMesh* FEMeshTopo::GetMesh()\n{\n\treturn imp->m_mesh;\n}\n\nbool FEMeshTopo::Create(FEMesh* mesh)\n{\n\timp->m_mesh = mesh;\n\tFEElementList elemList(*mesh);\n\n\t// create a vector of all elements\n\tint NEL = mesh->Elements();\n\timp->m_elem.resize(NEL);\n\tNEL = 0;\n\tfor (int i = 0; i < mesh->Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh->Domain(i);\n\t\tint nel = dom.Elements();\n\t\tfor (int j = 0; j < nel; ++j)\n\t\t{\n\t\t\timp->m_elem[NEL++] = &dom.ElementRef(j);\n\t\t}\n\t}\n\n\t// build the index lookup table\n\tFEElementIterator it(mesh);\n\timp->m_minId = -1;\n\tint minId = -1, maxId = -1;\n\tfor (; it.isValid(); ++it)\n\t{\n\t\tint nid = (*it).GetID(); assert(nid != -1);\n\t\tif (minId == -1)\n\t\t{\n\t\t\tminId = nid;\n\t\t\tmaxId = nid;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (nid < minId) minId = nid;\n\t\t\tif (nid > maxId) maxId = nid;\n\t\t}\n\t}\n\tint lutSize = maxId - minId + 1;\n\timp->m_lut.assign(lutSize, -1);\n\timp->m_minId = minId;\n\tint n = 0;\n\tfor (it.reset(); it.isValid(); ++it, ++n)\n\t{\n\t\tint nid = (*it).GetID() - minId;\n\t\timp->m_lut[nid] = n;\n\t}\n\n\t// create the element neighbor list\n\tif (imp->m_ENL.Create(mesh) == false) return false;\n\n\t// create a face list\n\tif (imp->m_faceList.Create(*mesh, imp->m_ENL) == false) return false;\n\n\t// extract the surface facets\n\timp->m_surface = imp->m_faceList.GetSurface();\n\n\t// create the element-face list\n\tif (imp->m_EFL.Create(elemList, imp->m_faceList) == false) return false;\n\n\t// create the element-surface facet list\n\tif (imp->m_ESL.Create(elemList, imp->m_surface) == false) return false;\n\timp->m_surface.BuildNeighbors();\n\n\t// create the edge list (from the face list)\n\tif (imp->m_edgeList.Create(mesh) == false) return false;\n\n\t// create the element-edge list\n\tif (imp->m_EEL.Create(elemList, imp->m_edgeList) == false) return false;\n\n\t// create the face-edge list\n\tif (imp->m_FEL.Create(imp->m_faceList, imp->m_edgeList) == false) return false;\n\n\treturn true;\n}\n\n// return elements\nint FEMeshTopo::Elements()\n{\n\treturn (int)imp->m_elem.size();\n}\n\n// return an element\nFEElement* FEMeshTopo::Element(int i)\n{\n\tif (i < 0) return nullptr;\n\telse return imp->m_elem[i];\n}\n\nint FEMeshTopo::GetElementIndexFromID(int elemId)\n{\n\tint eid = elemId - imp->m_minId;\n\tif ((eid < 0) || (eid >= imp->m_lut.size())) { assert(false); return -1; }\n\tint lid = imp->m_lut[eid]; \n\tassert(lid != -1);\n\treturn lid;\n}\n\nint FEMeshTopo::Faces()\n{\n\treturn imp->m_faceList.Faces();\n}\n\n// return the number of surface faces\nint FEMeshTopo::SurfaceFaces() const\n{\n\treturn imp->m_surface.Faces();\n}\n\n// return a face\nconst FEFaceList::FACE& FEMeshTopo::Face(int i)\n{\n\treturn imp->m_faceList[i];\n}\n\n// return a surface facet\nconst FEFaceList::FACE& FEMeshTopo::SurfaceFace(int i) const\n{\n\treturn imp->m_surface[i];\n}\n\n// return the element-face list\nconst std::vector<int>& FEMeshTopo::ElementFaceList(int nelem)\n{\n\treturn imp->m_EFL.FaceList(nelem);\n}\n\n// return the number of edges in the mesh\nint FEMeshTopo::Edges()\n{\n\treturn imp->m_edgeList.Edges();\n}\n\n// return a edge\nconst FEEdgeList::EDGE& FEMeshTopo::Edge(int i)\n{\n\treturn imp->m_edgeList[i];\n}\n\n// return the face-edge list\nconst std::vector<int>& FEMeshTopo::FaceEdgeList(int nface)\n{\n\treturn imp->m_FEL.EdgeList(nface);\n}\n\n// return the element-edge list\nconst std::vector<int>& FEMeshTopo::ElementEdgeList(int nelem)\n{\n\treturn imp->m_EEL.EdgeList(nelem);\n}\n\n// return the list of face indices of a surface\nstd::vector<int> FEMeshTopo::FaceIndexList(FESurface& s)\n{\n\tFENodeFaceList NFL;\n\tNFL.Create(imp->m_faceList);\n\n\tint NF = s.Elements();\n\tstd::vector<int> fil(NF, -1);\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\tint nval = NFL.Faces(el.m_node[0]);\n\t\tconst std::vector<int>& nfl = NFL.FaceList(el.m_node[0]);\n\t\tfor (int j = 0; j < nval; ++j)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = imp->m_faceList[nfl[j]];\n\t\t\tif (face.IsEqual(&el.m_node[0]))\n\t\t\t{\n\t\t\t\tfil[i] = nfl[j];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tassert(fil[i] != -1);\n\t}\n\n\treturn fil;\n}\n\n// return the list of face indices of a surface\nstd::vector<int> FEMeshTopo::SurfaceFaceIndexList(FESurface& s)\n{\n\tFENodeFaceList NFL;\n\tNFL.Create(imp->m_surface);\n\n\tint NF = s.Elements();\n\tstd::vector<int> fil(NF, -1);\n\tfor (int i = 0; i < NF; ++i)\n\t{\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\tint nval = NFL.Faces(el.m_node[0]);\n\t\tconst std::vector<int>& nfl = NFL.FaceList(el.m_node[0]);\n\t\tfor (int j = 0; j < nval; ++j)\n\t\t{\n\t\t\tconst FEFaceList::FACE& face = imp->m_surface[nfl[j]];\n\t\t\tif (face.IsEqual(&el.m_node[0]))\n\t\t\t{\n\t\t\t\tfil[i] = nfl[j];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tassert(fil[i] != -1);\n\t}\n\n\treturn fil;\n}\n\n// return the element neighbor list\nstd::vector<FEElement*> FEMeshTopo::ElementNeighborList(int n)\n{\n\tFEElement* el = Element(n);\n\tint nbrs = 0;\n\tswitch (el->Shape())\n\t{\n        case ET_HEX8:\n        case ET_HEX20:\n        case ET_HEX27:\n            nbrs = 6; break;\n        case ET_TET4:\n        case ET_TET5:\n        case ET_TET10:\n        case ET_TET15:\n        case ET_TET20:\n            nbrs = 4; break;\n        case ET_PENTA6:\n        case ET_PENTA15:\n        case ET_PYRA5:\n        case ET_PYRA13:\n            nbrs = 5; break;\n        default:\n            assert(false);\n\t}\n\n\tvector<FEElement*> elemList;\n\tfor (int i = 0; i < nbrs; ++i)\n\t{\n\t\telemList.push_back(imp->m_ENL.Neighbor(n, i));\n\t}\n\n\treturn elemList;\n}\n\n// return the element neighbor list\nstd::vector<int> FEMeshTopo::ElementNeighborIndexList(int n)\n{\n\tFEElement* el = Element(n);\n\tint nbrs = 0;\n\tswitch (el->Shape())\n\t{\n        case ET_HEX8:\n        case ET_HEX20:\n        case ET_HEX27:\n            nbrs = 6; break;\n        case ET_TET4:\n        case ET_TET5:\n        case ET_TET10:\n        case ET_TET15:\n        case ET_TET20:\n            nbrs = 4; break;\n        case ET_PENTA6:\n        case ET_PENTA15:\n        case ET_PYRA5:\n        case ET_PYRA13:\n            nbrs = 5; break;\n        default:\n            assert(false);\n\t}\n\n\tvector<int> elemList;\n\tfor (int i = 0; i < nbrs; ++i)\n\t{\n\t\telemList.push_back(imp->m_ENL.NeighborIndex(n, i));\n\t}\n\n\treturn elemList;\n}\n\n// evaluate centroid of element as average position of integration points\nvec3d ElementCentroid(FEElement& el)\n{\n    int nint = el.GaussPoints();\n    vec3d c(0,0,0);\n    for (int n=0; n<nint; ++n) {\n        FEMaterialPoint* pt = el.GetMaterialPoint(n);\n        c += pt->m_rt;\n    }\n    c /= nint;\n    return c;\n}\n\n// find neighboring elements that fall within given proximity d\nstd::vector<int> FEMeshTopo::ElementProximityList(int i, double d, bool excludeSelf, bool matchMaterial)\n{\n\tstd::vector<int>    EPL; // element proximity list\n\tstd::vector<bool>   vst; // list of visited elements\n\n\tint NE = Elements();\n\tvst.assign(NE, false);\n\n\t// exclude element i itself from the list, if requested\n\tif (!excludeSelf)\n\t{\n\t\tEPL.push_back(i);\n\t}\n\n\tFEElement& el_i = *Element(i);\n\n\t// get centroid of element i\n\tvec3d x = ElementCentroid(el_i);\n\n\tstd::stack<int> stack;\n\tstack.push(i);\n\tvst[i] = true;\n\twhile (!stack.empty())\n\t{\n\t\tint n = stack.top(); stack.pop();\n\t\tstd::vector<int> ENIL = ElementNeighborIndexList(n);\n\n\t\t// search each neighbor to see if it falls within given proximity d\n\t\tfor (int j = 0; j < ENIL.size(); ++j) \n\t\t{\n\t\t\tint k = ENIL[j];\n\t\t\tif ((k != -1) && !vst[k])\n\t\t\t{\n\t\t\t\tFEElement& el_k = *Element(k);\n\t\t\t\tvec3d xk = ElementCentroid(el_k);\n\t\t\t\tdouble dk = (x - xk).Length();\n\t\t\t\tif (dk <= d)\n\t\t\t\t{\n\t\t\t\t\tif (!matchMaterial || (el_k.GetMatID() == el_i.GetMatID()))\n\t\t\t\t\t{\n\t\t\t\t\t\tEPL.push_back(k);\n\t\t\t\t\t\tstack.push(k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvst[k] = true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn EPL;\n}\n"
  },
  {
    "path": "FECore/FEMeshTopo.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEEdgeList.h\"\n#include \"FEFaceList.h\"\n#include \"vec3d.h\"\n\nclass FEMesh;\nclass FEElement;\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n//! This is a helper class for looping over elements, faces (internal and external), \n//! and edges of a FEMesh.\nclass FECORE_API FEMeshTopo\n{\n\tclass MeshTopoImp;\n\npublic:\n\tFEMeshTopo();\n\t~FEMeshTopo();\n\n\t// Create the FEMeshTopo from a mesh\n\tbool Create(FEMesh* mesh);\n\n\t// get the mesh\n\tFEMesh* GetMesh();\n\n\t// return number of elements\n\tint Elements();\n\n\t// return an element\n\tFEElement* Element(int i);\n\n\t// get the element index (into global element array) from an element ID\n\tint GetElementIndexFromID(int elemId);\n\n\t// return the number of faces in the mesh\n\tint Faces();\n\n\t// return a face\n\tconst FEFaceList::FACE& Face(int i);\n\n\t// return the number of surface faces\n\tint SurfaceFaces() const;\n\n\t// return the number of surface faces\n\tconst FEFaceList::FACE& SurfaceFace(int i) const;\n\n\t// return the element-face list\n\tconst std::vector<int>& ElementFaceList(int nelem);\n\n\t// return the number of edges in the mesh\n\tint Edges();\n\n\t// return an edge\n\tconst FEEdgeList::EDGE& Edge(int i);\n\n\t// return the face-edge list\n\tconst std::vector<int>& FaceEdgeList(int nface);\n\n\t// return the element-edge list\n\tconst std::vector<int>& ElementEdgeList(int nelem);\n\n\t// return the list of face indices of a surface\n\tstd::vector<int> FaceIndexList(FESurface& s);\n\n\t// return the list of face indices of a surface\n\tstd::vector<int> SurfaceFaceIndexList(FESurface& s);\n\n\t// return the element neighbor list\n\tstd::vector<FEElement*> ElementNeighborList(int i);\n\n\t// return the element neighbor index list\n\tstd::vector<int> ElementNeighborIndexList(int i);\n    \n    // find neighboring elements that fall within given proximity d\n    std::vector<int> ElementProximityList(int i, double d, bool excludeSelf = true, bool matchMaterial = true);\n\nprivate:\n\tMeshTopoImp*\timp;\n};\n"
  },
  {
    "path": "FECore/FEModel.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEModel.h\"\n#include \"FELoadController.h\"\n#include \"FEMaterial.h\"\n#include \"FEModelLoad.h\"\n#include \"FEBoundaryCondition.h\"\n#include \"FENodalLoad.h\"\n#include \"FESurfaceLoad.h\"\n#include \"FEEdgeLoad.h\"\n#include \"FEBodyLoad.h\"\n#include \"FEInitialCondition.h\"\n#include \"FESurfacePairConstraint.h\"\n#include \"FENLConstraint.h\"\n#include \"FEAnalysis.h\"\n#include \"FEGlobalData.h\"\n#include \"FECoreKernel.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"log.h\"\n#include \"FEDataArray.h\"\n#include \"FESurfaceConstraint.h\"\n#include \"FEModelParam.h\"\n#include \"FEEdge.h\"\n#include \"FEMeshAdaptor.h\"\n#include <string>\n#include <map>\n#include \"DumpStream.h\"\n#include \"LinearSolver.h\"\n#include \"FETimeStepController.h\"\n#include \"Timer.h\"\n#include \"DumpMemStream.h\"\n#include \"FEPlotDataStore.h\"\n#include \"FESolidDomain.h\"\n#include \"FEShellDomain.h\"\n#include \"FETrussDomain.h\"\n#include \"FEDomain2D.h\"\n#include \"FEDiscreteDomain.h\"\n#include \"FEDataGenerator.h\"\n#include \"FEModule.h\"\n#include \"FELogNodeData.h\"\n#include \"FELogElemData.h\"\n#include \"log.h\"\n#include <stdarg.h>\n#include <sstream>\nusing namespace std;\n\ntemplate <class T> int findComponentInVector(std::vector<T*>& v, FECoreBase* item)\n{\n\tfor (size_t i = 0; i < v.size(); ++i)\n\t{\n\t\tif (v[i] == item) return (int) i;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n// Implementation class for the FEModel class\nclass FEModel::Implementation\n{\npublic:\n\tstruct LoadParam\n\t{\n\t\tFEParam*\t\t\tparam;\n\t\tint\t\t\t\t\tlc;\n\n\t\tdouble\t\tm_scl;\n\t\tvec3d\t\tm_vscl;\n\n\t\tLoadParam()\n\t\t{\n\t\t\tm_scl = 1.0;\n\t\t\tm_vscl = vec3d(0, 0, 0);\n\t\t}\n\n\t\tvoid Serialize(DumpStream& ar)\n\t\t{\n\t\t\tar & lc;\n\t\t\tar & m_scl & m_vscl;\n\n\t\t\tif (ar.IsShallow() == false)\n\t\t\t{\n\t\t\t\t// we can't save the FEParam* directly, so we need to store meta data and try to find it on loading\n\t\t\t\tif (ar.IsSaving())\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pc = dynamic_cast<FECoreBase*>(param->parent()); assert(pc);\n\t\t\t\t\tar << pc;\n\t\t\t\t\tar << param->name();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pc = nullptr;\n\t\t\t\t\tar >> pc; assert(pc);\n\t\t\t\t\t\n\t\t\t\t\tchar name[256] = { 0 };\n\t\t\t\t\tar >> name;\n\n\t\t\t\t\tparam = pc->FindParameter(name); assert(param);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse param = nullptr;\n\t\t}\n\t};\n\npublic:\n\tImplementation(FEModel* fem) : m_fem(fem), m_mesh(fem), m_dmp(*fem)\n\t{\n\t\t// --- Analysis Data ---\n\t\tm_pStep = 0;\n\t\tm_nStep = -1;\n\t\tm_ftime0 = 0;\n\n\t\tm_nupdates = 0;\n\n\t\tm_bsolved = false;\n\n\t\tm_block_log = false;\n\n\t\tm_printParams = false;\n\n\t\tm_meshUpdate = false;\n\n\t\t// create the linear constraint manager\n\t\tm_LCM = new FELinearConstraintManager(fem);\n\n\t\t// allocate timers\n\t\t// Make sure enough timers are allocated for all the TimerIds!\n\t\tm_timers.resize(TIMER_COUNT);\n\t}\n\n\t~Implementation()\n\t{\n\t\tfor (auto p : m_logData) delete p;\n\t\tm_logData.clear();\n\t}\n\n\tvoid Serialize(DumpStream& ar);\n\n\tvoid PushState()\n\t{\n\t\tDumpMemStream& ar = m_dmp;\n\t\tar.clear(); // this also prepares the stream for writing\n\t\tm_fem->Serialize(ar);\n\t}\n\n\tbool PopState()\n\t{\n\t\t// get the dump stream\n\t\tDumpMemStream& ar = m_dmp;\n\n\t\t// make sure we have data to rewind\n\t\tif (ar.size() == 0) return false;\n\n\t\t// prepare the archive for reading\n\t\tar.Open(false, true);\n\n\t\t// restore the previous state\n\t\tm_fem->Serialize(m_dmp);\n\n\t\treturn true;\n\t}\n\n\tstd::pair<int, int> FindComponent(FECoreBase* pc)\n\t{\n\t\tint n = -1;\n\t\tn = findComponentInVector<FEMaterial             >(m_MAT , pc); if (n >= 0) return { FEMATERIAL_ID, n };\n\t\tn = findComponentInVector<FEBoundaryCondition    >(m_BC  , pc); if (n >= 0) return { FEBC_ID, n };\n\t\tn = findComponentInVector<FEModelLoad            >(m_ML  , pc); if (n >= 0) return { FELOAD_ID, n };\n\t\tn = findComponentInVector<FEInitialCondition     >(m_IC  , pc); if (n >= 0) return { FEIC_ID, n };\n\t\tn = findComponentInVector<FESurfacePairConstraint>(m_CI  , pc); if (n >= 0) return { FESURFACEINTERACTION_ID, n };\n\t\tn = findComponentInVector<FENLConstraint         >(m_NLC , pc); if (n >= 0) return { FENLCONSTRAINT_ID, n };\n\t\tn = findComponentInVector<FELoadController       >(m_LC  , pc); if (n >= 0) return { FELOADCONTROLLER_ID, n };\n\t\tn = findComponentInVector<FEAnalysis             >(m_Step, pc); if (n >= 0) return { FEANALYSIS_ID, n };\n\t\tn = findComponentInVector<FEMeshAdaptor          >(m_MA  , pc); if (n >= 0) return { FEMESHADAPTOR_ID, n };\n\t\tn = findComponentInVector<FEMeshDataGenerator    >(m_MD  , pc); if (n >= 0) return { FEMESHDATAGENERATOR_ID, n };\n\t\treturn { -1,-1 };\n\t}\n\n\npublic: // TODO: Find a better place for these parameters\n\tFETimeInfo\tm_timeInfo;\t\t\t//!< current time value\n\tdouble\t\tm_ftime0;\t\t\t//!< start time of current step\n\n\tbool\tm_block_log;\n\tbool\tm_log_verbose = false;\n\n\tint\t\tm_nupdates;\t//!< number of calls to FEModel::Update\n\npublic:\n\tstd::vector<FEMaterial*>\t\t\t\tm_MAT;\t\t//!< array of materials\n\tstd::vector<FEBoundaryCondition*>\t\tm_BC;\t\t//!< boundary conditions\n\tstd::vector<FEModelLoad*>\t\t\t\tm_ML;\t\t//!< model loads\n\tstd::vector<FEInitialCondition*>\t\tm_IC;\t\t//!< initial conditions\n    std::vector<FESurfacePairConstraint*>   m_CI;       //!< contact interface array\n\tstd::vector<FENLConstraint*>\t\t\tm_NLC;\t\t//!< nonlinear constraints\n\tstd::vector<FELoadController*>\t\t\tm_LC;\t\t//!< load controller data\n\tstd::vector<FEAnalysis*>\t\t\t\tm_Step;\t\t//!< array of analysis steps\n\tstd::vector<FEMeshAdaptor*>\t\t\t\tm_MA;\t\t//!< mesh adaptors\n\tstd::vector<FEMeshDataGenerator*>\t\tm_MD;\t\t//!< mesh data generators\n\n\tstd::vector<LoadParam>\t\tm_Param;\t//!< list of parameters controller by load controllers\n\t\n\tbool m_dotiming = true; // flag to enable/disable timings\n\tstd::vector<Timer>\t\t\tm_timers;\t// list of timers\n\n\tstd::unordered_map<std::string, FEScript> m_scripts;\n\npublic:\n\tFEAnalysis*\t\tm_pStep;\t//!< pointer to current analysis step\n\tint\t\t\t\tm_nStep;\t//!< current index of analysis step\n\tbool\t\t\tm_printParams;\t//!< print parameters\n\tbool\t\t\tm_meshUpdate;\t//!< mesh update flag\n\n\tstd::string\t\tm_units;\t// units string\n\npublic:\n\t// The model\n\tFEModel*\tm_fem;\n\n\t// module name\n\tstd::string\t\tm_moduleName;\n\n\tbool\tm_bsolved;\t// solved flag\n\n\t// DOFS data\n\tDOFS\tm_dofs;\t\t\t\t//!< list of degree of freedoms in this model\n\n\t// Geometry data\n\tFEMesh\t\tm_mesh;\t\t\t//!< the one and only FE mesh\n\n\t// linear constraint data\n\tFELinearConstraintManager*\tm_LCM;\n\n\tDataStore\tm_dataStore;\t\t\t//!< the data store used for data logging\n\n\tFEPlotDataStore\tm_plotData;\t\t//!< Output request for plot file\n\n\tDumpMemStream\tm_dmp;\t// only used by incremental solver\n\n\t// user-requested log data (via GetDataValue)\n\tvector<FELogData*> m_logData;\n\npublic: // Global Data\n\tstd::map<string, double> m_Const;\t//!< Global model constants\n\tvector<FEGlobalData*>\tm_GD;\t\t//!< global data structures\n\tstd::vector<FEGlobalVariable*>\tm_Var;\n\n\tFEMODEL_MEMORY_STATS\tm_memstats;\n};\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEModel, FECoreBase)\n\n\t// model parameters\n\tADD_PARAMETER(m_imp->m_timeInfo.currentTime, \"time\");\n\n\t// model properties\n\tADD_PROPERTY(m_imp->m_MAT , \"material\"       );\n\tADD_PROPERTY(m_imp->m_BC  , \"bc\"             );\n\tADD_PROPERTY(m_imp->m_ML  , \"load\"           );\n\tADD_PROPERTY(m_imp->m_IC  , \"initial\"        );\n\tADD_PROPERTY(m_imp->m_CI  , \"contact\"        );\n\tADD_PROPERTY(m_imp->m_NLC , \"constraint\"     );\n\tADD_PROPERTY(m_imp->m_MA  , \"mesh_adaptor\"   );\n\tADD_PROPERTY(m_imp->m_LC  , \"load_controller\");\n\tADD_PROPERTY(m_imp->m_MD  , \"mesh_data\"      );\n\tADD_PROPERTY(m_imp->m_Step, \"step\"           );\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEModel::FEModel(void) : FECoreBase(this), m_imp(new FEModel::Implementation(this))\n{\n\t// set the name\n\tSetName(\"fem\");\n\n\t// reset all timers\n\tResetAllTimers();\n}\n\n//-----------------------------------------------------------------------------\n//! Delete all dynamically allocated data\nFEModel::~FEModel(void)\n{\n\tClear();\n\tdelete m_imp;\n}\n\nvoid FEModel::SetVerboseMode(bool b)\n{\n\tm_imp->m_log_verbose = b;\n}\n\n//-----------------------------------------------------------------------------\n//! return the data store\nDataStore& FEModel::GetDataStore()\n{\n\treturn m_imp->m_dataStore;\n}\n\n//-----------------------------------------------------------------------------\nFEPlotDataStore& FEModel::GetPlotDataStore() { return m_imp->m_plotData; }\n\n//-----------------------------------------------------------------------------\nconst FEPlotDataStore& FEModel::GetPlotDataStore() const { return m_imp->m_plotData; }\n\n//-----------------------------------------------------------------------------\n//! will return true if the model solved succussfully\nbool FEModel::IsSolved() const\n{\n\treturn m_imp->m_bsolved;\n}\n\n//-----------------------------------------------------------------------------\n// call this function to set the mesh's update flag\nvoid FEModel::SetMeshUpdateFlag(bool b)\n{\n\tm_imp->m_meshUpdate = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::Clear()\n{\n\t// clear dofs\n\tm_imp->m_dofs.Reset();\n\n\t// clear all properties\n\tfor (FEMaterial* mat             : m_imp->m_MAT ) delete  mat; m_imp->m_MAT.clear();\n\tfor (FEBoundaryCondition* bc     : m_imp->m_BC  ) delete   bc; m_imp->m_BC.clear();\n\tfor (FEModelLoad* ml             : m_imp->m_ML  ) delete   ml; m_imp->m_ML.clear();\n\tfor (FEInitialCondition* ic      : m_imp->m_IC  ) delete   ic; m_imp->m_IC.clear();\n\tfor (FESurfacePairConstraint* ci : m_imp->m_CI  ) delete   ci; m_imp->m_CI.clear();\n\tfor (FENLConstraint* nlc         : m_imp->m_NLC ) delete   nlc; m_imp->m_NLC.clear();\n\tfor (FELoadController* lc        : m_imp->m_LC  ) delete   lc; m_imp->m_LC.clear();\n\tfor (FEMeshDataGenerator* md     : m_imp->m_MD  ) delete   md; m_imp->m_MD.clear();\n\tfor (FEAnalysis* step            : m_imp->m_Step) delete step; m_imp->m_Step.clear();\n\n\t// global data\n\tfor (size_t i = 0; i<m_imp->m_GD.size(); ++i) delete m_imp->m_GD[i]; m_imp->m_GD.clear();\n\tm_imp->m_Const.clear();\n\n\t// global variables (TODO: Should I delete the corresponding parameters?)\n\tfor (size_t i = 0; i < m_imp->m_Var.size(); ++i) delete m_imp->m_Var[i];\n\tm_imp->m_Var.clear();\n\n\t// clear the linear constraints\n\tif (m_imp->m_LCM) m_imp->m_LCM->Clear();\n\n\t// clear the mesh\n\tm_imp->m_mesh.Clear();\n\n\t// clear load parameters\n\tm_imp->m_Param.clear();\n}\n\n//-----------------------------------------------------------------------------\n//! set the module name\nvoid FEModel::SetActiveModule(const std::string& moduleName)\n{\n\tm_imp->m_moduleName = moduleName;\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tfecore.SetActiveModule(moduleName.c_str());\n\tFEModule* pmod = fecore.GetActiveModule();\n\tpmod->InitModel(this);\n}\n\n//-----------------------------------------------------------------------------\n//! get the module name\nstring FEModel::GetModuleName() const\n{\n\treturn m_imp->m_moduleName;\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::BoundaryConditions() const { return (int)m_imp->m_BC.size(); }\n\n//-----------------------------------------------------------------------------\nFEBoundaryCondition* FEModel::BoundaryCondition(int i) { return m_imp->m_BC[i]; }\n\n//-----------------------------------------------------------------------------\nvoid FEModel::AddBoundaryCondition(FEBoundaryCondition* pbc) { m_imp->m_BC.push_back(pbc); }\n\n//-----------------------------------------------------------------------------\nvoid FEModel::ClearBoundaryConditions() { m_imp->m_BC.clear(); }\n\n//-----------------------------------------------------------------------------\nint FEModel::InitialConditions() { return (int)m_imp->m_IC.size(); }\n\n//-----------------------------------------------------------------------------\nFEInitialCondition* FEModel::InitialCondition(int i) { return m_imp->m_IC[i]; }\n\n//-----------------------------------------------------------------------------\nvoid FEModel::AddInitialCondition(FEInitialCondition* pbc) { m_imp->m_IC.push_back(pbc); }\n\n//-----------------------------------------------------------------------------\n//! retrieve the number of steps\nint FEModel::Steps() const { return (int)m_imp->m_Step.size(); }\n\n//-----------------------------------------------------------------------------\n//! clear the steps\nvoid FEModel::ClearSteps() { m_imp->m_Step.clear(); }\n\n//-----------------------------------------------------------------------------\n//! Add an analysis step\nvoid FEModel::AddStep(FEAnalysis* pstep) { m_imp->m_Step.push_back(pstep); }\n\n//-----------------------------------------------------------------------------\n//! Get a particular step\nFEAnalysis* FEModel::GetStep(int i) { return m_imp->m_Step[i]; }\n\n//-----------------------------------------------------------------------------\n//! Get the current step\nFEAnalysis* FEModel::GetCurrentStep() { return m_imp->m_pStep; }\nconst FEAnalysis* FEModel::GetCurrentStep() const { return m_imp->m_pStep; }\n\n//-----------------------------------------------------------------------------\n//! Set the current step\nvoid FEModel::SetCurrentStep(FEAnalysis* pstep) { m_imp->m_pStep = pstep; }\n\n//-----------------------------------------------------------------------------\n//! Set the current step index\nint FEModel::GetCurrentStepIndex() const\n{\n\treturn m_imp->m_nStep;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the current step index\nvoid FEModel::SetCurrentStepIndex(int n)\n{\n\tm_imp->m_nStep = n;\n}\n\n//-----------------------------------------------------------------------------\n//! return number of surface pair interactions\nint FEModel::SurfacePairConstraints() { return (int)m_imp->m_CI.size(); }\n\n//-----------------------------------------------------------------------------\n//! retrive a surface pair interaction\nFESurfacePairConstraint* FEModel::SurfacePairConstraint(int i) { return m_imp->m_CI[i]; }\n\n//-----------------------------------------------------------------------------\n//! Add a surface pair interaction\nvoid FEModel::AddSurfacePairConstraint(FESurfacePairConstraint* pci) { m_imp->m_CI.push_back(pci); }\n\n//-----------------------------------------------------------------------------\n//! return number of nonlinear constraints\nint FEModel::NonlinearConstraints() { return (int)m_imp->m_NLC.size(); }\n\n//-----------------------------------------------------------------------------\n//! retrieve a nonlinear constraint\nFENLConstraint* FEModel::NonlinearConstraint(int i) { return m_imp->m_NLC[i]; }\n\n//-----------------------------------------------------------------------------\n//! add a nonlinear constraint\nvoid FEModel::AddNonlinearConstraint(FENLConstraint* pnlc) { m_imp->m_NLC.push_back(pnlc); }\n\n//-----------------------------------------------------------------------------\n//! return the number of model loads\nint FEModel::ModelLoads() { return (int)m_imp->m_ML.size(); }\n\n//-----------------------------------------------------------------------------\n//! retrieve a model load\nFEModelLoad* FEModel::ModelLoad(int i) { return m_imp->m_ML[i]; }\n\n//-----------------------------------------------------------------------------\n//! Add a model load\nvoid FEModel::AddModelLoad(FEModelLoad* pml) { m_imp->m_ML.push_back(pml); }\n\n//-----------------------------------------------------------------------------\n// get the FE mesh\nFEMesh& FEModel::GetMesh() { return m_imp->m_mesh; }\n\n//-----------------------------------------------------------------------------\nFELinearConstraintManager& FEModel::GetLinearConstraintManager() { return *m_imp->m_LCM; }\n\n//-----------------------------------------------------------------------------\nbool FEModel::Init()\n{\n\t// make sure there is something to do\n\tif (m_imp->m_Step.size() == 0) return false;\n\n\t// intitialize time\n\tFETimeInfo& tp = GetTime();\n\ttp.currentTime = 0;\n\ttp.timeIncrement = m_imp->m_Step[0]->m_dt0;\n\tm_imp->m_ftime0 = 0;\n\n\t// initialize global data\n\t// TODO: I'd like to do this here for consistency, but\n\t//       the problem is that solute dofs (i.e. concentration dofs) have\n\t//       to be allocated before the materials are read in.\n\t//       So right now the Init function is called when the solute data is created.\n/*\tfor (int i=0; i<(int) m_GD.size(); ++i)\n\t{\n\t\tFEGlobalData* pd = m_GD[i]; assert(pd);\n\t\tif (pd->Init() == false) return false;\n\t}\n*/\n\t// Initialize all load controllers and evaluate at the initial time\n\tif (InitLoadControllers() == false) return false;\n\n\t// Initialize and evaluate all mesh data generators\n\tif (InitMeshDataGenerators() == false) return false;\n\n\t// initialize step data\n\tif (InitSteps() == false) return false;\n\n\t// validate BC's\n\tif (InitBCs() == false) return false;\n\n\t// initialize material data\n\t// NOTE: This must be called after the rigid system is initialiazed since the rigid materials will\n\t//       reference the rigid bodies\n\tif (InitMaterials() == false) return false;\n\n\t// Validate the materials\n\tif (ValidateMaterials() == false) return false;\n\n\t// initialize mesh data\n\t// NOTE: this must be done AFTER the elements have been assigned material point data !\n\t// this is because the mesh data is reset\n\t// TODO: perhaps I should not reset the mesh data during the initialization\n\tif (InitMesh() == false) return false;\n\n\t// initialize model loads\n\t// NOTE: This must be called after the InitMaterials since the COM of the rigid bodies\n\t//       are set in that function. \n\tif (InitModelLoads() == false) return false;\n\n\t// initialize contact data\n\tif (InitContact() == false) return false;\n\n\t// initialize nonlinear constraints\n\tif (InitConstraints() == false) return false;\n\n\t// initialize mesh adaptors\n\tif (InitMeshAdaptors() == false) return false;\n\n\t// evaluate all load parameters\n\t// Do this last in case any model components redefined their load curves.\n\tif (EvaluateLoadParameters() == false) return false;\n\n\t// activate all permanent dofs\n\tActivate();\n\n\t// check if all load curves are being used\n\tint NLC = LoadControllers();\n\tvector<int> tag(NLC, 0);\n\tfor (int i = 0; i < m_imp->m_Param.size(); ++i)\n\t{\n\t\tint lc = m_imp->m_Param[i].lc;\n\t\ttag[lc]++;\n\t}\n\tfor (int i = 0; i < m_imp->m_Step.size(); ++i)\n\t{\n\t\tFEAnalysis* step = m_imp->m_Step[i];\n\t\tif (step->m_timeController)\n\t\t{\n\t\t\tint lc = step->m_timeController->m_nmplc;\n\t\t\tif (lc >= 0) tag[lc]++;\n\t\t}\n\t}\n\tint unused = 0;\n\tfor (int i = 0; i < NLC; ++i) if (tag[i] == 0) unused++;\n\tif (unused != 0)\n\t{\n\t\tfeLogWarning(\"Model has %d unreferenced load controllers.\", unused);\n\t}\n\n\tbool ret = false;\n\ttry\n\t{\n\t\tret = DoCallback(CB_INIT);\n\t}\n\tcatch (std::exception c)\n\t{\n\t\tret = false;\n\t\tfeLogError(c.what());\n\t}\n\n\t// do the callback\n\treturn ret;\n}\n\n//-----------------------------------------------------------------------------\n//! initialization of load controllers\nbool FEModel::InitLoadControllers()\n{\n\tfor (int i = 0; i < LoadControllers(); ++i)\n\t{\n\t\tFELoadController* plc = m_imp->m_LC[i];\n\t\tif (plc->Init() == false)\n\t\t{\n\t\t\tstd::string s = plc->GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Load controller %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t\tplc->Evaluate(0);\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize mesh data generators\nbool FEModel::InitMeshDataGenerators()\n{\n\tfor (int i = 0; i < MeshDataGenerators(); ++i)\n\t{\n\t\tFEMeshDataGenerator* pmd = m_imp->m_MD[i];\n\t\tif (pmd->Init() == false)\n\t\t{\n\t\t\tstd::string s = pmd->GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Node data generator %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t\tpmd->Evaluate(0);\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! initialize steps\nbool FEModel::InitSteps()\n{\n\tfor (int i = 0; i < (int)m_imp->m_Step.size(); ++i)\n\t{\n\t\tFEAnalysis& step = *m_imp->m_Step[i];\n\t\tif (step.Init() == false)\n\t\t{\n\t\t\tstd::string s = step.GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Step %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::InitMeshAdaptors()\n{\n\tfor (int i = 0; i < MeshAdaptors(); ++i)\n\t{\n\t\tFEMeshAdaptor* ma = MeshAdaptor(i);\n\t\tif (ma->Init() == false)\n\t\t{\n\t\t\tstd::string s = ma->GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Mesh adaptor %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// get the number of calls to Update()\nint FEModel::UpdateCounter() const\n{\n\treturn m_imp->m_nupdates;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::IncrementUpdateCounter()\n{\n\tm_imp->m_nupdates++;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::Update()\n{\n\t// update model counter\n\tm_imp->m_nupdates++;\n\t\n\t// update mesh\n\tFEMesh& mesh = GetMesh();\n\tconst FETimeInfo& tp = GetTime();\n\ttry {\n\t\tTRACK_TIME(TimerID::Timer_Update);\n\t\tmesh.Update(tp);\n\t}\n\tcatch (NegativeJacobianDetected e)\n\t{\n\t\t// for debug modes we do want to see inverted elements on the post side\n\t\tDoCallback(CB_MODEL_UPDATE);\n\t\tthrow;\n\t}\n\n\t// update model components\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Update);\n\n\t\t// set the mesh update flag to false\n\t\t// If any load sets this to true, the\n\t\t// mesh will also be update after the loads are updated\n\t\tm_imp->m_meshUpdate = false;\n\n\t\tint nvel = BoundaryConditions();\n\t\tfor (int i = 0; i < nvel; ++i)\n\t\t{\n\t\t\tFEBoundaryCondition& bc = *BoundaryCondition(i);\n\t\t\tif (bc.IsActive()) bc.UpdateModel();\n\t\t}\n\n\t\t// update all model loads\n\t\tfor (int i = 0; i < ModelLoads(); ++i)\n\t\t{\n\t\t\tFEModelLoad* pml = ModelLoad(i);\n\t\t\tif (pml && pml->IsActive()) pml->Update();\n\t\t}\n\n\t\t// update all paired-interfaces\n\t\tfor (int i = 0; i < SurfacePairConstraints(); ++i)\n\t\t{\n\t\t\tFESurfacePairConstraint* psc = SurfacePairConstraint(i);\n\t\t\tif (psc && psc->IsActive()) psc->Update();\n\t\t}\n\n\t\t// update all constraints\n\t\tfor (int i = 0; i < NonlinearConstraints(); ++i)\n\t\t{\n\t\t\tFENLConstraint* pc = NonlinearConstraint(i);\n\t\t\tif (pc && pc->IsActive()) pc->Update();\n\t\t}\n\n\t\t// some of the loads may alter the prescribed dofs, so we update the mesh again\n\t\tif (m_imp->m_meshUpdate)\n\t\t{\n\t\t\tmesh.Update(tp);\n\t\t\tm_imp->m_meshUpdate = false;\n\t\t}\n\t}\n    \n\t// do the callback\n\tDoCallback(CB_MODEL_UPDATE);\n}\n\n//-----------------------------------------------------------------------------\n//! See if the BC's are setup correctly.\nbool FEModel::InitBCs()\n{\n\t// check the IC's\n\tint NIC = InitialConditions();\n\tfor (int i = 0; i<NIC; ++i)\n\t{\n\t\tFEInitialCondition* pic = InitialCondition(i);\n\t\tif (pic->Init() == false)\n\t\t{\n\t\t\tstd::string s = pic->GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Initial condition %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// check the BC's\n\tint NBC = BoundaryConditions();\n\tfor (int i=0; i<NBC; ++i)\n\t{\n\t\tFEBoundaryCondition* pbc = BoundaryCondition(i);\n\t\tif (pbc->Init() == false)\n\t\t{\n\t\t\tstd::string s = pbc->GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Boundary condition %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// check the linear constraints\n\tif (GetLinearConstraintManager().Initialize() == false) return false;\n\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::AddMaterial(FEMaterial* pm) \n{ \n\tm_imp->m_MAT.push_back(pm);\n}\n\n//-----------------------------------------------------------------------------\n//! get the number of materials\nint FEModel::Materials() { return (int)m_imp->m_MAT.size(); }\n\n//-----------------------------------------------------------------------------\n//! return a pointer to a material\nFEMaterial* FEModel::GetMaterial(int i) { return m_imp->m_MAT[i]; }\n\n//-----------------------------------------------------------------------------\nFEMaterial* FEModel::FindMaterial(int nid)\n{\n\tfor (int i = 0; i<Materials(); ++i)\n\t{\n\t\tFEMaterial* pm = GetMaterial(i);\n\t\tif (pm->GetID() == nid) return pm;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nFEMaterial* FEModel::FindMaterial(const std::string& matName)\n{\n\tfor (int i = 0; i<Materials(); ++i)\n\t{\n\t\tFEMaterial* mat = GetMaterial(i);\n\t\tif (mat->GetName() == matName) return mat;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize material data (This also does an initial validation).\nbool FEModel::InitMaterials()\n{\n\t// initialize material data\n\tfor (int i=0; i<Materials(); ++i)\n\t{\n\t\t// get the material\n\t\tFEMaterial* pmat = GetMaterial(i);\n\n\t\t// initialize material data\n\t\tif (pmat->Init() == false)\n\t\t{\n\t\t\tfeLogError(\"Failed initializing material %d (name=\\\"%s\\\")\", i+1, pmat->GetName().c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! validate material data\nbool FEModel::ValidateMaterials()\n{\n\t// initialize material data\n\tfor (int i=0; i<Materials(); ++i)\n\t{\n\t\t// get the material\n\t\tFEMaterial* pmat = GetMaterial(i);\n\n\t\t// initialize material data\n\t\tif (pmat->Validate() == false)\n\t\t{\n\t\t\tfeLogError(\"Failed validating material %d (name=\\\"%s\\\")\", i+1, pmat->GetName().c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Add a loadcurve to the model\nvoid FEModel::AddLoadController(FELoadController* plc) \n{ \n\tm_imp->m_LC.push_back(plc) ;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::ReplaceLoadController(int n, FELoadController* plc)\n{\n\tassert((n >= 0) && (n < LoadControllers()));\n\tdelete m_imp->m_LC[n];\n\tm_imp->m_LC[n] = plc;\n}\n\n//-----------------------------------------------------------------------------\n//! get a loadcurve\nFELoadController* FEModel::GetLoadController(int i)\n{ \n\treturn m_imp->m_LC[i];\n}\n\n//-----------------------------------------------------------------------------\n//! get the number of loadcurves\nint FEModel::LoadControllers() const \n{ \n\treturn (int)m_imp->m_LC.size();\n}\n\n//-----------------------------------------------------------------------------\n//! Attach a load controller to a parameter\nvoid FEModel::AttachLoadController(FEParam* param, int lc)\n{\n\tImplementation::LoadParam lp;\n\tlp.param = param;\n\tlp.lc = lc;\n\n\tswitch (param->type())\n\t{\n\tcase FE_PARAM_DOUBLE: lp.m_scl  = param->value<double>(); break;\n\tcase FE_PARAM_VEC3D : lp.m_vscl = param->value<vec3d >(); break;\n\t}\n\n\tm_imp->m_Param.push_back(lp);\n}\n\n//! return the number of load-controlled parameters\nint FEModel::LoadParams() const\n{\n\treturn (int)m_imp->m_Param.size();\n}\n\n//! return a load-controlled parameter\nFEParam* FEModel::GetLoadParam(int n)\n{\n\tif ((n < 0) || (n >= LoadParams())) return nullptr;\n\treturn m_imp->m_Param[n].param;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::AttachLoadController(FEParam* p, FELoadController* plc)\n{\n\tAttachLoadController(p, plc->GetID());\n}\n\n//-----------------------------------------------------------------------------\n//! Detach a load controller from a parameter\nbool FEModel::DetachLoadController(FEParam* p)\n{\n\tfor (int i = 0; i < (int)m_imp->m_Param.size(); ++i)\n\t{\n\t\tImplementation::LoadParam& pi = m_imp->m_Param[i];\n\t\tif (pi.param == p)\n\t\t{\n\t\t\tm_imp->m_Param.erase(m_imp->m_Param.begin() + i);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! Get a load controller for a parameter (returns null if the param is not under load control)\nFELoadController* FEModel::GetLoadController(FEParam* p)\n{\n\tfor (int i = 0; i < (int)m_imp->m_Param.size(); ++i)\n\t{\n\t\tImplementation::LoadParam& pi = m_imp->m_Param[i];\n\t\tif (pi.param == p)\n\t\t{\n\t\t\treturn (pi.lc >= 0 ? GetLoadController(pi.lc) : nullptr);\n\t\t}\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Add a mesh data generator to the model\nvoid FEModel::AddMeshDataGenerator(FEMeshDataGenerator* pmd)\n{\n\tm_imp->m_MD.push_back(pmd);\n}\n\n//-----------------------------------------------------------------------------\nFEMeshDataGenerator* FEModel::GetMeshDataGenerator(int i)\n{\n\treturn m_imp->m_MD[i];\n}\n\n//-----------------------------------------------------------------------------\n//! get the number of mesh data generators\nint FEModel::MeshDataGenerators() const\n{\n\treturn (int)m_imp->m_MD.size();\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize rigid force data\nbool FEModel::InitModelLoads()\n{\n\t// call the Init() function of all rigid forces\n\tfor (int i=0; i<ModelLoads(); ++i)\n\t{\n\t\tFEModelLoad& FC = *ModelLoad(i);\n\t\tif (FC.Init() == false)\n\t\t{\n\t\t\tstd::string s = FC.GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Load %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Does one-time initialization of the Mesh data. Call FEMesh::Reset for resetting \n//! the mesh data.\nbool FEModel::InitMesh()\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// find and remove isolated vertices\n\tint ni = mesh.RemoveIsolatedVertices();\n\tif (ni != 0)\n\t{\n\t\tif (ni == 1)\n\t\t\tfeLogWarning(\"%d isolated vertex removed.\", ni);\n\t\telse\n\t\t\tfeLogWarning(\"%d isolated vertices removed.\", ni);\n\t}\n\n\t// Initialize shell data\n\t// This has to be done before the domains are initialized below\n\tif (!InitShells())\n\t{\n\t\tfeLogError(\"Errors found during initialization of shells.\");\n\t\treturn false;\n\t}\n\n\t// reset data\n\t// TODO: Not sure why this is here\n\ttry {\n\t\tmesh.Reset();\n\t}\n\tcatch (NegativeJacobian e)\n\t{\n\t\tfeLogError(\"Negative jacobian detected during mesh initialization.\");\n\t\treturn false;\n\t}\n\n\t// initialize all domains\n\t// Initialize shell domains first (in order to establish SSI)\n\t// TODO: I'd like to move the initialization of the SSI to InitShells, but I can't \n\t//       do that because FESSIShellDomain::FindSSI depends on the FEDomain::m_Node array which is\n\t//       initialized in FEDomain::Init.\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tif (dom.Class() == FE_DOMAIN_SHELL)\n\t\t\tif (dom.Init() == false) return false;\n\t}\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tif (dom.Class() != FE_DOMAIN_SHELL)\n\t\t\tif (dom.Init() == false) return false;\n\t}\n\n\t// initialize surfaces\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tif (mesh.Surface(i).Init() == false) return false;\n\t}\n\n\t// do some additional mesh validation\n\tValidateMesh();\n\n\t// All done\n\treturn true;\n}\n\nvoid FEModel::ValidateMesh()\n{\n\tFEMesh& mesh = GetMesh();\n\n\tfor (int i = 0; i < mesh.NodeSets(); ++i)\n\t{\n\t\tFENodeSet* ns = mesh.NodeSet(i);\n\t\tif (ns->Size() == 0)\n\t\t{\n\t\t\tstd::string name = ns->GetName();\n\t\t\tfeLogWarning(\"The nodeset \\\"%s\\\" is empty!\", name.c_str());\n\t\t}\n\t}\n\tfor (int i = 0; i < mesh.Surfaces(); ++i)\n\t{\n\t\tFESurface& surf = mesh.Surface(i);\n\t\tif (surf.Elements() == 0)\n\t\t{\n\t\t\tstd::string name = surf.GetName();\n\t\t\tfeLogWarning(\"The surface \\\"%s\\\" is empty!\", name.c_str());\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::InitShells()\n{\n\tFEMesh& mesh = GetMesh();\n\n\t// calculate initial directors for shell nodes\n\tint NN = mesh.Nodes();\n\tvector<vec3d> D(NN, vec3d(0, 0, 0));\n\tvector<int> ND(NN, 0);\n\n\t// loop over all domains\n\tfor (int nd = 0; nd < mesh.Domains(); ++nd)\n\t{\n\t\t// Calculate the shell directors as the local node normals\n\t\tif (mesh.Domain(nd).Class() == FE_DOMAIN_SHELL)\n\t\t{\n\t\t\tFEShellDomain& sd = static_cast<FEShellDomain&>(mesh.Domain(nd));\n\t\t\tvec3d r0[FEElement::MAX_NODES];\n\t\t\tfor (int i = 0; i<sd.Elements(); ++i)\n\t\t\t{\n\t\t\t\tFEShellElement& el = sd.Element(i);\n\n\t\t\t\tint n = el.Nodes();\n\t\t\t\tint* en = &el.m_node[0];\n\n\t\t\t\t// get the nodes\n\t\t\t\tfor (int j = 0; j<n; ++j) r0[j] = mesh.Node(en[j]).m_r0;\n\t\t\t\tfor (int j = 0; j<n; ++j)\n\t\t\t\t{\n\t\t\t\t\tint m0 = j;\n\t\t\t\t\tint m1 = (j + 1) % n;\n\t\t\t\t\tint m2 = (j == 0 ? n - 1 : j - 1);\n\n\t\t\t\t\tvec3d a = r0[m0];\n\t\t\t\t\tvec3d b = r0[m1];\n\t\t\t\t\tvec3d c = r0[m2];\n\t\t\t\t\tvec3d d = (b - a) ^ (c - a); d.unit();\n\n\t\t\t\t\tD[en[m0]] += d*el.m_h0[j];\n\t\t\t\t\t++ND[en[m0]];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// assign initial directors to shell nodes\n\t// make sure we average the directors\n\tfor (int i = 0; i<NN; ++i)\n\t\tif (ND[i] > 0) mesh.Node(i).m_d0 = D[i] / ND[i];\n\n\t// do any other shell initialization \n\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& dom = mesh.Domain(nd);\n\t\tif (dom.Class() == FE_DOMAIN_SHELL)\n\t\t{\n\t\t\tFEShellDomain& shellDom = static_cast<FEShellDomain&>(dom);\n\t\t\tif (!shellDom.InitShells()) return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initializes contact data\nbool FEModel::InitContact()\n{\n\t// loop over all contact interfaces\n    for (int i=0; i<SurfacePairConstraints(); ++i)\n\t{\n\t\t// get the contact interface\n        FESurfacePairConstraint& ci = *SurfacePairConstraint(i);\n\n\t\t// initializes contact interface data\n\t\tif (ci.Init() == false)\n\t\t{\n\t\t\tstd::string s = ci.GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Contact %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize the nonlinear constraints.\n//! This function is called during model initialization (\\sa FEModel::Init)\nbool FEModel::InitConstraints()\n{\n\tfor (int i=0; i<NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = NonlinearConstraint(i);\n\n\t\t// initialize\n\t\tif (plc->Init() == false)\n\t\t{\n\t\t\tstd::string s = plc->GetName();\n\t\t\tconst char* sz = (s.empty() ? \"<unnamed>\" : s.c_str());\n\t\t\tfeLogError(\"Nonlinear constraint %d (%s) failed to initialize\", i + 1, sz);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! This function solves the FE problem by calling the solve method for each step.\nbool FEModel::Solve()\n{\n\t// error flag\n\tbool bok = true;\n\n\t{\n\t\tTRACK_TIME(Timer_ModelSolve);\n\n\n\t\t// loop over all analysis steps\n\t\t// Note that we don't necessarily from step 0.\n\t\t// This is because the user could have restarted\n\t\t// the analysis. \n\t\tfor (size_t nstep = m_imp->m_nStep; nstep < Steps(); ++nstep)\n\t\t{\n\t\t\t// set the current analysis step\n\t\t\tm_imp->m_nStep = (int)nstep;\n\t\t\tm_imp->m_pStep = m_imp->m_Step[(int)nstep];\n\n\t\t\t// In the case we restarted, the current step can already be active\n\t\t\t// so don't activate it again. \n\t\t\tif (m_imp->m_pStep->IsActive() == false)\n\t\t\t{\n\t\t\t\tif (m_imp->m_pStep->Activate() == false)\n\t\t\t\t{\n\t\t\t\t\tbok = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// do callback\n\t\t\t\tDoCallback(CB_STEP_ACTIVE);\n\t\t\t}\n\n\t\t\t// solve the analaysis step\n\t\t\tbok = m_imp->m_pStep->Solve();\n\n\t\t\t// do callbacks\n\t\t\tDoCallback(CB_STEP_SOLVED);\n\n\t\t\tif (nstep + 1 == Steps())\n\t\t\t{\n\t\t\t\t// set the solved flag\n\t\t\t\tm_imp->m_bsolved = bok;\n\t\t\t}\n\n\t\t\t// wrap it up\n\t\t\tm_imp->m_pStep->Deactivate();\n\n\t\t\t// break if the step has failed\n\t\t\tif (bok == false) break;\n\t\t}\n\n\t\t// do the callbacks\n\t\tDoCallback(CB_SOLVED);\n\t}\n\n\treturn bok;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::RCI_Rewind()\n{\n\treturn m_imp->PopState();\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::RCI_ClearRewindStack()\n{\n\tif (m_imp->m_dmp.size() == 0) return false;\n\tm_imp->m_dmp.clear();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::RCI_Init()\n{\n\t// start the timer\n\tTRACK_TIME(Timer_ModelSolve);\n\n\t// reset solver status flag\n\tm_imp->m_bsolved = false;\n\n\t// loop over all analysis steps\n\tint nstep = m_imp->m_nStep;\n\tm_imp->m_pStep = m_imp->m_Step[(int)nstep];\n\n\tFEAnalysis* step = m_imp->m_pStep;\n\n\t// intitialize step data\n\tif (step->Activate() == false)\n\t{\n\t\treturn false;\n\t}\n\n\t// do callback\n\tDoCallback(CB_STEP_ACTIVE);\n\n\t// initialize the step's solver\n\tif (step->InitSolver() == false)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool FEModel::RCI_Restart()\n{\n\tFEAnalysis* step = GetCurrentStep();\n\tif (step == nullptr) return false;\n\n\treturn step->InitSolver();\n}\n\nbool FEModel::RCI_Advance()\n{\n\t// get the current step\n\tFEAnalysis* step = m_imp->m_pStep;\n\tif (step == nullptr) return false;\n\n\t// first see if the step has finished\n\tconst double eps = step->m_tend * 1e-7;\n\tdouble currentTime = GetCurrentTime();\n\tif (step->m_tend - currentTime <= eps)\n\t{\n\t\t// TODO: not sure why this is needed.\n\t\tSetStartTime(GetCurrentTime());\n\n\t\t// wrap it up\n\t\tDoCallback(CB_STEP_SOLVED);\n\t\tstep->Deactivate();\n\n\t\t// go to the next step\n\t\tint nstep = ++m_imp->m_nStep;\n\t\tif (nstep >= m_imp->m_Step.size())\n\t\t{\n\t\t\t// we're done\n\t\t\tm_imp->m_bsolved = true;\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// go to the next step\n\t\t\tstep = m_imp->m_pStep = m_imp->m_Step[nstep];\n\t\t\tif (step->Activate() == false) return false;\n\t\t\tDoCallback(CB_STEP_ACTIVE);\n\t\t\tif (step->InitSolver() == false) return false;\n\t\t}\n\t}\n\n\t// store current state in case we need to rewind\n\tm_imp->PushState();\n\n\t// Inform that the time is about to change. (Plugins can use \n\t// this callback to modify time step)\n\tDoCallback(CB_UPDATE_TIME);\n\n\t// update time\n\tFETimeInfo& tp = GetTime();\n\tdouble newTime = tp.currentTime + step->m_dt;\n\ttp.currentTime = newTime;\n\ttp.timeIncrement = step->m_dt;\n\tfeLog(\"\\n===== beginning time step %d : %lg =====\\n\", step->m_ntimesteps + 1, newTime);\n\n\t// initialize the solver step\n\t// (This basically evaluates all the parameter lists, but let's the solver\n\t//  customize this process to the specific needs of the solver)\n\tif (step->GetFESolver()->InitStep(newTime) == false) return false;\n\n\t// Solve the time step\n\tint ierr = step->SolveTimeStep();\n\tif (ierr != 0) return false;\n\n\t// update counters\n\tFESolver* psolver = step->GetFESolver();\n\tstep->m_ntotref += psolver->m_ntotref;\n\tstep->m_ntotiter += psolver->m_niter;\n\tstep->m_ntotrhs += psolver->m_nrhs;\n\n\t// Yes! We have converged!\n\tfeLog(\"\\n------- converged at time : %lg\\n\\n\", GetCurrentTime());\n\n\t// update nr of completed timesteps\n\tstep->m_ntimesteps++;\n\n\t// call callback function\n\tif (DoCallback(CB_MAJOR_ITERS) == false)\n\t{\n\t\tfeLogWarning(\"Early termination on user's request\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool FEModel::RCI_Finish()\n{\n\t// stop the timer\n\tGetTimer(Timer_ModelSolve)->stop();\n\n\t// do the callbacks\n\tDoCallback(CB_SOLVED);\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Model activation.\n// BC's that are not assigned to a step will not have their Activate() member called\n// so we do it here. This function is called in Init() and Reset()\nvoid FEModel::Activate()\n{\n\t// initial conditions\n\t// Must be activated before prescribed BC's\n\t// since relative prescribed BC's use the initial values\n\tfor (int i=0; i<InitialConditions(); ++i)\n\t{\n\t\tFEInitialCondition& ic = *InitialCondition(i);\n\t\tif (ic.IsActive()) ic.Activate();\n\t}\n\n\t// Boundary conditions\n\tfor (int i = 0; i<BoundaryConditions(); ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Activate();\n\t}\n\n\t// model loads\n\tfor (int i=0; i<ModelLoads(); ++i)\n\t{\n\t\tFEModelLoad& FC = *ModelLoad(i);\n\t\tif (FC.IsActive()) FC.Activate();\n\t}\n\n\t// nonlinear constraints\n\tfor (int i=0; i<NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = NonlinearConstraint(i);\n\t\tif (plc->IsActive()) plc->Activate();\n\t}\n\n\t// initialize material points before evaluating contact autopenalty\n    m_imp->m_mesh.InitMaterialPoints();\n    \n\t// contact interfaces\n    for (int i=0; i<SurfacePairConstraints(); ++i)\n\t{\n        FESurfacePairConstraint& ci = *SurfacePairConstraint(i);\n\t\tif (ci.IsActive()) ci.Activate();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// TODO: temporary construction. Need to see if I can just use Activate(). \n//       This is called after remeshed\nvoid FEModel::Reactivate()\n{\n\t// reactivate BCs\n\tfor (int i = 0; i < BoundaryConditions(); ++i)\n\t{\n\t\tFEBoundaryCondition& bc = *BoundaryCondition(i);\n\t\tif (bc.IsActive()) bc.Activate();\n\t}\n\n\t// reactivate model loads\n\tfor (int i = 0; i < ModelLoads(); ++i)\n\t{\n\t\tFEModelLoad& ml = *ModelLoad(i);\n\t\tif (ml.IsActive()) ml.Activate();\n\t}\n\n\t// update surface interactions\n\tfor (int i = 0; i < SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint& ci = *SurfacePairConstraint(i);\n\t\tif (ci.IsActive()) ci.Activate();\n\t}\n\n\t// reactivate the linear constraints\n\tGetLinearConstraintManager().Activate();\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Do I really need this function. I think calling FEModel::Init achieves the\n//! same effect.\nbool FEModel::Reset()\n{\n\t// reset all timers\n\tResetAllTimers();\n\n\t// reset solved flag\n\tm_imp->m_bsolved = false;\n\n\t// initialize materials\n\tfor (int i=0; i<Materials(); ++i)\n\t{\n\t\tFEMaterial* pmat = GetMaterial(i);\n\t\tpmat->Init();\n\t}\n\n\t// reset mesh data\n\tm_imp->m_mesh.Reset();\n\n\t// set up rigid joints\n\tif (m_imp->m_NLC.size() > 0)\n\t{\n\t\tint NC = (int)m_imp->m_NLC.size();\n\t\tfor (int i=0; i<NC; ++i)\n\t\t{\n\t\t\tFENLConstraint* plc = m_imp->m_NLC[i];\n\t\t\tplc->Reset();\n\t\t}\n\t}\n\n\t// set the start time\n\tm_imp->m_timeInfo.currentTime = 0;\n\tm_imp->m_timeInfo.timeIncrement = 0;\n\tm_imp->m_ftime0 = 0;\n\n\t// set first time step\n\tm_imp->m_pStep = m_imp->m_Step[0];\n\tm_imp->m_nStep = 0;\n\tfor (int i=0; i<Steps(); ++i) GetStep(i)->Reset();\n\n\t// reset contact data\n\t// TODO: I just call Init which I think is okay\n\tInitContact();\n\n\t// Call Activate() to activate all permanent BC's\n\tActivate();\n\n\t// Reevaluate load parameters\n\tfor (int i = 0; i < LoadControllers(); ++i)\n\t{\n\t\tFELoadController& lc = *GetLoadController(i);\n\t\tlc.Reset();\n\t}\n\tEvaluateLoadParameters();\n\n\tDoCallback(CB_RESET);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the current time information.\nFETimeInfo& FEModel::GetTime()\n{\n\treturn m_imp->m_timeInfo;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEModel::GetStartTime() const { return m_imp->m_ftime0; }\n\n//-----------------------------------------------------------------------------\nvoid FEModel::SetStartTime(double t) { m_imp->m_ftime0 = t; }\n\n//-----------------------------------------------------------------------------\ndouble FEModel::GetCurrentTime() const { return m_imp->m_timeInfo.currentTime; }\n\n//-----------------------------------------------------------------------------\nvoid FEModel::SetCurrentTime(double t) { m_imp->m_timeInfo.currentTime = t; }\n\n//-----------------------------------------------------------------------------\nvoid FEModel::SetCurrentTimeStep(double dt)\n{ \n\tFEAnalysis* step = GetCurrentStep(); assert(step);\n\tif (step) step->m_dt = dt;\n}\n\n//=============================================================================\n//    P A R A M E T E R   F U N C T I O N S\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nFEParam* FEModel::FindParameter(const ParamString& s)\n{\n\t// make sure it starts with the name of this model\n\tif (s != GetName()) return 0;\n\treturn FECoreBase::FindParameter(s.next());\n}\n\n//-----------------------------------------------------------------------------\nFEParamValue GetComponent(FEParamValue& p, const ParamString& c)\n{\n\tswitch (p.type())\n\t{\n\tcase FE_PARAM_MAT3DS:\n\t\tif (c == \"xx\") return p.component(0);\n\t\tif (c == \"xy\") return p.component(1);\n\t\tif (c == \"yy\") return p.component(2);\n\t\tif (c == \"xz\") return p.component(3);\n\t\tif (c == \"yz\") return p.component(4);\n\t\tif (c == \"zz\") return p.component(5);\n\t\tbreak;\n\t}\n\treturn FEParamValue();\n}\n\n//-----------------------------------------------------------------------------\nFEParamValue GetComponent(vec3d& r, const ParamString& c)\n{\n\tif (c == \"x\") return FEParamValue(0, &r.x, FE_PARAM_DOUBLE);\n\tif (c == \"y\") return FEParamValue(0, &r.y, FE_PARAM_DOUBLE);\n\tif (c == \"z\") return FEParamValue(0, &r.z, FE_PARAM_DOUBLE);\n\treturn FEParamValue();\n}\n\n//! return the parameter string for a parameter\nstd::string FEModel::GetParamString(FEParam* p)\n{\n\tif (p == nullptr) return string();\n\n\tFECoreBase* pc = dynamic_cast<FECoreBase*>(p->parent());\n\tif (pc == nullptr) return string();\n\n\tstring paramName = p->name();\n\twhile (pc->GetParent())\n\t{\n\t\tFECoreBase* parent = pc->GetParent();\n\t\tFEProperty* prop = parent->FindProperty(pc);\n\t\tif (prop == nullptr) return string();\n\n\t\tif (prop->IsArray())\n\t\t{\n\t\t\tint n = -1;\n\t\t\tfor (int i = 0; i < prop->size(); ++i)\n\t\t\t\tif (prop->get(i) == pc)\n\t\t\t\t{\n\t\t\t\t\tn = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\tif (n == -1) return string();\n\t\t\tstringstream ss;\n\t\t\tss << prop->GetName() << \"[\" << n << \"].\";\n\t\t\tparamName = ss.str() + paramName;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tparamName = string(prop->GetName()) + \".\" + paramName;\n\t\t}\n\t\tpc = parent;\n\t}\n\tif (paramName.empty()) return string();\n\n\tstd::pair<int, int> item = m_imp->FindComponent(pc);\n\tint typeId = item.first;\n\tint index = item.second;\n\tif ((typeId == -1) || (index == -1)) return string();\n\n\tstring typeStr;\n\tswitch (typeId)\n\t{\n\tcase FEMATERIAL_ID          : typeStr = \"material\"; break;\n\tcase FEBC_ID                : typeStr = \"bc\"; break;\n\tcase FELOAD_ID              : typeStr = \"load\"; break;\n\tcase FEIC_ID                : typeStr = \"initial\"; break;\n\tcase FESURFACEINTERACTION_ID: typeStr = \"contact\"; break;\n\tcase FENLCONSTRAINT_ID      : typeStr = \"constraint\"; break;\n\tcase FEMESHADAPTOR_ID       : typeStr = \"mesh_adaptor\"; break;\n\tcase FELOADCONTROLLER_ID    : typeStr = \"load_controller\"; break;\n\tcase FEMESHDATAGENERATOR_ID : typeStr = \"mesh_data\"; break;\n\tcase FEANALYSIS_ID          : typeStr = \"step\"; break;\n\tdefault:\n\t\treturn string();\n\t}\n\n\tstringstream ss;\n\tss << \"fem.\" << typeStr << \"[\" << index << \"].\" << paramName;\n\tstring s = ss.str();\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n// helper function for evaluating mesh data\nFEParamValue FEModel::GetMeshParameter(const ParamString& paramString)\n{\n\tFEMesh& mesh = GetMesh();\n\n\tParamString next = paramString.next();\n\tif (next == \"node\")\n\t{\n\t\tFENode* node = 0;\n\t\tint nid = next.Index();\n\t\tif (nid < 0)\n\t\t{\n\t\t\tParamString fnc = next.next();\n\t\t\tif (fnc == \"fromId\")\n\t\t\t{\n\t\t\t\tnid = fnc.ID();\n\t\t\t\tnode = mesh.FindNodeFromID(nid);\n\t\t\t\tnext = next.next();\n\t\t\t}\n\t\t}\n\t\telse if ((nid >= 0) && (nid < mesh.Nodes()))\n\t\t{\n\t\t\tnode = &mesh.Node(nid);\n\t\t}\n\n\t\tif (node)\n\t\t{\n\t\t\tParamString paramString = next.next();\n\t\t\tif      (paramString == \"position\"      ) return GetComponent(node->m_rt, paramString.next());\n\t\t\t// TODO: the m_Fr is not a vec3d any more, so not sure what to do here.\n\t\t\t//       In any case, this should probably be handled by the FEMechModel\n//\t\t\telse if (paramString == \"reaction_force\") return GetComponent(node->m_Fr, paramString.next());\n\t\t\telse\n\t\t\t{\n\t\t\t\t// see if it corresponds to a solution variable\n\t\t\t\tint n = GetDOFIndex(paramString.c_str());\n\t\t\t\tif ((n >= 0) && (n < node->dofs()))\n\t\t\t\t{\n\t\t\t\t\treturn FEParamValue(0, &node->get(n), FE_PARAM_DOUBLE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if (next == \"elem\")\n\t{\n\t\tFEElement* elem = 0;\n\t\tint nid = next.Index();\n\t\tif (nid < 0)\n\t\t{\n\t\t\tParamString fnc = next.next();\n\t\t\tif (fnc == \"fromId\")\n\t\t\t{\n\t\t\t\tnid = fnc.Index();\n\t\t\t\telem = mesh.FindElementFromID(nid);\n\t\t\t\tnext = next.next();\n\t\t\t}\n\t\t}\n\t\telse if ((nid >= 0) && (nid < mesh.Elements()))\n\t\t{\n\t\t\telem = mesh.Element(nid);\n\t\t}\n\n\t\tif (elem)\n\t\t{\n\t\t\tParamString paramString = next.next();\n\t\t\tFEDomain* dom = dynamic_cast<FEDomain*>(elem->GetMeshPartition());\n\t\t\tif (dom == nullptr) return FEParamValue();\n\n\t\t\tif (paramString == \"var\")\n\t\t\t{\n\t\t\t\tstd::string varName = paramString.IDString();\n\t\t\t\tFEMaterial* mat = dom->GetMaterial();\n\t\t\t\tif (mat)\n\t\t\t\t{\n\t\t\t\t\tFEDomainParameter* var = mat->FindDomainParameter(varName);\n\t\t\t\t\tif (var)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamValue v = var->value(*elem->GetMaterialPoint(0));\n\t\t\t\t\t\tif (v.isValid())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tParamString comp = paramString.next();\n\t\t\t\t\t\t\treturn GetComponent(v, comp);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if (next == \"domain\")\n\t{\n\t\tint nid = next.Index();\n\t\tif ((nid >= 0) && (nid < mesh.Domains()))\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(nid);\n\t\t\tParamString paramName = next.next();\n\t\t\tFEParam* param = dom.FindParameter(paramName);\n\t\t\tif (param)\n\t\t\t{\n\t\t\t\tif (param->type() == FE_PARAM_DOUBLE_MAPPED)\n\t\t\t\t{\n\t\t\t\t\tFEParamDouble& v = param->value<FEParamDouble>();\n\t\t\t\t\tif (v.isConst()) return FEParamValue(param, &v.constValue(), FE_PARAM_DOUBLE);\n\t\t\t\t}\n\t\t\t\treturn FEParamValue(param, param->data_ptr(), param->type());\n\t\t\t}\n\t\t}\n\t}\n\n\t// if we get here, we did not find it\n\treturn FEParamValue();\n}\n\n//-----------------------------------------------------------------------------\n//! Return a pointer to the named variable\n//! This function returns a pointer to a named variable.\n\nFEParamValue FEModel::GetParameterValue(const ParamString& paramString)\n{\n\t// make sure it starts with the name of this model\n\tif (paramString != GetName()) return FEParamValue();\n\tParamString next = paramString.next();\n\tFEParamValue val = FECoreBase::GetParameterValue(next);\n\tif (!val.isValid())\n\t{\n\t\tif (next == \"mesh\") return GetMeshParameter(next);\n\t}\n\treturn val;\n}\n\nFEDataValue FEModel::GetDataValue(const ParamString& s)\n{\n\tFEDataValue val;\n\tif (s != GetName()) return val;\n\tParamString data = s.next();\n\n\tif (data == \"node_data\")\n\t{\n\t\tstring params = data.IDString();\n\n\t\tFELogNodeData* pd = fecore_new<FELogNodeData>(params.c_str(), this);\n\t\tif (pd == nullptr) { feLogError(\"Invalid data variable %s\", params.c_str()); return val; }\n\n\t\tm_imp->m_logData.push_back(pd);\n\n\t\tval.SetLogData(pd);\n\t}\n\telse if (data == \"elem_data\")\n\t{\n\t\tstring params = data.IDString();\n\n\t\tFELogElemData* pd = fecore_new<FELogElemData>(params.c_str(), this);\n\t\tif (pd == nullptr) { feLogError(\"Invalid data variable %s\", params.c_str()); return val; }\n\n\t\tm_imp->m_logData.push_back(pd);\n\n\t\tval.SetLogData(pd);\n\t}\n\n\treturn val;\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FEModel::FindComponent(const ParamString& prop)\n{\n\t// make sure it starts with the name of this model\n\tif (prop != GetName()) return 0;\n\n\t// see what the next reference is\n\tParamString next = prop.next();\n\n\t// next, find the property\n\tFECoreBase* pc = GetProperty(next);\n\n\treturn pc;\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluates all load curves at the specified time\nvoid FEModel::EvaluateLoadControllers(double time)\n{\n\tconst int NLC = LoadControllers();\n\tfor (int i=0; i<NLC; ++i) GetLoadController(i)->Evaluate(time);\n}\n\n//-----------------------------------------------------------------------------\n//! Evaluates all load curves at the specified time\nvoid FEModel::EvaluateDataGenerators(double time)\n{\n\tfor (int i = 0; i < MeshDataGenerators(); ++i) GetMeshDataGenerator(i)->Evaluate(time);\n}\n\n//-----------------------------------------------------------------------------\n//! Set the print parameters flag\nvoid FEModel::SetPrintParametersFlag(bool b)\n{\n\tm_imp->m_printParams = b;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the print parameter flag\nbool FEModel::GetPrintParametersFlag() const\n{\n\treturn m_imp->m_printParams;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::EvaluateLoadParameters()\n{\n\tfeLog(\"\\n\");\n\tint NLC = LoadControllers();\n\tfor (int i = 0; i<(int)m_imp->m_Param.size(); ++i)\n\t{\n\t\tImplementation::LoadParam& pi = m_imp->m_Param[i];\n\t\tint nlc = pi.lc;\n\t\tif ((nlc >= 0) && (nlc < NLC))\n\t\t{\n\t\t\tdouble s = GetLoadController(nlc)->Value();\n\t\t\tFEParam* p = pi.param;\n\t\t\tFECoreBase* parent = dynamic_cast<FECoreBase*>(p->parent());\n\t\t\tif (m_imp->m_printParams)\n\t\t\t{\n\t\t\t\tif (parent && (parent->GetName().empty() == false))\n\t\t\t\t{\n\t\t\t\t\tconst char* pname = parent->GetName().c_str();\n\t\t\t\t\tfeLog(\"Setting parameter \\\"%s.%s\\\" to : \", pname, p->name());\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tfeLog(\"Setting parameter \\\"%s\\\" to : \", p->name());\n\t\t\t};\n\t\t\tassert(p->IsVolatile());\n\t\t\tswitch (p->type())\n\t\t\t{\n\t\t\tcase FE_PARAM_INT: {\n\t\t\t\tp->value<int>() = (int)s;\n\t\t\t\tif (m_imp->m_printParams) feLog(\"%d\\n\", p->value<int>());\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE: {\n\t\t\t\tp->value<double>() = pi.m_scl*s;\n\t\t\t\tif (m_imp->m_printParams) feLog(\"%lg\\n\", p->value<double>());\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_BOOL: {\n\t\t\t\tp->value<bool>() = (s > 0 ? true : false); \n\t\t\t\tif (m_imp->m_printParams) feLog(\"%s\\n\", (p->value<bool>() ? \"true\" : \"false\"));\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_VEC3D: {\n\t\t\t\tvec3d& v = p->value<vec3d>();\n\t\t\t\tp->value<vec3d>() = pi.m_vscl*s;\n\t\t\t\tif (m_imp->m_printParams) feLog(\"%lg, %lg, %lg\\n\", v.x, v.y, v.z);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED: \n\t\t\t{\n\t\t\t\tFEParamDouble& v = p->value<FEParamDouble>();\n\t\t\t\tdouble c = 1.0;\n\t\t\t\tif (v.isConst()) c = v.constValue();\n\t\t\t\tv.SetScaleFactor(s * pi.m_scl);\n\t\t\t\tif (m_imp->m_printParams) feLog(\"%lg\\n\", c*p->value<FEParamDouble>().GetScaleFactor());\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_VEC3D_MAPPED :\n\t\t\t{\n\t\t\t\tFEParamVec3& v = p->value<FEParamVec3>();\n\t\t\t\tv.SetScaleFactor(s * pi.m_scl);\n\t\t\t\tif (m_imp->m_printParams) feLog(\"%lg\\n\", v.GetScaleFactor());\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tfeLog(\"\\n\");\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfeLogError(\"Invalid load curve ID\");\n\t\t\treturn false;\n\t\t}\n\t}\n\tfeLog(\"\\n\");\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nDOFS& FEModel::GetDOFS()\n{\n\treturn m_imp->m_dofs;\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::GetDOFIndex(const char* sz) const\n{\n\tint n = m_imp->m_dofs.GetDOF(sz);\n\tif (n < 0)\n\t{\n\t\t// NOTE: This is a hack so that concentration variables can\n\t\t// also be referenced by the solute name\n\t\tn = FindGlobalDataIndex(sz);\n\t}\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::GetDOFIndex(const char* szvar, int n) const\n{\n\treturn m_imp->m_dofs.GetDOF(szvar, n);\n}\n\nstd::string CallbackId2String(unsigned int nevent)\n{\n\tswitch (nevent)\n\t{\n\tcase CB_INIT            : return \"CB_INIT\"            ; break;\n\tcase CB_STEP_ACTIVE     : return \"CB_STEP_ACTIVE\"     ; break;\n\tcase CB_MAJOR_ITERS     : return \"CB_MAJOR_ITERS\"     ; break;\n\tcase CB_MINOR_ITERS     : return \"CB_MINOR_ITERS\"     ; break;\n\tcase CB_SOLVED          : return \"CB_SOLVED\"          ; break;\n\tcase CB_UPDATE_TIME     : return \"CB_UPDATE_TIME\"     ; break;\n\tcase CB_AUGMENT         : return \"CB_AUGMENT\"         ; break;\n\tcase CB_STEP_SOLVED     : return \"CB_STEP_SOLVED\"     ; break;\n\tcase CB_MATRIX_REFORM   : return \"CB_MATRIX_REFORM\"   ; break;\n\tcase CB_REMESH          : return \"CB_REMESH\"          ; break;\n\tcase CB_PRE_MATRIX_SOLVE: return \"CB_PRE_MATRIX_SOLVE\"; break;\n\tcase CB_RESET           : return \"CB_RESET\"           ; break;\n\tcase CB_MODEL_UPDATE    : return \"CB_MODEL_UPDATE\"    ; break;\n\tcase CB_TIMESTEP_SOLVED : return \"CB_TIMESTEP_SOLVED\" ; break;\n\tcase CB_SERIALIZE_SAVE  : return \"CB_SERIALIZE_SAVE\"  ; break;\n\tcase CB_SERIALIZE_LOAD  : return \"CB_SERIALIZE_LOAD\"  ; break;\n\tcase CB_TIMESTEP_FAILED : return \"CB_TIMESTEP_FAILED\" ; break;\n\tcase CB_QUASIN_CONVERGED: return \"CB_QUASIN_CONVERGED\"; break;\n\tcase CB_USER1           : return \"CB_USER1\"           ; break;\n\tdefault:\n\t\treturn \"<unknown>\";\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Call the callback function if there is one defined\n//\nbool FEModel::DoCallback(unsigned int nevent)\n{\n\tTRACK_TIME(TimerID::Timer_Callback);\n\n\tif (m_imp->m_log_verbose)\n\t{\n\t\tstring name = GetName();\n\t\tstring cbname = CallbackId2String(nevent);\n\t\tfeLog(\"[%s.%s]===>\\n\", name.c_str(), cbname.c_str());\n\t}\n\n\ttry\n\t{\n\t\t// do the callbacks\n\t\tbool bret = CallbackHandler::DoCallback(this, nevent);\n\n\t\tif (m_imp->m_log_verbose)\n\t\t{\n\t\t\tstring name = GetName();\n\t\t\tstring cbname = CallbackId2String(nevent);\n\t\t\tfeLog(\"<===[%s.%s]\\n\", name.c_str(), cbname.c_str());\n\t\t}\n\n\t\treturn bret;\n\t}\n\tcatch (ForceConversion)\n\t{\n\t\tthrow;\n\t}\n\tcatch (IterationFailure)\n\t{\n\t\tthrow;\n\t}\n\tcatch (DoRunningRestart)\n\t{\n\t\tthrow;\n\t}\n\tcatch (std::exception e)\n\t{\n\t\tthrow;\n\t}\n\tcatch (...)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::Log(int ntag, const char* msg)\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::Logf(int ntag, const char* msg, ...)\n{\n\tif (m_imp->m_block_log) return;\n\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar sztxt[2048] = { 0 };\n\tva_start(args, msg);\n\tvsnprintf(sztxt, sizeof(sztxt), msg, args);\n\tva_end(args);\n\n\tLog(ntag, sztxt);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::BlockLog()\n{\n\tm_imp->m_block_log = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::UnBlockLog()\n{\n\tm_imp->m_block_log = false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::LogBlocked() const\n{\n\treturn m_imp->m_block_log;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::SetGlobalConstant(const string& s, double v)\n{\n\tm_imp->m_Const[s] = v;\n\treturn;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEModel::GetGlobalConstant(const string& s)\n{\n\treturn (m_imp->m_Const.count(s) ? m_imp->m_Const.find(s)->second : 0);\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::GlobalVariables() const\n{\n\treturn (int)m_imp->m_Var.size();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::AddGlobalVariable(const string& s, double v)\n{\n\tFEGlobalVariable* var = new FEGlobalVariable;\n\tvar->v = v;\n\tvar->name = s;\n\tAddParameter(var->v, var->name.c_str());\n\tm_imp->m_Var.push_back(var);\n}\n\nconst FEGlobalVariable& FEModel::GetGlobalVariable(int n)\n{\n\treturn *m_imp->m_Var[n];\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::AddGlobalData(FEGlobalData* psd)\n{\n\tm_imp->m_GD.push_back(psd);\n}\n\n//-----------------------------------------------------------------------------\nFEGlobalData* FEModel::GetGlobalData(int i)\n{\n\treturn m_imp->m_GD[i];\n}\n\n//-----------------------------------------------------------------------------\nFEGlobalData* FEModel::FindGlobalData(const char* szname)\n{\n\tfor (int i = 0; i < m_imp->m_GD.size(); ++i)\n\t{\n\t\tif (m_imp->m_GD[i]->GetName() == szname) return m_imp->m_GD[i];\n\t}\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::FindGlobalDataIndex(const char* szname) const\n{\n\tfor (int i = 0; i < m_imp->m_GD.size(); ++i)\n\t{\n\t\tif (m_imp->m_GD[i]->GetName() == szname) return i;\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::GlobalDataItems()\n{\n\treturn (int)m_imp->m_GD.size();\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* CopyFEBioClass(FECoreBase* pc, FEModel* fem)\n{\n\tif ((pc == nullptr) || (fem == nullptr))\n\t{\n\t\tassert(false);\n\t\treturn nullptr;\n\t}\n\n\tconst char* sztype = pc->GetTypeStr();\n\n\t// create a new material\n\tFECoreBase* pcnew = fecore_new<FECoreBase>(pc->GetSuperClassID(), sztype, fem);\n\tassert(pcnew);\n\n\tpcnew->SetID(pc->GetID());\n\n\t// copy parameters\n\tpcnew->GetParameterList() = pc->GetParameterList();\n\n\t// copy properties\n\tfor (int i = 0; i < pc->PropertyClasses(); ++i)\n\t{\n\t\tFEProperty* prop = pc->PropertyClass(i);\n\t\tif (prop->size() > 0)\n\t\t{\n\t\t\tfor (int j = 0; j < prop->size(); ++j)\n\t\t\t{\n\t\t\t\tFECoreBase* pci = prop->get(j);\n\t\t\t\tif (pc)\n\t\t\t\t{\n\t\t\t\t\tFECoreBase* pci_new = CopyFEBioClass(pci, fem); assert(pci_new);\n\t\t\t\t\tbool b = pcnew->SetProperty(i, pci_new); assert(b);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pcnew;\n}\n\n//-----------------------------------------------------------------------------\n//! This function copies the model data from the fem object. Note that it only copies\n//! the model definition, i.e. mesh, bc's, contact interfaces, etc..\nvoid FEModel::CopyFrom(FEModel& fem)\n{\n\t// clear the current model data\n\tClear();\n\n\t// copy the active module\n\tSetActiveModule(fem.GetModuleName());\n\n\t// --- Parameters ---\n\n\t// copy parameters (not sure if I need/want to copy all of these)\n\tm_imp->m_nStep = fem.m_imp->m_nStep;\n\tm_imp->m_timeInfo = fem.m_imp->m_timeInfo;\n\tm_imp->m_ftime0 = fem.m_imp->m_ftime0;\n\tm_imp->m_pStep = 0;\n\n\t// copy model variables\n\t// we only copy the user created parameters, which presumably don't exist yet\n\t// in this model.\n\tint NS = fem.GlobalVariables();\n\tif (NS > 0)\n\t{\n\t\tassert(GlobalVariables() == 0);\n\t\tFEParameterList& PL = GetParameterList();\n\t\tfor (int i = 0; i < NS; ++i)\n\t\t{\n\t\t\tconst FEGlobalVariable& var = fem.GetGlobalVariable(i);\n\t\t\tAddGlobalVariable(var.name, var.v);\n\t\t}\n\t}\n\n\t// --- Steps ---\n\n\t// copy the steps\n\t// NOTE: This does not copy the boundary conditions of the steps\n\tint NSTEP = fem.Steps();\n\tfor (int i=0; i<NSTEP; ++i)\n\t{\n\t\t// get the type string\n\t\tFEAnalysis* ps = fem.GetStep(i);\n\n\t\t// create a new step\n\t\tFEAnalysis* pnew = new FEAnalysis(this);\n\n\t\t// copy additional info\n\t\tpnew->CopyFrom(ps);\n\n\t\t// copy the solver\n\t\tFESolver* psolver = ps->GetFESolver();\n\t\tconst char* sztype = psolver->GetTypeStr();\n\n\t\t// create a new solver\n\t\tFESolver* pnew_solver = fecore_new<FESolver>(sztype, this);\n\t\tassert(pnew_solver);\n\t\tpnew->SetFESolver(pnew_solver);\n\n\t\t// copy parameters\n\t\tpnew_solver->GetParameterList() = psolver->GetParameterList();\n\n\t\t// add the step\n\t\tAddStep(pnew);\n\t}\n\n\t// --- Materials ---\n\n\t// copy material data\n\tint NMAT = fem.Materials();\n\tfor (int i=0; i<NMAT; ++i)\n\t{\n\t\t// get the type info from the old material\n\t\tFEMaterial* pmat = fem.GetMaterial(i);\n\n\t\t// copy the material\n\t\tFEMaterial* pnew = dynamic_cast<FEMaterial*>(CopyFEBioClass(pmat, this));\n\t\tassert(pnew);\n\n\t\t// copy the name\n\t\tpnew->SetName(pmat->GetName());\n\n\t\t// add the material\n\t\tAddMaterial(pnew);\n\t}\n\tassert(m_imp->m_MAT.size() == fem.m_imp->m_MAT.size());\n\n\t// --- Mesh ---\n\n\t// copy the mesh data\n\t// NOTE: This will not assign materials to the new domains\n\t// A. copy nodes\n\tFEMesh& sourceMesh = fem.GetMesh();\n\tFEMesh& mesh = GetMesh();\n\tint N = sourceMesh.Nodes();\n\tmesh.CreateNodes(N);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tmesh.Node(i) = sourceMesh.Node(i);\n\t}\n\n\t// B. domains\n\t// let's first create a table of material indices for the old domains\n\tint NDOM = sourceMesh.Domains();\n\tvector<int> LUT(NDOM);\n\tfor (int i=0; i<NDOM; ++i)\n\t{\n\t\tFEMaterial* pm = sourceMesh.Domain(i).GetMaterial();\n\t\tfor (int j=0; j<NMAT; ++j)\n\t\t{\n\t\t\tif (pm == fem.GetMaterial(j))\n\t\t\t{\n\t\t\t\tLUT[i] = j;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// now allocate domains\n\tfor (int i=0; i<NDOM; ++i)\n\t{\n\t\tFEDomain& dom = sourceMesh.Domain(i);\n\t\tconst char* sz = dom.GetTypeStr();\n\n\t\t// create a new domain\n\t\tFEDomain* pd = nullptr;\n\t\tswitch (dom.Class())\n\t\t{\n\t\tcase FE_DOMAIN_SOLID   : pd = fecore_new<FESolidDomain   >(sz, this); break;\n\t\tcase FE_DOMAIN_SHELL   : pd = fecore_new<FEShellDomain   >(sz, this); break;\n\t\tcase FE_DOMAIN_BEAM    : pd = fecore_new<FEBeamDomain    >(sz, this); break;\n\t\tcase FE_DOMAIN_2D      : pd = fecore_new<FEDomain2D      >(sz, this); break;\n\t\tcase FE_DOMAIN_DISCRETE: pd = fecore_new<FEDiscreteDomain>(sz, this); break;\n\t\t}\n\t\tassert(pd);\n\t\tpd->SetMaterial(GetMaterial(LUT[i]));\n\n\t\t// copy domain data\n\t\tpd->CopyFrom(&dom);\n\n\t\t// add it to the mesh\n\t\tmesh.AddDomain(pd);\n\t}\n\n\t// --- boundary conditions ---\n\n\tint NDC = fem.BoundaryConditions();\n\tfor (int i=0; i<NDC; ++i)\n\t{\n\t\tFEBoundaryCondition* pbc = fem.BoundaryCondition(i);\n\t\tconst char* sz = pbc->GetTypeStr();\n\n\t\tFEBoundaryCondition* pnew = fecore_new<FEBoundaryCondition>(sz, this);\n\t\tassert(pnew);\n\n\t\tpnew->CopyFrom(pbc);\n\n\t\t// add to model\n\t\tAddBoundaryCondition(pnew);\n\t}\n\n\t// --- contact interfaces ---\n    int NCI = fem.SurfacePairConstraints();\n\tfor (int i=0; i<NCI; ++i)\n\t{\n\t\t// get the next interaction\n        FESurfacePairConstraint* pci = fem.SurfacePairConstraint(i);\n\t\tconst char* sztype = pci->GetTypeStr();\n\n\t\t// create a new contact interface\n        FESurfacePairConstraint* pnew = fecore_new<FESurfacePairConstraint>(sztype, this);\n\t\tassert(pnew);\n\n\t\t// create a copy\n\t\tpnew->CopyFrom(pci);\n\n\t\t// add the new interface\n        AddSurfacePairConstraint(pnew);\n\n\t\t// add the surfaces to the surface list\n\t\tmesh.AddSurface(pnew->GetSecondarySurface());\n\t\tmesh.AddSurface(pnew->GetPrimarySurface ());\n\t}\n\n\t// --- nonlinear constraints ---\n\tint NLC = fem.NonlinearConstraints();\n\tfor (int i=0; i<NLC; ++i)\n\t{\n\t\t// get the next constraint\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tconst char* sztype = plc->GetTypeStr();\n\n\t\t// create a new nonlinear constraint\n\t\tFENLConstraint* plc_new = fecore_new<FENLConstraint>(sztype, this);\n\t\tassert(plc_new);\n\n\t\t// create a copy\n\t\tplc_new->CopyFrom(plc);\n\n\t\t// add the nonlinear constraint\n\t\tAddNonlinearConstraint(plc_new);\n\n\t\t// add the surface to the mesh (if any)\n        FESurfaceConstraint* psc = dynamic_cast<FESurfaceConstraint*>(plc_new);\n        if (psc)\n        {\n            FESurface* ps = psc->GetSurface();\n            if (ps) mesh.AddSurface(ps);\n        }\n\t}\n\n\t// --- Load curves ---\n\t// copy load curves\n\tint NLD = fem.LoadControllers();\n\tfor (int i = 0; i<NLD; ++i)\n\t{\n\t\tFELoadController* lc = fem.GetLoadController(i);\n\t\tFELoadController* newlc = fecore_new<FELoadController>(lc->GetTypeStr(), this); assert(newlc);\n\t\tAddLoadController(newlc);\n\t}\n\n\t// copy linear constraints\n\tif (fem.m_imp->m_LCM)\n\t{\n\t\tif (m_imp->m_LCM) delete m_imp->m_LCM;\n\t\tm_imp->m_LCM = new FELinearConstraintManager(this);\n\t\tm_imp->m_LCM->CopyFrom(*fem.m_imp->m_LCM);\n\t}\n\n\t// copy output data\n\tm_imp->m_plotData = fem.m_imp->m_plotData;\n\n\t// TODO: copy all the properties\n//\tassert(false);\n}\n\n//-----------------------------------------------------------------------------\n// This function serializes data to a stream.\n// This is used for running and cold restarts.\nvoid FEModel::Implementation::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow())\n\t{\n\t\t// stream model data\n\t\tar & m_timeInfo;\n\n\t\t// stream mesh\n\t\tar & m_mesh;\n\n\t\t// stream domains\n\t\tm_fem->SerializeGeometry(ar);\n\n\t\t// serialize contact\n\t\tar & m_CI;\n\n\t\t// serialize nonlinear constraints\n\t\tar & m_NLC;\n\n\t\t// serialize step and solver data\n\t\tar & m_Step;\n\t}\n\telse\n\t{\n\t\tif (ar.IsLoading()) m_fem->Clear();\n\n\t\tar & m_moduleName;\n\n\t\tif (ar.IsLoading())\n\t\t{\n\t\t\tFECoreKernel::GetInstance().SetActiveModule(m_moduleName.c_str());\n\t\t}\n\n\t\tar & m_timeInfo;\n\t\tar & m_dofs;\n\t\tar & m_Const;\n\t\tar & m_GD;\n\t\tar & m_ftime0;\n\t\tar & m_bsolved;\n\n\t\t// serialize the mesh first\n\t\tar & m_mesh;\n\n\t\t// we have to stream materials before we serialize the domains\n\t\tar & m_MAT;\n\n\t\t// stream geometry (domains)\n\t\tm_fem->SerializeGeometry(ar);\n\n\t\t// stream all boundary conditions\n\t\tar & m_BC;\n\t\tar & m_ML;\n\t\tar & m_IC;\n\t\tar & m_CI;\n\t\tar & m_NLC;\n\n\t\t// stream step data next\n\t\tar & m_nStep;\n\t\tar & m_Step;\n\t\tar & m_pStep; // This must be streamed after m_Step\n\n\t\t// serialize linear constraints\n\t\tif (m_LCM) m_LCM->Serialize(ar);\n\n\t\t// serialize data generators\n\t\tar & m_MD;\n\n\t\t// load controllers and load parameters are streamed last\n\t\t// since they can depend on other model parameters.\n\t\tar & m_LC;\n\t\tar & m_Param;\n\n\t\t// if the model was solved, then start at the next step\n\t\tif (ar.IsLoading())\n\t\t{\n\t\t\tif (m_bsolved)\n\t\t\t{\n\t\t\t\tm_bsolved = false;\n\t\t\t\tm_nStep++;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This is called to serialize geometry.\n//! Derived classes can override this\nvoid FEModel::SerializeGeometry(DumpStream& ar)\n{\n\tm_imp->m_mesh.SerializeDomains(ar);\n}\n\n//-----------------------------------------------------------------------------\n// This function serializes data to a stream.\n// This is used for running and cold restarts.\nvoid FEModel::Serialize(DumpStream& ar)\n{\n\tTRACK_TIME(TimerID::Timer_Serialize);\n\n\tm_imp->Serialize(ar);\n\tDoCallback(ar.IsSaving() ? CB_SERIALIZE_SAVE : CB_SERIALIZE_LOAD);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::BuildMatrixProfile(FEGlobalMatrix& G, bool breset)\n{\n\tFEAnalysis* pstep = GetCurrentStep();\n\tFESolver* solver = pstep->GetFESolver();\n\tsolver->BuildMatrixProfile(G, breset);\n}\n\n//-----------------------------------------------------------------------------\nbool FEModel::GetNodeData(int ndof, vector<double>& data)\n{\n\t// get the dofs\n\tDOFS& dofs = GetDOFS();\n\n\t// make sure the dof index is valid\n\tif ((ndof < 0) || (ndof >= dofs.GetTotalDOFS())) return false;\n\n\t// get the mesh and number of nodes\n\tFEMesh& mesh = GetMesh();\n\tint N = mesh.Nodes();\n\n\t// make sure data is correct size\n\tdata.resize(N, 0.0);\n\n\t// loop over all nodes\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tdata[i] = node.get(ndof);\n\t}\n\t\n\treturn true;\n}\n\nvoid FEModel::CollectTimings(bool b)\n{\n\tm_imp->m_dotiming = b;\n}\n\nbool FEModel::CollectTimings() const\n{\n\treturn m_imp->m_dotiming;\n}\n\n//-----------------------------------------------------------------------------\n// reset all the timers\nvoid FEModel::ResetAllTimers()\n{\n\tfor (size_t i = 0; i<m_imp->m_timers.size(); ++i)\n\t{\n\t\tTimer& ti = m_imp->m_timers[i];\n\t\tti.reset();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nint FEModel::Timers()\n{\n\treturn (int)m_imp->m_timers.size();\n}\n\n//-----------------------------------------------------------------------------\nTimer* FEModel::GetTimer(int i)\n{\n\treturn &(m_imp->m_timers[i]);\n}\n\n//-----------------------------------------------------------------------------\n//! return number of mesh adaptors\nint FEModel::MeshAdaptors()\n{\n\treturn (int)m_imp->m_MA.size();\n}\n\n//-----------------------------------------------------------------------------\n//! retrieve a mesh adaptors\nFEMeshAdaptor* FEModel::MeshAdaptor(int i)\n{\n\treturn m_imp->m_MA[i];\n}\n\n//-----------------------------------------------------------------------------\n//! add a mesh adaptor\nvoid FEModel::AddMeshAdaptor(FEMeshAdaptor* meshAdaptor)\n{\n\tm_imp->m_MA.push_back(meshAdaptor);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModel::SetUnits(const char* szunits)\n{\n\tif (szunits)\n\t\tm_imp->m_units = szunits;\n\telse\n\t\tm_imp->m_units.clear();\n}\n\n//-----------------------------------------------------------------------------\nconst char* FEModel::GetUnits() const\n{\n\tif (m_imp->m_units.empty()) return nullptr;\n\telse return m_imp->m_units.c_str();\n}\n\nbool FEModel::AddScript(const std::string& name, const std::string& script)\n{\n\tif (name.empty() || script.empty()) return false;\n\n\t// make sure the script name is unique\n\tif (m_imp->m_scripts.count(name) > 0)\n\t{\n\t\tfeLogError(\"A script with the name \\\"%s\\\" already exists\", name.c_str());\n\t\treturn false;\n\t}\n\n\n\tm_imp->m_scripts[name] = {name, script};\n\treturn true;\n}\n\nFEScript FEModel::GetScript(const std::string& name) const\n{\n\tauto it = m_imp->m_scripts.find(name);\n\tif (it != m_imp->m_scripts.end())\n\t{\n\t\treturn it->second;\n\t}\n\telse\n\t{\n\t\treturn FEScript();\n\t}\n}\n"
  },
  {
    "path": "FECore/FEModel.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"DOFS.h\"\n#include \"FEMesh.h\"\n#include \"FETimeInfo.h\"\n#include \"FEStepComponent.h\"\n#include \"Callback.h\"\n#include \"FECoreKernel.h\"\n#include \"DataStore.h\"\n#include \"FEDataValue.h\"\n#include <string>\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FELoadController;\nclass FEMaterial;\nclass FEModelLoad;\nclass FEBoundaryCondition;\nclass FEInitialCondition;\nclass FENLConstraint;\nclass FESurfacePairConstraint;\nclass FEAnalysis;\nclass FEGlobalData;\nclass FEGlobalMatrix;\nclass FELinearConstraintManager;\nclass FEDataArray;\nclass FEMeshAdaptor;\nclass Timer;\nclass FEPlotDataStore;\nclass FEMeshDataGenerator;\n\n//-----------------------------------------------------------------------------\n// struct that breaks down memory usage of FEModel\nstruct FEMODEL_MEMORY_STATS {\n\tsize_t\t\tStiffnessMatrix;\n\tsize_t\t\tMesh;\n\tsize_t\t\tLinearSolver;\n\tsize_t\t\tNonLinSolver;\n};\n\n//-----------------------------------------------------------------------------\n// helper class for managing global (user-defined) variables.\nclass FEGlobalVariable\n{\npublic:\n\tdouble\tv;\n\tstring\tname;\n};\n\n//-----------------------------------------------------------------------------\n// Definition of a febcode script\nstruct FEScript\n{\n\tstring\t\tname;\t\t//!< script name\n\tstring\t\tscript;\t\t//!< script content\n};\n\n//! The FEModel class stores all the data for the finite element model, including\n//! geometry, analysis steps, boundary and loading conditions, contact interfaces\n//! and so on.\n//!\nclass FECORE_API FEModel : public FECoreBase, public CallbackHandler\n{\n\tFECORE_SUPER_CLASS(FEMODEL_ID)\n\npublic:\n\tenum {MAX_STRING = 256};\n\npublic:\n\tFEModel(void);\n\tvirtual ~FEModel(void);\n\n\t// Initialization\n\tvirtual bool Init() override;\n\n\t//! Resets data structures\n\tvirtual bool Reset();\n\n\t// solve the model\n\tvirtual bool Solve();\n\n\t// copy the model data\n\tvirtual void CopyFrom(FEModel& fem);\n\n\t// clear all model data\n\tvirtual void Clear();\n\n\t// model activation\n\tvirtual void Activate();\n\n\t// TODO: temporary construction. Need to see if I can just use Activate(). \n\t//       This is called after remeshed\n\tvirtual void Reactivate();\n\n\t//! Call this function whenever the geometry of the model has changed.\n\tvirtual void Update();\n\n\t//! will return true if the model solved succussfully\n\tbool IsSolved() const;\n\npublic: // reverse control solver interface\n\tbool RCI_Init();\n\tbool RCI_Restart();\n\tbool RCI_Rewind();\n\tbool RCI_Advance();\n\tbool RCI_Finish();\n\tbool RCI_ClearRewindStack();\n\npublic:\n\t// get the FE mesh\n\tFEMesh& GetMesh();\n\n\t// get the linear constraint manager\n\tFELinearConstraintManager& GetLinearConstraintManager();\n\n\t//! Validate BC's\n\tbool InitBCs();\n\n\t//! Initialize the mesh\n\tvirtual bool InitMesh();\n\n\t//! mesh validation\n\tvoid ValidateMesh();\n\n\t//! Initialize shells\n\tvirtual bool InitShells();\n\n\t//! Build the matrix profile for this model\n\tvirtual void BuildMatrixProfile(FEGlobalMatrix& G, bool breset);\n\n\t// call this function to set the mesh's update flag\n\tvoid SetMeshUpdateFlag(bool b);\n\npublic:\t// --- Load controller functions ----\n\n\t//! Add a load controller to the model\n\tvoid AddLoadController(FELoadController* plc);\n\n\t//! replace a load controller\n\tvoid ReplaceLoadController(int n, FELoadController* plc);\n\n\t//! get a load controller\n\tFELoadController* GetLoadController(int i);\n\n\t//! get the number of load controllers\n\tint LoadControllers() const;\n\n\t//! Attach a load controller to a parameter\n\tvoid AttachLoadController(FEParam* p, int lc);\n\tvoid AttachLoadController(FEParam* p, FELoadController* plc);\n\n\t//! Detach a load controller from a parameter\n\tbool DetachLoadController(FEParam* p);\n\n\t//! return the number of load-controlled parameters\n\tint LoadParams() const;\n\n\t//! return a load-controlled parameter\n\tFEParam* GetLoadParam(int n);\n\n\t//! Get a load controller for a parameter (returns null if the param is not under load control)\n\tFELoadController* GetLoadController(FEParam* p);\n\n\t//! initialization of load controllers\n\tbool InitLoadControllers();\n\npublic:\t// --- mesh data generators ---\n\n\t//! Add a mesh data generator to the model\n\tvoid AddMeshDataGenerator(FEMeshDataGenerator* pmd);\n\n\t//! get a mesh data generator\n\tFEMeshDataGenerator* GetMeshDataGenerator(int i);\n\n\t//! get the number of mesh data generators\n\tint MeshDataGenerators() const;\n\n\t//! initialize mesh data generators\n\tbool InitMeshDataGenerators();\n\npublic: // --- Material functions ---\n\n\t//! Add a material to the model\n\tvoid AddMaterial(FEMaterial* pm);\n\n\t//! get the number of materials\n\tint Materials();\n\n\t//! return a pointer to a material\n\tFEMaterial* GetMaterial(int i);\n\n\t//! find a material based on its index\n\tFEMaterial* FindMaterial(int nid);\n\n\t//! find a material based on its name\n\tFEMaterial* FindMaterial(const std::string& matName);\n\n\t//! material initialization\n\tbool InitMaterials();\n\n\t//! material validation\n\tbool ValidateMaterials();\n\npublic:\n\t// Boundary conditions\n\tint BoundaryConditions() const;\n\tFEBoundaryCondition* BoundaryCondition(int i);\n\tvoid AddBoundaryCondition(FEBoundaryCondition* bc);\n\tvoid ClearBoundaryConditions();\n\n\t// initial conditions\n\tint InitialConditions();\n\tFEInitialCondition* InitialCondition(int i);\n\tvoid AddInitialCondition(FEInitialCondition* pbc);\n\npublic: // --- Analysis steps functions ---\n\n\t//! retrieve the number of steps\n\tint Steps() const;\n\n\t//! clear the steps\n\tvoid ClearSteps();\n\n\t//! Add an analysis step\n\tvoid AddStep(FEAnalysis* pstep);\n\n\t//! Get a particular step\n\tFEAnalysis* GetStep(int i);\n\n\t//! Get the current step\n\tFEAnalysis* GetCurrentStep();\n\tconst FEAnalysis* GetCurrentStep() const;\n\n\t//! Set the current step index\n\tint GetCurrentStepIndex() const;\n\n\t//! Set the current step\n\tvoid SetCurrentStep(FEAnalysis* pstep);\n\n\t//! Set the current step index\n\tvoid SetCurrentStepIndex(int n);\n\n\t//! Get the current time\n\tFETimeInfo& GetTime();\n\n\t//! Get the start time\n\tdouble GetStartTime() const;\n\n\t//! Set the start time\n\tvoid SetStartTime(double t);\n\n\t//! Get the current time\n\tdouble GetCurrentTime() const;\n\n\t//! Set the current time\n\tvoid SetCurrentTime(double t);\n\n\t//! set the current time step\n\tvoid SetCurrentTimeStep(double dt);\n\n\t//! initialize steps\n\tbool InitSteps();\n\npublic: // --- Contact interface functions ---\n\n\t//! return number of surface pair constraints\n\tint SurfacePairConstraints();\n\n\t//! retrive a surface pair interaction\n\tFESurfacePairConstraint* SurfacePairConstraint(int i);\n\n\t//! Add a surface pair constraint\n\tvoid AddSurfacePairConstraint(FESurfacePairConstraint* pci);\n\n\t//! Initializes contact data\n\tbool InitContact();\n\npublic: // --- Nonlinear constraints functions ---\n\n\t//! return number of nonlinear constraints\n\tint NonlinearConstraints();\n\n\t//! retrieve a nonlinear constraint\n\tFENLConstraint* NonlinearConstraint(int i);\n\n\t//! add a nonlinear constraint\n\tvoid AddNonlinearConstraint(FENLConstraint* pnlc);\n\n\t//! Initialize constraint data\n\tbool InitConstraints();\n\npublic:\t// --- Model Loads ----\n\t//! return the number of model loads\n\tint ModelLoads();\n\n\t//! retrieve a model load\n\tFEModelLoad* ModelLoad(int i);\n\n\t//! Add a model load\n\tvoid AddModelLoad(FEModelLoad* pml);\n\n\t//! initialize model loads\n\tbool InitModelLoads();\n\npublic:\t// --- Mesh adaptors ---\n\t//! return number of mesh adaptors\n\tint MeshAdaptors();\n\n\t//! retrieve a mesh adaptors\n\tFEMeshAdaptor* MeshAdaptor(int i);\n\n\t//! add a mesh adaptor\n\tvoid AddMeshAdaptor(FEMeshAdaptor* meshAdaptor);\n\n\t//! initialize mesh adaptors\n\tbool InitMeshAdaptors();\n\npublic: // --- parameter functions ---\n\n\t//! evaluate all load controllers at some time\n\tvoid EvaluateLoadControllers(double time);\n\n\t// evaluate all mesh data\n\tvoid EvaluateDataGenerators(double time);\n\n\t//! evaluate all load parameters\n\tvirtual bool EvaluateLoadParameters();\n\n\t//! Find a model parameter\n\tFEParam* FindParameter(const ParamString& s) override;\n\n\t//! return a reference to the named parameter\n\tFEParamValue GetParameterValue(const ParamString& param) override;\n\n\t//! return the parameter string for a parameter\n\tstd::string GetParamString(FEParam* p);\n\n\t//! Find property \n\t//! Note: Can't call this FindProperty, since this is already defined in base class\n\tFECoreBase* FindComponent(const ParamString& prop);\n\n\t//! Set the print parameters flag\n\tvoid SetPrintParametersFlag(bool b);\n\n\t//! Get the print parameter flag\n\tbool GetPrintParametersFlag() const;\n\n\t//! return a data value object\n\tFEDataValue GetDataValue(const ParamString& s);\n\npublic:\t// --- Miscellaneous routines ---\n\n\t//! call the callback function\n\t//! This function returns fals if the run is to be aborted\n\tbool DoCallback(unsigned int nevent);\n\n\t//! I'd like to place the list of DOFS inside the model.\n\t//! As a first step, all classes that have access to the model\n\t//! should get the DOFS from this function\n\tDOFS& GetDOFS();\n\n\t//! Get the index of a DOF\n\tint GetDOFIndex(const char* sz) const;\n\tint GetDOFIndex(const char* szvar, int n) const;\n\n\t//! serialize data for restarts\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! This is called to serialize geometry.\n\t//! Derived classes can override this\n\tvirtual void SerializeGeometry(DumpStream& ar);\n\n\t//! set the active module\n\tvoid SetActiveModule(const std::string& moduleName);\n\n\t//! get the module name\n\tstring GetModuleName() const;\n\npublic:\n\t//! Log a message\n\tvoid Logf(int ntag, const char* msg, ...);\n\tvoid BlockLog();\n\tvoid UnBlockLog();\n\tbool LogBlocked() const;\n\n\tvoid SetVerboseMode(bool b);\n\npublic:\n\t// Derived classes can use this to implement the actual logging mechanism\n\tvirtual void Log(int ntag, const char* msg);\n\npublic: // Global data\n\tvoid AddGlobalData(FEGlobalData* psd);\n\tFEGlobalData* GetGlobalData(int i);\n\tFEGlobalData* FindGlobalData(const char* szname);\n\tint FindGlobalDataIndex(const char* szname) const;\n\tint GlobalDataItems();\n\n\t// get/set global data\n\tvoid SetGlobalConstant(const string& s, double v);\n\tdouble GetGlobalConstant(const string& s);\n\n\tint GlobalVariables() const;\n\tvoid AddGlobalVariable(const string& s, double v);\n\tconst FEGlobalVariable& GetGlobalVariable(int n);\n\npublic: // Data retrieval\n\n\t// get nodal dof data\n\tbool GetNodeData(int dof, vector<double>& data);\n\n\t//! return the data store\n\tDataStore& GetDataStore();\n\n\t//! return plot data\n\tFEPlotDataStore& GetPlotDataStore();\n\tconst FEPlotDataStore& GetPlotDataStore() const;\n\npublic:\n\t// decide whether to collect timings\n\tvoid CollectTimings(bool b);\n\n\t// return if this model has timing collection on\n\tbool CollectTimings() const;\n\n\t// reset all the timers\n\tvoid ResetAllTimers();\n\n\t// return total number of timers\n\tint Timers();\n\n\t// return a timer by index\n\tTimer* GetTimer(int i);\n\n\t// get the number of calls to Update()\n\tint UpdateCounter() const;\n\n\t// this can be used to change the update counter\n\tvoid IncrementUpdateCounter();\n\n\tbool AddScript(const std::string& name, const std::string& script);\n\n\tFEScript GetScript(const std::string& name) const;\n\npublic:\n\tvoid SetUnits(const char* szunits);\n\tconst char* GetUnits() const;\n\nprotected:\n\tFEParamValue GetMeshParameter(const ParamString& paramString);\n\nprivate:\n\tclass Implementation;\n\tImplementation*\tm_imp;\n\n\tDECLARE_FECORE_CLASS();\n};\n\nFECORE_API FECoreBase* CopyFEBioClass(FECoreBase* pc, FEModel* fem);\n"
  },
  {
    "path": "FECore/FEModelComponent.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEModelComponent.h\"\n#include \"FEModel.h\"\n#include \"FEAnalysis.h\"\n#include \"DumpStream.h\"\n#include <string.h>\n\n//-----------------------------------------------------------------------------\nFEModelComponent::FEModelComponent(FEModel* fem) : FECoreBase(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nFEModelComponent::~FEModelComponent()\n{\n\t\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelComponent::Update()\n{\n\n}\n\n//-----------------------------------------------------------------------------\ndouble FEModelComponent::CurrentTime() const\n{\n\treturn GetFEModel()->GetTime().currentTime;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEModelComponent::CurrentTimeIncrement() const\n{\n\treturn GetFEModel()->GetTime().timeIncrement;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEModelComponent::GetGlobalConstant(const char* sz) const\n{\n\treturn GetFEModel()->GetGlobalConstant(sz);\n}\n\n//-----------------------------------------------------------------------------\nint FEModelComponent::GetDOFIndex(const char* szvar, int n) const\n{\n\treturn GetFEModel()->GetDOFIndex(szvar, n);\n}\n\n//-----------------------------------------------------------------------------\nint FEModelComponent::GetDOFIndex(const char* szdof) const\n{\n\treturn GetFEModel()->GetDOFIndex(szdof);\n}\n\n//-----------------------------------------------------------------------------\n//! Get the model's mesh\nFEMesh& FEModelComponent::GetMesh()\n{\n\treturn GetFEModel()->GetMesh();\n}\n\n//-----------------------------------------------------------------------------\nconst FETimeInfo& FEModelComponent::GetTimeInfo() const\n{\n\treturn GetFEModel()->GetTime();\n}\n\nFESolver* FEModelComponent::GetSolver()\n{\n\tFEAnalysis* step = GetFEModel()->GetCurrentStep();\n\tif (step == nullptr) return nullptr;\n\treturn step->GetFESolver();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelComponent::AttachLoadController(const char* szparam, int lc)\n{\n\tFEParam* p = GetParameter(szparam); assert(p);\n\tif (p)\n\t{\n\t\tFEModel* fem = GetFEModel();\n\t\tfem->AttachLoadController(p, lc);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelComponent::AttachLoadController(void* pd, int lc)\n{\n\tFEParam* p = FindParameterFromData(pd); assert(p);\n\tif (p)\n\t{\n\t\tFEModel* fem = GetFEModel();\n\t\tfem->AttachLoadController(p, lc);\n\t}\n}\n"
  },
  {
    "path": "FECore/FEModelComponent.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n#include \"FETimeInfo.h\"\n\n//-----------------------------------------------------------------------------\n//! forward declaration of the FEModel class.\n//! All classes inherited from FEModelComponent should take the model as a parameter\n//! to the constructor.\nclass FENodeSet;\nclass FEMesh;\nclass FESolver;\n\n//-----------------------------------------------------------------------------\n//! This class serves as a base class for many of the FECore classes. It defines\n//! activation and deactivation functions which are used in multi-step analyses to determine which\n//! components are active during an analysis.\n//! A model component is basically anything that affects the state of a model.\n//! For instance, boundary conditions, loads, contact definitions, etc.\nclass FECORE_API FEModelComponent : public FECoreBase\n{\npublic:\n\t//! constructor\n\tFEModelComponent(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FEModelComponent();\n\n\t//-----------------------------------------------------------------------------------\n\t//! Update the component\n\t//! This is called whenever the model is updated, i.e. the primary variables were updated.\n\tvirtual void Update();\n\npublic: // some convenience functions (to pull data from FEModel without the need to include)\n\tdouble CurrentTime() const;\n\tdouble CurrentTimeIncrement() const;\n\tdouble GetGlobalConstant(const char* sz) const;\n\tint GetDOFIndex(const char* szvar, int n) const;\n\tint GetDOFIndex(const char* szdof) const;\n\tconst FETimeInfo& GetTimeInfo() const;\n\tFESolver* GetSolver();\n\tvoid AttachLoadController(const char* szparam, int lc);\n\tvoid AttachLoadController(void* pd, int lc);\n\n\t//! Get the model's mesh\n\tFEMesh& GetMesh();\n};\n"
  },
  {
    "path": "FECore/FEModelDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEModelDataRecord.h\"\n\nFEModelLogData::FEModelLogData(FEModel* fem) : FELogData(fem) {}\nFEModelLogData::~FEModelLogData() {}\n\nFEModelDataRecord::FEModelDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_MODEL) {}\ndouble FEModelDataRecord::Evaluate(int item, int ndata)\n{\n\tassert(item == 0);\n\treturn m_data[ndata]->value();\n}\n\nvoid FEModelDataRecord::ClearData()\n{\n\tfor (int i = 0; i < m_data.size(); ++i) delete m_data[i];\n\tm_data.clear();\n}\n\nvoid FEModelDataRecord::SetData(const char* szexpr)\n{\n\tchar szcopy[MAX_STRING] = { 0 };\n\tstrcpy(szcopy, szexpr);\n\tchar* sz = szcopy, * ch;\n\tClearData();\n\tstrcpy(m_szdata, szexpr);\n\tdo\n\t{\n\t\tch = strchr(sz, ';');\n\t\tif (ch) *ch++ = 0;\n\t\tFEModelLogData* pdata = fecore_new<FEModelLogData>(sz, GetFEModel());\n\t\tif (pdata) m_data.push_back(pdata);\n\t\telse throw UnknownDataField(sz);\n\t\tsz = ch;\n\t} while (ch);\n}\n\nvoid FEModelDataRecord::SelectAllItems()\n{\n\tstd::vector<int> items;\n\titems.push_back(0);\n\tSetItemList(items);\n}\n\nint FEModelDataRecord::Size() const\n{\n\treturn 1;\n}\n"
  },
  {
    "path": "FECore/FEModelDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n\n//-----------------------------------------------------------------------------\n//! This is the base class for a model data\nclass FECORE_API FEModelLogData : public FELogData\n{\n\tFECORE_SUPER_CLASS(FELOGMODELDATA_ID)\n\tFECORE_BASE_CLASS(FEModelLogData)\n\npublic:\n\tFEModelLogData(FEModel* fem);\n\tvirtual ~FEModelLogData();\n\tvirtual double value() = 0;\n};\n\n//-----------------------------------------------------------------------------\n//! This class records model data\nclass FECORE_API FEModelDataRecord : public DataRecord\n{\npublic:\n\tFEModelDataRecord(FEModel* pfem);\n\tdouble Evaluate(int item, int ndata);\n\tvoid SetData(const char* sz);\n\tvoid ClearData();\n\tvoid SelectAllItems();\n\tint Size() const;\n\nprivate:\n\tstd::vector<FEModelLogData*>\tm_data;\n};\n"
  },
  {
    "path": "FECore/FEModelLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEModelLoad.h\"\n#include \"FESolver.h\"\n\n//-----------------------------------------------------------------------------\nFEModelLoad::FEModelLoad(FEModel* pfem) : FEStepComponent(pfem), m_dof(pfem)\n{\n}\n\n//-----------------------------------------------------------------------------\nconst FEDofList& FEModelLoad::GetDofList() const\n{\n\treturn m_dof;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelLoad::Serialize(DumpStream& ar)\n{\n\tFEStepComponent::Serialize(ar);\n\tar & m_dof;\n}\n\nvoid FEModelLoad::PrepStep()\n{\n\n}\n\nMatrix_Type FEModelLoad::PreferredMatrixType()\n{\n\tFEParam* p = GetParameter(\"symmetric_stiffness\");\n\tif (p && (p->type() == FE_PARAM_BOOL) && !p->value<bool>())\n\t{\n\t\treturn REAL_UNSYMMETRIC;\n\t}\n\treturn REAL_SYMMETRIC;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelLoad::LoadVector(FEGlobalVector& R)\n{\n\t// base class does nothing\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModelLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// base class does nothing.\n}\n"
  },
  {
    "path": "FECore/FEModelLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEStepComponent.h\"\n#include \"FEGlobalVector.h\"\n#include <FECore/FEDofList.h>\n\n//-----------------------------------------------------------------------------\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! This class is the base class for all classes that affect the state of the model\n//! and contribute directly to the residual and the global stiffness matrix. This\n//! includes most boundary loads, body loads, contact, etc.\nclass FECORE_API FEModelLoad : public FEStepComponent\n{\n\tFECORE_SUPER_CLASS(FELOAD_ID)\n\tFECORE_BASE_CLASS(FEModelLoad)\n\npublic:\n\t//! constructor\n\tFEModelLoad(FEModel* pfem);\n\n\tconst FEDofList& GetDofList() const;\n\t\n\tvoid Serialize(DumpStream& ar) override;\n\n\tvirtual void PrepStep();\n\n\tvirtual Matrix_Type PreferredMatrixType();\n\npublic:\n\t// all classes derived from this base class must implement\n\t// the following functions.\n\n\t//! evaluate the contribution to the external load vector\n\tvirtual void LoadVector(FEGlobalVector& R);\n\n\t//! evaluate the contribution to the global stiffness matrix\n\tvirtual void StiffnessMatrix(FELinearSystem& LS);\n\nprotected:\n\tFEDofList\tm_dof;\n};\n"
  },
  {
    "path": "FECore/FEModelParam.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEModelParam.h\"\n#include \"MObjBuilder.h\"\n#include \"FEDataArray.h\"\n#include \"DumpStream.h\"\n#include \"FEConstValueVec3.h\"\n\n//---------------------------------------------------------------------------------------\nFEModelParam::FEModelParam()\n{ \n\tm_scl = 1.0;\n\tm_dom = 0;\n}\n\nFEModelParam::~FEModelParam()\n{\n}\n\n// serialization\nvoid FEModelParam::Serialize(DumpStream& ar)\n{\n\tar & m_scl;\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_dom;\n\t}\n}\n\n//---------------------------------------------------------------------------------------\nFEParamDouble::FEParamDouble()\n{\n\tm_val = fecore_new<FEScalarValuator>(\"const\", nullptr);\n}\n\nFEParamDouble::FEParamDouble(double v)\n{\n\tm_val = fecore_new<FEScalarValuator>(\"const\", nullptr);\n\t*static_cast<FEConstValue*>(m_val)->constValue() = v;\n}\n\nFEParamDouble::~FEParamDouble()\n{\n\tdelete m_val;\n}\n\nFEParamDouble::FEParamDouble(const FEParamDouble& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n\tm_dom = p.m_dom;\n}\n\n// set the value\nvoid FEParamDouble::operator = (double v)\n{\n\tFEConstValue* val = fecore_new<FEConstValue>(\"const\", nullptr);\n\t*val->constValue() = v;\n\tsetValuator(val);\n}\n\nvoid FEParamDouble::operator = (const FEParamDouble& p)\n{\n\tif (m_val) delete m_val;\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n//\tm_dom = p.m_dom;\n}\n\n// set the valuator\nvoid FEParamDouble::setValuator(FEScalarValuator* val)\n{\n\tif (m_val) delete m_val;\n\tm_val = val;\n\tif (val) val->SetModelParam(this);\n}\n\n// get the valuator\nFEScalarValuator* FEParamDouble::valuator()\n{\n\treturn m_val;\n}\n\n// is this a const value\nbool FEParamDouble::isConst() const { return m_val->isConst(); };\n\n// get the const value (returns 0 if param is not const)\ndouble& FEParamDouble::constValue() { assert(isConst());  return *m_val->constValue(); }\ndouble FEParamDouble::constValue() const { assert(isConst()); return *m_val->constValue(); }\n\nvoid FEParamDouble::Serialize(DumpStream& ar)\n{\n\tFEModelParam::Serialize(ar);\n\tar & m_val;\n}\n\nbool FEParamDouble::Init()\n{\n\treturn (m_val ? m_val->Init() : true);\n}\n\n//---------------------------------------------------------------------------------------\nFEParamVec3::FEParamVec3()\n{\n\tm_val = fecore_new<FEVec3dValuator>(\"vector\", nullptr);\n}\n\nFEParamVec3::FEParamVec3(vec3d v)\n{\n\tm_val = fecore_new<FEVec3dValuator>(\"vector\", nullptr);\n\t*static_cast<FEConstValueVec3*>(m_val)->constValue() = v;\n}\n\nFEParamVec3::~FEParamVec3()\n{\n\tdelete m_val;\n}\n\nFEParamVec3::FEParamVec3(const FEParamVec3& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n\tm_dom = p.m_dom;\n}\n\nbool FEParamVec3::Init()\n{\n\treturn (m_val ? m_val->Init() : true);\n}\n\n// set the value\nvoid FEParamVec3::operator = (const vec3d& v)\n{\n\tFEConstValueVec3* val = fecore_new<FEConstValueVec3>(\"vector\", nullptr);\n\tval->value() = v;\n\tsetValuator(val);\n}\n\nvoid FEParamVec3::operator = (const FEParamVec3& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n//\tm_dom = p.m_dom;\n}\n\n// set the valuator\nvoid FEParamVec3::setValuator(FEVec3dValuator* val)\n{\n\tif (m_val) delete m_val;\n\tm_val = val;\n\tif (val) val->SetModelParam(this);\n}\n\nFEVec3dValuator* FEParamVec3::valuator()\n{\n\treturn m_val;\n}\n\nvoid FEParamVec3::Serialize(DumpStream& ar)\n{\n\tFEModelParam::Serialize(ar);\n\tar & m_val;\n}\n\n//==========================================================================\n\nFEParamMat3d::FEParamMat3d()\n{\n\tm_val = fecore_new<FEMat3dValuator>(\"const\", nullptr);\n}\n\nFEParamMat3d::FEParamMat3d(const mat3d& m)\n{\n\tm_val = fecore_new<FEMat3dValuator>(\"const\", nullptr);\n\t*static_cast<FEConstValueMat3d*>(m_val)->constValue() = m;\n}\n\nFEParamMat3d::~FEParamMat3d()\n{\n\tdelete m_val;\n}\n\nFEParamMat3d::FEParamMat3d(const FEParamMat3d& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n\tm_dom = p.m_dom;\n}\n\n// set the value\nvoid FEParamMat3d::operator = (const mat3d& v)\n{\n\tFEConstValueMat3d* val = fecore_new<FEConstValueMat3d>(\"const\", nullptr);\n\tval->value() = v;\n\tsetValuator(val);\n}\n\nvoid FEParamMat3d::operator = (const FEParamMat3d& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n//\tm_dom = p.m_dom;\n}\n\nbool FEParamMat3d::Init()\n{\n\treturn (m_val ? m_val->Init() : true);\n}\n\n// set the valuator\nvoid FEParamMat3d::setValuator(FEMat3dValuator* val)\n{\n\tif (m_val) delete m_val;\n\tm_val = val;\n\tif (val) val->SetModelParam(this);\n}\n\n// get the valuator\nFEMat3dValuator* FEParamMat3d::valuator()\n{\n\treturn m_val;\n}\n\nvoid FEParamMat3d::Serialize(DumpStream& ar)\n{\n\tFEModelParam::Serialize(ar);\n\tar & m_val;\n}\n\n//==========================================================================\nFEParamMat3ds::FEParamMat3ds()\n{\n\tm_val = fecore_new<FEMat3dsValuator>(\"const\", nullptr);\n}\n\nFEParamMat3ds::~FEParamMat3ds()\n{\n\tdelete m_val;\n}\n\nbool FEParamMat3ds::Init()\n{\n\treturn (m_val ? m_val->Init() : true);\n}\n\nFEParamMat3ds::FEParamMat3ds(const FEParamMat3ds& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n\tm_dom = p.m_dom;\n}\n\n// set the value\nvoid FEParamMat3ds::operator = (const mat3ds& v)\n{\n\tFEConstValueMat3ds* val = fecore_new<FEConstValueMat3ds>(\"const\", nullptr);\n\tval->value() = v;\n\tsetValuator(val);\n}\n\nvoid FEParamMat3ds::operator = (const FEParamMat3ds& p)\n{\n\tm_val = p.m_val->copy();\n\tm_scl = p.m_scl;\n//\tm_dom = p.m_dom;\n}\n\n// set the valuator\nvoid FEParamMat3ds::setValuator(FEMat3dsValuator* val)\n{\n\tif (m_val) delete m_val;\n\tm_val = val;\n\tif (val) val->SetModelParam(this);\n}\n\nFEMat3dsValuator* FEParamMat3ds::valuator()\n{\n\treturn m_val;\n}\n\nvoid FEParamMat3ds::Serialize(DumpStream& ar)\n{\n\tFEModelParam::Serialize(ar);\n\tar & m_val;\n}\n"
  },
  {
    "path": "FECore/FEModelParam.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEScalarValuator.h\"\n#include \"FEVec3dValuator.h\"\n#include \"FEMat3dValuator.h\"\n#include \"FEMat3dsValuator.h\"\n#include \"FEItemList.h\"\n\n//---------------------------------------------------------------------------------------\n// Base for model parameters.\nclass FECORE_API FEModelParam\n{\npublic:\n\tFEModelParam();\n\tvirtual ~FEModelParam();\n\n\t// initialization\n\tvirtual bool Init() { return true; }\n\n\t// set the domain\n\tvoid SetItemList(FEItemList* itemList) { m_dom = itemList; }\n\n\t// get the domain list\n\tFEItemList* GetItemList() { return m_dom;  }\n\n\t// set the scale factor\n\tvoid SetScaleFactor(double s) { m_scl = s; }\n\n\t// return the scale factor\n\tdouble GetScaleFactor() const { return m_scl; }\n\npublic: // serialization\n\n\tvirtual void Serialize(DumpStream& ar);\n\n\t// these are never called, but we need them to compile some templates\n\tstatic void SaveClass(DumpStream& ar, FEModelParam*& a) { assert(false); }\n\tstatic FEModelParam* LoadClass(DumpStream& ar, FEModelParam* a) { assert(false); return nullptr; }\n\nprotected:\n\tdouble\t\t\tm_scl;\t//!< scale factor. Used to store load curve value\n\tFEItemList*\t\tm_dom;\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEParamDouble : public FEModelParam\n{\npublic:\n\tFEParamDouble();\n\t~FEParamDouble();\n\n\tFEParamDouble(double v);\n\n\tFEParamDouble(const FEParamDouble& p);\n\n\t// set the value\n\tvoid operator = (double v);\n\tvoid operator = (const FEParamDouble& p);\n\n\t// set the valuator\n\tvoid setValuator(FEScalarValuator* val);\n\n\t// get the valuator\n\tFEScalarValuator* valuator();\n\n\t// evaluate the parameter at a material point\n\tdouble operator () (const FEMaterialPoint& pt) { return m_scl*(*m_val)(pt); }\n\n\t// is this a const value\n\tbool isConst() const;\n\n\t// get the const value (return value undefined if param is not const)\n\tdouble& constValue();\n\tdouble constValue() const;\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tbool Init() override;\n\nprivate:\n\tFEScalarValuator*\tm_val;\n};\n\n//=======================================================================================\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEParamVec3 : public FEModelParam\n{\npublic:\n\tFEParamVec3();\n\tFEParamVec3(vec3d v);\n\t~FEParamVec3();\n\n\tFEParamVec3(const FEParamVec3& p);\n\n\tbool Init() override;\n\n\t// set the value\n\tvoid operator = (const vec3d& v);\n\tvoid operator = (const FEParamVec3& p);\n\n\t// set the valuator\n\tvoid setValuator(FEVec3dValuator* val);\n\tFEVec3dValuator* valuator();\n\n\t// evaluate the parameter at a material point\n\tvec3d operator () (const FEMaterialPoint& pt) { return (*m_val)(pt)*m_scl; }\n\n\t// return a unit vector\n\tvec3d unitVector(const FEMaterialPoint& pt) { return (*this)(pt).normalized(); }\n\n\t// is this a const\n\tbool isConst() const { return m_val->isConst(); }\n\n\t// (return value undefined if param is not const)\n\tvec3d& constValue() { assert(isConst()); return *m_val->constValue(); };\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFEVec3dValuator*\tm_val;\n};\n\n//=======================================================================================\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEParamMat3d : public FEModelParam\n{\npublic:\n\tFEParamMat3d();\n\tFEParamMat3d(const mat3d& m);\n\t~FEParamMat3d();\n\n\tFEParamMat3d(const FEParamMat3d& p);\n\n\t// set the value\n\tvoid operator = (const mat3d& v);\n\tvoid operator = (const FEParamMat3d& v);\n\n\tbool Init() override;\n\n\t// set the valuator\n\tvoid setValuator(FEMat3dValuator* val);\n\n\t// get the valuator\n\tFEMat3dValuator* valuator();\n\n\t// evaluate the parameter at a material point\n\tmat3d operator () (const FEMaterialPoint& pt) { return (*m_val)(pt)*m_scl; }\n\n\t// is this a const\n\tbool isConst() const { return m_val->isConst(); }\n\n\t// (return value undefined if not constant)\n\tmat3d& constValue() { assert(isConst()); return *m_val->constValue(); };\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFEMat3dValuator*\tm_val;\n};\n\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEParamMat3ds : public FEModelParam\n{\npublic:\n\tFEParamMat3ds();\n\t~FEParamMat3ds();\n\n\tFEParamMat3ds(const FEParamMat3ds& p);\n\n\t// set the value\n\tvoid operator = (const mat3ds& v);\n\tvoid operator = (const FEParamMat3ds& v);\n\n\tbool Init() override;\n\n\t// set the valuator\n\tvoid setValuator(FEMat3dsValuator* val);\n\n\t// get the valuator\n\tFEMat3dsValuator* valuator();\n\n\t// evaluate the parameter at a material point\n\tmat3ds operator () (const FEMaterialPoint& pt) { return (*m_val)(pt)*m_scl; }\n\n\t// is this a const\n\tbool isConst() const { return m_val->isConst(); }\n\n\t// (return value undefined if not constant)\n\tmat3ds& constValue() { assert(isConst()); return *m_val->constValue(); };\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFEMat3dsValuator*\tm_val;\n};\n"
  },
  {
    "path": "FECore/FEModifiedNewtonStrategy.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEModifiedNewtonStrategy.h\"\n#include \"FESolver.h\"\n#include \"FEException.h\"\n#include \"FENewtonSolver.h\"\n\n//-----------------------------------------------------------------------------\nFEModifiedNewtonStrategy::FEModifiedNewtonStrategy(FEModel* fem) : FENewtonStrategy(fem)\n{\n\tm_plinsolve = nullptr;\n\tm_maxups = 0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModifiedNewtonStrategy::Init()\n{\n\tif (m_pns == nullptr) return false;\n\tm_plinsolve = m_pns->GetLinearSolver();\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEModifiedNewtonStrategy::Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1)\n{\n\t// nothing to do here.\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEModifiedNewtonStrategy::SolveEquations(vector<double>& x, vector<double>& b)\n{\n\t// perform a backsubstitution\n\tif (m_plinsolve->BackSolve(x, b) == false)\n\t{\n\t\tthrow LinearSolverFailed();\n\t}\n}\n"
  },
  {
    "path": "FECore/FEModifiedNewtonStrategy.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include \"matrix.h\"\n#include \"vector.h\"\n#include \"LinearSolver.h\"\n#include \"FENewtonStrategy.h\"\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FEModifiedNewtonStrategy : public FENewtonStrategy\n{\npublic:\n\t//! constructor\n\tFEModifiedNewtonStrategy(FEModel* fem);\n\n\t//! New initialization method\n\tbool Init() override;\n\n\t//! perform a BFGS udpate\n\tbool Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1) override;\n\n\t//! solve the equations\n\tvoid SolveEquations(vector<double>& x, vector<double>& b) override;\n\npublic:\n\t// keep a pointer to the linear solver\n\tLinearSolver*\tm_plinsolve;\t//!< pointer to linear solver\n};\n"
  },
  {
    "path": "FECore/FEModule.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEModule.h\"\n#include <vector>\n#include <cstring>\n\nclass FEModule::Impl\n{\npublic:\n\tconst char*\t\t\tszname = 0;\t\t// name of module\n\tconst char*\t\t\tszdesc = 0;\t\t// description of module (optional, can be null)\n\tunsigned int\t\tid = 0;\t\t\t// unqiue ID (starting at one)\n\tint\t\t\t\t\talloc_id = -1;\t// ID of allocator\n\tint\t\t\t\t\tm_status = FEModule::RELEASED;\t// Status of module\n\tstd::vector<int>\tdepMods;\t// module dependencies\n\npublic:\n\tvoid AddDependency(int mid)\n\t{\n\t\tfor (size_t i = 0; i < depMods.size(); ++i)\n\t\t{\n\t\t\tif (depMods[i] == mid) return;\n\t\t}\n\t\tdepMods.push_back(mid);\n\t}\n\n\tvoid AddDependencies(const std::vector<int>& mid)\n\t{\n\t\tfor (size_t i = 0; i < mid.size(); ++i)\n\t\t{\n\t\t\tAddDependency(mid[i]);\n\t\t}\n\t}\n};\n\nFEModule::FEModule() : im(new FEModule::Impl)\n{\n\n}\n\nFEModule::FEModule(const char* szname, const char* szdescription) : im(new FEModule::Impl)\n{\n\tSetName(szname);\n\tSetDescription(szdescription);\n}\n\nFEModule::~FEModule()\n{\n\tdelete im;\n}\n\nvoid FEModule::InitModel(FEModel* fem)\n{\n\n}\n\nint FEModule::GetModuleID() const\n{\n\treturn im->id;\n}\n\nvoid FEModule::SetAllocID(int id)\n{\n    im->alloc_id = id;\n}\n\nint FEModule::GetAllocID() const\n{\n    return im->alloc_id;\n}\n\nconst char* FEModule::GetName() const\n{\n\treturn im->szname;\n}\n\nconst char* FEModule::GetDescription() const\n{\n\treturn im->szdesc;\n}\n\nvoid FEModule::SetStatus(FEModule::Status status)\n{\n\tim->m_status = status;\n}\n\nint FEModule::GetStatus() const\n{\n\treturn im->m_status;\n}\n\nbool FEModule::HasDependent(int modId) const\n{\n\tif (modId == im->id) return true;\n\tfor (int i : im->depMods)\n\t{\n\t\tif (i == modId) return true;\n\t}\n\treturn false;\n}\n\nvoid FEModule::AddDependency(FEModule& mod)\n{\n\tim->AddDependency(mod.GetModuleID());\n\tim->AddDependencies(mod.im->depMods);\n}\n\nvoid FEModule::ClearDependencies()\n{\n\tim->depMods.clear();\n}\n\nstd::vector<int> FEModule::GetDependencies() const\n{\n\treturn im->depMods;\n}\n\nvoid FEModule::SetID(int newId)\n{\n\tim->id = newId;\n}\n\nvoid FEModule::SetName(const char* szname)\n{\n\tim->szname = szname;\n}\n\nvoid FEModule::SetDescription(const char* szdesc)\n{\n\tim->szdesc = szdesc;\n}\n"
  },
  {
    "path": "FECore/FEModule.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n\nclass FEModel;\nclass FECoreKernel;\n\n// The FEModule class defines the physics variables for a particular module\nclass FECORE_API FEModule\n{\n\tclass Impl;\n\npublic:\n\tenum Status {\n\t\tEXPERIMENTAL,\n\t\tRELEASED\n\t};\n\npublic:\n\tFEModule();\n\tFEModule(const char* szname, const char* szdescription = nullptr);\n\n\tvirtual ~FEModule();\n\n\t// this function must be overridden by derived classes\n\tvirtual void InitModel(FEModel* fem);\n\n\tvoid AddDependency(FEModule& mod);\n\n\tvoid ClearDependencies();\n\n\tstd::vector<int> GetDependencies() const;\n\n\tint GetModuleID() const;\n\n    void SetAllocID(int id);\n    int GetAllocID() const;\n\n\tconst char* GetName() const;\n\n\tconst char* GetDescription() const;\n\n\tint GetStatus() const;\n\n\tbool HasDependent(int modId) const;\n\nprotected:\n\tvoid SetStatus(FEModule::Status status);\n\nprivate:\n\tvoid SetID(int newId);\n\tvoid SetName(const char* szname);\n\tvoid SetDescription(const char* szdesc);\n\npublic:\n\tImpl* im;\n\n\tfriend class FECoreKernel;\n};\n"
  },
  {
    "path": "FECore/FENLConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENLConstraint.h\"\n\n//-----------------------------------------------------------------------------\nFENLConstraint::FENLConstraint(FEModel* pfem) : FEStepComponent(pfem)\n{\n\tstatic int ncount = 1;\n\tSetID(ncount++);\n}\n\n//-----------------------------------------------------------------------------\nFENLConstraint::~FENLConstraint()\n{\n}\n"
  },
  {
    "path": "FECore/FENLConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESolver.h\"\n#include \"FEStepComponent.h\"\n#include \"FEGlobalVector.h\"\n#include \"FEGlobalMatrix.h\"\n#include \"FETimeInfo.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n// forward declaration of the model class\nclass FEModel;\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\n//! Base class for nonlinear constraints enforced using an augmented Lagrangian method.\n\n//! The constraint must provide a residual (force) contribution, its stiffness matrix,\n//! and an augmentation function.\n//!\nclass FECORE_API FENLConstraint : public FEStepComponent\n{\n\tFECORE_SUPER_CLASS(FENLCONSTRAINT_ID)\n\tFECORE_BASE_CLASS(FENLConstraint);\n\npublic:\n\tFENLConstraint(FEModel* pfem);\n\tvirtual ~FENLConstraint();\n\n\t// clone the constraint\n\tvirtual void CopyFrom(FENLConstraint* plc) {}\n\n\t// initialize equations\n\t// Overridden by constraint classes that need to allocate more equations,\n\t// e.g. for Lagrange Multipliers.\n\tvirtual int InitEquations(int neq) { return 0; }\n\npublic:\n\t// The LoadVector function evaluates the \"forces\" that contribute to the residual of the system\n\tvirtual void LoadVector(FEGlobalVector& R, const FETimeInfo& tp) = 0;\n\n\t// Evaluates the contriubtion to the stiffness matrix\n\tvirtual void StiffnessMatrix(FELinearSystem& LS, const FETimeInfo& tp) = 0;\n\n\t// Performs an augmentation step\n\tvirtual bool Augment(int naug, const FETimeInfo& tp) { return true; }\n\n\t// Build the matrix profile\n\tvirtual void BuildMatrixProfile(FEGlobalMatrix& M) = 0;\n\n\t// reset the state data\n\tvirtual void Reset() {}\n\n\t// called at start of time step\n\tvirtual void PrepStep() {}\n\n\t// update \n\tusing FEModelComponent::Update;\n\n\t// update for Lagrange Multiplier implementations\n\tvirtual void Update(const std::vector<double>& Ui, const std::vector<double>& ui) {}\n\tvirtual void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) {}\n};\n"
  },
  {
    "path": "FECore/FENNQuery.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENNQuery.h\"\n#include \"FESurface.h\"\n#include <stdlib.h>\n#include \"FEMesh.h\"\nusing namespace std;\n\nint cmp_node(const void* e1, const void* e2)\n{\n\tFENNQuery::NODE& n1 = *((FENNQuery::NODE*)e1);\n\tFENNQuery::NODE& n2 = *((FENNQuery::NODE*)e2);\n\n\treturn (n1.d1 > n2.d1 ? 1 : -1);\n}\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFENNQuery::FENNQuery(FESurface* ps)\n{\n\tm_ps = ps;\n}\n\nFENNQuery::~FENNQuery()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FENNQuery::Init()\n{\n\tassert(m_ps);\n\n\tint i;\n\tvec3d r0, r;\n\tint N = m_ps->Nodes();\n\n\t// pick a random point as pivot\n\tr0 = m_q1 = m_ps->Node(0).m_rt;\n\n\t// find the farthest node of this node\n\tdouble dmax = 0, d;\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tr = m_ps->Node(i).m_rt;\n\t\td = (r - r0)*(r - r0);\n\t\tif (d > dmax)\n\t\t{\n\t\t\tm_q1 = r;\n\t\t\tdmax = d;\n\t\t}\n\t}\n\n\t// let's find the farthest node of this node\n\tr0 = m_q2 = m_q1;\n\tdmax = 0;\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tr = m_ps->Node(i).m_rt;\n\t\td = (r - r0)*(r - r0);\n\t\tif (d > dmax)\n\t\t{\n\t\t\tm_q2 = r;\n\t\t\tdmax = d;\n\t\t}\n\t}\n\n\t// create the BK-\"tree\"\n\tm_bk.resize(N);\n\tfor (i=0; i<N; ++i) \n\t{\n\t\tr = m_ps->Node(i).m_rt;\n\t\tm_bk[i].i = i;\n\t\tm_bk[i].r = r;\n\t\tm_bk[i].d1 = (m_q1 - r)*(m_q1 - r);\n\t\tm_bk[i].d2 = (m_q2 - r)*(m_q2 - r);\n\t}\n\n\t// sort the tree\n\tqsort(&m_bk[0], N, sizeof(NODE), cmp_node);\n\n\t// set the initial search item\n\tm_imin = 0;\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FENNQuery::InitReference()\n{\n\tassert(m_ps);\n\n\tint i;\n\tvec3d r0, r;\n\tint N = m_ps->Nodes();\n\n\t// pick a random point as pivot\n\tr0 = m_q1 = m_ps->Node(0).m_r0;\n\n\t// find the furtest node of this node\n\tdouble dmax = 0, d;\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tr = m_ps->Node(i).m_r0;\n\t\td = (r - r0)*(r - r0);\n\t\tif (d > dmax)\n\t\t{\n\t\t\tm_q1 = r;\n\t\t\tdmax = d;\n\t\t}\n\t}\n\n\t// let's find the furthest node of this node\n\tr0 = m_q2 = m_q1;\n\tdmax = 0;\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tr = m_ps->Node(i).m_r0;\n\t\td = (r - r0)*(r - r0);\n\t\tif (d > dmax)\n\t\t{\n\t\t\tm_q2 = r;\n\t\t\tdmax = d;\n\t\t}\n\t}\n\n\t// create the BK-\"tree\"\n\tm_bk.resize(N);\n\tfor (i=0; i<N; ++i) \n\t{\n\t\tr = m_ps->Node(i).m_r0;\n\t\tm_bk[i].i = i;\n\t\tm_bk[i].r = r;\n\t\tm_bk[i].d1 = (m_q1 - r)*(m_q1 - r);\n\t\tm_bk[i].d2 = (m_q2 - r)*(m_q2 - r);\n\t}\n\n\t// sort the tree\n\tqsort(&m_bk[0], N, sizeof(NODE), cmp_node);\n\n\t// set the initial search item\n\tm_imin = 0;\n}\n\n//-----------------------------------------------------------------------------\n\nint FENNQuery::Find(vec3d x)\n{\n\tint m_i0 = -1;\n\tdouble rmin1, rmin2, rmax1, rmax2;\n\tdouble rmin1s, rmin2s, rmax1s, rmax2s;\n\tdouble d, d1, d2, dmin;\n\tvec3d r;\n\n\t// set the initial search radii\n\td1 = sqrt((m_q1 - x)*(m_q1 - x)); \n\trmin1 = 0;\n\trmax1 = 2*d1;\n\n\td2 = sqrt((m_q2 - x)*(m_q2 - x));\n\trmin2 = 0;\n\trmax2 = 2*d2;\n\n\t// check the last found item\n\tint imin = m_imin;\n\tr = m_ps->Node(imin).m_rt;\n\tdmin = (r - x)*(r - x);\n\td = sqrt(dmin);\n\t\n\t// adjust search radii\n\tif (d1 - d > rmin1) rmin1 = d1 - d;\n\tif (d1 + d < rmax1) rmax1 = d1 + d;\n\trmin1s = rmin1*rmin1;\n\trmax1s = rmax1*rmax1;\n\n\tif (d2 - d > rmin2) rmin2 = d2 - d;\n\tif (d2 + d < rmax2) rmax2 = d2 + d;\n\trmin2s = rmin2*rmin2;\n\trmax2s = rmax2*rmax2;\n\n\t// find the first item that satisfies d(i, q1) >= rmin1\n\tint i0 = FindRadius(rmin1s);\n\n\tfor (int i=i0; i<(int) m_bk.size(); ++i)\n\t{\n\t\tNODE& n = m_bk[i];\n\t\tif (n.d1 <= rmax1s)\n\t\t{\n\t\t\tif ((n.d2 >= rmin2s) && (n.d2 <= rmax2s))\n\t\t\t{\n\t\t\t\tr = n.r;\n\t\t\t\td = (r - x)*(r - x);\n\t\t\t\tif (d < dmin)\n\t\t\t\t{\n\t\t\t\t\tdmin = d;\n\t\t\t\t\td = sqrt(dmin);\n\t\t\t\t\timin = n.i;\n\n//\t\t\t\t\tif (d1 - d > rmin1) rmin1 = d1 - d;\n\t\t\t\t\tif (d1 + d < rmax1) rmax1 = d1 + d;\n//\t\t\t\t\trmin1s = rmin1*rmin1;\n\t\t\t\t\trmax1s = rmax1*rmax1;\n\n\t\t\t\t\tif (d2 - d > rmin2) rmin2 = d2 - d;\n\t\t\t\t\tif (d2 + d < rmax2) rmax2 = d2 + d;\n\t\t\t\t\trmin2s = rmin2*rmin2;\n\t\t\t\t\trmax2s = rmax2*rmax2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse break;\n\t}\n\n/*\n\t// do it the hard way\n\tint imin = 0;\n\tr = m_ps->Node(imin).m_rt;\n\tdouble d0 = (r - x)*(r - x);\n\tfor (i=0; i<m_ps->Nodes(); ++i)\n\t{\n\t\tr = m_ps->Node(i).m_rt;\n\t\td = (r - x)*(r - x);\n\t\tif (d < d0)\n\t\t{\n\t\t\td0 = d;\n\t\t\timin = i;\n\t\t}\n\t}\n\tassert(imin == m_imin);\n*/\n\n\t#pragma omp critical\n\tm_imin = imin;\n\n\treturn imin;\n}\n\n//-----------------------------------------------------------------------------\n\nint FENNQuery::FindReference(vec3d x)\n{\n\tint m_i0 = -1;\n\tdouble rmin1, rmin2, rmax1, rmax2;\n\tdouble rmin1s, rmin2s, rmax1s, rmax2s;\n\tdouble d, d1, d2, dmin;\n\tvec3d r;\n\n\t// set the initial search radii\n\td1 = sqrt((m_q1 - x)*(m_q1 - x)); \n\trmin1 = 0;\n\trmax1 = 2*d1;\n\n\td2 = sqrt((m_q2 - x)*(m_q2 - x));\n\trmin2 = 0;\n\trmax2 = 2*d2;\n\n\t// check the last found item\n\tr = m_ps->Node(m_imin).m_r0;\n\tdmin = (r - x)*(r - x);\n\td = sqrt(dmin);\n\t\n\t// adjust search radii\n\tif (d1 - d > rmin1) rmin1 = d1 - d;\n\tif (d1 + d < rmax1) rmax1 = d1 + d;\n\trmin1s = rmin1*rmin1;\n\trmax1s = rmax1*rmax1;\n\n\tif (d2 - d > rmin2) rmin2 = d2 - d;\n\tif (d2 + d < rmax2) rmax2 = d2 + d;\n\trmin2s = rmin2*rmin2;\n\trmax2s = rmax2*rmax2;\n\n\t// find the first item that satisfies d(i, q1) >= rmin1\n\tint i0 = FindRadius(rmin1s);\n\n\tfor (int i=i0; i<(int) m_bk.size(); ++i)\n\t{\n\t\tNODE& n = m_bk[i];\n\t\tif (n.d1 <= rmax1s)\n\t\t{\n\t\t\tif ((n.d2 >= rmin2s) && (n.d2 <= rmax2s))\n\t\t\t{\n\t\t\t\tr = n.r;\n\t\t\t\td = (r - x)*(r - x);\n\t\t\t\tif (d < dmin)\n\t\t\t\t{\n\t\t\t\t\tdmin = d;\n\t\t\t\t\td = sqrt(dmin);\n\t\t\t\t\tm_imin = n.i;\n\n//\t\t\t\t\tif (d1 - d > rmin1) rmin1 = d1 - d;\n\t\t\t\t\tif (d1 + d < rmax1) rmax1 = d1 + d;\n//\t\t\t\t\trmin1s = rmin1*rmin1;\n\t\t\t\t\trmax1s = rmax1*rmax1;\n\n\t\t\t\t\tif (d2 - d > rmin2) rmin2 = d2 - d;\n\t\t\t\t\tif (d2 + d < rmax2) rmax2 = d2 + d;\n\t\t\t\t\trmin2s = rmin2*rmin2;\n\t\t\t\t\trmax2s = rmax2*rmax2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse break;\n\t}\n\n/*\n\t// do it the hard way\n\tint imin = 0;\n\tr = m_ps->Node(imin).m_r0;\n\tdouble d0 = (r - x)*(r - x);\n\tfor (int i=0; i<m_ps->Nodes(); ++i)\n\t{\n\t\tr = m_ps->Node(i).m_r0;\n\t\td = (r - x)*(r - x);\n\t\tif (d < d0)\n\t\t{\n\t\t\td0 = d;\n\t\t\timin = i;\n\t\t}\n\t}\n//\tassert(imin == m_imin);\n*/\n\treturn m_imin;\n}\n\n//-----------------------------------------------------------------------------\n\nint FENNQuery::FindRadius(double r)\n{\n\tint N = (int)m_bk.size();\n\tint L = N - 1;\n\tint i0 = 0;\n\tint i1 = L;\n\tif (m_bk[i1].d1 < r) return N;\n\tint i = i1 / 2;\n\tdo\n\t{\n\t\tif (m_bk[i].d1 < r)\n\t\t{\n\t\t\ti0 = i;\n\t\t\tif (m_bk[i+1].d1 >= r) { ++i; break; }\n\t\t}\n\t\telse\n\t\t{\n\t\t\ti1 = i;\n\t\t\tif ((i==0) || (m_bk[i-1].d1 < r)) break;\n\t\t}\n\t\ti = (i1 + i0) / 2;\n\t}\n\twhile(i0 != i1);\n\n\treturn i;\n}\n\n\n//-----------------------------------------------------------------------------\nint findNeirestNeighbors(const std::vector<vec3d>& point, const vec3d& x, int k, std::vector<int>& closestNodes)\n{\n\tint N0 = (int) point.size();\n\tif (N0 < k) k = N0;\n\n\tvector<double> dist(k, 0.0);\n\tclosestNodes.resize(k);\n\tint n = 0;\n\tfor (int i = 0; i < N0; ++i)\n\t{\n\t\tvec3d ri = point[i] - x;\n\t\tdouble L2 = ri*ri;\n\n\t\tif (n == 0)\n\t\t{\n\t\t\tclosestNodes[0] = i;\n\t\t\tdist[0] = L2;\n\t\t\tn++;\n\t\t}\n\t\telse if (L2 <= dist[n - 1])\n\t\t{\n\t\t\tint m;\n\t\t\tfor (m = 0; m < n; ++m)\n\t\t\t{\n\t\t\t\tif (L2 <= dist[m])\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (n < k) n++;\n\t\t\tfor (int l = n - 1; l > m; l--)\n\t\t\t{\n\t\t\t\tclosestNodes[l] = closestNodes[l - 1];\n\t\t\t\tdist[l] = dist[l - 1];\n\t\t\t}\n\n\t\t\tclosestNodes[m] = i;\n\t\t\tdist[m] = L2;\n\t\t}\n\t\telse if (n < k)\n\t\t{\n\t\t\tclosestNodes[n] = i;\n\t\t\tdist[n] = L2;\n\t\t\tn++;\n\t\t}\n\t}\n\n\treturn n;\n}\n"
  },
  {
    "path": "FECore/FENNQuery.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include <vector>\n#include \"fecore_api.h\"\n\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n//! This class is a helper class to locate the nearest neighbour on a surface\n\nclass FECORE_API FENNQuery\n{\npublic:\n\tstruct NODE\n\t{\n\t\tint\t\ti;\t// index of node\n\t\tvec3d\tr;\t// position of node\n\t\tdouble\td1;\t// distance to pivot 1\n\t\tdouble\td2;\t// distance to pivot 2\n\t};\n\npublic:\n\tFENNQuery(FESurface* ps = 0);\n\tvirtual ~FENNQuery();\n\n\t//! initialize search structures\n\tvoid Init();\n\tvoid InitReference();\n\n\t//! attach to a surface\n\tvoid Attach(FESurface* ps) { m_ps = ps; }\n\n\t//! find the neirest neighbour of r\n\tint Find(vec3d x);\t\n\tint FindReference(vec3d x);\t\n\nprotected:\n\tint FindRadius(double r);\n\nprotected:\n\tFESurface*\tm_ps;\t//!< the surface to search\n\tstd::vector<NODE>\tm_bk;\t// BK tree\n\n\tvec3d\tm_q1;\t// pivot 1\n\tvec3d\tm_q2;\t// pivot 2\n\n\tint\t\tm_imin;\t// last found index\n};\n\n// function for finding the k closest neighbors\nint FECORE_API findNeirestNeighbors(const std::vector<vec3d>& point, const vec3d& x, int k, std::vector<int>& closestNodes);\n"
  },
  {
    "path": "FECore/FENewtonSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonSolver.h\"\n#include \"FEModel.h\"\n#include \"FEGlobalMatrix.h\"\n#include \"LinearSolver.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"FEAnalysis.h\"\n#include \"FEPrescribedDOF.h\"\n#include \"log.h\"\n#include \"sys.h\"\n#include \"FEDomain.h\"\n#include \"DumpStream.h\"\n#include \"FELinearSystem.h\"\n\n//-----------------------------------------------------------------------------\n// define the parameter list\nBEGIN_FECORE_CLASS(FENewtonSolver, FESolver)\n\tBEGIN_PARAM_GROUP(\"line search\");\n\t\tADD_PARAMETER(m_lineSearch->m_LStol , FE_RANGE_GREATER_OR_EQUAL(0.0), \"lstol\"   );\n\t\tADD_PARAMETER(m_lineSearch->m_LSmin , FE_RANGE_GREATER_OR_EQUAL(0.0), \"lsmin\"   );\n\t\tADD_PARAMETER(m_lineSearch->m_LSiter, FE_RANGE_GREATER_OR_EQUAL(0), \"lsiter\"  );\n\t\tADD_PARAMETER(m_lineSearch->m_checkJacobians, \"ls_check_jacobians\");\n\tEND_PARAM_GROUP();\n\n\tBEGIN_PARAM_GROUP(\"Nonlinear solver\");\n\t\tADD_PARAMETER(m_maxref              , FE_RANGE_GREATER_OR_EQUAL(0.0), \"max_refs\");\n\t\tADD_PARAMETER(m_bzero_diagonal      , \"check_zero_diagonal\");\n\t\tADD_PARAMETER(m_zero_tol            , \"zero_diagonal_tol\"  );\n\t\tADD_PARAMETER(m_force_partition     , \"force_partition\");\n\t\tADD_PARAMETER(m_breformtimestep     , \"reform_each_time_step\");\n\t\tADD_PARAMETER(m_breformAugment      , \"reform_augment\");\n\t\tADD_PARAMETER(m_bdivreform          , \"diverge_reform\");\n//\t\tADD_PARAMETER(m_bdoreforms          , \"do_reforms\"  );\n\t\tADD_PARAMETER(m_Rmin, FE_RANGE_GREATER_OR_EQUAL(0.0), \"min_residual\");\n\t\tADD_PARAMETER(m_Rmax, FE_RANGE_GREATER_OR_EQUAL(0.0), \"max_residual\");\n\tEND_PARAM_GROUP();\n\n\tADD_PROPERTY(m_qnstrategy, \"qn_method\", FEProperty::Preferred)->SetDefaultType(\"BFGS\").SetLongName(\"Quasi-Newton method\");\n\tADD_PROPERTY(m_plinsolve, \"linear_solver\", FEProperty::Optional)->SetDefaultType(\"pardiso\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENewtonSolver::FENewtonSolver(FEModel* pfem) : FESolver(pfem)\n{\n\tm_lineSearch = new FELineSearch(this);\n\tm_ls = 0.0;\n\n\t// default parameters\n\tm_maxref = 15;\n\n\tm_nref = 0;\n\n    m_neq = 0;\n    m_plinsolve = 0;\n\tm_pK = 0;\n\n\tm_Rtol = 0.001;\n\tm_Etol = 0.01;\n\tm_Rmin = 1.0e-20;\n\tm_Rmax = 0;     // not used if zero\n\n\tm_qnstrategy = nullptr;\n\n\tm_bforceReform = true;\n\tm_bdivreform = true;\n\tm_bdoreforms = true;\n\tm_persistMatrix = true;\n\n\tm_bzero_diagonal = false;\n\tm_zero_tol = 0.0;\n\n\tm_force_partition = 0;\n\tm_breformtimestep = true;\n\tm_breformAugment = false;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the default solution strategy\nvoid FENewtonSolver::SetDefaultStrategy(QN_STRATEGY qn)\n{\n\tGetParameterList(); // This needs to be called to ensure that the parameter and property lists have been setup.\n\n\tFEProperty& p = *FindProperty(\"qn_method\");\n\tswitch (qn)\n\t{\n\tcase QN_BFGS   : p.SetDefaultType(\"BFGS\"); break;\n\tcase QN_BROYDEN: p.SetDefaultType(\"Broyden\"); break;\n\tcase QN_JFNK   : p.SetDefaultType(\"JFNK\"); break;\n\tdefault:\n\t\tassert(false);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonSolver::SetSolutionStrategy(FENewtonStrategy* pstrategy)\n{\n\tif (m_qnstrategy) delete m_qnstrategy;\n\tm_qnstrategy = pstrategy;\n\tm_qnstrategy->SetNewtonSolver(this);\n}\n\n//-----------------------------------------------------------------------------\nFENewtonSolver::~FENewtonSolver()\n{\n\tif (m_plinsolve) delete m_plinsolve;\n\tm_plinsolve = nullptr;\n\tClean();\n}\n\n//-----------------------------------------------------------------------------\n//! Add a degree of freedom list for convergence\nvoid FENewtonSolver::AddSolutionVariable(FEDofList* dofs, int order, const char* szname, double tol)\n{\n\t// Add a variable\n\tFESolutionVariable var(szname, dofs, order);\n\tm_Var.push_back(var);\n\n\t// Set convergence info on variable\n\tConvergenceInfo cinfo;\n\tcinfo.nvar = (int) m_Var.size() - 1;\n\tcinfo.tol = tol;\n\tm_solutionNorm.push_back(cinfo);\n}\n\n//-----------------------------------------------------------------------------\n// return line search\nFELineSearch* FENewtonSolver::GetLineSearch()\n{\n\treturn m_lineSearch;\n}\n\n//-----------------------------------------------------------------------------\nFEGlobalMatrix* FENewtonSolver::GetStiffnessMatrix()\n{\n\treturn m_pK;\n}\n\n//-----------------------------------------------------------------------------\n//! Check the zero diagonal\nvoid FENewtonSolver::CheckZeroDiagonal(bool bcheck, double ztol)\n{\n\tm_bzero_diagonal = bcheck;\n\tm_zero_tol = fabs(ztol);\n}\n\n//-----------------------------------------------------------------------------\n//! Reforms a stiffness matrix and factorizes it\nbool FENewtonSolver::ReformStiffness()\n{\n\tfeLog(\"Reforming stiffness matrix: reformation #%d\\n\\n\", m_nref + 1);\n\n    // first, let's make sure we have not reached the max nr of reformations allowed\n    if (m_nref >= m_maxref) throw MaxStiffnessReformations();\n\n\tFEModel& fem = *GetFEModel();\n\n    // recalculate the shape of the stiffness matrix if necessary\n    if (m_breshape)\n    {\n        // reshape the stiffness matrix\n        if (!CreateStiffness(m_niter == 0)) return false;\n        \n        // reset reshape flag, except for contact\n\t\tm_breshape = (((fem.SurfacePairConstraints() > 0) || (fem.NonlinearConstraints() > 0)) ? true : false);\n    }\n    \n    // calculate the global stiffness matrix\n\tbool bret = false;\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Stiffness);\n\n\t\t// zero the stiffness matrix\n\t\tm_pK->Zero();\n\n\t\t// Zero the rhs adjustment vector\n\t\tzero(m_Fd);\n\n\t\t// calculate the global stiffness matrix\n\t    bret = StiffnessMatrix();\n\n\t\t// check for zero diagonals\n\t\tif (m_bzero_diagonal)\n\t\t{\n\t\t\t// get the stiffness matrix\n\t\t\tSparseMatrix& K = *m_pK;\n\t\t\tvector<int> zd;\n\t\t\tint neq = K.Rows();\n\t\t\tfor (int i=0; i<neq; ++i)\n\t\t\t{\n\t\t\t\tdouble di = fabs(K.diag(i));\n\t\t\t\tif (di <= m_zero_tol)\n\t\t\t\t{\n\t\t\t\t\tzd.push_back(i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (zd.empty() == false) throw ZeroDiagonal(-1, -1);\n\t\t}\n\t}\n\n\t// if the stiffness matrix was evaluated successfully,\n\t// we factor it.\n    if (bret)\n    {\n        {\n\t\t\tTRACK_TIME(TimerID::Timer_LinSol_Factor);\n\t\t\t// factorize the stiffness matrix\n\t\t\tif (m_plinsolve->Factor() == false)\n\t\t\t{\n\t\t\t\tthrow FactorizationError();\n\t\t\t}\n        }\n\n        // increase total nr of reformations\n        m_nref++;\n        m_ntotref++;\n        \n        // reset bfgs update counter\n\t\tm_qnstrategy->m_nups = 0;\n    }\n    \n    return bret;\n}\n\n//-----------------------------------------------------------------------------\n//! get the RHS\nstd::vector<double> FENewtonSolver::GetLoadVector()\n{\n\treturn m_R0;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the total solution vector (for current Newton iteration)\nvoid FENewtonSolver::GetSolutionVector(std::vector<double>& U)\n{\n\tU = m_Ui + m_ui;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the total solution vector\nstd::vector<double> FENewtonSolver::GetSolutionVector() const\n{\n\treturn m_Ut;\n}\n\n//-----------------------------------------------------------------------------\n//!  Creates the global stiffness matrix\n//! \\todo Can we move this to the FEGlobalMatrix::Create function?\nbool FENewtonSolver::CreateStiffness(bool breset)\n{\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Reform);\n\t\t// clean up the solver\n\t\tm_plinsolve->Destroy();\n\n\t\t// clean up the stiffness matrix\n\t\tm_pK->Clear();\n\n\t\t// create the stiffness matrix\n\t\tfeLog(\"===== reforming stiffness matrix:\\n\");\n\t\tif (m_pK->Create(GetFEModel(), m_neq, breset) == false)\n\t\t{\n\t\t\tfeLogError(\"An error occured while building the stiffness matrix\\n\\n\");\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// output some information about the direct linear solver\n\t\t\tint neq = m_pK->Rows();\n\t\t\tint nnz = m_pK->NonZeroes();\n\t\t\tfeLog(\"\\tNr of equations ........................... : %d\\n\", neq);\n\t\t\tfeLog(\"\\tNr of nonzeroes in stiffness matrix ....... : %d\\n\", nnz);\n\n\t\t\tint parts = m_plinsolve->Partitions();\n\t\t\tif (parts > 1)\n\t\t\t{\n\t\t\t\tfeLog(\"\\tNr of partitions .......................... : %d\\n\", parts);\n\t\t\t\tfor (int i = 0; i < parts; ++i)\n\t\t\t\t{\n\t\t\t\t\tfeLog(\"\\t\\tpartition %d ............................ : %d\\n\", i+1, m_plinsolve->GetPartitionSize(i));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Do the preprocessing of the solver\n\t{\n\t\tif (!m_plinsolve->PreProcess())\n\t\t{\n\t\t\tfeLogError(\"An error occurred during preprocessing of linear solver\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// done!\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! return the linear solver\nLinearSolver* FENewtonSolver::GetLinearSolver()\n{\n\treturn m_plinsolve;\n}\n\n//-----------------------------------------------------------------------------\nbool FENewtonSolver::AllocateLinearSystem()\n{\n\t// Now that we have determined the equation numbers we can continue\n\t// with creating the stiffness matrix. First we select the linear solver\n\t// The stiffness matrix is created in CreateStiffness\n\t// Note that if a particular solver was requested in the input file\n\t// then the solver might already be allocated. That's way we need to check it.\n\tif (m_plinsolve == 0)\n\t{\n\t\tFEModel* fem = GetFEModel();\n\t\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\t\tm_plinsolve = fecore.CreateDefaultLinearSolver(fem);\n\t\tif (m_plinsolve == 0)\n\t\t{\n\t\t\tfeLogError(\"Unknown solver type selected\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (m_part.empty() || (m_part.size() == 1))\n\t{\n\t\t// Set the partitioning of the global matrix\n\t\t// This is only used for debugging block solvers for problems that\n\t\t// usually don't generate a block structure\n\t\tif (m_force_partition > 0)\n\t\t{\n\t\t\tm_part.resize(2);\n\t\t\tm_part[0] = m_force_partition;\n\t\t\tm_part[1] = m_neq - m_force_partition;\n\t\t}\n\t}\n\n\tif (m_part.empty() == false)\n\t{\n\t\tm_plinsolve->SetPartitions(m_part);\n\t}\n\n\tfeLogInfo(\"Selecting linear solver %s\", m_plinsolve->GetTypeStr());\n\n\tMatrix_Type mtype = MatrixType();\n\tSparseMatrix* pS = m_qnstrategy->CreateSparseMatrix(mtype);\n\tif ((pS == 0) && (m_msymm == REAL_SYMMETRIC))\n\t{\n\t\t// oh, oh, something went wrong. It's probably because the user requested a symmetric matrix for a \n\t\t// solver that wants a non-symmetric. If so, let's force a non-symmetric format.\n\t\tpS = m_qnstrategy->CreateSparseMatrix(REAL_UNSYMMETRIC);\n\n\t\tif (pS)\n\t\t{\n\t\t\t// Problem solved! Let's inform the user.\n\t\t\tm_msymm = REAL_UNSYMMETRIC;\n\t\t\tfeLogWarning(\"The matrix format was changed to non-symmetric since the selected linear solver does not support a symmetric format.\");\n\t\t}\n\t}\n\n\t// if the sparse matrix is still zero, we have a problem\n\tif (pS == 0)\n\t{\n\t\tfeLogError(\"The selected linear solver does not support the requested matrix format.\\nPlease select a different linear solver.\");\n\t\treturn false;\n\t}\n\n\t// clean up the stiffness matrix if we have one\n\tif (m_pK) delete m_pK; m_pK = 0;\n\n\t// Create the stiffness matrix.\n\t// Note that this does not construct the stiffness matrix. This\n\t// is done later in the CreateStiffness routine.\n\tm_pK = new FEGlobalMatrix(pS);\n\tif (m_pK == 0)\n\t{\n\t\tfeLogError(\"Failed allocating stiffness matrix.\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FENewtonSolver::Init()\n{\n\t// choose a solution strategy\n\tif (m_qnstrategy == nullptr)\n\t{\n\t\tm_qnstrategy = fecore_new<FENewtonStrategy>(\"BFGS\", GetFEModel());\n\t\tassert(m_qnstrategy);\n\t\tif (m_qnstrategy == nullptr) return false;\n\t}\n\n\t// make sure the QN strategy knows what solver it belongs to\n\tm_qnstrategy->SetNewtonSolver(this);\n\n\t// allocate data vectors\n\tm_R0.assign(m_neq, 0);\n\tm_R1.assign(m_neq, 0);\n\tm_ui.assign(m_neq, 0);\n\tm_Ui.assign(m_neq, 0);\n\tm_Ut.assign(m_neq, 0);\n\tm_Fd.assign(m_neq, 0);\n\n\t// allocate storage for the sparse matrix that will hold the stiffness matrix data\n\t// we let the linear solver allocate the correct type of matrix format\n\tif (AllocateLinearSystem() == false) return false;\n\n\t// Base class initialization and validation\n\tif (FESolver::Init() == false) return false;\n\n\t// set the create stiffness matrix flag\n\tm_breshape = true;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Clean\nvoid FENewtonSolver::Clean()\n{\n\tif (m_pK) delete m_pK; m_pK = nullptr;\n\tif (m_qnstrategy) m_qnstrategy->Reset();\n\tm_Var.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonSolver::Serialize(DumpStream& ar)\n{\n\tFESolver::Serialize(ar);\n\tif (m_lineSearch) m_lineSearch->Serialize(ar);\n\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_neq;\n\t\tar << m_maxref;\n//\t\tar << m_qndefault;\n\t\tar << m_qnstrategy;\n\t}\n\telse\n\t{\n\t\tar >> m_neq;\n\t\tar >> m_maxref;\n//\t\tar >> m_qndefault;\n\t\tar >> m_qnstrategy;\n\n\t\t// realloc data\n\t\tif (m_neq > 0 && m_qnstrategy)\n\t\t{\n\t\t\tm_R0.assign(m_neq, 0);\n\t\t\tm_R1.assign(m_neq, 0);\n\t\t\tm_ui.assign(m_neq, 0);\n\t\t\tm_Fd.assign(m_neq, 0);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//!  This function mainly calls the Quasin routine \n//!  and deals with exceptions that require the immediate termination of\n//!\tquasi-Newton iterations.\nbool FENewtonSolver::SolveStep()\n{\n\tbool bret;\n\n\t// initialize counters\n\tm_niter = 0;\t// nr of iterations\n\tm_nrhs = 0;\t\t// nr of RHS evaluations\n\tm_nref = 0;\t\t// nr of stiffness reformations\n\tm_ntotref = 0;\n\tm_naug = 0;\t\t// nr of augmentations\n\n\ttry\n\t{\n\t\t// let's try to call Quasin\n\t\tbret = Quasin();\n\t}\n\tcatch (FEException e)\n\t{\n\t\tif (e.level() == FEException::Error)\n\t\t\tfeLogError(\"%s\", e.what());\n\t\telse\n\t\t\tfeLogWarning(\"%s\", e.what());\n\t\treturn false;\n\t}\n\n\tif (bret)\n\t{\n\t\t// print a convergence summary to the felog file\n\t\tfeLog(\"\\nconvergence summary\\n\");\n\t\tfeLog(\"    number of iterations   : %d\\n\", m_niter);\n\t\tfeLog(\"    number of reformations : %d\\n\", m_nref);\n\t}\n\n\t// if we don't want to hold on to the stiffness matrix, let's clean it up\n\tif (m_persistMatrix == false)\n\t{\n\t\t// clean up the solver\n\t\tm_plinsolve->Destroy();\n\n\t\t// clean up the stiffness matrix\n\t\tm_pK->Clear();\n\n\t\t// make sure we recreate it in the next time step\n\t\tm_breshape = true;\n\t}\n\n\treturn bret;\n}\n\n//-----------------------------------------------------------------------------\nbool FENewtonSolver::Quasin()\n{\n\tFEModel& fem = *GetFEModel();\n\tFETimeInfo& tp = fem.GetTime();\n\n\t// initialize counters\n\tm_niter = 0;\t\t// nr of iterations\n\tm_nrhs = 0;\t\t\t// nr of RHS evaluations\n\tm_nref = 0;\t\t\t// nr of stiffness reformations\n\tm_ntotref = 0;\n\n\t// TODO: Maybe call FEQuasiNewtonStrategy::Init or something?\n\tm_qnstrategy->m_nups = 0;\t// nr of stiffness updates between reformations\n\n\t// do the presolve update\n\tPrepStep();\n\n\t// Initialize QN method\n\tQNInit();\n\n\t// Start the quasi-Newton loop\n\tbool bconv = false;\n\tdo\n\t{\n\t\tfeLog(\" %d\\n\", m_niter + 1);\n\n\t\t// solve the equations (returns line search; solution stored in m_ui)\n\t\tdouble ls = QNSolve();\n\n\t\t// update solution vector\n\t\tfor (int i = 0; i<m_neq; ++i) m_Ui[i] += ls*m_ui[i];\n\n\t\tfeLog(\" Nonlinear solution status: time= %lg\\n\", tp.currentTime);\n\t\tfeLog(\"\\tstiffness updates             = %d\\n\", m_qnstrategy->m_nups);\n\t\tfeLog(\"\\tright hand side evaluations   = %d\\n\", m_nrhs);\n\t\tfeLog(\"\\tstiffness matrix reformations = %d\\n\", m_nref);\n\t\tif (m_lineSearch->m_LStol > 0) feLog(\"\\tstep from line search         = %lf\\n\", ls);\n\n\t\t// check convergence\n\t\tbconv = CheckConvergence(m_niter, m_ui, ls);\n\n\t\t// if we did not converge, do QN update\n\t\tif (bconv == false)\n\t\t{\n\t\t\tif (ls < m_lineSearch->m_LSmin)\n\t\t\t{\n\t\t\t\t// check for zero linestep size\n\t\t\t\tfeLogWarning(\"Zero linestep size. Stiffness matrix will now be reformed\");\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\t\t\telse if ((m_energyNorm.norm > m_energyNorm.maxnorm) && m_bdivreform)\n\t\t\t{\n\t\t\t\t// check for diverging\n\t\t\t\tfeLogWarning(\"Problem is diverging. Stiffness matrix will now be reformed\");\n\t\t\t\tm_energyNorm.maxnorm = m_energyNorm.norm;\n\t\t\t\tm_energyNorm.norm0 = m_energyNorm.norm;\n\t\t\t\tm_residuNorm.norm0 = m_residuNorm.norm;\n\t\t\t\tfor (int i = 0; i < m_solutionNorm.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tConvergenceInfo& ci = m_solutionNorm[i];\n\t\t\t\t\tci.norm0 = ci.normi;\n\t\t\t\t}\n\t\t\t\tQNForceReform(true);\n\t\t\t}\n\n\t\t\t// do the QN update (this may also do a stiffness reformation if necessary)\n\t\t\tbool bret = QNUpdate();\n\n\t\t\t// Oh, oh, something went wrong\n\t\t\tif (bret == false) break;\n\t\t}\n\t\telse if (m_baugment)\n\t\t{\n\t\t\t// do augmentations\n\t\t\tbconv = DoAugmentations();\n\t\t}\n\n\t\t// increase iteration number\n\t\tm_niter++;\n\n\t\t// do minor iterations callbacks\n\t\tfem.DoCallback(CB_MINOR_ITERS);\n\t}\n\twhile (!bconv);\n\n\t// if converged we update the total solution\n\tif (bconv)\n\t{\n\t\tm_Ut += m_Ui;\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nbool FENewtonSolver::CheckConvergence(int niter, const vector<double>& ui, double ls)\n{\n\tint vars = (int)m_solutionNorm.size();\n\n\t// If this is the first iteration, calculate initial convergence norms\n\tif (niter == 0)\n\t{\n\t\t// initial residual norm\n\t\tm_residuNorm.norm0 = m_R0*m_R0;\n\n\t\t// initial energy norm\n\t\tm_energyNorm.norm0 = fabs(m_ui*m_R0);\t// why is line search not taken into account here?\n\t\tm_energyNorm.maxnorm = m_energyNorm.norm0;\n\n\t\t// calculate initial solution norms\n\t\tfor (int i = 0; i < vars; ++i)\n\t\t{\n\t\t\tConvergenceInfo& c = m_solutionNorm[i];\n\t\t\tFESolutionVariable& var = m_Var[c.nvar];\n\t\t\tdouble d2 = ExtractSolutionNorm(ui, *var.m_dofs);\n\t\t\tc.norm0 = d2;\t// why no linesearch?\n\t\t}\n\t}\n\n\t// calculate current norms\n\tm_residuNorm.norm = m_R1*m_R1;\n\tm_energyNorm.norm = fabs(ls*(ui*m_R1));\n\tfor (int i = 0; i < vars; ++i)\n\t{\n\t\tConvergenceInfo& c = m_solutionNorm[i];\n\t\tFESolutionVariable& var = m_Var[c.nvar];\n\t\tdouble d2 = ExtractSolutionNorm(ui, *var.m_dofs);\n\t\tdouble D2 = ExtractSolutionNorm(m_Ui, *var.m_dofs);\n\t\tc.normi = (d2)*(ls*ls);\n\t\tc.norm = D2;\n\t}\n\n\t// check convergence\n\tbool bconv = true;\n\tif ((m_Rtol > 0) && (m_residuNorm.norm > m_Rtol*m_residuNorm.norm0)) bconv = false;\n\tif ((m_Etol > 0) && (m_energyNorm.norm > m_Etol*m_energyNorm.norm0)) bconv = false;\n\tfor (int i = 0; i < vars; ++i)\n\t{\n\t\tConvergenceInfo& c = m_solutionNorm[i];\n\t\tif (c.IsConverged() == false) bconv = false;\n\t}\n\n\t// print convergence information\n\tfeLog(\"\\tconvergence norms :     INITIAL         CURRENT         REQUIRED\\n\");\n\tfeLog(\"\\t   residual         %15le %15le %15le \\n\", m_residuNorm.norm0, m_residuNorm.norm, m_Rtol*m_residuNorm.norm0);\n\tfeLog(\"\\t   energy           %15le %15le %15le \\n\", m_energyNorm.norm0, m_energyNorm.norm, m_Etol*m_energyNorm.norm0);\n\tfor (int i = 0; i < vars; ++i)\n\t{\n\t\tConvergenceInfo& c = m_solutionNorm[i];\n\t\tFESolutionVariable& var = m_Var[c.nvar];\n\t\tconst char* varName = var.m_szname;\n\n\t\t// print the values of this field variable only\n\t\tfeLog(\"\\t   %-17s%15le %15le %15le \\n\", varName, c.norm0, c.normi, (c.tol*c.tol)*c.norm);\n\t}\n\n\t// see if we may have a small residual\n\tif ((bconv == false) && (m_residuNorm.norm < m_Rmin))\n\t{\n\t\t// check for almost zero-residual on the first iteration\n\t\t// this might be an indication that there is no load on the system\n\t\tfeLogWarning(\"No load acting on the system.\");\n\t\tbconv = true;\n\t}\n\n\t// see if we have exceeded the max residual\n\tif ((bconv == false) && (m_Rmax > 0) && (m_residuNorm.norm >= m_Rmax))\n\t{\n\t\t// doesn't look like we're getting anywhere, so let's retry the time step\n\t\tthrow MaxResidualError();\n\t}\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Solve the linear system of equations.\n//! x is the solution vector\n//! R is the right-hand-side vector\nvoid FENewtonSolver::SolveLinearSystem(vector<double>& x, vector<double>& R)\n{\n\t// solve the equations\n\tif (m_plinsolve->BackSolve(x, R) == false)\n\t\tthrow LinearSolverFailed();\n}\n\n//-----------------------------------------------------------------------------\n//! rewind solver\n//! This is called when the time step failed.\nvoid FENewtonSolver::Rewind()\n{\n\t// reset the forceReform flag so that we reform the stiffness matrix\n\tm_bforceReform = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonSolver::PrepStep()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// zero total DOFs\n\tzero(m_Ui);\n\n\t// apply prescribed velocities\n\t// we save the prescribed velocity increments in the ui vector\n\tvector<double>& ui = m_ui;\n\tzero(ui);\n\tint nbc = fem.BoundaryConditions();\n\tfor (int i = 0; i<nbc; ++i)\n\t{\n\t\tFEBoundaryCondition& dc = *fem.BoundaryCondition(i);\n\t\tif (dc.IsActive()) dc.PrepStep(ui);\n\t}\n\n\t// intialize material point data\n\t// NOTE: do this before the model is updated\n\t// TODO: does it matter if the stresses are updated before\n\t//       the material point data is initialized\n\t// update domain data\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i<mesh.Domains(); ++i) mesh.Domain(i).PreSolveUpdate(tp);\n\n\t// update model\n\tfem.Update();\n}\n\n//-----------------------------------------------------------------------------\n//! call this at the start of the quasi-newton loop (after PrepStep)\nbool FENewtonSolver::QNInit()\n{\n\t// see if we reform at the start of every time step\n\tbool breform = (m_breformtimestep || (m_qnstrategy->m_maxups == 0));\n\n\t// if the force reform flag was set, we force a reform\n\t// (This will be the case for the first time this is called, or when the previous time step failed)\n\tif (m_bforceReform)\n\t{\n\t\tbreform = true;\n\n\t\tm_bforceReform = false;\n\t}\n\n\tm_qnstrategy->PreSolveUpdate();\n\n\t// do the reform\n\t// NOTE: It is important for JFNK that the matrix is reformed before the \n\t//       residual is evaluated, so do not switch these two calculations!\n\tif (breform)\n\t{\n\t\t// do the first stiffness formation\n\t\tif (m_qnstrategy->ReformStiffness() == false) return false;\n\t}\n\n\t// calculate initial residual\n\tif (m_qnstrategy->Residual(m_R0, true) == false) return false;\n\n\t// add the contribution from prescribed dofs\n\tm_R0 += m_Fd;\n\n\t// TODO: I can check here if the residual is zero.\n\t// If it is than there is probably no force acting on the system\n\t// if (m_R0*m_R0 < eps) bconv = true;\n\n\t//\tdouble r0 = m_R0*m_R0;\n\n\t// do callback (we do it here since we want the RHS to be formed as well)\n\tGetFEModel()->DoCallback(CB_MATRIX_REFORM);\n\n\treturn true;\n}\t\n\n//-----------------------------------------------------------------------------\n//! solve the equations\nvoid FENewtonSolver::SolveEquations(std::vector<double>& u, std::vector<double>& R)\n{\n\tif (m_plinsolve->IsIterative())\n\t{\n\t\t// for the first iteration, we pass the solution of the last time step\n\t\tif (m_niter == 0)\n\t\t\tu = m_Ui;\n\t\telse\n\t\t\tu = m_up;\n\t}\n\telse zero(u);\n\n\tGetFEModel()->DoCallback(CB_PRE_MATRIX_SOLVE);\n\n\t// check for nans in the residual\n\tdouble r2 = R * R;\n\tif (ISNAN(r2))\n\t{\n\t\tFENodalDofInfo info;\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tfor (int i = 0; i < u.size(); ++i)\n\t\t{\n\t\t\tif (ISNAN(R[i]))\n\t\t\t{\n\t\t\t\tinfo = GetDOFInfoFromEquation(i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthrow NANInResidualDetected(info);\n\t}\n\n\t// call the qn strategy to actually solve the equations\n\t{\n\t\tTRACK_TIME(TimerID::Timer_LinSol_Backsolve);\n\t\tm_qnstrategy->SolveEquations(u, R);\n\t}\n\n\t// check for nans\n\tdouble u2 = u*u;\n\tif (ISNAN(u2))\n\t{\n\t\tFENodalDofInfo info;\n\t\tFEMesh& mesh = GetFEModel()->GetMesh();\n\t\tfor (int i = 0; i < u.size(); ++i)\n\t\t{\n\t\t\tif (ISNAN(u[i]))\n\t\t\t{\n\t\t\t\tinfo = GetDOFInfoFromEquation(i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthrow (NANInSolutionDetected(info));\n\t}\n\n\t// store the last solution for iterative solvers\n\tif (m_plinsolve->IsIterative()) m_up = u;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENewtonSolver::DoLineSearch()\n{\n\t// the geometry is also updated in the line search\n\tm_ls = 1.0;\n\tif (m_lineSearch && (m_lineSearch->m_LStol > 0.0)) m_ls = m_lineSearch->DoLineSearch();\n\telse\n\t{\n\t\t// Update geometry\n\t\tUpdate(m_ui);\n\n\t\t// calculate residual at this point\n\t\tm_qnstrategy->Residual(m_R1, false);\n\t}\n\n\t// return line search\n\treturn m_ls;\n}\n\n//-----------------------------------------------------------------------------\ndouble FENewtonSolver::QNSolve()\n{\n\t// solve linear system of equations\n\tSolveEquations(m_ui, m_R0);\n\n\t// perform a linesearch\n\treturn DoLineSearch();\n}\n\n//-----------------------------------------------------------------------------\nvoid FENewtonSolver::QNForceReform(bool b)\n{\n\tm_bforceReform = b;\n}\n\n//-----------------------------------------------------------------------------\n//! Do a QN update\nbool FENewtonSolver::QNUpdate()\n{\n\t// see if the force reform flag was set\n\tbool breform = m_bforceReform; m_bforceReform = false;\n\n\t// do a QN update\n\tif (breform == false)\n\t{\n\t\tTRACK_TIME(TimerID::Timer_QNUpdate);\n\t\tif (m_qnstrategy->Update(m_ls, m_ui, m_R0, m_R1) == false)\n\t\t{\n\t\t\t// the qn update failed. Force matrix reformations\n\t\t\tbreform = true;\n\t\t}\n\t}\n\n\t// zero displacement increments\n\t// we must set this to zero before the reformation\n\t// because we assume that the prescribed displacements are stored \n\t// in the m_ui vector.\n\tzero(m_ui);\n\n\t// reform stiffness matrices if necessary\n\tif (breform && m_bdoreforms)\n\t{\n\t\t// reform the matrix\n\t\tif (m_qnstrategy->ReformStiffness() == false) return false;\n\t}\n\n\t// copy last calculated residual\n\tm_R0 = m_R1;\n\n\tif (breform && m_bdoreforms) GetFEModel()->DoCallback(CB_MATRIX_REFORM);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FENewtonSolver::DoAugmentations()\n{\n\tFEModel& fem = *GetFEModel();\n\tFEAnalysis* pstep = fem.GetCurrentStep();\n\n\t// we have converged, so let's see if the augmentations have converged as well\n\tfeLog(\"\\n........................ augmentation # %d\\n\", m_naug + 1);\n\n\t// do callback\n\tfem.DoCallback(CB_AUGMENT);\n\n\t// do the augmentations\n\tbool bconv = Augment();\n\n\t// update counter\n\t++m_naug;\n\n\t// we reset the reformations counter\n\tm_nref = 0;\n\n\t// If we havn't converged we prepare for the next iteration\n\tif (!bconv)\n\t{\n\t\t// Since the Lagrange multipliers have changed, we can't just copy\n\t\t// the last residual but have to recalculate the residual\n\t\t// we also recalculate the stresses in case we are doing augmentations\n\t\t// for incompressible materials\n\t\tUpdateModel();\n\t\t{\n\t\t\t// TODO: Why is this not calling strategy->Residual? \n\t\t\tTRACK_TIME(TimerID::Timer_Residual);\n\t\t\tResidual(m_R0);\n\t\t}\n\n\t\tm_qnstrategy->PreSolveUpdate();\n\n\t\t// reform the matrix if we are using full-Newton or\n\t\t// force reform after augmentations\n\t\tif ((m_qnstrategy->m_maxups == 0) || (m_breformAugment))\n\t\t{\n\t\t\t// TODO: Note sure how to handle a false return from ReformStiffness. \n\t\t\t//       I think this is pretty rare so I'm ignoring it for now.\n//\t\t\tif (ReformStiffness() == false) break;\n\t\t\tm_qnstrategy->ReformStiffness();\n\t\t}\n\t}\n\n\treturn bconv;\n}\n\n//! Update the state of the model\nvoid FENewtonSolver::Update(std::vector<double>& ui)\n{\n\t{\n\t\tTRACK_TIME(TimerID::Timer_Update);\n\n\t\t// get the mesh\n\t\tFEModel& fem = *GetFEModel();\n\t\tFEMesh& mesh = fem.GetMesh();\n\n\t\t// update nodes\n\t\tvector<double> U(m_Ut.size());\n\t\tfor (size_t i = 0; i < m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n\t\t// scatter solution to nodes\n\t\tfor (int i = 0; i < m_solutionNorm.size(); ++i)\n\t\t{\n\t\t\tConvergenceInfo& ci = m_solutionNorm[i];\n\t\t\tFESolutionVariable& var = m_Var[ci.nvar];\n\t\t\tscatter(U, mesh, *var.m_dofs);\n\t\t}\n\n\t\t// enforce the linear constraints\n\t\t// TODO: do we really have to do this? Shouldn't the algorithm\n\t\t// already guarantee that the linear constraints are satisfied?\n\t\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\t\tif (LCM.LinearConstraints() > 0)\n\t\t{\n\t\t\tLCM.Update();\n\t\t}\n\n\t\t// make sure the prescribed velocities are fullfilled\n\t\tint nbc = fem.BoundaryConditions();\n\t\tfor (int i = 0; i < nbc; ++i)\n\t\t{\n\t\t\tFEBoundaryCondition& dc = *fem.BoundaryCondition(i);\n\t\t\tif (dc.IsActive()) dc.Update();\n\t\t}\n\n\t\t// update element degrees of freedom\n\t\tint ndom = mesh.Domains();\n\t\tfor (int i = 0; i < ndom; ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tint NE = dom.Elements();\n\t\t\tfor (int j = 0; j < NE; ++j)\n\t\t\t{\n\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\tif (el.m_lm >= 0) el.m_val = U[el.m_lm];\n\t\t\t}\n\t\t}\n\t}\n\n\t// update model state\n\tGetFEModel()->Update();\n}\n\n\n//-----------------------------------------------------------------------------\n//! Updates the current state of the model\n//! NOTE: The ui vector also contains prescribed displacement increments. Also note that this\n//!       only works for a limited set of FEBio features (no rigid bodies or shells!).\nvoid FENewtonSolver::Update2(const vector<double>& ui)\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// total displacements\n\tvector<double> U(m_Ut.size());\n\tfor (size_t i = 0; i<m_Ut.size(); ++i) U[i] = ui[i] + m_Ui[i] + m_Ut[i];\n\n\t// scatter solution to nodes\n\tfor (int i = 0; i < m_solutionNorm.size(); ++i)\n\t{\n\t\tConvergenceInfo& ci = m_solutionNorm[i];\n\t\tFESolutionVariable& var = m_Var[ci.nvar];\n\t\tscatter(U, mesh, *var.m_dofs);\n\t}\n\n\t// Update the prescribed nodes\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tif (node.m_rid == -1)\n\t\t{\n\t\t\tvec3d dv(0, 0, 0);\n\t\t\tfor (int j = 0; j < node.m_ID.size(); ++j)\n\t\t\t{\n\t\t\t\tint nj = -node.m_ID[j] - 2; if (nj >= 0) node.set(j, node.get(j) + ui[nj]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// update element degrees of freedom\n\tint ndom = mesh.Domains();\n\tfor (int i = 0; i<ndom; ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tif (el.m_lm >= 0) el.m_val = U[el.m_lm];\n\t\t}\n\t}\n\n\t// update model state\n\tGetFEModel()->Update();\n}\n\n//-----------------------------------------------------------------------------\n//! Update the model\nvoid FENewtonSolver::UpdateModel()\n{\n\tFEModel& fem = *GetFEModel();\n\tfem.Update();\n}\n\n//-----------------------------------------------------------------------------\n//! calculates the global stiffness matrix (needs to be overwritten by derived classes)\nbool FENewtonSolver::StiffnessMatrix()\n{\n\t// setup the linear system\n\tFELinearSystem LS(GetFEModel(), *m_pK, m_Fd, m_ui, (m_msymm == REAL_SYMMETRIC));\n\n\t// build the stiffness matrix\n\treturn StiffnessMatrix(LS);\n}\n\n//-----------------------------------------------------------------------------\nbool FENewtonSolver::StiffnessMatrix(FELinearSystem& LS)\n{\n\tassert(false);\n\treturn false;\n}\n"
  },
  {
    "path": "FECore/FENewtonSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESolver.h\"\n#include \"FENewtonStrategy.h\"\n#include \"FETimeInfo.h\"\n#include \"FELineSearch.h\"\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FEModel;\nclass FEGlobalMatrix;\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\nenum QN_STRATEGY\n{\n\tQN_BFGS,\n\tQN_BROYDEN,\n\tQN_JFNK\n};\n\n//-----------------------------------------------------------------------------\nstruct ConvergenceInfo\n{\n\tint\t\t\tnvar;\t\t// corresponding solution variable\n\tdouble\t\ttol;\t\t// convergence tolerance\n\tdouble\t\tnorm0;\t\t// initial norm\n\tdouble\t\tnormi;\t\t// current incremental norm\n\tdouble\t\tnorm;\t\t// current total norm\n\tdouble\t\tmaxnorm;\t// the max norm\n\n\tConvergenceInfo()\n\t{\n\t\tnvar = -1;\n\t\ttol = 0.0;\n\t\tnorm0 = 0.0;\n\t\tnormi = 0.0;\n\t\tnorm = 0.0;\n\t\tmaxnorm = 0.0;\n\t}\n\n\tbool IsConverged() const\n\t{\n\t\treturn (tol > 0 ? normi <= (tol*tol)*norm : true);\n\t}\n};\n\n//-----------------------------------------------------------------------------\n//! This class defines the base class for Newton-type solvers. \n//! The class implements the basic logic behind a newton-solver but defers some\n//! of the logic, especially relating to the update of the stiffness matrix to\n//! the FENewtonStrategy class. \n//! \\todo there is some common functionality with the FELinearSolver. Perhaps\n//! I can make the FELinearSolver a base class?\n//! \\todo Perhaps I can introduce a base class for linear search algorithms\n//! so that the line search strategy can be customized as well.\nclass FECORE_API FENewtonSolver : public FESolver\n{\npublic:\n\t//! constructor\n\tFENewtonSolver(FEModel* pfem);\n\n\t//! destrcutor\n\t~FENewtonSolver();\n\n\t//! Set the default solution strategy\n\tvoid SetDefaultStrategy(QN_STRATEGY qn);\n\n\t//! Check the zero diagonal\n\tvoid CheckZeroDiagonal(bool bcheck, double ztol = 0.0);\n\npublic: // overloaded from FESolver\n\n\t//! Initialization\n\tbool Init() override;\n\n\t//! return number of equations\n\tint NumberOfEquations() const { return m_neq; }\n\n\t//! Clean up\n\tvoid Clean() override;\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Solve an analysis step\n\tbool SolveStep() override;\n\n\t//! rewind solver\n\tvoid Rewind() override;\n\n\t//! prep the solver for the QN updates\n\tvirtual void PrepStep();\n\npublic:\t// Quasi-Newton methods\n\n\t//! call this at the start of the quasi-newton loop\n\tbool QNInit();\n\n\t//! Do a qn update\n\tbool QNUpdate();\n\n\t//! solve the equations using QN method (returns line search size)\n\tdouble QNSolve();\n\n\t//! Force a stiffness reformation during next update\n\tvoid QNForceReform(bool b);\n\n\t// return line search\n\tFELineSearch* GetLineSearch();\n\npublic:\n\t//! return the stiffness matrix\n\tFEGlobalMatrix* GetStiffnessMatrix() override;\n\n\t//! reform the stiffness matrix\n    bool ReformStiffness();\n\n    //! recalculates the shape of the stiffness matrix\n    bool CreateStiffness(bool breset);\n\n\t//! get the RHS\n\tstd::vector<double> GetLoadVector() override;\n\n\t//! Get the total solution vector (for current Newton iteration)\n\tvirtual void GetSolutionVector(std::vector<double>& U);\n\n\t//! Get the total solution vector\n\tstd::vector<double> GetSolutionVector() const override;\n\npublic:\n\t//! do augmentations\n\tbool DoAugmentations();\n\n\t//! solve the equations\n\tvoid SolveEquations(std::vector<double>& u, std::vector<double>& R);\n\n\t//! do a line search\n\tdouble DoLineSearch();\n\npublic:\n\t//! Set the solution strategy\n\tvoid SetSolutionStrategy(FENewtonStrategy* pstrategy);\n\n\t//! solve the linear system of equations\n\tvoid SolveLinearSystem(vector<double>& x, vector<double>& R);\n\n\t//! Do a Quasi-Newton step\n\t//! This is called from SolveStep.\n\tvirtual bool Quasin();\n\n    //! calculates the global stiffness matrix (needs to be overwritten by derived classes)\n    virtual bool StiffnessMatrix();\n\n\t//! this is the new method for building the stiffness matrix\n\tvirtual bool StiffnessMatrix(FELinearSystem& LS);\n\n\t//! calculates the global residual vector (needs to be overwritten by derived classes)\n\tvirtual bool Residual(vector<double>& R) = 0;\n\n\t//! Check convergence. Derived classes that don't override Quasin, should implement this\n\t//! niter = iteration number\n\t//! ui    = search direction\n\t//! ls    = line search factor\n\tvirtual bool CheckConvergence(int niter, const vector<double>& ui, double ls);\n\n\t//! return the linear solver\n\tLinearSolver* GetLinearSolver() override;\n\n\t//! Add a solution variable from a doflist\n\tvoid AddSolutionVariable(FEDofList* dofs, int order, const char* szname, double tol);\n\n\t//! Update the state of the model\n\tvoid Update(std::vector<double>& u) override;\n\n\t//! TODO: This is a helper function to get around an issue with the current implementation\n\t//        regarding prescribed displacements. The purpose of this Update2 function is to update\n\t//        all degrees of freedom, including prescribed ones. This is currently only used by the JFNKMatrix class.\n\t//        and overridden in FESolidSolver2. \n\tvirtual void Update2(const vector<double>& ui);\n\n\t//! Update the model\n\tvirtual void UpdateModel();\n\npublic:\n\tConvergenceInfo GetResidualConvergence() { return m_residuNorm; }\n\tConvergenceInfo GetEnergyConvergence() { return m_energyNorm; }\n\tConvergenceInfo GetSolutionConvergence(int n) { return m_solutionNorm[n]; }\n\nprotected:\n\tbool AllocateLinearSystem();\n\npublic:\n\t// line search options\n\tFELineSearch*\tm_lineSearch;\n\n\t// solver parameters\n\tint\t\t\t\t\tm_maxref;\t\t//!< max nr of reformations per time step\n\tint\t\t\t\t\tm_force_partition;\t//!< Force a partition of the global matrix (e.g. for testing with BIPN solver)\n\tdouble\t\t\t\tm_Rtol;\t\t\t//!< residual convergence norm\n\tdouble\t\t\t\tm_Etol;\t\t\t//!< energy convergence norm\n\tdouble\t\t\t\tm_Rmin;\t\t\t//!< min residual value\n\tdouble\t\t\t\tm_Rmax;\t\t\t//!< max residual value\n\n\t// solution strategy\n\tFENewtonStrategy*\tm_qnstrategy;\t\t//!< class handling the specific stiffness update logic\n\tbool\t\t\t\tm_breformtimestep;\t//!< reform at start of time step\n\tbool\t\t\t\tm_breformAugment;\t//!< reform after each (failed) augmentations\n\tbool\t\t\t\tm_bforceReform;\t\t//!< forces a reform in QNInit\n\tbool\t\t\t\tm_bdivreform;\t\t//!< reform when diverging\n\tbool\t\t\t\tm_bdoreforms;\t\t//!< do reformations\n\n\t// counters\n\tint\t\tm_nref;\t\t\t//!< nr of stiffness retormations\n\n\t// Error handling\n\tbool\tm_bzero_diagonal;\t//!< check for zero diagonals\n\tdouble\tm_zero_tol;\t\t\t//!< tolerance for zero diagonal\n\n\t// linear solver data\n\tLinearSolver*\t\tm_plinsolve;\t//!< the linear solver\n\tFEGlobalMatrix*\t\tm_pK;\t\t\t//!< global stiffness matrix\n    bool\t\t\t\tm_breshape;\t\t//!< Matrix reshape flag\n\tbool\t\t\t\tm_persistMatrix;//!< Don't delete stiffness matrix until necessary (if true, K is deleted at end of time step)\n\n\t// data used by Quasin\n\tvector<double> m_R0;\t//!< residual at iteration i-1\n\tvector<double> m_R1;\t//!< residual at iteration i\n\tvector<double> m_ui;\t//!< solution increment vector\n\tvector<double> m_Ut;\t//!< total solution vector\n\tvector<double> m_Ui;\t//!< total solution increments of current time step\n\tvector<double> m_up;\t//!< solution increment of previous iteration\n\tvector<double> m_Fd;\t//!< residual correction due to prescribed degrees of freedom\n\nprivate:\n\tdouble\tm_ls;\t//!< line search factor calculated in last call to QNSolve\n\nprotected:\n\tConvergenceInfo\t\t\tm_residuNorm;\t// residual convergence info\n\tConvergenceInfo\t\t\tm_energyNorm;\t// energy convergence info\n\tvector<ConvergenceInfo>\tm_solutionNorm;\t// converge info for solution variables\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FENewtonStrategy.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENewtonStrategy.h\"\n#include \"FENewtonSolver.h\"\n#include \"LinearSolver.h\"\n\nFENewtonStrategy::FENewtonStrategy(FEModel* fem) : FECoreBase(fem)\n{\n\tm_pns = nullptr;\n\n\tm_maxups = 10;\n\tm_cmax = 1e5;\n\tm_max_buf_size = 0; // when zero, it should default to m_maxups\n\tm_cycle_buffer = true;\n\n\tm_nups = 0;\n}\n\nFENewtonStrategy::~FENewtonStrategy()\n{\n}\n\nvoid FENewtonStrategy::SetNewtonSolver(FENewtonSolver* solver)\n{\n\tm_pns = solver;\n}\n\n//! initialize the linear system\nSparseMatrix* FENewtonStrategy::CreateSparseMatrix(Matrix_Type mtype)\n{\n\tif (m_pns == 0) return 0;\n\n\tLinearSolver* plinsolve = m_pns->m_plinsolve;\n\n\tSparseMatrix* pS = plinsolve->CreateSparseMatrix(mtype);\n\n\treturn pS;\n}\n\nbool FENewtonStrategy::ReformStiffness()\n{\n\treturn m_pns->ReformStiffness();\n}\n\n//! calculate the residual\nbool FENewtonStrategy::Residual(std::vector<double>& R, bool binit)\n{\n\tTRACK_TIME(TimerID::Timer_Residual);\n\treturn m_pns->Residual(R);\n}\n\nvoid FENewtonStrategy::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\tar & m_nups;\n\tar & m_pns;\n}\n\n//! reset data for new run\nvoid FENewtonStrategy::Reset()\n{\n\n}\n"
  },
  {
    "path": "FECore/FENewtonStrategy.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n#include \"fecore_enum.h\"\n#include \"FECoreBase.h\"\n\n//-----------------------------------------------------------------------------\nclass FENewtonSolver;\nclass SparseMatrix;\nclass LinearSolver;\n\n//-----------------------------------------------------------------------------\n//! A Base class for newton-type solution strategies\nclass FECORE_API FENewtonStrategy : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FENEWTONSTRATEGY_ID)\n\tFECORE_BASE_CLASS(FENewtonStrategy)\n\npublic:\n\tFENewtonStrategy(FEModel* fem);\n\tvirtual ~FENewtonStrategy();\n\n\tvoid SetNewtonSolver(FENewtonSolver* solver);\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! reset data for new run\n\tvirtual void Reset();\n\npublic:\n\t//! initialize the linear system\n\tvirtual SparseMatrix* CreateSparseMatrix(Matrix_Type mtype);\n\n\t//! Presolve update\n\tvirtual void PreSolveUpdate() {}\n\n\t//! perform a Newton udpate (returning false will force a matrix reformations)\n\tvirtual bool Update(double s, std::vector<double>& ui, std::vector<double>& R0, std::vector<double>& R1) = 0;\n\n\t//! solve the equations\n\tvirtual void SolveEquations(std::vector<double>& x, std::vector<double>& b) = 0;\n\n\t//! reform the stiffness matrix\n\tvirtual bool ReformStiffness();\n\n\t//! calculate the residual\n\tvirtual bool Residual(std::vector<double>& R, bool binit);\n\npublic:\n\tint\t\tm_maxups;\t\t//!< max nr of QN iters permitted between stiffness reformations\n\tint\t\tm_max_buf_size;\t//!< max buffer size for update vector storage\n\tbool\tm_cycle_buffer;\t//!< recycle the buffer when updates is larger than buffer size\n\tdouble\tm_cmax;\t\t\t//!< maximum value for the condition number\n\tint\t\tm_nups;\t\t\t//!< nr of stiffness updates\n\nprotected:\n\tFENewtonSolver*\tm_pns;\n};\n"
  },
  {
    "path": "FECore/FENodalBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FENodalBC.h\"\n\n//==================================================================\nFENodalBC::FENodalBC(FEModel* fem) : FEBoundaryCondition(fem)\n{\n\tm_nodeSet = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalBC::SetNodeSet(FENodeSet* nodeSet)\n{\n\tm_nodeSet = nodeSet;\n}\n\n//-----------------------------------------------------------------------------\nFENodeSet* FENodalBC::GetNodeSet()\n{\n\treturn m_nodeSet;\n}\n\nvoid FENodalBC::Serialize(DumpStream& ar)\n{\n\tFEBoundaryCondition::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_nodeSet;\n}\n"
  },
  {
    "path": "FECore/FENodalBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include \"FEBoundaryCondition.h\"\n#include \"fecore_api.h\"\n\nclass FENodeSet;\n\nclass FECORE_API FENodalBC : public FEBoundaryCondition\n{\n\tFECORE_BASE_CLASS(FENodalBC)\n\npublic:\n\tFENodalBC(FEModel* fem);\n\n\t// Set the node set\n\tvoid SetNodeSet(FENodeSet* nodeSet);\n\n\t// Get the node set\n\tFENodeSet* GetNodeSet();\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFENodeSet* m_nodeSet;\n};\n"
  },
  {
    "path": "FECore/FENodalLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENodalLoad.h\"\n#include \"FENodeSet.h\"\n#include \"DumpStream.h\"\n#include \"FENode.h\"\n#include \"FEMaterialPoint.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FENodalLoad, FEModelLoad)\n\tADD_PARAMETER(m_brelative, \"relative\");\n//\tADD_PROPERTY(m_nodeSet, \"node_set\", FEProperty::Reference);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENodalLoad::FENodalLoad(FEModel* pfem) : FEModelLoad(pfem), m_dofs(pfem)\n{\n\tm_brelative = false;\n\tm_nodeSet = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalLoad::Serialize(DumpStream& ar)\n{\n\tFEModelComponent::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_dofs & m_nodeSet;\n}\n\n//-----------------------------------------------------------------------------\nbool FENodalLoad::Init()\n{\n\t// Make sure we have a node set\n\tif (m_nodeSet == nullptr) return false;\n\n\t// Get the DOF list from the derived class\n\tm_dofs.Clear();\n\tif (SetDofList(m_dofs) == false) return false;\n\n\t// make sure the dof list is not empty\n\tif (m_dofs.IsEmpty()) return false;\n\n\t// so far so good.\n\treturn FEModelLoad::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! activation\nvoid FENodalLoad::Activate()\n{\n\tFEModelLoad::Activate();\n\n\tif (m_brelative)\n\t{\n\t\tint nodes = m_nodeSet->Size();\n\t\tint dofs = m_dofs.Size();\n\t\tif ((dofs == 0) || (nodes == 0)) return;\n\n\t\tm_rval.resize(nodes, vector<double>(dofs, 0.0));\n\n\t\t// get the current nodal loads\n\t\tfor (int i = 0; i < nodes; ++i)\n\t\t{\n\t\t\tFENode& node = *m_nodeSet->Node(i);\n\n\t\t\tfor (int j = 0; j < dofs; ++j)\n\t\t\t{\n\t\t\t\tm_rval[i][j] = -node.get_load(m_dofs[j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Get the DOF list\nconst FEDofList& FENodalLoad::GetDOFList() const\n{\n\treturn m_dofs;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalLoad::SetNodeSet(FENodeSet* ns)\n{\n\tm_nodeSet = ns;\n}\n\n//-----------------------------------------------------------------------------\n//! get the nodeset\nFENodeSet* FENodalLoad::GetNodeSet()\n{\n\treturn m_nodeSet;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalLoad::LoadVector(FEGlobalVector& R)\n{\n\tFENodeSet& nset = *m_nodeSet;\n\tint dofs = m_dofs.Size();\n\tvector<double> val(dofs, 0.0);\n\tint N = nset.Size();\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\tint nid = nset[i];\n\n\t\t// get the nodal values\n\t\tGetNodalValues(i, val);\n\n\t\t// add relative values\n\t\tif (m_brelative)\n\t\t{\n\t\t\tfor (int j = 0; j < dofs; ++j) val[j] += m_rval[i][j];\n\t\t}\n\n\t\t// assemble into residual\n\t\tfor (int j=0; j<dofs; ++j)\n\t\t\tR.Assemble(nid, m_dofs[j], val[j]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalLoad::StiffnessMatrix(FELinearSystem& LS)\n{\n\t// Nothing to do here.\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FENodalDOFLoad, FENodalLoad)\n\tADD_PARAMETER(m_dof, \"dof\", 0, \"$(dof_list)\");\n\tADD_PARAMETER(m_scale, \"scale\")->SetFlags(FE_PARAM_ADDLC | FE_PARAM_VOLATILE);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFENodalDOFLoad::FENodalDOFLoad(FEModel* fem) : FENodalLoad(fem)\n{\n\tm_dof = -1;\n\tm_scale = 0.0;\n\n\tm_dtscale = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalDOFLoad::SetDtScale(bool b)\n{\n\tm_dtscale = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodalDOFLoad::SetLoad(double s)\n{\n\tm_scale = s;\n}\n\n//-----------------------------------------------------------------------------\nbool FENodalDOFLoad::SetDofList(FEDofList& dofList)\n{\n\treturn dofList.AddDof(m_dof);\n}\n\n//-----------------------------------------------------------------------------\n//! Return the current value of the nodal load\nvoid FENodalDOFLoad::GetNodalValues(int n, std::vector<double>& val)\n{\n\tassert(val.size() == 1);\n\tconst FENodeSet& nset = *GetNodeSet();\n\tint nid = nset[n];\n\tconst FENode& node = *nset.Node(n);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = n;\n\n\tval[0] = m_scale(mp);\n\n\tif (m_dtscale)\n\t{\n\t\tdouble dt = GetTimeInfo().timeIncrement;\n\t\tval[0] *= dt;\n\t}\n}\n\ndouble FENodalDOFLoad::NodeValue(int n)\n{\n\tconst FENodeSet& nset = *GetNodeSet();\n\tint nid = nset[n];\n\tconst FENode& node = *nset.Node(n);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = n;\n\n\tdouble val = m_scale(mp);\n\n\tif (m_dtscale)\n\t{\n\t\tdouble dt = GetTimeInfo().timeIncrement;\n\t\tval *= dt;\n\t}\n\n\treturn val;\n}\n"
  },
  {
    "path": "FECore/FENodalLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelLoad.h\"\n#include \"FENodeDataMap.h\"\n#include \"FEDofList.h\"\n#include \"FEModelParam.h\"\n\n//-----------------------------------------------------------------------------\nclass FENodeSet;\n\n//-----------------------------------------------------------------------------\n//! Nodal load boundary condition\nclass FECORE_API FENodalLoad : public FEModelLoad\n{\n\tFECORE_BASE_CLASS(FENodalLoad)\n\npublic:\n\t//! constructor\n\tFENodalLoad(FEModel* pfem);\n\n\t//! initialization\n\tbool Init() override;\n\n\t//! activation\n\tvoid Activate() override;\n\n\t//! Get the DOF list\n\tconst FEDofList& GetDOFList() const;\n\n\t//! add a node set\n\tvoid SetNodeSet(FENodeSet* ns);\n\n\t//! get the nodeset\n\tFENodeSet* GetNodeSet();\n\n\t//! serialiation\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\t//! Get the DOF list\n\t//! This must be implemented by derived classes.\n\tvirtual bool SetDofList(FEDofList& dofList) = 0;\n\n\t//! Get the nodal value\n\t//! This must be implemented by derived classes.\n\t//! The vals array will have the same size as the dof list.\n\tvirtual void GetNodalValues(int inode, std::vector<double>& vals) = 0;\n\npublic:\n\t//! evaluate the contribution to the residual\n\tvirtual void LoadVector(FEGlobalVector& R) override;\n\n\t//! evaluate the contribution to the global stiffness matrix\n\tvirtual void StiffnessMatrix(FELinearSystem& LS) override;\n\nprotected:\n\tFEDofList\tm_dofs;\n\tFENodeSet*\tm_nodeSet;\n\tbool\t\tm_brelative;\n\tvector<vector<double> >\tm_rval;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Class for prescribing the \"load\" on a degree of freedom.\nclass FECORE_API FENodalDOFLoad : public FENodalLoad\n{\npublic:\n\tFENodalDOFLoad(FEModel* fem);\n\n\t//! Set the DOF list\n\tbool SetDofList(FEDofList& dofList) override;\n\n\t//! get/set degree of freedom\n\tvoid SetDOF(int ndof) { m_dof = ndof; }\n\tint GetDOF() const { return m_dof; }\n\n\t//! get/set load \n\tvoid SetLoad(double s);\n\n\tvoid GetNodalValues(int n, std::vector<double>& val) override;\n\n\tdouble NodeValue(int n);\n\n\tvoid SetDtScale(bool b);\n\nprivate:\n\tint\t\t\t\tm_dof;\t\t//!< degree of freedom index\n\tFEParamDouble\tm_scale;\t//!< applied load scale factor\n\n\tbool m_dtscale;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FENode.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENode.h\"\n#include \"DumpStream.h\"\n\n//=============================================================================\n// FENode\n//-----------------------------------------------------------------------------\nFENode::FENode()\n{\n\t// set the default state\n\tm_nstate = 0;\n\n\t// rigid body data\n\tm_rid = -1;\n\n\t// default ID\n\tm_nID = -1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENode::SetDOFS(int n)\n{\n\t// initialize dof stuff\n\tm_ID.assign(n, -1);\n\tm_BC.assign(n, 0);\n\tm_val_t.assign(n, 0.0);\n\tm_val_p.assign(n, 0.0);\n\tm_Fr.assign(n, 0.0);\n}\n\n//-----------------------------------------------------------------------------\nFENode::FENode(const FENode& n)\n{\n\tm_r0 = n.m_r0;\n\tm_rt = n.m_rt;\n\tm_at = n.m_at;\n\tm_rp = n.m_rp;\n\tm_vp = n.m_vp;\n\tm_ap = n.m_ap;\n\tm_d0 = n.m_d0;\n    m_dt = n.m_dt;\n    m_dp = n.m_dp;\n\n\tm_nID = n.m_nID;\n\tm_rid = n.m_rid;\n\tm_nstate = n.m_nstate;\n\n\tm_ID = n.m_ID;\n\tm_BC = n.m_BC;\n\tm_val_t = n.m_val_t;\n\tm_val_p = n.m_val_p;\n\tm_Fr = n.m_Fr;\n}\n\n//-----------------------------------------------------------------------------\nFENode& FENode::operator = (const FENode& n)\n{\n\tm_r0 = n.m_r0;\n\tm_rt = n.m_rt;\n\tm_at = n.m_at;\n\tm_rp = n.m_rp;\n\tm_vp = n.m_vp;\n\tm_ap = n.m_ap;\n\tm_d0 = n.m_d0;\n    m_dt = n.m_dt;\n    m_dp = n.m_dp;\n\n\tm_nID = n.m_nID;\n\tm_rid = n.m_rid;\n\tm_nstate = n.m_nstate;\n\n\tm_ID = n.m_ID;\n\tm_BC = n.m_BC;\n\tm_val_t = n.m_val_t;\n\tm_val_p = n.m_val_p;\n\tm_Fr = n.m_Fr;\n\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\n// Serialize\nvoid FENode::Serialize(DumpStream& ar)\n{\n\tar & m_rt & m_at;\n\tar & m_rp & m_vp & m_ap;\n\tar & m_Fr;\n\tar & m_val_t & m_val_p;\n\tar & m_dt & m_dp;\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_nID;\n\t\tar & m_nstate;\n\t\tar & m_ID;\n\t\tar & m_BC;\n\t\tar & m_r0;\n\t\tar & m_ra;\n\t\tar & m_rid;\n\t\tar & m_d0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Update nodal values, which copies the current values to the previous array\nvoid FENode::UpdateValues()\n{\n\tm_val_p = m_val_t;\n}\n"
  },
  {
    "path": "FECore/FENode.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"DOFS.h\"\n#include \"vec3d.h\"\n#include <vector>\n\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! This class defines a finite element node\n\n//! It stores nodal positions and nodal equations numbers and more.\n//!\n//! The m_ID array will store the equation number for the corresponding\n//! degree of freedom. Its values can be (a) non-negative (0 or higher) which\n//! gives the equation number in the linear system of equations, (b) -1 if the\n//! dof is fixed, and (c) < -1 if the dof corresponds to a prescribed dof. In\n//! that case the corresponding equation number is given by -ID-2.\n\nclass FECORE_API FENode\n{\npublic:\n\t// Node status flags\n\tenum Status {\n\t\tEXCLUDE     = 0x01,\t// exclude node from analysis\n\t\tSHELL       = 0x02,\t// this node belongs to a shell\n\t\tRIGID_CLAMP = 0x04,\t// this node should be clamped to a rigid body (only applies to shell nodes)\n\t\tHANGING     = 0x08\t// This is a hanging node\n\t};\n\npublic:\n\t//! default constructor\n\tFENode();\n\n\t//! copy constructor\n\tFENode(const FENode& n);\n\n\t//! assignment operator\n\tFENode& operator = (const FENode& n);\n\n\t//! Set the number of DOFS\n\tvoid SetDOFS(int n);\n\n\t//! Get the nodal ID\n\tint GetID() const { return m_nID; }\n\n\t//! Set the node ID\n\tvoid SetID(int n) { m_nID = n; }\n\n\t//! see if status flags are set\n\tbool HasFlags(unsigned int flags) const { return ((m_nstate & flags) != 0); }\n\n\t//! set all the status flags\n\tvoid SetAllFlags(unsigned int flags) { m_nstate = flags; }\n\n\t//! get the status falgs\n\tunsigned int Flags() const { return m_nstate; }\n\n\t//! Add flags\n\tvoid SetFlags(unsigned int flags) { m_nstate |= flags; }\n\n\t//! Remove flags\n\tvoid UnsetFlags(unsigned int flags) { m_nstate &= ~flags; }\n\n\t// Serialize\n\tvoid Serialize(DumpStream& ar);\n\n\t//! Update nodal values, which copies the current values to the previous array\n\tvoid UpdateValues();\n\nprotected:\n\tint\t\tm_nID;\t//!< nodal ID\n\npublic: // geometry data\n\tvec3d\tm_r0;\t//!< initial position\n\tvec3d\tm_rt;\t//!< current position\n\tvec3d\tm_ra;\t//!< used by rigid solver\n\n\tvec3d\tm_at;\t//!< nodal acceleration\n\n\tvec3d\tm_rp;\t//!< position of node at previous time step\n\tvec3d\tm_vp;\t//!< previous velocity\n\tvec3d\tm_ap;\t//!< previous acceleration\n\n\tvec3d   m_d0;   //!< initial director\n\tvec3d\tm_dt;\t//!< current director\n    vec3d   m_dp;   //!< director at previous time step\n\npublic:\t// rigid body data\n\tunsigned int\tm_nstate;\t//!< node state flags\n\tint\t\t\t\tm_rid;\t\t//!< rigid body number\n\npublic:\n\t// get/set functions for current value array\n\tdouble& get(int n) { return m_val_t[n]; }\n\tdouble get(int n) const { return m_val_t[n]; }\n\tvoid set(int n, double v) { m_val_t[n] = v; }\n\tvoid add(int n, double v) { m_val_t[n] += v; }\n\tvoid sub(int n, double v) { m_val_t[n] -= v; }\n\tvec3d get_vec3d(int i, int j, int k) const { return vec3d(m_val_t[i], m_val_t[j], m_val_t[k]); }\n\tvoid set_vec3d(int i, int j, int k, const vec3d& v) { m_val_t[i] = v.x; m_val_t[j] = v.y; m_val_t[k] = v.z; }\n\n\t// get functions for previous value array\n\t// to set these values, call UpdateValues which copies the current values\n\tdouble get_prev(int n) const { return m_val_p[n]; }\n\tvec3d get_vec3d_prev(int i, int j, int k) const { return vec3d(m_val_p[i], m_val_p[j], m_val_p[k]); }\n\n\tdouble get_load(int n) const { return m_Fr[n]; }\n\tvec3d get_load3(int i, int j, int k) const { return vec3d(m_Fr[i], m_Fr[j], m_Fr[k]); }\n\n\tvoid set_load(int n, double v) { m_Fr[n] = v; }\n\npublic:\n\t// dof functions\n\tvoid set_bc(int ndof, int bcflag) { m_BC[ndof] = ((m_BC[ndof] & 0xF0) | bcflag); }\n\tvoid set_active  (int ndof) { m_BC[ndof] |= 0x10; }\n\tvoid set_inactive(int ndof) { m_BC[ndof] &= 0x0F; }\n\n\tint get_bc(int ndof) const { return (m_BC[ndof] & 0x0F); }\n\tbool is_active(int ndof) const { return ((m_BC[ndof] & 0xF0) != 0); }\n\n\tint dofs() const { return (int) m_ID.size(); }\n    \npublic:\n\t// return position of shell back-node\n    vec3d s0() const { return m_r0 - m_d0; }\n    vec3d st() const { return m_rt - m_dt; }\n    vec3d sp() const { return m_rp - m_dp; }\n\nprivate:\n\tstd::vector<int>\t\tm_BC;\t\t//!< boundary condition array\n\tstd::vector<double>\t\tm_val_t;\t//!< current nodal DOF values\n\tstd::vector<double>\t\tm_val_p;\t//!< previous nodal DOF values\n\tstd::vector<double>\t\tm_Fr;\t\t//!< equivalent nodal forces\n\npublic:\n\tstd::vector<int>\t\tm_ID;\t//!< nodal equation numbers\n};\n"
  },
  {
    "path": "FECore/FENodeDataMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FENodeDataMap.h\"\n#include \"FENodeSet.h\"\n#include \"FEMaterialPoint.h\"\n\nFENodeDataMap::FENodeDataMap() : FEDataMap(FE_NODE_DATA_MAP, FE_INVALID_TYPE)\n{\n\tm_nodeSet = nullptr;\n}\n\nFENodeDataMap::FENodeDataMap(FEDataType dataType) : FEDataMap(FE_NODE_DATA_MAP, dataType)\n{\n\tm_nodeSet = nullptr;\n}\n\nvoid FENodeDataMap::Create(const FENodeSet* nodeSet, double val)\n{\n\tm_nodeSet = nodeSet;\n\tint nsize = nodeSet->Size();\n\tresize(nsize, val);\n}\n\nconst FENodeSet* FENodeDataMap::GetNodeSet() const\n{ \n\treturn m_nodeSet; \n}\n\ndouble FENodeDataMap::getValue(int n) const\n{\n\treturn get<double>(n);\n}\n\nvoid FENodeDataMap::setValue(int n, double v)\n{\n\tset<double>(n, v);\n}\n\nvoid FENodeDataMap::setValue(int n, const vec2d& v)\n{\n\tset<vec2d>(n, v);\n}\n\nvoid FENodeDataMap::setValue(int n, const vec3d& v)\n{\n\tset<vec3d>(n, v);\n}\n\nvoid FENodeDataMap::setValue(int n, const mat3d& v)\n{\n\tset<mat3d>(n, v);\n}\n\nvoid FENodeDataMap::setValue(int n, const mat3ds& v)\n{\n\tset<mat3ds>(n, v);\n}\n\nvoid FENodeDataMap::fillValue(double v)\n{\n\tset<double>(v);\n}\n\nvoid FENodeDataMap::fillValue(const vec2d& v)\n{\n\tset<vec2d>(v);\n}\n\nvoid FENodeDataMap::fillValue(const vec3d& v)\n{\n\tset<vec3d>(v);\n}\n\nvoid FENodeDataMap::fillValue(const mat3d& v)\n{\n\tset<mat3d>(v);\n}\n\nvoid FENodeDataMap::fillValue(const mat3ds& v)\n{\n\tset<mat3ds>(v);\n}\n\ndouble FENodeDataMap::value(const FEMaterialPoint& mp)\n{\n\tassert(mp.m_elem == nullptr);\n\treturn get<double>(mp.m_index);\n}\n\nvec3d FENodeDataMap::valueVec3d(const FEMaterialPoint& mp)\n{\n\tassert(mp.m_elem == nullptr);\n\treturn get<vec3d>(mp.m_index);\n}\n\nmat3d FENodeDataMap::valueMat3d(const FEMaterialPoint& mp)\n{\n\tassert(mp.m_elem == nullptr);\n\treturn get<mat3d>(mp.m_index);\n}\n\nmat3ds FENodeDataMap::valueMat3ds(const FEMaterialPoint& mp)\n{\n\tassert(mp.m_elem == nullptr);\n\treturn get<mat3ds>(mp.m_index);\n}\n\n// return the item list associated with this map\nFEItemList* FENodeDataMap::GetItemList()\n{\n\treturn const_cast<FENodeSet*>(m_nodeSet);\n}\n\nvoid FENodeDataMap::Serialize(DumpStream& ar)\n{\n\tFEDataMap::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\t// We have to cast the const away before serializing\n\t\t\tFENodeSet* ns = const_cast<FENodeSet*>(m_nodeSet);\n\t\t\tar << ns;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFENodeSet* ns;\n\t\t\tar >> ns;\n\t\t\tm_nodeSet = ns;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FENodeDataMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDataMap.h\"\n\nclass FENodeSet;\n\nclass FECORE_API FENodeDataMap : public FEDataMap\n{\npublic:\n\tFENodeDataMap();\n\tFENodeDataMap(FEDataType dataType);\n\n\tvoid Create(const FENodeSet* nodeSet, double val = 0.0);\n\n\tconst FENodeSet* GetNodeSet() const;\n\n\t// return the item list associated with this map\n\tFEItemList* GetItemList() override;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tvoid setValue(int n, double v) override;\n\tvoid setValue(int n, const vec2d& v) override;\n\tvoid setValue(int n, const vec3d& v) override;\n\tvoid setValue(int n, const mat3d& v) override;\n\tvoid setValue(int n, const mat3ds& v) override;\n\n\tdouble getValue(int n) const;\n\n\tvoid fillValue(double v) override;\n\tvoid fillValue(const vec2d& v) override;\n\tvoid fillValue(const vec3d& v) override;\n\tvoid fillValue(const mat3d& v) override;\n\tvoid fillValue(const mat3ds& v) override;\n\n\tdouble value(const FEMaterialPoint& mp) override;\n\tvec3d valueVec3d(const FEMaterialPoint& mp) override;\n\tmat3d valueMat3d(const FEMaterialPoint& mp) override;\n\tmat3ds valueMat3ds(const FEMaterialPoint& mp) override;\n\nprivate:\n\tconst FENodeSet*\tm_nodeSet;\n};\n"
  },
  {
    "path": "FECore/FENodeElemList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENodeElemList.h\"\n#include \"FESurface.h\"\n#include \"FEMesh.h\"\n#include \"FEDomain.h\"\n#include \"FEShellDomain.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nint FENodeElemList::MaxValence()\n{\n\tint nmax = 0;\n\tint N = (int) m_nval.size();\n\tfor (int i=0; i<N; ++i) if (m_nval[i] > nmax) nmax = m_nval[i];\n\treturn nmax;\n}\n\n//-----------------------------------------------------------------------------\n//! This function builds the node-element list for a surface\n\nvoid FENodeElemList::Create(const FESurface& s)\n{\n\tint i, j, n;\n\n\t// get the number of nodes\n\tint nn = s.Nodes();\n\n\t// get the number of elements\n\tint ne = s.Elements();\n\n\t// create nodal valence array\n\tm_nval.assign(nn, 0);\n\tm_pn.resize(nn);\n\n\t// fill valence table\n\tint nsize = 0;\n\tfor (i=0; i<ne; ++i)\n\t{\n\t\tconst FESurfaceElement& el = s.Element(i);\n\n\t\tfor (j=0; j<el.Nodes(); ++j)\n\t\t{\n\t\t\tn = el.m_lnode[j];\n\t\t\tm_nval[n]++;\n\t\t\tnsize++;\n\t\t}\n\t}\n\n\t// create the element reference array\n\tm_eref.resize(nsize);\n\tm_iref.resize(nsize);\n\n\t// set eref pointers\n\tm_pn[0] = 0;\n\tfor (i=1; i<nn; ++i)\n\t{\n\t\tm_pn[i] = m_pn[i-1] + m_nval[i-1];\n\t}\n\n\t// reset valence pointers\n\tfor (i=0; i<nn; ++i) m_nval[i] = 0;\n\n\t// fill eref table\n\tfor (i=0; i<ne; ++i)\n\t{\n\t\tconst FESurfaceElement& el = s.Element(i);\n\n\t\tfor (j=0; j<el.Nodes(); ++j)\n\t\t{\n\t\t\tn = el.m_lnode[j];\n\t\t\tm_eref[m_pn[n] + m_nval[n]] = const_cast<FESurfaceElement*>(&el);\n\t\t\tm_iref[m_pn[n] + m_nval[n]] = i;\n\t\t\tm_nval[n]++;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function builds the node-element list for a mesh\n\nvoid FENodeElemList::Create(FEMesh& mesh)\n{\n\tint i, j, n, nd;\n\n\t// get the number of nodes\n\tint NN = mesh.Nodes();\n\n\t// create nodal valence array\n\tm_nval.assign(NN, 0);\n\tm_pn.resize(NN);\n\n\t// fill valence table\n\tint nsize = 0;\n\tfor (nd=0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& d = mesh.Domain(nd);\n\t\tfor (i=0; i<d.Elements(); ++i)\n\t\t{\n\t\t\tFEElement& el = d.ElementRef(i);\n\t\t\tfor (j=0; j<el.Nodes(); ++j)\n\t\t\t{\n\t\t\t\tn = el.m_node[j];\n\t\t\t\tm_nval[n]++;\n\t\t\t\tnsize++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// create the element reference array\n\tm_eref.resize(nsize);\n\tm_iref.resize(nsize);\n\n\t// set eref pointers\n\tm_pn[0] = 0;\n\tfor (i=1; i<NN; ++i)\n\t{\n\t\tm_pn[i] = m_pn[i-1] + m_nval[i-1];\n\t}\n\n\t// reset valence pointers\n\tfor (i=0; i<NN; ++i) m_nval[i] = 0;\n\n\t// fill eref table\n    // Prioritize shell domains over other domains.\n    // This is needed when shells are connected to solids\n    // and contact interfaces need to use the shell properties\n    // for auto-penalty calculation.\n\tint nindex = 0;\n\tfor (nd=0; nd<mesh.Domains(); ++nd)\n\t{\n\t\tFEDomain& d = mesh.Domain(nd);\n\t\tif (d.Class() == FE_DOMAIN_SHELL) {\n\t\t\tfor (i = 0; i < d.Elements(); ++i, ++nindex)\n\t\t\t{\n\t\t\t\tFEElement& el = d.ElementRef(i);\n\t\t\t\tfor (j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tn = el.m_node[j];\n\t\t\t\t\tm_eref[m_pn[n] + m_nval[n]] = &el;\n\t\t\t\t\tm_iref[m_pn[n] + m_nval[n]] = nindex;\n\t\t\t\t\tm_nval[n]++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse nindex += d.Elements();\n\t}\n\tassert(nindex == mesh.Elements());\n\tnindex = 0;\n    for (nd=0; nd<mesh.Domains(); ++nd)\n    {\n        FEDomain& d = mesh.Domain(nd);\n\t\tif (d.Class() != FE_DOMAIN_SHELL) {\n\t\t\tfor (i = 0; i < d.Elements(); ++i, ++nindex)\n\t\t\t{\n\t\t\t\tFEElement& el = d.ElementRef(i);\n\t\t\t\tfor (j = 0; j < el.Nodes(); ++j)\n\t\t\t\t{\n\t\t\t\t\tn = el.m_node[j];\n\t\t\t\t\tm_eref[m_pn[n] + m_nval[n]] = &el;\n\t\t\t\t\tm_iref[m_pn[n] + m_nval[n]] = nindex;\n\t\t\t\t\tm_nval[n]++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse nindex += d.Elements();\n    }\n\tassert(nindex == mesh.Elements());\n}\n\n//-----------------------------------------------------------------------------\n//! This function builds the node-element list for a domain\n\nvoid FENodeElemList::Create(FEDomain& dom)\n{\n\tint i, j, n;\n\n\t// get the mesh\n\tFEMesh& mesh = *dom.GetMesh();\n\n\t// get the number of nodes\n\tint NN = mesh.Nodes();\n\n\t// create nodal valence array\n\tm_nval.assign(NN, 0);\n\tm_pn.resize(NN);\n\n\t// fill valence table\n\tint nsize = 0;\n\tfor (i=0; i<dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tfor (j=0; j<el.Nodes(); ++j)\n\t\t{\n\t\t\tn = el.m_node[j];\n\t\t\tm_nval[n]++;\n\t\t\tnsize++;\n\t\t}\n\t}\n\n\t// create the element reference array\n\tm_eref.resize(nsize);\n\tm_iref.resize(nsize);\n\n\t// set eref pointers\n\tm_pn[0] = 0;\n\tfor (i=1; i<NN; ++i)\n\t{\n\t\tm_pn[i] = m_pn[i-1] + m_nval[i-1];\n\t}\n\n\t// reset valence pointers\n\tfor (i=0; i<NN; ++i) m_nval[i] = 0;\n\n\t// fill eref table\n\tfor (i=0; i<dom.Elements(); ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tfor (j=0; j<el.Nodes(); ++j)\n\t\t{\n\t\t\tn = el.m_node[j];\n\t\t\tm_eref[m_pn[n] + m_nval[n]] = &el;\n\t\t\tm_iref[m_pn[n] + m_nval[n]] = i;\n\t\t\tm_nval[n]++;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeElemList::Clear()\n{\n\tm_nval.clear();\n\tm_eref.clear();\n\tm_iref.clear();\n\tm_pn.clear();\n}\n\n//-----------------------------------------------------------------------------\n//! Save data to dump file\n\nvoid FENodeElemList::Serialize(DumpStream& ar)\n{\n\tar & m_nval & m_iref & m_pn;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeElemTree::Create(FESurface* ps, int k)\n{\n\tint NN = ps->Nodes();\n\tint NE = ps->Elements();\n\n\t// temporary arrays\n\tvector< vector<int> > nel;\n\tvector<int> tag;\n\tnel.resize(NN);\n\ttag.assign(NE, -1);\n\n\t// build the first level\n\tfor (int i=0; i<NE; ++i)\n\t{\n\t\tFESurfaceElement* pe = &ps->Element(i);\n\t\tint ne = pe->Nodes();\n\t\tfor (int j=0; j<ne; ++j) nel[pe->m_lnode[j]].push_back(i);\n\t}\n\n\t// build the other levels\n\tfor (int l=0; l<k; ++l)\n\t{\n\t\tvector<int> ns(NN);\n\t\tfor (int i=0; i<NN; ++i) ns[i] = (int) nel[i].size();\n\n\t\tfor (int i=0; i<NN; ++i)\n\t\t{\n\t\t\tint ntag = l*NN + i;\n\t\t\tvector<int>& NI = nel[i];\n\t\t\tint ni = ns[i];\n\t\t\tfor (int j=0; j<ni; ++j) tag[NI[j]] = ntag;\n\n\t\t\tfor (int j=0; j<ni; ++j)\n\t\t\t{\n\t\t\t\tFESurfaceElement& e = ps->Element(NI[j]);\n\t\t\t\tint ne = e.Nodes();\n\t\t\t\tfor (int n=0; n<ne; ++n)\n\t\t\t\t{\n\t\t\t\t\tif (e.m_lnode[n] != i)\n\t\t\t\t\t{\n\t\t\t\t\t\tvector<int>& NJ = nel[e.m_lnode[n]];\n\t\t\t\t\t\tint nj = ns[e.m_lnode[n]];\n\t\t\t\t\t\tfor (int m=0; m<nj; ++m)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (tag[NJ[m]] < ntag) \n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tNI.push_back(NJ[m]);\n\t\t\t\t\t\t\t\ttag[NJ[m]] = ntag;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// assign the element pointers\n\tm_nel.resize(NN);\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tvector<int>& NI = nel[i];\n\t\tsort(NI.begin(), NI.end());\n\t\tint ni = (int)NI.size();\n\t\tm_nel[i].resize(ni);\n\t\tfor (int j=0; j<ni; ++j) m_nel[i][j] = &ps->Element(NI[j]);\n\t}\n}\n"
  },
  {
    "path": "FECore/FENodeElemList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n\nclass FESurface;\nclass FEMesh;\nclass FEElement;\nclass FEDomain;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! The FENodeElemList class is a utility class that determines for each node \n//! to which element it belongs.\n\n//! This class analyzes a mesh and finds for each node all elements that have\n//! this node\n\nclass FECORE_API FENodeElemList\n{\npublic:\n\tFENodeElemList(){}\n\tvirtual ~FENodeElemList(){}\n\n\t//! build the node-element list for a surface\n\tvoid Create(const FESurface& s);\n\n\t//! build the node-selement list for a mesh\n\tvoid Create(FEMesh& mesh);\n\n\t//! build the node-element list for a domain\n\tvoid Create(FEDomain& dom);\n\n\t//! serialize data to/from dump file\n\tvoid Serialize(DumpStream& ar);\n\n\t//! Clear the list\n\tvoid Clear();\n\n\tint MaxValence();\n\tint Valence(int n) { return m_nval[n]; }\n\tFEElement** ElementList(int n) { return &m_eref[0] + m_pn[n]; }\n\tint* ElementIndexList(int n) { return &m_iref[0] + m_pn[n]; }\n\n\tint Size() { return (int) m_nval.size(); }\n\nprotected:\n\tstd::vector<int>\t\t\tm_nval;\t// nodal valences\n\tstd::vector<FEElement*>\t\tm_eref;\t// element pointers\n\tstd::vector<int>\t\t\tm_iref;\t// element indices\n\tstd::vector<int>\t\t\tm_pn;\t// start index into the eref array\n};\n\n//-----------------------------------------------------------------------------\n//! Like the FEElemElemList, but can create multiple levels\nclass FENodeElemTree\n{\npublic:\n\tFENodeElemTree() {}\n\tvirtual ~FENodeElemTree() {}\n\n\tvoid Create(FESurface* ps, int k = 0);\n\n\tint Valence(int n) { return (int) m_nel[n].size(); }\n\n\tFEElement** ElementList(int n) { return &(m_nel[n][0]);}\n\n\tbool empty() { return m_nel.empty(); }\n\nprotected:\n\tstd::vector< std::vector<FEElement*> >\tm_nel;\n};\n"
  },
  {
    "path": "FECore/FENodeList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#include \"stdafx.h\"\n#include \"FENodeList.h\"\n#include \"FEMesh.h\"\n#include \"DumpStream.h\"\n\nFENodeList::FENodeList(FEMesh* mesh) : m_mesh(mesh)\n{\n\n}\n\nFENodeList::FENodeList(const FENodeList& nodeList)\n{\n\tm_mesh = nodeList.m_mesh;\n\tm_nodes = nodeList.m_nodes;\n}\n\nFENodeList& FENodeList::operator = (const FENodeList& nodeList)\n{\n\tm_mesh = nodeList.m_mesh;\n\tm_nodes = nodeList.m_nodes;\n\treturn *this;\n}\n\nvoid FENodeList::Add(int n)\n{\n\tassert(m_mesh);\n\tassert((n >= 0) && (n < m_mesh->Nodes()));\n\tm_nodes.push_back(n);\n}\n\nvoid FENodeList::Add(const std::vector<int>& nodeList)\n{\n\tassert(m_mesh);\n\tm_nodes.insert(m_nodes.end(), nodeList.begin(), nodeList.end());\n}\n\nvoid FENodeList::Add(const FENodeList& nodeList)\n{\n\tassert(m_mesh == nodeList.m_mesh);\n\tAdd(nodeList.m_nodes);\n}\n\nvoid FENodeList::Clear()\n{\n\tm_nodes.clear();\n}\n\nFENode* FENodeList::Node(int i)\n{\n\tassert(m_mesh);\n\treturn &m_mesh->Node(m_nodes[i]);\n}\n\nconst FENode* FENodeList::Node(int i) const\n{\n\tassert(m_mesh);\n\treturn &m_mesh->Node(m_nodes[i]);\n}\n\nint FENodeList::Size() const\n{\n\treturn (int)m_nodes.size();\n}\n\nvoid FENodeList::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow() == false) ar & m_mesh;\n\tar & m_nodes;\n}\n\nint FENodeList::GlobalToLocalID(int globalId) const\n{\n\tfor (int i = 0; i < m_nodes.size(); ++i)\n\t{\n\t\tif (m_nodes[i] == globalId) return i;\n\t}\n\treturn -1;\n}\n"
  },
  {
    "path": "FECore/FENodeList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass FEMesh;\nclass FENode;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n// Defines an array of node indices.\nclass FECORE_API FENodeList\n{\npublic:\n\tFENodeList(FEMesh* mesh = nullptr);\n\tFENodeList(const FENodeList& nodeList);\n\tFENodeList& operator = (const FENodeList& nodeList);\n\n\tint Size() const;\n\n\tvoid Add(int n);\n\tvoid Add(const std::vector<int>& nodeList);\n\tvoid Add(const FENodeList& nodeList);\n\n\tvoid Clear();\n\n\tint operator[](int n) const { return m_nodes[n]; }\n\tFENode* Node(int i);\n\tconst FENode* Node(int i) const;\n\n\tvoid Serialize(DumpStream& ar);\n\n\tFEMesh* GetMesh() { return m_mesh; }\n\n\t// returns the local index from a global (mesh based) node index, \n\t// or -1 if the node is not part of the set.\n\tint GlobalToLocalID(int globalId) const;\n\nprivate:\n\tFEMesh*\t\t\t\tm_mesh;\n\tstd::vector<int>\tm_nodes;\n};\n"
  },
  {
    "path": "FECore/FENodeNodeList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENodeNodeList.h\"\n#include \"FENodeElemList.h\"\n#include \"FEMesh.h\"\n#include \"FEDomain.h\"\n#include \"FESurface.h\"\n#include <stdlib.h>\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFENodeNodeList::FENodeNodeList()\n{\n\n}\n\nFENodeNodeList::~FENodeNodeList()\n{\n\n}\n\nFENodeNodeList* FENodeNodeList::m_pthis = 0;\n\n//////////////////////////////////////////////////////////////////////\n// FENodeNodeList\n//////////////////////////////////////////////////////////////////////\n\nvoid FENodeNodeList::Create(FEMesh& mesh)\n{\n\tint i, j, k, n, m;\n\n\t// get the nr of nodes\n\tint NN = mesh.Nodes();\n\n\t// create the node-element list\n\tFENodeElemList EL; \n\tEL.Create(mesh);\n\n\t// create the nodal tag array\n\tvector<int> tag; tag.assign(NN, 0);\n\n\t// calculate nodal valences\n\tm_nval.assign(NN, 0);\n\tm_pn.resize(NN);\n\n\tint nsize = 0;\n\tint* en;\n\tvector<int> buf(NN);\n\tint nb;\n\tfor (i=0; i<NN; ++i)\n\t{\n\t\tnb = 0;\n\t\tn = EL.Valence(i);\n\t\tFEElement** pe = EL.ElementList(i);\n\t\tfor (j=0; j<n; ++j)\n\t\t{\n\t\t\tFEElement* pel = pe[j];\n\t\t\tm = pel->Nodes();\n\t\t\ten = &pel->m_node[0];\n\t\t\tfor (k=0; k<m; ++k)\n\t\t\t\tif ((en[k] != i) && (tag[ en[k] ] == 0))\n\t\t\t\t{\n\t\t\t\t\t++m_nval[i];\n\t\t\t\t\t++tag[en[k]];\n\t\t\t\t\tbuf[nb++] = en[k];\n\t\t\t\t\t++nsize;\n\t\t\t\t}\n\t\t}\n\n\t\t// clear the tag array\n\t\tfor (j=0; j<nb; ++j) tag[ buf[j] ] = 0;\n\t\tnb = 0;\n\t}\n\n\t// create the node reference array\n\tm_nref.resize(nsize);\n\n\t// set nref pointers\n\tm_pn[0] = 0;\n\tfor (i=1; i<NN; ++i)\n\t{\n\t\tm_pn[i] = m_pn[i-1] + m_nval[i-1];\n\t}\n\n\t// reset valence pointers\n\tfor (i=0; i<NN; ++i) m_nval[i] = 0;\n\n\t// fill the nref pointers\n\tfor (i=0; i<NN; ++i)\n\t{\n\t\tnb = 0;\n\t\tn = EL.Valence(i);\n\t\tFEElement** pe = EL.ElementList(i);\n\t\tfor (j=0; j<n; ++j)\n\t\t{\n\t\t\tFEElement* pel = pe[j];\n\t\t\tm = pel->Nodes();\n\t\t\ten = &pel->m_node[0];\n\t\t\tfor (k=0; k<m; ++k)\n\t\t\t\tif ((en[k] != i) && (tag[ en[k] ] == 0))\n\t\t\t\t{\n\t\t\t\t\tm_nref[m_pn[i] + m_nval[i]] = en[k];\t\n\n\t\t\t\t\t++tag[en[k]];\n\t\t\t\t\t++m_nval[i];\n\t\t\t\t\tbuf[nb++] = en[k];\n\t\t\t\t\t++nsize;\n\t\t\t\t}\n\t\t}\n\n\t\t// clear the tag array\n\t\tfor (j=0; j<nb; ++j) tag[ buf[j] ] = 0;\n\t\tnb = 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeNodeList::Create(FEDomain& dom)\n{\n\tint i, j, k, n, m;\n\n\t// get the mesh\n\tFEMesh& mesh = *dom.GetMesh();\n\n\t// get the nr of nodes\n\tint NN = mesh.Nodes();\n\n\t// create the node-element list\n\tFENodeElemList EL; \n\tEL.Create(dom);\n\n\t// create the nodal tag array\n\tvector<int> tag; tag.assign(NN, 0);\n\n\t// calculate nodal valences\n\tm_nval.assign(NN, 0);\n\tm_pn.resize(NN);\n\n\tint nsize = 0;\n\tint* en;\n\tvector<int> buf(NN);\n\tint nb;\n\tfor (i=0; i<NN; ++i)\n\t{\n\t\tnb = 0;\n\t\tn = EL.Valence(i);\n\t\tFEElement** pe = EL.ElementList(i);\n\t\tfor (j=0; j<n; ++j)\n\t\t{\n\t\t\tFEElement* pel = pe[j];\n\t\t\tm = pel->Nodes();\n\t\t\ten = &pel->m_node[0];\n\t\t\tfor (k=0; k<m; ++k)\n\t\t\t\tif ((en[k] != i) && (tag[ en[k] ] == 0))\n\t\t\t\t{\n\t\t\t\t\t++m_nval[i];\n\t\t\t\t\t++tag[en[k]];\n\t\t\t\t\tbuf[nb++] = en[k];\n\t\t\t\t\t++nsize;\n\t\t\t\t}\n\t\t}\n\n\t\t// clear the tag array\n\t\tfor (j=0; j<nb; ++j) tag[ buf[j] ] = 0;\n\t\tnb = 0;\n\t}\n\n\t// create the node reference array\n\tm_nref.resize(nsize);\n\n\t// set nref pointers\n\tm_pn[0] = 0;\n\tfor (i=1; i<NN; ++i)\n\t{\n\t\tm_pn[i] = m_pn[i-1] + m_nval[i-1];\n\t}\n\n\t// reset valence pointers\n\tfor (i=0; i<NN; ++i) m_nval[i] = 0;\n\n\t// fill the nref pointers\n\tfor (i=0; i<NN; ++i)\n\t{\n\t\tnb = 0;\n\t\tn = EL.Valence(i);\n\t\tFEElement** pe = EL.ElementList(i);\n\t\tfor (j=0; j<n; ++j)\n\t\t{\n\t\t\tFEElement* pel = pe[j];\n\t\t\tm = pel->Nodes();\n\t\t\ten = &pel->m_node[0];\n\t\t\tfor (k=0; k<m; ++k)\n\t\t\t\tif ((en[k] != i) && (tag[ en[k] ] == 0))\n\t\t\t\t{\n\t\t\t\t\tm_nref[m_pn[i] + m_nval[i]] = en[k];\t\n\n\t\t\t\t\t++tag[en[k]];\n\t\t\t\t\t++m_nval[i];\n\t\t\t\t\tbuf[nb++] = en[k];\n\t\t\t\t\t++nsize;\n\t\t\t\t}\n\t\t}\n\n\t\t// clear the tag array\n\t\tfor (j=0; j<nb; ++j) tag[ buf[j] ] = 0;\n\t\tnb = 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeNodeList::Create(FESurface& surf)\n{\n\t// get the nr of nodes\n\tint NN = surf.Nodes();\n\n\t// create the node-element list\n\tFENodeElemList EL;\n\tEL.Create(surf);\n\n\t// create the nodal tag array\n\tvector<int> tag; tag.assign(NN, 0);\n\n\t// calculate nodal valences\n\tm_nval.assign(NN, 0);\n\tm_pn.resize(NN);\n\n\tint nsize = 0;\n\tint* en;\n\tvector<int> buf(NN);\n\tint nb;\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tnb = 0;\n\t\tint n = EL.Valence(i);\n\t\tFEElement** pe = EL.ElementList(i);\n\t\tfor (int j = 0; j < n; ++j)\n\t\t{\n\t\t\tFEElement* pel = pe[j];\n\t\t\tint m = pel->Nodes();\n\t\t\ten = &pel->m_lnode[0];\n\t\t\tfor (int k = 0; k < m; ++k)\n\t\t\t\tif ((en[k] != i) && (tag[en[k]] == 0))\n\t\t\t\t{\n\t\t\t\t\t++m_nval[i];\n\t\t\t\t\t++tag[en[k]];\n\t\t\t\t\tbuf[nb++] = en[k];\n\t\t\t\t\t++nsize;\n\t\t\t\t}\n\t\t}\n\n\t\t// clear the tag array\n\t\tfor (int j = 0; j < nb; ++j) tag[buf[j]] = 0;\n\t\tnb = 0;\n\t}\n\n\t// create the node reference array\n\tm_nref.resize(nsize);\n\n\t// set nref pointers\n\tm_pn[0] = 0;\n\tfor (int i = 1; i < NN; ++i)\n\t{\n\t\tm_pn[i] = m_pn[i - 1] + m_nval[i - 1];\n\t}\n\n\t// reset valence pointers\n\tfor (int i = 0; i < NN; ++i) m_nval[i] = 0;\n\n\t// fill the nref pointers\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tnb = 0;\n\t\tint n = EL.Valence(i);\n\t\tFEElement** pe = EL.ElementList(i);\n\t\tfor (int j = 0; j < n; ++j)\n\t\t{\n\t\t\tFEElement* pel = pe[j];\n\t\t\tint m = pel->Nodes();\n\t\t\ten = &pel->m_lnode[0];\n\t\t\tfor (int k = 0; k < m; ++k)\n\t\t\t\tif ((en[k] != i) && (tag[en[k]] == 0))\n\t\t\t\t{\n\t\t\t\t\tm_nref[m_pn[i] + m_nval[i]] = en[k];\n\n\t\t\t\t\t++tag[en[k]];\n\t\t\t\t\t++m_nval[i];\n\t\t\t\t\tbuf[nb++] = en[k];\n\t\t\t\t\t++nsize;\n\t\t\t\t}\n\t\t}\n\n\t\t// clear the tag array\n\t\tfor (int j = 0; j < nb; ++j) tag[buf[j]] = 0;\n\t\tnb = 0;\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\nint FENodeNodeList::compare(const void* e1, const void* e2)\n{\n\tint n1 = *((int*) e1);\n\tint n2 = *((int*) e2);\n\n\tFENodeNodeList& L = *m_pthis;\n\n\treturn (L.Valence(n1) - L.Valence(n2));\n}\n\nvoid FENodeNodeList::Sort()\n{\n\tint n, *pn;\n\tm_pthis = this;\n\tfor (int i=0; i<Size(); ++i)\n\t{\n\t\tn = Valence(i);\n\t\tpn = NodeList(i);\n\t\tqsort(pn, n, sizeof(int), compare);\n\t}\n}\n"
  },
  {
    "path": "FECore/FENodeNodeList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n\nclass FEMesh;\nclass FEDomain;\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n//! The FENodeNodeList class is a utility class that determines for each node \n//! the adjacent nodes\n\n//! This class analyzes a mesh and finds for each node all nodes that are \n//! adjacent to this node\n\nclass FECORE_API FENodeNodeList\n{\npublic:\n\t//! default constructor\n\tFENodeNodeList();\n\n\t//! desctructor\n\tvirtual ~FENodeNodeList();\n\n\t//! create the node-node list for a mesh\n\tvoid Create(FEMesh& mesh);\n\n\t//! create the node-node list for a domain\n\tvoid Create(FEDomain& dom);\n\n\t//! create the node-node list for a surface\n\tvoid Create(FESurface& surf);\n\n\tint Size() const { return (int) m_nval.size(); }\n\n\tint Valence(int i) { return m_nval[i]; }\n\tint* NodeList(int i) { return &m_nref[0] + m_pn[i]; }\n\n\tvoid Sort();\n\nprotected:\n\tstd::vector<int>\tm_nval;\t// nodal valences\n\tstd::vector<int>\tm_nref;\t// adjacent nodes indices\n\tstd::vector<int>\tm_pn;\t// start index into the nref array\n\n\tstatic FENodeNodeList*\tm_pthis;\n\tstatic int compare(const void* e1, const void* e2);\n};\n"
  },
  {
    "path": "FECore/FENodeReorder.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENodeReorder.h\"\n#include \"FEMesh.h\"\n#include <stack>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n\nFENodeReorder::FENodeReorder()\n{\n\n}\n\nFENodeReorder::~FENodeReorder()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! This function applies the node-reordering algorithm to a particular mesh.\n//! The new node number is stored in P. To be precise, P stores for each new\n//! node the old node that corresponds to this node.\n\nvoid FENodeReorder::Apply(FEMesh& mesh, vector<int>& P)\n{\n\tint i, j, n, l, m;\n\tint* pn;\n\n\t// get the nr of nodes\n\tint N = mesh.Nodes();\n\n\t// initialize the permutation vector\n\t// Set all entries to -1 to indicate that\n\t// no node has been given a new number yet.\n\tvector<int> Q; Q.assign(N, -1);\n\n\t// create the node-node list\n\t// this list stores for each node of the mesh\n\t// a list of nodes that are adjacent to it.\n\tFENodeNodeList NL;\n\tNL.Create(mesh);\n\n\t// sort the nodelist in order of increasing degree\n\tNL.Sort();\n\n\t// Levelstructures are used to group all nodes\n\t// in levels\n\tFELevelStructure Lv, Lu, Ls, L;\n\n\t// this swap variable will tell you if \n\t// the node numbering needs to be reversed at the end\n\tbool bswap;\n\n\t// The mesh may comprise of several disconnected\n\t// components. We therefor have to apply the algorithm\n\t// several times until all components are processed.\n\tint neq = 0, neq0;\n\twhile (true)\n\t{\n\t\t// To identify an unprocessed component we try to\n\t\t// find an unprocessed node (nv). An unprocessed\n\t\t// node will have Q[i]<0. We also require this node \n\t\t// to be of minimal degree.\n\t\tint valmin = 0x7fffffff;\t// initialize the min valence to a ridiculously large value\n\t\tint nv = -1, nu;\n\t\tint nval;\n\t\tfor (i=0; i<N; ++i)\n\t\t{\n\t\t\tnval = NL.Valence(i);\n\t\t\tif ((Q[i] < 0) && (nval < valmin)) \n\t\t\t{ \n\t\t\t\tvalmin = nval;\n\t\t\t\tnv = i; \n\t\t\t}\n\t\t}\n\n\t\t// if we havn't found an unprocessed node, we can stop\n\t\tif (nv == -1) break;\n\n\t\t// store the starting equation number\n\t\t// we need this in case we need to reverse the \n\t\t// node ordering\n\t\tneq0 = neq;\n\n\t\t// we now will find the pseudo-diameter of the node graph (i.e. NodeNodeList),\n\t\t// which identifies to nodes at the end of the diameter,\n\t\t// namely nv, and nu. We will require the level structure for\n\t\t// nu to be of minimal width, which is stored in wmin\n\t\tint wmin = 0x7fffffff;\n\n\t\t// Let's find the pseudo-diameter\n\t\t// if we found one bok will be true\n\t\tbool bok;\n\t\tdo\n\t\t{\n\t\t\t// let's be optimistic\n\t\t\tbok = true;\n\n\t\t\t// create a level structure rooted at nv\n\t\t\tLv.Create(NL, nv);\n\n\t\t\t// sort the last level in order of increasing degree\n\t\t\tl = Lv.Depth() - 1;\n\t\t\tLv.SortLevels(l, l);\n\n\t\t\t// get the list of nodes at the last level of Lv\n\t\t\tn = Lv.Valence(l);\n\t\t\tpn = Lv.NodeList(l);\n\t\t\t// loop over all the nodes\n\t\t\tfor (i=0; i<n; ++i)\n\t\t\t{\n\t\t\t\t// get a possible candidate for nu\n\t\t\t\tnu = pn[i];\n\n\t\t\t\t// create a level structure rooted at u\n\t\t\t\tLs.Create(NL, nu);\n\n\t\t\t\t// make sure that the depth of Ls is not\n\t\t\t\t// greater than that of Lv\n\t\t\t\tif (Ls.Depth() > Lv.Depth())\n\t\t\t\t{\n\t\t\t\t\t// Oh, oh, the depth of Ls is greater than\n\t\t\t\t\t// that of nv, so replace nv with nu and try\n\t\t\t\t\t// again.\n\t\t\t\t\tnv = nu;\n\t\t\t\t\twmin = 0x7fffffff;\t// reset the min width\n\t\t\t\t\tbok = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// store the level stucture with minimal width\n\t\t\t\t\tif (Ls.Width() < wmin)\n\t\t\t\t\t{\n\t\t\t\t\t\tLu = Ls;\t// store the level structure\n\t\t\t\t\t\twmin = Ls.Width();\t// store the minimum width\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\twhile (!bok);\n\n\t\t// we have found the pseudo-diamter u,v\n\t\t// Next, merge the level structures into one\n\t\t// This new level structure will usually have a width\n\t\t// that is smaller than either Lv or Lu.\n\t\t// make sure we start with the node of lowest degree\n\t\tif (NL.Valence(nu) < NL.Valence(nv))\n\t\t{\n\t\t\t// swap u and v\n\t\t\tnu ^= nv;\n\t\t\tnv ^= nu;\n\t\t\tnu ^= nv;\n\t\t\tL.Merge(Lu, Lv, bswap);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tL.Merge(Lv, Lu, bswap);\n\t\t\tbswap = !bswap;\n\t\t}\n\n\t\t// sort all levels of L in order of increasing degree\n\t\tL.SortLevels(0, L.Depth()-1);\n\n\t\t// get the width of L. This width is the largest width\n\t\t// of any level of L\n\t\tint lmax = L.Width();\n\n\t\t// the following stack and vectors will assist us in\n\t\t// renumbering the nodes.\n\t\tstack<int> NQ;\n\t\tvector<int> Vi(0, lmax);\n\t\tvector<int> Vip1(0, lmax);\n\n\t\t// Using the level structure L we can now go ahead and define\n\t\t// the new node numbering. The basic idea is to loop over all\n\t\t// levels and assign the node numbers level by level. In each\n\t\t// level the numbers adjacent to the lowest number node in the same\n\t\t// level gets numbered first. Then the nodes adjacent to the lowest\n\t\t// numbered nodes in the previous level gets numbered next.\n\t\tint nw;\n\t\tQ[nv] = neq++;\n\t\tVi.push_back(nv);\n\t\tfor (l=0; l<L.Depth(); ++l)\n\t\t{\n\t\t\t// assign numbers to level l\n\t\t\t// The Vi array stores the nodes of level l that have been\n\t\t\t// numbered in order of increasing node number.\n\t\t\t// We loop over all these nodes and assign a \n\t\t\t// node number to unnumbered adjacent nodes until\n\t\t\t// all nodes of level l have been numbered \n\t\t\tfor(i=0; i<(int) Vi.size(); ++i)\n\t\t\t{\n\t\t\t\tnw = Vi[i];\n\n\t\t\t\t// loop over all unassigned nodes adjacent to nw\n\t\t\t\tn = NL.Valence(nw);\n\t\t\t\tpn = NL.NodeList(nw);\n\t\t\t\tfor (j=0; j<n; ++j)\n\t\t\t\t{\n\t\t\t\t\tm = pn[j];\n\t\t\t\t\tif ((L.NodeLevel(m) == l) && (Q[m] < 0))\n\t\t\t\t\t{\n\t\t\t\t\t\tQ[m] = neq++;\n\t\t\t\t\t\tVi.push_back(m);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If we are about to terminate the loop make sure\n\t\t\t\t// that all nodes of level l have been assigned \n\t\t\t\t// a new node number\n\t\t\t\tif (i==(Vi.size() - 1))\n\t\t\t\t{\n\t\t\t\t\t// check to see if all nodes in level l are numbered\n\t\t\t\t\tn = L.Valence(l);\n\t\t\t\t\tpn = L.NodeList(l);\n\t\t\t\t\tfor (j=0; j<n; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tm = pn[j];\n\t\t\t\t\t\tif (Q[m] < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Aha, we found an unnumbered node in this level\n\t\t\t\t\t\t\t// so place in the array an continue the loop over i\n\t\t\t\t\t\t\tVi.push_back(m);\n\t\t\t\t\t\t\tQ[m] = neq++;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If this is not the last level, we go ahead and number\n\t\t\t// all nodes in level l+1 that are adjacent to numbered nodes\n\t\t\t// of level l\n\t\t\tif (l<L.Depth()-1)\n\t\t\t{\n\t\t\t\tVip1.clear();\n\n\t\t\t\t// assign numbers to the next level\n\t\t\t\tfor (i=0; i<(int) Vi.size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tnw = Vi[i];\n\t\t\t\t\tn = NL.Valence(nw);\n\t\t\t\t\tpn = NL.NodeList(nw);\n\t\t\t\t\tfor (j=0; j<n; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tm = pn[j];\n\t\t\t\t\t\tif ((L.NodeLevel(m) == l+1) && (Q[m] < 0))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ[m] = neq++;\n\t\t\t\t\t\t\tVip1.push_back(m);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// copy the numbered nodes of level l+1 into Vi\n\t\t\t\tVi = Vip1;\n\t\t\t}\n\t\t}\n\n\t\t// we have assigned new node numbers \n\t\t// see if we need to invert the numbering\n\t\tif (bswap)\n\t\t{\n\t\t\tfor (i=0; i<N; ++i) \n\t\t\t{\n\t\t\t\tif (Q[i] >= neq0)\n\t\t\t\t\tQ[i] = neq - 1 - Q[i] + neq0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// The Q array stores for each old node its new node number\n\t// but we actually need the inverse permutation. That is, for\n\t// each new node, the old node number that is associated with it.\n\t// This array is stored in P.\n\tP.resize(N);\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tP[Q[i]] = i;\n\t}\n}\n"
  },
  {
    "path": "FECore/FENodeReorder.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FELevelStructure.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements an algoritm that calculates a permutation of \n//! the node numbering in order to obtain a bandwidth reduced stiffness matrix\n\n//! The algorithm comes from \"An algorithm for reducing the bandwidth and \n//! profile of a sparse matrix\", by N.E.Gibbs e.a. It applies the algorithm\n//! on the node numberings in stead of the actual sparse matrix since that\n//! was easier to implement :). In the future I would like to extend it to\n//! work with the actual sparse matrix. \n\nclass FECORE_API FENodeReorder\n{\n\npublic:\n\t//! default constructor\n\tFENodeReorder();\n\n\t//! destructor\n\tvirtual ~FENodeReorder();\n\n\t//! calculates the permutation vector\n\tvoid Apply(FEMesh& m, std::vector<int>& P);\n};\n"
  },
  {
    "path": "FECore/FENodeSet.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENodeSet.h\"\n#include \"FEMesh.h\"\n#include \"DumpStream.h\"\n#include \"FEModel.h\"\n\n//=============================================================================\n// FENodeSet\n//-----------------------------------------------------------------------------\nFENodeSet::FENodeSet(FEModel* fem) : FEItemList(fem, FEItemList::FE_ELEMENT_SET), m_Node(fem ? &fem->GetMesh() : nullptr)\n{\n\tif (fem) SetMesh(&fem->GetMesh());\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeSet::Add(int id)\n{\n\tm_Node.Add(id);\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeSet::Add(const std::vector<int>& ns)\n{\n\tm_Node.Add(ns);\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeSet::Add(const FENodeList& ns)\n{\n\tm_Node.Add(ns);\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeSet::Clear()\n{\n\tm_Node.Clear();\n}\n\n//-----------------------------------------------------------------------------\nFENode* FENodeSet::Node(int i)\n{\n\tFEMesh* mesh = GetMesh();\n\treturn (mesh ? &mesh->Node(m_Node[i]) : nullptr);\n}\n\n//-----------------------------------------------------------------------------\nconst FENode* FENodeSet::Node(int i) const\n{\n\tFEMesh* mesh = GetMesh();\n\treturn (mesh ? &mesh->Node(m_Node[i]) : nullptr);\n}\n\n//-----------------------------------------------------------------------------\nvoid FENodeSet::Serialize(DumpStream& ar)\n{\n\tFEItemList::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Node;\n}\n\nvoid FENodeSet::SaveClass(DumpStream& ar, FENodeSet* p)\n{\n}\n\nFENodeSet* FENodeSet::LoadClass(DumpStream& ar, FENodeSet* p)\n{\n\tp = new FENodeSet(&ar.GetFEModel());\n\treturn p;\n}\n"
  },
  {
    "path": "FECore/FENodeSet.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"FEItemList.h\"\n#include \"FENodeList.h\"\n#include <vector>\n#include <string>\n\n//-----------------------------------------------------------------------------\n// Forward declarations\nclass FEMesh;\nclass FENode;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! Defines a node set of the model\n//\nclass FECORE_API FENodeSet : public FEItemList\n{\npublic:\n\tFENodeSet(FEModel* fem);\n\n\tvoid Add(int n);\n\tvoid Add(const std::vector<int>& ns);\n\tvoid Add(const FENodeList& nodeList);\n\n\tvoid Clear();\n\n\tint Size() const { return m_Node.Size(); }\n\n\tint operator [] (int i) const { return m_Node[i]; }\n\n\tFENodeList GetNodeList() { return m_Node; }\n\tconst FENodeList& GetNodeList() const { return m_Node; }\n\n\tFENode* Node(int i);\n\tconst FENode* Node(int i) const;\n\npublic:\n\tvoid Serialize(DumpStream& ar);\n\tstatic void SaveClass(DumpStream& ar, FENodeSet* p);\n\tstatic FENodeSet* LoadClass(DumpStream& ar, FENodeSet* p);\n\nprotected:\n\tFENodeList\tm_Node;\t\t//!< list of nodes\n};\n"
  },
  {
    "path": "FECore/FENodeSetConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENodeSetConstraint.h\"\n\nFENodeSetConstraint::FENodeSetConstraint(FEModel* fem) : FENLConstraint(fem)\n{\n}\n"
  },
  {
    "path": "FECore/FENodeSetConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENLConstraint.h\"\n\nclass FENodeSet;\n\n// Base class for nonlinear constraints that are defined using a node set.\nclass FECORE_API FENodeSetConstraint : public FENLConstraint\n{\npublic:\n    FENodeSetConstraint(FEModel* fem);\n\n    // return the surface\n    virtual FENodeSet* GetNodeSet() { return 0; }\n};\n"
  },
  {
    "path": "FECore/FENormalProjection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FENormalProjection.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFENormalProjection::FENormalProjection(FESurface& s) : m_surf(s)\n{\n\tm_tol = 0.0;\n\tm_rad = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FENormalProjection::Init()\n{\n\tm_OT.Attach(&m_surf);\n\tm_OT.Init(m_tol);\n}\n\n//-----------------------------------------------------------------------------\n//! This function finds the element which is intersected by the ray (r,n).\n//! It returns a pointer to the element, as well as the isoparametric coordinates\n//! of the intersection point. It searches for the closest patch based on\n//! algebraic value of the gap function\n//!\nFESurfaceElement* FENormalProjection::Project(vec3d r, vec3d n, double rs[2])\n{\n\t// let's find all the candidate surface elements\n\tset<int>selist;\n\tm_OT.FindCandidateSurfaceElements(r, n, selist, m_rad);\n\t\n\t// now that we found candidate surface elements, lets see if we can find \n\t// those that intersect the ray, then pick the closest intersection\n\tset<int>::iterator it;\n\tbool found = false;\n\tdouble rsl[2], gl, g = 0;\n\tFESurfaceElement* pei = 0;\n\tfor (it=selist.begin(); it!=selist.end(); ++it) {\n\t\t// get the surface element\n\t\tint j = *it;\n\t\t// project the node on the element\n\t\tFESurfaceElement* pe = &m_surf.Element(j);\n\t\tif (pe->isActive())\n\t\t{\n\t\t\tif (m_surf.Intersect(*pe, r, n, rsl, gl, m_tol)) {\n\t\t\t\tif ((!found) && (gl > -m_rad)) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tg = gl;\n\t\t\t\t\trs[0] = rsl[0];\n\t\t\t\t\trs[1] = rsl[1];\n\t\t\t\t\tpei = pe;\n\t\t\t\t}\n\t\t\t\telse if ((gl < g) && (gl > -m_rad)) {\n\t\t\t\t\tg = gl;\n\t\t\t\t\trs[0] = rsl[0];\n\t\t\t\t\trs[1] = rsl[1];\n\t\t\t\t\tpei = pe;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (found) return pei;\n\t\n\t// we did not find a surface\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! This function finds the element which is intersected by the ray (r,n).\n//! It returns a pointer to the element, as well as the isoparametric coordinates\n//! of the intersection point.  It searches for the closest patch based on\n//! the absolute value of the gap function\n//!\nFESurfaceElement* FENormalProjection::Project2(vec3d r, vec3d n, double rs[2])\n{\n\t// let's find all the candidate surface elements\n\tset<int>selist;\n\tm_OT.FindCandidateSurfaceElements(r, n, selist, m_rad);\n\t\n\t// now that we found candidate surface elements, lets see if we can find \n\t// those that intersect the ray, then pick the closest intersection\n\tset<int>::iterator it;\n\tbool found = false;\n\tdouble rsl[2], gl, g = 0;\n\tFESurfaceElement* pei = 0;\n\tfor (it=selist.begin(); it!=selist.end(); ++it) {\n\t\t// get the surface element\n\t\tint j = *it;\n\t\tFESurfaceElement* pe = &m_surf.Element(j);\n\t\t// project the node on the element\n\t\tif (m_surf.Intersect(*pe, r, n, rsl, gl, m_tol)) {\n\t\t\tif ((!found) && (fabs(gl) < m_rad)) {\n\t\t\t\tfound = true;\n\t\t\t\tg = gl;\n\t\t\t\trs[0] = rsl[0];\n\t\t\t\trs[1] = rsl[1];\n\t\t\t\tpei = pe;\n\t\t\t} else if ((fabs(gl) < m_rad) && (fabs(gl) < fabs(g))) {\n\t\t\t\tg = gl;\n\t\t\t\trs[0] = rsl[0];\n\t\t\t\trs[1] = rsl[1];\n\t\t\t\tpei = pe;\n\t\t\t}\n\t\t}\n\t}\n\tif (found) return pei;\n\t\n\t// we did not find a surface\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! This function finds the element which is intersected by the ray (r,n).\n//! It returns a pointer to the element, as well as the isoparametric coordinates\n//! of the intersection point.\n//!\nFESurfaceElement* FENormalProjection::Project3(const vec3d& r, const vec3d& n, double rs[2], int* pei)\n{\n\t// let's find all the candidate surface elements\n\tset<int>selist;\n\tm_OT.FindCandidateSurfaceElements(r, n, selist, m_rad);\n\n\tdouble g, gmax = -1e99, r2[2] = {rs[0], rs[1]};\n\tint imin = -1;\n\tFESurfaceElement* pme = 0;\n\n\t// loop over all surface element\n\tset<int>::iterator it;\n\tfor (it = selist.begin(); it != selist.end(); ++it)\n\t{\n\t\tFESurfaceElement& el = m_surf.Element(*it);\n\n\t\t// see if the ray intersects this element\n\t\tif (m_surf.Intersect(el, r, n, r2, g, m_tol))\n\t\t{\n\t\t\t// see if this is the best intersection found so far\n\t\t\t// TODO: should I put a limit on how small g can\n\t\t\t//       be to be considered a valid intersection?\n//\t\t\tif (g < gmin)\n\t\t\tif (g > gmax)\n\t\t\t{\n\t\t\t\t// keep results\n\t\t\t\tpme = &el;\n//\t\t\t\tgmin = g;\n\t\t\t\tgmax = g;\n\t\t\t\timin = *it;\n\t\t\t\trs[0] = r2[0];\n\t\t\t\trs[1] = r2[1];\n\t\t\t}\n\t\t}\t\n\t}\n\n\tif (pei) *pei = imin;\n\n\t// return the intersected element (or zero if none)\n\treturn pme;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FENormalProjection::Project(const vec3d& x, const vec3d& N)\n{\n\tdouble rs[2];\n\tFESurfaceElement* pe = FENormalProjection::Project3(x, N, rs);\n\tif (pe)\n\t{\n\t\tFEMesh& mesh = *m_surf.GetMesh();\n\t\tvec3d r[FEElement::MAX_NODES];\n\t\tfor (int i=0; i<pe->Nodes(); ++i) r[i] = mesh.Node(pe->m_node[i]).m_rt;\n\t\tvec3d q = pe->eval(r, rs[0], rs[1]);\n\t\treturn q;\n\t}\n\telse return x;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FENormalProjection::Project2(const vec3d& x, const vec3d& N)\n{\n\tdouble rs[2];\n\tFESurfaceElement* pe = FENormalProjection::Project3(x, N, rs);\n\tif (pe == nullptr) pe = FENormalProjection::Project3(x, -N, rs);\n\n\tif (pe)\n\t{\n\t\tFEMesh& mesh = *m_surf.GetMesh();\n\t\tvec3d r[FEElement::MAX_NODES];\n\t\tfor (int i = 0; i<pe->Nodes(); ++i) r[i] = mesh.Node(pe->m_node[i]).m_rt;\n\t\tvec3d q = pe->eval(r, rs[0], rs[1]);\n\t\treturn q;\n\t}\n\telse return x;\n}\n"
  },
  {
    "path": "FECore/FENormalProjection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FESurface.h\"\n#include \"FEOctree.h\"\n\n//-----------------------------------------------------------------------------\n//! This class calculates the normal projection on to a surface.\n//! This is used by some contact algorithms.\nclass FECORE_API FENormalProjection\n{\npublic:\n\t//! constructor\n\tFENormalProjection(FESurface& s);\n\n\t// initialization\n\tvoid Init();\n\n\tvoid SetTolerance(double tol) { m_tol = tol; }\n\tvoid SetSearchRadius(double srad) { m_rad = srad; }\n\npublic:\n\t//! find the intersection of a ray with the surface\n\tFESurfaceElement* Project(vec3d r, vec3d n, double rs[2]);\n\tFESurfaceElement* Project2(vec3d r, vec3d n, double rs[2]);\n\tFESurfaceElement* Project3(const vec3d& r, const vec3d& n, double rs[2], int* pei = 0);\n\n\tvec3d Project(const vec3d& r, const vec3d& N);\n\tvec3d Project2(const vec3d& r, const vec3d& N);\n\nprivate:\n\tdouble\tm_tol;\t//!< projection tolerance\n\tdouble\tm_rad;\t//!< search radius\n\nprivate:\n\tFESurface&\tm_surf;\t//!< the target surface\n\tFEOctree\tm_OT;\t//!< used to optimize ray-surface intersections\n};\n"
  },
  {
    "path": "FECore/FEOctree.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEOctree.h\"\n#include \"FESurface.h\"\n#include \"FEMesh.h\"\n\nOTnode::OTnode()\n{\n\tm_ps = nullptr;\n\tlevel = 0;\n}\n\nOTnode::~OTnode()\n{\n}\n\nvoid OTnode::Clear()\n{ \n\tchildren.clear(); \n}\n\n//-----------------------------------------------------------------------------\n// Create the eight children of an octree node and find their contents\n\nvoid OTnode::CreateChildren(const int max_level, const int max_elem)\n{\n\tvec3d dc = (cmax - cmin)/2;\n\tfor (int i=0; i<=1; ++i) {\n\t\tfor (int j=0; j<=1; ++j) {\n\t\t\tfor (int k=0; k<=1; ++k) {\n\t\t\t\tOTnode node;\n\t\t\t\tnode.m_ps = m_ps;\n\t\t\t\t// evaluate bounding box by subdividing parent node box\n\t\t\t\tnode.cmin = vec3d(cmin.x+i*dc.x,\n\t\t\t\t\t\t\t\t  cmin.y+j*dc.y,\n\t\t\t\t\t\t\t\t  cmin.z+k*dc.z);\n\t\t\t\tnode.cmax = vec3d(cmax.x-(1-i)*dc.x,\n\t\t\t\t\t\t\t\t  cmax.y-(1-j)*dc.y,\n\t\t\t\t\t\t\t\t  cmax.z-(1-k)*dc.z);\n\t\t\t\t// update octree level\n\t\t\t\tnode.level = level + 1;\n\t\t\t\t// find all surface elements in this child node\n\t\t\t\tnode.FillNode(selist);\n\t\t\t\tif (node.selist.size()) {\n\t\t\t\t\t// use recursion to create children of this node\n\t\t\t\t\t// as long as node contains too many elements\n\t\t\t\t\t// and max octree levels not exceeded\n\t\t\t\t\tif ((node.level < max_level) &&\n\t\t\t\t\t\t(node.selist.size() > max_elem))\n\t\t\t\t\t\tnode.CreateChildren(max_level, max_elem);\n\t\t\t\t}\n\t\t\t\t// store this node\n\t\t\t\tchildren.push_back(node);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Find all surface elements that fall inside a node\n\nvoid OTnode::FillNode(const vector<int>& parent_selist)\n{\n\t// Loop over all surface elements in the parent node\n\tint nsize = (int)parent_selist.size();\n\tfor (int i=0; i<nsize; ++i) {\n\t\tint j = parent_selist[i];\n\t\tif (ElementIntersectsNode(j)) {\n\t\t\t// add this surface element to the current node\n\t\t\tselist.push_back(j);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Determine whether a surface element intersects a node\n\nbool OTnode::ElementIntersectsNode(const int iel)\n{\n\t// Extract FE node coordinates from surface element\n\t// and determine bounding box of surface element\n\tFEMesh& mesh = *(m_ps->GetMesh());\n\tFESurfaceElement& el = m_ps->Element(iel);\n\tvec3d rn = mesh.Node(el.m_node[0]).m_rt;\n\tvec3d fmin = rn;\n\tvec3d fmax = rn;\n\tint N = el.Nodes();\n\tfor (int i=1; i<N; ++i) {\n\t\trn = mesh.Node(el.m_node[i]).m_rt;\n\t\tif (rn.x < fmin.x) fmin.x = rn.x;\n\t\tif (rn.x > fmax.x) fmax.x = rn.x;\n\t\tif (rn.y < fmin.y) fmin.y = rn.y;\n\t\tif (rn.y > fmax.y) fmax.y = rn.y;\n\t\tif (rn.z < fmin.z) fmin.z = rn.z;\n\t\tif (rn.z > fmax.z) fmax.z = rn.z;\n\t}\n\t\n\t// Check if bounding boxes of OT node and surface element overlap\n\tif ((fmax.x < cmin.x) || (fmin.x > cmax.x)) return false;\n\tif ((fmax.y < cmin.y) || (fmin.y > cmax.y)) return false;\n\tif ((fmax.z < cmin.z) || (fmin.z > cmax.z)) return false;\n\t\n\t// At this point we find that bounding boxes overlap.\n\t// Technically that does not prove that the surface element is\n\t// inside the octree node, but any additional check would be\n\t// more expensive.\n\t\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Determine if a ray intersects any of the faces of this node.\n// The ray originates at p and is directed along the unit vector n\n\nbool OTnode::RayIntersectsNode(const vec3d& p, const vec3d& n)\n{\n\t// check intersection with x-faces\n\tif (n.x) {\n\t\t// face passing through cmin\n\t\tdouble t = (cmin.x - p.x)/n.x;\n\t\tdouble y = p.y + t*n.y;\n\t\tdouble z = p.z + t*n.z;\n\t\tif ((y >= cmin.y) && (y <= cmax.y)\n\t\t\t&& (z >= cmin.z) && (z <= cmax.z))\n\t\t\treturn true;\n\t\t// face passing through cmax\n\t\tt = (cmax.x - p.x)/n.x;\n\t\ty = p.y + t*n.y;\n\t\tz = p.z + t*n.z;\n\t\tif ((y >= cmin.y) && (y <= cmax.y)\n\t\t\t&& (z >= cmin.z) && (z <= cmax.z))\n\t\t\treturn true;\n\t}\n\t// check intersection with y-faces\n\tif (n.y) {\n\t\t// face passing through cmin\n\t\tdouble t = (cmin.y - p.y)/n.y;\n\t\tdouble x = p.x + t*n.x;\n\t\tdouble z = p.z + t*n.z;\n\t\tif ((x >= cmin.x) && (x <= cmax.x)\n\t\t\t&& (z >= cmin.z) && (z <= cmax.z))\n\t\t\treturn true;\n\t\t// face passing through cmax\n\t\tt = (cmax.y - p.y)/n.y;\n\t\tx = p.x + t*n.x;\n\t\tz = p.z + t*n.z;\n\t\tif ((x >= cmin.x) && (x <= cmax.x)\n\t\t\t&& (z >= cmin.z) && (z <= cmax.z))\n\t\t\treturn true;\n\t}\n\t// check intersection with z-faces\n\tif (n.z) {\n\t\t// face passing through cmin\n\t\tdouble t = (cmin.z - p.z)/n.z;\n\t\tdouble x = p.x + t*n.x;\n\t\tdouble y = p.y + t*n.y;\n\t\tif ((x >= cmin.x) && (x <= cmax.x)\n\t\t\t&& (y >= cmin.y) && (y <= cmax.y))\n\t\t\treturn true;\n\t\t// face passing through cmax\n\t\tt = (cmax.z - p.z)/n.z;\n\t\tx = p.x + t*n.x;\n\t\ty = p.y + t*n.y;\n\t\tif ((x >= cmin.x) && (x <= cmax.x)\n\t\t\t&& (y >= cmin.y) && (y <= cmax.y))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Find intersected octree leaves and return a set of their surface elements\nvoid OTnode::FindIntersectedLeaves(const vec3d& p, const vec3d& n, set<int>& sel, double srad)\n{\n\t// Check if octree node is within search radius from p.\n\tbool bNodeWithinSRad = ( (cmin.x - srad <= p.x) && (cmax.x + srad >= p.x) &&\n\t                         (cmin.y - srad <= p.y) && (cmax.y + srad >= p.y) &&\n\t                         (cmin.z - srad <= p.z) && (cmax.z + srad >= p.z) );\n\n\tif (bNodeWithinSRad && RayIntersectsNode(p, n)) {\n\t\tint nc = (int)children.size();\n\t\t// if this node has children, search them for intersections\n\t\tif (nc) {\n\t\t\tfor (int ic=0; ic<nc; ++ic) {\n\t\t\t\tchildren[ic].FindIntersectedLeaves(p, n, sel, srad);\n\t\t\t}\n\t\t}\n\t\t// otherwise we have reached the smallest intersected node in this\n\t\t// branch, return its surface element list\n\t\telse {\n\t\t\t// using a 'set' container avoids duplication of surface\n\t\t\t// elements shared by multiple octree nodes\n\t\t\tsel.insert(selist.begin(), selist.end());\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Print node content (for debugging purposes)\n\nvoid OTnode::PrintNodeContent()\n{\n\tint nel = (int)selist.size();\n\tprintf(\"Level = %d\\n\", level);\n\tfor (int i=0; i<nel; ++i)\n\t\tprintf(\"%d\\n\",selist[i]);\n\tprintf(\"-----------------------------------------------------\\n\");\n\t\n\tint nc = (int)children.size();\n\tfor (int i=0; i<nc; ++i) {\n\t\tprintf(\"Child = %d\\n\", i);\n\t\tchildren[i].PrintNodeContent();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Count nodes (for debugging purposes)\n\nvoid OTnode::CountNodes(int& nnode, int& nlevel)\n{\n\tint nc = (int)children.size();\n\tnnode += nc;\n\tnlevel = (level > nlevel) ? level : nlevel;\n\tfor (int i=0; i<nc; ++i) {\n\t\tchildren[i].CountNodes(nnode, nlevel);\n\t}\n}\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEOctree::FEOctree(FESurface* ps)\n{\n\tm_ps = ps;\n\tmax_level = 6;\n\tmax_elem = 9;\n\tassert(max_level && max_elem);\n}\n\nFEOctree::~FEOctree()\n{\n\t\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FEOctree::Init(const double stol)\n{\n\tassert(m_ps);\n\troot.Clear();\n\t\n\t// Set up the root node in the octree\n\troot.m_ps = m_ps;\n\troot.level = 0;\n\t\n\t// Create the list of all surface elements in the root node\n\tint nel = m_ps->Elements();\n\troot.selist.resize(nel);\n\tfor (int i=0; i<nel; ++i)\n\t\troot.selist[i] = i;\n\t\n\t// Find the bounding box of the surface\n\tvec3d fenode = (m_ps->Node(0)).m_rt;\n\troot.cmin = fenode;\n\troot.cmax = fenode;\n\tfor (int i=1; i<m_ps->Nodes(); ++i) {\n\t\tfenode = (m_ps->Node(i)).m_rt;\n\t\tif (fenode.x < root.cmin.x) root.cmin.x = fenode.x;\n\t\tif (fenode.x > root.cmax.x) root.cmax.x = fenode.x;\n\t\tif (fenode.y < root.cmin.y) root.cmin.y = fenode.y;\n\t\tif (fenode.y > root.cmax.y) root.cmax.y = fenode.y;\n\t\tif (fenode.z < root.cmin.z) root.cmin.z = fenode.z;\n\t\tif (fenode.z > root.cmax.z) root.cmax.z = fenode.z;\n\t}\n    \n    // expand bounding box by search tolerance stol\n    double d = (root.cmax - root.cmin).norm()*stol;\n    root.cmin -= vec3d(d, d, d);\n    root.cmax += vec3d(d, d, d);\n\t\n\t// Recursively create children of this root\n\tif (root.selist.size()) {\n\t\tif ((root.level < max_level) &&\n\t\t\t(root.selist.size() > max_elem))\n\t\t\troot.CreateChildren(max_level, max_elem);\n\t}\n\t\n\treturn;\n}\n\nvoid FEOctree::FindCandidateSurfaceElements(vec3d p, vec3d n, set<int>& sel, double srad)\n{\n\troot.FindIntersectedLeaves(p, n, sel, srad);\n}\n"
  },
  {
    "path": "FECore/FEOctree.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"vec3d.h\"\n#include \"vector.h\"\n#include <set>\n\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n//! This is a class for an octree node\n\nclass OTnode\n{\npublic:\n\tOTnode();\n\t~OTnode();\n\tvoid Clear();\n\tvoid CreateChildren(const int max_level, const int max_elem);\n\tvoid FillNode(const std::vector<int>& parent_selist);\n\tbool ElementIntersectsNode(const int j);\n\tvoid PrintNodeContent();\n\tbool RayIntersectsNode(const vec3d& p, const vec3d& n);\n\tvoid FindIntersectedLeaves(const vec3d& p, const vec3d& n, std::set<int>& sel, double srad);\n\tvoid CountNodes(int& nnode, int& nlevel);\n\t\npublic:\n\tint\t\t\t\tlevel;\t\t//!< node level\n\tvec3d\t\t\tcmin, cmax;\t//!< node bounding box\n\tstd::vector<int>\t\tselist;\t\t//!< list of surface elements inside this node\n\tstd::vector<OTnode>\tchildren;\t//!< children of this node\n\tFESurface*\t\tm_ps;\t\t//!< the surface to search\n};\n\n//-----------------------------------------------------------------------------\n//! This class is a helper class to find ray intersection with a surface\n\nclass FECORE_API FEOctree\n{\n\t\npublic:\n\tFEOctree(FESurface* ps = 0);\n\t~FEOctree();\n\t\n\t//! attach to a surface\n\tvoid Attach(FESurface* ps) { m_ps = ps; }\n\t\n\t//! initialize search structures\n\tvoid Init(const double stol);\n\t\n\t//! find all candidate surface elements intersected by ray\n\tvoid FindCandidateSurfaceElements(vec3d p, vec3d n, std::set<int>& sel, double srad);\n\t\nprotected:\n\tFESurface*\tm_ps;\t//!< the surface to search\n\tOTnode root;\t\t//!< root node in octree\n\tint max_level;\t\t//!< maximum allowable number of levels in octree\n\tint max_elem;\t\t//!< maximum allowable number of elements in any node\n};\n"
  },
  {
    "path": "FECore/FEOctreeSearch.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEOctreeSearch.h\"\n#include \"FEMesh.h\"\n#include \"FEElementList.h\"\n#include \"FESolidDomain.h\"\n\n//-----------------------------------------------------------------------------\nFEOctreeSearch::Block::Block(FEMesh* mesh, int level)\n{\n\tm_parent = nullptr;\n\tm_mesh = mesh;\n\tm_level = level;\n}\n\n//-----------------------------------------------------------------------------\nFEOctreeSearch::Block::Block(FEOctreeSearch::Block* parent, int level)\n{\n\tm_parent = parent;\n\tm_mesh = parent->m_mesh;\n\tm_level = level;\n}\n\n//-----------------------------------------------------------------------------\nFEOctreeSearch::Block::~Block()\n{ \n\tClear(); \n}\n\n//-----------------------------------------------------------------------------\nvoid FEOctreeSearch::Block::Clear()\n{\n\tfor (size_t i = 0; i < m_children.size(); ++i) delete m_children[i];\n\tm_children.clear();\n}\n\n//-----------------------------------------------------------------------------\n// Create the eight children of an octree node and find their contents\n\nvoid FEOctreeSearch::Block::CreateChildren(const int max_level, const int max_elem)\n{\n\tvec3d dc = (m_cmax - m_cmin) / 2;\n\tfor (int i = 0; i <= 1; ++i) {\n\t\tfor (int j = 0; j <= 1; ++j) {\n\t\t\tfor (int k = 0; k <= 1; ++k) {\n\t\t\t\tBlock* node = new Block(this, m_level + 1);\n\n\t\t\t\t// evaluate bounding box by subdividing parent node box\n\t\t\t\tnode->m_cmin = vec3d(m_cmin.x + i*dc.x,\n\t\t\t\t\tm_cmin.y + j*dc.y,\n\t\t\t\t\tm_cmin.z + k*dc.z);\n\t\t\t\tnode->m_cmax = vec3d(m_cmax.x - (1 - i)*dc.x,\n\t\t\t\t\tm_cmax.y - (1 - j)*dc.y,\n\t\t\t\t\tm_cmax.z - (1 - k)*dc.z);\n\n\t\t\t\t// find all elements in this child node\n\t\t\t\tnode->FillBlock();\n\n\t\t\t\tif (node->m_selist.size()) {\n\t\t\t\t\t// use recursion to create children of this node\n\t\t\t\t\t// as long as node contains too many elements\n\t\t\t\t\t// and max octree levels not exceeded\n\t\t\t\t\tif ((node->m_level < max_level) &&\n\t\t\t\t\t\t(node->m_selist.size() > max_elem))\n\t\t\t\t\t\tnode->CreateChildren(max_level, max_elem);\n\t\t\t\t}\n\n\t\t\t\t// store this node\n\t\t\t\tm_children.push_back(node);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Find all elements that fall inside a block\n\nvoid FEOctreeSearch::Block::FillBlock()\n{\n\t// Loop over all elements in the parent node\n\tfor (int i = 0; i<(int)m_parent->m_selist.size(); ++i) {\n\t\tFEElement* pe = m_parent->m_selist[i];\n\t\tif (ElementIntersectsNode(pe)) {\n\t\t\t// add this surface element to the current node\n\t\t\tm_selist.push_back(pe);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Determine whether a surface element intersects a node\n\nbool FEOctreeSearch::Block::ElementIntersectsNode(FEElement* pel)\n{\n\t// Extract FE node coordinates from element\n\t// and determine bounding box of element\n\tFEMesh& mesh = *m_mesh;\n\tFEElement& el = *pel;\n\tint N = el.Nodes();\n\tvector<vec3d> fenode(N);\n\tfenode[0] = mesh.Node(el.m_node[0]).m_r0;\n\tvec3d fmin = fenode[0];\n\tvec3d fmax = fenode[0];\n\tfor (int i = 1; i<N; ++i) {\n\t\tfenode[i] = mesh.Node(el.m_node[i]).m_r0;\n\t\tif (fenode[i].x < fmin.x) fmin.x = fenode[i].x;\n\t\tif (fenode[i].x > fmax.x) fmax.x = fenode[i].x;\n\t\tif (fenode[i].y < fmin.y) fmin.y = fenode[i].y;\n\t\tif (fenode[i].y > fmax.y) fmax.y = fenode[i].y;\n\t\tif (fenode[i].z < fmin.z) fmin.z = fenode[i].z;\n\t\tif (fenode[i].z > fmax.z) fmax.z = fenode[i].z;\n\t}\n\n\t// Check if bounding boxes of OT node and surface element overlap\n\tif ((fmax.x < m_cmin.x) || (fmin.x > m_cmax.x)) return false;\n\tif ((fmax.y < m_cmin.y) || (fmin.y > m_cmax.y)) return false;\n\tif ((fmax.z < m_cmin.z) || (fmin.z > m_cmax.z)) return false;\n\n\t// At this point we find that bounding boxes overlap.\n\t// Technically that does not prove that the surface element is\n\t// inside the octree node, but any additional check would be\n\t// more expensive.\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEOctreeSearch::Block::IsInside(const vec3d& r) const\n{\n\tif ((r.x < m_cmin.x) || (r.x > m_cmax.x)) return false;\n\tif ((r.y < m_cmin.y) || (r.y > m_cmax.y)) return false;\n\tif ((r.z < m_cmin.z) || (r.z > m_cmax.z)) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEElement* FEOctreeSearch::Block::FindElement(const vec3d& y, double r[3])\n{\n\tif (IsInside(y))\n\t{\n\t\tif (m_children.empty() == false)\n\t\t{\n\t\t\tfor (int i = 0; i < m_children.size(); ++i)\n\t\t\t{\n\t\t\t\tFEElement* pe = m_children[i]->FindElement(y, r);\n\t\t\t\tif (pe) return pe;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3d x[FEElement::MAX_NODES];\n\t\t\tint NE = (int)m_selist.size();\n\t\t\tfor (int i = 0; i<NE; ++i)\n\t\t\t{\n\t\t\t\t// get the next element\n\t\t\t\tFESolidElement& e = *((FESolidElement*)m_selist[i]);\n\n\t\t\t\t// get the element nodal coordinates\n\t\t\t\tint neln = e.Nodes();\n\t\t\t\tfor (int j = 0; j<neln; ++j) x[j] = m_mesh->Node(e.m_node[j]).m_r0;\n\n\t\t\t\t// first, as a quick check, we see if y lies in the bounding box defined by x\n\t\t\t\tFEBoundingBox box(x[0]);\n\t\t\t\tfor (int j = 1; j<neln; ++j) box.add(x[j]);\n\n\t\t\t\t// inflate a little for round-off\n\t\t\t\tdouble dr = box.radius()*1e-6;\n\t\t\t\tbox.inflate(dr, dr, dr);\n\n\t\t\t\tif (box.IsInside(y))\n\t\t\t\t{\n\t\t\t\t\tFESolidDomain* dom = dynamic_cast<FESolidDomain*>(e.GetMeshPartition());\n\n\t\t\t\t\t// If the point y lies inside the box, we apply a Newton method to find\n\t\t\t\t\t// the isoparametric coordinates r\n\t\t\t\t\tif (dom->ProjectToReferenceElement(e, y, r)) return &e;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn nullptr;\n}\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\nFEOctreeSearch::FEOctreeSearch(FEMesh* mesh)\n{\n\tm_root = nullptr;\n\tm_mesh = mesh;\n\tm_dom = nullptr;\n\tm_max_level = 6;\n\tm_max_elem = 9;\n}\n\nFEOctreeSearch::FEOctreeSearch(FEDomain* domain)\n{\n\tm_root = nullptr;\n\tm_mesh = domain->GetMesh();\n\tm_dom = domain;\n\tm_max_level = 6;\n\tm_max_elem = 9;\n}\n\n//-----------------------------------------------------------------------------\nFEOctreeSearch::~FEOctreeSearch()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nbool FEOctreeSearch::Init(double inflate)\n{\n\tif (m_mesh == nullptr) return false;\n\n\t// Set up the root node in the octree\n\tif (m_root) delete m_root;\n\tm_root = new Block(m_mesh, 0);\n\n\tBlock& root = *m_root;\n\n\t// Create the list of all elements in the root node\n\tint nel = 0;\n\tif (m_dom == nullptr)\n\t{\n\t\tFEElementList EL(*m_mesh);\n\t\tnel = m_mesh->Elements();\n\t\tfor (FEElementList::iterator it = EL.begin(); it != EL.end(); ++it)\n\t\t\troot.m_selist.push_back(it);\n\t}\n\telse\n\t{\n\t\tnel = m_dom->Elements();\n\t\tfor (int i = 0; i < nel; ++i)\n\t\t{\n\t\t\troot.m_selist.push_back(&m_dom->ElementRef(i));\n\t\t}\n\t}\n\n\t// Find the bounding box of the mesh\n\tvec3d r0 = (m_mesh->Node(0)).m_r0;\n\troot.m_cmin = r0;\n\troot.m_cmax = r0;\n\tfor (int i = 1; i<m_mesh->Nodes(); ++i) {\n\t\tvec3d r = (m_mesh->Node(i)).m_r0;\n\t\tif (r.x < root.m_cmin.x) root.m_cmin.x = r.x;\n\t\tif (r.x > root.m_cmax.x) root.m_cmax.x = r.x;\n\t\tif (r.y < root.m_cmin.y) root.m_cmin.y = r.y;\n\t\tif (r.y > root.m_cmax.y) root.m_cmax.y = r.y;\n\t\tif (r.z < root.m_cmin.z) root.m_cmin.z = r.z;\n\t\tif (r.z > root.m_cmax.z) root.m_cmax.z = r.z;\n\t}\n\n\t// expand bounding box by search tolerance stol\n\tdouble d = (root.m_cmax - root.m_cmin).norm()*inflate;\n\troot.m_cmin -= vec3d(d, d, d);\n\troot.m_cmax += vec3d(d, d, d);\n\n\t// figure out the levels\n\tint level = (int) (log((double)nel) / log(8.0));\n\tif (level < 1) level = 1;\n\tif (level > m_max_level) level = m_max_level - 1;\n\n\t// Recursively create children of this root\n\tif (root.m_selist.size()) {\n\t\tif ((root.m_level < m_max_level) &&\n\t\t\t(root.m_selist.size() > m_max_elem))\n\t\t\troot.CreateChildren(level, m_max_elem);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFEElement* FEOctreeSearch::FindElement(const vec3d& x, double r[3])\n{\n\treturn m_root->FindElement(x, r);\n}\n"
  },
  {
    "path": "FECore/FEOctreeSearch.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include \"vec3d.h\"\n#include \"vector.h\"\n#include <set>\n\nclass FEElement;\nclass FEMesh;\nclass FEDomain;\n\n//-----------------------------------------------------------------------------\n//! This class is a helper class to find ray intersection with a surface\n\nclass FECORE_API FEOctreeSearch\n{\n\tclass Block\n\t{\n\tpublic:\n\t\tBlock(FEMesh* mesh, int level);\n\t\tBlock(Block* parent, int level);\n\t\t~Block();\n\t\tvoid Clear();\n\t\tvoid CreateChildren(int max_level, int max_elem);\n\t\tvoid FillBlock();\n\t\tbool ElementIntersectsNode(FEElement* pe);\n\n\t\tFEElement* FindElement(const vec3d& y, double r[3]);\n\n\t\tbool IsInside(const vec3d& r) const;\n\n\tpublic:\n\t\tint\t\t\t\t\tm_level;\t\t//!< node level\n\t\tvec3d\t\t\t\tm_cmin, m_cmax;\t//!< node bounding box\n\t\tstd::vector<FEElement*>\tm_selist;\t\t//!< list of surface elements inside this node\n\t\tstd::vector<Block*>\t\tm_children;\t\t//!< children of this node\n\t\tFEMesh*\t\t\t\tm_mesh;\t\t\t//!< the mesh to search\n\t\tBlock*\t\t\t\tm_parent;\n\t};\n\npublic:\n\tFEOctreeSearch(FEMesh* mesh);\n\tFEOctreeSearch(FEDomain* domain);\n\t~FEOctreeSearch();\n\n\t//! initialize search structures\n\tbool Init(double inflate = 0.005);\n\n\tFEElement* FindElement(const vec3d& x, double r[3]);\n\nprotected:\n\tFEMesh*\t\tm_mesh;\t\t\t//!< the mesh\n\tFEDomain*\tm_dom;\t\t\t//!< the domain to search (if null whole mesh will be searched)\n\tBlock*\t\tm_root;\t\t\t//!< root node in octree\n\tint\t\t\tm_max_level;\t//!< maximum allowable number of levels in octree\n\tint\t\t\tm_max_elem;\t\t//!< maximum allowable number of elements in any node\n};\n"
  },
  {
    "path": "FECore/FEPIDController.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPIDController.h\"\n#include \"FEModel.h\"\n#include \"log.h\"\n\nBEGIN_FECORE_CLASS(FEPIDController, FELoadController)\n\tADD_PARAMETER(m_paramName, \"var\");\n\tADD_PARAMETER(m_trg, \"target\");\n\tADD_PARAMETER(m_Kp, \"Kp\");\n\tADD_PARAMETER(m_Ki, \"Ki\");\n\tADD_PARAMETER(m_Kd, \"Kd\");\nEND_FECORE_CLASS();\n\nFEPIDController::FEPIDController(FEModel* fem) : FELoadController(fem)\n{\n\tm_prev = 0;\n\tm_int = 0;\n\tm_prevTime = 0.0;\n\tm_paramVal = 0.0;\n\tm_error = 0.0;\n}\n\nbool FEPIDController::Init()\n{\n\tFEModel& fem = *GetFEModel();\n\n\tParamString ps(m_paramName.c_str());\n\tm_param = fem.GetParameterValue(ps);\n\tif (m_param.isValid() == false) return false;\n\tif (m_param.type() != FE_PARAM_DOUBLE) return false;\n\n\treturn FELoadController::Init();\n}\n\ndouble FEPIDController::GetValue(double time)\n{\n\tm_paramVal = m_param.value<double>();\n\tm_error = m_trg - m_paramVal;\n\n\tdouble newVal = m_Kp* m_error;\n\n\tdouble dt = time - m_prevTime;\n\tif (dt != 0.0)\n\t{\n\t\tdouble der = (m_error - m_prev) / dt;\n\t\tm_int += m_error *dt;\n\t\tnewVal += m_Kd*der + m_Ki*m_int;\n\t}\n\n\tm_prev = m_error;\n\tm_prevTime = time;\n\n\tif (GetFEModel()->GetPrintParametersFlag())\n\t{\n\t\tfeLog(\"PID controller %d:\\n\", GetID());\n\t\tfeLog(\"\\tparameter = %lg\\n\", m_paramVal);\n\t\tfeLog(\"\\terror     = %lg\\n\", m_error);\n\t\tfeLog(\"\\tvalue     = %lg\\n\", newVal);\n\t}\n\n\treturn newVal;\n}\n\nvoid FEPIDController::Serialize(DumpStream& ar)\n{\n\tFELoadController::Serialize(ar);\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_paramVal << m_error << m_prev << m_prevTime << m_int;\n\t}\n\telse\n\t{\n\t\tar >> m_paramVal >> m_error >> m_prev >> m_prevTime >> m_int;\n\n\t\tif (ar.IsShallow())\n\t\t{\n\t\t\tFEModel& fem = *GetFEModel();\n\t\t\tParamString ps(m_paramName.c_str());\n\t\t\tm_param = fem.GetParameterValue(ps);\n\t\t\tassert(m_param.isValid());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FEPIDController.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FELoadController.h\"\n#include \"MathObject.h\"\n\nclass FEPIDController : public FELoadController\n{\npublic:\n\tFEPIDController(FEModel* fem);\n\n\tbool Init() override;\n\n\tdouble GetParameterValue() const { return m_paramVal; }\n\tdouble GetError() const { return m_error; }\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprotected:\n\tdouble GetValue(double time) override;\n\nprivate:\n\tstd::string\t\tm_paramName;\t// the parameter to target\n\tdouble\t\t\tm_trg;\t\t\t// the target value\n\n\tdouble\tm_Kp;\n\tdouble\tm_Kd;\n\tdouble\tm_Ki;\n\n\tFEParamValue\tm_param;\t\t//!< parameter that is tracked\n\tdouble\t\t\tm_paramVal;\t\t//!< current parameter value\n\tdouble\t\t\tm_error;\t\t//!< current error\n\tdouble\t\t\tm_prev;\t\t\t//!< error at previous time step\n\tdouble\t\t\tm_prevTime;\t\t//!< previous time step\n\tdouble\t\t\tm_int;\t\t\t//!< integral value\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEParabolicMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEParabolicMap.h\"\n#include \"FESurfaceMap.h\"\n#include \"FESurface.h\"\n#include \"FEElemElemList.h\"\n#include \"log.h\"\n#include \"SparseMatrix.h\"\n#include \"LinearSolver.h\"\n#include \"FEGlobalMatrix.h\"\n#include \"FEModel.h\"\n\nBEGIN_FECORE_CLASS(FEParabolicMap, FEFaceDataGenerator)\n\tADD_PARAMETER(m_scale, \"value\");\nEND_FECORE_CLASS();\n\nFEParabolicMap::FEParabolicMap(FEModel* fem) : FEFaceDataGenerator(fem), m_dofs(fem)\n{\n\tm_scale = 1.0;\n}\n\nFEParabolicMap::~FEParabolicMap()\n{\n\n}\n\nvoid FEParabolicMap::SetDOFConstraint(const FEDofList& dofs)\n{\n\tm_dofs = dofs;\n}\n\nFEDataMap* FEParabolicMap::Generate()\n{\n\tconst FEFacetSet& facetSet = *GetFacetSet();\n\tFESurfaceMap* map = new FESurfaceMap(FEDataType::FE_DOUBLE);\n\tmap->Create(&facetSet, 0.0, FMT_NODE);\n\n\t// create temporary surface\n\tFESurface surf(GetFEModel());\n\tsurf.Create(facetSet);\n\tsurf.InitSurface();\n\n\t// find surface boundary nodes\n\tFEElemElemList EEL;\n\tEEL.Create(&surf);\n\n\tvector<bool> boundary(surf.Nodes(), false);\n\tfor (int i = 0; i<surf.Elements(); ++i) {\n\t\tFESurfaceElement& el = surf.Element(i);\n\t\tfor (int j = 0; j<el.facet_edges(); ++j) {\n\t\t\tFEElement* nel = EEL.Neighbor(i, j);\n\t\t\tif (nel == nullptr) {\n\t\t\t\tint en[3] = { -1,-1,-1 };\n\t\t\t\tel.facet_edge(j, en);\n\t\t\t\tboundary[en[0]] = true;\n\t\t\t\tboundary[en[1]] = true;\n\t\t\t\tif (en[2] > -1) boundary[en[2]] = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Apply dof constraints\n\tif (m_dofs.IsEmpty() == false)\n\t{\n\t\t// only consider nodes with fixed dofs as boundary nodes\n\t\tfor (int i = 0; i < surf.Nodes(); ++i)\n\t\t\tif (boundary[i]) {\n\t\t\t\tFENode& node = surf.Node(i);\n\n\t\t\t\tbool b = false;\n\t\t\t\tfor (int j = 0; j < m_dofs.Size(); ++j)\n\t\t\t\t{\n\t\t\t\t\tif (node.get_bc(m_dofs[j]) != DOF_FIXED) b = true;\n\t\t\t\t}\n\n\t\t\t\tif (b) boundary[i] = false;\n\t\t\t}\n\t}\n\n\t// count number of non-boundary nodes\n\tint neq = 0;\n\tvector<int> glm(surf.Nodes(), -1);\n\tfor (int i = 0; i< surf.Nodes(); ++i)\n\t\tif (!boundary[i]) glm[i] = neq++;\n\tif (neq == 0)\n\t{\n\t\tfeLogError(\"Unable to set parabolic map\\n\");\n\t\treturn nullptr;\n\t}\n\n\t// create a linear solver\n\tLinearSolver*\t\tplinsolve;\t//!< the linear solver\n\tFEGlobalMatrix*\t\tpK;\t\t\t//!< stiffness matrix\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tplinsolve = fecore_new<LinearSolver>(\"skyline\", nullptr);\n\tif (plinsolve == 0)\n\t{\n\t\tfeLogError(\"Unknown solver type selected\\n\");\n\t\treturn nullptr;\n\t}\n\n\tSparseMatrix* pS = plinsolve->CreateSparseMatrix(REAL_SYMMETRIC);\n\tpK = new FEGlobalMatrix(pS);\n\tif (pK == 0)\n\t{\n\t\tfeLogError(\"Failed allocating stiffness matrix\\n\\n\");\n\t\treturn nullptr;\n\t}\n\t// build matrix profile for normal velocity at non-boundary nodes\n\tpK->build_begin(neq);\n\tfor (int i = 0; i< surf.Elements(); ++i) {\n\t\tFESurfaceElement& el = surf.Element(i);\n\t\tvector<int> elm(el.Nodes(), -1);\n\t\tfor (int j = 0; j<el.Nodes(); ++j)\n\t\t\telm[j] = glm[el.m_lnode[j]];\n\t\tpK->build_add(elm);\n\t}\n\tpK->build_end();\n\tpS->Zero();\n\n\t// create global vector\n\tvector<double> v;           //!< solution\n\tvector<double> rhs;         //!< right-hand-side\n\tvector<double> Fr;          //!< reaction forces\n\tv.assign(neq, 0);\n\trhs.assign(neq, 0);\n\tFr.assign(neq, 0);\n\tFEModel pfem;\n\tFEGlobalVector pR(pfem, rhs, Fr);\n\n\t// calculate the global matrix and vector\n\tFEElementMatrix ke;\n\tvector<double> fe;\n\tvector<int> lm;\n\n\tfor (int m = 0; m< surf.Elements(); ++m)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = surf.Element(m);\n\n\t\tint neln = el.Nodes();\n\n\t\t// get the element stiffness matrix\n\t\tke.resize(neln, neln);\n\t\tlm.resize(neln);\n\t\tfe.resize(neln);\n\t\tvector<vec3d> gradN(neln);\n\n\t\t// calculate stiffness\n\t\tint nint = el.GaussPoints();\n\n\t\t// gauss weights\n\t\tdouble* w = el.GaussWeights();\n\n\t\t// nodal coordinates\n\t\tFEMesh& mesh = *surf.GetMesh();\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j<neln; ++j) rt[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// repeat over integration points\n\t\tke.zero();\n\t\tzero(fe);\n\t\tvec3d gcnt[2];\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tdouble* N = el.H(n);\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\t\t\tsurf.ContraBaseVectors(el, n, gcnt);\n\n\t\t\tvec3d dxr(0, 0, 0), dxs(0, 0, 0);\n\t\t\tfor (int i = 0; i<neln; ++i)\n\t\t\t{\n\t\t\t\tdxr += rt[i] * Gr[i];\n\t\t\t\tdxs += rt[i] * Gs[i];\n\t\t\t\tgradN[i] = gcnt[0] * Gr[i] + gcnt[1] * Gs[i];\n\t\t\t}\n\n\t\t\tdouble da = (dxr ^ dxs).norm();\n\n\t\t\t// calculate stiffness component\n\t\t\tfor (int i = 0; i<neln; ++i) {\n\t\t\t\tfe[i] += N[i] * w[n] * da;\n\t\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t\t\tke[i][j] += (gradN[i] * gradN[j])*w[n] * da;\n\t\t\t}\n\t\t}\n\n\t\t// get the element's LM vector\n\t\tfor (int j = 0; j<el.Nodes(); ++j)\n\t\t\tlm[j] = glm[el.m_lnode[j]];\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tke.SetIndices(lm);\n\t\tpK->Assemble(ke);\n\t\tpR.Assemble(lm, fe);\n\t}\n\n\t// solve linear system\n\tplinsolve->PreProcess();\n\tplinsolve->Factor();\n\tif (plinsolve->BackSolve(v, rhs) == false)\n\t{\n\t\tfeLogError(\"Unable to solve for parabolic field\\n\");\n\t\treturn nullptr;\n\t}\n\tplinsolve->Destroy();\n\n\t// set the nodal normal velocity scale factors\n\tvector<double> VN(surf.Nodes(), 0.0);\n\tfor (int i = 0; i< surf.Nodes(); ++i) {\n\t\tif (glm[i] == -1) VN[i] = 0;\n\t\telse VN[i] = v[glm[i]];\n\t}\n\n\t// evaluate net area and volumetric flow rate\n\tdouble A = 0, Q = 0;\n\tfor (int m = 0; m< surf.Elements(); ++m)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = surf.Element(m);\n\n\t\tint neln = el.Nodes();\n\t\tint nint = el.GaussPoints();\n\t\tdouble* w = el.GaussWeights();\n\n\t\t// nodal coordinates\n\t\tFEMesh& mesh = *surf.GetMesh();\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j<neln; ++j) rt[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// repeat over integration points\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tdouble* N = el.H(n);\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\n\t\t\tdouble vn = 0;\n\t\t\tvec3d dxr(0, 0, 0), dxs(0, 0, 0);\n\t\t\tfor (int i = 0; i<neln; ++i)\n\t\t\t{\n\t\t\t\tvn += N[i] * VN[el.m_lnode[i]];\n\t\t\t\tdxr += rt[i] * Gr[i];\n\t\t\t\tdxs += rt[i] * Gs[i];\n\t\t\t}\n\n\t\t\tdouble da = (dxr ^ dxs).norm();\n\n\t\t\tfor (int i = 0; i<neln; ++i) {\n\t\t\t\tA += N[i] * w[n] * da;\n\t\t\t\tQ += N[i] * vn*w[n] * da;\n\t\t\t}\n\t\t}\n\t}\n\n\t// normalize nodal velocity cards\n\tdouble vbar = Q / A;\n\tfor (int i = 0; i< surf.Nodes(); ++i) VN[i] /= vbar;\n\n\t// assign nodal values to surface map\n\tmap->set<double>(0.0);\n\tfor (int i = 0; i < surf.Nodes(); ++i)\n\t{\n\t\tmap->set<double>(i, VN[i] * m_scale);\n\t}\n\n\treturn map;\n}\n"
  },
  {
    "path": "FECore/FEParabolicMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDataGenerator.h\"\n#include \"FEDofList.h\"\n\nclass FESurface;\n\nclass FECORE_API FEParabolicMap : public FEFaceDataGenerator\n{\npublic:\n\tFEParabolicMap(FEModel* fem);\n\n\t~FEParabolicMap();\n\n\tFEDataMap* Generate() override;\n\n\tvoid SetDOFConstraint(const FEDofList& dofs);\n\nprivate:\n\tdouble\tm_scale;\n\n\tFEDofList\tm_dofs;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEParam.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEParam.h\"\n#include \"FEParamValidator.h\"\n#include \"DumpStream.h\"\n#include \"FEDataArray.h\"\n#include \"tens3d.h\"\n#include \"FEModelParam.h\"\nusing namespace std;\n\nFEParamValue FEParamValue::component(int n)\n{\n\tswitch (m_itype)\n\t{\n\tcase FE_PARAM_VEC2D:\n\t{\n\t\tvec2d& r = value<vec2d>();\n\t\tif (n == 0) return FEParamValue(m_param, &r.x(), FE_PARAM_DOUBLE);\n\t\tif (n == 1) return FEParamValue(m_param, &r.y(), FE_PARAM_DOUBLE);\n\t}\n\tbreak;\n\tcase FE_PARAM_VEC3D:\n\t{\n\t\tvec3d& r = value<vec3d>();\n\t\tif (n == 0) return FEParamValue(m_param, &r.x, FE_PARAM_DOUBLE);\n\t\tif (n == 1) return FEParamValue(m_param, &r.y, FE_PARAM_DOUBLE);\n\t\tif (n == 2) return FEParamValue(m_param, &r.z, FE_PARAM_DOUBLE);\n\t}\n\tbreak;\n\tcase FE_PARAM_MAT3DS:\n\t{\n\t\tmat3ds& a = value<mat3ds>();\n\t\tif (n == 0) return FEParamValue(m_param, &a.xx(), FE_PARAM_DOUBLE);\n\t\tif (n == 1) return FEParamValue(m_param, &a.xy(), FE_PARAM_DOUBLE);\n\t\tif (n == 2) return FEParamValue(m_param, &a.yy(), FE_PARAM_DOUBLE);\n\t\tif (n == 3) return FEParamValue(m_param, &a.xz(), FE_PARAM_DOUBLE);\n\t\tif (n == 4) return FEParamValue(m_param, &a.yz(), FE_PARAM_DOUBLE);\n\t\tif (n == 5) return FEParamValue(m_param, &a.zz(), FE_PARAM_DOUBLE);\n\t}\n\tbreak;\n\t}\n\n\tassert(false);\n\treturn FEParamValue();\n}\n\n\n//-----------------------------------------------------------------------------\nFEParam::FEParam(void* pdata, FEParamType itype, int ndim, const char* szname, bool* watch)\n{\n\tm_pv = pdata;\n\tm_type = itype;\n\tm_dim = ndim;\n\tm_watch = watch;\n\tif (m_watch) *m_watch = false;\n\n\t// default flags depend on type\n\t// (see also FEModel::EvaluateLoadParameters())\n\tm_flag = 0;\n\tif (ndim == 1)\n\t{\n\t\tswitch (itype)\n\t\t{\n\t\tcase FE_PARAM_DOUBLE:\n\t\tcase FE_PARAM_VEC3D:\n\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\tcase FE_PARAM_VEC3D_MAPPED:\n\t\t\t// all these types can be modified via a load curve\n\t\t\tm_flag = FE_PARAM_VOLATILE;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tm_group = -1;\n\n\t// set the name\n\t// note that we just copy the pointer, not the actual string\n\t// this is okay as long as the name strings are defined\n\t// as literal strings\n\tm_szname = szname;\n\tm_szlongname = szname;\n\n\tm_szenum = nullptr;\n\n\tm_pvalid = nullptr;\t// no default validator\n\n\tm_parent = nullptr;\n\n\tm_szunit = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEParam::FEParam(const FEParam& p)\n{\n\tm_pv = p.m_pv;\n\tm_type = p.m_type;\n\tm_dim = p.m_dim;\n\tm_watch = p.m_watch;\n\n\tm_flag = p.m_flag;\n\tm_group = p.m_group;\n\n\tm_szname = p.m_szname;\n\tm_szlongname = p.m_szlongname;\n\n\tm_szenum = 0;\n\tm_parent = p.m_parent;\n\n\tm_szunit = p.m_szunit;\n\n\tm_pvalid = (p.m_pvalid ? p.m_pvalid->copy() : 0);\n}\n\n//-----------------------------------------------------------------------------\nint FEParam::GetParamGroup() const\n{\n\treturn m_group;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParam::SetParamGroup(int i)\n{\n\tm_group = i;\n}\n\n//-----------------------------------------------------------------------------\nFEParam::~FEParam()\n{\n\tif (m_flag & FEParamFlag::FE_PARAM_USER)\n\t{\n\t\tfree((void*)m_szname);\n\t\tswitch (m_type)\n\t\t{\n\t\tcase FE_PARAM_BOOL  : delete (bool*  )m_pv; break;\n\t\tcase FE_PARAM_INT   : delete (int*   )m_pv; break;\n\t\tcase FE_PARAM_DOUBLE: delete (double*)m_pv; break;\n\t\tcase FE_PARAM_DOUBLE_MAPPED: delete (FEParamDouble*)m_pv; break;\n\t\tcase FE_PARAM_VEC3D_MAPPED : delete (FEParamVec3*  )m_pv; break;\n\t\tcase FE_PARAM_MAT3D_MAPPED : delete (FEParamMat3d* )m_pv; break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFEParam& FEParam::operator=(const FEParam& p)\n{\n\tm_pv = p.m_pv;\n\tm_type = p.m_type;\n\tm_dim = p.m_dim;\n\tm_watch = p.m_watch;\n\n\tm_flag = p.m_flag;\n\n\tm_szname = p.m_szname;\n\tm_szenum = 0;\n\tm_parent = p.m_parent;\n\n\tm_szunit = p.m_szunit;\n\n\tif (m_pvalid) delete m_pvalid;\n\tm_pvalid = (p.m_pvalid ? p.m_pvalid->copy() : 0);\n\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nbool FEParam::is_valid() const\n{\n\tif (m_pvalid) return m_pvalid->is_valid(*this);\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// return the name of the parameter\nconst char* FEParam::name() const \n{ \n\treturn m_szname; \n}\n\n//-----------------------------------------------------------------------------\nconst char* FEParam::longName() const\n{\n\treturn m_szlongname;\n}\n\n//-----------------------------------------------------------------------------\n// return the enum values\nconst char* FEParam::enums() const \n{ \n\treturn m_szenum; \n}\n\n//-----------------------------------------------------------------------------\n// get the current enum value (or nullptr)\nconst char* FEParam::enumKey() const\n{\n\tconst char* sz = enums();\n\tif (sz == nullptr) return nullptr;\n\tif (sz[0] == '$') return nullptr;\n\n\tint n = value<int>();\n\tif (n < 0) return nullptr;\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tsz += strlen(sz) + 1;\n\t\tif ((sz == nullptr) || (*sz == 0)) return nullptr;\n\t}\n\treturn sz;\n}\n\n//-----------------------------------------------------------------------------\nconst char* FEParam::units() const\n{\n\treturn m_szunit;\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParam::setUnits(const char* szunit) { m_szunit = szunit; return this; }\n\n//-----------------------------------------------------------------------------\n// set the enum values (\\0 separated. Make sure the end of the string has two \\0's)\nFEParam* FEParam::setEnums(const char* sz)\n{ \n\t// count the enums\n\tif (sz && (sz[0] != '$') && (type() == FE_PARAM_INT))\n\t{\n\t\tint n = 0;\n\t\tconst char* s = sz;\n\t\twhile ((s != nullptr) && (*s != 0))\n\t\t{\n\t\t\ts += strlen(s) + 1;\n\t\t\tn++;\n\t\t}\n\t\tSetValidator(new FEIntValidator(FEParamRange::FE_CLOSED, 0, n - 1));\n\t}\n\tm_szenum = sz; return this; \n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParam::setLongName(const char* sz)\n{\n\tm_szlongname = sz; \n\treturn this;\n}\n\n//-----------------------------------------------------------------------------\n// parameter dimension\nint FEParam::dim() const { return m_dim; }\n\n//-----------------------------------------------------------------------------\n// parameter type\nFEParamType FEParam::type() const { return m_type; }\n\n//-----------------------------------------------------------------------------\n// data pointer\nvoid* FEParam::data_ptr() const { return m_pv; }\n\n//-----------------------------------------------------------------------------\n//! override the template for char pointers\nchar* FEParam::cvalue() { return (char*)data_ptr(); }\n\n//-----------------------------------------------------------------------------\nFEParamValue FEParam::paramValue(int i)\n{\n\tswitch (m_type)\n\t{\n\tcase FE_PARAM_DOUBLE:\n\t{\n\t\tif (i == -1)\n\t\t{\n\t\t\tassert(m_dim == 1);\n\t\t\treturn FEParamValue(this, &value<double>(), FE_PARAM_DOUBLE);\n\t\t}\n\t\telse return FEParamValue(this, &value<double>(i), FE_PARAM_DOUBLE);\n\t}\n\tbreak;\n\tcase FE_PARAM_VEC3D:\n\t{\n\t\tif (i == -1)\n\t\t{\n\t\t\tassert(m_dim == 1);\n\t\t\treturn FEParamValue(this, &value<vec3d>(), FE_PARAM_VEC3D);\n\t\t}\n\t\telse return FEParamValue(this, &value<vec3d>(i), FE_PARAM_VEC3D);\n\t}\n\tbreak;\n\tcase FE_PARAM_DOUBLE_MAPPED:\n\t{\n\t\tif (i == -1)\n\t\t{\n\t\t\tassert(m_dim == 1);\n\t\t\tFEParamDouble& p = value<FEParamDouble>();\n\t\t\tif (p.isConst()) return FEParamValue(this, &p.constValue(), FE_PARAM_DOUBLE);\n\t\t\telse return FEParamValue(this, m_pv, m_type);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEParamDouble& data = value<FEParamDouble>(i);\n\t\t\treturn FEParamValue(this, &data, FE_PARAM_DOUBLE_MAPPED);\n\t\t}\n\t}\n\tbreak;\n\tcase FE_PARAM_VEC3D_MAPPED:\n\t{\n\t\tif (i == -1)\n\t\t{\n\t\t\tassert(m_dim == 1);\n\t\t\tFEParamVec3& p = value<FEParamVec3>();\n\t\t\tif (p.isConst()) return FEParamValue(this, &p.constValue(), FE_PARAM_VEC3D);\n\t\t\telse return FEParamValue(this, m_pv, m_type);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEParamVec3& data = value<FEParamVec3>(i);\n\t\t\treturn FEParamValue(this, &data, FE_PARAM_VEC3D_MAPPED);\n\t\t}\n\t}\n\tbreak;\n\tcase FE_PARAM_MAT3D_MAPPED:\n\t{\n\t\tif (i == -1)\n\t\t{\n\t\t\tassert(m_dim == 1);\n\t\t\tFEParamMat3d& p = value<FEParamMat3d>();\n\t\t\tif (p.isConst()) return FEParamValue(this, &p.constValue(), FE_PARAM_MAT3D);\n\t\t\telse return FEParamValue(this, m_pv, m_type);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFEParamMat3d& data = value<FEParamMat3d>(i);\n\t\t\treturn FEParamValue(this, &data, FE_PARAM_MAT3D_MAPPED);\n\t\t}\n\t}\n\tbreak;\n\tcase FE_PARAM_STD_VECTOR_DOUBLE:\n\t{\n\t\tvector<double>& data = value< vector<double> >();\n\t\tif ((i >= 0) && (i < (int)data.size()))\n\t\t\treturn FEParamValue(this, &data[i], FE_PARAM_DOUBLE);\n\t\telse assert(false);\n\t}\n\tbreak;\n\tcase FE_PARAM_STD_VECTOR_VEC2D:\n\t{\n\t\tvector<vec2d>& data = value< vector<vec2d> >();\n\t\tif ((i >= 0) && (i < (int)data.size()))\n\t\t\treturn FEParamValue(this, &data[i], FE_PARAM_VEC2D);\n\t\telse assert(false);\n\t}\n\tbreak;\n\tcase FE_PARAM_STD_VECTOR_STRING:\n\t{\n\t\tvector<string>& data = value< vector<string> >();\n\t\tif ((i >= 0) && (i < (int)data.size()))\n\t\t\treturn FEParamValue(this, &data[i], FE_PARAM_STD_STRING);\n\t}\n\tbreak;\n\tdefault:\n\t{\n\t\tif (i == -1)\n\t\t{\n\t\t\tassert(m_dim == 1);\n\t\t\treturn FEParamValue(this, m_pv, m_type);\n\t\t}\n\t}\n\t}\n\n\treturn FEParamValue();\n}\n\n//-----------------------------------------------------------------------------\n//! This function deletes the existing validator and replaces it with the parameter\n//! passed in the function. \n//! The pvalid can be null in which case the parameter will no longer be validated.\n//! (i.e. is_valid() will always return true.\n//! TODO: Should I delete the validator here? What if it was allocated in a plugin?\n//!       Perhaps I should just return the old validator?\nvoid FEParam::SetValidator(FEParamValidator* pvalid)\n{\n\tif (m_pvalid) delete m_pvalid;\n\tm_pvalid = pvalid;\n}\n\nFEParamValidator* FEParam::GetValidator()\n{\n\treturn m_pvalid;\n}\n\nvoid FEParam::Serialize(DumpStream& ar)\n{\n\tif (ar.IsSaving())\n\t{\n\t\tar << (int) m_type << m_flag;\n\n\t\tbool b = (m_watch ? *m_watch : false);\n\t\tar << (b ? 1 : 0);\n\n\t\tif ((ar.IsShallow() == false) && (m_flag & FEParamFlag::FE_PARAM_USER))\n\t\t{\n\t\t\tassert(m_type == FE_PARAM_DOUBLE);\n\t\t\tar << m_szname;\n\t\t}\n\n\t\tif (m_dim == 1)\n\t\t{\n\t\t\tswitch (m_type)\n\t\t\t{\n\t\t\tcase FE_PARAM_INT       : ar << value<int>(); break;\n\t\t\tcase FE_PARAM_BOOL      : ar << value<bool>(); break;\n\t\t\tcase FE_PARAM_DOUBLE    : ar << value<double>(); break;\n\t\t\tcase FE_PARAM_VEC3D     : ar << value<vec3d>(); break;\n\t\t\tcase FE_PARAM_MAT3D     : ar << value<mat3d>(); break;\n\t\t\tcase FE_PARAM_MAT3DS    : ar << value<mat3ds>(); break;\n\t\t\tcase FE_PARAM_TENS3DRS  : ar << value<tens3ds>(); break;\n\t\t\tcase FE_PARAM_DATA_ARRAY:\n\t\t\t{\n\t\t\t\tFEDataArray& m = value<FEDataArray>();\n\t\t\t\tm.Serialize(ar);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STRING: ar << (const char*)data_ptr(); break;\n\t\t\tcase FE_PARAM_STD_STRING: ar << value<string>(); break;\n\t\t\tcase FE_PARAM_STD_VECTOR_VEC2D: ar << value< std::vector<vec2d> >(); break;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamDouble& p = value<FEParamDouble>();\n\t\t\t\tar << p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_VEC3D_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamVec3& p = value<FEParamVec3>();\n\t\t\t\tar << p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_MAT3D_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamMat3d& p = value<FEParamMat3d>();\n\t\t\t\tar << p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STD_VECTOR_INT:\n\t\t\t{\n\t\t\t\tvector<int>& p = value< vector<int> >();\n\t\t\t\tar << p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STD_VECTOR_DOUBLE:\n\t\t\t{\n\t\t\t\tvector<double>& p = value< vector<double> >();\n\t\t\t\tar << p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tar << m_dim;\n\t\t\tswitch (m_type)\n\t\t\t{\n\t\t\tcase FE_PARAM_INT:\n\t\t\t{\n\t\t\t\tint* pi = (int*) m_pv;\n\t\t\t\tfor (int i = 0; i<m_dim; ++i) ar << pi[i];\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE:\n\t\t\t{\n\t\t\t\tdouble* pv = (double*) m_pv;\n\t\t\t\tfor (int i = 0; i<m_dim; ++i) ar << pv[i];\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamDouble* p = (FEParamDouble*)(m_pv);\n\t\t\t\tfor (int i = 0; i < m_dim; ++i)\n\t\t\t\t{\n\t\t\t\t\tar << p[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tint ntype;\n\t\tar >> ntype >> m_flag;\n\t\tif (ntype != (int) m_type) throw DumpStream::ReadError();\n\n\t\tint watch = 0;\n\t\tar >> watch;\n\t\tif (m_watch) *m_watch = (watch == 1);\n\n\t\tif ((ar.IsShallow() == false) && (m_flag & FEParamFlag::FE_PARAM_USER))\n\t\t{\n\t\t\tassert(m_type == FE_PARAM_DOUBLE);\n\t\t\tchar sz[512] = { 0 };\n\t\t\tar >> sz;\n\t\t\tm_szname = strdup(sz);\n\t\t\tm_pv = new double(0);\n\t\t}\n\n\t\tif (m_dim == 1)\n\t\t{\n\t\t\tswitch (m_type)\n\t\t\t{\n\t\t\tcase FE_PARAM_INT       : ar >> value<int         >(); break;\n\t\t\tcase FE_PARAM_BOOL      : ar >> value<bool        >(); break;\n\t\t\tcase FE_PARAM_DOUBLE    : ar >> value<double      >(); break;\n\t\t\tcase FE_PARAM_VEC3D     : ar >> value<vec3d       >(); break;\n\t\t\tcase FE_PARAM_MAT3D     : ar >> value<mat3d       >(); break;\n\t\t\tcase FE_PARAM_MAT3DS    : ar >> value<mat3ds      >(); break;\n\t\t\tcase FE_PARAM_TENS3DRS  : ar >> value<tens3drs>(); break;\n\t\t\tcase FE_PARAM_DATA_ARRAY:\n\t\t\t{\n\t\t\t\tFEDataArray& m = value<FEDataArray>();\n\t\t\t\tm.Serialize(ar);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STRING: ar >> (char*)data_ptr(); break;\n\t\t\tcase FE_PARAM_STD_STRING: ar >> value<string>(); break;\n\t\t\tcase FE_PARAM_STD_VECTOR_VEC2D: ar >> value< std::vector<vec2d> >(); break;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamDouble& p = value<FEParamDouble>();\n\t\t\t\tar >> p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_VEC3D_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamVec3& p = value<FEParamVec3>();\n\t\t\t\tar >> p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_MAT3D_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamMat3d& p = value<FEParamMat3d>();\n\t\t\t\tar >> p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STD_VECTOR_INT:\n\t\t\t{\n\t\t\t\tvector<int>& p = value< vector<int> >();\n\t\t\t\tar >> p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STD_VECTOR_DOUBLE:\n\t\t\t{\n\t\t\t\tvector<double>& p = value< vector<double> >();\n\t\t\t\tar >> p;\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint ndim = 0;\n\t\t\tar >> ndim;\n\t\t\tif (ndim != (int) m_dim) throw DumpStream::ReadError();\n\t\t\tswitch (m_type)\n\t\t\t{\n\t\t\tcase FE_PARAM_INT:\n\t\t\t{\n\t\t\t\tint* pi = (int*)data_ptr();\n\t\t\t\tfor (int i = 0; i<m_dim; ++i) ar >> pi[i];\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE:\n\t\t\t{\n\t\t\t\tdouble* pv = (double*)data_ptr();\n\t\t\t\tfor (int i = 0; i<m_dim; ++i) ar >> pv[i];\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamDouble* p = (FEParamDouble*)(m_pv);\n\t\t\t\tfor (int i = 0; i < m_dim; ++i)\n\t\t\t\t{\n\t\t\t\t\tar >> p[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t}\n\n\t// serialize the validator\n\tif (m_pvalid) m_pvalid->Serialize(ar);\n}\n\nvoid FEParam::SaveClass(DumpStream& ar, FEParam* p)\n{\n\n}\n\nFEParam* FEParam::LoadClass(DumpStream& ar, FEParam* p)\n{\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\n//! This function copies the state of a parameter to this parameter.\n//! This assumes that the parameters are compatible (i.e. have the same type)\n//! This is used in FEParamContainer::CopyParameterListState()\nbool FEParam::CopyState(const FEParam& p)\n{\n\tif (p.type() != type()) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParam::setParent(FEParamContainer* pc) { m_parent = pc; }\nFEParamContainer* FEParam::parent() { return m_parent; }\n\n//-----------------------------------------------------------------------------\nvoid FEParam::SetWatchVariable(bool* watchvar)\n{\n\tm_watch = watchvar;\n}\n\n//-----------------------------------------------------------------------------\nbool* FEParam::GetWatchVariable()\n{\n\treturn m_watch;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParam::SetWatchFlag(bool b)\n{\n\tif (m_watch) *m_watch = b;\n}\n\n//-----------------------------------------------------------------------------\nbool FEParam::IsHidden() const\n{\n\treturn (m_flag & FEParamFlag::FE_PARAM_HIDDEN);\n}\n\nbool FEParam::IsObsolete() const\n{\n\treturn (m_flag & FEParamFlag::FE_PARAM_OBSOLETE);\n}\n\n//-----------------------------------------------------------------------------\nbool FEParam::IsVolatile() const\n{\n\treturn (m_flag & FEParamFlag::FE_PARAM_VOLATILE);\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParam::MakeVolatile(bool b)\n{\n\tif (b) m_flag = (m_flag | FEParamFlag::FE_PARAM_VOLATILE);\n\telse m_flag = (m_flag & ~FEParamFlag::FE_PARAM_VOLATILE);\n\treturn this;\n}\n\n//-----------------------------------------------------------------------------\nbool FEParam::IsTopLevel() const\n{\n\treturn (m_flag & FEParamFlag::FE_PARAM_TOPLEVEL);\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParam::MakeTopLevel(bool b)\n{\n\tif (b) m_flag = (m_flag | FEParamFlag::FE_PARAM_TOPLEVEL);\n\telse m_flag = (m_flag & ~FEParamFlag::FE_PARAM_TOPLEVEL);\n\treturn this;\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParam::SetFlags(unsigned int flags) { m_flag = flags; return this; }\nunsigned int FEParam::GetFlags() const { return m_flag; }\n\n//-----------------------------------------------------------------------------\n// helper functions for accessing components of parameters via parameter strings\nFEParamValue GetParameterComponent(const ParamString& paramName, FEParam* param)\n{\n\t// make sure we have something to do\n\tif (param == 0) return FEParamValue();\n\n\tif (param->type() == FE_PARAM_DOUBLE)\n\t{\n\t\treturn param->paramValue(paramName.Index());\n\t}\n\telse if (param->type() == FE_PARAM_VEC3D)\n\t{\n\t\tvec3d* v = param->pvalue<vec3d>(0);\n\t\tassert(v);\n\t\tif (v)\n\t\t{\n\t\t\tif      (paramName == \"x\") return FEParamValue(param, &v->x, FE_PARAM_DOUBLE);\n\t\t\telse if (paramName == \"y\") return FEParamValue(param, &v->y, FE_PARAM_DOUBLE);\n\t\t\telse if (paramName == \"z\") return FEParamValue(param, &v->z, FE_PARAM_DOUBLE);\n\t\t\telse return FEParamValue();\n\t\t}\n\t\telse return FEParamValue();\n\t}\n\telse if (param->type() == FE_PARAM_STD_VECTOR_DOUBLE)\n\t{\n\t\tint index = paramName.Index();\n\t\treturn param->paramValue(index);\n\t}\n\telse if (param->type() == FE_PARAM_STD_VECTOR_VEC2D)\n\t{\n\t\tint index = paramName.Index();\n\t\treturn param->paramValue(index);\n\t}\n\telse if (param->type() == FE_PARAM_STD_VECTOR_STRING)\n\t{\n\t\tint index = paramName.Index();\n\t\treturn param->paramValue(index);\n\t}\n\telse if (param->type() == FE_PARAM_DOUBLE_MAPPED)\n\t{\n\t\treturn param->paramValue(paramName.Index());\n\t}\n\telse if (param->type() == FE_PARAM_VEC3D_MAPPED)\n\t{\n\t\treturn param->paramValue(paramName.Index());\n\t}\n\telse if (param->type() == FE_PARAM_MAT3D_MAPPED)\n\t{\n\t\treturn param->paramValue(paramName.Index());\n\t}\n\telse if (param->type() == FE_PARAM_MATERIALPOINT)\n\t{\n\t\treturn param->paramValue(paramName.Index());\n\t}\n\n\treturn FEParamValue();\n}\n\nFECORE_API FEParamValue GetParameterComponent(FEParamValue& paramVal, int index)\n{\n\tswitch (paramVal.type())\n\t{\n\tcase FE_PARAM_STD_VECTOR_INT:\n\t{\n\t\tstd::vector<int>& d = paramVal.value<std::vector<int>>();\n\t\tif ((index >= 0) && (index < d.size())) return FEParamValue(d[index]);\n\t}\n\tbreak;\n\tcase FE_PARAM_STD_VECTOR_DOUBLE:\n\t{\n\t\tstd::vector<double>& d = paramVal.value<std::vector<double>>();\n\t\tif ((index >= 0) && (index < d.size())) return FEParamValue(d[index]);\n\t}\n\tbreak;\n\tcase FE_PARAM_STD_VECTOR_VEC2D:\n\t{\n\t\tstd::vector<vec2d>& d = paramVal.value<std::vector<vec2d>>();\n\t\tif ((index >= 0) && (index < d.size())) return FEParamValue(d[index]);\n\t}\n\tbreak;\n\tcase FE_PARAM_DOUBLE_MAPPED:\n\t{\n\t\tFEParam* p = paramVal.param(); assert(p);\n\t\tif (p->dim() > 0)\n\t\t{\n\t\t\tFEParamDouble* d = p->pvalue<FEParamDouble>(index);\n\t\t\treturn FEParamValue(p, d, p->type());\n\t\t}\n\t}\n\tbreak;\n\t}\n\tassert(false);\n\treturn FEParamValue();\n}\n\nFECORE_API FEParamValue GetParameterComponent(FEParamValue& paramVal, const char* szcomp)\n{\n\tswitch (paramVal.type())\n\t{\n\tcase FE_PARAM_VEC2D:\n\t{\n\t\tvec3d& v = paramVal.value<vec3d>();\n\t\tif      (strcmp(szcomp, \"x\") == 0) return FEParamValue(v.x);\n\t\telse if (strcmp(szcomp, \"y\") == 0) return FEParamValue(v.y);\n\t}\n\tbreak;\n\tcase FE_PARAM_VEC3D:\n\t{\n\t\tvec3d& v = paramVal.value<vec3d>();\n\t\tif      (strcmp(szcomp, \"x\") == 0) return FEParamValue(v.x);\n\t\telse if (strcmp(szcomp, \"y\") == 0) return FEParamValue(v.y);\n\t\telse if (strcmp(szcomp, \"z\") == 0) return FEParamValue(v.z);\n\t}\n\tbreak;\n\t}\n\tassert(false);\n\treturn FEParamValue();\n}\n"
  },
  {
    "path": "FECore/FEParam.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include \"mat3d.h\"\n#include <assert.h>\n#include <vector>\n#include \"fecore_api.h\"\n#include \"ParamString.h\"\n#include \"units.h\"\n\n//-----------------------------------------------------------------------------\nclass FEParamValidator;\nclass DumpStream;\nclass FEParamContainer;\n\n//-----------------------------------------------------------------------------\n// Different supported parameter types\nenum FEParamType {\n\tFE_PARAM_INVALID,\n\tFE_PARAM_INT,\n\tFE_PARAM_BOOL,\n\tFE_PARAM_DOUBLE,\n\tFE_PARAM_VEC2D,\n\tFE_PARAM_VEC3D,\n\tFE_PARAM_MAT3D,\n\tFE_PARAM_MAT3DS,\n\tFE_PARAM_STRING,\n\tFE_PARAM_DATA_ARRAY,\n\tFE_PARAM_TENS3DRS,\n\tFE_PARAM_STD_STRING,\n\tFE_PARAM_STD_VECTOR_INT,\n\tFE_PARAM_STD_VECTOR_DOUBLE,\n\tFE_PARAM_STD_VECTOR_VEC2D,\n\tFE_PARAM_STD_VECTOR_STRING,\n\tFE_PARAM_DOUBLE_MAPPED,\n\tFE_PARAM_VEC3D_MAPPED,\n\tFE_PARAM_MAT3D_MAPPED,\n\tFE_PARAM_MAT3DS_MAPPED,\n\tFE_PARAM_MATERIALPOINT,\n};\n\n//-----------------------------------------------------------------------------\n// Parameter flags\nenum FEParamFlag {\n\tFE_PARAM_ATTRIBUTE = 0x01,\t\t// parameter will be read as attribute\n\tFE_PARAM_USER      = 0x02,\t\t// user parameter (owned by parameter list)\t\n\tFE_PARAM_HIDDEN\t   = 0x04,\t\t// Hides parameter (in FEBio Studio)\n\tFE_PARAM_ADDLC     = 0x08,\t\t// parameter should get a default load curve in FEBio Studio\n\tFE_PARAM_VOLATILE  = 0x10,\t\t// parameter can change (e.g. via a load curve)\n\tFE_PARAM_TOPLEVEL  = 0x20,\t\t// parameter should only defined at top-level (materials only)\n\tFE_PARAM_WATCH\t   = 0x40,\t\t// This is a watch parameter \n\tFE_PARAM_OBSOLETE  = 0x80\t\t// Parameter is obsolete\n};\n\nclass FEParam;\n\n//-----------------------------------------------------------------------------\n// class describing the value of parameter\nclass FEParamValue\n{\nprivate:\n\tvoid*\t\t\tm_pv;\t\t// pointer to variable data\n\tFEParamType\t\tm_itype;\t// type of variable (this is not the type of the param!)\n\tFEParam*\t\tm_param;\t// the parameter (can be null if it is not a parameter)\n\npublic:\n\n\tFEParamValue()\n\t{\n\t\tm_pv = 0;\n\t\tm_itype = FE_PARAM_INVALID;\n\t\tm_param = 0;\n\t}\n\n\texplicit FEParamValue(FEParam* p, void* v, FEParamType itype)\n\t{\n\t\tm_pv = v;\n\t\tm_itype = itype;\n\t\tm_param = p;\n\t}\n\n\tFEParamValue(int&    v) : FEParamValue(0, &v, FE_PARAM_INT) {}\n\tFEParamValue(double& v) : FEParamValue(0, &v, FE_PARAM_DOUBLE) {}\n\tFEParamValue(vec2d&  v) : FEParamValue(0, &v, FE_PARAM_VEC2D) {}\n\tFEParamValue(vec3d&  v) : FEParamValue(0, &v, FE_PARAM_VEC3D) {}\n\tFEParamValue(mat3ds& v) : FEParamValue(0, &v, FE_PARAM_MAT3DS) {}\n    FEParamValue(mat3d&  v) : FEParamValue(0, &v, FE_PARAM_MAT3D ) {}\n\n\tbool isValid() const { return (m_pv != 0); }\n\n\tFEParamType type() const { return m_itype; }\n\n\tvoid* data_ptr() const { return m_pv; }\n\n\tFEParam* param() { return m_param; }\n\n\ttemplate <typename T> T& value() { return *((T*)m_pv); }\n\ttemplate <typename T> const T& value() const { return *((T*)m_pv); }\n\n\tFECORE_API FEParamValue component(int n);\n};\n\n//-----------------------------------------------------------------------------\n//! This class describes a user-defined parameter\nclass FECORE_API FEParam\n{\nprivate:\n\tvoid*\t\t\tm_pv;\t\t// pointer to variable data\n\tint\t\t\t\tm_dim;\t\t// dimension (in case data is array)\n\tFEParamType\t\tm_type;\t\t// type of variable\n\tunsigned int\tm_flag;\t\t// parameter flags\n\tbool*\t\t\tm_watch;\t// parameter watch (set to true if read in)\n\tint\t\t\t\tm_group;\t// index of parameter group (-1 by default)\n\n\tconst char*\tm_szname;\t// name of the parameter\n\tconst char*\tm_szenum;\t// enumerate values for ints\n\tconst char* m_szunit;\t// unit string\n\tconst char* m_szlongname;\t// a longer, more descriptive name (optional)\n\n\t// parameter validator\n\tFEParamValidator*\tm_pvalid;\n\n\tFEParamContainer* m_parent;\t// parent object of parameter\n\npublic:\n\t// constructor\n\tFEParam(void* pdata, FEParamType itype, int ndim, const char* szname, bool* watch = nullptr);\n\tFEParam(const FEParam& p);\n\t~FEParam();\n\tFEParam& operator = (const FEParam& p);\n\n\t// set the parameter's validator\n\tvoid SetValidator(FEParamValidator* pvalid);\n\n\tFEParamValidator* GetValidator();\n\n\t// see if the parameter's value is valid\n\tbool is_valid() const;\n\n\t// return the name of the parameter\n\tconst char* name() const;\n\n\t// return the long name of the parameter\n\tconst char* longName() const;\n\n\t// return the enum values\n\tconst char* enums() const;\n\n\t// get the current enum value (or nullptr)\n\tconst char* enumKey() const;\n\n\t// get the unit string\n\tconst char* units() const;\n\tFEParam* setUnits(const char* szunit);\n\n\t// set the enum values (\\0 separated. Make sure the end of the string has two \\0's)\n\tFEParam* setEnums(const char* sz);\n\n\t// set the long name\n\tFEParam* setLongName(const char* sz);\n\n\t// parameter dimension\n\tint dim() const;\n\n\t// parameter type\n\tFEParamType type() const;\n\n\t// data pointer\n\tvoid* data_ptr() const;\n\n\t// get the param value\n\tFEParamValue paramValue(int i = -1);\n\n\t// Copy the state of one parameter to this parameter.\n\t// This requires that the parameters are compatible (i.e. same type, etc.)\n\tbool CopyState(const FEParam& p);\n\n\tvoid setParent(FEParamContainer* pc);\n\tFEParamContainer* parent();\n\n\tFEParam* SetFlags(unsigned int flags);\n\tunsigned int GetFlags() const;\n\n\tvoid SetWatchVariable(bool* watchVar);\n\tbool* GetWatchVariable();\n\tvoid SetWatchFlag(bool b);\n\n\tbool IsHidden() const;\n\n\tbool IsObsolete() const;\n\n\tbool IsVolatile() const;\n\n\tFEParam* MakeVolatile(bool b);\n\n\tbool IsTopLevel() const;\n\tFEParam* MakeTopLevel(bool b);\n\npublic:\n\tint GetParamGroup() const;\n\tvoid SetParamGroup(int i);\n\npublic:\n\tvoid Serialize(DumpStream& ar);\n\n\tstatic void SaveClass(DumpStream& ar, FEParam* p);\n\tstatic FEParam* LoadClass(DumpStream& ar, FEParam* p);\n\npublic:\n\t//! retrieves the value for a non-array item\n\ttemplate <class T> T& value() { return *((T*) data_ptr()); }\n\n\t//! retrieves the value for a non-array item\n\ttemplate <class T> const T& value() const { return *((T*) data_ptr()); }\n\n\t//! retrieves the value for an array item\n\ttemplate <class T> T* pvalue() { return (T*) data_ptr(); }\n\n\t//! retrieves the value for an array item\n\ttemplate <class T> T& value(int i) { return ((T*)data_ptr())[i]; }\n\ttemplate <class T> T value(int i) const { return ((T*) data_ptr())[i]; }\n\n\t//! retrieves pointer to element in array\n\ttemplate <class T> T* pvalue(int n);\n\n\t//! override the template for char pointers\n\tchar* cvalue();\n};\n\n//-----------------------------------------------------------------------------\n//! Retrieves a pointer to element in array\ntemplate<class T> inline T* FEParam::pvalue(int n)\n{\n\tassert((n >= 0) && (n < m_dim));\n\treturn &(pvalue<T>()[n]);\n}\n\n//-----------------------------------------------------------------------------\nFECORE_API FEParamValue GetParameterComponent(const ParamString& paramName, FEParam* param);\nFECORE_API FEParamValue GetParameterComponent(FEParamValue& paramVal, int index);\nFECORE_API FEParamValue GetParameterComponent(FEParamValue& paramVal, const char* szcomp);\n"
  },
  {
    "path": "FECore/FEParamValidator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEParamValidator.h\"\n#include \"FEParam.h\"\n#include \"FECoreKernel.h\"\n#include \"DumpStream.h\"\n#include \"FEModelParam.h\"\n\n//-----------------------------------------------------------------------------\nbool is_inside_range_int(int ival, int rng, int imin, int imax)\n{\n\tswitch (rng)\n\t{\n\tcase FE_GREATER         : return (ival >  imin); break;\n\tcase FE_GREATER_OR_EQUAL: return (ival >= imin); break;\n\tcase FE_LESS            : return (ival <  imin); break;\n\tcase FE_LESS_OR_EQUAL   : return (ival <= imin); break;\n\tcase FE_OPEN            : return ((ival >  imin) && (ival <  imax)); break;\n\tcase FE_CLOSED          : return ((ival >= imin) && (ival <= imax)); break;\n\tcase FE_LEFT_OPEN       : return ((ival >  imin) && (ival <= imax)); break;\n\tcase FE_RIGHT_OPEN      : return ((ival >= imin) && (ival <  imax)); break;\n\tcase FE_NOT_EQUAL       : return (ival != imin); break;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool is_inside_range_double(double val, int rng, double dmin, double dmax)\n{\n\tswitch (rng)\n\t{\n\tcase FE_GREATER         : return (val >  dmin); break;\n\tcase FE_GREATER_OR_EQUAL: return (val >= dmin); break;\n\tcase FE_LESS            : return (val <  dmin); break;\n\tcase FE_LESS_OR_EQUAL   : return (val <= dmin); break;\n\tcase FE_OPEN            : return ((val >  dmin) && (val <  dmax)); break;\n\tcase FE_CLOSED          : return ((val >= dmin) && (val <= dmax)); break;\n\tcase FE_LEFT_OPEN       : return ((val >  dmin) && (val <= dmax)); break;\n\tcase FE_RIGHT_OPEN      : return ((val >= dmin) && (val <  dmax)); break;\n\tcase FE_NOT_EQUAL       : return (val != dmin); break;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool FEIntValidator::is_valid(const FEParam& p) const\n{\n\tif (p.type() != FE_PARAM_INT) return false;\n\n\tbool bvalid = true;\n\tint val = 0;\n\tif (p.dim() == 1)\n\t{\n\t\tval = p.value<int>();\n\t\tbvalid = is_inside_range_int(val, m_rng, m_nmin, m_nmax);\n\t}\n\telse \n\t{\n\t\tfor (int i = 0; i<p.dim(); ++i)\n\t\t{\n\t\t\tval = p.value<int>(i);\n\t\t\tbvalid = is_inside_range_int(val, m_rng, m_nmin, m_nmax);\n\t\t\tif (bvalid == false) break;\n\t\t}\n\t}\n\n\treturn bvalid;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEIntValidator::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_rng;\n\tar & m_nmin & m_nmax;\n}\n\n//-----------------------------------------------------------------------------\nbool FEDoubleValidator::is_valid(const FEParam& p) const\n{\n\tif (p.type() != FE_PARAM_DOUBLE) return false;\n\n\tbool bvalid;\n\tdouble val = 0;\n\tif (p.dim() == 1)\n\t{\n\t\tval = p.value<double>();\n\t\tbvalid = is_inside_range_double(val, m_rng, m_fmin, m_fmax);\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i<p.dim(); ++i)\n\t\t{\n\t\t\tval = p.value<double>(i);\n\t\t\tbvalid = is_inside_range_double(val, m_rng, m_fmin, m_fmax);\n\t\t\tif (bvalid == false) break;\n\t\t}\n\t}\n\n\treturn bvalid;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEDoubleValidator::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_rng;\n\tar & m_fmin & m_fmax;\n}\n\n//-----------------------------------------------------------------------------\nbool FEParamDoubleValidator::is_valid(const FEParam& p) const\n{\n\tif (p.type() != FE_PARAM_DOUBLE_MAPPED) return false;\n\tconst FEParamDouble& d = p.value<FEParamDouble>();\n\n\t// This only works for const values\n\tdouble val = 0.0;\n\tbool bvalid = true;\n\tif (d.isConst())\n\t{\n\t\tval = d.constValue();\n\t\tbvalid = is_inside_range_double(val, m_rng, m_fmin, m_fmax);\n\t}\n\n\treturn bvalid;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParamDoubleValidator::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\tar & m_rng;\n\tar & m_fmin & m_fmax;\n}\n"
  },
  {
    "path": "FECore/FEParamValidator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass FEParam;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! Range for parameters\n//! Used only by parameters of type FE_PARAM_INT and FE_PARAM_DOUBLE\nenum FEParamRange {\n\tFE_DONT_CARE,\t\t\t// parameter can take on any value\n\tFE_GREATER,\t\t\t\t// parameter must be greater than val (> val)\n\tFE_GREATER_OR_EQUAL,\t// parameter must be greater or equal than val ( >= val)\n\tFE_LESS,\t\t\t\t// parameter must be less than val ( < val)\n\tFE_LESS_OR_EQUAL,\t\t// parameter must be less or equal than val ( <= val)\n\tFE_OPEN,\t\t\t\t// parameter must be inside the open interval (min,max)\n\tFE_CLOSED,\t\t\t\t// parameter must be inside the closed interval [min, max]\n\tFE_LEFT_OPEN,\t\t\t// parameter must be inside the half-open interval (min, max]\n\tFE_RIGHT_OPEN,\t\t\t// parameter must be inside the right-open interval [min, max)\n\tFE_NOT_EQUAL,\t\t\t// parameter must not equal val ( != val)\n};\n\n//-----------------------------------------------------------------------------\n// helper class for defining ranges\nstruct RANGE\n{\npublic:\n\tFEParamRange\tm_rt;\n\tdouble\t\t\tm_fmin;\n\tdouble\t\t\tm_fmax;\npublic:\n\tRANGE(FEParamRange rt) { m_rt = rt; m_fmin = m_fmax = 0.0; }\n\tRANGE(FEParamRange rt, double fval) { m_rt = rt; m_fmin = fval; m_fmax = 0.0; }\n\tRANGE(FEParamRange rt, double fmin, double fmax) { m_rt = rt; m_fmin = fmin; m_fmax = fmax; }\n};\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for parameter validators. \nclass FECORE_API FEParamValidator\n{\npublic:\n\tFEParamValidator(){}\n\tvirtual ~FEParamValidator(){}\n\n\t//! Function that is used to see if a parameter is valid\n\tvirtual bool is_valid(const FEParam& p) const = 0;\n\n\t//! Creates a copy of the validator\n\tvirtual FEParamValidator* copy() const = 0;\n\n\t//! Serialize validator data\n\tvirtual void Serialize(DumpStream& ar) = 0;\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for parameter validators that uses CRTP for implementing a copy method.\n//! Deriving validators from this class automatically adds the copy method which simplifies\n//! the implementation of new validators. It does assume that the derived class has a valid\n//! copy constructor.\ntemplate <class T> class FEParamValidator_ : public FEParamValidator\n{\npublic:\n\tFEParamValidator* copy() const { return new T(static_cast<T const&>(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n//! Validator for FE_PARAM_INT parameters that does basic range checking.\nclass FEIntValidator : public FEParamValidator_<FEIntValidator>\n{\npublic:\n\tFEIntValidator(FEParamRange rng, int nmin, int nmax) : m_rng(rng), m_nmin(nmin), m_nmax(nmax) {}\n\n\t//! See if the parameter is an FE_PARAM_INT and within the specified range\n\tbool is_valid(const FEParam& p) const;\n\n\tvoid Serialize(DumpStream& ar);\n\n\tRANGE GetRange() { return { (FEParamRange)m_rng, (double)m_nmin, (double)m_nmax }; }\n\nprivate:\n\tint\t\tm_rng;\n\tint\t\tm_nmin;\n\tint\t\tm_nmax;\n};\n\n//-----------------------------------------------------------------------------\n//! Validator for FE_PARAM_DOUBLE parameters that does basic range checking.\nclass FEDoubleValidator : public FEParamValidator_<FEDoubleValidator>\n{\npublic:\n\tFEDoubleValidator(FEParamRange rng, double fmin, double fmax) : m_rng(rng), m_fmin(fmin), m_fmax(fmax) {}\n\n\t//! See if the parameter is an FE_PARAM_DOUBLE and within the specified range\n\tbool is_valid(const FEParam& p) const;\n\n\tvoid Serialize(DumpStream& ar);\n\n\tRANGE GetRange() { return { (FEParamRange)m_rng, m_fmin, m_fmax }; }\n\nprivate:\n\tint\t\t\tm_rng;\n\tdouble\t\tm_fmin;\n\tdouble\t\tm_fmax;\n};\n\n//-----------------------------------------------------------------------------\n//! Validator for FE_PARAM_DOUBLE parameters that does basic range checking.\nclass FEParamDoubleValidator : public FEParamValidator_<FEParamDoubleValidator>\n{\npublic:\n\tFEParamDoubleValidator(FEParamRange rng, double fmin, double fmax) : m_rng(rng), m_fmin(fmin), m_fmax(fmax) {}\n\n\t//! See if the parameter is an FE_PARAM_DOUBLE and within the specified range\n\tbool is_valid(const FEParam& p) const;\n\n\tvoid Serialize(DumpStream& ar);\n\n\tRANGE GetRange() { return { (FEParamRange)m_rng, m_fmin, m_fmax }; }\n\nprivate:\n\tint\t\t\tm_rng;\n\tdouble\t\tm_fmin;\n\tdouble\t\tm_fmax;\n};\n\n\ninline RANGE FE_RANGE_DONT_CARE() { return RANGE(FE_DONT_CARE); }\ninline RANGE FE_RANGE_GREATER         (double f) { return RANGE(FE_GREATER         , f); }\ninline RANGE FE_RANGE_GREATER_OR_EQUAL(double f) { return RANGE(FE_GREATER_OR_EQUAL, f); }\ninline RANGE FE_RANGE_LESS            (double f) { return RANGE(FE_LESS            , f); }\ninline RANGE FE_RANGE_LESS_OR_EQUAL   (double f) { return RANGE(FE_LESS_OR_EQUAL   , f); }\ninline RANGE FE_RANGE_OPEN      (double fmin, double fmax) {return RANGE(FE_OPEN      , fmin, fmax); }\ninline RANGE FE_RANGE_CLOSED    (double fmin, double fmax) {return RANGE(FE_CLOSED    , fmin, fmax); }\ninline RANGE FE_RANGE_LEFT_OPEN (double fmin, double fmax) {return RANGE(FE_LEFT_OPEN , fmin, fmax); }\ninline RANGE FE_RANGE_RIGHT_OPEN(double fmin, double fmax) {return RANGE(FE_RIGHT_OPEN, fmin, fmax); }\ninline RANGE FE_RANGE_NOT_EQUAL (double f) { return RANGE(FE_NOT_EQUAL, f); }\n"
  },
  {
    "path": "FECore/FEParameterList.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEParameterList.h\"\n#include \"FECoreKernel.h\"\n#include \"DumpStream.h\"\n#include \"tens3d.h\"\n#include \"FEModelParam.h\"\n#include <string>\n#include <assert.h>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nFEParameterList::FEParameterList(FEParamContainer* pc) : m_pc(pc) \n{\n\tm_currentGroup = -1;\n}\n\n//-----------------------------------------------------------------------------\nFEParameterList::~FEParameterList() \n{\n}\n\n//-----------------------------------------------------------------------------\n//! This function copies the parameter data from the passed parameter list.\n//! This assumes that the two parameter lists are identical.\nvoid FEParameterList::operator = (FEParameterList& l)\n{\n\tif (m_pl.size() != l.m_pl.size()) { assert(false); return; }\n\n\tlist<FEParam>::iterator ps, pd;\n\tps = l.m_pl.begin();\n\tpd = m_pl.begin();\n\tfor (; pd != m_pl.end(); ++pd, ++ps)\n\t{\n\t\tFEParam& s = *ps;\n\t\tFEParam& d = *pd;\n\n\t\tif (s.type() != d.type()) { assert(false); return; }\n\t\tif (s.dim() != d.dim()) { assert(false); return; }\n\t\tif (s.dim() == 1)\n\t\t{\n\t\t\tswitch (s.type())\n\t\t\t{\n\t\t\tcase FE_PARAM_INT   : d.value<int   >() = s.value<int   >(); break;\n\t\t\tcase FE_PARAM_BOOL  : d.value<bool  >() = s.value<bool  >(); break;\n\t\t\tcase FE_PARAM_DOUBLE: d.value<double>() = s.value<double>(); break;\n\t\t\tcase FE_PARAM_VEC3D : d.value<vec3d >() = s.value<vec3d >(); break;\n\t\t\tcase FE_PARAM_MAT3D : d.value<mat3d >() = s.value<mat3d >(); break;\n\t\t\tcase FE_PARAM_MAT3DS: d.value<mat3ds>() = s.value<mat3ds>(); break;\n\t\t\tcase FE_PARAM_TENS3DRS: d.value<tens3drs>() = s.value<tens3drs>(); break;\n\t\t\tcase FE_PARAM_STD_STRING: d.value<std::string>() = s.value<std::string>(); break;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamDouble& mat3d = d.value<FEParamDouble>();\n\t\t\t\tFEParamDouble& src = s.value<FEParamDouble>();\n\t\t\t\tmat3d.setValuator(src.valuator()->copy());\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_MAT3D_MAPPED:\n\t\t\t{\n\t\t\t\tFEParamMat3d& mat3d = d.value<FEParamMat3d>();\n\t\t\t\tFEParamMat3d& src = s.value<FEParamMat3d>();\n\t\t\t\tmat3d.setValuator(src.valuator()->copy());\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase FE_PARAM_STD_VECTOR_VEC2D:\n\t\t\t{\n\t\t\t\td.value< std::vector<vec2d> >() = s.value< std::vector<vec2d> >();\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (s.type())\n\t\t\t{\n\t\t\tcase FE_PARAM_INT:\n\t\t\t\t{\n\t\t\t\t\tfor (int i=0; i<s.dim(); ++i) d.pvalue<int>()[i] = s.pvalue<int>()[i];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE:\n\t\t\t\t{\n\t\t\t\t\tfor (int i=0; i<s.dim(); ++i) d.pvalue<double>()[i] = s.pvalue<double>()[i];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FE_PARAM_DOUBLE_MAPPED:\n\t\t\t\t{\n\t\t\t\t\tfor (int i=0; i<s.dim(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamDouble& pd = d.value<FEParamDouble>(i);\n\t\t\t\t\t\tFEParamDouble& ps = s.value<FEParamDouble>(i);\n\t\t\t\t\t\tassert(ps.isConst());\n\t\t\t\t\t\tpd = ps.constValue();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function adds a parameter to the parameter list\nFEParam* FEParameterList::AddParameter(void *pv, FEParamType itype, int ndim, const char *sz, bool* watch)\n{\n\t// sanity checks\n\tassert(pv);\n\tassert(sz);\n\n\t// create a new parameter object\n\tFEParam p(pv, itype, ndim, sz, watch);\n\tp.SetParamGroup(m_currentGroup);\n\n\t// add the parameter to the list\n\tm_pl.push_back(p);\n\n\treturn &(m_pl.back());\n}\n//-----------------------------------------------------------------------------\n// This function adds a parameter to the parameter list\nFEParam* FEParameterList::AddParameter(void *pv, FEParamType itype, int ndim, FEParamRange rng, double fmin, double fmax, const char *sz)\n{\n\tassert(pv);\n\tassert(sz);\n\n\t// create a new parameter object\n\tFEParam p(pv, itype, ndim, sz);\n\tp.SetParamGroup(m_currentGroup);\n\n\t// set the range\n\t// (range checking is only supported for int and double params)\n\tif (rng != FE_DONT_CARE)\n\t{\n\t\tif      (itype == FE_PARAM_INT) p.SetValidator(new FEIntValidator(rng, (int) fmin, (int) fmax));\n\t\telse if (itype == FE_PARAM_DOUBLE) p.SetValidator(new FEDoubleValidator(rng, fmin, fmax));\n\t\telse if (itype == FE_PARAM_DOUBLE_MAPPED) p.SetValidator(new FEParamDoubleValidator(rng, fmin, fmax));\n\t}\n\n\t// add the parameter to the list\n\tm_pl.push_back(p);\n\n\treturn &(m_pl.back());\n}\n\n//-----------------------------------------------------------------------------\n// Find a parameter using its data pointer\nFEParam* FEParameterList::FindFromData(void* pv)\n{\n\tFEParam* pp = 0;\n\tif (m_pl.empty() == false)\n\t{\n\t\tlist<FEParam>::iterator it;\n\t\tfor (it = m_pl.begin(); it != m_pl.end(); ++it)\n\t\t{\n\t\t\tif (it->dim() <= 1)\n\t\t\t{\n\t\t\t\tif (it->data_ptr() == pv)\n\t\t\t\t{\n\t\t\t\t\tpp = &(*it);\n\t\t\t\t\treturn pp;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < it->dim(); ++i)\n\t\t\t\t{\n\t\t\t\t\tvoid* pd = nullptr;\n\t\t\t\t\tswitch (it->type())\n\t\t\t\t\t{\n\t\t\t\t\tcase FE_PARAM_DOUBLE_MAPPED: pd = &(it->value<FEParamDouble>(i)); break;\n\t\t\t\t\tcase FE_PARAM_INT: pd = &(it->value<int>(i)); break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tassert(false);\n\t\t\t\t\t}\n\t\t\t\t\tif (pv == pd)\n\t\t\t\t\t{\n\t\t\t\t\t\tpp = &(*it);\n\t\t\t\t\t\treturn pp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn pp;\n}\n\n//-----------------------------------------------------------------------------\n// This function searches the parameters in the list for a parameter\n// with the name given by the input argument\n// \\param sz name of parameter to find\nFEParam* FEParameterList::FindFromName(const char* sz)\n{\n\tif (sz == 0) return 0;\n\n\tFEParam* pp = 0;\n\tif (m_pl.size() > 0)\n\t{\n\t\tlist<FEParam>::iterator it;\n\t\tfor (it = m_pl.begin(); it != m_pl.end(); ++it)\n\t\t{\n\t\t\tif (strcmp(it->name(), sz) == 0)\n\t\t\t{\n\t\t\t\tpp = &(*it);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pp;\n}\n\n//-----------------------------------------------------------------------------\nint FEParameterList::SetActiveGroup(const char* szgroup)\n{\n\tif (szgroup == nullptr) m_currentGroup = -1;\n\telse\n\t{\n\t\tm_currentGroup = -1;\n\t\tfor (size_t i = 0; i < m_pg.size(); ++i)\n\t\t{\n\t\t\tif (strcmp(m_pg[i], szgroup) == 0)\n\t\t\t{\n\t\t\t\tm_currentGroup = i;\n\t\t\t}\n\t\t}\n\t\tif (m_currentGroup == -1)\n\t\t{\n\t\t\tm_currentGroup = (int)m_pg.size();\n\t\t\tm_pg.push_back(szgroup);\n\t\t}\n\t}\n\treturn m_currentGroup;\n}\n\n//-----------------------------------------------------------------------------\nint FEParameterList::GetActiveGroup()\n{\n\treturn m_currentGroup;\n}\n\n//-----------------------------------------------------------------------------\nint FEParameterList::ParameterGroups() const\n{\n\treturn (int)m_pg.size();\n}\n\n//-----------------------------------------------------------------------------\nconst char* FEParameterList::GetParameterGroupName(int i)\n{\n\treturn m_pg[i];\n}\n\n//=============================================================================\nFEParamContainer::FEParamContainer()\n{\n\tm_pParam = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEParamContainer::~FEParamContainer()\n{\n\tdelete m_pParam; \n\tm_pParam = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEParameterList& FEParamContainer::GetParameterList()\n{\n\tif (m_pParam == 0) \n\t{\n\t\tm_pParam = new FEParameterList(this);\n\t\tBuildParamList();\n\t}\n\treturn *m_pParam;\n}\n\n//-----------------------------------------------------------------------------\nconst FEParameterList& FEParamContainer::GetParameterList() const\n{\n\tassert(m_pParam);\n\treturn *m_pParam;\n}\n\n//-----------------------------------------------------------------------------\n// Find a parameter from its name\nFEParam* FEParamContainer::GetParameter(const char* szname)\n{\n\tFEParameterList& pl = GetParameterList();\n\treturn pl.FindFromName(szname);\n}\n\n//-----------------------------------------------------------------------------\n// Find a parameter from its name\nFEParam* FEParamContainer::FindParameter(const ParamString& s)\n{\n\tFEParameterList& pl = GetParameterList();\n\treturn pl.FindFromName(s.c_str());\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParamContainer::FindParameterFromData(void* pv)\n{\n\tFEParameterList& pl = GetParameterList();\n\treturn pl.FindFromData(pv);\n}\n\n//-----------------------------------------------------------------------------\n//! This function will be overridden by each class that defines a parameter list\nvoid FEParamContainer::BuildParamList() \n{\n}\n\n//-----------------------------------------------------------------------------\n// Add a parameter to the parameter list\nFEParam* FEParamContainer::AddParameter(void* pv, FEParamType itype, int ndim, const char* sz, bool* watch)\n{\n\tassert(m_pParam);\n\tFEParam* p = m_pParam->AddParameter(pv, itype, ndim, sz, watch);\n\tp->setParent(this);\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\n// Add a parameter to the parameter list\nFEParam* FEParamContainer::AddParameter(void* pv, FEParamType itype, int ndim, RANGE rng, const char* sz)\n{\n\tassert(m_pParam);\n\tFEParam* p = m_pParam->AddParameter(pv, itype, ndim, rng.m_rt, rng.m_fmin, rng.m_fmax, sz);\n\tp->setParent(this);\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParamContainer::AddParameter(int&                      v, const char* sz) { return AddParameter(&v, FE_PARAM_INT, 1, sz); }\nFEParam* FEParamContainer::AddParameter(bool&                     v, const char* sz) { return AddParameter(&v, FE_PARAM_BOOL, 1, sz); }\nFEParam* FEParamContainer::AddParameter(double&                   v, const char* sz) { return AddParameter(&v, FE_PARAM_DOUBLE, 1, sz); }\nFEParam* FEParamContainer::AddParameter(vec2d&                    v, const char* sz) { return AddParameter(&v, FE_PARAM_VEC2D, 1, sz); }\nFEParam* FEParamContainer::AddParameter(vec3d&                    v, const char* sz) { return AddParameter(&v, FE_PARAM_VEC3D, 1, sz); }\nFEParam* FEParamContainer::AddParameter(mat3d&                    v, const char* sz) { return AddParameter(&v, FE_PARAM_MAT3D, 1, sz); }\nFEParam* FEParamContainer::AddParameter(mat3ds&                   v, const char* sz) { return AddParameter(&v, FE_PARAM_MAT3DS, 1, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamDouble&            v, const char* sz) { return AddParameter(&v, FE_PARAM_DOUBLE_MAPPED, 1, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamVec3&              v, const char* sz) { return AddParameter(&v, FE_PARAM_VEC3D_MAPPED, 1, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamMat3d&             v, const char* sz) { return AddParameter(&v, FE_PARAM_MAT3D_MAPPED, 1, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamMat3ds&            v, const char* sz) { return AddParameter(&v, FE_PARAM_MAT3DS_MAPPED, 1, sz); }\nFEParam* FEParamContainer::AddParameter(FEDataArray&              v, const char* sz) { return AddParameter(&v, FE_PARAM_DATA_ARRAY, 1, sz); }\nFEParam* FEParamContainer::AddParameter(tens3drs& \t\t          v, const char* sz) { return AddParameter(&v, FE_PARAM_TENS3DRS, 1, sz); }\nFEParam* FEParamContainer::AddParameter(std::string&              v, const char* sz) { return AddParameter(&v, FE_PARAM_STD_STRING, 1, sz); }\nFEParam* FEParamContainer::AddParameter(std::vector<int>&         v, const char* sz) { return AddParameter(&v, FE_PARAM_STD_VECTOR_INT, 1, sz); }\nFEParam* FEParamContainer::AddParameter(std::vector<double>&      v, const char* sz) { return AddParameter(&v, FE_PARAM_STD_VECTOR_DOUBLE, 1, sz); }\nFEParam* FEParamContainer::AddParameter(std::vector<vec2d>&       v, const char* sz) { return AddParameter(&v, FE_PARAM_STD_VECTOR_VEC2D, 1, sz); }\nFEParam* FEParamContainer::AddParameter(std::vector<std::string>& v, const char* sz) { return AddParameter(&v, FE_PARAM_STD_VECTOR_STRING, 1, sz); }\nFEParam* FEParamContainer::AddParameter(FEMaterialPointProperty&  v, const char* sz) { return AddParameter(&v, FE_PARAM_MATERIALPOINT, 1, sz); }\n//FEParam* FEParamContainer::AddParameter(Image& v                   , const char* sz) { return AddParameter(&v, FE_PARAM_IMAGE_3D, 1, sz); }\n\nFEParam* FEParamContainer::AddParameter(int&           v, RANGE rng, const char* sz) { return AddParameter(&v, FE_PARAM_INT, 1, rng, sz); }\nFEParam* FEParamContainer::AddParameter(double&        v, RANGE rng, const char* sz) { return AddParameter(&v, FE_PARAM_DOUBLE, 1, rng, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamDouble& v, RANGE rng, const char* sz) { return AddParameter(&v, FE_PARAM_DOUBLE_MAPPED, 1, rng, sz); }\n\nFEParam* FEParamContainer::AddParameter(double&        v, const char* sz, bool& watch) { return AddParameter(&v, FE_PARAM_DOUBLE, 1, sz, &watch); }\n\nFEParam* FEParamContainer::AddParameter(int*           v, int ndim, const char* sz) { return AddParameter(v, FE_PARAM_INT, ndim, sz); }\nFEParam* FEParamContainer::AddParameter(double*        v, int ndim, const char* sz) { return AddParameter(v, FE_PARAM_DOUBLE, ndim, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamDouble* v, int ndim, const char* sz) { return AddParameter(v, FE_PARAM_DOUBLE_MAPPED, ndim, sz); }\n\nFEParam* FEParamContainer::AddParameter(int*           v, int ndim, RANGE rng, const char* sz) { return AddParameter(v, FE_PARAM_INT, ndim, rng, sz); }\nFEParam* FEParamContainer::AddParameter(double*        v, int ndim, RANGE rng, const char* sz) { return AddParameter(v, FE_PARAM_DOUBLE, ndim, rng, sz); }\nFEParam* FEParamContainer::AddParameter(FEParamDouble* v, int ndim, RANGE rng, const char* sz) { return AddParameter(v, FE_PARAM_DOUBLE_MAPPED, ndim, rng, sz); }\n\n//-----------------------------------------------------------------------------\nFEParam* FEParamContainer::AddParameter(int& v, const char* sz, unsigned int flags, const char* szenum)\n{\n\tFEParam* p = AddParameter(&v, FE_PARAM_INT, 1, sz);\n\tp->setParent(this);\n\tp->SetFlags(flags);\n\tp->setEnums(szenum);\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParamContainer::AddParameter(std::vector<int>& v, const char* sz, unsigned int flags, const char* szenum)\n{\n\tFEParam* p = AddParameter(&v, FE_PARAM_STD_VECTOR_INT, 1, sz);\n\tp->setParent(this);\n\tp->SetFlags(flags);\n\tp->setEnums(szenum);\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\nFEParam* FEParamContainer::AddParameter(std::string& s, const char* sz, unsigned int flags, const char* szenum)\n{\n\tFEParam* p = AddParameter(&s, FE_PARAM_STD_STRING, 1, sz);\n\tp->setParent(this);\n\tp->SetFlags(flags);\n\tp->setEnums(szenum);\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\n// Serialize parameters to archive\nvoid FEParamContainer::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tint NP = 0;\n\t\tlist<FEParam>::iterator it;\n\t\tif (m_pParam) // If the input file doesn't set any parameters, the parameter list won't be created.\n\t\t{\n\t\t\tNP = m_pParam->Parameters();\n\t\t\tit = m_pParam->first();\n\t\t}\n\t\tar << NP;\n\t\tfor (int i=0; i<NP; ++i)\n\t\t{\n\t\t\tFEParam& p = *it++;\n\t\t\tar << p;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint NP = 0;\n\t\tar >> NP;\n\t\tif (NP)\n\t\t{\n\t\t\tFEParameterList& pl = GetParameterList();\n\t\t\tif (NP != pl.Parameters()) throw DumpStream::ReadError();\n\t\t\tlist<FEParam>::iterator it = pl.first();\n\t\t\tfor (int i=0; i<NP; ++i)\n\t\t\t{\n\t\t\t\tFEParam& p = *it++;\n\t\t\t\tar >> p;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function validates all parameters. \n//! It fails on the first parameter that is outside its allowed range.\n//! Use fecore_get_error_string() to find out which parameter failed validation.\nbool FEParamContainer::Validate()\n{\n\tFEParameterList& pl = GetParameterList();\n\tint N = pl.Parameters();\n\tlist<FEParam>::iterator pi = pl.first();\n\tfor (int i=0; i<N; ++i, pi++)\n\t{\n\t\tFEParam& p = *pi;\n\t\tif (p.is_valid() == false)\n\t\t{\n            return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParamContainer::CopyParameterListState(const FEParameterList& pl)\n{\n\tFEParameterList& pl_this = GetParameterList();\n\tassert(pl_this.Parameters() == pl.Parameters());\n\tint NP = pl.Parameters();\n\tFEParamIteratorConst it_s = pl.first();\n\tFEParamIterator it_d = pl_this.first();\n\tfor (int i=0; i<NP; ++i, ++it_s, ++it_d)\n\t{\n\t\tconst FEParam& ps = *it_s;\n\t\tFEParam& pd = *it_d;\n\t\tif (pd.CopyState(ps) == false) { assert(false); }\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParamContainer::BeginParameterGroup(const char* szname)\n{\n\tFEParameterList& pl_this = GetParameterList();\n\tpl_this.SetActiveGroup(szname);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEParamContainer::EndParameterGroup()\n{\n\tFEParameterList& pl_this = GetParameterList();\n\tpl_this.SetActiveGroup(nullptr);\n}\n"
  },
  {
    "path": "FECore/FEParameterList.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec2d.h\"\n#include \"vec3d.h\"\n#include \"mat3d.h\"\n#include <assert.h>\n#include <list>\n#include <memory>\n#include \"FEParam.h\"\n#include \"FEParamValidator.h\"\n#include \"ParamString.h\"\n#include \"fecore_api.h\"\n#include <stdio.h>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nclass DumpStream;\nclass FEParamContainer;\nclass FEParamDouble;\nclass FEParamVec3;\nclass FEParamMat3d;\nclass FEParamMat3ds;\nclass FEDataArray;\nclass tens3drs;\nclass FEMaterialPointProperty;\n\n//-----------------------------------------------------------------------------\ntypedef std::list<FEParam>::iterator FEParamIterator;\ntypedef std::list<FEParam>::const_iterator FEParamIteratorConst;\n\n//-----------------------------------------------------------------------------\n//! A list of material parameters\nclass FECORE_API FEParameterList\n{\npublic:\n\tFEParameterList(FEParamContainer* pc);\n\tvirtual ~FEParameterList();\n\n\t//! assignment operator\n\tvoid operator = (FEParameterList& l);\n\n\t//! Add a parameter to the list\n\tFEParam* AddParameter(void* pv, FEParamType itype, int ndim, const char* sz, bool* watch = nullptr);\n\n\t//! Add a parameter to the list\n\tFEParam* AddParameter(void* pv, FEParamType type, int ndim, FEParamRange rng, double fmin, double fmax, const char* sz);\n\n\t//! find a parameter using the data pointer\n\tFEParam* FindFromData(void* pv);\n\n\t//! find a parameter using its name (the safe way)\n\tFEParam* FindFromName(const char* sz);\n\n\t//! get a parameter (the dangerous way)\n\tFEParam& operator [] (const char* sz) { return *FindFromName(sz); }\n\n\t//! returs the first parameter\n\tFEParamIterator first() { return m_pl.begin(); }\n\n\t//! returs the first parameter\n\tFEParamIteratorConst first() const { return m_pl.begin(); }\n\n\t//! number of parameters\n\tint Parameters() const { return (int) m_pl.size(); }\n\n\t//! return the parameter container\n\tFEParamContainer* GetContainer() { return m_pc; }\n\npublic:\n\tint SetActiveGroup(const char* szgroup);\n\tint GetActiveGroup();\n\tint ParameterGroups() const;\n\tconst char* GetParameterGroupName(int i);\n\nprotected:\n\tFEParamContainer*\tm_pc;\t//!< parent container\n\tstd::list<FEParam>\t\tm_pl;\t//!< the actual parameter list\n\tstd::vector<const char*>\tm_pg;\t//!< parameter groups\n\tint\tm_currentGroup;\t//!< active parameter group (new parameters are assigned to the current group; can be -1)\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for classes that wish to support parameter lists\nclass FECORE_API FEParamContainer\n{\npublic:\n\t//! constructor\n\tFEParamContainer();\n\n\t//! destructor\n\tvirtual ~FEParamContainer();\n\n\t//! return the material's parameter list\n\tFEParameterList& GetParameterList();\n\tconst FEParameterList& GetParameterList() const;\n\n\t//! find a parameter using it's name\n\tvirtual FEParam* GetParameter(const char* szname);\n\tvirtual FEParam* FindParameter(const ParamString& s);\n\n\t//! find a parameter using a pointer to the variable\n\tvirtual FEParam* FindParameterFromData(void* pv);\n\n\t//! serialize parameter data\n\tvirtual void Serialize(DumpStream& ar);\n\n\t//! validate material parameters.\n\t//! This function returns false on the first parameter encountered\n\t//! that is not valid (i.e. is outside its defined range). \n\t//! Overload this function to do additional validation. Make sure to always call the base class.\n\t//! Use fecore_get_error_string() to find out which parameter failed validation.\n\tvirtual bool Validate();\n\npublic:\n\t//! This copies the state of a parameter list (i.e. assigned load curve IDs)\n\t//! This function assumes that there is a one-to-one correspondence between\n\t//! source and target parameter lists.\n\tvoid CopyParameterListState(const FEParameterList& pl);\n\npublic:\n\tvoid BeginParameterGroup(const char* szname);\n\tvoid EndParameterGroup();\n\npublic:\n\t//! This function will be overridden by each class that defines a parameter list\n\tvirtual void BuildParamList();\n\n\t//! Add a parameter to the list\n\tFEParam* AddParameter(void* pv, FEParamType itype, int ndim, const char* sz, bool* watch = nullptr);\n\n\t//! Add a parameter to the list\n\tFEParam* AddParameter(void* pv, FEParamType type, int ndim, RANGE rng, const char* sz);\n\npublic:\n\tFEParam* AddParameter(int&                 v, const char* sz);\n\tFEParam* AddParameter(bool&                v, const char* sz);\n\tFEParam* AddParameter(double&              v, const char* sz);\n\tFEParam* AddParameter(vec2d&               v, const char* sz);\n\tFEParam* AddParameter(vec3d&               v, const char* sz);\n\tFEParam* AddParameter(mat3d&               v, const char* sz);\n\tFEParam* AddParameter(mat3ds&              v, const char* sz);\n\tFEParam* AddParameter(FEParamDouble&       v, const char* sz);\n\tFEParam* AddParameter(FEParamVec3&         v, const char* sz);\n\tFEParam* AddParameter(FEParamMat3d&        v, const char* sz);\n\tFEParam* AddParameter(FEParamMat3ds&       v, const char* sz);\n\tFEParam* AddParameter(FEDataArray&         v, const char* sz);\n\tFEParam* AddParameter(tens3drs& \t\t   v, const char* sz);\n\tFEParam* AddParameter(std::string&         v, const char* sz);\n\tFEParam* AddParameter(std::vector<int>& v   , const char* sz);\n\tFEParam* AddParameter(std::vector<double>& v, const char* sz);\n\tFEParam* AddParameter(std::vector<vec2d>&  v, const char* sz);\n\tFEParam* AddParameter(std::vector<std::string>& v, const char* sz);\n\tFEParam* AddParameter(FEMaterialPointProperty& v, const char* sz);\n\n\tFEParam* AddParameter(int&           v, RANGE rng, const char* sz);\n\tFEParam* AddParameter(double&        v, RANGE rng, const char* sz);\n\tFEParam* AddParameter(FEParamDouble& v, RANGE rng, const char* sz);\n\n\tFEParam* AddParameter(double& v, const char* sz, bool& watch);\n\n\tFEParam* AddParameter(int*           v, int ndim, const char* sz);\n\tFEParam* AddParameter(double*        v, int ndim, const char* sz);\n\tFEParam* AddParameter(FEParamDouble* v, int ndim, const char* sz);\n\n\tFEParam* AddParameter(int*           v, int ndim, RANGE rng, const char* sz);\n\tFEParam* AddParameter(double*        v, int ndim, RANGE rng, const char* sz);\n\tFEParam* AddParameter(FEParamDouble* v, int ndim, RANGE rng, const char* sz);\n\n\tFEParam* AddParameter(int& v, const char* sz, unsigned int flags, const char* szenum);\n\tFEParam* AddParameter(std::vector<int>& v, const char* sz, unsigned int flags, const char* szenum);\n\tFEParam* AddParameter(std::string& s, const char* sz, unsigned int flags, const char* szenum = nullptr);\n\n\ttemplate <typename T> bool SetParameter(const char* sz, T v);\n\nprivate:\n\tFEParameterList*\tm_pParam;\t//!< parameter list\n};\n\n//-----------------------------------------------------------------------------\ntemplate <typename T> bool FEParamContainer::SetParameter(const char* sz, T v)\n{\n\tFEParam* p = m_pParam->FindFromName(sz);\n\tif (p) p->value<T>() = v;\n\treturn (p != nullptr);\n}\n\n//-----------------------------------------------------------------------------\n// To add parameter list to a class, simply do the following two steps\n// 1) add the DECLARE_FECORE_CLASS macro in the material class declaration\n// 2) use the BEGIN_FECORE_CLASS, ADD_PARAM and END_FECORE_CLASS to\n//    define a parameter list\n\n// the following macro declares the parameter list for a material\n#define DECLARE_FECORE_CLASS() \\\n\tpublic: void BuildParamList() override;\n\n#define FECORE_BASE_CLASS(theClass) \\\n\tpublic: static const char* BaseClassName() { return #theClass; } \\\n\n// the BEGIN_FECORE_CLASS defines the beginning of a parameter list\n#define BEGIN_FECORE_CLASS(theClass, baseClass) \\\n\tvoid theClass::BuildParamList() { \\\n\t\t\tbaseClass::BuildParamList(); \\\n\n// the ADD_PARAMETER macro adds a parameter to the parameter list\n#define ADD_PARAMETER(...) \\\n\tAddParameter(__VA_ARGS__)\n\n// the END_FECORE_CLASS defines the end of a parameter list\n#define END_FECORE_CLASS() \\\n\t}\n\n// macro for starting a parameter group\n#define BEGIN_PARAM_GROUP(a) BeginParameterGroup(a)\n\n// macro for ending a parameter group\n#define END_PARAM_GROUP()    EndParameterGroup()\n"
  },
  {
    "path": "FECore/FEPeriodicLinearConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPeriodicLinearConstraint.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"FELinearConstraint.h\"\n#include \"FEModel.h\"\n#include \"FESurface.h\"\n\nFEPeriodicLinearConstraint::FEPeriodicLinearConstraint(FEModel* fem) : FEStepComponent(fem), m_exclude(fem)\n{\n\tm_refNode = -1;\n}\n\nFEPeriodicLinearConstraint::~FEPeriodicLinearConstraint()\n{\n}\n\nvoid FEPeriodicLinearConstraint::AddNodeSetPair(const FENodeList& ms, const FENodeList& ss, bool push_back)\n{\n\tNodeSetPair sp;\n\tsp.secondary = ms;\n\tsp.primary = ss;\n\tif (push_back) m_set.push_back(sp); else m_set.insert(m_set.begin(), sp);\n}\n\n// helper function for finding the closest node\nint closestNode(FEMesh& mesh, const FENodeList& set, const vec3d& r);\n\n// helper function for adding the linear constraints\nvoid addLinearConstraint(FEModel& fem, int parent, int child, int nodeA, int nodeB);\n\n// This function generates a linear constraint set based on the definition\n// of three surface pairs.\nbool FEPeriodicLinearConstraint::GenerateConstraints(FEModel* fem)\n{\n\t// get the model's mesh\n\tFEMesh& mesh = GetMesh();\n\n\t// make sure there is a list of sets\n\tif (m_set.empty()) return true;\n\n\t// make sure we have three sets\n\tif (m_set.size() != 3) return false;\n\n\t// tag all nodes to identify what they are (edge, face, corner)\n\tint N = mesh.Nodes();\n\tvector<int> tag(N, 0);\n\n\tfor (size_t i=0; i<m_set.size(); ++i)\n\t{\n\t\tFENodeList& ms = m_set[i].secondary;\n\t\tFENodeList& ss = m_set[i].primary;\n\n\t\tfor (int j=0; j<(int)ms.Size(); ++j) tag[ms[j]]++;\n\t\tfor (int j=0; j<(int)ss.Size(); ++j) tag[ss[j]]++;\n\t}\n\n\t// flip signs on primary\n\tfor (size_t i = 0; i<m_set.size(); ++i)\n\t{\n\t\tFENodeList& ss = m_set[i].primary;\n\t\tfor (int j = 0; j<(int)ss.Size(); ++j) \n\t\t{\n\t\t\tint ntag = tag[ss[j]];\n\t\t\tif (ntag > 0) tag[ss[j]] = -ntag;\n\t\t}\n\t}\n\n\t// At this point, the following should hold\n\t// primary nodes: tag < 0, secondary nodes: tag > 0, interior nodes: tag = 0\n\t// only one secondary node should have a value of 3. We make this the reference node\n\tint refNode = -1;\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tif (tag[i] == 3)\n\t\t{\n\t\t\tassert(refNode == -1);\n\t\t\trefNode = i;\n\t\t}\n\t}\n\tassert(refNode != -1);\n\tif (refNode == -1) return false;\n\n\t// get the position of the reference node\n\tvec3d rm = mesh.Node(refNode).m_r0;\n\n\t// create the linear constraints for the surface nodes that don't belong to an edge (i.e. tag = 1)\n\tfor (size_t n = 0; n<m_set.size(); ++n)\n\t{\n\t\tFENodeList& ms = m_set[n].secondary;\n\t\tFENodeList& ss = m_set[n].primary;\n\n\t\t// find the corresponding reference node on the primary surface\n\t\tint mref = closestNode(mesh, ss, rm);\n\n\t\t// make sure this is a corner node\n\t\tassert(tag[ss[mref]] == -3);\n\n\t\t// repeat for all primary nodes\n\t\tfor (int i=0; i<(int)ss.Size(); ++i)\n\t\t{\n\t\t\tassert(tag[ss[i]] < 0);\n\t\t\tif (tag[ss[i]] == -1)\n\t\t\t{\n\t\t\t\t// get the primary node position\n\t\t\t\tvec3d& rs = ss.Node(i)->m_r0;\n\n\t\t\t\t// find the closest secondary node\n\t\t\t\tint m = closestNode(mesh, ms, rs);\n\t\t\t\tassert(tag[ms[m]] == 1);\n\n\t\t\t\t// add the linear constraints\n\t\t\t\taddLinearConstraint(*fem, ss[i], ms[m], ss[mref], refNode);\n\t\t\t}\n\t\t}\n\t}\n\n\t// extract all 12 edges\n\tvector<FENodeList> surf;\n\tfor (int i=0; i<(int)m_set.size(); ++i)\n\t{\n\t\tsurf.push_back(m_set[i].secondary);\n\t\tsurf.push_back(m_set[i].primary);\n\t}\n\n\tvector<FENodeList> secondaryEdges;\n\tvector<FENodeList> primaryEdges;\n\tfor (int i=0; i<surf.size(); ++i)\n\t{\n\t\tFENodeList& s0 = surf[i];\n\t\tfor (int j=i+1; j<surf.size(); ++j)\n\t\t{\n\t\t\tFENodeList& s1 = surf[j];\n\t\t\tvector<int> tmp(N, 0);\n\t\t\tfor (int k=0; k<s0.Size(); ++k) tmp[s0[k]]++;\n\t\t\tfor (int k=0; k<s1.Size(); ++k) tmp[s1[k]]++;\n\n\t\t\tFENodeList edge(&mesh);\n\t\t\tfor (int k=0; k<N; ++k)\n\t\t\t{\n\t\t\t\tif (tmp[k] == 2) edge.Add(k);\n\t\t\t}\n\n\t\t\tif (edge.Size() != 0)\n\t\t\t{\n\t\t\t\t// see if this is a secondary edge or not\n\t\t\t\t// we assume it's a secondary edge if it connects to the refnode\n\t\t\t\tbool bsecondary = false;\n\t\t\t\tfor (int k=0; k<edge.Size(); ++k)\n\t\t\t\t{\n\t\t\t\t\tif (edge[k] == refNode)\n\t\t\t\t\t{\n\t\t\t\t\t\tbsecondary = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (bsecondary)\n\t\t\t\t\tsecondaryEdges.push_back(edge);\n\t\t\t\telse\n\t\t\t\t\tprimaryEdges.push_back(edge);\n\t\t\t}\n\t\t}\n\t}\n\n\t// since it is assumed the geometry is a cube, the following must hold\n\tassert(secondaryEdges.size() == 3);\n\tassert(primaryEdges.size() == 9);\n\n\t// find the secondary edge vectors\n\tvec3d Em[3];\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tFENodeList& edge = secondaryEdges[i];\n\n\t\t// get the edge vector\n\t\tEm[i] = edge.Node(0)->m_r0 - edge.Node(1)->m_r0; assert(edge[0] != edge[1]); \n\t\tEm[i].unit();\n\t}\n\n\t// setup linear constraints for edges\n\tfor (int n = 0; n< primaryEdges.size(); ++n)\n\t{\n\t\tFENodeList& edge = primaryEdges[n];\n\n\t\t// get the edge vector\n\t\tvec3d E = edge.Node(0)->m_r0 - edge.Node(1)->m_r0; assert(edge[0] != edge[1]); E.unit();\n\n\t\t// find the corresponding secondary edge\n\t\tbool bfound = true;\n\t\tfor (int m=0; m<3; ++m)\n\t\t{\n\t\t\tif (fabs(E*Em[m]) > 0.9999)\n\t\t\t{\n\t\t\t\tFENodeList& medge = secondaryEdges[m];\n\n\t\t\t\tint mref = closestNode(mesh, edge, rm);\n\n\t\t\t\tfor (int i=0; i<(int)edge.Size(); ++i)\n\t\t\t\t{\n\t\t\t\t\tassert(tag[edge[i]] < 0);\n\t\t\t\t\tif (tag[edge[i]] == -2)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3d ri = edge.Node(i)->m_r0;\n\t\t\t\t\t\tint k = closestNode(mesh, medge, ri);\n\n\t\t\t\t\t\taddLinearConstraint(*fem, edge[i], medge[k], edge[mref], refNode);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tassert(bfound);\n\t}\n\n\treturn true;\n}\n\nint closestNode(FEMesh& mesh, const FENodeList& set, const vec3d& r)\n{\n\tint nmin = -1;\n\tdouble Dmin = 0.0;\n\tfor (int i = 0; i<(int)set.Size(); ++i)\n\t{\n\t\tvec3d& ri = mesh.Node(set[i]).m_r0;\n\t\tdouble D = (r - ri)*(r - ri);\n\t\tif ((D < Dmin) || (nmin == -1))\n\t\t{\n\t\t\tDmin = D;\n\t\t\tnmin = i;\n\t\t}\n\t}\n\treturn nmin;\n}\n\n// helper function for adding the linear constraints\nvoid addLinearConstraint(FEModel& fem, int parent, int child, int nodeA, int nodeB)\n{\n\t// get the linear constraint manager\n\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\n\t// do one constraint for x, y, z\n\tfor (int j = 0; j<3; ++j)\n\t{\n\t\tFELinearConstraint* lc = fecore_alloc(FELinearConstraint, &fem);\n\t\tlc->SetParentDof(j, parent);\n\n\t\tlc->AddChildDof(j, child, 1.0);\n\t\tlc->AddChildDof(j, nodeA, 1.0);\n\t\tlc->AddChildDof(j, nodeB, -1.0);\n\n\t\tLCM.AddLinearConstraint(lc);\n\t}\n}\n"
  },
  {
    "path": "FECore/FEPeriodicLinearConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEStepComponent.h\"\n#include <FECore/FENodeList.h>\n#include <FECore/FENodeSet.h>\n#include \"fecore_api.h\"\n\nclass FECORE_API FEPeriodicLinearConstraint : public FEStepComponent\n{\n\tstruct NodeSetPair\n\t{\n\t\tFENodeList primary;\n\t\tFENodeList secondary;\n\t};\n\npublic:\n\tFEPeriodicLinearConstraint(FEModel* fem);\n\t~FEPeriodicLinearConstraint();\n\n\tvoid AddNodeSetPair(const FENodeList& ms, const FENodeList& ss, bool push_back = true);\n\n\tvoid SetReferenceNode(int node) { m_refNode = node; }\n\n\tvoid ExcludeNodes(const FENodeSet& ps) { m_exclude = ps; }\n\npublic:\n\t// generate the linear constraints\n\tbool GenerateConstraints(FEModel* fem);\n\nprivate:\n\tstd::vector<NodeSetPair>\tm_set;\t// list of node set pairs\n\tFENodeSet\tm_exclude;\t\t// nodes to exclude\n\tint\t\t\tm_refNode;\t\t// reference node\n};\n"
  },
  {
    "path": "FECore/FEPlotData.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPlotData.h\"\n\n//-----------------------------------------------------------------------------\nFEPlotData::FEPlotData(FEModel* fem) : FECoreBase(fem)\n{\n\tm_ntype = PLT_FLOAT;\n\tm_sfmt = FMT_NODE;\n\tm_nregion = FE_REGION_NODE;\n\n\tm_arraySize = 0;\n\tm_szdom[0] = 0;\n\tm_szunit = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nFEPlotData::FEPlotData(FEModel* fem, Region_Type R, Var_Type t, Storage_Fmt s) : FECoreBase(fem)\n{ \n\tm_ntype = t; \n\tm_sfmt = s; \n    m_nregion = R;\n\n\tm_arraySize = 0;\n\tm_szdom[0] = 0;\n\n\tm_szunit = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotData::SetArraySize(int n)\n{\n\tm_arraySize = n;\n}\n\n//-----------------------------------------------------------------------------\nint FEPlotData::GetArraysize() const\n{\n\treturn m_arraySize;\n}\n\n//-----------------------------------------------------------------------------\nint FEPlotData::VarSize(Var_Type t)\n{\n\tint ndata = 0;\n\tswitch (DataType())\n\t{\n\tcase PLT_FLOAT  : ndata =  1; break;\n\tcase PLT_VEC3F  : ndata =  3; break;\n\tcase PLT_MAT3FS : ndata =  6; break;\n\tcase PLT_MAT3FD : ndata =  3; break;\n    case PLT_TENS4FS: ndata = 21; break;\n\tcase PLT_MAT3F  : ndata =  9; break;\n\tcase PLT_ARRAY  : ndata = GetArraysize(); break;\n\tcase PLT_ARRAY_VEC3F: ndata = GetArraysize()*3; break;\n\t}\n\tassert(ndata);\n\treturn ndata;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotData::SetDomainName(const char* szdom)\n{\n\tstrcpy(m_szdom, szdom); \n}\n\nFEPlotFieldDescriptor::FEPlotFieldDescriptor(const std::string& typeString)\n{\n\tm_valid = false;\n\tm_filterType = NO_FILTER;\n\tnumFilter = -1;\n\tfieldName = typeString;\n\n\t// see if there is an alias defined\n\tsize_t equalSign = fieldName.find('=');\n\tif (equalSign != string::npos)\n\t{\n\t\talias = fieldName.substr(equalSign + 1, string::npos);\n\t\tfieldName.erase(equalSign, string::npos);\n\t}\n\telse alias = fieldName;\n\n\t// see if there is a filter\n\tsize_t leftBracket = fieldName.find('[');\n\tif (leftBracket != string::npos)\n\t{\n\t\t// find the right bracket\n\t\tsize_t rightBracket = fieldName.rfind(']');\n\t\tif (rightBracket == string::npos) return;\n\n\t\tstring filter = fieldName.substr(leftBracket + 1, rightBracket - leftBracket - 1);\n\t\tfieldName.erase(leftBracket, string::npos);\n\n\t\t// see if the filter is a number or a string\n\t\tsize_t leftQuote = filter.find('\\'');\n\t\tif (leftQuote != string::npos)\n\t\t{\n\t\t\tsize_t rightQuote = filter.rfind('\\'');\n\t\t\tif ((rightQuote == string::npos) || (rightQuote == leftQuote)) return;\n\n\t\t\tm_filterType = STRING_FILTER;\n\t\t\tstrFilter = filter.substr(leftQuote + 1, rightQuote - leftQuote - 1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm_filterType = NUMBER_FILTER;\n\t\t\tnumFilter = atoi(filter.c_str());\n\t\t}\n\t}\n\n\tm_valid = true;\n}\n"
  },
  {
    "path": "FECore/FEPlotData.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FECoreBase.h\"\n#include \"fecore_enum.h\"\n#include \"FEDataStream.h\"\n#include <functional>\n\n//-----------------------------------------------------------------------------\n// Region types\nenum Region_Type {\n\tFE_REGION_GLOBAL,\n\tFE_REGION_NODE,\n\tFE_REGION_DOMAIN,\n\tFE_REGION_SURFACE,\n\tFE_REGION_EDGE\n};\n\n//-----------------------------------------------------------------------------\n// forward declarations\nclass FEModel;\nclass FEMesh;\nclass FENode;\nclass FEEdge;\nclass FESurface;\nclass FEDomain;\nclass FESolidDomain;\nclass FEMaterialPoint;\nclass FENodeSet;\nclass FEElement;\n\n//-----------------------------------------------------------------------------\n//! This is the base class for all classes that wish to store data to the \n//! plot file. However, classes will not use this class directly as their\n//! base class. Instead they'll use one of the more specialized classes\n//! defined below.\n//!\nclass FECORE_API FEPlotData : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FEPLOTDATA_ID)\n\npublic:\n\tFEPlotData(FEModel* fem);\n\tFEPlotData(FEModel* fem, Region_Type R, Var_Type t, Storage_Fmt s);\n\n\t// The filter can be used to pass additional information to the plot field.\n\t// The interpretation of this filter is up to the derived class, but could\n\t// be used e.g. for disambiguation. There are currently two flavors of this\n\t// function: one that takes a const char* and one that takes an int. Derived\n\t// classes can overload both or just the one that makes sense for that plot field.\n\t// Note that these functions return false by default. This implies that trying\n\t// to specify a filter on a variable that doesn't support it will automatically cause \n\t// an error.\n\tvirtual bool SetFilter(const char* sz) { return false; }\n\tvirtual bool SetFilter(int n) { return false;}\n\n\tRegion_Type RegionType() { return m_nregion; }\n\tVar_Type DataType() { return m_ntype; }\n\tStorage_Fmt StorageFormat() { return m_sfmt; }\n\n\tint VarSize(Var_Type t);\n\n\tvoid SetItemList(vector<int>& item) { m_item = item; }\n\n\tvector<int> GetItemList() { return m_item; }\n\n\tvoid SetDomainName(const char* szdom);\n\tconst char* GetDomainName() { return m_szdom;  }\n\nprotected:\n\tvoid SetRegionType(Region_Type rt) { m_nregion = rt; }\n\tvoid SetVarType(Var_Type vt) { m_ntype = vt; }\n\tvoid SetStorageFormat(Storage_Fmt sf) { m_sfmt = sf; }\n\npublic: // override one of these functions depending on the Region_Type\n\tvirtual bool Save(FEDataStream& a) { return false; }\t\t\t\t\t// for FE_REGION_GLOBAL\n\tvirtual bool Save(FEMesh&    m, FEDataStream& a) { return false; }\t\t// for FE_REGION_NODE\n\tvirtual bool Save(FEDomain&  D, FEDataStream& a) { return false; }\t\t// for FE_REGION_DOMAIN\n\tvirtual bool Save(FESurface& S, FEDataStream& a) { return false; }\t\t// for FE_REGION_SURFACE\n\tvirtual bool Save(FEEdge&    E, FEDataStream& a) { return false; }\t\t// for FE_REGION_EDGE\n\npublic:\n\t// will be called before Save\n\tvirtual bool PreSave() { return true; }\n\npublic: // used by array variables\n\tvoid SetArraySize(int n);\n\tint GetArraysize() const;\n\n\tvoid SetArrayNames(vector<string>& s) { m_arrayNames = s; }\n\tvector<string>& GetArrayNames() { return m_arrayNames; }\n\npublic:\n\tvoid SetUnits(const char* sz) { m_szunit = sz; }\n\tconst char* GetUnits() const { return m_szunit; }\n\nprivate:\n\tRegion_Type\t\tm_nregion;\t\t//!< region type\n\tVar_Type\t\tm_ntype;\t\t//!< data type\n\tStorage_Fmt\t\tm_sfmt;\t\t\t//!< data storage format\n\tvector<int>\t\tm_item;\t\t\t//!< Data will only be stored for the item's in this list\n    char\t\t\tm_szdom[64];\t//!< Data will only be stored for the domain with this name\n\tconst char*\t\tm_szunit;\n\tint\t\t\t\tm_arraySize;\t//!< size of arrays (used by arrays)\n\tvector<string>\tm_arrayNames;\t//!< optional names of array components (used by arrays)\n};\n\n//-----------------------------------------------------------------------------\n//! Base class for global data. Data that wish to store data that is not directly\n//! evaluated on a part of the mesh should inherit from this class. \nclass FECORE_API FEPlotGlobalData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotGlobalData)\n\npublic:\n\tFEPlotGlobalData(FEModel* fem, Var_Type t) : FEPlotData(fem, FE_REGION_GLOBAL, t, FMT_ITEM) {}\n};\n\n//-----------------------------------------------------------------------------\n//! This is the base class for node data. Classes that wish to store data\n//! associated with each node of the mesh, will use this base class.\nclass FECORE_API FEPlotNodeData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotNodeData)\n\npublic:\n\tFEPlotNodeData(FEModel* fem, Var_Type t, Storage_Fmt s) : FEPlotData(fem, FE_REGION_NODE, t, s) {}\n};\n\n//-----------------------------------------------------------------------------\n//! This is the base class for domain data. Classes that wish to store data\n//! associated with each element or node of a domain, will use this base class.\nclass FECORE_API FEPlotDomainData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotDomainData)\n\npublic:\n\tFEPlotDomainData(FEModel* fem, Var_Type t, Storage_Fmt s) : FEPlotData(fem, FE_REGION_DOMAIN, t, s) {}\n};\n\n//-----------------------------------------------------------------------------\n//! This is the base class for surface data. Classes that wish to store data\n//! associated with each node or facet of a surface, will use this base class.\nclass FECORE_API FEPlotSurfaceData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotSurfaceData)\n\npublic:\n\tFEPlotSurfaceData(FEModel* fem, Var_Type t, Storage_Fmt s) : FEPlotData(fem, FE_REGION_SURFACE, t, s) {}\n};\n\n//! This is the base class for edge data. Classes that wish to store data\n//! associated with each node or facet of a FEEdge, will use this base class.\nclass FECORE_API FEPlotEdgeData : public FEPlotData\n{\n\tFECORE_BASE_CLASS(FEPlotEdgeData)\n\npublic:\n\tFEPlotEdgeData(FEModel* fem, Var_Type t, Storage_Fmt s) : FEPlotData(fem, FE_REGION_EDGE, t, s) {}\n};\n\n// helper class for parsing the type string of plot fields\nclass FECORE_API FEPlotFieldDescriptor\n{\nprivate:\n\tenum FilterType {\n\t\tNO_FILTER,\n\t\tNUMBER_FILTER,\n\t\tSTRING_FILTER\n\t};\n\npublic:\n\tFEPlotFieldDescriptor(const std::string& typeString);\n\n\tbool isValid() const { return m_valid; }\n\n\tbool HasFilter() const { return (m_filterType != NO_FILTER); }\n\tbool IsNumberFilter() const { return (m_filterType == NUMBER_FILTER); }\n\tbool IsStringFilter() const { return (m_filterType == STRING_FILTER); }\n\npublic:\n\tstring\tfieldName;\n\tstring\talias;\n\n\tint\t\tnumFilter = -1;\n\tstring\tstrFilter;\n\nprivate:\n\tFilterType m_filterType = NO_FILTER;\n\tbool m_valid = false;\n};\n"
  },
  {
    "path": "FECore/FEPlotDataStore.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPlotDataStore.h\"\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\nFEPlotVariable::FEPlotVariable() {}\n\n//-----------------------------------------------------------------------------\nFEPlotVariable::FEPlotVariable(const FEPlotVariable& pv)\n{\n    m_svar = pv.m_svar;\n    m_sdom = pv.m_sdom;\n    m_item = pv.m_item;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotVariable::operator = (const FEPlotVariable& pv)\n{\n    m_svar = pv.m_svar;\n    m_sdom = pv.m_sdom;\n    m_item = pv.m_item;\n}\n\nFEPlotVariable::FEPlotVariable(const std::string& var, std::vector<int>& item, const char* szdom)\n{\n    m_svar = var;\n    if (szdom) m_sdom = szdom;\n    m_item = item;\n}\n\nvoid FEPlotVariable::Serialize(DumpStream& ar)\n{\n    ar & m_svar;\n    ar & m_sdom;\n    ar & m_item;\n}\n\n//=======================================================================================\nFEPlotDataStore::FEPlotDataStore()\n{\n\tm_splot_type = \"febio\";\n    m_plot.clear();\n    m_nplot_compression = 0;\n}\n\n//-----------------------------------------------------------------------------\nFEPlotDataStore::FEPlotDataStore(const FEPlotDataStore& plt)\n{\n    m_splot_type = plt.m_splot_type;\n    m_nplot_compression = plt.m_nplot_compression;\n    m_plot = plt.m_plot;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotDataStore::operator = (const FEPlotDataStore& plt)\n{\n    m_splot_type = plt.m_splot_type;\n    m_nplot_compression = plt.m_nplot_compression;\n    m_plot = plt.m_plot;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotDataStore::AddPlotVariable(const char* szvar, std::vector<int>& item, const char* szdom)\n{\n    FEPlotVariable var(szvar, item, szdom);\n    m_plot.push_back(var);\n}\n\n//-----------------------------------------------------------------------------\nint FEPlotDataStore::GetPlotCompression() const\n{\n    return m_nplot_compression;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotDataStore::SetPlotCompression(int n)\n{\n    m_nplot_compression = n;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPlotDataStore::SetPlotFileType(const std::string& fileType)\n{\n    m_splot_type = fileType;\n}\n\nstd::string FEPlotDataStore::GetPlotFileType()\n{\n\treturn m_splot_type;\n}\n\nvoid FEPlotDataStore::Serialize(DumpStream& ar)\n{\n    ar & m_nplot_compression;\n    ar & m_splot_type;\n    ar & m_plot;\n}\n"
  },
  {
    "path": "FECore/FEPlotDataStore.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n#include <string>\n\nclass DumpStream;\n\nclass FECORE_API FEPlotVariable\n{\npublic:\n\tFEPlotVariable();\n\tFEPlotVariable(const FEPlotVariable& pv);\n\tvoid operator = (const FEPlotVariable& pv);\n\n\tFEPlotVariable(const std::string& var, std::vector<int>& item, const char* szdom = \"\");\n\n\tvoid Serialize(DumpStream& ar);\n\n\tconst std::string& Name() const { return m_svar; }\n\tconst std::string& DomainName() const { return m_sdom; }\n\npublic:\n\tstd::string\t\t\tm_svar;\t\t//!< name of output variable\n\tstd::string\t\t\tm_sdom;\t\t//!< (optional) name of domain\n\tstd::vector<int>\tm_item;\t\t//!< (optional) list of items\n};\n\nclass FECORE_API FEPlotDataStore\n{\npublic:\n\tFEPlotDataStore();\n\tFEPlotDataStore(const FEPlotDataStore&);\n\tvoid operator = (const FEPlotDataStore&);\n\n\tvoid AddPlotVariable(const char* szvar, std::vector<int>& item, const char* szdom = \"\");\n\n\tint GetPlotCompression() const;\n\tvoid SetPlotCompression(int n);\n\n\tvoid SetPlotFileType(const std::string& fileType);\n\tstd::string GetPlotFileType();\n\n\tvoid Serialize(DumpStream& ar);\n\n\tint PlotVariables() const { return (int)m_plot.size(); }\n\tFEPlotVariable& GetPlotVariable(int n) { return m_plot[n]; }\n\nprivate:\n\tstd::string\t\t\t\t\tm_splot_type;\n\tstd::vector<FEPlotVariable>\tm_plot;\n\tint\t\t\t\t\t\t\tm_nplot_compression;\n};\n"
  },
  {
    "path": "FECore/FEPointFunction.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEPointFunction.h\"\n#include \"DumpStream.h\"\n#include \"log.h\"\n#include \"BSpline.h\"\n\n#ifndef min\n#define min(a,b) ((a)<(b)?(a):(b))\n#endif\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPointFunction, FEFunction1D)\n\tADD_PARAMETER(m_int, \"interpolate\", 0, \"linear\\0step\\0smooth\\0cubic spline\\0control points\\0approximation\\0smooth-step\\0C2-smooth\\0\");\n\tADD_PARAMETER(m_ext, \"extend\"     , 0, \"constant\\0extrapolate\\0repeat\\0repeat offset\\0\");\n    ADD_PARAMETER(m_bln, \"log\")->SetFlags(FE_PARAM_HIDDEN);\n\tADD_PARAMETER(m_points, \"points\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! default constructor\nFEPointFunction::FEPointFunction(FEModel* fem) : FEFunction1D(fem)\n{\n\tm_int = PointCurve::LINEAR;\n\tm_ext = PointCurve::CONSTANT;\n    m_bln = false;\n}\n\n//-----------------------------------------------------------------------------\nFEPointFunction::~FEPointFunction()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! Clears the loadcurve data\nbool FEPointFunction::Init()\n{\n\tm_fnc.SetInterpolator(m_int);\n\tm_fnc.SetExtendMode(m_ext);\n\tm_fnc.SetPoints(m_points);\n\tif (m_fnc.Update() == false) return false;\n\n    return FEFunction1D::Init();\n}\n\n//-----------------------------------------------------------------------------\n//! Clears the loadcurve data\nvoid FEPointFunction::Clear()\n{ \n\tm_fnc.Clear();\n}\n\n//-----------------------------------------------------------------------------\n//! return nr of points\nint FEPointFunction::Points() const\n{ \n\treturn (int) m_points.size(); \n}\n\n//-----------------------------------------------------------------------------\n//! set the points\nvoid FEPointFunction::SetPoints(const std::vector<vec2d>& pts)\n{\n\tm_points = pts;\n}\n\n//-----------------------------------------------------------------------------\n// Sets the time and data value of point i\n// This function assumes that the load curve data has already been created\n//\nvoid FEPointFunction::SetPoint(int i, double x, double y)\n{\n\tm_fnc.SetPoint(i, x, y);\n}\n\n//-----------------------------------------------------------------------------\n//! Set the type of interpolation\nvoid FEPointFunction::SetInterpolation(int fnc) { m_int = fnc; }\n\n//-----------------------------------------------------------------------------\n//! Set the extend mode\nvoid FEPointFunction::SetExtendMode(int mode) { m_ext = mode; }\n\n//-----------------------------------------------------------------------------\n//! returns point i\nLOADPOINT FEPointFunction::LoadPoint(int i) const\n{ \n\tconst vec2d& p = m_points[i];\n\tLOADPOINT lp;\n\tlp.time  = p.x();\n\tlp.value = p.y();\n\treturn lp; \n}\n\n//-----------------------------------------------------------------------------\n//! This function adds a datapoint to the loadcurve. The datapoint is inserted\n//! at the appropriate place by examining the time parameter.\n\nvoid FEPointFunction::Add(double x, double y)\n{\n\t// find the place to insert the data point\n\tint n = 0;\n\tint nsize = m_points.size();\n\twhile ((n<nsize) && (m_points[n].x() < x)) ++n;\n\n\t// insert loadpoint\n\tm_points.insert(m_points.begin() + n, vec2d(x, y));\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointFunction::Scale(double s)\n{\n\tfor (int i = 0; i < Points(); ++i)\n\t{\n\t\tm_points[i].y() *= s;\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPointFunction::value(double time) const\n{\n    if (m_bln) time = (time > 0) ? log(time) : m_points[0].x();\n\treturn m_fnc.value(time);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointFunction::Serialize(DumpStream& ar)\n{\n\tFEFunction1D::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_int << m_ext;\n\t\tar << m_points;\n\t}\n\telse\n\t{\n\t\tar >> m_int >> m_ext;\n\t\tar >> m_points;\n\n\t\tm_fnc.Clear();\n\t\tm_fnc.SetInterpolator(m_int);\n\t\tm_fnc.SetExtendMode(m_ext);\n\t\tm_fnc.SetPoints(m_points);\n\t\tm_fnc.Update();\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPointFunction::derive(double time) const\n{\n    if (m_bln) time = (time > 0) ? log(time) : m_points[0].x();\n\treturn m_fnc.derive(time);\n}\n\n//-----------------------------------------------------------------------------\ndouble FEPointFunction::deriv2(double time) const\n{\n    if (m_bln) time = (time > 0) ? log(time) : m_points[0].x();\n\treturn m_fnc.deriv2(time);\n}\n\ndouble FEPointFunction::integrate(double a, double b) const\n{\n\treturn m_fnc.integrate(a, b);\n}\n\n//-----------------------------------------------------------------------------\nFEFunction1D* FEPointFunction::copy()\n{\n\tFEPointFunction* f = new FEPointFunction(GetFEModel());\n\n\tf->m_int = m_int;\n\tf->m_ext = m_ext;\n\tf->m_points = m_points;\n\tf->m_fnc = m_fnc;\n\treturn f;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointFunction::CopyFrom(const FEPointFunction& f)\n{\n\tm_int = f.m_int;\n\tm_ext = f.m_ext;\n\tm_points = f.m_points;\n\tm_fnc = f.m_fnc;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPointFunction::CopyFrom(const PointCurve& f)\n{\n\tm_int = f.GetInterpolator();\n\tm_ext = f.GetExtendMode();\n\tm_points = f.GetPoints();\n\tm_fnc = f;\n}\n"
  },
  {
    "path": "FECore/FEPointFunction.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEFunction1D.h\"\n#include \"PointCurve.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! This class implements a function defined by a set of ordered (point,value) pairs.\n//! It uses an interpolation scheme to interpolate between data points and also \n//! has a way of specifying the function value outside of the domain defined by the first and \n//! last data point. \n\nclass FECORE_API FEPointFunction : public FEFunction1D\n{\npublic:\n\t//! Load point structure\n\tstruct LOADPOINT\n\t{\n\t\tdouble time;\n\t\tdouble value;\n\t};\n\npublic:\n\t//! default constructor\n\tFEPointFunction(FEModel* fem);\n\n\t//! destructor\n\t~FEPointFunction();\n    \n    //! initialize\n    bool Init() override;\n\n\t//! adds a point to the point curve\n\tvoid Add(double x, double y);\n\n\t//! Clears the loadcurve data\n\tvoid Clear() override;\n\n\t//! set the x and y value of point i\n\tvoid SetPoint(int i, double x, double y);\n\n\t//! Set the type of interpolation\n\tvoid SetInterpolation(int n);\n\n\t//! Set the extend mode\n\tvoid SetExtendMode(int n);\n\n\t//! returns point i\n\tLOADPOINT LoadPoint(int i) const;\n\n\t//! return nr of points\n\tint Points() const;\n\n\t//! set the points\n\tvoid SetPoints(const std::vector<vec2d>& pts);\n\n\t//! Serialize data to archive\n\tvoid Serialize(DumpStream& ar) override;\n\n\t// copy data from other curve\n\tFEFunction1D* copy() override;\n\n\t// copy from another function\n\tvoid CopyFrom(const FEPointFunction& f);\n\tvoid CopyFrom(const PointCurve& f);\n\npublic: // operations\n\n\t// scale all y points by s\n\tvoid Scale(double s);\n\npublic: // implement from base class\n\n\t//! returns the value of the load curve at time\n\tdouble value(double x) const override;\n\n\t//! returns the derivative value at time\n\tdouble derive(double x) const override;\n\n    //! returns the second derivative value at time\n    double deriv2(double x) const override;\n\n\t//! returns the definite integral value between a and b\n\tdouble integrate(double a, double b) const override;\n\n\t// TODO: I need to make this public so the parameters can be mapped to the FELoadCurve\nprivate:\n\tint\t\tm_int;\t//!< interpolation function\n\tint\t\tm_ext;\t//!< extend mode\n    bool    m_bln;  //!< points represent (ln(x),y) instead of (x,y)\n\tstd::vector<vec2d>\tm_points;\n\nprivate:\n\tPointCurve\tm_fnc;\n    \n\tDECLARE_FECORE_CLASS();\n};\n\ntypedef FEPointFunction::LOADPOINT LOADPOINT;\n"
  },
  {
    "path": "FECore/FEPrescribedBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedBC.h\"\n#include \"FESurface.h\"\n#include \"FENode.h\"\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEPrescribedNodeSet, FENodalBC)\n\tADD_PARAMETER(m_brelative, \"relative\");\nEND_FECORE_CLASS();\n\nFEPrescribedNodeSet::FEPrescribedNodeSet(FEModel* fem) : FENodalBC(fem)\n{\n\tm_brelative = false;\n}\n\n//-----------------------------------------------------------------------------\n// set the relative flag\nvoid FEPrescribedNodeSet::SetRelativeFlag(bool br)\n{\n\tm_brelative = br;\n}\n\nvoid FEPrescribedNodeSet::Activate()\n{\n\tFENodalBC::Activate();\n\n\tFENodeSet& nodeSet = *GetNodeSet();\n\tint N = nodeSet.Size();\n\tint dofs = m_dof.Size();\n\tif (m_brelative) m_rval.assign(N * dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *nodeSet.Node(i);\n\n\t\t// set the dofs to prescribed\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tnode.set_bc(m_dof[j], DOF_PRESCRIBED);\n\t\t\tif (m_brelative)\n\t\t\t{\n\t\t\t\tm_rval[i * dofs + j] = node.get(m_dof[j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedNodeSet::Deactivate()\n{\n\tFEBoundaryCondition::Deactivate();\n\tFENodeSet& nodeSet = *GetNodeSet();\n\tint N = nodeSet.Size();\n\tint dofs = m_dof.Size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *nodeSet.Node(i);\n\n\t\t// set the dof to open\n\t\tfor (int j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tnode.set_bc(m_dof[j], DOF_OPEN);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function is called when the solver needs to know the \n// prescribed dof values. The brel flag indicates wheter the total \n// value is needed or the value with respect to the current nodal dof value\nvoid FEPrescribedNodeSet::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tFENodeSet& nodeSet = *GetNodeSet();\n\tint N = nodeSet.Size();\n\tint dofs = m_dof.Size();\n\tvector<double> val(dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *nodeSet.Node(i);\n\n\t\t// get the values\n\t\tGetNodalValues(i, val);\n\t\tassert(val.size() == dofs);\n\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tdouble uj = val[j];\n\t\t\tif (m_brelative)\n\t\t\t{\n\t\t\t\tuj += m_rval[i * dofs + j];\n\t\t\t}\n\n\t\t\tint I = -node.m_ID[m_dof[j]] - 2;\n\t\t\tif (I >= 0) ui[I] = (brel ? uj - node.get(m_dof[j]) : uj);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FEPrescribedNodeSet::Serialize(DumpStream& ar)\n{\n\tFENodalBC::Serialize(ar);\n\tar & m_rval;\n}\n\n//-----------------------------------------------------------------------------\n// This is called during nodal update and should be used to enforce the \n// nodal degrees of freedoms\nvoid FEPrescribedNodeSet::Update()\n{\n\tFENodeSet& nodeSet = *GetNodeSet();\n\tint N = nodeSet.Size();\n\tint dofs = m_dof.Size();\n\tstd::vector<double> val(dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *nodeSet.Node(i);\n\n\t\t// get the values\n\t\tGetNodalValues(i, val);\n\t\tassert(val.size() == dofs);\n\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tdouble uj = val[j];\n\t\t\tif (m_brelative)\n\t\t\t{\n\t\t\t\tuj += m_rval[i * dofs + j];\n\t\t\t}\n\n\t\t\tnode.set(m_dof[j], uj);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This is called during contact update and should be used to enforce the\n// nodal degrees of freedoms\nvoid FEPrescribedNodeSet::Repair()\n{\n\tFENodeSet& nodeSet = *GetNodeSet();\n\tint N = nodeSet.Size();\n\tint dofs = m_dof.Size();\n\tstd::vector<double> val(dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *nodeSet.Node(i);\n\n\t\t// get the values\n\t\tGetNodalValues(i, val);\n\t\tassert(val.size() == dofs);\n\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tif (node.m_ID[m_dof[j]] >= 0) {\n\t\t\t\tnode.m_ID[m_dof[j]] = -node.m_ID[m_dof[j]] - 2;\n\t\t\t\tdouble uj = val[j];\n\t\t\t\tif (m_brelative)\n\t\t\t\t{\n\t\t\t\t\tuj += m_rval[i * dofs + j];\n\t\t\t\t}\n\n\t\t\t\tnode.set(m_dof[j], uj);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FEPrescribedSurface, FESurfaceBC)\nEND_FECORE_CLASS();\n\nFEPrescribedSurface::FEPrescribedSurface(FEModel* fem) : FESurfaceBC(fem)\n{\n\tm_brelative = false;\n}\n\nvoid FEPrescribedSurface::Activate()\n{\n\tFESurfaceBC::Activate();\n\n\tFESurface* surface = GetSurface();\n\tif (surface == nullptr) return;\n\n\tm_nodeList = surface->GetNodeList();\n\t\n\tint N = m_nodeList.Size();\n\tint dofs = m_dof.Size();\n\tif (m_brelative) m_rval.assign(N * dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *m_nodeList.Node(i);\n\n\t\t// set the dofs to prescribed\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tnode.set_bc(m_dof[j], DOF_PRESCRIBED);\n\n\t\t\tif (m_brelative)\n\t\t\t{\n\t\t\t\tm_rval[i * dofs + j] = node.get(m_dof[j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedSurface::Deactivate()\n{\n\tFEBoundaryCondition::Deactivate();\n\tint N = m_nodeList.Size();\n\tint dofs = m_dof.Size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *m_nodeList.Node(i);\n\n\t\t// set the dof to open\n\t\tfor (int j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tnode.set_bc(m_dof[j], DOF_OPEN);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function is called when the solver needs to know the \n// prescribed dof values. The brel flag indicates wheter the total \n// value is needed or the value with respect to the current nodal dof value\nvoid FEPrescribedSurface::PrepStep(std::vector<double>& ui, bool brel)\n{\n\tint N = m_nodeList.Size();\n\tint dofs = m_dof.Size();\n\tvector<double> val(dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *m_nodeList.Node(i);\n\n\t\t// get the values\n\t\tGetNodalValues(i, val);\n\t\tassert(val.size() == dofs);\n\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tdouble uj = val[j];\n\t\t\tif (m_brelative)\n\t\t\t{\n\t\t\t\tuj += m_rval[i * dofs + j];\n\t\t\t}\n\n\t\t\tint I = -node.m_ID[m_dof[j]] - 2;\n\t\t\tif (I >= 0) ui[I] = (brel ? uj - node.get(m_dof[j]) : uj);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// set the relative flag\nvoid FEPrescribedSurface::SetRelativeFlag(bool br)\n{\n\tm_brelative = br;\n}\n\n//-----------------------------------------------------------------------------\n// serialization\nvoid FEPrescribedSurface::Serialize(DumpStream& ar)\n{\n\tFEBoundaryCondition::Serialize(ar);\n\tar & m_rval;\n\tif (ar.IsShallow() == false) ar & m_nodeList;\n}\n\n//-----------------------------------------------------------------------------\n// This is called during nodal update and should be used to enforce the \n// nodal degrees of freedoms\nvoid FEPrescribedSurface::Update()\n{\n\tint N = m_nodeList.Size();\n\tint dofs = m_dof.Size();\n\tstd::vector<double> val(dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *m_nodeList.Node(i);\n\n\t\t// get the values\n\t\tGetNodalValues(i, val);\n\t\tassert(val.size() == dofs);\n\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tdouble uj = val[j];\n\t\t\tif (m_brelative)\n\t\t\t{\n\t\t\t\tuj += m_rval[i * dofs + j];\n\t\t\t}\n\n\t\t\tnode.set(m_dof[j], uj);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This is called during contact update and should be used to enforce the\n// nodal degrees of freedoms\nvoid FEPrescribedSurface::Repair()\n{\n\tint N = m_nodeList.Size();\n\tint dofs = m_dof.Size();\n\tstd::vector<double> val(dofs, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = *m_nodeList.Node(i);\n\n\t\t// get the values\n\t\tGetNodalValues(i, val);\n\t\tassert(val.size() == dofs);\n\n\t\tfor (size_t j = 0; j < dofs; ++j)\n\t\t{\n\t\t\tif (node.m_ID[m_dof[j]] >= 0) {\n\t\t\t\tnode.m_ID[m_dof[j]] = -node.m_ID[m_dof[j]] - 2;\n\t\t\t\tdouble uj = val[j];\n\t\t\t\tif (m_brelative)\n\t\t\t\t{\n\t\t\t\t\tuj += m_rval[i * dofs + j];\n\t\t\t\t}\n\n\t\t\t\tnode.set(m_dof[j], uj);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/FEPrescribedBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENodalBC.h\"\n#include \"FESurfaceBC.h\"\n#include \"FEDofList.h\"\n\n//-----------------------------------------------------------------------------\n// Base class for prescribed BCs on a nodeset\nclass FECORE_API FEPrescribedNodeSet : public FENodalBC\n{\npublic:\n\tFEPrescribedNodeSet(FEModel* fem);\n\n\tvoid Activate() override;\n\n\t// deactivation\n\tvoid Deactivate() override;\n\n\tvoid Update() override;\n\n\tvoid Repair() override;\n\n\t// set the relative flag\n\tvoid SetRelativeFlag(bool br);\n\n\t// This function is called when the solver needs to know the \n\t// prescribed dof values. The brel flag indicates wheter the total \n\t// value is needed or the value with respect to the current nodal dof value\n\tvoid PrepStep(std::vector<double>& ui, bool brel = true) override;\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Derived classes need to override this function.\n\t//! return the value for node i, dof j (i is index into nodeset, j is index into doflist)\n\tvirtual void GetNodalValues(int nodelid, std::vector<double>& val) = 0;\n\nprotected:\n\tbool\t\t\t\tm_brelative;\t\t//!< relative flag\n\nprotected:\n\tstd::vector<double>\tm_rval;\t\t\t\t//!< values used for relative BC\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Base class for prescribed BCs on a surface\nclass FECORE_API FEPrescribedSurface : public FESurfaceBC\n{\npublic:\n\tFEPrescribedSurface(FEModel* fem);\n\n\tvoid Activate() override;\n\n\t// deactivation\n\tvoid Deactivate() override;\n\n\tvoid Update() override;\n\n\tvoid Repair() override;\n\n\t// This function is called when the solver needs to know the \n\t// prescribed dof values. The brel flag indicates wheter the total \n\t// value is needed or the value with respect to the current nodal dof value\n\tvoid PrepStep(std::vector<double>& ui, bool brel = true) override;\n\n\t// set the relative flag\n\tvoid SetRelativeFlag(bool br);\n\n\t// serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! Derived classes need to override this function.\n\t//! return the value for node i, dof j (i is index into nodeset, j is index into doflist)\n\tvirtual void GetNodalValues(int nodelid, std::vector<double>& val) = 0;\n\nprotected:\n\tbool\t\t\t\tm_brelative;\t\t//!< relative flag\n\nprotected:\n\tFENodeList\t\t\tm_nodeList;\t\t\t//!< list of nodes to apply bc too\n\tstd::vector<double>\tm_rval;\t\t\t\t//!< values used for relative BC\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEPrescribedDOF.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEPrescribedDOF.h\"\n#include \"FENodeSet.h\"\n#include \"DumpStream.h\"\n#include \"FEMesh.h\"\n#include \"log.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FEPrescribedDOF, FEPrescribedNodeSet)\n\tADD_PARAMETER(m_scale, \"scale\");\n\tADD_PARAMETER(m_dof  , \"dof\", 0, \"$(dof_list)\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFEPrescribedDOF::FEPrescribedDOF(FEModel* pfem) : FEPrescribedNodeSet(pfem)\n{\n\tm_scale = 0.0;\n\tm_dof = -1;\n}\n\n//-----------------------------------------------------------------------------\nFEPrescribedDOF::FEPrescribedDOF(FEModel* pfem, int dof, FENodeSet* nset) : FEPrescribedNodeSet(pfem)\n{\n\tm_scale = 0.0;\n\tSetNodeSet(nset);\n\tSetDOF(dof);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedDOF::SetDOF(int ndof)\n{\n\tm_dof = ndof;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrescribedDOF::SetDOF(const char* szdof)\n{\n\tint ndof = GetDOFIndex(szdof);\n\tassert(ndof >= 0);\n\tif (ndof < 0) return false;\n\tSetDOF(ndof);\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Sets the displacement scale factor. An optional load curve index can be given\n// of the load curve that will control the scale factor.\nFEPrescribedDOF& FEPrescribedDOF::SetScale(double s, int lc)\n{\n\tm_scale = s;\n\tif (lc >= 0)\n\t{\n\t\tAttachLoadController(&m_scale, lc);\n\t}\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nbool FEPrescribedDOF::Init()\n{\n\t// set the dof first before calling base class\n\tif (m_dof < 0) return false;\n\tSetDOFList(m_dof);\n\n\t// don't forget to call the base class\n\tif (FEPrescribedNodeSet::Init() == false) return false;\n\n\t// make sure this is not a rigid node\n\tFEMesh& mesh = GetMesh();\n\tint NN = mesh.Nodes();\n\tconst FENodeSet& nset = *GetNodeSet();\n\tfor (size_t i = 0; i<nset.Size(); ++i)\n\t{\n\t\tint nid = nset[i];\n\t\tif ((nid < 0) || (nid >= NN)) return false;\n\t\tif (mesh.Node(nid).m_rid != -1)\n\t\t{\n\t\t\tfeLogError(\"Rigid nodes cannot be prescribed.\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedDOF::GetNodalValues(int n, std::vector<double>& val)\n{\n\tassert(val.size() == 1);\n\tconst FENodeSet& nset = *GetNodeSet();\n\tint nid = nset[n];\n\tconst FENode& node = *nset.Node(n);\n\n\tFEMaterialPoint mp;\n\tmp.m_r0 = node.m_r0;\n\tmp.m_index = n;\n\n\tval[0] = m_scale(mp);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEPrescribedDOF::CopyFrom(FEBoundaryCondition* pbc)\n{\n\tFEPrescribedDOF* ps = dynamic_cast<FEPrescribedDOF*>(pbc); assert(ps);\n\tm_scale = ps->m_scale;\n\tCopyParameterListState(ps->GetParameterList());\n}\n"
  },
  {
    "path": "FECore/FEPrescribedDOF.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEPrescribedBC.h\"\n#include \"FEModelParam.h\"\n\n//-----------------------------------------------------------------------------\n//! Boundary condition for prescribing a degree of freedom\nclass FECORE_API FEPrescribedDOF : public FEPrescribedNodeSet\n{\npublic:\n\tFEPrescribedDOF(FEModel* pfem);\n\tFEPrescribedDOF(FEModel* pfem, int dof, FENodeSet* nset);\n\n\tvoid SetDOF(int ndof);\n\tbool SetDOF(const char* szdof);\n\n\tFEPrescribedDOF& SetScale(double s, int lc = -1);\n\n\tbool Init() override;\n\n\tvoid CopyFrom(FEBoundaryCondition* pbc) override;\n\npublic:\n\tvoid GetNodalValues(int n, std::vector<double>& val) override;\n\nprotected:\n\tint\t\t\t\tm_dof;\t\t//!< degree of freedom to prescribe\n\tFEParamDouble\tm_scale;\t//!< overall scale factor\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEProperty.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEProperty.h\"\n#include \"FECoreBase.h\"\n#include \"DumpStream.h\"\n#include \"FECoreKernel.h\"\n\n//-----------------------------------------------------------------------------\nFEProperty::FEProperty(SUPER_CLASS_ID superClassID) : m_szname(nullptr), m_className(nullptr), m_szlongname(nullptr), m_szdefaultType(nullptr), m_flags(0), m_superClassID(superClassID) {}\n\n//-----------------------------------------------------------------------------\nFEProperty::~FEProperty(){}\n\n//-----------------------------------------------------------------------------\n//! Set the name of the property.\n//! Note that the name is not copied so it must point to a static string.\nFEProperty& FEProperty::SetName(const char* sz)\n{\n\tm_szname = sz;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\n//! Return the name of this property\nconst char* FEProperty::GetName() const { return m_szname; }\n\n//-----------------------------------------------------------------------------\nFEProperty& FEProperty::SetLongName(const char* sz)\n{\n\tm_szlongname = sz;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nconst char* FEProperty::GetLongName() const { return m_szlongname; }\n\n//-----------------------------------------------------------------------------\nconst char* FEProperty::GetDefaultType() const\n{\n\treturn m_szdefaultType;\n}\n\n//-----------------------------------------------------------------------------\nFEProperty& FEProperty::SetDefaultType(const char* szdefType)\n{\n\tm_szdefaultType = szdefType;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEProperty::Write(DumpStream& ar, FECoreBase* pc)\n{\n\tint nflag = (pc == 0 ? 0 : 1);\n\tar << nflag;\n\tif (nflag)\n\t{\n\t\tint ntype = (int) pc->GetSuperClassID();\n\t\tar << pc->GetTypeStr();\n\t\tar << ntype;\n\t\tpc->Serialize(ar);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFECoreBase* FEProperty::Read(DumpStream& ar)\n{\n\tint nflag = 0;\n\tFECoreBase* pm = 0;\n\tar >> nflag;\n\tif (nflag)\n\t{\n\t\tchar sz[256];\n\t\tint ntype = FEINVALID_ID;\n\t\tar >> sz;\n\t\tar >> ntype;\n\t\tpm = fecore_new<FECoreBase>(ntype, sz, &ar.GetFEModel());\n\t\tpm->SetParent(GetParent());\n\t\tpm->Serialize(ar);\n\n\t\t// TODO: Do I really need to do this here?\n\t\t//pm->Init();\n\t}\n\treturn pm;\n}\n"
  },
  {
    "path": "FECore/FEProperty.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n#include \"fecore_enum.h\"\n\n//-----------------------------------------------------------------------------\nclass FECoreBase;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\n//! A property of a class reflects a member variable of the class that is a \n//! pointer to a FECoreBase derived class. \nclass FECORE_API FEProperty\n{\npublic:\n\tenum Flags\n\t{\n\t\tOptional\t\t= 0x00,\n\t\tRequired\t\t= 0x01,\t\t// the property is required (default)\n\t\tPreferred\t\t= 0x02,\t\t// the property is not required, but a default should be allocated when possible.\n\t\tReference       = 0x04,\t\t// references another class in the model\n\t\tFixed\t\t\t= 0x08,\t\t// fixed properties are fixed type class members\n\t\tTopLevel\t\t= 0x10,\t\t// This is a \"top-level\" property. \n\t};\n\nprivate:\n\t//! Name of the property.\n\t//! Note that the name is not copied so it must point to a static string.\n\tconst char*\t\tm_szname;\n\tconst char*\t\tm_szlongname;\t// long name (optional; used in FEBio Studio)\n\tunsigned int\tm_flags;\t\t// bitwise or of flags defined above \n\n\tconst char* m_szdefaultType;\t// default type string (used by FEBio Studio to initialize required properties).\n\nprotected:\n\tconst char* m_className;\t// name of class that can be assigned to this\n\npublic:\n\t// Set\\Get the name of the property\n\tFEProperty& SetName(const char* sz);\n\tconst char* GetName() const;\n\n\t// get\\set the long name\n\tFEProperty& SetLongName(const char* sz);\n\tconst char* GetLongName() const;\n\n\t// get the class name\n\tconst char* GetClassName() const { return m_className; }\n\n\t// is the property required\n\tbool IsRequired() const { return (m_flags & Required) != 0; }\n\n\t// is the property preferred\n\tbool IsPreferred() const { return (m_flags & Preferred) != 0; }\n\n\t// is this a reference property\n\tbool IsReference() const { return (m_flags & Reference) != 0; }\n\n\t// is this a top-level property\n\tbool IsTopLevel() const { return (m_flags & TopLevel) != 0; }\n\n\t// is this a fixed property\n\tbool IsFixed() const { return (m_flags & Fixed) != 0; }\n\n\t// set the flags\n\tvoid SetFlags(unsigned int flags) { m_flags = flags; }\n\n\t// add a flag\n\tvoid AddFlag(unsigned int flag) { m_flags |= flag; }\n\n\t// get the flags\n\tunsigned int Flags() const { return m_flags; }\n\n\t// get default type (can be null)\n\tconst char* GetDefaultType() const;\n\n\t// set the default type\n\tFEProperty& SetDefaultType(const char* szdefType);\n\npublic: // these functions have to be implemented by derived classes\n\n\t//! helper function for identifying if this is an array property or not\n\tvirtual bool IsArray() const = 0;\n\n\t//! see if the pc parameter is of the correct type for this property\n\tvirtual bool IsType(FECoreBase* pc) const = 0;\n\n\t//! set the property\n\tvirtual void SetProperty(FECoreBase* pc) = 0;\n\n\t//! return the size of the property\n\tvirtual int size() const = 0;\n\n\t//! return a specific property by index\n\tvirtual FECoreBase* get(int i) = 0;\n\n\t//! return a specific property by name\n\tvirtual FECoreBase* get(const char* szname) = 0;\n\n\t//! return a specific property by ID\n\tvirtual FECoreBase* getFromID(int nid) = 0;\n\n\t//! serialize property data\n\tvirtual void Serialize(DumpStream& ar) = 0;\n\n\t//! initializatoin\n\tvirtual bool Init() = 0;\n\n\t//! validation\n\tvirtual bool Validate() = 0;\n\n\t//! Get the parent of this property\n\tFECoreBase* GetParent() { return m_pParent; }\n\n\t//! Set the parent of this property\n\tvirtual void SetParent(FECoreBase* parent) { m_pParent = parent; }\n\n\t//! Get the class ID\n\tSUPER_CLASS_ID GetSuperClassID() const { return m_superClassID; }\n\npublic:\n\tvirtual ~FEProperty();\n\nprotected:\n\t//! some helper functions for reading, writing properties\n\tvoid Write(DumpStream& ar, FECoreBase* pc);\n\tFECoreBase* Read(DumpStream& ar);\n\nprotected:\n\t// This class should not be created directly\n\tFEProperty(SUPER_CLASS_ID classID);\n\nprotected:\n\tFECoreBase*\t\tm_pParent;\t\t//!< pointer to the parent class (i.e. the class that defines this property)\n\tSUPER_CLASS_ID\tm_superClassID;\t//!< The super class ID\n};\n"
  },
  {
    "path": "FECore/FEPropertyT.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"DumpStream.h\"\n\n//-----------------------------------------------------------------------------\ntemplate <class T>\nclass FEPropertyT : public FEProperty\n{\nprivate:\n\tT**\tm_pc;\t//!< pointer to pointer of property\n\npublic:\n\tFEPropertyT(T** ppc) : FEProperty(T::superClassID())\n\t{ \n\t\tm_pc = ppc; \n\t\tm_className = T::BaseClassName();\n\t}\n\n\tbool IsArray() const override { return false; }\n\tbool IsType(FECoreBase* pc) const override { return (dynamic_cast<T*>(pc) != nullptr); }\n\tvoid SetProperty(FECoreBase* pc) override \n\t{ \n\t\t*m_pc = dynamic_cast<T*>(pc);\n\t\tpc->SetParent(GetParent());\n\t}\n\tint size() const override { return ((*m_pc) == 0 ? 0 : 1); }\n\n\tFECoreBase* get(int i) override { return *m_pc; }\n\tFECoreBase* get(const char* szname) override\n\t{\n\t\tif ((*m_pc)->GetName() == std::string(szname))\n\t\t\treturn *m_pc;\n\t\telse\n\t\t\treturn 0;\n\t}\n\n\tFECoreBase* getFromID(int nid) override\n\t{\n\t\tif (m_pc && (*m_pc) && ((*m_pc)->GetID() == nid)) return *m_pc; else return 0;\n\t}\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tar & (*m_pc);\n\t}\n\n\tbool Init() override\n\t{\n\t\tif (m_pc && (*m_pc)) { return (*m_pc)->Init(); }\n\t\treturn (IsRequired() == false);\n\t}\n\n\tbool Validate() override\n\t{\n\t\tif (m_pc && (*m_pc)) return (*m_pc)->Validate();\n\t\treturn true;\n\t}\n};\n\n//-----------------------------------------------------------------------------\ntemplate <class T>\nclass FEFixedPropertyT : public FEProperty\n{\nprivate:\n\tT* m_pc;\t//!< pointer to property\n\npublic:\n\tFEFixedPropertyT(T* ppc) : FEProperty(T::superClassID())\n\t{\n\t\tm_pc = ppc;\n\t\tm_className = T::BaseClassName();\n\t}\n\n\tbool IsArray() const override { return false; }\n\tbool IsType(FECoreBase* pc) const override { return (dynamic_cast<T*>(pc) != nullptr); }\n\tvoid SetProperty(FECoreBase* pc) override\n\t{\n\t\tassert(m_pc == nullptr);\n\t\tm_pc = dynamic_cast<T*>(pc);\n\t\tpc->SetParent(GetParent());\n\t}\n\tint size() const override { return (m_pc == 0 ? 0 : 1); }\n\n\tFECoreBase* get(int i) override { return m_pc; }\n\tFECoreBase* get(const char* szname) override\n\t{\n\t\tif (m_pc->GetName() == std::string(szname))\n\t\t\treturn m_pc;\n\t\telse\n\t\t\treturn 0;\n\t}\n\n\tFECoreBase* getFromID(int nid) override\n\t{\n\t\tif (m_pc && (m_pc->GetID() == nid)) return m_pc; else return nullptr;\n\t}\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tassert(m_pc);\n\t\tar & (*m_pc);\n\t}\n\n\tbool Init() override\n\t{\n\t\tif (m_pc) { return m_pc->Init(); }\n\t\telse return false;\n\t}\n\n\tbool Validate() override\n\t{\n\t\tif (m_pc) return m_pc->Validate();\n\t\treturn true;\n\t}\n};\n\ntemplate <class T>\nclass FEFixedVecPropertyT : public FEProperty\n{\nprivate:\n\tstd::vector<T>* m_pc;\t//!< pointer to property list\n\npublic:\n\tFEFixedVecPropertyT(std::vector<T>* ppc) : FEProperty(T::superClassID())\n\t{\n\t\tm_pc = ppc;\n\t\tm_className = T::BaseClassName();\n\t}\n\n\tbool IsArray() const override { return true; }\n\tbool IsType(FECoreBase* pc) const override { return (dynamic_cast<T*>(pc) != nullptr); }\n\tvoid SetProperty(FECoreBase* pc) override\n\t{\n\t\tT* p = dynamic_cast<T*>(pc); assert(p);\n\t\tif (p == nullptr) return;\n\t\tm_pc->push_back(*p);\n\t}\n\tint size() const override { return (int)(m_pc == 0 ? 0 : m_pc->size()); }\n\n\tFECoreBase* get(int i) override \n\t{ \n\t\tif ((i >= 0) && (i < (int)m_pc->size())) return &(*m_pc)[i];\n\t\tif (i == m_pc->size())\n\t\t{\n\t\t\tm_pc->emplace_back(T());\n\t\t\treturn &m_pc->back();\n\t\t}\n\t\treturn nullptr;\n\t}\n\tFECoreBase* get(const char* szname) override\n\t{\n\t\t// This requires that T has a GetName() member function, which may not be the case.\n\t\treturn nullptr;\n\t}\n\n\tFECoreBase* getFromID(int nid) override\n\t{\n\t\t// This assumes that T has a GetID() member, which may not be the case.\n\t\t// I don't think I can implement this.\n\t\treturn nullptr;\n\t}\n\n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\t// TODO: implement serialization\n\t}\n\n\tbool Init() override\n\t{\n\t\t// TODO: implement initialization\n\t\treturn true;\n\t}\n\n\tbool Validate() override\n\t{\n\t\t// TODO: implement validation\n\t\treturn true;\n\t}\n};\n\n//-----------------------------------------------------------------------------\n//! Use this class to define array material properties\ntemplate<class T> class FEVecPropertyT : public FEProperty\n{\nprivate:\n\ttypedef std::vector<T*>\tY;\n\tY*\tm_pmp;\t\t//!< pointer to actual material property\n\npublic:\n\tFEVecPropertyT(Y* p) : FEProperty(T::superClassID()) \n\t{ \n\t\tm_pmp = p; \n\t\tm_className = T::BaseClassName();\n\t}\n\tT* operator [] (int i) { return (*m_pmp)[i]; }\n\tconst T* operator [] (int i) const { return (*m_pmp)[i]; }\n\n\tvirtual bool IsArray() const { return true; }\n\tvirtual bool IsType(FECoreBase* pc) const { return (dynamic_cast<T*>(pc) != 0); }\n\tvirtual void SetProperty(FECoreBase* pc) {\n\t\tT* pt = dynamic_cast<T*>(pc); assert(pt != nullptr);\n\t\tm_pmp->push_back(pt); \n\t\tpt->SetParent(GetParent());\n\t}\n\tvirtual int size() const { return (int)m_pmp->size(); }\n\tvirtual FECoreBase* get(int i) { return (*m_pmp)[i]; }\n\n\tvirtual FECoreBase* get(const char* szname)\n\t{ \n\t\tstd::string name(szname);\n\t\tfor (int i=0; i<(int) m_pmp->size(); ++i)\n\t\t{\n\t\t\tT* p = (*m_pmp)[i];\n\t\t\tif (p->GetName() == name) return p;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tvirtual FECoreBase* getFromID(int nid)\n\t{\n\t\tfor (int i = 0; i<(int)m_pmp->size(); ++i)\n\t\t{\n\t\t\tT* p = (*m_pmp)[i];\n\t\t\tif (p && (p->GetID() == nid)) return p;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tvoid AddProperty(FECoreBase* pc) { \n\t\tm_pmp->push_back(dynamic_cast<T*>(pc)); \n\t\tpc->SetParent(GetParent());\n\t}\n\n\tvoid Clear()\n\t{\n\t\tfor (int i=0; i<(int) m_pmp->size(); ++i) delete (*m_pmp)[i];\n\t\tm_pmp->clear();\n\t}\n\n\tvoid Insert(int n, T* pc)\n\t{\n\t\tm_pmp->insert(m_pmp->begin()+n, pc);\n\t\tpc->SetParent(GetParent());\n\t}\n\n\tvoid Serialize(DumpStream& ar)\n\t{\n\t\tif (ar.IsSaving())\n\t\t{\n\t\t\tint n = size();\n\t\t\tar << n;\n\t\t\tfor (int i = 0; i<n; ++i)\n\t\t\t{\n\t\t\t\tT* pm = (*m_pmp)[i];\n\t\t\t\tar << pm;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint n = 0;\n\t\t\tar >> n;\n\t\t\tif (ar.IsShallow() == false) m_pmp->assign(n, nullptr);\n\t\t\tassert(m_pmp->size() == n);\n\t\t\tfor (int i = 0; i<n; ++i)\n\t\t\t{\n\t\t\t\tar >> (*m_pmp)[i];\n\t\t\t}\n\t\t}\n\t}\n\n\tbool Init() {\n\t\tif (m_pmp->empty() && IsRequired()) return false;\n\t\tfor (size_t i = 0; i<m_pmp->size(); ++i)\n\t\t{\n\t\t\tif ((*m_pmp)[i])\n\t\t\t{\n\t\t\t\tif ((*m_pmp)[i]->Init() == false) return false;\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tbool Validate() {\n\t\tif (m_pmp->empty()) return true;\n\t\tfor (size_t i = 0; i<m_pmp->size(); ++i)\n\t\t{\n\t\t\tif ((*m_pmp)[i])\n\t\t\t{\n\t\t\t\tif ((*m_pmp)[i]->Validate() == false) return false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n};\n\n"
  },
  {
    "path": "FECore/FESPRProjection.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESPRProjection.h\"\n#include \"FESolidDomain.h\"\n#include \"FEMesh.h\"\nusing namespace std;\n\nFESPRProjection::FESPRProjection(FESolidDomain& dom, int interpolationOrder) : m_dom(dom)\n{\n\tm_p = interpolationOrder;\n\n\tInit();\n}\n\nvoid FESPRProjection::Init()\n{\n\tFESolidDomain& dom = m_dom;\n\n\t// build the node-element-list. This will define our patches\n\tNEL.Create(dom);\n\n\t// get the mesh\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NN = dom.Nodes();\n\n\t// check element type\n\tint NDOF = -1;\t// number of degrees of freedom of polynomial\n\tint NCN = -1;\t// number of corner nodes\n\tint nshape = dom.GetElementShape();\n\tswitch (nshape)\n\t{\n\tcase ET_TET4:\n\tcase ET_TET5 : { NDOF = 4; NCN = 4; } break;\n\tcase ET_TET10: { NDOF = (m_p == 1 ? 4 : 10); NCN = 4; } break;\n\tcase ET_TET15: { NDOF = (m_p == 1 ? 4 : 10); NCN = 4; } break;\n\tcase ET_TET20: { NDOF = 10; NCN = 4; } break;\n\tcase ET_HEX8 : { NDOF = 7; NCN = 8; } break;\n\tcase ET_HEX20: { NDOF = (m_p == 1 ? 7 : 10); NCN = 8; } break;\n\tcase ET_HEX27: { NDOF = (m_p == 1 ? 7 : 10); NCN = 8; } break;\n\tdefault:\n\t\treturn;\n\t}\n\n\t// we keep a tag array to keep track of which nodes we processed\n\tint NM = mesh.Nodes();\n\tvector<int> tag; tag.assign(NM, 0);\n\n\t// for higher order elements\n\t// we need to make sure that we don't process the edge nodes\n\t// we assume here that the first NCN nodes of the element\n\t// are the corner nodes and that all other nodes are edge or interior nodes\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = NCN; j < ne; ++j) tag[el.m_node[j]] = 2;\n\t}\n\n\t// loop over all nodes\n\tm_valence.assign(NN, 0);\n\tm_Ai.resize(NN);\n\n#pragma omp parallel for schedule(static)\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = dom.Node(i);\n\t\tint in = dom.NodeIndex(i);\n\n\t\t// don't loop over edge nodes (edge or interior nodes have a tag > 1)\n\t\tif (tag[in] <= 1)\n\t\t{\n\t\t\t// get the nodal position\n\t\t\tvec3d rc = node.m_rt;\n\n\t\t\t// get the element patch\n\t\t\tint ne = NEL.Valence(in);\n\t\t\tFEElement** ppe = NEL.ElementList(in);\n\t\t\tint* pei = NEL.ElementIndexList(in);\n\n\t\t\t// setup the A-matrix\n\t\t\tvector<double> pk(NDOF);\n\t\t\tmatrix A(NDOF, NDOF); A.zero();\n\t\t\tint m = 0;\n\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t{\n\t\t\t\tFEElement& el = *(ppe[j]);\n\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int n = 0; n < nint; ++n, ++m)\n\t\t\t\t{\n\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\tvec3d r = mp.m_rt - rc;\n\t\t\t\t\tpk[0] = 1.0; pk[1] = r.x; pk[2] = r.y; pk[3] = r.z;\n\t\t\t\t\tif (NDOF >= 7) { pk[4] = r.x * r.y; pk[5] = r.y * r.z; pk[6] = r.x * r.z; }\n\t\t\t\t\tif (NDOF >= 10) { pk[7] = r.x * r.x; pk[8] = r.y * r.y; pk[9] = r.z * r.z; }\n\t\t\t\t\tA += outer_product(pk);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// invert matrix and make sure condition number is good enough\n\t\t\tmatrix Ai = A.inverse();\n\t\t\tm_Ai[i] = Ai;\n\t\t\tm_valence[i] = m;\n\t\t}\n\t}\n}\n\n//! Projects the integration point data, stored in d, onto the nodes of the domain.\n//! The result is stored in o.\nvoid FESPRProjection::Project(const vector< vector<double> >& d, vector<double>& o)\n{\n\tFESolidDomain& dom = m_dom;\n\n\t// get the mesh\n\tFEMesh& mesh = *dom.GetMesh();\n\tint NN = dom.Nodes();\n\n\t// allocate output array\n\to.assign(NN, 0.0);\n\n\t// check element type\n\tint NDOF = -1;\t// number of degrees of freedom of polynomial\n\tint NCN = -1;\t// number of corner nodes\n\tint nshape = dom.GetElementShape();\n\tswitch (nshape)\n\t{\n\tcase ET_TET4:\n\tcase ET_TET5: { NDOF = 4; NCN = 4; } break;\n\tcase ET_TET10: { NDOF = (m_p == 1 ? 4 : 10); NCN = 4; } break;\n\tcase ET_TET15: { NDOF = (m_p == 1 ? 4 : 10); NCN = 4; } break;\n\tcase ET_TET20: { NDOF = 10; NCN = 4; } break;\n\tcase ET_HEX8: { NDOF = 7; NCN = 8; } break;\n\tcase ET_HEX20: { NDOF = (m_p == 1 ? 7 : 10); NCN = 8; } break;\n\tcase ET_HEX27: { NDOF = (m_p == 1 ? 7 : 10); NCN = 8; } break;\n\tdefault:\n\t\treturn;\n\t}\n\n\t// we keep a tag array to keep track of which nodes we processed\n\tint NM = mesh.Nodes();\n\tvector<int> tag; tag.assign(NM, 0);\n\n\t// for higher order elements\n\t// we need to make sure that we don't process the edge nodes\n\t// we assume here that the first NCN nodes of the element\n\t// are the corner nodes and that all other nodes are edge or interior nodes\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = NCN; j < ne; ++j) tag[el.m_node[j]] = 2;\n\t}\n\n\t// this array will store the results\n\tvector<double> val;\n\tval.assign(NM, 0.0);\n\n\t// loop over all nodes\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\t// get the node\n\t\tFENode& node = dom.Node(i);\n\t\tint in = dom.NodeIndex(i);\n\n\t\t// don't loop over edge nodes (edge or interior nodes have a tag > 1)\n\t\tif (tag[in] <= 1)\n\t\t{\n\t\t\t// get the nodal position\n\t\t\tvec3d rc = node.m_rt;\n\n\t\t\t// get the element patch\n\t\t\tint ne = NEL.Valence(in);\n\t\t\tFEElement** ppe = NEL.ElementList(in);\n\t\t\tint* pei = NEL.ElementIndexList(in);\n\n\t\t\tvector<double> pk(NDOF);\n\t\t\tint m = m_valence[i];\n\t\t\tmatrix Ai = m_Ai[i];\n\n\t\t\t// make sure we have enough sampling points\n\t\t\tif (m > NDOF + 1)\n\t\t\t{\n\t\t\t\tvector<double> b; b.assign(NDOF, 0.0);\n\t\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = *(ppe[j]);\n\t\t\t\t\tconst vector<double>& ed = d[pei[j]];\n\n\t\t\t\t\tassert(ppe[j] == &dom.Element(pei[j]));\n\n\t\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\t\t\tvec3d r = mp.m_rt - rc;\n\t\t\t\t\t\tpk[0] = 1.0; pk[1] = r.x; pk[2] = r.y; pk[3] = r.z;\n\t\t\t\t\t\tif (NDOF >= 7) { pk[4] = r.x * r.y; pk[5] = r.y * r.z; pk[6] = r.x * r.z; }\n\t\t\t\t\t\tif (NDOF >= 10) { pk[7] = r.x * r.x; pk[8] = r.y * r.y; pk[9] = r.z * r.z; }\n\n\t\t\t\t\t\tdouble s = ed[n];\n\t\t\t\t\t\tfor (int k = 0; k < NDOF; k++) b[k] += s * pk[k];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// solve the linear system\n\t\t\t\tvector<double> c = Ai * b;\n\n\t\t\t\t// tag this node as processed\n\t\t\t\ttag[in] = 1;\n\n\t\t\t\t// store result\n\t\t\t\tval[in] = c[0];\n\n\t\t\t\t// loop over all unprocessed nodes of this patch\n\t\t\t\tfor (int j = 0; j < ne; ++j)\n\t\t\t\t{\n\t\t\t\t\tFEElement& el = *(ppe[j]);\n\t\t\t\t\tint en = el.Nodes();\n\t\t\t\t\tfor (int k = 0; k < en; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tint em = el.m_node[k];\n\t\t\t\t\t\tif (tag[em] != 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvec3d r = mesh.Node(em).m_rt - rc;\n\t\t\t\t\t\t\tpk[0] = 1.0; pk[1] = r.x; pk[2] = r.y; pk[3] = r.z;\n\t\t\t\t\t\t\tif (NDOF >= 7) { pk[4] = r.x * r.y; pk[5] = r.y * r.z; pk[6] = r.x * r.z; }\n\t\t\t\t\t\t\tif (NDOF >= 10) { pk[7] = r.x * r.x; pk[8] = r.y * r.y; pk[9] = r.z * r.z; }\n\n\t\t\t\t\t\t\t// calculate the value for this node\n\t\t\t\t\t\t\tdouble v = 0;\n\t\t\t\t\t\t\tfor (int l = 0; l < NDOF; ++l) v += pk[l] * c[l];\n\n\t\t\t\t\t\t\t// for edge nodes, we need to keep track of how often we visit this node\n\t\t\t\t\t\t\t// Therefore we increment the tag.\n\t\t\t\t\t\t\t// (remember that the tag started at 2 for edge/interior nodes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (tag[em] >= 2)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttag[em]++;\n\t\t\t\t\t\t\t\t\tval[em] += v;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse val[em] = v;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// copy results to archive\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tint in = dom.NodeIndex(i);\n\t\tdouble s = val[in];\n\n\t\t// for edge nodes we need to average\n\t\t// (remember that the tag started at 2 for edge/interior nodes)\n\t\tif (tag[in] >= 2)\n\t\t{\n\t\t\t//\t\t\tassert(tag[in] > 2);\t// all edges nodes must be visited at least once!\n\t\t\tint l = tag[in] - 2;\n\t\t\tif (l > 0) s /= (double)(l);\n\t\t}\n\n\t\to[i] = s;\n\t}\n}\n"
  },
  {
    "path": "FECore/FESPRProjection.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include \"fecore_api.h\"\n#include <FECore/matrix.h>\n#include <FECore/FENodeElemList.h>\n\nclass FESolidDomain;\n\n//-------------------------------------------------------------------------------------------------\n//! This class implements the super-convergent-patch recovery method which projects integration point\n//! data to the finite element nodes.\nclass FECORE_API FESPRProjection\n{\npublic:\n\tFESPRProjection(FESolidDomain& dom, int interpolationOrder = -1);\n\n\tvoid Project(const std::vector< std::vector<double> >& d, std::vector<double>& o);\n\nprivate:\n\tvoid Init();\n\nprotected:\n\tFESolidDomain& m_dom;\t//!< reference to the solid domain\t\n\tFENodeElemList NEL;\n\tstd::vector<matrix> m_Ai;\n\tstd::vector<int> m_valence;\n\tint\t\tm_p;\t//!< interpolation order (set to -1 for default rules)\n};\n"
  },
  {
    "path": "FECore/FEScalarValuator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEScalarValuator.h\"\n#include \"FEMaterialPoint.h\"\n#include \"FEModelParam.h\"\n#include \"FEModel.h\"\n#include \"DumpStream.h\"\n#include \"log.h\"\n\n//=============================================================================\nbool FEMathExpression::Init(const std::string& expr, FECoreBase* pc)\n{\n\tClear();\n\n\tAddVariable(\"X\");\n\tAddVariable(\"Y\");\n\tAddVariable(\"Z\");\n\tAddVariable(\"t\");\n\tbool b = Create(expr, true);\n\n\t// lookup all the other variables.\n\tif (Variables() > 4)\n\t{\n\t\tif (pc == nullptr) return false;\n\n\t\tfor (int i = 4; i < Variables(); ++i)\n\t\t{\n\t\t\tMVariable* vari = Variable(i);\n\n\t\t\tParamString ps(vari->Name().c_str());\n\t\t\tFEParam* p = pc->FindParameter(ps);\n\t\t\tif (p)\n\t\t\t{\n\t\t\t\tassert((p->type() == FE_PARAM_DOUBLE_MAPPED) || (p->type() == FE_PARAM_DOUBLE) || (p->type() == FE_PARAM_INT));\n\n\t\t\t\tMathParam mp;\n\t\t\t\tmp.type = 0;\n\t\t\t\tmp.pp = p;\n\t\t\t\tm_vars.push_back(mp);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// see if it's a data map\n\t\t\t\tFEModel* fem = pc->GetFEModel();\n\n\t\t\t\t// see if it's a global variable\n\t\t\t\tp = fem->FindParameter(ps);\n\t\t\t\tif (p)\n\t\t\t\t{\n\t\t\t\t\tMathParam mp;\n\t\t\t\t\tmp.type = 0;\n\t\t\t\t\tmp.pp = p;\n\t\t\t\t\tm_vars.push_back(mp);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFEMesh& mesh = fem->GetMesh();\n\n\t\t\t\t\tFEDataMap* map = mesh.FindDataMap(vari->Name());\n\t\t\t\t\tassert(map);\n\t\t\t\t\tif (map == nullptr) {\n\t\t\t\t\t\tconst char* szvar = vari->Name().c_str();\n\t\t\t\t\t\tfeLogErrorEx(fem, \"Don't understand variable name \\\"%s\\\" in math expression.\", szvar);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (map->DataType() != FEDataType::FE_DOUBLE) {\n\t\t\t\t\t\tconst char* szvar = vari->Name().c_str();\n\t\t\t\t\t\tfeLogErrorEx(fem, \"Variable \\\"%s\\\" is not a scalar variable.\", szvar);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tMathParam mp;\n\t\t\t\t\tmp.type = 1;\n\t\t\t\t\tmp.map = map;\n\t\t\t\t\tm_vars.push_back(mp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tassert(b);\n\treturn b;\n}\n\nvoid FEMathExpression::operator = (const FEMathExpression& me)\n{\n\tMSimpleExpression::operator=(me);\n\tm_vars = me.m_vars;\n}\n\ndouble FEMathExpression::value(FEModel* fem, const FEMaterialPoint& pt)\n{\n\tstd::vector<double> var(4 + m_vars.size());\n\tvar[0] = pt.m_r0.x;\n\tvar[1] = pt.m_r0.y;\n\tvar[2] = pt.m_r0.z;\n\tvar[3] = fem->GetTime().currentTime;\n\tif (m_vars.empty() == false)\n\t{\n\t\tfor (int i = 0; i < (int)m_vars.size(); ++i)\n\t\t{\n\t\t\tMathParam& mp = m_vars[i];\n\t\t\tif (mp.type == 0)\n\t\t\t{\n\t\t\t\tFEParam* pi = mp.pp;\n\t\t\t\tswitch (pi->type())\n\t\t\t\t{\n\t\t\t\tcase FE_PARAM_INT: var[4 + i] = (double)pi->value<int>(); break;\n\t\t\t\tcase FE_PARAM_DOUBLE: var[4 + i] = pi->value<double>(); break;\n\t\t\t\tcase FE_PARAM_DOUBLE_MAPPED: var[4 + i] = pi->value<FEParamDouble>()(pt); break;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFEDataMap& map = *mp.map;\n\t\t\t\tvar[4 + i] = map.value(pt);\n\t\t\t}\n\t\t}\n\t}\n\treturn value_s(var);\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FEConstValue, FEScalarValuator)\n\tADD_PARAMETER(m_val, \"const\");\nEND_FECORE_CLASS();\n\nFEScalarValuator* FEConstValue::copy()\n{\n\tFEConstValue* val = fecore_alloc(FEConstValue, GetFEModel());\n\tval->m_val = m_val;\n\treturn val;\n}\n\n//=============================================================================\n\nBEGIN_FECORE_CLASS(FEMathValue, FEScalarValuator)\n\tADD_PARAMETER(m_expr, \"math\");\nEND_FECORE_CLASS();\n\nFEMathValue::FEMathValue(FEModel* fem) : FEScalarValuator(fem)\n{\n\tm_parent = nullptr;\n}\n\nvoid FEMathValue::setMathString(const std::string& s)\n{\n\tm_expr = s;\n}\n\nbool FEMathValue::Init()\n{\n\treturn create();\n}\n\nvoid FEMathValue::Serialize(DumpStream& ar)\n{\n\tFEScalarValuator::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tif (ar.IsSaving())\n\t{\n\t\tar << m_parent;\n\t}\n\telse if (ar.IsLoading())\n\t{\n\t\tar >> m_parent;\n\t\tcreate(m_parent);\n\t}\n}\n\nbool FEMathValue::create(FECoreBase* pc)\n{\n\t// see if this is already initialized\n\tif (m_math.Variables() > 0) return true;\n\n\tif (pc == nullptr)\n\t{\n\t\t// try to find the owner of this parameter\n\t\t// First, we need the model parameter\n\t\tFEModelParam* param = GetModelParam();\n\t\tif (param == nullptr) return false;\n\n\t\t// we'll need the model for this\n\t\tFEModel* fem = GetFEModel();\n\t\tif (fem == nullptr) return false;\n\n\t\t// Now try to find the owner of this parameter\n\t\tpc = fem->FindParameterOwner(param);\n\t}\n\tm_parent = pc;\n\n\t// initialize the math expression\n\tbool b = m_math.Init(m_expr, pc);\n\treturn b;\n}\n\nFEMathValue::~FEMathValue()\n{\n}\n\nFEScalarValuator* FEMathValue::copy()\n{\n\tFEMathValue* newExpr = fecore_alloc(FEMathValue, GetFEModel());\n\tnewExpr->m_expr = m_expr;\n\tnewExpr->m_math = m_math;\n\treturn newExpr;\n}\n\ndouble FEMathValue::operator()(const FEMaterialPoint& pt)\n{\n\treturn m_math.value(GetFEModel(), pt);\n}\n\n//---------------------------------------------------------------------------------------\n\nFEMappedValue::FEMappedValue(FEModel* fem) : FEScalarValuator(fem), m_val(nullptr)\n{\n\tm_scale = 1.0;\n}\n\nvoid FEMappedValue::setDataMap(FEDataMap* val)\n{\n\tm_val = val;\n}\n\nFEDataMap* FEMappedValue::dataMap()\n{\n\treturn m_val;\n}\n\nvoid FEMappedValue::setScaleFactor(double s)\n{\n\tm_scale = s;\n}\n\ndouble FEMappedValue::operator()(const FEMaterialPoint& pt)\n{\n\treturn m_scale*m_val->value(pt);\n}\n\nFEScalarValuator* FEMappedValue::copy()\n{\n\tFEMappedValue* map = fecore_alloc(FEMappedValue, GetFEModel());\n\tmap->setDataMap(m_val);\n\tmap->m_scale = m_scale;\n\treturn map;\n}\n\nvoid FEMappedValue::Serialize(DumpStream& dmp)\n{\n\tif (dmp.IsShallow()) return;\n\tdmp & m_scale;\n\tdmp & m_val;\n}\n\n//---------------------------------------------------------------------------------------\n\nFENodeMappedValue::FENodeMappedValue(FEModel* fem) : FEScalarValuator(fem), m_val(nullptr)\n{\n\n}\n\nvoid FENodeMappedValue::setDataMap(FENodeDataMap* val)\n{\n\tm_val = val;\n}\n\ndouble FENodeMappedValue::operator()(const FEMaterialPoint& pt)\n{\n\treturn m_val->getValue(pt.m_index);\n}\n\nFEScalarValuator* FENodeMappedValue::copy()\n{\n\tFENodeMappedValue* map = fecore_alloc(FENodeMappedValue, GetFEModel());\n\tmap->setDataMap(m_val);\n\treturn map;\n}\n"
  },
  {
    "path": "FECore/FEScalarValuator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEValuator.h\"\n#include \"MathObject.h\"\n#include \"FEDataMap.h\"\n#include \"FENodeDataMap.h\"\n\n//---------------------------------------------------------------------------------------\n// Base class for evaluating scalar parameters\nclass FECORE_API FEScalarValuator : public FEValuator\n{\n\tFECORE_SUPER_CLASS(FESCALARVALUATOR_ID)\n\tFECORE_BASE_CLASS(FEScalarValuator)\n\npublic:\n\tFEScalarValuator(FEModel* fem) : FEValuator(fem) {};\n\n\tvirtual double operator()(const FEMaterialPoint& pt) = 0;\n\n\tvirtual FEScalarValuator* copy() = 0;\n\n\tvirtual bool isConst() { return false; }\n\n\tvirtual double* constValue() { return nullptr; }\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEConstValue : public FEScalarValuator\n{\npublic:\n\tFEConstValue(FEModel* fem) : FEScalarValuator(fem), m_val(0.0) {};\n\tdouble operator()(const FEMaterialPoint& pt) override { return m_val; }\n\n\tbool isConst() override { return true; }\n\n\tdouble* constValue() override { return &m_val; }\n\n\tFEScalarValuator* copy() override;\n\nprivate:\n\tdouble\tm_val;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------------------\nclass FEMathExpression : public MSimpleExpression\n{\n\tstruct MathParam\n\t{\n\t\tint\t\t\ttype;\t// 0 = param, 1 = map\n\t\tFEParam* pp;\n\t\tFEDataMap* map;\n\t};\n\npublic:\n\tbool Init(const std::string& expr, FECoreBase* pc = nullptr);\n\n\tvoid operator = (const FEMathExpression& me);\n\n\tdouble value(FEModel* fem, const FEMaterialPoint& pt);\n\nprivate:\n\tstd::vector<MathParam>\tm_vars;\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEMathValue : public FEScalarValuator\n{\npublic:\n\tFEMathValue(FEModel* fem);\n\t~FEMathValue();\n\tdouble operator()(const FEMaterialPoint& pt) override;\n\n\tbool Init() override;\n\n\tFEScalarValuator* copy() override;\n\n\tvoid setMathString(const std::string& s);\n\n\tbool create(FECoreBase* pc = 0);\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tstd::string\t\t\tm_expr;\n\tFEMathExpression\tm_math;\n\tFECoreBase*\t\t\tm_parent;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FEMappedValue : public FEScalarValuator\n{\npublic:\n\tFEMappedValue(FEModel* fem);\n\tvoid setDataMap(FEDataMap* val);\n\tvoid setScaleFactor(double s);\n\n\tFEDataMap* dataMap();\n\n\tdouble operator()(const FEMaterialPoint& pt) override;\n\n\tFEScalarValuator* copy() override;\n\n\tvoid Serialize(DumpStream& dmp) override;\n\nprivate:\n\tdouble\t\tm_scale;\t// scale factor\n\tFEDataMap*\tm_val;\n};\n\n//---------------------------------------------------------------------------------------\nclass FECORE_API FENodeMappedValue : public FEScalarValuator\n{\npublic:\n\tFENodeMappedValue(FEModel* fem);\n\tvoid setDataMap(FENodeDataMap* val);\n\n\tdouble operator()(const FEMaterialPoint& pt) override;\n\n\tFEScalarValuator* copy() override;\n\nprivate:\n\tFENodeDataMap*\t\tm_val;\n};\n\n"
  },
  {
    "path": "FECore/FEScriptedBehavior.cpp",
    "content": "#include \"FEScriptedBehavior.h\"\n#include <febcode/vm.h>\n#include <febcode/parser.h>\n#include <febcode/compiler.h>\n#include <febcode/differentiator.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FEModelParam.h>\n#include <sstream>\n\nclass FEScriptedBehavior::Imp\n{\npublic:\n\tstruct Global {\n\t\tint slot = -1;\n\t\tstd::string name;\n\t};\n\n\tstruct Script {\n\t\tstd::vector<Global> globals;\n\t\tstd::string script;\n\t\tfebcode::Program program;\n\t\tFECoreBase* pc = nullptr;\n\n\t\tint AddGlobalDouble(const std::string& name)\n\t\t{\n\t\t\tGlobal g;\n\t\t\tg.name = name;\n\t\t\tg.slot = program.injectGlobal(name, program.types.Double());\n\t\t\tglobals.push_back(g);\n\t\t\treturn g.slot;\n\t\t}\n\n\t\tint AddGlobalVec3(const std::string& name)\n\t\t{\n\t\t\tGlobal g;\n\t\t\tg.name = name;\n\t\t\tg.slot = program.injectGlobal(name, program.types.Vec3());\n\t\t\tglobals.push_back(g);\n\t\t\treturn g.slot;\n\t\t}\n\n\t\tvoid SetReturnType(FEValueType type)\n\t\t{\n\t\t\tswitch (type)\n\t\t\t{\n\t\t\tcase FEValueType::Bool  : program.returnType = program.types.Bool(); break;\n\t\t\tcase FEValueType::Int   : program.returnType = program.types.Int(); break;\n\t\t\tcase FEValueType::Double: program.returnType = program.types.Double(); break;\n\t\t\tcase FEValueType::Vec3d : program.returnType = program.types.Vec3(); break;\n\t\t\tcase FEValueType::Mat3d : program.returnType = program.types.Mat3(); break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t}\n\t\t}\n\n\t\tbool Init()\n\t\t{\n\t\t\tfebcode::ParseSource(program, script);\n\n\t\t\tfebcode::Compiler compiler(program);\n\t\t\tcompiler.compile();\n\n\t\t\t// see if the globals were actually used\n\t\t\tfor (auto& g : globals)\n\t\t\t{\n\t\t\t\tif (program.globals[g.slot].refcount == 0)\n\t\t\t\t{\n\t\t\t\t\tg.slot = -1; // mark as unused\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tdouble Run(const FEMaterialPoint& mp, const std::vector<double>& vars)\n\t\t{\n\t\t\tthread_local febcode::VM vm;\n\t\t\tvm.setProgram(program);\n\n\t\t\tassert(globals.size() == vars.size());\n\t\t\tfor (int i = 0; i < (int)vars.size(); ++i)\n\t\t\t{\n\t\t\t\tImp::Global& g = globals[i];\n\t\t\t\tif (g.slot >= 0)\n\t\t\t\t{\n\t\t\t\t\tvm.setGlobal(g.slot, vars[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (pc)\n\t\t\t{\n\t\t\t\tFEParameterList& pl = pc->GetParameterList();\n\t\t\t\tauto it = pl.first();\n\t\t\t\tfor (int i=0; i < pl.Parameters(); ++i, it++)\n\t\t\t\t{\n\t\t\t\t\tFEParam& pi = *it;\n\t\t\t\t\tswitch (pi.type())\n\t\t\t\t\t{\n\t\t\t\t\tcase FEParamType::FE_PARAM_BOOL: vm.setInput(pi.name(), pi.value<bool  >()); break;\n\t\t\t\t\tcase FEParamType::FE_PARAM_INT: vm.setInput(pi.name(), pi.value<int   >()); break;\n\t\t\t\t\tcase FEParamType::FE_PARAM_DOUBLE: vm.setInput(pi.name(), pi.value<double>()); break;\n\t\t\t\t\tcase FEParamType::FE_PARAM_DOUBLE_MAPPED:\n\t\t\t\t\t{\n\t\t\t\t\t\tFEParamDouble& p = pi.value<FEParamDouble>();\n\t\t\t\t\t\tvm.setInput(pi.name(), p(mp));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tassert(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfebcode::Value v = vm.run();\n\t\t\treturn febcode::getDouble(v);\n\t\t}\n\n\t\tFEValue Run(const FEMaterialPoint& mp, const std::vector<FEValue>& vars)\n\t\t{\n\t\t\tthread_local febcode::VM vm;\n\t\t\tvm.setProgram(program);\n\n\t\t\tassert(globals.size() == vars.size());\n\t\t\tfor (int i = 0; i < (int)vars.size(); ++i)\n\t\t\t{\n\t\t\t\tImp::Global& g = globals[i];\n\t\t\t\tif (g.slot >= 0)\n\t\t\t\t{\n\t\t\t\t\tswitch (vars[i].type)\n\t\t\t\t\t{\n\t\t\t\t\tcase FEValueType::Bool  : vm.setGlobal(g.slot, (double)vars[i].b); break;\n\t\t\t\t\tcase FEValueType::Int   : vm.setGlobal(g.slot, (double)vars[i].i); break;\n\t\t\t\t\tcase FEValueType::Double: vm.setGlobal(g.slot, vars[i].d); break;\n\t\t\t\t\tcase FEValueType::Vec3d:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst vec3d& v3 = vars[i].v3;\n\t\t\t\t\t\t\tfebcode::vec3 v3_febcode(v3.x, v3.y, v3.z);\n\t\t\t\t\t\t\tvm.setGlobal(g.slot, v3_febcode);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase FEValueType::Mat3d:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst mat3d& m3 = vars[i].m3;\n\t\t\t\t\t\t\tfebcode::mat3 m3_febcode(m3(0,0), m3(0, 1), m3(0, 2), m3(1, 0), m3(1, 1), m3(1, 2), m3(2, 0), m3(2, 1), m3(2, 2));\n\t\t\t\t\t\t\tvm.setGlobal(g.slot, m3_febcode);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (pc)\n\t\t\t{\n\t\t\t\tFEParameterList& pl = pc->GetParameterList();\n\t\t\t\tauto it = pl.first();\n\t\t\t\tfor (int i = 0; i < pl.Parameters(); ++i, it++)\n\t\t\t\t{\n\t\t\t\t\tFEParam& pi = *it;\n\t\t\t\t\tif (pi.GetFlags() & FEParamFlag::FE_PARAM_USER)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (pi.type())\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase FEParamType::FE_PARAM_BOOL  : vm.setInput(pi.name(), pi.value<bool  >()); break;\n\t\t\t\t\t\tcase FEParamType::FE_PARAM_INT   : vm.setInput(pi.name(), pi.value<int   >()); break;\n\t\t\t\t\t\tcase FEParamType::FE_PARAM_DOUBLE: vm.setInput(pi.name(), pi.value<double>()); break;\n\t\t\t\t\t\tcase FEParamType::FE_PARAM_VEC3D:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvec3d v3 = pi.value<vec3d>();\n\t\t\t\t\t\t\tfebcode::vec3 v3_febcode(v3.x, v3.y, v3.z);\n\t\t\t\t\t\t\tvm.setInput(pi.name(), v3_febcode); \n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase FEParamType::FE_PARAM_DOUBLE_MAPPED:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEParamDouble& p = pi.value<FEParamDouble>();\n\t\t\t\t\t\t\tvm.setInput(pi.name(), p(mp));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase FEParamType::FE_PARAM_VEC3D_MAPPED:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFEParamVec3& p = pi.value<FEParamVec3>();\n\t\t\t\t\t\t\tvec3d v3 = p(mp);\n\t\t\t\t\t\t\tfebcode::vec3 v3_febcode(v3.x, v3.y, v3.z);\n\t\t\t\t\t\t\tvm.setInput(pi.name(), v3_febcode);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tassert(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfebcode::Value v = vm.run();\n\n\t\t\tFEValue result;\n\t\t\tswitch (v.index)\n\t\t\t{\n\t\t\tcase febcode::ValueIndex::BOOL:\n\t\t\t\tresult.type = FEValueType::Bool;\n\t\t\t\tresult.b = v.b;\n\t\t\t\tbreak;\n\t\t\tcase febcode::ValueIndex::INT:\n\t\t\t\tresult.type = FEValueType::Int;\n\t\t\t\tresult.i = v.i;\n\t\t\t\tbreak;\n\t\t\tcase febcode::ValueIndex::DOUBLE:\n\t\t\t\tresult.type = FEValueType::Double;\n\t\t\t\tresult.d = v.d;\n\t\t\t\tbreak;\n\t\t\tcase febcode::ValueIndex::VEC3:\n\t\t\t\tresult.type = FEValueType::Vec3d;\n\t\t\t\tresult.v3 = vec3d(v.vec3Value.x, v.vec3Value.y, v.vec3Value.z);\n\t\t\t\tbreak;\n\t\t\tcase febcode::ValueIndex::MAT3:\n\t\t\t\tresult.type = FEValueType::Mat3d;\n\t\t\t\tfebcode::mat3& m = v.mat3Value;\n\t\t\t\tresult.m3 = mat3d(m.m[0][0], m.m[0][1], m.m[0][2], m.m[1][0], m.m[1][1], m.m[1][2], m.m[2][0], m.m[2][1], m.m[2][2]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t};\n\n\tstruct Derive {\n\t\tScript code;\n\t\tstd::string varName; // variable with respect to which we are taking the derivative\n\t\tbool isNullProgram = false; // flag to indicate if the derivative program is null (i.e. the original program does not depend on the variable we're differentiating with respect to)\n\n\t\tbool Init()\n\t\t{\n\t\t\tfebcode::ParseSource(code.program, code.script);\n\n\t\t\t// get the type of the variable\n\t\t\tif (code.program.globalIndices.count(varName) == 0)\n\t\t\t{\n\t\t\t\tfeLogErrorEx(code.pc->GetFEModel(), \"Internal error: variable \\\"%s\\\" not found in global indices after differentiation\", varName.c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfebcode::Type varType = code.program.globalType(varName);\n\t\t\tif (varType == nullptr) return false;\n\n\t\t\tfebcode::Differentiator diff(code.program);\n\t\t\tdiff.differentiate(varName);\n\n\t\t\tif (!diff.DependencyFound())\n\t\t\t{\n\t\t\t\t// no dependency was found on the variable we are differentiating with respect to\n\t\t\t\tisNullProgram = true;\n\t\t\t\tcode.program.ast.reset();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfebcode::Compiler compiler(code.program);\n\t\t\t\tcompiler.compile();\n\n\t\t\t\t// see if the globals were actually used\n\t\t\t\tfor (auto& g : code.globals)\n\t\t\t\t{\n\t\t\t\t\tif (code.program.globals[g.slot].refcount == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tg.slot = -1; // mark as unused\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t};\n\npublic:\n\tFEModel* fem = nullptr;\n\n\tScriptContext context;\n\n\tScript code;\n\tstd::vector<Derive> valDeriv; //!< programs for derivatives\n};\n\nFEScriptedBehavior::FEScriptedBehavior(FEModel* fem) : FECoreBase(fem), m(*new Imp())\n{\n\tm.fem = fem;\n}\n\nvoid FEScriptedBehavior::SetScriptContext(const ScriptContext& context)\n{\n\tm.context = context;\n}\n\nvoid FEScriptedBehavior::SetDefaultContext(FEValueType returnType, FECoreBase* parent)\n{\n\tScriptContext ctx;\n\tctx.returnType = returnType;\n\tctx.addVariable(\"pos0\", FEValueType::Vec3d, false);\n\tctx.addVariable(\"time\", FEValueType::Double, false);\n\tSetScriptContext(ctx);\n}\n\nScriptContext FEScriptedBehavior::GetScriptContext() const\n{\n\treturn m.context;\n}\n\nvoid FEScriptedBehavior::SetScriptName(const std::string& scriptName)\n{\n\tm_scriptName = scriptName;\n}\n\nbool FEScriptedBehavior::HasDerivative(int id) const\n{\n\tif ((id < 0) || (id >= m.valDeriv.size())) return false;\n\treturn !m.valDeriv[id].isNullProgram;\n}\n\nbool FEScriptedBehavior::Init()\n{\n\tif (m_scriptName.empty())\n\t\tm_scriptName = GetName();\n\n#ifndef NDEBUG\n\tfeLogEx(m.fem, \"compiling script \\\"%s\\\":\\n\", m_scriptName.c_str());\n#endif\n\n\tm.code.script = m.fem->GetScript(m_scriptName).script;\n\tif (m.code.script.empty())\n\t{\n\t\tfeLogErrorEx(m.fem, \"Script \\\"%s\\\" is empty\", m_scriptName.c_str());\n\t\treturn false;\n\t}\n\n\tScriptContext ctx = m.context;\n\n\tm.code.SetReturnType(ctx.returnType);\n\tm.code.pc = this;\n\n\t// get the number of variables\n\tint nvars = (int)ctx.variables.size();\n\n\t// prep the variable names (prefix with underscore)\n\tstd::vector<std::string> varNamesPrefixed(nvars);\n\tfor (int i = 0; i < nvars; ++i)\n\t{\n\t\tvarNamesPrefixed[i] = \"_\" + ctx.variables[i].name;\n\t}\n\n\t// add the variables to the globals list\n\tfor (int i = 0; i < nvars; ++i)\n\t{\n\t\tswitch (ctx.variables[i].type)\n\t\t{\n\t\tcase FEValueType::Double: m.code.AddGlobalDouble(varNamesPrefixed[i]); break;\n\t\tcase FEValueType::Vec3d : m.code.AddGlobalVec3  (varNamesPrefixed[i]); break;\n\t\tdefault:\n\t\t\tfeLogErrorEx(m.fem, \"Unsupported variable type for variable \\\"%s\\\"\", ctx.variables[i].name.c_str());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// construct the derivatives\n\tm.valDeriv.resize(nvars);\n\tfor (int i = 0; i < nvars; ++i)\n\t{\n\t\tm.valDeriv[i].code.script = m.code.script;\n\t\tm.valDeriv[i].varName = varNamesPrefixed[i];\n\t\tm.valDeriv[i].code.pc = m.code.pc;\n\n\t\t// add the variables to the derivative code's global list\n\t\tfor (int j = 0; j < nvars; ++j)\n\t\t{\n\t\t\tswitch (ctx.variables[j].type)\n\t\t\t{\n\t\t\tcase FEValueType::Double: m.valDeriv[i].code.AddGlobalDouble(varNamesPrefixed[j]); break;\n\t\t\tcase FEValueType::Vec3d: m.valDeriv[i].code.AddGlobalVec3(varNamesPrefixed[j]); break;\n\t\t\t}\n\t\t}\n\t}\n\n\t// compile the main program\n\tm.code.SetReturnType(ctx.returnType);\n\tif (m.code.Init() == false) return false;\n\n\t// initialize the derivates\n\tfor (int i = 0; i < m.valDeriv.size(); ++i)\n\t{\n\t\tImp::Derive& deriv_i = m.valDeriv[i];\n\n\t\t// Set the return type of the original code. \n\t\t// The derivative code's return type will be set during differentiation based on the type of the variable \n\t\t// we're differentiating with respect to.\n\t\tderiv_i.code.SetReturnType(ctx.returnType); \n\n\t\t// if the corresponding variable is not differentiable, we mark the derivative program as null and skip initialization\n\t\tif (!ctx.variables[i].differentiable)\n\t\t{\n\t\t\tderiv_i.isNullProgram = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool success = true;\n\t\ttry {\n\n\t\t\tif (!deriv_i.isNullProgram && deriv_i.Init() == false)\n\t\t\t{\n\t\t\t\tfeLogErrorEx(m.fem, \"Failed to initialize derivative valuator for variable %d\", i);\n\t\t\t\tsuccess = false;\n\t\t\t}\n\t\t}\n\t\tcatch (std::runtime_error e)\n\t\t{\n\t\t\tfeLogErrorEx(m.fem, \"Runtime error while initializing derivative valuator for variable %d: %s\", i, e.what());\n\t\t\tsuccess = false;\n\t\t}\n\t\tcatch (...)\n\t\t{\n\t\t\tfeLogErrorEx(m.fem, \"Unknown error while initializing derivative valuator for variable %d\", i);\n\t\t\tsuccess = false;\n\t\t}\n\n#ifndef NDEBUG\n\t\tif (deriv_i.isNullProgram)\n\t\t{\n\t\t\tfeLogEx(m.fem, \"Derivative AST w.r.t %s is null (no dependency found)\\n\\n\", deriv_i.varName.c_str());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfeLogEx(m.fem, \"Derivative AST w.r.t %s :\\n>>>\\n\", deriv_i.varName.c_str());\n\t\t\tstd::stringstream ss;\n\t\t\tfebcode::prettyPrintAST(ss, *deriv_i.code.program.ast);\n\t\t\tfeLogEx(m.fem, \"%s\\n<<<\\n\\n\", ss.str().c_str());\n\t\t}\n#endif\n\t\tif (!success) return false;\n\t}\n\treturn true;\n}\n\ndouble FEScriptedBehavior::Value(double v)\n{\n\tthread_local std::vector<double> vars(1);\n\tvars[0] = v;\n\n\tstatic FEMaterialPoint dummy;\n\n\treturn Value(dummy, vars);\n}\n\nFEValue FEScriptedBehavior::Value(const FEMaterialPoint& mp)\n{\n\tthread_local std::vector<FEValue> vars(2);\n\tvars[0] = mp.m_r0; // pos0\n\tvars[1] = GetFEModel()->GetCurrentTime(); // time\n\treturn Value(mp, vars);\n}\n\nFEValue FEScriptedBehavior::Value(const FEMaterialPoint& mp, const std::vector<FEValue>& vars)\n{\n\treturn m.code.Run(mp, vars);\n}\n\ndouble FEScriptedBehavior::Value(const FEMaterialPoint& mp, const std::vector<double>& vars)\n{\n\treturn m.code.Run(mp, vars);\n}\n\nFEValue FEScriptedBehavior::DerivValue(const FEMaterialPoint& mp, const std::vector<FEValue>& vars, int varIndex)\n{\n\tif ((varIndex >= 0) && (varIndex < m.valDeriv.size()))\n\t{\n\t\tImp::Derive& deriv_i = m.valDeriv[varIndex];\n\t\tif (deriv_i.isNullProgram)\n\t\t{\n\t\t\t// this derivative was optimized out, so we return zero\n\t\t\tswitch (deriv_i.code.program.returnType->kind)\n\t\t\t{\n\t\t\tcase febcode::TypeKind::Double:\n\t\t\t{\n\t\t\t\tFEValue result;\n\t\t\t\tresult.type = FEValueType::Double;\n\t\t\t\tresult.d = 0.0;\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tcase febcode::TypeKind::Vec3:\n\t\t\t{\n\t\t\t\tFEValue result;\n\t\t\t\tresult.type = FEValueType::Vec3d;\n\t\t\t\tresult.v3 = vec3d(0.0, 0.0, 0.0);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tcase febcode::TypeKind::Mat3:\n\t\t\t{\n\t\t\t\tFEValue result;\n\t\t\t\tresult.type = FEValueType::Mat3d;\n\t\t\t\tresult.m3 = mat3dd(0.0);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tFEValue dr = deriv_i.code.Run(mp, vars);\n\t\treturn dr;\n\t}\n\treturn FEValue();\n}\n\ndouble FEScriptedBehavior::DerivValue(const FEMaterialPoint& mp, const std::vector<double>& vars, int varIndex)\n{\n\tif ((varIndex >= 0) && (varIndex < m.valDeriv.size()))\n\t{\n\t\tImp::Derive& deriv_i = m.valDeriv[varIndex];\n\t\tif (deriv_i.isNullProgram)\n\t\t{\n\t\t\t// this derivative was optimized out, so we return zero\n\t\t\treturn 0.0;\n\t\t}\n\n\t\tdouble dr = deriv_i.code.Run(mp, vars);\n\t\treturn dr;\n\t}\n\treturn 0.0;\n}\n\nbool ValidateScript(febcode::Program& program, const std::string& script, const ScriptContext& context, std::string& err)\n{\n\terr.clear();\n\n\tswitch (context.returnType)\n\t{\n\tcase FEValueType::Bool  : program.returnType = program.types.Bool(); break;\n\tcase FEValueType::Int   : program.returnType = program.types.Int(); break;\n\tcase FEValueType::Double: program.returnType = program.types.Double(); break;\n\tcase FEValueType::Vec3d : program.returnType = program.types.Vec3(); break;\n\tcase FEValueType::Mat3d : program.returnType = program.types.Mat3(); break;\n\tdefault:\n\t\terr = \"Invalid return type specified in script context\";\n\t\treturn false;\n\t}\n\n\ttry {\n\n\t\tstd::string varPrefix = \"_\";\n\t\tfor (const auto& var : context.variables)\n\t\t{\n\t\t\tswitch (var.type)\n\t\t\t{\n\t\t\tcase FEValueType::Bool  : program.injectGlobal(varPrefix + var.name, program.types.Bool()); break;\n\t\t\tcase FEValueType::Int   : program.injectGlobal(varPrefix + var.name, program.types.Int()); break;\n\t\t\tcase FEValueType::Double: program.injectGlobal(varPrefix + var.name, program.types.Double()); break;\n\t\t\tcase FEValueType::Vec3d : program.injectGlobal(varPrefix + var.name, program.types.Vec3()); break;\n\t\t\tcase FEValueType::Mat3d : program.injectGlobal(varPrefix + var.name, program.types.Mat3()); break;\n\t\t\tdefault:\n\t\t\t\terr = \"Unsupported variable type for variable \\\"\" + var.name + \"\\\" in script context\";\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfebcode::ParseSource(program, script);\n\n\t\tfebcode::Compiler compiler(program);\n\n\t\tcompiler.compile();\n\t}\n\tcatch (const std::exception& e)\n\t{\n\t\terr = e.what();\n\t\treturn false;\n\t}\n\tcatch (...)\n\t{\n\t\terr = \"Unknown error compiling code\";\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nbool ValidateScript(const std::string& script, const ScriptContext& context, std::string& err)\n{\n\tfebcode::Program program;\n\treturn ValidateScript(program, script, context, err);\n}\n\nFECORE_API std::vector<ScriptInputVariable> GetScriptInputVariables(const std::string& script, const ScriptContext& context, bool& ok)\n{\n\tstd::vector<ScriptInputVariable> inputs;\n\n\tfebcode::Program program;\n\tstd::string err;\n\tbool success = ValidateScript(program, script, context, /*out*/ err);\n\tok = success;\n\tif (success)\n\t{\n\t\tfor (auto& input : program.inputs)\n\t\t{\n\t\t\tFEValueType valueType;\n\t\t\tswitch (input.type->kind)\n\t\t\t{\n\t\t\tcase febcode::TypeKind::Bool  : valueType = FEValueType::Bool  ; break;\n\t\t\tcase febcode::TypeKind::Int   : valueType = FEValueType::Int   ; break;\n\t\t\tcase febcode::TypeKind::Double: valueType = FEValueType::Double; break;\n\t\t\tcase febcode::TypeKind::Vec3  : valueType = FEValueType::Vec3d ; break;\n\t\t\tcase febcode::TypeKind::Mat3  : valueType = FEValueType::Mat3d ; break;\n\t\t\tdefault:\n\t\t\t\tassert(false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tinputs.push_back({input.name, valueType});\n\t\t}\n\t}\n\treturn inputs;\n}\n"
  },
  {
    "path": "FECore/FEScriptedBehavior.h",
    "content": "#pragma once\n#include <vector>\n#include <string>\n#include \"fecore_api.h\"\n#include \"vec3d.h\"\n#include \"FECoreBase.h\"\n\nclass FEMaterialPoint;\nclass FEModel;\n\n// NOTE: Don't change the order of these values!\n// Also make sure this list matches the union in FEValue!\nenum class FEValueType\n{\n\tInvalid,\n\tBool,\n\tInt,\n\tDouble,\n\tVec2d,\n\tVec3d,\n\tMat2d,\n\tMat3d\n};\n\nstruct FEValue\n{\n\tFEValueType type = FEValueType::Invalid;\n\tunion {\n\t\tbool b;\n\t\tint i;\n\t\tdouble d;\n\t\tvec2d v2;\n\t\tvec3d v3;\n\t\tmat2d m2;\n\t\tmat3d m3;\n\t};\n\n\tFEValue() {}\n\n\tbool   toBool  () const { assert(type == FEValueType::Bool  ); return  b; }\n\tint    toInt   () const { assert(type == FEValueType::Int   ); return  i; }\n\tdouble toDouble() const { assert(type == FEValueType::Double); return  d; }\n\tvec2d  toVec2d () const { assert(type == FEValueType::Vec2d ); return v2; }\n\tvec3d  toVec3d () const { assert(type == FEValueType::Vec3d ); return v3; }\n\tmat2d  toMat2d () const { assert(type == FEValueType::Mat2d ); return m2; }\n\tmat3d  toMat3d () const { assert(type == FEValueType::Mat3d ); return m3; }\n\n\tFEValue(const FEValue& other)\n\t{\n\t\ttype = other.type;\n\t\tswitch (type)\n\t\t{\n\t\tcase FEValueType::Bool  : b = other.b; break;\n\t\tcase FEValueType::Int   : i = other.i; break;\n\t\tcase FEValueType::Double: d = other.d; break;\n\t\tcase FEValueType::Vec2d : v2 = other.v2; break;\n\t\tcase FEValueType::Vec3d : v3 = other.v3; break;\n\t\tcase FEValueType::Mat2d : m2 = other.m2; break;\n\t\tcase FEValueType::Mat3d : m3 = other.m3; break;\n\t\t}\n\t}\n\n\tFEValue& operator = (const FEValue& other)\n\t{\n\t\ttype = other.type;\n\t\tswitch (type)\n\t\t{\n\t\tcase FEValueType::Bool: b = other.b; break;\n\t\tcase FEValueType::Int: i = other.i; break;\n\t\tcase FEValueType::Double: d = other.d; break;\n\t\tcase FEValueType::Vec2d: v2 = other.v2; break;\n\t\tcase FEValueType::Vec3d: v3 = other.v3; break;\n\t\tcase FEValueType::Mat2d: m2 = other.m2; break;\n\t\tcase FEValueType::Mat3d: m3 = other.m3; break;\n\t\t}\n\t\treturn *this;\n\t}\n\n\tvoid operator = (bool v) { type = FEValueType::Bool; b = v; }\n\tvoid operator = (int v) { type = FEValueType::Int; i = v; }\n\tvoid operator = (double v) { type = FEValueType::Double; d = v; }\n\tvoid operator = (const vec2d& v) { type = FEValueType::Vec2d; v2 = v; }\n\tvoid operator = (const vec3d& v) { type = FEValueType::Vec3d; v3 = v; }\n\tvoid operator = (const mat2d& v) { type = FEValueType::Mat2d; m2 = v; }\n\tvoid operator = (const mat3d& v) { type = FEValueType::Mat3d; m3 = v; }\n};\n\nstruct FECORE_API ScriptContext\n{\n\tstruct Variable\n\t{\n\t\tstd::string name;\n\t\tFEValueType type;\n\t\tbool differentiable;\n\t};\n\n\tFEValueType returnType; // expected return type of the script\n\tstd::vector<Variable> variables; // list injected variables\n\n\tbool allowMappedInputs = true; // if true, input variables can be mapped. \n\tbool allowVolatileInputs = true; // if true, input variables can be assigned load controllers\n\n\tvoid addVariable(const std::string& name, FEValueType type, bool differentiable)\n\t{\n\t\tvariables.push_back({ name, type, differentiable });\n\t}\n\n\tbool operator == (const ScriptContext& other) const\n\t{\n\t\tif (returnType != other.returnType) return false;\n\t\tif (variables.size() != other.variables.size()) return false;\n\t\tfor (size_t i = 0; i < variables.size(); ++i)\n\t\t{\n\t\t\tif (variables[i].name != other.variables[i].name) return false;\n\t\t\tif (variables[i].type != other.variables[i].type) return false;\n\t\t\tif (variables[i].differentiable != other.variables[i].differentiable) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tbool operator != (const ScriptContext& other) const\n\t{\n\t\treturn !(*this == other);\n\t}\n};\n\nclass FECORE_API FEScriptedBehavior : public FECoreBase\n{\n\tclass Imp; // PIMPL for hiding implementation details\n\n\tFECORE_SUPER_CLASS(FESCRIPT_ID)\n\tFECORE_BASE_CLASS(FEScriptedBehavior)\n\npublic:\n\tFEScriptedBehavior(FEModel* fem);\n\tvirtual ~FEScriptedBehavior() {}\n\n\tvoid SetScriptName(const std::string& scriptName);\n\n\tvoid SetScriptContext(const ScriptContext& context);\n\n\tvoid SetDefaultContext(FEValueType returnType, FECoreBase* parent);\n\n\tScriptContext GetScriptContext() const;\n\n\tbool Init();\n\n\tbool HasDerivative(int id) const;\n\npublic:\n\tdouble Value(double v);\n\n\tFEValue Value(const FEMaterialPoint& mp);\n\n\tFEValue Value(const FEMaterialPoint& mp, const std::vector<FEValue>& vars);\n\tdouble Value(const FEMaterialPoint& mp, const std::vector<double>& vars);\n\n\tFEValue DerivValue(const FEMaterialPoint& mp, const std::vector<FEValue>& vars, int varIndex);\n\tdouble DerivValue(const FEMaterialPoint& mp, const std::vector<double>& vars, int varIndex);\n\nprivate:\n\tstd::string m_scriptName;\n\nprivate:\n\tImp& m;\n};\n\n// helper function to see if a script compiles. This is used in FEBio studio.\nFECORE_API bool ValidateScript(const std::string& script, const ScriptContext& context, std::string& err);\n\nstruct FECORE_API ScriptInputVariable\n{\n\tstd::string name;\n\tFEValueType type;\n};\n\nFECORE_API std::vector<ScriptInputVariable> GetScriptInputVariables(const std::string& script, const ScriptContext& context, bool& ok);\n\n// use this as a base class for scripted model components.\ntemplate <class Base> \nclass FEScripted : public Base\n{\npublic:\n\tFEScripted(FEModel* fem) : Base(fem), m_script(fem) {}\n\n\tvoid BuildParamList() override {\n\t\tBase::BuildParamList();\n\n\t\tADD_PROPERTY(m_script, \"script\");\n\t}\n\n\tvoid SetScriptContext(const ScriptContext& context)\n\t{\n\t\tm_script.SetScriptContext(context);\n\t}\n\n\tvoid SetDefaultContext(FEValueType returnType, FECoreBase* parent)\n\t{\n\t\tm_script.SetDefaultContext(returnType, parent);\n\t}\n\n\tdouble Value(double v)\n\t{\n\t\treturn m_script.Value(v);\n\t}\n\n\tFEValue Value(const FEMaterialPoint& mp)\n\t{\n\t\treturn m_script.Value(mp);\n\t}\n\n\tFEValue Value(const FEMaterialPoint& mp, const std::vector<FEValue>& vars)\n\t{\n\t\treturn m_script.Value(mp, vars);\n\t}\n\n\tdouble Value(const FEMaterialPoint& mp, const std::vector<double>& vars)\n\t{\n\t\treturn m_script.Value(mp, vars);\n\t}\n\n\tFEValue DerivValue(const FEMaterialPoint& mp, const std::vector<FEValue>& vars, int varIndex)\n\t{\n\t\treturn m_script.DerivValue(mp, vars, varIndex);\n\t}\n\n\tdouble DerivValue(const FEMaterialPoint& mp, const std::vector<double>& vars, int varIndex)\n\t{\n\t\treturn m_script.DerivValue(mp, vars, varIndex);\n\t}\n\n\tbool HasDerivative(int varIndex) const\n\t{\n\t\treturn m_script.HasDerivative(varIndex);\n\t}\n\nprivate:\n\tFEScriptedBehavior m_script;\n};\n"
  },
  {
    "path": "FECore/FEScriptedLoadController.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEScriptedLoadController.h\"\n#include <FECore/log.h>\n\nFEScriptedLoadController::FEScriptedLoadController(FEModel* fem) : FEScripted<FELoadController>(fem)\n{\n\tScriptContext context;\n\tcontext.returnType = FEValueType::Double;\n\tcontext.allowMappedInputs = false;\t// can't assign maps to input parameters\n\tcontext.allowVolatileInputs = false; // can't assign load controllers to input parameters\n\tcontext.addVariable(\"time\", FEValueType::Double, true);\n\tSetScriptContext(context);\n}\n\ndouble FEScriptedLoadController::GetValue(double time)\n{\n\treturn Value(time);\n}\n\nvoid FEScriptedLoadController::Serialize(DumpStream& ar)\n{\n\t// TODO: implement this\n}\n\nvoid FEScriptedLoadController::Reset()\n{\n\t// TODO: implement this\n}\n"
  },
  {
    "path": "FECore/FEScriptedLoadController.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2026 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FELoadController.h\"\n#include \"FEScriptedBehavior.h\"\n\nclass FEScriptedLoadController : public FEScripted<FELoadController>\n{\npublic:\n\tFEScriptedLoadController(FEModel* fem);\n\n\tvoid Serialize(DumpStream& ar) override;\n\n\tvoid Reset() override;\n\nprotected:\n\n\tdouble GetValue(double time) override;\n};\n"
  },
  {
    "path": "FECore/FESegmentSet.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESegmentSet.h\"\n#include \"DumpStream.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nvoid FESegmentSet::SEGMENT::Serialize(DumpStream& ar)\n{\n\tar & node;\n\tar & ntype;\n}\n\n//-----------------------------------------------------------------------------\nFESegmentSet::FESegmentSet(FEModel* fem) : FEItemList(fem, FEItemList::FE_ELEMENT_SET)\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FESegmentSet::Create(int n)\n{\n\tm_Seg.resize(n);\n}\n\n//-----------------------------------------------------------------------------\nFESegmentSet::SEGMENT& FESegmentSet::Segment(int i)\n{\n\treturn m_Seg[i];\n}\n\n//-----------------------------------------------------------------------------\nconst FESegmentSet::SEGMENT& FESegmentSet::Segment(int i) const\n{\n\treturn m_Seg[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESegmentSet::Serialize(DumpStream& ar)\n{\n\tFEItemList::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_Seg;\n}\n\nvoid FESegmentSet::SaveClass(DumpStream& ar, FESegmentSet* p)\n{\n\n}\n\nFESegmentSet* FESegmentSet::LoadClass(DumpStream& ar, FESegmentSet* p)\n{\n\tp = new FESegmentSet(&ar.GetFEModel());\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\nFENodeList FESegmentSet::GetNodeList() const\n{\n\tFEMesh* mesh = GetMesh();\n\tFENodeList set(mesh);\n\tvector<int> tag(mesh->Nodes(), 0);\n\tfor (int i = 0; i < Segments(); ++i)\n\t{\n\t\tconst SEGMENT& el = m_Seg[i];\n\t\tint ne = el.ntype;\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tif (tag[el.node[j]] == 0)\n\t\t\t{\n\t\t\t\tset.Add(el.node[j]);\n\t\t\t\ttag[el.node[j]] = 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn set;\n}\n"
  },
  {
    "path": "FECore/FESegmentSet.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include \"FEElement.h\"\n#include <vector>\n#include <string>\n#include \"FEItemList.h\"\n#include \"FENodeList.h\"\n\n//-----------------------------------------------------------------------------\n//! This class defines a set of segments. This can be used in the creation of edges.\nclass FECORE_API FESegmentSet : public FEItemList\n{\npublic:\n\tstruct SEGMENT\n\t{\n\t\tenum SegmentType {\n\t\t\tINVALID=0,\n\t\t\tLINE2 = 2,\n\t\t\tLINE3 = 3\n\t\t};\n\n\t\tint\tnode[3];\n\t\tint\tntype;\t//\t2=line2\n\n\t\tvoid Serialize(DumpStream& ar);\n\n\t\tSEGMENT() { ntype = SEGMENT::INVALID; }\n\t};\n\npublic:\n\t// constructor\n\tFESegmentSet(FEModel* fem);\n\n\t// allocate segments\n\tvoid Create(int n);\n\n\t// return nr of segments\n\tint Segments() const { return (int)m_Seg.size(); }\n\n\t// return a segment\n\tSEGMENT& Segment(int i);\n\tconst SEGMENT& Segment(int i) const;\n\n\tFENodeList GetNodeList() const;\n\npublic:\n\t// serialization\n\tvoid Serialize(DumpStream& ar);\n\n\tstatic void SaveClass(DumpStream& ar, FESegmentSet* p);\n\tstatic FESegmentSet* LoadClass(DumpStream& ar, FESegmentSet* p);\n\nprivate:\n\tvector<SEGMENT>\tm_Seg;\t// the actual segment list\n};\n"
  },
  {
    "path": "FECore/FEShellDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEShellDomain.h\"\n#include \"FEMesh.h\"\n#include \"FEMaterial.h\"\n\nBEGIN_FECORE_CLASS(FEShellDomain, FEDomain)\n\tADD_PROPERTY(m_matAxis, \"mat_axis\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nFEShellDomain::FEShellDomain(FEModel* fem) : FEDomain(FE_DOMAIN_SHELL, fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FEShellDomain::PreSolveUpdate(const FETimeInfo& timeInfo)\n{\n\tForEachMaterialPoint([&](FEMaterialPoint& mp) {\n\t\tmp.Update(timeInfo);\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FEShellDomain::Reset()\n{\n\tForEachShellElement([](FEShellElement& el) {\n\t\tint ni = el.GaussPoints();\n\t\tfor (int j = 0; j<ni; ++j) el.GetMaterialPoint(j)->Init();\n\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j<ne; ++j) el.m_ht[j] = el.m_h0[j];\n\t});\n}\n\n//-----------------------------------------------------------------------------\nbool FEShellDomain::InitShells()\n{\n\tForEachShellElement([](FEShellElement& el) {\n\t\tint n = el.Nodes();\n\t\tfor (int j = 0; j<n; ++j) el.m_ht[j] = el.m_h0[j];\n\t});\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! get the current nodal coordinates\nvoid FEShellDomain::GetCurrentNodalCoordinates(const FEShellElement& el, vec3d* rt, const bool back)\n{\n    int neln = el.Nodes();\n    if (!back)\n        for (int i = 0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n    else\n        for (int i = 0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).st();\n}\n\n//-----------------------------------------------------------------------------\n//! get the current nodal coordinates\nvoid FEShellDomain::GetCurrentNodalCoordinates(const FEShellElement& el, vec3d* rt, double alpha, const bool back)\n{\n    int neln = el.Nodes();\n    if (!back) {\n        for (int i = 0; i<neln; ++i) {\n            FENode& nd = m_pMesh->Node(el.m_node[i]);\n            rt[i] = nd.m_rt*alpha + nd.m_rp*(1 - alpha);\n        }\n    }\n    else {\n        for (int i = 0; i<neln; ++i) {\n            FENode& nd = m_pMesh->Node(el.m_node[i]);\n            rt[i] = nd.st()*alpha + nd.sp()*(1 - alpha);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! get the reference nodal coordinates\nvoid FEShellDomain::GetReferenceNodalCoordinates(const FEShellElement& el, vec3d* r0, const bool back)\n{\n    int neln = el.Nodes();\n    if (!back)\n        for (int i = 0; i<neln; ++i) r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    else\n        for (int i = 0; i<neln; ++i) r0[i] = m_pMesh->Node(el.m_node[i]).s0();\n}\n\n//-----------------------------------------------------------------------------\n//! get the previous nodal coordinates\nvoid FEShellDomain::GetPreviousNodalCoordinates(const FEShellElement& el, vec3d* rp, const bool back)\n{\n    int neln = el.Nodes();\n    if (!back)\n        for (int i = 0; i<neln; ++i) rp[i] = m_pMesh->Node(el.m_node[i]).m_rp;\n    else\n        for (int i = 0; i<neln; ++i) rp[i] = m_pMesh->Node(el.m_node[i]).sp();\n}\n\n//-----------------------------------------------------------------------------\nvoid FEShellDomain::ForEachShellElement(std::function<void(FEShellElement& el)> f)\n{\n\tint NE = Elements();\n\tfor (int i = 0; i < NE; ++i) f(Element(i));\n}\n\n//=================================================================================================\n\nFEShellDomainOld::FEShellDomainOld(FEModel* fem) : FEShellDomain(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FEShellDomainOld::Create(int nelems, FE_Element_Spec espec)\n{\n\tm_Elem.resize(nelems);\n\tfor (int i = 0; i < nelems; ++i)\n\t{\n\t\tFEShellElementOld& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (espec.etype != FE_ELEM_INVALID_TYPE)\n\t\tfor (int i=0; i<nelems; ++i) m_Elem[i].SetType(espec.etype);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ndouble FEShellDomainOld::Volume(FEShellElement& se)\n{\n\tFEShellElementOld& el = static_cast<FEShellElementOld&>(se);\n\n\tint neln = el.Nodes();\n\n\t// initial nodal coordinates and directors\n\tvec3d r0[FEElement::MAX_NODES], D0[FEElement::MAX_NODES];\n\tfor (int i = 0; i<neln; ++i)\n\t{\n\t\tr0[i] = Node(el.m_lnode[i]).m_r0;\n\t\tD0[i] = el.m_D0[i];\n\t}\n\n\tint nint = el.GaussPoints();\n\tdouble *w = el.GaussWeights();\n\tdouble V = 0;\n\tvec3d g[3];\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\t// jacobian matrix\n\t\tdouble eta = el.gt(n);\n\n\t\tdouble* Mr = el.Hr(n);\n\t\tdouble* Ms = el.Hs(n);\n\t\tdouble* M = el.H(n);\n\n\t\t// evaluate covariant basis vectors\n\t\tg[0] = g[1] = g[2] = vec3d(0, 0, 0);\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tg[0] += (r0[i] + D0[i] * eta / 2)*Mr[i];\n\t\t\tg[1] += (r0[i] + D0[i] * eta / 2)*Ms[i];\n\t\t\tg[2] += D0[i] * (M[i] / 2);\n\t\t}\n\n\t\tmat3d J = mat3d(g[0].x, g[1].x, g[2].x,\n\t\t\tg[0].y, g[1].y, g[2].y,\n\t\t\tg[0].z, g[1].z, g[2].z);\n\n\t\t// calculate the determinant\n\t\tdouble detJ0 = J.det();\n\n\t\tV += detJ0*w[n];\n\t}\n\n\treturn V;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate all shell normals (i.e. the shell directors).\n//! And find shell nodes\nbool FEShellDomainOld::InitShells()\n{\n\tif (!FEShellDomain::InitShells()) return false;\n\n\tFEMesh& mesh = *GetMesh();\n\tfor (int i = 0; i<Elements(); ++i)\n\t{\n\t\tFEShellElementOld& el = ShellElement(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j<ne; ++j)\n\t\t{\n\t\t\tvec3d d0 = mesh.Node(el.m_node[j]).m_d0;\n\t\t\td0.unit();\n\t\t\tel.m_D0[j] = d0 * el.m_h0[j];\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//=================================================================================================\n\nBEGIN_FECORE_CLASS(FEShellDomainNew, FEShellDomain)\n\tADD_PARAMETER(m_h0, \"shell_thickness\");\nEND_FECORE_CLASS();\n\nFEShellDomainNew::FEShellDomainNew(FEModel* fem) : FEShellDomain(fem)\n{\n\tm_h0 = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool FEShellDomainNew::Create(int nelems, FE_Element_Spec espec)\n{\n\tm_Elem.resize(nelems);\n\tfor (int i = 0; i < nelems; ++i)\n\t{\n\t\tFEShellElementNew& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (espec.etype != FE_ELEM_INVALID_TYPE)\n\t\tfor (int i = 0; i<nelems; ++i) m_Elem[i].SetType(espec.etype);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEShellDomainNew::AssignDefaultShellThickness()\n{\n\tdouble h0 = DefaultShellThickness();\n\tif (h0 <= 0.0) return;\n\n\tfor (int j = 0; j < Elements(); ++j)\n\t{\n\t\tFEShellElement& el = Element(j);\n\t\tint ne = el.Nodes();\n\t\tfor (int n = 0; n < ne; ++n) el.m_ht[n] = el.m_h0[n] = h0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FEShellDomainNew::Volume(FEShellElement& se)\n{\n\tFEShellElementNew& el = static_cast<FEShellElementNew&>(se);\n\n\tint neln = el.Nodes();\n\n\t// initial nodal coordinates and directors\n\tvec3d r0[FEElement::MAX_NODES], D0[FEElement::MAX_NODES];\n\tfor (int i = 0; i<neln; ++i)\n\t{\n\t\tr0[i] = Node(el.m_lnode[i]).m_r0;\n\t\tD0[i] = Node(el.m_lnode[i]).m_d0;\n\t}\n\n\tint nint = el.GaussPoints();\n\tdouble *w = el.GaussWeights();\n\tdouble V = 0;\n\tvec3d g[3];\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\t// jacobian matrix\n\t\tdouble eta = el.gt(n);\n\n\t\tdouble* Mr = el.Hr(n);\n\t\tdouble* Ms = el.Hs(n);\n\t\tdouble* M = el.H(n);\n\n\t\t// evaluate covariant basis vectors\n\t\tg[0] = g[1] = g[2] = vec3d(0, 0, 0);\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tg[0] += (r0[i] + D0[i] * eta / 2)*Mr[i];\n\t\t\tg[1] += (r0[i] + D0[i] * eta / 2)*Ms[i];\n\t\t\tg[2] += D0[i] * (M[i] / 2);\n\t\t}\n\n\t\tmat3d J = mat3d(g[0].x, g[1].x, g[2].x,\n\t\t\tg[0].y, g[1].y, g[2].y,\n\t\t\tg[0].z, g[1].z, g[2].z);\n\n\t\t// calculate the determinant\n\t\tdouble detJ0 = J.det();\n\n\t\tV += detJ0*w[n];\n\t}\n\n\treturn V;\n}\n\n\n"
  },
  {
    "path": "FECore/FEShellDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDomain.h\"\n#include \"FEShellElement.h\"\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for shell element domains\nclass FECORE_API FEShellDomain : public FEDomain\n{\n\tFECORE_SUPER_CLASS(FESHELLDOMAIN_ID)\n\tFECORE_BASE_CLASS(FEShellDomain)\n\npublic:\n\t//! constructor\n\tFEShellDomain(FEModel* fem);\n\n\t//! Update element data prior to solving time step\n\tvoid PreSolveUpdate(const FETimeInfo& timeInfo);\n\n\t//! Reset element data\n\tvoid Reset();\n\n\t// get a shell element\n\tvirtual FEShellElement& Element(int i) = 0;\n\n\t// get the element type (TODO: Move to FEDomain class?)\n\tint GetElementType() { return ElementRef(0).Type(); };\n\npublic:\n\t// evaluate volume of element in reference frame\n\tvirtual double Volume(FEShellElement& el) { return 0.0; }\n\n\t// evaluate volume of element in current frame\n\tvirtual double CurrentVolume(FEShellElement& el) { return 0.0; }\n\n\t// Initialize shell data (Called from FEMesh::InitShells)\n\tvirtual bool InitShells();\n\n\tvirtual void AssignDefaultShellThickness() {}\n\npublic:\n    //! get the current nodal coordinates\n    void GetCurrentNodalCoordinates(const FEShellElement& el, vec3d* rt, const bool back = false);\n    void GetCurrentNodalCoordinates(const FEShellElement& el, vec3d* rt, double alpha, const bool back = false);\n    \n    //! get the reference nodal coordinates\n    void GetReferenceNodalCoordinates(const FEShellElement& el, vec3d* r0, const bool back = false);\n    \n    //! get the nodal coordinates at previous state\n    void GetPreviousNodalCoordinates(const FEShellElement& el, vec3d* rp, const bool back = false);\n    \npublic:\n\tvoid ForEachShellElement(std::function<void(FEShellElement& el)> f);\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//-----------------------------------------------------------------------------\n// Old director-based shell formulation\nclass FECORE_API FEShellDomainOld : public FEShellDomain\n{\npublic:\n\tFEShellDomainOld(FEModel* fem);\n\n\t//! create storage for elements\n\tbool Create(int nsize, FE_Element_Spec espec) override;\n\npublic:\n\t//! return nr of elements\n\tint Elements() const override { return (int)m_Elem.size(); }\n\n\t//! element access\n\tFEShellElement& Element(int n) override { return m_Elem[n]; }\n\tFEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\n\tFEShellElementOld& ShellElement(int i) { return m_Elem[i]; }\n\n\tdouble Volume(FEShellElement& el) override;\n\n\tbool InitShells() override;\n\nprotected:\n\tvector<FEShellElementOld>\tm_Elem;\t//!< array of elements\n};\n\n//-----------------------------------------------------------------------------\n// New shell formulation\nclass FECORE_API FEShellDomainNew : public FEShellDomain\n{\npublic:\n\tFEShellDomainNew(FEModel* fem);\n\n\t//! create storage for elements\n\tbool Create(int nsize, FE_Element_Spec espec) override;\n\npublic:\n\t//! return nr of elements\n\tint Elements() const override { return (int)m_Elem.size(); }\n\n\t//! element access\n\tFEShellElement& Element(int n) override { return m_Elem[n]; }\n\tFEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\n\tFEShellElementNew& ShellElement(int i) { return m_Elem[i]; }\n\n\tdouble Volume(FEShellElement& el) override;\n\n\tdouble DefaultShellThickness() const { return m_h0; }\n\n\tvoid AssignDefaultShellThickness() override;\n\nprotected:\n\tdouble\tm_h0;\n\nprotected:\n\tvector<FEShellElementNew>\tm_Elem;\t//!< array of elements\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEShellElement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEShellElement.h\"\n#include \"DumpStream.h\"\nusing namespace std;\n\n//=================================================================================================\n// FEShellElement\n//=================================================================================================\n\nFEShellElement::FEShellElement()\n{\n\tm_elem[0] = m_elem[1] = -1;\n}\n\nFEShellElement::FEShellElement(const FEShellElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\tm_status = el.m_status;\n\n\t// copy shell data\n\tm_h0 = el.m_h0;\n\tm_ht = el.m_ht;\n\tm_d0 = el.m_d0;\n\tm_g0[0] = el.m_g0[0]; m_g0[1] = el.m_g0[1]; m_g0[2] = el.m_g0[2];\n\tm_gt[0] = el.m_gt[0]; m_gt[1] = el.m_gt[1]; m_gt[2] = el.m_gt[2];\n\tm_gp[0] = el.m_gp[0]; m_gp[1] = el.m_gp[1]; m_gp[2] = el.m_gp[2];\n\n\tm_G0[0] = el.m_G0[0]; m_G0[1] = el.m_G0[1]; m_G0[2] = el.m_G0[2];\n\tm_Gt[0] = el.m_Gt[0]; m_Gt[1] = el.m_Gt[1]; m_Gt[2] = el.m_Gt[2];\n\n\tm_elem[0] = el.m_elem[0];\n\tm_elem[1] = el.m_elem[1];\n}\n\n//! assignment operator\nFEShellElement& FEShellElement::operator = (const FEShellElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\tm_status = el.m_status;\n\n\t// copy shell data\n\tm_h0 = el.m_h0;\n\tm_ht = el.m_ht;\n\tm_d0 = el.m_d0;\n\tm_g0[0] = el.m_g0[0]; m_g0[1] = el.m_g0[1]; m_g0[2] = el.m_g0[2];\n\tm_gt[0] = el.m_gt[0]; m_gt[1] = el.m_gt[1]; m_gt[2] = el.m_gt[2];\n\tm_gp[0] = el.m_gp[0]; m_gp[1] = el.m_gp[1]; m_gp[2] = el.m_gp[2];\n\n\tm_G0[0] = el.m_G0[0]; m_G0[1] = el.m_G0[1]; m_G0[2] = el.m_G0[2];\n\tm_Gt[0] = el.m_Gt[0]; m_Gt[1] = el.m_Gt[1]; m_Gt[2] = el.m_Gt[2];\n\n\tm_elem[0] = el.m_elem[0];\n\tm_elem[1] = el.m_elem[1];\n\n\treturn (*this);\n}\n\nvoid FEShellElement::SetTraits(FEElementTraits* ptraits)\n{\n\tFEElement::SetTraits(ptraits);\n\tm_h0.assign(Nodes(), 0.0);\n\tm_ht.assign(Nodes(), 0.0);\n\tm_d0.assign(Nodes(), vec3d(0, 0, 0));\n\tm_g0[0].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_g0[1].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_g0[2].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_gt[0].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_gt[1].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_gt[2].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_gp[0].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_gp[1].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_gp[2].assign(GaussPoints(), vec3d(0, 0, 0));\n\n\tm_G0[0].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_G0[1].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_G0[2].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_Gt[0].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_Gt[1].assign(GaussPoints(), vec3d(0, 0, 0));\n\tm_Gt[2].assign(GaussPoints(), vec3d(0, 0, 0));\n}\n\nvoid FEShellElement::Serialize(DumpStream &ar)\n{\n\tFEElement::Serialize(ar);\n\tif (ar.IsShallow() == false) {\n\t\tar & m_h0;\n\t\tar & m_d0;\n\t\tar & m_g0[0] & m_g0[1] & m_g0[2];\n\t\tar & m_gt[0] & m_gt[1] & m_gt[2];\n\t\tar & m_gp[0] & m_gp[1] & m_gp[2];\n\t\tar & m_G0[0] & m_G0[1] & m_G0[2];\n\t\tar & m_Gt[0] & m_Gt[1] & m_Gt[2];\n\t\tar & m_elem[0] & m_elem[1];\n\t}\n\tar & m_ht;\n}\n\n//=================================================================================================\n// FEShellElementOld\n//=================================================================================================\nFEShellElementOld::FEShellElementOld()\n{\n}\n\nFEShellElementOld::FEShellElementOld(const FEShellElementOld& el) : FEShellElement(el)\n{\n\tm_D0 = el.m_D0;\n}\n\n//! assignment operator\nFEShellElementOld& FEShellElementOld::operator = (const FEShellElementOld& el)\n{\n\t// copy base class\n\tFEShellElement::operator=(el);\n\n\t// copy this class data\n\tm_D0 = el.m_D0;\n\n\treturn (*this);\n}\n\nvoid FEShellElementOld::SetTraits(FEElementTraits* ptraits)\n{\n\tFEShellElement::SetTraits(ptraits);\n\tm_D0.resize(Nodes());\n}\n\nvoid FEShellElementOld::Serialize(DumpStream& ar)\n{\n\tFEShellElement::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_D0;\n}\n\n//=================================================================================================\n// FEShellElementNew\n//=================================================================================================\n\nFEShellElementNew::FEShellElementNew()\n{\n\n}\n\nFEShellElementNew::FEShellElementNew(const FEShellElementNew& el) : FEShellElement(el)\n{\n\t// TODO: What about all the EAS parameters?\n}\n\n//! assignment operator\nFEShellElementNew& FEShellElementNew::operator = (const FEShellElementNew& el)\n{\n\tFEShellElement::operator=(el);\n\n\t// TODO: What about all the EAS parameters?\n\n\treturn (*this);\n}\n\nvoid FEShellElementNew::SetTraits(FEElementTraits* ptraits)\n{\n\tFEShellElement::SetTraits(ptraits);\n\n\t// TODO: What about all the EAS parameters?\n}\n\nvoid FEShellElementNew::Serialize(DumpStream &ar)\n{\n\tFEShellElement::Serialize(ar);\n\tar & m_fa;\n\tar & m_Kaai;\n\tar & m_alpha;\n\tar & m_alphai;\n\tar & m_alphat;\n\tar & m_Kua;\n\tar & m_Kwa;\n\tar & m_E;\n}\n"
  },
  {
    "path": "FECore/FEShellElement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElement.h\"\n\n\n//-----------------------------------------------------------------------------\n//!  This class defines the shell element. \n\n//! A shell element is similar to a surface\n//! element except that it has a thickness. \n\nclass FECORE_API FEShellElement : public FEElement\n{\npublic:\n\tFEShellElement();\n\n\t//! copy constructor\n\tFEShellElement(const FEShellElement& el);\n\n\t//! assignment operator\n\tFEShellElement& operator = (const FEShellElement& el);\n\n\tvirtual void SetTraits(FEElementTraits* ptraits) override;\n\n\tdouble gr(int n) { return ((FEShellElementTraits*)(m_pT))->gr[n]; }\n\tdouble gs(int n) { return ((FEShellElementTraits*)(m_pT))->gs[n]; }\n\tdouble gt(int n) { return ((FEShellElementTraits*)(m_pT))->gt[n]; }\n\n\tdouble* GaussWeights() { return &((FEShellElementTraits*)(m_pT))->gw[0]; }\t// weights of integration points\n\n\tdouble* Hr(int n) { return ((FEShellElementTraits*)(m_pT))->Hr[n]; }\t// shape function derivative to r\n\tdouble* Hs(int n) { return ((FEShellElementTraits*)(m_pT))->Hs[n]; }\t// shape function derivative to s\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) const { ((FEShellElementTraits*)(m_pT))->shape_fnc(H, r, s); }\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) const { ((FEShellElementTraits*)(m_pT))->shape_deriv(Hr, Hs, r, s); }\n\n\t//! serialize data associated with this element\n\tvoid Serialize(DumpStream &ar) override;\n\npublic:\n\tstd::vector<double>\tm_h0;\t//!< initial shell thicknesses\n\tstd::vector<double>\tm_ht;\t//!< current shell thickness\n\tstd::vector<vec3d>\tm_d0;   //!< initial shell director\n\n\tstd::vector<vec3d>\tm_g0[3];//!< reference covariant base vectors\n\tstd::vector<vec3d>\tm_gt[3];//!< current covariant base vectors\n\tstd::vector<vec3d>\tm_gp[3];//!< previous covariant base vectors\n\n\tstd::vector<vec3d>\tm_G0[3];//!< reference contravariant base vectors\n\tstd::vector<vec3d>\tm_Gt[3];//!< current contravariant base vectors\n\n\t// indices of solid elements this shell element is attached to.\n\t// the first element is attached to the back of the shell\n\t// and the second element is attached to the front.\n\t// the index is -1 if no solid is attached on that side.\n\tint        m_elem[2];\n};\n\n//-----------------------------------------------------------------------------\n// Shell element used by old shell formulation\nclass FECORE_API FEShellElementOld : public FEShellElement\n{\npublic:\n\tFEShellElementOld();\n\n\t//! copy constructor\n\tFEShellElementOld(const FEShellElementOld& el);\n\n\t//! assignment operator\n\tFEShellElementOld& operator = (const FEShellElementOld& el);\n\n\t// set the element traits class\n\tvoid SetTraits(FEElementTraits* ptraits) override;\n\n\t//! serialize data associated with this element\n\tvoid Serialize(DumpStream &ar) override;\n\npublic:\n\tstd::vector<vec3d>\tm_D0;\t//!< initial shell directors\n};\n\n//-----------------------------------------------------------------------------\n// Shell element used by new shell formulations\nclass FECORE_API FEShellElementNew : public FEShellElement\n{\npublic:\n\tFEShellElementNew();\n\n\t//! copy constructor\n\tFEShellElementNew(const FEShellElementNew& el);\n\n\t//! assignment operator\n\tFEShellElementNew& operator = (const FEShellElementNew& el);\n\n\t// set the element traits class\n\tvoid SetTraits(FEElementTraits* ptraits) override;\n\n\t//! serialize data associated with this element\n\tvoid Serialize(DumpStream &ar) override;\n\npublic: // EAS parameters\n\n\tmatrix          m_Kaai;\n\tmatrix          m_fa;\n\tmatrix          m_alpha;\n\tmatrix          m_alphat;\n\tmatrix          m_alphai;\n\tstd::vector<matrix>  m_Kua;\n\tstd::vector<matrix>  m_Kwa;\n\tstd::vector<mat3ds>  m_E;\n};\n\n"
  },
  {
    "path": "FECore/FESolidDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolidDomain.h\"\n#include \"FEMaterial.h\"\n#include \"tools.h\"\n#include \"log.h\"\n#include \"FEModel.h\"\n\nBEGIN_FECORE_CLASS(FESolidDomain, FEDomain)\n\tADD_PROPERTY(m_matAxis, \"mat_axis\", FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESolidDomain::FESolidDomain(FEModel* pfem) : FEDomain(FE_DOMAIN_SOLID, pfem), m_dofU(pfem), m_dofSU(pfem)\n{\n\tif (pfem)\n\t{\n\t\tm_dofU.AddDof(pfem->GetDOFIndex(\"x\"));\n\t\tm_dofU.AddDof(pfem->GetDOFIndex(\"y\"));\n\t\tm_dofU.AddDof(pfem->GetDOFIndex(\"z\"));\n\t\tm_dofSU.AddDof(pfem->GetDOFIndex(\"sx\"));\n\t\tm_dofSU.AddDof(pfem->GetDOFIndex(\"sy\"));\n\t\tm_dofSU.AddDof(pfem->GetDOFIndex(\"sz\"));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nbool FESolidDomain::Create(int nsize, FE_Element_Spec espec)\n{\n\t// allocate elements\n    m_Elem.resize(nsize);\n\tfor (int i = 0; i < nsize; ++i)\n\t{\n\t\tFESolidElement& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\t// set element type\n\tif (espec.etype != FE_ELEM_INVALID_TYPE)\n\t\tForEachElement([=](FEElement& el) { el.SetType(espec.etype); });\n\n\tm_elemSpec = espec;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nFE_Element_Spec FESolidDomain::GetElementSpec() const\n{\n\treturn m_elemSpec;\n}\n\n//-----------------------------------------------------------------------------\n//! return nr of elements\nint FESolidDomain::Elements() const { return (int)m_Elem.size(); }\n\n//-----------------------------------------------------------------------------\n//! loop over elements\nvoid FESolidDomain::ForEachSolidElement(std::function<void(FESolidElement& el)> f, bool runInParallel)\n{\n\tint NE = Elements();\n\tif (runInParallel)\n\t{\n#pragma omp parallel for\n\t\tfor (int i = 0; i < NE; ++i) \n\t\t\tf(m_Elem[i]);\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < NE; ++i) f(m_Elem[i]);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFESolidElement& FESolidDomain::Element(int n) { return m_Elem[n]; }\n\n//-----------------------------------------------------------------------------\nvoid FESolidDomain::CopyFrom(FEMeshPartition* pd)\n{\n\tFEDomain::CopyFrom(pd);\n\tFESolidDomain* psd = dynamic_cast<FESolidDomain*>(pd);\n    m_Elem = psd->m_Elem;\n\tForEachElement([=](FEElement& el) { el.SetMeshPartition(this); });\n}\n\n//-----------------------------------------------------------------------------\n//! initialize element data\nbool FESolidDomain::Init()\n{\n\t// base class first\n\tif (FEDomain::Init() == false) return false;\n\n\t// init solid element data\n\t// TODO: In principle I could parallelize this, but right now this cannot be done\n\t//       because of the try block. \n\ttry {\n\t\tForEachSolidElement([=](FESolidElement& el) {\n\n\t\t\t// evaluate nodal coordinates\n\t\t\tconst int NELN = FEElement::MAX_NODES;\n\t\t\tvec3d r0[NELN], r[NELN], v[NELN], a[NELN];\n\t\t\tint neln = el.Nodes();\n\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = m_pMesh->Node(el.m_node[j]);\n\t\t\t\tr0[j] = node.m_r0;\n\t\t\t}\n\n\t\t\t// initialize reference Jacobians\n\t\t\tdouble Ji[3][3];\n\n\t\t\t// loop over the integration points\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t\t// initiali Jacobian\n\t\t\t\tmp.m_J0 = invjac0(el, Ji, n);\n\t\t\t\tel.m_J0i[n] = mat3d(Ji);\n\n\t\t\t\t// material point coordinates\n\t\t\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\t\t}\n\t\t}, false);\n\t}\n\tcatch (NegativeJacobian e)\n\t{\n\t\tfeLogError(\"Negative jacobian detected during domain initialization\\nDomain: %s\\nElement %d, vol = %lg\\n\", GetName().c_str(), e.m_iel, e.m_vol);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Reset data\nvoid FESolidDomain::Reset()\n{\n\t// re-evaluate the material points initial position and jacobian\n\tForEachSolidElement([=](FESolidElement& el) {\n\n\t\t// evaluate nodal coordinates\n\t\tconst int NELN = FEElement::MAX_NODES;\n\t\tvec3d r0[NELN], r[NELN], v[NELN], a[NELN];\n\t\tint neln = el.Nodes();\n\t\tfor (int j = 0; j < neln; ++j)\n\t\t{\n\t\t\tFENode& node = m_pMesh->Node(el.m_node[j]);\n\t\t\tr0[j] = node.m_r0;\n\t\t}\n\n\t\t// initialize reference Jacobians\n\t\tdouble Ji[3][3];\n\n\t\t// loop over the integration points\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t// initial Jacobian\n\t\t\tmp.m_J0 = invjac0(el, Ji, n);\n\t\t\tel.m_J0i[n] = mat3d(Ji);\n\n\t\t\t// material point coordinates\n\t\t\tmp.m_r0 = el.Evaluate(r0, n);\n\t\t}\n\t}, false); // don't run in parallel since this may throw exceptions!\n\n\tForEachMaterialPoint([](FEMaterialPoint& mp) {\n\t\tmp.Init();\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! This function finds the element in which point y lies and returns\n//! the isoparametric coordinates in r if an element is found\n//! (This has only been implemeneted for hexes!)\nFESolidElement* FESolidDomain::FindElement(const vec3d& y, double r[3])\n{\n    int i, j;\n    int NE = Elements();\n    vec3d x[FEElement::MAX_NODES];\n    for (i=0; i<NE; ++i)\n    {\n        // get the next element\n        FESolidElement& e = Element(i);\n        assert(e.Type() == FE_HEX8G8);\n        \n        // get the element nodal coordinates\n        int neln = e.Nodes();\n        for (j=0; j<neln; ++j) x[j] = m_pMesh->Node(e.m_node[j]).m_rt;\n        \n        // first, as a quick check, we see if y lies in the bounding box defined by x\n        FEBoundingBox box(x[0]);\n        for (j=1; j<neln; ++j) box.add(x[j]);\n        \n        if (box.IsInside(y))\n        {\n\t\t\t// If the point y lies inside the box, we apply a Newton method to find\n\t\t\t// the isoparametric coordinates r\n\t\t\tProjectToElement(e, y, r);\n            \n            // see if the point r lies inside the element\n            const double eps = 1.0001;\n            if ((r[0] >= -eps) && (r[0] <= eps) &&\n                (r[1] >= -eps) && (r[1] <= eps) &&\n                (r[2] >= -eps) && (r[2] <= eps)) return &e;\n        }\n    }\n    return 0;\n}\n\n\n//-----------------------------------------------------------------------------\n//! This function finds the element in which point y lies and returns\n//! the isoparametric coordinates in r if an element is found\nFESolidElement* FESolidDomain::FindReferenceElement(const vec3d& y, double r[3])\n{\n\tint NE = Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESolidElement& e = Element(i);\n\n\t\t// get the element nodal coordinates\n\t\tint neln = e.Nodes();\n\t\tfor (int j = 0; j<neln; ++j) x[j] = m_pMesh->Node(e.m_node[j]).m_r0;\n\n\t\t// first, as a quick check, we see if y lies in the bounding box defined by x\n\t\tFEBoundingBox box(x[0]);\n\t\tfor (int j = 1; j<neln; ++j) box.add(x[j]);\n\n\t\tif (box.IsInside(y))\n\t\t{\n\t\t\t// If the point y lies inside the box, we apply a Newton method to find\n\t\t\t// the isoparametric coordinates r\n\t\t\tif (ProjectToReferenceElement(e, y, r)) return &e;\n\t\t}\n\t}\n\treturn 0;\n}\n\n\n//-----------------------------------------------------------------------------\nvoid FESolidDomain::ProjectToElement(FESolidElement& el, const vec3d& p, double r[3])\n{\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d rt[MN];\n\n\t// get the element nodal coordinates\n\tint ne = el.Nodes();\n\tfor (int i = 0; i<ne; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n\n    r[0] = r[1] = r[2] = 0;\n    const double tol = 1e-5;\n    double dr[3], norm;\n\tdouble H[MN], Gr[MN], Gs[MN], Gt[MN];\n    do\n    {\n\t\t// evaluate shape functions\n\t\tel.shape_fnc(H, r[0], r[1], r[2]);\n                \n\t\t// evaluate shape function derivatives\n\t\tel.shape_deriv(Gr, Gs, Gt, r[0], r[1], r[2]);\n                \n\t\t// solve for coordinate increment\n        double R[3] = {0}, A[3][3] = {0};\n        for (int i=0; i<ne; ++i)\n        {\n            R[0] += rt[i].x*H[i];\n            R[1] += rt[i].y*H[i];\n            R[2] += rt[i].z*H[i];\n                    \n            A[0][0] -= rt[i].x*Gr[i]; A[0][1] -= rt[i].x*Gs[i]; A[0][2] -= rt[i].x*Gt[i];\n            A[1][0] -= rt[i].y*Gr[i]; A[1][1] -= rt[i].y*Gs[i]; A[1][2] -= rt[i].y*Gt[i];\n            A[2][0] -= rt[i].z*Gr[i]; A[2][1] -= rt[i].z*Gs[i]; A[2][2] -= rt[i].z*Gt[i];\n        }\n        R[0] = p.x - R[0];\n        R[1] = p.y - R[1];\n        R[2] = p.z - R[2];\n                \n        solve_3x3(A, R, dr);\n        r[0] -= dr[0];\n        r[1] -= dr[1];\n        r[2] -= dr[2];\n                \n        norm = dr[0]*dr[0] + dr[1]*dr[1] + dr[2]*dr[2];\n    }\n    while (norm > tol);\t\n}\n\n//-----------------------------------------------------------------------------\nbool FESolidDomain::ProjectToReferenceElement(FESolidElement& el, const vec3d& p, double r[3])\n{\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d rt[MN];\n\n\t// get the element nodal coordinates\n\tint ne = el.Nodes();\n\tfor (int i = 0; i<ne; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\n    r[0] = r[1] = r[2] = 0;\n    const double tol = 1e-5;\n    double dr[3], norm;\n\tdouble H[MN], Gr[MN], Gs[MN], Gt[MN];\n    int ncount = 0;\n    do\n    {\n\t\t// evaluate shape functions\n\t\tel.shape_fnc(H, r[0], r[1], r[2]);\n                \n\t\t// evaluate shape function derivatives\n\t\tel.shape_deriv(Gr, Gs, Gt, r[0], r[1], r[2]);\n                \n\t\t// solve for coordinate increment\n        double R[3] = {0}, A[3][3] = {0};\n        for (int i=0; i<ne; ++i)\n        {\n            R[0] += rt[i].x*H[i];\n            R[1] += rt[i].y*H[i];\n            R[2] += rt[i].z*H[i];\n                    \n            A[0][0] -= rt[i].x*Gr[i]; A[0][1] -= rt[i].x*Gs[i]; A[0][2] -= rt[i].x*Gt[i];\n            A[1][0] -= rt[i].y*Gr[i]; A[1][1] -= rt[i].y*Gs[i]; A[1][2] -= rt[i].y*Gt[i];\n            A[2][0] -= rt[i].z*Gr[i]; A[2][1] -= rt[i].z*Gs[i]; A[2][2] -= rt[i].z*Gt[i];\n        }\n        R[0] = p.x - R[0];\n        R[1] = p.y - R[1];\n        R[2] = p.z - R[2];\n                \n        solve_3x3(A, R, dr);\n        r[0] -= dr[0];\n        r[1] -= dr[1];\n        r[2] -= dr[2];\n        \n        if (ncount++ > 100) return false;\n                \n        norm = dr[0]*dr[0] + dr[1]*dr[1] + dr[2]*dr[2];\n    }\n    while (norm > tol);\n\n\t// check if point is inside element\n\tif ((el.Shape() == ET_HEX8) || (el.Shape() == ET_HEX20) || (el.Shape() == ET_HEX27))\n\t{\n\t\tconst double eps = 1.0001;\n\t\tif ((r[0] >= -eps) && (r[0] <= eps) &&\n\t\t\t(r[1] >= -eps) && (r[1] <= eps) &&\n\t\t\t(r[2] >= -eps) && (r[2] <= eps)) return true;\n\t}\n\telse if ((el.Shape() == ET_TET4) || (el.Shape() == ET_TET5) || (el.Shape() == ET_TET10))\n\t{\n\t\tconst double eps = 0.0001;\n\t\tif ((r[0] >= -eps) && (r[0] <= 1.0 + eps) &&\n\t\t\t(r[1] >= -eps) && (r[1] <= 1.0 + eps) &&\n\t\t\t(r[2] >= -eps) && (r[2] <= 1.0 + eps) &&\n\t\t\t(r[0] + r[1] + r[2] <= 1 + eps)) return true;\n\t}\n    else if ((el.Shape() == ET_PENTA6) || (el.Shape() == ET_PENTA15))\n    {\n        const double eps = 0.0001;\n        if ((r[0] >= -eps) && (r[0] <= 1.0 + eps) &&\n            (r[1] >= -eps) && (r[1] <= 1.0 + eps) &&\n            (r[2] >= -1.0 - eps) && (r[2] <= 1.0 + eps) &&\n            (r[0] + r[1] <= 1 + eps)) return true;\n    }\n\telse {\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\treturn false;\n}\n\n\n//-----------------------------------------------------------------------------\n//! get the current nodal coordinates\nvoid FESolidDomain::GetCurrentNodalCoordinates(const FESolidElement& el, vec3d* rt)\n{\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n\n\t// check for solid-shell interface nodes\n\tif (el.m_bitfc.empty() == false)\n\t{\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tif (el.m_bitfc[i])\n\t\t\t{\n\t\t\t\tFENode& nd = m_pMesh->Node(el.m_node[i]);\n\t\t\t\trt[i] -= nd.m_dt;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the current nodal coordinates\nvoid FESolidDomain::GetCurrentNodalCoordinates(const FESolidElement& el, vec3d* rt, double alpha)\n{\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i) {\n\t\tFENode& nd = m_pMesh->Node(el.m_node[i]);\n\t\trt[i] = nd.m_rt*alpha + nd.m_rp*(1 - alpha);\n\t}\n\n\t// check for solid-shell interface nodes\n\tif (el.m_bitfc.empty() == false)\n\t{\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tif (el.m_bitfc[i]) {\n\t\t\t\tFENode& nd = m_pMesh->Node(el.m_node[i]);\n\t\t\t\trt[i] = nd.m_r0 - nd.m_d0 \\\n\t\t\t\t\t+ nd.get_vec3d(m_dofSU[0], m_dofSU[1], m_dofSU[2])*alpha \\\n\t\t\t\t\t+ nd.get_vec3d_prev(m_dofSU[0], m_dofSU[1], m_dofSU[2])*(1 - alpha);\n\t\t\t}\n\t\t}\n\t}\n\n}\n\n//-----------------------------------------------------------------------------\n//! get the reference nodal coordinates\nvoid FESolidDomain::GetReferenceNodalCoordinates(const FESolidElement& el, vec3d* r0)\n{\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i) r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n\n\t// check for solid-shell interface nodes\n\tif (el.m_bitfc.empty() == false)\n\t{\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tif (el.m_bitfc[i])\n\t\t\t{\n\t\t\t\tFENode& nd = m_pMesh->Node(el.m_node[i]);\n\t\t\t\tr0[i] -= nd.m_d0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! get the previous nodal coordinates\nvoid FESolidDomain::GetPreviousNodalCoordinates(const FESolidElement& el, vec3d* rp)\n{\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i) rp[i] = m_pMesh->Node(el.m_node[i]).m_rp;\n\n\t// check for solid-shell interface nodes\n\tif (el.m_bitfc.empty() == false)\n\t{\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tif (el.m_bitfc[i])\n\t\t\t{\n\t\t\t\tFENode& nd = m_pMesh->Node(el.m_node[i]);\n\t\t\t\trp[i] -= nd.m_dp;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deformation gradient of element el at integration point n.\n//! The deformation gradient is returned in F and its determinant is the return\n//! value of the function\ndouble FESolidDomain::defgrad(FESolidElement &el, mat3d &F, int n)\n{\n    // nodal points\n    vec3d r[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, r);\n    \n    // calculate inverse jacobian\n//    double Ji[3][3];\n//    invjac0(el, Ji, n);\n\tmat3d& Ji = el.m_J0i[n];\n\n\t// shape function derivatives\n\tdouble *Grn = el.Gr(n);\n\tdouble *Gsn = el.Gs(n);\n\tdouble *Gtn = el.Gt(n);\n\n    // calculate deformation gradient\n\tF[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        double Gri = Grn[i];\n        double Gsi = Gsn[i];\n        double Gti = Gtn[i];\n        \n        double x = r[i].x;\n        double y = r[i].y;\n        double z = r[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n    \n    return D;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the gradJ of element el at integration point n.\n//! Mult GradJ by F-1\nvec3d FESolidDomain::GradJ(FESolidElement &el, int n)\n{\n    // nodal points\n    vec3d r[FEElement::MAX_NODES];\n    GetCurrentNodalCoordinates(el, r);\n    mat3d F;\n    \n    vec3d g[3], dg[3][3];\n    \n    // calculate inverse jacobian\n    //    double Ji[3][3];\n    //    invjac0(el, Ji, n);\n    //Ji for Grad\n    mat3d& Ji = el.m_J0i[n];\n    \n    ContraBaseVectors0(el, n, g);\n    ContraBaseVectorDerivatives0(el, n, dg);\n    \n    // shape function derivatives\n    double *Grn = el.Gr(n);\n    double *Gsn = el.Gs(n);\n    double *Gtn = el.Gt(n);\n    \n    // get the shape function derivatives\n    double* Grr = el.Grr(n); double* Grs = el.Grs(n); double* Grt = el.Grt(n);\n    double* Gsr = el.Gsr(n); double* Gss = el.Gss(n); double* Gst = el.Gst(n);\n    double* Gtr = el.Gtr(n); double* Gts = el.Gts(n); double* Gtt = el.Gtt(n);\n    \n    // calculate deformation gradient\n    F[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n    \n    //Initialize gradTF tens3d (gradiFjk)\n    tens3d gradTF;\n    mat3d gradTF1, gradTF2, gradTF3;\n    gradTF.d[ 0] = gradTF.d[ 1] = gradTF.d[ 2] = gradTF.d[ 3] = gradTF.d[ 4] = gradTF.d[ 5] = gradTF.d[ 6] = gradTF.d[ 7] = gradTF.d[ 8] = gradTF.d[ 9] = gradTF.d[10] = gradTF.d[11] = gradTF.d[12] = gradTF.d[13] = gradTF.d[14] = gradTF.d[15] = gradTF.d[16] = gradTF.d[17] = gradTF.d[18] = gradTF.d[19] = gradTF.d[20] = gradTF.d[21] = gradTF.d[22] = gradTF.d[23] = gradTF.d[24] = gradTF.d[25] = gradTF.d[26] = 0.0;\n    \n    int neln = el.Nodes();\n    for (int i = 0; i<neln; ++i)\n    {\n        double Gri = Grn[i];\n        double Gsi = Gsn[i];\n        double Gti = Gtn[i];\n        \n        double Grri = Grr[i]; double Grsi = Grs[i]; double Grti = Grt[i];\n        double Gsri = Gsr[i]; double Gssi = Gss[i]; double Gsti = Gst[i];\n        double Gtri = Gtr[i]; double Gtsi = Gts[i]; double Gtti = Gtt[i];\n        \n        double x = r[i].x;\n        double y = r[i].y;\n        double z = r[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n        \n        // calculate GradTF\n        \n        mat3d gradgrad = (((dg[0][0] & g[0]) + (dg[0][1] & g[1]) + (dg[0][2] & g[2]))*Gri\n                          + ((dg[1][0] & g[0]) + (dg[1][1] & g[1]) + (dg[1][2] & g[2]))*Gsi\n                          + ((dg[2][0] & g[0]) + (dg[2][1] & g[1]) + (dg[2][2] & g[2]))*Gti\n                          + (g[0] & g[0])*Grri + (g[0] & g[1])*Gsri + (g[0] & g[2])*Gtri\n                          + (g[1] & g[0])*Grsi + (g[1] & g[1])*Gssi + (g[1] & g[2] )*Gtsi\n                          + (g[2] & g[0])*Grti + (g[2] & g[1])*Gsti + (g[2] & g[2])*Gtti);\n        \n        gradTF1 = gradgrad*x;\n        gradTF2 = gradgrad*y;\n        gradTF3 = gradgrad*z;\n        \n        gradTF.d[0] += gradTF1[0][0]; gradTF.d[1] += gradTF1[1][0]; gradTF.d[2] += gradTF1[2][0];\n        gradTF.d[3] += gradTF2[0][0]; gradTF.d[4] += gradTF2[1][0]; gradTF.d[5] += gradTF2[2][0];\n        gradTF.d[6] += gradTF3[0][0]; gradTF.d[7] += gradTF3[1][0]; gradTF.d[8] += gradTF3[2][0];\n        gradTF.d[9] += gradTF1[0][1]; gradTF.d[10] += gradTF1[1][1]; gradTF.d[11] += gradTF1[2][1];\n        gradTF.d[12] += gradTF2[0][1]; gradTF.d[13] += gradTF2[1][1]; gradTF.d[14] += gradTF2[2][1];\n        gradTF.d[15] += gradTF3[0][1]; gradTF.d[16] += gradTF3[1][1]; gradTF.d[17] += gradTF3[2][1];\n        gradTF.d[18] += gradTF1[0][2]; gradTF.d[19] += gradTF1[1][2]; gradTF.d[20] += gradTF1[2][2];\n        gradTF.d[21] += gradTF2[0][2]; gradTF.d[22] += gradTF2[1][2]; gradTF.d[23] += gradTF2[2][2];\n        gradTF.d[24] += gradTF3[0][2]; gradTF.d[25] += gradTF3[1][2]; gradTF.d[26] += gradTF3[2][2];\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n    mat3d FinvT = F.transinv();\n    vec3d gJ = gradTF.contract2(FinvT)*D;\n    \n    return gJ;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the gradJ of element el at integration point n prev time.\nvec3d FESolidDomain::GradJp(FESolidElement &el, int n)\n{\n    // nodal points\n    vec3d r[FEElement::MAX_NODES];\n    GetPreviousNodalCoordinates(el, r);\n    mat3d F;\n    \n    vec3d g[3], dg[3][3];\n    \n    // calculate inverse jacobian\n    //    double Ji[3][3];\n    //    invjac0(el, Ji, n);\n    //Ji for Grad\n    mat3d& Ji = el.m_J0i[n];\n    \n    ContraBaseVectors0(el, n, g);\n    ContraBaseVectorDerivatives0(el, n, dg);\n    \n    // shape function derivatives\n    double *Grn = el.Gr(n);\n    double *Gsn = el.Gs(n);\n    double *Gtn = el.Gt(n);\n    \n    // get the shape function derivatives\n    double* Grr = el.Grr(n); double* Grs = el.Grs(n); double* Grt = el.Grt(n);\n    double* Gsr = el.Gsr(n); double* Gss = el.Gss(n); double* Gst = el.Gst(n);\n    double* Gtr = el.Gtr(n); double* Gts = el.Gts(n); double* Gtt = el.Gtt(n);\n    \n    // calculate deformation gradient\n    F[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n    \n    //Initialize gradTF tens3d (gradiFjk)\n    tens3d gradTF;\n    mat3d gradTF1, gradTF2, gradTF3;\n    gradTF.d[ 0] = gradTF.d[ 1] = gradTF.d[ 2] = gradTF.d[ 3] = gradTF.d[ 4] = gradTF.d[ 5] = gradTF.d[ 6] = gradTF.d[ 7] = gradTF.d[ 8] = gradTF.d[ 9] = gradTF.d[10] = gradTF.d[11] = gradTF.d[12] = gradTF.d[13] = gradTF.d[14] = gradTF.d[15] = gradTF.d[16] = gradTF.d[17] = gradTF.d[18] = gradTF.d[19] = gradTF.d[20] = gradTF.d[21] = gradTF.d[22] = gradTF.d[23] = gradTF.d[24] = gradTF.d[25] = gradTF.d[26] = 0.0;\n    \n    int neln = el.Nodes();\n    for (int i = 0; i<neln; ++i)\n    {\n        double Gri = Grn[i];\n        double Gsi = Gsn[i];\n        double Gti = Gtn[i];\n        \n        double Grri = Grr[i]; double Grsi = Grs[i]; double Grti = Grt[i];\n        double Gsri = Gsr[i]; double Gssi = Gss[i]; double Gsti = Gst[i];\n        double Gtri = Gtr[i]; double Gtsi = Gts[i]; double Gtti = Gtt[i];\n        \n        double x = r[i].x;\n        double y = r[i].y;\n        double z = r[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n        \n        // calculate gradTF\n        \n        mat3d gradgrad = (((dg[0][0] & g[0]) + (dg[0][1] & g[1]) + (dg[0][2] & g[2]))*Gri\n                          + ((dg[1][0] & g[0]) + (dg[1][1] & g[1]) + (dg[1][2] & g[2]))*Gsi\n                          + ((dg[2][0] & g[0]) + (dg[2][1] & g[1]) + (dg[2][2] & g[2]))*Gti\n                          + (g[0] & g[0])*Grri + (g[0] & g[1])*Gsri + (g[0] & g[2])*Gtri\n                          + (g[1] & g[0])*Grsi + (g[1] & g[1])*Gssi + (g[1] & g[2] )*Gtsi\n                          + (g[2] & g[0])*Grti + (g[2] & g[1])*Gsti + (g[2] & g[2])*Gtti);\n        \n        gradTF1 = gradgrad*x;\n        gradTF2 = gradgrad*y;\n        gradTF3 = gradgrad*z;\n        \n        gradTF.d[0] += gradTF1[0][0]; gradTF.d[1] += gradTF1[1][0]; gradTF.d[2] += gradTF1[2][0];\n        gradTF.d[3] += gradTF2[0][0]; gradTF.d[4] += gradTF2[1][0]; gradTF.d[5] += gradTF2[2][0];\n        gradTF.d[6] += gradTF3[0][0]; gradTF.d[7] += gradTF3[1][0]; gradTF.d[8] += gradTF3[2][0];\n        gradTF.d[9] += gradTF1[0][1]; gradTF.d[10] += gradTF1[1][1]; gradTF.d[11] += gradTF1[2][1];\n        gradTF.d[12] += gradTF2[0][1]; gradTF.d[13] += gradTF2[1][1]; gradTF.d[14] += gradTF2[2][1];\n        gradTF.d[15] += gradTF3[0][1]; gradTF.d[16] += gradTF3[1][1]; gradTF.d[17] += gradTF3[2][1];\n        gradTF.d[18] += gradTF1[0][2]; gradTF.d[19] += gradTF1[1][2]; gradTF.d[20] += gradTF1[2][2];\n        gradTF.d[21] += gradTF2[0][2]; gradTF.d[22] += gradTF2[1][2]; gradTF.d[23] += gradTF2[2][2];\n        gradTF.d[24] += gradTF3[0][2]; gradTF.d[25] += gradTF3[1][2]; gradTF.d[26] += gradTF3[2][2];\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n    mat3d FinvT = F.transinv();\n    vec3d gJ = gradTF.contract2(FinvT)*D;\n    \n    return gJ;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the Gradb of element el at integration point n.\n//! Mult Gradb by F-1 later\ntens3dls FESolidDomain::Gradb(FESolidElement &el, int n)\n{\n    // nodal points\n    vec3d r[FEElement::MAX_NODES];\n    GetCurrentNodalCoordinates(el, r);\n    mat3d F, b;\n    \n    vec3d g[3], dg[3][3];\n    \n    // calculate inverse jacobian\n    //    double Ji[3][3];\n    //    invjac0(el, Ji, n);\n    //Ji for Grad\n    mat3d& Ji = el.m_J0i[n];\n    \n    ContraBaseVectors0(el, n, g);\n    ContraBaseVectorDerivatives0(el, n, dg);\n    \n    // shape function derivatives\n    double *Grn = el.Gr(n);\n    double *Gsn = el.Gs(n);\n    double *Gtn = el.Gt(n);\n    \n    // calculate deformation gradient\n    F[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n    \n    b[0][0] = b[0][1] = b[0][2] = 0;\n    b[1][0] = b[1][1] = b[1][2] = 0;\n    b[2][0] = b[2][1] = b[2][2] = 0;\n    \n    //Initialize gradTF tens3d (gradiFjk)\n    tens3dls GB;\n    GB.zero();\n    \n    int neln = el.Nodes();\n    for (int i = 0; i<neln; ++i)\n    {\n        double Gri = Grn[i];\n        double Gsi = Gsn[i];\n        double Gti = Gtn[i];\n        \n        double x = r[i].x;\n        double y = r[i].y;\n        double z = r[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n        \n        // calculate GB Grad(FikFjk)l\n        // [G] = [G111 G112 G113 G121 G122 G123 G131 G132 G133 G221 G222 G223 G231 G232 G233 G331 G332 G333]\n        //     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\n        GB.d[ 0] += (x*GX*GX*x + x*GY*GY*x + x*GZ*GZ*x)*GX;\n        GB.d[ 1] += (x*GX*GX*x + x*GY*GY*x + x*GZ*GZ*x)*GY;\n        GB.d[ 2] += (x*GX*GX*x + x*GY*GY*x + x*GZ*GZ*x)*GZ;\n        GB.d[ 3] += (x*GX*GX*y + x*GY*GY*y + x*GZ*GZ*y)*GX;\n        GB.d[ 4] += (x*GX*GX*y + x*GY*GY*y + x*GZ*GZ*y)*GY;\n        GB.d[ 5] += (x*GX*GX*y + x*GY*GY*y + x*GZ*GZ*y)*GZ;\n        GB.d[ 6] += (x*GX*GX*z + x*GY*GY*z + x*GZ*GZ*z)*GX;\n        GB.d[ 7] += (x*GX*GX*z + x*GY*GY*z + x*GZ*GZ*z)*GY;\n        GB.d[ 8] += (x*GX*GX*z + x*GY*GY*z + x*GZ*GZ*z)*GZ;\n        GB.d[ 9] += (y*GX*GX*y + y*GY*GY*y + y*GZ*GZ*y)*GX;\n        GB.d[10] += (y*GX*GX*y + y*GY*GY*y + y*GZ*GZ*y)*GY;\n        GB.d[11] += (y*GX*GX*y + y*GY*GY*y + y*GZ*GZ*y)*GZ;\n        GB.d[12] += (y*GX*GX*z + y*GY*GY*z + y*GZ*GZ*z)*GX;\n        GB.d[13] += (y*GX*GX*z + y*GY*GY*z + y*GZ*GZ*z)*GY;\n        GB.d[14] += (y*GX*GX*z + y*GY*GY*z + y*GZ*GZ*z)*GZ;\n        GB.d[15] += (z*GX*GX*z + z*GY*GY*z + z*GZ*GZ*z)*GX;\n        GB.d[16] += (z*GX*GX*z + z*GY*GY*z + z*GZ*GZ*z)*GY;\n        GB.d[17] += (z*GX*GX*z + z*GY*GY*z + z*GZ*GZ*z)*GZ;\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n    b = F*F.transpose();\n    \n    return GB;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the Gradb of element el at integration point n.\n//! Mult Gradb by F-1 later\ntens3dls FESolidDomain::Gradbp(FESolidElement &el, int n)\n{\n    // nodal points\n    vec3d r[FEElement::MAX_NODES];\n    GetPreviousNodalCoordinates(el, r);\n    mat3d F, b;\n    \n    vec3d g[3], dg[3][3];\n    \n    // calculate inverse jacobian\n    //    double Ji[3][3];\n    //    invjac0(el, Ji, n);\n    //Ji for Grad\n    mat3d& Ji = el.m_J0i[n];\n    \n    ContraBaseVectors0(el, n, g);\n    ContraBaseVectorDerivatives0(el, n, dg);\n    \n    // shape function derivatives\n    double *Grn = el.Gr(n);\n    double *Gsn = el.Gs(n);\n    double *Gtn = el.Gt(n);\n    \n    // calculate deformation gradient\n    F[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n    \n    b[0][0] = b[0][1] = b[0][2] = 0;\n    b[1][0] = b[1][1] = b[1][2] = 0;\n    b[2][0] = b[2][1] = b[2][2] = 0;\n    \n    //Initialize gradTF tens3d (gradiFjk)\n    tens3dls GB;\n    GB.zero();\n    \n    int neln = el.Nodes();\n    for (int i = 0; i<neln; ++i)\n    {\n        double Gri = Grn[i];\n        double Gsi = Gsn[i];\n        double Gti = Gtn[i];\n        \n        double x = r[i].x;\n        double y = r[i].y;\n        double z = r[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n        \n        // calculate GB\n        // [G] = [G111 G112 G113 G121 G122 G123 G131 G132 G133 G221 G222 G223 G231 G232 G233 G331 G332 G333]\n        //     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\n        GB.d[ 0] += (x*GX*GX*x + x*GY*GY*x + x*GZ*GZ*x)*GX;\n        GB.d[ 1] += (x*GX*GX*x + x*GY*GY*x + x*GZ*GZ*x)*GY;\n        GB.d[ 2] += (x*GX*GX*x + x*GY*GY*x + x*GZ*GZ*x)*GZ;\n        GB.d[ 3] += (x*GX*GX*y + x*GY*GY*y + x*GZ*GZ*y)*GX;\n        GB.d[ 4] += (x*GX*GX*y + x*GY*GY*y + x*GZ*GZ*y)*GY;\n        GB.d[ 5] += (x*GX*GX*y + x*GY*GY*y + x*GZ*GZ*y)*GZ;\n        GB.d[ 6] += (x*GX*GX*z + x*GY*GY*z + x*GZ*GZ*z)*GX;\n        GB.d[ 7] += (x*GX*GX*z + x*GY*GY*z + x*GZ*GZ*z)*GY;\n        GB.d[ 8] += (x*GX*GX*z + x*GY*GY*z + x*GZ*GZ*z)*GZ;\n        GB.d[ 9] += (y*GX*GX*y + y*GY*GY*y + y*GZ*GZ*y)*GX;\n        GB.d[10] += (y*GX*GX*y + y*GY*GY*y + y*GZ*GZ*y)*GY;\n        GB.d[11] += (y*GX*GX*y + y*GY*GY*y + y*GZ*GZ*y)*GZ;\n        GB.d[12] += (y*GX*GX*z + y*GY*GY*z + y*GZ*GZ*z)*GX;\n        GB.d[13] += (y*GX*GX*z + y*GY*GY*z + y*GZ*GZ*z)*GY;\n        GB.d[14] += (y*GX*GX*z + y*GY*GY*z + y*GZ*GZ*z)*GZ;\n        GB.d[15] += (z*GX*GX*z + z*GY*GY*z + z*GZ*GZ*z)*GX;\n        GB.d[16] += (z*GX*GX*z + z*GY*GY*z + z*GZ*GZ*z)*GY;\n        GB.d[17] += (z*GX*GX*z + z*GY*GY*z + z*GZ*GZ*z)*GZ;\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n    b = F*F.transpose();\n    \n    return GB;\n}\n\n\n//-----------------------------------------------------------------------------\n//! Calculate the deformation gradient of element el at integration point n.\n//! The deformation gradient is returned in F and its determinant is the return\n//! value of the function\ndouble FESolidDomain::defgrad(FESolidElement &el, mat3d &F, int n, vec3d* r)\n{\n\t// calculate inverse jacobian\n\t//    double Ji[3][3];\n\t//    invjac0(el, Ji, n);\n\tmat3d& Ji = el.m_J0i[n];\n\n\t// shape function derivatives\n\tdouble *Grn = el.Gr(n);\n\tdouble *Gsn = el.Gs(n);\n\tdouble *Gtn = el.Gt(n);\n\n\t// calculate deformation gradient\n\tF[0][0] = F[0][1] = F[0][2] = 0;\n\tF[1][0] = F[1][1] = F[1][2] = 0;\n\tF[2][0] = F[2][1] = F[2][2] = 0;\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n\t{\n\t\tdouble Gri = Grn[i];\n\t\tdouble Gsi = Gsn[i];\n\t\tdouble Gti = Gtn[i];\n\n\t\tdouble x = r[i].x;\n\t\tdouble y = r[i].y;\n\t\tdouble z = r[i].z;\n\n\t\t// calculate global gradient of shape functions\n\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\tdouble GX = Ji[0][0] * Gri + Ji[1][0] * Gsi + Ji[2][0] * Gti;\n\t\tdouble GY = Ji[0][1] * Gri + Ji[1][1] * Gsi + Ji[2][1] * Gti;\n\t\tdouble GZ = Ji[0][2] * Gri + Ji[1][2] * Gsi + Ji[2][2] * Gti;\n\n\t\t// calculate deformation gradient F\n\t\tF[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n\t\tF[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n\t\tF[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n\t}\n\n\tdouble D = F.det();\n\tif (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n\n\treturn D;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deformation gradient of element at point r,s,t\ndouble FESolidDomain::defgrad(FESolidElement &el, mat3d &F, double r, double s, double t)\n{\n    // shape function derivatives\n    const int NME = FEElement::MAX_NODES;\n    double Gr[NME], Gs[NME], Gt[NME];\n    el.shape_deriv(Gr, Gs, Gt, r, s, t);\n    \n    // nodal points\n    vec3d rt[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, rt);\n\n    // calculate inverse jacobian\n    double Ji[3][3];\n    invjac0(el, Ji, r, s, t);\n    \n    // calculate deformation gradient\n\tF[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        double Gri = Gr[i];\n        double Gsi = Gs[i];\n        double Gti = Gt[i];\n        \n        double x = rt[i].x;\n        double y = rt[i].y;\n        double z = rt[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), -1, D, &el);\n    \n    return D;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the deformation gradient of element el at integration point n.\n//! The deformation gradient is returned in F and its determinant is the return\n//! value of the function\ndouble FESolidDomain::defgradp(FESolidElement &el, mat3d &F, int n)\n{\n    // nodal coordinates\n    vec3d r[FEElement::MAX_NODES];\n\tGetPreviousNodalCoordinates(el, r);\n    \n    // calculate inverse jacobian\n//    double Ji[3][3];\n//    invjac0(el, Ji, n);\n\tmat3d& Ji = el.m_J0i[n];\n\n\t// shape function derivatives\n\tdouble *Grn = el.Gr(n);\n\tdouble *Gsn = el.Gs(n);\n\tdouble *Gtn = el.Gt(n);\n\n    // calculate deformation gradient\n    F[0][0] = F[0][1] = F[0][2] = 0;\n    F[1][0] = F[1][1] = F[1][2] = 0;\n    F[2][0] = F[2][1] = F[2][2] = 0;\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        double Gri = Grn[i];\n        double Gsi = Gsn[i];\n        double Gti = Gtn[i];\n        \n        double x = r[i].x;\n        double y = r[i].y;\n        double z = r[i].z;\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        double GX = Ji[0][0]*Gri+Ji[1][0]*Gsi+Ji[2][0]*Gti;\n        double GY = Ji[0][1]*Gri+Ji[1][1]*Gsi+Ji[2][1]*Gti;\n        double GZ = Ji[0][2]*Gri+Ji[1][2]*Gsi+Ji[2][2]*Gti;\n        \n        // calculate deformation gradient F\n        F[0][0] += GX*x; F[0][1] += GY*x; F[0][2] += GZ*x;\n        F[1][0] += GX*y; F[1][1] += GY*y; F[1][2] += GZ*y;\n        F[2][0] += GX*z; F[2][1] += GY*z; F[2][2] += GZ*z;\n    }\n    \n    double D = F.det();\n    if (D <= 0) throw NegativeJacobian(el.GetID(), n, D, &el);\n    \n    return D;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the reference frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjac0(const FESolidElement& el, double Ji[3][3], int n)\n{\n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n\tGetReferenceNodalCoordinates(el, r0);\n   \n    // calculate Jacobian\n    double J[3][3] = {0};\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        const double& Gri = el.Gr(n)[i];\n        const double& Gsi = el.Gs(n)[i];\n        const double& Gti = el.Gt(n)[i];\n        \n        const double& x = r0[i].x;\n        const double& y = r0[i].y;\n        const double& z = r0[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n    \n    // calculate the inverse jacobian\n    double deti = 1.0 / det;\n    \n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the reference frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjac0(const FESolidElement& el, double Ji[3][3], double r, double s, double t)\n{\n    // nodal coordinates\n    const int NMAX = FEElement::MAX_NODES;\n    vec3d r0[NMAX];\n\tGetReferenceNodalCoordinates(el, r0);\n    \n    // evaluate shape function derivatives\n    double Gr[NMAX], Gs[NMAX], Gt[NMAX];\n    el.shape_deriv(Gr, Gs, Gt, r, s, t);\n    \n    // calculate Jacobian\n    double J[3][3] = {0};\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        const double& Gri = Gr[i];\n        const double& Gsi = Gs[i];\n        const double& Gti = Gt[i];\n        \n        const double& x = r0[i].x;\n        const double& y = r0[i].y;\n        const double& z = r0[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), -1, det);\n    \n    // calculate the inverse jacobian\n    double deti = 1.0 / det;\n    \n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolidDomain::invjac0(const FESolidElement& el, double r, double s, double t, mat3d& J)\n{\n    double Ji[3][3];\n    double J0 = invjac0(el, Ji, r, s, t);\n    J = mat3d(Ji[0][0], Ji[0][1], Ji[0][2], Ji[1][0], Ji[1][1], Ji[1][2], Ji[2][0], Ji[2][1], Ji[2][2]);\n    return J0;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the current frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjact(FESolidElement& el, double Ji[3][3], int n)\n{\n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, rt);\n\n    // calculate jacobian\n    double J[3][3] = {0};\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        const double& Gri = el.Gr(n)[i];\n        const double& Gsi = el.Gs(n)[i];\n        const double& Gti = el.Gt(n)[i];\n        \n        const double& x = rt[i].x;\n        const double& y = rt[i].y;\n        const double& z = rt[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n    \n    // calculate inverse jacobian\n    double deti = 1.0 / det;\n\t\t\t\t\n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the current frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjact(FESolidElement& el, double Ji[3][3], int n, const vec3d* rt)\n{\n\t// calculate jacobian\n\tdouble J[3][3] = { 0 };\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n\t{\n\t\tconst double& Gri = el.Gr(n)[i];\n\t\tconst double& Gsi = el.Gs(n)[i];\n\t\tconst double& Gti = el.Gt(n)[i];\n\n\t\tconst double& x = rt[i].x;\n\t\tconst double& y = rt[i].y;\n\t\tconst double& z = rt[i].z;\n\n\t\tJ[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n\t\tJ[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n\t\tJ[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n\t}\n\n\t// calculate the determinant\n\tdouble det = J[0][0] * (J[1][1] * J[2][2] - J[1][2] * J[2][1])\n\t\t+ J[0][1] * (J[1][2] * J[2][0] - J[2][2] * J[1][0])\n\t\t+ J[0][2] * (J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\t// make sure the determinant is positive\n\tif (det <= 0) throw NegativeJacobian(el.GetID(), n + 1, det);\n\n\t// calculate inverse jacobian\n\tdouble deti = 1.0 / det;\n\n\tJi[0][0] = deti*(J[1][1] * J[2][2] - J[1][2] * J[2][1]);\n\tJi[1][0] = deti*(J[1][2] * J[2][0] - J[1][0] * J[2][2]);\n\tJi[2][0] = deti*(J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\tJi[0][1] = deti*(J[0][2] * J[2][1] - J[0][1] * J[2][2]);\n\tJi[1][1] = deti*(J[0][0] * J[2][2] - J[0][2] * J[2][0]);\n\tJi[2][1] = deti*(J[0][1] * J[2][0] - J[0][0] * J[2][1]);\n\n\tJi[0][2] = deti*(J[0][1] * J[1][2] - J[1][1] * J[0][2]);\n\tJi[1][2] = deti*(J[0][2] * J[1][0] - J[0][0] * J[1][2]);\n\tJi[2][2] = deti*(J[0][0] * J[1][1] - J[0][1] * J[1][0]);\n\n\treturn det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the current frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjact(FESolidElement& el, double Ji[3][3], int n, const double alpha)\n{\n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, rt, alpha);\n    \n    // calculate jacobian\n\tint neln = el.Nodes();\n\tdouble J[3][3] = { 0 };\n    for (int i=0; i<neln; ++i)\n    {\n        const double& Gri = el.Gr(n)[i];\n        const double& Gsi = el.Gs(n)[i];\n        const double& Gti = el.Gt(n)[i];\n        \n        const double& x = rt[i].x;\n        const double& y = rt[i].y;\n        const double& z = rt[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n    \n    // calculate inverse jacobian\n    double deti = 1.0 / det;\n\t\t\t\t\n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the previous time frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjactp(FESolidElement& el, double Ji[3][3], int n)\n{\n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n\tGetPreviousNodalCoordinates(el, rt);\n    \n    // calculate jacobian\n\tint neln = el.Nodes();\n\tdouble J[3][3] = { 0 };\n    for (int i=0; i<neln; ++i)\n    {\n        const double& Gri = el.Gr(n)[i];\n        const double& Gsi = el.Gs(n)[i];\n        const double& Gti = el.Gt(n)[i];\n        \n        const double& x = rt[i].x;\n        const double& y = rt[i].y;\n        const double& z = rt[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), n+1, det);\n    \n    // calculate inverse jacobian\n    double deti = 1.0 / det;\n\t\t\t\t\n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse jacobian with respect to the reference frame at\n//! integration point n. The inverse jacobian is retured in Ji\n//! The return value is the determinant of the Jacobian (not the inverse!)\ndouble FESolidDomain::invjact(FESolidElement& el, double Ji[3][3], double r, double s, double t)\n{\n    // nodal coordinates\n    const int NMAX = FEElement::MAX_NODES;\n    vec3d rt[NMAX];\n\tGetCurrentNodalCoordinates(el, rt);\n\n    // evaluate shape function derivatives\n    double Gr[NMAX], Gs[NMAX], Gt[NMAX];\n    el.shape_deriv(Gr, Gs, Gt, r, s, t);\n    \n    // calculate Jacobian\n    double J[3][3] = {0};\n\tconst int neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        const double& Gri = Gr[i];\n        const double& Gsi = Gs[i];\n        const double& Gti = Gt[i];\n        \n        const double& x = rt[i].x;\n        const double& y = rt[i].y;\n        const double& z = rt[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    // make sure the determinant is positive\n    if (det <= 0) throw NegativeJacobian(el.GetID(), -1, det);\n    \n    // calculate the inverse jacobian\n    double deti = 1.0 / det;\n    \n    Ji[0][0] =  deti*(J[1][1]*J[2][2] - J[1][2]*J[2][1]);\n    Ji[1][0] =  deti*(J[1][2]*J[2][0] - J[1][0]*J[2][2]);\n    Ji[2][0] =  deti*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    Ji[0][1] =  deti*(J[0][2]*J[2][1] - J[0][1]*J[2][2]);\n    Ji[1][1] =  deti*(J[0][0]*J[2][2] - J[0][2]*J[2][0]);\n    Ji[2][1] =  deti*(J[0][1]*J[2][0] - J[0][0]*J[2][1]);\n    \n    Ji[0][2] =  deti*(J[0][1]*J[1][2] - J[1][1]*J[0][2]);\n    Ji[1][2] =  deti*(J[0][2]*J[1][0] - J[0][0]*J[1][2]);\n    Ji[2][2] =  deti*(J[0][0]*J[1][1] - J[0][1]*J[1][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate gradient of function at integration points\nvec3d FESolidDomain::gradient(FESolidElement& el, double* fn, int n)\n{\n    double Ji[3][3];\n    invjact(el, Ji, n);\n\t\t\t\t\n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    double Gx, Gy, Gz;\n    \n    vec3d gradf;\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i]+Ji[2][0]*Gtn[i];\n        Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i]+Ji[2][1]*Gtn[i];\n        Gz = Ji[0][2]*Grn[i]+Ji[1][2]*Gsn[i]+Ji[2][2]*Gtn[i];\n        \n        // calculate pressure gradient\n        gradf.x += Gx*fn[i];\n        gradf.y += Gy*fn[i];\n        gradf.z += Gz*fn[i];\n    }\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate gradient of function at integration points\nvec3d FESolidDomain::gradient(FESolidElement& el, int order, double* fn, int n)\n{\n\tdouble Ji[3][3];\n\tinvjact(el, Ji, n);\n\n\tdouble* Grn = el.Gr(order, n);\n\tdouble* Gsn = el.Gs(order, n);\n\tdouble* Gtn = el.Gt(order, n);\n\n\tdouble Gx, Gy, Gz;\n\n\tvec3d gradf;\n\tint N = el.ShapeFunctions(order);\n\tfor (int i = 0; i<N; ++i)\n\t{\n\t\t// calculate global gradient of shape functions\n\t\t// note that we need the transposed of Ji, not Ji itself !\n\t\tGx = Ji[0][0] * Grn[i] + Ji[1][0] * Gsn[i] + Ji[2][0] * Gtn[i];\n\t\tGy = Ji[0][1] * Grn[i] + Ji[1][1] * Gsn[i] + Ji[2][1] * Gtn[i];\n\t\tGz = Ji[0][2] * Grn[i] + Ji[1][2] * Gsn[i] + Ji[2][2] * Gtn[i];\n\n\t\t// calculate pressure gradient\n\t\tgradf.x += Gx*fn[i];\n\t\tgradf.y += Gy*fn[i];\n\t\tgradf.z += Gz*fn[i];\n\t}\n\n\treturn gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate gradient of function at integration points\nvec3d FESolidDomain::gradient(FESolidElement& el, vector<double>& fn, int n)\n{\n    double Ji[3][3];\n    invjact(el, Ji, n);\n\t\t\t\t\n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    double Gx, Gy, Gz;\n    \n    vec3d gradf;\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i]+Ji[2][0]*Gtn[i];\n        Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i]+Ji[2][1]*Gtn[i];\n        Gz = Ji[0][2]*Grn[i]+Ji[1][2]*Gsn[i]+Ji[2][2]*Gtn[i];\n        \n        // calculate pressure gradient\n        gradf.x += Gx*fn[i];\n        gradf.y += Gy*fn[i];\n        gradf.z += Gz*fn[i];\n    }\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points\nmat3d FESolidDomain::gradient(FESolidElement& el, vec3d* fn, int n)\n{\n    double Ji[3][3];\n    invjact(el, Ji, n);\n\t\t\t\t\n    vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n    vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n    vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n    \n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    mat3d gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gradf += fn[i] & (g1*Gr[i] + g2*Gs[i] + g3*Gt[i]);\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate gradient of function at integration points at intermediate time\nvec3d FESolidDomain::gradient(FESolidElement& el, vector<double>& fn, int n, const double alpha)\n{\n    double Ji[3][3];\n    invjact(el, Ji, n, alpha);\n    \n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    double Gx, Gy, Gz;\n    \n    vec3d gradf;\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i]+Ji[2][0]*Gtn[i];\n        Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i]+Ji[2][1]*Gtn[i];\n        Gz = Ji[0][2]*Grn[i]+Ji[1][2]*Gsn[i]+Ji[2][2]*Gtn[i];\n        \n        // calculate pressure gradient\n        gradf.x += Gx*fn[i];\n        gradf.y += Gy*fn[i];\n        gradf.z += Gz*fn[i];\n    }\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points at intermediate time\nmat3d FESolidDomain::gradient(FESolidElement& el, vec3d* fn, int n, const double alpha)\n{\n    double Ji[3][3];\n    invjact(el, Ji, n, alpha);\n    \n    vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n    vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n    vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n    \n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    mat3d gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gradf += fn[i] & (g1*Gr[i] + g2*Gs[i] + g3*Gt[i]);\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points\n//! at previous time\nmat3d FESolidDomain::gradientp(FESolidElement& el, vec3d* fn, int n)\n{\n    double Ji[3][3];\n    invjactp(el, Ji, n);\n\t\t\t\t\n    vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n    vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n    vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n    \n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    mat3d gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gradf += fn[i] & (g1*Gr[i] + g2*Gs[i] + g3*Gt[i]);\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points\ntens3dls FESolidDomain::gradient(FESolidElement& el, mat3ds* fn, int n)\n{\n    double Ji[3][3];\n    invjact(el, Ji, n);\n    \n    vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n    vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n    vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n    \n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    tens3dls gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gradf += dyad3ls(fn[i], (g1*Gr[i] + g2*Gs[i] + g3*Gt[i]));\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate spatial gradient of function at integration points\n//! at previous time\ntens3dls FESolidDomain::gradientp(FESolidElement& el, mat3ds* fn, int n)\n{\n    double Ji[3][3];\n    invjactp(el, Ji, n);\n    \n    vec3d g1(Ji[0][0],Ji[0][1],Ji[0][2]);\n    vec3d g2(Ji[1][0],Ji[1][1],Ji[1][2]);\n    vec3d g3(Ji[2][0],Ji[2][1],Ji[2][2]);\n    \n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    tens3dls gradf;\n    gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gradf += dyad3ls(fn[i], (g1*Gr[i] + g2*Gs[i] + g3*Gt[i]));\n    \n    return gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate material gradient of function at integration points\nvec3d FESolidDomain::Gradient(FESolidElement& el, vector<double>& fn, int n)\n{\n    double Ji[3][3];\n    invjac0(el, Ji, n);\n    \n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    double Gx, Gy, Gz;\n    \n    vec3d Gradf;\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i]+Ji[2][0]*Gtn[i];\n        Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i]+Ji[2][1]*Gtn[i];\n        Gz = Ji[0][2]*Grn[i]+Ji[1][2]*Gsn[i]+Ji[2][2]*Gtn[i];\n        \n        // calculate pressure gradient\n        Gradf.x += Gx*fn[i];\n        Gradf.y += Gy*fn[i];\n        Gradf.z += Gz*fn[i];\n    }\n    \n    return Gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate material gradient of function at integration points\nvec3d FESolidDomain::Gradient(FESolidElement& el, double* fn, int n)\n{\n    double Ji[3][3];\n    invjac0(el, Ji, n);\n    \n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    double Gx, Gy, Gz;\n    \n    vec3d Gradf;\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        Gx = Ji[0][0]*Grn[i]+Ji[1][0]*Gsn[i]+Ji[2][0]*Gtn[i];\n        Gy = Ji[0][1]*Grn[i]+Ji[1][1]*Gsn[i]+Ji[2][1]*Gtn[i];\n        Gz = Ji[0][2]*Grn[i]+Ji[1][2]*Gsn[i]+Ji[2][2]*Gtn[i];\n        \n        // calculate pressure gradient\n        Gradf.x += Gx*fn[i];\n        Gradf.y += Gy*fn[i];\n        Gradf.z += Gz*fn[i];\n    }\n    \n    return Gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate material gradient of function at integration points\nmat3d FESolidDomain::Gradient(FESolidElement& el, vec3d* fn, int n)\n{\n    vec3d Gcnt[3];\n    ContraBaseVectors0(el, n, Gcnt);\n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    mat3d Gradf;\n    Gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        Gradf += fn[i] & (Gcnt[0]*Gr[i] + Gcnt[1]*Gs[i] + Gcnt[2]*Gt[i]);\n    \n    return Gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate material gradient of function at integration points\ntens3dls FESolidDomain::Gradient(FESolidElement& el, mat3ds* fn, int n)\n{\n    vec3d Gcnt[3];\n    ContraBaseVectors0(el, n, Gcnt);\n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    tens3dls Gradf;\n    Gradf.zero();\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        Gradf += dyad3ls(fn[i], (Gcnt[0]*Gr[i] + Gcnt[1]*Gs[i] + Gcnt[2]*Gt[i]));\n    \n    return Gradf;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to current frame\ndouble FESolidDomain::detJt(FESolidElement &el, int n)\n{\n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, rt);\n\n    // shape function derivatives\n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    // jacobian matrix\n    double J[3][3] = {0};\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        const double& Gri = Grn[i];\n        const double& Gsi = Gsn[i];\n        const double& Gti = Gtn[i];\n        \n        const double& x = rt[i].x;\n        const double& y = rt[i].y;\n        const double& z = rt[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to current frame\ndouble FESolidDomain::detJt(FESolidElement &el, int n, const double alpha)\n{\n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, rt, alpha);\n\n    // shape function derivatives\n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    // jacobian matrix\n\tint neln = el.Nodes();\n\tdouble J[3][3] = { 0 };\n    for (int i=0; i<neln; ++i)\n    {\n        const double& Gri = Grn[i];\n        const double& Gsi = Gsn[i];\n        const double& Gti = Gtn[i];\n        \n        const double& x = rt[i].x;\n        const double& y = rt[i].y;\n        const double& z = rt[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n    + J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n    + J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate jacobian with respect to reference frame\ndouble FESolidDomain::detJ0(FESolidElement &el, int n)\n{\n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n\tGetReferenceNodalCoordinates(el, r0);\n\n    // shape function derivatives\n    double* Grn = el.Gr(n);\n    double* Gsn = el.Gs(n);\n    double* Gtn = el.Gt(n);\n    \n    // jacobian matrix\n    double J[3][3] = {0};\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i)\n    {\n        const double& Gri = Grn[i];\n        const double& Gsi = Gsn[i];\n        const double& Gti = Gtn[i];\n        \n        const double& x = r0[i].x;\n        const double& y = r0[i].y;\n        const double& z = r0[i].z;\n        \n        J[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n        J[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n        J[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n    }\n    \n    // calculate the determinant\n    double det =  J[0][0]*(J[1][1]*J[2][2] - J[1][2]*J[2][1])\n\t\t\t\t+ J[0][1]*(J[1][2]*J[2][0] - J[2][2]*J[1][0])\n\t\t\t\t+ J[0][2]*(J[1][0]*J[2][1] - J[1][1]*J[2][0]);\n    \n    return det;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant basis vectors of a solid element\n//! in reference configuration at an integration point\n\nvoid FESolidDomain::CoBaseVectors0(FESolidElement& el, int j, vec3d g[3])\n{\n    // get the shape function derivatives\n    double* Hr = el.Gr(j);\n    double* Hs = el.Gs(j);\n    double* Ht = el.Gt(j);\n    \n    // nodal coordinates\n    vec3d r0[FEElement::MAX_NODES];\n\tGetReferenceNodalCoordinates(el, r0);\n    \n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n\tint n = el.Nodes();\n\tfor (int i = 0; i<n; ++i)\n    {\n        g[0] += r0[i]*Hr[i];\n        g[1] += r0[i]*Hs[i];\n        g[2] += r0[i]*Ht[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant basis vectors of a solid element\n//! at an integration point\n\nvoid FESolidDomain::CoBaseVectors(FESolidElement& el, int j, vec3d g[3])\n{\n    // get the shape function derivatives\n    double* Hr = el.Gr(j);\n    double* Hs = el.Gs(j);\n    double* Ht = el.Gt(j);\n    \n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n\tGetCurrentNodalCoordinates(el, rt);\n\n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n\tint n = el.Nodes();\n\tfor (int i = 0; i<n; ++i)\n    {\n        g[0] += rt[i]*Hr[i];\n        g[1] += rt[i]*Hs[i];\n        g[2] += rt[i]*Ht[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant basis vectors of a solid element\n//! at an integration point\n\nvoid FESolidDomain::CoBaseVectors(FESolidElement& el, int j, vec3d g[3], const double alpha)\n{\n    // get the shape function derivatives\n    double* Hr = el.Gr(j);\n    double* Hs = el.Gs(j);\n    double* Ht = el.Gt(j);\n    \n    // nodal coordinates\n    vec3d rt[FEElement::MAX_NODES];\n    GetCurrentNodalCoordinates(el, rt, alpha);\n    \n    g[0] = g[1] = g[2] = vec3d(0,0,0);\n    int n = el.Nodes();\n    for (int i = 0; i<n; ++i)\n    {\n        g[0] += rt[i]*Hr[i];\n        g[1] += rt[i]*Hs[i];\n        g[2] += rt[i]*Ht[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contravariant basis vectors in ref config\n//! of a solid element at an integration point\n\nvoid FESolidDomain::ContraBaseVectors0(FESolidElement& el, int j, vec3d gcnt[3])\n{\n    vec3d gcov[3];\n    CoBaseVectors0(el, j, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contravariant basis vectors of a solid element\n//! at an integration point\n\nvoid FESolidDomain::ContraBaseVectors(FESolidElement& el, int j, vec3d gcnt[3])\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, j, gcov);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the contravariant basis vectors of a solid element\n//! at an integration point\n\nvoid FESolidDomain::ContraBaseVectors(FESolidElement& el, int j, vec3d gcnt[3], const double alpha)\n{\n    vec3d gcov[3];\n    CoBaseVectors(el, j, gcov, alpha);\n    \n    mat3d J = mat3d(gcov[0].x, gcov[1].x, gcov[2].x,\n                    gcov[0].y, gcov[1].y, gcov[2].y,\n                    gcov[0].z, gcov[1].z, gcov[2].z);\n    mat3d Ji = J.inverse();\n    \n    gcnt[0] = vec3d(Ji(0,0),Ji(0,1),Ji(0,2));\n    gcnt[1] = vec3d(Ji(1,0),Ji(1,1),Ji(1,2));\n    gcnt[2] = vec3d(Ji(2,0),Ji(2,1),Ji(2,2));\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of covariant basis\n//! vectors of a solid element at an integration point\n\nvoid FESolidDomain::CoBaseVectorDerivatives(FESolidElement& el, int j, vec3d dg[3][3])\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hrr = el.Grr(j); double* Hrs = el.Grs(j); double* Hrt = el.Grt(j);\n    double* Hsr = el.Gsr(j); double* Hss = el.Gss(j); double* Hst = el.Gst(j);\n    double* Htr = el.Gtr(j); double* Hts = el.Gts(j); double* Htt = el.Gtt(j);\n    \n    dg[0][0] = dg[0][1] = dg[0][2] = vec3d(0,0,0);  // derivatives of g[0]\n    dg[1][0] = dg[1][1] = dg[1][2] = vec3d(0,0,0);  // derivatives of g[1]\n    dg[2][0] = dg[2][1] = dg[2][2] = vec3d(0,0,0);  // derivatives of g[2]\n    \n    for (int i=0; i<n; ++i)\n    {\n        vec3d rt = m.Node(el.m_node[i]).m_rt;\n        dg[0][0] += rt*Hrr[i]; dg[0][1] += rt*Hsr[i]; dg[0][2] += rt*Htr[i];\n        dg[1][0] += rt*Hrs[i]; dg[1][1] += rt*Hss[i]; dg[1][2] += rt*Hts[i];\n        dg[2][0] += rt*Hrt[i]; dg[2][1] += rt*Hst[i]; dg[2][2] += rt*Htt[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of covariant basis\n//! vectors of a solid element at an integration point\n\nvoid FESolidDomain::CoBaseVectorDerivatives0(FESolidElement& el, int j, vec3d dg[3][3])\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hrr = el.Grr(j); double* Hrs = el.Grs(j); double* Hrt = el.Grt(j);\n    double* Hsr = el.Gsr(j); double* Hss = el.Gss(j); double* Hst = el.Gst(j);\n    double* Htr = el.Gtr(j); double* Hts = el.Gts(j); double* Htt = el.Gtt(j);\n    \n    dg[0][0] = dg[0][1] = dg[0][2] = vec3d(0,0,0);  // derivatives of g[0]\n    dg[1][0] = dg[1][1] = dg[1][2] = vec3d(0,0,0);  // derivatives of g[1]\n    dg[2][0] = dg[2][1] = dg[2][2] = vec3d(0,0,0);  // derivatives of g[2]\n    \n    for (int i=0; i<n; ++i)\n    {\n        vec3d rt = m.Node(el.m_node[i]).m_r0;\n        dg[0][0] += rt*Hrr[i]; dg[0][1] += rt*Hsr[i]; dg[0][2] += rt*Htr[i];\n        dg[1][0] += rt*Hrs[i]; dg[1][1] += rt*Hss[i]; dg[1][2] += rt*Hts[i];\n        dg[2][0] += rt*Hrt[i]; dg[2][1] += rt*Hst[i]; dg[2][2] += rt*Htt[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of covariant basis\n//! vectors of a solid element at an integration point\n\nvoid FESolidDomain::CoBaseVectorDerivatives(FESolidElement& el, int j, vec3d dg[3][3], const double alpha)\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hrr = el.Grr(j); double* Hrs = el.Grs(j); double* Hrt = el.Grt(j);\n    double* Hsr = el.Gsr(j); double* Hss = el.Gss(j); double* Hst = el.Gst(j);\n    double* Htr = el.Gtr(j); double* Hts = el.Gts(j); double* Htt = el.Gtt(j);\n    \n    dg[0][0] = dg[0][1] = dg[0][2] = vec3d(0,0,0);  // derivatives of g[0]\n    dg[1][0] = dg[1][1] = dg[1][2] = vec3d(0,0,0);  // derivatives of g[1]\n    dg[2][0] = dg[2][1] = dg[2][2] = vec3d(0,0,0);  // derivatives of g[2]\n    \n    for (int i=0; i<n; ++i)\n    {\n        vec3d rt = m.Node(el.m_node[i]).m_rt*alpha + m.Node(el.m_node[i]).m_rp*(1.0-alpha);\n        dg[0][0] += rt*Hrr[i]; dg[0][1] += rt*Hsr[i]; dg[0][2] += rt*Htr[i];\n        dg[1][0] += rt*Hrs[i]; dg[1][1] += rt*Hss[i]; dg[1][2] += rt*Hts[i];\n        dg[2][0] += rt*Hrt[i]; dg[2][1] += rt*Hst[i]; dg[2][2] += rt*Htt[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of contravariant basis\n//! vectors of a solid element at an integration point\n\nvoid FESolidDomain::ContraBaseVectorDerivatives(FESolidElement& el, int j, vec3d dgcnt[3][3])\n{\n    vec3d gcnt[3];\n    vec3d dgcov[3][3];\n    ContraBaseVectors(el, j, gcnt);\n    CoBaseVectorDerivatives(el, j, dgcov);\n    \n    // derivatives of gcnt[0]\n    dgcnt[0][0] = -gcnt[0]*(gcnt[0]*dgcov[0][0])-gcnt[1]*(gcnt[0]*dgcov[1][0])-gcnt[2]*(gcnt[0]*dgcov[2][0]);\n    dgcnt[0][1] = -gcnt[0]*(gcnt[0]*dgcov[0][1])-gcnt[1]*(gcnt[0]*dgcov[1][1])-gcnt[2]*(gcnt[0]*dgcov[2][1]);\n    dgcnt[0][2] = -gcnt[0]*(gcnt[0]*dgcov[0][2])-gcnt[1]*(gcnt[0]*dgcov[1][2])-gcnt[2]*(gcnt[0]*dgcov[2][2]);\n    \n    // derivatives of gcnt[1]\n    dgcnt[1][0] = -gcnt[0]*(gcnt[1]*dgcov[0][0])-gcnt[1]*(gcnt[1]*dgcov[1][0])-gcnt[2]*(gcnt[1]*dgcov[2][0]);\n    dgcnt[1][1] = -gcnt[0]*(gcnt[1]*dgcov[0][1])-gcnt[1]*(gcnt[1]*dgcov[1][1])-gcnt[2]*(gcnt[1]*dgcov[2][1]);\n    dgcnt[1][2] = -gcnt[0]*(gcnt[1]*dgcov[0][2])-gcnt[1]*(gcnt[1]*dgcov[1][2])-gcnt[2]*(gcnt[1]*dgcov[2][2]);\n    \n    // derivatives of gcnt[2]\n    dgcnt[2][0] = -gcnt[0]*(gcnt[2]*dgcov[0][0])-gcnt[1]*(gcnt[2]*dgcov[1][0])-gcnt[2]*(gcnt[2]*dgcov[2][0]);\n    dgcnt[2][1] = -gcnt[0]*(gcnt[2]*dgcov[0][1])-gcnt[1]*(gcnt[2]*dgcov[1][1])-gcnt[2]*(gcnt[2]*dgcov[2][1]);\n    dgcnt[2][2] = -gcnt[0]*(gcnt[2]*dgcov[0][2])-gcnt[1]*(gcnt[2]*dgcov[1][2])-gcnt[2]*(gcnt[2]*dgcov[2][2]);\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of contravariant basis\n//! vectors of a solid element at an integration point\n\nvoid FESolidDomain::ContraBaseVectorDerivatives0(FESolidElement& el, int j, vec3d dgcnt[3][3])\n{\n    vec3d gcnt[3];\n    vec3d dgcov[3][3];\n    ContraBaseVectors0(el, j, gcnt);\n    CoBaseVectorDerivatives0(el, j, dgcov);\n    \n    // derivatives of gcnt[0]\n    dgcnt[0][0] = -gcnt[0]*(gcnt[0]*dgcov[0][0])-gcnt[1]*(gcnt[0]*dgcov[1][0])-gcnt[2]*(gcnt[0]*dgcov[2][0]);\n    dgcnt[0][1] = -gcnt[0]*(gcnt[0]*dgcov[0][1])-gcnt[1]*(gcnt[0]*dgcov[1][1])-gcnt[2]*(gcnt[0]*dgcov[2][1]);\n    dgcnt[0][2] = -gcnt[0]*(gcnt[0]*dgcov[0][2])-gcnt[1]*(gcnt[0]*dgcov[1][2])-gcnt[2]*(gcnt[0]*dgcov[2][2]);\n    \n    // derivatives of gcnt[1]\n    dgcnt[1][0] = -gcnt[0]*(gcnt[1]*dgcov[0][0])-gcnt[1]*(gcnt[1]*dgcov[1][0])-gcnt[2]*(gcnt[1]*dgcov[2][0]);\n    dgcnt[1][1] = -gcnt[0]*(gcnt[1]*dgcov[0][1])-gcnt[1]*(gcnt[1]*dgcov[1][1])-gcnt[2]*(gcnt[1]*dgcov[2][1]);\n    dgcnt[1][2] = -gcnt[0]*(gcnt[1]*dgcov[0][2])-gcnt[1]*(gcnt[1]*dgcov[1][2])-gcnt[2]*(gcnt[1]*dgcov[2][2]);\n    \n    // derivatives of gcnt[2]\n    dgcnt[2][0] = -gcnt[0]*(gcnt[2]*dgcov[0][0])-gcnt[1]*(gcnt[2]*dgcov[1][0])-gcnt[2]*(gcnt[2]*dgcov[2][0]);\n    dgcnt[2][1] = -gcnt[0]*(gcnt[2]*dgcov[0][1])-gcnt[1]*(gcnt[2]*dgcov[1][1])-gcnt[2]*(gcnt[2]*dgcov[2][1]);\n    dgcnt[2][2] = -gcnt[0]*(gcnt[2]*dgcov[0][2])-gcnt[1]*(gcnt[2]*dgcov[1][2])-gcnt[2]*(gcnt[2]*dgcov[2][2]);\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the parametric derivatives of contravariant basis\n//! vectors of a solid element at an integration point\n\nvoid FESolidDomain::ContraBaseVectorDerivatives(FESolidElement& el, int j, vec3d dgcnt[3][3], const double alpha)\n{\n    vec3d gcnt[3];\n    vec3d dgcov[3][3];\n    ContraBaseVectors(el, j, gcnt, alpha);\n    CoBaseVectorDerivatives(el, j, dgcov, alpha);\n    \n    // derivatives of gcnt[0]\n    dgcnt[0][0] = -gcnt[0]*(gcnt[0]*dgcov[0][0])-gcnt[1]*(gcnt[0]*dgcov[1][0])-gcnt[2]*(gcnt[0]*dgcov[2][0]);\n    dgcnt[0][1] = -gcnt[0]*(gcnt[0]*dgcov[0][1])-gcnt[1]*(gcnt[0]*dgcov[1][1])-gcnt[2]*(gcnt[0]*dgcov[2][1]);\n    dgcnt[0][2] = -gcnt[0]*(gcnt[0]*dgcov[0][2])-gcnt[1]*(gcnt[0]*dgcov[1][2])-gcnt[2]*(gcnt[0]*dgcov[2][2]);\n    \n    // derivatives of gcnt[1]\n    dgcnt[1][0] = -gcnt[0]*(gcnt[1]*dgcov[0][0])-gcnt[1]*(gcnt[1]*dgcov[1][0])-gcnt[2]*(gcnt[1]*dgcov[2][0]);\n    dgcnt[1][1] = -gcnt[0]*(gcnt[1]*dgcov[0][1])-gcnt[1]*(gcnt[1]*dgcov[1][1])-gcnt[2]*(gcnt[1]*dgcov[2][1]);\n    dgcnt[1][2] = -gcnt[0]*(gcnt[1]*dgcov[0][2])-gcnt[1]*(gcnt[1]*dgcov[1][2])-gcnt[2]*(gcnt[1]*dgcov[2][2]);\n    \n    // derivatives of gcnt[2]\n    dgcnt[2][0] = -gcnt[0]*(gcnt[2]*dgcov[0][0])-gcnt[1]*(gcnt[2]*dgcov[1][0])-gcnt[2]*(gcnt[2]*dgcov[2][0]);\n    dgcnt[2][1] = -gcnt[0]*(gcnt[2]*dgcov[0][1])-gcnt[1]*(gcnt[2]*dgcov[1][1])-gcnt[2]*(gcnt[2]*dgcov[2][1]);\n    dgcnt[2][2] = -gcnt[0]*(gcnt[2]*dgcov[0][2])-gcnt[1]*(gcnt[2]*dgcov[1][2])-gcnt[2]*(gcnt[2]*dgcov[2][2]);\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the laplacian of a vector function at an integration point\nvec3d FESolidDomain::lapvec(FESolidElement& el, vec3d* fn, int n)\n{\n    vec3d gcnt[3];\n    vec3d dgcnt[3][3];\n    ContraBaseVectors(el, n, gcnt);\n    ContraBaseVectorDerivatives(el, n, dgcnt);\n\t\t\t\t\n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    // get the shape function derivatives\n    double* Hrr = el.Grr(n); double* Hrs = el.Grs(n); double* Hrt = el.Grt(n);\n    double* Hsr = el.Gsr(n); double* Hss = el.Gss(n); double* Hst = el.Gst(n);\n    double* Htr = el.Gtr(n); double* Hts = el.Gts(n); double* Htt = el.Gtt(n);\n    \n    vec3d lapv(0,0,0);\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        lapv += fn[i]*((gcnt[0]*Hrr[i]+dgcnt[0][0]*Gr[i])*gcnt[0]+\n                       (gcnt[0]*Hrs[i]+dgcnt[0][1]*Gr[i])*gcnt[1]+\n                       (gcnt[0]*Hrt[i]+dgcnt[0][2]*Gr[i])*gcnt[2]+\n                       (gcnt[1]*Hsr[i]+dgcnt[1][0]*Gs[i])*gcnt[0]+\n                       (gcnt[1]*Hss[i]+dgcnt[1][1]*Gs[i])*gcnt[1]+\n                       (gcnt[1]*Hst[i]+dgcnt[1][2]*Gs[i])*gcnt[2]+\n                       (gcnt[2]*Htr[i]+dgcnt[2][0]*Gt[i])*gcnt[0]+\n                       (gcnt[2]*Hts[i]+dgcnt[2][1]*Gt[i])*gcnt[1]+\n                       (gcnt[2]*Htt[i]+dgcnt[2][2]*Gt[i])*gcnt[2]);\n    \n    return lapv;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the gradient of the divergence of a vector function at an integration point\nvec3d FESolidDomain::gradivec(FESolidElement& el, vec3d* fn, int n)\n{\n    vec3d gcnt[3];\n    vec3d dgcnt[3][3];\n    ContraBaseVectors(el, n, gcnt);\n    ContraBaseVectorDerivatives(el, n, dgcnt);\n\t\t\t\t\n    double* Gr = el.Gr(n);\n    double* Gs = el.Gs(n);\n    double* Gt = el.Gt(n);\n    \n    // get the shape function derivatives\n    double* Hrr = el.Grr(n); double* Hrs = el.Grs(n); double* Hrt = el.Grt(n);\n    double* Hsr = el.Gsr(n); double* Hss = el.Gss(n); double* Hst = el.Gst(n);\n    double* Htr = el.Gtr(n); double* Hts = el.Gts(n); double* Htt = el.Gtt(n);\n    \n    vec3d gdv(0,0,0);\n    int N = el.Nodes();\n    for (int i=0; i<N; ++i)\n        gdv +=\n        gcnt[0]*((gcnt[0]*Hrr[i]+dgcnt[0][0]*Gr[i])*fn[i])+\n        gcnt[1]*((gcnt[0]*Hrs[i]+dgcnt[0][1]*Gr[i])*fn[i])+\n        gcnt[2]*((gcnt[0]*Hrt[i]+dgcnt[0][2]*Gr[i])*fn[i])+\n        gcnt[0]*((gcnt[1]*Hsr[i]+dgcnt[1][0]*Gs[i])*fn[i])+\n        gcnt[1]*((gcnt[1]*Hss[i]+dgcnt[1][1]*Gs[i])*fn[i])+\n        gcnt[2]*((gcnt[1]*Hst[i]+dgcnt[1][2]*Gs[i])*fn[i])+\n        gcnt[0]*((gcnt[2]*Htr[i]+dgcnt[2][0]*Gt[i])*fn[i])+\n        gcnt[1]*((gcnt[2]*Hts[i]+dgcnt[2][1]*Gt[i])*fn[i])+\n        gcnt[2]*((gcnt[2]*Htt[i]+dgcnt[2][2]*Gt[i])*fn[i]);\n    \n    return gdv;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the transpose of the spatial gradient of the shape\n//! function spatial gradients, gradT(grad H), at an integration point\n\nvoid FESolidDomain::gradTgradShape(FESolidElement& el, int j, vector<mat3d>& mn)\n{\n    int N = el.Nodes();\n    vec3d g[3], dg[3][3];\n    ContraBaseVectors(el, j, g);\n    ContraBaseVectorDerivatives(el, j, dg);\n    \n    // get the shape functions\n    double* Hr = el.Gr(j);\n    double* Hs = el.Gs(j);\n    double* Ht = el.Gt(j);\n    \n    // get the shape function derivatives\n    double* Hrr = el.Grr(j); double* Hrs = el.Grs(j); double* Hrt = el.Grt(j);\n    double* Hsr = el.Gsr(j); double* Hss = el.Gss(j); double* Hst = el.Gst(j);\n    double* Htr = el.Gtr(j); double* Hts = el.Gts(j); double* Htt = el.Gtt(j);\n    \n    for (int i=0; i<N; ++i) {\n        mn[i] = ((g[0] & dg[0][0]) + (g[1] & dg[0][1]) + (g[2] & dg[0][2]))*Hr[i]\n        + ((g[0] & dg[1][0]) + (g[1] & dg[1][1]) + (g[2] & dg[1][2]))*Hs[i]\n        + ((g[0] & dg[2][0]) + (g[1] & dg[2][1]) + (g[2] & dg[2][2]))*Ht[i]\n        + (g[0] & g[0])*Hrr[i] + (g[1] & g[0])*Hsr[i] + (g[2] & g[0])*Htr[i]\n        + (g[0] & g[1])*Hrs[i] + (g[1] & g[1])*Hss[i] + (g[2] & g[1])*Hts[i]\n        + (g[0] & g[2])*Hrt[i] + (g[1] & g[2])*Hst[i] + (g[2] & g[2])*Htt[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolidDomain::ShapeGradient(FESolidElement& el, int n, vec3d* GradH)\n{\n    // calculate jacobian\n    double Ji[3][3];\n    double detJt = invjact(el, Ji, n);\n    \n    // evaluate shape function derivatives\n    int ne = el.Nodes();\n    for (int i = 0; i<ne; ++i)\n    {\n        double Gr = el.Gr(n)[i];\n        double Gs = el.Gs(n)[i];\n        double Gt = el.Gt(n)[i];\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        GradH[i].x = Ji[0][0] * Gr + Ji[1][0] * Gs + Ji[2][0] * Gt;\n        GradH[i].y = Ji[0][1] * Gr + Ji[1][1] * Gs + Ji[2][1] * Gt;\n        GradH[i].z = Ji[0][2] * Gr + Ji[1][2] * Gs + Ji[2][2] * Gt;\n    }\n    \n    return detJt;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolidDomain::ShapeGradient(FESolidElement& el, int n, vec3d* GradH, const double alpha)\n{\n    // calculate jacobian\n    double Ji[3][3];\n    double detJt = invjact(el, Ji, n, alpha);\n    \n    // evaluate shape function derivatives\n    int ne = el.Nodes();\n    for (int i = 0; i<ne; ++i)\n    {\n        double Gr = el.Gr(n)[i];\n        double Gs = el.Gs(n)[i];\n        double Gt = el.Gt(n)[i];\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        GradH[i].x = Ji[0][0] * Gr + Ji[1][0] * Gs + Ji[2][0] * Gt;\n        GradH[i].y = Ji[0][1] * Gr + Ji[1][1] * Gs + Ji[2][1] * Gt;\n        GradH[i].z = Ji[0][2] * Gr + Ji[1][2] * Gs + Ji[2][2] * Gt;\n    }\n    \n    return detJt;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolidDomain::ShapeGradient0(FESolidElement& el, int n, vec3d* GradH)\n{\n    // calculate jacobian\n    double Ji[3][3];\n    double detJ0 = invjac0(el, Ji, n);\n    \n    // evaluate shape function derivatives\n    int ne = el.Nodes();\n    for (int i = 0; i<ne; ++i)\n    {\n        double Gr = el.Gr(n)[i];\n        double Gs = el.Gs(n)[i];\n        double Gt = el.Gt(n)[i];\n        \n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        GradH[i].x = Ji[0][0] * Gr + Ji[1][0] * Gs + Ji[2][0] * Gt;\n        GradH[i].y = Ji[0][1] * Gr + Ji[1][1] * Gs + Ji[2][1] * Gt;\n        GradH[i].z = Ji[0][2] * Gr + Ji[1][2] * Gs + Ji[2][2] * Gt;\n    }\n    \n    return detJ0;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolidDomain::ShapeGradient(FESolidElement& el, double r, double s, double t, vec3d* GradH)\n{\n    // calculate jacobian\n    double Ji[3][3];\n    double detJt = invjact(el, Ji, r, s, t);\n    \n    // shape function derivatives\n    double Gr[FEElement::MAX_NODES];\n    double Gs[FEElement::MAX_NODES];\n    double Gt[FEElement::MAX_NODES];\n    el.shape_deriv(Gr, Gs, Gt, r, s, t);\n    \n    int neln = el.Nodes();\n    for (int i = 0; i<neln; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        GradH[i].x = Ji[0][0] * Gr[i] + Ji[1][0] * Gs[i] + Ji[2][0] * Gt[i];\n        GradH[i].y = Ji[0][1] * Gr[i] + Ji[1][1] * Gs[i] + Ji[2][1] * Gt[i];\n        GradH[i].z = Ji[0][2] * Gr[i] + Ji[1][2] * Gs[i] + Ji[2][2] * Gt[i];\n    }\n    \n    return detJt;\n}\n\n//-----------------------------------------------------------------------------\ndouble FESolidDomain::ShapeGradient0(FESolidElement& el, double r, double s, double t, vec3d* GradH)\n{\n    // calculate jacobian\n    double Ji[3][3];\n    double detJ0 = invjac0(el, Ji, r, s, t);\n    \n    // shape function derivatives\n    double Gr[FEElement::MAX_NODES];\n    double Gs[FEElement::MAX_NODES];\n    double Gt[FEElement::MAX_NODES];\n    el.shape_deriv(Gr, Gs, Gt, r, s, t);\n    \n    int neln = el.Nodes();\n    for (int i = 0; i<neln; ++i)\n    {\n        // calculate global gradient of shape functions\n        // note that we need the transposed of Ji, not Ji itself !\n        GradH[i].x = Ji[0][0] * Gr[i] + Ji[1][0] * Gs[i] + Ji[2][0] * Gt[i];\n        GradH[i].y = Ji[0][1] * Gr[i] + Ji[1][1] * Gs[i] + Ji[2][1] * Gt[i];\n        GradH[i].z = Ji[0][2] * Gr[i] + Ji[1][2] * Gs[i] + Ji[2][2] * Gt[i];\n    }\n    \n    return detJ0;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the volume of an element\ndouble FESolidDomain::Volume(FESolidElement& el)\n{\n\tvec3d r0[FEElement::MAX_NODES];\n\n\tint neln = el.Nodes();\n\tfor (int i = 0; i<neln; ++i) r0[i] = Node(el.m_lnode[i]).m_r0;\n\n\tint nint = el.GaussPoints();\n\tdouble *w = el.GaussWeights();\n\tdouble V = 0;\n\tfor (int n = 0; n<nint; ++n)\n\t{\n\t\t// shape function derivatives\n\t\tdouble* Grn = el.Gr(n);\n\t\tdouble* Gsn = el.Gs(n);\n\t\tdouble* Gtn = el.Gt(n);\n\n\t\t// jacobian matrix\n\t\tdouble J[3][3] = { 0 };\n\t\tfor (int i = 0; i<neln; ++i)\n\t\t{\n\t\t\tconst double& Gri = Grn[i];\n\t\t\tconst double& Gsi = Gsn[i];\n\t\t\tconst double& Gti = Gtn[i];\n\n\t\t\tconst double& x = r0[i].x;\n\t\t\tconst double& y = r0[i].y;\n\t\t\tconst double& z = r0[i].z;\n\n\t\t\tJ[0][0] += Gri*x; J[0][1] += Gsi*x; J[0][2] += Gti*x;\n\t\t\tJ[1][0] += Gri*y; J[1][1] += Gsi*y; J[1][2] += Gti*y;\n\t\t\tJ[2][0] += Gri*z; J[2][1] += Gsi*z; J[2][2] += Gti*z;\n\t\t}\n\n\t\t// calculate the determinant\n\t\tdouble detJ0 = J[0][0] * (J[1][1] * J[2][2] - J[1][2] * J[2][1])\n\t\t\t+ J[0][1] * (J[1][2] * J[2][0] - J[2][2] * J[1][0])\n\t\t\t+ J[0][2] * (J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n\t\tV += detJ0*w[n];\n\t}\n\n\treturn V;\n}\n\n//-----------------------------------------------------------------------------\n//! calculate the volume of an element\ndouble FESolidDomain::CurrentVolume(FESolidElement& el)\n{\n    vec3d rt[FEElement::MAX_NODES];\n\n    int neln = el.Nodes();\n    for (int i = 0; i < neln; ++i) rt[i] = Node(el.m_lnode[i]).m_rt;\n\n    int nint = el.GaussPoints();\n    double* w = el.GaussWeights();\n    double V = 0;\n    for (int n = 0; n < nint; ++n)\n    {\n        // shape function derivatives\n        double* Grn = el.Gr(n);\n        double* Gsn = el.Gs(n);\n        double* Gtn = el.Gt(n);\n\n        // jacobian matrix\n        double J[3][3] = { 0 };\n        for (int i = 0; i < neln; ++i)\n        {\n            const double& Gri = Grn[i];\n            const double& Gsi = Gsn[i];\n            const double& Gti = Gtn[i];\n\n            const double& x = rt[i].x;\n            const double& y = rt[i].y;\n            const double& z = rt[i].z;\n\n            J[0][0] += Gri * x; J[0][1] += Gsi * x; J[0][2] += Gti * x;\n            J[1][0] += Gri * y; J[1][1] += Gsi * y; J[1][2] += Gti * y;\n            J[2][0] += Gri * z; J[2][1] += Gsi * z; J[2][2] += Gti * z;\n        }\n\n        // calculate the determinant\n        double detJ0 = J[0][0] * (J[1][1] * J[2][2] - J[1][2] * J[2][1])\n            + J[0][1] * (J[1][2] * J[2][0] - J[2][2] * J[1][0])\n            + J[0][2] * (J[1][0] * J[2][1] - J[1][1] * J[2][0]);\n\n        V += detJ0 * w[n];\n    }\n\n    return V;\n}\n\n//-----------------------------------------------------------------------------\n//! return the degrees of freedom of an element for this domain\nint FESolidDomain::GetElementDofs(FESolidElement& el)\n{\n\treturn (int)GetDOFList().Size()*el.Nodes();\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate an integral over the domain and assemble into global load vector\nvoid FESolidDomain::LoadVector(\n\tFEGlobalVector& R,\t\t\t// the global vector to assembe the load vector in\n\tconst FEDofList& dofList,\t// the degree of freedom list\n\tFEVolumeVectorIntegrand f\t// the actual integrand function\n)\n{\n\tFEMesh& mesh = *GetMesh();\n\n\t// degrees of freedom per node\n\tint dofPerNode = dofList.Size();\n\n\t// loop over all the elements\n\tint NE = Elements();\n\t#pragma omp parallel for \n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESolidElement& el = Element(i);\n\t\tint neln = el.Nodes();\n\n\t\t// only consider active elements\n\t\tif (el.isActive()) \n\t\t{\n\t\t\tstd::vector<double> val(dofPerNode, 0.0);\n\n\t\t\t// total size of the element vector\n\t\t\tint ndof = dofPerNode * el.Nodes();\n\n\t\t\t// setup the element vector\n\t\t\tvector<double> fe;\n\t\t\tfe.assign(ndof, 0);\n\n\t\t\t// loop over integration points\n\t\t\tdouble* w = el.GaussWeights();\n\t\t\tint nint = el.GaussPoints();\n\t\t\tfor (int n = 0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t\tmp.m_Jt = detJt(el, n);\n\t\t\t\tmp.m_shape = el.H(n);\n\n\t\t\t\t// loop over all nodes\n\t\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t\t{\n\t\t\t\t\t// get the value of the integrand for this node\n\t\t\t\t\tf(mp, j, val);\n\n\t\t\t\t\t// add it all up\n\t\t\t\t\tfor (int k=0; k<dofPerNode; ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tfe[dofPerNode*j + k] += val[k] * w[n];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get the element's LM vector\n\t\t\tvector<int> lm(ndof, -1);\n\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(el.m_node[j]);\n\t\t\t\tvector<int>& ID = node.m_ID;\n\t\t\t\tfor (int k = 0; k < dofPerNode; ++k)\n\t\t\t\t{\n\t\t\t\t\tlm[dofPerNode*j + k] = ID[dofList[k]];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Assemble into global vector\n\t\t\tR.Assemble(el.m_node, lm, fe);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolidDomain::LoadStiffness(FELinearSystem& LS, const FEDofList& dofList_a, const FEDofList& dofList_b, FEVolumeMatrixIntegrand f)\n{\n#pragma omp parallel shared(f)\n\t{\n\tFEElementMatrix ke;\n\n\tint dofPerNode_a = dofList_a.Size();\n\tint dofPerNode_b = dofList_b.Size();\n\n\tFEMesh& mesh = *GetMesh();\n\tvec3d rt[FEElement::MAX_NODES];\n\n\tmatrix kab(dofPerNode_a, dofPerNode_b);\n\n\tint NE = Elements();\n\t#pragma omp for nowait\n\tfor (int m = 0; m < NE; ++m)\n\t{\n\t\t// get the element\n\t\tFESolidElement& el = Element(m);\n\t\tif (el.isActive())\n\t\t{\n\t\t\t// calculate nodal normal tractions\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// get the element stiffness matrix\n\t\t\tke.SetNodes(el.m_node);\n\t\t\tint ndof_a = dofPerNode_a * neln;\n\t\t\tint ndof_b = dofPerNode_b * neln;\n\t\t\tke.resize(ndof_a, ndof_b);\n\n\t\t\t// calculate element stiffness\n\t\t\tint nint = el.GaussPoints();\n\n\t\t\t// gauss weights\n\t\t\tdouble* w = el.GaussWeights();\n\n\t\t\t// repeat over integration points\n\t\t\tke.zero();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& pt = *el.GetMaterialPoint(n);\n\n\t\t\t\t// set the shape function values\n\t\t\t\tpt.m_shape = el.H(n);\n\n\t\t\t\t// calculate stiffness component\n\t\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\t// evaluate integrand\n\t\t\t\t\t\tkab.zero();\n\t\t\t\t\t\tf(pt, i, j, kab);\n\t\t\t\t\t\tke.adds(dofPerNode_a * i, dofPerNode_b * j, kab, w[n]);\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get the element's LM vector\n\t\t\tstd::vector<int>& lma = ke.RowIndices();\n\t\t\tstd::vector<int>& lmb = ke.ColumnsIndices();\n\t\t\tlma.assign(ndof_a, -1);\n\t\t\tlmb.assign(ndof_b, -1);\n\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(el.m_node[j]);\n\t\t\t\tstd::vector<int>& ID = node.m_ID;\n\n\t\t\t\tfor (int k = 0; k < dofPerNode_a; ++k)\n\t\t\t\t\tlma[dofPerNode_a * j + k] = ID[dofList_a[k]];\n\n\t\t\t\tfor (int k = 0; k < dofPerNode_b; ++k)\n\t\t\t\t\tlmb[dofPerNode_b * j + k] = ID[dofList_b[k]];\n\t\t\t}\n\n\t\t\t// assemble element matrix in global stiffness matrix\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n\t} // omp parallel\n}\n"
  },
  {
    "path": "FECore/FESolidDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDomain.h\"\n#include \"FEDofList.h\"\n#include \"FELinearSystem.h\"\n#include \"FESolidElement.h\"\n\n//-----------------------------------------------------------------------------\n// This typedef defines a surface integrand. \n// It evaluates the function at surface material point mp, and returns the value\n// it the val vector. The size of the vector is determined by the field variable\n// that is being integrated and is already set when the integrand is called.\n// This is used in the FESurface::LoadVector function.\ntypedef std::function<void(FEMaterialPoint& mp, int node_a, std::vector<double>& val)> FEVolumeVectorIntegrand;\n\ntypedef std::function<void(FEMaterialPoint& mp, int node_a, int node_b, matrix& val)> FEVolumeMatrixIntegrand;\n\n//-----------------------------------------------------------------------------\n//! abstract base class for 3D volumetric elements\nclass FECORE_API FESolidDomain : public FEDomain\n{\n    FECORE_SUPER_CLASS(FESOLIDDOMAIN_ID)\n    FECORE_BASE_CLASS(FESolidDomain)\n\npublic:\n    //! constructor\n    FESolidDomain(FEModel* pfem);\n    \n    //! create storage for elements\n\tbool Create(int nsize, FE_Element_Spec espec) override;\n\n    //! return nr of elements\n\tint Elements() const override;\n\n\t//! initialize element data\n\tbool Init() override;\n    \n    //! reset data (overridden from FEDomain)\n    void Reset() override;\n    \n    //! copy data from another domain (overridden from FEDomain)\n    void CopyFrom(FEMeshPartition* pd) override;\n\n    //! element access\n\tFESolidElement& Element(int n);\n    FEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\n    int GetElementType() const { return m_Elem[0].Type(); }\n    \n    int GetElementShape() const { return m_Elem[0].Shape(); }\n\n\tFE_Element_Spec GetElementSpec() const;\n    \n    //! find the element in which point y lies\n    FESolidElement* FindElement(const vec3d& y, double r[3]);\n\n\t//! find the element in which point y lies (reference configuration)\n\tFESolidElement* FindReferenceElement(const vec3d& y, double r[3]);\n\n\t//! Project a point to an element and return natural coordinates\n\tvoid ProjectToElement(FESolidElement& el, const vec3d& p, double r[3]);\n\n\t//! Project a point to an element in the reference frame and return natural coordinates\n\t//! returns true if the point lies in the element\n\tbool ProjectToReferenceElement(FESolidElement& el, const vec3d& p, double r[3]);\n\n    //! Calculate deformation gradient at integration point n\n    double defgrad(FESolidElement& el, mat3d& F, int n);\n\tdouble defgrad(FESolidElement& el, mat3d& F, int n, vec3d* r);\n\n    //! Calculate deformation gradient at integration point n\n    double defgrad(FESolidElement& el, mat3d& F, double r, double s, double t);\n    \n    //! Calculate deformation gradient at integration point n at previous time\n    double defgradp(FESolidElement& el, mat3d& F, int n);\n    \n    //! Calculate GradJ at integration point n at current time\n    vec3d GradJ(FESolidElement& el, int n);\n    \n    //! Calculate GradJ at integration point n at prev time\n    vec3d GradJp(FESolidElement& el, int n);\n    \n    //! Calculate GradJ at integration point n at current time\n    tens3dls Gradb(FESolidElement& el, int n);\n    \n    //! Calculate GradJ at integration point n at prev time\n    tens3dls Gradbp(FESolidElement& el, int n);\n    \n    //! calculate inverse jacobian matrix w.r.t. reference frame\n    double invjac0(const FESolidElement& el, double J[3][3], int n);\n    \n    //! calculate inverse jacobian matrix w.r.t. reference frame\n    double invjac0(const FESolidElement& el, double J[3][3], double r, double s, double t);\n    \n    //! calculate inverse jacobian matrix w.r.t. reference frame\n    double invjac0(const FESolidElement& el, double r, double s, double t, mat3d& J);\n    \n    //! calculate inverse jacobian matrix w.r.t. current frame\n    double invjact(FESolidElement& el, double J[3][3], int n);\n\tdouble invjact(FESolidElement& el, double J[3][3], int n, const vec3d* r);\n\n    //! calculate inverse jacobian matrix w.r.t. reference frame\n    double invjact(FESolidElement& el, double J[3][3], double r, double s, double t);\n    \n    //! calculate inverse jacobian matrix w.r.t. intermediate frame\n    double invjact(FESolidElement& el, double J[3][3], int n, const double alpha);\n    \n    //! calculate inverse jacobian matrix w.r.t. previous time\n    double invjactp(FESolidElement& el, double J[3][3], int n);\n    \n    //! calculate gradient of function at integration points\n    vec3d gradient(FESolidElement& el, double* fn, int n);\n\tvec3d gradient(FESolidElement& el, int order, double* fn, int n);\n\n    //! calculate gradient of function at integration points\n    vec3d gradient(FESolidElement& el, vector<double>& fn, int n);\n    \n    //! calculate spatial gradient of vector function at integration points\n    mat3d gradient(FESolidElement& el, vec3d* fn, int n);\n    \n    //! calculate gradient of function at integration points at intermediate time\n    vec3d gradient(FESolidElement& el, vector<double>& fn, int n, const double alpha);\n    \n    //! calculate spatial gradient of vector function at integration points at intermediate time\n    mat3d gradient(FESolidElement& el, vec3d* fn, int n, const double alpha);\n    \n    //! calculate spatial gradient of vector function at integration points\n    //! at previous time\n    mat3d gradientp(FESolidElement& el, vec3d* fn, int n);\n    \n    //! calculate spatial gradient of vector function at integration points\n    tens3dls gradient(FESolidElement& el, mat3ds* fn, int n);\n    \n    //! calculate spatial gradient of vector function at integration points\n    //! at previous time\n    tens3dls gradientp(FESolidElement& el, mat3ds* fn, int n);\n    \n    //! calculate material gradient of scalar function at integration points\n    vec3d Gradient(FESolidElement& el, double* fn, int n);\n    \n    //! calculate material gradient of scalar function at integration points\n    vec3d Gradient(FESolidElement& el, vector<double>& fn, int n);\n    \n    //! calculate material gradient of vector function at integration points\n    mat3d Gradient(FESolidElement& el, vec3d* fn, int n);\n    \n    //! calculate material gradient of vector function at integration points\n    tens3dls Gradient(FESolidElement& el, mat3ds* fn, int n);\n    \n    //! calculate jacobian in reference frame\n    double detJ0(FESolidElement& el, int n);\n    \n    //! calculate jacobian in current frame\n    double detJt(FESolidElement& el, int n);\n    \n    //! calculate jacobian in current frame\n    double detJt(FESolidElement& el, int n, const double alpha);\n    \n    //! calculates covariant basis vectors in reference configuration at an integration point\n    void CoBaseVectors0(FESolidElement& el, int j, vec3d g[3]);\n    \n    //! calculates covariant basis vectors at an integration point\n    void CoBaseVectors(FESolidElement& el, int j, vec3d g[3]);\n    \n    //! calculates covariant basis vectors at an integration point\n    void CoBaseVectors(FESolidElement& el, int j, vec3d g[3], const double alpha);\n    \n    //! calculates contravariant basis vectors in reference configuration at an integration point\n    void ContraBaseVectors0(FESolidElement& el, int j, vec3d g[3]);\n    \n    //! calculates contravariant basis vectors at an integration point\n    void ContraBaseVectors(FESolidElement& el, int j, vec3d g[3]);\n    \n    //! calculates contravariant basis vectors at an integration point\n    void ContraBaseVectors(FESolidElement& el, int j, vec3d g[3], const double alpha);\n    \n    //! calculates parametric derivatives of covariant basis vectors at an integration point\n    void CoBaseVectorDerivatives(FESolidElement& el, int j, vec3d dg[3][3]);\n    \n    //! calculates parametric derivatives of covariant basis vectors at an integration point\n    void CoBaseVectorDerivatives0(FESolidElement& el, int j, vec3d dg[3][3]);\n    \n    //! calculates parametric derivatives of covariant basis vectors at an integration point\n    void CoBaseVectorDerivatives(FESolidElement& el, int j, vec3d dg[3][3], const double alpha);\n    \n    //! calculates parametric derivatives of contravariant basis vectors at an integration point\n    void ContraBaseVectorDerivatives(FESolidElement& el, int j, vec3d dg[3][3]);\n    \n    //! calculates parametric derivatives of contravariant basis vectors at an integration point\n    void ContraBaseVectorDerivatives0(FESolidElement& el, int j, vec3d dg[3][3]);\n    \n    //! calculates parametric derivatives of contravariant basis vectors at an integration point\n    void ContraBaseVectorDerivatives(FESolidElement& el, int j, vec3d dg[3][3], const double alpha);\n    \n    //! calculate the laplacian of a vector function at an integration point\n    vec3d lapvec(FESolidElement& el, vec3d* fn, int n);\n    \n    //! calculate the gradient of the divergence of a vector function at an integration point\n    vec3d gradivec(FESolidElement& el, vec3d* fn, int n);\n    \n    //! calculate the transpose of the gradient of the shape function gradients at an integration point\n    void gradTgradShape(FESolidElement& el, int j, vector<mat3d>& mn);\n    \n    //! calculate spatial gradient of shapefunctions at integration point (returns Jacobian determinant)\n    double ShapeGradient(FESolidElement& el, int n, vec3d* GradH);\n    \n    //! calculate spatial gradient of shapefunctions at integration point (returns Jacobian determinant)\n    double ShapeGradient(FESolidElement& el, int n, vec3d* GradH, const double alpha);\n    \n    //! calculate spatial gradient of shapefunctions at integration point in reference frame (returns Jacobian determinant)\n    double ShapeGradient0(FESolidElement& el, int n, vec3d* GradH);\n    \n    //! calculate spatial gradient of shapefunctions at integration point (returns Jacobian determinant)\n    double ShapeGradient(FESolidElement& el, double r, double s, double t, vec3d* GradH);\n    \n    //! calculate spatial gradient of shapefunctions at integration point in reference frame (returns Jacobian determinant)\n    double ShapeGradient0(FESolidElement& el, double r, double s, double t, vec3d* GradH);\n\n\t//! calculate the volume of an element in reference frame\n\tdouble Volume(FESolidElement& el);\n\n\t//! calculate the volume of an element in current frame\n\tdouble CurrentVolume(FESolidElement& el);\n\npublic:\n\t//! get the current nodal coordinates\n\tvoid GetCurrentNodalCoordinates(const FESolidElement& el, vec3d* rt);\n\tvoid GetCurrentNodalCoordinates(const FESolidElement& el, vec3d* rt, double alpha);\n\n\t//! get the reference nodal coordinates\n\tvoid GetReferenceNodalCoordinates(const FESolidElement& el, vec3d* r0);\n\n\t//! get the nodal coordinates at previous state\n\tvoid GetPreviousNodalCoordinates(const FESolidElement& el, vec3d* rp);\n\npublic:\n\t//! loop over elements\n\tvoid ForEachSolidElement(std::function<void(FESolidElement& el)> f, bool runInParallel = true);\n\n\t//! return the degrees of freedom of an element for this domain\n\tvirtual int GetElementDofs(FESolidElement& el);\n\npublic:\n\t// Evaluate an integral over the domain and assemble into global load vector\n\tvirtual void LoadVector(\n\t\tFEGlobalVector& R,\t\t\t// the global vector to assembe the load vector in\n\t\tconst FEDofList& dofList,\t// the degree of freedom list\n\t\tFEVolumeVectorIntegrand f\t// the actual integrand function\n\t);\n\n\t//! Evaluate the stiffness matrix of a load\n\tvirtual void LoadStiffness(\n\t\tFELinearSystem& LS,\t\t\t// The solver does the assembling\n\t\tconst FEDofList& dofList_a,\t// The degree of freedom list of node a\n\t\tconst FEDofList& dofList_b,\t// The degree of freedom list of node b\n\t\tFEVolumeMatrixIntegrand f\t// the matrix function to evaluate\n\t);\n\nprotected:\n    vector<FESolidElement>\tm_Elem;\t\t//!< array of elements\n\tFE_Element_Spec\t\t\tm_elemSpec;\t//!< the element spec\n\n\tFEDofList\tm_dofU;\n\tFEDofList\tm_dofSU;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FESolidElement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESolidElement.h\"\n#include \"DumpStream.h\"\n\n//=================================================================================================\n// FESolidElement\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFESolidElement::FESolidElement(const FESolidElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\tm_status = el.m_status;\n}\n\nFESolidElement& FESolidElement::operator = (const FESolidElement& el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) { SetTraits(el.m_pT); m_State = el.m_State; }\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\tm_status = el.m_status;\n\n\treturn (*this);\n}\n\nvoid FESolidElement::SetTraits(FEElementTraits* pt)\n{\n\tFEElement::SetTraits(pt);\n\n\tint ni = GaussPoints();\n\tm_J0i.resize(ni);\n}\n\nvec3d FESolidElement::evaluate(vec3d* v, double r, double s, double t) const\n{\n\tdouble H[FEElement::MAX_NODES];\n\tshape_fnc(H, r, s, t);\n\tint neln = Nodes();\n\tvec3d p(0, 0, 0);\n\tfor (int i = 0; i<neln; ++i) p += v[i] * H[i];\n\treturn p;\n}\n\ndouble FESolidElement::evaluate(double* v, double r, double s, double t) const\n{\n\tdouble H[FEElement::MAX_NODES];\n\tshape_fnc(H, r, s, t);\n\tint neln = Nodes();\n\tdouble p = 0.0;\n\tfor (int i = 0; i<neln; ++i) p += v[i] * H[i];\n\treturn p;\n}\n\ndouble* FESolidElement::Gr(int order, int n) const { return (order >= 0 ? ((FESolidElementTraits*)(m_pT))->m_Gr_p[order][n] : ((FESolidElementTraits*)(m_pT))->m_Gr[n]); }\t// shape function derivative to r\ndouble* FESolidElement::Gs(int order, int n) const { return (order >= 0 ? ((FESolidElementTraits*)(m_pT))->m_Gs_p[order][n] : ((FESolidElementTraits*)(m_pT))->m_Gs[n]); }\t// shape function derivative to s\ndouble* FESolidElement::Gt(int order, int n) const { return (order >= 0 ? ((FESolidElementTraits*)(m_pT))->m_Gt_p[order][n] : ((FESolidElementTraits*)(m_pT))->m_Gt[n]); }\t// shape function derivative to t\n\nvoid FESolidElement::Serialize(DumpStream& ar)\n{\n\tFEElement::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_J0i;\n\t\tar & m_bitfc;\n\t}\n}\n"
  },
  {
    "path": "FECore/FESolidElement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElement.h\"\n\n//-----------------------------------------------------------------------------\n//!  This class defines a solid element\n\nclass FECORE_API FESolidElement : public FEElement\n{\npublic:\n\t//! default constructor\n\tFESolidElement() {}\n\n\t//! copy constructor\n\tFESolidElement(const FESolidElement& el);\n\n\t//! assignment operator\n\tFESolidElement& operator = (const FESolidElement& el);\n\n\t//! set the element traits\n\tvoid SetTraits(FEElementTraits* pt) override;\n\n\tdouble gr(int n) const { return ((FESolidElementTraits*)(m_pT))->gr[n]; }\t// integration point coordinate r\n\tdouble gs(int n) const { return ((FESolidElementTraits*)(m_pT))->gs[n]; }\t// integration point coordinate s\n\tdouble gt(int n) const { return ((FESolidElementTraits*)(m_pT))->gt[n]; }\t// integration point coordinate t\n\n\tdouble* GaussWeights() const { return &((FESolidElementTraits*)(m_pT))->gw[0]; }\t\t\t// weights of integration points\n\n\tdouble* Gr(int n) const { return ((FESolidElementTraits*)(m_pT))->m_Gr[n]; }\t// shape function derivative to r\n\tdouble* Gs(int n) const { return ((FESolidElementTraits*)(m_pT))->m_Gs[n]; }\t// shape function derivative to s\n\tdouble* Gt(int n) const { return ((FESolidElementTraits*)(m_pT))->m_Gt[n]; }\t// shape function derivative to t\n\n\tdouble* Grr(int n) const { return ((FESolidElementTraits*)(m_pT))->Grr[n]; }\t// shape function 2nd derivative to rr\n\tdouble* Gsr(int n) const { return ((FESolidElementTraits*)(m_pT))->Gsr[n]; }\t// shape function 2nd derivative to sr\n\tdouble* Gtr(int n) const { return ((FESolidElementTraits*)(m_pT))->Gtr[n]; }\t// shape function 2nd derivative to tr\n\n\tdouble* Grs(int n) const { return ((FESolidElementTraits*)(m_pT))->Grs[n]; }\t// shape function 2nd derivative to rs\n\tdouble* Gss(int n) const { return ((FESolidElementTraits*)(m_pT))->Gss[n]; }\t// shape function 2nd derivative to ss\n\tdouble* Gts(int n) const { return ((FESolidElementTraits*)(m_pT))->Gts[n]; }\t// shape function 2nd derivative to ts\n\n\tdouble* Grt(int n) const { return ((FESolidElementTraits*)(m_pT))->Grt[n]; }\t// shape function 2nd derivative to rt\n\tdouble* Gst(int n) const { return ((FESolidElementTraits*)(m_pT))->Gst[n]; }\t// shape function 2nd derivative to st\n\tdouble* Gtt(int n) const { return ((FESolidElementTraits*)(m_pT))->Gtt[n]; }\t// shape function 2nd derivative to tt\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t) const { ((FESolidElementTraits*)(m_pT))->shape_fnc(H, r, s, t); }\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t) const { ((FESolidElementTraits*)(m_pT))->shape_deriv(Hr, Hs, Ht, r, s, t); }\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t) const { ((FESolidElementTraits*)(m_pT))->shape_deriv2(Hrr, Hss, Htt, Hrs, Hst, Hrt, r, s, t); }\n\n\tvec3d evaluate(vec3d* v, double r, double s, double t) const;\n\tdouble evaluate(double* v, double r, double s, double t) const;\n\n\tdouble* Gr(int order, int n) const;\n\tdouble* Gs(int order, int n) const;\n\tdouble* Gt(int order, int n) const;\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n\tstd::vector<bool>    m_bitfc;    //!< flag for interface nodes\n\tstd::vector<mat3d>\tm_J0i;\t\t//!< inverse of reference Jacobian\n};\n"
  },
  {
    "path": "FECore/FESolidElementShape.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESolidElementShape.h\"\n\n//=============================================================================\n//                                 H E X 8\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEHex8::shape_fnc(double* H, double r, double s, double t)\n{\n\tH[0] = 0.125*(1 - r)*(1 - s)*(1 - t);\n\tH[1] = 0.125*(1 + r)*(1 - s)*(1 - t);\n\tH[2] = 0.125*(1 + r)*(1 + s)*(1 - t);\n\tH[3] = 0.125*(1 - r)*(1 + s)*(1 - t);\n\tH[4] = 0.125*(1 - r)*(1 - s)*(1 + t);\n\tH[5] = 0.125*(1 + r)*(1 - s)*(1 + t);\n\tH[6] = 0.125*(1 + r)*(1 + s)*(1 + t);\n\tH[7] = 0.125*(1 - r)*(1 + s)*(1 + t);\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEHex8::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -0.125*(1 - s)*(1 - t);\n\tHr[1] = 0.125*(1 - s)*(1 - t);\n\tHr[2] = 0.125*(1 + s)*(1 - t);\n\tHr[3] = -0.125*(1 + s)*(1 - t);\n\tHr[4] = -0.125*(1 - s)*(1 + t);\n\tHr[5] = 0.125*(1 - s)*(1 + t);\n\tHr[6] = 0.125*(1 + s)*(1 + t);\n\tHr[7] = -0.125*(1 + s)*(1 + t);\n\n\tHs[0] = -0.125*(1 - r)*(1 - t);\n\tHs[1] = -0.125*(1 + r)*(1 - t);\n\tHs[2] = 0.125*(1 + r)*(1 - t);\n\tHs[3] = 0.125*(1 - r)*(1 - t);\n\tHs[4] = -0.125*(1 - r)*(1 + t);\n\tHs[5] = -0.125*(1 + r)*(1 + t);\n\tHs[6] = 0.125*(1 + r)*(1 + t);\n\tHs[7] = 0.125*(1 - r)*(1 + t);\n\n\tHt[0] = -0.125*(1 - r)*(1 - s);\n\tHt[1] = -0.125*(1 + r)*(1 - s);\n\tHt[2] = -0.125*(1 + r)*(1 + s);\n\tHt[3] = -0.125*(1 - r)*(1 + s);\n\tHt[4] = 0.125*(1 - r)*(1 - s);\n\tHt[5] = 0.125*(1 + r)*(1 - s);\n\tHt[6] = 0.125*(1 + r)*(1 + s);\n\tHt[7] = 0.125*(1 - r)*(1 + s);\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FEHex8::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[0] = 0.0; Hss[0] = 0.0; Htt[0] = 0.0;\n\tHrr[1] = 0.0; Hss[1] = 0.0; Htt[1] = 0.0;\n\tHrr[2] = 0.0; Hss[2] = 0.0; Htt[2] = 0.0;\n\tHrr[3] = 0.0; Hss[3] = 0.0; Htt[3] = 0.0;\n\tHrr[4] = 0.0; Hss[4] = 0.0; Htt[4] = 0.0;\n\tHrr[5] = 0.0; Hss[5] = 0.0; Htt[5] = 0.0;\n\tHrr[6] = 0.0; Hss[6] = 0.0; Htt[6] = 0.0;\n\tHrr[7] = 0.0; Hss[7] = 0.0; Htt[7] = 0.0;\n\n\tHrs[0] = 0.125*(1 - t);\n\tHrs[1] = -0.125*(1 - t);\n\tHrs[2] = 0.125*(1 - t);\n\tHrs[3] = -0.125*(1 - t);\n\tHrs[4] = 0.125*(1 + t);\n\tHrs[5] = -0.125*(1 + t);\n\tHrs[6] = 0.125*(1 + t);\n\tHrs[7] = -0.125*(1 + t);\n\n\tHrt[0] = 0.125*(1 - s);\n\tHrt[1] = -0.125*(1 - s);\n\tHrt[2] = -0.125*(1 + s);\n\tHrt[3] = 0.125*(1 + s);\n\tHrt[4] = -0.125*(1 - s);\n\tHrt[5] = 0.125*(1 - s);\n\tHrt[6] = 0.125*(1 + s);\n\tHrt[7] = -0.125*(1 + s);\n\n\tHst[0] = 0.125*(1 - r);\n\tHst[1] = 0.125*(1 + r);\n\tHst[2] = -0.125*(1 + r);\n\tHst[3] = -0.125*(1 - r);\n\tHst[4] = -0.125*(1 - r);\n\tHst[5] = -0.125*(1 + r);\n\tHst[6] = 0.125*(1 + r);\n\tHst[7] = 0.125*(1 - r);\n}\n\n//=============================================================================\n//                                 T E T 4\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FETet4::shape_fnc(double* H, double r, double s, double t)\n{\n\tH[0] = 1 - r - s - t;\n\tH[1] = r;\n\tH[2] = s;\n\tH[3] = t;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FETet4::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -1; Hs[0] = -1; Ht[0] = -1;\n\tHr[1] = 1;\tHs[1] = 0; Ht[1] = 0;\n\tHr[2] = 0;\tHs[2] = 1; Ht[2] = 0;\n\tHr[3] = 0;\tHs[3] = 0; Ht[3] = 1;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FETet4::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[0] = 0.0; Hss[0] = 0.0; Htt[0] = 0.0;\n\tHrr[1] = 0.0; Hss[1] = 0.0; Htt[1] = 0.0;\n\tHrr[2] = 0.0; Hss[2] = 0.0; Htt[2] = 0.0;\n\tHrr[3] = 0.0; Hss[3] = 0.0; Htt[3] = 0.0;\n\n\tHrs[0] = 0.0; Hst[0] = 0.0; Hrt[0] = 0.0;\n\tHrs[1] = 0.0; Hst[1] = 0.0; Hrt[1] = 0.0;\n\tHrs[2] = 0.0; Hst[2] = 0.0; Hrt[2] = 0.0;\n\tHrs[3] = 0.0; Hst[3] = 0.0; Hrt[3] = 0.0;\n}\n\n//=============================================================================\n//                                 T E T 5\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FETet5::shape_fnc(double* H, double r, double s, double t)\n{\n\tH[0] = 1 - r - s - t;\n\tH[1] = r;\n\tH[2] = s;\n\tH[3] = t;\n\tH[4] = 256.0 * H[0] * H[1] * H[2] * H[3];\n\n\tH[0] -= 0.25*H[4];\n\tH[1] -= 0.25*H[4];\n\tH[2] -= 0.25*H[4];\n\tH[3] -= 0.25*H[4];\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FETet5::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -1; Hs[0] = -1; Ht[0] = -1;\n\tHr[1] = 1;\tHs[1] = 0; Ht[1] = 0;\n\tHr[2] = 0;\tHs[2] = 1; Ht[2] = 0;\n\tHr[3] = 0;\tHs[3] = 0; Ht[3] = 1;\n\n\tHr[4] = 256.0*s*t*(1.0 - 2.0*r - s - t);\n\tHs[4] = 256.0*r*t*(1.0 - r - 2.0*s - t);\n\tHt[4] = 256.0*r*s*(1.0 - r - s - 2.0*t);\n\n\tHr[0] -= 0.25*Hr[4]; Hr[1] -= 0.25*Hr[4]; Hr[2] -= 0.25*Hr[4]; Hr[3] -= 0.25*Hr[4];\n\tHs[0] -= 0.25*Hs[4]; Hs[1] -= 0.25*Hs[4]; Hs[2] -= 0.25*Hs[4]; Hs[3] -= 0.25*Hs[4];\n\tHt[0] -= 0.25*Ht[4]; Ht[1] -= 0.25*Ht[4]; Ht[2] -= 0.25*Ht[4]; Ht[3] -= 0.25*Ht[4];\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FETet5::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[4] = -512 * s*t;\n\tHss[4] = -512 * r*t;\n\tHtt[4] = -512 * r*s;\n\n\tHrs[4] = 256.0*t*(1.0 - 2.0*r - 2.0*s - t);\n\tHst[4] = 256.0*r*(1.0 - r - 2.0*s - 2.0*t);\n\tHrt[4] = 256.0*s*(1.0 - 2.0*r - s - 2.0*t);\n\n\tHrr[0] = -0.25*Hrr[4]; Hss[0] = -0.25*Hss[4]; Htt[0] = -0.25*Htt[4];\n\tHrr[1] = -0.25*Hrr[4]; Hss[1] = -0.25*Hss[4]; Htt[1] = -0.25*Htt[4];\n\tHrr[2] = -0.25*Hrr[4]; Hss[2] = -0.25*Hss[4]; Htt[2] = -0.25*Htt[4];\n\tHrr[3] = -0.25*Hrr[4]; Hss[3] = -0.25*Hss[4]; Htt[3] = -0.25*Htt[4];\n\n\tHrs[0] = -0.25*Hrs[4]; Hst[0] = -0.25*Hst[4]; Hrt[0] = -Hrt[4];\n\tHrs[1] = -0.25*Hrs[4]; Hst[1] = -0.25*Hst[4]; Hrt[1] = -Hrt[4];\n\tHrs[2] = -0.25*Hrs[4]; Hst[2] = -0.25*Hst[4]; Hrt[2] = -Hrt[4];\n\tHrs[3] = -0.25*Hrs[4]; Hst[3] = -0.25*Hst[4]; Hrt[3] = -Hrt[4];\n}\n\n//=============================================================================\n//                                 P E N T A 6\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FEPenta6::shape_fnc(double* H, double r, double s, double t)\n{\n\tH[0] = 0.5*(1 - t)*(1 - r - s);\n\tH[1] = 0.5*(1 - t)*r;\n\tH[2] = 0.5*(1 - t)*s;\n\tH[3] = 0.5*(1 + t)*(1 - r - s);\n\tH[4] = 0.5*(1 + t)*r;\n\tH[5] = 0.5*(1 + t)*s;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEPenta6::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -0.5*(1 - t);\n\tHr[1] = 0.5*(1 - t);\n\tHr[2] = 0.0;\n\tHr[3] = -0.5*(1 + t);\n\tHr[4] = 0.5*(1 + t);\n\tHr[5] = 0.0;\n\n\tHs[0] = -0.5*(1 - t);\n\tHs[1] = 0.0;\n\tHs[2] = 0.5*(1 - t);\n\tHs[3] = -0.5*(1 + t);\n\tHs[4] = 0.0;\n\tHs[5] = 0.5*(1 + t);\n\n\tHt[0] = -0.5*(1 - r - s);\n\tHt[1] = -0.5*r;\n\tHt[2] = -0.5*s;\n\tHt[3] = 0.5*(1 - r - s);\n\tHt[4] = 0.5*r;\n\tHt[5] = 0.5*s;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FEPenta6::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[0] = 0.0; Hss[0] = 0.0; Htt[0] = 0.0;\n\tHrr[1] = 0.0; Hss[1] = 0.0; Htt[1] = 0.0;\n\tHrr[2] = 0.0; Hss[2] = 0.0; Htt[2] = 0.0;\n\tHrr[3] = 0.0; Hss[3] = 0.0; Htt[3] = 0.0;\n\tHrr[4] = 0.0; Hss[4] = 0.0; Htt[4] = 0.0;\n\tHrr[5] = 0.0; Hss[5] = 0.0; Htt[5] = 0.0;\n\n\tHrs[0] = 0.0; Hst[0] = 0.5; Hrt[0] = 0.5;\n\tHrs[1] = 0.0; Hst[1] = 0.0; Hrt[1] = -0.5;\n\tHrs[2] = 0.0; Hst[2] = -0.5; Hrt[2] = 0.0;\n\tHrs[3] = 0.0; Hst[3] = -0.5; Hrt[3] = -0.5;\n\tHrs[4] = 0.0; Hst[4] = 0.0; Hrt[4] = 0.5;\n\tHrs[5] = 0.0; Hst[5] = 0.5; Hrt[5] = 0.0;\n}\n\n//=============================================================================\n//                                 P E N T A 1 5\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FEPenta15::shape_fnc(double* H, double r, double s, double t)\n{\n\tdouble u = 1 - r - s;\n\n\tH[0] = -((1 - t*t)*u) / 2. + ((1 - t)*u*(-1 + 2 * u)) / 2.;\n\tH[1] = (r*(-1 + 2 * r)*(1 - t)) / 2. - (r*(1 - t*t)) / 2.;\n\tH[2] = (s*(-1 + 2 * s)*(1 - t)) / 2. - (s*(1 - t*t)) / 2.;\n\tH[3] = -((1 - t*t)*u) / 2. + ((1 + t)*u*(-1 + 2 * u)) / 2.;\n\tH[4] = (r*(-1 + 2 * r)*(1 + t)) / 2. - (r*(1 - t*t)) / 2.;\n\tH[5] = (s*(-1 + 2 * s)*(1 + t)) / 2. - (s*(1 - t*t)) / 2.;\n\tH[6] = 2 * r*(1 - t)*u;\n\tH[7] = 2 * r*s*(1 - t);\n\tH[8] = 2 * s*(1 - t)*u;\n\tH[9] = 2 * r*(1 + t)*u;\n\tH[10] = 2 * r*s*(1 + t);\n\tH[11] = 2 * s*(1 + t)*u;\n\tH[12] = (1 - t*t)*u;\n\tH[13] = r*(1 - t*t);\n\tH[14] = s*(1 - t*t);\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEPenta15::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -((-1 + t)*(-2 + 4 * r + 4 * s + t)) / 2.;\n\tHr[1] = (-2 - 4 * r*(-1 + t) + t + t*t) / 2.;\n\tHr[2] = 0;\n\tHr[3] = ((-2 + 4 * r + 4 * s - t)*(1 + t)) / 2.;\n\tHr[4] = ((1 + t)*(-2 + 4 * r + t)) / 2.;\n\tHr[5] = 0;\n\tHr[6] = 2 * (-1 + 2 * r + s)*(-1 + t);\n\tHr[7] = -2 * s*(-1 + t);\n\tHr[8] = 2 * s*(-1 + t);\n\tHr[9] = -2 * (-1 + 2 * r + s)*(1 + t);\n\tHr[10] = 2 * s*(1 + t);\n\tHr[11] = -2 * s*(1 + t);\n\tHr[12] = -1 + t*t;\n\tHr[13] = 1 - t*t;\n\tHr[14] = 0;\n\n\tHs[0] = -((-1 + t)*(-2 + 4 * r + 4 * s + t)) / 2.;\n\tHs[1] = 0;\n\tHs[2] = (-2 - 4 * s*(-1 + t) + t + t*t) / 2.;\n\tHs[3] = ((-2 + 4 * r + 4 * s - t)*(1 + t)) / 2.;\n\tHs[4] = 0;\n\tHs[5] = ((1 + t)*(-2 + 4 * s + t)) / 2.;\n\tHs[6] = 2 * r*(-1 + t);\n\tHs[7] = -2 * r*(-1 + t);\n\tHs[8] = 2 * (-1 + r + 2 * s)*(-1 + t);\n\tHs[9] = -2 * r*(1 + t);\n\tHs[10] = 2 * r*(1 + t);\n\tHs[11] = -2 * (-1 + r + 2 * s)*(1 + t);\n\tHs[12] = -1 + t*t;\n\tHs[13] = 0;\n\tHs[14] = 1 - t*t;\n\n\tHt[0] = -((-1 + r + s)*(-1 + 2 * r + 2 * s + 2 * t)) / 2.;\n\tHt[1] = (r*(1 - 2 * r + 2 * t)) / 2.;\n\tHt[2] = (s*(1 - 2 * s + 2 * t)) / 2.;\n\tHt[3] = ((-1 + r + s)*(-1 + 2 * r + 2 * s - 2 * t)) / 2.;\n\tHt[4] = r*(-0.5 + r + t);\n\tHt[5] = s*(-0.5 + s + t);\n\tHt[6] = 2 * r*(-1 + r + s);\n\tHt[7] = -2 * r*s;\n\tHt[8] = 2 * s*(-1 + r + s);\n\tHt[9] = -2 * r*(-1 + r + s);\n\tHt[10] = 2 * r*s;\n\tHt[11] = -2 * s*(-1 + r + s);\n\tHt[12] = 2 * (-1 + r + s)*t;\n\tHt[13] = -2 * r*t;\n\tHt[14] = -2 * s*t;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FEPenta15::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[0] = 2 - 2 * t;\n\tHrr[1] = 2 - 2 * t;\n\tHrr[2] = 0;\n\tHrr[3] = 2 * (1 + t);\n\tHrr[4] = 2 * (1 + t);\n\tHrr[5] = 0;\n\tHrr[6] = 4 * (-1 + t);\n\tHrr[7] = 0;\n\tHrr[8] = 0;\n\tHrr[9] = -4 * (1 + t);\n\tHrr[10] = 0;\n\tHrr[11] = 0;\n\tHrr[12] = 0;\n\tHrr[13] = 0;\n\tHrr[14] = 0;\n\n\tHss[0] = 2 - 2 * t;\n\tHss[1] = 0;\n\tHss[2] = 2 - 2 * t;\n\tHss[3] = 2 * (1 + t);\n\tHss[4] = 0;\n\tHss[5] = 2 * (1 + t);\n\tHss[6] = 0;\n\tHss[7] = 0;\n\tHss[8] = 4 * (-1 + t);\n\tHss[9] = 0;\n\tHss[10] = 0;\n\tHss[11] = -4 * (1 + t);\n\tHss[12] = 0;\n\tHss[13] = 0;\n\tHss[14] = 0;\n\n\tHtt[0] = 1 - r - s;\n\tHtt[1] = r;\n\tHtt[2] = s;\n\tHtt[3] = 1 - r - s;\n\tHtt[4] = r;\n\tHtt[5] = s;\n\tHtt[6] = 0;\n\tHtt[7] = 0;\n\tHtt[8] = 0;\n\tHtt[9] = 0;\n\tHtt[10] = 0;\n\tHtt[11] = 0;\n\tHtt[12] = 2 * (-1 + r + s);\n\tHtt[13] = -2 * r;\n\tHtt[14] = -2 * s;\n\n\tHrs[0] = 2 - 2 * t;\n\tHrs[1] = 0;\n\tHrs[2] = 0;\n\tHrs[3] = 2 * (1 + t);\n\tHrs[4] = 0;\n\tHrs[5] = 0;\n\tHrs[6] = 2 * (-1 + t);\n\tHrs[7] = 2 - 2 * t;\n\tHrs[8] = 2 * (-1 + t);\n\tHrs[9] = -2 * (1 + t);\n\tHrs[10] = 2 * (1 + t);\n\tHrs[11] = -2 * (1 + t);\n\tHrs[12] = 0;\n\tHrs[13] = 0;\n\tHrs[14] = 0;\n\n\tHst[0] = 1.5 - 2 * r - 2 * s - t;\n\tHst[1] = 0;\n\tHst[2] = 0.5 - 2 * s + t;\n\tHst[3] = -1.5 + 2 * r + 2 * s - t;\n\tHst[4] = 0;\n\tHst[5] = -0.5 + 2 * s + t;\n\tHst[6] = 2 * r;\n\tHst[7] = -2 * r;\n\tHst[8] = 2 * (-1 + r + 2 * s);\n\tHst[9] = -2 * r;\n\tHst[10] = 2 * r;\n\tHst[11] = -2 * (-1 + r + 2 * s);\n\tHst[12] = 2 * t;\n\tHst[13] = 0;\n\tHst[14] = -2 * t;\n\n\tHrt[0] = 1.5 - 2 * r - 2 * s - t;\n\tHrt[1] = 0.5 - 2 * r + t;\n\tHrt[2] = 0;\n\tHrt[3] = -1.5 + 2 * r + 2 * s - t;\n\tHrt[4] = -0.5 + 2 * r + t;\n\tHrt[5] = 0;\n\tHrt[6] = 2 * (-1 + 2 * r + s);\n\tHrt[7] = -2 * s;\n\tHrt[8] = 2 * s;\n\tHrt[9] = -2 * (-1 + 2 * r + s);\n\tHrt[10] = 2 * s;\n\tHrt[11] = -2 * s;\n\tHrt[12] = 2 * t;\n\tHrt[13] = -2 * t;\n\tHrt[14] = 0;\n}\n\n//=============================================================================\n//                                 T E T 1 0\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FETet10::shape_fnc(double* H, double r, double s, double t)\n{\n\tdouble r1 = 1.0 - r - s - t;\n\tdouble r2 = r;\n\tdouble r3 = s;\n\tdouble r4 = t;\n\n\tH[0] = r1*(2.0*r1 - 1.0);\n\tH[1] = r2*(2.0*r2 - 1.0);\n\tH[2] = r3*(2.0*r3 - 1.0);\n\tH[3] = r4*(2.0*r4 - 1.0);\n\tH[4] = 4.0*r1*r2;\n\tH[5] = 4.0*r2*r3;\n\tH[6] = 4.0*r3*r1;\n\tH[7] = 4.0*r1*r4;\n\tH[8] = 4.0*r2*r4;\n\tH[9] = 4.0*r3*r4;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FETet10::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -3.0 + 4.0*r + 4.0*(s + t);\n\tHr[1] = 4.0*r - 1.0;\n\tHr[2] = 0.0;\n\tHr[3] = 0.0;\n\tHr[4] = 4.0 - 8.0*r - 4.0*(s + t);\n\tHr[5] = 4.0*s;\n\tHr[6] = -4.0*s;\n\tHr[7] = -4.0*t;\n\tHr[8] = 4.0*t;\n\tHr[9] = 0.0;\n\n\tHs[0] = -3.0 + 4.0*s + 4.0*(r + t);\n\tHs[1] = 0.0;\n\tHs[2] = 4.0*s - 1.0;\n\tHs[3] = 0.0;\n\tHs[4] = -4.0*r;\n\tHs[5] = 4.0*r;\n\tHs[6] = 4.0 - 8.0*s - 4.0*(r + t);\n\tHs[7] = -4.0*t;\n\tHs[8] = 0.0;\n\tHs[9] = 4.0*t;\n\n\tHt[0] = -3.0 + 4.0*t + 4.0*(r + s);\n\tHt[1] = 0.0;\n\tHt[2] = 0.0;\n\tHt[3] = 4.0*t - 1.0;\n\tHt[4] = -4.0*r;\n\tHt[5] = 0.0;\n\tHt[6] = -4.0*s;\n\tHt[7] = 4.0 - 8.0*t - 4.0*(r + s);\n\tHt[8] = 4.0*r;\n\tHt[9] = 4.0*s;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FETet10::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[0] = 4.0; Hss[0] = 4.0; Htt[0] = 4.0;\n\tHrr[1] = 4.0; Hss[1] = 0.0; Htt[1] = 0.0;\n\tHrr[2] = 0.0; Hss[2] = 4.0; Htt[2] = 0.0;\n\tHrr[3] = 0.0; Hss[3] = 0.0; Htt[3] = 4.0;\n\tHrr[4] = -8.0; Hss[4] = 0.0; Htt[4] = 0.0;\n\tHrr[5] = 0.0; Hss[5] = 0.0; Htt[5] = 0.0;\n\tHrr[6] = 0.0; Hss[6] = -8.0; Htt[6] = 0.0;\n\tHrr[7] = 0.0; Hss[7] = 0.0; Htt[7] = -8.0;\n\tHrr[8] = 0.0; Hss[8] = 0.0; Htt[8] = 0.0;\n\tHrr[9] = 0.0; Hss[9] = 0.0; Htt[9] = 0.0;\n\n\tHrs[0] = 4.0; Hst[0] = 4.0; Hrt[0] = 4.0;\n\tHrs[1] = 0.0; Hst[1] = 0.0; Hrt[1] = 0.0;\n\tHrs[2] = 0.0; Hst[2] = 0.0; Hrt[2] = 0.0;\n\tHrs[3] = 0.0; Hst[3] = 0.0; Hrt[3] = 0.0;\n\tHrs[4] = -4.0; Hst[4] = 0.0; Hrt[4] = -4.0;\n\tHrs[5] = 4.0; Hst[5] = 0.0; Hrt[5] = 0.0;\n\tHrs[6] = -4.0; Hst[6] = -4.0; Hrt[6] = 0.0;\n\tHrs[7] = 0.0; Hst[7] = -4.0; Hrt[7] = -4.0;\n\tHrs[8] = 0.0; Hst[8] = 0.0; Hrt[8] = 4.0;\n\tHrs[9] = 0.0; Hst[9] = 4.0; Hrt[9] = 0.0;\n}\n\n\n//=============================================================================\n//                           T E T 1 5\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FETet15::shape_fnc(double* H, double r, double s, double t)\n{\n\tdouble r1 = 1.0 - r - s - t;\n\tdouble r2 = r;\n\tdouble r3 = s;\n\tdouble r4 = t;\n\n\tH[14] = 256 * r1*r2*r3*r4;\n\n\tH[10] = 27.0*r1*r2*r3 - 27.0*H[14] / 64.0;\n\tH[11] = 27.0*r1*r2*r4 - 27.0*H[14] / 64.0;\n\tH[12] = 27.0*r2*r3*r4 - 27.0*H[14] / 64.0;\n\tH[13] = 27.0*r3*r1*r4 - 27.0*H[14] / 64.0;\n\n\tH[0] = r1*(2.0*r1 - 1.0) + (H[10] + H[11] + H[13]) / 9.0 + H[14] / 8.0;\n\tH[1] = r2*(2.0*r2 - 1.0) + (H[10] + H[11] + H[12]) / 9.0 + H[14] / 8.0;\n\tH[2] = r3*(2.0*r3 - 1.0) + (H[10] + H[12] + H[13]) / 9.0 + H[14] / 8.0;\n\tH[3] = r4*(2.0*r4 - 1.0) + (H[11] + H[12] + H[13]) / 9.0 + H[14] / 8.0;\n\n\tH[4] = 4.0*r1*r2 - 4.0*(H[10] + H[11]) / 9.0 - H[14] / 4.0;\n\tH[5] = 4.0*r2*r3 - 4.0*(H[10] + H[12]) / 9.0 - H[14] / 4.0;\n\tH[6] = 4.0*r3*r1 - 4.0*(H[10] + H[13]) / 9.0 - H[14] / 4.0;\n\tH[7] = 4.0*r1*r4 - 4.0*(H[11] + H[13]) / 9.0 - H[14] / 4.0;\n\tH[8] = 4.0*r2*r4 - 4.0*(H[11] + H[12]) / 9.0 - H[14] / 4.0;\n\tH[9] = 4.0*r3*r4 - 4.0*(H[12] + H[13]) / 9.0 - H[14] / 4.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet15::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tdouble u = 1.0 - r - s - t;\n\n\tHr[14] = 256.0*s*t*(u - r);\n\tHs[14] = 256.0*r*t*(u - s);\n\tHt[14] = 256.0*r*s*(u - t);\n\n\tHr[10] = 27.0*s*(u - r) - 27.0*Hr[14] / 64.0;\n\tHr[11] = 27.0*t*(u - r) - 27.0*Hr[14] / 64.0;\n\tHr[12] = 27.0*s*t - 27.0*Hr[14] / 64.0;\n\tHr[13] = -27.0*s*t - 27.0*Hr[14] / 64.0;\n\n\tHs[10] = 27.0*r*(u - s) - 27.0*Hs[14] / 64.0;\n\tHs[11] = -27.0*r*t - 27.0*Hs[14] / 64.0;\n\tHs[12] = 27.0*r*t - 27.0*Hs[14] / 64.0;\n\tHs[13] = 27.0*t*(u - s) - 27.0*Hs[14] / 64.0;\n\n\tHt[10] = -27.0*r*s - 27.0*Ht[14] / 64.0;\n\tHt[11] = 27.0*r*(u - t) - 27.0*Ht[14] / 64.0;\n\tHt[12] = 27.0*r*s - 27.0*Ht[14] / 64.0;\n\tHt[13] = 27.0*s*(u - t) - 27.0*Ht[14] / 64.0;\n\n\tHr[0] = -(4.0*u - 1.0) + (Hr[10] + Hr[11] + Hr[13]) / 9.0 + Hr[14] / 8.0;\n\tHr[1] = (4.0*r - 1.0) + (Hr[10] + Hr[11] + Hr[12]) / 9.0 + Hr[14] / 8.0;\n\tHr[2] = 0.0 + (Hr[10] + Hr[12] + Hr[13]) / 9.0 + Hr[14] / 8.0;\n\tHr[3] = 0.0 + (Hr[11] + Hr[12] + Hr[13]) / 9.0 + Hr[14] / 8.0;\n\tHr[4] = 4.0*(u - r) - 4.0*(Hr[10] + Hr[11]) / 9.0 - Hr[14] / 4.0;\n\tHr[5] = 4.0*s - 4.0*(Hr[10] + Hr[12]) / 9.0 - Hr[14] / 4.0;\n\tHr[6] = -4.0*s - 4.0*(Hr[10] + Hr[13]) / 9.0 - Hr[14] / 4.0;\n\tHr[7] = -4.0*t - 4.0*(Hr[11] + Hr[13]) / 9.0 - Hr[14] / 4.0;\n\tHr[8] = 4.0*t - 4.0*(Hr[11] + Hr[12]) / 9.0 - Hr[14] / 4.0;\n\tHr[9] = 0.0 - 4.0*(Hr[12] + Hr[13]) / 9.0 - Hr[14] / 4.0;\n\n\tHs[0] = -(4.0*u - 1.0) + (Hs[10] + Hs[11] + Hs[13]) / 9.0 + Hs[14] / 8.0;\n\tHs[1] = 0.0 + (Hs[10] + Hs[11] + Hs[12]) / 9.0 + Hs[14] / 8.0;\n\tHs[2] = (4.0*s - 1.0) + (Hs[10] + Hs[12] + Hs[13]) / 9.0 + Hs[14] / 8.0;\n\tHs[3] = 0.0 + (Hs[11] + Hs[12] + Hs[13]) / 9.0 + Hs[14] / 8.0;\n\tHs[4] = -4.0*r - 4.0*(Hs[10] + Hs[11]) / 9.0 - Hs[14] / 4.0;\n\tHs[5] = 4.0*r - 4.0*(Hs[10] + Hs[12]) / 9.0 - Hs[14] / 4.0;\n\tHs[6] = 4.0*(u - s) - 4.0*(Hs[10] + Hs[13]) / 9.0 - Hs[14] / 4.0;\n\tHs[7] = -4.0*t - 4.0*(Hs[11] + Hs[13]) / 9.0 - Hs[14] / 4.0;\n\tHs[8] = 0.0 - 4.0*(Hs[11] + Hs[12]) / 9.0 - Hs[14] / 4.0;\n\tHs[9] = 4.0*t - 4.0*(Hs[12] + Hs[13]) / 9.0 - Hs[14] / 4.0;\n\n\tHt[0] = -(4.0*u - 1.0) + (Ht[10] + Ht[11] + Ht[13]) / 9.0 + Ht[14] / 8.0;\n\tHt[1] = 0.0 + (Ht[10] + Ht[11] + Ht[12]) / 9.0 + Ht[14] / 8.0;\n\tHt[2] = 0.0 + (Ht[10] + Ht[12] + Ht[13]) / 9.0 + Ht[14] / 8.0;\n\tHt[3] = (4.0*t - 1.0) + (Ht[11] + Ht[12] + Ht[13]) / 9.0 + Ht[14] / 8.0;\n\tHt[4] = -4.0*r - 4.0*(Ht[10] + Ht[11]) / 9.0 - Ht[14] / 4.0;\n\tHt[5] = 0.0 - 4.0*(Ht[10] + Ht[12]) / 9.0 - Ht[14] / 4.0;\n\tHt[6] = -4.0*s - 4.0*(Ht[10] + Ht[13]) / 9.0 - Ht[14] / 4.0;\n\tHt[7] = 4.0*(u - t) - 4.0*(Ht[11] + Ht[13]) / 9.0 - Ht[14] / 4.0;\n\tHt[8] = 4.0*r - 4.0*(Ht[11] + Ht[12]) / 9.0 - Ht[14] / 4.0;\n\tHt[9] = 4.0*s - 4.0*(Ht[12] + Ht[13]) / 9.0 - Ht[14] / 4.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETet15::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tdouble u = 1.0 - r - s - t;\n\n\tHrr[14] = -512 * s*t;\n\tHss[14] = -512 * r*t;\n\tHtt[14] = -512 * r*s;\n\tHrs[14] = 256.0*t*(u - r - s);\n\tHst[14] = 256.0*r*(u - s - t);\n\tHrt[14] = 256.0*s*(u - r - t);\n\n\tHrr[10] = -54.0*s - 27.0*Hrr[14] / 64.0;\n\tHss[10] = -54.0*r - 27.0*Hss[14] / 64.0;\n\tHtt[10] = 0.0 - 27.0*Htt[14] / 64.0;\n\tHrs[10] = 27.0*(u - r - s) - 27.0*Hrs[14] / 64.0;\n\tHst[10] = -27.0*r - 27.0*Hst[14] / 64.0;\n\tHrt[10] = -27.0*s - 27.0*Hrt[14] / 64.0;\n\n\tHrr[11] = -54.0*t - 27.0*Hrr[14] / 64.0;\n\tHss[11] = 0.0 - 27.0*Hss[14] / 64.0;\n\tHtt[11] = -54.*r - 27.0*Htt[14] / 64.0;\n\tHrs[11] = -27.0*t - 27.0*Hrs[14] / 64.0;\n\tHst[11] = -27.0*r - 27.0*Hst[14] / 64.0;\n\tHrt[11] = 27.0*(u - r - t) - 27.0*Hrt[14] / 64.0;\n\n\tHrr[12] = 0.0 - 27.0*Hrr[14] / 64.0;\n\tHss[12] = 0.0 - 27.0*Hss[14] / 64.0;\n\tHtt[12] = 0.0 - 27.0*Htt[14] / 64.0;\n\tHrs[12] = 27.0*t - 27.0*Hrs[14] / 64.0;\n\tHst[12] = 27.0*r - 27.0*Hst[14] / 64.0;\n\tHrt[12] = 27.0*s - 27.0*Hrt[14] / 64.0;\n\n\tHrr[13] = 0.0 - 27.0*Hrr[14] / 64.0;\n\tHss[13] = -54.0*t - 27.0*Hss[14] / 64.0;\n\tHtt[13] = -54.0*s - 27.0*Htt[14] / 64.0;\n\tHrs[13] = -27.0*t - 27.0*Hrs[14] / 64.0;\n\tHst[13] = 27.0*(u - t - s) - 27.0*Hst[14] / 64.0;\n\tHrt[13] = -27.0*s - 27.0*Hrt[14] / 64.0;\n\n\tHrr[0] = 4.0 + (Hrr[10] + Hrr[11] + Hrr[13]) / 9.0 + Hrr[14] / 8.0;\n\tHss[0] = 4.0 + (Hss[10] + Hss[11] + Hss[13]) / 9.0 + Hss[14] / 8.0;\n\tHtt[0] = 4.0 + (Htt[10] + Htt[11] + Htt[13]) / 9.0 + Htt[14] / 8.0;\n\tHrs[0] = 4.0 + (Hrs[10] + Hrs[11] + Hrs[13]) / 9.0 + Hrs[14] / 8.0;\n\tHst[0] = 4.0 + (Hst[10] + Hst[11] + Hst[13]) / 9.0 + Hst[14] / 8.0;\n\tHrt[0] = 4.0 + (Hrt[10] + Hrt[11] + Hrt[13]) / 9.0 + Hrt[14] / 8.0;\n\n\tHrr[1] = 4.0 + (Hrr[10] + Hrr[11] + Hrr[12]) / 9.0 + Hrr[14] / 8.0;\n\tHss[1] = 0.0 + (Hss[10] + Hss[11] + Hss[12]) / 9.0 + Hss[14] / 8.0;\n\tHtt[1] = 0.0 + (Htt[10] + Htt[11] + Htt[12]) / 9.0 + Htt[14] / 8.0;\n\tHrs[1] = 0.0 + (Hrs[10] + Hrs[11] + Hrs[12]) / 9.0 + Hrs[14] / 8.0;\n\tHst[1] = 0.0 + (Hst[10] + Hst[11] + Hst[12]) / 9.0 + Hst[14] / 8.0;\n\tHrt[1] = 0.0 + (Hrt[10] + Hrt[11] + Hrt[12]) / 9.0 + Hrt[14] / 8.0;\n\n\tHrr[2] = 0.0 + (Hrr[10] + Hrr[12] + Hrr[13]) / 9.0 + Hrr[14] / 8.0;\n\tHss[2] = 4.0 + (Hss[10] + Hss[12] + Hss[13]) / 9.0 + Hss[14] / 8.0;\n\tHtt[2] = 0.0 + (Htt[10] + Htt[12] + Htt[13]) / 9.0 + Htt[14] / 8.0;\n\tHrs[2] = 0.0 + (Hrs[10] + Hrs[12] + Hrs[13]) / 9.0 + Hrs[14] / 8.0;\n\tHst[2] = 0.0 + (Hst[10] + Hst[12] + Hst[13]) / 9.0 + Hst[14] / 8.0;\n\tHrt[2] = 0.0 + (Hrt[10] + Hrt[12] + Hrt[13]) / 9.0 + Hrt[14] / 8.0;\n\n\tHrr[3] = 0.0 + (Hrr[11] + Hrr[12] + Hrr[13]) / 9.0 + Hrr[14] / 8.0;\n\tHss[3] = 0.0 + (Hss[11] + Hss[12] + Hss[13]) / 9.0 + Hss[14] / 8.0;\n\tHtt[3] = 4.0 + (Htt[11] + Htt[12] + Htt[13]) / 9.0 + Htt[14] / 8.0;\n\tHrs[3] = 0.0 + (Hrs[11] + Hrs[12] + Hrs[13]) / 9.0 + Hrs[14] / 8.0;\n\tHst[3] = 0.0 + (Hst[11] + Hst[12] + Hst[13]) / 9.0 + Hst[14] / 8.0;\n\tHrt[3] = 0.0 + (Hrt[11] + Hrt[12] + Hrt[13]) / 9.0 + Hrt[14] / 8.0;\n\n\tHrr[4] = -8.0 - 4.0*(Hrr[10] + Hrr[11]) / 9.0 - Hrr[14] / 4.0;\n\tHss[4] = 0.0 - 4.0*(Hss[10] + Hss[11]) / 9.0 - Hss[14] / 4.0;\n\tHtt[4] = 0.0 - 4.0*(Htt[10] + Htt[11]) / 9.0 - Htt[14] / 4.0;\n\tHrs[4] = -4.0 - 4.0*(Hrs[10] + Hrs[11]) / 9.0 - Hrs[14] / 4.0;\n\tHst[4] = 0.0 - 4.0*(Hst[10] + Hst[11]) / 9.0 - Hst[14] / 4.0;\n\tHrt[4] = -4.0 - 4.0*(Hrt[10] + Hrt[11]) / 9.0 - Hrt[14] / 4.0;\n\n\tHrr[5] = 0.0 - 4.0*(Hrr[10] + Hrr[12]) / 9.0 - Hrr[14] / 4.0;\n\tHss[5] = 0.0 - 4.0*(Hss[10] + Hss[12]) / 9.0 - Hss[14] / 4.0;\n\tHtt[5] = 0.0 - 4.0*(Htt[10] + Htt[12]) / 9.0 - Htt[14] / 4.0;\n\tHrs[5] = 4.0 - 4.0*(Hrs[10] + Hrs[12]) / 9.0 - Hrs[14] / 4.0;\n\tHst[5] = 0.0 - 4.0*(Hst[10] + Hst[12]) / 9.0 - Hst[14] / 4.0;\n\tHrt[5] = 0.0 - 4.0*(Hrt[10] + Hrt[12]) / 9.0 - Hrt[14] / 4.0;\n\n\tHrr[6] = 0.0 - 4.0*(Hrr[10] + Hrr[13]) / 9.0 - Hrr[14] / 4.0;\n\tHss[6] = -8.0 - 4.0*(Hss[10] + Hss[13]) / 9.0 - Hss[14] / 4.0;\n\tHtt[6] = 0.0 - 4.0*(Htt[10] + Htt[13]) / 9.0 - Htt[14] / 4.0;\n\tHrs[6] = -4.0 - 4.0*(Hrs[10] + Hrs[13]) / 9.0 - Hrs[14] / 4.0;\n\tHst[6] = -4.0 - 4.0*(Hst[10] + Hst[13]) / 9.0 - Hst[14] / 4.0;\n\tHrt[6] = 0.0 - 4.0*(Hrt[10] + Hrt[13]) / 9.0 - Hrt[14] / 4.0;\n\n\tHrr[7] = 0.0 - 4.0*(Hrr[11] + Hrr[13]) / 9.0 - Hrr[14] / 4.0;\n\tHss[7] = 0.0 - 4.0*(Hss[11] + Hss[13]) / 9.0 - Hss[14] / 4.0;\n\tHtt[7] = -8.0 - 4.0*(Htt[11] + Htt[13]) / 9.0 - Htt[14] / 4.0;\n\tHrs[7] = 0.0 - 4.0*(Hrs[11] + Hrs[13]) / 9.0 - Hrs[14] / 4.0;\n\tHst[7] = -4.0 - 4.0*(Hst[11] + Hst[13]) / 9.0 - Hst[14] / 4.0;\n\tHrt[7] = -4.0 - 4.0*(Hrt[11] + Hrt[13]) / 9.0 - Hrt[14] / 4.0;\n\n\tHrr[8] = 0.0 - 4.0*(Hrr[11] + Hrr[12]) / 9.0 - Hrr[14] / 4.0;\n\tHss[8] = 0.0 - 4.0*(Hss[11] + Hss[12]) / 9.0 - Hss[14] / 4.0;\n\tHtt[8] = 0.0 - 4.0*(Htt[11] + Htt[12]) / 9.0 - Htt[14] / 4.0;\n\tHrs[8] = 0.0 - 4.0*(Hrs[11] + Hrs[12]) / 9.0 - Hrs[14] / 4.0;\n\tHst[8] = 0.0 - 4.0*(Hst[11] + Hst[12]) / 9.0 - Hst[14] / 4.0;\n\tHrt[8] = 4.0 - 4.0*(Hrt[11] + Hrt[12]) / 9.0 - Hrt[14] / 4.0;\n\n\tHrr[9] = 0.0 - 4.0*(Hrr[12] + Hrr[13]) / 9.0 - Hrr[14] / 4.0;\n\tHss[9] = 0.0 - 4.0*(Hss[12] + Hss[13]) / 9.0 - Hss[14] / 4.0;\n\tHtt[9] = 0.0 - 4.0*(Htt[12] + Htt[13]) / 9.0 - Htt[14] / 4.0;\n\tHrs[9] = 0.0 - 4.0*(Hrs[12] + Hrs[13]) / 9.0 - Hrs[14] / 4.0;\n\tHst[9] = 4.0 - 4.0*(Hst[12] + Hst[13]) / 9.0 - Hst[14] / 4.0;\n\tHrt[9] = 0.0 - 4.0*(Hrt[12] + Hrt[13]) / 9.0 - Hrt[14] / 4.0;\n}\n\n//=============================================================================\n//                           T E T 2 0\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FETet20::shape_fnc(double* H, double r, double s, double t)\n{\n\tdouble L1 = 1.0 - r - s - t;\n\tdouble L2 = r;\n\tdouble L3 = s;\n\tdouble L4 = t;\n\n\tH[0] = 0.5*(3 * L1 - 1)*(3 * L1 - 2)*L1;\n\tH[1] = 0.5*(3 * L2 - 1)*(3 * L2 - 2)*L2;\n\tH[2] = 0.5*(3 * L3 - 1)*(3 * L3 - 2)*L3;\n\tH[3] = 0.5*(3 * L4 - 1)*(3 * L4 - 2)*L4;\n\tH[4] = 9.0 / 2.0*(3 * L1 - 1)*L1*L2;\n\tH[5] = 9.0 / 2.0*(3 * L2 - 1)*L1*L2;\n\tH[6] = 9.0 / 2.0*(3 * L2 - 1)*L2*L3;\n\tH[7] = 9.0 / 2.0*(3 * L3 - 1)*L2*L3;\n\tH[8] = 9.0 / 2.0*(3 * L1 - 1)*L1*L3;\n\tH[9] = 9.0 / 2.0*(3 * L3 - 1)*L1*L3;\n\tH[10] = 9.0 / 2.0*(3 * L1 - 1)*L1*L4;\n\tH[11] = 9.0 / 2.0*(3 * L4 - 1)*L1*L4;\n\tH[12] = 9.0 / 2.0*(3 * L2 - 1)*L2*L4;\n\tH[13] = 9.0 / 2.0*(3 * L4 - 1)*L2*L4;\n\tH[14] = 9.0 / 2.0*(3 * L3 - 1)*L3*L4;\n\tH[15] = 9.0 / 2.0*(3 * L4 - 1)*L3*L4;\n\tH[16] = 27.0*L1*L2*L4;\n\tH[17] = 27.0*L2*L3*L4;\n\tH[18] = 27.0*L1*L3*L4;\n\tH[19] = 27.0*L1*L2*L3;\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FETet20::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tdouble L1 = 1.0 - r - s - t;\n\tdouble L2 = r;\n\tdouble L3 = s;\n\tdouble L4 = t;\n\n\tHr[0] = -3. / 2.*(3 * L1 - 2)*L1 - 3. / 2.*(3 * L1 - 1)*L1 - 0.5*(3 * L1 - 1)*(3 * L1 - 2);\n\tHr[1] = 3. / 2.*(3 * L2 - 2)*L2 + 3. / 2.*(3 * L2 - 1)*L2 + 0.5*(3 * L2 - 1)*(3 * L2 - 2);\n\tHr[2] = 0.0;\n\tHr[3] = 0.0;\n\tHr[4] = -27. / 2.*L1*L2 - 9.0 / 2.0*(3 * L1 - 1)*L2 + 9.0 / 2.0*(3 * L1 - 1)*L1;\n\tHr[5] = 27. / 2.*L1*L2 - 9.0 / 2.0*(3 * L2 - 1)*L2 + 9.0 / 2.0*(3 * L2 - 1)*L1;\n\tHr[6] = 27. / 2.*L2*L3 + 9.0 / 2.0*(3 * L2 - 1)*L3;\n\tHr[7] = 9.0 / 2.0*(3 * L3 - 1)*L3;\n\tHr[8] = -27. / 2.*L1*L3 - 9.0 / 2.0*(3 * L1 - 1)*L3;\n\tHr[9] = -9.0 / 2.0*(3 * L3 - 1)*L3;\n\tHr[10] = -27. / 2.*L1*L4 - 9.0 / 2.0*(3 * L1 - 1)*L4;\n\tHr[11] = -9. / 2.*(3 * L4 - 1)*L4;\n\tHr[12] = 27. / 2.*L2*L4 + 9. / 2.*(3 * L2 - 1)*L4;\n\tHr[13] = 9. / 2.*(3 * L4 - 1)*L4;\n\tHr[14] = 0.0;\n\tHr[15] = 0.0;\n\tHr[16] = -27 * L2*L4 + 27 * L1*L4;\n\tHr[17] = 27 * L3*L4;\n\tHr[18] = -27 * L3*L4;\n\tHr[19] = -27 * L2*L3 + 27 * L1*L3;\n\n\tHs[0] = -3. / 2.*(3 * L1 - 2)*L1 - 3. / 2.*(3 * L1 - 1)*L1 - 0.5*(3 * L1 - 1)*(3 * L1 - 2);\n\tHs[1] = 0.0;\n\tHs[2] = 3. / 2.*(3 * L3 - 2)*L3 + 3. / 2.*(3 * L3 - 1)*L3 + 0.5*(3 * L3 - 1)*(3 * L3 - 2);\n\tHs[3] = 0.0;\n\tHs[4] = -27. / 2.*L1*L2 - 9. / 2.*(3 * L1 - 1)*L2;\n\tHs[5] = -9. / 2.*(3 * L2 - 1)*L2;\n\tHs[6] = 9. / 2.*(3 * L2 - 1)*L2;\n\tHs[7] = 27. / 2.*L2*L3 + 9. / 2.*(3 * L3 - 1)*L2;\n\tHs[8] = -27. / 2.*L1*L3 - 9. / 2.*(3 * L1 - 1)*L3 + 9. / 2.*(3 * L1 - 1)*L1;\n\tHs[9] = 27. / 2.*L1*L3 - 9. / 2.*(3 * L3 - 1)*L3 + 9. / 2.*(3 * L3 - 1)*L1;\n\tHs[10] = -27. / 2.*L1*L4 - 9. / 2.*(3 * L1 - 1)*L4;\n\tHs[11] = -9. / 2.*(3 * L4 - 1)*L4;\n\tHs[12] = 0.0;\n\tHs[13] = 0.0;\n\tHs[14] = 27. / 2.*L3*L4 + 9. / 2.*(3 * L3 - 1)*L4;\n\tHs[15] = 9. / 2.*(3 * L4 - 1)*L4;\n\tHs[16] = -27 * L2*L4;\n\tHs[17] = 27 * L2*L4;\n\tHs[18] = -27 * L3*L4 + 27 * L1*L4;\n\tHs[19] = -27 * L2*L3 + 27 * L1*L2;\n\n\tHt[0] = -3. / 2.*(3 * L1 - 2)*L1 - 3. / 2.*(3 * L1 - 1)*L1 - 0.5*(3 * L1 - 1)*(3 * L1 - 2);\n\tHt[1] = 0.0;\n\tHt[2] = 0.0;\n\tHt[3] = 3. / 2.*(3 * L4 - 2)*L4 + 3. / 2.*(3 * L4 - 1)*L4 + 0.5*(3 * L4 - 1)*(3 * L4 - 2);\n\tHt[4] = -27. / 2.*L1*L2 - 9. / 2.*(3 * L1 - 1)*L2;\n\tHt[5] = -9. / 2.*(3 * L2 - 1)*L2;\n\tHt[6] = 0.0;\n\tHt[7] = 0.0;\n\tHt[8] = -27. / 2.*L1*L3 - 9. / 2.*(3 * L1 - 1)*L3;\n\tHt[9] = -9. / 2.*(3 * L3 - 1)*L3;\n\tHt[10] = -27. / 2.*L1*L4 - 9. / 2.*(3 * L1 - 1)*L4 + 9. / 2.*(3 * L1 - 1)*L1;\n\tHt[11] = 27. / 2.*L1*L4 - 9. / 2.*(3 * L4 - 1)*L4 + 9. / 2.*(3 * L4 - 1)*L1;\n\tHt[12] = 9. / 2.*(3 * L2 - 1)*L2;\n\tHt[13] = 27. / 2.*L2*L4 + 9. / 2.*(3 * L4 - 1)*L2;\n\tHt[14] = 9. / 2.*(3 * L3 - 1)*L3;\n\tHt[15] = 27. / 2.*L3*L4 + 9. / 2.*(3 * L4 - 1)*L3;\n\tHt[16] = -27 * L2*L4 + 27 * L1*L2;\n\tHt[17] = 27 * L2*L3;\n\tHt[18] = -27 * L3*L4 + 27 * L1*L3;\n\tHt[19] = -27 * L2*L3;\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo implement this\nvoid FETet20::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\t// do this\n}\n\n\n//=============================================================================\n//              H E X 2 0\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEHex20::shape_fnc(double* H, double r, double s, double t)\n{\n\tH[8] = 0.25*(1 - r*r)*(1 - s)*(1 - t);\n\tH[9] = 0.25*(1 - s*s)*(1 + r)*(1 - t);\n\tH[10] = 0.25*(1 - r*r)*(1 + s)*(1 - t);\n\tH[11] = 0.25*(1 - s*s)*(1 - r)*(1 - t);\n\tH[12] = 0.25*(1 - r*r)*(1 - s)*(1 + t);\n\tH[13] = 0.25*(1 - s*s)*(1 + r)*(1 + t);\n\tH[14] = 0.25*(1 - r*r)*(1 + s)*(1 + t);\n\tH[15] = 0.25*(1 - s*s)*(1 - r)*(1 + t);\n\tH[16] = 0.25*(1 - t*t)*(1 - r)*(1 - s);\n\tH[17] = 0.25*(1 - t*t)*(1 + r)*(1 - s);\n\tH[18] = 0.25*(1 - t*t)*(1 + r)*(1 + s);\n\tH[19] = 0.25*(1 - t*t)*(1 - r)*(1 + s);\n\n\tH[0] = 0.125*(1 - r)*(1 - s)*(1 - t) - 0.5*(H[8] + H[11] + H[16]);\n\tH[1] = 0.125*(1 + r)*(1 - s)*(1 - t) - 0.5*(H[8] + H[9] + H[17]);\n\tH[2] = 0.125*(1 + r)*(1 + s)*(1 - t) - 0.5*(H[9] + H[10] + H[18]);\n\tH[3] = 0.125*(1 - r)*(1 + s)*(1 - t) - 0.5*(H[10] + H[11] + H[19]);\n\tH[4] = 0.125*(1 - r)*(1 - s)*(1 + t) - 0.5*(H[12] + H[15] + H[16]);\n\tH[5] = 0.125*(1 + r)*(1 - s)*(1 + t) - 0.5*(H[12] + H[13] + H[17]);\n\tH[6] = 0.125*(1 + r)*(1 + s)*(1 + t) - 0.5*(H[13] + H[14] + H[18]);\n\tH[7] = 0.125*(1 - r)*(1 + s)*(1 + t) - 0.5*(H[14] + H[15] + H[19]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEHex20::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[8] = -0.5*r*(1 - s)*(1 - t);\n\tHr[9] = 0.25*(1 - s*s)*(1 - t);\n\tHr[10] = -0.5*r*(1 + s)*(1 - t);\n\tHr[11] = -0.25*(1 - s*s)*(1 - t);\n\tHr[12] = -0.5*r*(1 - s)*(1 + t);\n\tHr[13] = 0.25*(1 - s*s)*(1 + t);\n\tHr[14] = -0.5*r*(1 + s)*(1 + t);\n\tHr[15] = -0.25*(1 - s*s)*(1 + t);\n\tHr[16] = -0.25*(1 - t*t)*(1 - s);\n\tHr[17] = 0.25*(1 - t*t)*(1 - s);\n\tHr[18] = 0.25*(1 - t*t)*(1 + s);\n\tHr[19] = -0.25*(1 - t*t)*(1 + s);\n\n\tHr[0] = -0.125*(1 - s)*(1 - t) - 0.5*(Hr[8] + Hr[11] + Hr[16]);\n\tHr[1] = 0.125*(1 - s)*(1 - t) - 0.5*(Hr[8] + Hr[9] + Hr[17]);\n\tHr[2] = 0.125*(1 + s)*(1 - t) - 0.5*(Hr[9] + Hr[10] + Hr[18]);\n\tHr[3] = -0.125*(1 + s)*(1 - t) - 0.5*(Hr[10] + Hr[11] + Hr[19]);\n\tHr[4] = -0.125*(1 - s)*(1 + t) - 0.5*(Hr[12] + Hr[15] + Hr[16]);\n\tHr[5] = 0.125*(1 - s)*(1 + t) - 0.5*(Hr[12] + Hr[13] + Hr[17]);\n\tHr[6] = 0.125*(1 + s)*(1 + t) - 0.5*(Hr[13] + Hr[14] + Hr[18]);\n\tHr[7] = -0.125*(1 + s)*(1 + t) - 0.5*(Hr[14] + Hr[15] + Hr[19]);\n\n\tHs[8] = -0.25*(1 - r*r)*(1 - t);\n\tHs[9] = -0.5*s*(1 + r)*(1 - t);\n\tHs[10] = 0.25*(1 - r*r)*(1 - t);\n\tHs[11] = -0.5*s*(1 - r)*(1 - t);\n\tHs[12] = -0.25*(1 - r*r)*(1 + t);\n\tHs[13] = -0.5*s*(1 + r)*(1 + t);\n\tHs[14] = 0.25*(1 - r*r)*(1 + t);\n\tHs[15] = -0.5*s*(1 - r)*(1 + t);\n\tHs[16] = -0.25*(1 - t*t)*(1 - r);\n\tHs[17] = -0.25*(1 - t*t)*(1 + r);\n\tHs[18] = 0.25*(1 - t*t)*(1 + r);\n\tHs[19] = 0.25*(1 - t*t)*(1 - r);\n\n\tHs[0] = -0.125*(1 - r)*(1 - t) - 0.5*(Hs[8] + Hs[11] + Hs[16]);\n\tHs[1] = -0.125*(1 + r)*(1 - t) - 0.5*(Hs[8] + Hs[9] + Hs[17]);\n\tHs[2] = 0.125*(1 + r)*(1 - t) - 0.5*(Hs[9] + Hs[10] + Hs[18]);\n\tHs[3] = 0.125*(1 - r)*(1 - t) - 0.5*(Hs[10] + Hs[11] + Hs[19]);\n\tHs[4] = -0.125*(1 - r)*(1 + t) - 0.5*(Hs[12] + Hs[15] + Hs[16]);\n\tHs[5] = -0.125*(1 + r)*(1 + t) - 0.5*(Hs[12] + Hs[13] + Hs[17]);\n\tHs[6] = 0.125*(1 + r)*(1 + t) - 0.5*(Hs[13] + Hs[14] + Hs[18]);\n\tHs[7] = 0.125*(1 - r)*(1 + t) - 0.5*(Hs[14] + Hs[15] + Hs[19]);\n\n\tHt[8] = -0.25*(1 - r*r)*(1 - s);\n\tHt[9] = -0.25*(1 - s*s)*(1 + r);\n\tHt[10] = -0.25*(1 - r*r)*(1 + s);\n\tHt[11] = -0.25*(1 - s*s)*(1 - r);\n\tHt[12] = 0.25*(1 - r*r)*(1 - s);\n\tHt[13] = 0.25*(1 - s*s)*(1 + r);\n\tHt[14] = 0.25*(1 - r*r)*(1 + s);\n\tHt[15] = 0.25*(1 - s*s)*(1 - r);\n\tHt[16] = -0.5*t*(1 - r)*(1 - s);\n\tHt[17] = -0.5*t*(1 + r)*(1 - s);\n\tHt[18] = -0.5*t*(1 + r)*(1 + s);\n\tHt[19] = -0.5*t*(1 - r)*(1 + s);\n\n\tHt[0] = -0.125*(1 - r)*(1 - s) - 0.5*(Ht[8] + Ht[11] + Ht[16]);\n\tHt[1] = -0.125*(1 + r)*(1 - s) - 0.5*(Ht[8] + Ht[9] + Ht[17]);\n\tHt[2] = -0.125*(1 + r)*(1 + s) - 0.5*(Ht[9] + Ht[10] + Ht[18]);\n\tHt[3] = -0.125*(1 - r)*(1 + s) - 0.5*(Ht[10] + Ht[11] + Ht[19]);\n\tHt[4] = 0.125*(1 - r)*(1 - s) - 0.5*(Ht[12] + Ht[15] + Ht[16]);\n\tHt[5] = 0.125*(1 + r)*(1 - s) - 0.5*(Ht[12] + Ht[13] + Ht[17]);\n\tHt[6] = 0.125*(1 + r)*(1 + s) - 0.5*(Ht[13] + Ht[14] + Ht[18]);\n\tHt[7] = 0.125*(1 - r)*(1 + s) - 0.5*(Ht[14] + Ht[15] + Ht[19]);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEHex20::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\t// Hrr\n\tHrr[8] = -0.5*(1 - s)*(1 - t);\n\tHrr[9] = 0.;\n\tHrr[10] = -0.5*(1 + s)*(1 - t);\n\tHrr[11] = 0.;\n\tHrr[12] = -0.5*(1 - s)*(1 + t);\n\tHrr[13] = 0.;\n\tHrr[14] = -0.5*(1 + s)*(1 + t);\n\tHrr[15] = 0.;\n\tHrr[16] = 0.;\n\tHrr[17] = 0.;\n\tHrr[18] = 0.;\n\tHrr[19] = 0.;\n\n\tHrr[0] = -0.5*(Hrr[8] + Hrr[11] + Hrr[16]);\n\tHrr[1] = -0.5*(Hrr[8] + Hrr[9] + Hrr[17]);\n\tHrr[2] = -0.5*(Hrr[9] + Hrr[10] + Hrr[18]);\n\tHrr[3] = -0.5*(Hrr[10] + Hrr[11] + Hrr[19]);\n\tHrr[4] = -0.5*(Hrr[12] + Hrr[15] + Hrr[16]);\n\tHrr[5] = -0.5*(Hrr[12] + Hrr[13] + Hrr[17]);\n\tHrr[6] = -0.5*(Hrr[13] + Hrr[14] + Hrr[18]);\n\tHrr[7] = -0.5*(Hrr[14] + Hrr[15] + Hrr[19]);\n\n\t// Hss\n\tHss[8] = 0.;\n\tHss[9] = -0.5*(1 + r)*(1 - t);\n\tHss[10] = 0.;\n\tHss[11] = -0.5*(1 - r)*(1 - t);\n\tHss[12] = 0.;\n\tHss[13] = -0.5*(1 + r)*(1 + t);\n\tHss[14] = 0.;\n\tHss[15] = -0.5*(1 - r)*(1 + t);\n\tHss[16] = 0.;\n\tHss[17] = 0.;\n\tHss[18] = 0.;\n\tHss[19] = 0.;\n\n\tHss[0] = -0.5*(Hss[8] + Hss[11] + Hss[16]);\n\tHss[1] = -0.5*(Hss[8] + Hss[9] + Hss[17]);\n\tHss[2] = -0.5*(Hss[9] + Hss[10] + Hss[18]);\n\tHss[3] = -0.5*(Hss[10] + Hss[11] + Hss[19]);\n\tHss[4] = -0.5*(Hss[12] + Hss[15] + Hss[16]);\n\tHss[5] = -0.5*(Hss[12] + Hss[13] + Hss[17]);\n\tHss[6] = -0.5*(Hss[13] + Hss[14] + Hss[18]);\n\tHss[7] = -0.5*(Hss[14] + Hss[15] + Hss[19]);\n\n\t// Htt\n\tHtt[8] = 0.;\n\tHtt[9] = 0.;\n\tHtt[10] = 0.;\n\tHtt[11] = 0.;\n\tHtt[12] = 0.;\n\tHtt[13] = 0.;\n\tHtt[14] = 0.;\n\tHtt[15] = 0.;\n\tHtt[16] = -0.5*(1 - r)*(1 - s);\n\tHtt[17] = -0.5*(1 + r)*(1 - s);\n\tHtt[18] = -0.5*(1 + r)*(1 + s);\n\tHtt[19] = -0.5*(1 - r)*(1 + s);\n\n\tHtt[0] = -0.5*(Htt[8] + Htt[11] + Htt[16]);\n\tHtt[1] = -0.5*(Htt[8] + Htt[9] + Htt[17]);\n\tHtt[2] = -0.5*(Htt[9] + Htt[10] + Htt[18]);\n\tHtt[3] = -0.5*(Htt[10] + Htt[11] + Htt[19]);\n\tHtt[4] = -0.5*(Htt[12] + Htt[15] + Htt[16]);\n\tHtt[5] = -0.5*(Htt[12] + Htt[13] + Htt[17]);\n\tHtt[6] = -0.5*(Htt[13] + Htt[14] + Htt[18]);\n\tHtt[7] = -0.5*(Htt[14] + Htt[15] + Htt[19]);\n\n\t// Hrs\n\tHrs[8] = 0.5*r*(1 - t);\n\tHrs[9] = -0.5*s*(1 - t);\n\tHrs[10] = -0.5*r*(1 - t);\n\tHrs[11] = 0.5*s*(1 - t);\n\tHrs[12] = 0.5*r*(1 + t);\n\tHrs[13] = -0.5*s*(1 + t);\n\tHrs[14] = -0.5*r*(1 + t);\n\tHrs[15] = 0.5*s*(1 + t);\n\tHrs[16] = 0.25*(1 - t*t);\n\tHrs[17] = -0.25*(1 - t*t);\n\tHrs[18] = 0.25*(1 - t*t);\n\tHrs[19] = -0.25*(1 - t*t);\n\n\tHrs[0] = 0.125*(1 - t) - 0.5*(Hrs[8] + Hrs[11] + Hrs[16]);\n\tHrs[1] = -0.125*(1 - t) - 0.5*(Hrs[8] + Hrs[9] + Hrs[17]);\n\tHrs[2] = 0.125*(1 - t) - 0.5*(Hrs[9] + Hrs[10] + Hrs[18]);\n\tHrs[3] = -0.125*(1 - t) - 0.5*(Hrs[10] + Hrs[11] + Hrs[19]);\n\tHrs[4] = 0.125*(1 + t) - 0.5*(Hrs[12] + Hrs[15] + Hrs[16]);\n\tHrs[5] = -0.125*(1 + t) - 0.5*(Hrs[12] + Hrs[13] + Hrs[17]);\n\tHrs[6] = 0.125*(1 + t) - 0.5*(Hrs[13] + Hrs[14] + Hrs[18]);\n\tHrs[7] = -0.125*(1 + t) - 0.5*(Hrs[14] + Hrs[15] + Hrs[19]);\n\n\t// Hst\n\tHst[8] = 0.25*(1 - r*r);\n\tHst[9] = 0.5*s*(1 + r);\n\tHst[10] = -0.25*(1 - r*r);\n\tHst[11] = 0.5*s*(1 - r);\n\tHst[12] = -0.25*(1 - r*r);\n\tHst[13] = -0.5*s*(1 + r);\n\tHst[14] = 0.25*(1 - r*r);\n\tHst[15] = -0.5*s*(1 - r);\n\tHst[16] = 0.5*t*(1 - r);\n\tHst[17] = 0.5*t*(1 + r);\n\tHst[18] = -0.5*t*(1 + r);\n\tHst[19] = -0.5*t*(1 - r);\n\n\tHst[0] = 0.125*(1 - r) - 0.5*(Hst[8] + Hst[11] + Hst[16]);\n\tHst[1] = 0.125*(1 + r) - 0.5*(Hst[8] + Hst[9] + Hst[17]);\n\tHst[2] = -0.125*(1 + r) - 0.5*(Hst[9] + Hst[10] + Hst[18]);\n\tHst[3] = -0.125*(1 - r) - 0.5*(Hst[10] + Hst[11] + Hst[19]);\n\tHst[4] = -0.125*(1 - r) - 0.5*(Hst[12] + Hst[15] + Hst[16]);\n\tHst[5] = -0.125*(1 + r) - 0.5*(Hst[12] + Hst[13] + Hst[17]);\n\tHst[6] = 0.125*(1 + r) - 0.5*(Hst[13] + Hst[14] + Hst[18]);\n\tHst[7] = 0.125*(1 - r) - 0.5*(Hst[14] + Hst[15] + Hst[19]);\n\n\t// Hrt\n\tHrt[8] = 0.5*r*(1 - s);\n\tHrt[9] = -0.25*(1 - s*s);\n\tHrt[10] = 0.5*r*(1 + s);\n\tHrt[11] = 0.25*(1 - s*s);\n\tHrt[12] = -0.5*r*(1 - s);\n\tHrt[13] = 0.25*(1 - s*s);\n\tHrt[14] = -0.5*r*(1 + s);\n\tHrt[15] = -0.25*(1 - s*s);\n\tHrt[16] = 0.5*t*(1 - s);\n\tHrt[17] = -0.5*t*(1 - s);\n\tHrt[18] = -0.5*t*(1 + s);\n\tHrt[19] = 0.5*t*(1 + s);\n\n\tHrt[0] = 0.125*(1 - s) - 0.5*(Hrt[8] + Hrt[11] + Hrt[16]);\n\tHrt[1] = -0.125*(1 - s) - 0.5*(Hrt[8] + Hrt[9] + Hrt[17]);\n\tHrt[2] = -0.125*(1 + s) - 0.5*(Hrt[9] + Hrt[10] + Hrt[18]);\n\tHrt[3] = 0.125*(1 + s) - 0.5*(Hrt[10] + Hrt[11] + Hrt[19]);\n\tHrt[4] = -0.125*(1 - s) - 0.5*(Hrt[12] + Hrt[15] + Hrt[16]);\n\tHrt[5] = 0.125*(1 - s) - 0.5*(Hrt[12] + Hrt[13] + Hrt[17]);\n\tHrt[6] = 0.125*(1 + s) - 0.5*(Hrt[13] + Hrt[14] + Hrt[18]);\n\tHrt[7] = -0.125*(1 + s) - 0.5*(Hrt[14] + Hrt[15] + Hrt[19]);\n}\n\n//=============================================================================\n//              H E X 2 7\n//=============================================================================\n// Lookup table for 27-node hex that maps a node index into a triplet that identifies\n// the shape functions.\nstatic int HEX27_LUT[27][3] = {\n\t{ 0,0,0 },\n\t{ 1,0,0 },\n\t{ 1,1,0 },\n\t{ 0,1,0 },\n\t{ 0,0,1 },\n\t{ 1,0,1 },\n\t{ 1,1,1 },\n\t{ 0,1,1 },\n\t{ 2,0,0 },\n\t{ 1,2,0 },\n\t{ 2,1,0 },\n\t{ 0,2,0 },\n\t{ 2,0,1 },\n\t{ 1,2,1 },\n\t{ 2,1,1 },\n\t{ 0,2,1 },\n\t{ 0,0,2 },\n\t{ 1,0,2 },\n\t{ 1,1,2 },\n\t{ 0,1,2 },\n\t{ 2,0,2 },\n\t{ 1,2,2 },\n\t{ 2,1,2 },\n\t{ 0,2,2 },\n\t{ 2,2,0 },\n\t{ 2,2,1 },\n\t{ 2,2,2 }\n};\n\n//-----------------------------------------------------------------------------\n//! values of shape functions\nvoid FEHex27::shape_fnc(double* H, double r, double s, double t)\n{\n\tdouble R[3] = { 0.5*r*(r - 1.0), 0.5*r*(r + 1.0), 1.0 - r*r };\n\tdouble S[3] = { 0.5*s*(s - 1.0), 0.5*s*(s + 1.0), 1.0 - s*s };\n\tdouble T[3] = { 0.5*t*(t - 1.0), 0.5*t*(t + 1.0), 1.0 - t*t };\n\n\tH[0] = R[0] * S[0] * T[0];\n\tH[1] = R[1] * S[0] * T[0];\n\tH[2] = R[1] * S[1] * T[0];\n\tH[3] = R[0] * S[1] * T[0];\n\tH[4] = R[0] * S[0] * T[1];\n\tH[5] = R[1] * S[0] * T[1];\n\tH[6] = R[1] * S[1] * T[1];\n\tH[7] = R[0] * S[1] * T[1];\n\tH[8] = R[2] * S[0] * T[0];\n\tH[9] = R[1] * S[2] * T[0];\n\tH[10] = R[2] * S[1] * T[0];\n\tH[11] = R[0] * S[2] * T[0];\n\tH[12] = R[2] * S[0] * T[1];\n\tH[13] = R[1] * S[2] * T[1];\n\tH[14] = R[2] * S[1] * T[1];\n\tH[15] = R[0] * S[2] * T[1];\n\tH[16] = R[0] * S[0] * T[2];\n\tH[17] = R[1] * S[0] * T[2];\n\tH[18] = R[1] * S[1] * T[2];\n\tH[19] = R[0] * S[1] * T[2];\n\tH[20] = R[2] * S[0] * T[2];\n\tH[21] = R[1] * S[2] * T[2];\n\tH[22] = R[2] * S[1] * T[2];\n\tH[23] = R[0] * S[2] * T[2];\n\tH[24] = R[2] * S[2] * T[0];\n\tH[25] = R[2] * S[2] * T[1];\n\tH[26] = R[2] * S[2] * T[2];\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function derivatives\nvoid FEHex27::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tdouble R[3] = { 0.5*r*(r - 1.0), 0.5*r*(r + 1.0), 1.0 - r*r };\n\tdouble S[3] = { 0.5*s*(s - 1.0), 0.5*s*(s + 1.0), 1.0 - s*s };\n\tdouble T[3] = { 0.5*t*(t - 1.0), 0.5*t*(t + 1.0), 1.0 - t*t };\n\n\tdouble DR[3] = { r - 0.5, r + 0.5, -2.0*r };\n\tdouble DS[3] = { s - 0.5, s + 0.5, -2.0*s };\n\tdouble DT[3] = { t - 0.5, t + 0.5, -2.0*t };\n\n\tHr[0] = DR[0] * S[0] * T[0]; Hs[0] = R[0] * DS[0] * T[0]; Ht[0] = R[0] * S[0] * DT[0];\n\tHr[1] = DR[1] * S[0] * T[0]; Hs[1] = R[1] * DS[0] * T[0];\tHt[1] = R[1] * S[0] * DT[0];\n\tHr[2] = DR[1] * S[1] * T[0]; Hs[2] = R[1] * DS[1] * T[0];\tHt[2] = R[1] * S[1] * DT[0];\n\tHr[3] = DR[0] * S[1] * T[0]; Hs[3] = R[0] * DS[1] * T[0];\tHt[3] = R[0] * S[1] * DT[0];\n\tHr[4] = DR[0] * S[0] * T[1]; Hs[4] = R[0] * DS[0] * T[1];\tHt[4] = R[0] * S[0] * DT[1];\n\tHr[5] = DR[1] * S[0] * T[1]; Hs[5] = R[1] * DS[0] * T[1];\tHt[5] = R[1] * S[0] * DT[1];\n\tHr[6] = DR[1] * S[1] * T[1]; Hs[6] = R[1] * DS[1] * T[1];\tHt[6] = R[1] * S[1] * DT[1];\n\tHr[7] = DR[0] * S[1] * T[1]; Hs[7] = R[0] * DS[1] * T[1];\tHt[7] = R[0] * S[1] * DT[1];\n\tHr[8] = DR[2] * S[0] * T[0]; Hs[8] = R[2] * DS[0] * T[0];\tHt[8] = R[2] * S[0] * DT[0];\n\tHr[9] = DR[1] * S[2] * T[0]; Hs[9] = R[1] * DS[2] * T[0];\tHt[9] = R[1] * S[2] * DT[0];\n\tHr[10] = DR[2] * S[1] * T[0]; Hs[10] = R[2] * DS[1] * T[0];\tHt[10] = R[2] * S[1] * DT[0];\n\tHr[11] = DR[0] * S[2] * T[0]; Hs[11] = R[0] * DS[2] * T[0];\tHt[11] = R[0] * S[2] * DT[0];\n\tHr[12] = DR[2] * S[0] * T[1]; Hs[12] = R[2] * DS[0] * T[1];\tHt[12] = R[2] * S[0] * DT[1];\n\tHr[13] = DR[1] * S[2] * T[1]; Hs[13] = R[1] * DS[2] * T[1];\tHt[13] = R[1] * S[2] * DT[1];\n\tHr[14] = DR[2] * S[1] * T[1]; Hs[14] = R[2] * DS[1] * T[1];\tHt[14] = R[2] * S[1] * DT[1];\n\tHr[15] = DR[0] * S[2] * T[1]; Hs[15] = R[0] * DS[2] * T[1];\tHt[15] = R[0] * S[2] * DT[1];\n\tHr[16] = DR[0] * S[0] * T[2]; Hs[16] = R[0] * DS[0] * T[2];\tHt[16] = R[0] * S[0] * DT[2];\n\tHr[17] = DR[1] * S[0] * T[2]; Hs[17] = R[1] * DS[0] * T[2];\tHt[17] = R[1] * S[0] * DT[2];\n\tHr[18] = DR[1] * S[1] * T[2]; Hs[18] = R[1] * DS[1] * T[2];\tHt[18] = R[1] * S[1] * DT[2];\n\tHr[19] = DR[0] * S[1] * T[2]; Hs[19] = R[0] * DS[1] * T[2];\tHt[19] = R[0] * S[1] * DT[2];\n\tHr[20] = DR[2] * S[0] * T[2]; Hs[20] = R[2] * DS[0] * T[2];\tHt[20] = R[2] * S[0] * DT[2];\n\tHr[21] = DR[1] * S[2] * T[2]; Hs[21] = R[1] * DS[2] * T[2];\tHt[21] = R[1] * S[2] * DT[2];\n\tHr[22] = DR[2] * S[1] * T[2]; Hs[22] = R[2] * DS[1] * T[2];\tHt[22] = R[2] * S[1] * DT[2];\n\tHr[23] = DR[0] * S[2] * T[2]; Hs[23] = R[0] * DS[2] * T[2];\tHt[23] = R[0] * S[2] * DT[2];\n\tHr[24] = DR[2] * S[2] * T[0]; Hs[24] = R[2] * DS[2] * T[0];\tHt[24] = R[2] * S[2] * DT[0];\n\tHr[25] = DR[2] * S[2] * T[1]; Hs[25] = R[2] * DS[2] * T[1];\tHt[25] = R[2] * S[2] * DT[1];\n\tHr[26] = DR[2] * S[2] * T[2]; Hs[26] = R[2] * DS[2] * T[2];\tHt[26] = R[2] * S[2] * DT[2];\n}\n\n//-----------------------------------------------------------------------------\n//! values of shape function second derivatives\nvoid FEHex27::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tdouble NR[3] = { 0.5*r*(r - 1.0), 0.5*r*(r + 1.0), 1.0 - r*r };\n\tdouble NS[3] = { 0.5*s*(s - 1.0), 0.5*s*(s + 1.0), 1.0 - s*s };\n\tdouble NT[3] = { 0.5*t*(t - 1.0), 0.5*t*(t + 1.0), 1.0 - t*t };\n\n\tdouble DR[3] = { r - 0.5, r + 0.5, -2.0*r };\n\tdouble DS[3] = { s - 0.5, s + 0.5, -2.0*s };\n\tdouble DT[3] = { t - 0.5, t + 0.5, -2.0*t };\n\n\tdouble HR[3] = { 1.0, 1.0, -2.0 };\n\tdouble HS[3] = { 1.0, 1.0, -2.0 };\n\tdouble HT[3] = { 1.0, 1.0, -2.0 };\n\n\tfor (int a = 0; a<27; ++a)\n\t{\n\t\tint i = HEX27_LUT[a][0];\n\t\tint j = HEX27_LUT[a][1];\n\t\tint k = HEX27_LUT[a][2];\n\n\t\tHrr[a] = HR[i] * NS[j] * NT[k];\n\t\tHss[a] = NR[i] * HS[j] * NT[k];\n\t\tHtt[a] = NR[i] * NS[j] * HT[k];\n\n\t\tHrs[a] = DR[i] * DS[j] * NT[k];\n\t\tHst[a] = NR[i] * DS[j] * DT[k];\n\t\tHrt[a] = DR[i] * NS[j] * DT[k];\n\t}\n}\n\n//=============================================================================\n//              P Y R A 5\n//=============================================================================\n\n//! values of shape functions\nvoid FEPyra5::shape_fnc(double* H, double r, double s, double t)\n{\n\tH[0] = 0.125*(1.0 - r)*(1.0 - s)*(1.0 - t);\n\tH[1] = 0.125*(1.0 + r)*(1.0 - s)*(1.0 - t);\n\tH[2] = 0.125*(1.0 + r)*(1.0 + s)*(1.0 - t);\n\tH[3] = 0.125*(1.0 - r)*(1.0 + s)*(1.0 - t);\n\tH[4] = 0.5*(1.0 + t);\n}\n\n//! values of shape function derivatives\nvoid FEPyra5::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n\tHr[0] = -0.125*(1.0 - s)*(1.0 - t);\n\tHr[1] = 0.125*(1.0 - s)*(1.0 - t);\n\tHr[2] = 0.125*(1.0 + s)*(1.0 - t);\n\tHr[3] = -0.125*(1.0 + s)*(1.0 - t);\n\tHr[4] = 0.0;\n\n\tHs[0] = -0.125*(1.0 - r)*(1.0 - t);\n\tHs[1] = -0.125*(1.0 + r)*(1.0 - t);\n\tHs[2] = 0.125*(1.0 + r)*(1.0 - t);\n\tHs[3] = 0.125*(1.0 - r)*(1.0 - t);\n\tHs[4] = 0.0;\n\n\tHt[0] = -0.125*(1.0 - r)*(1.0 - s);\n\tHt[1] = -0.125*(1.0 + r)*(1.0 - s);\n\tHt[2] = -0.125*(1.0 + r)*(1.0 + s);\n\tHt[3] = -0.125*(1.0 - r)*(1.0 + s);\n\tHt[4] = 0.5;\n}\n\n//! values of shape function second derivatives\nvoid FEPyra5::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n\tHrr[0] = 0.0; Hss[0] = 0.0; Htt[0] = 0.0;\n\tHrr[1] = 0.0; Hss[1] = 0.0; Htt[1] = 0.0;\n\tHrr[2] = 0.0; Hss[2] = 0.0; Htt[2] = 0.0;\n\tHrr[3] = 0.0; Hss[3] = 0.0; Htt[3] = 0.0;\n\tHrr[4] = 0.0; Hss[4] = 0.0; Htt[4] = 0.0;\n\n\tHrs[0] = 0.125*(1.0 - t); Hrt[0] = 0.125*(1.0 - s); Hst[0] = 0.125*(1.0 - r);\n\tHrs[1] = -0.125*(1.0 - t); Hrt[1] = -0.125*(1.0 - s); Hst[1] = 0.125*(1.0 + r);\n\tHrs[2] = 0.125*(1.0 - t); Hrt[2] = -0.125*(1.0 + s); Hst[2] = -0.125*(1.0 + r);\n\tHrs[3] = -0.125*(1.0 - t); Hrt[3] = 0.125*(1.0 + s); Hst[3] = -0.125*(1.0 - r);\n\tHrs[4] = 0.0; Hrt[4] = 0.0; Hst[4] = 0.0;\n}\n\n//=============================================================================\n//              P Y R A 1 3\n//=============================================================================\n\n//! values of shape functions\nvoid FEPyra13::shape_fnc(double* H, double r, double s, double t)\n{\n    H[5] = 0.25*(1 - r*r)*(1 - s)*(1 - t);\n    H[6] = 0.25*(1 - s*s)*(1 + r)*(1 - t);\n    H[7] = 0.25*(1 - r*r)*(1 + s)*(1 - t);\n    H[8] = 0.25*(1 - s*s)*(1 - r)*(1 - t);\n    H[9] = 0.25*(1 - t*t)*(1 - r)*(1 - s);\n    H[10] = 0.25*(1 - t*t)*(1 + r)*(1 - s);\n    H[11] = 0.25*(1 - t*t)*(1 + r)*(1 + s);\n    H[12] = 0.25*(1 - t*t)*(1 - r)*(1 + s);\n    \n    H[0] = 0.125*(1 - r)*(1 - s)*(1 - t) - 0.5*(H[5] + H[8] + H[9]);\n    H[1] = 0.125*(1 + r)*(1 - s)*(1 - t) - 0.5*(H[5] + H[6] + H[10]);\n    H[2] = 0.125*(1 + r)*(1 + s)*(1 - t) - 0.5*(H[6] + H[7] + H[11]);\n    H[3] = 0.125*(1 - r)*(1 + s)*(1 - t) - 0.5*(H[7] + H[8] + H[12]);\n    H[4] = 0.5*t*(1 + t);\n}\n\n//! values of shape function derivatives\nvoid FEPyra13::shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t)\n{\n    Hr[ 0] = 0.125 + r*(0.25 + s*(-0.25 + 0.25*t) - 0.25*t) + s*s*(-0.125 + 0.125*t) +\n    s*(-0.125 + 0.125*t)*t - 0.125*t*t;\n    Hr[ 1] = -0.125 + r*(0.25 + s*(-0.25 + 0.25*t) - 0.25*t) + s*s*(0.125 - 0.125*t) +\n    s*(0.125 - 0.125*t)*t + 0.125*t*t;\n    Hr[ 2] = -0.125 + r*(0.25 + s*(0.25 - 0.25*t) - 0.25*t) + s*s*(0.125 - 0.125*t) +\n    s*(-0.125 + 0.125*t)*t + 0.125*t*t;\n    Hr[ 3] = 0.125 + r*(0.25 + s*(0.25 - 0.25*t) - 0.25*t) + s*s*(-0.125 + 0.125*t) +\n    s*(0.125 - 0.125*t)*t - 0.125*t*t;\n    Hr[ 4] = 0;\n    Hr[ 5] = -0.5*r*(-1. + s)*(-1. + t);\n    Hr[ 6] = 0.25*(-1. + s*s)*(-1. + t);\n    Hr[ 7] = 0.5*r*(1. + s)*(-1. + t);\n    Hr[ 8] = -0.25*(-1. + s*s)*(-1. + t);\n    Hr[ 9] = -0.25*(-1. + s)*(-1. + t*t);\n    Hr[10] = 0.25*(-1. + s)*(-1. + t*t);\n    Hr[11] = -0.25*(1. + s)*(-1. + t*t);\n    Hr[12] = 0.25*(1. + s)*(-1. + t*t);\n    \n    Hs[ 0] = 0.125 + s*(0.25 - 0.25*t) + r*r*(-0.125 + 0.125*t) - 0.125*t*t +\n    r*(s*(-0.25 + 0.25*t) + (-0.125 + 0.125*t)*t);\n    Hs[ 1] = 0.125 + s*(0.25 - 0.25*t) + r*r*(-0.125 + 0.125*t) - 0.125*t*t +\n    r*(s*(0.25 - 0.25*t) + (0.125 - 0.125*t)*t);\n    Hs[ 2] = -0.125 + s*(0.25 - 0.25*t) + r*r*(0.125 - 0.125*t) + 0.125*t*t +\n    r*(s*(0.25 - 0.25*t) + (-0.125 + 0.125*t)*t);\n    Hs[ 3] = -0.125 + s*(0.25 - 0.25*t) + r*r*(0.125 - 0.125*t) + 0.125*t*t +\n    r*(s*(-0.25 + 0.25*t) + (0.125 - 0.125*t)*t);\n    Hs[ 4] = 0;\n    Hs[ 5] = -0.25*(-1. + r*r)*(-1. + t);\n    Hs[ 6] = 0.5*(1. + r)*s*(-1. + t);\n    Hs[ 7] = 0.25*(-1. + r*r)*(-1. + t);\n    Hs[ 8] = -0.5*(-1. + r)*s*(-1. + t);\n    Hs[ 9] = -0.25*(-1. + r)*(-1. + t*t);\n    Hs[10] = 0.25*(1. + r)*(-1. + t*t);\n    Hs[11] = -0.25*(1. + r)*(-1. + t*t);\n    Hs[12] = 0.25*(-1. + r)*(-1. + t*t);\n\n    Ht[ 0] = -0.125*(-1. + r)*(-1. + s) + 0.125*(-1. + r*r)*(-1. + s) +\n    0.125*(-1. + r)*(-1. + s*s) + 0.25*(-1. + r)*(-1. + s)*t;\n    Ht[ 1] = 0.125*(1. + r)*(-1. + s) + 0.125*(-1. + r*r)*(-1. + s) -\n    0.125*(1. + r)*(-1. + s*s) - 0.25*(1. + r)*(-1. + s)*t;\n    Ht[ 2] = -0.125*(1. + r)*(1. + s) - 0.125*(-1. + r*r)*(1. + s) -\n    0.125*(1. + r)*(-1. + s*s) + 0.25*(1. + r)*(1. + s)*t;\n    Ht[ 3] = 0.125*(-1. + r)*(1. + s) - 0.125*(-1. + r*r)*(1. + s) +\n    0.125*(-1. + r)*(-1. + s*s) - 0.25*(-1. + r)*(1. + s)*t;\n    Ht[ 4] = 0.5 + 1.*t;\n    Ht[ 5] = -0.25*(-1. + r*r)*(-1. + s);\n    Ht[ 6] = 0.25*(1. + r)*(-1. + s*s);\n    Ht[ 7] = 0.25*(-1. + r*r)*(1. + s);\n    Ht[ 8] = -0.25*(-1. + r)*(-1. + s*s);\n    Ht[ 9] = -0.5*(-1. + r)*(-1. + s)*t;\n    Ht[10] = 0.5*(1. + r)*(-1. + s)*t;\n    Ht[11] = -0.5*(1. + r)*(1. + s)*t;\n    Ht[12] = 0.5*(-1. + r)*(1. + s)*t;\n}\n\n//! values of shape function second derivatives\nvoid FEPyra13::shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t)\n{\n    Hrr[ 0] = 0.25*(-1. + s)*(-1. + t);\n    Hrr[ 1] = 0.25*(-1. + s)*(-1. + t);\n    Hrr[ 2] = -0.25*(1. + s)*(-1. + t);\n    Hrr[ 3] = -0.25*(1. + s)*(-1. + t);\n    Hrr[ 4] = 0;\n    Hrr[ 5] = -0.5*(-1. + s)*(-1. + t);\n    Hrr[ 6] = 0.;\n    Hrr[ 7] = 0.5*(1. + s)*(-1. + t);\n    Hrr[ 8] = 0.;\n    Hrr[ 9] = 0.;\n    Hrr[10] = 0.;\n    Hrr[11] = 0.;\n    Hrr[12] = 0.;\n\n    Hss[ 0] = 0.25*(-1. + r)*(-1. + t);\n    Hss[ 1] = -0.25*(1. + r)*(-1. + t);\n    Hss[ 2] = -0.25*(1. + r)*(-1. + t);\n    Hss[ 3] = 0.25*(-1. + r)*(-1. + t);\n    Hss[ 4] = 0;\n    Hss[ 5] = 0;\n    Hss[ 6] = 0.5*(1. + r)*(-1. + t);\n    Hss[ 7] = 0;\n    Hss[ 8] = -0.5*(-1. + r)*(-1. + t);\n    Hss[ 9] = 0;\n    Hss[10] = 0;\n    Hss[11] = 0;\n    Hss[12] = 0;\n\n    Htt[ 0] = 0.25*(-1. + r)*(-1. + s);\n    Htt[ 1] = -0.25*(1. + r)*(-1. + s);\n    Htt[ 2] = 0.25*(1. + r)*(1. + s);\n    Htt[ 3] = -0.25*(-1. + r)*(1. + s);\n    Htt[ 4] = 1.;\n    Htt[ 5] = 0;\n    Htt[ 6] = 0;\n    Htt[ 7] = 0;\n    Htt[ 8] = 0;\n    Htt[ 9] = -0.5*(-1. + r)*(-1. + s);\n    Htt[10] = 0.5*(1. + r)*(-1. + s);\n    Htt[11] = -0.5*(1. + r)*(1. + s);\n    Htt[12] = 0.5*(-1. + r)*(1. + s);\n\n    Hrs[ 0] = r*(-0.25 + 0.25*t) + s*(-0.25 + 0.25*t) - 0.125*t + 0.125*t*t;\n    Hrs[ 1] = s*(0.25 - 0.25*t) + r*(-0.25 + 0.25*t) + 0.125*t - 0.125*t*t;\n    Hrs[ 2] = r*(0.25 - 0.25*t) + s*(0.25 - 0.25*t) - 0.125*t + 0.125*t*t;\n    Hrs[ 3] = r*(0.25 - 0.25*t) + s*(-0.25 + 0.25*t) + 0.125*t - 0.125*t*t;\n    Hrs[ 4] = 0;\n    Hrs[ 5] = -0.5*r*(-1. + t);\n    Hrs[ 6] = 0.5*s*(-1. + t);\n    Hrs[ 7] = 0.5*r*(-1. + t);\n    Hrs[ 8] = -0.5*s*(-1. + t);\n    Hrs[ 9] = -0.25*(-1. + t*t);\n    Hrs[10] = 0.25*(-1. + t*t);\n    Hrs[11] = -0.25*(-1. + t*t);\n    Hrs[12] = 0.25*(-1. + t*t);\n    \n    Hrs[ 0] = 0.125*r*r - 0.25*s + r*(-0.125 + 0.25*s + 0.25*t) - 0.25*t;\n    Hst[ 1] = 0.125*r*r - 0.25*s + r*(0.125 - 0.25*s - 0.25*t) - 0.25*t;\n    Hst[ 2] = -0.125*r*r - 0.25*s + r*(-0.125 - 0.25*s + 0.25*t) + 0.25*t;\n    Hst[ 3] = -0.125*r*r - 0.25*s + r*(0.125 + 0.25*s - 0.25*t) + 0.25*t;\n    Hst[ 4] = 0;\n    Hst[ 5] = -0.25*(-1. + r*r);\n    Hst[ 6] = 0.5*(1. + r)*s;\n    Hst[ 7] = 0.25*(-1. + r*r);\n    Hst[ 8] = -0.5*(-1. + r)*s;\n    Hst[ 9] = -0.5*(-1. + r)*t;\n    Hst[10] = 0.5*(1. + r)*t;\n    Hst[11] = -0.5*(1. + r)*t;\n    Hst[12] = 0.5*(-1. + r)*t;\n    \n    Hrt[ 0] = r*(-0.25 + 0.25*s) + 0.125*s*s + s*(-0.125 + 0.25*t) - 0.25*t;\n    Hrt[ 1] = r*(-0.25 + 0.25*s) - 0.125*s*s + s*(0.125 - 0.25*t) + 0.25*t;\n    Hrt[ 2] = r*(-0.25 - 0.25*s) - 0.125*s*s + s*(-0.125 + 0.25*t) + 0.25*t;\n    Hrt[ 3] = r*(-0.25 - 0.25*s) + 0.125*s*s + s*(0.125 - 0.25*t) - 0.25*t;\n    Hrt[ 4] = 0;\n    Hrt[ 5] = -0.5*r*(-1. + s);\n    Hrt[ 6] = 0.25*(-1. + s*s);\n    Hrt[ 7] = 0.5*r*(1. + s);\n    Hrt[ 8] = -0.25*(-1. + s*s);\n    Hrt[ 9] = -0.5*(-1. + s)*t;\n    Hrt[10] = 0.5*(-1. + s)*t;\n    Hrt[11] = -0.5*(1. + s)*t;\n    Hrt[12] = 0.5*(1. + s)*t;\n\n    Hrs[0] = 0.125*(1.0 - t); Hrt[0] = 0.125*(1.0 - s); Hst[0] = 0.125*(1.0 - r);\n    Hrs[1] = -0.125*(1.0 - t); Hrt[1] = -0.125*(1.0 - s); Hst[1] = 0.125*(1.0 + r);\n    Hrs[2] = 0.125*(1.0 - t); Hrt[2] = -0.125*(1.0 + s); Hst[2] = -0.125*(1.0 + r);\n    Hrs[3] = -0.125*(1.0 - t); Hrt[3] = 0.125*(1.0 + s); Hst[3] = -0.125*(1.0 - r);\n    Hrs[4] = 0.0; Hrt[4] = 0.0; Hst[4] = 0.0;\n}\n"
  },
  {
    "path": "FECore/FESolidElementShape.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElementShape.h\"\n\n//=============================================================================\n// Base class for defining element shape classes for (3D) solid elements\nclass FESolidElementShape : public FEElementShape\n{\npublic:\n\tFESolidElementShape(FE_Element_Shape shape, int nodes) : FEElementShape(shape, nodes) {}\n\n\t//! values of shape functions\n\tvirtual void shape_fnc(double* H, double r, double s, double t) = 0;\n\n\t//! values of shape function derivatives\n\tvirtual void shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t) = 0;\n\n\t//! values of shape function second derivatives\n\tvirtual void shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t) = 0;\n};\n\n//=============================================================================\nclass FETet4 : public FESolidElementShape\n{\npublic:\n\tFETet4() : FESolidElementShape(ET_TET4, 4) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n//=============================================================================\nclass FETet5 : public FESolidElementShape\n{\npublic:\n\tFETet5() : FESolidElementShape(ET_TET5, 5) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FEHex8 : public FESolidElementShape\n{\npublic:\n\tFEHex8() : FESolidElementShape(ET_HEX8, 8) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FEPenta6 : public FESolidElementShape\n{\npublic:\n\tFEPenta6() : FESolidElementShape(ET_PENTA6, 6) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n//=============================================================================\nclass FEPenta15 : public FESolidElementShape\n{\npublic:\n\tFEPenta15() : FESolidElementShape(ET_PENTA15, 15) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FETet10 : public FESolidElementShape\n{\npublic:\n\tFETet10() : FESolidElementShape(ET_TET10, 10) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FETet15 : public FESolidElementShape\n{\npublic:\n\tFETet15() : FESolidElementShape(ET_TET15, 15) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n//=============================================================================\nclass FETet20 : public FESolidElementShape\n{\npublic:\n\tFETet20() : FESolidElementShape(ET_TET20, 20) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FEHex20 : public FESolidElementShape\n{\npublic:\n\tFEHex20() : FESolidElementShape(ET_HEX20, 20) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\n//! Base class for 27-node quadratic hexahedral element\nclass FEHex27 : public FESolidElementShape\n{\npublic:\n\tFEHex27() : FESolidElementShape(ET_HEX27, 27) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FEPyra5 : public FESolidElementShape\n{\npublic:\n\tFEPyra5() : FESolidElementShape(ET_PYRA5, 5) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s, double t);\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n\n//=============================================================================\nclass FEPyra13 : public FESolidElementShape\n{\npublic:\n    FEPyra13() : FESolidElementShape(ET_PYRA13, 13) {}\n    \n    //! values of shape functions\n    void shape_fnc(double* H, double r, double s, double t);\n    \n    //! values of shape function derivatives\n    void shape_deriv(double* Hr, double* Hs, double* Ht, double r, double s, double t);\n    \n    //! values of shape function second derivatives\n    void shape_deriv2(double* Hrr, double* Hss, double* Htt, double* Hrs, double* Hst, double* Hrt, double r, double s, double t);\n};\n\n"
  },
  {
    "path": "FECore/FESolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESolver.h\"\n#include \"FEModel.h\"\n#include \"FENodeReorder.h\"\n#include \"DumpStream.h\"\n#include \"FEDomain.h\"\n#include \"FESurfacePairConstraint.h\"\n#include \"FENLConstraint.h\"\n#include \"FELinearConstraintManager.h\"\n#include \"FENodalLoad.h\"\n#include \"LinearSolver.h\"\n#include \"log.h\"\n\nBEGIN_FECORE_CLASS(FESolver, FECoreBase)\n\tBEGIN_PARAM_GROUP(\"linear system\");\n\t\tADD_PARAMETER(m_msymm    , \"symmetric_stiffness\", 0, \"non-symmetric\\0symmetric\\0symmetric structure\\0preferred\\0\")->setLongName(\"matrix format\");\n\t\tADD_PARAMETER(m_eq_scheme, \"equation_scheme\", 0, \"staggered\\0block\\0\");\n\t\tADD_PARAMETER(m_eq_order , \"equation_order\", 0, \"default\\0reverse\\0febio2\\0\");\n\t\tADD_PARAMETER(m_bwopt    , \"optimize_bw\");\n\tEND_PARAM_GROUP();\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFESolver::FESolver(FEModel* fem) : FECoreBase(fem)\n{ \n\tm_msymm = REAL_SYMMETRIC; // assume symmetric stiffness matrix\n\tm_niter = 0;\n\n\tm_nref = 0;\n\t\n\tm_baugment = false;\n\tm_naug = 0;\n\n\tm_neq = 0;\n\n\tm_bwopt = false;\n\n\tm_eq_scheme = EQUATION_SCHEME::STAGGERED;\n\tm_eq_order = EQUATION_ORDER::NORMAL_ORDER;\n}\n\n//-----------------------------------------------------------------------------\nFESolver::~FESolver()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolver::SetEquationScheme(int scheme)\n{\n\tm_eq_scheme = scheme;\n}\n\n//-----------------------------------------------------------------------------\n//! set the linear system partitions\nvoid FESolver::SetPartitions(const vector<int>& part)\n{\n\tm_part = part;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the size of a partition\nint FESolver::GetPartitionSize(int partition)\n{\n\tassert((partition >= 0) && (partition < (int)m_part.size()));\n\tif ((partition >= 0) && (partition < (int)m_part.size())) return m_part[partition];\n\telse return 0;\n}\n\n//-----------------------------------------------------------------------------\n//! get the current stiffness matrix\nFEGlobalMatrix* FESolver::GetStiffnessMatrix()\n{\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! get the current load vector\nstd::vector<double> FESolver::GetLoadVector()\n{\n\treturn std::vector<double>();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolver::Clean()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolver::Reset()\n{\n\tm_niter = 0;\n\tm_nref = 0;\n\tm_naug = 0;\n}\n\n//-----------------------------------------------------------------------------\n// get the linear solver\nLinearSolver* FESolver::GetLinearSolver()\n{\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! get matrix type\nMatrix_Type FESolver::MatrixType() const\n{\n\tMatrix_Type mtype;\n\tswitch (m_msymm)\n\t{\n\tcase REAL_UNSYMMETRIC   : mtype = REAL_UNSYMMETRIC; break;\n\tcase REAL_SYMMETRIC     : mtype = REAL_SYMMETRIC; break;\n\tcase REAL_SYMM_STRUCTURE: mtype = REAL_SYMM_STRUCTURE; break;\n\tdefault:\n\t\tmtype = PreferredMatrixType();\n\t\tconst char* szfmt = \"\";\n\t\tswitch (mtype)\n\t\t{\n\t\tcase REAL_UNSYMMETRIC: szfmt = \"unsymmetric\"; break;\n\t\tcase REAL_SYMMETRIC  : szfmt = \"symmetric\"; break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t\tfeLogInfo(\"Setting matrix format to: %s\", szfmt);\n\t}\n\treturn mtype;\n}\n\n// find the preferred matrix type: \n// symmetric unless any model component has its symmetric_stiffness parameter set to false\nMatrix_Type FESolver::PreferredMatrixType() const\n{\n\tFEModel& fem = *GetFEModel();\n\tfor (int i = 0; i < fem.ModelLoads(); ++i)\n\t{\n\t\tFEModelLoad* pl = fem.ModelLoad(i);\n\t\tif (pl->IsActive() && (pl->PreferredMatrixType() == REAL_UNSYMMETRIC))\n\t\t{\n\t\t\treturn REAL_UNSYMMETRIC; // no point in continuing\n\t\t}\n\t}\n\tfor (int i = 0; i < fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* pc = fem.NonlinearConstraint(i);\n\t\tif (pc->IsActive())\n\t\t{\n\t\t\tFEParam* p = pc->GetParameter(\"symmetric_stiffness\");\n\t\t\tif (p && (p->type() == FE_PARAM_BOOL) && !p->value<bool>())\n\t\t\t{\n\t\t\t\treturn REAL_UNSYMMETRIC; // no point in continuing\n\t\t\t}\n\t\t}\n\t}\n\tfor (int i = 0; i < fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* pc = fem.SurfacePairConstraint(i);\n\t\tif (pc->IsActive())\n\t\t{\n\t\t\tFEParam* p = pc->GetParameter(\"symmetric_stiffness\");\n\t\t\tif (p && (p->type() == FE_PARAM_BOOL) && !p->value<bool>())\n\t\t\t{\n\t\t\t\treturn REAL_UNSYMMETRIC; // no point in continuing\n\t\t\t}\n\t\t}\n\t}\n\treturn REAL_SYMMETRIC;\n}\n\n//-----------------------------------------------------------------------------\n// extract the (square) norm of a solution vector\ndouble FESolver::ExtractSolutionNorm(const vector<double>& v, const FEDofList& dofs) const\n{\n\tassert(v.size() == m_dofMap.size());\n\tdouble norm = 0;\n\tfor (int n = 0; n < dofs.Size(); ++n)\n\t{\n\t\tfor (int i = 0; i < v.size(); ++i)\n\t\t{\n\t\t\tif (m_dofMap[i] == dofs[n]) norm += v[i] * v[i];\n\t\t}\n\t}\n\treturn norm;\n}\n\n//-----------------------------------------------------------------------------\n// return the solution vector\nstd::vector<double> FESolver::GetSolutionVector() const\n{\n\treturn std::vector<double>();\n}\n\n//-----------------------------------------------------------------------------\n// see if the dofs in the dof list are active in this solver\nbool FESolver::HasActiveDofs(const FEDofList& dof)\n{\n\tassert(dof.IsEmpty() == false);\n\tif (dof.IsEmpty()) return true;\n\n\tassert(m_Var.size());\n\tfor (int i = 0; i < dof.Size(); ++i)\n\t{\n\t\tint dof_i = dof[i];\n\n\t\tfor (int i = 0; i < m_Var.size(); ++i)\n\t\t{\n\t\t\tFESolutionVariable& vi = m_Var[i];\n\t\t\tif (vi.m_dofs->Contains(dof_i))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// get the active dof map (returns nr of functions)\nint FESolver::GetActiveDofMap(vector<int>& activeDofMap)\n{\n\t// get the dof map\n\tint neq = (int)m_dofMap.size();\n\tif (m_dofMap.empty() || (m_dofMap.size() < neq)) return -1;\n\n\t// We need the partitions here, but for now we assume that\n\t// it is the first partition\n\n\t// The dof map indices point to the dofs as defined by the variables.\n\t// Since there could be more dofs than actually used in the linear system\n\t// we need to reindex this map. \n\t// First, find the min and max\n\tint imin = m_dofMap[0], imax = m_dofMap[0];\n\tfor (size_t i = 0; i < neq; ++i)\n\t{\n\t\tif (m_dofMap[i] > imax) imax = m_dofMap[i];\n\t\tif (m_dofMap[i] < imin) imin = m_dofMap[i];\n\t}\n\n\t// create the conversion table\n\tint nsize = imax - imin + 1;\n\tvector<int> LUT(nsize, -1);\n\tfor (size_t i = 0; i < neq; ++i)\n\t{\n\t\tLUT[m_dofMap[i] - imin] = 1;\n\t}\n\n\t// count how many dofs are actually used\n\tint nfunc = 0;\n\tfor (size_t i = 0; i < nsize; ++i)\n\t{\n\t\tif (LUT[i] != -1) LUT[i] = nfunc++;\n\t}\n\n\t// now, reindex the dof map\n\t// allocate dof map\n\tactiveDofMap.resize(neq);\n\tfor (size_t i = 0; i < neq; ++i)\n\t{\n\t\tactiveDofMap[i] = LUT[m_dofMap[i] - imin];\n\t}\n\n\treturn nfunc;\n}\n\n//-----------------------------------------------------------------------------\n//! build the matrix profile\nvoid FESolver::BuildMatrixProfile(FEGlobalMatrix& G, bool breset)\n{\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tDOFS& fedofs = fem.GetDOFS();\n\tint MAX_NDOFS = fedofs.GetTotalDOFS();\n\n\t// when reset is true we build the entire matrix profile\n\t// (otherwise we only build the \"dynamic\" profile)\n\tif (breset)\n\t{\n\t\t// Add all elements to the profile\n\t\t// Loop over all active domains\n\t\tfor (int nd = 0; nd<mesh.Domains(); ++nd)\n\t\t{\n\t\t\tFEDomain& d = mesh.Domain(nd);\n\t\t\td.BuildMatrixProfile(G);\n\t\t}\n\n\t\t// linear constraints\n\t\tFELinearConstraintManager& LCM = fem.GetLinearConstraintManager();\n\t\tLCM.BuildMatrixProfile(G);\n\t}\n\telse\n\t{\n\t\t// Do the \"dynamic\" profile. That is the part of the profile that always changes\n\t\t// This is mostly contact\n\t\t// do the nonlinear constraints\n\t\tint M = fem.NonlinearConstraints();\n\t\tfor (int m = 0; m<M; ++m)\n\t\t{\n\t\t\tFENLConstraint* pnlc = fem.NonlinearConstraint(m);\n\t\t\tif (pnlc->IsActive()) pnlc->BuildMatrixProfile(G);\n\t\t}\n\n\t\t// All following \"elements\" are nonstatic. That is, they can change\n\t\t// connectivity between calls to this function. All of these elements\n\t\t// are related to contact analysis (at this point).\n\t\tif (fem.SurfacePairConstraints() > 0)\n\t\t{\n\t\t\t// Add all contact interface elements\n\t\t\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t\t\t{\n\t\t\t\tFESurfacePairConstraint* pci = fem.SurfacePairConstraint(i);\n\t\t\t\tif (pci->IsActive()) pci->BuildMatrixProfile(G);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! This function is called right before SolveStep and should be used to initialize\n//! time dependent information and other settings.\nbool FESolver::InitStep(double time)\n{\n\tFEModel& fem = *GetFEModel();\n\n\t// evaluate load controllers values at current time\n\tfem.EvaluateLoadControllers(time);\n\n\t// evaluate data generators at current time\n\tfem.EvaluateDataGenerators(time);\n\n\t// evaluate load parameters\n\tfem.EvaluateLoadParameters();\n\n\t// re-validate materials\n\t// This is necessary since the material parameters can have changed (e.g. via load curves) and thus \n\t// a new validation needs to be done to see if the material parameters are still valid. \n\tif (fem.ValidateMaterials() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//!\tThis function initializes the equation system.\n//! It is assumed that all free dofs up until now have been given an ID >= 0\n//! and the fixed or rigid dofs an ID < 0.\n//! After this operation the nodal ID array will contain the equation\n//! number assigned to the corresponding degree of freedom. To distinguish\n//! between free or unconstrained dofs and constrained ones the following rules\n//! apply to the ID array:\n//!\n//!           /\n//!          |  >=  0 --> dof j of node i is a free dof\n//! ID[i][j] <  == -1 --> dof j of node i is a fixed (no equation assigned too)\n//!          |  <  -1 --> dof j of node i is constrained and has equation nr = -ID[i][j]-2\n//!           \\\n//!\nbool FESolver::InitEquations()\n{\n   // get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n    \n    // clear partitions\n\tm_part.clear();\n\n\t// reorder the node numbers\n\tint NN = mesh.Nodes();\n\tvector<int> P(NN);\n    \n    // see if we need to optimize the bandwidth\n\tif (m_bwopt)\n\t{\n\t\tFENodeReorder mod;\n\t\tmod.Apply(mesh, P);\n\t}\n\telse for (int i = 0; i < NN; ++i) P[i] = i;\n\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(P[i]);\n\t\tif (node.HasFlags(FENode::EXCLUDE))\n\t\t\tfor (int j = 0; j < (int)node.m_ID.size(); ++j) node.m_ID[j] = -1;\n\t}\n\tm_dofMap.clear();\n\n\t// assign equations based on allocation scheme\n\tint neq = 0;\n\tif (m_eq_scheme == EQUATION_SCHEME::STAGGERED)\n\t{\n\t\tDOFS& dofs = fem.GetDOFS();\n\t\tif (m_eq_order == EQUATION_ORDER::NORMAL_ORDER)\n\t\t{\n\t\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(P[i]);\n\t\t\t\tif (node.HasFlags(FENode::EXCLUDE) == false) {\n\t\t\t\t\tfor (int nv = 0; nv < dofs.Variables(); ++nv)\n\t\t\t\t\t{\n\t\t\t\t\t\tint n = dofs.GetVariableSize(nv);\n\t\t\t\t\t\tfor (int l = 0; l < n; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint nl = dofs.GetDOF(nv, l);\n\t\t\t\t\t\t\tif (node.is_active(nl))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint bcj = node.get_bc(nl);\n\t\t\t\t\t\t\t\tif      (bcj == DOF_OPEN      ) { node.m_ID[nl] = neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\t\telse if (bcj == DOF_FIXED     ) { node.m_ID[nl] = -1; }\n\t\t\t\t\t\t\t\telse if (bcj == DOF_PRESCRIBED) { node.m_ID[nl] = -neq - 2; neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\t\telse { assert(false); return false; }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse node.m_ID[nl] = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint NN = mesh.Nodes();\n\t\t\tfor (int i = NN-1; i >= 0; --i)\n\t\t\t{\n\t\t\t\tFENode& node = mesh.Node(P[i]);\n\t\t\t\tif (node.HasFlags(FENode::EXCLUDE) == false) {\n\t\t\t\t\tint dofs = (int)node.m_ID.size();\n\t\t\t\t\tfor (int j = dofs - 1; j >= 0; --j)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (node.is_active(j))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint bcj = node.get_bc(j);\n\t\t\t\t\t\t\tif      (bcj == DOF_OPEN      ) { node.m_ID[j] = neq++; m_dofMap.push_back(j); }\n\t\t\t\t\t\t\telse if (bcj == DOF_FIXED     ) { node.m_ID[j] = -1; }\n\t\t\t\t\t\t\telse if (bcj == DOF_PRESCRIBED) { node.m_ID[j] = -neq - 2; neq++; m_dofMap.push_back(j); }\n\t\t\t\t\t\t\telse { assert(false); return false; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse node.m_ID[j] = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// assign partition\n\t\tm_part.push_back(neq);\n\t}\n\telse\n\t{\n\t\t// Assign equations numbers in blocks\n\t\tassert(m_eq_scheme == EQUATION_SCHEME::BLOCK);\n\t\tDOFS& dofs = fem.GetDOFS();\n\n\t\tif (m_eq_order == EQUATION_ORDER::NORMAL_ORDER)\n\t\t{\n\t\t\tfor (int nv = 0; nv < dofs.Variables(); ++nv)\n\t\t\t{\n\t\t\t\tint neq0 = neq;\n\t\t\t\tfor (int i = 0; i < NN; ++i)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = mesh.Node(P[i]);\n\t\t\t\t\tif (node.HasFlags(FENode::EXCLUDE) == false) {\n\t\t\t\t\t\tint n = dofs.GetVariableSize(nv);\n\t\t\t\t\t\tfor (int l = 0; l < n; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint nl = dofs.GetDOF(nv, l);\n\n\t\t\t\t\t\t\tif (node.is_active(nl))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint bcl = node.get_bc(nl);\n\t\t\t\t\t\t\t\tif      (bcl == DOF_FIXED) { node.m_ID[nl] = -1; }\n\t\t\t\t\t\t\t\telse if (bcl == DOF_OPEN) { node.m_ID[nl] = neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\t\telse if (bcl == DOF_PRESCRIBED) { node.m_ID[nl] = -neq - 2; neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\t\telse { assert(false); return false; }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse node.m_ID[nl] = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// assign partitions\n\t\t\t\tif (neq - neq0 > 0)\n\t\t\t\t\tm_part.push_back(neq - neq0);\n\t\t\t}\n\t\t}\n\t\telse if (m_eq_order == EQUATION_ORDER::REVERSE_ORDER)\n\t\t{\n\t\t\tint vars = dofs.Variables();\n\t\t\tfor (int nv = vars-1; nv >= 0; --nv)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i <NN; ++i)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = mesh.Node(P[i]);\n\t\t\t\t\tif (node.HasFlags(FENode::EXCLUDE) == false) {\n\t\t\t\t\t\tint n = dofs.GetVariableSize(nv);\n\t\t\t\t\t\tfor (int l = 0; l < n; ++l)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint nl = dofs.GetDOF(nv, l);\n\t\t\t\t\t\t\tif (node.is_active(nl))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint bcl = node.get_bc(nl);\n\t\t\t\t\t\t\t\tif      (bcl == DOF_FIXED     ) { node.m_ID[nl] = -1; }\n\t\t\t\t\t\t\t\telse if (bcl == DOF_OPEN      ) { node.m_ID[nl] = neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\t\telse if (bcl == DOF_PRESCRIBED) { node.m_ID[nl] = -neq - 2; neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\t\telse { assert(false); return false; }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse node.m_ID[nl] = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// assign partitions\n\t\t\t\tif (nv == vars-1) m_part.push_back(neq);\n\t\t\t\telse m_part.push_back(neq - m_part[(vars-1) - nv - 1]);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tassert(m_eq_order == FEBIO2_ORDER);\n\n\t\t\t// Assign equations numbers in blocks\n\t\t\tfor (int nv = 0; nv < dofs.Variables(); ++nv)\n\t\t\t{\n\t\t\t\tint n = dofs.GetVariableSize(nv);\n\t\t\t\tint neq0 = neq;\n\t\t\t\tfor (int l = 0; l < n; ++l)\n\t\t\t\t{\n\t\t\t\t\tint nl = dofs.GetDOF(nv, l);\n\n\t\t\t\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tFENode& node = mesh.Node(i);\n\t\t\t\t\t\tif (node.is_active(nl))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint bcl = node.get_bc(nl);\n\t\t\t\t\t\t\tif      (bcl == DOF_FIXED     ) { node.m_ID[nl] = -1; }\n\t\t\t\t\t\t\telse if (bcl == DOF_OPEN      ) { node.m_ID[nl] = neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\telse if (bcl == DOF_PRESCRIBED) { node.m_ID[nl] = -neq - 2; neq++; m_dofMap.push_back(nl); }\n\t\t\t\t\t\t\telse { assert(false); return false; }\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse node.m_ID[nl] = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// assign partitions\n\t\t\t\tif (neq - neq0 > 0)\n\t\t\t\t\tm_part.push_back(neq - neq0);\n\t\t\t}\n\t\t}\n\t}\n    \n    // store the number of equations\n    m_neq = neq;\n\n\tassert(m_dofMap.size() == m_neq);\n    \n    // All initialization is done\n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool FESolver::InitEquations2()\n{\n\t// get the mesh\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\n\t// clear partitions\n\tm_part.clear();\n\n\t// reorder the node numbers\n\tint NN = mesh.Nodes();\n\tvector<int> P(NN);\n\n\t// see if we need to optimize the bandwidth\n\tif (m_bwopt)\n\t{\n\t\tFENodeReorder mod;\n\t\tmod.Apply(mesh, P);\n\t}\n\telse for (int i = 0; i < NN; ++i) P[i] = i;\n\n\t// reset all equation numbers\n\t// first, on all nodes\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(P[i]);\n\t\tif (node.HasFlags(FENode::EXCLUDE))\n\t\t\tfor (int j = 0; j < (int)node.m_ID.size(); ++j) node.m_ID[j] = -1;\n\t}\n\t// then, on all elements\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t{\n\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\tel.m_lm = -1;\n\t\t}\n\t}\n\tm_dofMap.clear();\n\n\t// see if we need to deactivate some nodal dofs based on requested interpolation order\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\n\t\t// get the interpolation orders for the different variables.\n\t\tfor (int n = 0; n < m_Var.size(); ++n)\n\t\t{\n\t\t\tFESolutionVariable& var = m_Var[n];\n\t\t\tFEDofList& dofs = *var.m_dofs;\n\t\t\tint P = var.m_order;\n\n\t\t\t// set unused dofs to -1\n\t\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t\t{\n\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\tint ne = el.Nodes();\n\t\t\t\tint ne_p = el.ShapeFunctions(P);\n\n\t\t\t\tfor (int n = ne_p; n < ne; ++n)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = mesh.Node(el.m_node[n]);\n\t\t\t\t\tfor (int k=0; k<dofs.Size(); ++k)\n\t\t\t\t\t\tnode.set_bc(dofs[k], DOF_FIXED);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// assign equations based on allocation scheme\n\tint neq = 0;\n\tif ((m_eq_scheme == EQUATION_SCHEME::STAGGERED) && (m_eq_order == EQUATION_ORDER::NORMAL_ORDER))\n\t{\n\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t{\n\t\t\tFENode& node = mesh.Node(P[i]);\n\t\t\tif (node.HasFlags(FENode::EXCLUDE) == false) \n\t\t\t{\n\t\t\t\tint nvar = (int)m_Var.size();\n\t\t\t\tfor (int j = 0; j < nvar; ++j)\n\t\t\t\t{\n\t\t\t\t\tFESolutionVariable& var = m_Var[j];\n\t\t\t\t\tFEDofList& dofs = *var.m_dofs;\n\t\t\t\t\tfor (int k = 0; k < dofs.Size(); ++k)\n\t\t\t\t\t{\n\t\t\t\t\t\tint nk = dofs[k];\n\t\t\t\t\t\tif (node.is_active(nk))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint bck = node.get_bc(nk);\n\t\t\t\t\t\t\tif      (bck == DOF_OPEN      ) { node.m_ID[nk] = neq++; m_dofMap.push_back(nk); }\n\t\t\t\t\t\t\telse if (bck == DOF_FIXED     ) { node.m_ID[nk] = -1; }\n\t\t\t\t\t\t\telse if (bck == DOF_PRESCRIBED) { node.m_ID[nk] = -neq - 2; neq++; m_dofMap.push_back(nk); }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// assign element dofs\n\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t{\n\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t\t{\n\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\tfor (int n = 0; n < m_Var.size(); ++n)\n\t\t\t\t{\n\t\t\t\t\tFESolutionVariable& var = m_Var[n];\n\t\t\t\t\tif (var.m_order == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEDofList& dofs = *var.m_dofs;\n\t\t\t\t\t\tassert(dofs.Size() == 1);\n\t\t\t\t\t\tassert(el.m_lm == -1);\n\t\t\t\t\t\tel.m_lm = neq++;\n\t\t\t\t\t\tm_dofMap.push_back(dofs[0]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// only one partition for this allocation scheme\n\t\tm_part.push_back(neq);\n\t}\n\telse if ((m_eq_scheme == EQUATION_SCHEME::BLOCK) && (m_eq_order == EQUATION_ORDER::NORMAL_ORDER))\n\t{\n\t\tint neq0 = 0;\n\t\tint nvar = (int)m_Var.size();\n\t\tfor (int j = 0; j < nvar; ++j)\n\t\t{\n\t\t\tneq0 = neq;\n\t\t\tFESolutionVariable& var = m_Var[j];\n\t\t\tFEDofList& dofs = *var.m_dofs;\n\t\t\tif (var.m_order != 0)\n\t\t\t{\n\t\t\t\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFENode& node = mesh.Node(P[i]);\n\t\t\t\t\tif (node.HasFlags(FENode::EXCLUDE) == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (int k = 0; k < dofs.Size(); ++k)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint nk = dofs[k];\n\t\t\t\t\t\t\tif (node.is_active(nk))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint bck = node.get_bc(nk);\n\t\t\t\t\t\t\t\tif      (bck == DOF_OPEN      ) { node.m_ID[nk] = neq++; m_dofMap.push_back(nk); }\n\t\t\t\t\t\t\t\telse if (bck == DOF_FIXED     ) { node.m_ID[nk] = -1; }\n\t\t\t\t\t\t\t\telse if (bck == DOF_PRESCRIBED) { node.m_ID[nk] = -neq - 2; neq++; m_dofMap.push_back(nk); }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tassert(dofs.Size() == 1);\n\t\t\t\t// assign element dofs\n\t\t\t\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t\t\t\t{\n\t\t\t\t\tFEDomain& dom = mesh.Domain(i);\n\t\t\t\t\tfor (int j = 0; j < dom.Elements(); ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tFEElement& el = dom.ElementRef(j);\n\t\t\t\t\t\tFEDofList& dofs = *var.m_dofs;\n\t\t\t\t\t\tassert(dofs.Size() == 1);\n\t\t\t\t\t\tassert(el.m_lm == -1);\n\t\t\t\t\t\tel.m_lm = neq++;\n\t\t\t\t\t\tm_dofMap.push_back(dofs[0]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// add a partition\n\t\t\tint partitionSize = neq - neq0;\n\t\t\tif (partitionSize > 0) m_part.push_back(partitionSize);\n\t\t}\n\t}\n\telse assert(false);\n\n\t// store the number of equations\n\tm_neq = neq;\n\n\tassert(m_dofMap.size() == m_neq);\n\n\t// All initialization is done\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! add equations\nvoid FESolver::AddEquations(int neq, int partition)\n{\n\tm_neq += neq;\n\tm_part[partition] += neq;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESolver::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\tar & m_nrhs & m_niter & m_nref & m_ntotref & m_naug;\n}\n\n//-----------------------------------------------------------------------------\n//! Update the state of the model\nvoid FESolver::Update(std::vector<double>& u)\n{ \n\tassert(false); \n};\n\n//-----------------------------------------------------------------------------\n// The augmentation is done after a time step converges and gives model components\n// an opportunity to modify the model's state. This will usually require that the time \n// step is solved again.\nbool FESolver::Augment()\n{ \n\tFEModel& fem = *GetFEModel();\n\n\tconst FETimeInfo& tp = fem.GetTime();\n\n\t// Assume we will pass (can't hurt to be optimistic)\n\tbool bconv = true;\n\n\t// Do contact augmentations\n\tfor (int i = 0; i<fem.SurfacePairConstraints(); ++i)\n\t{\n\t\tFESurfacePairConstraint* pci = fem.SurfacePairConstraint(i);\n\t\tif (pci->IsActive()) bconv = (pci->Augment(m_naug, tp) && bconv);\n\t}\n\n\t// do nonlinear constraint augmentations\n\tfor (int i = 0; i<fem.NonlinearConstraints(); ++i)\n\t{\n\t\tFENLConstraint* plc = fem.NonlinearConstraint(i);\n\t\tif (plc->IsActive()) bconv = plc->Augment(m_naug, tp) && bconv;\n\t}\n\n\t// do domain augmentations\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i<mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tbconv = dom.Augment(m_naug) && bconv;\n\t}\n\n\tfem.GetTime().augmentation++;\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n// return the node (mesh index) from an equation number\nFENodalDofInfo FESolver::GetDOFInfoFromEquation(int ieq)\n{\n\tFENodalDofInfo info;\n\tinfo.m_eq = ieq;\n\tinfo.m_node = -1;\n\tinfo.m_dof = -1;\n\tinfo.szdof = \"\";\n\n\tFEModel& fem = *GetFEModel();\n\tFEMesh& mesh = fem.GetMesh();\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tvector<int>& id = node.m_ID;\n\t\tfor (int j = 0; j < id.size(); ++j)\n\t\t{\n\t\t\tif (id[j] == ieq)\n\t\t\t{\n\t\t\t\tinfo.m_node = node.GetID();\n\t\t\t\tinfo.m_dof = j;\n\t\t\t\tDOFS& Dofs = GetFEModel()->GetDOFS();\n\t\t\t\tinfo.szdof = Dofs.GetDOFName(info.m_dof);\n\t\t\t\tif (info.szdof == nullptr) info.szdof = \"???\";\n\t\t\t\treturn info;\n\t\t\t}\n\t\t}\n\t}\n\treturn info;\n}\n"
  },
  {
    "path": "FECore/FESolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n#include \"Timer.h\"\n#include \"matrix.h\"\n#include \"vector.h\"\n#include \"FEDofList.h\"\n#include \"FETimeInfo.h\"\n\n//-----------------------------------------------------------------------------\n// Scheme for assigning equation numbers\n// STAGGERED: | a0, b0, a1, b1, ..., an, bn |\n// BLOCK    : | a0, a1, ..., an, b0, b1, ..., bn |\nenum EQUATION_SCHEME\n{\n\tSTAGGERED,\n\tBLOCK\n};\n\n//-----------------------------------------------------------------------------\nenum EQUATION_ORDER\n{\n\tNORMAL_ORDER,\n\tREVERSE_ORDER,\n\tFEBIO2_ORDER\n};\n\n//-----------------------------------------------------------------------------\n// Solution variable\nclass FESolutionVariable\n{\npublic:\n\tFESolutionVariable(const char* szname, FEDofList* dofs = nullptr, int order = 2)\n\t{\n\t\tm_szname = szname;\n\t\tm_dofs = dofs;\n\t\tm_order = order;\n\t}\n\npublic:\n\tFEDofList*\tm_dofs;\t\t// the dof list\n\tint\t\t\tm_order;\t// the order of interpolation (0 = constant, 1 = linear, 2 = default)\n\tconst char* m_szname;\t// name of solution variable\n};\n\n//-----------------------------------------------------------------------------\n// structure identifying nodal dof info\nstruct FECORE_API FENodalDofInfo\n{\n\tint\t\tm_eq = -1;\t\t// equation number\n\tint\t\tm_node = -1;\t\t// 0-based index into mesh!\n\tint\t\tm_dof = -1;\t\t// index into nodal m_ID array\n\tconst char* szdof = nullptr;\n};\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalMatrix;\nclass LinearSolver;\nclass FEGlobalVector;\n\n//-----------------------------------------------------------------------------\n//! This is the base class for all FE solvers.\n\n//! A class derived from FESolver implements a solver for a specific type\n//! of physics problem. It takes the FEModel in its constructor and implements\n//! the SolveStep function to solve the FE problem.\nclass FECORE_API FESolver : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FESOLVER_ID)\n\tFECORE_BASE_CLASS(FESolver)\n\npublic:\n\t//! constructor\n\tFESolver(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FESolver();\n\npublic:\n\t//! Data serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! This is called by FEAnalaysis::Deactivate\n\tvirtual void Clean();\n\n\t//! rewind the solver (This is called when the time step fails and needs to retry)\n\tvirtual void Rewind() {}\n\n\t//! called during model reset\n\tvirtual void Reset();\n\n\t// Initialize linear equation system\n\tvirtual bool InitEquations();\n\n\t// New equation initialization procedure\n\t// TODO: work in progress\n\tvirtual bool InitEquations2();\n\n\t//! add equations\n\tvoid AddEquations(int neq, int partition = 0);\n\n\t//! initialize the step (This is called before SolveStep)\n\tvirtual bool InitStep(double time);\n\n\t//! Solve an analysis step\n\tvirtual bool SolveStep() = 0;\n\n\t//! Update the state of the model\n\tvirtual void Update(std::vector<double>& u);\n\n\t//! Do the augmentations\n\tvirtual bool Augment();\n\n\t//! Calculates concentrated nodal loads\n//\tvirtual void NodalLoads(FEGlobalVector& R, const FETimeInfo& tp);\n\npublic:\n\t//! Set the equation allocation scheme\n\tvoid SetEquationScheme(int scheme);\n\n\t//! set the linear system partitions\n\tvoid SetPartitions(const vector<int>& part);\n\n\t//! Get the size of a partition\n\tint GetPartitionSize(int partition);\n\n\t//! get the current stiffness matrix\n\tvirtual FEGlobalMatrix* GetStiffnessMatrix();\n\n\t//! get the current load vector\n\tvirtual std::vector<double> GetLoadVector();\n\n\t// get the linear solver\n\tvirtual LinearSolver* GetLinearSolver();\n\n\t//! get matrix type\n\tMatrix_Type MatrixType() const;\n\n\t//! build the matrix profile\n\tvirtual void BuildMatrixProfile(FEGlobalMatrix& G, bool breset);\n\n\t// see if the dofs in the dof list are active in this solver\n\tbool HasActiveDofs(const FEDofList& dof);\n\n\t// get the active dof map (returns nr of functions)\n\tint GetActiveDofMap(vector<int>& dofMap);\n\n\t// return the node (mesh index) from an equation number\n\tFENodalDofInfo GetDOFInfoFromEquation(int ieq);\n\npublic:\n\t// extract the (square) norm of a solution vector\n\tdouble ExtractSolutionNorm(const vector<double>& v, const FEDofList& dofs) const;\n\n\t// return the solution vector\n\tvirtual std::vector<double> GetSolutionVector() const;\n\nprotected:\n\tvirtual Matrix_Type PreferredMatrixType() const;\n\npublic: //TODO Move these parameters elsewhere\n\tbool\t\t\t\tm_bwopt;\t    //!< bandwidth optimization flag\n\tint\t\t\t\t\tm_msymm;\t\t//!< matrix symmetry flag for linear solver allocation\n\tint\t\t\t\t\tm_eq_scheme;\t//!< equation number scheme (used in InitEquations)\n\tint\t\t\t\t\tm_eq_order;\t\t//!< normal or reverse ordering\n\tint\t\t\t\t\tm_neq;\t\t\t//!< number of equations\n\tstd::vector<int>\tm_part;\t\t\t//!< partitions of linear system\n\tstd::vector<int>\tm_dofMap;\t\t//!< array stores for each equation the corresponding dof index\n\n\t// counters\n\tint\t\tm_nrhs;\t\t\t//!< nr of right hand side evalutations\n\tint\t\tm_niter;\t\t//!< nr of quasi-newton iterations\n\tint\t\tm_nref;\t\t\t//!< nr of stiffness retormations\n\tint\t\tm_ntotref;\t\t//!< nr of total stiffness reformations\n\n\t// augmentation\n\tint\t\tm_naug;\t\t\t//!< nr of augmentations\n\tbool\tm_baugment;\t\t//!< do augmentations flag\n\nprotected:\n\t// list of solution variables\n\tvector<FESolutionVariable>\tm_Var;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FEStepComponent.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEStepComponent.h\"\n\nFEStepComponent::FEStepComponent(FEModel* fem) : FEModelComponent(fem)\n{\n\t// initialize parameters\n\tm_bactive = true;\n}\n\n//-----------------------------------------------------------------------------\nbool FEStepComponent::IsActive() const\n{\n\treturn m_bactive;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStepComponent::Activate()\n{\n\tm_bactive = true;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStepComponent::Deactivate()\n{\n\tm_bactive = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FEStepComponent::Serialize(DumpStream& ar)\n{\n\tFEModelComponent::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar& m_bactive;\n}\n"
  },
  {
    "path": "FECore/FEStepComponent.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEModelComponent.h\"\n\n//-----------------------------------------------------------------------------\n// A Step component is a model component that can be assigned to a step. \n// It adds a mechanism for activating and deactivating the component.\nclass FECORE_API FEStepComponent : public FEModelComponent\n{\npublic:\n\tFEStepComponent(FEModel* fem);\n\n\t//-----------------------------------------------------------------------------------\n\t//! This function checks if the component is active in the current step. \n\tbool IsActive() const;\n\n\t//-----------------------------------------------------------------------------------\n\t//! Activate the component.\n\t//! This function is called during the step initialization, right before the step is solved.\n\t//! This function can be used to initialize any data that could depend on the model state. \n\t//! Data allocation and initialization of data that does not depend on the model state should\n\t//! be done in Init().\n\tvirtual void Activate();\n\n\t//-----------------------------------------------------------------------------------\n\t//! Deactivate the component\n\tvirtual void Deactivate();\n\npublic:\n\t//! serialization\n\tvoid Serialize(DumpStream& ar);\n\nprivate:\n\tbool\t\tm_bactive;\t//!< flag indicating whether the component is active\n};\n"
  },
  {
    "path": "FECore/FESurface.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurface.h\"\n#include \"FEMesh.h\"\n#include \"FESolidDomain.h\"\n#include \"FEElemElemList.h\"\n#include \"DumpStream.h\"\n#include \"matrix.h\"\n#include <FECore/log.h>\n#include \"FEModelParam.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFESurface::FESurface(FEModel* fem) : FEMeshPartition(FE_DOMAIN_SURFACE, fem)\n{\n\tm_surf = 0;\n\tm_bitfc = false;\n\tm_alpha = 1;\n\tm_bshellb = false;\n}\n\n//-----------------------------------------------------------------------------\nFESurface::~FESurface()\n{\n\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::Create(int nsize, int elemType)\n{\n\tm_el.resize(nsize);\n\tfor (int i = 0; i < nsize; ++i)\n\t{\n\t\tFESurfaceElement& el = m_el[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t\tel.m_elem[0].Reset();\n\t\tel.m_elem[1].Reset();\n\t}\n\n\tif (elemType != -1)\n\t{\n\t\tfor (int i = 0; i < nsize; ++i) m_el[i].SetType(elemType);\n\t\tCreateMaterialPointData();\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::Create(const FEFacetSet& set)\n{\n\tif (m_surf == 0) m_surf = const_cast<FEFacetSet*>(&set);\n\tassert(m_surf == &set);\n\n\tFEMesh& m = *GetMesh();\n\n\t// count nr of faces\n\tint faces = set.Faces();\n\n\t// allocate storage for faces\n\tCreate(faces);\n\n\t// read faces\n\tfor (int i = 0; i<faces; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tconst FEFacetSet::FACET& fi = set.Face(i);\n\n\t\tif (fi.ntype == 4) el.SetType(FE_QUAD4G4);\n\t\telse if (fi.ntype == 3) el.SetType(FE_TRI3G1);\n\t\telse if (fi.ntype == 6) el.SetType(FE_TRI6G3);\n\t\telse if (fi.ntype == 7) el.SetType(FE_TRI7G4);\n\t\telse if (fi.ntype == 8) el.SetType(FE_QUAD8G9);\n\t\telse if (fi.ntype == 9) el.SetType(FE_QUAD9G9);\n\t\telse if (fi.ntype == 10) el.SetType(FE_TRI10G7);\n\t\telse assert(false);\n\n\t\tint N = el.Nodes(); assert(N == fi.ntype);\n\t\tfor (int j = 0; j<N; ++j) el.m_node[j] = fi.node[j];\n\t}\n\n\t// copy the name\n\tSetName(set.GetName());\n\n\t// allocate surface material points\n\tCreateMaterialPointData();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::CreateMaterialPointData()\n{\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = m_el[i];\n\t\tint nint = el.GaussPoints();\n\t\tel.ClearData();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint* pt = dynamic_cast<FESurfaceMaterialPoint*>(CreateMaterialPoint());\n\t\t\tassert(pt);\n\t\t\tel.SetMaterialPointData(pt, n);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// extract the nodes from this surface\nFENodeList FESurface::GetNodeList()\n{\n\tFEMesh* pm = GetMesh();\n\tFENodeList nset(pm);\n\n\tvector<int> tag(pm->Nodes(), 0);\n\tfor (int i=0; i<Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j=0; j<ne; ++j)\n\t\t{\n\t\t\tif (tag[el.m_node[j]] == 0)\n\t\t\t{\n\t\t\t\tnset.Add(el.m_node[j]);\n\t\t\t\ttag[el.m_node[j]] = 1;\n\t\t\t}\n\t\t}\n\t}\n\treturn nset;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the list of (local) node indices of the boundary nodes\nvoid FESurface::GetBoundaryFlags(std::vector<bool>& boundary) const\n{\n\tFEElemElemList EEL;\n\tEEL.Create(this);\n\n\tboundary.assign(Nodes(), false);\n\tfor (int i = 0; i < Elements(); ++i) {\n\t\tconst FESurfaceElement& el = Element(i);\n\t\tfor (int j = 0; j < el.facet_edges(); ++j) {\n\t\t\tFEElement* nel = EEL.Neighbor(i, j);\n\t\t\tif (nel == nullptr) {\n\t\t\t\tint en[3] = { -1,-1,-1 };\n\t\t\t\tel.facet_edge(j, en);\n\t\t\t\tboundary[en[0]] = true;\n\t\t\t\tboundary[en[1]] = true;\n\t\t\t\tif (en[2] > -1) boundary[en[2]] = true;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Create material point data for this surface\nFEMaterialPoint* FESurface::CreateMaterialPoint()\n{\n\treturn new FESurfaceMaterialPoint;\n}\n\n//-----------------------------------------------------------------------------\n// update surface data\nvoid FESurface::Update(const FETimeInfo& tp)\n{\n\tForEachSurfaceElement([=](FESurfaceElement& el) {\n\t\tint nint = el.GaussPoints();\n\t\tint neln = el.Nodes();\n\n\t\tvec3d rt[FEElement::MAX_NODES];\n\t\tNodalCoordinates(el, rt);\n\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint& mp = static_cast<FESurfaceMaterialPoint&>(*el.GetMaterialPoint(n));\n\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n            double* H = el.H(n);\n\n            mp.m_rt = vec3d(0,0,0);\n\t\t\tmp.dxr = vec3d(0, 0, 0);\n\t\t\tmp.dxs = vec3d(0, 0, 0);\n\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t{\n                mp.m_rt += rt[i] * H[i];\n\t\t\t\tmp.dxr += rt[i] * Gr[i];\n\t\t\t\tmp.dxs += rt[i] * Gs[i];\n\t\t\t}\n\t\t}\n\t});\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::InitSurface()\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *GetMesh();\n\n\t// This array is used to keep tags on each node\n\tvector<int> tag; tag.assign(mesh.Nodes(), -1);\n\n\t// let's find all nodes the surface needs\n\tint nn = 0;\n\tint ne = Elements();\n\tfor (int i = 0; i<ne; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tel.m_lid = i;\n\n\t\tfor (int j = 0; j<el.Nodes(); ++j)\n\t\t{\n\t\t\t// get the global node number\n\t\t\tint m = el.m_node[j];\n\n\t\t\t// create a local node number\n\t\t\tif (tag[m] == -1) tag[m] = nn++;\n\n\t\t\t// set the local node number\n\t\t\tel.m_lnode[j] = tag[m];\n\t\t}\n\t}\n\n\t// allocate node index table\n\tm_Node.resize(nn);\n\n\t// fill the node index table\n\tfor (int i = 0; i<mesh.Nodes(); ++i)\n\t{\n\t\tif (tag[i] >= 0)\n\t\t{\n\t\t\tm_Node[tag[i]] = i;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Initialize surface node data structure\n//! Note that it is assumed that the element array is already created\n//! and initialized.\n\nbool FESurface::Init()\n{\n\t// make sure that there is a surface defined\n\tif (Elements() == 0) return false;\n\n\t// initialize the surface data\n\tInitSurface();\n\n\t// NOTE: Make sure the mesh has its node-element list initialized\n\t// otherwise the omp loop below might crash due to race condition.\n\tGetMesh()->NodeElementList();\n\n\t// see if we can find all elements that the faces belong to\n\tint invalidFacets = 0;\n\tint ne = Elements();\n#pragma omp parallel for reduction(+:invalidFacets)\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n        if (m_bitfc && (el.m_elem[0].pe == nullptr)) FindElements(el);\n\t\telse if (el.m_elem[0].pe == nullptr) el.m_elem[0] = FindElement(el);\n        //to make sure\n        else if (m_bitfc && (el.m_elem[1].pe == nullptr)) FindElements(el);\n\t\tif (el.m_elem[0].pe == nullptr) { invalidFacets++; }\n\t}\n\n\tif (invalidFacets > 0)\n\t{\n\t\tstd::string surfName = GetName();\n\t\tif (surfName.empty()) surfName = \"(unknown)\";\n\t\tfeLogWarning(\"The surface \\\"%s\\\" has %d invalid facets. \\nThe model may not run correctly.\", surfName.c_str(), invalidFacets);\n\t}\n\n\tvec3d re[FEElement::MAX_NODES];\n\t// initialize material points of surface elements\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = m_el[i];\n\n\t\tNodalCoordinates(el, re);\n\n\t\tint nint = el.GaussPoints();\n\t\tint neln = el.Nodes();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint* pt = dynamic_cast<FESurfaceMaterialPoint*>(el.GetMaterialPoint(n));\n\t\t\tif (pt == nullptr) return false;\n\n\t\t\t// initialize some material point data\n\t\t\tdouble* H = el.H(n);\n\t\t\tvec3d rn(0, 0, 0);\n\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t{\n\t\t\t\trn += re[j]*H[j];\n\t\t\t}\n\n\t\t\tpt->m_r0 = rn;\n            pt->m_rt = pt->m_rp = rn;\n\n\t\t\t// calculate initial surface tangents\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\n\t\t\tvec3d dxr(0, 0, 0), dxs(0, 0, 0);\n\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t{\n\t\t\t\tdxr += re[i] * Gr[i];\n\t\t\t\tdxs += re[i] * Gs[i];\n\t\t\t}\n\t\t\tpt->dxr = dxr;\n\t\t\tpt->dxs = dxs;\n\n\t\t\t// initialize the other material point data\n\t\t\tpt->Init();\n\t\t}\n\t}\n\n    // allocate node normals and evaluate them in initial configuration\n    m_nn.assign(Nodes(), vec3d(0,0,0));\n    UpdateNodeNormals();\n    \n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Find the element that a face belongs to\n// TODO: I should be able to speed this up\nFESurfaceElement::ELEMENT_REF FESurface::FindElement(FESurfaceElement& el)\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *GetMesh();\n\tFENodeElemList& NEL = mesh.NodeElementList();\n\n\tFESurfaceElement::ELEMENT_REF ref;\n\n\tint node = el.m_node[0];\n\tint nval = NEL.Valence(node);\n\tFEElement** ppe = NEL.ElementList(node);\n\tfor (int i=0; i<nval; ++i)\n\t{\n\t\tFEElement* pe = ppe[i];\n\t\tint nfaces = pe->Faces();\n\t\t\n\t\tint nf[FEElement::MAX_NODES], nn;\n\t\tfor (int j=0; j<nfaces; ++j)\n\t\t{\n\t\t\tnn = pe->GetFace(j, nf);\n\t\t\tif (nn == el.Nodes())\n\t\t\t{\n\t\t\t\tint orient = 0;\n\t\t\t\tswitch (nn)\n\t\t\t\t{\n\t\t\t\tcase 3: orient = el.HasNodes(nf, 3); break;\n\t\t\t\tcase 4: orient = el.HasNodes(nf, 4); break;\n\t\t\t\tcase 6: orient = el.HasNodes(nf, 3); break;\n\t\t\t\tcase 7: orient = el.HasNodes(nf, 3); break;\n\t\t\t\tcase 8: orient = el.HasNodes(nf, 4); break;\n\t\t\t\tcase 9: orient = el.HasNodes(nf, 4); break;\n\t\t\t\tdefault:\n\t\t\t\t\tassert(false);\n\t\t\t\t}\n\n\t\t\t\tif (orient != 0)\n\t\t\t\t{\n\t\t\t\t\tref.pe = pe;\n\t\t\t\t\tref.face = j;\n\t\t\t\t\tref.orient = orient;\n\t\t\t\t\treturn ref;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ref;\n}\n\nvoid FESurface::ForEachSurfaceElement(std::function<void(FESurfaceElement& el)> f)\n{\n\tfor (size_t i = 0; i < m_el.size(); ++i) f(m_el[i]);\n}\n\nvoid FESurface::FindElements(FESurfaceElement& el)\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *GetMesh();\n\tFENodeElemList& NEL = mesh.NodeElementList();\n\n\tvector<int>& sf = el.m_node;\n\tint node = el.m_node[0];\n\tint nval = NEL.Valence(node);\n\tFEElement** ppe = NEL.ElementList(node);\n\tfor (int i = 0; i < nval; ++i)\n\t{\n\t\tFEElement& sel = *ppe[i];\n\t\tif (sel.isActive())\n\t\t{\n\t\t\t// check all faces of this solid element\n\t\t\tint orient = 0;\n\t\t\tint nfaces = sel.Faces();\n\t\t\tfor (int j = 0; j < nfaces; ++j)\n\t\t\t{\n\t\t\t\tint nf[FEElement::MAX_NODES];\n\t\t\t\tvec3d g[3];\n\t\t\t\tint nn = sel.GetFace(j, nf);\n\t\t\t\tif (nn == el.Nodes())\n\t\t\t\t{\n\t\t\t\t\tswitch (nn)\n\t\t\t\t\t{\n\t\t\t\t\tcase 3: orient = el.HasNodes(nf, 3); break;\n\t\t\t\t\tcase 4: orient = el.HasNodes(nf, 4); break;\n\t\t\t\t\tcase 6: orient = el.HasNodes(nf, 3); break;\n\t\t\t\t\tcase 7: orient = el.HasNodes(nf, 3); break;\n\t\t\t\t\tcase 8: orient = el.HasNodes(nf, 4); break;\n\t\t\t\t\tcase 9: orient = el.HasNodes(nf, 4); break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tassert(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (orient != 0) {\n\t\t\t\t\tif (el.m_elem[0].pe == nullptr) \n\t\t\t\t\t{ \n\t\t\t\t\t\tel.m_elem[0].pe = &sel;\n\t\t\t\t\t\tel.m_elem[0].face = j;\n\t\t\t\t\t\tel.m_elem[0].orient = orient;\n\t\t\t\t\t}\n\t\t\t\t\telse if (el.m_elem[0].pe != &sel)\n\t\t\t\t\t{\n\t\t\t\t\t\tel.m_elem[1].pe = &sel;\n\t\t\t\t\t\tel.m_elem[1].face = j;\n\t\t\t\t\t\tel.m_elem[1].orient = orient;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! unpack an LM vector from a dof list\nvoid FESurface::UnpackLM(const FESurfaceElement& el, const FEDofList& dofList, vector<int>& lm)\n{\n\tint dofPerNode = dofList.Size();\n\tint neln = el.Nodes();\n\tint ndof = neln*dofPerNode;\n\tlm.assign(ndof, -1);\n\tfor (int j = 0; j < neln; ++j)\n\t{\n\t\tFENode& node = Node(el.m_lnode[j]);\n\t\tfor (int k = 0; k < dofPerNode; ++k)\n\t\t{\n\t\t\tif (dofList[k] >= 0)\n\t\t\t\tlm[dofPerNode*j + k] = node.m_ID[dofList[k]];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// project onto a triangular face\nvec3d project2tri(vec3d* y, vec3d x, double& r, double& s)\n{\n\t// calculate base vectors \n\tvec3d e1 = y[1] - y[0];\n\tvec3d e2 = y[2] - y[0];\n\n\t// calculate plane normal\n\tvec3d n = e1^e2; n.unit();\n\n\t// project x onto the plane\n\tvec3d q = x - n*((x-y[0])*n);\n\n\t// set up metric tensor\n\tdouble G[2][2];\n\tG[0][0] = e1*e1;\n\tG[0][1] = G[1][0] = e1*e2;\n\tG[1][1] = e2*e2;\n\n\t// invert metric tensor\n\tdouble D = G[0][0]*G[1][1] - G[0][1]*G[1][0];\n\tdouble Gi[2][2];\n\tGi[0][0] = G[1][1]/D;\n\tGi[1][1] = G[0][0]/D;\n\tGi[0][1] = Gi[1][0] = -G[0][1]/D;\n\n\t// calculate dual base vectors\n\tvec3d E1 = e1*Gi[0][0] + e2*Gi[0][1];\n\tvec3d E2 = e1*Gi[1][0] + e2*Gi[1][1];\n\n\t// now we can calculate r and s\n\tvec3d t = q - y[0];\n\tr = t*E1;\n\ts = t*E2;\n\n\treturn q;\n}\n\n//-----------------------------------------------------------------------------\n// project onto a quadrilateral surface.\nbool project2quad(vec3d* y, vec3d x, double& r, double& s, vec3d& q)\n{\n\tdouble R[2], u[2], D;\n\tdouble gr[4] = {-1, +1, +1, -1};\n\tdouble gs[4] = {-1, -1, +1, +1};\n\tdouble H[4], Hr[4], Hs[4], Hrs[4];\n\n\tint i, j;\n\tint NMAX = 50, n=0;\n\n\t// evaulate scalar products\n\tdouble xy[4] = {x*y[0], x*y[1], x*y[2], x*y[3]};\n\tdouble yy[4][4];\n\tyy[0][0] = y[0]*y[0]; yy[1][1] = y[1]*y[1]; yy[2][2] = y[2]*y[2]; yy[3][3] = y[3]*y[3];\n\tyy[0][1] = yy[1][0] = y[0]*y[1];\n\tyy[0][2] = yy[2][0] = y[0]*y[2];\n\tyy[0][3] = yy[3][0] = y[0]*y[3];\n\tyy[1][2] = yy[2][1] = y[1]*y[2];\n\tyy[1][3] = yy[3][1] = y[1]*y[3];\n\tyy[2][3] = yy[3][2] = y[2]*y[3];\n\n\t// loop until converged\n\tbool bconv = false;\n\tdouble normu;\n\tdo\n\t{\n\t\t// evaluate shape functions and shape function derivatives.\n\t\tfor (i=0; i<4; ++i)\n\t\t{\n\t\t\tH[i] = 0.25*(1+gr[i]*r)*(1+gs[i]*s);\n\t\n\t\t\tHr[i] = 0.25*gr[i]*( 1 + gs[i]*s );\n\t\t\tHs[i] = 0.25*gs[i]*( 1 + gr[i]*r );\n\n\t\t\tHrs[i] = 0.25*gr[i]*gs[i];\n\t\t}\n\n\t\t// set up the system of equations\n\t\tR[0] = R[1] = 0;\n\t\tdouble A[2][2] = {0};\n\t\tfor (i=0; i<4; ++i)\n\t\t{\n\t\t\tR[0] -= (xy[i])*Hr[i];\n\t\t\tR[1] -= (xy[i])*Hs[i];\n\n\t\t\tA[0][1] += (xy[i])*Hrs[i];\n\t\t\tA[1][0] += (xy[i])*Hrs[i];\n\n\t\t\tfor (j=0; j<4; ++j)\n\t\t\t{\n\t\t\t\tdouble yij = yy[i][j];\n\t\t\t\tR[0] -= -H[j]*Hr[i]*(yij);\n\t\t\t\tR[1] -= -H[j]*Hs[i]*(yij);\n\n\t\t\t\tA[0][0] -= (yij)*(Hr[i]*Hr[j]);\n\t\t\t\tA[1][1] -= (yij)*(Hs[i]*Hs[j]);\n\n\t\t\t\tA[0][1] -= (yij)*(Hr[i]*Hs[j]+Hrs[i]*H[j]);\n\t\t\t\tA[1][0] -= (yij)*(Hs[i]*Hr[j]+Hrs[i]*H[j]);\n\t\t\t}\n\t\t}\n\t\n\t\t// determinant of A\n\t\tD = A[0][0]*A[1][1] - A[0][1]*A[1][0];\n\n\t\t// solve for u = A^(-1)*R\n\t\tu[0] = (A[1][1]*R[0] - A[0][1]*R[1])/D;\n\t\tu[1] = (A[0][0]*R[1] - A[1][0]*R[0])/D;\n\n\t\t// calculate displacement norm\n\t\tnormu = u[0]*u[0]+u[1]*u[1];\n\n\t\t// check for convergence\n\t\tbconv = ((normu < 1e-10));\n\t\tif (!bconv && (n <= NMAX))\n\t\t{\n\t\t\t// Don't update if converged otherwise the point q\n\t\t\t// does not correspond with the current values for (r,s)\n\t\t\tr += u[0];\n\t\t\ts += u[1];\n\t\t\t++n;\n\t\t}\n\t\telse break;\n\t}\n\twhile (1);\n\n\t// evaluate q\n\tq = y[0]*H[0] + y[1]*H[1] + y[2]*H[2] + y[3]*H[3];\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n// project to general surface element.\nbool project2surf(FESurfaceElement& el, vec3d* y, vec3d x, double& r, double& s, vec3d& q)\n{\n\tdouble Q[2], u[2], D;\n\tconst int NE = FEElement::MAX_NODES;\n\tdouble H[NE], Hr[NE], Hs[NE], Hrr[NE], Hss[NE], Hrs[NE];\n\n\tint i, j;\n\tint NMAX = 50, n=0;\n\n\t// get number of nodes\n\tint ne = el.Nodes();\n\n\t// evaulate scalar products\n\tdouble xy[NE];\n\tdouble yy[NE][NE];\n\tfor (i=0; i<ne; ++i)\n\t{\n\t\txy[i] = x*y[i];\n\t\tyy[i][i] = y[i]*y[i];\n\t\tfor (j=i+1; j<ne; ++j)\n\t\t{\n\t\t\tyy[i][j] = yy[j][i] = y[i]*y[j];\n\t\t}\n\t}\n\n\t// loop until converged\n\tbool bconv = false;\n\tdouble normu;\n\tdo\n\t{\n\t\t// evaluate shape functions and shape function derivatives.\n\t\tel.shape_fnc(H, r, s);\n\t\tel.shape_deriv(Hr, Hs, r, s);\n\t\tel.shape_deriv2(Hrr, Hrs, Hss, r, s);\n\n\t\t// set up the system of equations\n\t\tQ[0] = Q[1] = 0;\n\t\tdouble A[2][2] = {0};\n\t\tfor (i=0; i<ne; ++i)\n\t\t{\n\t\t\tQ[0] -= (xy[i])*Hr[i];\n\t\t\tQ[1] -= (xy[i])*Hs[i];\n\n\t\t\tA[0][0] += (xy[i])*Hrr[i];\n\t\t\tA[0][1] += (xy[i])*Hrs[i];\n\t\t\tA[1][0] += (xy[i])*Hrs[i];\n\t\t\tA[1][1] += (xy[i])*Hss[i];\n\n\t\t\tfor (j=0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tdouble yij = yy[i][j];\n\t\t\t\tQ[0] -= -H[j]*Hr[i]*(yij);\n\t\t\t\tQ[1] -= -H[j]*Hs[i]*(yij);\n\n\t\t\t\tA[0][0] -= (yij)*(H[i]*Hrr[j] + Hr[i]*Hr[j]);\n\t\t\t\tA[1][1] -= (yij)*(H[i]*Hss[j] + Hs[i]*Hs[j]);\n\n\t\t\t\tA[0][1] -= (yij)*(Hrs[i]*H[j] + Hr[i]*Hs[j]);\n\t\t\t\tA[1][0] -= (yij)*(Hrs[i]*H[j] + Hs[i]*Hr[j]);\n\t\t\t}\n\t\t}\n\t\n\t\t// determinant of A\n\t\tD = A[0][0]*A[1][1] - A[0][1]*A[1][0];\n\n\t\t// solve for u = A^(-1)*R\n\t\tu[0] = (A[1][1]*Q[0] - A[0][1]*Q[1])/D;\n\t\tu[1] = (A[0][0]*Q[1] - A[1][0]*Q[0])/D;\n\n\t\t// calculate displacement norm\n\t\tnormu = u[0]*u[0]+u[1]*u[1];\n\n\t\t// check for convergence\n\t\tbconv = ((normu < 1e-10));\n\t\tif (!bconv && (n <= NMAX))\n\t\t{\n\t\t\t// Don't update if converged otherwise the point q\n\t\t\t// does not correspond with the current values for (r,s)\n\t\t\tr += u[0];\n\t\t\ts += u[1];\n\t\t\t++n;\n\t\t}\n\t\telse break;\n\t}\n\twhile (1);\n\n\t// evaluate q\n\tq = vec3d(0,0,0);\n\tfor (int i=0; i<ne; ++i) q += y[i]*H[i];\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESurface::Position(FESurfaceElement& el, double r, double s)\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *m_pMesh;\n\n\t// number of element nodes\n\tint ne = el.Nodes();\n\n\t// get the elements nodal positions\n\tvec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i = 0; i<ne; ++i) y[i] = mesh.Node(el.m_node[i]).m_rt;\n    else for (int i = 0; i<ne; ++i) y[i] = mesh.Node(el.m_node[i]).st();\n\n\tdouble H[FEElement::MAX_NODES];\n\tel.shape_fnc(H, r, s);\n\n\tvec3d q(0,0,0);\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\tq += y[i]*H[i];\n\t}\n\n\treturn q;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the position of integration point n\n\nvec3d FESurface::Position(FESurfaceElement &el, int n)\n{\n    // get the mesh to which this surface belongs\n    FEMesh& mesh = *m_pMesh;\n    \n    // number of element nodes\n    int ne = el.Nodes();\n    \n    // get the elements nodal positions\n    vec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i = 0; i<ne; ++i) y[i] = mesh.Node(el.m_node[i]).m_rt;\n    else for (int i = 0; i<ne; ++i) y[i] = mesh.Node(el.m_node[i]).st();\n    \n    double* H = el.H(n);\n    \n    vec3d q(0,0,0);\n    for (int i=0; i<ne; ++i)\n    {\n        q += y[i]*H[i];\n    }\n    \n    return q;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::NodalCoordinates(FESurfaceElement& el, vec3d* re)\n{\n\tint ne = el.Nodes();\n\tif (!m_bshellb) for (int i = 0; i < ne; ++i) re[i] = Node(el.m_lnode[i]).m_rt;\n    else for (int i = 0; i < ne; ++i) re[i] = Node(el.m_lnode[i]).st();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::PreviousNodalCoordinates(FESurfaceElement& el, vec3d* re)\n{\n    int ne = el.Nodes();\n    if (!m_bshellb) for (int i = 0; i < ne; ++i) re[i] = Node(el.m_lnode[i]).m_rp;\n    else for (int i = 0; i < ne; ++i) re[i] = Node(el.m_lnode[i]).sp();\n}\n\n//-----------------------------------------------------------------------------\n//! detect face normal relative to element:\n//! return +1 if face points away from element, -1 if face points into element, 0 if invalid solution found\ndouble FESurface::FacePointing(FESurfaceElement& se, FEElement& el)\n{\n    FEMesh& mesh = *GetMesh();\n    // get point on surface element\n    vec3d sp = Position(se, 0,0);\n    \n    // get surface normal at that point;\n    vec3d sn = SurfaceNormal(se, 0,0);\n    \n    // check if element attached to this surface element is solid or shell\n    FESolidElement* sel = dynamic_cast<FESolidElement*>(&el);\n    FEShellElement* shl = dynamic_cast<FEShellElement*>(&el);\n    \n    // get centroid of element el\n    vec3d c(0,0,0);\n    if (sel) {\n        for (int i=0; i<sel->Nodes(); ++i) {\n            FENode& node = mesh.Node(sel->m_node[i]);\n            c += node.m_rt;\n        }\n        c /= sel->Nodes();\n    }\n    else if (shl) {\n        for (int i=0; i<sel->Nodes(); ++i) {\n            FENode& node = mesh.Node(sel->m_node[i]);\n            c += node.m_rt;\n            c += node.st();\n        }\n        c /= (2*sel->Nodes());\n    }\n    else\n        return 0;\n    \n    // project vector from centroid to surface point onto surface normal\n    double d = (sp - c)*sn;\n    if (d > 0) return 1.0;\n    else if (d < 0) return -1.0;\n    else return 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the projection of x on the surface element el.\n//! It does this by finding the solution of the nonlinear equation (x-y)*y,[a]=0,\n//! where the comma denotes differentation and a ranges from 1 to 2.\n//! The system is solved using the Newton-Raphson method.\n//! The surface element may be either a quad or a triangular element.\n\nvec3d FESurface::ProjectToSurface(FESurfaceElement& el, vec3d x, double& r, double& s)\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *m_pMesh;\n\n\t// number of element nodes\n\tint ne = el.Nodes();\n\n\t// get the elements nodal positions\n\tvec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i=0; i<ne; ++i) y[i] = mesh.Node(el.m_node[i]).m_rt;\n    else for (int i=0; i<ne; ++i) y[i] = mesh.Node(el.m_node[i]).st();\n\n\t// calculate normal projection of x onto element\n\tvec3d q;\n\tswitch (ne)\n\t{\n\tcase 3: q = project2tri(y, x, r, s); break;\n\tcase 4: \n\t\t// see if we get lucky\n\t\tif (project2quad(y, x, r, s, q) == false)\n\t\t{\n\t\t\t// the direct projection failed, so we'll try it more incrementally\n\t\t\tvec3d x0 = (y[0]+y[1]+y[2]+y[3])*0.25;\n\t\t\tr = s = 0;\n\t\t\tbool b = project2quad(y, x0, r, s, q);\n\t\t\tassert(b);\n\t\t\t\n\t\t\tdouble w = 0.5;\n\t\t\tint l = 1, N = 0, NMAX = 20;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tvec3d xi = x0*(1.0 - w) + x*w;\n\t\t\t\tb = project2quad(y, xi, r, s, q);\n\t\t\t\tif (b)\n\t\t\t\t{\n\t\t\t\t\t--l;\n\t\t\t\t\tif (l == 0) { x0 = xi; w = 1.0; }\n\t\t\t\t\telse w *= 2.0;\n\t\t\t\t}\n\t\t\t\telse \n\t\t\t\t{\n\t\t\t\t\t++l;\n\t\t\t\t\tw *= 0.5;\n\t\t\t\t}\n\t\t\t\t++N;\n\t\t\t}\n\t\t\twhile ((l >= 0) && (N<=NMAX) && (w>0.1));\n\t\t}\n\t\tbreak;\n\tcase 6: \n\tcase 7: \n\tcase 8:\n\tcase 9:\n\t\tif (project2surf(el, y, x, r, s, q)==false)\n\t\t{\n//\t\t\tassert(false);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn q;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the area of a surface element\ndouble FESurface::FaceArea(FESurfaceElement& el)\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *m_pMesh;\n\n\t// get the number of nodes\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\t// get the initial nodes\n\tvec3d r0[FEElement::MAX_NODES];\n\tif (!m_bshellb) for (int i=0; i<neln; ++i) r0[i] = mesh.Node(el.m_node[i]).m_r0;\n    else for (int i=0; i<neln; ++i) r0[i] = mesh.Node(el.m_node[i]).s0();\n\n\t// get the integration weights\n\tdouble* w = el.GaussWeights();\n\n\tdouble *Gr, *Gs;\n\tvec3d dxr, dxs;\n\n\tdouble detJ;\n\n\tdouble area = 0;\n\n\tint n, k;\n\n\tfor (n=0; n<nint; ++n)\n\t{\n\t\tGr = el.Gr(n);\n\t\tGs = el.Gs(n);\n\n\t\t// calculate jacobian\n\t\tdxr = dxs = vec3d(0,0,0);\n\t\tfor (k=0; k<neln; ++k)\n\t\t{\n\t\t\tdxr.x += Gr[k]*r0[k].x;\n\t\t\tdxr.y += Gr[k]*r0[k].y;\n\t\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\t\tdxs.x += Gs[k]*r0[k].x;\n\t\t\tdxs.y += Gs[k]*r0[k].y;\n\t\t\tdxs.z += Gs[k]*r0[k].z;\n\t\t}\n\n\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\tarea += w[n]*detJ;\n\t}\n\n\treturn area;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the area of a surface element\ndouble FESurface::CurrentFaceArea(FESurfaceElement& el)\n{\n\t// get the mesh to which this surface belongs\n\tFEMesh& mesh = *m_pMesh;\n\n\t// get the number of nodes\n\tint nint = el.GaussPoints();\n\tint neln = el.Nodes();\n\n\t// get the initial nodes\n\tvec3d rt[FEElement::MAX_NODES];\n\tif (!m_bshellb) for (int i = 0; i < neln; ++i) rt[i] = mesh.Node(el.m_node[i]).m_rt;\n\telse for (int i = 0; i < neln; ++i) rt[i] = mesh.Node(el.m_node[i]).st();\n\n\t// get the integration weights\n\tdouble* w = el.GaussWeights();\n\n\tdouble* Gr, * Gs;\n\tvec3d dxr, dxs;\n\n\tdouble detJ;\n\n\tdouble area = 0;\n\n\tint n, k;\n\n\tfor (n = 0; n < nint; ++n)\n\t{\n\t\tGr = el.Gr(n);\n\t\tGs = el.Gs(n);\n\n\t\t// calculate jacobian\n\t\tdxr = dxs = vec3d(0, 0, 0);\n\t\tfor (k = 0; k < neln; ++k)\n\t\t{\n\t\t\tdxr.x += Gr[k] * rt[k].x;\n\t\t\tdxr.y += Gr[k] * rt[k].y;\n\t\t\tdxr.z += Gr[k] * rt[k].z;\n\n\t\t\tdxs.x += Gs[k] * rt[k].x;\n\t\t\tdxs.y += Gs[k] * rt[k].y;\n\t\t\tdxs.z += Gs[k] * rt[k].z;\n\t\t}\n\n\t\tdetJ = (dxr ^ dxs).norm();\n\n\t\tarea += w[n] * detJ;\n\t}\n\n\treturn area;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the max element size.\ndouble FESurface::MaxElementSize()\n{\n\tFEMesh& mesh = *m_pMesh;\n\tdouble h2 = 0.0;\n\tint NS = Elements();\n\tfor (int m=0; m<NS; ++m)\n\t{\n\t\tFESurfaceElement& e = Element(m);\n\t\tint n = e.Nodes();\n\t\tfor (int i=0; i<n; ++i)\n\t\t\tfor (int j=i+1; j<n; ++j)\n\t\t\t{\n                vec3d& a = mesh.Node(e.m_node[i]).m_rt;\n                vec3d& b = mesh.Node(e.m_node[j]).m_rt;\n\t\t\t\tdouble L2 = (b - a)*(b - a);\n\t\t\t\tif (L2 > h2) h2 = L2;\n\t\t\t}\n\t}\n\treturn sqrt(h2);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the metric tensor at the point with surface coordinates (r,s)\n//! \\todo Perhaps I should place this function in the element class.\n\nmat2d FESurface::Metric0(FESurfaceElement& el, double r, double s)\n{\n\t// nr of element nodes\n\tint neln = el.Nodes();\n\t\n\t// element nodes\n\tvec3d r0[FEElement::MAX_NODES];\n\tif (!m_bshellb) for (int i=0; i<neln; ++i) r0[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    else for (int i=0; i<neln; ++i) r0[i] = m_pMesh->Node(el.m_node[i]).s0();\n\t\n\t// shape function derivatives\n\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\t\n\t// get the shape function values at this node\n\tel.shape_deriv(Hr, Hs, r, s);\n\t\n\t// get the tangent vectors\n\tvec3d t1(0,0,0);\n\tvec3d t2(0,0,0);\n\tfor (int k=0; k<neln; ++k)\n\t{\n\t\tt1 += r0[k]*Hr[k];\n\t\tt2 += r0[k]*Hs[k];\n\t}\n\t\n\t// calculate metric tensor\n\treturn mat2d(t1*t1, t1*t2, t2*t1, t2*t2);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the metric tensor at the point with surface coordinates (r,s)\n//! \\todo Perhaps I should place this function in the element class.\n\nmat2d FESurface::Metric(FESurfaceElement& el, double r, double s)\n{\n\t// nr of element nodes\n\tint neln = el.Nodes();\n\t\n\t// element nodes\n\tvec3d rt[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i=0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n    else for (int i=0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).st();\n\t\n\t// shape function derivatives\n\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\t\n\t// get the shape function values at this node\n\tel.shape_deriv(Hr, Hs, r, s);\n\t\n\t// get the tangent vectors\n\tvec3d t1(0,0,0);\n\tvec3d t2(0,0,0);\n\tfor (int k=0; k<neln; ++k)\n\t{\n\t\tt1 += rt[k]*Hr[k];\n\t\tt2 += rt[k]*Hs[k];\n\t}\n\t\n\t// calculate metric tensor\n\treturn mat2d(t1*t1, t1*t2, t2*t1, t2*t2);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the metric tensor at an integration point\n//! \\todo Perhaps I should place this function in the element class.\n\nmat2d FESurface::Metric(const FESurfaceElement& el, int n) const\n{\n    // nr of element nodes\n    int neln = el.Nodes();\n    \n    // element nodes\n    vec3d rt[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i=0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).m_rt;\n    else for (int i=0; i<neln; ++i) rt[i] = m_pMesh->Node(el.m_node[i]).st();\n    \n    // get the shape function derivatives at this integration point\n    double* Hr = el.Gr(n);\n    double* Hs = el.Gs(n);\n    \n    // get the tangent vectors\n    vec3d t1(0,0,0);\n    vec3d t2(0,0,0);\n    for (int k=0; k<neln; ++k)\n    {\n        t1 += rt[k]*Hr[k];\n        t2 += rt[k]*Hs[k];\n    }\n    \n    // calculate metric tensor\n    return mat2d(t1*t1, t1*t2, t2*t1, t2*t2);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculates the metric tensor at an integration point at previous time\n//! \\todo Perhaps I should place this function in the element class.\n\nmat2d FESurface::MetricP(FESurfaceElement& el, int n)\n{\n    // nr of element nodes\n    int neln = el.Nodes();\n    \n    // element nodes\n    vec3d rp[FEElement::MAX_NODES];\n    for (int i=0; i<neln; ++i) rp[i] = m_pMesh->Node(el.m_node[i]).m_rp;\n    \n    // get the shape function derivatives at this integration point\n    double* Hr = el.Gr(n);\n    double* Hs = el.Gs(n);\n    \n    // get the tangent vectors\n    vec3d t1(0,0,0);\n    vec3d t2(0,0,0);\n    for (int k=0; k<neln; ++k)\n    {\n        t1 += rp[k]*Hr[k];\n        t2 += rp[k]*Hs[k];\n    }\n    \n    // calculate metric tensor\n    return mat2d(t1*t1, t1*t2, t2*t1, t2*t2);\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the global location of an integration point in its reference configuration\n//!\n\nvec3d FESurface::Local2Global0(FESurfaceElement &el, int n)\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the shape functions at this integration point\n    double* H = el.H(n);\n    \n    // calculate the location\n    vec3d r(0);\n    int ne = el.Nodes();\n    if (!m_bshellb) for (int i=0; i<ne; ++i) r += m.Node(el.m_node[i]).m_r0*H[i];\n    else for (int i=0; i<ne; ++i) r += m.Node(el.m_node[i]).s0()*H[i];\n    \n    return r;\n}\n\n//-----------------------------------------------------------------------------\n//! Given an element an the natural coordinates of a point in this element, this\n//! function returns the global position vector.\nvec3d FESurface::Local2Global(FESurfaceElement &el, double r, double s)\n{\n\t// get the mesh\n\tFEMesh& mesh = *m_pMesh;\n\n\t// get the coordinates of the element nodes\n\tint ne = el.Nodes();\n\tvec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int l=0; l<ne; ++l) y[l] = mesh.Node(el.m_node[l]).m_rt;\n    else for (int l=0; l<ne; ++l) y[l] = mesh.Node(el.m_node[l]).st();\n\n\t// calculate the element position\n\treturn el.eval(y, r, s);\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the global location of an integration point\n//!\n\nvec3d FESurface::Local2Global(FESurfaceElement &el, int n)\n{\n\tFEMesh& m = *m_pMesh;\n\n\t// get the shape functions at this integration point\n\tdouble* H = el.H(n);\n\n\t// calculate the location\n\tvec3d r(0);\n\tint ne = el.Nodes();\n    if (!m_bshellb) for (int i=0; i<ne; ++i) r += m.Node(el.m_node[i]).m_rt*H[i];\n    else for (int i=0; i<ne; ++i) r += m.Node(el.m_node[i]).st()*H[i];\n\n\treturn r;\n}\n\n//-----------------------------------------------------------------------------\n//! Given an element an the natural coordinates of a point in this element, this\n//! function returns the global position vector at the previous time.\nvec3d FESurface::Local2GlobalP(FESurfaceElement &el, double r, double s)\n{\n    // get the mesh\n    FEMesh& mesh = *m_pMesh;\n    \n    // get the coordinates of the element nodes\n    int ne = el.Nodes();\n    vec3d y[FEElement::MAX_NODES];\n    for (int l=0; l<ne; ++l) y[l] = mesh.Node(el.m_node[l]).m_rp;\n    \n    // calculate the element position\n    return el.eval(y, r, s);\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the global location of an integration point\n//!\n\nvec3d FESurface::Local2GlobalP(FESurfaceElement &el, int n)\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the shape functions at this integration point\n    double* H = el.H(n);\n    \n    // calculate the location\n    vec3d r;\n    int ne = el.Nodes();\n    for (int i=0; i<ne; ++i) r += m.Node(el.m_node[i]).m_rp*H[i];\n    \n    return r;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the normal of a surface element at integration\n//! point n\n\nvec3d FESurface::SurfaceNormal(const FESurfaceElement &el, int n) const\n{\n\tFEMesh& m = *m_pMesh;\n\n\t// get the shape function derivatives at this integration point\n\tdouble* Hr = el.Gr(n);\n\tdouble* Hs = el.Gs(n);\n\n\t// get the coordinates of the element nodes\n\tint ne = el.Nodes();\n\tvec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i=0; i<ne; ++i) y[i] = m.Node(el.m_node[i]).m_rt;\n    else for (int i=0; i<ne; ++i) y[i] = m.Node(el.m_node[i]).st();\n\n\t// calculate the tangents\n\tvec3d xr, xs;\n\tfor (int i=0; i<ne; ++i)\n\t{\n\t\txr += y[i]*Hr[i];\n\t\txs += y[i]*Hs[i];\n\t}\n\n\t// calculate the normal\n\tvec3d np = xr ^ xs;\n\tnp.unit();\n    if (m_bshellb) np = -np;\n\n\treturn np;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the normal of a surface element at the natural\n//! coordinates (r,s)\n\nvec3d FESurface::SurfaceNormal(FESurfaceElement &el, double r, double s) const\n{\n\tint l;\n\tFEMesh& mesh = *m_pMesh;\n\t\n\t// get the coordinates of the element nodes\n\tint ne = el.Nodes();\n\tvec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (l=0; l<ne; ++l) y[l] = mesh.Node(el.m_node[l]).m_rt;\n    else for (l=0; l<ne; ++l) y[l] = mesh.Node(el.m_node[l]).st();\n\t\n\t// set up shape functions and derivatives\n\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\tel.shape_deriv(Hr, Hs, r, s);\n\t\n\t// calculate the element tangents\n\tvec3d xr(0,0,0), xs;\n\tfor (l=0; l<ne; ++l)\n\t{\n\t\txr += y[l]*Hr[l];\n\t\txs += y[l]*Hs[l];\n\t}\n\t\n\t// calculate the normal\n\tvec3d np = xr ^ xs;\n\tnp.unit();\n    \n    if (m_bshellb) np = -np;\n\t\n\treturn np;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the node normal. Due to the piecewise continuity\n//! of the surface elements this normal is not uniquely defined so in order to\n//! obtain a unique normal the normal is averaged for each node over all the\n//! element normals at the node\n\nvoid FESurface::UpdateNodeNormals()\n{\n    const int MN = FEElement::MAX_NODES;\n    vec3d y[MN];\n    \n    // zero nodal normals\n    zero(m_nn);\n    \n    // loop over all elements\n    for (int i=0; i<Elements(); ++i)\n    {\n        FESurfaceElement& el = Element(i);\n        int ne = el.Nodes();\n        \n        // get the nodal coordinates\n        for (int j=0; j<ne; ++j) y[j] = Node(el.m_lnode[j]).m_rt;\n        \n        // calculate the normals\n        for (int j=0; j<ne; ++j)\n        {\n            int jp1 = (j+1)%ne;\n            int jm1 = (j+ne-1)%ne;\n            vec3d n = (y[jp1] - y[j]) ^ (y[jm1] - y[j]);\n            m_nn[el.m_lnode[j]] += n;\n        }\n    }\n    \n    // normalize all vectors\n    const int N = Nodes();\n    for (int i=0; i<N; ++i) m_nn[i].unit();\n}\n\n//-----------------------------------------------------------------------------\n//! This function checks whether the point with natural coordinates (r,s) is\n//! inside the element, within a tolerance of tol.\n\nbool FESurface::IsInsideElement(FESurfaceElement& el, double r, double s, double tol)\n{\n\tint ne = el.Nodes();\n\tswitch (ne)\n\t{\n\tcase 4:\n\tcase 8:\n\tcase 9:\n\t\t{\n\t\t\t// check quads\n\t\t\tif ((r >= -1-tol) && (r <= 1+tol) && (s >= -1-tol) && (s <= 1+tol)) return true;\n\t\t\telse return false;\n\t\t}\n\t\tbreak;\n\tcase 3:\n\tcase 6:\n\tcase 7:\n\t\t{\n\t\t\t// check triangles\n\t\t\tif ((r >= -tol) && (s >= -tol) && (r+s <= 1+tol)) return true;\n\t\t\telse return false;\n\t\t}\n\t\tbreak;\n\t}\n\tassert(false);\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant base vectors of a surface element\n//! at an integration point\n\nvoid FESurface::CoBaseVectors(FESurfaceElement& el, double r, double s, vec3d t[2])\n{\n\tFEMesh& m = *m_pMesh;\n\n\t// get the nr of nodes\n\tint n = el.Nodes();\n\n\t// get the shape function derivatives\n\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\tel.shape_deriv(Hr, Hs, r, s);\n\n\tt[0] = t[1] = vec3d(0,0,0);\n    if (!m_bshellb) {\n        for (int i=0; i<n; ++i)\n        {\n            t[0] += m.Node(el.m_node[i]).m_rt*Hr[i];\n            t[1] += m.Node(el.m_node[i]).m_rt*Hs[i];\n        }\n    }\n    else {\n        for (int i=0; i<n; ++i)\n        {\n            t[0] -= m.Node(el.m_node[i]).st()*Hr[i];\n            t[1] -= m.Node(el.m_node[i]).st()*Hs[i];\n        }\n    }\n}\n\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant base vectors of a surface element\n//! at an integration point\n\nvoid FESurface::CoBaseVectors(const FESurfaceElement& el, int j, vec3d t[2]) const\n{\n\tFEMesh& m = *m_pMesh;\n\n\t// get the nr of nodes\n\tint n = el.Nodes();\n\n\t// get the shape function derivatives\n\tdouble* Hr = el.Gr(j);\n\tdouble* Hs = el.Gs(j);\n\n\tt[0] = t[1] = vec3d(0,0,0);\n    if (!m_bshellb) {\n        for (int i=0; i<n; ++i)\n        {\n            t[0] += m.Node(el.m_node[i]).m_rt*Hr[i];\n            t[1] += m.Node(el.m_node[i]).m_rt*Hs[i];\n        }\n    }\n    else {\n        for (int i=0; i<n; ++i)\n        {\n            t[0] -= m.Node(el.m_node[i]).st()*Hr[i];\n            t[1] -= m.Node(el.m_node[i]).st()*Hs[i];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant base vectors of a surface element\n//! at an integration point at previous time step\n\nvoid FESurface::CoBaseVectorsP(FESurfaceElement& el, int j, vec3d t[2])\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hr = el.Gr(j);\n    double* Hs = el.Gs(j);\n    \n    t[0] = t[1] = vec3d(0,0,0);\n    for (int i=0; i<n; ++i)\n    {\n        t[0] += m.Node(el.m_node[i]).m_rp*Hr[i];\n        t[1] += m.Node(el.m_node[i]).m_rp*Hs[i];\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant base vectors of a surface element\n//! at an integration point in the reference configuration\n\nvoid FESurface::CoBaseVectors0(const FESurfaceElement& el, int j, vec3d t[2]) const\n{\n    FEMesh& m = *m_pMesh;\n    \n    // get the nr of nodes\n    int n = el.Nodes();\n    \n    // get the shape function derivatives\n    double* Hr = el.Gr(j);\n    double* Hs = el.Gs(j);\n    \n    t[0] = t[1] = vec3d(0,0,0);\n    if (!m_bshellb) {\n        for (int i=0; i<n; ++i)\n        {\n            t[0] += m.Node(el.m_node[i]).m_r0*Hr[i];\n            t[1] += m.Node(el.m_node[i]).m_r0*Hs[i];\n        }\n    }\n    else {\n        for (int i=0; i<n; ++i)\n        {\n            t[0] -= m.Node(el.m_node[i]).s0()*Hr[i];\n            t[1] -= m.Node(el.m_node[i]).s0()*Hs[i];\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the covariant base vectors of a surface element\n//! at the natural coordinates (r,s)\n\nvoid FESurface::CoBaseVectors0(FESurfaceElement &el, double r, double s, vec3d t[2])\n{\n\tint i;\n\tconst int MN = FEElement::MAX_NODES;\n\tvec3d y[MN];\n\tdouble H0[MN], H1[MN];\n\tint n = el.Nodes();\n\tif (!m_bshellb) for (i=0; i<n; ++i) y[i] = m_pMesh->Node(el.m_node[i]).m_r0;\n    else for (i=0; i<n; ++i) y[i] = m_pMesh->Node(el.m_node[i]).s0();\n\tel.shape_deriv(H0, H1, r, s);\n\tt[0] = t[1] = vec3d(0,0,0);\n\tfor (i=0; i<n; ++i) \n\t{\n\t\tt[0] += y[i]*H0[i];\n\t\tt[1] += y[i]*H1[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::ContraBaseVectors(const FESurfaceElement& el, int j, vec3d t[2]) const\n{\n    vec3d e[2];\n    CoBaseVectors(el, j, e);\n    mat2d M = Metric(el, j);\n    mat2d Mi = M.inverse();\n    \n    t[0] = e[0]*Mi[0][0] + e[1]*Mi[0][1];\n    t[1] = e[0]*Mi[1][0] + e[1]*Mi[1][1];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::ContraBaseVectorsP(FESurfaceElement& el, int j, vec3d t[2])\n{\n    vec3d e[2];\n    CoBaseVectorsP(el, j, e);\n    mat2d M = MetricP(el, j);\n    mat2d Mi = M.inverse();\n    \n    t[0] = e[0]*Mi[0][0] + e[1]*Mi[0][1];\n    t[1] = e[0]*Mi[1][0] + e[1]*Mi[1][1];\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::ContraBaseVectors(FESurfaceElement& el, double r, double s, vec3d t[2])\n{\n\tvec3d e[2];\n\tCoBaseVectors(el, r, s, e);\n\tmat2d M = Metric(el, r, s);\n\tmat2d Mi = M.inverse();\n\n\tt[0] = e[0]*Mi[0][0] + e[1]*Mi[0][1];\n\tt[1] = e[0]*Mi[1][0] + e[1]*Mi[1][1];\n}\n\n//-----------------------------------------------------------------------------\n\nvoid FESurface::ContraBaseVectors0(FESurfaceElement& el, double r, double s, vec3d t[2])\n{\n\tvec3d e[2];\n\tCoBaseVectors0(el, r, s, e);\n\tmat2d M = Metric0(el, r, s);\n\tmat2d Mi = M.inverse();\n\n\tt[0] = e[0]*Mi[0][0] + e[1]*Mi[0][1];\n\tt[1] = e[0]*Mi[1][0] + e[1]*Mi[1][1];\n}\n\n//-----------------------------------------------------------------------------\ndouble FESurface::jac0(FESurfaceElement &el, int n)\n{\n\tconst int nseln = el.Nodes();\n\tvec3d r0[FEElement::MAX_NODES];\n\tif (!m_bshellb) for (int i=0; i<nseln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).m_r0;\n    else for (int i=0; i<nseln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).s0();\n\n\tdouble* Gr = el.Gr(n);\n\tdouble* Gs = el.Gs(n);\n\tvec3d dxr(0,0,0), dxs(0,0,0);\n\tfor (int k=0; k<nseln; ++k)\n\t{\n\t\tdxr.x += Gr[k]*r0[k].x;\n\t\tdxr.y += Gr[k]*r0[k].y;\n\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\tdxs.x += Gs[k]*r0[k].x;\n\t\tdxs.y += Gs[k]*r0[k].y;\n\t\tdxs.z += Gs[k]*r0[k].z;\n\t}\n\treturn (dxr ^ dxs).norm();\n}\n\n//-----------------------------------------------------------------------------\ndouble FESurface::jac0(const FESurfaceElement &el, int n, vec3d& nu)\n{\n\tconst int nseln = el.Nodes();\n\tvec3d r0[FEElement::MAX_NODES];\n\tif (!m_bshellb) for (int i=0; i<nseln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).m_r0;\n    else for (int i=0; i<nseln; ++i) r0[i] = GetMesh()->Node(el.m_node[i]).s0();\n\n\tdouble* Gr = el.Gr(n);\n\tdouble* Gs = el.Gs(n);\n\tvec3d dxr(0,0,0), dxs(0,0,0);\n\tfor (int k=0; k<nseln; ++k)\n\t{\n\t\tdxr.x += Gr[k]*r0[k].x;\n\t\tdxr.y += Gr[k]*r0[k].y;\n\t\tdxr.z += Gr[k]*r0[k].z;\n\n\t\tdxs.x += Gs[k]*r0[k].x;\n\t\tdxs.y += Gs[k]*r0[k].y;\n\t\tdxs.z += Gs[k]*r0[k].z;\n\t}\n\tnu = dxr ^ dxs;\n\treturn nu.unit();\n}\n\n//-----------------------------------------------------------------------------\n// This function calculates the intersection of a ray with a triangle\n// and returns true if the ray intersects the triangle.\n//\nbool IntersectTri(vec3d* y, vec3d r, vec3d n, double rs[2], double& g, double eps)\n{\n\tvec3d e[2], E[2];\n\tmat2d G;\n\n\t// create base vectors on triangle\n\te[0] = y[1]-y[0];\n\te[1] = y[2]-y[0];\n\n\t// create triangle normal\n\tvec3d m = e[0]^e[1]; m.unit();\n\n\tdouble d = n*m;\n//\tif (d != 0)\n\t// normals should be pointing opposite to each other for valid contact\n\tif (d < 0)\n\t{\n\t\t// distance from r to plane of triangle\n\t\tg = m*(y[0] - r)/d;\n\n\t\t// intersection point with plane of triangle\n\t\tvec3d q = r + n*g;\n\n\t\t// next, we decompose q into its components\n\t\t// in the triangle basis\n\t\t// we need to create the dual basis\n\t\t// first, we calculate the metric tensor\n\t\tG[0][0] = e[0]*e[0]; G[0][1] = e[0]*e[1];\n\t\tG[1][0] = e[1]*e[0]; G[1][1] = e[1]*e[1];\n\n\t\t// and its inverse\n\t\tmat2d Gi = G.inverse();\n\n\t\t// build dual basis\n\t\tE[0] = e[0]*Gi[0][0] + e[1]*Gi[0][1];\n\t\tE[1] = e[0]*Gi[1][0] + e[1]*Gi[1][1];\n\n\t\t// get the components\n\t\trs[0] = E[0]*(q - y[0]);\n\t\trs[1] = E[1]*(q - y[0]);\n\n\t\t// see if the intersection point is inside the triangle\n\t\tif ((rs[0] >= -eps) && (rs[1] >= -eps) && (rs[0]+rs[1] <= 1+eps)) return true;\n\t}\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the intersection of a ray with a quad\n//! and returns true if the ray intersected.\n//!\nbool IntersectQuad(vec3d* y, vec3d r, vec3d n, double rs[2], double& g, double eps, bool checkNormal = true)\n{\n\t// first we're going to see if the ray intersects the two subtriangles\n\tvec3d x1[3], x2[3];\n\tx1[0] = y[0]; x2[0] = y[2];\n\tx1[1] = y[1]; x2[1] = y[3];\n\tx1[2] = y[3]; x2[2] = y[1];\n\n\tbool b = false;\n\tdouble rp = 0, sp = 0;\n\n\tif (IntersectTri(x1, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the first triangle\n\t\tb = true;\n\t\trp = -1.0 + 2.0*rs[0];\n\t\tsp = -1.0 + 2.0*rs[1];\n\t}\n\telse if (IntersectTri(x2, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the second triangle\n\t\tb = true;\n\t\trp = 1.0 - 2.0*rs[0];\n\t\tsp = 1.0 - 2.0*rs[1];\n\t}\n\n\t// if one of the triangels was intersected,\n\t// we calculate a more accurate projection\n\tif (b)\n\t{\n\t\tmat3d A;\n\t\tvec3d dx;\n\t\tvec3d F, F1, F2, F3;\n\t\tdouble H[4], H1[4], H2[4];\n\n\t\tdouble l1 = rp;\n\t\tdouble l2 = sp;\n\t\tdouble l3 = g;\n\t\t\n\t\tint nn = 0;\n\t\tint maxn = 5;\n\t\tdo\n\t\t{\n\t\t\t// shape functions of quad\n\t\t\tH[0] = 0.25*(1 - l1)*(1 - l2);\n\t\t\tH[1] = 0.25*(1 + l1)*(1 - l2);\n\t\t\tH[2] = 0.25*(1 + l1)*(1 + l2);\n\t\t\tH[3] = 0.25*(1 - l1)*(1 + l2);\n\n\t\t\t// shape function derivatives\n\t\t\tH1[0] = -0.25*(1 - l2); H2[0] = -0.25*(1 - l1);\n\t\t\tH1[1] =  0.25*(1 - l2); H2[1] = -0.25*(1 + l1);\n\t\t\tH1[2] =  0.25*(1 + l2); H2[2] =  0.25*(1 + l1);\n\t\t\tH1[3] = -0.25*(1 + l2); H2[3] =  0.25*(1 - l1);\n\n\t\t\t// calculate residual\n\t\t\tF = r + n*l3 - y[0]*H[0] - y[1]*H[1] - y[2]*H[2] - y[3]*H[3];\n\n\t\t\t// residual derivatives\n\t\t\tF1 = - y[0]*H1[0] - y[1]*H1[1] - y[2]*H1[2] - y[3]*H1[3];\n\t\t\tF2 = - y[0]*H2[0] - y[1]*H2[1] - y[2]*H2[2] - y[3]*H2[3];\n\t\t\tF3 = n;\n\n\t\t\t// set up the tangent matrix\n\t\t\tA[0][0] = F1.x; A[0][1] = F2.x; A[0][2] = F3.x;\n\t\t\tA[1][0] = F1.y; A[1][1] = F2.y; A[1][2] = F3.y;\n\t\t\tA[2][0] = F1.z; A[2][1] = F2.z; A[2][2] = F3.z;\n\n\t\t\t// calculate solution increment\n            mat3d Ai = A.inverse();\n\t\t\tdx = -(Ai*F);\n\n\t\t\t// update solution\n\t\t\tl1 += dx.x;\n\t\t\tl2 += dx.y;\n\t\t\tl3 += dx.z;\n\n\t\t\t++nn;\n\t\t}\n\t\twhile ((dx.norm() > 1e-7) && (nn < maxn));\n        if (nn == maxn) return false;\n\n\t\t// store results\n\t\trs[0] = l1;\n\t\trs[1] = l2;\n\t\tg     = l3;\n\t\tif (checkNormal)\n\t\t{\n\t\t\tvec3d nu2 = F1 ^ F2;\n\t\t\tnu2.unit();\n\t\t\tdouble cosq = n * nu2;\n\t\t\tif (cosq > 0) return false;\n\t\t}\n\n\t\t// see if the point is inside the quad\n\t\tif ((rs[0] >= -1-eps) && (rs[0] <= 1+eps) && \n\t\t\t(rs[1] >= -1-eps) && (rs[1] <= 1+eps)) return true;\n\t}\n\n\treturn false;\n}\n\n\n//-----------------------------------------------------------------------------\n//! This function calculates the intersection of a ray with a quad\n//! and returns true if the ray intersected.\n//!\nbool IntersectQuad8(vec3d* y, vec3d r, vec3d n, double rs[2], double& g, double eps)\n{\n\tmat3d A;\n\tvec3d dx;\n\tvec3d F, F1, F2, F3;\n\tdouble H[8], H1[8], H2[8];\n\n\tdouble l1 = 0;\n\tdouble l2 = 0;\n\tdouble l3 = 0;\n\t\t\n\tint nn = 0;\n\tint maxn = 10;\n\tdo\n\t{\n\t\t// shape functions of quad\n\t\tH[4] = 0.5*(1 - l1*l1)*(1 - l2);\n\t\tH[5] = 0.5*(1 - l2*l2)*(1 + l1);\n\t\tH[6] = 0.5*(1 - l1*l1)*(1 + l2);\n\t\tH[7] = 0.5*(1 - l2*l2)*(1 - l1);\n\n\t\tH[0] = 0.25*(1 - l1)*(1 - l2) - 0.5*(H[4] + H[7]);\n\t\tH[1] = 0.25*(1 + l1)*(1 - l2) - 0.5*(H[4] + H[5]);\n\t\tH[2] = 0.25*(1 + l1)*(1 + l2) - 0.5*(H[5] + H[6]);\n\t\tH[3] = 0.25*(1 - l1)*(1 + l2) - 0.5*(H[6] + H[7]);\n\n\t\t// shape function derivatives\n\t\tH1[4] = -l1*(1 - l2);\n\t\tH1[5] = 0.5*(1 - l2*l2);\n\t\tH1[6] = -l1*(1 + l2);\n\t\tH1[7] = -0.5*(1 - l2*l2);\n\n\t\tH1[0] = -0.25*(1 - l2) - 0.5*(H1[4] + H1[7]);\n\t\tH1[1] =  0.25*(1 - l2) - 0.5*(H1[4] + H1[5]);\n\t\tH1[2] =  0.25*(1 + l2) - 0.5*(H1[5] + H1[6]);\n\t\tH1[3] = -0.25*(1 + l2) - 0.5*(H1[6] + H1[7]);\n\n\t\tH2[4] = -0.5*(1 - l1*l1);\n\t\tH2[5] = -l2*(1 + l1);\n\t\tH2[6] = 0.5*(1 - l1*l1);\n\t\tH2[7] = -l2*(1 - l1);\n\n\t\tH2[0] = -0.25*(1 - l1) - 0.5*(H2[4] + H2[7]);\n\t\tH2[1] = -0.25*(1 + l1) - 0.5*(H2[4] + H2[5]);\n\t\tH2[2] =  0.25*(1 + l1) - 0.5*(H2[5] + H2[6]);\n\t\tH2[3] =  0.25*(1 - l1) - 0.5*(H2[6] + H2[7]);\n\t\t\n\t\t// calculate residual\n\t\tF = r + n*l3 - y[0]*H[0] - y[1]*H[1] - y[2]*H[2] - y[3]*H[3] - y[4]*H[4] - y[5]*H[5] - y[6]*H[6] - y[7]*H[7];\n\n\t\t// residual derivatives\n\t\tF1 = - y[0]*H1[0] - y[1]*H1[1] - y[2]*H1[2] - y[3]*H1[3] - y[4]*H1[4] - y[5]*H1[5] - y[6]*H1[6] - y[7]*H1[7];\n\t\tF2 = - y[0]*H2[0] - y[1]*H2[1] - y[2]*H2[2] - y[3]*H2[3] - y[4]*H2[4] - y[5]*H2[5] - y[6]*H2[6] - y[7]*H2[7];\n\t\tF3 = n;\n\n\t\t// set up the tangent matrix\n\t\tA[0][0] = F1.x; A[0][1] = F2.x; A[0][2] = F3.x;\n\t\tA[1][0] = F1.y; A[1][1] = F2.y; A[1][2] = F3.y;\n\t\tA[2][0] = F1.z; A[2][1] = F2.z; A[2][2] = F3.z;\n\n\t\t// calculate solution increment\n        mat3d Ai = A.inverse();\n\t\tdx = -(Ai*F);\n\n\t\t// update solution\n\t\tl1 += dx.x;\n\t\tl2 += dx.y;\n\t\tl3 += dx.z;\n\n\t\t++nn;\n\t}\n\twhile ((dx.norm() > 1e-7) && (nn < maxn));\n    if (nn == maxn) return false;\n\n\t// store results\n\trs[0] = l1;\n\trs[1] = l2;\n\tg     = l3;\n    vec3d nu2 = F1 ^ F2;\n    nu2.unit();\n    double cosq = n*nu2;\n    if (cosq > 0) return false;\n\n\t// see if the point is inside the quad\n\tif ((rs[0] >= -1-eps) && (rs[0] <= 1+eps) && \n\t\t(rs[1] >= -1-eps) && (rs[1] <= 1+eps)) return true;\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the intersection of a ray with a quad\n//! and returns true if the ray intersected.\n//!\nbool IntersectQuad9(vec3d* y, vec3d r, vec3d n, double rs[2], double& g, double eps)\n{\n\tmat3d A;\n\tvec3d dx;\n\tvec3d F, F1, F2, F3;\n\tdouble H[9], H1[9], H2[9];\n\n\tdouble l1 = 0;\n\tdouble l2 = 0;\n\tdouble l3 = 0;\n\t\t\n\tint nn = 0;\n\tint maxn = 10;\n\tdo\n\t{\n\t\t// shape functions of quad\n\t\tdouble R[3] = {0.5*l1*(l1-1.0), 0.5*l1*(l1+1.0), 1.0 - l1*l1};\n\t\tdouble S[3] = {0.5*l2*(l2-1.0), 0.5*l2*(l2+1.0), 1.0 - l2*l2};\n\t\tdouble DR[3] = {l1-0.5, l1+0.5, -2.0*l1};\n\t\tdouble DS[3] = {l2-0.5, l2+0.5, -2.0*l2};\n\n\t\tH[0] = R[0]*S[0];\n\t\tH[1] = R[1]*S[0];\n\t\tH[2] = R[1]*S[1];\n\t\tH[3] = R[0]*S[1];\n\t\tH[4] = R[2]*S[0];\n\t\tH[5] = R[1]*S[2];\n\t\tH[6] = R[2]*S[1];\n\t\tH[7] = R[0]*S[2];\n\t\tH[8] = R[2]*S[2];\n\n\t\t// shape function derivatives\n\t\tH1[0] = DR[0]*S[0];\n\t\tH1[1] = DR[1]*S[0];\n\t\tH1[2] = DR[1]*S[1];\n\t\tH1[3] = DR[0]*S[1];\n\t\tH1[4] = DR[2]*S[0];\n\t\tH1[5] = DR[1]*S[2];\n\t\tH1[6] = DR[2]*S[1];\n\t\tH1[7] = DR[0]*S[2];\n\t\tH1[8] = DR[2]*S[2];\n\n\t\tH2[0] = R[0]*DS[0];\n\t\tH2[1] = R[1]*DS[0];\n\t\tH2[2] = R[1]*DS[1];\n\t\tH2[3] = R[0]*DS[1];\n\t\tH2[4] = R[2]*DS[0];\n\t\tH2[5] = R[1]*DS[2];\n\t\tH2[6] = R[2]*DS[1];\n\t\tH2[7] = R[0]*DS[2];\n\t\tH2[8] = R[2]*DS[2];\n\t\t\n\t\t// calculate residual\n\t\tF = r + n*l3 - y[0]*H[0] - y[1]*H[1] - y[2]*H[2] - y[3]*H[3] - y[4]*H[4] - y[5]*H[5] - y[6]*H[6] - y[7]*H[7] - y[8]*H[8];\n\n\t\t// residual derivatives\n\t\tF1 = - y[0]*H1[0] - y[1]*H1[1] - y[2]*H1[2] - y[3]*H1[3] - y[4]*H1[4] - y[5]*H1[5] - y[6]*H1[6] - y[7]*H1[7] - y[8]*H1[8];\n\t\tF2 = - y[0]*H2[0] - y[1]*H2[1] - y[2]*H2[2] - y[3]*H2[3] - y[4]*H2[4] - y[5]*H2[5] - y[6]*H2[6] - y[7]*H2[7] - y[8]*H2[8];\n\t\tF3 = n;\n\n\t\t// set up the tangent matrix\n\t\tA[0][0] = F1.x; A[0][1] = F2.x; A[0][2] = F3.x;\n\t\tA[1][0] = F1.y; A[1][1] = F2.y; A[1][2] = F3.y;\n\t\tA[2][0] = F1.z; A[2][1] = F2.z; A[2][2] = F3.z;\n\n\t\t// calculate solution increment\n        mat3d Ai = A.inverse();\n\t\tdx = -(Ai*F);\n\n\t\t// update solution\n\t\tl1 += dx.x;\n\t\tl2 += dx.y;\n\t\tl3 += dx.z;\n\n\t\t++nn;\n\t}\n\twhile ((dx.norm() > 1e-7) && (nn < maxn));\n    if (nn == maxn) return false;\n\n\t// store results\n\trs[0] = l1;\n\trs[1] = l2;\n\tg     = l3;\n    vec3d nu2 = F1 ^ F2;\n    nu2.unit();\n    double cosq = n*nu2;\n    if (cosq > 0) return false;\n\n\t// see if the point is inside the quad\n\tif ((rs[0] >= -1-eps) && (rs[0] <= 1+eps) && \n\t\t(rs[1] >= -1-eps) && (rs[1] <= 1+eps)) return true;\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the intersection of a ray with a 6-node triangle\n//! and returns true if the ray intersected.\n//!\nbool IntersectTri6(vec3d* y, vec3d r, vec3d n, double rs[2], double& g, double eps)\n{\n\t// first we're going to see if the ray intersects the four subtriangles\n\tvec3d x1[3], x2[3], x3[3], x4[3];\n\tx1[0] = y[0]; x2[0] = y[3]; x3[0] = y[5]; x4[0] = y[4];\n\tx1[1] = y[3]; x2[1] = y[1]; x3[1] = y[4]; x4[1] = y[5];\n\tx1[2] = y[5]; x2[2] = y[4]; x3[2] = y[2]; x4[2] = y[3];\n\t\n\tbool b = false;\n\tdouble rp = 0, sp = 0;\n\t\n\tif (IntersectTri(x1, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the first triangle\n\t\tb = true;\n\t\trp = rs[0]/2.0;\n\t\tsp = rs[1]/2.0;\n\t}\n\telse if (IntersectTri(x2, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the second triangle\n\t\tb = true;\n\t\trp = 0.5 + rs[0]/2.0;\n\t\tsp = rs[1]/2.0;\n\t}\n\telse if (IntersectTri(x3, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the third triangle\n\t\tb = true;\n\t\trp = rs[0]/2.0;\n\t\tsp = 0.5 + rs[1]/2.0;\n\t}\n\telse if (IntersectTri(x4, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the fourth triangle\n\t\tb = true;\n\t\trp = 0.5 - rs[0]/2.0;\n\t\tsp = 0.5 - rs[1]/2.0;\n\t}\n\t\n\t// if one of the triangels was intersected,\n\t// we calculate a more accurate projection\n\tif (b)\n\t{\n\t\tmat3d A;\n\t\tvec3d dx;\n\t\tvec3d F, F1, F2, F3;\n\t\tdouble H[6], H1[6], H2[6];\n\t\t\n\t\tdouble l0;\n\t\tdouble l1 = rp;\n\t\tdouble l2 = sp;\n\t\tdouble l3 = g;\n\t\t\n\t\tint nn = 0;\n\t\tint maxn = 10;\n\t\tdo\n\t\t{\n\t\t\tl0 = 1 - l1 - l2;\n\t\t\t\n\t\t\t// shape functions of quad\n\t\t\tH[0] = l0*(2.0*l0 - 1.0);\n\t\t\tH[1] = l1*(2.0*l1 - 1.0);\n\t\t\tH[2] = l2*(2.0*l2 - 1.0);\n\t\t\tH[3] = 4.0*l0*l1;\n\t\t\tH[4] = 4.0*l1*l2;\n\t\t\tH[5] = 4.0*l2*l0;\n\t\t\t\n\t\t\t// shape function derivatives\n\t\t\tH1[0] = -3.0 + 4.0*l1 + 4.0*l2;\n\t\t\tH1[1] =  4.0*l1 - 1.0;\n\t\t\tH1[2] =  0.0;\n\t\t\tH1[3] =  4.0 - 8.0*l1 - 4.0*l2;\n\t\t\tH1[4] =  4.0*l2;\n\t\t\tH1[5] = -4.0*l2;\n\t\t\t\n\t\t\tH2[0] = -3.0 + 4.0*l2 + 4.0*l1;\n\t\t\tH2[1] =  0.0;\n\t\t\tH2[2] =  4.0*l2 - 1.0;\n\t\t\tH2[3] = -4.0*l1;\n\t\t\tH2[4] =  4.0*l1;\n\t\t\tH2[5] =  4.0 - 8.0*l2 - 4.0*l1;\n\n\t\t\t// calculate residual\n\t\t\tF = r + n*l3 - y[0]*H[0] - y[1]*H[1] - y[2]*H[2] - y[3]*H[3] - y[4]*H[4] - y[5]*H[5];\n\t\t\t\n\t\t\t// residual derivatives\n\t\t\tF1 = - y[0]*H1[0] - y[1]*H1[1] - y[2]*H1[2] - y[3]*H1[3] - y[4]*H1[4] - y[5]*H1[5];\n\t\t\tF2 = - y[0]*H2[0] - y[1]*H2[1] - y[2]*H2[2] - y[3]*H2[3] - y[4]*H2[4] - y[5]*H2[5];\n\t\t\tF3 = n;\n\t\t\t\n\t\t\t// set up the tangent matrix\n\t\t\tA[0][0] = F1.x; A[0][1] = F2.x; A[0][2] = F3.x;\n\t\t\tA[1][0] = F1.y; A[1][1] = F2.y; A[1][2] = F3.y;\n\t\t\tA[2][0] = F1.z; A[2][1] = F2.z; A[2][2] = F3.z;\n\t\t\t\n\t\t\t// calculate solution increment\n            mat3d Ai = A.inverse();\n\t\t\tdx = -(Ai*F);\n\t\t\t\n\t\t\t// update solution\n\t\t\tl1 += dx.x;\n\t\t\tl2 += dx.y;\n\t\t\tl3 += dx.z;\n\t\t\t\n\t\t\t++nn;\n\t\t}\n\t\twhile ((dx.norm() > 1e-7) && (nn < maxn));\n        if (nn == maxn) return false;\n\n\t\t// store results\n\t\trs[0] = l1;\n\t\trs[1] = l2;\n\t\tg     = l3;\n        vec3d nu2 = F1 ^ F2;\n        nu2.unit();\n        double cosq = n*nu2;\n        if (cosq > 0) return false;\n\n\t\t// see if the point is inside the quad\n\t\tif ((rs[0] >= -eps) && (rs[1] >= -eps) && (rs[0]+rs[1] <= 1+eps)) return true;\n\t}\n\t\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the intersection of a ray with a 6-node triangle\n//! and returns true if the ray intersected.\n//!\nbool IntersectTri7(vec3d* y, vec3d r, vec3d n, double rs[2], double& g, double eps)\n{\n\t// first we're going to see if the ray intersects the four subtriangles\n\tvec3d x1[3], x2[3], x3[3], x4[3];\n\tx1[0] = y[0]; x2[0] = y[3]; x3[0] = y[5]; x4[0] = y[4];\n\tx1[1] = y[3]; x2[1] = y[1]; x3[1] = y[4]; x4[1] = y[5];\n\tx1[2] = y[5]; x2[2] = y[4]; x3[2] = y[2]; x4[2] = y[3];\n\t\n\tbool b = false;\n\tdouble rp = 0, sp = 0;\n\t\n\tif (IntersectTri(x1, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the first triangle\n\t\tb = true;\n\t\trp = rs[0]/2.0;\n\t\tsp = rs[1]/2.0;\n\t}\n\telse if (IntersectTri(x2, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the second triangle\n\t\tb = true;\n\t\trp = 0.5 + rs[0]/2.0;\n\t\tsp = rs[1]/2.0;\n\t}\n\telse if (IntersectTri(x3, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the third triangle\n\t\tb = true;\n\t\trp = rs[0]/2.0;\n\t\tsp = 0.5 + rs[1]/2.0;\n\t}\n\telse if (IntersectTri(x4, r, n, rs, g, eps))\n\t{\n\t\t// we've intersected the fourth triangle\n\t\tb = true;\n\t\trp = 0.5 - rs[0]/2.0;\n\t\tsp = 0.5 - rs[1]/2.0;\n\t}\n\t\n\t// if one of the triangels was intersected,\n\t// we calculate a more accurate projection\n\tif (b)\n\t{\n\t\tmat3d A;\n\t\tvec3d dx;\n\t\tvec3d F, F1, F2, F3;\n\t\tdouble H[7], H1[7], H2[7];\n\t\t\n\t\tdouble l0;\n\t\tdouble l1 = rp;\n\t\tdouble l2 = sp;\n\t\tdouble l3 = g;\n\t\t\n\t\tint nn = 0;\n\t\tint maxn = 5;\n\t\tdo\n\t\t{\n\t\t\tl0 = 1 - l1 - l2;\n\t\t\t\n\t\t\tH[6] = 27.0*l0*l1*l2;\n\t\t\tH[0] = l0*(2.0*l0 - 1.0) + H[6]/9.0;\n\t\t\tH[1] = l1*(2.0*l1 - 1.0) + H[6]/9.0;\n\t\t\tH[2] = l2*(2.0*l2 - 1.0) + H[6]/9.0;\n\t\t\tH[3] = 4.0*l0*l1 - 4.0*H[6]/9.0;\n\t\t\tH[4] = 4.0*l1*l2 - 4.0*H[6]/9.0;\n\t\t\tH[5] = 4.0*l2*l0 - 4.0*H[6]/9.0;\n\n\t\t\t\n\t\t\t// shape function derivatives\n\t\t\tH1[6] = 27.0*l2*(1.0 - 2.0*l1 - l2);\n\t\t\tH1[0] = -3.0 + 4.0*l1 + 4.0*l2 +     H1[6]/9.0;\n\t\t\tH1[1] =  4.0*l1 - 1.0          +     H1[6]/9.0;\n\t\t\tH1[2] =  0.0                   +     H1[6]/9.0;\n\t\t\tH1[3] =  4.0 - 8.0*l1 - 4.0*l2 - 4.0*H1[6]/9.0;\n\t\t\tH1[4] =  4.0*l2                - 4.0*H1[6]/9.0;\n\t\t\tH1[5] = -4.0*l2                - 4.0*H1[6]/9.0;\n\n\t\t\tH2[6] = 27.0*l1*(1.0 - l1 - 2.0*l2);\n\t\t\tH2[0] = -3.0 + 4.0*l2 + 4.0*l1     + H2[6]/9.0;\n\t\t\tH2[1] =  0.0                       + H2[6]/9.0;\n\t\t\tH2[2] =  4.0*l2 - 1.0              + H2[6]/9.0;\n\t\t\tH2[3] = -4.0*l1                - 4.0*H2[6]/9.0;\n\t\t\tH2[4] =  4.0*l1                - 4.0*H2[6]/9.0;\n\t\t\tH2[5] =  4.0 - 8.0*l2 - 4.0*l1 - 4.0*H2[6]/9.0;\n\n\t\t\t// calculate residual\n\t\t\tF = r + n*l3 - y[0]*H[0] - y[1]*H[1] - y[2]*H[2] - y[3]*H[3] - y[4]*H[4] - y[5]*H[5] - y[6]*H[6];\n\t\t\t\n\t\t\t// residual derivatives\n\t\t\tF1 = - y[0]*H1[0] - y[1]*H1[1] - y[2]*H1[2] - y[3]*H1[3] - y[4]*H1[4] - y[5]*H1[5] - y[6]*H1[6];\n\t\t\tF2 = - y[0]*H2[0] - y[1]*H2[1] - y[2]*H2[2] - y[3]*H2[3] - y[4]*H2[4] - y[5]*H2[5] - y[6]*H2[6];\n\t\t\tF3 = n;\n\t\t\t\n\t\t\t// set up the tangent matrix\n\t\t\tA[0][0] = F1.x; A[0][1] = F2.x; A[0][2] = F3.x;\n\t\t\tA[1][0] = F1.y; A[1][1] = F2.y; A[1][2] = F3.y;\n\t\t\tA[2][0] = F1.z; A[2][1] = F2.z; A[2][2] = F3.z;\n\t\t\t\n\t\t\t// calculate solution increment\n            mat3d Ai = A.inverse();\n\t\t\tdx = -(Ai*F);\n\t\t\t\n\t\t\t// update solution\n\t\t\tl1 += dx.x;\n\t\t\tl2 += dx.y;\n\t\t\tl3 += dx.z;\n\t\t\t\n\t\t\t++nn;\n\t\t}\n\t\twhile ((dx.norm() > 1e-7) && (nn < maxn));\n        if (nn == maxn) return false;\n\n\t\t// store results\n\t\trs[0] = l1;\n\t\trs[1] = l2;\n\t\tg     = l3;\n        vec3d nu2 = F1 ^ F2;\n        nu2.unit();\n        double cosq = n*nu2;\n        if (cosq > 0) return false;\n\n\t\t// see if the point is inside the quad\n\t\tif ((rs[0] >= -eps) && (rs[1] >= -eps) && (rs[0]+rs[1] <= 1+eps)) return true;\n\t}\n\t\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::Invert()\n{\n\tForEachSurfaceElement([](FESurfaceElement& el) {\n\t\tint tmp;\n\t\tswitch (el.Shape())\n\t\t{\n\t\tcase ET_TRI3 : tmp = el.m_node[1]; el.m_node[1] = el.m_node[2]; el.m_node[2] = tmp; break;\n\t\tcase ET_QUAD4: tmp = el.m_node[1]; el.m_node[1] = el.m_node[3]; el.m_node[3] = tmp; break;\n\t\tcase ET_QUAD8:\n\t\tcase ET_QUAD9:\n\t\t\ttmp = el.m_node[1]; el.m_node[1] = el.m_node[3]; el.m_node[3] = tmp;\n\t\t\ttmp = el.m_node[4]; el.m_node[4] = el.m_node[7]; el.m_node[7] = tmp;\n\t\t\ttmp = el.m_node[5]; el.m_node[5] = el.m_node[6]; el.m_node[6] = tmp;\n\t\t\tbreak;\n\t\tcase ET_TRI6:\n\t\tcase ET_TRI7:\n\t\t\ttmp = el.m_node[1]; el.m_node[1] = el.m_node[2]; el.m_node[2] = tmp;\n\t\t\ttmp = el.m_node[3]; el.m_node[3] = el.m_node[5]; el.m_node[5] = tmp;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t}\n\t});\n}\n\n//-----------------------------------------------------------------------------\n//! This function calculates the intersection of a ray with a surface element.\n//! It simply calls the correct intersection function based on the type\n//! of element.\n//!\nbool FESurface::Intersect(FESurfaceElement& el, vec3d r, vec3d n, double rs[2], double& g, double eps, bool checkNormal)\n{\n\tint N = el.Nodes();\n\n\t// get the element nodes\n\tFEMesh& mesh = *m_pMesh;\n\tvec3d y[FEElement::MAX_NODES];\n    if (!m_bshellb) for (int i=0; i<N; ++i) y[i] = mesh.Node(el.m_node[i]).m_rt;\n    else for (int i=0; i<N; ++i) y[i] = mesh.Node(el.m_node[i]).st();\n\n\t// call the correct intersection function\n\tswitch (N)\n\t{\n\tcase 3: return IntersectTri  (y, r, n, rs, g, eps); break;\n\tcase 4: return IntersectQuad (y, r, n, rs, g, eps, checkNormal); break;\n\tcase 6: return IntersectTri6 (y, r, n, rs, g, eps); break;\n\tcase 7: return IntersectTri7 (y, r, n, rs, g, eps); break;\n\tcase 8: return IntersectQuad8(y, r, n, rs, g, eps); break;\n\tcase 9: return IntersectQuad9(y, r, n, rs, g, eps); break;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\t// if we get here, the ray did not intersect the element\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::Serialize(DumpStream &ar)\n{\n\tFEMeshPartition::Serialize(ar);\n\tif (ar.IsShallow() == false)\n\t{\n\t\tar & m_surf;\n\t\tar & m_bitfc;\n\t\tar & m_alpha;\n\t\tar & m_bshellb;\n\t\tar & m_el;\n\n\t\t// reallocate integration point data on loading\n\t\tif (ar.IsSaving() == false)\n\t\t{\n\t\t\tfor (int i = 0; i < Elements(); ++i)\n\t\t\t{\n\t\t\t\tFESurfaceElement& el = Element(i);\n\t\t\t\tel.SetMeshPartition(this);\n\t\t\t\tint nint = el.GaussPoints();\n\t\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t\t{\n\t\t\t\t\tFESurfaceMaterialPoint* pt = dynamic_cast<FESurfaceMaterialPoint*>(CreateMaterialPoint());\n\t\t\t\t\tassert(pt);\n\t\t\t\t\tel.SetMaterialPointData(pt, n);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif ((ar.IsShallow() == false) && (ar.IsSaving() == false))\n\t{\n\t\t// see if we can find all elements that the faces belong to\n\t\tint ne = Elements();\n\t\tfor (int i = 0; i<ne; ++i)\n\t\t{\n\t\t\tFESurfaceElement& el = Element(i);\n\t\t\tif (m_bitfc && (el.m_elem[0].pe == nullptr)) FindElements(el);\n\t\t\telse if (el.m_elem[0].pe == nullptr) el.m_elem[0] = FindElement(el);\n\t\t\t//to make sure\n\t\t\telse if (m_bitfc && (el.m_elem[1].pe == nullptr)) FindElements(el);\n\t\t\tassert(el.m_elem[0].pe != nullptr);\n\t\t}\n\t}\n\n\t// serialize material point data\n\tfor (int i = 0; i < Elements(); ++i)\n\t{\n\t\tFESurfaceElement& el = Element(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint* pt = dynamic_cast<FESurfaceMaterialPoint*>(el.GetMaterialPoint(n));\n\t\t\tassert(pt);\n\t\t\tpt->Serialize(ar);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::GetNodalCoordinates(FESurfaceElement& el, vec3d* rt)\n{\n\tFEMesh& mesh = *GetMesh();\n\tint neln = el.Nodes();\n    if (!m_bshellb) for (int j = 0; j < neln; ++j) rt[j] = mesh.Node(el.m_node[j]).m_rt;\n    else for (int j = 0; j < neln; ++j) rt[j] = mesh.Node(el.m_node[j]).st();\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::GetReferenceNodalCoordinates(FESurfaceElement& el, vec3d* r0)\n{\n\tFEMesh& mesh = *GetMesh();\n\tint neln = el.Nodes();\n    if (!m_bshellb) for (int j = 0; j < neln; ++j) r0[j] = mesh.Node(el.m_node[j]).m_r0;\n    else for (int j = 0; j < neln; ++j) r0[j] = mesh.Node(el.m_node[j]).s0();\n}\n\n//-----------------------------------------------------------------------------\n// Get current coordinates at intermediate configuration\nvoid FESurface::GetNodalCoordinates(FESurfaceElement& el, double alpha, vec3d* rt)\n{\n\tint neln = el.Nodes();\n\tfor (int j = 0; j<neln; ++j) {\n\t\tFENode& node = Node(el.m_lnode[j]);\n\t\trt[j] = node.m_rt*alpha + node.m_rp*(1.0 - alpha);\n        if (m_bshellb) rt[j] -= node.m_dt*alpha + node.m_dp*(1.0 - alpha);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate field variables\ndouble FESurface::Evaluate(FESurfaceMaterialPoint& mp, int dof)\n{\n\tdouble v[FEElement::MAX_NODES];\n\n\tFESurfaceElement& el = *mp.SurfaceElement();\n\tint neln = el.Nodes();\n\tfor (int j = 0; j < neln; ++j) v[j] = Node(el.m_lnode[j]).get(dof);\n\n\tdouble s = el.eval(v, mp.m_index);\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n// Evaluate field variables\ndouble FESurface::Evaluate(int nface, int dof)\n{\n    double v = 0;\n\n    FESurfaceElement& el = Element(nface);\n    int neln = el.Nodes();\n    for (int j = 0; j < neln; ++j) v += Node(el.m_lnode[j]).get(dof);\n\n    return v/neln;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::LoadVector(FEGlobalVector& R, const FEDofList& dofList, bool breference, FESurfaceVectorIntegrand f)\n{\n\tint dofPerNode = dofList.Size();\n\tint order = (dofPerNode == 1 ? dofList.InterpolationOrder(0) : -1);\n\n\tint NE = Elements();\n\t#pragma omp parallel for shared(R, dofList, f)\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tvector<double> fe;\n\t\tvector<int> lm;\n\t\tvec3d re[FEElement::MAX_NODES];\n\t\tstd::vector<double> G(dofPerNode, 0.0);\n\n\t\t// get the next element\n\t\tFESurfaceElement& el = Element(i);\n\n\t\t// init the element vector\n\t\tint neln = el.ShapeFunctions(order);\n\t\tint ndof = dofPerNode * neln;\n\t\tfe.assign(ndof, 0.0);\n\n\t\t// get the nodal coordinates\n\t\tif (breference)\n\t\t\tGetReferenceNodalCoordinates(el, re);\n\t\telse\n\t\t\tGetNodalCoordinates(el, re);\n\n\t\t// calculate element vector\n\t\tFESurfaceDofShape dof_a;\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint& pt = static_cast<FESurfaceMaterialPoint&>(*el.GetMaterialPoint(n));\n\n\t\t\t// kinematics at integration points\n\t\t\tpt.dxr = el.eval_deriv1(re, n);\n\t\t\tpt.dxs = el.eval_deriv2(re, n);\n\n\t\t\tpt.m_shape = el.H(n);\n\n\t\t\tdouble* H = el.H(order, n);\n\t\t\tdouble* Hr = el.Gr(order, n);\n\t\t\tdouble* Hs = el.Gr(order, n);\n\n\t\t\t// put it all together\n\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t{\n\t\t\t\t// shape function and derivatives\n\t\t\t\tdof_a.index = j;\n\t\t\t\tdof_a.shape = H[j];\n\t\t\t\tdof_a.shape_deriv_r = Hr[j];\n\t\t\t\tdof_a.shape_deriv_s = Hs[j];\n\n\t\t\t\t// evaluate the integrand\n\t\t\t\tf(pt, dof_a, G);\n\n\t\t\t\tfor (int k = 0; k < dofPerNode; ++k)\n\t\t\t\t{\n\t\t\t\t\tfe[dofPerNode * j + k] += G[k] * w[n];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// get the corresponding LM vector\n\t\tUnpackLM(el, dofList, lm);\n\n\t\t// Assemble into global vector\n\t\tR.Assemble(el.m_node, lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurface::LoadStiffness(FELinearSystem& LS, const FEDofList& dofList_a, const FEDofList& dofList_b, FESurfaceMatrixIntegrand f)\n{\n\tFEElementMatrix ke;\n\n\tint dofPerNode_a = dofList_a.Size();\n\tint dofPerNode_b = dofList_b.Size();\n\n\tint order_a = (dofPerNode_a == 1 ? dofList_a.InterpolationOrder(0) : -1);\n\tint order_b = (dofPerNode_b == 1 ? dofList_b.InterpolationOrder(0) : -1);\n\n\tvec3d rt[FEElement::MAX_NODES];\n\n\tmatrix kab(dofPerNode_a, dofPerNode_b);\n\tFESurfaceDofShape dof_a, dof_b;\n\n\tint NE = Elements();\n\tfor (int m = 0; m<NE; ++m)\n\t{\n\t\t// get the surface element\n\t\tFESurfaceElement& el = Element(m);\n\n\t\tke.SetNodes(el.m_node);\n\n\t\t// shape functions\n\t\tint neln = el.Nodes();\n\t\tint nn_a = el.ShapeFunctions(dofPerNode_a);\n\t\tint nn_b = el.ShapeFunctions(dofPerNode_b);\n\n\t\t// get the element stiffness matrix\n\t\tint ndof_a = dofPerNode_a * nn_a;\n\t\tint ndof_b = dofPerNode_b * nn_b;\n\t\tke.resize(ndof_a, ndof_b);\n\n\t\t// calculate element stiffness\n\t\tint nint = el.GaussPoints();\n\n\t\t// gauss weights\n\t\tdouble* w = el.GaussWeights();\n\n\t\t// nodal coordinates\n\t\tGetNodalCoordinates(el, rt);\n\n\t\t// repeat over integration points\n\t\tke.zero();\n\t\tfor (int n = 0; n<nint; ++n)\n\t\t{\n\t\t\tFESurfaceMaterialPoint& pt = static_cast<FESurfaceMaterialPoint&>(*el.GetMaterialPoint(n));\n\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\n\t\t\t// tangents at integration point\n\t\t\tpt.dxr = vec3d(0, 0, 0);\n\t\t\tpt.dxs = vec3d(0, 0, 0);\n\t\t\tfor (int i = 0; i<neln; ++i)\n\t\t\t{\n\t\t\t\tpt.dxr += rt[i] * Gr[i];\n\t\t\t\tpt.dxs += rt[i] * Gs[i];\n\t\t\t}\n\n\t\t\t// calculate stiffness component\n\t\t\tfor (int i = 0; i < nn_a; ++i)\n\t\t\t{\n\t\t\t\t// shape function values\n\t\t\t\tdof_a.index = i;\n\t\t\t\tdof_a.shape = el.H(order_a, n)[i];\n\t\t\t\tdof_a.shape_deriv_r = el.Gr(order_a, n)[i];\n\t\t\t\tdof_a.shape_deriv_s = el.Gs(order_a, n)[i];\n\n\t\t\t\tfor (int j = 0; j < nn_b; ++j)\n\t\t\t\t{\n\t\t\t\t\t// shape function values\n\t\t\t\t\tdof_b.index = j;\n\t\t\t\t\tdof_b.shape = el.H(order_b, n)[j];\n\t\t\t\t\tdof_b.shape_deriv_r = el.Gr(order_b, n)[j];\n\t\t\t\t\tdof_b.shape_deriv_s = el.Gs(order_b, n)[j];\n\n\t\t\t\t\t// evaluate integrand\n\t\t\t\t\tkab.zero();\n\t\t\t\t\tf(pt, dof_a, dof_b, kab);\n\n\t\t\t\t\t// add it to the local element matrix\n\t\t\t\t\tke.adds(dofPerNode_a * i, dofPerNode_b * j, kab, w[n]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// get the element's LM vector\n\t\tstd::vector<int>& lma = ke.RowIndices();\n\t\tstd::vector<int>& lmb = ke.ColumnsIndices();\n\t\tUnpackLM(el, dofList_a, lma);\n\t\tUnpackLM(el, dofList_b, lmb);\n\n\t\t// assemble element matrix in global stiffness matrix\n\t\tLS.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// project FEParamDouble pd to nodes and store nodal values in vector<double> d\nvoid FESurface::ProjectToNodes(FEParamDouble& pd, std::vector<double>& d)\n{\n    d.assign(Nodes(), 0.0);\n    std::vector<int> nd(Nodes(),0);\n    for (int i=0; i<Elements(); ++i) {\n        FESurfaceElement& el = Element(i);\n        double ei[FEElement::MAX_INTPOINTS];\n        for (int j=0; j<el.GaussPoints(); ++j) {\n            FEMaterialPoint* pt = el.GetMaterialPoint(j);\n            ei[j] = pd(*pt);\n        }\n        double en[FEElement::MAX_NODES];\n        el.project_to_nodes(ei, en);\n        for (int j=0; j<el.Nodes(); ++j) {\n            d[el.m_lnode[j]] += en[j];\n            ++nd[el.m_lnode[j]];\n        }\n    }\n    \n    // evaluate average\n    for (int i=0; i<Nodes(); ++i)\n        if (nd[i]) d[i] /= nd[i];\n}\n\nFECORE_API double CalculateSurfaceVolume(FESurface& s)\n{\n\t// get the mesh\n\tFEMesh& mesh = *s.GetMesh();\n\n\t// loop over all elements\n\tdouble vol = 0.0;\n\tint NE = s.Elements();\n\tvec3d x[FEElement::MAX_NODES];\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\t// get the next element\n\t\tFESurfaceElement& el = s.Element(i);\n\n\t\t// get the nodal coordinates\n\t\tint neln = el.Nodes();\n\t\tfor (int j = 0; j < neln; ++j) x[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t// loop over integration points\n\t\tdouble* w = el.GaussWeights();\n\t\tint nint = el.GaussPoints();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\t// evaluate the position vector at this point\n\t\t\tvec3d r = el.eval(x, n);\n\n\t\t\t// calculate the tangent vectors\n\t\t\tdouble* Gr = el.Gr(n);\n\t\t\tdouble* Gs = el.Gs(n);\n\t\t\tvec3d dxr(0, 0, 0), dxs(0, 0, 0);\n\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t{\n\t\t\t\tdxr += x[j] * Gr[j];\n\t\t\t\tdxs += x[j] * Gs[j];\n\t\t\t}\n\n\t\t\t// update volume\n\t\t\tvol += w[n] * (r * (dxr ^ dxs));\n\t\t}\n\t}\n\treturn vol / 3.0;\n}\n"
  },
  {
    "path": "FECore/FESurface.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"mat2d.h\"\n#include \"vec2d.h\"\n#include \"matrix.h\"\n#include \"FEMeshPartition.h\"\n#include \"FENodeSet.h\"\n#include \"FEDofList.h\"\n#include \"FESurfaceElement.h\"\n#include \"FENode.h\"\n\n//-----------------------------------------------------------------------------\nclass FEMesh;\nclass FENodeSet;\nclass FEFacetSet;\nclass FELinearSystem;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FESurfaceMaterialPoint : public FEMaterialPoint\n{\npublic:\n\tvec3d\tdxr, dxs;\t\t// tangent vectors at material point\n    vec3d   m_rp;\n\n\t// return the surface element\n\tFESurfaceElement* SurfaceElement() { return (FESurfaceElement*)m_elem; }\n\n    void Update(const FETimeInfo& tp) override\n    {\n        m_rp = m_rt;\n    }\n    \n\tvoid Serialize(DumpStream& ar) override\n\t{\n\t\tFEMaterialPoint::Serialize(ar);\n\t\tar & dxr & dxs;\n\t}\n};\n\n// helper class for describing shape functions at dofs in integration routines\nstruct FECORE_API FESurfaceDofShape\n{\n\tint\t\t\tindex;\t\t\t// local dof index\n\tdouble\t\tshape;\t\t\t// shape functions\n\tdouble\t\tshape_deriv_r;\t// shape function r-derivative\n\tdouble\t\tshape_deriv_s;\t// shape function s-derivative\n};\n\n//-----------------------------------------------------------------------------\n// This typedef defines a surface integrand. \n// It evaluates the function at surface material point mp, and returns the value\n// it the val vector. The size of the vector is determined by the field variable\n// that is being integrated and is already set when the integrand is called.\n// This is used in the FESurface::LoadVector function.\ntypedef std::function<void(FESurfaceMaterialPoint& mp, const FESurfaceDofShape& node_a, std::vector<double>& val)> FESurfaceVectorIntegrand;\n\ntypedef std::function<void(FESurfaceMaterialPoint& mp, const FESurfaceDofShape& node_a, const FESurfaceDofShape& node_b, matrix& val)> FESurfaceMatrixIntegrand;\n\n//-----------------------------------------------------------------------------\n//! Surface mesh\n\n//! This class implements the basic functionality for an FE surface.\n//! More specialized surfaces are derived from this class\n\nclass FECORE_API FESurface : public FEMeshPartition\n{\n\tFECORE_SUPER_CLASS(FESURFACE_ID)\n\tFECORE_BASE_CLASS(FESurface)\n\npublic:\n\t//! default constructor\n\tFESurface(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~FESurface();\n\n\t//! initialize surface data structure\n\tbool Init() override;\n\t\n\tvirtual void InitSurface();\n    \n\t//! creates surface\n\tvoid Create(int nsize, int elemType = -1);\n\n\t//! Build a surface from a facet set\n\tvoid Create(const FEFacetSet& set);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! unpack an LM vector from a dof list\n\tvoid UnpackLM(const FESurfaceElement& el, const FEDofList& dofList, vector<int>& lm);\n\t\n\t//! Extract a node set from this surface\n\tFENodeList GetNodeList();\n\n\t//! Get a list of bools that indicate whether the corresponding node is on the boundary\n\t// TODO: Move to MeshPartition\n\tvoid GetBoundaryFlags(std::vector<bool>& boundary) const;\n    \n    //! Set alpha parameter for intermediate time\n    void SetAlpha(const double alpha) { m_alpha = alpha; }\n\npublic:\n\n\t//! return number of surface elements\n\tint Elements() const override { return (int)m_el.size(); }\n\n\t//! return an element of the surface\n\tFESurfaceElement& Element(int i) { return m_el[i]; }\n\n\t//! return an element of the surface\n\tconst FESurfaceElement& Element(int i) const { return m_el[i]; }\n\n\t//! returns reference to element\n\tFEElement& ElementRef(int n) override { return m_el[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_el[n]; }\n\n\t//! find the solid or shell element of a surface element\n\tFESurfaceElement::ELEMENT_REF FindElement(FESurfaceElement& el);\n\n    //! for interface surfaces, find the index of both solid elements\n    //! on either side of the interface\n    void FindElements(FESurfaceElement& el);\n\n\t//! loop over all elements\n\tvoid ForEachSurfaceElement(std::function<void(FESurfaceElement& el)> f);\n\npublic:\n\t// Create material point data for this surface\n\tvirtual FEMaterialPoint* CreateMaterialPoint();\n\n\t// update surface data\n\tvoid Update(const FETimeInfo& tp) override;\n\npublic:\n\n\t//! Project a node onto a surface element\n\tvec3d ProjectToSurface(FESurfaceElement& el, vec3d x, double& r, double& s);\n\n\t//! check to see if a point is on element\n\tbool IsInsideElement(FESurfaceElement& el, double r, double s, double tol = 0);\n\n\t//! See if a ray intersects an element\n\tbool Intersect(FESurfaceElement& el, vec3d r, vec3d n, double rs[2], double& g, double eps, bool checkNormal = true);\n\n\t//! Invert the surface\n\tvoid Invert();\n\n\t//! Get the spatial position given natural coordinates\n\tvec3d Position(FESurfaceElement& el, double r, double s);\n\n    //! Get the spatial position of an integration point\n    vec3d Position(FESurfaceElement& el, int n);\n    \n\t//! Get the nodal coordinates of an element\n\tvoid NodalCoordinates(FESurfaceElement& el, vec3d* re);\n    \n    //! Get the nodal coordinates of an element at previoust time\n    void PreviousNodalCoordinates(FESurfaceElement& el, vec3d* re);\n    \n    //! Determine if a face on this surface is pointing away or into a specified element\n    double FacePointing(FESurfaceElement& se, FEElement& el);\n    \n    //! Project a FEParamDouble to nodes of the surface\n    void ProjectToNodes(FEParamDouble& pd, std::vector<double>& d);\n    \n\npublic:\n\t//! calculate the reference surface area of a surface element\n\tdouble FaceArea(FESurfaceElement& el);\n\n\t//! calculate the current surface area of a surface element\n\tdouble CurrentFaceArea(FESurfaceElement& el);\n\n\t//! return the max element size\n\tdouble MaxElementSize();\n\n\t//! calculate the metric tensor in the current configuration\n\tmat2d Metric(FESurfaceElement& el, double r, double s);\n\n    //! calculate the metric tensor at an integration point\n    mat2d Metric(const FESurfaceElement& el, int n) const;\n    \n    //! calculate the metric tensor at an integration point at previous time\n    mat2d MetricP(FESurfaceElement& el, int n);\n    \n\t//! calculate the metric tensor in the reference configuration\n\tmat2d Metric0(FESurfaceElement& el, double r, double s);\n\n\t//! calculate the surface normal\n\tvec3d SurfaceNormal(FESurfaceElement& el, double r, double s) const;\n\n\t//! calculate the surface normal at an integration point\n\tvec3d SurfaceNormal(const FESurfaceElement& el, int n) const;\n\n    //! calculate the nodal normals\n    void UpdateNodeNormals();\n    \n    //! return the nodal normals\n    vec3d NodeNormal(const int inode) { return m_nn[inode]; }\n    \n    //! calculate the global position of an integration point\n    vec3d Local2Global0(FESurfaceElement& el, int n);\n    \n\t//! calculate the global position of a point on the surface\n\tvec3d Local2Global(FESurfaceElement& el, double r, double s);\n\n\t//! calculate the global position of an integration point\n\tvec3d Local2Global(FESurfaceElement& el, int n);\n\n    //! calculate the global position of a point on the surface at previous time\n    vec3d Local2GlobalP(FESurfaceElement& el, double r, double s);\n    \n    //! calculate the global position of an integration point at previous time\n    vec3d Local2GlobalP(FESurfaceElement& el, int n);\n    \n\t//! calculates the covariant base vectors of a surface at an integration point\n\tvoid CoBaseVectors(const FESurfaceElement& el, int j, vec3d t[2]) const;\n\n\t//! calculates the covariant base vectors of a surface\n\tvoid CoBaseVectors(FESurfaceElement& el, double r, double s, vec3d t[2]);\n\n    //! calculates the covariant base vectors of a surface at an integration point in the reference configuration\n    void CoBaseVectors0(const FESurfaceElement& el, int j, vec3d t[2]) const;\n    \n\t//! calculates covariant base vectors of a surface\n\tvoid CoBaseVectors0(FESurfaceElement& el, double r, double s, vec3d t[2]);\n\n    //! calculates the covariant base vectors of a surface at an integration point at previoust time step\n    void CoBaseVectorsP(FESurfaceElement& el, int j, vec3d t[2]);\n    \n    //! calculates contravariant base vectors of a surface  at an integration point\n    void ContraBaseVectors(const FESurfaceElement& el, int j, vec3d t[2]) const;\n    \n    //! calculates the contravariant base vectors of a surface at an integration point at previoust time step\n    void ContraBaseVectorsP(FESurfaceElement& el, int j, vec3d t[2]);\n    \n    //! calculates the parametric derivatives of covariant basis of a surface  at an integration point\n    void CoBaseVectorDerivatives(const FESurfaceElement& el, int j, vec3d dg[2][2]) const;\n    \n    //! calculates the the parametric derivatives of covariant basis of a surface at an integration point at previoust time step\n    void CoBaseVectorDerivativesP(FESurfaceElement& el, int j, vec3d dg[2][2]);\n    \n\t//! calculates contravariant base vectors of a surface\n\tvoid ContraBaseVectors(FESurfaceElement& el, double r, double s, vec3d t[2]);\n\n\t//! calculates contravariant base vectors of a surface\n\tvoid ContraBaseVectors0(FESurfaceElement& el, double r, double s, vec3d t[2]);\n\n\t//! Jacobian in reference configuration for integration point n\n\tdouble jac0(FESurfaceElement& el, int n);\n\n\t//! Jacobian in reference configuration for integration point n (and returns normal)\n\tdouble jac0(const FESurfaceElement& el, int n, vec3d& nu);\n\n    //! Interface status\n    void SetInterfaceStatus(const bool bitfc) { m_bitfc = bitfc; }\n    bool GetInterfaceStatus() { return m_bitfc; }\n\n\t//! Get the facet set that created this surface\n\tFEFacetSet* GetFacetSet() { return m_surf; }\n\npublic:\n\t// Get nodal reference coordinates \n\tvoid GetReferenceNodalCoordinates(FESurfaceElement& el, vec3d* r0);\n\n\t// Get current coordinates\n\tvoid GetNodalCoordinates(FESurfaceElement& el, vec3d* rt);\n\n\t// Get current coordinates at intermediate configuration\n\tvoid GetNodalCoordinates(FESurfaceElement& el, double alpha, vec3d* rt);\n\n\t// Get the shell bottom flag\n\tbool IsShellBottom() const { return m_bshellb; }\n\n\t// Set the shell bottom flag\n\tvoid SetShellBottom(bool b) { m_bshellb = b; }\n\npublic:\n\t// Evaluate field variables\n\tdouble Evaluate(FESurfaceMaterialPoint& mp, int dof);\n    double Evaluate(int nface, int dof);\n\npublic:\n\t//! Evaluate a load vector. \n\tvirtual void LoadVector(\n\t\tFEGlobalVector& R,\t\t\t\t// The global vector into which the loads are assembled\n\t\tconst FEDofList& dofList,\t\t// The degree of freedom list\n\t\tbool breference,\t\t\t\t// integrate over reference (true) or current (false) configuration\n\t\tFESurfaceVectorIntegrand f);\t// the function that evaluates the integrand\n\n\t//! Evaluate the stiffness matrix of a load\n\tvirtual void LoadStiffness(\n\t\tFELinearSystem& LS,\t\t\t// The linear system does the assembling\n\t\tconst FEDofList& dofList_a,\t// The degree of freedom list of node a\n\t\tconst FEDofList& dofList_b,\t// The degree of freedom list of node b\n\t\tFESurfaceMatrixIntegrand f\t// the matrix function to evaluate\n\t);\n\npublic:\n\tvoid CreateMaterialPointData();\n    \nprotected:\n\tFEFacetSet*\t\t\t\t\tm_surf;\t\t//!< the facet set from which this surface is built\n\tvector<FESurfaceElement>\tm_el;\t\t//!< surface elements\n    vector<vec3d>               m_nn;       //!< node normals\n    bool                        m_bitfc;    //!< interface status\n    double                      m_alpha;    //!< intermediate time fraction\n\tbool\t\t\t\t\t\tm_bshellb;\t//!< true if this surface is the bottom of a shell domain\n};\n\n// Calculates the volume inside a (closed) surface. \nFECORE_API double CalculateSurfaceVolume(FESurface& s);\n"
  },
  {
    "path": "FECore/FESurfaceBC.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESurfaceBC.h\"\n#include \"FESurface.h\"\n\nFESurfaceBC::FESurfaceBC(FEModel* fem) : FEBoundaryCondition(fem)\n{\n\tm_surface = nullptr;\n}\n\nvoid FESurfaceBC::SetSurface(FESurface* surface)\n{\n\tm_surface = surface;\n}\n\nFESurface* FESurfaceBC::GetSurface()\n{\n\treturn m_surface;\n}\n\nbool FESurfaceBC::Init()\n{\n\tif (m_surface == nullptr) return false;\n\tif (m_surface->Init() == false) return false;\n\treturn FEBoundaryCondition::Init();\n}\n"
  },
  {
    "path": "FECore/FESurfaceBC.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n\n#include \"FEBoundaryCondition.h\"\n#include \"fecore_api.h\"\n\nclass FESurface;\n\nclass FECORE_API FESurfaceBC : public FEBoundaryCondition\n{\n\tFECORE_BASE_CLASS(FESurfaceBC)\n\npublic:\n\tFESurfaceBC(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid SetSurface(FESurface* surface);\n\n\tFESurface* GetSurface();\n\nprivate:\n\tFESurface* m_surface;\n};\n"
  },
  {
    "path": "FECore/FESurfaceConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfaceConstraint.h\"\n\nFESurfaceConstraint::FESurfaceConstraint(FEModel* fem) : FENLConstraint(fem)\n{\n}\n"
  },
  {
    "path": "FECore/FESurfaceConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENLConstraint.h\"\n\nclass FESurface;\n\n// Base class for nonlinear constraints that are defined using a surface.\nclass FECORE_API FESurfaceConstraint : public FENLConstraint\n{\n\tFECORE_BASE_CLASS(FESurfaceConstraint)\n\npublic:\n\tFESurfaceConstraint(FEModel* fem);\n\n\t// return the surface\n\tvirtual FESurface* GetSurface() { return 0; }\n\n\t// we need an integration rule for all surfaces. \n\t// By default, this was always assumed to be a nodal integration rule\n\t// but this is not always desired, so derived classes can override this\n\tvirtual bool UseNodalIntegration() { return true; };\n};\n"
  },
  {
    "path": "FECore/FESurfaceElement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESurfaceElement.h\"\n#include \"DumpStream.h\"\n\n//=================================================================================================\n// FESurfaceElement\n//=================================================================================================\n\n//-----------------------------------------------------------------------------\nFESurfaceElement::FESurfaceElement()\n{\n\tm_lid = -1;\n}\n\nFESurfaceElement::FESurfaceElement(const FESurfaceElement& el) : FEElement(el)\n{\n\t// set the traits of the element\n\tif (el.m_pT) SetTraits(el.m_pT);\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\tm_status = el.m_status;\n\n\t// copy surface element data\n\tm_lid = el.m_lid;\n\tm_elem[0] = el.m_elem[0];\n\tm_elem[1] = el.m_elem[1];\n}\n\nFESurfaceElement& FESurfaceElement::operator = (const FESurfaceElement& el)\n{\n\t// make sure the element type is the same\n\tif (m_pT == 0) SetTraits(el.m_pT);\n\telse assert(m_pT == el.m_pT);\n\n\t// copy base class data\n\tm_mat = el.m_mat;\n\tm_nID = el.m_nID;\n\tm_lid = el.m_lid;\n\tm_node = el.m_node;\n\tm_lnode = el.m_lnode;\n\tm_lm = el.m_lm;\n\tm_val = el.m_val;\n\n\t// copy surface element data\n\tm_lid = el.m_lid;\n\tm_elem[0] = el.m_elem[0];\n\tm_elem[1] = el.m_elem[1];\n\n\treturn (*this);\n}\n\nvoid FESurfaceElement::SetTraits(FEElementTraits* pt)\n{\n\tm_pT = pt;\n\tm_node.resize(Nodes());\n\tm_lnode.resize(Nodes());\n\tm_State.Create(GaussPoints());\n}\n\nint FESurfaceElement::facet_edges() const\n{\n\tint nn = Nodes(), nf = 0;\n\tswitch (nn)\n\t{\n\tcase 3:\n\tcase 6:\n\tcase 7:\n\t\tnf = 3;\n\t\tbreak;\n\tcase 4:\n\tcase 8:\n\tcase 9:\n\t\tnf = 4;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t}\n\treturn nf;\n}\n\nvoid FESurfaceElement::facet_edge(int j, int* en) const\n{\n\tint nn = Nodes();\n\tswitch (nn)\n\t{\n\tcase 3:\n\t\ten[0] = m_lnode[j];\n\t\ten[1] = m_lnode[(j + 1) % 3];\n\t\tbreak;\n\tcase 6:\n\tcase 7:\n\t\ten[0] = m_lnode[j];\n\t\ten[1] = m_lnode[j + 3];\n\t\ten[2] = m_lnode[(j + 1) % 3];\n\t\tbreak;\n\tcase 4:\n\t\ten[0] = m_lnode[j];\n\t\ten[1] = m_lnode[(j + 1) % 4];\n\t\tbreak;\n\tcase 8:\n\tcase 9:\n\t\ten[0] = m_lnode[j];\n\t\ten[1] = m_lnode[j + 4];\n\t\ten[2] = m_lnode[(j + 1) % 4];\n\t\tbreak;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// see if this element has the list of nodes n. Return 0 if not or order is invalid, 1 if same order\n// and -1 if opposite order\nint FESurfaceElement::HasNodes(int* n, const int ns) const\n{\n\tint l = Nodes();\n\tif (l < ns) return 0;\n\n\t// find start index\n\tint m0 = -1;\n\tfor (int i = 0; i < ns; ++i)\n\t{\n\t\tif (m_node[0] == n[i])\n\t\t{\n\t\t\tm0 = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (m0 == -1) return 0;\n\n\t// check positive order\n\tint order = 1;\n\tfor (int i = 1; i < ns; ++i)\n\t{\n\t\tint m = (i + m0) % ns;\n\t\tif (m_node[i] != n[m])\n\t\t{\n\t\t\torder = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (order == 0)\n\t{\n\t\t// check negative order\n\t\torder = -1;\n\t\tfor (int i = 1; i < ns; ++i)\n\t\t{\n\t\t\tint m = (m0 + ns - i) % ns;\n\t\t\tassert((m >= 0) && (m < ns));\n\t\t\tif (m_node[i] != n[m])\n\t\t\t{\n\t\t\t\torder = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn order;\n}\n\ndouble* FESurfaceElement::Gr(int order, int n) const { return (order >= 0 ? ((FESurfaceElementTraits*)(m_pT))->Gr_p[order][n] : ((FESurfaceElementTraits*)(m_pT))->Gr[n]); }\t// shape function derivative to r\ndouble* FESurfaceElement::Gs(int order, int n) const { return (order >= 0 ? ((FESurfaceElementTraits*)(m_pT))->Gs_p[order][n] : ((FESurfaceElementTraits*)(m_pT))->Gs[n]); }\t// shape function derivative to s\n\nvoid FESurfaceElement::Serialize(DumpStream& ar)\n{\n\tFEElement::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_lid;\n\t// TODO: Serialize m_elem\n}\n"
  },
  {
    "path": "FECore/FESurfaceElement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElement.h\"\n\n//-----------------------------------------------------------------------------\n//!  This class defines a surface element\n\nclass FECORE_API FESurfaceElement : public FEElement\n{\npublic:\n\tstruct ELEMENT_REF {\n\t\tFEElement* pe = nullptr;\t// solid element the surface is attached to (if any)\n\t\tint face = -1;\t\t\t\t// local face index\n\t\tint orient = 0;\t\t\t\t// orientation of surface element relative to solid element\n\n\t\tvoid Reset()\n\t\t{\n\t\t\tpe = nullptr;\n\t\t\tface = -1;\n\t\t\torient = 0;\n\t\t}\n\t};\n\npublic:\n\tFESurfaceElement();\n\n\tFESurfaceElement(const FESurfaceElement& el);\n\n\tFESurfaceElement& operator = (const FESurfaceElement& el);\n\n\tvirtual void SetTraits(FEElementTraits* pt) override;\n\n\tdouble* GaussWeights() { return &((FESurfaceElementTraits*)(m_pT))->gw[0]; }\t\t\t// weights of integration points\n\tconst double* GaussWeights() const { return &((FESurfaceElementTraits*)(m_pT))->gw[0]; }\t\t\t// weights of integration points\n\tdouble gr(int n) const { return ((FESurfaceElementTraits*)(m_pT))->gr[n]; }\t// integration point coordinate r\n\tdouble gs(int n) const { return ((FESurfaceElementTraits*)(m_pT))->gs[n]; }\t// integration point coordinate  s\n    double cr() const { return ((FESurfaceElementTraits*)(m_pT))->cr; }    // centroid point coordinate r\n    double cs() const { return ((FESurfaceElementTraits*)(m_pT))->cs; }    // centroid point coordinate s\n\n\tdouble* Gr(int n) const { return ((FESurfaceElementTraits*)(m_pT))->Gr[n]; }\t// shape function derivative to r\n\tdouble* Gs(int n) const { return ((FESurfaceElementTraits*)(m_pT))->Gs[n]; }\t// shape function derivative to s\n\n\tdouble eval(double* d, int n)\n\t{\n\t\tdouble* N = H(n);\n\t\tint ne = Nodes();\n\t\tdouble a = 0;\n\t\tfor (int i=0; i<ne; ++i) a += N[i]*d[i];\n\t\treturn a;\n\t}\n\n\tdouble eval(int order, double* d, int n)\n\t{\n\t\tdouble* N = H(order, n);\n\t\tint ne = ShapeFunctions(order);\n\t\tdouble a = 0;\n\t\tfor (int i = 0; i<ne; ++i) a += N[i] * d[i];\n\t\treturn a;\n\t}\n\n\tdouble eval(double* d, double r, double s)\n\t{\n\t\tint n = Nodes();\n\t\tdouble H[FEElement::MAX_NODES];\n\t\tshape_fnc(H, r, s);\n\t\tdouble a = 0;\n\t\tfor (int i=0; i<n; ++i) a += H[i]*d[i];\n\t\treturn a;\n\t}\n\n\tdouble eval(int order, double* d, double r, double s)\n\t{\n\t\tint n = ShapeFunctions(order);\n\t\tdouble H[FEElement::MAX_NODES];\n\t\tshape_fnc(order, H, r, s);\n\t\tdouble a = 0;\n\t\tfor (int i = 0; i<n; ++i) a += H[i] * d[i];\n\t\treturn a;\n\t}\n\n\tvec3d eval(vec3d* d, double r, double s)\n\t{\n\t\tint n = Nodes();\n\t\tdouble H[FEElement::MAX_NODES];\n\t\tshape_fnc(H, r, s);\n\t\tvec3d a(0,0,0);\n\t\tfor (int i=0; i<n; ++i) a += d[i]*H[i];\n\t\treturn a;\n\t}\n\n\tvec3d eval(vec3d* d, int n)\n\t{\n\t\tint ne = Nodes();\n\t\tdouble* N = H(n);\n\t\tvec3d a(0,0,0);\n\t\tfor (int i=0; i<ne; ++i) a += d[i]*N[i];\n\t\treturn a;\n\t}\n\n\tdouble eval_deriv1(double* d, int j)\n\t{\n\t\tdouble* Hr = Gr(j);\n\t\tint n = Nodes();\n\t\tdouble s = 0;\n\t\tfor (int i=0; i<n; ++i) s +=  Hr[i]*d[i];\n\t\treturn s;\n\t}\n\n\tdouble eval_deriv1(int order, double* d, int j)\n\t{\n\t\tdouble* Hr = Gr(order, j);\n\t\tint n = ShapeFunctions(order);\n\t\tdouble s = 0;\n\t\tfor (int i = 0; i<n; ++i) s += Hr[i] * d[i];\n\t\treturn s;\n\t}\n\n\tdouble eval_deriv2(double* d, int j)\n\t{\n\t\tdouble* Hs = Gs(j);\n\t\tint n = Nodes();\n\t\tdouble s = 0;\n\t\tfor (int i=0; i<n; ++i) s +=  Hs[i]*d[i];\n\t\treturn s;\n\t}\n\n\tvec3d eval_deriv1(vec3d* d, int j)\n\t{\n\t\tdouble* Hr = Gr(j);\n\t\tint n = Nodes();\n\t\tvec3d v(0,0,0);\n\t\tfor (int i = 0; i<n; ++i) v += d[i]*Hr[i];\n\t\treturn v;\n\t}\n\n\tvec3d eval_deriv2(vec3d* d, int j)\n\t{\n\t\tdouble* Hs = Gs(j);\n\t\tint n = Nodes();\n\t\tvec3d v(0,0,0);\n\t\tfor (int i = 0; i<n; ++i) v += d[i]*Hs[i];\n\t\treturn v;\n\t}\n\n\tdouble eval_deriv1(double* d, double r, double s)\n\t{\n\t\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\t\tshape_deriv(Hr, Hs, r, s);\n\t\tint n = Nodes();\n\t\tdouble a = 0;\n\t\tfor (int i = 0; i<n; ++i) a += Hr[i] * d[i];\n\t\treturn a;\n\t}\n\n\tdouble eval_deriv1(int order, double* d, double r, double s)\n\t{\n\t\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\t\tshape_deriv(order, Hr, Hs, r, s);\n\t\tint n = ShapeFunctions(order);\n\t\tdouble a = 0;\n\t\tfor (int i=0; i<n; ++i) a +=  Hr[i]*d[i];\n\t\treturn a;\n\t}\n\n\tdouble eval_deriv2(double* d, double r, double s)\n\t{\n\t\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\t\tshape_deriv(Hr, Hs, r, s);\n\t\tint n = Nodes();\n\t\tdouble a = 0;\n\t\tfor (int i=0; i<n; ++i) a +=  Hs[i]*d[i];\n\t\treturn a;\n\t}\n\n\tdouble eval_deriv2(int order, double* d, double r, double s)\n\t{\n\t\tdouble Hr[FEElement::MAX_NODES], Hs[FEElement::MAX_NODES];\n\t\tshape_deriv(order, Hr, Hs, r, s);\n\t\tint n = ShapeFunctions(order);\n\t\tdouble a = 0;\n\t\tfor (int i = 0; i<n; ++i) a += Hs[i] * d[i];\n\t\treturn a;\n\t}\n\n\tvoid shape_fnc(double* H, double r, double s)\n\t{\n\t\t((FESurfaceElementTraits*)m_pT)->shape_fnc(H, r, s);\n\t}\n\n\tvoid shape_deriv(double* Gr, double* Gs, double r, double s)\n\t{\n\t\t((FESurfaceElementTraits*)m_pT)->shape_deriv(Gr, Gs, r, s);\n\t}\n\n\tvoid shape_deriv(int order, double* Gr, double* Gs, double r, double s)\n\t{\n\t\t((FESurfaceElementTraits*)m_pT)->shape_deriv(order, Gr, Gs, r, s);\n\t}\n\n\tvoid shape_deriv2(double* Grr, double* Grs, double* Gss, double r, double s)\n\t{\n\t\t((FESurfaceElementTraits*)m_pT)->shape_deriv2(Grr, Grs, Gss, r, s);\n\t}\n\n\tvoid shape_fnc(int order, double* H, double r, double s)\n\t{\n\t\t((FESurfaceElementTraits*)m_pT)->shape_fnc(order, H, r, s);\n\t}\n\n    //! return number of edges\n    int facet_edges() const;\n    \n    //! return node list of edge\n    void facet_edge(int j, int* en) const;\n\n\t// see if this element has the list of nodes n\n\tint HasNodes(int* n, const int ns) const;\n\n\t//! serialize\n\tvoid Serialize(DumpStream& ar) override;\n\n\tdouble* Gr(int order, int n) const;\n\tdouble* Gs(int order, int n) const;\n    \npublic:\n    //! local ID of surface element\n\tint\t\tm_lid;\n\n\t// indices of solid or shell element this surface is a face of\n\t// For solids, a surface element can be connected to two elements \n\t// if the surface is an inside surface. For boundary surfaces\n\t// the second element index is -1. \n\tELEMENT_REF \tm_elem[2];\n};\n"
  },
  {
    "path": "FECore/FESurfaceElementShape.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESurfaceElementShape.h\"\n\n//=============================================================================\n//              Q U A D 4\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FEQuad4::shape_fnc(double* H, double r, double s)\n{\n\tH[0] = 0.25*(1 - r)*(1 - s);\n\tH[1] = 0.25*(1 + r)*(1 - s);\n\tH[2] = 0.25*(1 + r)*(1 + s);\n\tH[3] = 0.25*(1 - r)*(1 + s);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad4::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[0] = -0.25*(1 - s); Hs[0] = -0.25*(1 - r);\n\tHr[1] =  0.25*(1 - s); Hs[1] = -0.25*(1 + r);\n\tHr[2] =  0.25*(1 + s); Hs[2] =  0.25*(1 + r);\n\tHr[3] = -0.25*(1 + s); Hs[3] =  0.25*(1 - r);\n}\n\n//-----------------------------------------------------------------------------\nvoid FEQuad4::shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s)\n{\n\tHrr[0] = 0; Hrs[0] =  0.25; Hss[0] = 0;\n\tHrr[1] = 0; Hrs[1] = -0.25; Hss[1] = 0;\n\tHrr[2] = 0; Hrs[2] =  0.25; Hss[2] = 0;\n\tHrr[3] = 0; Hrs[3] = -0.25; Hss[3] = 0;\n}\n\n//=============================================================================\n//              Q U A D 8\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// shape function at (r,s)\nvoid FEQuad8::shape_fnc(double* H, double r, double s)\n{\n\tH[4] = 0.5*(1 - r*r)*(1 - s);\n\tH[5] = 0.5*(1 - s*s)*(1 + r);\n\tH[6] = 0.5*(1 - r*r)*(1 + s);\n\tH[7] = 0.5*(1 - s*s)*(1 - r);\n\n\tH[0] = 0.25*(1 - r)*(1 - s) - 0.5*(H[4] + H[7]);\n\tH[1] = 0.25*(1 + r)*(1 - s) - 0.5*(H[4] + H[5]);\n\tH[2] = 0.25*(1 + r)*(1 + s) - 0.5*(H[5] + H[6]);\n\tH[3] = 0.25*(1 - r)*(1 + s) - 0.5*(H[6] + H[7]);\n}\n\n//-----------------------------------------------------------------------------\n// shape function derivatives at (r,s)\nvoid FEQuad8::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[4] = -r*(1 - s);\n\tHr[5] = 0.5*(1 - s*s);\n\tHr[6] = -r*(1 + s);\n\tHr[7] = -0.5*(1 - s*s);\n\n\tHr[0] = -0.25*(1 - s) - 0.5*(Hr[4] + Hr[7]);\n\tHr[1] = 0.25*(1 - s) - 0.5*(Hr[4] + Hr[5]);\n\tHr[2] = 0.25*(1 + s) - 0.5*(Hr[5] + Hr[6]);\n\tHr[3] = -0.25*(1 + s) - 0.5*(Hr[6] + Hr[7]);\n\n\tHs[4] = -0.5*(1 - r*r);\n\tHs[5] = -s*(1 + r);\n\tHs[6] = 0.5*(1 - r*r);\n\tHs[7] = -s*(1 - r);\n\n\tHs[0] = -0.25*(1 - r) - 0.5*(Hs[4] + Hs[7]);\n\tHs[1] = -0.25*(1 + r) - 0.5*(Hs[4] + Hs[5]);\n\tHs[2] = 0.25*(1 + r) - 0.5*(Hs[5] + Hs[6]);\n\tHs[3] = 0.25*(1 - r) - 0.5*(Hs[6] + Hs[7]);\n}\n\n//-----------------------------------------------------------------------------\n//! shape function derivatives at (r,s)\n//! \\todo implement this\nvoid FEQuad8::shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s)\n{\n\tHrr[4] = -(1 - s);\n\tHrr[5] = 0.0;\n\tHrr[6] = -(1 + s);\n\tHrr[7] = 0.0;\n\n\tHrs[4] = r;\n\tHrs[5] = -s;\n\tHrs[6] = -r;\n\tHrs[7] = s;\n\n\tHss[4] = 0.0;\n\tHss[5] = -(1 + r);\n\tHss[6] = 0.0;\n\tHss[7] = -(1 - r);\n\n\tHrr[0] = -0.5*(Hrr[4] + Hrr[7]);\n\tHrr[1] = -0.5*(Hrr[4] + Hrr[5]);\n\tHrr[2] = -0.5*(Hrr[5] + Hrr[6]);\n\tHrr[3] = -0.5*(Hrr[6] + Hrr[7]);\n\n\tHrs[0] = 0.25 - 0.5*(Hrs[4] + Hrs[7]);\n\tHrs[1] = -0.25 - 0.5*(Hrs[4] + Hrs[5]);\n\tHrs[2] = 0.25 - 0.5*(Hrs[5] + Hrs[6]);\n\tHrs[3] = -0.25 - 0.5*(Hrs[6] + Hrs[7]);\n\n\tHss[0] = -0.5*(Hss[4] + Hss[7]);\n\tHss[1] = -0.5*(Hss[4] + Hss[5]);\n\tHss[2] = -0.5*(Hss[5] + Hss[6]);\n\tHss[3] = -0.5*(Hss[6] + Hss[7]);\n}\n\n//=============================================================================\n//              Q U A D 9\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// shape function at (r,s)\nvoid FEQuad9::shape_fnc(double* H, double r, double s)\n{\n\tdouble R[3] = { 0.5*r*(r - 1.0), 0.5*r*(r + 1.0), 1.0 - r*r };\n\tdouble S[3] = { 0.5*s*(s - 1.0), 0.5*s*(s + 1.0), 1.0 - s*s };\n\n\tH[0] = R[0] * S[0];\n\tH[1] = R[1] * S[0];\n\tH[2] = R[1] * S[1];\n\tH[3] = R[0] * S[1];\n\tH[4] = R[2] * S[0];\n\tH[5] = R[1] * S[2];\n\tH[6] = R[2] * S[1];\n\tH[7] = R[0] * S[2];\n\tH[8] = R[2] * S[2];\n}\n\n//-----------------------------------------------------------------------------\n// shape function derivatives at (r,s)\nvoid FEQuad9::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tdouble R[3] = { 0.5*r*(r - 1.0), 0.5*r*(r + 1.0), 1.0 - r*r };\n\tdouble S[3] = { 0.5*s*(s - 1.0), 0.5*s*(s + 1.0), 1.0 - s*s };\n\tdouble DR[3] = { r - 0.5, r + 0.5, -2.0*r };\n\tdouble DS[3] = { s - 0.5, s + 0.5, -2.0*s };\n\n\tHr[0] = DR[0] * S[0];\n\tHr[1] = DR[1] * S[0];\n\tHr[2] = DR[1] * S[1];\n\tHr[3] = DR[0] * S[1];\n\tHr[4] = DR[2] * S[0];\n\tHr[5] = DR[1] * S[2];\n\tHr[6] = DR[2] * S[1];\n\tHr[7] = DR[0] * S[2];\n\tHr[8] = DR[2] * S[2];\n\n\tHs[0] = R[0] * DS[0];\n\tHs[1] = R[1] * DS[0];\n\tHs[2] = R[1] * DS[1];\n\tHs[3] = R[0] * DS[1];\n\tHs[4] = R[2] * DS[0];\n\tHs[5] = R[1] * DS[2];\n\tHs[6] = R[2] * DS[1];\n\tHs[7] = R[0] * DS[2];\n\tHs[8] = R[2] * DS[2];\n}\n\n//-----------------------------------------------------------------------------\n//! shape function derivatives at (r,s)\nvoid FEQuad9::shape_deriv2(double* Grr, double* Gss, double* Grs, double r, double s)\n{\n\tdouble R[3] = { 0.5*r*(r - 1.0), 0.5*r*(r + 1.0), 1.0 - r*r };\n\tdouble S[3] = { 0.5*s*(s - 1.0), 0.5*s*(s + 1.0), 1.0 - s*s };\n\tdouble DR[3] = { r - 0.5, r + 0.5, -2.0*r };\n\tdouble DS[3] = { s - 0.5, s + 0.5, -2.0*s };\n\tdouble DDR[3] = { 1.0, 1.0, -2.0 };\n\tdouble DDS[3] = { 1.0, 1.0, -2.0 };\n\n\tGrr[0] = DDR[0] * S[0]; Grs[0] = DR[0] * DS[0]; Gss[0] = R[0] * DDS[0];\n\tGrr[1] = DDR[1] * S[0]; Grs[1] = DR[1] * DS[0]; Gss[1] = R[1] * DDS[0];\n\tGrr[2] = DDR[1] * S[1]; Grs[2] = DR[1] * DS[1]; Gss[2] = R[1] * DDS[1];\n\tGrr[3] = DDR[0] * S[1]; Grs[3] = DR[0] * DS[1]; Gss[3] = R[0] * DDS[1];\n\tGrr[4] = DDR[2] * S[0]; Grs[4] = DR[2] * DS[0]; Gss[4] = R[2] * DDS[0];\n\tGrr[5] = DDR[1] * S[2]; Grs[5] = DR[1] * DS[2]; Gss[5] = R[1] * DDS[2];\n\tGrr[6] = DDR[2] * S[1]; Grs[6] = DR[2] * DS[1]; Gss[6] = R[2] * DDS[1];\n\tGrr[7] = DDR[0] * S[2]; Grs[7] = DR[0] * DS[2]; Gss[7] = R[0] * DDS[2];\n\tGrr[8] = DDR[2] * S[2]; Grs[8] = DR[2] * DS[2]; Gss[8] = R[2] * DDS[2];\n}\n\n//=============================================================================\n//              T R I 3\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FETri3::shape_fnc(double* H, double r, double s)\n{\n\tH[0] = 1.0 - r - s;\n\tH[1] = r;\n\tH[2] = s;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri3::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[0] = -1; Hs[0] = -1;\n\tHr[1] =  1; Hs[1] =  0;\n\tHr[2] =  0; Hs[2] =  1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri3::shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s)\n{\n\tHrr[0] = 0; Hrs[0] = 0; Hss[0] = 0;\n\tHrr[1] = 0; Hrs[1] = 0; Hss[1] = 0;\n\tHrr[2] = 0; Hrs[2] = 0; Hss[2] = 0;\n}\n\n//=============================================================================\n//              T R I 6\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FETri6::shape_fnc(double* H, double r, double s)\n{\n\tdouble r1 = 1.0 - r - s;\n\tdouble r2 = r;\n\tdouble r3 = s;\n\n\tH[0] = r1*(2.0*r1 - 1.0);\n\tH[1] = r2*(2.0*r2 - 1.0);\n\tH[2] = r3*(2.0*r3 - 1.0);\n\tH[3] = 4.0*r1*r2;\n\tH[4] = 4.0*r2*r3;\n\tH[5] = 4.0*r3*r1;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[0] = -3.0 + 4.0*r + 4.0*s;\n\tHr[1] = 4.0*r - 1.0;\n\tHr[2] = 0.0;\n\tHr[3] = 4.0 - 8.0*r - 4.0*s;\n\tHr[4] = 4.0*s;\n\tHr[5] = -4.0*s;\n\n\tHs[0] = -3.0 + 4.0*s + 4.0*r;\n\tHs[1] = 0.0;\n\tHs[2] = 4.0*s - 1.0;\n\tHs[3] = -4.0*r;\n\tHs[4] = 4.0*r;\n\tHs[5] = 4.0 - 8.0*s - 4.0*r;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri6::shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s)\n{\n\tHrr[0] =  4.0; Hrs[0] =  4.0; Hss[0] =  4.0;\n\tHrr[1] =  4.0; Hrs[1] =  0.0; Hss[1] =  0.0;\n\tHrr[2] =  0.0; Hrs[2] =  0.0; Hss[2] =  4.0;\n\tHrr[3] = -8.0; Hrs[3] = -4.0; Hss[3] =  0.0;\n\tHrr[4] =  0.0; Hrs[4] =  4.0; Hss[4] =  0.0;\n\tHrr[5] =  0.0; Hrs[5] = -4.0; Hss[5] = -8.0;\n}\n\n//=============================================================================\n//              T R I 7\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FETri7::shape_fnc(double* H, double r, double s)\n{\n\tdouble r1 = 1.0 - r - s;\n\tdouble r2 = r;\n\tdouble r3 = s;\n\n\tH[6] = 27.0*r1*r2*r3;\n\tH[0] = r1*(2.0*r1 - 1.0) + H[6] / 9.0;\n\tH[1] = r2*(2.0*r2 - 1.0) + H[6] / 9.0;\n\tH[2] = r3*(2.0*r3 - 1.0) + H[6] / 9.0;\n\tH[3] = 4.0*r1*r2 - 4.0*H[6] / 9.0;\n\tH[4] = 4.0*r2*r3 - 4.0*H[6] / 9.0;\n\tH[5] = 4.0*r3*r1 - 4.0*H[6] / 9.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri7::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tHr[6] = 27.0*s*(1.0 - 2.0*r - s);\n\tHr[0] = -3.0 + 4.0*r + 4.0*s + Hr[6] / 9.0;\n\tHr[1] = 4.0*r - 1.0 + Hr[6] / 9.0;\n\tHr[2] = 0.0 + Hr[6] / 9.0;\n\tHr[3] = 4.0 - 8.0*r - 4.0*s - 4.0*Hr[6] / 9.0;\n\tHr[4] = 4.0*s - 4.0*Hr[6] / 9.0;\n\tHr[5] = -4.0*s - 4.0*Hr[6] / 9.0;\n\n\tHs[6] = 27.0*r*(1.0 - r - 2.0*s);\n\tHs[0] = -3.0 + 4.0*s + 4.0*r + Hs[6] / 9.0;\n\tHs[1] = 0.0 + Hs[6] / 9.0;\n\tHs[2] = 4.0*s - 1.0 + Hs[6] / 9.0;\n\tHs[3] = -4.0*r - 4.0*Hs[6] / 9.0;\n\tHs[4] = 4.0*r - 4.0*Hs[6] / 9.0;\n\tHs[5] = 4.0 - 8.0*s - 4.0*r - 4.0*Hs[6] / 9.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri7::shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s)\n{\n\tHrr[6] = -54.0*s;\n\tHss[6] = -54.0*r;\n\tHrs[6] = 27.0*(1.0 - 2.0*r - 2.0*s);\n\n\tHrr[0] = 4.0 + Hrr[6] / 9.0; Hrs[0] = 4.0 + Hrs[6] / 9.0; Hss[0] = 4.0 + Hss[6] / 9.0;\n\tHrr[1] = 4.0 + Hrr[6] / 9.0; Hrs[1] = 0.0 + Hrs[6] / 9.0; Hss[1] = 0.0 + Hss[6] / 9.0;\n\tHrr[2] = 0.0 + Hrr[6] / 9.0; Hrs[2] = 0.0 + Hrs[6] / 9.0; Hss[2] = 4.0 + Hss[6] / 9.0;\n\tHrr[3] = -8.0 - 4.0*Hrr[6] / 9.0; Hrs[3] = -4.0 - 4.0*Hrs[6] / 9.0; Hss[3] = 0.0 - 4.0*Hss[6] / 9.0;\n\tHrr[4] = 0.0 - 4.0*Hrr[6] / 9.0; Hrs[4] = 4.0 - 4.0*Hrs[6] / 9.0; Hss[4] = 0.0 - 4.0*Hss[6] / 9.0;\n\tHrr[5] = 0.0 - 4.0*Hrr[6] / 9.0; Hrs[5] = -4.0 - 4.0*Hrs[6] / 9.0; Hss[5] = -8.0 - 4.0*Hss[6] / 9.0;\n}\n\n//=============================================================================\n//              T R I 10\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nvoid FETri10::shape_fnc(double* H, double r, double s)\n{\n\tdouble L1 = 1.0 - r - s;\n\tdouble L2 = r;\n\tdouble L3 = s;\n\n\tH[0] = 0.5*(3 * L1 - 1)*(3 * L1 - 2)*L1;\n\tH[1] = 0.5*(3 * L2 - 1)*(3 * L2 - 2)*L2;\n\tH[2] = 0.5*(3 * L3 - 1)*(3 * L3 - 2)*L3;\n\tH[3] = 4.5*(3 * L1 - 1)*L1*L2;\n\tH[4] = 4.5*(3 * L2 - 1)*L1*L2;\n\tH[5] = 4.5*(3 * L2 - 1)*L2*L3;\n\tH[6] = 4.5*(3 * L3 - 1)*L2*L3;\n\tH[7] = 4.5*(3 * L1 - 1)*L1*L3;\n\tH[8] = 4.5*(3 * L3 - 1)*L1*L3;\n\tH[9] = 27.*L1*L2*L3;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri10::shape_deriv(double* Hr, double* Hs, double r, double s)\n{\n\tdouble L1 = 1.0 - r - s;\n\tdouble L2 = r;\n\tdouble L3 = s;\n\n\tHr[0] = -3. / 2.*(3 * L1 - 2)*L1 - 3. / 2.*(3 * L1 - 1)*L1 - 0.5*(3 * L1 - 1)*(3 * L1 - 2);\n\tHr[1] = 3. / 2.*(3 * L2 - 2)*L2 + 3. / 2.*(3 * L2 - 1)*L2 + 0.5*(3 * L2 - 1)*(3 * L2 - 2);\n\tHr[2] = 0.0;\n\tHr[3] = -27. / 2.*L1*L2 - 9. / 2.*(3 * L1 - 1)*L2 + 9. / 2.*(3 * L1 - 1)*L1;\n\tHr[4] = 27. / 2.*L1*L2 - 9. / 2.*(3 * L2 - 1)*L2 + 9. / 2.*(3 * L2 - 1)*L1;\n\tHr[5] = 27. / 2.*L2*L3 + 9. / 2.*(3 * L2 - 1)*L3;\n\tHr[6] = 9. / 2.*(3 * L3 - 1)*L3;\n\tHr[7] = -27. / 2.*L1*L3 - 9. / 2.*(3 * L1 - 1)*L3;\n\tHr[8] = -9. / 2.*(3 * L3 - 1)*L3;\n\tHr[9] = -27.*L2*L3 + 27.*L1*L3;\n\n\tHs[0] = -3. / 2.*(3 * L1 - 2)*L1 - 3. / 2.*(3 * L1 - 1)*L1 - 0.5*(3 * L1 - 1)*(3 * L1 - 2);\n\tHs[1] = 0.0;\n\tHs[2] = 3. / 2.*(3 * L3 - 2)*L3 + 3. / 2.*(3 * L3 - 1)*L3 + 0.5*(3 * L3 - 1)*(3 * L3 - 2);\n\tHs[3] = -27. / 2.*L1*L2 - 9. / 2.*(3 * L1 - 1)*L2;\n\tHs[4] = -9. / 2.*(3 * L2 - 1)*L2;\n\tHs[5] = 9. / 2.*(3 * L2 - 1)*L2;\n\tHs[6] = 27. / 2.*L2*L3 + 9. / 2.*(3 * L3 - 1)*L2;\n\tHs[7] = -27. / 2.*L1*L3 - 9. / 2.*(3 * L1 - 1)*L3 + 9. / 2.*(3 * L1 - 1)*L1;\n\tHs[8] = 27. / 2.*L1*L3 - 9. / 2.*(3 * L3 - 1)*L3 + 9. / 2.*(3 * L3 - 1)*L1;\n\tHs[9] = -27.*L2*L3 + 27.*L1*L2;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETri10::shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s)\n{\n\t// TODO: Implement this\n}\n"
  },
  {
    "path": "FECore/FESurfaceElementShape.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEElementShape.h\"\n\n//=============================================================================\n// Base class for defining element shape classes for (3D) solid elements\nclass FESurfaceElementShape : public FEElementShape\n{\npublic:\n\tFESurfaceElementShape(FE_Element_Shape shape, int nodes) : FEElementShape(shape, nodes) {}\n\n\t//! values of shape functions\n\tvirtual void shape_fnc(double* H, double r, double s) = 0;\n\n\t//! values of shape function derivatives\n\tvirtual void shape_deriv(double* Hr, double* Hs, double r, double s) = 0;\n\n\t//! values of shape function second derivatives\n\tvirtual void shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) = 0;\n};\n\n//=============================================================================\n// Class for QUAD4 elements\nclass FEQuad4 : public FESurfaceElementShape\n{\npublic:\n\tFEQuad4() : FESurfaceElementShape(ET_QUAD4, 4) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n\n//=============================================================================\n// Class for QUAD8 elements\nclass FEQuad8 : public FESurfaceElementShape\n{\npublic:\n\tFEQuad8() : FESurfaceElementShape(ET_QUAD8, 8) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n\n//=============================================================================\n// Class for QUAD9 elements\nclass FEQuad9 : public FESurfaceElementShape\n{\npublic:\n\tFEQuad9() : FESurfaceElementShape(ET_QUAD9, 9) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n\n//=============================================================================\n// Class for TRI3 elements\nclass FETri3 : public FESurfaceElementShape\n{\npublic:\n\tFETri3() : FESurfaceElementShape(ET_TRI3, 4) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n\n//=============================================================================\n// Class for TRI6 elements\nclass FETri6 : public FESurfaceElementShape\n{\npublic:\n\tFETri6() : FESurfaceElementShape(ET_TRI6, 6) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n\n//=============================================================================\n// Class for TRI7 elements\nclass FETri7 : public FESurfaceElementShape\n{\npublic:\n\tFETri7() : FESurfaceElementShape(ET_TRI7, 7) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n\n//=============================================================================\n// Class for TRI10 elements\nclass FETri10 : public FESurfaceElementShape\n{\npublic:\n\tFETri10() : FESurfaceElementShape(ET_TRI10, 10) {}\n\n\t//! values of shape functions\n\tvoid shape_fnc(double* H, double r, double s) override;\n\n\t//! values of shape function derivatives\n\tvoid shape_deriv(double* Hr, double* Hs, double r, double s) override;\n\n\t//! values of shape function second derivatives\n\tvoid shape_deriv2(double* Hrr, double* Hss, double* Hrs, double r, double s) override;\n};\n"
  },
  {
    "path": "FECore/FESurfaceLoad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfaceLoad.h\"\n#include \"FEMesh.h\"\n#include \"DumpStream.h\"\n#include \"FEModel.h\"\n\nFESurfaceLoad::FESurfaceLoad(FEModel* pfem) : FEModelLoad(pfem)\n{\n\tm_psurf = 0;\n}\n\nFESurfaceLoad::~FESurfaceLoad(void)\n{\n\n}\n\n//! Set the surface to apply the load to\nvoid FESurfaceLoad::SetSurface(FESurface* ps)\n{\n\tm_psurf = ps; \n}\n\nbool FESurfaceLoad::Init()\n{\n\tif (m_psurf == 0) return false;\n\tif (m_psurf->Init() == false) return false;\n\treturn FEModelLoad::Init();\n}\n\nvoid FESurfaceLoad::Serialize(DumpStream& ar)\n{\n\tFEModelLoad::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\n\tar & m_psurf;\n\n\t// the mesh manages surfaces for surface loads\n\tif (m_psurf && ar.IsLoading())\n\t{\n\t\tFEMesh* pm = m_psurf->GetMesh();\n\t\tpm->AddSurface(m_psurf);\n\t}\n}\n\nvoid FESurfaceLoad::ForceMeshUpdate()\n{\n\tGetFEModel()->SetMeshUpdateFlag(true);\n}\n"
  },
  {
    "path": "FECore/FESurfaceLoad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEModelLoad.h\"\n#include \"FESurface.h\"\n#include \"FESolver.h\"\n#include \"FETimeInfo.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalVector;\n\n//-----------------------------------------------------------------------------\n//! This is the base class for all loads that are applied to surfaces\nclass FECORE_API FESurfaceLoad : public FEModelLoad\n{\npublic:\n\tFESurfaceLoad(FEModel* pfem);\n\tvirtual ~FESurfaceLoad(void);\n\n\t//! Set the surface to apply the load to\n\tvirtual void SetSurface(FESurface* ps);\n\n\tbool Init() override;\n\n\t//! Get the surface\n\tFESurface& GetSurface() { return *m_psurf; }\n\n\tvoid Serialize(DumpStream& ar) override;\n\npublic:\n    virtual double ScalarLoad(FESurfaceMaterialPoint& mp) { return 0; }\n    virtual vec3d VectorLoad(FESurfaceMaterialPoint& mp) { return vec3d(0,0,0); }\n\n\t// TODO: Can I get rid of this?\n\t// This is needed to update the mesh after some surface loads, which aren't really\n\t// surface loads modify boundary conditions\n\tvoid ForceMeshUpdate();\n\nprotected:\n\tFESurface*\tm_psurf;\n\n\tFECORE_BASE_CLASS(FESurfaceLoad)\n};\n"
  },
  {
    "path": "FECore/FESurfaceMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfaceMap.h\"\n#include \"FESurface.h\"\n#include \"DumpStream.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFESurfaceMap::FESurfaceMap() : FEDataMap(FE_SURFACE_MAP)\n{\n\tm_maxFaceNodes = 0;\n\tm_format = FMT_MULT;\n}\n\n//-----------------------------------------------------------------------------\nFESurfaceMap::FESurfaceMap(FEDataType dataType) : FEDataMap(FE_SURFACE_MAP, dataType)\n{\n\tm_maxFaceNodes = 0;\n\tm_format = FMT_MULT;\n}\n\n//-----------------------------------------------------------------------------\nFESurfaceMap::FESurfaceMap(const FESurfaceMap& map) : FEDataMap(map)\n{\n\tm_maxFaceNodes = map.m_maxFaceNodes;\n\tm_format = map.m_format;\n}\n\n//-----------------------------------------------------------------------------\nFESurfaceMap& FESurfaceMap::operator = (const FESurfaceMap& map)\n{\n\tFEDataArray::operator=(map);\n\tm_name = map.m_name;\n\tm_maxFaceNodes = map.m_maxFaceNodes;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\n// return the item list associated with this map\nFEItemList* FESurfaceMap::GetItemList()\n{\n\treturn const_cast<FEFacetSet*>(m_surf);\n}\n\n//-----------------------------------------------------------------------------\nint FESurfaceMap::StorageFormat() const\n{\n\treturn m_format;\n}\n\n//-----------------------------------------------------------------------------\nbool FESurfaceMap::Create(const FEFacetSet* ps, double val, Storage_Fmt fmt)\n{\n\tm_surf = ps;\n\tm_format = fmt;\n\tif (fmt == FMT_MULT)\n\t{\n\t\tint NF = ps->Faces();\n\t\tm_maxFaceNodes = 0;\n\t\tfor (int i = 0; i < NF; ++i)\n\t\t{\n\t\t\tconst FEFacetSet::FACET& f = ps->Face(i);\n\n\t\t\t// TODO: currently, the number of nodes matches the type, but not sure if this will remain the case.\n\t\t\tif (f.ntype > m_maxFaceNodes) m_maxFaceNodes = f.ntype;\n\t\t}\n\t\treturn resize(NF*m_maxFaceNodes, val);\n\t}\n\telse if (fmt == FMT_NODE)\n\t{\n\t\tFENodeList nodeList = ps->GetNodeList();\n\t\tint NN = nodeList.Size();\n\t\tm_maxFaceNodes = 1;\n\t\treturn resize(NN, val);\n\t}\n\telse return false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::setValue(int n, double v)\n{\n\tint index = n*m_maxFaceNodes;\n\tfor (int i=0; i<m_maxFaceNodes; ++i) set<double>(index+i, v);\t\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::setValue(int n, const vec2d& v)\n{\n\tint index = n*m_maxFaceNodes;\n\tfor (int i = 0; i<m_maxFaceNodes; ++i) set<vec2d>(index + i, v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::setValue(int n, const vec3d& v)\n{\n\tint index = n*m_maxFaceNodes;\n\tfor (int i = 0; i<m_maxFaceNodes; ++i) set<vec3d>(index + i, v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::setValue(int n, const mat3d& v)\n{\n\tint index = n*m_maxFaceNodes;\n\tfor (int i = 0; i<m_maxFaceNodes; ++i) set<mat3d>(index + i, v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::setValue(int n, const mat3ds& v)\n{\n\tint index = n * m_maxFaceNodes;\n\tfor (int i = 0; i < m_maxFaceNodes; ++i) set<mat3ds>(index + i, v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::fillValue(double v)\n{\n\tset<double>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::fillValue(const vec2d& v)\n{\n\tset<vec2d>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::fillValue(const vec3d& v)\n{\n\tset<vec3d>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::fillValue(const mat3d& v)\n{\n\tset<mat3d>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::fillValue(const mat3ds& v)\n{\n\tset<mat3ds>(v);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceMap::Serialize(DumpStream& ar)\n{\n\tFEDataMap::Serialize(ar);\n\tif (ar.IsShallow()) return;\n\tar & m_maxFaceNodes & m_format;\n\tif (ar.IsSaving())\n\t{\n\t\tFEFacetSet* fs = const_cast<FEFacetSet*>(m_surf);\n\t\tar << fs;\n\t}\n\telse\n\t{\n\t\tFEFacetSet* fs = nullptr;\n\t\tar >> fs;\n\t\tm_surf = fs;\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble FESurfaceMap::value(const FEMaterialPoint& pt)\n{\n\tdouble v = 0.0;\n\tswitch (m_format)\n\t{\n\tcase FMT_NODE:\n\t\t{\n\t\t\tif (pt.m_elem)\n\t\t\t{\n\t\t\t\t// get the element this material point is in\n\t\t\t\tFESurfaceElement* pe = dynamic_cast<FESurfaceElement*>(pt.m_elem);\n\t\t\t\tassert(pe);\n\n\t\t\t\t// make sure this element belongs to this domain\n\t\t\t\t// TODO: Can't check this if map was created through FEFacetSet\n\t\t\t//\tassert(pe->GetMeshPartition() == m_dom);\n\n\t\t\t\tif (pt.m_index < 0x10000)\n\t\t\t\t{\n\t\t\t\t\t// integration point\n\t\t\t\t\t// get shape functions\n\t\t\t\t\tdouble* H = pe->H(pt.m_index);\n\n\t\t\t\t\tint ne = pe->Nodes();\n\t\t\t\t\tfor (int i = 0; i < ne; ++i)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble vi = value<double>(pe->m_lnode[i], 0);\n\t\t\t\t\t\tv += vi * H[i];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// element node\n\t\t\t\t\tint n = pt.m_index - 0x10000;\n\t\t\t\t\tv = value<double>(pe->m_lnode[n], 0);\n\t\t\t\t}\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// assume material point is a node\n\t\t\t\treturn value<double>(pt.m_index, 0);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase FMT_MULT:\n\t\t{\n\t\t\t// get the element this material point is in\n\t\t\tFESurfaceElement* pe = dynamic_cast<FESurfaceElement*>(pt.m_elem);\n\t\t\tassert(pe);\n\n\t\t\t// make sure this element belongs to this domain\n\t\t\t// TODO: Can't check this if map was created through FEFacetSet\n\t\t//\tassert(pe->GetMeshPartition() == m_dom);\n\n\t\t\t// get its local ID\n\t\t\tint lid = pe->GetLocalID();\n\n\t\t\t// get shape functions\n\t\t\tif (pt.m_index < 0x10000)\n\t\t\t{\n\t\t\t\tdouble* H = pe->H(pt.m_index);\n\n\t\t\t\tint ne = pe->Nodes();\n\t\t\t\tfor (int i = 0; i < ne; ++i)\n\t\t\t\t{\n\t\t\t\t\tdouble vi = value<double>(lid, i);\n\t\t\t\t\tv += vi * H[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// element node\n\t\t\t\tint n = pt.m_index - 0x10000;\n\t\t\t\tv = value<double>(lid, n);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FESurfaceMap::valueVec3d(const FEMaterialPoint& pt)\n{\n\t// get the element this material point is in\n\tFESurfaceElement* pe = dynamic_cast<FESurfaceElement*>(pt.m_elem);\n\tassert(pe);\n\n\t// make sure this element belongs to this domain\n\t// TODO: Can't check this if map was created through FEFacetSet\n\t//\tassert(pe->GetMeshPartition() == m_dom);\n\n\t// get its local ID\n\tint lid = pe->GetLocalID();\n\n\t// get shape functions\n\tdouble* H = pe->H(pt.m_index);\n\n\tvec3d v(0,0,0);\n\tint ne = pe->Nodes();\n\tfor (int i = 0; i < ne; ++i)\n\t{\n\t\tvec3d vi = value<vec3d>(lid, i);\n\t\tv += vi*H[i];\n\t}\n\n\treturn v;\n}\n\n//-----------------------------------------------------------------------------\nmat3d FESurfaceMap::valueMat3d(const FEMaterialPoint& pt)\n{\n\t// get the element this material point is in\n\tFESurfaceElement* pe = dynamic_cast<FESurfaceElement*>(pt.m_elem);\n\tassert(pe);\n\n\t// make sure this element belongs to this domain\n\t// TODO: Can't check this if map was created through FEFacetSet\n\t//\tassert(pe->GetMeshPartition() == m_dom);\n\n\t// get its local ID\n\tint lid = pe->GetLocalID();\n\n\t// get shape functions\n\tdouble* H = pe->H(pt.m_index);\n\n\tmat3d v; v.zero();\n\tint ne = pe->Nodes();\n\tfor (int i = 0; i < ne; ++i)\n\t{\n\t\tmat3d vi = value<mat3d>(lid, i);\n\t\tv += vi*H[i];\n\t}\n\n\treturn v;\n}\n\n\n//-----------------------------------------------------------------------------\nmat3ds FESurfaceMap::valueMat3ds(const FEMaterialPoint& pt)\n{\n\t// get the element this material point is in\n\tFESurfaceElement* pe = dynamic_cast<FESurfaceElement*>(pt.m_elem);\n\tassert(pe);\n\n\t// make sure this element belongs to this domain\n\t// TODO: Can't check this if map was created through FEFacetSet\n\t//\tassert(pe->GetMeshPartition() == m_dom);\n\n\t// get its local ID\n\tint lid = pe->GetLocalID();\n\n\t// get shape functions\n\tdouble* H = pe->H(pt.m_index);\n\n\tmat3ds v; v.zero();\n\tint ne = pe->Nodes();\n\tfor (int i = 0; i < ne; ++i)\n\t{\n\t\tmat3ds vi = value<mat3ds>(lid, i);\n\t\tv += vi * H[i];\n\t}\n\n\treturn v;\n}\n"
  },
  {
    "path": "FECore/FESurfaceMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <string>\n#include <assert.h>\n#include \"FEDataMap.h\"\n\n//-----------------------------------------------------------------------------\nclass FESurface;\nclass FEFacetSet;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\ntypedef int FEFacetIndex;\n\n\n//-----------------------------------------------------------------------------\n// TODO: Perhaps I should rename this FESurfaceData\n//       and then define FESurfaceMap as a tool for evaluating data across a surface (i.e. via shape functions)\nclass FECORE_API FESurfaceMap : public FEDataMap\n{\npublic:\n\t//! default constructor\n\tFESurfaceMap();\n\tFESurfaceMap(FEDataType dataType);\n\n\t//! copy constructor\n\tFESurfaceMap(const FESurfaceMap& map);\n\n\t//! assignment operator\n\tFESurfaceMap& operator = (const FESurfaceMap& map);\n\n\t//! Create a surface data map for this surface\n\tbool Create(const FEFacetSet* ps, double val = 0.0, Storage_Fmt fmt = FMT_MULT);\n\n\t//! serialization\n\tvoid Serialize(DumpStream& ar) override;\n\n\tconst FEFacetSet* GetFacetSet() const { return m_surf; }\n\n\tint MaxNodes() const { return m_maxFaceNodes; }\n\n\t// return the item list associated with this map\n\tFEItemList* GetItemList() override;\n\n\tint StorageFormat() const;\n\npublic: // from FEDataMap\n\tdouble value(const FEMaterialPoint& pt) override;\n\tvec3d valueVec3d(const FEMaterialPoint& mp) override;\n\tmat3d valueMat3d(const FEMaterialPoint& mp) override;\n\tmat3ds valueMat3ds(const FEMaterialPoint& mp) override;\n\npublic:\n\ttemplate <typename T> T value(int nface, int node)\n\t{\n\t\treturn get<T>(nface*m_maxFaceNodes + node);\n\t}\n\n\ttemplate <typename T> void setValue(int nface, int node, const T& v)\n\t{\n\t\tset<T>(nface*m_maxFaceNodes + node, v);\n\t}\n\n\tvoid setValue(int n, double v) override;\n\tvoid setValue(int n, const vec2d& v) override;\n\tvoid setValue(int n, const vec3d& v) override;\n\tvoid setValue(int n, const mat3d& v) override;\n\tvoid setValue(int n, const mat3ds& v) override;\n\n\tvoid fillValue(double v) override;\n\tvoid fillValue(const vec2d& v) override;\n\tvoid fillValue(const vec3d& v) override;\n\tvoid fillValue(const mat3d& v) override;\n\tvoid fillValue(const mat3ds& v) override;\n\nprivate:\n\tconst FEFacetSet*\tm_surf;\t\t// the surface for which this data set is defined\n\tint\t\t\t\t\tm_format;\t// the storage format\n\tint\tm_maxFaceNodes;\t\t\t\t// number of nodes for each face\n};\n"
  },
  {
    "path": "FECore/FESurfacePair.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfacePair.h\"\n#include \"FEFacetSet.h\"\n#include \"FEMesh.h\"\n#include \"DumpStream.h\"\n\n//--------------------------------------------------------\nFESurfacePair::FESurfacePair(FEMesh* pm) : m_mesh(pm)\n{\n\tm_surface1 = 0;\n\tm_surface2 = 0;\n}\n\nvoid FESurfacePair::SetName(const std::string& name)\n{\n\tm_name = name;\n}\n\nconst std::string& FESurfacePair::GetName() const\n{\n\treturn m_name;\n}\n\nFEFacetSet* FESurfacePair::GetPrimarySurface()\n{\n\treturn m_surface1;\n}\n\nvoid FESurfacePair::SetPrimarySurface(FEFacetSet* pf)\n{\n\tm_surface1 = pf;\n}\n\nFEFacetSet* FESurfacePair::GetSecondarySurface()\n{\n\treturn m_surface2;\n}\n\nvoid FESurfacePair::SetSecondarySurface(FEFacetSet* pf)\n{\n\tm_surface2 = pf;\n}\n\nvoid FESurfacePair::Serialize(DumpStream& ar)\n{\n\tif (ar.IsShallow()) return;\n\n\tar& m_surface1;\n\tar& m_surface2;\n\tar& m_mesh;\n}\n"
  },
  {
    "path": "FECore/FESurfacePair.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <string>\n\n//-----------------------------------------------------------------------------\nclass FEMesh;\nclass FEFacetSet;\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FESurfacePair\n{\npublic:\n\tFESurfacePair(FEMesh* pm);\n\n\tvoid SetName(const std::string& name);\n\tconst std::string& GetName() const;\n\n\tFEFacetSet* GetPrimarySurface();\n\tvoid SetPrimarySurface(FEFacetSet* pf);\n\n\tFEFacetSet* GetSecondarySurface();\n\tvoid SetSecondarySurface(FEFacetSet* pf);\n\n\tvoid Serialize(DumpStream& ar);\n\nprivate:\n\tstd::string\t\tm_name;\n\tFEFacetSet*\t\tm_surface1;\t// the primary surface\n\tFEFacetSet*\t\tm_surface2;\t// the secondary surface\n\tFEMesh*\t\t\tm_mesh;\n};\n"
  },
  {
    "path": "FECore/FESurfacePairConstraint.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfacePairConstraint.h\"\n\n//-----------------------------------------------------------------------------\nFESurfacePairConstraint::FESurfacePairConstraint(FEModel* pfem) : FEStepComponent(pfem)\n{\n}\n"
  },
  {
    "path": "FECore/FESurfacePairConstraint.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEStepComponent.h\"\n#include \"FESurface.h\"\n\n//-----------------------------------------------------------------------------\nclass FEModel;\nclass FEGlobalMatrix;\n\n//-----------------------------------------------------------------------------\n//! This class describes a general purpose interaction between two surfaces.\n// TODO: I like to inherit this from FENLConstraint and potentially eliminate\n//       The distinction between a nonlinear constraint and a contact interface.\n//       Since a contact interface essentially is a nonlinear constraint, I think\n//       this may make things a lot easier. I already made the function definitions consistent\n//       but am hesitant to push this through at this point. \nclass FECORE_API FESurfacePairConstraint : public FEStepComponent\n{\n\tFECORE_SUPER_CLASS(FESURFACEINTERACTION_ID)\n\tFECORE_BASE_CLASS(FESurfacePairConstraint);\n\npublic:\n\t//! constructor\n\tFESurfacePairConstraint(FEModel* pfem);\n\npublic:\n\t//! return the primary surface\n\tvirtual FESurface* GetPrimarySurface() = 0;\n\n\t//! return the secondary surface\n\tvirtual FESurface* GetSecondarySurface () = 0;\n\n\t//! temporary construct to determine if contact interface uses nodal integration rule (or facet)\n\tvirtual bool UseNodalIntegration() = 0;\n\n\t//! create a copy of this interface\n\tvirtual void CopyFrom(FESurfacePairConstraint* pci) {}\n\npublic:\n\t// Build the matrix profile\n\tvirtual void BuildMatrixProfile(FEGlobalMatrix& M) = 0;\n\n\t// reset the state data\n\tvirtual void Reset() {}\n\n\t// do the augmentation\n\tvirtual bool Augment(int naug, const FETimeInfo& tp) { return true; }\n\n\t// allocate equations for lagrange multipliers\n\t// (should return the number of equations to be allocated)\n\tvirtual int InitEquations(int neq) { return 0; }\n\n\t// prepare for next time step\n\tvirtual void PrepStep() {}\n\n\t// update based on solution (use for updating Lagrange Multipliers)\n\t// Ui is the total update for the current time step\n\t// ui is the trial incremental update (e.g. from line search)\n\tvirtual void Update(const std::vector<double>& Ui, const std::vector<double>& ui) {}\n\n\t// This is called after each iteration and asks to Update Ui using ui (this is used by Lagrange Multiplier implementations)\n\tvirtual void UpdateIncrements(std::vector<double>& Ui, const std::vector<double>& ui) {}\n\n\tusing FEModelComponent::Update;\n};\n"
  },
  {
    "path": "FECore/FESurfaceToSurfaceMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FESurfaceToSurfaceMap.h\"\n#include \"FEMesh.h\"\n#include \"FESurface.h\"\n#include <FECore/FEDomainMap.h>\n\nBEGIN_FECORE_CLASS(FESurfaceToSurfaceMap, FEElemDataGenerator)\n\tADD_PROPERTY(m_func, \"function\");\n\tADD_PROPERTY(m_surf1, \"bottom_surface\", FEProperty::Reference);\n\tADD_PROPERTY(m_surf2, \"top_surface\", FEProperty::Reference);\nEND_FECORE_CLASS();\n\nFESurfaceToSurfaceMap::FESurfaceToSurfaceMap(FEModel* fem) : FEElemDataGenerator(fem)\n{\n\tm_ccp1 = 0;\n\tm_ccp2 = 0;\n\tm_func = 0;\n\n\tm_surf1 = nullptr;\n\tm_surf2 = nullptr;\n\n\tm_binverted = false;\n}\n\nFESurfaceToSurfaceMap::~FESurfaceToSurfaceMap()\n{\n\tif (m_ccp1) delete m_ccp1;\n\tif (m_ccp2) delete m_ccp2;\n}\n\nbool FESurfaceToSurfaceMap::Init()\n{\n\tFEMesh& mesh = GetMesh();\n\tif (m_func == 0) return false;\n\tif ((m_surf1 == nullptr) || (m_surf2 == nullptr)) return false;\n\t\n\t// we need to invert the second surface, otherwise the normal projections won't work\n\tif (m_binverted == false)\n\t{\n\t\tm_surf2->Invert();\n\t\tm_binverted = true;\n\t}\n\n\t// initialize projections\n\tif (m_ccp1 == nullptr)\n\t{\n\t\tm_ccp1 = new FEClosestPointProjection(*m_surf1);\n\t\tm_ccp1->HandleSpecialCases(true);\n\t\tm_ccp1->AllowBoundaryProjections(true);\n\t\tif (m_ccp1->Init() == false) return false;\n\t}\n\n\tif (m_ccp2 == nullptr)\n\t{\n\t\tm_ccp2 = new FEClosestPointProjection(*m_surf2);\n\t\tm_ccp2->HandleSpecialCases(true);\n\t\tm_ccp2->AllowBoundaryProjections(true);\n\t\tif (m_ccp2->Init() == false) return false;\n\t}\n    \n    m_func->Init();\n\n\treturn FEMeshDataGenerator::Init();\n}\n\ndouble FESurfaceToSurfaceMap::value(const vec3d& x)\n{\n\tvec3d r(x);\n\n\t// project x onto surface 1\n\tvec3d q1(0,0,0), q2(0,0,0);\n\tvec2d r1, r2;\n\tFESurfaceElement* pe1 = m_ccp1->Project(r, q1, r1);\n\tif (pe1 == nullptr)\n\t{\n\t\tassert(false);\n\t\treturn 0.0;\n\t}\n\n\t// project x onto surface 2\n\tFESurfaceElement* pe2 = m_ccp2->Project(r, q2, r2);\n\tif (pe2 == nullptr)\n\t{\n\t\tassert(false);\n\t\treturn 0.0;\n\t}\n\n\tdouble L1 = (x - q1).norm();\n\tdouble L2 = (q2 - x).norm();\n\n\tdouble D = L1 + L2;\n\tif (D == 0.0) D = 1.0;\n\n\t// find the fractional distance\n\tdouble w = L1 / D;\n\n\t// evaluate the function\n\treturn m_func->value(w);\n}\n\nFEDataMap* FESurfaceToSurfaceMap::Generate()\n{\n\tFEElementSet* elset = GetElementSet();\n\tif (elset == nullptr) return nullptr;\n\n\tFEDomainMap* map = new FEDomainMap(FEDataType::FE_DOUBLE, Storage_Fmt::FMT_NODE);\n\tmap->Create(elset);\n\n\tFENodeList nodeList = elset->GetNodeList();\n\tfor (int i = 0; i < nodeList.Size(); ++i)\n\t{\n\t\tFENode* pn = nodeList.Node(i);\n\t\tdouble v = value(pn->m_r0);\n\t\tmap->setValue(i, v);\n\t}\n\n\treturn map;\n}\n"
  },
  {
    "path": "FECore/FESurfaceToSurfaceMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEDataGenerator.h\"\n#include \"FEFunction1D.h\"\n#include \"FEClosestPointProjection.h\"\n#include \"FENormalProjection.h\"\n\nclass FEModel;\nclass FESurface;\n\nclass FESurfaceToSurfaceMap : public FEElemDataGenerator\n{\npublic:\n\tFESurfaceToSurfaceMap(FEModel* fem);\n\t~FESurfaceToSurfaceMap();\n\n\tbool Init() override;\n\n\tFEDataMap* Generate() override;\n\nprivate:\n\tdouble value(const vec3d& x);\n\nprivate:\n\tFEFunction1D*\tm_func;\n\tFESurface*\tm_surf1;\n\tFESurface*\tm_surf2;\n\tFEClosestPointProjection*\tm_ccp1;\n\tFEClosestPointProjection*\tm_ccp2;\n\tbool\t\t\tm_binverted;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FESurfaceToSurfaceVectorMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FESurfaceToSurfaceVectorMap.h\"\n#include \"FEMesh.h\"\n#include \"FESurface.h\"\n#include \"FEDomainMap.h\"\n#include \"FECoreKernel.h\"\n#include \"LinearSolver.h\"\n#include \"FEGlobalMatrix.h\"\n#include \"FELinearSystem.h\"\n#include \"FESolidDomain.h\"\n#include \"FEModel.h\"\n#include \"log.h\"\n\nBEGIN_FECORE_CLASS(FESurfaceToSurfaceVectorMap, FEElemDataGenerator)\n\tADD_PROPERTY(m_surf[0], \"surface1\", FEProperty::Reference);\n\tADD_PROPERTY(m_surf[1], \"surface2\", FEProperty::Reference);\n\tADD_PARAMETER(m_normal, \"normal\");\n\tADD_PARAMETER(m_cross, \"cross\");\n\tADD_PARAMETER(m_inAngle, \"in_angle\");\n\tADD_PARAMETER(m_outAngle, \"out_angle\");\n\tADD_PARAMETER(m_smoothIters, \"smooth_iters\");\nEND_FECORE_CLASS();\n\nFESurfaceToSurfaceVectorMap::FESurfaceToSurfaceVectorMap(FEModel* fem) : FEElemDataGenerator(fem)\n{\n\tm_surf[0] = nullptr;\n\tm_surf[1] = nullptr;\n\tm_normal = vec3d(0, 0, 1);\n\n\tm_cross = false;\n\tm_inAngle = 0;\n\tm_outAngle = 0;\n}\n\nFESurfaceToSurfaceVectorMap::~FESurfaceToSurfaceVectorMap()\n{\n}\n\nbool FESurfaceToSurfaceVectorMap::Init()\n{\n\tif ((m_surf[0] == nullptr) || (m_surf[1] == nullptr)) return false;\n\treturn FEMeshDataGenerator::Init();\n}\n\nFEDataMap* FESurfaceToSurfaceVectorMap::Generate()\n{\n\tFEElementSet* elset = GetElementSet();\n\tif (elset == nullptr) return nullptr;\n\n\tFEMesh& mesh = GetMesh();\n\n\tint NN = mesh.Nodes();\n\tvector<int> bn_mesh(NN, 0);\n\tvector<double> val_mesh(NN, 0.0);\n\n\tfor (int i = 0; i < 2; ++i)\n\t{\n\t\tFESurface* surf = m_surf[i];\n\t\tdouble v = (double)i;\n\n\t\tfor (int n = 0; n < surf->Nodes(); n++)\n\t\t{\n\t\t\tint nid = surf->NodeIndex(n);\n\t\t\tassert((nid >= 0) && (nid < NN));\n\t\t\tval_mesh[nid] = v;\n\t\t\tbn_mesh[nid] = 1;\n\t\t}\n\t}\n\n\tFENodeList nodeList = elset->GetNodeList();\n\tint nn = nodeList.Size();\n\tvector<int> bn(nn, 0);\n\tvector<double> val(nn, 0.0);\n\tfor (int i = 0; i < nn; ++i)\n\t{\n\t\tint globalIndex = nodeList[i];\n\t\tbn[i] = bn_mesh[globalIndex];\n\t\tval[i] = val_mesh[globalIndex];\n\t}\n\n\t// count equations\n\tint neq = 0;\n\tfor (int i = 0; i < nn; ++i)\n\t{\n\t\tif (bn[i] == 0)\n\t\t{\n\t\t\tbn[i] = neq++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbn[i] = -neq-2;\n\t\t\tneq++;\n\t\t}\n\t}\n\n\t// solve Laplace equation\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\tLinearSolver* ls = fecore.CreateDefaultLinearSolver(GetFEModel());\n\tif (ls == nullptr) return nullptr;\n\n\tSparseMatrix* A = ls->CreateSparseMatrix(Matrix_Type::REAL_SYMMETRIC);\n\tif (A == nullptr) return nullptr;\n\n\tFEGlobalMatrix K(A);\n\tFEModel dummy;\n\tstd::vector<double> b(neq, 0);\n\tFELinearSystem LS(&dummy, K, b, val, true);\n\n\t// build matrix profile\n\tK.build_begin(neq);\n\tfor (int i = 0; i < elset->Elements(); ++i)\n\t{\n\t\tFEElement& el = elset->Element(i);\n\t\tint ne = el.Nodes();\n\t\tstd::vector<int> lm(ne, -1);\n\t\tfor (int k=0; k<ne; ++k)\n\t\t{\n\t\t\tint l = nodeList.GlobalToLocalID(el.m_node[k]);\n\t\t\tlm[k] = bn[l];\n\t\t}\n\t\tK.build_add(lm);\n\t}\n\tK.build_end();\n\tA->Zero();\n\n\t// fill matrix and RHS\n\tFEElementMatrix ke;\n\tvector<int> lm;\n\tFEDomainList& domList = elset->GetDomainList();\n\tfor (int ndom = 0; ndom < domList.size(); ++ndom)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(*domList.GetDomain(ndom));\n\t\tfor (int m = 0; m < dom.Elements(); ++m)\n\t\t{\n\t\t\t// get the element\n\t\t\tFESolidElement& el = dom.Element(m);\n\n\t\t\tint neln = el.Nodes();\n\n\t\t\t// get the element stiffness matrix\n\t\t\tke.resize(neln, neln);\n\t\t\tlm.resize(neln);\n\t\t\tvector<vec3d> gradN(neln);\n\n\t\t\t// calculate stiffness\n\t\t\tint nint = el.GaussPoints();\n\n\t\t\t// gauss weights\n\t\t\tdouble* w = el.GaussWeights();\n\n\t\t\t// nodal coordinates\n\t\t\tvec3d rt[FEElement::MAX_NODES];\n\t\t\tfor (int j = 0; j < neln; ++j) rt[j] = mesh.Node(el.m_node[j]).m_rt;\n\n\t\t\t// repeat over integration points\n\t\t\tke.zero();\n\t\t\tfor (int n = 0; n < nint; ++n)\n\t\t\t{\n\t\t\t\tdouble Jt = dom.ShapeGradient0(el, n, gradN.data());\n\n\t\t\t\t// calculate stiffness component\n\t\t\t\tfor (int i = 0; i < neln; ++i) {\n\t\t\t\t\tfor (int j = 0; j < neln; ++j)\n\t\t\t\t\t\tke[i][j] += (gradN[i] * gradN[j]) * w[n] * Jt;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get the element's LM vector\n\t\t\tfor (int j = 0; j < el.Nodes(); ++j)\n\t\t\t{\n\t\t\t\tint m = nodeList.GlobalToLocalID(el.m_node[j]);\n\t\t\t\tlm[j] = bn[m];\n\t\t\t}\n\n\t\t\t// assemble element matrix in global stiffness matrix\n\t\t\tke.SetIndices(lm);\n\t\t\tLS.Assemble(ke);\n\t\t}\n\t}\n\n\t// solve linear system\n\tstd::vector<double> x(neq, 0);\n\tls->PreProcess();\n\tls->Factor();\n\tls->BackSolve(x, b);\n\tfor (int i = 0; i < nn; ++i)\n\t{\n\t\tint m = bn[i];\n\t\tif (m >= 0) val[i] = x[m];\n\t}\n\n\n\tvec3d N = m_normal;\n\tN.unit();\n\n\tFEDomainMap* map = new FEDomainMap(FEDataType::FE_VEC3D, Storage_Fmt::FMT_ITEM);\n\tmap->Create(elset);\n\n\tfor (int i=0; i<elset->Elements(); ++i)\n\t{\n\t\tFESolidElement& el = dynamic_cast<FESolidElement&>(elset->Element(i));\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(*el.GetMeshPartition());\n\n\t\tint ne = el.Nodes();\n\t\tdouble vn[FEElement::MAX_NODES];\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tint m = nodeList.GlobalToLocalID(el.m_node[j]);\n\t\t\tvn[j] = val[m];\n\t\t}\n\n\t\t// calculate average gradient\n\t\tvec3d grad(0,0,0);\n\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t{\n\t\t\tgrad += dom.gradient(el, vn, j);\n\t\t}\n\t\tgrad.Normalize();\n\n\t\tif (m_cross)\n\t\t{\n\t\t\tvec3d b = grad ^ N;\n\t\t\tb.unit();\n\t\t\tgrad = b;\n\t\t}\n\n\t\t// do plane rotations\n\t\tif (m_outAngle != 0)\n\t\t{\n\t\t\tvec3d a = grad ^ N;\n\t\t\tquatd q(m_outAngle * PI / 180.0, a);\n\t\t\tq.RotateVector(grad);\n\t\t}\n\n\t\tif (m_inAngle != 0)\n\t\t{\n\t\t\tquatd q(m_inAngle * PI / 180.0, N);\n\t\t\tq.RotateVector(grad);\n\t\t}\n\n\t\tmap->setValue(i, grad);\n\t}\n\treturn map;\n}\n"
  },
  {
    "path": "FECore/FESurfaceToSurfaceVectorMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FEDataGenerator.h\"\n\nclass FESurface;\n\nclass FESurfaceToSurfaceVectorMap : public FEElemDataGenerator\n{\npublic:\n\tFESurfaceToSurfaceVectorMap(FEModel* fem);\n\t~FESurfaceToSurfaceVectorMap();\n\n\tbool Init() override;\n\n\tFEDataMap* Generate() override;\n\nprivate:\n\tFESurface* m_surf[2];\n\n\tvec3d\tm_normal;\n\tdouble\tm_inAngle;\n\tdouble\tm_outAngle;\n\tbool m_cross;\n\n\tint m_smoothIters;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FETimeInfo.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETimeInfo.h\"\n#include \"DumpStream.h\"\n\nFETimeInfo::FETimeInfo()\n{\n\tcurrentTime = 0.0;\n\ttimeIncrement = 0.0;\n\talpha = 1.0;\n\tbeta = 0.25;\n\tgamma = 0.5;\n\talphaf = 1.0;\n\talpham = 1.0;\n\tcurrentIteration = 0;\n\taugmentation = 0;\n\ttimeStep = 0;\n}\n\nFETimeInfo::FETimeInfo(double time, double tinc)\n{\n\tcurrentTime = time;\n\ttimeIncrement = tinc;\n\talpha = 1.0;\n\tbeta = 0.25;\n\tgamma = 0.5;\n\talphaf = 1.0;\n\talpham = 1.0;\n\tcurrentIteration = 0;\n\taugmentation = 0;\n}\n\nvoid FETimeInfo::Serialize(DumpStream& ar)\n{\n\tar & currentTime;\n\tar & timeIncrement;\n\tar & currentIteration;\n\tar & augmentation;\n\n\tar & alpha;\n\tar & alphaf;\n\tar & alpham;\n\tar & beta;\n\tar & gamma;\n}\n"
  },
  {
    "path": "FECore/FETimeInfo.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\nclass DumpStream;\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FETimeInfo\n{\npublic:\n\tFETimeInfo();\n\n\tFETimeInfo(double time, double tinc);\n\n\tvoid Serialize(DumpStream& ar);\n\npublic:\n\tdouble\tcurrentTime;\t\t//!< current time value\n\tdouble\ttimeIncrement;\t\t//!< current time step (difference between this time and previous one)\n\tint\t\ttimeStep;\t\t\t//!< current time step\n\tint\t\tcurrentIteration;\t//!< iteration number\n\tint\t\taugmentation;\t\t//!< augmentation\n\n\t// HHT time integration parameters\n\tdouble\talpha;\n\tdouble\tbeta;\n\tdouble\tgamma;\n\tdouble  alphaf;\n\tdouble  alpham;\n};\n"
  },
  {
    "path": "FECore/FETimeStepController.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETimeStepController.h\"\n#include \"FELoadCurve.h\"\n#include \"FEAnalysis.h\"\n#include \"FEPointFunction.h\"\n#include \"DumpStream.h\"\n#include \"FEModel.h\"\n#include \"log.h\"\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(FETimeStepController, FEParamContainer)\n\tADD_PARAMETER(m_maxretries, \"max_retries\")->setLongName(\"max retries\");\n\tADD_PARAMETER(m_iteopt    , \"opt_iter\")->setLongName(\"optimal iterations\");\n\tADD_PARAMETER(m_dtmin     , \"dtmin\")->setLongName(\"min stepsize\");\n\tADD_PARAMETER(m_dtmax     , \"dtmax\")->setLongName(\"max stepsize\");\n\tADD_PARAMETER(m_naggr     , \"aggressiveness\");\n\tADD_PARAMETER(m_cutback   , \"cutback\");\n\tADD_PARAMETER(m_dtforce   , \"dtforce\");\n//\tADD_PARAMETER(m_must_points, \"must_points\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFETimeStepController::FETimeStepController(FEModel* fem) : FECoreBase(fem)\n{\n\tm_step = nullptr; // must be set with SetAnalysis\n\tm_nretries = 0;\n\tm_maxretries = 5;\n\tm_naggr = 0;\n\tm_cutback = 0.5;\n\tm_nmust = -1;\n\tm_next_must = -1;\n\tm_nmplc = -1;\n\tm_iteopt = 11;\n\tm_dtmin = 0;\n\tm_dtmax = 0.1;\n\n\tm_ddt = 0;\n\tm_dtp = 0;\n\tm_mp_repeat = false;\n\tm_mp_toff = 0.0;\n\n\tm_dtforce = false;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETimeStepController::SetAnalysis(FEAnalysis* step)\n{\n\tm_step = step;\n}\n\n//-----------------------------------------------------------------------------\n//! copy from\nvoid FETimeStepController::CopyFrom(FETimeStepController* tc)\n{\n\tassert(m_step);\n\n\tm_naggr = tc->m_naggr;\n\tm_nmplc = tc->m_nmplc;\n\tm_iteopt = tc->m_iteopt;\n\tm_dtmin = tc->m_dtmin;\n\tm_dtmax = tc->m_dtmax;\n\tm_cutback = tc->m_cutback;\n\n\tm_ddt = tc->m_ddt;\n\tm_dtp = tc->m_dtp;\n\n\tm_must_points = tc->m_must_points;\n}\n\n//-----------------------------------------------------------------------------\n// initialization\nbool FETimeStepController::Init()\n{\n\t// make sure we have a step assigned\n\tif (m_step == 0) return false;\n\n\t// steal the load curve param from the dtmax parameter\n\tFEParam* p = FindParameterFromData((void*) &m_dtmax); assert(p);\n\tFEModel* fem = GetFEModel();\n\tFELoadController* plc = fem->GetLoadController(p);\n\tif (plc)\n\t{\n\t\tm_nmplc = plc->GetID();\n\t\tfem->DetachLoadController(p);\n\n\t\t// print a warning that dtmax is ignored\n\t\tif (m_dtmax != 0)\n\t\t{\n\t\t\tfeLogWarning(\"dtmax is ignored when specifying must points.\");\n\t\t}\n\n\t\t// if a must-point curve is defined and the must-points are empty,\n\t\t// we copy the load curve points to the must-points\n\t\tif (m_must_points.empty())\n\t\t{\n\t\t\tFELoadCurve* lc = dynamic_cast<FELoadCurve*>(plc);\n\t\t\tif (lc)\n\t\t\t{\n\t\t\t\tPointCurve& f = lc->GetFunction();\n\t\t\t\t// make sure we have at least two points\n\t\t\t\tif (f.Points() < 2) return false;\n\t\t\t\tfor (int i = 0; i < f.Points(); ++i)\n\t\t\t\t{\n\t\t\t\t\tdouble ti = f.Point(i).x();\n\t\t\t\t\tm_must_points.push_back(ti);\n\t\t\t\t}\n\n\t\t\t\t// check for repeat setting\n\t\t\t\tif (f.GetExtendMode() == PointCurve::REPEAT) m_mp_repeat = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// initialize \"previous\" time step\n\tm_dtp = m_step->m_dt0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! reset\nvoid FETimeStepController::Reset()\n{\n\tm_dtp = m_step->m_dt0;\n\tm_nmust = -1;\n\tm_next_must = -1;\n\tm_mp_toff = 0.0;\n}\n\n//-----------------------------------------------------------------------------\n//! Restores data for a running restart\n\nvoid FETimeStepController::Retry()\n{\n\tFEModel* fem = m_step->GetFEModel();\n\tfeLogEx(fem, \"Retrying time step. Retry attempt %d of max %d\\n\\n\", m_nretries + 1, m_maxretries);\n\n\t// adjust time step\n\tdouble dt = m_step->m_dt;\n\tif (m_nretries == 0) m_ddt = (dt) / (m_maxretries + 1);\n\n\tdouble dtn;\n\tif (m_naggr == 0) dtn = dt - m_ddt;\n\telse dtn = dt*m_cutback;\n\n\tfeLogEx(fem, \"\\nAUTO STEPPER: retry step, dt = %lg\\n\\n\", dtn);\n\n\t// increase retry counter\n\tm_nretries++;\n\n\t// the new time step cannot be a must-point\n\tif (m_nmust != -1)\n\t{\n\t\t// if we were at a must-point, make sure we can hit this must-point again\n\t\tm_next_must--;\n\t\tm_nmust = -1;\n\t}\n\n\tm_dtp = dtn;\n\t\n\tm_step->m_dt = dtn;\n}\n\n//-----------------------------------------------------------------------------\n//! Adjusts the time step size based on the convergence information.\n//!\tIf the previous time step was able to converge in less than\n//! m_fem.m_iteopt iterations the step size is increased, else it\n//! is decreased.\n\nvoid FETimeStepController::AutoTimeStep(int niter)\n{\n\tFEModel* fem = m_step->GetFEModel();\n\tdouble dt = m_step->m_dt;\n\n\tdouble dtn = m_dtp;\n\tdouble told = fem->GetCurrentTime();\n\n\t// make sure the timestep size is at least the minimum\n\tif (dtn < m_dtmin) dtn = m_dtmin;\n\n\t// get the max time step\n\tdouble dtmax = m_dtmax;\n\n\t// If we have a must-point load curve\n\t// we take the max step size from the lc\n\tif (m_nmplc >= 0)\n\t{\n\t\tFELoadCurve& mpc = *(dynamic_cast<FELoadCurve*>(fem->GetLoadController(m_nmplc)));\n\t\tPointCurve& lc = mpc.GetFunction();\n\t\tdtmax = lc.value(told);\n\t}\n\n\t// adjust time step size\n\tif (m_dtforce)\n\t{\n\t\t// if the force flag is set, we just set the time step to the max value\n\t\tdtn = dtmax;\n\t}\n\telse if (niter > 0)\n\t{\n\t\tdouble scale = sqrt((double)m_iteopt / (double)niter);\n\n\t\t// Adjust time step size\n\t\tif (scale >= 1)\n\t\t{\n\t\t\tdtn = dtn + (dtmax - dtn) * MIN(.20, scale - 1);\n\t\t\tdtn = MIN(dtn, 5.0 * m_dtp);\n\t\t\tif (dtmax > 0) dtn = MIN(dtn, dtmax);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdtn = dtn - (dtn - m_dtmin) * (1 - scale);\n\t\t\tif (m_dtmin > 0) dtn = MAX(dtn, m_dtmin);\n\t\t\tif (dtmax   > 0) dtn = MIN(dtn, dtmax);\n\t\t}\n\t} \n\telse if (niter == 0)\n\t{\n\t\tif (m_dtmin > 0) dtn = MAX(dtn, m_dtmin);\n\t\tif (dtmax   > 0) dtn = MIN(dtn, dtmax);\n\t}\n\n\t// Report new time step size\n\tif (dtn > dt)\n\t\tfeLogEx(fem, \"\\nAUTO STEPPER: increasing time step, dt = %lg\\n\\n\", dtn);\n\telse if (dtn < dt)\n\t\tfeLogEx(fem, \"\\nAUTO STEPPER: decreasing time step, dt = %lg\\n\\n\", dtn);\n\n\t// Store this time step value. This is the value that will be used to evaluate\n\t// the next time step increment. This will not include adjustments due to the must-point\n\t// controller since this could create really small time steps that may be difficult to\n\t// recover from. \n\tm_dtp = dtn;\n\n\t// check for mustpoints\n\tif (m_must_points.empty() == false) dtn = CheckMustPoints(told, dtn);\n\n\t// make sure we are not exceeding the final time\n\tif (told + dtn > m_step->m_tend)\n\t{\n\t\tdtn = m_step->m_tend - told;\n\t\tfeLogEx(fem, \"MUST POINT CONTROLLER: adjusting time step. dt = %lg\\n\\n\", dtn);\n\t}\n\n\t// store time step size\n\tassert(dtn > 0);\n\tm_step->m_dt = dtn;\n}\n\n//-----------------------------------------------------------------------------\n//! This function makes sure that no must points are passed. It returns an\n//! updated value (less than dt) if t + dt would pass a must point. Otherwise\n//! it returns dt.\n//! \\param t current time\n//! \\param dt current time step\n//! \\return updated time step.\ndouble FETimeStepController::CheckMustPoints(double t, double dt)\n{\n\tFEModel* fem = m_step->GetFEModel();\n\tconst double eps = m_step->m_tend * 1e-12;\n\n\tm_nmust = -1;\n\tconst int points = (int)m_must_points.size();\n\n\tif (m_next_must >= points)\n\t{\n\t\tif (m_mp_repeat)\n\t\t{\n\t\t\tm_mp_toff += m_must_points.back();\n\t\t\tm_next_must = -1;\n\t\t}\n\t\telse return dt;\n\t}\n\n\t// set the first must-point if it has not been set\n\tif (m_next_must < 0)\n\t{\n\t\tm_next_must = 0;\n\t\twhile ((m_next_must < points) && (m_must_points[m_next_must] + m_mp_toff < t + eps))\n\t\t{\n\t\t\tm_next_must++;\n\t\t\tif (m_next_must >= points)\n\t\t\t{\n\t\t\t\tif (m_mp_repeat)\n\t\t\t\t{\n\t\t\t\t\tm_next_must = 0;\n\t\t\t\t\tm_mp_toff += m_must_points.back();\n\t\t\t\t}\n\t\t\t\telse return dt;\n\t\t\t}\n\t\t}\n\t}\n\n\tassert(m_next_must < points);\n\tdouble tmust = m_must_points[m_next_must] + m_mp_toff;\n\tassert(tmust + eps > t);\n\n\tdouble dtnew = dt;\n\tdouble tnew = t + dt;\n\tif (tmust < tnew + eps)\n\t{\n\t\tdtnew = tmust - t;\n\t\tfeLogEx(fem, \"MUST POINT CONTROLLER: adjusting time step. dt = %lg\\n\\n\", dtnew);\n\t\tm_nmust = m_next_must++;\n\t}\n\n\treturn dtnew;\n}\n\n//-----------------------------------------------------------------------------\n//! serialize\nvoid FETimeStepController::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\tar & m_nretries;\n\tar & m_nmplc;\n\tar & m_nmust;\n\tar & m_next_must;\n\tar & m_mp_toff;\n\tar & m_mp_repeat;\n\tar & m_ddt & m_dtp;\n\tar & m_step;\n\tar & m_must_points;\n}\n"
  },
  {
    "path": "FECore/FETimeStepController.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\nclass FEAnalysis;\nclass DumpStream;\n\n//-------------------------------------------------------------------\n// Class to control the time step\nclass FECORE_API FETimeStepController : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FETIMECONTROLLER_ID)\n\tFECORE_BASE_CLASS(FETimeStepController)\n\npublic:\n\tFETimeStepController(FEModel* fem);\n\n\tvoid SetAnalysis(FEAnalysis* step);\n\n\t// initialization\n\tbool Init() override;\n\n\t//! reset\n\tvoid Reset();\n\n\t//! serialize\n\tvoid Serialize(DumpStream& ar) override;\n\n\t//! copy from\n\tvoid CopyFrom(FETimeStepController* tc);\n\npublic:\n\t//! Do a running restart\n\tvoid Retry();\n\n\t//! Update Time step\n\tvoid AutoTimeStep(int niter);\n\n\t//! Adjust for must points\n\tdouble CheckMustPoints(double t, double dt);\n\nprivate:\n\tFEAnalysis*\tm_step;\n\npublic:\n\tint\t\tm_nretries;\t\t//!< nr of retries tried so far\n\tint\t\tm_maxretries;\t//!< max nr of retries allowed per time step\n\tint\t\tm_naggr;\t\t//!< aggressivness parameter\n\tint\t\tm_nmplc;\t\t//!< must point load curve number\n\tint\t\tm_nmust;\t\t//!< current must-point\n\tint\t\tm_next_must;\t//!< next must-point to visit\n\tint\t\tm_iteopt;\t\t//!< optimum nr of iterations\n\tdouble\tm_dtmin;\t\t//!< min time step size\n\tdouble\tm_dtmax;\t\t//!< max time step size\n\tdouble\tm_cutback;\t\t//!< cut back factor used in aggressive time stepping\n\n\tstd::vector<double>\tm_must_points;\t//!< the list of must-points\n\tbool\t\t\t\tm_mp_repeat;\t//!< repeat must-points\n\tdouble\t\t\t\tm_mp_toff;\t\t//!< offset for repeat must-points\n\npublic:\n\tdouble\tm_ddt;\t\t\t//!< used by auto-time stepper\n\tdouble\tm_dtp;\t\t\t//!< previous time step size\n\n\tbool\tm_dtforce;\t\t//!< force max time step\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/FETransform.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETransform.h\"\n\nTransform::Transform()\n{\n\tReset();\n}\n\nvoid Transform::Reset()\n{\n\tm_scl = vec3d(1, 1, 1);\n\tm_pos = vec3d(0, 0, 0);\n\tm_rot = quatd(0, 0, 0, 1);\n\tm_roti = quatd(0, 0, 0, 1);\n}\n\nvoid Transform::SetPosition(const vec3d& t)\n{\n\tm_pos = t;\n}\n\nvoid Transform::SetScale(double sx, double sy, double sz)\n{\n\tm_scl.x = sx;\n\tm_scl.y = sy;\n\tm_scl.z = sz;\n}\n\nvoid Transform::SetRotation(const quatd& q)\n{\n\tm_rot = q;\n\tm_roti = m_rot.Inverse();\n}\n\nvoid Transform::SetRotation(const vec3d& r)\n{\n\tm_rot = quatd(r*DEG2RAD);\n\tm_roti = m_rot.Inverse();\n}\n\nvoid Transform::SetRotation(double X, double Y, double Z)\n{\n\tX *= DEG2RAD;\n\tY *= DEG2RAD;\n\tZ *= DEG2RAD;\n\tm_rot.SetEuler(X, Y, Z);\n\tm_roti = m_rot.Inverse();\n}\n\nvec3d Transform::Apply(const vec3d& r) const\n{\n\tvec3d p(m_scl.x * r.x, m_scl.y * r.y, m_scl.z * r.z);\n\tm_rot.RotateVector(p);\n\tp += m_pos;\n\treturn p;\n}\n"
  },
  {
    "path": "FECore/FETransform.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"vec3d.h\"\n#include \"quatd.h\"\n\n//-----------------------------------------------------------------------------\n//! Class that defines an affine transformation (scale, rotate, translate).\n//! This currently applies the transformation as follows: \n//! 1. scale : the scale is applied in the local coordinate system\n//! 2. rotate: rotation from local to global coordinates\n//! 3. translate: translate to a global position\n//! \nclass Transform\n{\npublic:\n\tFECORE_API Transform();\n\n\t//! Reset the transform\n\tFECORE_API void Reset();\n\n\t//! set the scale factors\n\tFECORE_API void SetScale(double sx, double sy, double sz);\n\n\t//! set the scale of the object\n\tvoid SetScale(const vec3d& s) { m_scl = s; }\n\n\t//! get scale of the object\n\tconst vec3d& GetScale() const { return m_scl; }\n\n\t//! set the position (or translation)\n\tFECORE_API void SetPosition(const vec3d& t);\n\n\t//! get position of object\n\tconst vec3d& GetPosition() const { return m_pos; }\n\n\t//! set the rotation quaternion\n\tFECORE_API void SetRotation(const quatd& q);\n\n\t//! set the rotation vector (uses degrees)\n\tFECORE_API void SetRotation(const vec3d& r);\n\n\t//! set rotation via Euler angles Tait-Bryan (Z,Y,X) convention (in degrees)\n\tFECORE_API void SetRotation(double X, double Y, double Z);\n\n\t//! get orientation\n\tconst quatd& GetRotation() const { return m_rot; }\n\n\t//! get inverse of rotation\n\tquatd GetRotationInverse() const { return m_roti; }\n\n\t//! apply transformation\n\tFECORE_API vec3d Apply(const vec3d& r) const;\n\n\t//! translate the transform\n\tvoid Translate(const vec3d& dr);\n\n\t//! scale an object\n\tvoid Scale(double s, vec3d r, vec3d rc);\n\n\t//! rotate around the center rc\n\tvoid Rotate(quatd q, vec3d rc);\n\n\t//! Rotate angle w around an axis defined by the position vectors a, b.\n\tvoid Rotate(const vec3d& a, const vec3d& b, double w);\n\n\t//! comparison\n\tbool operator == (const Transform& T) const;\n\npublic:\n\t//! convert from local to global coordinates\n\tvec3d LocalToGlobal(const vec3d& r) const;\n\n\t//! convert from global to local coordinates\n\tvec3d GlobalToLocal(const vec3d& r) const;\n\n\t//! get a normal-like vector from global to local\n\tvec3d LocalToGlobalNormal(const vec3d& n) const;\n\n\t//! get a normal-like vector from global to local\n\tvec3d GlobalToLocalNormal(const vec3d& n) const;\n\nprivate:\n\tvec3d\tm_scl;\t\t//! scale factors\n\tvec3d\tm_pos;\t\t//! translation (global space)\n\tquatd\tm_rot;\t\t//! rotation\n\tquatd\tm_roti;\t\t//! inverse rotation\n};\n\ninline bool Transform::operator == (const Transform& T) const\n{\n\treturn ((m_pos == T.m_pos) && (m_scl == T.m_scl) && (m_rot == T.m_rot));\n}\n\ninline void Transform::Translate(const vec3d& dr) { m_pos += dr; }\n\n//! convert from local to global coordinates\ninline vec3d Transform::LocalToGlobal(const vec3d& r) const\n{\n\treturn m_pos + m_rot * vec3d(r.x * m_scl.x, r.y * m_scl.y, r.z * m_scl.z);\n}\n\n//! convert from global to local coordinates\ninline vec3d Transform::GlobalToLocal(const vec3d& r) const\n{\n\tvec3d p = m_roti * (r - m_pos);\n\treturn vec3d(p.x / m_scl.x, p.y / m_scl.y, p.z / m_scl.z);\n}\n\n//! get a normal-like vector from global to local\ninline vec3d Transform::LocalToGlobalNormal(const vec3d& n) const\n{\n\t// NOTE: scaling is turned off because this is used in the generation of material axes.\n\t//       If I use scaling the axes may no longer be orthogonal. Maybe I should create another\n\t//       function for this since this is now inconsistent with the reverse operation.\n//\t\treturn m_rot*vec3d(n.x / m_scl.x, n.y / m_scl.y, n.z / m_scl.z);\n\treturn m_rot * vec3d(n.x, n.y, n.z);\n}\n\n//! get a normal-like vector from global to local\ninline vec3d Transform::GlobalToLocalNormal(const vec3d& n) const\n{\n\tvec3d m = m_roti * n;\n\tm.x /= m_scl.x; m.y /= m_scl.y; m.z /= m_scl.z;\n\tm.Normalize();\n\treturn m;\n}\n\n//! scale \ninline void Transform::Scale(double s, vec3d r, vec3d rc)\n{\n\tvec3d r0 = GlobalToLocal(rc);\n\n\tdouble a = s - 1;\n\tm_roti.RotateVector(r);\n\tr.Normalize();\n\n\tr.x = 1 + a * fabs(r.x);\n\tr.y = 1 + a * fabs(r.y);\n\tr.z = 1 + a * fabs(r.z);\n\n\tm_scl.x *= r.x;\n\tm_scl.y *= r.y;\n\tm_scl.z *= r.z;\n\n\tm_pos -= LocalToGlobal(r0) - rc;\n}\n\n//! rotate around the center rc\ninline void Transform::Rotate(quatd q, vec3d rc)\n{\n\tm_rot = q * m_rot;\n\tm_roti = m_rot.Inverse();\n\n\tm_rot.MakeUnit();\n\tm_roti.MakeUnit();\n\n\tm_pos = rc + q * (m_pos - rc);\n}\n\n//! Rotate angle w around an axis defined by the position vectors a, b.\ninline void Transform::Rotate(const vec3d& a, const vec3d& b, double w)\n{\n\tdouble wr = PI * w / 180.0;\n\tvec3d n = (b - a); n.Normalize();\n\tquatd q(wr, n);\n\tRotate(q, a);\n}\n"
  },
  {
    "path": "FECore/FETrussDomain.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FETrussDomain.h\"\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\nFETrussDomain::FETrussDomain(FEModel* fem) : FEBeamDomain(fem)\n{\n}\n\n//-----------------------------------------------------------------------------\nbool FETrussDomain::Create(int nsize, FE_Element_Spec espec)\n{\n\tm_Elem.resize(nsize);\n\tfor (int i = 0; i < nsize; ++i)\n\t{\n\t\tFETrussElement& el = m_Elem[i];\n\t\tel.SetLocalID(i);\n\t\tel.SetMeshPartition(this);\n\t}\n\n\tif (espec.etype != FE_ELEM_INVALID_TYPE)\n\t\tfor (int i=0; i<nsize; ++i) m_Elem[i].SetType(espec.etype);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FETrussDomain::Init()\n{\n\tif (FEBeamDomain::Init() == false) return false;\n\tFEMesh& mesh = *GetMesh();\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tvec3d r0[2];\n\t\tr0[0] = mesh.Node(el.m_node[0]).m_r0;\n\t\tr0[1] = mesh.Node(el.m_node[1]).m_r0;\n\n\t\tel.m_L0 = (r0[1] - r0[0]).Length();\n\t\tel.m_Lt = el.m_L0;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvec3d FETrussDomain::GetTrussAxisVector(FETrussElement& el)\n{\n\tvec3d rt[2];\n\trt[0] = m_pMesh->Node(el.m_node[0]).m_rt;\n\trt[1] = m_pMesh->Node(el.m_node[1]).m_rt;\n\tvec3d n = rt[1] - rt[0];\n\tn.unit();\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\nvoid FETrussDomain::ForEachTrussElement(std::function<void(FETrussElement& el)> f)\n{\n\tfor (FETrussElement& el : m_Elem)\n\t{\n\t\tf(el);\n\t}\n}\n"
  },
  {
    "path": "FECore/FETrussDomain.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEBeamDomain.h\"\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for truss elements\nclass FECORE_API FETrussDomain : public FEBeamDomain\n{\npublic:\n\tFETrussDomain(FEModel* pm);\n\npublic:\n\tbool Create(int nsize, FE_Element_Spec espec) override;\n\n\tbool Init() override;\n\n\tint Elements() const override { return (int)m_Elem.size(); }\n\n\tFETrussElement& Element(int i) { return m_Elem[i]; }\n\n\tFEElement& ElementRef(int n) override { return m_Elem[n]; }\n\tconst FEElement& ElementRef(int n) const override { return m_Elem[n]; }\n\npublic:\n\tvoid ForEachTrussElement(std::function<void(FETrussElement& el)> f);\n\npublic:\n\t//! Calculate the truss normal\n\tvec3d GetTrussAxisVector(FETrussElement& el);\n\nprotected:\n\tvector<FETrussElement>\tm_Elem;\n};\n"
  },
  {
    "path": "FECore/FEValuator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FEValuator.h\"\n#include \"FEModelParam.h\"\n\nFEValuator::FEValuator(FEModel* fem) : FECoreBase(fem), m_param(nullptr) {}\n\nFEValuator::~FEValuator() {}\n\nvoid FEValuator::SetModelParam(FEModelParam* p) { m_param = p; }\n\nFEModelParam* FEValuator::GetModelParam() { return m_param; }\n\nvoid FEValuator::Serialize(DumpStream& ar)\n{\n\tFECoreBase::Serialize(ar);\n\tif (!ar.IsShallow()) ar& m_param;\n}\n"
  },
  {
    "path": "FECore/FEValuator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n\nclass FEMaterialPoint;\n\nclass FEModelParam;\n\n// Base class for evaluating model parameters\nclass FECORE_API FEValuator : public FECoreBase\n{\npublic:\n\tFEValuator(FEModel* fem);\n\tvirtual ~FEValuator();\n\n\tvoid SetModelParam(FEModelParam* p);\n\tFEModelParam* GetModelParam();\n\n\tvoid Serialize(DumpStream& ar) override;\n\nprivate:\n\tFEModelParam*\tm_param;\t//!< the model param that is using this valuator\n};\n"
  },
  {
    "path": "FECore/FEVec3dValuator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FEVec3dValuator.h\"\n\nFEVec3dValuator::FEVec3dValuator(FEModel* fem) : FEValuator(fem) \n{\n\n};\n"
  },
  {
    "path": "FECore/FEVec3dValuator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEValuator.h\"\n#include \"FEDataMap.h\"\n#include \"MathObject.h\"\n\n//---------------------------------------------------------------------------------------\n// Base class for evaluating vec3d parameters\nclass FECORE_API FEVec3dValuator : public FEValuator\n{\n\tFECORE_SUPER_CLASS(FEVEC3DVALUATOR_ID)\n\tFECORE_BASE_CLASS(FEVec3dValuator)\n\npublic:\n\tFEVec3dValuator(FEModel* fem);\n\npublic:\n\t// evaluate value at a material point\n\tvirtual vec3d operator()(const FEMaterialPoint& pt) = 0;\n\n\t// create a copy of the valuator\n\tvirtual FEVec3dValuator* copy() = 0;\n\n\t// is the valuator constant\n\tvirtual bool isConst() { return false; }\n\n\t// return the const value\n\tvirtual vec3d* constValue() { return nullptr; }\n\n\t// return a unit vector\n\tvec3d unitVector(const FEMaterialPoint& pt)\n\t{\n\t\tvec3d v = operator () (pt);\n\t\treturn v.Normalized();\n\t}\n};\n"
  },
  {
    "path": "FECore/FSPath.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <regex>\n#include <string.h>\n#include \"FSPath.h\"\n\n\nbool FSPath::isAbsolute(const char* path)\n{\n#ifdef WIN32\n\treturn std::regex_search(path, std::regex(\"^([A-Z]|[a-z])\\\\:[\\\\\\\\\\\\/]\"));\n#else\n\treturn std::regex_search(path, std::regex(\"^\\\\/\"));\n#endif\n}\n\nbool FSPath::isPath(const char* path)\n{\n\treturn std::regex_search(path, std::regex(\"\\\\/|\\\\\\\\\"));\n}\n\nvoid FSPath::filePath(char* filename, char* path)\n{\n\tstd::string name(filename);\n\n\tint index = name.find_last_of(\"/\\\\\");\n\n    if(index == std::string::npos)\n    {\n        strcpy(path,\"\");\n        return;\n    }\n\n#ifdef WIN32\n    char sep = '\\\\';\n#else\n    char sep = '/';\n#endif\n\n    snprintf(path, name.length(), \"%s%c\", name.substr(0,index).c_str(), sep);\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "FECore/FSPath.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n\nclass FECORE_API FSPath\n{\npublic:\n\tstatic bool isAbsolute(const char* path);\n\tstatic bool isPath(const char* path);\n\n\tstatic void filePath(char* filename, char* path);\n};\n\n"
  },
  {
    "path": "FECore/FaceDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"FaceDataRecord.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModel.h\"\n#include \"FESurface.h\"\n\n//-----------------------------------------------------------------------------\nFELogFaceData::FELogFaceData(FEModel* fem) : FELogData(fem) {}\n\n//-----------------------------------------------------------------------------\nFELogFaceData::~FELogFaceData() {}\n\n//-----------------------------------------------------------------------------\nFaceDataRecord::FaceDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_FACE) \n{\n\tm_surface = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nint FaceDataRecord::Size() const { return (int)m_Data.size(); }\n\n//-----------------------------------------------------------------------------\nvoid FaceDataRecord::SetData(const char* szexpr)\n{\n\tchar szcopy[MAX_STRING] = { 0 };\n\tstrcpy(szcopy, szexpr);\n\tchar* sz = szcopy, *ch;\n\tm_Data.clear();\n\tstrcpy(m_szdata, szexpr);\n\tdo\n\t{\n\t\tch = strchr(sz, ';');\n\t\tif (ch) *ch++ = 0;\n\t\tFELogFaceData* pdata = fecore_new<FELogFaceData>(sz, GetFEModel());\n\t\tif (pdata) m_Data.push_back(pdata);\n\t\telse throw UnknownDataField(sz);\n\t\tsz = ch;\n\t}\n\twhile (ch);\n}\n\n//-----------------------------------------------------------------------------\nbool FaceDataRecord::Initialize()\n{\n\treturn (m_item.empty() == false);\n}\n\n//-----------------------------------------------------------------------------\ndouble FaceDataRecord::Evaluate(int item, int ndata)\n{\n\tint nface = item - 1;\n\treturn m_Data[ndata]->value(m_surface->Element(nface));\n}\n\n//-----------------------------------------------------------------------------\nvoid FaceDataRecord::SelectAllItems()\n{\n\tassert(false);\n}\n\nvoid FaceDataRecord::SetItemList(FEItemList* itemList, const std::vector<int>& selection)\n{\n\tFEFacetSet* facetSet = dynamic_cast<FEFacetSet*>(itemList); assert(facetSet);\n\tm_surface = facetSet->GetSurface(); assert(m_surface);\n\tint n = m_surface->Elements();\n\tif (selection.empty())\n\t{\n\t\tm_item.resize(n);\n\t\tfor (int i = 0; i < n; ++i) m_item[i] = i + 1;\n\t}\n\telse\n\t{\n\t\tm_item = selection;\n\t}\n}\n"
  },
  {
    "path": "FECore/FaceDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n\nclass FESurface;\nclass FESurfaceElement;\n\n//-----------------------------------------------------------------------------\n//! This is the base class for a face data value.\nclass FECORE_API FELogFaceData : public FELogData\n{\n\tFECORE_SUPER_CLASS(FELOGFACEDATA_ID)\n\tFECORE_BASE_CLASS(FELogFaceData)\n\npublic:\n\tFELogFaceData(FEModel* fem);\n\tvirtual ~FELogFaceData();\n\tvirtual double value(FESurfaceElement& el) = 0;\n};\n\n//-----------------------------------------------------------------------------\n//! This class records surface data\nclass FECORE_API FaceDataRecord : public DataRecord\n{\npublic:\n\tFaceDataRecord(FEModel* pfem);\n\tdouble Evaluate(int item, int ndata) override;\n\tbool Initialize() override;\n\tvoid SetData(const char* sz) override;\n\tvoid SelectAllItems() override;\n\tint Size() const override;\n\n\tvoid SetItemList(FEItemList* itemList, const std::vector<int>& selection) override;\n\nprivate:\n\tFESurface*\tm_surface;\n\tvector<FELogFaceData*>\tm_Data;\n};\n"
  },
  {
    "path": "FECore/Integrate.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"Integrate.h\"\n#include \"FESolidDomain.h\"\n#include \"FELinearSystem.h\"\n#include \"FESolver.h\"\n\n//-----------------------------------------------------------------------------\nvoid FECORE_API IntegrateBDB(FESolidDomain& dom, FESolidElement& el, double D, matrix& ke)\n{\n\t// vector to store global shape functions\n\tconst int EN = FEElement::MAX_NODES;\n\tvec3d G[EN];\n\n\t// loop over all integration points\n\tconst double *gw = el.GaussWeights();\n\tint ne = el.Nodes();\n\tint ni = el.GaussPoints();\n\tfor (int n = 0; n<ni; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdouble detJt = dom.ShapeGradient(el, n, G);\n\n\t\t// form the matrix\n\t\tfor (int i = 0; i<ne; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tke[i][j] += (G[i]*G[j])*(D*detJt*gw[n]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FECORE_API IntegrateBDB(FESolidDomain& dom, FESolidElement& el, const mat3ds& D, matrix& ke)\n{\n\t// vector to store global shape functions\n\tconst int EN = FEElement::MAX_NODES;\n\tvec3d G[EN];\n\n\t// loop over all integration points\n\tconst double *gw = el.GaussWeights();\n\tint ne = el.Nodes();\n\tint ni = el.GaussPoints();\n\tfor (int n = 0; n<ni; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdouble detJt = dom.ShapeGradient(el, n, G);\n\n\t\t// form the matrix\n\t\tfor (int i = 0; i<ne; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tke[i][j] += (G[i] * (D * G[j]))*(detJt*gw[n]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nFECORE_API void IntegrateBDB(FESolidDomain& dom, FESolidElement& el, std::function<mat3ds(const FEMaterialPoint& mp)> D, matrix& ke)\n{\n\t// vector to store global shape functions\n\tconst int EN = FEElement::MAX_NODES;\n\tvec3d G[EN];\n\n\t// loop over all integration points\n\tconst double *gw = el.GaussWeights();\n\tint ne = el.Nodes();\n\tint ni = el.GaussPoints();\n\tfor (int n = 0; n<ni; ++n)\n\t{\n\t\t// get the material point\n\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t// calculate jacobian\n\t\tdouble detJt = dom.ShapeGradient(el, n, G);\n\n\t\t// calculate D at this point\n\t\tmat3ds Dn = D(mp);\n\n\t\t// form the matrix\n\t\tfor (int i = 0; i<ne; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tke[i][j] += (G[i] * (Dn * G[j]))*(detJt*gw[n]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid FECORE_API IntegrateNCN(FESolidDomain& dom, FESolidElement& el, double C, matrix& ke)\n{\n\t// number of nodes\n\tint ne = el.Nodes();\n\n\t// jacobian\n\tdouble Ji[3][3];\n\n\t// loop over all integration points\n\tconst double *gw = el.GaussWeights();\n\tint ni = el.GaussPoints();\n\tfor (int n = 0; n<ni; ++n)\n\t{\n\t\t// calculate jacobian\n\t\tdouble detJt = dom.invjact(el, Ji, n);\n\n\t\t// shape function values at integration point n\n\t\tdouble* H = el.H(n);\n\n\t\tfor (int i = 0; i<ne; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j<ne; ++j)\n\t\t\t{\n\t\t\t\tke[i][j] += H[i] * H[j]*C*detJt*gw[n];\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Generice integrator class for solid domains\nFECORE_API void AssembleSolidDomain(FESolidDomain& dom, FELinearSystem& ls, std::function<void(FESolidElement& el, matrix& ke)> elementIntegrand)\n{\n\t// loop over all elements in domain\n\tint NE = dom.Elements();\n#pragma omp parallel for shared (NE)\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint ndofs = dom.GetElementDofs(el);\n\n\t\t// build the element stiffness matrix\n\t\tFEElementMatrix ke(ndofs, ndofs);\n\t\telementIntegrand(el, ke);\n\n\t\t// set up the LM matrix\n\t\tvector<int> lm;\n\t\tdom.UnpackLM(el, lm);\n\n\t\t// assemble into global matrix\n\t\tke.SetNodes(el.m_node);\n\t\tke.SetIndices(lm);\n\t\tls.Assemble(ke);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Generic integrator class for solid domains\nFECORE_API void AssembleSolidDomain(FESolidDomain& dom, FEGlobalVector& R, std::function<void(FESolidElement& el, vector<double>& fe)> elementIntegrand)\n{\n\tint NE = dom.Elements();\n#pragma omp parallel for\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint ndofs = dom.GetElementDofs(el);\n\n\t\t// get element contribution\n\t\tvector<double> fe(ndofs, 0.0);\n\t\telementIntegrand(el, fe);\n\n\t\t// assemble into RHS\n\t\tvector<int> lm;\n\t\tdom.UnpackLM(el, lm);\n\t\tR.Assemble(lm, fe);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Generice integrator class for solid domains\nFECORE_API void IntegrateSolidDomain(FESolidDomain& dom, FELinearSystem& ls, std::function<void(FEMaterialPoint& mp, matrix& ke)> elementIntegrand)\n{\n\t// loop over all elements in domain\n\tint NE = dom.Elements();\n#pragma omp parallel for shared (NE)\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint ndofs = dom.GetElementDofs(el);\n\n\t\t// build the element stiffness matrix\n\t\tFEElementMatrix ke(ndofs, ndofs);\n\t\tke.zero();\n\t\tmatrix kn(ndofs, ndofs);\n\n\t\t// loop over all integration points\n\t\tint nint = el.GaussPoints();\n\t\tdouble* w = el.GaussWeights();\n\t\tfor (int n = 0; n < nint; ++n)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\n\t\t\t// evaluate the integration point's contribution\n\t\t\telementIntegrand(mp, kn);\n\n\t\t\t// add it to the element matrix\n\t\t\tke.adds(kn, w[n]);\n\t\t}\n\n\t\t// set up the LM matrix\n\t\tvector<int> lm;\n\t\tdom.UnpackLM(el, lm);\n\n\t\t// assemble into global matrix\n\t\tke.SetNodes(el.m_node);\n\t\tke.SetIndices(lm);\n\t\tls.Assemble(ke);\n\t}\n}"
  },
  {
    "path": "FECore/Integrate.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"matrix.h\"\n#include <functional>\n\n//-----------------------------------------------------------------------------\n// The purpose of this file is to explore mechanisms for evaluating the integrals \n// that commonly appear in FE formulations. The goal is that this would simplify \n// the implementation of new FE features.\n\n//-----------------------------------------------------------------------------\nclass FESolidDomain;\nclass FESolidElement;\nclass FEMaterialPoint;\nclass FELinearSystem;\nclass FESolver;\nclass FEGlobalVector;\n\n//-----------------------------------------------------------------------------\n// Integrator function for BDB forms\n// where B is the shape function gradients\nFECORE_API void IntegrateBDB(FESolidDomain& dom, FESolidElement& el, double D, matrix& ke);\nFECORE_API void IntegrateBDB(FESolidDomain& dom, FESolidElement& el, const mat3ds& D, matrix& ke);\nFECORE_API void IntegrateBDB(FESolidDomain& dom, FESolidElement& el, std::function<mat3ds (const FEMaterialPoint& mp)> f, matrix& ke);\n\n//-----------------------------------------------------------------------------\n// Integrator function for NCN forms\n// where N are the shape functions\nFECORE_API void IntegrateNCN(FESolidDomain& dom, FESolidElement& el, double C, matrix& ke);\n\n//-----------------------------------------------------------------------------\n// Generic integrator class for solid domains\n// Requires that the domain implements the GetElementDofs function.\nFECORE_API void AssembleSolidDomain(FESolidDomain& dom, FELinearSystem& ls, std::function<void(FESolidElement& el, matrix& ke)> elementIntegrand);\nFECORE_API void AssembleSolidDomain(FESolidDomain& dom, FEGlobalVector& R, std::function<void(FESolidElement& el, std::vector<double>& fe)> elementIntegrand);\n\nFECORE_API void IntegrateSolidDomain(FESolidDomain& dom, FELinearSystem& ls, std::function<void(FEMaterialPoint& mp, matrix& ke)> elementIntegrand);\n"
  },
  {
    "path": "FECore/JFNKMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"JFNKMatrix.h\"\n#include \"FENewtonSolver.h\"\n#include \"FEModel.h\"\n#include \"FEDomain.h\"\n\nJFNKMatrix::JFNKMatrix(FENewtonSolver* pns, SparseMatrix* K) : m_pns(pns), m_K(K)\n{\n\tm_nrow = m_ncol = pns->m_neq;\n\tm_nsize = 0;\n\n\tm_bauto_eps = false;\n\tm_eps = 1e-6;\n\n\tm_policy = ZERO_PRESCRIBED_DOFS;\n\n\t// TODO: For contact problems we'll need some mechanism to change the array size\n\tm_v.resize(m_nrow);\n\tm_R.resize(m_nrow);\n\n\t// figure out the free and prescribed equation numbers\n\tm_freeDofs.clear();\n\tm_prescribedDofs.clear();\n\n\tFEModel* fem = m_pns->GetFEModel();\n\tFEMesh& mesh = fem->GetMesh();\n\tfor (int i = 0; i < mesh.Nodes(); ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tfor (int j = 0; j < node.m_ID.size(); ++j)\n\t\t{\n\t\t\tint id = node.m_ID[j];\n\n\t\t\tif (id >= 0) m_freeDofs.push_back(id);\n\t\t\tif (id < -1) m_prescribedDofs.push_back(-id - 2);\n\t\t}\n\t}\n\n\t// Add element dofs\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFEDomain& dom = mesh.Domain(i);\n\t\tint NEL = dom.Elements();\n\t\tfor (int j = 0; j < NEL; ++j)\n\t\t{\n\t\t\tFEElement& elj = dom.ElementRef(j);\n\t\t\tif (elj.m_lm >= 0) m_freeDofs.push_back(elj.m_lm);\n\t\t}\n\t}\n\n\t// make sure it all matches\n\tassert(m_freeDofs.size() + m_prescribedDofs.size() == m_pns->m_neq);\n}\n\n//! set matrix policy\nvoid JFNKMatrix::SetPolicy(MultiplyPolicy p)\n{\n\tm_policy = p;\n}\n\n//! Set the forward difference epsilon\nvoid JFNKMatrix::SetEpsilon(double eps)\n{\n\tm_eps = eps;\n}\n\n//! Create a sparse matrix from a sparse-matrix profile\nvoid JFNKMatrix::Create(SparseMatrixProfile& MP) \n{ \n\tm_K->Create(MP); \n\tm_nrow = m_K->Rows();\n\tm_ncol = m_K->Columns();\n\tm_nsize = m_K->NonZeroes(); \n}\n\nvoid JFNKMatrix::SetReferenceResidual(std::vector<double>& R0)\n{\n\tm_R0 = R0;\n}\n\nbool JFNKMatrix::mult_vector(double* x, double* r)\n{\n\tint neq = (int)m_pns->m_ui.size();\n\n\tif (m_policy == ZERO_PRESCRIBED_DOFS)\n\t{\n\t\tfor (int i = 0; i < m_freeDofs.size(); ++i)\n\t\t{\n\t\t\tint id = m_freeDofs[i];\n\t\t\tm_v[id] = x[id];\n\t\t}\n\t\tfor (int i = 0; i < m_prescribedDofs.size(); ++i)\n\t\t{\n\t\t\tint id = m_prescribedDofs[i];\n\t\t\tm_v[id] = 0.0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < m_freeDofs.size(); ++i)\n\t\t{\n\t\t\tint id = m_freeDofs[i];\n\t\t\tm_v[id] = 0.0;\n\t\t}\n\t\tfor (int i = 0; i < m_prescribedDofs.size(); ++i)\n\t\t{\n\t\t\tint id = m_prescribedDofs[i];\n\t\t\tm_v[id] = x[id];\n\t\t}\n\t}\n\n\tdouble eps = m_eps;\n\tif (m_bauto_eps)\n\t{\n\t\tvector<double> u;\n\t\tm_pns->GetSolutionVector(u);\n\n\t\tassert(neq == Rows());\n\t\tdouble norm_v = l2_norm(m_v);\n\n\t\teps = 0.0;\n\t\tif (norm_v != 0.0)\n\t\t{\n\t\t\tfor (int i = 0; i < neq; ++i) eps += fabs(u[i]);\n\t\t\teps *= m_eps / (neq*norm_v);\n\t\t}\n\n\t\teps += m_eps;\n\t}\n\n\t// multiply by eps\n\tfor (int i = 0; i < neq; ++i) m_v[i] *= eps;\n\n\tm_pns->Update2(m_v);\n\tif (m_pns->Residual(m_R) == false) return false;\n\n\tfor (int i = 0; i < m_freeDofs.size(); ++i)\n\t{\n\t\tint id = m_freeDofs[i];\n\t\tr[id] = (m_R0[id] - m_R[id]) / eps;\n\t}\n\n\tif (m_policy == ZERO_PRESCRIBED_DOFS)\n\t{\n\t\tfor (int i = 0; i < m_prescribedDofs.size(); ++i)\n\t\t{\n\t\t\tint id = m_prescribedDofs[i];\n\t\t\tr[id] = x[id];\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (int i = 0; i < m_prescribedDofs.size(); ++i)\n\t\t{\n\t\t\tint id = m_prescribedDofs[i];\n\t\t\tr[id] = 0.0;\n\t\t}\n\t}\n\n\treturn  true;\n}\n"
  },
  {
    "path": "FECore/JFNKMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"SparseMatrix.h\"\n\nclass FENewtonSolver;\n\n//-----------------------------------------------------------------------------\n// This is a class that just mimics a sparse matrix.\n// It is only used by the JFNK strategy. \n// The only function it implements is the mult_vector.\nclass FECORE_API JFNKMatrix : public SparseMatrix\n{\npublic:\n\tenum MultiplyPolicy {\n\t\tZERO_FREE_DOFS,\n\t\tZERO_PRESCRIBED_DOFS\n\t};\n\npublic:\n\tJFNKMatrix(FENewtonSolver* pns, SparseMatrix* K = 0);\n\n\t//! override multiply with vector (Does not use sparse matrix m_K)\n\tbool mult_vector(double* x, double* r) override;\n\n\t//! set the reference residual\n\tvoid SetReferenceResidual(std::vector<double>& R0);\n\n\t//! set matrix policy\n\tvoid SetPolicy(MultiplyPolicy p);\n\n\t//! set the forward difference epsilon\n\tvoid SetEpsilon(double eps);\n\npublic: // these functions use the actual sparse matrix m_K\n\n\t//! set all matrix elements to zero\n\tvoid Zero() override { m_K->Zero(); }\n\n\t//! Create a sparse matrix from a sparse-matrix profile\n\tvoid Create(SparseMatrixProfile& MP) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override { m_K->Assemble(ke, lm); }\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override { m_K->Assemble(ke, lmi, lmj); }\n\n\t//! check if an entry was allocated\n\tbool check(int i, int j) override { return m_K->check(i, j); }\n\n\t//! set entry to value\n\tvoid set(int i, int j, double v) override { m_K->set(i, j, v); }\n\n\t//! add value to entry\n\tvoid add(int i, int j, double v) override { m_K->add(i, j, v); }\n\n\t//! retrieve value\n\tdouble get(int i, int j) override { return m_K->get(i, j); }\n\n\t//! get the diagonal value\n\tdouble diag(int i) override { return m_K->diag(i); }\n\n\t//! release memory for storing data\n\tvoid Clear() override { m_K->Clear(); }\n\n\t// interface to compact matrices\n\tdouble* Values() override { return m_K->Values(); }\n\tint*    Indices() override { return m_K->Indices(); }\n\tint*    Pointers() override { return m_K->Pointers(); }\n\tint     Offset() const override { return m_K->Offset(); }\n\nprivate:\n\tbool\t\t\tm_bauto_eps;\t// calculate epsilon automatically\n\tdouble\t\t\tm_eps;\t\t// forward difference epsilon\n\tSparseMatrix*\tm_K;\t\t// the actual sparse matrix (This is only used as a preconditioner and can be null)\n\tFENewtonSolver*\tm_pns;\n\tstd::vector<double>\tm_v, m_R;\n\n\tstd::vector<double>\tm_R0;\n\n\tstd::vector<int>\t\tm_freeDofs, m_prescribedDofs;\n\tMultiplyPolicy\tm_policy;\n};\n"
  },
  {
    "path": "FECore/JFNKStrategy.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"JFNKStrategy.h\"\n#include \"FENewtonSolver.h\"\n#include \"JFNKMatrix.h\"\n#include \"FEException.h\"\n#include \"LinearSolver.h\"\n#include \"log.h\"\n\nBEGIN_FECORE_CLASS(JFNKStrategy, FENewtonStrategy)\n\tADD_PARAMETER(m_jfnk_eps, \"jfnk_eps\");\nEND_FECORE_CLASS();\n\nJFNKStrategy::JFNKStrategy(FEModel* fem) : FENewtonStrategy(fem)\n{\n//\tm_jfnk_eps = 5e-12;\n\tm_jfnk_eps = 1e-6;\n\tm_bprecondition = false;\n\tm_A = nullptr;\n\n\tm_plinsolve = nullptr;\n\tm_neq = 0;\n}\n\n//! New initialization method\nbool JFNKStrategy::Init()\n{\n\tif (m_pns == nullptr) return false;\n\tm_plinsolve = m_pns->GetLinearSolver();\n\treturn true;\n}\n\nSparseMatrix* JFNKStrategy::CreateSparseMatrix(Matrix_Type mtype)\n{\n\tif (m_A) delete m_A;\n\tm_A = 0;\n\n\t// make sure the linear solver is an iterative linear solver\n\tIterativeLinearSolver* ls = dynamic_cast<IterativeLinearSolver*>(m_pns->m_plinsolve);\n\tif (ls)\n\t{\n\t\t// see if this solver has a preconditioner\n\t\tm_bprecondition = ls->HasPreconditioner();\n\n\t\t// if the solver has a preconditioner, we still need to create the stiffness matrix\n\t\tSparseMatrix* K = 0;\n\t\tif (m_bprecondition) \n\t\t{\n\t\t\tK = ls->CreateSparseMatrix(mtype);\n\t\t\tif (K == 0) return 0;\n\t\t}\n\n\t\t// Now, override the matrix used\n\t\tm_A = new JFNKMatrix(m_pns, K);\n\t\tls->SetSparseMatrix(m_A);\n\n\t\tm_A->SetEpsilon(m_jfnk_eps);\n\n\t\t// If there is no preconditioner we can do the pre-processing here\n\t\tif (m_bprecondition == false)\n\t\t{\n\t\t\tls->PreProcess();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ls->GetLeftPreconditioner()) ls->GetLeftPreconditioner()->SetSparseMatrix(K);\n\t\t\tif (ls->GetRightPreconditioner()) ls->GetRightPreconditioner()->SetSparseMatrix(K);\n\t\t}\n\t}\n\n\treturn m_A;\n}\n\n//! perform a quasi-Newton udpate\nbool JFNKStrategy::Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1)\n{\n\t// nothing to do here\n\treturn true;\n}\n\n//! solve the equations\nvoid JFNKStrategy::SolveEquations(vector<double>& x, vector<double>& b)\n{\n\t// perform a backsubstitution\n\tif (m_plinsolve->BackSolve(x, b) == false)\n\t{\n\t\tthrow LinearSolverFailed();\n\t}\n}\n\nbool JFNKStrategy::ReformStiffness()\n{\n\tif (m_bprecondition) return m_pns->ReformStiffness();\n\telse return true;\n}\n\n//! override so we can store a copy of the residual before we add Fd\nbool JFNKStrategy::Residual(std::vector<double>& R, bool binit)\n{\n\t// first calculate the residual\n\tbool b = m_pns->Residual(R);\n\tif (b == false) return false;\n\n\t// store a copy\n\tm_A->SetReferenceResidual(R);\n\n\t// at the first iteration we need to calculate the Fd\n\tif (binit)\n\t{\n\t\t// get the vector of prescribed displacements\n\t\tstd::vector<double>& ui = m_pns->m_ui;\n\n\t\t// build m_Fd\n\t\tvector<double>& Fd = m_pns->m_Fd;\n\t\tm_A->SetPolicy(JFNKMatrix::ZERO_FREE_DOFS);\n\t\tm_A->mult_vector(&ui[0], &Fd[0]);\n\t\tm_A->SetPolicy(JFNKMatrix::ZERO_PRESCRIBED_DOFS);\n\n\t\t// we need to flip the sign on Fd\n\t\tfor (size_t i = 0; i < Fd.size(); ++i) Fd[i] = -Fd[i];\n\n\t\t// NOTE: Usually, the residual is called after an Update. However, here, \n\t\t//       the model is updated (via Update2) in m_A->mult_vector. I wonder\n\t\t//       if I need to restore the model's state to before the mult_vector call\n\t\t//       by calling another Update2 method with a zero vector.\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/JFNKStrategy.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FENewtonStrategy.h\"\n#include \"SparseMatrix.h\"\n\nclass JFNKMatrix;\n\n//-----------------------------------------------------------------------------\n// Implements a Jacobian-Free Newton-Krylov strategy\nclass JFNKStrategy : public FENewtonStrategy\n{\npublic:\n\tJFNKStrategy(FEModel* fem);\n\n\t//! New initialization method\n\tbool Init() override;\n\n\t//! initialize the linear system\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type mtype) override;\n\n\t//! perform a BFGS udpate\n\tbool Update(double s, vector<double>& ui, vector<double>& R0, vector<double>& R1) override;\n\n\t//! solve the equations\n\tvoid SolveEquations(vector<double>& x, vector<double>& b) override;\n\n\t//! Overide reform stiffness because we don't want to do any reformations\n\tbool ReformStiffness() override;\n\n\t//! override so we can store a copy of the residual before we add Fd\n\tbool Residual(std::vector<double>& R, bool binit) override;\n\nprivate:\n\tdouble\t\t\t\tm_jfnk_eps;\t\t\t//!< JFNK epsilon\n\npublic:\n\t// keep a pointer to the linear solver\n\tLinearSolver*\tm_plinsolve;\t\t//!< pointer to linear solver\n\tint\t\t\t\tm_neq;\t\t\t\t//!< number of equations\n\tbool\t\t\tm_bprecondition;\t//!< the solver requires preconditioning\n\n\tJFNKMatrix*\t\tm_A;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FECore/LUSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"LUSolver.h\"\n#include <math.h>\n\n//-----------------------------------------------------------------------------\nLUSolver::LUSolver(FEModel* fem) : LinearSolver(fem), m_pA(nullptr)\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! Create a sparse matrix\nSparseMatrix* LUSolver::CreateSparseMatrix(Matrix_Type ntype)\n{ \n\treturn (m_pA = new FECore::DenseMatrix()); \n}\n\n//-----------------------------------------------------------------------------\nvoid LUSolver::SetMatrix(FECore::DenseMatrix* pA) \n{ \n\tm_pA = pA; \n}\n\n//-----------------------------------------------------------------------------\nbool LUSolver::PreProcess()\n{\n\t// We don't need to do any preprocessing for this solver\n\treturn LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool LUSolver::Factor()\n{\n\tFECore::DenseMatrix& a = *m_pA;\n\n\tconst double TINY = 1.0e-20;\n\tint i, imax, j, k;\n\tdouble big, dum, sum, temp;\n\n\tint n = a.Rows();\n\t// create index vector\n\tindx.resize(n);\n\n\tvector<double> vv(n);\n\tfor (i=0; i<n; ++i)\n\t{\n\t\tbig = 0;\n\t\tfor (j=0; j<n; ++j)\n\t\t\tif ((temp=fabs(a(i,j))) > big) big = temp;\n\t\tif (big == 0) return false; // singular matrix\n\t\tvv[i] = 1.0 / big;\n\t}\n\n\tfor (j=0; j<n; ++j)\n\t{\n\t\tfor (i=0; i<j; ++i)\n\t\t{\n\t\t\tsum = a(i,j);\n\t\t\tfor (k=0; k<i; ++k) sum -= a(i,k)*a(k,j);\n\t\t\ta(i,j) = sum;\n\t\t}\n\t\tbig = 0;\n\t\timax = j;\n\t\tfor (i=j;i<n;++i)\n\t\t{\n\t\t\tsum = a(i,j);\n\t\t\tfor (k=0; k<j; ++k) sum -= a(i,k)*a(k,j);\n\t\t\ta(i,j) = sum;\n\t\t\tif ((dum=vv[i]*fabs(sum))>=big)\n\t\t\t{\n\t\t\t\tbig = dum;\n\t\t\t\timax = i;\n\t\t\t}\n\t\t}\n\n\t\tif (j != imax)\n\t\t{\n\t\t\tfor (k=0; k<n; ++k)\n\t\t\t{\n\t\t\t\tdum = a(imax,k);\n\t\t\t\ta(imax,k) = a(j,k);\n\t\t\t\ta(j,k) = dum;\n\t\t\t}\n\t\t\tvv[imax] = vv[j];\n\t\t}\n\n\t\tindx[j] = imax;\n\t\tif (a(j,j) == 0) a(j,j) = TINY;\n\t\tif (j != n-1)\n\t\t{\n\t\t\tdum = 1.0/a(j,j);\n\t\t\tfor (i=j+1;i<n; ++i) a(i,j) *= dum;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool LUSolver::BackSolve(double* x, double* b)\n{\n\tFECore::DenseMatrix& a = *m_pA;\n\n\tint n = a.Rows();\n\tfor (int i=0; i<n; i++) x[i] = b[i];\n\n\tint ii=0;\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tint ip = indx[i];\n\t\tdouble sum = x[ip];\n\t\tx[ip] = x[i];\n\t\tif (ii != 0)\n\t\t\tfor (int j=ii-1;j<i;++j) sum -= a(i,j)*x[j];\n\t\telse if (sum != 0)\n\t\t\tii = i+1;\n\t\tx[i] = sum;\n\t}\n\n\tfor (int i=n-1; i>=0; --i)\n\t{\n\t\tdouble sum = x[i];\n\t\tfor (int j=i+1; j<n; ++j) sum -= a(i,j)*x[j];\n\t\tx[i] = sum/a(i,i);\n\t}\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid LUSolver::Destroy()\n{\n\t// nothing to destroy\n\tLinearSolver::Destroy();\n}\n"
  },
  {
    "path": "FECore/LUSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"LinearSolver.h\"\n#include \"DenseMatrix.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\n//! LU decomposition solver\n\n//! This solver performs an LU decomposition and uses a backsolving algorithm\n//! to solve the equations.\n//! This solver uses the FullMatrix class and therefore is not the preferred\n//! solver. It should only be used for small problems and only when the other\n//! solvers are not adequate.\n\nclass FECORE_API LUSolver : public LinearSolver\n{\npublic:\n\t//! constructor\n\tLUSolver(FEModel* fem = nullptr);\n\n\t//! Pre-process data\n\tbool PreProcess() override;\n\n\t//! Factor matrix\n\tbool Factor() override;\n\n\t//! solve using factored matrix\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Clean-up\n\tvoid Destroy() override;\n\n\t//! Create a sparse matrix\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! Set the matrix\n\tvoid SetMatrix(FECore::DenseMatrix* pA);\n\nprotected:\n\tstd::vector<int>\t\tindx;\t//!< indices\n\tFECore::DenseMatrix*\tm_pA;\t//!< sparse matrix\n};\n"
  },
  {
    "path": "FECore/LinearSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"LinearSolver.h\"\n\n//-----------------------------------------------------------------------------\nLinearSolver::LinearSolver(FEModel* fem) : FECoreBase(fem)\n{\n\tResetStats();\n}\n\n//-----------------------------------------------------------------------------\nLinearSolver::~LinearSolver()\n{ \n\tDestroy();\n}\n\n//-----------------------------------------------------------------------------\n// returns whether this is an iterative solver or not\nbool LinearSolver::IsIterative() const\n{\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool LinearSolver::PreProcess()\n{ \n\treturn true; \n}\n\n//-----------------------------------------------------------------------------\nvoid LinearSolver::SetPartitions(const vector<int>& part)\n{\n\tm_part = part;\n}\n\n//-----------------------------------------------------------------------------\nvoid LinearSolver::SetPartitions(int npart0, int npart1)\n{\n\tm_part.resize(2);\n\tm_part[0] = npart0;\n\tm_part[1] = npart1;\n}\n\n//-----------------------------------------------------------------------------\n// nr of partitions\nint LinearSolver::Partitions() const\n{\n\treturn (int)m_part.size();\n}\n\n//-----------------------------------------------------------------------------\n// get the size of a partition\nint LinearSolver::GetPartitionSize(int part) const\n{\n\treturn m_part[part];\n}\n\n//-----------------------------------------------------------------------------\nconst LinearSolverStats& LinearSolver::GetStats() const\n{\n\treturn\tm_stats;\n}\n\ndouble LinearSolver::ConditionNumber()\n{\n\t// returns an invalid value for the condition number\n\treturn 0.0;\n}\n\n//-----------------------------------------------------------------------------\nvoid LinearSolver::ResetStats()\n{\n\tm_stats.backsolves = 0;\n\tm_stats.iterations = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid LinearSolver::UpdateStats(int iterations)\n{\n\tm_stats.backsolves++;\n\tm_stats.iterations += iterations;\n}\n\n//-----------------------------------------------------------------------------\nvoid LinearSolver::Destroy()\n{\n\n}\n\n//-----------------------------------------------------------------------------\n//! helper function for when this solver is used as a preconditioner\nbool LinearSolver::mult_vector(double* x, double* y)\n{\n\treturn BackSolve(y, x);\n}\n\n//-----------------------------------------------------------------------------\nbool LinearSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n\tassert(false);\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n//! convenience function for solving linear systems\nbool LinearSolver::Solve(vector<double>& x, vector<double>& y)\n{\n\tif (PreProcess() == false) return false;\n\tif (Factor() == false) return false;\n\tif (BackSolve(x, y) == false) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nIterativeLinearSolver::IterativeLinearSolver(FEModel* fem) : LinearSolver(fem) \n{\n\n}\n\n//! convenience function for solving linear systems with an iterative solver\nbool IterativeLinearSolver::Solve(SparseMatrix& A, std::vector<double>& x, std::vector<double>& b, LinearSolver* pc)\n{\n\tif (SetSparseMatrix(&A) == false) return false;\n\tSetLeftPreconditioner(pc);\n\tif (PreProcess() == false) return false;\n\tif (Factor() == false) return false;\n\treturn BackSolve(x, b);\n}\n\n// returns whether this is an iterative solver or not\nbool IterativeLinearSolver::IsIterative() const\n{\n\treturn true;\n}\n\nvoid IterativeLinearSolver::SetLeftPreconditioner(LinearSolver* pc) { assert(false); }\nvoid IterativeLinearSolver::SetRightPreconditioner(LinearSolver* pc) { assert(false); }\n\n// get the preconditioner\nLinearSolver* IterativeLinearSolver::GetLeftPreconditioner() { return nullptr; }\nLinearSolver* IterativeLinearSolver::GetRightPreconditioner() { return nullptr; }\n"
  },
  {
    "path": "FECore/LinearSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"SparseMatrix.h\"\n#include \"FECoreBase.h\"\n#include \"fecore_enum.h\"\n#include <vector>\n\nclass FEModel;\n\n//-----------------------------------------------------------------------------\nstruct FECORE_API LinearSolverStats\n{\n\tint\t\tbacksolves;\t\t// number of times backsolve was called\n\tint\t\titerations;\t\t// total number of iterations\n};\n\n//-----------------------------------------------------------------------------\n//! Abstract base class for the linear solver classes. Linear solver classes\n//! are derived from this class and must implement the abstract virtual methods.\n\n//! This class assumes that a linear system is solved in two steps. First, the Factor()\n//! method factorizes the matrix, and then BackSolve() solves the system for a given \n//! right hand side vector using the previously factored matrix. \n\nclass FECORE_API LinearSolver : public FECoreBase\n{\n\tFECORE_SUPER_CLASS(FELINEARSOLVER_ID)\n\tFECORE_BASE_CLASS(LinearSolver)\n\npublic:\n\t//! constructor\n\tLinearSolver(FEModel* fem);\n\n\t//! destructor\n\tvirtual ~LinearSolver();\n\n\tvirtual void SetPrintLevel(int n) {}\n\npublic:\n\n\t//! create a sparse matrix that can be used with this solver (must be overridden)\n\tvirtual SparseMatrix* CreateSparseMatrix(Matrix_Type ntype) = 0;\n\n\t//! Set the sparse matrix\n\tvirtual bool SetSparseMatrix(SparseMatrix* pA);\n\n\t//! Perform any preprocessing\n\t//! This is called after the structure of the stiffness matrix was determined. \n\t//! At this point, we know the size of the matrix and its sparsity pattern.\n\tvirtual bool PreProcess();\n\n\t//! Factor the matrix (must be overridden)\n\t//! Iterative solvers can use this function for creating a pre-conditioner.\n\tvirtual bool Factor() = 0;\n\n\t//! do a backsolve, i.e. solve for a right-hand side vector y (must be overridden)\n\tvirtual bool BackSolve(double* x, double* y) = 0;\n\n\t//! Do any cleanup\n\tvirtual void Destroy();\n\n\t//! helper function for when this solver is used as a preconditioner\n\tvirtual bool mult_vector(double* x, double* y);\n\n\t//! Used by block solvers do determine the block partition\n\t//! The partition is where the global matrix will be divided into blocks\n\tvoid SetPartitions(const vector<int>& part);\n\tvoid SetPartitions(int npart0, int npart1);\n\n\t// nr of partitions\n\tint Partitions() const;\n\n\t// get the size of a partition\n\tint GetPartitionSize(int part) const;\n\n\t//! version for std::vector\n\tbool BackSolve(std::vector<double>& x, std::vector<double>& b)\n\t{\n\t\treturn BackSolve(&x[0], &b[0]);\n\t}\n\n\t//! convenience function for solving linear systems\n\tbool Solve(vector<double>& x, vector<double>& y);\n\n\t// returns whether this is an iterative solver or not\n\tvirtual bool IsIterative() const;\n\npublic:\n\tconst LinearSolverStats& GetStats() const;\n\n\tvoid ResetStats();\n\n\t// Calculate (or estimate) condition number. Must be implemented by derived class.\n\t// Base class returns 0;\n\tvirtual double ConditionNumber();\n\nprotected:\n\t// used by derived classes to update stats.\n\t// Should be called after each backsolve. Will increment backsolves by one and add iterations\n\tvoid UpdateStats(int iterations);\n\nprotected:\n\tstd::vector<int>\tm_part;\t\t//!< partitions of linear system.\n\nprivate:\n\tLinearSolverStats\tm_stats;\t//!< stats on how often linear solver was called.\n};\n\n//-----------------------------------------------------------------------------\n// base class for iterative solvers\nclass FECORE_API IterativeLinearSolver : public LinearSolver\n{\npublic:\n\t// constructor\n\tIterativeLinearSolver(FEModel* fem);\n\n\t// return whether the iterative solver has a preconditioner or not\n\tvirtual bool HasPreconditioner() const = 0;\n\n\t// set the preconditioner\n\tvirtual void SetLeftPreconditioner(LinearSolver* pc);\n\tvirtual void SetRightPreconditioner(LinearSolver* pc);\n\n\t// get the preconditioner\n\tvirtual LinearSolver* GetLeftPreconditioner();\n\tvirtual LinearSolver* GetRightPreconditioner();\n\n\t// returns whether this is an iterative solver or not\n\tbool IsIterative() const override;\n\npublic:\n\t// helper function for solving a linear system of equations\n\tbool Solve(SparseMatrix& A, std::vector<double>& x, std::vector<double>& b, LinearSolver* pc = 0);\n};\n"
  },
  {
    "path": "FECore/MCollect.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\n\n//---------------------------------------------------\nvoid MCollect(const MITEM& e, const MITEM& x, MITEM& a, MITEM& b);\n\n//---------------------------------------------------\n// collect terms in x\nMITEM MCollect(const MITEM& e, const MITEM& x)\n{\n\tMITEM a(0.0);\n\tMITEM b(0.0);\n\tMCollect(e, x, a, b);\n\treturn MEvaluate(a*x + b);\n}\n\n//---------------------------------------------------\nvoid MCollect(const MITEM& e, const MITEM& x, MITEM& a, MITEM& b)\n{\n\t// check for equality\n\tif (e == x)\n\t{\n\t\ta = a + 1.0;\n\t\treturn;\n\t}\n\n\t// check for dependancy\n\tif (is_dependent(e, x) == false)\n\t{\n\t\tb = MEvaluate(b + e);\n\t\treturn;\n\t}\n\n\t// process operators\n\tswitch (e.Type())\n\t{\n\tcase MNEG:\n\t\t{\n\t\t\tMITEM c(0.0), d(0.0);\n\t\t\tMCollect(e.Item(), x, c, d);\n\t\t\ta = a - c;\n\t\t\tb = b - d;\n\t\t}\n\t\tbreak;\n\tcase MADD:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_dependent(l, x) == false)\n\t\t\t{\n\t\t\t\tb = MEvaluate(b + l);\n\t\t\t\tMCollect(r, x, a, b);\n\t\t\t}\n\t\t\telse if (is_dependent(r, x) == false)\n\t\t\t{\n\t\t\t\tb = MEvaluate(b + r);\n\t\t\t\tMCollect(l, x, a, b);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMITEM al(0.0), ar(0.0), bl(0.0), br(0.0);\n\t\t\t\tMCollect(l, x, al, bl);\n\t\t\t\tMCollect(r, x, ar, br);\n\t\t\t\ta = a + al + ar;\n\t\t\t\tb = b + bl + br;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase MSUB:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_dependent(l, x) == false)\n\t\t\t{\n\t\t\t\tb = MEvaluate(b + l);\n\t\t\t\tMCollect(-r, x, a, b);\n\t\t\t}\n\t\t\telse if (is_dependent(r, x) == false)\n\t\t\t{\n\t\t\t\tb = MEvaluate(b - r);\n\t\t\t\tMCollect(l, x, a, b);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMITEM al(0.0), ar(0.0), bl(0.0), br(0.0);\n\t\t\t\tMCollect(l, x, al, bl);\n\t\t\t\tMCollect(r, x, ar, br);\n\t\t\t\ta = a + al - ar;\n\t\t\t\tb = b + bl - br;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (l == x) a = a + r;\n\t\t\telse if (r == x) a = a + l;\n\t\t\telse if (is_dependent(l, x) == false)\n\t\t\t{\n\t\t\t\tMCollect(r, x, a, b);\n\t\t\t\ta = MEvaluate(l*a);\n\t\t\t\tb = MEvaluate(l*b);\n\t\t\t}\n\t\t\telse if (is_dependent(r, x) == false)\n\t\t\t{\n\t\t\t\tMCollect(l, x, a, b);\n\t\t\t\ta = MEvaluate(r*a);\n\t\t\t\tb = MEvaluate(r*b);\n\t\t\t}\n\t\t\telse b = b + e;\n\t\t}\n\t\tbreak;\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_dependent(l, x))\n\t\t\t{\n\t\t\t\tMCollect(l, x, a, b);\n\t\t\t\ta = MEvaluate(a/r);\n\t\t\t\tb = MEvaluate(b/r);\n\t\t\t}\n\t\t\telse b = b + e;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tb = b + e;\n\t}\n}\n"
  },
  {
    "path": "FECore/MDerive.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\nusing namespace std;\n\n#ifndef PI\n#define PI\t3.141592653589793\n#endif\n\n//-----------------------------------------------------------------------------\nMITEM MDerive(const MITEM& a, const MVariable& x)\n{\n\tMITEM e = MEvaluate(a);\n\n\tif (is_dependent(e, x) == false) return 0.0;\n\n\tswitch (e.Type())\n\t{\n\tcase MCONST:\n\tcase MFRAC:\n\tcase MNAMED: return 0.0; \n\tcase MVAR: if (e == x) return 1.0; else return 0.0;\n\tcase MNEG: return -MDerive(-e, x); \n\tcase MADD: return (MDerive(e.Left(), x) + MDerive(e.Right(), x));\n\tcase MSUB: \n\t\t{\n\t\t\tMITEM l = MDerive(e.Left(), x);\n\t\t\tMITEM r = MDerive(e.Right(), x);\n\t\t\treturn l - r;\n\t\t}\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM dl = MDerive(l, x);\n\t\t\tMITEM r = e.Right();\n\t\t\tMITEM dr = MDerive(r, x);\n\n\t\t\treturn (dl*r + l*dr);\n\t\t}\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tMITEM dl = MDerive(l, x)*r;\n\t\t\tMITEM dr = l*MDerive(r, x);\n\t\t\t// be careful here that we are not subtracting two pointers\n\t\t\tMITEM a = dl/(r^2.0);\n\t\t\tMITEM b = dr/(r^2.0);\n\t\t\treturn (a-b);\n\t\t}\n\tcase MPOW:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_dependent(r, x) == false) return (r*(l^(r-1.0)))*MDerive(l, x);\n\t\t\telse if (is_dependent(l, x) == false) return (Log(l)*e)*MDerive(r, x);\n\t\t\telse\n\t\t\t{\n\t\t\t\tMITEM dl = MDerive(l, x);\n\t\t\t\tMITEM dr = MDerive(r, x);\n\t\t\t\treturn (e*(dr*Log(l) + (r*dl)/l));\n\t\t\t}\n\t\t}\n\tcase MF1D:\n\t\t{\n\t\t\tconst string& s = mfnc1d(e)->Name();\n\t\t\tMITEM p = mfnc1d(e)->Item()->copy();\n\t\t\tMITEM dp = MDerive(p,x);\n\t\t\tif (s.compare(\"cos\") == 0) return -Sin(p)*dp;\n\t\t\tif (s.compare(\"sin\") == 0) return  Cos(p)*dp;\n\t\t\tif (s.compare(\"tan\") == 0) return (Sec(p)^2.0)*dp;\n\t\t\tif (s.compare(\"sec\") == 0) return (Sec(p)*Tan(p))*dp;\n\t\t\tif (s.compare(\"csc\") == 0) return (-Csc(p)*Cot(p))*dp;\n\t\t\tif (s.compare(\"cot\") == 0) return -((Csc(p)^2.0)*dp);\n\t\t\tif (s.compare(\"abs\") == 0) return Sgn(p)*dp;\n\t\t\tif (s.compare(\"ln\" ) == 0) return (dp/p);\n\t\t\tif (s.compare(\"log\") == 0) return (dp/p)/Log(MITEM(10.0));\n\t\t\tif (s.compare(\"asin\") == 0) return (dp/Sqrt(1.0 - (p^2)));\n\t\t\tif (s.compare(\"acos\") == 0) return (-dp/Sqrt(1.0 - (p^2)));\n\t\t\tif (s.compare(\"atan\") == 0) return (dp/((p^2) + 1.0));\n\t\t\tif (s.compare(\"cosh\") == 0) return (dp*Sinh(p));\n\t\t\tif (s.compare(\"sinh\") == 0) return (dp*Cosh(p));\n\t\t\tif (s.compare(\"sqrt\") == 0) return (dp/(2.0*Sqrt(p)));\n\t\t\tif (s.compare(\"acosh\") == 0) return (dp/Sqrt((p^2) - 1.0));\n#ifdef WIN32\n\t\t\tif (s.compare(\"J0\"  ) == 0) return (-J1(p))*dp;\n\t\t\tif (s.compare(\"J1\"  ) == 0) return dp*(J0(p) - Jn(2, p))/2.0;\n\t\t\tif (s.compare(\"Y0\"  ) == 0) return (-Y1(p))*dp;\n\t\t\tif (s.compare(\"Y1\"  ) == 0) return dp*(Y0(p) - Yn(2, p))/2.0;\n#endif\n\t\t\tif (s.compare(\"erf\" ) == 0)\n\t\t\t{\n\t\t\t\tMITEM Pi = new MNamedCt(PI, \"pi\");\n\t\t\t\treturn 2/Sqrt(Pi)*Exp(-(p^2))*dp;\n\t\t\t}\n\t\t\tif (s.compare(\"erfc\") == 0)\n\t\t\t{\n\t\t\t\tMITEM Pi = new MNamedCt(PI, \"pi\");\n\t\t\t\treturn -(2/Sqrt(Pi))*Exp(-(p^2))*dp;\n\t\t\t}\n\t\t\tif (s.compare(\"H\") == 0)\n\t\t\t{\n\t\t\t\treturn 0.0;\n\t\t\t}\n\t\t\tassert(false);\n\t\t}\n\t\tbreak;\n\tcase MF2D:\n\t\t{\n\t\t\tconst string& s = mfnc2d(e)->Name();\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (s.compare(\"pow\") == 0)\n\t\t\t{\n\t\t\t\tif (isConst(r) || is_named(r) || is_frac(r)) return (r * (l ^ (r - 1.0))) * MDerive(l, x);\n\t\t\t\telse if (isConst(l) || is_named(l) || is_frac(l)) return (Log(l) * e) * MDerive(r, x);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tMITEM dl = MDerive(l, x);\n\t\t\t\t\tMITEM dr = MDerive(r, x);\n\t\t\t\t\treturn (e * (dr * Log(l) + (r * dl) / l));\n\t\t\t\t}\n\t\t\t}\n#ifdef WIN32\n\t\t\telse if (s.compare(\"Jn\") == 0)\n\t\t\t{\n\t\t\t\tMITEM dr = MDerive(r, x);\n\t\t\t\tif (is_int(l))\n\t\t\t\t{\n\t\t\t\t\tint n = (int) l.value();\n\t\t\t\t\tif (n==0) return (-J1(r))*dr;\n\t\t\t\t\telse return ((Jn(n-1, r) - Jn(n+1, r))/2.0)*dr;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (s.compare(\"Yn\") == 0)\n\t\t\t{\n\t\t\t\tMITEM dr = MDerive(r, x);\n\t\t\t\tif (is_int(l))\n\t\t\t\t{\n\t\t\t\t\tint n = (int) l.value();\n\t\t\t\t\tif (n==0) return (-Y1(r))*dr;\n\t\t\t\t\telse return ((Yn(n-1, r) - Yn(n+1, r))/2.0)*dr;\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t}\n\t\tbreak;\n\tcase MMATRIX:\n\t\t{\n\t\t\tconst MMatrix& m = *mmatrix(e);\n\t\t\tint ncol = m.columns();\n\t\t\tint nrow = m.rows();\n\n\t\t\tMMatrix* pdm = new MMatrix;\n\t\t\tpdm->Create(nrow, ncol);\n\t\t\tfor (int i=0; i<nrow; ++i)\n\t\t\t\tfor (int j=0; j<ncol; ++j)\n\t\t\t\t{\n\t\t\t\t\tMITEM mij(m.Item(i,j)->copy());\n\t\t\t\t\tMITEM dmij = MDerive(mij, x);\n\t\t\t\t\t(*pdm)[i][j] = dmij.copy();\n\t\t\t\t}\n\t\t\treturn MITEM(pdm);\n\t\t}\n\t\tbreak;\n\tcase MSFNC:\n\t\t{\n\t\t\tconst MSFuncND& f = *msfncnd(e);\n\t\t\tMITEM v(f.Value()->copy());\n\t\t\treturn MDerive(v, x);\n\t\t}\n\t\tbreak;\n\t}\n\tassert(false);\n\treturn e;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MDerive(const MITEM& e, const MVariable& x, int n)\n{\n\tMITEM d = e;\n\tfor (int i=0; i<n; ++i) d = MDerive(d, x);\n\treturn d;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MDerive(const MITEM& e, const MSequence& x)\n{\n\tMITEM d = e;\n\tfor (int i=0; i<(int) x.size(); ++i)\n\t{\n\t\tconst MVariable& xi = *(mvar(x[i])->GetVariable());\n\t\td = MDerive(d, xi);\n\t}\n\treturn d;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MTaylor(const MITEM& e, const MVariable& v, double z, int n)\n{\n\tMITEM a(z);\n\tMITEM x(v);\n\n\tMITEM dx = x - a;\n\tMITEM s = MReplace(e, v, a);\n\tMITEM t(e);\n\tdouble d = 1;\n\n\tfor (int i=1; i<=n; ++i)\n\t{\n\t\tt = MDerive(t /d, v);\n\t\td = (double) i;\n\n\t\tMITEM f = MReplace(t, v, a);\n\t\tMITEM Di = dx^d;\n\t\tMITEM ds = ((f/d)*Di);\n\t\ts = s + ds;\n\t}\n\n\treturn s;\n}\n"
  },
  {
    "path": "FECore/MEvaluate.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MEvaluate.h\"\n#include \"MMath.h\"\n#include <map>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nvoid MEvaluate(MathObject* po)\n{\n\tif (dynamic_cast<MSimpleExpression*>(po))\n\t{\n\t\tMSimpleExpression* pe = dynamic_cast<MSimpleExpression*>(po);\n\t\tMITEM it = MEvaluate(pe->GetExpression());\n\t\tpe->SetExpression(it);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMITEM MEvaluate(const MITEM& i)\n{\n\tMITEM a;\n\tswitch (i.Type())\n\t{\n\tcase MNEG: a = -MEvaluate(i.Item()); break;\n\tcase MADD:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\ta = MAddition(l, r);\n\t\t}\n\t\tbreak;\n\tcase MSUB:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\ta = MAddition(l, -r);\n\t\t}\n\t\tbreak;\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\ta = MMultiply(l, r);\n\t\t}\n\t\tbreak;\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\ta = MDivide(l, r);\n\t\t}\n\t\tbreak;\n\tcase MPOW:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\ta = (l ^ r);\n\t\t}\n\t\tbreak;\n\tcase MEQUATION:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\ta = MITEM(new MEquation(l.copy(), r.copy()));\n\t\t}\n\t\tbreak;\n\tcase MEQUALITY:\n\t\t{\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\tbool b = is_equal(l.ItemPtr(), r.ItemPtr());\n\t\t\ta = MITEM(new MBoolean(b));\n\t\t}\n\t\tbreak;\n\tcase MMATRIX:\n\t\t{\n\t\t\tconst MMatrix& A = *mmatrix(i);\n\t\t\ta = MEvaluate(A);\n\t\t}\n\t\tbreak;\n\tcase MF1D:\n\t\t{\n\t\t\tconst MFunc1D* pf = mfnc1d(i);\n\t\t\tstring s = pf->Name();\n\t\t\tMITEM p = MEvaluate(i.Item());\n\t\t\tif (s.compare(\"sin\" ) == 0) return Sin  (p);\n\t\t\tif (s.compare(\"cos\" ) == 0) return Cos  (p);\n\t\t\tif (s.compare(\"tan\" ) == 0) return Tan  (p);\n\t\t\tif (s.compare(\"cot\" ) == 0) return Cot  (p);\n\t\t\tif (s.compare(\"sec\" ) == 0) return Sec  (p);\n\t\t\tif (s.compare(\"csc\" ) == 0) return Csc  (p);\n\t\t\tif (s.compare(\"atan\") == 0) return Atan (p);\n\t\t\tif (s.compare(\"cosh\") == 0) return Cosh (p);\n\t\t\tif (s.compare(\"sinh\") == 0) return Sinh (p);\n\t\t\tif (s.compare(\"exp\" ) == 0) return Exp  (p);\n\t\t\tif (s.compare(\"ln\"  ) == 0) return Log  (p);\n\t\t\tif (s.compare(\"log\" ) == 0) return Log10(p);\n\t\t\tif (s.compare(\"sqrt\") == 0) return Sqrt (p);\n\t\t\tif (s.compare(\"fl\"  ) == 0) return Float(p);\n\t\t\tif (s.compare(\"abs\" ) == 0) return Abs  (p);\n\t\t\tif (s.compare(\"sgn\" ) == 0) return Sgn  (p);\n#ifdef WIN32\n\t\t\tif (s.compare(\"J0\"  ) == 0) return J0   (p);\n\t\t\tif (s.compare(\"J1\"  ) == 0) return J1   (p);\n\t\t\tif (s.compare(\"Y0\"  ) == 0) return Y0   (p);\n\t\t\tif (s.compare(\"Y1\"  ) == 0) return Y1   (p);\n#endif\n\t\t\tif (s.compare(\"fac\" ) == 0) return Fac  (p);\n\t\t\tif (s.compare(\"erf\" ) == 0) return Erf  (p);\n\t\t\tif (s.compare(\"erfc\") == 0) return Erfc (p);\n\t\t\treturn i;\n\t\t}\n\t\tbreak;\n\tcase MF2D:\n\t\t{\n\t\t\tconst MFunc2D* pf = mfnc2d(i);\n\t\t\tstring s = pf->Name();\n\t\t\tMITEM a = MEvaluate(i.Left());\n\t\t\tMITEM b = MEvaluate(i.Right());\n\t\t\tif (s.compare(\"binomial\") == 0) return Binomial(a, b);\n#ifdef WIN32\n\t\t\telse if (s.compare(\"Jn\")==0) return Jn(a,b);\n\t\t\telse if (s.compare(\"Yn\")==0) return Yn(a,b);\n#endif\n\t\t\telse if (s.compare(\"Tn\")==0) return Tn(a,b);\n\t\t\treturn i;\n\t\t}\n\t\tbreak;\n\tcase MFMAT:\n\t\t{\n\t\t\tconst MFuncMat* pfm = mfncmat(i);\n\t\t\tMITEM m = MEvaluate(i.Item());\n\t\t\tconst MMatrix& A = *mmatrix(m.ItemPtr());\n\t\t\tFUNCMATPTR pf = pfm->funcptr();\n\t\t\ta = pf(A);\n\t\t}\n\t\tbreak;\n\tcase MFMAT2:\n\t\t{\n\t\t\tconst MFuncMat2* pfm = mfncmat2(i);\n\t\t\tMITEM l = MEvaluate(i.Left());\n\t\t\tMITEM r = MEvaluate(i.Right());\n\t\t\tconst MMatrix& A = *mmatrix(l.ItemPtr());\n\t\t\tconst MMatrix& B = *mmatrix(r.ItemPtr());\n\t\t\tFUNCMAT2PTR pf = pfm->funcptr();\n\t\t\ta = pf(A, B);\n\t\t}\n\t\tbreak;\n\tcase MSFNC:\n\t\t{\n\t\t\tconst MSFuncND* pf = msfncnd(i);\n\t\t\tMITEM v = pf->Value()->copy();\n\t\t\tMITEM e = MEvaluate(v);\n//\t\t\tif (is_rconst(e.ItemPtr())) return e; else return i;\n\t\t\treturn e;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\treturn i;\n\t}\n\n\tif (a.Type() != i.Type()) a = MEvaluate(a);\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MEvaluate(const MMatrix& A)\n{\n\tMMatrix& B = *(new MMatrix());\n\tB.Create(A.rows(), A.columns());\n\tfor (int i=0; i<A.rows(); ++i)\n\t\tfor (int j=0; j<A.columns(); ++j)\n\t\t{\n\t\t\tMITEM aij = A[i][j]->copy();\n\t\t\tB[i][j] = MEvaluate(aij).copy();\n\t\t}\n\treturn (&B);\n}\n\n//-----------------------------------------------------------------------------\nMITEM MMultiply(const MITEM& l, const MITEM& r)\n{\n\tMProduct M(l*r);\n\treturn M.Item();\n}\n\n//-----------------------------------------------------------------------------\nMITEM MDivide(const MITEM& n, const MITEM& d)\n{\n\tif (d == 0.0) throw DivisionByZero();\n\tif (n == d) return 1.0;\n\tMProduct L(n), D(d);\n\treturn L / D;\n}\n\n//-----------------------------------------------------------------------------\nMProduct::MProduct(const MITEM& a)\n{\n\tm_p.push_back(1.0);\n\tMultiply(a);\n}\n\n//-----------------------------------------------------------------------------\nvoid MProduct::Multiply(const MITEM& a)\n{\n\tif (is_mul(a))\n\t{\n\t\tMITEM l = a.Left();\n\t\tMITEM r = a.Right();\n\t\tMultiply(l);\n\t\tMultiply(r);\n\t}\n\telse \n\t{\n\t\tif (isConst(a))\n\t\t{\n\t\t\tMITEM& i0 = *m_p.begin();\n\t\t\tif (isConst(i0)) i0 = (a.value()*i0.value()); else m_p.push_front(a);\n\t\t}\n\t\telse \n\t\t{\n\t\t\t// see if a already appears in the product\n\t\t\tbool binsert = true;\n\t\t\tMITEM b(a), pb(1.0);\n\t\t\tif (is_pow(a))\n\t\t\t{\n\t\t\t\tb = a.Left();\n\t\t\t\tpb = a.Right();\n\t\t\t}\n\t\t\tlist<MITEM>::iterator ic;\n\t\t\tfor (ic=m_p.begin(); ic != m_p.end(); ++ic)\n\t\t\t{\n\t\t\t\tMITEM c(*ic), pc(1.0);\n\t\t\t\tif (is_pow(c))\n\t\t\t\t{\n\t\t\t\t\tc = (*ic).Left();\n\t\t\t\t\tpc = (*ic).Right();\n\t\t\t\t}\n\n\t\t\t\tif (b == c)\n\t\t\t\t{\n\t\t\t\t\t(*ic) = c^(pc + pb);\n\t\t\t\t\tbinsert = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (binsert) m_p.push_back(a);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMITEM MProduct::Item()\n{\n\tMITEM c(1.0), r(1.0);\n\tlist<MITEM>::iterator it;\n\tfor (it=m_p.begin(); it != m_p.end(); ++it) \n\t{\n\t\tif (isConst(*it)) c = c*(*it);\n\t\telse r = (r*(*it));\n\t}\n\treturn c*r;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MProduct::operator / (const MProduct& d)\n{\n\tMProduct tmp(d);\n\tlist<MITEM>::iterator ib;\n\tlist<MITEM>::iterator ia;\n\tfor (ib = tmp.m_p.begin(); ib != tmp.m_p.end();)\n\t{\n\t\tMITEM b(*ib), pb(1.0);\n\t\tbool binc = true;\n\t\tif (b != 0.0)\n\t\t{\n\t\t\tif (is_pow(b))\n\t\t\t{\n\t\t\t\tb  = (*ib).Left();\n\t\t\t\tpb = (*ib).Right();\n\t\t\t}\n\t\t\tfor (ia = m_p.begin(); ia != m_p.end(); ++ia)\n\t\t\t{\n\t\t\t\tMITEM a(*ia), pa(1.0);\n\t\t\t\tif (is_pow(a))\n\t\t\t\t{\n\t\t\t\t\ta  = (*ia).Left();\n\t\t\t\t\tpa = (*ia).Right();\n\t\t\t\t}\n\n\t\t\t\tif (a == b)\n\t\t\t\t{\n\t\t\t\t\t(*ia) = a^(pa - pb);\n\t\t\t\t\tib = tmp.m_p.erase(ib);\n\t\t\t\t\tbinc = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (binc) ++ib;\n\t}\n\n\tif (!tmp.m_p.empty())\n\t{\n\t\tif (isConst(*m_p.begin()) && isConst(*tmp.m_p.begin()))\n\t\t{\n\t\t\tMITEM& a = *m_p.begin();\n\t\t\tMITEM& b = *tmp.m_p.begin();\n\t\t\tdouble n = a.value();\n\t\t\tdouble d = b.value();\n\t\t\tif ((d != 0) && is_int(n) && is_int(d))\n\t\t\t{\n\t\t\t\tlong  c = gcf((long) n, (long) d);\n\t\t\t\tn /= (double) c;\n\t\t\t\td /= (double) c;\n\t\t\t\ta = n;\n\t\t\t\tb = d;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Item() / tmp.Item();\n}\n\n//-----------------------------------------------------------------------------\nbool MProduct::operator==(const MProduct& b)\n{\n\tlist<MITEM>::iterator pf;\n\tfor (pf = m_p.begin(); pf != m_p.end(); ++pf)\n\t{\n\t\tMITEM& i = *pf;\n\t\tif (b.contains(i) == false) return false;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool MProduct::contains(const MITEM& a) const\n{\n\tlist<MITEM>::const_iterator pf;\n\tfor (pf = m_p.begin(); pf != m_p.end(); ++pf)\n\t{\n\t\tconst MITEM& i = *pf;\n\t\tif (i == a) return true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MAddition(const MITEM& l, const MITEM& r)\n{\n\tMSum S(l+r);\n\treturn S.Item();\n}\n\n//-----------------------------------------------------------------------------\nMSum::MSum(const MITEM& a)\n{\n\tm_c = 0.0;\n\tAdd(a);\n}\n\n//-----------------------------------------------------------------------------\nMSum::MTerm::MTerm(const MITEM& i) : m_s(1.0)\n{\n\t// extract the scalar from i\n\tif (is_mul(i))\n\t{\n\t\tMITEM l = i.Left();\n\t\tif (isConst(l))\n\t\t{\n\t\t\tm_s = l.value();\n\t\t\tm_a = i.Right();\n\t\t}\n\t\telse m_a = i;\n\t}\n\telse if (is_div(i))\n\t{\n\t\tMITEM n = i.Left();\n\t\tMITEM d = i.Right();\n\n\t\tif (is_mul(n))\n\t\t{\n\t\t\tMITEM l = n.Left();\n\t\t\tif (isConst(l))\n\t\t\t{\n\t\t\t\tm_s = l.value();\n\t\t\t\tn = n.Right();\n\t\t\t}\n\t\t}\n\n\t\tif (is_mul(d))\n\t\t{\n\t\t\tMITEM l = d.Left();\n\t\t\tif (isConst(l))\n\t\t\t{\n\t\t\t\tm_s /= l.value();\n\t\t\t\td = d.Right();\n\t\t\t}\n\t\t}\n\t\telse if (isConst(d))\n\t\t{\n\t\t\tm_s /= d.value();\n\t\t\td = 1.0;\n\t\t}\n\n\t\tm_a = MDivide(n, d);\n\t}\n\telse m_a = i;\n}\n\n//-----------------------------------------------------------------------------\nvoid MSum::Add(const MITEM& a)\n{\n\tif (is_add(a))\n\t{\n\t\tAdd(a.Left());\n\t\tAdd(a.Right());\n\t}\n\telse if (is_sub(a))\n\t{\n\t\tAdd(a.Left());\n\t\tSub(a.Right());\n\t}\n\telse if (isConst(a)) m_c += a.value();\n\telse if (is_frac(a)) m_c += mfrac(a)->fraction();\n\telse if (m_t.empty()) m_t.push_back(MTerm(a));\n\telse\n\t{\n\t\tMTerm b(a);\n\t\tbool badd = true;\n\t\t// see if term already exists\n\t\tlist<MTerm>::iterator pt;\n\t\tfor (pt = m_t.begin(); pt != m_t.end(); ++pt)\n\t\t{\n\t\t\tif (b.m_a == pt->m_a)\n\t\t\t{\n\t\t\t\tbadd = false;\n\t\t\t\tpt->m_s += b.m_s;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (badd) m_t.push_back(b);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid MSum::Sub(const MITEM& a)\n{\n\tif (is_add(a))\n\t{\n\t\tSub(a.Left());\n\t\tSub(a.Right());\n\t}\n\telse if (is_sub(a))\n\t{\n\t\tSub(a.Left());\n\t\tAdd(a.Right());\n\t}\n\telse if (isConst(a)) m_c -= a.value();\n\telse if (is_frac(a)) m_c -= mfrac(a)->fraction();\n\telse\n\t{\n\t\tMTerm b(a);\n\n\t\tbool badd = true;\n\t\t// see if term already exists\n\t\tlist<MTerm>::iterator pt;\n\t\tfor (pt = m_t.begin(); pt != m_t.end(); ++pt)\n\t\t{\n\t\t\tif (b.m_a == pt->m_a)\n\t\t\t{\n\t\t\t\tbadd = false;\n\t\t\t\tpt->m_s -= b.m_s;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (badd) { b.m_s = -b.m_s; m_t.push_back(b); }\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMITEM MSum::Item()\n{\n\tif (m_t.empty()) return Fraction(m_c.n, m_c.d);\n\n\tlist<MTerm>::iterator pt = m_t.begin();\n\tMITEM g = Fraction(pt->m_s.n, pt->m_s.d);\n\tMITEM s = g*pt->m_a;\n\t++pt;\n\tfor (; pt != m_t.end(); ++pt)\n\t{\n\t\tg = Fraction(pt->m_s.n, pt->m_s.d);\n\t\ts = s + g*pt->m_a;\n\t}\n\tif (m_c.n != 0.0) s = s + Fraction(m_c.n, m_c.d);\n\treturn s;\n}\n"
  },
  {
    "path": "FECore/MEvaluate.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"MathObject.h\"\n#include \"MMatrix.h\"\n#include <list>\n\n//-----------------------------------------------------------------------------\nvoid MEvaluate(MathObject* po);\n\n//-----------------------------------------------------------------------------\nMITEM MEvaluate(const MITEM& e);\n\n//-----------------------------------------------------------------------------\nMITEM MEvaluate(const MMatrix& A);\n\n//-----------------------------------------------------------------------------\nMITEM MMultiply(const MITEM& l, const MITEM& r);\n\n//-----------------------------------------------------------------------------\nMITEM MDivide(const MITEM& n, const MITEM& d);\n\n//-----------------------------------------------------------------------------\nMITEM MAddition(const MITEM& l, const MITEM& r);\n\n//-----------------------------------------------------------------------------\n// This class describes a multi-factor product. It is used to simplify \n// expressions involving factors for which the binary operations might be\n// too difficult to process\nclass MProduct\n{\npublic:\n\tMProduct(const MITEM& a);\n\npublic:\n\tvoid Multiply(const MITEM& a);\n\tMITEM operator / (const MProduct& D);\n\n\tMITEM Item();\n\n\tbool operator == (const MProduct& a);\n\n\tbool contains(const MITEM& i) const;\n\nprotected:\n\tstd::list<MITEM>\t\tm_p;\n};\n\n//-----------------------------------------------------------------------------\n// This class describes a general sum\nclass MSum\n{\n\tclass MTerm\n\t{\n\tpublic:\n\t\tMITEM\t\tm_a;\t// term\n\t\tFRACTION\tm_s;\t// multiplier (can be negative!)\n\n\tpublic:\n\t\tMTerm(const MITEM& i);\n\t\tMTerm(const MTerm& i) { m_a = i.m_a; m_s = i.m_s; }\n\t\tvoid operator = (const MTerm& i) { m_a = i.m_a; m_s = i.m_s; }\n\t};\n\npublic:\n\tMSum(const MITEM& a);\n\tvoid Add(const MITEM& a);\n\tvoid Sub(const MITEM& a);\n\n\tMITEM Item();\n\nprotected:\n\tstd::list<MTerm>\tm_t;\n\tFRACTION\tm_c;\t// accumulator for constants\n};\n"
  },
  {
    "path": "FECore/MExpand.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nMITEM MExpand(const MITEM& i)\n{\n\treturn MExpand(i, MITEM((MItem*)0));\n}\n\n//-----------------------------------------------------------------------------\nMITEM MExpand(const MITEM& i, const MITEM& s)\n{\n\tif (i == s) return i;\n\n\tMITEM e = MEvaluate(i);\n\n\tswitch (e.Type())\n\t{\n\tcase MFRAC:\n\t\t{\n\t\t\tFRACTION f = mfrac(i)->fraction();\n\t\t\tif (f.d == 1.0) return f.n;\n\t\t}\n\t\tbreak;\n\tcase MNEG: return -MExpand(e.Item(),s);\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_add(r)&&(s != r)) \n\t\t\t{\n\t\t\t\tMITEM a = r.Left();\n\t\t\t\tMITEM b = r.Right();\n\t\t\t\treturn MExpand(l*a,s) + MExpand(l*b,s);\n\t\t\t}\n\t\t\tif (is_sub(r)&&(s != r))\n\t\t\t{\n\t\t\t\tMITEM a = r.Left();\n\t\t\t\tMITEM b = r.Right();\n\t\t\t\treturn MExpand(l*a,s) - MExpand(l*b,s);\n\t\t\t}\n\t\t\tif (is_add(l)&&(s != l)) \n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(r*a,s) + MExpand(r*b,s);\n\t\t\t}\n\t\t\tif (is_sub(l)&&(s != l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(r*a,s) - MExpand(r*b,s);\n\t\t\t}\n\t\t\tMITEM Ml = MExpand(l,s);\n\t\t\tMITEM Mr = MExpand(r,s);\n//\t\t\tif ((l != Ml) || (r != Mr)) return Ml*Mr;\n\t\t\tif ((l != Ml) || (r != Mr)) return MExpand(Ml*Mr,s); // it looks like this could create an infinite loop\n\t\t}\n\t\tbreak;\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_add(l)&&(s != l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(a/r,s) + MExpand(b/r,s);\n\t\t\t}\n\t\t\tif (is_sub(l)&&(s != l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(a/r,s) - MExpand(b/r,s);\n\t\t\t}\n\t\t\tMITEM Ml = MExpand(l,s);\n\t\t\tMITEM Mr = MExpand(r, s);\n\t\t\tif ((l != Ml) || (r != Mr)) return MExpand(Ml/Mr,s); else return Ml/Mr;\n\t\t}\n\t\tbreak;\n\tcase MADD:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\treturn MExpand(l,s) + MExpand(r,s);\n\t\t}\n\t\tbreak;\n\tcase MSUB:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\treturn MExpand(l,s) - MExpand(r,s);\n\t\t}\n\t\tbreak;\n\tcase MPOW:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_int(r) && is_add(l) && (l != s))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\tint n = (int) r.value();\n\t\t\t\tif (n == 0) return 1.0;\n\t\t\t\tMITEM sum(0.0);\n\t\t\t\tfor (int i=0; i<=n; ++i)\n\t\t\t\t{\n\t\t\t\t\tdouble C = binomial(n,i);\n\t\t\t\t\tsum = sum + C*MExpand(a^(n-i),s)*MExpand(b^i,s);\n\t\t\t\t}\n\t\t\t\treturn sum;\n\t\t\t}\n\t\t\tif (is_int(r) && is_sub(l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\tint n = (int) r.value();\n\t\t\t\tif (n == 0) return 1.0;\n\t\t\t\tMITEM sum(0.0);\n\t\t\t\tfor (int i=0; i<=n; ++i)\n\t\t\t\t{\n\t\t\t\t\tdouble C = binomial(n,i);\n\t\t\t\t\tMITEM t = C*MExpand(a^(n-i),s)*MExpand(b^i,s);\n\t\t\t\t\tif (i%2 == 0) sum = sum + t;\n\t\t\t\t\telse sum = sum - t;\n\t\t\t\t}\n\t\t\t\treturn sum;\n\t\t\t}\n\t\t\tif (is_add(r) && (r != s))\n\t\t\t{\n\t\t\t\tMITEM a = r.Left();\n\t\t\t\tMITEM b = r.Right();\n\t\t\t\treturn ((l^a)*(l^b));\n\t\t\t}\n\t\t\tMITEM le = MExpand(l);\n\t\t\tMITEM re = MExpand(r);\n\t\t\treturn le^re;\n\t\t}\n\t\tbreak;\n\tcase MF1D:\n\t\t{\n\t\t\tconst string& f = mfnc1d(e)->Name();\n\t\t\tif (f.compare(\"cos\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (p.Type() == MADD)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Cos(a)*Cos(b) - Sin(a)*Sin(b),s); \n\t\t\t\t}\n\t\t\t\tif (p.Type() == MSUB)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Cos(a)*Cos(b) + Sin(a)*Sin(b),s); \n\t\t\t\t}\n\t\t\t}\n\t\t\tif (f.compare(\"sin\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (p.Type() == MADD)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Sin(a)*Cos(b) + Cos(a)*Sin(b),s); \n\t\t\t\t}\n\t\t\t\tif (p.Type() == MSUB)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Sin(a)*Cos(b) - Cos(a)*Sin(b),s); \n\t\t\t\t}\n\t\t\t}\n\t\t\tif (f.compare(\"tan\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (p.Type() == MADD)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand((Tan(a)+Tan(b))/(1.0 - Tan(a)*Tan(b)),s); \n\t\t\t\t}\n\t\t\t\tif (p.Type() == MSUB)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand((Tan(a)-Tan(b))/(1.0 + Tan(a)*Tan(b)),s); \n\t\t\t\t}\n\t\t\t}\n/*\t\t\tif (f.compare(\"ln\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (is_mul(p))\n\t\t\t\t{\n\t\t\t\t\tMITEM a = MExpand(p.Left());\n\t\t\t\t\tMITEM b = MExpand(p.Right());\n\t\t\t\t\treturn MExpand(Log(a)+Log(b));\n\t\t\t\t}\n\t\t\t\tif (is_div(p))\n\t\t\t\t{\n\t\t\t\t\tMITEM a = MExpand(p.Left());\n\t\t\t\t\tMITEM b = MExpand(p.Right());\n\t\t\t\t\treturn MExpand(Log(a)-Log(b));\n\t\t\t\t}\n\t\t\t\tif (is_pow(p))\n\t\t\t\t{\n\t\t\t\t\tMITEM a = MExpand(p.Left());\n\t\t\t\t\tMITEM b = MExpand(p.Right());\n\t\t\t\t\treturn MExpand(b*Log(a));\n\t\t\t\t}\n\t\t\t}\n*/\t\t\tconst MFunc1D* pf = mfnc1d(e);\n\t\t\tMITEM v = MExpand(e.Param(), s);\n\t\t\treturn new MFunc1D(pf->funcptr(), pf->Name(), v.copy());\n\t\t}\n\t\tbreak;\n\tcase MSFNC:\n\t\t{\n\t\t\tMITEM f(msfncnd(i)->Value()->copy());\n\t\t\treturn MExpand(f, s);\n\t\t}\n\t\tbreak;\n\tcase MEQUATION:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tMITEM Ml = MExpand(l,s);\n\t\t\tMITEM Mr = MExpand(r,s);\n\t\t\treturn new MEquation(Ml.copy(), Mr.copy());\n\t\t}\n\t\tbreak;\n\tcase MSEQUENCE:\n\t\t{\n\t\t\tconst MSequence& q = *msequence(i);\n\t\t\tMSequence* ps = new MSequence();\n\t\t\tfor (int i=0; i<q.size(); ++i)\n\t\t\t{\n\t\t\t\tMITEM qi = q[i]->copy();\n\t\t\t\tMITEM vi = MExpand(qi, s);\n\t\t\t\tps->add(vi.copy());\n\t\t\t}\n\t\t\treturn ps;\n\t\t}\n\t\tbreak;\n\tcase MMATRIX:\n\t\t{\n\t\t\tconst MMatrix& m = *mmatrix(e);\n\t\t\tint ncol = m.columns();\n\t\t\tint nrow = m.rows();\n\n\t\t\tMMatrix* pdm = new MMatrix;\n\t\t\tpdm->Create(nrow, ncol);\n\t\t\tfor (int i=0; i<nrow; ++i)\n\t\t\t\tfor (int j=0; j<ncol; ++j)\n\t\t\t\t{\n\t\t\t\t\tMITEM mij(m.Item(i,j)->copy());\n\t\t\t\t\tMITEM aij = MExpand(mij, s);\n\t\t\t\t\t(*pdm)[i][j] = aij.copy();\n\t\t\t\t}\n\t\t\treturn MITEM(pdm);\n\t\t}\n\t\tbreak;\n\t}\n\treturn e;\n}\n"
  },
  {
    "path": "FECore/MFunctions.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MFunctions.h\"\n#include <assert.h>\n\ndouble fl  (double x) { return x; }\ndouble csc (double x) { return 1.0 / sin(x); }\ndouble sec (double x) { return 1.0 / cos(x); }\ndouble cot (double x) { return 1.0 / tan(x); }\ndouble sinc(double x) { return (x == 0? 1 : sin(x)/x); }\ndouble sgn (double x) { return (x<0?-1.0 : 1.0); }\ndouble heaviside (double x) { return (x> 0 ? 1.0 : (x < 0.0 ? 0.0 : 0.5)); }\ndouble unit_step (double x) { return (x< 0 ? 0.0 : 1.0); }\n\n#ifdef WIN32\ndouble jn(double x, double y)\n{\n\treturn _jn((int) x, y);\n}\n\ndouble yn(double x, double y)\n{\n\treturn _yn((int) x, y);\n}\n#endif\n\ndouble fmax(double* x, int n)\n{\n\tdouble v = x[0];\n\tfor (int i=1; i<n; ++i)\n\t\tif (x[i] > v) v = x[i];\n\treturn v;\n}\n\ndouble fmin(double* x, int n)\n{\n\tdouble v = x[0];\n\tfor (int i=1; i<n; ++i)\n\t\tif (x[i] < v) v = x[i];\n\treturn v;\n}\n\ndouble avg(double* x, int n)\n{\n\tdouble v = x[0];\n\tfor (int i=1; i<n; ++i) v += x[i];\n\treturn (v/n);\n}\n\ndouble chebyshev(double f, double x)\n{\n\tint n = (int)(f);\n\tif (n<=0) return 1;\n\tif (n==1) return x;\n\n\tdouble T0 = 0;\n\tdouble T1 = 1;\n\tdouble Tn = x;\n\tfor (int i=2; i<=n; ++i)\n\t{\n\t\tT0 = T1;\n\t\tT1 = Tn;\n\t\tTn = 2*x*T1 - T0;\n\t}\n\treturn Tn;\n}\n\ndouble fac(double n)\n{\n\tassert(n >= 0.0);\n\tif (n <= 1) return 1.0;\n\tdouble p = 1.0;\n\twhile (n > 1.0) p *= (n--);\n\treturn p;\n}\n\ndouble prod(double a, double b)\n{\n\tdouble p = 1;\n\tif (a >= b)\n\t{\n\t\twhile (a >= b) p *= a--;\n\t}\n\telse\n\t{\n\t\twhile (b >= a) p *= b--;\n\t}\n\treturn p;\n}\n\ndouble binomial(double n, double r)\n{\n\tassert(r <= n);\n\tif (n == r) return 1.0;\n\tif (r == 0.0) return 1.0;\n\tdouble b = 0;\n\tif (r >= n/2)\n\t{\n\t\tb = prod(n, r+1)/fac(n - r);\n\t}\n\telse b = prod(n, n-r+1) / fac(r);\n\treturn b;\n}\n\n//-----------------------------------------------------------------------------\n// approximation of gamma function using Lanczos approximation\ndouble gamma(double z)\n{\n\tconst int g = 7;\n\tconst double p[] = {0.99999999999980993, 676.5203681218851, -1259.1392167224028,\n     771.32342877765313, -176.61502916214059, 12.507343278686905,\n\t -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7};\n\tconst double pi = 4.0*atan(1.0);\n \n    if (z < 0.5)\n\t{\n        return pi / (sin(pi*z) * gamma(1-z));\n\t}\n    else\n\t{\n        z -= 1;\n        double x = p[0];\n        for (int i=1; i<g+2; ++i) x += p[i]/(z+i);\n\t\tdouble t = z + g + 0.5;\n        return sqrt(2*pi) * pow(t, z+0.5) * exp(-t) * x;\n\t}\n}\n"
  },
  {
    "path": "FECore/MFunctions.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"math.h\"\n#include \"fecore_api.h\"\n\ntypedef double (*FUNCPTR) (double);\ntypedef double (*FUNC2PTR) (double, double);\ntypedef double (*FUNCNPTR) (double*, int);\n\n// trigonometric functions\ndouble csc(double x);\ndouble sec(double x);\ndouble cot(double x);\ndouble sinc(double x);\n\n// Bessel functions\n#ifdef WIN32\ndouble jn(double x, double y);\ndouble yn(double x, double y);\n#endif\n\n// multi-variate functions\ndouble fmax(double* x, int n);\ndouble fmin(double* x, int n);\ndouble avg(double* x, int n);\n\n// special polynomials\ndouble chebyshev(double f, double x);\n\n// additional functios\ndouble fl(double x);\ndouble sgn(double x);\ndouble fac(double x);\t// factorials\ndouble heaviside(double x);\ndouble unit_step(double x);\n\n// binomials\ndouble binomial(double n, double r);\n\n// gamma function\nFECORE_API double gamma(double x);\n"
  },
  {
    "path": "FECore/MIntegral.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n//! Calculate the definite integral of an expression\nMITEM MIntegral(const MITEM& i, const MVariable& x, const MITEM& a, const MITEM& b)\n{\n\tMITEM e = MEvaluate(i);\n\tMITEM Ie = MIntegral(e, x);\n\n\treturn MReplace(Ie, x, b) - MReplace(Ie, x, a);\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the indefinite integral of an expression.\n//! Note that the integration constant is not included in the result\nMITEM MIntegral(const MITEM& i, const MVariable& x)\n{\n\t// simplify the expression\n\tMITEM e = MEvaluate(i);\n\n\t// see if there is a dependency on x\n\tif (is_dependent(e, x) == false) \n\t{\n\t\t// if not, multiply by x and return\n\t\treturn e*MITEM(x); \n\t}\n\n\t// if we get here, there is a dependency on x so we\n\t// need to find an appropriate integration rule\n\tswitch (e.Type())\n\t{\n\tcase MVAR: \n\t\tif (e == x) \n\t\t{\n\t\t\tMITEM X(x);\n\t\t\treturn Fraction(1.0, 2.0)*(X ^ 2.0); \n\t\t}\n\t\tbreak;\n\tcase MNEG: return -MIntegral(e.Item(), x); \n\tcase MADD: return MIntegral(e.Left(), x) + MIntegral(e.Right(), x); \n\tcase MSUB: return MIntegral(e.Left(), x) - MIntegral(e.Right(), x); \n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_dependent(l, x) == false) return l*MIntegral(r, x);\n\t\t\tif (is_dependent(r, x) == false) return r*MIntegral(l, x);\n\t\t\treturn MIntegral(MExpand(e), x);\n\t\t}\n\t\tbreak;\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_dependent(r, x) == false) return MIntegral(l, x) / r;\n\t\t}\n\t\tbreak;\n\tcase MPOW:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_var(l) && isConst(r))\n\t\t\t{\n\t\t\t\tif (l == x) \n\t\t\t\t{\n\t\t\t\t\tif (r.value() != -1.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tMITEM np1( r.value() + 1.0);\n\t\t\t\t\t\treturn (l^np1)/np1;\n\t\t\t\t\t}\n\t\t\t\t\telse return Log(Abs(l));\n\t\t\t\t}\n\t\t\t\telse return e*MITEM(x);\n\t\t\t}\n\t\t\tif (is_int(r) && (is_add(l) || is_sub(l))) return MIntegral(MExpand(e),x);\n\t\t\tif (is_number(l) && (is_dependent(r,x)))\n\t\t\t{\n\t\t\t\tif ((is_int(l) == false) || (mnumber(l)->value() > 1.0))\n\t\t\t\t{\n\t\t\t\t\tif (r == x)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn e/Log(l);\n\t\t\t\t\t}\n\t\t\t\t\telse if (is_mul(r))\n\t\t\t\t\t{\n\t\t\t\t\t\tMITEM rl = r.Left();\n\t\t\t\t\t\tMITEM rr = r.Right();\n\t\t\t\t\t\tif (is_number(rl) && (rr == x))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn e/(rl*Log(l));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase MF1D:\n\t\t{\n\t\t\tconst string& s = mfnc1d(e)->Name();\n\t\t\tMITEM p = mfnc1d(e)->Item()->copy();\n\t\t\tif (p == x)\n\t\t\t{\n\t\t\t\tif (s.compare(\"cos\") == 0) return Sin(p);\n\t\t\t\tif (s.compare(\"sin\") == 0) return -Cos(p);\n\t\t\t\tif (s.compare(\"tan\") == 0) return -Log(Abs(Cos(p)));\n\t\t\t\tif (s.compare(\"cot\") == 0) return Log(Abs(Sin(p)));\n\t\t\t\tif (s.compare(\"sec\") == 0) return Log(Abs(Sec(p) + Tan(p)));\n\t\t\t\tif (s.compare(\"csc\") == 0) return -Log(Abs(Csc(p) + Cot(p)));\n\t\t\t\tif (s.compare(\"sinh\") == 0) return Cosh(p);\n\t\t\t\tif (s.compare(\"cosh\") == 0) return Sinh(p);\n\t\t\t\tif (s.compare(\"tanh\") == 0) return Log(Cosh(p));\n\t\t\t\tif (s.compare(\"sech\") == 0) return Atan(Sinh(p));\n\t\t\t\tif (s.compare(\"exp\") == 0) return Exp(p);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn new MOpIntegral(e.copy(), new MVarRef(&x));\n}\n"
  },
  {
    "path": "FECore/MItem.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MItem.h\"\n#include \"MMatrix.h\"\n#include \"MEvaluate.h\"\n#include \"MMath.h\"\nusing namespace std;\n\n#ifndef PI\n#define PI\t3.141592653589793\n#endif\n\n//-----------------------------------------------------------------------------\n// Some special constants\nMITEM MPi (new MNamedCt(PI      , \"pi\"));\nMITEM ME  (new MNamedCt(exp(1.0), \"e\" ));\nMITEM MINF(new MNamedCt(1e308   , \"_inf_\"));\n\n//-----------------------------------------------------------------------------\nMItem* MFuncND::copy() const\n{\n\treturn new MFuncND(m_pf, m_name, GetSequence());\n}\n\n//-----------------------------------------------------------------------------\nMSequence::MSequence(const MSequence& s) : MItem(MSEQUENCE)\n{\n\tint N = s.size();\n\tfor (int i=0; i<N; ++i) add(s[i]->copy());\n}\n\n//-----------------------------------------------------------------------------\nMSequence::~MSequence()\n{\n\tint M = size();\n\tfor (int i=0; i<M; ++i) delete m_item[i]; \n\tm_item.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid MSequence::remove(int i)\n{\n\tdelete m_item[i];\n\tm_item.erase(m_item.begin()+i);\n}\n\n//-----------------------------------------------------------------------------\nvoid MSequence::replace(int i, MItem* pi)\n{\n\tdelete m_item[i];\n\tm_item[i] = pi;\n}\n\n//-----------------------------------------------------------------------------\nMSequence& MSequence::operator = (MSequence& s)\n{\n\tint M = size();\n\tfor (int i=0; i<M; ++i) delete m_item[i]; m_item.clear();\n\tint N = s.size();\n\tfor (int i=0; i<N; ++i) add(s[i]->copy());\n\treturn (*this);\n}\n\n\n//-----------------------------------------------------------------------------\nMITEM Fraction(double n, double d)\n{\n\tdouble s = (n*d<0?-1:1);\n\tn = fabs(n);\n\td = fabs(d);\n\n\tif ((n - floor(n) == 0) && (d - floor(d) == 0))\n\t{\n\t\tif (d != 0.0) \n\t\t{\n\t\t\tif (n==0) return 0.0;\n\t\t\tdouble f = n/d;\n\t\t\tif (f - floor(f) == 0)\n\t\t\t{\n\t\t\t\tMITEM c(f);\n\t\t\t\tif (s<0) return -c; else return c;\n\t\t\t}\n\n\t\t\tdouble c = (double) gcf((long) n, (long) d);\n\t\t\tn /= c;\n\t\t\td /= c;\n\t\t}\n\t}\n/*\telse if (d - floor(d) == 0.0)\n\t{\n\t\tdouble v = n / d;\n\t\treturn v;\n\t}\n*/\n\tif (d == 1.0) return (s*n);\n\tMITEM r = new MFraction(n, d);\n\tif (s<0) return -r; else return r;\n}\n\n//=============================================================================\n//                        N E G A T I O N   ( - )\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nMITEM operator - (const MITEM& l) \n{\n\t// get the Item pointer\n\tconst MItem* pi = l.ItemPtr();\n\n\t// remove unnecessary negative signs\n\tint n = 0;\n\twhile (is_neg(pi)) { pi = mneg(pi)->Item(); n = (n+1)%2; }\n\n\t// -(a-b) = b-a\n\tif (is_sub(pi))\n\t{\n\t\tconst MItem* pl = msub(pi)->LeftItem ();\n\t\tconst MItem* pr = msub(pi)->RightItem();\n\n\t\tif (!is_neg(pl) && !is_neg(pr))\n\t\t{\n\t\t\tMITEM a = pl->copy();\n\t\t\tMITEM b = pr->copy();\n\t\t\treturn (b - a);\n\t\t}\n\t}\n\n\t// remove negative from zero\n\tif (is_zero(pi)) return 0.0;\n\n\t// return the negative of a copy of pi\n\tMItem* pret = pi->copy();\n\tif (n==0) pret = new MNeg(pret);\n\treturn pret; \n}\n\n//-----------------------------------------------------------------------------\n\n\n//=============================================================================\n//                            A D D I T I O N   ( + )\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nMITEM operator + (const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r))\n\t{\n\t\tif (is_matrix(l) && is_matrix(r))\n\t\t{\n\t\t\tconst MMatrix& A = *mmatrix(l);\n\t\t\tconst MMatrix& B = *mmatrix(r);\n\t\t\treturn A+B;\n\t\t}\n//\t\telse throw InvalidOperation();\n\t}\n\tif (isConst(l)) return (r + l.value());\n\tif (isConst(r)) return (l + r.value());\n\tif (is_neg  (r)) return (l - (-r)); // a+(-b) = a - b\n\tif (is_neg  (l)) return (r - (-l)); // ((-a)+b) = b - a\n\tif (is_frac(l) && is_frac(r)) \n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\tFRACTION b = mfrac(r)->fraction();\n\t\treturn Fraction(a + b);\n\t}\n\treturn new MAdd(l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator + (const MITEM& l, double r)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (r==0) return l;\n\tif (isConst(l)) return (l.value() + r);\n\tif (is_neg  (l)) return (r - l.Item());\n\tif (is_frac (l))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\treturn Fraction(a + r);\n\t}\n\treturn new MAdd(l.copy(), r);\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator + (double l, const MITEM& r)\n{\n\tif (is_matrix(r)) throw InvalidOperation();\n\tif (l==0) return r;\n\tif (isConst(r)) return (l + r.value());\n\tif (is_neg  (r)) return (l - r.Item());\n\tif (is_frac (r))\n\t{\n\t\tFRACTION a = mfrac(r)->fraction();\n\t\treturn Fraction(a + l);\n\t}\n\treturn new MAdd(r.copy(), l);\n}\n\n//=============================================================================\n//                         S U B T R A C T I O N   ( - )\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nMITEM operator - (const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r))\n\t{\n\t\tif (is_matrix(l) && is_matrix(r))\n\t\t{\n\t\t\tconst MMatrix& A = *mmatrix(l);\n\t\t\tconst MMatrix& B = *mmatrix(r);\n\t\t\treturn A-B;\n\t\t}\n//\t\telse throw InvalidOperation();\n\t}\n\tif (isConst(l)) return (l.value() - r);\n\tif (isConst(r)) return (l - r.value());\n\tif (is_neg(l) && is_neg(r)) return (-r)-(-l);  // (-a)-(-b) = b - a;\n\tif (is_neg(r)) return (l+(-r));\n\tif (is_neg(l)) return -((-l)+r);\n\tif (is_frac(l) && is_frac(r)) \n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\tFRACTION b = mfrac(r)->fraction();\n\t\treturn Fraction(a - b);\n\t}\n\treturn new MSub(l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator - (double l, const MITEM& r)\n{\n\tif (is_matrix(r)) throw InvalidOperation();\n\tif (l==0) return -r;\n\tif (isConst(r)) return (l - r.value());\n\tif (is_neg  (r)) return (-r) + l;\n\tif (is_frac (r))\n\t{\n\t\tFRACTION a = mfrac(r)->fraction();\n\t\treturn Fraction(l - a);\n\t}\n\treturn new MSub(l, r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator - (const MITEM& l, double r)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (r == 0) return l;\n\tif (isConst(l)) return (l.value() - r);\n\tif (is_neg  (l)) return -((-l) + r);\n\tif (is_frac (l))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\treturn Fraction(a - r);\n\t}\n\telse return new MSub(l.copy(), r);\n}\n\n//=============================================================================\n//                      M U L T I P L I C A T I O N   ( * )\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nMITEM operator * (const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) && ::is_scalar(r))\n\t{\n\t\tconst MMatrix& A = *mmatrix(l);\n\t\treturn A*r;\n\t}\n\tif (is_matrix(r) && ::is_scalar(l))\n\t{\n\t\tconst MMatrix& A = *mmatrix(r);\n\t\treturn A*l;\n\t}\n\tif (isConst(l)) return (l.value() * r);\n\tif (isConst(r)) return (r.value() * l);\n\tif (is_neg(l) && is_neg(r)) return ((-l)*(-r)); \n\tif (is_neg(l)) return -((-l)*r);\n\tif (is_neg(r)) return -(l*(-r));\n\tif (is_frac(l) && is_frac(r))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\tFRACTION b = mfrac(r)->fraction();\n\t\treturn Fraction(a*b);\n\t}\n\tif (is_frac(l))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\treturn (a.n*r)/a.d;\n\t}\n\tif (is_frac(r))\n\t{\n\t\tFRACTION a = mfrac(r)->fraction();\n\t\treturn (a.n*l)/a.d;\n\t}\n\tif (is_div(l) && (is_matrix(r) == false))\n\t{\n\t\tMITEM l1 = l.Left();\n\t\tMITEM l2 = l.Right();\n\t\treturn (l1*r)/l2;\n\t}\n\tif (is_div(r) && (is_matrix(r) == false))\n\t{\n\t\tMITEM r1 = r.Left();\n\t\tMITEM r2 = r.Right();\n\t\treturn (l*r1)/r2;\n\t}\n\tif (is_matrix(l) && is_matrix(r))\n\t{\n\t\tconst MMatrix& A = *mmatrix(l);\n\t\tconst MMatrix& B = *mmatrix(r);\n\t\treturn A*B;\n\t}\n\treturn new MMul(l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator * (double l, const MITEM& r)\n{ \n\tif (l == 0.0) return 0.0;\n\tif (l == 1.0) return r;\n\tif (isConst(r)) return l*r.value();\n\tif (is_neg  (r)) return -(l * (-r)); \n\tif (is_frac (r))\n\t{\n\t\tFRACTION a = mfrac(r)->fraction();\n\t\treturn Fraction(l*a);\n\t}\n\tif (is_div(r))\n\t{\n\t\tMITEM fl = r.Left ();\n\t\tMITEM fr = r.Right();\n\t\treturn (l*fl)/fr;\n\t}\n\treturn new MMul(l, r.copy()); \n}\n\n//=============================================================================\n//                            D I V I S I O N   ( / )\n//=============================================================================\n\nMITEM operator / (const MITEM& l, const MITEM& r)\n{\n\tif (r == 0.0) throw DivisionByZero();\n\tif (r == 1.0) return l;\n\tif (is_matrix(r)) throw InvalidOperation();\n\tif (is_matrix(l))\n\t{\n\t\tconst MMatrix& A = *mmatrix(l);\n\t\treturn A/r;\n\t}\n\tif (isConst(l)) return (l.value() / r);\n\tif (isConst(r)) return (l / r.value());\n\tif (is_neg  (l)) return -((-l)/r);\n\tif (is_neg  (r)) return -(l/(-r));\n\tif (is_frac(l) && is_frac(r))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\tFRACTION b = mfrac(r)->fraction();\n\t\treturn Fraction(a/b);\n\t}\n\tif (is_div(r))\n\t{\n\t\tMITEM r1 = r.Left();\n\t\tMITEM r2 = r.Right();\n\t\treturn (l*r2)/r1;\n\t}\n\tif (is_div(l))\n\t{\n\t\tMITEM l1 = l.Left();\n\t\tMITEM l2 = l.Right();\n\t\treturn (l1/(l2*r));\n\t}\n\t//------------------------------------------------------\n\treturn new MDiv(l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator / (double l, const MITEM& r)\n{\n\tif (is_matrix(r)) throw InvalidOperation();\n\tif ((l==0.0) && (r!=0.0)) return 0.0;\n\tif (isConst(r)) return Fraction(l, r.value());\n\tif (is_neg  (r)) return -(l/(-r));\n\tif (is_frac (r))\n\t{\n\t\tFRACTION a = mfrac(r)->fraction();\n\t\treturn Fraction(l/a);\n\t}\n\tif (is_div(r))\n\t{\n\t\tMITEM a = r.Left();\n\t\tMITEM b = r.Right();\n\t\treturn (l*b)/a;\n\t}\n\treturn new MDiv(l, r.copy()); \n}\n\n//-----------------------------------------------------------------------------\nMITEM operator / (const MITEM& l, double r)\n{\n\tif (r == 1.0) return l;\n\tif (isConst(l)) return Fraction(l.value(), r);\n\tif (is_neg  (l)) return -((-l)/r);\n\tif (is_frac (l))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\treturn Fraction(a/r);\n\t}\n\tif (is_div(l))\n\t{\n\t\tMITEM a = l.Left();\n\t\tMITEM b = l.Right();\n\t\treturn (a/(r*b));\n\t}\n\treturn new MDiv(l.copy(), r); \n}\n\n//=============================================================================\n//                            P O W E R   ( ^ )\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nMITEM operator ^ (const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r)) throw InvalidOperation();\n\tif (isConst(r)) return (l ^ r.value());\n\tif (is_neg(r))\n\t{\n\t\tMITEM mr = -r;\n\t\tif (isConst(mr)) return 1/ (l^mr);\n\t}\n\tif (is_fnc1d(l, \"sqrt\"))\n\t{\n\t\tMITEM a = l.Item();\n\t\treturn (a^(r / 2));\n\t}\n\treturn new MPow(l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM operator ^ (const MITEM& l, double r)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (r==1) return l;\n\tif (r==0) return 1.0;\n\tif (l==1.0) return 1.0;\n\tif (isConst(l) && is_int(r)) return pow(l.value(), r);\n\tif (is_neg(l) && is_int(r))\n\t{\n\t\tint n = (int) r;\n\t\tif (n%2==0) return (-l)^r;\n\t\telse return -((-l)^r);\n\t}\n\tif (is_frac(l) && is_int(r))\n\t{\n\t\tFRACTION a = mfrac(l)->fraction();\n\t\treturn Fraction(pow(a.n,r), pow(a.d,r));\n\t}\n\tif (is_pow(l))\n\t{\n\t\tMITEM v = l.Left();\n\t\tMITEM p = l.Right();\n\t\treturn (v^(r*p));\n\t}\n\tif (is_mul(l))\n\t{\n\t\tMITEM l1 = l.Left ();\n\t\tMITEM r1 = l.Right();\n\t\treturn ((l1^r)*(r1^r));\n\t}\n\tif (is_div(l))\n\t{\n\t\tMITEM l1 = l.Left ();\n\t\tMITEM r1 = l.Right();\n\t\treturn ((l1^r)/(r1^r));\n\t}\n\tif (is_fnc1d(l, \"sqrt\"))\n\t{\n\t\tMITEM a = l.Item();\n\t\treturn (a^(Fraction(r, 2)));\n\t}\n\treturn new MPow(l.copy(), r);\n}\n\n//=============================================================================\n//                            F U N C T I O N S\n//=============================================================================\n\n//-----------------------------------------------------------------------------\n// Absolute value\nMITEM Abs(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\tif (is_neg(a)) return Abs(-a);\n\n\t// remove redundant abs\n\tconst MItem* pi = a.ItemPtr();\n\twhile (is_neg(pi) || is_abs(pi)) { pi = munary(pi)->Item(); }\n\n\tif (isConst(pi)) return fabs(mnumber(pi)->value());\n\tif (is_named(pi)) return pi->copy(); // This assumes that all named constants are positive!\n\treturn new MFunc1D(fabs, \"abs\", pi->copy());\n}\n\n//-----------------------------------------------------------------------------\n// return the sign of the expression\nMITEM Sgn(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\tif (isConst(a)) return 1.0; // this assumes that all constants are positive\n\tif (is_neg(a)) return -Sgn(-a);\n\tif (is_pow(a))\n\t{\n\t\tconst MItem *pr = mpow(a)->RightItem();\n\t\tif (isConst(pr) && is_int(mnumber(pr)->value()))\n\t\t{\n\t\t\tMITEM l = a.Left();\n\t\t\tint n = (int) mnumber(pr)->value();\n\t\t\tif (n%2 == 0) return 1.0;\n\t\t\telse return Sgn(l);\n\t\t}\n\t}\n\treturn new MFunc1D(sgn, \"sgn\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\n// This function sees if a is a multiple of pi where the multiplier is a fraction\n// of integers. \nbool pi_multiple(const MITEM& a, int& num, int& den)\n{\n\tif (a==0.0) { num = 0; den = 1; return true; }\n\tif (is_pi(a)) { num = den = 1; return true; }\n\telse if (is_mul(a))\n\t{\n\t\tconst MItem* pl = mmul(a)->LeftItem();\n\t\tconst MItem* pr = mmul(a)->RightItem();\n\t\tif (is_pi(pl) && is_int(pr)) { num = (int)mconst(pr)->value(); den = 1; return true; }\n\t\tif (is_pi(pr) && is_int(pl)) { num = (int)mconst(pl)->value(); den = 1; return true; }\n\t}\n\telse if (is_div(a))\n\t{\n\t\tconst MItem* pl = mdiv(a)->LeftItem();\n\t\tconst MItem* pr = mdiv(a)->RightItem();\n\t\tif (is_pi(pl)&&is_int(pr)) { num = 1; den = (int)mconst(pr)->value(); return true; }\n\t\tif (is_mul(pl)&&is_int(pr))\n\t\t{\n\t\t\tden = (int)mconst(pr)->value();\n\t\t\tconst MItem* pl1 = mmul(pl)->LeftItem();\n\t\t\tconst MItem* pl2 = mmul(pl)->RightItem();\n\t\t\tif (is_pi(pl1) && (is_int(pl2))) { num = (int)mconst(pl2)->value(); return true; }\n\t\t\tif (is_pi(pl2) && (is_int(pl1))) { num = (int)mconst(pl1)->value(); return true; }\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nMITEM Sin(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\t// sin(-x) = -sin(x)\n\tif (is_neg(a)) return -Sin(-a);\n\n\t// check for multiples of pi\n\tint num, den;\n\tif (pi_multiple(a, num, den))\n\t{\n\t\tif (num == 0) return 0.0;\n\t\tif (den == 1) return 0.0;\n\t\tif ((num == 1) && (den == 2)) return 1.0;\n\t\tif ((num == 1) && (den == 3)) return Sqrt(MITEM(3.0))/2.0;\n\t\tif ((num == 1) && (den == 4)) return Sqrt(MITEM(2.0))/2.0;\n\t\tif ((num == 1) && (den == 6)) return 1.0 / MITEM(2.0);\n\t\tif ((num == 1) && (den == 12)) return Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) - 1.0);\n\t\tif ((num == 5) && (den == 12)) return Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) + 1.0);\n\t\tif ((num == 7) && (den == 12)) return Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) + 1.0);\n\t\tif ((num == 2) && (den == 3 )) return Sqrt(MITEM(3.0))/2;\n\t\tif ((num == 3) && (den == 4 )) return Sqrt(MITEM(2.0))/2;\n\t\tif ((num == 5) && (den == 6 )) return 1.0 / MITEM(2.0);\n\t\tif ((num == 11) && (den == 12)) return Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) - 1.0);\n\t}\n\treturn new MFunc1D(sin, \"sin\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Cos(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\t// cos(-x) = cos(x)\n\tif (is_neg(a)) return Cos(-a);\n\n\t// check for multiples of pi\n\tint num, den;\n\tif (pi_multiple(a, num, den))\n\t{\n\t\tif (num == 0) return  1.0;\n\t\tif (den == 1) return (num%2==0?1.0:-1.0);\n\t\tif ((num == 1) && (den == 2)) return 0.0;\n\t\tif ((num == 1) && (den == 3)) return 1.0 / MITEM(2.0);\n\t\tif ((num == 1) && (den == 4)) return Sqrt(MITEM(2.0))/2.0;\n\t\tif ((num == 1) && (den == 6)) return Sqrt(MITEM(3.0))/2.0;\n\t\tif ((num == 1) && (den == 12)) return Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) + 1.0);\n\t\tif ((num == 5) && (den == 12)) return Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) - 1.0);\n\t\tif ((num == 7) && (den == 12)) return -Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) - 1.0);\n\t\tif ((num == 2) && (den == 3 )) return -1.0 / MITEM(2.0);\n\t\tif ((num == 3) && (den == 4 )) return -Sqrt(MITEM(2.0))/2;\n\t\tif ((num == 5) && (den == 6 )) return -Sqrt(MITEM(3.0))/2;\n\t\tif ((num == 11) && (den == 12)) return -Sqrt(MITEM(2.0))/4*(Sqrt(MITEM(3.0)) + 1.0);\n\t}\n\treturn new MFunc1D(cos, \"cos\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Sec(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\n\t// sec(-x) = sec(x)\n\tif (is_neg(a)) return Sec(-a);\n\n\t// check for multiples of pi\n\tint num, den;\n\tif (pi_multiple(a, num, den))\n\t{\n\t\tif (num == 0) return  1.0;\n\t\tif (den == 1) return (num%2==0?1.0:-1.0);\n//\t\tif ((num == 1) && (den == 2)) return INFINITY;\n\t\tif ((num == 1) && (den == 3)) return 2.0;\n\t\tif ((num == 1) && (den == 4)) return Sqrt(MITEM(2.0));\n\t\tif ((num == 1) && (den == 6)) return 2.0*Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 1) && (den == 12)) return Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) - 1.0);\n\t\tif ((num == 5) && (den == 12)) return Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) + 1.0);\n\t\tif ((num == 7) && (den == 12)) return -Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) + 1.0);\n\t\tif ((num == 2) && (den == 3 )) return -2.0;\n\t\tif ((num == 3) && (den == 4 )) return -Sqrt(MITEM(2.0));\n\t\tif ((num == 5) && (den == 6 )) return -(2.0*Sqrt(MITEM(3.0))/3);\n\t\tif ((num == 11) && (den == 12)) return -Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) - 1.0);\n\t}\n\n\treturn new MFunc1D(sec, \"sec\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Csc(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\t// csc(-x) = -csc(x)\n\tif (is_neg(a)) return -Csc(-a);\n\n\t// check for multiples of pi\n\tint num, den;\n\tif (pi_multiple(a, num, den))\n\t{\n//\t\tif (num == 0) return INFINITY;\n//\t\tif (den == 1) return INFINITY;\n\t\tif ((num == 1) && (den == 2)) return 1.0;\n\t\tif ((num == 1) && (den == 3)) return 2.0*Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 1) && (den == 4)) return Sqrt(MITEM(2.0));\n\t\tif ((num == 1) && (den == 6)) return 2.0;\n\t\tif ((num == 1) && (den == 12)) return Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) + 1.0);\n\t\tif ((num == 5) && (den == 12)) return Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) - 1.0);\n\t\tif ((num == 7) && (den == 12)) return Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) - 1.0);\n\t\tif ((num == 2) && (den == 3 )) return 2.0*Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 3) && (den == 4 )) return Sqrt(MITEM(2.0));\n\t\tif ((num == 5) && (den == 6 )) return 2.0;\n\t\tif ((num == 11) && (den == 12)) return Sqrt(MITEM(2.0))*(Sqrt(MITEM(3.0)) + 1.0);\n\t}\n\n\treturn new MFunc1D(csc, \"csc\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Tan(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\n\t// tan(-x) = -tan(x)\n\tif (is_neg(a)) return -Tan(-a);\n\n\t// check for multiples of pi\n\tint num, den;\n\tif (pi_multiple(a, num, den))\n\t{\n\t\tif (num == 0) return 0.0;\n\t\tif (den == 1) return 0.0;\n//\t\tif ((num == 1) && (den == 2)) +/- return INFINITY;\n\t\tif ((num == 1) && (den == 3)) return Sqrt(MITEM(3.0));\n\t\tif ((num == 1) && (den == 4)) return 1.0;\n\t\tif ((num == 1) && (den == 6)) return Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 1) && (den == 12)) return MITEM(2.0) - Sqrt(MITEM(3.0));\n\t\tif ((num == 5) && (den == 12)) return MITEM(2.0) + Sqrt(MITEM(3.0));\n\t\tif ((num == 7) && (den == 12)) return -(MITEM(2.0) + Sqrt(MITEM(3.0)));\n\t\tif ((num == 2) && (den == 3 )) return -Sqrt(MITEM(3.0));\n\t\tif ((num == 3) && (den == 4 )) return -1.0;\n\t\tif ((num == 5) && (den == 6 )) return -Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 11) && (den == 12)) return -(MITEM(2.0) - Sqrt(MITEM(3.0)));\n\t}\n\n\treturn new MFunc1D(tan, \"tan\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Cot(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\n\t// cot(-x) = -cot(x)\n\tif (is_neg(a)) return -Cot(-a);\n\n\t// check for multiples of pi\n\tint num, den;\n\tif (pi_multiple(a, num, den))\n\t{\n//\t\tif (num == 0) return +/- INFINITY;\n//\t\tif (den == 1) return +/- INFINITY;\n\t\tif ((num == 1) && (den == 2)) return 0.0;\n\t\tif ((num == 1) && (den == 3)) return Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 1) && (den == 4)) return 1.0;\n\t\tif ((num == 1) && (den == 6)) return Sqrt(MITEM(3.0));\n\t\tif ((num == 1) && (den == 12)) return MITEM(2.0) + Sqrt(MITEM(3.0));\n\t\tif ((num == 5) && (den == 12)) return MITEM(2.0) - Sqrt(MITEM(3.0));\n\t\tif ((num == 7) && (den == 12)) return -(MITEM(2.0) - Sqrt(MITEM(3.0)));\n\t\tif ((num == 2) && (den == 3 )) return -Sqrt(MITEM(3.0))/3.0;\n\t\tif ((num == 3) && (den == 4 )) return -1.0;\n\t\tif ((num == 5) && (den == 6 )) return -Sqrt(MITEM(3.0));\n\t\tif ((num == 11) && (den == 12)) return -(MITEM(2.0) + Sqrt(MITEM(3.0)));\n\t}\n\treturn new MFunc1D(cot, \"cot\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Atan(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\tif (is_neg(a)) return -Atan(-a);\n\tif (a == 0.0) return 0.0;\n\tif (a == 1.0) return MPi/4.0;\n\treturn new MFunc1D(atan, \"atan\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Cosh(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 0) return 1.0;\n\tif (is_neg(l)) return Cosh(-l);\n\treturn new MFunc1D(cosh, \"cosh\", l.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Sinh(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 0) return 0.0;\n\tif (is_neg(l)) return -Sinh(-l);\n\treturn new MFunc1D(sinh, \"sinh\", l.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Exp(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\treturn ME^a;\n}\n\n//-----------------------------------------------------------------------------\n// Natural logarithm (base e)\nMITEM Log(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\tif (a==0.0) return -MINF;\n\tif (a==1.0) return 0.0;\n\tif (is_named(a))\n\t{\n\t\tstring sz = mnamed(a)->Name();\n\t\tif (sz.compare(\"e\") == 0) return 1.0;\n\t}\n\tif (is_pow(a))\n\t{\n\t\tconst MItem* pl = mpow(a)->LeftItem();\n\t\tconst MItem* pr = mpow(a)->RightItem();\n\t\tif (is_named(pl))\n\t\t{\n\t\t\tstring sz = mnamed(pl)->Name();\n\t\t\tif (sz.compare(\"e\") == 0)\n\t\t\t{\n\t\t\t\treturn pr->copy();\n\t\t\t}\n\t\t}\n\t}\n\treturn new MFunc1D(log, \"ln\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\n// base 10 log\nMITEM Log10(const MITEM& a)\n{\n\tif (is_matrix(a)) throw InvalidOperation();\n\tif (isConst(a))\n\t{\n\t\tdouble w = a.value();\n\t\tif (w > 0)\n\t\t{\n\t\t\tdouble p = log10(w);\n\t\t\tdouble pi = (double) ((int) p);\n\t\t\tif (pi == p) return p; \n\t\t}\n\t}\n\treturn new MFunc1D(log10, \"log\", a.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Sqrt(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 1.0) return 1.0;\n\tif (is_pow(l))\n\t{\n\t\tMITEM a = l.Left();\n\t\tMITEM b = l.Right();\n\t\treturn a^(b/2);\n\t}\n\tif (isConst(l))\n\t{\n\t\tdouble a = l.value();\n\t\tdouble f = sqrt(a);\n\t\tif (is_int(f)) return f;\n\n\t\treturn new MFunc1D(sqrt, \"sqrt\", l.copy());\n\t}\n\tif (is_var(l) || is_named(l))\n\t{\n\t\treturn new MFunc1D(sqrt, \"sqrt\", l.copy());\n\t}\n\treturn l ^ Fraction(1,2);\n}\n\n//-----------------------------------------------------------------------------\nMITEM Erf(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 0.0) return 0.0;\n\treturn new MFunc1D(erf, \"erf\", l.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Erfc(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 0.0) return 1.0;\n\treturn new MFunc1D(erfc, \"erfc\", l.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Tn(int n, const MITEM& r)\n{\n\tif (is_matrix(r)) throw InvalidOperation();\n\tif (n >= 0)\n\t{\n\t\tif (n == 0) return MITEM(1.0);\n\t\tif (n == 1) return r;\n\t\treturn MEvaluate(MExpand(2*r*Tn(n-1,r) - Tn(n-2,r)));\n\t}\n\telse return new MFunc2D(chebyshev, \"Tn\", new MConstant((double)n), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Tn(const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r)) throw InvalidOperation();\n\tif (is_int(l) && (l.value() >= 0.0)) return Tn((int) l.value(), r);\n\treturn new MFunc2D(chebyshev, \"Tn\", l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM J0(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 0.0) return 1.0;\n\treturn new MFunc1D(_j0, \"J0\", l.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM J1(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (l == 0.0) return 0.0;\n\treturn new MFunc1D(_j1, \"J1\", l.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM Jn(int n, const MITEM& r)\n{\n\tif (is_matrix(r)) throw InvalidOperation();\n\tif (n >= 0)\n\t{\n\t\tif (r == 0.0) return ((n == 0)?1.0:0.0);\n\t}\n\treturn new MFunc2D(jn, \"Jn\", new MConstant((double)n), r.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM Jn(const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r)) throw InvalidOperation();\n\tif (is_int(l)) return Jn((int)l.value(), r);\n\treturn new MFunc2D(jn, \"Jn\", l.copy(), r.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM Y0(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\treturn new MFunc1D(_y0, \"Y0\", l.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM Y1(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\treturn new MFunc1D(_y1, \"Y1\", l.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM Yn(int n, const MITEM& r)\n{\n\tif (is_matrix(r)) throw InvalidOperation();\n\treturn new MFunc2D(yn, \"Yn\", new MConstant((double)n), r.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\n#ifdef WIN32\nMITEM Yn(const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r)) throw InvalidOperation();\n\tif (is_int(l)) return Yn((int)l.value(), r);\n\treturn new MFunc2D(yn, \"Yn\", l.copy(), r.copy());\n}\n#endif\n\n//-----------------------------------------------------------------------------\nMITEM Fac(const MITEM& l)\n{\n\tif (is_matrix(l)) throw InvalidOperation();\n\tif (is_int(l) && (l.value() >= 0)) return fac(l.value());\n\treturn new MFunc1D(fac, \"fac\", l.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Binomial(const MITEM& l, const MITEM& r)\n{\n\tif (is_matrix(l) || is_matrix(r)) throw InvalidOperation();\n\tif (is_rconst(l.ItemPtr()) && is_rconst(r.ItemPtr())) return binomial(l.value(), r.value());\n\treturn new MFunc2D(binomial, \"binomial\", l.copy(), r.copy());\n}\n\n//-----------------------------------------------------------------------------\nMITEM Identity(const MITEM& n)\n{\n\tif (is_int(n))\n\t{\n\t\tint m = (int) n.value();\n\t\tif (m <= 0) throw InvalidOperation();\n\t\treturn matrix_identity(m);\n\t}\n\telse throw InvalidOperation();\n}\n\n//-----------------------------------------------------------------------------\nMITEM Float(const MITEM& a)\n{\n\tif (is_rconst(a.ItemPtr()))\n\t{\n\t\tif (is_number(a.ItemPtr())) return a.value();\n\t\tif (is_neg(a)) return -Float(-a);\n\t\tif (is_add(a)) return Float(a.Left()).value() + Float(a.Right()).value();\n\t\tif (is_sub(a)) return Float(a.Left()).value() - Float(a.Right()).value();\n\t\tif (is_mul(a)) return Float(a.Left()).value() * Float(a.Right()).value();\n\t\tif (is_div(a)) return Float(a.Left()).value() / Float(a.Right()).value();\n\t\tif (is_pow(a))\n\t\t{\n\t\t\tif (is_neg(mpow(a)->RightItem())) return pow(Float(a.Left()).value(), -(Float(-(a.Right())).value()));\n\t\t\telse return pow(Float(a.Left()).value(), Float(a.Right()).value());\n\t\t}\n\t\tif (is_func1d(a))\n\t\t{\n\t\t\tFUNCPTR pf = mfnc1d(a)->funcptr();\n\t\t\tMITEM v(mfnc1d(a)->Item()->copy());\n\t\t\treturn pf(Float(v).value());\n\t\t}\n\t\treturn a.value();\n\t}\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nbool is_equal(const MItem* pl, const MItem* pr)\n{\n\tif ((pl == 0)&&(pr == 0)) return true;\n\tif ((pl == 0)||(pr == 0)) return false;\n\n\tItem_Type t = pl->Type();\n\tif (t != pr->Type()) return false;\n\tswitch (t)\n\t{\n\tcase MCONST: \n\tcase MFRAC:\n\tcase MNAMED: return (mnumber(pl)->value() == mnumber(pr)->value());\n\tcase MVAR:\n\t\t{\n\t\t\tconst string& vl = mvar(pl)->Name();\n\t\t\tconst string& vr = mvar(pr)->Name();\n\t\t\treturn (vl.compare(vr) == 0);\n\t\t}\n\tcase MNEG:\n\t\t{\n\t\t\tconst MItem* pa = munary(pl)->Item();\n\t\t\tconst MItem* pb = munary(pr)->Item();\n\t\t\treturn is_equal(pa, pb);\n\t\t}\n\tcase MADD:\n\t\t{\n\t\t\tconst MItem* pl1 = mbinary(pl)->LeftItem(), *pr1 = mbinary(pl)->RightItem();\n\t\t\tconst MItem* pl2 = mbinary(pr)->LeftItem(), *pr2 = mbinary(pr)->RightItem();\n\n\t\t\t// TODO: since this only looks one level deep, this may not always return the correct answer\n\t\t\treturn ((is_equal(pl1, pl2) && is_equal(pr1, pr2)) || (is_equal(pl1, pr2) && is_equal(pr1, pl2)));\n\t\t}\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l(pl->copy());\n\t\t\tMITEM r(pr->copy());\n\t\t\tMProduct a(l), b(r);\n\t\t\treturn (a==b);\n\t\t}\n\tcase MSUB:\n\tcase MDIV:\n\tcase MPOW:\n\t\t{\n\t\t\tconst MItem* pl1 = mbinary(pl)->LeftItem(), *pr1 = mbinary(pl)->RightItem();\n\t\t\tconst MItem* pl2 = mbinary(pr)->LeftItem(), *pr2 = mbinary(pr)->RightItem();\n\t\t\treturn (is_equal(pl1, pl2) && is_equal(pr1, pr2));\n\t\t}\n\tcase MF1D:\n\t\t{\n\t\t\tif (mfnc1d(pl)->funcptr() == mfnc1d(pr)->funcptr())\n\t\t\t{\n\t\t\t\tconst MItem* pa = munary(pl)->Item();\n\t\t\t\tconst MItem* pb = munary(pr)->Item();\n\t\t\t\treturn is_equal(pa, pb);\n\t\t\t}\n\t\t\telse return false;\n\t\t}\n\tcase MSFNC:\n\t\t{\n\t\t\tconst string& vl = msfncnd(pl)->Name();\n\t\t\tconst string& vr = msfncnd(pr)->Name();\n\t\t\tif (vl.compare(vr) != 0) return false;\n\t\t\tconst MItem* pa = msfncnd(pl)->Value();\n\t\t\tconst MItem* pb = msfncnd(pr)->Value();\n\t\t\treturn is_equal(pa, pb);\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// See if the expression is constant. That is, there are no variables in\n// this expression.\nbool is_rconst(const MItem* pi)\n{\n\tif (is_var(pi)) return false;\n\tif (isConst(pi) || is_named(pi) || is_frac(pi)) return true;\n\tif (is_unary(pi)) return is_rconst(munary(pi)->Item());\n\tif (is_binary(pi))\n\t{\n\t\tconst MItem* pl = mbinary(pi)->LeftItem ();\n\t\tconst MItem* pr = mbinary(pi)->RightItem();\n\t\treturn (is_rconst(pl) && is_rconst(pr));\n\t}\n\tif (is_nary(pi))\n\t{\n\t\tconst MNary* pn = mnary(pi);\n\t\tint n = pn->Params();\n\t\tfor (int i=0; i<n; ++i)\n\t\t\tif (is_rconst(pn->Param(i)) == false) return false;\n\t\treturn true;\n\t}\n\tif (is_matrix(pi))\n\t{\n\t\tconst MMatrix& A = *mmatrix(pi);\n\t\tfor (int i=0; i<A.rows(); ++i)\n\t\t\tfor (int j=0; j<A.columns(); ++j)\n\t\t\t{\n\t\t\t\tMItem* pij = A[i][j];\n\t\t\t\tif (is_rconst(pij) == false) return false;\n\t\t\t}\n\t\treturn true;\n\t}\n\tassert(false);\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// See if the expression is dependent on the variable x\nbool is_dependent(const MItem* pi, const MVariable& x)\n{\n\tif (is_var(pi)) return (mvar(pi)->Name().compare(x.Name()) == 0);\n\tif (isConst(pi) || is_named(pi) || is_frac(pi)) return false;\n\tif (is_unary(pi)) return is_dependent(munary(pi)->Item(), x);\n\tif (is_binary(pi))\n\t{\n\t\tconst MItem* pl = mbinary(pi)->LeftItem ();\n\t\tconst MItem* pr = mbinary(pi)->RightItem();\n\t\treturn (is_dependent(pl, x) || is_dependent(pr, x));\n\t}\n\tif (is_nary(pi))\n\t{\n\t\tconst MNary* pn = mnary(pi);\n\t\tint n = pn->Params();\n\t\tbool b = false;\n\t\tfor (int i=0; i<n; ++i) b = (b || is_dependent(pn->Param(i), x));\n\t\treturn b;\n\t}\n\tif (is_matrix(pi))\n\t{\n\t\tconst MMatrix& m = *mmatrix(pi);\n\t\tint nr = m.rows();\n\t\tint nc = m.columns();\n\t\tfor (int i=0; i<nr; ++i)\n\t\t\tfor (int j=0; j<nc; ++j)\n\t\t\t{\n\t\t\t\tconst MItem* mij = m(i,j);\n\t\t\t\tif (is_dependent(mij, x)) return true;\n\t\t\t}\n\t\treturn false;\n\t}\n\tassert(false);\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// See if the expression contains the expression px\nbool is_dependent(const MItem* pi, const MItem* px)\n{\n\tif (is_equal(pi, px)) return true;\n\tif (is_unary(pi)) return is_dependent(munary(pi)->Item(), px);\n\tif (is_binary(pi))\n\t{\n\t\tconst MItem* pl = mbinary(pi)->LeftItem ();\n\t\tconst MItem* pr = mbinary(pi)->RightItem();\n\t\treturn (is_dependent(pl, px) || is_dependent(pr, px));\n\t}\n\tif (is_nary(pi))\n\t{\n\t\tconst MNary* pn = mnary(pi);\n\t\tint n = pn->Params();\n\t\tbool b = false;\n\t\tfor (int i=0; i<n; ++i) b = (b || is_dependent(pn->Param(i), px));\n\t\treturn b;\n\t}\n\tif (is_matrix(pi))\n\t{\n\t\tconst MMatrix& m = *mmatrix(pi);\n\t\tint nr = m.rows();\n\t\tint nc = m.columns();\n\t\tfor (int i=0; i<nr; ++i)\n\t\t\tfor (int j=0; j<nc; ++j)\n\t\t\t{\n\t\t\t\tconst MItem* mij = m(i,j);\n\t\t\t\tif (is_dependent(mij, px)) return true;\n\t\t\t}\n\t\treturn false;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// see if the item is the named constant pi\nbool is_pi(const MItem* pi)\n{\n\tif (is_named(pi))\n\t{\n\t\tconst string& sz = mnamed(pi)->Name();\n\t\tif (sz.compare(\"pi\") == 0) return true;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// see if the expression is a scalar expression (e.g. does not contain matrices)\nbool is_scalar(const MItem* pi)\n{\n\tif (is_number(pi)) return true;\n\tif (is_unary(pi)) return ::is_scalar(munary(pi)->Item());\n\tif (is_binary(pi))\n\t{\n\t\tconst MItem* pl = mbinary(pi)->LeftItem ();\n\t\tconst MItem* pr = mbinary(pi)->RightItem();\n\t\treturn (::is_scalar(pl) && ::is_scalar(pr));\n\t}\n\tif (is_nary(pi))\n\t{\n\t\tconst MNary* pn = mnary(pi);\n\t\tint n = pn->Params();\n\t\tbool b = true;\n\t\tfor (int i=0; i<n; ++i) b = (b && ::is_scalar(pn->Param(i)));\n\t\treturn b;\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the number of operations for the expression.\nint op_count(const MItem* pi)\n{\n\tint n = 0;\n\tif (is_unary(pi)) n = op_count(munary(pi)->Item()) + 1;\n\telse if (is_binary(pi))\n\t{\n\t\tint n1 = op_count(mbinary(pi)->LeftItem());\n\t\tint n2 = op_count(mbinary(pi)->RightItem());\n\t\tn = n1 + n2 + 1;\n\t}\n\telse if (is_nary(pi))\n\t{\n\t\tn = 1;\n\t\tconst MNary* pn = mnary(pi);\n\t\tint N = pn->Params();\n\t\tfor (int i=0; i<N; ++i) n += op_count(pn->Param(i));\n\t}\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\nconst char* read_format(const MItem* pe, const char* sz)\n{\n\tswitch (sz[0])\n\t{\n\tcase '+':\n\t\t{\n\t\t\tconst MBinary* pb = mbinary(pe);\n\t\t\tif (pe->Type() != MADD) return nullptr;\n\t\t\tsz = read_format(pb->LeftItem(), sz+1);\n\t\t\tread_format(pb->RightItem(), sz);\t\t\t\n\t\t}\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool is_format(const MItem* pe, const char* sz)\n{\n\treturn (read_format(pe, sz) != 0);\n}\n"
  },
  {
    "path": "FECore/MItem.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"MFunctions.h\"\n#include \"MTypes.h\"\n#include <vector>\n#include <assert.h>\n#include <string>\n#include <string.h>\n\n//-----------------------------------------------------------------------------\n// This exception will get thrown when an invalid operation is performed.\nclass InvalidOperation {};\nclass DivisionByZero   {};\n\n//-----------------------------------------------------------------------------\n// The following classes define the componenents of a mathematical expression\nenum Item_Type {\n\tMCONST, MFRAC, MNAMED, MVAR, MBOOL,\n\tMMATRIX,\n\tMNEG,\n\tMADD, MSUB, MMUL, MDIV, MPOW,\n\tMEQUATION, MEQUALITY,\n\tMF1D, MF2D, MFND, MFMAT, MFMAT2,\n\tMSFNC,\n\tMINTEGRAL,\n\tMSEQUENCE\n};\n\n//-----------------------------------------------------------------------------\n// stores the value of a variable\nclass MVariable\n{\npublic:\n\tMVariable(const std::string& s, double initVal = 0.0) : m_v(initVal), m_name(s), m_index(-1) {}\n\tMVariable(const MVariable& v) : m_v(0), m_name(v.m_name), m_index(-1) {}\n\tvoid operator = (const MVariable& v) { m_name = v.m_name; m_index = v.m_index; }\n\tdouble value() const { return m_v; }\n\tvoid value(double v) { m_v = v; }\n\tconst std::string& Name() const { return m_name; }\n\tvoid setIndex(int n) { m_index = n; }\n\tint index() const { return m_index; }\n\nprotected:\n\tdouble\t\tm_v;\n\tint\t\t\tm_index;\n\tstd::string\tm_name;\n};\n\n//-----------------------------------------------------------------------------\n// base class for Math items\n// a math item can be a constant, a variable, a matrix, an operator or a function\nclass MItem\n{\npublic:\n\tMItem(Item_Type ntype) : m_ntype(ntype) {}\n\tvirtual ~MItem(){}\n\n\t// return the item's type\n\tItem_Type Type() const { return m_ntype; }\n\n\t// create a copy of this item\n\tvirtual MItem* copy() const = 0;\n\nprotected:\n\tItem_Type m_ntype;\n};\n\n//-----------------------------------------------------------------------------\n// base class for numbers.\n// numbers can be either constant or variables.\nclass MNumber : public MItem\n{\npublic: \n\tMNumber(Item_Type ntype) : MItem(ntype){}\n\n\t// return the float value for this number\n\tvirtual double value() const = 0;\n};\n\n//-----------------------------------------------------------------------------\n// defines a constant number\nclass MConstant : public MNumber\n{\npublic:\n\tMConstant(double x) : MNumber(MCONST), m_v(x) { assert(m_v>=0); }\n\tvoid value(double v) { m_v = v; }\n\npublic:\n\tdouble value() const override { return m_v; }\n\tMItem* copy() const override { return new MConstant(m_v); }\n\nprivate:\n\tdouble\tm_v;\n};\n\n//-----------------------------------------------------------------------------\n// defines a constant fraction\nclass MFraction : public MNumber\n{\npublic:\n\tMFraction(double a, double b) : MNumber(MFRAC), m_v(a,b) {}\n\tMFraction(const FRACTION& a) : MNumber(MFRAC), m_v(a) {}\n\tFRACTION fraction() const { return m_v; }\n\npublic:\n\tdouble value() const override { return m_v.n / m_v.d; }\n\tMItem* copy() const override { return new MFraction(m_v); }\n\nprivate:\n\tFRACTION\tm_v;\n};\n\n//-----------------------------------------------------------------------------\n// defines a named constant number\nclass MNamedCt : public MNumber\n{\npublic:\n\tMNamedCt(double x, const std::string& sz) : MNumber(MNAMED), m_v(x), m_name(sz) {}\n\tconst std::string& Name() const { return m_name; }\n\npublic:\n\tdouble value() const override { return m_v; }\n\tMItem* copy() const override { return new MNamedCt(m_v, m_name); }\n\nprivate:\n\tdouble\t\t\tm_v;\n\tstd::string\t\tm_name;\n};\n\n//-----------------------------------------------------------------------------\n// defines a reference to a variable\nclass MVarRef : public MNumber\n{\npublic:\n\tMVarRef(const MVariable* pv) : MNumber(MVAR), m_pv(pv) {}\n\tconst MVariable* GetVariable() const { return m_pv; }\n\tconst std::string& Name() const { return m_pv->Name(); }\n\tint index() const { return m_pv->index(); }\n\n\tvoid SetVariable(const MVariable* v) { m_pv = v; }\n\npublic:\n\tdouble value() const override { return m_pv->value(); }\n\tMItem* copy() const override { return new MVarRef(m_pv); }\n\nprivate:\n\tconst MVariable*\tm_pv;\n};\n\n//-----------------------------------------------------------------------------\n// Item for representing boolean values\nclass MBoolean : public MItem\n{\npublic:\n\tMBoolean(bool b) : MItem(MBOOL), m_b(b) {}\n\tMItem* copy() const override { return new MBoolean(m_b); }\n\n\tbool value() { return m_b; }\n\nprivate:\n\tbool\tm_b;\n};\n\n//-----------------------------------------------------------------------------\n// base class for unary operations\nclass MUnary : public MItem\n{\npublic:\n\tMUnary(MItem* pi, Item_Type ntype) : MItem(ntype), m_pi(pi) {}\n\t~MUnary() { delete m_pi; }\n\t\n\tMItem* Item() { return m_pi; }\n\tconst MItem* Item() const { return m_pi; }\n\nprotected:\n\tMItem*\tm_pi;\n};\n\n//-----------------------------------------------------------------------------\n// base class for binary operations\nclass MBinary : public MItem\n{\npublic:\n\tMBinary(MItem* pl, MItem* pr, Item_Type ntype) : MItem(ntype), m_pleft(pl), m_pright(pr) {}\n\t~MBinary() { delete m_pleft; delete m_pright; }\n\n\tMItem* LeftItem() { return m_pleft; }\n\tMItem* RightItem() { return m_pright; }\n\n\tconst MItem* LeftItem() const { return m_pleft; }\n\tconst MItem* RightItem() const { return m_pright; }\n\nprotected:\n\tMItem *m_pleft, *m_pright;\n};\n\n//-----------------------------------------------------------------------------\n// a list of items\nclass MSequence : public MItem\n{\npublic:\n\tMSequence() : MItem(MSEQUENCE){}\n\tMSequence(const MSequence& s);\n\t~MSequence();\n\n\tMSequence* copy() const override { return new MSequence(*this); }\n\tMSequence& operator = (MSequence& s);\n\n\tMItem* operator [] (int i) { return m_item[i]; }\n\tconst MItem* operator [] (int i) const { return m_item[i]; }\n\n\tint size() const { return (int) m_item.size(); }\n\n\tvoid add(MItem* pi) { m_item.push_back(pi); }\n\n\tvoid replace(int i, MItem* pi);\n\n\tvoid remove(int i);\n\nprotected:\n\tstd::vector<MItem*>\tm_item;\n};\n\n//-----------------------------------------------------------------------------\n// base class for N-ary operators and functions\nclass MNary : public MItem\n{\npublic:\n\tMNary(const MSequence& s, Item_Type ntype) : MItem(ntype), m_s(s) {}\n\t~MNary() {}\n\tint Params() const { return (int) m_s.size(); }\n\tMItem* Param(int n) { return m_s[n]; }\n\tconst MItem* Param(int n) const { return m_s[n]; }\n\tMSequence& GetSequence() { return m_s; }\n\tconst MSequence& GetSequence() const { return m_s; }\n\nprotected:\n\tMSequence\tm_s;\n};\n\n//-----------------------------------------------------------------------------\n// defines a negative operation\nclass MNeg : public MUnary\n{\npublic: \n\tMNeg(MItem* pi) : MUnary(pi, MNEG) {}\n\tMItem* copy() const override { return new MNeg(m_pi->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// addition operator\nclass MAdd : public MBinary\n{\npublic:\n\tMAdd(MItem* pl, MItem* pr) : MBinary(pl, pr, MADD){}\n\tMAdd(MItem* pl, double  r) : MBinary(pl, new MConstant(r), MADD){}\n\tMItem* copy() const override { return new MAdd(m_pleft->copy(), m_pright->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// subtraction operator\nclass MSub : public MBinary\n{\npublic:\n\tMSub(MItem* pl, MItem* pr) : MBinary(pl, pr, MSUB){}\n\tMSub(double  l, MItem* pr) : MBinary(new MConstant(l), pr, MSUB){}\n\tMSub(MItem* pl, double  r) : MBinary(pl, new MConstant(r), MSUB){}\n\tMItem* copy() const override { return new MSub(m_pleft->copy(), m_pright->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// multiplication operator\nclass MMul : public MBinary\n{\npublic:\n\tMMul(MItem* pl, MItem* pr) : MBinary(pl, pr, MMUL){}\n\tMMul(double  l, MItem* pr) : MBinary(new MConstant(l), pr, MMUL){}\n\tMItem* copy() const override { return new MMul(m_pleft->copy(), m_pright->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// division operator\nclass MDiv : public MBinary\n{\npublic:\n\tMDiv(MItem* pl, MItem* pr) : MBinary(pl, pr, MDIV){}\n\tMDiv(double  l, MItem* pr) : MBinary(new MConstant(l), pr, MDIV){}\n\tMDiv(MItem* pl, double  r) : MBinary(pl, new MConstant(r), MDIV){}\n\tMDiv(double  l, double  r) : MBinary(new MConstant(l), new MConstant(r), MDIV){}\n\tMItem* copy() const override { return new MDiv(m_pleft->copy(), m_pright->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// power operator\nclass MPow : public MBinary\n{\npublic:\n\tMPow(MItem* pl, MItem* pr) : MBinary(pl, pr, MPOW){}\n\tMPow(MItem* pl, double  r) : MBinary(pl, new MConstant(r), MPOW){}\n\tMItem* copy() const override { return new MPow(m_pleft->copy(), m_pright->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// equal-than operator\nclass MEquation : public MBinary\n{\npublic:\n\tMEquation(MItem* pl, MItem* pr) : MBinary(pl, pr, MEQUATION){}\n\tMItem* copy() const override { return new MEquation(m_pleft->copy(), m_pright->copy()); }\n};\n\n//-----------------------------------------------------------------------------\n// equality test\nclass MEquality : public MBinary\n{\npublic:\n\tMEquality(MItem* pl, MItem* pr) : MBinary(pl, pr, MEQUALITY){}\n\tMItem* copy() const override { return new MEquality(m_pleft->copy(), m_pright->copy()); }\n};\n\n\n//-----------------------------------------------------------------------------\n// function of one variable\nclass MFunc1D : public MUnary\n{\npublic: \n\tMFunc1D(FUNCPTR pf, const std::string& s, MItem* pi) : MUnary(pi, MF1D), m_name(s), m_pf(pf) {}\n\tMItem* copy() const override { return new MFunc1D(m_pf, m_name, m_pi->copy()); }\n\tconst std::string& Name() const { return m_name; }\n\tFUNCPTR funcptr() const { return m_pf; }\n\nprotected:\n\tstd::string\tm_name;\n\tFUNCPTR\t\tm_pf;\n};\n\n//-----------------------------------------------------------------------------\n// function of two variables\nclass MFunc2D : public MBinary\n{\npublic: \n\tMFunc2D(FUNC2PTR pf, const std::string& s, MItem* p1, MItem* p2) : MBinary(p1, p2, MF2D), m_name(s), m_pf(pf)  {}\n\tMItem* copy() const override { return new MFunc2D(m_pf, m_name, m_pleft->copy(), m_pright->copy()); }\n\tconst std::string& Name() const { return m_name; }\n\tFUNC2PTR\tfuncptr() const { return m_pf; }\n\nprotected:\n\tstd::string\t\tm_name;\n\tFUNC2PTR\t\tm_pf;\n};\n\n//-----------------------------------------------------------------------------\n// function of N variables\nclass MFuncND : public MNary\n{\npublic: \n\tMFuncND(FUNCNPTR pf, const std::string& s, const MSequence& l) : MNary(l, MFND), m_name(s), m_pf(pf) {}\n\tMItem* copy() const override;\n\tconst std::string& Name() const { return m_name; }\n\tFUNCNPTR funcptr() const { return m_pf; }\n\nprotected:\n\tstd::string\t\tm_name;\n\tFUNCNPTR\t\tm_pf;\n};\n\n//-----------------------------------------------------------------------------\n// class for a symbolic function definition of N variables\nclass MSFuncND : public MNary\n{\npublic:\n\tMSFuncND(const std::string& s, MItem* pv, const MSequence* ps) : MNary(*ps, MSFNC), m_name(s), m_pval(pv) {}\n\t~MSFuncND(){ delete m_pval; }\n\tMItem* copy() const override { return new MSFuncND(m_name, m_pval->copy(), &GetSequence()); }\n\n\tconst MItem* Value() const { return m_pval; }\n\n\tconst std::string& Name() const { return m_name; }\n\nprivate:\n\tstd::string\t\tm_name;\n\tMItem*\t\t\tm_pval;\t//!< result of function evaluation\n};\n\n\n//-----------------------------------------------------------------------------\n// Symbolic operator\nclass MSymOp : public MItem\n{\npublic:\n\tMSymOp(Item_Type ntype) : MItem(ntype) {}\n};\n\n//-----------------------------------------------------------------------------\nclass MOpIntegral : public MSymOp\n{\npublic:\n\tMOpIntegral(MItem* pf, MVarRef* px) : MSymOp(MINTEGRAL), m_pf(pf), m_px(px){}\n\t~MOpIntegral() { delete m_pf; delete m_px; }\n\n\tMItem* copy() const override { return new MOpIntegral(m_pf->copy(), dynamic_cast<MVarRef*>(m_px->copy())); }\n\n\tMItem* Item() { return m_pf; }\n\tMVarRef* Var() { return m_px; }\n\nprotected:\n\tMItem*\t\tm_pf;\n\tMVarRef*\tm_px;\n};\n\n//-----------------------------------------------------------------------------\ninline const MNumber*   mnumber  (const MItem* pi) { return static_cast<const MNumber*  >(pi); }\ninline const MConstant* mconst   (const MItem* pi) { return static_cast<const MConstant*>(pi); }\ninline const MFraction* mfrac    (const MItem* pi) { return static_cast<const MFraction*>(pi); }\ninline const MNamedCt*  mnamed   (const MItem* pi) { return static_cast<const MNamedCt *>(pi); }\ninline const MVarRef*   mvar     (const MItem* pi) { return static_cast<const MVarRef  *>(pi); }\ninline const MBoolean*  mboolean (const MItem* pi) { return static_cast<const MBoolean* >(pi); }\ninline const MNeg*      mneg     (const MItem* pi) { return static_cast<const MNeg     *>(pi); }\ninline const MAdd*      madd     (const MItem* pi) { return static_cast<const MAdd     *>(pi); }\ninline const MSub*      msub     (const MItem* pi) { return static_cast<const MSub     *>(pi); }\ninline const MMul*      mmul     (const MItem* pi) { return static_cast<const MMul     *>(pi); }\ninline const MDiv*      mdiv     (const MItem* pi) { return static_cast<const MDiv     *>(pi); }\ninline const MPow*      mpow     (const MItem* pi) { return static_cast<const MPow     *>(pi); }\ninline const MUnary*    munary   (const MItem* pi) { return static_cast<const MUnary   *>(pi); }\ninline const MBinary*   mbinary  (const MItem* pi) { return static_cast<const MBinary  *>(pi); }\ninline const MNary*     mnary    (const MItem* pi) { return static_cast<const MNary    *>(pi); }\ninline const MFunc1D*   mfnc1d   (const MItem* pi) { return static_cast<const MFunc1D  *>(pi); }\ninline const MFunc2D*   mfnc2d   (const MItem* pi) { return static_cast<const MFunc2D  *>(pi); }\ninline const MFuncND*   mfncnd   (const MItem* pi) { return static_cast<const MFuncND  *>(pi); }\ninline const MSFuncND*  msfncnd  (const MItem* pi) { return static_cast<const MSFuncND *>(pi); }\ninline const MSequence* msequence(const MItem* pi) { return static_cast<const MSequence*>(pi); }\ninline const MEquation* mequation(const MItem* pi) { return static_cast<const MEquation*>(pi); }\ninline const MEquality* mequality(const MItem* pi) { return static_cast<const MEquality*>(pi); }\n\n//-----------------------------------------------------------------------------\n// This class is defined so that we can do arithmetic with the  math classes \n// without worrying about making copies and garbage collection. Each MITEM \n// stores its own expression. This will probably cause a proliferation of \n// copying, so perhaps I can come up with a better memory managment model.\n// (e.g. like auto_ptr, where owner- ship of an pointer can be passed along)\nclass MITEM\n{\npublic:\n\tMITEM() { m_pi = 0; }\n\t~MITEM() { delete m_pi; }\n\t//~MITEM() { }\n\n\tMITEM(MItem* pi) { m_pi = pi; } // Note that the item is not copied in this constructor\n\tMITEM(const MItem* pi) { m_pi = pi->copy(); }\n\tMITEM(const MITEM& it) { m_pi = (it.m_pi?it.m_pi->copy():0); }\n\tMITEM(const MVariable& v) { m_pi = new MVarRef(&v); }\n\tMITEM(double g) \n\t{\n\t\tm_pi = new MConstant(fabs(g));\n\t\tif (g < 0) m_pi = new MNeg(m_pi);\n\t}\n\tMITEM(FRACTION& g)\n\t{\n\t\tm_pi = new MFraction(fabs(g.n), fabs(g.d));\n\t\tif (g < 0) m_pi = new MNeg(m_pi);\n\t}\n\n\tvoid operator = (const MITEM& it) { delete m_pi; m_pi = it.m_pi->copy(); }\n\tvoid operator = (MItem* pi) { delete m_pi; m_pi = pi; }\n\n\tMItem* ItemPtr() { return m_pi; }\n\tconst MItem* ItemPtr() const { return m_pi; }\n\n\tMItem* copy() const { return m_pi->copy(); }\n\n\tMITEM Left () const { return (static_cast<MBinary*>(m_pi))->LeftItem()->copy(); }\n\tMITEM Right() const { return (static_cast<MBinary*>(m_pi))->RightItem()->copy(); }\n\n\tMITEM Item() const { return (static_cast<MUnary*>(m_pi))->Item()->copy(); }\n\n\tMITEM Param() const { return (static_cast<MFunc1D*>(m_pi))->Item()->copy(); }\n\n\tItem_Type Type() const { return m_pi->Type(); }\n\n\tdouble value() const { return (Type()==MNEG? -mnumber(mneg(m_pi)->Item())->value() : mnumber(m_pi)->value()); }\n\nprivate:\n\tMItem*\tm_pi;\n};\n\n//-----------------------------------------------------------------------------\ninline const MNumber*   mnumber  (const MITEM& i) { return static_cast<const MNumber  *>(i.ItemPtr()); }\ninline const MConstant* mconst   (const MITEM& i) { return static_cast<const MConstant*>(i.ItemPtr()); }\ninline const MFraction* mfrac    (const MITEM& i) { return static_cast<const MFraction*>(i.ItemPtr()); }\ninline const MNamedCt*  mnamed   (const MITEM& i) { return static_cast<const MNamedCt *>(i.ItemPtr()); }\ninline const MVarRef*   mvar     (const MITEM& i) { return static_cast<const MVarRef  *>(i.ItemPtr()); }\ninline const MBoolean*  mboolean (const MITEM& i) { return static_cast<const MBoolean* >(i.ItemPtr()); }\ninline const MNeg*      mneg     (const MITEM& i) { return static_cast<const MNeg     *>(i.ItemPtr()); }\ninline const MAdd*      madd     (const MITEM& i) { return static_cast<const MAdd     *>(i.ItemPtr()); }\ninline const MSub*      msub     (const MITEM& i) { return static_cast<const MSub     *>(i.ItemPtr()); }\ninline const MMul*      mmul     (const MITEM& i) { return static_cast<const MMul     *>(i.ItemPtr()); }\ninline const MDiv*      mdiv     (const MITEM& i) { return static_cast<const MDiv     *>(i.ItemPtr()); }\ninline const MPow*      mpow     (const MITEM& i) { return static_cast<const MPow     *>(i.ItemPtr()); }\ninline const MUnary*    munary   (const MITEM& i) { return static_cast<const MUnary   *>(i.ItemPtr()); }\ninline const MBinary*   mbinary  (const MITEM& i) { return static_cast<const MBinary  *>(i.ItemPtr()); }\ninline const MNary*     mnary    (const MITEM& i) { return static_cast<const MNary    *>(i.ItemPtr()); }\ninline const MFunc1D*   mfnc1d   (const MITEM& i) { return static_cast<const MFunc1D  *>(i.ItemPtr()); }\ninline const MFunc2D*   mfnc2d   (const MITEM& i) { return static_cast<const MFunc2D  *>(i.ItemPtr()); }\ninline const MSFuncND*  msfncnd  (const MITEM& i) { return static_cast<const MSFuncND *>(i.ItemPtr()); }\ninline const MSequence* msequence(const MITEM& i) { return static_cast<const MSequence*>(i.ItemPtr()); }\ninline const MEquation* mequation(const MITEM& i) { return static_cast<const MEquation*>(i.ItemPtr()); }\ninline const MEquality* mequality(const MITEM& i) { return static_cast<const MEquality*>(i.ItemPtr()); }\n\n//-----------------------------------------------------------------------------\n// Some helper functions to determine the type of an MItem\ninline bool is_number(const MItem* pi) { return (dynamic_cast<const MNumber  *>(pi) != 0); }\ninline bool is_unary (const MItem* pi) { return (dynamic_cast<const MUnary   *>(pi) != 0); }\ninline bool is_binary(const MItem* pi) { return (dynamic_cast<const MBinary  *>(pi) != 0); }\ninline bool is_nary  (const MItem* pi) { return (dynamic_cast<const MNary    *>(pi) != 0); }\n\ninline bool isConst    (const MItem* pi) { return (pi->Type() == MCONST   ); }\ninline bool is_frac    (const MItem* pi) { return (pi->Type() == MFRAC    ); }\ninline bool is_named   (const MItem* pi) { return (pi->Type() == MNAMED   ); }\ninline bool is_var     (const MItem* pi) { return (pi->Type() == MVAR     ); }\ninline bool is_boolean (const MItem* pi) { return (pi->Type() == MBOOL    ); }\ninline bool is_neg     (const MItem* pi) { return (pi->Type() == MNEG     ); }\ninline bool is_add     (const MItem* pi) { return (pi->Type() == MADD     ); }\ninline bool is_sub     (const MItem* pi) { return (pi->Type() == MSUB     ); }\ninline bool is_mul     (const MItem* pi) { return (pi->Type() == MMUL     ); }\ninline bool is_div     (const MItem* pi) { return (pi->Type() == MDIV     ); }\ninline bool is_pow     (const MItem* pi) { return (pi->Type() == MPOW     ); }\ninline bool is_equation(const MItem* pi) { return (pi->Type() == MEQUATION); }\ninline bool is_equality(const MItem* pi) { return (pi->Type() == MEQUALITY); }\ninline bool is_func1d  (const MItem* pi) { return (pi->Type() == MF1D     ); }\ninline bool is_matrix  (const MItem* pi) { return (pi->Type() == MMATRIX  ); }\ninline bool is_sfunc   (const MItem* pi) { return (pi->Type() == MSFNC    ); }\n\ninline bool isConst    (const MITEM& i) { return (i.Type() == MCONST   ); }\ninline bool is_frac    (const MITEM& i) { return (i.Type() == MFRAC    ); }\ninline bool is_named   (const MITEM& i) { return (i.Type() == MNAMED   ); }\ninline bool is_var     (const MITEM& i) { return (i.Type() == MVAR     ); }\ninline bool is_boolean (const MITEM& i) { return (i.Type() == MBOOL    ); }\ninline bool is_neg     (const MITEM& i) { return (i.Type() == MNEG     ); }\ninline bool is_add     (const MITEM& i) { return (i.Type() == MADD     ); }\ninline bool is_sub     (const MITEM& i) { return (i.Type() == MSUB     ); }\ninline bool is_mul     (const MITEM& i) { return (i.Type() == MMUL     ); }\ninline bool is_div     (const MITEM& i) { return (i.Type() == MDIV     ); }\ninline bool is_pow     (const MITEM& i) { return (i.Type() == MPOW     ); }\ninline bool is_equation(const MITEM& i) { return (i.Type() == MEQUATION); }\ninline bool is_equality(const MITEM& i) { return (i.Type() == MEQUALITY); }\ninline bool is_func1d  (const MITEM& i) { return (i.Type() == MF1D     ); }\ninline bool is_matrix  (const MITEM& i) { return (i.Type() == MMATRIX  ); }\ninline bool is_sequence(const MITEM& i) { return (i.Type() == MSEQUENCE); }\ninline bool is_sfunc   (const MITEM& i) { return (i.Type() == MSFNC    ); }\n\ninline bool is_number(const MITEM& i) { return is_number(i.ItemPtr()); }\n\ninline bool is_func(const MItem* pi) { Item_Type t = pi->Type(); return ((t==MF1D) || (t==MF2D) || (t==MFND)); }\ninline bool is_int (double  r) { return (r - floor(r) == 0.0); }\ninline bool is_abs (const MItem* pi) { return (is_func1d(pi) && (mfnc1d(pi)->Name().compare(\"abs\") == 0)); }\n\ninline bool is_func(const MITEM& i) { return is_func(i.ItemPtr()); }\ninline bool is_abs (const MITEM& i) { return is_abs (i.ItemPtr()); }\n\ninline bool is_int(const MItem* pi) { return isConst(pi) && is_int(mnumber(pi)->value()); }\ninline bool is_int (const MITEM& i) { return is_int(i.ItemPtr()); }\n\ninline bool is_fnc1d(const MITEM& i, const char* sz) { return (is_func1d(i) && (strcmp(mfnc1d(i)->Name().c_str(), sz) == 0)); }\n\nbool is_rconst(const MItem* pi); // is recursive constant\nbool is_dependent(const MItem* pi, const MVariable& x); // is i dependent on x\nbool is_dependent(const MItem* pi, const MItem* px); // is i dependent on x\nbool is_scalar(const MItem* pi);\t// is the expression scalar\n\ninline bool is_rconst(const MITEM& i) { return is_rconst(i.ItemPtr()); }\n\nbool is_pi(const MItem* pi); // is pi the constant pi?\ninline bool is_pi(const MITEM& i) { return is_pi(i.ItemPtr()); }\n\ninline bool is_zero(const MItem* pi) { if (isConst(pi)) return (mconst(pi)->value() == 0.0); else return false; }\n\ninline bool is_scalar(const MITEM& m) { return ::is_scalar(m.ItemPtr()); }\n\n//-----------------------------------------------------------------------------\n// Algebraic operators for MITEM's\nMITEM operator - (const MITEM& l);\nMITEM operator + (const MITEM& l, const MITEM& r);\nMITEM operator + (const MITEM& l, double r);\nMITEM operator + (double l, const MITEM& r);\nMITEM operator - (const MITEM& l, const MITEM& r);\nMITEM operator - (double l, const MITEM& r);\nMITEM operator - (const MITEM& l, double r);\nMITEM operator * (const MITEM& l, const MITEM& r);\nMITEM operator * (double l, const MITEM& r);\nMITEM operator / (const MITEM& l, const MITEM& r);\nMITEM operator / (double l, const MITEM& r);\nMITEM operator / (const MITEM& l, double r);\nMITEM operator ^ (const MITEM& l, const MITEM& r);\nMITEM operator ^ (const MITEM& l, double r);\n\n//-----------------------------------------------------------------------------\n// Fractions\nMITEM Fraction(double n, double d);\ninline MITEM Fraction(FRACTION f) { return Fraction(f.n, f.d); }\n\n//-----------------------------------------------------------------------------\n// Symbolic math functions\nMITEM Abs(const MITEM& l);\nMITEM Sgn(const MITEM& l);\nMITEM Sin(const MITEM& l);\nMITEM Cos(const MITEM& l);\nMITEM Tan(const MITEM& l);\nMITEM Cot(const MITEM& l);\nMITEM Sec(const MITEM& l);\nMITEM Csc(const MITEM& l);\nMITEM Atan(const MITEM& l);\nMITEM Cosh(const MITEM& l);\nMITEM Sinh(const MITEM& l);\nMITEM Exp(const MITEM& l);\nMITEM Log(const MITEM& l);\nMITEM Log10(const MITEM& l);\nMITEM Float(const MITEM& l);\nMITEM Sqrt(const MITEM& l);\nMITEM Erf(const MITEM& l);\nMITEM Erfc(const MITEM& l);\n\n// Chebyshev polynomials\nMITEM Tn(int n, MITEM& r);\nMITEM Tn(const MITEM& l, const MITEM& r);\n\n// Bessel functions (1st kind)\n#ifdef WIN32\nMITEM J0(const MITEM& l);\nMITEM J1(const MITEM& l);\nMITEM Jn(int n, const MITEM& r);\nMITEM Jn(const MITEM& l, const MITEM& r);\n#endif \n\n// Bessel functions (2e kind)\n#ifdef WIN32\nMITEM Y0(const MITEM& l);\nMITEM Y1(const MITEM& l);\nMITEM Yn(int n, const MITEM& r);\nMITEM Yn(const MITEM& l, const MITEM& r);\n#endif\n\n// Miscellaneous functions\nMITEM Fac(const MITEM& l);\nMITEM Binomial(const MITEM& l, const MITEM& r);\nMITEM Identity(const MITEM& n);\n\n//-----------------------------------------------------------------------------\n// check equality of two items\ninline bool operator == (const MITEM& l, const std::string& r) { if (is_var(l)) { const std::string& s = mvar(l)->Name(); return (s.compare(r) == 0); } else return false; }\ninline bool operator == (const MITEM& l, const MVariable& x) { if (is_var(l)) { const std::string& s = mvar(l)->Name(); return (s.compare(x.Name()) == 0); } else return false; }\ninline bool operator == (const MITEM& l, double r) { if (isConst(l)) return (mnumber(l)->value() == r); else return false; }\ninline bool operator != (const MITEM& l, double r) { return !(l==r); }\ninline bool is_dependent(const MITEM& l, const MVariable& x) { return is_dependent(l.ItemPtr(), x); }\ninline bool is_dependent(const MITEM& l, const MITEM& x) { return is_dependent(l.ItemPtr(), x.ItemPtr()); }\n\nbool is_equal(const MItem* pl, const MItem* pr);\ninline bool is_equal(const MItem* pl, const MVariable& x) { if (is_var(pl)) { const std::string& s = mvar(pl)->Name(); return (s.compare(x.Name()) == 0); } else return false; }\ninline bool operator == (const MITEM& l, const MITEM& r) { return is_equal(l.ItemPtr(), r.ItemPtr()); }\ninline bool operator != (const MITEM& l, const MITEM& r) { return (is_equal(l.ItemPtr(), r.ItemPtr()) == false); }\n\n//-----------------------------------------------------------------------------\n// functions for calculating and comparing heuristics\nint op_count(MItem* pi);\ninline int op_count(MITEM& i) { return op_count(i.ItemPtr()); }\n\n//-----------------------------------------------------------------------------\nbool is_format(MItem* pe, const char* sz);\ninline bool is_format(MITEM& e, const char* sz) { return is_format(e.ItemPtr(), sz); }\n"
  },
  {
    "path": "FECore/MMath.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"MItem.h\"\n#include \"fecore_api.h\"\n\n// Take the derivative with respect of x\nMITEM FECORE_API MDerive(const MITEM& e, const MVariable& x);\n\n// Take the nth-derivative with respect of x\nMITEM FECORE_API MDerive(const MITEM& e, const MVariable& x, int n);\n\n// Take the partial derivative with respect to x, y, z, ...\nMITEM FECORE_API MDerive(const MITEM& e, const MSequence& x);\n\n// Replace an expression with an expression\nMITEM FECORE_API MReplace(const MITEM& e, const MVariable& x, const MITEM& s);\n\n// Replace an expression with an expression (assuming x is an equality)\nMITEM FECORE_API MReplace(const MITEM& e, const MITEM& x);\n\n// Replace an expression with an expression\nMITEM FECORE_API MReplace(const MITEM& e, const MITEM& x, const MITEM& s);\n\n// replace multiple expressions with other expressions\nMITEM FECORE_API MReplace(const MITEM& e, const MSequence& x, const MSequence& s);\n\n// Calculate the taylor expansion of an expression\nMITEM FECORE_API MTaylor(const MITEM& e, const MVariable& x, double z, int n);\n\n// Calculate the indefinite integral (without constant)\nMITEM FECORE_API MIntegral(const MITEM& e, const MVariable& x);\n\n// Calculate the definite integral\nMITEM FECORE_API MIntegral(const MITEM& e, const MVariable& x, const MITEM& a, const MITEM& b);\n\n// Expand an expression\nMITEM FECORE_API MExpand(const MITEM& e);\n\n// Expand an expression but don't expand the second argument\nMITEM FECORE_API MExpand(const MITEM& e, const MITEM& s);\n\n// solve an expression\nMITEM FECORE_API MSolve(const MITEM& e, const MITEM& v);\n\n// collect terms\nMITEM FECORE_API MCollect(const MITEM& e, const MITEM& x);\n\n// simplify an expression\nMITEM FECORE_API MSimplify(const MITEM& e);\n"
  },
  {
    "path": "FECore/MMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMatrix.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nMMatrix::MMatrix() : MItem(MMATRIX)\n{\n\tm_nrow = 0; m_ncol = 0;\n}\n\n//-----------------------------------------------------------------------------\nMMatrix::~MMatrix()\n{\n\tfor (int i=0;i<m_nrow; ++i)\n\t\tfor (int j=0; j<m_ncol; ++j) delete m_d[i][j];\n\tm_d.clear();\n\tm_nrow = 0;\n\tm_ncol = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid MMatrix::Create(int nrow, int ncol)\n{\n\tm_d.clear();\n\tm_d.assign(nrow, vector<MItem*>(ncol));\n\tm_nrow = nrow;\n\tm_ncol = ncol;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MMatrix::copy() const\n{\n\tMMatrix* pm = new MMatrix();\n\tMMatrix& m = *pm;\n\tconst MMatrix& a = (*this);\n\tm.Create(m_nrow, m_ncol);\n\tfor (int i=0; i<m_nrow; ++i)\n\t\tfor (int j=0; j<m_ncol; ++j) m[i][j] = a[i][j]->copy();\n\treturn pm;\n}\n\n//-----------------------------------------------------------------------------\nMMatrix* operator + (const MMatrix& A, const MMatrix& B)\n{\n\tif ((A.rows() != B.rows()) || (A.columns() != B.columns())) throw InvalidOperation();\n\tMMatrix& C = *(new MMatrix());\n\tint N = A.rows();\n\tint M = A.columns();\n\tC.Create(A.rows(), A.columns());\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tfor (int j=0; j<M; ++j)\n\t\t{\n\t\t\tMITEM aij = MITEM(A[i][j]->copy());\n\t\t\tMITEM bij = MITEM(B[i][j]->copy());\n\t\t\tC[i][j] = (aij + bij).copy();\n\t\t}\n\t}\n\treturn &C;\n}\n\n//-----------------------------------------------------------------------------\nMMatrix* operator - (const MMatrix& A, const MMatrix& B)\n{\n\tif ((A.rows() != B.rows()) || (A.columns() != B.columns())) throw InvalidOperation();\n\tMMatrix& C = *(new MMatrix());\n\tint N = A.rows();\n\tint M = A.columns();\n\tC.Create(A.rows(), A.columns());\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tfor (int j=0; j<M; ++j)\n\t\t{\n\t\t\tMITEM aij = MITEM(A[i][j]->copy());\n\t\t\tMITEM bij = MITEM(B[i][j]->copy());\n\t\t\tC[i][j] = (aij - bij).copy();\n\t\t}\n\t}\n\treturn &C;\n}\n\n//-----------------------------------------------------------------------------\nMMatrix* operator * (const MMatrix& A, const MMatrix& B)\n{\n\tint RA = A.rows();\n\tint CA = A.columns();\n\tif (B.rows() != CA) throw InvalidOperation();\n\tint CB = B.columns();\n\tMMatrix& C = *(new MMatrix());\n\tC.Create(RA, CB);\n\tfor (int i=0; i<RA; ++i)\n\t{\n\t\tfor (int j=0; j<CB; ++j)\n\t\t{\n\t\t\tMITEM ai0 = A[i][0]->copy();\n\t\t\tMITEM b0j = B[0][j]->copy();\n\t\t\tMITEM cij = ai0*b0j;\n\t\t\tfor (int k=1; k<CA; ++k)\n\t\t\t{\n\t\t\t\tMITEM aik = A[i][k]->copy();\n\t\t\t\tMITEM bkj = B[k][j]->copy();\n\t\t\t\tcij = cij + aik*bkj;\n\t\t\t}\n\t\t\tC[i][j] = cij.copy();\n\t\t}\n\t}\n\treturn (&C);\n}\n\n//-----------------------------------------------------------------------------\nMMatrix* operator * (const MMatrix& A, const MITEM& n)\n{\n\tif (n == 1.0) return static_cast<MMatrix*>(A.copy());\n\tMMatrix& C = *(new MMatrix());\n\tint N = A.rows();\n\tint M = A.columns();\n\tC.Create(A.rows(), A.columns());\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tfor (int j=0; j<M; ++j)\n\t\t{\n\t\t\tMITEM aij(A[i][j]->copy());\n\t\t\tMITEM f(n.copy());\n\t\t\tC[i][j] = (f*aij).copy();\n\t\t}\n\t}\n\treturn &C;\n}\n\n//-----------------------------------------------------------------------------\nMMatrix* operator / (const MMatrix& A, const MITEM& n)\n{\n\tif (n == 1.0) return static_cast<MMatrix*>(A.copy());\n\tMMatrix& C = *(new MMatrix());\n\tint N = A.rows();\n\tint M = A.columns();\n\tC.Create(A.rows(), A.columns());\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tfor (int j=0; j<M; ++j)\n\t\t{\n\t\t\tMITEM aij(A[i][j]->copy());\n\t\t\tMITEM f(n.copy());\n\t\t\tC[i][j] = (aij/f).copy();\n\t\t}\n\t}\n\treturn &C;\n}\n\n\n//-----------------------------------------------------------------------------\nMItem* matrix_transpose(const MMatrix& A)\n{\n\tMMatrix& B = *(new MMatrix());\n\tB.Create(A.columns(), A.rows());\n\tfor (int i=0; i<B.rows(); ++i)\n\t\tfor (int j=0; j<B.columns(); ++j)\n\t\t{\n\t\t\tB[i][j] = A[j][i]->copy();\n\t\t}\n\treturn &B;\n}\n\n//-----------------------------------------------------------------------------\nMItem* matrix_trace(const MMatrix& A)\n{\n\tif (A.rows() != A.columns()) throw InvalidOperation();\n\tMITEM t = A[0][0]->copy();\n\tfor (int i=1; i<A.rows(); ++i)\n\t{\n\t\tMITEM aii = A[i][i]->copy();\n\t\tt = t + aii;\n\t}\n\treturn t.copy();\n}\n\n//-----------------------------------------------------------------------------\nMItem* matrix_determinant(const MMatrix& A)\n{\n\t// make sure the matrix is square\n\tif (A.rows() != A.columns()) throw InvalidOperation();\n\n\tif (A.rows() == 1) return A(0,0)->copy();\n\tif (A.rows() == 2)\n\t{\n\t\tMITEM D = MITEM(A(0,0))*MITEM(A(1,1)) - MITEM(A(0,1))*MITEM(A(1,0));\n\t\treturn D.copy();\n\t}\n\tif (A.rows() == 3)\n\t{\n\t\tMITEM D(0.0);\n\t\tD = D + MITEM(A(0,0))*MITEM(A(1,1))*MITEM(A(2,2));\n\t\tD = D + MITEM(A(0,1))*MITEM(A(1,2))*MITEM(A(2,0));\n\t\tD = D + MITEM(A(0,2))*MITEM(A(1,0))*MITEM(A(2,1));\n\t\tD = D - MITEM(A(0,2))*MITEM(A(1,1))*MITEM(A(2,0));\n\t\tD = D - MITEM(A(0,1))*MITEM(A(1,0))*MITEM(A(2,2));\n\t\tD = D - MITEM(A(0,0))*MITEM(A(1,2))*MITEM(A(2,1));\n\t\treturn D.copy();\n\t}\n\tthrow InvalidOperation();\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nMItem* matrix_contract(const MMatrix& A, const MMatrix& B)\n{\n\tif ((A.rows() != B.rows())||(A.columns() != B.columns())) throw InvalidOperation();\n\tMITEM s(0.0);\n\tfor (int i=0; i<A.rows(); ++i)\n\t\tfor (int j=0; j<A.columns(); ++j)\n\t\t{\n\t\t\tMITEM aij = A[i][j]->copy();\n\t\t\tMITEM bij = B[i][j]->copy();\n\t\t\ts = s + aij*bij;\n\t\t}\n\treturn s.copy();\n}\n\n//-----------------------------------------------------------------------------\nMMatrix* matrix_identity(int n)\n{\n\tMMatrix& m = *(new MMatrix());\n\tm.Create(n, n);\n\tfor (int i=0; i<n; ++i)\n\t\tfor (int j=0; j<n; ++j)\n\t\t{\n\t\t\tm[i][j] = new MConstant((i==j?1.0:0.0));\n\t\t}\n\treturn &m;\n}\n\n//-----------------------------------------------------------------------------\n//! Calculate the inverse of a matrix\nMItem* matrix_inverse(const MMatrix& m)\n{\n\t// make sure it is a square matrix\n\tint nrows = m.rows();\n\tint ncols = m.columns();\n\tif (nrows != ncols) throw InvalidOperation();\n\n\t// calculate the inverse, but we can only handle matrix up to order 3 for now\n\tif (nrows == 1)\n\t{\n\t\tMMatrix* pmi = new MMatrix;\n\t\tMMatrix& mi = *pmi;\n\t\tmi.Create(1,1);\n\t\tMITEM mij(m[0][0]->copy());\n\t\tmi[0][0] = (MITEM(1.0) / mij).copy();\n\t\treturn pmi;\n\t}\n\telse if (nrows == 2)\n\t{\n\t\tMITEM m00(m[0][0]->copy());\n\t\tMITEM m01(m[0][1]->copy());\n\t\tMITEM m10(m[1][0]->copy());\n\t\tMITEM m11(m[1][1]->copy());\n\t\tMITEM D(matrix_determinant(m));\n\t\tif (D == 0.0) throw DivisionByZero();\n\n\t\tMMatrix mi;\n\t\tmi.Create(2,2);\n\t\tmi[0][0] = m11.copy();\n\t\tmi[1][1] = m00.copy();\n\t\tmi[0][1] = (-m01).copy();\n\t\tmi[1][0] = (-m10).copy();\n\n\t\tMMatrix& Mi = *(mi/D);\n\t\treturn &Mi;\n\t}\n\telse if (nrows == 3)\n\t{\n\t\tMITEM m00(m[0][0]->copy());\n\t\tMITEM m01(m[0][1]->copy());\n\t\tMITEM m02(m[0][2]->copy());\n\t\tMITEM m10(m[1][0]->copy());\n\t\tMITEM m11(m[1][1]->copy());\n\t\tMITEM m12(m[1][2]->copy());\n\t\tMITEM m20(m[2][0]->copy());\n\t\tMITEM m21(m[2][1]->copy());\n\t\tMITEM m22(m[2][2]->copy());\n\t\tMITEM D(matrix_determinant(m));\n\t\tif (D == 0.0) throw DivisionByZero();\n\n\t\tMMatrix mi;\n\t\tmi.Create(3,3);\n\t\tmi[0][0] = (m11*m22 - m21*m12).copy();\n\t\tmi[1][0] = (m12*m20 - m10*m22).copy();\n\t\tmi[2][0] = (m10*m21 - m20*m11).copy();\n\t\tmi[0][1] = (m21*m02 - m01*m22).copy();\n\t\tmi[1][1] = (m00*m22 - m20*m02).copy();\n\t\tmi[2][1] = (m20*m01 - m00*m21).copy();\n\t\tmi[0][2] = (m01*m12 - m11*m02).copy();\n\t\tmi[1][2] = (m10*m02 - m00*m12).copy();\n\t\tmi[2][2] = (m00*m11 - m10*m01).copy();\n\n\t\tMMatrix& Mi  = *(mi/D);\n\t\treturn &Mi;\n\t}\n\telse throw InvalidOperation();\n\n\t// we should not get here\n\tassert(false);\n\treturn 0;\n}\n"
  },
  {
    "path": "FECore/MMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"MItem.h\"\n\n//-----------------------------------------------------------------------------\nclass MMatrix;\n\ntypedef MItem* (*FUNCMATPTR )(const MMatrix& A);\ntypedef MItem* (*FUNCMAT2PTR)(const MMatrix& A, const MMatrix& B);\n\n//-----------------------------------------------------------------------------\n// Matrix class\nclass MMatrix : public MItem\n{\npublic:\n\tMMatrix();\n\t~MMatrix();\n\tvoid Create(int nrow, int ncol);\n\npublic:\n\tMItem* copy() const override;\n\n\tMItem* Item(int i, int j) { return m_d[i][j]; }\n\tconst MItem* Item(int i, int j) const { return m_d[i][j]; }\n\n\tstd::vector<MItem*>& operator [] (int n) { return m_d[n]; }\n\tconst std::vector<MItem*>& operator [] (int n) const { return m_d[n]; }\n\n\tMItem* operator () (int i, int j) { return m_d[i][j]->copy(); }\n\tconst MItem* operator () (int i, int j) const { return m_d[i][j]->copy(); }\n\n\tint rows   () const { return m_nrow; }\n\tint columns() const { return m_ncol; }\n\nprotected:\n\tstd::vector< std::vector<MItem*>\t>\tm_d;\n\tint\tm_nrow;\n\tint\tm_ncol;\n};\n\n//-----------------------------------------------------------------------------\n// function of matrices\nclass MFuncMat : public MUnary\n{\npublic: \n\tMFuncMat(FUNCMATPTR pf, const std::string& s, MItem* pi) : MUnary(pi, MFMAT), m_name(s), m_pf(pf) {}\n\tMItem* copy() const override { return new MFuncMat(m_pf, m_name, m_pi->copy()); }\n\tconst std::string& Name() { return m_name; }\n\tFUNCMATPTR\tfuncptr() const { return m_pf; }\n\nprotected:\n\tstd::string\tm_name;\n\tFUNCMATPTR\tm_pf;\n};\n\n//-----------------------------------------------------------------------------\n// function of 2 matrices\nclass MFuncMat2 : public MBinary\n{\npublic: \n\tMFuncMat2(FUNCMAT2PTR pf, const std::string& s, MItem* pl, MItem* pr) : MBinary(pl, pr, MFMAT2), m_name(s), m_pf(pf) {}\n\tMItem* copy() const override { return new MFuncMat2(m_pf, m_name, m_pleft->copy(), m_pright->copy()); }\n\tconst std::string& Name() { return m_name; }\n\tFUNCMAT2PTR\tfuncptr() const { return m_pf; }\n\nprotected:\n\tstd::string\tm_name;\n\tFUNCMAT2PTR\tm_pf;\n};\n\ninline MMatrix*\tmmatrix (MItem* pi) { return static_cast<MMatrix  *>(pi); }\ninline MFuncMat*  mfncmat (MItem* pi) { return static_cast<MFuncMat *>(pi); }\ninline MFuncMat2* mfncmat2(MItem* pi) { return static_cast<MFuncMat2*>(pi); }\n\ninline const MMatrix*\tmmatrix (const MItem* pi) { return static_cast<const MMatrix  *>(pi); }\ninline const MFuncMat*  mfncmat (const MItem* pi) { return static_cast<const MFuncMat *>(pi); }\ninline const MFuncMat2* mfncmat2(const MItem* pi) { return static_cast<const MFuncMat2*>(pi); }\n\ninline const MMatrix*   mmatrix (const MITEM& i) { return static_cast<const MMatrix  *>(i.ItemPtr()); }\ninline const MFuncMat*  mfncmat (const MITEM& i) { return static_cast<const MFuncMat *>(i.ItemPtr()); }\ninline const MFuncMat2* mfncmat2(const MITEM& i) { return static_cast<const MFuncMat2*>(i.ItemPtr()); }\n\n//-----------------------------------------------------------------------------\n// Matrix operations\nMMatrix* operator + (const MMatrix& A, const MMatrix& B);\nMMatrix* operator - (const MMatrix& A, const MMatrix& B);\nMMatrix* operator * (const MMatrix& A, const MITEM& n);\nMMatrix* operator / (const MMatrix& A, const MITEM& n);\nMMatrix* operator * (const MMatrix& A, const MMatrix& B);\n\nMItem* matrix_transpose  (const MMatrix& A);\nMItem* matrix_trace      (const MMatrix& A);\nMItem* matrix_determinant(const MMatrix& A);\nMItem* matrix_inverse    (const MMatrix& A);\n\nMItem* matrix_contract(const MMatrix& A, const MMatrix& B);\n\nMMatrix* matrix_identity(int n);\n"
  },
  {
    "path": "FECore/MObj2String.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MObj2String.h\"\n#include <sstream>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n// Convert a math object to a string\nstring MObj2String::Convert(const MathObject& o)\n{\n\tconst MathObject* po = &o;\n\tif (dynamic_cast<const MSimpleExpression*>(po))\n\t{\n\t\tconst MSimpleExpression* pe = dynamic_cast<const MSimpleExpression*>(po);\n\t\tconst MITEM& i = pe->GetExpression();\n\t\treturn Convert(i.ItemPtr());\n\t}\n\tassert(false);\n\treturn \"\";\n}\n\n//-----------------------------------------------------------------------------\n// Convert a math item to string\nstring MObj2String::Convert(const MItem* pi)\n{\n\tswitch(pi->Type())\n\t{\n\tcase MCONST   : return Constant (dynamic_cast<const MConstant*> (pi));\n\tcase MFRAC    : return Fraction (dynamic_cast<const MFraction*> (pi));\n\tcase MNAMED   : return NamedCt  (dynamic_cast<const MNamedCt* > (pi));\n\tcase MVAR     : return Variable (dynamic_cast<const MVarRef*  > (pi));\n\tcase MNEG     : return OpNeg    (dynamic_cast<const MNeg*     > (pi));\n\tcase MADD     : return OpAdd    (dynamic_cast<const MAdd*     > (pi));\n\tcase MSUB     : return OpSub    (dynamic_cast<const MSub*     > (pi));\n\tcase MMUL     : return OpMul    (dynamic_cast<const MMul*     > (pi));\n\tcase MDIV     : return OpDiv    (dynamic_cast<const MDiv*     > (pi));\n\tcase MPOW     : return OpPow    (dynamic_cast<const MPow*     > (pi));\n\tcase MEQUATION: return OpEqual  (dynamic_cast<const MEquation*> (pi));\n\tcase MF1D     : return OpFnc1D  (dynamic_cast<const MFunc1D*  > (pi));\n\tcase MF2D     : return OpFnc2D  (dynamic_cast<const MFunc2D*  > (pi));\n\tcase MFND     : return OpFncND  (dynamic_cast<const MFuncND*  > (pi));\n\tcase MSFNC    : return OpSFnc   (dynamic_cast<const MSFuncND* > (pi));\n\t}\n\tassert(false);\n\treturn \"\";\n}\n\n//-----------------------------------------------------------------------------\n// convert a constant to a string\nstring MObj2String::Constant(const MConstant *pc)\n{\n\tstringstream ss;\n\tss << pc->value();\n\treturn ss.str();\n}\n\n//-----------------------------------------------------------------------------\n// convert a fraction to a string\nstring MObj2String::Fraction(const MFraction *pc)\n{\n\tFRACTION a = pc->fraction();\n\tstringstream ss;\n\tss << a.n << \"/\" << a.d;\n\treturn ss.str();\n}\n\n//-----------------------------------------------------------------------------\n// convert a constant to a string\nstring MObj2String::NamedCt(const MNamedCt *pc)\n{\n\treturn pc->Name();\n}\n\n//-----------------------------------------------------------------------------\n// convert a variable to a string\nstring MObj2String::Variable(const MVarRef* pv)\n{\n\tconst MVariable* pvar = pv->GetVariable();\n\treturn string(pvar->Name());\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpNeg(const MNeg* po)\n{\n\tconst MItem* pi = po->Item();\n\tstring s = Convert(pi);\n#ifndef NDEBUG\n\ts = \"-[\" + s + \"]\";\n#else\n\tif (is_add(pi) || is_sub(pi) || is_neg(pi)) s = \"-(\" + s + \")\"; else s = \"-\" + s;\n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpAdd(const MAdd* po)\n{\n\tstring s;\n\tconst MItem* pl = po->LeftItem ();\n\tconst MItem* pr = po->RightItem();\n\tstring sl = Convert(pl);\n\tstring sr = Convert(pr);\n#ifndef NDEBUG\n\tif (is_binary(pl)) s = \"(\" + sl + \")\"; else s = sl;\n\ts += '+';\n\tif (is_binary(pr)) s += \"(\" + sr + \")\"; else s += sr;\n#else\n\tif (is_neg(pl)) s = '(' + sl + ')'; else s = sl; \n\ts += '+';\n\tif (is_neg(pr)) s += '(' + sr + ')'; else s += sr; \n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpSub(const MSub* po)\n{\n\tstring s;\n\tconst MItem* pl = po->LeftItem ();\n\tconst MItem* pr = po->RightItem();\n\tstring sl = Convert(pl);\n\tstring sr = Convert(pr);\n#ifndef NDEBUG\n\tif (is_binary(pl)) s = \"(\" + sl + \")\"; else s = sl;\n\ts += '-';\n\tif (is_binary(pr)) s += \"(\" + sr + \")\"; else s += sr;\n#else\n\tif (is_neg(pl)) s = '(' + sl + ')'; else s = sl; \n\ts += '-';\n\tif (is_add(pr) || is_sub(pr) || is_neg(pr)) s += '(' + sr + ')'; else s += sr; \n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpMul(const MMul* po)\n{\n\tstring s;\n\tconst MItem* pl = po->LeftItem ();\n\tconst MItem* pr = po->RightItem();\n\tstring sl = Convert(pl);\n\tstring sr = Convert(pr);\n#ifndef NDEBUG\n\tif (is_binary(pl)) s = \"(\" + sl + \")\"; else s = sl;\n\ts += '*';\n\tif (is_binary(pr)) s += \"(\" + sr + \")\"; else s += sr;\n#else\n\tif (is_add(pl) || is_sub(pl) || is_neg(pl) || is_div(pl)) s = '(' + sl + ')'; else s = sl;\n\ts += '*';\n\tif (is_add(pr) || is_sub(pr) || is_neg(pr) || is_div(pr)) s += '(' + sr + ')'; else s += sr;\n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpDiv(const MDiv* po)\n{\n\tstring s;\n\tconst MItem* pl = po->LeftItem ();\n\tconst MItem* pr = po->RightItem();\n\tstring sl = Convert(pl);\n\tstring sr = Convert(pr);\n#ifndef NDEBUG\n\tif (is_binary(pl)) s = \"(\" + sl + \")\"; else s = sl;\n\ts += '/';\n\tif (is_binary(pr)) s += \"(\" + sr + \")\"; else s += sr;\n#else\n\tif (is_add(pl) || is_sub(pl) || is_neg(pl) || is_mul(pl) || is_div(pl) || is_pow(pr)) s = '(' + sl + ')'; else s = sl;\n\ts += '/';\n\tif (is_add(pr) || is_sub(pr) || is_neg(pr) || is_mul(pr) || is_div(pr)) s += '(' + sr + ')'; else s += sr;\n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpPow(const MPow* po)\n{\n\tstring s;\n\tconst MItem* pl = po->LeftItem ();\n\tconst MItem* pr = po->RightItem();\n\tstring sl = Convert(pl);\n\tstring sr = Convert(pr);\n#ifndef NDEBUG\n\tif (is_binary(pl)) s = \"(\" + sl + \")\"; else s = sl;\n\ts += '^';\n\tif (is_binary(pr)) s += \"(\" + sr + \")\"; else s += sr;\n#else\n\tif (is_add(pl) || is_sub(pl) || is_neg(pl) || is_pow(pl) || is_mul(pl) || is_div(pl)) s = '(' + sl + ')'; else s = sl; \n\ts += '^';\n\tif (is_add(pr) || is_sub(pr) || is_neg(pr) || is_pow(pr) || is_mul(pr) || is_div(pr)) s += '(' + sr + ')'; else s += sr;\n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpEqual(const MEquation* po)\n{\n\tstring s;\n\tconst MItem* pl = po->LeftItem ();\n\tconst MItem* pr = po->RightItem();\n\tstring sl = Convert(pl);\n\tstring sr = Convert(pr);\n#ifndef NDEBUG\n\tif (is_binary(pl)) s = \"(\" + sl + \")\"; else s = sl;\n\ts += '=';\n\tif (is_binary(pr)) s += \"(\" + sr + \")\"; else s += sr;\n#else\n\ts += '=';\n#endif\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpFnc1D(const MFunc1D *po)\n{\n\treturn string(po->Name() + \"(\" + Convert(po->Item()) + \")\");\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpFnc2D(const MFunc2D *po)\n{\n\treturn string(po->Name() + \"(\" + Convert(po->LeftItem()) + \",\" + Convert(po->RightItem()) + \")\");\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpFncND(const MFuncND *po)\n{\n\tint N = po->Params();\n\tstring s = po->Name() + \"(\";\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tconst MItem* pi = po->Param(i);\n\t\ts += Convert(pi);\n\t\tif (i != N-1) s += \",\";\n\t}\n\treturn s + \")\";\n}\n\n//-----------------------------------------------------------------------------\nstring MObj2String::OpSFnc(const MSFuncND *po)\n{\n\tint N = po->Params();\n\tstring s = po->Name() + \"(\";\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tconst MItem* pi = po->Param(i);\n\t\ts += Convert(pi);\n\t\tif (i != N-1) s += \",\";\n\t}\n\treturn s + \")\";\n}\n"
  },
  {
    "path": "FECore/MObj2String.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"MathObject.h\"\n#include <string>\n\n// this class converts a MathObject to a string\nclass FECORE_API MObj2String\n{\npublic:\n\tstd::string Convert(const MathObject& o);\n\nprotected:\n\tstd::string Convert(const MItem* pi);\n\n\tstd::string Constant (const MConstant* pc);\n\tstd::string Fraction (const MFraction* pc);\n\tstd::string NamedCt  (const MNamedCt*  pc);\n\tstd::string Variable (const MVarRef*   pv);\n\tstd::string OpNeg    (const MNeg*      po);\n\tstd::string OpAdd    (const MAdd*      po);\n\tstd::string OpSub    (const MSub*      po);\n\tstd::string OpMul    (const MMul*      po);\n\tstd::string OpDiv    (const MDiv*      po);\n\tstd::string OpPow    (const MPow*      po);\n\tstd::string OpEqual  (const MEquation* po);\n\tstd::string OpFnc1D  (const MFunc1D*   po);\n\tstd::string OpFnc2D  (const MFunc2D*   po);\n\tstd::string OpFncND  (const MFuncND*   po);\n\tstd::string OpSFnc   (const MSFuncND*  po);\n};\n"
  },
  {
    "path": "FECore/MObjBuilder.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MObjBuilder.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\n#include \"MFunctions.h\"\n#include \"assert.h\"\n#include <map>\n#include <string>\n#include <math.h>\nusing namespace std;\n\nmap<string, FUNCPTR>\t\tFNC;\t// 1-D functions\nmap<string, FUNC2PTR>\t\tFNC2;\t// 2-D functions\nmap<string, FUNCNPTR>\t\tFNCN;\t// N-D functions\nmap<string, FUNCMATPTR>\t\tFMAT;\t// Matrix functions\nmap<string, double>\t\t\tCNT;\t// predefined constants\n\nvoid init_function_lists()\n{\n\t// 1-parameter functions\n\tFNC[\"cos\"  ] = cos;\n\tFNC[\"sin\"  ] = sin;\n\tFNC[\"tan\"  ] = tan;\n\tFNC[\"csc\"  ] = csc;\n\tFNC[\"sec\"  ] = sec;\n\tFNC[\"cot\"  ] = cot;\n\tFNC[\"acos\" ] = acos;\n\tFNC[\"asin\" ] = asin;\n\tFNC[\"atan\" ] = atan;\n\tFNC[\"cosh\" ] = cosh;\n\tFNC[\"sinh\" ] = sinh;\n\tFNC[\"tanh\" ] = tanh;\n\tFNC[\"acosh\"] = acosh;\n\tFNC[\"ln\"   ] = log;\n\tFNC[\"log\"  ] = log10;\n\tFNC[\"exp\"  ] = exp;\n\tFNC[\"sqrt\" ] = sqrt;\n\tFNC[\"abs\"  ] = fabs;\n\tFNC[\"sgn\"  ] = sgn;\n\tFNC[\"H\"    ] = heaviside;\n\tFNC[\"step\" ] = unit_step;\n\n#ifdef WIN32\n\tFNC[\"J0\"   ] = _j0;\n\tFNC[\"J1\"   ] = _j1;\n\tFNC[\"Y0\"   ] = _y0;\n\tFNC[\"Y1\"   ] = _y1;\n#endif\n\tFNC[\"sinc\" ] = sinc;\n\tFNC[\"fl\"   ] = fl;\n\tFNC[\"fac\"  ] = fac;\n\tFNC[\"erf\"  ] = erf;\n\tFNC[\"erfc\" ] = erfc;\n\tFNC[\"tgamma\"] = tgamma;\n\n\t// 2-parameter functions\n#ifdef WIN32\n\tFNC2[\"Jn\" ]   = jn;\n\tFNC2[\"Yn\" ]   = yn;\n#endif\n\tFNC2[\"atan2\"] = atan2;\n\tFNC2[\"pow\"  ] = pow;\n\tFNC2[\"mod\"  ] = fmod;\n\tFNC2[\"Tn\"]    = chebyshev;\n\tFNC2[\"binomial\"] = binomial;\n\n\t// n-parameter functions\n\tFNCN[\"max\"] = fmax;\n\tFNCN[\"min\"] = fmin;\n\tFNCN[\"avg\"] = avg;\n\n\t// Matrix functions\n\tFMAT[\"transpose\"] = matrix_transpose;\n\tFMAT[\"trace\"    ] = matrix_trace;\n\tFMAT[\"det\"      ] = matrix_determinant;\n\tFMAT[\"inverse\"  ] = matrix_inverse;\n\n\t// predefined constants\n\tCNT[\"pi\"   ] = 3.1415926535897932385;\t// \"pi\"\n\tCNT[\"e\"    ] = 2.7182818284590452354;\t// exp(1)\n\tCNT[\"gamma\"] = 0.57721566490153286061;\t// Euler-Mascheroni constant\n\tCNT[\"_inf_\"] = 1e308;\t// \"infinity\"\n}\n\n//-----------------------------------------------------------------------------\nbool MObjBuilder::Add1DFunction(const std::string& name, double (*f)(double))\n{\n\tif (FNC.find(name) != FNC.end()) return false;\n\tFNC[name] = f;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nMObjBuilder::MObjBuilder()\n{\n\tstatic bool bfirst = true;\n\tif (bfirst) init_function_lists();\n\tbfirst = false;\n\n\tm_autoVars = true;\n}\n\n//-----------------------------------------------------------------------------\nchar* find_next(char* sz)\n{\n\tchar* ch = sz;\n\tint l = 0;\n\twhile (*ch)\n\t{\n\t\tif      (*ch == '(') l++;\n\t\telse if (*ch == ')') l--;\n\t\telse if ((*ch == ',') && (l==0)) return ch;\n\t\t++ch;\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nbool string_replace(string& s, const std::string& in, const std::string& out)\n{\n\tsize_t n = s.find(in);\n\tif (n != string::npos)\n\t{\n\t\ts.replace(n, in.size(), out);\n\t\treturn true;\n\t}\n\telse return false;\n}\n\n//-----------------------------------------------------------------------------\n// process string substitutions\n// Math expressions can be defined with string literals, which will be substituted\n// in the last statement.\n// e.g.\n// s = a + b; 2*s\n// will be replaced by: s*(a+b)\n\nstd::string MObjBuilder::processStrings(const std::string& ex)\n{\n\t// remove white space\n\tstring tmp;\n\tfor (size_t i = 0; i < ex.size(); ++i)\n\t{\n\t\tchar c = ex[i];\n\t\tif (isspace(c) == 0) tmp.push_back(c);\n\t}\n\n\t// keep track of string subs\n\tstd::map<string, string> subs;\n\n\t// build string sub list\n\tsize_t lp = 0;\n\tsize_t l1 = tmp.find(';', lp);\n\twhile (l1 != std::string::npos)\n\t{\n\t\tsize_t equalSign = tmp.find('=', lp);\n\t\tstring leftVal = tmp.substr(lp, equalSign - lp);\n\t\tstring rightVal = tmp.substr(equalSign + 1, l1 - equalSign - 1);\n\t\tsubs[leftVal] = rightVal;\n\n\t\tlp = l1 + 1;\n\t\tl1 = tmp.find(';', lp);\n\t}\n\n\tstring out = tmp.substr(lp);\n\n\tbool b = true;\n\twhile (b)\n\t{\n\t\tstd::map<string, string>::iterator it = subs.begin();\n\t\tb = false;\n\t\tfor (; it != subs.end(); ++it)\n\t\t{\n\t\t\tstring rep = \"(\" + it->second + \")\";\n\t\t\tbool ret = string_replace(out, it->first, rep);\n\t\t\tif (ret) b = true;\n\t\t}\n\t}\n\n\treturn out;\n}\n\n\n//-----------------------------------------------------------------------------\nbool MObjBuilder::Create(MSimpleExpression* mo, const std::string& expression, bool beval)\n{\n\t// process the strins for string substitutions\n\tstd::string ex = processStrings(expression);\n\n\t// make a copy of the original string\n\tchar szcopy[512] = { 0 };\n\tstrcpy(szcopy, ex.c_str());\n\tm_szexpr = m_szorg = szcopy;\n\n\t// keep pointer to object being constructed\n\tm_po = mo;\n\n\t// let's build a simple expression\n\ttry {\n\t\tMITEM i = create();\n\t\tif (beval) i = MEvaluate(i);\n\t\tmo->SetExpression(i);\n\t}\n\tcatch (MathError e)\n\t{\n\t\tfprintf(stderr, \"Error evaluating math expression: %s (position %d)\\n\", e.GetErrorStr(), e.GetPosition());\n\t\treturn false;\n\t}\n\tcatch (...)\n\t{\n\t\tfprintf(stderr, \"Error evaluating math expression: (unknown)\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Convert a string to a math object\nMathObject* MObjBuilder::Create(const std::string& ex, bool beval)\n{\n\t// make a copy of the original string\n\tchar szcopy[512] = {0};\n\tstrcpy(szcopy, ex.c_str());\n\tm_szexpr = m_szorg = szcopy;\n\n\tm_po = 0;\n\n\t// let's build a simple expression\n\tMSimpleExpression* pe = new MSimpleExpression;\n\tm_po = pe;\n\tMITEM i = create();\n\tif (beval) i = MEvaluate(i);\n\tpe->SetExpression(i);\n\n\treturn m_po;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::sequence()\n{\n\tMSequence* ps = new MSequence;\n\tdo\n\t{\n\t\tMItem* pi = create();\n\t\tps->add(pi);\n\t}\n\twhile (curr_tok == COMMA);\n\n\treturn ps;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::create()\n{\n\tMItem* pi = expr();\n\tfor (;;)\n\t{\n\t\tswitch(curr_tok)\n\t\t{\n\t\tcase EQUAL:\n\t\t\tpi = new MEquation(pi, expr());\n\t\t\tbreak;\n\t\tcase EQUALITY:\n\t\t\tpi = new MEquality(pi, expr());\n\t\t\tbreak;\n/*\t\tcase COMMA:\n\t\t\t{\n\t\t\t\tMSequence* pe = dynamic_cast<MSequence*>(pi);\n\t\t\t\tif (pe == 0)\n\t\t\t\t{\n\t\t\t\t\tpe = new MSequence;\n\t\t\t\t\tpe->add(pi);\n\t\t\t\t}\n\t\t\t\tpe->add(expr());\n\t\t\t\tpi = pe;\n\t\t\t}\n\t\t\tbreak;\n*/\t\tdefault:\n\t\t\treturn pi;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::create_sequence()\n{\n\tMItem* pi = expr();\n\tfor (;;)\n\t{\n\t\tswitch(curr_tok)\n\t\t{\n\t\tcase EQUAL:\n\t\t\tpi = new MEquation(pi, expr());\n\t\t\tbreak;\n\t\tcase COMMA:\n\t\t\t{\n\t\t\t\tMSequence* pe = dynamic_cast<MSequence*>(pi);\n\t\t\t\tif (pe == 0)\n\t\t\t\t{\n\t\t\t\t\tpe = new MSequence;\n\t\t\t\t\tpe->add(pi);\n\t\t\t\t}\n\t\t\t\tpe->add(expr());\n\t\t\t\tpi = pe;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn pi;\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::expr()\n{\n\tMItem* pi = term();\n\tfor (;;)\n\t{\n\t\tswitch(curr_tok)\n\t\t{\n\t\tcase PLUS:\n\t\t\tpi = new MAdd(pi, term());\n\t\t\tbreak;\n\t\tcase MINUS:\n\t\t\tpi = new MSub(pi, term());\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn pi;\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::term()\n{\n\tMItem* pi = power();\n\tfor (;;)\n\t\tswitch(curr_tok)\n\t\t{\n\t\tcase MUL:\n\t\t\tpi = new MMul(pi, power());\n\t\t\tbreak;\n\t\tcase DIV:\n\t\t\tpi = new MDiv(pi, power());\n\t\t\tbreak;\n\t\tcase CONTRACT:\n\t\t\t{\n\t\t\t\tMMatrix* pl = mmatrix(pi);\n\t\t\t\tif (pl == 0) throw MathError(Position(), \"invalid left term\");\n\t\t\t\tMMatrix* pr = mmatrix(power());\n\t\t\t\tif (pr == 0) throw MathError(Position(), \"invalid right term\");\n\t\t\t\tpi = new MFuncMat2(matrix_contract, \"contract\", pl, pr);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn pi;\n\t\t}\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::power()\n{\n\tMItem* pi = prim();\n\tfor (;;)\n\t\tswitch(curr_tok)\n\t\t{\n\t\tcase POW:\n\t\t\tpi = new MPow(pi, prim());\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn pi;\n\t\t}\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::prim()\n{\n\tMItem* pi = 0;\n\tget_token();\n\n\tswitch (curr_tok)\n\t{\n\tcase NUMBER:\n\t\t{\n\t\t\tpi = new MConstant(number_value);\n\t\t\tget_token();\n\t\t}\n\t\tbreak;\n\tcase NAME:\n\t\t{\n\t\t\tstring s(string_value);\n\t\t\tget_token();\n\n\t\t\t// if the next token is a left bracket, let's assume it's a function\n\t\t\tif (curr_tok == LP)\n\t\t\t{\n\t\t\t\tpi = func();\n\t\t\t\tif (pi == 0) throw MathError(Position(), \"Unknown function\"); \n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpi = var();\n\t\t\t\tif (is_matrix(pi))\n\t\t\t\t{\n\t\t\t\t\t// we might be extracting the component of a matrix\n\t\t\t\t\tif (curr_tok == LB)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSequence* pe = dynamic_cast<MSequence*>(sequence());\n\t\t\t\t\t\tread_token(RB);\n\n\t\t\t\t\t\tif (pe == 0) { delete pe; throw MathError(Position(), \"comma expected\"); }\n\t\t\t\t\t\tMSequence& e = *pe;\n\t\t\t\t\t\tif (e.size() != 2) throw MathError(Position(), \"syntax error\");\n\t\t\t\t\t\tconst MConstant* pc0 = mconst(e[0]); if ((pc0 == 0) || (is_int(pc0->value()) == false)) throw MathError(Position(), \"syntax error\");\n\t\t\t\t\t\tconst MConstant* pc1 = mconst(e[1]); if ((pc1 == 0) || (is_int(pc1->value()) == false)) throw MathError(Position(), \"syntax error\");\n\t\t\t\t\t\tint i = (int)(pc0->value());\n\t\t\t\t\t\tint j = (int)(pc1->value());\n\n\t\t\t\t\t\tconst MMatrix& m = *mmatrix(pi);\n\t\t\t\t\t\tint nr = m.rows();\n\t\t\t\t\t\tint nc = m.columns();\n\t\t\t\t\t\tif ((i<0)||(i>=nr)) throw MathError(Position(), \"invalid matrix component\");\n\t\t\t\t\t\tif ((j<0)||(j>=nc)) throw MathError(Position(), \"invalid matrix component\");\n\n\t\t\t\t\t\tpi = (m[i][j]->copy());\n\t\t\t\t\t\tdelete pe;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase MINUS:\n\t\t{\n\t\t\tpi = new MNeg(power());\n\t\t}\n\t\tbreak;\n\tcase LP:\n\t\t{\n\t\t\tpi = create();\n\t\t\tread_token(RP); // eat ')'\n\t\t}\n\t\tbreak;\n\tcase LC:\n\t\t{\n\t\t\tpi = sequence();\n\t\t\tif (pi == 0) { throw MathError(Position(), \"comma expected\"); }\n\t\t\tread_token(RC);\n\t\t}\n\t\tbreak;\n\tcase LB:\n\t\t{\n\t\t\tMSequence* pe = dynamic_cast<MSequence*>(sequence());\n\t\t\tif (pe == 0) { delete pe; throw MathError(Position(), \"comma expected\"); }\n\t\t\tMSequence& e = *pe;\n\t\t\tread_token(RB);\n\t\t\tif (is_matrix(e[0]))\n\t\t\t{\n\t\t\t\tint nrow = e.size();\n\t\t\t\tint ncol = mmatrix(e[0])->columns();\n\t\t\t\tint N = e.size();\n\t\t\t\tfor (int i=0; i<N; ++i)\n\t\t\t\t{\n\t\t\t\t\tconst MMatrix* pm = mmatrix(e[i]);\n\t\t\t\t\tif (pm == 0) throw MathError(Position(), \"invalid matrix definition\");\n\t\t\t\t\tif (pm->columns() != ncol) throw MathError(Position(), \"invalid matrix definition\");\n\t\t\t\t\tif (pm->rows() != 1) throw MathError(Position(), \"invalid matrix definition\");\n\t\t\t\t}\n\n\t\t\t\tMMatrix* pm = new MMatrix();\n\t\t\t\tpm->Create(nrow, ncol);\n\t\t\t\tfor (int i=0; i<nrow; ++i) \n\t\t\t\t\tfor (int j=0; j<ncol; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst MMatrix* pk = mmatrix(e[i]);\n\t\t\t\t\t\t(*pm)[i][j] = (*pk)[0][j]->copy();\n\t\t\t\t\t}\n\t\t\t\tpi = pm;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMMatrix* pm = new MMatrix();\n\t\t\t\tint ncol = e.size();\n\t\t\t\tpm->Create(1, ncol);\n\t\t\t\tfor (int i=0; i<ncol; ++i) (*pm)[0][i] = e[i]->copy();\n\t\t\t\tpi = pm;\n\t\t\t}\n\t\t\tdelete pe;\n\t\t}\n\t\tbreak;\n\tcase AB:\n\t\t{\n\t\t\tpi = new MFunc1D(fabs, \"abs\", create());\n\t\t\tif (curr_tok != AB) throw MathError(Position(), \"'|' expected\");\n\t\t\tget_token(); // eat '|'\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tthrow MathError(Position(), \"Token expected\");\n\t}\n\tassert(pi);\n\n\n\t// look for post-operators\n\tdo \n\t{\n\t\tif (curr_tok == FACT)\n\t\t{\n\t\t\tpi = new MFunc1D(fac, \"fac\", pi);\n\t\t\tget_token(); // eat '!'\n\t\t}\n\t\telse if (curr_tok == TRANS)\n\t\t{\n\t\t\tpi = new MFuncMat(matrix_transpose, \"transpose\", pi);\n\t\t\tget_token(); // eat '''\n\t\t}\n\t\telse break;\n\t}\n\twhile (true);\n\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::var()\n{\n\tstring s(string_value);\n\tMItem* pi = 0;\n\t// see if it is a predefined variable\n\tmap<string, double>::iterator pc = CNT.find(s);\n\tif (pc != CNT.end())\n\t{\n\t\tpi = new MNamedCt(pc->second, pc->first);\n\t}\n\telse\n\t{\n\t\t// see if it is a user variable\n\t\tMVariable* pvar = m_po->FindVariable(s);\n\t\tif (pvar == 0)\n\t\t{\n\t\t\tif (m_autoVars == false) throw MathError(Position(), \"Unknown variable\");\n\t\t\t// create a new variable\n\t\t\tpvar = new MVariable(s);\n\t\t\tm_po->AddVariable(pvar);\n\t\t}\n\t\tpi = new MVarRef(pvar);\n\t}\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\n// parse a function\nMItem* MObjBuilder::func()\n{\n\tMItem* pi = 0;\n\tpi = fncnd();\n\tif (pi == 0) pi = fnc1d();\n\tif (pi == 0) pi = fnc2d();\n\tif (pi == 0) pi = fmat();\n\tif (pi == 0) pi = fsym();\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::fnc1d()\n{\n\tMItem* pi = 0;\n\tstring s(string_value);\n\tmap<string, FUNCPTR>::iterator pf = FNC.find(s);\n\tif (pf != FNC.end())\n\t{\n\t\tFUNCPTR fnc = pf->second;\n\t\tif (curr_tok != LP) throw MathError(Position(), \"'(' expected\");\n\t\tpi = new MFunc1D(fnc, pf->first, create());\n\t\tif (curr_tok != RP) throw MathError(Position(), \"')' expected\");\n\t\tget_token();\n\t}\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::fnc2d()\n{\n\tMItem* pi = 0;\n\tstring s(string_value);\n\tmap<string, FUNC2PTR>::iterator pf = FNC2.find(s);\n\tif (pf != FNC2.end())\n\t{\n\t\tFUNC2PTR fnc = pf->second;\n\t\tMItem *p1, *p2;\n\t\tp1 = create();\n\t\tif (curr_tok != COMMA) throw MathError(Position(), \"',' expected\");\n\t\tp2 = create();\n\t\tpi = new MFunc2D(fnc, pf->first, p1, p2);\n\t\tif (curr_tok != RP) throw MathError(Position(), \"')' expected\");\n\t\tget_token();\n\t}\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::fncnd()\n{\n\tMItem* pi = 0;\n\tstring s(string_value);\n\tmap<string, FUNCNPTR>::iterator pf = FNCN.find(s);\n\tif (pf != FNCN.end())\n\t{\n\t\tFUNCNPTR fnc = pf->second;\n\t\tMSequence pl;\n\t\tdo\n\t\t{\n\t\t\tpl.add(create());\n\t\t}\n\t\twhile (curr_tok == COMMA);\n\t\tif (curr_tok != RP) throw MathError(Position(), \"')' expected\");\n\t\tget_token();\n\t\tpi = new MFuncND(fnc, pf->first, pl);\n\t}\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::fmat()\n{\n\tMItem* pi = 0;\n\tstring s(string_value);\n\tmap<string, FUNCMATPTR>::iterator pf = FMAT.find(s);\n\tif (pf != FMAT.end())\n\t{\n\t\tFUNCMATPTR fnc = pf->second;\n\t\tif (curr_tok != LP) throw MathError(Position(), \"'(' expected\");\n\t\tpi = new MFuncMat(fnc, pf->first, create());\n\t\tif (curr_tok != RP) throw MathError(Position(), \"')' expected\");\n\t\tget_token();\n\t}\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\n// Read a symbolic function.\nMItem*\tMObjBuilder::fsym()\n{\n\tstring s(string_value);\n\tif      (s.compare(\"derive\"   ) == 0) return derive   ();\n\telse if (s.compare(\"replace\"  ) == 0) return replace  ();\n\telse if (s.compare(\"taylor\"   ) == 0) return taylor   ();\n\telse if (s.compare(\"integrate\") == 0) return integrate();\n\telse if (s.compare(\"expand\"   ) == 0) return expand   ();\n\telse if (s.compare(\"simplify\" ) == 0) return simplify ();\n\telse if (s.compare(\"solve\"    ) == 0) return solve    ();\n\telse if (s.compare(\"collect\"  ) == 0) return collect  ();\n\telse if (s.compare(\"identity\" ) == 0) return identity ();\n\telse if (s.compare(\"mdiag\"    ) == 0) return mdiag    ();\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nMObjBuilder::Token_value MObjBuilder::get_token()\n{\n\t// remove leading whitespace\n\twhile ((*m_szexpr==' ') || (*m_szexpr=='\\t')) m_szexpr++;\n\n\t// get the first character\n\tchar ch = *m_szexpr++;\n\n\tswitch(ch)\n\t{\n\tcase 0:\n\t\treturn curr_tok = END;\n\tcase '^':\n\tcase '*':\n\tcase '/':\n\tcase '+':\n\tcase '-':\n\tcase '(':\n\tcase ')':\n\tcase '[':\n\tcase ']':\n\tcase '{':\n\tcase '}':\n\tcase '%':\n\tcase ',':\n\tcase '|':\n\tcase '!':\n\tcase '\\'':\n\tcase ':':\n\t\treturn curr_tok = Token_value(ch);\n\tcase '=':\n\t\tif (*m_szexpr=='=') { m_szexpr++; return curr_tok = EQUALITY; } else return curr_tok = EQUAL;\n\t\tbreak;\n\tcase '0': case '1': case '2': case '3': case '4':\n\tcase '5': case '6': case '7': case '8': case '9':\n\tcase '.':\n\t\tm_szexpr--;\n\t\tnumber_value = get_number();\n\t\treturn curr_tok = NUMBER;\n\tcase '$':\n\t\t{\n\t\tif (*m_szexpr != '{') throw MathError(Position(), \"'{' expected\");\n\t\tm_szexpr++;\n\t\tint n = 0;\n\t\tstring_value[0] = 0;\n\t\twhile ((*m_szexpr) && (*m_szexpr != '}')) string_value[n++] = *m_szexpr++;\n\t\tstring_value[n] = 0;\n\t\tif (*m_szexpr != '}') throw MathError(Position(), \"'}' expected\");\n\t\tm_szexpr++;\n\t\t}\n\t\treturn curr_tok = NAME;\n\tdefault:\n\t\tif (isalpha(ch)||(ch=='_'))\n\t\t{\n\t\t\tm_szexpr--;\n\t\t\tget_name(string_value);\n\t\t\treturn curr_tok = NAME;\n\t\t}\n\t\tthrow MathError(Position(), \"Bad token\");\n\t\treturn curr_tok=PRINT;\n\t}\n}\n\ndouble MObjBuilder::get_number()\n{\t\n\tconst char* ch = m_szexpr;\n\n\t// read first digits\n\twhile (isdigit(*ch)) ch++;\n\tif (*ch == '.')\n\t{\n\t\t// read more digits after decimal point\n\t\tch++;\n\t\twhile (isdigit(*ch)) ch++;\n\t}\n\n\t// see if we're using exp notation\n\tif ((*ch == 'E') || (*ch == 'e'))\n\t{\n\t\tch++;\n\t\tif ((*ch == '-') || (*ch == '+')) ch++;\n\n\t\t// read digits\n\t\twhile (isdigit(*ch)) ch++;\n\t}\n\n\tdouble val = atof(m_szexpr);\n\tm_szexpr = ch;\n\n\treturn val;\n}\n\nvoid MObjBuilder::get_name(char* str)\n{\n\tint n = 0;\n\tbool binside = false;\n\twhile (isalnum(*m_szexpr) || (*m_szexpr == '_') || (*m_szexpr == '.') || (*m_szexpr == '{') || ((*m_szexpr == '}')&&binside) || binside)\n\t{\n\t\tif (*m_szexpr == '{') binside = true;\n\t\tif (*m_szexpr == '}') binside = false;\n\t\tstr[n++] = *m_szexpr++;\n\t}\n\tstr[n] = 0;\n}\n\n//-----------------------------------------------------------------------------\n// Check's to see if the current token is\nvoid MObjBuilder::read_token(MObjBuilder::Token_value n, bool bnext)\n{\n\tswitch (n)\n\t{\n\tcase COMMA : if (curr_tok != COMMA ) throw MathError(Position(), \"',' expected\"); break;\n\tcase NAME  : if (curr_tok != NAME  ) throw MathError(Position(), \"syntax error\"); break;\n\tcase NUMBER: if (curr_tok != NUMBER) throw MathError(Position(), \"syntax error\"); break;\n\tcase RP    : if (curr_tok != RP    ) throw MathError(Position(), \"')' expected\"); break;\n\tcase LC    : if (curr_tok != LC    ) throw MathError(Position(), \"'{' expected\"); break;\n\tcase RC    : if (curr_tok != RC    ) throw MathError(Position(), \"'}' expected\"); break;\n\tcase RB    : if (curr_tok != RB    ) throw MathError(Position(), \"']' expected\"); break;\n\t}\n\n\tif (bnext) get_token();\n}\n\n//-----------------------------------------------------------------------------\n// Read a variable name; assuming the current token is indeed a variable\nMVariable* MObjBuilder::read_var(bool badd)\n{\n\t// read the name token\n\tread_token(NAME);\n\n\t// get the variable\n\tMVariable* pv = m_po->FindVariable(string_value);\n\tif (pv == 0)\n\t{\n\t\tif (badd)\n\t\t{\n\t\t\tpv = new MVariable(string_value);\n\t\t\tm_po->AddVariable(pv);\n\t\t}\n\t\telse throw MathError(Position(), \"unknown variable\"); \n\t}\n\treturn pv;\n}\n\n//-----------------------------------------------------------------------------\n// Read an integer number\nint MObjBuilder::read_int()\n{\n\tread_token(NUMBER);\n\tdouble f = number_value;\n\tif (f - floor(f) != 0) throw MathError(Position(), \"integer expected\");\n\treturn (int) f;\n}\n\n//-----------------------------------------------------------------------------\n// Read a double number\ndouble MObjBuilder::read_double()\n{\n\tread_token(NUMBER);\n\treturn number_value;\n}\n\n//-----------------------------------------------------------------------------\n// read a mathematical expression\nMItem* MObjBuilder::read_math()\n{\n\treturn create();\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::derive()\n{\n\t// read the argument\n\tMITEM e = read_math();\n\n\t// read the comma\n\tread_token(COMMA, false);\n\n\t// find the variable\n\tMITEM v = read_math();\n\n\tMItem* pi = 0;\n\tif (is_sequence(v))\n\t{\n\t\t// read the parentheses\n\t\tread_token(RP);\n\n\t\t// calculate derivatives\n\t\tconst MSequence& s = *msequence(v);\n\t\tpi = MDerive(e, s).copy();\n\t}\n\telse\n\t{\n\t\t// make sure v is a variable\n\t\tif (is_var(v) == false) throw MathError(Position(), \"This is not a variable\");\n\t\tconst MVariable& x = *(mvar(v)->GetVariable());\n\t\t\n\t\t// read optional arguments\n\t\tif (curr_tok == COMMA)\n\t\t{\n\t\t\t// skip the comma\n\t\t\tget_token();\n\n\t\t\tif (curr_tok == NUMBER)\n\t\t\t{\n\t\t\t\t// read the number\n\t\t\t\tint n = read_int();\n\n\t\t\t\t// make sure it is at least one\n\t\t\t\tif (n <= 0) throw MathError(Position(), \"integer must be at least one\");\n\n\t\t\t\tread_token(RP);\n\n\t\t\t\tpi = MDerive(e, x, n).copy();\n\t\t\t}\n\t\t\telse MathError(Position(), \"number unexpected\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tread_token(RP);\n\t\t\tpi = MDerive(e, x).copy();\n\t\t}\n\t}\n\n\t// return the result\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::replace()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\n\t// read the comma\n\tread_token(COMMA, false);\n\n\t// read the expression to replace\n\tMITEM x = read_math();\n\n\tif (is_sequence(x))\n\t{\n\t\tconst MSequence& v = *msequence(x);\n\n\t\t// read the comma\n\t\tread_token(COMMA);\n\n\t\t// read the left curly bracket\n\t\tread_token(LC, false);\n\n\t\t// read the list of new variables\n\t\tconst MSequence& s = *msequence(sequence());\n\n\t\t// read the right curly bracket\n\t\tread_token(RC);\n\n\t\t// read the right parenthesis\n\t\tread_token(RP);\n\n\t\t// calculate the new expression\n\t\tMItem* pi = MReplace(e, v, s).copy();\n\n\t\t// clean-up\n\t\tdelete &s;\n\n\t\t// done\n\t\treturn pi;\n\t}\n\telse\n\t{\n\t\t// read comma\n\t\tif (curr_tok == COMMA)\n\t\t{\n\t\t\t// read the second expression\n\t\t\tMITEM s = read_math();\n\n\t\t\t// read the right parenthesis\n\t\t\tread_token(RP);\n\n\t\t\t// call replace function\n\t\t\treturn MReplace(e, x, s).copy();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tread_token(RP);\n\t\t\treturn MReplace(e,x).copy();\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::taylor()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\n\t// read comma\n\tread_token(COMMA);\n\n\t// read the variable name\n\tMVariable* pv = read_var();\n\n\t// read comma\n\tread_token(COMMA);\n\n\t// read the point at which to expand the expression\n\tdouble z = read_double();\n\n\t// read comma\n\tread_token(COMMA);\n\n\t// read the number of terms in taylor expansion\n\tint n = read_int();\n\tif (n < 0) throw MathError(Position(), \"integer must be larger than zero\");\n\n\t// read the right parenthesis\n\tread_token(RP);\n\n\treturn MTaylor(e, *pv, z, n).copy();\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::integrate()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\n\t// read comma\n\tread_token(COMMA);\n\n\t// read the variable\n\tMVariable* pv = read_var(true);\n\n\tMItem* pi = 0;\n\tif (curr_tok == COMMA)\n\t{\n\t\t// if a comma is there we calculate the definite integral\n\t\tMITEM a = read_math();\n\n\t\tread_token(COMMA, false);\n\n\t\tMITEM b = create();\n\n\t\tread_token(RP);\n\n\t\tpi = MIntegral(e, *pv, a, b).copy();\n\t}\n\telse\n\t{\n\t\t// else we calculate the indefinite integral\n\t\tread_token(RP);\n\n\t\tpi = MIntegral(e, *pv).copy();\n\t}\n\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::expand()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\n\tMItem* pi = 0;\n\tif (curr_tok == COMMA)\n\t{\n\t\tMITEM s = read_math();\n\t\tread_token(RP);\n\t\tpi = MExpand(e, s).copy();\n\t}\n\telse \n\t{\n\t\tread_token(RP);\n\t\tpi = MExpand(e).copy();\n\t}\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::simplify()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\tread_token(RP);\n\n\tMItem* pi = MExpand(e).copy();\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::solve()\n{\n\t// read the argument\n\tMITEM e = read_math();\n\n\t// read the comma\n\tread_token(COMMA, false);\n\n\t// find the variable\n\tMITEM v = read_math();\n\n\tread_token(RP);\n\n\tMItem* pi = MSolve(e, v).copy();\n\n\t// return the result\n\treturn pi;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::collect()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\tread_token(COMMA, false);\n\tMITEM a = read_math();\n\tread_token(RP);\n\treturn MCollect(e, a).copy();\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::identity()\n{\n\t// read the expression\n\tMITEM e = read_math();\n\tread_token(RP);\n\tif (is_int(e))\n\t{\n\t\treturn matrix_identity((int)e.value());\n\t}\n\telse throw MathError(Position(), \"constant expected\");\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nMItem* MObjBuilder::mdiag()\n{\n\t// read the expression\n\tMSequence* pe = dynamic_cast<MSequence*>(sequence());\n\tread_token(RP);\n\tMSequence& e = *pe;\n\n\tint n = pe->size();\n\tMMatrix* pm = new MMatrix();\n\tpm->Create(n, n);\n\tfor (int i=0; i<n; ++i) \n\t\tfor (int j=0; j<n; ++j)\n\t\t\t{\n\t\t\t\tif (i == j)\n\t\t\t\t{\n\t\t\t\t\tconst MItem* pi = e[i];\n\t\t\t\t\t(*pm)[i][j] = pi->copy();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t(*pm)[i][j] = new MConstant(0.0);\n\t\t\t\t}\n\t\t\t}\n\tdelete pe;\n\treturn pm;\n}\n"
  },
  {
    "path": "FECore/MObjBuilder.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"MathObject.h\"\n\nclass MathError\n{\npublic:\n\tMathError(int n, const char* sz) : m_npos(n), m_szerr(sz){}\n\tint GetPosition() { return m_npos; }\n\tconst char* GetErrorStr() { return m_szerr; }\n\nprotected:\n\tint\tm_npos;\n\tconst char*\tm_szerr;\n};\n\n\nclass FECORE_API MObjBuilder\n{\nprivate:\n\tenum Token_value {\n\t\tNAME, NUMBER, END,\n\t\tEQUAL='=', PLUS='+', MINUS='-', MUL='*', DIV='/', MOD = '%', POW='^', FACT='!', TRANS = '\\'',\n\t\tLP='(',\tRP=')', AB = '|', LB = '[', RB = ']', LC='{', RC='}',\n\t\tCONTRACT = ':',\n\t\tCOMMA = ',', PRINT, EQUALITY\n\t};\n\npublic:\n\tMObjBuilder();\n\n\tbool Create(MSimpleExpression* mo, const std::string& ex, bool eval);\n\tMathObject* Create(const std::string& ex , bool eval);\n\n\tvoid setAutoVars(bool b) { m_autoVars = b; }\n\n\tstatic bool Add1DFunction(const std::string& name, double (*f)(double));\n\nprotected:\n\tMItem*\tcreate();\n\tMItem*\tcreate_sequence();\n\tMItem*\texpr();\n\tMItem*\tterm ();\n\tMItem*\tpower();\n\tMItem*\tprim ();\n\tMItem*  var  ();\n\tMItem*  func ();\n\tMItem*\tfnc1d();\n\tMItem*\tfnc2d();\n\tMItem*\tfncnd();\n\tMItem*\tfmat ();\n\tMItem*\tfsym();\n\tMItem*\tsequence();\n\n\tdouble get_number();\n\tvoid get_name(char* str);\n\n\tint Position() { return (int)(m_szexpr - m_szorg); }\n\nprotected:\n\tMItem*\tderive   ();\n\tMItem*\treplace  ();\n\tMItem*\ttaylor   ();\n\tMItem*\tintegrate();\n\tMItem*\texpand   ();\n\tMItem*\tsimplify ();\n\tMItem*\tsolve    ();\n\tMItem*\tcollect  ();\n\tMItem*  identity ();\n\tMItem*  mdiag    ();\n\nprotected:\n\tMathObject*\tm_po;\n\n\tToken_value get_token();\n\tToken_value\tcurr_tok;\n\n\t// read an expression\n\tMItem* read_math();\n\n\t// read a required token\n\tvoid read_token(Token_value n, bool bnext = true);\n\n\t// read a variable name\n\tMVariable* read_var(bool badd = false);\n\n\t// read an int\n\tint read_int();\n\n\t// read a double\n\tdouble read_double();\n\n\t// process string substitutions\n\tstd::string processStrings(const std::string& ex);\n\n\nprotected:\n\tconst char*\tm_szexpr, *m_szorg;\n\tdouble\t\tnumber_value;\n\tchar\t\tstring_value[256];\n\n\tbool\t\tm_autoVars;\t// add new variables automatically\n};\n"
  },
  {
    "path": "FECore/MReplace.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\n#include \"MathObject.h\"\n#include <map>\n#include <string>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nMITEM MReplace(const MITEM& e, const MITEM& x)\n{\n\tif (is_equation(x))\n\t{\n\t\tMITEM l = x.Left();\n\t\tMITEM r = x.Right();\n\t\treturn MReplace(e, l, r);\n\t}\n\telse return e;\n}\n\n//-----------------------------------------------------------------------------\nMITEM MReplace(const MITEM& e, const MVariable& x, const MITEM& s)\n{\n\tMITEM v(x);\n\treturn MReplace(e,v,s);\n}\n\n//-----------------------------------------------------------------------------\nMITEM MReplace(const MITEM& e, const MITEM& x, const MITEM& s)\n{\n\t// see if they are equal\n\tif (e == x) return s;\n\n\tswitch (e.Type())\n\t{\n\tcase MCONST:\n\tcase MFRAC:\n\tcase MNAMED:\n\tcase MVAR:\n\t\treturn e;\n\tcase MNEG:\n\t\treturn MEvaluate(-(MReplace(-e, x, s)));\n\tcase MADD:\n\t\t{\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn MEvaluate(l+r);\n\t\t}\n\tcase MSUB:\n\t\t{\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn MEvaluate(l-r);\n\t\t}\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn MEvaluate(l*r);\n\t\t}\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn MEvaluate(l/r);\n\t\t}\n\tcase MPOW:\n\t\t{\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn MEvaluate(l^r);\n\t\t}\n\tcase MEQUATION:\n\t\t{\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn MITEM(new MEquation(l.copy(), r.copy()));\n\t\t}\n\tcase MF1D:\n\t\t{\n\t\t\tconst MFunc1D* pf = mfnc1d(e);\n\t\t\tMITEM p = pf->Item()->copy();\n\t\t\treturn new MFunc1D(pf->funcptr(), pf->Name(), MReplace(p, x, s).copy());\n\t\t}\n\tcase MF2D:\n\t\t{\n\t\t\tconst MFunc2D* pf = mfnc2d(e);\n\t\t\tMITEM l = MReplace(e.Left (), x, s);\n\t\t\tMITEM r = MReplace(e.Right(), x, s);\n\t\t\treturn new MFunc2D(pf->funcptr(), pf->Name(), l.copy(), r.copy());\n\t\t}\n/*\tcase MSFND:\n\t\t{\n\t\t\tMSFuncND* pf = msfncnd(e);\n\t\t\tMITEM v(pf->Item()->copy());\n\t\t\tMITEM vnew = MReplace(v, x, s);\n\n\t\t\tstring s = pf->Name();\n\t\t\tM1DFuncDef* pfd = DEF1D.find(s)->second;\n\t\t\tassert(pfd);\n\t\t\treturn pfd->CreateFunction(vnew.ItemPtr());\n\t\t}\n*/\tcase MMATRIX:\n\t\t{\n\t\t\tconst MMatrix* pm = mmatrix(e);\n\t\t\tint nrows = pm->rows();\n\t\t\tint ncols = pm->columns();\n\t\t\tMMatrix* pmnew = new MMatrix;\n\t\t\tpmnew->Create(nrows, ncols);\n\t\t\tfor (int i=0; i<nrows; ++i)\n\t\t\t\tfor (int j=0; j<nrows; ++j)\n\t\t\t\t{\n\t\t\t\t\tMITEM mij = pm->Item(i,j)->copy();\n\t\t\t\t\t(*pmnew)[i][j] = MReplace(mij, x, s).copy();\n\t\t\t\t}\n\t\t\treturn pmnew;\n\t\t}\n\t}\n\n\tassert(false);\n\treturn e;\n}\n\n//-----------------------------------------------------------------------------\n// replace multiple expressions with other expressions\nMITEM MReplace(const MITEM& e, const MSequence& x, const MSequence& s)\n{\n\tif (x.size() != s.size()) return e;\n\n\tMITEM r = e;\n\tconst int N = x.size();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tMITEM xi = x[i]->copy();\n\t\tMITEM si = s[i]->copy();\n\t\tr = MReplace(r, xi, si);\n\t}\n\n\treturn r;\n}\n"
  },
  {
    "path": "FECore/MSimplify.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\nusing namespace std;\n\n//-----------------------------------------------------------------------------\n// Simplify an expression\nMITEM MSimplify(const MITEM& i)\n{\n\tMITEM e = MEvaluate(i);\n\tswitch (e.Type())\n\t{\n\tcase MFRAC:\n\t\t{\n\t\t\tFRACTION f = mfrac(i)->fraction();\n\t\t\tif (f.d == 1.0) return f.n;\n\t\t}\n\t\tbreak;\n\tcase MNEG: return -MExpand(e.Item());\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_add(r))\n\t\t\t{\n\t\t\t\tMITEM a = r.Left();\n\t\t\t\tMITEM b = r.Right();\n\t\t\t\treturn MExpand(l*a) + MExpand(l*b);\n\t\t\t}\n\t\t\tif (is_sub(r))\n\t\t\t{\n\t\t\t\tMITEM a = r.Left();\n\t\t\t\tMITEM b = r.Right();\n\t\t\t\treturn MExpand(l*a) - MExpand(l*b);\n\t\t\t}\n\t\t\tif (is_add(l)) \n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(r*a) + MExpand(r*b);\n\t\t\t}\n\t\t\tif (is_sub(l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(r*a) - MExpand(r*b);\n\t\t\t}\n\t\t\tMITEM Ml = MExpand(l);\n\t\t\tMITEM Mr = MExpand(r);\n//\t\t\tif ((l != Ml) || (r != Mr)) return Ml*Mr;\n\t\t\tif ((l != Ml) || (r != Mr)) return MExpand(Ml*Mr); // it looks like this could create an infinite loop\n\t\t}\n\t\tbreak;\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_add(l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(a/r) + MExpand(b/r);\n\t\t\t}\n\t\t\tif (is_sub(l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\treturn MExpand(a/r) - MExpand(b/r);\n\t\t\t}\n\t\t\tMITEM Ml = MExpand(l);\n\t\t\tMITEM Mr = MExpand(r);\n\t\t\tif ((l != Ml) || (r != Mr)) return MExpand(Ml/Mr); else return Ml/Mr;\n\t\t}\n\t\tbreak;\n\tcase MADD:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\treturn MExpand(l) + MExpand(r);\n\t\t}\n\t\tbreak;\n\tcase MSUB:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\treturn MExpand(l) - MExpand(r);\n\t\t}\n\t\tbreak;\n\tcase MPOW:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tif (is_int(r) && is_add(l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\tint n = (int) r.value();\n\t\t\t\tif (n == 0) return 1.0;\n\t\t\t\tMITEM s(0.0);\n\t\t\t\tfor (int i=0; i<=n; ++i)\n\t\t\t\t{\n\t\t\t\t\tdouble C = binomial(n,i);\n\t\t\t\t\ts = s + C*MExpand(a^(n-i))*MExpand(b^i);\n\t\t\t\t}\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\tif (is_int(r) && is_sub(l))\n\t\t\t{\n\t\t\t\tMITEM a = l.Left();\n\t\t\t\tMITEM b = l.Right();\n\t\t\t\tint n = (int) r.value();\n\t\t\t\tif (n == 0) return 1.0;\n\t\t\t\tMITEM s(0.0);\n\t\t\t\tfor (int i=0; i<=n; ++i)\n\t\t\t\t{\n\t\t\t\t\tdouble C = binomial(n,i);\n\t\t\t\t\tMITEM t = C*MExpand(a^(n-i))*MExpand(b^i);\n\t\t\t\t\tif (i%2 == 0) s = s + t;\n\t\t\t\t\telse s = s - t;\n\t\t\t\t}\n\t\t\t\treturn s;\n\t\t\t}\n\t\t\tif (is_add(r))\n\t\t\t{\n\t\t\t\tMITEM a = r.Left();\n\t\t\t\tMITEM b = r.Right();\n\t\t\t\treturn ((l^a)*(l^b));\n\t\t\t}\n\t\t\tMITEM le = MExpand(l);\n\t\t\tMITEM re = MExpand(r);\n\t\t\treturn le^re;\n\t\t}\n\t\tbreak;\n\tcase MF1D:\n\t\t{\n\t\t\tconst string& f = mfnc1d(e)->Name();\n\t\t\tif (f.compare(\"cos\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (p.Type() == MADD)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Cos(a)*Cos(b) - Sin(a)*Sin(b)); \n\t\t\t\t}\n\t\t\t\tif (p.Type() == MSUB)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Cos(a)*Cos(b) + Sin(a)*Sin(b)); \n\t\t\t\t}\n\t\t\t}\n\t\t\tif (f.compare(\"sin\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (p.Type() == MADD)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Sin(a)*Cos(b) + Cos(a)*Sin(b)); \n\t\t\t\t}\n\t\t\t\tif (p.Type() == MSUB)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand(Sin(a)*Cos(b) - Cos(a)*Sin(b)); \n\t\t\t\t}\n\t\t\t}\n\t\t\tif (f.compare(\"tan\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (p.Type() == MADD)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand((Tan(a)+Tan(b))/(1.0 - Tan(a)*Tan(b))); \n\t\t\t\t}\n\t\t\t\tif (p.Type() == MSUB)\n\t\t\t\t{\n\t\t\t\t\tMITEM a = p.Left();\n\t\t\t\t\tMITEM b = p.Right();\n\t\t\t\t\treturn MExpand((Tan(a)-Tan(b))/(1.0 + Tan(a)*Tan(b))); \n\t\t\t\t}\n\t\t\t}\n/*\t\t\tif (f.compare(\"ln\") == 0)\n\t\t\t{\n\t\t\t\tMITEM p = e.Param();\n\t\t\t\tif (is_mul(p))\n\t\t\t\t{\n\t\t\t\t\tMITEM a = MExpand(p.Left());\n\t\t\t\t\tMITEM b = MExpand(p.Right());\n\t\t\t\t\treturn MExpand(Log(a)+Log(b));\n\t\t\t\t}\n\t\t\t\tif (is_div(p))\n\t\t\t\t{\n\t\t\t\t\tMITEM a = MExpand(p.Left());\n\t\t\t\t\tMITEM b = MExpand(p.Right());\n\t\t\t\t\treturn MExpand(Log(a)-Log(b));\n\t\t\t\t}\n\t\t\t\tif (is_pow(p))\n\t\t\t\t{\n\t\t\t\t\tMITEM a = MExpand(p.Left());\n\t\t\t\t\tMITEM b = MExpand(p.Right());\n\t\t\t\t\treturn MExpand(b*Log(a));\n\t\t\t\t}\n\t\t\t}\n*/\t\t\tconst MFunc1D* pf = mfnc1d(e);\n\t\t\tMITEM v = MExpand(e.Param());\n\t\t\treturn new MFunc1D(pf->funcptr(), pf->Name(), v.copy());\n\t\t}\n\t\tbreak;\n\tcase MSFNC:\n\t\t{\n\t\t\tMITEM f(msfncnd(i)->Value()->copy());\n\t\t\treturn MExpand(f);\n\t\t}\n\t\tbreak;\n\tcase MEQUATION:\n\t\t{\n\t\t\tMITEM l = e.Left();\n\t\t\tMITEM r = e.Right();\n\t\t\tMITEM Ml = MExpand(l);\n\t\t\tMITEM Mr = MExpand(r);\n\t\t\treturn new MEquation(Ml.copy(), Mr.copy());\n\t\t}\n\t\tbreak;\n\tcase MSEQUENCE:\n\t\t{\n\t\t\tconst MSequence& q = *msequence(i);\n\t\t\tMSequence* ps = new MSequence();\n\t\t\tfor (int i=0; i<q.size(); ++i)\n\t\t\t{\n\t\t\t\tMITEM qi = q[i]->copy();\n\t\t\t\tMITEM vi = MExpand(qi);\n\t\t\t\tps->add(vi.copy());\n\t\t\t}\n\t\t\treturn ps;\n\t\t}\n\t\tbreak;\n\tcase MMATRIX:\n\t\t{\n\t\t\tconst MMatrix& m = *mmatrix(e);\n\t\t\tint ncol = m.columns();\n\t\t\tint nrow = m.rows();\n\n\t\t\tMMatrix* pdm = new MMatrix;\n\t\t\tpdm->Create(nrow, ncol);\n\t\t\tfor (int i=0; i<nrow; ++i)\n\t\t\t\tfor (int j=0; j<ncol; ++j)\n\t\t\t\t{\n\t\t\t\t\tMITEM mij(m.Item(i,j)->copy());\n\t\t\t\t\tMITEM aij = MExpand(mij);\n\t\t\t\t\t(*pdm)[i][j] = aij.copy();\n\t\t\t\t}\n\t\t\treturn MITEM(pdm);\n\t\t}\n\t\tbreak;\n\t}\n\treturn e;\n}\n"
  },
  {
    "path": "FECore/MSolve.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MMath.h\"\n#include \"MEvaluate.h\"\n\n//-----------------------------------------------------------------------------\nMITEM MSolve(const MITEM& e, const MVariable& x);\nMITEM MSolve(const MITEM& l, const MITEM& r, const MVariable& x);\nMITEM MSolve(const MSequence& e, const MSequence& x);\nMITEM MSolve(const MEquation& e, const MVariable& x);\n\n//-----------------------------------------------------------------------------\n// Solve an expression e for the variable(s) v.\n// The expression can be an equation linear in v or a system of linear equations.\nMITEM MSolve(const MITEM& e, const MITEM& v)\n{\n\tif (is_sequence(e))\n\t{\n\t\t// solve a linear system of equations\n\t\t// make sure v contains a list of variables\n\t\tif (is_sequence(v) == false) throw InvalidOperation();\n\n\t\tconst MSequence& se = *msequence(e);\n\t\tconst MSequence& sv = *msequence(v);\n\n\t\t// make sure we have as many equations as unknowns\n\t\tif (se.size() != sv.size()) throw InvalidOperation();\n\n\t\t// make sure all entries in e are equations\n\t\tfor (int i=0; i<se.size(); ++i) if (is_equation(se[i]) == false) throw InvalidOperation();\n\n\t\t// make sure all items in v are variables\n\t\tfor (int i=0; i<sv.size(); ++i) if (is_var(sv[i]) == false) throw InvalidOperation();\n\n\t\t// solve the system of equations\n\t\treturn MSolve(se, sv);\n\t}\n\telse\n\t{\n\t\tif (is_var(v))\n\t\t{\n\t\t\tconst MVariable& x = *mvar(v)->GetVariable();\n\t\t\treturn MSolve(e, x);\n\t\t}\n\t\telse throw InvalidOperation();\n\t}\n\tassert(false);\n\treturn e;\n}\n\n//-----------------------------------------------------------------------------\n// solve a system of linear equations\nMITEM MSolve(const MSequence& e, const MSequence& v)\n{\n\tMSequence sol = e;\n\tMSequence var = v;\n\tint neq = e.size();\n\tfor (int i=0; i<neq; ++i)\n\t{\n\t\tconst MEquation& eqi = *mequation(sol[i]);\n\n\t\t// find a variable this equation depends on\n\t\tint nvar = var.size();\n\t\tconst MVariable* pv = 0;\n\t\tfor (int j=0; j<nvar; ++j)\n\t\t{\n\t\t\tconst MVariable* pvj = mvar(var[j])->GetVariable();\n\t\t\tif (is_dependent(sol[i], *pvj))\n\t\t\t{ \n\t\t\t\tpv = pvj; \n\t\t\t\tvar.remove(j);\n\t\t\t\tbreak; \n\t\t\t}\n\t\t}\n\t\tif (pv == 0) throw InvalidOperation();\n\n\t\tconst MVariable& x = *pv;\n\n\t\t// solve the i-th equation for x\n\t\tMITEM si = MSolve(eqi, x);\n\n\t\t// make sure the LHS is the variable\n\t\tif (is_equation(si) == false) throw InvalidOperation();\n\t\tconst MEquation& ei = *mequation(si);\n\t\tif (is_equal(ei.LeftItem(), x) == false) throw InvalidOperation();\n\n\t\t// replace i-th equation with this solution\n\t\tsol.replace(i, si.copy());\n\n\t\t// substitute the solution in all other equations\n\t\tfor (int j=0; j<neq; ++j)\n\t\t{\n\t\t\tif (i != j)\n\t\t\t{\n\t\t\t\tMITEM ej = sol[j]->copy();\n\t\t\t\tMITEM newj = MReplace(ej, si);\n\t\t\t\tsol.replace(j, newj.copy());\n\t\t\t}\n\t\t}\n\t}\n\treturn sol.copy();\n}\n\n//-----------------------------------------------------------------------------\nMITEM MSolve(const MEquation& e, const MVariable& x)\n{\n\tMITEM l = e.LeftItem()->copy();\n\tMITEM r = e.RightItem()->copy();\n\treturn MSolve(l, r, x);\n}\n\n//-----------------------------------------------------------------------------\nMITEM MSolve(const MITEM& e, const MVariable& x)\n{\n\tif (is_equation(e))\n\t{\n\t\tconst MEquation& eq = *mequation(e);\n\t\treturn MSolve(eq, x);\n\t}\n\telse\n\t{\n\t\tMITEM i(new MEquation(e.copy(), new MConstant(0)));\n\t\treturn MSolve(i, x);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMITEM MSolve(const MITEM& L, const MITEM& r, const MVariable& x)\n{\n\t// if the RHS depends on x, bring it to the left side\n\tif (is_dependent(r,x))\n\t{\n\t\tMITEM c(0.0);\n\t\treturn MEvaluate(MSolve(L - r, c, x));\n\t}\n\n\t// simplify LHS as much as possible\n\tMITEM l = MEvaluate(L);\n\n\t// check LHS for dependancy on x and try to solve\n\tswitch (l.Type())\n\t{\n\tcase MNEG: return MEvaluate(MSolve(l.Item(), -r, x)); break;\n\tcase MADD:\n\t\t{\n\t\t\tMITEM a = l.Left();\n\t\t\tMITEM b = l.Right();\n\t\t\tif (is_dependent(a, x) == false) return MEvaluate(MSolve(b, r - a, x));\n\t\t\tif (is_dependent(b, x) == false) return MEvaluate(MSolve(a, r - b, x));\n\t\t\telse\n\t\t\t{\n\t\t\t\tMITEM v(x);\n\t\t\t\tMITEM a = MCollect(l, v);\n\t\t\t\treturn MSolve(a, r, x);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase MSUB:\n\t\t{\n\t\t\tMITEM a = l.Left();\n\t\t\tMITEM b = l.Right();\n\t\t\tif (is_dependent(a, x) == false) return MEvaluate(MSolve(-b, r - a, x));\n\t\t\tif (is_dependent(b, x) == false) return MEvaluate(MSolve(a, r + b, x));\n\t\t\telse\n\t\t\t{\n\t\t\t\tMITEM v(x);\n\t\t\t\tMITEM a = MCollect(l, v);\n\t\t\t\treturn MSolve(a, r, x);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase MMUL:\n\t\t{\n\t\t\tMITEM a = l.Left();\n\t\t\tMITEM b = l.Right();\n\t\t\tif (is_dependent(a, x) == false) return MEvaluate(MSolve(b, r/a, x));\n\t\t\tif (is_dependent(b, x) == false) return MEvaluate(MSolve(a, r/b, x));\n\t\t}\n\t\tbreak;\n\tcase MDIV:\n\t\t{\n\t\t\tMITEM a = l.Left();\n\t\t\tMITEM b = l.Right();\n\t\t\treturn MEvaluate(MSolve(a, r*b, x));\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn MITEM(new MEquation(l.copy(), r.copy()));\n}\n"
  },
  {
    "path": "FECore/MTypes.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MTypes.h\"\n#include <math.h>\n\n//-----------------------------------------------------------------------------\n// Calculates the greatest common factor of two integers\nlong gcf(long a, long b)\n{\n\tif ((a==0) || (b==0)) return 1;\n\tif (a<0) a = -a;\n\tif (b<0) b = -b;\n\n\tif (a > b) { a ^= b; b ^= a; a ^= b; }\n\n\tlong q, r;\n\tdo\n\t{\n\t\tq = b/a;\n\t\tr = b%a;\n\n\t\tif (r > 0)\n\t\t{\n\t\t\tb = (b-r)/q;\n\t\t\ta = r;\n\t\t}\n\t}\n\twhile (r>0);\n\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\n// reduces the fraction to it simplest form\nvoid FRACTION::normalize()\n{\n\tdouble s = (n*d<0?-1:1);\n\tn = fabs(n);\n\td = fabs(d);\n\n\tif (d != 0)\n\t{\n\t\tlong in = (long ) n;\n\t\tlong id = (long ) d;\n\t\tif ((n==(double) in) && (d == (double) id))\n\t\t{\n\t\t\tdouble c = (double) gcf(in, id);\n\t\t\tn /= c;\n\t\t\td /= c;\n\t\t}\n\t}\n\n\tif (s<0) n = -n;\n}\n"
  },
  {
    "path": "FECore/MTypes.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n//-----------------------------------------------------------------------------\n// class that describes a fraction of two real numbers\nclass FRACTION\n{\npublic:\n\tdouble n, d;\t\t// nominator, denominator\n\npublic:\n\tFRACTION() : n(0), d(1) {}\n\tFRACTION(double a) : n(a), d(1) {}\n\tFRACTION(double a, double b) : n(a), d(b) {}\n\tFRACTION(const FRACTION& f) { n = f.n; d = f.d; }\n\n\tFRACTION& operator = (double a) { n = a; d = 1; return (*this); }\n\tFRACTION& operator = (const FRACTION& f) { n = f.n; d = f.d; return (*this); }\n\n\tFRACTION& operator += (double a) { n += a*d; return (*this); }\n\tFRACTION& operator += (const FRACTION& a) { n = n*a.d + d*a.n; d *= a.d; return (*this); }\n\n\tFRACTION& operator -= (double a) { n -= a*d; return (*this); }\n\tFRACTION& operator -= (const FRACTION& a) { n = n*a.d - d*a.n; d *= a.d; return (*this); }\n\n\tFRACTION& operator /= (double a) { d *= a; return (*this); }\n\n\toperator double() { return n / d; }\n\n\tvoid normalize();\n};\n\n//-----------------------------------------------------------------------------\n// operators for FRACTION\ninline FRACTION operator + (FRACTION& l, FRACTION& r) { return FRACTION(l.n*r.d + r.n*l.d, l.d*r.d); }\ninline FRACTION operator + (FRACTION& l, double    r) { return FRACTION(l.n + r*l.d, l.d); }\ninline FRACTION operator + (double    l, FRACTION& r) { return FRACTION(l*r.d + r.n, r.d); }\n\ninline FRACTION operator - (FRACTION& l, FRACTION& r) { return FRACTION(l.n*r.d - r.n*l.d, l.d*r.d); }\ninline FRACTION operator - (FRACTION& l, double    r) { return FRACTION(l.n - r*l.d, l.d); }\ninline FRACTION operator - (double    l, FRACTION& r) { return FRACTION(l*r.d - r.n, r.d); }\n\ninline FRACTION operator * (FRACTION& l, FRACTION& r) { return FRACTION(l.n*r.n, l.d*r.d); }\ninline FRACTION operator * (FRACTION& l, double    r) { return FRACTION(l.n*r, l.d); }\ninline FRACTION operator * (double    l, FRACTION& r) { return FRACTION(r.n*l, r.d); }\n\ninline FRACTION operator / (FRACTION& l, FRACTION& r) { return FRACTION(l.n*r.d, l.d*r.n); }\ninline FRACTION operator / (FRACTION& l, double    r) { return FRACTION(l.n, l.d*r); }\ninline FRACTION operator / (double    l, FRACTION& r) { return FRACTION(l*r.d, r.n); }\n\ninline FRACTION operator - (FRACTION& a) { return FRACTION(-a.n, a.d); }\n//-----------------------------------------------------------------------------\n// Calculates the greatest common factor\nlong gcf(long a, long b);\n\n"
  },
  {
    "path": "FECore/MathObject.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MathObject.h\"\n#include \"MMath.h\"\n#include \"MObjBuilder.h\"\n#include <float.h>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nMathObject::MathObject()\n{\n\n}\n\nMathObject::MathObject(const MathObject& mo)\n{\n\t// copy variable list\n\tfor (int i = 0; i < mo.Variables(); ++i)\n\t{\n\t\tAddVariable(new MVariable(*mo.Variable(i)));\n\t}\n}\n\nvoid MathObject::Clear()\n{\n\tfor (int i = 0; i < m_Var.size(); ++i) delete m_Var[i];\n\tm_Var.clear();\n}\n\n//-----------------------------------------------------------------------------\nvoid MathObject::operator = (const MathObject& mo)\n{\n\t// copy variable list\n\tfor (int i = 0; i < mo.Variables(); ++i)\n\t{\n\t\tAddVariable(new MVariable(*mo.Variable(i)));\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMathObject::~MathObject()\n{\n\tfor (int i = 0; i < m_Var.size(); ++i) delete m_Var[i];\n\tm_Var.clear();\n}\n\n//-----------------------------------------------------------------------------\nMVariable* MathObject::AddVariable(const std::string& var, double initVal)\n{\n\tif (m_Var.empty() == false)\n\t{\n\t\tMVarList::iterator it = m_Var.begin();\n\t\tfor (it = m_Var.end(); it != m_Var.end(); ++it)\n\t\t\tif ((*it)->Name() == var) return *it;\n\t}\n\n\tMVariable* pv = new MVariable(var, initVal);\n\tpv->setIndex((int)m_Var.size());\n\tm_Var.push_back(pv);\n\treturn pv;\n}\n\nvoid MathObject::AddVariable(MVariable* pv)\n{\n\tif (m_Var.empty() == false)\n\t{\n\t\tMVarList::iterator it = m_Var.begin();\n\t\tfor (it = m_Var.end(); it != m_Var.end(); ++it)\n\t\t\tif (*it == pv) return;\n\t}\n\tpv->setIndex((int)m_Var.size());\n\tm_Var.push_back(pv);\n}\n\nvoid MathObject::AddVariables(const vector<std::string>& varList)\n{\n\tfor (const std::string& s : varList)\n\t{\n\t\tAddVariable(s);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMVariable* MathObject::FindVariable(const string& s)\n{\n\tfor (MVarList::iterator it = m_Var.begin(); it != m_Var.end(); ++it)\n\t\tif ((*it)->Name() == s) return *it;\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\nint MSimpleExpression::Items()\n{\n\tif (m_item.Type() == MSEQUENCE)\n\t{\n\t\tMSequence* pe = dynamic_cast<MSequence*>(m_item.ItemPtr());\n\t\treturn pe->size();\n\t}\n\telse return 1;\n}\n\nbool MSimpleExpression::IsValid() const\n{\n\treturn (m_item.ItemPtr() != nullptr);\n}\n\n//-----------------------------------------------------------------------------\ndouble MSimpleExpression::value(const std::string& s)\n{\n\tCreate(s);\n\treturn value();\n}\n\n//-----------------------------------------------------------------------------\ndouble MSimpleExpression::value(const MItem* pi) const\n{\n\tswitch (pi->Type())\n\t{\n\tcase MCONST: return (mnumber(pi)->value());\n\tcase MFRAC : return (mnumber(pi)->value());\n\tcase MNAMED: return (mnumber(pi)->value());\n\tcase MVAR  : return (mnumber(pi)->value());\n\tcase MNEG: return -value(munary(pi)->Item());\n\tcase MADD: return value(mbinary(pi)->LeftItem()) + value(mbinary(pi)->RightItem());\n\tcase MSUB: return value(mbinary(pi)->LeftItem()) - value(mbinary(pi)->RightItem());\n\tcase MMUL: return value(mbinary(pi)->LeftItem()) * value(mbinary(pi)->RightItem());\n\tcase MDIV: return value(mbinary(pi)->LeftItem()) / value(mbinary(pi)->RightItem());\n\tcase MPOW: return pow(value(mbinary(pi)->LeftItem()), value(mbinary(pi)->RightItem()));\n\tcase MF1D:\n\t\t{\n\t\t\tdouble a = value(munary(pi)->Item());\n\t\t\treturn (mfnc1d(pi)->funcptr())(a);\n\t\t}\n\t\tbreak;\n\tcase MF2D:\n\t\t{\n\t\t\tdouble a = value(mbinary(pi)->LeftItem());\n\t\t\tdouble b = value(mbinary(pi)->RightItem());\n\t\t\treturn (mfnc2d(pi)->funcptr())(a, b);\n\t\t}\n\t\tbreak;\n\tcase MSFNC:\n\t\t{\n\t\t\treturn value(msfncnd(pi)->Value());\n\t\t};\n\t\tbreak;\n\tcase MFND:\n\t\t{\n\t\t\tconst MFuncND* f = mfncnd(pi);\n\t\t\tint n = f->Params();\n\t\t\tvector<double> d(n, 0.0);\n\t\t\tfor (int i = 0; i < n; ++i) d[i] = value(f->Param(i));\n\t\t\treturn (f->funcptr())(d.data(), n);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\treturn 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble MSimpleExpression::value(const MItem* pi, const std::vector<double>& var) const\n{\n\tswitch (pi->Type())\n\t{\n\tcase MCONST: return (mnumber(pi)->value());\n\tcase MFRAC : return (mnumber(pi)->value());\n\tcase MNAMED: return (mnumber(pi)->value());\n\tcase MVAR  : return (var[mvar(pi)->index()]);\n\tcase MNEG: return -value(munary(pi)->Item(), var);\n\tcase MADD: return value(mbinary(pi)->LeftItem(), var) + value(mbinary(pi)->RightItem(), var);\n\tcase MSUB: return value(mbinary(pi)->LeftItem(), var) - value(mbinary(pi)->RightItem(), var);\n\tcase MMUL: return value(mbinary(pi)->LeftItem(), var) * value(mbinary(pi)->RightItem(), var);\n\tcase MDIV: return value(mbinary(pi)->LeftItem(), var) / value(mbinary(pi)->RightItem(), var);\n\tcase MPOW: return pow(value(mbinary(pi)->LeftItem(), var), value(mbinary(pi)->RightItem(), var));\n\tcase MF1D:\n\t\t{\n\t\t\tdouble a = value(munary(pi)->Item(), var);\n\t\t\treturn (mfnc1d(pi)->funcptr())(a);\n\t\t}\n\t\tbreak;\n\tcase MF2D:\n\t\t{\n\t\t\tdouble a = value(mbinary(pi)->LeftItem(), var);\n\t\t\tdouble b = value(mbinary(pi)->RightItem(), var);\n\t\t\treturn (mfnc2d(pi)->funcptr())(a, b);\n\t\t}\n\t\tbreak;\n\tcase MSFNC:\n\t\t{\n\t\t\treturn value(msfncnd(pi)->Value(), var);\n\t\t};\n\t\tbreak;\n\tcase MFND:\n\t\t{\n\t\t\tconst MFuncND* f = mfncnd(pi);\n\t\t\tint n = f->Params();\n\t\t\tvector<double> d(n, 0.0);\n\t\t\tfor (int i = 0; i < n; ++i) d[i] = value(f->Param(i), var);\n\t\t\treturn (f->funcptr())(d.data(), n);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\treturn 0;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nMSimpleExpression::MSimpleExpression(const MSimpleExpression& mo) : MathObject(mo), m_item(mo.m_item)\n{\n\t// The copy c'tor of MathObject copied the variables, but any MVarRefs still point to the mo object, not this object's var list.\n\t// Calling the following function fixes this\n\tfixVariableRefs(m_item.ItemPtr());\n}\n\n//-----------------------------------------------------------------------------\nvoid MSimpleExpression::operator=(const MSimpleExpression& mo)\n{\n\t// copy base object\n\tMathObject::operator=(mo);\n\n\t// copy the item\n\tm_item = mo.m_item;\n\n\t// The = operator of MathObject copied the variables, but any MVarRefs still point to the mo object, not this object's var list.\n\t// Calling the following function fixes this\n\tfixVariableRefs(m_item.ItemPtr());\n}\n\n//-----------------------------------------------------------------------------\nvoid MSimpleExpression::fixVariableRefs(MItem* pi)\n{\n\tif (pi->Type() == MVAR)\n\t{\n\t\tMVarRef* var = static_cast<MVarRef*>(pi);\n\t\tint index = var->GetVariable()->index();\n\t\tvar->SetVariable(Variable(index));\n\t}\n\telse if (is_unary(pi))\n\t{\n\t\tMUnary* uno = static_cast<MUnary*>(pi);\n\t\tfixVariableRefs(uno->Item());\n\t}\n\telse if (is_binary(pi))\n\t{\n\t\tMBinary* bin = static_cast<MBinary*>(pi);\n\t\tfixVariableRefs(bin->LeftItem());\n\t\tfixVariableRefs(bin->RightItem());\n\t}\n\telse if (is_nary(pi))\n\t{\n\t\tMNary* any = static_cast<MNary*>(pi);\n\t\tfor (int i = 0; i < any->Params(); ++i) fixVariableRefs(any->Param(i));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Create a simple expression object from a string\nbool MSimpleExpression::Create(const std::string& expr, bool autoVars)\n{\n\tMObjBuilder mob;\n\tmob.setAutoVars(autoVars);\n\tif (mob.Create(this, expr, false) == false) return false;\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/MathObject.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"MItem.h\"\n#include <vector>\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\ntypedef std::vector<MVariable*>\tMVarList;\n\n//-----------------------------------------------------------------------------\n// This class defines the base class for all math objects\n// It also stores a list of all the variables\nclass FECORE_API MathObject\n{\npublic:\n\tMathObject();\n\tMathObject(const MathObject& mo);\n\tvoid operator = (const MathObject& mo);\n\n\tvirtual ~MathObject();\n\n\tint Dim() { return (int)m_Var.size(); }\n\n\tMVariable* AddVariable(const std::string& var, double initVal = 0.0);\n\tvoid AddVariables(const std::vector<std::string>& varList);\n\n\tvoid AddVariable(MVariable* pv);\n\tMVariable* FindVariable(const std::string& s);\n\tint Variables() const { return (int)m_Var.size(); }\n\n\tMVariable* Variable(int i) { return m_Var[i]; }\n\tconst MVariable* Variable(int i) const { return m_Var[i]; }\n\n\tvirtual void Clear();\n\n\tvirtual MathObject* copy() = 0;\n\nprotected:\n\tMVarList\tm_Var;\t\t// list of variables\n};\n\n//-----------------------------------------------------------------------------\n// This class defines a simple epxression that can be evaluated by\n// setting the values of the variables.\nclass FECORE_API MSimpleExpression : public MathObject\n{\npublic:\n\tMSimpleExpression() {}\n\tMSimpleExpression(const MSimpleExpression& mo);\n\tvoid operator = (const MSimpleExpression& mo);\n\n\tvoid SetExpression(MITEM& e) { m_item = e; }\n\tMITEM& GetExpression() { return m_item; }\n\tconst MITEM& GetExpression() const { return m_item; }\n\n\t// Create a simple expression object from a string\n\tbool Create(const std::string& expr, bool autoVars = false);\n\n\t// copy the expression\n\tMathObject* copy() { return new MSimpleExpression(*this); }\n\n\t// These functions are not thread safe since variable values can be overridden by different threads\n\t// In multithreaded applications, use the thread safe functions below.\n\tdouble value() const { return value(m_item.ItemPtr());  }\n\n\t// combines Create and value. Not efficient usage! \n\tdouble value(const std::string& s);\n\n\t// This is a thread safe function to evaluate the expression\n\t// The values of the variables are passed as an argument. This function\n\t// does not call MVariable->value, but uses these passed values insteads.\n\t// Make sure that the var array has the same size as the variable array of the expression\n\tdouble value_s(const std::vector<double>& var) const\n\t{ \n\t\tassert(var.size() == m_Var.size());\n\t\treturn value(m_item.ItemPtr(), var); \n\t}\n\n\tint Items();\n\n\tbool IsValid() const;\n\nprotected:\n\tdouble value(const MItem* pi) const;\n\tdouble value(const MItem* pi, const std::vector<double>& var) const;\n\nprotected:\n\tvoid fixVariableRefs(MItem* pi);\n\nprotected:\n\tMITEM\tm_item;\n};\n"
  },
  {
    "path": "FECore/MatrixOperator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n\n// abstract base class for matrix operators, i.e. a class that can calculate a matrix-vector product\nclass FECORE_API MatrixOperator\n{\npublic:\n\tMatrixOperator() {}\n\tvirtual ~MatrixOperator() {}\n\n\t// calculate the product Ax = y\n\tvirtual bool mult_vector(double* x, double* y) = 0;\n};\n"
  },
  {
    "path": "FECore/MatrixProfile.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"MatrixProfile.h\"\n#include <assert.h>\nusing namespace std;\n\nSparseMatrixProfile::ColumnProfile::ColumnProfile(const SparseMatrixProfile::ColumnProfile& a)\n{\n\tm_data = a.m_data;\n}\n\nvoid SparseMatrixProfile::ColumnProfile::insertRow(int row)\n{\n\t// first, check if empty\n\tif (m_data.empty()) { push_back(row, row); return; }\n\n\tint N = size();\n\n\t// check some easy cases first\n\tif (row + 1 <  m_data[0].start) { push_front(row, row); return; }\n\tif (row + 1 == m_data[0].start) { m_data[0].start--; return; }\n\n\tif (row - 1 >  m_data[N-1].end) { push_back(row, row); return; }\n\tif (row - 1 == m_data[N-1].end) { m_data[N-1].end++; return; }\n\n\t// general case, find via bisection\n\tint N0 = 0, N1 = N-1;\n\tint n = N/2, m = 0;\n\twhile (true)\n\t{\n\t\tRowEntry& rn = m_data[n];\n\n\t\t// see if row falls inside the interval\n\t\tif ((row >= rn.start) && (row <= rn.end))\n\t\t{\n\t\t\t// no need to do anything\n\t\t\treturn;\n\t\t}\n\n\t\tif (row < rn.start)\n\t\t{\n\t\t\tassert(n>0); // this should always be the case due to the easy case handling above\n\n\t\t\t// get the previous entry\n\t\t\tRowEntry& r0 = m_data[n-1];\n\n\t\t\tif (row > r0.end)\n\t\t\t{\n\t\t\t\tif (row + 1 == rn.start)\n\t\t\t\t{\n\t\t\t\t\tif (r0.end == row - 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t// merge entries\n\t\t\t\t\t\tr0.end = rn.end;\n\t\t\t\t\t\tm_data.erase(m_data.begin() + n);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\telse \n\t\t\t\t\t{\n\t\t\t\t\t\trn.start--;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (row - 1 == r0.end)\n\t\t\t\t{\n\t\t\t\t\tr0.end++;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tRowEntry re = {row, row};\n\t\t\t\t\tm_data.insert(m_data.begin() + n, re);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tN1 = n;\n\t\t\t\tn = (N0 + N1) / 2;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tassert(row > rn.end);\n\n\t\t\t// get the next entry\n\t\t\tRowEntry& r1 = m_data[n + 1];\n\n\t\t\tif (row < r1.start)\n\t\t\t{\n\t\t\t\tif (row - 1 == rn.end)\n\t\t\t\t{\n\t\t\t\t\tif (r1.start == row + 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t// merge entries\n\t\t\t\t\t\tr1.start = rn.start;\n\t\t\t\t\t\tm_data.erase(m_data.begin() + n);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\trn.end++;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (row + 1 == r1.start)\n\t\t\t\t{\n\t\t\t\t\tr1.start--;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tRowEntry re = { row, row };\n\t\t\t\t\tm_data.insert(m_data.begin() + n + 1, re);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tN0 = n;\n\t\t\t\tn = (N0 + N1 + 1) / 2;\n\t\t\t}\n\t\t}\n\t\t++m;\n\t\tassert(m <= N);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! MatrixProfile constructor. Takes the nr of equations as input argument.\n//! If n is larger than zero a default profile is constructor for a diagonal\n//! matrix.\nSparseMatrixProfile::SparseMatrixProfile(int nrow, int ncol)\n{\n\tm_nrow = nrow;\n\tm_ncol = ncol;\n\n\t// allocate storage profile\n\tif (ncol > 0) \n\t{\n\t\tint nres = (m_ncol < 100 ? m_ncol : 100);\n\t\tm_prof.resize(ncol);\n\t\tfor (int i=0; i<ncol; ++i) m_prof[i].reserve(nres);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! allocate storage for profile\nvoid SparseMatrixProfile::Create(int nrow, int ncol)\n{\n\tm_nrow = nrow;\n\tm_ncol = ncol;\n\n\tint nres = (m_ncol < 100 ? m_ncol : 100);\n\tm_prof.resize(ncol);\n\tfor (int i = 0; i<ncol; ++i) m_prof[i].reserve(nres);\n}\n\n//-----------------------------------------------------------------------------\n//! Copy constructor. Simply copies the profile\n\nSparseMatrixProfile::SparseMatrixProfile(const SparseMatrixProfile& mp)\n{\n\tm_nrow = mp.m_nrow;\n\tm_ncol = mp.m_ncol;\n\tm_prof = mp.m_prof;\n}\n\n//-----------------------------------------------------------------------------\n//! Assignment operator. Copies the profile.\n \nSparseMatrixProfile& SparseMatrixProfile::operator =(const SparseMatrixProfile& mp)\n{\n\tm_nrow = mp.m_nrow;\n\tm_ncol = mp.m_ncol;\n\tm_prof = mp.m_prof;\n\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\n//! Create the profile of a diagonal matrix\nvoid SparseMatrixProfile::CreateDiagonal()\n{\n\tint n = min(m_nrow, m_ncol);\n\n\t// initialize the profile to a diagonal matrix\n\tfor (int i = 0; i<n; ++i)\n\t{\n\t\tColumnProfile& a = m_prof[i];\n\t\ta.insertRow(i);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid SparseMatrixProfile::Clear()\n{ \n\tm_prof.clear(); \n}\n\n//-----------------------------------------------------------------------------\n//! Updates the profile. The LM array contains a list of elements that contribute\n//! to the sparse matrix. Each \"element\" defines a set of degrees of freedom that\n//! are somehow connected. Each pair of dofs that are connected contributes to\n//! the global stiffness matrix and therefor also to the matrix profile.\nvoid SparseMatrixProfile::UpdateProfile(vector< vector<int> >& LM, int M)\n{\n\t// get the dimensions of the matrix\n\tint nr = m_nrow;\n\tint nc = m_ncol;\n\n\t// make sure there is work to do\n\tif (nr*nc == 0) return;\n\n\t// Count the number of elements that contribute to a certain column\n\t// The pval array stores this number (which I also call the valence\n\t// of the column)\n\tvector<int> pval(nc, 0);\n\n\t// fill the valence array\n\tint Ntot = 0;\n\tfor (int i = 0; i<M; ++i)\n\t{\n\t\tint* lm = &(LM[i])[0];\n\t\tint N = (int)LM[i].size();\n\t\tNtot += N;\n\t\tfor (int j = 0; j<N; ++j)\n\t\t{\n\t\t\tif (lm[j] >= 0) pval[lm[j]]++;\n\t\t}\n\t}\n\n\t// create a \"compact\" 2D array that stores for each column the element\n\t// numbers that contribute to that column. The compact array consists\n\t// of two arrays. The first one (pelc) contains all element numbers, sorted\n\t// by column. The second array stores for each column a pointer to the first\n\t// element in the pelc array that contributes to that column.\n\tvector<int> pelc(Ntot);\n\tvector<int*> ppelc(nc);\n\n\t// set the column pointers\n\tppelc[0] = &pelc[0];\n\tfor (int i = 1; i<nc; ++i) ppelc[i] = ppelc[i - 1] + pval[i - 1];\n\n\t// fill the pelc array\n\tfor (int i = 0; i<M; ++i)\n\t{\n\t\tint* lm = &(LM[i])[0];\n\t\tint N = (int)LM[i].size();\n\t\tfor (int j = 0; j<N; ++j)\n\t\t{\n\t\t\tif (lm[j] >= 0) *(ppelc[lm[j]])++ = i;\n\t\t}\n\t}\n\n\t// reset pelc pointers\n\tppelc[0] = &pelc[0];\n\tfor (int i = 1; i<nc; ++i) ppelc[i] = ppelc[i - 1] + pval[i - 1];\n\n\t// loop over all columns\n#pragma omp parallel for schedule(dynamic)\n\tfor (int i = 0; i<nc; ++i)\n\t{\n\t\tif (pval[i] > 0)\n\t\t{\n\t\t\t// get the column\n\t\t\tColumnProfile& a = m_prof[i];\n\n\t\t\t// loop over all elements in the plec\n\t\t\tfor (int j = 0; j<pval[i]; ++j)\n\t\t\t{\n\t\t\t\tint iel = (ppelc[i])[j];\n\t\t\t\tint* lm = &(LM[iel])[0];\n\t\t\t\tint N = (int)LM[iel].size();\n\t\t\t\tfor (int k = 0; k<N; ++k)\n\t\t\t\t{\n\t\t\t\t\tif (lm[k] >= 0)\n\t\t\t\t\t{ \n\t\t\t\t\t\ta.insertRow(lm[k]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! inserts an entry into the profile\nvoid SparseMatrixProfile::Insert(int i, int j)\n{\n\tColumnProfile& a = m_prof[j];\n\ta.insertRow(i);\n}\n\n//-----------------------------------------------------------------------------\n// extract the matrix profile of a block\nSparseMatrixProfile SparseMatrixProfile::GetBlockProfile(int nrow0, int ncol0, int nrow1, int ncol1) const\n{\n\tint nrows = nrow1 - nrow0 + 1;\n\tint ncols = ncol1 - ncol0 + 1;\n\tassert(nrows > 0);\n\tassert(ncols > 0);\n\n\t// This will store the block profile\n\tSparseMatrixProfile bMP(nrows, ncols);\n\n\tfor (int j=0; j<ncols; ++j)\n\t{\n\t\tconst ColumnProfile& sj = m_prof[ncol0+j];\n\t\tColumnProfile& dj = bMP.m_prof[j];\n\t\tint nr = sj.size();\n\t\tfor (int i=0; i<nr; i++)\n\t\t{\n\t\t\tconst RowEntry& ri = sj[i];\n\n\t\t\tint n0 = ri.start;\n\t\t\tint n1 = ri.end;\n\n\t\t\tif ((n1 >= nrow0)&&(n0 <= nrow1))\n\t\t\t{\n\t\t\t\tif (n0 < nrow0) n0 = nrow0;\n\t\t\t\tif (n1 > nrow1) n1 = nrow1;\n\n\t\t\t\tn0 -= nrow0;\n\t\t\t\tn1 -= nrow0;\n\n\t\t\t\tdj.push_back(n0, n1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn bMP;\n}\n"
  },
  {
    "path": "FECore/MatrixProfile.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vector.h\"\n\n//-----------------------------------------------------------------------------\n//! This class stores the profile of a sparse matrix. A profile is defined by the\n//! column and row indices of the non-zero elements of a matrix. \n//! These elements are stored in a condensed format.\n//! This means that for each column, an array of pairs is stored where each pair\n//! identifies the start and end row index of the nonzero elements in that column.\n//! The matrix profile is used to build the sparse matrix structure \n//! in an efficient way.\n\nclass FECORE_API SparseMatrixProfile\n{\npublic:\n\tstruct RowEntry\n\t{\n\t\tint start, end;\n\t};\n\n\tclass ColumnProfile\n\t{\n\tpublic:\n\t\tColumnProfile() {}\n\n\t\tColumnProfile(const ColumnProfile& a);\n\n\t\t// get the number of row entries\n\t\tint size() const { return (int) m_data.size(); }\n\n\t\t// access\n\t\tRowEntry& operator [] (int i) { return m_data[i]; }\n\t\tconst RowEntry& operator [] (int i) const { return m_data[i]; }\n\n\t\t// make room\n\t\tvoid clear() { m_data.clear(); }\n\n\t\t// reserve some storage\n\t\tvoid reserve(int n)\n\t\t{\n\t\t\tm_data.reserve(n);\n\t\t}\n\n\t\t// add to the end\n\t\tvoid push_back(int n0, int n1)\n\t\t{\n\t\t\tRowEntry re = { n0, n1 };\n\t\t\tm_data.push_back(re);\n\t\t}\n\n\t\tvoid push_front(int n0, int n1)\n\t\t{\n\t\t\tRowEntry re = {n0, n1};\n\t\t\tm_data.insert(m_data.begin(), re);\n\t\t}\n\n\t\t// add row index to column profile\n\t\tvoid insertRow(int row);\n\n\tprivate:\n\t\tstd::vector<RowEntry>\tm_data;\t// the column profile data\n\t};\n\npublic:\n\t//! Constructor. Takes the nr of equations as the input argument\n\tSparseMatrixProfile(int nrow = 0, int ncol = 0);\n\n\t//! allocate storage for profile\n\tvoid Create(int nrow, int ncol);\n\n\t//! copy constructor\n\tSparseMatrixProfile(const SparseMatrixProfile& mp);\n\n\t//! assignment operator\n\tSparseMatrixProfile& operator = (const SparseMatrixProfile& mp);\n\n\t//! Create the profile of a diagonal matrix\n\tvoid CreateDiagonal();\n\n\t//! clears the matrix profile\n\tvoid Clear();\n\n\t//! updates the profile for an array of elements\n\tvoid UpdateProfile(std::vector< std::vector<int> >& LM, int N);\n\n\t//! inserts an entry into the profile (This is an expensive operation!)\n\tvoid Insert(int i, int j);\n\n\t//! returns the number of rows\n\tint Rows() const { return m_nrow; }\n\n\t//! returns the number of columns\n\tint Columns() const { return m_ncol; }\n\n\t//! returns the non-zero row indices (in condensed format) for a column\n\tColumnProfile& Column(int i) { return m_prof[i]; }\n\n\t// Extracts a block profile\n\tSparseMatrixProfile GetBlockProfile(int nrow0, int ncol0, int nrow1, int ncol1) const;\n\nprivate:\n\tint\tm_nrow, m_ncol;\t\t\t\t//!< dimensions of matrix\n\tstd::vector<ColumnProfile>\tm_prof;\t//!< the actual profile in condensed format\n};\n"
  },
  {
    "path": "FECore/NLConstraintDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"NLConstraintDataRecord.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModel.h\"\n\n//-----------------------------------------------------------------------------\nvoid NLConstraintDataRecord::SetData(const char* szexpr)\n{\n    char szcopy[MAX_STRING] = {0};\n    strcpy(szcopy, szexpr);\n    char* sz = szcopy, *ch;\n    m_Data.clear();\n    strcpy(m_szdata, szexpr);\n    do\n    {\n        ch = strchr(sz, ';');\n        if (ch) *ch++ = 0;\n        FELogNLConstraintData* pdata = fecore_new<FELogNLConstraintData>(sz, GetFEModel());\n        if (pdata) m_Data.push_back(pdata);\n        else throw UnknownDataField(sz);\n        sz = ch;\n    }\n    while (ch);\n}\n\n//-----------------------------------------------------------------------------\nNLConstraintDataRecord::NLConstraintDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_NLC) {}\n\n//-----------------------------------------------------------------------------\nint NLConstraintDataRecord::Size() const { return (int)m_Data.size(); }\n\n//-----------------------------------------------------------------------------\ndouble NLConstraintDataRecord::Evaluate(int item, int ndata)\n{\n    FEModel* fem = GetFEModel();\n    int nc = item - 1;\n    if ((nc < 0) || (nc >= fem->NonlinearConstraints())) return 0;\n    \n\tFENLConstraint& nlc = *fem->NonlinearConstraint(nc);\n\treturn m_Data[ndata]->value(nlc);\n}\n\n//-----------------------------------------------------------------------------\nvoid NLConstraintDataRecord::SelectAllItems()\n{\n    FEModel* fem = GetFEModel();\n    int n = fem->NonlinearConstraints();\n\tm_item.resize(n);\n\tfor (int i = 0; i<n; ++i) m_item[i] = i + 1;\n}\n"
  },
  {
    "path": "FECore/NLConstraintDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n#include \"FENLConstraint.h\"\n\n//-----------------------------------------------------------------------------\n//! Base class for nonlinear constraints log data (e.g. rigid connectors)\nclass FECORE_API FELogNLConstraintData : public FELogData\n{\n    FECORE_SUPER_CLASS(FELOGNLCONSTRAINTDATA_ID)\n    FECORE_BASE_CLASS(FELogNLConstraintData)\n\npublic:\n    FELogNLConstraintData(FEModel* fem) : FELogData(fem) {}\n    virtual ~FELogNLConstraintData(){}\n    virtual double value(FENLConstraint& rc) = 0;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API NLConstraintDataRecord : public DataRecord\n{\npublic:\n\tNLConstraintDataRecord(FEModel* pfem);\n    double Evaluate(int item, int ndata) override;\n    void SetData(const char* sz) override;\n    void SelectAllItems() override;\n\tint Size() const override;\n    \nprivate:\n    vector<FELogNLConstraintData*>\tm_Data;\n};\n"
  },
  {
    "path": "FECore/NodeDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"NodeDataRecord.h\"\n#include \"FEAnalysis.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModel.h\"\n\n//-----------------------------------------------------------------------------\nNodeDataRecord::NodeDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_NODE) {}\n\n//-----------------------------------------------------------------------------\nint NodeDataRecord::Size() const { return (int)m_Data.size(); }\n\n//-----------------------------------------------------------------------------\nvoid NodeDataRecord::SetData(const char* szexpr)\n{\n\tchar szcopy[MAX_STRING] = {0};\n\tstrcpy(szcopy, szexpr);\n\tchar* sz = szcopy, *ch;\n\tm_Data.clear();\n\tstrcpy(m_szdata, szexpr);\n\tFEModel* fem = GetFEModel();\n\tdo\n\t{\n\t\tch = strchr(sz, ';');\n\t\tif (ch) *ch++ = 0;\n\t\tFELogNodeData* pdata = fecore_new<FELogNodeData>(sz, fem);\n\t\tif (pdata) m_Data.push_back(pdata);\n\t\telse \n\t\t{\n\t\t\t// see if this refers to a DOF of the model\n\t\t\tint ndof = fem->GetDOFIndex(sz);\n\t\t\tif (ndof >= 0)\n\t\t\t{\n\t\t\t\t// Add an output for a nodal variable\n\t\t\t\tpdata = new FENodeVarData(fem, ndof);\n\t\t\t\tm_Data.push_back(pdata);\n\t\t\t}\n\t\t\telse throw UnknownDataField(sz);\n\t\t}\n\t\tsz = ch;\n\t}\n\twhile (ch);\n}\n\n//-----------------------------------------------------------------------------\ndouble NodeDataRecord::Evaluate(int item, int ndata)\n{\n\tFEMesh& mesh = GetFEModel()->GetMesh();\n\tint nnode = item - 1;\n\tassert((nnode>=0)&&(nnode<mesh.Nodes()));\n\tif ((nnode < 0) || (nnode >= mesh.Nodes())) return 0;\n\n\tFENode& node = mesh.Node(nnode);\n\treturn m_Data[ndata]->value(node);\n}\n\n//-----------------------------------------------------------------------------\nvoid NodeDataRecord::SelectAllItems()\n{\n\tint n = GetFEModel()->GetMesh().Nodes();\n\tm_item.resize(n);\n\tfor (int i=0; i<n; ++i) m_item[i] = i+1;\n}\n\n//-----------------------------------------------------------------------------\nvoid NodeDataRecord::SetItemList(FEItemList* items, const std::vector<int>& selection)\n{\n\t// TODO: We don't support using a selection of a node set yet. \n\tassert(selection.empty());\n\tFENodeSet* pns = dynamic_cast<FENodeSet*>(items); assert(pns);\n\tint n = pns->Size();\n\tm_item.resize(n);\n\tfor (int i = 0; i < n; ++i) m_item[i] = (*pns)[i] + 1;\n}\n\n//-----------------------------------------------------------------------------\nFENodeVarData::FENodeVarData(FEModel* pfem, int ndof) : FELogNodeData(pfem), m_ndof(ndof) {}\n\n//-----------------------------------------------------------------------------\ndouble FENodeVarData::value(const FENode& node)\n{\n\treturn node.get(m_ndof);\n}\n"
  },
  {
    "path": "FECore/NodeDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n#include \"FELogNodeData.h\"\n\nclass FENodeSet;\nclass FENode;\n\n//-----------------------------------------------------------------------------\n//! This class records nodal data\n//! \\todo should I create a different class for each data record? Like for the plot file?\nclass FECORE_API NodeDataRecord : public DataRecord\n{\npublic:\n\tNodeDataRecord(FEModel* pfem);\n\tdouble Evaluate(int item, int ndata) override;\n\tvoid SetData(const char* sz) override;\n\tvoid SelectAllItems() override;\n\tint Size() const override;\n\n\tvoid SetItemList(FEItemList* items, const std::vector<int>& selection) override;\n\nprivate:\n\tvector<FELogNodeData*>\tm_Data;\n};\n\n//-----------------------------------------------------------------------------\n// Special class for outputting nodal variables\nclass FECORE_API FENodeVarData : public FELogNodeData\n{\npublic:\n\tFENodeVarData(FEModel* pfem, int ndof);\n\tdouble value(const FENode& node) override;\n\nprivate:\n\tint\tm_ndof;\n};\n"
  },
  {
    "path": "FECore/ParamString.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"ParamString.h\"\n\n//=============================================================================\nvoid cstrncpy(char* pd, char* ps, int l)\n{\n\tfor (int i = 0; i <= l; ++i) pd[i] = ps[i];\n}\n\nParamString::ParamString(const char* szparam)\n{\n\tParamRef p;\n\tstd::string tmp;\n\tconst char* sz = szparam;\n\tint in = 0;\n\twhile (*sz)\n\t{\n\t\tif (*sz == '.')\n\t\t{\n\t\t\t// add parameter\n\t\t\tm_param.push_back(p);\n\n\t\t\t// reset\n\t\t\tp.clear();\n\t\t\ttmp.clear();\n\t\t\tin = 0;\n\t\t}\n\t\telse if (*sz == '[')\n\t\t{\n\t\t\tin = 1;\n\t\t}\n\t\telse if (*sz == ']')\n\t\t{\n\t\t\tif (in == 1)\n\t\t\t{\n\t\t\t\tconst char* s = tmp.c_str();\n\t\t\t\tp._index = atoi(s);\n\t\t\t}\n\t\t}\n\t\telse if (*sz == '(')\n\t\t{\n\t\t\tin = 2;\n\t\t}\n\t\telse if (*sz == ')')\n\t\t{\n\t\t\tif (in == 2)\n\t\t\t{\n\t\t\t\tconst char* s = tmp.c_str();\n\t\t\t\tp._id = atoi(s);\n\t\t\t}\n\t\t}\n\t\telse if (*sz == '\\'')\n\t\t{\n\t\t\tif (in == 2)\n\t\t\t{\n\t\t\t\tin = 3;\n\t\t\t}\n\t\t}\n\t\telse if (*sz == '\"')\n\t\t{\n\t\t\tif (in == 2)\n\t\t\t{\n\t\t\t\tin = 3;\n\t\t\t}\n\t\t}\n\t\telse \n\t\t{\n\t\t\tif      (in == 0) p._name.push_back(*sz);\n\t\t\telse if (in == 1) tmp.push_back(*sz);\n\t\t\telse if (in == 2) tmp.push_back(*sz);\n\t\t\telse if (in == 3) p._idName.push_back(*sz);\n\t\t}\n\n\t\t// next char\n\t\tsz++;\n\t}\n\n\t// don't forget to add the last ref\n\tm_param.push_back(p);\n}\n\n//-----------------------------------------------------------------------------\nParamString::ParamString(const ParamString& p)\n{\n\tm_param = p.m_param;\n}\n\n//-----------------------------------------------------------------------------\nvoid ParamString::operator=(const ParamString& p)\n{\n\tm_param = p.m_param;\n}\n\n//-----------------------------------------------------------------------------\nParamString::~ParamString()\n{\n}\n\n//-----------------------------------------------------------------------------\n//! number of refs in string\nint ParamString::count() const\n{\n\treturn (int) m_param.size();\n}\n\n//-----------------------------------------------------------------------------\nParamString ParamString::next() const\n{\n\tParamString p;\n\tif (m_param.size() > 1)\n\t{\n\t\tp.m_param.insert(p.m_param.end(), m_param.begin() + 1, m_param.end());\n\t}\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\nbool ParamString::isValid() const { return (m_param.empty() == false); }\n\n//-----------------------------------------------------------------------------\nvoid ParamString::clear() { m_param.clear(); }\n\n//-----------------------------------------------------------------------------\nParamString ParamString::last() const\n{\n\tParamString p;\n\tif (m_param.size() > 1)\n\t{\n\t\tp.m_param.push_back(m_param[m_param.size()-1]);\n\t}\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\nbool ParamString::operator==(const std::string& s) const\n{\n\tif (m_param.empty()) return false;\n\treturn m_param[0]._name == s;\n}\n\n//-----------------------------------------------------------------------------\nbool ParamString::operator!=(const std::string& s) const\n{\n\tif (m_param.empty()) return false;\n\treturn m_param[0]._name != s;\n}\n\n//-----------------------------------------------------------------------------\nconst char* ParamString::c_str() const\n{\n\tif (m_param.empty()) return 0;\n\treturn m_param[0]._name.c_str();\n}\n\n//-----------------------------------------------------------------------------\nstd::string ParamString::string() const\n{\n\tif (m_param.empty()) return 0;\n\treturn m_param[0]._name;\n}\n\n//-----------------------------------------------------------------------------\n//! get the index (-1 if index not a number)\nint ParamString::Index() const\n{\n\tif (m_param.empty()) return -1;\n\treturn m_param[0]._index;\n}\n\n//-----------------------------------------------------------------------------\n//! get the ID (-1 if index not a number)\nint ParamString::ID() const\n{\n\tif (m_param.empty()) return -1;\n\treturn m_param[0]._id;\n}\n\n//-----------------------------------------------------------------------------\n//! get the index name (null if not defined)\nconst char* ParamString::IDString() const\n{\n\tif (m_param.empty()) return 0;\n\tif (m_param[0]._idName.empty()) return 0;\n\treturn m_param[0]._idName.c_str();\n}\n"
  },
  {
    "path": "FECore/ParamString.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n#include <string>\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\nclass ParamRef\n{\npublic:\n\tint\t\t\t_index;\t\t// zero-based index of parameter (-1 if not available)\n\tint\t\t\t_id;\t\t// ID of parameter (-1 if not available)\n\tstd::string\t_name;\t\t// name of parameter\n\tstd::string\t_idName;\t// string ID of parameter\n\npublic:\n\tParamRef() : _id(-1), _index(-1) {}\n\tParamRef(const ParamRef& p)\n\t{\n\t\t_name = p._name;\n\t\t_id = p._id;\n\t\t_index = p._index;\n\t\t_idName = p._idName;\n\t}\n\n\tvoid operator = (const ParamRef& p)\n\t{\n\t\t_name = p._name;\n\t\t_id = p._id;\n\t\t_index = p._index;\n\t\t_idName = p._idName;\n\t}\n\n\tvoid clear()\n\t{\n\t\t_name.clear();\n\t\t_id = _index = -1;\n\t\t_idName.clear();\n\t}\n};\n\n//-----------------------------------------------------------------------------\n// helper class for retrieving parameters\nclass FECORE_API ParamString\n{\npublic:\n\t//! constructor\n\tParamString(const char* sz);\n\n\t//! copy constructor\n\tParamString(const ParamString& p);\n\n\t//! assignment operator\n\tvoid operator = (const ParamString& p);\n\n\t//! destructor\n\t~ParamString();\n\n\t//! number of refs in string\n\tint count() const;\n\n\t//! return a new string starting from the next component\n\tParamString next() const;\n\n\t//! return the last string\n\tParamString last() const;\n\n\t//! is the string valid\n\tbool isValid() const;\n\n\t//! clear the string\n\tvoid clear();\n\npublic:\n\n\t//! compare to a string\n\tbool operator == (const std::string& s) const;\n\n\t//! compare to a string\n\tbool operator != (const std::string& s) const;\n\n\t//! Get the ID (-1 if ID not a number)\n\tint ID() const;\n\n\t//! get the index (-1 if index not used)\n\tint Index() const;\n\n\t//! get the index name (null if not defined)\n\tconst char* IDString() const;\n\n\t//! get the zero-valued string \n\tconst char* c_str() const;\n\n\t//! return a string\n\tstd::string string() const;\n\nprivate:\n\tParamString() {}\n\nprivate:\n\tstd::vector<ParamRef>\tm_param;\n};\n"
  },
  {
    "path": "FECore/PointCurve.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"PointCurve.h\"\n#include \"BSpline.h\"\n#include \"AkimaSpline.h\"\n#include <assert.h>\n#include <algorithm>\n\n#ifndef min\n#define min(a,b) ((a)<(b)?(a):(b))\n#endif\n\nsize_t binarySearch(const std::vector<vec2d>& points, double x)\n{\n\tsize_t N = points.size();\n\tsize_t low = 0;\n\tsize_t high = N - 1;\n\t// Repeat until the pointers low and high meet each other\n\twhile (low <= high) {\n\t\tsize_t mid = low + (high - low) / 2;\n\n\t\tif (points[mid].x() <= x)\n\t\t\tlow = mid + 1;\n\t\telse\n\t\t\thigh = mid - 1;\n\t}\n\treturn low;\n}\n\nclass PointCurve::Imp\n{\npublic:\n\tint\t\tfnc;\t//!< interpolation function\n\tint\t\text;\t//!< extend mode\n\tstd::vector<vec2d>\tpoints;\n\tstd::vector<vec2d>\tdpts;\t\t// derivative points for C2SMOOTH option\n\tBSpline* spline;    //!< B-spline\n    BSpline* sderiv;    //!< B-spline for 1st derivative\n    AkimaSpline* akima; //!< Akima spline\n};\n\n//-----------------------------------------------------------------------------\nPointCurve::PointCurve() : im(new PointCurve::Imp)\n{\n\tim->fnc = LINEAR;\n\tim->ext = CONSTANT;\n\tim->spline = nullptr;\n    im->sderiv = nullptr;\n    im->akima = nullptr;\n}\n\n//-----------------------------------------------------------------------------\nPointCurve::PointCurve(const PointCurve& pc) : im(new PointCurve::Imp)\n{\n\tim->fnc = pc.im->fnc;\n\tim->ext = pc.im->ext;\n\tim->points = pc.im->points;\n\tim->spline = nullptr;\n    im->sderiv = nullptr;\n    im->akima = nullptr;\n\tUpdate();\n}\n\n//-----------------------------------------------------------------------------\nvoid PointCurve::operator = (const PointCurve& pc)\n{\n\tim->fnc = pc.im->fnc;\n\tim->ext = pc.im->ext;\n\tim->points = pc.im->points;\n    if (im->spline) delete im->spline;\n    if (im->sderiv) delete im->spline;\n    if (im->akima) delete im->akima;\n\tUpdate();\n}\n\n//-----------------------------------------------------------------------------\nPointCurve::~PointCurve()\n{\n\tif (im->spline) delete im->spline;\n    if (im->sderiv) delete im->sderiv;\n    if (im->akima) delete im->akima;\n}\n\n//-----------------------------------------------------------------------------\n//! adds a point to the point curve\nint PointCurve::Add(double x, double y)\n{\n\t// find the place to insert the data point\n\tint n = 0;\n\tint nsize = Points();\n\twhile ((n < nsize) && (im->points[n].x() < x)) ++n;\n\n\t// insert loadpoint\n\tim->points.insert(im->points.begin() + n, vec2d(x, y));\n\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\nint PointCurve::Add(const vec2d& p)\n{\n\treturn Add(p.x(), p.y());\n}\n\n//-----------------------------------------------------------------------------\n//! Clears the loadcurve data\nvoid PointCurve::Clear()\n{\n\tim->points.clear();\n\tif (im->spline) delete im->spline;\n\tim->spline = nullptr;\n    if (im->sderiv) delete im->sderiv;\n    im->sderiv = nullptr;\n    if (im->akima) delete im->akima;\n    im->akima = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! return nr of points\nint PointCurve::Points() const\n{\n\treturn (int) im->points.size();\n}\n\n//-----------------------------------------------------------------------------\n// Sets the time and data value of point i\n// This function assumes that the load curve data has already been created\n//\nvoid PointCurve::SetPoint(int i, double x, double y)\n{\n\tvec2d& pt = im->points[i];\n\tpt.x() = x;\n\tpt.y() = y;\n}\n\n//-----------------------------------------------------------------------------\nvoid PointCurve::SetPoint(int i, const vec2d& p)\n{\n\tim->points[i] = p;\n}\n\n//-----------------------------------------------------------------------------\nvoid PointCurve::SetPoints(const std::vector<vec2d>& points)\n{\n\tim->points = points;\n}\n\n//-----------------------------------------------------------------------------\n//! return all points\nstd::vector<vec2d> PointCurve::GetPoints() const\n{\n\treturn im->points;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the type of interpolation\nvoid PointCurve::SetInterpolator(int fnc) \n{ \n\tim->fnc = fnc; \n}\n\n//-----------------------------------------------------------------------------\n//! return current interpolator\nint PointCurve::GetInterpolator() const\n{\n\treturn im->fnc;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the extend mode\nvoid PointCurve::SetExtendMode(int mode) \n{ \n\tim->ext = mode; \n}\n\n//-----------------------------------------------------------------------------\n//! Get the extend mode\nint PointCurve::GetExtendMode() const\n{\n\treturn im->ext;\n}\n\n//-----------------------------------------------------------------------------\n//! get a point\nvec2d PointCurve::Point(int i) const\n{\n\treturn im->points[i];\n}\n\n//-----------------------------------------------------------------------------\nvoid PointCurve::Delete(int n)\n{\n\tif ((n >= 0) && (n < Points()) && (Points() > 2))\n\t{\n\t\tim->points.erase(im->points.begin() + n);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid PointCurve::Delete(const std::vector<int>& indexList)\n{\n\tstd::vector<int> tmp;\n\tint N = (int)indexList.size();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tint n = indexList[i];\n\t\tif ((n >= 0) && (n < Points())) tmp.push_back(n);\n\t}\n\n\tstd::sort(tmp.begin(), tmp.end());\n\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tint n = tmp[i];\n\t\tim->points.erase(im->points.begin() + n);\n\t\tfor (int j = i + 1; j < N; ++j) tmp[j]--;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid PointCurve::Scale(double s)\n{\n\tfor (int i = 0; i < Points(); ++i)\n\t{\n\t\tim->points[i].y() *= s;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// FUNCTION : LoadCurve::Value\n// Returns the load curve's value at time t.\n// When the time value is outside the time range, the return value\n// is that of the closest data value.\n//\n// TODO: maybe I should extrapolate the out-of-domain return values,\n// in stead of clamping them. I think that is what NIKE does. Or even\n// better let the user determine the out-of-range behaviour. Options could\n// be zero, clamp to range, linear extrapolation, ...\n//\n\n\ninline double lerp(double t, double t0, double f0, double t1, double f1)\n{\n\treturn f0 + (f1 - f0) * (t - t0) / (t1 - t0);\n}\n\ninline double qerp(double t, double t0, double f0, double t1, double f1, double t2, double f2)\n{\n\tdouble q0 = ((t2 - t) * (t1 - t)) / ((t2 - t0) * (t1 - t0));\n\tdouble q1 = ((t2 - t) * (t - t0)) / ((t2 - t1) * (t1 - t0));\n\tdouble q2 = ((t - t1) * (t - t0)) / ((t2 - t1) * (t2 - t0));\n\n\treturn f0 * q0 + f1 * q1 + f2 * q2;\n}\n\ndouble PointCurve::value(double time) const\n{\n\tstd::vector<vec2d>& points = im->points;\n\tint nsize = Points();\n\tif (nsize == 0) return 0;\n\tif (nsize == 1) return points[0].y();\n\n\tint N = nsize - 1;\n\n\tif (time == points[0].x()) return points[0].y();\n\tif (time == points[N].x()) return points[N].y();\n\n\tdouble tmax = points[N].x();\n\tdouble tmin = points[0].x();\n\n\tif (time < tmin) return ExtendValue(time);\n\tif (time > tmax) return ExtendValue(time);\n\n\tif (im->fnc == LINEAR)\n\t{\n        size_t n = binarySearch(points, time);\n\n\t\tdouble t0 = points[n - 1].x();\n\t\tdouble t1 = points[n].x();\n\n\t\tdouble f0 = points[n - 1].y();\n\t\tdouble f1 = points[n].y();\n\n\t\treturn lerp(time, t0, f0, t1, f1);\n\t}\n\telse if (im->fnc == STEP)\n\t{\n        size_t n = binarySearch(points, time);\n\n\t\treturn points[n].y();\n\t}\n\telse if (im->fnc == SMOOTH_STEP)\n\t{\n        size_t n = binarySearch(points, time);\n\n\t\tdouble t0 = points[n - 1].x();\n\t\tdouble t1 = points[n].x();\n\n\t\tdouble f0 = points[n - 1].y();\n\t\tdouble f1 = points[n].y();\n\n\t\tdouble w = (time - t0) / (t1 - t0);\n\t\tdouble w2 = w * w;\n\t\tdouble w3 = w * w2;\n\n\t\treturn f0 + (f1 - f0)*w3*(10.0 - 15.0*w + 6.0*w2);\n\t}\n    else if ((im->fnc == CSPLINE) || (im->fnc == CPOINTS) || (im->fnc == APPROX)) {\n        if (time > tmax) return im->spline->eval(tmax);\n        else if (time < tmin) return im->spline->eval(tmin);\n        else return im->spline->eval(time);\n    }\n    else if ((im->fnc == SMOOTH) || (im->fnc == C2SMOOTH))\n\t{\n\t\tif (nsize == 2)\n\t\t{\n\t\t\tdouble t0 = points[0].x();\n\t\t\tdouble t1 = points[1].x();\n\n\t\t\tdouble f0 = points[0].y();\n\t\t\tdouble f1 = points[1].y();\n\n\t\t\treturn lerp(time, t0, f0, t1, f1);\n\t\t}\n\t\telse if (nsize == 3)\n\t\t{\n\t\t\tdouble t0 = points[0].x();\n\t\t\tdouble t1 = points[1].x();\n\t\t\tdouble t2 = points[2].x();\n\n\t\t\tdouble f0 = points[0].y();\n\t\t\tdouble f1 = points[1].y();\n\t\t\tdouble f2 = points[2].y();\n\n\t\t\treturn qerp(time, t0, f0, t1, f1, t2, f2);\n\t\t}\n\t\telse\n\t\t{\n            size_t n = binarySearch(points, time);\n\n\t\t\tif (n == 1)\n\t\t\t{\n\t\t\t\tdouble t0 = points[0].x();\n\t\t\t\tdouble t1 = points[1].x();\n\t\t\t\tdouble t2 = points[2].x();\n\n\t\t\t\tdouble f0 = points[0].y();\n\t\t\t\tdouble f1 = points[1].y();\n\t\t\t\tdouble f2 = points[2].y();\n\n\t\t\t\treturn qerp(time, t0, f0, t1, f1, t2, f2);\n\t\t\t}\n\t\t\telse if (n == nsize - 1)\n\t\t\t{\n\t\t\t\tdouble t0 = points[n - 2].x();\n\t\t\t\tdouble t1 = points[n - 1].x();\n\t\t\t\tdouble t2 = points[n].x();\n\n\t\t\t\tdouble f0 = points[n - 2].y();\n\t\t\t\tdouble f1 = points[n - 1].y();\n\t\t\t\tdouble f2 = points[n].y();\n\n\t\t\t\treturn qerp(time, t0, f0, t1, f1, t2, f2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble t0 = points[n - 2].x();\n\t\t\t\tdouble t1 = points[n - 1].x();\n\t\t\t\tdouble t2 = points[n].x();\n\t\t\t\tdouble t3 = points[n + 1].x();\n\n\t\t\t\tdouble f0 = points[n - 2].y();\n\t\t\t\tdouble f1 = points[n - 1].y();\n\t\t\t\tdouble f2 = points[n].y();\n\t\t\t\tdouble f3 = points[n + 1].y();\n\n\t\t\t\tdouble q1 = qerp(time, t0, f0, t1, f1, t2, f2);\n\t\t\t\tdouble q2 = qerp(time, t1, f1, t2, f2, t3, f3);\n\n\t\t\t\treturn lerp(time, t1, q1, t2, q2);\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n//! This function determines the value of the point curve outside of its domain\n//!\ndouble PointCurve::ExtendValue(double t) const\n{\n\tint nsize = Points();\n\tint N = nsize - 1;\n\n\tstd::vector<vec2d>& points = im->points;\n\n\tif (nsize == 0) return 0;\n\tif (nsize == 1) return points[0].y();\n\n\tdouble Dt = (points[N].x() - points[0].x());\n\tdouble dt = 0.001 * Dt;\n\tif (dt == 0) return points[0].y();\n\n\tswitch (im->ext)\n\t{\n\tcase CONSTANT:\n\t\tif (t < points[0].x()) return points[0].y();\n\t\tif (t > points[N].x()) return points[N].y();\n\t\tbreak;\n\tcase EXTRAPOLATE:\n\t\tswitch (im->fnc)\n\t\t{\n\t\tcase STEP:\n\t\tcase SMOOTH_STEP:\n\t\t{\n\t\t\tif (t < points[0].x()) return points[0].y();\n\t\t\tif (t > points[N].x()) return points[N].y();\n\t\t}\n\t\tbreak;\n\t\tcase LINEAR:\n\t\t{\n\t\t\tif (t < points[0].x()) return lerp(t, points[0].x(), points[0].y(), points[1].x(), points[1].y());\n\t\t\telse return lerp(t, points[N - 1].x(), points[N - 1].y(), points[N].x(), points[N].y());\n\t\t}\n\t\tbreak;\n\t\tcase SMOOTH:\n        case CSPLINE:\n        case CPOINTS:\n        case APPROX:\n        case C2SMOOTH:\n\t\t{\n\t\t\tif (t < points[0].x()) return lerp(t, points[0].x(), points[0].y(), points[0].x() + dt, value(points[0].x() + dt));\n\t\t\telse return lerp(t, points[N].x() - dt, value(points[N].x() - dt), points[N].x(), points[N].y());\n\t\t}\n\t\treturn 0;\n\t\t}\n\t\tbreak;\n\tcase REPEAT:\n\t{\n\t\tif (t < points[0].x()) while (t < points[0].x()) t += Dt;\n\t\telse while (t > points[N].x()) t -= Dt;\n\t\treturn value(t);\n\t}\n\tbreak;\n\tcase REPEAT_OFFSET:\n\t{\n\t\tint n = 0;\n\t\tif (t < points[0].x()) while (t < points[0].x()) { t += Dt; --n; }\n\t\telse while (t > points[N].x()) { t -= Dt; ++n; }\n\t\tdouble off = n * (points[N].y() - points[0].y());\n\t\treturn value(t) + off;\n\t}\n\tbreak;\n\t}\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// This function finds the index of the first load point \n// for which the time is greater than t.\n// It returns -1 if t is larger than the last time value\n//\n\nint PointCurve::FindPoint(double t, double& tval, int startIndex)\n{\n\tswitch (im->ext)\n\t{\n\tcase REPEAT:\n\tcase REPEAT_OFFSET:\n\t{\n\t\tdouble toff = 0.0;\n\t\twhile (1)\n\t\t{\n\t\t\tdouble ti = 0;\n\t\t\tfor (int i = 0; i < Points(); ++i)\n\t\t\t{\n\t\t\t\tti = im->points[i].x() + toff;\n\t\t\t\tif (ti > t) { tval = ti; return i; }\n\t\t\t}\n\t\t\ttoff = ti;\n\t\t}\n\t}\n\tbreak;\n\tdefault:\n\t\tif (startIndex < 0) startIndex = 0;\n\t\tif (startIndex >= Points()) return -1;\n\t\tfor (int i = startIndex; i < Points(); ++i)\n\t\t{\n\t\t\tdouble ti = im->points[i].x();\n\t\t\tif (ti > t) { tval = ti; return i; }\n\t\t}\n\t}\n\treturn -1;\n}\n\n//-----------------------------------------------------------------------------\n\nbool PointCurve::HasPoint(double t) const\n{\n\tconst double tmax = im->points[Points() - 1].x();\n\tconst double eps = 1e-7 * tmax;\n\n\tfor (int i = 0; i < Points(); ++i) if (fabs(im->points[i].x() - t) < eps) return true;\n\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\ndouble PointCurve::derive(double time) const\n{\n\tint N = (int)im->points.size();\n\tif (N <= 1) return 0;\n\tdouble tmax = im->points[N - 1].x();\n\tdouble tmin = im->points[0].x();\n\n    double Dt = im->points[N - 1].x() - im->points[0].x();\n    double dt = Dt * 1e-9;\n    double D = 0;\n    \n    if (time >= tmax) {\n        // use backward difference\n        double t2 = time - 2 * dt;\n        double t1 = time - dt;\n        double t0 = time;\n        \n        double v2 = value(t2);\n        double v1 = value(t1);\n        double v0 = value(t0);\n        \n        return (v2 - 4 * v1 + 3 * v0) / (2 * dt);\n    }\n    else if (time < tmin) {\n        // use forward difference\n        double t0 = time;\n        double t1 = time + dt;\n        double t2 = time + 2 * dt;\n        \n        double v0 = value(t0);\n        double v1 = value(t1);\n        double v2 = value(t2);\n        \n        return (-v2 + 4 * v1 - 3 * v0) / (2 * dt);\n    }\n    \n    if ((im->fnc == CSPLINE) || (im->fnc == CPOINTS) || (im->fnc == APPROX)) {\n        return im->spline->eval_deriv(time);\n    }\n    else if (im->fnc == C2SMOOTH)\n    {\n        if (N == 3)\n        {\n            double t0 = im->dpts[0].x();\n            double t1 = im->dpts[1].x();\n            double t2 = im->dpts[2].x();\n            \n            double f0 = im->dpts[0].y();\n            double f1 = im->dpts[1].y();\n            double f2 = im->dpts[2].y();\n            \n            return qerp(time, t0, f0, t1, f1, t2, f2);\n        }\n        else\n        {\n            size_t n = binarySearch(im->dpts, time);\n            \n            if (n == 1)\n            {\n                double t0 = im->dpts[0].x();\n                double t1 = im->dpts[1].x();\n                double t2 = im->dpts[2].x();\n                \n                double f0 = im->dpts[0].y();\n                double f1 = im->dpts[1].y();\n                double f2 = im->dpts[2].y();\n                \n                return qerp(time, t0, f0, t1, f1, t2, f2);\n            }\n            else if (n == N - 1)\n            {\n                double t0 = im->dpts[n - 2].x();\n                double t1 = im->dpts[n - 1].x();\n                double t2 = im->dpts[n].x();\n                \n                double f0 = im->dpts[n - 2].y();\n                double f1 = im->dpts[n - 1].y();\n                double f2 = im->dpts[n].y();\n                \n                return qerp(time, t0, f0, t1, f1, t2, f2);\n            }\n            else\n            {\n                double t0 = im->dpts[n - 2].x();\n                double t1 = im->dpts[n - 1].x();\n                double t2 = im->dpts[n].x();\n                double t3 = im->dpts[n + 1].x();\n                \n                double f0 = im->dpts[n - 2].y();\n                double f1 = im->dpts[n - 1].y();\n                double f2 = im->dpts[n].y();\n                double f3 = im->dpts[n + 1].y();\n                \n                double q1 = qerp(time, t0, f0, t1, f1, t2, f2);\n                double q2 = qerp(time, t1, f1, t2, f2, t3, f3);\n                \n                return lerp(time, t1, q1, t2, q2);\n            }\n        }\n    }\n    else {\n        // use central difference\n        double t0 = time - dt;\n        double t1 = time + dt;\n        \n        double v1 = value(t1);\n        double v0 = value(t0);\n        \n        return (v1 - v0) / (2 * dt);\n    }\n}\n\n//-----------------------------------------------------------------------------\ndouble PointCurve::deriv2(double time) const\n{\n\tint N = (int)im->points.size();\n\tif (N <= 1) return 0;\n\tdouble tmax = im->points[N - 1].x();\n\tdouble tmin = im->points[0].x();\n\n    double Dt = im->points[N - 1].x() - im->points[0].x();\n    double dt = Dt * 1e-3;\n    \n    if (time > tmax) {\n        // use backward difference\n        double t2 = time - 2 * dt;\n        double t1 = time - dt;\n        double t0 = time;\n        \n        double v2 = value(t2);\n        double v1 = value(t1);\n        double v0 = value(t0);\n        \n        return (v2 - 2 * v1 + v0) / (dt * dt);\n    }\n    else if (time < tmin) {\n        // use forward difference\n        double t0 = time;\n        double t1 = time + dt;\n        double t2 = time + 2 * dt;\n        \n        double v0 = value(t0);\n        double v1 = value(t1);\n        double v2 = value(t2);\n        \n        return (v2 - 2 * v1 + v0) / (dt * dt);\n    }\n    \n    if ((im->fnc == CSPLINE) || (im->fnc == CPOINTS) || (im->fnc == APPROX)) {\n        return im->spline->eval_deriv2(time);\n    }\n    else {\n        // use central difference\n        double t0 = time - dt;\n        double t1 = time;\n        double t2 = time + dt;\n        \n        double v0 = value(t0);\n        double v1 = value(t1);\n        double v2 = value(t2);\n        \n        return (v2 - 2 * v1 + v0) / (dt * dt);\n    }\n}\n\ndouble PointCurve::integrate(double a, double b) const\n{\n\tif (a == b) return 0;\n\n\t// Swap a and b if a is greater than b\n\t// negate the answer at the end.\n\tint neg = 1;\n\tif (a > b)\n\t{\n\t\tdouble temp = a;\n\t\ta = b;\n\t\tb = temp;\n\t\tneg = -1;\n\t}\n\n\tdouble integral = 0.0;\n\n\t// if both points are outside the bounds of the load curve \n\t// just do a single trapezoid\n\t// TODO: add cases for  repeat and repeat offset curves\n\tstd::vector<vec2d>& points = im->points;\n\tif (a > points[Points() - 1].x() || b < points[0].x())\n\t{\n\t\tintegral = (b - a) * (value(a) + value(b)) / 2;\n\t}\n\telse\n\t{\n\t\t// Find index of first point larger than a\n\t\tint start = -1;\n\n\t\tfor (int index = 0; index < Points(); index++)\n\t\t{\n\t\t\tif (points[index].x() > a)\n\t\t\t{\n\t\t\t\tstart = index;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Do trapezoid rule from a to next point\n\t\tintegral += (points[start].x() - value(a)) * ((points[start].y() + value(a)) / 2);\n\n\n\t\t// Loop over points between a and b and do trapezoid rule for each interval\n\t\tint index;\n\t\tfor (index = start; index < Points() - 1; index++)\n\t\t{\n\t\t\t// Stop before overshooting b\n\t\t\tif (points[index + 1].x() >= b) break;\n\t\t\tintegral += (points[index + 1].x() - points[index].x()) * ((points[index + 1].y() + points[index].y()) * 0.5);\n\t\t}\n\n\t\t//Do trapezoid rule from most recent point to b\n\t\tintegral += (value(b) - points[index].x()) * ((value(b) + points[index].y()) * 0.5);\n\t}\n\n\treturn integral * neg;\n}\n\nbool PointCurve::Update()\n{\n\tbool bvalid = true;\n\n\tif ((im->fnc > SMOOTH) && (im->fnc != SMOOTH_STEP))\n\t{\n\t\tconst int N = Points();\n\n\t\tswitch (im->fnc) {\n\t\tcase CSPLINE:\n\t\t{\n            // initialize B-spline\n            if (im->spline) delete im->spline;\n            im->spline = new BSpline();\n            int korder = min(N, 4);\n\t\t\tif (!im->spline->init_interpolation(korder, im->points)) bvalid = false;\n\t\t}\n\t\tbreak;\n\t\tcase CPOINTS:\n\t\t{\n            // initialize B-spline\n            if (im->spline) delete im->spline;\n            im->spline = new BSpline();\n\t\t\tint korder = min(N, 4);\n\t\t\tif (!im->spline->init(korder, im->points)) bvalid = false;\n\t\t}\n\t\tbreak;\n\t\tcase APPROX:\n\t\t{\n            // initialize B-spline\n            if (im->spline) delete im->spline;\n            im->spline = new BSpline();\n\t\t\tint korder = min(N / 2 + 1, 4);\n\t\t\tif (!im->spline->init_approximation(korder, N / 2 + 1, im->points)) bvalid = false;\n\t\t}\n        break;\n        case C2SMOOTH:\n        {\n            if (N < 3) {\n                bvalid = false;\n                break;\n            }\n            \n            // evaluate control points of spline derivative using finite difference scheme (non-uniform)\n\t\t\tstd::vector<vec2d>& dpts = im->dpts;\n\t\t\tdpts = im->points;\n            double d01, d10, d11, d20,d21, d02, d12;\n            // forward difference at first point\n            d10 = dpts[1].x() - dpts[0].x();\n            d20 = dpts[2].x() - dpts[0].x();\n            d21 = dpts[2].x() - dpts[1].x();\n            dpts[0].y() = (im->points[0].y()*(pow(d10,2)-pow(d20,2)) + im->points[1].y()*pow(d20,2) - im->points[2].y()*pow(d10,2))/(d10*d20*d21);\n            // backward difference at last point\n            int n = N-1;\n            d02 = dpts[n].x() - dpts[n-2].x();\n            d01 = dpts[n].x() - dpts[n-1].x();\n            d12 = dpts[n-1].x() - dpts[n-2].x();\n            dpts[n].y() = (im->points[n].y()*(pow(d02,2)-pow(d01,2)) - im->points[n-1].y()*pow(d02,2) + im->points[n-2].y()*pow(d01,2))/(d01*d02*d12);\n            // central difference at intermediate points\n            for (int i=1; i<n; ++i) {\n                d10 = dpts[i+1].x() - dpts[i].x();\n                d01 = dpts[i].x() - dpts[i-1].x();\n                d11 = dpts[i+1].x() - dpts[i-1].x();\n                dpts[i].y() = (im->points[i].y()*(pow(d10,2)-pow(d01,2)) - im->points[i-1].y()*pow(d10,2) + im->points[i+1].y()*pow(d01,2))/(d01*d10*d11);\n            }\n        }\n        break;\n\t\tdefault:\n\t\t\tbvalid = false;\n\t\t\tassert(false);\n\t\t}\n\n        if (bvalid == false) {\n            if (im->spline) delete im->spline; im->spline = nullptr;\n            if (im->akima) delete im->akima; im->akima = nullptr;\n        }\n\t}\n\treturn bvalid;\n}\n"
  },
  {
    "path": "FECore/PointCurve.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n#include \"vec2d.h\"\n#include <vector>\n\nclass FECORE_API PointCurve\n{\n    class Imp;\n    \npublic:\n    //! Interpolation functions\n    enum INTFUNC { LINEAR = 0, STEP = 1, SMOOTH = 2, CSPLINE = 3, CPOINTS = 4, APPROX = 5, SMOOTH_STEP = 6, C2SMOOTH = 7 };\n    \n    //! Extend mode\n    enum EXTMODE { CONSTANT, EXTRAPOLATE, REPEAT, REPEAT_OFFSET };\n    \npublic:\n    //! default constructor\n    PointCurve();\n    \n    //! copy constructor\n    PointCurve(const PointCurve& pc);\n    \n    //! assignment operator\n    void operator = (const PointCurve& pc);\n    \n    //! destructor\n    ~PointCurve();\n    \n    //! call this to update internal data structures\n    bool Update();\n    \n    //! adds a point to the point curve\n    int Add(double x, double y);\n    \n    //! adds a point to the point curve\n    int Add(const vec2d& p);\n    \n    //! Clears the loadcurve data\n    void Clear();\n    \n    //! set the x and y value of point i\n    void SetPoint(int i, double x, double y);\n    void SetPoint(int i, const vec2d& p);\n    \n    //! set all points at once\n    void SetPoints(const std::vector<vec2d>& points);\n    \n    //! return all points\n    std::vector<vec2d> GetPoints() const;\n    \n    //! remove a point\n    void Delete(int n);\n    \n    //! remove several points at once\n    void Delete(const std::vector<int>& indexList);\n    \n    //! Set the type of interpolation\n    void SetInterpolator(int fnc);\n    \n    //! return current interpolator\n    int GetInterpolator() const;\n    \n    //! Set the extend mode\n    void SetExtendMode(int mode);\n    \n    //! Get the extend mode\n    int GetExtendMode() const;\n    \n    //! get a point\n    vec2d Point(int i) const;\n    \n    //! finds closest load point\n    int FindPoint(double t, double& tval, int startIndex = 0);\n    \n    //! return nr of points\n    int Points() const;\n    \n    //! see if there is a point at time t\n    bool HasPoint(double t) const;\n    \npublic: // operations\n    \n    // scale all y points by s\n    void Scale(double s);\n    \npublic:\n    //! returns the value of the load curve at time\n    double value(double x) const;\n    \n    //! returns the derivative value at time\n    double derive(double x) const;\n    \n    //! returns the second derivative value at time\n    double deriv2(double x) const;\n    \n    //! returns the definite integral value between a and b\n    double integrate(double a, double b) const;\n    \nprotected:\n    double ExtendValue(double t) const;\n    \nprivate:\n    Imp* im;\n};\n"
  },
  {
    "path": "FECore/Preconditioner.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"Preconditioner.h\"\n\n//=================================================================================================\nDiagonalPreconditioner::DiagonalPreconditioner(FEModel* fem) : Preconditioner(fem)\n{\n\tm_bsqr = false;\n}\n\n// take square root of diagonal entries\nvoid DiagonalPreconditioner::CalculateSquareRoot(bool b)\n{\n\tm_bsqr = b;\n}\n\n// create a preconditioner for a sparse matrix\nbool DiagonalPreconditioner::Factor()\n{\n\tSparseMatrix* A = GetSparseMatrix();\n\tif (A == nullptr) return false;\n\n\tint N = A->Rows();\n\tif (A->Columns() != N) return false;\n\n\tm_D.resize(N);\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tdouble dii = A->diag(i);\n\t\tif (dii == 0.0) return false;\n\t\tif (m_bsqr)\n\t\t{\n\t\t\tif (dii < 0) return false;\n\t\t\tdii = sqrt(dii);\n\t\t}\n\t\tm_D[i] = 1.0 / dii;\n\t}\n\n\treturn true;\n}\n\n// apply to vector P x = y\nbool DiagonalPreconditioner::BackSolve(double* x, double* y)\n{\n\tint N = (int)m_D.size();\n\n#pragma omp parallel for\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tx[i] = y[i]*m_D[i];\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/Preconditioner.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"SparseMatrix.h\"\n#include \"LinearSolver.h\"\n\n//-----------------------------------------------------------------------------\nclass CRSSparseMatrix;\nclass CompactMatrix;\n\n//-----------------------------------------------------------------------------\n// Preconditioner class is a special type of linear solver that should only be\n// used as a preconditioner for iterative linear solvers\nclass FECORE_API Preconditioner : public LinearSolver\n{\n\tFECORE_BASE_CLASS(Preconditioner)\n\npublic: \n\tPreconditioner(FEModel* fem) : LinearSolver(fem), m_A(nullptr) {}\n\n\tbool Create(SparseMatrix* A)\n\t{\n\t\tif (SetSparseMatrix(A) == false) return false;\n\t\tif (PreProcess() == false) return false;\n\t\tif (Factor() == false) return false;\n\t\treturn true;\n\t}\n\n\tbool SetSparseMatrix(SparseMatrix* M) override { m_A = M; return true; }\n\n\tSparseMatrix* GetSparseMatrix() { return m_A; }\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override { return nullptr; }\n\nprivate:\n\tSparseMatrix*\tm_A;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API DiagonalPreconditioner : public Preconditioner\n{\npublic:\n\tDiagonalPreconditioner(FEModel* fem);\n\n\t// take square root of diagonal entries\n\tvoid CalculateSquareRoot(bool b);\n\npublic:\n\t// create a preconditioner for a sparse matrix\n\tbool Factor() override;\n\n\t// apply to vector P x = y\n\tbool BackSolve(double* x, double* y) override;\n\nprivate:\n\tvector<double>\tm_D;\n\n\tbool\tm_bsqr;\t\t// Take square root\n};\n"
  },
  {
    "path": "FECore/Quadric.cpp",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio-Studio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"Quadric.h\"\n#include \"matrix.h\"\n#include <math.h>\nusing namespace std;\n#ifndef M_PI\n#define M_PI 3.141592653589793238462643\n#endif\n\n//-------------------------------------------------------------------------------\n// constructor\nQuadric::Quadric(Quadric* q)\n{\n    for (int i=0; i<10; ++i) m_c[i] = q->m_c[i];\n    m_p = q->m_p;\n}\n\n//-------------------------------------------------------------------------------\n// copy constructor\nQuadric::Quadric(const Quadric& q)\n{\n    for (int i=0; i<10; ++i) m_c[i] = q.m_c[i];\n    m_p = q.m_p;\n}\n\n//-------------------------------------------------------------------------------\n// destructor\nQuadric::~Quadric()\n{\n    for (int i=0; i<10; ++i) m_c[i] = 0;\n    m_p.clear();\n}\n\n//--------------------------------------------------------------------------------------\nbool Quadric::GetQuadricCoeficients()\n{\n    // get number of points in point cloud\n    int N = (int)m_p.size();\n\n    matrix A(10,10);\n    matrix d(1,10);\n    \n    //Populate D\n    A.zero();\n    for (int j = 0; j < N; ++j)\n    {\n        vec3d p = m_p[j];\n        d(0,0) = p.x*p.x;\n        d(0,1) = p.y*p.y;\n        d(0,2) = p.z*p.z;\n        d(0,3) = p.y*p.z;\n        d(0,4) = p.x*p.z;\n        d(0,5) = p.x*p.y;\n        d(0,6) = p.x;\n        d(0,7) = p.y;\n        d(0,8) = p.z;\n        d(0,9) = 1;\n        matrix D(10,10);\n        D = d.transpose()*d;\n        A += D;\n    }\n\n    // find eigenvalues and eigenvectors of A\n    vector<double> Aval;\n    matrix Avec(10,10);\n    bool good = A.eigen_vectors(Avec, Aval);\n\n    if (good) {\n        // find smallest eigenvalue\n        int imin=0;\n        for (int i=1; i<10; ++i)\n            if (fabs(Aval[i]) < fabs(Aval[imin])) imin = i;\n            //if (Aval[i] < Aval[imin]) imin = i;\n        \n        // store the coefficients of the quadric surface\n        for (int j=0; j<10; ++j)\n            m_c[j] = Avec(j,imin);\n        \n        return true;\n    }\n    \n    return false;\n}\n\n//--------------------------------------------------------------------------------------\n// Evaluate the surface normal at point p\nvec3d Quadric::SurfaceNormal(const vec3d p)\n{\n    // extract quadric surface coefficients\n    // F(x,y,z) = a x^2 + b y^2 + c z^2 + e y z + f z x + g x y + l x + m y + n z + d = 0\n    double a = m_c[0];\n    double b = m_c[1];\n    double c = m_c[2];\n    double e = m_c[3];\n    double f = m_c[4];\n    double g = m_c[5];\n    double l = m_c[6];\n    double m = m_c[7];\n    double n = m_c[8];\n    \n    // evaluate first derivatives\n    double Fx = 2*a*p.x + f*p.z + g*p.y + l;\n    double Fy = 2*b*p.y + e*p.z + g*p.x + m;\n    double Fz = 2*c*p.z + e*p.y + f*p.x + n;\n    \n    vec3d xn(Fx,Fy,Fz);\n    xn.unit();\n    \n    return xn;\n}\n\n//--------------------------------------------------------------------------------------\nvoid Quadric::SurfaceCurvature(const vec3d p, const vec3d pn, vec2d& kappa, vec3d* v)\n{\n    double eps = 1e-9;\n    \n    // find magnitude of quadric gradient components\n    // to determine if we need to do permutation of axes\n    double f1 = fabs(2*m_c[0]*p.x + 2*m_c[4]*p.z + 2*m_c[5]*p.y + 2*m_c[6]);\n    double f2 = fabs(2*m_c[1]*p.y + 2*m_c[3]*p.z + 2*m_c[5]*p.x + 2*m_c[7]);\n    double f3 = fabs(2*m_c[2]*p.z + 2*m_c[3]*p.y + 2*m_c[4]*p.x + 2*m_c[8]);\n    \n    // pick direction with maximum gradient component magnitude\n    int e1 = 0, e2 = 1, e3 = 2;\n    double vmax = fmax(fmax(f1,f2),f3);\n    if (vmax == f1) { e3 = 0; e1 = 1; e2 = 2; }\n    else if (vmax == f2) { e3 = 1; e1 = 2; e2 = 0; }\n    \n    // extract quadric surface coefficients (and clean up roundoff errors)\n    // F(x,y,z) = a x^2 + b y^2 + c z^2 + e y z + f z x + g x y + l x + m y + n z + d = 0\n    double a = (fabs(m_c[e1]) > eps) ? m_c[e1] : 0;\n    double b = (fabs(m_c[e2]) > eps) ? m_c[e2] : 0;\n    double c = (fabs(m_c[e3]) > eps) ? m_c[e3] : 0;\n    double e = (fabs(m_c[e1+3]) > eps) ? m_c[e1+3] : 0;\n    double f = (fabs(m_c[e2+3]) > eps) ? m_c[e2+3] : 0;\n    double g = (fabs(m_c[e3+3]) > eps) ? m_c[e3+3] : 0;\n    double l = (fabs(m_c[e1+6]) > eps) ? m_c[e1+6] : 0;\n    double m = (fabs(m_c[e2+6]) > eps) ? m_c[e2+6] : 0;\n    double n = (fabs(m_c[e3+6]) > eps) ? m_c[e3+6] : 0;\n    \n    // evaluate quadric surface derivatives and normal\n    double x = p(e1), y = p(e2), z = p(e3);\n    double den = n + f*x + e*y + 2*c*z;\n    double zx = -(l + 2*a*x + g*y + f*z)/den;\n    double zy = -(m + g*x + 2*b*y + e*z)/den;\n    double zxx = -2*(a + (f + c*zx)*zx)/den;\n    double zyy = -2*(b + (e + c*zy)*zy)/den;\n    double zxy = -(g + e*zx + f*zy + 2*c*zx*zy)/den;\n    vec3d xu, xv;\n    xu(e1) = 1; xu(e2) = 0; xu(e3) = zx;\n    xv(e1) = 0; xv(e2) = 1; xv(e3) = zy;\n    vec3d xuu, xvv, xuv;\n    xuu(e1) = 0; xuu(e2) = 0; xuu(e3) = zxx;\n    xvv(e1) = 0; xvv(e2) = 0; xvv(e3) = zyy;\n    xuv(e1) = 0; xuv(e2) = 0; xuv(e3) = zxy;\n    vec3d xn = (xu ^ xv).normalized();\n    \n    // get coefficients of fundamental forms\n    double E = xu*xu, F = xu*xv, G = xv*xv;\n    double L = xuu*xn, M = xuv*xn, N = xvv*xn;\n\n    // evaluate mean and gaussian curvatures\n    double tmp = E*G - F*F;\n    // gaussian\n    double kg = (L*N - M*M)/tmp;\n    // mean\n    double km = (2*F*M - E*N - G*L)/2./tmp;\n    // evaluate principal curvatures\n    double d1 = sqrt(km*km - kg);\n    // max curvature\n    double kmax = km + d1;\n    double kmin = km - d1;\n\n    // evaluate principal directions of curvature\n    double a2 = F*N - G*M, b2 = E*N - G*L, c2 = E*M - F*L;\n    double d2 = b2*b2 - 4*a2*c2; if (fabs(d2) < 0) d2 = 0;\n    d2 = sqrt(d2);  // d2=0 represents an umbilical point\n    double thmax = atan2(-b2 + d2, 2*a2);\n    double thmin = (d2 != 0) ? atan2(-b2 - d2, 2*a2) : thmax + M_PI/2;\n    vec3d xmax = xu*cos(thmax) + xv*sin(thmax); xmax.unit();\n    vec3d xmin = xu*cos(thmin) + xv*sin(thmin); xmin.unit();\n    \n    // check quadric normal versus face normal\n    if (xn*pn >= 0) {\n        kappa.x() = kmax; kappa.y() = kmin;\n        v[0] = xmax; v[1] = xmin;\n    }\n    else {\n        kappa.x() = kmin; kappa.y() = kmax;\n        v[0] = xmin; v[1] = xmax;\n    }\n    // fix handedness if neeeded\n    if ((v[0]^v[1])*pn < 0) v[0] = -v[0];\n}\n\n//--------------------------------------------------------------------------------------\n// Find ray-quadric surface intersections x: p is point on ray, n is normal along ray\n// There are three possible solutions: 0 roots, 1 root, and 2 roots\n// When 2 roots are found, sort results from closest to farthest\nvoid Quadric::RayQuadricIntersection(const vec3d p, const vec3d n, vector<vec3d>* x, vector<double>* t)\n{\n    double a = n.x*n.x*m_c[0]+n.y*n.y*m_c[1]+n.z*n.z*m_c[2]+n.y*n.z*m_c[3]+n.x*n.z*m_c[4]+n.x*n.y*m_c[5];\n    double b = n.x*(2*p.x*m_c[0]+p.z*m_c[4]+p.y*m_c[5]+m_c[6])\n    +n.y*(2*p.y*m_c[1]+p.z*m_c[3]+p.x*m_c[5]+m_c[7])\n    +n.z*(2*p.z*m_c[2]+p.y*m_c[3]+p.x*m_c[4]+m_c[8]);\n    double c = p.x*p.x*m_c[0]+p.y*p.y*m_c[1]+p.z*p.z*m_c[2]\n    +p.x*(p.z*m_c[4]+p.y*m_c[5]+m_c[6])\n    +p.y*(p.z*m_c[3]+m_c[7])+p.z*m_c[8]+m_c[9];\n    \n    x->clear();\n    t->clear();\n    double d = b*b - 4*a*c;\n    if (d < 0) return;\n    else if ((a == 0) && (b != 0)) {\n        double t1 = -c/b;\n        t->push_back(t1);\n        x->push_back(p + n*t1);\n        return;\n    }\n    else if ((d == 0) && (a != 0)) {\n        double t1 = -b/(2*a);\n        t->push_back(t1);\n        x->push_back(p + n*t1);\n        return;\n    }\n    else if (a != 0)  {\n        d = sqrt(d);\n        double t1 = (-b - d)/(2*a);\n        double t2 = (-b + d)/(2*a);\n        if (fabs(t1) < fabs(t2)) {\n            t->push_back(t1);\n            t->push_back(t2);\n            x->push_back(p + n*t1);\n            x->push_back(p + n*t2);\n        }\n        else {\n            t->push_back(t2);\n            t->push_back(t1);\n            x->push_back(p + n*t2);\n            x->push_back(p + n*t1);\n        }\n    }\n}\n\n//--------------------------------------------------------------------------------------\n// This routine finds a closest point approximation (not the exact solution)\nvec3d Quadric::ClosestPoint(const vec3d p)\n{\n    vector<vec3d> xsol;\n    vector<double> tsol;\n\n    vec3d n1 = vec3d(1,0,0);\n    vec3d n2 = vec3d(0,1,0);\n    vec3d n3 = vec3d(0,0,1);\n    vector<vec3d> x1, x2, x3;\n    vector<double> t1, t2, t3;\n    RayQuadricIntersection(p, n1, &x1, &t1);\n    RayQuadricIntersection(p, n2, &x2, &t2);\n    RayQuadricIntersection(p, n3, &x3, &t3);\n    if (t1.size() > 0) {\n        xsol.push_back(x1[0]);\n        tsol.push_back(t1[0]);\n    }\n    if (t2.size() > 0) {\n        xsol.push_back(x2[0]);\n        tsol.push_back(t2[0]);\n    }\n    if (t3.size() > 0) {\n        xsol.push_back(x3[0]);\n        tsol.push_back(t3[0]);\n    }\n\n    int N = (int)tsol.size();\n    if (N > 0) {\n        int imin = 0;\n        double tmin = fabs(tsol[imin]);\n        for (int i = 1; i<N; ++i) {\n            if (fabs(tsol[i]) < tmin) {\n                imin = i;\n                tmin = fabs(tsol[i]);\n            }\n        }\n        return xsol[imin];\n    }\n    return p;\n}\n\n//--------------------------------------------------------------------------------------\n// This routine finds a closest point approximation (use norm of face)\nvec3d Quadric::ClosestPoint(const vec3d p, const vec3d norm)\n{\n    vec3d xsol;\n    double tsol;\n    \n    vector<vec3d> x;\n    vector<double> t;\n    RayQuadricIntersection(p, norm, &x, &t);\n\n    if (t.size() > 0) {\n        xsol = x[0];\n        tsol = t[0];\n        \n        return xsol;\n    }\n    else {\n        ClosestPoint(p);\n    }\n    return p;\n}\n"
  },
  {
    "path": "FECore/Quadric.h",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio-Studio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <vector>\n#include \"vec3d.h\"\n#include \"fecore_api.h\"\n\nclass FECORE_API Quadric\n{\npublic:\n    Quadric() {}\n    Quadric(std::vector<vec3d>& p) { m_p = p; }\n    Quadric(Quadric* q);\n    Quadric(const Quadric& q);\n    ~Quadric();\n    \npublic:\n    // assign a point cloud to this bivariate spline object\n    void SetPoints(std::vector<vec3d>& p) { m_p = p; }\n    \n    // fit the point cloud to get quadric surface coefficients\n    bool GetQuadricCoeficients();\n    \n    // Evaluate the surface normal at point p\n    vec3d SurfaceNormal(const vec3d p);\n    \n    // Evaluate the surface principal curvatures kappa and directions v at point p\n    void SurfaceCurvature(const vec3d p, const vec3d n, vec2d& kappa, vec3d* v);\n    \n    // Find ray-quadric surface intersections x: p is point on ray, n is normal along ray\n    void RayQuadricIntersection(const vec3d p, const vec3d n, std::vector<vec3d>* x, std::vector<double>* t = nullptr);\n    \n    // Find the point on the quadric closest to the point p\n    vec3d ClosestPoint(const vec3d p);\n    \n    // Find the point on the quadric closest to the point p\n    vec3d ClosestPoint(const vec3d p, const vec3d norm);\n\npublic:\n    double          m_c[10];    // quadric surface coefficients\n    std::vector<vec3d>  m_p;    // point coordinates\n};\n"
  },
  {
    "path": "FECore/QuadricFit.cpp",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio-Studio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n// onchoidFit.cpp: implementation of the QuadricFit class.\n//\n//////////////////////////////////////////////////////////////////////\n#include \"stdafx.h\"\n#include \"QuadricFit.h\"\n#include <stdio.h>\n\nusing std::swap;\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\n//-------------------------------------------------------------------------------\n// constructor\nQuadricFit::QuadricFit()\n{\n    m_rc = m_c2  = vec3d(0,0,0);\n    m_c = 0;\n    m_ax[0] = m_ax[1] = m_ax [2] = vec3d(0,0,0);\n    m_quad = new Quadric();\n    m_eps = 1e-3;\n}\n\n//-------------------------------------------------------------------------------\n// copy constructor\nQuadricFit::QuadricFit(const QuadricFit& qf)\n{\n    m_rc = qf.m_rc;\n    m_c2 = qf.m_c2;\n    m_c = qf.m_c;\n    m_ax[0] = qf.m_ax[0];\n    m_ax[1] = qf.m_ax[1];\n    m_ax[2] = qf.m_ax[2];\n    m_quad = new Quadric(*qf.m_quad);\n    m_eps = qf.m_eps;\n}\n\n//-------------------------------------------------------------------------------\n// destructor\nQuadricFit::~QuadricFit()\n{\n    \n}\n\n//-------------------------------------------------------------------------------\nvoid QuadricFit::GetOrientation()\n{\n    \n    mat3ds C(m_quad->m_c[0], m_quad->m_c[1], m_quad->m_c[2],\n             m_quad->m_c[5], m_quad->m_c[3], m_quad->m_c[4]);\n    vec3d v = vec3d(m_quad->m_c[6], m_quad->m_c[7], m_quad->m_c[8]);\n    double c = m_quad->m_c[9];\n\n    double eval[3];\n    vec3d evec[3];\n    C.eigen2(eval, evec);   // eigenvalues sorted from smallest to largest\n    \n    // rectify sign of eigenvalues\n    double emag = sqrt(eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2]);\n    if (eval[2] <= emag*m_eps) {\n        eval[0] = -eval[0];\n        eval[1] = -eval[1];\n        eval[2] = -eval[2];\n        v = -v;\n        c = -c;\n    }\n    \n    // re-sort eigenvalues and eigenvectors from largest to smalles\n    if (eval[0] < eval[2]) { swap(eval[0],eval[2]); swap(evec[0], evec[2]); }\n    if (eval[0] < eval[1]) { swap(eval[0],eval[1]); swap(evec[0], evec[1]); }\n    if (eval[1] < eval[2]) { swap(eval[1],eval[2]); swap(evec[1], evec[2]); }\n\n    // estimate the relative magnitudes of the eigenvalues\n    if (fabs(eval[0]) < m_eps*emag) eval[0] = 0;\n    if (fabs(eval[1]) < m_eps*emag) eval[1] = 0;\n    if (fabs(eval[2]) < m_eps*emag) eval[2] = 0;\n    \n    // normalize vectors\n    evec[0].unit(); evec[1].unit(); evec[2].unit();\n    // clean up vector\n    if (fabs(evec[0].x) < m_eps) evec[0].x = 0; if (fabs(evec[0].y) < m_eps) evec[0].y = 0; if (fabs(evec[0].z) < m_eps) evec[0].z = 0;\n    if (fabs(evec[1].x) < m_eps) evec[1].x = 0; if (fabs(evec[1].y) < m_eps) evec[1].y = 0; if (fabs(evec[1].z) < m_eps) evec[1].z = 0;\n    if (fabs(evec[2].x) < m_eps) evec[2].x = 0; if (fabs(evec[2].y) < m_eps) evec[2].y = 0; if (fabs(evec[2].z) < m_eps) evec[2].z = 0;\n    evec[0].unit(); evec[1].unit(); evec[2].unit();\n    // check if basis is right-handed or not\n    double rh = (evec[0] ^ evec[1])*evec[2];\n    if (rh < 0) evec[1] = evec[2] ^ evec[0];\n    \n    // estimate the relative magnitudes of the components of v\n    if (fabs(v.x) < m_eps*emag) v.x = 0;\n    if (fabs(v.y) < m_eps*emag) v.y = 0;\n    if (fabs(v.z) < m_eps*emag) v.z = 0;\n\n    mat3d Q(evec[0].x, evec[1].x, evec[2].x,\n            evec[0].y, evec[1].y, evec[2].y,\n            evec[0].z, evec[1].z, evec[2].z);\n    \n    m_ax[0] = evec[0];\n    m_ax[1] = evec[1];\n    m_ax[2] = evec[2];\n    \n    m_c2 = vec3d(eval[0], eval[1], eval[2]);\n    vec3d d = Q.transpose()*v;\n    m_v = v;\n\n    vec3d X0;\n    if (m_c2.x*m_c2.y*m_c2.z != 0) {\n        X0.x = -d.x/(2*m_c2.x);\n        X0.y = -d.y/(2*m_c2.y);\n        X0.z = -d.z/(2*m_c2.z);\n        m_c = c - m_c2.x*pow(X0.x,2) - m_c2.y*pow(X0.y,2) - m_c2.z*pow(X0.z,2);\n        if (fabs(m_c) < m_eps) m_c = 0;\n        if (m_c != 0) {\n            m_c2 /= fabs(m_c);\n            m_c /= fabs(m_c);\n        }\n    }\n    else if (m_c2.x*m_c2.y != 0) {\n        X0.x = -d.x/(2*m_c2.x);\n        X0.y = -d.y/(2*m_c2.y);\n        X0.z = 0;\n        m_c = c - m_c2.x*pow(X0.x,2) - m_c2.y*pow(X0.y,2);\n        if (m_c != 0) {\n            m_c2 /= fabs(m_c);\n            m_c /= fabs(m_c);\n        }\n    }\n    else {\n        X0.x = -d.x/(2*m_c2.x);\n        X0.y = 0;\n        X0.z = 0;\n        m_c = c - m_c2.x*pow(X0.x,2);\n        if (m_c != 0) {\n            m_c2 /= fabs(m_c);\n            m_c /= fabs(m_c);\n        }\n    }\n    \n    m_rc = Q*X0;\n    // clean up\n    double rc = m_rc.norm();\n    if (fabs(m_rc.x) < m_eps*rc) m_rc.x = 0;\n    if (fabs(m_rc.y) < m_eps*rc) m_rc.y = 0;\n    if (fabs(m_rc.z) < m_eps*rc) m_rc.z = 0;\n\n}\n\n//-------------------------------------------------------------------------------\n// complete fit algorithm\n//bool QuadricFit::Fit(PointCloud3d* pc)\nbool QuadricFit::Fit(std::vector<vec3d>& pc)\n{\n//    m_quad->SetPointCloud3d(pc);\n    m_quad->m_p = pc;\n    if (m_quad->GetQuadricCoeficients())\n    {\n        GetOrientation();\n        return true;\n    }\n    else\n        return false;\n}\n\n//-------------------------------------------------------------------------------\nbool QuadricFit::isSame(const double& a, const double&b)\n{\n    if (fabs(b-a) < m_eps*fabs(a+b)/2) return true;\n    else return false;\n}\n\n//-------------------------------------------------------------------------------\nQuadricFit::Q_TYPE QuadricFit::GetType()\n{\n    // determine quadric type\n    Q_TYPE type = Q_UNKNOWN;\n    double A = m_c2.x, B =  m_c2.y, C = m_c2.z;\n    \n    if ((A > 0) && (B > 0)) {\n        if ((C > 0) && isSame(m_c,-1)) {\n            type = Q_ELLIPSOID;\n            if (isSame(A,B)) {\n                type = Q_SPHEROID;\n                if (isSame(B,C)) type = Q_SPHERE;\n            }\n        }\n        else if (C == 0) {\n            if (isSame(m_v.z,-1) && (m_c == 0)) {\n                type = Q_ELLIPTIC_PARABOLOID;\n                if (isSame(A,B))\n                    type = Q_CIRCULAR_PARABOLOID;\n            }\n            else if ((m_v.z == 0) && isSame(m_c,-1)) {\n                type = Q_ELLIPTIC_CYLINDER;\n                if (isSame(A,B))\n                    type = Q_CIRCULAR_CYLINDER;\n            }\n        }\n        else if (C < 0) {\n            if  (isSame(m_c,-1)) {\n                type = Q_ELLIPTIC_HYPERBOLOID_1;\n                if (isSame(A,B)) {\n                    type = Q_CIRCULAR_HYPERBOLOID_1;\n                }\n            }\n            else if (isSame(m_c,1)) {\n                type = Q_ELLIPTIC_HYPERBOLOID_2;\n                if (isSame(A,B)) {\n                    type = Q_CIRCULAR_HYPERBOLOID_2;\n                }\n            }\n            else if  (m_c == 0) {\n                type = Q_ELLIPTIC_CONE;\n                if (isSame(A,B)) {\n                    type = Q_CIRCULAR_CONE;\n                }\n            }\n        }\n    }\n    else if ((A > 0) && (B < 0)) {\n        if ((C == 0) && (m_c == 0) && isSame(m_v.z,-1)) {\n            type = Q_HYPERBOLIC_PARABOLOID;\n        }\n        else if ((C == 0) && isSame(m_c,-1) && (m_v.z == 0)) {\n            type = Q_HYPERBOLIC_CYLINDER;\n        }\n    }\n    else if ((A > 0) && (B == 0)) {\n        type = Q_PARABOLIC_CYLINDER;\n    }\n\n    return type;\n}\n\n//-------------------------------------------------------------------------------\nstd::string QuadricFit::GetStringType(Q_TYPE qtype)\n{\n    std::string quadric;\n    switch (qtype) {\n        case QuadricFit::Q_ELLIPSOID: quadric = std::string(\"Ellipsoid\"); break;\n        case QuadricFit::Q_ELLIPTIC_PARABOLOID: quadric = std::string(\"Elliptic Paraboloid\"); break;\n        case QuadricFit::Q_HYPERBOLIC_PARABOLOID: quadric = std::string(\"Hyperbolic Paraboloid\"); break;\n        case QuadricFit::Q_ELLIPTIC_HYPERBOLOID_1: quadric = std::string(\"Elliptic Hyperboloid of One Sheet\"); break;\n        case QuadricFit::Q_ELLIPTIC_HYPERBOLOID_2: quadric = std::string(\"Elliptic Hyperboloid of Two Sheets\"); break;\n        case QuadricFit::Q_ELLIPTIC_CONE: quadric = std::string(\"Elliptic Cone\"); break;\n        case QuadricFit::Q_ELLIPTIC_CYLINDER: quadric = std::string(\"Elliptic Cylinder\"); break;\n        case QuadricFit::Q_PARABOLIC_CYLINDER: quadric = std::string(\"Parabolic Cylinder\"); break;\n        case QuadricFit::Q_SPHEROID: quadric = std::string(\"Spheroid\"); break;\n        case QuadricFit::Q_SPHERE: quadric = std::string(\"Sphere\"); break;\n        case QuadricFit::Q_CIRCULAR_PARABOLOID: quadric = std::string(\"Circular Paraboloid\"); break;\n        case QuadricFit::Q_CIRCULAR_HYPERBOLOID_1: quadric = std::string(\"Circular Hyperboloid of One Sheet\"); break;\n        case QuadricFit::Q_CIRCULAR_HYPERBOLOID_2: quadric = std::string(\"Circular Hyperboloid of Two Sheets\"); break;\n        case QuadricFit::Q_CIRCULAR_CONE: quadric = std::string(\"Circular Cone\"); break;\n        case QuadricFit::Q_CIRCULAR_CYLINDER: quadric = std::string(\"Circular Cylinder\"); break;\n        case QuadricFit::Q_UNKNOWN: quadric = std::string(\"Unknown\"); break;\n        default: break;\n    }\n    return quadric;\n}\n"
  },
  {
    "path": "FECore/QuadricFit.h",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio-Studio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n#pragma once\n#include <vector>\n#include <string>\n#include \"Quadric.h\"\n#include \"quatd.h\"\n#include \"fecore_api.h\"\n\nclass FECORE_API QuadricFit\n{\npublic:\n    enum Q_TYPE { Q_ELLIPSOID, Q_ELLIPTIC_PARABOLOID, Q_HYPERBOLIC_PARABOLOID,\n        Q_ELLIPTIC_HYPERBOLOID_1, Q_ELLIPTIC_HYPERBOLOID_2, Q_ELLIPTIC_CONE,\n        Q_ELLIPTIC_CYLINDER, Q_HYPERBOLIC_CYLINDER, Q_PARABOLIC_CYLINDER,\n        Q_SPHEROID, Q_SPHERE, Q_CIRCULAR_PARABOLOID, Q_CIRCULAR_HYPERBOLOID_1,\n        Q_CIRCULAR_HYPERBOLOID_2, Q_CIRCULAR_CONE, Q_CIRCULAR_CYLINDER, Q_UNKNOWN\n    };\n\npublic:\n    QuadricFit();\n    QuadricFit(const QuadricFit& qf);\n    virtual ~QuadricFit();\n    \n    bool Fit(std::vector<vec3d>& pc);\n    Q_TYPE GetType();\n    std::string GetStringType(Q_TYPE qtype);\n\nprotected:\n    vec3d Transform(vec3d& rc, quatd& q, const vec3d& p)\n    {\n        vec3d r = p - rc;\n        q.RotateVector(r);\n        return r;\n    }\n    \n    void GetOrientation();\n    bool isSame(const double& a, const double&b);\n    \n\npublic:\n    vec3d   m_rc;   // center of quadric\n    vec3d   m_ax[3];// quadric axes\n    vec3d   m_c2;   // coefficients of square terms\n    vec3d   m_v;    // coefficients of linear terms\n    double  m_c;    // constant\n    double  m_eps;  // tolerance\n    \n    Quadric*    m_quad; // quadric object\n};\n"
  },
  {
    "path": "FECore/SchurComplement.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"SchurComplement.h\"\n\nSchurComplementA::SchurComplementA(LinearSolver* A, SparseMatrix* B, SparseMatrix* C, SparseMatrix* D)\n{\n\tm_print_level = 0;\n\n\tm_A = A;\n\tm_B = B;\n\tm_C = C;\n\tm_D = D;\n\n\tint n0 = m_B->Rows();\n\tint n1 = m_B->Columns();\n\tassert(n0 == m_C->Columns());\n\tassert(n1 == m_C->Rows());\n\n\tm_tmp1.resize(n0, 0.0);\n\tm_tmp2.resize(n0, 0.0);\n\tm_tmp3.resize(n1, 0.0);\n\n\tm_nrow = n1;\n\tm_ncol = n1;\n\n\tm_bnegate = false;\n}\n\n// negate schur complement\nvoid SchurComplementA::NegateSchur(bool b)\n{\n\tm_bnegate = b;\n}\n\n// set the print level\nvoid SchurComplementA::SetPrintLevel(int printLevel)\n{\n\tm_print_level = printLevel;\n}\n\n//! multiply with vector\nbool SchurComplementA::mult_vector(double* x, double* r)\n{\n\tm_B->mult_vector(x, &m_tmp1[0]);\n\n\tif (m_print_level != 0) printf(\"backsolving in SchurComplement\\n\");\n\tif (m_A) { if (m_A->BackSolve(m_tmp2, m_tmp1) == false) return false; }\n\telse m_tmp2 = m_tmp1;\n\tm_C->mult_vector(&m_tmp2[0], r);\n\n\tif (m_D)\n\t{\n\t\tif (m_D->mult_vector(x, &m_tmp3[0]) == false) return false;\n\n\t\tsize_t n = m_tmp3.size();\n\t\tfor (size_t i = 0; i<n; ++i) r[i] -= m_tmp3[i];\n\t}\n\n\tif (m_bnegate)\n\t{\n\t\tsize_t n = m_tmp3.size();\n\t\tfor (int i = 0; i < n; ++i) r[i] = -r[i];\n\t}\n\n\treturn true;\n}\n\n\nSchurComplementD::SchurComplementD(SparseMatrix* A, SparseMatrix* B, SparseMatrix* C, LinearSolver* D)\n{\n\tm_print_level = 0;\n\n\tm_A = A;\n\tm_B = B;\n\tm_C = C;\n\tm_D = D;\n\n\tint n0 = m_B->Rows();\n\tint n1 = m_B->Columns();\n\tassert(n0 == m_C->Columns());\n\tassert(n1 == m_C->Rows());\n\n\tm_tmp1.resize(n1, 0.0);\n\tm_tmp2.resize(n1, 0.0);\n\tm_tmp3.resize(n0, 0.0);\n\n\tm_nrow = n0;\n\tm_ncol = n0;\n}\n\n// set the print level\nvoid SchurComplementD::SetPrintLevel(int printLevel)\n{\n\tm_print_level = printLevel;\n}\n\n//! multiply with vector\nbool SchurComplementD::mult_vector(double* x, double* r)\n{\n\tm_C->mult_vector(x, &m_tmp1[0]);\n\n\tif (m_print_level != 0) printf(\"backsolving in SchurComplement\\n\");\n\tif (m_D->BackSolve(m_tmp2, m_tmp1) == false) return false;\n\tm_B->mult_vector(&m_tmp2[0], r);\n\n\tif (m_A)\n\t{\n\t\tif (m_A->mult_vector(x, &m_tmp3[0]) == false) return false;\n\n\t\tsize_t n = m_tmp3.size();\n\t\tfor (size_t i = 0; i<n; ++i) r[i] -= m_tmp3[i];\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/SchurComplement.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"SparseMatrix.h\"\n#include \"LinearSolver.h\"\n\n// This class implements a sparse matrix operator that represents the Schur complement of a matrix M\n//\n//       | A | B |\n//  M =  | --+-- |\n//       | C | D |\n//\n// The Schur complement of A, is given by \n//       \n//  S\\A = C*A^-1*B - D\n//\n// The only operator this class implements is the matrix-vector multiplication operator (in mult_vector function). \n// This function evaluates a product of the form S*x, where v is a vector, as follows:\n// 1. evaluate u = B*x\n// 2. evaluate v = A^-1*u, by solving A*v = u\n// 3. evaluate r = C*v\n// 4. if D is given, then subtract r <-- r - D*x\n//\n// If D = 0, it does not need to be specified, in which case step 4 is not done.\n\nclass FECORE_API SchurComplementA : public SparseMatrix\n{\npublic:\n\tSchurComplementA(LinearSolver* A, SparseMatrix* B, SparseMatrix* C, SparseMatrix* D = 0);\n\n\t// set the print level\n\tvoid SetPrintLevel(int printLevel);\n\n\t// negate schur complement\n\tvoid NegateSchur(bool b);\n\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override;\n\nprivate: // we need to override these functions although we don't want to use them\n\tvoid Zero() override { assert(false); }\n\tvoid Create(SparseMatrixProfile& MP) override { assert(false); }\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override { assert(false); }\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override { assert(false); }\n\tbool check(int i, int j) override { assert(false); return false; }\n\tvoid set(int i, int j, double v) override  { assert(false); }\n\tvoid add(int i, int j, double v) override  { assert(false); }\n\tdouble diag(int i) override  { assert(false); return 0.0; }\n\nprivate:\n\tint\tm_print_level;\n\tbool\tm_bnegate;\n\t\n\tLinearSolver*\tm_A;\n\tSparseMatrix*\tm_B;\n\tSparseMatrix*\tm_C;\n\tSparseMatrix*\tm_D;\n\n\tvector<double>\tm_tmp1, m_tmp2, m_tmp3;\n};\n\n//       | A | B |\n//  M =  | --+-- |\n//       | C | D |\n//\n// The Schur complement of D, is given by \n//       \n//  S\\D = B*D^-1*C - A\n\nclass FECORE_API SchurComplementD : public SparseMatrix\n{\npublic:\n\tSchurComplementD(SparseMatrix* A, SparseMatrix* B, SparseMatrix* C, LinearSolver* D);\n\n\t// set the print level\n\tvoid SetPrintLevel(int printLevel);\n\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override;\n\nprivate: // we need to override these functions although we don't want to use them\n\tvoid Zero() override { assert(false); }\n\tvoid Create(SparseMatrixProfile& MP) override { assert(false); }\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override { assert(false); }\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override { assert(false); }\n\tbool check(int i, int j) override { assert(false); return false; }\n\tvoid set(int i, int j, double v) override { assert(false); }\n\tvoid add(int i, int j, double v) override { assert(false); }\n\tdouble diag(int i) override { assert(false); return 0.0; }\n\nprivate:\n\tint\tm_print_level;\n\n\tLinearSolver*\tm_D;\n\tSparseMatrix*\tm_B;\n\tSparseMatrix*\tm_C;\n\tSparseMatrix*\tm_A;\n\n\tvector<double>\tm_tmp1, m_tmp2, m_tmp3;\n};\n"
  },
  {
    "path": "FECore/SkylineMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"SkylineMatrix.h\"\nusing namespace std;\n\n//=============================================================================\n// SkylineMatrix\n//=============================================================================\n\n//-----------------------------------------------------------------------------\nSkylineMatrix::SkylineMatrix()\n{\n\tm_ppointers = 0;\n\tm_pd = 0;\n}\n\n//-----------------------------------------------------------------------------\nSkylineMatrix::~SkylineMatrix()\n{\n\tClear();\n}\n\n//-----------------------------------------------------------------------------\nvoid SkylineMatrix::Zero()\n{\n\tmemset(m_pd, 0, m_nsize*sizeof(double)); \n}\n\n//-----------------------------------------------------------------------------\nvoid SkylineMatrix::Clear()\n{\n\tif (m_pd       ) delete [] m_pd       ; m_pd = 0;\n\tif (m_ppointers) delete [] m_ppointers; m_ppointers = 0;\n\n\tSparseMatrix::Clear();\n}\n\n//-----------------------------------------------------------------------------\n//! \\todo Can I get rid of this function?\nvoid SkylineMatrix::Create(double* pv, int* pp, int N)\n{\n\tdelete [] m_pd  ; m_pd = pv;\n\tdelete [] m_ppointers; m_ppointers = pp;\n\n\tm_nrow = m_ncol = N;\n\tm_nsize = pp[N];\n}\n\n//-----------------------------------------------------------------------------\nvoid SkylineMatrix::Create(SparseMatrixProfile& mp)\n{\n\tint neq = mp.Rows();\n\tint* pointers = new int[neq + 1];\n\n\tpointers[0] = 0;\n\tfor (int i=1; i<=neq; ++i)\n\t{\n\t\tSparseMatrixProfile::ColumnProfile& a = mp.Column(i-1);\n\t\tint n = i - a[0].start;\n\t\tpointers[i] = pointers[i-1] + n;\n\t}\n\n\t// allocate stiffness matrix\n\tdouble* values = new double[pointers[neq]];\n\tif (values==0)\n\t{\n\t\tdouble falloc = (double) sizeof(double) * (double) (pointers[neq]);\n\t}\n\n\t// create the matrix\n\tCreate(values, pointers, neq);\n}\n\n\n//-----------------------------------------------------------------------------\n//! This function assembles the local stiffness matrix\n//! into the global stiffness matrix which is in skyline format\n//!\nvoid SkylineMatrix::Assemble(const matrix& ke, const vector<int>& LM)\n{\n\tint i, j, I, J;\n\n\tconst int N = ke.rows();\n\n\tdouble* pv = values();\n\tint* pi = pointers();\n\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tI = LM[i];\n\n\t\tif (I>=0)\n\t\t{\n\t\t\tfor (j=0; j<N; ++j)\n\t\t\t{\n\t\t\t\tJ = LM[j];\n\n\t\t\t\t// only add values to upper-diagonal part of stiffness matrix\n\t\t\t\tif (J>=I)\n\t\t\t\t{\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tpv[ pi[J] + J - I] += ke[i][j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n//-----------------------------------------------------------------------------\nvoid SkylineMatrix::Assemble(const matrix& ke, const vector<int>& LMi, const vector<int>& LMj)\n{\n\tint i, j, I, J;\n\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\n\tdouble* pv = values();\n\tint* pi = pointers();\n\n\tfor (i=0; i<N; ++i)\n\t{\n\t\tI = LMi[i];\n\n\t\tif (I>=0)\n\t\t{\n\t\t\tfor (j=0; j<M; ++j)\n\t\t\t{\n\t\t\t\tJ = LMj[j];\n\n\t\t\t\t// only add values to upper-diagonal part of stiffness matrix\n\t\t\t\tif (J>=I)\n\t\t\t\t{\n\t\t\t\t\t#pragma omp atomic\n\t\t\t\t\tpv[ pi[J] + J - I] += ke[i][j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool SkylineMatrix::check(int i, int j)\n{\n\tassert(false);\n\treturn true;\n}\n\nvoid SkylineMatrix::add(int i, int j, double v)\n{\n\t// only add to the upper triangular part\n\tif (j >= i)\n\t{\n\t\t#pragma omp atomic\n\t\tm_pd[m_ppointers[j] + j - i] += v;\n\t}\n}\n\nvoid SkylineMatrix::set(int i, int j, double v)\n{\n\t// only add to the upper triangular part\n\tif (j >= i)\n\t{\n\t\t#pragma omp critical (SKY_set)\n\t\tm_pd[m_ppointers[j] + j - i] = v;\n\t}\n}\n\ndouble SkylineMatrix::get(int i, int j)\n{\n\tif (i>j) { i^= j; j ^= i; i ^= j; }\n\n\tint l = m_ppointers[j+1] - m_ppointers[j];\n\tif (j-i < l) return m_pd[ m_ppointers[j] + j-i];\n\treturn 0;\n}\n\ndouble SkylineMatrix::diag(int i)\n{\n\treturn m_pd[ m_ppointers[i] ];\n}\n"
  },
  {
    "path": "FECore/SkylineMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"SparseMatrix.h\"\n#include \"fecore_api.h\"\n\n//=============================================================================\n//! Implements a sparse matrix using the skyline storage\n\n//! This class implements a symmetric sparse matrix where only the values\n//! below the skyline are stored.\n\nclass FECORE_API SkylineMatrix : public SparseMatrix\n{\npublic:\n\tSkylineMatrix();\n\tvirtual ~SkylineMatrix();\n\npublic: // from SparseMatrix\n\n\tvoid Zero() override;\n\n\tvoid Clear() override;\n\n\tvoid Create(SparseMatrixProfile& mp) override;\n\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\tvoid add(int i, int j, double v) override;\n\n\tvoid set(int i, int j, double v) override;\n\n\t// NOTE: This is not implemented yet!\n\tbool check(int i, int j) override;\n\n\tdouble get(int i, int j) override;\n\n\tdouble diag(int i) override;\n\n\tdouble* values() { return m_pd; }\n\tint* pointers() { return m_ppointers; }\n\nprotected:\n\tvoid Create(double* pv, int* pp, int N);\n\nprotected:\n\tdouble*\tm_pd;\t\t\t//!< matrix values\n\tint*\tm_ppointers;\t//!< arrays of indices to diagonal elements\n};\n"
  },
  {
    "path": "FECore/SkylineSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"SkylineSolver.h\"\n\n//-----------------------------------------------------------------------------\nvoid colsol_factor(int N, double* values, int* pointers);\nvoid colsol_solve(int N, double* values, int* pointers, double* R);\n\n//-----------------------------------------------------------------------------\nSkylineSolver::SkylineSolver(FEModel* fem) : LinearSolver(fem), m_pA(0)\n{\n}\n\n//-----------------------------------------------------------------------------\n//! Create a sparse matrix\nSparseMatrix* SkylineSolver::CreateSparseMatrix(Matrix_Type ntype)\n{ \n\treturn (m_pA = (ntype == REAL_SYMMETRIC? new SkylineMatrix() : 0)); \n}\n\n//-----------------------------------------------------------------------------\nbool SkylineSolver::PreProcess()\n{\n\t// We don't need to do any preprocessing for this solver\n\treturn LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool SkylineSolver::Factor()\n{\n\tcolsol_factor(m_pA->Rows(), m_pA->values(), m_pA->pointers());\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool SkylineSolver::BackSolve(double* x, double* b)\n{\n\t// we need to make a copy of R since colsol overwrites the right hand side vector\n\t// with the solution\n\tint neq = m_pA->Rows();\n\tfor (int i=0; i<neq; ++i) x[i] = b[i];\n\tcolsol_solve(m_pA->Rows(), m_pA->values(), m_pA->pointers(), x);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid SkylineSolver::Destroy()\n{\n\t// Nothing to destroy\n\tLinearSolver::Destroy();\n}\n"
  },
  {
    "path": "FECore/SkylineSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"LinearSolver.h\"\n#include \"SkylineMatrix.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\n//! Implements a linear solver that uses a skyline format\n\nclass FECORE_API SkylineSolver : public LinearSolver\n{\npublic:\n\t//! constructor\n\tSkylineSolver(FEModel* fem);\n\n\t//! Preprocess \n\tbool PreProcess() override;\n\n\t//! Factor matrix\n\tbool Factor() override;\n\n\t//! Backsolve the linear system\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Clean up\n\tvoid Destroy() override;\n\n\t//! Create a sparse matrix\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\nprivate:\n\tSkylineMatrix*\tm_pA;\n};\n"
  },
  {
    "path": "FECore/SparseMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"SparseMatrix.h\"\n#include <memory.h>\nusing namespace std;\n\n//-----------------------------------------------------------------------------\nSparseMatrix::SparseMatrix()\n{\n\tm_nrow = m_ncol = 0;\n\tm_nsize = 0;\n}\n\nSparseMatrix::~SparseMatrix()\n{\n}\n\nvoid SparseMatrix::Clear()\n{\n\tm_nrow = m_ncol = 0;\n\tm_nsize = 0;\n}\n\n//! scale matrix\nvoid SparseMatrix::scale(const vector<double>& L, const vector<double>& R)\n{\n\tassert(false);\n}\n"
  },
  {
    "path": "FECore/SparseMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"MatrixProfile.h\"\n#include \"MatrixOperator.h\"\n#include \"matrix.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n//! Base class for sparse matrices\n\n//! This is the base class for the sparse matrix classes and defines the interface\n//! to the different matrix classes\n\nclass FECORE_API SparseMatrix : public MatrixOperator\n{\npublic:\n\t//! constructor\n\tSparseMatrix();\n\n\t//! destructor\n\tvirtual ~SparseMatrix();\n\npublic:\n\t//! return number of rows\n\tint Rows() const { return m_nrow; }\n\n\t//! return number of columns\n\tint Columns() const { return m_ncol; }\n\n\t//! is the matrix square?\n\tbool IsSquare() const { return (m_nrow == m_ncol); }\n\n\t//! return number of nonzeros\n\tsize_t NonZeroes() const { return m_nsize; }\n\npublic: // functions to be overwritten in derived classes\n\n\t//! set all matrix elements to zero\n\tvirtual void Zero() = 0;\n\n\t//! Create a sparse matrix from a sparse-matrix profile\n\tvirtual void Create(SparseMatrixProfile& MP) = 0;\n\n\t//! assemble a matrix into the sparse matrix\n\tvirtual void Assemble(const matrix& ke, const std::vector<int>& lm) = 0;\n\n\t//! assemble a matrix into the sparse matrix\n\tvirtual void Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) = 0;\n\n\t//! check if an entry was allocated\n\tvirtual bool check(int i, int j) = 0;\n\n\t//! set entry to value\n\tvirtual void set(int i, int j, double v) = 0;\n\n\t//! add value to entry\n\tvirtual void add(int i, int j, double v) = 0;\n\n\t//! retrieve value\n\tvirtual double get(int i, int j) { return 0; }\n\n\t//! get the diagonal value\n\tvirtual double diag(int i) = 0;\n\n\t//! release memory for storing data\n\tvirtual void Clear();\n\n\t//! scale matrix\n\tvirtual void scale(const std::vector<double>& L, const std::vector<double>& R);\n\npublic:\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override { assert(false); return false; }\n\npublic:\n\t// NOTE: The following functions are only used by the compact matrices, but I need to be able to override them\n\t// for the JFNKMatrix so I've moved them here. \n\tvirtual double* Values() { return 0; }\n\tvirtual int*    Indices() { return 0; }\n\tvirtual int*    Pointers() { return 0; }\n\tvirtual int     Offset() const { return 0; }\n\nprotected:\n\t// NOTE: These values are set by derived classes\n\tint\tm_nrow, m_ncol;\t\t//!< dimension of matrix\n\tsize_t m_nsize;\t\t\t//!< number of nonzeroes (i.e. matrix elements actually allocated)\n};\n"
  },
  {
    "path": "FECore/SurfaceDataRecord.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"SurfaceDataRecord.h\"\n#include \"FECoreKernel.h\"\n#include \"FEModel.h\"\n#include \"FESurface.h\"\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceDataRecord::SetData(const char* szexpr)\n{\n    char szcopy[MAX_STRING] = { 0 };\n    strcpy(szcopy, szexpr);\n    char* sz = szcopy, * ch;\n    m_Data.clear();\n    strcpy(m_szdata, szexpr);\n    do\n    {\n        ch = strchr(sz, ';');\n        if (ch) *ch++ = 0;\n        FELogSurfaceData* pdata = fecore_new<FELogSurfaceData>(sz, GetFEModel());\n        if (pdata) m_Data.push_back(pdata);\n        else throw UnknownDataField(sz);\n        sz = ch;\n    } while (ch);\n}\n\n//-----------------------------------------------------------------------------\nFESurfaceDataRecord::FESurfaceDataRecord(FEModel* pfem) : DataRecord(pfem, FE_DATA_SURFACE) {}\n\n//-----------------------------------------------------------------------------\nint FESurfaceDataRecord::Size() const { return (int)m_Data.size(); }\n\n//-----------------------------------------------------------------------------\ndouble FESurfaceDataRecord::Evaluate(int item, int ndata)\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    int nd = item - 1;\n    if ((nd < 0) || (nd >= mesh.Surfaces())) return 0;\n\n    FESurface& surf = mesh.Surface(nd);\n    return m_Data[ndata]->value(surf);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceDataRecord::SetSurface(int surfIndex)\n{\n    m_item.clear();\n    m_item.push_back(surfIndex + 1);\n}\n\n//-----------------------------------------------------------------------------\nvoid FESurfaceDataRecord::SelectAllItems()\n{\n    FEMesh& mesh = GetFEModel()->GetMesh();\n    int n = mesh.Surfaces();\n    m_item.resize(n);\n    for (int i = 0; i < n; ++i) m_item[i] = i + 1;\n}\n"
  },
  {
    "path": "FECore/SurfaceDataRecord.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"FECoreBase.h\"\n#include \"DataRecord.h\"\n\nclass FESurface;\n\n//-----------------------------------------------------------------------------\n//! Base class for surface log data\nclass FECORE_API FELogSurfaceData : public FELogData\n{\n    FECORE_SUPER_CLASS(FELOGSURFACEDATA_ID)\n    FECORE_BASE_CLASS(FELogSurfaceData);\n\npublic:\n    FELogSurfaceData(FEModel* fem) : FELogData(fem) {}\n    virtual ~FELogSurfaceData() {}\n    virtual double value(FESurface& surface) = 0;\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FESurfaceDataRecord : public DataRecord\n{\npublic:\n    FESurfaceDataRecord(FEModel* pfem);\n    double Evaluate(int item, int ndata) override;\n    void SetData(const char* sz) override;\n    void SetSurface(int surfIndex);\n    void SelectAllItems() override;\n    int Size() const override;\n\nprivate:\n    vector<FELogSurfaceData*>\tm_Data;\n};\n"
  },
  {
    "path": "FECore/Timer.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"Timer.h\"\n#include <stdio.h>\n#include <string>\n#include <chrono>\n#include \"FEModel.h\"\nusing namespace std::chrono;\n\nusing dseconds = duration<double>;\n\n// data storing timing info\nstruct\tTimer::Imp {\n\ttime_point<steady_clock>\tstart;\t//!< time at start\n\ttime_point<steady_clock>\tstop;\t//!< time at last stop\n\ttime_point<steady_clock>\tpause;\t//!< time when paused\n\n\tbool\tisRunning = false;\t//!< flag indicating whether start was called\n\tdseconds total; //!< total accumulated time (between start and stop)\n\n\tbool isPaused = false;\n\tdseconds paused; //!< accumulated time while paused\n\n\tTimer* parent = nullptr; // the timer that was active when this timer starts\n\n\tbool track = true;\n\n\tstatic Timer* activeTimer;\n};\n\nTimer* Timer::Imp::activeTimer = nullptr;\n\nTimer::Timer(bool isTracked)\n{\n\tm = new Imp;\n\tm->track = isTracked;\n\treset();\n}\n\nTimer::~Timer()\n{\n\tif (Imp::activeTimer == this) Imp::activeTimer = nullptr;\n\tdelete m;\n}\n\nTimer* Timer::activeTimer()\n{\n\treturn Imp::activeTimer;\n}\n\nvoid Timer::start()\n{\n\tm->parent = (m->track ? m->activeTimer : nullptr);\n\tif (m->parent) m->parent->pause();\n\tif (m->track) m->activeTimer = this;\n\tm->start = steady_clock::now();\n\tassert(m->isRunning == false);\n\tm->isRunning = true;\n}\n\nvoid Timer::stop()\n{\n\tm->stop = steady_clock::now();\n\tassert(m->isRunning == true);\n\tm->isRunning = false;\n\tm->total += m->stop - m->start;\n\n\tif (m->track)\n\t{\n\t\tassert(m->activeTimer == this);\n\t\tm->activeTimer = m->parent;\n\t\tif (m->parent) m->parent->unpause();\n\t}\n}\n\nvoid Timer::pause()\n{\n\tassert(!m->isPaused);\n\tm->pause = steady_clock::now();\n\tm->isPaused = true;\n}\n\nvoid Timer::unpause()\n{\n\tassert(m->isPaused);\n\tauto tmp = steady_clock::now();\n\tm->paused += tmp - m->pause;\n\tm->isPaused = false;\n}\n\nvoid Timer::reset()\n{\n\tm->total = dseconds(0);\n\tm->paused = dseconds(0);\n\tm->isRunning = false;\n\tm->isPaused = false;\n\tm->parent = nullptr;\n\tif (m->activeTimer == this) m->activeTimer = nullptr;\n}\n\nbool Timer::isRunning() const { return m->isRunning; }\n\ndouble Timer::peek()\n{\n\tif (m->isRunning)\n\t{\n\t\ttime_point<steady_clock> tmp = steady_clock::now();\n\t\treturn duration_cast<dseconds>(m->total + (tmp - m->start)).count();\n\t}\n\telse \n\t{\n\t\treturn m->total.count();\n\t}\n}\n\nvoid Timer::GetTime(int& nhour, int& nmin, int& nsec)\n{\n\tdouble sec = peek();\n\tGetTime(sec, nhour, nmin, nsec);\n}\n\ndouble Timer::GetExclusiveTime()\n{\n\tassert(!m->isPaused);\n\tdouble sec = peek() - m->paused.count();\n\treturn sec;\n}\n\nvoid Timer::GetTime(double fsec, int& nhour, int& nmin, int& nsec)\n{\n\tnhour = (int) (fsec / 3600.0); fsec -= nhour*3600;\n\tnmin  = (int) (fsec /   60.0); fsec -= nmin*60;\n\tnsec  = (int) (fsec + 0.5);\n}\n\ndouble Timer::GetTime()\n{\n\treturn (m->isRunning? peek() : m->total.count());\n}\n\nvoid Timer::time_str(char* sz)\n{\n\tint nhour, nmin, nsec;\n\tGetTime(nhour, nmin, nsec);\n\tsnprintf(sz, 64, \"%d:%02d:%02d\", nhour, nmin, nsec);\n}\n\nvoid Timer::time_str(double fsec, char* sz)\n{\n\tint nhour, nmin, nsec;\n\tGetTime(fsec, nhour, nmin, nsec);\n\tsnprintf(sz, 64, \"%d:%02d:%02d\", nhour, nmin, nsec);\n}\n\n//============================================================================\nTimerTracker::TimerTracker(FEModel* fem, int timerId) : TimerTracker(fem->CollectTimings() ? fem->GetTimer(timerId) : nullptr) {}\n"
  },
  {
    "path": "FECore/Timer.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n\nclass FEModel;\n\n//! This class implements a simple timer. \n\n//! The start function starts the timer, the stop\n//! function stops it and the GetTime function returns the time elapsed between\n//! the call to start and stop\n\nclass FECORE_API Timer\n{\n\tstruct Imp;\n\npublic:\n\t//! constructor\n\tTimer(bool isTracked = true);\n    ~Timer();\n\n\t//! Start the timer\n\tvoid start();\n\n\t//! Stop the timer\n\tvoid stop();\n\n\t//! pause the timer\n\tvoid pause();\n\n\t//! continue\n\tvoid unpause();\n\n\tvoid reset();\n\n\t//! Get the elapsed time\n\tvoid GetTime(int& nhour, int& nmin, int& nsec);\n\n\t//! Get the time in seconds\n\tdouble GetTime();\n\n\t//! Get the exclusive time (i.e. time when not paused)\n\tdouble GetExclusiveTime();\n\n\t//! return the time as a text string\n\tvoid time_str(char* sz);\n\n\t//! check the elapsed time\n\tdouble peek();\n\n\t//! see if the timer is running\n\tbool isRunning() const;\n\npublic:\n\tstatic void time_str(double fsec, char* sz);\n\tstatic void GetTime(double fsec, int& nhour, int& nmin, int& nsec);\n\tstatic Timer* activeTimer();\n\nprivate:\n\tImp*\tm;\t//!< local timing data (using PIMPL ididom)\n};\n\n//-----------------------------------------------------------------------------\nclass FEModel;\n\n//-----------------------------------------------------------------------------\n// Timer IDs\nenum TimerID {\n\tTimer_Init,\n\tTimer_Update,\n\tTimer_LinSol_Factor,\n\tTimer_LinSol_Backsolve,\n\tTimer_Reform,\n\tTimer_Residual,\n\tTimer_Stiffness,\n\tTimer_QNUpdate,\n\tTimer_Serialize,\n\tTimer_ModelSolve,\n\tTimer_Callback,\n\tTimer_USER1,\n\tTimer_USER2,\n\tTimer_USER3,\n\tTimer_USER4,\n\tTIMER_COUNT // leave this at the end so that it equals the nr. of timers we need\n};\n\n//-----------------------------------------------------------------------------\n// This is helper class that can be used to ensure that a timer is stopped when\n// the function that is being timed exits. That way, the Timer::stop member does not \n// have to be called at every exit point of a function.\n// In addition, it will also check if the timer is already running (e.g. from a function\n// higher in the call stack) in which case it will track the timer. \nclass FECORE_API TimerTracker\n{\npublic:\n\tTimerTracker(FEModel* fem, int timerId);\n\tTimerTracker(Timer* timer)\n\t{\n\t\tif (timer && !timer->isRunning()) { m_timer = timer; timer->start(); }\n\t\telse m_timer = nullptr;\n\t}\n\t~TimerTracker() { if (m_timer) m_timer->stop(); }\n\nprivate:\n\tTimer*\tm_timer;\n};\n\n#define TRACK_TIME(timerId) TimerTracker _trackTimer(GetFEModel(), timerId);\n"
  },
  {
    "path": "FECore/ad.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"ad.h\"\n\ndouble ad::Evaluate(std::function<ad::number(vec3d&)> W, const ::vec3d& a)\n{\n\tad::vec3d da(a);\n\treturn W(da).r;\n}\n\n::vec3d ad::Grad(std::function<ad::number(vec3d&)> W, const ::vec3d& a)\n{\n\tad::vec3d da(a);\n\tda.x.dr = 1; da.y.dr = 0; da.z.dr = 0; double dx = W(da).dr;\n\tda.x.dr = 0; da.y.dr = 1; da.z.dr = 0; double dy = W(da).dr;\n\tda.x.dr = 0; da.y.dr = 0; da.z.dr = 1; double dz = W(da).dr;\n\treturn ::vec3d(dx, dy, dz);\n}\n\n::mat3d ad::Grad(std::function<ad::vec3d(ad::vec3d&)> F, const ::vec3d& a)\n{\n\tad::vec3d da(a);\n\tdouble D[3][3] = { 0 };\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tda[i].dr = 1;\n\t\t::vec3d Fi = F(da).partials();\n\t\tda[i].dr = 0;\n\t\tD[0][i] = Fi.x;\n\t\tD[1][i] = Fi.y;\n\t\tD[2][i] = Fi.z;\n\t}\n\treturn ::mat3d(D);\n}\n\ndouble ad::Evaluate(std::function<ad::number(ad::mat3ds& C)> W, const ::mat3ds& C)\n{\n\tad::mat3ds dC(C);\n\treturn W(dC).r;\n}\n\n::mat3ds ad::Derive(std::function<ad::number(ad::mat3ds& C)> W, const ::mat3ds& C)\n{\n\tad::mat3ds dC(C);\n\tdouble S[6] = { 0.0 };\n\tfor (int i = 0; i < 6; ++i)\n\t{\n\t\tdC[i].dr = 1;\n\t\tS[i] = W(dC).dr;\n\t\tdC[i].dr = 0;\n\t}\n\treturn ::mat3ds(S[0], S[2], S[5], 0.5 * S[1], 0.5 * S[4], 0.5 * S[3]);\n}\n\n::tens4ds ad::Derive(std::function<mat3ds(mat3ds& C)> S, const ::mat3ds& C)\n{\n\tad::mat3ds dC(C);\n\n\tconstexpr int l[6] = { 0, 2, 5, 1, 4, 3 };\n\tdouble D[6][6] = { 0 };\n\tfor (int i = 0; i < 6; ++i)\n\t{\n\t\tint n = l[i];\n\t\tdC[n].dr = 1;\n\t\t::mat3ds Si = S(dC).partials();\n\t\tdC[n].dr = 0;\n\n\t\tD[0][i] = Si.xx();\n\t\tD[1][i] = Si.yy();\n\t\tD[2][i] = Si.zz();\n\t\tD[3][i] = 0.5 * Si.xy();\n\t\tD[4][i] = 0.5 * Si.yz();\n\t\tD[5][i] = 0.5 * Si.xz();\n\t}\n\n\treturn tens4ds(D);\n}\n"
  },
  {
    "path": "FECore/ad.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"mat3d.h\"\n#include \"tens4d.h\"\n#include \"quatd.h\"\n#include \"fecore_api.h\"\n#include <functional>\n\nnamespace ad {\n\n\tstruct number {\n\t\tdouble r, dr;\n\t\tnumber() : r(0.0), dr(0.0) {}\n\t\tnumber(double v, double dv = 0) : r(v), dr(dv) {}\n\t\tvoid operator = (const double& a) { r = a; dr = 0.0; }\n\t};\n\n\t// negation\n\tinline number operator - (const number& a)\n\t{\n\t\treturn number(-a.r, -a.dr);\n\t}\n\n\t// addition\n\tinline number operator + (const number& a, const number& b)\n\t{\n\t\treturn number(a.r + b.r, a.dr + b.dr);\n\t}\n\n\tinline number operator + (double a, const number& b)\n\t{\n\t\treturn number(a + b.r, b.dr);\n\t}\n\n\tinline number operator + (const number& a, double b)\n\t{\n\t\treturn number(a.r + b, a.dr);\n\t}\n\n\t// subtraction\n\tinline number operator - (const number& a, const number& b)\n\t{\n\t\treturn number(a.r - b.r, a.dr - b.dr);\n\t}\n\n\tinline number operator - (double a, const number& b)\n\t{\n\t\treturn number(a - b.r, -b.dr);\n\t}\n\n\tinline number operator - (const number& a, double b)\n\t{\n\t\treturn number(a.r - b, a.dr);\n\t}\n\n\t// multiplication\n\tinline number operator * (const number& a, const number& b)\n\t{\n\t\treturn number(a.r * b.r, a.dr * b.r + a.r * b.dr);\n\t}\n\n\tinline number operator * (double a, const number& b)\n\t{\n\t\treturn number(a * b.r, a * b.dr);\n\t}\n\n\tinline number operator * (const number& a, double b)\n\t{\n\t\treturn number(a.r * b, a.dr * b);\n\t}\n\n\t// division\n\tinline number operator / (const number& a, const number& b)\n\t{\n\t\treturn number(a.r / b.r, (a.dr - a.r * b.dr / b.r) / b.r);\n\t}\n\n\tinline number operator / (double a, const number& b)\n\t{\n\t\treturn number(a / b.r, -a * b.dr / (b.r * b.r));\n\t}\n\n\tinline number operator / (const number& a, double b)\n\t{\n\t\treturn number(a.r / b, a.dr / b);\n\t}\n\n\t// math functions\n\tinline number log(const number& a)\n\t{\n\t\treturn number(::log(a.r), a.dr / a.r);\n\t}\n\n\tinline number sqrt(const number& a)\n\t{\n\t\tdouble s = ::sqrt(a.r);\n\t\treturn number(s, 0.5 * a.dr / s);\n\t}\n\n\tinline number exp(const number& a)\n\t{\n\t\tdouble e = ::exp(a.r);\n\t\treturn number(e, e * a.dr);\n\t}\n\n\tinline number pow(const number& a, double e)\n\t{\n\t\tif (e == 0.0) return number(1.0);\n\t\tdouble b = ::pow(a.r, e - 1.0);\n\t\treturn number(a.r * b, e * b * a.dr);\n\t}\n\n\tinline number sin(const number& a)\n\t{\n\t\treturn number(::sin(a.r), ::cos(a.r) * a.dr);\n\t}\n\n\tinline number cos(const number& a)\n\t{\n\t\treturn number(::cos(a.r), -::sin(a.r) * a.dr);\n\t}\n\n\tinline number cosh(const number& a)\n\t{\n\t\treturn number(::cosh(a.r), ::sinh(a.r) * a.dr);\n\t}\n\n\tinline number sinh(const number& a)\n\t{\n\t\treturn number(::sinh(a.r), ::cosh(a.r) * a.dr);\n\t}\n\n\tstruct vec3d\n\t{\n\t\tnumber x, y, z;\n\t\tvec3d() {}\n\t\tvec3d(const ::vec3d& a) : x(a.x), y(a.y), z(a.z) {}\n\t\tvec3d(double a, double b, double c) : x(a), y(b), z(c) {}\n\t\tvec3d(const number& a, const number& b, const number& c) : x(a), y(b), z(c) {}\n\n\t\t::vec3d values() const { return ::vec3d(x.r, y.r, z.r); }\n\t\t::vec3d partials() const { return ::vec3d(x.dr, y.dr, z.dr); }\n\n\t\tnumber& operator [] (size_t n) { return (&x)[n]; }\n\n\t\tnumber length() const { return sqrt(x * x + y * y + z * z); }\n\t};\n\n\tinline number operator * (const vec3d& a, const vec3d& b)\n\t{\n\t\treturn a.x * b.x + a.y * b.y + a.z * b.z;\n\t}\n\n\tinline vec3d operator * (double a, const vec3d& b)\n\t{\n\t\treturn vec3d(a * b.x, a * b.y, a * b.z);\n\t}\n\n\tinline vec3d operator * (const number& a, const vec3d& b)\n\t{\n\t\treturn vec3d(a * b.x, a * b.y, a * b.z);\n\t}\n\n\tinline vec3d operator * (const vec3d& a, double b)\n\t{\n\t\treturn vec3d(a.x * b, a.y * b, a.z * b);\n\t}\n\n\tinline vec3d operator * (const vec3d& a, const number& b)\n\t{\n\t\treturn vec3d(a.x * b, a.y * b, a.z * b);\n\t}\n\n\tinline vec3d operator + (const vec3d& a, const vec3d& b)\n\t{\n\t\treturn vec3d(a.x + b.x, a.y + b.y, a.z + b.z);\n\t}\n\n\tinline vec3d operator - (const vec3d& a, const vec3d& b)\n\t{\n\t\treturn vec3d(a.x - b.x, a.y - b.y, a.z - b.z);\n\t}\n\n\tinline vec3d operator - (const vec3d& a)\n\t{\n\t\treturn vec3d(-a.x, -a.y, -a.z);\n\t}\n\n\tinline vec3d cross(const vec3d& a, const vec3d& b)\n\t{\n\t\treturn vec3d(a.y * b.z - a.z * b.y,\n\t\t\ta.z * b.x - a.x * b.z,\n\t\t\ta.x * b.y - a.y * b.x);\n\t}\n\n\t// alternative cross product operator. \n\t// Make sure to include parentheses when using this operator since ^ has a lower precedence than some other operators.\n\t// e.g. use (a ^ b) * c instead of a ^ b * c\n\tinline vec3d operator ^ (const vec3d& a, const vec3d& b)\n\t{\n\t\treturn cross(a, b);\n\t}\n\n\tFECORE_API double Evaluate(std::function<number(ad::vec3d&)> W, const ::vec3d& a);\n\tFECORE_API::vec3d Grad(std::function<number(ad::vec3d&)> W, const ::vec3d& a);\n\tFECORE_API::mat3d Grad(std::function<ad::vec3d(ad::vec3d&)> F, const ::vec3d& a);\n\n\tstruct mat3d\n\t{\n\t\tnumber m[3][3];\n\t\tnumber* operator [] (size_t n) { return m[n]; }\n\t\tconst number* operator [] (size_t n) const { return m[n]; }\n\n\t\tmat3d() {}\n\t\tmat3d(const ::mat3d& A)\n\t\t{\n\t\t\tfor (int i = 0; i < 3; ++i)\n\t\t\t\tfor (int j = 0; j < 3; ++j) m[i][j] = A[i][j];\n\t\t}\n\t\tmat3d(const number& a11, const number& a12, const number& a13,\n\t\t\tconst number& a21, const number& a22, const number& a23,\n\t\t\tconst number& a31, const number& a32, const number& a33)\n\t\t{\n\t\t\tm[0][0] = a11; m[0][1] = a12; m[0][2] = a13;\n\t\t\tm[1][0] = a21; m[1][1] = a22; m[1][2] = a23;\n\t\t\tm[2][0] = a31; m[2][1] = a32; m[2][2] = a33;\n\t\t}\n\n\t\tmat3d(double d)\n\t\t{\n\t\t\tm[0][0].r = d; m[0][1].r = 0.0; m[0][2].r = 0.0;\n\t\t\tm[1][0].r = 0.0; m[1][1].r = d; m[1][2].r = 0.0;\n\t\t\tm[2][0].r = 0.0; m[2][1].r = 0.0; m[2][2].r = d;\n\t\t}\n\t\t::mat3d values() const\n\t\t{\n\t\t\treturn ::mat3d(m[0][0].r, m[0][1].r, m[0][2].r,\n\t\t\t\tm[1][0].r, m[1][1].r, m[1][2].r,\n\t\t\t\tm[2][0].r, m[2][1].r, m[2][2].r);\n\t\t}\n\t\t::mat3d partials() const\n\t\t{\n\t\t\treturn ::mat3d(m[0][0].dr, m[0][1].dr, m[0][2].dr,\n\t\t\t\tm[1][0].dr, m[1][1].dr, m[1][2].dr,\n\t\t\t\tm[2][0].dr, m[2][1].dr, m[2][2].dr);\n\t\t}\n\t};\n\n\tinline mat3d operator * (const mat3d& A, const mat3d& B)\n\t{\n\t\tmat3d C(0.0);\n\t\tfor (int i = 0; i < 3; ++i)\n\t\t\tfor (int j = 0; j < 3; ++j)\n\t\t\t\tfor (int k = 0; k < 3; ++k)\n\t\t\t\t\tC[i][j] = C[i][j] + A[i][k] * B[k][j];\n\t\treturn C;\n\t}\n\n\tinline vec3d operator * (const mat3d& A, const vec3d& v)\n\t{\n\t\treturn vec3d(\n\t\t\tA[0][0] * v.x + A[0][1] * v.y + A[0][2] * v.z,\n\t\t\tA[1][0] * v.x + A[1][1] * v.y + A[1][2] * v.z,\n\t\t\tA[2][0] * v.x + A[2][1] * v.y + A[2][2] * v.z\n\t\t);\n\t}\n\n\tinline mat3d dyad(const vec3d& a)\n\t{\n\t\treturn mat3d(\n\t\t\ta.x * a.x, a.x * a.y, a.x * a.z,\n\t\t\ta.y * a.x, a.y * a.y, a.y * a.z,\n\t\t\ta.z * a.x, a.z * a.y, a.z * a.z\n\t\t);\n\t}\n\n\tinline mat3d dyad(const vec3d& a, const vec3d& b)\n\t{\n\t\treturn mat3d(\n\t\t\ta.x * b.x, a.x * b.y, a.x * b.z,\n\t\t\ta.y * b.x, a.y * b.y, a.y * b.z,\n\t\t\ta.z * b.x, a.z * b.y, a.z * b.z\n\t\t);\n\t}\n\n\tstruct mat3ds\n\t{\n\t\t// This enumeration can be used to remember the order\n\t\t// in which the components are stored.\n\t\tenum {\n\t\t\tXX = 0,\n\t\t\tXY = 1,\n\t\t\tYY = 2,\n\t\t\tXZ = 3,\n\t\t\tYZ = 4,\n\t\t\tZZ = 5\n\t\t};\n\n\t\tnumber m[6]; // {xx,xy,yy,xz,yz,zz}\n\t\tnumber& operator [] (size_t n) { return m[n]; }\n\t\tmat3ds() {}\n\t\tmat3ds(\n\t\t\tconst number& xx,\n\t\t\tconst number& yy,\n\t\t\tconst number& zz,\n\t\t\tconst number& xy,\n\t\t\tconst number& yz,\n\t\t\tconst number& xz)\n\t\t{\n\t\t\tm[XX] = xx;\n\t\t\tm[YY] = yy;\n\t\t\tm[ZZ] = zz;\n\t\t\tm[XY] = xy;\n\t\t\tm[YZ] = yz;\n\t\t\tm[XZ] = xz;\n\t\t}\n\n\t\tmat3ds(const ::mat3ds& C)\n\t\t{\n\t\t\tm[0] = C.xx();\n\t\t\tm[1] = C.xy();\n\t\t\tm[2] = C.yy();\n\t\t\tm[3] = C.xz();\n\t\t\tm[4] = C.yz();\n\t\t\tm[5] = C.zz();\n\t\t}\n\n\t\tmat3ds(double d)\n\t\t{\n\t\t\tm[XX].r = d;\n\t\t\tm[YY].r = d;\n\t\t\tm[ZZ].r = d;\n\t\t}\n\n\t\tnumber& xx() { return m[XX]; }\n\t\tnumber& yy() { return m[YY]; }\n\t\tnumber& zz() { return m[ZZ]; }\n\t\tnumber& xy() { return m[XY]; }\n\t\tnumber& yz() { return m[YZ]; }\n\t\tnumber& xz() { return m[XZ]; }\n\n\t\tconst number& xx() const { return m[XX]; }\n\t\tconst number& yy() const { return m[YY]; }\n\t\tconst number& zz() const { return m[ZZ]; }\n\t\tconst number& xy() const { return m[XY]; }\n\t\tconst number& yz() const { return m[YZ]; }\n\t\tconst number& xz() const { return m[XZ]; }\n\n\t\t::mat3ds values() const\n\t\t{\n\t\t\treturn ::mat3ds(m[XX].r, m[YY].r, m[ZZ].r, m[XY].r, m[YZ].r, m[XZ].r);\n\t\t}\n\n\t\t::mat3ds partials() const\n\t\t{\n\t\t\treturn ::mat3ds(m[XX].dr, m[YY].dr, m[ZZ].dr, m[XY].dr, m[YZ].dr, m[XZ].dr);\n\t\t}\n\n\t\t// functions\n\t\tnumber tr() const { return m[XX] + m[YY] + m[ZZ]; }\n\n\t\tnumber det() const {\n\t\t\treturn (m[XX] * (m[YY] * m[ZZ] - m[YZ] * m[YZ])\n\t\t\t\t+ m[XY] * (m[YZ] * m[XZ] - m[ZZ] * m[XY])\n\t\t\t\t+ m[XZ] * (m[XY] * m[YZ] - m[YY] * m[XZ]));\n\t\t}\n\n\t\t// double contraction\n\t\tnumber dotdot(const mat3ds& B) const\n\t\t{\n\t\t\tconst number* n = B.m;\n\t\t\treturn m[XX] * n[XX] + m[YY] * n[YY] + m[ZZ] * n[ZZ] + 2.0 * (m[XY] * n[XY] + m[YZ] * n[YZ] + m[XZ] * n[XZ]);\n\t\t}\n\n\t\tmat3ds inverse() const\n\t\t{\n\t\t\tnumber Di = 1.0 / det();\n\n\t\t\treturn mat3ds(\n\t\t\t\tDi * (m[YY] * m[ZZ] - m[YZ] * m[YZ]),\n\t\t\t\tDi * (m[XX] * m[ZZ] - m[XZ] * m[XZ]),\n\t\t\t\tDi * (m[XX] * m[YY] - m[XY] * m[XY]),\n\t\t\t\tDi * (m[XZ] * m[YZ] - m[XY] * m[ZZ]),\n\t\t\t\tDi * (m[XY] * m[XZ] - m[XX] * m[YZ]),\n\t\t\t\tDi * (m[XY] * m[YZ] - m[YY] * m[XZ]));\n\t\t}\n\n\t\t// return the square \n\t\tmat3ds sqr() const\n\t\t{\n\t\t\treturn mat3ds(\n\t\t\t\tm[XX] * m[XX] + m[XY] * m[XY] + m[XZ] * m[XZ],\n\t\t\t\tm[XY] * m[XY] + m[YY] * m[YY] + m[YZ] * m[YZ],\n\t\t\t\tm[XZ] * m[XZ] + m[YZ] * m[YZ] + m[ZZ] * m[ZZ],\n\t\t\t\tm[XX] * m[XY] + m[XY] * m[YY] + m[XZ] * m[YZ],\n\t\t\t\tm[XY] * m[XZ] + m[YY] * m[YZ] + m[YZ] * m[ZZ],\n\t\t\t\tm[XX] * m[XZ] + m[XY] * m[YZ] + m[XZ] * m[ZZ]\n\t\t\t);\n\t\t}\n\t};\n\n\t// arithmetic operations\n\tinline mat3ds operator + (const mat3ds& A, const mat3ds& B)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() + B.xx(),\n\t\t\tA.yy() + B.yy(),\n\t\t\tA.zz() + B.zz(),\n\t\t\tA.xy() + B.xy(),\n\t\t\tA.yz() + B.yz(),\n\t\t\tA.xz() + B.xz()\n\t\t);\n\t}\n\n\tinline mat3ds operator - (const mat3ds& A, const mat3ds& B)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() - B.xx(),\n\t\t\tA.yy() - B.yy(),\n\t\t\tA.zz() - B.zz(),\n\t\t\tA.xy() - B.xy(),\n\t\t\tA.yz() - B.yz(),\n\t\t\tA.xz() - B.xz()\n\t\t);\n\t}\n\n\tinline mat3ds operator * (const mat3ds& A, double b)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() * b,\n\t\t\tA.yy() * b,\n\t\t\tA.zz() * b,\n\t\t\tA.xy() * b,\n\t\t\tA.yz() * b,\n\t\t\tA.xz() * b\n\t\t);\n\t}\n\n\tinline mat3ds operator * (double a, const mat3ds& B)\n\t{\n\t\treturn mat3ds(\n\t\t\tB.xx() * a,\n\t\t\tB.yy() * a,\n\t\t\tB.zz() * a,\n\t\t\tB.xy() * a,\n\t\t\tB.yz() * a,\n\t\t\tB.xz() * a\n\t\t);\n\t}\n\n\tinline mat3ds operator * (const mat3ds& A, const number& b)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() * b,\n\t\t\tA.yy() * b,\n\t\t\tA.zz() * b,\n\t\t\tA.xy() * b,\n\t\t\tA.yz() * b,\n\t\t\tA.xz() * b\n\t\t);\n\t}\n\n\tFECORE_API double Evaluate(std::function<number(mat3ds& C)> W, const ::mat3ds& C);\n\tFECORE_API::mat3ds Derive(std::function<number(mat3ds& C)> W, const ::mat3ds& C);\n\tFECORE_API::tens4ds Derive(std::function<mat3ds(mat3ds& C)> S, const ::mat3ds& C);\n}\n\n// This namespace contains classes and functions that can be used to evaluate directional \n// derivatives for vec3d and quatd variables automatically. \n// To use this, first create some variables, either dd::vec3d or dd::quat3d.\n// For example,\n// \n//  dd::vec3d r(1,0,0);\n//  dd::quatd q(1,0,0);\n//\n// then, create functions using these variables. For example,\n//\n//  auto F = [&]() { \n//    ::vec3d z0(0, 1, 0);\n//    return r + q*z0;\n// };\n//\n// Make sure to capture by reference!\n// Finally, to evaluate the directional derivatives: \n//\n//  mat3d dFr = dd::D(F, r);\n//  mat3d dFq = dd::D(F, q);\n//\nnamespace dd {\n\n\t// this class defines a number that can be used in calculations.\n\t// Never initialize directly. Instead, use vec3d operators to construct numbers.\n\t// For example.\n\t// dd::vec3d r(1,0,0);\n\t// dd::number l = dd::sqrt(r*r);\n\tstruct number {\n\t\tdouble v = 0;\n\t\t::vec3d dv = ::vec3d(0.0, 0.0, 0.0);\n\t};\n\n\tinline dd::number operator - (double a, const dd::number& n)\n\t{\n\t\treturn { a - n.v, -n.dv };\n\t}\n\n\tinline dd::number operator * (double a, const dd::number& n)\n\t{\n\t\treturn { a * n.v, n.dv * a };\n\t}\n\n\tinline dd::number operator * (const dd::number& n, double a)\n\t{\n\t\treturn { a * n.v, n.dv * a };\n\t}\n\n\tinline dd::number operator / (double a, const dd::number& n)\n\t{\n\t\treturn { a / n.v, n.dv * (-a / (n.v * n.v)) };\n\t}\n\n\tinline dd::number sqrt(const dd::number& a)\n\t{\n\t\treturn { ::sqrt(a.v), a.dv * (0.5 / ::sqrt(a.v)) };\n\t};\n\n\t// represents vector variables.\n\tstruct vec3d {\n\t\t::vec3d v = ::vec3d(0, 0, 0);\n\t\t::mat3d dv = ::mat3d(0.0);\n\n\t\tvoid activate(bool b) { dv = (b ? ::mat3d::identity() : ::mat3d(0.0)); }\n\n\t\tvec3d(::vec3d a) : v(a), dv(::mat3d(0.0)) {}\n\t\tvec3d(::vec3d a, ::mat3d da) : v(a), dv(da) {}\n\t\tvec3d(double x, double y, double z) : v(::vec3d(x, y, z)), dv(::mat3d(0.0)) {}\n\t};\n\n\tinline dd::number operator * (const dd::vec3d& a, const dd::vec3d& b)\n\t{\n\t\treturn dd::number{ a.v * b.v, b.dv.transpose() * a.v + a.dv.transpose() * b.v };\n\t}\n\n\tinline dd::vec3d operator * (const dd::vec3d& r, const dd::number& a)\n\t{\n\t\treturn { r.v * a.v, (r.v & a.dv) + r.dv*a.v };\n\t}\n\n\tinline dd::vec3d operator - (const dd::vec3d& a)\n\t{\n\t\treturn { -a.v, -a.dv };\n\t}\n\n\tinline dd::vec3d operator + (const dd::vec3d& a, const dd::vec3d& b)\n\t{\n\t\treturn { a.v + b.v, a.dv + b.dv };\n\t}\n\n\tinline dd::vec3d operator - (const dd::vec3d& a, const dd::vec3d& b)\n\t{\n\t\treturn { a.v - b.v, a.dv - b.dv };\n\t}\n\n\tinline dd::vec3d operator ^ (const dd::vec3d& a, const dd::vec3d& b)\n\t{\n\t\t::mat3da ahat(a.v);\n\t\t::mat3da bhat(b.v);\n\t\treturn { (a.v ^ b.v), ahat * b.dv - bhat * a.dv };\n\t}\n\n\tinline dd::vec3d operator * (const dd::vec3d& a, double s)\n\t{\n\t\treturn { a.v * s, a.dv * s };\n\t}\n\n\t// represents rotational variables\n\tstruct quatd {\n\t\t::quatd q;\n\t\t::mat3d dq;\n\n\t\tquatd(::quatd r) { q = r; dq = ::mat3d(0.0); }\n\t\tquatd(::vec3d r) { q = ::quatd(r); dq = ::mat3d(0.0); }\n\t\tquatd(double x, double y, double z) { q = ::quatd(::vec3d(x,y,z)); dq = ::mat3d(0.0); }\n\t\tquatd(double x, double y, double z, double w) { q = ::quatd(x,y,z,w); dq = ::mat3d(0.0); }\n\n\t\tvoid activate(bool b) { dq = (b ? ::mat3d::identity() : ::mat3d(0.0)); }\n\n\t\tdd::vec3d operator * (const ::vec3d& a)\n\t\t{\n\t\t\t::vec3d qa = q * a;\n\t\t\treturn { qa, ::mat3da(-qa) * dq };\n\t\t}\n\t};\n\n\t// Use this function to return the directional derivative of \"F\" w.r.t. \"r\".\n\ttemplate <typename T>\n\t::mat3d D(std::function<dd::vec3d()> F, T& r)\n\t{\n\t\tr.activate(true);\n\t\t::mat3d dF = F().dv;\n\t\tr.activate(false);\n\t\treturn dF;\n\t}\n}\n"
  },
  {
    "path": "FECore/ad2.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"ad2.h\"\n\ndouble ad2::Evaluate(std::function<ad2::number(ad2::mat3ds& C)> W, const ::mat3ds& C)\n{\n\tad2::mat3ds dC(C);\n\treturn W(dC).r;\n}\n\n::mat3ds ad2::Derive(std::function<ad2::number(ad2::mat3ds& C)> W, const ::mat3ds& C)\n{\n\tad2::mat3ds dC(C);\n\tdouble S[6] = { 0.0 };\n\tfor (int i = 0; i < 6; ++i)\n\t{\n\t\tdC[i].d1 = 1;\n\t\tS[i] = W(dC).d1;\n\t\tdC[i].d1 = 0;\n\t}\n\treturn ::mat3ds(S[0], S[2], S[5], 0.5 * S[1], 0.5 * S[4], 0.5 * S[3]);\n}\n\n::tens4ds ad2::Derive2(std::function<ad2::number(ad2::mat3ds& C)> W, const ::mat3ds& C)\n{\n\tconstexpr int l[6] = { 0, 2, 5, 1, 4, 3 };\n\tconstexpr double w[6] = { 1.0, 1.0, 1.0, 0.5, 0.5, 0.5 };\n\tad2::mat3ds dC(C);\n\tdouble D[6][6] = { 0 };\n\tfor (int i = 0; i < 6; ++i)\n\t\tfor (int j = 0; j < 6; ++j)\n\t\t{\n\t\t\tdC[l[i]].d1 = 1;\n\t\t\tdC[l[j]].d2 = 1;\n\t\t\tdouble ddW = W(dC).dd;\n\t\t\tdC[l[i]].d1 = 0;\n\t\t\tdC[l[j]].d2 = 0;\n\n\t\t\tD[i][j] = ddW*w[i]*w[j];\n\t\t}\n\n\treturn ::tens4ds(D);\n}\n"
  },
  {
    "path": "FECore/ad2.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"tens4d.h\"\n#include \"fecore_api.h\"\n#include <functional>\n\nnamespace ad2 {\n\n\tstruct number {\n\t\tdouble r, d1, d2, dd;\n\t\tnumber() : r(0.0), d1(0.0), d2(0.0), dd(0.0) {}\n\t\tnumber(double v, double v1 = 0, double v2=0, double ddv = 0) : r(v), d1(v1), d2(v2), dd(ddv) {}\n\t\tvoid operator = (const double& a) { r = a; d1 = d2 = dd = 0.0; }\n\t};\n\n\t// addition\n\tinline number operator + (const number& a, const number& b)\n\t{\n\t\treturn number(a.r + b.r, a.d1 + b.d1, a.d2 + b.d2, a.dd + b.dd);\n\t}\n\n\tinline number operator + (double a, const number& b)\n\t{\n\t\treturn number(a + b.r, b.d1, b.d2, b.dd);\n\t}\n\n\tinline number operator + (const number& a, double b)\n\t{\n\t\treturn number(a.r + b, a.d1, a.d2, a.dd);\n\t}\n\n\t// subtraction\n\tinline number operator - (const number& a, const number& b)\n\t{\n\t\treturn number(a.r - b.r, a.d1 - b.d1, a.d2 - b.d2, a.dd - b.dd);\n\t}\n\n\tinline number operator - (double a, const number& b)\n\t{\n\t\treturn number(a - b.r, -b.d1, -b.d2, -b.dd);\n\t}\n\n\tinline number operator - (const number& a, double b)\n\t{\n\t\treturn number(a.r - b, a.d1, a.d2, a.dd);\n\t}\n\n\t// multiplication\n\tinline number operator * (const number& a, const number& b)\n\t{\n\t\treturn number(\n\t\t\ta.r * b.r,\n\t\t\ta.d1 * b.r + a.r * b.d1,\n\t\t\ta.d2 * b.r + a.r * b.d2,\n\t\t\ta.dd * b.r + a.d1 * b.d2 + a.d2 * b.d1 + a.r * b.dd\n\t\t\t);\n\t}\n\n\tinline number operator * (double a, const number& b)\n\t{\n\t\treturn number(\n\t\t\ta * b.r,\n\t\t\ta * b.d1,\n\t\t\ta * b.d2,\n\t\t\ta * b.dd\n\t\t);\n\t}\n\n\tinline number operator * (const number& a, double b)\n\t{\n\t\treturn number(\n\t\t\ta.r * b,\n\t\t\ta.d1 * b,\n\t\t\ta.d2 * b,\n\t\t\ta.dd * b\n\t\t);\n\t}\n\n\t// invert\n\tinline number inv(const number& a)\n\t{\n\t\treturn number(\n\t\t\t1.0/a.r,\n\t\t\t-a.d1/(a.r*a.r),\n\t\t\t-a.d2/(a.r*a.r),\n\t\t\t((2.0 * a.d1 * a.d2) / a.r - a.dd)/(a.r*a.r)\n\t\t);\n\t}\n\n\t// division\n\tinline number operator / (const number& a, const number& b)\n\t{\n\t\treturn a*inv(b);\n\t}\n\n\tinline number operator / (double a, const number& b)\n\t{\n\t\treturn a * inv(b);\n\t}\n\n\tinline number operator / (const number& a, double b)\n\t{\n\t\treturn number(\n\t\t\ta.r / b,\n\t\t\ta.d1 / b,\n\t\t\ta.d2 / b,\n\t\t\ta.dd / b\n\t\t);\n\t}\n\n\t// math functions\n\tinline number log(const number& a)\n\t{\n\t\treturn number(\n\t\t\t::log(a.r), \n\t\t\ta.d1 / a.r,\n\t\t\ta.d2 / a.r,\n\t\t\t(a.dd - a.d1*a.d2/a.r)/a.r\n\t\t\t);\n\t}\n\n\tinline number sqrt(const number& a)\n\t{\n\t\tdouble s = ::sqrt(a.r);\n\t\treturn number(\n\t\t\ts, \n\t\t\t0.5 * a.d1 / s,\n\t\t\t0.5 * a.d2 / s,\n\t\t\t0.5 * a.dd / s - 0.25*a.d1*a.d2/(s*s*s)\n\t\t\t);\n\t}\n\n\tinline number exp(const number& a)\n\t{\n\t\tdouble e = ::exp(a.r);\n\t\treturn number(\n\t\t\te, \n\t\t\te * a.d1,\n\t\t\te * a.d2,\n\t\t\te * (a.d1*a.d2 + a.dd)\n\t\t\t);\n\t}\n\n\tinline number pow(const number& a, double e)\n\t{\n\t\tif (e == 0.0) return number(1.0);\n\n\t\tdouble b = ::pow(a.r, e - 2.0);\n\t\treturn number(\n\t\t\tb * a.r * a.r, \n\t\t\te * a.r * b * a.d1,\n\t\t\te * a.r * b * a.d2,\n\t\t\te * b * ((e - 1.0)* a.d1 * a.d2 + a.r * a.dd)\n\t\t);\n\t}\n\n\tinline number sin(const number& a)\n\t{\n\t\tdouble sa = ::sin(a.r);\n\t\tdouble ca = ::cos(a.r);\n\t\treturn number(\n\t\t\tsa,\n\t\t\tca * a.d1,\n\t\t\tca * a.d2,\n\t\t\tca * a.dd - sa * a.d1 * a.d2\n\t\t);\n\t}\n\n\tinline number cos(const number& a)\n\t{\n\t\tdouble sa = ::sin(a.r);\n\t\tdouble ca = ::cos(a.r);\n\t\treturn number(\n\t\t\tca,\n\t\t\t-sa*a.d1,\n\t\t\t-sa*a.d2,\n\t\t\t-sa*a.dd - ca*a.d1*a.d2\n\t\t\t);\n\t}\n\n\tinline number cosh(const number& a)\n\t{\n\t\tdouble sa = ::sinh(a.r);\n\t\tdouble ca = ::cosh(a.r);\n\t\treturn number(\n\t\t\tca,\n\t\t\tsa * a.d1,\n\t\t\tsa * a.d2,\n\t\t\tca * a.d1 * a.d2 + sa * a.dd\n\t\t\t);\n\t}\n\n\tinline number sinh(const number& a)\n\t{\n\t\tdouble sa = ::sinh(a.r);\n\t\tdouble ca = ::cosh(a.r);\n\t\treturn number(\n\t\t\tsa,\n\t\t\tca * a.d1,\n\t\t\tca * a.d2,\n\t\t\tsa * a.d1 * a.d2 + ca * a.dd\n\t\t);\n\t}\n\n\tstruct mat3ds\n\t{\n\t\t// This enumeration can be used to remember the order\n\t\t// in which the components are stored.\n\t\tenum {\n\t\t\tXX = 0,\n\t\t\tXY = 1,\n\t\t\tYY = 2,\n\t\t\tXZ = 3,\n\t\t\tYZ = 4,\n\t\t\tZZ = 5\n\t\t};\n\n\t\tnumber m[6]; // {xx,xy,yy,xz,yz,zz}\n\t\tnumber& operator [] (size_t n) { return m[n]; }\n\t\tmat3ds() {}\n\t\tmat3ds(\n\t\t\tconst number& xx,\n\t\t\tconst number& yy,\n\t\t\tconst number& zz,\n\t\t\tconst number& xy,\n\t\t\tconst number& yz,\n\t\t\tconst number& xz)\n\t\t{\n\t\t\tm[XX] = xx;\n\t\t\tm[YY] = yy;\n\t\t\tm[ZZ] = zz;\n\t\t\tm[XY] = xy;\n\t\t\tm[YZ] = yz;\n\t\t\tm[XZ] = xz;\n\t\t}\n\n\t\tmat3ds(const ::mat3ds& C)\n\t\t{\n\t\t\tm[0] = C.xx();\n\t\t\tm[1] = C.xy();\n\t\t\tm[2] = C.yy();\n\t\t\tm[3] = C.xz();\n\t\t\tm[4] = C.yz();\n\t\t\tm[5] = C.zz();\n\t\t}\n\n\t\tmat3ds(double d)\n\t\t{\n\t\t\tm[XX].r = d;\n\t\t\tm[YY].r = d;\n\t\t\tm[ZZ].r = d;\n\t\t}\n\n\t\tnumber& xx() { return m[XX]; }\n\t\tnumber& yy() { return m[YY]; }\n\t\tnumber& zz() { return m[ZZ]; }\n\t\tnumber& xy() { return m[XY]; }\n\t\tnumber& yz() { return m[YZ]; }\n\t\tnumber& xz() { return m[XZ]; }\n\n\t\tconst number& xx() const { return m[XX]; }\n\t\tconst number& yy() const { return m[YY]; }\n\t\tconst number& zz() const { return m[ZZ]; }\n\t\tconst number& xy() const { return m[XY]; }\n\t\tconst number& yz() const { return m[YZ]; }\n\t\tconst number& xz() const { return m[XZ]; }\n\n\t\t// functions\n\t\tnumber tr() const { return m[XX] + m[YY] + m[ZZ]; }\n\n\t\tnumber det() const {\n\t\t\treturn (m[XX] * (m[YY] * m[ZZ] - m[YZ] * m[YZ])\n\t\t\t\t+ m[XY] * (m[YZ] * m[XZ] - m[ZZ] * m[XY])\n\t\t\t\t+ m[XZ] * (m[XY] * m[YZ] - m[YY] * m[XZ]));\n\t\t}\n\n\t\t// double contraction\n\t\tnumber dotdot(const mat3ds& B) const\n\t\t{\n\t\t\tconst number* n = B.m;\n\t\t\treturn m[XX] * n[XX] + m[YY] * n[YY] + m[ZZ] * n[ZZ] + 2.0 * (m[XY] * n[XY] + m[YZ] * n[YZ] + m[XZ] * n[XZ]);\n\t\t}\n\n\t\tmat3ds inverse() const\n\t\t{\n\t\t\tnumber Di = 1.0 / det();\n\n\t\t\treturn mat3ds(\n\t\t\t\tDi * (m[YY] * m[ZZ] - m[YZ] * m[YZ]),\n\t\t\t\tDi * (m[XX] * m[ZZ] - m[XZ] * m[XZ]),\n\t\t\t\tDi * (m[XX] * m[YY] - m[XY] * m[XY]),\n\t\t\t\tDi * (m[XZ] * m[YZ] - m[XY] * m[ZZ]),\n\t\t\t\tDi * (m[XY] * m[XZ] - m[XX] * m[YZ]),\n\t\t\t\tDi * (m[XY] * m[YZ] - m[YY] * m[XZ]));\n\t\t}\n\n\t\t// return the square \n\t\tmat3ds sqr() const\n\t\t{\n\t\t\treturn mat3ds(\n\t\t\t\tm[XX] * m[XX] + m[XY] * m[XY] + m[XZ] * m[XZ],\n\t\t\t\tm[XY] * m[XY] + m[YY] * m[YY] + m[YZ] * m[YZ],\n\t\t\t\tm[XZ] * m[XZ] + m[YZ] * m[YZ] + m[ZZ] * m[ZZ],\n\t\t\t\tm[XX] * m[XY] + m[XY] * m[YY] + m[XZ] * m[YZ],\n\t\t\t\tm[XY] * m[XZ] + m[YY] * m[YZ] + m[YZ] * m[ZZ],\n\t\t\t\tm[XX] * m[XZ] + m[XY] * m[YZ] + m[XZ] * m[ZZ]\n\t\t\t);\n\t\t}\n\t};\n\n\t// arithmetic operations\n\tinline mat3ds operator + (const mat3ds& A, const mat3ds& B)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() + B.xx(),\n\t\t\tA.yy() + B.yy(),\n\t\t\tA.zz() + B.zz(),\n\t\t\tA.xy() + B.xy(),\n\t\t\tA.yz() + B.yz(),\n\t\t\tA.xz() + B.xz()\n\t\t);\n\t}\n\n\tinline mat3ds operator - (const mat3ds& A, const mat3ds& B)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() - B.xx(),\n\t\t\tA.yy() - B.yy(),\n\t\t\tA.zz() - B.zz(),\n\t\t\tA.xy() - B.xy(),\n\t\t\tA.yz() - B.yz(),\n\t\t\tA.xz() - B.xz()\n\t\t);\n\t}\n\n\tinline mat3ds operator * (const mat3ds& A, double b)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() * b,\n\t\t\tA.yy() * b,\n\t\t\tA.zz() * b,\n\t\t\tA.xy() * b,\n\t\t\tA.yz() * b,\n\t\t\tA.xz() * b\n\t\t);\n\t}\n\n\tinline mat3ds operator * (double a, const mat3ds& B)\n\t{\n\t\treturn mat3ds(\n\t\t\tB.xx() * a,\n\t\t\tB.yy() * a,\n\t\t\tB.zz() * a,\n\t\t\tB.xy() * a,\n\t\t\tB.yz() * a,\n\t\t\tB.xz() * a\n\t\t);\n\t}\n\n\tinline mat3ds operator * (const mat3ds& A, const number& b)\n\t{\n\t\treturn mat3ds(\n\t\t\tA.xx() * b,\n\t\t\tA.yy() * b,\n\t\t\tA.zz() * b,\n\t\t\tA.xy() * b,\n\t\t\tA.yz() * b,\n\t\t\tA.xz() * b\n\t\t);\n\t}\n\n\tFECORE_API double Evaluate(std::function<number(mat3ds& C)> W, const ::mat3ds& C);\n\tFECORE_API ::mat3ds Derive(std::function<number(mat3ds& C)> W, const ::mat3ds& C);\n\tFECORE_API ::tens4ds Derive2(std::function<number(mat3ds& C)> W, const ::mat3ds& C);\n}\n"
  },
  {
    "path": "FECore/besselIK.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n#include \"gamma.h\"\n#include <limits>\n#include <math.h>\n\n\n#include \"besselIK.h\"\n\n// modified Bessel function of the first kind I0(x) (real x)\ndouble i0(double x)\n{\n    double ax,ans;\n    double y;\n    if ((ax=fabs(x)) < 3.75) {\n        y=x/3.75;\n        y*=y;\n        ans=1.0+y*(3.5156229+y*(3.0899424+y*\n                                (1.2067492+y*\n                                 (0.2659732+y*(0.360768e-1+y*0.45813e-2))\n                                 )\n                                )\n                   );\n    }\n    else {\n        y=3.75/ax;\n        ans=(exp(ax)/sqrt(ax))*(0.39894228+y*\n                                (0.1328592e-1+y*\n                                 (0.225319e-2+y*\n                                  (-0.157565e-2+y*(0.916281e-2 +y*\n                                                   (-0.2057706e-1+y*\n                                                    (0.2635537e-1+y*\n                                                     (-0.1647633e-1 +y*0.392377e-2)\n                                                     )\n                                                    )\n                                                   )\n                                   )\n                                  )\n                                 )\n                                );\n    }\n    return ans;\n}\n\n// modified Bessel function of the first kind I1(x) (real x)\ndouble i1(double x)\n{\n    double ax,ans;\n    double y;\n    if ((ax=fabs(x)) < 3.75) {\n        y=x/3.75;\n        y*=y;\n        ans=ax*(0.5+y*(0.87890594+y*\n                       (0.51498869+y*(0.15084934+y*\n                                      (0.2658733e-1+y*\n                                       (0.301532e-2+y*0.32411e-3)\n                                       )\n                                      )\n                        )\n                       )\n                );\n    }\n    else {\n        y=3.75/ax;\n        ans=0.2282967e-1+y*(-0.2895312e-1+y*\n                            (0.1787654e-1-y*0.420059e-2));\n        ans=0.39894228+y*(-0.3988024e-1+y*(-0.362018e-2+y*\n                                           (0.163801e-2+y*(-0.1031555e-1+y*ans)\n                                            )\n                                           )\n                          );\n        ans *= (exp(ax)/sqrt(ax));\n    }\n    return (x < 0.0) ? -ans : ans;\n}\n\n// modified Bessel function of the second kind K0(x) (real x)\ndouble k0(double x)\n{\n    double y,ans;\n    if (x <= 2.0) {\n        y=x*x/4.0;\n        ans=(-log(x/2.0)*i0(x))+(-0.57721566+y*\n                                 (0.42278420 +y*\n                                  (0.23069756+y*\n                                   (0.3488590e-1+y*\n                                    (0.262698e-2 +y*\n                                     (0.10750e-3+y*0.74e-5)\n                                     )\n                                    )\n                                   )\n                                  )\n                                 );\n    }\n    else {\n        y=2.0/x;\n        ans=(exp(-x)/sqrt(x))*(1.25331414+y*\n                               (-0.7832358e-1 +y*\n                                (0.2189568e-1+y*(-0.1062446e-1+y*\n                                                 (0.587872e-2 +y*\n                                                  (-0.251540e-2+y*0.53208e-3)\n                                                  )\n                                                 )\n                                 )\n                                )\n                               );\n    }\n    return ans;\n}\n\n// modified Bessel function of the second kind K1(x) (real x)\ndouble k1(double x)\n{\n    double y,ans;\n    if (x <= 2.0) {\n        y=x*x/4.0;\n        ans=(log(x/2.0)*i1(x))+(1.0/x)*(1.0+y*\n                                        (0.15443144 +y*\n                                         (-0.67278579+y*\n                                          (-0.18156897+y*\n                                           (-0.1919402e-1 +y*\n                                            (-0.110404e-2+y*\n                                             (-0.4686e-4)\n                                             )\n                                            )\n                                           )\n                                          )\n                                         )\n                                        );\n    }\n    else {\n        y=2.0/x;\n        ans=(exp(-x)/sqrt(x))*(1.25331414+y*\n                               (0.23498619 +y*\n                                (-0.3655620e-1+y*\n                                 (0.1504268e-1+y*\n                                  (-0.780353e-2 +y*\n                                   (0.325614e-2+y*\n                                    (-0.68245e-3)\n                                    )\n                                   )\n                                  )\n                                 )\n                                )\n                               );\n    }\n    return ans;\n}\n\n"
  },
  {
    "path": "FECore/besselIK.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n\n\n// modified Bessel function of the first kind I0(x) (real x)\nFECORE_API double i0(double x);\n\n// modified Bessel function of the first kind I1(x) (real x)\nFECORE_API double i1(double x);\n\n// modified Bessel function of the second kind I0(x) (real x)\nFECORE_API double k0(double x);\n\n// modified Bessel function of the second kind I1(x) (real x)\nFECORE_API double k1(double x);\n\n"
  },
  {
    "path": "FECore/colsol.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"math.h\"\n#include \"fecore_api.h\"\n\n///////////////////////////////////////////////////////////////////////////////\n// LINEAR SOLVER : colsol\n// This solver solves linear system of matrices using a skyline format storage\n// and a column reduction scheme. The symmetric matrix is stored in skyline format, \n// where the values array store the matrix elements that are below the skyline \n// and pointers is an array of indices that point to the diagonal elements of \n// the matrix.\n// The matrix is overwritten with the LDLt factorization and the right hand side\n// vector R is replaced by the solution.\n//\n// The implementation is split into two routines. A matrix factorization and a \n// back substitution part. colsol_factor performs the LDLt factorization while\n// colsol_solve does the backsubstitution. colsol_solve needs to be called after\n// colsol_factor. In order to solve for multiple right hand sides call colsol_factor\n// once and then call colsol_solve with the different right hand sides.\n//\n// Details of the algorithm can be found in Bathe, \"Finite Element Procedures\",\n// section 8.2, page 696 and following\n//\n\nFECORE_API void colsol_factor(int N, double* values, int* pointers)\n{\n\tint i, j, r, mi, mj, mm;\n\tdouble krj;\n\tint pi, pj;\n\n\t// -A- factorize the matrix \n\n\t// repeat over all columns\n\tfor (j=1; j<N; ++j)\n\t{\n\t\t// find the first non-zero row in column j\n\t\tmj = j+1 - pointers[j+1] + pointers[j];\n\n\t\tpj = pointers[j]+j;\n\n\t\t// loop over all rows in column j\n\t\tfor (i=mj+1; i<j; ++i)\n\t\t{\n\t\t\t// find the first non-zero row in column i\n\t\t\tmi = i+1 - pointers[i+1] + pointers[i];\n\n\t\t\t// determine max of mi and mj\n\t\t\tmm = (mi > mj ? mi : mj);\n\n\t\t\tpi = pointers[i]+i;\n\n\t\t\tdouble& kij = values[pj - i];\n\n\t\t\t// the next line is replaced by the piece of code between arrows\n\t\t\t// where the r loop is unrolled to give this algorithm a \n\t\t\t// significant boost in speed. \n\t\t\t// Although on good compilers this should not do much,\n\t\t\t// on compilers that do a poor optimization this trick can\n\t\t\t// double the speed of this algorithm.\n\n//\t\t\tfor (r=mm; r<i; ++r) kij -= values[pi - r]*values[pj - r];\n\n//-------------->\n\t\t\tfor (r=mm; r<i-7; r+=8) \n\t\t\t{\n\t\t\t\tkij -= values[pi - r  ]*values[pj - r  ] +\n\t\t\t\t       values[pi - r-1]*values[pj - r-1] +\n\t\t\t\t       values[pi - r-2]*values[pj - r-2] +\n\t\t\t\t       values[pi - r-3]*values[pj - r-3] +\n\t\t\t\t       values[pi - r-4]*values[pj - r-4] +\n\t\t\t\t       values[pi - r-5]*values[pj - r-5] +\n\t\t\t\t       values[pi - r-6]*values[pj - r-6] +\n\t\t\t\t       values[pi - r-7]*values[pj - r-7];\n\t\t\t}\n\n\t\t\tfor (r=0; r<(i-mm)%8; ++r)\n\t\t\t\t\tkij -= values[pi - (i-1)+r]*values[pj - (i-1)+r];\n//-------------->\n\n\t\t}\n\n\t\t// determine l[i][j]\n\t\tfor (i=mj; i<j; ++i) values[pj - i] /= values[ pointers[i] ];\n\n\t\t// calculate d[j][j] value\n\t\tdouble& kjj = values[ pointers[j] ];\n\t\tfor (r=mj; r<j; ++r) \n\t\t{\n\t\t\tkrj = values[pj - r];\n\t\t\tkjj -= krj*krj*values[ pointers[r] ];\n\t\t}\n\t}\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\nFECORE_API void colsol_solve(int N, double* values, int* pointers, double* R)\n{\n\tint i, mi, r;\n\n\t// -B- back substitution\n\n\t// calculate V = L^(-T)*R vector\n\tfor (i=1; i<N; ++i)\n\t{\n\t\tmi = i+1 - pointers[i+1] + pointers[i];\n\t\tfor (r=mi; r<i; ++r)\n\t\t\tR[i] -= values[ pointers[i] + i - r]*R[r];\n\t}\n\n\t// calculate Vbar = D^(-1)*V\n\tfor (i=0; i<N; ++i) R[i] /= values[ pointers[i] ];\n\n\t// calculate the solution\n\tfor (i=N-1; i>0; --i)\n\t{\n\t\tmi = i+1 - pointers[i+1] + pointers[i];\n\n\t\tconst double ri = R[i];\n\t\tconst int pi = pointers[i] + i;\n\n\t\t// the following line was replaced\n\t\t// by the code segment between the arrows\n//\t\tfor (r=mi; r<i; ++r) R[r] -= values[ pointers[i] + i - r]*R[i];\n\n//--------->\n\t\tfor (r=mi; r<i-7; r += 8) \n\t\t{\n\t\t\tR[r  ] -= values[ pi - r  ]*ri;\n\t\t\tR[r+1] -= values[ pi - r-1]*ri;\n\t\t\tR[r+2] -= values[ pi - r-2]*ri;\n\t\t\tR[r+3] -= values[ pi - r-3]*ri;\n\t\t\tR[r+4] -= values[ pi - r-4]*ri;\n\t\t\tR[r+5] -= values[ pi - r-5]*ri;\n\t\t\tR[r+6] -= values[ pi - r-6]*ri;\n\t\t\tR[r+7] -= values[ pi - r-7]*ri;\n\t\t}\n\n\t\tfor (r=0; r<(i-mi)%8; ++r)\n\t\t\tR[(i-1)- r] -= values[ pi - (i-1) + r  ]*ri;\n//--------->\n\t}\n}\n\n\n///////////////////////////////////////////////////////////////////////////////\n// This LU solver is grabbed from Numerical Recipes in C.\n// To solve a system of equations first call ludcmp to calculate\n// its LU decomposition. Next you can call the lubksb to perform\n// a back substitution. The preferred way of using these functions\n// therefore is:\n//\n// double** a;\n// double* b;\n// int* indx;\n// int n;\n// ...\n// ludcmp(a,n,indx)\n// lubksb(a,n,indx, b)\n//\n// The lubksb can be called as many times as there are rhs vectors for \n// this system that you wish to call. For a specific matrix ludcmp only\n// has to be called once.\n//\n\nvoid ludcmp(double**a, int n, int* indx)\n{\n\tint i, imax, j, k;\n\tdouble big, dum, sum, temp;\n\tdouble* vv;\n\n\tconst double TINY = 1.0e-20;\n\n\tvv = new double[n];\n\n\tfor (i=0; i<n; ++i)\n\t{\n\t\tbig = 0;\n\t\tfor (j=0; j<n; ++j)\n\t\t\tif ((temp = fabs(a[i][j])) > big) big = temp;\n\n\t\tvv[i] = 1.0 / big;\n\t}\n\n\tfor (j=0; j<n; ++j)\n\t{\n\t\tfor (i=0; i<j; ++i)\n\t\t{\n\t\t\tsum = a[i][j];\n\t\t\tfor (k=0; k<i; ++k) sum -= a[i][k]*a[k][j];\n\t\t\ta[i][j] = sum;\n\t\t}\n\t\tbig = 0;\n\t\timax = j;\n\t\tfor (i=j; i<n; ++i)\n\t\t{\n\t\t\tsum = a[i][j];\n\t\t\tfor (k=0; k<j; ++k) sum -= a[i][k]*a[k][j];\n\t\t\ta[i][j] = sum;\n\t\t\tif ((dum=vv[i]*fabs(sum)) >= big)\n\t\t\t{\n\t\t\t\tbig = dum;\n\t\t\t\timax = i;\n\t\t\t}\n\t\t}\n\t\tif (j != imax)\n\t\t{\n\t\t\tfor (k=0; k<n; ++k)\n\t\t\t{\n\t\t\t\tdum = a[imax][k];\n\t\t\t\ta[imax][k] = a[j][k];\n\t\t\t\ta[j][k] = dum;\n\t\t\t}\n\t\t\tvv[imax] = vv[j];\n\t\t}\n\t\tindx[j] = imax;\n\t\tif (a[j][j] == 0.0) \n\t\t{\n\t\t\ta[j][j] = TINY;\n\t\t}\n\t\tif (j!=n-1)\n\t\t{\n\t\t\tdum = 1.0/(a[j][j]);\n\t\t\tfor (i=j+1;i<n; ++i) a[i][j] *= dum;\n\t\t}\n\t}\n\n\t// clean up\n\tdelete [] vv;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\nvoid lubksb(double**a, int n, int *indx, double b[])\n{\n\tint i, ii=0, ip, j;\n\tdouble sum;\n\n\tfor (i=0; i<n; ++i)\n\t{\n\t\tip = indx[i];\n\t\tsum = b[ip];\n\t\tb[ip] = b[i];\n\t\tif (ii != 0)\n\t\t\tfor (j=ii-1; j<i; ++j) sum -= a[i][j]*b[j];\n\t\telse if (sum != 0.0) ii=i+1;\n\t\tb[i] = sum;\n\t}\n\tfor (i=n-1; i>=0; --i)\n\t{\n\t\tsum = b[i];\n\t\tfor (j=i+1;j<n;++j) sum -= a[i][j]*b[j];\n\t\tb[i] = sum/a[i][i];\n\t}\n}\n"
  },
  {
    "path": "FECore/eig3.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <math.h>\n\n#ifdef MAX\n#undef MAX\n#endif\n\n#define MAX(a, b) ((a)>(b)?(a):(b))\n\n#define n 3\n\nstatic double hypot2(double x, double y) {\n  return sqrt(x*x+y*y);\n}\n\n// Symmetric Householder reduction to tridiagonal form.\n\nstatic void tred2(double V[n][n], double d[n], double e[n]) {\n\n//  This is derived from the Algol procedures tred2 by\n//  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for\n//  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding\n//  Fortran subroutine in EISPACK.\n\n  for (int j = 0; j < n; j++) {\n    d[j] = V[n-1][j];\n  }\n\n  // Householder reduction to tridiagonal form.\n\n  for (int i = n-1; i > 0; i--) {\n\n    // Scale to avoid under/overflow.\n\n    double scale = 0.0;\n    double h = 0.0;\n    for (int k = 0; k < i; k++) {\n      scale = scale + fabs(d[k]);\n    }\n    if (scale == 0.0) {\n      e[i] = d[i-1];\n      for (int j = 0; j < i; j++) {\n        d[j] = V[i-1][j];\n        V[i][j] = 0.0;\n        V[j][i] = 0.0;\n      }\n    } else {\n\n      // Generate Householder vector.\n\n      for (int k = 0; k < i; k++) {\n        d[k] /= scale;\n        h += d[k] * d[k];\n      }\n      double f = d[i-1];\n      double g = sqrt(h);\n      if (f > 0) {\n        g = -g;\n      }\n      e[i] = scale * g;\n      h = h - f * g;\n      d[i-1] = f - g;\n      for (int j = 0; j < i; j++) {\n        e[j] = 0.0;\n      }\n\n      // Apply similarity transformation to remaining columns.\n\n      for (int j = 0; j < i; j++) {\n        f = d[j];\n        V[j][i] = f;\n        g = e[j] + V[j][j] * f;\n        for (int k = j+1; k <= i-1; k++) {\n          g += V[k][j] * d[k];\n          e[k] += V[k][j] * f;\n        }\n        e[j] = g;\n      }\n      f = 0.0;\n      for (int j = 0; j < i; j++) {\n        e[j] /= h;\n        f += e[j] * d[j];\n      }\n      double hh = f / (h + h);\n      for (int j = 0; j < i; j++) {\n        e[j] -= hh * d[j];\n      }\n      for (int j = 0; j < i; j++) {\n        f = d[j];\n        g = e[j];\n        for (int k = j; k <= i-1; k++) {\n          V[k][j] -= (f * e[k] + g * d[k]);\n        }\n        d[j] = V[i-1][j];\n        V[i][j] = 0.0;\n      }\n    }\n    d[i] = h;\n  }\n\n  // Accumulate transformations.\n\n  for (int i = 0; i < n-1; i++) {\n    V[n-1][i] = V[i][i];\n    V[i][i] = 1.0;\n    double h = d[i+1];\n    if (h != 0.0) {\n      for (int k = 0; k <= i; k++) {\n        d[k] = V[k][i+1] / h;\n      }\n      for (int j = 0; j <= i; j++) {\n        double g = 0.0;\n        for (int k = 0; k <= i; k++) {\n          g += V[k][i+1] * V[k][j];\n        }\n        for (int k = 0; k <= i; k++) {\n          V[k][j] -= g * d[k];\n        }\n      }\n    }\n    for (int k = 0; k <= i; k++) {\n      V[k][i+1] = 0.0;\n    }\n  }\n  for (int j = 0; j < n; j++) {\n    d[j] = V[n-1][j];\n    V[n-1][j] = 0.0;\n  }\n  V[n-1][n-1] = 1.0;\n  e[0] = 0.0;\n} \n\n// Symmetric tridiagonal QL algorithm.\n\nstatic void tql2(double V[n][n], double d[n], double e[n]) {\n\n//  This is derived from the Algol procedures tql2, by\n//  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for\n//  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding\n//  Fortran subroutine in EISPACK.\n\n  for (int i = 1; i < n; i++) {\n    e[i-1] = e[i];\n  }\n  e[n-1] = 0.0;\n\n  double f = 0.0;\n  double tst1 = 0.0;\n  double eps = pow(2.0,-52.0);\n  for (int l = 0; l < n; l++) {\n\n    // Find small subdiagonal element\n\n    tst1 = MAX(tst1,fabs(d[l]) + fabs(e[l]));\n    int m = l;\n    while (m < n) {\n      if (fabs(e[m]) <= eps*tst1) {\n        break;\n      }\n      m++;\n    }\n\n    // If m == l, d[l] is an eigenvalue,\n    // otherwise, iterate.\n\n    if (m > l) {\n      int iter = 0;\n      do {\n        iter = iter + 1;  // (Could check iteration count here.)\n\n        // Compute implicit shift\n\n        double g = d[l];\n        double p = (d[l+1] - g) / (2.0 * e[l]);\n        double r = hypot2(p,1.0);\n        if (p < 0) {\n          r = -r;\n        }\n        d[l] = e[l] / (p + r);\n        d[l+1] = e[l] * (p + r);\n        double dl1 = d[l+1];\n        double h = g - d[l];\n        for (int i = l+2; i < n; i++) {\n          d[i] -= h;\n        }\n        f = f + h;\n\n        // Implicit QL transformation.\n\n        p = d[m];\n        double c = 1.0;\n        double c2 = c;\n        double c3 = c;\n        double el1 = e[l+1];\n        double s = 0.0;\n        double s2 = 0.0;\n        for (int i = m-1; i >= l; i--) {\n          c3 = c2;\n          c2 = c;\n          s2 = s;\n          g = c * e[i];\n          h = c * p;\n          r = hypot2(p,e[i]);\n          e[i+1] = s * r;\n          s = e[i] / r;\n          c = p / r;\n          p = c * d[i] - s * g;\n          d[i+1] = h + s * (c * g + s * d[i]);\n\n          // Accumulate transformation.\n\n          for (int k = 0; k < n; k++) {\n            h = V[k][i+1];\n            V[k][i+1] = s * V[k][i] + c * h;\n            V[k][i] = c * V[k][i] - s * h;\n          }\n        }\n        p = -s * s2 * c3 * el1 * e[l] / dl1;\n        e[l] = s * p;\n        d[l] = c * p;\n\n        // Check for convergence.\n\n      } while (fabs(e[l]) > eps*tst1);\n    }\n    d[l] = d[l] + f;\n    e[l] = 0.0;\n  }\n  \n  // Sort eigenvalues and corresponding vectors.\n\n  for (int i = 0; i < n-1; i++) {\n    int k = i;\n    double p = d[i];\n    for (int j = i+1; j < n; j++) {\n      if (d[j] < p) {\n        k = j;\n        p = d[j];\n      }\n    }\n    if (k != i) {\n      d[k] = d[i];\n      d[i] = p;\n      for (int j = 0; j < n; j++) {\n        p = V[j][i];\n        V[j][i] = V[j][k];\n        V[j][k] = p;\n      }\n    }\n  }\n}\n\nvoid eigen_decomposition(double A[n][n], double V[n][n], double d[n]) {\n  double e[n];\n  for (int i = 0; i < n; i++) {\n    for (int j = 0; j < n; j++) {\n      V[i][j] = A[i][j];\n    }\n  }\n  tred2(V, d, e);\n  tql2(V, d, e);\n}\n"
  },
  {
    "path": "FECore/eig3.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// Symmetric matrix A => eigenvectors in columns of V, corresponding eigenvalues in d.\nvoid eigen_decomposition(double A[3][3], double V[3][3], double d[3]);\n"
  },
  {
    "path": "FECore/expint_Ei.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n#include \"expint_Ei.h\"\n#include <limits>\n#include <math.h>\n\n// This is our homemade function for evaluating the exponential integral Ei(x), valid for\n// negative and positive values of x.\n\ndouble expint_Ei(double x)\n{\n    const int MAXITER = 100;\n    if (x == 0) return -INFINITY;\n    const double eps = 10*std::numeric_limits<double>::epsilon();\n    const double gamma = 0.5772156649015328606065120900824024310421;\n    double ei = 0;\n\n    // use series expansion if x is sufficiently small\n    if (fabs(x) < fabs(log(eps))) {\n        // use series expansion if x is sufficiently small\n        ei = gamma;\n        ei += (x > 0) ? log(x) : log(-x);\n        double d = x;\n        int i=0;\n        bool convgd = false;\n        while (!convgd) {\n            ++i;\n            ei += d;\n            if ((fabs(d) < eps*fabs(ei)) || (i > MAXITER)) convgd = true;\n            else d *= (i*x)/pow(i+1,2);\n        }\n    }\n    else {\n        // use asymptotic expansion for sufficiently large x\n        ei = 1;\n        double d = 1./x;\n        int i=0;\n        bool convgd = false;\n        while (!convgd) {\n            ++i;\n            ei += d;\n            if ((fabs(d) < eps*fabs(ei)) || (i > MAXITER)) convgd = true;\n            else d *= (i+1)/x;\n        }\n        ei *= exp(x)/x;\n    }\n    return ei;\n}\n"
  },
  {
    "path": "FECore/expint_Ei.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n\nFECORE_API double expint_Ei(double x);\n"
  },
  {
    "path": "FECore/fecore_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef fecore_EXPORTS\n\t\t\t#define FECORE_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FECORE_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FECORE_API\n\t#endif\n#else\n\t#define FECORE_API\n#endif\n"
  },
  {
    "path": "FECore/fecore_debug.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"fecore_debug.h\"\n#include <string>\n#include <iostream>\n#include <stdarg.h>\n#include \"version.h\"\n#include <assert.h>\n#include <iomanip>\nusing namespace std;\n\nstd::list<FECoreDebugger::Variable*>\tFECoreDebugger::m_var;\n\nvoid FECoreDebugger::Break(FECoreBreakPoint* pbr)\n{\n\tstring s;\n\tdo\n\t{\n\t\tif (pbr) cout << \"#\" << pbr->GetID();\n\t\tcout << \">>\";\n\t\tcin >> s;\n\n\t\tif      (s == \"cont\" ) break;\n\t\telse if (s == \"print\")\n\t\t{\n\t\t\tcin >> s;\n\t\t\tlist<Variable*>::iterator it;\n\t\t\tbool bfound = false;\n\t\t\tfor (it = m_var.begin(); it != m_var.end(); ++it)\n\t\t\t{\n\t\t\t\tif (s == (*it)->m_szname)\n\t\t\t\t{\n\t\t\t\t\t(*it)->print();\n\t\t\t\t\tcout << endl;\n\t\t\t\t\tbfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bfound == false) cout << \"Error: unknown variable\\n\";\n\t\t}\n\t\telse if (s == \"list\")\n\t\t{\n\t\t\tlist<Variable*>::iterator it;\n\t\t\tfor (it = m_var.begin(); it != m_var.end(); ++it)\n\t\t\t{\n\t\t\t\tcout << (*it)->m_szname << endl;\n\t\t\t}\n\t\t}\n\t\telse if (s == \"remove\")\n\t\t{\n\t\t\tif (pbr) { pbr->Deactivate(); break; }\n\t\t\telse cout << \"No active breakpoint\\n\";\n\t\t}\n\t\telse if (s == \"help\")\n\t\t{\n\t\t\tcout << \"cont      = continue\\n\";\n\t\t\tcout << \"list      = list all watch variables\\n\";\n\t\t\tcout << \"print var = print variable\\n\";\n\t\t\tcout << \"remove    = remove breakpoint and continue\\n\";\n\t\t\tcout << \"help      = show this information\\n\";\n\t\t}\n\t\telse cout << \"Error: Unknown command\\n\";\n\t}\n\twhile (1);\n}\n\nvoid FECoreDebugger::Clear()\n{\n\tlist<Variable*>::iterator it;\n\tfor (it = m_var.begin(); it != m_var.end(); ++it) delete (*it);\n\tm_var.clear();\n}\n\nvoid FECoreDebugger::Add(FECoreDebugger::Variable* pvar)\n{\n\tm_var.push_back(pvar);\n}\n\nvoid FECoreDebugger::Remove(FECoreDebugger::Variable* pvar)\n{\n\tlist<Variable*>::iterator it;\n\tfor (it = m_var.begin(); it != m_var.end(); ++it)\n\t{\n\t\tif ((*it)==pvar)\n\t\t{\n\t\t\tdelete *it;\n\t\t\tm_var.erase(it);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nFILE* FECoreDebugger::m_fp = nullptr;\n\nvoid FECoreDebugger::Print(const char* szformat, ...)\n{\n\tif (m_fp == nullptr)\n\t{\n\t\tchar fileName[256] = { 0 };\n\t\tsnprintf(fileName, sizeof(fileName), \"febio_%d.%d.%d_debug.log\", FE_SDK_MAJOR_VERSION, FE_SDK_SUB_VERSION, FE_SDK_SUBSUB_VERSION);\n\t\tm_fp = fopen(fileName, \"wt\"); assert(m_fp);\n\t}\n\n\tif (m_fp)\n\t{\n\t\tva_list\targs;\n\t\tva_start(args, szformat);\n\t\tvfprintf(m_fp, szformat, args);\n\t\tva_end(args);\n\n\t\tfflush(m_fp);\n\t}\n}\n\ntemplate <> void fecore_print_T<matrix>(matrix* pd)\n{\n\tmatrix& m = *pd;\n\tint nr = m.rows();\n\tint nc = m.columns();\n\tfor (int i=0; i<nr; ++i)\n\t{\n\t\tfor (int j=0; j<nc; ++j)\n\t\t{\n\t\t\tcout << std::setw(8) << std::setprecision(4) << m(i,j);\n\t\t}\n\t\tcout << endl;\n\t}\n}\n\ntemplate <> void fecore_print_T<mat3d>(mat3d* pd)\n{\n\tmat3d& m = *pd;\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tfor (int j=0; j<3; ++j) cout << m(i,j) << \" \";\n\t\tcout << endl;\n\t}\n}\n\ntemplate <> void fecore_print_T<mat3ds>(mat3ds* pd)\n{\n\tmat3ds& m = *pd;\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tfor (int j=0; j<3; ++j) cout << m(i,j) << \" \";\n\t\tcout << endl;\n\t}\n}\n\ntemplate <> void fecore_print_T<mat3da>(mat3da* pd)\n{\n\tmat3da& m = *pd;\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tfor (int j=0; j<3; ++j) cout << m(i,j) << \" \";\n\t\tcout << endl;\n\t}\n}\n\ntemplate <> void fecore_print_T<mat3dd>(mat3dd* pd)\n{\n\tmat3dd& m = *pd;\n\tfor (int i=0; i<3; ++i)\n\t{\n\t\tfor (int j=0; j<3; ++j) cout << m(i,j) << \" \";\n\t\tcout << endl;\n\t}\n}\n\ntemplate <> void fecore_print_T<vec3d>(vec3d* pd)\n{\n\tvec3d& v = *pd;\n\tcout << v.x << endl;\n\tcout << v.y << endl;\n\tcout << v.z << endl;\n}\n\ntemplate <> void fecore_print_T<tens4ds>(tens4ds* pd)\n{\n\ttens4ds& m = *pd;\n\tfor (int i=0; i<6; ++i)\n\t{\n\t\tfor (int j=0; j<6; ++j)\n\t\t{\n\t\t\tcout << m(i,j) <<\" \";\n\t\t}\n\t\tcout << endl;\n\t}\n}\n\ntemplate <> void fecore_print_T<std::vector<double> >(std::vector<double>* pv)\n{\n\tvector<double>& v = *pv;\n\tint n = (int)v.size();\n\tfor (int i=0; i<n; ++i) cout << v[i] << endl;\n}\n\ntemplate <> void fecore_print_T<std::vector<int> >(std::vector<int>* pv)\n{\n\tvector<int>& v = *pv;\n\tint n = (int)v.size();\n\tfor (int i=0; i<n; ++i) cout << v[i] << endl;\n}\n\n//=============================================================================\n\nFECoreDebugStream::FECoreDebugStream()\n{\n\tm_mode = STREAM_MODE::WRITING_MODE;\n\tm_fp = nullptr;\n\tm_counter = 0;\n}\n\nFECoreDebugStream::FECoreDebugStream(const char* szfilename, STREAM_MODE mode)\n{\n\tm_mode = mode;\n\tm_fp = nullptr;\n\tm_counter = 0;\n\tOpen(szfilename, mode);\n}\n\nFECoreDebugStream::~FECoreDebugStream()\n{\n\tif (m_fp) fclose(m_fp);\n\tm_fp = nullptr;\n}\n\nbool FECoreDebugStream::Open(const char* szfilename, FECoreDebugStream::STREAM_MODE mode)\n{\n\tm_counter = 0;\n\tm_filename = szfilename;\n\tm_mode = mode;\n\tif (mode == WRITING_MODE)\n\t\tm_fp = fopen(szfilename, \"wb\");\n\telse\n\t\tm_fp = fopen(szfilename, \"rb\");\n\treturn (m_fp != nullptr);\n}\n\nbool FECoreDebugStream::ReopenForReading()\n{\n\tif (is_reading()) return true;\n\tif (m_fp) fclose(m_fp);\n\tm_mode = READING_MODE;\n\tm_fp = fopen(m_filename.c_str(), \"rb\");\n\treturn (m_fp != nullptr);\n}\n\nsize_t FECoreDebugStream::write(const void* pd, size_t size, size_t count)\n{\n\tm_counter++;\n\tassert(is_writing());\n\treturn fwrite(pd, size, count, m_fp);\n}\n\nsize_t FECoreDebugStream::read(void* pd, size_t size, size_t count)\n{\n\tm_counter++;\n\tassert(is_reading());\n\treturn fread(pd, size, count, m_fp);\n}\n"
  },
  {
    "path": "FECore/fecore_debug.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// Load debugger template constructions.\n// Don't use anything in this file directly.\n// Instead use the functions and macros defined below\n#include \"fecore_debug_t.h\"\n\n//-----------------------------------------------------------------------------\n// Set a break point. This will bring up the debugger prompt.\n// (type help for a list of available debugger commands)\n#define fecore_break() { \\\n\tstatic FECoreBreakPoint _br_##__LINE__; \\\n\tif (_br_##__LINE__.IsActive()) { \\\n\t\tcout << \"breakpoint \" << _br_##__LINE__.GetID() << \" : \" << __FILE__ <<  \"(line \" << __LINE__ << \")\\n\"; \\\n\t\t_br_##__LINE__.Break(); }}\n\n//-----------------------------------------------------------------------------\n// Add a variable to the watch list.\n// A variable on the watch list can be inspected from the debugger \n// (type print var on the debugger prompt, where var is the variable name)\n#define fecore_watch(a) FECoreWatchVariable _##a(create_watch_variable(&a, #a));\n\n//-----------------------------------------------------------------------------\n// print the contents of a variable to the screen\n#define fecore_print(a) { \\\n\tcout << #a << endl << typeid(a).name() << endl; \\\n\tfecore_print_T(&a); \\\n\tcout << endl; }\n\n//-----------------------------------------------------------------------------\nclass FECORE_API FECoreDebugStream\n{\npublic:\n\tenum STREAM_MODE\n\t{\n\t\tWRITING_MODE,\n\t\tREADING_MODE\n\t};\n\npublic:\n\tFECoreDebugStream();\n\tFECoreDebugStream(const char* szfilename, STREAM_MODE mode);\n\t~FECoreDebugStream();\n\n\tbool Open(const char* szfilename, STREAM_MODE mode);\n\n\tbool is_writing() const { return m_mode == STREAM_MODE::WRITING_MODE; }\n\tbool is_reading() const { return m_mode == STREAM_MODE::READING_MODE; }\n\n\tbool ReopenForReading();\n\n\ttemplate <typename T> bool check(T& v)\n\t{\n\t\tif (is_writing())\n\t\t{\n\t\t\twrite(&v, sizeof(T), 1);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tT tmp;\n\t\t\tread(&tmp, sizeof(T), 1);\n\t\t\tassert(tmp == v);\n\t\t\treturn (tmp == v);\n\t\t}\n\t}\n\n\ttemplate <typename T> bool check(std::vector<T>& v)\n\t{\n\t\tsize_t n = v.size();\n\t\tif (is_writing())\n\t\t{\n\t\t\twrite(v.data(), sizeof(T), n);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\n\t\t\tstd::vector<T> tmp(n);\n\t\t\tread(tmp.data(), sizeof(T), n);\n\t\t\tfor (size_t i = 0; i < n; ++i)\n\t\t\t{\n\t\t\t\tassert(tmp[i] == v[i]);\n\t\t\t\tif (tmp[i] != v[i]) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\nprivate:\n\tsize_t write(const void* pd, size_t size, size_t count);\n\tsize_t read(void* pd, size_t size, size_t count);\n\nprivate:\n\tSTREAM_MODE\tm_mode;\n\tFILE* m_fp;\n\tstd::string\tm_filename;\n\tint\tm_counter;\n};\n"
  },
  {
    "path": "FECore/fecore_debug_t.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <list>\n#include <vector>\n#include <iostream>\n#include <typeinfo>\n#include <stdlib.h>\n#include \"matrix.h\"\n#include \"mat3d.h\"\n#include \"vec3d.h\"\n#include \"tens4d.h\"\n#include \"fecore_api.h\"\n\n// This file defines template constructions that are used by the FECore debugger.\n// Don't use anything in here or include this file directly. \n// Instead include fecore_debug.h and use the user functions and macros defined there.\n\ntemplate <typename T> void fecore_print_T(T* pd) { std::cout << (*pd); }\ntemplate <> FECORE_API void fecore_print_T<matrix>(matrix* pd);\ntemplate <> FECORE_API void fecore_print_T<mat3d>(mat3d* pd);\ntemplate <> FECORE_API void fecore_print_T<mat3ds>(mat3ds* pd);\ntemplate <> FECORE_API void fecore_print_T<mat3da>(mat3da* pd);\ntemplate <> FECORE_API void fecore_print_T<mat3dd>(mat3dd* pd);\ntemplate <> FECORE_API void fecore_print_T<vec3d>(vec3d* pd);\ntemplate <> FECORE_API void fecore_print_T<tens4ds>(tens4ds* pd);\ntemplate <> FECORE_API void fecore_print_T<std::vector<double> >(std::vector<double>* pv);\ntemplate <> FECORE_API void fecore_print_T<std::vector<int> >(std::vector<int>* pv);\n\nclass FECoreBreakPoint;\n\nclass FECORE_API FECoreDebugger\n{\npublic:\n\tclass Variable\n\t{\n\tpublic:\n\t\tVariable(void* pd, const char* sz) { m_pd = pd; m_szname = sz; }\n\t\tvirtual ~Variable() {}\n\n\t\tvirtual void print() = 0;\n\n\tpublic:\n\t\tvoid* m_pd;\n\t\tconst char* m_szname;\n\t};\n\n\ttemplate <typename T> class Variable_T : public Variable\n\t{\n\tpublic:\n\t\tVariable_T(void* pd, const char* sz) : Variable(pd, sz) {}\n\n\t\tvoid print()\n\t\t{\n\t\t\tstd::cout << typeid(T).name() << std::endl;\n\t\t\tfecore_print_T<T>((T*)m_pd);\n\t\t}\n\t};\n\npublic:\n\tstatic void Break(FECoreBreakPoint* pbr);\n\tstatic void Clear();\n\tstatic void Add(Variable* pvar);\n\tstatic void Remove(Variable* pvar);\n\n\tstatic void Print(const char* szformat, ...);\n\nprivate:\n\tFECoreDebugger() {}\n\n\tstatic std::list<Variable*>\tm_var;\n\tstatic FILE* m_fp;\n};\n\nclass FECoreWatchVariable\n{\npublic:\n\tFECoreWatchVariable(FECoreDebugger::Variable* pvar) : m_pvar(pvar)\n\t{\n\t\tif (pvar) FECoreDebugger::Add(pvar);\n\t}\n\n\t~FECoreWatchVariable()\n\t{\n\t\tif (m_pvar) FECoreDebugger::Remove(m_pvar);\n\t}\n\nprotected:\n\tFECoreDebugger::Variable* m_pvar;\n};\n\nclass FECoreBreakPoint\n{\npublic:\n\tFECoreBreakPoint() { static int n = 1; m_bactive = true; m_nid = n++; }\n\tvoid Deactivate() { m_bactive = false; }\n\tbool IsActive() { return m_bactive; }\n\n\tvoid Break()\n\t{\n\t\tif (m_bactive) { FECoreDebugger::Break(this); }\n\t}\n\n\tint GetID() { return m_nid; }\n\nprivate:\n\tint\t\tm_nid;\n\tbool\tm_bactive;\n};\n\ntemplate <typename T> FECoreDebugger::Variable* create_watch_variable(T* pd, const char* sz) { return new FECoreDebugger::Variable_T<T>(pd, sz); }\n"
  },
  {
    "path": "FECore/fecore_enum.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n//-----------------------------------------------------------------------------\n// Element Class:\n// Defines the general category of element.\nenum FE_Element_Class {\n\tFE_ELEM_INVALID_CLASS,\n\n\tFE_ELEM_SOLID,\n\tFE_ELEM_SHELL,\n\tFE_ELEM_BEAM,\n\tFE_ELEM_SURFACE,\n\tFE_ELEM_TRUSS,\n\tFE_ELEM_DISCRETE,\n\tFE_ELEM_2D,\n\tFE_ELEM_EDGE,\n\n\tFE_ELEM_WIRE = 100\t// temporary. Can change.\n};\n\n//-----------------------------------------------------------------------------\n// Element shapes:\n// This defines the general element shape classes. This classification differs from the\n// element types below, in that the latter is defined by a shape and integration rule.\n// Do not change the order of these enums! (NOTE: why??)\nenum FE_Element_Shape {\n\t// 3D elements\n\tET_TET4,\n\tET_TET5,\n\tET_TET10,\n\tET_TET15,\n\tET_TET20,\n\tET_PENTA6,\n\tET_PENTA15,\n\tET_HEX8,\n\tET_HEX20,\n\tET_HEX27,\n\tET_PYRA5,\n    ET_PYRA13,\n\n\t// 2.5D elements\n\tET_QUAD4,\n    ET_QUAD8,\n    ET_QUAD9,\n\tET_TRI3,\n    ET_TRI6,\n\tET_TRI7,\n\tET_TRI10,\n\n\t// line elements\n\tET_TRUSS2,\n\tET_LINE2,\n\tET_LINE3,\n\tET_DISCRETE,\n\n\tFE_ELEM_INVALID_SHAPE = 999\n};\n\n//-----------------------------------------------------------------------------\n// Element types:\n//  Note that these numbers are actually indices into the m_Traits array\n//  of the ElementLibrary class so make sure the numbers correspond\n//  with the entries into this array\n//\n\nenum FE_Element_Type {\n\t// 3D solid elements\n\tFE_HEX8G8,\t\n\tFE_HEX8RI,\n\tFE_HEX8G1,\t\n\tFE_TET4G1,\t\n\tFE_TET4G4,\n\tFE_TET5G4,\n\tFE_PENTA6G6,\t\n\tFE_TET10G1,\n\tFE_TET10G4,\n\tFE_TET10G8,\n\tFE_TET10GL11,\n\tFE_TET10G4RI1,\n\tFE_TET10G8RI4,\n\tFE_TET15G4,\n\tFE_TET15G8,\n\tFE_TET15G11,\n\tFE_TET15G15,\n\tFE_TET15G15RI4,\n\tFE_TET20G15,\n    FE_HEX20G8,\n\tFE_HEX20G27,\n\tFE_HEX27G27,\n    FE_PENTA15G8,\n    FE_PENTA15G21,\n\tFE_PYRA5G8,\n    FE_PYRA13G8,\n\n\t// 2.5D surface elements\n\tFE_QUAD4G4,\n\tFE_QUAD4G16,\n\tFE_QUAD4NI,\n\tFE_TRI3G1,\n\tFE_TRI3G3,\n\tFE_TRI3G7,\n\tFE_TRI3NI,\n\tFE_TRI6G3,\n\tFE_TRI6G4,\n\tFE_TRI6G7,\n//\tFE_TRI6MG7,\n\tFE_TRI6GL7,\n\tFE_TRI6NI,\n\tFE_TRI7G3,\n\tFE_TRI7G4,\n\tFE_TRI7G7,\n\tFE_TRI7GL7,\n\tFE_TRI10G7,\n\tFE_TRI10G12,\n\tFE_QUAD8G9,\n    FE_QUAD8NI,\n\tFE_QUAD9G9,\n    FE_QUAD9NI,\n\n\t// shell elements\n    FE_SHELL_QUAD4G4,\n    FE_SHELL_QUAD4G8,\n    FE_SHELL_QUAD4G12,\n    FE_SHELL_QUAD8G18,\n    FE_SHELL_QUAD8G27,\n    FE_SHELL_TRI3G3,\n    FE_SHELL_TRI3G6,\n    FE_SHELL_TRI3G9,\n    FE_SHELL_TRI6G14,\n    FE_SHELL_TRI6G21,\n\n\t// truss elements\n\tFE_TRUSS,\n\n\t// discrete elements\n\tFE_DISCRETE,\n\n\t// 2D elements\n\tFE2D_TRI3G1,\n\tFE2D_TRI6G3,\n\tFE2D_QUAD4G4,\n\tFE2D_QUAD8G9,\n\tFE2D_QUAD9G9,\n\n\t// line elements\n\tFE_LINE2G1,\n\tFE_LINE2NI,\n\n\t// beam elements\n\tFE_BEAM2G1,\n\tFE_BEAM2G2,\n\tFE_BEAM3G2,\n\n\t// unspecified\n\tFE_ELEM_INVALID_TYPE = 0xFFFF\n};\n\n//-----------------------------------------------------------------------------\n// Shell formulations\nenum SHELL_FORMULATION {\n\tNEW_SHELL,\n\tOLD_SHELL,\n\tEAS_SHELL,\n\tANS_SHELL\n};\n\n//-----------------------------------------------------------------------------\n//! Helper class for creating domain classes.\nstruct FE_Element_Spec\n{\n\tFE_Element_Class    eclass;\n\tFE_Element_Shape\teshape;\n\tFE_Element_Type\t\tetype;\n\tbool\t\t\t\tm_bthree_field;\n\tint\t\t\t\t\tm_shell_formulation;\n    bool                m_shell_norm_nodal;\n\n\tbool\t\tm_but4;\n\tdouble\t\tm_ut4_alpha;\n\tbool\t\tm_ut4_bdev;\n\n\tFE_Element_Spec()\n\t{\n\t\teclass = FE_ELEM_INVALID_CLASS;\n\t\teshape = FE_ELEM_INVALID_SHAPE;\n\t\tetype  = FE_ELEM_INVALID_TYPE;\n\t\tm_bthree_field = false;\n\t\tm_shell_formulation = NEW_SHELL;\n        m_shell_norm_nodal = true;\n\t\tm_but4 = false;\n\t\tm_ut4_alpha = 0.05;\n\t\tm_ut4_bdev = false;\n\t}\n\n\tbool operator == (const FE_Element_Spec& s)\n\t{\n\t\tif ((eclass == s.eclass) &&\n\t\t\t(eshape == s.eshape) &&\n\t\t\t(etype  == s.etype )) return true;\n\t\treturn false;\n\t}\n};\n\n//-----------------------------------------------------------------------------\n//! This lists the super-class id's that can be used to register new classes\n//! with the kernel. It effectively defines the base class that a class\n//! is derived from.\nenum SUPER_CLASS_ID {\n\tFEINVALID_ID,\t\t\t\t\t// an invalid ID\n\tFEOBJECT_ID,\t\t\t\t\t// derived from FECoreBase (TODO: work in progress)\n\tFETASK_ID,                   \t// derived from FECoreTask\n\tFESOLVER_ID,                 \t// derived from FESolver\n\tFEMATERIAL_ID,               \t// derived from FEMaterial\n\tFEMATERIALPROP_ID,\t\t\t\t// derived from FEMaterialProperty\n\tFEDISCRETEMATERIAL_ID,\t\t\t// derived from FEDiscreteMaterial\n\tFELOAD_ID,               \t    // derived from FEModelLoad\n\tFENLCONSTRAINT_ID,           \t// derived from FENLConstraint\n\tFEPLOTDATA_ID,               \t// derived from FEPlotData\n\tFEANALYSIS_ID,               \t// derived from FEAnalysis\n\tFESURFACEINTERACTION_ID, \t\t// derived from FESurfaceInterface\n\tFELOGNODEDATA_ID,            \t// derived from FELogNodeData\n\tFELOGFACEDATA_ID,            \t// derived from FELogFaceData\n\tFELOGELEMDATA_ID,            \t// derived from FELogElemData\n\tFELOGOBJECTDATA_ID,            \t// derived from FELogObjectData\n\tFELOGDOMAINDATA_ID,            \t// derived from FELogDomainData\n\tFELOGNLCONSTRAINTDATA_ID,      \t// derived from FELogNLConstraintData\n\tFELOGSURFACEDATA_ID,      \t\t// derived from FELogSurfaceData\n\tFELOGMODELDATA_ID,            \t// derived from FEModelLogData\n\tFEBC_ID,\t\t\t\t\t\t// derived from FEBoundaryCondition\n\tFEGLOBALDATA_ID,\t\t\t\t// derived from FEGlobalData\n\tFECALLBACK_ID,\t\t\t\t\t// derived from FECallBack\n\tFESOLIDDOMAIN_ID,\t\t\t\t// derived from FESolidDomain\n\tFESHELLDOMAIN_ID,\t\t\t\t// derived from FEShellDomain\n\tFEBEAMDOMAIN_ID,\t\t\t\t// derived from FEBeamDomain\n\tFEDISCRETEDOMAIN_ID,\t\t\t// derived from FEDiscreteDomain\n\tFEDOMAIN2D_ID,\t\t\t\t\t// derived from FEDomain2D\n\tFESURFACE_ID,\t\t\t\t\t// derived from FESurface\n\tFEEDGE_ID,\t\t\t\t\t\t// derived from FEEdge\n\tFEIC_ID,\t\t\t\t\t\t// derived from FEInitialCondition\n\tFEMESHDATAGENERATOR_ID,\t\t\t// derived from FEMeshDataGenerator\n\tFELOADCONTROLLER_ID,\t\t\t// derived from FELoadContoller\n\tFEMODEL_ID,\t\t\t\t\t\t// derived from FEModel (TODO: work in progress)\n\tFESCALARVALUATOR_ID,\t\t\t// derived from FEScalarValuator\n\tFEVEC3DVALUATOR_ID,\t\t\t\t// derived from FEVectorValuator\n\tFEMAT3DVALUATOR_ID,\t\t\t\t// derived from FEMAT3DValuator\n\tFEMAT3DSVALUATOR_ID,\t\t\t// derived from FEMAT3DSValuator\n\tFEFUNCTION1D_ID,\t\t\t\t// derived from FEFunction1D\n\tFELINEARSOLVER_ID,\t\t\t\t// derived from LinearSolver\n\tFEMESHADAPTOR_ID,\t\t\t\t// derived from FEMeshAdaptor\n\tFEMESHADAPTORCRITERION_ID,\t\t// derived from FEMeshAdaptorCriterion\n\tFENEWTONSTRATEGY_ID,\t\t\t// derived from FENewtonStrategy\n\tFETIMECONTROLLER_ID,\t\t\t// derived from FETimeStepController\n\tFEEIGENSOLVER_ID,\t\t\t\t// derived from EigenSolver\n\tFEDATARECORD_ID,\t\t\t\t// derived from DataRecord\n\tFECLASS_ID,\t\t\t\t\t\t// derived from FECoreClass\n\tFESCRIPT_ID,\t\t\t\t\t// derived from FEScriptedBehavior\n};\n\n//-----------------------------------------------------------------------------\n// Plot level sets the frequency of writes to the plot file.\nenum FE_Plot_Level {\n\tFE_PLOT_NEVER,\t\t\t// don't output anything\n\tFE_PLOT_MAJOR_ITRS,\t\t// only output major iterations (i.e. converged time steps)\n\tFE_PLOT_MINOR_ITRS,\t\t// output minor iterations (i.e. every Newton iteration)\n\tFE_PLOT_MUST_POINTS,\t// output only on must-points\n\tFE_PLOT_FINAL,\t\t\t// only output final converged state\n\tFE_PLOT_AUGMENTATIONS,\t// plot state before augmentations\n\tFE_PLOT_STEP_FINAL,\t\t// output the final step of a step\n\tFE_PLOT_USER1\t\t\t// plot will only happen on CB_USER1 callback\n};\n\n//-----------------------------------------------------------------------------\n// Plot hint\nenum FE_Plot_Hint {\n\tFE_PLOT_NO_HINT = 0,\n\tFE_PLOT_APPEND = 1\t\t// don't close plot file after run\n};\n\n//-----------------------------------------------------------------------------\n// Output level sets the frequency of data output is written to the log or data files.\nenum FE_Output_Level {\n\tFE_OUTPUT_NEVER,\n\tFE_OUTPUT_MAJOR_ITRS,\n\tFE_OUTPUT_MINOR_ITRS,\n\tFE_OUTPUT_MUST_POINTS,\n\tFE_OUTPUT_FINAL\n};\n\n//-----------------------------------------------------------------------------\n//! Domain classes\n//! The domain class defines the general catergory of element types\n#define\tFE_DOMAIN_SOLID\t\t1\n#define\tFE_DOMAIN_SHELL\t\t2\n#define\tFE_DOMAIN_BEAM\t\t3\n#define\tFE_DOMAIN_SURFACE\t4\n#define\tFE_DOMAIN_DISCRETE\t5\n#define\tFE_DOMAIN_2D\t\t6\n#define FE_DOMAIN_EDGE\t\t7\n\n// --- data types ---\nenum Var_Type { \n\tPLT_FLOAT,\t\t// scalar             : single fp\n\tPLT_VEC3F,\t\t// 3D vector          : 3 fps\n\tPLT_MAT3FS,\t\t// symm 2o tensor     : 6 fps\n\tPLT_MAT3FD,\t\t// diagonal 2o tensor : 3 fps\n\tPLT_TENS4FS,\t// symm 4o tensor     : 21 fps\n\tPLT_MAT3F,\t\t// 2o tensor          : 9 fps\n\tPLT_ARRAY,\t\t// variable array (see dictionary for size)\n\tPLT_ARRAY_VEC3F\t// array of vec3f (see dictionary for size)\n};\n\n// --- storage format ---\n// FMT_NODE : one value stored for each node of a region\n// FMT_ITEM : one value stored for each item (e.g. element) of a region\n// FMT_MULT : one value for each node of each item of a region\n// FMT_REGION: one value per region (surface, domain)\nenum Storage_Fmt { FMT_NODE, FMT_ITEM, FMT_MULT, FMT_REGION, FMT_MATPOINTS };\n\n//-----------------------------------------------------------------------------\nenum FEDataType {\n\tFE_INVALID_TYPE,\n\tFE_DOUBLE,\n\tFE_VEC2D,\n\tFE_VEC3D,\n\tFE_MAT3D,\n\tFE_MAT3DS\n};\n\n//-----------------------------------------------------------------------------\nenum FEDataMapType {\n\tFE_INVALID_MAP_TYPE,\n\tFE_NODE_DATA_MAP,\n\tFE_DOMAIN_MAP,\n\tFE_SURFACE_MAP,\n\tFE_EDGE_MAP\n};\n\n//-----------------------------------------------------------------------------\n//! Different matrix types. This is used when requesting a sparse matrix format\n//! from a linear solver. \n//! \\sa LinearSolver::CreateSparseMatrix.\nenum Matrix_Type {\n\tREAL_UNSYMMETRIC,\t\t\t// non-symmetric \n\tREAL_SYMMETRIC,\t\t\t\t// symmetric (not necessarily positive definite)\n\tREAL_SYMM_STRUCTURE\t\t\t// structurally symmetric\n};\n\n//! Constraint enforcement method\nnamespace FECore {\n\tenum CONSTRAINT_ENFORCEMENT {\n\t\tPENALTY_METHOD,\n\t\tAUGLAG_METHOD,\n\t\tLAGMULT_METHOD\n\t};\n}\n"
  },
  {
    "path": "FECore/fecore_type.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"fecore_type.h\"\n#include <assert.h>\n\nint fecore_data_size(FEDataType type)\n{\n\tswitch (type)\n\t{\n\tcase FE_INVALID_TYPE: return 0; break;\n\tcase FE_DOUBLE: return fecoreType<double>::size(); break;\n\tcase FE_VEC2D : return fecoreType<vec2d >::size(); break;\n\tcase FE_VEC3D : return fecoreType<vec3d >::size(); break;\n\tcase FE_MAT3D : return fecoreType<mat3d >::size(); break;\n\tcase FE_MAT3DS: return fecoreType<mat3ds>::size(); break;\n\tdefault:\n\t\tassert(false);\n\t}\n\n\treturn 0;\n};\n"
  },
  {
    "path": "FECore/fecore_type.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_enum.h\"\n#include \"fecore_api.h\"\n\nclass vec2d;\nclass vec3d;\nclass mat3d;\nclass mat3ds;\n\ntemplate <typename T> struct fecoreType {};\n\ntemplate <> struct fecoreType<double>\n{\n\tstatic FEDataType type() { return FE_DOUBLE; }\n\tstatic int size() { return 1; }\n};\n\ntemplate <> struct fecoreType<vec2d>\n{\n\tstatic FEDataType type() { return FE_VEC2D; }\n\tstatic int size() { return 2; }\n};\n\ntemplate <> struct fecoreType<vec3d>\n{\n\tstatic FEDataType type() { return FE_VEC3D; }\n\tstatic int size() { return 3; }\n};\n\ntemplate <> struct fecoreType<mat3d>\n{\n\tstatic FEDataType type() { return FE_MAT3D; }\n\tstatic int size() { return 9; }\n};\n\ntemplate <> struct fecoreType<mat3ds>\n{\n\tstatic FEDataType type() { return FE_MAT3DS; }\n\tstatic int size() { return 6; }\n};\n\nFECORE_API int fecore_data_size(FEDataType type);\n"
  },
  {
    "path": "FECore/gamma.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#include \"stdafx.h\"\n#include \"gamma.h\"\n#include <limits>\n#include <math.h>\n\n#ifndef M_PI\n#define M_PI 3.141592653589793238462643\n#endif\n\n//---------------------------------------------------------------------------------\n// evaluate natural logarithm of gamma function\ndouble gammaln(double x)\n{\n    static double c[6] = {\n        76.18009172947146,\n        -86.50532032941677,\n        24.01409824083091,\n        -1.231739572450155,\n        0.1208650973866179e-2,\n        -0.5395239384953e-5};\n    double z = x;\n    double gln = x + 5.5;\n    gln -= (x+0.5)*log(gln);\n    double s = 1.000000000190015;\n    for (int i=0; i<6; ++i) s += c[i]/++z;\n    gln = -gln + log(2.5066282746310005*s/x);\n    return gln;\n}\n\n//---------------------------------------------------------------------------------\n// evaluate the inverse of the gamma function\ndouble gammainv(double x) {\n    if (x >= 0) return exp(-gammaln(x));\n    else return sin(M_PI*x)*exp(gammaln(1-x))/M_PI;\n}\n\n//---------------------------------------------------------------------------------\n// evaluate the incomplete Gamma function\n// adapted from Numerical Recipes in Fortran\n\nbool gser(double& gamser, double a, double x, double& gln) {\n    const int ITMAX = 100;\n    const double EPS = 3e-7;\n    gln = gammaln(a);\n    if (x > 0) {\n        double ap = a;\n        double sum = 1./a;\n        double del = sum;\n        bool cnvgd = true;\n        for (int n=1; n<=ITMAX; ++n) {\n            ap += 1;\n            del*= x/ap;\n            sum += del;\n            if (fabs(del) < fabs(sum)*EPS) break;\n            if (n == ITMAX) cnvgd = false;\n        }\n        if (!cnvgd) return false;\n        gamser = sum*exp(-x+a*log(x)-gln);\n    }\n    else {\n        gamser = 0;\n        return false;\n    }\n\treturn true;\n}\n\nbool gcf(double& gammcf, double a, double x, double& gln) {\n    const int ITMAX = 100;\n    const double EPS = 3e-7, FPMIN = 10*std::numeric_limits<double>::epsilon();\n    gln = gammaln(a);\n    double b = x+1-a;\n    double c = 1./FPMIN;\n    double d = 1./b;\n    double h = d;\n    bool cnvgd = true;\n    for (int i=1; i<=ITMAX; ++i) {\n        double an = -i*(i-a);\n        b += 2;\n        d = an*d+b;\n        if (fabs(d) < FPMIN) d = FPMIN;\n        c = b + an/c;\n        if (fabs(c) < FPMIN) c = FPMIN;\n        d = 1./d;\n        double del = d*c;\n        h *= del;\n        if (fabs(del-1) < EPS) break;\n        if (i == ITMAX) cnvgd = false;\n    }\n    if (!cnvgd) return false;\n    gammcf = exp(-x+a*log(x)-gln)*h;\n    return true;\n}\n\ndouble gamma_inc_P(double a, double x)\n{\n    // returns the lower incomplete Gamma function\n    if ((x >= 0) && (a >= 0)) {\n        if (x < a+1) {\n            double gamser, gln;\n            gser(gamser,a,x,gln);\n            return gamser*exp(gln);\n        }\n        else {\n            double gammcf, gln;\n            gcf(gammcf, a, x, gln);\n            return (1-gammcf)*exp(gln);\n        }\n    }\n    return 0.0;\n}\n\ndouble gamma_inc_Q(double a, double x)\n{\n    // returns the upper incomplete Gamma function\n    if ((x >= 0) && (a >= 0)) {\n        if (x < a+1) {\n            double gamser, gln;\n            gser(gamser,a,x,gln);\n            return (1-gamser)*exp(gln);\n        }\n        else {\n            double gammcf, gln;\n            gcf(gammcf, a, x, gln);\n            return gammcf*exp(gln);\n        }\n    }\n    return 0.0;\n}\n"
  },
  {
    "path": "FECore/gamma.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2020 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\n\n\n// natural log of Gamma function (x ≥ 0)\nFECORE_API double gammaln(double x);\n\n// inverse of Gamma function (any x)\nFECORE_API double gammainv(double x);\n\n// lower incomplete Gamma function P\nFECORE_API double gamma_inc_P(double a, double x);\n\n// upper incomplete Gamma function Q\nFECORE_API double gamma_inc_Q(double a, double x);\n\n"
  },
  {
    "path": "FECore/log.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"log.h\"\n#include \"FEModel.h\"\n#include <stdarg.h>\n\nvoid write_log(FEModel* fem, int ntag, const char* szmsg, ...)\n{\n\tassert(fem);\n\tif (fem->LogBlocked()) return;\n\n\t// get a pointer to the argument list\n\tva_list\targs;\n\n\t// make the message\n\tchar sztxt[2048] = { 0 };\n\tva_start(args, szmsg);\n\tvsnprintf(sztxt, sizeof(sztxt), szmsg, args);\n\tva_end(args);\n\n\tfem->Log(ntag, sztxt);\n}\n"
  },
  {
    "path": "FECore/log.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"fecore_api.h\"\nclass FEModel;\n\nFECORE_API void write_log(FEModel* fem, int ntag, const char* szmsg, ...);\n\n#define feLog(...) write_log(GetFEModel(), 0, __VA_ARGS__)\n#define feLogWarning(...) write_log(GetFEModel(), 1, __VA_ARGS__)\n#define feLogError(...) write_log(GetFEModel(), 2, __VA_ARGS__)\n#define feLogInfo(...) write_log(GetFEModel(), 3, __VA_ARGS__)\n#define feLogDebug(...) write_log(GetFEModel(), 4, __VA_ARGS__)\n\n#define feLogEx(fem, ...) write_log(fem, 0, __VA_ARGS__)\n#define feLogWarningEx(fem, ...) write_log(fem, 1, __VA_ARGS__)\n#define feLogErrorEx(fem, ...) write_log(fem, 2, __VA_ARGS__)\n#define feLogInfoEx(fem, ...) write_log(fem, 3, __VA_ARGS__)\n#define feLogDebugEx(fem, ...) write_log(fem, 4, __VA_ARGS__)\n"
  },
  {
    "path": "FECore/mat2d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"vec2d.h\"\n\nclass mat2d\n{\npublic:\n\t// constructors\n\tmat2d(){}\n\tmat2d(double a) { d[0][0] = d[1][1] = a; d[1][0] = d[0][1] = 0; }\n\tmat2d(double a00, double a01, double a10, double a11)\n\t{\n\t\td[0][0] = a00; d[0][1] = a01;\n\t\td[1][0] = a10; d[1][1] = a11;\n\t}\n\n\t// access operators\n\tdouble& operator () (int i, int j) { return d[i][j]; }\n\tdouble operator () (int i, int j) const { return d[i][j]; }\n\tdouble* operator [] (int i) { return d[i]; }\n\tconst double* operator [] (int i) const { return d[i]; }\n\npublic: // arithmetic operations\n\tmat2d operator + (const mat2d& m) { return mat2d(d[0][0]+m.d[0][0], d[0][1]+m.d[0][1], d[1][0]+m.d[1][0], d[1][1]+m.d[1][1]); }\n\tmat2d operator - (const mat2d& m) { return mat2d(d[0][0]-m.d[0][0], d[0][1]-m.d[0][1], d[1][0]-m.d[1][0], d[1][1]-m.d[1][1]); }\n\tmat2d operator * (double g) { return mat2d(d[0][0]*g, d[0][1]*g, d[1][0]*g, d[1][1]*g); }\n\tmat2d operator / (double g) { return mat2d(d[0][0]/g, d[0][1]/g, d[1][0]/g, d[1][1]/g); }\n\n\tmat2d& operator += (const mat2d& m) { d[0][0] += m.d[0][0]; d[0][1] += m.d[0][1]; d[1][0] += m.d[1][0]; d[1][1] += m.d[1][1]; return *this; }\n\tmat2d& operator -= (const mat2d& m) { d[0][0] -= m.d[0][0]; d[0][1] -= m.d[0][1]; d[1][0] -= m.d[1][0]; d[1][1] -= m.d[1][1]; return *this; }\n\tmat2d& operator *= (double g) { d[0][0] *= g; d[0][1] *= g; d[1][0] *= g; d[1][1] *= g; return *this; }\n\tmat2d& operator /= (double g) { d[0][0] /= g; d[0][1] /= g; d[1][0] /= g; d[1][1] /= g; return *this; }\n\n\tmat2d operator * (const mat2d& m) { \n\t\treturn mat2d(\n\t\t\td[0][0]*m.d[0][0]+d[0][1]*m.d[1][0],\n\t\t\td[0][0]*m.d[0][1]+d[0][1]*m.d[1][1],\n\t\t\td[1][0]*m.d[0][0]+d[1][1]*m.d[1][0],\n\t\t\td[1][0]*m.d[0][1]+d[1][1]*m.d[1][1]); \n\t}\n\npublic:\t// matrix operations\n\tmat2d inverse() const\n\t{\n\t\tdouble Di = 1/(d[0][0]*d[1][1] - d[0][1]*d[1][0]);\n\t\treturn mat2d(d[1][1]*Di, -d[0][1]*Di, -d[1][0]*Di, d[0][0]*Di);\n\t}\n\n\tmat2d transpose() const\n\t{\n\t\treturn mat2d(d[0][0], d[1][0], d[0][1], d[1][1]);\n\t}\n\n\tvoid zero()\n\t{\n\t\td[0][0] = d[0][1] = d[1][0] = d[1][1] = 0.0;\n\t}\n\n\tvoid identity()\n\t{\n\t\td[0][0] = d[1][1] = 1.0;\n\t\td[0][1] = d[1][0] = 0.0;\n\t}\n\t\nprotected:\n\tdouble\td[2][2];\n};\n\n// matrix-vector operations\ninline vec2d operator * (mat2d& m, vec2d& a) { return vec2d(m[0][0]*a[0]+m[0][1]*a[1], m[1][0]*a[0]+m[1][1]*a[1]); }\n\n// dyadic product\ninline mat2d dyad(vec2d& a, vec2d& b) { return mat2d(a.r[0]*b.r[0], a.r[0]*b.r[1], a.r[1]*b.r[0], a.r[1]*b.r[1]); }\n"
  },
  {
    "path": "FECore/mat3d.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"mat3d.h\"\n#include \"eig3.h\"\n#include \"sys.h\"\n\n#define ROTATE(a, i, j, k, l) g=a[i][j]; h=a[k][l];a[i][j]=g-s*(h+g*tau); a[k][l] = h + s*(g - h*tau);\n\nvoid mat3ds::eigen(double l[3], vec3d r[3]) const\n{\n\tconst int NMAX = 50;\n\tdouble sm, tresh, g, h, t, c, tau, s, th;\n\tint i, j, k;\n\n\t// copy the matrix components since we will be overwriting them\n\tdouble a[3][3] = {\n\t\t\t{m[XX], m[XY], m[XZ]},\n\t\t\t{m[XY], m[YY], m[YZ]}, \n\t\t\t{m[XZ], m[YZ], m[ZZ]}\n\t};\n\n\t// the v matrix contains the eigen vectors\n\t// intialize to identity\n\tdouble v[3][3] = {\n\t\t{ 1, 0, 0 },\n\t\t{ 0, 1, 0 },\n\t\t{ 0, 0, 1 }\n\t};\n\n\t// initialize b and d to the diagonal of a\n\tdouble b[3] = {a[0][0], a[1][1], a[2][2]};\n\tdouble d[3] = {a[0][0], a[1][1], a[2][2]};\n\tdouble z[3] = {0};\n\n\tconst double eps = 0;//1.0e-15;\n\n\t// loop\n\tint n, nrot = 0;\n\tfor (n=0; n<NMAX; ++n)\n\t{\n\t\t// sum off-diagonal elements\n\t\tsm = fabs(a[0][1]) + fabs(a[0][2]) + fabs(a[1][2]);\n\t\tif (sm <= eps) break;\n\n\t\t// set the treshold\n\t\tif (n < 3) tresh = 0.2*sm/9.0; else tresh = 0.0;\n\n\t\t// loop over off-diagonal elements\n\t\tfor (i=0; i<2; ++i)\n\t\t{\n\t\t\tfor (j=i+1; j<3; ++j)\n\t\t\t{\n\t\t\t\tg = 100.0*fabs(a[i][j]);\n\n\t\t\t\t// after four sweeps, skip the rotation if the off-diagonal element is small\n\t\t\t\tif ((n > 3) && ((fabs(d[i])+g) == fabs(d[i]))\n\t\t\t\t\t\t\t&& ((fabs(d[j])+g) == fabs(d[j])))\n\t\t\t\t{\n\t\t\t\t\ta[i][j] = 0.0;\n\t\t\t\t}\n\t\t\t\telse if (fabs(a[i][j]) > tresh)\n\t\t\t\t{\n\t\t\t\t\th = d[j] - d[i];\n\t\t\t\t\tif ((fabs(h)+g) == fabs(h))\n\t\t\t\t\t\tt = a[i][j]/h;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tth = 0.5*h/a[i][j];\n\t\t\t\t\t\tt = 1.0/(fabs(th) + sqrt(1+th*th));\n\t\t\t\t\t\tif (th < 0.0) t = -t;\n\t\t\t\t\t}\n\n\t\t\t\t\tc = 1.0/sqrt(1.0 + t*t);\n\t\t\t\t\ts = t*c;\n\t\t\t\t\ttau = s/(1.0+c);\n\t\t\t\t\th = t*a[i][j];\n\t\t\t\t\tz[i] -= h;\n\t\t\t\t\tz[j] += h;\n\t\t\t\t\td[i] -= h;\n\t\t\t\t\td[j] += h;\n\t\t\t\t\ta[i][j] = 0;\n\n\t\t\t\t\tfor (k=  0; k<=i-1; ++k) { ROTATE(a, k, i, k, j) }\n\t\t\t\t\tfor (k=i+1; k<=j-1; ++k) { ROTATE(a, i, k, k, j) }\n\t\t\t\t\tfor (k=j+1; k<   3; ++k) { ROTATE(a, i, k, j, k) }\n\t\t\t\t\tfor (k=  0; k<   3; ++k) { ROTATE(v, k, i, k, j) }\n\t\t\t\t\t++nrot;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i=0; i<3; ++i) \n\t\t{\n\t\t\tb[i] += z[i];\n\t\t\td[i] = b[i];\n\t\t\tz[i] = 0.0;\n\t\t}\n\t}\n\n\t// we sure we converged\n\tassert(n < NMAX);\n\n\t// copy eigenvalues\n\tl[0] = d[0];\n\tl[1] = d[1];\n\tl[2] = d[2];\n\n\t// copy eigenvectors\n\tif (r)\n\t{\n\t\tr[0].x = v[0][0]; r[0].y = v[1][0]; r[0].z = v[2][0];\n\t\tr[1].x = v[0][1]; r[1].y = v[1][1]; r[1].z = v[2][1];\n\t\tr[2].x = v[0][2]; r[2].y = v[1][2]; r[2].z = v[2][2];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the eigenvalues of A using an analytical expression for the \n// eigen values.\nvoid mat3ds::exact_eigen(double l[3]) const\n{\n\tconst double S3 = sqrt(3.0);\n\tconst double S2 = sqrt(2.0);\n\n\tmat3ds S = dev();\n\tdouble nS = S.norm();\n\tmat3ds T = (S.sqr()).dev();\n\tdouble nT = T.norm();\n\n\tdouble D = nS * nT;\n\tif (D > 0.0)\n\t{\n\t\tdouble w = S.dotdot(T) / D;\tif (w > 1.0) w = 1.0; if (w < -1.0) w = -1.0;\n\t\tdouble t = asin(w) / 3.0;\n\t\tdouble r = S.norm();\n\t\tdouble z = tr() / S3;\n\n\t\tl[0] = z / S3 + (r / S2)*(sin(t) / S3 + cos(t));\n\t\tl[1] = z / S3 - (S2 / S3)*r*sin(t);\n\t\tl[2] = z / S3 + (r / S2)*(sin(t) / S3 - cos(t));\n\t}\n\telse\n\t{\n        // check it matrix is exact diagonal\n        if ((m[1] == 0) && (m[3] == 0) && (m[4] == 0)) {\n            l[0] = m[0]; l[1] = m[2]; l[2] = m[5];\n        }\n        else {\n            l[0] = l[1] = l[2] = 0.0;\n        }\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the eigenvalues and eigenvectors of A using the method of\n// Connelly Barnes ( http://barnesc.blogspot.com/2007/02/eigenvectors-of-3x3-symmetric-matrix.html )\nvoid mat3ds::eigen2(double l[3], vec3d r[3]) const\n{\n    double A[3][3] = {xx(), xy(), xz(), xy(), yy(), yz(), xz(), yz(), zz()};\n    double V[3][3];\n    if (ISNAN(tr())) return;\n    eigen_decomposition(A, V, l);\n    if (r) {\n        r[0] = vec3d(V[0][0],V[1][0],V[2][0]);\n        r[1] = vec3d(V[0][1],V[1][1],V[2][1]);\n        r[2] = vec3d(V[0][2],V[1][2],V[2][2]);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// calculates the unique right polar decomposition F = R*U\nvoid mat3d::right_polar(mat3d& R, mat3ds& U) const\n{\n\tconst mat3d& F = *this;\n\tmat3ds C = (F.transpose()*F).sym();\n\n\tdouble l[3];\n\tvec3d v[3];\n\tC.eigen2(l, v);\n\n\tU = dyad(v[0])*sqrt(l[0]) + dyad(v[1])*sqrt(l[1]) + dyad(v[2])*sqrt(l[2]);\n\tR = F*U.inverse();\n}\n\n//-----------------------------------------------------------------------------\n// calculates the unique left polar decomposition F = V*R\nvoid mat3d::left_polar(mat3ds& V, mat3d& R) const\n{\n\tconst mat3d& F = *this;\n\tmat3ds b = (F*F.transpose()).sym();\n\n\tdouble l[3];\n\tvec3d v[3];\n\tb.eigen2(l, v);\n\n\tV = dyad(v[0])*sqrt(l[0]) + dyad(v[1])*sqrt(l[1]) + dyad(v[2])*sqrt(l[2]);\n\tR = V.inverse()*F;\n}\n\n//-----------------------------------------------------------------------------\n// the \"max shear\" value\ndouble mat3ds::max_shear() const\n{\n\tdouble e[3];\n\texact_eigen(e);\n\n\tdouble t1 = fabs(0.5f*(e[1] - e[2]));\n\tdouble t2 = fabs(0.5f*(e[2] - e[0]));\n\tdouble t3 = fabs(0.5f*(e[0] - e[1]));\n\n\t// TODO: is this necessary? I think the values returned\n\t//       by Principals are already ordered.\n\tdouble tmax = t1;\n\tif (t2 > tmax) tmax = t2;\n\tif (t3 > tmax) tmax = t3;\n\n\treturn tmax;\n}\n\n\n//-----------------------------------------------------------------------------\nvoid mat3fs::Principals(float e[3]) const\n{\n\tconst static float ONETHIRD = 1.f / 3.f;\n\n\t// pressure\n\tfloat p = -(x + y + z) * ONETHIRD;\n\n\tDeviatoricPrincipals(e);\n\n\te[0] -= p;\n\te[1] -= p;\n\te[2] -= p;\n}\n\n//-----------------------------------------------------------------------------\nvoid mat3fs::DeviatoricPrincipals(float e[3]) const\n{\n\tconst static float ONETHIRD = 1.f / 3.f;\n\n\t// pressure\n\tfloat p = -(x + y + z) * ONETHIRD;\n\n\t// deviatoric stresses\n\tfloat dev[3];\n\tdev[0] = x + p;\n\tdev[1] = y + p;\n\tdev[2] = z + p;\n\n\t// invariants\n\tfloat I[3];\n\tI[0] = dev[0] + dev[1] + dev[2]; // = tr[s']\n\n\tI[1] = 0.5f * (dev[0] * dev[0]\n\t\t+ dev[1] * dev[1]\n\t\t+ dev[2] * dev[2])\n\t\t+ xy * xy\n\t\t+ yz * yz\n\t\t+ xz * xz; // = s':s'\n\n\tI[2] = -dev[0] * dev[1] * dev[2] - 2.0f * xy * yz * xz\n\t\t+ dev[0] * yz * yz\n\t\t+ dev[1] * xz * xz\n\t\t+ dev[2] * xy * xy; // = -det(s')\n\n\t\t\t\t\t\t  // check to see if we can have a non-zero divisor, if no\n\t\t\t\t\t\t  // set principal stress to 0\n\tif (I[1] != 0)\n\t{\n\t\tdouble a = -0.5 * sqrt(27.0 / I[1]) * I[2] / I[1];\n\t\tif (a < 0)\n\t\t\ta = MAX(a, -1.0);\n\t\telse\n\t\t\ta = MIN(a, 1.0);\n\t\tdouble w = acos(a) * ONETHIRD;\n\t\tdouble val = 2.0 * sqrt(I[1] * ONETHIRD);\n\t\te[0] = (float)(val * cos(w));\n\t\tw = w - 2.0 * PI * ONETHIRD;\n\t\te[1] = (float)(val * cos(w));\n\t\tw = w + 4.0 * PI * ONETHIRD;\n\t\te[2] = (float)(val * cos(w));\n\t}\n\telse\n\t{\n\t\te[0] = e[1] = e[2] = 0.f;\n\t}\n}\n\n//-----------------------------------------------------------------------------\nfloat mat3fs::MaxShear() const\n{\n\tfloat e[3];\n\tPrincipals(e);\n\n\tfloat t1 = (float)fabs(0.5f * (e[1] - e[2]));\n\tfloat t2 = (float)fabs(0.5f * (e[2] - e[0]));\n\tfloat t3 = (float)fabs(0.5f * (e[0] - e[1]));\n\n\t// TODO: is this necessary? I think the values returned\n\t//       by Principals are already ordered.\n\tfloat tmax = t1;\n\tif (t2 > tmax) tmax = t2;\n\tif (t3 > tmax) tmax = t3;\n\n\treturn tmax;\n}\n\n//-----------------------------------------------------------------------------\n#define ROTATE(a, i, j, k, l) g=a[i][j]; h=a[k][l];a[i][j]=g-s*(h+g*tau); a[k][l] = h + s*(g - h*tau);\n#define SWAPF(a, b) { float t = a; a = b; b = t; }\n#define SWAPV(a, b) { vec3f t = a; a = b; b = t; }\n\nvoid mat3fs::eigen(vec3f e[3], float l[3]) const\n{\n\tconst int NMAX = 50;\n\tdouble sm, tresh, g, h, t, c, tau, s, th;\n\tint i, j, k;\n\n\t// copy the Matrix components since we will be overwriting them\n\tdouble a[3][3] = {\n\t\t{ x , xy, xz },\n\t\t{ xy, y , yz },\n\t\t{ xz, yz, z }\n\t};\n\n\t// the v Matrix contains the eigen vectors\n\t// intialize to identity\n\tdouble v[3][3] = {\n\t\t{ 1, 0, 0 },\n\t\t{ 0, 1, 0 },\n\t\t{ 0, 0, 1 }\n\t};\n\n\t// initialize b and d to the diagonal of a\n\tdouble b[3] = { a[0][0], a[1][1], a[2][2] };\n\tdouble d[3] = { a[0][0], a[1][1], a[2][2] };\n\tdouble z[3] = { 0 };\n\n\tconst double eps = 0;//1.0e-15;\n\n\t\t\t\t\t\t // loop\n\tint n, nrot = 0;\n\tfor (n = 0; n < NMAX; ++n)\n\t{\n\t\t// sum off-diagonal elements\n\t\tsm = fabs(a[0][1]) + fabs(a[0][2]) + fabs(a[1][2]);\n\t\tif (sm <= eps) break;\n\n\t\t// set the treshold\n\t\tif (n < 3) tresh = 0.2 * sm / 9.0; else tresh = 0.0;\n\n\t\t// loop over off-diagonal elements\n\t\tfor (i = 0; i < 2; ++i)\n\t\t{\n\t\t\tfor (j = i + 1; j < 3; ++j)\n\t\t\t{\n\t\t\t\tg = 100.0 * fabs(a[i][j]);\n\n\t\t\t\t// after four sweeps, skip the rotation if the off-diagonal element is small\n\t\t\t\tif ((n > 3) && ((fabs(d[i]) + g) == fabs(d[i]))\n\t\t\t\t\t&& ((fabs(d[j]) + g) == fabs(d[j])))\n\t\t\t\t{\n\t\t\t\t\ta[i][j] = 0.0;\n\t\t\t\t}\n\t\t\t\telse if (fabs(a[i][j]) > tresh)\n\t\t\t\t{\n\t\t\t\t\th = d[j] - d[i];\n\t\t\t\t\tif ((fabs(h) + g) == fabs(h))\n\t\t\t\t\t\tt = a[i][j] / h;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tth = 0.5 * h / a[i][j];\n\t\t\t\t\t\tt = 1.0 / (fabs(th) + sqrt(1 + th * th));\n\t\t\t\t\t\tif (th < 0.0) t = -t;\n\t\t\t\t\t}\n\n\t\t\t\t\tc = 1.0 / sqrt(1.0 + t * t);\n\t\t\t\t\ts = t * c;\n\t\t\t\t\ttau = s / (1.0 + c);\n\t\t\t\t\th = t * a[i][j];\n\t\t\t\t\tz[i] -= h;\n\t\t\t\t\tz[j] += h;\n\t\t\t\t\td[i] -= h;\n\t\t\t\t\td[j] += h;\n\t\t\t\t\ta[i][j] = 0;\n\n\t\t\t\t\tfor (k = 0; k <= i - 1; ++k) { ROTATE(a, k, i, k, j) }\n\t\t\t\t\tfor (k = i + 1; k <= j - 1; ++k) { ROTATE(a, i, k, k, j) }\n\t\t\t\t\tfor (k = j + 1; k < 3; ++k) { ROTATE(a, i, k, j, k) }\n\t\t\t\t\tfor (k = 0; k < 3; ++k) { ROTATE(v, k, i, k, j) }\n\t\t\t\t\t++nrot;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < 3; ++i)\n\t\t{\n\t\t\tb[i] += z[i];\n\t\t\td[i] = b[i];\n\t\t\tz[i] = 0.0;\n\t\t}\n\t}\n\n\t// we sure we converged\n\tassert(n < NMAX);\n\n\t// copy eigenvalues\n\tl[0] = (float)d[0];\n\tl[1] = (float)d[1];\n\tl[2] = (float)d[2];\n\n\t// copy eigenvectors\n\te[0].x = (float)v[0][0]; e[0].y = (float)v[1][0]; e[0].z = (float)v[2][0];\n\te[1].x = (float)v[0][1]; e[1].y = (float)v[1][1]; e[1].z = (float)v[2][1];\n\te[2].x = (float)v[0][2]; e[2].y = (float)v[1][2]; e[2].z = (float)v[2][2];\n\n\t// we still need to sort the eigenvalues\n\tif (l[1] > l[0]) { SWAPF(l[0], l[1]); SWAPV(e[0], e[1]); }\n\tif (l[2] > l[0]) { SWAPF(l[0], l[2]); SWAPV(e[0], e[2]); }\n\tif (l[2] > l[1]) { SWAPF(l[2], l[1]); SWAPV(e[2], e[1]); }\n}\n\n//-----------------------------------------------------------------------------\nvec3f mat3fs::PrincDirection(int l)\n{\n\tvec3f e[3];\n\tfloat lam[3];\n\teigen(e, lam);\n\treturn e[l] * lam[l];\n}\n\n//-----------------------------------------------------------------------------\ndouble fractional_anisotropy(const mat3fs& m)\n{\n\tvec3f e[3];\n\tfloat l[3];\n\tm.eigen(e, l);\n\n\tdouble la = (l[0] + l[1] + l[2]) / 3.0;\n\tdouble D = sqrt(l[0] * l[0] + l[1] * l[1] + l[2] * l[2]);\n\tdouble fa = 0.0;\n\tif (D != 0) fa = sqrt(3.0 / 2.0) * sqrt((l[0] - la) * (l[0] - la) + (l[1] - la) * (l[1] - la) + (l[2] - la) * (l[2] - la)) / D;\n\n\treturn fa;\n}\n"
  },
  {
    "path": "FECore/mat3d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <assert.h>\n#include \"vec3d.h\"\n#include \"mat2d.h\"\n\n//-----------------------------------------------------------------------------\n// useful constants for trig\n#ifndef PI\n#define PI 3.141592653589793\n#endif\n\n#ifndef RAD2DEG\n#define RAD2DEG (180.0/PI)\n#endif\n\n#ifndef DEG2RAD\n#define DEG2RAD (PI/180.0)\n#endif\n\n#ifndef MAX\n#define MAX(a, b) ((a)>(b)?(a):(b))\n#endif\n\n#ifndef MIN\n#define MIN(a, b) ((a)<(b)?(a):(b))\n#endif\n\n//-----------------------------------------------------------------------------\n// The following classes are defined in this file\nclass mat3d;\t// general 3D matrix of doubles\nclass mat3ds;\t// symmetric 3D matrix of doubles\nclass mat3da;\t// anti-symmetric 3D matrix of doubles\nclass mat3dd;\t// diagonal matrix of doubles\n\n//-----------------------------------------------------------------------------\n//! This class describes a diagonal matrix of doubles in 3D\n\nclass mat3dd\n{\npublic:\n\t// default constructor\n\tmat3dd(){}\n\n\t// constructors\n\texplicit mat3dd(double a);\n\tmat3dd(double a0, double a1, double a2);\n\n\t// assignment operators\n\tmat3dd& operator = (const mat3dd& m);\n\tmat3dd& operator = (double a);\n\n\t// access operators\n\tdouble operator () (int i, int j) const;\n\tdouble& diag(int i);\n\tconst double& diag(int i) const;\n\n\t// arithmetic operators\n\tmat3dd operator + (const mat3dd& m) const;\n\tmat3dd operator - (const mat3dd& m) const;\n\tmat3dd operator * (const mat3dd& m) const;\n\tmat3dd operator * (double a) const;\n\tmat3dd operator / (double a) const;\n\n\tmat3dd operator - () const;\n\n\t// arithmetic operators for mat3ds\n\tmat3ds operator + (const mat3ds& m) const;\n\tmat3ds operator - (const mat3ds& m) const;\n\tmat3ds operator * (const mat3ds& m) const;\n\n\t// arithmetic operators for mat3d\n\tmat3d operator + (const mat3d& m) const;\n\tmat3d operator - (const mat3d& m) const;\n\tmat3d operator * (const mat3d& m) const;\n\n\t// arithmetic operators for mat3da const;\n\tmat3d operator + (const mat3da& m) const;\n\tmat3d operator - (const mat3da& m) const;\n\tmat3d operator * (const mat3da& m) const;\n\n\t// arithmetic assignment operators\n\tmat3dd& operator += (const mat3dd& m);\n\tmat3dd& operator -= (const mat3dd& m);\n\tmat3dd& operator *= (const mat3dd& m);\n\tmat3dd& operator *= (double a);\n\tmat3dd& operator /= (double a);\n\n\t// matrix-vector multiplication\n\tvec3d operator * (const vec3d& r) const;\n\n\t// trace\n\tdouble tr() const;\n\n\t// determinant\n\tdouble det() const;\n\n\tdouble xx() const { return d[0]; }\n\tdouble yy() const { return d[1]; }\n\tdouble zz() const { return d[2]; }\n\n\t// TODO: Make these constexpr\n\tdouble xy() const { return 0.0; }\n\tdouble yz() const { return 0.0; }\n\tdouble xz() const { return 0.0; }\n\nprotected:\n\tdouble\td[3];\t// the diagonal elements\n\n\tfriend class mat3d;\n\tfriend class mat3ds;\n\tfriend class mat3da;\n};\n\ninline mat3dd operator * (double a, const mat3dd& d) { return d*a; }\n\n//-----------------------------------------------------------------------------\n//! This class describes a symmetric 3D matrix of doubles\n\nclass mat3ds\n{\nprotected:\n\t// This enumeration can be used to remember the order\n\t// in which the components are stored.\n\tenum {\n\t\tXX = 0,\n\t\tXY = 1,\n\t\tYY = 2,\n\t\tXZ = 3,\n\t\tYZ = 4,\n\t\tZZ = 5 };\npublic:\n\t// default constructor\n\tmat3ds(){}\n\n\t// constructors\n\texplicit mat3ds(double a);\n\tmat3ds(double xx, double yy, double zz, double xy, double yz, double xz);\n\tmat3ds(const mat3dd& d);\n\tmat3ds(const mat3ds& d);\n\n\t// access operators\n\tdouble& operator () (int i, int j);\n\tconst double& operator () (int i, int j) const;\n\n\tdouble& xx() { return m[XX]; }\n\tdouble& yy() { return m[YY]; }\n\tdouble& zz() { return m[ZZ]; }\n\tdouble& xy() { return m[XY]; }\n\tdouble& yz() { return m[YZ]; }\n\tdouble& xz() { return m[XZ]; }\n\n\tconst double& xx() const { return m[XX]; }\n\tconst double& yy() const { return m[YY]; }\n\tconst double& zz() const { return m[ZZ]; }\n\tconst double& xy() const { return m[XY]; }\n\tconst double& yz() const { return m[YZ]; }\n\tconst double& xz() const { return m[XZ]; }\n\n\t// arithmetic operators for mat3dd objects\n\tmat3ds operator + (const mat3dd& d) const;\n\tmat3ds operator - (const mat3dd& d) const;\n\tmat3ds operator * (const mat3dd& d) const;\n\n\t// arithmetic operators\n\tmat3ds operator + (const mat3ds& t) const;\n\tmat3ds operator - (const mat3ds& t) const;\n\tmat3d  operator * (const mat3ds& t) const;\n\tmat3ds operator * (double g) const;\n\tmat3ds operator / (double g) const;\n\n\t// arithmetic operators for mat3d objects\n\tmat3d operator + (const mat3d& t) const;\n\tmat3d operator - (const mat3d& t) const;\n\tmat3d operator * (const mat3d& t) const;\n\t\n    // arithmetic operators for mat3da objects\n    mat3d operator + (const mat3da& t) const;\n    mat3d operator - (const mat3da& t) const;\n    mat3d operator * (const mat3da& t) const;\n    \n\t// unary operators\n\tmat3ds operator - () const;\n\t\n\t// arithmetic assignment operators\n\tmat3ds& operator += (const mat3ds& t);\n\tmat3ds& operator -= (const mat3ds& t);\n\tmat3ds& operator *= (const mat3ds& t);\n\tmat3ds& operator *= (double g);\n\tmat3ds& operator /= (double g);\n\n\t// arithmetic assignment operators for mat3dd\n\tmat3ds& operator += (const mat3dd& d);\n\tmat3ds& operator -= (const mat3dd& d);\n\n\t// comparison\n\tbool operator == (const mat3ds& d);\n\n\t// matrix-vector multiplication\n\tvec3d operator * (const vec3d& r) const;\n\n\t// trace\n\tdouble tr() const;\n\n\t// determinant\n\tdouble det() const;\n\n\t// intialize to zero\n\tvoid zero();\n\n\t// initialize to unit tensor\n\tvoid unit();\n\n\t// deviator\n\tmat3ds dev() const;\n\n\t// isotropic part\n\tmat3ds iso() const;\n\n\t// return the square \n\tmat3ds sqr() const;\n\n\t// calculates the inverse\n\tmat3ds inverse() const;\n    double invert(mat3ds& Ai);\n\t\n\t// determine eigen values and vectors\n\tFECORE_API void eigen(double d[3], vec3d r[3] = 0) const;\n\tFECORE_API void exact_eigen(double l[3]) const;\n\tFECORE_API void eigen2(double d[3], vec3d r[3] = 0) const;\n\n\t// L2-norm \n\tdouble norm() const;\n\n\t// double contraction\n\tdouble dotdot(const mat3ds& S) const;\n\n\t// \"effective\" or von-Mises norm\n\tdouble effective_norm() const;\n\n\t// the \"max shear\" value\n\tFECORE_API double max_shear() const;\n\nprotected:\n\tdouble m[6];\t// stores data in the order xx, xy, yy, xz, yz, zz\n\n\tfriend class mat3dd;\n\tfriend class mat3d;\n};\n\ninline mat3ds operator * (double a, const mat3ds& m) { return m*a; }\n\n//-----------------------------------------------------------------------------\n//! This class describes an anti-symmetric 3D matrix of doubles\n//! The matrix is defined such that for a vector b the following is true:\n//! A.b = a x b where A = mat3da(a).\n//!\n//     | 0 -z  y |   |   0  d0  d2 |\n// A = | z  0 -x | = | -d0   0  d1 |\n//     |-y  x  0 |   | -d2 -d1   0 |\n//\n\nclass mat3da\n{\npublic:\n\t// default constructor\n\tmat3da(){}\n\n\t// constructors\n\tmat3da(double xy, double yz, double xz);\n\n\t// calculates the antisymmetric matrix from a vector\n\t// A.b = a x b where A = mat3da(a).\n\texplicit mat3da(const vec3d& a);\n\n\t// access operator\n\tdouble operator () (int i, int j) const;\n\n\tdouble& xy() { return d[0]; }\n\tdouble& yz() { return d[1]; }\n\tdouble& xz() { return d[2]; }\n\n\tconst double& xy() const { return d[0]; }\n\tconst double& yz() const { return d[1]; }\n\tconst double& xz() const { return d[2]; }\n\n\tmat3da operator + (const mat3da& a);\n\tmat3da operator - (const mat3da& a);\n\n\tmat3da operator - () const;\n\n\tmat3da operator * (double g) const;\n\n\tmat3da transpose() const;\n\n\t// matrix algebra\n\tmat3d operator * (const mat3d& a);\n\n\t// return the equivalent vector\n\tvec3d vec() const { return vec3d(-d[1], d[2], -d[0]); }\n\n\tvec3d operator * (const vec3d& a);\n\n    // arithmetic operators for mat3ds objects\n    mat3d operator + (const mat3ds& t) const;\n    mat3d operator - (const mat3ds& t) const;\n    \nprotected:\n\tdouble\td[3];\t// stores xy, yz, xz\n\n\tfriend class mat3dd;\n\tfriend class mat3ds;\n\tfriend class mat3d;\n};\n\n\n//-----------------------------------------------------------------------------\n//! This class describes a general 3D matrix of doubles\nclass mat3d\n{\npublic:\n\t// default constructor\n\tmat3d() {}\n\n\t// constructs diagonal matrix with a on the diagonal\n\texplicit mat3d(double a);\n\n\t// constructors\n\tmat3d(double a00, double a01, double a02,\n\t\t  double a10, double a11, double a12,\n\t\t  double a20, double a21, double a22);\n\n\tmat3d(double m[3][3]);\n\tmat3d(double a[9]);\n\n\tmat3d(const mat3dd& m);\n\tmat3d(const mat3ds& m);\n\tmat3d(const mat3da& m);\n\n\tmat3d(const mat2d& m);\n\n\tmat3d(const vec3d& e1, const vec3d& e2, const vec3d& e3);\n\n\t// construct a matrix from two vectors a and b. (a and b not colinear!)\n\t// e1 = a.unit()\n\t// e3 = (a ^ b).unit()\n\t// e2 = e3 ^ e1\n\t// Q = [e1 e2 e3]\n\tmat3d(const vec3d& a, const vec3d& b);\n\n\t// assignment operators\n\tmat3d& operator = (const mat3dd& m);\n\tmat3d& operator = (const mat3ds& m);\n\tmat3d& operator = (const mat3d& m);\n\tmat3d& operator = (const double m[3][3]);\n\n\t// mat3d\n\tmat3d operator - ()  const\n\t{\n\t\treturn mat3d(-d[0][0], -d[0][1], -d[0][2], \\\n\t\t\t\t\t -d[1][0], -d[1][1], -d[1][2], \\\n\t\t\t\t\t -d[2][0], -d[2][1], -d[2][2]);\n\t}\n\n\t// access operators\n\tdouble& operator () (int i, int j);\n\tconst double& operator () (int i, int j) const;\n\tdouble* operator [] (int i);\n\tconst double* operator [] (int i) const;\n\n\t// arithmetic operators\n\tmat3d operator + (const mat3d& m) const;\n\tmat3d operator - (const mat3d& m) const;\n\tmat3d operator * (const mat3d& m) const;\n\tmat3d operator * (double a) const;\n\tmat3d operator / (double a) const;\n\n\t// arithmetic operators for mat3dd\n\tmat3d operator + (const mat3dd& m) const;\n\tmat3d operator - (const mat3dd& m) const;\n\tmat3d operator * (const mat3dd& m) const;\n\n\t// arithmetic operators for mat3ds\n\tmat3d operator + (const mat3ds& m) const;\n\tmat3d operator - (const mat3ds& m) const;\n\tmat3d operator * (const mat3ds& m) const;\n\n\t// arithmetic assignment operators\n\tmat3d& operator += (const mat3d& m);\n\tmat3d& operator -= (const mat3d& m);\n\tmat3d& operator *= (const mat3d& m);\n\tmat3d& operator *= (double a);\n\tmat3d& operator /= (double a);\n\n\t// arithmetic assignment operators for mat3dd\n\tmat3d& operator += (const mat3dd& m);\n\tmat3d& operator -= (const mat3dd& m);\n\tmat3d& operator *= (const mat3dd& m);\n\n\t// arithmetic assignment operators for mat3ds\n\tmat3d& operator += (const mat3ds& m);\n\tmat3d& operator -= (const mat3ds& m);\n\tmat3d& operator *= (const mat3ds& m);\n\n\t// matrix-vector muliplication\n\tvec3d operator * (const vec3d& r) const;\n\n\t// determinant\n\tdouble det() const;\n\n\t// trace\n\tdouble trace() const;\n\n\t// zero the matrix\n\tvoid zero();\n\n\t// make unit matrix\n\tvoid unit();\n\n\t// return a column vector from the matrix\n\tvec3d col(int j) const;\n\n\t// return a row vector from the matrix\n\tvec3d row(int j) const;\n\n\t// set the column of the matrix\n\tvoid setCol(int i, const vec3d& a);\n\n\t// set the row of the matrix\n\tvoid setRow(int i, const vec3d& a);\n\n\t// return the symmetric matrix 0.5*(A+A^T)\n\tmat3ds sym() const;\n\n\t// return the antisymmetric matrix 0.5*(A-A^T)\n\tmat3da skew() const;\n\n\t// calculates the inverse\n\tmat3d inverse() const;\n    \n\t// inverts the matrix.\n\tbool invert();\n\n\t// calculates the transpose\n\tmat3d transpose() const;\n\n\t// calculates the transposed inverse\n\tmat3d transinv() const;\n\n\t// calculate the skew-symmetric matrix from a vector\n\tvoid skew(const vec3d& v);\n\n\t// calculate the exponential map\n\tvoid exp(const vec3d& v);\n\n\t// calculate the one-norm\n\tdouble norm() const;\n\n\t// double contraction\n\tdouble dotdot(const mat3d& T) const;\n\n\t// polar decomposition\n\tFECORE_API void right_polar(mat3d& R, mat3ds& U) const;\n\tFECORE_API void left_polar(mat3ds& V, mat3d& R) const;\n\n\t// return identity matrix\n\tstatic mat3d identity() { return mat3d(1,0,0, 0,1,0, 0,0,1); }\n\nprotected:\n\tdouble d[3][3];\t// matrix data\n\n\tfriend class mat3dd;\n\tfriend class mat3ds;\n\tfriend class mat3da;\n};\n\n// outer product for vectors\ninline mat3d operator & (const vec3d& a, const vec3d& b)\n{\n\treturn mat3d(a.x*b.x, a.x*b.y, a.x*b.z,\n\t\t\t\t a.y*b.x, a.y*b.y, a.y*b.z,\n\t\t\t\t a.z*b.x, a.z*b.y, a.z*b.z);\n}\n\ninline mat3ds dyad(const vec3d& a)\n{\n\treturn mat3ds(a.x*a.x, a.y*a.y, a.z*a.z, a.x*a.y, a.y*a.z, a.x*a.z);\n}\n\n// c_ij = a_i*b_j + a_j*b_i\ninline mat3ds dyads(const vec3d& a, const vec3d& b)\n{\n\treturn mat3ds(2.0*a.x*b.x, 2.0*a.y*b.y, 2.0*a.z*b.z, a.x*b.y + a.y*b.x, a.y*b.z + a.z*b.y, a.x*b.z + a.z*b.x);\n}\n\n// skew-symmetric matrix of dual vector\ninline mat3d skew(const vec3d& a)\n{\n    return mat3d(   0, -a.z,  a.y,\n                  a.z,    0, -a.x,\n                 -a.y,  a.x,    0);\n}\n\n//-----------------------------------------------------------------------------\n// This class stores a 2nd order diagonal tensor\nclass mat3fd\n{\npublic:\n\tmat3fd() { x = y = z = 0.f; }\n\tmat3fd(float X, float Y, float Z) { x = X; y = Y; z = Z; }\n\npublic:\n\tfloat x, y, z;\n};\n\n//-----------------------------------------------------------------------------\n// mat3fs stores a 2nd order symmetric tensor\n//\nclass mat3fs\n{\npublic:\n\t// constructors\n\tmat3fs() { x = y = z = xy = yz = xz = 0; }\n\tmat3fs(float fx, float fy, float fz, float fxy, float fyz, float fxz)\n\t{\n\t\tx = fx; y = fy; z = fz;\n\t\txy = fxy; yz = fyz; xz = fxz;\n\t}\n\n\t// operators\n\tmat3fs& operator += (const mat3fs& v)\n\t{\n\t\tx += v.x;\n\t\ty += v.y;\n\t\tz += v.z;\n\t\txy += v.xy;\n\t\tyz += v.yz;\n\t\txz += v.xz;\n\n\t\treturn (*this);\n\t}\n\n\t// operators\n\tmat3fs& operator -= (const mat3fs& v)\n\t{\n\t\tx -= v.x;\n\t\ty -= v.y;\n\t\tz -= v.z;\n\t\txy -= v.xy;\n\t\tyz -= v.yz;\n\t\txz -= v.xz;\n\n\t\treturn (*this);\n\t}\n\n\tmat3fs& operator *= (float g)\n\t{\n\t\tx *= g;\n\t\ty *= g;\n\t\tz *= g;\n\t\txy *= g;\n\t\tyz *= g;\n\t\txz *= g;\n\n\t\treturn (*this);\n\t}\n\n\tmat3fs& operator /= (float g)\n\t{\n\t\tx /= g;\n\t\ty /= g;\n\t\tz /= g;\n\t\txy /= g;\n\t\tyz /= g;\n\t\txz /= g;\n\n\t\treturn (*this);\n\t}\n\n\tmat3fs operator + (const mat3fs& a) { return mat3fs(x + a.x, y + a.y, z + a.z, xy + a.xy, yz + a.yz, xz + a.xz); }\n\tmat3fs operator - (const mat3fs& a) { return mat3fs(x - a.x, y - a.y, z - a.z, xy - a.xy, yz - a.yz, xz - a.xz); }\n\n\tmat3fs operator * (float a)\n\t{\n\t\treturn mat3fs(x * a, y * a, z * a, xy * a, yz * a, xz * a);\n\t}\n\n\tmat3fs operator / (float g)\n\t{\n\t\treturn mat3fs(x / g, y / g, z / g, xy / g, yz / g, xz / g);\n\t}\n\n\tvec3f operator * (vec3f& r)\n\t{\n\t\treturn vec3f(\n\t\t\tx * r.x + xy * r.y + xz * r.z,\n\t\t\txy * r.x + y * r.y + yz * r.z,\n\t\t\txz * r.x + yz * r.y + z * r.z);\n\t}\n\n\t// Effective or von-mises value\n\tfloat von_mises() const\n\t{\n\t\tfloat vm;\n\t\tvm = x * x + y * y + z * z;\n\t\tvm -= x * y + y * z + x * z;\n\t\tvm += 3 * (xy * xy + yz * yz + xz * xz);\n\t\tvm = (float)sqrt(vm >= 0.0 ? vm : 0.0);\n\t\treturn vm;\n\t}\n\n\t// principle values\n\tFECORE_API void Principals(float e[3]) const;\n\n\t// principle directions\n\tFECORE_API vec3f PrincDirection(int l);\n\n\t// deviatroric principle values\n\tFECORE_API void DeviatoricPrincipals(float e[3]) const;\n\n\t// max-shear value\n\tFECORE_API float MaxShear() const;\n\n\t// eigen-vectors and values\n\tFECORE_API void eigen(vec3f e[3], float l[3]) const;\n\n\t// trace\n\tfloat tr() const { return x + y + z; }\n\n\t// determinant\n\tfloat det() const { return (x * y * z + xy * yz * xz + xz * xy * yz - y * xz * xz - x * yz * yz - z * xy * xy); }\n\n\t// L2 norm\n\tfloat norm() const {\n\t\tdouble d = x * x + y * y + z * z + 2 * (xy * xy + yz * yz + xz * xz);\n\t\treturn (float)sqrt(d);\n\t}\n\npublic:\n\tfloat x, y, z;\n\tfloat xy, yz, xz;\n};\n\nFECORE_API double fractional_anisotropy(const mat3fs& m);\n\n///////////////////////////////////////////////////////////////////\n// mat3f\n\nclass mat3f\n{\npublic:\n\tmat3f() { zero(); }\n\n\tmat3f(float a00, float a01, float a02, float a10, float a11, float a12, float a20, float a21, float a22)\n\t{\n\t\td[0][0] = a00; d[0][1] = a01; d[0][2] = a02;\n\t\td[1][0] = a10; d[1][1] = a11; d[1][2] = a12;\n\t\td[2][0] = a20; d[2][1] = a21; d[2][2] = a22;\n\t}\n\n\tmat3f(const mat3fs& a)\n\t{\n\t\td[0][0] = a.x; d[0][1] = a.xy; d[0][2] = a.xz;\n\t\td[1][0] = a.xy; d[1][1] = a.y; d[1][2] = a.yz;\n\t\td[2][0] = a.xz; d[2][1] = a.yz; d[2][2] = a.z;\n\t}\n\n\tfloat* operator [] (int i) { return d[i]; }\n\tconst float* operator [] (int i) const { return d[i]; }\n\n\tfloat& operator () (int i, int j) { return d[i][j]; }\n\tfloat operator () (int i, int j) const { return d[i][j]; }\n\n\tmat3f operator * (float a) const\n\t{\n\t\treturn mat3f(\\\n\t\t\td[0][0] * a, d[0][1] * a, d[0][2] * a, \\\n\t\t\td[1][0] * a, d[1][1] * a, d[1][2] * a, \\\n\t\t\td[2][0] * a, d[2][1] * a, d[2][2] * a);\n\t}\n\n\tmat3f operator * (mat3f& m)\n\t{\n\t\tmat3f a;\n\n\t\tint k;\n\t\tfor (k = 0; k < 3; k++)\n\t\t{\n\t\t\ta[0][0] += d[0][k] * m[k][0]; a[0][1] += d[0][k] * m[k][1]; a[0][2] += d[0][k] * m[k][2];\n\t\t\ta[1][0] += d[1][k] * m[k][0]; a[1][1] += d[1][k] * m[k][1]; a[1][2] += d[1][k] * m[k][2];\n\t\t\ta[2][0] += d[2][k] * m[k][0]; a[2][1] += d[2][k] * m[k][1]; a[2][2] += d[2][k] * m[k][2];\n\t\t}\n\n\t\treturn a;\n\t}\n\n\tvec3f operator * (const vec3f& a) const\n\t{\n\t\treturn vec3f(\n\t\t\td[0][0] * a.x + d[0][1] * a.y + d[0][2] * a.z,\n\t\t\td[1][0] * a.x + d[1][1] * a.y + d[1][2] * a.z,\n\t\t\td[2][0] * a.x + d[2][1] * a.y + d[2][2] * a.z\n\t\t\t);\n\t}\n\n\tmat3f& operator *= (float g)\n\t{\n\t\td[0][0] *= g;\td[0][1] *= g; d[0][2] *= g;\n\t\td[1][0] *= g;\td[1][1] *= g; d[1][2] *= g;\n\t\td[2][0] *= g;\td[2][1] *= g; d[2][2] *= g;\n\t\treturn (*this);\n\t}\n\n\tmat3f& operator /= (float g)\n\t{\n\t\td[0][0] /= g;\td[0][1] /= g; d[0][2] /= g;\n\t\td[1][0] /= g;\td[1][1] /= g; d[1][2] /= g;\n\t\td[2][0] /= g;\td[2][1] /= g; d[2][2] /= g;\n\t\treturn (*this);\n\t}\n\n\tmat3f operator + (const mat3f& a) const\n\t{\n\t\treturn mat3f( \\\n\t\t\td[0][0] + a.d[0][0], d[0][1] + a.d[0][1], d[0][2] + a.d[0][2], \\\n\t\t\td[1][0] + a.d[1][0], d[1][1] + a.d[1][1], d[1][2] + a.d[1][2], \\\n\t\t\td[2][0] + a.d[2][0], d[2][1] + a.d[2][1], d[2][2] + a.d[2][2]);\n\t}\n\n\tmat3f operator - (const mat3f& a) const\n\t{\n\t\treturn mat3f(\\\n\t\t\td[0][0] - a.d[0][0], d[0][1] - a.d[0][1], d[0][2] - a.d[0][2], \\\n\t\t\td[1][0] - a.d[1][0], d[1][1] - a.d[1][1], d[1][2] - a.d[1][2], \\\n\t\t\td[2][0] - a.d[2][0], d[2][1] - a.d[2][1], d[2][2] - a.d[2][2]);\n\t}\n\n\tmat3f operator += (const mat3f& a)\n\t{\n\t\td[0][0] += a.d[0][0]; d[0][1] += a.d[0][1]; d[0][2] += a.d[0][2];\n\t\td[1][0] += a.d[1][0]; d[1][1] += a.d[1][1]; d[1][2] += a.d[1][2];\n\t\td[2][0] += a.d[2][0]; d[2][1] += a.d[2][1]; d[2][2] += a.d[2][2];\n\t\treturn (*this);\n\t}\n\n\tmat3f operator -= (const mat3f& a)\n\t{\n\t\td[0][0] -= a.d[0][0]; d[0][1] -= a.d[0][1]; d[0][2] -= a.d[0][2];\n\t\td[1][0] -= a.d[1][0]; d[1][1] -= a.d[1][1]; d[1][2] -= a.d[1][2];\n\t\td[2][0] -= a.d[2][0]; d[2][1] -= a.d[2][1]; d[2][2] -= a.d[2][2];\n\t\treturn (*this);\n\t}\n\n\tmat3fs sym() const\n\t{\n\t\treturn mat3fs(d[0][0], d[1][1], d[2][2], 0.5f * (d[0][1] + d[1][0]), 0.5f * (d[1][2] + d[2][1]), 0.5f * (d[0][2] + d[2][0]));\n\t}\n\n\tvoid zero()\n\t{\n\t\td[0][0] = d[0][1] = d[0][2] = 0.f;\n\t\td[1][0] = d[1][1] = d[1][2] = 0.f;\n\t\td[2][0] = d[2][1] = d[2][2] = 0.f;\n\t}\n\n\tvec3f col(int i) const\n\t{\n\t\tvec3f r;\n\t\tswitch (i)\n\t\t{\n\t\tcase 0: r.x = d[0][0]; r.y = d[1][0]; r.z = d[2][0]; break;\n\t\tcase 1: r.x = d[0][1]; r.y = d[1][1]; r.z = d[2][1]; break;\n\t\tcase 2: r.x = d[0][2]; r.y = d[1][2]; r.z = d[2][2]; break;\n\t\t}\n\t\treturn r;\n\t}\n\n\tvec3f row(int i) const\n\t{\n\t\tvec3f r;\n\t\tswitch (i)\n\t\t{\n\t\tcase 0: r.x = d[0][0]; r.y = d[0][1]; r.z = d[0][2]; break;\n\t\tcase 1: r.x = d[1][0]; r.y = d[1][1]; r.z = d[1][2]; break;\n\t\tcase 2: r.x = d[2][0]; r.y = d[2][1]; r.z = d[2][2]; break;\n\t\t}\n\t\treturn r;\n\t}\n\n\tmat3f transpose() const\n\t{\n\t\treturn mat3f(\n\t\t\td[0][0], d[1][0], d[2][0],\n\t\t\td[0][1], d[1][1], d[2][1],\n\t\t\td[0][2], d[1][2], d[2][2]\n\t\t);\n\t}\n\n\t// inverts the matrix.\n\tbool invert();\n\npublic:\n\tfloat d[3][3];\n};\n\ninline mat3f to_mat3f(const mat3d& m)\n{\n\treturn mat3f(\n\t\t(float)m[0][0], (float)m[0][1], (float)m[0][2],\n\t\t(float)m[1][0], (float)m[1][1], (float)m[1][2],\n\t\t(float)m[2][0], (float)m[2][1], (float)m[2][2]);\n}\n\ninline mat3d to_mat3d(const mat3f& m)\n{\n\treturn mat3d(\n\t\tm[0][0], m[0][1], m[0][2],\n\t\tm[1][0], m[1][1], m[1][2],\n\t\tm[2][0], m[2][1], m[2][2]);\n}\n\n// The following file contains the actual definition of the class functions\n#include \"mat3d.hpp\"\n"
  },
  {
    "path": "FECore/mat3d.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from mat3d.h\n// Users should not include this file manually!\n\n//-----------------------------------------------------------------------------\n// class mat3dd : class describing diagonal 3D matrices of doubles\n//-----------------------------------------------------------------------------\n\n// constructor\ninline mat3dd::mat3dd(double a) { d[0] = d[1] = d[2] = a; }\ninline mat3dd::mat3dd(double a0, double a1, double a2) { d[0] = a0; d[1] = a1; d[2] = a2; }\n\n// assignment operators\ninline mat3dd& mat3dd::operator = (const mat3dd& m) { d[0] = m.d[0]; d[1] = m.d[1]; d[2] = m.d[2]; return (*this); }\ninline mat3dd& mat3dd::operator = (double a) { d[0] = d[1] = d[2] = a; return (*this); }\n\n// access operators\ninline double mat3dd::operator () (int i, int j) const { return (i==j? d[i] : 0); }\ninline double& mat3dd::diag(int i) { return d[i]; }\ninline const double& mat3dd::diag(int i) const { return d[i]; }\n\n// arithmetic operators\ninline mat3dd mat3dd::operator + (const mat3dd& m) const { return mat3dd(d[0]+m.d[0],d[1]+m.d[1],d[2]+m.d[2]);}\ninline mat3dd mat3dd::operator - (const mat3dd& m) const { return mat3dd(d[0]-m.d[0],d[1]-m.d[1],d[2]-m.d[2]);}\ninline mat3dd mat3dd::operator * (const mat3dd& m) const { return mat3dd(d[0]*m.d[0],d[1]*m.d[1],d[2]*m.d[2]);}\ninline mat3dd mat3dd::operator * (double a) const { return mat3dd(d[0]*a, d[1]*a, d[2]*a); }\ninline mat3dd mat3dd::operator / (double a) const { a = 1./a; return mat3dd(d[0]*a, d[1]*a, d[2]*a); }\ninline mat3dd mat3dd::operator - () const { return mat3dd(-d[0], -d[1], -d[2]); }\n\n// arithmetic operators with mat3ds\ninline mat3ds mat3dd::operator + (const mat3ds& m) const\n{\n\treturn mat3ds(\n\t\td[0]+m.m[mat3ds::XX],\n\t\td[1]+m.m[mat3ds::YY],\n\t\td[2]+m.m[mat3ds::ZZ],\n\t\tm.m[mat3ds::XY],\n\t\tm.m[mat3ds::YZ],\n\t\tm.m[mat3ds::XZ]\n\t\t);\n}\n\ninline mat3ds mat3dd::operator - (const mat3ds& m) const\n{\n\treturn mat3ds(\n\t\td[0]-m.m[mat3ds::XX],\n\t\td[1]-m.m[mat3ds::YY],\n\t\td[2]-m.m[mat3ds::ZZ],\n\t\t-m.m[mat3ds::XY],\n\t\t-m.m[mat3ds::YZ],\n\t\t-m.m[mat3ds::XZ]\n\t\t);\n}\n\n\ninline mat3ds mat3dd::operator * (const mat3ds& m) const\n{\n\treturn mat3ds(\n\t\td[0]*m.m[mat3ds::XX],\n\t\td[1]*m.m[mat3ds::YY],\n\t\td[2]*m.m[mat3ds::ZZ],\n\t\td[0]*m.m[mat3ds::XY],\n\t\td[1]*m.m[mat3ds::YZ],\n\t\td[0]*m.m[mat3ds::XZ]\n\t);\n}\n\n// arithmetic operators for mat3d\ninline mat3d mat3dd::operator + (const mat3d& m) const\n{\n\treturn mat3d(d[0]+m.d[0][0], m.d[0][1], m.d[0][2],\n\t\t\t\t m.d[1][0], d[1]+m.d[1][1], m.d[1][2],\n\t\t\t\t m.d[2][0], m.d[2][1], d[2]+m.d[2][2]);\n}\n\ninline mat3d mat3dd::operator - (const mat3d& m) const\n{\n\treturn mat3d(d[0]-m.d[0][0], -m.d[0][1], -m.d[0][2],\n\t\t\t\t -m.d[1][0], d[1]-m.d[1][1], -m.d[1][2],\n\t\t\t\t -m.d[2][0], -m.d[2][1], d[2]-m.d[2][2]);\n}\n\ninline mat3d mat3dd::operator * (const mat3d& m) const\n{\n\treturn mat3d(d[0]*m.d[0][0], d[0]*m.d[0][1], d[0]*m.d[0][2],\n\t\t\t\t d[1]*m.d[1][0], d[1]*m.d[1][1], d[1]*m.d[1][2],\n\t\t\t\t d[2]*m.d[2][0], d[2]*m.d[2][1], d[2]*m.d[2][2]);\n}\n\n// arithmetic operators for mat3da\ninline mat3d mat3dd::operator + (const mat3da& m) const\n{\n\treturn mat3d(   d[0],  m.d[0], m.d[2],\n\t\t\t\t -m.d[0],    d[1], m.d[1],\n\t\t\t\t -m.d[2], -m.d[1],   d[2]);\n}\n\ninline mat3d mat3dd::operator - (const mat3da& m) const\n{\n\treturn mat3d(  d[0],-m.d[0], -m.d[2],\n\t\t\t\t m.d[0],   d[1], -m.d[1],\n\t\t\t\t m.d[2], m.d[1],   d[2]);\n}\n\ninline mat3d mat3dd::operator *(const mat3da& m) const\n{\n\treturn mat3d(           0,  d[0]*m.d[0], d[0]*m.d[2],\n\t\t\t\t -d[1]*m.d[0],            0, d[1]*m.d[1],\n\t\t\t\t -d[2]*m.d[2], -d[2]*m.d[1],           0);\n}\n\n// arithmetic assignment operators\ninline mat3dd& mat3dd::operator += (const mat3dd& m) { d[0] += m.d[0]; d[1] += m.d[1]; d[2] += m.d[2]; return (*this); }\ninline mat3dd& mat3dd::operator -= (const mat3dd& m) { d[0] -= m.d[0]; d[1] -= m.d[1]; d[2] -= m.d[2]; return (*this); }\ninline mat3dd& mat3dd::operator *= (const mat3dd& m) { d[0] *= m.d[0]; d[1] *= m.d[1]; d[2] *= m.d[2]; return (*this); }\ninline mat3dd& mat3dd::operator *= (double a) { d[0] *= a; d[1] *= a; d[2] *= a; return (*this); }\ninline mat3dd& mat3dd::operator /= (double a) { a = 1./a; d[0] *= a; d[1] *= a; d[2] *= a; return (*this); }\n\n// matrix-vector multiplication\ninline vec3d mat3dd::operator * (const vec3d& r) const { return vec3d(r.x*d[0], r.y*d[1], r.z*d[2]); }\n\n// trace\ninline double mat3dd::tr() const { return d[0]+d[1]+d[2]; }\n\n// determinant\ninline double mat3dd::det() const { return d[0]*d[1]*d[2]; }\n\n//-----------------------------------------------------------------------------\n// class mat3ds : this class describes a symmetric 3D matrix of doubles\n//-----------------------------------------------------------------------------\n\n// constructor\ninline mat3ds::mat3ds(double a)\n{\n\tm[XX] = a;\n\tm[XY] = a;\n\tm[YY] = a;\n\tm[XZ] = a;\n\tm[YZ] = a;\n\tm[ZZ] = a;\n}\n\ninline mat3ds::mat3ds(double xx, double yy, double zz, double xy, double yz, double xz)\n{\n\tm[XX] = xx;\n\tm[XY] = xy;\n\tm[YY] = yy;\n\tm[XZ] = xz;\n\tm[YZ] = yz;\n\tm[ZZ] = zz;\n}\n\ninline mat3ds::mat3ds(const mat3dd& d)\n{\n\tm[XX] = d.d[0];\n\tm[YY] = d.d[1];\n\tm[ZZ] = d.d[2];\n\tm[XY] = m[YZ] = m[XZ] = 0.;\n}\n\ninline mat3ds::mat3ds(const mat3ds& d)\n{\n\tm[0] = d.m[0];\n\tm[1] = d.m[1];\n\tm[2] = d.m[2];\n\tm[3] = d.m[3];\n\tm[4] = d.m[4];\n\tm[5] = d.m[5];\n}\n\n// access operator\ninline double& mat3ds::operator ()(int i, int j)\n{\n\tconst int n[] = {0, 1, 3};\n\treturn (i<=j? m[n[j]+i] : m[n[i]+j]);\n}\n\n// access operator for const objects\ninline const double& mat3ds::operator ()(int i, int j) const\n{\n\tconst int n[] = {0, 1, 3};\n\treturn (i<=j? m[n[j]+i] : m[n[i]+j]);\n}\n\n// operator + for mat3dd objects\ninline mat3ds mat3ds::operator + (const mat3dd& d) const\n{\n\treturn mat3ds(m[XX]+d.d[0], m[YY]+d.d[1], m[ZZ]+d.d[2], m[XY], m[YZ], m[XZ]);\n}\n\n// operator - for mat3dd objects\ninline mat3ds mat3ds::operator - (const mat3dd& d) const\n{\n\treturn mat3ds(m[XX]-d.d[0], m[YY]-d.d[1], m[ZZ]-d.d[2], m[XY], m[YZ], m[XZ]);\n}\n\n// operator * for mat3dd objects\ninline mat3ds mat3ds::operator * (const mat3dd& d) const\n{\n\treturn mat3ds(m[XX]*d.d[0], m[YY]*d.d[1], m[ZZ]*d.d[2], m[XY]*d.d[1], m[YZ]*d.d[2], m[XZ]*d.d[2]);\n}\n\n\n// operator +\ninline mat3ds mat3ds::operator +(const mat3ds& t) const\n{\n\treturn mat3ds(m[XX]+t.m[XX], m[YY]+t.m[YY], m[ZZ]+t.m[ZZ], m[XY]+t.m[XY], m[YZ]+t.m[YZ],m[XZ]+t.m[XZ]);\n}\n\n// operator -\ninline mat3ds mat3ds::operator -(const mat3ds& t) const\n{\n\treturn mat3ds(m[XX]-t.m[XX], m[YY]-t.m[YY], m[ZZ]-t.m[ZZ], m[XY]-t.m[XY], m[YZ]-t.m[YZ],m[XZ]-t.m[XZ]);\n}\n\n// operator *\ninline mat3d mat3ds::operator *(const mat3ds& t) const\n{\n\treturn mat3d(\n\t\tm[XX] * t.m[XX] + m[XY] * t.m[XY] + m[XZ] * t.m[XZ],\n\t\tm[XX] * t.m[XY] + m[XY] * t.m[YY] + m[XZ] * t.m[YZ],\n\t\tm[XX] * t.m[XZ] + m[XY] * t.m[YZ] + m[XZ] * t.m[ZZ],\n\n\t\tm[XY] * t.m[XX] + m[YY] * t.m[XY] + m[YZ] * t.m[XZ],\n\t\tm[XY] * t.m[XY] + m[YY] * t.m[YY] + m[YZ] * t.m[YZ],\n\t\tm[XY] * t.m[XZ] + m[YY] * t.m[YZ] + m[YZ] * t.m[ZZ],\n\n\t\tm[XZ] * t.m[XX] + m[YZ] * t.m[XY] + m[ZZ] * t.m[XZ],\n\t\tm[XZ] * t.m[XY] + m[YZ] * t.m[YY] + m[ZZ] * t.m[YZ],\n\t\tm[XZ] * t.m[XZ] + m[YZ] * t.m[YZ] + m[ZZ] * t.m[ZZ]\n\t);\n}\n\n// operator *\ninline mat3ds mat3ds::operator * (double g) const\n{\n\treturn mat3ds(m[XX]*g, m[YY]*g, m[ZZ]*g, m[XY]*g, m[YZ]*g, m[XZ]*g);\n}\n\n// operator /\ninline mat3ds mat3ds::operator / (double g) const\n{\n\tg = 1.0/g;\n\treturn mat3ds(m[XX]*g, m[YY]*g, m[ZZ]*g, m[XY]*g, m[YZ]*g, m[XZ]*g);\n}\n\n// operator + for mat3d objects\ninline mat3d mat3ds::operator + (const mat3d& d) const\n{\n\treturn mat3d(m[XX]+d.d[0][0], m[XY]+d.d[0][1], m[XZ]+d.d[0][2],\n\t\t\t\t m[XY]+d.d[1][0], m[YY]+d.d[1][1], m[YZ]+d.d[1][2],\n\t\t\t\t m[XZ]+d.d[2][0], m[YZ]+d.d[2][1], m[ZZ]+d.d[2][2]);\n}\n\n// operator - for mat3d objects\ninline mat3d mat3ds::operator - (const mat3d& d) const\n{\n\treturn mat3d(m[XX]-d.d[0][0], m[XY]-d.d[0][1], m[XZ]-d.d[0][2],\n\t\t\t\t m[XY]-d.d[1][0], m[YY]-d.d[1][1], m[YZ]-d.d[1][2],\n\t\t\t\t m[XZ]-d.d[2][0], m[YZ]-d.d[2][1], m[ZZ]-d.d[2][2]);\n}\n\n// operator * for mat3d objects\ninline mat3d mat3ds::operator * (const mat3d& d) const\n{\n\treturn mat3d(d.d[0][0]*m[XX] + d.d[1][0]*m[XY] + d.d[2][0]*m[XZ], \n\t\t\t\t d.d[0][1]*m[XX] + d.d[1][1]*m[XY] + d.d[2][1]*m[XZ], \n\t\t\t\t d.d[0][2]*m[XX] + d.d[1][2]*m[XY] + d.d[2][2]*m[XZ],\n\t\t\t\t d.d[0][0]*m[XY] + d.d[1][0]*m[YY] + d.d[2][0]*m[YZ], \n\t\t\t\t d.d[0][1]*m[XY] + d.d[1][1]*m[YY] + d.d[2][1]*m[YZ], \n\t\t\t\t d.d[0][2]*m[XY] + d.d[1][2]*m[YY] + d.d[2][2]*m[YZ],\n\t\t\t\t d.d[0][0]*m[XZ] + d.d[1][0]*m[YZ] + d.d[2][0]*m[ZZ], \n\t\t\t\t d.d[0][1]*m[XZ] + d.d[1][1]*m[YZ] + d.d[2][1]*m[ZZ], \n\t\t\t\t d.d[0][2]*m[XZ] + d.d[1][2]*m[YZ] + d.d[2][2]*m[ZZ]);\n}\n\n// operator + for mat3da objects\ninline mat3d mat3ds::operator + (const mat3da& d) const\n{\n    return mat3d(m[XX]       , m[XY]+d.xy(), m[XZ]+d.xz(),\n                 m[XY]-d.xy(), m[YY]       , m[YZ]+d.yz(),\n                 m[XZ]-d.xz(), m[YZ]-d.yz(), m[ZZ]       );\n}\n\n// operator - for mat3da objects\ninline mat3d mat3ds::operator - (const mat3da& d) const\n{\n    return mat3d(m[XX]       , m[XY]-d.xy(), m[XZ]-d.xz(),\n                 m[XY]+d.xy(), m[YY]       , m[YZ]-d.yz(),\n                 m[XZ]+d.xz(), m[YZ]+d.yz(), m[ZZ]       );\n}\n\n// operator * for mat3d objects\ninline mat3d mat3ds::operator * (const mat3da& d) const\n{\n    return mat3d(\n                 -d.xy()*m[XY]-d.xz()*m[XZ],d.xy()*m[XX]-d.yz()*m[XZ],d.xz()*m[XX]+d.yz()*m[XY],\n                 -d.xy()*m[YY]-d.xz()*m[YZ],d.xy()*m[XY]-d.yz()*m[YZ],d.xz()*m[XY]+d.yz()*m[YY],\n                 -d.xy()*m[YZ]-d.xz()*m[ZZ],d.xy()*m[XZ]-d.yz()*m[ZZ],d.xz()*m[XZ]+d.yz()*m[YZ]\n                 );\n}\n\n\n// unary operator -\ninline mat3ds mat3ds::operator - () const\n{\n\treturn mat3ds(-m[XX], -m[YY], -m[ZZ], -m[XY], -m[YZ], -m[XZ]);\n}\n\n// assignment operator +=\ninline mat3ds& mat3ds::operator += (const mat3ds& t)\n{\n\tm[XX] += t.m[XX]; m[YY] += t.m[YY]; m[ZZ] += t.m[ZZ];\n\tm[XY] += t.m[XY]; m[YZ] += t.m[YZ]; m[XZ] += t.m[XZ];\n\treturn (*this);\n}\n\n// assignment operator -=\ninline mat3ds& mat3ds::operator -= (const mat3ds& t)\n{\n\tm[XX] -= t.m[XX]; m[YY] -= t.m[YY]; m[ZZ] -= t.m[ZZ];\n\tm[XY] -= t.m[XY]; m[YZ] -= t.m[YZ]; m[XZ] -= t.m[XZ];\n\treturn (*this);\n}\n\n// assignment operator *=\ninline mat3ds& mat3ds::operator *= (const mat3ds& t)\n{\n\tdouble xx = m[XX]*t.m[XX]+m[XY]*t.m[XY]+m[XZ]*t.m[XZ];\n\tdouble yy = m[XY]*t.m[XY]+m[YY]*t.m[YY]+m[YZ]*t.m[YZ];\n\tdouble zz = m[XZ]*t.m[XZ]+m[YZ]*t.m[YZ]+m[ZZ]*t.m[ZZ];\n\tdouble xy = m[XX]*t.m[XY]+m[XY]*t.m[YY]+m[XZ]*t.m[YZ];\n\tdouble yz = m[XY]*t.m[XZ]+m[YY]*t.m[YZ]+m[YZ]*t.m[ZZ];\n\tdouble xz = m[XX]*t.m[XZ]+m[XY]*t.m[YZ]+m[XZ]*t.m[ZZ];\n\n\tm[XX] = xx; m[YY] = yy; m[ZZ] = zz;\n\tm[XY] = xy; m[YZ] = yz; m[XZ] = xz;\n\n\treturn (*this);\n}\n\n// assignment operator *=\ninline mat3ds& mat3ds::operator *= (double g)\n{\n\tm[XX] *= g; m[YY] *= g; m[ZZ] *= g;\n\tm[XY] *= g; m[YZ] *= g; m[XZ] *= g;\n\treturn (*this);\n}\n\n// assignment operator /=\ninline mat3ds& mat3ds::operator /= (double g)\n{\n\tg = 1./g;\n\tm[XX] *= g; m[YY] *= g; m[ZZ] *= g;\n\tm[XY] *= g; m[YZ] *= g; m[XZ] *= g;\n\treturn (*this);\n}\n\n// arithmetic assignment operators for mat3dd\ninline mat3ds& mat3ds::operator += (const mat3dd& d)\n{\n\tm[XX] += d.d[0];\n\tm[YY] += d.d[1];\n\tm[ZZ] += d.d[2];\n\treturn (*this);\n}\n\ninline mat3ds& mat3ds::operator -= (const mat3dd& d)\n{\n\tm[XX] -= d.d[0];\n\tm[YY] -= d.d[1];\n\tm[ZZ] -= d.d[2];\n\treturn (*this);\n}\n\n// matrix-vector multiplication\ninline vec3d mat3ds::operator* (const vec3d& r) const\n{\n\treturn vec3d(\n\t\tr.x*m[XX]+r.y*m[XY]+r.z*m[XZ],\n\t\tr.x*m[XY]+r.y*m[YY]+r.z*m[YZ],\n\t\tr.x*m[XZ]+r.y*m[YZ]+r.z*m[ZZ]\n\t);\n}\n\n// comparison\ninline bool mat3ds::operator == (const mat3ds& d)\n{\n\treturn (\n\t\t(m[XX] == d.m[XX]) &&\n\t\t(m[XY] == d.m[XY]) &&\n\t\t(m[YY] == d.m[YY]) &&\n\t\t(m[XZ] == d.m[XZ]) &&\n\t\t(m[YZ] == d.m[YZ]) &&\n\t\t(m[ZZ] == d.m[ZZ]));\n}\n\n// trace\ninline double mat3ds::tr() const\n{\n\treturn m[XX]+m[YY]+m[ZZ];\n}\n\n// determinant\ninline double mat3ds::det() const\n{\n\treturn (m[XX]*(m[YY]*m[ZZ] - m[YZ]*m[YZ])\n\t\t  + m[XY]*(m[YZ]*m[XZ] - m[ZZ]*m[XY])\n\t\t  + m[XZ]*(m[XY]*m[YZ] - m[YY]*m[XZ]));\n}\n\n// zero\ninline void mat3ds::zero()\n{\n\tm[0] = m[1] = m[2] = m[3] = m[4] = m[5] = 0;\n}\n\n// unit tensor\ninline void mat3ds::unit()\n{\n\tm[XX] = m[YY] = m[ZZ] = 1.0;\n\tm[XY] = m[YZ] = m[XZ] = 0.0;\n}\n\n// deviator\ninline mat3ds mat3ds::dev() const\n{\n\tdouble t = (m[XX]+m[YY]+m[ZZ])/3.0;\n\treturn mat3ds(m[XX]-t, m[YY]-t, m[ZZ]-t, m[XY], m[YZ], m[XZ]);\n}\n\n// isotropic part\ninline mat3ds mat3ds::iso() const\n{\n\tdouble t = (m[XX]+m[YY]+m[ZZ])/3.0;\n\treturn mat3ds(t, t, t, 0, 0, 0);\n}\n\n// return the square \ninline mat3ds mat3ds::sqr() const\n{\n\treturn mat3ds(\n\t\tm[XX] * m[XX] + m[XY] * m[XY] + m[XZ] * m[XZ],\n\t\tm[XY] * m[XY] + m[YY] * m[YY] + m[YZ] * m[YZ],\n\t\tm[XZ] * m[XZ] + m[YZ] * m[YZ] + m[ZZ] * m[ZZ],\n\t\tm[XX] * m[XY] + m[XY] * m[YY] + m[XZ] * m[YZ], \n\t\tm[XY] * m[XZ] + m[YY] * m[YZ] + m[YZ] * m[ZZ],\n\t\tm[XX] * m[XZ] + m[XY] * m[YZ] + m[XZ] * m[ZZ]\n\t);\n}\n\n// inverse\ninline mat3ds mat3ds::inverse() const\n{\n\tdouble D = det();\n\tassert(D != 0);\n\tD = 1/D;\n\t\n\treturn mat3ds(D*(m[YY]*m[ZZ]-m[YZ]*m[YZ]), \n\t\t\t\t  D*(m[XX]*m[ZZ]-m[XZ]*m[XZ]), \n\t\t\t\t  D*(m[XX]*m[YY]-m[XY]*m[XY]),\n\t\t\t\t  D*(m[XZ]*m[YZ]-m[XY]*m[ZZ]), \n\t\t\t\t  D*(m[XY]*m[XZ]-m[XX]*m[YZ]), \n\t\t\t\t  D*(m[XY]*m[YZ]-m[YY]*m[XZ]));\n}\n\n// invert\ninline double mat3ds::invert(mat3ds& Ai)\n{\n    double D = det();\n    if (D != 0) {\n        double Di = 1/D;\n        \n        Ai = mat3ds(Di*(m[YY]*m[ZZ]-m[YZ]*m[YZ]),\n                    Di*(m[XX]*m[ZZ]-m[XZ]*m[XZ]),\n                    Di*(m[XX]*m[YY]-m[XY]*m[XY]),\n                    Di*(m[XZ]*m[YZ]-m[XY]*m[ZZ]),\n                    Di*(m[XY]*m[XZ]-m[XX]*m[YZ]),\n                    Di*(m[XY]*m[YZ]-m[YY]*m[XZ]));\n    }\n    return D;\n}\n\n// L2-norm\ninline double mat3ds::norm() const\n{ \n\tdouble D = m[XX]*m[XX] + m[YY]*m[YY] + m[ZZ]*m[ZZ] + 2*(m[XY]*m[XY] + m[YZ]*m[YZ] + m[XZ]*m[XZ]);\n\treturn sqrt(D); \n}\n\n// double contraction\ninline double mat3ds::dotdot(const mat3ds& B) const\n{\n\tconst double* n = B.m;\n\treturn m[XX]*n[XX] + m[YY]*n[YY] + m[ZZ]*n[ZZ] + 2.0*(m[XY]*n[XY] + m[YZ]*n[YZ] + m[XZ]*n[XZ]);\n}\n\n// Effective or von-mises value\ninline double mat3ds::effective_norm() const\n{\n\tdouble vm;\n\tvm = m[XX] * m[XX] + m[YY] * m[YY] + m[ZZ] * m[ZZ];\n\tvm -= m[XX] * m[YY] + m[YY] * m[ZZ] + m[XX] * m[ZZ];\n\tvm += 3 * (m[XY] * m[XY] + m[YZ] * m[YZ] + m[XZ] * m[XZ]);\n\tvm = sqrt(vm >= 0.0 ? vm : 0.0);\n\treturn vm;\n}\n\n//-----------------------------------------------------------------------------\n// class mat3da : anti-symmetric 3D matrix of doubles\n//-----------------------------------------------------------------------------\n\n// constructors\ninline mat3da::mat3da(double xy, double yz, double xz)\n{\n\td[0] = xy; d[1] = yz; d[2] = xz;\n}\n\n// calculates the antisymmetric matrix from a vector such that for any b,\n// A.b = a x b where A = mat3da(a).\ninline mat3da::mat3da(const vec3d& a)\n{\n\td[0] = -a.z; d[1] = -a.x; d[2] = a.y;\n}\n\n// access operator\ninline double mat3da::operator ()(int i, int j) const\n{\n\treturn (i==j? 0 : (i<j? d[((j-1)<<1)-i] : -d[((i-1)<<1)-j]));\n}\n\n// scalar multiplication\ninline mat3da mat3da::operator * (double g) const\n{\n\treturn mat3da(d[0]*g, d[1]*g, d[2]*g);\n}\n\ninline mat3da mat3da::operator + (const mat3da& a)\n{\n\treturn mat3da(d[0]+a.d[0], d[1]+a.d[1], d[2]+a.d[2]);\n}\n\ninline mat3da mat3da::operator - (const mat3da& a)\n{\n\treturn mat3da(d[0]-a.d[0], d[1]-a.d[1], d[2]-a.d[2]);\n}\n\ninline mat3da mat3da::operator - () const\n{\n\treturn mat3da(-d[0], -d[1], -d[2]);\n}\n\ninline mat3da mat3da::transpose() const\n{\n\treturn mat3da(-d[0], -d[1], -d[2]);\n}\n\n// matrix multiplication\ninline mat3d mat3da::operator * (const mat3d& m)\n{\n\treturn mat3d(\n\t\t d[0]*m.d[1][0] + d[2]*m.d[2][0],  d[0]*m.d[1][1] + d[2]*m.d[2][1],  d[0]*m.d[1][2] + d[2]*m.d[2][2],\n\t\t-d[0]*m.d[0][0] + d[1]*m.d[2][0], -d[0]*m.d[0][1] + d[1]*m.d[2][1], -d[0]*m.d[0][2] + d[1]*m.d[2][2],\n\t\t-d[2]*m.d[0][0] - d[1]*m.d[1][0], -d[2]*m.d[0][1] - d[1]*m.d[1][1], -d[2]*m.d[0][2] - d[1]*m.d[1][2]\n\t);\n}\n\ninline mat3d mat3da::operator + (const mat3ds& a) const\n{\n    return mat3d(\n                 a.xx(),a.xy()+xy(),a.xz()+xz(),\n                 a.xy()-xy(),a.yy(),a.yz()+yz(),\n                 a.xz()-xz(),a.yz()-yz(),a.zz()\n                 );\n}\n\ninline mat3d mat3da::operator - (const mat3ds& a) const\n{\n    return mat3d(\n                  -a.xx(),-a.xy()+xy(),-a.xz()+xz(),\n                  -a.xy()-xy(),-a.yy(),-a.yz()+yz(),\n                  -a.xz()-xz(),-a.yz()-yz(),-a.zz()\n                  );\n}\n\ninline vec3d mat3da::operator * (const vec3d& a)\n{\n\treturn vec3d(\n\t\t d[0] * a.y + d[2] * a.z, \\\n\t\t-d[0] * a.x + d[1] * a.z, \\\n\t\t-d[2] * a.x - d[1] * a.y);\n}\n\n//-----------------------------------------------------------------------------\n// class mat3d : general 3D matrix of doubles\n//-----------------------------------------------------------------------------\n\n// constructors\n\ninline mat3d::mat3d(double a)\n{\n\td[0][0] = a; d[0][1] = 0; d[0][2] = 0;\n\td[1][0] = 0; d[1][1] = a; d[1][2] = 0;\n\td[2][0] = 0; d[2][1] = 0; d[2][2] = a;\n}\n\ninline mat3d::mat3d(double a00, double a01, double a02,\n\t\t\t\t\tdouble a10, double a11, double a12,\n\t\t\t\t\tdouble a20, double a21, double a22)\n{\n\td[0][0] = a00; d[0][1] = a01; d[0][2] = a02;\n\td[1][0] = a10; d[1][1] = a11; d[1][2] = a12;\n\td[2][0] = a20; d[2][1] = a21; d[2][2] = a22;\n}\n\ninline mat3d::mat3d(double m[3][3])\n{\n\td[0][0] = m[0][0]; d[0][1] = m[0][1]; d[0][2] = m[0][2];\n\td[1][0] = m[1][0]; d[1][1] = m[1][1]; d[1][2] = m[1][2];\n\td[2][0] = m[2][0]; d[2][1] = m[2][1]; d[2][2] = m[2][2];\n}\n\ninline mat3d::mat3d(double a[9])\n{\n\td[0][0] = a[0]; d[0][1] = a[1]; d[0][2] = a[2];\n\td[1][0] = a[3]; d[1][1] = a[4]; d[1][2] = a[5];\n\td[2][0] = a[6]; d[2][1] = a[7]; d[2][2] = a[8];\n}\n\ninline mat3d::mat3d(const mat3dd& m)\n{\n\td[0][0] = m.d[0]; d[1][1] = m.d[1]; d[2][2] = m.d[2];\n\td[0][1] = d[1][0] = 0;\n\td[1][2] = d[2][1] = 0;\n\td[0][2] = d[2][0] = 0;\n}\n\ninline mat3d::mat3d(const mat3ds& m)\n{\n\td[0][0] = m.m[mat3ds::XX];\n\td[1][1] = m.m[mat3ds::YY];\n\td[2][2] = m.m[mat3ds::ZZ];\n\td[0][1] = d[1][0] = m.m[mat3ds::XY];\n\td[1][2] = d[2][1] = m.m[mat3ds::YZ];\n\td[0][2] = d[2][0] = m.m[mat3ds::XZ];\n}\n\ninline mat3d::mat3d(const mat3da& m)\n{\n\td[0][0] = d[1][1] = d[2][2] = 0;\n\td[0][1] = m.d[0]; d[1][0] = -m.d[0];\n\td[1][2] = m.d[1]; d[2][1] = -m.d[1];\n\td[0][2] = m.d[2]; d[2][0] = -m.d[2];\n}\n\ninline mat3d::mat3d(const mat2d& m)\n{\n\td[0][0] = m(0,0); d[0][1] = m(0,1); d[0][2] = 0.0;\n\td[1][0] = m(1,0); d[1][1] = m(1,1); d[1][2] = 0.0;\n\td[2][0] = d[2][1] = 0.0;\n\td[2][2] = 0.0;\t// Should I set this to 1.0 instead? that way det(), inverse() etc. remain valid for the mat3d\n}\n\ninline mat3d::mat3d(const vec3d& e1, const vec3d& e2, const vec3d& e3)\n{\n\td[0][0] = e1.x; d[0][1] = e2.x; d[0][2] = e3.x;\n\td[1][0] = e1.y; d[1][1] = e2.y; d[1][2] = e3.y;\n\td[2][0] = e1.z; d[2][1] = e2.z; d[2][2] = e3.z;\n}\n\ninline mat3d::mat3d(const vec3d& a, const vec3d& b)\n{\n\tvec3d e1(a);\n\tvec3d e3 = a ^ b;\n\tvec3d e2 = e3 ^ e1;\n\n\t// normalize\n\te1.unit();\n\te2.unit();\n\te3.unit();\n\n\t// set the value\n\td[0][0] = e1.x; d[0][1] = e2.x; d[0][2] = e3.x;\n\td[1][0] = e1.y; d[1][1] = e2.y; d[1][2] = e3.y;\n\td[2][0] = e1.z; d[2][1] = e2.z; d[2][2] = e3.z;\n}\n\n// assignment operators\ninline mat3d& mat3d::operator = (const mat3dd& m)\n{\n\td[0][0] = m.d[0];\n\td[1][1] = m.d[1];\n\td[2][2] = m.d[2];\n\td[0][1] = d[1][0] = 0;\n\td[1][2] = d[2][1] = 0;\n\td[0][2] = d[2][0] = 0;\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator = (const mat3ds& m)\n{\n\td[0][0] = m.m[mat3ds::XX];\n\td[1][1] = m.m[mat3ds::YY];\n\td[2][2] = m.m[mat3ds::ZZ];\n\td[0][1] = d[1][0] = m.m[mat3ds::XY];\n\td[1][2] = d[2][1] = m.m[mat3ds::YZ];\n\td[0][2] = d[2][0] = m.m[mat3ds::XZ];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator = (const double m[3][3])\n{\n\td[0][0] = m[0][0]; d[0][1] = m[0][1]; d[0][2] = m[0][2];\n\td[1][0] = m[1][0]; d[1][1] = m[1][1]; d[1][2] = m[1][2];\n\td[2][0] = m[2][0]; d[2][1] = m[2][1]; d[2][2] = m[2][2];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator = (const mat3d& m)\n{\n\td[0][0] = m.d[0][0]; d[0][1] = m.d[0][1]; d[0][2] = m.d[0][2];\n\td[1][0] = m.d[1][0]; d[1][1] = m.d[1][1]; d[1][2] = m.d[1][2];\n\td[2][0] = m.d[2][0]; d[2][1] = m.d[2][1]; d[2][2] = m.d[2][2];\n\treturn (*this);\n}\n\n// access operator\ninline double& mat3d::operator () (int i, int j) { return d[i][j]; }\ninline const double& mat3d::operator () (int i, int j) const { return d[i][j]; }\ninline double* mat3d::operator [] (int i) { return d[i]; }\ninline const double* mat3d::operator [] (int i) const { return d[i]; }\n\n// arithmetic operators\ninline mat3d mat3d::operator + (const mat3d& m) const\n{\n\treturn mat3d( d[0][0]+m.d[0][0], d[0][1]+m.d[0][1], d[0][2]+m.d[0][2],\n\t\t\t\t  d[1][0]+m.d[1][0], d[1][1]+m.d[1][1], d[1][2]+m.d[1][2],\n\t\t\t\t  d[2][0]+m.d[2][0], d[2][1]+m.d[2][1], d[2][2]+m.d[2][2]);\n}\n\ninline mat3d mat3d::operator - (const mat3d& m) const\n{\n\treturn mat3d( d[0][0]-m.d[0][0], d[0][1]-m.d[0][1], d[0][2]-m.d[0][2],\n\t\t\t\t  d[1][0]-m.d[1][0], d[1][1]-m.d[1][1], d[1][2]-m.d[1][2],\n\t\t\t\t  d[2][0]-m.d[2][0], d[2][1]-m.d[2][1], d[2][2]-m.d[2][2]);\n}\n\ninline mat3d mat3d::operator * (const mat3d& m) const\n{\n\treturn mat3d(d[0][0]*m.d[0][0]+d[0][1]*m.d[1][0]+d[0][2]*m.d[2][0],\n\t\t\t\t d[0][0]*m.d[0][1]+d[0][1]*m.d[1][1]+d[0][2]*m.d[2][1],\n\t\t\t\t d[0][0]*m.d[0][2]+d[0][1]*m.d[1][2]+d[0][2]*m.d[2][2],\n\t\t\t\t d[1][0]*m.d[0][0]+d[1][1]*m.d[1][0]+d[1][2]*m.d[2][0],\n\t\t\t\t d[1][0]*m.d[0][1]+d[1][1]*m.d[1][1]+d[1][2]*m.d[2][1],\n\t\t\t\t d[1][0]*m.d[0][2]+d[1][1]*m.d[1][2]+d[1][2]*m.d[2][2],\n\t\t\t\t d[2][0]*m.d[0][0]+d[2][1]*m.d[1][0]+d[2][2]*m.d[2][0],\n\t\t\t\t d[2][0]*m.d[0][1]+d[2][1]*m.d[1][1]+d[2][2]*m.d[2][1],\n\t\t\t\t d[2][0]*m.d[0][2]+d[2][1]*m.d[1][2]+d[2][2]*m.d[2][2]);\n}\n\ninline mat3d mat3d::operator * (double a) const\n{\n\treturn mat3d(d[0][0]*a, d[0][1]*a, d[0][2]*a,\n\t\t\t\t d[1][0]*a, d[1][1]*a, d[1][2]*a,\n\t\t\t\t d[2][0]*a, d[2][1]*a, d[2][2]*a);\n}\n\ninline mat3d mat3d::operator / (double a) const\n{\n\ta = 1./a;\n\treturn mat3d(d[0][0]*a, d[0][1]*a, d[0][2]*a,\n\t\t\t\t d[1][0]*a, d[1][1]*a, d[1][2]*a,\n\t\t\t\t d[2][0]*a, d[2][1]*a, d[2][2]*a);\n}\n\n// arithmetic operators for mat3dd\ninline mat3d mat3d::operator + (const mat3dd& m) const\n{\n\treturn mat3d( d[0][0]+m.d[0], d[0][1], d[0][2],\n\t\t\t\t  d[1][0], d[1][1]+m.d[1], d[1][2],\n\t\t\t\t  d[2][0], d[2][1], d[2][2]+m.d[2]);\n}\n\ninline mat3d mat3d::operator - (const mat3dd& m) const\n{\n\treturn mat3d( d[0][0]-m.d[0], d[0][1], d[0][2],\n\t\t\t\t  d[1][0], d[1][1]-m.d[1], d[1][2],\n\t\t\t\t  d[2][0], d[2][1], d[2][2]-m.d[2]);\n}\n\ninline mat3d mat3d::operator * (const mat3dd& m) const\n{\n\treturn mat3d( d[0][0]*m.d[0], d[0][1]*m.d[1], d[0][2]*m.d[2],\n\t\t\t\t  d[1][0]*m.d[0], d[1][1]*m.d[1], d[1][2]*m.d[2],\n\t\t\t\t  d[2][0]*m.d[0], d[2][1]*m.d[1], d[2][2]*m.d[2]);\n}\n\n// arithmetic operators for mat3ds\ninline mat3d mat3d::operator + (const mat3ds& m) const\n{\n\treturn mat3d(d[0][0]+m.m[m.XX], d[0][1]+m.m[m.XY], d[0][2]+m.m[m.XZ],\n\t\t\t\t d[1][0]+m.m[m.XY], d[1][1]+m.m[m.YY], d[1][2]+m.m[m.YZ],\n\t\t\t\t d[2][0]+m.m[m.XZ], d[2][1]+m.m[m.YZ], d[2][2]+m.m[m.ZZ]);\n}\n\ninline mat3d mat3d::operator - (const mat3ds& m) const\n{\n\treturn mat3d(d[0][0]-m.m[m.XX], d[0][1]-m.m[m.XY], d[0][2]-m.m[m.XZ],\n\t\t\t\t d[1][0]-m.m[m.XY], d[1][1]-m.m[m.YY], d[1][2]-m.m[m.YZ],\n\t\t\t\t d[2][0]-m.m[m.XZ], d[2][1]-m.m[m.YZ], d[2][2]-m.m[m.ZZ]);\n}\n\ninline mat3d mat3d::operator * (const mat3ds& m) const\n{\n\treturn mat3d(\n\t\td[0][0]*m.m[m.XX] + d[0][1]*m.m[m.XY] + d[0][2]*m.m[m.XZ],\n\t\td[0][0]*m.m[m.XY] + d[0][1]*m.m[m.YY] + d[0][2]*m.m[m.YZ],\n\t\td[0][0]*m.m[m.XZ] + d[0][1]*m.m[m.YZ] + d[0][2]*m.m[m.ZZ],\n\t\td[1][0]*m.m[m.XX] + d[1][1]*m.m[m.XY] + d[1][2]*m.m[m.XZ],\n\t\td[1][0]*m.m[m.XY] + d[1][1]*m.m[m.YY] + d[1][2]*m.m[m.YZ],\n\t\td[1][0]*m.m[m.XZ] + d[1][1]*m.m[m.YZ] + d[1][2]*m.m[m.ZZ],\n\t\td[2][0]*m.m[m.XX] + d[2][1]*m.m[m.XY] + d[2][2]*m.m[m.XZ],\n\t\td[2][0]*m.m[m.XY] + d[2][1]*m.m[m.YY] + d[2][2]*m.m[m.YZ],\n\t\td[2][0]*m.m[m.XZ] + d[2][1]*m.m[m.YZ] + d[2][2]*m.m[m.ZZ]);\n}\n\n// arithmetic assignment operators\ninline mat3d& mat3d::operator += (const mat3d& m)\n{\n\td[0][0] += m.d[0][0]; d[0][1] += m.d[0][1]; d[0][2] += m.d[0][2];\n\td[1][0] += m.d[1][0]; d[1][1] += m.d[1][1]; d[1][2] += m.d[1][2];\n\td[2][0] += m.d[2][0]; d[2][1] += m.d[2][1]; d[2][2] += m.d[2][2];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator -= (const mat3d& m)\n{\n\td[0][0] -= m.d[0][0]; d[0][1] -= m.d[0][1]; d[0][2] -= m.d[0][2];\n\td[1][0] -= m.d[1][0]; d[1][1] -= m.d[1][1]; d[1][2] -= m.d[1][2];\n\td[2][0] -= m.d[2][0]; d[2][1] -= m.d[2][1]; d[2][2] -= m.d[2][2];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator *= (const mat3d& m)\n{\n\tdouble d00 = d[0][0]*m.d[0][0]+d[0][1]*m.d[1][0]+d[0][2]*m.d[2][0];\n\tdouble d01 = d[0][0]*m.d[0][1]+d[0][1]*m.d[1][1]+d[0][2]*m.d[2][1];\n\tdouble d02 = d[0][0]*m.d[0][2]+d[0][1]*m.d[1][2]+d[0][2]*m.d[2][2];\n\tdouble d10 = d[1][0]*m.d[0][0]+d[1][1]*m.d[1][0]+d[1][2]*m.d[2][0];\n\tdouble d11 = d[1][0]*m.d[0][1]+d[1][1]*m.d[1][1]+d[1][2]*m.d[2][1];\n\tdouble d12 = d[1][0]*m.d[0][2]+d[1][1]*m.d[1][2]+d[1][2]*m.d[2][2];\n\tdouble d20 = d[2][0]*m.d[0][0]+d[2][1]*m.d[1][0]+d[2][2]*m.d[2][0];\n\tdouble d21 = d[2][0]*m.d[0][1]+d[2][1]*m.d[1][1]+d[2][2]*m.d[2][1];\n\tdouble d22 = d[2][0]*m.d[0][2]+d[2][1]*m.d[1][2]+d[2][2]*m.d[2][2];\n\n\td[0][0] = d00; d[0][1] = d01; d[0][2] = d02;\n\td[1][0] = d10; d[1][1] = d11; d[1][2] = d12;\n\td[2][0] = d20; d[2][1] = d21; d[2][2] = d22;\n\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator *= (double a)\n{\n\td[0][0]*=a; d[0][1]*=a; d[0][2]*=a;\n\td[1][0]*=a; d[1][1]*=a; d[1][2]*=a;\n\td[2][0]*=a; d[2][1]*=a; d[2][2]*=a;\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator /= (double a)\n{\n\ta = 1./a;\n\td[0][0]*=a; d[0][1]*=a; d[0][2]*=a;\n\td[1][0]*=a; d[1][1]*=a; d[1][2]*=a;\n\td[2][0]*=a; d[2][1]*=a; d[2][2]*=a;\n\treturn (*this);\n}\n\n// arithmetic assignment operators for mat3dd\ninline mat3d& mat3d::operator += (const mat3dd& m)\n{\n\td[0][0] += m.d[0];\n\td[1][1] += m.d[1];\n\td[2][2] += m.d[2];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator -= (const mat3dd& m)\n{\n\td[0][0] -= m.d[0];\n\td[1][1] -= m.d[1];\n\td[2][2] -= m.d[2];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator *= (const mat3dd& m)\n{\n\td[0][0] *= m.d[0]; d[0][1] *= m.d[1]; d[0][2] *= m.d[2];\n\td[1][0] *= m.d[0]; d[1][1] *= m.d[1]; d[1][2] *= m.d[2];\n\td[2][0] *= m.d[0]; d[2][1] *= m.d[1]; d[2][2] *= m.d[2];\n\treturn (*this);\n}\n\n// arithmetic operators for mat3ds\ninline mat3d& mat3d::operator += (const mat3ds& m)\n{\n\td[0][0] += m.m[m.XX]; d[0][1] += m.m[m.XY]; d[0][2] += m.m[m.XZ];\n\td[1][0] += m.m[m.XY]; d[1][1] += m.m[m.YY]; d[1][2] += m.m[m.YZ];\n\td[2][0] += m.m[m.XZ]; d[2][1] += m.m[m.YZ]; d[2][2] += m.m[m.ZZ];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator -= (const mat3ds& m)\n{\n\td[0][0] -= m.m[m.XX]; d[0][1] -= m.m[m.XY]; d[0][2] -= m.m[m.XZ];\n\td[1][0] -= m.m[m.XY]; d[1][1] -= m.m[m.YY]; d[1][2] -= m.m[m.YZ];\n\td[2][0] -= m.m[m.XZ]; d[2][1] -= m.m[m.YZ]; d[2][2] -= m.m[m.ZZ];\n\treturn (*this);\n}\n\ninline mat3d& mat3d::operator *= (const mat3ds& m)\n{\n\tdouble d00 = d[0][0]*m.m[m.XX]+d[0][1]*m.m[m.XY]+d[0][2]*m.m[m.XZ];\n\tdouble d01 = d[0][0]*m.m[m.XY]+d[0][1]*m.m[m.YY]+d[0][2]*m.m[m.YZ];\n\tdouble d02 = d[0][0]*m.m[m.XZ]+d[0][1]*m.m[m.YZ]+d[0][2]*m.m[m.ZZ];\n\tdouble d10 = d[1][0]*m.m[m.XX]+d[1][1]*m.m[m.XY]+d[1][2]*m.m[m.XZ];\n\tdouble d11 = d[1][0]*m.m[m.XY]+d[1][1]*m.m[m.YY]+d[1][2]*m.m[m.YZ];\n\tdouble d12 = d[1][0]*m.m[m.XZ]+d[1][1]*m.m[m.YZ]+d[1][2]*m.m[m.ZZ];\n\tdouble d20 = d[2][0]*m.m[m.XX]+d[2][1]*m.m[m.XY]+d[2][2]*m.m[m.XZ];\n\tdouble d21 = d[2][0]*m.m[m.XY]+d[2][1]*m.m[m.YY]+d[2][2]*m.m[m.YZ];\n\tdouble d22 = d[2][0]*m.m[m.XZ]+d[2][1]*m.m[m.YZ]+d[2][2]*m.m[m.ZZ];\n\n\td[0][0] = d00; d[0][1] = d01; d[0][2] = d02;\n\td[1][0] = d10; d[1][1] = d11; d[1][2] = d12;\n\td[2][0] = d20; d[2][1] = d21; d[2][2] = d22;\n\n\treturn (*this);\n}\n\n\n// matrix-vector multiplication\ninline vec3d mat3d::operator * (const vec3d& r) const\n{\n\treturn vec3d(d[0][0]*r.x+d[0][1]*r.y+d[0][2]*r.z,\n\t\t\t\t d[1][0]*r.x+d[1][1]*r.y+d[1][2]*r.z,\n\t\t\t\t d[2][0]*r.x+d[2][1]*r.y+d[2][2]*r.z);\n}\n\n// determinant\ninline double mat3d::det() const\n{\n\treturn (d[0][0]*(d[1][1]*d[2][2] - d[1][2]*d[2][1])\n\t\t  + d[0][1]*(d[1][2]*d[2][0] - d[2][2]*d[1][0])\n\t\t  + d[0][2]*(d[1][0]*d[2][1] - d[1][1]*d[2][0]));\n}\n\n// trace\ninline double mat3d::trace() const { return d[0][0]+d[1][1]+d[2][2]; }\n\ninline void mat3d::unit()\n{\n\td[0][0] = d[1][1] = d[2][2] = 1;\n\td[0][1] = d[1][0] = 0;\n\td[0][2] = d[2][0] = 0;\n\td[1][2] = d[2][1] = 0;\n}\n\n// zero the matrix\ninline void mat3d::zero()\n{\n\td[0][0] = d[0][1] = d[0][2] = 0;\n\td[1][0] = d[1][1] = d[1][2] = 0;\n\td[2][0] = d[2][1] = d[2][2] = 0;\n}\n\n// return a column vector from the matrix\ninline vec3d mat3d::col(int j) const\n{\n\treturn vec3d(d[0][j], d[1][j], d[2][j]);\n}\n\n// return a row vector from the matrix\ninline vec3d mat3d::row(int j) const\n{\n\treturn vec3d(d[j][0], d[j][1], d[j][2]);\n}\n\n// set the column of the matrix\ninline void mat3d::setCol(int i, const vec3d& a)\n{\n\td[0][i] = a.x;\n\td[1][i] = a.y;\n\td[2][i] = a.z;\n}\n\n// set the row of the matrix\ninline void mat3d::setRow(int i, const vec3d& a)\n{\n\td[i][0] = a.x;\n\td[i][1] = a.y;\n\td[i][2] = a.z;\n}\n\n// return the symmetric matrix 0.5*(A+A^T)\ninline mat3ds mat3d::sym() const\n{\n\treturn mat3ds(\n\t\td[0][0],\n\t\td[1][1],\n\t\td[2][2],\n\t\t0.5*(d[0][1]+d[1][0]),\n\t\t0.5*(d[1][2]+d[2][1]),\n\t\t0.5*(d[0][2]+d[2][0]));\n}\n\n// return the anti-symmetric matrix 0.5*(A - A^T)\ninline mat3da mat3d::skew() const\n{\n\treturn mat3da(\n\t\t0.5*(d[0][1] - d[1][0]),\n\t\t0.5*(d[1][2] - d[2][1]),\n\t\t0.5*(d[0][2] - d[2][0]));\n}\n\n// return the inverse matrix\ninline mat3d mat3d::inverse() const\n{\n\tdouble D = det();\n\tassert(D != 0);\n\tD = 1/D;\n\n\treturn mat3d(D*(d[1][1]*d[2][2] - d[1][2]*d[2][1]),\n\t\t\t\t D*(d[0][2]*d[2][1] - d[0][1]*d[2][2]),\n\t\t\t\t D*(d[0][1]*d[1][2] - d[1][1]*d[0][2]),\n\t\t\t\t D*(d[1][2]*d[2][0] - d[1][0]*d[2][2]),\n\t\t\t\t D*(d[0][0]*d[2][2] - d[0][2]*d[2][0]),\n\t\t\t\t D*(d[0][2]*d[1][0] - d[0][0]*d[1][2]),\n\t\t\t\t D*(d[1][0]*d[2][1] - d[1][1]*d[2][0]),\n\t\t\t\t D*(d[0][1]*d[2][0] - d[0][0]*d[2][1]),\n\t\t\t\t D*(d[0][0]*d[1][1] - d[0][1]*d[1][0]));\n}\n\n// return the inverse matrix\ninline bool mat3d::invert()\n{\n    double D = det();\n\tif (D == 0) return false;\n\tD = 1.0 / D;\n\n\t// calculate conjugate Matrix\n\tdouble mi[3][3];\n\n\tmi[0][0] =  (d[1][1] * d[2][2] - d[1][2] * d[2][1]);\n\tmi[0][1] = -(d[1][0] * d[2][2] - d[1][2] * d[2][0]);\n\tmi[0][2] =  (d[1][0] * d[2][1] - d[1][1] * d[2][0]);\n\n\tmi[1][0] = -(d[0][1] * d[2][2] - d[0][2] * d[2][1]);\n\tmi[1][1] =  (d[0][0] * d[2][2] - d[0][2] * d[2][0]);\n\tmi[1][2] = -(d[0][0] * d[2][1] - d[0][1] * d[2][0]);\n\n\tmi[2][0] =  (d[0][1] * d[1][2] - d[0][2] * d[1][1]);\n\tmi[2][1] = -(d[0][0] * d[1][2] - d[0][2] * d[1][0]);\n\tmi[2][2] =  (d[0][0] * d[1][1] - d[0][1] * d[1][0]);\n\n\t// divide by det and transpose\n\td[0][0] = mi[0][0] * D; d[1][0] = mi[0][1] * D; d[2][0] = mi[0][2] * D;\n\td[0][1] = mi[1][0] * D; d[1][1] = mi[1][1] * D; d[2][1] = mi[1][2] * D;\n\td[0][2] = mi[2][0] * D; d[1][2] = mi[2][1] * D; d[2][2] = mi[2][2] * D;\n\n    return true;\n}\n\n// return the transpose matrix\ninline mat3d mat3d::transpose() const\n{\n\treturn mat3d(d[0][0], d[1][0], d[2][0],\n\t\t\t\t d[0][1], d[1][1], d[2][1],\n\t\t\t\t d[0][2], d[1][2], d[2][2]);\n}\n\n// return the transposed inverse matrix\ninline mat3d mat3d::transinv() const\n{\n\tdouble D = det();\n\tassert(D != 0);\n\tD = 1/D;\n\n\treturn mat3d(D*(d[1][1]*d[2][2] - d[1][2]*d[2][1]), // xx\n\t\t\t\t D*(d[1][2]*d[2][0] - d[1][0]*d[2][2]), // yx\n\t\t\t\t D*(d[1][0]*d[2][1] - d[1][1]*d[2][0]), // zx\n\t\t\t\t D*(d[0][2]*d[2][1] - d[0][1]*d[2][2]), // xy\n\t\t\t\t D*(d[0][0]*d[2][2] - d[0][2]*d[2][0]), // yy\n\t\t\t\t D*(d[0][1]*d[2][0] - d[0][0]*d[2][1]), // zy\n\t\t\t\t D*(d[0][1]*d[1][2] - d[1][1]*d[0][2]), // xz\n\t\t\t\t D*(d[0][2]*d[1][0] - d[0][0]*d[1][2]), // yz\n\t\t\t\t D*(d[0][0]*d[1][1] - d[0][1]*d[1][0])); // zz\n}\n\n// calculate the skew symmetric matrix from a vector\ninline void mat3d::skew(const vec3d& v)\n{\n\td[0][0] =    0; d[0][1] = -v.z; d[0][2] =  v.y;\n\td[1][0] =  v.z; d[1][1] =    0; d[1][2] = -v.x;\n\td[2][0] = -v.y; d[2][1] =  v.x; d[2][2] =    0;\n}\n\n// calculate the one-norm (max of absolute column-sum)\ninline double mat3d::norm() const\n{\n\tdouble s, sc;\n\tsc = fabs(d[0][0]) + fabs(d[1][0]) + fabs(d[2][0]); s = sc;\n\tsc = fabs(d[0][1]) + fabs(d[1][1]) + fabs(d[2][1]); if (sc > s) s = sc;\n\tsc = fabs(d[0][2]) + fabs(d[1][2]) + fabs(d[2][2]); if (sc > s) s = sc;\n\treturn s;\n}\n\n// double contraction\ninline double mat3d::dotdot(const mat3d& T) const\n{\n\treturn (T.d[0][0]*d[0][0] + T.d[0][1]*d[0][1] + T.d[0][2]*d[0][2] + T.d[1][0]*d[1][0] + T.d[1][1]*d[1][1] + T.d[1][2]*d[1][2] + T.d[2][0]*d[2][0] + T.d[2][1]*d[2][1] + T.d[2][2]*d[2][2]);\n}\n\n\n// return the inverse matrix\ninline bool mat3f::invert()\n{\n\tfloat D = d[0][0] * (d[1][1] * d[2][2] - d[1][2] * d[2][1])\n\t\t+ d[0][1] * (d[1][2] * d[2][0] - d[2][2] * d[1][0])\n\t\t+ d[0][2] * (d[1][0] * d[2][1] - d[1][1] * d[2][0]);\n\n\tif (D == 0.f) return false;\n\tD = 1.f / D;\n\n\t// calculate conjugate Matrix\n\tfloat mi[3][3];\n\n\tmi[0][0] =  (d[1][1] * d[2][2] - d[1][2] * d[2][1]);\n\tmi[0][1] = -(d[1][0] * d[2][2] - d[1][2] * d[2][0]);\n\tmi[0][2] =  (d[1][0] * d[2][1] - d[1][1] * d[2][0]);\n\n\tmi[1][0] = -(d[0][1] * d[2][2] - d[0][2] * d[2][1]);\n\tmi[1][1] =  (d[0][0] * d[2][2] - d[0][2] * d[2][0]);\n\tmi[1][2] = -(d[0][0] * d[2][1] - d[0][1] * d[2][0]);\n\n\tmi[2][0] =  (d[0][1] * d[1][2] - d[0][2] * d[1][1]);\n\tmi[2][1] = -(d[0][0] * d[1][2] - d[0][2] * d[1][0]);\n\tmi[2][2] =  (d[0][0] * d[1][1] - d[0][1] * d[1][0]);\n\n\t// divide by det and transpose\n\td[0][0] = mi[0][0] * D; d[1][0] = mi[0][1] * D; d[2][0] = mi[0][2] * D;\n\td[0][1] = mi[1][0] * D; d[1][1] = mi[1][1] * D; d[2][1] = mi[1][2] * D;\n\td[0][2] = mi[2][0] * D; d[1][2] = mi[2][1] * D; d[2][2] = mi[2][2] * D;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\ninline void mat3d::exp(const vec3d& v)\n{\n\tdouble l = v.norm();\n\tif (l == 0.0) *this = mat3d::identity();\n\telse\n\t{\n\t\tdouble a = 0.5 * (tan(0.5 * l) / (0.5 * l));\n\t\tvec3d w = v * a;\n\t\tmat3da S(w);\n\t\tmat3d S2 = S * S;\n\t\tmat3dd I(1.0);\n\t\tmat3d E = I + (S2 + S) * (2.0 / (1.0 + w.norm2()));\n\n\t\t*this = E;\n\t}\n}\n"
  },
  {
    "path": "FECore/mat6d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n//-----------------------------------------------------------------------------\n// Class describing a symmetric 6x6 matrix.\n// Only the upper triangular matrix is stored in column major order\n//     / 0   1   3   6   10   15  \\\n//     |     2   4   7   11   16  |\n//     |         5   8   12   17  |\n// A = |             9   13   18  |\n//     |                 14   19  |\n//     \\                      20  /\n\nclass mat6ds\n{\npublic:\n\tenum { NNZ = 21 };\n\npublic:\n\tmat6ds() {}\n\n\t//! operators\n\tmat6ds& operator *= (double g);\n\npublic:\n\t// access operators\n\tdouble& operator () (int i, int j);\n\tconst double& operator () (int i, int j) const;\n\npublic:\n\t//! initialize to zero\n\tvoid zero();\n\nprivate:\n\tdouble\td[NNZ];\n};\n\n//-----------------------------------------------------------------------------\n// Class describing a 6x6 matrix\nclass mat6d\n{\npublic:\n\t//! default constructor\n\tmat6d() {}\n\n\t//! operators\n\tmat6d& operator *= (double g);\n\npublic:\n\t// access operator\n\tdouble* operator [] (int i) { return d[i]; }\n\npublic:\n\t//! initialize to zero\n\tvoid zero();\n\nprivate:\n\tdouble\td[6][6];\t//!< matrix data\n};\n\n//-----------------------------------------------------------------------------\ninline double& mat6ds::operator()(int i, int j)\n{\n\tconst int n[] = {0, 1, 3, 6, 10, 15};\n\treturn (i<=j? d[n[j]+i] : d[n[i]+j]);\n}\n\n//-----------------------------------------------------------------------------\ninline const double& mat6ds::operator()(int i, int j) const\n{\n\tconst int n[] = {0, 1, 3, 6, 10, 15};\n\treturn (i<=j? d[n[j]+i] : d[n[i]+j]);\n}\n\n//-----------------------------------------------------------------------------\ninline mat6ds& mat6ds::operator *= (double g)\n{\n\td[ 0] *= g; d[ 1] *= g; d[ 2] *= g; d[ 3] *= g; d[ 4] *= g; d[ 5] *= g;\n\td[ 6] *= g; d[ 7] *= g; d[ 8] *= g; d[ 9] *= g; d[10] *= g;\n\td[11] *= g; d[12] *= g; d[13] *= g; d[14] *= g;\n\td[15] *= g; d[16] *= g; d[17] *= g;\n\td[18] *= g; d[19] *= g;\n\td[20] *= g;\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\ninline void mat6ds::zero()\n{\n\td[ 0] = d[ 1] = d[ 2] = d[ 3] = d[ 4] = d[ 5] = 0.0;\n\td[ 6] = d[ 7] = d[ 8] = d[ 9] = d[10] = 0.0;\n\td[11] = d[12] = d[13] = d[14] = 0.0;\n\td[15] = d[16] = d[17] = 0.0;\n\td[18] = d[19] = 0.0;\n\td[20] = 0.0;\n}\n\n//-----------------------------------------------------------------------------\ninline void mat6d::zero()\n{\n\td[0][0] = d[0][1] = d[0][2] = d[0][3] = d[0][4] = d[0][5] = 0.0;\n\td[1][0] = d[1][1] = d[1][2] = d[1][3] = d[1][4] = d[1][5] = 0.0;\n\td[2][0] = d[2][1] = d[2][2] = d[2][3] = d[2][4] = d[2][5] = 0.0;\n\td[3][0] = d[3][1] = d[3][2] = d[3][3] = d[3][4] = d[3][5] = 0.0;\n\td[4][0] = d[4][1] = d[4][2] = d[4][3] = d[4][4] = d[4][5] = 0.0;\n\td[5][0] = d[5][1] = d[5][2] = d[5][3] = d[5][4] = d[5][5] = 0.0;\n}\n\n//-----------------------------------------------------------------------------\nmat6d& mat6d::operator *= (double g)\n{\n\td[0][0] *= g; d[0][1] *= g; d[0][2] *= g; d[0][3] *= g; d[0][4] *= g; d[0][5] *= g;\n\td[1][0] *= g; d[1][1] *= g; d[1][2] *= g; d[1][3] *= g; d[1][4] *= g; d[1][5] *= g;\n\td[2][0] *= g; d[2][1] *= g; d[2][2] *= g; d[2][3] *= g; d[2][4] *= g; d[2][5] *= g;\n\td[3][0] *= g; d[3][1] *= g; d[3][2] *= g; d[3][3] *= g; d[3][4] *= g; d[3][5] *= g;\n\td[4][0] *= g; d[4][1] *= g; d[4][2] *= g; d[4][3] *= g; d[4][4] *= g; d[4][5] *= g;\n\td[5][0] *= g; d[5][1] *= g; d[5][2] *= g; d[5][3] *= g; d[5][4] *= g; d[5][5] *= g;\n\treturn *this;\n}\n"
  },
  {
    "path": "FECore/mathalg.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"mathalg.h\"\n\nmat3ds Log(const mat3ds& p, const mat3ds& X)\n{\n\tdouble l[3], s[3];\n\tvec3d u[3], v[3];\n\n\t// evaluate eigen-decomposition of p\n\tp.eigen(l, u);\n\tmat3d U(u[0], u[1], u[2]);\n\tmat3dd L(l[0], l[1], l[2]);\n\tmat3dd rootL(sqrt(l[0]), sqrt(l[1]), sqrt(l[2]));\n\n\tmat3d G = U * rootL;\n\tmat3d Gi = G.inverse();\n\n\tmat3ds Y = (Gi * X*Gi.transpose()).sym();\n\n\tY.eigen(s, v);\n\tmat3d V = mat3d(v[0], v[1], v[2]);\n\n\tmat3d GV = G * V;\n\n\tmat3dd logS(log(s[0]), log(s[1]), log(s[2]));\n\n\tmat3d LogX = (GV)*logS*(GV.transpose());\n\n\treturn LogX.sym();\n}\n\nmat3ds Exp(const mat3ds& p, const mat3ds& X)\n{\n\tdouble l[3], s[3];\n\tvec3d u[3], v[3];\n\n\t// evaluate eigen-decomposition of p\n\tp.eigen(l, u);\n\tmat3d U(u[0], u[1], u[2]);\n\tmat3dd L(l[0], l[1], l[2]);\n\tmat3dd rootL(sqrt(l[0]), sqrt(l[1]), sqrt(l[2]));\n\n\tmat3d G = U * rootL;\n\tmat3d Gi = G.inverse();\n\n\tmat3ds Y = (Gi * X*Gi.transpose()).sym();\n\n\tY.eigen(s, v);\n\tmat3d V = mat3d(v[0], v[1], v[2]);\n\n\tmat3d GV = G * V;\n\n\tmat3dd expS(exp(s[0]), exp(s[1]), exp(s[2]));\n\n\tmat3d ExpX = (GV)*expS*(GV.transpose());\n\n\treturn ExpX.sym();\n}\n\nmat3ds weightedAverageStructureTensor(mat3ds* d, double* w, int n)\n{\n\tconst double eps = 1.0e-9;\n\n\tmat3ds mu(1.0, 1.0, 1.0, 0.0, 0.0, 0.0);\n\tdouble tau = 1.0;\n\n\tdouble normXi = 0.0, normXip = 0.0;\n\tmat3ds Xi, Xip;\n\tint nc = 0;\n\tdo\n\t{\n\t\tXip = Xi;\n\t\tnormXip = normXi;\n\n\t\tXi = weightedAverage<mat3ds>(d, w, n, [&](const mat3ds& a) {\n\t\t\treturn Log(mu, a); \n\t\t});\n\n\t\tmu = Exp(mu*tau, Xi);\n\n\t\tnormXi = Xi.norm();\n\n\t\tif ((nc != 0) && (normXi > normXip))\n\t\t{\n\t\t\tXi = Xip;\n\t\t\ttau *= 0.5;\n\t\t}\n\t\tnc++;\n\t} \n\twhile (normXi > eps);\n\n\treturn mu;\n}\n"
  },
  {
    "path": "FECore/mathalg.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"mat3d.h\"\n#include <functional>\n#include \"fecore_api.h\"\n\ntemplate <class T> T weightedAverage(T* d, double* w, int n)\n{\n\tT s = d[0] * w[0];\n\tfor (int i = 1; i < n; ++i) s += d[i] * w[i];\n\treturn s;\n}\n\ntemplate <class T> T weightedAverage(T* d, double* w, int n, std::function<T(const T&)> fnc)\n{\n\tT s = fnc(d[0]) * w[0];\n\tfor (int i = 1; i < n; ++i) s += fnc(d[i]) * w[i];\n\treturn s;\n}\n\nFECORE_API mat3ds weightedAverageStructureTensor(mat3ds* d, double* w, int n);\n\n// evaluate Log_p (X)\nFECORE_API mat3ds Log(const mat3ds& p, const mat3ds& X);\nFECORE_API mat3ds Exp(const mat3ds& p, const mat3ds& X);\n"
  },
  {
    "path": "FECore/matrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"matrix.h\"\n#include <assert.h>\n#include <math.h>\n#include <memory>\nusing namespace std;\n\n//////////////////////////////////////////////////////////////////////\n// Construction/Destruction\n//////////////////////////////////////////////////////////////////////\n\n//-----------------------------------------------------------------------------\n// These functions are defined in colsol.cpp\nvoid lubksb(double**a, int n, int *indx, double b[]);\nvoid ludcmp(double**a, int n, int* indx);\n\n//-----------------------------------------------------------------------------\n// These functions are defined in svd.cpp\nvoid svbksb(matrix& u, vector<double>& w, matrix& v, vector<double>& b, vector<double>& x);\nvoid svdcmp(matrix& a, vector<double>& w, matrix& v);\n\n//-----------------------------------------------------------------------------\nvector<double> operator / (vector<double>& b, matrix& m)\n{\n\tint n = (int)b.size();\n\n\tvector<double> x(b);\n\tvector<int> indx(n);\n\tmatrix a(m);\n\n\tludcmp(a, n, &indx[0]);\n\tlubksb(a, n, &indx[0], &x[0]);\n\n\treturn x;\n}\n\nvector<double> operator * (matrix& m, vector<double>& b)\n{\n\tint i, j;\n\tint NR = m.rows();\n\tint NC = m.columns();\n\tassert(NC == b.size());\n\tvector<double> r(NR);\n\tfor (i=0; i<NR; ++i)\n\t{\n\t\tr[i] = 0.0;\n\t\tfor (j=0; j<NC; ++j) r[i] += m[i][j]*b[j];\n\t}\n\n\treturn r;\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::alloc(int nr, int nc)\n{\n\tm_nr = nr;\n\tm_nc = nc;\n\tm_nsize = nr*nc;\n\n\tm_pd = new double [m_nsize];\n\tm_pr = new double*[nr];\n\tfor (int i=0; i<nr; i++) m_pr[i] = m_pd + i*nc;\n}\n\n//-----------------------------------------------------------------------------\n//! Constructor for matrix class. \nmatrix::matrix(int nr, int nc)\n{\n\talloc(nr, nc);\n}\n\n//-----------------------------------------------------------------------------\n//! matrix destructor\nvoid matrix::clear()\n{\n\tif (m_pr) delete [] m_pr;\n\tif (m_pd) delete [] m_pd;\n\tm_pd = 0;\n\tm_pr = 0;\n\tm_nr = m_nc = 0;\n}\n\n//-----------------------------------------------------------------------------\n//! Copy constructor for matrix class. \nmatrix::matrix(const matrix& m)\n{\n\talloc(m.m_nr, m.m_nc);\n\tfor (int i=0; i<m_nsize; ++i) m_pd[i] = m.m_pd[i];\n}\n\n//-----------------------------------------------------------------------------\n//! constructor\nmatrix::matrix(const mat3d& m)\n{\n\talloc(3, 3);\n\tm_pr[0][0] = m[0][0]; m_pr[0][1] = m[0][1]; m_pr[0][2] = m[0][2];\n\tm_pr[1][0] = m[1][0]; m_pr[1][1] = m[1][1]; m_pr[1][2] = m[1][2];\n\tm_pr[2][0] = m[2][0]; m_pr[2][1] = m[2][1]; m_pr[2][2] = m[2][2];\n}\n\n//-----------------------------------------------------------------------------\n//! assignment operator\nmatrix& matrix::operator = (const mat3d& m)\n{\n\tresize(3, 3);\n\tm_pr[0][0] = m[0][0]; m_pr[0][1] = m[0][1]; m_pr[0][2] = m[0][2];\n\tm_pr[1][0] = m[1][0]; m_pr[1][1] = m[1][1]; m_pr[1][2] = m[1][2];\n\tm_pr[2][0] = m[2][0]; m_pr[2][1] = m[2][1]; m_pr[2][2] = m[2][2];\n\treturn *this;\n}\n\n//-----------------------------------------------------------------------------\nmatrix& matrix::operator = (const matrix& m)\n{\n\tif ((m.m_nr != m_nr) || (m.m_nc != m_nc))\n\t{\n\t\tclear();\n\t\talloc(m.m_nr, m.m_nc);\n\t}\n\tfor (int i=0; i<m_nsize; ++i) m_pd[i] = m.m_pd[i];\n\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::resize(int nr, int nc)\n{\n\tif ((nr != m_nr) || (nc != m_nc))\n\t{\n\t\tclear();\n\t\talloc(nr, nc);\n\t}\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::operator * (double a) const\n{\n\tmatrix m(m_nr, m_nc);\n\tint n = m_nr*m_nc;\n\tfor (int i = 0; i < n; ++i) m.m_pd[i] = m_pd[i] * a;\n\treturn m;\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::operator * (const matrix& m) const\n{\n\tassert(m_nc == m.m_nr);\n\tmatrix a(m_nr, m.m_nc);\n\n\t// NOTE: commented this out since this can be called for small matrices.\n\t// TODO: make a separate function that implements a parallel version. (e.g. pmult) \n//\t#pragma omp parallel for shared(a)\n\tfor (int i = 0; i < m_nr; ++i)\n\t{\n\t\tdouble* pa = a.m_pr[i];\n\t\tfor (int j = 0; j < m.m_nc; ++j) pa[j] = 0.0;\n\t\tfor (int k = 0; k < m_nc; ++k)\n\t\t{\n\t\t\tconst double pik = m_pr[i][k];\n\t\t\tconst double* pm = m.m_pr[k];\n\t\t\tfor (int j = 0; j < m.m_nc; ++j)\n\t\t\t{\n\t\t\t\tpa[j] += pik * pm[j];\n\t\t\t}\n\t\t}\n\t}\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::add(int i, int j, const matrix& m)\n{\n\tint mr = m.rows();\n\tint mc = m.columns();\n\tfor (int r = 0; r < mr; ++r)\n\t\tfor (int c = 0; c < mc; ++c)\n\t\t\tm_pr[i + r][j + c] += m[r][c];\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::add(int i, int j, const vec3d&  a)\n{\n\tm_pr[i  ][j] += a.x;\n\tm_pr[i+1][j] += a.y;\n\tm_pr[i+2][j] += a.z;\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::adds(int i, int j, const matrix& m, double s)\n{\n\tint mr = m.rows();\n\tint mc = m.columns();\n\tfor (int r = 0; r < mr; ++r)\n\t\tfor (int c = 0; c < mc; ++c) \n\t\t\tm_pr[i + r][j + c] += m[r][c]*s;\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::adds(const matrix& m, double s)\n{\n\tconst int mr = m.rows();\n\tconst int mc = m.columns();\n\tfor (int r = 0; r < mr; ++r)\n\t\tfor (int c = 0; c < mc; ++c)\n\t\t\tm_pr[r][c] += m[r][c] * s;\n}\n\n//-----------------------------------------------------------------------------\n// Calculate the LU decomposition of this matrix. Note that this will modify\n// the matrix. This is used for repeated solves of a linear system. Use \n// lusolve for solving after lufactor.\nvoid matrix::lufactor(vector<int>& indx)\n{\n\t// make sure this is a square matrix\n\tassert(m_nr == m_nc);\n\n\t// do a LU decomposition\n\tint n = m_nr;\n\tindx.resize(n);\n\tludcmp(*(this), n, &indx[0]);\n}\n\n//-----------------------------------------------------------------------------\n// Solve the linear system Ax=b, where A has been factored using lufactor.\n// The indx array is the same one that was returned from lufactor\nvoid matrix::lusolve(vector<double>& b, vector<int>& indx)\n{\n\t// make sure this is a square matrix\n\tassert(m_nr == m_nc);\n\tlubksb(*(this), m_nr, &indx[0], &b[0]);\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::inverse()\n{\n\t// make sure this is a square matrix\n\tassert(m_nr == m_nc);\n\n\t// make a copy of this matrix\n\t// since we don't want to change it\n\tmatrix a(*this);\n\n\t// do a LU decomposition\n\tint n = m_nr;\n\tvector<int> indx(n);\n\tludcmp(a, n, &indx[0]);\n\n\t// allocate the inverse matrix\n\tmatrix ai(n, n);\n\n\t// do a backsubstituation on the columns of a\n\tvector<double> b; b.assign(n, 0);\n\tfor (int j=0; j<n; ++j)\n\t{\n\t\tb[j] = 1;\n\t\tlubksb(a, n, &indx[0], &b[0]);\n\n\t\tfor (int i=0; i<n; ++i)\n\t\t{\n\t\t\tai[i][j] = b[i];\n\t\t\tb[i] = 0;\n\t\t}\n\t}\n\n\treturn ai;\n}\n\n//-----------------------------------------------------------------------------\n// Matrix using singular value decomposition\nmatrix matrix::svd_inverse()\n{\n\tmatrix U(*this);\n\tmatrix V(m_nr, m_nc);\n\tvector<double> w(m_nc);\n\n\t// calculate the decomposition\n\tsvdcmp(U, w, V);\n\n\tmatrix Ai(m_nc, m_nr); // inverse\n\tfor (int i=0; i<m_nc; ++i)\n\t\tfor (int j=0; j<m_nr; ++j)\n\t\t{\n\t\t\tdouble s = 0.0;\n\t\t\tfor (int k=0;k<m_nc; ++k)\n\t\t\t{\n\t\t\t\tif (w[k] > 0.0)\n\t\t\t\t{\n\t\t\t\t\ts += (V[i][k]*U[j][k])/w[k];\n\t\t\t\t}\n\t\t\t}\n\t\t\tAi[i][j] = s;\n\t\t}\n\treturn Ai;\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::transpose() const\n{\n\tint i, j;\n\tmatrix At(m_nc, m_nr);\n\tfor (i=0; i<m_nr; ++i)\n\t\tfor (j=0; j<m_nc; ++j) At[j][i] = m_pr[i][j];\n\treturn At;\n}\n\n//-----------------------------------------------------------------------------\nmatrix& matrix::operator += (const matrix& m)\n{\n\tassert((m_nr == m.m_nr ) && (m_nc == m.m_nc));\n\tfor (int i=0; i<m_nsize; ++i) m_pd[i] += m.m_pd[i];\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nmatrix& matrix::operator -= (const matrix& m)\n{\n\tassert((m_nr == m.m_nr ) && (m_nc == m.m_nc));\n\tfor (int i=0; i<m_nsize; ++i) m_pd[i] -= m.m_pd[i];\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::operator * (const vec3d& v) const\n{\n\tassert(m_nc == 3);\n\tmatrix A(m_nr, 1);\n\tconst matrix& T = *this;\n\tfor (int i = 0; i < m_nr; ++i)\n\t{\n\t\tA[i][0] = T[i][0]*v.x + T[i][1] * v.y + T[i][2] * v.z;\n\t}\n\treturn A;\n}\n\n//-----------------------------------------------------------------------------\n// calculate outer product of a vector to produce a matrix\nmatrix outer_product(vector<double>& a)\n{\n\tint n = (int) a.size();\n\tmatrix m(n,n);\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tfor (int j=0; j<n; ++j) m[i][j] = a[i]*a[j];\n\t}\n\treturn m;\n}\n\n//-----------------------------------------------------------------------------\n// Calculates the infinity norm. That is, the max of the absolute row sum.\ndouble matrix::inf_norm()\n{\n\tmatrix& self = (*this);\n\tdouble m = 0;\n\tfor (int j=0; j<m_nr; ++j)\n\t{\n\t\tdouble s = 0;\n\t\tfor (int i=0; i<m_nc; ++i) s += fabs(self[j][i]);\n\t\tif (s > m) m = s;\n\t}\n\treturn m;\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::get(int i, int j, int rows, int cols, matrix& A) const\n{\n\t// make sure we create a valid matrix\n\tif ((rows <= 0) || (cols <= 0)) return;\n\n\t// initialize the matrix\n\tA.resize(rows, cols);\n\tA.zero();\n\n\t// make sure the bounds are within this matrix\n\tif ((i >= m_nr) || (j >= m_nc)) return;\n\tif ((i + rows <= 0) || (j + cols <= 0)) return;\n\n\t// set range\n\tint r0 = 0;\n\tint r1 = rows - 1;\n\tint c0 = 0;\n\tint c1 = cols - 1;\n\tif (i < 0) r0 -= i;\n\tif (j < 0) c0 -= j;\n\tif (i + rows > m_nr) r1 -= rows + i - m_nr;\n\tif (j + cols > m_nc) c1 -= cols + j - m_nc;\n\n\tfor (int r=r0; r<=r1; ++r)\n\t\tfor (int c=c0; c<=c1; ++c)\n\t\t\t\tA[r][c] = (*this)(i+r, j+c);\n}\n\n//-----------------------------------------------------------------------------\n// fill a matrix\nvoid matrix::fill(int i, int j, int rows, int cols, double val)\n{\n\tif ((i >= m_nr) || (j >= m_nc)) return;\n\tif ((i + rows <= 0) || (j + cols <= 0)) return;\n\n\tint r0 = 0;\n\tint r1 = rows - 1;\n\tint c0 = 0;\n\tint c1 = cols - 1;\n\tif (i < 0) r0 -= i;\n\tif (j < 0) c0 -= j;\n\tif (i + rows > m_nr) r1 -= rows + i - m_nr;\n\tif (j + cols > m_nc) c1 -= cols + j - m_nc;\n\n\tfor (int r = r0; r<=r1; ++r)\n\t\tfor (int c = c0; c<=c1; ++c)\n\t\t\t(*this)(i + r, j + c) = val;\n}\n\n//-----------------------------------------------------------------------------\n// solve the linear system Ax=b\nvoid matrix::solve(vector<double>& x, const vector<double>& b)\n{\n\tmatrix A(*this);\n\tvector<int> index;\n\tA.lufactor(index);\n\tx = b;\n\tA.lusolve(x, index);\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::operator + (const matrix& m) const\n{\n\tmatrix s(*this);\n\tfor (int i=0; i<m_nr; ++i)\n\t\tfor (int j=0; j<m_nc; ++j)\n\t\t\ts(i,j) += m(i,j);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nmatrix matrix::operator - (const matrix& m) const\n{\n\tmatrix s(*this);\n\tfor (int i = 0; i<m_nr; ++i)\n\t\tfor (int j = 0; j<m_nc; ++j)\n\t\t\ts(i, j) -= m(i, j);\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::mult(vector<double>& x, vector<double>& y)\n{\n\tfor (int i = 0; i < m_nr; ++i)\n\t{\n\t\tdouble* di = m_pd + i * m_nc;\n\t\ty[i] = 0.0;\n\t\tfor (int j = 0; j < m_nc; ++j) y[i] += di[j] * x[j];\n\t}\n}\n\n// Here y = this*m*x. This is useful if this*m is a very large matrix, \n// but is then immediately multiplied by a vector, brining its size down \n// to m_nr x 1. In this unique circumstance, the memory requrements can be \n// drastically lower. \nvoid matrix::mult(const matrix& m, std::vector<double>& x, std::vector<double>& y)\n{\n    assert(m_nc == m.m_nr);\n    assert(m.m_nc == x.size());\n\ty.assign(m_nr, 0.0);\n\n\tint nx = (int)x.size();\n\tvector<double> tmp(m_nc, 0.0);\n#pragma omp parallel shared(tmp)\n\t{\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_nc; ++i)\n\t\t{\n\t\t\tfor (int k = 0; k < nx; ++k) tmp[i] += m.m_pr[i][k] * x[k];\n\t\t}\n\n\t\t#pragma omp for\n\t\tfor (int i = 0; i < m_nr; ++i)\n\t\t{\n\t\t\tfor (int k = 0; k < m_nc; ++k) y[i] += m_pr[i][k] * tmp[k];\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::mult_transpose(vector<double>& x, vector<double>& y)\n{\n\tfor (int i = 0; i < m_nc; ++i) y[i] = 0.0;\n\n\tfor (int i = 0; i < m_nr; ++i)\n\t{\n\t\tdouble* di = m_pd + i * m_nc;\n\t\tfor (int j = 0; j < m_nc; ++j) y[j] += di[j] * x[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid matrix::mult_transpose_self(matrix& AAt)\n{\n\tmatrix& A = *this;\n\tint N = m_nc;\n\tint R = m_nr;\n\tfor (int i = 0; i < N; ++i)\n\t\tfor (int j = 0; j < N; ++j)\n\t\t{\n\t\t\tdouble& aij = AAt[i][j];\n\t\t\taij = 0.0;\n\t\t\tfor (int k = 0; k < R; ++k) aij += A[k][i] * A[k][j];\n\t\t}\n}\n\n//-----------------------------------------------------------------------------\nbool matrix::lsq_solve(vector<double>& x, vector<double>& b)\n{\n\tif ((int)x.size() != m_nc) return false;\n\tif ((int)b.size() != m_nr) return false;\n\n\tvector<double> y(m_nc);\n\tmult_transpose(b, y);\n\n\tmatrix AA(m_nc, m_nc);\n\tmult_transpose_self(AA);\n\n\tAA.solve(x, y);\n\n\treturn true;\n}\n\n#define ROTATE(a, i, j, k, l) g=a[i][j]; h=a[k][l];a[i][j]=g-s*(h+g*tau); a[k][l] = h + s*(g - h*tau);\n\n//-----------------------------------------------------------------------------\nbool matrix::eigen_vectors(matrix& Eigen, vector<double>& eigen_values)\n{\n\tmatrix& A = *this;\n\tint N = m_nc;\n\tint R = m_nr;\n\tconst int NMAX = 50;\n\tdouble sm, tresh, g, h, t, c, tau, s, th;\n\tconst double eps = 0;//1.0e-15;\n\tint k;\n\n\t//initialize Eigen to identity\n\tfor (int i = 0; i < R; i++)\n\t{\n\t\tfor (int j = 0; j < N; j++) Eigen[i][j] = 0;\n\t\tEigen[i][i] = 1;\n\t}\n\tvector<double> b;\n\tb.reserve(R);\n\tvector<double> z;\n\tz.reserve(R);\n\n\t// initialize b and eigen_values to the diagonal of A\n\tfor (int i = 0; i < R; i++)\n\t{\n\t\tb.push_back(A[i][i]);\n\t\teigen_values.push_back(A[i][i]);\n\t\tz.push_back(0);\n\t}\n\t// loop\n\tint n, nrot = 0;\n\tfor (n = 0; n < NMAX; ++n)\n\t{\n\t\t// sum off-diagonal elements\n\t\tsm = 0;\n\t\tfor (int i = 0; i < N - 1; i++) {\n\t\t\tfor (int j = i + 1; j < N; j++)\n\t\t\t\tsm += fabs(A[i][j]);\n\t\t}\n\t\tif (sm <= eps) {\n\t\t\tbreak;\n\t\t}\n\t\t// set the treshold\n\t\tif (n < 3) tresh = 0.2 * sm / (R * R); else tresh = 0.0;\n\n\t\t// loop over off-diagonal elements\n\t\tfor (int i = 0; i < N - 1; i++) {\n\t\t\tfor (int j = i + 1; j < N; j++) {\n\n\t\t\t\tg = 100.0 * fabs(A[i][j]);\n\t\t\t\t// after four sweeps, skip the rotation if the off-diagonal element is small\n\t\t\t\tif ((n > 3) && ((fabs(eigen_values[i]) + g) == fabs(eigen_values[i])) && ((fabs(eigen_values[j]) + g) == fabs(eigen_values[j])))\n\t\t\t\t{\n\t\t\t\t\tA[i][j] = 0.0;\n\t\t\t\t}\n\t\t\t\telse if (fabs(A[i][j]) > tresh) {\n\t\t\t\t\th = eigen_values[j] - eigen_values[i];\n\t\t\t\t\tif ((fabs(h) + g) == fabs(h))\n\t\t\t\t\t\tt = A[i][j] / h;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tth = 0.5 * h / A[i][j];\n\t\t\t\t\t\tt = 1.0 / (fabs(th) + sqrt(1 + th * th));\n\t\t\t\t\t\tif (th < 0.0) t = -t;\n\t\t\t\t\t}\n\t\t\t\t\tc = 1.0 / sqrt(1.0 + t * t);\n\t\t\t\t\ts = t * c;\n\t\t\t\t\ttau = s / (1.0 + c);\n\t\t\t\t\th = t * A[i][j];\n\t\t\t\t\tz[i] -= h;\n\t\t\t\t\tz[j] += h;\n\t\t\t\t\teigen_values[i] -= h;\n\t\t\t\t\teigen_values[j] += h;\n\t\t\t\t\tA[i][j] = 0;\n\t\t\t\t\tfor (k = 0; k <= i - 1; ++k) { ROTATE(A, k, i, k, j) }\n\t\t\t\t\tfor (k = i + 1; k <= j - 1; ++k) { ROTATE(A, i, k, k, j) }\n\t\t\t\t\tfor (k = j + 1; k < N; ++k) { ROTATE(A, i, k, j, k) }\n\t\t\t\t\tfor (k = 0; k < N; ++k) { ROTATE(Eigen, k, i, k, j) }\n\t\t\t\t\t++nrot;\n\t\t\t\t}\n\t\t\t}\n\t\t}//end of for loop\n\n\t\t//Update eigen_values with the sum. Reinitialize z.\n\t\tfor (int i = 0; i < R; ++i)\n\t\t{\n\t\t\tb[i] += z[i];\n\t\t\teigen_values[i] = b[i];\n\t\t\tz[i] = 0.0;\n\t\t}\n\t}\n\n\t// we sure we converged\n\tassert(n < NMAX);\n\treturn true;\n}\n\nmatrix covariance(const matrix& a)\n{\n\tint n = a.rows();\n\tint d = a.columns();\n\n\t// calculate mean row vector\n\tvector<double> mu(d, 0.0);\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tfor (int j = 0; j < d; ++j) mu[j] += a[i][j];\n\t}\n\tfor (int i = 0; i < d; ++i) mu[i] /= n;\n\n\t// normalization factor\n\t// NOTE: it is n - 1, to be consistent with MatLab!\n\tdouble R = (n == 1 ? 1.0 : n - 1);\n\n\t// calculate covariance matrix\n\tmatrix c(d, d); c.zero();\n\tfor (int i = 0; i < d; ++i)\n\t\tfor (int j = 0; j < d; ++j)\n\t\t{\n\t\t\tdouble cij = 0.0;\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tcij += (a[k][i] - mu[i]) * (a[k][j] - mu[j]);\n\t\t\t}\n\t\t\tcij /= R;\n\n\t\t\tc[i][j] = cij;\n\t\t}\n\n\treturn c;\n}\n"
  },
  {
    "path": "FECore/matrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <memory.h>\n#include <vector>\n#include \"fecore_api.h\"\n#include \"mat3d.h\"\n\n//-----------------------------------------------------------------------------\n//! General purpose matrix class.\nclass FECORE_API matrix\n{\npublic:\n\t//! constructor\n\tmatrix() : m_nr(0), m_nc(0), m_nsize(0), m_pd(nullptr), m_pr(nullptr) {}\n\n\t//! constructor\n\tmatrix(int nr, int nc);\n\n\t//! copy constructor\n\tmatrix(const matrix& m);\n\n\t//! constructor\n\tmatrix(const mat3d& m);\n\n\t//! move constructor\n\tmatrix(matrix&& m);\n\n\t//! assignment operator\n\tmatrix& operator = (const matrix& m);\n\n\t//! move assigment operator\n\tmatrix& operator = (matrix&& m);\n\n\t//! assignment operator\n\tmatrix& operator = (const mat3d& m);\n\n\t//! Matrix reallocation\n\tvoid resize(int nr, int nc);\n\n\t//! destructor\n\t~matrix() { clear(); }\n\n\t//! access operator\n\tdouble * operator [] (int l) { return m_pr[l]; }\n\tconst double* operator [] (int l) const { return m_pr[l]; }\n\tdouble& operator () (int i, int j) { return m_pr[i][j]; }\n\tdouble operator () (int i, int j) const { return m_pr[i][j]; }\n\toperator double** () { return m_pr; }\n\n\tint rows   () const { return m_nr; }\n\tint columns() const { return m_nc; }\n\n\tvoid zero() { memset(m_pd, 0, sizeof(double)*m_nsize); }\n\n\t//! matrix transpose\n\tmatrix transpose() const;\n\n\t//! matrix inversion\n\tmatrix inverse();\n\n\t//! matrix inverse using SVD\n\tmatrix svd_inverse();\n\n\t//! matrix operators\n\tmatrix operator *(double a) const;\n\tmatrix operator * (const matrix& m) const;\n\n\tmatrix operator + (const matrix& m) const;\n\n\tmatrix operator - (const matrix& m) const;\n\n\tmatrix& operator += (const matrix& m);\n\n\tmatrix& operator -= (const matrix& m);\n\n\tmatrix& operator *=(double g)\n\t{\n\t\tfor (int i=0; i<m_nsize; ++i) m_pd[i] *= g;\n\t\treturn *this;\n\t}\n\n\tmatrix operator * (const vec3d& v) const;\n\n\t// calculate the LU decomposition\n\t// note that this modifies the matrix\n\tvoid lufactor(std::vector<int>& indx);\n\n\t// solve using the lu factor calculated with lufactor\n\tvoid lusolve(std::vector<double>& b, std::vector<int>& indx);\n\n\t// solve the linear system Ax=b\n\tvoid solve(std::vector<double>& x, const std::vector<double>& b);\n\n\tbool lsq_solve(std::vector<double>& x, std::vector<double>& b);\n\n\tbool eigen_vectors(matrix& Eigen, std::vector<double>& eigen_values);\n\n\t// infinity-norm\n\tdouble inf_norm();\n\n\tvoid mult(std::vector<double>& x, std::vector<double>& y);\n    void mult(const matrix& m, std::vector<double>& x, std::vector<double>& y);\n\tvoid mult_transpose(std::vector<double>& x, std::vector<double>& y);\n\tvoid mult_transpose_self(matrix& AAt);\n\npublic:\n\tvoid set(int i, int j, const mat3d& a);\n\t\n\tvoid add(int i, int j, const mat3ds& a);\n\tvoid add(int i, int j, const mat3da& a);\n\tvoid add(int i, int j, const mat3dd& a);\n\tvoid add(int i, int j, const mat3d&  a);\n\tvoid add(int i, int j, const matrix& a);\n\n\tvoid add(int i, int j, const vec3d&  a);\n\n\tvoid add_symm(int i, int j, const mat3d&  a);\n\tvoid add_symm(int i, int j, const vec3d&  a);\n\n\tvoid adds(int i, int j, const matrix& m, double s);\n\tvoid adds(const matrix& m, double s);\n\n\tvoid sub(int i, int j, const mat3ds& a);\n\tvoid sub(int i, int j, const mat3da& a);\n\tvoid sub(int i, int j, const mat3dd& a);\n\tvoid sub(int i, int j, const mat3d&  a);\n\tvoid sub(int i, int j, const matrix& a);\n\n\tvoid get(int i, int j, mat3d& a) const;\n\n\t// copy-lower-triangular\n\t// make the matrix symmetric by copying the lower triangular part\n\tvoid copy_lt()\n\t{\n\t\tassert(m_nr==m_nc);\n\t\tif (m_nr != m_nc) return;\n\t\tfor (int i=0; i<m_nr; ++i)\n\t\t{\n\t\t\tfor (int j=i+1; j<m_nr; ++j)\n\t\t\t{\n\t\t\t\tm_pr[i][j] = m_pr[j][i];\n\t\t\t}\n\t\t}\n\t}\n\n\t// copy-upper-triangular\n\t// make the matrix symmetric by copying the upper triangular part\n\tvoid copy_ut()\n\t{\n\t\tassert(m_nr==m_nc);\n\t\tif (m_nr != m_nc) return;\n\t\tfor (int i=0; i<m_nr; ++i)\n\t\t{\n\t\t\tfor (int j=i+1; j<m_nr; ++j)\n\t\t\t{\n\t\t\t\tm_pr[j][i] = m_pr[i][j];\n\t\t\t}\n\t\t}\n\t}\n\n\t// extract a matrix block\n\t// the returned matrix will have the dimensions rows x cols\n\t// if the matrix doesn't fit in this matrix, the missing entries will be set to zero\n\tvoid get(int i, int j, int rows, int cols, matrix& A) const;\n\n\t// fill a matrix\n\tvoid fill(int i, int j, int rows, int cols, double val);\n\nprivate:\n\tvoid alloc(int nr, int nc);\n\tvoid clear();\n\nprotected:\n\tdouble**\tm_pr;\t// pointer to rows\n\tdouble*\t\tm_pd;\t// matrix elements\n\n\tint\tm_nr;\t\t// nr of rows\n\tint\tm_nc;\t\t// nr of columns\n\tint\tm_nsize;\t// size of matrix (ie. total nr of elements = nr*nc)\n};\n\nstd::vector<double> FECORE_API operator / (std::vector<double>& b, matrix& m);\nstd::vector<double> FECORE_API operator * (matrix& m, std::vector<double>& b);\nmatrix FECORE_API outer_product(std::vector<double>& a);\n\ninline void matrix::set(int i, int j, const mat3d& a)\n{\n\tm_pr[i][j] = a(0,0); m_pr[i][j+1] = a(0,1); m_pr[i][j+2] = a(0,2); i++;\n\tm_pr[i][j] = a(1,0); m_pr[i][j+1] = a(1,1); m_pr[i][j+2] = a(1,2); i++;\n\tm_pr[i][j] = a(2,0); m_pr[i][j+1] = a(2,1); m_pr[i][j+2] = a(2,2);\n}\n\ninline void matrix::add(int i, int j, const mat3ds& a)\n{\n\tm_pr[i][j] += a.xx(); m_pr[i][j+1] += a.xy(); m_pr[i][j+2] += a.xz(); i++;\n\tm_pr[i][j] += a.xy(); m_pr[i][j+1] += a.yy(); m_pr[i][j+2] += a.yz(); i++;\n\tm_pr[i][j] += a.xz(); m_pr[i][j+1] += a.yz(); m_pr[i][j+2] += a.zz();\n}\n\ninline void matrix::add(int i, int j, const mat3da& a)\n{\n\tm_pr[i][j+1] += a.xy(); m_pr[i][j+2] += a.xz(); i++;\n\tm_pr[i][j  ] -= a.xy(); m_pr[i][j+2] += a.yz(); i++;\n\tm_pr[i][j  ] -= a.xz(); m_pr[i][j+1] -= a.yz();\n}\n\ninline void matrix::add(int i, int j, const mat3dd& a)\n{\n\tm_pr[i][j  ] += a.diag(0); i++;\n\tm_pr[i][j+1] += a.diag(1); i++;\n\tm_pr[i][j+2] += a.diag(2);\n}\n\ninline void matrix::add(int i, int j, const mat3d& a)\n{\n\tm_pr[i][j] += a(0,0); m_pr[i][j+1] += a(0,1); m_pr[i][j+2] += a(0,2); i++;\n\tm_pr[i][j] += a(1,0); m_pr[i][j+1] += a(1,1); m_pr[i][j+2] += a(1,2); i++;\n\tm_pr[i][j] += a(2,0); m_pr[i][j+1] += a(2,1); m_pr[i][j+2] += a(2,2);\n}\n\ninline void matrix::add_symm(int i, int j, const mat3d&  a)\n{\n\tm_pr[i  ][j] += a(0, 0); m_pr[i  ][j + 1] += a(0, 1); m_pr[i  ][j + 2] += a(0, 2);\n\tm_pr[i+1][j] += a(1, 0); m_pr[i+1][j + 1] += a(1, 1); m_pr[i+1][j + 2] += a(1, 2);\n\tm_pr[i+2][j] += a(2, 0); m_pr[i+2][j + 1] += a(2, 1); m_pr[i+2][j + 2] += a(2, 2);\n\n\tm_pr[j  ][i] += a(0, 0); m_pr[j  ][i + 1] += a(1, 0); m_pr[j  ][i + 2] += a(2, 0);\n\tm_pr[j+1][i] += a(0, 1); m_pr[j+1][i + 1] += a(1, 1); m_pr[j+1][i + 2] += a(2, 1);\n\tm_pr[j+2][i] += a(0, 2); m_pr[j+2][i + 1] += a(1, 2); m_pr[j+2][i + 2] += a(2, 2);\n}\n\ninline void matrix::add_symm(int i, int j, const vec3d&  a)\n{\n\tm_pr[i  ][j] += a.x;\n\tm_pr[i+1][j] += a.y;\n\tm_pr[i+2][j] += a.z;\n\n\tm_pr[j][i  ] += a.x;\n\tm_pr[j][i+1] += a.y;\n\tm_pr[j][i+2] += a.z;\n}\n\ninline void matrix::sub(int i, int j, const mat3ds& a)\n{\n\tm_pr[i][j] -= a.xx(); m_pr[i][j+1] -= a.xy(); m_pr[i][j+2] -= a.xz(); i++;\n\tm_pr[i][j] -= a.xy(); m_pr[i][j+1] -= a.yy(); m_pr[i][j+2] -= a.yz(); i++;\n\tm_pr[i][j] -= a.xz(); m_pr[i][j+1] -= a.yz(); m_pr[i][j+2] -= a.zz();\n}\n\ninline void matrix::sub(int i, int j, const mat3da& a)\n{\n\tm_pr[i][j+1] -= a.xy(); m_pr[i][j+2] -= a.xz(); i++;\n\tm_pr[i][j  ] += a.xy(); m_pr[i][j+2] -= a.yz(); i++;\n\tm_pr[i][j  ] += a.xz(); m_pr[i][j+1] += a.yz();\n}\n\ninline void matrix::sub(int i, int j, const mat3dd& a)\n{\n\tm_pr[i][j  ] -= a.diag(0); i++;\n\tm_pr[i][j+1] -= a.diag(1); i++;\n\tm_pr[i][j+2] -= a.diag(2);\n}\n\ninline void matrix::sub(int i, int j, const mat3d& a)\n{\n\tm_pr[i][j] -= a(0,0); m_pr[i][j+1] -= a(0,1); m_pr[i][j+2] -= a(0,2); i++;\n\tm_pr[i][j] -= a(1,0); m_pr[i][j+1] -= a(1,1); m_pr[i][j+2] -= a(1,2); i++;\n\tm_pr[i][j] -= a(2,0); m_pr[i][j+1] -= a(2,1); m_pr[i][j+2] -= a(2,2);\n}\n\ninline void matrix::sub(int i, int j, const matrix& m)\n{\n\tint mr = m.rows();\n\tint mc = m.columns();\n\tfor (int r = 0; r < mr; ++r)\n\t\tfor (int c = 0; c < mc; ++c)\n\t\t\tm_pr[i + r][j + c] -= m[r][c];\n}\n\ninline void matrix::get(int i, int j, mat3d& a) const\n{\n\ta[0][0] = m_pr[i  ][j]; a[0][1] = m_pr[i  ][j+1]; a[0][2] = m_pr[i  ][j+2];\n\ta[1][0] = m_pr[i+1][j]; a[1][1] = m_pr[i+1][j+1]; a[1][2] = m_pr[i+1][j+2];\n\ta[2][0] = m_pr[i+2][j]; a[2][1] = m_pr[i+2][j+1]; a[2][2] = m_pr[i+2][j+2];\n}\n\n//! move constructor\ninline matrix::matrix(matrix&& m)\n{\n\tm_nr = m.m_nr;\n\tm_nc = m.m_nc;\n\tm_pd = m.m_pd;\n\tm_pr = m.m_pr;\n\n\tm.m_pr = nullptr;\n\tm.m_pd = nullptr;\n}\n\n//! move assigment operator\ninline matrix& matrix::operator = (matrix&& m)\n{\n\tif (this != &m)\n\t{\n\t\tif (m_pd) delete[] m_pd;\n\t\tif (m_pr) delete[] m_pr;\n\n\t\tm_nr = m.m_nr;\n\t\tm_nc = m.m_nc;\n\t\tm_pd = m.m_pd;\n\t\tm_pr = m.m_pr;\n\n\t\tm.m_pr = nullptr;\n\t\tm.m_pd = nullptr;\n\t}\n\n\treturn *this;\n}\n\n// Calculate the covariance of a matrix. \n// The rows represent the observations, and the columns represent random variables.\n// The returned covariance matrix is an ncol x ncol matrix.\nmatrix FECORE_API covariance(const matrix& a);\n"
  },
  {
    "path": "FECore/mortar.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <assert.h>\n#include \"mortar.h\"\n#include <math.h>\n#include \"FEMesh.h\"\n\n//-----------------------------------------------------------------------------\n// subtract operator for POINT2D\nPOINT2D operator - (POINT2D a, POINT2D b)\n{\n\tPOINT2D p = {a.x - b.x, a.y - b.y};\n\treturn p;\n}\n\n//-----------------------------------------------------------------------------\n// dot product for POINT2D\ndouble operator * (POINT2D a, POINT2D b)\n{\n\treturn a.x*b.x + a.y*b.y;\n}\n\n//-----------------------------------------------------------------------------\n// This function returns twice the area of the triangle defined by [a,b,c].\ndouble Area2(POINT2D a, POINT2D b, POINT2D c)\n{\n\treturn ((b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y));\n}\n\n//-----------------------------------------------------------------------------\n// This function returns the sign of the area, defined by three POINT2Ds. \nint AreaSign(POINT2D a, POINT2D b, POINT2D c, const double eps)\n{\n\tdouble d = ((b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y));\n\tif (d > eps) return 1;\n\telse if (d < -eps) return -1;\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// Checks collinearity of three POINT2Ds\nbool Collinear(POINT2D a, POINT2D b, POINT2D c, const double eps)\n{\n\treturn fabs(Area2(a, b, c)) <= eps;\n}\n\n//-----------------------------------------------------------------------------\n// This function checks if c is between a, b. \n// NOTE: This function assumes that the three points are collinear\nbool Between(POINT2D a, POINT2D b, POINT2D c)\n{\n\tif (a.x != b.x)\n\t{\n\t\treturn (((a.x <= c.x)&&(c.x <= b.x))||\n\t\t\t\t((a.x >= c.x)&&(c.x >= b.x)));\n\t}\n\telse\n\t{\n\t\treturn (((a.y <= c.y)&&(c.y <= b.y))||\n\t\t\t\t((a.y >= c.y)&&(c.y >= b.y)));\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// This function checks if the two segments (a,b) and (c,d) are collinear\n// and if so, returns a POINT2D of intersection. The return code can be used \n// to identify the intersection type\n// 0 = no overlap\n// 3 = overlap\n// NOTE: This function assumes that the segments are parallel\nint ParallelInt(POINT2D a, POINT2D b, POINT2D c, POINT2D d, POINT2D& p, const double eps)\n{\n\t// make sure POINT2Ds are collinear\n\tif (!Collinear(a,b,c, eps)) return 0;\n\n\tif (Between(a, b, c)) { p = c; return 3; }\n\tif (Between(a, b, d)) { p = d; return 3; }\n\tif (Between(c, d, a)) { p = a; return 3; }\n\tif (Between(c, d, b)) { p = b; return 3; }\n\n\treturn 0;\n}\n\n//-----------------------------------------------------------------------------\n// This function calculates the segment-segment intersection. The first segment\n// is defined by POINT2Ds (a,b) and the second segment by (c,d). The intersection\n// POINT2D (if it exists) is returned in p. The return code can be used to identify\n// the intersection type:\n// 0 = no intersection\n// 1 = proper intersection\n// 2 = vertex intersection (segments coincide at vertex)\n// 3 = edge intersections (segments cooincide)\nint SegSegInt(POINT2D a, POINT2D b, POINT2D c, POINT2D d, POINT2D& p, const double eps)\n{\n\t// return code\n\tint nret = -1;\n\n\t// calculate the denominator\n\tdouble denom = a.x*(d.y - c.y) + b.x*(c.y - d.y) + d.x*(b.y - a.y) + c.x*(a.y - b.y);\n\n\t// if the denominator is zero, the segments are parallel; handle separately\n\tif (fabs(denom) <= eps) return ParallelInt(a, b, c, d, p, eps);\n\n\tdouble num = a.x*(d.y - c.y) + c.x*(a.y - d.y) + d.x*(c.y - a.y);\n\tif ((fabs(num)<=eps)||(num==denom)) nret = 2;\n\tdouble s = num / denom;\n\n\tnum = -(a.x*(c.y - b.y) + b.x*(a.y - c.y) + c.x*(b.y - a.y));\n\tif ((fabs(num)<=eps)||(num==denom)) nret = 2;\n\tdouble t = num / denom;\n\n\t// check for proper intersection\n\tif ((0.0 < s)&&(s < 1.0)&&(0.0 < t)&&(t < 1.0)) nret = 1;\n\telse if ((0.0 > s)||(s > 1.0) || (0.0 > t) || (t > 1.0)) nret = 0;\n\n\t// find the intersection POINT2D\n\tp.x = a.x + s*(b.x - a.x);\n\tp.y = a.y + s*(b.y - a.y);\n\n\tassert(nret >= 0);\n\treturn nret;\n}\n\n//-----------------------------------------------------------------------------\n// See if a point is inside a (counter-clockwise) polygon\nbool PointInConvexPoly(POINT2D p, POINT2D* P, int n)\n{\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tint i1 = (i+1)%n;\n\t\tif (Area2(P[i],P[i1],p) < 0) return false;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// see if a convex polygon P is inside the convex polygon Q\nbool ConvexPolyInConvexPoly(POINT2D* P, int n, POINT2D* Q, int m)\n{\n\tfor (int i=0; i<n; ++i)\n\t{\n\t\tif (PointInConvexPoly(P[i], Q, m) == false) return false;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nint InOut(int inFlag, int aHB, int bHA)\n{\n\tif      (aHB > 0) return -1;\n\telse if (bHA > 0) return  1;\n\telse return inFlag;\n}\n\nint Advance(int a, int *aa, int n)\n{\n\t(*aa)++;\n\treturn (a+1)%n;\n}\n\n//-----------------------------------------------------------------------------\n// Calculates the intersection between two convex polygons\nint ConvexIntersect(POINT2D* P, int n, POINT2D* Q, int m, POINT2D* R)\n{\n\tint\t\ta, b;\t\t\t// indices on P and Q (resp)\n\tint\t\ta1, b1;\t\t\t// a-1, b-1 (resp)\n\tPOINT2D\tA, B;\t\t\t// directed edges on P, Q (resp)\n\tint\t\tcross;\t\t\t// sign of z-component of A x B\n\tint\t\tbHA, aHB;\t\t// b in H(a); a in H(b)\n\tPOINT2D\tO = {0,0};\t\t// origin\n\tPOINT2D\tp;\t\t\t\t// intersection points\n\tint\t\tinFlag;\t\t\t// -1,0,1 = InP, Unknown, InQ\n\tint\t\taa, ba;\t\t\t// number of advances on a & b indices (after 1st iter)\n\tbool\tFirstPOINT2D;\t\t// is this the first POINT2D (used to initialize)\n\tPOINT2D\tp0;\t\t\t\t// the first POINT2D\n\tint\t\tcode;\t\t\t// SegSegInt return code\n\tint\t\tnr;\t\t\t\t// number of POINT2Ds in intersection\n\n\tconst double eps = 1e-7;\n\n\t// Initialize the variables\n\tnr = 0;\n\ta = 0; b = 0; aa = 0; ba = 0;\n\tinFlag = 0; FirstPOINT2D = true;\n\n\tdo \n\t{\n\t\t// computation of key variables\n\t\ta1 = (a + n - 1) % n;\n\t\tb1 = (b + m - 1) % m;\n\n\t\tA = P[a] - P[a1];\n\t\tB = Q[b] - Q[b1];\n\n\t\tcross = AreaSign(O, A, B, eps);\n\t\taHB = AreaSign(Q[b1], Q[b], P[a], eps);\n\t\tbHA = AreaSign(P[a1], P[a], Q[b], eps);\n\n\t\t// if A,B intersect, update inflag\n\t\tcode = SegSegInt(P[a1], P[a], Q[b1], Q[b], p, eps);\n\t\tif ((code==1)||(code==2))\n\t\t{\n\t\t\tif ((inFlag == 0) && FirstPOINT2D)\n\t\t\t{\n\t\t\t\taa = ba = 0;\n\t\t\t\tFirstPOINT2D = false;\n\t\t\t\tp0 = p;\n\t\t\t}\n\t\t\tR[nr++] = p;\n\t\t\tinFlag = InOut(inFlag, aHB, bHA);\n\t\t}\n\n\t\t// --- Advance rules ---\n\t\tif ((code == 3) && (A*B < 0))\n\t\t{\n\t\t\t// P,Q intersect in this edge only\n\t\t\tbreak;\n\t\t}\n\n\t\tif ((cross == 0) && (aHB < 0) && (bHA < 0))\n\t\t{\n//\t\t\tprintf(\"P,Q are disjoint\\n\"); \n\t\t\tbreak;\n\t\t}\n\t\telse if ((cross == 0) && (aHB == 0) && (bHA == 0))\n\t\t{\n\t\t\tif (inFlag == -1)\n\t\t\t\tb = Advance(b, &ba, m);\n\t\t\telse\n\t\t\t\ta = Advance(a, &aa, n);\n\t\t}\n\t\telse if (cross >= 0)\n\t\t{\n\t\t\tif (bHA > 0) \n\t\t\t{\n\t\t\t\tif (inFlag == -1) R[nr++] = P[a];\n\t\t\t\ta = Advance(a, &aa, n);\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\tif (inFlag == 1) R[nr++] = Q[b];\n\t\t\t\tb = Advance(b, &ba, m);\n\t\t\t}\n\t\t}\n\t\telse // if cross < 0\n\t\t{\n\t\t\tif (aHB > 0)\n\t\t\t{\n\t\t\t\tif (inFlag == 1) R[nr++] = Q[b];\n\t\t\t\tb = Advance(b, &ba, m);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (inFlag == -1) R[nr++] = P[a];\n\t\t\t\ta = Advance(a, &aa, n);\n\t\t\t}\n\t\t}\n\n\t\t// Quit when both adv, indices have cycled, or one has cycled twice\n\t}\n\twhile ( ((aa<n) || (ba < m)) && (aa < 2*n) && (ba < 2*m) );\n\n\t// deal with special cases (not implemented)\n\tif (inFlag == 0)\n\t{\n\t\t// The boundaries of P and Q do not cross\n\t\t// see if one polygon is inside the other one\n\t\tif (ConvexPolyInConvexPoly(P, n, Q, m))\n\t\t{\n\t\t\t// P is inside Q so copy P to R\n\t\t\tfor (int i=0; i<n; ++i) R[i] = P[i];\n\t\t\tnr = n;\n\t\t}\n\t\telse if (ConvexPolyInConvexPoly(Q, m, P, n))\n\t\t{\n\t\t\t// Q is inside P so copy Q to R\n\t\t\tfor (int i=0; i<m; ++i) R[i] = Q[i];\n\t\t\tnr = m;\n\t\t}\n\t}\n\n\treturn nr;\n}\n\n//-----------------------------------------------------------------------------\n// This function calculates the intersection of a line and a segment. The line\n// is defined by POINT2Ds (a,b) and the segment by (c,d). The intersection\n// POINT2D (if it exists) is returned in p. The return code can be used to identify\n// the intersection type:\n// 0 = no intersection\n// 1 = proper intersection\n// 2 = vertex intersection (segment coincide at vertex)\n// 3 = edge intersections (segment cooincide with line)\nint LineSegInt(POINT2D a, POINT2D b, POINT2D c, POINT2D d, POINT2D& p, double eps)\n{\n\t// return code\n\tint nret = -1;\n\n\t// calculate the denominator\n\tdouble denom = a.x*(d.y - c.y) + b.x*(c.y - d.y) + d.x*(b.y - a.y) + c.x*(a.y - b.y);\n\n\t// if the denominator is zero, the segments are parallel; handle separately\n\tif (fabs(denom) <= eps) return ParallelInt(a, b, c, d, p, eps);\n\n\tdouble num = a.x*(d.y - c.y) + c.x*(a.y - d.y) + d.x*(c.y - a.y);\n\tif ((fabs(num) <= eps)||(num==denom)) nret = 2;\n\tdouble s = num / denom;\n\n\tnum = -(a.x*(c.y - b.y) + b.x*(a.y - c.y) + c.x*(b.y - a.y));\n\tif ((fabs(num) <= eps)||(num==denom)) nret = 2;\n\tdouble t = num / denom;\n\n\t// check for proper intersection\n\tif (nret == -1)\n\t{\n\t\tif      ((0.0 < t) && (t < 1.0)) nret = 1;\n\t\telse if ((0.0 > t) || (t > 1.0)) nret = 0;\n\t}\n\n\t// find the intersection POINT2D\n\tp.x = a.x + s*(b.x - a.x);\n\tp.y = a.y + s*(b.y - a.y);\n\n\tassert(nret >= 0);\n\treturn nret;\n}\n\n//-----------------------------------------------------------------------------\nint ConvexIntersectSH(POINT2D* P, int n, POINT2D* Q, int m, POINT2D* R)\n{\n\t// temporary buffer\n\tPOINT2D tmp[11];\n\n\t// copy Q to R\n\tfor (int i=0; i<m; ++i) R[i] = Q[i];\n\tint nr = m;\n\tif (nr == 0) return 0;\n\n\tconst double eps = 1e-7;\n\n\tPOINT2D p;\n\t// loop over all the edges of the clipping polygon P\n\tfor (int a=0; a<n; ++a)\n\t{\n\t\t// an edge is defined by the current point and the next one\n\t\tint a1 = (a+1)%n;\n\n\t\t// reset the tmp buffer\n\t\tint ntmp = 0;\n\n\t\t// see if the last point lies inside the edge A\n\t\t// we'll use this to decide if the edges cross\n\t\tint b1 = nr-1;\n\t\tint b1HA = AreaSign(P[a], P[a1], R[b1], eps);\n\n\t\t// loop over all the input points\n\t\tfor (int b=0; b<nr; ++b)\n\t\t{\n\t\t\t// see if this point lies inside the edge\n\t\t\tint bHA = AreaSign(P[a], P[a1], R[b], eps);\n\t\t\tif (bHA == 0)\n\t\t\t{\n\t\t\t\t// point b lies on the edge so just add it\n\t\t\t\ttmp[ntmp++] = R[b];\n\t\t\t}\n\t\t\telse if (bHA > 0)\n\t\t\t{\n\t\t\t\t// point b lies inside the edge A\n\t\t\t\t// so check whether an edge was crossed\n\t\t\t\tif (b1HA < 0)\n\t\t\t\t{\n\t\t\t\t\tint ncode = LineSegInt(P[a],P[a1],R[b1],R[b], p, eps);\n\t\t\t\t\tassert((ncode==1)||(ncode==2));\n\t\t\t\t\t// add intersection point\n\t\t\t\t\ttmp[ntmp++] = p;\n\t\t\t\t}\n\n\t\t\t\t// add point b\n\t\t\t\ttmp[ntmp++] = R[b];\n\t\t\t}\n\t\t\telse if (bHA < 0)\n\t\t\t{\n\t\t\t\t// point b lies outside edge, but we still may have an intersection\n\t\t\t\tif (b1HA > 0)\n\t\t\t\t{\n\t\t\t\t\tint ncode = LineSegInt(P[a],P[a1],R[b1],R[b], p, eps);\n\t\t\t\t\tassert((ncode==1)||(ncode==2));\n\t\t\t\t\t// add intersection point\n\t\t\t\t\ttmp[ntmp++] = p;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// advance\n\t\t\tb1 = b;\n\t\t\tb1HA = bHA;\n\t\t}\n\n\t\t// copy tmp buffer to output buffer\n\t\tfor (int i=0; i<ntmp; i++) R[i] = tmp[i];\n\t\tnr = ntmp;\n\n\t\t// if we did not add any points, the polygons do not intersect\n\t\tif (nr == 0) break;\n\t}\n\n\treturn nr;\n}\n\ndouble tri_area(vec3d r[3])\n{\n\treturn ((r[1]-r[0])^(r[2]-r[0])).norm()*0.5;\n}\n\nbool CalculateMortarIntersection(FESurface& ss, FESurface& ms, int k, int l, Patch& patch)\n{\n\t// clear the patch\n\tpatch.Clear();\n\n\t// get the surface elements\n\tFESurfaceElement& es = ss.Element(k);\n\tFESurfaceElement& em = ms.Element(l);\n\n\t// get the nodal coordinates\n\tconst int M = FEElement::MAX_NODES;\n\tvec3d rs[M], rm[M];\n\tint ns = es.Nodes(), nm = em.Nodes();\n\tfor (int i=0; i<ns; ++i) rs[i] = ss.Node(es.m_lnode[i]).m_rt;\n\tfor (int i=0; i<nm; ++i) rm[i] = ms.Node(em.m_lnode[i]).m_rt;\n\n\t// setup an orthonormal coordinate system\n\tvec3d c = rs[0];\n\tvec3d e1 = rs[   1] - rs[0]; e1.unit();\n\tvec3d e2 = rs[ns-1] - rs[0]; e2.unit();\n\tvec3d e3 = e1^e2; e3.unit();\n\te2 = e3^e1;\n\n\t// project all points onto this plane\n\tPOINT2D P[M], Q[M], R[M];\n\tfor (int i=0; i<ns; ++i)\n\t{\n\t\tvec3d r = rs[i] - c;\n\t\tvec3d q = r - e3*(r*e3);\n\t\tP[i].x = q*e1;\n\t\tP[i].y = q*e2;\n\t}\n\n\t// now we do the nodes\n\t// Note that we loop backwards since the element will\n\t// in general have opposite winding\n\tfor (int i=0; i<nm; ++i)\n\t{\n\t\tvec3d r = rm[nm-i-1] - c;\n\t\tvec3d q = r - e3*(r*e3);\n\t\tQ[i].x = q*e1;\n\t\tQ[i].y = q*e2;\n\t}\n\n\t// now we calculate the intersection\n\tint nr = ConvexIntersectSH(P, ns, Q, nm, R);\n/*\tif (nr > 0)\n\t{\n\t\tfor (int i=0; i<nr; ++i)\n\t\t{\n\t\t\tPOINT2D& r = R[i];\n\t\t\tvec3d x = e1*r.x + e2*r.y + c;\n\n\t\t\tdouble qr, qs;\n\t\t\tvec3d ps = ss.ProjectToSurface(es, x, qr, qs);\n\t\t\tconst double eps = 1e-4;\n\t\t\tif ((qr < -eps) || (qs < -eps) || (qr+qs > 1+eps))\n\t\t\t{\n\t\t\t\t// error\n\t\t\t\tint a = 0;\n\t\t\t}\n\n\t\t\tvec3d pm = ms.ProjectToSurface(em, x, qr, qs);\n\t\t\tif ((qr < -eps) || (qs < -eps) || (qr+qs > 1+eps))\n\t\t\t{\n\t\t\t\t// error\n\t\t\t\tint a = 0;\n\t\t\t}\n\n\t\t}\n\t}\n*/\n\tif (nr >= 3)\n\t{\n\t\t// evaluate the center of the patch\n\t\tPOINT2D d;\n\t\td.x = d.y = 0;\n\t\tfor (int k=0; k<nr; ++k)\n\t\t{\n\t\t\td.x += R[k].x;\n\t\t\td.y += R[k].y;\n\t\t}\n\t\td.x /= nr; d.y /= nr;\n\n\t\t// the center point is always the same\n\t\tvec3d r[3];\n\t\tr[0] = e1*d.x + e2*d.y + c;\n\n\t\t// calculate the other patch points\n\t\tfor (int k=0; k<nr; ++k)\n\t\t{\n\t\t\tint k1 = (k+1)%nr;\n\t\t\tr[1] = e1*R[k ].x + e2*R[k ].y + c;\n\t\t\tr[2] = e1*R[k1].x + e2*R[k1].y + c;\n\n\t\t\tpatch.Add(r);\n\t\t}\n\t}\n\n\t// return\n\treturn (patch.Empty() == false);\n}\n\nvoid CalculateMortarSurface(FESurface& ss, FESurface& ms, MortarSurface& mortar)\n{\n\t// loop over all non-mortar facets\n\tint NSF = ss.Elements();\n\tint NMF = ms.Elements();\n\tfor (int i=0; i<NSF; ++i)\n\t{\n\t\t// get the non-mortar surface element\n\t\tFESurfaceElement& se = ss.Element(i);\n\n\t\t// loop over all the mortar surface elements\n\t\tfor (int j=0; j<NMF; ++j)\n\t\t{\n\t\t\t// get the next surface element\n\t\t\tFESurfaceElement& me = ms.Element(j);\n\n\t\t\t// calculate the patch of triangles, representing the intersection\n\t\t\t// of the non-mortar facet with the mortar facet\n\t\t\tPatch patch(i,j);\n\t\t\tCalculateMortarIntersection(ss, ms, i, j, patch);\n\t\t\tmortar.AddPatch(patch);\n\t\t}\n\t}\n}\n\nbool ExportMortar(MortarSurface& mortar, const char* szfile)\n{\n\tFILE* fp = fopen(szfile, \"wt\");\n\tif (fp == 0) return false;\n\n\tfprintf(fp, \"solid %s\\n\", \"mortar\");\n\tint NP = mortar.Patches();\n\tfor (int i=0; i<NP; ++i)\n\t{\n\t\tPatch& patch = mortar.GetPatch(i);\n\t\tint np = patch.Size();\n\t\tfor (int j=0; j<np; j++)\n\t\t{\n\t\t\tPatch::FACET& tri = patch.Facet(j);\n\n\t\t\tvec3d e1 = tri.r[1] - tri.r[0];\n\t\t\tvec3d e2 = tri.r[2] - tri.r[0];\n\t\t\tvec3d n = e1^e2; n.unit();\n\n\t\t\tfprintf(fp, \"facet normal %g %g %g\\n\", n.x, n.y, n.z);\n\t\t\tfprintf(fp, \"outer loop\\n\");\n\t\t\tfor (int k=0; k<3; ++k)\n\t\t\t{\n\t\t\t\tvec3d& r = tri.r[k];\n\t\t\t\tfprintf(fp, \"vertex %g %g %g\\n\", r.x, r.y, r.z);\n\t\t\t}\n\t\t\tfprintf(fp, \"endloop\\n\");\n\t\t\tfprintf(fp, \"endfacet\\n\");\n\t\t}\n\t}\n\tfprintf(fp, \"endsolid\\n\");\n\n\tfclose(fp);\n\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/mortar.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include \"FESurface.h\"\n#include <vector>\n\n//-----------------------------------------------------------------------------\n// Structure for representing 2D points\nstruct POINT2D\n{\n\tdouble x, y;\n};\n\n//-----------------------------------------------------------------------------\n// Calculates the intersection between two convex polygons\nFECORE_API int ConvexIntersect(POINT2D* P, int n, POINT2D* Q, int m, POINT2D* R);\n\n//-----------------------------------------------------------------------------\nclass FECORE_API Patch\n{\npublic:\n\tstruct FACET\n\t{\n\t\tFACET(){}\n\t\tFACET(vec3d x[3]) { r[0] = x[0]; r[1] = x[1]; r[2] = x[2]; }\n\t\tvec3d\tr[3];\t//!< position of nodes\n\n\t\t// Evaluate the spatial position using iso-parametric coordinates\n\t\tvec3d Position(double rp, double sp)\n\t\t{\n\t\t\treturn (r[0]*(1.0 - rp - sp) + r[1]*rp + r[2]*sp);\n\t\t}\n\n\t\t//! calculate the area of the patch\n\t\tdouble Area()\n\t\t{\n\t\t\tvec3d n = (r[1] - r[0])^(r[2] - r[0]);\n\t\t\treturn n.norm()*0.5;\n\t\t}\n\t};\n\npublic:\n\tPatch(int k, int l) : m_primary_facet_id(k), m_secondary_facet_id(l) {}\n\n\tPatch(const Patch& p) { \n\t\tm_tri = p.m_tri; \n\t\tm_primary_facet_id = p.m_primary_facet_id;\n\t\tm_secondary_facet_id = p.m_secondary_facet_id;\n\t}\n\n\tPatch& operator = (const Patch& p) { \n\t\tm_tri = p.m_tri; \n\t\tm_primary_facet_id = p.m_primary_facet_id;\n\t\tm_secondary_facet_id = p.m_secondary_facet_id;\n\t\treturn *this; \n\t}\n\n\tint GetPrimaryFacetID() const { return m_primary_facet_id; }\n\tint GetSecondaryFacetID() const { return m_secondary_facet_id; }\n\npublic:\n\t//! Clear the patch\n\tvoid Clear() { m_tri.clear(); }\n\n\t//! Add a facet to the patch\n\tvoid Add(vec3d x[3]) { m_tri.push_back(FACET(x)); }\n\n\t//! retrieve a facet\n\tFACET& Facet(int i) { return m_tri[i]; }\n\n\t//! facet count\n\tint Size() { return (int) m_tri.size(); }\n\n\t//! See if the facet is empty\n\tbool Empty() { return m_tri.empty(); }\n\nprivate:\n\tint\t\tm_primary_facet_id;\t\t//!< index of primary facet\n\tint\t\tm_secondary_facet_id;\t//!< index of secondary facet\n\n\tstd::vector<FACET>\tm_tri;\t//!< triangular patches\n};\n\n//-----------------------------------------------------------------------------\nclass FECORE_API MortarSurface\n{\npublic:\n\tMortarSurface(){}\n\n\tint Patches() { return (int) m_patch.size(); }\n\n\tPatch& GetPatch(int i) { return m_patch[i]; }\n\n\tvoid AddPatch(const Patch& p) { m_patch.push_back(p); }\n\n\tvoid Clear() { m_patch.clear(); }\n\nprivate:\n\tstd::vector<Patch>\tm_patch;\n};\n\n//-----------------------------------------------------------------------------\n// Calculates the intersection between two segments and adds it to the patch\nFECORE_API bool CalculateMortarIntersection(FESurface& ss, FESurface& ms, int k, int l, Patch& patch);\n\n//-----------------------------------------------------------------------------\n// Calculates the mortar intersection between two surfaces\nFECORE_API void CalculateMortarSurface(FESurface& ss, FESurface& ms, MortarSurface& s);\n\n//-----------------------------------------------------------------------------\n// Stores the mortar surface in STL format\nFECORE_API bool ExportMortar(MortarSurface& mortar, const char* szfile);\n"
  },
  {
    "path": "FECore/qsort.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"fecore_api.h\"\n\n#define SWAP(a,b) { itemp = a; a = b; b = itemp; }\n\nFECORE_API void qsort(int n, const int* arr, int* indx)\n{\n\tconst int M = 7;\n\tconst int NSTACK = 50;\n\tint i, indxt, ir=n-1, itemp, j, k, l=0;\n\tint jstack = -1, istack[NSTACK];\n\tint a;\n\n\tfor (j=0; j<n; ++j) indx[j] = j;\n\tfor (;;)\n\t{\n\t\tif (ir-l<M)\n\t\t{\n\t\t\tfor (j=l+1; j<=ir; ++j)\n\t\t\t{\n\t\t\t\tindxt = indx[j];\n\t\t\t\ta = arr[indxt];\n\t\t\t\tfor (i=j-1; i>=l; --i)\n\t\t\t\t{\n\t\t\t\t\tif (arr[indx[i]] <= a) break;\n\t\t\t\t\tindx[i+1] = indx[i];\n\t\t\t\t}\n\t\t\t\tindx[i+1] = indxt;\n\t\t\t}\n\t\t\tif (jstack == -1) break;\n\t\t\tir = istack[jstack--];\n\t\t\tl = istack[jstack--];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tk = ((l+ir+2) >> 1) - 1;\n\t\t\tSWAP(indx[k], indx[l+1]);\n\t\t\tif (arr[indx[l]] > arr[indx[ir]]) SWAP(indx[l], indx[ir]);\n\t\t\tif (arr[indx[l+1]] > arr[indx[ir]]) SWAP(indx[l+1], indx[ir]);\n\t\t\tif (arr[indx[l]] > arr[indx[l+1]]) SWAP(indx[l], indx[l+1]);\n\t\t\ti=l+1;\n\t\t\tj=ir;\n\t\t\tindxt=indx[l+1];\n\t\t\ta = arr[indxt];\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tdo i++; while (arr[indx[i]] < a);\n\t\t\t\tdo j--; while (arr[indx[j]] > a);\n\t\t\t\tif (j<i) break;\n\t\t\t\tSWAP(indx[i], indx[j]);\n\t\t\t}\n\t\t\tindx[l+1] = indx[j];\n\t\t\tindx[j] = indxt;\n\t\t\tjstack += 2;\n//\t\t\tif (jstack < NSTACK) throw \"a fit\";\n\t\t\tif (ir-i+1 >= j-l)\n\t\t\t{\n\t\t\t\tistack[jstack] = ir;\n\t\t\t\tistack[jstack-1] = i;\n\t\t\t\tir = j-1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tistack[jstack] = j-1;\n\t\t\t\tistack[jstack-1] = l;\n\t\t\t\tl=i;\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "FECore/quatd.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"quatd.h\"\n#include <math.h>\n\n//-----------------------------------------------------------------------------\n//! Spherical linear interpolation between two quaternions.\nquatd quatd::slerp(quatd &q1, quatd &q2, const double t) \n{\n\tquatd q3;\n\tdouble dot = quatd::dot(q1, q2);\n\n\t/*\tdot = cos(theta)\n\t\tif (dot < 0), q1 and q2 are more than 90 degrees apart,\n\t\tso we can invert one to reduce spinning\t*/\n\tif (dot < 0)\n\t{\n\t\tdot = -dot;\n\t\tq3 = -q2;\n\t} else q3 = q2;\n\t\t\n\tif (dot < 0.95f)\n\t{\n\t\tdouble angle = acos(dot);\n\t\treturn (q1*sin(angle*(1-t)) + q3*sin(angle*t))/sin(angle);\n\t} else // if the angle is small, use linear interpolation\t\t\t\t\t\t\t\t\n\t\treturn quatd::lerp(q1,q3,t);\n}\n\n//-----------------------------------------------------------------------------\nquatd::quatd(const mat3d & m)\n{\n\t// find max(tr(m), m00,m11,m22)\n\tdouble M = m.trace();\n\tif ((M >= m(0, 0)) && (M >= m(1, 1)) && (M >= m(2, 2)))\n\t{\n\t\tw = 0.5*sqrt(1.0 + M);\n\t\tx = 0.25 * (m(2, 1) - m(1, 2)) / w;\n\t\ty = 0.25 * (m(0, 2) - m(2, 0)) / w;\n\t\tz = 0.25 * (m(1, 0) - m(0, 1)) / w;\n\t}\n\telse\n\t{\n\t\tint i = 0;\n\t\tif (m(1, 1) > m(0, 0)) i = 1;\n\t\tif (m(2, 2) > m(i, i)) i = 2;\n\t\tint j = (i + 1) % 3;\n\t\tint k = (j + 1) % 3;\n\n\t\tdouble q[3];\n\t\tq[i] = sqrt(0.5 * m(i, i) + 0.25 * (1.0 - M));\n\t\tw = 0.25 * (m(k, j) - m(j, k)) / q[i];\n\t\tq[j] = 0.25 * (m(j, i) + m(i, j)) / q[i];\n\t\tq[k] = 0.25 * (m(k, i) + m(i, k)) / q[i];\n\n\t\tx = q[0];\n\t\ty = q[1];\n\t\tz = q[2];\n\t}\n}\n\n// TODO: This looks like it calculates the opposite rotation\n/*\nquatd::quatd(const mat3d& m)\n{\n\tquatd& q = *this;\n\tdouble t;\n\tif (m(2, 2) < 0)\n\t{\n\t\tif (m(0, 0) > m(1, 1))\n\t\t{\n\t\t\tt = 1 + m(0, 0) - m(1, 1) - m(2, 2);\n\t\t\tq = quatd(t, m(0, 1) + m(1, 0), m(2, 0) + m(0, 2), m(1, 2) - m(2, 1));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tt = 1 - m(0, 0) + m(1, 1) - m(2, 2);\n\t\t\tq = quatd(m(0, 1) + m(1, 0), t, m(1, 2) + m(2, 1), m(2, 0) - m(0, 2));\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (m(0, 0) < -m(1, 1))\n\t\t{\n\t\t\tt = 1 - m(0, 0) - m(1, 1) + m(2, 2);\n\t\t\tq = quatd(m(2, 0) + m(0, 2), m(1, 2) + m(2, 1), t, m(0, 1) - m(1, 0));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tt = 1 + m(0, 0) + m(1, 1) + m(2, 2);\n\t\t\tq = quatd(m(1, 2) - m(2, 1), m(2, 0) - m(0, 2), m(0, 1) - m(1, 0), t);\n\t\t}\n\t}\n\n\tdouble s = 0.5 / sqrt(t);\n\tq.x *= s;\n\tq.y *= s;\n\tq.z *= s;\n\tq.w *= s;\n}\n*/\n\n//-----------------------------------------------------------------------------\nvoid quatd::SetEuler(double X, double Y, double Z)\n{\n\t// calculate cos and sin of angles\n\tdouble cz = cos(Z*0.5);\n\tdouble sz = sin(Z*0.5);\n\tdouble cx = cos(X*0.5);\n\tdouble sx = sin(X*0.5);\n\tdouble cy = cos(Y*0.5);\n\tdouble sy = sin(Y*0.5);\n\n\t// define quaternion\n\tw = cz * cx * cy + sz * sx * sy;\n\tx = cz * sx * cy - sz * cx * sy;\n\ty = cz * cx * sy + sz * sx * cy;\n\tz = sz * cx * cy - cz * sx * sy;\n}\n\n//-----------------------------------------------------------------------------\nvoid quatd::GetEuler(double& X, double& Y, double& Z) const\n{\n\t// roll (x-axis rotation)\n\tdouble t0 = +2.0 * (w * x + y * z);\n\tdouble t1 = +1.0 - 2.0 * (x*x + y*y);\n\tX = atan2(t0, t1);\n\n\t// pitch (y-axis rotation)\n\tdouble t2 = +2.0 * (w*y - z*x);\n\tt2 = t2 > 1.0 ? 1.0 : t2;\n\tt2 = t2 < -1.0 ? -1.0 : t2;\n\tY = asin(t2);\n\n\t// yaw (z-axis rotation)\n\tdouble t3 = +2.0 * (w * z + x * y);\n\tdouble t4 = +1.0 - 2.0 * (y*y + z*z);\n\tZ = atan2(t3, t4);\n}\n\n//-----------------------------------------------------------------------------\n// convert euler angles to a rotation matrix\n// l[0] = psi   (x-rotation)\n// l[1] = theta (y-rotation)\n// l[2] = phi   (z-rotation)\nmat3d euler2rot(double l[3])\n{\n\tdouble c0 = cos(l[0]), s0 = sin(l[0]);\n\tdouble c1 = cos(l[1]), s1 = sin(l[1]);\n\tdouble c2 = cos(l[2]), s2 = sin(l[2]);\n\tmat3d Rx(1.0, 0.0, 0.0, 0.0, c0, -s0, 0.0, s0, c0);\n\tmat3d Ry(c1, 0.0, s1, 0.0, 1.0, 0.0, -s1, 0.0, c1);\n\tmat3d Rz(c2, -s2, 0.0, s2, c2, 0.0, 0.0, 0.0, 1.0);\n\treturn Rz*Ry*Rx;\n}\n\n//-----------------------------------------------------------------------------\n// convert a rotation matrix to euler angles\n// l[0] = psi   (x-rotation)\n// l[1] = theta (y-rotation)\n// l[2] = phi   (z-rotation)\nvoid rot2euler(const mat3d& m, double l[3])\n{\n\tconst double e = 1e-12;\n\tif (fabs(m(2,0) - 1.0) < e)\n\t{\n\t\tif (m(2,0) < 0.0)\n\t\t{\n\t\t\tl[2] = 0.0;\n\t\t\tl[1] = PI/2.0;\n\t\t\tl[0] = atan2(m(0,1),m(0,2));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tl[2] = 0.0;\n\t\t\tl[1] = -PI/2.0;\n\t\t\tl[0] = atan2(-m(0,1),-m(0,2));\n\t\t}\n\t}\n\telse\n\t{\n\t\tl[1] = -asin(m(2,0));\n\t\tdouble c1 = cos(l[1]);\n\t\tl[0] = atan2(m(2,1)/c1, m(2,2)/c1);\n\t\tl[2] = atan2(m(1,0)/c1, m(0,0)/c1);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// convert a quaternion to Euler angles\nvoid quat2euler(const quatd& q, double l[3])\n{\n\tmat3d Q = q.RotationMatrix();\n\trot2euler(Q, l);\n}\n"
  },
  {
    "path": "FECore/quatd.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"vec3d.h\"\n#include \"mat3d.h\"\n#include \"fecore_api.h\"\n\n//-----------------------------------------------------------------------------\n//! This class implements a quaternion. \n\nclass FECORE_API quatd\n{\npublic:\n\t//! Default constructor - creates identity quaternion (0, 0, 0, 1)\n\tquatd() { x = y = z = 0.0;  w = 1.0; }\n\n\t//! Constructor from rotation angle and axis vector\n\tquatd( const double angle, vec3d v)\n\t{\n\t\tw = (double) cos(angle * 0.5);\n\n\t\tdouble sina = (double) sin(angle * 0.5);\n\n\t\tv.unit();\n\t\t\n\t\tx = v.x*sina;\n\t\ty = v.y*sina;\n\t\tz = v.z*sina;\n\t}\n\n\t//! Constructor from rotation vector (magnitude is angle, direction is axis)\n\tquatd(vec3d v)\n\t{\n        double angle = v.unit();\n        \n\t\tw = (double) cos(angle * 0.5);\n        \n\t\tdouble sina = (double) sin(angle * 0.5);\n        \n\t\tx = v.x*sina;\n\t\ty = v.y*sina;\n\t\tz = v.z*sina;\n\t}\n    \n\t//! Constructor from two vectors - creates rotation from v1 to v2\n\tquatd (const vec3d& v1, const vec3d& v2)\n\t{\n\t\tvec3d n = v1^v2;\n\t\tn.unit();\n\n\t\tdouble d = v1*v2;\n\n\t\tdouble sina = (double) sqrt((1.0-d)*0.5);\n\t\tdouble cosa = (double) sqrt((1.0+d)*0.5);\n\n\t\tw = cosa;\n\n\t\tx = n.x*sina;\n\t\ty = n.y*sina;\n\t\tz = n.z*sina;\n\n\t}\n\n\t//! Constructor from four components\n\tquatd(const double qx, const double qy, const double qz, const double qw = 1.0)\n\t{\n\t\tw = qw;\n\t\tx = qx;\n\t\ty = qy;\n\t\tz = qz;\n\t}\n\n\t//! Constructor from rotation matrix\n\tquatd(const mat3d& a);\n\n\t//! Inequality comparison operator\n\tbool operator != (const quatd& q) const { return ((x!=q.x) || (y!=q.y) || (z!=q.z) || (w!=q.w)); }\n\n\t//! Equality comparison operator\n\tbool operator == (const quatd& q) const { return ((x == q.x) && (y == q.y) && (z == q.z) && (w == q.w)); }\n\n\t//! Unary negation operator\n\tquatd operator - () { return quatd(-x, -y, -z, -w); }\n\n\t//! Quaternion addition operator\n\tquatd operator + (const quatd& q) const\n\t{\n\t\treturn quatd(x + q.x, y + q.y, z + q.z, w + q.w);\n\t}\n\n\t//! Quaternion subtraction operator\n\tquatd operator - (const quatd& q) const\n\t{\n\t\treturn quatd(x - q.x, y - q.y, z - q.z, w - q.w);\n\t}\n\n\t//! Quaternion addition assignment operator\n\tquatd& operator += (const quatd& q)\n\t{\n\t\tx += q.x;\n\t\ty += q.y;\n\t\tz += q.z;\n\t\tw += q.w;\n\n\t\treturn *this;\n\t}\n\n\t//! Quaternion subtraction assignment operator\n\tquatd& operator -= (const quatd& q)\n\t{\n\t\tx -= q.x;\n\t\ty -= q.y;\n\t\tz -= q.z;\n\t\tw -= q.w;\n\n\t\treturn *this;\n\t}\n\n\t//! Quaternion multiplication operator\n\tquatd operator * (const quatd& q) const\n\t{\n\t\tdouble qw = w*q.w - x*q.x - y*q.y - z*q.z;\n\t\tdouble qx = w*q.x + x*q.w + y*q.z - z*q.y;\n\t\tdouble qy = w*q.y + y*q.w + z*q.x - x*q.z;\n\t\tdouble qz = w*q.z + z*q.w + x*q.y - y*q.x;\n\n\t\treturn quatd(qx, qy, qz, qw);\n\t}\n\n\t//! Quaternion multiplication assignment operator\n\tquatd& operator *= (const quatd& q)\n\t{\n\t\tdouble qw = w*q.w - x*q.x - y*q.y - z*q.z;\n\t\tdouble qx = w*q.x + x*q.w + y*q.z - z*q.y;\n\t\tdouble qy = w*q.y + y*q.w + z*q.x - x*q.z;\n\t\tdouble qz = w*q.z + z*q.w + x*q.y - y*q.x;\n\n\t\tx = qx;\n\t\ty = qy;\n\t\tz = qz;\n\t\tw = qw;\n\n\t\treturn *this;\n\t}\n\n\t//! Scalar multiplication operator\n\tquatd operator*(const double a) const\n\t{\n\t\treturn quatd(x*a, y*a, z*a, w*a);\n\t}\n\n\t//! Scalar division operator\n\tquatd operator / (const double a) const\n\t{\n\t\treturn quatd(x/a, y/a, z/a, w/a);\n\t}\n\n\t//! Scalar division assignment operator\n\tquatd& operator /= (const double a)\n\t{\n\t\tx /= a;\n\t\ty /= a;\n\t\tz /= a;\n\t\tw /= a;\n\n\t\treturn *this;\n\t}\n\n\t//! Return the conjugate of the quaternion\n\tquatd Conjugate() const { return quatd(-x, -y, -z, w); }\n\n\t//! Return the norm (squared magnitude) of the quaternion\n\tdouble Norm() const { return w*w + x*x + y*y + z*z; } \n\n\t//! Normalize the quaternion to unit length\n\tvoid MakeUnit() \n\t{\n\t\tdouble N = (double) sqrt(w*w + x*x + y*y + z*z);\n\n\t\tif (N != 0)\n\t\t{\n\t\t\tx /= N;\n\t\t\ty /= N;\n\t\t\tz /= N;\n\t\t\tw /= N;\n\t\t}\n\t\telse w = 1.f;\n\t}\n\n\t//! Return the inverse of the quaternion\n\tquatd Inverse() const\n\t{\n\t\tdouble N = w*w + x*x + y*y + z*z;\n\t\tif (N == 0.0) return quatd(x, y, z, w);\n\t\telse return quatd(-x/N, -y/N, -z/N, w/N);\n\t}\n\n\t//! Calculate dot product with another quaternion\n\tdouble DotProduct(const quatd& q) const\n\t{\n\t\treturn w*q.w + x*q.x + y*q.y + z*q.z;\n\t}\n\n\t//! Get the normalized rotation axis vector\n\tvec3d GetVector() const\n\t{\n\t\tvec3d r(x,y,z);\n\t\tr.unit();\n\t\treturn r;\n\t}\n\n\t//! Get the rotation vector (axis scaled by angle)\n\tvec3d GetRotationVector() const\n\t{\n\t\tvec3d r(x,y,z);\n\t\tr.unit();\n\t\tdouble a = GetAngle();\n\t\treturn r*a;\n\t}\n\n\t//! Get the rotation angle in radians\n\tdouble GetAngle() const\n\t{\n        vec3d r(x,y,z);\n        double sha = r.unit();\n        double cha = w;\n\t\treturn (double)(atan2(sha,cha)*2.0);\n\t}\n\n\t//! Rotate a vector using this quaternion (in-place, for unit quaternions only)\n\tvoid RotateVector(vec3d& v) const\n\t{\n\t\tif ((w == 0) || ((x==0) && (y==0) && (z==0))) return;\n\n\t\t// v*q^-1\n\t\tdouble qw = v.x*x + v.y*y + v.z*z;\n\t\tdouble qx = v.x*w - v.y*z + v.z*y;\n\t\tdouble qy = v.y*w - v.z*x + v.x*z;\n\t\tdouble qz = v.z*w - v.x*y + v.y*x;\n\n\t\t// q* (v* q^-1)\n\t\tv.x = w*qx + x*qw + y*qz - z*qy;\n\t\tv.y = w*qy + y*qw + z*qx - x*qz;\n\t\tv.z = w*qz + z*qw + x*qy - y*qx;\n\t}\n\n\t//! Vector rotation operator (for unit quaternions only)\n\tvec3d operator * (const vec3d& r) const\n\t{\n\t\tvec3d n = r;\n\n\t\t// v*q^-1\n\t\tdouble qw = n.x*x + n.y*y + n.z*z;\n\t\tdouble qx = n.x*w - n.y*z + n.z*y;\n\t\tdouble qy = n.y*w - n.z*x + n.x*z;\n\t\tdouble qz = n.z*w - n.x*y + n.y*x;\n\n\t\t// q* (v* q^-1)\n\t\tn.x = w*qx + x*qw + y*qz - z*qy;\n\t\tn.y = w*qy + y*qw + z*qx - x*qz;\n\t\tn.z = w*qz + z*qw + x*qy - y*qx;\n\n\t\treturn n;\n\t}\n\n\t//! Rotate vector using raw pointer arrays\n\tvoid RotateVectorP(double* v, double* r) const\n\t{\n\t\tdouble qw = v[0]*x + v[1]*y + v[2]*z;\n\t\tdouble qx = v[0]*w - v[1]*z + v[2]*y;\n\t\tdouble qy = v[1]*w - v[2]*x + v[0]*z;\n\t\tdouble qz = v[2]*w - v[0]*y + v[1]*x;\n\n\t\tr[0] = w*qx + x*qw + y*qz - z*qy;\n\t\tr[1] = w*qy + y*qw + z*qx - x*qz;\n\t\tr[2] = w*qz + z*qw + x*qy - y*qx;\n\t}\n\n\t//! Convert a quaternion to a rotation matrix\n\tmat3d RotationMatrix() const\n\t{\n\t\treturn mat3d(\n\t\t\tw*w + x*x - y*y - z*z,\n\t\t\t2.0*(x*y - w*z),\n\t\t\t2.0*(x*z + w*y),\n\t\t\t2.0*(x*y + w*z),\n\t\t\tw*w - x*x + y*y - z*z,\n\t\t\t2.0*(y*z - w*x),\n\t\t\t2.0*(x*z - w*y),\n\t\t\t2.0*(y*z + w*x),\n\t\t\tw*w - x*x - y*y + z*z);\n\t}\n\n\t//! Calculate dot product between two quaternions\n\tstatic double dot(quatd &q1, quatd &q2) \n\t{ return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; }\n\n\t//! Linear interpolation between two quaternions\n\tstatic quatd lerp(quatd &q1, quatd &q2, const double t) \n\t{ quatd q = (q1*(1.0-t) + q2*t); q.MakeUnit(); return q; }\n\n\t//! Spherical linear interpolation between two quaternions\n\tstatic quatd slerp(quatd &q1, quatd &q2, const double t);\n\n\t//! Set quaternion from XYZ Euler angles (roll, pitch, yaw in radians)\n\tvoid SetEuler(double x, double y, double z);\n\t//! Get XYZ Euler angles from quaternion (roll, pitch, yaw in radians)\n\tvoid GetEuler(double& x, double& y, double& z) const;\n\npublic:\n\t//! X component of quaternion\n\tdouble x, y, z, w;\n};\n\n//! Scalar multiplication operator (scalar * quaternion)\ninline quatd operator * (const double a, const quatd& q)\n{\n\treturn q*a;\n}\n\n//! Convert euler-angles to a rotation matrix\nFECORE_API mat3d euler2rot(double l[3]);\n\n//! Convert a rotation matrix to euler angles\nFECORE_API void rot2euler(const mat3d& m, double l[3]);\n\n//! Extract euler angles from a quaternion\nFECORE_API void quat2euler(const quatd& q, double l[3]);"
  },
  {
    "path": "FECore/sdk.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FECoreKernel.h\"\n\n//------------------------------------------------------------------------------\n// This macro should be used to start the definition of the plugin classes\n#define BEGIN_PLUGIN_DEFINITION\t\tstatic std::vector<FECoreFactory*>\t_facs; \\\nFECORE_API void PluginInitialize(FECoreKernel& febio) { FECoreKernel::SetInstance(&febio);\n\n//------------------------------------------------------------------------------\n// Use this macro to define each new plugin class\n#define REGISTER_PLUGIN(theClass, theID, theName)  \\\n\t_facs.push_back(new FEPluginFactory_T<theClass, theID>(theName));\n\n//------------------------------------------------------------------------------\n// This macro ends the plugin definition section\n#define END_PLUGIN_DEFINITION\n\n#ifdef WIN32\n\t#define FECORE_PLUGIN extern \"C\" __declspec(dllexport)\n#else\n\t#define FECORE_PLUGIN extern \"C\"\n#endif\n"
  },
  {
    "path": "FECore/stdafx.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n\n// TODO: reference any additional headers you need in STDAFX.H\n// and not in this file\n"
  },
  {
    "path": "FECore/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#ifdef WIN32\n#include \"targetver.h\"\n\n#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\n\n#endif\n\n// TODO: reference additional headers your program requires here\n"
  },
  {
    "path": "FECore/svd.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"matrix.h\"\n#include <math.h>\nusing namespace std;\n\n#define SQR(a) ((a)*(a))\n#define FMAX(a, b) ((a)>(b)?(a):(b))\n#define IMIN(a, b) ((a)<(b)?(a):(b))\n#define SIGN(a, b) ((b) > 0.0 ? fabs(a) : -fabs(a))\n\nvoid svbksb(matrix& u, vector<double>& w, matrix& v, vector<double>& b, vector<double>& x)\n{\n\tint jj, j, i;\n\tdouble s;\n\n\tint m = u.rows();\n\tint n = u.columns();\n\n\tvector<double> tmp(n);\n\tfor (j=0; j<n; ++j)\n\t{\n\t\ts = 0.0;\n\t\tif (w[j])\n\t\t{\n\t\t\tfor (i=0; i<m; ++i) s += u[i][j]*b[i];\n\t\t\ts /= w[j];\n\t\t}\n\t\ttmp[j] = s;\n\t}\n\tfor (j=0; j<n; ++j)\n\t{\n\t\ts = 0.0;\n\t\tfor (jj=0; jj<n; ++jj) s += v[j][jj]*tmp[jj];\n\t\tx[j] = s;\n\t}\n}\n\ndouble pythag(double a, double b)\n{\n\tdouble absa, absb;\n\tabsa=fabs(a);\n\tabsb=fabs(b);\n\tif (absa > absb) return absa*sqrt(1.0 + SQR(absb/absa));\n\telse return (absb == 0.0 ? 0.0 : absb*sqrt(1.0 + SQR(absa/absb)));\n}\n\nvoid svdcmp(matrix& a, vector<double>& w, matrix& v)\n{\n\tint flag, i, its, j, jj, k, l, nm;\n\tdouble anorm, c, f, g, h, s, scale, x, y, z;\n\n\tint m = a.rows();\n\tint n = a.columns();\n\n\tvector<double> rv1(n);\n\tg = scale = anorm = 0.0;\n\tfor (i=0; i<n; ++i)\n\t{\n\t\tl=i+1;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// diff between c++/c NR\n\t\trv1[i] = scale*g;\n\t\tg = s = scale = 0.0;\n\t\tif (i < m)\n\t\t{\n\t\t\tfor (k=i; k<m; ++k) scale += fabs(a[k][i]);\n\t\t\tif (scale != 0.0)\n\t\t\t{\n\t\t\t\tfor (k=i; k<m; ++k) \n\t\t\t\t{\n\t\t\t\t\ta[k][i] /= scale;\n\t\t\t\t\ts += a[k][i]*a[k][i];\n\t\t\t\t}\n\t\t\t\tf = a[i][i];\n\t\t\t\tg = -SIGN(sqrt(s), f);\n\t\t\t\th = f*g-s;\n\t\t\t\ta[i][i] = f-g;\n\t\t\t\tfor (j=l; j<n; ++j)\t\t\t\t\t\t\t\t\t\t// diff between c++/c NR\n\t\t\t\t{\n\t\t\t\t\tfor (s=0.0, k=i; k<m; k++) s += a[k][i]*a[k][j];\n\t\t\t\t\tf = s/h;\n\t\t\t\t\tfor (k=i; k<m; ++k) a[k][j] += f*a[k][i];\n\t\t\t\t}\n\t\t\t\tfor (k=i; k<m; k++) a[k][i] *= scale;\n\t\t\t}\n\t\t}\n\t\tw[i] = scale*g;\n\t\tg=s=scale = 0.0;\n\t\tif ((i+1<=m) && (i!=n-1))\t\t\t\t\t\t\t\t\t// diff between c++/c NR\n\t\t{\n\t\t\tfor (k=l; k<n; ++k) scale += fabs(a[i][k]);\t\t// diff between c++/c NR\n\t\t\tif (scale != 0.0)\n\t\t\t{\n\t\t\t\tfor (k=l; k<n; ++k)\t\t\t\t\t\t\t// diff between c++/c NR\n\t\t\t\t{\n\t\t\t\t\ta[i][k] /= scale;\n\t\t\t\t\ts += a[i][k]*a[i][k];\n\t\t\t\t}\n\t\t\t\tf = a[i][l];\t\t\t\t\t\t\t\t\t// diff between c++/c NR\n\t\t\t\tg = -SIGN(sqrt(s), f);\n\t\t\t\th=f*g-s;\n\t\t\t\ta[i][l] = f - g;\n\t\t\t\tfor (k=l; k<n; k++) rv1[k] = a[i][k]/h;\t\t// diff between c++/c NR\n\t\t\t\tfor (j=l; j<m; ++j)\t\t\t\t\t\t\t// diff between c++/c NR\n\t\t\t\t{\n\t\t\t\t\tfor (s=0.0, k=l; k<n; k++) s += a[j][k]*a[i][k];\t\t// diff between c++/c NR\n\t\t\t\t\tfor (k=l; k<n; k++) a[j][k] += s*rv1[k];\t\t\t\t// diff between c++/c NR\n\t\t\t\t}\n\t\t\t\tfor (k=l; k<n; k++) a[i][k] *= scale;\t\t\t// diff between c++/c NR\n\t\t\t}\n\t\t}\n\t\tanorm = FMAX(anorm, (fabs(w[i])+fabs(rv1[i])));\n\t}\n\tfor (i=n-1; i>=0; --i) \n\t{\n\t\tif (i<n-1)\n\t\t{\n\t\t\tif (g != 0.0)\n\t\t\t{\n\t\t\t\tfor (j=l; j<n; ++j)\n\t\t\t\t\tv[j][i] = (a[i][j]/a[i][l])/g;\n\t\t\t\tfor (j=l; j<n; ++j)\n\t\t\t\t{\n\t\t\t\t\tfor (s=0.0, k=l; k<n; ++k) s += a[i][k]*v[k][j];\n\t\t\t\t\tfor (k=l; k<n; k++) v[k][j] += s*v[k][i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (j=l; j<n; j++) v[i][j] = v[j][i] = 0.0;\n\t\t}\n\t\tv[i][i] = 1.0;\n\t\tg = rv1[i];\n\t\tl = i;\n\t}\n\tfor (i=IMIN(m,n)-1; i>=0; i--)\n\t{\n\t\tl=i+1;\n\t\tg=w[i];\n\t\tfor (j=l; j<n; ++j) a[i][j] = 0.0;\n\t\tif (g != 0.0)\n\t\t{\n\t\t\tg = 1.0/g;\n\t\t\tfor (j=l; j<n; ++j)\n\t\t\t{\n\t\t\t\tfor (s=0.0, k=l; k<m; k++) s += a[k][i]*a[k][j];\n\t\t\t\tf = (s/a[i][i])*g;\n\t\t\t\tfor (k=i; k<m; k++) a[k][j] += f*a[k][i];\n\t\t\t}\n\t\t\tfor (j=i; j<m; j++) a[j][i] *= g;\n\t\t}\n\t\telse for (j=i; j<m; j++) a[j][i] = 0.0;\n\t\t++a[i][i];\n\t}\n\tfor (k=n-1; k>=0; k--)\n\t{\n\t\tfor (its=0; its<30; ++its)\n\t\t{\n\t\t\tflag = 1;\n\t\t\tfor (l=k; l>=0; --l)\n\t\t\t{\n\t\t\t\tnm = l-1;\n\t\t\t\tif ((fabs(rv1[l])+anorm) == anorm)\n\t\t\t\t{\n\t\t\t\t\tflag = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ((fabs(w[nm])+anorm) == anorm) break;\n\t\t\t}\n\t\t\tif (flag == 1)\n\t\t\t{\n\t\t\t\tc = 0.0;\n\t\t\t\ts = 1.0;\n\t\t\t\tfor (i=l; i<=k; ++i)\t\t// diff between c++/c NR\n\t\t\t\t{\n\t\t\t\t\tf = s*rv1[i];\n\t\t\t\t\trv1[i] = c*rv1[i];\n\t\t\t\t\tif ((fabs(f)+anorm) == anorm) break;\n\t\t\t\t\tg = w[i];\n\t\t\t\t\th = pythag(f, g);\n\t\t\t\t\tw[i] = h;\n\t\t\t\t\th = 1.0/h;\n\t\t\t\t\tc = g*h;\n\t\t\t\t\ts= -f*h;\n\t\t\t\t\tfor (j=0; j<m; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\ty=a[j][nm];\n\t\t\t\t\t\tz=a[j][i];\n\t\t\t\t\t\ta[j][nm] = y*c+z*s;\n\t\t\t\t\t\ta[j][i] = z*c - y*s;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tz = w[k];\n\t\t\tif (l == k)\n\t\t\t{\n\t\t\t\tif (z < 0.0)\n\t\t\t\t{\n\t\t\t\t\tw[k] = -z;\n\t\t\t\t\tfor (j=0; j<n; j++) v[j][k] = -v[j][k];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// TODO: do something drastic when its hits 29\n\t\t\tx=w[l];\n\t\t\tnm=k-1;\n\t\t\ty=w[nm];\n\t\t\tg=rv1[nm];\n\t\t\th=rv1[k];\n\t\t\tf=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);\n\t\t\tg = pythag(f, 1.0);\n\t\t\tf=((x-z)*(x+z)+h*((y/(f+SIGN(g, f)))-h)) / x;\n\t\t\tc = s = 1.0;\n\t\t\tfor (j=l; j<=nm; j++)\n\t\t\t{\n\t\t\t\ti = j+1;\n\t\t\t\tg = rv1[i];\n\t\t\t\ty = w[i];\n\t\t\t\th = s*g;\n\t\t\t\tg = c*g;\n\t\t\t\tz = pythag(f, h);\n\t\t\t\trv1[j] = z;\n\t\t\t\tc = f/z;\n\t\t\t\ts = h/z;\n\t\t\t\tf = x*c + g*s;\n\t\t\t\tg = g*c - x*s;\n\t\t\t\th = y*s;\n\t\t\t\ty *= c;\n\t\t\t\tfor (jj=0; jj<n; ++jj)\n\t\t\t\t{\n\t\t\t\t\tx=v[jj][j];\n\t\t\t\t\tz=v[jj][i];\n\t\t\t\t\tv[jj][j] = x*c + z*s;\n\t\t\t\t\tv[jj][i] = z*c - x*s;\n\t\t\t\t}\n\t\t\t\tz = pythag(f, h);\n\t\t\t\tw[j] = z;\n\t\t\t\tif (z)\n\t\t\t\t{\n\t\t\t\t\tz = 1.0/z;\n\t\t\t\t\tc = f*z;\n\t\t\t\t\ts = h*z;\n\t\t\t\t}\n\t\t\t\tf = c*g + s*y;\n\t\t\t\tx = c*y - s*g;\n\t\t\t\tfor (jj=0; jj<m; ++jj)\n\t\t\t\t{\n\t\t\t\t\ty = a[jj][j];\n\t\t\t\t\tz = a[jj][i];\n\t\t\t\t\ta[jj][j] = y*c + z*s;\n\t\t\t\t\ta[jj][i] = z*c - y*s;\n\t\t\t\t}\n\t\t\t}\n\t\t\trv1[l] = 0.0;\n\t\t\trv1[k] = f;\n\t\t\tw[k] = x;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FECore/sys.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n#include <float.h>\n#define ISNAN(x) _isnan(x)\n#endif\n\n#ifdef LINUX\n\t#ifdef CENTOS\n\t\t#define ISNAN(x) isnan(x)\n\t#else\n\t\t#define ISNAN(x) std::isnan(x)\n\t#endif\n#endif\n\n#ifdef __APPLE__\n#include <math.h>\n#define ISNAN(x) isnan(x)\n#endif\n\n#ifdef WIN32\nextern \"C\" int __cdecl omp_get_num_threads(void);\nextern \"C\" int __cdecl omp_get_thread_num(void);\n#else\nextern \"C\" int omp_get_num_threads(void);\nextern \"C\" int omp_get_thread_num(void);\n#endif\n"
  },
  {
    "path": "FECore/table.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <vector>\n\n// template class for storing 2D data\ntemplate <typename T> class table\n{\npublic:\n\t// constructors\n\ttable() : m_rows(0), m_cols(0) {}\n\ttable(int nrows, int ncols) : m_rows(0), m_cols(0) { resize(nrows, ncols); }\n\ttable(const table& t) { m_data = t.m_data; m_rows = t.m_rows; m_cols = t.m_cols; }\n\n\t// assignment operator\n\ttable& operator = (const table& t) { m_data = t.m_data; m_rows = t.m_rows; m_cols = t.m_cols; return (*this); }\n\n\t// resize table\n\tvoid resize(int nrows, int ncols, T def = T(0))\n\t{\n\t\tstd::vector<T> tmp(m_data);\n\t\tm_data.assign(nrows*ncols, def);\n\n\t\tint nr = (nrows < m_rows ? nrows : m_rows);\n\t\tint nc = (ncols < m_cols ? ncols : m_cols);\n\t\tfor (int i=0; i<nr; ++i)\n\t\t{\n\t\t\tfor (int j=0; j<nc; ++j) m_data[i*ncols + j] = tmp[i*m_cols + j];\n\t\t}\n\n\t\tm_rows = nrows;\n\t\tm_cols = ncols;\n\t}\n\n\t// assign a value to the entire table\n\tvoid set(const T& v) \n\t{ \n\t\tif (m_data.empty() == false) \n\t\t\tm_data.assign(m_rows*m_cols, v); \n\t}\n\n\t// get sizes\n\tint rows() const { return m_rows; }\n\tint columns() const { return m_cols; }\n\n\t// access operator\n\tconst T& operator () (int i, int j) const { return m_data[i*m_cols + j]; }\n\tT& operator () (int i, int j) { return m_data[i*m_cols + j]; }\n\nprivate:\n\tstd::vector<T>\tm_data;\n\tint\t\t\t\tm_rows, m_cols;\n};\n"
  },
  {
    "path": "FECore/targetver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// The following macros define the minimum required platform.  The minimum required platform\n// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run \n// your application.  The macros work by enabling all features available on platform versions up to and \n// including the version specified.\n\n// Modify the following defines if you have to target a platform prior to the ones specified below.\n// Refer to MSDN for the latest info on corresponding values for different platforms.\n#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows Vista.\n#define _WIN32_WINNT 0x0600     // Change this to the appropriate value to target other versions of Windows.\n#endif\n\n"
  },
  {
    "path": "FECore/tens3d.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"tens3d.h\"\n"
  },
  {
    "path": "FECore/tens3d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"mat3d.h\"\n#include \"tensor_base.h\"\n\n//-----------------------------------------------------------------------------\n// The following classes are defined in this file\nclass tens3ds;\t// symmetric 3o tensor\nclass tens3drs;\t// right-conjugate symmetric 3o tensor\nclass tens3dls;\t// left-conjugate symmetric 3o tensor\nclass tens3d;\t// general 3o tensor (no symmetry)\n\n//-----------------------------------------------------------------------------\n// traits for these classes defining the number of components\ntemplate <> class tensor_traits<tens3ds > {public: enum { NNZ = 10}; };\ntemplate <> class tensor_traits<tens3drs> {public: enum { NNZ = 18}; };\ntemplate <> class tensor_traits<tens3dls> {public: enum { NNZ = 18}; };\ntemplate <> class tensor_traits<tens3d  > {public: enum { NNZ = 27}; };\n\n//-----------------------------------------------------------------------------\n//! Class for 3rd order tensor with full symmetry Tijk = Tjik = Tkji = Tikj = Tkij = Tjki (only 10 out of 27 components are unique)\n\n// We store this tensor as a 1x10 array.\n// [T] = [T111 T112 T113 T122 T123 T133 T222 T223 T233 T333]\n//     =    T0   T1   T2   T3   T4   T5   T6   T7   T8   T9\n\nclass tens3ds : public tensor_base<tens3ds>\n{\npublic:\n\t// constructors\n\ttens3ds(){}\n\n\t// access operator\n\tdouble operator () (int i, int j, int k) const;\n\n\tvec3d contractdyad1(const vec3d& v);\n\tdouble tripledot(const tens3ds& H);\n};\n\ntens3ds dyad3s(const vec3d& l, const vec3d& r);\n\n//-----------------------------------------------------------------------------\n//! Class for 3rd order tensor with right-conjugate symmetry Gijk = Gikj (only 18 out of 27 components are unique)\n\n// We store this tensor as a 1x18 array.\n// [G] = [G111 G112 G113 G122 G123 G133 G211 G212 G213 G222 G223 G233 G311 G312 G313 G322 G323 G333]\n//     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\n\nclass tens3drs : public tensor_base<tens3drs>\n{\npublic:\n\t// constructors\n\texplicit tens3drs(double a);\n\ttens3drs(){}\n\n\t// access operator\n\tdouble  operator () (int i, int j, int k) const;\n\tdouble& operator () (int i, int j, int k);\n\n\tvec3d contractdyad1(const vec3d& v) const;\n\tvec3d contract2s(const mat3ds& s) const;\n\tdouble tripledot(const tens3drs& H) const;\n\tvec3d contractdyad2(const vec3d& v, const vec3d& w);\n\ttens3dls transpose();\n\tvoid contractleg2(const mat3d& F, int leg);\n};\n\ntens3drs operator * (const mat3d& F, const tens3drs& t);\ntens3drs dyad3rs(const vec3d& l, const vec3d& r);\ntens3drs dyad3rs(const mat3d& L, const vec3d& r);\n\n//-----------------------------------------------------------------------------\n//! Class for 3rd order tensor with left-conjugate symmetry Gijk = Gjik (only 18 out of 27 components are unique)\n\n// We store this tensor as a 1x18 array.\n// [G] = [G111 G112 G113 G121 G122 G123 G131 G132 G133 G221 G222 G223 G231 G232 G233 G331 G332 G333]\n//     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\n\nclass tens3dls : public tensor_base<tens3dls>\n{\npublic:\n\t// constructors\n\ttens3dls(){}\n\n\ttens3dls operator * (const mat3d& F) const;\n    tens3dls operator * (const double& f) const;\n\n\t// transpose\n\ttens3drs transpose();\n    vec3d trace();\n    tens3d generalize();\n};\n\ntens3dls dyad3ls(const mat3ds& L, const vec3d& r);\n\n//-----------------------------------------------------------------------------\n//! Class for 3rd order tensor with no symmetry (27 components)\n\n// Due to symmetry we can store this tensor as a 1x27 array.\n// [T] = [T111 T112 T113 T121 T122 T123 T131 T132 T133 T211 T212 T213 T221 T222 T223 T231 T232 T233 T311 T312 T313 T321 T322 T323 T331 T332 T333\n//     =    T0   T1   T2   T3   T4   T5   T6   T7   T8   T9  T10  T11  T12  T13  T14  T15  T16  T17  T18  T19  T20  T21  T22  T23  T24  T25  T26\n\nclass tens3d : public tensor_base<tens3d>\n{\npublic:\n\t// constructors\n\ttens3d(){}\n    explicit tens3d(double a);\n\n\t// access operators\n\tdouble operator () (int i, int j, int k) const;\n\tdouble& operator () (int i, int j, int k);\n\n\t// return symmetric tens3ds\n\ttens3ds symm();\n    \n    // right transpose\n    tens3d transposer();\n    \n    //Contract by 2nd order tensor\n    vec3d contract2(const mat3d& s) const;\n\n    //Contract on right by vector\n    mat3d contract1(const vec3d& v) const;\n};\n\ntens3d operator + (const tens3dls& l, const tens3drs& r);\ninline tens3d operator + (const tens3drs& r, const tens3dls& l) { return l+r; }\n\n// The following file contains the actual definition of the class functions\n#include \"tens3ds.hpp\"\n#include \"tens3drs.hpp\"\n#include \"tens3dls.hpp\"\n#include \"tens3d.hpp\"\n"
  },
  {
    "path": "FECore/tens3d.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens3drs.h\n// Users should not include this file manually!\n\n// access operator\ninline double tens3d::operator()(int i, int j, int k) const\n{\n\treturn d[i*9 + j*3 + k];\n}\n\n// access operator\ninline double& tens3d::operator()(int i, int j, int k)\n{\n\treturn d[i*9 + j*3 + k];\n}\n\ninline tens3d::tens3d(double a)\n{\n    int nnz = tensor_traits<tens3d>::NNZ;\n    for (int i = 0; i < nnz; ++i) d[i] = a;\n}\n\n// symmetrize a general 3o tensor\ninline tens3ds tens3d::symm()\n{\n\ttens3ds t;\n\n\tt.d[0] =  d[0]; \n\tt.d[1] = (d[1] + d[3]  + d[9])/3.; \n\tt.d[2] = (d[2] + d[6]  + d[18])/3.;\n\tt.d[3] = (d[4] + d[10] + d[12])/3.; \n\tt.d[4] = (d[5] + d[11] + d[21] + d[7] + d[19] + d[15])/6.; \n\tt.d[5] = (d[8] + d[20] + d[24])/3.;\n\tt.d[6] =  d[13]; \n\tt.d[7] = (d[14] + d[16] + d[22])/3.;\n\tt.d[8] = (d[17] + d[23] + d[25])/3.;\n\tt.d[9] =  d[26]; \n\n\treturn t;\n}\n\n// return transpose of right side\n// [T] = [T111 T112 T113 T121 T122 T123 T131 T132 T133 T211 T212 T213 T221 T222 T223 T231 T232 T233 T311 T312 T313 T321 T322 T323 T331 T332 T333\n//     =    T0   T1   T2   T3   T4   T5   T6   T7   T8   T9  T10  T11  T12  T13  T14  T15  T16  T17  T18  T19  T20  T21  T22  T23  T24  T25  T26\ninline tens3d tens3d::transposer()\n{\n    tens3d s;\n    s.d[ 0] = d[ 0];\n    s.d[ 1] = d[ 3];\n    s.d[ 2] = d[ 6];\n    s.d[ 3] = d[ 1];\n    s.d[ 4] = d[ 4];\n    s.d[ 5] = d[ 7];\n    s.d[ 6] = d[ 2];\n    s.d[ 7] = d[ 5];\n    s.d[ 8] = d[ 8];\n    s.d[ 9] = d[ 9];\n    s.d[10] = d[12];\n    s.d[11] = d[15];\n    s.d[12] = d[10];\n    s.d[13] = d[13];\n    s.d[14] = d[16];\n    s.d[15] = d[11];\n    s.d[16] = d[14];\n    s.d[17] = d[17];\n    s.d[18] = d[18];\n    s.d[19] = d[21];\n    s.d[20] = d[24];\n    s.d[21] = d[19];\n    s.d[22] = d[22];\n    s.d[23] = d[25];\n    s.d[24] = d[20];\n    s.d[25] = d[23];\n    s.d[26] = d[26];\n    return s;\n}\n\n// contract the right two legs by a 2o tensor  xi = Gijk*Sjk\n// [T] = [T111 T112 T113 T121 T122 T123 T131 T132 T133 T211 T212 T213 T221 T222 T223 T231 T232 T233 T311 T312 T313 T321 T322 T323 T331 T332 T333\n//     =    T0   T1   T2   T3   T4   T5   T6   T7   T8   T9  T10  T11  T12  T13  T14  T15  T16  T17  T18  T19  T20  T21  T22  T23  T24  T25  T26\ninline vec3d tens3d::contract2(const mat3d& s) const\n{\n    vec3d x;\n    x.x = d[0]*s[0][0] + d[1]*s[0][1] + d[2]*s[0][2] + d[3]*s[1][0] + d[4]*s[1][1] + d[5]*s[1][2] + d[6]*s[2][0] + d[7]*s[2][1] + d[8]*s[2][2];\n    x.y = d[9]*s[0][0] + d[10]*s[0][1] + d[11]*s[0][2] + d[12]*s[1][0] + d[13]*s[1][1] + d[14]*s[1][2] + d[15]*s[2][0] + d[16]*s[2][1] + d[17]*s[2][2];\n    x.z = d[18]*s[0][0] + d[19]*s[0][1] + d[20]*s[0][2] + d[21]*s[1][0] + d[22]*s[1][1] + d[23]*s[1][2] + d[24]*s[2][0] + d[25]*s[2][1] + d[26]*s[2][2];\n    \n    return x;\n}\n\n// contract the right leg by a vector  xij = Gijk*vk\n// [T] = [T111 T112 T113 T121 T122 T123 T131 T132 T133 T211 T212 T213 T221 T222 T223 T231 T232 T233 T311 T312 T313 T321 T322 T323 T331 T332 T333\n//     =    T0   T1   T2   T3   T4   T5   T6   T7   T8   T9  T10  T11  T12  T13  T14  T15  T16  T17  T18  T19  T20  T21  T22  T23  T24  T25  T26\ninline mat3d tens3d::contract1(const vec3d& v) const\n{\n    mat3d x;\n    \n    x[0][0] = d[0]*v.x + d[1]*v.y + d[2]*v.z;\n    x[0][1] = d[3]*v.x + d[4]*v.y + d[5]*v.z;\n    x[0][2] = d[6]*v.x + d[7]*v.y + d[8]*v.z;\n    \n    x[1][0] = d[9]*v.x + d[10]*v.y + d[11]*v.z;\n    x[1][1] = d[12]*v.x + d[13]*v.y + d[14]*v.z;\n    x[1][2] = d[15]*v.x + d[16]*v.y + d[17]*v.z;\n    \n    x[2][0] = d[18]*v.x + d[19]*v.y + d[20]*v.z;\n    x[2][1] = d[21]*v.x + d[22]*v.y + d[23]*v.z;\n    x[2][2] = d[24]*v.x + d[25]*v.y + d[26]*v.z;\n    \n    return x;\n}\n\ninline tens3d operator + (const tens3dls& l, const tens3drs& r)\n{\n\ttens3d s;\n\ts.d[ 0] = l.d[ 0] + r.d[ 0];\t// S111 = L111 + R111\n\ts.d[ 1] = l.d[ 1] + r.d[ 1];\t// S112 = L112 + R112\n\ts.d[ 2] = l.d[ 2] + r.d[ 2];\t// S113 = L113 + R113\n\ts.d[ 3] = l.d[ 3] + r.d[ 1];\t// S121 = L121 + R112\n\ts.d[ 4] = l.d[ 4] + r.d[ 3];\t// S122 = L122 + R122\n\ts.d[ 5] = l.d[ 5] + r.d[ 4];\t// S123 = L123 + R123\n\ts.d[ 6] = l.d[ 6] + r.d[ 2];\t// S131 = L131 + R113\n\ts.d[ 7] = l.d[ 7] + r.d[ 4];\t// S132 = L132 + R123\n\ts.d[ 8] = l.d[ 8] + r.d[ 5];\t// S133 = L133 + R133\n\ts.d[ 9] = l.d[ 3] + r.d[ 6];\t// S211 = L121 + R211\n\ts.d[10] = l.d[ 4] + r.d[ 7];\t// S212 = L122 + R212\n\ts.d[11] = l.d[ 5] + r.d[ 8];\t// S213 = L123 + R213\n\ts.d[12] = l.d[ 9] + r.d[ 7];\t// S221 = L221 + R212\n\ts.d[13] = l.d[10] + r.d[ 9];\t// S222 = L222 + R222\n\ts.d[14] = l.d[11] + r.d[10];\t// S223 = L223 + R223\n\ts.d[15] = l.d[12] + r.d[ 8];\t// S231 = L231 + R213\n\ts.d[16] = l.d[13] + r.d[10];\t// S232 = L232 + R223\n\ts.d[17] = l.d[14] + r.d[11];\t// S233 = L233 + R233\n\ts.d[18] = l.d[ 6] + r.d[12];\t// S311 = L131 + R311\n\ts.d[19] = l.d[ 7] + r.d[13];\t// S312 = L132 + R312\n\ts.d[20] = l.d[ 8] + r.d[14];\t// S313 = L133 + R313\n\ts.d[21] = l.d[12] + r.d[13];\t// S321 = L231 + R312\n\ts.d[22] = l.d[13] + r.d[15];\t// S322 = L232 + R322\n\ts.d[23] = l.d[14] + r.d[16];\t// S323 = L233 + R323\n\ts.d[24] = l.d[15] + r.d[14];\t// S331 = L331 + R313\n\ts.d[25] = l.d[16] + r.d[16];\t// S332 = L332 + R323\n\ts.d[26] = l.d[17] + r.d[17];\t// S333 = L333 + R333\n\treturn s;\n}\n"
  },
  {
    "path": "FECore/tens3dls.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens3d.h\n// Users should not include this file manually!\n\n// calculate the transpose ((G_KJi)T = G_iJK)\ninline tens3drs tens3dls::transpose()\n{\n\ttens3drs GRC;\n\n\tGRC.d[0] =  d[ 0];\n\tGRC.d[1] =  d[ 3];\n\tGRC.d[2] =  d[ 6];\n\tGRC.d[3] =  d[ 9];\n\tGRC.d[4] =  d[12];\n\tGRC.d[5] =  d[15];\n\tGRC.d[6] =  d[ 1];\n\tGRC.d[7] =  d[ 4];\n\tGRC.d[8] =  d[ 7];\n\tGRC.d[9] =  d[10];\n\tGRC.d[10] = d[13];\n\tGRC.d[11] = d[16];\n\tGRC.d[12] = d[ 2];\n\tGRC.d[13] = d[ 5];\n\tGRC.d[14] = d[ 8];\n\tGRC.d[15] = d[11];\n\tGRC.d[16] = d[14];\n\tGRC.d[17] = d[17];\n\n\treturn GRC;\n}\n\n// multiply by a double on right (G_ifk * d)\ninline tens3dls tens3dls::operator * (const double& f) const\n{\n    tens3dls G;\n    \n    G.d[0] = d[0]*f;\n    G.d[1] = d[1]*f;\n    G.d[2] = d[2]*f;\n    \n    G.d[3] = d[3]*f;\n    G.d[4] = d[4]*f;\n    G.d[5] = d[5]*f;\n    \n    G.d[6] = d[6]*f;\n    G.d[7] = d[7]*f;\n    G.d[8] = d[8]*f;\n    \n    G.d[ 9] = d[9]*f;\n    G.d[10] = d[10]*f;\n    G.d[11] = d[11]*f;\n    \n    G.d[12] = d[12]*f;\n    G.d[13] = d[13]*f;\n    G.d[14] = d[14]*f;\n    \n    G.d[15] = d[15]*f;\n    G.d[16] = d[16]*f;\n    G.d[17] = d[17]*f;\n    \n    return G;\n}\n\n// multiply by a 2o tensor on the right (G_KJi * F_iI)\ninline tens3dls tens3dls::operator * (const mat3d& F) const\n{\n\ttens3dls G;\n\n\tG.d[0] = d[0]*F(0,0) + d[1]*F(1,0) + d[2]*F(2,0);\n\tG.d[1] = d[0]*F(0,1) + d[1]*F(1,1) + d[2]*F(2,1);\n\tG.d[2] = d[0]*F(0,2) + d[1]*F(1,2) + d[2]*F(2,2);\n\t\n\tG.d[3] = d[3]*F(0,0) + d[4]*F(1,0) + d[5]*F(2,0);\n\tG.d[4] = d[3]*F(0,1) + d[4]*F(1,1) + d[5]*F(2,1);\n\tG.d[5] = d[3]*F(0,2) + d[4]*F(1,2) + d[5]*F(2,2);\n\n\tG.d[6] = d[6]*F(0,0) + d[7]*F(1,0) + d[8]*F(2,0);\n\tG.d[7] = d[6]*F(0,1) + d[7]*F(1,1) + d[8]*F(2,1);\n\tG.d[8] = d[6]*F(0,2) + d[7]*F(1,2) + d[8]*F(2,2);\n\n\tG.d[ 9] = d[9]*F(0,0) + d[10]*F(1,0) + d[11]*F(2,0);\n\tG.d[10] = d[9]*F(0,1) + d[10]*F(1,1) + d[11]*F(2,1);\n\tG.d[11] = d[9]*F(0,2) + d[10]*F(1,2) + d[11]*F(2,2);\n\n\tG.d[12] = d[12]*F(0,0) + d[13]*F(1,0) + d[14]*F(2,0);\n\tG.d[13] = d[12]*F(0,1) + d[13]*F(1,1) + d[14]*F(2,1);\n\tG.d[14] = d[12]*F(0,2) + d[13]*F(1,2) + d[14]*F(2,2);\n\n\tG.d[15] = d[15]*F(0,0) + d[16]*F(1,0) + d[17]*F(2,0);\n\tG.d[16] = d[15]*F(0,1) + d[16]*F(1,1) + d[17]*F(2,1);\n\tG.d[17] = d[15]*F(0,2) + d[16]*F(1,2) + d[17]*F(2,2);\n\n\treturn G;\n}\n\n// calculate the trace Tijk -> Tijj\ninline vec3d tens3dls::trace()\n{\n    double a = d[0] + d[4] + d[8];\n    double b = d[3] + d[10] + d[14];\n    double c = d[6] + d[13] + d[17];\n    vec3d v = vec3d(a,b,c);\n    \n    return v;\n}\n\n// generalize tensor from tens3dls to tens3d\n// [G] = [G111 G112 G113 G121 G122 G123 G131 G132 G133 G221 G222 G223 G231 G232 G233 G331 G332 G333]\n//     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\n// [T] = [T111 T112 T113 T121 T122 T123 T131 T132 T133 T211 T212 T213 T221 T222 T223 T231 T232 T233 T311 T312 T313 T321 T322 T323 T331 T332 T333\n//     =    T0   T1   T2   T3   T4   T5   T6   T7   T8   T9  T10  T11  T12  T13  T14  T15  T16  T17  T18  T19  T20  T21  T22  T23  T24  T25  T26\ninline tens3d tens3dls::generalize()\n{\n    tens3d G;\n    \n    G.d[0] = d[0];\n    G.d[1] = d[1];\n    G.d[2] = d[2];\n    G.d[3] = d[3];\n    G.d[4] = d[4];\n    G.d[5] = d[5];\n    G.d[6] = d[6];\n    G.d[7] = d[7];\n    G.d[8] = d[8];\n    G.d[9] = d[3];\n    G.d[10] = d[4];\n    G.d[11] = d[5];\n    G.d[12] = d[9];\n    G.d[13] = d[10];\n    G.d[14] = d[11];\n    G.d[15] = d[12];\n    G.d[16] = d[13];\n    G.d[17] = d[14];\n    G.d[18] = d[6];\n    G.d[19] = d[7];\n    G.d[20] = d[8];\n    G.d[21] = d[12];\n    G.d[22] = d[13];\n    G.d[23] = d[14];\n    G.d[24] = d[15];\n    G.d[25] = d[16];\n    G.d[26] = d[17];\n    \n    return G;\n}\n\n// calculates the dyadic product T_ijk = 1/2*(L_ij*r_k + L_ji*r_k)\n// [G] = [G111 G112 G113 G121 G122 G123 G131 G132 G133 G221 G222 G223 G231 G232 G233 G331 G332 G333]\n//     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\ninline tens3dls dyad3ls(const mat3ds& L, const vec3d& r)\n{\n    tens3dls a;\n    a.d[ 0] = L(0, 0)*r.x;\n    a.d[ 1] = L(0, 0)*r.y;\n    a.d[ 2] = L(0, 0)*r.z;\n    a.d[ 3] = L(0, 1)*r.x;\n    a.d[ 4] = L(0, 1)*r.y;\n    a.d[ 5] = L(0, 1)*r.z;\n    a.d[ 6] = L(0, 2)*r.x;\n    a.d[ 7] = L(0, 2)*r.y;\n    a.d[ 8] = L(0, 2)*r.z;\n    a.d[ 9] = L(1, 1)*r.x;\n    a.d[10] = L(1, 1)*r.y;\n    a.d[11] = L(1, 1)*r.z;\n    a.d[12] = L(1, 2)*r.x;\n    a.d[13] = L(1, 2)*r.y;\n    a.d[14] = L(1, 2)*r.z;\n    a.d[15] = L(2, 2)*r.x;\n    a.d[16] = L(2, 2)*r.y;\n    a.d[17] = L(2, 2)*r.z;\n    \n    return a;\n}\n"
  },
  {
    "path": "FECore/tens3drs.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens3drs.h\n// Users should not include this file manually!\n\n#include \"mat3d.h\"\n\n// access operator\ninline double tens3drs::operator () (int i, int j, int k) const\n{\n\tconst int m[3][3] = {{0,1,2},{1,3,4},{2,4,5}};\n\treturn d[6*i + m[j][k] ];\n}\n\n// access operator\ninline double& tens3drs::operator () (int i, int j, int k)\n{\n\tconst int m[3][3] = {{0,1,2},{1,3,4},{2,4,5}};\n\treturn d[6*i + m[j][k] ];\n}\n\ninline tens3drs::tens3drs(double a)\n{\n\tint nnz = tensor_traits<tens3drs>::NNZ;\n\tfor (int i = 0; i < nnz; ++i) d[i] = a;\n}\n\n// contract the right two legs by the dyad formed by a vector  xi = Gijk*Xj*Xk\ninline vec3d tens3drs::contractdyad1(const vec3d& v) const\n{\n    vec3d x;\n\tx.x = d[ 0]*v.x*v.x + 2*d[ 1]*v.x*v.y + 2*d[ 2]*v.x*v.z + d[ 3]*v.y*v.y + 2*d[ 4]*v.y*v.z + d[ 5]*v.z*v.z;\n\tx.y = d[ 6]*v.x*v.x + 2*d[ 7]*v.x*v.y + 2*d[ 8]*v.x*v.z + d[ 9]*v.y*v.y + 2*d[10]*v.y*v.z + d[11]*v.z*v.z;\n\tx.z = d[12]*v.x*v.x + 2*d[13]*v.x*v.y + 2*d[14]*v.x*v.z + d[15]*v.y*v.y + 2*d[16]*v.y*v.z + d[17]*v.z*v.z;\n\n\treturn x;\n}\n\n// contract the right two legs by a symmetric 2o tensor  xi = Gijk*Sjk\ninline vec3d tens3drs::contract2s(const mat3ds& s) const\n{\n    vec3d x;\n\tx.x = d[ 0]*s.xx() + 2*d[ 1]*s.xy() + 2*d[ 2]*s.xz() + d[ 3]*s.yy() + 2*d[ 4]*s.yz() + d[ 5]*s.zz();\n\tx.y = d[ 6]*s.xx() + 2*d[ 7]*s.xy() + 2*d[ 8]*s.xz() + d[ 9]*s.yy() + 2*d[10]*s.yz() + d[11]*s.zz();\n\tx.z = d[12]*s.xx() + 2*d[13]*s.xy() + 2*d[14]*s.xz() + d[15]*s.yy() + 2*d[16]*s.yz() + d[17]*s.zz();\n\n\treturn x;\n}\n\n// triple contraction by a similar 3o tensor m = Gijk*Hijk\ninline double tens3drs::tripledot(const tens3drs& H) const\n{\n\tconst double* h = H.d;\n\treturn\td[ 0]*h[ 0] + 2*d[ 1]*h[ 1] + 2*d[ 2]*h[ 2] + d[ 3]*h[ 3] + 2*d[ 4]*h[ 4] + d[ 5]*h[ 5] \n\t\t  + d[ 6]*h[ 6] + 2*d[ 7]*h[ 7] + 2*d[ 8]*h[ 8] + d[ 9]*h[ 9] + 2*d[10]*h[10] + d[11]*h[11]\n\t\t  + d[12]*h[12] + 2*d[13]*h[13] + 2*d[14]*h[14] + d[15]*h[15] + 2*d[16]*h[16] + d[17]*h[17];  \n}\n\n// contract the right two legs by the dyad formed by a vector  xi = Gijk*Vj*Wk\ninline vec3d tens3drs::contractdyad2(const vec3d& v, const vec3d& w)\n{\n    vec3d x;\n\tx.x = d[ 0]*v.x*w.x + d[ 1]*(v.x*w.y + v.y*w.x) + d[ 2]*(v.x*w.z + v.z*w.x) + d[ 3]*v.y*w.y + d[ 4]*(v.y*w.z + v.z*w.y) + d[ 5]*v.z*w.z;\n\tx.y = d[ 6]*v.x*w.x + d[ 7]*(v.x*w.y + v.y*w.x) + d[ 8]*(v.x*w.z + v.z*w.x) + d[ 9]*v.y*w.y + d[10]*(v.y*w.z + v.z*w.y) + d[11]*v.z*w.z;\n\tx.z = d[12]*v.x*w.x + d[13]*(v.x*w.y + v.y*w.x) + d[14]*(v.x*w.z + v.z*w.x) + d[15]*v.y*w.y + d[16]*(v.y*w.z + v.z*w.y) + d[17]*v.z*w.z;\n\n\treturn x;\n}\n\n// calculates the dyadic product T_ijk = l_i*r_j*r_k\ninline tens3drs dyad3rs(const vec3d& l, const vec3d& r)\n{\n\ttens3drs a;\n\ta.d[ 0] = l.x*r.x*r.x; \n\ta.d[ 1] = l.x*r.x*r.y;\n\ta.d[ 2] = l.x*r.x*r.z;\n\ta.d[ 3] = l.x*r.y*r.y;\n\ta.d[ 4] = l.x*r.y*r.z;\n\ta.d[ 5] = l.x*r.z*r.z;\n\ta.d[ 6] = l.y*r.x*r.x;\n\ta.d[ 7] = l.y*r.x*r.y;\n\ta.d[ 8] = l.y*r.x*r.z;\n\ta.d[ 9] = l.y*r.y*r.y;\n\ta.d[10] = l.y*r.y*r.z;\n\ta.d[11] = l.y*r.z*r.z;\n\ta.d[12] = l.z*r.x*r.x;\n\ta.d[13] = l.z*r.x*r.y;\n\ta.d[14] = l.z*r.x*r.z;\n\ta.d[15] = l.z*r.y*r.y;\n\ta.d[16] = l.z*r.y*r.z;\n\ta.d[17] = l.z*r.z*r.z;\n\treturn a;\n}\n\n// calculates the dyadic product T_ijk = 1/2*(L_ij*r_k + L_ik*r_j)\ninline tens3drs dyad3rs(const mat3d& L, const vec3d& r)\n{\n\ttens3drs a;\n\ta.d[0] =  L(0, 0)*r.x;\n\ta.d[1] = (L(0, 0)*r.y + L(0, 1)*r.x)*0.5;\n\ta.d[2] = (L(0, 0)*r.z + L(0, 2)*r.x)*0.5;\n\ta.d[3] =  L(0, 1)*r.y;\n\ta.d[4] = (L(0, 1)*r.z + L(0, 2)*r.y)*0.5;\n\ta.d[5] =  L(0, 2)*r.z;\n\n\ta.d[ 6] =  L(1, 0)*r.x;\n\ta.d[ 7] = (L(1, 0)*r.y + L(1, 1)*r.x)*0.5;\n\ta.d[ 8] = (L(1, 0)*r.z + L(1, 2)*r.x)*0.5;\n\ta.d[ 9] =  L(1, 1)*r.y;\n\ta.d[10] = (L(1, 1)*r.z + L(1, 2)*r.y)*0.5;\n\ta.d[11] =  L(1, 2)*r.z;\n\n\ta.d[12] =  L(2, 0)*r.x;\n\ta.d[13] = (L(2, 0)*r.y + L(2, 1)*r.x)*0.5;\n\ta.d[14] = (L(2, 0)*r.z + L(2, 2)*r.x)*0.5;\n\ta.d[15] =  L(2, 1)*r.y;\n\ta.d[16] = (L(2, 1)*r.z + L(2, 2)*r.y)*0.5;\n\ta.d[17] =  L(2, 2)*r.z;\n\n\treturn a;\n}\n\n// calculate the transpose ((G_iJK)T = G_KJi)\ninline tens3dls tens3drs::transpose()\n{\n\ttens3dls GLC;\n\n\tGLC.d[ 0] = d[ 0];\n\tGLC.d[ 3] = d[ 1];\n\tGLC.d[ 6] = d[ 2];\n\tGLC.d[ 9] = d[ 3];\n\tGLC.d[12] = d[ 4];\n\tGLC.d[15] = d[ 5];\n\tGLC.d[ 1] = d[ 6];\n\tGLC.d[ 4] = d[ 7];\n\tGLC.d[ 7] = d[ 8];\n\tGLC.d[10] = d[ 9];\n\tGLC.d[13] = d[10];\n\tGLC.d[16] = d[11];\n\tGLC.d[ 2] = d[12];\n\tGLC.d[ 5] = d[13];\n\tGLC.d[ 8] = d[14];\n\tGLC.d[11] = d[15];\n\tGLC.d[14] = d[16];\n\tGLC.d[17] = d[17];\n\n\treturn GLC;\n}\n\n// contract each leg by a 2o tensor (intended to calculate the inverse deformation hessian according to Finv_Ii * G_iJK * Finv_Jj * Fin_Kk)\ninline void tens3drs::contractleg2(const mat3d& F, int leg)\n{\n\ttens3drs G = *this;\n\t\n\tif (leg == 1)\n\t{\n\t\td[0] = F(0,0)*G.d[0] + F(0,1)*G.d[6] + F(0,2)*G.d[12];\n\t\td[1] = F(0,0)*G.d[1] + F(0,1)*G.d[7] + F(0,2)*G.d[13];\n\t\td[2] = F(0,0)*G.d[2] + F(0,1)*G.d[8] + F(0,2)*G.d[14];\n\t\td[3] = F(0,0)*G.d[3] + F(0,1)*G.d[9] + F(0,2)*G.d[15];\n\t\td[4] = F(0,0)*G.d[4] + F(0,1)*G.d[10] + F(0,2)*G.d[16];\n\t\td[5] = F(0,0)*G.d[5] + F(0,1)*G.d[11] + F(0,2)*G.d[17];\n\t\td[6] = F(1,0)*G.d[0] + F(1,1)*G.d[6] + F(1,2)*G.d[12];\n\t\td[7] = F(1,0)*G.d[1] + F(1,1)*G.d[7] + F(1,2)*G.d[13];\n\t\td[8] = F(1,0)*G.d[2] + F(1,1)*G.d[8] + F(1,2)*G.d[14];\n\t\td[9] = F(1,0)*G.d[3] + F(1,1)*G.d[9] + F(1,2)*G.d[15];\n\t\td[10] = F(1,0)*G.d[4] + F(1,1)*G.d[10] + F(1,2)*G.d[16];\n\t\td[11] = F(1,0)*G.d[5] + F(1,1)*G.d[11] + F(1,2)*G.d[17];\n\t\td[12] = F(2,0)*G.d[0] + F(2,1)*G.d[6] + F(2,2)*G.d[12];\n\t\td[13] = F(2,0)*G.d[1] + F(2,1)*G.d[7] + F(2,2)*G.d[13];\n\t\td[14] = F(2,0)*G.d[2] + F(2,1)*G.d[8] + F(2,2)*G.d[14];\n\t\td[15] = F(2,0)*G.d[3] + F(2,1)*G.d[9] + F(2,2)*G.d[15];\n\t\td[16] = F(2,0)*G.d[4] + F(2,1)*G.d[10] + F(2,2)*G.d[16];\n\t\td[17] = F(2,0)*G.d[5] + F(2,1)*G.d[11] + F(2,2)*G.d[17];\n\t}\n\telse if (leg == 2)\n\t{\n\t\td[0] = G.d[0]*F(0,0) + G.d[1]*F(1,0) + G.d[2]*F(2,0);\n\t\td[1] = G.d[1]*F(0,0) + G.d[3]*F(1,0) + G.d[4]*F(2,0);\n\t\td[2] = G.d[2]*F(0,0) + G.d[4]*F(1,0) + G.d[5]*F(2,0);\n\t\td[3] = G.d[1]*F(0,1) + G.d[3]*F(1,1) + G.d[4]*F(2,1);\n\t\td[4] = G.d[2]*F(0,1) + G.d[4]*F(1,1) + G.d[5]*F(2,1);\n\t\td[5] = G.d[2]*F(0,2) + G.d[4]*F(1,2) + G.d[5]*F(2,2);\n\t\td[6] = G.d[6]*F(0,0) + G.d[7]*F(1,0) + G.d[8]*F(2,0);\n\t\td[7] = G.d[7]*F(0,0) + G.d[9]*F(1,0) + G.d[10]*F(2,0);\n\t\td[8] = G.d[8]*F(0,0) + G.d[10]*F(1,0) + G.d[11]*F(2,0);\n\t\td[9] = G.d[7]*F(0,1) + G.d[9]*F(1,1) + G.d[10]*F(2,1);\n\t\td[10] = G.d[8]*F(0,1) + G.d[10]*F(1,1) + G.d[11]*F(2,1);\n\t\td[11] = G.d[8]*F(0,2) + G.d[10]*F(1,2) + G.d[11]*F(2,2);\n\t\td[12] = G.d[12]*F(0,0) + G.d[13]*F(1,0) + G.d[4]*F(2,0);\n\t\td[13] = G.d[13]*F(0,0) + G.d[14]*F(1,0) + G.d[16]*F(2,0);\n\t\td[14] = G.d[14]*F(0,0) + G.d[16]*F(1,0) + G.d[17]*F(2,0);\n\t\td[15] = G.d[13]*F(0,1) + G.d[15]*F(1,1) + G.d[16]*F(2,1);\n\t\td[16] = G.d[14]*F(0,1) + G.d[16]*F(1,1) + G.d[17]*F(2,1);\n\t\td[17] = G.d[14]*F(0,2) + G.d[16]*F(1,2) + G.d[17]*F(2,2);\n\t}\n\telse if (leg == 3)\n\t{\n\t\td[0] = G.d[0]*F(0,0) + G.d[1]*F(1,0) + G.d[2]*F(2,0);\n\t\td[1] = G.d[0]*F(0,1) + G.d[1]*F(1,1) + G.d[2]*F(2,1);\n\t\td[2] = G.d[0]*F(0,2) + G.d[1]*F(1,2) + G.d[2]*F(2,2);\n\t\td[3] = G.d[1]*F(0,1) + G.d[3]*F(1,1) + G.d[4]*F(2,1);\n\t\td[4] = G.d[1]*F(0,2) + G.d[3]*F(1,2) + G.d[4]*F(2,2);\n\t\td[5] = G.d[2]*F(0,2) + G.d[4]*F(1,2) + G.d[5]*F(2,2);\n\t\td[6] = G.d[6]*F(0,0) + G.d[7]*F(1,0) + G.d[8]*F(2,0);\n\t\td[7] = G.d[6]*F(0,1) + G.d[7]*F(1,1) + G.d[8]*F(2,1);\n\t\td[8] = G.d[6]*F(0,2) + G.d[7]*F(1,2) + G.d[8]*F(2,2);\n\t\td[9] = G.d[7]*F(0,1) + G.d[9]*F(1,1) + G.d[10]*F(2,1);\n\t\td[10] = G.d[7]*F(0,2) + G.d[9]*F(1,2) + G.d[10]*F(2,2);\n\t\td[11] = G.d[8]*F(0,2) + G.d[10]*F(1,2) + G.d[11]*F(2,2);\n\t\td[12] = G.d[12]*F(0,0) + G.d[13]*F(1,0) + G.d[14]*F(2,0);\n\t\td[13] = G.d[12]*F(0,1) + G.d[13]*F(1,1) + G.d[14]*F(2,1);\n\t\td[14] = G.d[12]*F(0,2) + G.d[13]*F(1,2) + G.d[14]*F(2,2);\n\t\td[15] = G.d[13]*F(0,1) + G.d[15]*F(1,1) + G.d[16]*F(2,1);\n\t\td[16] = G.d[13]*F(0,2) + G.d[15]*F(1,2) + G.d[16]*F(2,2);\n\t\td[17] = G.d[14]*F(0,2) + G.d[16]*F(1,2) + G.d[17]*F(2,2);\n\t}\n}\n\n// multiply by a 2o tensor on the left (F_Ii * G_iJK)\ninline tens3drs operator * (const mat3d& F, const tens3drs& t)\n{\n\ttens3drs G;\n\n\tconst double* d = t.d;\n\tG.d[ 0] = F(0,0)*d[0] + F(0,1)*d[ 6] + F(0,2)*d[12];\n\tG.d[ 1] = F(0,0)*d[1] + F(0,1)*d[ 7] + F(0,2)*d[13];\n\tG.d[ 2] = F(0,0)*d[2] + F(0,1)*d[ 8] + F(0,2)*d[14];\n\tG.d[ 3] = F(0,0)*d[3] + F(0,1)*d[ 9] + F(0,2)*d[15];\n\tG.d[ 4] = F(0,0)*d[4] + F(0,1)*d[10] + F(0,2)*d[16];\n\tG.d[ 5] = F(0,0)*d[5] + F(0,1)*d[11] + F(0,2)*d[17];\n\n\tG.d[ 6] = F(1,0)*d[0] + F(1,1)*d[ 6] + F(1,2)*d[12];\n\tG.d[ 7] = F(1,0)*d[1] + F(1,1)*d[ 7] + F(1,2)*d[13];\n\tG.d[ 8] = F(1,0)*d[2] + F(1,1)*d[ 8] + F(1,2)*d[14];\n\tG.d[ 9] = F(1,0)*d[3] + F(1,1)*d[ 9] + F(1,2)*d[15];\n\tG.d[10] = F(1,0)*d[4] + F(1,1)*d[10] + F(1,2)*d[16];\n\tG.d[11] = F(1,0)*d[5] + F(1,1)*d[11] + F(1,2)*d[17];\n\n\tG.d[12] = F(2,0)*d[0] + F(2,1)*d[ 6] + F(2,2)*d[12];\n\tG.d[13] = F(2,0)*d[1] + F(2,1)*d[ 7] + F(2,2)*d[13];\n\tG.d[14] = F(2,0)*d[2] + F(2,1)*d[ 8] + F(2,2)*d[14];\n\tG.d[15] = F(2,0)*d[3] + F(2,1)*d[ 9] + F(2,2)*d[15];\n\tG.d[16] = F(2,0)*d[4] + F(2,1)*d[10] + F(2,2)*d[16];\n\tG.d[17] = F(2,0)*d[5] + F(2,1)*d[11] + F(2,2)*d[17];\n\n\treturn G;\n}\n"
  },
  {
    "path": "FECore/tens3ds.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens3drs.h\n// Users should not include this file manually!\n\n// access operator\ninline double tens3ds::operator()(int i, int j, int k) const\n{\n\tconst int LUT[3][3][3] = {\n\t\t{{0,1,2},{1,3,4},{2,4,5}},\n\t\t{{1,3,4},{3,6,7},{4,7,8}},\n\t\t{{2,4,5},{4,7,8},{5,8,9}}};\n\n\treturn d[LUT[i][j][k]];\n}\n\n// contract the right two legs by the dyad formed by a vector  xi = Tijk*Xi*Xk\ninline vec3d tens3ds::contractdyad1(const vec3d& v)\n{\n    vec3d x;\n\tx.x = d[0]*v.x*v.x + 2*d[1]*v.x*v.y + 2*d[2]*v.x*v.z + d[3]*v.y*v.y + 2*d[4]*v.y*v.z + d[5]*v.z*v.z;\n\tx.y = d[1]*v.x*v.x + 2*d[3]*v.x*v.y + 2*d[4]*v.x*v.z + d[6]*v.y*v.y + 2*d[7]*v.y*v.z + d[8]*v.z*v.z;\n\tx.z = d[2]*v.x*v.x + 2*d[4]*v.x*v.y + 2*d[5]*v.x*v.z + d[7]*v.y*v.y + 2*d[8]*v.y*v.z + d[9]*v.z*v.z;\n\n\treturn x;\n}\n\n// triple contraction by a similar 3o tensor m = Tijk*Hijk\ninline double tens3ds::tripledot(const tens3ds& H)\n{\n\tconst double* h = H.d;\n\treturn d[0]*h[0] + 3*d[1]*h[1] + 3*d[2]*h[2] + 3*d[3]*h[3] + 6*d[4]*h[4] + 3*d[5]*h[5] + d[6]*h[6] + 3*d[7]*h[7] + 3*d[8]*h[8] + d[9]*h[9];\n}\n\n// calculates the symmetric tensor A_ijk = (l_i*m_j*r_k + perm(i,j,k))/6\ninline tens3ds dyad3s(const vec3d& l, const vec3d& m, const vec3d& r)\n{\n\ttens3ds a;\n\ta.d[0] = (l.x*m.x*r.x); \n\ta.d[1] = (l.x*m.x*r.y + l.x*m.y*r.x + l.y*m.x*r.x)/3.0; \n\ta.d[2] = (l.x*m.x*r.z + l.x*m.z*r.x + l.z*m.x*r.x)/3.0;\n\ta.d[3] = (l.x*m.y*r.y + l.y*m.x*r.y + l.y*m.y*r.x)/3.0; \n\ta.d[4] = (l.x*m.y*r.z + l.y*m.x*r.z + l.z*m.y*r.x + l.x*m.z*r.y + l.z*m.x*r.y + l.y*m.z*r.x)/6.0; \n\ta.d[5] = (l.x*m.z*r.z + l.z*m.x*r.z + l.z*m.z*r.x)/3.0;\n\ta.d[6] = (l.y*m.y*r.y); \n\ta.d[7] = (l.y*m.y*r.z + l.y*m.z*r.y + l.z*m.y*r.y)/3.0;\n\ta.d[8] = (l.y*m.z*r.z + l.z*m.y*r.z + l.z*m.z*r.y)/3.0;\n\ta.d[9] = (l.z*m.z*r.z);\n\treturn a;\n}\n"
  },
  {
    "path": "FECore/tens4d.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"tens4d.h\"\n#include <math.h>\n\n\n//-----------------------------------------------------------------------------\n//! This function checks the positive definiteness of a 4th order tensor\n//! having both major and minor symmetries. The function does not do an\n//! exhaustive test, in the sense it can only detect failure. If a tensor passes\n//! it is not guaranteed that the tensor is indeed positive-definite.\nbool IsPositiveDefinite(const tens4ds& t)\n{\n\t// test 1. all diagonal entries have to be positive\n\tif (t(0,0) <= 0) return false;\n\tif (t(1,1) <= 0) return false;\n\tif (t(2,2) <= 0) return false;\n\tif (t(3,3) <= 0) return false;\n\tif (t(4,4) <= 0) return false;\n\tif (t(5,5) <= 0) return false;\n\n\t// test 2. t(i,i)+t(j,j) > 2t(i,j)\n\tint i, j;\n\tfor (i=0; i<6; ++i)\n\t{\n\t\tfor (j=i+1; j<6; ++j)\n\t\t{\n\t\t\tif (t(i,i)+t(j,j) <= 2*t(i,j))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// test 3. the element with largest modulus lies on the main diagonal\n\tdouble l = -1, v;\n\tbool d = false;\n\tfor (i=0; i<6; ++i)\n\t{\n\t\tfor (j=i; j<6; ++j)\n\t\t{\n\t\t\tv = fabs(t(i,j));\n\t\t\tif (v > l)\n\t\t\t{\n\t\t\t\tl = v;\n\t\t\t\td = (i==j);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (d == false)\n\t{\n\t\treturn false;\n\t}\n\n\t// if all tests pass, it is not guaranteed that the tensor is indeed positive-definite\n\t// but we'd have some good reasons to believe so.\n\treturn true;\n}\n"
  },
  {
    "path": "FECore/tens4d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"tensor_base.h\"\n#include \"mat3d.h\"\n#include \"tens3d.h\"\n\n//-----------------------------------------------------------------------------\nclass tens4ds;\nclass tens4dms;\nclass tens4dmm;\nclass tens4d;\n\n//-----------------------------------------------------------------------------\n//! Class for 4th order tensors with major and minor symmetries (i.e., super-symmetry)\n\n// Due to the major symmetry we can store this tensor as a 6x6 matrix.\n// The tensor is stored in column major order:\n//\n//     / 0   1   3   6   10   15  \\   / C0000  C0011  C0022  C0001  C0012  C0002 \\\n//     |     2   4   7   11   16  |   |        C1111  C1122  C1101  C1112  C1102 |\n//     |         5   8   12   17  |   |               C2222  C2201  C2212  C2202 |\n// A = |             9   13   18  | = |                      C0101  C0112  C0102 |\n//     |                 14   19  |   |                             C1212  C1202 |\n//     \\                      20  /   \\                                    C0202 /\n//\n// Note that due to the major symmetry we only store the upper triangular matrix of this tensor\n//\n\nclass tens4ds\n{\npublic:\n\tenum { NNZ = 21 };\n\n\t// default constructor\n\ttens4ds() {}\n\texplicit tens4ds(const double g);\n\ttens4ds(double m[6][6]);\n\n\tdouble& operator () (int i, int j, int k, int l);\n\tdouble operator () (int i, int j, int k, int l) const;\n\n\t// TODO: remove this?\n\tdouble& operator () (int i, int j);\n\tdouble operator () (int i, int j) const;\n\n\t// arithmetic operators\n\ttens4ds operator + (const tens4ds& t) const;\n\ttens4ds operator - (const tens4ds& t) const;\n\ttens4ds operator * (double g) const;\n\ttens4ds operator / (double g) const;\n\n\t// arithmetic assignment operators\n\ttens4ds& operator += (const tens4ds& t);\n\ttens4ds& operator -= (const tens4ds& t);\n\ttens4ds& operator *= (double g);\n\ttens4ds& operator /= (double g);\n\n\t// unary operators\n\ttens4ds operator - () const;\n\t\n\t// double dot product with 2nd order tensor\n\tmat3ds dot(const mat3ds& m) const;\n    mat3ds dot(const mat3dd& m) const { return dot(mat3ds(m)); }\n    mat3ds dot(const mat3d& m) const;\n    tens3dls dot(const vec3d& m) const;\n    mat3ds dot2(const mat3d& m) const;\n\n\t// contractions\n\tmat3ds contract() const;\n\n\t// trace\n\tdouble tr() const;\n\t\n\t// initialize to zero\n\tvoid zero();\n\n\t// extract 6x6 matrix\n\tvoid extract(double d[6][6]);\n\n\t// calculates the inverse\n\ttens4ds inverse() const;\n    \n    // evaluate push/pull operation\n    tens4ds pp(const mat3d& F);\n\npublic:\n\tdouble d[NNZ];\t// stored in column major order\n};\n\n//! Check positive definiteness of a 4th-order symmetric tensor\nbool IsPositiveDefinite(const tens4ds& t);\n\n// outer (dyadic) products for symmetric matrices\ntens4ds dyad1s(const mat3dd& a);\ntens4ds dyad1s(const mat3ds& a);\ntens4ds dyad1s(const mat3dd& a, const mat3dd& b); \ntens4ds dyad1s(const mat3ds& a, const mat3dd& b);\ntens4ds dyad1s(const mat3ds& a, const mat3ds& b);\ninline tens4ds dyad1s(const mat3dd& a, const mat3ds& b) { return dyad1s(b, a); }\ntens4ds dyad4s(const mat3dd& a);\ntens4ds dyad4s(const mat3ds& a);\ntens4ds dyad4s(const mat3ds& a, const mat3ds& b);\ntens4ds dyad4s(const mat3ds& a, const mat3dd& b);\ntens4ds dyad4s(const vec3d& a, const mat3d& K, const vec3d& b);\ntens4ds dyad5s(const mat3ds& a, const mat3ds& b);\ntens4ds ddots(const tens4ds& a, const tens4ds& b);\nmat3d vdotTdotv(const vec3d& a, const tens4ds& T, const vec3d& b);\n\ninline tens4ds operator * (const double g, const tens4ds& a) { return a*g; }\n\n// The following file contains the actual definition of the class functions\n#include \"tens4ds.hpp\"\n\n//-----------------------------------------------------------------------------\n//! Class for 4th order tensors with major symmetry only\n\n// Due to the lack of minor symmetry, we have to store additional components of this tensor as a 9x9 matrix.\n// Major symmetry ensures that this storage matrix is symmetric about its main diagonal.\n// The tensor is stored in column major order:\n//\n//     / 0   1   3   6   10   15  21   28   36  \\\n//     |     2   4   7   11   16  22   29   37  |\n//     |         5   8   12   17  23   30   38  |\n// A = |             9   13   18  24   31   39  |\n//     |                 14   19  25   32   40  |\n//     |                      20  26   33   41  |\n//     |                          27   34   42  |\n//     |                               35   43  |\n//     \\                                    44  /\n//\n\n\nclass tens4dms\n{\npublic:\n\tenum { NNZ = 45 };\n\n\t// Default constructor\n\ttens4dms() {}\n\ttens4dms(const double g);\n\ttens4dms(double m[9][9]);\n\n\t// access operators\n\tdouble& operator () (int i, int j, int k, int l);\n\tdouble operator () (int i, int j, int k, int l) const;\n\tdouble& operator () (int i, int j);\n\tdouble operator () (int i, int j) const;\n\n\t// arithmetic operators\n\ttens4dms operator + (const tens4dms& t) const;\n\ttens4dms operator - (const tens4dms& t) const;\n\ttens4dms operator * (double g) const;\n\ttens4dms operator / (double g) const;\n\n\t// arithmetic assignment operators\n\ttens4dms& operator += (const tens4dms& t);\n\ttens4dms& operator -= (const tens4dms& t);\n\ttens4dms& operator *= (double g);\n\ttens4dms& operator /= (double g);\n\n\t// unary operators\n\ttens4dms operator - () const;\n\t\n\t// trace\n\tdouble tr() const;\n\t\n\t// initialize to zero\n\tvoid zero();\n\n\t// extract 9x9 matrix\n\tvoid extract(double d[9][9]);\n\n\t// compute the super-symmetric (major and minor symmetric) component of the tensor\n\ttens4ds supersymm() const;\n\n\t//// calculates the inverse\n\t//tens4dms inverse() const;\n\npublic:\n\tdouble d[NNZ];\t// stored in column major order\n};\n\n// outer (dyadic) products for symmetric and non-symmetric matrices\ntens4dms dyad1ms(const mat3d& a);\ntens4dms dyad1ms(const mat3ds& a, const mat3ds& b);\n\n// The following file contains the actual definition of the class functions\n#include \"tens4dms.hpp\"\n\n//-----------------------------------------------------------------------------\n//! Class for 4th order tensors with both minor symmetries\n\n// We store components of this tensor as a 6x6 matrix.\n// The tensor is stored in column major order:\n//\n//       00  11  22   01   12   20    |\n//       --------------------------------------------+---\n//     / 0    6  12   18   24   30  \\ | 00\n//     | 1    7  13   19   25   31  | | 11\n//     | 2    8  14   20   26   32  | | 22\n// A = | 3    9  15   21   27   33  | | 01\n//     | 4   10  16   22   28   34  | | 12\n//     \\ 5   11  17   23   29   35  / | 20\n//\n\ntemplate <> class tensor_traits<tens4dmm> {public: enum { NNZ = 36}; };\n\nclass tens4dmm : public tensor_base<tens4dmm>\n{\npublic:\n    // constructors\n    tens4dmm() {}\n    explicit tens4dmm(const double g);\n    tens4dmm(tens4ds t);\n    tens4dmm(double m[6][6]);\n\npublic:\n    // access operators\n    double& operator () (int i, int j, int k, int l);\n    double operator () (int i, int j, int k, int l) const;\n\nprivate:\n    double& operator () (int i, int j);\n    double operator () (int i, int j) const;\n    \npublic:\n    // arithmetic operators\n    tens4dmm operator + (const tens4dmm& t) const;\n    tens4dmm operator - (const tens4dmm& t) const;\n    tens4dmm operator + (const tens4ds& t) const;\n    tens4dmm operator - (const tens4ds& t) const;\n    tens4dmm operator * (double g) const;\n    tens4dmm operator / (double g) const;\n\n    // arithmetic assignment operators\n    tens4dmm& operator += (const tens4dmm& t);\n    tens4dmm& operator -= (const tens4dmm& t);\n    tens4dmm& operator += (const tens4ds& t);\n    tens4dmm& operator -= (const tens4ds& t);\n    tens4dmm& operator *= (double g);\n    tens4dmm& operator /= (double g);\n\n    // unary operators\n    tens4dmm operator - () const;\n    \n    // double dot product with 2nd order tensor\n    mat3ds dot(const mat3ds& m) const;\n    mat3ds dot(const mat3dd& m) const { return dot(mat3ds(m)); }\n    \n    // double dot product with 4th order tensor\n    tens4dmm ddot(const tens4ds& t) const;\n    tens4dmm ddot(const tens4dmm& t) const;\n    \n    // single dot product with 2nd order tensor\n    tens4dmm operator * (const mat3ds& m) const;\n    tens4dmm operator * (const mat3d& m) const;\n\n    // trace\n    double tr() const;\n    \n    // initialize to zero\n    void zero();\n\n    // extract 9x9 matrix\n    void extract(double d[6][6]);\n\n    // compute the super-symmetric (major and minor symmetric) component of the tensor\n    tens4ds supersymm() const;\n\n    // compute the major transpose (Sijkl -> Sklij)\n    tens4dmm transpose() const;\n\n    // calculates the inverse\n    tens4dmm inverse() const;\n    \n    // evaluate push/pull operation\n    tens4dmm pp(const mat3d& F);\n\n};\n\n// dyadic products of second-order tensors\ntens4dmm dyad1mm(const mat3ds& a, const mat3ds& b);\ntens4dmm dyad4mm(const mat3d& a, const mat3d& b);\n\ninline tens4dmm dyad1mm(const mat3dd& as, const mat3ds& b) { mat3ds a(as); return dyad1mm(a,b); }\ninline tens4dmm dyad1mm(const mat3ds& a, const mat3dd& bs) { mat3ds b(bs); return dyad1mm(a,b); }\ninline tens4dmm dyad1mm(const mat3dd& as, const mat3dd& bs) { mat3ds a(as); mat3ds b(bs); return dyad1mm(a,b); }\n\ninline tens4dmm dyad4mm(const mat3dd& as, const mat3ds& b) { mat3ds a(as); return dyad4mm(a,b); }\ninline tens4dmm dyad4mm(const mat3ds& a, const mat3dd& bs) { mat3ds b(bs); return dyad4mm(a,b); }\ninline tens4dmm dyad4mm(const mat3dd& as, const mat3dd& bs) { mat3ds a(as); mat3ds b(bs); return dyad4mm(a,b); }\n\n// other common operations\ntens4dmm ddot(const tens4dmm& a, const tens4dmm& b);\ntens4dmm ddot(const tens4dmm& a, const tens4ds& b);\ninline mat3ds ddot(const tens4dmm& a, const mat3ds& m) { return a.dot(m); }\ninline mat3ds ddot(const tens4dmm& a, const mat3dd& m) { return a.dot(m); }\ninline tens4dmm operator * (const double g, const tens4dmm& a) { return a*g; }\nmat3d vdotTdotv(const vec3d& a, const tens4dmm& T, const vec3d& b);\n\n// The following file contains the actual definition of the class functions\n#include \"tens4dmm.hpp\"\n\n//-----------------------------------------------------------------------------\n//! Class for 4th order tensors without symmetry\n\n// We store components of this tensor as a 9x9 matrix.\n// The tensor is stored in column major order:\n//\n//       00  11  22   01   12   20   10   21   02    |\n//       --------------------------------------------+---\n//     / 0   9   18   27   36   45   54   63   72  \\ | 00\n//     | 1   10  19   28   37   46   55   64   73  | | 11\n//     | 2   11  20   29   38   47   56   65   74  | | 22\n// A = | 3   12  21   30   39   48   57   66   75  | | 01\n//     | 4   13  22   31   40   49   58   67   76  | | 12\n//     | 5   14  23   32   41   50   59   68   77  | | 20\n//     | 6   15  24   33   42   51   60   69   78  | | 10\n//     | 7   16  25   34   43   52   61   70   79  | | 21\n//     \\ 8   17  26   35   44   53   62   71   80  / | 02\n//\n\ntemplate <> class tensor_traits<tens4d> {public: enum { NNZ = 81}; };\n\nclass tens4d : public tensor_base<tens4d>\n{\npublic:\n\t// constructors\n\ttens4d() {}\n    explicit tens4d(const double g);\n    tens4d(tens4ds t);\n    tens4d(tens4dmm t);\n    tens4d(double m[9][9]);\n\npublic:\n\t// access operators\n\tdouble& operator () (int i, int j, int k, int l);\n\tdouble operator () (int i, int j, int k, int l) const;\n\nprivate:\n\tdouble& operator () (int i, int j);\n\tdouble operator () (int i, int j) const;\n    \npublic:\n    // arithmetic operators\n    tens4d operator + (const tens4d& t) const;\n    tens4d operator - (const tens4d& t) const;\n    tens4d operator + (const tens4ds& t) const;\n    tens4d operator - (const tens4ds& t) const;\n    tens4d operator * (double g) const;\n    tens4d operator / (double g) const;\n\n    // arithmetic assignment operators\n    tens4d& operator += (const tens4d& t);\n    tens4d& operator -= (const tens4d& t);\n    tens4d& operator += (const tens4ds& t);\n    tens4d& operator -= (const tens4ds& t);\n    tens4d& operator *= (double g);\n    tens4d& operator /= (double g);\n\n    // unary operators\n    tens4d operator - () const;\n    \n    // double dot product with 2nd order tensor\n    mat3d dot(const mat3d& m) const;\n    mat3d dot(const mat3ds& m) const { return dot(mat3d(m)); }\n    mat3d dot(const mat3dd& m) const { return dot(mat3d(m)); }\n\n    // single dot product with 2nd order tensor\n    tens4d sdot(const mat3d& m) const;\n    tens4d sdot(const mat3ds& m) const { return sdot(mat3d(m)); };\n    tens4d sdot(const mat3dd& m) const { return sdot(mat3d(m)); };\n\n    // trace\n    double tr() const;\n    \n    // initialize to zero\n    void zero();\n\n    // extract 9x9 matrix\n    void extract(double d[9][9]);\n\n    // compute the super-symmetric (major and minor symmetric) component of the tensor\n    tens4ds supersymm() const;\n\n    // compute the major transpose (Sijkl -> Sklij)\n    tens4d transpose() const;\n\n    // compute the left transpose (Sijkl -> Sjikl)\n    tens4d left_transpose() const;\n\n    // compute the right transpose (Sijkl -> Sijlk)\n    tens4d right_transpose() const;\n\n    // calculates the inverse\n    tens4d inverse() const;\n    \n    // evaluate push/pull operation\n//    tens4d pp(const mat3d& F);\n\n};\n\n// dyadic products of second-order tensors\ntens4d dyad1(const mat3d& a, const mat3d& b);\ntens4d dyad2(const mat3d& a, const mat3d& b);\ntens4d dyad3(const mat3d& a, const mat3d& b);\ninline tens4d dyad4(const mat3d& a, const mat3d& b) { return (dyad2(a,b) + dyad3(a,b))/2; }\n\ninline tens4d dyad1(const mat3ds& as, const mat3d& b) { mat3d a(as); return dyad1(a,b); }\ninline tens4d dyad1(const mat3d& a, const mat3ds& bs) { mat3d b(bs); return dyad1(a,b); }\ninline tens4d dyad1(const mat3ds& as, const mat3ds& bs) { mat3d a(as); mat3d b(bs); return dyad1(a,b); }\ninline tens4d dyad2(const mat3ds& as, const mat3d& b) { mat3d a(as); return dyad2(a,b); }\ninline tens4d dyad2(const mat3d& a, const mat3ds& bs) { mat3d b(bs); return dyad2(a,b); }\ninline tens4d dyad2(const mat3ds& as, const mat3ds& bs) { mat3d a(as); mat3d b(bs); return dyad2(a,b); }\ninline tens4d dyad3(const mat3ds& as, const mat3d& b) { mat3d a(as); return dyad3(a,b); }\ninline tens4d dyad3(const mat3d& a, const mat3ds& bs) { mat3d b(bs); return dyad3(a,b); }\ninline tens4d dyad3(const mat3ds& as, const mat3ds& bs) { mat3d a(as); mat3d b(bs); return dyad3(a,b); }\ninline tens4d dyad4(const mat3ds& as, const mat3d& b) { mat3d a(as); return dyad4(a,b); }\ninline tens4d dyad4(const mat3d& a, const mat3ds& bs) { mat3d b(bs); return dyad4(a,b); }\ninline tens4d dyad4(const mat3ds& as, const mat3ds& bs) { mat3d a(as); mat3d b(bs); return dyad4(a,b); }\n\ninline tens4d dyad1(const mat3dd& as, const mat3d& b) { mat3d a(as); return dyad1(a,b); }\ninline tens4d dyad1(const mat3d& a, const mat3dd& bs) { mat3d b(bs); return dyad1(a,b); }\ninline tens4d dyad1(const mat3dd& as, const mat3dd& bs) { mat3d a(as); mat3d b(bs); return dyad1(a,b); }\ninline tens4d dyad2(const mat3dd& as, const mat3d& b) { mat3d a(as); return dyad2(a,b); }\ninline tens4d dyad2(const mat3d& a, const mat3dd& bs) { mat3d b(bs); return dyad2(a,b); }\ninline tens4d dyad2(const mat3dd& as, const mat3dd& bs) { mat3d a(as); mat3d b(bs); return dyad2(a,b); }\ninline tens4d dyad3(const mat3dd& as, const mat3d& b) { mat3d a(as); return dyad3(a,b); }\ninline tens4d dyad3(const mat3d& a, const mat3dd& bs) { mat3d b(bs); return dyad3(a,b); }\ninline tens4d dyad3(const mat3dd& as, const mat3dd& bs) { mat3d a(as); mat3d b(bs); return dyad3(a,b); }\ninline tens4d dyad4(const mat3dd& as, const mat3d& b) { mat3d a(as); return dyad4(a,b); }\ninline tens4d dyad4(const mat3d& a, const mat3dd& bs) { mat3d b(bs); return dyad4(a,b); }\ninline tens4d dyad4(const mat3dd& as, const mat3dd& bs) { mat3d a(as); mat3d b(bs); return dyad4(a,b); }\n\ninline tens4d dyad1(const mat3dd& ad, const mat3ds& bs) { mat3d a(ad); mat3d b(bs); return dyad1(a,b); }\ninline tens4d dyad1(const mat3ds& as, const mat3dd& bd) { mat3d a(as); mat3d b(bd); return dyad1(a,b); }\ninline tens4d dyad2(const mat3dd& ad, const mat3ds& bs) { mat3d a(ad); mat3d b(bs); return dyad2(a,b); }\ninline tens4d dyad2(const mat3ds& as, const mat3dd& bd) { mat3d a(as); mat3d b(bd); return dyad2(a,b); }\ninline tens4d dyad3(const mat3dd& ad, const mat3ds& bs) { mat3d a(ad); mat3d b(bs); return dyad3(a,b); }\ninline tens4d dyad3(const mat3ds& as, const mat3dd& bd) { mat3d a(as); mat3d b(bd); return dyad3(a,b); }\ninline tens4d dyad4(const mat3dd& ad, const mat3ds& bs) { mat3d a(ad); mat3d b(bs); return dyad4(a,b); }\ninline tens4d dyad4(const mat3ds& as, const mat3dd& bd) { mat3d a(as); mat3d b(bd); return dyad4(a,b); }\n// other common operations\nmat3d vdotTdotv(const vec3d& a, const tens4d& T, const vec3d& b);\ntens4d ddot(const tens4d& a, const tens4d& b);\ntens4d ddot(const tens4d& a, const tens4ds& b);\ninline mat3d ddot(const tens4d& a, const mat3d& m) { return a.dot(m); }\ninline mat3d ddot(const tens4d& a, const mat3ds& m) { return a.dot(m); }\ninline mat3d ddot(const tens4d& a, const mat3dd& m) { return a.dot(m); }\ninline tens4d operator * (const double g, const tens4d& a) { return a*g; }\n\nclass tens4fs\n{\npublic:\n\tenum { NNZ = 21 };\n\n\t// default constructor\n\ttens4fs() {}\n\ttens4fs(const float g)\n\t{\n\t\td[0] = g;\n\t\td[1] = g; d[2] = g;\n\t\td[3] = g; d[4] = g; d[5] = g;\n\t\td[6] = g; d[7] = g; d[8] = g; d[9] = g;\n\t\td[10] = g; d[11] = g; d[12] = g; d[13] = g; d[14] = g;\n\t\td[15] = g; d[16] = g; d[17] = g; d[18] = g; d[19] = g; d[20] = g;\n\t}\n\n\ttens4fs(float m[6][6])\n\t{\n\t\td[0] = m[0][0];\n\t\td[1] = m[0][1]; d[2] = m[1][1];\n\t\td[3] = m[0][2]; d[4] = m[1][2]; d[5] = m[2][2];\n\t\td[6] = m[0][3]; d[7] = m[1][3]; d[8] = m[2][3]; d[9] = m[3][3];\n\t\td[10] = m[0][4]; d[11] = m[1][4]; d[12] = m[2][4]; d[13] = m[3][4]; d[14] = m[4][4];\n\t\td[15] = m[0][5]; d[16] = m[1][5]; d[17] = m[2][5]; d[18] = m[3][5]; d[19] = m[4][5]; d[20] = m[5][5];\n\t}\n\n\ttens4fs(float D[21])\n\t{\n\t\td[0] = D[0];\n\t\td[1] = D[1]; d[2] = D[2];\n\t\td[3] = D[3]; d[4] = D[4]; d[5] = D[5];\n\t\td[6] = D[6]; d[7] = D[7]; d[8] = D[8]; d[9] = D[9];\n\t\td[10] = D[10]; d[11] = D[11]; d[12] = D[12]; d[13] = D[13]; d[14] = D[14];\n\t\td[15] = D[15]; d[16] = D[16]; d[17] = D[17]; d[18] = D[18]; d[19] = D[19]; d[20] = D[20];\n\t}\n\n\tfloat& operator () (int i, int j, int k, int l)\n\t{\n\t\tconst int m[3][3] = { { 0,3,5 },{ 3,1,4 },{ 5,4,2 } };\n\t\ttens4fs& T = (*this);\n\t\treturn T(m[i][j], m[k][l]);\n\t}\n\n\tfloat operator () (int i, int j, int k, int l) const\n\t{\n\t\tconst int m[3][3] = { { 0,3,5 },{ 3,1,4 },{ 5,4,2 } };\n\t\tconst tens4fs& T = (*this);\n\t\treturn T(m[i][j], m[k][l]);\n\t}\n\n\tfloat& operator () (int i, int j)\n\t{\n\t\tconst int m[6] = { 0, 1, 3, 6, 10, 15 };\n\t\tif (i <= j) return d[m[j] + i]; else return d[m[i] + j];\n\t}\n\n\tfloat operator () (int i, int j) const\n\t{\n\t\tconst int m[6] = { 0, 1, 3, 6, 10, 15 };\n\t\tif (i <= j) return d[m[j] + i]; else return d[m[i] + j];\n\t}\n\n\t// arithmetic operators\n\ttens4fs operator + (const tens4fs& t) const {\n\t\ttens4fs s;\n\t\tfor (int i = 0; i < NNZ; i++) s.d[i] = d[i] + t.d[i];\n\t\treturn s;\n\t}\n\ttens4fs operator - (const tens4fs& t) const {\n\t\ttens4fs s;\n\t\tfor (int i = 0; i < NNZ; i++) s.d[i] = d[i] - t.d[i];\n\t\treturn s;\n\t}\n\ttens4fs operator * (float g) const {\n\t\ttens4fs s;\n\t\tfor (int i = 0; i < NNZ; i++) s.d[i] = g * d[i];\n\t\treturn s;\n\t}\n\ttens4fs operator / (float g) const {\n\t\ttens4fs s;\n\t\tfor (int i = 0; i < NNZ; i++) s.d[i] = d[i] / g;\n\t\treturn s;\n\t}\n\n\t// arithmetic assignment operators\n\ttens4fs& operator += (const tens4fs& t) {\n\t\tfor (int i = 0; i < NNZ; i++) d[i] += t.d[i];\n\t\treturn (*this);\n\t}\n\ttens4fs& operator -= (const tens4fs& t) {\n\t\tfor (int i = 0; i < NNZ; i++) d[i] -= t.d[i];\n\t\treturn (*this);\n\t}\n\ttens4fs& operator *= (float g) {\n\t\tfor (int i = 0; i < NNZ; i++) d[i] *= g;\n\t\treturn (*this);\n\t}\n\ttens4fs& operator /= (float g) {\n\t\tfor (int i = 0; i < NNZ; i++) d[i] /= g;\n\t\treturn (*this);\n\t}\n\n\t// unary operators\n\ttens4fs operator - () const {\n\t\ttens4fs s;\n\t\tfor (int i = 0; i < NNZ; i++) s.d[i] = -d[i];\n\t\treturn s;\n\t}\n\n\t// double dot product with tensor\n\tmat3fs dot(const mat3fs& m) const {\n\t\tmat3fs a;\n\t\ta.x = d[0] * m.x + d[1] * m.y + d[3] * m.z + 2 * d[6] * m.xy + 2 * d[10] * m.yz + 2 * d[15] * m.xz;\n\t\ta.y = d[1] * m.x + d[2] * m.y + d[4] * m.z + 2 * d[7] * m.xy + 2 * d[11] * m.yz + 2 * d[16] * m.xz;\n\t\ta.z = d[3] * m.x + d[4] * m.y + d[5] * m.z + 2 * d[8] * m.xy + 2 * d[12] * m.yz + 2 * d[17] * m.xz;\n\t\ta.xy = d[6] * m.x + d[7] * m.y + d[8] * m.z + 2 * d[9] * m.xy + 2 * d[13] * m.yz + 2 * d[18] * m.xz;\n\t\ta.yz = d[10] * m.x + d[11] * m.y + d[12] * m.z + 2 * d[13] * m.xy + 2 * d[14] * m.yz + 2 * d[19] * m.xz;\n\t\ta.xz = d[15] * m.x + d[16] * m.y + d[17] * m.z + 2 * d[18] * m.xy + 2 * d[19] * m.yz + 2 * d[20] * m.xz;\n\t\treturn a;\n\t}\n\n\t// trace\n\tfloat tr() const {\n\t\treturn (d[0] + d[2] + d[5] + 2 * (d[1] + d[3] + d[4]));\n\t}\n\n\t// initialize to zero\n\tvoid zero() {\n\t\td[0] = d[1] = d[2] = d[3] = d[4] = d[5] = d[6] = d[7] = d[8] = d[9] =\n\t\t\td[10] = d[11] = d[12] = d[13] = d[14] = d[15] = d[16] = d[17] = d[18] = d[19] = d[20] = 0;\n\t}\n\n\t// extract 6x6 Matrix\n\tvoid extract(float D[6][6]) {\n\t\tD[0][0] = d[0];  D[0][1] = d[1];  D[0][2] = d[3];  D[0][3] = d[6];  D[0][4] = d[10]; D[0][5] = d[15];\n\t\tD[1][0] = d[1];  D[1][1] = d[2];  D[1][2] = d[4];  D[1][3] = d[7];  D[1][4] = d[11]; D[1][5] = d[16];\n\t\tD[2][0] = d[3];  D[2][1] = d[4];  D[2][2] = d[5];  D[2][3] = d[8];  D[2][4] = d[12]; D[2][5] = d[17];\n\t\tD[3][0] = d[6];  D[3][1] = d[7];  D[3][2] = d[8];  D[3][3] = d[9];  D[3][4] = d[13]; D[3][5] = d[18];\n\t\tD[4][0] = d[10]; D[4][1] = d[11]; D[4][2] = d[12]; D[4][3] = d[13]; D[4][4] = d[14]; D[4][5] = d[19];\n\t\tD[5][0] = d[15]; D[5][1] = d[16]; D[5][2] = d[17]; D[5][3] = d[18]; D[5][4] = d[19]; D[5][5] = d[20];\n\t}\n\n\t// calculates the inverse\n\t//\ttens4fs inverse() const;\n\npublic:\n\tfloat d[NNZ];\t// stored in column major order\n};\n\n// The following file contains the actual definition of the class functions\n#include \"tens4d.hpp\"\n"
  },
  {
    "path": "FECore/tens4d.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens4d.h\n// Users should not include this file manually!\n\ninline tens4d::tens4d(const double g)\n{\n    d[ 0] = d[ 1] = d[ 2] = d[ 3] = d[ 4] = d[ 5] = d[ 6] = d[ 7] = d[ 8] = g;\n    d[ 9] = d[10] = d[11] = d[12] = d[13] = d[14] = d[15] = d[16] = d[17] = g;\n    d[18] = d[19] = d[20] = d[21] = d[22] = d[23] = d[24] = d[25] = d[26] = g;\n    d[27] = d[28] = d[29] = d[30] = d[31] = d[32] = d[33] = d[34] = d[35] = g;\n    d[36] = d[37] = d[38] = d[39] = d[40] = d[41] = d[42] = d[43] = d[44] = g;\n    d[45] = d[46] = d[47] = d[48] = d[49] = d[50] = d[51] = d[52] = d[53] = g;\n    d[54] = d[55] = d[56] = d[57] = d[58] = d[59] = d[60] = d[61] = d[62] = g;\n    d[63] = d[64] = d[65] = d[66] = d[67] = d[68] = d[69] = d[70] = d[71] = g;\n    d[72] = d[73] = d[74] = d[75] = d[76] = d[77] = d[78] = d[79] = d[80] = g;\n}\n\ninline tens4d::tens4d(const tens4ds t)\n{\n    d[ 0]=t.d[ 0]; d[ 9]=t.d[ 1]; d[18]=t.d[ 3]; d[27]=t.d[ 6]; d[36]=t.d[10]; d[45]=t.d[15]; d[54]=t.d[ 6]; d[63]=t.d[10]; d[72]=t.d[15];\n    d[ 1]=t.d[ 1]; d[10]=t.d[ 2]; d[19]=t.d[ 4]; d[28]=t.d[ 7]; d[37]=t.d[11]; d[46]=t.d[16]; d[55]=t.d[ 7]; d[64]=t.d[11]; d[73]=t.d[16];\n    d[ 2]=t.d[ 3]; d[11]=t.d[ 4]; d[20]=t.d[ 5]; d[29]=t.d[ 8]; d[38]=t.d[12]; d[47]=t.d[17]; d[56]=t.d[ 8]; d[65]=t.d[12]; d[74]=t.d[17];\n    d[ 3]=t.d[ 6]; d[12]=t.d[ 7]; d[21]=t.d[ 8]; d[30]=t.d[ 9]; d[39]=t.d[13]; d[48]=t.d[18]; d[57]=t.d[ 9]; d[66]=t.d[13]; d[75]=t.d[18];\n    d[ 4]=t.d[10]; d[13]=t.d[11]; d[22]=t.d[12]; d[31]=t.d[13]; d[40]=t.d[14]; d[49]=t.d[19]; d[58]=t.d[13]; d[67]=t.d[14]; d[76]=t.d[19];\n    d[ 5]=t.d[15]; d[14]=t.d[16]; d[23]=t.d[17]; d[32]=t.d[18]; d[41]=t.d[19]; d[50]=t.d[20]; d[59]=t.d[18]; d[68]=t.d[19]; d[77]=t.d[20];\n    d[ 6]=t.d[ 6]; d[15]=t.d[ 7]; d[24]=t.d[ 8]; d[33]=t.d[ 9]; d[42]=t.d[13]; d[51]=t.d[18]; d[60]=t.d[ 9]; d[69]=t.d[13]; d[78]=t.d[18];\n    d[ 7]=t.d[10]; d[16]=t.d[11]; d[25]=t.d[12]; d[34]=t.d[13]; d[43]=t.d[14]; d[52]=t.d[19]; d[61]=t.d[13]; d[70]=t.d[14]; d[79]=t.d[19];\n    d[ 8]=t.d[15]; d[17]=t.d[16]; d[26]=t.d[17]; d[35]=t.d[18]; d[44]=t.d[19]; d[53]=t.d[20]; d[62]=t.d[18]; d[71]=t.d[19]; d[80]=t.d[20];\n}\n\ninline tens4d::tens4d(const tens4dmm t)\n{\n    d[ 0]=t.d[ 0]; d[ 9]=t.d[ 6]; d[18]=t.d[12]; d[27]=t.d[18]; d[36]=t.d[24]; d[45]=t.d[30]; d[54]=t.d[18]; d[63]=t.d[24]; d[72]=t.d[30];\n    d[ 1]=t.d[ 1]; d[10]=t.d[ 7]; d[19]=t.d[13]; d[28]=t.d[19]; d[37]=t.d[25]; d[46]=t.d[31]; d[55]=t.d[19]; d[64]=t.d[25]; d[73]=t.d[31];\n    d[ 2]=t.d[ 2]; d[11]=t.d[ 8]; d[20]=t.d[14]; d[29]=t.d[20]; d[38]=t.d[26]; d[47]=t.d[32]; d[56]=t.d[20]; d[65]=t.d[26]; d[74]=t.d[32];\n    d[ 3]=t.d[ 3]; d[12]=t.d[ 9]; d[21]=t.d[15]; d[30]=t.d[21]; d[39]=t.d[27]; d[48]=t.d[33]; d[57]=t.d[21]; d[66]=t.d[27]; d[75]=t.d[33];\n    d[ 4]=t.d[ 4]; d[13]=t.d[10]; d[22]=t.d[16]; d[31]=t.d[22]; d[40]=t.d[28]; d[49]=t.d[34]; d[58]=t.d[22]; d[67]=t.d[28]; d[76]=t.d[34];\n    d[ 5]=t.d[ 5]; d[14]=t.d[11]; d[23]=t.d[17]; d[32]=t.d[23]; d[41]=t.d[29]; d[50]=t.d[35]; d[59]=t.d[23]; d[68]=t.d[29]; d[77]=t.d[35];\n    d[ 6]=t.d[ 3]; d[15]=t.d[ 9]; d[24]=t.d[15]; d[33]=t.d[21]; d[42]=t.d[27]; d[51]=t.d[33]; d[60]=t.d[21]; d[69]=t.d[27]; d[78]=t.d[33];\n    d[ 7]=t.d[ 4]; d[16]=t.d[10]; d[25]=t.d[16]; d[34]=t.d[22]; d[43]=t.d[28]; d[52]=t.d[34]; d[61]=t.d[22]; d[70]=t.d[28]; d[79]=t.d[34];\n    d[ 8]=t.d[ 5]; d[17]=t.d[11]; d[26]=t.d[17]; d[35]=t.d[23]; d[44]=t.d[29]; d[53]=t.d[35]; d[62]=t.d[23]; d[71]=t.d[29]; d[80]=t.d[35];\n}\n\ninline tens4d::tens4d(double m[9][9])\n{\n    d[ 0]=m[0][0]; d[ 9]=m[0][1]; d[18]=m[0][2]; d[27]=m[0][3]; d[36]=m[0][4]; d[45]=m[0][5]; d[54]=m[0][6]; d[63]=m[0][7]; d[72]=m[0][8];\n    d[ 1]=m[1][0]; d[10]=m[1][1]; d[19]=m[1][2]; d[28]=m[1][3]; d[37]=m[1][4]; d[46]=m[1][5]; d[55]=m[1][6]; d[64]=m[1][7]; d[73]=m[1][8];\n    d[ 2]=m[2][0]; d[11]=m[2][1]; d[20]=m[2][2]; d[29]=m[2][3]; d[38]=m[2][4]; d[47]=m[2][5]; d[56]=m[2][6]; d[65]=m[2][7]; d[74]=m[2][8];\n    d[ 3]=m[3][0]; d[12]=m[3][1]; d[21]=m[3][2]; d[30]=m[3][3]; d[39]=m[3][4]; d[48]=m[3][5]; d[57]=m[3][6]; d[66]=m[3][7]; d[75]=m[3][8];\n    d[ 4]=m[4][0]; d[13]=m[4][1]; d[22]=m[4][2]; d[31]=m[4][3]; d[40]=m[4][4]; d[49]=m[4][5]; d[58]=m[4][6]; d[67]=m[4][7]; d[76]=m[4][8];\n    d[ 5]=m[5][0]; d[14]=m[5][1]; d[23]=m[5][2]; d[32]=m[5][3]; d[41]=m[5][4]; d[50]=m[5][5]; d[59]=m[5][6]; d[68]=m[5][7]; d[77]=m[5][8];\n    d[ 6]=m[6][0]; d[15]=m[6][1]; d[24]=m[6][2]; d[33]=m[6][3]; d[42]=m[6][4]; d[51]=m[6][5]; d[60]=m[6][6]; d[69]=m[6][7]; d[78]=m[6][8];\n    d[ 7]=m[7][0]; d[16]=m[7][1]; d[25]=m[7][2]; d[34]=m[7][3]; d[43]=m[7][4]; d[52]=m[7][5]; d[61]=m[7][6]; d[70]=m[7][7]; d[79]=m[7][8];\n    d[ 8]=m[8][0]; d[17]=m[8][1]; d[26]=m[8][2]; d[35]=m[8][3]; d[44]=m[8][4]; d[53]=m[8][5]; d[62]=m[8][6]; d[71]=m[8][7]; d[80]=m[8][8];\n}\n\ninline double& tens4d::operator () (int i, int j, int k, int l)\n{\n\tconst int m[3][3] = {{0,3,8},{6,1,4},{5,7,2}};\n\ttens4d& T = (*this);\n\treturn T(m[i][j], m[k][l]);\n}\n\ninline double tens4d::operator () (int i, int j, int k, int l) const\n{\n\tconst int m[3][3] = {{0,3,8},{6,1,4},{5,7,2}};\n\tconst tens4d& T = (*this);\n\treturn T(m[i][j], m[k][l]);\n}\n\ninline double& tens4d::operator () (int i, int j)\n{\n\tconst int m[9] = {0, 9, 18, 27, 36, 45, 54, 63, 72};\n\treturn d[m[j]+i];\n}\n\ninline double tens4d::operator () (int i, int j) const\n{\n\tconst int m[9] = {0, 9, 18, 27, 36, 45, 54, 63, 72};\n\treturn d[m[j]+i];\n}\n\n//-----------------------------------------------------------------------------\n// operator +\ninline tens4d tens4d::operator + (const tens4d& t) const\n{\n    tens4d s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = d[i] + t.d[i];\n    s.d[ 0] = d[ 0] + t.d[ 0];    s.d[27] = d[27] + t.d[27];    s.d[54] = d[54] + t.d[54];\n    s.d[ 1] = d[ 1] + t.d[ 1];    s.d[28] = d[28] + t.d[28];    s.d[55] = d[55] + t.d[55];\n    s.d[ 2] = d[ 2] + t.d[ 2];    s.d[29] = d[29] + t.d[29];    s.d[56] = d[56] + t.d[56];\n    s.d[ 3] = d[ 3] + t.d[ 3];    s.d[30] = d[30] + t.d[30];    s.d[57] = d[57] + t.d[57];\n    s.d[ 4] = d[ 4] + t.d[ 4];    s.d[31] = d[31] + t.d[31];    s.d[58] = d[58] + t.d[58];\n    s.d[ 5] = d[ 5] + t.d[ 5];    s.d[32] = d[32] + t.d[32];    s.d[59] = d[59] + t.d[59];\n    s.d[ 6] = d[ 6] + t.d[ 6];    s.d[33] = d[33] + t.d[33];    s.d[60] = d[60] + t.d[60];\n    s.d[ 7] = d[ 7] + t.d[ 7];    s.d[34] = d[34] + t.d[34];    s.d[61] = d[61] + t.d[61];\n    s.d[ 8] = d[ 8] + t.d[ 8];    s.d[35] = d[35] + t.d[35];    s.d[62] = d[62] + t.d[62];\n    s.d[ 9] = d[ 9] + t.d[ 9];    s.d[36] = d[36] + t.d[36];    s.d[63] = d[63] + t.d[63];\n    s.d[10] = d[10] + t.d[10];    s.d[37] = d[37] + t.d[37];    s.d[64] = d[64] + t.d[64];\n    s.d[11] = d[11] + t.d[11];    s.d[38] = d[38] + t.d[38];    s.d[65] = d[65] + t.d[65];\n    s.d[12] = d[12] + t.d[12];    s.d[39] = d[39] + t.d[39];    s.d[66] = d[66] + t.d[66];\n    s.d[13] = d[13] + t.d[13];    s.d[40] = d[40] + t.d[40];    s.d[67] = d[67] + t.d[67];\n    s.d[14] = d[14] + t.d[14];    s.d[41] = d[41] + t.d[41];    s.d[68] = d[68] + t.d[68];\n    s.d[15] = d[15] + t.d[15];    s.d[42] = d[42] + t.d[42];    s.d[69] = d[69] + t.d[69];\n    s.d[16] = d[16] + t.d[16];    s.d[43] = d[43] + t.d[43];    s.d[70] = d[70] + t.d[70];\n    s.d[17] = d[17] + t.d[17];    s.d[44] = d[44] + t.d[44];    s.d[71] = d[71] + t.d[71];\n    s.d[18] = d[18] + t.d[18];    s.d[45] = d[45] + t.d[45];    s.d[72] = d[72] + t.d[72];\n    s.d[19] = d[19] + t.d[19];    s.d[46] = d[46] + t.d[46];    s.d[73] = d[73] + t.d[73];\n    s.d[20] = d[20] + t.d[20];    s.d[47] = d[47] + t.d[47];    s.d[74] = d[74] + t.d[74];\n    s.d[21] = d[21] + t.d[21];    s.d[48] = d[48] + t.d[48];    s.d[75] = d[75] + t.d[75];\n    s.d[22] = d[22] + t.d[22];    s.d[49] = d[49] + t.d[49];    s.d[76] = d[76] + t.d[76];\n    s.d[23] = d[23] + t.d[23];    s.d[50] = d[50] + t.d[50];    s.d[77] = d[77] + t.d[77];\n    s.d[24] = d[24] + t.d[24];    s.d[51] = d[51] + t.d[51];    s.d[78] = d[78] + t.d[78];\n    s.d[25] = d[25] + t.d[25];    s.d[52] = d[52] + t.d[52];    s.d[79] = d[79] + t.d[79];\n    s.d[26] = d[26] + t.d[26];    s.d[53] = d[53] + t.d[53];    s.d[80] = d[80] + t.d[80];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator -\ninline tens4d tens4d::operator - (const tens4d& t) const\n{\n    tens4d s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = d[i] - t.d[i];\n    s.d[ 0] = d[ 0] - t.d[ 0];    s.d[27] = d[27] - t.d[27];    s.d[54] = d[54] - t.d[54];\n    s.d[ 1] = d[ 1] - t.d[ 1];    s.d[28] = d[28] - t.d[28];    s.d[55] = d[55] - t.d[55];\n    s.d[ 2] = d[ 2] - t.d[ 2];    s.d[29] = d[29] - t.d[29];    s.d[56] = d[56] - t.d[56];\n    s.d[ 3] = d[ 3] - t.d[ 3];    s.d[30] = d[30] - t.d[30];    s.d[57] = d[57] - t.d[57];\n    s.d[ 4] = d[ 4] - t.d[ 4];    s.d[31] = d[31] - t.d[31];    s.d[58] = d[58] - t.d[58];\n    s.d[ 5] = d[ 5] - t.d[ 5];    s.d[32] = d[32] - t.d[32];    s.d[59] = d[59] - t.d[59];\n    s.d[ 6] = d[ 6] - t.d[ 6];    s.d[33] = d[33] - t.d[33];    s.d[60] = d[60] - t.d[60];\n    s.d[ 7] = d[ 7] - t.d[ 7];    s.d[34] = d[34] - t.d[34];    s.d[61] = d[61] - t.d[61];\n    s.d[ 8] = d[ 8] - t.d[ 8];    s.d[35] = d[35] - t.d[35];    s.d[62] = d[62] - t.d[62];\n    s.d[ 9] = d[ 9] - t.d[ 9];    s.d[36] = d[36] - t.d[36];    s.d[63] = d[63] - t.d[63];\n    s.d[10] = d[10] - t.d[10];    s.d[37] = d[37] - t.d[37];    s.d[64] = d[64] - t.d[64];\n    s.d[11] = d[11] - t.d[11];    s.d[38] = d[38] - t.d[38];    s.d[65] = d[65] - t.d[65];\n    s.d[12] = d[12] - t.d[12];    s.d[39] = d[39] - t.d[39];    s.d[66] = d[66] - t.d[66];\n    s.d[13] = d[13] - t.d[13];    s.d[40] = d[40] - t.d[40];    s.d[67] = d[67] - t.d[67];\n    s.d[14] = d[14] - t.d[14];    s.d[41] = d[41] - t.d[41];    s.d[68] = d[68] - t.d[68];\n    s.d[15] = d[15] - t.d[15];    s.d[42] = d[42] - t.d[42];    s.d[69] = d[69] - t.d[69];\n    s.d[16] = d[16] - t.d[16];    s.d[43] = d[43] - t.d[43];    s.d[70] = d[70] - t.d[70];\n    s.d[17] = d[17] - t.d[17];    s.d[44] = d[44] - t.d[44];    s.d[71] = d[71] - t.d[71];\n    s.d[18] = d[18] - t.d[18];    s.d[45] = d[45] - t.d[45];    s.d[72] = d[72] - t.d[72];\n    s.d[19] = d[19] - t.d[19];    s.d[46] = d[46] - t.d[46];    s.d[73] = d[73] - t.d[73];\n    s.d[20] = d[20] - t.d[20];    s.d[47] = d[47] - t.d[47];    s.d[74] = d[74] - t.d[74];\n    s.d[21] = d[21] - t.d[21];    s.d[48] = d[48] - t.d[48];    s.d[75] = d[75] - t.d[75];\n    s.d[22] = d[22] - t.d[22];    s.d[49] = d[49] - t.d[49];    s.d[76] = d[76] - t.d[76];\n    s.d[23] = d[23] - t.d[23];    s.d[50] = d[50] - t.d[50];    s.d[77] = d[77] - t.d[77];\n    s.d[24] = d[24] - t.d[24];    s.d[51] = d[51] - t.d[51];    s.d[78] = d[78] - t.d[78];\n    s.d[25] = d[25] - t.d[25];    s.d[52] = d[52] - t.d[52];    s.d[79] = d[79] - t.d[79];\n    s.d[26] = d[26] - t.d[26];    s.d[53] = d[53] - t.d[53];    s.d[80] = d[80] - t.d[80];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator + tens4ds\ninline tens4d tens4d::operator + (const tens4ds& t) const\n{\n    tens4d s;\n    s.d[ 0] = d[ 0] + t.d[ 0];    s.d[27] = d[27] + t.d[ 6];    s.d[54] = d[54] + t.d[ 6];\n    s.d[ 1] = d[ 1] + t.d[ 1];    s.d[28] = d[28] + t.d[ 7];    s.d[55] = d[55] + t.d[ 7];\n    s.d[ 2] = d[ 2] + t.d[ 3];    s.d[29] = d[29] + t.d[ 8];    s.d[56] = d[56] + t.d[ 8];\n    s.d[ 3] = d[ 3] + t.d[ 6];    s.d[30] = d[30] + t.d[ 9];    s.d[57] = d[57] + t.d[ 9];\n    s.d[ 4] = d[ 4] + t.d[10];    s.d[31] = d[31] + t.d[13];    s.d[58] = d[58] + t.d[13];\n    s.d[ 5] = d[ 5] + t.d[15];    s.d[32] = d[32] + t.d[18];    s.d[59] = d[59] + t.d[18];\n    s.d[ 6] = d[ 6] + t.d[ 6];    s.d[33] = d[33] + t.d[ 9];    s.d[60] = d[60] + t.d[9];\n    s.d[ 7] = d[ 7] + t.d[10];    s.d[34] = d[34] + t.d[13];    s.d[61] = d[61] + t.d[13];\n    s.d[ 8] = d[ 8] + t.d[15];    s.d[35] = d[35] + t.d[18];    s.d[62] = d[62] + t.d[18];\n    s.d[ 9] = d[ 9] + t.d[ 1];    s.d[36] = d[36] + t.d[10];    s.d[63] = d[63] + t.d[10];\n    s.d[10] = d[10] + t.d[ 2];    s.d[37] = d[37] + t.d[11];    s.d[64] = d[64] + t.d[11];\n    s.d[11] = d[11] + t.d[ 4];    s.d[38] = d[38] + t.d[12];    s.d[65] = d[65] + t.d[12];\n    s.d[12] = d[12] + t.d[ 7];    s.d[39] = d[39] + t.d[13];    s.d[66] = d[66] + t.d[13];\n    s.d[13] = d[13] + t.d[11];    s.d[40] = d[40] + t.d[14];    s.d[67] = d[67] + t.d[14];\n    s.d[14] = d[14] + t.d[16];    s.d[41] = d[41] + t.d[19];    s.d[68] = d[68] + t.d[19];\n    s.d[15] = d[15] + t.d[ 7];    s.d[42] = d[42] + t.d[13];    s.d[69] = d[69] + t.d[13];\n    s.d[16] = d[16] + t.d[11];    s.d[43] = d[43] + t.d[14];    s.d[70] = d[70] + t.d[14];\n    s.d[17] = d[17] + t.d[16];    s.d[44] = d[44] + t.d[19];    s.d[71] = d[71] + t.d[19];\n    s.d[18] = d[18] + t.d[ 3];    s.d[45] = d[45] + t.d[15];    s.d[72] = d[72] + t.d[15];\n    s.d[19] = d[19] + t.d[ 4];    s.d[46] = d[46] + t.d[16];    s.d[73] = d[73] + t.d[16];\n    s.d[20] = d[20] + t.d[ 5];    s.d[47] = d[47] + t.d[17];    s.d[74] = d[74] + t.d[17];\n    s.d[21] = d[21] + t.d[ 8];    s.d[48] = d[48] + t.d[18];    s.d[75] = d[75] + t.d[18];\n    s.d[22] = d[22] + t.d[12];    s.d[49] = d[49] + t.d[19];    s.d[76] = d[76] + t.d[19];\n    s.d[23] = d[23] + t.d[17];    s.d[50] = d[50] + t.d[20];    s.d[77] = d[77] + t.d[20];\n    s.d[24] = d[24] + t.d[ 8];    s.d[51] = d[51] + t.d[18];    s.d[78] = d[78] + t.d[18];\n    s.d[25] = d[25] + t.d[12];    s.d[52] = d[52] + t.d[19];    s.d[79] = d[79] + t.d[19];\n    s.d[26] = d[26] + t.d[17];    s.d[53] = d[53] + t.d[20];    s.d[80] = d[80] + t.d[20];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator - tens4ds\ninline tens4d tens4d::operator - (const tens4ds& t) const\n{\n    tens4d s;\n    s.d[ 0] = d[ 0] - t.d[ 0];    s.d[27] = d[27] - t.d[ 6];    s.d[54] = d[54] - t.d[ 6];\n    s.d[ 1] = d[ 1] - t.d[ 1];    s.d[28] = d[28] - t.d[ 7];    s.d[55] = d[55] - t.d[ 7];\n    s.d[ 2] = d[ 2] - t.d[ 3];    s.d[29] = d[29] - t.d[ 8];    s.d[56] = d[56] - t.d[ 8];\n    s.d[ 3] = d[ 3] - t.d[ 6];    s.d[30] = d[30] - t.d[ 9];    s.d[57] = d[57] - t.d[ 9];\n    s.d[ 4] = d[ 4] - t.d[10];    s.d[31] = d[31] - t.d[13];    s.d[58] = d[58] - t.d[13];\n    s.d[ 5] = d[ 5] - t.d[15];    s.d[32] = d[32] - t.d[18];    s.d[59] = d[59] - t.d[18];\n    s.d[ 6] = d[ 6] - t.d[ 6];    s.d[33] = d[33] - t.d[ 9];    s.d[60] = d[60] - t.d[9];\n    s.d[ 7] = d[ 7] - t.d[10];    s.d[34] = d[34] - t.d[13];    s.d[61] = d[61] - t.d[13];\n    s.d[ 8] = d[ 8] - t.d[15];    s.d[35] = d[35] - t.d[18];    s.d[62] = d[62] - t.d[18];\n    s.d[ 9] = d[ 9] - t.d[ 1];    s.d[36] = d[36] - t.d[10];    s.d[63] = d[63] - t.d[10];\n    s.d[10] = d[10] - t.d[ 2];    s.d[37] = d[37] - t.d[11];    s.d[64] = d[64] - t.d[11];\n    s.d[11] = d[11] - t.d[ 4];    s.d[38] = d[38] - t.d[12];    s.d[65] = d[65] - t.d[12];\n    s.d[12] = d[12] - t.d[ 7];    s.d[39] = d[39] - t.d[13];    s.d[66] = d[66] - t.d[13];\n    s.d[13] = d[13] - t.d[11];    s.d[40] = d[40] - t.d[14];    s.d[67] = d[67] - t.d[14];\n    s.d[14] = d[14] - t.d[16];    s.d[41] = d[41] - t.d[19];    s.d[68] = d[68] - t.d[19];\n    s.d[15] = d[15] - t.d[ 7];    s.d[42] = d[42] - t.d[13];    s.d[69] = d[69] - t.d[13];\n    s.d[16] = d[16] - t.d[11];    s.d[43] = d[43] - t.d[14];    s.d[70] = d[70] - t.d[14];\n    s.d[17] = d[17] - t.d[16];    s.d[44] = d[44] - t.d[19];    s.d[71] = d[71] - t.d[19];\n    s.d[18] = d[18] - t.d[ 3];    s.d[45] = d[45] - t.d[15];    s.d[72] = d[72] - t.d[15];\n    s.d[19] = d[19] - t.d[ 4];    s.d[46] = d[46] - t.d[16];    s.d[73] = d[73] - t.d[16];\n    s.d[20] = d[20] - t.d[ 5];    s.d[47] = d[47] - t.d[17];    s.d[74] = d[74] - t.d[17];\n    s.d[21] = d[21] - t.d[ 8];    s.d[48] = d[48] - t.d[18];    s.d[75] = d[75] - t.d[18];\n    s.d[22] = d[22] - t.d[12];    s.d[49] = d[49] - t.d[19];    s.d[76] = d[76] - t.d[19];\n    s.d[23] = d[23] - t.d[17];    s.d[50] = d[50] - t.d[20];    s.d[77] = d[77] - t.d[20];\n    s.d[24] = d[24] - t.d[ 8];    s.d[51] = d[51] - t.d[18];    s.d[78] = d[78] - t.d[18];\n    s.d[25] = d[25] - t.d[12];    s.d[52] = d[52] - t.d[19];    s.d[79] = d[79] - t.d[19];\n    s.d[26] = d[26] - t.d[17];    s.d[53] = d[53] - t.d[20];    s.d[80] = d[80] - t.d[20];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator *\ninline tens4d tens4d::operator * (double g) const\n{\n    tens4d s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = g*d[i];\n    s.d[ 0] = g*d[ 0];    s.d[27] = g*d[27];    s.d[54] = g*d[54];\n    s.d[ 1] = g*d[ 1];    s.d[28] = g*d[28];    s.d[55] = g*d[55];\n    s.d[ 2] = g*d[ 2];    s.d[29] = g*d[29];    s.d[56] = g*d[56];\n    s.d[ 3] = g*d[ 3];    s.d[30] = g*d[30];    s.d[57] = g*d[57];\n    s.d[ 4] = g*d[ 4];    s.d[31] = g*d[31];    s.d[58] = g*d[58];\n    s.d[ 5] = g*d[ 5];    s.d[32] = g*d[32];    s.d[59] = g*d[59];\n    s.d[ 6] = g*d[ 6];    s.d[33] = g*d[33];    s.d[60] = g*d[60];\n    s.d[ 7] = g*d[ 7];    s.d[34] = g*d[34];    s.d[61] = g*d[61];\n    s.d[ 8] = g*d[ 8];    s.d[35] = g*d[35];    s.d[62] = g*d[62];\n    s.d[ 9] = g*d[ 9];    s.d[36] = g*d[36];    s.d[63] = g*d[63];\n    s.d[10] = g*d[10];    s.d[37] = g*d[37];    s.d[64] = g*d[64];\n    s.d[11] = g*d[11];    s.d[38] = g*d[38];    s.d[65] = g*d[65];\n    s.d[12] = g*d[12];    s.d[39] = g*d[39];    s.d[66] = g*d[66];\n    s.d[13] = g*d[13];    s.d[40] = g*d[40];    s.d[67] = g*d[67];\n    s.d[14] = g*d[14];    s.d[41] = g*d[41];    s.d[68] = g*d[68];\n    s.d[15] = g*d[15];    s.d[42] = g*d[42];    s.d[69] = g*d[69];\n    s.d[16] = g*d[16];    s.d[43] = g*d[43];    s.d[70] = g*d[70];\n    s.d[17] = g*d[17];    s.d[44] = g*d[44];    s.d[71] = g*d[71];\n    s.d[18] = g*d[18];    s.d[45] = g*d[45];    s.d[72] = g*d[72];\n    s.d[19] = g*d[19];    s.d[46] = g*d[46];    s.d[73] = g*d[73];\n    s.d[20] = g*d[20];    s.d[47] = g*d[47];    s.d[74] = g*d[74];\n    s.d[21] = g*d[21];    s.d[48] = g*d[48];    s.d[75] = g*d[75];\n    s.d[22] = g*d[22];    s.d[49] = g*d[49];    s.d[76] = g*d[76];\n    s.d[23] = g*d[23];    s.d[50] = g*d[50];    s.d[77] = g*d[77];\n    s.d[24] = g*d[24];    s.d[51] = g*d[51];    s.d[78] = g*d[78];\n    s.d[25] = g*d[25];    s.d[52] = g*d[52];    s.d[79] = g*d[79];\n    s.d[26] = g*d[26];    s.d[53] = g*d[53];    s.d[80] = g*d[80];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator /\ninline tens4d tens4d::operator / (double g) const\n{\n    tens4d s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = d[i]/g;\n    s.d[ 0] = d[ 0]/g;    s.d[27] = d[27]/g;    s.d[54] = d[54]/g;\n    s.d[ 1] = d[ 1]/g;    s.d[28] = d[28]/g;    s.d[55] = d[55]/g;\n    s.d[ 2] = d[ 2]/g;    s.d[29] = d[29]/g;    s.d[56] = d[56]/g;\n    s.d[ 3] = d[ 3]/g;    s.d[30] = d[30]/g;    s.d[57] = d[57]/g;\n    s.d[ 4] = d[ 4]/g;    s.d[31] = d[31]/g;    s.d[58] = d[58]/g;\n    s.d[ 5] = d[ 5]/g;    s.d[32] = d[32]/g;    s.d[59] = d[59]/g;\n    s.d[ 6] = d[ 6]/g;    s.d[33] = d[33]/g;    s.d[60] = d[60]/g;\n    s.d[ 7] = d[ 7]/g;    s.d[34] = d[34]/g;    s.d[61] = d[61]/g;\n    s.d[ 8] = d[ 8]/g;    s.d[35] = d[35]/g;    s.d[62] = d[62]/g;\n    s.d[ 9] = d[ 9]/g;    s.d[36] = d[36]/g;    s.d[63] = d[63]/g;\n    s.d[10] = d[10]/g;    s.d[37] = d[37]/g;    s.d[64] = d[64]/g;\n    s.d[11] = d[11]/g;    s.d[38] = d[38]/g;    s.d[65] = d[65]/g;\n    s.d[12] = d[12]/g;    s.d[39] = d[39]/g;    s.d[66] = d[66]/g;\n    s.d[13] = d[13]/g;    s.d[40] = d[40]/g;    s.d[67] = d[67]/g;\n    s.d[14] = d[14]/g;    s.d[41] = d[41]/g;    s.d[68] = d[68]/g;\n    s.d[15] = d[15]/g;    s.d[42] = d[42]/g;    s.d[69] = d[69]/g;\n    s.d[16] = d[16]/g;    s.d[43] = d[43]/g;    s.d[70] = d[70]/g;\n    s.d[17] = d[17]/g;    s.d[44] = d[44]/g;    s.d[71] = d[71]/g;\n    s.d[18] = d[18]/g;    s.d[45] = d[45]/g;    s.d[72] = d[72]/g;\n    s.d[19] = d[19]/g;    s.d[46] = d[46]/g;    s.d[73] = d[73]/g;\n    s.d[20] = d[20]/g;    s.d[47] = d[47]/g;    s.d[74] = d[74]/g;\n    s.d[21] = d[21]/g;    s.d[48] = d[48]/g;    s.d[75] = d[75]/g;\n    s.d[22] = d[22]/g;    s.d[49] = d[49]/g;    s.d[76] = d[76]/g;\n    s.d[23] = d[23]/g;    s.d[50] = d[50]/g;    s.d[77] = d[77]/g;\n    s.d[24] = d[24]/g;    s.d[51] = d[51]/g;    s.d[78] = d[78]/g;\n    s.d[25] = d[25]/g;    s.d[52] = d[52]/g;    s.d[79] = d[79]/g;\n    s.d[26] = d[26]/g;    s.d[53] = d[53]/g;    s.d[80] = d[80]/g;\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator +=\ninline tens4d& tens4d::operator += (const tens4d& t)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] += t.d[i];\n    d[ 0] += t.d[ 0];    d[27] += t.d[27];    d[54] += t.d[54];\n    d[ 1] += t.d[ 1];    d[28] += t.d[28];    d[55] += t.d[55];\n    d[ 2] += t.d[ 2];    d[29] += t.d[29];    d[56] += t.d[56];\n    d[ 3] += t.d[ 3];    d[30] += t.d[30];    d[57] += t.d[57];\n    d[ 4] += t.d[ 4];    d[31] += t.d[31];    d[58] += t.d[58];\n    d[ 5] += t.d[ 5];    d[32] += t.d[32];    d[59] += t.d[59];\n    d[ 6] += t.d[ 6];    d[33] += t.d[33];    d[60] += t.d[60];\n    d[ 7] += t.d[ 7];    d[34] += t.d[34];    d[61] += t.d[61];\n    d[ 8] += t.d[ 8];    d[35] += t.d[35];    d[62] += t.d[62];\n    d[ 9] += t.d[ 9];    d[36] += t.d[36];    d[63] += t.d[63];\n    d[10] += t.d[10];    d[37] += t.d[37];    d[64] += t.d[64];\n    d[11] += t.d[11];    d[38] += t.d[38];    d[65] += t.d[65];\n    d[12] += t.d[12];    d[39] += t.d[39];    d[66] += t.d[66];\n    d[13] += t.d[13];    d[40] += t.d[40];    d[67] += t.d[67];\n    d[14] += t.d[14];    d[41] += t.d[41];    d[68] += t.d[68];\n    d[15] += t.d[15];    d[42] += t.d[42];    d[69] += t.d[69];\n    d[16] += t.d[16];    d[43] += t.d[43];    d[70] += t.d[70];\n    d[17] += t.d[17];    d[44] += t.d[44];    d[71] += t.d[71];\n    d[18] += t.d[18];    d[45] += t.d[45];    d[72] += t.d[72];\n    d[19] += t.d[19];    d[46] += t.d[46];    d[73] += t.d[73];\n    d[20] += t.d[20];    d[47] += t.d[47];    d[74] += t.d[74];\n    d[21] += t.d[21];    d[48] += t.d[48];    d[75] += t.d[75];\n    d[22] += t.d[22];    d[49] += t.d[49];    d[76] += t.d[76];\n    d[23] += t.d[23];    d[50] += t.d[50];    d[77] += t.d[77];\n    d[24] += t.d[24];    d[51] += t.d[51];    d[78] += t.d[78];\n    d[25] += t.d[25];    d[52] += t.d[52];    d[79] += t.d[79];\n    d[26] += t.d[26];    d[53] += t.d[53];    d[80] += t.d[80];\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator -=\ninline tens4d& tens4d::operator -= (const tens4d& t)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] -= t.d[i];\n    d[ 0] -= t.d[ 0];    d[27] -= t.d[27];    d[54] -= t.d[54];\n    d[ 1] -= t.d[ 1];    d[28] -= t.d[28];    d[55] -= t.d[55];\n    d[ 2] -= t.d[ 2];    d[29] -= t.d[29];    d[56] -= t.d[56];\n    d[ 3] -= t.d[ 3];    d[30] -= t.d[30];    d[57] -= t.d[57];\n    d[ 4] -= t.d[ 4];    d[31] -= t.d[31];    d[58] -= t.d[58];\n    d[ 5] -= t.d[ 5];    d[32] -= t.d[32];    d[59] -= t.d[59];\n    d[ 6] -= t.d[ 6];    d[33] -= t.d[33];    d[60] -= t.d[60];\n    d[ 7] -= t.d[ 7];    d[34] -= t.d[34];    d[61] -= t.d[61];\n    d[ 8] -= t.d[ 8];    d[35] -= t.d[35];    d[62] -= t.d[62];\n    d[ 9] -= t.d[ 9];    d[36] -= t.d[36];    d[63] -= t.d[63];\n    d[10] -= t.d[10];    d[37] -= t.d[37];    d[64] -= t.d[64];\n    d[11] -= t.d[11];    d[38] -= t.d[38];    d[65] -= t.d[65];\n    d[12] -= t.d[12];    d[39] -= t.d[39];    d[66] -= t.d[66];\n    d[13] -= t.d[13];    d[40] -= t.d[40];    d[67] -= t.d[67];\n    d[14] -= t.d[14];    d[41] -= t.d[41];    d[68] -= t.d[68];\n    d[15] -= t.d[15];    d[42] -= t.d[42];    d[69] -= t.d[69];\n    d[16] -= t.d[16];    d[43] -= t.d[43];    d[70] -= t.d[70];\n    d[17] -= t.d[17];    d[44] -= t.d[44];    d[71] -= t.d[71];\n    d[18] -= t.d[18];    d[45] -= t.d[45];    d[72] -= t.d[72];\n    d[19] -= t.d[19];    d[46] -= t.d[46];    d[73] -= t.d[73];\n    d[20] -= t.d[20];    d[47] -= t.d[47];    d[74] -= t.d[74];\n    d[21] -= t.d[21];    d[48] -= t.d[48];    d[75] -= t.d[75];\n    d[22] -= t.d[22];    d[49] -= t.d[49];    d[76] -= t.d[76];\n    d[23] -= t.d[23];    d[50] -= t.d[50];    d[77] -= t.d[77];\n    d[24] -= t.d[24];    d[51] -= t.d[51];    d[78] -= t.d[78];\n    d[25] -= t.d[25];    d[52] -= t.d[52];    d[79] -= t.d[79];\n    d[26] -= t.d[26];    d[53] -= t.d[53];    d[80] -= t.d[80];\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator += tens4ds\ninline tens4d& tens4d::operator += (const tens4ds& t)\n{\n    d[ 0] += t.d[ 0];    d[27] += t.d[ 6];    d[54] += t.d[ 6];\n    d[ 1] += t.d[ 1];    d[28] += t.d[ 7];    d[55] += t.d[ 7];\n    d[ 2] += t.d[ 3];    d[29] += t.d[ 8];    d[56] += t.d[ 8];\n    d[ 3] += t.d[ 6];    d[30] += t.d[ 9];    d[57] += t.d[ 9];\n    d[ 4] += t.d[10];    d[31] += t.d[13];    d[58] += t.d[13];\n    d[ 5] += t.d[15];    d[32] += t.d[18];    d[59] += t.d[18];\n    d[ 6] += t.d[ 6];    d[33] += t.d[ 9];    d[60] += t.d[ 9];\n    d[ 7] += t.d[10];    d[34] += t.d[13];    d[61] += t.d[13];\n    d[ 8] += t.d[15];    d[35] += t.d[18];    d[62] += t.d[18];\n    d[ 9] += t.d[ 1];    d[36] += t.d[10];    d[63] += t.d[10];\n    d[10] += t.d[ 2];    d[37] += t.d[11];    d[64] += t.d[11];\n    d[11] += t.d[ 4];    d[38] += t.d[12];    d[65] += t.d[12];\n    d[12] += t.d[ 7];    d[39] += t.d[13];    d[66] += t.d[13];\n    d[13] += t.d[11];    d[40] += t.d[14];    d[67] += t.d[14];\n    d[14] += t.d[16];    d[41] += t.d[19];    d[68] += t.d[19];\n    d[15] += t.d[ 7];    d[42] += t.d[13];    d[69] += t.d[13];\n    d[16] += t.d[11];    d[43] += t.d[14];    d[70] += t.d[14];\n    d[17] += t.d[16];    d[44] += t.d[19];    d[71] += t.d[19];\n    d[18] += t.d[ 3];    d[45] += t.d[15];    d[72] += t.d[15];\n    d[19] += t.d[ 4];    d[46] += t.d[16];    d[73] += t.d[16];\n    d[20] += t.d[ 5];    d[47] += t.d[17];    d[74] += t.d[17];\n    d[21] += t.d[ 8];    d[48] += t.d[18];    d[75] += t.d[18];\n    d[22] += t.d[12];    d[49] += t.d[19];    d[76] += t.d[19];\n    d[23] += t.d[17];    d[50] += t.d[20];    d[77] += t.d[20];\n    d[24] += t.d[ 8];    d[51] += t.d[18];    d[78] += t.d[18];\n    d[25] += t.d[12];    d[52] += t.d[19];    d[79] += t.d[19];\n    d[26] += t.d[17];    d[53] += t.d[20];    d[80] += t.d[20];\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator -= tens4ds\ninline tens4d& tens4d::operator -= (const tens4ds& t)\n{\n    d[ 0] -= t.d[ 0];    d[27] -= t.d[ 6];    d[54] -= t.d[ 6];\n    d[ 1] -= t.d[ 1];    d[28] -= t.d[ 7];    d[55] -= t.d[ 7];\n    d[ 2] -= t.d[ 3];    d[29] -= t.d[ 8];    d[56] -= t.d[ 8];\n    d[ 3] -= t.d[ 6];    d[30] -= t.d[ 9];    d[57] -= t.d[ 9];\n    d[ 4] -= t.d[10];    d[31] -= t.d[13];    d[58] -= t.d[13];\n    d[ 5] -= t.d[15];    d[32] -= t.d[18];    d[59] -= t.d[18];\n    d[ 6] -= t.d[ 6];    d[33] -= t.d[ 9];    d[60] -= t.d[ 9];\n    d[ 7] -= t.d[10];    d[34] -= t.d[13];    d[61] -= t.d[13];\n    d[ 8] -= t.d[15];    d[35] -= t.d[18];    d[62] -= t.d[18];\n    d[ 9] -= t.d[ 1];    d[36] -= t.d[10];    d[63] -= t.d[10];\n    d[10] -= t.d[ 2];    d[37] -= t.d[11];    d[64] -= t.d[11];\n    d[11] -= t.d[ 4];    d[38] -= t.d[12];    d[65] -= t.d[12];\n    d[12] -= t.d[ 7];    d[39] -= t.d[13];    d[66] -= t.d[13];\n    d[13] -= t.d[11];    d[40] -= t.d[14];    d[67] -= t.d[14];\n    d[14] -= t.d[16];    d[41] -= t.d[19];    d[68] -= t.d[19];\n    d[15] -= t.d[ 7];    d[42] -= t.d[13];    d[69] -= t.d[13];\n    d[16] -= t.d[11];    d[43] -= t.d[14];    d[70] -= t.d[14];\n    d[17] -= t.d[16];    d[44] -= t.d[19];    d[71] -= t.d[19];\n    d[18] -= t.d[ 3];    d[45] -= t.d[15];    d[72] -= t.d[15];\n    d[19] -= t.d[ 4];    d[46] -= t.d[16];    d[73] -= t.d[16];\n    d[20] -= t.d[ 5];    d[47] -= t.d[17];    d[74] -= t.d[17];\n    d[21] -= t.d[ 8];    d[48] -= t.d[18];    d[75] -= t.d[18];\n    d[22] -= t.d[12];    d[49] -= t.d[19];    d[76] -= t.d[19];\n    d[23] -= t.d[17];    d[50] -= t.d[20];    d[77] -= t.d[20];\n    d[24] -= t.d[ 8];    d[51] -= t.d[18];    d[78] -= t.d[18];\n    d[25] -= t.d[12];    d[52] -= t.d[19];    d[79] -= t.d[19];\n    d[26] -= t.d[17];    d[53] -= t.d[20];    d[80] -= t.d[20];\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator *=\ninline tens4d& tens4d::operator *= (double g)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] *= g;\n    d[ 0] *= g;    d[27] *= g;    d[54] *= g;\n    d[ 1] *= g;    d[28] *= g;    d[55] *= g;\n    d[ 2] *= g;    d[29] *= g;    d[56] *= g;\n    d[ 3] *= g;    d[30] *= g;    d[57] *= g;\n    d[ 4] *= g;    d[31] *= g;    d[58] *= g;\n    d[ 5] *= g;    d[32] *= g;    d[59] *= g;\n    d[ 6] *= g;    d[33] *= g;    d[60] *= g;\n    d[ 7] *= g;    d[34] *= g;    d[61] *= g;\n    d[ 8] *= g;    d[35] *= g;    d[62] *= g;\n    d[ 9] *= g;    d[36] *= g;    d[63] *= g;\n    d[10] *= g;    d[37] *= g;    d[64] *= g;\n    d[11] *= g;    d[38] *= g;    d[65] *= g;\n    d[12] *= g;    d[39] *= g;    d[66] *= g;\n    d[13] *= g;    d[40] *= g;    d[67] *= g;\n    d[14] *= g;    d[41] *= g;    d[68] *= g;\n    d[15] *= g;    d[42] *= g;    d[69] *= g;\n    d[16] *= g;    d[43] *= g;    d[70] *= g;\n    d[17] *= g;    d[44] *= g;    d[71] *= g;\n    d[18] *= g;    d[45] *= g;    d[72] *= g;\n    d[19] *= g;    d[46] *= g;    d[73] *= g;\n    d[20] *= g;    d[47] *= g;    d[74] *= g;\n    d[21] *= g;    d[48] *= g;    d[75] *= g;\n    d[22] *= g;    d[49] *= g;    d[76] *= g;\n    d[23] *= g;    d[50] *= g;    d[77] *= g;\n    d[24] *= g;    d[51] *= g;    d[78] *= g;\n    d[25] *= g;    d[52] *= g;    d[79] *= g;\n    d[26] *= g;    d[53] *= g;    d[80] *= g;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator /=\ninline tens4d& tens4d::operator /= (double g)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] /= g;\n    d[ 0] /= g;    d[27] /= g;    d[54] /= g;\n    d[ 1] /= g;    d[28] /= g;    d[55] /= g;\n    d[ 2] /= g;    d[29] /= g;    d[56] /= g;\n    d[ 3] /= g;    d[30] /= g;    d[57] /= g;\n    d[ 4] /= g;    d[31] /= g;    d[58] /= g;\n    d[ 5] /= g;    d[32] /= g;    d[59] /= g;\n    d[ 6] /= g;    d[33] /= g;    d[60] /= g;\n    d[ 7] /= g;    d[34] /= g;    d[61] /= g;\n    d[ 8] /= g;    d[35] /= g;    d[62] /= g;\n    d[ 9] /= g;    d[36] /= g;    d[63] /= g;\n    d[10] /= g;    d[37] /= g;    d[64] /= g;\n    d[11] /= g;    d[38] /= g;    d[65] /= g;\n    d[12] /= g;    d[39] /= g;    d[66] /= g;\n    d[13] /= g;    d[40] /= g;    d[67] /= g;\n    d[14] /= g;    d[41] /= g;    d[68] /= g;\n    d[15] /= g;    d[42] /= g;    d[69] /= g;\n    d[16] /= g;    d[43] /= g;    d[70] /= g;\n    d[17] /= g;    d[44] /= g;    d[71] /= g;\n    d[18] /= g;    d[45] /= g;    d[72] /= g;\n    d[19] /= g;    d[46] /= g;    d[73] /= g;\n    d[20] /= g;    d[47] /= g;    d[74] /= g;\n    d[21] /= g;    d[48] /= g;    d[75] /= g;\n    d[22] /= g;    d[49] /= g;    d[76] /= g;\n    d[23] /= g;    d[50] /= g;    d[77] /= g;\n    d[24] /= g;    d[51] /= g;    d[78] /= g;\n    d[25] /= g;    d[52] /= g;    d[79] /= g;\n    d[26] /= g;    d[53] /= g;    d[80] /= g;\n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// unary operator -\ninline tens4d tens4d::operator - () const\n{\n    tens4d s;\n    s.d[ 0] = -d[ 0];    s.d[27] = -d[27];    s.d[54] = -d[54];\n    s.d[ 1] = -d[ 1];    s.d[28] = -d[28];    s.d[55] = -d[55];\n    s.d[ 2] = -d[ 2];    s.d[29] = -d[29];    s.d[56] = -d[56];\n    s.d[ 3] = -d[ 3];    s.d[30] = -d[30];    s.d[57] = -d[57];\n    s.d[ 4] = -d[ 4];    s.d[31] = -d[31];    s.d[58] = -d[58];\n    s.d[ 5] = -d[ 5];    s.d[32] = -d[32];    s.d[59] = -d[59];\n    s.d[ 6] = -d[ 6];    s.d[33] = -d[33];    s.d[60] = -d[60];\n    s.d[ 7] = -d[ 7];    s.d[34] = -d[34];    s.d[61] = -d[61];\n    s.d[ 8] = -d[ 8];    s.d[35] = -d[35];    s.d[62] = -d[62];\n    s.d[ 9] = -d[ 9];    s.d[36] = -d[36];    s.d[63] = -d[63];\n    s.d[10] = -d[10];    s.d[37] = -d[37];    s.d[64] = -d[64];\n    s.d[11] = -d[11];    s.d[38] = -d[38];    s.d[65] = -d[65];\n    s.d[12] = -d[12];    s.d[39] = -d[39];    s.d[66] = -d[66];\n    s.d[13] = -d[13];    s.d[40] = -d[40];    s.d[67] = -d[67];\n    s.d[14] = -d[14];    s.d[41] = -d[41];    s.d[68] = -d[68];\n    s.d[15] = -d[15];    s.d[42] = -d[42];    s.d[69] = -d[69];\n    s.d[16] = -d[16];    s.d[43] = -d[43];    s.d[70] = -d[70];\n    s.d[17] = -d[17];    s.d[44] = -d[44];    s.d[71] = -d[71];\n    s.d[18] = -d[18];    s.d[45] = -d[45];    s.d[72] = -d[72];\n    s.d[19] = -d[19];    s.d[46] = -d[46];    s.d[73] = -d[73];\n    s.d[20] = -d[20];    s.d[47] = -d[47];    s.d[74] = -d[74];\n    s.d[21] = -d[21];    s.d[48] = -d[48];    s.d[75] = -d[75];\n    s.d[22] = -d[22];    s.d[49] = -d[49];    s.d[76] = -d[76];\n    s.d[23] = -d[23];    s.d[50] = -d[50];    s.d[77] = -d[77];\n    s.d[24] = -d[24];    s.d[51] = -d[51];    s.d[78] = -d[78];\n    s.d[25] = -d[25];    s.d[52] = -d[52];    s.d[79] = -d[79];\n    s.d[26] = -d[26];    s.d[53] = -d[53];    s.d[80] = -d[80];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of general 4th-order tensor with a general 2nd-order tensor\n// Aij = Dijkl Mkl\ninline mat3d tens4d::dot(const mat3d &m) const\n{\n    mat3d a;\n    \n    a(0,0) = d[0]*m(0,0) + d[27]*m(0,1) + d[72]*m(0,2) + d[54]*m(1,0) + d[ 9]*m(1,1) + d[36]*m(1,2) + d[45]*m(2,0) + d[63]*m(2,1) + d[18]*m(2,2);\n    a(0,1) = d[1]*m(0,0) + d[28]*m(0,1) + d[73]*m(0,2) + d[55]*m(1,0) + d[10]*m(1,1) + d[37]*m(1,2) + d[46]*m(2,0) + d[64]*m(2,1) + d[19]*m(2,2);\n    a(0,2) = d[2]*m(0,0) + d[29]*m(0,1) + d[74]*m(0,2) + d[56]*m(1,0) + d[11]*m(1,1) + d[38]*m(1,2) + d[47]*m(2,0) + d[65]*m(2,1) + d[20]*m(2,2);\n    a(1,0) = d[3]*m(0,0) + d[30]*m(0,1) + d[75]*m(0,2) + d[57]*m(1,0) + d[12]*m(1,1) + d[39]*m(1,2) + d[48]*m(2,0) + d[66]*m(2,1) + d[21]*m(2,2);\n    a(1,1) = d[4]*m(0,0) + d[31]*m(0,1) + d[76]*m(0,2) + d[58]*m(1,0) + d[13]*m(1,1) + d[40]*m(1,2) + d[49]*m(2,0) + d[67]*m(2,1) + d[22]*m(2,2);\n    a(1,2) = d[5]*m(0,0) + d[32]*m(0,1) + d[77]*m(0,2) + d[59]*m(1,0) + d[14]*m(1,1) + d[41]*m(1,2) + d[50]*m(2,0) + d[68]*m(2,1) + d[23]*m(2,2);\n    a(2,0) = d[6]*m(0,0) + d[33]*m(0,1) + d[78]*m(0,2) + d[60]*m(1,0) + d[15]*m(1,1) + d[42]*m(1,2) + d[51]*m(2,0) + d[69]*m(2,1) + d[24]*m(2,2);\n    a(2,1) = d[7]*m(0,0) + d[34]*m(0,1) + d[79]*m(0,2) + d[61]*m(1,0) + d[16]*m(1,1) + d[43]*m(1,2) + d[52]*m(2,0) + d[70]*m(2,1) + d[25]*m(2,2);\n    a(2,2) = d[8]*m(0,0) + d[35]*m(0,1) + d[80]*m(0,2) + d[62]*m(1,0) + d[17]*m(1,1) + d[44]*m(1,2) + d[53]*m(2,0) + d[71]*m(2,1) + d[26]*m(2,2);\n    \n    return a;\n}\n\n// single dot product with 2nd order tensor\ninline tens4d tens4d::sdot(const mat3d& m) const\n{\n    tens4d s;\n    s.d[ 0] = d[0]*m(0,0) + d[27]*m(1,0) + d[72]*m(2,0);\n    s.d[ 1] = d[1]*m(0,0) + d[28]*m(1,0) + d[73]*m(2,0);\n    s.d[ 2] = d[2]*m(0,0) + d[29]*m(1,0) + d[74]*m(2,0);\n    s.d[ 3] = d[3]*m(0,0) + d[30]*m(1,0) + d[75]*m(2,0);\n    s.d[ 4] = d[4]*m(0,0) + d[31]*m(1,0) + d[76]*m(2,0);\n    s.d[ 5] = d[5]*m(0,0) + d[32]*m(1,0) + d[77]*m(2,0);\n    s.d[ 6] = d[6]*m(0,0) + d[33]*m(1,0) + d[78]*m(2,0);\n    s.d[ 7] = d[7]*m(0,0) + d[34]*m(1,0) + d[79]*m(2,0);\n    s.d[ 8] = d[8]*m(0,0) + d[35]*m(1,0) + d[80]*m(2,0);\n\n    s.d[ 9] = d[54]*m(0,1) + d[ 9]*m(1,1) + d[36]*m(2,1);\n    s.d[10] = d[55]*m(0,1) + d[10]*m(1,1) + d[37]*m(2,1);\n    s.d[11] = d[56]*m(0,1) + d[11]*m(1,1) + d[38]*m(2,1);\n    s.d[12] = d[57]*m(0,1) + d[12]*m(1,1) + d[39]*m(2,1);\n    s.d[13] = d[58]*m(0,1) + d[13]*m(1,1) + d[40]*m(2,1);\n    s.d[14] = d[59]*m(0,1) + d[14]*m(1,1) + d[41]*m(2,1);\n    s.d[15] = d[60]*m(0,1) + d[15]*m(1,1) + d[42]*m(2,1);\n    s.d[16] = d[61]*m(0,1) + d[16]*m(1,1) + d[43]*m(2,1);\n    s.d[17] = d[62]*m(0,1) + d[17]*m(1,1) + d[44]*m(2,1);\n\n    s.d[18] = d[45]*m(0,2) + d[63]*m(1,2) + d[18]*m(2,2);\n    s.d[19] = d[46]*m(0,2) + d[64]*m(1,2) + d[19]*m(2,2);\n    s.d[20] = d[47]*m(0,2) + d[65]*m(1,2) + d[20]*m(2,2);\n    s.d[21] = d[48]*m(0,2) + d[66]*m(1,2) + d[21]*m(2,2);\n    s.d[22] = d[49]*m(0,2) + d[67]*m(1,2) + d[22]*m(2,2);\n    s.d[23] = d[50]*m(0,2) + d[68]*m(1,2) + d[23]*m(2,2);\n    s.d[24] = d[51]*m(0,2) + d[69]*m(1,2) + d[24]*m(2,2);\n    s.d[25] = d[52]*m(0,2) + d[70]*m(1,2) + d[25]*m(2,2);\n    s.d[26] = d[53]*m(0,2) + d[71]*m(1,2) + d[26]*m(2,2);\n\n    s.d[27] = d[0]*m(0,1) + d[27]*m(1,1) + d[72]*m(2,1);\n    s.d[28] = d[1]*m(0,1) + d[28]*m(1,1) + d[73]*m(2,1);\n    s.d[29] = d[2]*m(0,1) + d[29]*m(1,1) + d[74]*m(2,1);\n    s.d[30] = d[3]*m(0,1) + d[30]*m(1,1) + d[75]*m(2,1);\n    s.d[31] = d[4]*m(0,1) + d[31]*m(1,1) + d[76]*m(2,1);\n    s.d[32] = d[5]*m(0,1) + d[32]*m(1,1) + d[77]*m(2,1);\n    s.d[33] = d[6]*m(0,1) + d[33]*m(1,1) + d[78]*m(2,1);\n    s.d[34] = d[7]*m(0,1) + d[34]*m(1,1) + d[79]*m(2,1);\n    s.d[35] = d[8]*m(0,1) + d[35]*m(1,1) + d[80]*m(2,1);\n\n    s.d[36] = d[54]*m(0,2) + d[ 9]*m(1,2) + d[36]*m(2,2);\n    s.d[37] = d[55]*m(0,2) + d[10]*m(1,2) + d[37]*m(2,2);\n    s.d[38] = d[56]*m(0,2) + d[11]*m(1,2) + d[38]*m(2,2);\n    s.d[39] = d[57]*m(0,2) + d[12]*m(1,2) + d[39]*m(2,2);\n    s.d[40] = d[58]*m(0,2) + d[13]*m(1,2) + d[40]*m(2,2);\n    s.d[41] = d[59]*m(0,2) + d[14]*m(1,2) + d[41]*m(2,2);\n    s.d[42] = d[60]*m(0,2) + d[15]*m(1,2) + d[42]*m(2,2);\n    s.d[43] = d[61]*m(0,2) + d[16]*m(1,2) + d[43]*m(2,2);\n    s.d[44] = d[62]*m(0,2) + d[17]*m(1,2) + d[44]*m(2,2);\n\n    s.d[45] = d[45]*m(0,0) + d[63]*m(1,0) + d[18]*m(2,0);\n    s.d[46] = d[46]*m(0,0) + d[64]*m(1,0) + d[19]*m(2,0);\n    s.d[47] = d[47]*m(0,0) + d[65]*m(1,0) + d[20]*m(2,0);\n    s.d[48] = d[48]*m(0,0) + d[66]*m(1,0) + d[21]*m(2,0);\n    s.d[49] = d[49]*m(0,0) + d[67]*m(1,0) + d[22]*m(2,0);\n    s.d[50] = d[50]*m(0,0) + d[68]*m(1,0) + d[23]*m(2,0);\n    s.d[51] = d[51]*m(0,0) + d[69]*m(1,0) + d[24]*m(2,0);\n    s.d[52] = d[52]*m(0,0) + d[70]*m(1,0) + d[25]*m(2,0);\n    s.d[53] = d[53]*m(0,0) + d[71]*m(1,0) + d[26]*m(2,0);\n\n    s.d[54] = d[54]*m(0,0) + d[ 9]*m(1,0) + d[36]*m(2,0);\n    s.d[55] = d[55]*m(0,0) + d[10]*m(1,0) + d[37]*m(2,0);\n    s.d[56] = d[56]*m(0,0) + d[11]*m(1,0) + d[38]*m(2,0);\n    s.d[57] = d[57]*m(0,0) + d[12]*m(1,0) + d[39]*m(2,0);\n    s.d[58] = d[58]*m(0,0) + d[13]*m(1,0) + d[40]*m(2,0);\n    s.d[59] = d[59]*m(0,0) + d[14]*m(1,0) + d[41]*m(2,0);\n    s.d[60] = d[60]*m(0,0) + d[15]*m(1,0) + d[42]*m(2,0);\n    s.d[61] = d[61]*m(0,0) + d[16]*m(1,0) + d[43]*m(2,0);\n    s.d[62] = d[62]*m(0,0) + d[17]*m(1,0) + d[44]*m(2,0);\n\n    s.d[63] = d[45]*m(0,1) + d[63]*m(1,1) + d[18]*m(2,1);\n    s.d[64] = d[46]*m(0,1) + d[64]*m(1,1) + d[19]*m(2,1);\n    s.d[65] = d[47]*m(0,1) + d[65]*m(1,1) + d[20]*m(2,1);\n    s.d[66] = d[48]*m(0,1) + d[66]*m(1,1) + d[21]*m(2,1);\n    s.d[67] = d[49]*m(0,1) + d[67]*m(1,1) + d[22]*m(2,1);\n    s.d[68] = d[50]*m(0,1) + d[68]*m(1,1) + d[23]*m(2,1);\n    s.d[69] = d[51]*m(0,1) + d[69]*m(1,1) + d[24]*m(2,1);\n    s.d[70] = d[52]*m(0,1) + d[70]*m(1,1) + d[25]*m(2,1);\n    s.d[71] = d[53]*m(0,1) + d[71]*m(1,1) + d[26]*m(2,1);\n\n    s.d[72] = d[0]*m(0,2) + d[27]*m(1,2) + d[72]*m(2,2);\n    s.d[73] = d[1]*m(0,2) + d[28]*m(1,2) + d[73]*m(2,2);\n    s.d[74] = d[2]*m(0,2) + d[29]*m(1,2) + d[74]*m(2,2);\n    s.d[75] = d[3]*m(0,2) + d[30]*m(1,2) + d[75]*m(2,2);\n    s.d[76] = d[4]*m(0,2) + d[31]*m(1,2) + d[76]*m(2,2);\n    s.d[77] = d[5]*m(0,2) + d[32]*m(1,2) + d[77]*m(2,2);\n    s.d[78] = d[6]*m(0,2) + d[33]*m(1,2) + d[78]*m(2,2);\n    s.d[79] = d[7]*m(0,2) + d[34]*m(1,2) + d[79]*m(2,2);\n    s.d[80] = d[8]*m(0,2) + d[35]*m(1,2) + d[80]*m(2,2);\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// trace\n// C.tr() = I:C:I\ninline double tens4d::tr() const\n{\n    return (d[0]+d[1]+d[2]+d[9]+d[10]+d[11]+d[18]+d[19]+d[20]);\n}\n\n//-----------------------------------------------------------------------------\n// intialize to zero\ninline void tens4d::zero()\n{\n    d[ 0] = d[ 1] = d[ 2] = d[ 3] = d[ 4] = d[ 5] = d[ 6] = d[ 7] = d[ 8] = 0;\n    d[ 9] = d[10] = d[11] = d[12] = d[13] = d[14] = d[15] = d[16] = d[17] = 0;\n    d[18] = d[19] = d[20] = d[21] = d[22] = d[23] = d[24] = d[25] = d[26] = 0;\n    d[27] = d[28] = d[29] = d[30] = d[31] = d[32] = d[33] = d[34] = d[35] = 0;\n    d[36] = d[37] = d[38] = d[39] = d[40] = d[41] = d[42] = d[43] = d[44] = 0;\n    d[45] = d[46] = d[47] = d[48] = d[49] = d[50] = d[51] = d[52] = d[53] = 0;\n    d[54] = d[55] = d[56] = d[57] = d[58] = d[59] = d[60] = d[61] = d[62] = 0;\n    d[63] = d[64] = d[65] = d[66] = d[67] = d[68] = d[69] = d[70] = d[71] = 0;\n    d[72] = d[73] = d[74] = d[75] = d[76] = d[77] = d[78] = d[79] = d[80] = 0;\n}\n\n//-----------------------------------------------------------------------------\n// extract 9x9 matrix\ninline void tens4d::extract(double D[9][9])\n{\n    D[0][0] = d[ 0];    D[0][3] = d[27];    D[0][6] = d[54];\n    D[1][0] = d[ 1];    D[1][3] = d[28];    D[1][6] = d[55];\n    D[2][0] = d[ 2];    D[2][3] = d[29];    D[2][6] = d[56];\n    D[3][0] = d[ 3];    D[3][3] = d[30];    D[3][6] = d[57];\n    D[4][0] = d[ 4];    D[4][3] = d[31];    D[4][6] = d[58];\n    D[5][0] = d[ 5];    D[5][3] = d[32];    D[5][6] = d[59];\n    D[6][0] = d[ 6];    D[6][3] = d[33];    D[6][6] = d[60];\n    D[7][0] = d[ 7];    D[7][3] = d[34];    D[7][6] = d[61];\n    D[8][0] = d[ 8];    D[8][3] = d[35];    D[8][6] = d[62];\n    D[0][1] = d[ 9];    D[0][4] = d[36];    D[0][7] = d[63];\n    D[1][1] = d[10];    D[1][4] = d[37];    D[1][7] = d[64];\n    D[2][1] = d[11];    D[2][4] = d[38];    D[2][7] = d[65];\n    D[3][1] = d[12];    D[3][4] = d[39];    D[3][7] = d[66];\n    D[4][1] = d[13];    D[4][4] = d[40];    D[4][7] = d[67];\n    D[5][1] = d[14];    D[5][4] = d[41];    D[5][7] = d[68];\n    D[6][1] = d[15];    D[6][4] = d[42];    D[6][7] = d[69];\n    D[7][1] = d[16];    D[7][4] = d[43];    D[7][7] = d[70];\n    D[8][1] = d[17];    D[8][4] = d[44];    D[8][7] = d[71];\n    D[0][2] = d[18];    D[0][5] = d[45];    D[0][8] = d[72];\n    D[1][2] = d[19];    D[1][5] = d[46];    D[1][8] = d[73];\n    D[2][2] = d[20];    D[2][5] = d[47];    D[2][8] = d[74];\n    D[3][2] = d[21];    D[3][5] = d[48];    D[3][8] = d[75];\n    D[4][2] = d[22];    D[4][5] = d[49];    D[4][8] = d[76];\n    D[5][2] = d[23];    D[5][5] = d[50];    D[5][8] = d[77];\n    D[6][2] = d[24];    D[6][5] = d[51];    D[6][8] = d[78];\n    D[7][2] = d[25];    D[7][5] = d[52];    D[7][8] = d[79];\n    D[8][2] = d[26];    D[8][5] = d[53];    D[8][8] = d[80];\n}\n\n\n//-----------------------------------------------------------------------------\n// compute the super symmetric (major and minor symmetric) component of the tensor\n// Sijkl = (1/8)*(Cijkl + Cjikl + Cijlk + Cjilk + Cklij + Clkij + Cklji + Clkji)\ninline tens4ds tens4d::supersymm() const\n{\n    tens4ds s;\n    \n    s.d[ 0] = d[0];\n    s.d[ 1] = (d[9]+d[1])/2;\n    s.d[ 2] = d[10];\n    s.d[ 3] = (d[18]+d[2])/2;\n    s.d[ 4] = (d[19]+d[11])/2;\n    s.d[ 5] = d[20];\n    s.d[ 6] = (d[3] + d[6] + d[27] + d[54])/4;\n    s.d[ 7] = (d[12] + d[15] + d[28] + d[55])/4;\n    s.d[ 8] = (d[21] + d[24] + d[29] + d[56])/4;\n    s.d[ 9] = (d[30] + d[33] + d[57] + d[60])/4;\n    s.d[10] = (d[4] + d[7] + d[36] + d[63])/4;\n    s.d[11] = (d[13] + d[16] + d[37] + d[64])/4;\n    s.d[12] = (d[22] + d[25] + d[38] + d[65])/4;\n    s.d[13] = (d[31] + d[34] + d[39] + d[42] + d[58] + d[61] + d[66] + d[69])/8;\n    s.d[14] = (d[40] + d[43] + d[67] + d[70])/4;\n    s.d[15] = (d[5] + d[8] + d[45] + d[72])/4;\n    s.d[16] = (d[14] + d[17] + d[46] + d[73])/4;\n    s.d[17] = (d[23] + d[26] + d[47] + d[74])/4;\n    s.d[18] = (d[32] + d[35] + d[48] + d[51] + d[59] + d[62] + d[75] + d[78])/8;\n    s.d[19] = (d[41] + d[44] + d[49] + d[52] + d[68] + d[71] + d[76] + d[79])/8;\n    s.d[20] = (d[50] + d[53] + d[77] + d[80])/4;\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// compute the major transpose of the tensor\n// Sijkl -> Sklij\ninline tens4d tens4d::transpose() const\n{\n    tens4d s;\n    \n    s.d[ 0] = d[ 0];    s.d[ 6] = d[54];    s.d[ 5] = d[45];\n    s.d[27] = d[ 3];    s.d[33] = d[57];    s.d[32] = d[48];\n    s.d[72] = d[ 8];    s.d[78] = d[62];    s.d[77] = d[53];\n    s.d[54] = d[ 6];    s.d[60] = d[60];    s.d[59] = d[51];\n    s.d[ 9] = d[ 1];    s.d[15] = d[55];    s.d[14] = d[46];\n    s.d[36] = d[ 4];    s.d[42] = d[58];    s.d[41] = d[49];\n    s.d[45] = d[ 5];    s.d[51] = d[59];    s.d[50] = d[50];\n    s.d[63] = d[ 7];    s.d[69] = d[61];    s.d[68] = d[52];\n    s.d[18] = d[ 2];    s.d[24] = d[56];    s.d[23] = d[47];\n\n    s.d[ 3] = d[27];    s.d[ 1] = d[ 9];    s.d[ 7] = d[63];\n    s.d[30] = d[30];    s.d[28] = d[12];    s.d[34] = d[66];\n    s.d[75] = d[35];    s.d[73] = d[17];    s.d[79] = d[71];\n    s.d[57] = d[33];    s.d[55] = d[15];    s.d[61] = d[69];\n    s.d[12] = d[28];    s.d[10] = d[10];    s.d[16] = d[64];\n    s.d[39] = d[31];    s.d[37] = d[13];    s.d[43] = d[67];\n    s.d[48] = d[32];    s.d[46] = d[14];    s.d[52] = d[68];\n    s.d[66] = d[34];    s.d[64] = d[16];    s.d[70] = d[70];\n    s.d[21] = d[29];    s.d[19] = d[11];    s.d[25] = d[65];\n\n    s.d[ 8] = d[72];    s.d[ 4] = d[36];    s.d[ 2] = d[18];\n    s.d[35] = d[75];    s.d[31] = d[39];    s.d[29] = d[21];\n    s.d[80] = d[80];    s.d[76] = d[44];    s.d[74] = d[26];\n    s.d[62] = d[78];    s.d[58] = d[42];    s.d[56] = d[24];\n    s.d[17] = d[73];    s.d[13] = d[37];    s.d[11] = d[19];\n    s.d[44] = d[76];    s.d[40] = d[40];    s.d[38] = d[22];\n    s.d[53] = d[77];    s.d[49] = d[41];    s.d[47] = d[23];\n    s.d[71] = d[79];    s.d[67] = d[43];    s.d[65] = d[25];\n    s.d[26] = d[74];    s.d[22] = d[38];    s.d[20] = d[20];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// compute the left transpose of the tensor\n// Sijkl -> Sjikl\ninline tens4d tens4d::left_transpose() const\n{\n    tens4d s;\n    \n    s.d[ 0] = d[ 0];    s.d[ 6] = d[ 3];    s.d[ 5] = d[ 8];\n    s.d[27] = d[27];    s.d[33] = d[30];    s.d[32] = d[35];\n    s.d[72] = d[72];    s.d[78] = d[75];    s.d[77] = d[80];\n    s.d[54] = d[54];    s.d[60] = d[57];    s.d[59] = d[62];\n    s.d[ 9] = d[ 9];    s.d[15] = d[12];    s.d[14] = d[17];\n    s.d[36] = d[36];    s.d[42] = d[39];    s.d[41] = d[44];\n    s.d[45] = d[45];    s.d[51] = d[48];    s.d[50] = d[53];\n    s.d[63] = d[63];    s.d[69] = d[66];    s.d[68] = d[71];\n    s.d[18] = d[18];    s.d[24] = d[21];    s.d[23] = d[26];\n\n    s.d[ 3] = d[ 6];    s.d[ 1] = d[ 1];    s.d[ 7] = d[ 4];\n    s.d[30] = d[33];    s.d[28] = d[28];    s.d[34] = d[31];\n    s.d[75] = d[78];    s.d[73] = d[73];    s.d[79] = d[76];\n    s.d[57] = d[60];    s.d[55] = d[55];    s.d[61] = d[58];\n    s.d[12] = d[15];    s.d[10] = d[10];    s.d[16] = d[13];\n    s.d[39] = d[42];    s.d[37] = d[37];    s.d[43] = d[40];\n    s.d[48] = d[51];    s.d[46] = d[46];    s.d[52] = d[49];\n    s.d[66] = d[69];    s.d[64] = d[64];    s.d[70] = d[67];\n    s.d[21] = d[24];    s.d[19] = d[19];    s.d[25] = d[22];\n\n    s.d[ 8] = d[ 5];    s.d[ 4] = d[ 7];    s.d[ 2] = d[ 2];\n    s.d[35] = d[32];    s.d[31] = d[34];    s.d[29] = d[29];\n    s.d[80] = d[77];    s.d[76] = d[79];    s.d[74] = d[74];\n    s.d[62] = d[59];    s.d[58] = d[61];    s.d[56] = d[56];\n    s.d[17] = d[14];    s.d[13] = d[16];    s.d[11] = d[11];\n    s.d[44] = d[41];    s.d[40] = d[43];    s.d[38] = d[38];\n    s.d[53] = d[50];    s.d[49] = d[52];    s.d[47] = d[47];\n    s.d[71] = d[68];    s.d[67] = d[70];    s.d[65] = d[65];\n    s.d[26] = d[23];    s.d[22] = d[25];    s.d[20] = d[20];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// compute the right transpose of the tensor\n// Sijkl -> Sijlk\ninline tens4d tens4d::right_transpose() const\n{\n    tens4d s;\n    \n    s.d[ 0] = d[ 0];    s.d[ 6] = d[ 6];    s.d[ 5] = d[ 5];\n    s.d[27] = d[54];    s.d[33] = d[60];    s.d[32] = d[59];\n    s.d[72] = d[45];    s.d[78] = d[51];    s.d[77] = d[50];\n    s.d[54] = d[27];    s.d[60] = d[33];    s.d[59] = d[32];\n    s.d[ 9] = d[ 9];    s.d[15] = d[15];    s.d[14] = d[14];\n    s.d[36] = d[63];    s.d[42] = d[69];    s.d[41] = d[68];\n    s.d[45] = d[72];    s.d[51] = d[78];    s.d[50] = d[77];\n    s.d[63] = d[36];    s.d[69] = d[42];    s.d[68] = d[41];\n    s.d[18] = d[18];    s.d[24] = d[24];    s.d[23] = d[23];\n\n    s.d[ 3] = d[ 3];    s.d[ 1] = d[ 1];    s.d[ 7] = d[ 7];\n    s.d[30] = d[57];    s.d[28] = d[55];    s.d[34] = d[61];\n    s.d[75] = d[48];    s.d[73] = d[46];    s.d[79] = d[52];\n    s.d[57] = d[30];    s.d[55] = d[28];    s.d[61] = d[34];\n    s.d[12] = d[12];    s.d[10] = d[10];    s.d[16] = d[16];\n    s.d[39] = d[66];    s.d[37] = d[64];    s.d[43] = d[70];\n    s.d[48] = d[75];    s.d[46] = d[73];    s.d[52] = d[79];\n    s.d[66] = d[39];    s.d[64] = d[37];    s.d[70] = d[43];\n    s.d[21] = d[21];    s.d[19] = d[19];    s.d[25] = d[25];\n\n    s.d[ 8] = d[ 8];    s.d[ 4] = d[ 4];    s.d[ 2] = d[ 2];\n    s.d[35] = d[62];    s.d[31] = d[58];    s.d[29] = d[56];\n    s.d[80] = d[53];    s.d[76] = d[49];    s.d[74] = d[47];\n    s.d[62] = d[35];    s.d[58] = d[31];    s.d[56] = d[29];\n    s.d[17] = d[17];    s.d[13] = d[13];    s.d[11] = d[11];\n    s.d[44] = d[71];    s.d[40] = d[67];    s.d[38] = d[65];\n    s.d[53] = d[80];    s.d[49] = d[76];    s.d[47] = d[74];\n    s.d[71] = d[44];    s.d[67] = d[40];    s.d[65] = d[38];\n    s.d[26] = d[26];    s.d[22] = d[22];    s.d[20] = d[20];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1 b)_ijkl = a_ij b_kl\ninline tens4d dyad1(const mat3d& a, const mat3d& b)\n{\n    tens4d c;\n    \n    c.d[ 0] = a(0,0)*b(0,0);\n    c.d[27] = a(0,0)*b(0,1);\n    c.d[72] = a(0,0)*b(0,2);\n    c.d[54] = a(0,0)*b(1,0);\n    c.d[ 9] = a(0,0)*b(1,1);\n    c.d[36] = a(0,0)*b(1,2);\n    c.d[45] = a(0,0)*b(2,0);\n    c.d[63] = a(0,0)*b(2,1);\n    c.d[18] = a(0,0)*b(2,2);\n\n    c.d[ 3] = a(0,1)*b(0,0);\n    c.d[30] = a(0,1)*b(0,1);\n    c.d[75] = a(0,1)*b(0,2);\n    c.d[57] = a(0,1)*b(1,0);\n    c.d[12] = a(0,1)*b(1,1);\n    c.d[39] = a(0,1)*b(1,2);\n    c.d[48] = a(0,1)*b(2,0);\n    c.d[66] = a(0,1)*b(2,1);\n    c.d[21] = a(0,1)*b(2,2);\n\n    c.d[ 8] = a(0,2)*b(0,0);\n    c.d[35] = a(0,2)*b(0,1);\n    c.d[80] = a(0,2)*b(0,2);\n    c.d[62] = a(0,2)*b(1,0);\n    c.d[17] = a(0,2)*b(1,1);\n    c.d[44] = a(0,2)*b(1,2);\n    c.d[53] = a(0,2)*b(2,0);\n    c.d[71] = a(0,2)*b(2,1);\n    c.d[26] = a(0,2)*b(2,2);\n\n    c.d[6] = a(1,0)*b(0,0);\n    c.d[33] = a(1,0)*b(0,1);\n    c.d[78] = a(1,0)*b(0,2);\n    c.d[60] = a(1,0)*b(1,0);\n    c.d[15] = a(1,0)*b(1,1);\n    c.d[42] = a(1,0)*b(1,2);\n    c.d[51] = a(1,0)*b(2,0);\n    c.d[69] = a(1,0)*b(2,1);\n    c.d[24] = a(1,0)*b(2,2);\n\n    c.d[ 1] = a(1,1)*b(0,0);\n    c.d[28] = a(1,1)*b(0,1);\n    c.d[73] = a(1,1)*b(0,2);\n    c.d[55] = a(1,1)*b(1,0);\n    c.d[10] = a(1,1)*b(1,1);\n    c.d[37] = a(1,1)*b(1,2);\n    c.d[46] = a(1,1)*b(2,0);\n    c.d[64] = a(1,1)*b(2,1);\n    c.d[19] = a(1,1)*b(2,2);\n\n    c.d[ 4] = a(1,2)*b(0,0);\n    c.d[31] = a(1,2)*b(0,1);\n    c.d[76] = a(1,2)*b(0,2);\n    c.d[58] = a(1,2)*b(1,0);\n    c.d[13] = a(1,2)*b(1,1);\n    c.d[40] = a(1,2)*b(1,2);\n    c.d[49] = a(1,2)*b(2,0);\n    c.d[67] = a(1,2)*b(2,1);\n    c.d[22] = a(1,2)*b(2,2);\n\n    c.d[ 5] = a(2,0)*b(0,0);\n    c.d[32] = a(2,0)*b(0,1);\n    c.d[77] = a(2,0)*b(0,2);\n    c.d[59] = a(2,0)*b(1,0);\n    c.d[14] = a(2,0)*b(1,1);\n    c.d[41] = a(2,0)*b(1,2);\n    c.d[50] = a(2,0)*b(2,0);\n    c.d[68] = a(2,0)*b(2,1);\n    c.d[23] = a(2,0)*b(2,2);\n\n    c.d[ 7] = a(2,1)*b(0,0);\n    c.d[34] = a(2,1)*b(0,1);\n    c.d[79] = a(2,1)*b(0,2);\n    c.d[61] = a(2,1)*b(1,0);\n    c.d[16] = a(2,1)*b(1,1);\n    c.d[43] = a(2,1)*b(1,2);\n    c.d[52] = a(2,1)*b(2,0);\n    c.d[70] = a(2,1)*b(2,1);\n    c.d[25] = a(2,1)*b(2,2);\n\n    c.d[ 2] = a(2,2)*b(0,0);\n    c.d[29] = a(2,2)*b(0,1);\n    c.d[74] = a(2,2)*b(0,2);\n    c.d[56] = a(2,2)*b(1,0);\n    c.d[11] = a(2,2)*b(1,1);\n    c.d[38] = a(2,2)*b(1,2);\n    c.d[47] = a(2,2)*b(2,0);\n    c.d[65] = a(2,2)*b(2,1);\n    c.d[20] = a(2,2)*b(2,2);\n\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad2 b)_ijkl = a_ik b_jl\ninline tens4d dyad2(const mat3d& a, const mat3d& b)\n{\n    tens4d c;\n    \n    c.d[ 0] = a(0,0)*b(0,0);\n    c.d[27] = a(0,0)*b(0,1);\n    c.d[72] = a(0,0)*b(0,2);\n    c.d[54] = a(0,1)*b(0,0);\n    c.d[ 9] = a(0,1)*b(0,1);\n    c.d[36] = a(0,1)*b(0,2);\n    c.d[45] = a(0,2)*b(0,0);\n    c.d[63] = a(0,2)*b(0,1);\n    c.d[18] = a(0,2)*b(0,2);\n\n    c.d[ 3] = a(0,0)*b(1,0);\n    c.d[30] = a(0,0)*b(1,1);\n    c.d[75] = a(0,0)*b(1,2);\n    c.d[57] = a(0,1)*b(1,0);\n    c.d[12] = a(0,1)*b(1,1);\n    c.d[39] = a(0,1)*b(1,2);\n    c.d[48] = a(0,2)*b(1,0);\n    c.d[66] = a(0,2)*b(1,1);\n    c.d[21] = a(0,2)*b(1,2);\n\n    c.d[ 8] = a(0,0)*b(2,0);\n    c.d[35] = a(0,0)*b(2,1);\n    c.d[80] = a(0,0)*b(2,2);\n    c.d[62] = a(0,1)*b(2,0);\n    c.d[17] = a(0,1)*b(2,1);\n    c.d[44] = a(0,1)*b(2,2);\n    c.d[53] = a(0,2)*b(2,0);\n    c.d[71] = a(0,2)*b(2,1);\n    c.d[26] = a(0,2)*b(2,2);\n\n    c.d[ 6] = a(1,0)*b(0,0);\n    c.d[33] = a(1,0)*b(0,1);\n    c.d[78] = a(1,0)*b(0,2);\n    c.d[60] = a(1,1)*b(0,0);\n    c.d[15] = a(1,1)*b(0,1);\n    c.d[42] = a(1,1)*b(0,2);\n    c.d[51] = a(1,2)*b(0,0);\n    c.d[69] = a(1,2)*b(0,1);\n    c.d[24] = a(1,2)*b(0,2);\n\n    c.d[ 1] = a(1,0)*b(1,0);\n    c.d[28] = a(1,0)*b(1,1);\n    c.d[73] = a(1,0)*b(1,2);\n    c.d[55] = a(1,1)*b(1,0);\n    c.d[10] = a(1,1)*b(1,1);\n    c.d[37] = a(1,1)*b(1,2);\n    c.d[46] = a(1,2)*b(1,0);\n    c.d[64] = a(1,2)*b(1,1);\n    c.d[19] = a(1,2)*b(1,2);\n\n    c.d[ 4] = a(1,0)*b(2,0);\n    c.d[31] = a(1,0)*b(2,1);\n    c.d[76] = a(1,0)*b(2,2);\n    c.d[58] = a(1,1)*b(2,0);\n    c.d[13] = a(1,1)*b(2,1);\n    c.d[40] = a(1,1)*b(2,2);\n    c.d[49] = a(1,2)*b(2,0);\n    c.d[67] = a(1,2)*b(2,1);\n    c.d[22] = a(1,2)*b(2,2);\n\n    c.d[ 5] = a(2,0)*b(0,0);\n    c.d[32] = a(2,0)*b(0,1);\n    c.d[77] = a(2,0)*b(0,2);\n    c.d[59] = a(2,1)*b(0,0);\n    c.d[14] = a(2,1)*b(0,1);\n    c.d[41] = a(2,1)*b(0,2);\n    c.d[50] = a(2,2)*b(0,0);\n    c.d[68] = a(2,2)*b(0,1);\n    c.d[23] = a(2,2)*b(0,2);\n\n    c.d[ 7] = a(2,0)*b(1,0);\n    c.d[34] = a(2,0)*b(1,1);\n    c.d[79] = a(2,0)*b(1,2);\n    c.d[61] = a(2,1)*b(1,0);\n    c.d[16] = a(2,1)*b(1,1);\n    c.d[43] = a(2,1)*b(1,2);\n    c.d[52] = a(2,2)*b(1,0);\n    c.d[70] = a(2,2)*b(1,1);\n    c.d[25] = a(2,2)*b(1,2);\n\n    c.d[ 2] = a(2,0)*b(2,0);\n    c.d[29] = a(2,0)*b(2,1);\n    c.d[74] = a(2,0)*b(2,2);\n    c.d[56] = a(2,1)*b(2,0);\n    c.d[11] = a(2,1)*b(2,1);\n    c.d[38] = a(2,1)*b(2,2);\n    c.d[47] = a(2,2)*b(2,0);\n    c.d[65] = a(2,2)*b(2,1);\n    c.d[20] = a(2,2)*b(2,2);\n\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad3 b)_ijkl = a_il b_jk\ninline tens4d dyad3(const mat3d& a, const mat3d& b)\n{\n    tens4d c;\n    \n    c.d[ 0] = a(0,0)*b(0,0);\n    c.d[27] = a(0,1)*b(0,0);\n    c.d[72] = a(0,2)*b(0,0);\n    c.d[54] = a(0,0)*b(0,1);\n    c.d[ 9] = a(0,1)*b(0,1);\n    c.d[36] = a(0,2)*b(0,1);\n    c.d[45] = a(0,0)*b(0,2);\n    c.d[63] = a(0,1)*b(0,2);\n    c.d[18] = a(0,2)*b(0,2);\n\n    c.d[ 3] = a(0,0)*b(1,0);\n    c.d[30] = a(0,1)*b(1,0);\n    c.d[75] = a(0,2)*b(1,0);\n    c.d[57] = a(0,0)*b(1,1);\n    c.d[12] = a(0,1)*b(1,1);\n    c.d[39] = a(0,2)*b(1,1);\n    c.d[48] = a(0,0)*b(1,2);\n    c.d[66] = a(0,1)*b(1,2);\n    c.d[21] = a(0,2)*b(1,2);\n\n    c.d[ 8] = a(0,0)*b(2,0);\n    c.d[35] = a(0,1)*b(2,0);\n    c.d[80] = a(0,2)*b(2,0);\n    c.d[62] = a(0,0)*b(2,1);\n    c.d[17] = a(0,1)*b(2,1);\n    c.d[44] = a(0,2)*b(2,1);\n    c.d[53] = a(0,0)*b(2,2);\n    c.d[71] = a(0,1)*b(2,2);\n    c.d[26] = a(0,2)*b(2,2);\n\n    c.d[ 6] = a(1,0)*b(0,0);\n    c.d[33] = a(1,1)*b(0,0);\n    c.d[78] = a(1,2)*b(0,0);\n    c.d[60] = a(1,0)*b(0,1);\n    c.d[15] = a(1,1)*b(0,1);\n    c.d[42] = a(1,2)*b(0,1);\n    c.d[51] = a(1,0)*b(0,2);\n    c.d[69] = a(1,1)*b(0,2);\n    c.d[24] = a(1,2)*b(0,2);\n\n    c.d[ 1] = a(1,0)*b(1,0);\n    c.d[28] = a(1,1)*b(1,0);\n    c.d[73] = a(1,2)*b(1,0);\n    c.d[55] = a(1,0)*b(1,1);\n    c.d[10] = a(1,1)*b(1,1);\n    c.d[37] = a(1,2)*b(1,1);\n    c.d[46] = a(1,0)*b(1,2);\n    c.d[64] = a(1,1)*b(1,2);\n    c.d[19] = a(1,2)*b(1,2);\n\n    c.d[ 4] = a(1,0)*b(2,0);\n    c.d[31] = a(1,1)*b(2,0);\n    c.d[76] = a(1,2)*b(2,0);\n    c.d[58] = a(1,0)*b(2,1);\n    c.d[13] = a(1,1)*b(2,1);\n    c.d[40] = a(1,2)*b(2,1);\n    c.d[49] = a(1,0)*b(2,2);\n    c.d[67] = a(1,1)*b(2,2);\n    c.d[22] = a(1,2)*b(2,2);\n\n    c.d[ 5] = a(2,0)*b(0,0);\n    c.d[32] = a(2,1)*b(0,0);\n    c.d[77] = a(2,2)*b(0,0);\n    c.d[59] = a(2,0)*b(0,1);\n    c.d[14] = a(2,1)*b(0,1);\n    c.d[41] = a(2,2)*b(0,1);\n    c.d[50] = a(2,0)*b(0,2);\n    c.d[68] = a(2,1)*b(0,2);\n    c.d[23] = a(2,2)*b(0,2);\n\n    c.d[ 7] = a(2,0)*b(1,0);\n    c.d[34] = a(2,1)*b(1,0);\n    c.d[79] = a(2,2)*b(1,0);\n    c.d[61] = a(2,0)*b(1,1);\n    c.d[16] = a(2,1)*b(1,1);\n    c.d[43] = a(2,2)*b(1,1);\n    c.d[52] = a(2,0)*b(1,2);\n    c.d[70] = a(2,1)*b(1,2);\n    c.d[25] = a(2,2)*b(1,2);\n\n    c.d[ 2] = a(2,0)*b(2,0);\n    c.d[29] = a(2,1)*b(2,0);\n    c.d[74] = a(2,2)*b(2,0);\n    c.d[56] = a(2,0)*b(2,1);\n    c.d[11] = a(2,1)*b(2,1);\n    c.d[38] = a(2,2)*b(2,1);\n    c.d[47] = a(2,0)*b(2,2);\n    c.d[65] = a(2,1)*b(2,2);\n    c.d[20] = a(2,2)*b(2,2);\n\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n// (a ddot b)_ijkl = a_ijmn b_mnkl\ninline tens4d ddot(const tens4d& a, const tens4d& b)\n{\n    tens4d c;\n    \n    c.d[0] = a.d[0]*b.d[0] + a.d[9]*b.d[1] + a.d[18]*b.d[2] + a.d[27]*b.d[3] + a.d[36]*b.d[4] + a.d[45]*b.d[5] + a.d[54]*b.d[6] + a.d[63]*b.d[7] + a.d[72]*b.d[8];\n    c.d[1] = a.d[0]*b.d[9] + a.d[9]*b.d[10] + a.d[18]*b.d[11] + a.d[27]*b.d[12] + a.d[36]*b.d[13] + a.d[45]*b.d[14] + a.d[54]*b.d[15] + a.d[63]*b.d[16] + a.d[72]*b.d[17];\n    c.d[2] = a.d[0]*b.d[18] + a.d[9]*b.d[19] + a.d[18]*b.d[20] + a.d[27]*b.d[21] + a.d[36]*b.d[22] + a.d[45]*b.d[23] + a.d[54]*b.d[24] + a.d[63]*b.d[25] + a.d[72]*b.d[26];\n    c.d[3] = a.d[0]*b.d[27] + a.d[9]*b.d[28] + a.d[18]*b.d[29] + a.d[27]*b.d[30] + a.d[36]*b.d[31] + a.d[45]*b.d[32] + a.d[54]*b.d[33] + a.d[63]*b.d[34] + a.d[72]*b.d[35];\n    c.d[4] = a.d[0]*b.d[36] + a.d[9]*b.d[37] + a.d[18]*b.d[38] + a.d[27]*b.d[39] + a.d[36]*b.d[40] + a.d[45]*b.d[41] + a.d[54]*b.d[42] + a.d[63]*b.d[43] + a.d[72]*b.d[44];\n    c.d[5] = a.d[0]*b.d[45] + a.d[9]*b.d[46] + a.d[18]*b.d[47] + a.d[27]*b.d[48] + a.d[36]*b.d[49] + a.d[45]*b.d[50] + a.d[54]*b.d[51] + a.d[63]*b.d[52] + a.d[72]*b.d[53];\n    c.d[6] = a.d[0]*b.d[54] + a.d[9]*b.d[55] + a.d[18]*b.d[56] + a.d[27]*b.d[57] + a.d[36]*b.d[58] + a.d[45]*b.d[59] + a.d[54]*b.d[60] + a.d[63]*b.d[61] + a.d[72]*b.d[62];\n    c.d[7] = a.d[0]*b.d[63] + a.d[9]*b.d[64] + a.d[18]*b.d[65] + a.d[27]*b.d[66] + a.d[36]*b.d[67] + a.d[45]*b.d[68] + a.d[54]*b.d[69] + a.d[63]*b.d[70] + a.d[72]*b.d[71];\n    c.d[8] = a.d[0]*b.d[72] + a.d[9]*b.d[73] + a.d[18]*b.d[74] + a.d[27]*b.d[75] + a.d[36]*b.d[76] + a.d[45]*b.d[77] + a.d[54]*b.d[78] + a.d[63]*b.d[79] + a.d[72]*b.d[80];\n\n    c.d[9] = a.d[1]*b.d[0] + a.d[10]*b.d[1] + a.d[19]*b.d[2] + a.d[28]*b.d[3] + a.d[37]*b.d[4] + a.d[46]*b.d[5] + a.d[55]*b.d[6] + a.d[64]*b.d[7] + a.d[73]*b.d[8];\n    c.d[10] = a.d[1]*b.d[9] + a.d[10]*b.d[10] + a.d[19]*b.d[11] + a.d[28]*b.d[12] + a.d[37]*b.d[13] + a.d[46]*b.d[14] + a.d[55]*b.d[15] + a.d[64]*b.d[16] + a.d[73]*b.d[17];\n    c.d[11] = a.d[1]*b.d[18] + a.d[10]*b.d[19] + a.d[19]*b.d[20] + a.d[28]*b.d[21] + a.d[37]*b.d[22] + a.d[46]*b.d[23] + a.d[55]*b.d[24] + a.d[64]*b.d[25] + a.d[73]*b.d[26];\n    c.d[12] = a.d[1]*b.d[27] + a.d[10]*b.d[28] + a.d[19]*b.d[29] + a.d[28]*b.d[30] + a.d[37]*b.d[31] + a.d[46]*b.d[32] + a.d[55]*b.d[33] + a.d[64]*b.d[34] + a.d[73]*b.d[35];\n    c.d[13] = a.d[1]*b.d[36] + a.d[10]*b.d[37] + a.d[19]*b.d[38] + a.d[28]*b.d[39] + a.d[37]*b.d[40] + a.d[46]*b.d[41] + a.d[55]*b.d[42] + a.d[64]*b.d[43] + a.d[73]*b.d[44];\n    c.d[14] = a.d[1]*b.d[45] + a.d[10]*b.d[46] + a.d[19]*b.d[47] + a.d[28]*b.d[48] + a.d[37]*b.d[49] + a.d[46]*b.d[50] + a.d[55]*b.d[51] + a.d[64]*b.d[52] + a.d[73]*b.d[53];\n    c.d[15] = a.d[1]*b.d[54] + a.d[10]*b.d[55] + a.d[19]*b.d[56] + a.d[28]*b.d[57] + a.d[37]*b.d[58] + a.d[46]*b.d[59] + a.d[55]*b.d[60] + a.d[64]*b.d[61] + a.d[73]*b.d[62];\n    c.d[16] = a.d[1]*b.d[63] + a.d[10]*b.d[64] + a.d[19]*b.d[65] + a.d[28]*b.d[66] + a.d[37]*b.d[67] + a.d[46]*b.d[68] + a.d[55]*b.d[69] + a.d[64]*b.d[70] + a.d[73]*b.d[71];\n    c.d[17] = a.d[1]*b.d[72] + a.d[10]*b.d[73] + a.d[19]*b.d[74] + a.d[28]*b.d[75] + a.d[37]*b.d[76] + a.d[46]*b.d[77] + a.d[55]*b.d[78] + a.d[64]*b.d[79] + a.d[73]*b.d[80];\n\n    c.d[18] = a.d[2]*b.d[0] + a.d[11]*b.d[1] + a.d[20]*b.d[2] + a.d[29]*b.d[3] + a.d[38]*b.d[4] + a.d[47]*b.d[5] + a.d[56]*b.d[6] + a.d[65]*b.d[7] + a.d[74]*b.d[8];\n    c.d[19] = a.d[2]*b.d[9] + a.d[11]*b.d[10] + a.d[20]*b.d[11] + a.d[29]*b.d[12] + a.d[38]*b.d[13] + a.d[47]*b.d[14] + a.d[56]*b.d[15] + a.d[65]*b.d[16] + a.d[74]*b.d[17];\n    c.d[20] = a.d[2]*b.d[18] + a.d[11]*b.d[19] + a.d[20]*b.d[20] + a.d[29]*b.d[21] + a.d[38]*b.d[22] + a.d[47]*b.d[23] + a.d[56]*b.d[24] + a.d[65]*b.d[25] + a.d[74]*b.d[26];\n    c.d[21] = a.d[2]*b.d[27] + a.d[11]*b.d[28] + a.d[20]*b.d[29] + a.d[29]*b.d[30] + a.d[38]*b.d[31] + a.d[47]*b.d[32] + a.d[56]*b.d[33] + a.d[65]*b.d[34] + a.d[74]*b.d[35];\n    c.d[22] = a.d[2]*b.d[36] + a.d[11]*b.d[37] + a.d[20]*b.d[38] + a.d[29]*b.d[39] + a.d[38]*b.d[40] + a.d[47]*b.d[41] + a.d[56]*b.d[42] + a.d[65]*b.d[43] + a.d[74]*b.d[44];\n    c.d[23] = a.d[2]*b.d[45] + a.d[11]*b.d[46] + a.d[20]*b.d[47] + a.d[29]*b.d[48] + a.d[38]*b.d[49] + a.d[47]*b.d[50] + a.d[56]*b.d[51] + a.d[65]*b.d[52] + a.d[74]*b.d[53];\n    c.d[24] = a.d[2]*b.d[54] + a.d[11]*b.d[55] + a.d[20]*b.d[56] + a.d[29]*b.d[57] + a.d[38]*b.d[58] + a.d[47]*b.d[59] + a.d[56]*b.d[60] + a.d[65]*b.d[61] + a.d[74]*b.d[62];\n    c.d[25] = a.d[2]*b.d[63] + a.d[11]*b.d[64] + a.d[20]*b.d[65] + a.d[29]*b.d[66] + a.d[38]*b.d[67] + a.d[47]*b.d[68] + a.d[56]*b.d[69] + a.d[65]*b.d[70] + a.d[74]*b.d[71];\n    c.d[26] = a.d[2]*b.d[72] + a.d[11]*b.d[73] + a.d[20]*b.d[74] + a.d[29]*b.d[75] + a.d[38]*b.d[76] + a.d[47]*b.d[77] + a.d[56]*b.d[78] + a.d[65]*b.d[79] + a.d[74]*b.d[80];\n\n    c.d[27] = a.d[3]*b.d[0] + a.d[12]*b.d[1] + a.d[21]*b.d[2] + a.d[30]*b.d[3] + a.d[39]*b.d[4] + a.d[48]*b.d[5] + a.d[57]*b.d[6] + a.d[66]*b.d[7] + a.d[75]*b.d[8];\n    c.d[28] = a.d[3]*b.d[9] + a.d[12]*b.d[10] + a.d[21]*b.d[11] + a.d[30]*b.d[12] + a.d[39]*b.d[13] + a.d[48]*b.d[14] + a.d[57]*b.d[15] + a.d[66]*b.d[16] + a.d[75]*b.d[17];\n    c.d[29] = a.d[3]*b.d[18] + a.d[12]*b.d[19] + a.d[21]*b.d[20] + a.d[30]*b.d[21] + a.d[39]*b.d[22] + a.d[48]*b.d[23] + a.d[57]*b.d[24] + a.d[66]*b.d[25] + a.d[75]*b.d[26];\n    c.d[30] = a.d[3]*b.d[27] + a.d[12]*b.d[28] + a.d[21]*b.d[29] + a.d[30]*b.d[30] + a.d[39]*b.d[31] + a.d[48]*b.d[32] + a.d[57]*b.d[33] + a.d[66]*b.d[34] + a.d[75]*b.d[35];\n    c.d[31] = a.d[3]*b.d[36] + a.d[12]*b.d[37] + a.d[21]*b.d[38] + a.d[30]*b.d[39] + a.d[39]*b.d[40] + a.d[48]*b.d[41] + a.d[57]*b.d[42] + a.d[66]*b.d[43] + a.d[75]*b.d[44];\n    c.d[32] = a.d[3]*b.d[45] + a.d[12]*b.d[46] + a.d[21]*b.d[47] + a.d[30]*b.d[48] + a.d[39]*b.d[49] + a.d[48]*b.d[50] + a.d[57]*b.d[51] + a.d[66]*b.d[52] + a.d[75]*b.d[53];\n    c.d[33] = a.d[3]*b.d[54] + a.d[12]*b.d[55] + a.d[21]*b.d[56] + a.d[30]*b.d[57] + a.d[39]*b.d[58] + a.d[48]*b.d[59] + a.d[57]*b.d[60] + a.d[66]*b.d[61] + a.d[75]*b.d[62];\n    c.d[34] = a.d[3]*b.d[63] + a.d[12]*b.d[64] + a.d[21]*b.d[65] + a.d[30]*b.d[66] + a.d[39]*b.d[67] + a.d[48]*b.d[68] + a.d[57]*b.d[69] + a.d[66]*b.d[70] + a.d[75]*b.d[71];\n    c.d[35] = a.d[3]*b.d[72] + a.d[12]*b.d[73] + a.d[21]*b.d[74] + a.d[30]*b.d[75] + a.d[39]*b.d[76] + a.d[48]*b.d[77] + a.d[57]*b.d[78] + a.d[66]*b.d[79] + a.d[75]*b.d[80];\n\n    c.d[36] = a.d[4]*b.d[0] + a.d[13]*b.d[1] + a.d[22]*b.d[2] + a.d[31]*b.d[3] + a.d[40]*b.d[4] + a.d[49]*b.d[5] + a.d[58]*b.d[6] + a.d[67]*b.d[7] + a.d[76]*b.d[8];\n    c.d[37] = a.d[4]*b.d[9] + a.d[13]*b.d[10] + a.d[22]*b.d[11] + a.d[31]*b.d[12] + a.d[40]*b.d[13] + a.d[49]*b.d[14] + a.d[58]*b.d[15] + a.d[67]*b.d[16] + a.d[76]*b.d[17];\n    c.d[38] = a.d[4]*b.d[18] + a.d[13]*b.d[19] + a.d[22]*b.d[20] + a.d[31]*b.d[21] + a.d[40]*b.d[22] + a.d[49]*b.d[23] + a.d[58]*b.d[24] + a.d[67]*b.d[25] + a.d[76]*b.d[26];\n    c.d[39] = a.d[4]*b.d[27] + a.d[13]*b.d[28] + a.d[22]*b.d[29] + a.d[31]*b.d[30] + a.d[40]*b.d[31] + a.d[49]*b.d[32] + a.d[58]*b.d[33] + a.d[67]*b.d[34] + a.d[76]*b.d[35];\n    c.d[40] = a.d[4]*b.d[36] + a.d[13]*b.d[37] + a.d[22]*b.d[38] + a.d[31]*b.d[39] + a.d[40]*b.d[40] + a.d[49]*b.d[41] + a.d[58]*b.d[42] + a.d[67]*b.d[43] + a.d[76]*b.d[44];\n    c.d[41] = a.d[4]*b.d[45] + a.d[13]*b.d[46] + a.d[22]*b.d[47] + a.d[31]*b.d[48] + a.d[40]*b.d[49] + a.d[49]*b.d[50] + a.d[58]*b.d[51] + a.d[67]*b.d[52] + a.d[76]*b.d[53];\n    c.d[42] = a.d[4]*b.d[54] + a.d[13]*b.d[55] + a.d[22]*b.d[56] + a.d[31]*b.d[57] + a.d[40]*b.d[58] + a.d[49]*b.d[59] + a.d[58]*b.d[60] + a.d[67]*b.d[61] + a.d[76]*b.d[62];\n    c.d[43] = a.d[4]*b.d[63] + a.d[13]*b.d[64] + a.d[22]*b.d[65] + a.d[31]*b.d[66] + a.d[40]*b.d[67] + a.d[49]*b.d[68] + a.d[58]*b.d[69] + a.d[67]*b.d[70] + a.d[76]*b.d[71];\n    c.d[44] = a.d[4]*b.d[72] + a.d[13]*b.d[73] + a.d[22]*b.d[74] + a.d[31]*b.d[75] + a.d[40]*b.d[76] + a.d[49]*b.d[77] + a.d[58]*b.d[78] + a.d[67]*b.d[79] + a.d[76]*b.d[80];\n\n    c.d[45] = a.d[5]*b.d[0] + a.d[14]*b.d[1] + a.d[23]*b.d[2] + a.d[32]*b.d[3] + a.d[41]*b.d[4] + a.d[50]*b.d[5] + a.d[59]*b.d[6] + a.d[68]*b.d[7] + a.d[77]*b.d[8];\n    c.d[46] = a.d[5]*b.d[9] + a.d[14]*b.d[10] + a.d[23]*b.d[11] + a.d[32]*b.d[12] + a.d[41]*b.d[13] + a.d[50]*b.d[14] + a.d[59]*b.d[15] + a.d[68]*b.d[16] + a.d[77]*b.d[17];\n    c.d[47] = a.d[5]*b.d[18] + a.d[14]*b.d[19] + a.d[23]*b.d[20] + a.d[32]*b.d[21] + a.d[41]*b.d[22] + a.d[50]*b.d[23] + a.d[59]*b.d[24] + a.d[68]*b.d[25] + a.d[77]*b.d[26];\n    c.d[48] = a.d[5]*b.d[27] + a.d[14]*b.d[28] + a.d[23]*b.d[29] + a.d[32]*b.d[30] + a.d[41]*b.d[31] + a.d[50]*b.d[32] + a.d[59]*b.d[33] + a.d[68]*b.d[34] + a.d[77]*b.d[35];\n    c.d[49] = a.d[5]*b.d[36] + a.d[14]*b.d[37] + a.d[23]*b.d[38] + a.d[32]*b.d[39] + a.d[41]*b.d[40] + a.d[50]*b.d[41] + a.d[59]*b.d[42] + a.d[68]*b.d[43] + a.d[77]*b.d[44];\n    c.d[50] = a.d[5]*b.d[45] + a.d[14]*b.d[46] + a.d[23]*b.d[47] + a.d[32]*b.d[48] + a.d[41]*b.d[49] + a.d[50]*b.d[50] + a.d[59]*b.d[51] + a.d[68]*b.d[52] + a.d[77]*b.d[53];\n    c.d[51] = a.d[5]*b.d[54] + a.d[14]*b.d[55] + a.d[23]*b.d[56] + a.d[32]*b.d[57] + a.d[41]*b.d[58] + a.d[50]*b.d[59] + a.d[59]*b.d[60] + a.d[68]*b.d[61] + a.d[77]*b.d[62];\n    c.d[52] = a.d[5]*b.d[63] + a.d[14]*b.d[64] + a.d[23]*b.d[65] + a.d[32]*b.d[66] + a.d[41]*b.d[67] + a.d[50]*b.d[68] + a.d[59]*b.d[69] + a.d[68]*b.d[70] + a.d[77]*b.d[71];\n    c.d[53] = a.d[5]*b.d[72] + a.d[14]*b.d[73] + a.d[23]*b.d[74] + a.d[32]*b.d[75] + a.d[41]*b.d[76] + a.d[50]*b.d[77] + a.d[59]*b.d[78] + a.d[68]*b.d[79] + a.d[77]*b.d[80];\n\n    c.d[54] = a.d[6]*b.d[0] + a.d[15]*b.d[1] + a.d[24]*b.d[2] + a.d[33]*b.d[3] + a.d[42]*b.d[4] + a.d[51]*b.d[5] + a.d[60]*b.d[6] + a.d[69]*b.d[7] + a.d[78]*b.d[8];\n    c.d[55] = a.d[6]*b.d[9] + a.d[15]*b.d[10] + a.d[24]*b.d[11] + a.d[33]*b.d[12] + a.d[42]*b.d[13] + a.d[51]*b.d[14] + a.d[60]*b.d[15] + a.d[69]*b.d[16] + a.d[78]*b.d[17];\n    c.d[56] = a.d[6]*b.d[18] + a.d[15]*b.d[19] + a.d[24]*b.d[20] + a.d[33]*b.d[21] + a.d[42]*b.d[22] + a.d[51]*b.d[23] + a.d[60]*b.d[24] + a.d[69]*b.d[25] + a.d[78]*b.d[26];\n    c.d[57] = a.d[6]*b.d[27] + a.d[15]*b.d[28] + a.d[24]*b.d[29] + a.d[33]*b.d[30] + a.d[42]*b.d[31] + a.d[51]*b.d[32] + a.d[60]*b.d[33] + a.d[69]*b.d[34] + a.d[78]*b.d[35];\n    c.d[58] = a.d[6]*b.d[36] + a.d[15]*b.d[37] + a.d[24]*b.d[38] + a.d[33]*b.d[39] + a.d[42]*b.d[40] + a.d[51]*b.d[41] + a.d[60]*b.d[42] + a.d[69]*b.d[43] + a.d[78]*b.d[44];\n    c.d[59] = a.d[6]*b.d[45] + a.d[15]*b.d[46] + a.d[24]*b.d[47] + a.d[33]*b.d[48] + a.d[42]*b.d[49] + a.d[51]*b.d[50] + a.d[60]*b.d[51] + a.d[69]*b.d[52] + a.d[78]*b.d[53];\n    c.d[60] = a.d[6]*b.d[54] + a.d[15]*b.d[55] + a.d[24]*b.d[56] + a.d[33]*b.d[57] + a.d[42]*b.d[58] + a.d[51]*b.d[59] + a.d[60]*b.d[60] + a.d[69]*b.d[61] + a.d[78]*b.d[62];\n    c.d[61] = a.d[6]*b.d[63] + a.d[15]*b.d[64] + a.d[24]*b.d[65] + a.d[33]*b.d[66] + a.d[42]*b.d[67] + a.d[51]*b.d[68] + a.d[60]*b.d[69] + a.d[69]*b.d[70] + a.d[78]*b.d[71];\n    c.d[62] = a.d[6]*b.d[72] + a.d[15]*b.d[73] + a.d[24]*b.d[74] + a.d[33]*b.d[75] + a.d[42]*b.d[76] + a.d[51]*b.d[77] + a.d[60]*b.d[78] + a.d[69]*b.d[79] + a.d[78]*b.d[80];\n\n    c.d[63] = a.d[7]*b.d[0] + a.d[16]*b.d[1] + a.d[25]*b.d[2] + a.d[34]*b.d[3] + a.d[43]*b.d[4] + a.d[52]*b.d[5] + a.d[61]*b.d[6] + a.d[70]*b.d[7] + a.d[79]*b.d[8];\n    c.d[64] = a.d[7]*b.d[9] + a.d[16]*b.d[10] + a.d[25]*b.d[11] + a.d[34]*b.d[12] + a.d[43]*b.d[13] + a.d[52]*b.d[14] + a.d[61]*b.d[15] + a.d[70]*b.d[16] + a.d[79]*b.d[17];\n    c.d[65] = a.d[7]*b.d[18] + a.d[16]*b.d[19] + a.d[25]*b.d[20] + a.d[34]*b.d[21] + a.d[43]*b.d[22] + a.d[52]*b.d[23] + a.d[61]*b.d[24] + a.d[70]*b.d[25] + a.d[79]*b.d[26];\n    c.d[66] = a.d[7]*b.d[27] + a.d[16]*b.d[28] + a.d[25]*b.d[29] + a.d[34]*b.d[30] + a.d[43]*b.d[31] + a.d[52]*b.d[32] + a.d[61]*b.d[33] + a.d[70]*b.d[34] + a.d[79]*b.d[35];\n    c.d[67] = a.d[7]*b.d[36] + a.d[16]*b.d[37] + a.d[25]*b.d[38] + a.d[34]*b.d[39] + a.d[43]*b.d[40] + a.d[52]*b.d[41] + a.d[61]*b.d[42] + a.d[70]*b.d[43] + a.d[79]*b.d[44];\n    c.d[68] = a.d[7]*b.d[45] + a.d[16]*b.d[46] + a.d[25]*b.d[47] + a.d[34]*b.d[48] + a.d[43]*b.d[49] + a.d[52]*b.d[50] + a.d[61]*b.d[51] + a.d[70]*b.d[52] + a.d[79]*b.d[53];\n    c.d[69] = a.d[7]*b.d[54] + a.d[16]*b.d[55] + a.d[25]*b.d[56] + a.d[34]*b.d[57] + a.d[43]*b.d[58] + a.d[52]*b.d[59] + a.d[61]*b.d[60] + a.d[70]*b.d[61] + a.d[79]*b.d[62];\n    c.d[70] = a.d[7]*b.d[63] + a.d[16]*b.d[64] + a.d[25]*b.d[65] + a.d[34]*b.d[66] + a.d[43]*b.d[67] + a.d[52]*b.d[68] + a.d[61]*b.d[69] + a.d[70]*b.d[70] + a.d[79]*b.d[71];\n    c.d[71] = a.d[7]*b.d[72] + a.d[16]*b.d[73] + a.d[25]*b.d[74] + a.d[34]*b.d[75] + a.d[43]*b.d[76] + a.d[52]*b.d[77] + a.d[61]*b.d[78] + a.d[70]*b.d[79] + a.d[79]*b.d[80];\n\n    c.d[72] = a.d[8]*b.d[0] + a.d[17]*b.d[1] + a.d[26]*b.d[2] + a.d[35]*b.d[3] + a.d[44]*b.d[4] + a.d[53]*b.d[5] + a.d[62]*b.d[6] + a.d[71]*b.d[7] + a.d[80]*b.d[8];\n    c.d[73] = a.d[8]*b.d[9] + a.d[17]*b.d[10] + a.d[26]*b.d[11] + a.d[35]*b.d[12] + a.d[44]*b.d[13] + a.d[53]*b.d[14] + a.d[62]*b.d[15] + a.d[71]*b.d[16] + a.d[80]*b.d[17];\n    c.d[74] = a.d[8]*b.d[18] + a.d[17]*b.d[19] + a.d[26]*b.d[20] + a.d[35]*b.d[21] + a.d[44]*b.d[22] + a.d[53]*b.d[23] + a.d[62]*b.d[24] + a.d[71]*b.d[25] + a.d[80]*b.d[26];\n    c.d[75] = a.d[8]*b.d[27] + a.d[17]*b.d[28] + a.d[26]*b.d[29] + a.d[35]*b.d[30] + a.d[44]*b.d[31] + a.d[53]*b.d[32] + a.d[62]*b.d[33] + a.d[71]*b.d[34] + a.d[80]*b.d[35];\n    c.d[76] = a.d[8]*b.d[36] + a.d[17]*b.d[37] + a.d[26]*b.d[38] + a.d[35]*b.d[39] + a.d[44]*b.d[40] + a.d[53]*b.d[41] + a.d[62]*b.d[42] + a.d[71]*b.d[43] + a.d[80]*b.d[44];\n    c.d[77] = a.d[8]*b.d[45] + a.d[17]*b.d[46] + a.d[26]*b.d[47] + a.d[35]*b.d[48] + a.d[44]*b.d[49] + a.d[53]*b.d[50] + a.d[62]*b.d[51] + a.d[71]*b.d[52] + a.d[80]*b.d[53];\n    c.d[78] = a.d[8]*b.d[54] + a.d[17]*b.d[55] + a.d[26]*b.d[56] + a.d[35]*b.d[57] + a.d[44]*b.d[58] + a.d[53]*b.d[59] + a.d[62]*b.d[60] + a.d[71]*b.d[61] + a.d[80]*b.d[62];\n    c.d[79] = a.d[8]*b.d[63] + a.d[17]*b.d[64] + a.d[26]*b.d[65] + a.d[35]*b.d[66] + a.d[44]*b.d[67] + a.d[53]*b.d[68] + a.d[62]*b.d[69] + a.d[71]*b.d[70] + a.d[80]*b.d[71];\n    c.d[80] = a.d[8]*b.d[72] + a.d[17]*b.d[73] + a.d[26]*b.d[74] + a.d[35]*b.d[75] + a.d[44]*b.d[76] + a.d[53]*b.d[77] + a.d[62]*b.d[78] + a.d[71]*b.d[79] + a.d[80]*b.d[80];\n\n    return c;\n    \n}\n\n//-----------------------------------------------------------------------------\n// (a ddot b)_ijkl = a_ijmn b_mnkl where b is super-symmetric\ninline tens4d ddot(const tens4d& a, const tens4ds& b)\n{\n    tens4d c;\n    \n    c.d[0] = a.d[0]*b.d[0] + a.d[9]*b.d[1] + a.d[18]*b.d[3] + a.d[27]*b.d[6] + a.d[54]*b.d[6] + a.d[36]*b.d[10] + a.d[63]*b.d[10] + a.d[45]*b.d[15] + a.d[72]*b.d[15];\n    c.d[1] = a.d[0]*b.d[1] + a.d[9]*b.d[2] + a.d[18]*b.d[4] + a.d[27]*b.d[7] + a.d[54]*b.d[7] + a.d[36]*b.d[11] + a.d[63]*b.d[11] + a.d[45]*b.d[16] + a.d[72]*b.d[16];\n    c.d[2] = a.d[0]*b.d[3] + a.d[9]*b.d[4] + a.d[18]*b.d[5] + a.d[27]*b.d[8] + a.d[54]*b.d[8] + a.d[36]*b.d[12] + a.d[63]*b.d[12] + a.d[45]*b.d[17] + a.d[72]*b.d[17];\n    c.d[3] = a.d[0]*b.d[6] + a.d[9]*b.d[7] + a.d[18]*b.d[8] + a.d[27]*b.d[9] + a.d[54]*b.d[9] + a.d[36]*b.d[13] + a.d[63]*b.d[13] + a.d[45]*b.d[18] + a.d[72]*b.d[18];\n    c.d[4] = a.d[0]*b.d[10] + a.d[9]*b.d[11] + a.d[18]*b.d[12] + a.d[27]*b.d[13] + a.d[54]*b.d[13] + a.d[36]*b.d[14] + a.d[63]*b.d[14] + a.d[45]*b.d[19] + a.d[72]*b.d[19];\n    c.d[5] = a.d[0]*b.d[15] + a.d[9]*b.d[16] + a.d[18]*b.d[17] + a.d[27]*b.d[18] + a.d[54]*b.d[18] + a.d[36]*b.d[19] + a.d[63]*b.d[19] + a.d[45]*b.d[20] + a.d[72]*b.d[20];\n    c.d[6] = a.d[0]*b.d[6] + a.d[9]*b.d[7] + a.d[18]*b.d[8] + a.d[27]*b.d[9] + a.d[54]*b.d[9] + a.d[36]*b.d[13] + a.d[63]*b.d[13] + a.d[45]*b.d[18] + a.d[72]*b.d[18];\n    c.d[7] = a.d[0]*b.d[10] + a.d[9]*b.d[11] + a.d[18]*b.d[12] + a.d[27]*b.d[13] + a.d[54]*b.d[13] + a.d[36]*b.d[14] + a.d[63]*b.d[14] + a.d[45]*b.d[19] + a.d[72]*b.d[19];\n    c.d[8] = a.d[0]*b.d[15] + a.d[9]*b.d[16] + a.d[18]*b.d[17] + a.d[27]*b.d[18] + a.d[54]*b.d[18] + a.d[36]*b.d[19] + a.d[63]*b.d[19] + a.d[45]*b.d[20] + a.d[72]*b.d[20];\n\n    c.d[9] = a.d[1]*b.d[0] + a.d[10]*b.d[1] + a.d[19]*b.d[3] + a.d[28]*b.d[6] + a.d[55]*b.d[6] + a.d[37]*b.d[10] + a.d[64]*b.d[10] + a.d[46]*b.d[15] + a.d[73]*b.d[15];\n    c.d[10] = a.d[1]*b.d[1] + a.d[10]*b.d[2] + a.d[19]*b.d[4] + a.d[28]*b.d[7] + a.d[55]*b.d[7] + a.d[37]*b.d[11] + a.d[64]*b.d[11] + a.d[46]*b.d[16] + a.d[73]*b.d[16];\n    c.d[11] = a.d[1]*b.d[3] + a.d[10]*b.d[4] + a.d[19]*b.d[5] + a.d[28]*b.d[8] + a.d[55]*b.d[8] + a.d[37]*b.d[12] + a.d[64]*b.d[12] + a.d[46]*b.d[17] + a.d[73]*b.d[17];\n    c.d[12] = a.d[1]*b.d[6] + a.d[10]*b.d[7] + a.d[19]*b.d[8] + a.d[28]*b.d[9] + a.d[55]*b.d[9] + a.d[37]*b.d[13] + a.d[64]*b.d[13] + a.d[46]*b.d[18] + a.d[73]*b.d[18];\n    c.d[13] = a.d[1]*b.d[10] + a.d[10]*b.d[11] + a.d[19]*b.d[12] + a.d[28]*b.d[13] + a.d[55]*b.d[13] + a.d[37]*b.d[14] + a.d[64]*b.d[14] + a.d[46]*b.d[19] + a.d[73]*b.d[19];\n    c.d[14] = a.d[1]*b.d[15] + a.d[10]*b.d[16] + a.d[19]*b.d[17] + a.d[28]*b.d[18] + a.d[55]*b.d[18] + a.d[37]*b.d[19] + a.d[64]*b.d[19] + a.d[46]*b.d[20] + a.d[73]*b.d[20];\n    c.d[15] = a.d[1]*b.d[6] + a.d[10]*b.d[7] + a.d[19]*b.d[8] + a.d[28]*b.d[9] + a.d[55]*b.d[9] + a.d[37]*b.d[13] + a.d[64]*b.d[13] + a.d[46]*b.d[18] + a.d[73]*b.d[18];\n    c.d[16] = a.d[1]*b.d[10] + a.d[10]*b.d[11] + a.d[19]*b.d[12] + a.d[28]*b.d[13] + a.d[55]*b.d[13] + a.d[37]*b.d[14] + a.d[64]*b.d[14] + a.d[46]*b.d[19] + a.d[73]*b.d[19];\n    c.d[17] = a.d[1]*b.d[15] + a.d[10]*b.d[16] + a.d[19]*b.d[17] + a.d[28]*b.d[18] + a.d[55]*b.d[18] + a.d[37]*b.d[19] + a.d[64]*b.d[19] + a.d[46]*b.d[20] + a.d[73]*b.d[20];\n\n    c.d[18] = a.d[2]*b.d[0] + a.d[11]*b.d[1] + a.d[20]*b.d[3] + a.d[29]*b.d[6] + a.d[56]*b.d[6] + a.d[38]*b.d[10] + a.d[65]*b.d[10] + a.d[47]*b.d[15] + a.d[74]*b.d[15];\n    c.d[19] = a.d[2]*b.d[1] + a.d[11]*b.d[2] + a.d[20]*b.d[4] + a.d[29]*b.d[7] + a.d[56]*b.d[7] + a.d[38]*b.d[11] + a.d[65]*b.d[11] + a.d[47]*b.d[16] + a.d[74]*b.d[16];\n    c.d[20] = a.d[2]*b.d[3] + a.d[11]*b.d[4] + a.d[20]*b.d[5] + a.d[29]*b.d[8] + a.d[56]*b.d[8] + a.d[38]*b.d[12] + a.d[65]*b.d[12] + a.d[47]*b.d[17] + a.d[74]*b.d[17];\n    c.d[21] = a.d[2]*b.d[6] + a.d[11]*b.d[7] + a.d[20]*b.d[8] + a.d[29]*b.d[9] + a.d[56]*b.d[9] + a.d[38]*b.d[13] + a.d[65]*b.d[13] + a.d[47]*b.d[18] + a.d[74]*b.d[18];\n    c.d[22] = a.d[2]*b.d[10] + a.d[11]*b.d[11] + a.d[20]*b.d[12] + a.d[29]*b.d[13] + a.d[56]*b.d[13] + a.d[38]*b.d[14] + a.d[65]*b.d[14] + a.d[47]*b.d[19] + a.d[74]*b.d[19];\n    c.d[23] = a.d[2]*b.d[15] + a.d[11]*b.d[16] + a.d[20]*b.d[17] + a.d[29]*b.d[18] + a.d[56]*b.d[18] + a.d[38]*b.d[19] + a.d[65]*b.d[19] + a.d[47]*b.d[20] + a.d[74]*b.d[20];\n    c.d[24] = a.d[2]*b.d[6] + a.d[11]*b.d[7] + a.d[20]*b.d[8] + a.d[29]*b.d[9] + a.d[56]*b.d[9] + a.d[38]*b.d[13] + a.d[65]*b.d[13] + a.d[47]*b.d[18] + a.d[74]*b.d[18];\n    c.d[25] = a.d[2]*b.d[10] + a.d[11]*b.d[11] + a.d[20]*b.d[12] + a.d[29]*b.d[13] + a.d[56]*b.d[13] + a.d[38]*b.d[14] + a.d[65]*b.d[14] + a.d[47]*b.d[19] + a.d[74]*b.d[19];\n    c.d[26] = a.d[2]*b.d[15] + a.d[11]*b.d[16] + a.d[20]*b.d[17] + a.d[29]*b.d[18] + a.d[56]*b.d[18] + a.d[38]*b.d[19] + a.d[65]*b.d[19] + a.d[47]*b.d[20] + a.d[74]*b.d[20];\n\n    c.d[27] = a.d[3]*b.d[0] + a.d[12]*b.d[1] + a.d[21]*b.d[3] + a.d[30]*b.d[6] + a.d[57]*b.d[6] + a.d[39]*b.d[10] + a.d[66]*b.d[10] + a.d[48]*b.d[15] + a.d[75]*b.d[15];\n    c.d[28] = a.d[3]*b.d[1] + a.d[12]*b.d[2] + a.d[21]*b.d[4] + a.d[30]*b.d[7] + a.d[57]*b.d[7] + a.d[39]*b.d[11] + a.d[66]*b.d[11] + a.d[48]*b.d[16] + a.d[75]*b.d[16];\n    c.d[29] = a.d[3]*b.d[3] + a.d[12]*b.d[4] + a.d[21]*b.d[5] + a.d[30]*b.d[8] + a.d[57]*b.d[8] + a.d[39]*b.d[12] + a.d[66]*b.d[12] + a.d[48]*b.d[17] + a.d[75]*b.d[17];\n    c.d[30] = a.d[3]*b.d[6] + a.d[12]*b.d[7] + a.d[21]*b.d[8] + a.d[30]*b.d[9] + a.d[57]*b.d[9] + a.d[39]*b.d[13] + a.d[66]*b.d[13] + a.d[48]*b.d[18] + a.d[75]*b.d[18];\n    c.d[31] = a.d[3]*b.d[10] + a.d[12]*b.d[11] + a.d[21]*b.d[12] + a.d[30]*b.d[13] + a.d[57]*b.d[13] + a.d[39]*b.d[14] + a.d[66]*b.d[14] + a.d[48]*b.d[19] + a.d[75]*b.d[19];\n    c.d[32] = a.d[3]*b.d[15] + a.d[12]*b.d[16] + a.d[21]*b.d[17] + a.d[30]*b.d[18] + a.d[57]*b.d[18] + a.d[39]*b.d[19] + a.d[66]*b.d[19] + a.d[48]*b.d[20] + a.d[75]*b.d[20];\n    c.d[33] = a.d[3]*b.d[6] + a.d[12]*b.d[7] + a.d[21]*b.d[8] + a.d[30]*b.d[9] + a.d[57]*b.d[9] + a.d[39]*b.d[13] + a.d[66]*b.d[13] + a.d[48]*b.d[18] + a.d[75]*b.d[18];\n    c.d[34] = a.d[3]*b.d[10] + a.d[12]*b.d[11] + a.d[21]*b.d[12] + a.d[30]*b.d[13] + a.d[57]*b.d[13] + a.d[39]*b.d[14] + a.d[66]*b.d[14] + a.d[48]*b.d[19] + a.d[75]*b.d[19];\n    c.d[35] = a.d[3]*b.d[15] + a.d[12]*b.d[16] + a.d[21]*b.d[17] + a.d[30]*b.d[18] + a.d[57]*b.d[18] + a.d[39]*b.d[19] + a.d[66]*b.d[19] + a.d[48]*b.d[20] + a.d[75]*b.d[20];\n\n    c.d[36] = a.d[4]*b.d[0] + a.d[13]*b.d[1] + a.d[22]*b.d[3] + a.d[31]*b.d[6] + a.d[58]*b.d[6] + a.d[40]*b.d[10] + a.d[67]*b.d[10] + a.d[49]*b.d[15] + a.d[76]*b.d[15];\n    c.d[37] = a.d[4]*b.d[1] + a.d[13]*b.d[2] + a.d[22]*b.d[4] + a.d[31]*b.d[7] + a.d[58]*b.d[7] + a.d[40]*b.d[11] + a.d[67]*b.d[11] + a.d[49]*b.d[16] + a.d[76]*b.d[16];\n    c.d[38] = a.d[4]*b.d[3] + a.d[13]*b.d[4] + a.d[22]*b.d[5] + a.d[31]*b.d[8] + a.d[58]*b.d[8] + a.d[40]*b.d[12] + a.d[67]*b.d[12] + a.d[49]*b.d[17] + a.d[76]*b.d[17];\n    c.d[39] = a.d[4]*b.d[6] + a.d[13]*b.d[7] + a.d[22]*b.d[8] + a.d[31]*b.d[9] + a.d[58]*b.d[9] + a.d[40]*b.d[13] + a.d[67]*b.d[13] + a.d[49]*b.d[18] + a.d[76]*b.d[18];\n    c.d[40] = a.d[4]*b.d[10] + a.d[13]*b.d[11] + a.d[22]*b.d[12] + a.d[31]*b.d[13] + a.d[58]*b.d[13] + a.d[40]*b.d[14] + a.d[67]*b.d[14] + a.d[49]*b.d[19] + a.d[76]*b.d[19];\n    c.d[41] = a.d[4]*b.d[15] + a.d[13]*b.d[16] + a.d[22]*b.d[17] + a.d[31]*b.d[18] + a.d[58]*b.d[18] + a.d[40]*b.d[19] + a.d[67]*b.d[19] + a.d[49]*b.d[20] + a.d[76]*b.d[20];\n    c.d[42] = a.d[4]*b.d[6] + a.d[13]*b.d[7] + a.d[22]*b.d[8] + a.d[31]*b.d[9] + a.d[58]*b.d[9] + a.d[40]*b.d[13] + a.d[67]*b.d[13] + a.d[49]*b.d[18] + a.d[76]*b.d[18];\n    c.d[43] = a.d[4]*b.d[10] + a.d[13]*b.d[11] + a.d[22]*b.d[12] + a.d[31]*b.d[13] + a.d[58]*b.d[13] + a.d[40]*b.d[14] + a.d[67]*b.d[14] + a.d[49]*b.d[19] + a.d[76]*b.d[19];\n    c.d[44] = a.d[4]*b.d[15] + a.d[13]*b.d[16] + a.d[22]*b.d[17] + a.d[31]*b.d[18] + a.d[58]*b.d[18] + a.d[40]*b.d[19] + a.d[67]*b.d[19] + a.d[49]*b.d[20] + a.d[76]*b.d[20];\n\n    c.d[45] = a.d[5]*b.d[0] + a.d[14]*b.d[1] + a.d[23]*b.d[3] + a.d[32]*b.d[6] + a.d[59]*b.d[6] + a.d[41]*b.d[10] + a.d[68]*b.d[10] + a.d[50]*b.d[15] + a.d[77]*b.d[15];\n    c.d[46] = a.d[5]*b.d[1] + a.d[14]*b.d[2] + a.d[23]*b.d[4] + a.d[32]*b.d[7] + a.d[59]*b.d[7] + a.d[41]*b.d[11] + a.d[68]*b.d[11] + a.d[50]*b.d[16] + a.d[77]*b.d[16];\n    c.d[47] = a.d[5]*b.d[3] + a.d[14]*b.d[4] + a.d[23]*b.d[5] + a.d[32]*b.d[8] + a.d[59]*b.d[8] + a.d[41]*b.d[12] + a.d[68]*b.d[12] + a.d[50]*b.d[17] + a.d[77]*b.d[17];\n    c.d[48] = a.d[5]*b.d[6] + a.d[14]*b.d[7] + a.d[23]*b.d[8] + a.d[32]*b.d[9] + a.d[59]*b.d[9] + a.d[41]*b.d[13] + a.d[68]*b.d[13] + a.d[50]*b.d[18] + a.d[77]*b.d[18];\n    c.d[49] = a.d[5]*b.d[10] + a.d[14]*b.d[11] + a.d[23]*b.d[12] + a.d[32]*b.d[13] + a.d[59]*b.d[13] + a.d[41]*b.d[14] + a.d[68]*b.d[14] + a.d[50]*b.d[19] + a.d[77]*b.d[19];\n    c.d[50] = a.d[5]*b.d[15] + a.d[14]*b.d[16] + a.d[23]*b.d[17] + a.d[32]*b.d[18] + a.d[59]*b.d[18] + a.d[41]*b.d[19] + a.d[68]*b.d[19] + a.d[50]*b.d[20] + a.d[77]*b.d[20];\n    c.d[51] = a.d[5]*b.d[6] + a.d[14]*b.d[7] + a.d[23]*b.d[8] + a.d[32]*b.d[9] + a.d[59]*b.d[9] + a.d[41]*b.d[13] + a.d[68]*b.d[13] + a.d[50]*b.d[18] + a.d[77]*b.d[18];\n    c.d[52] = a.d[5]*b.d[10] + a.d[14]*b.d[11] + a.d[23]*b.d[12] + a.d[32]*b.d[13] + a.d[59]*b.d[13] + a.d[41]*b.d[14] + a.d[68]*b.d[14] + a.d[50]*b.d[19] + a.d[77]*b.d[19];\n    c.d[53] = a.d[5]*b.d[15] + a.d[14]*b.d[16] + a.d[23]*b.d[17] + a.d[32]*b.d[18] + a.d[59]*b.d[18] + a.d[41]*b.d[19] + a.d[68]*b.d[19] + a.d[50]*b.d[20] + a.d[77]*b.d[20];\n\n    c.d[54] = a.d[6]*b.d[0] + a.d[15]*b.d[1] + a.d[24]*b.d[3] + a.d[33]*b.d[6] + a.d[60]*b.d[6] + a.d[42]*b.d[10] + a.d[69]*b.d[10] + a.d[51]*b.d[15] + a.d[78]*b.d[15];\n    c.d[55] = a.d[6]*b.d[1] + a.d[15]*b.d[2] + a.d[24]*b.d[4] + a.d[33]*b.d[7] + a.d[60]*b.d[7] + a.d[42]*b.d[11] + a.d[69]*b.d[11] + a.d[51]*b.d[16] + a.d[78]*b.d[16];\n    c.d[56] = a.d[6]*b.d[3] + a.d[15]*b.d[4] + a.d[24]*b.d[5] + a.d[33]*b.d[8] + a.d[60]*b.d[8] + a.d[42]*b.d[12] + a.d[69]*b.d[12] + a.d[51]*b.d[17] + a.d[78]*b.d[17];\n    c.d[57] = a.d[6]*b.d[6] + a.d[15]*b.d[7] + a.d[24]*b.d[8] + a.d[33]*b.d[9] + a.d[60]*b.d[9] + a.d[42]*b.d[13] + a.d[69]*b.d[13] + a.d[51]*b.d[18] + a.d[78]*b.d[18];\n    c.d[58] = a.d[6]*b.d[10] + a.d[15]*b.d[11] + a.d[24]*b.d[12] + a.d[33]*b.d[13] + a.d[60]*b.d[13] + a.d[42]*b.d[14] + a.d[69]*b.d[14] + a.d[51]*b.d[19] + a.d[78]*b.d[19];\n    c.d[59] = a.d[6]*b.d[15] + a.d[15]*b.d[16] + a.d[24]*b.d[17] + a.d[33]*b.d[18] + a.d[60]*b.d[18] + a.d[42]*b.d[19] + a.d[69]*b.d[19] + a.d[51]*b.d[20] + a.d[78]*b.d[20];\n    c.d[60] = a.d[6]*b.d[6] + a.d[15]*b.d[7] + a.d[24]*b.d[8] + a.d[33]*b.d[9] + a.d[60]*b.d[9] + a.d[42]*b.d[13] + a.d[69]*b.d[13] + a.d[51]*b.d[18] + a.d[78]*b.d[18];\n    c.d[61] = a.d[6]*b.d[10] + a.d[15]*b.d[11] + a.d[24]*b.d[12] + a.d[33]*b.d[13] + a.d[60]*b.d[13] + a.d[42]*b.d[14] + a.d[69]*b.d[14] + a.d[51]*b.d[19] + a.d[78]*b.d[19];\n    c.d[62] = a.d[6]*b.d[15] + a.d[15]*b.d[16] + a.d[24]*b.d[17] + a.d[33]*b.d[18] + a.d[60]*b.d[18] + a.d[42]*b.d[19] + a.d[69]*b.d[19] + a.d[51]*b.d[20] + a.d[78]*b.d[20];\n\n    c.d[63] = a.d[7]*b.d[0] + a.d[16]*b.d[1] + a.d[25]*b.d[3] + a.d[34]*b.d[6] + a.d[61]*b.d[6] + a.d[43]*b.d[10] + a.d[70]*b.d[10] + a.d[52]*b.d[15] + a.d[79]*b.d[15];\n    c.d[64] = a.d[7]*b.d[1] + a.d[16]*b.d[2] + a.d[25]*b.d[4] + a.d[34]*b.d[7] + a.d[61]*b.d[7] + a.d[43]*b.d[11] + a.d[70]*b.d[11] + a.d[52]*b.d[16] + a.d[79]*b.d[16];\n    c.d[65] = a.d[7]*b.d[3] + a.d[16]*b.d[4] + a.d[25]*b.d[5] + a.d[34]*b.d[8] + a.d[61]*b.d[8] + a.d[43]*b.d[12] + a.d[70]*b.d[12] + a.d[52]*b.d[17] + a.d[79]*b.d[17];\n    c.d[66] = a.d[7]*b.d[6] + a.d[16]*b.d[7] + a.d[25]*b.d[8] + a.d[34]*b.d[9] + a.d[61]*b.d[9] + a.d[43]*b.d[13] + a.d[70]*b.d[13] + a.d[52]*b.d[18] + a.d[79]*b.d[18];\n    c.d[67] = a.d[7]*b.d[10] + a.d[16]*b.d[11] + a.d[25]*b.d[12] + a.d[34]*b.d[13] + a.d[61]*b.d[13] + a.d[43]*b.d[14] + a.d[70]*b.d[14] + a.d[52]*b.d[19] + a.d[79]*b.d[19];\n    c.d[68] = a.d[7]*b.d[15] + a.d[16]*b.d[16] + a.d[25]*b.d[17] + a.d[34]*b.d[18] + a.d[61]*b.d[18] + a.d[43]*b.d[19] + a.d[70]*b.d[19] + a.d[52]*b.d[20] + a.d[79]*b.d[20];\n    c.d[69] = a.d[7]*b.d[6] + a.d[16]*b.d[7] + a.d[25]*b.d[8] + a.d[34]*b.d[9] + a.d[61]*b.d[9] + a.d[43]*b.d[13] + a.d[70]*b.d[13] + a.d[52]*b.d[18] + a.d[79]*b.d[18];\n    c.d[70] = a.d[7]*b.d[10] + a.d[16]*b.d[11] + a.d[25]*b.d[12] + a.d[34]*b.d[13] + a.d[61]*b.d[13] + a.d[43]*b.d[14] + a.d[70]*b.d[14] + a.d[52]*b.d[19] + a.d[79]*b.d[19];\n    c.d[71] = a.d[7]*b.d[15] + a.d[16]*b.d[16] + a.d[25]*b.d[17] + a.d[34]*b.d[18] + a.d[61]*b.d[18] + a.d[43]*b.d[19] + a.d[70]*b.d[19] + a.d[52]*b.d[20] + a.d[79]*b.d[20];\n\n    c.d[72] = a.d[8]*b.d[0] + a.d[17]*b.d[1] + a.d[26]*b.d[3] + a.d[35]*b.d[6] + a.d[62]*b.d[6] + a.d[44]*b.d[10] + a.d[71]*b.d[10] + a.d[53]*b.d[15] + a.d[80]*b.d[15];\n    c.d[73] = a.d[8]*b.d[1] + a.d[17]*b.d[2] + a.d[26]*b.d[4] + a.d[35]*b.d[7] + a.d[62]*b.d[7] + a.d[44]*b.d[11] + a.d[71]*b.d[11] + a.d[53]*b.d[16] + a.d[80]*b.d[16];\n    c.d[74] = a.d[8]*b.d[3] + a.d[17]*b.d[4] + a.d[26]*b.d[5] + a.d[35]*b.d[8] + a.d[62]*b.d[8] + a.d[44]*b.d[12] + a.d[71]*b.d[12] + a.d[53]*b.d[17] + a.d[80]*b.d[17];\n    c.d[75] = a.d[8]*b.d[6] + a.d[17]*b.d[7] + a.d[26]*b.d[8] + a.d[35]*b.d[9] + a.d[62]*b.d[9] + a.d[44]*b.d[13] + a.d[71]*b.d[13] + a.d[53]*b.d[18] + a.d[80]*b.d[18];\n    c.d[76] = a.d[8]*b.d[10] + a.d[17]*b.d[11] + a.d[26]*b.d[12] + a.d[35]*b.d[13] + a.d[62]*b.d[13] + a.d[44]*b.d[14] + a.d[71]*b.d[14] + a.d[53]*b.d[19] + a.d[80]*b.d[19];\n    c.d[77] = a.d[8]*b.d[15] + a.d[17]*b.d[16] + a.d[26]*b.d[17] + a.d[35]*b.d[18] + a.d[62]*b.d[18] + a.d[44]*b.d[19] + a.d[71]*b.d[19] + a.d[53]*b.d[20] + a.d[80]*b.d[20];\n    c.d[78] = a.d[8]*b.d[6] + a.d[17]*b.d[7] + a.d[26]*b.d[8] + a.d[35]*b.d[9] + a.d[62]*b.d[9] + a.d[44]*b.d[13] + a.d[71]*b.d[13] + a.d[53]*b.d[18] + a.d[80]*b.d[18];\n    c.d[79] = a.d[8]*b.d[10] + a.d[17]*b.d[11] + a.d[26]*b.d[12] + a.d[35]*b.d[13] + a.d[62]*b.d[13] + a.d[44]*b.d[14] + a.d[71]*b.d[14] + a.d[53]*b.d[19] + a.d[80]*b.d[19];\n    c.d[80] = a.d[8]*b.d[15] + a.d[17]*b.d[16] + a.d[26]*b.d[17] + a.d[35]*b.d[18] + a.d[62]*b.d[18] + a.d[44]*b.d[19] + a.d[71]*b.d[19] + a.d[53]*b.d[20] + a.d[80]*b.d[20];\n\n    return c;\n    \n}\n\n//-----------------------------------------------------------------------------\n// vdotTdotv_jk = a_i T_ijkl b_l\ninline mat3d vdotTdotv(const vec3d& a, const tens4d& T, const vec3d& b)\n{\n    return mat3d(a.x*b.x*T.d[0] + a.z*b.x*T.d[5] + a.y*b.x*T.d[6] + a.x*b.y*T.d[27] + a.z*b.y*T.d[32] + a.y*b.y*T.d[33] + a.x*b.z*T.d[72] + a.z*b.z*T.d[77] + a.y*b.z*T.d[78],\n    a.y*b.x*T.d[1] + a.x*b.x*T.d[3] + a.z*b.x*T.d[7] + a.y*b.y*T.d[28] + a.x*b.y*T.d[30] + a.z*b.y*T.d[34] + a.y*b.z*T.d[73] + a.x*b.z*T.d[75] + a.z*b.z*T.d[79],\n    a.z*b.x*T.d[2] + a.y*b.x*T.d[4] + a.x*b.x*T.d[8] + a.z*b.y*T.d[29] + a.y*b.y*T.d[31] + a.x*b.y*T.d[35] + a.z*b.z*T.d[74] + a.y*b.z*T.d[76] + a.x*b.z*T.d[80],\n    a.x*b.y*T.d[9] + a.z*b.y*T.d[14] + a.y*b.y*T.d[15] + a.x*b.z*T.d[36] + a.z*b.z*T.d[41] + a.y*b.z*T.d[42] + a.x*b.x*T.d[54] + a.z*b.x*T.d[59] + a.y*b.x*T.d[60],\n    a.y*b.y*T.d[10] + a.x*b.y*T.d[12] + a.z*b.y*T.d[16] + a.y*b.z*T.d[37] + a.x*b.z*T.d[39] + a.z*b.z*T.d[43] + a.y*b.x*T.d[55] + a.x*b.x*T.d[57] + a.z*b.x*T.d[61],\n    a.z*b.y*T.d[11] + a.y*b.y*T.d[13] + a.x*b.y*T.d[17] + a.z*b.z*T.d[38] + a.y*b.z*T.d[40] + a.x*b.z*T.d[44] + a.z*b.x*T.d[56] + a.y*b.x*T.d[58] + a.x*b.x*T.d[62],\n    a.x*b.z*T.d[18] + a.z*b.z*T.d[23] + a.y*b.z*T.d[24] + a.x*b.x*T.d[45] + a.z*b.x*T.d[50] + a.y*b.x*T.d[51] + a.x*b.y*T.d[63] + a.z*b.y*T.d[68] + a.y*b.y*T.d[69],\n    a.y*b.z*T.d[19] + a.x*b.z*T.d[21] + a.z*b.z*T.d[25] + a.y*b.x*T.d[46] + a.x*b.x*T.d[48] + a.z*b.x*T.d[52] + a.y*b.y*T.d[64] + a.x*b.y*T.d[66] + a.z*b.y*T.d[70],\n    a.z*b.z*T.d[20] + a.y*b.z*T.d[22] + a.x*b.z*T.d[26] + a.z*b.x*T.d[47] + a.y*b.x*T.d[49] + a.x*b.x*T.d[53] + a.z*b.y*T.d[65] + a.y*b.y*T.d[67] + a.x*b.y*T.d[71]);\n}\n\n//-----------------------------------------------------------------------------\n// inverse\ninline tens4d tens4d::inverse() const\n{\n    matrix c(9,9);\n    \n    // populate c\n    c(0,0) = d[ 0]; c(0,1) = d[ 9]; c(0,2) = d[18]; c(0,3) = d[27]; c(0,4) = d[36]; c(0,5) = d[45]; c(0,6) = d[54]; c(0,7) = d[63]; c(0,8) = d[72];\n    c(1,0) = d[ 1]; c(1,1) = d[10]; c(1,2) = d[19]; c(1,3) = d[28]; c(1,4) = d[37]; c(1,5) = d[46]; c(1,6) = d[55]; c(1,7) = d[64]; c(1,8) = d[73];\n    c(2,0) = d[ 2]; c(2,1) = d[11]; c(2,2) = d[20]; c(2,3) = d[29]; c(2,4) = d[38]; c(2,5) = d[47]; c(2,6) = d[56]; c(2,7) = d[65]; c(2,8) = d[74];\n    c(3,0) = d[ 3]; c(3,1) = d[12]; c(3,2) = d[21]; c(3,3) = d[30]; c(3,4) = d[39]; c(3,5) = d[48]; c(3,6) = d[57]; c(3,7) = d[66]; c(3,8) = d[75];\n    c(4,0) = d[ 4]; c(4,1) = d[13]; c(4,2) = d[22]; c(4,3) = d[31]; c(4,4) = d[40]; c(4,5) = d[49]; c(4,6) = d[58]; c(4,7) = d[67]; c(4,8) = d[76];\n    c(5,0) = d[ 5]; c(5,1) = d[14]; c(5,2) = d[23]; c(5,3) = d[32]; c(5,4) = d[41]; c(5,5) = d[50]; c(5,6) = d[59]; c(5,7) = d[68]; c(5,8) = d[77];\n    c(6,0) = d[ 6]; c(6,1) = d[15]; c(6,2) = d[24]; c(6,3) = d[33]; c(6,4) = d[42]; c(6,5) = d[51]; c(6,6) = d[60]; c(6,7) = d[69]; c(6,8) = d[78];\n    c(7,0) = d[ 7]; c(7,1) = d[16]; c(7,2) = d[25]; c(7,3) = d[34]; c(7,4) = d[43]; c(7,5) = d[52]; c(7,6) = d[61]; c(7,7) = d[70]; c(7,8) = d[79];\n    c(8,0) = d[ 8]; c(8,1) = d[17]; c(8,2) = d[26]; c(8,3) = d[35]; c(8,4) = d[44]; c(8,5) = d[53]; c(8,6) = d[62]; c(8,7) = d[71]; c(8,8) = d[80];\n\n    // invert c\n    matrix s = c.inverse();\n    \n    // return inverse\n    tens4d S;\n    S.d[ 0] = s(0,0); S.d[ 9] = s(0,1); S.d[18] = s(0,2); S.d[27] = s(0,3); S.d[36] = s(0,4); S.d[45] = s(0,5); S.d[54] = s(0,6); S.d[63] = s(0,7); S.d[72] = s(0,8);\n    S.d[ 1] = s(1,0); S.d[10] = s(1,1); S.d[19] = s(1,2); S.d[28] = s(1,3); S.d[37] = s(1,4); S.d[46] = s(1,5); S.d[55] = s(1,6); S.d[64] = s(1,7); S.d[73] = s(1,8);\n    S.d[ 2] = s(2,0); S.d[11] = s(2,1); S.d[20] = s(2,2); S.d[29] = s(2,3); S.d[38] = s(2,4); S.d[47] = s(2,5); S.d[56] = s(2,6); S.d[65] = s(2,7); S.d[74] = s(2,8);\n    S.d[ 3] = s(3,0); S.d[12] = s(3,1); S.d[21] = s(3,2); S.d[30] = s(3,3); S.d[39] = s(3,4); S.d[48] = s(3,5); S.d[57] = s(3,6); S.d[66] = s(3,7); S.d[75] = s(3,8);\n    S.d[ 4] = s(4,0); S.d[13] = s(4,1); S.d[22] = s(4,2); S.d[31] = s(4,3); S.d[40] = s(4,4); S.d[49] = s(4,5); S.d[58] = s(4,6); S.d[67] = s(4,7); S.d[76] = s(4,8);\n    S.d[ 5] = s(5,0); S.d[14] = s(5,1); S.d[23] = s(5,2); S.d[32] = s(5,3); S.d[41] = s(5,4); S.d[50] = s(5,5); S.d[59] = s(5,6); S.d[68] = s(5,7); S.d[77] = s(5,8);\n    S.d[ 6] = s(6,0); S.d[15] = s(6,1); S.d[24] = s(6,2); S.d[33] = s(6,3); S.d[42] = s(6,4); S.d[51] = s(6,5); S.d[60] = s(6,6); S.d[69] = s(6,7); S.d[78] = s(6,8);\n    S.d[ 7] = s(7,0); S.d[16] = s(7,1); S.d[25] = s(7,2); S.d[34] = s(7,3); S.d[43] = s(7,4); S.d[52] = s(7,5); S.d[61] = s(7,6); S.d[70] = s(7,7); S.d[79] = s(7,8);\n    S.d[ 8] = s(8,0); S.d[17] = s(8,1); S.d[26] = s(8,2); S.d[35] = s(8,3); S.d[44] = s(8,4); S.d[53] = s(8,5); S.d[62] = s(8,6); S.d[71] = s(8,7); S.d[80] = s(8,8);\n\n    return S;\n}\n\n"
  },
  {
    "path": "FECore/tens4dmm.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens4d.h\n// Users should not include this file manually!\n\ninline tens4dmm::tens4dmm(const double g)\n{\n    d[ 0] = d[ 1] = d[ 2] = d[ 3] = d[ 4] = d[ 5] =\n    d[ 6] = d[ 7] = d[ 8] = d[ 9] = d[10] = d[11] =\n    d[12] = d[13] = d[14] = d[15] = d[16] = d[17] =\n    d[18] = d[19] = d[20] = d[21] = d[22] = d[23] =\n    d[24] = d[25] = d[26] = d[27] = d[28] = d[29] =\n    d[30] = d[31] = d[32] = d[33] = d[34] = d[35] = g;\n}\n\ninline tens4dmm::tens4dmm(const tens4ds t)\n{\n    d[ 0] = t.d[ 0];    d[ 6] = t.d[ 1];    d[12] = t.d[ 3];    d[18] = t.d[ 6];    d[24] = t.d[10];    d[30] = t.d[15];\n    d[ 1] = t.d[ 1];    d[ 7] = t.d[ 2];    d[13] = t.d[ 4];    d[19] = t.d[ 7];    d[25] = t.d[11];    d[31] = t.d[16];\n    d[ 2] = t.d[ 3];    d[ 8] = t.d[ 4];    d[14] = t.d[ 5];    d[20] = t.d[ 8];    d[26] = t.d[12];    d[32] = t.d[17];\n    d[ 3] = t.d[ 6];    d[ 9] = t.d[ 7];    d[15] = t.d[ 8];    d[21] = t.d[ 9];    d[27] = t.d[13];    d[33] = t.d[18];\n    d[ 4] = t.d[10];    d[10] = t.d[11];    d[16] = t.d[12];    d[22] = t.d[13];    d[28] = t.d[14];    d[34] = t.d[19];\n    d[ 5] = t.d[15];    d[11] = t.d[16];    d[17] = t.d[17];    d[23] = t.d[18];    d[29] = t.d[19];    d[35] = t.d[20];\n}\n\ninline tens4dmm::tens4dmm(double m[6][6])\n{\n    d[ 0]=m[0][0]; d[ 6]=m[0][1]; d[12]=m[0][2]; d[18]=m[0][3]; d[24]=m[0][4]; d[30]=m[0][5];\n    d[ 1]=m[1][0]; d[ 7]=m[1][1]; d[13]=m[1][2]; d[19]=m[1][3]; d[25]=m[1][4]; d[31]=m[1][5];\n    d[ 2]=m[2][0]; d[ 8]=m[2][1]; d[14]=m[2][2]; d[20]=m[2][3]; d[26]=m[2][4]; d[32]=m[2][5];\n    d[ 3]=m[3][0]; d[ 9]=m[3][1]; d[15]=m[3][2]; d[21]=m[3][3]; d[27]=m[3][4]; d[33]=m[3][5];\n    d[ 4]=m[4][0]; d[10]=m[4][1]; d[16]=m[4][2]; d[22]=m[4][3]; d[28]=m[4][4]; d[34]=m[4][5];\n    d[ 5]=m[5][0]; d[11]=m[5][1]; d[17]=m[5][2]; d[23]=m[5][3]; d[29]=m[5][4]; d[35]=m[5][5];\n}\n\ninline double& tens4dmm::operator () (int i, int j, int k, int l)\n{\n    const int m[3][3] = {{0,3,5},{3,1,4},{5,4,2}};\n    tens4dmm& T = (*this);\n    return T(m[i][j], m[k][l]);\n}\n\ninline double tens4dmm::operator () (int i, int j, int k, int l) const\n{\n    const int m[3][3] = {{0,3,5},{3,1,4},{5,4,2}};\n    const tens4dmm& T = (*this);\n    return T(m[i][j], m[k][l]);\n}\n\ninline double& tens4dmm::operator () (int i, int j)\n{\n    const int m[6] = {0, 6, 12, 18, 24, 30};\n    return d[m[j]+i];\n}\n\ninline double tens4dmm::operator () (int i, int j) const\n{\n    const int m[6] = {0, 6, 12, 18, 24, 30};\n    return d[m[j]+i];\n}\n\n//-----------------------------------------------------------------------------\n// operator +\ninline tens4dmm tens4dmm::operator + (const tens4dmm& t) const\n{\n    tens4dmm s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = d[i] + t.d[i];\n    s.d[ 0] = d[ 0] + t.d[ 0];  s.d[12] = d[12] + t.d[12];  s.d[24] = d[24] + t.d[24];\n    s.d[ 1] = d[ 1] + t.d[ 1];  s.d[13] = d[13] + t.d[13];  s.d[25] = d[25] + t.d[25];\n    s.d[ 2] = d[ 2] + t.d[ 2];  s.d[14] = d[14] + t.d[14];  s.d[26] = d[26] + t.d[26];\n    s.d[ 3] = d[ 3] + t.d[ 3];  s.d[15] = d[15] + t.d[15];  s.d[27] = d[27] + t.d[27];\n    s.d[ 4] = d[ 4] + t.d[ 4];  s.d[16] = d[16] + t.d[16];  s.d[28] = d[28] + t.d[28];\n    s.d[ 5] = d[ 5] + t.d[ 5];  s.d[17] = d[17] + t.d[17];  s.d[29] = d[29] + t.d[29];\n    s.d[ 6] = d[ 6] + t.d[ 6];  s.d[18] = d[18] + t.d[18];  s.d[30] = d[30] + t.d[30];\n    s.d[ 7] = d[ 7] + t.d[ 7];  s.d[19] = d[19] + t.d[19];  s.d[31] = d[31] + t.d[31];\n    s.d[ 8] = d[ 8] + t.d[ 8];  s.d[20] = d[20] + t.d[20];  s.d[32] = d[32] + t.d[32];\n    s.d[ 9] = d[ 9] + t.d[ 9];  s.d[21] = d[21] + t.d[21];  s.d[33] = d[33] + t.d[33];\n    s.d[10] = d[10] + t.d[10];  s.d[22] = d[22] + t.d[22];  s.d[34] = d[34] + t.d[34];\n    s.d[11] = d[11] + t.d[11];  s.d[23] = d[23] + t.d[23];  s.d[35] = d[35] + t.d[35];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator -\ninline tens4dmm tens4dmm::operator - (const tens4dmm& t) const\n{\n    tens4dmm s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = d[i] - t.d[i];\n    s.d[ 0] = d[ 0] - t.d[ 0];  s.d[12] = d[12] - t.d[12];  s.d[24] = d[24] - t.d[24];\n    s.d[ 1] = d[ 1] - t.d[ 1];  s.d[13] = d[13] - t.d[13];  s.d[25] = d[25] - t.d[25];\n    s.d[ 2] = d[ 2] - t.d[ 2];  s.d[14] = d[14] - t.d[14];  s.d[26] = d[26] - t.d[26];\n    s.d[ 3] = d[ 3] - t.d[ 3];  s.d[15] = d[15] - t.d[15];  s.d[27] = d[27] - t.d[27];\n    s.d[ 4] = d[ 4] - t.d[ 4];  s.d[16] = d[16] - t.d[16];  s.d[28] = d[28] - t.d[28];\n    s.d[ 5] = d[ 5] - t.d[ 5];  s.d[17] = d[17] - t.d[17];  s.d[29] = d[29] - t.d[29];\n    s.d[ 6] = d[ 6] - t.d[ 6];  s.d[18] = d[18] - t.d[18];  s.d[30] = d[30] - t.d[30];\n    s.d[ 7] = d[ 7] - t.d[ 7];  s.d[19] = d[19] - t.d[19];  s.d[31] = d[31] - t.d[31];\n    s.d[ 8] = d[ 8] - t.d[ 8];  s.d[20] = d[20] - t.d[20];  s.d[32] = d[32] - t.d[32];\n    s.d[ 9] = d[ 9] - t.d[ 9];  s.d[21] = d[21] - t.d[21];  s.d[33] = d[33] - t.d[33];\n    s.d[10] = d[10] - t.d[10];  s.d[22] = d[22] - t.d[22];  s.d[34] = d[34] - t.d[34];\n    s.d[11] = d[11] - t.d[11];  s.d[23] = d[23] - t.d[23];  s.d[35] = d[35] - t.d[35];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator + tens4ds\ninline tens4dmm tens4dmm::operator + (const tens4ds& t) const\n{\n    tens4dmm s;\n    \n    s.d[ 0] = d[ 0] + t.d[ 0];    s.d[12] = d[12] + t.d[ 3];    s.d[24] = d[24] + t.d[10];\n    s.d[ 1] = d[ 1] + t.d[ 1];    s.d[13] = d[13] + t.d[ 4];    s.d[25] = d[25] + t.d[11];\n    s.d[ 2] = d[ 2] + t.d[ 3];    s.d[14] = d[14] + t.d[ 5];    s.d[26] = d[26] + t.d[12];\n    s.d[ 3] = d[ 3] + t.d[ 6];    s.d[15] = d[15] + t.d[ 8];    s.d[27] = d[27] + t.d[13];\n    s.d[ 4] = d[ 4] + t.d[10];    s.d[16] = d[16] + t.d[12];    s.d[28] = d[28] + t.d[14];\n    s.d[ 5] = d[ 5] + t.d[15];    s.d[17] = d[17] + t.d[17];    s.d[29] = d[29] + t.d[19];\n    s.d[ 6] = d[ 6] + t.d[ 1];    s.d[18] = d[18] + t.d[ 6];    s.d[30] = d[30] + t.d[15];\n    s.d[ 7] = d[ 7] + t.d[ 2];    s.d[19] = d[19] + t.d[ 7];    s.d[31] = d[31] + t.d[16];\n    s.d[ 8] = d[ 8] + t.d[ 4];    s.d[20] = d[20] + t.d[ 8];    s.d[32] = d[32] + t.d[17];\n    s.d[ 9] = d[ 9] + t.d[ 7];    s.d[21] = d[21] + t.d[ 9];    s.d[33] = d[33] + t.d[18];\n    s.d[10] = d[10] + t.d[11];    s.d[22] = d[22] + t.d[13];    s.d[34] = d[34] + t.d[19];\n    s.d[11] = d[11] + t.d[16];    s.d[23] = d[23] + t.d[18];    s.d[35] = d[35] + t.d[20];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator - tens4ds\ninline tens4dmm tens4dmm::operator - (const tens4ds& t) const\n{\n    tens4dmm s;\n    \n    s.d[ 0] = d[ 0] - t.d[ 0];    s.d[12] = d[12] - t.d[ 3];    s.d[24] = d[24] - t.d[10];\n    s.d[ 1] = d[ 1] - t.d[ 1];    s.d[13] = d[13] - t.d[ 4];    s.d[25] = d[25] - t.d[11];\n    s.d[ 2] = d[ 2] - t.d[ 3];    s.d[14] = d[14] - t.d[ 5];    s.d[26] = d[26] - t.d[12];\n    s.d[ 3] = d[ 3] - t.d[ 6];    s.d[15] = d[15] - t.d[ 8];    s.d[27] = d[27] - t.d[13];\n    s.d[ 4] = d[ 4] - t.d[10];    s.d[16] = d[16] - t.d[12];    s.d[28] = d[28] - t.d[14];\n    s.d[ 5] = d[ 5] - t.d[15];    s.d[17] = d[17] - t.d[17];    s.d[29] = d[29] - t.d[19];\n    s.d[ 6] = d[ 6] - t.d[ 1];    s.d[18] = d[18] - t.d[ 6];    s.d[30] = d[30] - t.d[15];\n    s.d[ 7] = d[ 7] - t.d[ 2];    s.d[19] = d[19] - t.d[ 7];    s.d[31] = d[31] - t.d[16];\n    s.d[ 8] = d[ 8] - t.d[ 4];    s.d[20] = d[20] - t.d[ 8];    s.d[32] = d[32] - t.d[17];\n    s.d[ 9] = d[ 9] - t.d[ 7];    s.d[21] = d[21] - t.d[ 9];    s.d[33] = d[33] - t.d[18];\n    s.d[10] = d[10] - t.d[11];    s.d[22] = d[22] - t.d[13];    s.d[34] = d[34] - t.d[19];\n    s.d[11] = d[11] - t.d[16];    s.d[23] = d[23] - t.d[18];    s.d[35] = d[35] - t.d[20];\n\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator *\ninline tens4dmm tens4dmm::operator * (double g) const\n{\n    tens4dmm s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = g*d[i];\n    s.d[ 0] = g*d[ 0];    s.d[12] = g*d[12];    s.d[24] = g*d[24];\n    s.d[ 1] = g*d[ 1];    s.d[13] = g*d[13];    s.d[25] = g*d[25];\n    s.d[ 2] = g*d[ 2];    s.d[14] = g*d[14];    s.d[26] = g*d[26];\n    s.d[ 3] = g*d[ 3];    s.d[15] = g*d[15];    s.d[27] = g*d[27];\n    s.d[ 4] = g*d[ 4];    s.d[16] = g*d[16];    s.d[28] = g*d[28];\n    s.d[ 5] = g*d[ 5];    s.d[17] = g*d[17];    s.d[29] = g*d[29];\n    s.d[ 6] = g*d[ 6];    s.d[18] = g*d[18];    s.d[30] = g*d[30];\n    s.d[ 7] = g*d[ 7];    s.d[19] = g*d[19];    s.d[31] = g*d[31];\n    s.d[ 8] = g*d[ 8];    s.d[20] = g*d[20];    s.d[32] = g*d[32];\n    s.d[ 9] = g*d[ 9];    s.d[21] = g*d[21];    s.d[33] = g*d[33];\n    s.d[10] = g*d[10];    s.d[22] = g*d[22];    s.d[34] = g*d[34];\n    s.d[11] = g*d[11];    s.d[23] = g*d[23];    s.d[35] = g*d[35];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator /\ninline tens4dmm tens4dmm::operator / (double g) const\n{\n    tens4dmm s;\n//    for (int i=0; i<NNZ; i++)\n//        s.d[i] = d[i]/g;\n    s.d[ 0] = d[ 0]/g;    s.d[12] = d[12]/g;    s.d[24] = d[24]/g;\n    s.d[ 1] = d[ 1]/g;    s.d[13] = d[13]/g;    s.d[25] = d[25]/g;\n    s.d[ 2] = d[ 2]/g;    s.d[14] = d[14]/g;    s.d[26] = d[26]/g;\n    s.d[ 3] = d[ 3]/g;    s.d[15] = d[15]/g;    s.d[27] = d[27]/g;\n    s.d[ 4] = d[ 4]/g;    s.d[16] = d[16]/g;    s.d[28] = d[28]/g;\n    s.d[ 5] = d[ 5]/g;    s.d[17] = d[17]/g;    s.d[29] = d[29]/g;\n    s.d[ 6] = d[ 6]/g;    s.d[18] = d[18]/g;    s.d[30] = d[30]/g;\n    s.d[ 7] = d[ 7]/g;    s.d[19] = d[19]/g;    s.d[31] = d[31]/g;\n    s.d[ 8] = d[ 8]/g;    s.d[20] = d[20]/g;    s.d[32] = d[32]/g;\n    s.d[ 9] = d[ 9]/g;    s.d[21] = d[21]/g;    s.d[33] = d[33]/g;\n    s.d[10] = d[10]/g;    s.d[22] = d[22]/g;    s.d[34] = d[34]/g;\n    s.d[11] = d[11]/g;    s.d[23] = d[23]/g;    s.d[35] = d[35]/g;\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator +=\ninline tens4dmm& tens4dmm::operator += (const tens4dmm& t)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] += t.d[i];\n    d[ 0] += t.d[ 0];    d[12] += t.d[12];    d[24] += t.d[24];\n    d[ 1] += t.d[ 1];    d[13] += t.d[13];    d[25] += t.d[25];\n    d[ 2] += t.d[ 2];    d[14] += t.d[14];    d[26] += t.d[26];\n    d[ 3] += t.d[ 3];    d[15] += t.d[15];    d[27] += t.d[27];\n    d[ 4] += t.d[ 4];    d[16] += t.d[16];    d[28] += t.d[28];\n    d[ 5] += t.d[ 5];    d[17] += t.d[17];    d[29] += t.d[29];\n    d[ 6] += t.d[ 6];    d[18] += t.d[18];    d[30] += t.d[30];\n    d[ 7] += t.d[ 7];    d[19] += t.d[19];    d[31] += t.d[31];\n    d[ 8] += t.d[ 8];    d[20] += t.d[20];    d[32] += t.d[32];\n    d[ 9] += t.d[ 9];    d[21] += t.d[21];    d[33] += t.d[33];\n    d[10] += t.d[10];    d[22] += t.d[22];    d[34] += t.d[34];\n    d[11] += t.d[11];    d[23] += t.d[23];    d[35] += t.d[35];\n    \n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator -=\ninline tens4dmm& tens4dmm::operator -= (const tens4dmm& t)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] -= t.d[i];\n    d[ 0] -= t.d[ 0];    d[12] -= t.d[12];    d[24] -= t.d[24];\n    d[ 1] -= t.d[ 1];    d[13] -= t.d[13];    d[25] -= t.d[25];\n    d[ 2] -= t.d[ 2];    d[14] -= t.d[14];    d[26] -= t.d[26];\n    d[ 3] -= t.d[ 3];    d[15] -= t.d[15];    d[27] -= t.d[27];\n    d[ 4] -= t.d[ 4];    d[16] -= t.d[16];    d[28] -= t.d[28];\n    d[ 5] -= t.d[ 5];    d[17] -= t.d[17];    d[29] -= t.d[29];\n    d[ 6] -= t.d[ 6];    d[18] -= t.d[18];    d[30] -= t.d[30];\n    d[ 7] -= t.d[ 7];    d[19] -= t.d[19];    d[31] -= t.d[31];\n    d[ 8] -= t.d[ 8];    d[20] -= t.d[20];    d[32] -= t.d[32];\n    d[ 9] -= t.d[ 9];    d[21] -= t.d[21];    d[33] -= t.d[33];\n    d[10] -= t.d[10];    d[22] -= t.d[22];    d[34] -= t.d[34];\n    d[11] -= t.d[11];    d[23] -= t.d[23];    d[35] -= t.d[35];\n    \n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator += tens4ds\ninline tens4dmm& tens4dmm::operator += (const tens4ds& t)\n{\n    d[ 0] += t.d[ 0];    d[12] += t.d[ 3];    d[24] += t.d[10];\n    d[ 1] += t.d[ 1];    d[13] += t.d[ 4];    d[25] += t.d[11];\n    d[ 2] += t.d[ 3];    d[14] += t.d[ 5];    d[26] += t.d[12];\n    d[ 3] += t.d[ 6];    d[15] += t.d[ 8];    d[27] += t.d[13];\n    d[ 4] += t.d[10];    d[16] += t.d[12];    d[28] += t.d[14];\n    d[ 5] += t.d[15];    d[17] += t.d[17];    d[29] += t.d[19];\n    d[ 6] += t.d[ 1];    d[18] += t.d[ 6];    d[30] += t.d[15];\n    d[ 7] += t.d[ 2];    d[19] += t.d[ 7];    d[31] += t.d[16];\n    d[ 8] += t.d[ 4];    d[20] += t.d[ 8];    d[32] += t.d[17];\n    d[ 9] += t.d[ 7];    d[21] += t.d[ 9];    d[33] += t.d[18];\n    d[10] += t.d[11];    d[22] += t.d[13];    d[34] += t.d[19];\n    d[11] += t.d[16];    d[23] += t.d[18];    d[35] += t.d[20];\n    \n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator -= tens4ds\ninline tens4dmm& tens4dmm::operator -= (const tens4ds& t)\n{\n    d[ 0] -= t.d[ 0];    d[12] -= t.d[ 3];    d[24] -= t.d[10];\n    d[ 1] -= t.d[ 1];    d[13] -= t.d[ 4];    d[25] -= t.d[11];\n    d[ 2] -= t.d[ 3];    d[14] -= t.d[ 5];    d[26] -= t.d[12];\n    d[ 3] -= t.d[ 6];    d[15] -= t.d[ 8];    d[27] -= t.d[13];\n    d[ 4] -= t.d[10];    d[16] -= t.d[12];    d[28] -= t.d[14];\n    d[ 5] -= t.d[15];    d[17] -= t.d[17];    d[29] -= t.d[19];\n    d[ 6] -= t.d[ 1];    d[18] -= t.d[ 6];    d[30] -= t.d[15];\n    d[ 7] -= t.d[ 2];    d[19] -= t.d[ 7];    d[31] -= t.d[16];\n    d[ 8] -= t.d[ 4];    d[20] -= t.d[ 8];    d[32] -= t.d[17];\n    d[ 9] -= t.d[ 7];    d[21] -= t.d[ 9];    d[33] -= t.d[18];\n    d[10] -= t.d[11];    d[22] -= t.d[13];    d[34] -= t.d[19];\n    d[11] -= t.d[16];    d[23] -= t.d[18];    d[35] -= t.d[20];\n    \n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator *=\ninline tens4dmm& tens4dmm::operator *= (double g)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] *= g;\n    d[ 0] *= g;    d[12] *= g;    d[24] *= g;\n    d[ 1] *= g;    d[13] *= g;    d[25] *= g;\n    d[ 2] *= g;    d[14] *= g;    d[26] *= g;\n    d[ 3] *= g;    d[15] *= g;    d[27] *= g;\n    d[ 4] *= g;    d[16] *= g;    d[28] *= g;\n    d[ 5] *= g;    d[17] *= g;    d[29] *= g;\n    d[ 6] *= g;    d[18] *= g;    d[30] *= g;\n    d[ 7] *= g;    d[19] *= g;    d[31] *= g;\n    d[ 8] *= g;    d[20] *= g;    d[32] *= g;\n    d[ 9] *= g;    d[21] *= g;    d[33] *= g;\n    d[10] *= g;    d[22] *= g;    d[34] *= g;\n    d[11] *= g;    d[23] *= g;    d[35] *= g;\n    \n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// assignment operator /=\ninline tens4dmm& tens4dmm::operator /= (double g)\n{\n//    for (int i=0; i<NNZ; i++)\n//        d[i] /= g;\n    d[ 0] /= g;    d[12] /= g;    d[24] /= g;\n    d[ 1] /= g;    d[13] /= g;    d[25] /= g;\n    d[ 2] /= g;    d[14] /= g;    d[26] /= g;\n    d[ 3] /= g;    d[15] /= g;    d[27] /= g;\n    d[ 4] /= g;    d[16] /= g;    d[28] /= g;\n    d[ 5] /= g;    d[17] /= g;    d[29] /= g;\n    d[ 6] /= g;    d[18] /= g;    d[30] /= g;\n    d[ 7] /= g;    d[19] /= g;    d[31] /= g;\n    d[ 8] /= g;    d[20] /= g;    d[32] /= g;\n    d[ 9] /= g;    d[21] /= g;    d[33] /= g;\n    d[10] /= g;    d[22] /= g;    d[34] /= g;\n    d[11] /= g;    d[23] /= g;    d[35] /= g;\n    \n    return (*this);\n}\n\n//-----------------------------------------------------------------------------\n// unary operator -\ninline tens4dmm tens4dmm::operator - () const\n{\n    tens4dmm s;\n    s.d[ 0] = -d[ 0];    s.d[12] = -d[12];    s.d[24] = -d[24];\n    s.d[ 1] = -d[ 1];    s.d[13] = -d[13];    s.d[25] = -d[25];\n    s.d[ 2] = -d[ 2];    s.d[14] = -d[14];    s.d[26] = -d[26];\n    s.d[ 3] = -d[ 3];    s.d[15] = -d[15];    s.d[27] = -d[27];\n    s.d[ 4] = -d[ 4];    s.d[16] = -d[16];    s.d[28] = -d[28];\n    s.d[ 5] = -d[ 5];    s.d[17] = -d[17];    s.d[29] = -d[29];\n    s.d[ 6] = -d[ 6];    s.d[18] = -d[18];    s.d[30] = -d[30];\n    s.d[ 7] = -d[ 7];    s.d[19] = -d[19];    s.d[31] = -d[31];\n    s.d[ 8] = -d[ 8];    s.d[20] = -d[20];    s.d[32] = -d[32];\n    s.d[ 9] = -d[ 9];    s.d[21] = -d[21];    s.d[33] = -d[33];\n    s.d[10] = -d[10];    s.d[22] = -d[22];    s.d[34] = -d[34];\n    s.d[11] = -d[11];    s.d[23] = -d[23];    s.d[35] = -d[35];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator * mat3ds\ninline tens4dmm tens4dmm::operator * (const mat3ds& m) const\n{\n    tens4dmm t = *this;\n    tens4dmm s;\n    for (int i=0; i<3; ++i)\n        for (int j=0; j<3; ++j)\n            for (int k=0; k<3; ++k)\n                for (int n=0; n<3; ++n) {\n                    s(i,j,k,n) = t(i,j,k,0)*m(0,n) + t(i,j,k,1)*m(1,n) + t(i,j,k,2)*m(2,n);\n            }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// operator * mat3d\ninline tens4dmm tens4dmm::operator * (const mat3d& m) const\n{\n    tens4dmm t = *this;\n    tens4dmm s;\n    for (int i=0; i<3; ++i)\n        for (int j=0; j<3; ++j)\n            for (int k=0; k<3; ++k)\n                for (int n=0; n<3; ++n) {\n                    s(i,j,k,n) = t(i,j,k,0)*m(0,n) + t(i,j,k,1)*m(1,n) + t(i,j,k,2)*m(2,n);\n                }\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// vdotTdotv_jk = a_i T_ijkl b_l\ninline mat3d vdotTdotv(const vec3d& a, const tens4dmm& T, const vec3d& b)\n{\n    return mat3d(a.x*b.x*T.d[0] + a.y*b.x*T.d[3] + a.z*b.x*T.d[5] + a.x*b.y*T.d[18] + a.y*b.y*T.d[21] +\n                 a.z*b.y*T.d[23] + a.x*b.z*T.d[30] + a.y*b.z*T.d[33] + a.z*b.z*T.d[35],\n                 a.x*b.y*T.d[6] + a.y*b.y*T.d[9] + a.z*b.y*T.d[11] + a.x*b.x*T.d[18] + a.y*b.x*T.d[21] + a.z*b.x*T.d[23] +\n                 a.x*b.z*T.d[24] + a.y*b.z*T.d[27] + a.z*b.z*T.d[29],\n                 a.x*b.z*T.d[12] + a.y*b.z*T.d[15] + a.z*b.z*T.d[17] + a.x*b.y*T.d[24] + a.y*b.y*T.d[27] + a.z*b.y*T.d[29] +\n                 a.x*b.x*T.d[30] + a.y*b.x*T.d[33] + a.z*b.x*T.d[35],\n                 a.y*b.x*T.d[1] + a.x*b.x*T.d[3] + a.z*b.x*T.d[4] + a.y*b.y*T.d[19] + a.x*b.y*T.d[21] + a.z*b.y*T.d[22] +\n                 a.y*b.z*T.d[31] + a.x*b.z*T.d[33] + a.z*b.z*T.d[34],\n                 a.y*b.y*T.d[7] + a.x*b.y*T.d[9] + a.z*b.y*T.d[10] + a.y*b.x*T.d[19] + a.x*b.x*T.d[21] + a.z*b.x*T.d[22] +\n                 a.y*b.z*T.d[25] + a.x*b.z*T.d[27] + a.z*b.z*T.d[28],\n                 a.y*b.z*T.d[13] + a.x*b.z*T.d[15] + a.z*b.z*T.d[16] + a.y*b.y*T.d[25] + a.x*b.y*T.d[27] + a.z*b.y*T.d[28] +\n                 a.y*b.x*T.d[31] + a.x*b.x*T.d[33] + a.z*b.x*T.d[34],\n                 a.z*b.x*T.d[2] + a.y*b.x*T.d[4] + a.x*b.x*T.d[5] + a.z*b.y*T.d[20] + a.y*b.y*T.d[22] + a.x*b.y*T.d[23] +\n                 a.z*b.z*T.d[32] + a.y*b.z*T.d[34] + a.x*b.z*T.d[35],\n                 a.z*b.y*T.d[8] + a.y*b.y*T.d[10] + a.x*b.y*T.d[11] + a.z*b.x*T.d[20] + a.y*b.x*T.d[22] + a.x*b.x*T.d[23] +\n                 a.z*b.z*T.d[26] + a.y*b.z*T.d[28] + a.x*b.z*T.d[29],\n                 a.z*b.z*T.d[14] + a.y*b.z*T.d[16] + a.x*b.z*T.d[17] + a.z*b.y*T.d[26] + a.y*b.y*T.d[28] + a.x*b.y*T.d[29] +\n                 a.z*b.x*T.d[32] + a.y*b.x*T.d[34] + a.x*b.x*T.d[35]);\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of general 4th-order tensor with a general 2nd-order tensor\n// Aij = Dijkl Mkl\ninline mat3ds tens4dmm::dot(const mat3ds &m) const\n{\n    mat3ds a;\n    \n    a.xx() = d[0]*m.xx() + d[18]*m.xy() + d[30]*m.xz() + d[6]*m.yy() + d[24]*m.yz() + d[12]*m.zz();\n    a.yy() = d[1]*m.xx() + d[19]*m.xy() + d[31]*m.xz() + d[7]*m.yy() + d[25]*m.yz() + d[13]*m.zz();\n    a.zz() = d[2]*m.xx() + d[20]*m.xy() + d[32]*m.xz() + d[8]*m.yy() + d[26]*m.yz() + d[14]*m.zz();\n    a.xy() = d[3]*m.xx() + d[21]*m.xy() + d[33]*m.xz() + d[9]*m.yy() + d[27]*m.yz() + d[15]*m.zz();\n    a.yz() = d[4]*m.xx() + d[22]*m.xy() + d[34]*m.xz() + d[10]*m.yy() + d[28]*m.yz() + d[16]*m.zz();\n    a.xz() = d[5]*m.xx() + d[23]*m.xy() + d[35]*m.xz() + d[11]*m.yy() + d[29]*m.yz() + d[17]*m.zz();\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of mm 4th-order tensor with a symmetric 4th-order tensor\n// Aijmn = Bijkl Tklmn\ninline tens4dmm tens4dmm::ddot(const tens4ds& t) const\n{\n    tens4dmm b = *this;\n    tens4dmm a;\n    for (int i=0; i<3; ++i) {\n        for (int j=0; j<3; ++j) {\n            for (int m=0; m<3; ++m) {\n                for (int n=0; n<3; ++n) {\n                    a(i,j,m,n) = 0;\n                    for (int k=0; k<3; ++k) {\n                        for (int l=0; l<3; ++l) {\n                            a(i,j,m,n) += b(i,j,k,l)*t(k,l,m,n);\n                        }\n                    }\n                }\n            }\n        }\n    }\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of mm 4th-order tensor with a mm 4th-order tensor\n// Aijmn = Bijkl Tklmn\ninline tens4dmm tens4dmm::ddot(const tens4dmm& t) const\n{\n    tens4dmm b = *this;\n    tens4dmm a;\n    for (int i=0; i<3; ++i) {\n        for (int j=0; j<3; ++j) {\n            for (int m=0; m<3; ++m) {\n                for (int n=0; n<3; ++n) {\n                    a(i,j,m,n) = 0;\n                    for (int k=0; k<3; ++k) {\n                        for (int l=0; l<3; ++l) {\n                            a(i,j,m,n) += b(i,j,k,l)*t(k,l,m,n);\n                        }\n                    }\n                }\n            }\n        }\n    }\n    \n    return a;\n}\n\n//-----------------------------------------------------------------------------\n// trace\n// C.tr() = I:C:I\ninline double tens4dmm::tr() const\n{\n    return (d[0]+d[1]+d[2]+d[6]+d[7]+d[8]+d[12]+d[13]+d[14]);\n}\n\n//-----------------------------------------------------------------------------\n// intialize to zero\ninline void tens4dmm::zero()\n{\n    d[ 0] = d[ 1] = d[ 2] = d[ 3] = d[ 4] = d[ 5] = d[ 6] = d[ 7] = d[ 8] =\n    d[ 9] = d[10] = d[11] = d[12] = d[13] = d[14] = d[15] = d[16] = d[17] =\n    d[18] = d[19] = d[20] = d[21] = d[22] = d[23] = d[24] = d[25] = d[26] =\n    d[27] = d[28] = d[29] = d[30] = d[31] = d[32] = d[33] = d[34] = d[35] = 0;\n}\n\n//-----------------------------------------------------------------------------\n// extract 6x6 matrix\ninline void tens4dmm::extract(double D[6][6])\n{\n    D[0][0] = d[0]; D[0][1] = d[ 6]; D[0][2] = d[12]; D[0][3] = d[18]; D[0][4] = d[24]; D[0][5] = d[30];\n    D[1][0] = d[1]; D[1][1] = d[ 7]; D[1][2] = d[13]; D[1][3] = d[19]; D[1][4] = d[25]; D[1][5] = d[31];\n    D[2][0] = d[2]; D[2][1] = d[ 8]; D[2][2] = d[14]; D[2][3] = d[20]; D[2][4] = d[26]; D[2][5] = d[32];\n    D[3][0] = d[3]; D[3][1] = d[ 9]; D[3][2] = d[15]; D[3][3] = d[21]; D[3][4] = d[27]; D[3][5] = d[33];\n    D[4][0] = d[4]; D[4][1] = d[10]; D[4][2] = d[16]; D[4][3] = d[22]; D[4][4] = d[28]; D[4][5] = d[34];\n    D[5][0] = d[5]; D[5][1] = d[11]; D[5][2] = d[17]; D[5][3] = d[23]; D[5][4] = d[29]; D[5][5] = d[35];\n}\n\n\n//-----------------------------------------------------------------------------\n// compute the super symmetric (major and minor symmetric) component of the tensor\n// Sijkl = (1/8)*(Cijkl + Cjikl + Cijlk + Cjilk + Cklij + Clkij + Cklji + Clkji)\ninline tens4ds tens4dmm::supersymm() const\n{\n    tens4ds s;\n    \n    s.d[ 0] = d[0];\n    s.d[ 1] = (d[1]+d[6])/2;\n    s.d[ 2] = d[7];\n    s.d[ 3] = (d[2]+d[12])/2;\n    s.d[ 4] = (d[8]+d[13])/2;\n    s.d[ 5] = d[14];\n    s.d[ 6] = (d[3]+d[18])/2;\n    s.d[ 7] = (d[9]+d[19])/2;\n    s.d[ 8] = (d[15]+d[20])/2;\n    s.d[ 9] = d[21];\n    s.d[10] = (d[4]+d[24])/2;\n    s.d[11] = (d[10]+d[25])/2;\n    s.d[12] = (d[16]+d[26])/2;\n    s.d[13] = (d[22]+d[27])/2;\n    s.d[14] = d[28];\n    s.d[15] = (d[5]+d[30])/2;\n    s.d[16] = (d[11]+d[31])/2;\n    s.d[17] = (d[17]+d[32])/2;\n    s.d[18] = (d[23]+d[33])/2;\n    s.d[19] = (d[29]+d[34])/2;\n    s.d[20] = d[35];\n    \n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// compute the major transpose of the tensor\n// Sijkl -> Sklij\ninline tens4dmm tens4dmm::transpose() const\n{\n    tens4dmm s;\n    \n    s.d[ 0] = d[ 0];    s.d[ 2] = d[12];    s.d[ 4] = d[24];\n    s.d[ 6] = d[ 1];    s.d[ 8] = d[13];    s.d[10] = d[25];\n    s.d[12] = d[ 2];    s.d[14] = d[14];    s.d[16] = d[26];\n    s.d[18] = d[ 3];    s.d[20] = d[15];    s.d[22] = d[27];\n    s.d[24] = d[ 4];    s.d[26] = d[16];    s.d[28] = d[28];\n    s.d[30] = d[ 5];    s.d[32] = d[17];    s.d[34] = d[29];\n    s.d[ 1] = d[ 6];    s.d[ 3] = d[18];    s.d[ 5] = d[30];\n    s.d[ 7] = d[ 7];    s.d[ 9] = d[19];    s.d[11] = d[31];\n    s.d[13] = d[ 8];    s.d[15] = d[20];    s.d[17] = d[32];\n    s.d[19] = d[ 9];    s.d[21] = d[21];    s.d[23] = d[33];\n    s.d[25] = d[10];    s.d[27] = d[22];    s.d[29] = d[34];\n    s.d[31] = d[11];    s.d[33] = d[23];    s.d[35] = d[35];\n    return s;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1 b)_ijkl = a_ij b_kl\ninline tens4dmm dyad1mm(const mat3ds& a, const mat3ds& b)\n{\n    tens4dmm c;\n    \n    c.d[ 0] = a.xx()*b.xx();    c.d[12] = a.xx()*b.zz();    c.d[24] = a.xx()*b.yz();\n    c.d[ 1] = a.yy()*b.xx();    c.d[13] = a.yy()*b.zz();    c.d[25] = a.yy()*b.yz();\n    c.d[ 2] = a.zz()*b.xx();    c.d[14] = a.zz()*b.zz();    c.d[26] = a.zz()*b.yz();\n    c.d[ 3] = a.xy()*b.xx();    c.d[15] = a.xy()*b.zz();    c.d[27] = a.xy()*b.yz();\n    c.d[ 4] = a.yz()*b.xx();    c.d[16] = a.yz()*b.zz();    c.d[28] = a.yz()*b.yz();\n    c.d[ 5] = a.xz()*b.xx();    c.d[17] = a.xz()*b.zz();    c.d[29] = a.xz()*b.yz();\n    c.d[ 6] = a.xx()*b.yy();    c.d[18] = a.xx()*b.xy();    c.d[30] = a.xx()*b.xz();\n    c.d[ 7] = a.yy()*b.yy();    c.d[19] = a.yy()*b.xy();    c.d[31] = a.yy()*b.xz();\n    c.d[ 8] = a.zz()*b.yy();    c.d[20] = a.zz()*b.xy();    c.d[32] = a.zz()*b.xz();\n    c.d[ 9] = a.xy()*b.yy();    c.d[21] = a.xy()*b.xy();    c.d[33] = a.xy()*b.xz();\n    c.d[10] = a.yz()*b.yy();    c.d[22] = a.yz()*b.xy();    c.d[34] = a.yz()*b.xz();\n    c.d[11] = a.xz()*b.yy();    c.d[23] = a.xz()*b.xy();    c.d[35] = a.xz()*b.xz();\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad2 b)_ijkl = a_ik b_jl\ninline tens4dmm dyad2mm(const mat3ds& a, const mat3ds& b)\n{\n    tens4dmm c;\n    \n    c.d[ 0] = a.xx()*b.xx();    c.d[12] = a.xz()*b.xz();    c.d[24] = a.xy()*b.xz();\n    c.d[ 1] = a.xy()*b.xy();    c.d[13] = a.yz()*b.yz();    c.d[25] = a.yy()*b.yz();\n    c.d[ 2] = a.xz()*b.xz();    c.d[14] = a.zz()*b.zz();    c.d[26] = a.yz()*b.zz();\n    c.d[ 3] = a.xx()*b.xy();    c.d[15] = a.xz()*b.yz();    c.d[27] = a.xy()*b.yz();\n    c.d[ 4] = a.xy()*b.xz();    c.d[16] = a.yz()*b.zz();    c.d[28] = a.yy()*b.zz();\n    c.d[ 5] = a.xx()*b.xz();    c.d[17] = a.xz()*b.zz();    c.d[29] = a.xy()*b.zz();\n    c.d[ 6] = a.xy()*b.xy();    c.d[18] = a.xx()*b.xy();    c.d[30] = a.xx()*b.xz();\n    c.d[ 7] = a.yy()*b.yy();    c.d[19] = a.xy()*b.yy();    c.d[31] = a.xy()*b.yz();\n    c.d[ 8] = a.yz()*b.yz();    c.d[20] = a.xz()*b.yz();    c.d[32] = a.xz()*b.zz();\n    c.d[ 9] = a.xy()*b.yy();    c.d[21] = a.xx()*b.yy();    c.d[33] = a.xx()*b.yz();\n    c.d[10] = a.yy()*b.yz();    c.d[22] = a.xy()*b.yz();    c.d[34] = a.xy()*b.zz();\n    c.d[11] = a.xy()*b.yz();    c.d[23] = a.xx()*b.yz();    c.d[35] = a.xx()*b.zz();\n    \n    return c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad3 b)_ijkl = a_il b_jk\ninline tens4dmm dyad3mm(const mat3ds& a, const mat3ds& b)\n{\n    tens4dmm c;\n\n    c.d[ 0] = a.xx()*b.xx();    c.d[12] = a.xz()*b.xz();    c.d[24] = a.xz()*b.xy();\n    c.d[ 1] = a.xy()*b.xy();    c.d[13] = a.yz()*b.yz();    c.d[25] = a.yz()*b.yy();\n    c.d[ 2] = a.xz()*b.xz();    c.d[14] = a.zz()*b.zz();    c.d[26] = a.zz()*b.yz();\n    c.d[ 3] = a.xx()*b.xy();    c.d[15] = a.xz()*b.yz();    c.d[27] = a.xz()*b.yy();\n    c.d[ 4] = a.xy()*b.xz();    c.d[16] = a.yz()*b.zz();    c.d[28] = a.yz()*b.yz();\n    c.d[ 5] = a.xx()*b.xz();    c.d[17] = a.xz()*b.zz();    c.d[29] = a.xz()*b.yz();\n    c.d[ 6] = a.xy()*b.xy();    c.d[18] = a.xy()*b.xx();    c.d[30] = a.xz()*b.xx();\n    c.d[ 7] = a.yy()*b.yy();    c.d[19] = a.yy()*b.xy();    c.d[31] = a.yz()*b.xy();\n    c.d[ 8] = a.yz()*b.yz();    c.d[20] = a.yz()*b.xz();    c.d[32] = a.zz()*b.xz();\n    c.d[ 9] = a.xy()*b.yy();    c.d[21] = a.xy()*b.xy();    c.d[33] = a.xz()*b.xy();\n    c.d[10] = a.yy()*b.yz();    c.d[22] = a.yy()*b.xz();    c.d[34] = a.yz()*b.xz();\n    c.d[11] = a.xy()*b.yz();    c.d[23] = a.xy()*b.xz();    c.d[35] = a.xz()*b.xz();\n\n    return c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad4 b)_ijkl = 0.5(a_ik b_jl + a_il b_jk)\ninline tens4dmm dyad4mm(const mat3ds& a, const mat3ds& b)\n{\n    return (dyad2mm(a,b) + dyad3mm(a,b))/2;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad4 b)_ijkl = 0.5(a_ik b_jl + a_il b_jk)\ninline tens4dmm dyad4mm(const mat3d& a, const mat3d& b)\n{\n    tens4dmm c;\n    c.d[ 0] = 2*a(0,0)*b(0,0);\n    c.d[ 1] = 2*a(1,0)*b(1,0);\n    c.d[ 2] = 2*a(2,0)*b(2,0);\n    c.d[ 3] = a(1,0)*b(0,0) + a(0,0)*b(1,0);\n    c.d[ 4] = a(2,0)*b(1,0) + a(1,0)*b(2,0);\n    c.d[ 5] = a(2,0)*b(0,0) + a(0,0)*b(2,0);\n\n    c.d[ 6] = 2*a(0,1)*b(0,1);\n    c.d[ 7] = 2*a(1,1)*b(1,1);\n    c.d[ 8] = 2*a(2,1)*b(2,1);\n    c.d[ 9] = a(1,1)*b(0,1) + a(0,1)*b(1,1);\n    c.d[10] = a(2,1)*b(1,1) + a(1,1)*b(2,1);\n    c.d[11] = a(2,1)*b(0,1) + a(0,1)*b(2,1);\n\n    c.d[12] = 2*a(0,2)*b(0,2);\n    c.d[13] = 2*a(1,2)*b(1,2);\n    c.d[14] = 2*a(2,2)*b(2,2);\n    c.d[15] = a(1,2)*b(0,2) + a(0,2)*b(1,2);\n    c.d[16] = a(2,2)*b(1,2) + a(1,2)*b(2,2);\n    c.d[17] = a(2,2)*b(0,2) + a(0,2)*b(2,2);\n\n    c.d[18] = a(0,1)*b(0,0) + a(0,0)*b(0,1);\n    c.d[19] = a(1,1)*b(1,0) + a(1,0)*b(1,1);\n    c.d[20] = a(2,1)*b(2,0) + a(2,0)*b(2,1);\n    c.d[21] = (a(1,1)*b(0,0) + a(1,0)*b(0,1) + a(0,1)*b(1,0) + a(0,0)*b(1,1))/2.;\n    c.d[22] = (a(2,1)*b(1,0) + a(2,0)*b(1,1) + a(1,1)*b(2,0) + a(1,0)*b(2,1))/2.;\n    c.d[23] = (a(2,1)*b(0,0) + a(2,0)*b(0,1) + a(0,1)*b(2,0) + a(0,0)*b(2,1))/2.;\n\n    c.d[24] = a(0,2)*b(0,1) + a(0,1)*b(0,2);\n    c.d[25] = a(1,2)*b(1,1) + a(1,1)*b(1,2);\n    c.d[26] = a(2,2)*b(2,1) + a(2,1)*b(2,2);\n    c.d[27] = (a(1,2)*b(0,1) + a(1,1)*b(0,2) + a(0,2)*b(1,1) + a(0,1)*b(1,2))/2.;\n    c.d[28] = (a(2,2)*b(1,1) + a(2,1)*b(1,2) + a(1,2)*b(2,1) + a(1,1)*b(2,2))/2.;\n    c.d[29] = (a(2,2)*b(0,1) + a(2,1)*b(0,2) + a(0,2)*b(2,1) + a(0,1)*b(2,2))/2.;\n\n    c.d[30] = a(0,2)*b(0,0) + a(0,0)*b(0,2);\n    c.d[31] = a(1,2)*b(1,0) + a(1,0)*b(1,2);\n    c.d[32] = a(2,2)*b(2,0) + a(2,0)*b(2,2);\n    c.d[33] = (a(1,2)*b(0,0) + a(1,0)*b(0,2) + a(0,2)*b(1,0) + a(0,0)*b(1,2))/2.;\n    c.d[34] = (a(2,2)*b(1,0) + a(2,0)*b(1,2) + a(1,2)*b(2,0) + a(1,0)*b(2,2))/2.;\n    c.d[35] = (a(2,2)*b(0,0) + a(2,0)*b(0,2) + a(0,2)*b(2,0) + a(0,0)*b(2,2))/2.;\n    \n    return c/2;\n}\n\n//-----------------------------------------------------------------------------\n// (a ddot b)_ijkl = a_ijmn b_mnkl\ninline tens4dmm ddot(const tens4dmm& a, const tens4dmm& b)\n{\n    tens4dmm c;\n    \n    c.d[ 0] = a.d[0]*b.d[0] + a.d[6]*b.d[1] + a.d[12]*b.d[2] + a.d[18]*b.d[3] + a.d[24]*b.d[4] + a.d[30]*b.d[5];\n    c.d[ 1] = a.d[1]*b.d[0] + a.d[7]*b.d[1] + a.d[13]*b.d[2] + a.d[19]*b.d[3] + a.d[25]*b.d[4] + a.d[31]*b.d[5];\n    c.d[ 2] = a.d[2]*b.d[0] + a.d[8]*b.d[1] + a.d[14]*b.d[2] + a.d[20]*b.d[3] + a.d[26]*b.d[4] + a.d[32]*b.d[5];\n    c.d[ 3] = a.d[3]*b.d[0] + a.d[9]*b.d[1] + a.d[15]*b.d[2] + a.d[21]*b.d[3] + a.d[27]*b.d[4] + a.d[33]*b.d[5];\n    c.d[ 4] = a.d[4]*b.d[0] + a.d[10]*b.d[1] + a.d[16]*b.d[2] + a.d[22]*b.d[3] + a.d[28]*b.d[4] + a.d[34]*b.d[5];\n    c.d[ 5] = a.d[5]*b.d[0] + a.d[11]*b.d[1] + a.d[17]*b.d[2] + a.d[23]*b.d[3] + a.d[29]*b.d[4] + a.d[35]*b.d[5];\n    c.d[ 6] = a.d[24]*b.d[10] + a.d[30]*b.d[11] + a.d[0]*b.d[6] + a.d[6]*b.d[7] + a.d[12]*b.d[8] + a.d[18]*b.d[9];\n    c.d[ 7] = a.d[25]*b.d[10] + a.d[31]*b.d[11] + a.d[1]*b.d[6] + a.d[7]*b.d[7] + a.d[13]*b.d[8] + a.d[19]*b.d[9];\n    c.d[ 8] = a.d[26]*b.d[10] + a.d[32]*b.d[11] + a.d[2]*b.d[6] + a.d[8]*b.d[7] + a.d[14]*b.d[8] + a.d[20]*b.d[9];\n    c.d[ 9] = a.d[27]*b.d[10] + a.d[33]*b.d[11] + a.d[3]*b.d[6] + a.d[9]*b.d[7] + a.d[15]*b.d[8] + a.d[21]*b.d[9];\n    c.d[10] = a.d[28]*b.d[10] + a.d[34]*b.d[11] + a.d[4]*b.d[6] + a.d[10]*b.d[7] + a.d[16]*b.d[8] + a.d[22]*b.d[9];\n    c.d[11] = a.d[29]*b.d[10] + a.d[35]*b.d[11] + a.d[5]*b.d[6] + a.d[11]*b.d[7] + a.d[17]*b.d[8] + a.d[23]*b.d[9];\n    c.d[12] = a.d[0]*b.d[12] + a.d[6]*b.d[13] + a.d[12]*b.d[14] + a.d[18]*b.d[15] + a.d[24]*b.d[16] + a.d[30]*b.d[17];\n    c.d[13] = a.d[1]*b.d[12] + a.d[7]*b.d[13] + a.d[13]*b.d[14] + a.d[19]*b.d[15] + a.d[25]*b.d[16] + a.d[31]*b.d[17];\n    c.d[14] = a.d[2]*b.d[12] + a.d[8]*b.d[13] + a.d[14]*b.d[14] + a.d[20]*b.d[15] + a.d[26]*b.d[16] + a.d[32]*b.d[17];\n    c.d[15] = a.d[3]*b.d[12] + a.d[9]*b.d[13] + a.d[15]*b.d[14] + a.d[21]*b.d[15] + a.d[27]*b.d[16] + a.d[33]*b.d[17];\n    c.d[16] = a.d[4]*b.d[12] + a.d[10]*b.d[13] + a.d[16]*b.d[14] + a.d[22]*b.d[15] + a.d[28]*b.d[16] + a.d[34]*b.d[17];\n    c.d[17] = a.d[5]*b.d[12] + a.d[11]*b.d[13] + a.d[17]*b.d[14] + a.d[23]*b.d[15] + a.d[29]*b.d[16] + a.d[35]*b.d[17];\n    c.d[18] = a.d[0]*b.d[18] + a.d[6]*b.d[19] + a.d[12]*b.d[20] + a.d[18]*b.d[21] + a.d[24]*b.d[22] + a.d[30]*b.d[23];\n    c.d[19] = a.d[1]*b.d[18] + a.d[7]*b.d[19] + a.d[13]*b.d[20] + a.d[19]*b.d[21] + a.d[25]*b.d[22] + a.d[31]*b.d[23];\n    c.d[20] = a.d[2]*b.d[18] + a.d[8]*b.d[19] + a.d[14]*b.d[20] + a.d[20]*b.d[21] + a.d[26]*b.d[22] + a.d[32]*b.d[23];\n    c.d[21] = a.d[3]*b.d[18] + a.d[9]*b.d[19] + a.d[15]*b.d[20] + a.d[21]*b.d[21] + a.d[27]*b.d[22] + a.d[33]*b.d[23];\n    c.d[22] = a.d[4]*b.d[18] + a.d[10]*b.d[19] + a.d[16]*b.d[20] + a.d[22]*b.d[21] + a.d[28]*b.d[22] + a.d[34]*b.d[23];\n    c.d[23] = a.d[5]*b.d[18] + a.d[11]*b.d[19] + a.d[17]*b.d[20] + a.d[23]*b.d[21] + a.d[29]*b.d[22] + a.d[35]*b.d[23];\n    c.d[24] = a.d[0]*b.d[24] + a.d[6]*b.d[25] + a.d[12]*b.d[26] + a.d[18]*b.d[27] + a.d[24]*b.d[28] + a.d[30]*b.d[29];\n    c.d[25] = a.d[1]*b.d[24] + a.d[7]*b.d[25] + a.d[13]*b.d[26] + a.d[19]*b.d[27] + a.d[25]*b.d[28] + a.d[31]*b.d[29];\n    c.d[26] = a.d[2]*b.d[24] + a.d[8]*b.d[25] + a.d[14]*b.d[26] + a.d[20]*b.d[27] + a.d[26]*b.d[28] + a.d[32]*b.d[29];\n    c.d[27] = a.d[3]*b.d[24] + a.d[9]*b.d[25] + a.d[15]*b.d[26] + a.d[21]*b.d[27] + a.d[27]*b.d[28] + a.d[33]*b.d[29];\n    c.d[28] = a.d[4]*b.d[24] + a.d[10]*b.d[25] + a.d[16]*b.d[26] + a.d[22]*b.d[27] + a.d[28]*b.d[28] + a.d[34]*b.d[29];\n    c.d[29] = a.d[5]*b.d[24] + a.d[11]*b.d[25] + a.d[17]*b.d[26] + a.d[23]*b.d[27] + a.d[29]*b.d[28] + a.d[35]*b.d[29];\n    c.d[30] = a.d[0]*b.d[30] + a.d[6]*b.d[31] + a.d[12]*b.d[32] + a.d[18]*b.d[33] + a.d[24]*b.d[34] + a.d[30]*b.d[35];\n    c.d[31] = a.d[1]*b.d[30] + a.d[7]*b.d[31] + a.d[13]*b.d[32] + a.d[19]*b.d[33] + a.d[25]*b.d[34] + a.d[31]*b.d[35];\n    c.d[32] = a.d[2]*b.d[30] + a.d[8]*b.d[31] + a.d[14]*b.d[32] + a.d[20]*b.d[33] + a.d[26]*b.d[34] + a.d[32]*b.d[35];\n    c.d[33] = a.d[3]*b.d[30] + a.d[9]*b.d[31] + a.d[15]*b.d[32] + a.d[21]*b.d[33] + a.d[27]*b.d[34] + a.d[33]*b.d[35];\n    c.d[34] = a.d[4]*b.d[30] + a.d[10]*b.d[31] + a.d[16]*b.d[32] + a.d[22]*b.d[33] + a.d[28]*b.d[34] + a.d[34]*b.d[35];\n    c.d[35] = a.d[5]*b.d[30] + a.d[11]*b.d[31] + a.d[17]*b.d[32] + a.d[23]*b.d[33] + a.d[29]*b.d[34] + a.d[35]*b.d[35];\n    \n    return c;\n    \n}\n\n//-----------------------------------------------------------------------------\n// (a ddot b)_ijkl = a_ijmn b_mnkl where b is super-symmetric\ninline tens4dmm ddot(const tens4dmm& a, const tens4ds& b)\n{\n    tens4dmm c;\n    \n    c.d[ 0] = a.d[0]*b.d[0] + a.d[6]*b.d[1] + a.d[12]*b.d[3] + a.d[18]*b.d[6] + a.d[24]*b.d[10] + a.d[30]*b.d[15];\n    c.d[ 1] = a.d[1]*b.d[0] + a.d[7]*b.d[1] + a.d[13]*b.d[3] + a.d[19]*b.d[6] + a.d[25]*b.d[10] + a.d[31]*b.d[15];\n    c.d[ 2] = a.d[2]*b.d[0] + a.d[8]*b.d[1] + a.d[14]*b.d[3] + a.d[20]*b.d[6] + a.d[26]*b.d[10] + a.d[32]*b.d[15];\n    c.d[ 3] = a.d[3]*b.d[0] + a.d[9]*b.d[1] + a.d[15]*b.d[3] + a.d[21]*b.d[6] + a.d[27]*b.d[10] + a.d[33]*b.d[15];\n    c.d[ 4] = a.d[4]*b.d[0] + a.d[10]*b.d[1] + a.d[16]*b.d[3] + a.d[22]*b.d[6] + a.d[28]*b.d[10] + a.d[34]*b.d[15];\n    c.d[ 5] = a.d[5]*b.d[0] + a.d[11]*b.d[1] + a.d[17]*b.d[3] + a.d[23]*b.d[6] + a.d[29]*b.d[10] + a.d[35]*b.d[15];\n    c.d[ 6] = a.d[0]*b.d[1] + a.d[6]*b.d[2] + a.d[12]*b.d[4] + a.d[18]*b.d[7] + a.d[24]*b.d[11] + a.d[30]*b.d[16];\n    c.d[ 7] = a.d[1]*b.d[1] + a.d[7]*b.d[2] + a.d[13]*b.d[4] + a.d[19]*b.d[7] + a.d[25]*b.d[11] + a.d[31]*b.d[16];\n    c.d[ 8] = a.d[2]*b.d[1] + a.d[8]*b.d[2] + a.d[14]*b.d[4] + a.d[20]*b.d[7] + a.d[26]*b.d[11] + a.d[32]*b.d[16];\n    c.d[ 9] = a.d[3]*b.d[1] + a.d[9]*b.d[2] + a.d[15]*b.d[4] + a.d[21]*b.d[7] + a.d[27]*b.d[11] + a.d[33]*b.d[16];\n    c.d[10] = a.d[4]*b.d[1] + a.d[10]*b.d[2] + a.d[16]*b.d[4] + a.d[22]*b.d[7] + a.d[28]*b.d[11] + a.d[34]*b.d[16];\n    c.d[11] = a.d[5]*b.d[1] + a.d[11]*b.d[2] + a.d[17]*b.d[4] + a.d[23]*b.d[7] + a.d[29]*b.d[11] + a.d[35]*b.d[16];\n    c.d[12] = a.d[0]*b.d[3] + a.d[6]*b.d[4] + a.d[12]*b.d[5] + a.d[18]*b.d[8] + a.d[24]*b.d[12] + a.d[30]*b.d[17];\n    c.d[13] = a.d[1]*b.d[3] + a.d[7]*b.d[4] + a.d[13]*b.d[5] + a.d[19]*b.d[8] + a.d[25]*b.d[12] + a.d[31]*b.d[17];\n    c.d[14] = a.d[2]*b.d[3] + a.d[8]*b.d[4] + a.d[14]*b.d[5] + a.d[20]*b.d[8] + a.d[26]*b.d[12] + a.d[32]*b.d[17];\n    c.d[15] = a.d[3]*b.d[3] + a.d[9]*b.d[4] + a.d[15]*b.d[5] + a.d[21]*b.d[8] + a.d[27]*b.d[12] + a.d[33]*b.d[17];\n    c.d[16] = a.d[4]*b.d[3] + a.d[10]*b.d[4] + a.d[16]*b.d[5] + a.d[22]*b.d[8] + a.d[28]*b.d[12] + a.d[34]*b.d[17];\n    c.d[17] = a.d[5]*b.d[3] + a.d[11]*b.d[4] + a.d[17]*b.d[5] + a.d[23]*b.d[8] + a.d[29]*b.d[12] + a.d[35]*b.d[17];\n    c.d[18] = a.d[0]*b.d[6] + a.d[6]*b.d[7] + a.d[12]*b.d[8] + a.d[18]*b.d[9] + a.d[24]*b.d[13] + a.d[30]*b.d[18];\n    c.d[19] = a.d[1]*b.d[6] + a.d[7]*b.d[7] + a.d[13]*b.d[8] + a.d[19]*b.d[9] + a.d[25]*b.d[13] + a.d[31]*b.d[18];\n    c.d[20] = a.d[2]*b.d[6] + a.d[8]*b.d[7] + a.d[14]*b.d[8] + a.d[20]*b.d[9] + a.d[26]*b.d[13] + a.d[32]*b.d[18];\n    c.d[21] = a.d[3]*b.d[6] + a.d[9]*b.d[7] + a.d[15]*b.d[8] + a.d[21]*b.d[9] + a.d[27]*b.d[13] + a.d[33]*b.d[18];\n    c.d[22] = a.d[4]*b.d[6] + a.d[10]*b.d[7] + a.d[16]*b.d[8] + a.d[22]*b.d[9] + a.d[28]*b.d[13] + a.d[34]*b.d[18];\n    c.d[23] = a.d[5]*b.d[6] + a.d[11]*b.d[7] + a.d[17]*b.d[8] + a.d[23]*b.d[9] + a.d[29]*b.d[13] + a.d[35]*b.d[18];\n    c.d[24] = a.d[0]*b.d[10] + a.d[6]*b.d[11] + a.d[12]*b.d[12] + a.d[18]*b.d[13] + a.d[24]*b.d[14] + a.d[30]*b.d[19];\n    c.d[25] = a.d[1]*b.d[10] + a.d[7]*b.d[11] + a.d[13]*b.d[12] + a.d[19]*b.d[13] + a.d[25]*b.d[14] + a.d[31]*b.d[19];\n    c.d[26] = a.d[2]*b.d[10] + a.d[8]*b.d[11] + a.d[14]*b.d[12] + a.d[20]*b.d[13] + a.d[26]*b.d[14] + a.d[32]*b.d[19];\n    c.d[27] = a.d[3]*b.d[10] + a.d[9]*b.d[11] + a.d[15]*b.d[12] + a.d[21]*b.d[13] + a.d[27]*b.d[14] + a.d[33]*b.d[19];\n    c.d[28] = a.d[4]*b.d[10] + a.d[10]*b.d[11] + a.d[16]*b.d[12] + a.d[22]*b.d[13] + a.d[28]*b.d[14] + a.d[34]*b.d[19];\n    c.d[29] = a.d[5]*b.d[10] + a.d[11]*b.d[11] + a.d[17]*b.d[12] + a.d[23]*b.d[13] + a.d[29]*b.d[14] + a.d[35]*b.d[19];\n    c.d[30] = a.d[0]*b.d[15] + a.d[6]*b.d[16] + a.d[12]*b.d[17] + a.d[18]*b.d[18] + a.d[24]*b.d[19] + a.d[30]*b.d[20];\n    c.d[31] = a.d[1]*b.d[15] + a.d[7]*b.d[16] + a.d[13]*b.d[17] + a.d[19]*b.d[18] + a.d[25]*b.d[19] + a.d[31]*b.d[20];\n    c.d[32] = a.d[2]*b.d[15] + a.d[8]*b.d[16] + a.d[14]*b.d[17] + a.d[20]*b.d[18] + a.d[26]*b.d[19] + a.d[32]*b.d[20];\n    c.d[33] = a.d[3]*b.d[15] + a.d[9]*b.d[16] + a.d[15]*b.d[17] + a.d[21]*b.d[18] + a.d[27]*b.d[19] + a.d[33]*b.d[20];\n    c.d[34] = a.d[4]*b.d[15] + a.d[10]*b.d[16] + a.d[16]*b.d[17] + a.d[22]*b.d[18] + a.d[28]*b.d[19] + a.d[34]*b.d[20];\n    c.d[35] = a.d[5]*b.d[15] + a.d[11]*b.d[16] + a.d[17]*b.d[17] + a.d[23]*b.d[18] + a.d[29]*b.d[19] + a.d[35]*b.d[20];\n    \n    return c;\n    \n}\n\n//-----------------------------------------------------------------------------\n// inverse\ninline tens4dmm tens4dmm::inverse() const\n{\n    matrix c(6,6);\n    \n    // populate c\n    c(0,0) = d[ 0]; c(0,1) = d[ 6]; c(0,2) = d[12]; c(0,3) = d[18]; c(0,4) = d[24]; c(0,5) = d[30];\n    c(1,0) = d[ 1]; c(1,1) = d[ 7]; c(1,2) = d[13]; c(1,3) = d[19]; c(1,4) = d[25]; c(1,5) = d[31];\n    c(2,0) = d[ 2]; c(2,1) = d[ 8]; c(2,2) = d[14]; c(2,3) = d[20]; c(2,4) = d[26]; c(2,5) = d[32];\n    c(3,0) = d[ 3]; c(3,1) = d[ 9]; c(3,2) = d[15]; c(3,3) = d[21]; c(3,4) = d[27]; c(3,5) = d[33];\n    c(4,0) = d[ 4]; c(4,1) = d[10]; c(4,2) = d[16]; c(4,3) = d[22]; c(4,4) = d[28]; c(4,5) = d[34];\n    c(5,0) = d[ 5]; c(5,1) = d[11]; c(5,2) = d[17]; c(5,3) = d[23]; c(5,4) = d[29]; c(5,5) = d[35];\n    \n    // invert c\n    matrix s = c.inverse();\n    \n    // return inverse\n    tens4dmm S;\n    S.d[ 0] = s(0,0); S.d[ 6] = s(0,1); S.d[12] = s(0,2); S.d[18] = s(0,3); S.d[24] = s(0,4); S.d[30] = s(0,5);\n    S.d[ 1] = s(1,0); S.d[ 7] = s(1,1); S.d[13] = s(1,2); S.d[19] = s(1,3); S.d[25] = s(1,4); S.d[31] = s(1,5);\n    S.d[ 2] = s(2,0); S.d[ 8] = s(2,1); S.d[14] = s(2,2); S.d[20] = s(2,3); S.d[26] = s(2,4); S.d[32] = s(2,5);\n    S.d[ 3] = s(3,0); S.d[ 9] = s(3,1); S.d[15] = s(3,2); S.d[21] = s(3,3); S.d[27] = s(3,4); S.d[33] = s(3,5);\n    S.d[ 4] = s(4,0); S.d[10] = s(4,1); S.d[16] = s(4,2); S.d[22] = s(4,3); S.d[28] = s(4,4); S.d[34] = s(4,5);\n    S.d[ 5] = s(5,0); S.d[11] = s(5,1); S.d[17] = s(5,2); S.d[23] = s(5,3); S.d[29] = s(5,4); S.d[35] = s(5,5);\n\n    return S;\n}\n\n//-----------------------------------------------------------------------------\n// evaluate push/pull operation\n// c_ijpq = F_ik F_jl C_klmn F_pm F_qn\ninline tens4dmm tens4dmm::pp(const mat3d& F)\n{\n    tens4dmm c;\n\n    c.d[0] = d[0]*pow(F(0,0),4) + 2*d[3]*pow(F(0,0),3)*F(0,1) + 2*d[18]*pow(F(0,0),3)*F(0,1) + d[1]*pow(F(0,0),2)*pow(F(0,1),2) + d[6]*pow(F(0,0),2)*pow(F(0,1),2) + 4*d[21]*pow(F(0,0),2)*pow(F(0,1),2) + 2*d[9]*F(0,0)*pow(F(0,1),3) + 2*d[19]*F(0,0)*pow(F(0,1),3) + d[7]*pow(F(0,1),4) + 2*d[5]*pow(F(0,0),3)*F(0,2) + 2*d[30]*pow(F(0,0),3)*F(0,2) + 2*d[4]*pow(F(0,0),2)*F(0,1)*F(0,2) + 4*d[23]*pow(F(0,0),2)*F(0,1)*F(0,2) + 2*d[24]*pow(F(0,0),2)*F(0,1)*F(0,2) + 4*d[33]*pow(F(0,0),2)*F(0,1)*F(0,2) + 2*d[11]*F(0,0)*pow(F(0,1),2)*F(0,2) + 4*d[22]*F(0,0)*pow(F(0,1),2)*F(0,2) + 4*d[27]*F(0,0)*pow(F(0,1),2)*F(0,2) + 2*d[31]*F(0,0)*pow(F(0,1),2)*F(0,2) + 2*d[10]*pow(F(0,1),3)*F(0,2) + 2*d[25]*pow(F(0,1),3)*F(0,2) + d[2]*pow(F(0,0),2)*pow(F(0,2),2) + d[12]*pow(F(0,0),2)*pow(F(0,2),2) + 4*d[35]*pow(F(0,0),2)*pow(F(0,2),2) + 2*d[15]*F(0,0)*F(0,1)*pow(F(0,2),2) + 2*d[20]*F(0,0)*F(0,1)*pow(F(0,2),2) + 4*d[29]*F(0,0)*F(0,1)*pow(F(0,2),2) + 4*d[34]*F(0,0)*F(0,1)*pow(F(0,2),2) + d[8]*pow(F(0,1),2)*pow(F(0,2),2) + d[13]*pow(F(0,1),2)*pow(F(0,2),2) + 4*d[28]*pow(F(0,1),2)*pow(F(0,2),2) + 2*d[17]*F(0,0)*pow(F(0,2),3) + 2*d[32]*F(0,0)*pow(F(0,2),3) + 2*d[16]*F(0,1)*pow(F(0,2),3) + 2*d[26]*F(0,1)*pow(F(0,2),3) + d[14]*pow(F(0,2),4);\n\n    c.d[1] = d[0]*pow(F(0,0),2)*pow(F(1,0),2) + 2*d[18]*F(0,0)*F(0,1)*pow(F(1,0),2) + d[6]*pow(F(0,1),2)*pow(F(1,0),2) + 2*d[30]*F(0,0)*F(0,2)*pow(F(1,0),2) + 2*d[24]*F(0,1)*F(0,2)*pow(F(1,0),2) + d[12]*pow(F(0,2),2)*pow(F(1,0),2) + 2*d[3]*pow(F(0,0),2)*F(1,0)*F(1,1) + 4*d[21]*F(0,0)*F(0,1)*F(1,0)*F(1,1) + 2*d[9]*pow(F(0,1),2)*F(1,0)*F(1,1) + 4*d[33]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + 4*d[27]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + 2*d[15]*pow(F(0,2),2)*F(1,0)*F(1,1) + d[1]*pow(F(0,0),2)*pow(F(1,1),2) + 2*d[19]*F(0,0)*F(0,1)*pow(F(1,1),2) + d[7]*pow(F(0,1),2)*pow(F(1,1),2) + 2*d[31]*F(0,0)*F(0,2)*pow(F(1,1),2) + 2*d[25]*F(0,1)*F(0,2)*pow(F(1,1),2) + d[13]*pow(F(0,2),2)*pow(F(1,1),2) + 2*d[5]*pow(F(0,0),2)*F(1,0)*F(1,2) + 4*d[23]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + 2*d[11]*pow(F(0,1),2)*F(1,0)*F(1,2) + 4*d[35]*F(0,0)*F(0,2)*F(1,0)*F(1,2) + 4*d[29]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + 2*d[17]*pow(F(0,2),2)*F(1,0)*F(1,2) + 2*d[4]*pow(F(0,0),2)*F(1,1)*F(1,2) + 4*d[22]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + 2*d[10]*pow(F(0,1),2)*F(1,1)*F(1,2) + 4*d[34]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + 4*d[28]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + 2*d[16]*pow(F(0,2),2)*F(1,1)*F(1,2) + d[2]*pow(F(0,0),2)*pow(F(1,2),2) + 2*d[20]*F(0,0)*F(0,1)*pow(F(1,2),2) + d[8]*pow(F(0,1),2)*pow(F(1,2),2) + 2*d[32]*F(0,0)*F(0,2)*pow(F(1,2),2) + 2*d[26]*F(0,1)*F(0,2)*pow(F(1,2),2) + d[14]*pow(F(0,2),2)*pow(F(1,2),2);\n\n    c.d[2] = d[0]*pow(F(0,0),2)*pow(F(2,0),2) + 2*d[18]*F(0,0)*F(0,1)*pow(F(2,0),2) + d[6]*pow(F(0,1),2)*pow(F(2,0),2) + 2*d[30]*F(0,0)*F(0,2)*pow(F(2,0),2) + 2*d[24]*F(0,1)*F(0,2)*pow(F(2,0),2) + d[12]*pow(F(0,2),2)*pow(F(2,0),2) + 2*d[3]*pow(F(0,0),2)*F(2,0)*F(2,1) + 4*d[21]*F(0,0)*F(0,1)*F(2,0)*F(2,1) + 2*d[9]*pow(F(0,1),2)*F(2,0)*F(2,1) + 4*d[33]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + 4*d[27]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + 2*d[15]*pow(F(0,2),2)*F(2,0)*F(2,1) + d[1]*pow(F(0,0),2)*pow(F(2,1),2) + 2*d[19]*F(0,0)*F(0,1)*pow(F(2,1),2) + d[7]*pow(F(0,1),2)*pow(F(2,1),2) + 2*d[31]*F(0,0)*F(0,2)*pow(F(2,1),2) + 2*d[25]*F(0,1)*F(0,2)*pow(F(2,1),2) + d[13]*pow(F(0,2),2)*pow(F(2,1),2) + 2*d[5]*pow(F(0,0),2)*F(2,0)*F(2,2) + 4*d[23]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + 2*d[11]*pow(F(0,1),2)*F(2,0)*F(2,2) + 4*d[35]*F(0,0)*F(0,2)*F(2,0)*F(2,2) + 4*d[29]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + 2*d[17]*pow(F(0,2),2)*F(2,0)*F(2,2) + 2*d[4]*pow(F(0,0),2)*F(2,1)*F(2,2) + 4*d[22]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + 2*d[10]*pow(F(0,1),2)*F(2,1)*F(2,2) + 4*d[34]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + 4*d[28]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + 2*d[16]*pow(F(0,2),2)*F(2,1)*F(2,2) + d[2]*pow(F(0,0),2)*pow(F(2,2),2) + 2*d[20]*F(0,0)*F(0,1)*pow(F(2,2),2) + d[8]*pow(F(0,1),2)*pow(F(2,2),2) + 2*d[32]*F(0,0)*F(0,2)*pow(F(2,2),2) + 2*d[26]*F(0,1)*F(0,2)*pow(F(2,2),2) + d[14]*pow(F(0,2),2)*pow(F(2,2),2);\n\n    c.d[3] = d[0]*pow(F(0,0),3)*F(1,0) + 2*d[18]*pow(F(0,0),2)*F(0,1)*F(1,0) + d[6]*F(0,0)*pow(F(0,1),2)*F(1,0) + 2*d[21]*F(0,0)*pow(F(0,1),2)*F(1,0) + d[9]*pow(F(0,1),3)*F(1,0) + d[5]*pow(F(0,0),2)*F(0,2)*F(1,0) + 2*d[30]*pow(F(0,0),2)*F(0,2)*F(1,0) + 2*d[23]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + 2*d[24]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + 2*d[33]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + d[11]*pow(F(0,1),2)*F(0,2)*F(1,0) + 2*d[27]*pow(F(0,1),2)*F(0,2)*F(1,0) + d[12]*F(0,0)*pow(F(0,2),2)*F(1,0) + 2*d[35]*F(0,0)*pow(F(0,2),2)*F(1,0) + d[15]*F(0,1)*pow(F(0,2),2)*F(1,0) + 2*d[29]*F(0,1)*pow(F(0,2),2)*F(1,0) + d[17]*pow(F(0,2),3)*F(1,0) + d[1]*pow(F(0,0),2)*F(0,1)*F(1,1) + 2*d[21]*pow(F(0,0),2)*F(0,1)*F(1,1) + d[9]*F(0,0)*pow(F(0,1),2)*F(1,1) + 2*d[19]*F(0,0)*pow(F(0,1),2)*F(1,1) + d[7]*pow(F(0,1),3)*F(1,1) + d[4]*pow(F(0,0),2)*F(0,2)*F(1,1) + 2*d[33]*pow(F(0,0),2)*F(0,2)*F(1,1) + 2*d[22]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + 2*d[27]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + 2*d[31]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + d[10]*pow(F(0,1),2)*F(0,2)*F(1,1) + 2*d[25]*pow(F(0,1),2)*F(0,2)*F(1,1) + d[15]*F(0,0)*pow(F(0,2),2)*F(1,1) + 2*d[34]*F(0,0)*pow(F(0,2),2)*F(1,1) + d[13]*F(0,1)*pow(F(0,2),2)*F(1,1) + 2*d[28]*F(0,1)*pow(F(0,2),2)*F(1,1) + d[16]*pow(F(0,2),3)*F(1,1) + d[3]*pow(F(0,0),2)*(F(0,1)*F(1,0) + F(0,0)*F(1,1)) + d[5]*pow(F(0,0),3)*F(1,2) + d[4]*pow(F(0,0),2)*F(0,1)*F(1,2) + 2*d[23]*pow(F(0,0),2)*F(0,1)*F(1,2) + d[11]*F(0,0)*pow(F(0,1),2)*F(1,2) + 2*d[22]*F(0,0)*pow(F(0,1),2)*F(1,2) + d[10]*pow(F(0,1),3)*F(1,2) + d[2]*pow(F(0,0),2)*F(0,2)*F(1,2) + 2*d[35]*pow(F(0,0),2)*F(0,2)*F(1,2) + 2*d[20]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + 2*d[29]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + 2*d[34]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + d[8]*pow(F(0,1),2)*F(0,2)*F(1,2) + 2*d[28]*pow(F(0,1),2)*F(0,2)*F(1,2) + d[17]*F(0,0)*pow(F(0,2),2)*F(1,2) + 2*d[32]*F(0,0)*pow(F(0,2),2)*F(1,2) + d[16]*F(0,1)*pow(F(0,2),2)*F(1,2) + 2*d[26]*F(0,1)*pow(F(0,2),2)*F(1,2) + d[14]*pow(F(0,2),3)*F(1,2);\n\n    c.d[4] = d[0]*pow(F(0,0),2)*F(1,0)*F(2,0) + 2*d[18]*F(0,0)*F(0,1)*F(1,0)*F(2,0) + d[6]*pow(F(0,1),2)*F(1,0)*F(2,0) + 2*d[30]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + 2*d[24]*F(0,1)*F(0,2)*F(1,0)*F(2,0) + d[12]*pow(F(0,2),2)*F(1,0)*F(2,0) + d[3]*pow(F(0,0),2)*F(1,1)*F(2,0) + 2*d[21]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[9]*pow(F(0,1),2)*F(1,1)*F(2,0) + 2*d[33]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + 2*d[27]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[15]*pow(F(0,2),2)*F(1,1)*F(2,0) + d[5]*pow(F(0,0),2)*F(1,2)*F(2,0) + 2*d[23]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[11]*pow(F(0,1),2)*F(1,2)*F(2,0) + 2*d[35]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + 2*d[29]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[17]*pow(F(0,2),2)*F(1,2)*F(2,0) + d[3]*pow(F(0,0),2)*F(1,0)*F(2,1) + 2*d[21]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[9]*pow(F(0,1),2)*F(1,0)*F(2,1) + 2*d[33]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + 2*d[27]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[15]*pow(F(0,2),2)*F(1,0)*F(2,1) + d[1]*pow(F(0,0),2)*F(1,1)*F(2,1) + 2*d[19]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[7]*pow(F(0,1),2)*F(1,1)*F(2,1) + 2*d[31]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + 2*d[25]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[13]*pow(F(0,2),2)*F(1,1)*F(2,1) + d[4]*pow(F(0,0),2)*F(1,2)*F(2,1) + 2*d[22]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[10]*pow(F(0,1),2)*F(1,2)*F(2,1) + 2*d[34]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + 2*d[28]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[16]*pow(F(0,2),2)*F(1,2)*F(2,1) + d[5]*pow(F(0,0),2)*F(1,0)*F(2,2) + 2*d[23]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[11]*pow(F(0,1),2)*F(1,0)*F(2,2) + 2*d[35]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + 2*d[29]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[17]*pow(F(0,2),2)*F(1,0)*F(2,2) + d[4]*pow(F(0,0),2)*F(1,1)*F(2,2) + 2*d[22]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[10]*pow(F(0,1),2)*F(1,1)*F(2,2) + 2*d[34]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + 2*d[28]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[16]*pow(F(0,2),2)*F(1,1)*F(2,2) + d[2]*pow(F(0,0),2)*F(1,2)*F(2,2) + 2*d[20]*F(0,0)*F(0,1)*F(1,2)*F(2,2) + d[8]*pow(F(0,1),2)*F(1,2)*F(2,2) + 2*d[32]*F(0,0)*F(0,2)*F(1,2)*F(2,2) + 2*d[26]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[14]*pow(F(0,2),2)*F(1,2)*F(2,2);\n\n    c.d[5] = d[0]*pow(F(0,0),3)*F(2,0) + 2*d[18]*pow(F(0,0),2)*F(0,1)*F(2,0) + d[6]*F(0,0)*pow(F(0,1),2)*F(2,0) + 2*d[21]*F(0,0)*pow(F(0,1),2)*F(2,0) + d[9]*pow(F(0,1),3)*F(2,0) + d[5]*pow(F(0,0),2)*F(0,2)*F(2,0) + 2*d[30]*pow(F(0,0),2)*F(0,2)*F(2,0) + 2*d[23]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + 2*d[24]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + 2*d[33]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + d[11]*pow(F(0,1),2)*F(0,2)*F(2,0) + 2*d[27]*pow(F(0,1),2)*F(0,2)*F(2,0) + d[12]*F(0,0)*pow(F(0,2),2)*F(2,0) + 2*d[35]*F(0,0)*pow(F(0,2),2)*F(2,0) + d[15]*F(0,1)*pow(F(0,2),2)*F(2,0) + 2*d[29]*F(0,1)*pow(F(0,2),2)*F(2,0) + d[17]*pow(F(0,2),3)*F(2,0) + d[1]*pow(F(0,0),2)*F(0,1)*F(2,1) + 2*d[21]*pow(F(0,0),2)*F(0,1)*F(2,1) + d[9]*F(0,0)*pow(F(0,1),2)*F(2,1) + 2*d[19]*F(0,0)*pow(F(0,1),2)*F(2,1) + d[7]*pow(F(0,1),3)*F(2,1) + d[4]*pow(F(0,0),2)*F(0,2)*F(2,1) + 2*d[33]*pow(F(0,0),2)*F(0,2)*F(2,1) + 2*d[22]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + 2*d[27]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + 2*d[31]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + d[10]*pow(F(0,1),2)*F(0,2)*F(2,1) + 2*d[25]*pow(F(0,1),2)*F(0,2)*F(2,1) + d[15]*F(0,0)*pow(F(0,2),2)*F(2,1) + 2*d[34]*F(0,0)*pow(F(0,2),2)*F(2,1) + d[13]*F(0,1)*pow(F(0,2),2)*F(2,1) + 2*d[28]*F(0,1)*pow(F(0,2),2)*F(2,1) + d[16]*pow(F(0,2),3)*F(2,1) + d[3]*pow(F(0,0),2)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*pow(F(0,0),3)*F(2,2) + d[4]*pow(F(0,0),2)*F(0,1)*F(2,2) + 2*d[23]*pow(F(0,0),2)*F(0,1)*F(2,2) + d[11]*F(0,0)*pow(F(0,1),2)*F(2,2) + 2*d[22]*F(0,0)*pow(F(0,1),2)*F(2,2) + d[10]*pow(F(0,1),3)*F(2,2) + d[2]*pow(F(0,0),2)*F(0,2)*F(2,2) + 2*d[35]*pow(F(0,0),2)*F(0,2)*F(2,2) + 2*d[20]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + 2*d[29]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + 2*d[34]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + d[8]*pow(F(0,1),2)*F(0,2)*F(2,2) + 2*d[28]*pow(F(0,1),2)*F(0,2)*F(2,2) + d[17]*F(0,0)*pow(F(0,2),2)*F(2,2) + 2*d[32]*F(0,0)*pow(F(0,2),2)*F(2,2) + d[16]*F(0,1)*pow(F(0,2),2)*F(2,2) + 2*d[26]*F(0,1)*pow(F(0,2),2)*F(2,2) + d[14]*pow(F(0,2),3)*F(2,2);\n\n    c.d[6] = d[0]*pow(F(0,0),2)*pow(F(1,0),2) + 2*d[3]*F(0,0)*F(0,1)*pow(F(1,0),2) + d[1]*pow(F(0,1),2)*pow(F(1,0),2) + 2*d[5]*F(0,0)*F(0,2)*pow(F(1,0),2) + 2*d[4]*F(0,1)*F(0,2)*pow(F(1,0),2) + d[2]*pow(F(0,2),2)*pow(F(1,0),2) + 2*d[18]*pow(F(0,0),2)*F(1,0)*F(1,1) + 4*d[21]*F(0,0)*F(0,1)*F(1,0)*F(1,1) + 2*d[19]*pow(F(0,1),2)*F(1,0)*F(1,1) + 4*d[23]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + 4*d[22]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + 2*d[20]*pow(F(0,2),2)*F(1,0)*F(1,1) + d[6]*pow(F(0,0),2)*pow(F(1,1),2) + 2*d[9]*F(0,0)*F(0,1)*pow(F(1,1),2) + d[7]*pow(F(0,1),2)*pow(F(1,1),2) + 2*d[11]*F(0,0)*F(0,2)*pow(F(1,1),2) + 2*d[10]*F(0,1)*F(0,2)*pow(F(1,1),2) + d[8]*pow(F(0,2),2)*pow(F(1,1),2) + 2*d[30]*pow(F(0,0),2)*F(1,0)*F(1,2) + 4*d[33]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + 2*d[31]*pow(F(0,1),2)*F(1,0)*F(1,2) + 4*d[35]*F(0,0)*F(0,2)*F(1,0)*F(1,2) + 4*d[34]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + 2*d[32]*pow(F(0,2),2)*F(1,0)*F(1,2) + 2*d[24]*pow(F(0,0),2)*F(1,1)*F(1,2) + 4*d[27]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + 2*d[25]*pow(F(0,1),2)*F(1,1)*F(1,2) + 4*d[29]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + 4*d[28]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + 2*d[26]*pow(F(0,2),2)*F(1,1)*F(1,2) + d[12]*pow(F(0,0),2)*pow(F(1,2),2) + 2*d[15]*F(0,0)*F(0,1)*pow(F(1,2),2) + d[13]*pow(F(0,1),2)*pow(F(1,2),2) + 2*d[17]*F(0,0)*F(0,2)*pow(F(1,2),2) + 2*d[16]*F(0,1)*F(0,2)*pow(F(1,2),2) + d[14]*pow(F(0,2),2)*pow(F(1,2),2);\n\n    c.d[7] = d[0]*pow(F(1,0),4) + 2*d[3]*pow(F(1,0),3)*F(1,1) + 2*d[18]*pow(F(1,0),3)*F(1,1) + d[1]*pow(F(1,0),2)*pow(F(1,1),2) + d[6]*pow(F(1,0),2)*pow(F(1,1),2) + 4*d[21]*pow(F(1,0),2)*pow(F(1,1),2) + 2*d[9]*F(1,0)*pow(F(1,1),3) + 2*d[19]*F(1,0)*pow(F(1,1),3) + d[7]*pow(F(1,1),4) + 2*d[5]*pow(F(1,0),3)*F(1,2) + 2*d[30]*pow(F(1,0),3)*F(1,2) + 2*d[4]*pow(F(1,0),2)*F(1,1)*F(1,2) + 4*d[23]*pow(F(1,0),2)*F(1,1)*F(1,2) + 2*d[24]*pow(F(1,0),2)*F(1,1)*F(1,2) + 4*d[33]*pow(F(1,0),2)*F(1,1)*F(1,2) + 2*d[11]*F(1,0)*pow(F(1,1),2)*F(1,2) + 4*d[22]*F(1,0)*pow(F(1,1),2)*F(1,2) + 4*d[27]*F(1,0)*pow(F(1,1),2)*F(1,2) + 2*d[31]*F(1,0)*pow(F(1,1),2)*F(1,2) + 2*d[10]*pow(F(1,1),3)*F(1,2) + 2*d[25]*pow(F(1,1),3)*F(1,2) + d[2]*pow(F(1,0),2)*pow(F(1,2),2) + d[12]*pow(F(1,0),2)*pow(F(1,2),2) + 4*d[35]*pow(F(1,0),2)*pow(F(1,2),2) + 2*d[15]*F(1,0)*F(1,1)*pow(F(1,2),2) + 2*d[20]*F(1,0)*F(1,1)*pow(F(1,2),2) + 4*d[29]*F(1,0)*F(1,1)*pow(F(1,2),2) + 4*d[34]*F(1,0)*F(1,1)*pow(F(1,2),2) + d[8]*pow(F(1,1),2)*pow(F(1,2),2) + d[13]*pow(F(1,1),2)*pow(F(1,2),2) + 4*d[28]*pow(F(1,1),2)*pow(F(1,2),2) + 2*d[17]*F(1,0)*pow(F(1,2),3) + 2*d[32]*F(1,0)*pow(F(1,2),3) + 2*d[16]*F(1,1)*pow(F(1,2),3) + 2*d[26]*F(1,1)*pow(F(1,2),3) + d[14]*pow(F(1,2),4);\n\n    c.d[8] = d[0]*pow(F(1,0),2)*pow(F(2,0),2) + 2*d[18]*F(1,0)*F(1,1)*pow(F(2,0),2) + d[6]*pow(F(1,1),2)*pow(F(2,0),2) + 2*d[30]*F(1,0)*F(1,2)*pow(F(2,0),2) + 2*d[24]*F(1,1)*F(1,2)*pow(F(2,0),2) + d[12]*pow(F(1,2),2)*pow(F(2,0),2) + 2*d[3]*pow(F(1,0),2)*F(2,0)*F(2,1) + 4*d[21]*F(1,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[9]*pow(F(1,1),2)*F(2,0)*F(2,1) + 4*d[33]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + 4*d[27]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + 2*d[15]*pow(F(1,2),2)*F(2,0)*F(2,1) + d[1]*pow(F(1,0),2)*pow(F(2,1),2) + 2*d[19]*F(1,0)*F(1,1)*pow(F(2,1),2) + d[7]*pow(F(1,1),2)*pow(F(2,1),2) + 2*d[31]*F(1,0)*F(1,2)*pow(F(2,1),2) + 2*d[25]*F(1,1)*F(1,2)*pow(F(2,1),2) + d[13]*pow(F(1,2),2)*pow(F(2,1),2) + 2*d[5]*pow(F(1,0),2)*F(2,0)*F(2,2) + 4*d[23]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + 2*d[11]*pow(F(1,1),2)*F(2,0)*F(2,2) + 4*d[35]*F(1,0)*F(1,2)*F(2,0)*F(2,2) + 4*d[29]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + 2*d[17]*pow(F(1,2),2)*F(2,0)*F(2,2) + 2*d[4]*pow(F(1,0),2)*F(2,1)*F(2,2) + 4*d[22]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + 2*d[10]*pow(F(1,1),2)*F(2,1)*F(2,2) + 4*d[34]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + 4*d[28]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[16]*pow(F(1,2),2)*F(2,1)*F(2,2) + d[2]*pow(F(1,0),2)*pow(F(2,2),2) + 2*d[20]*F(1,0)*F(1,1)*pow(F(2,2),2) + d[8]*pow(F(1,1),2)*pow(F(2,2),2) + 2*d[32]*F(1,0)*F(1,2)*pow(F(2,2),2) + 2*d[26]*F(1,1)*F(1,2)*pow(F(2,2),2) + d[14]*pow(F(1,2),2)*pow(F(2,2),2);\n\n    c.d[9] = d[0]*F(0,0)*pow(F(1,0),3) + d[5]*F(0,2)*pow(F(1,0),3) + 2*d[18]*F(0,0)*pow(F(1,0),2)*F(1,1) + d[1]*F(0,1)*pow(F(1,0),2)*F(1,1) + 2*d[21]*F(0,1)*pow(F(1,0),2)*F(1,1) + d[4]*F(0,2)*pow(F(1,0),2)*F(1,1) + 2*d[23]*F(0,2)*pow(F(1,0),2)*F(1,1) + d[6]*F(0,0)*F(1,0)*pow(F(1,1),2) + 2*d[21]*F(0,0)*F(1,0)*pow(F(1,1),2) + d[9]*F(0,1)*F(1,0)*pow(F(1,1),2) + 2*d[19]*F(0,1)*F(1,0)*pow(F(1,1),2) + d[11]*F(0,2)*F(1,0)*pow(F(1,1),2) + 2*d[22]*F(0,2)*F(1,0)*pow(F(1,1),2) + d[9]*F(0,0)*pow(F(1,1),3) + d[7]*F(0,1)*pow(F(1,1),3) + d[10]*F(0,2)*pow(F(1,1),3) + d[3]*pow(F(1,0),2)*(F(0,1)*F(1,0) + F(0,0)*F(1,1)) + d[5]*F(0,0)*pow(F(1,0),2)*F(1,2) + 2*d[30]*F(0,0)*pow(F(1,0),2)*F(1,2) + d[4]*F(0,1)*pow(F(1,0),2)*F(1,2) + 2*d[33]*F(0,1)*pow(F(1,0),2)*F(1,2) + d[2]*F(0,2)*pow(F(1,0),2)*F(1,2) + 2*d[35]*F(0,2)*pow(F(1,0),2)*F(1,2) + 2*d[23]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 2*d[24]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 2*d[33]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 2*d[22]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[27]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[31]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[20]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + 2*d[29]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + 2*d[34]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + d[11]*F(0,0)*pow(F(1,1),2)*F(1,2) + 2*d[27]*F(0,0)*pow(F(1,1),2)*F(1,2) + d[10]*F(0,1)*pow(F(1,1),2)*F(1,2) + 2*d[25]*F(0,1)*pow(F(1,1),2)*F(1,2) + d[8]*F(0,2)*pow(F(1,1),2)*F(1,2) + 2*d[28]*F(0,2)*pow(F(1,1),2)*F(1,2) + d[12]*F(0,0)*F(1,0)*pow(F(1,2),2) + 2*d[35]*F(0,0)*F(1,0)*pow(F(1,2),2) + d[15]*F(0,1)*F(1,0)*pow(F(1,2),2) + 2*d[34]*F(0,1)*F(1,0)*pow(F(1,2),2) + d[17]*F(0,2)*F(1,0)*pow(F(1,2),2) + 2*d[32]*F(0,2)*F(1,0)*pow(F(1,2),2) + d[15]*F(0,0)*F(1,1)*pow(F(1,2),2) + 2*d[29]*F(0,0)*F(1,1)*pow(F(1,2),2) + d[13]*F(0,1)*F(1,1)*pow(F(1,2),2) + 2*d[28]*F(0,1)*F(1,1)*pow(F(1,2),2) + d[16]*F(0,2)*F(1,1)*pow(F(1,2),2) + 2*d[26]*F(0,2)*F(1,1)*pow(F(1,2),2) + d[17]*F(0,0)*pow(F(1,2),3) + d[16]*F(0,1)*pow(F(1,2),3) + d[14]*F(0,2)*pow(F(1,2),3);\n\n    c.d[10] = d[0]*pow(F(1,0),3)*F(2,0) + 2*d[18]*pow(F(1,0),2)*F(1,1)*F(2,0) + d[6]*F(1,0)*pow(F(1,1),2)*F(2,0) + 2*d[21]*F(1,0)*pow(F(1,1),2)*F(2,0) + d[9]*pow(F(1,1),3)*F(2,0) + d[5]*pow(F(1,0),2)*F(1,2)*F(2,0) + 2*d[30]*pow(F(1,0),2)*F(1,2)*F(2,0) + 2*d[23]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[24]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[33]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + d[11]*pow(F(1,1),2)*F(1,2)*F(2,0) + 2*d[27]*pow(F(1,1),2)*F(1,2)*F(2,0) + d[12]*F(1,0)*pow(F(1,2),2)*F(2,0) + 2*d[35]*F(1,0)*pow(F(1,2),2)*F(2,0) + d[15]*F(1,1)*pow(F(1,2),2)*F(2,0) + 2*d[29]*F(1,1)*pow(F(1,2),2)*F(2,0) + d[17]*pow(F(1,2),3)*F(2,0) + d[1]*pow(F(1,0),2)*F(1,1)*F(2,1) + 2*d[21]*pow(F(1,0),2)*F(1,1)*F(2,1) + d[9]*F(1,0)*pow(F(1,1),2)*F(2,1) + 2*d[19]*F(1,0)*pow(F(1,1),2)*F(2,1) + d[7]*pow(F(1,1),3)*F(2,1) + d[4]*pow(F(1,0),2)*F(1,2)*F(2,1) + 2*d[33]*pow(F(1,0),2)*F(1,2)*F(2,1) + 2*d[22]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[27]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[31]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + d[10]*pow(F(1,1),2)*F(1,2)*F(2,1) + 2*d[25]*pow(F(1,1),2)*F(1,2)*F(2,1) + d[15]*F(1,0)*pow(F(1,2),2)*F(2,1) + 2*d[34]*F(1,0)*pow(F(1,2),2)*F(2,1) + d[13]*F(1,1)*pow(F(1,2),2)*F(2,1) + 2*d[28]*F(1,1)*pow(F(1,2),2)*F(2,1) + d[16]*pow(F(1,2),3)*F(2,1) + d[3]*pow(F(1,0),2)*(F(1,1)*F(2,0) + F(1,0)*F(2,1)) + d[5]*pow(F(1,0),3)*F(2,2) + d[4]*pow(F(1,0),2)*F(1,1)*F(2,2) + 2*d[23]*pow(F(1,0),2)*F(1,1)*F(2,2) + d[11]*F(1,0)*pow(F(1,1),2)*F(2,2) + 2*d[22]*F(1,0)*pow(F(1,1),2)*F(2,2) + d[10]*pow(F(1,1),3)*F(2,2) + d[2]*pow(F(1,0),2)*F(1,2)*F(2,2) + 2*d[35]*pow(F(1,0),2)*F(1,2)*F(2,2) + 2*d[20]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[29]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[34]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + d[8]*pow(F(1,1),2)*F(1,2)*F(2,2) + 2*d[28]*pow(F(1,1),2)*F(1,2)*F(2,2) + d[17]*F(1,0)*pow(F(1,2),2)*F(2,2) + 2*d[32]*F(1,0)*pow(F(1,2),2)*F(2,2) + d[16]*F(1,1)*pow(F(1,2),2)*F(2,2) + 2*d[26]*F(1,1)*pow(F(1,2),2)*F(2,2) + d[14]*pow(F(1,2),3)*F(2,2);\n\n    c.d[11] = d[0]*F(0,0)*pow(F(1,0),2)*F(2,0) + d[5]*F(0,2)*pow(F(1,0),2)*F(2,0) + 2*d[18]*F(0,0)*F(1,0)*F(1,1)*F(2,0) + 2*d[21]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + 2*d[23]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[6]*F(0,0)*pow(F(1,1),2)*F(2,0) + d[9]*F(0,1)*pow(F(1,1),2)*F(2,0) + d[11]*F(0,2)*pow(F(1,1),2)*F(2,0) + 2*d[30]*F(0,0)*F(1,0)*F(1,2)*F(2,0) + 2*d[33]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + 2*d[35]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + 2*d[24]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[27]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + 2*d[29]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[12]*F(0,0)*pow(F(1,2),2)*F(2,0) + d[15]*F(0,1)*pow(F(1,2),2)*F(2,0) + d[17]*F(0,2)*pow(F(1,2),2)*F(2,0) + d[1]*F(0,1)*pow(F(1,0),2)*F(2,1) + d[4]*F(0,2)*pow(F(1,0),2)*F(2,1) + 2*d[21]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + 2*d[19]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + 2*d[22]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[9]*F(0,0)*pow(F(1,1),2)*F(2,1) + d[7]*F(0,1)*pow(F(1,1),2)*F(2,1) + d[10]*F(0,2)*pow(F(1,1),2)*F(2,1) + 2*d[33]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + 2*d[31]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + 2*d[34]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + 2*d[27]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[25]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + 2*d[28]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[15]*F(0,0)*pow(F(1,2),2)*F(2,1) + d[13]*F(0,1)*pow(F(1,2),2)*F(2,1) + d[16]*F(0,2)*pow(F(1,2),2)*F(2,1) + d[3]*pow(F(1,0),2)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*F(0,0)*pow(F(1,0),2)*F(2,2) + d[4]*F(0,1)*pow(F(1,0),2)*F(2,2) + d[2]*F(0,2)*pow(F(1,0),2)*F(2,2) + 2*d[23]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + 2*d[22]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + 2*d[20]*F(0,2)*F(1,0)*F(1,1)*F(2,2) + d[11]*F(0,0)*pow(F(1,1),2)*F(2,2) + d[10]*F(0,1)*pow(F(1,1),2)*F(2,2) + d[8]*F(0,2)*pow(F(1,1),2)*F(2,2) + 2*d[35]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + 2*d[34]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + 2*d[32]*F(0,2)*F(1,0)*F(1,2)*F(2,2) + 2*d[29]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[28]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + 2*d[26]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[17]*F(0,0)*pow(F(1,2),2)*F(2,2) + d[16]*F(0,1)*pow(F(1,2),2)*F(2,2) + d[14]*F(0,2)*pow(F(1,2),2)*F(2,2);\n\n    c.d[12] = d[0]*pow(F(0,0),2)*pow(F(2,0),2) + 2*d[3]*F(0,0)*F(0,1)*pow(F(2,0),2) + d[1]*pow(F(0,1),2)*pow(F(2,0),2) + 2*d[5]*F(0,0)*F(0,2)*pow(F(2,0),2) + 2*d[4]*F(0,1)*F(0,2)*pow(F(2,0),2) + d[2]*pow(F(0,2),2)*pow(F(2,0),2) + 2*d[18]*pow(F(0,0),2)*F(2,0)*F(2,1) + 4*d[21]*F(0,0)*F(0,1)*F(2,0)*F(2,1) + 2*d[19]*pow(F(0,1),2)*F(2,0)*F(2,1) + 4*d[23]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + 4*d[22]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + 2*d[20]*pow(F(0,2),2)*F(2,0)*F(2,1) + d[6]*pow(F(0,0),2)*pow(F(2,1),2) + 2*d[9]*F(0,0)*F(0,1)*pow(F(2,1),2) + d[7]*pow(F(0,1),2)*pow(F(2,1),2) + 2*d[11]*F(0,0)*F(0,2)*pow(F(2,1),2) + 2*d[10]*F(0,1)*F(0,2)*pow(F(2,1),2) + d[8]*pow(F(0,2),2)*pow(F(2,1),2) + 2*d[30]*pow(F(0,0),2)*F(2,0)*F(2,2) + 4*d[33]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + 2*d[31]*pow(F(0,1),2)*F(2,0)*F(2,2) + 4*d[35]*F(0,0)*F(0,2)*F(2,0)*F(2,2) + 4*d[34]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + 2*d[32]*pow(F(0,2),2)*F(2,0)*F(2,2) + 2*d[24]*pow(F(0,0),2)*F(2,1)*F(2,2) + 4*d[27]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + 2*d[25]*pow(F(0,1),2)*F(2,1)*F(2,2) + 4*d[29]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + 4*d[28]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + 2*d[26]*pow(F(0,2),2)*F(2,1)*F(2,2) + d[12]*pow(F(0,0),2)*pow(F(2,2),2) + 2*d[15]*F(0,0)*F(0,1)*pow(F(2,2),2) + d[13]*pow(F(0,1),2)*pow(F(2,2),2) + 2*d[17]*F(0,0)*F(0,2)*pow(F(2,2),2) + 2*d[16]*F(0,1)*F(0,2)*pow(F(2,2),2) + d[14]*pow(F(0,2),2)*pow(F(2,2),2);\n\n    c.d[13] = d[0]*pow(F(1,0),2)*pow(F(2,0),2) + 2*d[3]*F(1,0)*F(1,1)*pow(F(2,0),2) + d[1]*pow(F(1,1),2)*pow(F(2,0),2) + 2*d[5]*F(1,0)*F(1,2)*pow(F(2,0),2) + 2*d[4]*F(1,1)*F(1,2)*pow(F(2,0),2) + d[2]*pow(F(1,2),2)*pow(F(2,0),2) + 2*d[18]*pow(F(1,0),2)*F(2,0)*F(2,1) + 4*d[21]*F(1,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[19]*pow(F(1,1),2)*F(2,0)*F(2,1) + 4*d[23]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + 4*d[22]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + 2*d[20]*pow(F(1,2),2)*F(2,0)*F(2,1) + d[6]*pow(F(1,0),2)*pow(F(2,1),2) + 2*d[9]*F(1,0)*F(1,1)*pow(F(2,1),2) + d[7]*pow(F(1,1),2)*pow(F(2,1),2) + 2*d[11]*F(1,0)*F(1,2)*pow(F(2,1),2) + 2*d[10]*F(1,1)*F(1,2)*pow(F(2,1),2) + d[8]*pow(F(1,2),2)*pow(F(2,1),2) + 2*d[30]*pow(F(1,0),2)*F(2,0)*F(2,2) + 4*d[33]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + 2*d[31]*pow(F(1,1),2)*F(2,0)*F(2,2) + 4*d[35]*F(1,0)*F(1,2)*F(2,0)*F(2,2) + 4*d[34]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + 2*d[32]*pow(F(1,2),2)*F(2,0)*F(2,2) + 2*d[24]*pow(F(1,0),2)*F(2,1)*F(2,2) + 4*d[27]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + 2*d[25]*pow(F(1,1),2)*F(2,1)*F(2,2) + 4*d[29]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + 4*d[28]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[26]*pow(F(1,2),2)*F(2,1)*F(2,2) + d[12]*pow(F(1,0),2)*pow(F(2,2),2) + 2*d[15]*F(1,0)*F(1,1)*pow(F(2,2),2) + d[13]*pow(F(1,1),2)*pow(F(2,2),2) + 2*d[17]*F(1,0)*F(1,2)*pow(F(2,2),2) + 2*d[16]*F(1,1)*F(1,2)*pow(F(2,2),2) + d[14]*pow(F(1,2),2)*pow(F(2,2),2);\n\n    c.d[14] = d[0]*pow(F(2,0),4) + 2*d[3]*pow(F(2,0),3)*F(2,1) + 2*d[18]*pow(F(2,0),3)*F(2,1) + d[1]*pow(F(2,0),2)*pow(F(2,1),2) + d[6]*pow(F(2,0),2)*pow(F(2,1),2) + 4*d[21]*pow(F(2,0),2)*pow(F(2,1),2) + 2*d[9]*F(2,0)*pow(F(2,1),3) + 2*d[19]*F(2,0)*pow(F(2,1),3) + d[7]*pow(F(2,1),4) + 2*d[5]*pow(F(2,0),3)*F(2,2) + 2*d[30]*pow(F(2,0),3)*F(2,2) + 2*d[4]*pow(F(2,0),2)*F(2,1)*F(2,2) + 4*d[23]*pow(F(2,0),2)*F(2,1)*F(2,2) + 2*d[24]*pow(F(2,0),2)*F(2,1)*F(2,2) + 4*d[33]*pow(F(2,0),2)*F(2,1)*F(2,2) + 2*d[11]*F(2,0)*pow(F(2,1),2)*F(2,2) + 4*d[22]*F(2,0)*pow(F(2,1),2)*F(2,2) + 4*d[27]*F(2,0)*pow(F(2,1),2)*F(2,2) + 2*d[31]*F(2,0)*pow(F(2,1),2)*F(2,2) + 2*d[10]*pow(F(2,1),3)*F(2,2) + 2*d[25]*pow(F(2,1),3)*F(2,2) + d[2]*pow(F(2,0),2)*pow(F(2,2),2) + d[12]*pow(F(2,0),2)*pow(F(2,2),2) + 4*d[35]*pow(F(2,0),2)*pow(F(2,2),2) + 2*d[15]*F(2,0)*F(2,1)*pow(F(2,2),2) + 2*d[20]*F(2,0)*F(2,1)*pow(F(2,2),2) + 4*d[29]*F(2,0)*F(2,1)*pow(F(2,2),2) + 4*d[34]*F(2,0)*F(2,1)*pow(F(2,2),2) + d[8]*pow(F(2,1),2)*pow(F(2,2),2) + d[13]*pow(F(2,1),2)*pow(F(2,2),2) + 4*d[28]*pow(F(2,1),2)*pow(F(2,2),2) + 2*d[17]*F(2,0)*pow(F(2,2),3) + 2*d[32]*F(2,0)*pow(F(2,2),3) + 2*d[16]*F(2,1)*pow(F(2,2),3) + 2*d[26]*F(2,1)*pow(F(2,2),3) + d[14]*pow(F(2,2),4);\n\n    c.d[15] = d[0]*F(0,0)*F(1,0)*pow(F(2,0),2) + d[5]*F(0,2)*F(1,0)*pow(F(2,0),2) + d[1]*F(0,1)*F(1,1)*pow(F(2,0),2) + d[4]*F(0,2)*F(1,1)*pow(F(2,0),2) + d[3]*(F(0,1)*F(1,0) + F(0,0)*F(1,1))*pow(F(2,0),2) + d[5]*F(0,0)*F(1,2)*pow(F(2,0),2) + d[4]*F(0,1)*F(1,2)*pow(F(2,0),2) + d[2]*F(0,2)*F(1,2)*pow(F(2,0),2) + 2*d[18]*F(0,0)*F(1,0)*F(2,0)*F(2,1) + 2*d[21]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + 2*d[23]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + 2*d[21]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[19]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + 2*d[22]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + 2*d[23]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + 2*d[22]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + 2*d[20]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[6]*F(0,0)*F(1,0)*pow(F(2,1),2) + d[9]*F(0,1)*F(1,0)*pow(F(2,1),2) + d[11]*F(0,2)*F(1,0)*pow(F(2,1),2) + d[9]*F(0,0)*F(1,1)*pow(F(2,1),2) + d[7]*F(0,1)*F(1,1)*pow(F(2,1),2) + d[10]*F(0,2)*F(1,1)*pow(F(2,1),2) + d[11]*F(0,0)*F(1,2)*pow(F(2,1),2) + d[10]*F(0,1)*F(1,2)*pow(F(2,1),2) + d[8]*F(0,2)*F(1,2)*pow(F(2,1),2) + 2*d[30]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + 2*d[33]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + 2*d[35]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + 2*d[33]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + 2*d[31]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + 2*d[34]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + 2*d[35]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + 2*d[34]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + 2*d[32]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + 2*d[24]*F(0,0)*F(1,0)*F(2,1)*F(2,2) + 2*d[27]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + 2*d[29]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + 2*d[27]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + 2*d[25]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + 2*d[28]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + 2*d[29]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + 2*d[28]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[26]*F(0,2)*F(1,2)*F(2,1)*F(2,2) + d[12]*F(0,0)*F(1,0)*pow(F(2,2),2) + d[15]*F(0,1)*F(1,0)*pow(F(2,2),2) + d[17]*F(0,2)*F(1,0)*pow(F(2,2),2) + d[15]*F(0,0)*F(1,1)*pow(F(2,2),2) + d[13]*F(0,1)*F(1,1)*pow(F(2,2),2) + d[16]*F(0,2)*F(1,1)*pow(F(2,2),2) + d[17]*F(0,0)*F(1,2)*pow(F(2,2),2) + d[16]*F(0,1)*F(1,2)*pow(F(2,2),2) + d[14]*F(0,2)*F(1,2)*pow(F(2,2),2);\n\n    c.d[16] = d[0]*F(1,0)*pow(F(2,0),3) + d[5]*F(1,2)*pow(F(2,0),3) + 2*d[18]*F(1,0)*pow(F(2,0),2)*F(2,1) + d[1]*F(1,1)*pow(F(2,0),2)*F(2,1) + 2*d[21]*F(1,1)*pow(F(2,0),2)*F(2,1) + d[4]*F(1,2)*pow(F(2,0),2)*F(2,1) + 2*d[23]*F(1,2)*pow(F(2,0),2)*F(2,1) + d[6]*F(1,0)*F(2,0)*pow(F(2,1),2) + 2*d[21]*F(1,0)*F(2,0)*pow(F(2,1),2) + d[9]*F(1,1)*F(2,0)*pow(F(2,1),2) + 2*d[19]*F(1,1)*F(2,0)*pow(F(2,1),2) + d[11]*F(1,2)*F(2,0)*pow(F(2,1),2) + 2*d[22]*F(1,2)*F(2,0)*pow(F(2,1),2) + d[9]*F(1,0)*pow(F(2,1),3) + d[7]*F(1,1)*pow(F(2,1),3) + d[10]*F(1,2)*pow(F(2,1),3) + d[3]*pow(F(2,0),2)*(F(1,1)*F(2,0) + F(1,0)*F(2,1)) + d[5]*F(1,0)*pow(F(2,0),2)*F(2,2) + 2*d[30]*F(1,0)*pow(F(2,0),2)*F(2,2) + d[4]*F(1,1)*pow(F(2,0),2)*F(2,2) + 2*d[33]*F(1,1)*pow(F(2,0),2)*F(2,2) + d[2]*F(1,2)*pow(F(2,0),2)*F(2,2) + 2*d[35]*F(1,2)*pow(F(2,0),2)*F(2,2) + 2*d[23]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[24]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[33]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[22]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[27]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[31]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[20]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[29]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[34]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + d[11]*F(1,0)*pow(F(2,1),2)*F(2,2) + 2*d[27]*F(1,0)*pow(F(2,1),2)*F(2,2) + d[10]*F(1,1)*pow(F(2,1),2)*F(2,2) + 2*d[25]*F(1,1)*pow(F(2,1),2)*F(2,2) + d[8]*F(1,2)*pow(F(2,1),2)*F(2,2) + 2*d[28]*F(1,2)*pow(F(2,1),2)*F(2,2) + d[12]*F(1,0)*F(2,0)*pow(F(2,2),2) + 2*d[35]*F(1,0)*F(2,0)*pow(F(2,2),2) + d[15]*F(1,1)*F(2,0)*pow(F(2,2),2) + 2*d[34]*F(1,1)*F(2,0)*pow(F(2,2),2) + d[17]*F(1,2)*F(2,0)*pow(F(2,2),2) + 2*d[32]*F(1,2)*F(2,0)*pow(F(2,2),2) + d[15]*F(1,0)*F(2,1)*pow(F(2,2),2) + 2*d[29]*F(1,0)*F(2,1)*pow(F(2,2),2) + d[13]*F(1,1)*F(2,1)*pow(F(2,2),2) + 2*d[28]*F(1,1)*F(2,1)*pow(F(2,2),2) + d[16]*F(1,2)*F(2,1)*pow(F(2,2),2) + 2*d[26]*F(1,2)*F(2,1)*pow(F(2,2),2) + d[17]*F(1,0)*pow(F(2,2),3) + d[16]*F(1,1)*pow(F(2,2),3) + d[14]*F(1,2)*pow(F(2,2),3);\n\n    c.d[17] = d[0]*F(0,0)*pow(F(2,0),3) + d[5]*F(0,2)*pow(F(2,0),3) + 2*d[18]*F(0,0)*pow(F(2,0),2)*F(2,1) + d[1]*F(0,1)*pow(F(2,0),2)*F(2,1) + 2*d[21]*F(0,1)*pow(F(2,0),2)*F(2,1) + d[4]*F(0,2)*pow(F(2,0),2)*F(2,1) + 2*d[23]*F(0,2)*pow(F(2,0),2)*F(2,1) + d[6]*F(0,0)*F(2,0)*pow(F(2,1),2) + 2*d[21]*F(0,0)*F(2,0)*pow(F(2,1),2) + d[9]*F(0,1)*F(2,0)*pow(F(2,1),2) + 2*d[19]*F(0,1)*F(2,0)*pow(F(2,1),2) + d[11]*F(0,2)*F(2,0)*pow(F(2,1),2) + 2*d[22]*F(0,2)*F(2,0)*pow(F(2,1),2) + d[9]*F(0,0)*pow(F(2,1),3) + d[7]*F(0,1)*pow(F(2,1),3) + d[10]*F(0,2)*pow(F(2,1),3) + d[3]*pow(F(2,0),2)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*F(0,0)*pow(F(2,0),2)*F(2,2) + 2*d[30]*F(0,0)*pow(F(2,0),2)*F(2,2) + d[4]*F(0,1)*pow(F(2,0),2)*F(2,2) + 2*d[33]*F(0,1)*pow(F(2,0),2)*F(2,2) + d[2]*F(0,2)*pow(F(2,0),2)*F(2,2) + 2*d[35]*F(0,2)*pow(F(2,0),2)*F(2,2) + 2*d[23]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[24]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[33]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[22]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[27]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[31]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[20]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[29]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[34]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + d[11]*F(0,0)*pow(F(2,1),2)*F(2,2) + 2*d[27]*F(0,0)*pow(F(2,1),2)*F(2,2) + d[10]*F(0,1)*pow(F(2,1),2)*F(2,2) + 2*d[25]*F(0,1)*pow(F(2,1),2)*F(2,2) + d[8]*F(0,2)*pow(F(2,1),2)*F(2,2) + 2*d[28]*F(0,2)*pow(F(2,1),2)*F(2,2) + d[12]*F(0,0)*F(2,0)*pow(F(2,2),2) + 2*d[35]*F(0,0)*F(2,0)*pow(F(2,2),2) + d[15]*F(0,1)*F(2,0)*pow(F(2,2),2) + 2*d[34]*F(0,1)*F(2,0)*pow(F(2,2),2) + d[17]*F(0,2)*F(2,0)*pow(F(2,2),2) + 2*d[32]*F(0,2)*F(2,0)*pow(F(2,2),2) + d[15]*F(0,0)*F(2,1)*pow(F(2,2),2) + 2*d[29]*F(0,0)*F(2,1)*pow(F(2,2),2) + d[13]*F(0,1)*F(2,1)*pow(F(2,2),2) + 2*d[28]*F(0,1)*F(2,1)*pow(F(2,2),2) + d[16]*F(0,2)*F(2,1)*pow(F(2,2),2) + 2*d[26]*F(0,2)*F(2,1)*pow(F(2,2),2) + d[17]*F(0,0)*pow(F(2,2),3) + d[16]*F(0,1)*pow(F(2,2),3) + d[14]*F(0,2)*pow(F(2,2),3);\n\n    c.d[18] = d[0]*pow(F(0,0),3)*F(1,0) + 2*d[3]*pow(F(0,0),2)*F(0,1)*F(1,0) + d[18]*pow(F(0,0),2)*F(0,1)*F(1,0) + d[1]*F(0,0)*pow(F(0,1),2)*F(1,0) + 2*d[21]*F(0,0)*pow(F(0,1),2)*F(1,0) + d[19]*pow(F(0,1),3)*F(1,0) + 2*d[5]*pow(F(0,0),2)*F(0,2)*F(1,0) + d[30]*pow(F(0,0),2)*F(0,2)*F(1,0) + 2*d[4]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + 2*d[23]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + 2*d[33]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + 2*d[22]*pow(F(0,1),2)*F(0,2)*F(1,0) + d[31]*pow(F(0,1),2)*F(0,2)*F(1,0) + d[2]*F(0,0)*pow(F(0,2),2)*F(1,0) + 2*d[35]*F(0,0)*pow(F(0,2),2)*F(1,0) + d[20]*F(0,1)*pow(F(0,2),2)*F(1,0) + 2*d[34]*F(0,1)*pow(F(0,2),2)*F(1,0) + d[32]*pow(F(0,2),3)*F(1,0) + d[18]*pow(F(0,0),3)*F(1,1) + d[6]*pow(F(0,0),2)*F(0,1)*F(1,1) + 2*d[21]*pow(F(0,0),2)*F(0,1)*F(1,1) + 2*d[9]*F(0,0)*pow(F(0,1),2)*F(1,1) + d[19]*F(0,0)*pow(F(0,1),2)*F(1,1) + d[7]*pow(F(0,1),3)*F(1,1) + 2*d[23]*pow(F(0,0),2)*F(0,2)*F(1,1) + d[24]*pow(F(0,0),2)*F(0,2)*F(1,1) + 2*d[11]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + 2*d[22]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + 2*d[27]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + 2*d[10]*pow(F(0,1),2)*F(0,2)*F(1,1) + d[25]*pow(F(0,1),2)*F(0,2)*F(1,1) + d[20]*F(0,0)*pow(F(0,2),2)*F(1,1) + 2*d[29]*F(0,0)*pow(F(0,2),2)*F(1,1) + d[8]*F(0,1)*pow(F(0,2),2)*F(1,1) + 2*d[28]*F(0,1)*pow(F(0,2),2)*F(1,1) + d[26]*pow(F(0,2),3)*F(1,1) + d[30]*pow(F(0,0),3)*F(1,2) + d[24]*pow(F(0,0),2)*F(0,1)*F(1,2) + 2*d[33]*pow(F(0,0),2)*F(0,1)*F(1,2) + 2*d[27]*F(0,0)*pow(F(0,1),2)*F(1,2) + d[31]*F(0,0)*pow(F(0,1),2)*F(1,2) + d[25]*pow(F(0,1),3)*F(1,2) + d[12]*pow(F(0,0),2)*F(0,2)*F(1,2) + 2*d[35]*pow(F(0,0),2)*F(0,2)*F(1,2) + 2*d[15]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + 2*d[29]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + 2*d[34]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + d[13]*pow(F(0,1),2)*F(0,2)*F(1,2) + 2*d[28]*pow(F(0,1),2)*F(0,2)*F(1,2) + 2*d[17]*F(0,0)*pow(F(0,2),2)*F(1,2) + d[32]*F(0,0)*pow(F(0,2),2)*F(1,2) + 2*d[16]*F(0,1)*pow(F(0,2),2)*F(1,2) + d[26]*F(0,1)*pow(F(0,2),2)*F(1,2) + d[14]*pow(F(0,2),3)*F(1,2);\n\n    c.d[19] = d[0]*F(0,0)*pow(F(1,0),3) + d[30]*F(0,2)*pow(F(1,0),3) + 2*d[3]*F(0,0)*pow(F(1,0),2)*F(1,1) + d[6]*F(0,1)*pow(F(1,0),2)*F(1,1) + 2*d[21]*F(0,1)*pow(F(1,0),2)*F(1,1) + d[24]*F(0,2)*pow(F(1,0),2)*F(1,1) + 2*d[33]*F(0,2)*pow(F(1,0),2)*F(1,1) + d[1]*F(0,0)*F(1,0)*pow(F(1,1),2) + 2*d[21]*F(0,0)*F(1,0)*pow(F(1,1),2) + 2*d[9]*F(0,1)*F(1,0)*pow(F(1,1),2) + d[19]*F(0,1)*F(1,0)*pow(F(1,1),2) + 2*d[27]*F(0,2)*F(1,0)*pow(F(1,1),2) + d[31]*F(0,2)*F(1,0)*pow(F(1,1),2) + d[19]*F(0,0)*pow(F(1,1),3) + d[7]*F(0,1)*pow(F(1,1),3) + d[25]*F(0,2)*pow(F(1,1),3) + d[18]*pow(F(1,0),2)*(F(0,1)*F(1,0) + F(0,0)*F(1,1)) + 2*d[5]*F(0,0)*pow(F(1,0),2)*F(1,2) + d[30]*F(0,0)*pow(F(1,0),2)*F(1,2) + 2*d[23]*F(0,1)*pow(F(1,0),2)*F(1,2) + d[24]*F(0,1)*pow(F(1,0),2)*F(1,2) + d[12]*F(0,2)*pow(F(1,0),2)*F(1,2) + 2*d[35]*F(0,2)*pow(F(1,0),2)*F(1,2) + 2*d[4]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 2*d[23]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 2*d[33]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 2*d[11]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[22]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[27]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[15]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + 2*d[29]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + 2*d[34]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + 2*d[22]*F(0,0)*pow(F(1,1),2)*F(1,2) + d[31]*F(0,0)*pow(F(1,1),2)*F(1,2) + 2*d[10]*F(0,1)*pow(F(1,1),2)*F(1,2) + d[25]*F(0,1)*pow(F(1,1),2)*F(1,2) + d[13]*F(0,2)*pow(F(1,1),2)*F(1,2) + 2*d[28]*F(0,2)*pow(F(1,1),2)*F(1,2) + d[2]*F(0,0)*F(1,0)*pow(F(1,2),2) + 2*d[35]*F(0,0)*F(1,0)*pow(F(1,2),2) + d[20]*F(0,1)*F(1,0)*pow(F(1,2),2) + 2*d[29]*F(0,1)*F(1,0)*pow(F(1,2),2) + 2*d[17]*F(0,2)*F(1,0)*pow(F(1,2),2) + d[32]*F(0,2)*F(1,0)*pow(F(1,2),2) + d[20]*F(0,0)*F(1,1)*pow(F(1,2),2) + 2*d[34]*F(0,0)*F(1,1)*pow(F(1,2),2) + d[8]*F(0,1)*F(1,1)*pow(F(1,2),2) + 2*d[28]*F(0,1)*F(1,1)*pow(F(1,2),2) + 2*d[16]*F(0,2)*F(1,1)*pow(F(1,2),2) + d[26]*F(0,2)*F(1,1)*pow(F(1,2),2) + d[32]*F(0,0)*pow(F(1,2),3) + d[26]*F(0,1)*pow(F(1,2),3) + d[14]*F(0,2)*pow(F(1,2),3);\n\n    c.d[20] = d[0]*F(0,0)*F(1,0)*pow(F(2,0),2) + d[30]*F(0,2)*F(1,0)*pow(F(2,0),2) + d[6]*F(0,1)*F(1,1)*pow(F(2,0),2) + d[24]*F(0,2)*F(1,1)*pow(F(2,0),2) + d[18]*(F(0,1)*F(1,0) + F(0,0)*F(1,1))*pow(F(2,0),2) + d[30]*F(0,0)*F(1,2)*pow(F(2,0),2) + d[24]*F(0,1)*F(1,2)*pow(F(2,0),2) + d[12]*F(0,2)*F(1,2)*pow(F(2,0),2) + 2*d[3]*F(0,0)*F(1,0)*F(2,0)*F(2,1) + 2*d[21]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + 2*d[33]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + 2*d[21]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[9]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + 2*d[27]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + 2*d[33]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + 2*d[27]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + 2*d[15]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[1]*F(0,0)*F(1,0)*pow(F(2,1),2) + d[19]*F(0,1)*F(1,0)*pow(F(2,1),2) + d[31]*F(0,2)*F(1,0)*pow(F(2,1),2) + d[19]*F(0,0)*F(1,1)*pow(F(2,1),2) + d[7]*F(0,1)*F(1,1)*pow(F(2,1),2) + d[25]*F(0,2)*F(1,1)*pow(F(2,1),2) + d[31]*F(0,0)*F(1,2)*pow(F(2,1),2) + d[25]*F(0,1)*F(1,2)*pow(F(2,1),2) + d[13]*F(0,2)*F(1,2)*pow(F(2,1),2) + 2*d[5]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + 2*d[23]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + 2*d[35]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + 2*d[23]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + 2*d[11]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + 2*d[29]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + 2*d[35]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + 2*d[29]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + 2*d[17]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + 2*d[4]*F(0,0)*F(1,0)*F(2,1)*F(2,2) + 2*d[22]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + 2*d[34]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + 2*d[22]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + 2*d[10]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + 2*d[28]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + 2*d[34]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + 2*d[28]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[16]*F(0,2)*F(1,2)*F(2,1)*F(2,2) + d[2]*F(0,0)*F(1,0)*pow(F(2,2),2) + d[20]*F(0,1)*F(1,0)*pow(F(2,2),2) + d[32]*F(0,2)*F(1,0)*pow(F(2,2),2) + d[20]*F(0,0)*F(1,1)*pow(F(2,2),2) + d[8]*F(0,1)*F(1,1)*pow(F(2,2),2) + d[26]*F(0,2)*F(1,1)*pow(F(2,2),2) + d[32]*F(0,0)*F(1,2)*pow(F(2,2),2) + d[26]*F(0,1)*F(1,2)*pow(F(2,2),2) + d[14]*F(0,2)*F(1,2)*pow(F(2,2),2);\n\n    c.d[21] = d[0]*pow(F(0,0),2)*pow(F(1,0),2) + d[18]*F(0,0)*F(0,1)*pow(F(1,0),2) + d[21]*pow(F(0,1),2)*pow(F(1,0),2) + d[5]*F(0,0)*F(0,2)*pow(F(1,0),2) + d[30]*F(0,0)*F(0,2)*pow(F(1,0),2) + d[23]*F(0,1)*F(0,2)*pow(F(1,0),2) + d[33]*F(0,1)*F(0,2)*pow(F(1,0),2) + d[35]*pow(F(0,2),2)*pow(F(1,0),2) + d[18]*pow(F(0,0),2)*F(1,0)*F(1,1) + d[1]*F(0,0)*F(0,1)*F(1,0)*F(1,1) + d[6]*F(0,0)*F(0,1)*F(1,0)*F(1,1) + 2*d[21]*F(0,0)*F(0,1)*F(1,0)*F(1,1) + d[9]*pow(F(0,1),2)*F(1,0)*F(1,1) + d[19]*pow(F(0,1),2)*F(1,0)*F(1,1) + d[4]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + d[23]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + d[24]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + d[33]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + d[11]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + d[22]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + d[27]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + d[31]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + d[29]*pow(F(0,2),2)*F(1,0)*F(1,1) + d[34]*pow(F(0,2),2)*F(1,0)*F(1,1) + d[21]*pow(F(0,0),2)*pow(F(1,1),2) + d[9]*F(0,0)*F(0,1)*pow(F(1,1),2) + d[19]*F(0,0)*F(0,1)*pow(F(1,1),2) + d[7]*pow(F(0,1),2)*pow(F(1,1),2) + d[22]*F(0,0)*F(0,2)*pow(F(1,1),2) + d[27]*F(0,0)*F(0,2)*pow(F(1,1),2) + d[10]*F(0,1)*F(0,2)*pow(F(1,1),2) + d[25]*F(0,1)*F(0,2)*pow(F(1,1),2) + d[28]*pow(F(0,2),2)*pow(F(1,1),2) + d[3]*F(0,0)*F(1,0)*(F(0,1)*F(1,0) + F(0,0)*F(1,1)) + d[5]*pow(F(0,0),2)*F(1,0)*F(1,2) + d[30]*pow(F(0,0),2)*F(1,0)*F(1,2) + d[4]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + d[23]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + d[24]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + d[33]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + d[22]*pow(F(0,1),2)*F(1,0)*F(1,2) + d[27]*pow(F(0,1),2)*F(1,0)*F(1,2) + d[2]*F(0,0)*F(0,2)*F(1,0)*F(1,2) + d[12]*F(0,0)*F(0,2)*F(1,0)*F(1,2) + 2*d[35]*F(0,0)*F(0,2)*F(1,0)*F(1,2) + d[15]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + d[20]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + d[29]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + d[34]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + d[17]*pow(F(0,2),2)*F(1,0)*F(1,2) + d[32]*pow(F(0,2),2)*F(1,0)*F(1,2) + d[23]*pow(F(0,0),2)*F(1,1)*F(1,2) + d[33]*pow(F(0,0),2)*F(1,1)*F(1,2) + d[11]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + d[22]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + d[27]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + d[31]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + d[10]*pow(F(0,1),2)*F(1,1)*F(1,2) + d[25]*pow(F(0,1),2)*F(1,1)*F(1,2) + d[15]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + d[20]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + d[29]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + d[34]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + d[8]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + d[13]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + 2*d[28]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + d[16]*pow(F(0,2),2)*F(1,1)*F(1,2) + d[26]*pow(F(0,2),2)*F(1,1)*F(1,2) + d[35]*pow(F(0,0),2)*pow(F(1,2),2) + d[29]*F(0,0)*F(0,1)*pow(F(1,2),2) + d[34]*F(0,0)*F(0,1)*pow(F(1,2),2) + d[28]*pow(F(0,1),2)*pow(F(1,2),2) + d[17]*F(0,0)*F(0,2)*pow(F(1,2),2) + d[32]*F(0,0)*F(0,2)*pow(F(1,2),2) + d[16]*F(0,1)*F(0,2)*pow(F(1,2),2) + d[26]*F(0,1)*F(0,2)*pow(F(1,2),2) + d[14]*pow(F(0,2),2)*pow(F(1,2),2);\n\n    c.d[22] = d[0]*F(0,0)*pow(F(1,0),2)*F(2,0) + d[30]*F(0,2)*pow(F(1,0),2)*F(2,0) + d[3]*F(0,0)*F(1,0)*F(1,1)*F(2,0) + d[6]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + d[21]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + d[24]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[33]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[21]*F(0,0)*pow(F(1,1),2)*F(2,0) + d[9]*F(0,1)*pow(F(1,1),2)*F(2,0) + d[27]*F(0,2)*pow(F(1,1),2)*F(2,0) + d[18]*F(1,0)*(F(0,1)*F(1,0) + F(0,0)*F(1,1))*F(2,0) + d[5]*F(0,0)*F(1,0)*F(1,2)*F(2,0) + d[30]*F(0,0)*F(1,0)*F(1,2)*F(2,0) + d[23]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + d[24]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + d[12]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + d[35]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + d[23]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + d[33]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + d[11]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + d[27]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + d[15]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[29]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[35]*F(0,0)*pow(F(1,2),2)*F(2,0) + d[29]*F(0,1)*pow(F(1,2),2)*F(2,0) + d[17]*F(0,2)*pow(F(1,2),2)*F(2,0) + d[3]*F(0,0)*pow(F(1,0),2)*F(2,1) + d[21]*F(0,1)*pow(F(1,0),2)*F(2,1) + d[33]*F(0,2)*pow(F(1,0),2)*F(2,1) + d[1]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + d[21]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + d[9]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + d[19]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + d[27]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[31]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[19]*F(0,0)*pow(F(1,1),2)*F(2,1) + d[7]*F(0,1)*pow(F(1,1),2)*F(2,1) + d[25]*F(0,2)*pow(F(1,1),2)*F(2,1) + d[4]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + d[33]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + d[22]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + d[27]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + d[15]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + d[34]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + d[22]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + d[31]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + d[10]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + d[25]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + d[13]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[28]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[34]*F(0,0)*pow(F(1,2),2)*F(2,1) + d[28]*F(0,1)*pow(F(1,2),2)*F(2,1) + d[16]*F(0,2)*pow(F(1,2),2)*F(2,1) + d[5]*F(0,0)*pow(F(1,0),2)*F(2,2) + d[23]*F(0,1)*pow(F(1,0),2)*F(2,2) + d[35]*F(0,2)*pow(F(1,0),2)*F(2,2) + d[4]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + d[23]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + d[11]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + d[22]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + d[29]*F(0,2)*F(1,0)*F(1,1)*F(2,2) + d[34]*F(0,2)*F(1,0)*F(1,1)*F(2,2) + d[22]*F(0,0)*pow(F(1,1),2)*F(2,2) + d[10]*F(0,1)*pow(F(1,1),2)*F(2,2) + d[28]*F(0,2)*pow(F(1,1),2)*F(2,2) + d[2]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + d[35]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + d[20]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + d[29]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + d[17]*F(0,2)*F(1,0)*F(1,2)*F(2,2) + d[32]*F(0,2)*F(1,0)*F(1,2)*F(2,2) + d[20]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + d[34]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + d[8]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + d[28]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + d[16]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[26]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[32]*F(0,0)*pow(F(1,2),2)*F(2,2) + d[26]*F(0,1)*pow(F(1,2),2)*F(2,2) + d[14]*F(0,2)*pow(F(1,2),2)*F(2,2);\n\n    c.d[23] = d[0]*pow(F(0,0),2)*F(1,0)*F(2,0) + d[18]*F(0,0)*F(0,1)*F(1,0)*F(2,0) + d[21]*pow(F(0,1),2)*F(1,0)*F(2,0) + d[5]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + d[30]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + d[23]*F(0,1)*F(0,2)*F(1,0)*F(2,0) + d[33]*F(0,1)*F(0,2)*F(1,0)*F(2,0) + d[35]*pow(F(0,2),2)*F(1,0)*F(2,0) + d[18]*pow(F(0,0),2)*F(1,1)*F(2,0) + d[6]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[21]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[9]*pow(F(0,1),2)*F(1,1)*F(2,0) + d[23]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + d[24]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + d[11]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[27]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[29]*pow(F(0,2),2)*F(1,1)*F(2,0) + d[30]*pow(F(0,0),2)*F(1,2)*F(2,0) + d[24]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[33]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[27]*pow(F(0,1),2)*F(1,2)*F(2,0) + d[12]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + d[35]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + d[15]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[29]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[17]*pow(F(0,2),2)*F(1,2)*F(2,0) + d[1]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[21]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[19]*pow(F(0,1),2)*F(1,0)*F(2,1) + d[4]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + d[33]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + d[22]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[31]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[34]*pow(F(0,2),2)*F(1,0)*F(2,1) + d[21]*pow(F(0,0),2)*F(1,1)*F(2,1) + d[9]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[19]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[7]*pow(F(0,1),2)*F(1,1)*F(2,1) + d[22]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + d[27]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + d[10]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[25]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[28]*pow(F(0,2),2)*F(1,1)*F(2,1) + d[33]*pow(F(0,0),2)*F(1,2)*F(2,1) + d[27]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[31]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[25]*pow(F(0,1),2)*F(1,2)*F(2,1) + d[15]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + d[34]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + d[13]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[28]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[16]*pow(F(0,2),2)*F(1,2)*F(2,1) + d[3]*F(0,0)*F(1,0)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*pow(F(0,0),2)*F(1,0)*F(2,2) + d[4]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[23]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[22]*pow(F(0,1),2)*F(1,0)*F(2,2) + d[2]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + d[35]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + d[20]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[34]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[32]*pow(F(0,2),2)*F(1,0)*F(2,2) + d[23]*pow(F(0,0),2)*F(1,1)*F(2,2) + d[11]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[22]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[10]*pow(F(0,1),2)*F(1,1)*F(2,2) + d[20]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + d[29]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + d[8]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[28]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[26]*pow(F(0,2),2)*F(1,1)*F(2,2) + d[35]*pow(F(0,0),2)*F(1,2)*F(2,2) + d[29]*F(0,0)*F(0,1)*F(1,2)*F(2,2) + d[34]*F(0,0)*F(0,1)*F(1,2)*F(2,2) + d[28]*pow(F(0,1),2)*F(1,2)*F(2,2) + d[17]*F(0,0)*F(0,2)*F(1,2)*F(2,2) + d[32]*F(0,0)*F(0,2)*F(1,2)*F(2,2) + d[16]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[26]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[14]*pow(F(0,2),2)*F(1,2)*F(2,2);\n\n    c.d[24] = d[0]*pow(F(0,0),2)*F(1,0)*F(2,0) + 2*d[3]*F(0,0)*F(0,1)*F(1,0)*F(2,0) + d[1]*pow(F(0,1),2)*F(1,0)*F(2,0) + 2*d[5]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + 2*d[4]*F(0,1)*F(0,2)*F(1,0)*F(2,0) + d[2]*pow(F(0,2),2)*F(1,0)*F(2,0) + d[18]*pow(F(0,0),2)*F(1,1)*F(2,0) + 2*d[21]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[19]*pow(F(0,1),2)*F(1,1)*F(2,0) + 2*d[23]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + 2*d[22]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[20]*pow(F(0,2),2)*F(1,1)*F(2,0) + d[30]*pow(F(0,0),2)*F(1,2)*F(2,0) + 2*d[33]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[31]*pow(F(0,1),2)*F(1,2)*F(2,0) + 2*d[35]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + 2*d[34]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[32]*pow(F(0,2),2)*F(1,2)*F(2,0) + d[18]*pow(F(0,0),2)*F(1,0)*F(2,1) + 2*d[21]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[19]*pow(F(0,1),2)*F(1,0)*F(2,1) + 2*d[23]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + 2*d[22]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[20]*pow(F(0,2),2)*F(1,0)*F(2,1) + d[6]*pow(F(0,0),2)*F(1,1)*F(2,1) + 2*d[9]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[7]*pow(F(0,1),2)*F(1,1)*F(2,1) + 2*d[11]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + 2*d[10]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[8]*pow(F(0,2),2)*F(1,1)*F(2,1) + d[24]*pow(F(0,0),2)*F(1,2)*F(2,1) + 2*d[27]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[25]*pow(F(0,1),2)*F(1,2)*F(2,1) + 2*d[29]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + 2*d[28]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[26]*pow(F(0,2),2)*F(1,2)*F(2,1) + d[30]*pow(F(0,0),2)*F(1,0)*F(2,2) + 2*d[33]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[31]*pow(F(0,1),2)*F(1,0)*F(2,2) + 2*d[35]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + 2*d[34]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[32]*pow(F(0,2),2)*F(1,0)*F(2,2) + d[24]*pow(F(0,0),2)*F(1,1)*F(2,2) + 2*d[27]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[25]*pow(F(0,1),2)*F(1,1)*F(2,2) + 2*d[29]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + 2*d[28]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[26]*pow(F(0,2),2)*F(1,1)*F(2,2) + d[12]*pow(F(0,0),2)*F(1,2)*F(2,2) + 2*d[15]*F(0,0)*F(0,1)*F(1,2)*F(2,2) + d[13]*pow(F(0,1),2)*F(1,2)*F(2,2) + 2*d[17]*F(0,0)*F(0,2)*F(1,2)*F(2,2) + 2*d[16]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[14]*pow(F(0,2),2)*F(1,2)*F(2,2);\n\n    c.d[25] = d[0]*pow(F(1,0),3)*F(2,0) + 2*d[3]*pow(F(1,0),2)*F(1,1)*F(2,0) + d[18]*pow(F(1,0),2)*F(1,1)*F(2,0) + d[1]*F(1,0)*pow(F(1,1),2)*F(2,0) + 2*d[21]*F(1,0)*pow(F(1,1),2)*F(2,0) + d[19]*pow(F(1,1),3)*F(2,0) + 2*d[5]*pow(F(1,0),2)*F(1,2)*F(2,0) + d[30]*pow(F(1,0),2)*F(1,2)*F(2,0) + 2*d[4]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[23]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[33]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[22]*pow(F(1,1),2)*F(1,2)*F(2,0) + d[31]*pow(F(1,1),2)*F(1,2)*F(2,0) + d[2]*F(1,0)*pow(F(1,2),2)*F(2,0) + 2*d[35]*F(1,0)*pow(F(1,2),2)*F(2,0) + d[20]*F(1,1)*pow(F(1,2),2)*F(2,0) + 2*d[34]*F(1,1)*pow(F(1,2),2)*F(2,0) + d[32]*pow(F(1,2),3)*F(2,0) + d[18]*pow(F(1,0),3)*F(2,1) + d[6]*pow(F(1,0),2)*F(1,1)*F(2,1) + 2*d[21]*pow(F(1,0),2)*F(1,1)*F(2,1) + 2*d[9]*F(1,0)*pow(F(1,1),2)*F(2,1) + d[19]*F(1,0)*pow(F(1,1),2)*F(2,1) + d[7]*pow(F(1,1),3)*F(2,1) + 2*d[23]*pow(F(1,0),2)*F(1,2)*F(2,1) + d[24]*pow(F(1,0),2)*F(1,2)*F(2,1) + 2*d[11]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[22]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[27]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[10]*pow(F(1,1),2)*F(1,2)*F(2,1) + d[25]*pow(F(1,1),2)*F(1,2)*F(2,1) + d[20]*F(1,0)*pow(F(1,2),2)*F(2,1) + 2*d[29]*F(1,0)*pow(F(1,2),2)*F(2,1) + d[8]*F(1,1)*pow(F(1,2),2)*F(2,1) + 2*d[28]*F(1,1)*pow(F(1,2),2)*F(2,1) + d[26]*pow(F(1,2),3)*F(2,1) + d[30]*pow(F(1,0),3)*F(2,2) + d[24]*pow(F(1,0),2)*F(1,1)*F(2,2) + 2*d[33]*pow(F(1,0),2)*F(1,1)*F(2,2) + 2*d[27]*F(1,0)*pow(F(1,1),2)*F(2,2) + d[31]*F(1,0)*pow(F(1,1),2)*F(2,2) + d[25]*pow(F(1,1),3)*F(2,2) + d[12]*pow(F(1,0),2)*F(1,2)*F(2,2) + 2*d[35]*pow(F(1,0),2)*F(1,2)*F(2,2) + 2*d[15]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[29]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[34]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + d[13]*pow(F(1,1),2)*F(1,2)*F(2,2) + 2*d[28]*pow(F(1,1),2)*F(1,2)*F(2,2) + 2*d[17]*F(1,0)*pow(F(1,2),2)*F(2,2) + d[32]*F(1,0)*pow(F(1,2),2)*F(2,2) + 2*d[16]*F(1,1)*pow(F(1,2),2)*F(2,2) + d[26]*F(1,1)*pow(F(1,2),2)*F(2,2) + d[14]*pow(F(1,2),3)*F(2,2);\n\n    c.d[26] = d[0]*F(1,0)*pow(F(2,0),3) + d[30]*F(1,2)*pow(F(2,0),3) + 2*d[3]*F(1,0)*pow(F(2,0),2)*F(2,1) + d[6]*F(1,1)*pow(F(2,0),2)*F(2,1) + 2*d[21]*F(1,1)*pow(F(2,0),2)*F(2,1) + d[24]*F(1,2)*pow(F(2,0),2)*F(2,1) + 2*d[33]*F(1,2)*pow(F(2,0),2)*F(2,1) + d[1]*F(1,0)*F(2,0)*pow(F(2,1),2) + 2*d[21]*F(1,0)*F(2,0)*pow(F(2,1),2) + 2*d[9]*F(1,1)*F(2,0)*pow(F(2,1),2) + d[19]*F(1,1)*F(2,0)*pow(F(2,1),2) + 2*d[27]*F(1,2)*F(2,0)*pow(F(2,1),2) + d[31]*F(1,2)*F(2,0)*pow(F(2,1),2) + d[19]*F(1,0)*pow(F(2,1),3) + d[7]*F(1,1)*pow(F(2,1),3) + d[25]*F(1,2)*pow(F(2,1),3) + d[18]*pow(F(2,0),2)*(F(1,1)*F(2,0) + F(1,0)*F(2,1)) + 2*d[5]*F(1,0)*pow(F(2,0),2)*F(2,2) + d[30]*F(1,0)*pow(F(2,0),2)*F(2,2) + 2*d[23]*F(1,1)*pow(F(2,0),2)*F(2,2) + d[24]*F(1,1)*pow(F(2,0),2)*F(2,2) + d[12]*F(1,2)*pow(F(2,0),2)*F(2,2) + 2*d[35]*F(1,2)*pow(F(2,0),2)*F(2,2) + 2*d[4]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[23]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[33]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[11]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[22]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[27]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[15]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[29]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[34]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[22]*F(1,0)*pow(F(2,1),2)*F(2,2) + d[31]*F(1,0)*pow(F(2,1),2)*F(2,2) + 2*d[10]*F(1,1)*pow(F(2,1),2)*F(2,2) + d[25]*F(1,1)*pow(F(2,1),2)*F(2,2) + d[13]*F(1,2)*pow(F(2,1),2)*F(2,2) + 2*d[28]*F(1,2)*pow(F(2,1),2)*F(2,2) + d[2]*F(1,0)*F(2,0)*pow(F(2,2),2) + 2*d[35]*F(1,0)*F(2,0)*pow(F(2,2),2) + d[20]*F(1,1)*F(2,0)*pow(F(2,2),2) + 2*d[29]*F(1,1)*F(2,0)*pow(F(2,2),2) + 2*d[17]*F(1,2)*F(2,0)*pow(F(2,2),2) + d[32]*F(1,2)*F(2,0)*pow(F(2,2),2) + d[20]*F(1,0)*F(2,1)*pow(F(2,2),2) + 2*d[34]*F(1,0)*F(2,1)*pow(F(2,2),2) + d[8]*F(1,1)*F(2,1)*pow(F(2,2),2) + 2*d[28]*F(1,1)*F(2,1)*pow(F(2,2),2) + 2*d[16]*F(1,2)*F(2,1)*pow(F(2,2),2) + d[26]*F(1,2)*F(2,1)*pow(F(2,2),2) + d[32]*F(1,0)*pow(F(2,2),3) + d[26]*F(1,1)*pow(F(2,2),3) + d[14]*F(1,2)*pow(F(2,2),3);\n\n    c.d[27] = d[0]*F(0,0)*pow(F(1,0),2)*F(2,0) + d[5]*F(0,2)*pow(F(1,0),2)*F(2,0) + d[18]*F(0,0)*F(1,0)*F(1,1)*F(2,0) + d[1]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + d[21]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + d[4]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[23]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[21]*F(0,0)*pow(F(1,1),2)*F(2,0) + d[19]*F(0,1)*pow(F(1,1),2)*F(2,0) + d[22]*F(0,2)*pow(F(1,1),2)*F(2,0) + d[3]*F(1,0)*(F(0,1)*F(1,0) + F(0,0)*F(1,1))*F(2,0) + d[5]*F(0,0)*F(1,0)*F(1,2)*F(2,0) + d[30]*F(0,0)*F(1,0)*F(1,2)*F(2,0) + d[4]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + d[33]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + d[2]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + d[35]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + d[23]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + d[33]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + d[22]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + d[31]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + d[20]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[34]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[35]*F(0,0)*pow(F(1,2),2)*F(2,0) + d[34]*F(0,1)*pow(F(1,2),2)*F(2,0) + d[32]*F(0,2)*pow(F(1,2),2)*F(2,0) + d[18]*F(0,0)*pow(F(1,0),2)*F(2,1) + d[21]*F(0,1)*pow(F(1,0),2)*F(2,1) + d[23]*F(0,2)*pow(F(1,0),2)*F(2,1) + d[6]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + d[21]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + d[9]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + d[19]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + d[11]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[22]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[9]*F(0,0)*pow(F(1,1),2)*F(2,1) + d[7]*F(0,1)*pow(F(1,1),2)*F(2,1) + d[10]*F(0,2)*pow(F(1,1),2)*F(2,1) + d[23]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + d[24]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + d[22]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + d[27]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + d[20]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + d[29]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + d[11]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + d[27]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + d[10]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + d[25]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + d[8]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[28]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[29]*F(0,0)*pow(F(1,2),2)*F(2,1) + d[28]*F(0,1)*pow(F(1,2),2)*F(2,1) + d[26]*F(0,2)*pow(F(1,2),2)*F(2,1) + d[30]*F(0,0)*pow(F(1,0),2)*F(2,2) + d[33]*F(0,1)*pow(F(1,0),2)*F(2,2) + d[35]*F(0,2)*pow(F(1,0),2)*F(2,2) + d[24]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + d[33]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + d[27]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + d[31]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + d[29]*F(0,2)*F(1,0)*F(1,1)*F(2,2) + d[34]*F(0,2)*F(1,0)*F(1,1)*F(2,2) + d[27]*F(0,0)*pow(F(1,1),2)*F(2,2) + d[25]*F(0,1)*pow(F(1,1),2)*F(2,2) + d[28]*F(0,2)*pow(F(1,1),2)*F(2,2) + d[12]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + d[35]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + d[15]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + d[34]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + d[17]*F(0,2)*F(1,0)*F(1,2)*F(2,2) + d[32]*F(0,2)*F(1,0)*F(1,2)*F(2,2) + d[15]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + d[29]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + d[13]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + d[28]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + d[16]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[26]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[17]*F(0,0)*pow(F(1,2),2)*F(2,2) + d[16]*F(0,1)*pow(F(1,2),2)*F(2,2) + d[14]*F(0,2)*pow(F(1,2),2)*F(2,2);\n\n    c.d[28] = d[0]*pow(F(1,0),2)*pow(F(2,0),2) + d[18]*F(1,0)*F(1,1)*pow(F(2,0),2) + d[21]*pow(F(1,1),2)*pow(F(2,0),2) + d[5]*F(1,0)*F(1,2)*pow(F(2,0),2) + d[30]*F(1,0)*F(1,2)*pow(F(2,0),2) + d[23]*F(1,1)*F(1,2)*pow(F(2,0),2) + d[33]*F(1,1)*F(1,2)*pow(F(2,0),2) + d[35]*pow(F(1,2),2)*pow(F(2,0),2) + d[18]*pow(F(1,0),2)*F(2,0)*F(2,1) + d[1]*F(1,0)*F(1,1)*F(2,0)*F(2,1) + d[6]*F(1,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[21]*F(1,0)*F(1,1)*F(2,0)*F(2,1) + d[9]*pow(F(1,1),2)*F(2,0)*F(2,1) + d[19]*pow(F(1,1),2)*F(2,0)*F(2,1) + d[4]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + d[23]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + d[24]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + d[33]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + d[11]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + d[22]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + d[27]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + d[31]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + d[29]*pow(F(1,2),2)*F(2,0)*F(2,1) + d[34]*pow(F(1,2),2)*F(2,0)*F(2,1) + d[21]*pow(F(1,0),2)*pow(F(2,1),2) + d[9]*F(1,0)*F(1,1)*pow(F(2,1),2) + d[19]*F(1,0)*F(1,1)*pow(F(2,1),2) + d[7]*pow(F(1,1),2)*pow(F(2,1),2) + d[22]*F(1,0)*F(1,2)*pow(F(2,1),2) + d[27]*F(1,0)*F(1,2)*pow(F(2,1),2) + d[10]*F(1,1)*F(1,2)*pow(F(2,1),2) + d[25]*F(1,1)*F(1,2)*pow(F(2,1),2) + d[28]*pow(F(1,2),2)*pow(F(2,1),2) + d[3]*F(1,0)*F(2,0)*(F(1,1)*F(2,0) + F(1,0)*F(2,1)) + d[5]*pow(F(1,0),2)*F(2,0)*F(2,2) + d[30]*pow(F(1,0),2)*F(2,0)*F(2,2) + d[4]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + d[23]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + d[24]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + d[33]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + d[22]*pow(F(1,1),2)*F(2,0)*F(2,2) + d[27]*pow(F(1,1),2)*F(2,0)*F(2,2) + d[2]*F(1,0)*F(1,2)*F(2,0)*F(2,2) + d[12]*F(1,0)*F(1,2)*F(2,0)*F(2,2) + 2*d[35]*F(1,0)*F(1,2)*F(2,0)*F(2,2) + d[15]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + d[20]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + d[29]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + d[34]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + d[17]*pow(F(1,2),2)*F(2,0)*F(2,2) + d[32]*pow(F(1,2),2)*F(2,0)*F(2,2) + d[23]*pow(F(1,0),2)*F(2,1)*F(2,2) + d[33]*pow(F(1,0),2)*F(2,1)*F(2,2) + d[11]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + d[22]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + d[27]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + d[31]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + d[10]*pow(F(1,1),2)*F(2,1)*F(2,2) + d[25]*pow(F(1,1),2)*F(2,1)*F(2,2) + d[15]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + d[20]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + d[29]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + d[34]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + d[8]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + d[13]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[28]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + d[16]*pow(F(1,2),2)*F(2,1)*F(2,2) + d[26]*pow(F(1,2),2)*F(2,1)*F(2,2) + d[35]*pow(F(1,0),2)*pow(F(2,2),2) + d[29]*F(1,0)*F(1,1)*pow(F(2,2),2) + d[34]*F(1,0)*F(1,1)*pow(F(2,2),2) + d[28]*pow(F(1,1),2)*pow(F(2,2),2) + d[17]*F(1,0)*F(1,2)*pow(F(2,2),2) + d[32]*F(1,0)*F(1,2)*pow(F(2,2),2) + d[16]*F(1,1)*F(1,2)*pow(F(2,2),2) + d[26]*F(1,1)*F(1,2)*pow(F(2,2),2) + d[14]*pow(F(1,2),2)*pow(F(2,2),2);\n\n    c.d[29] = d[0]*F(0,0)*F(1,0)*pow(F(2,0),2) + d[5]*F(0,2)*F(1,0)*pow(F(2,0),2) + d[18]*F(0,0)*F(1,1)*pow(F(2,0),2) + d[21]*F(0,1)*F(1,1)*pow(F(2,0),2) + d[23]*F(0,2)*F(1,1)*pow(F(2,0),2) + d[30]*F(0,0)*F(1,2)*pow(F(2,0),2) + d[33]*F(0,1)*F(1,2)*pow(F(2,0),2) + d[35]*F(0,2)*F(1,2)*pow(F(2,0),2) + d[18]*F(0,0)*F(1,0)*F(2,0)*F(2,1) + d[1]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + d[21]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + d[4]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + d[23]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + d[6]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + d[21]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + d[9]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + d[19]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + d[11]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + d[22]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + d[24]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + d[33]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + d[27]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + d[31]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + d[29]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[34]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[21]*F(0,0)*F(1,0)*pow(F(2,1),2) + d[19]*F(0,1)*F(1,0)*pow(F(2,1),2) + d[22]*F(0,2)*F(1,0)*pow(F(2,1),2) + d[9]*F(0,0)*F(1,1)*pow(F(2,1),2) + d[7]*F(0,1)*F(1,1)*pow(F(2,1),2) + d[10]*F(0,2)*F(1,1)*pow(F(2,1),2) + d[27]*F(0,0)*F(1,2)*pow(F(2,1),2) + d[25]*F(0,1)*F(1,2)*pow(F(2,1),2) + d[28]*F(0,2)*F(1,2)*pow(F(2,1),2) + d[3]*F(1,0)*F(2,0)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + d[30]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + d[4]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + d[33]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + d[2]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + d[35]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + d[23]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + d[24]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + d[22]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + d[27]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + d[20]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + d[29]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + d[12]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + d[35]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + d[15]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + d[34]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + d[17]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + d[32]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + d[23]*F(0,0)*F(1,0)*F(2,1)*F(2,2) + d[33]*F(0,0)*F(1,0)*F(2,1)*F(2,2) + d[22]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + d[31]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + d[20]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + d[34]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + d[11]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + d[27]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + d[10]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + d[25]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + d[8]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + d[28]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + d[15]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + d[29]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + d[13]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + d[28]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + d[16]*F(0,2)*F(1,2)*F(2,1)*F(2,2) + d[26]*F(0,2)*F(1,2)*F(2,1)*F(2,2) + d[35]*F(0,0)*F(1,0)*pow(F(2,2),2) + d[34]*F(0,1)*F(1,0)*pow(F(2,2),2) + d[32]*F(0,2)*F(1,0)*pow(F(2,2),2) + d[29]*F(0,0)*F(1,1)*pow(F(2,2),2) + d[28]*F(0,1)*F(1,1)*pow(F(2,2),2) + d[26]*F(0,2)*F(1,1)*pow(F(2,2),2) + d[17]*F(0,0)*F(1,2)*pow(F(2,2),2) + d[16]*F(0,1)*F(1,2)*pow(F(2,2),2) + d[14]*F(0,2)*F(1,2)*pow(F(2,2),2);\n\n    c.d[30] = d[0]*pow(F(0,0),3)*F(2,0) + 2*d[3]*pow(F(0,0),2)*F(0,1)*F(2,0) + d[18]*pow(F(0,0),2)*F(0,1)*F(2,0) + d[1]*F(0,0)*pow(F(0,1),2)*F(2,0) + 2*d[21]*F(0,0)*pow(F(0,1),2)*F(2,0) + d[19]*pow(F(0,1),3)*F(2,0) + 2*d[5]*pow(F(0,0),2)*F(0,2)*F(2,0) + d[30]*pow(F(0,0),2)*F(0,2)*F(2,0) + 2*d[4]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + 2*d[23]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + 2*d[33]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + 2*d[22]*pow(F(0,1),2)*F(0,2)*F(2,0) + d[31]*pow(F(0,1),2)*F(0,2)*F(2,0) + d[2]*F(0,0)*pow(F(0,2),2)*F(2,0) + 2*d[35]*F(0,0)*pow(F(0,2),2)*F(2,0) + d[20]*F(0,1)*pow(F(0,2),2)*F(2,0) + 2*d[34]*F(0,1)*pow(F(0,2),2)*F(2,0) + d[32]*pow(F(0,2),3)*F(2,0) + d[18]*pow(F(0,0),3)*F(2,1) + d[6]*pow(F(0,0),2)*F(0,1)*F(2,1) + 2*d[21]*pow(F(0,0),2)*F(0,1)*F(2,1) + 2*d[9]*F(0,0)*pow(F(0,1),2)*F(2,1) + d[19]*F(0,0)*pow(F(0,1),2)*F(2,1) + d[7]*pow(F(0,1),3)*F(2,1) + 2*d[23]*pow(F(0,0),2)*F(0,2)*F(2,1) + d[24]*pow(F(0,0),2)*F(0,2)*F(2,1) + 2*d[11]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + 2*d[22]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + 2*d[27]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + 2*d[10]*pow(F(0,1),2)*F(0,2)*F(2,1) + d[25]*pow(F(0,1),2)*F(0,2)*F(2,1) + d[20]*F(0,0)*pow(F(0,2),2)*F(2,1) + 2*d[29]*F(0,0)*pow(F(0,2),2)*F(2,1) + d[8]*F(0,1)*pow(F(0,2),2)*F(2,1) + 2*d[28]*F(0,1)*pow(F(0,2),2)*F(2,1) + d[26]*pow(F(0,2),3)*F(2,1) + d[30]*pow(F(0,0),3)*F(2,2) + d[24]*pow(F(0,0),2)*F(0,1)*F(2,2) + 2*d[33]*pow(F(0,0),2)*F(0,1)*F(2,2) + 2*d[27]*F(0,0)*pow(F(0,1),2)*F(2,2) + d[31]*F(0,0)*pow(F(0,1),2)*F(2,2) + d[25]*pow(F(0,1),3)*F(2,2) + d[12]*pow(F(0,0),2)*F(0,2)*F(2,2) + 2*d[35]*pow(F(0,0),2)*F(0,2)*F(2,2) + 2*d[15]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + 2*d[29]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + 2*d[34]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + d[13]*pow(F(0,1),2)*F(0,2)*F(2,2) + 2*d[28]*pow(F(0,1),2)*F(0,2)*F(2,2) + 2*d[17]*F(0,0)*pow(F(0,2),2)*F(2,2) + d[32]*F(0,0)*pow(F(0,2),2)*F(2,2) + 2*d[16]*F(0,1)*pow(F(0,2),2)*F(2,2) + d[26]*F(0,1)*pow(F(0,2),2)*F(2,2) + d[14]*pow(F(0,2),3)*F(2,2);\n\n    c.d[31] = d[0]*F(0,0)*pow(F(1,0),2)*F(2,0) + d[30]*F(0,2)*pow(F(1,0),2)*F(2,0) + 2*d[3]*F(0,0)*F(1,0)*F(1,1)*F(2,0) + 2*d[21]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + 2*d[33]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[1]*F(0,0)*pow(F(1,1),2)*F(2,0) + d[19]*F(0,1)*pow(F(1,1),2)*F(2,0) + d[31]*F(0,2)*pow(F(1,1),2)*F(2,0) + 2*d[5]*F(0,0)*F(1,0)*F(1,2)*F(2,0) + 2*d[23]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + 2*d[35]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + 2*d[4]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[22]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + 2*d[34]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[2]*F(0,0)*pow(F(1,2),2)*F(2,0) + d[20]*F(0,1)*pow(F(1,2),2)*F(2,0) + d[32]*F(0,2)*pow(F(1,2),2)*F(2,0) + d[6]*F(0,1)*pow(F(1,0),2)*F(2,1) + d[24]*F(0,2)*pow(F(1,0),2)*F(2,1) + 2*d[21]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + 2*d[9]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + 2*d[27]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[19]*F(0,0)*pow(F(1,1),2)*F(2,1) + d[7]*F(0,1)*pow(F(1,1),2)*F(2,1) + d[25]*F(0,2)*pow(F(1,1),2)*F(2,1) + 2*d[23]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + 2*d[11]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + 2*d[29]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + 2*d[22]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[10]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + 2*d[28]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[20]*F(0,0)*pow(F(1,2),2)*F(2,1) + d[8]*F(0,1)*pow(F(1,2),2)*F(2,1) + d[26]*F(0,2)*pow(F(1,2),2)*F(2,1) + d[18]*pow(F(1,0),2)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[30]*F(0,0)*pow(F(1,0),2)*F(2,2) + d[24]*F(0,1)*pow(F(1,0),2)*F(2,2) + d[12]*F(0,2)*pow(F(1,0),2)*F(2,2) + 2*d[33]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + 2*d[27]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + 2*d[15]*F(0,2)*F(1,0)*F(1,1)*F(2,2) + d[31]*F(0,0)*pow(F(1,1),2)*F(2,2) + d[25]*F(0,1)*pow(F(1,1),2)*F(2,2) + d[13]*F(0,2)*pow(F(1,1),2)*F(2,2) + 2*d[35]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + 2*d[29]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + 2*d[17]*F(0,2)*F(1,0)*F(1,2)*F(2,2) + 2*d[34]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[28]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + 2*d[16]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[32]*F(0,0)*pow(F(1,2),2)*F(2,2) + d[26]*F(0,1)*pow(F(1,2),2)*F(2,2) + d[14]*F(0,2)*pow(F(1,2),2)*F(2,2);\n\n    c.d[32] = d[0]*F(0,0)*pow(F(2,0),3) + d[30]*F(0,2)*pow(F(2,0),3) + 2*d[3]*F(0,0)*pow(F(2,0),2)*F(2,1) + d[6]*F(0,1)*pow(F(2,0),2)*F(2,1) + 2*d[21]*F(0,1)*pow(F(2,0),2)*F(2,1) + d[24]*F(0,2)*pow(F(2,0),2)*F(2,1) + 2*d[33]*F(0,2)*pow(F(2,0),2)*F(2,1) + d[1]*F(0,0)*F(2,0)*pow(F(2,1),2) + 2*d[21]*F(0,0)*F(2,0)*pow(F(2,1),2) + 2*d[9]*F(0,1)*F(2,0)*pow(F(2,1),2) + d[19]*F(0,1)*F(2,0)*pow(F(2,1),2) + 2*d[27]*F(0,2)*F(2,0)*pow(F(2,1),2) + d[31]*F(0,2)*F(2,0)*pow(F(2,1),2) + d[19]*F(0,0)*pow(F(2,1),3) + d[7]*F(0,1)*pow(F(2,1),3) + d[25]*F(0,2)*pow(F(2,1),3) + d[18]*pow(F(2,0),2)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + 2*d[5]*F(0,0)*pow(F(2,0),2)*F(2,2) + d[30]*F(0,0)*pow(F(2,0),2)*F(2,2) + 2*d[23]*F(0,1)*pow(F(2,0),2)*F(2,2) + d[24]*F(0,1)*pow(F(2,0),2)*F(2,2) + d[12]*F(0,2)*pow(F(2,0),2)*F(2,2) + 2*d[35]*F(0,2)*pow(F(2,0),2)*F(2,2) + 2*d[4]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[23]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[33]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 2*d[11]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[22]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[27]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[15]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[29]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[34]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[22]*F(0,0)*pow(F(2,1),2)*F(2,2) + d[31]*F(0,0)*pow(F(2,1),2)*F(2,2) + 2*d[10]*F(0,1)*pow(F(2,1),2)*F(2,2) + d[25]*F(0,1)*pow(F(2,1),2)*F(2,2) + d[13]*F(0,2)*pow(F(2,1),2)*F(2,2) + 2*d[28]*F(0,2)*pow(F(2,1),2)*F(2,2) + d[2]*F(0,0)*F(2,0)*pow(F(2,2),2) + 2*d[35]*F(0,0)*F(2,0)*pow(F(2,2),2) + d[20]*F(0,1)*F(2,0)*pow(F(2,2),2) + 2*d[29]*F(0,1)*F(2,0)*pow(F(2,2),2) + 2*d[17]*F(0,2)*F(2,0)*pow(F(2,2),2) + d[32]*F(0,2)*F(2,0)*pow(F(2,2),2) + d[20]*F(0,0)*F(2,1)*pow(F(2,2),2) + 2*d[34]*F(0,0)*F(2,1)*pow(F(2,2),2) + d[8]*F(0,1)*F(2,1)*pow(F(2,2),2) + 2*d[28]*F(0,1)*F(2,1)*pow(F(2,2),2) + 2*d[16]*F(0,2)*F(2,1)*pow(F(2,2),2) + d[26]*F(0,2)*F(2,1)*pow(F(2,2),2) + d[32]*F(0,0)*pow(F(2,2),3) + d[26]*F(0,1)*pow(F(2,2),3) + d[14]*F(0,2)*pow(F(2,2),3);\n\n    c.d[33] = d[0]*pow(F(0,0),2)*F(1,0)*F(2,0) + d[18]*F(0,0)*F(0,1)*F(1,0)*F(2,0) + d[21]*pow(F(0,1),2)*F(1,0)*F(2,0) + d[5]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + d[30]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + d[23]*F(0,1)*F(0,2)*F(1,0)*F(2,0) + d[33]*F(0,1)*F(0,2)*F(1,0)*F(2,0) + d[35]*pow(F(0,2),2)*F(1,0)*F(2,0) + d[1]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[21]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[19]*pow(F(0,1),2)*F(1,1)*F(2,0) + d[4]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + d[33]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + d[22]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[31]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[34]*pow(F(0,2),2)*F(1,1)*F(2,0) + d[3]*F(0,0)*(F(0,1)*F(1,0) + F(0,0)*F(1,1))*F(2,0) + d[5]*pow(F(0,0),2)*F(1,2)*F(2,0) + d[4]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[23]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[22]*pow(F(0,1),2)*F(1,2)*F(2,0) + d[2]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + d[35]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + d[20]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[34]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[32]*pow(F(0,2),2)*F(1,2)*F(2,0) + d[18]*pow(F(0,0),2)*F(1,0)*F(2,1) + d[6]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[21]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[9]*pow(F(0,1),2)*F(1,0)*F(2,1) + d[23]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + d[24]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + d[11]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[27]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[29]*pow(F(0,2),2)*F(1,0)*F(2,1) + d[21]*pow(F(0,0),2)*F(1,1)*F(2,1) + d[9]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[19]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[7]*pow(F(0,1),2)*F(1,1)*F(2,1) + d[22]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + d[27]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + d[10]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[25]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[28]*pow(F(0,2),2)*F(1,1)*F(2,1) + d[23]*pow(F(0,0),2)*F(1,2)*F(2,1) + d[11]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[22]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[10]*pow(F(0,1),2)*F(1,2)*F(2,1) + d[20]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + d[29]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + d[8]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[28]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[26]*pow(F(0,2),2)*F(1,2)*F(2,1) + d[30]*pow(F(0,0),2)*F(1,0)*F(2,2) + d[24]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[33]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[27]*pow(F(0,1),2)*F(1,0)*F(2,2) + d[12]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + d[35]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + d[15]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[29]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[17]*pow(F(0,2),2)*F(1,0)*F(2,2) + d[33]*pow(F(0,0),2)*F(1,1)*F(2,2) + d[27]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[31]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[25]*pow(F(0,1),2)*F(1,1)*F(2,2) + d[15]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + d[34]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + d[13]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[28]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[16]*pow(F(0,2),2)*F(1,1)*F(2,2) + d[35]*pow(F(0,0),2)*F(1,2)*F(2,2) + d[29]*F(0,0)*F(0,1)*F(1,2)*F(2,2) + d[34]*F(0,0)*F(0,1)*F(1,2)*F(2,2) + d[28]*pow(F(0,1),2)*F(1,2)*F(2,2) + d[17]*F(0,0)*F(0,2)*F(1,2)*F(2,2) + d[32]*F(0,0)*F(0,2)*F(1,2)*F(2,2) + d[16]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[26]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[14]*pow(F(0,2),2)*F(1,2)*F(2,2);\n\n    c.d[34] = d[0]*F(0,0)*F(1,0)*pow(F(2,0),2) + d[30]*F(0,2)*F(1,0)*pow(F(2,0),2) + d[3]*F(0,0)*F(1,1)*pow(F(2,0),2) + d[21]*F(0,1)*F(1,1)*pow(F(2,0),2) + d[33]*F(0,2)*F(1,1)*pow(F(2,0),2) + d[5]*F(0,0)*F(1,2)*pow(F(2,0),2) + d[23]*F(0,1)*F(1,2)*pow(F(2,0),2) + d[35]*F(0,2)*F(1,2)*pow(F(2,0),2) + d[3]*F(0,0)*F(1,0)*F(2,0)*F(2,1) + d[6]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + d[21]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + d[24]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + d[33]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + d[1]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + d[21]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + d[9]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + d[19]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + d[27]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + d[31]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + d[4]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + d[23]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + d[11]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + d[22]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + d[29]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[34]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[21]*F(0,0)*F(1,0)*pow(F(2,1),2) + d[9]*F(0,1)*F(1,0)*pow(F(2,1),2) + d[27]*F(0,2)*F(1,0)*pow(F(2,1),2) + d[19]*F(0,0)*F(1,1)*pow(F(2,1),2) + d[7]*F(0,1)*F(1,1)*pow(F(2,1),2) + d[25]*F(0,2)*F(1,1)*pow(F(2,1),2) + d[22]*F(0,0)*F(1,2)*pow(F(2,1),2) + d[10]*F(0,1)*F(1,2)*pow(F(2,1),2) + d[28]*F(0,2)*F(1,2)*pow(F(2,1),2) + d[18]*F(1,0)*F(2,0)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + d[30]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + d[23]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + d[24]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + d[12]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + d[35]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + d[4]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + d[33]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + d[22]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + d[27]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + d[15]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + d[34]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + d[2]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + d[35]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + d[20]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + d[29]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + d[17]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + d[32]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + d[23]*F(0,0)*F(1,0)*F(2,1)*F(2,2) + d[33]*F(0,0)*F(1,0)*F(2,1)*F(2,2) + d[11]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + d[27]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + d[15]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + d[29]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + d[22]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + d[31]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + d[10]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + d[25]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + d[13]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + d[28]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + d[20]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + d[34]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + d[8]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + d[28]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + d[16]*F(0,2)*F(1,2)*F(2,1)*F(2,2) + d[26]*F(0,2)*F(1,2)*F(2,1)*F(2,2) + d[35]*F(0,0)*F(1,0)*pow(F(2,2),2) + d[29]*F(0,1)*F(1,0)*pow(F(2,2),2) + d[17]*F(0,2)*F(1,0)*pow(F(2,2),2) + d[34]*F(0,0)*F(1,1)*pow(F(2,2),2) + d[28]*F(0,1)*F(1,1)*pow(F(2,2),2) + d[16]*F(0,2)*F(1,1)*pow(F(2,2),2) + d[32]*F(0,0)*F(1,2)*pow(F(2,2),2) + d[26]*F(0,1)*F(1,2)*pow(F(2,2),2) + d[14]*F(0,2)*F(1,2)*pow(F(2,2),2);\n\n    c.d[35] = d[0]*pow(F(0,0),2)*pow(F(2,0),2) + d[18]*F(0,0)*F(0,1)*pow(F(2,0),2) + d[21]*pow(F(0,1),2)*pow(F(2,0),2) + d[5]*F(0,0)*F(0,2)*pow(F(2,0),2) + d[30]*F(0,0)*F(0,2)*pow(F(2,0),2) + d[23]*F(0,1)*F(0,2)*pow(F(2,0),2) + d[33]*F(0,1)*F(0,2)*pow(F(2,0),2) + d[35]*pow(F(0,2),2)*pow(F(2,0),2) + d[18]*pow(F(0,0),2)*F(2,0)*F(2,1) + d[1]*F(0,0)*F(0,1)*F(2,0)*F(2,1) + d[6]*F(0,0)*F(0,1)*F(2,0)*F(2,1) + 2*d[21]*F(0,0)*F(0,1)*F(2,0)*F(2,1) + d[9]*pow(F(0,1),2)*F(2,0)*F(2,1) + d[19]*pow(F(0,1),2)*F(2,0)*F(2,1) + d[4]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + d[23]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + d[24]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + d[33]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + d[11]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + d[22]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + d[27]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + d[31]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + d[29]*pow(F(0,2),2)*F(2,0)*F(2,1) + d[34]*pow(F(0,2),2)*F(2,0)*F(2,1) + d[21]*pow(F(0,0),2)*pow(F(2,1),2) + d[9]*F(0,0)*F(0,1)*pow(F(2,1),2) + d[19]*F(0,0)*F(0,1)*pow(F(2,1),2) + d[7]*pow(F(0,1),2)*pow(F(2,1),2) + d[22]*F(0,0)*F(0,2)*pow(F(2,1),2) + d[27]*F(0,0)*F(0,2)*pow(F(2,1),2) + d[10]*F(0,1)*F(0,2)*pow(F(2,1),2) + d[25]*F(0,1)*F(0,2)*pow(F(2,1),2) + d[28]*pow(F(0,2),2)*pow(F(2,1),2) + d[3]*F(0,0)*F(2,0)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) + d[5]*pow(F(0,0),2)*F(2,0)*F(2,2) + d[30]*pow(F(0,0),2)*F(2,0)*F(2,2) + d[4]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + d[23]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + d[24]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + d[33]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + d[22]*pow(F(0,1),2)*F(2,0)*F(2,2) + d[27]*pow(F(0,1),2)*F(2,0)*F(2,2) + d[2]*F(0,0)*F(0,2)*F(2,0)*F(2,2) + d[12]*F(0,0)*F(0,2)*F(2,0)*F(2,2) + 2*d[35]*F(0,0)*F(0,2)*F(2,0)*F(2,2) + d[15]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + d[20]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + d[29]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + d[34]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + d[17]*pow(F(0,2),2)*F(2,0)*F(2,2) + d[32]*pow(F(0,2),2)*F(2,0)*F(2,2) + d[23]*pow(F(0,0),2)*F(2,1)*F(2,2) + d[33]*pow(F(0,0),2)*F(2,1)*F(2,2) + d[11]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + d[22]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + d[27]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + d[31]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + d[10]*pow(F(0,1),2)*F(2,1)*F(2,2) + d[25]*pow(F(0,1),2)*F(2,1)*F(2,2) + d[15]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + d[20]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + d[29]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + d[34]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + d[8]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + d[13]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + 2*d[28]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + d[16]*pow(F(0,2),2)*F(2,1)*F(2,2) + d[26]*pow(F(0,2),2)*F(2,1)*F(2,2) + d[35]*pow(F(0,0),2)*pow(F(2,2),2) + d[29]*F(0,0)*F(0,1)*pow(F(2,2),2) + d[34]*F(0,0)*F(0,1)*pow(F(2,2),2) + d[28]*pow(F(0,1),2)*pow(F(2,2),2) + d[17]*F(0,0)*F(0,2)*pow(F(2,2),2) + d[32]*F(0,0)*F(0,2)*pow(F(2,2),2) + d[16]*F(0,1)*F(0,2)*pow(F(2,2),2) + d[26]*F(0,1)*F(0,2)*pow(F(2,2),2) + d[14]*pow(F(0,2),2)*pow(F(2,2),2);\n\n    return c;\n}\n"
  },
  {
    "path": "FECore/tens4dms.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens4d.h\n// Users should not include this file manually!\n\n#include \"matrix.h\"\n\ninline tens4dms::tens4dms(const double g)\n{\n\tfor (int i = 0; i < NNZ; i++)\n\t\td[i] = g;\n}\n\ninline tens4dms::tens4dms(double m[9][9])\n{\n\td[ 0] = m[0][0];\n\td[ 1] = m[0][1]; d[ 2] = m[1][1];\n\td[ 3] = m[0][2]; d[ 4] = m[1][2]; d[ 5] = m[2][2];\n\td[ 6] = m[0][3]; d[ 7] = m[1][3]; d[ 8] = m[2][3]; d[ 9] = m[3][3];\n\td[10] = m[0][4]; d[11] = m[1][4]; d[12] = m[2][4]; d[13] = m[3][4]; d[14] = m[4][4];\n\td[15] = m[0][5]; d[16] = m[1][5]; d[17] = m[2][5]; d[18] = m[3][5]; d[19] = m[4][5]; d[20] = m[5][5];\n\td[21] = m[0][6]; d[22] = m[1][6]; d[23] = m[2][6]; d[24] = m[3][6]; d[25] = m[4][6]; d[26] = m[5][6]; d[27] = m[6][6];\n\td[28] = m[0][7]; d[29] = m[1][7]; d[30] = m[2][7]; d[31] = m[3][7]; d[32] = m[4][7]; d[33] = m[5][7]; d[34] = m[6][7]; d[35] = m[7][7];\n\td[36] = m[0][8]; d[37] = m[1][8]; d[38] = m[2][8]; d[39] = m[3][8]; d[40] = m[4][8]; d[41] = m[5][8]; d[42] = m[6][8]; d[43] = m[7][8]; d[44] = m[8][8];\n}\n\ninline double& tens4dms::operator () (int i, int j, int k, int l)\n{\n\tconst int m[3][3] = {{0,3,5},{6,1,4},{8,7,2}};\n\ttens4dms& T = (*this);\n\treturn T(m[i][j], m[k][l]);\n}\n\ninline double tens4dms::operator () (int i, int j, int k, int l) const\n{\n\tconst int m[3][3] = {{0,3,5},{6,1,4},{8,7,2}};\n\tconst tens4dms& T = (*this);\n\treturn T(m[i][j], m[k][l]);\n}\n\ninline double& tens4dms::operator () (int i, int j)\n{\n\tconst int m[9] = {0, 1, 3, 6, 10, 15, 21, 28, 36};\n\tif (i<=j) return d[m[j]+i]; else return d[m[i]+j];\n}\n\ninline double tens4dms::operator () (int i, int j) const\n{\n\tconst int m[9] = {0, 1, 3, 6, 10, 15, 21, 28, 36};\n\tif (i<=j) return d[m[j]+i]; else return d[m[i]+j];\n}\n\n// operator +\ninline tens4dms tens4dms::operator + (const tens4dms& t) const\n{\n\ttens4dms s;\n\tfor (int i=0; i<NNZ; i++)\n\t\ts.d[i] = d[i] + t.d[i];\n\t\n\treturn s;\n}\n\n// operator -\ninline tens4dms tens4dms::operator - (const tens4dms& t) const\n{\n\ttens4dms s;\n\tfor (int i=0; i<NNZ; i++)\n\t\ts.d[i] = d[i] - t.d[i];\n\n\treturn s;\n}\n\n// operator *\ninline tens4dms tens4dms::operator * (double g) const\n{\n\ttens4dms s;\n\tfor (int i=0; i<NNZ; i++)\n\t\ts.d[i] = g*d[i];\n\t\n\treturn s;\n}\n\n// operator /\ninline tens4dms tens4dms::operator / (double g) const\n{\n\ttens4dms s;\n\tfor (int i=0; i<NNZ; i++)\n\t\ts.d[i] = d[i]/g;\n\t\n\treturn s;\n}\n\n// assignment operator +=\ninline tens4dms& tens4dms::operator += (const tens4dms& t)\n{\n\tfor (int i=0; i<NNZ; i++)\n\t\td[i] += t.d[i];\n\t\n\treturn (*this);\n}\n\n// assignment operator -=\ninline tens4dms& tens4dms::operator -= (const tens4dms& t)\n{\n\tfor (int i=0; i<NNZ; i++)\n\t\td[i] -= t.d[i];\n\t\n\treturn (*this);\n}\n\n// assignment operator *=\ninline tens4dms& tens4dms::operator *= (double g)\n{\n\tfor (int i=0; i<NNZ; i++)\n\t\td[i] *= g;\n\t\n\treturn (*this);\n}\n\n// assignment operator /=\ninline tens4dms& tens4dms::operator /= (double g)\n{\n\tfor (int i=0; i<NNZ; i++)\n\t\td[i] /= g;\n\t\n\treturn (*this);\n}\n\n// unary operator -\ninline tens4dms tens4dms::operator - () const\n{\n\ttens4dms s;\n\tfor (int i = 0; i < NNZ; i++)\n\t\ts.d[i] = -d[i];\n\n\treturn s;\n}\n\n// trace\n// C.tr() = I:C:I\n// No need to change the trace operation because major symmetry still applies\ninline double tens4dms::tr() const\n{\n\treturn (d[0]+d[2]+d[5]+2*(d[1]+d[3]+d[4]));\n}\n\n// intialize to zero\ninline void tens4dms::zero()\n{\n\tfor (int i = 0; i < NNZ; i++)\n\t\td[i] = 0;\n}\n\n// extract 9x9 matrix\ninline void tens4dms::extract(double D[9][9])\n{\n\tD[0][0] = d[0];  D[0][1] = d[1];  D[0][2] = d[3];  D[0][3] = d[6];  D[0][4] = d[10]; D[0][5] = d[15]; D[0][6] = d[21]; D[0][7] = d[28]; D[0][8] = d[36];\n\tD[1][0] = d[1];  D[1][1] = d[2];  D[1][2] = d[4];  D[1][3] = d[7];  D[1][4] = d[11]; D[1][5] = d[16]; D[1][6] = d[22]; D[1][7] = d[29]; D[1][8] = d[37];\n\tD[2][0] = d[3];  D[2][1] = d[4];  D[2][2] = d[5];  D[2][3] = d[8];  D[2][4] = d[12]; D[2][5] = d[17]; D[2][6] = d[23]; D[2][7] = d[30]; D[2][8] = d[38];\n\tD[3][0] = d[6];  D[3][1] = d[7];  D[3][2] = d[8];  D[3][3] = d[9];  D[3][4] = d[13]; D[3][5] = d[18]; D[3][6] = d[24]; D[3][7] = d[31]; D[3][8] = d[39];\n\tD[4][0] = d[10]; D[4][1] = d[11]; D[4][2] = d[12]; D[4][3] = d[13]; D[4][4] = d[14]; D[4][5] = d[19]; D[4][6] = d[25]; D[4][7] = d[32]; D[4][8] = d[40];\n\tD[5][0] = d[15]; D[5][1] = d[16]; D[5][2] = d[17]; D[5][3] = d[18]; D[5][4] = d[19]; D[5][5] = d[20]; D[5][6] = d[26]; D[5][7] = d[33]; D[5][8] = d[41];\n\tD[6][0] = d[21]; D[6][1] = d[22]; D[6][2] = d[23]; D[6][3] = d[24]; D[6][4] = d[25]; D[6][5] = d[26]; D[6][6] = d[27]; D[6][7] = d[34]; D[6][8] = d[42];\n\tD[7][0] = d[28]; D[7][1] = d[29]; D[7][2] = d[30]; D[7][3] = d[31]; D[7][4] = d[32]; D[7][5] = d[33]; D[7][6] = d[34]; D[7][7] = d[35]; D[7][8] = d[43];\n\tD[8][0] = d[36]; D[8][1] = d[37]; D[8][2] = d[38]; D[8][3] = d[39]; D[8][4] = d[40]; D[8][5] = d[41]; D[8][6] = d[42]; D[8][7] = d[43]; D[8][8] = d[44];\n}\n\n// compute the super symmetric (major and minor symmetric) component of the tensor\n// Sijkl = (1/4)*(Cijkl + Cijlk + Cjikl + Cjilk)\ninline tens4ds tens4dms::supersymm() const\n{\n\ttens4ds s;\n\n\ts.d[0] = d[0]; s.d[1] = d[1]; s.d[3] = d[3]; s.d[6] = (d[6] + d[21])/2.;            s.d[10] = (d[10] + d[28])/2.;                 s.d[15] = (d[15] + d[36])/2.;\n\t               s.d[2] = d[2]; s.d[4] = d[4]; s.d[7] = (d[7] + d[22])/2.;            s.d[11] = (d[11] + d[29])/2.;                 s.d[16] = (d[16] + d[37])/2.;\n\t\t\t\t                  s.d[5] = d[5]; s.d[8] = (d[8] + d[23])/2.;            s.d[12] = (d[12] + d[30])/2.;                 s.d[17] = (d[17] + d[38])/2.;\n\t\t\t\t\t\t\t\t                 s.d[9] = (d[9] + 2.*d[24] + d[27])/4.; s.d[13] = (d[13] + d[31] + d[25] + d[34])/4.; s.d[18] = (d[18] + d[39] + d[26] + d[42])/4.;\n\t\t\t\t\t\t\t\t\t\t\t\t                                        s.d[14] = (d[14] + 2.*d[32] + d[35])/4.;      s.d[19] = (d[19] + d[40] + d[33] + d[43])/4.;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t                                                          s.d[20] = (d[20] + 2.*d[41] + d[44])/4.;\n\n\treturn s;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s a)_ijkl = a_ij a_kl\ninline tens4dms dyad1ms(const mat3d& a)\n{\n\ttens4dms c;\n\t\n\tc.d[ 0] = a(0,0)*a(0,0);\n\tc.d[ 1] = a(0,0)*a(1,1);\n\tc.d[ 3] = a(0,0)*a(2,2);\n\tc.d[ 6] = a(0,0)*a(0,1);\n\tc.d[10] = a(0,0)*a(1,2);\n\tc.d[15] = a(0,0)*a(0,2);\n\tc.d[21] = a(0,0)*a(1,0);\n\tc.d[28] = a(0,0)*a(2,1);\n\tc.d[36] = a(0,0)*a(2,0);\n\n\tc.d[ 2] = a(1,1)*a(1,1);\n\tc.d[ 4] = a(1,1)*a(2,2);\n\tc.d[ 7] = a(1,1)*a(0,1);\n\tc.d[11] = a(1,1)*a(1,2);\n\tc.d[16] = a(1,1)*a(0,2);\n\tc.d[22] = a(1,1)*a(1,0);\n\tc.d[29] = a(1,1)*a(2,1);\n\tc.d[37] = a(1,1)*a(2,0);\n\n\tc.d[ 5] = a(2,2)*a(2,2);\n\tc.d[ 8] = a(2,2)*a(0,1);\n\tc.d[12] = a(2,2)*a(1,2);\n\tc.d[17] = a(2,2)*a(0,2);\n\tc.d[23] = a(2,2)*a(1,0);\n\tc.d[30] = a(2,2)*a(2,1);\n\tc.d[38] = a(2,2)*a(2,0);\n\n\tc.d[ 9] = a(0,1)*a(0,1);\n\tc.d[13] = a(0,1)*a(1,2);\n\tc.d[18] = a(0,1)*a(0,2);\n\tc.d[24] = a(0,1)*a(1,0);\n\tc.d[31] = a(0,1)*a(2,1);\n\tc.d[39] = a(0,1)*a(2,0);\n\n\tc.d[14] = a(1,2)*a(1,2);\n\tc.d[19] = a(1,2)*a(0,2);\n\tc.d[25] = a(1,2)*a(1,0);\n\tc.d[32] = a(1,2)*a(2,1);\n\tc.d[40] = a(1,2)*a(2,0);\n\n\tc.d[20] = a(0,2)*a(0,2);\n\tc.d[26] = a(0,2)*a(1,0);\n\tc.d[33] = a(0,2)*a(2,1);\n\tc.d[41] = a(0,2)*a(2,0);\n\n\tc.d[27] = a(1,0)*a(1,0);\n\tc.d[34] = a(1,0)*a(2,1);\n\tc.d[42] = a(1,0)*a(2,0);\n\n\tc.d[35] = a(2,1)*a(2,1);\n\tc.d[43] = a(2,1)*a(2,0);\n\n\tc.d[44] = a(2,0)*a(2,0);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s b)_ijkl = a_ij b_kl + b_ij a_kl\ninline tens4dms dyad1ms(const mat3d& a, const mat3d& b)\n{\n\ttens4dms c;\n\t\n\tc.d[0] = 2*a(0,0)*b(0,0);\n\tc.d[1] = a(0,0)*b(1,1) + b(0,0)*a(1,1);\n\tc.d[3] = a(0,0)*b(2,2) + b(0,0)*a(2,2);\n\tc.d[6] = a(0,0)*b(0,1) + b(0,0)*a(0,1);\n\tc.d[10] = a(0,0)*b(1,2) + b(0,0)*a(1,2);\n\tc.d[15] = a(0,0)*b(0,2) + b(0,0)*a(0,2);\n\tc.d[21] = a(0,0)*b(1,0) + b(0,0)*a(1,0);\n\tc.d[28] = a(0,0)*b(2,1) + b(0,0)*a(2,1);\n\tc.d[36] = a(0,0)*b(2,0) + b(0,0)*a(2,0);\n\t\n\tc.d[2] = 2*a(1,1)*b(1,1);\n\tc.d[4] = a(1,1)*b(2,2) + b(1,1)*a(2,2);\n\tc.d[7] = a(1,1)*b(0,1) + b(1,1)*a(0,1);\n\tc.d[11] = a(1,1)*b(1,2) + b(1,1)*a(1,2);\n\tc.d[16] = a(1,1)*b(0,2) + b(1,1)*a(0,2);\n\tc.d[22] = a(1,1)*b(1,0) + b(1,1)*a(1,0);\n\tc.d[29] = a(1,1)*b(2,1) + b(1,1)*a(2,1);\n\tc.d[37] = a(1,1)*b(2,0) + b(1,1)*a(2,0);\n\t\n\tc.d[5] = 2*a(2,2)*b(2,2);\n\tc.d[8] = a(2,2)*b(0,1) + b(2,2)*a(0,1);\n\tc.d[12] = a(2,2)*b(1,2) + b(2,2)*a(1,2);\n\tc.d[17] = a(2,2)*b(0,2) + b(2,2)*a(0,2);\n\tc.d[23] = a(2,2)*b(1,0) + b(2,2)*a(1,0);\n\tc.d[30] = a(2,2)*b(2,1) + b(2,2)*a(2,1);\n\tc.d[38] = a(2,2)*b(2,0) + b(2,2)*a(2,0);\n\n\tc.d[9] = 2*a(0,1)*b(0,1);\n\tc.d[13] = a(0,1)*b(1,2) + b(0,1)*a(1,2);\n\tc.d[18] = a(0,1)*b(0,2) + b(0,1)*a(0,2);\n\tc.d[24] = a(0,1)*b(1,0) + b(0,1)*a(1,0);\n\tc.d[31] = a(0,1)*b(2,1) + b(0,1)*a(2,1);\n\tc.d[39] = a(0,1)*b(2,0) + b(0,1)*a(2,0);\n\n\tc.d[14] = 2*a(1,2)*b(1,2);\n\tc.d[19] = a(1,2)*b(0,2) + b(1,2)*a(0,2);\n\tc.d[25] = a(1,2)*b(1,0) + b(1,2)*a(1,0);\n\tc.d[32] = a(1,2)*b(2,1) + b(1,2)*a(2,1);\n\tc.d[40] = a(1,2)*b(2,0) + b(1,2)*a(2,0);\n\n\tc.d[20] = 2*a(0,2)*b(0,2);\n\tc.d[26] = a(0,2)*b(1,0) + b(0,2)*a(1,0);\n\tc.d[33] = a(0,2)*b(2,1) + b(0,2)*a(2,1);\n\tc.d[41] = a(0,2)*b(2,0) + b(0,2)*a(2,0);\n\n\tc.d[27] = 2*a(1,0)*b(1,0);\n\tc.d[34] = a(1,0)*b(2,1) + b(1,0)*a(2,1);\n\tc.d[42] = a(1,0)*b(2,0) + b(1,0)*a(2,0);\n\n\tc.d[35] = 2*a(2,1)*b(2,1);\n\tc.d[43] = a(2,1)*b(2,0) + b(2,1)*a(2,0);\n\n\tc.d[44] = 2*a(2,0)*b(2,0);\n\n\treturn c;\n}\n"
  },
  {
    "path": "FECore/tens4ds.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens4d.h\n// Users should not include this file manually!\n\n#include \"matrix.h\"\n\ninline tens4ds::tens4ds(const double g)\n{\n\td[ 0] =\n\td[ 1] = d[ 2] =\n\td[ 3] = d[ 4] = d[ 5] =\n\td[ 6] = d[ 7] = d[ 8] = d[ 9] =\n\td[10] = d[11] = d[12] = d[13] = d[14] =\n\td[15] = d[16] = d[17] = d[18] = d[19] = d[20] = g;\n}\n\ninline tens4ds::tens4ds(double m[6][6])\n{\n\td[ 0] = m[0][0];\n\td[ 1] = m[0][1]; d[ 2] = m[1][1];\n\td[ 3] = m[0][2]; d[ 4] = m[1][2]; d[ 5] = m[2][2];\n\td[ 6] = m[0][3]; d[ 7] = m[1][3]; d[ 8] = m[2][3]; d[ 9] = m[3][3];\n\td[10] = m[0][4]; d[11] = m[1][4]; d[12] = m[2][4]; d[13] = m[3][4]; d[14] = m[4][4];\n\td[15] = m[0][5]; d[16] = m[1][5]; d[17] = m[2][5]; d[18] = m[3][5]; d[19] = m[4][5]; d[20] = m[5][5];\n}\n\ninline double& tens4ds::operator () (int i, int j, int k, int l)\n{\n\tconst int m[3][3] = {{0,3,5},{3,1,4},{5,4,2}};\n\ttens4ds& T = (*this);\n\treturn T(m[i][j], m[k][l]);\n}\n\ninline double tens4ds::operator () (int i, int j, int k, int l) const\n{\n\tconst int m[3][3] = {{0,3,5},{3,1,4},{5,4,2}};\n\tconst tens4ds& T = (*this);\n\treturn T(m[i][j], m[k][l]);\n}\n\ninline double& tens4ds::operator () (int i, int j)\n{\n\tconst int m[6] = {0, 1, 3, 6, 10, 15};\n\tif (i<=j) return d[m[j]+i]; else return d[m[i]+j];\n}\n\ninline double tens4ds::operator () (int i, int j) const\n{\n\tconst int m[6] = {0, 1, 3, 6, 10, 15};\n\tif (i<=j) return d[m[j]+i]; else return d[m[i]+j];\n}\n\n// operator +\ninline tens4ds tens4ds::operator + (const tens4ds& t) const\n{\n\ttens4ds s;\n//\tfor (int i=0; i<NNZ; i++)\n//\t\ts.d[i] = d[i] + t.d[i];\n\ts.d[ 0] = d[ 0] + t.d[ 0];\n\ts.d[ 1] = d[ 1] + t.d[ 1];\n\ts.d[ 2] = d[ 2] + t.d[ 2];\n\ts.d[ 3] = d[ 3] + t.d[ 3];\n\ts.d[ 4] = d[ 4] + t.d[ 4];\n\ts.d[ 5] = d[ 5] + t.d[ 5];\n\ts.d[ 6] = d[ 6] + t.d[ 6];\n\ts.d[ 7] = d[ 7] + t.d[ 7];\n\ts.d[ 8] = d[ 8] + t.d[ 8];\n\ts.d[ 9] = d[ 9] + t.d[ 9];\n\ts.d[10] = d[10] + t.d[10];\n\ts.d[11] = d[11] + t.d[11];\n\ts.d[12] = d[12] + t.d[12];\n\ts.d[13] = d[13] + t.d[13];\n\ts.d[14] = d[14] + t.d[14];\n\ts.d[15] = d[15] + t.d[15];\n\ts.d[16] = d[16] + t.d[16];\n\ts.d[17] = d[17] + t.d[17];\n\ts.d[18] = d[18] + t.d[18];\n\ts.d[19] = d[19] + t.d[19];\n\ts.d[20] = d[20] + t.d[20];\n\treturn s;\n}\n\n// operator -\ninline tens4ds tens4ds::operator - (const tens4ds& t) const\n{\n\ttens4ds s;\n//\tfor (int i=0; i<NNZ; i++)\n//\t\ts.d[i] = d[i] - t.d[i];\n\ts.d[ 0] = d[ 0] - t.d[ 0];\n\ts.d[ 1] = d[ 1] - t.d[ 1];\n\ts.d[ 2] = d[ 2] - t.d[ 2];\n\ts.d[ 3] = d[ 3] - t.d[ 3];\n\ts.d[ 4] = d[ 4] - t.d[ 4];\n\ts.d[ 5] = d[ 5] - t.d[ 5];\n\ts.d[ 6] = d[ 6] - t.d[ 6];\n\ts.d[ 7] = d[ 7] - t.d[ 7];\n\ts.d[ 8] = d[ 8] - t.d[ 8];\n\ts.d[ 9] = d[ 9] - t.d[ 9];\n\ts.d[10] = d[10] - t.d[10];\n\ts.d[11] = d[11] - t.d[11];\n\ts.d[12] = d[12] - t.d[12];\n\ts.d[13] = d[13] - t.d[13];\n\ts.d[14] = d[14] - t.d[14];\n\ts.d[15] = d[15] - t.d[15];\n\ts.d[16] = d[16] - t.d[16];\n\ts.d[17] = d[17] - t.d[17];\n\ts.d[18] = d[18] - t.d[18];\n\ts.d[19] = d[19] - t.d[19];\n\ts.d[20] = d[20] - t.d[20];\n\treturn s;\n}\n\n// operator *\ninline tens4ds tens4ds::operator * (double g) const\n{\n\ttens4ds s;\n//\tfor (int i=0; i<NNZ; i++)\n//\t\ts.d[i] = g*d[i];\n\ts.d[ 0] = g*d[ 0];\n\ts.d[ 1] = g*d[ 1];\n\ts.d[ 2] = g*d[ 2];\n\ts.d[ 3] = g*d[ 3];\n\ts.d[ 4] = g*d[ 4];\n\ts.d[ 5] = g*d[ 5];\n\ts.d[ 6] = g*d[ 6];\n\ts.d[ 7] = g*d[ 7];\n\ts.d[ 8] = g*d[ 8];\n\ts.d[ 9] = g*d[ 9];\n\ts.d[10] = g*d[10];\n\ts.d[11] = g*d[11];\n\ts.d[12] = g*d[12];\n\ts.d[13] = g*d[13];\n\ts.d[14] = g*d[14];\n\ts.d[15] = g*d[15];\n\ts.d[16] = g*d[16];\n\ts.d[17] = g*d[17];\n\ts.d[18] = g*d[18];\n\ts.d[19] = g*d[19];\n\ts.d[20] = g*d[20];\n\treturn s;\n}\n\n// operator /\ninline tens4ds tens4ds::operator / (double g) const\n{\n\ttens4ds s;\n//\tfor (int i=0; i<NNZ; i++)\n//\t\ts.d[i] = d[i]/g;\n\ts.d[ 0] = d[ 0]/g;\n\ts.d[ 1] = d[ 1]/g;\n\ts.d[ 2] = d[ 2]/g;\n\ts.d[ 3] = d[ 3]/g;\n\ts.d[ 4] = d[ 4]/g;\n\ts.d[ 5] = d[ 5]/g;\n\ts.d[ 6] = d[ 6]/g;\n\ts.d[ 7] = d[ 7]/g;\n\ts.d[ 8] = d[ 8]/g;\n\ts.d[ 9] = d[ 9]/g;\n\ts.d[10] = d[10]/g;\n\ts.d[11] = d[11]/g;\n\ts.d[12] = d[12]/g;\n\ts.d[13] = d[13]/g;\n\ts.d[14] = d[14]/g;\n\ts.d[15] = d[15]/g;\n\ts.d[16] = d[16]/g;\n\ts.d[17] = d[17]/g;\n\ts.d[18] = d[18]/g;\n\ts.d[19] = d[19]/g;\n\ts.d[20] = d[20]/g;\n\treturn s;\n}\n\n// assignment operator +=\ninline tens4ds& tens4ds::operator += (const tens4ds& t)\n{\n//\tfor (int i=0; i<NNZ; i++)\n//\t\td[i] += t.d[i];\n\td[ 0] += t.d[ 0];\n\td[ 1] += t.d[ 1];\n\td[ 2] += t.d[ 2];\n\td[ 3] += t.d[ 3];\n\td[ 4] += t.d[ 4];\n\td[ 5] += t.d[ 5];\n\td[ 6] += t.d[ 6];\n\td[ 7] += t.d[ 7];\n\td[ 8] += t.d[ 8];\n\td[ 9] += t.d[ 9];\n\td[10] += t.d[10];\n\td[11] += t.d[11];\n\td[12] += t.d[12];\n\td[13] += t.d[13];\n\td[14] += t.d[14];\n\td[15] += t.d[15];\n\td[16] += t.d[16];\n\td[17] += t.d[17];\n\td[18] += t.d[18];\n\td[19] += t.d[19];\n\td[20] += t.d[20];\n\treturn (*this);\n}\n\n// assignment operator -=\ninline tens4ds& tens4ds::operator -= (const tens4ds& t)\n{\n//\tfor (int i=0; i<NNZ; i++)\n//\t\td[i] -= t.d[i];\n\td[ 0] -= t.d[ 0];\n\td[ 1] -= t.d[ 1];\n\td[ 2] -= t.d[ 2];\n\td[ 3] -= t.d[ 3];\n\td[ 4] -= t.d[ 4];\n\td[ 5] -= t.d[ 5];\n\td[ 6] -= t.d[ 6];\n\td[ 7] -= t.d[ 7];\n\td[ 8] -= t.d[ 8];\n\td[ 9] -= t.d[ 9];\n\td[10] -= t.d[10];\n\td[11] -= t.d[11];\n\td[12] -= t.d[12];\n\td[13] -= t.d[13];\n\td[14] -= t.d[14];\n\td[15] -= t.d[15];\n\td[16] -= t.d[16];\n\td[17] -= t.d[17];\n\td[18] -= t.d[18];\n\td[19] -= t.d[19];\n\td[20] -= t.d[20];\n\treturn (*this);\n}\n\n// assignment operator *=\ninline tens4ds& tens4ds::operator *= (double g)\n{\n//\tfor (int i=0; i<NNZ; i++)\n//\t\td[i] *= g;\n\td[ 0] *= g;\n\td[ 1] *= g;\n\td[ 2] *= g;\n\td[ 3] *= g;\n\td[ 4] *= g;\n\td[ 5] *= g;\n\td[ 6] *= g;\n\td[ 7] *= g;\n\td[ 8] *= g;\n\td[ 9] *= g;\n\td[10] *= g;\n\td[11] *= g;\n\td[12] *= g;\n\td[13] *= g;\n\td[14] *= g;\n\td[15] *= g;\n\td[16] *= g;\n\td[17] *= g;\n\td[18] *= g;\n\td[19] *= g;\n\td[20] *= g;\n\treturn (*this);\n}\n\n// assignment operator /=\ninline tens4ds& tens4ds::operator /= (double g)\n{\n//\tfor (int i=0; i<NNZ; i++)\n//\t\td[i] /= g;\n\td[ 0] /= g;\n\td[ 1] /= g;\n\td[ 2] /= g;\n\td[ 3] /= g;\n\td[ 4] /= g;\n\td[ 5] /= g;\n\td[ 6] /= g;\n\td[ 7] /= g;\n\td[ 8] /= g;\n\td[ 9] /= g;\n\td[10] /= g;\n\td[11] /= g;\n\td[12] /= g;\n\td[13] /= g;\n\td[14] /= g;\n\td[15] /= g;\n\td[16] /= g;\n\td[17] /= g;\n\td[18] /= g;\n\td[19] /= g;\n\td[20] /= g;\n\treturn (*this);\n}\n\n// unary operator -\ninline tens4ds tens4ds::operator - () const\n{\n\ttens4ds s;\n\ts.d[ 0] = -d[ 0];\n\ts.d[ 1] = -d[ 1];\n\ts.d[ 2] = -d[ 2];\n\ts.d[ 3] = -d[ 3];\n\ts.d[ 4] = -d[ 4];\n\ts.d[ 5] = -d[ 5];\n\ts.d[ 6] = -d[ 6];\n\ts.d[ 7] = -d[ 7];\n\ts.d[ 8] = -d[ 8];\n\ts.d[ 9] = -d[ 9];\n\ts.d[10] = -d[10];\n\ts.d[11] = -d[11];\n\ts.d[12] = -d[12];\n\ts.d[13] = -d[13];\n\ts.d[14] = -d[14];\n\ts.d[15] = -d[15];\n\ts.d[16] = -d[16];\n\ts.d[17] = -d[17];\n\ts.d[18] = -d[18];\n\ts.d[19] = -d[19];\n\ts.d[20] = -d[20];\n\treturn s;\n}\n\n// trace\n// C.tr() = I:C:I\ninline double tens4ds::tr() const\n{\n\treturn (d[0]+d[2]+d[5]+2*(d[1]+d[3]+d[4]));\n}\n\n// intialize to zero\ninline void tens4ds::zero()\n{\n\td[0] = d[1] = d[2] = d[3] = d[4] = d[5] = d[6] = d[7] = d[8] = d[9] =\n\td[10] = d[11] = d[12] = d[13] = d[14] = d[15] = d[16] = d[17] = d[18] = d[19] = d[20] = 0;\n}\n\n// extract 6x6 matrix\ninline void tens4ds::extract(double D[6][6])\n{\n\tD[0][0] = d[0];  D[0][1] = d[1];  D[0][2] = d[3];  D[0][3] = d[6];  D[0][4] = d[10]; D[0][5] = d[15];\n\tD[1][0] = d[1];  D[1][1] = d[2];  D[1][2] = d[4];  D[1][3] = d[7];  D[1][4] = d[11]; D[1][5] = d[16];\n\tD[2][0] = d[3];  D[2][1] = d[4];  D[2][2] = d[5];  D[2][3] = d[8];  D[2][4] = d[12]; D[2][5] = d[17];\n\tD[3][0] = d[6];  D[3][1] = d[7];  D[3][2] = d[8];  D[3][3] = d[9];  D[3][4] = d[13]; D[3][5] = d[18];\n\tD[4][0] = d[10]; D[4][1] = d[11]; D[4][2] = d[12]; D[4][3] = d[13]; D[4][4] = d[14]; D[4][5] = d[19];\n\tD[5][0] = d[15]; D[5][1] = d[16]; D[5][2] = d[17]; D[5][3] = d[18]; D[5][4] = d[19]; D[5][5] = d[20];\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s a)_ijkl = a_ij a_kl\ninline tens4ds dyad1s(const mat3dd& a)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = a.xx()*a.xx();\n\n    c.d[ 1] = a.xx()*a.yy();\n    c.d[ 2] = a.yy()*a.yy();\n\n    c.d[ 3] = a.xx()*a.zz();\n    c.d[ 4] = a.yy()*a.zz();\n    c.d[ 5] = a.zz()*a.zz();\n\n    c.d[ 6] = 0.0;\n    c.d[ 7] = 0.0;\n    c.d[ 8] = 0.0;\n    c.d[ 9] = 0.0;\n\n    c.d[10] = 0.0;\n    c.d[11] = 0.0;\n    c.d[12] = 0.0;\n    c.d[13] = 0.0;\n    c.d[14] = 0.0;\n\n    c.d[15] = 0.0;\n    c.d[16] = 0.0;\n    c.d[17] = 0.0;\n    c.d[18] = 0.0;\n    c.d[19] = 0.0;\n    c.d[20] = 0.0;\n    \n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s a)_ijkl = a_ij a_kl\ninline tens4ds dyad1s(const mat3ds& a)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = a.xx()*a.xx();\n\n    c.d[ 1] = a.xx()*a.yy();\n    c.d[ 2] = a.yy()*a.yy();\n\n    c.d[ 3] = a.xx()*a.zz();\n    c.d[ 4] = a.yy()*a.zz();\n    c.d[ 5] = a.zz()*a.zz();\n\n    c.d[ 6] = a.xx()*a.xy();\n    c.d[ 7] = a.xy()*a.yy();\n    c.d[ 8] = a.xy()*a.zz();\n    c.d[ 9] = a.xy()*a.xy();\n\n    c.d[10] = a.xx()*a.yz();\n    c.d[11] = a.yy()*a.yz();\n    c.d[12] = a.yz()*a.zz();\n    c.d[13] = a.xy()*a.yz();\n    c.d[14] = a.yz()*a.yz();\n\n    c.d[15] = a.xx()*a.xz();\n    c.d[16] = a.xz()*a.yy();\n    c.d[17] = a.xz()*a.zz();\n    c.d[18] = a.xy()*a.xz();\n    c.d[19] = a.xz()*a.yz();\n    c.d[20] = a.xz()*a.xz();\n    \n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s b)_ijkl = a_ij b_kl + b_ij a_kl\ninline tens4ds dyad1s(const mat3ds& a, const mat3ds& b)\n{\n\ttens4ds c;\n\n    c.d[ 0] = 2*a.xx()*b.xx();\n    \n    c.d[ 1] = a.xx()*b.yy() + b.xx()*a.yy();\n    c.d[ 2] = 2*a.yy()*b.yy();\n\n    c.d[ 3] = a.xx()*b.zz() + b.xx()*a.zz();\n    c.d[ 4] = a.yy()*b.zz() + b.yy()*a.zz();\n    c.d[ 5] = 2*a.zz()*b.zz();\n\n    c.d[ 6] = a.xx()*b.xy() + b.xx()*a.xy();\n    c.d[ 7] = a.xy()*b.yy() + b.xy()*a.yy();\n    c.d[ 8] = a.xy()*b.zz() + b.xy()*a.zz();\n    c.d[ 9] = 2*a.xy()*b.xy();\n\n    c.d[10] = a.xx()*b.yz() + b.xx()*a.yz();\n    c.d[11] = a.yy()*b.yz() + b.yy()*a.yz();\n    c.d[12] = a.yz()*b.zz() + b.yz()*a.zz();\n    c.d[13] = a.xy()*b.yz() + b.xy()*a.yz();\n    c.d[14] = 2*a.yz()*b.yz();\n\n    c.d[15] = a.xx()*b.xz() + b.xx()*a.xz();\n    c.d[16] = a.xz()*b.yy() + b.xz()*a.yy();\n    c.d[17] = a.xz()*b.zz() + b.xz()*a.zz();\n    c.d[18] = a.xy()*b.xz() + b.xy()*a.xz();\n    c.d[19] = a.xz()*b.yz() + b.xz()*a.yz();\n    c.d[20] = 2*a.xz()*b.xz();\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s b)_ijkl = a_ij b_kl + b_ij a_kl\ninline tens4ds dyad1s(const mat3dd& a, const mat3dd& b)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = 2*a.xx()*b.xx();\n    \n    c.d[ 1] = a.xx()*b.yy() + b.xx()*a.yy();\n    c.d[ 2] = 2*a.yy()*b.yy();\n\n    c.d[ 3] = a.xx()*b.zz() + b.xx()*a.zz();\n    c.d[ 4] = a.yy()*b.zz() + b.yy()*a.zz();\n    c.d[ 5] = 2*a.zz()*b.zz();\n\n    c.d[ 6] = 0.0;\n    c.d[ 7] = 0.0;\n    c.d[ 8] = 0.0;\n    c.d[ 9] = 0.0;\n\n    c.d[10] = 0.0;\n    c.d[11] = 0.0;\n    c.d[12] = 0.0;\n    c.d[13] = 0.0;\n    c.d[14] = 0.0;\n\n    c.d[15] = 0.0;\n    c.d[16] = 0.0;\n    c.d[17] = 0.0;\n    c.d[18] = 0.0;\n    c.d[19] = 0.0;\n    c.d[20] = 0.0;\n    \n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad1s b)_ijkl = a_ij b_kl + b_ij a_kl\ninline tens4ds dyad1s(const mat3ds& a, const mat3dd& b)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = 2*a.xx()*b.xx();\n    \n    c.d[ 1] = a.xx()*b.yy() + b.xx()*a.yy();\n    c.d[ 2] = 2*a.yy()*b.yy();\n\n    c.d[ 3] = a.xx()*b.zz() + b.xx()*a.zz();\n    c.d[ 4] = a.yy()*b.zz() + b.yy()*a.zz();\n    c.d[ 5] = 2*a.zz()*b.zz();\n\n    c.d[ 6] = b.xx()*a.xy();\n    c.d[ 7] = a.xy()*b.yy();\n    c.d[ 8] = a.xy()*b.zz();\n    c.d[ 9] = 0.0;\n\n    c.d[10] = b.xx()*a.yz();\n    c.d[11] = b.yy()*a.yz();\n    c.d[12] = a.yz()*b.zz();\n    c.d[13] = 0.0;\n    c.d[14] = 0.0;\n\n    c.d[15] = b.xx()*a.xz();\n    c.d[16] = a.xz()*b.yy();\n    c.d[17] = a.xz()*b.zz();\n    c.d[18] = 0.0;\n    c.d[19] = 0.0;\n    c.d[20] = 0.0;\n    \n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad4s a)_ijkl = (a_ik a_jl + a_il a_jk)/2\ninline tens4ds dyad4s(const mat3dd& a)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = a.xx()*a.xx();\n\n    c.d[ 1] = 0.0;\n    c.d[ 2] = a.yy()*a.yy();\n\n    c.d[ 3] = 0.0;\n    c.d[ 4] = 0.0;\n    c.d[ 5] = a.zz()*a.zz();\n\n    c.d[ 6] = 0.0;\n    c.d[ 7] = 0.0;\n    c.d[ 8] = 0.0;\n    c.d[ 9] = a.xx()*a.yy()/2;\n\n    c.d[10] = 0.0;\n    c.d[11] = 0.0;\n    c.d[12] = 0.0;\n    c.d[13] = 0.0;\n    c.d[14] = a.yy()*a.zz()/2;\n\n    c.d[15] = 0.0;\n    c.d[16] = 0.0;\n    c.d[17] = 0.0;\n    c.d[18] = 0.0;\n    c.d[19] = 0.0;\n    c.d[20] = a.xx()*a.zz()/2;\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad4s a)_ijkl = (a_ik a_jl + a_il a_jk)/2\ninline tens4ds dyad4s(const mat3ds& a)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = a.xx()*a.xx();\n\n    c.d[ 1] = a.xy()*a.xy();\n    c.d[ 2] = a.yy()*a.yy();\n\n    c.d[ 3] = a.xz()*a.xz();\n    c.d[ 4] = a.yz()*a.yz();\n    c.d[ 5] = a.zz()*a.zz();\n\n    c.d[ 6] = a.xx()*a.xy();\n    c.d[ 7] = a.xy()*a.yy();\n    c.d[ 8] = a.xz()*a.yz();\n    c.d[ 9] = (a.xx()*a.yy() + a.xy()*a.xy())/2;\n\n    c.d[10] = a.xy()*a.xz();\n    c.d[11] = a.yy()*a.yz();\n    c.d[12] = a.yz()*a.zz();\n    c.d[13] = (a.xy()*a.yz() + a.xz()*a.yy())/2;\n    c.d[14] = (a.yy()*a.zz() + a.yz()*a.yz())/2;\n\n    c.d[15] = a.xx()*a.xz();\n    c.d[16] = a.xy()*a.yz();\n    c.d[17] = a.xz()*a.zz();\n    c.d[18] = (a.xx()*a.yz() + a.xy()*a.xz())/2;\n    c.d[19] = (a.xy()*a.zz() + a.xz()*a.yz())/2;\n    c.d[20] = (a.xx()*a.zz() + a.xz()*a.xz())/2;\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad4s b)_ijkl = (a_ik b_jl + a_il b_jk)/2 +  (b_ik a_jl + b_il a_jk)/2\ninline tens4ds dyad4s(const mat3ds& a, const mat3dd& b)\n{\n\ttens4ds c;\n\n    c.d[ 0] = 2*a.xx()*b.xx();\n\n    c.d[ 1] = 0.0;\n    c.d[ 2] = 2*a.yy()*b.yy();\n\n    c.d[ 3] = 0.0;\n    c.d[ 4] = 0.0;\n    c.d[ 5] = 2*a.zz()*b.zz();\n\n    c.d[ 6] = b.xx()*a.xy();\n    c.d[ 7] = a.xy()*b.yy();\n    c.d[ 8] = 0.0;\n    c.d[ 9] = (a.xx()*b.yy() + b.xx()*a.yy())/2;\n\n    c.d[10] = 0.0;\n    c.d[11] = b.yy()*a.yz();\n    c.d[12] = a.yz()*b.zz();\n    c.d[13] = a.xz()*b.yy()/2;\n    c.d[14] = (a.yy()*b.zz() + b.yy()*a.zz())/2;\n\n    c.d[15] = b.xx()*a.xz();\n    c.d[16] = 0.0;\n    c.d[17] = a.xz()*b.zz();\n    c.d[18] = b.xx()*a.yz()/2;\n    c.d[19] = a.xy()*b.zz()/2;\n    c.d[20] = (a.xx()*b.zz() + b.xx()*a.zz())/2;\n    \n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad5s b)_ijkl = (a_ik b_jl + a_il b_jk)/2 +  (a_jl b_ik + a_jk b_il)/2\ninline tens4ds dyad5s(const mat3ds& a, const mat3ds& b)\n{\n\tint L[21][4] = { { 0, 0, 0, 0},\n\t{ 0, 0, 1, 1},{ 1, 1, 1, 1},\n\t{ 0, 0, 2, 2},{ 1, 1, 2, 2},{ 2, 2, 2, 2},\n\t{ 0, 0, 0, 1},{ 1, 1, 0, 1},{ 2, 2, 0, 1},{ 0, 1, 0, 1},\n\t{ 0, 0, 1, 2},{ 1, 1, 1, 2},{ 2, 2, 1, 2},{ 0, 1, 1, 2},{ 1, 2, 1, 2 },\n\t{ 0, 0, 0, 2},{ 1, 1, 0, 2},{ 2, 2, 0, 2},{ 0, 1, 0, 2},{ 1, 2, 0, 2 },{ 0, 2, 0, 2 }};\n\n\ttens4ds c;\n\tfor (int n = 0; n < 21; ++n)\n\t{\n\t\tint i = L[n][0];\n\t\tint j = L[n][1];\n\t\tint k = L[n][2];\n\t\tint l = L[n][3];\n\t\tc.d[n] = 0.5*(a(i, k)*b(j, l) + a(i, l)*b(j, k)) + 0.5*(a(j, l)*b(i, k) + a(j, k)*b(i, l));\n\t}\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// (a dyad4s b)_ijkl = (a_ik b_jl + a_il b_jk)/2 +  (b_ik a_jl + b_il a_jk)/2\ninline tens4ds dyad4s(const mat3ds& a, const mat3ds& b)\n{\n\ttens4ds c;\n    \n    c.d[ 0] = 2*a.xx()*b.xx();\n\n    c.d[ 1] = 2*a.xy()*b.xy();\n    c.d[ 2] = 2*a.yy()*b.yy();\n\n    c.d[ 3] = 2*a.xz()*b.xz();\n    c.d[ 4] = 2*a.yz()*b.yz();\n    c.d[ 5] = 2*a.zz()*b.zz();\n\n    c.d[ 6] = a.xx()*b.xy() + b.xx()*a.xy();\n    c.d[ 7] = a.xy()*b.yy() + b.xy()*a.yy();\n    c.d[ 8] = a.xz()*b.yz() + b.xz()*a.yz();\n    c.d[ 9] = (a.xx()*b.yy() + 2*a.xy()*b.xy() + b.xx()*a.yy())/2;\n\n    c.d[10] = a.xy()*b.xz() + b.xy()*a.xz();\n    c.d[11] = a.yy()*b.yz() + b.yy()*a.yz();\n    c.d[12] = a.yz()*b.zz() + b.yz()*a.zz();\n    c.d[13] = (a.xy()*b.yz() + a.xz()*b.yy() + b.xy()*a.yz() + b.xz()*a.yy())/2;\n    c.d[14] = (a.yy()*b.zz() + 2*a.yz()*b.yz() + b.yy()*a.zz())/2;\n\n    c.d[15] = a.xx()*b.xz() + b.xx()*a.xz();\n    c.d[16] = a.xy()*b.yz() + b.xy()*a.yz();\n    c.d[17] = a.xz()*b.zz() + b.xz()*a.zz();\n    c.d[18] = (a.xx()*b.yz() + a.xy()*b.xz() + b.xx()*a.yz() + b.xy()*a.xz())/2;\n    c.d[19] = (a.xy()*b.zz() + a.xz()*b.yz() + b.xy()*a.zz() + b.xz()*a.yz())/2;\n    c.d[20] = (a.xx()*b.zz() + 2*a.xz()*b.xz() + b.xx()*a.zz())/2;\n    \n\treturn c;\n\n}\n\n//-----------------------------------------------------------------------------\n// (a ddots b)_ijkl = a_ijmn b_mnkl + b_ijmn a_mnkl\ninline tens4ds ddots(const tens4ds& a, const tens4ds& b)\n{\n\ttens4ds c;\n\t\n\tc.d[0] = 2*(a.d[0]*b.d[0] + a.d[1]*b.d[1] + a.d[3]*b.d[3] + 2*a.d[6]*b.d[6] \n\t\t\t\t+ 2*a.d[10]*b.d[10] + 2*a.d[15]*b.d[15]);\n\tc.d[1] = a.d[0]*b.d[1] + a.d[2]*b.d[1] + a.d[1]*(b.d[0] + b.d[2]) + a.d[4]*b.d[3] \n\t+ a.d[3]*b.d[4] + 2*a.d[7]*b.d[6] + 2*a.d[6]*b.d[7] + 2*a.d[11]*b.d[10] \n\t+ 2*a.d[10]*b.d[11] + 2*a.d[16]*b.d[15] + 2*a.d[15]*b.d[16];\n\tc.d[3] = a.d[4]*b.d[1] + a.d[0]*b.d[3] + a.d[5]*b.d[3] + a.d[1]*b.d[4] \n\t+ a.d[3]*(b.d[0] + b.d[5]) + 2*a.d[8]*b.d[6] + 2*a.d[6]*b.d[8] + 2*a.d[12]*b.d[10] \n\t+ 2*a.d[10]*b.d[12] + 2*a.d[17]*b.d[15] + 2*a.d[15]*b.d[17];\n\tc.d[6] = a.d[7]*b.d[1] + a.d[8]*b.d[3] + a.d[0]*b.d[6] + 2*a.d[9]*b.d[6] + a.d[1]*b.d[7] \n\t+ a.d[3]*b.d[8] + a.d[6]*(b.d[0] + 2*b.d[9]) + 2*a.d[13]*b.d[10] + 2*a.d[10]*b.d[13] \n\t+ 2*a.d[18]*b.d[15] + 2*a.d[15]*b.d[18];\n\tc.d[10] = a.d[11]*b.d[1] + a.d[12]*b.d[3] + 2*a.d[13]*b.d[6] + a.d[0]*b.d[10] \n\t+ 2*a.d[14]*b.d[10] + a.d[1]*b.d[11] + a.d[3]*b.d[12] + 2*a.d[6]*b.d[13] \n\t+ a.d[10]*(b.d[0] + 2*b.d[14]) + 2*a.d[19]*b.d[15] + 2*a.d[15]*b.d[19];\n\tc.d[15] = a.d[16]*b.d[1] + a.d[17]*b.d[3] + 2*a.d[18]*b.d[6] + 2*a.d[19]*b.d[10] \n\t+ a.d[0]*b.d[15] + 2*a.d[20]*b.d[15] + a.d[1]*b.d[16] + a.d[3]*b.d[17] \n\t+ 2*a.d[6]*b.d[18] + 2*a.d[10]*b.d[19] + a.d[15]*(b.d[0] + 2*b.d[20]);\n\t\n\tc.d[2] = 2*(a.d[1]*b.d[1] + a.d[2]*b.d[2] + a.d[4]*b.d[4] + 2*a.d[7]*b.d[7] \n\t\t\t\t+ 2*a.d[11]*b.d[11] + 2*a.d[16]*b.d[16]);\n\tc.d[4] = a.d[3]*b.d[1] + a.d[1]*b.d[3] + a.d[2]*b.d[4] + a.d[5]*b.d[4] \n\t+ a.d[4]*(b.d[2] + b.d[5]) + 2*a.d[8]*b.d[7] + 2*a.d[7]*b.d[8] + 2*a.d[12]*b.d[11] \n\t+ 2*a.d[11]*b.d[12] + 2*a.d[17]*b.d[16] + 2*a.d[16]*b.d[17];\n\tc.d[7] = a.d[6]*b.d[1] + a.d[8]*b.d[4] + a.d[1]*b.d[6] + a.d[2]*b.d[7] \n\t+ 2*a.d[9]*b.d[7] + a.d[4]*b.d[8] + a.d[7]*(b.d[2] + 2*b.d[9]) + 2*a.d[13]*b.d[11] \n\t+ 2*a.d[11]*b.d[13] + 2*a.d[18]*b.d[16] + 2*a.d[16]*b.d[18];\n\tc.d[11] = a.d[10]*b.d[1] + a.d[12]*b.d[4] + 2*a.d[13]*b.d[7] + a.d[1]*b.d[10] \n\t+ a.d[2]*b.d[11] + 2*a.d[14]*b.d[11] + a.d[4]*b.d[12] + 2*a.d[7]*b.d[13] \n\t+ a.d[11]*(b.d[2] + 2*b.d[14]) + 2*a.d[19]*b.d[16] + 2*a.d[16]*b.d[19];\n\tc.d[16] = a.d[15]*b.d[1] + a.d[17]*b.d[4] + 2*a.d[18]*b.d[7] + 2*a.d[19]*b.d[11] \n\t+ a.d[1]*b.d[15] + a.d[2]*b.d[16] + 2*a.d[20]*b.d[16] + a.d[4]*b.d[17] \n\t+ 2*a.d[7]*b.d[18] + 2*a.d[11]*b.d[19] + a.d[16]*(b.d[2] + 2*b.d[20]);\n\t\n\tc.d[5] = 2*(a.d[3]*b.d[3] + a.d[4]*b.d[4] + a.d[5]*b.d[5] + 2*a.d[8]*b.d[8] \n\t\t\t\t+ 2*a.d[12]*b.d[12] + 2*a.d[17]*b.d[17]);\n\tc.d[8] = a.d[6]*b.d[3] + a.d[7]*b.d[4] + a.d[8]*b.d[5] + a.d[3]*b.d[6] \n\t+ a.d[4]*b.d[7] + a.d[5]*b.d[8] + 2*a.d[9]*b.d[8] + 2*a.d[8]*b.d[9] + 2*a.d[13]*b.d[12] \n\t+ 2*a.d[12]*b.d[13] + 2*a.d[18]*b.d[17] + 2*a.d[17]*b.d[18];\n\tc.d[12] = a.d[10]*b.d[3] + a.d[11]*b.d[4] + a.d[12]*b.d[5] + 2*a.d[13]*b.d[8] \n\t+ a.d[3]*b.d[10] + a.d[4]*b.d[11] + a.d[5]*b.d[12] + 2*a.d[14]*b.d[12] + 2*a.d[8]*b.d[13] \n\t+ 2*a.d[12]*b.d[14] + 2*a.d[19]*b.d[17] + 2*a.d[17]*b.d[19];\n\tc.d[17] = a.d[15]*b.d[3] + a.d[16]*b.d[4] + a.d[17]*b.d[5] + 2*a.d[18]*b.d[8] \n\t+ 2*a.d[19]*b.d[12] + a.d[3]*b.d[15] + a.d[4]*b.d[16] + a.d[5]*b.d[17] \n\t+ 2*a.d[20]*b.d[17] + 2*a.d[8]*b.d[18] + 2*a.d[12]*b.d[19] + 2*a.d[17]*b.d[20];\n\t\n\tc.d[9] = 2*(a.d[6]*b.d[6] + a.d[7]*b.d[7] + a.d[8]*b.d[8] + 2*a.d[9]*b.d[9] \n\t\t\t\t+ 2*a.d[13]*b.d[13] + 2*a.d[18]*b.d[18]);\n\tc.d[13] = a.d[10]*b.d[6] + a.d[11]*b.d[7] + a.d[12]*b.d[8] + 2*a.d[13]*b.d[9] \n\t+ a.d[6]*b.d[10] + a.d[7]*b.d[11] + a.d[8]*b.d[12] + 2*a.d[9]*b.d[13] \n\t+ 2*a.d[14]*b.d[13] + 2*a.d[13]*b.d[14] + 2*a.d[19]*b.d[18] + 2*a.d[18]*b.d[19];\n\tc.d[18] = a.d[15]*b.d[6] + a.d[16]*b.d[7] + a.d[17]*b.d[8] + 2*a.d[18]*b.d[9] \n\t+ 2*a.d[19]*b.d[13] + a.d[6]*b.d[15] + a.d[7]*b.d[16] + a.d[8]*b.d[17] \n\t+ 2*a.d[9]*b.d[18] + 2*a.d[20]*b.d[18] + 2*a.d[13]*b.d[19] + 2*a.d[18]*b.d[20];\n\t\n\tc.d[14] = 2*(a.d[10]*b.d[10] + a.d[11]*b.d[11] + a.d[12]*b.d[12] \n\t\t\t\t + 2*a.d[13]*b.d[13] + 2*a.d[14]*b.d[14] + 2*a.d[19]*b.d[19]);\n\tc.d[19] = a.d[15]*b.d[10] + a.d[16]*b.d[11] + a.d[17]*b.d[12] \n\t+ 2*a.d[18]*b.d[13] + 2*a.d[19]*b.d[14] + a.d[10]*b.d[15] + a.d[11]*b.d[16] \n\t+ a.d[12]*b.d[17] + 2*a.d[13]*b.d[18] + 2*a.d[14]*b.d[19] + 2*a.d[20]*b.d[19] + 2*a.d[19]*b.d[20];\n\t\n\tc.d[20] = 2*(a.d[15]*b.d[15] + a.d[16]*b.d[16] + a.d[17]*b.d[17] \n\t\t\t\t + 2*a.d[18]*b.d[18] + 2*a.d[19]*b.d[19] + 2*a.d[20]*b.d[20]);\n\t\n\treturn c;\n\t\n}\n\n//-----------------------------------------------------------------------------\n// Evaluates the dyadic product C_ijkl = 0.25*(a_i * K_jk * b_l + perms.)\ninline tens4ds dyad4s(const vec3d& a, const mat3d& K, const vec3d& b)\n{\n\ttens4ds c;\n\n\tc(0,0) = a.x * K(0,0) * b.x;\n\tc(1,1) = a.y * K(1,1) * b.y;\n\tc(2,2) = a.z * K(2,2) * b.z;\n\n\tc(0,1) = 0.5*(a.x * K(0,1) * b.y + a.y * K(1,0) * b.x);\n\tc(0,2) = 0.5*(a.x * K(0,2) * b.z + a.z * K(2,0) * b.x);\n\tc(1,2) = 0.5*(a.y * K(1,2) * b.z + a.z * K(2,1) * b.y);\n\n\tc(0,3) = 0.25*(a.x * K(0,0) * b.y + a.x * K(0,1) * b.x + a.x * K(1,0) * b.x + a.y * K(0,0) * b.x);\n\tc(0,4) = 0.25*(a.x * K(0,1) * b.z + a.x * K(0,2) * b.y + a.y * K(2,0) * b.x + a.z * K(1,0) * b.x);\n\tc(0,5) = 0.25*(a.x * K(0,0) * b.z + a.x * K(0,2) * b.x + a.x * K(2,0) * b.x + a.z * K(0,0) * b.x);\n\tc(1,3) = 0.25*(a.y * K(1,0) * b.y + a.y * K(1,1) * b.x + a.x * K(1,1) * b.y + a.y * K(0,1) * b.y);\n\tc(1,4) = 0.25*(a.y * K(1,1) * b.z + a.y * K(1,2) * b.y + a.y * K(2,1) * b.y + a.z * K(1,1) * b.y);\n\tc(1,5) = 0.25*(a.y * K(1,0) * b.z + a.y * K(1,2) * b.x + a.x * K(2,1) * b.y + a.z * K(0,1) * b.y);\n\tc(2,3) = 0.25*(a.z * K(2,0) * b.y + a.z * K(2,1) * b.x + a.x * K(1,2) * b.z + a.y * K(0,2) * b.z);\n\tc(2,4) = 0.25*(a.z * K(2,1) * b.z + a.z * K(2,2) * b.y + a.y * K(2,2) * b.z + a.z * K(1,2) * b.z);\n\tc(2,5) = 0.25*(a.z * K(2,0) * b.z + a.z * K(2,2) * b.x + a.x * K(2,2) * b.z + a.z * K(0,2) * b.z);\n\n\tc(3,3) = 0.25*(a.x * K(1,0) * b.y + a.x * K(1,1) * b.x + a.x * K(1,0) * b.y + a.y * K(0,0) * b.y);\n\tc(3,4) = 0.25*(a.x * K(1,1) * b.z + a.x * K(1,2) * b.y + a.y * K(2,0) * b.y + a.z * K(1,0) * b.y);\n\tc(3,5) = 0.25*(a.x * K(1,0) * b.z + a.x * K(1,2) * b.x + a.x * K(2,0) * b.y + a.z * K(0,0) * b.y);\n\n\tc(4,4) = 0.25*(a.y * K(2,1) * b.z + a.y * K(2,2) * b.y + a.y * K(2,1) * b.z + a.z * K(1,1) * b.z);\n\tc(4,5) = 0.25*(a.y * K(2,0) * b.z + a.y * K(2,2) * b.x + a.x * K(2,1) * b.z + a.z * K(0,1) * b.z);\n\n\tc(5,5) = 0.25*(a.x * K(2,0) * b.z + a.x * K(2,2) * b.x + a.x * K(2,0) * b.z + a.z * K(0,0) * b.z);\n\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of symmetric 4th-order tensor with a symmetric 2nd-order tensor\n// Aij = Dijkl Mkl\ninline mat3ds tens4ds::dot(const mat3ds &m) const\n{\n\tmat3ds a;\n\ta.xx() = d[ 0]*m.xx() + d[ 1]*m.yy() + d[ 3]*m.zz() + 2*d[ 6]*m.xy() + 2*d[10]*m.yz() + 2*d[15]*m.xz();\n\ta.yy() = d[ 1]*m.xx() + d[ 2]*m.yy() + d[ 4]*m.zz() + 2*d[ 7]*m.xy() + 2*d[11]*m.yz() + 2*d[16]*m.xz();\n\ta.zz() = d[ 3]*m.xx() + d[ 4]*m.yy() + d[ 5]*m.zz() + 2*d[ 8]*m.xy() + 2*d[12]*m.yz() + 2*d[17]*m.xz();\n\ta.xy() = d[ 6]*m.xx() + d[ 7]*m.yy() + d[ 8]*m.zz() + 2*d[ 9]*m.xy() + 2*d[13]*m.yz() + 2*d[18]*m.xz();\n\ta.yz() = d[10]*m.xx() + d[11]*m.yy() + d[12]*m.zz() + 2*d[13]*m.xy() + 2*d[14]*m.yz() + 2*d[19]*m.xz();\n\ta.xz() = d[15]*m.xx() + d[16]*m.yy() + d[17]*m.zz() + 2*d[18]*m.xy() + 2*d[19]*m.yz() + 2*d[20]*m.xz();\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of symmetric 4th-order tensor with a general 2nd-order tensor\n// Aij = Dijkl Mkl\ninline mat3ds tens4ds::dot(const mat3d &m) const\n{\n    mat3ds a;\n    a.xx() = d[ 0]*m(0,0) + d[ 1]*m(1,1) + d[ 3]*m(2,2) + d[ 6]*(m(0,1)+m(1,0)) + d[10]*(m(1,2)+m(2,1)) + d[15]*(m(0,2)+m(2,0));\n    a.yy() = d[ 1]*m(0,0) + d[ 2]*m(1,1) + d[ 4]*m(2,2) + d[ 7]*(m(0,1)+m(1,0)) + d[11]*(m(1,2)+m(2,1)) + d[16]*(m(0,2)+m(2,0));\n    a.zz() = d[ 3]*m(0,0) + d[ 4]*m(1,1) + d[ 5]*m(2,2) + d[ 8]*(m(0,1)+m(1,0)) + d[12]*(m(1,2)+m(2,1)) + d[17]*(m(0,2)+m(2,0));\n    a.xy() = d[ 6]*m(0,0) + d[ 7]*m(1,1) + d[ 8]*m(2,2) + d[ 9]*(m(0,1)+m(1,0)) + d[13]*(m(1,2)+m(2,1)) + d[18]*(m(0,2)+m(2,0));\n    a.yz() = d[10]*m(0,0) + d[11]*m(1,1) + d[12]*m(2,2) + d[13]*(m(0,1)+m(1,0)) + d[14]*(m(1,2)+m(2,1)) + d[19]*(m(0,2)+m(2,0));\n    a.xz() = d[15]*m(0,0) + d[16]*m(1,1) + d[17]*m(2,2) + d[18]*(m(0,1)+m(1,0)) + d[19]*(m(1,2)+m(2,1)) + d[20]*(m(0,2)+m(2,0));\n    return a;\n}\n\n//-----------------------------------------------------------------------------\n// contraction of symmetric 4th-order tensor with a vector\n// Aijk = Dijkl Ml\n//\n//     / 0   1   3   6   10   15  \\   / C0000  C0011  C0022  C0001  C0012  C0002 \\\n//     |     2   4   7   11   16  |   |        C1111  C1122  C1101  C1112  C1102 |\n//     |         5   8   12   17  |   |               C2222  C2201  C2212  C2202 |\n// A = |             9   13   18  | = |                      C0101  C0112  C0102 |\n//     |                 14   19  |   |                             C1212  C1202 |\n//     \\                      20  /   \\                                    C0202 /\n//\n// [G] = [G111 G112 G113 G121 G122 G123 G131 G132 G133 G221 G222 G223 G231 G232 G233 G331 G332 G333]\n//     =    G0   G1   G2   G3   G4   G5   G6   G7   G8   G9  G10  G11  G12  G13  G14  G15  G16  G17\ninline tens3dls tens4ds::dot(const vec3d &m) const\n{\n    tens3dls a;\n    a.d[0] = d[0]*m.x + d[6]*m.y + d[15]*m.z;\n    a.d[1] = d[6]*m.x + d[1]*m.y + d[10]*m.z;\n    a.d[2] = d[15]*m.x + d[10]*m.y + d[3]*m.z;\n    a.d[3] = d[6]*m.x + d[9]*m.y + d[18]*m.z;\n    a.d[4] = d[9]*m.x + d[7]*m.y + d[13]*m.z;\n    a.d[5] = d[18]*m.x + d[13]*m.y + d[8]*m.z;\n    a.d[6] = d[15]*m.x + d[18]*m.y + d[20]*m.z;\n    a.d[7] = d[18]*m.x + d[16]*m.y + d[19]*m.z;\n    a.d[8] = d[20]*m.x + d[19]*m.y + d[17]*m.z;\n    a.d[9] = d[1]*m.x + d[7]*m.y + d[16]*m.z;\n    a.d[10] = d[7]*m.x + d[2]*m.y + d[11]*m.z;\n    a.d[11] = d[16]*m.x + d[11]*m.y + d[4]*m.z;\n    a.d[12] = d[10]*m.x + d[13]*m.y + d[19]*m.z;\n    a.d[13] = d[13]*m.x + d[11]*m.y + d[14]*m.z;\n    a.d[14] = d[19]*m.x + d[14]*m.y + d[12]*m.z;\n    a.d[15] = d[3]*m.x + d[8]*m.y + d[17]*m.z;\n    a.d[16] = d[8]*m.x + d[4]*m.y + d[12]*m.z;\n    a.d[17] = d[17]*m.x + d[12]*m.y + d[5]*m.z;\n    return a;\n}\n\n//-----------------------------------------------------------------------------\n// double contraction of symmetric 4th-order tensor with a general 2nd-order tensor (2nd kind)\n// Aij = Dikjl Mkl\ninline mat3ds tens4ds::dot2(const mat3d &m) const\n{\n    mat3ds a;\n    a.xx() = d[ 0]*m(0,0) + d[ 9]*m(1,1) + d[20]*m(2,2) + d[ 6]*(m(0,1)+m(1,0)) + d[18]*(m(1,2)+m(2,1)) + d[15]*(m(0,2)+m(2,0));\n    a.yy() = d[ 9]*m(0,0) + d[ 2]*m(1,1) + d[14]*m(2,2) + d[ 7]*(m(0,1)+m(1,0)) + d[11]*(m(1,2)+m(2,1)) + d[13]*(m(0,2)+m(2,0));\n    a.zz() = d[20]*m(0,0) + d[14]*m(1,1) + d[ 5]*m(2,2) + d[19]*(m(0,1)+m(1,0)) + d[12]*(m(1,2)+m(2,1)) + d[17]*(m(0,2)+m(2,0));\n    a.xy() = d[ 6]*m(0,0) + d[ 7]*m(1,1) + d[19]*m(2,2) + d[ 1]*(m(0,1)+m(1,0)) + d[13]*(m(1,2)+m(2,1)) + d[10]*(m(0,2)+m(2,0));\n    a.yz() = d[18]*m(0,0) + d[11]*m(1,1) + d[12]*m(2,2) + d[13]*(m(0,1)+m(1,0)) + d[ 4]*(m(1,2)+m(2,1)) + d[ 8]*(m(0,2)+m(2,0));\n    a.xz() = d[15]*m(0,0) + d[13]*m(1,1) + d[17]*m(2,2) + d[10]*(m(0,1)+m(1,0)) + d[ 8]*(m(1,2)+m(2,1)) + d[ 3]*(m(0,2)+m(2,0));\n    return a;\n}\n\n// contraction\n// Aij = c_qqij = c_ijqq\ninline mat3ds tens4ds::contract() const\n{\n\tmat3ds a;\n\ta.xx() = d[ 0] + d[ 1] + d[ 3];\n\ta.yy() = d[ 1] + d[ 2] + d[ 4];\n\ta.zz() = d[ 3] + d[ 4] + d[ 5];\n\ta.xy() = d[ 6] + d[ 7] + d[ 8];\n\ta.yz() = d[10] + d[11] + d[12];\n\ta.xz() = d[15] + d[16] + d[17];\n\treturn a;\n}\n\n//-----------------------------------------------------------------------------\n// vdotTdotv_jk = a_i T_ijkl b_l\ninline mat3d vdotTdotv(const vec3d& a, const tens4ds& T, const vec3d& b)\n{\n\treturn mat3d(a.x*(b.x*T.d[0] + b.y*T.d[6] + b.z*T.d[15]) + a.y*(b.x*T.d[6] + b.y*T.d[9] + b.z*T.d[18]) + a.z*(b.x*T.d[15] + b.y*T.d[18] + b.z*T.d[20]),\n\t\t\t\t a.x*(b.y*T.d[1] + b.x*T.d[6] + b.z*T.d[10]) + a.y*(b.y*T.d[7] + b.x*T.d[9] + b.z*T.d[13]) + a.z*(b.y*T.d[16] + b.x*T.d[18] + b.z*T.d[19]),\n\t\t\t\t a.x*(b.z*T.d[3] + b.y*T.d[10] + b.x*T.d[15]) + a.y*(b.z*T.d[8] + b.y*T.d[13] + b.x*T.d[18]) + a.z*(b.z*T.d[17] + b.y*T.d[19] + b.x*T.d[20]),\n\t\t\t\t a.y*(b.x*T.d[1] + b.y*T.d[7] + b.z*T.d[16]) + a.x*(b.x*T.d[6] + b.y*T.d[9] + b.z*T.d[18]) + a.z*(b.x*T.d[10] + b.y*T.d[13] + b.z*T.d[19]),\n\t\t\t\t a.y*(b.y*T.d[2] + b.x*T.d[7] + b.z*T.d[11]) + a.x*(b.y*T.d[7] + b.x*T.d[9] + b.z*T.d[13]) + a.z*(b.y*T.d[11] + b.x*T.d[13] + b.z*T.d[14]),\n\t\t\t\t a.y*(b.z*T.d[4] + b.y*T.d[11] + b.x*T.d[16]) + a.x*(b.z*T.d[8] + b.y*T.d[13] + b.x*T.d[18]) + a.z*(b.z*T.d[12] + b.y*T.d[14] + b.x*T.d[19]),\n\t\t\t\t a.z*(b.x*T.d[3] + b.y*T.d[8] + b.z*T.d[17]) + a.y*(b.x*T.d[10] + b.y*T.d[13] + b.z*T.d[19]) + a.x*(b.x*T.d[15] + b.y*T.d[18] + b.z*T.d[20]),\n\t\t\t\t a.z*(b.y*T.d[4] + b.x*T.d[8] + b.z*T.d[12]) + a.y*(b.y*T.d[11] + b.x*T.d[13] + b.z*T.d[14]) + a.x*(b.y*T.d[16] + b.x*T.d[18] + b.z*T.d[19]),\n\t\t\t\t a.z*(b.z*T.d[5] + b.y*T.d[12] + b.x*T.d[17]) + a.y*(b.z*T.d[12] + b.y*T.d[14] + b.x*T.d[19]) + a.x*(b.z*T.d[17] + b.y*T.d[19] + b.x*T.d[20]));\n}\n\n\n//-----------------------------------------------------------------------------\n// inverse\ninline tens4ds tens4ds::inverse() const\n{\n\tmatrix c(6,6);\n\t\n\t// populate c\n\tc(0,0) = d[0];\n\tc(1,1) = d[2];\n\tc(2,2) = d[5];\n\tc(3,3) = d[9];\n\tc(4,4) = d[14];\n\tc(5,5) = d[20];\n\tc(0,1) = c(1,0) = d[1];\n\tc(0,2) = c(2,0) = d[3];\n\tc(0,3) = c(3,0) = d[6];\n\tc(0,4) = c(4,0) = d[10];\n\tc(0,5) = c(5,0) = d[15];\n\tc(1,2) = c(2,1) = d[4];\n\tc(1,3) = c(3,1) = d[7];\n\tc(1,4) = c(4,1) = d[11];\n\tc(1,5) = c(5,1) = d[16];\n\tc(2,3) = c(3,2) = d[8];\n\tc(2,4) = c(4,2) = d[12];\n\tc(2,5) = c(5,2) = d[17];\n\tc(3,4) = c(4,3) = d[13];\n\tc(3,5) = c(5,3) = d[18];\n\tc(4,5) = c(5,4) = d[19];\n\t\n\t// invert c\n\tmatrix s = c.inverse();\n\t\n\t// return inverse\n\ttens4ds S;\n\tS.d[ 0] = s(0,0);\n\tS.d[ 2] = s(1,1);\n\tS.d[ 5] = s(2,2);\n\tS.d[ 9] = s(3,3);\n\tS.d[14] = s(4,4);\n\tS.d[20] = s(5,5);\n\tS.d[ 1] = s(0,1);\n\tS.d[ 3] = s(0,2);\n\tS.d[ 6] = s(0,3);\n\tS.d[10] = s(0,4);\n\tS.d[15] = s(0,5);\n\tS.d[ 4] = s(1,2);\n\tS.d[ 7] = s(1,3);\n\tS.d[11] = s(1,4);\n\tS.d[16] = s(1,5);\n\tS.d[ 8] = s(2,3);\n\tS.d[12] = s(2,4);\n\tS.d[17] = s(2,5);\n\tS.d[13] = s(3,4);\n\tS.d[18] = s(3,5);\n\tS.d[19] = s(4,5);\n\t\n\treturn S;\n}\n\n//-----------------------------------------------------------------------------\n// evaluate push/pull operation\n// c_ijpq = F_ik F_jl C_klmn F_pm F_qn\ninline tens4ds tens4ds::pp(const mat3d& F)\n{\n    tens4ds c;\n\n    c.d[0] = d[0]*pow(F(0,0),4) + 4*d[6]*pow(F(0,0),3)*F(0,1) +\n    2*d[1]*pow(F(0,0),2)*pow(F(0,1),2) + 4*d[9]*pow(F(0,0),2)*pow(F(0,1),2) +\n    4*d[7]*F(0,0)*pow(F(0,1),3) + d[2]*pow(F(0,1),4) +\n    4*d[15]*pow(F(0,0),3)*F(0,2) + 4*d[10]*pow(F(0,0),2)*F(0,1)*F(0,2) +\n    8*d[18]*pow(F(0,0),2)*F(0,1)*F(0,2) + 8*d[13]*F(0,0)*pow(F(0,1),2)*F(0,2) +\n    4*d[16]*F(0,0)*pow(F(0,1),2)*F(0,2) + 4*d[11]*pow(F(0,1),3)*F(0,2) +\n    2*d[3]*pow(F(0,0),2)*pow(F(0,2),2) + 4*d[20]*pow(F(0,0),2)*pow(F(0,2),2) +\n    4*d[8]*F(0,0)*F(0,1)*pow(F(0,2),2) + 8*d[19]*F(0,0)*F(0,1)*pow(F(0,2),2) +\n    2*d[4]*pow(F(0,1),2)*pow(F(0,2),2) + 4*d[14]*pow(F(0,1),2)*pow(F(0,2),2) +\n    4*d[17]*F(0,0)*pow(F(0,2),3) + 4*d[12]*F(0,1)*pow(F(0,2),3) +\n    d[5]*pow(F(0,2),4);\n    \n    c.d[1] = d[0]*pow(F(0,0),2)*pow(F(1,0),2) + d[1]*pow(F(0,1),2)*pow(F(1,0),2) +\n    2*d[15]*F(0,0)*F(0,2)*pow(F(1,0),2) + 2*d[10]*F(0,1)*F(0,2)*pow(F(1,0),2) +\n    d[3]*pow(F(0,2),2)*pow(F(1,0),2) + 4*d[9]*F(0,0)*F(0,1)*F(1,0)*F(1,1) +\n    2*d[7]*pow(F(0,1),2)*F(1,0)*F(1,1) + 4*d[18]*F(0,0)*F(0,2)*F(1,0)*F(1,1) +\n    4*d[13]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + 2*d[8]*pow(F(0,2),2)*F(1,0)*F(1,1) +\n    d[1]*pow(F(0,0),2)*pow(F(1,1),2) + 2*d[7]*F(0,0)*F(0,1)*pow(F(1,1),2) +\n    d[2]*pow(F(0,1),2)*pow(F(1,1),2) + 2*d[16]*F(0,0)*F(0,2)*pow(F(1,1),2) +\n    2*d[11]*F(0,1)*F(0,2)*pow(F(1,1),2) + d[4]*pow(F(0,2),2)*pow(F(1,1),2) +\n    2*d[6]*F(0,0)*F(1,0)*(F(0,1)*F(1,0) + F(0,0)*F(1,1)) +\n    2*d[15]*pow(F(0,0),2)*F(1,0)*F(1,2) + 4*d[18]*F(0,0)*F(0,1)*F(1,0)*F(1,2) +\n    2*d[16]*pow(F(0,1),2)*F(1,0)*F(1,2) + 4*d[20]*F(0,0)*F(0,2)*F(1,0)*F(1,2) +\n    4*d[19]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + 2*d[17]*pow(F(0,2),2)*F(1,0)*F(1,2) +\n    2*d[10]*pow(F(0,0),2)*F(1,1)*F(1,2) + 4*d[13]*F(0,0)*F(0,1)*F(1,1)*F(1,2) +\n    2*d[11]*pow(F(0,1),2)*F(1,1)*F(1,2) + 4*d[19]*F(0,0)*F(0,2)*F(1,1)*F(1,2) +\n    4*d[14]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + 2*d[12]*pow(F(0,2),2)*F(1,1)*F(1,2) +\n    d[3]*pow(F(0,0),2)*pow(F(1,2),2) + 2*d[8]*F(0,0)*F(0,1)*pow(F(1,2),2) +\n    d[4]*pow(F(0,1),2)*pow(F(1,2),2) + 2*d[17]*F(0,0)*F(0,2)*pow(F(1,2),2) +\n    2*d[12]*F(0,1)*F(0,2)*pow(F(1,2),2) + d[5]*pow(F(0,2),2)*pow(F(1,2),2);\n    \n    c.d[2] = d[0]*pow(F(1,0),4) + 4*d[6]*pow(F(1,0),3)*F(1,1) +\n    2*d[1]*pow(F(1,0),2)*pow(F(1,1),2) + 4*d[9]*pow(F(1,0),2)*pow(F(1,1),2) +\n    4*d[7]*F(1,0)*pow(F(1,1),3) + d[2]*pow(F(1,1),4) +\n    4*d[15]*pow(F(1,0),3)*F(1,2) + 4*d[10]*pow(F(1,0),2)*F(1,1)*F(1,2) +\n    8*d[18]*pow(F(1,0),2)*F(1,1)*F(1,2) + 8*d[13]*F(1,0)*pow(F(1,1),2)*F(1,2) +\n    4*d[16]*F(1,0)*pow(F(1,1),2)*F(1,2) + 4*d[11]*pow(F(1,1),3)*F(1,2) +\n    2*d[3]*pow(F(1,0),2)*pow(F(1,2),2) + 4*d[20]*pow(F(1,0),2)*pow(F(1,2),2) +\n    4*d[8]*F(1,0)*F(1,1)*pow(F(1,2),2) + 8*d[19]*F(1,0)*F(1,1)*pow(F(1,2),2) +\n    2*d[4]*pow(F(1,1),2)*pow(F(1,2),2) + 4*d[14]*pow(F(1,1),2)*pow(F(1,2),2) +\n    4*d[17]*F(1,0)*pow(F(1,2),3) + 4*d[12]*F(1,1)*pow(F(1,2),3) +\n    d[5]*pow(F(1,2),4);\n    \n    c.d[3] = d[0]*pow(F(0,0),2)*pow(F(2,0),2) + d[1]*pow(F(0,1),2)*pow(F(2,0),2) +\n    2*d[15]*F(0,0)*F(0,2)*pow(F(2,0),2) + 2*d[10]*F(0,1)*F(0,2)*pow(F(2,0),2) +\n    d[3]*pow(F(0,2),2)*pow(F(2,0),2) + 4*d[9]*F(0,0)*F(0,1)*F(2,0)*F(2,1) +\n    2*d[7]*pow(F(0,1),2)*F(2,0)*F(2,1) + 4*d[18]*F(0,0)*F(0,2)*F(2,0)*F(2,1) +\n    4*d[13]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + 2*d[8]*pow(F(0,2),2)*F(2,0)*F(2,1) +\n    d[1]*pow(F(0,0),2)*pow(F(2,1),2) + 2*d[7]*F(0,0)*F(0,1)*pow(F(2,1),2) +\n    d[2]*pow(F(0,1),2)*pow(F(2,1),2) + 2*d[16]*F(0,0)*F(0,2)*pow(F(2,1),2) +\n    2*d[11]*F(0,1)*F(0,2)*pow(F(2,1),2) + d[4]*pow(F(0,2),2)*pow(F(2,1),2) +\n    2*d[6]*F(0,0)*F(2,0)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) +\n    2*d[15]*pow(F(0,0),2)*F(2,0)*F(2,2) + 4*d[18]*F(0,0)*F(0,1)*F(2,0)*F(2,2) +\n    2*d[16]*pow(F(0,1),2)*F(2,0)*F(2,2) + 4*d[20]*F(0,0)*F(0,2)*F(2,0)*F(2,2) +\n    4*d[19]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + 2*d[17]*pow(F(0,2),2)*F(2,0)*F(2,2) +\n    2*d[10]*pow(F(0,0),2)*F(2,1)*F(2,2) + 4*d[13]*F(0,0)*F(0,1)*F(2,1)*F(2,2) +\n    2*d[11]*pow(F(0,1),2)*F(2,1)*F(2,2) + 4*d[19]*F(0,0)*F(0,2)*F(2,1)*F(2,2) +\n    4*d[14]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + 2*d[12]*pow(F(0,2),2)*F(2,1)*F(2,2) +\n    d[3]*pow(F(0,0),2)*pow(F(2,2),2) + 2*d[8]*F(0,0)*F(0,1)*pow(F(2,2),2) +\n    d[4]*pow(F(0,1),2)*pow(F(2,2),2) + 2*d[17]*F(0,0)*F(0,2)*pow(F(2,2),2) +\n    2*d[12]*F(0,1)*F(0,2)*pow(F(2,2),2) + d[5]*pow(F(0,2),2)*pow(F(2,2),2);\n    \n    c.d[4] = d[0]*pow(F(1,0),2)*pow(F(2,0),2) + d[1]*pow(F(1,1),2)*pow(F(2,0),2) +\n    2*d[15]*F(1,0)*F(1,2)*pow(F(2,0),2) + 2*d[10]*F(1,1)*F(1,2)*pow(F(2,0),2) +\n    d[3]*pow(F(1,2),2)*pow(F(2,0),2) + 4*d[9]*F(1,0)*F(1,1)*F(2,0)*F(2,1) +\n    2*d[7]*pow(F(1,1),2)*F(2,0)*F(2,1) + 4*d[18]*F(1,0)*F(1,2)*F(2,0)*F(2,1) +\n    4*d[13]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + 2*d[8]*pow(F(1,2),2)*F(2,0)*F(2,1) +\n    d[1]*pow(F(1,0),2)*pow(F(2,1),2) + 2*d[7]*F(1,0)*F(1,1)*pow(F(2,1),2) +\n    d[2]*pow(F(1,1),2)*pow(F(2,1),2) + 2*d[16]*F(1,0)*F(1,2)*pow(F(2,1),2) +\n    2*d[11]*F(1,1)*F(1,2)*pow(F(2,1),2) + d[4]*pow(F(1,2),2)*pow(F(2,1),2) +\n    2*d[6]*F(1,0)*F(2,0)*(F(1,1)*F(2,0) + F(1,0)*F(2,1)) +\n    2*d[15]*pow(F(1,0),2)*F(2,0)*F(2,2) + 4*d[18]*F(1,0)*F(1,1)*F(2,0)*F(2,2) +\n    2*d[16]*pow(F(1,1),2)*F(2,0)*F(2,2) + 4*d[20]*F(1,0)*F(1,2)*F(2,0)*F(2,2) +\n    4*d[19]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + 2*d[17]*pow(F(1,2),2)*F(2,0)*F(2,2) +\n    2*d[10]*pow(F(1,0),2)*F(2,1)*F(2,2) + 4*d[13]*F(1,0)*F(1,1)*F(2,1)*F(2,2) +\n    2*d[11]*pow(F(1,1),2)*F(2,1)*F(2,2) + 4*d[19]*F(1,0)*F(1,2)*F(2,1)*F(2,2) +\n    4*d[14]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[12]*pow(F(1,2),2)*F(2,1)*F(2,2) +\n    d[3]*pow(F(1,0),2)*pow(F(2,2),2) + 2*d[8]*F(1,0)*F(1,1)*pow(F(2,2),2) +\n    d[4]*pow(F(1,1),2)*pow(F(2,2),2) + 2*d[17]*F(1,0)*F(1,2)*pow(F(2,2),2) +\n    2*d[12]*F(1,1)*F(1,2)*pow(F(2,2),2) + d[5]*pow(F(1,2),2)*pow(F(2,2),2);\n    \n    c.d[5] = d[0]*pow(F(2,0),4) + 4*d[6]*pow(F(2,0),3)*F(2,1) +\n    2*d[1]*pow(F(2,0),2)*pow(F(2,1),2) + 4*d[9]*pow(F(2,0),2)*pow(F(2,1),2) +\n    4*d[7]*F(2,0)*pow(F(2,1),3) + d[2]*pow(F(2,1),4) +\n    4*d[15]*pow(F(2,0),3)*F(2,2) + 4*d[10]*pow(F(2,0),2)*F(2,1)*F(2,2) +\n    8*d[18]*pow(F(2,0),2)*F(2,1)*F(2,2) + 8*d[13]*F(2,0)*pow(F(2,1),2)*F(2,2) +\n    4*d[16]*F(2,0)*pow(F(2,1),2)*F(2,2) + 4*d[11]*pow(F(2,1),3)*F(2,2) +\n    2*d[3]*pow(F(2,0),2)*pow(F(2,2),2) + 4*d[20]*pow(F(2,0),2)*pow(F(2,2),2) +\n    4*d[8]*F(2,0)*F(2,1)*pow(F(2,2),2) + 8*d[19]*F(2,0)*F(2,1)*pow(F(2,2),2) +\n    2*d[4]*pow(F(2,1),2)*pow(F(2,2),2) + 4*d[14]*pow(F(2,1),2)*pow(F(2,2),2) +\n    4*d[17]*F(2,0)*pow(F(2,2),3) + 4*d[12]*F(2,1)*pow(F(2,2),3) +\n    d[5]*pow(F(2,2),4);\n    \n    c.d[6] = d[0]*pow(F(0,0),3)*F(1,0) + d[1]*F(0,0)*pow(F(0,1),2)*F(1,0) +\n    2*d[9]*F(0,0)*pow(F(0,1),2)*F(1,0) + d[7]*pow(F(0,1),3)*F(1,0) +\n    3*d[15]*pow(F(0,0),2)*F(0,2)*F(1,0) + 2*d[10]*F(0,0)*F(0,1)*F(0,2)*F(1,0) +\n    4*d[18]*F(0,0)*F(0,1)*F(0,2)*F(1,0) + 2*d[13]*pow(F(0,1),2)*F(0,2)*F(1,0) +\n    d[16]*pow(F(0,1),2)*F(0,2)*F(1,0) + d[3]*F(0,0)*pow(F(0,2),2)*F(1,0) +\n    2*d[20]*F(0,0)*pow(F(0,2),2)*F(1,0) + d[8]*F(0,1)*pow(F(0,2),2)*F(1,0) +\n    2*d[19]*F(0,1)*pow(F(0,2),2)*F(1,0) + d[17]*pow(F(0,2),3)*F(1,0) +\n    d[1]*pow(F(0,0),2)*F(0,1)*F(1,1) + 2*d[9]*pow(F(0,0),2)*F(0,1)*F(1,1) +\n    3*d[7]*F(0,0)*pow(F(0,1),2)*F(1,1) + d[2]*pow(F(0,1),3)*F(1,1) +\n    d[10]*pow(F(0,0),2)*F(0,2)*F(1,1) + 2*d[18]*pow(F(0,0),2)*F(0,2)*F(1,1) +\n    4*d[13]*F(0,0)*F(0,1)*F(0,2)*F(1,1) + 2*d[16]*F(0,0)*F(0,1)*F(0,2)*F(1,1) +\n    3*d[11]*pow(F(0,1),2)*F(0,2)*F(1,1) + d[8]*F(0,0)*pow(F(0,2),2)*F(1,1) +\n    2*d[19]*F(0,0)*pow(F(0,2),2)*F(1,1) + d[4]*F(0,1)*pow(F(0,2),2)*F(1,1) +\n    2*d[14]*F(0,1)*pow(F(0,2),2)*F(1,1) + d[12]*pow(F(0,2),3)*F(1,1) +\n    d[6]*pow(F(0,0),2)*(3*F(0,1)*F(1,0) + F(0,0)*F(1,1)) +\n    d[15]*pow(F(0,0),3)*F(1,2) + d[10]*pow(F(0,0),2)*F(0,1)*F(1,2) +\n    2*d[18]*pow(F(0,0),2)*F(0,1)*F(1,2) + 2*d[13]*F(0,0)*pow(F(0,1),2)*F(1,2) +\n    d[16]*F(0,0)*pow(F(0,1),2)*F(1,2) + d[11]*pow(F(0,1),3)*F(1,2) +\n    d[3]*pow(F(0,0),2)*F(0,2)*F(1,2) + 2*d[20]*pow(F(0,0),2)*F(0,2)*F(1,2) +\n    2*d[8]*F(0,0)*F(0,1)*F(0,2)*F(1,2) + 4*d[19]*F(0,0)*F(0,1)*F(0,2)*F(1,2) +\n    d[4]*pow(F(0,1),2)*F(0,2)*F(1,2) + 2*d[14]*pow(F(0,1),2)*F(0,2)*F(1,2) +\n    3*d[17]*F(0,0)*pow(F(0,2),2)*F(1,2) + 3*d[12]*F(0,1)*pow(F(0,2),2)*F(1,2) +\n    d[5]*pow(F(0,2),3)*F(1,2);\n    \n    c.d[7] = d[0]*F(0,0)*pow(F(1,0),3) + d[15]*F(0,2)*pow(F(1,0),3) +\n    d[1]*F(0,1)*pow(F(1,0),2)*F(1,1) + 2*d[9]*F(0,1)*pow(F(1,0),2)*F(1,1) +\n    d[10]*F(0,2)*pow(F(1,0),2)*F(1,1) + 2*d[18]*F(0,2)*pow(F(1,0),2)*F(1,1) +\n    d[1]*F(0,0)*F(1,0)*pow(F(1,1),2) + 2*d[9]*F(0,0)*F(1,0)*pow(F(1,1),2) +\n    3*d[7]*F(0,1)*F(1,0)*pow(F(1,1),2) + 2*d[13]*F(0,2)*F(1,0)*pow(F(1,1),2) +\n    d[16]*F(0,2)*F(1,0)*pow(F(1,1),2) + d[7]*F(0,0)*pow(F(1,1),3) +\n    d[2]*F(0,1)*pow(F(1,1),3) + d[11]*F(0,2)*pow(F(1,1),3) +\n    d[6]*pow(F(1,0),2)*(F(0,1)*F(1,0) + 3*F(0,0)*F(1,1)) +\n    3*d[15]*F(0,0)*pow(F(1,0),2)*F(1,2) + d[10]*F(0,1)*pow(F(1,0),2)*F(1,2) +\n    2*d[18]*F(0,1)*pow(F(1,0),2)*F(1,2) + d[3]*F(0,2)*pow(F(1,0),2)*F(1,2) +\n    2*d[20]*F(0,2)*pow(F(1,0),2)*F(1,2) + 2*d[10]*F(0,0)*F(1,0)*F(1,1)*F(1,2) +\n    4*d[18]*F(0,0)*F(1,0)*F(1,1)*F(1,2) + 4*d[13]*F(0,1)*F(1,0)*F(1,1)*F(1,2) +\n    2*d[16]*F(0,1)*F(1,0)*F(1,1)*F(1,2) + 2*d[8]*F(0,2)*F(1,0)*F(1,1)*F(1,2) +\n    4*d[19]*F(0,2)*F(1,0)*F(1,1)*F(1,2) + 2*d[13]*F(0,0)*pow(F(1,1),2)*F(1,2) +\n    d[16]*F(0,0)*pow(F(1,1),2)*F(1,2) + 3*d[11]*F(0,1)*pow(F(1,1),2)*F(1,2) +\n    d[4]*F(0,2)*pow(F(1,1),2)*F(1,2) + 2*d[14]*F(0,2)*pow(F(1,1),2)*F(1,2) +\n    d[3]*F(0,0)*F(1,0)*pow(F(1,2),2) + 2*d[20]*F(0,0)*F(1,0)*pow(F(1,2),2) +\n    d[8]*F(0,1)*F(1,0)*pow(F(1,2),2) + 2*d[19]*F(0,1)*F(1,0)*pow(F(1,2),2) +\n    3*d[17]*F(0,2)*F(1,0)*pow(F(1,2),2) + d[8]*F(0,0)*F(1,1)*pow(F(1,2),2) +\n    2*d[19]*F(0,0)*F(1,1)*pow(F(1,2),2) + d[4]*F(0,1)*F(1,1)*pow(F(1,2),2) +\n    2*d[14]*F(0,1)*F(1,1)*pow(F(1,2),2) + 3*d[12]*F(0,2)*F(1,1)*pow(F(1,2),2) +\n    d[17]*F(0,0)*pow(F(1,2),3) + d[12]*F(0,1)*pow(F(1,2),3) +\n    d[5]*F(0,2)*pow(F(1,2),3);\n    \n    c.d[8] = d[0]*F(0,0)*F(1,0)*pow(F(2,0),2) + d[15]*F(0,2)*F(1,0)*pow(F(2,0),2) +\n    d[1]*F(0,1)*F(1,1)*pow(F(2,0),2) + d[10]*F(0,2)*F(1,1)*pow(F(2,0),2) +\n    d[15]*F(0,0)*F(1,2)*pow(F(2,0),2) + d[10]*F(0,1)*F(1,2)*pow(F(2,0),2) +\n    d[3]*F(0,2)*F(1,2)*pow(F(2,0),2) + 2*d[9]*F(0,1)*F(1,0)*F(2,0)*F(2,1) +\n    2*d[18]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + 2*d[9]*F(0,0)*F(1,1)*F(2,0)*F(2,1) +\n    2*d[7]*F(0,1)*F(1,1)*F(2,0)*F(2,1) + 2*d[13]*F(0,2)*F(1,1)*F(2,0)*F(2,1) +\n    2*d[18]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + 2*d[13]*F(0,1)*F(1,2)*F(2,0)*F(2,1) +\n    2*d[8]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[1]*F(0,0)*F(1,0)*pow(F(2,1),2) +\n    d[7]*F(0,1)*F(1,0)*pow(F(2,1),2) + d[16]*F(0,2)*F(1,0)*pow(F(2,1),2) +\n    d[7]*F(0,0)*F(1,1)*pow(F(2,1),2) + d[2]*F(0,1)*F(1,1)*pow(F(2,1),2) +\n    d[11]*F(0,2)*F(1,1)*pow(F(2,1),2) + d[16]*F(0,0)*F(1,2)*pow(F(2,1),2) +\n    d[11]*F(0,1)*F(1,2)*pow(F(2,1),2) + d[4]*F(0,2)*F(1,2)*pow(F(2,1),2) +\n    d[6]*F(2,0)*(F(0,1)*F(1,0)*F(2,0) + F(0,0)*(F(1,1)*F(2,0) + 2*F(1,0)*F(2,1))) +\n    2*d[15]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + 2*d[18]*F(0,1)*F(1,0)*F(2,0)*F(2,2) +\n    2*d[20]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + 2*d[18]*F(0,0)*F(1,1)*F(2,0)*F(2,2) +\n    2*d[16]*F(0,1)*F(1,1)*F(2,0)*F(2,2) + 2*d[19]*F(0,2)*F(1,1)*F(2,0)*F(2,2) +\n    2*d[20]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + 2*d[19]*F(0,1)*F(1,2)*F(2,0)*F(2,2) +\n    2*d[17]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + 2*d[10]*F(0,0)*F(1,0)*F(2,1)*F(2,2) +\n    2*d[13]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + 2*d[19]*F(0,2)*F(1,0)*F(2,1)*F(2,2) +\n    2*d[13]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + 2*d[11]*F(0,1)*F(1,1)*F(2,1)*F(2,2) +\n    2*d[14]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + 2*d[19]*F(0,0)*F(1,2)*F(2,1)*F(2,2) +\n    2*d[14]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[12]*F(0,2)*F(1,2)*F(2,1)*F(2,2) +\n    d[3]*F(0,0)*F(1,0)*pow(F(2,2),2) + d[8]*F(0,1)*F(1,0)*pow(F(2,2),2) +\n    d[17]*F(0,2)*F(1,0)*pow(F(2,2),2) + d[8]*F(0,0)*F(1,1)*pow(F(2,2),2) +\n    d[4]*F(0,1)*F(1,1)*pow(F(2,2),2) + d[12]*F(0,2)*F(1,1)*pow(F(2,2),2) +\n    d[17]*F(0,0)*F(1,2)*pow(F(2,2),2) + d[12]*F(0,1)*F(1,2)*pow(F(2,2),2) +\n    d[5]*F(0,2)*F(1,2)*pow(F(2,2),2);\n    \n    c.d[9] = d[0]*pow(F(0,0),2)*pow(F(1,0),2) + d[9]*pow(F(0,1),2)*pow(F(1,0),2) +\n    2*d[15]*F(0,0)*F(0,2)*pow(F(1,0),2) + 2*d[18]*F(0,1)*F(0,2)*pow(F(1,0),2) +\n    d[20]*pow(F(0,2),2)*pow(F(1,0),2) + 2*d[1]*F(0,0)*F(0,1)*F(1,0)*F(1,1) +\n    2*d[9]*F(0,0)*F(0,1)*F(1,0)*F(1,1) + 2*d[7]*pow(F(0,1),2)*F(1,0)*F(1,1) +\n    2*d[10]*F(0,0)*F(0,2)*F(1,0)*F(1,1) + 2*d[18]*F(0,0)*F(0,2)*F(1,0)*F(1,1) +\n    2*d[13]*F(0,1)*F(0,2)*F(1,0)*F(1,1) + 2*d[16]*F(0,1)*F(0,2)*F(1,0)*F(1,1) +\n    2*d[19]*pow(F(0,2),2)*F(1,0)*F(1,1) + d[9]*pow(F(0,0),2)*pow(F(1,1),2) +\n    2*d[7]*F(0,0)*F(0,1)*pow(F(1,1),2) + d[2]*pow(F(0,1),2)*pow(F(1,1),2) +\n    2*d[13]*F(0,0)*F(0,2)*pow(F(1,1),2) + 2*d[11]*F(0,1)*F(0,2)*pow(F(1,1),2) +\n    d[14]*pow(F(0,2),2)*pow(F(1,1),2) +\n    2*d[6]*F(0,0)*F(1,0)*(F(0,1)*F(1,0) + F(0,0)*F(1,1)) +\n    2*d[15]*pow(F(0,0),2)*F(1,0)*F(1,2) + 2*d[10]*F(0,0)*F(0,1)*F(1,0)*F(1,2) +\n    2*d[18]*F(0,0)*F(0,1)*F(1,0)*F(1,2) + 2*d[13]*pow(F(0,1),2)*F(1,0)*F(1,2) +\n    2*d[3]*F(0,0)*F(0,2)*F(1,0)*F(1,2) + 2*d[20]*F(0,0)*F(0,2)*F(1,0)*F(1,2) +\n    2*d[8]*F(0,1)*F(0,2)*F(1,0)*F(1,2) + 2*d[19]*F(0,1)*F(0,2)*F(1,0)*F(1,2) +\n    2*d[17]*pow(F(0,2),2)*F(1,0)*F(1,2) + 2*d[18]*pow(F(0,0),2)*F(1,1)*F(1,2) +\n    2*d[13]*F(0,0)*F(0,1)*F(1,1)*F(1,2) + 2*d[16]*F(0,0)*F(0,1)*F(1,1)*F(1,2) +\n    2*d[11]*pow(F(0,1),2)*F(1,1)*F(1,2) + 2*d[8]*F(0,0)*F(0,2)*F(1,1)*F(1,2) +\n    2*d[19]*F(0,0)*F(0,2)*F(1,1)*F(1,2) + 2*d[4]*F(0,1)*F(0,2)*F(1,1)*F(1,2) +\n    2*d[14]*F(0,1)*F(0,2)*F(1,1)*F(1,2) + 2*d[12]*pow(F(0,2),2)*F(1,1)*F(1,2) +\n    d[20]*pow(F(0,0),2)*pow(F(1,2),2) + 2*d[19]*F(0,0)*F(0,1)*pow(F(1,2),2) +\n    d[14]*pow(F(0,1),2)*pow(F(1,2),2) + 2*d[17]*F(0,0)*F(0,2)*pow(F(1,2),2) +\n    2*d[12]*F(0,1)*F(0,2)*pow(F(1,2),2) + d[5]*pow(F(0,2),2)*pow(F(1,2),2);\n    \n    c.d[10] = d[0]*pow(F(0,0),2)*F(1,0)*F(2,0) + d[1]*pow(F(0,1),2)*F(1,0)*F(2,0) +\n    2*d[15]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + 2*d[10]*F(0,1)*F(0,2)*F(1,0)*F(2,0) +\n    d[3]*pow(F(0,2),2)*F(1,0)*F(2,0) + 2*d[9]*F(0,0)*F(0,1)*F(1,1)*F(2,0) +\n    d[7]*pow(F(0,1),2)*F(1,1)*F(2,0) + 2*d[18]*F(0,0)*F(0,2)*F(1,1)*F(2,0) +\n    2*d[13]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[8]*pow(F(0,2),2)*F(1,1)*F(2,0) +\n    d[15]*pow(F(0,0),2)*F(1,2)*F(2,0) + 2*d[18]*F(0,0)*F(0,1)*F(1,2)*F(2,0) +\n    d[16]*pow(F(0,1),2)*F(1,2)*F(2,0) + 2*d[20]*F(0,0)*F(0,2)*F(1,2)*F(2,0) +\n    2*d[19]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[17]*pow(F(0,2),2)*F(1,2)*F(2,0) +\n    2*d[9]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[7]*pow(F(0,1),2)*F(1,0)*F(2,1) +\n    2*d[18]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + 2*d[13]*F(0,1)*F(0,2)*F(1,0)*F(2,1) +\n    d[8]*pow(F(0,2),2)*F(1,0)*F(2,1) + d[1]*pow(F(0,0),2)*F(1,1)*F(2,1) +\n    2*d[7]*F(0,0)*F(0,1)*F(1,1)*F(2,1) + d[2]*pow(F(0,1),2)*F(1,1)*F(2,1) +\n    2*d[16]*F(0,0)*F(0,2)*F(1,1)*F(2,1) + 2*d[11]*F(0,1)*F(0,2)*F(1,1)*F(2,1) +\n    d[4]*pow(F(0,2),2)*F(1,1)*F(2,1) + d[10]*pow(F(0,0),2)*F(1,2)*F(2,1) +\n    2*d[13]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[11]*pow(F(0,1),2)*F(1,2)*F(2,1) +\n    2*d[19]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + 2*d[14]*F(0,1)*F(0,2)*F(1,2)*F(2,1) +\n    d[12]*pow(F(0,2),2)*F(1,2)*F(2,1) +\n    d[6]*F(0,0)*(2*F(0,1)*F(1,0)*F(2,0) + F(0,0)*(F(1,1)*F(2,0) + F(1,0)*F(2,1))) +\n    d[15]*pow(F(0,0),2)*F(1,0)*F(2,2) + 2*d[18]*F(0,0)*F(0,1)*F(1,0)*F(2,2) +\n    d[16]*pow(F(0,1),2)*F(1,0)*F(2,2) + 2*d[20]*F(0,0)*F(0,2)*F(1,0)*F(2,2) +\n    2*d[19]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[17]*pow(F(0,2),2)*F(1,0)*F(2,2) +\n    d[10]*pow(F(0,0),2)*F(1,1)*F(2,2) + 2*d[13]*F(0,0)*F(0,1)*F(1,1)*F(2,2) +\n    d[11]*pow(F(0,1),2)*F(1,1)*F(2,2) + 2*d[19]*F(0,0)*F(0,2)*F(1,1)*F(2,2) +\n    2*d[14]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[12]*pow(F(0,2),2)*F(1,1)*F(2,2) +\n    d[3]*pow(F(0,0),2)*F(1,2)*F(2,2) + 2*d[8]*F(0,0)*F(0,1)*F(1,2)*F(2,2) +\n    d[4]*pow(F(0,1),2)*F(1,2)*F(2,2) + 2*d[17]*F(0,0)*F(0,2)*F(1,2)*F(2,2) +\n    2*d[12]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[5]*pow(F(0,2),2)*F(1,2)*F(2,2);\n    \n    c.d[11] = d[0]*pow(F(1,0),3)*F(2,0) + d[1]*F(1,0)*pow(F(1,1),2)*F(2,0) +\n    2*d[9]*F(1,0)*pow(F(1,1),2)*F(2,0) + d[7]*pow(F(1,1),3)*F(2,0) +\n    3*d[15]*pow(F(1,0),2)*F(1,2)*F(2,0) + 2*d[10]*F(1,0)*F(1,1)*F(1,2)*F(2,0) +\n    4*d[18]*F(1,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[13]*pow(F(1,1),2)*F(1,2)*F(2,0) +\n    d[16]*pow(F(1,1),2)*F(1,2)*F(2,0) + d[3]*F(1,0)*pow(F(1,2),2)*F(2,0) +\n    2*d[20]*F(1,0)*pow(F(1,2),2)*F(2,0) + d[8]*F(1,1)*pow(F(1,2),2)*F(2,0) +\n    2*d[19]*F(1,1)*pow(F(1,2),2)*F(2,0) + d[17]*pow(F(1,2),3)*F(2,0) +\n    d[1]*pow(F(1,0),2)*F(1,1)*F(2,1) + 2*d[9]*pow(F(1,0),2)*F(1,1)*F(2,1) +\n    3*d[7]*F(1,0)*pow(F(1,1),2)*F(2,1) + d[2]*pow(F(1,1),3)*F(2,1) +\n    d[10]*pow(F(1,0),2)*F(1,2)*F(2,1) + 2*d[18]*pow(F(1,0),2)*F(1,2)*F(2,1) +\n    4*d[13]*F(1,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[16]*F(1,0)*F(1,1)*F(1,2)*F(2,1) +\n    3*d[11]*pow(F(1,1),2)*F(1,2)*F(2,1) + d[8]*F(1,0)*pow(F(1,2),2)*F(2,1) +\n    2*d[19]*F(1,0)*pow(F(1,2),2)*F(2,1) + d[4]*F(1,1)*pow(F(1,2),2)*F(2,1) +\n    2*d[14]*F(1,1)*pow(F(1,2),2)*F(2,1) + d[12]*pow(F(1,2),3)*F(2,1) +\n    d[6]*pow(F(1,0),2)*(3*F(1,1)*F(2,0) + F(1,0)*F(2,1)) +\n    d[15]*pow(F(1,0),3)*F(2,2) + d[10]*pow(F(1,0),2)*F(1,1)*F(2,2) +\n    2*d[18]*pow(F(1,0),2)*F(1,1)*F(2,2) + 2*d[13]*F(1,0)*pow(F(1,1),2)*F(2,2) +\n    d[16]*F(1,0)*pow(F(1,1),2)*F(2,2) + d[11]*pow(F(1,1),3)*F(2,2) +\n    d[3]*pow(F(1,0),2)*F(1,2)*F(2,2) + 2*d[20]*pow(F(1,0),2)*F(1,2)*F(2,2) +\n    2*d[8]*F(1,0)*F(1,1)*F(1,2)*F(2,2) + 4*d[19]*F(1,0)*F(1,1)*F(1,2)*F(2,2) +\n    d[4]*pow(F(1,1),2)*F(1,2)*F(2,2) + 2*d[14]*pow(F(1,1),2)*F(1,2)*F(2,2) +\n    3*d[17]*F(1,0)*pow(F(1,2),2)*F(2,2) + 3*d[12]*F(1,1)*pow(F(1,2),2)*F(2,2) +\n    d[5]*pow(F(1,2),3)*F(2,2);\n    \n    c.d[12] = d[0]*F(1,0)*pow(F(2,0),3) + d[15]*F(1,2)*pow(F(2,0),3) +\n    d[1]*F(1,1)*pow(F(2,0),2)*F(2,1) + 2*d[9]*F(1,1)*pow(F(2,0),2)*F(2,1) +\n    d[10]*F(1,2)*pow(F(2,0),2)*F(2,1) + 2*d[18]*F(1,2)*pow(F(2,0),2)*F(2,1) +\n    d[1]*F(1,0)*F(2,0)*pow(F(2,1),2) + 2*d[9]*F(1,0)*F(2,0)*pow(F(2,1),2) +\n    3*d[7]*F(1,1)*F(2,0)*pow(F(2,1),2) + 2*d[13]*F(1,2)*F(2,0)*pow(F(2,1),2) +\n    d[16]*F(1,2)*F(2,0)*pow(F(2,1),2) + d[7]*F(1,0)*pow(F(2,1),3) +\n    d[2]*F(1,1)*pow(F(2,1),3) + d[11]*F(1,2)*pow(F(2,1),3) +\n    d[6]*pow(F(2,0),2)*(F(1,1)*F(2,0) + 3*F(1,0)*F(2,1)) +\n    3*d[15]*F(1,0)*pow(F(2,0),2)*F(2,2) + d[10]*F(1,1)*pow(F(2,0),2)*F(2,2) +\n    2*d[18]*F(1,1)*pow(F(2,0),2)*F(2,2) + d[3]*F(1,2)*pow(F(2,0),2)*F(2,2) +\n    2*d[20]*F(1,2)*pow(F(2,0),2)*F(2,2) + 2*d[10]*F(1,0)*F(2,0)*F(2,1)*F(2,2) +\n    4*d[18]*F(1,0)*F(2,0)*F(2,1)*F(2,2) + 4*d[13]*F(1,1)*F(2,0)*F(2,1)*F(2,2) +\n    2*d[16]*F(1,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[8]*F(1,2)*F(2,0)*F(2,1)*F(2,2) +\n    4*d[19]*F(1,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[13]*F(1,0)*pow(F(2,1),2)*F(2,2) +\n    d[16]*F(1,0)*pow(F(2,1),2)*F(2,2) + 3*d[11]*F(1,1)*pow(F(2,1),2)*F(2,2) +\n    d[4]*F(1,2)*pow(F(2,1),2)*F(2,2) + 2*d[14]*F(1,2)*pow(F(2,1),2)*F(2,2) +\n    d[3]*F(1,0)*F(2,0)*pow(F(2,2),2) + 2*d[20]*F(1,0)*F(2,0)*pow(F(2,2),2) +\n    d[8]*F(1,1)*F(2,0)*pow(F(2,2),2) + 2*d[19]*F(1,1)*F(2,0)*pow(F(2,2),2) +\n    3*d[17]*F(1,2)*F(2,0)*pow(F(2,2),2) + d[8]*F(1,0)*F(2,1)*pow(F(2,2),2) +\n    2*d[19]*F(1,0)*F(2,1)*pow(F(2,2),2) + d[4]*F(1,1)*F(2,1)*pow(F(2,2),2) +\n    2*d[14]*F(1,1)*F(2,1)*pow(F(2,2),2) + 3*d[12]*F(1,2)*F(2,1)*pow(F(2,2),2) +\n    d[17]*F(1,0)*pow(F(2,2),3) + d[12]*F(1,1)*pow(F(2,2),3) +\n    d[5]*F(1,2)*pow(F(2,2),3);\n    \n    c.d[13] = d[0]*F(0,0)*pow(F(1,0),2)*F(2,0) + d[15]*F(0,2)*pow(F(1,0),2)*F(2,0) +\n    d[1]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + d[9]*F(0,1)*F(1,0)*F(1,1)*F(2,0) +\n    d[10]*F(0,2)*F(1,0)*F(1,1)*F(2,0) + d[18]*F(0,2)*F(1,0)*F(1,1)*F(2,0) +\n    d[9]*F(0,0)*pow(F(1,1),2)*F(2,0) + d[7]*F(0,1)*pow(F(1,1),2)*F(2,0) +\n    d[13]*F(0,2)*pow(F(1,1),2)*F(2,0) + 2*d[15]*F(0,0)*F(1,0)*F(1,2)*F(2,0) +\n    d[10]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + d[18]*F(0,1)*F(1,0)*F(1,2)*F(2,0) +\n    d[3]*F(0,2)*F(1,0)*F(1,2)*F(2,0) + d[20]*F(0,2)*F(1,0)*F(1,2)*F(2,0) +\n    2*d[18]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + d[13]*F(0,1)*F(1,1)*F(1,2)*F(2,0) +\n    d[16]*F(0,1)*F(1,1)*F(1,2)*F(2,0) + d[8]*F(0,2)*F(1,1)*F(1,2)*F(2,0) +\n    d[19]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[20]*F(0,0)*pow(F(1,2),2)*F(2,0) +\n    d[19]*F(0,1)*pow(F(1,2),2)*F(2,0) + d[17]*F(0,2)*pow(F(1,2),2)*F(2,0) +\n    d[9]*F(0,1)*pow(F(1,0),2)*F(2,1) + d[18]*F(0,2)*pow(F(1,0),2)*F(2,1) +\n    d[1]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + d[9]*F(0,0)*F(1,0)*F(1,1)*F(2,1) +\n    2*d[7]*F(0,1)*F(1,0)*F(1,1)*F(2,1) + d[13]*F(0,2)*F(1,0)*F(1,1)*F(2,1) +\n    d[16]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[7]*F(0,0)*pow(F(1,1),2)*F(2,1) +\n    d[2]*F(0,1)*pow(F(1,1),2)*F(2,1) + d[11]*F(0,2)*pow(F(1,1),2)*F(2,1) +\n    d[10]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + d[18]*F(0,0)*F(1,0)*F(1,2)*F(2,1) +\n    2*d[13]*F(0,1)*F(1,0)*F(1,2)*F(2,1) + d[8]*F(0,2)*F(1,0)*F(1,2)*F(2,1) +\n    d[19]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + d[13]*F(0,0)*F(1,1)*F(1,2)*F(2,1) +\n    d[16]*F(0,0)*F(1,1)*F(1,2)*F(2,1) + 2*d[11]*F(0,1)*F(1,1)*F(1,2)*F(2,1) +\n    d[4]*F(0,2)*F(1,1)*F(1,2)*F(2,1) + d[14]*F(0,2)*F(1,1)*F(1,2)*F(2,1) +\n    d[19]*F(0,0)*pow(F(1,2),2)*F(2,1) + d[14]*F(0,1)*pow(F(1,2),2)*F(2,1) +\n    d[12]*F(0,2)*pow(F(1,2),2)*F(2,1) +\n    d[6]*F(1,0)*(F(0,1)*F(1,0)*F(2,0) + F(0,0)*(2*F(1,1)*F(2,0) + F(1,0)*F(2,1))) +\n    d[15]*F(0,0)*pow(F(1,0),2)*F(2,2) + d[18]*F(0,1)*pow(F(1,0),2)*F(2,2) +\n    d[20]*F(0,2)*pow(F(1,0),2)*F(2,2) + d[10]*F(0,0)*F(1,0)*F(1,1)*F(2,2) +\n    d[18]*F(0,0)*F(1,0)*F(1,1)*F(2,2) + d[13]*F(0,1)*F(1,0)*F(1,1)*F(2,2) +\n    d[16]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + 2*d[19]*F(0,2)*F(1,0)*F(1,1)*F(2,2) +\n    d[13]*F(0,0)*pow(F(1,1),2)*F(2,2) + d[11]*F(0,1)*pow(F(1,1),2)*F(2,2) +\n    d[14]*F(0,2)*pow(F(1,1),2)*F(2,2) + d[3]*F(0,0)*F(1,0)*F(1,2)*F(2,2) +\n    d[20]*F(0,0)*F(1,0)*F(1,2)*F(2,2) + d[8]*F(0,1)*F(1,0)*F(1,2)*F(2,2) +\n    d[19]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + 2*d[17]*F(0,2)*F(1,0)*F(1,2)*F(2,2) +\n    d[8]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + d[19]*F(0,0)*F(1,1)*F(1,2)*F(2,2) +\n    d[4]*F(0,1)*F(1,1)*F(1,2)*F(2,2) + d[14]*F(0,1)*F(1,1)*F(1,2)*F(2,2) +\n    2*d[12]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[17]*F(0,0)*pow(F(1,2),2)*F(2,2) +\n    d[12]*F(0,1)*pow(F(1,2),2)*F(2,2) + d[5]*F(0,2)*pow(F(1,2),2)*F(2,2);\n    \n    c.d[14] = d[0]*pow(F(1,0),2)*pow(F(2,0),2) + d[9]*pow(F(1,1),2)*pow(F(2,0),2) +\n    2*d[15]*F(1,0)*F(1,2)*pow(F(2,0),2) + 2*d[18]*F(1,1)*F(1,2)*pow(F(2,0),2) +\n    d[20]*pow(F(1,2),2)*pow(F(2,0),2) + 2*d[1]*F(1,0)*F(1,1)*F(2,0)*F(2,1) +\n    2*d[9]*F(1,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[7]*pow(F(1,1),2)*F(2,0)*F(2,1) +\n    2*d[10]*F(1,0)*F(1,2)*F(2,0)*F(2,1) + 2*d[18]*F(1,0)*F(1,2)*F(2,0)*F(2,1) +\n    2*d[13]*F(1,1)*F(1,2)*F(2,0)*F(2,1) + 2*d[16]*F(1,1)*F(1,2)*F(2,0)*F(2,1) +\n    2*d[19]*pow(F(1,2),2)*F(2,0)*F(2,1) + d[9]*pow(F(1,0),2)*pow(F(2,1),2) +\n    2*d[7]*F(1,0)*F(1,1)*pow(F(2,1),2) + d[2]*pow(F(1,1),2)*pow(F(2,1),2) +\n    2*d[13]*F(1,0)*F(1,2)*pow(F(2,1),2) + 2*d[11]*F(1,1)*F(1,2)*pow(F(2,1),2) +\n    d[14]*pow(F(1,2),2)*pow(F(2,1),2) +\n    2*d[6]*F(1,0)*F(2,0)*(F(1,1)*F(2,0) + F(1,0)*F(2,1)) +\n    2*d[15]*pow(F(1,0),2)*F(2,0)*F(2,2) + 2*d[10]*F(1,0)*F(1,1)*F(2,0)*F(2,2) +\n    2*d[18]*F(1,0)*F(1,1)*F(2,0)*F(2,2) + 2*d[13]*pow(F(1,1),2)*F(2,0)*F(2,2) +\n    2*d[3]*F(1,0)*F(1,2)*F(2,0)*F(2,2) + 2*d[20]*F(1,0)*F(1,2)*F(2,0)*F(2,2) +\n    2*d[8]*F(1,1)*F(1,2)*F(2,0)*F(2,2) + 2*d[19]*F(1,1)*F(1,2)*F(2,0)*F(2,2) +\n    2*d[17]*pow(F(1,2),2)*F(2,0)*F(2,2) + 2*d[18]*pow(F(1,0),2)*F(2,1)*F(2,2) +\n    2*d[13]*F(1,0)*F(1,1)*F(2,1)*F(2,2) + 2*d[16]*F(1,0)*F(1,1)*F(2,1)*F(2,2) +\n    2*d[11]*pow(F(1,1),2)*F(2,1)*F(2,2) + 2*d[8]*F(1,0)*F(1,2)*F(2,1)*F(2,2) +\n    2*d[19]*F(1,0)*F(1,2)*F(2,1)*F(2,2) + 2*d[4]*F(1,1)*F(1,2)*F(2,1)*F(2,2) +\n    2*d[14]*F(1,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[12]*pow(F(1,2),2)*F(2,1)*F(2,2) +\n    d[20]*pow(F(1,0),2)*pow(F(2,2),2) + 2*d[19]*F(1,0)*F(1,1)*pow(F(2,2),2) +\n    d[14]*pow(F(1,1),2)*pow(F(2,2),2) + 2*d[17]*F(1,0)*F(1,2)*pow(F(2,2),2) +\n    2*d[12]*F(1,1)*F(1,2)*pow(F(2,2),2) + d[5]*pow(F(1,2),2)*pow(F(2,2),2);\n    \n    c.d[15] = d[0]*pow(F(0,0),3)*F(2,0) + d[1]*F(0,0)*pow(F(0,1),2)*F(2,0) +\n    2*d[9]*F(0,0)*pow(F(0,1),2)*F(2,0) + d[7]*pow(F(0,1),3)*F(2,0) +\n    3*d[15]*pow(F(0,0),2)*F(0,2)*F(2,0) + 2*d[10]*F(0,0)*F(0,1)*F(0,2)*F(2,0) +\n    4*d[18]*F(0,0)*F(0,1)*F(0,2)*F(2,0) + 2*d[13]*pow(F(0,1),2)*F(0,2)*F(2,0) +\n    d[16]*pow(F(0,1),2)*F(0,2)*F(2,0) + d[3]*F(0,0)*pow(F(0,2),2)*F(2,0) +\n    2*d[20]*F(0,0)*pow(F(0,2),2)*F(2,0) + d[8]*F(0,1)*pow(F(0,2),2)*F(2,0) +\n    2*d[19]*F(0,1)*pow(F(0,2),2)*F(2,0) + d[17]*pow(F(0,2),3)*F(2,0) +\n    d[1]*pow(F(0,0),2)*F(0,1)*F(2,1) + 2*d[9]*pow(F(0,0),2)*F(0,1)*F(2,1) +\n    3*d[7]*F(0,0)*pow(F(0,1),2)*F(2,1) + d[2]*pow(F(0,1),3)*F(2,1) +\n    d[10]*pow(F(0,0),2)*F(0,2)*F(2,1) + 2*d[18]*pow(F(0,0),2)*F(0,2)*F(2,1) +\n    4*d[13]*F(0,0)*F(0,1)*F(0,2)*F(2,1) + 2*d[16]*F(0,0)*F(0,1)*F(0,2)*F(2,1) +\n    3*d[11]*pow(F(0,1),2)*F(0,2)*F(2,1) + d[8]*F(0,0)*pow(F(0,2),2)*F(2,1) +\n    2*d[19]*F(0,0)*pow(F(0,2),2)*F(2,1) + d[4]*F(0,1)*pow(F(0,2),2)*F(2,1) +\n    2*d[14]*F(0,1)*pow(F(0,2),2)*F(2,1) + d[12]*pow(F(0,2),3)*F(2,1) +\n    d[6]*pow(F(0,0),2)*(3*F(0,1)*F(2,0) + F(0,0)*F(2,1)) +\n    d[15]*pow(F(0,0),3)*F(2,2) + d[10]*pow(F(0,0),2)*F(0,1)*F(2,2) +\n    2*d[18]*pow(F(0,0),2)*F(0,1)*F(2,2) + 2*d[13]*F(0,0)*pow(F(0,1),2)*F(2,2) +\n    d[16]*F(0,0)*pow(F(0,1),2)*F(2,2) + d[11]*pow(F(0,1),3)*F(2,2) +\n    d[3]*pow(F(0,0),2)*F(0,2)*F(2,2) + 2*d[20]*pow(F(0,0),2)*F(0,2)*F(2,2) +\n    2*d[8]*F(0,0)*F(0,1)*F(0,2)*F(2,2) + 4*d[19]*F(0,0)*F(0,1)*F(0,2)*F(2,2) +\n    d[4]*pow(F(0,1),2)*F(0,2)*F(2,2) + 2*d[14]*pow(F(0,1),2)*F(0,2)*F(2,2) +\n    3*d[17]*F(0,0)*pow(F(0,2),2)*F(2,2) + 3*d[12]*F(0,1)*pow(F(0,2),2)*F(2,2) +\n    d[5]*pow(F(0,2),3)*F(2,2);\n    \n    c.d[16] = d[0]*F(0,0)*pow(F(1,0),2)*F(2,0) + d[15]*F(0,2)*pow(F(1,0),2)*F(2,0) +\n    2*d[9]*F(0,1)*F(1,0)*F(1,1)*F(2,0) + 2*d[18]*F(0,2)*F(1,0)*F(1,1)*F(2,0) +\n    d[1]*F(0,0)*pow(F(1,1),2)*F(2,0) + d[7]*F(0,1)*pow(F(1,1),2)*F(2,0) +\n    d[16]*F(0,2)*pow(F(1,1),2)*F(2,0) + 2*d[15]*F(0,0)*F(1,0)*F(1,2)*F(2,0) +\n    2*d[18]*F(0,1)*F(1,0)*F(1,2)*F(2,0) + 2*d[20]*F(0,2)*F(1,0)*F(1,2)*F(2,0) +\n    2*d[10]*F(0,0)*F(1,1)*F(1,2)*F(2,0) + 2*d[13]*F(0,1)*F(1,1)*F(1,2)*F(2,0) +\n    2*d[19]*F(0,2)*F(1,1)*F(1,2)*F(2,0) + d[3]*F(0,0)*pow(F(1,2),2)*F(2,0) +\n    d[8]*F(0,1)*pow(F(1,2),2)*F(2,0) + d[17]*F(0,2)*pow(F(1,2),2)*F(2,0) +\n    d[1]*F(0,1)*pow(F(1,0),2)*F(2,1) + d[10]*F(0,2)*pow(F(1,0),2)*F(2,1) +\n    2*d[9]*F(0,0)*F(1,0)*F(1,1)*F(2,1) + 2*d[7]*F(0,1)*F(1,0)*F(1,1)*F(2,1) +\n    2*d[13]*F(0,2)*F(1,0)*F(1,1)*F(2,1) + d[7]*F(0,0)*pow(F(1,1),2)*F(2,1) +\n    d[2]*F(0,1)*pow(F(1,1),2)*F(2,1) + d[11]*F(0,2)*pow(F(1,1),2)*F(2,1) +\n    2*d[18]*F(0,0)*F(1,0)*F(1,2)*F(2,1) + 2*d[16]*F(0,1)*F(1,0)*F(1,2)*F(2,1) +\n    2*d[19]*F(0,2)*F(1,0)*F(1,2)*F(2,1) + 2*d[13]*F(0,0)*F(1,1)*F(1,2)*F(2,1) +\n    2*d[11]*F(0,1)*F(1,1)*F(1,2)*F(2,1) + 2*d[14]*F(0,2)*F(1,1)*F(1,2)*F(2,1) +\n    d[8]*F(0,0)*pow(F(1,2),2)*F(2,1) + d[4]*F(0,1)*pow(F(1,2),2)*F(2,1) +\n    d[12]*F(0,2)*pow(F(1,2),2)*F(2,1) +\n    d[6]*F(1,0)*(F(0,1)*F(1,0)*F(2,0) + F(0,0)*(2*F(1,1)*F(2,0) + F(1,0)*F(2,1))) +\n    d[15]*F(0,0)*pow(F(1,0),2)*F(2,2) + d[10]*F(0,1)*pow(F(1,0),2)*F(2,2) +\n    d[3]*F(0,2)*pow(F(1,0),2)*F(2,2) + 2*d[18]*F(0,0)*F(1,0)*F(1,1)*F(2,2) +\n    2*d[13]*F(0,1)*F(1,0)*F(1,1)*F(2,2) + 2*d[8]*F(0,2)*F(1,0)*F(1,1)*F(2,2) +\n    d[16]*F(0,0)*pow(F(1,1),2)*F(2,2) + d[11]*F(0,1)*pow(F(1,1),2)*F(2,2) +\n    d[4]*F(0,2)*pow(F(1,1),2)*F(2,2) + 2*d[20]*F(0,0)*F(1,0)*F(1,2)*F(2,2) +\n    2*d[19]*F(0,1)*F(1,0)*F(1,2)*F(2,2) + 2*d[17]*F(0,2)*F(1,0)*F(1,2)*F(2,2) +\n    2*d[19]*F(0,0)*F(1,1)*F(1,2)*F(2,2) + 2*d[14]*F(0,1)*F(1,1)*F(1,2)*F(2,2) +\n    2*d[12]*F(0,2)*F(1,1)*F(1,2)*F(2,2) + d[17]*F(0,0)*pow(F(1,2),2)*F(2,2) +\n    d[12]*F(0,1)*pow(F(1,2),2)*F(2,2) + d[5]*F(0,2)*pow(F(1,2),2)*F(2,2);\n    \n    c.d[17] = d[0]*F(0,0)*pow(F(2,0),3) + d[15]*F(0,2)*pow(F(2,0),3) +\n    d[1]*F(0,1)*pow(F(2,0),2)*F(2,1) + 2*d[9]*F(0,1)*pow(F(2,0),2)*F(2,1) +\n    d[10]*F(0,2)*pow(F(2,0),2)*F(2,1) + 2*d[18]*F(0,2)*pow(F(2,0),2)*F(2,1) +\n    d[1]*F(0,0)*F(2,0)*pow(F(2,1),2) + 2*d[9]*F(0,0)*F(2,0)*pow(F(2,1),2) +\n    3*d[7]*F(0,1)*F(2,0)*pow(F(2,1),2) + 2*d[13]*F(0,2)*F(2,0)*pow(F(2,1),2) +\n    d[16]*F(0,2)*F(2,0)*pow(F(2,1),2) + d[7]*F(0,0)*pow(F(2,1),3) +\n    d[2]*F(0,1)*pow(F(2,1),3) + d[11]*F(0,2)*pow(F(2,1),3) +\n    d[6]*pow(F(2,0),2)*(F(0,1)*F(2,0) + 3*F(0,0)*F(2,1)) +\n    3*d[15]*F(0,0)*pow(F(2,0),2)*F(2,2) + d[10]*F(0,1)*pow(F(2,0),2)*F(2,2) +\n    2*d[18]*F(0,1)*pow(F(2,0),2)*F(2,2) + d[3]*F(0,2)*pow(F(2,0),2)*F(2,2) +\n    2*d[20]*F(0,2)*pow(F(2,0),2)*F(2,2) + 2*d[10]*F(0,0)*F(2,0)*F(2,1)*F(2,2) +\n    4*d[18]*F(0,0)*F(2,0)*F(2,1)*F(2,2) + 4*d[13]*F(0,1)*F(2,0)*F(2,1)*F(2,2) +\n    2*d[16]*F(0,1)*F(2,0)*F(2,1)*F(2,2) + 2*d[8]*F(0,2)*F(2,0)*F(2,1)*F(2,2) +\n    4*d[19]*F(0,2)*F(2,0)*F(2,1)*F(2,2) + 2*d[13]*F(0,0)*pow(F(2,1),2)*F(2,2) +\n    d[16]*F(0,0)*pow(F(2,1),2)*F(2,2) + 3*d[11]*F(0,1)*pow(F(2,1),2)*F(2,2) +\n    d[4]*F(0,2)*pow(F(2,1),2)*F(2,2) + 2*d[14]*F(0,2)*pow(F(2,1),2)*F(2,2) +\n    d[3]*F(0,0)*F(2,0)*pow(F(2,2),2) + 2*d[20]*F(0,0)*F(2,0)*pow(F(2,2),2) +\n    d[8]*F(0,1)*F(2,0)*pow(F(2,2),2) + 2*d[19]*F(0,1)*F(2,0)*pow(F(2,2),2) +\n    3*d[17]*F(0,2)*F(2,0)*pow(F(2,2),2) + d[8]*F(0,0)*F(2,1)*pow(F(2,2),2) +\n    2*d[19]*F(0,0)*F(2,1)*pow(F(2,2),2) + d[4]*F(0,1)*F(2,1)*pow(F(2,2),2) +\n    2*d[14]*F(0,1)*F(2,1)*pow(F(2,2),2) + 3*d[12]*F(0,2)*F(2,1)*pow(F(2,2),2) +\n    d[17]*F(0,0)*pow(F(2,2),3) + d[12]*F(0,1)*pow(F(2,2),3) +\n    d[5]*F(0,2)*pow(F(2,2),3);\n    \n    c.d[18] = d[0]*pow(F(0,0),2)*F(1,0)*F(2,0) + d[9]*pow(F(0,1),2)*F(1,0)*F(2,0) +\n    2*d[15]*F(0,0)*F(0,2)*F(1,0)*F(2,0) + 2*d[18]*F(0,1)*F(0,2)*F(1,0)*F(2,0) +\n    d[20]*pow(F(0,2),2)*F(1,0)*F(2,0) + d[1]*F(0,0)*F(0,1)*F(1,1)*F(2,0) +\n    d[9]*F(0,0)*F(0,1)*F(1,1)*F(2,0) + d[7]*pow(F(0,1),2)*F(1,1)*F(2,0) +\n    d[10]*F(0,0)*F(0,2)*F(1,1)*F(2,0) + d[18]*F(0,0)*F(0,2)*F(1,1)*F(2,0) +\n    d[13]*F(0,1)*F(0,2)*F(1,1)*F(2,0) + d[16]*F(0,1)*F(0,2)*F(1,1)*F(2,0) +\n    d[19]*pow(F(0,2),2)*F(1,1)*F(2,0) + d[15]*pow(F(0,0),2)*F(1,2)*F(2,0) +\n    d[10]*F(0,0)*F(0,1)*F(1,2)*F(2,0) + d[18]*F(0,0)*F(0,1)*F(1,2)*F(2,0) +\n    d[13]*pow(F(0,1),2)*F(1,2)*F(2,0) + d[3]*F(0,0)*F(0,2)*F(1,2)*F(2,0) +\n    d[20]*F(0,0)*F(0,2)*F(1,2)*F(2,0) + d[8]*F(0,1)*F(0,2)*F(1,2)*F(2,0) +\n    d[19]*F(0,1)*F(0,2)*F(1,2)*F(2,0) + d[17]*pow(F(0,2),2)*F(1,2)*F(2,0) +\n    d[1]*F(0,0)*F(0,1)*F(1,0)*F(2,1) + d[9]*F(0,0)*F(0,1)*F(1,0)*F(2,1) +\n    d[7]*pow(F(0,1),2)*F(1,0)*F(2,1) + d[10]*F(0,0)*F(0,2)*F(1,0)*F(2,1) +\n    d[18]*F(0,0)*F(0,2)*F(1,0)*F(2,1) + d[13]*F(0,1)*F(0,2)*F(1,0)*F(2,1) +\n    d[16]*F(0,1)*F(0,2)*F(1,0)*F(2,1) + d[19]*pow(F(0,2),2)*F(1,0)*F(2,1) +\n    d[9]*pow(F(0,0),2)*F(1,1)*F(2,1) + 2*d[7]*F(0,0)*F(0,1)*F(1,1)*F(2,1) +\n    d[2]*pow(F(0,1),2)*F(1,1)*F(2,1) + 2*d[13]*F(0,0)*F(0,2)*F(1,1)*F(2,1) +\n    2*d[11]*F(0,1)*F(0,2)*F(1,1)*F(2,1) + d[14]*pow(F(0,2),2)*F(1,1)*F(2,1) +\n    d[18]*pow(F(0,0),2)*F(1,2)*F(2,1) + d[13]*F(0,0)*F(0,1)*F(1,2)*F(2,1) +\n    d[16]*F(0,0)*F(0,1)*F(1,2)*F(2,1) + d[11]*pow(F(0,1),2)*F(1,2)*F(2,1) +\n    d[8]*F(0,0)*F(0,2)*F(1,2)*F(2,1) + d[19]*F(0,0)*F(0,2)*F(1,2)*F(2,1) +\n    d[4]*F(0,1)*F(0,2)*F(1,2)*F(2,1) + d[14]*F(0,1)*F(0,2)*F(1,2)*F(2,1) +\n    d[12]*pow(F(0,2),2)*F(1,2)*F(2,1) +\n    d[6]*F(0,0)*(2*F(0,1)*F(1,0)*F(2,0) + F(0,0)*(F(1,1)*F(2,0) + F(1,0)*F(2,1))) +\n    d[15]*pow(F(0,0),2)*F(1,0)*F(2,2) + d[10]*F(0,0)*F(0,1)*F(1,0)*F(2,2) +\n    d[18]*F(0,0)*F(0,1)*F(1,0)*F(2,2) + d[13]*pow(F(0,1),2)*F(1,0)*F(2,2) +\n    d[3]*F(0,0)*F(0,2)*F(1,0)*F(2,2) + d[20]*F(0,0)*F(0,2)*F(1,0)*F(2,2) +\n    d[8]*F(0,1)*F(0,2)*F(1,0)*F(2,2) + d[19]*F(0,1)*F(0,2)*F(1,0)*F(2,2) +\n    d[17]*pow(F(0,2),2)*F(1,0)*F(2,2) + d[18]*pow(F(0,0),2)*F(1,1)*F(2,2) +\n    d[13]*F(0,0)*F(0,1)*F(1,1)*F(2,2) + d[16]*F(0,0)*F(0,1)*F(1,1)*F(2,2) +\n    d[11]*pow(F(0,1),2)*F(1,1)*F(2,2) + d[8]*F(0,0)*F(0,2)*F(1,1)*F(2,2) +\n    d[19]*F(0,0)*F(0,2)*F(1,1)*F(2,2) + d[4]*F(0,1)*F(0,2)*F(1,1)*F(2,2) +\n    d[14]*F(0,1)*F(0,2)*F(1,1)*F(2,2) + d[12]*pow(F(0,2),2)*F(1,1)*F(2,2) +\n    d[20]*pow(F(0,0),2)*F(1,2)*F(2,2) + 2*d[19]*F(0,0)*F(0,1)*F(1,2)*F(2,2) +\n    d[14]*pow(F(0,1),2)*F(1,2)*F(2,2) + 2*d[17]*F(0,0)*F(0,2)*F(1,2)*F(2,2) +\n    2*d[12]*F(0,1)*F(0,2)*F(1,2)*F(2,2) + d[5]*pow(F(0,2),2)*F(1,2)*F(2,2);\n    \n    c.d[19] = d[0]*F(0,0)*F(1,0)*pow(F(2,0),2) + d[15]*F(0,2)*F(1,0)*pow(F(2,0),2) +\n    d[9]*F(0,1)*F(1,1)*pow(F(2,0),2) + d[18]*F(0,2)*F(1,1)*pow(F(2,0),2) +\n    d[15]*F(0,0)*F(1,2)*pow(F(2,0),2) + d[18]*F(0,1)*F(1,2)*pow(F(2,0),2) +\n    d[20]*F(0,2)*F(1,2)*pow(F(2,0),2) + d[1]*F(0,1)*F(1,0)*F(2,0)*F(2,1) +\n    d[9]*F(0,1)*F(1,0)*F(2,0)*F(2,1) + d[10]*F(0,2)*F(1,0)*F(2,0)*F(2,1) +\n    d[18]*F(0,2)*F(1,0)*F(2,0)*F(2,1) + d[1]*F(0,0)*F(1,1)*F(2,0)*F(2,1) +\n    d[9]*F(0,0)*F(1,1)*F(2,0)*F(2,1) + 2*d[7]*F(0,1)*F(1,1)*F(2,0)*F(2,1) +\n    d[13]*F(0,2)*F(1,1)*F(2,0)*F(2,1) + d[16]*F(0,2)*F(1,1)*F(2,0)*F(2,1) +\n    d[10]*F(0,0)*F(1,2)*F(2,0)*F(2,1) + d[18]*F(0,0)*F(1,2)*F(2,0)*F(2,1) +\n    d[13]*F(0,1)*F(1,2)*F(2,0)*F(2,1) + d[16]*F(0,1)*F(1,2)*F(2,0)*F(2,1) +\n    2*d[19]*F(0,2)*F(1,2)*F(2,0)*F(2,1) + d[9]*F(0,0)*F(1,0)*pow(F(2,1),2) +\n    d[7]*F(0,1)*F(1,0)*pow(F(2,1),2) + d[13]*F(0,2)*F(1,0)*pow(F(2,1),2) +\n    d[7]*F(0,0)*F(1,1)*pow(F(2,1),2) + d[2]*F(0,1)*F(1,1)*pow(F(2,1),2) +\n    d[11]*F(0,2)*F(1,1)*pow(F(2,1),2) + d[13]*F(0,0)*F(1,2)*pow(F(2,1),2) +\n    d[11]*F(0,1)*F(1,2)*pow(F(2,1),2) + d[14]*F(0,2)*F(1,2)*pow(F(2,1),2) +\n    d[6]*F(2,0)*(F(0,1)*F(1,0)*F(2,0) + F(0,0)*(F(1,1)*F(2,0) + 2*F(1,0)*F(2,1))) +\n    2*d[15]*F(0,0)*F(1,0)*F(2,0)*F(2,2) + d[10]*F(0,1)*F(1,0)*F(2,0)*F(2,2) +\n    d[18]*F(0,1)*F(1,0)*F(2,0)*F(2,2) + d[3]*F(0,2)*F(1,0)*F(2,0)*F(2,2) +\n    d[20]*F(0,2)*F(1,0)*F(2,0)*F(2,2) + d[10]*F(0,0)*F(1,1)*F(2,0)*F(2,2) +\n    d[18]*F(0,0)*F(1,1)*F(2,0)*F(2,2) + 2*d[13]*F(0,1)*F(1,1)*F(2,0)*F(2,2) +\n    d[8]*F(0,2)*F(1,1)*F(2,0)*F(2,2) + d[19]*F(0,2)*F(1,1)*F(2,0)*F(2,2) +\n    d[3]*F(0,0)*F(1,2)*F(2,0)*F(2,2) + d[20]*F(0,0)*F(1,2)*F(2,0)*F(2,2) +\n    d[8]*F(0,1)*F(1,2)*F(2,0)*F(2,2) + d[19]*F(0,1)*F(1,2)*F(2,0)*F(2,2) +\n    2*d[17]*F(0,2)*F(1,2)*F(2,0)*F(2,2) + 2*d[18]*F(0,0)*F(1,0)*F(2,1)*F(2,2) +\n    d[13]*F(0,1)*F(1,0)*F(2,1)*F(2,2) + d[16]*F(0,1)*F(1,0)*F(2,1)*F(2,2) +\n    d[8]*F(0,2)*F(1,0)*F(2,1)*F(2,2) + d[19]*F(0,2)*F(1,0)*F(2,1)*F(2,2) +\n    d[13]*F(0,0)*F(1,1)*F(2,1)*F(2,2) + d[16]*F(0,0)*F(1,1)*F(2,1)*F(2,2) +\n    2*d[11]*F(0,1)*F(1,1)*F(2,1)*F(2,2) + d[4]*F(0,2)*F(1,1)*F(2,1)*F(2,2) +\n    d[14]*F(0,2)*F(1,1)*F(2,1)*F(2,2) + d[8]*F(0,0)*F(1,2)*F(2,1)*F(2,2) +\n    d[19]*F(0,0)*F(1,2)*F(2,1)*F(2,2) + d[4]*F(0,1)*F(1,2)*F(2,1)*F(2,2) +\n    d[14]*F(0,1)*F(1,2)*F(2,1)*F(2,2) + 2*d[12]*F(0,2)*F(1,2)*F(2,1)*F(2,2) +\n    d[20]*F(0,0)*F(1,0)*pow(F(2,2),2) + d[19]*F(0,1)*F(1,0)*pow(F(2,2),2) +\n    d[17]*F(0,2)*F(1,0)*pow(F(2,2),2) + d[19]*F(0,0)*F(1,1)*pow(F(2,2),2) +\n    d[14]*F(0,1)*F(1,1)*pow(F(2,2),2) + d[12]*F(0,2)*F(1,1)*pow(F(2,2),2) +\n    d[17]*F(0,0)*F(1,2)*pow(F(2,2),2) + d[12]*F(0,1)*F(1,2)*pow(F(2,2),2) +\n    d[5]*F(0,2)*F(1,2)*pow(F(2,2),2);\n    \n    c.d[20] = d[0]*pow(F(0,0),2)*pow(F(2,0),2) + d[9]*pow(F(0,1),2)*pow(F(2,0),2) +\n    2*d[15]*F(0,0)*F(0,2)*pow(F(2,0),2) + 2*d[18]*F(0,1)*F(0,2)*pow(F(2,0),2) +\n    d[20]*pow(F(0,2),2)*pow(F(2,0),2) + 2*d[1]*F(0,0)*F(0,1)*F(2,0)*F(2,1) +\n    2*d[9]*F(0,0)*F(0,1)*F(2,0)*F(2,1) + 2*d[7]*pow(F(0,1),2)*F(2,0)*F(2,1) +\n    2*d[10]*F(0,0)*F(0,2)*F(2,0)*F(2,1) + 2*d[18]*F(0,0)*F(0,2)*F(2,0)*F(2,1) +\n    2*d[13]*F(0,1)*F(0,2)*F(2,0)*F(2,1) + 2*d[16]*F(0,1)*F(0,2)*F(2,0)*F(2,1) +\n    2*d[19]*pow(F(0,2),2)*F(2,0)*F(2,1) + d[9]*pow(F(0,0),2)*pow(F(2,1),2) +\n    2*d[7]*F(0,0)*F(0,1)*pow(F(2,1),2) + d[2]*pow(F(0,1),2)*pow(F(2,1),2) +\n    2*d[13]*F(0,0)*F(0,2)*pow(F(2,1),2) + 2*d[11]*F(0,1)*F(0,2)*pow(F(2,1),2) +\n    d[14]*pow(F(0,2),2)*pow(F(2,1),2) +\n    2*d[6]*F(0,0)*F(2,0)*(F(0,1)*F(2,0) + F(0,0)*F(2,1)) +\n    2*d[15]*pow(F(0,0),2)*F(2,0)*F(2,2) + 2*d[10]*F(0,0)*F(0,1)*F(2,0)*F(2,2) +\n    2*d[18]*F(0,0)*F(0,1)*F(2,0)*F(2,2) + 2*d[13]*pow(F(0,1),2)*F(2,0)*F(2,2) +\n    2*d[3]*F(0,0)*F(0,2)*F(2,0)*F(2,2) + 2*d[20]*F(0,0)*F(0,2)*F(2,0)*F(2,2) +\n    2*d[8]*F(0,1)*F(0,2)*F(2,0)*F(2,2) + 2*d[19]*F(0,1)*F(0,2)*F(2,0)*F(2,2) +\n    2*d[17]*pow(F(0,2),2)*F(2,0)*F(2,2) + 2*d[18]*pow(F(0,0),2)*F(2,1)*F(2,2) +\n    2*d[13]*F(0,0)*F(0,1)*F(2,1)*F(2,2) + 2*d[16]*F(0,0)*F(0,1)*F(2,1)*F(2,2) +\n    2*d[11]*pow(F(0,1),2)*F(2,1)*F(2,2) + 2*d[8]*F(0,0)*F(0,2)*F(2,1)*F(2,2) +\n    2*d[19]*F(0,0)*F(0,2)*F(2,1)*F(2,2) + 2*d[4]*F(0,1)*F(0,2)*F(2,1)*F(2,2) +\n    2*d[14]*F(0,1)*F(0,2)*F(2,1)*F(2,2) + 2*d[12]*pow(F(0,2),2)*F(2,1)*F(2,2) +\n    d[20]*pow(F(0,0),2)*pow(F(2,2),2) + 2*d[19]*F(0,0)*F(0,1)*pow(F(2,2),2) +\n    d[14]*pow(F(0,1),2)*pow(F(2,2),2) + 2*d[17]*F(0,0)*F(0,2)*pow(F(2,2),2) +\n    2*d[12]*F(0,1)*F(0,2)*pow(F(2,2),2) + d[5]*pow(F(0,2),2)*pow(F(2,2),2);\n    \n    return c;\n}\n"
  },
  {
    "path": "FECore/tens5d.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"tens5d.h\"\n"
  },
  {
    "path": "FECore/tens5d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"tensor_base.h\"\n\n//-----------------------------------------------------------------------------\n// Classes defined in this file\nclass tens5ds;\nclass tens5d;\n\n//-----------------------------------------------------------------------------\n// traits for these classes defining the number of components\ntemplate <> class tensor_traits<tens5ds> {public: enum { NNZ =  21}; };\ntemplate <> class tensor_traits<tens5d > {public: enum { NNZ = 243}; };\n\n//-----------------------------------------------------------------------------\n//! Class for 5th order tensors with full symmetry (any pair of indices can be swapped)\n//\nclass tens5ds : public tensor_base<tens5ds>\n{\npublic:\n\t// default constructor\n\ttens5ds() {}\n\n\t// access operator\n\t// TODO: implement this\n//\tdouble operator () (int i, int j, int k, int l, int m) const;\n};\n\n//-----------------------------------------------------------------------------\n//! Class for 5th order tensors (no symmetries)\n//! This is stored as a 27x9 matrix\n//\n//      /  0  27  54  81  108  135 162 189 216  \\\n//  A = |  .                                 .  |\n//      |  .                                 .  |\n//      |  .                                 .  |\n//      \\  26                               242 /\n//\nclass tens5d : public tensor_base<tens5d>\n{\npublic:\n\t// default constructor\n\ttens5d() {}\n\n\t// access operators\n\tdouble  operator () (int i, int j, int k, int l, int m) const;\n\tdouble& operator () (int i, int j, int k, int l, int m);\n};\n\n// The following file contains the actual definition of the class functions\n#include \"tens5d.hpp\"\n#include \"tens5ds.hpp\"\n"
  },
  {
    "path": "FECore/tens5d.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens5d.h\n// Users should not include this file manually!\n\n// access operator\ninline double tens5d::operator () (int i, int j, int k, int l, int m) const\n{\n\tint R = 3*(3*i + j) + k;\n\tint C = 3*l + m;\n\treturn d[27*C + R];\n}\n\n// access operator\ninline double& tens5d::operator () (int i, int j, int k, int l, int m)\n{\n\tint R = 3*(3*i + j) + k;\n\tint C = 3*l + m;\n\treturn d[27*C + R];\n}\n"
  },
  {
    "path": "FECore/tens5ds.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens5d.h\n// Users should not include this file manually!\n"
  },
  {
    "path": "FECore/tens6d.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"tens6d.h\"\n\n//-----------------------------------------------------------------------------\ndouble calc_6ds_comp(double K[3][3], double Ri[3], double Rj[3], int i, int j, int k, int l, int m, int n)\n{\n\ti -= 1; j -= 1;  k -= 1; l -= 1; m -= 1; n -= 1;\n\t\t\n\treturn (1/72)*(   Ri[i]*Ri[j]*K[k][l]*Rj[m]*Rj[n] + Ri[i]*Ri[j]*K[k][n]*Rj[m]*Rj[l] + Ri[i]*Ri[j]*K[k][m]*Rj[l]*Rj[n] + Ri[i]*Ri[j]*K[k][l]*Rj[n]*Rj[m] + Ri[i]*Ri[j]*K[k][m]*Rj[n]*Rj[l] + Ri[i]*Ri[j]*K[k][n]*Rj[l]*Rj[m]     \n\t                + Ri[k]*Ri[j]*K[i][l]*Rj[m]*Rj[n] + Ri[k]*Ri[j]*K[i][n]*Rj[m]*Rj[l] + Ri[k]*Ri[j]*K[i][m]*Rj[l]*Rj[n] + Ri[k]*Ri[j]*K[i][l]*Rj[n]*Rj[m] + Ri[k]*Ri[j]*K[i][m]*Rj[n]*Rj[l] + Ri[k]*Ri[j]*K[i][n]*Rj[l]*Rj[m]\n\t\t\t\t    + Ri[j]*Ri[i]*K[k][l]*Rj[m]*Rj[n] + Ri[j]*Ri[i]*K[k][n]*Rj[m]*Rj[l] + Ri[j]*Ri[i]*K[k][m]*Rj[l]*Rj[n] + Ri[j]*Ri[i]*K[k][l]*Rj[n]*Rj[m] + Ri[j]*Ri[i]*K[k][m]*Rj[n]*Rj[l] + Ri[j]*Ri[i]*K[k][n]*Rj[l]*Rj[m]\n\t\t\t\t\t+ Ri[i]*Ri[k]*K[j][l]*Rj[m]*Rj[n] + Ri[i]*Ri[k]*K[j][n]*Rj[m]*Rj[l] + Ri[i]*Ri[k]*K[j][m]*Rj[l]*Rj[n] + Ri[i]*Ri[k]*K[j][l]*Rj[n]*Rj[m] + Ri[i]*Ri[k]*K[j][m]*Rj[n]*Rj[l] + Ri[i]*Ri[k]*K[j][n]*Rj[l]*Rj[m]\n\t\t\t\t\t+ Ri[j]*Ri[k]*K[i][l]*Rj[m]*Rj[n] + Ri[j]*Ri[k]*K[i][n]*Rj[m]*Rj[l] + Ri[j]*Ri[k]*K[i][m]*Rj[l]*Rj[n] + Ri[j]*Ri[k]*K[i][l]*Rj[n]*Rj[m] + Ri[j]*Ri[k]*K[i][m]*Rj[n]*Rj[l] + Ri[j]*Ri[k]*K[i][n]*Rj[l]*Rj[m]\n\t\t\t\t\t+ Ri[k]*Ri[i]*K[j][l]*Rj[m]*Rj[n] + Ri[k]*Ri[i]*K[j][n]*Rj[m]*Rj[l] + Ri[k]*Ri[i]*K[j][m]*Rj[l]*Rj[n] + Ri[k]*Ri[i]*K[j][l]*Rj[n]*Rj[m] + Ri[k]*Ri[i]*K[j][m]*Rj[n]*Rj[l] + Ri[k]*Ri[i]*K[j][n]*Rj[l]*Rj[m]\n\t\t\t\t\t+ Ri[l]*Ri[m]*K[n][i]*Rj[j]*Rj[k] + Ri[l]*Ri[m]*K[n][k]*Rj[j]*Rj[i] + Ri[l]*Ri[m]*K[n][j]*Rj[i]*Rj[k] + Ri[l]*Ri[m]*K[n][i]*Rj[k]*Rj[j] + Ri[l]*Ri[m]*K[n][j]*Rj[k]*Rj[i] + Ri[l]*Ri[m]*K[n][k]*Rj[i]*Rj[j]\n\t\t\t\t\t+ Ri[n]*Ri[m]*K[l][i]*Rj[j]*Rj[k] + Ri[n]*Ri[m]*K[l][k]*Rj[j]*Rj[i] + Ri[n]*Ri[m]*K[l][j]*Rj[i]*Rj[k] + Ri[n]*Ri[m]*K[l][i]*Rj[k]*Rj[j] + Ri[n]*Ri[m]*K[l][j]*Rj[k]*Rj[i] + Ri[n]*Ri[m]*K[l][k]*Rj[i]*Rj[j]\n\t\t\t\t\t+ Ri[m]*Ri[l]*K[n][i]*Rj[j]*Rj[k] + Ri[m]*Ri[l]*K[n][k]*Rj[j]*Rj[i] + Ri[m]*Ri[l]*K[n][j]*Rj[i]*Rj[k] + Ri[m]*Ri[l]*K[n][i]*Rj[k]*Rj[j] + Ri[m]*Ri[l]*K[n][j]*Rj[k]*Rj[i] + Ri[m]*Ri[l]*K[n][k]*Rj[i]*Rj[j]\n\t\t\t\t\t+ Ri[l]*Ri[n]*K[m][i]*Rj[j]*Rj[k] + Ri[l]*Ri[n]*K[m][k]*Rj[j]*Rj[i] + Ri[l]*Ri[n]*K[m][j]*Rj[i]*Rj[k] + Ri[l]*Ri[n]*K[m][i]*Rj[k]*Rj[j] + Ri[l]*Ri[n]*K[m][j]*Rj[k]*Rj[i] + Ri[l]*Ri[n]*K[m][k]*Rj[i]*Rj[j]\n\t\t\t\t\t+ Ri[m]*Ri[n]*K[l][i]*Rj[j]*Rj[k] + Ri[m]*Ri[n]*K[l][k]*Rj[j]*Rj[i] + Ri[m]*Ri[n]*K[l][j]*Rj[i]*Rj[k] + Ri[m]*Ri[n]*K[l][i]*Rj[k]*Rj[j] + Ri[m]*Ri[n]*K[l][j]*Rj[k]*Rj[i] + Ri[m]*Ri[n]*K[l][k]*Rj[i]*Rj[j]\n\t\t\t\t\t+ Ri[n]*Ri[l]*K[m][i]*Rj[j]*Rj[k] + Ri[n]*Ri[l]*K[m][k]*Rj[j]*Rj[i] + Ri[n]*Ri[l]*K[m][j]*Rj[i]*Rj[k] + Ri[n]*Ri[l]*K[m][i]*Rj[k]*Rj[j] + Ri[n]*Ri[l]*K[m][j]*Rj[k]*Rj[i] + Ri[n]*Ri[l]*K[m][k]*Rj[i]*Rj[j]);\n}\n\n//-----------------------------------------------------------------------------\nvoid calculate_e2O(tens6ds& e, double K[3][3], double Ri[3], double Rj[3] )\n{\n\te.d[ 0] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 1, 1, 1);\n\te.d[ 1] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 1, 1, 2);\n\te.d[ 2] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 1, 1, 3);\n\te.d[ 3] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 1, 2, 2);\n\te.d[ 4] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 1, 2, 3);\n\te.d[ 5] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 1, 3, 3);\n\te.d[ 6] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 2, 2, 2);\n\te.d[ 7] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 2, 2, 3);\n\te.d[ 8] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 2, 3, 3);\n\te.d [9] += calc_6ds_comp(K, Ri, Rj, 1, 1, 1, 3, 3, 3);\n\t\n\te.d[10] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 1, 2, 2);\n\te.d[11] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 1, 2, 3);\n\te.d[12] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 1, 3, 3);\n\te.d[13] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 2, 2, 2);\n\te.d[14] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 2, 2, 3);\n\te.d[15] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 2, 3, 3);\n\te.d[16] += calc_6ds_comp(K, Ri, Rj, 1, 1, 2, 3, 3, 3);\n\n\te.d[17] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 1, 2, 2);\n\te.d[18] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 1, 2, 3);\n\te.d[19] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 1, 3, 3);\n\te.d[20] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 2, 2, 2);\n\te.d[21] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 2, 2, 3);\n\te.d[22] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 2, 3, 3);\n\te.d[23] += calc_6ds_comp(K, Ri, Rj, 1, 1, 3, 3, 3, 3);\n\n\te.d[24] += calc_6ds_comp(K, Ri, Rj, 1, 2, 2, 1, 3, 3);\n\te.d[25] += calc_6ds_comp(K, Ri, Rj, 1, 2, 2, 2, 2, 2);\n\te.d[26] += calc_6ds_comp(K, Ri, Rj, 1, 2, 2, 2, 2, 3);\n\te.d[27] += calc_6ds_comp(K, Ri, Rj, 1, 2, 2, 2, 3, 3);\n\te.d[28] += calc_6ds_comp(K, Ri, Rj, 1, 2, 2, 3, 3, 3);\n\n\te.d[29] += calc_6ds_comp(K, Ri, Rj, 1, 2, 3, 1, 3, 3);\n\te.d[30] += calc_6ds_comp(K, Ri, Rj, 1, 2, 3, 2, 2, 2);\n\te.d[31] += calc_6ds_comp(K, Ri, Rj, 1, 2, 3, 2, 2, 3);\n\te.d[32] += calc_6ds_comp(K, Ri, Rj, 1, 2, 3, 2, 3, 3);\n\te.d[33] += calc_6ds_comp(K, Ri, Rj, 1, 2, 3, 3, 3, 3);\n\n\te.d[34] += calc_6ds_comp(K, Ri, Rj, 1, 3, 3, 2, 2, 2);\n\te.d[35] += calc_6ds_comp(K, Ri, Rj, 1, 3, 3, 2, 2, 3);\n\te.d[36] += calc_6ds_comp(K, Ri, Rj, 1, 3, 3, 2, 3, 3);\n\te.d[37] += calc_6ds_comp(K, Ri, Rj, 1, 3, 3, 3, 3, 3);\n\n\te.d[38] += calc_6ds_comp(K, Ri, Rj, 2, 2, 2, 2, 2, 2);\n\te.d[39] += calc_6ds_comp(K, Ri, Rj, 2, 2, 2, 2, 2, 3);\n\te.d[40] += calc_6ds_comp(K, Ri, Rj, 2, 2, 2, 2, 3, 3);\n\te.d[41] += calc_6ds_comp(K, Ri, Rj, 2, 2, 2, 3, 3, 3);\n\n\te.d[42] += calc_6ds_comp(K, Ri, Rj, 2, 2, 3, 2, 3, 3);\n\te.d[43] += calc_6ds_comp(K, Ri, Rj, 2, 2, 3, 3, 3, 3);\n\n\te.d[44] += calc_6ds_comp(K, Ri, Rj, 2, 3, 3, 3, 3, 3);\n\n\te.d[45] += calc_6ds_comp(K, Ri, Rj, 3, 3, 3, 3, 3, 3);\n}\n\n"
  },
  {
    "path": "FECore/tens6d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"tensor_base.h\"\n\n//-----------------------------------------------------------------------------\n// Tensor classes defined in this file\nclass tens6ds;\nclass tens6d;\n\n//-----------------------------------------------------------------------------\n// traits for these classes defining the number of components\ntemplate <> class tensor_traits<tens6ds>{public: enum { NNZ =  55}; };\ntemplate <> class tensor_traits<tens6d >{public: enum { NNZ = 729}; };\n\n//-----------------------------------------------------------------------------\n//! Class for 6th order tensors. This class assumes the following symmetries:\n//  - full (minor) symmetry in the first three legs \n//  - full (minor) symmetry in the last three legs \n//  - major symmetry (A[i,j,k;l,m,n] = A[l,m,n;i,j,k])\n//\n// This tensor is effectively stored as an upper-triangular 10x10 matrix\n// using column major ordering.\n//    \n//       / 0  1  3  6 10 15 21 28 37 46 \\     / A00 A01 A02  ...   A09 \\\n//      |     2  4  7 11 16 22 29 38 47  |   |      A11 A12  ...   A19  |\n//      |        5  8 12 17 23 30 39 48  |   |          A22  ...   A18  |\n//      |           9 13 18 24 31 40 49  |   |            .             |\n//      |             14 19 25 32 41 50  |   |              .           |\n//  A = |                20 26 33 42 51  | = |                .         |\n//      |                   27 34 43 52  |   |                          |\n//      |                      35 44 53  |   |                          |\n//      |                         45 54  |   |                          |\n//      \\                            55 /     \\                    A99 / \n//\n//  where A[I,J] = A[i,j,k;l,m,n] using the following convention\n//\n//    I/J  |  i/l   j/m    k/n\n// --------+-------------------\n//     0   |   0      0      0\n//     1   |   0      0      1\n//     2   |   0      0      2\n//     3   |   0      1      1\n//     4   |   0      1      2\n//     5   |   0      2      2\n//     6   |   1      1      1\n//     7   |   1      1      2\n//     8   |   1      2      2\n//     9   |   2      2      2\n//\nclass tens6ds : public tensor_base<tens6ds>\n{\npublic:\n\t// constructors\n\ttens6ds(){}\n\npublic:\n\t// access operator\n\tdouble operator () (int i, int j, int k, int l, int m, int n);\n};\n\nvoid calculate_e2O(tens6ds& e, double K[3][3], double Ri[3], double Rj[3] );\n\n\n//-----------------------------------------------------------------------------\n// class for general 6-th order tensors. No assumed symmetries\nclass tens6d : public tensor_base<tens6d>\n{\npublic:\n\t// default constructor\n\ttens6d() {}\n\n\t// access operators\n\tdouble  operator () (int i, int j, int k, int l, int m, int n) const;\n\tdouble& operator () (int i, int j, int k, int l, int m, int n);\n};\n\n// The following file contains the actual definition of the class functions\n#include \"tens6d.hpp\"\n#include \"tens6ds.hpp\"\n"
  },
  {
    "path": "FECore/tens6d.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens5d.h\n// Users should not include this file manually!\n\ninline double tens6d::operator () (int i, int j, int k, int l, int m, int n) const\n{\n\tint R = 3*(3*i + j) + k;\n\tint C = 3*(3*l + m) + n;\n\treturn d[27*C + R];\n}\n\ninline double& tens6d::operator () (int i, int j, int k, int l, int m, int n)\n{\n\tint R = 3*(3*i + j) + k;\n\tint C = 3*(3*l + m) + n;\n\treturn d[27*C + R];\n}\n"
  },
  {
    "path": "FECore/tens6ds.hpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n// NOTE: This file is automatically included from tens6d.h\n// Users should not include this file manually!\n\n// access operator\ninline double tens6ds::operator() (int i, int j, int k, int l, int m, int n)\n{\n\t// lookup table convering triplets to a row/column index\n\tconst int LUT[3][3][3] = {\n\t\t{{0,1,2},{1,3,4},{2,4,5}},\n\t\t{{1,3,4},{3,6,7},{4,7,8}},\n\t\t{{2,4,5},{4,7,8},{5,8,9}}};\n\n\t// index to start of columns\n\tconst int M[10] = {0,1,3,6,10,15,21,28,37,46};\n\n\tint I = LUT[i][j][k];\n\tint J = LUT[l][m][n];\n\treturn (I <= J ? d[M[J]+I] : d[M[I]+J]);\n}\n"
  },
  {
    "path": "FECore/tensor_base.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n//-----------------------------------------------------------------------------\n// traits class for tensors. Classes derived from tensor_base must specialize\n// this class and define the NNZ enum variable which defines the number of components\n// stored for that tensor class.\ntemplate <class T> class tensor_traits {};\n\n//-----------------------------------------------------------------------------\n// Template class for constructing some of the higher order tensor classes.\n// Defines storage as well as some basic operations that do not depend on the \n// order in which the tensor components are stored.\ntemplate <class T> class tensor_base\n{\n\tenum { NNZ = tensor_traits<T>::NNZ};\n\npublic:\n\ttensor_base(){}\n\n\t// arithmetic operators\n\tT operator + (const T& t) const;\n\tT operator - (const T& t) const;\n\tT operator * (double g) const;\n\tT operator / (double g) const;\n\n\t// arithmetic assignment operators\n\tT& operator += (const T& t);\n\tT& operator -= (const T& t);\n\tT& operator *= (double g);\n\tT& operator /= (double g);\n\n\t// unary operators\n\tT operator - () const;\n\n\t// initialize to zero\n\tvoid zero();\n\npublic:\n\tdouble\td[NNZ];\n};\n\n// operator +\ntemplate<class T> T tensor_base<T>::operator + (const T& t) const\n{\n\tT s;\n\tfor (int i=0; i<NNZ; i++) s.d[i] = d[i] + t.d[i];\n\treturn s;\n}\n\n// operator -\ntemplate<class T> T tensor_base<T>::operator - (const T& t) const\n{\n\tT s;\n\tfor (int i=0; i<NNZ; i++) s.d[i] = d[i] - t.d[i];\n\treturn s;\n}\n\n// operator *\ntemplate<class T> T tensor_base<T>::operator * (double g) const\n{\n\tT s;\n\tfor (int i=0; i<NNZ; i++) s.d[i] = g*d[i];\n\treturn s;\n}\n\n// operator /\ntemplate<class T> T tensor_base<T>::operator / (double g) const\n{\n\tT s;\n\tfor (int i=0; i<NNZ; i++) s.d[i] = d[i]/g;\n\treturn s;\n}\n\n// assignment operator +=\ntemplate<class T> T& tensor_base<T>::operator += (const T& t)\n{\n\tfor (int i=0; i<NNZ; i++) d[i] += t.d[i];\n\treturn static_cast<T&>(*this);\n}\n\n// assignment operator -=\ntemplate<class T> T& tensor_base<T>::operator -= (const T& t)\n{\n\tfor (int i=0; i<NNZ; i++) d[i] -= t.d[i];\n\treturn static_cast<T&>(*this);\n}\n\n// assignment operator *=\ntemplate<class T> T& tensor_base<T>::operator *= (double g)\n{\n\tfor (int i=0; i<NNZ; i++) d[i] *= g;\n\treturn static_cast<T&>(*this);\n}\n\n// assignment operator /=\ntemplate<class T> T& tensor_base<T>::operator /= (double g)\n{\n\tfor (int i=0; i<NNZ; i++) d[i] /= g;\n\treturn static_cast<T&>(*this);\n}\n\n// unary operator -\ntemplate<class T> T tensor_base<T>::operator - () const\n{\n\tT s;\n\tfor (int i = 0; i < NNZ; i++) s.d[i] = -d[i];\n\treturn s;\n}\n\n// intialize to zero\ntemplate<class T> void tensor_base<T>::zero()\n{\n\tfor (int i = 0; i < NNZ; i++) d[i] = 0.0;\n}\n"
  },
  {
    "path": "FECore/tools.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"tools.h\"\n#include <math.h>\n#include <limits>\n#include <assert.h>\n#include <float.h>\n#include \"matrix.h\"\n\n#ifndef SQR\n#define SQR(x) ((x)*(x))\n#endif\n\n#define FMAX(a,b) ((a)>(b) ? (a) : (b))\n\n//=============================================================================\n// powell's method\n// from Numerical Recipes in C, section 10.5, page 417-419\n// modified for this application\n//\nvoid powell(double* p, double* xi, int n, double ftol, int* iter, double* fret, double(*fnc)(double[]))\n{\n\tint i, j, ibig;\n\tdouble fp, fptt, del, t;\n\n\tconst int ITMAX = 200;\n\tconst double TINY = 1.0e-25;\n\n\tdouble* pt = new double[n];\n\tdouble* ptt = new double[n];\n\tdouble* xit = new double[n];\n\n\t*fret = (*fnc)(p);\n\tfor (i = 0; i<n; i++) pt[i] = p[i];\t\t\t\t// save the initial point\n\tfor (*iter = 1;; ++(*iter))\n\t{\n\t\tfp = *fret;\n\t\tibig = 0;\n\t\tdel = 0.0;\t\t// will be biggest function decrease\n\n\t\tfor (i = 0; i<n; i++)\t// in each iteration loop over all directions in the set\n\t\t{\n\t\t\tfor (j = 0; j<n; j++) xit[j] = xi[i*n + j];\t// copy the direction\n\t\t\tfptt = (*fret);\n\t\t\tlinmin(p, xit, n, fret, fnc);\t\t// minimize along it\n\t\t\tif (fptt - (*fret) > del)\t\t\t// and record it as the largets decrease so far \t\n\t\t\t{\n\t\t\t\tdel = fptt - (*fret);\n\t\t\t\tibig = i;\n\t\t\t}\n\t\t}\n\n\t\t// termination criterion\n\t\tif (2.0*(fp - (*fret)) <= ftol*(fabs(fp) + fabs(*fret)) + TINY)\n\t\t{\n\t\t\tdelete[] pt;\n\t\t\tdelete[] ptt;\n\t\t\tdelete[] xit;\n\t\t\treturn;\n\t\t}\n\n\t\t// check we are not exceeding max number of iterations\n\t\tif (*iter == ITMAX)\n\t\t{\n\t\t\tprintf(\"FATAL ERROR : Max iterations reached\\n\");\n\t\t\texit(0);\n\t\t}\n\n\t\tfor (j = 0; j<n; j++)\n\t\t{\n\t\t\tptt[j] = 2.0*p[j] - pt[j];\n\t\t\txit[j] = p[j] - pt[j];\n\t\t\tpt[j] = p[j];\n\t\t}\n\n\t\tfptt = (*fnc)(ptt);\n\t\tif (fptt < fp)\n\t\t{\n\t\t\tt = 2.0*(fp - 2.0*(*fret) + fptt)*SQR(fp - (*fret) - del) - del*SQR(fp - fptt);\n\t\t\tif (t < 0.0)\n\t\t\t{\n\t\t\t\tlinmin(p, xit, n, fret, fnc);\n\t\t\t\tfor (j = 0; j<n; j++)\n\t\t\t\t{\n\t\t\t\t\txi[ibig *n + j] = xi[(n - 1)*n + j];\n\t\t\t\t\txi[(n - 1)*n + j] = xit[j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//--------------------------------------------------------------------------------\n// line minimization routine\n// from Numerical Recipes in C, section 10.5, page 419-420\n// modified for this application\n//\nint ncom;\ndouble *pcom, *xicom, *xt;\ndouble(*nrfunc)(double[]);\n\ndouble f1dim(double x)\n{\n\tint j;\n\tdouble f;\n\tfor (j = 0; j<ncom; j++) xt[j] = pcom[j] + x*xicom[j];\n\tf = (*nrfunc)(xt);\n\treturn f;\n}\n\nvoid linmin(double* p, double* xi, int n, double* fret, double(*fnc)(double[]))\n{\n\tint j;\n\tdouble xx, xmin, fx, fb, fa, bx, ax;\n\tconst double TOL = 2.0e-4;\n\n\tncom = n;\n\tpcom = new double[n];\n\txicom = new double[n];\n\txt = new double[n];\n\n\tnrfunc = fnc;\n\tfor (j = 0; j<n; j++)\n\t{\n\t\tpcom[j] = p[j];\n\t\txicom[j] = xi[j];\n\t}\n\tax = 0.0;\n\txx = 1.0e-4;\n\tmnbrak(&ax, &xx, &bx, &fa, &fx, &fb, f1dim);\n\t*fret = brent(ax, xx, bx, f1dim, TOL, &xmin);\n\tfor (j = 0; j<n; j++)\n\t{\n\t\txi[j] *= xmin;\n\t\tp[j] += xi[j];\n\t}\n\n\tdelete[] xt;\n\tdelete[] pcom;\n\tdelete[] xicom;\n}\n\n\n//--------------------------------------------------------------------------------\n// routine to find 1D minimum\n// from Numerical Recipes in C, section 10.3, page 404-405\n// modified for this application\n//\n#define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d);\n#define SIGN(a,b) ((b)>=0.0 ? (a) : (-(a)))\n\ndouble brent(double ax, double bx, double cx, double(*f)(double), double tol, double* xmin)\n{\n\tconst int ITMAX = 100;\n\tconst double CGOLD = 0.3819660;\n\tconst double ZEPS = 1.0e-10;\n\tint iter;\n\tdouble a, b, d, etemp, fu, fv, fw, fx, p, q, r, tol1, tol2, u, v, w, x, xm;\n\tdouble e = 0.0;\n\n\ta = (ax < cx ? ax : cx);\n\tb = (ax > cx ? ax : cx);\n\tx = w = v = bx;\n\tfw = fv = fx = (*f)(x);\n\tfor (iter = 1; iter <= ITMAX; iter++)\n\t{\n\t\txm = 0.5*(a + b);\n\t\ttol2 = 2.0*(tol1 = tol*fabs(x) + ZEPS);\n\t\tif (fabs(x - xm) <= (tol2 - 0.5*(b - a)))\n\t\t{\n\t\t\t*xmin = x;\n\t\t\treturn fx;\n\t\t}\n\t\tif (fabs(e) > tol1)\n\t\t{\n\t\t\tr = (x - w)*(fx - fv);\n\t\t\tq = (x - v)*(fx - fw);\n\t\t\tp = (x - v)*q - (x - w)*r;\n\t\t\tq = 2.0*(q - r);\n\t\t\tif (q > 0.0) p = -p;\n\t\t\tq = fabs(q);\n\t\t\tetemp = e;\n\t\t\te = d;\n\t\t\tif (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a - x) || p >= q*(b - x))\n\t\t\t\td = CGOLD*(e = (x >= xm ? a - x : b - x));\n\t\t\telse\n\t\t\t{\n\t\t\t\td = p / q;\n\t\t\t\tu = x + d;\n\t\t\t\tif (u - a < tol2 || b - u < tol2) d = SIGN(tol1, xm - x);\n\t\t\t}\n\t\t}\n\t\telse d = CGOLD*(e = (x >= xm ? a - x : b - x));\n\t\tu = (fabs(d) >= tol1 ? x + d : x + SIGN(tol1, d));\n\t\tfu = (*f)(u);\n\n\t\tif (fu <= fx)\n\t\t{\n\t\t\tif (u >= x) a = x; else b = x;\n\t\t\tSHFT(v, w, x, u);\n\t\t\tSHFT(fv, fw, fx, fu);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (u<x) a = u; else b = u;\n\t\t\tif (fu <= fw || w == x)\n\t\t\t{\n\t\t\t\tv = w;\n\t\t\t\tw = u;\n\t\t\t\tfv = fw;\n\t\t\t\tfw = fu;\n\t\t\t}\n\t\t\telse if (fu <= fv || v == x || v == w)\n\t\t\t{\n\t\t\t\tv = u;\n\t\t\t\tfv = fu;\n\t\t\t}\n\t\t}\n\t}\n\n\tfprintf(stderr, \"ERROR : Too many iterations in brent routine\\n\");\n\n\t*xmin = x;\n\treturn fx;\n}\n\n\n#define SHFT2(a, b, c) (a)=(b);(b)=(c);\n#define SHFT(a, b, c, d) (a)=(b);(b)=(c);(c)=(d);\n#define SIGN2(a, b) ((b)>=0?fabs(a):(-fabs(a)))\n\nvoid mnbrak(double* pa, double* pb, double* pc, double* pfa, double* pfb, double* pfc, double(*func)(double))\n{\n\tconst double GOLD = 1.618034;\n\tconst double TINY = 1.0e-20;\n\tconst double GLIMIT = 100;\n\n\tdouble& a = *pa;\n\tdouble& b = *pb;\n\tdouble& c = *pc;\n\n\tdouble& fa = *pfa;\n\tdouble& fb = *pfb;\n\tdouble& fc = *pfc;\n\n\tdouble ulim, u, r, q, fu, dum;\n\n\tfa = func(a);\n\tfb = func(b);\n\tif (fb>fa)\n\t{\n\t\tSHFT(dum, a, b, dum);\n\t\tSHFT(dum, fb, fa, dum);\n\t}\n\n\tc = b + GOLD*(b - a);\n\tfc = func(c);\n\twhile (fb > fc)\n\t{\n\t\tr = (b - a)*(fb - fc);\n\t\tq = (b - c)*(fb - fa);\n\t\tu = b - ((b - c)*q - (b - a)*r) / (2.0*SIGN2(FMAX(fabs(q - r), TINY), q - r));\n\n\t\tulim = b + GLIMIT*(c - b);\n\n\t\tif ((b - u)*(u - c) > 0)\n\t\t{\n\t\t\tfu = func(u);\n\t\t\tif (fu < fc)\n\t\t\t{\n\t\t\t\ta = b;\n\t\t\t\tb = u;\n\t\t\t\tfa = fb;\n\t\t\t\tfb = fu;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (fu > fb)\n\t\t\t{\n\t\t\t\tc = u;\n\t\t\t\tfc = fu;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tu = c + GOLD*(c - b);\n\t\t\tfu = func(u);\n\t\t}\n\t\telse if ((c - u)*(u - ulim) > 0)\n\t\t{\n\t\t\tfu = func(u);\n\t\t\tif (fu < fc)\n\t\t\t{\n\t\t\t\tSHFT(b, c, u, c + GOLD*(c - b));\n\t\t\t\tSHFT(fb, fc, fu, func(u));\n\t\t\t}\n\t\t}\n\t\telse if ((u - ulim)*(ulim - c) >= 0)\n\t\t{\n\t\t\tu = ulim;\n\t\t\tfu = func(u);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tu = c + GOLD*(c - b);\n\t\t\tfu = func(u);\n\t\t}\n\n\t\tSHFT(a, b, c, u);\n\t\tSHFT(fa, fb, fc, fu);\n\t}\n}\n\ndouble golden(double ax, double bx, double cx, double(*f)(double), double tol, double* xmin)\n{\n\tconst double R = 0.61803399;\n\tconst double C = 1 - R;\n\n\tdouble f1, f2, x0, x1, x2, x3;\n\n\tx0 = ax;\n\tx3 = cx;\n\n\tif (fabs(cx - bx) > fabs(bx - ax))\n\t{\n\t\tx1 = bx;\n\t\tx2 = bx + C*(cx - bx);\n\t}\n\telse\n\t{\n\t\tx2 = bx;\n\t\tx1 = bx - C*(bx - ax);\n\t}\n\tf1 = f(x1);\n\tf2 = f(x2);\n\n\twhile (fabs(x3 - x0) > tol*(fabs(x1) + fabs(x2)))\n\t{\n\t\tif (f2 < f1)\n\t\t{\n\t\t\tSHFT(x0, x1, x2, R*x1 + C*x3);\n\t\t\tSHFT2(f1, f2, f(x2));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSHFT(x3, x2, x1, R*x2 + C*x0);\n\t\t\tSHFT2(f2, f1, f(x1));\n\t\t}\n\t}\n\n\tif (f1 < f2)\n\t{\n\t\t*xmin = x1;\n\t\treturn f1;\n\t}\n\telse\n\t{\n\t\t*xmin = x2;\n\t\treturn f2;\n\t}\n}\n\n//-----------------------------------------------------------------------------\ndouble zbrent(double func(double, void*), double x1, double x2, double tol, void* data)\n{\n\tconst int ITMAX = 100;\n\tconst double EPS = DBL_EPSILON;\n\tint iter;\n\tdouble a=x1, b=x2, c=x2, d, e, min1, min2;\n\tdouble fa=func(a, data), fb = func(b, data), fc, p, q, r, s, tol1, xm;\n\n\tif ((fa > 0.0 && fb > 00) || (fa < 0.0 && fb < 0.0))\n\t{\n\t\tassert(false);\n\t\treturn 0.0;\n\t}\n\n\tfc = fb;\n\tfor (iter = 0; iter<ITMAX; iter++) {\n\t\tif ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {\n\t\t\tc = a;\n\t\t\tfc = fa;\n\t\t\te = d = b - a;\n\t\t}\n\t\tif (fabs(fc) < fabs(fb)) {\n\t\t\ta = b;\n\t\t\tb = c;\n\t\t\tc = a;\n\t\t\tfa = fb;\n\t\t\tfb = fc;\n\t\t\tfc = fa;\n\t\t}\n\t\ttol1 = 2.0*EPS*fabs(b) + 0.5*tol;\n\t\txm = 0.5*(c - b);\n\t\tif (fabs(xm) <= tol1 || fb == 0.0) return b;\n\t\tif (fabs(e) >= tol1 && fabs(fa) > fabs(fb)) {\n\t\t\ts = fb / fa;\n\t\t\tif (a == c) {\n\t\t\t\tp = 2.0*xm*s;\n\t\t\t\tq = 1.0 - s;\n\t\t\t} else {\n\t\t\t\tq = fa/fc;\n\t\t\t\tr = fb/fc;\n\t\t\t\tp = s*(2.0*xm*q*(q-r)-(b-a)*(r - 1.0));\n\t\t\t\tq = (q - 1.0)*(r - 1.0)*(s - 1.0);\n\t\t\t}\n\t\t\tif (p > 0.0) q = -q;\n\t\t\tp = fabs(p);\n\t\t\tmin1 = 3.0*xm*q - fabs(tol1*q);\n\t\t\tmin2 = fabs(e*q);\n\t\t\tif (2.0*p < (min1 < min2 ? min1 : min2)) {\n\t\t\t\te = d;\n\t\t\t\td = p/q;\n\t\t\t} else {\n\t\t\t\td = xm;\n\t\t\t\te = d;\n\t\t\t}\n\t\t} else {\n\t\t\td = xm;\n\t\t\te = d;\n\t\t}\n\t\ta = b;\n\t\tfa = fb;\n\t\tif (fabs(d) > tol1)\n\t\t\tb += d;\n\t\telse\n\t\t\tb += SIGN(tol1, xm);\n\t\tfb = func(b, data);\n\t}\n\tassert(false);\n\treturn 0.0;\n}\n\n//-----------------------------------------------------------------------------\nbool zbrac(double f(double, void*), double& x1, double& x2, void* data)\n{\n\tconst int MAXTRY = 50;\n\tconst double FACTOR = 1.6;\n\n\tif (x1 == x2)\n\t{\n\t\tassert(false);\n\t\treturn false;\n\t}\n\n\tdouble f1 = f(x1, data);\n\tdouble f2 = f(x2, data);\n\tfor (int j=0; j<MAXTRY; ++j)\n\t{\n\t\tif (f1*f2 < 0.0) return true;\n\t\tif (fabs(f1) < fabs(f2))\n\t\t\tf1 = f(x1 += FACTOR*(x1 - x2), data);\n\t\telse\n\t\t\tf2 = f(x2 += FACTOR*(x2 - x1), data);\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid solve_3x3(double A[3][3], double b[3], double x[3])\n{\n\tdouble D = A[0][0] * A[1][1] * A[2][2] + A[0][1] * A[1][2] * A[2][0] + A[1][0] * A[2][1] * A[0][2] \\\n\t\t- A[1][1] * A[2][0] * A[0][2] - A[2][2] * A[1][0] * A[0][1] - A[0][0] * A[2][1] * A[1][2];\n\n\tassert(D != 0);\n\n\tdouble Ai[3][3];\n\tAi[0][0] = A[1][1] * A[2][2] - A[2][1] * A[1][2];\n\tAi[0][1] = A[2][1] * A[0][2] - A[0][1] * A[2][2];\n\tAi[0][2] = A[0][1] * A[1][2] - A[1][1] * A[0][2];\n\n\tAi[1][0] = A[2][0] * A[1][2] - A[1][0] * A[2][2];\n\tAi[1][1] = A[0][0] * A[2][2] - A[2][0] * A[0][2];\n\tAi[1][2] = A[1][0] * A[0][2] - A[0][0] * A[1][2];\n\n\tAi[2][0] = A[1][0] * A[2][1] - A[2][0] * A[1][1];\n\tAi[2][1] = A[2][0] * A[0][1] - A[0][0] * A[2][1];\n\tAi[2][2] = A[0][0] * A[1][1] - A[0][1] * A[1][0];\n\n\tx[0] = (Ai[0][0] * b[0] + Ai[0][1] * b[1] + Ai[0][2] * b[2]) / D;\n\tx[1] = (Ai[1][0] * b[0] + Ai[1][1] * b[1] + Ai[1][2] * b[2]) / D;\n\tx[2] = (Ai[2][0] * b[0] + Ai[2][1] * b[1] + Ai[2][2] * b[2]) / D;\n\n\n#ifndef NDEBUG\n\tdouble r[3];\n\tr[0] = b[0] - (A[0][0] * x[0] + A[0][1] * x[1] + A[0][2] * x[2]);\n\tr[1] = b[1] - (A[1][0] * x[0] + A[1][1] * x[1] + A[1][2] * x[2]);\n\tr[2] = b[2] - (A[2][0] * x[0] + A[2][1] * x[1] + A[2][2] * x[2]);\n\n\tdouble nr = sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);\n#endif\n}\n\n//=============================================================================\n\nbool LinearRegression(const std::vector<std::pair<double, double> >& data, std::pair<double, double>& res)\n{\n\tres.first = 0.0;\n\tres.second = 0.0;\n\n\tint n = (int)data.size();\n\tif (n == 0) return false;\n\n\tdouble mx = 0.0, my = 0.0;\n\tdouble sxx = 0.0, sxy = 0.0;\n\tfor (int i = 0; i < n; ++i)\n\t{\n\t\tdouble xi = data[i].first;\n\t\tdouble yi = data[i].second;\n\t\tmx += xi;\n\t\tmy += yi;\n\n\t\tsxx += xi * xi;\n\t\tsxy += xi * yi;\n\t}\n\tmx /= (double)n;\n\tmy /= (double)n;\n\tsxx /= (double)n;\n\tsxy /= (double)n;\n\n\tdouble D = sxx - mx * mx;\n\tif (D == 0.0) return false;\n\n\tdouble a = (sxy - mx * my) / D;\n\tdouble b = my - a * mx;\n\n\tres.first = a;\n\tres.second = b;\n\n\treturn true;\n}\n\nclass Func\n{\npublic:\n\tFunc() {}\n\tvirtual ~Func() {}\n\tvirtual void setParams(const std::vector<double>& v) = 0;\n\tvirtual double value(double x) = 0;\n\tvirtual double derive1(double x, int n) = 0;\n\tvirtual double derive2(double x, int n1, int n2) = 0;\n};\n\nclass Quadratic : public Func\n{\npublic:\n\tQuadratic() : m_a(0.0), m_b(0.0), m_c(0.0) {}\n\tvoid setParams(const std::vector<double>& v) override { m_a = v[0]; m_b = v[1]; m_c = v[2]; }\n\tdouble value(double x) override { return m_a * x * x + m_b * x + m_c; }\n\tdouble derive1(double x, int n) override\n\t{\n\t\tswitch (n)\n\t\t{\n\t\tcase 0: return x * x; break;\n\t\tcase 1: return x; break;\n\t\tcase 2: return 1; break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn 0.0;\n\t\t}\n\t}\n\n\tdouble derive2(double x, int n1, int n2) override\n\t{\n\t\treturn 0.0;\n\t}\n\nprivate:\n\tdouble\tm_a, m_b, m_c;\n};\n\nclass Exponential : public Func\n{\npublic:\n\tExponential() : m_a(0.0), m_b(0.0) {}\n\tvoid setParams(const std::vector<double>& v) override { m_a = v[0]; m_b = v[1]; }\n\tdouble value(double x) override { return m_a * exp(x * m_b); }\n\tdouble derive1(double x, int n) override\n\t{\n\t\tswitch (n)\n\t\t{\n\t\tcase 0: return exp(x * m_b); break;\n\t\tcase 1: return m_a * x * exp(x * m_b); break;\n\t\tdefault:\n\t\t\tassert(false);\n\t\t\treturn 0.0;\n\t\t}\n\t}\n\n\tdouble derive2(double x, int n1, int n2) override\n\t{\n\t\tif ((n1 == 0) && (n2 == 0)) return 0;\n\t\telse if ((n1 == 0) && (n2 == 1)) return x * exp(x * m_b);\n\t\telse if ((n1 == 1) && (n2 == 0)) return x * exp(x * m_b);\n\t\telse if ((n1 == 1) && (n2 == 1)) return m_a * x * x * exp(x * m_b);\n\t\telse return 0.0;\n\t}\n\nprivate:\n\tdouble\tm_a, m_b;\n};\n\nbool NonlinearRegression(const std::vector<std::pair<double, double> >& data, std::vector<double>& res, int func)\n{\n\tint MAX_ITER = 10;\n\tint niter = 0;\n\n\tint n = (int)data.size();\n\tint m = (int)res.size();\n\n\tFunc* f = 0;\n\tswitch (func)\n\t{\n\tcase 1: f = new Quadratic; break;\n\tcase 2: f = new Exponential; break;\n\t}\n\tif (f == 0) return false;\n\n\tstd::vector<double> R(m, 0.0), da(m, 0.0);\n\tmatrix K(m, m); K.zero();\n\n\tconst double absTol = 1e-15;\n\tconst double relTol = 1e-3;\n\tdouble norm0 = 0.0;\n\tdo\n\t{\n\t\tf->setParams(res);\n\n\t\t// evaluate residual (and norm)\n\t\tdouble norm = 0.0;\n\t\tfor (int i = 0; i < m; ++i)\n\t\t{\n\t\t\tR[i] = 0.0;\n\t\t\tfor (int j = 0; j < n; ++j)\n\t\t\t{\n\t\t\t\tdouble xj = data[j].first;\n\t\t\t\tdouble yj = data[j].second;\n\t\t\t\tdouble fj = f->value(xj);\n\t\t\t\tdouble Dfi = f->derive1(xj, i);\n\t\t\t\tR[i] -= (fj - yj) * Dfi;\n\t\t\t}\n\n\t\t\tnorm += R[i] * R[i];\n\t\t}\n\t\tnorm = sqrt(norm / n);\n\n\t\tif (norm < absTol) break;\n\n\t\tif (niter == 0) norm0 = norm;\n\t\telse\n\t\t{\n\t\t\tdouble rel = norm / norm0;\n\t\t\tif (rel < relTol) break;\n\t\t}\n\n\t\t// evaluate Jacobian\n\t\tfor (int i = 0; i < m; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j < m; ++j)\n\t\t\t{\n\t\t\t\tdouble Kij = 0.0;\n\t\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t\t{\n\t\t\t\t\tdouble xk = data[k].first;\n\t\t\t\t\tdouble yk = data[k].second;\n\t\t\t\t\tdouble fk = f->value(xk);\n\n\t\t\t\t\tdouble Dfi = f->derive1(xk, i);\n\t\t\t\t\tdouble Dfj = f->derive1(xk, j);\n\n\t\t\t\t\tdouble Dfij = f->derive2(xk, i, j);\n\n\t\t\t\t\tKij += Dfi * Dfj + (fk - yk) * Dfij;\n\t\t\t\t}\n\n\t\t\t\tK[i][j] = Kij;\n\t\t\t}\n\t\t}\n\n\t\t// solve linear system\n\t\tK.solve(da, R);\n\n\t\tfor (int i = 0; i < m; ++i) res[i] += da[i];\n\n\t\tniter++;\n\t} while (niter < MAX_ITER);\n\n\tdelete f;\n\n\treturn (niter < MAX_ITER);\n}\n\n//=============================================================================\n//! Polynomial root solver\n// function whose roots needs to be evaluated\nvoid fn(std::complex<double>& z, std::complex<double>& fz, std::vector<double> a)\n{\n\tint n = (int)a.size() - 1;\n\tfz = a[0];\n\tstd::complex<double> x(1, 0);\n\n\tfor (int i = 1; i <= n; ++i) {\n\t\tx *= z;\n\t\tfz += a[i] * x;\n\t}\n\treturn;\n}\n\n//-----------------------------------------------------------------------------\n// deflation\nbool dflate(std::complex<double> zero, const int i, int& kount,\n\tstd::complex<double>& fzero, std::complex<double>& fzrdfl,\n\tstd::complex<double>* zeros, std::vector<double> a)\n{\n\tstd::complex<double> den;\n\t++kount;\n\tfn(zero, fzero, a);\n\tfzrdfl = fzero;\n\tif (i < 1) return false;\n\tfor (int j = 0; j < i; ++j) {\n\t\tden = zero - zeros[j];\n\t\tif (abs(den) == 0) {\n\t\t\tzeros[i] = zero * 1.001;\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\tfzrdfl = fzrdfl / den;\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\n// Muller's method for solving roots of a function\nbool muller(bool fnreal, std::complex<double>* zeros, const int n, const int nprev,\n\tconst int maxit, const double ep1, const double ep2, std::vector<double> a)\n{\n\tint kount;\n\tstd::complex<double> dvdf1p, fzrprv, fzrdfl, divdf1, divdf2;\n\tstd::complex<double> fzr, zero, c, den, sqr, z;\n\n\t// initialization\n\tdouble eps1 = (ep1 > 1e-12) ? ep1 : 1e-12;\n\tdouble eps2 = (ep2 > 1e-20) ? ep2 : 1e-20;\n\n\tfor (int i = nprev; i < n; ++i) {\n\t\tkount = 0;\n\teloop:\n\t\tzero = zeros[i];\n\t\tstd::complex<double> h = 0.5;\n\t\tstd::complex<double> hprev = -1.0;\n\n\t\t// compute first three estimates for zero as\n\t\t// zero+0.5, zero-0.5, zero\n\t\tz = zero + 0.5;\n\t\tif (dflate(z, i, kount, fzr, dvdf1p, zeros, a)) goto eloop;\n\t\tz = zero - 0.5;\n\t\tif (dflate(z, i, kount, fzr, fzrprv, zeros, a)) goto eloop;\n\t\tdvdf1p = (fzrprv - dvdf1p) / hprev;\n\t\tif (dflate(zero, i, kount, fzr, fzrdfl, zeros, a)) goto eloop;\n\t\tdo {\n\t\t\tdivdf1 = (fzrdfl - fzrprv) / h;\n\t\t\tdivdf2 = (divdf1 - dvdf1p) / (h + hprev);\n\t\t\thprev = h;\n\t\t\tdvdf1p = divdf1;\n\t\t\tc = divdf1 + h * divdf2;\n\t\t\tsqr = c * c - 4. * fzrdfl * divdf2;\n\t\t\tif (fnreal && (sqr.real() < 0)) sqr = 0;\n\t\t\tsqr = sqrt(sqr);\n\t\t\tif (c.real() * sqr.real() + c.imag() * sqr.imag() < 0) {\n\t\t\t\tden = c - sqr;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tden = c + sqr;\n\t\t\t}\n\t\t\tif (abs(den) <= 0.) den = 1.;\n\t\t\th = -2. * fzrdfl / den;\n\t\t\tfzrprv = fzrdfl;\n\t\t\tzero = zero + h;\n\t\tdloop:\n\t\t\tfn(zero, fzrdfl, a);\n\t\t\t// check for convergence\n\t\t\tif (abs(h) < eps1 * abs(zero)) break;\n\t\t\tif (abs(fzrdfl) < eps2) break;\n\t\t\t// check for divergence\n\t\t\tif (abs(fzrdfl) >= 10. * abs(fzrprv)) {\n\t\t\t\th /= 2.;\n\t\t\t\tzero -= h;\n\t\t\t\tgoto dloop;\n\t\t\t}\n\t\t} while (kount < maxit);\n\t\tzeros[i] = zero;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// Newton's method for finding nearest root of a polynomial\nbool newton(double& zero, const int n, const int maxit,\n\tconst double ep1, const double ep2, std::vector<double> a)\n{\n\tbool done = false;\n\tbool conv = false;\n\tint it = 0;\n\tdouble f, df, x, dx, xi;\n\tx = zero;\n\n\twhile (!done) {\n\t\t// Evaluate function and its derivative\n\t\txi = x;\n\t\tf = a[0] + a[1] * xi;\n\t\tdf = a[1];\n\t\tfor (int i = 2; i <= n; ++i) {\n\t\t\tdf += i * a[i] * xi;\n\t\t\txi *= x;\n\t\t\tf += a[i] * xi;\n\t\t}\n\t\tif (df == 0) break;\n\t\t// check absolute convergence and don't update x if met\n\t\tif (abs(f) < ep2) {\n\t\t\tdone = true;\n\t\t\tconv = true;\n\t\t\tzero = x;\n\t\t\tbreak;\n\t\t}\n\t\t// evaluate increment in x\n\t\tdx = -f / df;\n\t\tx += dx;\n\t\t++it;\n\t\t// check relative convergence\n\t\tif (abs(dx) < ep1 * abs(x)) {\n\t\t\tdone = true;\n\t\t\tconv = true;\n\t\t\tzero = x;\n\t\t}\n\t\t// check iteration count\n\t\telse if (it > maxit) {\n\t\t\tdone = true;\n\t\t\tzero = x;\n\t\t}\n\t}\n\treturn conv;\n}\n\n//-----------------------------------------------------------------------------\n// linear\nbool poly1(std::vector<double> a, double& x)\n{\n\tif (a[1]) {\n\t\tx = -a[0] / a[1];\n\t\treturn true;\n\t}\n\telse {\n\t\treturn false;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// quadratic\nbool poly2(std::vector<double> a, double& x)\n{\n\tif (a[2]) {\n\t\tx = (-a[1] + sqrt(SQR(a[1]) - 4 * a[0] * a[2])) / (2 * a[2]);\n\t\treturn true;\n\t}\n\telse {\n\t\treturn poly1(a, x);\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// higher order\nbool polyn(int n, std::vector<double> a, double& x)\n{\n\t//    bool fnreal = true;\n\t//    vector< complex<double> > zeros(n,complex<double>(1,0));\n\tint maxit = 100;\n\tdouble ep1 = 1e-6;\n\tdouble ep2 = 1e-12;\n\treturn newton(x, n, maxit, ep1, ep2, a);\n}\n\n//-----------------------------------------------------------------------------\n// higher order\nbool polym(int n, std::vector<double> a, double& x)\n{\n\tbool fnreal = true;\n\tstd::vector< std::complex<double> > zeros(n, std::complex<double>(1, 0));\n\tint maxit = 100;\n\tdouble ep1 = 1e-6;\n\tdouble ep2 = 1e-12;\n\n\tmuller(fnreal, &zeros[0], n, 0, maxit, ep1, ep2, a);\n\tfor (int i = 0; i < n; ++i) {\n\t\tif (fabs(zeros[i].imag()) < ep2) {\n\t\t\tx = zeros[i].real();\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nbool solvepoly(int n, std::vector<double> a, double& x, bool nwt)\n{\n\tswitch (n) {\n\tcase 1:\n\t\treturn poly1(a, x);\n\t\tbreak;\n\tcase 2:\n\t\treturn poly2(a, x);\n\tdefault:\n\t\tif (a[n]) {\n\t\t\treturn nwt ? polyn(n, a, x) : polym(n, a, x);\n\t\t}\n\t\telse {\n\t\t\treturn solvepoly(n - 1, a, x);\n\t\t}\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "FECore/tools.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"fecore_api.h\"\n#include <vector>\n#include <complex>\n\nFECORE_API void linmin(double* p, double* xi, int n, double* fret, double(*fnc)(double[]));\nFECORE_API void powell(double* p, double* xi, int n, double ftol, int* iter, double* fret, double(*fnc)(double[]));\nFECORE_API double brent(double ax, double bx, double cx, double(*f)(double), double tol, double* xmin);\nFECORE_API void mnbrak(double* ax, double* bx, double* cx, double* fa, double* fb, double* fc, double(*fnc)(double));\nFECORE_API double golden(double ax, double bx, double cx, double(*f)(double), double tol, double* xmin);\nFECORE_API double zbrent(double f(double, void*), double x1, double x2, double tol, void* data);\nFECORE_API bool zbrac(double f(double, void*), double& x1, double& x2, void* data);\nFECORE_API void solve_3x3(double A[3][3], double b[3], double x[3]);\n\nFECORE_API bool LinearRegression(const std::vector<std::pair<double, double> >& data, std::pair<double, double>& res);\nFECORE_API bool NonlinearRegression(const std::vector<std::pair<double, double> >& data, std::vector<double>& res, int func);\n\nFECORE_API bool solvepoly(int n, std::vector<double> a, double& x, bool nwt = true);\n"
  },
  {
    "path": "FECore/units.h",
    "content": "/*This file is part of the FEBio Studio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio-Studio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n// units are defined as strings that use the following\n// characters to represent the physical quantities that\n// define the SI base units.\n// (Note that the symbols for time and temperature differ\n// from the conventional SI dimension symbols)\n//\n// L = length\n// M = mass\n// t = time\n// T = temperature\n// l = electric current\n// n = substance\n//\n// In addition, the following symbols for derived units are also defined:\n//\n// F = force\n// P = pressure\n// E = energy\n// W = power\n// d = angles in degrees\n// r = angles in radians\n//\n// or use one of these predefined constants\n#define UNIT_NONE\t\"\"\n#define UNIT_LENGTH \"L\"\n#define UNIT_MASS   \"M\"\n#define UNIT_TIME   \"t\"\n#define UNIT_TEMPERATURE \"T\"\n#define UNIT_CURRENT \"I\"\n#define UNIT_SUBSTANCE\t\"n\"\n#define UNIT_FORCE \"F\"\n#define UNIT_MOMENT \"F.L\"\n#define UNIT_PRESSURE \"P\"\n#define UNIT_ENERGY\t\"E\"\n#define UNIT_POWER \"W\"\n#define UNIT_VOLTAGE \"V\"\n#define UNIT_CONCENTRATION \"c\"\n#define UNIT_RELATIVE_TEMPERATURE \"R\"\n\n#define UNIT_DEGREE \"d\"\n#define UNIT_RADIAN\t\"r\"\n\n#define UNIT_ACCELERATION \"L/t^2\"\n#define UNIT_ANGULAR_MOMENTUM \"E.t\"\n#define UNIT_ANGULAR_VELOCITY \"r/t\"\n#define UNIT_AREA   \"L^2\"\n#define UNIT_COUPLE_VISCOSITY \"F.t/r\"\n#define UNIT_CURRENT_CONDUCTIVITY \"I/V.L^2\"\n#define UNIT_CURRENT_DENSITY \"I/L^2\"\n#define UNIT_DENSITY \"M/L^3\"\n#define UNIT_DENSITY_RATE \"M/L^3.t\"\n#define UNIT_DIFFUSIVITY  \"L^2/t\"\n#define UNIT_ENERGY_AREAL_DENSITY \"E/L^2\"\n#define UNIT_ENERGY_DENSITY \"E/L^3\"\n#define UNIT_ENERGY_FLUX \"W/L^2\"\n#define UNIT_FARADAY_CONSTANT \"I.t/n\"\n#define UNIT_FILTRATION \"L^2/F.t\"\n#define UNIT_FLOW_CAPACITANCE \"L^5/F\"\n#define UNIT_FLOW_RATE \"L^3/t\"\n#define UNIT_FLOW_RESISTANCE \"F.t/L^3\"\n#define UNIT_GAS_CONSTANT \"E/n.T\"\n#define UNIT_GRADIENT \"1/L\"\n#define UNIT_LINEAR_MOMENTUM \"F.t\"\n#define UNIT_MASS_FLOW_RATE \"M/t\"\n#define UNIT_MOLAR_AREAL_CONCENTRATION \"n/L^2\"\n#define UNIT_MOLAR_FLUX \"n/L^2.t\"\n#define UNIT_MOLAR_MASS \"M/n\"\n#define UNIT_MOLAR_VOLUME \"L^3/n\"\n#define UNIT_PERMEABILITY    \"L^4/F.t\"\n#define UNIT_POWER_DENSITY \"W/L^3\"\n#define UNIT_RECIPROCAL_LENGTH \"1/L\"\n#define UNIT_RECIPROCAL_TIME \"1/t\"\n#define UNIT_ROTATIONAL_VISCOSITY \"P.t/r\"\n#define UNIT_SPECIFIC_ENERGY \"E/M\"\n#define UNIT_SPECIFIC_ENTROPY \"E/M.T\"\n#define UNIT_SPECIFIC_FORCE \"F/M\"\n#define UNIT_SPECIFIC_MOMENT \"F.L/M\"\n#define UNIT_STIFFNESS \"F/L\"\n#define UNIT_THERMAL_CONDUCTIVITY \"W/L.T\"\n#define UNIT_VELOCITY \"L/t\"\n#define UNIT_VISCOSITY \"P.t\"\n#define UNIT_VOLUME \"L^3\"\n"
  },
  {
    "path": "FECore/vec2d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <math.h>\n#include \"fecore_api.h\"\n\nclass vec2d\n{\npublic:\n\t// constructor\n\tvec2d() { r[0] = r[1] = 0; }\n\texplicit vec2d(double v) { r[0] = r[1] = v; }\n\tvec2d(double x, double y) { r[0] = x; r[1] = y; }\n\n\tbool operator == (const vec2d& r) const { return (r[0] == r.r[0]) && (r[1] == r.r[1]); }\n\n\t// access operators\n\tdouble operator [] (int i) const { return r[i]; }\n\tdouble& operator [] (int i) { return r[i]; }\n\n\tdouble& x() { return r[0]; }\n\tdouble& y() { return r[1]; }\n\n\tdouble x() const { return r[0]; }\n\tdouble y() const { return r[1]; }\n\n\tdouble norm() const { return sqrt(r[0] * r[0] + r[1] * r[1]); }\n\tdouble norm2() const { return r[0] * r[0] + r[1] * r[1]; }\n\n\tdouble unit() { double R = norm(); if (R != 0) { r[0] /= R; r[1] /= R; }; return R; }\n\npublic: // arithmetic operators\n\n\tvec2d operator + (const vec2d& v) const { return vec2d(r[0]+v.r[0], r[1]+v.r[1]); }\n\tvec2d operator - (const vec2d& v) const { return vec2d(r[0]-v.r[0], r[1]-v.r[1]); }\n\tvec2d operator * (double g) const { return vec2d(r[0]*g, r[1]*g); }\n\tvec2d operator / (double g) const { return vec2d(r[0]/g, r[1]/g); }\n\n\tvec2d& operator += (const vec2d& v) { r[0] += v.r[0]; r[1] += v.r[1]; return *this; }\n\tvec2d& operator -= (const vec2d& v) { r[0] -= v.r[0]; r[1] -= v.r[1]; return *this; }\n\tvec2d& operator *= (double g) { r[0] *= g; r[1] *= g; return *this; }\n\tvec2d& operator /= (double g) { r[0] /= g; r[1] /= g; return *this; }\n\n    vec2d operator - () const { return vec2d(-r[0], -r[1]); }\n    \n\t// dot product\n\tdouble operator * (const vec2d& v) const { return r[0]*v[0] + r[1]*v[1]; }\n\npublic:\n\tdouble r[2];\n};\n\n//-----------------------------------------------------------------------------\nclass vec2i\n{\npublic:\n\tvec2i() { x = y = 0; }\n\tvec2i(int X, int Y) { x = X; y = Y; }\n\n    bool operator == (const vec2i& r) const { return (x == r.x) && (y == r.y); }\n\npublic:\n\tint\t\tx, y;\n};\n\n//-----------------------------------------------------------------------------\nclass vec2f\n{\npublic:\n\tvec2f() { x = y = 0.f; }\n\tvec2f(float rx, float ry) { x = rx; y = ry; }\n\npublic:\n\tfloat\tx, y;\n};\n"
  },
  {
    "path": "FECore/vec3d.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <math.h>\n#include \"vec2d.h\"\n\n//! 3D vector class with double precision components\nclass vec3d\n{\npublic:\n\t//! Default constructor - initializes vector to (0, 0, 0)\n\tvec3d() : x(0), y(0), z(0) {}\n\t//! Constructor with single value - initializes all components to the same value\n\texplicit vec3d(double a) : x(a), y(a), z(a) {}\n\t//! Constructor with three components\n\tvec3d(double X, double Y, double Z) : x(X), y(Y), z(Z) {}\n\t//! Constructor from 2D vector - sets z component to 0\n\tvec3d(const vec2d& v) { x = v.r[0]; y = v.r[1]; z = 0.0; }\n\n\t//! Vector addition operator\n\tvec3d operator + (const vec3d& r) const { return vec3d(x+r.x, y+r.y, z+r.z); }\n\t//! Vector subtraction operator\n\tvec3d operator - (const vec3d& r) const { return vec3d(x-r.x, y-r.y, z-r.z); }\n\n\t//! Scalar multiplication operator\n\tvec3d operator * (double a) const { return vec3d(x*a, y*a, z*a); }\n\t//! Scalar division operator\n\tvec3d operator / (double a) const { return vec3d(x/a, y/a, z/a); }\n\n\t//! Vector addition assignment operator\n\tvec3d& operator += (const vec3d& r) { x += r.x; y += r.y; z += r.z; return (*this); }\n\t//! Vector subtraction assignment operator\n\tvec3d& operator -= (const vec3d& r) { x -= r.x; y -= r.y; z -= r.z; return (*this); }\n\n\t//! Scalar multiplication assignment operator\n\tvec3d& operator *= (double a) { x*=a; y*=a; z*=a; return (*this); }\n\t//! Scalar division assignment operator\n\tvec3d& operator /= (double a) { x/=a; y/=a; z/=a; return (*this); }\n\n\t//! Unary negation operator\n\tvec3d operator - () const { return vec3d(-x, -y, -z); }\n\n    //! Component access operator (non-const)\n    double& operator() (int i)\n    {\n        switch(i)\n        {\n            case 0: {return x; break;}\n            case 1: {return y; break;}\n            case 2: {return z; break;}\n            default: {return x; break;}\n        }\n    }\n    \n    //! Component access operator (const)\n    double operator() (int i) const\n    {\n        switch(i)\n        {\n            case 0: {return x; break;}\n            case 1: {return y; break;}\n            case 2: {return z; break;}\n            default: {return x; break;}\n        }\n    }\n    \n\t//! Dot product operator\n\tdouble operator * (const vec3d& r) const { return (x*r.x + y*r.y + z*r.z); }\n\n\t//! Cross product operator\n\tvec3d operator ^ (const vec3d& r) const { return vec3d(y*r.z-z*r.y,z*r.x-x*r.z,x*r.y-y*r.x); }\n\n\t//! Normalize the vector in place and return original length\n\tdouble unit()\n\t{\n\t\tdouble d = sqrt(x*x+y*y+z*z);\n\t\tif (d != 0) { x/=d; y/=d; z/=d; }\n\t\treturn d;\n\t}\n\n\t//! Return a normalized copy of this vector\n\tvec3d normalized() const { \n\t\tdouble d = sqrt(x*x + y*y + z*z); \n\t\td = (d == 0.0? d = 1.0 : d = 1.0/d); \n\t\treturn vec3d(x*d, y*d, z*d);\n\t}\n\n\t//! Return the length (magnitude) of the vector\n\tdouble norm() const { return sqrt(x*x+y*y+z*z); }\n\n\t//! Return the squared length of the vector\n\tdouble norm2() const { return (x*x + y*y + z*z); }\n\npublic:\n\t//! Normalize the vector in place (FEBio Studio compatibility)\n\tvec3d Normalize() { unit(); return *this; }\n\t//! Return a normalized copy of this vector (FEBio Studio compatibility)\n\tvec3d Normalized() const { vec3d v(x, y, z); v.unit(); return v; }\n\t//! Return the length of the vector (FEBio Studio compatibility)\n\tdouble Length() const { return norm(); }\n\t//! Return the squared length of the vector (FEBio Studio compatibility)\n\tdouble SqrLength() const { return norm2(); }\n\t//! Equality comparison operator\n\tbool operator == (const vec3d& a) const { return ((a.x == x) && (a.y == y) && (a.z == z)); }\n\n public:\n\t//! X component of the vector\n\tdouble x, y, z;\n};\n\n\n//-----------------------------------------------------------------------------\n//! 3D vector class with single precision (float) components\nclass vec3f\n{\npublic:\n\t//! Default constructor - initializes vector to (0, 0, 0)\n\tvec3f() { x = y = z = 0; }\n\t//! Constructor with three float components\n\tvec3f(float rx, float ry, float rz) { x = rx; y = ry; z = rz; }\n\n\t//! Vector addition operator\n\tvec3f operator + (const vec3f& v) const { return vec3f(x + v.x, y + v.y, z + v.z); }\n\t//! Vector subtraction operator\n\tvec3f operator - (const vec3f& v) const { return vec3f(x - v.x, y - v.y, z - v.z); }\n\t//! Cross product operator\n\tvec3f operator ^ (const vec3f& v) const\n\t{\n\t\treturn vec3f(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);\n\t}\n\n\t//! Dot product operator\n\tfloat operator * (const vec3f& v) const { return (x * v.x + y * v.y + z * v.z); }\n\n\t//! Scalar multiplication operator\n\tvec3f operator * (const float g) const { return vec3f(x * g, y * g, z * g); }\n\t//! Scalar division operator\n\tvec3f operator / (const float g) const { return vec3f(x / g, y / g, z / g); }\n\n\t//! Vector addition assignment operator\n\tconst vec3f& operator += (const vec3f& v) { x += v.x; y += v.y; z += v.z; return (*this); }\n\t//! Vector subtraction assignment operator\n\tconst vec3f& operator -= (const vec3f& v) { x -= v.x; y -= v.y; z -= v.z; return (*this); }\n\t//! Float division assignment operator\n\tconst vec3f& operator /= (const float& f) { x /= f; y /= f; z /= f; return (*this); }\n\t//! Integer division assignment operator\n\tconst vec3f& operator /= (const int& n) { x /= n; y /= n; z /= n; return (*this); }\n\t//! Float multiplication assignment operator\n\tconst vec3f& operator *= (const float& f) { x *= f; y *= f; z *= f; return (*this); }\n\n\t//! Unary negation operator\n\tvec3f operator - () { return vec3f(-x, -y, -z); }\n\n\t//! Return the length (magnitude) of the vector\n\tfloat Length() const { return (float)sqrt(x * x + y * y + z * z); }\n\n\t//! Return the squared length of the vector\n\tfloat SqrLength() const { return (float)(x * x + y * y + z * z); }\n\n\t//! Normalize the vector in place\n\tvec3f& Normalize()\n\t{\n\t\tfloat L = Length();\n\t\tif (L != 0) { x /= L; y /= L; z /= L; }\n\n\t\treturn (*this);\n\t}\n\npublic:\n\t//! X component of the vector\n\tfloat x, y, z;\n};\n\n//! Convert vec3f to vec3d\ninline vec3d to_vec3d(const vec3f& r) { return vec3d((double)r.x, (double)r.y, (double)r.z); }\n//! Convert vec3d to vec3f\ninline vec3f to_vec3f(const vec3d& r) { return vec3f((float)r.x, (float)r.y, (float)r.z); }\n\n\n//-----------------------------------------------------------------------------\n//! 3D vector class with integer components\nclass vec3i\n{\npublic:\n\t//! Default constructor - initializes vector to (0, 0, 0)\n\tvec3i() { x = y = z = 0; }\n\t//! Constructor with three integer components\n\tvec3i(int X, int Y, int Z) { x = X; y = Y; z = Z;}\n\npublic:\n\t//! X component of the vector\n\tint\t\tx, y, z;\n};"
  },
  {
    "path": "FECore/vector.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <assert.h>\n#include \"vector.h\"\n#include \"FEMesh.h\"\n#include \"FEDofList.h\"\n#include <algorithm>\nusing namespace std;\n\ndouble operator*(const vector<double>& a, const vector<double>& b)\n{\n\t// This algorithm sums positive and negative products separately, \n\t// which is more accurate then just doing the running sum.\n\tassert(a.size() == b.size());\n\tdouble sum_p = 0, sum_n = 0;\n\tsize_t n = a.size();\n\tfor (size_t i = 0; i < n; i++)\n\t{\n\t\tdouble ab = a[i] * b[i];\n\t\tif (ab >= 0.0) sum_p += ab; else sum_n += ab;\n\t}\n\treturn sum_p + sum_n;\n}\n\nvector<double> operator - (vector<double>& a, vector<double>& b)\n{\n\tvector<double> c(a);\n\tsize_t n = c.size();\n\tfor (size_t i=0; i<n; ++i) c[i] -= b[i];\n\treturn c;\n}\n\nvoid operator += (vector<double>& a, const vector<double>& b)\n{\n\tassert(a.size() == b.size());\n\tfor (size_t i = 0; i < a.size(); ++i) a[i] += b[i];\n}\n\nvoid operator -= (vector<double>& a, const vector<double>& b)\n{\n\tassert(a.size() == b.size());\n\tfor (size_t i = 0; i < a.size(); ++i) a[i] -= b[i];\n}\n\nvoid operator *= (vector<double>& a, double b)\n{\n\tfor (size_t i=0; i<a.size(); ++i) a[i] *= b;\n}\n\nvoid vcopys(vector<double>& a, const vector<double>& b, double s)\n{\n\tassert(a.size() == b.size());\n\tfor (size_t i=0; i<a.size(); ++i) a[i] = b[i]*s;\n}\n\nvoid vadds(vector<double>& a, const vector<double>& b, double s)\n{\n\tassert(a.size() == b.size());\n\tfor (size_t i = 0; i<a.size(); ++i) a[i] += b[i] * s;\n}\n\nvoid vsubs(vector<double>& a, const vector<double>& b, double s)\n{\n\tassert(a.size() == b.size());\n\tfor (size_t i = 0; i<a.size(); ++i) a[i] -= b[i] * s;\n}\n\nvoid vscale(vector<double>& a, const vector<double>& s)\n{\n\tassert(a.size() == s.size());\n\tfor (size_t i = 0; i<a.size(); ++i) a[i] *= s[i];\n}\n\nvoid vsub(vector<double>& a, const vector<double>& l, const vector<double>& r)\n{\n\tassert((a.size()==l.size())&&(a.size()==r.size()));\n\tfor (size_t i=0; i<a.size(); ++i) a[i] = l[i] - r[i];\n}\n\nvector<double> operator + (const vector<double>& a, const vector<double>& b)\n{\n\tassert(a.size() == b.size());\n\tvector<double> s(a);\n\tfor (size_t i = 0; i < s.size(); ++i) s[i] += b[i];\n\treturn s;\n}\n\nvector<double> operator*(const vector<double>& a, double g)\n{\n\tvector<double> s(a.size());\n\tfor (size_t i = 0; i < s.size(); ++i) s[i] = a[i]*g;\n\treturn s;\n}\n\nvector<double> operator - (const vector<double>& a)\n{\n\tvector<double> s(a.size());\n\tfor (size_t i = 0; i < s.size(); ++i) s[i] = -a[i];\n\treturn s;\n}\n\nvoid gather(vector<double>& v, FEMesh& mesh, int ndof)\n{\n\tconst int NN = mesh.Nodes();\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tint n = node.m_ID[ndof]; if (n >= 0) v[n] = node.get(ndof);\n\t}\n}\n\nvoid gather(vector<double>& v, FEMesh& mesh, const vector<int>& dof)\n{\n\tconst int NN = mesh.Nodes();\n\tconst int NDOF = (const int) dof.size();\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tfor (int j=0; j<NDOF; ++j)\n\t\t{\n\t\t\tint n = node.m_ID[dof[j]]; \n\t\t\tif (n >= 0) v[n] = node.get(dof[j]);\n\t\t}\n\t}\n}\n\nvoid scatter(vector<double>& v, FEMesh& mesh, int ndof)\n{\n\tconst int NN = mesh.Nodes();\n\tfor (int i=0; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tint n = node.m_ID[ndof];\n\t\tif (n >= 0) node.set(ndof, v[n]);\n\t}\n}\n\nvoid scatter3(vector<double>& v, FEMesh& mesh, int ndof1, int ndof2, int ndof3)\n{\n\tconst int NN = mesh.Nodes();\n#pragma omp parallel for \n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tint n;\n\t\tn = node.m_ID[ndof1]; if (n >= 0) node.set(ndof1, v[n]);\n\t\tn = node.m_ID[ndof2]; if (n >= 0) node.set(ndof2, v[n]);\n\t\tn = node.m_ID[ndof3]; if (n >= 0) node.set(ndof3, v[n]);\n\t}\n}\n\nvoid scatter(vector<double>& v, FEMesh& mesh, const FEDofList& dofs)\n{\n\tconst int NN = mesh.Nodes();\n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\tFENode& node = mesh.Node(i);\n\t\tfor (int j = 0; j < dofs.Size(); ++j)\n\t\t{\n\t\t\tint n = node.m_ID[dofs[j]]; if (n >= 0) node.set(dofs[j], v[n]);\n\t\t}\n\t}\n}\n\ndouble l2_norm(const vector<double>& v)\n{\n\tdouble s = 0.0;\n\tfor (auto vi : v) s += vi*vi;\n\treturn sqrt(s);\n}\n\ndouble l2_sqrnorm(const vector<double>& v)\n{\n\tdouble s = 0.0;\n\tfor (auto vi : v) s += vi*vi;\n\treturn s;\n}\n\ndouble l2_norm(double* x, int n)\n{\n\tdouble s = 0.0;\n\tfor (int i = 0; i < n; ++i) s += x[i]*x[i];\n\treturn sqrt(s);\n}\n"
  },
  {
    "path": "FECore/vector.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <math.h>\n#include <memory.h>\n#include <vector>\n#include <algorithm>\n#include \"vec3d.h\"\n#include \"fecore_api.h\"\n\nclass FEMesh;\nclass FEDofList;\n\ndouble FECORE_API operator*(const std::vector<double>& a, const std::vector<double>& b);\nstd::vector<double> FECORE_API operator - (std::vector<double>& a, std::vector<double>& b);\ntemplate<typename T> void zero(std::vector<T>& a) { std::fill(a.begin(), a.end(), T(0)); }\ntemplate<> inline void zero<vec3d>(std::vector<vec3d>& a) { std::fill(a.begin(), a.end(), vec3d(0,0,0)); }\ntemplate<typename T> void assign(std::vector<T>& a, const T& v) { std::fill(a.begin(), a.end(), v); }\nvoid FECORE_API operator+=(std::vector<double>& a, const std::vector<double>& b);\nvoid FECORE_API operator-=(std::vector<double>& a, const std::vector<double>& b);\nvoid FECORE_API operator*=(std::vector<double>& a, double b);\nstd::vector<double> FECORE_API operator+(const std::vector<double>& a, const std::vector<double>& b);\nstd::vector<double> FECORE_API operator*(const std::vector<double>& a, double g);\nstd::vector<double> FECORE_API operator - (const std::vector<double>& a);\n\n// copy vector and scale\nvoid FECORE_API vcopys(std::vector<double>& a, const std::vector<double>& b, double s);\n\n// add scaled vector\nvoid FECORE_API vadds(std::vector<double>& a, const std::vector<double>& b, double s);\nvoid FECORE_API vsubs(std::vector<double>& a, const std::vector<double>& b, double s);\n\n// vector subtraction: a = l - r\nvoid FECORE_API vsub(std::vector<double>& a, const std::vector<double>& l, const std::vector<double>& r);\n\n// scale each component of a vector\nvoid FECORE_API vscale(std::vector<double>& a, const std::vector<double>& s);\n\n// gather operation (copy mesh data to vector)\nvoid FECORE_API gather(std::vector<double>& v, FEMesh& mesh, int ndof);\nvoid FECORE_API gather(std::vector<double>& v, FEMesh& mesh, const std::vector<int>& dof);\n\n// scatter operation (copy vector data to mesh)\nvoid FECORE_API scatter(std::vector<double>& v, FEMesh& mesh, int ndof);\nvoid FECORE_API scatter3(std::vector<double>& v, FEMesh& mesh, int ndof1, int ndof2, int ndof3);\nvoid FECORE_API scatter(std::vector<double>& v, FEMesh& mesh, const FEDofList& dofs);\n\n// calculate l2 norm of vector\ndouble FECORE_API l2_norm(const std::vector<double>& v);\ndouble FECORE_API l2_sqrnorm(const std::vector<double>& v);\ndouble l2_norm(double* x, int n);\n"
  },
  {
    "path": "FECore/version.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n//-----------------------------------------------------------------------------\n// This file defines the SDK versioning. It follows the versioning of FEBio, but\n// will only be modified if the SDK is no longer compatible with older versions\n// of FEBio. In that case, plugins need to be recompiled to be usable with the \n// newer version of FEBio.\n#define FE_SDK_MAJOR_VERSION\t4\n#define FE_SDK_SUB_VERSION\t\t12\n#define FE_SDK_SUBSUB_VERSION\t0\n\n//-----------------------------------------------------------------------------\n// This macro needs to be exported by all plugins in the GetSDKVersion() function.\n#define FE_SDK_VERSION ((FE_SDK_MAJOR_VERSION << 16) | (FE_SDK_SUB_VERSION << 8) | (FE_SDK_SUBSUB_VERSION))\n"
  },
  {
    "path": "FECore/writeplot.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"writeplot.h\"\n#include \"FESPRProjection.h\"\n\nvoid writeMaxElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<double(const FEMaterialPoint& mp)> fnc)\n{\n\tint NE = dom.Elements();\n\tstd::vector<double> v(NE);\n#pragma omp parallel for shared(v)\n\tfor (int i = 0; i < NE; ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tdouble s = 0.0;\n\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t{\n\t\t\tdouble sj = fnc(*el.GetMaterialPoint(j));\n\t\t\tif ((sj > s) || (j == 0)) s = sj;\n\t\t}\n\t\tv[i] = s;\n\t}\n\n\tfor (int i = 0; i < NE; ++i)\n\t\tar << v[i];\n}\n\nvoid writeNodalProjectedElementValues(FEMeshPartition& dom, FEDataStream& ar, FEParamDouble& var)\n{\n\t// temp storage \n\tdouble si[FEElement::MAX_INTPOINTS];\n\tdouble sn[FEElement::MAX_NODES];\n\n\t// loop over all elements\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint ne = e.Nodes();\n\t\tint ni = e.GaussPoints();\n\n\t\t// get the integration point values\n\t\tfor (int k = 0; k < ni; ++k)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(k);\n\t\t\tdouble s = var(mp);\n\t\t\tsi[k] = s;\n\t\t}\n\n\t\t// project to nodes\n\t\te.project_to_nodes(si, sn);\n\n\t\t// push data to archive\n\t\tfor (int j = 0; j < ne; ++j) ar << sn[j];\n\t}\n}\n\nvoid writeNodalProjectedElementValues(FESurface& dom, FEDataStream& ar, FEParamDouble& var)\n{\n\tdouble gi[FEElement::MAX_INTPOINTS];\n\tdouble gn[FEElement::MAX_NODES];\n\n\t// loop over all the elements in the domain\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\t// get the element and loop over its integration points\n\t\t// we only calculate the element's average\n\t\t// but since most material parameters can only defined \n\t\t// at the element level, this should get the same answer\n\t\tFESurfaceElement& e = dom.Element(i);\n\t\tint nint = e.GaussPoints();\n\t\tint neln = e.Nodes();\n\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\t// get the material point data for this integration point\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(j);\n\t\t\tgi[j] = var(mp);\n\t\t}\n\n\t\te.FEElement::project_to_nodes(gi, gn);\n\n\t\t// store the result\n\t\tfor (int j = 0; j < neln; ++j) ar << gn[j];\n\t}\n}\n\n\nvoid writeSPRElementValue(FESolidDomain& dom, FEDataStream& ar, std::function<double(const FEMaterialPoint&)> fnc, int interpolOrder)\n{\n\tint NN = dom.Nodes();\n\tint NE = dom.Elements();\n\n\t// build the element data array\n\tvector< vector<double> > ED;\n\tED.resize(NE);\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& e = dom.Element(i);\n\t\tint nint = e.GaussPoints();\n\t\tED[i].assign(nint, 0.0);\n\t}\n\n\t// this array will store the results\n\tFESPRProjection map(dom, interpolOrder);\n\n\tvector<double> val;\n\n\t// fill the ED array\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tdouble v = fnc(mp);\n\t\t\tED[i][j] = v;\n\t\t}\n\t}\n\n\t// project to nodes\n\tmap.Project(ED, val);\n\n\t// copy results to archive\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tar.push_back((float)val[i]);\n\t}\n}\n\n//-------------------------------------------------------------------------------------------------\nvoid writeSPRElementValueVectorDouble(FESolidDomain& dom, FEDataStream& ar, std::function<std::vector<double>(const FEMaterialPoint&)> fnc, int interpolOrder, int n_fields)\n{\n\n\t// get all nodes and elements\n\tint NN = dom.Nodes();\n\tint NE = dom.Elements();\n\n\t// build the element data array\n\tvector<vector< vector<double> > > ED(n_fields);\n\t// for each component\n\tfor (int n = 0; n < n_fields; ++n)\n\t{\n\t\t// fill element data. for each element add the number of gauss points in the element.\n\t\tED[n].resize(NE);\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFESolidElement& e = dom.Element(i);\n\t\t\tint nint = e.GaussPoints();\n\n\t\t\tED[n][i].assign(nint, 0.0);\n\t\t}\n\t}\n\n\t// this array will store the results\n\tFESPRProjection map(dom, interpolOrder);\n\tvector<vector<double> >val(n_fields);\n\n\t// fill the ED array\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tvector<double> v = fnc(mp);\n\n\t\t\t// loop over all solutes components\n\t\t\tfor (int n = 0; n < n_fields; ++n)\n\t\t\t{\n\t\t\t\tED[n][i][j] = v[n];\n\t\t\t}\n\t\t}\n\t}\n\n\t// project to nodes\n\t// loop over stress components\n\tfor (int n = 0; n < n_fields; ++n)\n\t{\n\t\tmap.Project(ED[n], val[n]);\n\t}\n\n\t// copy results to archive\n\tfor (int i_node = 0; i_node < NN; ++i_node)\n\t{\n\t\tfor (int i_field = 0; i_field < n_fields; ++i_field)\n\t\t\tar.push_back((float)val[i_field][i_node]);\n\t}\n}\n\nvoid writeSPRElementValueMat3dd(FESolidDomain& dom, FEDataStream& ar, std::function<mat3dd(const FEMaterialPoint&)> fnc, int interpolOrder)\n{\n\tint NN = dom.Nodes();\n\tint NE = dom.Elements();\n\n\t// build the element data array\n\tvector< vector<double> > ED[3];\n\tED[0].resize(NE);\n\tED[1].resize(NE);\n\tED[2].resize(NE);\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& e = dom.Element(i);\n\t\tint nint = e.GaussPoints();\n\t\tED[0][i].assign(nint, 0.0);\n\t\tED[1][i].assign(nint, 0.0);\n\t\tED[2][i].assign(nint, 0.0);\n\t}\n\n\t// this array will store the results\n\tFESPRProjection map(dom, interpolOrder);\n\tvector<double> val[3];\n\n\t// fill the ED array\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tmat3dd v = fnc(mp);\n\n\t\t\tED[0][i][j] = v.diag(0);\n\t\t\tED[1][i][j] = v.diag(1);\n\t\t\tED[2][i][j] = v.diag(2);\n\t\t}\n\t}\n\n\t// project to nodes\n\tmap.Project(ED[0], val[0]);\n\tmap.Project(ED[1], val[1]);\n\tmap.Project(ED[2], val[2]);\n\n\t// copy results to archive\n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\tar.push_back((float)val[0][i]);\n\t\tar.push_back((float)val[1][i]);\n\t\tar.push_back((float)val[2][i]);\n\t}\n}\n\n//-------------------------------------------------------------------------------------------------\nvoid writeSPRElementValueMat3ds(FESolidDomain& dom, FEDataStream& ar, std::function<mat3ds(const FEMaterialPoint&)> fnc, int interpolOrder)\n{\n\tconst int LUT[6][2] = { { 0,0 },{ 1,1 },{ 2,2 },{ 0,1 },{ 1,2 },{ 0,2 } };\n\n\tint NN = dom.Nodes();\n\tint NE = dom.Elements();\n\n\t// build the element data array\n\tvector< vector<double> > ED[6];\n\tfor (int n = 0; n < 6; ++n)\n\t{\n\t\tED[n].resize(NE);\n\t\tfor (int i = 0; i < NE; ++i)\n\t\t{\n\t\t\tFESolidElement& e = dom.Element(i);\n\t\t\tint nint = e.GaussPoints();\n\t\t\tED[n][i].assign(nint, 0.0);\n\t\t}\n\t}\n\n\t// this array will store the results\n\tFESPRProjection map(dom, interpolOrder);\n\n\tvector<double> val[6];\n\n\t// fill the ED array\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFESolidElement& el = dom.Element(i);\n\t\tint nint = el.GaussPoints();\n\t\tfor (int j = 0; j<nint; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tmat3ds s = fnc(mp);\n\n\t\t\t// loop over stress components\n\t\t\tfor (int n = 0; n < 6; ++n)\n\t\t\t{\n\t\t\t\tED[n][i][j] = s(LUT[n][0], LUT[n][1]);\n\t\t\t}\n\t\t}\n\t}\n\n\t// project to nodes\n\t// loop over stress components\n\tfor (int n = 0; n<6; ++n)\n\t{\n\t\tmap.Project(ED[n], val[n]);\n\t}\n\n\t// copy results to archive\n\tfor (int i = 0; i<NN; ++i)\n\t{\n\t\tar.push_back((float)val[0][i]);\n\t\tar.push_back((float)val[1][i]);\n\t\tar.push_back((float)val[2][i]);\n\t\tar.push_back((float)val[3][i]);\n\t\tar.push_back((float)val[4][i]);\n\t\tar.push_back((float)val[5][i]);\n\t}\n}\n\nvoid ProjectToNodes(FEDomain& dom, vector<double>& nodeVals, function<double(FEMaterialPoint& mp)> f)\n{\n\t// temp storage \n\tdouble si[FEElement::MAX_INTPOINTS];\n\tdouble sn[FEElement::MAX_NODES];\n\n\t// allocate nodeVals and create valence array (tag)\n\tint NN = dom.Nodes();\n\tvector<int> tag(NN, 0);\n\tnodeVals.assign(NN, 0.0);\n\n\t// loop over all elements\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint ne = e.Nodes();\n\t\tint ni = e.GaussPoints();\n\n\t\t// get the integration point values\n\t\tfor (int k = 0; k < ni; ++k)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(k);\n\t\t\tdouble v = f(mp);\n\t\t\tsi[k] = v;\n\t\t}\n\n\t\t// project to nodes\n\t\te.project_to_nodes(si, sn);\n\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tnodeVals[e.m_lnode[j]] += sn[j];\n\t\t\ttag[e.m_lnode[j]]++;\n\t\t}\n\t}\n\n\tfor (int i = 0; i < NN; ++i)\n\t{\n\t\tif (tag[i] > 0) nodeVals[i] /= (double)tag[i];\n\t}\n}\n\nvoid writeRelativeError(FEDomain& dom, FEDataStream& a, function<double(FEMaterialPoint& mp)> f)\n{\n\tint NE = dom.Elements();\n\tint NN = dom.Nodes();\n\n\t// calculate the recovered nodal values\n\tvector<double> sn(NN);\n\tProjectToNodes(dom, sn, f);\n\n\t// find the min and max values\n\tdouble smin = 1e99, smax = -1e99;\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tint ni = el.GaussPoints();\n\t\tfor (int j = 0; j < ni; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tdouble sj = f(mp);\n\n\t\t\tif (sj < smin) smin = sj;\n\t\t\tif (sj > smax) smax = sj;\n\t\t}\n\t}\n\tif (fabs(smin - smax) < 1e-12) smax++;\n\n\t// calculate errors\n\tdouble ev[FEElement::MAX_NODES];\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tint ne = el.Nodes();\n\t\tint ni = el.GaussPoints();\n\n\t\t// get the nodal values\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tev[j] = sn[el.m_lnode[j]];\n\t\t}\n\n\t\t// evaluate element error\n\t\tdouble max_err = 0;\n\t\tfor (int j = 0; j < ni; ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tdouble sj = f(mp);\n\n\t\t\tdouble snj = el.Evaluate(ev, j);\n\n\t\t\tdouble err = fabs(sj - snj) / (smax - smin);\n\t\t\tif (err > max_err) max_err = err;\n\t\t}\n\n\t\ta << max_err;\n\t}\n}\n"
  },
  {
    "path": "FECore/writeplot.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include \"FEMesh.h\"\n#include \"FESurface.h\"\n#include \"FEDataStream.h\"\n#include \"FESolidDomain.h\"\n#include <FEBioMech/FESSIShellDomain.h>\n#include \"FEDomainParameter.h\"\n#include \"fecore_api.h\"\n#include <functional>\n\n//=================================================================================================\ntemplate <class T> void writeNodalValues(FEMesh& mesh, FEDataStream& ar, std::function<T(const FENode& node)> f)\n{\n\tfor (int i = 0; i<mesh.Nodes(); ++i) ar << f(mesh.Node(i));\n}\n\n//=================================================================================================\ntemplate <class T> void writeNodalValues(FEMeshPartition& dom, FEDataStream& ar, std::function<T(int)> f)\n{\n\tfor (int i = 0; i<dom.Nodes(); ++i) ar << f(i);\n}\n\n//=================================================================================================\ntemplate <class T> void writeElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<T(int nface)> f)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i) ar << f(i);\n}\n\n//=================================================================================================\ntemplate <class T> void writeNodalValues(FENodeSet& nset, FEDataStream& ar, std::function<T (const FEMaterialPoint&)> var)\n{\n\tFEMesh& mesh = *nset.GetMesh();\n\tvector<T> data(mesh.Nodes(), T(0.0));\n\n\tFEMaterialPoint mp;\n\tfor (int i = 0; i < nset.Size(); ++i) {\n\t\tFENode& node = mesh.Node(nset[i]);\n\t\tmp.m_r0 = node.m_r0;\n\t\tmp.m_index = i;\n\t\tdata[nset[i]] = var(mp);\n\t}\n\tar << data;\n}\n\n//=================================================================================================\ntemplate <class T> void writeIntegratedElementValue(FESurface& surf, FEDataStream& ar, std::function<T(const FEMaterialPoint& mp)> fnc)\n{\n\tT s(0.0);\n\tfor (int i = 0; i<surf.Elements(); ++i) {\n\t\tFESurfaceElement& el = surf.Element(i);\n\t\tdouble* w = el.GaussWeights();\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j) s += fnc(*el.GetMaterialPoint(j))*w[j];\n\t}\n\tar << s;\n}\n\n//=================================================================================================\ntemplate <class T> void writeElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint& mp)> fnc)\n{\n\tint NE = dom.Elements();\n\tstd::vector<T> v(NE);\n#pragma omp parallel for shared(v)\n\tfor (int i = 0; i<NE; ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tv[i] = fnc(*el.GetMaterialPoint(0));\n\t}\n\n\tfor (int i = 0; i < NE; ++i)\n\t\tar << v[i];\n}\n\n//=================================================================================================\nvoid FECORE_API writeMaxElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<double(const FEMaterialPoint& mp)> fnc);\n\n//=================================================================================================\ntemplate <class T> void writeAverageElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint& mp)> fnc)\n{\n\tint NE = dom.Elements();\n\tstd::vector<T> v(NE);\n#pragma omp parallel for shared(v)\n\tfor (int i = 0; i<NE; ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tT s(0.0);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j) s += fnc(*el.GetMaterialPoint(j));\n\t\tv[i] = s / (double)el.GaussPoints();\n\t}\n\n\tfor (int i=0; i<NE; ++i)\n\t\tar << v[i];\n}\n\n//=================================================================================================\ntemplate <class T> void writeAverageElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<T(FEElement& el, int ip)> fnc)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tT s(0.0);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j) s += fnc(el, j);\n\t\tar << s / (double) el.GaussPoints();\n\t}\n}\n\n//=================================================================================================\ntemplate <class Tin, class Tout> void writeAverageElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<Tin(const FEMaterialPoint&)> fnc, std::function<Tout(const Tin& m)> flt)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tTin s(0.0);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j) s += fnc(*el.GetMaterialPoint(j));\n\t\tar << flt(s / (double) el.GaussPoints());\n\t}\n}\n\n//=================================================================================================\ntemplate <class Tin, class Tout> void writeAverageElementValue(FEMeshPartition& dom, FEDataStream& ar, std::function<Tin(FEElement& el, int ip)> fnc, std::function<Tout(const Tin& m)> flt)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tTin s(0.0);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j) s += fnc(el, j);\n\t\tar << flt(s / (double)el.GaussPoints());\n\t}\n}\n\n//=================================================================================================\ntemplate <class T> void writeAverageElementValue(FEMeshPartition& dom, FEDataStream& ar, FEDomainParameter* var)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i) {\n\t\tFEElement& el = dom.ElementRef(i);\n\t\tT s(0.0);\n\t\tfor (int j = 0; j < el.GaussPoints(); ++j)\n\t\t{\n\t\t\tFEParamValue v = var->value(*el.GetMaterialPoint(j));\n\t\t\ts += v.value<T>();\n\t\t}\n\t\tar << s / (double)el.GaussPoints();\n\t}\n}\n\n//=================================================================================================\ntemplate <class T> void writeIntegratedElementValue(FESolidDomain& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint& mp)> fnc)\n{\n\tfor (int i = 0; i<dom.Elements(); ++i) {\n\t\tFESolidElement& el = dom.Element(i);\n\t\tdouble* gw = el.GaussWeights();\n\n\t\tT ew(0.0);\n\t\tfor (int j = 0; j<el.GaussPoints(); ++j)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(j);\n\t\t\tew += fnc(mp)*dom.detJ0(el, j)*gw[j];\n\t\t}\n\t\tar << ew;\n\t}\n}\n\n//=================================================================================================\ntemplate <class T> void writeNodalProjectedElementValues(FEMeshPartition& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint&)> var)\n{\n\t// temp storage \n\tT si[FEElement::MAX_INTPOINTS];\n\tT sn[FEElement::MAX_NODES];\n\n\t// loop over all elements\n\tint NE = dom.Elements();\n\tfor (int i = 0; i<NE; ++i)\n\t{\n\t\tFEElement& e = dom.ElementRef(i);\n\t\tint ne = e.Nodes();\n\t\tint ni = e.GaussPoints();\n\n\t\t// get the integration point values\n\t\tfor (int k = 0; k<ni; ++k)\n\t\t{\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(k);\n\t\t\tT s = var(mp);\n\t\t\tsi[k] = s;\n\t\t}\n\n\t\t// project to nodes\n\t\te.project_to_nodes(si, sn);\n\n\t\t// push data to archive\n        for (int j = 0; j<ne; ++j) ar << sn[j];\n\t}\n}\n\nFECORE_API void writeNodalProjectedElementValues(FEMeshPartition& dom, FEDataStream& ar, FEParamDouble& var);\n\nFECORE_API void writeNodalProjectedElementValues(FESurface& dom, FEDataStream& ar, FEParamDouble& var);\n\n//=================================================================================================\ntemplate <class T> void writeShellElementValues(FEMeshPartition& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint&)> var, bool bbot)\n{\n    // accepts only shell elements\n    if (dynamic_cast<FESSIShellDomain*>(&dom)) {\n        // loop over all elements\n        int NE = dom.Elements();\n        for (int i = 0; i<NE; ++i)\n        {\n            FEElement& e = dom.ElementRef(i);\n            int ne = e.Nodes();\n            int ni = e.GaussPoints();\n            int NI = ni;\n            int NS = 1;\n            if (e.Type() == FE_SHELL_QUAD4G8) { NS = 2; }\n            else if (e.Type() == FE_SHELL_QUAD4G12) { NS = 3; }\n            else if (e.Type() == FE_SHELL_QUAD8G18) { NS = 2; }\n            else if (e.Type() == FE_SHELL_QUAD8G27) { NS = 3; }\n            else if (e.Type() == FE_SHELL_TRI3G6)  { NS = 2; }\n            else if (e.Type() == FE_SHELL_TRI6G14) { NS = 2; }\n            else if (e.Type() == FE_SHELL_TRI6G21) { NS = 3; }\n            if (NS == 1) return;    // no valid way to project to front or back\n            NI = ni/NS;\n            std::vector<double> gt = dynamic_cast<FEShellElementTraits*>(e.GetTraits())->gt;\n            \n            // get the integration point values\n            T d; d.zero();\n            \n            for (int k = 0; k<NI; ++k)\n            {\n                switch (NS) {\n                    case 2:\n                    {\n                        FEMaterialPoint& mpt = *e.GetMaterialPoint(k + NI);\n                        FEMaterialPoint& mpb = *e.GetMaterialPoint(k);\n                        T st = var(mpt);\n                        T sb = var(mpb);\n                        double tt = gt[k + NI];\n                        double tb = gt[k];\n                        T s;\n                        if (bbot)\n                            s = (st - sb)*((-1-tb)/(tt-tb)) + sb;\n                        else\n                            s = (st - sb)*((1-tb)/(tt-tb)) + sb;\n                        d += s;\n                    }\n                        break;\n                    case 3:\n                    {\n                        FEMaterialPoint& mpt = *e.GetMaterialPoint(k + 2*NI);\n                        FEMaterialPoint& mpm = *e.GetMaterialPoint(k + NI);\n                        FEMaterialPoint& mpb = *e.GetMaterialPoint(k);\n                        T st = var(mpt);\n                        T sm = var(mpm);\n                        T sb = var(mpb);\n                        double tt = gt[k + 2*NI];\n                        double tm = gt[k + NI];\n                        double tb = gt[k];\n                        T s;\n                        if (bbot)\n                            s = (-(sm*(1 + tb)*(tb - tt)*(1 + tt)) +\n                                 (1 + tm)*(st*(1 + tb)*(tb - tm) + sb*(tm - tt)*(1 + tt)))/\n                            ((tb - tm)*(tb - tt)*(tm - tt));\n                        else\n                            s = ((-1 + tm)*(st*(-1 + tb)*(tb - tm) + sb*(tm - tt)*(-1 + tt)) -\n                                 sm*(-1 + tb)*(tb - tt)*(-1 + tt))/((tb - tm)*(tb - tt)*(tm - tt));\n                        d += s;\n                    }\n                        break;\n                }\n            }\n            d /= NI;\n            \n            // push data to archive\n            ar << d;\n        }\n    }\n}\n\n//=================================================================================================\ntemplate <class T> void writeShellNodalProjectedElementValues(FEMeshPartition& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint&)> var, bool bbot)\n{\n    // temp storage\n    T si[FEElement::MAX_INTPOINTS];\n    T sn[FEElement::MAX_NODES];\n    \n    // loop over all elements\n    int NE = dom.Elements();\n    for (int i = 0; i<NE; ++i)\n    {\n        FEElement& e = dom.ElementRef(i);\n        if ((e.Type() == FE_SHELL_QUAD4G4) || (e.Type() == FE_SHELL_TRI3G3)) return;    // no valid way to project to front or back\n        int ne = e.Nodes();\n        int ni = e.GaussPoints();\n        int nvln = dynamic_cast<FEShellElementTraits*>(e.GetTraits())->m_nvln;\n        \n        // get the integration point values\n        for (int k = 0; k<ni; ++k)\n        {\n            FEMaterialPoint& mp = *e.GetMaterialPoint(k);\n            T s = var(mp);\n            si[k] = s;\n        }\n        \n        // project to nodes\n        e.project_to_nodes(si, sn);\n        \n        // push data to archive\n        for (int j = 0; j<nvln/2; ++j) (bbot) ? (ar << sn[j]) : (ar << sn[j+nvln/2]);\n    }\n}\n\n//=================================================================================================\ntemplate <class T> void writeNodalProjectedElementValues(FESurface& dom, FEDataStream& ar, std::function<T(const FEMaterialPoint&)> var)\n{\n\tT gi[FEElement::MAX_INTPOINTS];\n\tT gn[FEElement::MAX_NODES];\n\n\t// loop over all the elements in the domain\n\tint NE = dom.Elements();\n\tfor (int i = 0; i < NE; ++i)\n\t{\n\t\t// get the element and loop over its integration points\n\t\t// we only calculate the element's average\n\t\t// but since most material parameters can only defined \n\t\t// at the element level, this should get the same answer\n\t\tFESurfaceElement& e = dom.Element(i);\n\t\tint nint = e.GaussPoints();\n\t\tint neln = e.Nodes();\n\n\t\tfor (int j = 0; j < nint; ++j)\n\t\t{\n\t\t\t// get the material point data for this integration point\n\t\t\tFEMaterialPoint& mp = *e.GetMaterialPoint(j);\n\t\t\tgi[j] = var(mp);\n\t\t}\n\n\t\te.FEElement::project_to_nodes(gi, gn);\n\n\t\t// store the result\n\t\tfor (int j = 0; j < neln; ++j) ar << gn[j];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// helper functions for writing SPR projected element values\n// TODO: I needed to give these functions a different name because of the implicit conversion between mat3ds and mat3dd\nFECORE_API void writeSPRElementValue(FESolidDomain& dom, FEDataStream& ar, std::function<double(const FEMaterialPoint&)> fnc, int interpolOrder = -1);\nFECORE_API void writeSPRElementValueVectorDouble(FESolidDomain& dom, FEDataStream& ar, std::function<std::vector<double>(const FEMaterialPoint&)> fnc, int interpolOrder = -1, int n_fields = -1);\nFECORE_API void writeSPRElementValueMat3dd(FESolidDomain& dom, FEDataStream& ar, std::function<mat3dd(const FEMaterialPoint&)> fnc, int interpolOrder = -1);\nFECORE_API void writeSPRElementValueMat3ds(FESolidDomain& dom, FEDataStream& ar, std::function<mat3ds(const FEMaterialPoint&)> fnc, int interpolOrder = -1);\n\n// Helper functions for mapping data\nFECORE_API void ProjectToNodes(FEDomain& dom, vector<double>& nodeVals, function<double(FEMaterialPoint& mp)> f);\nFECORE_API void writeRelativeError(FEDomain& dom, FEDataStream& a, function<double(FEMaterialPoint& mp)> f);\n"
  },
  {
    "path": "FEImgLib/FEImageDataMap.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEImageDataMap.h\"\n#include <FECore/FEDomainMap.h>\n#include <FECore/FEMesh.h>\n#include \"image_tools.h\"\n\nBEGIN_FECORE_CLASS(FEImageDataMap, FEElemDataGenerator)\n\tADD_PARAMETER(m_r0, \"range_min\");\n\tADD_PARAMETER(m_r1, \"range_max\");\n\tADD_PARAMETER(m_blur, \"blur\");\n\tADD_PROPERTY(m_imgSrc, \"image\");\nEND_FECORE_CLASS();\n\nFEImageDataMap::FEImageDataMap(FEModel* fem) : FEElemDataGenerator(fem), m_map(m_im)\n{\n\tm_imgSrc = nullptr;\n\tm_blur = 0.0;\n\tm_data = nullptr;\n}\n\nbool FEImageDataMap::Init()\n{\n\tif (m_imgSrc == nullptr) return false;\n\n\tif (m_imgSrc->GetImage3D(m_im0) == false)\n\t{\n\t\treturn false;\n\t}\n\tm_im = m_im0;\n\tm_map.SetRange(m_r0, m_r1);\n\n\treturn FEElemDataGenerator::Init();\n}\n\nFEDataMap* FEImageDataMap::Generate()\n{\n\tFEElementSet* elset = GetElementSet();\n\tif (elset == nullptr) return nullptr;\n\n\t// TODO: Can I use FMT_NODE?\n\tif (m_data == nullptr) m_data = new FEDomainMap(FEDataType::FE_DOUBLE, Storage_Fmt::FMT_MULT);\n\tm_data->Create(elset);\n\n\tGenerateData();\n\treturn m_data;\n}\n\nvoid FEImageDataMap::GenerateData()\n{\n\tassert(m_data);\n\tFEElementSet& set = *GetElementSet();\n\tFEMesh& mesh = *set.GetMesh();\n\tint N = set.Elements();\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tFEElement& el = set.Element(i);\n\t\tint ne = el.Nodes();\n\t\tfor (int j = 0; j < ne; ++j)\n\t\t{\n\t\t\tvec3d ri = mesh.Node(el.m_node[j]).m_r0;\n\t\t\tdouble d = m_map.value(m_map.map(ri));\n\t\t\tm_data->setValue(i, j, d);\n\t\t}\n\t}\n}\n\nvoid FEImageDataMap::Evaluate(double time)\n{\n\tif (m_blur > 0)\n\t{\n\t\tif (m_im0.depth() == 1) blur_image_2d(m_im, m_im0, (float)m_blur);\n\t\telse blur_image_3d(m_im, m_im0, (float)m_blur);\n\t}\n\telse m_im = m_im0;\n\n\tGenerateData();\n}\n"
  },
  {
    "path": "FEImgLib/FEImageDataMap.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEDataGenerator.h>\n#include \"FEImageSource.h\"\n#include \"ImageMap.h\"\n#include \"feimglib_api.h\"\n\nclass FEDomainMap;\n\nclass FEIMGLIB_API FEImageDataMap : public FEElemDataGenerator\n{\npublic:\n\tFEImageDataMap(FEModel* fem);\n\n\tbool Init() override;\n\n\tFEDataMap* Generate() override;\n\n\tvoid Evaluate(double time) override;\n\nprivate:\n\tvoid GenerateData();\n\nprivate:\n\tvec3d\tm_r0;\n\tvec3d\tm_r1;\n\tdouble\tm_blur;\n\n\tFEImageSource* m_imgSrc;\n\nprivate:\n\tImage\t\tm_im0, m_im;\n\tImageMap\tm_map;\n\tFEDomainMap* m_data;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEImgLib/FEImageSource.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEImageSource.h\"\n#include <FECore/log.h>\n\nFEImageSource::FEImageSource(FEModel* fem) : FECoreClass(fem)\n{\n\n}\n\n//========================================================================\n\nBEGIN_FECORE_CLASS(FERawImage, FEImageSource)\n\tADD_PARAMETER(m_file, \"file\", FE_PARAM_ATTRIBUTE);\n\tADD_PARAMETER(m_format, \"format\", 0, \"RAW8\\0RAW16U\\0\");\n\tADD_PARAMETER(m_dim, 3, \"size\");\n\tADD_PARAMETER(m_bend, \"endianess\");\nEND_FECORE_CLASS();\n\nFERawImage::FERawImage(FEModel* fem) : FEImageSource(fem)\n{\n\tm_dim[0] = m_dim[1] = m_dim[2] = 0;\n\tm_bend = false;\n\tm_format = 0;\n}\n\nbool FERawImage::GetImage3D(Image& im)\n{\n\t// get the file name\n\tconst char* szfile = m_file.c_str();\n\n\tint* n = m_dim;\n\tim.Create(n[0], n[1], n[2]);\n\n\t// Try to load the image file\n\tImage::ImageFormat fmt;\n\tswitch (m_format)\n\t{\n\tcase 0: fmt = Image::RAW8; break;\n\tcase 1: fmt = Image::RAW16U; break;\n\tdefault:\n\t\tassert(false);\n\t\treturn false;\n\t}\n\treturn Load(szfile, im, fmt, m_bend);\n}\n\n//-----------------------------------------------------------------------------\nbool FERawImage::Load(const char* szfile, Image& im, Image::ImageFormat fmt, bool endianess)\n{\n\t// check the format\n\tint m = -1;\n\tswitch (fmt)\n\t{\n\tcase Image::RAW8: m = 1; break;\n\tcase Image::RAW16U: m = 2; break;\n\tdefault:\n\t\treturn false;\n\t\tbreak;\n\t}\n\n\t// open the file\n\tFILE* fp = fopen(szfile, \"rb\");\n\tif (fp == 0) return false;\n\n\t// read the data\n\tint nx = im.width();\n\tint ny = im.height();\n\tint nz = im.depth();\n\tint voxels = nx * ny * nz;\n\tint nsize = voxels * m;\n\tunsigned char* pb = new unsigned char[nsize];\n\tfread(pb, nsize, 1, fp);\n\n\tfloat* pf = im.data();\n\n\tif (fmt == Image::RAW8)\n\t{\n\t\tfor (int k = 0; k < nz; ++k)\n\t\t\tfor (int j = 0; j < ny; ++j)\n\t\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t\t{\n\t\t\t\t\tfloat f = (float)pb[(k * ny + j) * nx + i] / 255.f;\n\t\t\t\t\tpf[(k * ny + (ny - j - 1)) * nx + i] = f;\n\t\t\t\t}\n\t}\n\telse if (fmt == Image::RAW16U)\n\t{\n\t\tfor (int k = 0; k < nz; ++k)\n\t\t\tfor (int j = 0; j < ny; ++j)\n\t\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t\t{\n\t\t\t\t\tunsigned char* d = pb + 2 * ((k * ny + j) * nx + i);\n\n\t\t\t\t\tunsigned short n = *((unsigned short*)d);\n\n\t\t\t\t\tif (endianess)\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int n1 = (unsigned int)d[0];\n\t\t\t\t\t\tunsigned int n2 = (unsigned int)d[1];\n\t\t\t\t\t\tn = (n1 << 8) + n2;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat f = (float)n / 65535.f;\n\t\t\t\t\tpf[(k * ny + (ny - j - 1)) * nx + i] = f;\n\t\t\t\t}\n\t}\n\n\t// all done\n\tdelete[] pb;\n\tfclose(fp);\n\treturn true;\n}\n\n//=============================================================================\nBEGIN_FECORE_CLASS(FENRRDImage, FEImageSource)\n\tADD_PARAMETER(m_file, \"file\");\nEND_FECORE_CLASS();\n\nFENRRDImage::FENRRDImage(FEModel* fem) : FEImageSource(fem)\n{\n}\n\nbool FENRRDImage::GetImage3D(Image& im)\n{\n\tif (m_file.empty()) return false;\n\tconst char* szfile = m_file.c_str();\n\treturn Load(szfile, im);\n}\n\nbool nrrd_read_line(FILE* fp, char* buf)\n{\n\tchar c;\n\twhile (fread(&c, 1, 1, fp))\n\t{\n\t\tif ((c != '\\r') && (c != '\\n')) *buf++ = c;\n\t\tif (c == '\\n') {\n\t\t\t*buf = 0; return true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool nrrd_read_key_value(const char* szline, char* key, char* val)\n{\n\tconst char* c = szline;\n\tchar* d = key;\n\tbool skip_space = true;\n\twhile (*c)\n\t{\n\t\tif (*c == ':')\n\t\t{\n\t\t\t*d = 0;\n\t\t\td = val;\n\t\t\tskip_space = true;\n\t\t}\n\t\telse if (skip_space) {\n\t\t\tif (isspace(*c) == 0) {\n\t\t\t\t*d++ = *c;\n\t\t\t\tskip_space = false;\n\t\t\t}\n\t\t}\n\t\telse *d++ = *c;\n\t\tc++;\n\t}\n\t*d = 0;\n\treturn true;\n}\n\nbool FENRRDImage::Load(const char* szfile, Image& im)\n{\n\tif (szfile == nullptr) return false;\n\n\tFILE* fp = fopen(szfile, \"rb\");\n\tif (fp == nullptr) return false;\n\n\t// read the header\n\tchar buf[1024] = { 0 }, key[256] = { 0 }, val[256] = { 0 };\n\tnrrd_read_line(fp, buf);\n\tif (strncmp(buf, \"NRRD\", 4) != 0) { fclose(fp); return false; }\n\n\tNRRD_TYPE imType = NRRD_INVALID;\n\tint dim = 0;\n\tint sizes[3] = { 0 };\n\tNRRD_ENCODING enc = NRRD_RAW;\n\n\twhile (nrrd_read_line(fp, buf))\n\t{\n\t\tif (buf[0] == 0) break;\n\n\t\t// read key-value\n\t\tif (buf[0] != '#')\n\t\t{\n\t\t\tif (nrrd_read_key_value(buf, key, val) == false) { fclose(fp); return false; }\n\n\t\t\tif (strcmp(key, \"type\") == 0)\n\t\t\t{\n\t\t\t\tif (strcmp(val, \"float\") == 0) imType = NRRD_FLOAT;\n\t\t\t}\n\t\t\telse if (strcmp(key, \"dimension\") == 0) dim = atoi(val);\n\t\t\telse if (strcmp(key, \"sizes\") == 0) sscanf(val, \"%d %d %d\", sizes, sizes + 1, sizes + 2);\n\t\t\telse if (strcmp(key, \"encoding\") == 0)\n\t\t\t{\n\t\t\t\tif (strcmp(val, \"raw\") == 0) enc = NRRD_RAW;\n\t\t\t\tif (strcmp(val, \"ascii\") == 0) enc = NRRD_ASCII;\n\t\t\t}\n\t\t}\n\t}\n\n\t// read the image\n\tif (imType != NRRD_FLOAT) { feLog(\"Invalid image format (only float supported)\"); fclose(fp); return false; }\n\tif (dim != 3) { feLog(\"Invalid image dimensions (must be 3)\"); fclose(fp); return false; }\n\tif (enc != NRRD_RAW) { feLog(\"Invalid encoding (only raw supported)\"); fclose(fp); return false; }\n\n\tint nsize = sizes[0] * sizes[1] * sizes[2];\n\tif (nsize <= 0) { feLog(\"Invalid image sizes\"); fclose(fp); return false; }\n\n\tim.Create(sizes[0], sizes[1], sizes[2]);\n\tfloat* pf = im.data();\n\tint nread = fread(pf, sizeof(float), nsize, fp);\n\tif (nread != nsize) { feLog(\"Failed reading image data\"); fclose(fp); return false; }\n\t\n\tfclose(fp);\n\n\treturn true;\n}\n"
  },
  {
    "path": "FEImgLib/FEImageSource.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"Image.h\"\n#include <FECore/FECoreClass.h>\n#include \"feimglib_api.h\"\n\n//---------------------------------------------------------------------------\n// Base class for image sources. \nclass FEIMGLIB_API FEImageSource : public FECoreClass\n{\n\tFECORE_BASE_CLASS(FEImageSource)\n\npublic:\n\tFEImageSource(FEModel* fem);\n\n\tvirtual bool GetImage3D(Image& im) = 0;\n\n\tstd::string GetFileName() { return m_file; }\n\nprotected:\n\tstd::string\t\tm_file;\n};\n\n//---------------------------------------------------------------------------\n// Class for reading raw images\nclass FEIMGLIB_API FERawImage : public FEImageSource\n{\npublic:\n\tFERawImage(FEModel* fem);\n\n\tbool GetImage3D(Image& im) override;\n\nprivate:\n\t// load raw data from file\n\tbool Load(const char* szfile, Image& im, Image::ImageFormat fmt, bool endianess = false);\n\nprotected:\n\tint\t\t\t\tm_dim[3];\n\tint\t\t\t\tm_format;\n\tbool\t\t\tm_bend;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n//---------------------------------------------------------------------------\n// Class for reading NRRD images\nclass FEIMGLIB_API FENRRDImage : public FEImageSource\n{\n\tenum NRRD_TYPE {\n\t\tNRRD_INVALID,\n\t\tNRRD_FLOAT\n\t};\n\n\tenum NRRD_ENCODING {\n\t\tNRRD_RAW,\n\t\tNRRD_ASCII\n\t};\n\npublic:\n\tFENRRDImage(FEModel* fem);\n\n\tbool GetImage3D(Image& im) override;\n\nprivate:\n\t// load image data from file\n\tbool Load(const char* szfile, Image& im);\n\nprivate:\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEImgLib/FEImageValuator.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"FEImageValuator.h\"\n#include <FECore/FEMaterialPoint.h>\n\nBEGIN_FECORE_CLASS(FEImageValuator, FEScalarValuator)\n\tADD_PARAMETER(m_r0, \"range_min\");\n\tADD_PARAMETER(m_r1, \"range_max\");\n\tADD_PROPERTY(m_imSrc, \"image\")->SetDefaultType(\"raw\");\n\tADD_PROPERTY(m_transform, \"transform\");\nEND_FECORE_CLASS();\n\nFEImageValuator::FEImageValuator(FEModel* fem) : FEScalarValuator(fem), m_map(m_im)\n{\n\tm_imSrc = nullptr;\n}\n\nbool FEImageValuator::Init()\n{\n\tif (m_imSrc == nullptr) return false;\n\tif (m_imSrc->GetImage3D(m_im) == false) return false;\n\n\tm_map.SetRange(m_r0, m_r1);\n\n\treturn FEScalarValuator::Init();\n}\n\ndouble FEImageValuator::operator()(const FEMaterialPoint& pt)\n{\n\treturn m_transform->value(m_map.value(pt.m_r0));\n}\n\nFEScalarValuator* FEImageValuator::copy()\n{\n\tassert(false);\n\treturn nullptr;\n}\n"
  },
  {
    "path": "FEImgLib/FEImageValuator.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/FEScalarValuator.h>\n#include <FECore/FEFunction1D.h>\n#include \"FEImageSource.h\"\n#include \"ImageMap.h\"\n#include \"feimglib_api.h\"\n\nclass FEIMGLIB_API FEImageValuator : public FEScalarValuator\n{\npublic:\n\tFEImageValuator(FEModel* fem);\n\n\tbool Init() override;\n\n\tdouble operator()(const FEMaterialPoint& pt) override;\n\n\tFEScalarValuator* copy() override;\n\nprivate:\n\tvec3d\tm_r0, m_r1;\n\tFEImageSource* m_imSrc;\n\tFEFunction1D* m_transform;\n\n\tImage\t\tm_im;\n\tImageMap\tm_map;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "FEImgLib/FEImgLib.cpp",
    "content": "#include \"FEImgLib.h\"\n#include <FECore/FECoreKernel.h>\n#include \"FEImageSource.h\"\n#include \"FEImageDataMap.h\"\n#include \"FEImageValuator.h\"\n#include \"ImageFilter.h\"\n\nvoid FEImgLib::InitModule()\n{\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n\n\tfecore.SetActiveModule(0); // set to core module\n\n\t// image sources\n\tREGISTER_FECORE_CLASS(FERawImage, \"raw\");\n\tREGISTER_FECORE_CLASS(FENRRDImage, \"nrrd\");\n\n\t// data maps\n\tREGISTER_FECORE_CLASS(FEImageDataMap, \"image map\", FECORE_EXPERIMENTAL);\n\n\t// valuator\n\tREGISTER_FECORE_CLASS(FEImageValuator, \"image map\", FECORE_EXPERIMENTAL);\n\n\t// filter classes\n\tREGISTER_FECORE_CLASS(IterativeBlur, \"iterative blur\");\n\tREGISTER_FECORE_CLASS(BoxBlur, \"box blur\");\n#ifdef HAVE_FFTW\n\tREGISTER_FECORE_CLASS(FFTWBlur, \"FFTW blur\");\n#endif // HAVE_FFTW\n}\n"
  },
  {
    "path": "FEImgLib/FEImgLib.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"feimglib_api.h\"\n\nnamespace FEImgLib\n{\n\tFEIMGLIB_API void InitModule();\n}\n"
  },
  {
    "path": "FEImgLib/Image.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"Image.h\"\n#include <memory.h>\n\n//-----------------------------------------------------------------------------\nImage::Image(void)\n{\n\tm_pf = 0;\n\tm_nx = m_ny = m_nz = 0;\n}\n\n//-----------------------------------------------------------------------------\nImage::~Image(void)\n{\n\tdelete [] m_pf;\n\tm_pf = 0;\n}\n\n//-----------------------------------------------------------------------------\nvoid Image::Create(int nx, int ny, int nz)\n{\n\tif (m_pf) delete [] m_pf;\n\tm_nx = nx;\n\tm_ny = ny;\n\tm_nz = nz;\n\tm_pf = new float[m_nx*m_ny*m_nz];\n}\n\n//-----------------------------------------------------------------------------\nImage::Image(Image& im)\n{\n\tm_pf = 0;\n\tCreate(im.width(), im.height(), im.depth());\n\tmemcpy(m_pf, im.m_pf, m_nx*m_ny*m_nz*sizeof(float));\n}\n\n//-----------------------------------------------------------------------------\nImage& Image::operator = (Image& im)\n{\n\tif ((m_nx != im.m_nx)||(m_ny != im.m_ny)||(m_nz != im.m_nz)) Create(im.width(), im.height(), im.depth());\n\tmemcpy(m_pf, im.m_pf, m_nx*m_ny*m_nz*sizeof(float));\n\treturn (*this);\n}\n\n//-----------------------------------------------------------------------------\nvoid Image::zero()\n{\n\tint n = m_nx*m_ny*m_nz;\n\tfor (int i=0; i<n; ++i) m_pf[i] = 0.f;\n}\n\n//-----------------------------------------------------------------------------\nfloat* Image::data()\n{\n\treturn m_pf;\n}\n\n//-----------------------------------------------------------------------------\nvoid image_derive_x(Image& s, Image& d)\n{\n\tint nx = s.width();\n\tint ny = s.height();\n\tint nz = s.depth();\n\tfor (int k=0; k<nz; ++k)\n\t{\n\t\tfor (int j=0; j<ny; ++j)\n\t\t{\n\t\t\td.value(0, j, k) = s.value(1, j, k) - s.value(0, j, k);\n\t\t\tfor (int i=1; i<nx-1; ++i) d.value(i, j, k) = (s.value(i+1, j, k) - s.value(i-1, j, k))*0.5f;\n\t\t\td.value(nx-1, j, k) = s.value(nx-1, j, k) - s.value(nx-2, j, k);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid image_derive_y(Image& s, Image& d)\n{\n\tint nx = s.width();\n\tint ny = s.height();\n\tint nz = s.depth();\n\tfor (int k=0; k<nz; ++k)\n\t{\n\t\tfor (int i=0; i<nx; ++i)\n\t\t{\n\t\t\td.value(i, 0, k) = s.value(i, 1, k) - s.value(i, 0, k);\n\t\t\tfor (int j=1; j<ny-1; ++j) d.value(i, j, k) = (s.value(i, j+1, k) - s.value(i, j-1, k)) *0.5f;\n\t\t\td.value(i, ny-1, k) = s.value(i, ny-1, k) - s.value(i, ny-2, k);\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\nvoid image_derive_z(Image& s, Image& d)\n{\n\tint nx = s.width();\n\tint ny = s.height();\n\n\tint nz = s.depth();\n\tif (nz == 1) { d.zero(); return; }\n\n\tfor (int j=0; j<ny; ++j)\n\t{\n\t\tfor (int i=0; i<nx; ++i)\n\t\t{\n\t\t\td.value(i, j, 0) = s.value(i, j, 1) - s.value(i, j, 0);\n\t\t\tfor (int k=1; k<nz-1; ++k) d.value(i, j, k) = (s.value(i, j, k+1) - s.value(i, j, k-1)) *0.5f;\n\t\t\td.value(i, j, nz-1) = s.value(i, j, nz-1) - s.value(i, j, nz-2);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "FEImgLib/Image.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include \"feimglib_api.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a simple single precision grayscale 3D-image\nclass FEIMGLIB_API Image\n{\npublic:\n\tenum ImageFormat {\n\t\tRAW8,\n\t\tRAW16U\n\t};\n\npublic:\n\t// constructor\n\tImage(void);\n\n\t// destructor\n\t~Image(void);\n\n\t//! copy constructor\n\tImage(Image& im);\n\n\t//! assignment operator\n\tImage& operator = (Image& im);\n\n\t// allocate storage for image data\n\tvoid Create(int nx, int ny, int nz);\n\n\t// return size attributes\n\tint width () { return m_nx; }\n\tint height() { return m_ny; }\n\tint depth () { return m_nz; }\n\n\t// get a particular data value\n\tfloat& value(int x, int y, int z) { return m_pf[(z*m_ny + (m_ny-y-1))*m_nx+x]; }\n\n\t// zero image data\n\tvoid zero();\n\n\t// get the data pointer\n\tfloat* data();\n\nprotected:\n\tfloat*\tm_pf;\t\t\t\t// image data\n\tint\t\tm_nx, m_ny, m_nz;\t// image dimensions\n};\n\n//-----------------------------------------------------------------------------\n// helper functions for calculating image derivatives\nvoid image_derive_x(Image& s, Image& d);\nvoid image_derive_y(Image& s, Image& d);\nvoid image_derive_z(Image& s, Image& d);\n"
  },
  {
    "path": "FEImgLib/ImageFilter.cpp",
    "content": "#include \"Image.h\"\n#include \"ImageFilter.h\"\n#include <FECore/log.h>\n#include <vector>\n#include \"image_tools.h\"\n\n#ifdef HAVE_FFTW\n#include <fftw3.h>\n#endif // HAVE_FFTW\n\nImageFilter::ImageFilter(FEModel* fem) : FECoreClass(fem)\n{\n\n}\n\n//=================================================================================================\n// IterativeBlur\n//=================================================================================================\n\nBEGIN_FECORE_CLASS(IterativeBlur, ImageFilter)\n\tADD_PARAMETER(m_blur, \"blur\");\n\tADD_PARAMETER(m_norm_flag, \"normalize_values\");\nEND_FECORE_CLASS();\n\nIterativeBlur::IterativeBlur(FEModel* fem) : ImageFilter(fem)\n{\n\tm_blur = 0.0;\n\tm_norm_flag = false;\n}\n\nbool IterativeBlur::Init()\n{\n\treturn true;\n}\n\n//sequential 1D approach\nvoid IterativeBlur::Update(Image& trg, Image& src)\n{\n\tif (m_blur <= 0) { trg = src; return; }\n\tfeLog(\"Blurring images, blur factor %lg\\n\", m_blur);\n\n\tint K = (int)m_blur;\n\tfloat w = float(m_blur) - (float)K;\n\n\tint nx = src.width();\n\tint ny = src.height();\n\tint nz = src.depth();\n\n\ttrg = src;\n\tImage tmp(src);\n\t\n\tfor (int k = 0; k < K; ++k)\n\t{\n\t\tfor (int m = 0; m < 3; ++m)\n\t\t{\n#ifdef NDEBUG\n\t\t\t#pragma omp parallel for\n#endif\n\t\t\tfor (int z = 0; z < nz; ++z)\n\t\t\t\tfor (int y = 0; y < ny; ++y)\n\t\t\t\t\tfor (int x = 0; x < nx; ++x) {\n\t\t\t\t\t\tint m_pos[] = { x, y, z };\n\t\t\t\t\t\tint m_range[] = { nx, ny, nz };\n\t\t\t\t\t\ttrg.value(x, y, z) = Apply(tmp, m_pos, m_range, m);\n\t\t\t\t\t}\n\t\t\ttmp = trg;\n\t\t}\n\t}\n\n\tif (w > 0.0)\n\t{\n\t\tfor (int m = 0; m < 3; ++m)\n\t\t{\n#ifdef NDEBUG\n\t\t\t#pragma omp parallel for\n#endif\n\t\t\tfor (int z = 0; z < nz; ++z)\n\t\t\t\tfor (int y = 0; y < ny; ++y)\n\t\t\t\t\tfor (int x = 0; x < nx; ++x) {\n\t\t\t\t\t\tint m_pos[] = { x, y, z };\n\t\t\t\t\t\tint m_range[] = { nx, ny, nz };\n\t\t\t\t\t\tfloat f1 = Apply(tmp, m_pos, m_range, m);\n\t\t\t\t\t\tfloat f2 = trg.value(x, y, z);\n\t\t\t\t\t\ttrg.value(x, y, z) = Apply(tmp, m_pos, m_range, m);\n\t\t\t\t\t\ttrg.value(x, y, z) = f1 * w + f2 * (1.f - w);\n\t\t\t\t\t}\n\t\t\ttmp = trg;\n\t\t}\n\t}\n}\n\nfloat IterativeBlur::Apply(Image& img, int m_pos[3], int m_range[3], int m_dir)\n{\n\tint x = m_pos[0]; int y = m_pos[1]; int z = m_pos[2];\n\tint nx = m_range[0]; int ny = m_range[1]; int nz = m_range[2];\n\tfloat f = 0.0;\n\tif (x > 0) f += img.value(x - 1, y, z); else f += img.value(x, y, z);\n\tif (x < nx - 1) f += img.value(x + 1, y, z); else f += img.value(x, y, z);\n\tif (y > 0) f += img.value(x, y - 1, z); else f += img.value(x, y, z);\n\tif (y < ny - 1) f += img.value(x, y + 1, z); else f += img.value(x, y, z);\n\tif (z > 0) f += img.value(x, y, z - 1); else f += img.value(x, y, z);\n\tif (z < nz - 1) f += img.value(x, y, z + 1); else f += img.value(x, y, z);\n\treturn float(0.1666667f) * f;\n}\n\n//=================================================================================================\n// BoxBlur\n//=================================================================================================\n\nBEGIN_FECORE_CLASS(BoxBlur, ImageFilter)\n\tADD_PARAMETER(m_blur, \"blur\"); // blur radius\n\tADD_PARAMETER(m_K, \"iterations\");\n\tADD_PARAMETER(m_norm_flag, \"normalize_values\");\n\tADD_PARAMETER(m_res, 3, \"voxel_resolution\");\nEND_FECORE_CLASS();\n\nBoxBlur::BoxBlur(FEModel* fem) : ImageFilter(fem)\n{\n\tm_blur = 0.0;\n\tm_K = 0;\n\tm_norm_flag = false;\n\tm_res[0] = 1.0; m_res[1] = 1.0; m_res[2] = 1.0;\n\tm_ri[0] = 1; m_ri[1] = 1; m_ri[2] = 1;\n\tm_rp = 0.0;\n\tm_tp = -1.0;\n}\n\nbool BoxBlur::Init()\n{\n\treturn true;\n}\n\nvoid BoxBlur::Update(Image& trg, Image& src)\n{\n\tif (m_blur < 1) { trg = src; return; }\n\t// approximate gaussian blur from https://www.ipol.im/pub/art/2013/87/?utm_source=doi\n\tdouble r_eff = (int)floor(0.5 * sqrt(((12.0 * m_blur * m_blur / m_K) + 1.0)));\n\tdouble sigma_i[3] = { 0.0, 0.0, 0.0 };\n\tfor (int i = 0; i < 3; ++i)\n\t{\n\t\tsigma_i[i] = m_blur / m_res[i]; // (units of px)\n\t\tm_ri[i] = (int)floor(0.5 * sqrt(((12.0 * sigma_i[i] * sigma_i[i] / m_K) + 1.0)));\n\t}\n\tfeLog(\"Blurring images\\n\");\n\tfeLog(\"blur radius = %lg px, rx = %d px, ry = %d px, rz = %d px,\\n\", r_eff, m_ri[0], m_ri[1], m_ri[2]);\n\tfeLog(\"blur std = %lg, stdx = %lg, stdy = %lg, stdz = %lg\\n\", m_blur, sigma_i[0], sigma_i[1], sigma_i[2]);\n\n\tint nx = src.width();\n\tint ny = src.height();\n\tint nz = src.depth();\n\n\ttrg = src;\n\tImage tmp(src);\n\n\t// for each iteration\n\tfor (int k = 0; k < m_K; ++k)\n\t{\n\t\t// perform 1D blur along each dimension\n\t\tfor (int m = 0; m < 3; ++m)\n\t\t{\n#ifdef NDEBUG\n\t\t\t#pragma omp parallel for\n#endif\n\t\t\tfor (int z = 0; z < nz; ++z)\n\t\t\t\tfor (int y = 0; y < ny; ++y)\n\t\t\t\t\tfor (int x = 0; x < nx; ++x)\n\t\t\t\t\t{\n\t\t\t\t\t\tint m_pos[] = { x, y, z };\n\t\t\t\t\t\tint m_range[] = { nx, ny, nz };\n\t\t\t\t\t\t\n\t\t\t\t\t\ttrg.value(x, y, z) = Apply(tmp, m_pos, m_range, m);\n\t\t\t\t\t}\n\t\t\ttmp = trg;\n\t\t}\n\t}\n}\n\nfloat BoxBlur::Apply(Image& img, int m_pos[3], int m_range[3], int m_dir)\n{\n\t//naive implementation\n\tint x = m_pos[0]; int y = m_pos[1]; int z = m_pos[2];\n\tint nx = m_range[0]; int ny = m_range[1]; int nz = m_range[2];\n\tfloat f = 0.0;\n\tswitch (m_dir)\n\t{\n\t\tcase 0:\n\t\t{\n\t\t\tfor (int i_r = -m_ri[0]; i_r <= m_ri[0]; ++i_r)\n\t\t\t{\n\t\t\t\tint xr = symmetric_extension(x + i_r, nx);\n\t\t\t\tf += img.value(xr, y, z);\n\t\t\t}\n\t\t\tf /= (2.f * m_ri[0] + 1.f);\n\t\t\tbreak;\n\t\t}\n\t\tcase 1:\n\t\t{\n\t\t\tfor (int i_r = -m_ri[1]; i_r <= m_ri[1]; ++i_r)\n\t\t\t{\n\t\t\t\tint yr = symmetric_extension(y + i_r, ny);\n\t\t\t\tf += img.value(x, yr, z);\n\t\t\t}\n\t\t\tf /= (2.f * m_ri[1] + 1.f);\n\t\t\tbreak;\n\t\t}\n\t\tcase 2:\n\t\t{\n\t\t\tfor (int i_r = -m_ri[2]; i_r <= m_ri[2]; ++i_r)\n\t\t\t{\n\t\t\t\tint zr = symmetric_extension(z + i_r, nz);\n\t\t\t\tf += img.value(x, y, zr);\n\t\t\t}\n\t\t\tf /= (2.f * m_ri[2] + 1.f);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn f;\n}\n\n//=================================================================================================\n// Fastest Fourier Transform in the West (FFTW) Blur\n//=================================================================================================\n\n#ifdef HAVE_FFTW\nBEGIN_FECORE_CLASS(FFTWBlur, ImageFilter)\nADD_PARAMETER(m_blur, \"blur\");\nADD_PARAMETER(m_norm_flag, \"normalize_values\");\nADD_PARAMETER(m_res, 3, \"voxel_resolution\");\nEND_FECORE_CLASS();\n\nFFTWBlur::FFTWBlur(FEModel* fem) : ImageFilter(fem)\n{\n\tm_blur = 0.0;\n\tm_norm_flag = false;\n\tm_res[0] = 1.0; m_res[1] = 1.0; m_res[2] = 1.0;\n\tm_sigma[0] = 1.0; m_sigma[1] = 1.0; m_sigma[2] = 1.0;\n}\n\nbool FFTWBlur::Init() \n{\n\treturn true;\n}\n\nvoid FFTWBlur::Update(Image& trg, Image& src)\n{\n\tif (m_blur < 1) { trg = src; return; }\n\t// scale blur in each direction for voxel resolution\n\tfor (int i = 0; i < 3; ++i)\n\t\tm_sigma[i] = m_blur / m_res[i]; // (units of px)\n\tfeLog(\"Blurring images\\n\");\n\tfeLog(\"blur std = %lg, stdx = %lg, stdy = %lg, stdz = %lg\\n\", m_blur, m_sigma[0], m_sigma[1], m_sigma[2]);\n\tfloat d[3] = { float(m_sigma[0]), float(m_sigma[1]), float(m_sigma[2]) };\n\tfftw_blur_3d(trg, src, d);\n}\n\n// SL: Does nothing for now.\nfloat FFTWBlur::Apply(Image& img, int m_pos[3], int m_range[3], int m_dir)\n{\n\treturn 0.f;\n}\n#endif // HAVE_FFTW"
  },
  {
    "path": "FEImgLib/ImageFilter.h",
    "content": "#pragma once\n#include \"Image.h\"\n#include <FECore/FECoreClass.h>\n#include <FECore/mat3d.h>\n#include \"feimglib_api.h\"\n#include \"image_tools.h\"\n\n#ifdef HAVE_FFTW\n#include <fftw3.h>\n#endif // HAVE_FFTW\n\nclass FEIMGLIB_API ImageFilter : public FECoreClass\n{\n\tFECORE_BASE_CLASS(ImageFilter)\n\npublic:\n\t//! default constructor\n\tImageFilter(FEModel* fem);\n\n\t//! initialize filter\n\tvirtual bool Init() = 0;\n\n\t//! evaluate the filter at the current position\n\tvirtual void Update(Image& trg, Image& src) = 0;\n\n\t//! evaluate the filter at the current position\n\tvirtual float Apply(Image& img, int m_pos[3], int m_range[3], int m_dir) = 0;\n\n\t//! return blur value\n\tvirtual double GetBlur() = 0;\n\nprotected:\n};\n\nclass FEIMGLIB_API IterativeBlur : public ImageFilter\n{\npublic:\n\tIterativeBlur(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Update(Image& trg, Image& src) override;\n\n\tfloat Apply(Image& img, int m_pos[3], int m_range[3], int m_dir) override;\n\n\tdouble GetBlur() override { return m_blur; };\n\nprotected:\n\t//! flag to normalize data so that blur units coincide with physical dimensions rather than img dimensions\n\tbool m_norm_flag;\n\tdouble m_blur;\n\tDECLARE_FECORE_CLASS();\n};\n\nclass FEIMGLIB_API BoxBlur : public ImageFilter\n{\npublic:\n\tBoxBlur(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Update(Image& trg, Image& src) override;\n\n\tfloat Apply(Image& img, int m_pos[3], int m_range[3], int m_dir) override;\n\n\t//! return the current blur value\n\tdouble GetBlur() override { return m_blur; };\n\npublic:\n\tbool m_norm_flag = false;\n\tdouble m_blur; // sigma (units of length)\n\tdouble m_rp; // previous modulus\n\tdouble m_tp; // previous time\n\tint m_K; // iterations of box blur to apply\n\tdouble m_res[3]; // voxel resolution (L / px)\n\tint m_ri[3]; // effective radii along each direction\n\tDECLARE_FECORE_CLASS();\n};\n\n#ifdef HAVE_FFTW\nclass FEIMGLIB_API FFTWBlur : public ImageFilter\n{\npublic:\n\tFFTWBlur(FEModel* fem);\n\n\tbool Init() override;\n\n\tvoid Update(Image& trg, Image& src) override;\n\n\tfloat Apply(Image& img, int m_pos[3], int m_range[3], int m_dir) override;\n\n\tdouble GetBlur() override { return m_blur; };\n\npublic:\n\tbool m_norm_flag = false;\n\tdouble m_blur; // sigma (units of length)\n\tdouble m_res[3]; // voxel resolution (L / px)\n\tdouble m_sigma[3];\n\tDECLARE_FECORE_CLASS();\n};\n#endif // HAVE_FFTW"
  },
  {
    "path": "FEImgLib/ImageMap.cpp",
    "content": "//#include \"stdafx.h\"\n#include \"ImageMap.h\"\n#include \"math.h\"\n#include \"algorithm\"\n#include \"iostream\"\n\nImageMap::ImageMap(Image& img) : m_img(img)\n{\n\tm_r0 = vec3d(0,0,0);\n\tm_r1 = vec3d(0,0,0);\n}\n\nImageMap::~ImageMap(void)\n{\n}\n\nvoid ImageMap::SetRange(vec3d r0, vec3d r1)\n{\n\tm_r0 = r0;\n\tm_r1 = r1;\n}\n\nImageMap::POINT ImageMap::map(const vec3d& p)\n{\n\tint nx = m_img.width ();\n\tint ny = m_img.height();\n\tint nz = m_img.depth ();\n\n\tdouble x = (p.x - m_r0.x)/(m_r1.x - m_r0.x);\n\tdouble y = (p.y - m_r0.y)/(m_r1.y - m_r0.y);\n\tdouble z = (nz==1?0:(p.z - m_r0.z)/(m_r1.z - m_r0.z));\n\n\tdouble dx = 1.0 / (double) (nx - 1);\n\tdouble dy = 1.0 / (double) (ny - 1);\n\tdouble dz = (nz>1?1.0 / (double) (nz - 1):1.0);\n\n\tint i = (int) std::max(floor(x * (nx - 1)), 0.0);\n\tint j = (int) std::max(floor(y * (ny - 1)), 0.0);\n\tint k = (int) std::max(floor(z * (nz - 1)), 0.0);\n\n\tif (i == nx-1) i--;\n\tif (j == ny-1) j--;\n\tif ((k == nz-1)&&(nz>1)) k--;\n\n\tdouble r = (x - i*dx)/dx;\n\tdouble s = (y - j*dy)/dy;\n\tdouble t = (z - k*dz)/dz;\n\n\tPOINT pt;\n\tpt.i = i;\n\tpt.j = j;\n\tpt.k = k;\n\n\tif (nz == 1)\n\t{\n\t\tpt.h[0] = (1-r)*(1-s);\n\t\tpt.h[1] = r*(1-s);\n\t\tpt.h[2] = r*s;\n\t\tpt.h[3] = s*(1-r);\n\t\tpt.h[4] = pt.h[5] = pt.h[6] = pt.h[7] = 0.0;\n\t}\n\telse\n\t{\n\t\tpt.h[0] = (1-r)*(1-s)*(1-t);\n\t\tpt.h[1] = r*(1-s)*(1-t);\n\t\tpt.h[2] = r*s*(1-t);\n\t\tpt.h[3] = s*(1-r)*(1-t);\n\t\tpt.h[4] = (1-r)*(1-s)*t;\n\t\tpt.h[5] = r*(1-s)*t;\n\t\tpt.h[6] = r*s*t;\n\t\tpt.h[7] = s*(1-r)*t;\n\t}\n\n\treturn pt;\n}\n\ndouble ImageMap::value(const POINT& p)\n{\n\tint nx = m_img.width ();\n\tint ny = m_img.height();\n\tint nz = m_img.depth ();\n\n\tif (nz == 1)\n\t{\n\t\tif ((p.i<0) || (p.i >= nx-1)) return 0.0;\n\t\tif ((p.j<0) || (p.j >= ny-1)) return 0.0;\n\n\t\tdouble v[4];\n\t\tv[0] = m_img.value(p.i  , p.j  , 0);\n\t\tv[1] = m_img.value(p.i+1, p.j  , 0);\n\t\tv[2] = m_img.value(p.i+1, p.j+1, 0);\n\t\tv[3] = m_img.value(p.i  , p.j+1, 0);\n\n\t\treturn (p.h[0]*v[0] + p.h[1]*v[1] + p.h[2]*v[2] + p.h[3]*v[3]);\n\t}\n\telse\n\t{\n\t\tif ((p.i<0) || (p.i >= nx-1)) return 0.0;\n\t\tif ((p.j<0) || (p.j >= ny-1)) return 0.0;\n\t\tif ((p.k<0) || (p.k >= nz-1)) return 0.0;\n\n\t\tdouble v[8];\n\t\tv[0] = m_img.value(p.i  , p.j  , p.k  );\n\t\tv[1] = m_img.value(p.i+1, p.j  , p.k  );\n\t\tv[2] = m_img.value(p.i+1, p.j+1, p.k  );\n\t\tv[3] = m_img.value(p.i  , p.j+1, p.k  );\n\t\tv[4] = m_img.value(p.i  , p.j  , p.k+1);\n\t\tv[5] = m_img.value(p.i+1, p.j  , p.k+1);\n\t\tv[6] = m_img.value(p.i+1, p.j+1, p.k+1);\n\t\tv[7] = m_img.value(p.i  , p.j+1, p.k+1);\n\n\t\treturn (p.h[0]*v[0] + p.h[1]*v[1] + p.h[2]*v[2] + p.h[3]*v[3] + p.h[4]*v[4] + p.h[5]*v[5] + p.h[6]*v[6] + p.h[7]*v[7]);\n\t}\n}\n\nbool ImageMap::valid(const vec3d& p)\n{\n\tint nz = m_img.depth();\n\tbool r_valid = true;\n\tr_valid &= (p.x >= m_r0.x && p.x <= m_r1.x);\n\tr_valid &= (p.y >= m_r0.y && p.y <= m_r1.y);\n\tr_valid &= (nz == 1) ? true : (p.z >= m_r0.z && p.z <= m_r1.z);\n\treturn r_valid;\n}\n\nvec3d ImageMap::gradient(const vec3d& r)\n{\n\tPOINT p = map(r);\n\n\tif (m_img.depth() == 1)\n\t{\n\t\t// get the x-gradient values\n\t\tdouble gx[4];\n\t\tgx[0] = grad_x(p.i  , p.j  , 0);\n\t\tgx[1] = grad_x(p.i+1, p.j  , 0);\n\t\tgx[2] = grad_x(p.i+1, p.j+1, 0);\n\t\tgx[3] = grad_x(p.i  , p.j+1, 0);\n\n\t\t// get the y-gradient values\n\t\tdouble gy[4];\n\t\tgy[0] = grad_y(p.i  , p.j  , 0);\n\t\tgy[1] = grad_y(p.i+1, p.j  , 0);\n\t\tgy[2] = grad_y(p.i+1, p.j+1, 0);\n\t\tgy[3] = grad_y(p.i  , p.j+1, 0);\n\n\t\t// get the gradient at point r\n\t\tdouble hx = (p.h[0]*gx[0]+p.h[1]*gx[1]+p.h[2]*gx[2]+p.h[3]*gx[3]);\n\t\tdouble hy = (p.h[0]*gy[0]+p.h[1]*gy[1]+p.h[2]*gy[2]+p.h[3]*gy[3]);\n\n\t\tdouble Dx = dx();\n\t\tdouble Dy = dy();\n\n\t\treturn vec3d(hx/Dx, hy/Dy, 0.0);\n\t}\n\telse\n\t{\n\t\tint nx = m_img.width();\n\t\tint ny = m_img.height();\n\t\tint nz = m_img.depth();\n\n\t\tif ((p.i < 0) || (p.i >= nx - 1)) return vec3d(0,0,0);\n\t\tif ((p.j < 0) || (p.j >= ny - 1)) return vec3d(0,0,0);\n\t\tif ((p.k < 0) || (p.k >= nz - 1)) return vec3d(0,0,0);\n\n\t\t// get the x-gradient values\n\t\tdouble gx[8];\n\t\tgx[0] = grad_x(p.i  , p.j  , p.k  );\n\t\tgx[1] = grad_x(p.i+1, p.j  , p.k  );\n\t\tgx[2] = grad_x(p.i+1, p.j+1, p.k  );\n\t\tgx[3] = grad_x(p.i  , p.j+1, p.k  );\n\t\tgx[4] = grad_x(p.i  , p.j  , p.k+1);\n\t\tgx[5] = grad_x(p.i+1, p.j  , p.k+1);\n\t\tgx[6] = grad_x(p.i+1, p.j+1, p.k+1);\n\t\tgx[7] = grad_x(p.i  , p.j+1, p.k+1);\n\n\t\t// get the y-gradient values\n\t\tdouble gy[8];\n\t\tgy[0] = grad_y(p.i  , p.j  , p.k  );\n\t\tgy[1] = grad_y(p.i+1, p.j  , p.k  );\n\t\tgy[2] = grad_y(p.i+1, p.j+1, p.k  );\n\t\tgy[3] = grad_y(p.i  , p.j+1, p.k  );\n\t\tgy[4] = grad_y(p.i  , p.j  , p.k+1);\n\t\tgy[5] = grad_y(p.i+1, p.j  , p.k+1);\n\t\tgy[6] = grad_y(p.i+1, p.j+1, p.k+1);\n\t\tgy[7] = grad_y(p.i  , p.j+1, p.k+1);\n\n\t\t// get the z-gradient values\n\t\tdouble gz[8];\n\t\tgz[0] = grad_z(p.i  , p.j  , p.k  );\n\t\tgz[1] = grad_z(p.i+1, p.j  , p.k  );\n\t\tgz[2] = grad_z(p.i+1, p.j+1, p.k  );\n\t\tgz[3] = grad_z(p.i  , p.j+1, p.k  );\n\t\tgz[4] = grad_z(p.i  , p.j  , p.k+1);\n\t\tgz[5] = grad_z(p.i+1, p.j  , p.k+1);\n\t\tgz[6] = grad_z(p.i+1, p.j+1, p.k+1);\n\t\tgz[7] = grad_z(p.i  , p.j+1, p.k+1);\n\n\t\t// get the gradient at point r\n\t\tdouble hx = (p.h[0]*gx[0]+p.h[1]*gx[1]+p.h[2]*gx[2]+p.h[3]*gx[3]+p.h[4]*gx[4]+p.h[5]*gx[5]+p.h[6]*gx[6]+p.h[7]*gx[7]);\n\t\tdouble hy = (p.h[0]*gy[0]+p.h[1]*gy[1]+p.h[2]*gy[2]+p.h[3]*gy[3]+p.h[4]*gy[4]+p.h[5]*gy[5]+p.h[6]*gy[6]+p.h[7]*gy[7]);\n\t\tdouble hz = (p.h[0]*gz[0]+p.h[1]*gz[1]+p.h[2]*gz[2]+p.h[3]*gz[3]+p.h[4]*gz[4]+p.h[5]*gz[5]+p.h[6]*gz[6]+p.h[7]*gz[7]);\n\n\t\tdouble Dx = dx();\n\t\tdouble Dy = dy();\n\t\tdouble Dz = dz();\n\n\t\treturn vec3d(hx/Dx, hy/Dy, hz/Dz);\n\t}\n}\n\ndouble ImageMap::grad_x(int i, int j, int k)\n{\n\tint nx = m_img.width();\n\tif (i == 0   ) return m_img.value(i+1, j, k) - m_img.value(i  , j, k);\n\tif (i == nx-1) return m_img.value(i  , j, k) - m_img.value(i-1, j, k);\n\treturn 0.5*(m_img.value(i+1, j, k) - m_img.value(i-1, j, k));\n}\n\ndouble ImageMap::grad_y(int i, int j, int k)\n{\n\tint ny = m_img.height();\n\tif (j == 0   ) return m_img.value(i, j+1, k) - m_img.value(i, j  , k);\n\tif (j == ny-1) return m_img.value(i, j  , k) - m_img.value(i, j-1, k);\n\treturn 0.5*(m_img.value(i, j+1, k) - m_img.value(i, j-1, k));\n}\n\ndouble ImageMap::grad_z(int i, int j, int k)\n{\n\tint nz = m_img.depth();\n\tif (nz == 1) return 0.0;\n\tif (k == 0   ) return m_img.value(i, j, k+1) - m_img.value(i, j, k  );\n\tif (k == nz-1) return m_img.value(i, j, k  ) - m_img.value(i, j, k-1);\n\treturn 0.5*(m_img.value(i, j, k+1) - m_img.value(i, j, k-1));\n}\n\nmat3ds ImageMap::hessian(const vec3d& r)\n{\n\tPOINT p = map(r);\n\n\tif (m_img.depth() == 1)\n\t{\n\t\t// get the xx-hessian values\n\t\tdouble hxx[4];\n\t\thxx[0] = hessian_xx(p.i  , p.j  , 0);\n\t\thxx[1] = hessian_xx(p.i+1, p.j  , 0);\n\t\thxx[2] = hessian_xx(p.i+1, p.j+1, 0);\n\t\thxx[3] = hessian_xx(p.i  , p.j+1, 0);\n\n\t\t// get the yy-hessian values\n\t\tdouble hyy[4];\n\t\thyy[0] = hessian_yy(p.i  , p.j  , 0);\n\t\thyy[1] = hessian_yy(p.i+1, p.j  , 0);\n\t\thyy[2] = hessian_yy(p.i+1, p.j+1, 0);\n\t\thyy[3] = hessian_yy(p.i  , p.j+1, 0);\n\n\t\t// get the xy-hessian values\n\t\tdouble hxy[4];\n\t\thxy[0] = hessian_xy(p.i  , p.j  , 0);\n\t\thxy[1] = hessian_xy(p.i+1, p.j  , 0);\n\t\thxy[2] = hessian_xy(p.i+1, p.j+1, 0);\n\t\thxy[3] = hessian_xy(p.i  , p.j+1, 0);\n\n\t\t// get the hessian at point r\n\t\tdouble Hxx = (p.h[0]*hxx[0]+p.h[1]*hxx[1]+p.h[2]*hxx[2]+p.h[3]*hxx[3]);\n\t\tdouble Hyy = (p.h[0]*hyy[0]+p.h[1]*hyy[1]+p.h[2]*hyy[2]+p.h[3]*hyy[3]);\n\t\tdouble Hxy = (p.h[0]*hxy[0]+p.h[1]*hxy[1]+p.h[2]*hxy[2]+p.h[3]*hxy[3]);\n\n\t\tdouble Dxx = dx()*dx();\n\t\tdouble Dyy = dy()*dy();\n\t\tdouble Dxy = dx()*dy();\n\n\t\treturn mat3ds(Hxx/Dxx, Hyy/Dyy, 0, Hxy/Dxy, 0.0, 0.0);\n\t}\n\telse\n\t{\n\t\t// get the xx-hessian values\n\t\tdouble hxx[8];\n\t\thxx[0] = hessian_xx(p.i  , p.j  , p.k);\n\t\thxx[1] = hessian_xx(p.i+1, p.j  , p.k);\n\t\thxx[2] = hessian_xx(p.i+1, p.j+1, p.k);\n\t\thxx[3] = hessian_xx(p.i  , p.j+1, p.k);\n\t\thxx[4] = hessian_xx(p.i  , p.j  , p.k+1);\n\t\thxx[5] = hessian_xx(p.i+1, p.j  , p.k+1);\n\t\thxx[6] = hessian_xx(p.i+1, p.j+1, p.k+1);\n\t\thxx[7] = hessian_xx(p.i  , p.j+1, p.k+1);\n\n\t\t// get the yy-hessian values\n\t\tdouble hyy[8];\n\t\thyy[0] = hessian_yy(p.i  , p.j  , p.k);\n\t\thyy[1] = hessian_yy(p.i+1, p.j  , p.k);\n\t\thyy[2] = hessian_yy(p.i+1, p.j+1, p.k);\n\t\thyy[3] = hessian_yy(p.i  , p.j+1, p.k);\n\t\thyy[4] = hessian_yy(p.i  , p.j  , p.k+1);\n\t\thyy[5] = hessian_yy(p.i+1, p.j  , p.k+1);\n\t\thyy[6] = hessian_yy(p.i+1, p.j+1, p.k+1);\n\t\thyy[7] = hessian_yy(p.i  , p.j+1, p.k+1);\n\n\t\t// get the zz-hessian values\n\t\tdouble hzz[8];\n\t\thzz[0] = hessian_zz(p.i  , p.j  , p.k);\n\t\thzz[1] = hessian_zz(p.i+1, p.j  , p.k);\n\t\thzz[2] = hessian_zz(p.i+1, p.j+1, p.k);\n\t\thzz[3] = hessian_zz(p.i  , p.j+1, p.k);\n\t\thzz[4] = hessian_zz(p.i  , p.j  , p.k+1);\n\t\thzz[5] = hessian_zz(p.i+1, p.j  , p.k+1);\n\t\thzz[6] = hessian_zz(p.i+1, p.j+1, p.k+1);\n\t\thzz[7] = hessian_zz(p.i  , p.j+1, p.k+1);\n\n\t\t// get the xy-hessian values\n\t\tdouble hxy[8];\n\t\thxy[0] = hessian_xy(p.i  , p.j  , p.k);\n\t\thxy[1] = hessian_xy(p.i+1, p.j  , p.k);\n\t\thxy[2] = hessian_xy(p.i+1, p.j+1, p.k);\n\t\thxy[3] = hessian_xy(p.i  , p.j+1, p.k);\n\t\thxy[4] = hessian_xy(p.i  , p.j  , p.k+1);\n\t\thxy[5] = hessian_xy(p.i+1, p.j  , p.k+1);\n\t\thxy[6] = hessian_xy(p.i+1, p.j+1, p.k+1);\n\t\thxy[7] = hessian_xy(p.i  , p.j+1, p.k+1);\n\n\t\t// get the yz-hessian values\n\t\tdouble hyz[8];\n\t\thyz[0] = hessian_yz(p.i  , p.j  , p.k);\n\t\thyz[1] = hessian_yz(p.i+1, p.j  , p.k);\n\t\thyz[2] = hessian_yz(p.i+1, p.j+1, p.k);\n\t\thyz[3] = hessian_yz(p.i  , p.j+1, p.k);\n\t\thyz[4] = hessian_yz(p.i  , p.j  , p.k+1);\n\t\thyz[5] = hessian_yz(p.i+1, p.j  , p.k+1);\n\t\thyz[6] = hessian_yz(p.i+1, p.j+1, p.k+1);\n\t\thyz[7] = hessian_yz(p.i  , p.j+1, p.k+1);\n\n\t\t// get the xz-hessian values\n\t\tdouble hxz[8];\n\t\thxz[0] = hessian_xz(p.i  , p.j  , p.k);\n\t\thxz[1] = hessian_xz(p.i+1, p.j  , p.k);\n\t\thxz[2] = hessian_xz(p.i+1, p.j+1, p.k);\n\t\thxz[3] = hessian_xz(p.i  , p.j+1, p.k);\n\t\thxz[4] = hessian_xz(p.i  , p.j  , p.k+1);\n\t\thxz[5] = hessian_xz(p.i+1, p.j  , p.k+1);\n\t\thxz[6] = hessian_xz(p.i+1, p.j+1, p.k+1);\n\t\thxz[7] = hessian_xz(p.i  , p.j+1, p.k+1);\n\n\t\t// get the hessian at point r\n\t\tdouble Hxx = (p.h[0]*hxx[0]+p.h[1]*hxx[1]+p.h[2]*hxx[2]+p.h[3]*hxx[3]+p.h[4]*hxx[4]+p.h[5]*hxx[5]+p.h[6]*hxx[6]+p.h[7]*hxx[7]);\n\t\tdouble Hyy = (p.h[0]*hyy[0]+p.h[1]*hyy[1]+p.h[2]*hyy[2]+p.h[3]*hyy[3]+p.h[4]*hyy[4]+p.h[5]*hyy[5]+p.h[6]*hyy[6]+p.h[7]*hyy[7]);\n\t\tdouble Hzz = (p.h[0]*hzz[0]+p.h[1]*hzz[1]+p.h[2]*hzz[2]+p.h[3]*hzz[3]+p.h[4]*hzz[4]+p.h[5]*hzz[5]+p.h[6]*hzz[6]+p.h[7]*hzz[7]);\n\t\tdouble Hxy = (p.h[0]*hxy[0]+p.h[1]*hxy[1]+p.h[2]*hxy[2]+p.h[3]*hxy[3]+p.h[4]*hxy[4]+p.h[5]*hxy[5]+p.h[6]*hxy[6]+p.h[7]*hxy[7]);\n\t\tdouble Hyz = (p.h[0]*hyz[0]+p.h[1]*hyz[1]+p.h[2]*hyz[2]+p.h[3]*hyz[3]+p.h[4]*hyz[4]+p.h[5]*hyz[5]+p.h[6]*hyz[6]+p.h[7]*hyz[7]);\n\t\tdouble Hxz = (p.h[0]*hxz[0]+p.h[1]*hxz[1]+p.h[2]*hxz[2]+p.h[3]*hxz[3]+p.h[4]*hxz[4]+p.h[5]*hxz[5]+p.h[6]*hxz[6]+p.h[7]*hxz[7]);\n\n\t\tdouble Dxx = dx()*dx();\n\t\tdouble Dyy = dy()*dy();\n\t\tdouble Dzz = dz()*dz();\n\t\tdouble Dxy = dx()*dy();\n\t\tdouble Dyz = dy()*dz();\n\t\tdouble Dxz = dx()*dz();\n\n\t\treturn mat3ds(Hxx/Dxx, Hyy/Dyy, Hzz/Dzz, Hxy/Dxy, Hyz/Dyz, Hxz/Dxz);\n\t}\n}\n\ndouble ImageMap::hessian_xx(int i, int j, int k)\n{\n\tint nx = m_img.width();\n\tif (nx <= 2) return 0.0;\n\tif (i==0   ) return (grad_x(i+1,j,k) - grad_x(i  ,j,k));\n\tif (i==nx-1) return (grad_x(i  ,j,k) - grad_x(i-1,j,k));\n\treturn 0.5*(grad_x(i+1,j,k) - grad_x(i-1,j,k));\n}\n\ndouble ImageMap::hessian_yy(int i, int j, int k)\n{\n\tint ny = m_img.height();\n\tif (ny <= 2) return 0.0;\n\tif (j==0   ) return (grad_y(i,j+1,k) - grad_y(i  ,j,k));\n\tif (j==ny-1) return (grad_y(i  ,j,k) - grad_y(i,j-1,k));\n\treturn 0.5*(grad_y(i,j+1,k) - grad_y(i,j-1,k));\n}\n\ndouble ImageMap::hessian_zz(int i, int j, int k)\n{\n\tint nz = m_img.depth();\n\tif (nz <= 2) return 0.0;\n\tif (k==0   ) return (grad_z(i,j,k+1) - grad_z(i  ,j,k));\n\tif (k==nz-1) return (grad_z(i  ,j,k) - grad_z(i,j,k-1));\n\treturn 0.5*(grad_z(i,j,k+1) - grad_z(i,j,k-1));\n}\n\ndouble ImageMap::hessian_xy(int i, int j, int k)\n{\n\tint nx = m_img.width();\n\tif (nx <= 2) return 0.0;\n\tif (i==0   ) return (grad_y(i+1,j,k) - grad_y(i  ,j,k));\n\tif (i==nx-1) return (grad_y(i  ,j,k) - grad_y(i-1,j,k));\n\treturn 0.5*(grad_y(i+1,j,k) - grad_y(i-1,j,k));\n}\n\ndouble ImageMap::hessian_yz(int i, int j, int k)\n{\n\tint ny = m_img.height();\n\tif (ny <= 2) return 0.0;\n\tif (j==0   ) return (grad_z(i,j+1,k) - grad_z(i  ,j,k));\n\tif (j==ny-1) return (grad_z(i  ,j,k) - grad_z(i,j-1,k));\n\treturn 0.5*(grad_z(i,j+1,k) - grad_z(i,j-1,k));\n}\n\ndouble ImageMap::hessian_xz(int i, int j, int k)\n{\n\tint nx = m_img.width();\n\tif (nx <= 2) return 0.0;\n\tif (i==0   ) return (grad_z(i+1,j,k) - grad_z(i  ,j,k));\n\tif (i==nx-1) return (grad_z(i  ,j,k) - grad_z(i-1,j,k));\n\treturn 0.5*(grad_z(i+1,j,k) - grad_z(i-1,j,k));\n}\n"
  },
  {
    "path": "FEImgLib/ImageMap.h",
    "content": "#pragma once\n#include \"Image.h\"\n#include <FECore/vec3d.h>\n#include <FECore/mat3d.h>\n#include \"feimglib_api.h\"\n\nclass FEIMGLIB_API ImageMap\n{\npublic:\n\tstruct POINT\n\t{\n\t\tint\t\ti, j, k;\n\t\tdouble\th[8];\n\t};\n\npublic:\n\tImageMap(Image& img);\n\t~ImageMap(void);\n\n\tvoid SetRange(vec3d r0, vec3d r1);\n\n\t// map a vector to the image domain\n\tPOINT map(const vec3d& p);\n\n\t// evaluate image\n\tdouble value(const POINT& p);\n\tdouble value(const vec3d& r) { return value(map(r)); }\n\t\n\t// determine if a point is in bounds\n\tbool valid(const vec3d& p);\n\n\t// image gradient\n\tvec3d gradient(const vec3d& r);\n\n\t// image hessian\n\tmat3ds hessian(const vec3d& r);\n\n\t// pixel dimensions\n\tdouble dx() { return (m_r1.x - m_r0.x)/(double) (m_img.width () - 1); }\n\tdouble dy() { return (m_r1.y - m_r0.y)/(double) (m_img.height() - 1); }\n\tdouble dz() { int nz = m_img.depth(); if (nz == 1) return 1.0; else return (m_r1.z - m_r0.z)/(double) (m_img.depth () - 1); }\n\n    Image& GetImage() { return m_img; }\n\nprotected:\n\tdouble grad_x(int i, int j, int k);\n\tdouble grad_y(int i, int j, int k);\n\tdouble grad_z(int i, int j, int k);\n\n\tdouble hessian_xx(int i, int j, int k);\n\tdouble hessian_yy(int i, int j, int k);\n\tdouble hessian_zz(int i, int j, int k);\n\tdouble hessian_xy(int i, int j, int k);\n\tdouble hessian_yz(int i, int j, int k);\n\tdouble hessian_xz(int i, int j, int k);\n\nprotected:\n\tImage&\tm_img;\n\tvec3d\tm_r0;\n\tvec3d\tm_r1;\t\n};\n"
  },
  {
    "path": "FEImgLib/feimglib_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef feimglib_EXPORTS\n\t\t\t#define FEIMGLIB_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define FEIMGLIB_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define FEIMGLIB_API\n\t#endif\n#else\n\t#define FEIMGLIB_API\n#endif\n"
  },
  {
    "path": "FEImgLib/fft.cpp",
    "content": "#ifdef HAVE_MKL\n#include <mkl.h>\n#include <complex>\n\n//---------------------------------------------------------------------------------------\n// calculate the DFT of a source image x. The source image is assumed to be stored\n// in row-major order, indexed by (x,y), where x is the column index, and y the row index. \n// The complex Fourier coefficients are returned in c. The buffer must be pre-allocated and must have\n// the size (nx,ny), the same as the source image.\nbool mkl_dft2(int nx, int ny, float* x, MKL_Complex8* c)\n{\n\tDFTI_DESCRIPTOR_HANDLE my_desc_handle;\n\tMKL_LONG status;\n\n\tMKL_LONG sizes[2] = { nx, ny };\n\tMKL_LONG is[3] = { 0, 1, nx };\n\tMKL_LONG os[3] = { 0, 1, nx }; // { 0, 1, N2/2+1}\n\tstatus = DftiCreateDescriptor(&my_desc_handle, DFTI_SINGLE, DFTI_REAL, 2, sizes);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_INPUT_STRIDES, is);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_OUTPUT_STRIDES, os);\n\tstatus = DftiCommitDescriptor(my_desc_handle);\n\tstatus = DftiComputeForward(my_desc_handle, x, c);\n\tstatus = DftiFreeDescriptor(&my_desc_handle);\n\n\treturn true;\n}\n\n//---------------------------------------------------------------------------------------\n// calculate the inverse DFT. The fourier coefficients are passed and assumed to be stored\n// in row-major order, indexed by (x,y), where x is the column index, and y the row index. \n// The reconstructed image is returned in x, which must have the same size and storage. \nbool mkl_idft2(int nx, int ny, MKL_Complex8* c, float* x)\n{\n\tDFTI_DESCRIPTOR_HANDLE my_desc_handle;\n\tMKL_LONG status;\n\n\tMKL_LONG sizes[2] = { nx, ny };\n\tMKL_LONG is[3] = { 0, 1, nx };  // { 0, 1, Nx/2+1}\n\tMKL_LONG os[3] = { 0, 1, nx };\n\tstatus = DftiCreateDescriptor(&my_desc_handle, DFTI_SINGLE, DFTI_REAL, 2, sizes);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_INPUT_STRIDES, is);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_OUTPUT_STRIDES, os);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_BACKWARD_SCALE, 1.f / ((float)nx * (float)ny));\n\tstatus = DftiCommitDescriptor(my_desc_handle);\n\tstatus = DftiComputeBackward(my_desc_handle, c, x);\n\tstatus = DftiFreeDescriptor(&my_desc_handle);\n\n\treturn true;\n}\n\n//---------------------------------------------------------------------------------------\n// calculate the DFT of a source image x. The source image is assumed to be stored\n// in row-major order, indexed by (x,y,z), where x is the column index, y the row index, z is the plane index. \n// The complex Fourier coefficients are returned in c. The buffer must be pre-allocated and must have\n// the size (nx,ny,nz), the same as the source image.\nbool mkl_dft3(int nx, int ny, int nz, float* x, MKL_Complex8* c)\n{\n\tDFTI_DESCRIPTOR_HANDLE my_desc_handle;\n\tMKL_LONG status;\n\n\tMKL_LONG sizes[3] = { nx, ny, nz };\n\tMKL_LONG is[4] = { 0, 1, nx, nx*ny };\n\tMKL_LONG os[4] = { 0, 1, nx, nx*ny }; // { 0, 1, nx, ny, nz/2+1}\n\tstatus = DftiCreateDescriptor(&my_desc_handle, DFTI_SINGLE, DFTI_REAL, 3, sizes);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_INPUT_STRIDES, is);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_OUTPUT_STRIDES, os);\n\tstatus = DftiCommitDescriptor(my_desc_handle);\n\tstatus = DftiComputeForward(my_desc_handle, x, c);\n\tstatus = DftiFreeDescriptor(&my_desc_handle);\n\n\treturn true;\n}\n\n//---------------------------------------------------------------------------------------\n// calculate the inverse DFT. The fourier coefficients are passed and assumed to be stored\n// in row-major order, indexed by (x,y,z), where x is the column index, y the row index, and z the plane index. \n// The reconstructed image is returned in y, which must have the same size and storage. \nbool mkl_idft3(int nx, int ny, int nz, MKL_Complex8* c, float* y)\n{\n\tDFTI_DESCRIPTOR_HANDLE my_desc_handle;\n\tMKL_LONG status;\n\n\tMKL_LONG sizes[3] = { nx, ny, nz };\n\tMKL_LONG is[4] = { 0, 1, nx, nx * ny };  // { 0, 1, nx, ny, nz/2+1}\n\tMKL_LONG os[4] = { 0, 1, nx, nx * ny };\n\tstatus = DftiCreateDescriptor(&my_desc_handle, DFTI_SINGLE, DFTI_REAL, 3, sizes);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_INPUT_STRIDES, is);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_OUTPUT_STRIDES, os);\n\tstatus = DftiSetValue(my_desc_handle, DFTI_BACKWARD_SCALE, 1.f / ((float)nx * (float)ny * (float) nz));\n\tstatus = DftiCommitDescriptor(my_desc_handle);\n\tstatus = DftiComputeBackward(my_desc_handle, c, y);\n\tstatus = DftiFreeDescriptor(&my_desc_handle);\n\n\treturn true;\n}\n\n#endif\n"
  },
  {
    "path": "FEImgLib/image_tools.cpp",
    "content": "#include \"image_tools.h\"\n#include \"Image.h\"\n#include <math.h>\n#include <iostream>\n\n#ifdef HAVE_MKL\n#include <mkl.h>\n#endif\n\n#ifdef HAVE_FFTW\n#include <fftw3.h>\n#endif // HAVE_FFTW\n\nvoid blur_avg_2d(Image& trg, Image& src, float d)\n{\n\tif (d <= 0) { trg = src; return; }\n\n\tint n = (int)d;\n\tfloat w = d - (float)n;\n\n\tint nx = src.width();\n\tint ny = src.height();\n\tint nz = src.depth();\n\n\ttrg = src;\n\tImage tmp(src);\n\tfloat f[4];\n\tfor (int l = 0; l < n; ++l)\n\t{\n#ifdef NDEBUG\n\t\t#pragma omp parallel for\n#endif\n\t\tfor (int k = 0; k < nz; ++k)\n\t\t\tfor (int j = 0; j < ny; ++j)\n\t\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t\t{\n\t\t\t\t\tif (i > 0) f[0] = tmp.value(i - 1, j, k); else f[0] = tmp.value(i, j, k);\n\t\t\t\t\tif (i < nx - 1) f[1] = tmp.value(i + 1, j, k); else f[1] = tmp.value(i, j, k);\n\t\t\t\t\tif (j > 0) f[2] = tmp.value(i, j - 1, k); else f[2] = tmp.value(i, j, k);\n\t\t\t\t\tif (j < ny - 1) f[3] = tmp.value(i, j + 1, k); else f[3] = tmp.value(i, j, k);\n\t\t\t\t\ttrg.value(i, j, k) = 0.25f * (f[0] + f[1] + f[2] + f[3]);\n\t\t\t\t}\n\t\ttmp = trg;\n\t}\n\n\tif (w > 0.0)\n\t{\n#ifdef NDEBUG\n\t\t#pragma omp parallel for\n#endif\n\t\tfor (int k = 0; k < nz; ++k)\n\t\t\tfor (int j = 0; j < ny; ++j)\n\t\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t\t{\n\t\t\t\t\tif (i > 0) f[0] = tmp.value(i - 1, j, k); else f[0] = tmp.value(i, j, k);\n\t\t\t\t\tif (i < nx - 1) f[1] = tmp.value(i + 1, j, k); else f[1] = tmp.value(i, j, k);\n\t\t\t\t\tif (j > 0) f[2] = tmp.value(i, j - 1, k); else f[2] = tmp.value(i, j, k);\n\t\t\t\t\tif (j < ny - 1) f[3] = tmp.value(i, j + 1, k); else f[3] = tmp.value(i, j, k);\n\t\t\t\t\tfloat f1 = 0.25f * (f[0] + f[1] + f[2] + f[3]);\n\t\t\t\t\tfloat f2 = trg.value(i, j, k);\n\t\t\t\t\ttrg.value(i, j, k) = f1 * w + f2 * (1.f - w);\n\t\t\t\t}\n\t}\n}\n\nvoid blur_avg_3d(Image& trg, Image& src, float d)\n{\n\tif (d <= 0) { trg = src; return; }\n\n\tint n = (int)d;\n\tfloat w = d - (float)n;\n\n\tint nx = src.width();\n\tint ny = src.height();\n\tint nz = src.depth();\n\n\ttrg = src;\n\tImage tmp(src);\n\tfloat f[6];\n\tfor (int l = 0; l < n; ++l)\n\t{\n#ifdef NDEBUG\n\t\t#pragma omp parallel for\n#endif\n\t\tfor (int k = 0; k < nz; ++k)\n\t\t\tfor (int j = 0; j < ny; ++j)\n\t\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t\t{\n\t\t\t\t\tif (i > 0) f[0] = tmp.value(i - 1, j, k); else f[0] = tmp.value(i, j, k);\n\t\t\t\t\tif (i < nx - 1) f[1] = tmp.value(i + 1, j, k); else f[1] = tmp.value(i, j, k);\n\t\t\t\t\tif (j > 0) f[2] = tmp.value(i, j - 1, k); else f[2] = tmp.value(i, j, k);\n\t\t\t\t\tif (j < ny - 1) f[3] = tmp.value(i, j + 1, k); else f[3] = tmp.value(i, j, k);\n\t\t\t\t\tif (k > 0) f[4] = tmp.value(i, j, k - 1); else f[4] = tmp.value(i, j, k);\n\t\t\t\t\tif (k < nz - 1) f[5] = tmp.value(i, j, k + 1); else f[5] = tmp.value(i, j, k);\n\t\t\t\t\ttrg.value(i, j, k) = 0.1666667f * (f[0] + f[1] + f[2] + f[3] + f[4] + f[5]);\n\t\t\t\t}\n\t\ttmp = trg;\n\t}\n\n\tif (w > 0.0)\n\t{\n#ifdef NDEBUG\n\t\t#pragma omp parallel for\n#endif\n\t\tfor (int k = 0; k < nz; ++k)\n\t\t\tfor (int j = 0; j < ny; ++j)\n\t\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t\t{\n\t\t\t\t\tif (i > 0) f[0] = tmp.value(i - 1, j, k); else f[0] = tmp.value(i, j, k);\n\t\t\t\t\tif (i < nx - 1) f[1] = tmp.value(i + 1, j, k); else f[1] = tmp.value(i, j, k);\n\t\t\t\t\tif (j > 0) f[2] = tmp.value(i, j - 1, k); else f[2] = tmp.value(i, j, k);\n\t\t\t\t\tif (j < ny - 1) f[3] = tmp.value(i, j + 1, k); else f[3] = tmp.value(i, j, k);\n\t\t\t\t\tif (k > 0) f[4] = tmp.value(i, j, k - 1); else f[4] = tmp.value(i, j, k);\n\t\t\t\t\tif (k < nz - 1) f[5] = tmp.value(i, j, k + 1); else f[5] = tmp.value(i, j, k);\n\t\t\t\t\tfloat f1 = 0.1666667f * (f[0] + f[1] + f[2] + f[3] + f[4] + f[5]);\n\t\t\t\t\tfloat f2 = trg.value(i, j, k);\n\t\t\t\t\ttrg.value(i, j, k) = f1 * w + f2 * (1.f - w);\n\t\t\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// FFT-based blurs (rely on MKL library. Incompatible with pardiso/dss)\n#ifdef HAVE_MKL\n\n// in fft.cpp\nbool mkl_dft2(int nx, int ny, float* x, MKL_Complex8* c);\nbool mkl_idft2(int nx, int ny, MKL_Complex8* c, float* y);\n\nFEIMGLIB_API void mkl_blur_2d(Image& trg, Image& src, float d)\n{\n\tint nx = src.width();\n\tint ny = src.height();\n\n\tfloat* x = src.data();\n\tfloat* y = trg.data();\n\n\t// for zero blur radius, we just copy the image\n\tif (d <= 0.f)\n\t{\n\t\tfor (int i = 0; i < nx * ny; ++i) y[i] = x[i];\n\t\treturn;\n\t}\n\n\t// since the blurring is done in Fourier space,\n\t// we need to invert the blur radius\n\tfloat sigmax = nx / d;\n\tfloat sigmay = ny / d;\n\n\t// calculate the DFT\n\tMKL_Complex8* c = new MKL_Complex8[nx * ny];\n\tmkl_dft2(nx, ny, x, c);\n\n\t// multiply the DFT with blur mask\n\tfor (int j = 0; j <= ny / 2; ++j)\n\t\tfor (int i = 0; i < nx; ++i)\n\t\t{\n\t\t\tdouble wx = (i < nx / 2 ? i : i - nx) / sigmax;\n\t\t\tdouble wy = j / sigmay;\n\t\t\tfloat v = (float)exp(-(wx * wx + wy * wy));\n\n\t\t\tc[j * nx + i].real *= v;\n\t\t\tc[j * nx + i].imag *= v;\n\t\t}\n\n\t// calculate the inverse DFT\n\tmkl_idft2(nx, ny, c, y);\n\n\t// clean up\n\tdelete[] c;\n}\n\n// in fft.cpp\nbool mkl_dft3(int nx, int ny, int nz, float* x, MKL_Complex8* c);\nbool mkl_idft3(int nx, int ny, int nz, MKL_Complex8* c, float* y);\nFEIMGLIB_API void mkl_blur_3d(Image& trg, Image& src, float d)\n{\n\tint nx = src.width();\n\tint ny = src.height();\n\tint nz = src.depth();\n\n\tfloat* x = src.data();\n\tfloat* y = trg.data();\n\n\t// for zero blur radius, we just copy the image\n\t// SL: Why not copy i.e. if (d <= 0) { trg = src; return; }?\n\tif (d <= 0.f)\n\t{\n\t\tfor (int i = 0; i < nx * ny * nz; ++i) y[i] = x[i];\n\t\treturn;\n\t}\n\n\t// since the blurring is done in Fourier space,\n\t// we need to invert the blur radius\n\tfloat sigmax = nx / d;\n\tfloat sigmay = ny / d;\n\tfloat sigmaz = nz / d;\n\n\t// calculate the DFT\n\tMKL_Complex8* c = new MKL_Complex8[nx * ny * nz];\n\tmkl_dft3(nx, ny, nz, x, c);\n\n\t// multiply the DFT with blur mask\n\tfor (int k = 0; k <= nz / 2; ++k)\n\t\tfor (int j = 0; j < ny; ++j)\n\t\t\tfor (int i = 0; i < nx; ++i)\n\t\t\t{\n\t\t\t\tdouble wx = (i < nx / 2 ? i : i - nx) / sigmax;\n\t\t\t\tdouble wy = (j < ny / 2 ? j : j - ny) / sigmay;\n\t\t\t\tdouble wz = k / sigmaz;\n\t\t\t\tfloat v = (float)exp(-(wx * wx + wy * wy + wz * wz));\n\n\t\t\t\tc[k * nx * ny + j * nx + i].real *= v;\n\t\t\t\tc[k * nx * ny + j * nx + i].imag *= v;\n\t\t\t}\n\n\t// calculate the inverse DFT\n\tmkl_idft3(nx, ny, nz, c, y);\n\n\t// clean up\n\tdelete[] c;\n}\n#else // HAVE_MKL\nvoid mkl_blur_2d(Image& trg, Image& src, float d) {}\nvoid mkl_blur_3d(Image& trg, Image& src, float d) {}\n#endif // HAVE_MKL\n\n//-----------------------------------------------------------------------------\n// FFT-based blurs (rely on FFTW library)\n#ifdef HAVE_FFTW\nvoid create_gaussian_2d(double* kernel, int rows, int cols, float sigma[2]) {\n\tdouble sum = 0.0;\n\tfor (int y = 0; y < rows; ++y) {\n\t\tint dy = (y <= rows / 2) ? y : (rows - y);  // wrap-around distance\n\t\tfor (int x = 0; x < cols; ++x) {\n\t\t\tint dx = (x <= cols / 2) ? x : (cols - x);\n\t\t\tdouble gx = (double)(dx * dx / (sigma[0] * sigma[0]));\n\t\t\tdouble gy = (double)(dy * dy / (sigma[1] * sigma[1]));\n\t\t\tdouble g = exp(-0.5 * (gx + gy));\n\t\t\tkernel[y * cols + x] = g;\n\t\t\tsum += g;\n\t\t}\n\t}\n\t// Normalize\n\tfor (int i = 0; i < rows * cols; ++i) {\n\t\tkernel[i] /= sum;\n\t}\n}\n\n// Create 3D Gaussian kernel (centered)\nvoid create_gaussian_3d(double* kernel, int nz, int ny, int nx, float sigma[3]) {\n\tdouble sum = 0.0;\n\tfor (int z = 0; z < nz; ++z) {\n\t\tint dz = (z <= nz / 2) ? z : (nz - z);  // wrap-around distance\n\t\tfor (int y = 0; y < ny; ++y) {\n\t\t\tint dy = (y <= ny / 2) ? y : (ny - y);\n\t\t\tfor (int x = 0; x < nx; ++x) {\n\t\t\t\tint dx = (x <= nx / 2) ? x : (nx - x);\n\t\t\t\tdouble gx = double(dx * dx / (sigma[0] * sigma[0]));\n\t\t\t\tdouble gy = double(dy * dy / (sigma[1] * sigma[1]));\n\t\t\t\tdouble gz = double(dz * dz / (sigma[2] * sigma[2]));\n\t\t\t\tdouble g = exp(-0.5 * (gx + gy + gz));\n\t\t\t\tkernel[(z * ny + y) * nx + x] = g;\n\t\t\t\tsum += g;\n\t\t\t}\n\t\t}\n\t}\n\t// Normalize\n\tfor (int i = 0; i < nx * ny * nz; ++i) {\n\t\tkernel[i] /= sum;\n\t}\n}\n\nvoid fftw_blur_2d(Image& trg, Image& src, float sigma[2])\n{\n\tint width = src.width();\n\tint height = src.height();\n\n\tfloat* img_data = src.data();\n\n\t// Allocate FFTW arrays\n\tdouble* img_spatial = (double*) fftw_malloc(sizeof(double) * width * height);\n\tdouble* kernel_spatial = (double*) fftw_malloc(sizeof(double) * width * height);\n\tfftw_complex* img_freq = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * height * (width / 2 + 1));\n\tfftw_complex* kernel_freq = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * height * (width / 2 + 1));\n\n\t// Copy image to double buffer\n\tfor (int i = 0; i < width * height; ++i) {\n\t\timg_spatial[i] = (double)img_data[i];\n\t}\n\n\t// Create Gaussian kernel\n\tcreate_gaussian_2d(kernel_spatial, height, width, sigma);\n\n\t// Create FFT plans\n\tfftw_plan plan_fwd_img = fftw_plan_dft_r2c_2d(height, width, img_spatial, img_freq, FFTW_ESTIMATE);\n\tfftw_plan plan_fwd_kernel = fftw_plan_dft_r2c_2d(height, width, kernel_spatial, kernel_freq, FFTW_ESTIMATE);\n\tfftw_plan plan_inv = fftw_plan_dft_c2r_2d(height, width, img_freq, img_spatial, FFTW_ESTIMATE);\n\n\t// Forward FFTs\n\tfftw_execute(plan_fwd_img);\n\tfftw_execute(plan_fwd_kernel);\n\n\t// Multiply in frequency domain\n\tint nfreq = height * (width / 2 + 1);\n\tfor (int i = 0; i < nfreq; ++i) {\n\t\tdouble a = img_freq[i][0], b = img_freq[i][1];\n\t\tdouble c = kernel_freq[i][0], d = kernel_freq[i][1];\n\t\timg_freq[i][0] = a * c - b * d;\n\t\timg_freq[i][1] = a * d + b * c;\n\t}\n\n\t// Inverse FFT\n\tfftw_execute(plan_inv);\n\n\t// Normalize and convert back to float\n\tfloat* out_data = trg.data();\n\tfor (int i = 0; i < width * height; ++i) {\n\t\tdouble val = img_spatial[i] / (width * height);\n\t\tout_data[i] = (float)(val);\n\t}\n\n\t// Cleanup\n\tfftw_destroy_plan(plan_fwd_img);\n\tfftw_destroy_plan(plan_fwd_kernel);\n\tfftw_destroy_plan(plan_inv);\n\tfftw_free(img_spatial);\n\tfftw_free(kernel_spatial);\n\tfftw_free(img_freq);\n\tfftw_free(kernel_freq);\n}\n\nvoid fftw_blur_3d(Image& trg, Image& src, float sigma[3])\n{\n\tint nx = src.width();\n\tint ny = src.height();\n\tint nz = src.depth();\n\n\tfloat* img_data = src.data();\n\n\t// Allocate FFTW buffers\n\tdouble* img_spatial = (double*)fftw_malloc(sizeof(double) * nx * ny * nz);\n\tdouble* kernel_spatial = (double*)fftw_malloc(sizeof(double) * nx * ny * nz);\n\tfftw_complex* img_freq = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * nz * ny * (nx / 2 + 1));\n\tfftw_complex* kernel_freq = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * nz * ny * (nx / 2 + 1));\n\n\t// Copy image to double buffer\n\tfor (int i = 0; i < nx*ny*nz; ++i) {\n\t\timg_spatial[i] = (double)img_data[i];\n\t}\n\n\t// Create Gaussian kernel\n\tcreate_gaussian_3d(kernel_spatial, nz, ny, nx, sigma);\n\n\t// Plans\n\tfftw_plan plan_fwd_img = fftw_plan_dft_r2c_3d(nz, ny, nx, img_spatial, img_freq, FFTW_ESTIMATE);\n\tfftw_plan plan_fwd_kernel = fftw_plan_dft_r2c_3d(nz, ny, nx, kernel_spatial, kernel_freq, FFTW_ESTIMATE);\n\tfftw_plan plan_inv = fftw_plan_dft_c2r_3d(nz, ny, nx, img_freq, img_spatial, FFTW_ESTIMATE);\n\n\t// Forward FFTs\n\tfftw_execute(plan_fwd_img);\n\tfftw_execute(plan_fwd_kernel);\n\n\t// Multiply in frequency domain\n\tint nfreq = nz * ny * (nx / 2 + 1);\n\tfor (int i = 0; i < nfreq; ++i) \n\t{\n\t\tdouble a = img_freq[i][0]; // real part\n\t\tdouble b = img_freq[i][1]; // complex part\n\t\tdouble c = kernel_freq[i][0]; // real part\n\t\tdouble d = kernel_freq[i][1]; // complex part\n\t\timg_freq[i][0] = a * c - b * d; // real part\n\t\timg_freq[i][1] = a * d + b * c; // complex part\n\t}\n\n\t// Inverse FFT\n\tfftw_execute(plan_inv);\n\n\t// Normalize and convert back to float\n\tfloat* out_data = trg.data();\n\tdouble norm_factor = (double)(nx * ny * nz);\n\tfor (int i = 0; i < nx * ny * nz; ++i) {\n\t\tdouble val = img_spatial[i] / norm_factor;\n\t\tout_data[i] = (float)(val);\n\t}\n\n\t// Cleanup\n\tfftw_destroy_plan(plan_fwd_img);\n\tfftw_destroy_plan(plan_fwd_kernel);\n\tfftw_destroy_plan(plan_inv);\n\tfftw_free(img_spatial);\n\tfftw_free(kernel_spatial);\n\tfftw_free(img_freq);\n\tfftw_free(kernel_freq);\n}\n\n#else\nvoid fftw_blur_2d(Image& trg, Image& src, float sigma[2]) {}\nvoid fftw_blur_3d(Image& trg, Image& src, float sigma[3]) {}\n#endif // HAVE_FFTW\n\nvoid blur_image_2d(Image& trg, Image& src, float d, BlurMethod blurMethod)\n{\n\tswitch (blurMethod)\n\t{\n\tcase BlurMethod::BLUR_AVERAGE: blur_avg_2d(trg, src, d); break;\n\tcase BlurMethod::BLUR_FFT:\n\t{\n#ifdef HAVE_FFTW\n\t\t// currently this function will assume the voxels are isotropic\n\t\tfloat d2[2] = { d, d };\n\t\tfftw_blur_2d(trg, src, d2); break;\n#elif HAVE_MKL\n\t\tmkl_blur_2d(trg, src, d); break;\n#endif\n\t}\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid blur_image_3d(Image& trg, Image& src, float d, BlurMethod blurMethod)\n{\n\tswitch (blurMethod)\n\t{\n\tcase BlurMethod::BLUR_AVERAGE: blur_avg_3d(trg, src, d); break;\n\tcase BlurMethod::BLUR_FFT:\n\t{\n#ifdef HAVE_FFTW\n\t\t// currently this function will assume the voxels are isotropic\n\t\tfloat d3[3] = { d, d, d };\n\t\tfftw_blur_3d(trg, src, d3); break;\n#elif HAVE_MKL\n\t\tmkl_blur_3d(trg, src, d); break;\n#endif\n\t}\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// Extension functions for image filter edges\n// return integer for constant border extension\nint constant_extension(int n, int N)\n{\n\tif (n < 0)\n\t\tn = 0;\n\telse if (n >= N)\n\t\tn = N - 1;\n\treturn n;\n}\n\n// return integer for symmetric border extension\nint symmetric_extension(int n, int N)\n{\n\twhile (1)\n\t{\n\t\tif (n < 0)\n\t\t\tn = -1 - n;\n\t\telse if (n >= N)\n\t\t\tn = (2 * N) - 1 - n;\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn n;\n}"
  },
  {
    "path": "FEImgLib/image_tools.h",
    "content": "#pragma once\n#include <FECore/FECoreClass.h>\n#include \"feimglib_api.h\"\n\nclass Image;\n\n// iterative stencil-based blur\nenum class BlurMethod\n{\n\tBLUR_AVERAGE,\n\tBLUR_FFT,\n};\n\nFEIMGLIB_API void blur_image_2d(Image& trg, Image& src, float d, BlurMethod blurMethod = BlurMethod::BLUR_AVERAGE);\nFEIMGLIB_API void blur_image_3d(Image& trg, Image& src, float d, BlurMethod blurMethod = BlurMethod::BLUR_AVERAGE);\n\n\n// FFT-based blurs (rely on MKL library. Incompatible with pardiso/dss)\nFEIMGLIB_API void fftblur_2d(Image& trg, Image& src, float d);\nFEIMGLIB_API void fftblur_3d(Image& trg, Image& src, float d);\n\n// FFT-based blurs (rely on FFTW library)\nFEIMGLIB_API void fftw_blur_2d(Image& trg, Image& src, float sigma[2]);\nFEIMGLIB_API void fftw_blur_3d(Image& trg, Image& src, float sigma[3]);\n\n// Extension functions for image filter edges\nFEIMGLIB_API int constant_extension(int N, int n);\nFEIMGLIB_API int symmetric_extension(int N, int n);\n"
  },
  {
    "path": "FindDependencies.cmake",
    "content": "# MKL - On Unix the compilervars.sh should be run to find the MKL libraries.\nif(DEFINED ENV{MKLROOT})\n    set(MKLROOT $ENV{MKLROOT} CACHE PATH \"MKL root directory\")\nelse()\n    if(WIN32)\n        set(MKLPATHS $ENV{ProgramFiles\\(x86\\)}\\\\IntelSWTools $ENV{PROGRAMFILES}\\\\Intel* $ENV{ProgramFiles\\(x86\\)}\\\\Intel $ENV{SystemDrive} $ENV{SystemDrive}\\\\Intel*)\n        set(MKLSUFFIXES \"compilers_and_libraries/windows\" \"oneapi\")\n    elseif(APPLE)\n        set(MKLPATHS /opt/intel /intel /usr/local/intel /usr/local/opt/intel)\n        set(MKLSUFFIXES \"compilers_and_libraries/mac\" \"oneapi\")\n    else()\n        set(MKLPATHS /opt/intel /intel /usr/local/intel /usr/local/opt/intel $ENV{HOME}/intel $ENV{HOME}/*/intel)\n        set(MKLSUFFIXES \"compilers_and_libraries/linux\" \"oneapi\")\n    endif()\n    \n    find_file(MKLROOT mkl\n\t\tPATHS ${MKLPATHS}\n\t\tPATH_SUFFIXES ${MKLSUFFIXES}\n\t\tDOC \"MKL root directory\")\nendif()\n\nif(MKLROOT)\n    if(${MKLROOT} MATCHES \"oneapi\" OR ${MKLROOT} MATCHES \"oneAPI\")\n        find_path(MKL_INC mkl.h\n            PATHS\n              ${MKLROOT}/latest/include\n              ${MKLROOT}/include\n              ${MKLROOT}/mkl/latest/include\n            DOC \"MKL include directory\")\n        \n        find_library(MKL_CORE\n            NAMES mkl_core mkl_core.lib\n            PATHS\n              ${MKLROOT}/latest/lib\n              ${MKLROOT}/lib\n              ${MKLROOT}/mkl/latest/lib\n            PATH_SUFFIXES \"intel64\"\n            NO_DEFAULT_PATH)\n            \n        find_library(MKL_OMP_LIB\n            NAMES iomp5 iomp5md libiomp5md.lib\n            PATHS\n              ${MKLROOT}/../compiler/latest/*/compiler/lib/\n              ${MKLROOT}/../compiler/latest/lib/\n              ${MKLROOT}/../../compiler/latest/*/compiler/lib/\n              ${MKLROOT}/../../compiler/latest/lib/\n              ${MKLROOT}/compiler/latest/lib/\n            PATH_SUFFIXES \"intel64\" \"mac\"\n            NO_DEFAULT_PATH\n            DOC \"MKL OMP Library\")\n            \n    else()\n        find_path(MKL_INC mkl.h \n            PATHS\n              ${MKLROOT}/latest/include\n              ${MKLROOT}/include\n            DOC \"MKL include directory\")\n            \n        find_library(MKL_CORE mkl_core\n            PATHS\n              ${MKLROOT}/latest/lib\n              ${MKLROOT}/lib\n            PATH_SUFFIXES \"intel64\"\n            NO_DEFAULT_PATH)\n            \n        find_library(MKL_OMP_LIB \n            NAMES iomp5 iomp5md libiomp5md.lib\n\n            PATHS ${MKLROOT}/lib\n                  ${MKLROOT}/../lib\n                  ${MKLROOT}/../compiler/lib\n                  ${MKLROOT}/../../windows/compiler/lib/\n                  ${MKLROOT}/../../compiler/latest/*/compiler/lib/\n            PATH_SUFFIXES \"intel64\"\n            NO_DEFAULT_PATH\n            DOC \"MKL OMP Library\")\n    \n    endif()\n        \n    if(MKL_CORE)\n        get_filename_component(MKL_TEMP ${MKL_CORE} DIRECTORY)\n        set(MKL_LIB_DIR ${MKL_TEMP} CACHE PATH \"MKL Library directory\")\n        unset(MKL_TEMP)\n        unset(MKL_CORE CACHE)\n    else()\n        set(MKL_LIB_DIR \"MKL_LIB_DIR-NOTFOUND\" CACHE PATH \"MKL Library directory\")\n        unset(MKL_CORE CACHE)\n    endif()\n\t\nendif()\n\nif(NOT WIN32)\n    # OpenMP\n    find_package(OpenMP QUIET)\nendif()\n\nif(MKL_INC AND MKL_LIB_DIR AND MKL_OMP_LIB)\n\toption(USE_MKL \"Required for pardiso and iterative solvers\" ON)\n    mark_as_advanced(MKL_INC MKL_LIB_DIR MKL_OMP_LIB)\n    set(OpenMP_C_FOUND true)\nelse()\n\toption(USE_MKL \"Required for pardiso and iterative solvers\" OFF)\nendif()\n\n# FFTW\nif(WIN32)\n\tfind_path(FFTW_INC fftw3.h\n      PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n      PATH_SUFFIXES \"include\" \"fftw*\" \"include/fftw*\"\n      DOC \"FFTW include directory\")\n\tfind_library(FFTW_LIB fftw3 libfftw3-3\n      PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n      PATH_SUFFIXES \"vs2017/Release\"\n      DOC \"FFTW library path\")\nelse()\n\tfind_path(FFTW_INC fftw3.h\n      PATHS /usr/local/ /opt/fftw* $ENV{HOME}/* $ENV{HOME}/*/*\n      PATH_SUFFIXES \"include\" \"fftw*\" \"include/fftw*\"\n\t  DOC \"FFTW include directory\")\n\tfind_library(FFTW_LIB fftw3\n      PATHS /usr/local/ /opt/fftw* $ENV{HOME}/* $ENV{HOME}/*/*\n      PATH_SUFFIXES \"lib\" \"build\" \"cbuild\" \"cmbuild\"\n\t  DOC \"FFTW library path\")\nendif()\t\n\nif(FFTW_INC AND FFTW_LIB)\t\t\n\toption(USE_FFTW \"Required for FFTW functions\" ON)\n    mark_as_advanced(FFTW_INC FFTW_LIB)\nelse()\n\toption(USE_FFTW \"Required for FFTW functions\" OFF)\n    mark_as_advanced(CLEAR FFTW_INC FFTW_LIB)\nendif()\n\n# HYPRE\nif(WIN32)\n\tfind_path(HYPRE_INC HYPRE_IJ_mv.h\n        PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n        PATH_SUFFIXES \"include\" \"include/hypre\" \"src\" \"src/include\" \"src/hypre/include\"\n        DOC \"HYPRE include directory\")\n\n\tfind_library(HYPRE_LIB HYPRE \n        PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n        PATH_SUFFIXES \"src\" \"src/build\" \"src/mbuild\" \"/src/vs2017/Release\"\n\t\tDOC \"HYPRE library path\")\n\nelse()\n\tfind_path(HYPRE_INC HYPRE_IJ_mv.h\n        PATHS /opt/hypre* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"include\" \"include/hypre\" \"src\" \"src/include\" \"src/hypre/include\"\n\t\tDOC \"HYPRE include directory\")\n\tfind_library(HYPRE_LIB HYPRE \n        PATHS /opt/hypre* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"lib\" \"src\" \"src/build\" \"src/cbuild\"\n\t\tDOC \"HYPRE library path\")\nendif()\t\n\nif(HYPRE_INC AND HYPRE_LIB)\t\t\n\toption(USE_HYPRE \"Required for HYPRE solver\" ON)\n    mark_as_advanced(HYPRE_INC HYPRE_LIB)\nelse()\n\toption(USE_HYPRE \"Required for HYPRE solver\" OFF)\n    mark_as_advanced(CLEAR HYPRE_INC HYPRE_LIB)\nendif()\n\n# MMG\nif(WIN32)\n\tfind_path(MMG_INC mmg/mmg3d/libmmg3d.h\n        PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/* $ENV{HOMEPATH}/source/repos/*\n\t\tPATH_SUFFIXES \"include\" \"include/mmg*\" \"src\" \"build\" \"build/include\" \"cmbuild/include\"\n        DOC \"MMG include directory\")\n\tfind_library(MMG_LIB mmg3d \n        PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/* $ENV{HOMEPATH}/source/repos/*\n        PATH_SUFFIXES \"build/lib\" \"cmbuild/lib\" \"src/build/lib\" \"src/cmbuild/lib\" \"cmbuild/lib/Release\"\n\t\tDOC \"MMG library path\")\n    find_library(MMGS_LIB mmgs \n        PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/* $ENV{HOMEPATH}/source/repos/*\n        PATH_SUFFIXES \"build/lib\" \"cmbuild/lib\" \"src/build/lib\" \"src/cmbuild/lib\" \"cmbuild/lib/Release\"\n\t\tDOC \"MMGS library path\")\nelse()\n\tfind_path(MMG_INC mmg/mmg3d/libmmg3d.h\n        PATHS /opt/hypre* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"include\" \"include/mmg\" \"build\" \"build/include\" \"cbuild\" \"cbuild/include\" \"src\" \n\t\tDOC \"MMG include directory\")\n\tfind_library(MMG_LIB mmg3d \n        PATHS /opt/mmg* $ENV{HOME}/* $ENV{HOME}/*/* $ENV{HOME}/local/x86_64/lib\n        PATH_SUFFIXES \"lib\" \"build/lib\" \"cbuild/lib\" \"src/build/lib\" \"src/cbuild/lib\"\n\t\tDOC \"MMG library path\")\n    find_library(MMGS_LIB mmgs \n        PATHS /opt/mmg* $ENV{HOME}/* $ENV{HOME}/*/* $ENV{HOME}/local/x86_64/lib\n        PATH_SUFFIXES \"lib\" \"build/lib\" \"cbuild/lib\" \"src/build/lib\" \"src/cbuild/lib\"\n\t\tDOC \"MMGS library path\")\nendif()\t\n\nif(MMG_INC AND MMG_LIB AND MMGS_LIB)\t\t\n\toption(USE_MMG \"Required for MMG use\" ON)\n    mark_as_advanced(MMG_INC MMG_LIB MMGS_LIB)\nelse()\n\toption(USE_MMG \"Required for MMG use\" OFF)\n    mark_as_advanced(CLEAR MMG_INC MMG_LIB MMGS_LIB)\nendif()\n\n# LEVMAR\nif(WIN32)\n\tfind_path(LEVMAR_INC levmar.h PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n      PATH_SUFFIXES \"levmar\"\n      DOC \"Levmar include directory\")\n\tfind_library(LEVMAR_LIB levmar PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n      PATH_SUFFIXES \"vs2017/Release\"\n      DOC \"Levmar library path\")\nelse()\n\tfind_path(LEVMAR_INC levmar.h PATHS /usr/local/ /opt/levmar* $ENV{HOME}/* $ENV{HOME}/*/*\n      PATH_SUFFIXES \"include\" \"levmar\" \"include/levmar\"\n\t\tDOC \"Levmar include directory\")\n\tfind_library(LEVMAR_LIB levmar PATHS /usr/local/ /opt/levmar* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"lib\" \"build\" \"cbuild\" \"cmbuild\"\n\t\tDOC \"Levmar library path\")\nendif()\n\nif(LEVMAR_INC AND LEVMAR_LIB)\t\t\n\toption(USE_LEVMAR \"Required for optimization in FEBio\" ON)\n    mark_as_advanced(LEVMAR_INC LEVMAR_LIB)\nelse()\n\toption(USE_LEVMAR \"Required for optimization in FEBio\" OFF)\n    mark_as_advanced(CLEAR LEVMAR_INC LEVMAR_LIB)\nendif()\n\n# NLOPT\nif(WIN32)\n\tfind_path(NLOPT_INC nlopt.h PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/* \n      PATH_SUFFIXES \"nlopt\"\n      DOC \"NLOPT include directory\")\n\tfind_library(NLOPT_LIB nlopt PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n      PATH_SUFFIXES \"vs2017/Release\"\n      DOC \"NLOPT library path\")\nelse()\n\tfind_path(NLOPT_INC nlopt.h PATHS /usr/local/ /opt/nlopt* $ENV{HOME}/* $ENV{HOME}/*/*\n      PATH_SUFFIXES \"include\" \"nlopt\" \"include/nlopt\"\n\t\tDOC \"NLOPT include directory\")\n\tfind_library(NLOPT_LIB nlopt PATHS /usr/local/ /opt/nlopt* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"lib\" \"build\" \"cbuild\" \"cmbuild\"\n\t\tDOC \"NLOPT library path\")\nendif()\n\nif(NLOPT_INC AND NLOPT_LIB)\t\t\n\toption(USE_NLOPT \"Required for optimization in FEBio\" ON)\n    mark_as_advanced(NLOPT_INC NLOPT_LIB)\nelse()\n\toption(USE_NLOPT \"Required for optimization in FEBio\" OFF)\n    mark_as_advanced(CLEAR NLOPT_INC NLOPT_LIB)\nendif()\n\n\n\n# SuperLU_MT\nif (WIN32)\n    find_path(SUPERLU_MT_INC slu_mt_ddefs.h PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n    DOC \"SuperLU_MT include directory\")\n    find_library(SUPERLU_MT_LIB superlu PATHS C::/Program\\ Files/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n    DOC \"SuperLU_MT library path\")\nelse()\n    find_path(SUPERLU_MT_INC slu_mt_ddefs.h PATHS /usr/local/include/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n    DOC \"SuperLU_MT include directory\")\n    find_library(SUPERLU_MT_LIB superlu PATHS /usr/local/lib/* $ENV{HOMEPATH}/* $ENV{HOMEPATH}/*/*\n    DOC \"SuperLU_MT library path\")\nendif()\n\nif(SUPERLU_MT_INC AND SUPERLU_MT_LIB)\t\t\n\toption(USE_SUPERLU_MT \"Option for using SuperLU_MT\" ON)\n    mark_as_advanced(SUPERLU_MT_INC SUPERLU_MT_LIB)\nelse()\n\toption(USE_SUPERLU_MT \"Option for using SuperLU_MT\" OFF)\n    mark_as_advanced(CLEAR SUPERLU_MT_INC SUPERLU_MT_LIB)\nendif()\n\n# PDL\nif(WIN32)\n\tfind_library(PDL_LIB libpardiso600-WIN-X86-64* \n        PATHS C::/Program\\ Files/* $ENV{HOME}/* $ENV{HOME}/*/*\n\t\tDOC \"PDL library path\")\nelseif(APPLE)\n\tfind_library(PDL_LIB pardiso600-MACOS-X86-64 \n        PATHS /usr/local* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"lib\" \"pardiso/lib\" \"pardiso-project/lib\"\n\t\tDOC \"PDL library path\")\nelse()\n\tfind_library(PDL_LIB pardiso600-GNU*-X86-64 \n        PATHS /usr/local* $ENV{HOME}/* $ENV{HOME}/*/*\n        PATH_SUFFIXES \"lib\" \"pardiso/lib\" \"pardiso-project/lib\"\n\t\tDOC \"PDL library path\")\nendif()\t\n\nif(PDL_LIB)\t\t\n\toption(USE_PDL \"Required for pardiso-project use\" ON)\n    mark_as_advanced(PDL_LIB)\nelse()\n\toption(USE_PDL \"Required for pardiso-project use\" OFF)\n    mark_as_advanced(CLEAR PDL_LIB)\nendif()\n\n# ZLIB\ninclude(FindZLIB)\n\nif(ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY_RELEASE)\t\t\n\toption(USE_ZLIB \"Required for compressing xplt files\" ON)\n    mark_as_advanced(ZLIB_INCLUDE_DIR ZLIB_LIBRARY_RELEASE)\nelse()\n\toption(USE_ZLIB \"Required for compressing xplt files\" OFF)\n    mark_as_advanced(CLEAR ZLIB_INCLUDE_DIR ZLIB_LIBRARY_RELEASE)\nendif()\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in  the City of New York, and others\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "NumCore/AccelerateSparseSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2021 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"AccelerateSparseSolver.h\"\n#include \"MatrixTools.h\"\n#include <FECore/log.h>\n\n#ifdef HAS_ACCEL\n\nclass AccelerateSparseSolver::Implementation\n{\npublic:\n    FEModel*            m_fem;\n    \n    CompactMatrix*      m_pA;\n    int                 m_mtype; // matrix type\n    \n#ifdef __APPLE__\n    SparseMatrixStructure SMS;\n    SparseMatrix_Double A;\n    SparseOpaqueSymbolicFactorization ASS;\n    SparseSymbolicFactorOptions SSFO;\n    SparseOpaqueFactorization_Double ASF;\n    long* colS;\n    std::vector<long> pointers;\n    SparseIterativeStatus_t SStatus;\n#endif\n    \n    // solver control parameters\n    \n    \n    // Matrix data\n    int m_n, m_nnz, m_nrhs;\n    \n    bool    m_print_cn;     // estimate and print the condition number\n    bool    m_iparm3;       // use direct-iterative method\n    bool    m_isFactored;\n    int     m_ftype;        // factorization type\n    int     m_ordmthd;      // order method\n    int     m_itrmthd;      // iterative method\n    int     m_maxiter;      // max number of iterations\n    int     m_rtol;         // absolute tolerance\n    int     m_atol;         // relative tolerance\n    int     m_print_level;  // print level for status updates\n    \npublic:\n    Implementation()\n    {\n        m_print_cn = false;\n        m_mtype = -2;\n        m_iparm3 = false;\n        m_isFactored = false;\n        m_ftype = -1;\n        m_ordmthd = -1;\n        m_itrmthd = ITSparseLSMR;\n        m_pA = nullptr;\n        m_maxiter = 1000;\n        m_rtol = 1e-3;\n        m_atol = 1e-12;\n        m_print_level = 0;\n    }\n};\n\n//////////////////////////////////////////////////////////////\n// AccelerateSparseSolver\n//////////////////////////////////////////////////////////////\n\nBEGIN_FECORE_CLASS(AccelerateSparseSolver, LinearSolver)\n    ADD_PARAMETER(imp->m_print_cn, \"print_condition_number\");\n    ADD_PARAMETER(imp->m_iparm3  , \"iterative\");\n    ADD_PARAMETER(imp->m_ftype   , \"factorization\");\n    ADD_PARAMETER(imp->m_ordmthd , \"order_method\");\n    ADD_PARAMETER(imp->m_itrmthd , \"iterative_method\");\n    ADD_PARAMETER(imp->m_maxiter , \"max_iter\");\n    ADD_PARAMETER(imp->m_print_level, \"print_level\");\n    ADD_PARAMETER(imp->m_rtol    , \"rtol\");\n    ADD_PARAMETER(imp->m_atol    , \"atol\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nAccelerateSparseSolver::AccelerateSparseSolver(FEModel* fem) : LinearSolver(fem), imp(new AccelerateSparseSolver::Implementation)\n{\n    imp->m_fem = fem;\n}\n\n//-----------------------------------------------------------------------------\nAccelerateSparseSolver::~AccelerateSparseSolver()\n{\n    Destroy();\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::PrintConditionNumber(bool b)\n{\n    imp->m_print_cn = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::UseIterativeFactorization(bool b)\n{\n    imp->m_iparm3 = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::SetFactorizationType(int ftype)\n{\n    imp->m_ftype = ftype;\n}\n\nvoid AccelerateSparseSolver::SetPrintLevel(int printlevel)\n{\n    imp->m_print_level = printlevel;\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::SetOrderMethod(int order)\n{\n    imp->m_ordmthd = order;\n}\n\n//-----------------------------------------------------------------------------\nbool AccelerateSparseSolver::IsIterative() const\n{\n    return imp->m_iparm3;\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::MyReportError(const char* message) {\n    fprintf(stderr, \"\\n\\tERROR during iterative solve: %s\\n\",message);\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::MyReportStatus(const char* message) {\n    fprintf(stdout, \"iterative solver status: %s\",message);\n    return;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* AccelerateSparseSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n    // allocate the correct matrix format depending on matrix symmetry type\n    switch (ntype)\n    {\n        case REAL_SYMMETRIC     : imp->m_mtype = -2; imp->m_pA = new CCSSparseMatrix(0); break;\n        case REAL_UNSYMMETRIC   : imp->m_mtype = 11; imp->m_pA = new CCSSparseMatrix(0); break;\n        case REAL_SYMM_STRUCTURE: imp->m_mtype =  1; imp->m_pA = new CCSSparseMatrix(0); break;\n        default:\n            assert(false);\n            imp->m_pA = nullptr;\n    }\n    \n    return imp->m_pA;\n}\n\n//-----------------------------------------------------------------------------\nbool AccelerateSparseSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n    if (imp->m_pA && imp->m_isFactored) Destroy();\n    imp->m_pA = dynamic_cast<CompactMatrix*>(pA);\n    imp->m_mtype = -2;\n    if (dynamic_cast<CCSSparseMatrix*>(pA)) imp->m_mtype = 11;\n    return (imp->m_pA != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool AccelerateSparseSolver::PreProcess()\n{\n    assert(imp->m_isFactored == false);\n    \n    imp->m_n = imp->m_pA->Rows();\n    imp->m_nnz = imp->m_pA->NonZeroes();\n    imp->m_nrhs = 1;\n    \n    imp->pointers.resize(imp->m_nnz);\n    for (int i=0; i<imp->m_nnz; ++i) imp->pointers[i] = imp->m_pA->Pointers()[i];\n    imp->colS = &imp->pointers[0];\n    \n    if (__builtin_available(macOS 10.13, *)) {\n        imp->SMS.columnCount = imp->m_pA->Columns();\n        imp->SMS.rowCount = imp->m_pA->Rows();\n        imp->SMS.rowIndices = imp->m_pA->Indices();\n        imp->SMS.columnStarts = imp->colS;\n        imp->SMS.blockSize = 1;\n        if (!imp->m_iparm3) {\n            imp->SSFO.control = SparseDefaultControl;\n            imp->SSFO.order = nullptr;\n            imp->SSFO.ignoreRowsAndColumns = nullptr;\n            imp->SSFO.malloc = malloc;\n            imp->SSFO.free = free;\n            imp->SSFO.reportError = MyReportError;\n            if (imp->m_mtype == 11) {\n                // Use QR factorization as default for non-symmetric matrix\n                imp->SMS.attributes.kind = SparseOrdinary;\n                switch (imp->m_ordmthd) {\n                    case OMSparseOrderAMD:\n                        imp->SSFO.orderMethod = SparseOrderAMD;\n                        break;\n                    case OMSparseOrderMetis:\n                        imp->SSFO.orderMethod = SparseOrderMetis;\n                        break;\n                    case OMSparseOrderCOLAMD:\n                        imp->SSFO.orderMethod = SparseOrderCOLAMD;\n                        break;\n                    default:\n                        imp->SSFO.orderMethod = SparseOrderCOLAMD;\n                        break;\n                }\n                switch (imp->m_ftype) {\n                    case -1:\n                    case FTSparseFactorizationQR:\n                        imp->ASS = SparseFactor(SparseFactorizationQR, imp->SMS, imp->SSFO);\n                        break;\n                    case FTSparseFactorizationCholeskyAtA:\n                        imp->ASS = SparseFactor(SparseFactorizationCholeskyAtA, imp->SMS, imp->SSFO);\n                        break;\n                    default:\n                        fprintf(stderr, \"\\nIncorrect factorization type for non-symmetric matrix!\");\n                        return false;\n                        break;\n                }\n            }\n            else {\n                // Use LDLT as default factorization for symmetric matrix\n                // (Cholesky factorization does not seem to work)\n                imp->SMS.attributes.kind = SparseSymmetric;\n                switch (imp->m_ordmthd) {\n                    case -1:\n                    case OMSparseOrderAMD:\n                        imp->SSFO.orderMethod = SparseOrderAMD;\n                        break;\n                    case OMSparseOrderMetis:\n                        imp->SSFO.orderMethod = SparseOrderMetis;\n                        break;\n                    default:\n                        fprintf(stderr, \"\\nIncorrect order method for symmetric matrix!\");\n                        return false;\n                        break;\n                }\n                switch (imp->m_ftype) {\n                    case FTSparseFactorizationCholesky:\n                        imp->ASS = SparseFactor(SparseFactorizationCholesky, imp->SMS, imp->SSFO);\n                        break;\n                    case FTSparseFactorizationLDLT:\n                        imp->ASS = SparseFactor(SparseFactorizationLDLT, imp->SMS, imp->SSFO);\n                        break;\n                    case FTSparseFactorizationLDLTUnpivoted:\n                        imp->ASS = SparseFactor(SparseFactorizationLDLTUnpivoted, imp->SMS, imp->SSFO);\n                        break;\n                    case FTSparseFactorizationLDLTSBK:\n                        imp->ASS = SparseFactor(SparseFactorizationLDLTSBK, imp->SMS, imp->SSFO);\n                        break;\n                    case -1:    // default factorization\n                    case FTSparseFactorizationLDLTTPP:\n                        imp->ASS = SparseFactor(SparseFactorizationLDLTTPP, imp->SMS, imp->SSFO);\n                        break;\n                    default:\n                        fprintf(stderr, \"\\nIncorrect factorization type for symmetric matrix!\");\n                        return false;\n                        break;\n                }\n            }\n        }\n        else {\n            // for iterative solving we don't need to factorize\n            return true;\n        }\n    }\n    else {\n        // Fallback on earlier versions\n        fprintf(stderr, \"\\nERROR during preprocessing: \");\n        fprintf(stderr, \"\\naccelerate solver not available for macOS earlier than 10.13!\");\n        return false;\n    }\n    \n    return LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool AccelerateSparseSolver::Factor()\n{\n    // make sure we have work to do\n    if (imp->m_pA->Rows() == 0) return true;\n    \n    // ------------------------------------------------------------------------------\n    // This step does the factorization\n    // ------------------------------------------------------------------------------\n\n    if (__builtin_available(macOS 10.13, *)) {\n\n        // create the sparse matrix\n        imp->A.data = imp->m_pA->Values();\n        imp->A.structure = imp->SMS;\n\n        // for iterative solves we don't factor\n        if (imp->m_iparm3) {\n            return true;\n        }\n        else {\n            imp->ASF = SparseFactor(imp->ASS,imp->A);\n            if (imp->ASF.status != SparseStatusOK) {\n                fprintf(stderr, \"\\n\\tERROR during factorization:\");\n                switch (imp->ASF.status) {\n                    case SparseFactorizationFailed:\n                        fprintf(stderr, \"\\n\\tFactorization failed!\");\n                        break;\n                    case SparseMatrixIsSingular:\n                        fprintf(stderr, \"\\n\\tSparse matrix is singular!\");\n                        break;\n                    case SparseInternalError:\n                        fprintf(stderr, \"\\n\\tSolver called internal error!\");\n                        break;\n                    case SparseParameterError:\n                        fprintf(stderr, \"\\n\\tSolver called parameter error!\");\n                        break;\n                    default:\n                        fprintf(stderr, \"\\n\\tUnknown error!\");\n                }\n                return false;\n            }\n        }\n    }\n    else {\n        // Fallback on earlier versions\n        fprintf(stderr, \"\\nERROR during factorization: \");\n        fprintf(stderr, \"\\nAccelerate solver not available for macOS earlier than 10.13!\");\n        return false;\n    }\n    \n    // calculate and print the condition number\n    if (imp->m_print_cn)\n    {\n        double c = condition_number();\n        feLog(\"\\tcondition number (est.) ................... : %lg\\n\\n\", c);\n    }\n    \n    imp->m_isFactored = true;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool AccelerateSparseSolver::BackSolve(double* x, double* b)\n{\n    // make sure we have work to do\n    if (imp->m_pA->Rows() == 0) return true;\n    if ((!imp->m_iparm3) && (imp->m_isFactored == false)) return true;\n    \n    DenseVector_Double X, B;\n    X.count = B.count = imp->m_n;\n    X.data = x; B.data = b;\n    \n    // solve the system\n    if (__builtin_available(macOS 10.13, *)) {\n        if (!imp->m_iparm3) {\n            SparseSolve(imp->ASF,B,X);\n        }\n        else {\n            switch (imp->m_itrmthd) {\n                case ITSparseConjugateGradient:\n                    SparseCGOptions cg_opt;\n                    cg_opt.maxIterations = imp->m_maxiter;\n                    cg_opt.rtol = imp->m_rtol;\n                    cg_opt.atol = imp->m_atol;\n                    cg_opt.reportError = MyReportError;\n                    cg_opt.reportStatus = (imp->m_print_level>0) ? MyReportStatus : nullptr;\n                    imp->SStatus = SparseSolve(SparseConjugateGradient(cg_opt),imp->A,B,X,SparsePreconditionerDiagonal);\n                    break;\n                case ITSparseGMRES:\n                case ITSparseDQGMRES:\n                case ITSparseFGMRES:\n                    SparseGMRESOptions gmres_opt;\n                    gmres_opt.maxIterations = imp->m_maxiter;\n                    gmres_opt.reportError = MyReportError;\n                    gmres_opt.reportStatus = (imp->m_print_level>0) ? MyReportStatus : nullptr;\n                    gmres_opt.nvec = 0;\n                    gmres_opt.rtol = imp->m_rtol;\n                    gmres_opt.atol = imp->m_atol;\n                    switch (imp->m_itrmthd) {\n                        case ITSparseGMRES:\n                            gmres_opt.variant = SparseVariantGMRES;\n                            imp->SStatus = SparseSolve(SparseGMRES(gmres_opt),imp->A,B,X,SparsePreconditionerDiagonal);\n                            break;\n                        case ITSparseDQGMRES:\n                            gmres_opt.variant = SparseVariantDQGMRES;\n                            imp->SStatus = SparseSolve(SparseGMRES(gmres_opt),imp->A,B,X,SparsePreconditionerNone);\n                            break;\n                        case ITSparseFGMRES:\n                            gmres_opt.variant = SparseVariantFGMRES;\n                            imp->SStatus = SparseSolve(SparseGMRES(gmres_opt),imp->A,B,X,SparsePreconditionerDiagScaling);\n                            break;\n                    }\n                    break;\n                case ITSparseLSMR:\n                    SparseLSMROptions lsmr_opt;\n                    lsmr_opt.rtol = imp->m_rtol;\n                    lsmr_opt.atol = imp->m_atol;\n                    lsmr_opt.reportError = MyReportError;\n                    lsmr_opt.reportStatus = (imp->m_print_level>0) ? MyReportStatus : nullptr;\n                    imp->SStatus = SparseSolve(SparseLSMR(lsmr_opt),imp->A,B,X,SparsePreconditionerDiagScaling);\n                    break;\n            }\n            if (imp->SStatus != SparseIterativeConverged) {\n                fprintf(stderr, \"\\n\\tERROR during iterative solution:\\n\");\n                switch (imp->SStatus) {\n                    case SparseIterativeIllConditioned:\n                        fprintf(stderr, \"\\n\\tIll-conditioned system!\\n\");\n                        break;\n                    case SparseIterativeInternalError:\n                        fprintf(stderr, \"\\n\\tInternal failure!\\n\");\n                        break;\n                    case SparseIterativeMaxIterations:\n                        fprintf(stderr, \"\\n\\tExceeded maximum iteration limit!\\n\");\n                        break;\n                    case SparseIterativeParameterError:\n                        fprintf(stderr, \"\\n\\tError with one or more parameters!\\n\");\n                        break;\n                    default:\n                        fprintf(stderr, \"\\n\\tUnknown error!\\n\");\n                }\n                return false;\n            }\n        }\n    } else {\n        // Fallback on earlier versions\n        fprintf(stderr, \"\\nERROR during back solve: \");\n        fprintf(stderr, \"\\nAccelerate solver not available for macOS earlier than 10.13!\");\n        return false;\n    }\n    \n    // update stats\n    UpdateStats(1);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// This algorithm (naively) estimates the condition number. It is based on the observation that\n// for a linear system of equations A.x = b, the following holds\n// || A^-1 || >= ||x||.||b||\n// Thus the condition number can be estimated by\n// c = ||A||.||A^-1|| >= ||A|| . ||x|| / ||b||\n// This algorithm tries for some random b vectors with norm ||b||=1 to maxize the ||x||.\n// The returned value will be an underestimate of the condition number\ndouble AccelerateSparseSolver::condition_number()\n{\n    // This assumes that the factorization is already done!\n    int N = imp->m_pA->Rows();\n    \n    // get the norm of the matrix\n    double normA = imp->m_pA->infNorm();\n    \n    // estimate the norm of the inverse of A\n    double normAi = 0.0;\n    \n    // choose max iterations\n    int iters = (N < 50 ? N : 50);\n    \n    vector<double> b(N, 0), x(N, 0);\n    for (int i = 0; i < iters; ++i)\n    {\n        // create a random vector\n        NumCore::randomVector(b, -1.0, 1.0);\n        for (int j = 0; j < N; ++j) b[j] = (b[j] >= 0.0 ? 1.0 : -1.0);\n        \n        // calculate solution\n        BackSolve(&x[0], &b[0]);\n        \n        double normx = NumCore::infNorm(x);\n        if (normx > normAi) normAi = normx;\n        \n        int pct = (100 * i) / (iters - 1);\n        fprintf(stderr, \"calculating condition number: %d%%\\r\", pct);\n    }\n    \n    double c = normA*normAi;\n    return c;\n}\n\n//-----------------------------------------------------------------------------\nvoid AccelerateSparseSolver::Destroy()\n{\n    if (imp->m_pA && imp->m_isFactored)\n    {\n        SparseCleanup(imp->ASS);\n        SparseCleanup(imp->ASF);\n    }\n    imp->m_isFactored = false;\n}\n\n#else\nBEGIN_FECORE_CLASS(AccelerateSparseSolver, LinearSolver)\nEND_FECORE_CLASS();\n\nAccelerateSparseSolver::AccelerateSparseSolver(FEModel* fem) : LinearSolver(fem) {}\nAccelerateSparseSolver::~AccelerateSparseSolver() {}\nbool AccelerateSparseSolver::PreProcess() { return false; }\nbool AccelerateSparseSolver::Factor() { return false; }\nbool AccelerateSparseSolver::BackSolve(double* x, double* y) { return false; }\nvoid AccelerateSparseSolver::Destroy() {}\nSparseMatrix* AccelerateSparseSolver::CreateSparseMatrix(Matrix_Type ntype) { return nullptr; }\nbool AccelerateSparseSolver::SetSparseMatrix(SparseMatrix* pA) { return false; }\nvoid AccelerateSparseSolver::PrintConditionNumber(bool b) {}\ndouble AccelerateSparseSolver::condition_number() { return 0; }\nvoid AccelerateSparseSolver::UseIterativeFactorization(bool b) {}\nvoid AccelerateSparseSolver::SetFactorizationType(int ftype) {}\nvoid AccelerateSparseSolver::SetPrintLevel(int printlevel) {}\nbool AccelerateSparseSolver::IsIterative() const { return false; }\n#endif\n"
  },
  {
    "path": "NumCore/AccelerateSparseSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\n listed below.\n \n See Copyright-FEBio.txt for details.\n \n Copyright (c) 2022 University of Utah, The Trustees of Columbia University in\n the City of New York, and others.\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/CompactSymmMatrix.h>\n#ifdef __APPLE__\n#include <Accelerate/Accelerate.h>\n#endif\n\n// This sparse linear solver uses the Accelerate framework on MAC. \nclass AccelerateSparseSolver : public LinearSolver\n{\n    class   Implementation;\n    \npublic:\n    enum FactorizationType {\n        FTSparseFactorizationCholesky = 0,\n        FTSparseFactorizationLDLT = 1,\n        FTSparseFactorizationLDLTUnpivoted = 2,\n        FTSparseFactorizationLDLTSBK = 3,\n        FTSparseFactorizationLDLTTPP = 4,\n        FTSparseFactorizationQR = 5,\n        FTSparseFactorizationCholeskyAtA = 6,\n    };\n    \n    enum OrderMethod {\n        OMSparseOrderAMD = 0,\n        OMSparseOrderMetis = 1,\n        OMSparseOrderCOLAMD = 2,\n    };\n\n    enum IterativeMethod {\n        ITSparseConjugateGradient = 0,\n        ITSparseGMRES = 1,\n        ITSparseDQGMRES = 2,\n        ITSparseFGMRES = 3,\n        ITSparseLSMR = 4,\n    };\n    \npublic:\n    AccelerateSparseSolver(FEModel* fem);\n    ~AccelerateSparseSolver();\n    \npublic:\n    void PrintConditionNumber(bool b);\n    void UseIterativeFactorization(bool b);\n    void SetFactorizationType(int ftype);\n    void SetOrderMethod(int order);\n    void SetPrintLevel(int printlevel) override;\n\n    double condition_number();\n    static void MyReportError(const char* message);\n    static void MyReportStatus(const char* message);\n\npublic:\n    SparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n    bool SetSparseMatrix(SparseMatrix* pA) override;\n    \n    bool PreProcess() override;\n    bool Factor() override;\n    bool BackSolve(double* x, double* y) override;\n    void Destroy() override;\n    bool IsIterative() const override;\n    \nprivate:\n    Implementation*    imp;\n        \n    DECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/BIPNSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"BIPNSolver.h\"\n#include <FECore/vector.h>\n#include \"RCICGSolver.h\"\n#include <FECore/SchurComplement.h>\n#include <FECore/log.h>\n#include \"ILU0_Preconditioner.h\"\n#include \"IncompleteCholesky.h\"\n#include \"FGMRESSolver.h\"\n#include \"BoomerAMGSolver.h\"\n#include <FECore/Preconditioner.h>\n\n#ifdef MKL_ISS\n\n// We must undef PARDISO since it is defined as a function in mkl_solver.h\n#ifdef PARDISO\n#undef PARDISO\n#endif\n\n#include \"mkl_rci.h\"\n#include \"mkl_blas.h\"\n#include \"mkl_spblas.h\"\n\n// in SchurSolver.cpp\nbool BuildDiagonalMassMatrix(FEModel* fem, BlockMatrix* K, CompactSymmMatrix* M, double scale);\nbool BuildMassMatrix(FEModel* fem, BlockMatrix* K, CompactSymmMatrix* M, double scale);\n\nBEGIN_FECORE_CLASS(BIPNSolver, LinearSolver)\n\tADD_PARAMETER(m_maxiter             , \"maxiter\"    );\n\tADD_PARAMETER(m_tol                 , \"tol\"        );\n\tADD_PARAMETER(m_print_level         , \"print_level\");\n\tADD_PARAMETER(m_use_cg              , \"use_cg\");\n\tADD_PARAMETER(m_cg_maxiter          , \"cg_maxiter\" );\n\tADD_PARAMETER(m_cg_tol              , \"cg_tol\"     );\n\tADD_PARAMETER(m_cg_doResidualTest   , \"cg_check_residual\");\n\tADD_PARAMETER(m_gmres_maxiter       , \"gmres_maxiter\");\n\tADD_PARAMETER(m_gmres_tol           , \"gmres_tol\" );\n\tADD_PARAMETER(m_gmres_doResidualTest, \"gmres_check_residual\");\n\tADD_PARAMETER(m_gmres_pc            , \"gmres_precondition\");\n\tADD_PARAMETER(m_do_jacobi           , \"do_jacobi\");\n\tADD_PARAMETER(m_precondition_schur  , \"precondition_schur\");\nEND_FECORE_CLASS();\n\n// constructor\nBIPNSolver::BIPNSolver(FEModel* fem) : LinearSolver(fem), m_A(0)\n{\n\tm_print_level = 0;\n\tm_maxiter = 10;\n\tm_tol = 1e-6;\n\n\tm_cg_maxiter = 0;\n\tm_cg_tol = 0.0;\n\tm_cg_doResidualTest = true;\n\n\tm_gmres_maxiter = 0;\n\tm_gmres_tol = 0.0;\n\tm_gmres_doResidualTest = true;\n\tm_gmres_pc = 0;\n\n\tm_do_jacobi = true;\n\n\tm_precondition_schur = 0;\n\n\tm_use_cg = true;\n\n\tm_Asolver = nullptr;\n\tm_PS = nullptr;\n}\n\n// set the max nr of BIPN iterations\nvoid BIPNSolver::SetMaxIterations(int n)\n{\n\tm_maxiter = n;\n}\n\n// set the output level\nvoid BIPNSolver::SetPrintLevel(int n)\n{\n\tm_print_level = n;\n}\n\n// Set the BIPN convergence tolerance\nvoid BIPNSolver::SetTolerance(double eps)\n{\n\tm_tol = eps;\n}\n\n// Use CG for step 2 or not\nvoid BIPNSolver::UseConjugateGradient(bool b)\n{\n\tm_use_cg = b;\n}\n\n// set the CG convergence parameters\nvoid BIPNSolver::SetCGParameters(int maxiter, double tolerance, bool doResidualStoppingTest)\n{\n\tm_cg_maxiter        = maxiter;\n\tm_cg_tol            = tolerance;\n\tm_cg_doResidualTest = doResidualStoppingTest;\n}\n\n// set the GMRES convergence parameters\nvoid BIPNSolver::SetGMRESParameters(int maxiter, double tolerance, bool doResidualStoppingTest, int precondition)\n{\n\tm_gmres_maxiter        = maxiter;\n\tm_gmres_tol            = tolerance;\n\tm_gmres_doResidualTest = doResidualStoppingTest;\n\tm_gmres_pc             = precondition;\n}\n\n// Do Jacobi preconditioner\nvoid BIPNSolver::DoJacobiPreconditioner(bool b)\n{\n\tm_do_jacobi = b;\n}\n\n// set the schur preconditioner option\nvoid BIPNSolver::SetSchurPreconditioner(int n)\n{\n\tm_precondition_schur = n;\n}\n\n//! Return a sparse matrix compatible with this solver\nSparseMatrix* BIPNSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// make sure we can support this matrix\n\tif (ntype != Matrix_Type::REAL_UNSYMMETRIC) return 0;\n\n\t// make sure we have two partitions\n\tif (m_part.size() != 2) return 0;\n\n\tint noffset = 1;\n\tif (m_gmres_pc == 2) noffset = 0;\n\n\t// allocate new matrix\n\tif (m_A) delete m_A;\n\tm_A = new BlockMatrix();\n\tm_A->Partition(m_part, ntype, noffset);\n\n\t// and return\n\treturn m_A;\n}\n\n// set the sparse matrix\nbool BIPNSolver::SetSparseMatrix(SparseMatrix* A)\n{\n\tm_A = dynamic_cast<BlockMatrix*>(A);\n\tif (m_A == nullptr) return false;\n\n\treturn true;\n}\n\n// allocate storage\nbool BIPNSolver::PreProcess()\n{\n\t// make sure we have a matrix\n\tif (m_A == 0) return false;\n\tBlockMatrix& A = *m_A;\n\n\t// get the number of equations\n\tint N = A.Rows();\n\n\t// make sure the partition is valid\n\tif (m_part.size() != 2) return false;\n\tint Nu = m_part[0];\n\tint Np = m_part[1];\n\tassert((Nu + Np) == N);\n\n\t// allocate pre-conditioners\n\tKd.resize(Nu);\n\tWm.resize(Nu);\n\tWc.resize(Np);\n\n\t// allocate temp storage\n\tyu.resize(Nu);\n\typ.resize(Np);\n\n\tyu_n.resize(Nu);\n\typ_n.resize(Np);\n\n\tRm0.resize(Nu);\n\tRc0.resize(Np);\n\n\tRm_n.resize(Nu);\n\tRc_n.resize(Np);\n\n\tYu.resize(m_maxiter, std::vector<double>(Nu));\n\tYp.resize(m_maxiter, std::vector<double>(Np));\n\n\tRM.resize(Nu);\n\tRC.resize(Np);\n\n\tRmu.resize(m_maxiter, std::vector<double>(Nu));\n\tRmp.resize(m_maxiter, std::vector<double>(Nu));\n\tRcu.resize(m_maxiter, std::vector<double>(Np));\n\tRcp.resize(m_maxiter, std::vector<double>(Np));\n\n\tau.resize(m_maxiter+1);\n\tap.resize(m_maxiter+1);\n\n\tdu.resize(Nu);\n\tdp.resize(Np);\n\n\t// CG temp buffers\n\tif (m_use_cg)\n\t\tcg_tmp.resize(4* Np);\n\telse\n\t{\n\t\t// GMRES buffer\n\t\tint M = (Np < 150 ? Np : 150); // this is the default value of par[15] (i.e. par[14] in C)\n\t\tif (m_gmres_maxiter > 0) M = m_gmres_maxiter;\n\n\t\t// allocate temp storage\n\t\tcg_tmp.resize((Np*(2 * M + 1) + (M*(M + 9)) / 2 + 1));\n\t}\n\n\t// GMRES buffer\n\tint M = (Nu < 150 ? Nu : 150); // this is the default value of par[15] (i.e. par[14] in C)\n\tif (m_gmres_maxiter > 0) M = m_gmres_maxiter;\n\n\t// allocate temp storage\n\tgmres_tmp.resize((Nu*(2 * M + 1) + (M*(M + 9)) / 2 + 1));\n\n\tm_Asolver = new FGMRESSolver(GetFEModel());\n\n\t// initialize solver for A block\n\tLinearSolver* PC = nullptr;\n\tswitch (m_gmres_pc)\n\t{\n\tcase 0: PC = nullptr; break;\n\tcase 1: PC = new ILU0_Preconditioner(GetFEModel()); break;\n\tcase 2: PC = new BoomerAMGSolver(GetFEModel()); break;\n\tdefault:\n\t\treturn false;\n\t}\n\t\t\n\tm_Asolver->SetMaxIterations(m_gmres_maxiter);\n\tm_Asolver->SetRelativeResidualTolerance(m_gmres_tol);\n\tif (m_Asolver->SetSparseMatrix(A.Block(0, 0).pA) == false) return false;\n\n\tif (m_Asolver->GetLeftPreconditioner())\n\t{\n\t\tm_Asolver->GetLeftPreconditioner()->SetSparseMatrix(A.Block(0, 0).pA);\n\t}\n\tif (m_Asolver->PreProcess() == false) return false;\n\n\t// preconditioner of Schur solver\n\tif ((m_precondition_schur > 0) && (m_PS == nullptr))\n\t{\n\t\tif (m_precondition_schur == 1)\n\t\t{\n\t\t\t// diagonal mass matrix\n\t\t\tCompactSymmMatrix* M = new CompactSymmMatrix(1);\n\t\t\tif (BuildDiagonalMassMatrix(GetFEModel(), m_A, M, 1.0) == false) return false;\n\n\t\t\tDiagonalPreconditioner* PS = new DiagonalPreconditioner(GetFEModel());\n\t\t\tif (PS->SetSparseMatrix(M) == false) return false;\n\t\t\tif (PS->PreProcess() == false) return false;\n\t\t\tif (PS->Factor() == false) return false;\n\n\t\t\tm_PS = PS;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// mass matrix\n\t\t\tCompactSymmMatrix* M = new CompactSymmMatrix(1);\n\t\t\tif (BuildMassMatrix(GetFEModel(), m_A, M, 1.0) == false) return false;\n\n\t\t\t// We do an incomplete cholesky factorization\n\t\t\tIncompleteCholesky* PS = new IncompleteCholesky(GetFEModel());\n\t\t\tif (PS->SetSparseMatrix(M) == false) return false;\n\t\t\tif (PS->PreProcess() == false) return false;\n\t\t\tif (PS->Factor() == false) return false;\n\n\t\t\tm_PS = PS;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//! Pre-condition the matrix\nbool BIPNSolver::Factor()\n{\n\t// make sure we have a matrix\n\tif (m_A == 0) return false;\n\tBlockMatrix& A = *m_A;\n\n\t// get the number of equations\n\tint N = A.Rows();\n\tint Nu = m_part[0];\n\tint Np = m_part[1];\n\n\t// get the blocks\n\t// \n\t//     | K | G |\n\t// A = |---+---|\n\t//     | D | L |\n\t//\n\tBlockMatrix::BLOCK& K = m_A->Block(0, 0);\n\tBlockMatrix::BLOCK& G = m_A->Block(0, 1);\n\tBlockMatrix::BLOCK& D = m_A->Block(1, 0);\n\tBlockMatrix::BLOCK& L = m_A->Block(1, 1);\n\n\t// calculate preconditioner\n\tWm.assign(Nu, 1.0);\n\tWc.assign(Np, 1.0);\n\tif (m_do_jacobi)\n\t{\n\t\tfor (int i = 0; i < Nu; ++i)\n\t\t{\n\t\t\tdouble ki = fabs(K.pA->diag(i));\n\t\t\tif (ki > 0.0) \n\t\t\t\tWm[i] = 1.0 / sqrt(ki);\n\t\t}\n\t\tif (L.pA)\n\t\t{\n\t\t\tfor (int i = 0; i < Np; ++i)\n\t\t\t{\n\t\t\t\tdouble li = fabs(L.pA->diag(i));\n\t\t\t\tif (li > 0.0)\n\t\t\t\t\tWc[i] = 1.0 / sqrt(li);\n\t\t\t}\n\t\t}\n\t}\n\n\t// normalize the matrix\n\tK.pA->scale(Wm, Wm);\n\tG.pA->scale(Wm, Wc);\n\tD.pA->scale(Wc, Wm);\n\tif (L.pA) L.pA->scale(Wc, Wc);\n\n\t// Now calculate diagonal matrix of K\n\tif (m_do_jacobi) Kd.assign(Nu, 1.0);\n\telse\n\t{\n\t\tfor (int i = 0; i < Nu; ++i)\n\t\t{\n\t\t\tdouble ki = K.pA->diag(i);\n\t\t\tif (ki == 0.0) return false;\n\t\t\tKd[i] = 1.0 / ki;\n\t\t}\n\t}\n\n\tif (m_Asolver->Factor() == false) return false;\n\n\treturn true;\n}\n\n//! Calculate the solution of RHS b and store solution in x\nbool BIPNSolver::BackSolve(double* x, double* b)\n{\n\t// make sure we have a matrix\n\tif (m_A == 0) return false;\n\tBlockMatrix& A = *m_A;\n\n\tBlockMatrix::BLOCK& K = m_A->Block(0, 0);\n\tBlockMatrix::BLOCK& G = m_A->Block(0, 1);\n\tBlockMatrix::BLOCK& D = m_A->Block(1, 0);\n\tBlockMatrix::BLOCK& L = m_A->Block(1, 1);\n\n\t// number of equations\n\tint N = A.Rows();\n\tint Nu = m_part[0];\n\tint Np = m_part[1];\n\n\t// normalize RHS\n\tfor (int i = 0; i<Nu; ++i) Rm0[i] = Wm[i]*b[i     ];\n\tfor (int i = 0; i<Np; ++i) Rc0[i] = Wc[i]*b[i + Nu];\n\n\t// initialize\n\tRM = Rm0;\n\tRC = Rc0;\n\n\t// calculate initial error\n\tdouble err_0 = Rm0*Rm0 + Rc0*Rc0, err_n = 0.0;\n\n\t// setup the Schur complement\n\tDiagonalPreconditioner DPC(nullptr);\n\tif (DPC.Create(K.pA) == false) return false;\n\tSchurComplementA S(&DPC, G.pA, D.pA, L.pA);\n\tS.NegateSchur(true);\n\n\tif (m_print_level != 0) feLog(\"--- Starting BIPN:\\n\");\n\n\tint MI = m_maxiter;\n\tint M = 2 * m_maxiter;\n\tmatrix QM(M, M);\n\tQM.zero();\n\n\t// do the BIPN iterations \n\tint niter = 0;\n\tfor (int n=0; n<m_maxiter; ++n)\n\t{\n\t\tniter++;\n\t\tif (m_print_level != 0) feLog(\"BIPN %d: \", niter);\n\n\t\t// solve for yu_n (use GMRES): K*yu_n = RM[n]\n\t\tm_Asolver->ResetStats();\n\t\tm_Asolver->BackSolve(&yu_n[0], &(RM[0]));\n\t\tm_gmres1_iters = m_Asolver->GetStats().iterations;\n\t\tif (m_print_level != 0) feLog(\"%d, \", m_gmres1_iters);\n\n\t\t// compute the corrected residual Rc_n = RC[n] - D*yu_n\n\t\tD.vmult(yu_n, Rc_n);\n\t\tvsub(Rc_n, RC, Rc_n);\n\n\t\t// solve for yp_n (use CG): (L - D*G) * yp_n = Rc_n\n\t\tif (m_use_cg)\n\t\t\tm_cg_iters = cgsolve(&S, m_PS, yp_n, Rc_n);\n\t\telse\n\t\t\tm_cg_iters = gmressolve(&S, m_PS, yp_n, Rc_n);\n\t\tif (m_print_level != 0) feLog(\"%d, \", m_cg_iters);\n\n\t\t// compute corrected residual: Rm_n = RM[n] - G*yp_n\n\t\tG.vmult(yp_n, Rm_n);\n\t\tvsub(Rm_n, RM, Rm_n);\n\n\t\t// solve for yu_n (use GMRES): K*yu_n = Rm_n\n\t\tm_Asolver->ResetStats();\n\t\tm_Asolver->BackSolve(&yu_n[0], &Rm_n[0]);\n\t\tm_gmres2_iters = m_Asolver->GetStats().iterations;\n\t\tif (m_print_level != 0) feLog(\"%d, \", m_gmres2_iters);\n\n\t\t// calculate temp vectors\n\t\tK.vmult(yu_n, Rmu[n]);\t// Rmu[n] = K*yu_n;\n\t\tG.vmult(yp_n, Rmp[n]);\t// Rmp[n] = G*yp_n;\n\t\tD.vmult(yu_n, Rcu[n]);\t// Rcu[n] = D*yu_n;\n\t\tL.vmult(yp_n, Rcp[n]);\t// Rcp[n] = L*yp_n;\n\n\t\t// store solution candidates\n\t\tYu[n] = yu_n;\n\t\tYp[n] = yp_n;\n\n\t\t// update QM\n\t\tfor (int j = 0; j <= n; ++j)\n\t\t{\n\t\t\tQM(n     , j     ) = Rmu[n] * Rmu[j] + Rcu[n] * Rcu[j];\n\t\t\tQM(n     , j + MI) = Rmu[n] * Rmp[j] + Rcu[n] * Rcp[j];\n\t\t\tQM(n + MI, j     ) = Rmp[n] * Rmu[j] + Rcp[n] * Rcu[j];\n\t\t\tQM(n + MI, j + MI) = Rmp[n] * Rmp[j] + Rcp[n] * Rcp[j];\n\n\t\t\tQM(j     , n     ) = QM(     n, j     );\n\t\t\tQM(j + MI, n     ) = QM(     n, j + MI);\n\t\t\tQM(j     , n + MI) = QM(n + MI, j     );\n\t\t\tQM(j + MI, n + MI) = QM(n + MI, j + MI);\n\t\t}\n\n\t\t// number of coefficients to determine\n\t\tconst int m = 2 * (n + 1);\n\n\t\t// setup Q matrix\n\t\tmatrix Q(m, m);\n\t\tvector<double> q(m);\n\n\t\t// fill in the blocks of Q and q\n\t\tfor (int i = 0; i <= n; ++i)\n\t\t{\n\t\t\tfor (int j = 0; j <= n; ++j)\n\t\t\t{\n\t\t\t\tQ(i        , j        ) = QM(i, j);\n\t\t\t\tQ(i        , j + n + 1) = QM(i, j+ MI);\n\t\t\t\tQ(i + n + 1, j        ) = QM(i+ MI, j);\n\t\t\t\tQ(i + n + 1, j + n + 1) = QM(i+ MI, j+ MI);\n\t\t\t}\n\n\t\t\tq[i        ] = Rm0*Rmu[i] + Rc0*Rcu[i];\n\t\t\tq[i + n + 1] = Rm0*Rmp[i] + Rc0*Rcp[i];\n\t\t}\n\n\t\t// solve for the coefficients\n\t\tvector<double> a(m);\n\t\tQ.solve(a, q);\n\t\tfor (int i = 0; i <= n; ++i)\n\t\t{\n\t\t\tau[i] = a[i];\n\t\t\tap[i] = a[i + n + 1];\n\t\t}\n\n\t\t// calculate error\n\t\terr_n = err_0 - a*q;\n\t\tif (m_print_level != 0)\n\t\t{\n\t\t\tfeLog(\"%lg (%lg)\\n\", sqrt(fabs(err_n)), m_tol*sqrt(err_0));\n\t\t}\n\n\t\t// check for convergence\n\t\tif (sqrt(fabs(err_n)) < m_tol*sqrt(err_0)) break;\n\n\t\t// Update R vectors\n\t\tif (n < m_maxiter - 1)\n\t\t{\n\t\t\t// update R vectors\n\t\t\tRM = Rm0;\n\t\t\tRC = Rc0;\n\t\t\tfor (int i = 0; i <= n; ++i)\n\t\t\t{\n\t\t\t\tvsubs(RM, Rmu[i], au[i]);\n\t\t\t\tvsubs(RM, Rmp[i], ap[i]);\n\n\t\t\t\tvsubs(RC, Rcu[i], au[i]);\n\t\t\t\tvsubs(RC, Rcp[i], ap[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (m_print_level != 0) feLog(\"---\\n\");\n\n\t// calculate final solution vector\n\tyu.assign(Nu, 0.0);\n\typ.assign(Np, 0.0);\n\tfor (int i = 0; i<niter; ++i)\n\t{\n\t\tvadds(yu, Yu[i], au[i]);\n\t\tvadds(yp, Yp[i], ap[i]);\n\t}\n\n\t// de-normalize the solution\n\tvscale(yu, Wm);\n\tvscale(yp, Wc);\n\n\t// put it all together\n\tfor (int i = 0; i<Nu; ++i) x[i     ] = yu[i];\n\tfor (int i = 0; i<Np; ++i) x[i + Nu] = yp[i];\n\n\t// and ... scene!\n\treturn true;\n}\n\nint BIPNSolver::cgsolve(SparseMatrix* K, LinearSolver* PC, std::vector<double>& x, std::vector<double>& b)\n{\n\tm_cg_iters = 0;\n\tRCICGSolver cg(nullptr);\n\tif (cg.SetSparseMatrix(K) == false) return -1;\n\n\tif (PC) cg.SetLeftPreconditioner(PC);\n\n\tcg.SetMaxIterations(m_cg_maxiter);\n\tcg.SetTolerance(m_cg_tol);\n//\tcg.SetPrintLevel(1);\n\n\tif (cg.PreProcess() == false) return -1;\n\tif (cg.Factor() == false) return -1;\n\tcg.BackSolve(&x[0], &b[0]);\n\n\treturn cg.GetStats().iterations;\n}\n\nint BIPNSolver::gmressolve(SparseMatrix* K, LinearSolver* PC, vector<double>& x, vector<double>& b)\n{\n\tFGMRESSolver gmres(nullptr);\n\tif (gmres.SetSparseMatrix(K) == false) return -1;\n\n\tif (PC) gmres.SetLeftPreconditioner(PC);\n\n\tgmres.SetMaxIterations(m_gmres_maxiter);\n\tgmres.SetRelativeResidualTolerance(m_gmres_tol);\n\n\tif (gmres.PreProcess() == false) return -1;\n\tif (gmres.Factor() == false) return -1;\n\tgmres.BackSolve(&x[0], &b[0]);\n\n\treturn gmres.GetStats().iterations;\n}\n\n#else\t// ifdef MKL_ISS\nBEGIN_FECORE_CLASS(BIPNSolver, LinearSolver)\n\tADD_PARAMETER(m_maxiter, \"maxiter\");\n\tADD_PARAMETER(m_tol, \"tol\");\n\tADD_PARAMETER(m_print_level, \"print_level\");\n\tADD_PARAMETER(m_use_cg, \"use_cg\");\n\tADD_PARAMETER(m_cg_maxiter, \"cg_maxiter\");\n\tADD_PARAMETER(m_cg_tol, \"cg_tol\");\n\tADD_PARAMETER(m_cg_doResidualTest, \"cg_check_residual\");\n\tADD_PARAMETER(m_gmres_maxiter, \"gmres_maxiter\");\n\tADD_PARAMETER(m_gmres_tol, \"gmres_tol\");\n\tADD_PARAMETER(m_gmres_doResidualTest, \"gmres_check_residual\");\n\tADD_PARAMETER(m_gmres_pc, \"gmres_precondition\");\n\tADD_PARAMETER(m_do_jacobi, \"do_jacobi\");\n\tADD_PARAMETER(m_precondition_schur, \"precondition_schur\");\nEND_FECORE_CLASS();\n\nBIPNSolver::BIPNSolver(FEModel* fem) : LinearSolver(fem), m_A(0) {}\nbool BIPNSolver::PreProcess() { return false; }\nbool BIPNSolver::Factor() { return false; }\nbool BIPNSolver::BackSolve(double* x, double* b) { return false; }\nSparseMatrix* BIPNSolver::CreateSparseMatrix(Matrix_Type ntype) { return 0; }\nvoid BIPNSolver::SetPrintLevel(int n) {}\nvoid BIPNSolver::SetMaxIterations(int n) {}\nvoid BIPNSolver::SetTolerance(double eps) {}\nvoid BIPNSolver::UseConjugateGradient(bool b) {}\nvoid BIPNSolver::SetCGParameters(int maxiter, double tolerance, bool doResidualStoppingTest) {}\nvoid BIPNSolver::SetGMRESParameters(int maxiter, double tolerance, bool doResidualStoppingTest, int precondition) {}\nvoid BIPNSolver::DoJacobiPreconditioner(bool b) {}\nvoid BIPNSolver::SetSchurPreconditioner(int n) {}\nbool BIPNSolver::SetSparseMatrix(SparseMatrix* A) { return false; }\n#endif\n"
  },
  {
    "path": "NumCore/BIPNSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include \"BlockMatrix.h\"\n\nclass FGMRESSolver;\n\n//-----------------------------------------------------------------------------\n// This class implements the bi-partitioned iterative solver, by:\n// Esmaily-Moghadam, Bazilevs, Marsden, Comput. Methods Appl. Mech. Engrg. 286(2015) 40-62\n//\nclass BIPNSolver : public LinearSolver\n{\npublic:\n\t// constructor\n\tBIPNSolver(FEModel* fem);\n\n\t// set the output level\n\tvoid SetPrintLevel(int n) override;\n\n\t// set the max nr of BIPN iterations\n\tvoid SetMaxIterations(int n);\n\n\t// Set the BIPN convergence tolerance\n\tvoid SetTolerance(double eps);\n\n\t// Use CG for step 2 or not\n\tvoid UseConjugateGradient(bool b);\n\n\t// set the CG convergence parameters\n\tvoid SetCGParameters(int maxiter, double tolerance, bool doResidualStoppingTest);\n\n\t// set the GMRES convergence parameters\n\tvoid SetGMRESParameters(int maxiter, double tolerance, bool doResidualStoppingTest, int precondition);\n\n\t// Do Jacobi preconditioner\n\tvoid DoJacobiPreconditioner(bool b);\n\n\t// set the schur preconditioner option\n\tvoid SetSchurPreconditioner(int n);\n\npublic:\n\t// allocate storage\n\tbool PreProcess() override;\n\n\t//! Factor the matrix (for iterative solvers, this can be used for creating pre-conditioner)\n\tbool Factor() override;\n\n\t//! Calculate the solution of RHS b and store solution in x\n\tbool BackSolve(double* x, double* y) override;\n\n\t//! Return a sparse matrix compatible with this solver\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t// set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\nprivate:\n\tint cgsolve(SparseMatrix* K, LinearSolver* PC, vector<double>& x, vector<double>& b);\n\tint gmressolve(SparseMatrix* K, LinearSolver* PC, vector<double>& x, vector<double>& b);\n\nprivate:\n\tBlockMatrix*\tm_A;\t\t//!< the block matrix\n\tFGMRESSolver*\tm_Asolver;\t//!< the solver for the A - block\n\tLinearSolver*\tm_PS;\t\t//!< Schur complement preconditioner\n\n\tstd::vector<double>\t\tKd;\n\tstd::vector<double>\t\tWm, Wc;\n\tstd::vector<double>\t\tRm0, Rc0;\n\tstd::vector<double>\t\tRm_n, Rc_n;\n\tstd::vector<double>\t\tyu, yp;\n\tstd::vector<double>\t\tyu_n, yp_n;\n\n\tstd::vector< std::vector<double> >\tYu, Yp;\n\tstd::vector<double>\tau, ap;\n\tstd::vector<double> du, dp;\n\n\tvector<double> RM;\n\tvector<double> RC;\n\n\tvector< vector<double> > Rmu;\n\tvector< vector<double> > Rmp;\n\tvector< vector<double> > Rcu;\n\tvector< vector<double> > Rcp;\n\n\tint\t\tm_print_level;\t//!< level of output (0 is no output)\n\tint\t\tm_maxiter;\t\t//!< max nr of BIPN iterations\n\tdouble\tm_tol;\t\t\t//!< BPIN convergence tolerance\n\n\tbool\tm_use_cg;\t\t//!< use CG for step 2, otherwise GMRES is used\n\n\tbool\tm_do_jacobi;\t//!< do Jacobi precondition\n\n\tint\t\tm_precondition_schur;\t//!< preconditioner the Schur solver (0 = none, 1 = diag(M), 2 = ICHOL(M))\n\n\t// CG data\n\tint\t\tm_cg_maxiter;\t\t\t//!< max CG iterations\n\tdouble\tm_cg_tol;\t\t\t\t//!< CG tolerance\n\tbool\tm_cg_doResidualTest;\t//!< do the residual stopping test\n\tint\t\tm_cg_iters;\t\t\t\t//!< iterations of CG solve\n\n\tvector<double>\tcg_tmp;\n\n\t// GMRES data\n\tint\t\tm_gmres_maxiter;\t\t//!< max GMRES iterations\n\tdouble\tm_gmres_tol;\t\t\t//!< GMRES tolerance\n\tbool\tm_gmres_doResidualTest;\t//!< do the residual stopping test\n\tint\t\tm_gmres_pc;\t\t\t\t//!< preconditioner?\n\tint\t\tm_gmres1_iters, m_gmres2_iters;\n\n\tvector<double> gmres_tmp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/BiCGStabSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"BiCGStabSolver.h\"\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(BiCGStabSolver, IterativeLinearSolver)\n\tADD_PARAMETER(m_print_level, \"print_level\");\n\tADD_PARAMETER(m_tol, \"tol\");\n\tADD_PARAMETER(m_maxiter, \"max_iter\");\n\tADD_PARAMETER(m_fail_max_iter, \"fail_max_iters\");\n\tADD_PROPERTY(m_P, \"pc_left\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nBiCGStabSolver::BiCGStabSolver(FEModel* fem) : IterativeLinearSolver(fem), m_pA(0), m_P(0)\n{\n\tm_maxiter = 0;\n\tm_tol = 1e-5;\n\tm_abstol = 0.0;\n\tm_print_level = 0;\n\tm_fail_max_iter = true;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* BiCGStabSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// let the preconditioner decide\n\tm_pA = nullptr;\n\tif (m_P)\n\t{\n\t\tm_P->SetPartitions(m_part);\n\t\tm_pA = m_P->CreateSparseMatrix(ntype);\n\t}\n\telse\n\t{\n\t\tif (ntype == REAL_SYMMETRIC) m_pA = new CompactSymmMatrix;\n\t\telse m_pA = new CRSSparseMatrix(1);\n\t}\n\treturn m_pA;\n}\n\n//-----------------------------------------------------------------------------\nbool BiCGStabSolver::SetSparseMatrix(SparseMatrix* A)\n{\n\tm_pA = A;\n\treturn (m_pA != 0);\n}\n\n//-----------------------------------------------------------------------------\nvoid BiCGStabSolver::SetLeftPreconditioner(LinearSolver* P)\n{\n\tm_P = dynamic_cast<Preconditioner*>(P);\n}\n\n//-----------------------------------------------------------------------------\nLinearSolver* BiCGStabSolver::GetLeftPreconditioner()\n{\n\treturn m_P;\n}\n\n//-----------------------------------------------------------------------------\nbool BiCGStabSolver::HasPreconditioner() const\n{\n\treturn (m_P != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool BiCGStabSolver::PreProcess()\n{\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool BiCGStabSolver::Factor()\n{\n\tif (m_pA == 0) return false;\n\tif (m_P)\n\t{\n\t\tif (m_P->PreProcess() == false) return false;\n\t\tif (m_P->Factor() == false) return false;\n\t}\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool BiCGStabSolver::BackSolve(double* x, double* b)\n{\n\tif (m_pA == nullptr) return false;\n\n\tSparseMatrix& A = *m_pA;\n\tint neq = A.Rows();\n\n\t// assume initial guess is zero\n\tfor (int i = 0; i < neq; ++i) x[i] = 0.0;\n\n\t// calculate initial norm\n\t// r0 = b - A*x0\n\tvector<double> r_i(neq); double norm0 = 0.0, normi = 0.0;\n\tfor (int j = 0; j < neq; ++j)\n\t{\n\t\tr_i[j] = b[j];\n\t\tnorm0 += r_i[j] * r_i[j];\n\t}\n\tnorm0 = sqrt(norm0);\n\n\t// if the norm is zero, there is nothing to do\n\tif (norm0 == 0.0) return true;\n\n//\tChoose an arbitrary vector rt such that(rt, r0) != 0, e.g., rt = r0\n\tvector<double> rt(r_i);\n\n\t// initialize some stuff\n\tdouble rho_p = 1, alpha = 1, w_p = 1;\n\tvector<double> v_p(neq, 0.0), p_p(neq, 0.0), p_i(neq), y(neq, 0.0), h(neq), s(neq), z(neq), t(neq), q(neq);\n\n\tint max_iter = m_maxiter;\n\tif (max_iter == 0) max_iter = (neq < 150 ? neq : 150);\n\tint iter = 0;\n\tbool converged = false;\n\tdo\n\t{\n\t\tdouble rho_i = rt*r_i;\n\n\t\tdouble beta = (rho_i / rho_p)*(alpha / w_p);\n\n\t\tfor (int j = 0; j < neq; ++j) p_i[j] = r_i[j] + beta*(p_p[j] - w_p*v_p[j]);\n\n\t\t// apply preconditioner\n\t\tif (m_P)\n\t\t{\n\t\t\tm_P->BackSolve(y, p_i);\n\t\t}\n\t\telse y = p_i;\n\n\t\tA.mult_vector(&y[0], &v_p[0]);\n\n\t\talpha = rho_i / (rt*v_p);\n\n\t\tfor (int j = 0; j < neq; ++j) h[j] = x[j] + alpha*y[j];\n\n\t\tfor (int j = 0; j < neq; ++j) s[j] = r_i[j] - alpha*v_p[j];\n//\t\tIf h is accurate enough then xi = h and quit\n\n\t\tif (m_P)\n\t\t{\n\t\t\tm_P->BackSolve(z, s);\n\t\t}\n\t\telse z = s;\n\n\t\tA.mult_vector(&z[0], &t[0]);\n\n\t\tif (m_P)\n\t\t{\n\t\t\tm_P->BackSolve(q, t);\n\t\t}\n\t\telse q = t;\n\n\t\tw_p = (q*z) / (q*q);\n\n\t\tfor (int j = 0; j < neq; ++j) x[j] = h[j] + w_p*z[j];\n\t\t\n\t\tnormi = 0.0;\n\t\tfor (int j = 0; j < neq; ++j)\n\t\t{\n\t\t\tr_i[j] = s[j] - w_p*t[j];\n\t\t\tnormi += r_i[j] * r_i[j];\n\t\t}\n\t\tnormi = sqrt(normi);\n\n\t\t// see if we have converged\n\t\tdouble tol = norm0*m_tol + m_abstol;\n\t\tif (normi <= tol) converged = true;\n\t\telse\n\t\t{\n\t\t\t// prepare for next iteration\n\t\t\trho_p = rho_i;\n\t\t\tp_p = p_i;\n\t\t}\n\n\t\t// check max iterations\n\t\titer++;\n\t\tif (iter > max_iter) break;\n\n\t\tif (m_print_level > 1)\n\t\t{\n\t\t\tfeLog(\"%d:%lg, %lg\\n\", iter, normi, tol);\n\t\t}\n\t}\n\twhile (!converged);\n\n\tif (m_print_level == 1)\n\t{\n\t\tfeLog(\"%d:%lg, %lg\\n\", iter, normi, norm0);\n\t}\n\n\tUpdateStats(iter);\n\n\treturn (m_fail_max_iter ? converged : true);\n}\n\n//-----------------------------------------------------------------------------\nvoid BiCGStabSolver::Destroy()\n{\n}\n"
  },
  {
    "path": "NumCore/BiCGStabSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/Preconditioner.h>\n#include <FECore/CompactSymmMatrix.h>\n\nclass BiCGStabSolver : public IterativeLinearSolver\n{\npublic:\n\tBiCGStabSolver(FEModel* fem);\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* b) override;\n\tvoid Destroy() override;\n\npublic:\n\tbool HasPreconditioner() const override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\n\tvoid SetLeftPreconditioner(LinearSolver* P) override;\n\tLinearSolver* GetLeftPreconditioner() override;\n\n\tvoid SetMaxIterations(int n) { m_maxiter = n; }\n\tvoid SetTolerance(double tol) { m_tol = tol; }\n\tvoid SetPrintLevel(int n) override { m_print_level = n; }\n\nprotected:\n\tSparseMatrix*\t\tm_pA;\n\tPreconditioner*\t\tm_P;\n\n\tint\t\tm_maxiter;\t\t// max nr of iterations\n\tdouble\tm_tol;\t\t\t// residual relative tolerance\n\tdouble\tm_abstol;\t\t// absolute residual tolerance\n\tint\t\tm_print_level;\t// output level\n\tdouble\tm_fail_max_iter;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/BlockMatrix.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"BlockMatrix.h\"\n#include <assert.h>\n\n//-----------------------------------------------------------------------------\nBlockMatrix::BlockMatrix()\n{\n}\n\n//-----------------------------------------------------------------------------\nBlockMatrix::~BlockMatrix()\n{\n\t// clear memory allocations\n\tClear();\n\n\t// delete blocks\n\tconst int n = (int) m_Block.size();\n\tfor (int i=0; i<n; ++i) delete m_Block[i].pA;\n}\n\n//-----------------------------------------------------------------------------\n//! This function sets the partitions for the blocks. \n//! The partition list contains the number of rows (and cols) for each partition.\n//! So for instance, if part = {10,10}, a 2x2=4 partition is created, where\n//! each block is a 10x10 matrix.\n//\n//! TODO: I want to put the partition information in the matrix profile structure\n//!       so that the Create function can be used to create all the blocks.\nvoid BlockMatrix::Partition(const vector<int>& part, Matrix_Type mtype, int offset)\n{\n\t// copy the partitions, but store equation numbers instead of number of equations\n\tconst int n = (int)part.size();\n\tm_part.resize(n+1);\n\tm_part[0] = 0;\n\tfor (int i=0; i<n; ++i) m_part[i+1] = m_part[i] + part[i];\n\n\t// create the block structure for all the partitions\n\tm_Block.resize(n*n);\n\tint nrow = 0;\n\tfor (int i=0; i<n; ++i) // loop over rows\n\t{\n\t\tint ncol = 0;\n\t\tfor (int j=0; j<n; ++j) // loop over cols\n\t\t{\n\t\t\tBLOCK& Bij = m_Block[i*n+j];\n\t\t\tBij.nstart_row = nrow;\n\t\t\tBij.nend_row   = Bij.nstart_row + part[i] - 1;\n\n\t\t\tBij.nstart_col = ncol;\n\t\t\tBij.nend_col   = Bij.nstart_col + part[j] - 1;\n\n\t\t\t// Note the parameters in the constructors.\n\t\t\t// This is because we are using Pardiso for this\n\t\t\tif (i==j)\n\t\t\t{\n\t\t\t\tif (mtype == REAL_SYMMETRIC)\n\t\t\t\t\tBij.pA = new CompactSymmMatrix(offset);\n\t\t\t\telse\n\t\t\t\t\tBij.pA = new CRSSparseMatrix(offset);\n\t\t\t}\n\t\t\telse\n\t\t\t\tBij.pA = new CRSSparseMatrix(offset);\n\t\t\t\n\t\t\tncol += part[j];\n\t\t}\n\t\tnrow += part[i];\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! Create a sparse matrix from a sparse-matrix profile\nvoid BlockMatrix::Create(SparseMatrixProfile& MP)\n{\n\tm_nrow = MP.Rows();\n\tm_ncol = MP.Columns();\n\tm_nsize = 0;\n\tconst int N = (int) m_Block.size();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tBLOCK& Bi = m_Block[i];\n\t\tSparseMatrixProfile MPi = MP.GetBlockProfile(Bi.nstart_row, Bi.nstart_col, Bi.nend_row, Bi.nend_col);\n\t\tBi.pA->Create(MPi);\n\t\tm_nsize += Bi.pA->NonZeroes();\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! assemble a matrix into the sparse matrix\nvoid BlockMatrix::Assemble(const matrix& ke, const std::vector<int>& lm)\n{\n\tint I, J;\n\tconst int N = ke.rows();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tif ((I = lm[i])>=0)\n\t\t{\n\t\t\tfor (int j=0; j<N; ++j)\n\t\t\t{\n\t\t\t\tif ((J = lm[j]) >= 0) add(I,J, ke[i][j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n//! assemble a matrix into the sparse matrix\nvoid BlockMatrix::Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj)\n{\n\tint I, J;\n\tconst int N = ke.rows();\n\tconst int M = ke.columns();\n\tfor (int i=0; i<N; ++i)\n\t{\n\t\tif ((I = lmi[i])>=0)\n\t\t{\n\t\t\tfor (int j=0; j<M; ++j)\n\t\t\t{\n\t\t\t\tif ((J = lmj[j]) >= 0) add(I,J, ke[i][j]);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//-----------------------------------------------------------------------------\n// helper function for finding partitions\nint BlockMatrix::find_partition(int i)\n{\n\tconst int N = (int)m_part.size() - 1;\n\tint n = 0;\n\tfor (; n<N; ++n)\n\t\tif (m_part[n+1] > i) break;\n\tassert(n<N);\n\treturn n;\n}\n\n//-----------------------------------------------------------------------------\n// helper function for finding a block\nBlockMatrix::BLOCK& BlockMatrix::Block(int i, int j)\n{\n\tconst int N = (int)m_part.size() - 1;\n\treturn m_Block[i*N+j];\n}\n\n//-----------------------------------------------------------------------------\n//! set entry to value\nbool BlockMatrix::check(int i, int j)\n{\n\tint nr = find_partition(i);\n\tint nc = find_partition(j);\n\treturn Block(nr, nc).pA->check(i - m_part[nr], j - m_part[nc]);\n}\n\n//-----------------------------------------------------------------------------\n//! set entry to value\nvoid BlockMatrix::set(int i, int j, double v)\n{\n\tint nr = find_partition(i);\n\tint nc = find_partition(j);\n\tBlock(nr, nc).pA->set(i - m_part[nr], j - m_part[nc], v);\n}\n\n//-----------------------------------------------------------------------------\n//! add value to entry\nvoid BlockMatrix::add(int i, int j, double v)\n{\n\tint nr = find_partition(i);\n\tint nc = find_partition(j);\n\tBlock(nr, nc).pA->add(i - m_part[nr], j - m_part[nc], v);\n}\n\n//-----------------------------------------------------------------------------\n//! retrieve value\ndouble BlockMatrix::get(int i, int j)\n{\n\tint nr = find_partition(i);\n\tint nc = find_partition(j);\n\treturn Block(nr, nc).pA->get(i - m_part[nr], j - m_part[nc]);\n}\n\n//-----------------------------------------------------------------------------\n//! get the diagonal value\ndouble BlockMatrix::diag(int i)\n{\n\tint n = find_partition(i);\n\treturn Block(n, n).pA->diag(i - m_part[n]);\n}\n\n//-----------------------------------------------------------------------------\n//! release memory for storing data\nvoid BlockMatrix::Clear()\n{\n\t// clear the blocks\n\tconst int n = (int) m_Block.size();\n\tfor (int i=0; i<n; ++i) m_Block[i].pA->Clear();\n}\n\n//-----------------------------------------------------------------------------\n//! Zero all matrix elements\nvoid BlockMatrix::Zero()\n{\n\t// zero the blocks\n\tconst int n = (int) m_Block.size();\n\tfor (int i=0; i<n; ++i) m_Block[i].pA->Zero();\n}\n\n//-----------------------------------------------------------------------------\n//! multiply with vector\nbool BlockMatrix::mult_vector(double* x, double* r)\n{\n\tint nr = Rows();\n\tvector<double> tmp(nr, 0);\n\tfor (int i=0; i<nr; ++i) r[i] = 0.0;\n\tint NP = Partitions();\n\tfor (int i=0; i<NP; ++i)\n\t{\n\t\tint n0 = m_part[i];\n\t\tfor (int j=0; j<NP; ++j)\n\t\t{\n\t\t\tint m0 = m_part[j];\n\n\t\t\tBLOCK& bij = Block(i, j);\n\n\t\t\tbij.pA->mult_vector(x + m0, &tmp[0] + n0);\n\n\t\t\tint nj = bij.Rows();\n\t\t\tfor (int k=0; k<nj; ++k) r[n0 + k] += tmp[n0 + k];\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//! row and column scale\nvoid BlockMatrix::scale(const vector<double>& L, const vector<double>& R)\n{\n\tvector<double> Li, Rj;\n\tfor (int n = 0; n < m_Block.size(); ++n)\n\t{\n\t\tBLOCK& bn = m_Block[n];\n\t\tif (bn.pA)\n\t\t{\n\t\t\tint NR = bn.Rows();\n\t\t\tint NC = bn.Cols();\n\n\t\t\tLi.resize(NR);\n\t\t\tRj.resize(NC);\n\n\t\t\tfor (int i = 0; i < NR; ++i) Li[i] = L[i + bn.nstart_row];\n\t\t\tfor (int i = 0; i < NC; ++i) Rj[i] = R[i + bn.nstart_col];\n\n\t\t\tbn.pA->scale(Li, Rj);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "NumCore/BlockMatrix.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/SparseMatrix.h>\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactSymmMatrix.h>\n#include <FECore/CompactUnSymmMatrix.h>\n\n//-----------------------------------------------------------------------------\n// This class implements a diagonally symmetric block-structured matrix. That is\n// A matrix for which the diagonal blocks are symmetric, but the off-diagonal\n// matrices can be unsymmetric.\nclass BlockMatrix : public SparseMatrix\n{\npublic:\n\tstruct BLOCK\n\t{\n\t\tint\t\tnstart_row, nend_row;\n\t\tint\t\tnstart_col, nend_col;\n\t\tCompactMatrix*\tpA;\n\n\t\tint Rows() { return nend_row - nstart_row + 1; }\n\t\tint Cols() { return nend_col - nstart_col + 1; }\n\n\t\tbool vmult(vector<double>& x, vector<double>& y)\n\t\t{\n\t\t\tif (pA) return pA->mult_vector(&x[0], &y[0]);\n\t\t\telse for (size_t i = 0; i < x.size(); ++i) y[i] = 0.0;\n\t\t\treturn true;\n\t\t}\n\t};\n\npublic:\n\tBlockMatrix();\n\t~BlockMatrix();\n\npublic:\n\t//! Partition the matrix into blocks\n\tvoid Partition(const vector<int>& part, Matrix_Type mtype, int offset = 1);\n\npublic:\n\t//! Create a sparse matrix from a sparse-matrix profile\n\tvoid Create(SparseMatrixProfile& MP) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lm) override;\n\n\t//! assemble a matrix into the sparse matrix\n\tvoid Assemble(const matrix& ke, const std::vector<int>& lmi, const std::vector<int>& lmj) override;\n\n\t//! check if a matrix entry was allocated\n\tbool check(int i, int j) override;\n\n\t//! set entry to value\n\tvoid set(int i, int j, double v) override;\n\n\t//! add value to entry\n\tvoid add(int i, int j, double v) override;\n\n\t//! retrieve value\n\tdouble get(int i, int j) override;\n\n\t//! get the diagonal value\n\tdouble diag(int i) override;\n\n\t//! release memory for storing data\n\tvoid Clear() override;\n\n\t//! zero matrix elements\n\tvoid Zero() override;\n\n\t//! multiply with vector\n\tbool mult_vector(double* x, double* r) override;\n\n\t//! row and column scale\n\tvoid scale(const vector<double>& L, const vector<double>& R) override;\n\npublic:\n\t//! return number of blocks\n\tint Blocks() const { return (int) m_Block.size(); }\n\n\t//! get a block\n\tBLOCK& Block(int i, int j);\n\n\t//! find the partition index of an equation number i\n\tint find_partition(int i);\n\n\t//! return number of partitions\n\tint Partitions() const { return (int) m_part.size() - 1; }\n\n\t//! Start equation index of partition i\n\tint StartEquationIndex(int i) { return m_part[i]; }\n\n\t//! number of equations in partition i\n\tint PartitionEquations(int i) { return m_part[i+1]-m_part[i]; }\n\nprotected:\n\tvector<int>\t\tm_part;\t\t//!< partition list\n\tvector<BLOCK>\tm_Block;\t//!< block matrices\n};\n"
  },
  {
    "path": "NumCore/BlockSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"BlockSolver.h\"\n#include <FECore/log.h>\n\nBEGIN_FECORE_CLASS(BlockIterativeSolver, IterativeLinearSolver)\n\tADD_PARAMETER(m_maxiter   , \"max_iter\");\n\tADD_PARAMETER(m_printLevel, \"print_level\");\n\tADD_PARAMETER(m_tol        , \"tol\");\n\tADD_PARAMETER(m_failMaxIter, \"fail_max_iter\");\n\tADD_PARAMETER(m_method     , \"solution_method\");\n\tADD_PARAMETER(m_zeroInitGuess, \"zero_initial_guess\");\nEND_FECORE_CLASS()\n\n//-----------------------------------------------------------------------------\n//! constructor\nBlockIterativeSolver::BlockIterativeSolver(FEModel* fem) : IterativeLinearSolver(fem)\n{\n\tm_pA = 0;\n\tm_tol = 1e-8;\n\tm_maxiter = 150;\n\tm_iter = 0;\n\tm_printLevel = 0;\n\tm_failMaxIter = true;\n\tm_method = JACOBI;\n\tm_zeroInitGuess = true;\n}\n\n//-----------------------------------------------------------------------------\n//! constructor\nBlockIterativeSolver::~BlockIterativeSolver()\n{\n}\n\n//-----------------------------------------------------------------------------\n// return whether the iterative solver has a preconditioner or not\nbool BlockIterativeSolver::HasPreconditioner() const\n{\n\treturn false;\n}\n\n//-----------------------------------------------------------------------------\nvoid BlockIterativeSolver::SetRelativeTolerance(double tol)\n{\n\tm_tol = tol;\n}\n\n//-----------------------------------------------------------------------------\n// set the max nr of iterations\nvoid BlockIterativeSolver::SetMaxIterations(int maxiter)\n{\n\tm_maxiter = maxiter;\n}\n\n//-----------------------------------------------------------------------------\n// get the iteration count\nint BlockIterativeSolver::GetIterations() const\n{\n\treturn m_iter;\n}\n\n//-----------------------------------------------------------------------------\n// set the print level\nvoid BlockIterativeSolver::SetPrintLevel(int n)\n{\n\tm_printLevel = n;\n}\n\n//-----------------------------------------------------------------------------\n// set the solution method\nvoid BlockIterativeSolver::SetSolutionMethod(int method)\n{\n\tm_method = method;\n}\n\n//-----------------------------------------------------------------------------\n// set fail on max iterations flag\nvoid BlockIterativeSolver::SetFailMaxIters(bool b)\n{\n\tm_failMaxIter = b;\n}\n\n//-----------------------------------------------------------------------------\n// set the zero-initial-guess flag\nvoid BlockIterativeSolver::SetZeroInitialGuess(bool b)\n{\n\tm_zeroInitGuess = b;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a sparse matrix\nSparseMatrix* BlockIterativeSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\tm_pA = new BlockMatrix();\n\tm_pA->Partition(m_part, ntype);\n\treturn m_pA;\n}\n\n//-----------------------------------------------------------------------------\n//! set the sparse matrix\nbool BlockIterativeSolver::SetSparseMatrix(SparseMatrix* m)\n{\n\tm_pA = dynamic_cast<BlockMatrix*>(m);\n\tif (m_pA)\n\t{\n\t\tvector<int> p(m_pA->Partitions());\n\t\tfor (int i = 0; i < m_pA->Partitions(); ++i) p[i] = m_pA->PartitionEquations(i);\n\t\tSetPartitions(p);\n\t}\n\treturn (m_pA != nullptr);\n}\n\n//-----------------------------------------------------------------------------\n//! Preprocess \nbool BlockIterativeSolver::PreProcess()\n{\n\t// make sure we have a matrix\n\tif (m_pA == 0) return false;\n\n\t// get the number of partitions\n\tint NP = m_pA->Partitions();\n\n\t// allocate solvers for diagonal blocks\n\tm_solver.resize(NP);\n\tfor (int i=0; i<NP; ++i)\n\t{\n\t\tm_solver[i] = new PardisoSolver(GetFEModel());\n\t\tBlockMatrix::BLOCK& Bi = m_pA->Block(i,i);\n\t\tm_solver[i]->SetSparseMatrix(Bi.pA);\n\t\tif (m_solver[i]->PreProcess() == false) return false;\n\t}\n\n\tm_iter = 0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Factor matrix\nbool BlockIterativeSolver::Factor()\n{\n\t// factor the diagonal matrices\n\tint N = (int) m_solver.size();\n\tfor (int i=0; i<N; ++i) m_solver[i]->Factor();\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Backsolve the linear system\nbool BlockIterativeSolver::BackSolve(double* x, double* b)\n{\n\t// get partitions\n\tint NP = m_pA->Partitions();\n\tassert(NP == Partitions());\n\n\t// split right-hand-side and solution vector in partitions\n\tvector< vector<double> > R(NP);\n\tvector< vector<double> > X(NP);\n\tint neq0 = 0;\n\tfor (int i=0; i<NP; ++i)\n\t{\n\t\tint neq = m_pA->PartitionEquations(i);\n\t\tvector<double>& Ri = R[i];\n\t\tRi.resize(neq);\n\t\tfor (int j=0; j<neq; ++j) Ri[j] = b[j + neq0];\n\n\t\t// also allocate and initialize solution vectors\n\t\t// we assume that the passed x is an initial guess\n\t\tX[i].resize(neq);\n\t\tif (m_zeroInitGuess)\n\t\t{\n\t\t\tzero(X[i]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (int j = 0; j < neq; ++j) X[i][j] = x[j + neq0];\n\t\t}\n\n\t\tneq0 += neq;\n\t}\n\tassert(neq0 == m_pA->Rows());\n\n\t// residual vector\n\tvector<double> res(neq0);\n\n\t// calculate initial norm\n\tm_pA->mult_vector(x, &res[0]);\n\tfor (int i = 0; i<neq0; ++i) res[i] -= b[i];\n\tdouble norm0 = l2_norm(res);\n\tif (m_printLevel == 1) feLog(\"%d: %lg\\n\", 0, norm0);\n\n\t// temp storage for RHS\n\tvector< vector<double> > T(R.size());\n\n\t// solve the linear system iteratively\n\tbool bconv = false;\n\tm_iter = 0;\n\tdouble norm = 0.0;\n\tfor (int n=0; n<m_maxiter; ++n)\n\t{\n\t\t// loop over rows\n\t\tfor (int i=0; i<NP; ++i)\n\t\t{\n\t\t\tT[i].assign(R[i].size(), 0.0);\n\n\t\t\t// loop over columns\n\t\t\tfor (int j=0; j<NP; ++j)\n\t\t\t{\n\t\t\t\tif (i != j)\n\t\t\t\t{\n\t\t\t\t\t// get the off-diagonal matrix\n\t\t\t\t\tCompactMatrix& Cij = *(m_pA->Block(i,j).pA);\n\n\t\t\t\t\t// multiply with X[j] and add to T[i]\n\t\t\t\t\tvector<double>& Xj = X[j];\n\t\t\t\t\tvector<double>& Ti = T[i];\n\n\t\t\t\t\tif (l2_sqrnorm(Xj) != 0.0)\n\t\t\t\t\t\tCij.mult_vector(&Xj[0], &Ti[0]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// subtract temp from RHS\n\t\t\tint neq = m_pA->PartitionEquations(i);\n\t\t\tfor (int j=0; j<neq; ++j) T[i][j] = R[i][j] - T[i][j];\n\n\t\t\tif (m_method == GAUSS_SEIDEL)\n\t\t\t{\n\t\t\t\tif (l2_sqrnorm(T[i]) != 0.0)\n\t\t\t\t{\n\t\t\t\t\tif (m_solver[i]->BackSolve(&X[i][0], &T[i][0]) == false)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\telse zero(X[i]);\n\t\t\t}\n\t\t}\n\n\t\t// backsolve the equations\n\t\tif (m_method == JACOBI)\n\t\t{\n\t\t\tfor (int i = 0; i < NP; ++i)\n\t\t\t{\n\t\t\t\tif (m_solver[i]->BackSolve(&X[i][0], &T[i][0]) == false)\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// combine solution into single solution vector\n\t\tneq0 = 0;\n\t\tfor (int i = 0; i<NP; ++i)\n\t\t{\n\t\t\tint neq = m_pA->PartitionEquations(i);\n\t\t\tvector<double>& Xi = X[i];\n\t\t\tfor (int j = 0; j<neq; ++j) x[neq0 + j] = Xi[j];\n\t\t\tneq0 += neq;\n\t\t}\n\n\t\t// increment iteration count\n\t\tm_iter++;\n\n\t\t// calculate residual\n\t\tm_pA->mult_vector(&x[0], &res[0]);\n\t\tfor (int i=0; i<neq0; ++i) res[i] -= b[i];\n\t\tnorm = l2_norm(res);\n\t\tif (m_printLevel == 1) feLog(\"%d: %lg\\n\", m_iter, norm);\n\t\tif (norm <= norm0*m_tol)\n\t\t{\n\t\t\tbconv = true;\n\t\t\tbreak;\t\n\t\t}\n\t}\n\n\tif (m_printLevel == 2) feLog(\"%d: %lg\\n\", m_iter, norm);\n\n\tif ((bconv == false) && (m_failMaxIter == false))\n\t{\n\t\tif (m_printLevel != 0)\n\t\t\tfeLogWarning(\"Iterative solver reached max iterations, but convergence is forced.\");\n\t\tbconv = true;\n\t}\n\n\tUpdateStats(m_iter);\n\n\treturn bconv;\n}\n\n//-----------------------------------------------------------------------------\n//! Clean up\nvoid BlockIterativeSolver::Destroy()\n{\n\tint N = (int) m_solver.size();\n\tfor (int i=0; i<N; ++i) m_solver[i]->Destroy();\n}\n"
  },
  {
    "path": "NumCore/BlockSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include \"BlockMatrix.h\"\n#include \"PardisoSolver.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements an iterative block solution strategy for solving linear \n// systems.\n\nclass BlockIterativeSolver : public IterativeLinearSolver\n{\npublic:\n\tenum SOLUTION_METHOD {\n\t\tJACOBI,\n\t\tGAUSS_SEIDEL\n\t};\n\npublic:\n\t//! constructor\n\tBlockIterativeSolver(FEModel* fem);\n\n\t//! destructor\n\t~BlockIterativeSolver();\n\n\t//! Preprocess \n\tbool PreProcess() override;\n\n\t//! Factor matrix\n\tbool Factor() override;\n\n\t//! Backsolve the linear system\n\tbool BackSolve(double* x, double* y) override;\n\n\t//! Clean up\n\tvoid Destroy() override;\n\n\t//! Create a sparse matrix\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* m) override;\n\n\t// return whether the iterative solver has a preconditioner or not\n\tbool HasPreconditioner() const override;\n\npublic:\n\t// Set the relative convergence tolerance\n\tvoid SetRelativeTolerance(double tol);\n\n\t// set the max nr of iterations\n\tvoid SetMaxIterations(int maxiter);\n\n\t// get the iteration count\n\tint GetIterations() const;\n\n\t// set fail on max iterations flag\n\tvoid SetFailMaxIters(bool b);\n\n\t// set the print level\n\tvoid SetPrintLevel(int n) override;\n\n\t// set the solution method\n\tvoid SetSolutionMethod(int method);\n\n\t// set the zero-initial-guess flag\n\tvoid SetZeroInitialGuess(bool b);\n\nprivate:\n\tBlockMatrix*\t\t\tm_pA;\t\t//!< block matrices\n\tvector<PardisoSolver*>\tm_solver;\t//!< solvers for solving diagonal blocks\n\nprivate:\n\tint\t\tm_method;\t\t\t//!< 0 = Jacobi, 1 = Gauss-Seidel\n\tdouble\tm_tol;\t\t\t\t//!< convergence tolerance\n\tint\t\tm_maxiter;\t\t\t//!< max number of iterations\n\tint\t\tm_iter;\t\t\t\t//!< nr of iterations of last solve\n\tint\t\tm_printLevel;\t\t//!< set print level\n\tbool\tm_failMaxIter;\t\t//!< fail on max iterations reached\n\tbool\tm_zeroInitGuess;\t//!< always use zero as the initial guess\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/BoomerAMGSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"BoomerAMGSolver.h\"\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/log.h>\n#include <FECore/FEModel.h>\n#include <FECore/FESolver.h>\n#include <FECore/FEAnalysis.h>\n#ifdef HYPRE\n#include <HYPRE.h>\n#include <HYPRE_IJ_mv.h>\n#include <HYPRE_parcsr_mv.h>\n#include <HYPRE_parcsr_ls.h>\n#include <_hypre_utilities.h>\n#include <_hypre_IJ_mv.h>\n\nclass BoomerAMGSolver::Implementation\n{\npublic:\n\tFEModel*\t\t\tm_fem;\n\n\tCRSSparseMatrix*\tm_A;\n\tHYPRE_IJMatrix\t\tij_A;\n\tHYPRE_ParCSRMatrix\tpar_A;\n\n\tvector<int>\t\t\tm_ind;\n\tHYPRE_IJVector\t\tij_b, ij_x;\n\tHYPRE_ParVector\t\tpar_b, par_x;\n\n\tvector<double>\t\tW;\t\t// Jacobi preconditioner\n\tvector<double>\t\trhs;\t// right-hand side \n\n\tHYPRE_Solver\tm_solver;\n\tHYPRE_Int\t\tm_num_iterations;\n\tdouble\t\t\tm_final_res_norm;\n\n\tint\t\t\tm_print_level;\n\tint\t\t\tm_maxIter;\n\tint\t\t\tm_maxLevels;\n\tdouble\t\tm_tol;\n\tint\t\t\tm_coarsenType;\n    bool        m_set_num_funcs;\n    int         m_relaxType;\n    int         m_interpType;\n    int         m_PMaxElmts;\n    int         m_NumSweeps;\n    int         m_AggInterpType;\n    int         m_AggNumLevels;\n    double      m_strong_threshold;\n\tint\t\t\tm_nodal;\n\tbool\t\tm_jacobi_pc;\n\tbool\t\tm_failMaxIters;\n\n\tHYPRE_Int*\tm_dofMap;\n\npublic:\n\tImplementation()\n\t{\n\t\tm_print_level = 0;\n\t\tm_maxLevels = 25;\n\t\tm_maxIter = 20;\n\t\tm_tol = 1.e-7;\n\t\tm_coarsenType = -1;\t// don't set\n\t\tm_set_num_funcs = false;\n        m_relaxType = 3;\n        m_interpType = 6;\n        m_strong_threshold = 0.5;\n        m_PMaxElmts = 4;\n        m_NumSweeps = 1;\n        m_AggInterpType = 0;\n        m_AggNumLevels = 0;\n\t\tm_nodal = 0;\n\t\tm_jacobi_pc = false;\n\t\tm_failMaxIters = true;\n\n\t\tm_A = nullptr;\n\t\tm_solver = nullptr;\n\t\tij_A = nullptr;\n\t\tij_b = nullptr;\n\t\tij_x = nullptr;\n\t}\n\n\tint equations() const { return (m_A ? m_A->Rows() : 0); }\n\n\t// Allocate stiffness matrix\n\tvoid allocMatrix()\n\t{\n\t\tint neq = equations();\n\n\t\trhs.assign(neq, 0.0);\n\n\t\t// Create an empty matrix object\n\t\tint ret = 0;\n\t\tret = HYPRE_IJMatrixCreate(MPI_COMM_WORLD, 0, neq - 1, 0, neq - 1, &ij_A);\n\n\t\t// set the matrix object type\n\t\tret = HYPRE_IJMatrixSetObjectType(ij_A, HYPRE_PARCSR);\n\t}\n\n\t// destroy stiffness matrix\n\tvoid destroyMatrix()\n\t{\n\t\tif (ij_A) HYPRE_IJMatrixDestroy(ij_A);\n\t\tij_A = nullptr;\n\t}\n\n\t// update coefficient matrix\n\tbool updateMatrix()\n\t{\n\t\tint neq = equations();\n\n\t\tSparseMatrix* A = m_A;\n\t\tW.resize(neq, 1.0);\n\t\tif (m_jacobi_pc)\n\t\t{\n\t\t\tfor (int i = 0; i < neq; ++i)\n\t\t\t{\n\t\t\t\tdouble dii = fabs(A->diag(i));\n\t\t\t\tif (dii == 0.0) return false;\n\t\t\t\tW[i] = 1.0 / sqrt(dii);\n\t\t\t}\n\t\t}\n\n\t\tif (m_jacobi_pc)\n\t\t\tA->scale(W, W);\n\n\t\t// call initialize, after which we can set the matrix coefficients\n\t\tHYPRE_Int ret = HYPRE_IJMatrixInitialize(ij_A);\n\n\t\t// set the matrix coefficients\n\t\tdouble* values = m_A->Values();\n\t\tint* indices = m_A->Indices();\n\t\tint* pointers = m_A->Pointers();\n\t\tfor (int i = 0; i<neq; ++i)\n\t\t{\n\t\t\tconst int* cols = indices + pointers[i];\n\t\t\tint ncols = pointers[i + 1] - pointers[i];\n\t\t\tdouble* vals = values + pointers[i];\n\t\t\tHYPRE_Int nrows = 1;\n\t\t\tret = HYPRE_IJMatrixSetValues(ij_A, nrows, (HYPRE_Int*)&ncols, (HYPRE_Int*)&i, (HYPRE_Int*)cols, vals);\n\t\t}\n\n\t\t// Finalize the matrix assembly\n\t\tret = HYPRE_IJMatrixAssemble(ij_A);\n\n\t\t// get the matrix object for later use\n\t\tret = HYPRE_IJMatrixGetObject(ij_A, (void**)&par_A);\n\n\t\treturn true;\n\t}\n\n\t// Allocate vectors for rhs and solution\n\tvoid allocVectors()\n\t{\n\t\tint neq = equations();\n\t\tm_ind.resize(neq, 0);\n\t\tfor (int i = 0; i<neq; ++i) m_ind[i] = i;\n\n\t\t// create the vector object for the rhs\n\t\tHYPRE_IJVectorCreate(MPI_COMM_WORLD, 0, neq - 1, &ij_b);\n\t\tHYPRE_IJVectorSetObjectType(ij_b, HYPRE_PARCSR);\n\n\t\t// create the vector object for the solution\n\t\tHYPRE_IJVectorCreate(MPI_COMM_WORLD, 0, neq - 1, &ij_x);\n\t\tHYPRE_IJVectorSetObjectType(ij_x, HYPRE_PARCSR);\n\t}\n\n\t// destroy vectors\n\tvoid destroyVectors()\n\t{\n\t\tif (ij_b) HYPRE_IJVectorDestroy(ij_b); ij_b = nullptr;\n\t\tif (ij_x) HYPRE_IJVectorDestroy(ij_x); ij_x = nullptr;\n\t}\n\n\t// update vectors \n\tvoid updateVectors(double* x, double* b)\n\t{\n\t\t// initialize vectors for changing coefficient values\n\t\tHYPRE_IJVectorInitialize(ij_b);\n\t\tHYPRE_IJVectorInitialize(ij_x);\n\n\t\t// scale by Jacobi PC\n\t\tint neq = equations();\n\t\tfor (int i = 0; i < neq; ++i) rhs[i] = W[i] * b[i];\n\n\t\t// set the values\n\t\tHYPRE_IJVectorSetValues(ij_b, neq, (HYPRE_Int*)&m_ind[0], &rhs[0]);\n\t\tHYPRE_IJVectorSetValues(ij_x, neq, (HYPRE_Int*)&m_ind[0], x);\n\n\t\t// finialize assembly\n\t\tHYPRE_IJVectorAssemble(ij_b);\n\t\tHYPRE_IJVectorAssemble(ij_x);\n\n\t\tHYPRE_IJVectorGetObject(ij_b, (void**)&par_b);\n\t\tHYPRE_IJVectorGetObject(ij_x, (void**)&par_x);\n\t}\n\n\tbool allocSolver()\n\t{\n\t\t// create solver\n\t\tHYPRE_BoomerAMGCreate(&m_solver);\n\n\t\tint printLevel = m_print_level;\n\t\tif (printLevel > 3) printLevel = 0;\n\n\t\t/* Set some parameters (See Reference Manual for more parameters) */\n\t\tHYPRE_BoomerAMGSetPrintLevel(m_solver, printLevel);  // print solve info\n//\t\tHYPRE_BoomerAMGSetOldDefault(m_solver); /* Falgout coarsening with modified classical interpolation */\n\t\tif (m_coarsenType >= 0) HYPRE_BoomerAMGSetCoarsenType(m_solver, m_coarsenType);\n\t\tif (m_relaxType   >= 0) HYPRE_BoomerAMGSetRelaxType(m_solver, m_relaxType);\n\t\tHYPRE_BoomerAMGSetRelaxOrder(m_solver, 1);   // uses C/F relaxation\n        HYPRE_BoomerAMGSetInterpType(m_solver, m_interpType);\n        HYPRE_BoomerAMGSetPMaxElmts(m_solver, m_PMaxElmts);\n\t\tHYPRE_BoomerAMGSetNumSweeps(m_solver, m_NumSweeps);   /* Sweeps on each level */\n\t\tHYPRE_BoomerAMGSetMaxLevels(m_solver, m_maxLevels);  /* maximum number of levels */\n        HYPRE_BoomerAMGSetStrongThreshold(m_solver, m_strong_threshold);\n        HYPRE_BoomerAMGSetAggInterpType(m_solver, m_AggInterpType);\n        HYPRE_BoomerAMGSetAggNumLevels(m_solver, m_AggNumLevels);\n\t\tHYPRE_BoomerAMGSetMaxIter(m_solver, m_maxIter);\n\t\tHYPRE_BoomerAMGSetTol(m_solver, m_tol);      /* conv. tolerance */\n\n\t\t// sets the number of funtions, which I think means the dofs per variable\n\t\t// For 3D mechanics this would be 3.\n\t\t// I think this also requires the staggered equation numbering.\n\t\t// NOTE: when set to 3, this definitely seems to help with (some) mechanics problems!\n\t\tif (m_set_num_funcs)\n\t\t{\n\t\t\t// we need the FESolver so we can get the dof mapping\n\t\t\tFESolver* fesolve = m_fem->GetCurrentStep()->GetFESolver();\n\n\t\t\t// get the dof map\n\t\t\tvector<int> dofMap;\n\t\t\tint nfunc = fesolve->GetActiveDofMap(dofMap);\n\t\t\tif (nfunc == -1) return false;\n\n\t\t\t// allocate dof map\n\t\t\t// (We need to copy it here since Hypre will deallocate it)\n\t\t\tint neq = (int)dofMap.size();\n\t\t\tm_dofMap = (HYPRE_Int*)malloc(neq * sizeof(HYPRE_Int));\n\t\t\tfor (size_t i = 0; i < neq; ++i) m_dofMap[i] = dofMap[i];\n\n\t\t\tprintf(\"\\tNumber of functions : %d\\n\", nfunc);\n\n\t\t\t// assign to BoomerAMG\n\t\t\tHYPRE_BoomerAMGSetNumFunctions(m_solver, nfunc);\n\n\t\t\t// set the dof map\n\t\t\tHYPRE_BoomerAMGSetDofFunc(m_solver, m_dofMap);\n\t\t}\n\n\t\t// NOTE: Turning this option on seems to worsen convergence!\n\t\tif (m_nodal > 0) HYPRE_BoomerAMGSetNodal(m_solver, m_nodal);\n\n\t\treturn true;\n\t}\n\n\tvoid destroySolver()\n\t{\n\t\tif (m_solver) HYPRE_BoomerAMGDestroy(m_solver);\n\t\tm_solver = nullptr;\n\t}\n\n\tvoid doSetup()\n\t{\n\t\tHYPRE_BoomerAMGSetup(m_solver, par_A, par_b, par_x);\n\t}\n\n\tbool doSolve(double* x)\n\t{\n\t\tHYPRE_BoomerAMGSolve(m_solver, par_A, par_b, par_x);\n\n\t\t/* Run info - needed logging turned on */\n\t\tHYPRE_BoomerAMGGetNumIterations(m_solver, &m_num_iterations);\n\t\tHYPRE_BoomerAMGGetFinalRelativeResidualNorm(m_solver, &m_final_res_norm);\n\n\t\t// see if we converged\n\t\tbool bok = false;\n\t\tif ((m_num_iterations <= m_maxIter) && (m_final_res_norm < m_tol)) bok = true;\n\n\t\t/* get the local solution */\n\t\tint neq = equations();\n\t\tHYPRE_IJVectorGetValues(ij_x, neq, (HYPRE_Int*)&m_ind[0], &x[0]);\n\n\t\t// scale by Jacobi PC\n\t\tfor (int i = 0; i < neq; ++i) x[i] *= W[i];\n\n\t\treturn bok;\n\t}\n};\n\nBEGIN_FECORE_CLASS(BoomerAMGSolver, LinearSolver)\n\tADD_PARAMETER(imp->m_maxIter         , \"max_iter\");\n\tADD_PARAMETER(imp->m_print_level     , \"print_level\");\n\tADD_PARAMETER(imp->m_tol             , \"tol\");\n\tADD_PARAMETER(imp->m_maxLevels       , \"max_levels\");\n\tADD_PARAMETER(imp->m_coarsenType     , \"coarsen_type\");\n\tADD_PARAMETER(imp->m_set_num_funcs\t , \"use_num_funcs\");\n    ADD_PARAMETER(imp->m_relaxType       , \"relax_type\");\n    ADD_PARAMETER(imp->m_interpType      , \"interp_type\");\n    ADD_PARAMETER(imp->m_strong_threshold, \"strong_threshold\");\n    ADD_PARAMETER(imp->m_PMaxElmts       , \"p_max_elmts\");\n    ADD_PARAMETER(imp->m_NumSweeps       , \"num_sweeps\");\n    ADD_PARAMETER(imp->m_AggInterpType   , \"agg_interp_type\");\n    ADD_PARAMETER(imp->m_AggNumLevels    , \"agg_num_levels\");\n\tADD_PARAMETER(imp->m_nodal           , \"nodal\");\n\tADD_PARAMETER(imp->m_jacobi_pc       , \"do_jacobi\");\n\tADD_PARAMETER(imp->m_failMaxIters    , \"fail_max_iters\");\nEND_FECORE_CLASS();\n\nBoomerAMGSolver::BoomerAMGSolver(FEModel* fem) : LinearSolver(fem), imp(new BoomerAMGSolver::Implementation)\n{\n\timp->m_fem = fem;\n}\n\nBoomerAMGSolver::~BoomerAMGSolver()\n{\n\tDestroy();\n\tdelete imp;\n}\n\nvoid BoomerAMGSolver::SetPrintLevel(int printLevel)\n{\n\timp->m_print_level = printLevel;\n}\n\nvoid BoomerAMGSolver::SetMaxIterations(int maxIter)\n{\n\timp->m_maxIter = maxIter;\n}\n\nvoid BoomerAMGSolver::SetMaxLevels(int levels)\n{\n\timp->m_maxLevels = levels;\n}\n\nvoid BoomerAMGSolver::SetConvergenceTolerance(double tol)\n{\n\timp->m_tol = tol;\n}\n\nvoid BoomerAMGSolver::SetCoarsenType(int coarsenType)\n{\n\timp->m_coarsenType = coarsenType;\n}\n\nvoid BoomerAMGSolver::SetUseNumFunctions(bool b)\n{\n\timp->m_set_num_funcs = b;\n}\n\nvoid BoomerAMGSolver::SetRelaxType(int rlxtyp)\n{\n    imp->m_relaxType = rlxtyp;\n}\n\nvoid BoomerAMGSolver::SetInterpType(int inptyp)\n{\n    imp->m_interpType = inptyp;\n}\n\nvoid BoomerAMGSolver::SetStrongThreshold(double thresh)\n{\n    imp->m_strong_threshold = thresh;\n}\n\nvoid BoomerAMGSolver::SetPMaxElmts(int pmax)\n{\n    imp->m_PMaxElmts = pmax;\n}\n\nvoid BoomerAMGSolver::SetNumSweeps(int nswp)\n{\n    imp->m_NumSweeps = nswp;\n}\n\nvoid BoomerAMGSolver::SetAggInterpType(int aggit)\n{\n    imp->m_AggInterpType = aggit;\n}\n\nvoid BoomerAMGSolver::SetAggNumLevels(int anlv)\n{\n    imp->m_AggNumLevels = anlv;\n}\n\nvoid BoomerAMGSolver::SetNodal(int nodal)\n{\n\timp->m_nodal = nodal;\n}\n\nvoid BoomerAMGSolver::SetJacobiPC(bool b)\n{\n\timp->m_jacobi_pc = b;\n}\n\nbool BoomerAMGSolver::GetJacobiPC()\n{\n\treturn imp->m_jacobi_pc;\n}\n\nvoid BoomerAMGSolver::SetFailOnMaxIterations(bool b)\n{\n\timp->m_failMaxIters = b;\n}\n\nSparseMatrix* BoomerAMGSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// allocate the correct matrix format depending on matrix symmetry type\n\tswitch (ntype)\n\t{\n\tcase REAL_SYMMETRIC: imp->m_A = nullptr; break;\n\tcase REAL_UNSYMMETRIC: \n\tcase REAL_SYMM_STRUCTURE:\n\t\timp->m_A = new CRSSparseMatrix(0); break;\n\tdefault:\n\t\tassert(false);\n\t\timp->m_A = nullptr;\n\t}\n\n\treturn imp->m_A;\n}\n\nbool BoomerAMGSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n\tif (pA == imp->m_A) return true;\n\n\tif (imp->m_A) delete imp->m_A;\n\timp->m_A = dynamic_cast<CRSSparseMatrix*>(pA);\n\tif (imp->m_A == nullptr) return false;\n\tif (imp->m_A->Offset() != 0)\n\t{\n\t\timp->m_A = nullptr;\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nbool BoomerAMGSolver::PreProcess()\n{\n\timp->m_fem = GetFEModel();\n\timp->allocMatrix();\n\timp->allocVectors();\n\n\t// create solver\n\treturn imp->allocSolver();\n}\n\nbool BoomerAMGSolver::Factor()\n{\n\tif (imp->updateMatrix() == false) return false;\n\n\tvector<double> zero(imp->equations(), 0.0);\n\timp->updateVectors(&zero[0], &zero[0]);\n\n\timp->doSetup();\n\n\treturn true;\n}\n\nbool BoomerAMGSolver::BackSolve(double* x, double* b)\n{\n\timp->updateVectors(x, b);\n\n\tbool bok = imp->doSolve(x);\n\n\tif (imp->m_print_level > 3)\n\t{\n\t\tfeLog(\"AMG: %d iterations, %lg residual norm\\n\", imp->m_num_iterations, imp->m_final_res_norm);\n\t}\n\n\tUpdateStats(imp->m_num_iterations);\n\n\treturn (bok || (imp->m_failMaxIters == false));\n}\n\nvoid BoomerAMGSolver::Destroy()\n{\n\t// Destroy solver\n\timp->destroySolver();\n\n\timp->destroyMatrix();\n\timp->destroyVectors();\n}\n\n#else\nBEGIN_FECORE_CLASS(BoomerAMGSolver, LinearSolver)\nEND_FECORE_CLASS();\nBoomerAMGSolver::BoomerAMGSolver(FEModel* fem) : LinearSolver(fem) {}\nBoomerAMGSolver::~BoomerAMGSolver() {}\nvoid BoomerAMGSolver::SetPrintLevel(int printLevel) {}\nvoid BoomerAMGSolver::SetMaxIterations(int maxIter) {}\nvoid BoomerAMGSolver::SetConvergenceTolerance(double tol) {}\nvoid BoomerAMGSolver::SetMaxLevels(int levels) {}\nvoid BoomerAMGSolver::SetCoarsenType(int coarsenType) {}\nvoid BoomerAMGSolver::SetRelaxType(int rlxtyp) {}\nvoid BoomerAMGSolver::SetInterpType(int inptyp) {}\nvoid BoomerAMGSolver::SetStrongThreshold(double thresh) {}\nvoid BoomerAMGSolver::SetPMaxElmts(int pmax) {}\nvoid BoomerAMGSolver::SetNumSweeps(int nswp) {}\nvoid BoomerAMGSolver::SetAggInterpType(int aggit) {}\nvoid BoomerAMGSolver::SetAggNumLevels(int anlv) {}\nSparseMatrix* BoomerAMGSolver::CreateSparseMatrix(Matrix_Type ntype) { return nullptr; }\nbool BoomerAMGSolver::SetSparseMatrix(SparseMatrix* pA) { return false; }\nbool BoomerAMGSolver::PreProcess() { return false; }\nbool BoomerAMGSolver::Factor() { return false; }\nbool BoomerAMGSolver::BackSolve(double* x, double* y) { return false; }\nvoid BoomerAMGSolver::Destroy() {}\n#endif\n"
  },
  {
    "path": "NumCore/BoomerAMGSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n\nclass BoomerAMGSolver : public LinearSolver\n{\n\tclass Implementation;\n\npublic:\n\tenum InterpolationType {\n\t\tINTERPOL_OP_0 = 0,\t\t\t// classicial modified interpolation\n\t\tINTERPOL_OP_1 = 1,\t\t\t// LS interpolation (for use with GSMG)\n\t\tINTERPOL_OP_2 = 2,\t\t\t// classical modified interpolation for hyperbolic PDEs\n\t\tINTERPOL_OP_3 = 3,\t\t\t// direct interpolation (with separation of weights)\n\t\tINTERPOL_OP_4 = 4,\t\t\t// multipass interpolation\n\t\tINTERPOL_OP_5 = 5,\t\t\t// multipass interpolation (with separation of weights)\n\t\tINTERPOL_OP_6 = 6,\t\t\t// extended+i interpolation\t\n\t\tINTERPOL_OP_7 = 7,\t\t\t// extended+i (if no common C neighbor) interpolation\n\t\tINTERPOL_OP_8 = 8,\t\t\t// standard interpolation\n\t\tINTERPOL_OP_9 = 9,\t\t\t// standard interpolation (with separation of weights)\n\t\tINTERPOL_OP_10 = 10,\t\t// classical block interpolation (for use with nodal systems version only)\n\t\tINTERPOL_OP_11 = 11,\t\t// like 10, with diagonalized blocks\n\t\tINTERPOL_OP_12 = 12,\t\t// FF interpolation\n\t\tINTERPOL_OP_13 = 13,\t\t// FF1 interpolation\n\t\tINTERPOL_OP_14 = 14,\t\t// extended interpolation\n\t};\n\n\tenum RelaxationType {\n\t\tJACOBI = 0,\t\t\t\t\t\t\t// Jacobi\n\t\tGAUSS_SEIDEL_SEQ = 1,\t\t\t\t// Gauss-Seidel, sequential (very slow!)\n\t\tGAUSS_SEIDEL_PAR = 2,\t\t\t\t// Gauss-Seidel, interior points in parallel, boundary sequential (slow!)\n\t\tSOR_FORWARD = 3,\t\t\t\t\t// hybrid Gauss-Seidel or SOR, forward solve\n\t\tSOR_BACKWARD = 4,\t\t\t\t\t// hybrid Gauss-Seidel or SOR, backward solve\n\t\tCHAOTIC_GUASS_SEIDEL = 5,\t\t\t// hybrid chaotic Gauss-Seidel (works only with OpenMP)\n\t\tSYMMETRIC_GAUSS_SEIDEL = 6,\t\t\t// hybrid symmetric Gauss-Seidel or SSOR\n\t\tSYMMETRIC_GAUSS_SEIDEL_L1 = 8,\t\t// l1-scaled hybrid symmetric Gauss-Seidel\n\t\tGAUSSIAN_ELIMINATION = 9,\t\t\t// Gaussian elimination (only on coarsest level)\n\t\tCG = 15,\t\t\t\t\t\t\t// CG (warning - not a fixed smoother - may require RGMRES)\n\t\tCHEBYSHEV  = 16,\t\t\t\t\t// Chebyshev\n\t\tFCF_JACOBI = 17,\t\t\t\t\t// FCF-Jacobi\n\t\tJACOBI_L1  = 18\t\t\t\t\t\t// l1-scaled Jacobi\n\t};\n\n\tenum CoarsenType {\n\t\tCOARSE_OP_0 = 0,\t\t\t// CLJP-coarsening (a parallel coarsening algorithm using independent sets)\n\t\tCOARSE_OP_1 = 1,\t\t\t// classical Ruge-Stueben coarsening, no boundary treatment (not recommended!)\n\t\tCOARSE_OP_3 = 3,\t\t\t// classical Ruge-Stueben coarsening, with boundary treatment\n\t\tCOARSE_OP_6 = 6,\t\t\t// Falgout coarsening\n\t\tCOARSE_OP_7 = 7,\t\t\t// CLJP-coarsening (using fix random vector, for debugging purposes only)\n\t\tCOARSE_OP_8 = 8,\t\t\t// PMIS-coarsening (might lead to slower convergence)\n\t\tCOARSE_OP_9 = 9,\t\t\t// PMIS-coarsening (using a fixed random vector ,for debugging purposes only)\n\t\tCOARSE_OP_10 = 10,\t\t\t// HMIS-coarsening\n\t\tCOARSE_OP_11 = 11,\t\t\t// one-pass Ruge-Stueben coarsening (not recommended!)\n\t\tCOARSE_OP_21 = 21,\t\t\t// CGC coarsening\n\t\tCOARSE_OP_22 = 22,\t\t\t// CGC-E coarsening\n\t};\n    \n    enum AggInterpType {\n        AGG_IT_0 = 0,\n        AGG_IT_5 = 5,\n        AGG_IT_7 = 7,\n    };\n\npublic:\n\tBoomerAMGSolver(FEModel* fem);\n\t~BoomerAMGSolver();\n\npublic:\n\tvoid SetPrintLevel(int printLevel) override;\n\tvoid SetMaxIterations(int maxIter);\n\tvoid SetConvergenceTolerance(double tol);\n\tvoid SetMaxLevels(int levels);\n\tvoid SetCoarsenType(int coarsenType);\n\tvoid SetUseNumFunctions(bool b);\n    void SetRelaxType(int rlxtyp);\n    void SetInterpType(int inptyp);\n    void SetStrongThreshold(double thresh);\n    void SetPMaxElmts(int pmax);\n    void SetNumSweeps(int nswp);\n    void SetAggInterpType(int aggit);\n    void SetAggNumLevels(int anlv);\n\tvoid SetNodal(int nodal);\n\tvoid SetJacobiPC(bool b);\n\tvoid SetFailOnMaxIterations(bool b);\n\tbool GetJacobiPC();\n\npublic:\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\nprivate:\n\tImplementation*\timp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/FEASTEigenSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"FEASTEigenSolver.h\"\n#include <FECore/CompactSymmMatrix.h>\n\n\nBEGIN_FECORE_CLASS(FEASTEigenSolver, EigenSolver)\n\tADD_PARAMETER(m_m0, \"m0\");\n\tADD_PARAMETER(m_emin, \"emin\");\n\tADD_PARAMETER(m_emax, \"emax\");\nEND_FECORE_CLASS();\n\n#ifdef MKL_ISS\n#undef PARDISO\n#include <mkl.h>\n\nFEASTEigenSolver::FEASTEigenSolver(FEModel* fem) : EigenSolver(fem)\n{\n\tm_emin = 0.0;\n\tm_emax = 0.0;\n\tm_m0 = 0;\n}\n\nbool FEASTEigenSolver::Init()\n{\n\t// initialize the FEAST solver\n\tfeastinit(m_fpm);\n\n\t// fpm[0] = Specifies whether Extended Eigensolver routines print runtime status. (0 = no -default-, 1 = yes)\n\t// fpm[1] = Number of contour points Ne = 8. Must be one of {3,4,5,6,8,10,12,16,20,24,32,40,48}\n\t// fpm[2] = Error trace double precision stopping criteria eps = 10^-fpm[2] (default = 12)\n\t// fpm[3] = Maximum number of Extended Eigensolver refinement loops allowed. \n\t//          If no convergence is reached within fpm[3] refinement loops, Extended Eigensolver routines return info=2\n\t// fpm[4] = User initial subspace. If fpm[4]=0 then Extended Eigensolver routines generate initial subspace, \n\t//          if fpm[4]=1 the user supplied initial subspace is used.\n\t// fpm[5] = Extended Eigensolver stopping test.\n\t//          fpm[5]=0 (default): Extended Eigensolvers are stopped if this residual stopping test is satisfied.\n\t//          fpm[5]=1 : Extended Eigensolvers are stopped if this trace stopping test is satisfied.\n\t// fpm[6] = Error trace single precision stopping criteria(10 - fpm[6]) .(default = 5)\n\t// fpm[13] = fpm[13] = 0: Standard use for Extended Eigensolver routines. (default)\n\t//\t\t     fpm[13] = 1: Non-standard use for Extended Eigensolver routines : return the computed eigenvectors subspace after one single contour integration.\n\t// fpm[26] = Specifies whether Extended Eigensolver routines check input matrices(applies to CSR format only). (0 = no -default-, 1 = yes)\n\t// fpm[27] = Check if matrix B is positive definite.Set fpm[27] = 1 to check if B is positive definite. (default = 0)\n\t// fpm[63] = Use the Intel MKL PARDISO solver with the user - defined PARDISO iparm array settings.\n\t//           fpm[63]=0 (default): Extended Eigensolver routines use the Intel MKL PARDISO default iparm settings defined by calling the pardisoinit subroutine.\n\t//\t         fpm[63]=1 : The values from fpm[64] to fpm[127] correspond to iparm[0] to iparm[63] respectively according to the formula fpm[64 + i] = iparm[i] for i = 0, 1, ..., 63\n\n#ifndef NDEBUG\n\tm_fpm[0] = 1; // turn on FEAST output\n#endif\n\n\treturn true;\n}\n\nbool FEASTEigenSolver::EigenSolve(SparseMatrix* A, SparseMatrix* B, vector<double>& eigenValues, matrix& eigenVectors)\n{\n\tCompactSymmMatrix* cmA = dynamic_cast<CompactSymmMatrix*>(A);\n\tif (cmA == nullptr) return false;\n\n\tCompactSymmMatrix* cmB = nullptr; \n\tif (B)\n\t{\n\t\tcmB = dynamic_cast<CompactSymmMatrix*>(B);\n\t\tif (cmB == nullptr) return false;\n\n\t\t// make sure the matrices have the same size\n\t\tif (cmA->Rows() != cmB->Rows()) return false;\n\t}\n\n\t// parameters to dfeast_scsrgv\n\t// Input parameters:\n\tconst char* uplo = \"U\"; // the matrix is stored as lower triangular. \n\tMKL_INT n = cmA->Rows();\n\tdouble* a = cmA->Values(); // nonzero values of matrix A\n\tMKL_INT* ai = cmA->Pointers();\n\tMKL_INT* aj = cmA->Indices(); \n\n\t// Output parameters\n\tMKL_INT m = 0; // number of eigenvalues found in [emin, emax], 0 <= m <= m0.\n\tdouble epsout = 0; // relative error on the trace\n\tMKL_INT loops = 0; // number of refinement loops executed.\n\n\teigenValues.resize(m_m0, 0.0);\n\tdouble* e = &eigenValues[0];\n\n\teigenVectors.resize(n, m_m0);\n\tdouble* x = eigenVectors[0];\n\n\tvector<double> res(m_m0); // the first m entries contain the relative residual error. \n\n\tMKL_INT info = 0; // return value. If 0, all is well. \n\n\tif (B)\n\t{\n\t\tdouble* b = cmB->Values(); // nonzero values of matrix B\n\t\tMKL_INT* bi = cmB->Pointers();\n\t\tMKL_INT* bj = cmB->Indices();\n\t\tdfeast_scsrgv(uplo, &n, a, ai, aj, b, bi, bj, m_fpm, &epsout, &loops, &m_emin, &m_emax, &m_m0, e, x, &m, &res[0], &info);\n\t}\n\telse\n\t{\n\t\tdfeast_scsrev(uplo, &n, a, ai, aj, m_fpm, &epsout, &loops, &m_emin, &m_emax, &m_m0, e, x, &m, &res[0], &info);\n\t}\n\n\tif (m == 0)\n\t{\n\t\teigenValues.clear();\n\t}\n\telse if (m != m_m0) eigenValues.resize(m);\n\n\treturn (info == 0);\n}\n#else \nFEASTEigenSolver::FEASTEigenSolver(FEModel* fem) : EigenSolver(fem)  {}\nbool FEASTEigenSolver::Init() { return false; }\nbool FEASTEigenSolver::EigenSolve(SparseMatrix* A, SparseMatrix* B, vector<double>& eigenValues, matrix& eigenVectors) { return false; }\n#endif"
  },
  {
    "path": "NumCore/FEASTEigenSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/SparseMatrix.h>\n#include <FECore/matrix.h>\n#include <FECore/EigenSolver.h>\n\nclass FEASTEigenSolver : public EigenSolver\n{\npublic:\n\tFEASTEigenSolver(FEModel* fem);\n\n\tbool Init() override;\n\n\tbool EigenSolve(SparseMatrix* A, SparseMatrix* B, vector<double>& eigenValues, matrix& eigenVectors) override;\n\nprivate:\n\tint\tm_fpm[128];\n\tdouble\tm_emin, m_emax;\n\tint\tm_m0;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/FGMRESSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"FGMRESSolver.h\"\n#include <FECore/CompactSymmMatrix.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/log.h>\n#include \"MatrixTools.h\"\n\n//-----------------------------------------------------------------------------\n// We must undef PARDISO since it is defined as a function in mkl_solver.h\n#ifdef MKL_ISS\n#ifdef PARDISO\n#undef PARDISO\n#endif\n#include \"mkl_rci.h\"\n#include \"mkl_blas.h\"\n#include \"mkl_spblas.h\"\n#endif // MKL_ISS\n\nBEGIN_FECORE_CLASS(FGMRESSolver, IterativeLinearSolver)\n\tADD_PARAMETER(m_maxiter       , \"max_iter\");\n\tADD_PARAMETER(m_print_level   , \"print_level\");\n\tADD_PARAMETER(m_doResidualTest, \"check_residual\");\n\tADD_PARAMETER(m_nrestart      , \"max_restart\");\n\tADD_PARAMETER(m_reltol        , \"tol\");\n\tADD_PARAMETER(m_abstol        , \"abs_tol\");\n\tADD_PARAMETER(m_maxIterFail   , \"fail_max_iters\");\n\n\tADD_PROPERTY(m_P, \"pc_left\")->SetFlags(FEProperty::Optional);\n\tADD_PROPERTY(m_R, \"pc_right\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nFGMRESSolver::FGMRESSolver(FEModel* fem) : IterativeLinearSolver(fem), m_pA(0)\n{\n\tm_maxiter = 0; // use default min(N, 150)\n\tm_print_level = 0;\n\tm_doResidualTest = true;\n\tm_doZeroNormTest = true;\n\tm_reltol = 0.0;\n\tm_abstol = 0.0;\n\tm_nrestart = 0; // use default = maxiter\n\tm_print_cn = false;\n\n\tm_do_jacobi = false;\n\n\tm_P = 0;\t// we don't use a preconditioner for this solver\n\tm_R = 0;\t// no right preconditioner\n\n\tm_maxIterFail = true;\n}\n\n//-----------------------------------------------------------------------------\n// set the preconditioner\nvoid FGMRESSolver::SetLeftPreconditioner(LinearSolver* P)\n{\n\tm_P = dynamic_cast<Preconditioner*>(P);\n}\n\n//-----------------------------------------------------------------------------\n//! Set the right preconditioner\nvoid FGMRESSolver::SetRightPreconditioner(LinearSolver* R)\n{\n\tm_R = dynamic_cast<Preconditioner*>(R);\n}\n\n//-----------------------------------------------------------------------------\n// get the preconditioner\nLinearSolver* FGMRESSolver::GetLeftPreconditioner()\n{\n\treturn m_P;\n}\n\n//-----------------------------------------------------------------------------\n// get the preconditioner\nLinearSolver* FGMRESSolver::GetRightPreconditioner()\n{\n\treturn m_R;\n}\n\n//-----------------------------------------------------------------------------\n//! Set max nr of iterations\nvoid FGMRESSolver::SetMaxIterations(int n)\n{\n\tm_maxiter = n;\n}\n\n//-----------------------------------------------------------------------------\n//! Get the max nr of iterations\nint FGMRESSolver::GetMaxIterations() const\n{\n\treturn m_maxiter;\n}\n\n//-----------------------------------------------------------------------------\n//! Set the nr of non-restarted iterations\nvoid FGMRESSolver::SetNonRestartedIterations(int n)\n{\n\tm_nrestart = n;\n}\n\n//-----------------------------------------------------------------------------\n// Set the print level\nvoid FGMRESSolver::SetPrintLevel(int n)\n{\n\tm_print_level = n;\n}\n\n//-----------------------------------------------------------------------------\n// set residual stopping test flag\nvoid FGMRESSolver::DoResidualStoppingTest(bool b)\n{\n\tm_doResidualTest = b;\n}\n\n//-----------------------------------------------------------------------------\n// set zero norm stopping test flag\nvoid FGMRESSolver::DoZeroNormStoppingTest(bool b)\n{\n\tm_doZeroNormTest = b;\n}\n\n//-----------------------------------------------------------------------------\n// set the convergence tolerance for the residual stopping test\nvoid FGMRESSolver::SetRelativeResidualTolerance(double tol)\n{\n\tm_reltol = tol;\n}\n\n//-----------------------------------------------------------------------------\n// set the absolute convergence tolerance for the residual stopping test\nvoid FGMRESSolver::SetAbsoluteResidualTolerance(double tol)\n{\n\tm_abstol = tol;\n}\n\n//-----------------------------------------------------------------------------\n//! This solver does not use a preconditioner\nbool FGMRESSolver::HasPreconditioner() const \n{\n\treturn ((m_P != 0) || (m_R != 0)); \n}\n\n//-----------------------------------------------------------------------------\nvoid FGMRESSolver::FailOnMaxIterations(bool b)\n{\n\tm_maxIterFail = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid FGMRESSolver::PrintConditionNumber(bool b)\n{\n\tm_print_cn = b;\n}\n\n//-----------------------------------------------------------------------------\n// do jacobi preconditioning\nvoid FGMRESSolver::DoJacobiPreconditioning(bool b)\n{\n\tm_do_jacobi = b;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* FGMRESSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n#ifdef MKL_ISS\n\t// Cleanup if necessary\n\tif (m_pA) delete m_pA; \n\tm_pA = nullptr;\n\n\t// since FMGRES doesn't really care what matrix is requested, \n\t// see if the preconditioner cares.\n\tif (m_P)\n\t{\n\t\tm_P->SetPartitions(m_part);\n\t\tm_pA = m_P->CreateSparseMatrix(ntype);\n\t\treturn m_pA;\n\t}\n\telse if (m_R)\n\t{\n\t\tm_R->SetPartitions(m_part);\n\t\tm_pA = m_R->CreateSparseMatrix(ntype);\n\t\treturn m_pA;\n\t}\n\n\t// if the matrix is still zero, let's just allocate one\n\tif (m_pA == nullptr)\n\t{\n\t\t// allocate new matrix\n\t\tswitch (ntype)\n\t\t{\n\t\tcase REAL_SYMMETRIC: m_pA = new CompactSymmMatrix(1); break;\n\t\tcase REAL_UNSYMMETRIC: m_pA = new CRSSparseMatrix(1); break;\n\t\tcase REAL_SYMM_STRUCTURE: m_pA = new CRSSparseMatrix(1); break;\n\t\t}\n\t}\n\n\t// return the matrix (Can be null if matrix format not supported!)\n\treturn m_pA;\n#else\n\treturn 0;\n#endif\n}\n\n//-----------------------------------------------------------------------------\nbool FGMRESSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n\tm_pA = pA;\n\treturn (m_pA != 0);\n}\n\n//-----------------------------------------------------------------------------\n//! Clean up\nvoid FGMRESSolver::Destroy()\n{\n\tm_tmp.clear();\n\tm_tmp.shrink_to_fit();\n}\n\n//-----------------------------------------------------------------------------\nbool FGMRESSolver::PreProcess() \n{\n#ifdef MKL_ISS\n\t// number of equations\n\tMKL_INT N = m_pA->Rows();\n\n\tint M = (N < 150 ? N : 150); // this is the default value of ipar[14]\n\n\tif (m_nrestart > 0) M = m_nrestart;\n\telse if (m_maxiter > 0) M = m_maxiter;\n\n\t// allocate temp storage\n\tm_tmp.resize((N*(2 * M + 1) + (M*(M + 9)) / 2 + 1));\n\n\tm_Rv.resize(N);\n\n\tm_W.resize(N, 1.0);\n\n\treturn true; \n#else\n\treturn false;\n#endif\n}\n\n\n//! Factor the matrix\nbool FGMRESSolver::Factor()\n{\n\tint neq = m_pA->Rows();\n\tif (m_do_jacobi)\n\t{\n\t\tfor (int i = 0; i < neq; ++i)\n\t\t{\n\t\t\tdouble dii = fabs(m_pA->diag(i));\n\t\t\tif (dii == 0.0) return false;\n\t\t\tm_W[i] = 1.0 / sqrt(dii);\n\t\t}\n\t}\n\n\tif (m_do_jacobi)\n\t\tm_pA->scale(m_W, m_W);\n\n\tif (m_print_cn)\n\t{\n\t\tdouble c = NumCore::estimateConditionNumber(GetSparseMatrix());\n\t\tfeLog(\"\\tcondition number (est.) ................... : %lg\\n\\n\", c);\n\t}\n\n\t// call the preconditioner\n\tif (m_P)\n\t{\n\t\tm_P->SetFEModel(GetFEModel());\n\t\tif (m_P->PreProcess() == false) return false;\n\t\tif (m_P->Factor() == false) return false;\n\t}\n\n\tif (m_R)\n\t{\n\t\tm_R->SetFEModel(GetFEModel());\n\t\tif (m_R->PreProcess() == false) return false;\n\t\tif (m_R->Factor() == false) return false;\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool FGMRESSolver::BackSolve(double* x, double* b)\n{\n#ifdef MKL_ISS\n\t// make sure we have a matrix\n\tif (m_pA == 0) return false;\n\n\t// number of equations\n\tMKL_INT N = m_pA->Rows();\n\n\t// data allocation\n\tint M = (N < 150 ? N : 150); // this is the default value of ipar[4] and ipar[14]\n\n\tint nrestart = M;\n\tif (m_nrestart > 0) nrestart = m_nrestart;\n\telse if (m_maxiter > 0) nrestart = m_maxiter;\n\n\tint maxIter = M;\n\tif (m_maxiter > 0) maxIter = m_maxiter;\n\n\t// scale rhs\n\tvector<double> F(N);\n\tfor (int i = 0; i < N; ++i) F[i] = m_W[i] * b[i];\n\n\t// initialize the solver\n\tMKL_INT ipar[128] = { 0 };\n\tdouble dpar[128] = { 0.0 };\n\tMKL_INT ivar = N;\n\tMKL_INT RCI_request;\n\tdfgmres_init(&ivar, &x[0], &F[0], &RCI_request, ipar, dpar, &m_tmp[0]);\n\tif (RCI_request != 0) { MKL_Free_Buffers(); return false; }\n\n\t// Set the desired parameters:\n\tipar[ 4] = maxIter;\t                        // max number of iterations\n\tipar[ 7] = 1;\t\t\t\t\t\t\t\t// do the stopping test for maximal number of iterations\n\tipar[ 8] = (m_doResidualTest ? 1 : 0);\t\t// do residual stopping test\n\tipar[ 9] = 0;\t\t\t\t\t\t\t\t// do not request for the user defined stopping test\n\tipar[10] = (m_P != 0 ? 1 : 0);\t\t\t\t// do the pre-conditioned version of the FGMRES iterative solver\n\tipar[11] = (m_doZeroNormTest ? 1 : 0);\t\t// do the check of the norm of the next generated vector automatically\n\tipar[14] = nrestart;\t                    // number of non-restarted iterations\n\tif (m_reltol > 0) dpar[0] = m_reltol;\t\t// set the relative tolerance\n\tif (m_abstol > 0) dpar[1] = m_abstol;\t\t// set the absolute tolerance\n\n\t// Check the correctness and consistency of the newly set parameters\n\tdfgmres_check(&ivar, &x[0], &F[0], &RCI_request, ipar, dpar, &m_tmp[0]);\n\tif (RCI_request != 0) { MKL_Free_Buffers(); return false; }\n\n\t// zero solution vector\n\tfor (int i = 0; i < N; ++i) x[i] = 0.0;\n\n\tif (m_print_level > 0) feLog(\"FGMRES:\\n\");\n\n\t// solve the problem\n\tbool bdone = false;\n\tbool bconverged = !m_maxIterFail;\n\twhile (!bdone)\n\t{\n\t\t// compute the solution via FGMRES\n\t\tdfgmres(&ivar, &x[0], &F[0], &RCI_request, ipar, dpar, &m_tmp[0]);\n\n\t\tswitch (RCI_request)\n\t\t{\n\t\tcase 0: // solution converged. \n\t\t\tbdone = true;\n\t\t\tbconverged = true;\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t{\n\t\t\t\t// do matrix-vector multiplication\n\t\t\t\tif (m_R)\n\t\t\t\t{\n\t\t\t\t\t// first apply the right preconditioner\n\t\t\t\t\tm_R->mult_vector(&m_tmp[ipar[21] - 1], &m_Rv[0]);\n\n\t\t\t\t\t// then multiply with matrix\n\t\t\t\t\tm_pA->mult_vector(&m_Rv[0], &m_tmp[ipar[22] - 1]);\n\t\t\t\t}\n\t\t\t\telse m_pA->mult_vector(&m_tmp[ipar[21] - 1], &m_tmp[ipar[22] - 1]);\n\n\t\t\t\tif (m_print_level > 1)\n\t\t\t\t{\n\t\t\t\t\tfeLog(\"%3d = %lg (%lg), %lg (%lg)\\n\", ipar[3], dpar[4], dpar[3], dpar[6], dpar[7]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 3:\t// do the pre-conditioning step\n\t\t\t{\n\t\t\t\tassert(m_P);\n\t\t\t\tif (m_P->mult_vector(&m_tmp[ipar[21] - 1], &m_tmp[ipar[22] - 1]) == false)\n\t\t\t\t{\n\t\t\t\t\tbdone = true;\n\t\t\t\t\tbconverged = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tbreak;\n\t\tdefault:\t// something went wrong\n\t\t\tbdone = true;\n\t\t\tbconverged = !m_maxIterFail;\n\t\t}\n\t}\n\n\t// get the solution. \n\tMKL_INT itercount;\n\n\tdfgmres_get(&ivar, &x[0], &F[0], &RCI_request, ipar, dpar, &m_tmp[0], &itercount);\n\n\tif (m_do_jacobi)\n\t{\n\t\tfor (int i = 0; i < N; ++i) x[i] *= m_W[i];\n\t}\n\n\tif (m_R)\n\t{\n\t\tm_R->mult_vector(&x[0], &m_Rv[0]);\n\t\tfor (int i = 0; i < N; ++i) x[i] = m_Rv[i];\n\t}\n\n\tif (m_print_level > 0)\n\t{\n\t\tfeLog(\"%3d = %lg (%lg), %lg (%lg)\\n\", ipar[3]+1, dpar[4], dpar[3], dpar[6], dpar[7]);\n\t}\n\n//\tMKL_Free_Buffers();\n\n\t// update stats\n\tUpdateStats(itercount);\n\n\treturn bconverged;\n\n#else\n\treturn false;\n#endif // MKL_ISS\n}\n\n//! convenience function for solving linear system Ax = b\nbool FGMRESSolver::Solve(SparseMatrix* A, vector<double>& x, vector<double>& b)\n{\n\tSetSparseMatrix(A);\n\tif (PreProcess() == false) return false;\n\tif (Factor() == false) return false;\n\treturn BackSolve(&x[0], &b[0]);\n}\n"
  },
  {
    "path": "NumCore/FGMRESSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/Preconditioner.h>\n#include <FECore/SparseMatrix.h>\n\n//-----------------------------------------------------------------------------\n//! This class implements an interface to the MKL FGMRES iterative solver for \n//! nonsymmetric indefinite matrices (without pre-conditioning).\nclass FGMRESSolver : public IterativeLinearSolver\n{\npublic:\n\t//! constructor\n\tFGMRESSolver(FEModel* fem);\n\n\t//! do any pre-processing (allocates temp storage)\n\tbool PreProcess() override;\n\n\t//! Factor the matrix\n\tbool Factor() override;\n\n\t//! Calculate the solution of RHS b and store solution in x\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Clean up\n\tvoid Destroy() override;\n\n\t//! Return a sparse matrix compatible with this solver\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! Set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\n\t//! Set max nr of iterations\n\tvoid SetMaxIterations(int n);\n\n\t//! Get the max nr of iterations\n\tint GetMaxIterations() const;\n\n\t//! Set the nr of non-restarted iterations\n\tvoid SetNonRestartedIterations(int n);\n\n\t// Set the print level\n\tvoid SetPrintLevel(int n) override;\n\n\t// set residual stopping test flag\n\tvoid DoResidualStoppingTest(bool b);\n\n\t// set zero norm stopping test flag\n\tvoid DoZeroNormStoppingTest(bool b);\n\n\t// set the relative convergence tolerance for the residual stopping test\n\tvoid SetRelativeResidualTolerance(double tol);\n\n\t// set the absolute convergence tolerance for the residual stopping test\n\tvoid SetAbsoluteResidualTolerance(double tol);\n\n\t//! This solver does not use a preconditioner\n\tbool HasPreconditioner() const override;\n\n\t//! convenience function for solving linear system Ax = b\n\tbool Solve(SparseMatrix* A, vector<double>& x, vector<double>& b);\n\n\t//! fail if max iterations reached\n\tvoid FailOnMaxIterations(bool b);\n\n\t//! print the condition number\n\tvoid PrintConditionNumber(bool b);\n\n\t// do jacobi preconditioning\n\tvoid DoJacobiPreconditioning(bool b);\n\npublic:\n\t// set the preconditioner\n\tvoid SetLeftPreconditioner(LinearSolver* P) override;\n\tvoid SetRightPreconditioner(LinearSolver* P) override;\n\n\t// get the preconditioner\n\tLinearSolver* GetLeftPreconditioner() override;\n\tLinearSolver* GetRightPreconditioner() override;\n\nprotected:\n\tSparseMatrix* GetSparseMatrix() { return m_pA; }\n\nprivate:\n\tint\t\tm_maxiter;\t\t\t// max nr of iterations\n\tint\t\tm_nrestart;\t\t\t// max nr of non-restarted iterations\n\tint\t\tm_print_level;\t\t// output level\n\tbool\tm_doResidualTest;\t// do the residual stopping test\n\tbool\tm_doZeroNormTest;\t// do the zero-norm stopping test\n\tdouble\tm_reltol;\t\t\t// relative residual convergence tolerance\n\tdouble\tm_abstol;\t\t\t// absolute residual tolerance\n\tbool\tm_maxIterFail;\n\tbool\tm_print_cn;\t\t\t// Calculate and print the condition number\n\tbool\tm_do_jacobi;\n\nprivate:\n\tSparseMatrix*\tm_pA;\t\t//!< the sparse matrix format\n\tPreconditioner*\tm_P;\t\t//!< the left preconditioner\n\tPreconditioner*\tm_R;\t\t//!< the right preconditioner\n\tvector<double>\tm_tmp;\n\tvector<double>\tm_Rv;\t\t//!< used when a right preconditioner is ued\n\tvector<double>\tm_W;\t\t//!< Jacobi preconditioner\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/HypreGMRESsolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"HypreGMRESsolver.h\"\n#ifdef HYPRE\n#include <HYPRE.h>\n#include <HYPRE_IJ_mv.h>\n#include <HYPRE_parcsr_mv.h>\n#include <HYPRE_parcsr_ls.h>\n#include <_hypre_utilities.h>\n#include <_hypre_IJ_mv.h>\n#include <HYPRE_krylov.h>\n\n\nclass HypreGMRESsolver::Implementation\n{\npublic:\n\tCRSSparseMatrix*\tA;\t// global matrix\n\n\tvector<int>\t\t\tind; // indices array\n\n\t// Hypre stuff\n\tHYPRE_IJMatrix\t\tij_A;\n\tHYPRE_ParCSRMatrix\tpar_A;\n\tHYPRE_Solver\t\tsolver;\n\tHYPRE_Solver\t\tprecond;\n\tHYPRE_IJVector\t\tij_b, ij_x;\n\tHYPRE_ParVector\t\tpar_b, par_x;\n\npublic:\n\t// control parameters\n\tint\t\tm_maxiter;\n\tdouble\tm_tol;\n\tint\t\tm_print_level;\n\npublic:\n\tImplementation() : A(0)\n\t{\n\t\tm_print_level = 0;\n\t\tm_maxiter = 1000;\n\t\tm_tol = 1e-7;\n\t}\n\n\tbool isValid() const\n\t{\n\t\treturn (A != 0);\n\t}\n\n\tint equations() const { return (A ? A->Rows() : 0); }\n\n\t// Allocate stiffness matrix\n\tvoid allocMatrix()\n\t{\n\t\tint neq = equations();\n\n\t\t// Create an empty matrix object\n\t\tint ret = 0;\n\t\tret = HYPRE_IJMatrixCreate(MPI_COMM_WORLD, 0, neq - 1, 0, neq - 1, &ij_A);\n\n\t\t// set the matrix object type\n\t\tret = HYPRE_IJMatrixSetObjectType(ij_A, HYPRE_PARCSR);\n\t}\n\n\t// destroy stiffness matrix\n\tvoid destroyMatrix()\n\t{\n\t\tHYPRE_IJMatrixDestroy(ij_A);\n\t}\n\n\t// Allocate vectors for rhs and solution\n\tvoid allocVectors()\n\t{\n\t\tint neq = equations();\n\t\tind.resize(neq, 0);\n\t\tfor (int i = 0; i<neq; ++i) ind[i] = i;\n\n\t\t// create the vector object for the rhs\n\t\tHYPRE_IJVectorCreate(MPI_COMM_WORLD, 0, neq - 1, &ij_b);\n\t\tHYPRE_IJVectorSetObjectType(ij_b, HYPRE_PARCSR);\n\n\t\t// create the vector object for the solution\n\t\tHYPRE_IJVectorCreate(MPI_COMM_WORLD, 0, neq - 1, &ij_x);\n\t\tHYPRE_IJVectorSetObjectType(ij_x, HYPRE_PARCSR);\n\t}\n\n\t// destroy vectors\n\tvoid destroyVectors()\n\t{\n\t\tHYPRE_IJVectorDestroy(ij_b);\n\t\tHYPRE_IJVectorDestroy(ij_x);\n\t}\n\n\t// update vectors \n\tvoid updateVectors(double* x, double* b)\n\t{\n\t\t// initialize vectors for changing coefficient values\n\t\tHYPRE_IJVectorInitialize(ij_b);\n\t\tHYPRE_IJVectorInitialize(ij_x);\n\n\t\t// set the values\n\t\tint neq = equations();\n\t\tHYPRE_IJVectorSetValues(ij_b, neq, (HYPRE_Int*)&ind[0], b);\n\t\tHYPRE_IJVectorSetValues(ij_x, neq, (HYPRE_Int*)&ind[0], x);\n\n\t\t// finialize assembly\n\t\tHYPRE_IJVectorAssemble(ij_b);\n\t\tHYPRE_IJVectorAssemble(ij_x);\n\n\t\tHYPRE_IJVectorGetObject(ij_b, (void**)&par_b);\n\t\tHYPRE_IJVectorGetObject(ij_x, (void**)&par_x);\n\t}\n\n\t// update coefficient matrix\n\tvoid updateMatrix()\n\t{\n\t\tint neq = equations();\n\n\t\t// call initialize, after which we can set the matrix coefficients\n\t\tHYPRE_Int ret = HYPRE_IJMatrixInitialize(ij_A);\n\n\t\t// set the matrix coefficients\n\t\tdouble* values = A->Values();\n\t\tint* indices = A->Indices();\n\t\tint* pointers = A->Pointers();\n\t\tfor (int i = 0; i<neq; ++i)\n\t\t{\n\t\t\tconst int* cols = indices + pointers[i];\n\t\t\tint ncols = pointers[i + 1] - pointers[i];\n\t\t\tdouble* vals = values + pointers[i];\n\t\t\tHYPRE_Int nrows = 1;\n\t\t\tret = HYPRE_IJMatrixSetValues(ij_A, nrows, (HYPRE_Int*)&ncols, (HYPRE_Int*)&i, (HYPRE_Int*)cols, vals);\n\t\t}\n\n\t\t// Finalize the matrix assembly\n\t\tret = HYPRE_IJMatrixAssemble(ij_A);\n\n\t\t// get the matrix object for later use\n\t\tret = HYPRE_IJMatrixGetObject(ij_A, (void**)&par_A);\n\t}\n\n\t// allocate preconditioner\n\tvoid allocPrecond()\n\t{\n\t\t// Now set up the AMG preconditioner and specify any parameters\n\t\tHYPRE_BoomerAMGCreate(&precond);\n//\t\tHYPRE_BoomerAMGSetPrintLevel(imp->precond, 1); /* print amg solution info */\n//\t\tHYPRE_BoomerAMGSetCoarsenType(imp->precond, 6);\n\t\tHYPRE_BoomerAMGSetCoarsenType(precond, 10); /* HMIS-coarsening */\n\t\tHYPRE_BoomerAMGSetInterpType(precond, 6); /* extended+i interpolation */\n\t\tHYPRE_BoomerAMGSetPMaxElmts(precond, 4);\n\t\tHYPRE_BoomerAMGSetAggNumLevels(precond, 2);\n//\t\tHYPRE_BoomerAMGSetOldDefault(precond);\n//\t\tHYPRE_BoomerAMGSetRelaxType(precond, 6); /* Sym G.S./Jacobi hybrid */\n\t\tHYPRE_BoomerAMGSetRelaxType(precond, 3); /* hybrid Gauss-Seidel or SOR, forward solve */\n\t\tHYPRE_BoomerAMGSetStrongThreshold(precond, 0.5);\n\t\tHYPRE_BoomerAMGSetNumSweeps(precond, 1);\n//\t\tHYPRE_BoomerAMGSetTol(precond, 0.0); /* conv. tolerance zero */\n\t//\tHYPRE_BoomerAMGSetMaxIter(precond, 1); /* do only one iteration! */\n\t}\n\n\t// destroy preconditioner\n\tvoid destroyPrecond()\n\t{\n\t\tif (precond) HYPRE_BoomerAMGDestroy(precond);\n\t}\n\n\t// allocate solver\n\tvoid allocSolver()\n\t{\n\t\t// Create the solver object\n\t\tHYPRE_ParCSRFlexGMRESCreate(MPI_COMM_WORLD, &solver);\n\n\t\t/* Set some parameters (See Reference Manual for more parameters) */\n\t\tint    restart = 30;\n\t\tHYPRE_FlexGMRESSetKDim(solver, restart);\n\t\tHYPRE_FlexGMRESSetMaxIter(solver, m_maxiter); /* max iterations */\n\t\tHYPRE_FlexGMRESSetTol(solver, m_tol); /* conv. tolerance */\n\t\t//\tHYPRE_FlexGMRESSetPrintLevel(imp->solver, 2); /* print solve info */\n\t\tHYPRE_FlexGMRESSetLogging(solver, 1); /* needed to get run info later */\n\n\t\t// Set the preconditioner\n\t\tHYPRE_FlexGMRESSetPrecond(solver, (HYPRE_PtrToSolverFcn)HYPRE_BoomerAMGSolve,\n\t\t\t(HYPRE_PtrToSolverFcn)HYPRE_BoomerAMGSetup, precond);\n\t}\n\n\t// destroy the solver\n\tvoid destroySolver()\n\t{\n\t\tHYPRE_ParCSRFlexGMRESDestroy(solver);\n\t}\n\n\t// calculate the preconditioner\n\tvoid doPrecond()\n\t{\n\t\tHYPRE_ParCSRFlexGMRESSetup(solver, par_A, par_b, par_x);\n\t}\n\n\t// solve the linear system\n\tvoid doSolve(double* x)\n\t{\n\t\tHYPRE_ParCSRFlexGMRESSolve(solver, par_A, par_b, par_x);\n\n\t\t/* Run info - needed logging turned on */\n\t\tint    num_iterations;\n\t\tdouble final_res_norm;\n\t\tHYPRE_FlexGMRESGetNumIterations(solver, (HYPRE_Int*)&num_iterations);\n\t\tHYPRE_FlexGMRESGetFinalRelativeResidualNorm(solver, &final_res_norm);\n\t\tif (m_print_level != 0)\n\t\t{\n\t\t\tprintf(\"\\n\");\n\t\t\tprintf(\"Iterations = %d\\n\", num_iterations);\n\t\t\tprintf(\"Final Relative Residual Norm = %e\\n\", final_res_norm);\n\t\t\tprintf(\"\\n\");\n\t\t}\n\n\t\t/* get the local solution */\n\t\tint neq = equations();\n\t\tHYPRE_IJVectorGetValues(ij_x, neq, (HYPRE_Int*)&ind[0], &x[0]);\n\t}\n};\n\nBEGIN_FECORE_CLASS(HypreGMRESsolver, LinearSolver)\n\tADD_PARAMETER(imp->m_print_level, \"print_level\");\n\tADD_PARAMETER(imp->m_maxiter    , \"maxiter\"    );\n\tADD_PARAMETER(imp->m_tol        , \"tol\"        );\nEND_FECORE_CLASS();\n\nHypreGMRESsolver::HypreGMRESsolver(FEModel* fem) : LinearSolver(fem), imp(new HypreGMRESsolver::Implementation)\n{\n\n}\n\nHypreGMRESsolver::~HypreGMRESsolver()\n{\n\tdelete imp; imp = 0;\n}\n\nvoid HypreGMRESsolver::SetPrintLevel(int n)\n{\n\timp->m_print_level = n;\n}\n\nvoid HypreGMRESsolver::SetMaxIterations(int n)\n{\n\timp->m_maxiter = n;\n}\n\nvoid HypreGMRESsolver::SetConvergencTolerance(double tol)\n{\n\timp->m_tol = tol;\n}\n\nSparseMatrix* HypreGMRESsolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\tif (ntype == Matrix_Type::REAL_UNSYMMETRIC)\n\t\treturn (imp->A = new CRSSparseMatrix(0));\n\telse\n\t\treturn 0;\n}\n\nbool HypreGMRESsolver::SetSparseMatrix(SparseMatrix* A)\n{\n\tCRSSparseMatrix* K = dynamic_cast<CRSSparseMatrix*>(A);\n\tif (K == 0) return false;\n\n\tif (K->isRowBased() == false) return false;\n\tif (K->Offset() != 0) return false;\n\n\timp->A = K;\n\n\treturn true;\n}\n\n//! clean up\nvoid HypreGMRESsolver::Destroy()\n{\n\t// cleanup\n\timp->destroyPrecond();\n\timp->destroySolver();\n\n\t// destroy matrix\n\timp->destroyMatrix();\n\n\t// Destroy vectors\n\timp->destroyVectors();\n}\n\nbool HypreGMRESsolver::PreProcess()\n{ \n\t// make sure data is valid\n\tif (imp->isValid() == false) return false;\n\n\t// create coefficient matrix\n\timp->allocMatrix();\n\n\t// allocate rhs and solution vectors\n\timp->allocVectors();\n\n\treturn true; \n}\n\nbool HypreGMRESsolver::Factor()\n{ \n\t// make sure data is valid\n\tif (imp->isValid() == false) return false;\n\n\t// copy matrix values\n\timp->updateMatrix();\n\n\t// initialize vectors\n\tint neq = imp->equations();\n\tvector<double> zero(neq, 0.0);\n\timp->updateVectors(&zero[0], &zero[0]);\n\n\t// allocate preconditioner (always call before creating solver!)\n\timp->allocPrecond();\n\n\t// allocate solver\n\timp->allocSolver();\n\n\t// apply preconditioner\n\timp->doPrecond();\n\n\treturn true;\n}\n\nbool HypreGMRESsolver::BackSolve(double* x, double* b)\n{\n\t// make sure data is valid\n\tif (imp->isValid() == false) return false;\n\n\t// nr of equations\n\tint neq = imp->equations();\n\n\t// update the vectors\n\timp->updateVectors(x, b);\n\n\t// solve \n\timp->doSolve(x);\n\n\treturn true; \n}\n\n#else\nBEGIN_FECORE_CLASS(HypreGMRESsolver, LinearSolver)\nEND_FECORE_CLASS();\n\nHypreGMRESsolver::HypreGMRESsolver(FEModel* fem) : LinearSolver(fem) {}\nHypreGMRESsolver::~HypreGMRESsolver() {}\nvoid HypreGMRESsolver::Destroy() {}\nvoid HypreGMRESsolver::SetPrintLevel(int n) {}\nvoid HypreGMRESsolver::SetMaxIterations(int n) {}\nvoid HypreGMRESsolver::SetConvergencTolerance(double tol) {}\nbool HypreGMRESsolver::PreProcess() { return false; }\nbool HypreGMRESsolver::Factor() { return false; }\nbool HypreGMRESsolver::BackSolve(double* x, double* b) { return false; }\nSparseMatrix* HypreGMRESsolver::CreateSparseMatrix(Matrix_Type ntype) { return 0; }\nbool HypreGMRESsolver::SetSparseMatrix(SparseMatrix* A) { return false; }\n\n#endif // HYPRE\n"
  },
  {
    "path": "NumCore/HypreGMRESsolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n\n//-----------------------------------------------------------------------------\n// This class implements the HYPRE GMRES solver\nclass HypreGMRESsolver : public LinearSolver\n{\n\tclass Implementation;\n\npublic:\n\tHypreGMRESsolver(FEModel* fem);\n\t~HypreGMRESsolver();\n\n\tvoid SetPrintLevel(int n) override;\n\n\tvoid SetMaxIterations(int n);\n\n\tvoid SetConvergencTolerance(double tol);\n\npublic:\n\t// allocate storage\n\tbool PreProcess() override;\n\n\t//! Factor the matrix (for iterative solvers, this can be used for creating pre-conditioner)\n\tbool Factor() override;\n\n\t//! Calculate the solution of RHS b and store solution in x\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Return a sparse matrix compatible with this solver\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\n\t//! clean up\n\tvoid Destroy() override;\n\nprivate:\n\tImplementation*\timp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/Hypre_PCG_AMG.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"Hypre_PCG_AMG.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolver.h>\n#include <FECore/FEAnalysis.h>\n#include <FECore/log.h>\n#ifdef HYPRE\n#include <HYPRE.h>\n#include <HYPRE_IJ_mv.h>\n#include <HYPRE_parcsr_mv.h>\n#include <HYPRE_parcsr_ls.h>\n#include <_hypre_utilities.h>\n#include <_hypre_IJ_mv.h>\n#include <HYPRE_krylov.h>\n\n\nclass Hypre_PCG_AMG::Implementation\n{\npublic:\n\tCRSSparseMatrix*\tA;\t// global matrix\n\n\tvector<int>\t\t\tind; // indices array\n\n\t// Hypre stuff\n\tHYPRE_IJMatrix\t\tij_A;\n\tHYPRE_ParCSRMatrix\tpar_A;\n\tHYPRE_Solver\t\tsolver;\n\tHYPRE_Solver\t\tprecond;\n\tHYPRE_IJVector\t\tij_b, ij_x;\n\tHYPRE_ParVector\t\tpar_b, par_x;\n\n\tFEModel*\tm_fem;\n\n\tint\t\tm_iters;\n\npublic:\n\t// control parameters\n\tint\t\tm_maxiter;\n\tdouble\tm_tol;\n\tint\t\tm_print_level;\n\tdouble\tm_amg_tol;\n\npublic:\n\tImplementation() : A(0)\n\t{\n\t\tm_print_level = 0;\n\t\tm_maxiter = 1000;\n\t\tm_tol = 1e-7;\n\t\tm_amg_tol = 1e-7;\n\n\t\tm_iters = 0;\n\n\t\tij_A = nullptr;\n\t\tij_b = nullptr;\n\t\tij_x = nullptr;\n\t\tsolver = nullptr;\n\t\tprecond = nullptr;\n\t\tpar_A = nullptr;\n\t\tpar_b = nullptr;\n\t\tpar_x = nullptr;\n\t}\n\n\tbool isValid() const\n\t{\n\t\treturn (A != 0);\n\t}\n\n\tint equations() const { return (A ? A->Rows() : 0); }\n\n\t// Allocate stiffness matrix\n\tvoid allocMatrix()\n\t{\n\t\tint neq = equations();\n\n\t\t// Create an empty matrix object\n\t\tint ret = 0;\n\t\tret = HYPRE_IJMatrixCreate(MPI_COMM_WORLD, 0, neq - 1, 0, neq - 1, &ij_A);\n\n\t\t// set the matrix object type\n\t\tret = HYPRE_IJMatrixSetObjectType(ij_A, HYPRE_PARCSR);\n\t}\n\n\t// destroy stiffness matrix\n\tvoid destroyMatrix()\n\t{\n\t\tif (ij_A) HYPRE_IJMatrixDestroy(ij_A);\n\t\tij_A = nullptr;\n\t}\n\n\t// Allocate vectors for rhs and solution\n\tvoid allocVectors()\n\t{\n\t\tint neq = equations();\n\t\tind.resize(neq, 0);\n\t\tfor (int i = 0; i<neq; ++i) ind[i] = i;\n\n\t\t// create the vector object for the rhs\n\t\tHYPRE_IJVectorCreate(MPI_COMM_WORLD, 0, neq - 1, &ij_b);\n\t\tHYPRE_IJVectorSetObjectType(ij_b, HYPRE_PARCSR);\n\n\t\t// create the vector object for the solution\n\t\tHYPRE_IJVectorCreate(MPI_COMM_WORLD, 0, neq - 1, &ij_x);\n\t\tHYPRE_IJVectorSetObjectType(ij_x, HYPRE_PARCSR);\n\t}\n\n\t// destroy vectors\n\tvoid destroyVectors()\n\t{\n\t\tif (ij_b) HYPRE_IJVectorDestroy(ij_b); ij_b = nullptr;\n\t\tif (ij_x) HYPRE_IJVectorDestroy(ij_x); ij_x = nullptr;\n\t}\n\n\t// update vectors \n\tvoid updateVectors(double* x, double* b)\n\t{\n\t\t// initialize vectors for changing coefficient values\n\t\tHYPRE_IJVectorInitialize(ij_b);\n\t\tHYPRE_IJVectorInitialize(ij_x);\n\n\t\t// set the values\n\t\tint neq = equations();\n\t\tHYPRE_IJVectorSetValues(ij_b, neq, (HYPRE_Int*)&ind[0], b);\n\t\tHYPRE_IJVectorSetValues(ij_x, neq, (HYPRE_Int*)&ind[0], x);\n\n\t\t// finialize assembly\n\t\tHYPRE_IJVectorAssemble(ij_b);\n\t\tHYPRE_IJVectorAssemble(ij_x);\n\n\t\tHYPRE_IJVectorGetObject(ij_b, (void**)&par_b);\n\t\tHYPRE_IJVectorGetObject(ij_x, (void**)&par_x);\n\t}\n\n\t// update coefficient matrix\n\tvoid updateMatrix()\n\t{\n\t\tint neq = equations();\n\n\t\t// call initialize, after which we can set the matrix coefficients\n\t\tHYPRE_Int ret = HYPRE_IJMatrixInitialize(ij_A);\n\n\t\t// set the matrix coefficients\n\t\tdouble* values = A->Values();\n\t\tint* indices = A->Indices();\n\t\tint* pointers = A->Pointers();\n\t\tfor (int i = 0; i<neq; ++i)\n\t\t{\n\t\t\tconst int* cols = indices + pointers[i];\n\t\t\tint ncols = pointers[i + 1] - pointers[i];\n\t\t\tdouble* vals = values + pointers[i];\n\t\t\tHYPRE_Int nrows = 1;\n\t\t\tret = HYPRE_IJMatrixSetValues(ij_A, nrows, (HYPRE_Int*)&ncols, (HYPRE_Int*)&i, (HYPRE_Int*)cols, vals);\n\t\t}\n\n\t\t// Finalize the matrix assembly\n\t\tret = HYPRE_IJMatrixAssemble(ij_A);\n\n\t\t// get the matrix object for later use\n\t\tret = HYPRE_IJMatrixGetObject(ij_A, (void**)&par_A);\n\t}\n\n\t// allocate preconditioner\n\tbool allocPrecond()\n\t{\n\t\t// Now set up the AMG preconditioner and specify any parameters\n\t\tHYPRE_BoomerAMGCreate(&precond);\n//\t\tHYPRE_BoomerAMGSetPrintLevel(precond, 2);\t\t\t// print solve info\n\t\tHYPRE_BoomerAMGSetCoarsenType(precond, 10);\t\t\t// HMIS-coarsening\n\t\tHYPRE_BoomerAMGSetStrongThreshold(precond, 0.5);\t// Threshold for choosing weak/strong connections\n\t\tHYPRE_BoomerAMGSetMaxRowSum(precond, 1.0);\t\t\t// Disable dependency weakening based on maximum row sum.\n\t\tHYPRE_BoomerAMGSetCycleRelaxType(precond, 13, 1);\t// Pre-smoother: forward L1-Gauss-Seidel\n\t\tHYPRE_BoomerAMGSetCycleRelaxType(precond, 14, 2);\t// Post-smoother: backward L1-Gauss-Seidel\n\t\tHYPRE_BoomerAMGSetCycleRelaxType(precond, 9, 3);\t// Coarsest grid solver: Gauss elimination\n\t\tHYPRE_BoomerAMGSetAggNumLevels(precond, 1);\t\t\t// One level of aggressive coarsening\n\t\tHYPRE_BoomerAMGSetNumPaths(precond, 1);\t\t\t\t// Number of paths of length 2 for aggressive coarsening\n\t\tHYPRE_BoomerAMGSetInterpType(precond, 6);\t\t\t// Extended+i interpolation\n\t\tHYPRE_BoomerAMGSetMaxIter(precond, m_maxiter);      // Set maximum iterations\n\t\tHYPRE_BoomerAMGSetTol(precond, m_amg_tol);\t\t\t// conv. tolerance\n\n\t\tFESolver* fesolve = m_fem->GetCurrentStep()->GetFESolver();\n\n\t\t// get the dof map\n\t\tvector<int> dofMap;\n\t\tint nfunc = fesolve->GetActiveDofMap(dofMap);\n\t\tif (nfunc == -1) return false;\n\n\t\t// allocate dof map\n\t\t// (We need to copy it here since Hypre will deallocate it)\n\t\tint neq = (int)dofMap.size();\n\t\tint* dof_func = (int*)malloc(neq * sizeof(int));\n\t\tfor (size_t i = 0; i < neq; ++i) dof_func[i] = dofMap[i];\n\n\t\tprintf(\"\\tNumber of functions : %d\\n\", nfunc);\n\n\t\t// assign to BoomerAMG\n\t\tHYPRE_BoomerAMGSetNumFunctions(precond, nfunc);\n\n\t\t// set the dof map\n\t\tHYPRE_BoomerAMGSetDofFunc(precond, (HYPRE_Int*)dof_func);\n\n\t\treturn true;\n\t}\n\n\t// destroy preconditioner\n\tvoid destroyPrecond()\n\t{\n\t\tif (precond) HYPRE_BoomerAMGDestroy(precond);\n\t\tprecond = nullptr;\n\t}\n\n\t// allocate solver\n\tvoid allocSolver()\n\t{\n\t\t// Create the solver object\n\t\tHYPRE_ParCSRPCGCreate(MPI_COMM_WORLD, &solver);\n\n\t\t/* Set some parameters (See Reference Manual for more parameters) */\n\t\tHYPRE_PCGSetTwoNorm(solver, 1);\n\t\tHYPRE_PCGSetTol(solver, m_tol);\n\n\t\t// Set the preconditioner\n\t\tHYPRE_ParCSRPCGSetPrecond(solver, (HYPRE_PtrToParSolverFcn) HYPRE_BoomerAMGSolve,\n\t\t\t(HYPRE_PtrToParSolverFcn) HYPRE_BoomerAMGSetup, precond);\n\t}\n\n\t// destroy the solver\n\tvoid destroySolver()\n\t{\n\t\tif (solver) HYPRE_ParCSRPCGDestroy(solver);\n\t\tsolver = nullptr;\n\t}\n\n\t// calculate the preconditioner\n\tvoid doPrecond()\n\t{\n\t\tHYPRE_ParCSRPCGSetup(solver, par_A, par_b, par_x);\n\t}\n\n\t// solve the linear system\n\tvoid doSolve(double* x)\n\t{\n\t\tHYPRE_ParCSRPCGSolve(solver, par_A, par_b, par_x);\n\n\t\t/* Run info - needed logging turned on */\n\t\tdouble final_res_norm;\n\t\tHYPRE_ParCSRPCGGetNumIterations(solver, (HYPRE_Int*)&m_iters);\n\t\tHYPRE_ParCSRPCGGetFinalRelativeResidualNorm(solver, &final_res_norm);\n\t\tif (m_print_level != 0)\n\t\t{\n\t\t\tfeLogEx(m_fem, \"\\n\");\n\t\t\tfeLogEx(m_fem, \"Iterations = %d\\n\", m_iters);\n\t\t\tfeLogEx(m_fem, \"Final Relative Residual Norm = %e\\n\", final_res_norm);\n\t\t\tfeLogEx(m_fem, \"\\n\");\n\t\t}\n\n\t\t/* get the local solution */\n\t\tint neq = equations();\n\t\tHYPRE_IJVectorGetValues(ij_x, neq, (HYPRE_Int*)&ind[0], &x[0]);\n\t}\n};\n\nBEGIN_FECORE_CLASS(Hypre_PCG_AMG, LinearSolver)\n\tADD_PARAMETER(imp->m_print_level, \"print_level\");\n\tADD_PARAMETER(imp->m_maxiter    , \"maxiter\"    );\n\tADD_PARAMETER(imp->m_tol        , \"tol\"        );\n\tADD_PARAMETER(imp->m_amg_tol    , \"amg_tol\"    );\nEND_FECORE_CLASS();\n\nHypre_PCG_AMG::Hypre_PCG_AMG(FEModel* fem) : LinearSolver(fem), imp(new Hypre_PCG_AMG::Implementation)\n{\n\timp->m_fem = fem;\n}\n\nHypre_PCG_AMG::~Hypre_PCG_AMG()\n{\n\tdelete imp; imp = 0;\n}\n\nvoid Hypre_PCG_AMG::SetPrintLevel(int n)\n{\n\timp->m_print_level = n;\n}\n\nvoid Hypre_PCG_AMG::SetMaxIterations(int n)\n{\n\timp->m_maxiter = n;\n}\n\nvoid Hypre_PCG_AMG::SetConvergencTolerance(double tol)\n{\n\timp->m_tol = tol;\n}\n\nSparseMatrix* Hypre_PCG_AMG::CreateSparseMatrix(Matrix_Type ntype)\n{\n\tif (ntype == Matrix_Type::REAL_UNSYMMETRIC)\n\t\treturn (imp->A = new CRSSparseMatrix(0));\n\telse\n\t\treturn 0;\n}\n\nbool Hypre_PCG_AMG::SetSparseMatrix(SparseMatrix* A)\n{\n\tCRSSparseMatrix* K = dynamic_cast<CRSSparseMatrix*>(A);\n\tif (K == 0) return false;\n\n\tif (K->isRowBased() == false) return false;\n\tif (K->Offset() != 0) return false;\n\n\timp->A = K;\n\n\treturn true;\n}\n\n//! clean up\nvoid Hypre_PCG_AMG::Destroy()\n{\n\t// cleanup\n\timp->destroyPrecond();\n\timp->destroySolver();\n\n\t// destroy matrix\n\timp->destroyMatrix();\n\n\t// Destroy vectors\n\timp->destroyVectors();\n}\n\nbool Hypre_PCG_AMG::PreProcess()\n{ \n\t// make sure data is valid\n\tif (imp->isValid() == false) return false;\n\n\t// create coefficient matrix\n\timp->allocMatrix();\n\n\t// allocate rhs and solution vectors\n\timp->allocVectors();\n\n\treturn true; \n}\n\nbool Hypre_PCG_AMG::Factor()\n{ \n\t// make sure data is valid\n\tif (imp->isValid() == false) return false;\n\n\t// copy matrix values\n\timp->updateMatrix();\n\n\t// initialize vectors\n\tint neq = imp->equations();\n\tvector<double> zero(neq, 0.0);\n\timp->updateVectors(&zero[0], &zero[0]);\n\n\t// allocate preconditioner (always call before creating solver!)\n\timp->allocPrecond();\n\n\t// allocate solver\n\timp->allocSolver();\n\n\t// apply preconditioner\n\timp->doPrecond();\n\n\treturn true;\n}\n\nbool Hypre_PCG_AMG::BackSolve(double* x, double* b)\n{\n\t// make sure data is valid\n\tif (imp->isValid() == false) return false;\n\n\t// nr of equations\n\tint neq = imp->equations();\n\n\t// update the vectors\n\timp->updateVectors(x, b);\n\n\t// solve \n\timp->doSolve(x);\n\n\t// update stats\n\tUpdateStats(imp->m_iters);\n\n\treturn true; \n}\n\n#else\nBEGIN_FECORE_CLASS(Hypre_PCG_AMG, LinearSolver)\nEND_FECORE_CLASS();\n\nHypre_PCG_AMG::Hypre_PCG_AMG(FEModel* fem) : LinearSolver(fem) {}\nHypre_PCG_AMG::~Hypre_PCG_AMG() {}\nvoid Hypre_PCG_AMG::Destroy() {}\nvoid Hypre_PCG_AMG::SetPrintLevel(int n) {}\nvoid Hypre_PCG_AMG::SetMaxIterations(int n) {}\nvoid Hypre_PCG_AMG::SetConvergencTolerance(double tol) {}\nbool Hypre_PCG_AMG::PreProcess() { return false; }\nbool Hypre_PCG_AMG::Factor() { return false; }\nbool Hypre_PCG_AMG::BackSolve(double* x, double* b) { return false; }\nSparseMatrix* Hypre_PCG_AMG::CreateSparseMatrix(Matrix_Type ntype) { return 0; }\nbool Hypre_PCG_AMG::SetSparseMatrix(SparseMatrix* A) { return false; }\n\n#endif // HYPRE\n"
  },
  {
    "path": "NumCore/Hypre_PCG_AMG.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n\n//-----------------------------------------------------------------------------\n// This class implements the HYPRE GMRES solver\nclass Hypre_PCG_AMG : public LinearSolver\n{\n\tclass Implementation;\n\npublic:\n\tHypre_PCG_AMG(FEModel* fem);\n\t~Hypre_PCG_AMG();\n\n\tvoid SetPrintLevel(int n) override;\n\n\tvoid SetMaxIterations(int n);\n\n\tvoid SetConvergencTolerance(double tol);\n\npublic:\n\t// allocate storage\n\tbool PreProcess() override;\n\n\t//! Factor the matrix (for iterative solvers, this can be used for creating pre-conditioner)\n\tbool Factor() override;\n\n\t//! Calculate the solution of RHS b and store solution in x\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Return a sparse matrix compatible with this solver\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\n\t//! clean up\n\tvoid Destroy() override;\n\nprivate:\n\tImplementation*\timp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/ILU0_Preconditioner.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"ILU0_Preconditioner.h\"\n#include <FECore/CompactUnSymmMatrix.h>\n\n// We must undef PARDISO since it is defined as a function in mkl_solver.h\n#ifdef MKL_ISS\n#ifdef PARDISO\n#undef PARDISO\n#endif\n#include \"mkl_rci.h\"\n#include \"mkl_blas.h\"\n#include \"mkl_spblas.h\"\n#endif // MKL_ISS\n\nBEGIN_FECORE_CLASS(ILU0_Preconditioner, Preconditioner)\n\tADD_PARAMETER(m_checkZeroDiagonal, \"replace_zero_diagonal\");\n\tADD_PARAMETER(m_zeroThreshold    , \"zero_threshold\");\n\tADD_PARAMETER(m_zeroReplace      , \"zero_replace\");\nEND_FECORE_CLASS();\n\n//=================================================================================================\n\nILU0_Preconditioner::ILU0_Preconditioner(FEModel* fem) : Preconditioner(fem)\n{\n\tm_checkZeroDiagonal = true;\n\tm_zeroThreshold = 1e-16;\n\tm_zeroReplace = 1e-10;\n\n\tm_K = 0;\n}\n\nSparseMatrix* ILU0_Preconditioner::CreateSparseMatrix(Matrix_Type ntype)\n{\n\tif (ntype != REAL_UNSYMMETRIC) return nullptr;\n\tm_K = new CRSSparseMatrix(1);\n\treturn m_K;\n}\n\n#ifdef MKL_ISS\nbool ILU0_Preconditioner::Factor()\n{\n\n\tif (m_K == 0) return false;\n\tassert(m_K->Offset() == 1);\n\n\tint N = m_K->Rows();\n\tint NNZ = m_K->NonZeroes();\n\n\tdouble* pa = m_K->Values();\n\tint* ia = m_K->Pointers();\n\tint* ja = m_K->Indices();\n\n\tMKL_INT ipar[128] = { 0 };\n\tdouble dpar[128] = { 0.0 };\n\n\t// parameters affecting the pre-conditioner\n\tif (m_checkZeroDiagonal)\n\t{\n\t\tipar[30] = 1;\n\t\tdpar[30] = m_zeroThreshold;\n\t\tdpar[31] = m_zeroReplace;\n\t}\n\n\tm_tmp.resize(N, 0.0);\n\n\tm_bilu0.resize(NNZ);\n\tint ierr = 0;\n\tdcsrilu0(&N, pa, ia, ja, &m_bilu0[0], ipar, dpar, &ierr);\n\tif (ierr != 0) return false;\n\n\treturn true;\n} \n\nbool ILU0_Preconditioner::BackSolve(double* x, double* y)\n{\n\tint ivar = m_K->Rows();\n\tint* ia = m_K->Pointers();\n\tint* ja = m_K->Indices();\n\n\tchar cvar1 = 'L';\n\tchar cvar = 'N';\n\tchar cvar2 = 'U';\n\tmkl_dcsrtrsv(&cvar1, &cvar, &cvar2, &ivar, &m_bilu0[0], ia, ja, &y[0], &m_tmp[0]);\n\tcvar1 = 'U';\n\tcvar = 'N';\n\tcvar2 = 'N';\n\tmkl_dcsrtrsv(&cvar1, &cvar, &cvar2, &ivar, &m_bilu0[0], ia, ja, &m_tmp[0], &x[0]);\n\n\treturn true;\n}\n\n#else\nbool ILU0_Preconditioner::Factor() { return false; }\nbool ILU0_Preconditioner::BackSolve(double* x, double* y) { return false; }\n#endif\n"
  },
  {
    "path": "NumCore/ILU0_Preconditioner.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/Preconditioner.h>\n\n//-----------------------------------------------------------------------------\nclass ILU0_Preconditioner : public Preconditioner\n{\npublic:\n\tILU0_Preconditioner(FEModel* fem);\n\n\t// create a preconditioner for a sparse matrix\n\tbool Factor() override;\n\n\t// apply to vector P x = y\n\tbool BackSolve(double* x, double* y) override;\n\n\t// create sparse matrix\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\npublic:\n\tbool\tm_checkZeroDiagonal;\t// check for zero diagonals\n\tdouble\tm_zeroThreshold;\t\t// threshold for zero diagonal check\n\tdouble\tm_zeroReplace;\t\t\t// replacement value for zero diagonal\n\nprivate:\n\tvector<double>\t\tm_bilu0;\n\tvector<double>\t\tm_tmp;\n\tCRSSparseMatrix*\tm_K;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/ILUT_Preconditioner.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"ILUT_Preconditioner.h\"\n#include <FECore/CompactUnSymmMatrix.h>\n\n// We must undef PARDISO since it is defined as a function in mkl_solver.h\n#ifdef MKL_ISS\n#ifdef PARDISO\n#undef PARDISO\n#endif\n#include \"mkl_rci.h\"\n#include \"mkl_blas.h\"\n#include \"mkl_spblas.h\"\n#endif // MKL_ISS\n\nBEGIN_FECORE_CLASS(ILUT_Preconditioner, Preconditioner)\n\tADD_PARAMETER(m_maxfill, \"maxfill\");\n\tADD_PARAMETER(m_fillTol, \"filltol\");\n\tADD_PARAMETER(m_checkZeroDiagonal, \"replace_zero_diagonal\");\n\tADD_PARAMETER(m_zeroThreshold    , \"zero_threshold\");\n\tADD_PARAMETER(m_zeroReplace      , \"zero_replace\");\nEND_FECORE_CLASS();\n\nILUT_Preconditioner::ILUT_Preconditioner(FEModel* fem) : Preconditioner(fem)\n{\n\tm_maxfill = 1;\n\tm_fillTol = 1e-16;\n\n\tm_checkZeroDiagonal = true;\n\tm_zeroThreshold = 1e-16;\n\tm_zeroReplace = 1e-10;\n}\n\nSparseMatrix* ILUT_Preconditioner::CreateSparseMatrix(Matrix_Type ntype)\n{\n\tif (ntype != REAL_UNSYMMETRIC) return nullptr;\n\tm_K = new CRSSparseMatrix(1);\n\tSetSparseMatrix(m_K);\n\treturn m_K;\n}\n\n#ifdef MKL_ISS\n\nbool ILUT_Preconditioner::Factor()\n{\n\tif (m_K == 0) return false;\n\tassert(m_K->Offset() == 1);\n\n\tint N = m_K->Rows();\n\tint ivar = N;\n\n\tdouble* pa = m_K->Values();\n\tint* ia = m_K->Pointers();\n\tint* ja = m_K->Indices();\n\n\tMKL_INT ipar[128] = { 0 };\n\tdouble dpar[128] = { 0.0 };\n\n\tif (m_checkZeroDiagonal)\n\t{\n\t\tipar[30] = 1;\n\t\tdpar[30] = m_zeroThreshold;\n\t}\n\telse\n\t{\n\t\t// do this to avoid a warning from the preconditioner\n\t\tdpar[30] = m_fillTol;\n\t}\n\n\tconst int PCsize = (2 * m_maxfill + 1)*N - m_maxfill*(m_maxfill + 1) + 1;\n\tm_bilut.resize(PCsize, 0.0);\n\tm_jbilut.resize(PCsize, 0);\n\tm_ibilut.resize(N + 1, 0);\n\n\tm_tmp.resize(N, 0.0);\n\n\tint ierr;\n\tdcsrilut(&ivar, pa, ia, ja, &m_bilut[0], &m_ibilut[0], &m_jbilut[0], &m_fillTol, &m_maxfill, ipar, dpar, &ierr);\n\tif (ierr != 0) return false;\n\n\treturn true;\n}\n\nbool ILUT_Preconditioner::BackSolve(double* x, double* y)\n{\n\tint ivar = m_K->Rows();\n\tchar cvar1 = 'L';\n\tchar cvar = 'N';\n\tchar cvar2 = 'U';\n\tmkl_dcsrtrsv(&cvar1, &cvar, &cvar2, &ivar, &m_bilut[0], &m_ibilut[0], &m_jbilut[0], y, &m_tmp[0]);\n\tcvar1 = 'U';\n\tcvar = 'N';\n\tcvar2 = 'N';\n\tmkl_dcsrtrsv(&cvar1, &cvar, &cvar2, &ivar, &m_bilut[0], &m_ibilut[0], &m_jbilut[0], &m_tmp[0], x);\n\n\treturn true;\n}\n#else\nbool ILUT_Preconditioner::Factor() { return false; }\nbool ILUT_Preconditioner::BackSolve(double* x, double* y) { return false; }\n#endif"
  },
  {
    "path": "NumCore/ILUT_Preconditioner.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/Preconditioner.h>\n\n//-----------------------------------------------------------------------------\nclass ILUT_Preconditioner : public Preconditioner\n{\npublic:\n\tILUT_Preconditioner(FEModel* fem);\n\n\t// create a preconditioner for a sparse matrix\n\tbool Factor() override;\n\n\t// apply to vector P x = y\n\tbool BackSolve(double* x, double* y) override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\npublic:\n\tint\t\tm_maxfill;\n\tdouble\tm_fillTol;\n\tbool\tm_checkZeroDiagonal;\t// check for zero diagonals\n\tdouble\tm_zeroThreshold;\t\t// threshold for zero diagonal check\n\tdouble\tm_zeroReplace;\t\t\t// replacement value for zero diagonal\n\nprivate:\n\tCRSSparseMatrix*\tm_K;\n\tvector<double>\tm_bilut;\n\tvector<int>\t\tm_jbilut;\n\tvector<int>\t\tm_ibilut;\n\tvector<double>\tm_tmp;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/IncompleteCholesky.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"IncompleteCholesky.h\"\n#include <FECore/CompactSymmMatrix.h>\n#include <FECore/log.h>\n\n// We must undef PARDISO since it is defined as a function in mkl_solver.h\n#ifdef MKL_ISS\n#ifdef PARDISO\n#undef PARDISO\n#endif\n#include \"mkl_rci.h\"\n#include \"mkl_blas.h\"\n#include \"mkl_spblas.h\"\n#endif // MKL_ISS\n\nIncompleteCholesky::IncompleteCholesky(FEModel* fem) : Preconditioner(fem)\n{\n\tm_L = nullptr;\n}\n\nCompactSymmMatrix* IncompleteCholesky::getMatrix()\n{\n\treturn m_L;\n}\n\n// create a preconditioner for a sparse matrix\nbool IncompleteCholesky::Factor()\n{\n\t// make sure it's the correct format\n\tCompactSymmMatrix* K = dynamic_cast<CompactSymmMatrix*>(GetSparseMatrix());\n\tif (K == nullptr) return false;\n\n\tif (K->Offset() != 1) return false;\n\n\tint N = K->Rows();\n\tint nnz = K->NonZeroes();\n\n\tz.resize(N, 0.0);\n\n\t// create the preconditioner\n\tif (m_L) delete m_L;\n\tm_L = new CompactSymmMatrix(K->Offset());\n\tdouble* val = new double[nnz];\n\tint* row = new int[nnz];\n\tint* col = new int[N + 1];\n\tm_L->alloc(N, N, nnz, val, row, col);\n\n\t// copy the matrix\n\tdouble* aval = K->Values();\n\tint* arow = K->Indices();\n\tint* acol = K->Pointers();\n\tfor (int i = 0; i < nnz; ++i)\n\t{\n\t\tval[i] = aval[i];\n\t\trow[i] = arow[i];\n\t}\n\tfor (int i = 0; i <= N; ++i) col[i] = acol[i];\n\n\tCompactSymmMatrix& L = *m_L;\n\n\tvector<double> tmp(N, 0.0);\n\n\t// fill in the values\n\tint offset = m_L->Offset();\n\tfor (int k = 0; k < N; ++k)\n\t{\n\t\t// get the values for column k\n\t\tdouble* ak = val + (col[k] - offset);\n\t\tint* rowk = row + (col[k] - offset);\n\t\tint Lk = col[k + 1] - col[k];\n\n\t\t// sanity check\n\t\tif (rowk[0] - offset != k)\n\t\t{\n\t\t\tfeLogError(\"Fatal error in incomplete Cholesky preconditioner:\\nMatrix format error at row %d.\", k);\n\t\t\treturn false;\n\t\t}\n\n\t\t// make sure the diagonal element is not zero\n\t\tif (ak[0] == 0.0)\n\t\t{\n\t\t\tfeLogError(\"Fatal error in incomplete Cholesky preconditioner:\\nZero diagonal element at row %d.\", k);\n\t\t\treturn false;\n\t\t}\n\n\t\t// make sure the diagonal element is not negative either\n\t\tif (ak[0] < 0.0)\n\t\t{\n\t\t\tfeLogError(\"Fatal error in incomplete Cholesky preconditioner:\\nNegative diagonal element at row %d (value = %lg).\", k, ak[0]);\n\t\t\treturn false;\n\t\t}\n\n\t\t// set the diagonal element\n\t\tdouble akk = sqrt(ak[0]);\n\t\tak[0] = akk;\n\t\ttmp[rowk[0] - offset] = akk;\n\n\t\t// divide column by akk\n\t\tfor (int j = 1; j < Lk; ++j)\n\t\t{\n\t\t\tak[j] /= akk;\n\t\t\ttmp[rowk[j] - offset] = ak[j];\n\t\t}\n\n\t\t// loop over all other columns\n\t\tfor (int _j = 1; _j < Lk; ++_j)\n\t\t{\n\t\t\tint j = rowk[_j] - offset;\n\t\t\tdouble tjk = tmp[j];\n\t\t\tif (tjk != 0.0)\n\t\t\t{\n\t\t\t\tdouble* aj = val + col[j] - offset;\n\t\t\t\tint Lj = col[j + 1] - col[j];\n\t\t\t\tint* rowj = row + col[j] - offset;\n\n\t\t\t\tfor (int i = 0; i < Lj; i ++) aj[i] -= tmp[rowj[i] - offset] * tjk;\n\t\t\t}\n\t\t}\n\n\t\t// reset temp buffer\n\t\tfor (int j = 0; j < Lk; ++j) tmp[rowk[j] - offset] = 0.0;\n\t}\n\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\tdouble Lii = L.diag(i);\n\t\tassert(Lii != 0.0);\n\t}\n\n\treturn true;\n}\n\nbool IncompleteCholesky::BackSolve(double* x, double* y)\n{\n#ifdef MKL_ISS\n\tint ivar = m_L->Rows();\n\tdouble* pa = m_L->Values();\n\tint* ia = m_L->Pointers();\n\tint* ja = m_L->Indices();\n\n\tchar cvar1 = 'U';\n\tchar cvar = 'T';\n\tchar cvar2 = 'N';\n\tmkl_dcsrtrsv(&cvar1, &cvar, &cvar2, &ivar, pa, ia, ja, &y[0], &z[0]);\n\tcvar1 = 'U';\n\tcvar = 'N';\n\tcvar2 = 'N';\n\tmkl_dcsrtrsv(&cvar1, &cvar, &cvar2, &ivar, pa, ia, ja, &z[0], &x[0]);\n\n\treturn true;\n#else \n\treturn true;\n#endif\n}\n"
  },
  {
    "path": "NumCore/IncompleteCholesky.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/Preconditioner.h>\n\nclass CompactSymmMatrix;\n\nclass IncompleteCholesky : public Preconditioner\n{\npublic:\n\tIncompleteCholesky(FEModel* fem);\n\n\t// create a preconditioner for a sparse matrix\n\tbool Factor() override;\n\n\t// apply to vector P x = y\n\tbool BackSolve(double* x, double* y) override;\n\npublic:\n\tCompactSymmMatrix* getMatrix();\n\nprivate:\n\tCompactSymmMatrix*\tm_L;\n\tvector<double>\t\tz;\n};\n"
  },
  {
    "path": "NumCore/MKLDSSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"MKLDSSolver.h\"\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/CompactSymmMatrix.h>\n\n#ifdef PARDISO\n#undef PARDISO\n#include \"mkl_dss.h\"\n#include \"mkl_types.h\"\n#define PARDISO\n#endif\n\n#ifdef PARDISO\nclass MKLDSSolver::Imp \n{\npublic:\n\tCompactMatrix* A = nullptr;\n\t_MKL_DSS_HANDLE_t handle = nullptr;\n\tMKL_INT opt = MKL_DSS_DEFAULTS;\n\tMKL_INT sym = MKL_DSS_SYMMETRIC;\n\n\tint msglvl = 0;\n};\n\nBEGIN_FECORE_CLASS(MKLDSSolver, LinearSolver)\n\tADD_PARAMETER(m->msglvl, \"msglvl\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nMKLDSSolver::MKLDSSolver(FEModel* fem) : LinearSolver(fem), m(new MKLDSSolver::Imp)\n{\n}\n\n//-----------------------------------------------------------------------------\nMKLDSSolver::~MKLDSSolver()\n{\n\tDestroy();\n\tdelete m;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* MKLDSSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// allocate the correct matrix format depending on matrix symmetry type\n\tswitch (ntype)\n\t{\n\tcase REAL_SYMMETRIC     : m->A = new CompactSymmMatrix(1); m->sym = MKL_DSS_SYMMETRIC; break;\n\tcase REAL_UNSYMMETRIC   : m->A = new CRSSparseMatrix(1); m->sym = MKL_DSS_NON_SYMMETRIC; break;\n\tcase REAL_SYMM_STRUCTURE: m->A = new CRSSparseMatrix(1); m->sym = MKL_DSS_SYMMETRIC_STRUCTURE; break;\n\tdefault:\n\t\tassert(false);\n\t\tm->A = nullptr;\n\t}\n\n\treturn m->A;\n}\n\n//-----------------------------------------------------------------------------\nbool MKLDSSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n\treturn (m->A != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool MKLDSSolver::PreProcess()\n{\n\tSparseMatrix& A = *m->A;\n\tint nRows = A.Rows();\n\tint nCols = A.Columns();\n\tint nnz = A.NonZeroes();\n\n\t// create handle\n\tif (m->handle == nullptr)\n\t{\n\t\tm->opt = MKL_DSS_DEFAULTS;\n\t\tMKL_INT error = dss_create(m->handle, m->opt);\n\t\tif (error != MKL_DSS_SUCCESS) return false;\n\t}\n\n\t// define the structure\n\tMKL_INT error = dss_define_structure(m->handle, m->sym, A.Pointers(), nRows, nCols, A.Indices(), nnz);\n\tif (error != MKL_DSS_SUCCESS) return false;\n\n\t// reorder\n//\tm->opt = MKL_DSS_AUTO_ORDER;\n//\tm->opt = MKL_DSS_METIS_ORDER;\n\tm->opt = MKL_DSS_METIS_OPENMP_ORDER;\n\terror = dss_reorder(m->handle, m->opt, 0);\n\tif (error != MKL_DSS_SUCCESS) return false;\n\n\tif (m->msglvl != 0)\n\t{\n\t\tm->opt = MKL_DSS_DEFAULTS;\n\t\tconst char* szstat = \"PeakMem,FactorMem\";\n\t\tdouble ret[2];\n\t\tdss_statistics(m->handle, m->opt, szstat, ret);\n\t\tfprintf(stdout, \"\\nMKLDSS Memory info:\\n\");\n\t\tfprintf(stdout, \"===================\\n\");\n\t\tfprintf(stdout, \"Peak memory of symbolic factorization phase ........... : %lg KB\\n\", ret[0]);\n\t\tfprintf(stdout, \"Permanent memory for the factorization and solve phases : %lg KB\\n\", ret[1]);\n\t\tfprintf(stdout, \"\\n\");\n\t}\n\n\treturn LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool MKLDSSolver::Factor()\n{\n\t// make sure we have work to do\n\tif ((m->A == nullptr) || (m->A->Rows() == 0)) return true;\n\n\tSparseMatrix& A = *m->A;\n//\tMKL_INT type = MKL_DSS_POSITIVE_DEFINITE;\n\tMKL_INT type = MKL_DSS_INDEFINITE;\n\tMKL_INT error = dss_factor_real(m->handle, type, A.Values());\n\tif (error != MKL_DSS_SUCCESS) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool MKLDSSolver::BackSolve(double* x, double* b)\n{\n\t// make sure we have work to do\n\tif ((m->A == nullptr) || (m->A->Rows() == 0)) return true;\n\n\tint nRhs = 1;\n\tm->opt = MKL_DSS_DEFAULTS;\n\tMKL_INT error = dss_solve_real(m->handle, m->opt, b, nRhs, x);\n\tif (error != MKL_DSS_SUCCESS) return false;\n\n\t// update stats\n\tUpdateStats(1);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid MKLDSSolver::Destroy()\n{\n\tif (m->handle)\n\t{\n\t\tm->opt = MKL_DSS_DEFAULTS;\n\t\tMKL_INT error = dss_delete(m->handle, m->opt);\n\t\tassert(error == MKL_DSS_SUCCESS);\n\t\tm->handle = nullptr;\n\t}\n}\n#else\n//-----------------------------------------------------------------------------\nclass MKLDSSolver::Imp\n{\npublic:\n    CompactMatrix* A = nullptr;\n    \n    int msglvl = 0;\n};\n\nBEGIN_FECORE_CLASS(MKLDSSolver, LinearSolver)\nADD_PARAMETER(m->msglvl, \"msglvl\");\nEND_FECORE_CLASS();\n\nMKLDSSolver::MKLDSSolver(FEModel* fem) : LinearSolver(fem), m(new MKLDSSolver::Imp) {}\nMKLDSSolver::~MKLDSSolver() {}\nSparseMatrix* MKLDSSolver::CreateSparseMatrix(Matrix_Type ntype) {\treturn nullptr; }\nbool MKLDSSolver::SetSparseMatrix(SparseMatrix* pA) { return false; }\nbool MKLDSSolver::PreProcess() { return false; }\nbool MKLDSSolver::Factor() { return false; }\nbool MKLDSSolver::BackSolve(double* x, double* b) { return false; }\nvoid MKLDSSolver::Destroy() {}\n#endif\n"
  },
  {
    "path": "NumCore/MKLDSSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/LinearSolver.h>\n\nclass MKLDSSolver : public LinearSolver\n{\n\tclass Imp;\n\npublic:\n\tMKLDSSolver(FEModel* fem);\n\t~MKLDSSolver();\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\nprotected:\n\tImp* m;\n\n\tDECLARE_FECORE_CLASS();\n};\n\n"
  },
  {
    "path": "NumCore/MatrixTools.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <FECore/CompactMatrix.h>\n#include \"MatrixTools.h\"\n#include \"PardisoSolver.h\"\n#include <stdlib.h>\n#include <ostream>\n\nbool NumCore::write_hb(CompactMatrix& K, const char* szfile, int mode)\n{\n\t// This currently assumes a CRS matrix\n\tif (mode == 0)\n\t{\n\t\tFILE* fp = fopen(szfile, \"wb\");\n\t\tif (fp == 0) return false;\n\n\t\tint\tsymmFlag = K.isSymmetric();\n\t\tint offset = K.Offset();\n\t\tint rowFlag = K.isRowBased();\n\t\tint nrow = K.Rows();\n\t\tint ncol = K.Columns();\n\t\tint nsize = nrow;\t// this is the reason we currently assume CRS\n\t\tint nnz = K.NonZeroes();\n\t\tfwrite(&symmFlag, sizeof(symmFlag), 1, fp);\n\t\tfwrite(&offset, sizeof(offset), 1, fp);\n\t\tfwrite(&rowFlag, sizeof(rowFlag), 1, fp);\n\t\tfwrite(&nrow, sizeof(nrow), 1, fp);\n\t\tfwrite(&ncol, sizeof(ncol), 1, fp);\n\t\tfwrite(&nnz, sizeof(nnz), 1, fp);\n\t\tfwrite(K.Pointers(), sizeof(int), nsize + 1, fp);\n\t\tfwrite(K.Indices(), sizeof(int), nnz, fp);\n\t\tfwrite(K.Values(), sizeof(double), nnz, fp);\n\n\t\tfclose(fp);\n\t}\n\telse\n\t{\n\t\tFILE* fp = fopen(szfile, \"wt\");\n\t\tif (fp == 0) return false;\n\n\t\tint\tsymmFlag = K.isSymmetric();\n\t\tint offset = K.Offset();\n\t\tint rowFlag = K.isRowBased();\n\t\tint nrow = K.Rows();\n\t\tint ncol = K.Columns();\n\t\tint nsize = nrow;\t// this is the reason we currently assume CRS\n\t\tint nnz = K.NonZeroes();\n\t\tfprintf(fp, \"%d // symmetry flag\\n\", symmFlag);\n\t\tfprintf(fp, \"%d // offset\\n\", offset);\n\t\tfprintf(fp, \"%d // row flag\\n\", rowFlag);\n\t\tfprintf(fp, \"%d // number of rows\\n\", nrow);\n\t\tfprintf(fp, \"%d // number of columns\\n\", ncol);\n\t\tfprintf(fp, \"%d // nonzeroes\\n\", nnz);\n\t\tdouble* d = K.Values();\n\t\tfor (int i = 0; i < nnz; ++i)\n\t\t{\n\t\t\tfprintf(fp, \"%.12lg\\n\", d[i]);\n\t\t}\n\t\tfclose(fp);\n\t}\n\n\treturn true;\n}\n\n// write a vector to file\nbool NumCore::write_vector(const vector<double>& a, const char* szfile, int mode)\n{\n\tif (mode == 0)\n\t{\n\t\tFILE* fp = fopen(szfile, \"wb\");\n\t\tif (fp == 0) return false;\n\n\t\tint N = (int)a.size();\n\t\tfwrite(&N, sizeof(int), 1, fp);\n\t\tfwrite(&a[0], sizeof(double), N, fp);\n\n\t\tfclose(fp);\n\t}\n\telse\n\t{\n\t\tFILE* fp = fopen(szfile, \"wt\");\n\t\tif (fp == 0) return false;\n\n\t\tint N = (int)a.size();\n\t\tfprintf(fp, \"%d // size\\n\", N);\n\t\tfor (int i=0; i<N; ++i)\n\t\t\tfprintf(fp, \"%lg\\n\", a[i]);\n\n\t\tfclose(fp);\n\t}\n\n\treturn true;\n}\n\nCompactMatrix* NumCore::read_hb(const char* szfile)\n{\n\t// try to open the file\n\tif (szfile == nullptr) return nullptr;\n\tFILE* fp = fopen(szfile, \"rb\");\n\tif (fp == nullptr) return nullptr;\n\n\tint symmFlag, offset, rowFlag;\n\tint nrow = 0, ncol = 0, nnz = 0;\n\tif (fread(&symmFlag, sizeof(int), 1, fp) != 1) return nullptr;\n\tif (fread(&offset  , sizeof(int), 1, fp) != 1) return nullptr;\n\tif (fread(&rowFlag , sizeof(int), 1, fp) != 1) return nullptr;\n\tif (fread(&nrow    , sizeof(int), 1, fp) != 1) return nullptr;\n\tif (fread(&ncol    , sizeof(int), 1, fp) != 1) return nullptr;\n\tif (fread(&nnz     , sizeof(int), 1, fp) != 1) return nullptr;\n\n\t// allocate matrix\n\tint nsize = 0;\n\tCompactMatrix* K = 0;\n\tif (symmFlag == 1)\n\t{\n\t\tK = new CompactSymmMatrix(offset);\n\t\tnsize = nrow;\n\t}\n\telse\n\t{\n\t\tK = new CRSSparseMatrix(offset);\n\t\tnsize = ncol;\n\t}\n\n\t// allocate data\n\tdouble* pa = new double[nnz];\n\tint* pi = new int[nnz];\n\tint* pp = new int[nsize + 1];\n\tK->alloc(nrow, ncol, nnz, pa, pi, pp);\n\n\t// read in the data\n\tif (fread(pp, sizeof(int)   , nsize + 1, fp) != nsize + 1) { fclose(fp); delete K; return 0; }\n\tif (fread(pi, sizeof(int)   , nnz      , fp) != nnz      ) { fclose(fp); delete K; return 0; }\n\tif (fread(pa, sizeof(double), nnz      , fp) != nnz      ) { fclose(fp); delete K; return 0; }\n\n\tfclose(fp);\n\n\treturn K;\n}\n\n// read vector<double> from file\nbool NumCore::read_vector(std::vector<double>& a, const char* szfile)\n{\n\tif (szfile == nullptr) return false;\n\tFILE* fp = fopen(szfile, \"rb\");\n\tif (fp == nullptr) return false;\n\n\tint N;\n\tfread(&N, sizeof(int), 1, fp);\n\n\ta.resize(N);\n\tif (fread(&a[0], sizeof(double), N, fp) != N)\n\t{\n\t\tfclose(fp);\n\t\treturn false;\n\t}\n\n\tfclose(fp);\n\treturn true;\n}\n\n// calculate inf-norm of inverse matrix (only works with CRSSparsMatrix(1))\ndouble NumCore::inverse_infnorm(CompactMatrix* A)\n{\n\tPardisoSolver solver(0);\n\tif (solver.SetSparseMatrix(A) == false) return 0.0;\n\tif (solver.PreProcess() == false) return 0.0;\n\tif (solver.Factor() == false) return 0.0;\n\n\tint N = A->Rows();\n\tvector<double> e(N, 0.0), x(N, 0.0);\n\tvector<double> s(N, 0.0);\n\tfor (int i = 0; i < N; ++i)\n\t{\n\t\t// get the i-th column of the inverse matrix\n\t\te[i] = 1.0;\n\t\tsolver.BackSolve(&x[0], &e[0]);\n\n\t\t// add to net row sums\n\t\tfor (int j = 0; j < N; ++j) s[j] += fabs(x[j]);\n\n\t\t// reset e\n\t\te[i] = 0.0;\n\n\t\tif (i % 100 == 0)\n\t\t\tfprintf(stderr, \"%.2lg%%\\r\", 100.0 *i / N);\n\t}\n\n\t// get the max row sum\n\tdouble smax = s[0];\n\tfor (int i = 1; i < N; ++i) if (s[i] > smax) smax = s[i];\n\n\treturn smax;\n}\n\n// calculate condition number of a CRSSparseMatrix(1)\ndouble NumCore::conditionNumber(CRSSparseMatrix* A)\n{\n\tdouble norm = A->infNorm();\n\tdouble inorm = inverse_infnorm(A);\n\tdouble c = norm*inorm;\n\treturn c;\n}\n\n// This algorithm (naively) estimates the condition number. It is based on the observation that\n// for a linear system of equations A.x = b, the following holds\n// || A^-1 || >= ||x||.||b||\n// Thus the condition number can be estimated by\n// c = ||A||.||A^-1|| >= ||A|| . ||x|| / ||b||\n// This algorithm tries for some random b vectors with norm ||b||=1 to maxize the ||x||.\n// The returned value will be an underestimate of the condition number\ndouble NumCore::estimateConditionNumber(SparseMatrix* K)\n{\n\tCompactMatrix* A = dynamic_cast<CompactMatrix*>(K);\n\tif (A == nullptr) return 0.0;\n\tdouble normA = A->infNorm();\n\n\tPardisoSolver solver(0);\n\tif (solver.SetSparseMatrix(A) == false) return 0.0;\n\tif (solver.PreProcess() == false) return 0.0;\n\tif (solver.Factor() == false) return 0.0;\n\n\tint N = A->Rows();\n\tdouble normAi = 0.0;\n\n\tvector<double> b(N, 0), x(N, 0);\n\tint iters = (N < 50 ? N : 50);\n\tfor (int i = 0; i < iters; ++i)\n\t{\n\t\t// create a random vector\n\t\tNumCore::randomVector(b, -1.0, 1.0);\n\t\tfor (int j = 0; j < N; ++j) b[j] = (b[j] >= 0.0 ? 1.0 : -1.0);\n\n\t\t// calculate solution\n\t\tsolver.BackSolve(&x[0], &b[0]);\n\n\t\tdouble normb = infNorm(b);\n\t\tdouble normx = infNorm(x);\n\t\tif (normx > normAi) normAi = normx;\n\n//\t\tint pct = (100 * i) / (iters - 1);\n//\t\tfprintf(stderr, \"calculating condition number: %d%%\\r\", pct);\n\t}\n\n\treturn normA*normAi;\n}\n\ninline double frand() { return rand() / (double)RAND_MAX; }\n\nvoid NumCore::randomVector(vector<double>& R, double vmin, double vmax)\n{\n\tint neq = (int)R.size();\n\tfor (int i = 0; i<neq; ++i)\n\t{\n\t\tR[i] = vmin + frand()*(vmax - vmin);\n\t}\n}\n\n// inf-norm of a vector\ndouble NumCore::infNorm(const std::vector<double>& x)\n{\n\tdouble m = 0.0;\n\tfor (size_t i = 0; i < x.size(); ++i)\n\t{\n\t\tdouble xi = fabs(x[i]);\n\t\tif (xi > m) m = xi;\n\t}\n\treturn m;\n}\n\n// 1-norm of a vector\ndouble NumCore::oneNorm(const std::vector<double>& x)\n{\n\tdouble m = 0.0;\n\tfor (double xi : x)\n\t{\n\t\tm += fabs(xi);\n\t}\n\treturn m;\n}\n\n// print compact matrix pattern to svn file\nvoid NumCore::print_svg(CompactMatrix* m, std::ostream &out, int i0, int j0, int i1, int j1)\n{\n\tint rows = m->Rows();\n\tint cols = m->Columns();\n\n\tif (i0 < 0) i0 = 0;\n\tif (j0 < 0) j0 = 0;\n\tif (i1 == -1) i1 = rows - 1;\n\tif (j1 == -1) j1 = cols - 1;\n\tif (i1 >= rows) i1 = rows - 1;\n\tif (j1 >= cols) j1 = cols - 1;\n\n\tint rowSpan = i1 - i0 + 1;\n\tint colSpan = j1 - j0 + 1;\n\n\tout << \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" version=\\\"1.1\\\" viewBox=\\\"0 0 \" << colSpan + 2\n\t\t<< \" \" << rowSpan + 2 << \" \\\">\\n\"\n\t\t\"<style type=\\\"text/css\\\" >\\n\"\n\t\t\"     <![CDATA[\\n\"\n\t\t\"      rect.pixel1 {\\n\"\n\t\t\"          fill:   #ff0000;\\n\"\n\t\t\"      }\\n\"\n\t\t\"      rect.pixel2 {\\n\"\n\t\t\"          fill:   #0000ff;\\n\"\n\t\t\"      }\\n\"\n\t\t\"    ]]>\\n\"\n\t\t\"  </style>\\n\\n\"\n\t\t\"   <rect width=\\\"\" << colSpan + 2 << \"\\\" height=\\\"\" << rowSpan + 2 << \"\\\" fill=\\\"rgb(128, 128, 128)\\\"/>\\n\"\n\t\t\"   <rect x=\\\"1\\\" y=\\\"1\\\" width=\\\"\" << colSpan + 0.1 << \"\\\" height=\\\"\" << rowSpan + 0.1\n\t\t<< \"\\\" fill=\\\"rgb(255, 255, 255)\\\"/>\\n\\n\";\n\n\tdouble* pd = m->Values();\n\tint* pp = m->Pointers();\n\tint* pi = m->Indices();\n\tint offset = m->Offset();\n\n\tif (m->isRowBased())\n\t{\n\t\tint R = m->Rows();\n\t\tfor (int i = i0; i <= i1; ++i)\n\t\t{\n\t\t\tint* pc = pi + (pp[i] - offset);\n\t\t\tdouble* pv = pd + (pp[i] - offset);\n\t\t\tint n = pp[i + 1] - pp[i];\n\t\t\tfor (int k = 0; k < n; ++k)\n\t\t\t{\n\t\t\t\tint j = pc[k] - offset;\n\n\t\t\t\tif ((j >= j0) && (j <= j1))\n\t\t\t\t{\n\t\t\t\t\tdouble v = pv[k];\n\n\t\t\t\t\tif (v == 0.0)\n\t\t\t\t\t{\n\t\t\t\t\t\tout << \"  <rect class=\\\"pixel2\\\" x=\\\"\" << j - j0 + 1\n\t\t\t\t\t\t\t<< \"\\\" y=\\\"\" << i - i0 + 1\n\t\t\t\t\t\t\t<< \"\\\" width=\\\".9\\\" height=\\\".9\\\"/>\\n\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tout << \"  <rect class=\\\"pixel1\\\" x=\\\"\" << j - j0 + 1\n\t\t\t\t\t\t\t<< \"\\\" y=\\\"\" << i + i0 + 1\n\t\t\t\t\t\t\t<< \"\\\" width=\\\".9\\\" height=\\\".9\\\"/>\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tout << \"</svg>\" << std::endl;\n}\n"
  },
  {
    "path": "NumCore/MatrixTools.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <ostream>\n#include <FECore/CompactUnSymmMatrix.h>\n#include \"numcore_api.h\"\n\nnamespace NumCore\n{\n\t// write a matrix to file\n\t// mode:\n\t// - 0 = binary mode\n\t// - 1 = text mode\n\tNUMCORE_API bool write_hb(CompactMatrix& K, const char* szfile, int mode = 0);\n\n\t// write a vector to file\n\tNUMCORE_API bool write_vector(const std::vector<double>& a, const char* szfile, int mode = 0);\n\n\t// read compact matrix (binary mode only)\n\tNUMCORE_API CompactMatrix* read_hb(const char* szfile);\n\n\t// read vector<double> from file\n\tNUMCORE_API bool read_vector(std::vector<double>& a, const char* szfile);\n\n\t// calculate inf-norm of inverse matrix (only works with CRSSparsMatrix(1))\n\tNUMCORE_API double inverse_infnorm(CompactMatrix* A);\n\n\t// calculate condition number of a CRSSparseMatrix(1) (Very expensive!)\n\tNUMCORE_API double conditionNumber(CRSSparseMatrix* A);\n\n\t// estimate condition number\n\tNUMCORE_API double estimateConditionNumber(SparseMatrix* A);\n\n\t// create a random vector\n\tNUMCORE_API void randomVector(std::vector<double>& R, double vmin = 0.0, double vmax = 1.0);\n\n\t// inf-norm of a vector\n\tNUMCORE_API double infNorm(const std::vector<double>& x);\n\n\t// 1-norm of a vector\n\tNUMCORE_API double oneNorm(const std::vector<double>& x);\n\n\t// print matrix sparsity pattern to svn file\n\tNUMCORE_API void print_svg(CompactMatrix* m, std::ostream &out, int i0 = 0, int j0 = 0, int i1 = -1, int j1 = -1);\n\n} // namespace NumCore\n"
  },
  {
    "path": "NumCore/NumCore.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include \"NumCore.h\"\n#include \"PardisoSolver.h\"\n#include \"PardisoSolver64.h\"\n#include \"PardisoProjectSolver.h\"\n#include \"RCICGSolver.h\"\n#include \"FGMRESSolver.h\"\n#include \"ILU0_Preconditioner.h\"\n#include \"ILUT_Preconditioner.h\"\n#include \"BIPNSolver.h\"\n#include \"HypreGMRESsolver.h\"\n#include \"Hypre_PCG_AMG.h\"\n#include \"SchurSolver.h\"\n#include \"IncompleteCholesky.h\"\n#include \"BoomerAMGSolver.h\"\n#include \"BlockSolver.h\"\n#include \"BiCGStabSolver.h\"\n#include \"StrategySolver.h\"\n#include <FECore/fecore_enum.h>\n#include <FECore/FECoreFactory.h>\n#include <FECore/FECoreKernel.h>\n#include \"FEASTEigenSolver.h\"\n#include \"TestSolver.h\"\n#include \"AccelerateSparseSolver.h\"\n#include \"SuperLU_MT.h\"\n#include \"MKLDSSolver.h\"\n#include \"numcore_api.h\"\n\n//=============================================================================\n// Call this to initialize the NumCore module\nNUMCORE_API void NumCore::InitModule()\n{\n\t// register linear solvers\n#ifdef PARDISO\n\tREGISTER_FECORE_CLASS(PardisoSolver  , \"pardiso\");\n\tREGISTER_FECORE_CLASS(PardisoSolver64, \"pardiso_64\");\n\tREGISTER_FECORE_CLASS(MKLDSSolver, \"mkl_dss\");\n#endif\n\n    REGISTER_FECORE_CLASS(PardisoProjectSolver, \"pardiso-project\");\n\tREGISTER_FECORE_CLASS(FGMRESSolver        , \"fgmres\"   );\n\tREGISTER_FECORE_CLASS(BoomerAMGSolver     , \"boomeramg\");\n\tREGISTER_FECORE_CLASS(RCICGSolver         , \"cg\"    );\n\tREGISTER_FECORE_CLASS(SchurSolver         , \"schur\"    );\n\tREGISTER_FECORE_CLASS(HypreGMRESsolver    , \"hypre_gmres\");\n\tREGISTER_FECORE_CLASS(Hypre_PCG_AMG       , \"hypre_pcg_amg\");\n\tREGISTER_FECORE_CLASS(BlockIterativeSolver, \"block\");\n\tREGISTER_FECORE_CLASS(BIPNSolver          , \"bipn\");\n\tREGISTER_FECORE_CLASS(BiCGStabSolver      , \"bicgstab\");\n\tREGISTER_FECORE_CLASS(StrategySolver      , \"strategy\");\n\tREGISTER_FECORE_CLASS(TestSolver          , \"test\");\n    REGISTER_FECORE_CLASS(AccelerateSparseSolver, \"accelerate\");\n    REGISTER_FECORE_CLASS(SuperLU_MT_Solver     , \"superlu_mt\");\n\n\t// register preconditioners\n\tREGISTER_FECORE_CLASS(ILU0_Preconditioner, \"ilu0\");\n\tREGISTER_FECORE_CLASS(ILUT_Preconditioner, \"ilut\");\n\tREGISTER_FECORE_CLASS(IncompleteCholesky , \"ichol\");\n\n\t// register eigen solvers\n\tREGISTER_FECORE_CLASS(FEASTEigenSolver, \"feast\", FECORE_EXPERIMENTAL);\n\n\t// set default linear solver\n\t// (Set this before the configuration is read in because\n\t//  the configuration can change the default linear solver.)\n\tFECoreKernel& fecore = FECoreKernel::GetInstance();\n#ifdef PARDISO\n\tfecore.SetDefaultSolverType(\"pardiso\");\n#else\n\tfecore.SetDefaultSolverType(\"skyline\");\n#endif\n}\n"
  },
  {
    "path": "NumCore/NumCore.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#include \"FECore/LinearSolver.h\"\n#include \"numcore_api.h\"\n\nnamespace NumCore {\n\n\tNUMCORE_API void InitModule();\n\n} // namespace NumCore\n"
  },
  {
    "path": "NumCore/PardisoProjectSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"PardisoProjectSolver.h\"\n#include \"MatrixTools.h\"\n#include <FECore/log.h>\n\n//! This implementation of the Pardiso solver is for the version\n//! available in the Intel MKL.\n\n\n#ifdef PARDISODL\n\n#include <thread>\n\n/* PARDISO prototype. */\nextern \"C\" void pardisoinit (void   *, int    *,   int *, int *, double *, int *);\nextern \"C\" void pardiso     (void   *, int    *,   int *, int *,    int *, int *,\n                             double *, int    *,    int *, int *,   int *, int *,\n                             int *, double *, double *, int *, double *);\n\n//-----------------------------------------------------------------------------\n// print pardiso error message\nvoid print_err_pdl(int nerror)\n{\n    switch (-nerror)\n    {\n        case 1: fprintf(stderr, \"Inconsistent input\\n\"); break;\n        case 2: fprintf(stderr, \"Not enough memory\\n\"); break;\n        case 3: fprintf(stderr, \"Reordering problem\\n\"); break;\n        case 4: fprintf(stderr, \"Zero pivot, numerical fact. or iterative refinement problem\\n\"); break;\n        case 5: fprintf(stderr, \"Unclassified (internal) error\\n\"); break;\n        case 6: fprintf(stderr, \"Preordering failed\\n\"); break;\n        case 7: fprintf(stderr, \"Diagonal matrix problem\\n\"); break;\n        case 8: fprintf(stderr, \"32-bit integer overflow problem\\n\"); break;\n        default:\n            fprintf(stderr, \" Unknown\\n\");\n    }\n}\n\n//////////////////////////////////////////////////////////////\n// PardisoProjectSolver\n//////////////////////////////////////////////////////////////\n\nBEGIN_FECORE_CLASS(PardisoProjectSolver, LinearSolver)\nADD_PARAMETER(m_print_cn, \"print_condition_number\");\nADD_PARAMETER(m_iparm3  , \"precondition\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nPardisoProjectSolver::PardisoProjectSolver(FEModel* fem) : LinearSolver(fem), m_pA(0)\n{\n    m_print_cn = false;\n    m_mtype = -2;\n    m_iparm3 = false;\n    m_isFactored = false;\n    \n    /* If both PARDISO AND PARDISODL are defined, print a warning */\n#ifdef PARDISO\n    fprintf(stderr, \"WARNING: The pardiso-project version of the Pardiso solver is being used\\n\\n\");\n    exit(1);\n#endif\n}\n\n//-----------------------------------------------------------------------------\nPardisoProjectSolver::~PardisoProjectSolver()\n{\n}\n\n//-----------------------------------------------------------------------------\nvoid PardisoProjectSolver::PrintConditionNumber(bool b)\n{\n    m_print_cn = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid PardisoProjectSolver::UseIterativeFactorization(bool b)\n{\n    m_iparm3 = b;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* PardisoProjectSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n    // allocate the correct matrix format depending on matrix symmetry type\n    switch (ntype)\n    {\n        case REAL_SYMMETRIC     : m_mtype = -2; m_pA = new CompactSymmMatrix(1); break;\n        case REAL_UNSYMMETRIC   : m_mtype = 11; m_pA = new CRSSparseMatrix(1); break;\n        case REAL_SYMM_STRUCTURE: m_mtype =  1; m_pA = new CRSSparseMatrix(1); break;\n        default:\n            assert(false);\n            m_pA = nullptr;\n    }\n    \n    return m_pA;\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoProjectSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n    if (m_pA && m_isFactored) Destroy();\n    m_pA = dynamic_cast<CompactMatrix*>(pA);\n    m_mtype = -2;\n    if (dynamic_cast<CRSSparseMatrix*>(pA)) m_mtype = 11;\n    return (m_pA != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoProjectSolver::PreProcess()\n{\n    m_iparm[0] = 0; /* Use default values for parameters */\n//    for (int i=0; i<64; ++i) m_pt[i] = nullptr;\n    \n    //fprintf(stderr, \"In PreProcess\\n\");\n    assert(m_isFactored == false);\n    int error = 0;\n    int solver = 0; /* use sparse direct solver */\n    pardisoinit(m_pt, &m_mtype, &solver, m_iparm, m_dparm, &error);\n    \n    m_n = m_pA->Rows();\n    m_nnz = m_pA->NonZeroes();\n    m_nrhs = 1;\n    \n    /* Numbers of processors, value of OMP_NUM_THREADS (if available) or\n     total number of processors on machine */\n    const auto processor_count = std::thread::hardware_concurrency();\n    int num_procs = processor_count;\n    char* var = getenv(\"OMP_NUM_THREADS\");\n    if (var != NULL)\n        sscanf( var, \"%d\", &num_procs );\n    feLog(\"Number of processors: %d\\n\",num_procs);\n    if (num_procs == 0) {\n        feLogError(\"Specify number of processors in environmental variable OMP_NUM_THREADS!\");\n        return false;\n    }\n    m_iparm[2]  = num_procs;\n\n    m_maxfct = 1;    /* Maximum number of numerical factorizations */\n    m_mnum = 1;    /* Which factorization to use */\n    \n    m_msglvl = 0;    /* 0 Suppress printing, 1 Print statistical information */\n    \n    return LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoProjectSolver::Factor()\n{\n    // make sure we have work to do\n    if (m_pA->Rows() == 0) return true;\n    \n    // ------------------------------------------------------------------------------\n    // Reordering and Symbolic Factorization.  This step also allocates all memory\n    // that is necessary for the factorization.\n    // ------------------------------------------------------------------------------\n    \n    int phase = 11;\n    \n    int error = 0;\n    pardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, m_pA->Values(), m_pA->Pointers(), m_pA->Indices(),\n            NULL, &m_nrhs, m_iparm, &m_msglvl, NULL, NULL, &error, m_dparm);\n    \n    if (error)\n    {\n        fprintf(stderr, \"\\nERROR during symbolic factorization: \");\n        print_err_pdl(error);\n        exit(2);\n    }\n    \n    // ------------------------------------------------------------------------------\n    // This step does the factorization\n    // ------------------------------------------------------------------------------\n    \n    phase = 22;\n    \n    m_iparm[3] = (m_iparm3 ? 61 : 0);\n    error = 0;\n    pardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, m_pA->Values(), m_pA->Pointers(), m_pA->Indices(),\n            NULL, &m_nrhs, m_iparm, &m_msglvl, NULL, NULL, &error, m_dparm);\n    \n    if (error)\n    {\n        fprintf(stderr, \"\\nERROR during factorization: \");\n        print_err_pdl(error);\n        return false;\n    }\n    \n    // calculate and print the condition number\n    if (m_print_cn)\n    {\n        double c = condition_number();\n        feLog(\"\\tcondition number (est.) ................... : %lg\\n\\n\", c);\n    }\n    \n    m_isFactored = true;\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoProjectSolver::BackSolve(double* x, double* b)\n{\n    // make sure we have work to do\n    if (m_pA->Rows() == 0) return true;\n    \n    int phase = 33;\n    \n    m_iparm[7] = 1;    /* Maximum number of iterative refinement steps */\n    \n    int error = 0;\n    pardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, m_pA->Values(), m_pA->Pointers(), m_pA->Indices(),\n            NULL, &m_nrhs, m_iparm, &m_msglvl, b, x, &error, m_dparm);\n    \n    if (error)\n    {\n        fprintf(stderr, \"\\nERROR during solution: \");\n        print_err_pdl(error);\n        exit(3);\n    }\n    \n    // update stats\n    UpdateStats(1);\n    \n    return true;\n}\n\n//-----------------------------------------------------------------------------\n// This algorithm (naively) estimates the condition number. It is based on the observation that\n// for a linear system of equations A.x = b, the following holds\n// || A^-1 || >= ||x||.||b||\n// Thus the condition number can be estimated by\n// c = ||A||.||A^-1|| >= ||A|| . ||x|| / ||b||\n// This algorithm tries for some random b vectors with norm ||b||=1 to maxize the ||x||.\n// The returned value will be an underestimate of the condition number\ndouble PardisoProjectSolver::condition_number()\n{\n    // This assumes that the factorization is already done!\n    int N = m_pA->Rows();\n    \n    // get the norm of the matrix\n    double normA = m_pA->infNorm();\n    \n    // estimate the norm of the inverse of A\n    double normAi = 0.0;\n    \n    // choose max iterations\n    int iters = (N < 50 ? N : 50);\n    \n    vector<double> b(N, 0), x(N, 0);\n    for (int i = 0; i < iters; ++i)\n    {\n        // create a random vector\n        NumCore::randomVector(b, -1.0, 1.0);\n        for (int j = 0; j < N; ++j) b[j] = (b[j] >= 0.0 ? 1.0 : -1.0);\n        \n        // calculate solution\n        BackSolve(&x[0], &b[0]);\n        \n        double normb = NumCore::infNorm(b);\n        double normx = NumCore::infNorm(x);\n        if (normx > normAi) normAi = normx;\n        \n        int pct = (100 * i) / (iters - 1);\n        fprintf(stderr, \"calculating condition number: %d%%\\r\", pct);\n    }\n    \n    double c = normA*normAi;\n    return c;\n}\n\n//-----------------------------------------------------------------------------\nvoid PardisoProjectSolver::Destroy()\n{\n    int phase = -1;\n    \n    int error = 0;\n    \n    if (m_pA && m_pA->Pointers() && m_isFactored)\n    {\n        pardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, NULL, m_pA->Pointers(), m_pA->Indices(),\n                NULL, &m_nrhs, m_iparm, &m_msglvl, NULL, NULL, &error, m_dparm);\n    }\n    m_isFactored = false;\n}\n\n#else\nBEGIN_FECORE_CLASS(PardisoProjectSolver, LinearSolver)\n\tADD_PARAMETER(m_print_cn, \"print_condition_number\");\n\tADD_PARAMETER(m_iparm3, \"precondition\");\nEND_FECORE_CLASS();\n\nPardisoProjectSolver::PardisoProjectSolver(FEModel* fem) : LinearSolver(fem) {}\nPardisoProjectSolver::~PardisoProjectSolver() {}\nbool PardisoProjectSolver::PreProcess() { return false; }\nbool PardisoProjectSolver::Factor() { return false; }\nbool PardisoProjectSolver::BackSolve(double* x, double* y) { return false; }\nvoid PardisoProjectSolver::Destroy() {}\nSparseMatrix* PardisoProjectSolver::CreateSparseMatrix(Matrix_Type ntype) { return nullptr; }\nbool PardisoProjectSolver::SetSparseMatrix(SparseMatrix* pA) { return false; }\nvoid PardisoProjectSolver::PrintConditionNumber(bool b) {}\ndouble PardisoProjectSolver::condition_number() { return 0; }\nvoid PardisoProjectSolver::UseIterativeFactorization(bool b) {}\n#endif\n"
  },
  {
    "path": "NumCore/PardisoProjectSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/CompactSymmMatrix.h>\n\n//! This Pardiso solver can be installed as a shared object library from\n//!\t\thttp://www.pardiso-project.org\n\n\nclass PardisoProjectSolver : public LinearSolver\n{\npublic:\n\tPardisoProjectSolver(FEModel* fem);\n\t~PardisoProjectSolver();\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\n\tvoid PrintConditionNumber(bool b);\n\n\tdouble condition_number();\n\n\tvoid UseIterativeFactorization(bool b);\n\nprotected:\n\n\tCompactMatrix*\tm_pA;\n\tint\t\t\t\tm_mtype; // matrix type\n\n\t// Pardiso control parameters\n\tint m_iparm[64];\n\tint m_maxfct, m_mnum, m_msglvl;\n\tdouble m_dparm[64];\n\n\tbool m_iparm3;\t// use direct-iterative method\n\n\t// Matrix data\n\tint m_n, m_nnz, m_nrhs;\n\n\tbool\tm_print_cn;\t// estimate and print the condition number\n\n\tbool\tm_isFactored;\n\n\tvoid* m_pt[64]; // Internal solver memory pointer\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/PardisoSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"PardisoSolver.h\"\n#include \"MatrixTools.h\"\n#include <FECore/log.h>\n\n//! This implementation of the Pardiso solver is for the version\n//! available in the Intel MKL.\n\n\n#ifdef PARDISO\n#undef PARDISO\n#include <mkl.h>\n#include <mkl_pardiso.h>\n#define PARDISO\n#else\n/* Pardiso prototypes for shared object library version */\n\n#ifdef WIN32\n\n#define pardisoinit_ PARDISOINIT\n#define pardiso_ PARDISO\n\n#endif\n\nextern \"C\"\n{\n\tint pardisoinit_(void *, int *, int *, int *, double*, int*);\n\n\tint pardiso_(void *, int *, int *, int *, int *, int *,\n\t\tdouble *, int *, int *, int *, int *, int *,\n\t\tint *, double *, double *, int *, double*);\n}\n#endif\n\n#ifdef PARDISO\n\n//-----------------------------------------------------------------------------\n// print pardiso error message\nvoid print_err(int nerror)\n{\n\tswitch (-nerror)\n\t{\n\tcase 1: fprintf(stderr, \"Inconsistent input\\n\"); break;\n\tcase 2: fprintf(stderr, \"Not enough memory\\n\"); break;\n\tcase 3: fprintf(stderr, \"Reordering problem\\n\"); break;\n\tcase 4: fprintf(stderr, \"Zero pivot, numerical fact. or iterative refinement problem\\n\"); break;\n\tcase 5: fprintf(stderr, \"Unclassified (internal) error\\n\"); break;\n\tcase 6: fprintf(stderr, \"Preordering failed\\n\"); break;\n\tcase 7: fprintf(stderr, \"Diagonal matrix problem\\n\"); break;\n\tcase 8: fprintf(stderr, \"32-bit integer overflow problem\\n\"); break;\n\tdefault:\n\t\tfprintf(stderr, \" Unknown\\n\");\n\t}\n}\n\n//////////////////////////////////////////////////////////////\n// PardisoSolver\n//////////////////////////////////////////////////////////////\n\nBEGIN_FECORE_CLASS(PardisoSolver, LinearSolver)\n\tADD_PARAMETER(m_print_cn, \"print_condition_number\");\n\tADD_PARAMETER(m_iparm3  , \"precondition\");\n\tADD_PARAMETER(m_msglvl  , \"msglvl\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nPardisoSolver::PardisoSolver(FEModel* fem) : LinearSolver(fem), m_pA(0)\n{\n\tm_print_cn = false;\n\tm_mtype = -2;\n\tm_iparm3 = false;\n\tm_isFactored = false;\n\tm_msglvl = 0; /* 0 Suppress printing, 1 Print statistical information */\n}\n\n//-----------------------------------------------------------------------------\nPardisoSolver::~PardisoSolver()\n{\n#ifdef PARDISO\n\tMKL_Free_Buffers();\n#endif\n}\n\n//-----------------------------------------------------------------------------\nvoid PardisoSolver::PrintConditionNumber(bool b)\n{\n\tm_print_cn = b;\n}\n\n//-----------------------------------------------------------------------------\nvoid PardisoSolver::UseIterativeFactorization(bool b)\n{\n\tm_iparm3 = b;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* PardisoSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// allocate the correct matrix format depending on matrix symmetry type\n\tswitch (ntype)\n\t{\n\tcase REAL_SYMMETRIC     : m_mtype = -2; m_pA = new CompactSymmMatrix(1); break;\n\tcase REAL_UNSYMMETRIC   : m_mtype = 11; m_pA = new CRSSparseMatrix(1); break;\n\tcase REAL_SYMM_STRUCTURE: m_mtype =  1; m_pA = new CRSSparseMatrix(1); break;\n\tdefault:\n\t\tassert(false);\n\t\tm_pA = nullptr;\n\t}\n\n\treturn m_pA;\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n\tif (m_pA && m_isFactored) Destroy();\n\tm_pA = dynamic_cast<CompactMatrix*>(pA);\n\tm_mtype = -2;\n\tif (dynamic_cast<CRSSparseMatrix*>(pA)) m_mtype = 11;\n\treturn (m_pA != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoSolver::PreProcess()\n{\n\tm_iparm[0] = 0; /* Use default values for parameters */\n\n\t//fprintf(stderr, \"In PreProcess\\n\");\n\tassert(m_isFactored == false);\n\tpardisoinit(m_pt, &m_mtype, m_iparm);\n\n\t// Turn off reporting the number of non-zero elements in the factors.\n\t// According to the documentation turning this on (set to -1) will \n\t// increase the reordering time.\n\tm_iparm[18] = 0; \n\n\t// check the matrix offset\n\tm_iparm[34] = 0;\n\tif (m_pA->Offset() == 0) m_iparm[34] = 1;\n\n\tm_n = m_pA->Rows();\n\tm_nnz = m_pA->NonZeroes();\n\tm_nrhs = 1;\n\n\t// number of processors: This parameter is no longer used.\n\t// Use OMP_NUM_THREADS\n\t// m_iparm[2] = m_numthreads;\n\n\tm_maxfct = 1;\t/* Maximum number of numerical factorizations */\n\tm_mnum = 1;\t/* Which factorization to use */\n\n\treturn LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoSolver::Factor()\n{\n\t// make sure we have work to do\n\tif (m_pA->Rows() == 0) return true;\n\n// ------------------------------------------------------------------------------\n// Reordering and Symbolic Factorization.  This step also allocates all memory\n// that is necessary for the factorization.\n// ------------------------------------------------------------------------------\n\n\tint phase = 11;\n\n\tint error = 0;\n\tpardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, m_pA->Values(), m_pA->Pointers(), m_pA->Indices(),\n\t\t NULL, &m_nrhs, m_iparm, &m_msglvl, NULL, NULL, &error);\n\n\tif (error)\n\t{\n\t\tfprintf(stderr, \"\\nERROR during symbolic factorization: \");\n\t\tprint_err(error);\n\t\texit(2);\n\t}\n\n\tif (m_msglvl == 1)\n\t{\n\t\tint* ip = m_iparm;\n\t\tfprintf(stdout, \"\\nMemory info:\\n\");\n\t\tfprintf(stdout, \"============\\n\");\n\t\tfprintf(stdout, \"Peak memory on symbolic factorization ............. : %d KB\\n\", ip[14]);\n\t\tfprintf(stdout, \"Permanent memory on symbolic factorization ........ : %d KB\\n\", ip[15]);\n\t\tfprintf(stdout, \"Peak memory on numerical factorization and solution : %d KB\\n\", ip[16]);\n\t\tfprintf(stdout, \"Total peak memory ................................. : %d KB\\n\\n\", max(ip[14], ip[15]+ip[16]));\n\t}\n\n// ------------------------------------------------------------------------------\n// This step does the factorization\n// ------------------------------------------------------------------------------\n\n\tphase = 22;\n\n\tm_iparm[3] = (m_iparm3 ? 61 : 0);\n\terror = 0;\n\tpardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, m_pA->Values(), m_pA->Pointers(), m_pA->Indices(),\n\t\t NULL, &m_nrhs, m_iparm, &m_msglvl, NULL, NULL, &error);\n\n\tif (error)\n\t{\n\t\tfprintf(stderr, \"\\nERROR during factorization: \");\n\t\tprint_err(error);\n\t\treturn false;\n\t}\n\n\tm_isFactored = true;\n\n\t// calculate and print the condition number\n\tif (m_print_cn)\n\t{\n\t\tdouble c = ConditionNumber();\n\t\tfeLog(\"\\tcondition number (est.) ................... : %lg\\n\\n\", c);\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool PardisoSolver::BackSolve(double* x, double* b)\n{\n\t// make sure we have work to do\n\tif (m_pA->Rows() == 0) return true;\n\n\tint phase = 33;\n\n\tm_iparm[7] = 1;\t/* Maximum number of iterative refinement steps */\n\n\tint error = 0;\n\tpardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, m_pA->Values(), m_pA->Pointers(), m_pA->Indices(),\n\t\t NULL, &m_nrhs, m_iparm, &m_msglvl, b, x, &error);\n\n\tif (error)\n\t{\n\t\tfprintf(stderr, \"\\nERROR during solution: \");\n\t\tprint_err(error);\n\t\texit(3);\n\t}\n\n\t// update stats\n\tUpdateStats(1);\n\n\treturn true;\n}\n\ndouble PardisoSolver::ConditionNumber()\n{\n\tif (m_isFactored == false) return 0.0;\n\n\t// This assumes that the factorization is already done!\n\tint N = m_pA->Rows();\n\n\t// get the norm of the matrix\n\tdouble normA = m_pA->oneNorm();\n\n\t// estimate the norm of the inverse of A\n\tdouble normAi = 0.0;\n\n\t// choose max iterations\n\t// this method should converge, but just in case\n\tint iters = 50;\n\tint steps = 3;\n\n\tvector<double> b(N, 0), y(N, 0), z(N,0), x(N, 0), v(N, 1);\n\tfor (int n = 0; n < steps; ++n)\n\t{\n\t\t// initialize x\n\t\tdouble m = 0.0;\n\t\tfor (int i = 0; i < N; ++i)\n\t\t{\n\t\t\tx[i] = v[i];\n\t\t\tm += v[i];\n\t\t}\n\t\tfor (int i = 0; i < N; ++i) x[i] /= m;\n\n\t\tfor (int i = 0; i < iters; ++i)\n\t\t{\n\t\t\tBackSolve(&y[0], &x[0]);\n\n\t\t\tfor (int j = 0; j < N; ++j)\n\t\t\t{\n\t\t\t\tif (y[j] >= 0) b[j] = 1;\n\t\t\t\telse b[j] = -1;\n\t\t\t}\n\n\t\t\t// Solve transpose\n\t\t\tm_iparm[11] = 2;\n\t\t\tBackSolve(&z[0], &b[0]);\n\t\t\tm_iparm[11] = 0;\n\n\t\t\tdouble zmax = 0.0;\n\t\t\tint jmax = 0;\n\t\t\tdouble z_dot_x = 0.0;\n\t\t\tfor (int j = 0; j < N; ++j)\n\t\t\t{\n\t\t\t\tif (fabs(z[j]) > zmax)\n\t\t\t\t{\n\t\t\t\t\tzmax = fabs(z[j]);\n\t\t\t\t\tjmax = j;\n\t\t\t\t}\n\t\t\t\tz_dot_x += z[j] * x[j];\n\t\t\t}\n\t\t\tv[jmax] = 0;\n\n\t\t\tif (zmax <= z_dot_x) break;\n\n\t\t\tfor (int j = 0; j < N; ++j)\n\t\t\t\tx[j] = (j == jmax ? 1.0 : 0.0);\n\t\t}\n\n\t\tdouble normAi_n = NumCore::oneNorm(y);\n\t\tif (normAi_n > normAi) normAi = normAi_n;\n\t\telse break;\n\t}\n\n\tdouble c = normA * normAi;\n\treturn c;\n}\n\n//-----------------------------------------------------------------------------\nvoid PardisoSolver::Destroy()\n{\n\tint phase = -1;\n\n\tint error = 0;\n\n\tif (m_pA && m_isFactored)\n\t{\n\t\tpardiso(m_pt, &m_maxfct, &m_mnum, &m_mtype, &phase, &m_n, NULL, m_pA->Pointers(), m_pA->Indices(),\n\t\t\tNULL, &m_nrhs, m_iparm, &m_msglvl, NULL, NULL, &error);\n\t}\n\tm_isFactored = false;\n}\n#else \nBEGIN_FECORE_CLASS(PardisoSolver, LinearSolver)\n\tADD_PARAMETER(m_print_cn, \"print_condition_number\");\n\tADD_PARAMETER(m_iparm3, \"precondition\");\nEND_FECORE_CLASS();\n\nPardisoSolver::PardisoSolver(FEModel* fem) : LinearSolver(fem) {}\nPardisoSolver::~PardisoSolver() {}\nbool PardisoSolver::PreProcess() { return false; }\nbool PardisoSolver::Factor() { return false; }\nbool PardisoSolver::BackSolve(double* x, double* y) { return false; }\nvoid PardisoSolver::Destroy() {}\nSparseMatrix* PardisoSolver::CreateSparseMatrix(Matrix_Type ntype) { return nullptr; }\nbool PardisoSolver::SetSparseMatrix(SparseMatrix* pA) { return false; }\nvoid PardisoSolver::PrintConditionNumber(bool b) {}\ndouble PardisoSolver::ConditionNumber() { return 0; }\nvoid PardisoSolver::UseIterativeFactorization(bool b) {}\n#endif\n"
  },
  {
    "path": "NumCore/PardisoSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/CompactSymmMatrix.h>\n\n//! The Pardiso solver is included in the Intel Math Kernel Library (MKL).\n//! It can also be installed as a shared object library from\n//!\t\thttp://www.pardiso-project.org\n\n\nclass PardisoSolver : public LinearSolver\n{\npublic:\n\tPardisoSolver(FEModel* fem);\n\t~PardisoSolver();\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\n\tvoid PrintConditionNumber(bool b);\n\n\tdouble ConditionNumber() override;\n\n\tvoid UseIterativeFactorization(bool b);\n\nprotected:\n\n\tCompactMatrix*\tm_pA;\n\tint\t\t\t\tm_mtype; // matrix type\n\n\t// Pardiso control parameters\n\tint m_iparm[64];\n\tint m_maxfct, m_mnum, m_msglvl;\n\tdouble m_dparm[64];\n\n\tbool m_iparm3;\t// use direct-iterative method\n\n\t// Matrix data\n\tint m_n, m_nnz, m_nrhs;\n\n\tbool\tm_print_cn;\t// estimate and print the condition number\n\n\tbool\tm_isFactored;\n\n\tvoid* m_pt[64]; // Internal solver memory pointer\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/PardisoSolver64.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"PardisoSolver64.h\"\n#include \"MatrixTools.h\"\n#include <FECore/CompactSymmMatrix64.h>\n#include <FECore/log.h>\n\n#ifdef PARDISO\n#undef PARDISO\n#include <mkl.h>\n#include <mkl_pardiso.h>\n#define PARDISO\n#endif\n\nclass PardisoSolver64::Imp\n{\npublic:\n\n\tCompactSymmMatrix64*\tpA = nullptr;\n\tlong long\t\tmtype = -2; // matrix type\n\n\t// Pardiso control parameters\n\tlong long iparm[64] = { 0 };\n\tlong long maxfct = 0, mnum = 0;\n\tint msglvl = 0;\n\tdouble dparm[64] = { 0 };\n\n\tbool iparm3 = false;\t// use direct-iterative method\n\n\t// Matrix data\n\tlong long n = 0, nnz = 0, nrhs = 0;\n\n\tbool\tisFactored = false;\n\n\tvoid* pt[64] = { nullptr }; // Internal solver memory pointer\n\n\tvoid print_err(long long nerror);\n};\n\n//-----------------------------------------------------------------------------\n// print pardiso error message\nvoid PardisoSolver64::Imp::print_err(long long nerror)\n{\n\tswitch (-nerror)\n\t{\n\tcase 1: fprintf(stderr, \"Inconsistent input\\n\"); break;\n\tcase 2: fprintf(stderr, \"Not enough memory\\n\"); break;\n\tcase 3: fprintf(stderr, \"Reordering problem\\n\"); break;\n\tcase 4: fprintf(stderr, \"Zero pivot, numerical fact. or iterative refinement problem\\n\"); break;\n\tcase 5: fprintf(stderr, \"Unclassified (internal) error\\n\"); break;\n\tcase 6: fprintf(stderr, \"Preordering failed\\n\"); break;\n\tcase 7: fprintf(stderr, \"Diagonal matrix problem\\n\"); break;\n\tcase 8: fprintf(stderr, \"32-bit integer overflow problem\\n\"); break;\n\tdefault:\n\t\tfprintf(stderr, \" Unknown\\n\");\n\t}\n}\n\nBEGIN_FECORE_CLASS(PardisoSolver64, LinearSolver)\n\tADD_PARAMETER(m.iparm3  , \"precondition\");\n\tADD_PARAMETER(m.msglvl  , \"msglvl\");\nEND_FECORE_CLASS();\n\nPardisoSolver64::PardisoSolver64(FEModel* fem) : LinearSolver(fem), m(*(new Imp))\n{\n\n}\n\nPardisoSolver64::~PardisoSolver64()\n{\n#ifdef PARDISO\n\tMKL_Free_Buffers();\n#endif\n}\n\nvoid PardisoSolver64::UseIterativeFactorization(bool b)\n{\n\tm.iparm3 = b;\n}\n\nSparseMatrix* PardisoSolver64::CreateSparseMatrix(Matrix_Type ntype)\n{\n#ifdef PARDISO\n\t// allocate the correct matrix format depending on matrix symmetry type\n\tswitch (ntype)\n\t{\n\tcase REAL_SYMMETRIC     : m.mtype = -2; m.pA = new CompactSymmMatrix64(1); break;\n\tdefault:\n\t\tassert(false);\n\t\tm.pA = nullptr;\n\t}\n\n\treturn m.pA;\n#else\n\treturn nullptr;\n#endif\n}\n\nbool PardisoSolver64::SetSparseMatrix(SparseMatrix* pA)\n{\n#ifdef PARDISO\n\tif (m.pA && m.isFactored) Destroy();\n\tm.pA = dynamic_cast<CompactSymmMatrix64*>(pA);\n\tm.mtype = -2;\n\treturn (m.pA != nullptr);\n#else\n\treturn false;\n#endif\n}\n\nbool PardisoSolver64::PreProcess()\n{\n#ifdef PARDISO\n\tassert(m.isFactored == false);\n\tm.iparm[0] = 1; // supply all values\n\tm.iparm[1] = 2; // nested dissection algorithm from METIS\n\tm.iparm[9] = 8; // The default value for symmetric indefinite matrices (mtype =-2, mtype=-4, mtype=6), eps = 1e-8\n\tm.iparm[17] = 0; // Report the number of non-zero elements in the factors.(disable)\n\tm.iparm[18] = 0; // Disable report.\n\tm.iparm[20] = 1; // Pivoting for symmetric indefinite matrices. ( for matrices of mtype=-2, mtype=-4, or mtype=6.)\n\n\tm.n = m.pA->Rows();\n\tm.nnz = m.pA->NonZeroes();\n\tm.nrhs = 1;\n\n\tm.maxfct = 1;\t/* Maximum number of numerical factorizations */\n\tm.mnum = 1;\t/* Which factorization to use */\n\n\treturn LinearSolver::PreProcess();\n#else\n\treturn false;\n#endif\n}\n\nbool PardisoSolver64::Factor()\n{\n#ifdef PARDISO\n\t// make sure we have work to do\n\tif (m.pA->Rows() == 0) return true;\n\n// ------------------------------------------------------------------------------\n// Reordering and Symbolic Factorization.  This step also allocates all memory\n// that is necessary for the factorization.\n// ------------------------------------------------------------------------------\n\n\tlong long phase = 11;\n\tlong long error = 0;\n\tlong long msglvl = m.msglvl;\n\tpardiso_64(m.pt, &m.maxfct, &m.mnum, &m.mtype, &phase, &m.n, m.pA->Values(), m.pA->Pointers64(), m.pA->Indices64(),\n\t\t NULL, &m.nrhs, m.iparm, &msglvl, NULL, NULL, &error);\n\n\tif (error)\n\t{\n\t\tfprintf(stderr, \"\\nERROR during symbolic factorization: \");\n\t\tm.print_err(error);\n\t\texit(2);\n\t}\n\n\tif (m.msglvl == 1)\n\t{\n\t\tlong long* ip = m.iparm;\n\t\tfprintf(stdout, \"\\nMemory info:\\n\");\n\t\tfprintf(stdout, \"============\\n\");\n\t\tfprintf(stdout, \"Peak memory on symbolic factorization ............. : %d KB\\n\", (int)ip[14]);\n\t\tfprintf(stdout, \"Permanent memory on symbolic factorization ........ : %d KB\\n\", (int)ip[15]);\n\t\tfprintf(stdout, \"Peak memory on numerical factorization and solution : %d KB\\n\", (int)ip[16]);\n\t\tfprintf(stdout, \"Total peak memory ................................. : %d KB\\n\\n\", (int)max(ip[14], ip[15]+ip[16]));\n\t}\n\n// ------------------------------------------------------------------------------\n// This step does the factorization\n// ------------------------------------------------------------------------------\n\n\tm.iparm[3] = (m.iparm3 ? 61 : 0);\n\tphase = 22;\n\terror = 0;\n\tpardiso_64(m.pt, &m.maxfct, &m.mnum, &m.mtype, &phase, &m.n, m.pA->Values(), m.pA->Pointers64(), m.pA->Indices64(),\n\t\t NULL, &m.nrhs, m.iparm, &msglvl, NULL, NULL, &error);\n\n\tif (error)\n\t{\n\t\tfprintf(stderr, \"\\nERROR during factorization: \");\n\t\tm.print_err(error);\n\t\treturn false;\n\t}\n\n\tm.isFactored = true;\n\n\treturn true;\n#else\n\treturn false;\n#endif\n}\n\nbool PardisoSolver64::BackSolve(double* x, double* b)\n{\n#ifdef PARDISO\n\t// make sure we have work to do\n\tif (m.pA->Rows() == 0) return true;\n\n\tlong long phase = 33;\n\n\tm.iparm[7] = 1;\t/* Maximum number of iterative refinement steps */\n\n\tlong long error = 0;\n\tlong long msglvl = m.msglvl;\n\tpardiso_64(m.pt, &m.maxfct, &m.mnum, &m.mtype, &phase, &m.n, m.pA->Values(), m.pA->Pointers64(), m.pA->Indices64(),\n\t\t NULL, &m.nrhs, m.iparm, &msglvl, b, x, &error);\n\n\tif (error)\n\t{\n\t\tfprintf(stderr, \"\\nERROR during solution: \");\n\t\tm.print_err(error);\n\t\texit(3);\n\t}\n\n\t// update stats\n\tUpdateStats(1);\n\n\treturn true;\n#else\n\treturn false;\n#endif\n}\n\nvoid PardisoSolver64::Destroy()\n{\n#ifdef PARDISO\n\tif (m.pA && m.isFactored)\n\t{\n\t\tlong long phase = -1;\n\t\tlong long error = 0;\n\t\tlong long msglvl = m.msglvl;\n\t\tpardiso_64(m.pt, &m.maxfct, &m.mnum, &m.mtype, &phase, &m.n, NULL, m.pA->Pointers64(), m.pA->Indices64(),\n\t\t\tNULL, &m.nrhs, m.iparm, &msglvl, NULL, NULL, &error);\n\t}\n\tm.isFactored = false;\n#endif\n}\n"
  },
  {
    "path": "NumCore/PardisoSolver64.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/LinearSolver.h>\n\nclass PardisoSolver64 : public LinearSolver\n{\n\tclass Imp;\n\npublic:\n\tPardisoSolver64(FEModel* fem);\n\t~PardisoSolver64();\n\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\n\tvoid UseIterativeFactorization(bool b);\n\nprotected:\n\tImp& m;\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/RCICGSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"RCICGSolver.h\"\n#include \"IncompleteCholesky.h\"\n\n//-----------------------------------------------------------------------------\n// We must undef PARDISO since it is defined as a function in mkl_solver.h\n#ifdef MKL_ISS\n#ifdef PARDISO\n#undef PARDISO\n#endif\n#include \"mkl_rci.h\"\n#include \"mkl_blas.h\"\n#include \"mkl_spblas.h\"\n#endif // MKL_ISS\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(RCICGSolver, IterativeLinearSolver)\n\tADD_PARAMETER(m_print_level, \"print_level\");\n\tADD_PARAMETER(m_tol, \"tol\");\n\tADD_PARAMETER(m_maxiter, \"max_iter\");\n\tADD_PARAMETER(m_fail_max_iters, \"fail_max_iters\");\n\tADD_PROPERTY(m_P, \"pc_left\")->SetFlags(FEProperty::Optional);\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nRCICGSolver::RCICGSolver(FEModel* fem) : IterativeLinearSolver(fem), m_pA(0), m_P(0)\n{\n\tm_maxiter = 0;\n\tm_tol = 1e-5;\n\tm_print_level = 0;\n\tm_fail_max_iters = true;\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* RCICGSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n#ifdef MKL_ISS\n\tif (ntype != REAL_SYMMETRIC) return 0;\n\tm_pA = new CompactSymmMatrix(1);\n\tif (m_P) m_P->SetSparseMatrix(m_pA);\n\treturn m_pA;\n#else\n\treturn 0;\n#endif\n}\n\n//-----------------------------------------------------------------------------\nbool RCICGSolver::SetSparseMatrix(SparseMatrix* A)\n{\n\tm_pA = A;\n\treturn (m_pA != 0);\n}\n\n//-----------------------------------------------------------------------------\nvoid RCICGSolver::SetLeftPreconditioner(LinearSolver* P)\n{\n\tm_P = dynamic_cast<Preconditioner*>(P);\n}\n\n//-----------------------------------------------------------------------------\nLinearSolver* RCICGSolver::GetLeftPreconditioner()\n{\n\treturn m_P;\n}\n\n//-----------------------------------------------------------------------------\nbool RCICGSolver::HasPreconditioner() const\n{\n\treturn (m_P != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool RCICGSolver::PreProcess()\n{\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool RCICGSolver::Factor()\n{\n\tif (m_pA == 0) return false;\n\treturn (m_P ? m_P->Factor() : true);\n}\n\n//-----------------------------------------------------------------------------\nbool RCICGSolver::BackSolve(double* x, double* b)\n{\n#ifdef MKL_ISS\n\t// make sure we have a matrix\n\tif (m_pA == 0) return false;\n\n\t// get number of equations\n\tMKL_INT n = m_pA->Rows();\n\n\t// zero solution vector\n\tfor (int i=0; i<n; ++i) x[i] = 0.0;\n\n\t// get pointers to solution and RHS vector\n\tdouble* px = &x[0];\n\tdouble* pb = &b[0];\n\n\t// output parameters\n\tMKL_INT rci_request;\n\tMKL_INT ipar[128];\n\tdouble dpar[128];\n\tvector<double> tmp(n*4);\n\tdouble* ptmp = &tmp[0];\n\n\t// initialize parameters\n\tdcg_init(&n, px, pb, &rci_request, ipar, dpar, ptmp);\n\tif (rci_request != 0) return false;\n\n\t// set the desired parameters:\n\tif (m_maxiter > 0) ipar[4] = m_maxiter;\t// max nr of iterations\n\tipar[8] = 1;\t\t\t// do residual stopping test\n\tipar[9] = 0;\t\t\t// do not request for the user defined stopping test\n\tipar[10] = (m_P ? 1 : 0);\t\t// preconditioning\n\tdpar[0] = m_tol;\t\t// set the relative tolerance\n\n\t// check the consistency of the newly set parameters\n\tdcg_check(&n, px, pb, &rci_request, ipar, dpar, ptmp);\n\tif (rci_request != 0) return false;\n\n\t// loop until converged\n\tbool bsuccess = false;\n\tbool bdone = false;\n\tdo\n\t{\n\t\t// compute the solution by RCI\n\t\tdcg(&n, px, pb, &rci_request, ipar, dpar, ptmp);\n\n\t\tswitch (rci_request)\n\t\t{\n\t\tcase 0: // solution converged! \n\t\t\tbsuccess = true;\n\t\t\tbdone = true;\n\t\t\tbreak;\n\t\tcase 1: // compute vector A*tmp[0] and store in tmp[n]\n\t\t\t{\n\t\t\t\tbool bret = m_pA->mult_vector(ptmp, ptmp+n);\n\t\t\t\tif (bret == false)\n\t\t\t\t{\n\t\t\t\t\tbsuccess = false;\n\t\t\t\t\tbdone = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (m_print_level == 1)\n\t\t\t\t{\n\t\t\t\t\tfprintf(stderr, \"%3d = %lg (%lg), %lg (%lg)\\n\", ipar[3], dpar[4], dpar[3], dpar[6], dpar[7]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\t{\n\t\t\t\tassert(m_P);\n\t\t\t\tm_P->mult_vector(ptmp + n*2, ptmp + n*3);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbsuccess = false;\n\t\t\tbdone = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\twhile (!bdone);\n\n\t// get convergence information\n\tint niter;\n\tdcg_get(&n, px, pb, &rci_request, ipar, dpar, ptmp, &niter);\n\n\tif (m_print_level > 0)\n\t{\n\t\tfprintf(stderr, \"%3d = %lg (%lg), %lg (%lg)\\n\", ipar[3], dpar[4], dpar[3], dpar[6], dpar[7]);\n\t}\n\n\tUpdateStats(niter);\n\n\t// release internal MKL buffers\n//\tMKL_Free_Buffers();\n\n\treturn (m_fail_max_iters ? bsuccess : true);\n#else\n\treturn false;\n#endif // MKL_ISS\n}\n\n//-----------------------------------------------------------------------------\nvoid RCICGSolver::Destroy()\n{\n}\n"
  },
  {
    "path": "NumCore/RCICGSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/Preconditioner.h>\n#include <FECore/CompactSymmMatrix.h>\n\n// This class implements an interface to the RCI CG iterative solver from the MKL math library.\nclass RCICGSolver : public IterativeLinearSolver\n{\npublic:\n\tRCICGSolver(FEModel* fem);\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* b) override;\n\tvoid Destroy() override;\n\npublic:\n\tbool HasPreconditioner() const override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\n\tvoid SetLeftPreconditioner(LinearSolver* P) override;\n\tLinearSolver* GetLeftPreconditioner() override;\n\n\tvoid SetMaxIterations(int n) { m_maxiter = n; }\n\tvoid SetTolerance(double tol) { m_tol = tol; }\n\tvoid SetPrintLevel(int n) override { m_print_level = n; }\n\nprotected:\n\tSparseMatrix*\t\tm_pA;\n\tPreconditioner*\t\tm_P;\n\n\tint\t\tm_maxiter;\t\t// max nr of iterations\n\tdouble\tm_tol;\t\t\t// residual relative tolerance\n\tint\t\tm_print_level;\t// output level\n\tbool\tm_fail_max_iters;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/SchurSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n#include \"SchurSolver.h\"\n#include \"ILU0_Preconditioner.h\"\n#include <FECore/SchurComplement.h>\n#include \"IncompleteCholesky.h\"\n#include \"HypreGMRESsolver.h\"\n#include \"RCICGSolver.h\"\n#include <FECore/FEModel.h>\n#include <FECore/FESolidDomain.h>\n#include <FECore/FEGlobalMatrix.h>\n#include \"PardisoSolver.h\"\n#include \"BoomerAMGSolver.h\"\n#include \"FGMRESSolver.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nbool BuildDiagonalMassMatrix(FEModel* fem, BlockMatrix* K, CompactSymmMatrix* M, double scale)\n{\n\tFEMesh& mesh = fem->GetMesh();\n\n\t// get number of equations\n\tint N0 = K->Block(0, 0).Rows();\n\tint N = K->Block(1, 1).Rows();\n\n\t// build the global matrix\n\tSparseMatrixProfile MP(N, N);\n\tMP.CreateDiagonal();\n\tM->Create(MP);\n\tM->Zero();\n\n\t// build the mass matrix\n\tmatrix me;\n\tdouble density = 1.0;\n\tint n = 0;\n\tfor (int i = 0; i < mesh.Domains(); ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(mesh.Domain(i));\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j, ++n)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(j);\n\n\t\t\t// Get the current element's data\n\t\t\tconst int nint = el.GaussPoints();\n\t\t\tconst int neln = el.Nodes();\n\t\t\tconst int ndof = neln;\n\n\t\t\t// weights at gauss points\n\t\t\tconst double *gw = el.GaussWeights();\n\n\t\t\tmatrix me(ndof, ndof);\n\t\t\tme.zero();\n\n\t\t\t// calculate element stiffness matrix\n\t\t\tdouble Me = 0.0;\n\t\t\tfor (int n = 0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tdouble Dn = density;\n\n\t\t\t\t// Jacobian\n\t\t\t\tdouble Jw = dom.detJ0(el, n)*gw[n];\n\n\t\t\t\tMe += Dn*Jw;\n\t\t\t}\n\n\t\t\tint lm = el.m_lm - N0;\n\t\t\tM->set(lm, lm, Me);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool BuildMassMatrix(FEModel* fem, BlockMatrix* K, CompactSymmMatrix* M, double scale)\n{\n\tFEMesh& mesh = fem->GetMesh();\n\n\t// get number of equations\n\tint N0 = K->Block(0, 0).Rows();\n\tint N = K->Block(1, 1).Rows();\n\n\t// this is the degree of freedom in the LM arrays that we need\n\t// TODO: This is hard coded for fluid problems\n\tconst int edofs = 4; // nr of degrees per element node\n\tconst int dof = 3;\t// degree of freedom we need\n\n\t\t\t\t\t\t// build the global matrix\n\tvector<vector<int> > LM;\n\tvector<int> lm, lme;\n\tSparseMatrixProfile MP(N, N);\n\tMP.CreateDiagonal();\n\tint ND = mesh.Domains();\n\tfor (int i = 0; i < ND; ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(mesh.Domain(i));\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(j);\n\t\t\tint neln = el.Nodes();\n\t\t\t// get the equation numbers\n\t\t\tdom.UnpackLM(el, lme);\n\n\t\t\t// we don't want equation numbers below N0\n\t\t\tlm.resize(neln);\n\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t{\n\t\t\t\tlm[i] = lme[i*edofs + dof];\n\t\t\t\tif (lm[i] >= 0) lm[i] -= N0;\n\t\t\t\telse if (lm[i] < -1) lm[i] = -lm[i] - 2 - N0;\n\t\t\t}\n\n\t\t\tLM.push_back(lm);\n\t\t\tMP.UpdateProfile(LM, 1);\n\t\t\tLM.clear();\n\t\t}\n\t}\n\tM->Create(MP);\n\tM->Zero();\n\n\t// build the mass matrix\n\tmatrix me;\n\tdouble density = 1.0;\n\tint n = 0;\n\tfor (int i = 0; i < ND; ++i)\n\t{\n\t\tFESolidDomain& dom = dynamic_cast<FESolidDomain&>(mesh.Domain(i));\n\t\tint NE = dom.Elements();\n\t\tfor (int j = 0; j < NE; ++j, ++n)\n\t\t{\n\t\t\tFESolidElement& el = dom.Element(j);\n\n\t\t\t// Get the current element's data\n\t\t\tconst int nint = el.GaussPoints();\n\t\t\tconst int neln = el.Nodes();\n\t\t\tconst int ndof = neln;\n\n\t\t\t// weights at gauss points\n\t\t\tconst double *gw = el.GaussWeights();\n\n\t\t\tmatrix me(ndof, ndof);\n\t\t\tme.zero();\n\n\t\t\t// calculate element stiffness matrix\n\t\t\tfor (int n = 0; n<nint; ++n)\n\t\t\t{\n\t\t\t\tFEMaterialPoint& mp = *el.GetMaterialPoint(n);\n\t\t\t\tdouble Dn = density;\n\n\t\t\t\t// shape functions\n\t\t\t\tdouble* H = el.H(n);\n\n\t\t\t\t// Jacobian\n\t\t\t\tdouble J0 = dom.detJ0(el, n)*gw[n];\n\n\t\t\t\tfor (int i = 0; i<neln; ++i)\n\t\t\t\t\tfor (int j = 0; j<neln; ++j)\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble mab = Dn*H[i] * H[j] * J0;\n\t\t\t\t\t\tme[i][j] += mab*scale;\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get the equation numbers\n\t\t\tdom.UnpackLM(el, lme);\n\n\t\t\t// we don't want equation numbers below N0\n\t\t\tlm.resize(neln);\n\t\t\tfor (int i = 0; i < neln; ++i)\n\t\t\t{\n\t\t\t\tlm[i] = lme[i*edofs + dof];\n\t\t\t\tif (lm[i] >= 0) lm[i] -= N0;\n\t\t\t\telse if (lm[i] < -1) lm[i] = -lm[i] - 2 - N0;\n\t\t\t}\n\n\t\t\tM->Assemble(me, lm);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(SchurSolver, LinearSolver)\n\tADD_PARAMETER(m_printLevel   , \"print_level\");\n\tADD_PARAMETER(m_schurBlock   , \"schur_block\");\n\tADD_PARAMETER(m_doJacobi     , \"do_jacobi\");\n\tADD_PARAMETER(m_bzeroDBlock  , \"zero_D_block\");\n\tADD_PARAMETER(m_nSchurPreC   , \"schur_pc\");\n\n\tADD_PROPERTY(m_Asolver, \"A_solver\");\n\tADD_PROPERTY(m_schurSolver, \"schur_solver\");\n\tADD_PROPERTY(m_SchurAsolver, \"schur_A_solver\");\n\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\n//! constructor\nSchurSolver::SchurSolver(FEModel* fem) : LinearSolver(fem)\n{\n\t// default parameters\n\tm_printLevel = 0;\n\tm_bzeroDBlock = false;\n\tm_schurBlock = 0;\t\t// Use Schur complement S\\A\n\n\t// default solution strategy\n\tm_nSchurPreC = 0;\t\t// no preconditioner\n\tm_iter = 0;\n\tm_doJacobi = false;\n\n\t// initialize pointers\n\tm_pK = nullptr;\n\tm_Asolver = nullptr;\n\tm_PS = nullptr;\n\tm_schurSolver = nullptr;\n\tm_SchurAsolver = nullptr;\n\tm_Acopy = nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! constructor\nSchurSolver::~SchurSolver()\n{\n\tif (m_Acopy) delete m_Acopy;\n}\n\n//-----------------------------------------------------------------------------\n// get the iteration count\nint SchurSolver::GetIterations() const\n{\n\treturn m_iter;\n}\n\n//-----------------------------------------------------------------------------\n// set the print level\nvoid SchurSolver::SetPrintLevel(int n)\n{\n\tm_printLevel = n;\n}\n\n//-----------------------------------------------------------------------------\nvoid SchurSolver::SetSchurPreconditioner(int n)\n{\n\tm_nSchurPreC = n;\n}\n\n//-----------------------------------------------------------------------------\nvoid SchurSolver::ZeroDBlock(bool b)\n{\n\tm_bzeroDBlock = b;\n}\n\n//-----------------------------------------------------------------------------\n// Sets which Schur complement to use\n// n = 0: use S\\A\n// n = 1: use S\\D\nvoid SchurSolver::SetSchurBlock(int n)\n{\n\tassert((n == 0) || (n == 1));\n\tm_schurBlock = n;\n}\n\n//-----------------------------------------------------------------------------\nvoid SchurSolver::DoJacobiPreconditioning(bool b)\n{\n\tm_doJacobi = b;\n}\n\n//-----------------------------------------------------------------------------\n//! Create a sparse matrix\nSparseMatrix* SchurSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\tif (m_part.size() != 2) return 0;\n\tm_pK = new BlockMatrix();\n\tm_pK->Partition(m_part, ntype, 1);\n\n\t// we want the A solver to define the blocks\n\tSparseMatrix* K11 = m_Asolver->CreateSparseMatrix(ntype);\n\tif (K11) \n\t{\n\t\tCompactMatrix* A = dynamic_cast<CompactMatrix*>(K11);\n\t\tif (A == nullptr) { delete K11; delete m_pK; return nullptr; }\n\t\tdelete m_pK->Block(0, 0).pA; m_pK->Block(0, 0).pA = A; \n\t}\n\telse\n\t{\n\t\tdelete m_pK;\n\t\tm_pK = nullptr;\n\t}\n\n\treturn m_pK;\n}\n\n//-----------------------------------------------------------------------------\n//! set the sparse matrix\nbool SchurSolver::SetSparseMatrix(SparseMatrix* A)\n{\n\tm_pK = dynamic_cast<BlockMatrix*>(A);\n\tif (m_pK == 0) return false;\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n// allocate Schur complement solver\nLinearSolver* SchurSolver::BuildSchurPreconditioner(int nopt)\n{\n\tswitch (nopt)\n\t{\n\tcase Schur_PC_NONE:\n\t\t// no preconditioner selected\n\t\treturn nullptr;\n\t\tbreak;\n\tcase Schur_PC_DIAGONAL_MASS:\n\t{\n\t\t// diagonal mass matrix\n\t\tCompactSymmMatrix* M = new CompactSymmMatrix(1);\n\t\tif (BuildDiagonalMassMatrix(GetFEModel(), m_pK, M, 1.0) == false) return nullptr;\n\n\t\tDiagonalPreconditioner* PS = new DiagonalPreconditioner(GetFEModel());\n\t\tif (PS->Create(M) == false) return nullptr;\n\n\t\treturn PS;\n\t}\n\tbreak;\n\tcase Schur_PC_ICHOL_MASS:\n\t{\n\t\t// mass matrix\n\t\tCompactSymmMatrix* M = new CompactSymmMatrix(1);\n\t\tif (BuildMassMatrix(GetFEModel(), m_pK, M, 1.0) == false) return nullptr;\n\n\t\t// We do an incomplete cholesky factorization\n\t\tIncompleteCholesky* PS = new IncompleteCholesky(GetFEModel());\n\t\tif (PS->Create(M) == false) return nullptr;\n\n\t\treturn PS;\n\t}\n\tbreak;\n\tdefault:\n\t\tassert(false);\n\t};\n\n\treturn nullptr;\n}\n\n//-----------------------------------------------------------------------------\n//! Preprocess \nbool SchurSolver::PreProcess()\n{\n\t// make sure we have a matrix\n\tif (m_pK == 0) return false;\n\n\t// Get the blocks\n\tBlockMatrix::BLOCK& A = m_pK->Block(0, 0);\n\tBlockMatrix::BLOCK& B = m_pK->Block(0, 1);\n\tBlockMatrix::BLOCK& C = m_pK->Block(1, 0);\n\tBlockMatrix::BLOCK& D = m_pK->Block(1, 1);\n\n\t// get the number of partitions\n\t// and make sure we have two\n\tint NP = m_pK->Partitions();\n\tif (NP != 2) return false;\n\n\t// check the A solver\n\tif (m_Asolver == nullptr) return false;\n\tm_Asolver->SetFEModel(GetFEModel());\n\n\t// check the schur solver\n\tif (m_schurSolver == nullptr) return false;\n\tm_schurSolver->SetFEModel(GetFEModel());\n\n\t// build solver for A block in Schur solver\n\tif (m_SchurAsolver == nullptr) m_SchurAsolver = m_Asolver;\n\telse m_SchurAsolver->SetFEModel(GetFEModel());\n\n\tif (m_schurBlock == 0)\n\t{\n\t\t// Use the Schur complement of A\n\t\tm_Asolver->SetSparseMatrix(A.pA);\n\t\tif (m_SchurAsolver != m_Asolver)\n\t\t{\n\t\t\tSparseMatrix* AforSchur = A.pA;\n\t\t\tm_SchurAsolver->SetSparseMatrix(AforSchur);\n\t\t}\n\t\tSchurComplementA* S_A = new SchurComplementA(m_SchurAsolver, B.pA, C.pA, (m_bzeroDBlock ? nullptr : D.pA));\n\t\tif (m_schurSolver->SetSparseMatrix(S_A) == false) { delete S_A;  return false; }\n\t}\n\telse\n\t{\n\t\t// Use the Schur complement of D\n\t\tm_Asolver->SetSparseMatrix(D.pA);\n\t\tif (m_SchurAsolver != m_Asolver)\n\t\t{\n\t\t\tSparseMatrix* DforSchur = D.pA;\n\t\t\tm_SchurAsolver->SetSparseMatrix(DforSchur);\n\t\t}\n\t\tSchurComplementD* S_D = new SchurComplementD(A.pA, B.pA, C.pA, m_SchurAsolver);\n\t\tif (m_schurSolver->SetSparseMatrix(S_D) == false) { delete S_D;  return false; }\n\t}\n\n\tif (m_SchurAsolver != m_Asolver)\n\t{\n\t\tif (m_SchurAsolver->PreProcess() == false) return false;\n\t}\n\n\tif (m_Asolver->PreProcess() == false) return false;\n\n\t// build a preconditioner for the schur complement solver\n\tm_PS = BuildSchurPreconditioner(m_nSchurPreC);\n\tif (m_PS) m_schurSolver->SetLeftPreconditioner(m_PS);\n\n\tif (m_schurSolver->PreProcess() == false) return false;\n\n\t// reset iteration counter\n\tm_iter = 0;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Factor matrix\nbool SchurSolver::Factor()\n{\n\t// Get the blocks\n\tBlockMatrix::BLOCK& A = m_pK->Block(0, 0);\n\tBlockMatrix::BLOCK& B = m_pK->Block(0, 1);\n\tBlockMatrix::BLOCK& C = m_pK->Block(1, 0);\n\tBlockMatrix::BLOCK& D = m_pK->Block(1, 1);\n\n\t// Get the block matrices\n\tCRSSparseMatrix* MA = dynamic_cast<CRSSparseMatrix*>(A.pA);\n\tCRSSparseMatrix* MB = dynamic_cast<CRSSparseMatrix*>(B.pA);\n\tCRSSparseMatrix* MC = dynamic_cast<CRSSparseMatrix*>(C.pA);\n\tCRSSparseMatrix* MD = dynamic_cast<CRSSparseMatrix*>(D.pA);\n\n\t// get the number of partitions\n\t// and make sure we have two\n\tint NP = m_pK->Partitions();\n\tif (NP != 2) return false;\n\n\t// get the partition sizes\n\tint n0 = m_pK->PartitionEquations(0);\n\tint n1 = m_pK->PartitionEquations(1);\n\n\t// calculate the diagonal\n\tm_Wu.assign(n0, 1.0);\n\tm_Wp.assign(n1, 1.0);\n\n\tif (m_doJacobi)\n\t{\n\t\tfor (int i = 0; i < n0; ++i)\n\t\t{\n\t\t\tdouble dii = fabs(MA->diag(i));\n//\t\t\tif (dii < 0) return false;\n\t\t\tif (dii != 0.0) m_Wu[i] = 1.0 / sqrt(dii);\n\t\t}\n\t\tif (MD)\n\t\t{\n\t\t\tfor (int i = 0; i < n1; ++i)\n\t\t\t{\n\t\t\t\tdouble dii = fabs(MD->diag(i));\n//\t\t\t\tif (dii < 0) return false;\n\t\t\t\tif (dii != 0.0) m_Wp[i] = 1.0 / sqrt(dii);\n\t\t\t}\n\t\t}\n\n\t\t// scale the matrices\n\t\tMA->scale(m_Wu, m_Wu);\n\t\tMB->scale(m_Wu, m_Wp);\n\t\tMC->scale(m_Wp, m_Wu);\n\t\tif (MD) MD->scale(m_Wp, m_Wp);\n\t}\n\n\t// See if we need to copy the A (or D) block\n\tif (m_Acopy)\n\t{\n\t\tif (m_schurBlock == 0) m_Acopy->CopyValues(A.pA);\n\t\telse m_Acopy->CopyValues(D.pA);\n\t}\n\n\t// factor the A block solver\n\tif (m_Asolver->Factor() == false) return false;\n\n\t// factor the A block solver of the Schur complement (if necessary)\n\tif (m_SchurAsolver && (m_SchurAsolver != m_Asolver))\n\t{\n\t\tif (m_SchurAsolver->Factor() == false) return false;\n\t}\n\n\t// factor the schur complement solver\n\tif (m_schurSolver->Factor() == false) return false;\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Backsolve the linear system\nbool SchurSolver::BackSolve(double* x, double* b)\n{\n\t// get the partition sizes\n\tint n0 = m_pK->PartitionEquations(0);\n\tint n1 = m_pK->PartitionEquations(1);\n\n\t// Get the blocks\n\tBlockMatrix::BLOCK& A = m_pK->Block(0, 0);\n\tBlockMatrix::BLOCK& B = m_pK->Block(0, 1);\n\tBlockMatrix::BLOCK& C = m_pK->Block(1, 0);\n\tBlockMatrix::BLOCK& D = m_pK->Block(1, 1);\n\n\t// split right hand side in two\n\tvector<double> F(n0), G(n1);\n\tfor (int i = 0; i<n0; ++i) F[i] = m_Wu[i]*b[i];\n\tfor (int i = 0; i<n1; ++i) G[i] = m_Wp[i]*b[i + n0];\n\n\t// solution vectors\n\tvector<double> u(n0, 0.0);\n\tvector<double> v(n1, 0.0);\n\n\tif (m_schurBlock == 0)\n\t{\n\t\t// step 1: solve Ay = F\n\t\tvector<double> y(n0);\n\t\tif (m_printLevel != 0) feLog(\"----------------------\\nstep 1:\\n\");\n\t\tif (m_Asolver->BackSolve(y, F) == false) return false;\n\n\t\t// step 2: Solve Sv = H, where H = Cy - G\n\t\tif (m_printLevel != 0) feLog(\"step 2:\\n\");\n\t\tvector<double> H(n1);\n\t\tC.vmult(y, H);\n\t\tH -= G;\n\n\t\tif (m_schurSolver->BackSolve(v, H) == false) return false;\n\n\t\t// step 3: solve Au = L , where L = F - Bv\n\t\tif (m_printLevel != 0) feLog(\"step 3:\\n\");\n\t\tvector<double> tmp(n0);\n\t\tB.vmult(v, tmp);\n\t\tvector<double> L = F - tmp;\n\t\tif (m_Asolver->BackSolve(u, L) == false) return false;\n\t}\n\telse\n\t{\n\t\t// step 1: solve Dy = G\n\t\tvector<double> y(n1);\n\t\tif (m_printLevel != 0) feLog(\"----------------------\\nstep 1:\\n\");\n\t\tif (m_Asolver->BackSolve(y, G) == false) return false;\n\n\t\t// step 2: Solve Su = H, where H = By - F\n\t\tif (m_printLevel != 0) feLog(\"step 2:\\n\");\n\t\tvector<double> H(n0);\n\t\tB.vmult(y, H);\n\t\tH -= F;\n\n\t\tif (m_schurSolver->BackSolve(u, H) == false) return false;\n\n\t\t// step 3: solve Dv = L , where L = G - Cu\n\t\tif (m_printLevel != 0) feLog(\"step 3:\\n\");\n\t\tvector<double> tmp(n1);\n\t\tC.vmult(u, tmp);\n\t\tvector<double> L = G - tmp;\n\t\tif (m_Asolver->BackSolve(v, L) == false) return false;\n\t}\n\n\t// put it back together\n\tfor (int i = 0; i<n0; ++i) x[i     ] = m_Wu[i]*u[i];\n\tfor (int i = 0; i<n1; ++i) x[i + n0] = m_Wp[i]*v[i];\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\n//! Clean up\nvoid SchurSolver::Destroy()\n{\n\tif (m_SchurAsolver != m_Asolver) m_SchurAsolver->Destroy();\n\tif (m_Asolver) m_Asolver->Destroy();\n\tif (m_schurSolver) m_schurSolver->Destroy();\n\n\tm_Asolver = nullptr;\n\tm_SchurAsolver = nullptr;\n\tm_schurSolver = nullptr;\n}\n"
  },
  {
    "path": "NumCore/SchurSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n#include <FECore/LinearSolver.h>\n#include \"BlockMatrix.h\"\n\n//-----------------------------------------------------------------------------\n// This class implements a solution strategy for solving a linear system that is structured\n// as a 2x2 block matrix. It makes no assumption on the symmetry of the global matrix or its blocks.\nclass SchurSolver : public LinearSolver\n{\npublic:\n\t// options for Schur complement preconditioner\n\tenum Schur_PC {\n\t\tSchur_PC_NONE,\n\t\tSchur_PC_DIAGONAL_MASS,\n\t\tSchur_PC_ICHOL_MASS\n\t};\n\npublic:\n\t//! constructor\n\tSchurSolver(FEModel* fem);\n\n\t//! destructor\n\t~SchurSolver();\n\npublic:\n\t//! Preprocess \n\tbool PreProcess() override;\n\n\t//! Factor matrix\n\tbool Factor() override;\n\n\t//! Backsolve the linear system\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Clean up\n\tvoid Destroy() override;\n\n\t//! Create a sparse matrix\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\npublic:\n\t// get the iteration count\n\tint GetIterations() const;\n\n\t// set the print level\n\tvoid SetPrintLevel(int n) override;\n\n\tvoid SetSchurPreconditioner(int n);\n\n\tvoid ZeroDBlock(bool b);\n\n\tvoid DoJacobiPreconditioning(bool b);\n\n\tvoid SetSchurBlock(int n);\n\nprotected:\n\t// allocate the preconditioner for the Schur complement solver\n\tLinearSolver* BuildSchurPreconditioner(int nopt);\n\nprivate:\n\tint\t\tm_printLevel;\t//!< set print level\n\tint\t\tm_nSchurPreC;\t//!< Schur preconditioner : 0 = none\n\tbool\tm_bzeroDBlock;\n\tbool\tm_doJacobi;\t\t//!< apply Jacobi preconditioner to global system\n\tint\t\tm_schurBlock;\t//!< which diagonal block to use for Schur solver? (0 = S\\A (default), 1 = S\\D)\n\nprivate:\n\tBlockMatrix*\tm_pK;\t\t\t\t\t//!< block matrix\n\n\tLinearSolver*\tm_Asolver;\t\t\t\t//!< solver for solving A block\n\tLinearSolver*\tm_SchurAsolver;\t\t\t//!< solver for solving A block inside Schur solver\n\tIterativeLinearSolver*\tm_schurSolver;\t//!< solver of Schur complement\n\tLinearSolver*\tm_PS;\t\t\t\t\t//!< preconditioner for the Schur system\n\n\tCRSSparseMatrix*\tm_Acopy;\t//!< A copy of the A-block, needed for some solution strategies \n\n\tvector<double>\tm_Wu, m_Wp;\t\t//!< inverse of diagonals of global system (used by Jacobi preconditioner)\n\n\tint\t\tm_iter;\t\t\t//!< nr of iterations of last solve\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/StrategySolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#include \"stdafx.h\"\n#include \"StrategySolver.h\"\n#include \"MatrixTools.h\"\n#include <FECore/log.h>\n\n//-----------------------------------------------------------------------------\nBEGIN_FECORE_CLASS(StrategySolver, LinearSolver)\n\tADD_PARAMETER(m_strategy, \"strategy\");\n\tADD_PARAMETER(m_ctol, \"max_condition_number\");\n\tADD_PARAMETER(m_print_cn, \"print_condition_number\");\n\tADD_PROPERTY(m_solver1, \"solver1\");\n\tADD_PROPERTY(m_solver2, \"solver2\");\nEND_FECORE_CLASS();\n\nStrategySolver::StrategySolver(FEModel* fem) : LinearSolver(fem)\n{\n\tm_strategy = DONT_PERSIST;\n\tm_print_cn = false;\n\tm_ctol = 0;\n\n\tm_solver1 = nullptr;\n\tm_solver2 = nullptr;\n\tm_activeSolver = nullptr;\n\n\tm_pA = nullptr;\n}\n\nStrategySolver::~StrategySolver()\n{\n\tDestroy();\n\tdelete m_solver1;\n\tdelete m_solver2;\n}\n\n//! Preprocess \nbool StrategySolver::PreProcess()\n{\n\tif (m_solver1->PreProcess() == false) return false;\n\tif (m_solver2->PreProcess() == false) return false;\n\treturn true;\n}\n\n//! Factor matrix\nbool StrategySolver::Factor()\n{\n\tif ((m_activeSolver == nullptr) || (m_strategy != PERSIST_FOREVER)) m_activeSolver = m_solver1;\n\n\tif (m_pA && (m_print_cn || ((m_ctol > 0.0) && (m_activeSolver == m_solver1))))\n\t{\n\t\tdouble c = NumCore::estimateConditionNumber(m_pA);\n\n\t\tif (m_print_cn) feLog(\"\\nEst. condition number: %lg\\n\\n\", c);\n\t\tif ((m_ctol > 0.0) && (c >= m_ctol))\n\t\t{\n\t\t\tfeLogWarning(\"Condition number too large: %lg\\nSwitching to secondary solver.\", c);\n\t\t\tm_activeSolver = m_solver2;\n\t\t}\n\t}\n\n\treturn m_activeSolver->Factor();\n}\n\n//! Backsolve the linear system\nbool StrategySolver::BackSolve(double* x, double* b)\n{\n\tassert(m_activeSolver);\n\tbool success = m_activeSolver->BackSolve(x, b);\n\tif ((success == false) && (m_activeSolver != m_solver2))\n\t{\n\t\tfeLogError(\"Primary linear solver failed. Trying secondary linear solver ...\");\n\t\tm_activeSolver = m_solver2;\n\t\tm_solver2->Factor();\n\t\tsuccess = m_solver2->BackSolve(x, b);\n\n\t\tif (m_strategy == DONT_PERSIST)\n\t\t{\n\t\t\tm_solver2->Destroy();\n\t\t\tm_activeSolver = m_solver1;\n\t\t}\n\t}\n\treturn success;\n}\n\n//! Clean up\nvoid StrategySolver::Destroy()\n{\n\tif (m_solver1) m_solver1->Destroy();\n\tif (m_solver2) m_solver2->Destroy();\n\tif (m_strategy != PERSIST_FOREVER) m_activeSolver = nullptr;\n}\n\n//! Create a sparse matrix\nSparseMatrix* StrategySolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// let solver1 create the sparse system\n\tif (m_solver1 == nullptr) return nullptr;\n\tSparseMatrix* A = m_solver1->CreateSparseMatrix(ntype);\n\tif (A == nullptr) return nullptr;\n\n\t// see if this format works for the second solver\n\tif ((m_solver2 == nullptr) || (m_solver2->SetSparseMatrix(A) == false)) {\n\t\tdelete A;\n\t\treturn nullptr;\n\t}\n\n\tm_pA = A;\n\n\treturn A;\n}\n\n//! set the sparse matrix\nbool StrategySolver::SetSparseMatrix(SparseMatrix* A)\n{\n\tif ((m_solver1 == nullptr) || (m_solver1->SetSparseMatrix(A) == false)) return false;\n\tif ((m_solver2 == nullptr) || (m_solver2->SetSparseMatrix(A) == false)) return false;\n\treturn true;\n}\n"
  },
  {
    "path": "NumCore/StrategySolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n#pragma once\n#include <FECore/LinearSolver.h>\n\n// This class implements a linear solver that can switch between linear solvers\nclass StrategySolver : public LinearSolver\n{\n\t// the persist strategy determines when to switch back from solver2 to solver1\n\t// after solver1 fails. \n\t// DONT_PERSIST (0) = switch back immediately.\n\t// PERSIST          = switch back on the next Destroy()\n\t// PERSIST_FOREVER  = switch to solver2 permanently\n\tenum PersistStrategy\n\t{\n\t\tDONT_PERSIST,\n\t\tPERSIST,\n\t\tPERSIST_FOREVER\n\t};\n\npublic:\n\tStrategySolver(FEModel* fem);\n\n\t~StrategySolver();\n\npublic:\n\t//! Preprocess \n\tbool PreProcess() override;\n\n\t//! Factor matrix\n\tbool Factor() override;\n\n\t//! Backsolve the linear system\n\tbool BackSolve(double* x, double* b) override;\n\n\t//! Clean up\n\tvoid Destroy() override;\n\n\t//! Create a sparse matrix\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\n\t//! set the sparse matrix\n\tbool SetSparseMatrix(SparseMatrix* A) override;\n\nprivate:\n\tint\t\t\t\tm_strategy;\n\tdouble\t\t\tm_ctol;\n\n\tbool\t\t\tm_print_cn;\t// print the condition number\n\tLinearSolver*\tm_solver1;\t// the primary solver\n\tLinearSolver*\tm_solver2;\t// the seoncdary solver\n\tLinearSolver*\tm_activeSolver;\n\n\tSparseMatrix*\tm_pA;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/SuperLU_MT.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"SuperLU_MT.h\"\n#include \"MatrixTools.h\"\n#include <FECore/log.h>\n#ifdef HAVE_SUPERLU_MT\n#define __OPENMP\n#undef MAX\n#undef MIN\n#include <slu_mt_ddefs.h>\n#endif // HAVE_SUPERLU_MT\n\n#ifdef HAVE_SUPERLU_MT\n\n//-----------------------------------------------------------------------------\nclass SuperLU_MT_Solver::Impl\n{\npublic:\n\tSuperMatrix A, L, U;\n\tSuperMatrix B, X;\n\tSuperMatrix AC;\n\tvector<int> perm_c;\n\tvector<int> perm_r;\n\tbool isFactored = false;\n\tsuperlumt_options_t options;\n\tGstat_t  Gstat;\n\tint\tnprocs = 1;\n\t/*\n\t * Get column permutation vector perm_c[], according to permc_spec:\n\t *   permc_spec = 0: natural ordering\n\t *   permc_spec = 1: minimum degree ordering on structure of A'*A\n\t *   permc_spec = 2: minimum degree ordering on structure of A'+A\n\t *   permc_spec = 3: approximate minimum degree for unsymmetric matrices\n\t */\n\tint permc_spec = 3;\n};\n\nBEGIN_FECORE_CLASS(SuperLU_MT_Solver, LinearSolver)\n\tADD_PARAMETER(m->nprocs, \"nprocs\");\n\tADD_PARAMETER(m->permc_spec, \"permc_spec\");\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nSuperLU_MT_Solver::SuperLU_MT_Solver(FEModel* fem) : LinearSolver(fem), m_pA(nullptr)\n{\n\tm = new SuperLU_MT_Solver::Impl;\n}\n\n//-----------------------------------------------------------------------------\nSuperLU_MT_Solver::~SuperLU_MT_Solver()\n{\n\tDestroy();\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* SuperLU_MT_Solver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// allocate the correct matrix format depending on matrix symmetry type\n\tswitch (ntype)\n\t{\n\tcase REAL_SYMMETRIC: \n\tcase REAL_UNSYMMETRIC: \n\tcase REAL_SYMM_STRUCTURE: \n\t\tm_pA = new CCSSparseMatrix;\n\t\tbreak;\n\tdefault:\n\t\tassert(false);\n\t\tm_pA = nullptr;\n\t}\n\n\treturn m_pA;\n}\n\n//-----------------------------------------------------------------------------\nbool SuperLU_MT_Solver::SetSparseMatrix(SparseMatrix* pA)\n{\n\treturn (m_pA != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool SuperLU_MT_Solver::PreProcess()\n{\n\tif (m_pA == nullptr) return false;\n\n\tint n = m_pA->Rows();\n\tint nnz = m_pA->NonZeroes();\n\tdCreate_CompCol_Matrix(&m->A, n, n, nnz, m_pA->Values(), m_pA->Indices(), m_pA->Pointers(), SLU_NC, SLU_D, SLU_GE);\n\n\tm->perm_c.resize(n);\n\tm->perm_r.resize(n);\n\n\treturn LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool SuperLU_MT_Solver::Factor()\n{\n\t// make sure we have work to do\n\tif (m_pA->Rows() == 0) return true;\n\n\tif (m->isFactored)\n\t{\n\t\tDestroy_SuperNode_SCP(&m->L);\n\t\tDestroy_CompCol_NCP(&m->U);\n\t\tm->isFactored = false;\n\t}\n\n\tint n = m_pA->Rows();\n\tint nnz = m_pA->NonZeroes();\n\n\tint panel_size = sp_ienv(1);\n\tint relax = sp_ienv(2);\n\tStatAlloc(n, m->nprocs, panel_size, relax, &m->Gstat);\n\tStatInit(n, m->nprocs, &m->Gstat);\n\n\tget_perm_c(m->permc_spec, &m->A, m->perm_c.data());\n\n\t/* ------------------------------------------------------------\n\t   Initialize the option structure pdgstrf_options using the\n\t   user-input parameters;\n\t   Apply perm_c to the columns of original A to form AC.\n\t   ------------------------------------------------------------*/\n\tfact_t fact = fact_t::DOFACT;\n\ttrans_t trans = trans_t::NOTRANS;\n\tyes_no_t refact = yes_no_t::NO;\n\tdouble diag_pivot_thresh = 1.0;\n\tyes_no_t usepr = NO;\n\tdouble drop_tol = 0.0;\n\tpdgstrf_init(m->nprocs, fact, trans, refact, panel_size, relax,\n\t\tdiag_pivot_thresh, usepr, drop_tol, m->perm_c.data(), m->perm_r.data(),\n\t\tNULL, 0, &m->A, &m->AC, &m->options, &m->Gstat);\n\n\t/* ------------------------------------------------------------\n\t   Compute the LU factorization of A.\n\t   The following routine will create nprocs threads.\n\t   ------------------------------------------------------------*/\n\tint info = 0;\n\tpdgstrf(&m->options, &m->AC, m->perm_r.data(), &m->L, &m->U, &m->Gstat, &info);\n\n\tm->isFactored = true;\n\n\treturn (info == 0);\n}\n\n//-----------------------------------------------------------------------------\nbool SuperLU_MT_Solver::BackSolve(double* x, double* b)\n{\n\t// make sure we have work to do\n\tif (m_pA->Rows() == 0) return true;\n\n\tint n = m_pA->Rows();\n\tmemcpy(x, b, n * sizeof(double));\n\tdCreate_Dense_Matrix(&m->B, n, 1, x, n, SLU_DN, SLU_D, SLU_GE);\n\n\t/* ------------------------------------------------------------\n\t\tSolve the system A*X=B, overwriting B with X.\n\t\t------------------------------------------------------------*/\n\tint info = 0;\n\tdgstrs(trans_t::NOTRANS, &m->L, &m->U, m->perm_r.data(), m->perm_c.data(), &m->B, &m->Gstat, &info);\n\n\t// update stats\n\tUpdateStats(1);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid SuperLU_MT_Solver::Destroy()\n{\n//\tpdgstrf_finalize(&m->options, &m->AC);\n//\tStatFree(&m->Gstat);\n\tif (m->isFactored)\n\t{\n\t\tDestroy_SuperNode_SCP(&m->L);\n\t\tDestroy_CompCol_NCP(&m->U);\n\t\tm->isFactored = false;\n\t}\n}\n#else // HAVE_SUPERLU_MT\nBEGIN_FECORE_CLASS(SuperLU_MT_Solver, LinearSolver)\nEND_FECORE_CLASS();\n\nSuperLU_MT_Solver::SuperLU_MT_Solver(FEModel* fem) : LinearSolver(fem), m_pA(nullptr), m(nullptr) {}\nSuperLU_MT_Solver::~SuperLU_MT_Solver() {}\nbool SuperLU_MT_Solver::PreProcess() { return false; }\nbool SuperLU_MT_Solver::Factor() { return false; }\nbool SuperLU_MT_Solver::BackSolve(double* x, double* y) { return false; }\nvoid SuperLU_MT_Solver::Destroy() {}\nSparseMatrix* SuperLU_MT_Solver::CreateSparseMatrix(Matrix_Type ntype) { return nullptr; }\nbool SuperLU_MT_Solver::SetSparseMatrix(SparseMatrix* pA) { return false; }\n#endif // HAVE_SUPERLU_MT\n"
  },
  {
    "path": "NumCore/SuperLU_MT.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/CompactSymmMatrix.h>\n\nclass SuperLU_MT_Solver: public LinearSolver\n{\n\tclass Impl;\n\npublic:\n\tSuperLU_MT_Solver(FEModel* fem);\n\t~SuperLU_MT_Solver();\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\nprotected:\n\tCompactMatrix*\tm_pA;\n\tImpl* m;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/TestSolver.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#include \"stdafx.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include \"TestSolver.h\"\n#include <FECore/log.h>\n\n\nBEGIN_FECORE_CLASS(TestSolver, LinearSolver)\nEND_FECORE_CLASS();\n\n//-----------------------------------------------------------------------------\nTestSolver::TestSolver(FEModel* fem) : LinearSolver(fem), m_pA(0)\n{\n\tm_mtype = -2;\n}\n\n//-----------------------------------------------------------------------------\nTestSolver::~TestSolver()\n{\n\tDestroy();\n}\n\n//-----------------------------------------------------------------------------\nSparseMatrix* TestSolver::CreateSparseMatrix(Matrix_Type ntype)\n{\n\t// allocate the correct matrix format depending on matrix symmetry type\n\tswitch (ntype)\n\t{\n\tcase REAL_SYMMETRIC: m_mtype = -2; m_pA = new CompactSymmMatrix(1); break;\n\tcase REAL_UNSYMMETRIC: m_mtype = 11; m_pA = new CRSSparseMatrix(1); break;\n\tcase REAL_SYMM_STRUCTURE: m_mtype = 1; m_pA = new CRSSparseMatrix(1); break;\n\tdefault:\n\t\tassert(false);\n\t\tm_pA = nullptr;\n\t}\n\n\treturn m_pA;\n}\n\n//-----------------------------------------------------------------------------\nbool TestSolver::SetSparseMatrix(SparseMatrix* pA)\n{\n\tif (m_pA) Destroy();\n\tm_pA = dynamic_cast<CompactMatrix*>(pA);\n\tm_mtype = -2;\n\tif (dynamic_cast<CRSSparseMatrix*>(pA)) m_mtype = 11;\n\treturn (m_pA != nullptr);\n}\n\n//-----------------------------------------------------------------------------\nbool TestSolver::PreProcess()\n{\n\tm_n = m_pA->Rows();\n\tm_nnz = m_pA->NonZeroes();\n\tm_nrhs = 1;\n\treturn LinearSolver::PreProcess();\n}\n\n//-----------------------------------------------------------------------------\nbool TestSolver::Factor()\n{\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nbool TestSolver::BackSolve(double* x, double* b)\n{\n\t// make sure we have work to do\n\tif (m_pA->Rows() == 0) return true;\n\n\tfor (int i = 0; i < m_n; ++i) x[i] = 0.0;\n\n\t// update stats\n\tUpdateStats(1);\n\n\treturn true;\n}\n\n//-----------------------------------------------------------------------------\nvoid TestSolver::Destroy()\n{\n\n}\n"
  },
  {
    "path": "NumCore/TestSolver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n#pragma once\n#include <FECore/LinearSolver.h>\n#include <FECore/CompactUnSymmMatrix.h>\n#include <FECore/CompactSymmMatrix.h>\n\n\n// Test solver. Doesn't actually solve anything. Just returns 0 as solution. \nclass TestSolver : public LinearSolver\n{\npublic:\n\tTestSolver(FEModel* fem);\n\t~TestSolver();\n\tbool PreProcess() override;\n\tbool Factor() override;\n\tbool BackSolve(double* x, double* y) override;\n\tvoid Destroy() override;\n\n\tSparseMatrix* CreateSparseMatrix(Matrix_Type ntype) override;\n\tbool SetSparseMatrix(SparseMatrix* pA) override;\n\nprotected:\n\tCompactMatrix* m_pA;\n\tint\t\t\t\tm_mtype; // matrix type\n\n\t// Matrix data\n\tint m_n, m_nnz, m_nrhs;\n\n\tDECLARE_FECORE_CLASS();\n};\n"
  },
  {
    "path": "NumCore/numcore_api.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2020 University of Utah, The Trustees of Columbia University in \nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n\t#ifdef FECORE_DLL\n\t\t#ifdef numcore_EXPORTS\n\t\t\t#define NUMCORE_API __declspec(dllexport)\n\t\t#else\n\t\t\t#define NUMCORE_API __declspec(dllimport)\n\t\t#endif\n\t#else\n\t\t#define NUMCORE_API\n\t#endif\n#else\n\t#define NUMCORE_API\n#endif\n\n"
  },
  {
    "path": "NumCore/stdafx.cpp",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#include \"stdafx.h\"\n\n// TODO: reference any additional headers you need in STDAFX.H\n// and not in this file\n"
  },
  {
    "path": "NumCore/stdafx.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n#ifdef WIN32\n#include \"targetver.h\"\n\n#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers\n\n#endif\n\n// TODO: reference additional headers your program requires here\n#include <stdio.h>"
  },
  {
    "path": "NumCore/targetver.h",
    "content": "/*This file is part of the FEBio source code and is licensed under the MIT license\nlisted below.\n\nSee Copyright-FEBio.txt for details.\n\nCopyright (c) 2021 University of Utah, The Trustees of Columbia University in\nthe City of New York, and others.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.*/\n\n\n\n#pragma once\n\n// Including SDKDDKVer.h defines the highest available Windows platform.\n\n// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and\n// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.\n\n#include <SDKDDKVer.h>\n"
  },
  {
    "path": "README.md",
    "content": "<img src=\"Documentation/febio_logo_full.png\" href=\"https://gibboncode.org\" alt=\"FEBio\" width=\"75%\">\n<!-- ![febio logo](Documentation/febio_logo_full.png) -->\n\nFEBio is a nonlinear finite element (FE) solver that is specifically designed for biomechanical applications. It offers modeling scenarios, constitutive models and boundary conditions that are relevant to many research areas in biomechanics. All features can be used together seamlessly, giving the user a powerful tool for solving 3D problems in computational biomechanics. The software is open-source, and pre-compiled executables for Windows, OS-X and Linux platforms are available.\n\nExecutables for FEBio can be downloaded as part of the FEBio Studio installer from [https://febio.org/downloads/](https://febio.org/downloads/).  Please inform us of publications that use FEBio in research.  Information can be found on the Publications tab.  \n\nSupport forums can be found at https://forums.febio.org/.\n\n[![DOI](https://img.shields.io/badge/Citation-DOI:10.1115/1.4005694-green.svg)](https://dx.doi.org/10.1115%2F1.4005694)\n\n[![License](https://img.shields.io/badge/License-MIT-orange.svg)](LICENSE)\n\n# Automated Build Status\nOS | Build | Tests\n---|---|---\nWindows | [![Windows Build](http://repo.febio.org:5236/gh/actions/FEBio/windows/build.png)](https://github.com/febiosoftware/FEBio/actions/workflows/windows-push.yml) | [![Windows Tests](http://repo.febio.org:5236/gh/actions/FEBio/windows/test.png)](https://github.com/febiosoftware/FEBio/actions/workflows/windows-push.yml)\nmacOS | [![macOS Build](http://repo.febio.org:5236/gh/actions/FEBio/macos/build.png)](https://github.com/febiosoftware/FEBio/actions/workflows/macos-push.yml) | [![macOS Tests](http://repo.febio.org:5236/gh/actions/FEBio/macos/test.png)](https://github.com/febiosoftware/FEBio/actions/workflows/macos-push.yml)\nLinux | [![Linux Build](http://repo.febio.org:5236/gh/actions/FEBio/linux/build.png)](https://github.com/febiosoftware/FEBio/actions/workflows/linux-push.yml) | [![Linux Tests](http://repo.febio.org:5236/gh/actions/FEBio/linux/test.png)](https://github.com/febiosoftware/FEBio/actions/workflows/linux-push.yml)\n\n# Build Guide <a name=\"Build\"></a>\n\nRefer to [BUILD](BUILD.md)\n\n# Contributing <a name=\"Contributing\"></a>\n\nRefer to [CONTRIBUTING](CONTRIBUTING.md)\n\n# Code of conduct <a name=\"Conduct\"></a>\n\nRefer to [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md)\n"
  },
  {
    "path": "ROADMAP.md",
    "content": "# Roadmap\n\nThis roadmap briefly discusses some planned milestones for the FEBio project. \n\nPlease check out our [contribution guidelines](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) for information on how to contribute to the FEBio project.\n\n## Main Milestones\n\nThe main focus at this point is the implementation of the main aims of the NIH RO1 grant that funds FEBio development. The estimated completion date for these milestones is summer of 2023, although some functionality may be available sooner.\n\n### Formulate and implement a computationally efficient damage and fatigue failure framework for fibrous tissues. \n\nThis aim focuses on the development and implementation of a novel theoretical framework for modeling damage and fatigue failure mechanics. This work will extend the remodeling and growth framework already implemented in FEBio to include damage and failure of soft tissues. \n\n### Expand the multiphysics capabilities of FEBio to include solute transport, reactions involving solutes, and tissue growth and remodeling in fluid domains at their interfaces. \n\nIn this aim, the multiphisics capabilities of FEBio will be extended to include various transport and reaction modeling capabilites at solid-fluid boundaries. \n\n### Integrate the use of image data through the entire simulation pipeline, from model setup to model validation. \n\nThis aim proposes to include image data throughout the entire modeling pipeline, from pre-processing to post-processing. Most of this development will actually occur in FEBio Studio, but the focus in FEBio is the representation of heterogeneous model parameters (e.g. material parameters) that are extracted from image data. \n\n## Long term milestones\n\nThe following topics are currently under consideration as potential milestones, depending on user interest and feasibility. \n\n### topology optimization\n\nFEBio already has optimization algorithms for optimizing model parameters while preserving the geometry. In this milestone we would look into extending this framework to allow for topology changes. \n\n### Integration with the Julia programming language. \n\nThe Julia programming language appears to be an interesting potential path for extending FEBio's capabilities through scripting, or as a mechanism for integrating FEBio with other tools. \n\n"
  },
  {
    "path": "Xcode/FEAMR/FEAMR.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD505331E25DC4C0600FF68DE /* FETestRefine.h in Headers */ = {isa = PBXBuildFile; fileRef = D505331C25DC4C0600FF68DE /* FETestRefine.h */; };\n\t\tD505331F25DC4C0600FF68DE /* FETestRefine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D505331D25DC4C0600FF68DE /* FETestRefine.cpp */; };\n\t\tD515C31A259E24FC00F0C6FE /* FEMeshShapeInterpolator.h in Headers */ = {isa = PBXBuildFile; fileRef = D515C312259E24FC00F0C6FE /* FEMeshShapeInterpolator.h */; };\n\t\tD515C31B259E24FC00F0C6FE /* FEMeshDataInterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D515C313259E24FC00F0C6FE /* FEMeshDataInterpolator.cpp */; };\n\t\tD515C31C259E24FC00F0C6FE /* FEMeshDataInterpolator.h in Headers */ = {isa = PBXBuildFile; fileRef = D515C314259E24FC00F0C6FE /* FEMeshDataInterpolator.h */; };\n\t\tD515C31D259E24FC00F0C6FE /* FELeastSquaresInterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D515C315259E24FC00F0C6FE /* FELeastSquaresInterpolator.cpp */; };\n\t\tD515C31E259E24FC00F0C6FE /* FELeastSquaresInterpolator.h in Headers */ = {isa = PBXBuildFile; fileRef = D515C316259E24FC00F0C6FE /* FELeastSquaresInterpolator.h */; };\n\t\tD515C31F259E24FC00F0C6FE /* FEMeshShapeInterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D515C317259E24FC00F0C6FE /* FEMeshShapeInterpolator.cpp */; };\n\t\tD515C320259E24FC00F0C6FE /* FEDomainShapeInterpolator.h in Headers */ = {isa = PBXBuildFile; fileRef = D515C318259E24FC00F0C6FE /* FEDomainShapeInterpolator.h */; };\n\t\tD515C321259E24FC00F0C6FE /* FEDomainShapeInterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D515C319259E24FC00F0C6FE /* FEDomainShapeInterpolator.cpp */; };\n\t\tD53081442595627900FBA7BE /* FEScaleAdaptorCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D53081422595627900FBA7BE /* FEScaleAdaptorCriterion.h */; };\n\t\tD53081452595627900FBA7BE /* FEScaleAdaptorCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53081432595627900FBA7BE /* FEScaleAdaptorCriterion.cpp */; };\n\t\tD54626F0260E6FCA007E2D8E /* FEVariableCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54626EE260E6FCA007E2D8E /* FEVariableCriterion.cpp */; };\n\t\tD54626F1260E6FCA007E2D8E /* FEVariableCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D54626EF260E6FCA007E2D8E /* FEVariableCriterion.h */; };\n\t\tD568D3C525C84DB900FC67CD /* feamr_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D568D3C425C84DB900FC67CD /* feamr_api.h */; };\n\t\tD588571A260B989500659AEF /* FEFilterAdaptorCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5885718260B989500659AEF /* FEFilterAdaptorCriterion.cpp */; };\n\t\tD588571B260B989500659AEF /* FEFilterAdaptorCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5885719260B989500659AEF /* FEFilterAdaptorCriterion.h */; };\n\t\tD5AE8CAB2592EBC6003AC08B /* FETetRefine.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8C932592EBC5003AC08B /* FETetRefine.h */; };\n\t\tD5AE8CAC2592EBC6003AC08B /* FERefineMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8C942592EBC5003AC08B /* FERefineMesh.cpp */; };\n\t\tD5AE8CAE2592EBC6003AC08B /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8C962592EBC5003AC08B /* stdafx.h */; };\n\t\tD5AE8CAF2592EBC6003AC08B /* FEErosionAdaptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8C982592EBC5003AC08B /* FEErosionAdaptor.cpp */; };\n\t\tD5AE8CB02592EBC6003AC08B /* FEDomainErrorCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8C992592EBC5003AC08B /* FEDomainErrorCriterion.cpp */; };\n\t\tD5AE8CB12592EBC6003AC08B /* FEHexRefine.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8C9A2592EBC5003AC08B /* FEHexRefine.h */; };\n\t\tD5AE8CB32592EBC6003AC08B /* FEAMR.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8C9C2592EBC6003AC08B /* FEAMR.h */; };\n\t\tD5AE8CB52592EBC6003AC08B /* FEMMGRemesh.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8C9E2592EBC6003AC08B /* FEMMGRemesh.h */; };\n\t\tD5AE8CB62592EBC6003AC08B /* FERefineMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8C9F2592EBC6003AC08B /* FERefineMesh.h */; };\n\t\tD5AE8CB82592EBC6003AC08B /* FEHexRefine2D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CA12592EBC6003AC08B /* FEHexRefine2D.cpp */; };\n\t\tD5AE8CB92592EBC6003AC08B /* FEAMR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CA22592EBC6003AC08B /* FEAMR.cpp */; };\n\t\tD5AE8CBA2592EBC6003AC08B /* FEElementSelectionCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8CA32592EBC6003AC08B /* FEElementSelectionCriterion.h */; };\n\t\tD5AE8CBB2592EBC6003AC08B /* FEHexRefine2D.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8CA42592EBC6003AC08B /* FEHexRefine2D.h */; };\n\t\tD5AE8CBC2592EBC6003AC08B /* FEDomainErrorCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8CA52592EBC6003AC08B /* FEDomainErrorCriterion.h */; };\n\t\tD5AE8CBD2592EBC6003AC08B /* FEElementSelectionCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CA62592EBC6003AC08B /* FEElementSelectionCriterion.cpp */; };\n\t\tD5AE8CBE2592EBC6003AC08B /* FEHexRefine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CA72592EBC6003AC08B /* FEHexRefine.cpp */; };\n\t\tD5AE8CBF2592EBC6003AC08B /* FEMMGRemesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CA82592EBC6003AC08B /* FEMMGRemesh.cpp */; };\n\t\tD5AE8CC02592EBC6003AC08B /* FETetRefine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CA92592EBC6003AC08B /* FETetRefine.cpp */; };\n\t\tD5AE8CC12592EBC6003AC08B /* FEErosionAdaptor.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8CAA2592EBC6003AC08B /* FEErosionAdaptor.h */; };\n\t\tD5AE8CC32592EBE4003AC08B /* libmmg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5AE8CC22592EBE4003AC08B /* libmmg.a */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD505331C25DC4C0600FF68DE /* FETestRefine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETestRefine.h; sourceTree = \"<group>\"; };\n\t\tD505331D25DC4C0600FF68DE /* FETestRefine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETestRefine.cpp; sourceTree = \"<group>\"; };\n\t\tD515C312259E24FC00F0C6FE /* FEMeshShapeInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMeshShapeInterpolator.h; sourceTree = \"<group>\"; };\n\t\tD515C313259E24FC00F0C6FE /* FEMeshDataInterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMeshDataInterpolator.cpp; sourceTree = \"<group>\"; };\n\t\tD515C314259E24FC00F0C6FE /* FEMeshDataInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMeshDataInterpolator.h; sourceTree = \"<group>\"; };\n\t\tD515C315259E24FC00F0C6FE /* FELeastSquaresInterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELeastSquaresInterpolator.cpp; sourceTree = \"<group>\"; };\n\t\tD515C316259E24FC00F0C6FE /* FELeastSquaresInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELeastSquaresInterpolator.h; sourceTree = \"<group>\"; };\n\t\tD515C317259E24FC00F0C6FE /* FEMeshShapeInterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMeshShapeInterpolator.cpp; sourceTree = \"<group>\"; };\n\t\tD515C318259E24FC00F0C6FE /* FEDomainShapeInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomainShapeInterpolator.h; sourceTree = \"<group>\"; };\n\t\tD515C319259E24FC00F0C6FE /* FEDomainShapeInterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomainShapeInterpolator.cpp; sourceTree = \"<group>\"; };\n\t\tD53081422595627900FBA7BE /* FEScaleAdaptorCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEScaleAdaptorCriterion.h; sourceTree = \"<group>\"; };\n\t\tD53081432595627900FBA7BE /* FEScaleAdaptorCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEScaleAdaptorCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD54626EE260E6FCA007E2D8E /* FEVariableCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEVariableCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD54626EF260E6FCA007E2D8E /* FEVariableCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEVariableCriterion.h; sourceTree = \"<group>\"; };\n\t\tD568D3C425C84DB900FC67CD /* feamr_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = feamr_api.h; sourceTree = \"<group>\"; };\n\t\tD5885718260B989500659AEF /* FEFilterAdaptorCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFilterAdaptorCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5885719260B989500659AEF /* FEFilterAdaptorCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFilterAdaptorCriterion.h; sourceTree = \"<group>\"; };\n\t\tD5AE8C932592EBC5003AC08B /* FETetRefine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETetRefine.h; sourceTree = \"<group>\"; };\n\t\tD5AE8C942592EBC5003AC08B /* FERefineMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERefineMesh.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8C962592EBC5003AC08B /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5AE8C982592EBC5003AC08B /* FEErosionAdaptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEErosionAdaptor.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8C992592EBC5003AC08B /* FEDomainErrorCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomainErrorCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8C9A2592EBC5003AC08B /* FEHexRefine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEHexRefine.h; sourceTree = \"<group>\"; };\n\t\tD5AE8C9C2592EBC6003AC08B /* FEAMR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEAMR.h; sourceTree = \"<group>\"; };\n\t\tD5AE8C9E2592EBC6003AC08B /* FEMMGRemesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMMGRemesh.h; sourceTree = \"<group>\"; };\n\t\tD5AE8C9F2592EBC6003AC08B /* FERefineMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERefineMesh.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CA12592EBC6003AC08B /* FEHexRefine2D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEHexRefine2D.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CA22592EBC6003AC08B /* FEAMR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEAMR.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CA32592EBC6003AC08B /* FEElementSelectionCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElementSelectionCriterion.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CA42592EBC6003AC08B /* FEHexRefine2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEHexRefine2D.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CA52592EBC6003AC08B /* FEDomainErrorCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomainErrorCriterion.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CA62592EBC6003AC08B /* FEElementSelectionCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElementSelectionCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CA72592EBC6003AC08B /* FEHexRefine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEHexRefine.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CA82592EBC6003AC08B /* FEMMGRemesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMMGRemesh.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CA92592EBC6003AC08B /* FETetRefine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETetRefine.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CAA2592EBC6003AC08B /* FEErosionAdaptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEErosionAdaptor.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CC22592EBE4003AC08B /* libmmg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmmg.a; path = ../../../../../../../usr/local/lib/libmmg.a; sourceTree = \"<group>\"; };\n\t\tD5B9E3DD213F67CE0008B38A /* libFEAMR.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEAMR.a; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5B9E3DA213F67CE0008B38A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5AE8CC32592EBE4003AC08B /* libmmg.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5AE8C922592EBC5003AC08B /* FEAMR */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD568D3C425C84DB900FC67CD /* feamr_api.h */,\n\t\t\t\tD5AE8CA22592EBC6003AC08B /* FEAMR.cpp */,\n\t\t\t\tD5AE8C9C2592EBC6003AC08B /* FEAMR.h */,\n\t\t\t\tD5AE8C992592EBC5003AC08B /* FEDomainErrorCriterion.cpp */,\n\t\t\t\tD5AE8CA52592EBC6003AC08B /* FEDomainErrorCriterion.h */,\n\t\t\t\tD515C319259E24FC00F0C6FE /* FEDomainShapeInterpolator.cpp */,\n\t\t\t\tD515C318259E24FC00F0C6FE /* FEDomainShapeInterpolator.h */,\n\t\t\t\tD5AE8CA62592EBC6003AC08B /* FEElementSelectionCriterion.cpp */,\n\t\t\t\tD5AE8CA32592EBC6003AC08B /* FEElementSelectionCriterion.h */,\n\t\t\t\tD5AE8C982592EBC5003AC08B /* FEErosionAdaptor.cpp */,\n\t\t\t\tD5AE8CAA2592EBC6003AC08B /* FEErosionAdaptor.h */,\n\t\t\t\tD5885718260B989500659AEF /* FEFilterAdaptorCriterion.cpp */,\n\t\t\t\tD5885719260B989500659AEF /* FEFilterAdaptorCriterion.h */,\n\t\t\t\tD5AE8CA72592EBC6003AC08B /* FEHexRefine.cpp */,\n\t\t\t\tD5AE8C9A2592EBC5003AC08B /* FEHexRefine.h */,\n\t\t\t\tD5AE8CA12592EBC6003AC08B /* FEHexRefine2D.cpp */,\n\t\t\t\tD5AE8CA42592EBC6003AC08B /* FEHexRefine2D.h */,\n\t\t\t\tD515C315259E24FC00F0C6FE /* FELeastSquaresInterpolator.cpp */,\n\t\t\t\tD515C316259E24FC00F0C6FE /* FELeastSquaresInterpolator.h */,\n\t\t\t\tD515C313259E24FC00F0C6FE /* FEMeshDataInterpolator.cpp */,\n\t\t\t\tD515C314259E24FC00F0C6FE /* FEMeshDataInterpolator.h */,\n\t\t\t\tD515C317259E24FC00F0C6FE /* FEMeshShapeInterpolator.cpp */,\n\t\t\t\tD515C312259E24FC00F0C6FE /* FEMeshShapeInterpolator.h */,\n\t\t\t\tD5AE8CA82592EBC6003AC08B /* FEMMGRemesh.cpp */,\n\t\t\t\tD5AE8C9E2592EBC6003AC08B /* FEMMGRemesh.h */,\n\t\t\t\tD5AE8C942592EBC5003AC08B /* FERefineMesh.cpp */,\n\t\t\t\tD5AE8C9F2592EBC6003AC08B /* FERefineMesh.h */,\n\t\t\t\tD53081432595627900FBA7BE /* FEScaleAdaptorCriterion.cpp */,\n\t\t\t\tD53081422595627900FBA7BE /* FEScaleAdaptorCriterion.h */,\n\t\t\t\tD505331D25DC4C0600FF68DE /* FETestRefine.cpp */,\n\t\t\t\tD505331C25DC4C0600FF68DE /* FETestRefine.h */,\n\t\t\t\tD5AE8CA92592EBC6003AC08B /* FETetRefine.cpp */,\n\t\t\t\tD5AE8C932592EBC5003AC08B /* FETetRefine.h */,\n\t\t\t\tD54626EE260E6FCA007E2D8E /* FEVariableCriterion.cpp */,\n\t\t\t\tD54626EF260E6FCA007E2D8E /* FEVariableCriterion.h */,\n\t\t\t\tD5AE8C962592EBC5003AC08B /* stdafx.h */,\n\t\t\t);\n\t\t\tname = FEAMR;\n\t\t\tpath = ../../FEAMR;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5B9E3D4213F67CE0008B38A = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5AE8C922592EBC5003AC08B /* FEAMR */,\n\t\t\t\tD5B9E3DE213F67CE0008B38A /* Products */,\n\t\t\t\tD5E1B59C22B82A0A00E7939E /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5B9E3DE213F67CE0008B38A /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E3DD213F67CE0008B38A /* libFEAMR.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5E1B59C22B82A0A00E7939E /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5AE8CC22592EBE4003AC08B /* libmmg.a */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5B9E3DB213F67CE0008B38A /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5AE8CB32592EBC6003AC08B /* FEAMR.h in Headers */,\n\t\t\t\tD505331E25DC4C0600FF68DE /* FETestRefine.h in Headers */,\n\t\t\t\tD515C320259E24FC00F0C6FE /* FEDomainShapeInterpolator.h in Headers */,\n\t\t\t\tD515C31A259E24FC00F0C6FE /* FEMeshShapeInterpolator.h in Headers */,\n\t\t\t\tD5AE8CC12592EBC6003AC08B /* FEErosionAdaptor.h in Headers */,\n\t\t\t\tD5AE8CB12592EBC6003AC08B /* FEHexRefine.h in Headers */,\n\t\t\t\tD515C31C259E24FC00F0C6FE /* FEMeshDataInterpolator.h in Headers */,\n\t\t\t\tD588571B260B989500659AEF /* FEFilterAdaptorCriterion.h in Headers */,\n\t\t\t\tD5AE8CBB2592EBC6003AC08B /* FEHexRefine2D.h in Headers */,\n\t\t\t\tD5AE8CAE2592EBC6003AC08B /* stdafx.h in Headers */,\n\t\t\t\tD5AE8CBA2592EBC6003AC08B /* FEElementSelectionCriterion.h in Headers */,\n\t\t\t\tD5AE8CB52592EBC6003AC08B /* FEMMGRemesh.h in Headers */,\n\t\t\t\tD53081442595627900FBA7BE /* FEScaleAdaptorCriterion.h in Headers */,\n\t\t\t\tD5AE8CB62592EBC6003AC08B /* FERefineMesh.h in Headers */,\n\t\t\t\tD5AE8CAB2592EBC6003AC08B /* FETetRefine.h in Headers */,\n\t\t\t\tD515C31E259E24FC00F0C6FE /* FELeastSquaresInterpolator.h in Headers */,\n\t\t\t\tD54626F1260E6FCA007E2D8E /* FEVariableCriterion.h in Headers */,\n\t\t\t\tD568D3C525C84DB900FC67CD /* feamr_api.h in Headers */,\n\t\t\t\tD5AE8CBC2592EBC6003AC08B /* FEDomainErrorCriterion.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5B9E3DC213F67CE0008B38A /* FEAMR */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5B9E3E8213F67CE0008B38A /* Build configuration list for PBXNativeTarget \"FEAMR\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5B9E3D9213F67CE0008B38A /* Sources */,\n\t\t\t\tD5B9E3DA213F67CE0008B38A /* Frameworks */,\n\t\t\t\tD5B9E3DB213F67CE0008B38A /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEAMR;\n\t\t\tproductName = FECore;\n\t\t\tproductReference = D5B9E3DD213F67CE0008B38A /* libFEAMR.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5B9E3D5213F67CE0008B38A /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5B9E3DC213F67CE0008B38A = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5B9E3D8213F67CE0008B38A /* Build configuration list for PBXProject \"FEAMR\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5B9E3D4213F67CE0008B38A;\n\t\t\tproductRefGroup = D5B9E3DE213F67CE0008B38A /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5B9E3DC213F67CE0008B38A /* FEAMR */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5B9E3D9213F67CE0008B38A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5AE8CAF2592EBC6003AC08B /* FEErosionAdaptor.cpp in Sources */,\n\t\t\t\tD54626F0260E6FCA007E2D8E /* FEVariableCriterion.cpp in Sources */,\n\t\t\t\tD5AE8CBE2592EBC6003AC08B /* FEHexRefine.cpp in Sources */,\n\t\t\t\tD515C31D259E24FC00F0C6FE /* FELeastSquaresInterpolator.cpp in Sources */,\n\t\t\t\tD5AE8CBF2592EBC6003AC08B /* FEMMGRemesh.cpp in Sources */,\n\t\t\t\tD5AE8CB92592EBC6003AC08B /* FEAMR.cpp in Sources */,\n\t\t\t\tD515C321259E24FC00F0C6FE /* FEDomainShapeInterpolator.cpp in Sources */,\n\t\t\t\tD515C31F259E24FC00F0C6FE /* FEMeshShapeInterpolator.cpp in Sources */,\n\t\t\t\tD5AE8CBD2592EBC6003AC08B /* FEElementSelectionCriterion.cpp in Sources */,\n\t\t\t\tD53081452595627900FBA7BE /* FEScaleAdaptorCriterion.cpp in Sources */,\n\t\t\t\tD5AE8CB82592EBC6003AC08B /* FEHexRefine2D.cpp in Sources */,\n\t\t\t\tD515C31B259E24FC00F0C6FE /* FEMeshDataInterpolator.cpp in Sources */,\n\t\t\t\tD5AE8CB02592EBC6003AC08B /* FEDomainErrorCriterion.cpp in Sources */,\n\t\t\t\tD505331F25DC4C0600FF68DE /* FETestRefine.cpp in Sources */,\n\t\t\t\tD5AE8CAC2592EBC6003AC08B /* FERefineMesh.cpp in Sources */,\n\t\t\t\tD588571A260B989500659AEF /* FEFilterAdaptorCriterion.cpp in Sources */,\n\t\t\t\tD5AE8CC02592EBC6003AC08B /* FETetRefine.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5B9E3E6213F67CE0008B38A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t\t\"-DHAS_MMG\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5B9E3E7213F67CE0008B38A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t\t\"-DHAS_MMG\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5B9E3E9213F67CE0008B38A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5B9E3EA213F67CE0008B38A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5B9E3D8213F67CE0008B38A /* Build configuration list for PBXProject \"FEAMR\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5B9E3E6213F67CE0008B38A /* Debug */,\n\t\t\t\tD5B9E3E7213F67CE0008B38A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5B9E3E8213F67CE0008B38A /* Build configuration list for PBXNativeTarget \"FEAMR\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5B9E3E9213F67CE0008B38A /* Debug */,\n\t\t\t\tD5B9E3EA213F67CE0008B38A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5B9E3D5213F67CE0008B38A /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEAMR/FEAMR.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:/Users/gerard/Desktop/FECore/FEAMR.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEAMR/FEAMR.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBio3/FEBio3.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD50EC3932217AE5A006F6A57 /* FEBioApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50EC38E2217AE59006F6A57 /* FEBioApp.cpp */; };\n\t\tD50EC3942217AE5A006F6A57 /* febio_cb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50EC38F2217AE59006F6A57 /* febio_cb.cpp */; };\n\t\tD50EC3952217AE5A006F6A57 /* breakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50EC3922217AE5A006F6A57 /* breakpoint.cpp */; };\n\t\tD52EDF5F22BC3DA100849A6B /* libHYPRE.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D52EDF5E22BC3DA100849A6B /* libHYPRE.a */; };\n\t\tD53232912142AED6008DE511 /* FEBioCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232852142AED6008DE511 /* FEBioCommand.cpp */; };\n\t\tD53232922142AED6008DE511 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232882142AED6008DE511 /* console.cpp */; };\n\t\tD53232932142AED6008DE511 /* Interrupt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232892142AED6008DE511 /* Interrupt.cpp */; };\n\t\tD53232942142AED6008DE511 /* CommandManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D532328A2142AED6008DE511 /* CommandManager.cpp */; };\n\t\tD53232952142AED6008DE511 /* FEBio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D532328C2142AED6008DE511 /* FEBio.cpp */; };\n\t\tD53232962142AED6008DE511 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D532328E2142AED6008DE511 /* stdafx.cpp */; };\n\t\tD53232A02142AF9B008DE511 /* libFEBioFluid.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D532329F2142AF9B008DE511 /* libFEBioFluid.a */; };\n\t\tD53232A22142AF9B008DE511 /* libFEBioLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232A12142AF9B008DE511 /* libFEBioLib.a */; };\n\t\tD53232A42142AF9B008DE511 /* libFEBioMech.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232A32142AF9B008DE511 /* libFEBioMech.a */; };\n\t\tD53232A62142AF9B008DE511 /* libFEBioMix.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232A52142AF9B008DE511 /* libFEBioMix.a */; };\n\t\tD53232A82142AF9B008DE511 /* libFEBioOpt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232A72142AF9B008DE511 /* libFEBioOpt.a */; };\n\t\tD53232AA2142AF9B008DE511 /* libFEBioPlot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232A92142AF9B008DE511 /* libFEBioPlot.a */; };\n\t\tD53232AC2142AF9B008DE511 /* libFEBioTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232AB2142AF9B008DE511 /* libFEBioTest.a */; };\n\t\tD53232AE2142AF9B008DE511 /* libFEBioXML.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232AD2142AF9B008DE511 /* libFEBioXML.a */; };\n\t\tD53232B02142AF9B008DE511 /* libFECore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232AF2142AF9B008DE511 /* libFECore.a */; };\n\t\tD53232B22142AF9B008DE511 /* libNumCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D53232B12142AF9B008DE511 /* libNumCore.a */; };\n\t\tD5886C4F24D76CFA004DE966 /* libomp.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D5886C4E24D76CFA004DE966 /* libomp.dylib */; };\n\t\tD5AE8CCB2592EF7F003AC08B /* libFEAMR.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5AE8CCA2592EF7F003AC08B /* libFEAMR.a */; };\n\t\tD5B37C1B22A427670068CF1A /* febio.xml in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5B37C1A22A427670068CF1A /* febio.xml */; };\n\t\tD5C74CD622E8F7E300A6DFD2 /* libmpi.40.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D5C74CD522E8F7E300A6DFD2 /* libmpi.40.dylib */; };\n\t\tD5DEB5672145858F0041CEEA /* libmkl_intel_lp64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5DEB5642145858F0041CEEA /* libmkl_intel_lp64.a */; settings = {ATTRIBUTES = (Weak, ); }; };\n\t\tD5DEB5682145858F0041CEEA /* libmkl_intel_thread.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5DEB5652145858F0041CEEA /* libmkl_intel_thread.a */; settings = {ATTRIBUTES = (Weak, ); }; };\n\t\tD5DEB5692145858F0041CEEA /* libmkl_core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5DEB5662145858F0041CEEA /* libmkl_core.a */; settings = {ATTRIBUTES = (Weak, ); }; };\n\t\tD5E1AA9425ED649A003162AB /* ping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E1AA9325ED649A003162AB /* ping.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tD53232762142AEC2008DE511 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 12;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 16;\n\t\t\tfiles = (\n\t\t\t\tD5B37C1B22A427670068CF1A /* febio.xml in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\tD50EC38C2217AE59006F6A57 /* breakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpoint.h; sourceTree = \"<group>\"; };\n\t\tD50EC38D2217AE59006F6A57 /* cmdoptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmdoptions.h; sourceTree = \"<group>\"; };\n\t\tD50EC38E2217AE59006F6A57 /* FEBioApp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioApp.cpp; sourceTree = \"<group>\"; };\n\t\tD50EC38F2217AE59006F6A57 /* febio_cb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = febio_cb.cpp; sourceTree = \"<group>\"; };\n\t\tD50EC3902217AE5A006F6A57 /* FEBioApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioApp.h; sourceTree = \"<group>\"; };\n\t\tD50EC3912217AE5A006F6A57 /* febio_cb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febio_cb.h; sourceTree = \"<group>\"; };\n\t\tD50EC3922217AE5A006F6A57 /* breakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpoint.cpp; sourceTree = \"<group>\"; };\n\t\tD52EDF5E22BC3DA100849A6B /* libHYPRE.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libHYPRE.a; path = \"/usr/local/hypre-master/src/lib/libHYPRE.a\"; sourceTree = \"<absolute>\"; };\n\t\tD53232782142AEC2008DE511 /* FEBio3 */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = FEBio3; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232832142AED6008DE511 /* console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console.h; sourceTree = \"<group>\"; };\n\t\tD53232842142AED6008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD53232852142AED6008DE511 /* FEBioCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioCommand.cpp; sourceTree = \"<group>\"; };\n\t\tD53232862142AED6008DE511 /* FEBioCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioCommand.h; sourceTree = \"<group>\"; };\n\t\tD53232882142AED6008DE511 /* console.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = console.cpp; sourceTree = \"<group>\"; };\n\t\tD53232892142AED6008DE511 /* Interrupt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Interrupt.cpp; sourceTree = \"<group>\"; };\n\t\tD532328A2142AED6008DE511 /* CommandManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommandManager.cpp; sourceTree = \"<group>\"; };\n\t\tD532328B2142AED6008DE511 /* Interrupt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Interrupt.h; sourceTree = \"<group>\"; };\n\t\tD532328C2142AED6008DE511 /* FEBio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBio.cpp; sourceTree = \"<group>\"; };\n\t\tD532328D2142AED6008DE511 /* Command.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Command.h; sourceTree = \"<group>\"; };\n\t\tD532328E2142AED6008DE511 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = \"<group>\"; };\n\t\tD532328F2142AED6008DE511 /* CommandManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommandManager.h; sourceTree = \"<group>\"; };\n\t\tD532329F2142AF9B008DE511 /* libFEBioFluid.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioFluid.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232A12142AF9B008DE511 /* libFEBioLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioLib.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232A32142AF9B008DE511 /* libFEBioMech.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioMech.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232A52142AF9B008DE511 /* libFEBioMix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioMix.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232A72142AF9B008DE511 /* libFEBioOpt.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioOpt.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232A92142AF9B008DE511 /* libFEBioPlot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioPlot.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232AB2142AF9B008DE511 /* libFEBioTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioTest.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232AD2142AF9B008DE511 /* libFEBioXML.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEBioXML.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232AF2142AF9B008DE511 /* libFECore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFECore.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53232B12142AF9B008DE511 /* libNumCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libNumCore.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5886C4E24D76CFA004DE966 /* libomp.dylib */ = {isa = PBXFileReference; lastKnownFileType = \"compiled.mach-o.dylib\"; name = libomp.dylib; path = ../../../../../../../usr/local/Cellar/libomp/9.0.0/lib/libomp.dylib; sourceTree = \"<group>\"; };\n\t\tD5AE8CCA2592EF7F003AC08B /* libFEAMR.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFEAMR.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5B37C1A22A427670068CF1A /* febio.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = febio.xml; path = ../../build/bin/febio.xml; sourceTree = \"<group>\"; };\n\t\tD5C74CD522E8F7E300A6DFD2 /* libmpi.40.dylib */ = {isa = PBXFileReference; lastKnownFileType = \"compiled.mach-o.dylib\"; name = libmpi.40.dylib; path = \"/usr/local/Cellar/open-mpi/4.0.2/lib/libmpi.40.dylib\"; sourceTree = \"<absolute>\"; };\n\t\tD5DEB5642145858F0041CEEA /* libmkl_intel_lp64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmkl_intel_lp64.a; path = /opt/intel/compilers_and_libraries_2018.3.185/mac/mkl/lib/libmkl_intel_lp64.a; sourceTree = \"<absolute>\"; };\n\t\tD5DEB5652145858F0041CEEA /* libmkl_intel_thread.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmkl_intel_thread.a; path = /opt/intel/compilers_and_libraries_2018.3.185/mac/mkl/lib/libmkl_intel_thread.a; sourceTree = \"<absolute>\"; };\n\t\tD5DEB5662145858F0041CEEA /* libmkl_core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmkl_core.a; path = /opt/intel/compilers_and_libraries_2018.3.185/mac/mkl/lib/libmkl_core.a; sourceTree = \"<absolute>\"; };\n\t\tD5E1AA9225ED649A003162AB /* ping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ping.h; sourceTree = \"<group>\"; };\n\t\tD5E1AA9325ED649A003162AB /* ping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ping.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD53232752142AEC2008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5886C4F24D76CFA004DE966 /* libomp.dylib in Frameworks */,\n\t\t\t\tD5C74CD622E8F7E300A6DFD2 /* libmpi.40.dylib in Frameworks */,\n\t\t\t\tD52EDF5F22BC3DA100849A6B /* libHYPRE.a in Frameworks */,\n\t\t\t\tD5DEB5672145858F0041CEEA /* libmkl_intel_lp64.a in Frameworks */,\n\t\t\t\tD5DEB5682145858F0041CEEA /* libmkl_intel_thread.a in Frameworks */,\n\t\t\t\tD5DEB5692145858F0041CEEA /* libmkl_core.a in Frameworks */,\n\t\t\t\tD53232A02142AF9B008DE511 /* libFEBioFluid.a in Frameworks */,\n\t\t\t\tD53232A22142AF9B008DE511 /* libFEBioLib.a in Frameworks */,\n\t\t\t\tD53232A42142AF9B008DE511 /* libFEBioMech.a in Frameworks */,\n\t\t\t\tD53232A62142AF9B008DE511 /* libFEBioMix.a in Frameworks */,\n\t\t\t\tD53232A82142AF9B008DE511 /* libFEBioOpt.a in Frameworks */,\n\t\t\t\tD53232AA2142AF9B008DE511 /* libFEBioPlot.a in Frameworks */,\n\t\t\t\tD53232AC2142AF9B008DE511 /* libFEBioTest.a in Frameworks */,\n\t\t\t\tD53232AE2142AF9B008DE511 /* libFEBioXML.a in Frameworks */,\n\t\t\t\tD53232B02142AF9B008DE511 /* libFECore.a in Frameworks */,\n\t\t\t\tD5AE8CCB2592EF7F003AC08B /* libFEAMR.a in Frameworks */,\n\t\t\t\tD53232B22142AF9B008DE511 /* libNumCore.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD532326F2142AEC2008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B37C1A22A427670068CF1A /* febio.xml */,\n\t\t\t\tD53232822142AED6008DE511 /* FEBio3 */,\n\t\t\t\tD53232792142AEC2008DE511 /* Products */,\n\t\t\t\tD532329E2142AF9B008DE511 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53232792142AEC2008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD53232782142AEC2008DE511 /* FEBio3 */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53232822142AED6008DE511 /* FEBio3 */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD50EC3922217AE5A006F6A57 /* breakpoint.cpp */,\n\t\t\t\tD50EC38C2217AE59006F6A57 /* breakpoint.h */,\n\t\t\t\tD50EC38D2217AE59006F6A57 /* cmdoptions.h */,\n\t\t\t\tD532328D2142AED6008DE511 /* Command.h */,\n\t\t\t\tD532328A2142AED6008DE511 /* CommandManager.cpp */,\n\t\t\t\tD532328F2142AED6008DE511 /* CommandManager.h */,\n\t\t\t\tD53232882142AED6008DE511 /* console.cpp */,\n\t\t\t\tD53232832142AED6008DE511 /* console.h */,\n\t\t\t\tD50EC38F2217AE59006F6A57 /* febio_cb.cpp */,\n\t\t\t\tD50EC3912217AE5A006F6A57 /* febio_cb.h */,\n\t\t\t\tD532328C2142AED6008DE511 /* FEBio.cpp */,\n\t\t\t\tD50EC38E2217AE59006F6A57 /* FEBioApp.cpp */,\n\t\t\t\tD50EC3902217AE5A006F6A57 /* FEBioApp.h */,\n\t\t\t\tD53232852142AED6008DE511 /* FEBioCommand.cpp */,\n\t\t\t\tD53232862142AED6008DE511 /* FEBioCommand.h */,\n\t\t\t\tD53232892142AED6008DE511 /* Interrupt.cpp */,\n\t\t\t\tD532328B2142AED6008DE511 /* Interrupt.h */,\n\t\t\t\tD5E1AA9325ED649A003162AB /* ping.cpp */,\n\t\t\t\tD5E1AA9225ED649A003162AB /* ping.h */,\n\t\t\t\tD532328E2142AED6008DE511 /* stdafx.cpp */,\n\t\t\t\tD53232842142AED6008DE511 /* stdafx.h */,\n\t\t\t);\n\t\t\tname = FEBio3;\n\t\t\tpath = ../../FEBio3;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD532329E2142AF9B008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5AE8CCA2592EF7F003AC08B /* libFEAMR.a */,\n\t\t\t\tD5886C4E24D76CFA004DE966 /* libomp.dylib */,\n\t\t\t\tD5C74CD522E8F7E300A6DFD2 /* libmpi.40.dylib */,\n\t\t\t\tD52EDF5E22BC3DA100849A6B /* libHYPRE.a */,\n\t\t\t\tD5DEB5662145858F0041CEEA /* libmkl_core.a */,\n\t\t\t\tD5DEB5642145858F0041CEEA /* libmkl_intel_lp64.a */,\n\t\t\t\tD5DEB5652145858F0041CEEA /* libmkl_intel_thread.a */,\n\t\t\t\tD532329F2142AF9B008DE511 /* libFEBioFluid.a */,\n\t\t\t\tD53232A12142AF9B008DE511 /* libFEBioLib.a */,\n\t\t\t\tD53232A32142AF9B008DE511 /* libFEBioMech.a */,\n\t\t\t\tD53232A52142AF9B008DE511 /* libFEBioMix.a */,\n\t\t\t\tD53232A72142AF9B008DE511 /* libFEBioOpt.a */,\n\t\t\t\tD53232A92142AF9B008DE511 /* libFEBioPlot.a */,\n\t\t\t\tD53232AB2142AF9B008DE511 /* libFEBioTest.a */,\n\t\t\t\tD53232AD2142AF9B008DE511 /* libFEBioXML.a */,\n\t\t\t\tD53232AF2142AF9B008DE511 /* libFECore.a */,\n\t\t\t\tD53232B12142AF9B008DE511 /* libNumCore.a */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\tD53232772142AEC2008DE511 /* FEBio3 */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D532327F2142AEC2008DE511 /* Build configuration list for PBXNativeTarget \"FEBio3\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD53232742142AEC2008DE511 /* Sources */,\n\t\t\t\tD53232752142AEC2008DE511 /* Frameworks */,\n\t\t\t\tD53232762142AEC2008DE511 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBio3;\n\t\t\tproductName = FEBio3;\n\t\t\tproductReference = D53232782142AEC2008DE511 /* FEBio3 */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD53232702142AEC2008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1130;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD53232772142AEC2008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D53232732142AEC2008DE511 /* Build configuration list for PBXProject \"FEBio3\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D532326F2142AEC2008DE511;\n\t\t\tproductRefGroup = D53232792142AEC2008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD53232772142AEC2008DE511 /* FEBio3 */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD53232742142AEC2008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD50EC3952217AE5A006F6A57 /* breakpoint.cpp in Sources */,\n\t\t\t\tD53232952142AED6008DE511 /* FEBio.cpp in Sources */,\n\t\t\t\tD53232932142AED6008DE511 /* Interrupt.cpp in Sources */,\n\t\t\t\tD5E1AA9425ED649A003162AB /* ping.cpp in Sources */,\n\t\t\t\tD53232942142AED6008DE511 /* CommandManager.cpp in Sources */,\n\t\t\t\tD53232962142AED6008DE511 /* stdafx.cpp in Sources */,\n\t\t\t\tD53232912142AED6008DE511 /* FEBioCommand.cpp in Sources */,\n\t\t\t\tD53232922142AED6008DE511 /* console.cpp in Sources */,\n\t\t\t\tD50EC3942217AE5A006F6A57 /* febio_cb.cpp in Sources */,\n\t\t\t\tD50EC3932217AE5A006F6A57 /* FEBioApp.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD532327D2142AEC2008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tICC_RT_LIBRARY = default;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-DUSE_MPI\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD532327E2142AEC2008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = NDEBUG;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tICC_MKL = threaded;\n\t\t\t\tICC_RT_LIBRARY = static;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-DUSE_MPI\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD53232802142AEC2008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/opt/intel/compilers_and_libraries_2018.3.185/mac/compiler/lib,\n\t\t\t\t\t/opt/intel/compilers_and_libraries_2018.3.185/mac/mkl/lib,\n\t\t\t\t\t\"/usr/local/hypre-master/src/hypre/lib\",\n\t\t\t\t\t/usr/local/opt/openmpi/lib,\n\t\t\t\t\t\"/usr/local/hypre-master/src/lib\",\n\t\t\t\t\t/usr/local/opt/libomp/lib,\n\t\t\t\t\t\"/usr/local/opt/open-mpi/lib\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD53232812142AEC2008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/opt/intel/compilers_and_libraries_2018.3.185/mac/compiler/lib,\n\t\t\t\t\t/opt/intel/compilers_and_libraries_2018.3.185/mac/mkl/lib,\n\t\t\t\t\t\"/usr/local/hypre-master/src/hypre/lib\",\n\t\t\t\t\t/usr/local/opt/openmpi/lib,\n\t\t\t\t\t\"/usr/local/hypre-master/src/lib\",\n\t\t\t\t\t/usr/local/opt/libomp/lib,\n\t\t\t\t\t\"/usr/local/opt/open-mpi/lib\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD53232732142AEC2008DE511 /* Build configuration list for PBXProject \"FEBio3\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD532327D2142AEC2008DE511 /* Debug */,\n\t\t\t\tD532327E2142AEC2008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD532327F2142AEC2008DE511 /* Build configuration list for PBXNativeTarget \"FEBio3\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD53232802142AEC2008DE511 /* Debug */,\n\t\t\t\tD53232812142AEC2008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D53232702142AEC2008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBio3/FEBio3.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBio3.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBio3/FEBio3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBio3.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:FEBio3/FEBio3.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioFluid/FEBioFluid.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioLib/FEBioLib.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioMech/FEBioMech.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioMix/FEBioMix.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioOpt/FEBioOpt.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioPlot/FEBioPlot.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioTest/FEBioTest.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEBioXML/FEBioXML.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FECore/FECore.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:FEAMR/FEAMR.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:NumCore/NumCore.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBio3.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioFluid/FEBioFluid.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t690FCC6C25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 690FCC6A25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.cpp */; };\n\t\t690FCC6D25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = 690FCC6B25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.h */; };\n\t\t690FCC7025CDB9BE000A6204 /* FEMultiphasicFSIPressure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 690FCC6E25CDB9BE000A6204 /* FEMultiphasicFSIPressure.cpp */; };\n\t\t690FCC7125CDB9BE000A6204 /* FEMultiphasicFSIPressure.h in Headers */ = {isa = PBXBuildFile; fileRef = 690FCC6F25CDB9BE000A6204 /* FEMultiphasicFSIPressure.h */; };\n\t\t691BD30C2412B187002BC4A1 /* FEFluidMixtureTractionLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 691BD2F42412B182002BC4A1 /* FEFluidMixtureTractionLoad.cpp */; };\n\t\t691BD3102412B187002BC4A1 /* FEBiphasicFSIDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 691BD2F82412B183002BC4A1 /* FEBiphasicFSIDomain3D.cpp */; };\n\t\t691BD3112412B187002BC4A1 /* FEBiphasicFSIDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 691BD2F92412B184002BC4A1 /* FEBiphasicFSIDomain.h */; };\n\t\t691BD3172412B187002BC4A1 /* FEBiphasicFSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 691BD2FF2412B185002BC4A1 /* FEBiphasicFSI.cpp */; };\n\t\t691BD3182412B187002BC4A1 /* FEBiphasicFSI.h in Headers */ = {isa = PBXBuildFile; fileRef = 691BD3002412B185002BC4A1 /* FEBiphasicFSI.h */; };\n\t\t691BD31B2412B187002BC4A1 /* FEFluidMixtureTractionLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = 691BD3032412B186002BC4A1 /* FEFluidMixtureTractionLoad.h */; };\n\t\t691BD3202412B187002BC4A1 /* FEBiphasicFSIDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 691BD3082412B187002BC4A1 /* FEBiphasicFSIDomain.cpp */; };\n\t\t691BD3222412B187002BC4A1 /* FEBiphasicFSIDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = 691BD30A2412B187002BC4A1 /* FEBiphasicFSIDomain3D.h */; };\n\t\t693FEF6625645FC3004F9ACF /* FEFluidSolutesNaturalFlux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 693FEF6425645FC3004F9ACF /* FEFluidSolutesNaturalFlux.cpp */; };\n\t\t693FEF6725645FC3004F9ACF /* FEFluidSolutesNaturalFlux.h in Headers */ = {isa = PBXBuildFile; fileRef = 693FEF6525645FC3004F9ACF /* FEFluidSolutesNaturalFlux.h */; };\n\t\t694948DC2476D5220031BD24 /* FEThermoFluidPressureLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 694948D82476D5220031BD24 /* FEThermoFluidPressureLoad.cpp */; };\n\t\t694948DD2476D5220031BD24 /* FETemperatureBackFlowStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = 694948D92476D5220031BD24 /* FETemperatureBackFlowStabilization.h */; };\n\t\t694948DE2476D5220031BD24 /* FEThermoFluidPressureLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = 694948DA2476D5220031BD24 /* FEThermoFluidPressureLoad.h */; };\n\t\t694948DF2476D5220031BD24 /* FETemperatureBackFlowStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 694948DB2476D5220031BD24 /* FETemperatureBackFlowStabilization.cpp */; };\n\t\t6957D5B9247D6C4C008522FA /* FEFluidRCBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6957D5B7247D6C4B008522FA /* FEFluidRCBC.cpp */; };\n\t\t6957D5BA247D6C4C008522FA /* FEFluidRCBC.h in Headers */ = {isa = PBXBuildFile; fileRef = 6957D5B8247D6C4C008522FA /* FEFluidRCBC.h */; };\n\t\t695C594E25BF5BA9006B53DE /* FEFluidSolutesFlux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C594C25BF5BA9006B53DE /* FEFluidSolutesFlux.cpp */; };\n\t\t695C594F25BF5BA9006B53DE /* FEFluidSolutesFlux.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C594D25BF5BA9006B53DE /* FEFluidSolutesFlux.h */; };\n\t\t695C595225C31E9F006B53DE /* FEBioMultiphasicFSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C595025C31E9F006B53DE /* FEBioMultiphasicFSI.cpp */; };\n\t\t695C595325C31E9F006B53DE /* FEBioMultiphasicFSI.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C595125C31E9F006B53DE /* FEBioMultiphasicFSI.h */; };\n\t\t695C595625C32277006B53DE /* FEMultiphasicFSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C595425C32277006B53DE /* FEMultiphasicFSI.cpp */; };\n\t\t695C595725C32277006B53DE /* FEMultiphasicFSI.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C595525C32277006B53DE /* FEMultiphasicFSI.h */; };\n\t\t695C595A25C322B7006B53DE /* FEMultiphasicFSIDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C595825C322B7006B53DE /* FEMultiphasicFSIDomain.cpp */; };\n\t\t695C595B25C322B7006B53DE /* FEMultiphasicFSIDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C595925C322B7006B53DE /* FEMultiphasicFSIDomain.h */; };\n\t\t695C595E25C322F7006B53DE /* FEMultiphasicFSIDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C595C25C322F7006B53DE /* FEMultiphasicFSIDomain3D.cpp */; };\n\t\t695C595F25C322F7006B53DE /* FEMultiphasicFSIDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C595D25C322F7006B53DE /* FEMultiphasicFSIDomain3D.h */; };\n\t\t695C596225C3230F006B53DE /* FEMultiphasicFSIDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C596025C3230F006B53DE /* FEMultiphasicFSIDomainFactory.cpp */; };\n\t\t695C596325C3230F006B53DE /* FEMultiphasicFSIDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C596125C3230F006B53DE /* FEMultiphasicFSIDomainFactory.h */; };\n\t\t695C596625C32326006B53DE /* FEMultiphasicFSISolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C596425C32326006B53DE /* FEMultiphasicFSISolver.cpp */; };\n\t\t695C596725C32326006B53DE /* FEMultiphasicFSISolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C596525C32326006B53DE /* FEMultiphasicFSISolver.h */; };\n\t\t695C596E25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 695C596C25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.cpp */; };\n\t\t695C596F25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.h in Headers */ = {isa = PBXBuildFile; fileRef = 695C596D25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.h */; };\n\t\t696F85ED2432A02900890DD2 /* FEFluidRCRBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 696F85EB2432A02900890DD2 /* FEFluidRCRBC.cpp */; };\n\t\t696F85EE2432A02900890DD2 /* FEFluidRCRBC.h in Headers */ = {isa = PBXBuildFile; fileRef = 696F85EC2432A02900890DD2 /* FEFluidRCRBC.h */; };\n\t\t698D57F62582BD66004CFD27 /* FEFluidSolutesPressure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 698D57F42582BD66004CFD27 /* FEFluidSolutesPressure.cpp */; };\n\t\t698D57F72582BD66004CFD27 /* FEFluidSolutesPressure.h in Headers */ = {isa = PBXBuildFile; fileRef = 698D57F52582BD66004CFD27 /* FEFluidSolutesPressure.h */; };\n\t\t69BDB97D25A519720096F5D0 /* FEBiphasicFSITraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69BDB97B25A519720096F5D0 /* FEBiphasicFSITraction.cpp */; };\n\t\t69BDB97E25A519720096F5D0 /* FEBiphasicFSITraction.h in Headers */ = {isa = PBXBuildFile; fileRef = 69BDB97C25A519720096F5D0 /* FEBiphasicFSITraction.h */; };\n\t\tD5027076269F48D2007B16A3 /* FELinearElasticFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5027074269F48D2007B16A3 /* FELinearElasticFluid.cpp */; };\n\t\tD5027077269F48D2007B16A3 /* FELinearElasticFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5027075269F48D2007B16A3 /* FELinearElasticFluid.h */; };\n\t\tD502707A269F5092007B16A3 /* FENonlinearElasticFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5027078269F5092007B16A3 /* FENonlinearElasticFluid.cpp */; };\n\t\tD502707B269F5092007B16A3 /* FENonlinearElasticFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5027079269F5092007B16A3 /* FENonlinearElasticFluid.h */; };\n\t\tD50462BC22EF676700CCA668 /* FEBioFluidSolutes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50462BA22EF676700CCA668 /* FEBioFluidSolutes.cpp */; };\n\t\tD50462BD22EF676700CCA668 /* FEBioFluidSolutes.h in Headers */ = {isa = PBXBuildFile; fileRef = D50462BB22EF676700CCA668 /* FEBioFluidSolutes.h */; };\n\t\tD50462C022EF693600CCA668 /* FEFluidSolutesSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50462BE22EF693600CCA668 /* FEFluidSolutesSolver.cpp */; };\n\t\tD50462C122EF693600CCA668 /* FEFluidSolutesSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D50462BF22EF693600CCA668 /* FEFluidSolutesSolver.h */; };\n\t\tD50462C422EF6A4D00CCA668 /* FEFluidSolutesDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50462C222EF6A4D00CCA668 /* FEFluidSolutesDomain3D.cpp */; };\n\t\tD50462C522EF6A4D00CCA668 /* FEFluidSolutesDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = D50462C322EF6A4D00CCA668 /* FEFluidSolutesDomain3D.h */; };\n\t\tD50462C822EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50462C622EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.cpp */; };\n\t\tD50462C922EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D50462C722EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.h */; };\n\t\tD50462CC22EF6BB800CCA668 /* FEFluidSolutes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50462CA22EF6BB800CCA668 /* FEFluidSolutes.cpp */; };\n\t\tD50462CD22EF6BB800CCA668 /* FEFluidSolutes.h in Headers */ = {isa = PBXBuildFile; fileRef = D50462CB22EF6BB800CCA668 /* FEFluidSolutes.h */; };\n\t\tD509D27F22F32408007DFC44 /* FESoluteBackflowStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D509D27D22F32408007DFC44 /* FESoluteBackflowStabilization.cpp */; };\n\t\tD509D28022F32408007DFC44 /* FESoluteBackflowStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = D509D27E22F32408007DFC44 /* FESoluteBackflowStabilization.h */; };\n\t\tD509D28322F4CABC007DFC44 /* FESoluteConvectiveFlow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D509D28122F4CABC007DFC44 /* FESoluteConvectiveFlow.cpp */; };\n\t\tD509D28422F4CABC007DFC44 /* FESoluteConvectiveFlow.h in Headers */ = {isa = PBXBuildFile; fileRef = D509D28222F4CABC007DFC44 /* FESoluteConvectiveFlow.h */; };\n\t\tD511EF0F253652CD00893F69 /* FERealLiquid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D511EF0D253652CD00893F69 /* FERealLiquid.cpp */; };\n\t\tD511EF10253652CD00893F69 /* FERealLiquid.h in Headers */ = {isa = PBXBuildFile; fileRef = D511EF0E253652CD00893F69 /* FERealLiquid.h */; };\n\t\tD511EF1E253B782C00893F69 /* FERealGas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D511EF1C253B782C00893F69 /* FERealGas.cpp */; };\n\t\tD511EF1F253B782C00893F69 /* FERealGas.h in Headers */ = {isa = PBXBuildFile; fileRef = D511EF1D253B782C00893F69 /* FERealGas.h */; };\n\t\tD511EF22253C7C5800893F69 /* FETempDependentConductivity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D511EF20253C7C5800893F69 /* FETempDependentConductivity.cpp */; };\n\t\tD511EF23253C7C5800893F69 /* FETempDependentConductivity.h in Headers */ = {isa = PBXBuildFile; fileRef = D511EF21253C7C5800893F69 /* FETempDependentConductivity.h */; };\n\t\tD53232262142AE3D008DE511 /* FEFluidResistanceBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231DD2142AE3D008DE511 /* FEFluidResistanceBC.cpp */; };\n\t\tD53232272142AE3D008DE511 /* FEConstraintFrictionlessWall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231DE2142AE3D008DE511 /* FEConstraintFrictionlessWall.cpp */; };\n\t\tD53232282142AE3D008DE511 /* FEFluidFSITraction.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231DF2142AE3D008DE511 /* FEFluidFSITraction.h */; };\n\t\tD53232292142AE3D008DE511 /* FEFluidTractionLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231E02142AE3D008DE511 /* FEFluidTractionLoad.cpp */; };\n\t\tD532322A2142AE3D008DE511 /* FEFluidFSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231E12142AE3D008DE511 /* FEFluidFSI.cpp */; };\n\t\tD532322B2142AE3D008DE511 /* FECarreauYasudaFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231E22142AE3D008DE511 /* FECarreauYasudaFluid.cpp */; };\n\t\tD532322C2142AE3D008DE511 /* FENewtonianFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231E32142AE3D008DE511 /* FENewtonianFluid.h */; };\n\t\tD532322D2142AE3D008DE511 /* FEFluidDomain2D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231E42142AE3D008DE511 /* FEFluidDomain2D.cpp */; };\n\t\tD532322E2142AE3D008DE511 /* FEFluidTractionLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231E52142AE3D008DE511 /* FEFluidTractionLoad.h */; };\n\t\tD532322F2142AE3D008DE511 /* FEFluidFSIDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231E62142AE3D008DE511 /* FEFluidFSIDomain3D.h */; };\n\t\tD53232302142AE3D008DE511 /* FEFluidFSIDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231E72142AE3D008DE511 /* FEFluidFSIDomainFactory.cpp */; };\n\t\tD53232312142AE3D008DE511 /* FEFluidResidualVector.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231E82142AE3D008DE511 /* FEFluidResidualVector.h */; };\n\t\tD53232322142AE3D008DE511 /* FEBioFluidPlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231E92142AE3D008DE511 /* FEBioFluidPlot.cpp */; };\n\t\tD53232332142AE3D008DE511 /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231EA2142AE3D008DE511 /* stdafx.h */; };\n\t\tD53232342142AE3D008DE511 /* FEFluidRotationalVelocity.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231EB2142AE3D008DE511 /* FEFluidRotationalVelocity.h */; };\n\t\tD53232352142AE3D008DE511 /* FEConstraintNormalFlow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231EC2142AE3D008DE511 /* FEConstraintNormalFlow.cpp */; };\n\t\tD53232362142AE3D008DE511 /* FEFluidVelocity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231ED2142AE3D008DE511 /* FEFluidVelocity.cpp */; };\n\t\tD53232372142AE3D008DE511 /* FEConstraintNormalFlow.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231EE2142AE3D008DE511 /* FEConstraintNormalFlow.h */; };\n\t\tD53232382142AE3D008DE511 /* FEFluidDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231EF2142AE3D008DE511 /* FEFluidDomain3D.cpp */; };\n\t\tD53232392142AE3D008DE511 /* FEFluidFSI.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231F02142AE3D008DE511 /* FEFluidFSI.h */; };\n\t\tD532323A2142AE3D008DE511 /* FEFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231F12142AE3D008DE511 /* FEFluid.h */; };\n\t\tD532323B2142AE3D008DE511 /* FEFluidFSIDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231F22142AE3D008DE511 /* FEFluidFSIDomainFactory.h */; };\n\t\tD532323C2142AE3D008DE511 /* FECarreauYasudaFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231F32142AE3D008DE511 /* FECarreauYasudaFluid.h */; };\n\t\tD532323D2142AE3D008DE511 /* FEPowellEyringFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231F42142AE3D008DE511 /* FEPowellEyringFluid.h */; };\n\t\tD532323E2142AE3D008DE511 /* FEIdealGasIsentropic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231F52142AE3D008DE511 /* FEIdealGasIsentropic.cpp */; };\n\t\tD532323F2142AE3D008DE511 /* FEFluidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231F62142AE3D008DE511 /* FEFluidDomain.h */; };\n\t\tD53232402142AE3D008DE511 /* FEFluidDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231F72142AE3D008DE511 /* FEFluidDomainFactory.cpp */; };\n\t\tD53232412142AE3D008DE511 /* FEBackFlowStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231F82142AE3D008DE511 /* FEBackFlowStabilization.cpp */; };\n\t\tD53232422142AE3D008DE511 /* FEBioFluidData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231F92142AE3D008DE511 /* FEBioFluidData.cpp */; };\n\t\tD53232432142AE3D008DE511 /* FEFluidFSIDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231FA2142AE3D008DE511 /* FEFluidFSIDomain.cpp */; };\n\t\tD53232442142AE3D008DE511 /* FEConstraintFrictionlessWall.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231FB2142AE3D008DE511 /* FEConstraintFrictionlessWall.h */; };\n\t\tD53232452142AE3D008DE511 /* FETangentialFlowStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231FC2142AE3D008DE511 /* FETangentialFlowStabilization.cpp */; };\n\t\tD53232462142AE3D008DE511 /* FEFluidFSISolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231FD2142AE3D008DE511 /* FEFluidFSISolver.h */; };\n\t\tD53232472142AE3D008DE511 /* FEViscousFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231FE2142AE3D008DE511 /* FEViscousFluid.cpp */; };\n\t\tD53232482142AE3D008DE511 /* FEFluidNormalTraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231FF2142AE3D008DE511 /* FEFluidNormalTraction.cpp */; };\n\t\tD53232492142AE3D008DE511 /* FEFluidSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232002142AE3D008DE511 /* FEFluidSolver.h */; };\n\t\tD532324A2142AE3D008DE511 /* FEFluidDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232012142AE3D008DE511 /* FEFluidDomainFactory.h */; };\n\t\tD532324B2142AE3D008DE511 /* FENewtonianFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232022142AE3D008DE511 /* FENewtonianFluid.cpp */; };\n\t\tD532324C2142AE3D008DE511 /* FEBackFlowStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232032142AE3D008DE511 /* FEBackFlowStabilization.h */; };\n\t\tD532324D2142AE3D008DE511 /* FEFluidRotationalVelocity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232042142AE3D008DE511 /* FEFluidRotationalVelocity.cpp */; };\n\t\tD532324E2142AE3D008DE511 /* FEFluidFSIDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232052142AE3D008DE511 /* FEFluidFSIDomain.h */; };\n\t\tD532324F2142AE3D008DE511 /* FECarreauFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232062142AE3D008DE511 /* FECarreauFluid.h */; };\n\t\tD53232502142AE3D008DE511 /* FEFluidResidualVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232072142AE3D008DE511 /* FEFluidResidualVector.cpp */; };\n\t\tD53232512142AE3D008DE511 /* FEFluidFSISolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232082142AE3D008DE511 /* FEFluidFSISolver.cpp */; };\n\t\tD53232522142AE3D008DE511 /* FEFluidNormalVelocity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232092142AE3D008DE511 /* FEFluidNormalVelocity.cpp */; };\n\t\tD53232532142AE3D008DE511 /* FETiedFluidInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D532320A2142AE3D008DE511 /* FETiedFluidInterface.h */; };\n\t\tD53232542142AE3D008DE511 /* FEBioFluidPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = D532320B2142AE3D008DE511 /* FEBioFluidPlot.h */; };\n\t\tD53232552142AE3D008DE511 /* FETangentialFlowStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = D532320C2142AE3D008DE511 /* FETangentialFlowStabilization.h */; };\n\t\tD53232562142AE3D008DE511 /* FECrossFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D532320D2142AE3D008DE511 /* FECrossFluid.h */; };\n\t\tD53232572142AE3D008DE511 /* FEFluidFSIDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D532320E2142AE3D008DE511 /* FEFluidFSIDomain3D.cpp */; };\n\t\tD53232582142AE3D008DE511 /* FEFluidDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = D532320F2142AE3D008DE511 /* FEFluidDomain3D.h */; };\n\t\tD53232592142AE3E008DE511 /* FEBioFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232102142AE3D008DE511 /* FEBioFluid.cpp */; };\n\t\tD532325A2142AE3E008DE511 /* FEFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232112142AE3D008DE511 /* FEFluid.cpp */; };\n\t\tD532325B2142AE3E008DE511 /* FEFluidFSITraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232122142AE3D008DE511 /* FEFluidFSITraction.cpp */; };\n\t\tD532325C2142AE3E008DE511 /* FEFluidDomain2D.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232132142AE3D008DE511 /* FEFluidDomain2D.h */; };\n\t\tD532325D2142AE3E008DE511 /* FEFluidNormalTraction.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232142142AE3D008DE511 /* FEFluidNormalTraction.h */; };\n\t\tD532325E2142AE3E008DE511 /* FEBioFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232152142AE3D008DE511 /* FEBioFluid.h */; };\n\t\tD532325F2142AE3E008DE511 /* FETangentialDamping.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232162142AE3D008DE511 /* FETangentialDamping.h */; };\n\t\tD53232602142AE3E008DE511 /* FEFluidResistanceBC.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232172142AE3D008DE511 /* FEFluidResistanceBC.h */; };\n\t\tD53232612142AE3E008DE511 /* FETangentialDamping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232182142AE3D008DE511 /* FETangentialDamping.cpp */; };\n\t\tD53232622142AE3E008DE511 /* FECarreauFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232192142AE3D008DE511 /* FECarreauFluid.cpp */; };\n\t\tD53232632142AE3E008DE511 /* FEBioFSI.h in Headers */ = {isa = PBXBuildFile; fileRef = D532321A2142AE3D008DE511 /* FEBioFSI.h */; };\n\t\tD53232642142AE3E008DE511 /* FEViscousFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D532321B2142AE3D008DE511 /* FEViscousFluid.h */; };\n\t\tD53232652142AE3E008DE511 /* FEFluidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D532321C2142AE3D008DE511 /* FEFluidDomain.cpp */; };\n\t\tD53232662142AE3E008DE511 /* FEFluidNormalVelocity.h in Headers */ = {isa = PBXBuildFile; fileRef = D532321D2142AE3D008DE511 /* FEFluidNormalVelocity.h */; };\n\t\tD53232672142AE3E008DE511 /* FECrossFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D532321E2142AE3D008DE511 /* FECrossFluid.cpp */; };\n\t\tD53232682142AE3E008DE511 /* FEIdealGasIsentropic.h in Headers */ = {isa = PBXBuildFile; fileRef = D532321F2142AE3D008DE511 /* FEIdealGasIsentropic.h */; };\n\t\tD53232692142AE3E008DE511 /* FEPowellEyringFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232202142AE3D008DE511 /* FEPowellEyringFluid.cpp */; };\n\t\tD532326A2142AE3E008DE511 /* FEBioFluidData.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232212142AE3D008DE511 /* FEBioFluidData.h */; };\n\t\tD532326B2142AE3E008DE511 /* FEBioFSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232222142AE3D008DE511 /* FEBioFSI.cpp */; };\n\t\tD532326C2142AE3E008DE511 /* FETiedFluidInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232232142AE3D008DE511 /* FETiedFluidInterface.cpp */; };\n\t\tD532326D2142AE3E008DE511 /* FEFluidVelocity.h in Headers */ = {isa = PBXBuildFile; fileRef = D53232242142AE3D008DE511 /* FEFluidVelocity.h */; };\n\t\tD532326E2142AE3E008DE511 /* FEFluidSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53232252142AE3D008DE511 /* FEFluidSolver.cpp */; };\n\t\tD538E19F221843AD0043A6DA /* FEFluidPResistanceBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D538E19D221843AD0043A6DA /* FEFluidPResistanceBC.cpp */; };\n\t\tD538E1A0221843AD0043A6DA /* FEFluidPResistanceBC.h in Headers */ = {isa = PBXBuildFile; fileRef = D538E19E221843AD0043A6DA /* FEFluidPResistanceBC.h */; };\n\t\tD577565F220484E20022916B /* FEFluidP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D577565D220484E20022916B /* FEFluidP.cpp */; };\n\t\tD5775660220484E20022916B /* FEFluidP.h in Headers */ = {isa = PBXBuildFile; fileRef = D577565E220484E20022916B /* FEFluidP.h */; };\n\t\tD57A0AF92346311600B7C0B8 /* FEFluidSolutesDomain2.h in Headers */ = {isa = PBXBuildFile; fileRef = D57A0AF32346311600B7C0B8 /* FEFluidSolutesDomain2.h */; };\n\t\tD57A0AFA2346311600B7C0B8 /* FEFluidSolutesSolver2.h in Headers */ = {isa = PBXBuildFile; fileRef = D57A0AF42346311600B7C0B8 /* FEFluidSolutesSolver2.h */; };\n\t\tD57A0AFB2346311600B7C0B8 /* FEFluidSolutesSolver2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57A0AF52346311600B7C0B8 /* FEFluidSolutesSolver2.cpp */; };\n\t\tD57A0AFC2346311600B7C0B8 /* FEFluidSolutesMaterial2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57A0AF62346311600B7C0B8 /* FEFluidSolutesMaterial2.cpp */; };\n\t\tD57A0AFD2346311600B7C0B8 /* FEFluidSolutesDomain2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57A0AF72346311600B7C0B8 /* FEFluidSolutesDomain2.cpp */; };\n\t\tD57A0AFE2346311600B7C0B8 /* FEFluidSolutesMaterial2.h in Headers */ = {isa = PBXBuildFile; fileRef = D57A0AF82346311600B7C0B8 /* FEFluidSolutesMaterial2.h */; };\n\t\tD57C2A4B240863B60043C2A7 /* FEFluidThermalConductivity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A49240863B60043C2A7 /* FEFluidThermalConductivity.cpp */; };\n\t\tD57C2A4C240863B60043C2A7 /* FEFluidThermalConductivity.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A4A240863B60043C2A7 /* FEFluidThermalConductivity.h */; };\n\t\tD57C2A5324087BE40043C2A7 /* FEThermoFluidDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A5124087BE40043C2A7 /* FEThermoFluidDomainFactory.cpp */; };\n\t\tD57C2A5424087BE40043C2A7 /* FEThermoFluidDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A5224087BE40043C2A7 /* FEThermoFluidDomainFactory.h */; };\n\t\tD57C2A57240882500043C2A7 /* FEFluidHeatSupply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A55240882500043C2A7 /* FEFluidHeatSupply.cpp */; };\n\t\tD57C2A58240882500043C2A7 /* FEFluidHeatSupply.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A56240882500043C2A7 /* FEFluidHeatSupply.h */; };\n\t\tD57C2A5B240889CA0043C2A7 /* FEFluidHeatSupplyConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A59240889CA0043C2A7 /* FEFluidHeatSupplyConst.cpp */; };\n\t\tD57C2A5C240889CA0043C2A7 /* FEFluidHeatSupplyConst.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A5A240889CA0043C2A7 /* FEFluidHeatSupplyConst.h */; };\n\t\tD57C2A5F240969AD0043C2A7 /* FEThermoFluidSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A5D240969AD0043C2A7 /* FEThermoFluidSolver.cpp */; };\n\t\tD57C2A60240969AD0043C2A7 /* FEThermoFluidSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A5E240969AD0043C2A7 /* FEThermoFluidSolver.h */; };\n\t\tD57C2A63240975A90043C2A7 /* FEFluidNormalHeatFlux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A61240975A90043C2A7 /* FEFluidNormalHeatFlux.cpp */; };\n\t\tD57C2A64240975A90043C2A7 /* FEFluidNormalHeatFlux.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A62240975A90043C2A7 /* FEFluidNormalHeatFlux.h */; };\n\t\tD57C2A67240984CC0043C2A7 /* FEIdealLiquid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A65240984CC0043C2A7 /* FEIdealLiquid.cpp */; };\n\t\tD57C2A68240984CC0043C2A7 /* FEIdealLiquid.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A66240984CC0043C2A7 /* FEIdealLiquid.h */; };\n\t\tD57C2A6B240996D60043C2A7 /* FEFluidConstantConductivity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A69240996D60043C2A7 /* FEFluidConstantConductivity.cpp */; };\n\t\tD57C2A6C240996D60043C2A7 /* FEFluidConstantConductivity.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A6A240996D60043C2A7 /* FEFluidConstantConductivity.h */; };\n\t\tD57C2A6F240EFF610043C2A7 /* FEFluidMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57C2A6D240EFF610043C2A7 /* FEFluidMaterial.cpp */; };\n\t\tD57C2A70240EFF610043C2A7 /* FEFluidMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D57C2A6E240EFF610043C2A7 /* FEFluidMaterial.h */; };\n\t\tD58A40B522BA870F009AA77E /* FEFluidStressCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58A40B322BA870E009AA77E /* FEFluidStressCriterion.cpp */; };\n\t\tD58A40B622BA870F009AA77E /* FEFluidStressCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D58A40B422BA870E009AA77E /* FEFluidStressCriterion.h */; };\n\t\tD58DDDFC23233A8E007A490A /* FESolutesDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D58DDDF423233A8D007A490A /* FESolutesDomain.h */; };\n\t\tD58DDDFD23233A8E007A490A /* FESolutesDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D58DDDF523233A8D007A490A /* FESolutesDomainFactory.h */; };\n\t\tD58DDDFE23233A8E007A490A /* FESolutesSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D58DDDF623233A8D007A490A /* FESolutesSolver.h */; };\n\t\tD58DDDFF23233A8E007A490A /* FESolutesMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D58DDDF723233A8D007A490A /* FESolutesMaterial.h */; };\n\t\tD58DDE0023233A8E007A490A /* FESolutesDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58DDDF823233A8D007A490A /* FESolutesDomain.cpp */; };\n\t\tD58DDE0123233A8E007A490A /* FESolutesMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58DDDF923233A8E007A490A /* FESolutesMaterial.cpp */; };\n\t\tD58DDE0223233A8E007A490A /* FESolutesSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58DDDFA23233A8E007A490A /* FESolutesSolver.cpp */; };\n\t\tD58DDE0323233A8E007A490A /* FESolutesDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58DDDFB23233A8E007A490A /* FESolutesDomainFactory.cpp */; };\n\t\tD59368F926755C3700AF7D0A /* FEBinghamFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D59368F726755C3700AF7D0A /* FEBinghamFluid.cpp */; };\n\t\tD59368FA26755C3700AF7D0A /* FEBinghamFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D59368F826755C3700AF7D0A /* FEBinghamFluid.h */; };\n\t\tD5AB624322028A3900AAF141 /* FEFluidPDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AB624122028A3900AAF141 /* FEFluidPDomain3D.cpp */; };\n\t\tD5AB624422028A3900AAF141 /* FEFluidPDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AB624222028A3900AAF141 /* FEFluidPDomain3D.h */; };\n\t\tD5AB62472202A52C00AAF141 /* FEBioFluidP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AB62452202A52C00AAF141 /* FEBioFluidP.cpp */; };\n\t\tD5AB62482202A52C00AAF141 /* FEBioFluidP.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AB62462202A52C00AAF141 /* FEBioFluidP.h */; };\n\t\tD5AB624B2202A63800AAF141 /* FEFluidPDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AB62492202A63800AAF141 /* FEFluidPDomainFactory.cpp */; };\n\t\tD5AB624C2202A63800AAF141 /* FEFluidPDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AB624A2202A63800AAF141 /* FEFluidPDomainFactory.h */; };\n\t\tD5B805D3223C2E1200198805 /* FEFSIErosionVolumeRatio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B805D1223C2E1200198805 /* FEFSIErosionVolumeRatio.cpp */; };\n\t\tD5B805D4223C2E1200198805 /* FEFSIErosionVolumeRatio.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B805D2223C2E1200198805 /* FEFSIErosionVolumeRatio.h */; };\n\t\tD5C13EE3240479F9005897E1 /* FEThermoFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EE1240479F9005897E1 /* FEThermoFluid.cpp */; };\n\t\tD5C13EE4240479F9005897E1 /* FEThermoFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EE2240479F9005897E1 /* FEThermoFluid.h */; };\n\t\tD5C13EE7240484FD005897E1 /* FEFluidMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EE5240484FD005897E1 /* FEFluidMaterialPoint.cpp */; };\n\t\tD5C13EE8240484FD005897E1 /* FEFluidMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EE6240484FD005897E1 /* FEFluidMaterialPoint.h */; };\n\t\tD5C13EEB240486FF005897E1 /* FEThermoFluidMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EE9240486FF005897E1 /* FEThermoFluidMaterialPoint.cpp */; };\n\t\tD5C13EEC240486FF005897E1 /* FEThermoFluidMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EEA240486FF005897E1 /* FEThermoFluidMaterialPoint.h */; };\n\t\tD5C13EEF24048BBC005897E1 /* FEElasticFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EED24048BBC005897E1 /* FEElasticFluid.cpp */; };\n\t\tD5C13EF024048BBC005897E1 /* FEElasticFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EEE24048BBC005897E1 /* FEElasticFluid.h */; };\n\t\tD5C13EF32405D419005897E1 /* FEIdealGas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EF12405D419005897E1 /* FEIdealGas.cpp */; };\n\t\tD5C13EF42405D419005897E1 /* FEIdealGas.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EF22405D419005897E1 /* FEIdealGas.h */; };\n\t\tD5C13EF7240711D3005897E1 /* FEThermoFluidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EF5240711D3005897E1 /* FEThermoFluidDomain.cpp */; };\n\t\tD5C13EF8240711D3005897E1 /* FEThermoFluidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EF6240711D3005897E1 /* FEThermoFluidDomain.h */; };\n\t\tD5C13EFB2407124C005897E1 /* FEThermoFluidDomain3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EF92407124C005897E1 /* FEThermoFluidDomain3D.cpp */; };\n\t\tD5C13EFC2407124C005897E1 /* FEThermoFluidDomain3D.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EFA2407124C005897E1 /* FEThermoFluidDomain3D.h */; };\n\t\tD5C13EFF240714A4005897E1 /* FEBioThermoFluid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13EFD240714A4005897E1 /* FEBioThermoFluid.cpp */; };\n\t\tD5C13F00240714A4005897E1 /* FEBioThermoFluid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13EFE240714A4005897E1 /* FEBioThermoFluid.h */; };\n\t\tD5D65A8B222B109600F95841 /* FEIdealGasIsothermal.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D65A89222B109600F95841 /* FEIdealGasIsothermal.h */; };\n\t\tD5D65A8C222B109600F95841 /* FEIdealGasIsothermal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D65A8A222B109600F95841 /* FEIdealGasIsothermal.cpp */; };\n\t\tD5E83002262A467400B23605 /* FEFluidPressureLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E83000262A467300B23605 /* FEFluidPressureLoad.cpp */; };\n\t\tD5E83003262A467400B23605 /* FEFluidPressureLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E83001262A467300B23605 /* FEFluidPressureLoad.h */; };\n\t\tD5E85D902200F89800F5DF83 /* febiofluid_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E85D8F2200F89800F5DF83 /* febiofluid_api.h */; };\n\t\tD5EBDC7824AE9A6E0007B220 /* FEBackFlowFSIStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5EBDC7624AE9A6E0007B220 /* FEBackFlowFSIStabilization.cpp */; };\n\t\tD5EBDC7924AE9A6E0007B220 /* FEBackFlowFSIStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = D5EBDC7724AE9A6E0007B220 /* FEBackFlowFSIStabilization.h */; };\n\t\tD5EBDC7C24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5EBDC7A24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.cpp */; };\n\t\tD5EBDC7D24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = D5EBDC7B24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.h */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t690FCC6A25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSISoluteBackflowStabilization.cpp; sourceTree = \"<group>\"; };\n\t\t690FCC6B25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSISoluteBackflowStabilization.h; sourceTree = \"<group>\"; };\n\t\t690FCC6E25CDB9BE000A6204 /* FEMultiphasicFSIPressure.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSIPressure.cpp; sourceTree = \"<group>\"; };\n\t\t690FCC6F25CDB9BE000A6204 /* FEMultiphasicFSIPressure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSIPressure.h; sourceTree = \"<group>\"; };\n\t\t691BD2F42412B182002BC4A1 /* FEFluidMixtureTractionLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidMixtureTractionLoad.cpp; sourceTree = \"<group>\"; };\n\t\t691BD2F82412B183002BC4A1 /* FEBiphasicFSIDomain3D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicFSIDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\t691BD2F92412B184002BC4A1 /* FEBiphasicFSIDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicFSIDomain.h; sourceTree = \"<group>\"; };\n\t\t691BD2FF2412B185002BC4A1 /* FEBiphasicFSI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicFSI.cpp; sourceTree = \"<group>\"; };\n\t\t691BD3002412B185002BC4A1 /* FEBiphasicFSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicFSI.h; sourceTree = \"<group>\"; };\n\t\t691BD3032412B186002BC4A1 /* FEFluidMixtureTractionLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidMixtureTractionLoad.h; sourceTree = \"<group>\"; };\n\t\t691BD3082412B187002BC4A1 /* FEBiphasicFSIDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicFSIDomain.cpp; sourceTree = \"<group>\"; };\n\t\t691BD30A2412B187002BC4A1 /* FEBiphasicFSIDomain3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicFSIDomain3D.h; sourceTree = \"<group>\"; };\n\t\t693FEF6425645FC3004F9ACF /* FEFluidSolutesNaturalFlux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesNaturalFlux.cpp; sourceTree = \"<group>\"; };\n\t\t693FEF6525645FC3004F9ACF /* FEFluidSolutesNaturalFlux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesNaturalFlux.h; sourceTree = \"<group>\"; };\n\t\t694948D82476D5220031BD24 /* FEThermoFluidPressureLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluidPressureLoad.cpp; sourceTree = \"<group>\"; };\n\t\t694948D92476D5220031BD24 /* FETemperatureBackFlowStabilization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETemperatureBackFlowStabilization.h; sourceTree = \"<group>\"; };\n\t\t694948DA2476D5220031BD24 /* FEThermoFluidPressureLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEThermoFluidPressureLoad.h; sourceTree = \"<group>\"; };\n\t\t694948DB2476D5220031BD24 /* FETemperatureBackFlowStabilization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETemperatureBackFlowStabilization.cpp; sourceTree = \"<group>\"; };\n\t\t6957D5B7247D6C4B008522FA /* FEFluidRCBC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidRCBC.cpp; sourceTree = \"<group>\"; };\n\t\t6957D5B8247D6C4C008522FA /* FEFluidRCBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidRCBC.h; sourceTree = \"<group>\"; };\n\t\t695C594C25BF5BA9006B53DE /* FEFluidSolutesFlux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesFlux.cpp; sourceTree = \"<group>\"; };\n\t\t695C594D25BF5BA9006B53DE /* FEFluidSolutesFlux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesFlux.h; sourceTree = \"<group>\"; };\n\t\t695C595025C31E9F006B53DE /* FEBioMultiphasicFSI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMultiphasicFSI.cpp; sourceTree = \"<group>\"; };\n\t\t695C595125C31E9F006B53DE /* FEBioMultiphasicFSI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBioMultiphasicFSI.h; sourceTree = \"<group>\"; };\n\t\t695C595425C32277006B53DE /* FEMultiphasicFSI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSI.cpp; sourceTree = \"<group>\"; };\n\t\t695C595525C32277006B53DE /* FEMultiphasicFSI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSI.h; sourceTree = \"<group>\"; };\n\t\t695C595825C322B7006B53DE /* FEMultiphasicFSIDomain.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSIDomain.cpp; sourceTree = \"<group>\"; };\n\t\t695C595925C322B7006B53DE /* FEMultiphasicFSIDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSIDomain.h; sourceTree = \"<group>\"; };\n\t\t695C595C25C322F7006B53DE /* FEMultiphasicFSIDomain3D.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSIDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\t695C595D25C322F7006B53DE /* FEMultiphasicFSIDomain3D.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSIDomain3D.h; sourceTree = \"<group>\"; };\n\t\t695C596025C3230F006B53DE /* FEMultiphasicFSIDomainFactory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSIDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\t695C596125C3230F006B53DE /* FEMultiphasicFSIDomainFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSIDomainFactory.h; sourceTree = \"<group>\"; };\n\t\t695C596425C32326006B53DE /* FEMultiphasicFSISolver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSISolver.cpp; sourceTree = \"<group>\"; };\n\t\t695C596525C32326006B53DE /* FEMultiphasicFSISolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSISolver.h; sourceTree = \"<group>\"; };\n\t\t695C596C25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicFSISoluteFlux.cpp; sourceTree = \"<group>\"; };\n\t\t695C596D25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicFSISoluteFlux.h; sourceTree = \"<group>\"; };\n\t\t696F85EB2432A02900890DD2 /* FEFluidRCRBC.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidRCRBC.cpp; sourceTree = \"<group>\"; };\n\t\t696F85EC2432A02900890DD2 /* FEFluidRCRBC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidRCRBC.h; sourceTree = \"<group>\"; };\n\t\t698D57F42582BD66004CFD27 /* FEFluidSolutesPressure.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesPressure.cpp; sourceTree = \"<group>\"; };\n\t\t698D57F52582BD66004CFD27 /* FEFluidSolutesPressure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesPressure.h; sourceTree = \"<group>\"; };\n\t\t69BDB97B25A519720096F5D0 /* FEBiphasicFSITraction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicFSITraction.cpp; sourceTree = \"<group>\"; };\n\t\t69BDB97C25A519720096F5D0 /* FEBiphasicFSITraction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBiphasicFSITraction.h; sourceTree = \"<group>\"; };\n\t\tD5027074269F48D2007B16A3 /* FELinearElasticFluid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FELinearElasticFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD5027075269F48D2007B16A3 /* FELinearElasticFluid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FELinearElasticFluid.h; sourceTree = \"<group>\"; };\n\t\tD5027078269F5092007B16A3 /* FENonlinearElasticFluid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FENonlinearElasticFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD5027079269F5092007B16A3 /* FENonlinearElasticFluid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FENonlinearElasticFluid.h; sourceTree = \"<group>\"; };\n\t\tD50462BA22EF676700CCA668 /* FEBioFluidSolutes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioFluidSolutes.cpp; sourceTree = \"<group>\"; };\n\t\tD50462BB22EF676700CCA668 /* FEBioFluidSolutes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBioFluidSolutes.h; sourceTree = \"<group>\"; };\n\t\tD50462BE22EF693600CCA668 /* FEFluidSolutesSolver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD50462BF22EF693600CCA668 /* FEFluidSolutesSolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesSolver.h; sourceTree = \"<group>\"; };\n\t\tD50462C222EF6A4D00CCA668 /* FEFluidSolutesDomain3D.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\tD50462C322EF6A4D00CCA668 /* FEFluidSolutesDomain3D.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesDomain3D.h; sourceTree = \"<group>\"; };\n\t\tD50462C622EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD50462C722EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD50462CA22EF6BB800CCA668 /* FEFluidSolutes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutes.cpp; sourceTree = \"<group>\"; };\n\t\tD50462CB22EF6BB800CCA668 /* FEFluidSolutes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutes.h; sourceTree = \"<group>\"; };\n\t\tD509D27D22F32408007DFC44 /* FESoluteBackflowStabilization.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FESoluteBackflowStabilization.cpp; sourceTree = \"<group>\"; };\n\t\tD509D27E22F32408007DFC44 /* FESoluteBackflowStabilization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FESoluteBackflowStabilization.h; sourceTree = \"<group>\"; };\n\t\tD509D28122F4CABC007DFC44 /* FESoluteConvectiveFlow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FESoluteConvectiveFlow.cpp; sourceTree = \"<group>\"; };\n\t\tD509D28222F4CABC007DFC44 /* FESoluteConvectiveFlow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FESoluteConvectiveFlow.h; sourceTree = \"<group>\"; };\n\t\tD511EF0D253652CD00893F69 /* FERealLiquid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FERealLiquid.cpp; sourceTree = \"<group>\"; };\n\t\tD511EF0E253652CD00893F69 /* FERealLiquid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FERealLiquid.h; sourceTree = \"<group>\"; };\n\t\tD511EF1C253B782C00893F69 /* FERealGas.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FERealGas.cpp; sourceTree = \"<group>\"; };\n\t\tD511EF1D253B782C00893F69 /* FERealGas.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FERealGas.h; sourceTree = \"<group>\"; };\n\t\tD511EF20253C7C5800893F69 /* FETempDependentConductivity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FETempDependentConductivity.cpp; sourceTree = \"<group>\"; };\n\t\tD511EF21253C7C5800893F69 /* FETempDependentConductivity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FETempDependentConductivity.h; sourceTree = \"<group>\"; };\n\t\tD53231CE2142AE2D008DE511 /* libFEBioFluid.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioFluid.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53231DD2142AE3D008DE511 /* FEFluidResistanceBC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidResistanceBC.cpp; sourceTree = \"<group>\"; };\n\t\tD53231DE2142AE3D008DE511 /* FEConstraintFrictionlessWall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEConstraintFrictionlessWall.cpp; sourceTree = \"<group>\"; };\n\t\tD53231DF2142AE3D008DE511 /* FEFluidFSITraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSITraction.h; sourceTree = \"<group>\"; };\n\t\tD53231E02142AE3D008DE511 /* FEFluidTractionLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidTractionLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD53231E12142AE3D008DE511 /* FEFluidFSI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSI.cpp; sourceTree = \"<group>\"; };\n\t\tD53231E22142AE3D008DE511 /* FECarreauYasudaFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECarreauYasudaFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD53231E32142AE3D008DE511 /* FENewtonianFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENewtonianFluid.h; sourceTree = \"<group>\"; };\n\t\tD53231E42142AE3D008DE511 /* FEFluidDomain2D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidDomain2D.cpp; sourceTree = \"<group>\"; };\n\t\tD53231E52142AE3D008DE511 /* FEFluidTractionLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidTractionLoad.h; sourceTree = \"<group>\"; };\n\t\tD53231E62142AE3D008DE511 /* FEFluidFSIDomain3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSIDomain3D.h; sourceTree = \"<group>\"; };\n\t\tD53231E72142AE3D008DE511 /* FEFluidFSIDomainFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSIDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD53231E82142AE3D008DE511 /* FEFluidResidualVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidResidualVector.h; sourceTree = \"<group>\"; };\n\t\tD53231E92142AE3D008DE511 /* FEBioFluidPlot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioFluidPlot.cpp; sourceTree = \"<group>\"; };\n\t\tD53231EA2142AE3D008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD53231EB2142AE3D008DE511 /* FEFluidRotationalVelocity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidRotationalVelocity.h; sourceTree = \"<group>\"; };\n\t\tD53231EC2142AE3D008DE511 /* FEConstraintNormalFlow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEConstraintNormalFlow.cpp; sourceTree = \"<group>\"; };\n\t\tD53231ED2142AE3D008DE511 /* FEFluidVelocity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidVelocity.cpp; sourceTree = \"<group>\"; };\n\t\tD53231EE2142AE3D008DE511 /* FEConstraintNormalFlow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConstraintNormalFlow.h; sourceTree = \"<group>\"; };\n\t\tD53231EF2142AE3D008DE511 /* FEFluidDomain3D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\tD53231F02142AE3D008DE511 /* FEFluidFSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSI.h; sourceTree = \"<group>\"; };\n\t\tD53231F12142AE3D008DE511 /* FEFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluid.h; sourceTree = \"<group>\"; };\n\t\tD53231F22142AE3D008DE511 /* FEFluidFSIDomainFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSIDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD53231F32142AE3D008DE511 /* FECarreauYasudaFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECarreauYasudaFluid.h; sourceTree = \"<group>\"; };\n\t\tD53231F42142AE3D008DE511 /* FEPowellEyringFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPowellEyringFluid.h; sourceTree = \"<group>\"; };\n\t\tD53231F52142AE3D008DE511 /* FEIdealGasIsentropic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEIdealGasIsentropic.cpp; sourceTree = \"<group>\"; };\n\t\tD53231F62142AE3D008DE511 /* FEFluidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidDomain.h; sourceTree = \"<group>\"; };\n\t\tD53231F72142AE3D008DE511 /* FEFluidDomainFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD53231F82142AE3D008DE511 /* FEBackFlowStabilization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBackFlowStabilization.cpp; sourceTree = \"<group>\"; };\n\t\tD53231F92142AE3D008DE511 /* FEBioFluidData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioFluidData.cpp; sourceTree = \"<group>\"; };\n\t\tD53231FA2142AE3D008DE511 /* FEFluidFSIDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSIDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD53231FB2142AE3D008DE511 /* FEConstraintFrictionlessWall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConstraintFrictionlessWall.h; sourceTree = \"<group>\"; };\n\t\tD53231FC2142AE3D008DE511 /* FETangentialFlowStabilization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETangentialFlowStabilization.cpp; sourceTree = \"<group>\"; };\n\t\tD53231FD2142AE3D008DE511 /* FEFluidFSISolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSISolver.h; sourceTree = \"<group>\"; };\n\t\tD53231FE2142AE3D008DE511 /* FEViscousFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEViscousFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD53231FF2142AE3D008DE511 /* FEFluidNormalTraction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidNormalTraction.cpp; sourceTree = \"<group>\"; };\n\t\tD53232002142AE3D008DE511 /* FEFluidSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidSolver.h; sourceTree = \"<group>\"; };\n\t\tD53232012142AE3D008DE511 /* FEFluidDomainFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD53232022142AE3D008DE511 /* FENewtonianFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENewtonianFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD53232032142AE3D008DE511 /* FEBackFlowStabilization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBackFlowStabilization.h; sourceTree = \"<group>\"; };\n\t\tD53232042142AE3D008DE511 /* FEFluidRotationalVelocity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidRotationalVelocity.cpp; sourceTree = \"<group>\"; };\n\t\tD53232052142AE3D008DE511 /* FEFluidFSIDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSIDomain.h; sourceTree = \"<group>\"; };\n\t\tD53232062142AE3D008DE511 /* FECarreauFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECarreauFluid.h; sourceTree = \"<group>\"; };\n\t\tD53232072142AE3D008DE511 /* FEFluidResidualVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidResidualVector.cpp; sourceTree = \"<group>\"; };\n\t\tD53232082142AE3D008DE511 /* FEFluidFSISolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSISolver.cpp; sourceTree = \"<group>\"; };\n\t\tD53232092142AE3D008DE511 /* FEFluidNormalVelocity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidNormalVelocity.cpp; sourceTree = \"<group>\"; };\n\t\tD532320A2142AE3D008DE511 /* FETiedFluidInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedFluidInterface.h; sourceTree = \"<group>\"; };\n\t\tD532320B2142AE3D008DE511 /* FEBioFluidPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioFluidPlot.h; sourceTree = \"<group>\"; };\n\t\tD532320C2142AE3D008DE511 /* FETangentialFlowStabilization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETangentialFlowStabilization.h; sourceTree = \"<group>\"; };\n\t\tD532320D2142AE3D008DE511 /* FECrossFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECrossFluid.h; sourceTree = \"<group>\"; };\n\t\tD532320E2142AE3D008DE511 /* FEFluidFSIDomain3D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSIDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\tD532320F2142AE3D008DE511 /* FEFluidDomain3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidDomain3D.h; sourceTree = \"<group>\"; };\n\t\tD53232102142AE3D008DE511 /* FEBioFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD53232112142AE3D008DE511 /* FEFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD53232122142AE3D008DE511 /* FEFluidFSITraction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSITraction.cpp; sourceTree = \"<group>\"; };\n\t\tD53232132142AE3D008DE511 /* FEFluidDomain2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidDomain2D.h; sourceTree = \"<group>\"; };\n\t\tD53232142142AE3D008DE511 /* FEFluidNormalTraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidNormalTraction.h; sourceTree = \"<group>\"; };\n\t\tD53232152142AE3D008DE511 /* FEBioFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioFluid.h; sourceTree = \"<group>\"; };\n\t\tD53232162142AE3D008DE511 /* FETangentialDamping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETangentialDamping.h; sourceTree = \"<group>\"; };\n\t\tD53232172142AE3D008DE511 /* FEFluidResistanceBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidResistanceBC.h; sourceTree = \"<group>\"; };\n\t\tD53232182142AE3D008DE511 /* FETangentialDamping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETangentialDamping.cpp; sourceTree = \"<group>\"; };\n\t\tD53232192142AE3D008DE511 /* FECarreauFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECarreauFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD532321A2142AE3D008DE511 /* FEBioFSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioFSI.h; sourceTree = \"<group>\"; };\n\t\tD532321B2142AE3D008DE511 /* FEViscousFluid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEViscousFluid.h; sourceTree = \"<group>\"; };\n\t\tD532321C2142AE3D008DE511 /* FEFluidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD532321D2142AE3D008DE511 /* FEFluidNormalVelocity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidNormalVelocity.h; sourceTree = \"<group>\"; };\n\t\tD532321E2142AE3D008DE511 /* FECrossFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECrossFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD532321F2142AE3D008DE511 /* FEIdealGasIsentropic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEIdealGasIsentropic.h; sourceTree = \"<group>\"; };\n\t\tD53232202142AE3D008DE511 /* FEPowellEyringFluid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPowellEyringFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD53232212142AE3D008DE511 /* FEBioFluidData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioFluidData.h; sourceTree = \"<group>\"; };\n\t\tD53232222142AE3D008DE511 /* FEBioFSI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioFSI.cpp; sourceTree = \"<group>\"; };\n\t\tD53232232142AE3D008DE511 /* FETiedFluidInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedFluidInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD53232242142AE3D008DE511 /* FEFluidVelocity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidVelocity.h; sourceTree = \"<group>\"; };\n\t\tD53232252142AE3D008DE511 /* FEFluidSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD538E19D221843AD0043A6DA /* FEFluidPResistanceBC.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidPResistanceBC.cpp; sourceTree = \"<group>\"; };\n\t\tD538E19E221843AD0043A6DA /* FEFluidPResistanceBC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidPResistanceBC.h; sourceTree = \"<group>\"; };\n\t\tD577565D220484E20022916B /* FEFluidP.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidP.cpp; sourceTree = \"<group>\"; };\n\t\tD577565E220484E20022916B /* FEFluidP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidP.h; sourceTree = \"<group>\"; };\n\t\tD57A0AF32346311600B7C0B8 /* FEFluidSolutesDomain2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesDomain2.h; sourceTree = \"<group>\"; };\n\t\tD57A0AF42346311600B7C0B8 /* FEFluidSolutesSolver2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesSolver2.h; sourceTree = \"<group>\"; };\n\t\tD57A0AF52346311600B7C0B8 /* FEFluidSolutesSolver2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesSolver2.cpp; sourceTree = \"<group>\"; };\n\t\tD57A0AF62346311600B7C0B8 /* FEFluidSolutesMaterial2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesMaterial2.cpp; sourceTree = \"<group>\"; };\n\t\tD57A0AF72346311600B7C0B8 /* FEFluidSolutesDomain2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidSolutesDomain2.cpp; sourceTree = \"<group>\"; };\n\t\tD57A0AF82346311600B7C0B8 /* FEFluidSolutesMaterial2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidSolutesMaterial2.h; sourceTree = \"<group>\"; };\n\t\tD57C2A49240863B60043C2A7 /* FEFluidThermalConductivity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidThermalConductivity.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A4A240863B60043C2A7 /* FEFluidThermalConductivity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidThermalConductivity.h; sourceTree = \"<group>\"; };\n\t\tD57C2A5124087BE40043C2A7 /* FEThermoFluidDomainFactory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluidDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A5224087BE40043C2A7 /* FEThermoFluidDomainFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEThermoFluidDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD57C2A55240882500043C2A7 /* FEFluidHeatSupply.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidHeatSupply.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A56240882500043C2A7 /* FEFluidHeatSupply.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidHeatSupply.h; sourceTree = \"<group>\"; };\n\t\tD57C2A59240889CA0043C2A7 /* FEFluidHeatSupplyConst.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidHeatSupplyConst.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A5A240889CA0043C2A7 /* FEFluidHeatSupplyConst.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidHeatSupplyConst.h; sourceTree = \"<group>\"; };\n\t\tD57C2A5D240969AD0043C2A7 /* FEThermoFluidSolver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluidSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A5E240969AD0043C2A7 /* FEThermoFluidSolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEThermoFluidSolver.h; sourceTree = \"<group>\"; };\n\t\tD57C2A61240975A90043C2A7 /* FEFluidNormalHeatFlux.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidNormalHeatFlux.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A62240975A90043C2A7 /* FEFluidNormalHeatFlux.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidNormalHeatFlux.h; sourceTree = \"<group>\"; };\n\t\tD57C2A65240984CC0043C2A7 /* FEIdealLiquid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEIdealLiquid.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A66240984CC0043C2A7 /* FEIdealLiquid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEIdealLiquid.h; sourceTree = \"<group>\"; };\n\t\tD57C2A69240996D60043C2A7 /* FEFluidConstantConductivity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidConstantConductivity.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A6A240996D60043C2A7 /* FEFluidConstantConductivity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidConstantConductivity.h; sourceTree = \"<group>\"; };\n\t\tD57C2A6D240EFF610043C2A7 /* FEFluidMaterial.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD57C2A6E240EFF610043C2A7 /* FEFluidMaterial.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidMaterial.h; sourceTree = \"<group>\"; };\n\t\tD58A40B322BA870E009AA77E /* FEFluidStressCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidStressCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD58A40B422BA870E009AA77E /* FEFluidStressCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidStressCriterion.h; sourceTree = \"<group>\"; };\n\t\tD58DDDF423233A8D007A490A /* FESolutesDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolutesDomain.h; sourceTree = \"<group>\"; };\n\t\tD58DDDF523233A8D007A490A /* FESolutesDomainFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolutesDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD58DDDF623233A8D007A490A /* FESolutesSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolutesSolver.h; sourceTree = \"<group>\"; };\n\t\tD58DDDF723233A8D007A490A /* FESolutesMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolutesMaterial.h; sourceTree = \"<group>\"; };\n\t\tD58DDDF823233A8D007A490A /* FESolutesDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolutesDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD58DDDF923233A8E007A490A /* FESolutesMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolutesMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD58DDDFA23233A8E007A490A /* FESolutesSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolutesSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD58DDDFB23233A8E007A490A /* FESolutesDomainFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolutesDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD59368F726755C3700AF7D0A /* FEBinghamFluid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBinghamFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD59368F826755C3700AF7D0A /* FEBinghamFluid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBinghamFluid.h; sourceTree = \"<group>\"; };\n\t\tD5AB624122028A3900AAF141 /* FEFluidPDomain3D.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidPDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\tD5AB624222028A3900AAF141 /* FEFluidPDomain3D.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidPDomain3D.h; sourceTree = \"<group>\"; };\n\t\tD5AB62452202A52C00AAF141 /* FEBioFluidP.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioFluidP.cpp; sourceTree = \"<group>\"; };\n\t\tD5AB62462202A52C00AAF141 /* FEBioFluidP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBioFluidP.h; sourceTree = \"<group>\"; };\n\t\tD5AB62492202A63800AAF141 /* FEFluidPDomainFactory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidPDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD5AB624A2202A63800AAF141 /* FEFluidPDomainFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidPDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD5B805D1223C2E1200198805 /* FEFSIErosionVolumeRatio.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFSIErosionVolumeRatio.cpp; sourceTree = \"<group>\"; };\n\t\tD5B805D2223C2E1200198805 /* FEFSIErosionVolumeRatio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFSIErosionVolumeRatio.h; sourceTree = \"<group>\"; };\n\t\tD5C13EE1240479F9005897E1 /* FEThermoFluid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EE2240479F9005897E1 /* FEThermoFluid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEThermoFluid.h; sourceTree = \"<group>\"; };\n\t\tD5C13EE5240484FD005897E1 /* FEFluidMaterialPoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EE6240484FD005897E1 /* FEFluidMaterialPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5C13EE9240486FF005897E1 /* FEThermoFluidMaterialPoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluidMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EEA240486FF005897E1 /* FEThermoFluidMaterialPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEThermoFluidMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5C13EED24048BBC005897E1 /* FEElasticFluid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EEE24048BBC005897E1 /* FEElasticFluid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEElasticFluid.h; sourceTree = \"<group>\"; };\n\t\tD5C13EF12405D419005897E1 /* FEIdealGas.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEIdealGas.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EF22405D419005897E1 /* FEIdealGas.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEIdealGas.h; sourceTree = \"<group>\"; };\n\t\tD5C13EF5240711D3005897E1 /* FEThermoFluidDomain.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EF6240711D3005897E1 /* FEThermoFluidDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEThermoFluidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5C13EF92407124C005897E1 /* FEThermoFluidDomain3D.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEThermoFluidDomain3D.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EFA2407124C005897E1 /* FEThermoFluidDomain3D.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEThermoFluidDomain3D.h; sourceTree = \"<group>\"; };\n\t\tD5C13EFD240714A4005897E1 /* FEBioThermoFluid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioThermoFluid.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13EFE240714A4005897E1 /* FEBioThermoFluid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBioThermoFluid.h; sourceTree = \"<group>\"; };\n\t\tD5D65A89222B109600F95841 /* FEIdealGasIsothermal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEIdealGasIsothermal.h; sourceTree = \"<group>\"; };\n\t\tD5D65A8A222B109600F95841 /* FEIdealGasIsothermal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEIdealGasIsothermal.cpp; sourceTree = \"<group>\"; };\n\t\tD5E83000262A467300B23605 /* FEFluidPressureLoad.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidPressureLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5E83001262A467300B23605 /* FEFluidPressureLoad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFluidPressureLoad.h; sourceTree = \"<group>\"; };\n\t\tD5E85D8F2200F89800F5DF83 /* febiofluid_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febiofluid_api.h; sourceTree = \"<group>\"; };\n\t\tD5EBDC7624AE9A6E0007B220 /* FEBackFlowFSIStabilization.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEBackFlowFSIStabilization.cpp; sourceTree = \"<group>\"; };\n\t\tD5EBDC7724AE9A6E0007B220 /* FEBackFlowFSIStabilization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEBackFlowFSIStabilization.h; sourceTree = \"<group>\"; };\n\t\tD5EBDC7A24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FETangentialFlowFSIStabilization.cpp; sourceTree = \"<group>\"; };\n\t\tD5EBDC7B24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FETangentialFlowFSIStabilization.h; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD53231CB2142AE2D008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD53231C52142AE2D008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD53231DC2142AE3D008DE511 /* FEBioFluid */,\n\t\t\t\tD53231CF2142AE2D008DE511 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53231CF2142AE2D008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD53231CE2142AE2D008DE511 /* libFEBioFluid.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53231DC2142AE3D008DE511 /* FEBioFluid */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5EBDC7624AE9A6E0007B220 /* FEBackFlowFSIStabilization.cpp */,\n\t\t\t\tD5EBDC7724AE9A6E0007B220 /* FEBackFlowFSIStabilization.h */,\n\t\t\t\tD53231F82142AE3D008DE511 /* FEBackFlowStabilization.cpp */,\n\t\t\t\tD53232032142AE3D008DE511 /* FEBackFlowStabilization.h */,\n\t\t\t\tD59368F726755C3700AF7D0A /* FEBinghamFluid.cpp */,\n\t\t\t\tD59368F826755C3700AF7D0A /* FEBinghamFluid.h */,\n\t\t\t\tD5E85D8F2200F89800F5DF83 /* febiofluid_api.h */,\n\t\t\t\tD53232102142AE3D008DE511 /* FEBioFluid.cpp */,\n\t\t\t\tD53232152142AE3D008DE511 /* FEBioFluid.h */,\n\t\t\t\tD53231F92142AE3D008DE511 /* FEBioFluidData.cpp */,\n\t\t\t\tD53232212142AE3D008DE511 /* FEBioFluidData.h */,\n\t\t\t\tD5AB62452202A52C00AAF141 /* FEBioFluidP.cpp */,\n\t\t\t\tD5AB62462202A52C00AAF141 /* FEBioFluidP.h */,\n\t\t\t\tD53231E92142AE3D008DE511 /* FEBioFluidPlot.cpp */,\n\t\t\t\tD532320B2142AE3D008DE511 /* FEBioFluidPlot.h */,\n\t\t\t\tD50462BA22EF676700CCA668 /* FEBioFluidSolutes.cpp */,\n\t\t\t\tD50462BB22EF676700CCA668 /* FEBioFluidSolutes.h */,\n\t\t\t\tD53232222142AE3D008DE511 /* FEBioFSI.cpp */,\n\t\t\t\tD532321A2142AE3D008DE511 /* FEBioFSI.h */,\n\t\t\t\t695C595025C31E9F006B53DE /* FEBioMultiphasicFSI.cpp */,\n\t\t\t\t695C595125C31E9F006B53DE /* FEBioMultiphasicFSI.h */,\n\t\t\t\tD5C13EFD240714A4005897E1 /* FEBioThermoFluid.cpp */,\n\t\t\t\tD5C13EFE240714A4005897E1 /* FEBioThermoFluid.h */,\n\t\t\t\t691BD2FF2412B185002BC4A1 /* FEBiphasicFSI.cpp */,\n\t\t\t\t691BD3002412B185002BC4A1 /* FEBiphasicFSI.h */,\n\t\t\t\t691BD3082412B187002BC4A1 /* FEBiphasicFSIDomain.cpp */,\n\t\t\t\t691BD2F92412B184002BC4A1 /* FEBiphasicFSIDomain.h */,\n\t\t\t\t691BD2F82412B183002BC4A1 /* FEBiphasicFSIDomain3D.cpp */,\n\t\t\t\t691BD30A2412B187002BC4A1 /* FEBiphasicFSIDomain3D.h */,\n\t\t\t\t69BDB97B25A519720096F5D0 /* FEBiphasicFSITraction.cpp */,\n\t\t\t\t69BDB97C25A519720096F5D0 /* FEBiphasicFSITraction.h */,\n\t\t\t\tD53232192142AE3D008DE511 /* FECarreauFluid.cpp */,\n\t\t\t\tD53232062142AE3D008DE511 /* FECarreauFluid.h */,\n\t\t\t\tD53231E22142AE3D008DE511 /* FECarreauYasudaFluid.cpp */,\n\t\t\t\tD53231F32142AE3D008DE511 /* FECarreauYasudaFluid.h */,\n\t\t\t\tD53231DE2142AE3D008DE511 /* FEConstraintFrictionlessWall.cpp */,\n\t\t\t\tD53231FB2142AE3D008DE511 /* FEConstraintFrictionlessWall.h */,\n\t\t\t\tD53231EC2142AE3D008DE511 /* FEConstraintNormalFlow.cpp */,\n\t\t\t\tD53231EE2142AE3D008DE511 /* FEConstraintNormalFlow.h */,\n\t\t\t\tD532321E2142AE3D008DE511 /* FECrossFluid.cpp */,\n\t\t\t\tD532320D2142AE3D008DE511 /* FECrossFluid.h */,\n\t\t\t\tD5C13EED24048BBC005897E1 /* FEElasticFluid.cpp */,\n\t\t\t\tD5C13EEE24048BBC005897E1 /* FEElasticFluid.h */,\n\t\t\t\tD53232112142AE3D008DE511 /* FEFluid.cpp */,\n\t\t\t\tD53231F12142AE3D008DE511 /* FEFluid.h */,\n\t\t\t\tD57C2A69240996D60043C2A7 /* FEFluidConstantConductivity.cpp */,\n\t\t\t\tD57C2A6A240996D60043C2A7 /* FEFluidConstantConductivity.h */,\n\t\t\t\tD532321C2142AE3D008DE511 /* FEFluidDomain.cpp */,\n\t\t\t\tD53231F62142AE3D008DE511 /* FEFluidDomain.h */,\n\t\t\t\tD53231E42142AE3D008DE511 /* FEFluidDomain2D.cpp */,\n\t\t\t\tD53232132142AE3D008DE511 /* FEFluidDomain2D.h */,\n\t\t\t\tD53231EF2142AE3D008DE511 /* FEFluidDomain3D.cpp */,\n\t\t\t\tD532320F2142AE3D008DE511 /* FEFluidDomain3D.h */,\n\t\t\t\tD53231F72142AE3D008DE511 /* FEFluidDomainFactory.cpp */,\n\t\t\t\tD53232012142AE3D008DE511 /* FEFluidDomainFactory.h */,\n\t\t\t\tD53231E12142AE3D008DE511 /* FEFluidFSI.cpp */,\n\t\t\t\tD53231F02142AE3D008DE511 /* FEFluidFSI.h */,\n\t\t\t\tD53231FA2142AE3D008DE511 /* FEFluidFSIDomain.cpp */,\n\t\t\t\tD53232052142AE3D008DE511 /* FEFluidFSIDomain.h */,\n\t\t\t\tD532320E2142AE3D008DE511 /* FEFluidFSIDomain3D.cpp */,\n\t\t\t\tD53231E62142AE3D008DE511 /* FEFluidFSIDomain3D.h */,\n\t\t\t\tD53231E72142AE3D008DE511 /* FEFluidFSIDomainFactory.cpp */,\n\t\t\t\tD53231F22142AE3D008DE511 /* FEFluidFSIDomainFactory.h */,\n\t\t\t\tD53232082142AE3D008DE511 /* FEFluidFSISolver.cpp */,\n\t\t\t\tD53231FD2142AE3D008DE511 /* FEFluidFSISolver.h */,\n\t\t\t\tD53232122142AE3D008DE511 /* FEFluidFSITraction.cpp */,\n\t\t\t\tD53231DF2142AE3D008DE511 /* FEFluidFSITraction.h */,\n\t\t\t\tD57C2A55240882500043C2A7 /* FEFluidHeatSupply.cpp */,\n\t\t\t\tD57C2A56240882500043C2A7 /* FEFluidHeatSupply.h */,\n\t\t\t\tD57C2A59240889CA0043C2A7 /* FEFluidHeatSupplyConst.cpp */,\n\t\t\t\tD57C2A5A240889CA0043C2A7 /* FEFluidHeatSupplyConst.h */,\n\t\t\t\tD57C2A6D240EFF610043C2A7 /* FEFluidMaterial.cpp */,\n\t\t\t\tD57C2A6E240EFF610043C2A7 /* FEFluidMaterial.h */,\n\t\t\t\tD5C13EE5240484FD005897E1 /* FEFluidMaterialPoint.cpp */,\n\t\t\t\tD5C13EE6240484FD005897E1 /* FEFluidMaterialPoint.h */,\n\t\t\t\t691BD2F42412B182002BC4A1 /* FEFluidMixtureTractionLoad.cpp */,\n\t\t\t\t691BD3032412B186002BC4A1 /* FEFluidMixtureTractionLoad.h */,\n\t\t\t\tD57C2A61240975A90043C2A7 /* FEFluidNormalHeatFlux.cpp */,\n\t\t\t\tD57C2A62240975A90043C2A7 /* FEFluidNormalHeatFlux.h */,\n\t\t\t\tD53231FF2142AE3D008DE511 /* FEFluidNormalTraction.cpp */,\n\t\t\t\tD53232142142AE3D008DE511 /* FEFluidNormalTraction.h */,\n\t\t\t\tD53232092142AE3D008DE511 /* FEFluidNormalVelocity.cpp */,\n\t\t\t\tD532321D2142AE3D008DE511 /* FEFluidNormalVelocity.h */,\n\t\t\t\tD577565D220484E20022916B /* FEFluidP.cpp */,\n\t\t\t\tD577565E220484E20022916B /* FEFluidP.h */,\n\t\t\t\tD5AB624122028A3900AAF141 /* FEFluidPDomain3D.cpp */,\n\t\t\t\tD5AB624222028A3900AAF141 /* FEFluidPDomain3D.h */,\n\t\t\t\tD5AB62492202A63800AAF141 /* FEFluidPDomainFactory.cpp */,\n\t\t\t\tD5AB624A2202A63800AAF141 /* FEFluidPDomainFactory.h */,\n\t\t\t\tD538E19D221843AD0043A6DA /* FEFluidPResistanceBC.cpp */,\n\t\t\t\tD538E19E221843AD0043A6DA /* FEFluidPResistanceBC.h */,\n\t\t\t\tD5E83000262A467300B23605 /* FEFluidPressureLoad.cpp */,\n\t\t\t\tD5E83001262A467300B23605 /* FEFluidPressureLoad.h */,\n\t\t\t\t6957D5B7247D6C4B008522FA /* FEFluidRCBC.cpp */,\n\t\t\t\t6957D5B8247D6C4C008522FA /* FEFluidRCBC.h */,\n\t\t\t\t696F85EB2432A02900890DD2 /* FEFluidRCRBC.cpp */,\n\t\t\t\t696F85EC2432A02900890DD2 /* FEFluidRCRBC.h */,\n\t\t\t\tD53232072142AE3D008DE511 /* FEFluidResidualVector.cpp */,\n\t\t\t\tD53231E82142AE3D008DE511 /* FEFluidResidualVector.h */,\n\t\t\t\tD53231DD2142AE3D008DE511 /* FEFluidResistanceBC.cpp */,\n\t\t\t\tD53232172142AE3D008DE511 /* FEFluidResistanceBC.h */,\n\t\t\t\tD53232042142AE3D008DE511 /* FEFluidRotationalVelocity.cpp */,\n\t\t\t\tD53231EB2142AE3D008DE511 /* FEFluidRotationalVelocity.h */,\n\t\t\t\tD50462CA22EF6BB800CCA668 /* FEFluidSolutes.cpp */,\n\t\t\t\tD50462CB22EF6BB800CCA668 /* FEFluidSolutes.h */,\n\t\t\t\tD57A0AF72346311600B7C0B8 /* FEFluidSolutesDomain2.cpp */,\n\t\t\t\tD57A0AF32346311600B7C0B8 /* FEFluidSolutesDomain2.h */,\n\t\t\t\tD50462C222EF6A4D00CCA668 /* FEFluidSolutesDomain3D.cpp */,\n\t\t\t\tD50462C322EF6A4D00CCA668 /* FEFluidSolutesDomain3D.h */,\n\t\t\t\tD50462C622EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.cpp */,\n\t\t\t\tD50462C722EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.h */,\n\t\t\t\t695C594C25BF5BA9006B53DE /* FEFluidSolutesFlux.cpp */,\n\t\t\t\t695C594D25BF5BA9006B53DE /* FEFluidSolutesFlux.h */,\n\t\t\t\tD57A0AF62346311600B7C0B8 /* FEFluidSolutesMaterial2.cpp */,\n\t\t\t\tD57A0AF82346311600B7C0B8 /* FEFluidSolutesMaterial2.h */,\n\t\t\t\t693FEF6425645FC3004F9ACF /* FEFluidSolutesNaturalFlux.cpp */,\n\t\t\t\t693FEF6525645FC3004F9ACF /* FEFluidSolutesNaturalFlux.h */,\n\t\t\t\t698D57F42582BD66004CFD27 /* FEFluidSolutesPressure.cpp */,\n\t\t\t\t698D57F52582BD66004CFD27 /* FEFluidSolutesPressure.h */,\n\t\t\t\tD50462BE22EF693600CCA668 /* FEFluidSolutesSolver.cpp */,\n\t\t\t\tD50462BF22EF693600CCA668 /* FEFluidSolutesSolver.h */,\n\t\t\t\tD57A0AF52346311600B7C0B8 /* FEFluidSolutesSolver2.cpp */,\n\t\t\t\tD57A0AF42346311600B7C0B8 /* FEFluidSolutesSolver2.h */,\n\t\t\t\tD53232252142AE3D008DE511 /* FEFluidSolver.cpp */,\n\t\t\t\tD53232002142AE3D008DE511 /* FEFluidSolver.h */,\n\t\t\t\tD58A40B322BA870E009AA77E /* FEFluidStressCriterion.cpp */,\n\t\t\t\tD58A40B422BA870E009AA77E /* FEFluidStressCriterion.h */,\n\t\t\t\tD57C2A49240863B60043C2A7 /* FEFluidThermalConductivity.cpp */,\n\t\t\t\tD57C2A4A240863B60043C2A7 /* FEFluidThermalConductivity.h */,\n\t\t\t\tD53231E02142AE3D008DE511 /* FEFluidTractionLoad.cpp */,\n\t\t\t\tD53231E52142AE3D008DE511 /* FEFluidTractionLoad.h */,\n\t\t\t\tD53231ED2142AE3D008DE511 /* FEFluidVelocity.cpp */,\n\t\t\t\tD53232242142AE3D008DE511 /* FEFluidVelocity.h */,\n\t\t\t\tD5B805D1223C2E1200198805 /* FEFSIErosionVolumeRatio.cpp */,\n\t\t\t\tD5B805D2223C2E1200198805 /* FEFSIErosionVolumeRatio.h */,\n\t\t\t\tD5C13EF12405D419005897E1 /* FEIdealGas.cpp */,\n\t\t\t\tD5C13EF22405D419005897E1 /* FEIdealGas.h */,\n\t\t\t\tD53231F52142AE3D008DE511 /* FEIdealGasIsentropic.cpp */,\n\t\t\t\tD532321F2142AE3D008DE511 /* FEIdealGasIsentropic.h */,\n\t\t\t\tD5D65A8A222B109600F95841 /* FEIdealGasIsothermal.cpp */,\n\t\t\t\tD5D65A89222B109600F95841 /* FEIdealGasIsothermal.h */,\n\t\t\t\tD57C2A65240984CC0043C2A7 /* FEIdealLiquid.cpp */,\n\t\t\t\tD57C2A66240984CC0043C2A7 /* FEIdealLiquid.h */,\n\t\t\t\tD5027074269F48D2007B16A3 /* FELinearElasticFluid.cpp */,\n\t\t\t\tD5027075269F48D2007B16A3 /* FELinearElasticFluid.h */,\n\t\t\t\t695C595425C32277006B53DE /* FEMultiphasicFSI.cpp */,\n\t\t\t\t695C595525C32277006B53DE /* FEMultiphasicFSI.h */,\n\t\t\t\t695C595825C322B7006B53DE /* FEMultiphasicFSIDomain.cpp */,\n\t\t\t\t695C595925C322B7006B53DE /* FEMultiphasicFSIDomain.h */,\n\t\t\t\t695C595C25C322F7006B53DE /* FEMultiphasicFSIDomain3D.cpp */,\n\t\t\t\t695C595D25C322F7006B53DE /* FEMultiphasicFSIDomain3D.h */,\n\t\t\t\t695C596025C3230F006B53DE /* FEMultiphasicFSIDomainFactory.cpp */,\n\t\t\t\t695C596125C3230F006B53DE /* FEMultiphasicFSIDomainFactory.h */,\n\t\t\t\t690FCC6E25CDB9BE000A6204 /* FEMultiphasicFSIPressure.cpp */,\n\t\t\t\t690FCC6F25CDB9BE000A6204 /* FEMultiphasicFSIPressure.h */,\n\t\t\t\t690FCC6A25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.cpp */,\n\t\t\t\t690FCC6B25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.h */,\n\t\t\t\t695C596C25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.cpp */,\n\t\t\t\t695C596D25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.h */,\n\t\t\t\t695C596425C32326006B53DE /* FEMultiphasicFSISolver.cpp */,\n\t\t\t\t695C596525C32326006B53DE /* FEMultiphasicFSISolver.h */,\n\t\t\t\tD53232022142AE3D008DE511 /* FENewtonianFluid.cpp */,\n\t\t\t\tD53231E32142AE3D008DE511 /* FENewtonianFluid.h */,\n\t\t\t\tD5027078269F5092007B16A3 /* FENonlinearElasticFluid.cpp */,\n\t\t\t\tD5027079269F5092007B16A3 /* FENonlinearElasticFluid.h */,\n\t\t\t\tD53232202142AE3D008DE511 /* FEPowellEyringFluid.cpp */,\n\t\t\t\tD53231F42142AE3D008DE511 /* FEPowellEyringFluid.h */,\n\t\t\t\tD511EF1C253B782C00893F69 /* FERealGas.cpp */,\n\t\t\t\tD511EF1D253B782C00893F69 /* FERealGas.h */,\n\t\t\t\tD511EF0D253652CD00893F69 /* FERealLiquid.cpp */,\n\t\t\t\tD511EF0E253652CD00893F69 /* FERealLiquid.h */,\n\t\t\t\tD509D27D22F32408007DFC44 /* FESoluteBackflowStabilization.cpp */,\n\t\t\t\tD509D27E22F32408007DFC44 /* FESoluteBackflowStabilization.h */,\n\t\t\t\tD509D28122F4CABC007DFC44 /* FESoluteConvectiveFlow.cpp */,\n\t\t\t\tD509D28222F4CABC007DFC44 /* FESoluteConvectiveFlow.h */,\n\t\t\t\tD58DDDF823233A8D007A490A /* FESolutesDomain.cpp */,\n\t\t\t\tD58DDDF423233A8D007A490A /* FESolutesDomain.h */,\n\t\t\t\tD58DDDFB23233A8E007A490A /* FESolutesDomainFactory.cpp */,\n\t\t\t\tD58DDDF523233A8D007A490A /* FESolutesDomainFactory.h */,\n\t\t\t\tD58DDDF923233A8E007A490A /* FESolutesMaterial.cpp */,\n\t\t\t\tD58DDDF723233A8D007A490A /* FESolutesMaterial.h */,\n\t\t\t\tD58DDDFA23233A8E007A490A /* FESolutesSolver.cpp */,\n\t\t\t\tD58DDDF623233A8D007A490A /* FESolutesSolver.h */,\n\t\t\t\tD53232182142AE3D008DE511 /* FETangentialDamping.cpp */,\n\t\t\t\tD53232162142AE3D008DE511 /* FETangentialDamping.h */,\n\t\t\t\tD5EBDC7A24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.cpp */,\n\t\t\t\tD5EBDC7B24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.h */,\n\t\t\t\tD53231FC2142AE3D008DE511 /* FETangentialFlowStabilization.cpp */,\n\t\t\t\tD532320C2142AE3D008DE511 /* FETangentialFlowStabilization.h */,\n\t\t\t\tD511EF20253C7C5800893F69 /* FETempDependentConductivity.cpp */,\n\t\t\t\tD511EF21253C7C5800893F69 /* FETempDependentConductivity.h */,\n\t\t\t\t694948DB2476D5220031BD24 /* FETemperatureBackFlowStabilization.cpp */,\n\t\t\t\t694948D92476D5220031BD24 /* FETemperatureBackFlowStabilization.h */,\n\t\t\t\tD5C13EE1240479F9005897E1 /* FEThermoFluid.cpp */,\n\t\t\t\tD5C13EE2240479F9005897E1 /* FEThermoFluid.h */,\n\t\t\t\tD5C13EF5240711D3005897E1 /* FEThermoFluidDomain.cpp */,\n\t\t\t\tD5C13EF6240711D3005897E1 /* FEThermoFluidDomain.h */,\n\t\t\t\tD5C13EF92407124C005897E1 /* FEThermoFluidDomain3D.cpp */,\n\t\t\t\tD5C13EFA2407124C005897E1 /* FEThermoFluidDomain3D.h */,\n\t\t\t\tD57C2A5124087BE40043C2A7 /* FEThermoFluidDomainFactory.cpp */,\n\t\t\t\tD57C2A5224087BE40043C2A7 /* FEThermoFluidDomainFactory.h */,\n\t\t\t\tD5C13EE9240486FF005897E1 /* FEThermoFluidMaterialPoint.cpp */,\n\t\t\t\tD5C13EEA240486FF005897E1 /* FEThermoFluidMaterialPoint.h */,\n\t\t\t\t694948D82476D5220031BD24 /* FEThermoFluidPressureLoad.cpp */,\n\t\t\t\t694948DA2476D5220031BD24 /* FEThermoFluidPressureLoad.h */,\n\t\t\t\tD57C2A5D240969AD0043C2A7 /* FEThermoFluidSolver.cpp */,\n\t\t\t\tD57C2A5E240969AD0043C2A7 /* FEThermoFluidSolver.h */,\n\t\t\t\tD53232232142AE3D008DE511 /* FETiedFluidInterface.cpp */,\n\t\t\t\tD532320A2142AE3D008DE511 /* FETiedFluidInterface.h */,\n\t\t\t\tD53231FE2142AE3D008DE511 /* FEViscousFluid.cpp */,\n\t\t\t\tD532321B2142AE3D008DE511 /* FEViscousFluid.h */,\n\t\t\t\tD53231EA2142AE3D008DE511 /* stdafx.h */,\n\t\t\t);\n\t\t\tname = FEBioFluid;\n\t\t\tpath = ../../FEBioFluid;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD53231CC2142AE2D008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD53232332142AE3D008DE511 /* stdafx.h in Headers */,\n\t\t\t\t695C596325C3230F006B53DE /* FEMultiphasicFSIDomainFactory.h in Headers */,\n\t\t\t\tD532322C2142AE3D008DE511 /* FENewtonianFluid.h in Headers */,\n\t\t\t\tD5C13EE8240484FD005897E1 /* FEFluidMaterialPoint.h in Headers */,\n\t\t\t\t691BD3182412B187002BC4A1 /* FEBiphasicFSI.h in Headers */,\n\t\t\t\t695C595B25C322B7006B53DE /* FEMultiphasicFSIDomain.h in Headers */,\n\t\t\t\tD53232342142AE3D008DE511 /* FEFluidRotationalVelocity.h in Headers */,\n\t\t\t\tD5B805D4223C2E1200198805 /* FEFSIErosionVolumeRatio.h in Headers */,\n\t\t\t\tD532324C2142AE3D008DE511 /* FEBackFlowStabilization.h in Headers */,\n\t\t\t\tD53232602142AE3E008DE511 /* FEFluidResistanceBC.h in Headers */,\n\t\t\t\tD50462C522EF6A4D00CCA668 /* FEFluidSolutesDomain3D.h in Headers */,\n\t\t\t\t695C596725C32326006B53DE /* FEMultiphasicFSISolver.h in Headers */,\n\t\t\t\tD50462C922EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.h in Headers */,\n\t\t\t\tD53232532142AE3D008DE511 /* FETiedFluidInterface.h in Headers */,\n\t\t\t\tD532324A2142AE3D008DE511 /* FEFluidDomainFactory.h in Headers */,\n\t\t\t\tD5C13EEC240486FF005897E1 /* FEThermoFluidMaterialPoint.h in Headers */,\n\t\t\t\tD5EBDC7D24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.h in Headers */,\n\t\t\t\tD5C13EF024048BBC005897E1 /* FEElasticFluid.h in Headers */,\n\t\t\t\tD53232632142AE3E008DE511 /* FEBioFSI.h in Headers */,\n\t\t\t\tD532322E2142AE3D008DE511 /* FEFluidTractionLoad.h in Headers */,\n\t\t\t\t691BD3112412B187002BC4A1 /* FEBiphasicFSIDomain.h in Headers */,\n\t\t\t\tD58DDDFE23233A8E007A490A /* FESolutesSolver.h in Headers */,\n\t\t\t\tD57C2A68240984CC0043C2A7 /* FEIdealLiquid.h in Headers */,\n\t\t\t\tD538E1A0221843AD0043A6DA /* FEFluidPResistanceBC.h in Headers */,\n\t\t\t\tD57A0AFE2346311600B7C0B8 /* FEFluidSolutesMaterial2.h in Headers */,\n\t\t\t\tD53232642142AE3E008DE511 /* FEViscousFluid.h in Headers */,\n\t\t\t\tD5D65A8B222B109600F95841 /* FEIdealGasIsothermal.h in Headers */,\n\t\t\t\tD57C2A5424087BE40043C2A7 /* FEThermoFluidDomainFactory.h in Headers */,\n\t\t\t\tD57C2A58240882500043C2A7 /* FEFluidHeatSupply.h in Headers */,\n\t\t\t\tD53232462142AE3D008DE511 /* FEFluidFSISolver.h in Headers */,\n\t\t\t\tD5C13EF8240711D3005897E1 /* FEThermoFluidDomain.h in Headers */,\n\t\t\t\t6957D5BA247D6C4C008522FA /* FEFluidRCBC.h in Headers */,\n\t\t\t\tD53232562142AE3D008DE511 /* FECrossFluid.h in Headers */,\n\t\t\t\tD532325F2142AE3E008DE511 /* FETangentialDamping.h in Headers */,\n\t\t\t\tD509D28422F4CABC007DFC44 /* FESoluteConvectiveFlow.h in Headers */,\n\t\t\t\tD58A40B622BA870F009AA77E /* FEFluidStressCriterion.h in Headers */,\n\t\t\t\t694948DD2476D5220031BD24 /* FETemperatureBackFlowStabilization.h in Headers */,\n\t\t\t\t695C595F25C322F7006B53DE /* FEMultiphasicFSIDomain3D.h in Headers */,\n\t\t\t\t694948DE2476D5220031BD24 /* FEThermoFluidPressureLoad.h in Headers */,\n\t\t\t\t698D57F72582BD66004CFD27 /* FEFluidSolutesPressure.h in Headers */,\n\t\t\t\tD57A0AFA2346311600B7C0B8 /* FEFluidSolutesSolver2.h in Headers */,\n\t\t\t\t695C596F25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.h in Headers */,\n\t\t\t\tD5775660220484E20022916B /* FEFluidP.h in Headers */,\n\t\t\t\tD5EBDC7924AE9A6E0007B220 /* FEBackFlowFSIStabilization.h in Headers */,\n\t\t\t\t691BD31B2412B187002BC4A1 /* FEFluidMixtureTractionLoad.h in Headers */,\n\t\t\t\tD53232442142AE3D008DE511 /* FEConstraintFrictionlessWall.h in Headers */,\n\t\t\t\tD57C2A70240EFF610043C2A7 /* FEFluidMaterial.h in Headers */,\n\t\t\t\tD53232662142AE3E008DE511 /* FEFluidNormalVelocity.h in Headers */,\n\t\t\t\tD532323F2142AE3D008DE511 /* FEFluidDomain.h in Headers */,\n\t\t\t\t690FCC7125CDB9BE000A6204 /* FEMultiphasicFSIPressure.h in Headers */,\n\t\t\t\tD5AB624C2202A63800AAF141 /* FEFluidPDomainFactory.h in Headers */,\n\t\t\t\tD53232312142AE3D008DE511 /* FEFluidResidualVector.h in Headers */,\n\t\t\t\tD58DDDFF23233A8E007A490A /* FESolutesMaterial.h in Headers */,\n\t\t\t\tD511EF23253C7C5800893F69 /* FETempDependentConductivity.h in Headers */,\n\t\t\t\tD50462C122EF693600CCA668 /* FEFluidSolutesSolver.h in Headers */,\n\t\t\t\tD53232542142AE3D008DE511 /* FEBioFluidPlot.h in Headers */,\n\t\t\t\tD511EF1F253B782C00893F69 /* FERealGas.h in Headers */,\n\t\t\t\tD532324F2142AE3D008DE511 /* FECarreauFluid.h in Headers */,\n\t\t\t\tD532322F2142AE3D008DE511 /* FEFluidFSIDomain3D.h in Headers */,\n\t\t\t\tD532325C2142AE3E008DE511 /* FEFluidDomain2D.h in Headers */,\n\t\t\t\tD532323A2142AE3D008DE511 /* FEFluid.h in Headers */,\n\t\t\t\tD532323D2142AE3D008DE511 /* FEPowellEyringFluid.h in Headers */,\n\t\t\t\t695C595325C31E9F006B53DE /* FEBioMultiphasicFSI.h in Headers */,\n\t\t\t\tD53232282142AE3D008DE511 /* FEFluidFSITraction.h in Headers */,\n\t\t\t\t690FCC6D25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.h in Headers */,\n\t\t\t\tD5E83003262A467400B23605 /* FEFluidPressureLoad.h in Headers */,\n\t\t\t\tD5C13F00240714A4005897E1 /* FEBioThermoFluid.h in Headers */,\n\t\t\t\tD5E85D902200F89800F5DF83 /* febiofluid_api.h in Headers */,\n\t\t\t\tD57C2A6C240996D60043C2A7 /* FEFluidConstantConductivity.h in Headers */,\n\t\t\t\tD532326D2142AE3E008DE511 /* FEFluidVelocity.h in Headers */,\n\t\t\t\tD57C2A60240969AD0043C2A7 /* FEThermoFluidSolver.h in Headers */,\n\t\t\t\tD502707B269F5092007B16A3 /* FENonlinearElasticFluid.h in Headers */,\n\t\t\t\tD53232372142AE3D008DE511 /* FEConstraintNormalFlow.h in Headers */,\n\t\t\t\tD57A0AF92346311600B7C0B8 /* FEFluidSolutesDomain2.h in Headers */,\n\t\t\t\tD50462CD22EF6BB800CCA668 /* FEFluidSolutes.h in Headers */,\n\t\t\t\tD532326A2142AE3E008DE511 /* FEBioFluidData.h in Headers */,\n\t\t\t\t69BDB97E25A519720096F5D0 /* FEBiphasicFSITraction.h in Headers */,\n\t\t\t\tD532323C2142AE3D008DE511 /* FECarreauYasudaFluid.h in Headers */,\n\t\t\t\tD532325D2142AE3E008DE511 /* FEFluidNormalTraction.h in Headers */,\n\t\t\t\tD5C13EF42405D419005897E1 /* FEIdealGas.h in Headers */,\n\t\t\t\tD5027077269F48D2007B16A3 /* FELinearElasticFluid.h in Headers */,\n\t\t\t\tD59368FA26755C3700AF7D0A /* FEBinghamFluid.h in Headers */,\n\t\t\t\t693FEF6725645FC3004F9ACF /* FEFluidSolutesNaturalFlux.h in Headers */,\n\t\t\t\tD509D28022F32408007DFC44 /* FESoluteBackflowStabilization.h in Headers */,\n\t\t\t\tD5AB624422028A3900AAF141 /* FEFluidPDomain3D.h in Headers */,\n\t\t\t\tD532323B2142AE3D008DE511 /* FEFluidFSIDomainFactory.h in Headers */,\n\t\t\t\tD58DDDFD23233A8E007A490A /* FESolutesDomainFactory.h in Headers */,\n\t\t\t\tD50462BD22EF676700CCA668 /* FEBioFluidSolutes.h in Headers */,\n\t\t\t\tD5AB62482202A52C00AAF141 /* FEBioFluidP.h in Headers */,\n\t\t\t\tD532325E2142AE3E008DE511 /* FEBioFluid.h in Headers */,\n\t\t\t\tD57C2A5C240889CA0043C2A7 /* FEFluidHeatSupplyConst.h in Headers */,\n\t\t\t\t696F85EE2432A02900890DD2 /* FEFluidRCRBC.h in Headers */,\n\t\t\t\tD5C13EFC2407124C005897E1 /* FEThermoFluidDomain3D.h in Headers */,\n\t\t\t\tD57C2A64240975A90043C2A7 /* FEFluidNormalHeatFlux.h in Headers */,\n\t\t\t\tD511EF10253652CD00893F69 /* FERealLiquid.h in Headers */,\n\t\t\t\t695C594F25BF5BA9006B53DE /* FEFluidSolutesFlux.h in Headers */,\n\t\t\t\tD53232552142AE3D008DE511 /* FETangentialFlowStabilization.h in Headers */,\n\t\t\t\t691BD3222412B187002BC4A1 /* FEBiphasicFSIDomain3D.h in Headers */,\n\t\t\t\tD532324E2142AE3D008DE511 /* FEFluidFSIDomain.h in Headers */,\n\t\t\t\tD53232492142AE3D008DE511 /* FEFluidSolver.h in Headers */,\n\t\t\t\tD53232392142AE3D008DE511 /* FEFluidFSI.h in Headers */,\n\t\t\t\t695C595725C32277006B53DE /* FEMultiphasicFSI.h in Headers */,\n\t\t\t\tD53232582142AE3D008DE511 /* FEFluidDomain3D.h in Headers */,\n\t\t\t\tD57C2A4C240863B60043C2A7 /* FEFluidThermalConductivity.h in Headers */,\n\t\t\t\tD53232682142AE3E008DE511 /* FEIdealGasIsentropic.h in Headers */,\n\t\t\t\tD58DDDFC23233A8E007A490A /* FESolutesDomain.h in Headers */,\n\t\t\t\tD5C13EE4240479F9005897E1 /* FEThermoFluid.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD53231CD2142AE2D008DE511 /* FEBioFluid */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D53231D92142AE2D008DE511 /* Build configuration list for PBXNativeTarget \"FEBioFluid\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD53231CA2142AE2D008DE511 /* Sources */,\n\t\t\t\tD53231CB2142AE2D008DE511 /* Frameworks */,\n\t\t\t\tD53231CC2142AE2D008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioFluid;\n\t\t\tproductName = FEBioFluid;\n\t\t\tproductReference = D53231CE2142AE2D008DE511 /* libFEBioFluid.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD53231C62142AE2D008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD53231CD2142AE2D008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D53231C92142AE2D008DE511 /* Build configuration list for PBXProject \"FEBioFluid\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D53231C52142AE2D008DE511;\n\t\t\tproductRefGroup = D53231CF2142AE2D008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD53231CD2142AE2D008DE511 /* FEBioFluid */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD53231CA2142AE2D008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD511EF0F253652CD00893F69 /* FERealLiquid.cpp in Sources */,\n\t\t\t\tD53232362142AE3D008DE511 /* FEFluidVelocity.cpp in Sources */,\n\t\t\t\tD53232522142AE3D008DE511 /* FEFluidNormalVelocity.cpp in Sources */,\n\t\t\t\tD53232572142AE3D008DE511 /* FEFluidFSIDomain3D.cpp in Sources */,\n\t\t\t\tD57C2A63240975A90043C2A7 /* FEFluidNormalHeatFlux.cpp in Sources */,\n\t\t\t\tD53232612142AE3E008DE511 /* FETangentialDamping.cpp in Sources */,\n\t\t\t\tD5B805D3223C2E1200198805 /* FEFSIErosionVolumeRatio.cpp in Sources */,\n\t\t\t\tD57C2A5F240969AD0043C2A7 /* FEThermoFluidSolver.cpp in Sources */,\n\t\t\t\t693FEF6625645FC3004F9ACF /* FEFluidSolutesNaturalFlux.cpp in Sources */,\n\t\t\t\tD58A40B522BA870F009AA77E /* FEFluidStressCriterion.cpp in Sources */,\n\t\t\t\tD50462C022EF693600CCA668 /* FEFluidSolutesSolver.cpp in Sources */,\n\t\t\t\t69BDB97D25A519720096F5D0 /* FEBiphasicFSITraction.cpp in Sources */,\n\t\t\t\t695C595E25C322F7006B53DE /* FEMultiphasicFSIDomain3D.cpp in Sources */,\n\t\t\t\t691BD3102412B187002BC4A1 /* FEBiphasicFSIDomain3D.cpp in Sources */,\n\t\t\t\tD532326B2142AE3E008DE511 /* FEBioFSI.cpp in Sources */,\n\t\t\t\t6957D5B9247D6C4C008522FA /* FEFluidRCBC.cpp in Sources */,\n\t\t\t\tD53232652142AE3E008DE511 /* FEFluidDomain.cpp in Sources */,\n\t\t\t\tD509D27F22F32408007DFC44 /* FESoluteBackflowStabilization.cpp in Sources */,\n\t\t\t\tD57C2A57240882500043C2A7 /* FEFluidHeatSupply.cpp in Sources */,\n\t\t\t\tD532322A2142AE3D008DE511 /* FEFluidFSI.cpp in Sources */,\n\t\t\t\tD53232622142AE3E008DE511 /* FECarreauFluid.cpp in Sources */,\n\t\t\t\tD509D28322F4CABC007DFC44 /* FESoluteConvectiveFlow.cpp in Sources */,\n\t\t\t\tD5E83002262A467400B23605 /* FEFluidPressureLoad.cpp in Sources */,\n\t\t\t\tD53232482142AE3D008DE511 /* FEFluidNormalTraction.cpp in Sources */,\n\t\t\t\tD5C13EF7240711D3005897E1 /* FEThermoFluidDomain.cpp in Sources */,\n\t\t\t\tD53232352142AE3D008DE511 /* FEConstraintNormalFlow.cpp in Sources */,\n\t\t\t\tD511EF1E253B782C00893F69 /* FERealGas.cpp in Sources */,\n\t\t\t\tD5C13EFB2407124C005897E1 /* FEThermoFluidDomain3D.cpp in Sources */,\n\t\t\t\tD58DDE0123233A8E007A490A /* FESolutesMaterial.cpp in Sources */,\n\t\t\t\tD53232412142AE3D008DE511 /* FEBackFlowStabilization.cpp in Sources */,\n\t\t\t\tD5C13EE3240479F9005897E1 /* FEThermoFluid.cpp in Sources */,\n\t\t\t\tD50462C822EF6AFB00CCA668 /* FEFluidSolutesDomainFactory.cpp in Sources */,\n\t\t\t\tD53232672142AE3E008DE511 /* FECrossFluid.cpp in Sources */,\n\t\t\t\tD58DDE0223233A8E007A490A /* FESolutesSolver.cpp in Sources */,\n\t\t\t\tD5D65A8C222B109600F95841 /* FEIdealGasIsothermal.cpp in Sources */,\n\t\t\t\t695C596625C32326006B53DE /* FEMultiphasicFSISolver.cpp in Sources */,\n\t\t\t\tD532322D2142AE3D008DE511 /* FEFluidDomain2D.cpp in Sources */,\n\t\t\t\tD53232402142AE3D008DE511 /* FEFluidDomainFactory.cpp in Sources */,\n\t\t\t\tD57C2A67240984CC0043C2A7 /* FEIdealLiquid.cpp in Sources */,\n\t\t\t\t695C594E25BF5BA9006B53DE /* FEFluidSolutesFlux.cpp in Sources */,\n\t\t\t\tD53232502142AE3D008DE511 /* FEFluidResidualVector.cpp in Sources */,\n\t\t\t\t691BD3172412B187002BC4A1 /* FEBiphasicFSI.cpp in Sources */,\n\t\t\t\tD532324D2142AE3D008DE511 /* FEFluidRotationalVelocity.cpp in Sources */,\n\t\t\t\tD50462CC22EF6BB800CCA668 /* FEFluidSolutes.cpp in Sources */,\n\t\t\t\tD5C13EF32405D419005897E1 /* FEIdealGas.cpp in Sources */,\n\t\t\t\tD57A0AFC2346311600B7C0B8 /* FEFluidSolutesMaterial2.cpp in Sources */,\n\t\t\t\t695C595625C32277006B53DE /* FEMultiphasicFSI.cpp in Sources */,\n\t\t\t\tD532325A2142AE3E008DE511 /* FEFluid.cpp in Sources */,\n\t\t\t\tD50462C422EF6A4D00CCA668 /* FEFluidSolutesDomain3D.cpp in Sources */,\n\t\t\t\tD58DDE0023233A8E007A490A /* FESolutesDomain.cpp in Sources */,\n\t\t\t\t695C595A25C322B7006B53DE /* FEMultiphasicFSIDomain.cpp in Sources */,\n\t\t\t\t690FCC6C25CDB85E000A6204 /* FEMultiphasicFSISoluteBackflowStabilization.cpp in Sources */,\n\t\t\t\tD5C13EEF24048BBC005897E1 /* FEElasticFluid.cpp in Sources */,\n\t\t\t\tD5C13EEB240486FF005897E1 /* FEThermoFluidMaterialPoint.cpp in Sources */,\n\t\t\t\tD5EBDC7824AE9A6E0007B220 /* FEBackFlowFSIStabilization.cpp in Sources */,\n\t\t\t\tD57A0AFB2346311600B7C0B8 /* FEFluidSolutesSolver2.cpp in Sources */,\n\t\t\t\tD5027076269F48D2007B16A3 /* FELinearElasticFluid.cpp in Sources */,\n\t\t\t\tD53232292142AE3D008DE511 /* FEFluidTractionLoad.cpp in Sources */,\n\t\t\t\tD53232512142AE3D008DE511 /* FEFluidFSISolver.cpp in Sources */,\n\t\t\t\tD53232422142AE3D008DE511 /* FEBioFluidData.cpp in Sources */,\n\t\t\t\tD53232592142AE3E008DE511 /* FEBioFluid.cpp in Sources */,\n\t\t\t\t698D57F62582BD66004CFD27 /* FEFluidSolutesPressure.cpp in Sources */,\n\t\t\t\tD5EBDC7C24AE9D8B0007B220 /* FETangentialFlowFSIStabilization.cpp in Sources */,\n\t\t\t\tD577565F220484E20022916B /* FEFluidP.cpp in Sources */,\n\t\t\t\t696F85ED2432A02900890DD2 /* FEFluidRCRBC.cpp in Sources */,\n\t\t\t\tD5C13EE7240484FD005897E1 /* FEFluidMaterialPoint.cpp in Sources */,\n\t\t\t\tD532325B2142AE3E008DE511 /* FEFluidFSITraction.cpp in Sources */,\n\t\t\t\tD57C2A6B240996D60043C2A7 /* FEFluidConstantConductivity.cpp in Sources */,\n\t\t\t\tD53232472142AE3D008DE511 /* FEViscousFluid.cpp in Sources */,\n\t\t\t\t695C596E25C4A45D006B53DE /* FEMultiphasicFSISoluteFlux.cpp in Sources */,\n\t\t\t\tD5AB624B2202A63800AAF141 /* FEFluidPDomainFactory.cpp in Sources */,\n\t\t\t\tD5AB624322028A3900AAF141 /* FEFluidPDomain3D.cpp in Sources */,\n\t\t\t\tD57C2A5B240889CA0043C2A7 /* FEFluidHeatSupplyConst.cpp in Sources */,\n\t\t\t\t694948DC2476D5220031BD24 /* FEThermoFluidPressureLoad.cpp in Sources */,\n\t\t\t\tD5C13EFF240714A4005897E1 /* FEBioThermoFluid.cpp in Sources */,\n\t\t\t\tD53232322142AE3D008DE511 /* FEBioFluidPlot.cpp in Sources */,\n\t\t\t\tD50462BC22EF676700CCA668 /* FEBioFluidSolutes.cpp in Sources */,\n\t\t\t\tD57A0AFD2346311600B7C0B8 /* FEFluidSolutesDomain2.cpp in Sources */,\n\t\t\t\tD58DDE0323233A8E007A490A /* FESolutesDomainFactory.cpp in Sources */,\n\t\t\t\t691BD3202412B187002BC4A1 /* FEBiphasicFSIDomain.cpp in Sources */,\n\t\t\t\tD57C2A4B240863B60043C2A7 /* FEFluidThermalConductivity.cpp in Sources */,\n\t\t\t\tD532324B2142AE3D008DE511 /* FENewtonianFluid.cpp in Sources */,\n\t\t\t\tD532323E2142AE3D008DE511 /* FEIdealGasIsentropic.cpp in Sources */,\n\t\t\t\tD532326C2142AE3E008DE511 /* FETiedFluidInterface.cpp in Sources */,\n\t\t\t\tD53232692142AE3E008DE511 /* FEPowellEyringFluid.cpp in Sources */,\n\t\t\t\tD57C2A5324087BE40043C2A7 /* FEThermoFluidDomainFactory.cpp in Sources */,\n\t\t\t\tD59368F926755C3700AF7D0A /* FEBinghamFluid.cpp in Sources */,\n\t\t\t\t691BD30C2412B187002BC4A1 /* FEFluidMixtureTractionLoad.cpp in Sources */,\n\t\t\t\t694948DF2476D5220031BD24 /* FETemperatureBackFlowStabilization.cpp in Sources */,\n\t\t\t\tD57C2A6F240EFF610043C2A7 /* FEFluidMaterial.cpp in Sources */,\n\t\t\t\tD532326E2142AE3E008DE511 /* FEFluidSolver.cpp in Sources */,\n\t\t\t\tD53232272142AE3D008DE511 /* FEConstraintFrictionlessWall.cpp in Sources */,\n\t\t\t\tD502707A269F5092007B16A3 /* FENonlinearElasticFluid.cpp in Sources */,\n\t\t\t\tD53232452142AE3D008DE511 /* FETangentialFlowStabilization.cpp in Sources */,\n\t\t\t\t695C596225C3230F006B53DE /* FEMultiphasicFSIDomainFactory.cpp in Sources */,\n\t\t\t\tD53232382142AE3D008DE511 /* FEFluidDomain3D.cpp in Sources */,\n\t\t\t\tD532322B2142AE3D008DE511 /* FECarreauYasudaFluid.cpp in Sources */,\n\t\t\t\t695C595225C31E9F006B53DE /* FEBioMultiphasicFSI.cpp in Sources */,\n\t\t\t\tD538E19F221843AD0043A6DA /* FEFluidPResistanceBC.cpp in Sources */,\n\t\t\t\t690FCC7025CDB9BE000A6204 /* FEMultiphasicFSIPressure.cpp in Sources */,\n\t\t\t\tD53232262142AE3D008DE511 /* FEFluidResistanceBC.cpp in Sources */,\n\t\t\t\tD53232432142AE3D008DE511 /* FEFluidFSIDomain.cpp in Sources */,\n\t\t\t\tD511EF22253C7C5800893F69 /* FETempDependentConductivity.cpp in Sources */,\n\t\t\t\tD53232302142AE3D008DE511 /* FEFluidFSIDomainFactory.cpp in Sources */,\n\t\t\t\tD5AB62472202A52C00AAF141 /* FEBioFluidP.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD53231D72142AE2D008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t\tWARNING_CFLAGS = (\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD53231D82142AE2D008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t\tWARNING_CFLAGS = (\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD53231DA2142AE2D008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD53231DB2142AE2D008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD53231C92142AE2D008DE511 /* Build configuration list for PBXProject \"FEBioFluid\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD53231D72142AE2D008DE511 /* Debug */,\n\t\t\t\tD53231D82142AE2D008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD53231D92142AE2D008DE511 /* Build configuration list for PBXNativeTarget \"FEBioFluid\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD53231DA2142AE2D008DE511 /* Debug */,\n\t\t\t\tD53231DB2142AE2D008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D53231C62142AE2D008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioFluid/FEBioFluid.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioFluid.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioFluid/FEBioFluid.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioLib/FEBioLib.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD52D841121CE8B3B00472620 /* config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D841021CE8B3B00472620 /* config.cpp */; };\n\t\tD53231B52142ADB9008DE511 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231A52142ADB9008DE511 /* input.cpp */; };\n\t\tD53231B62142ADB9008DE511 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231A62142ADB9008DE511 /* version.h */; };\n\t\tD53231B72142ADB9008DE511 /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231A72142ADB9008DE511 /* stdafx.h */; };\n\t\tD53231B82142ADB9008DE511 /* plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231A82142ADB9008DE511 /* plugin.h */; };\n\t\tD53231B92142ADB9008DE511 /* febiolib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231A92142ADB9008DE511 /* febiolib.cpp */; };\n\t\tD53231BA2142ADB9008DE511 /* FEBioModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231AA2142ADB9008DE511 /* FEBioModel.cpp */; };\n\t\tD53231BB2142ADB9008DE511 /* hello.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231AB2142ADB9008DE511 /* hello.cpp */; };\n\t\tD53231BC2142ADB9008DE511 /* febiolib_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231AC2142ADB9008DE511 /* febiolib_api.h */; };\n\t\tD53231BD2142ADB9008DE511 /* targetver.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231AD2142ADB9008DE511 /* targetver.h */; };\n\t\tD53231BE2142ADB9008DE511 /* plugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231AE2142ADB9008DE511 /* plugin.cpp */; };\n\t\tD53231BF2142ADB9008DE511 /* febio.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231AF2142ADB9008DE511 /* febio.h */; };\n\t\tD53231C02142ADB9008DE511 /* getapppath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231B02142ADB9008DE511 /* getapppath.cpp */; };\n\t\tD53231C12142ADB9008DE511 /* FEBox.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231B12142ADB9008DE511 /* FEBox.h */; };\n\t\tD53231C22142ADB9008DE511 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231B22142ADB9008DE511 /* stdafx.cpp */; };\n\t\tD53231C32142ADB9008DE511 /* FEBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53231B32142ADB9008DE511 /* FEBox.cpp */; };\n\t\tD53231C42142ADB9008DE511 /* FEBioModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D53231B42142ADB9008DE511 /* FEBioModel.h */; };\n\t\tD54A704D242A3162007375FC /* FEBioConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54A704B242A3162007375FC /* FEBioConfig.cpp */; };\n\t\tD54A704E242A3162007375FC /* FEBioConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = D54A704C242A3162007375FC /* FEBioConfig.h */; };\n\t\tD5AE8CC92592ED24003AC08B /* version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CC82592ED24003AC08B /* version.cpp */; };\n\t\tD5B29B2C22309413000ACD5E /* LogFileStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B29B2622309412000ACD5E /* LogFileStream.h */; };\n\t\tD5B29B2D22309413000ACD5E /* LogStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B29B2722309413000ACD5E /* LogStream.cpp */; };\n\t\tD5B29B2E22309413000ACD5E /* LogStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B29B2822309413000ACD5E /* LogStream.h */; };\n\t\tD5B29B2F22309413000ACD5E /* Logfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B29B2922309413000ACD5E /* Logfile.cpp */; };\n\t\tD5B29B3022309413000ACD5E /* LogFileStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B29B2A22309413000ACD5E /* LogFileStream.cpp */; };\n\t\tD5B29B3122309413000ACD5E /* Logfile.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B29B2B22309413000ACD5E /* Logfile.h */; };\n\t\tD5C765EC2368E8F60090EB43 /* memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C765EB2368E8F60090EB43 /* memory.cpp */; };\n\t\tD5E85D932200F8B400F5DF83 /* FEBioStdSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E85D912200F8B400F5DF83 /* FEBioStdSolver.h */; };\n\t\tD5E85D942200F8B400F5DF83 /* FEBioStdSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E85D922200F8B400F5DF83 /* FEBioStdSolver.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD52D841021CE8B3B00472620 /* config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config.cpp; sourceTree = \"<group>\"; };\n\t\tD53231962142ADA3008DE511 /* libFEBioLib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioLib.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD53231A52142ADB9008DE511 /* input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input.cpp; sourceTree = \"<group>\"; };\n\t\tD53231A62142ADB9008DE511 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = \"<group>\"; };\n\t\tD53231A72142ADB9008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD53231A82142ADB9008DE511 /* plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plugin.h; sourceTree = \"<group>\"; };\n\t\tD53231A92142ADB9008DE511 /* febiolib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = febiolib.cpp; sourceTree = \"<group>\"; };\n\t\tD53231AA2142ADB9008DE511 /* FEBioModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioModel.cpp; sourceTree = \"<group>\"; };\n\t\tD53231AB2142ADB9008DE511 /* hello.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hello.cpp; sourceTree = \"<group>\"; };\n\t\tD53231AC2142ADB9008DE511 /* febiolib_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febiolib_api.h; sourceTree = \"<group>\"; };\n\t\tD53231AD2142ADB9008DE511 /* targetver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = targetver.h; sourceTree = \"<group>\"; };\n\t\tD53231AE2142ADB9008DE511 /* plugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = plugin.cpp; sourceTree = \"<group>\"; };\n\t\tD53231AF2142ADB9008DE511 /* febio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febio.h; sourceTree = \"<group>\"; };\n\t\tD53231B02142ADB9008DE511 /* getapppath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = getapppath.cpp; sourceTree = \"<group>\"; };\n\t\tD53231B12142ADB9008DE511 /* FEBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBox.h; sourceTree = \"<group>\"; };\n\t\tD53231B22142ADB9008DE511 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = \"<group>\"; };\n\t\tD53231B32142ADB9008DE511 /* FEBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBox.cpp; sourceTree = \"<group>\"; };\n\t\tD53231B42142ADB9008DE511 /* FEBioModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioModel.h; sourceTree = \"<group>\"; };\n\t\tD54A704B242A3162007375FC /* FEBioConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioConfig.cpp; sourceTree = \"<group>\"; };\n\t\tD54A704C242A3162007375FC /* FEBioConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioConfig.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CC82592ED24003AC08B /* version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version.cpp; sourceTree = \"<group>\"; };\n\t\tD5B29B2622309412000ACD5E /* LogFileStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFileStream.h; sourceTree = \"<group>\"; };\n\t\tD5B29B2722309413000ACD5E /* LogStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogStream.cpp; sourceTree = \"<group>\"; };\n\t\tD5B29B2822309413000ACD5E /* LogStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogStream.h; sourceTree = \"<group>\"; };\n\t\tD5B29B2922309413000ACD5E /* Logfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logfile.cpp; sourceTree = \"<group>\"; };\n\t\tD5B29B2A22309413000ACD5E /* LogFileStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFileStream.cpp; sourceTree = \"<group>\"; };\n\t\tD5B29B2B22309413000ACD5E /* Logfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logfile.h; sourceTree = \"<group>\"; };\n\t\tD5C765EB2368E8F60090EB43 /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = \"<group>\"; };\n\t\tD5E85D912200F8B400F5DF83 /* FEBioStdSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioStdSolver.h; sourceTree = \"<group>\"; };\n\t\tD5E85D922200F8B400F5DF83 /* FEBioStdSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioStdSolver.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD53231932142ADA3008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD532318D2142ADA3008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD53231A42142ADB9008DE511 /* FEBioLib */,\n\t\t\t\tD53231972142ADA3008DE511 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53231972142ADA3008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD53231962142ADA3008DE511 /* libFEBioLib.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53231A42142ADB9008DE511 /* FEBioLib */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD52D841021CE8B3B00472620 /* config.cpp */,\n\t\t\t\tD53231AF2142ADB9008DE511 /* febio.h */,\n\t\t\t\tD54A704B242A3162007375FC /* FEBioConfig.cpp */,\n\t\t\t\tD54A704C242A3162007375FC /* FEBioConfig.h */,\n\t\t\t\tD53231AC2142ADB9008DE511 /* febiolib_api.h */,\n\t\t\t\tD53231A92142ADB9008DE511 /* febiolib.cpp */,\n\t\t\t\tD53231AA2142ADB9008DE511 /* FEBioModel.cpp */,\n\t\t\t\tD53231B42142ADB9008DE511 /* FEBioModel.h */,\n\t\t\t\tD5E85D922200F8B400F5DF83 /* FEBioStdSolver.cpp */,\n\t\t\t\tD5E85D912200F8B400F5DF83 /* FEBioStdSolver.h */,\n\t\t\t\tD53231B32142ADB9008DE511 /* FEBox.cpp */,\n\t\t\t\tD53231B12142ADB9008DE511 /* FEBox.h */,\n\t\t\t\tD53231B02142ADB9008DE511 /* getapppath.cpp */,\n\t\t\t\tD53231AB2142ADB9008DE511 /* hello.cpp */,\n\t\t\t\tD53231A52142ADB9008DE511 /* input.cpp */,\n\t\t\t\tD5B29B2922309413000ACD5E /* Logfile.cpp */,\n\t\t\t\tD5B29B2B22309413000ACD5E /* Logfile.h */,\n\t\t\t\tD5B29B2A22309413000ACD5E /* LogFileStream.cpp */,\n\t\t\t\tD5B29B2622309412000ACD5E /* LogFileStream.h */,\n\t\t\t\tD5B29B2722309413000ACD5E /* LogStream.cpp */,\n\t\t\t\tD5B29B2822309413000ACD5E /* LogStream.h */,\n\t\t\t\tD5C765EB2368E8F60090EB43 /* memory.cpp */,\n\t\t\t\tD53231AE2142ADB9008DE511 /* plugin.cpp */,\n\t\t\t\tD53231A82142ADB9008DE511 /* plugin.h */,\n\t\t\t\tD53231B22142ADB9008DE511 /* stdafx.cpp */,\n\t\t\t\tD53231A72142ADB9008DE511 /* stdafx.h */,\n\t\t\t\tD53231AD2142ADB9008DE511 /* targetver.h */,\n\t\t\t\tD5AE8CC82592ED24003AC08B /* version.cpp */,\n\t\t\t\tD53231A62142ADB9008DE511 /* version.h */,\n\t\t\t);\n\t\t\tname = FEBioLib;\n\t\t\tpath = ../../FEBioLib;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD53231942142ADA3008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD53231C42142ADB9008DE511 /* FEBioModel.h in Headers */,\n\t\t\t\tD53231BF2142ADB9008DE511 /* febio.h in Headers */,\n\t\t\t\tD5E85D932200F8B400F5DF83 /* FEBioStdSolver.h in Headers */,\n\t\t\t\tD54A704E242A3162007375FC /* FEBioConfig.h in Headers */,\n\t\t\t\tD53231B72142ADB9008DE511 /* stdafx.h in Headers */,\n\t\t\t\tD53231BD2142ADB9008DE511 /* targetver.h in Headers */,\n\t\t\t\tD5B29B3122309413000ACD5E /* Logfile.h in Headers */,\n\t\t\t\tD53231BC2142ADB9008DE511 /* febiolib_api.h in Headers */,\n\t\t\t\tD53231C12142ADB9008DE511 /* FEBox.h in Headers */,\n\t\t\t\tD5B29B2C22309413000ACD5E /* LogFileStream.h in Headers */,\n\t\t\t\tD53231B82142ADB9008DE511 /* plugin.h in Headers */,\n\t\t\t\tD53231B62142ADB9008DE511 /* version.h in Headers */,\n\t\t\t\tD5B29B2E22309413000ACD5E /* LogStream.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD53231952142ADA3008DE511 /* FEBioLib */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D53231A12142ADA3008DE511 /* Build configuration list for PBXNativeTarget \"FEBioLib\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD53231922142ADA3008DE511 /* Sources */,\n\t\t\t\tD53231932142ADA3008DE511 /* Frameworks */,\n\t\t\t\tD53231942142ADA3008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioLib;\n\t\t\tproductName = FEBioLib;\n\t\t\tproductReference = D53231962142ADA3008DE511 /* libFEBioLib.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD532318E2142ADA3008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD53231952142ADA3008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D53231912142ADA3008DE511 /* Build configuration list for PBXProject \"FEBioLib\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D532318D2142ADA3008DE511;\n\t\t\tproductRefGroup = D53231972142ADA3008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD53231952142ADA3008DE511 /* FEBioLib */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD53231922142ADA3008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD53231BE2142ADB9008DE511 /* plugin.cpp in Sources */,\n\t\t\t\tD53231BA2142ADB9008DE511 /* FEBioModel.cpp in Sources */,\n\t\t\t\tD5AE8CC92592ED24003AC08B /* version.cpp in Sources */,\n\t\t\t\tD5B29B3022309413000ACD5E /* LogFileStream.cpp in Sources */,\n\t\t\t\tD5E85D942200F8B400F5DF83 /* FEBioStdSolver.cpp in Sources */,\n\t\t\t\tD53231B52142ADB9008DE511 /* input.cpp in Sources */,\n\t\t\t\tD53231BB2142ADB9008DE511 /* hello.cpp in Sources */,\n\t\t\t\tD53231B92142ADB9008DE511 /* febiolib.cpp in Sources */,\n\t\t\t\tD53231C22142ADB9008DE511 /* stdafx.cpp in Sources */,\n\t\t\t\tD53231C32142ADB9008DE511 /* FEBox.cpp in Sources */,\n\t\t\t\tD53231C02142ADB9008DE511 /* getapppath.cpp in Sources */,\n\t\t\t\tD54A704D242A3162007375FC /* FEBioConfig.cpp in Sources */,\n\t\t\t\tD52D841121CE8B3B00472620 /* config.cpp in Sources */,\n\t\t\t\tD5B29B2F22309413000ACD5E /* Logfile.cpp in Sources */,\n\t\t\t\tD5C765EC2368E8F60090EB43 /* memory.cpp in Sources */,\n\t\t\t\tD5B29B2D22309413000ACD5E /* LogStream.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD532319F2142ADA3008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD53231A02142ADA3008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD53231A22142ADA3008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD53231A32142ADA3008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD53231912142ADA3008DE511 /* Build configuration list for PBXProject \"FEBioLib\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD532319F2142ADA3008DE511 /* Debug */,\n\t\t\t\tD53231A02142ADA3008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD53231A12142ADA3008DE511 /* Build configuration list for PBXNativeTarget \"FEBioLib\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD53231A22142ADA3008DE511 /* Debug */,\n\t\t\t\tD53231A32142ADA3008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D532318E2142ADA3008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioLib/FEBioLib.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioLib.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioLib/FEBioLib.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioMech/FEBioMech.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD511EF13253A74AE00893F69 /* FEBioMechModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D511EF11253A74AE00893F69 /* FEBioMechModule.cpp */; };\n\t\tD511EF14253A74AE00893F69 /* FEBioMechModule.h in Headers */ = {isa = PBXBuildFile; fileRef = D511EF12253A74AE00893F69 /* FEBioMechModule.h */; };\n\t\tD5135C29232191C6008AFD7D /* FENaturalNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5135C27232191C6008AFD7D /* FENaturalNeoHookean.cpp */; };\n\t\tD5135C2A232191C6008AFD7D /* FENaturalNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5135C28232191C6008AFD7D /* FENaturalNeoHookean.h */; };\n\t\tD51E614C22443FDB0049F545 /* FEMaxStressCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D51E614A22443FDB0049F545 /* FEMaxStressCriterion.cpp */; };\n\t\tD51E614D22443FDB0049F545 /* FEMaxStressCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D51E614B22443FDB0049F545 /* FEMaxStressCriterion.h */; };\n\t\tD524F6B4258BEB6000A403CD /* FEHolzapfelUnconstrained.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D524F6B2258BEB6000A403CD /* FEHolzapfelUnconstrained.cpp */; };\n\t\tD524F6B5258BEB6000A403CD /* FEHolzapfelUnconstrained.h in Headers */ = {isa = PBXBuildFile; fileRef = D524F6B3258BEB6000A403CD /* FEHolzapfelUnconstrained.h */; };\n\t\tD52D841C21CE9A7600472620 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D841921CE9A7600472620 /* stdafx.cpp */; };\n\t\tD5322FE72142ACD9008DE511 /* FEDeformableSpringDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E442142ACD7008DE511 /* FEDeformableSpringDomain.cpp */; };\n\t\tD5322FE82142ACD9008DE511 /* FEExplicitSolidSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E452142ACD7008DE511 /* FEExplicitSolidSolver.h */; };\n\t\tD5322FE92142ACD9008DE511 /* FEPrescribedActiveContractionIsotropicUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E462142ACD7008DE511 /* FEPrescribedActiveContractionIsotropicUC.h */; };\n\t\tD5322FEA2142ACD9008DE511 /* FESRIElasticSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E472142ACD7008DE511 /* FESRIElasticSolidDomain.cpp */; };\n\t\tD5322FEB2142ACD9008DE511 /* FEFungOrthoCompressible.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E482142ACD7008DE511 /* FEFungOrthoCompressible.h */; };\n\t\tD5322FEC2142ACD9008DE511 /* FEMortarTiedContact.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E492142ACD7008DE511 /* FEMortarTiedContact.h */; };\n\t\tD5322FED2142ACD9008DE511 /* FEFiberIntegrationTriangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E4A2142ACD7008DE511 /* FEFiberIntegrationTriangle.cpp */; };\n\t\tD5322FEE2142ACD9008DE511 /* FEFiberExpPow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E4B2142ACD7008DE511 /* FEFiberExpPow.cpp */; };\n\t\tD5322FEF2142ACD9008DE511 /* FEResidualVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E4C2142ACD7008DE511 /* FEResidualVector.cpp */; };\n\t\tD5322FF02142ACD9008DE511 /* FEFiberExpLinear.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E4D2142ACD7008DE511 /* FEFiberExpLinear.h */; };\n\t\tD5322FF12142ACD9008DE511 /* geodesic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E4E2142ACD7008DE511 /* geodesic.h */; };\n\t\tD5322FF22142ACD9008DE511 /* FEUncoupledFiberExpLinear.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E4F2142ACD7008DE511 /* FEUncoupledFiberExpLinear.h */; };\n\t\tD5322FF32142ACD9008DE511 /* FEElasticANSShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E502142ACD7008DE511 /* FEElasticANSShellDomain.h */; };\n\t\tD5322FF42142ACD9008DE511 /* FERigidWallInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E512142ACD7008DE511 /* FERigidWallInterface.h */; };\n\t\tD5322FF52142ACD9008DE511 /* FETiedInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E522142ACD7008DE511 /* FETiedInterface.h */; };\n\t\tD5322FF62142ACD9008DE511 /* FEEllipsoidalFiberDistribution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E532142ACD7008DE511 /* FEEllipsoidalFiberDistribution.cpp */; };\n\t\tD5322FF72142ACD9008DE511 /* FEVonMisesPlasticity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E542142ACD7008DE511 /* FEVonMisesPlasticity.cpp */; };\n\t\tD5322FF82142ACD9008DE511 /* FEDistanceConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E552142ACD7008DE511 /* FEDistanceConstraint.cpp */; };\n\t\tD5322FF92142ACD9008DE511 /* FETiedElasticInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E562142ACD7008DE511 /* FETiedElasticInterface.h */; };\n\t\tD5322FFA2142ACD9008DE511 /* FEElasticMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E572142ACD7008DE511 /* FEElasticMaterial.h */; };\n\t\tD5322FFB2142ACD9008DE511 /* FEOrthotropicCLE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E582142ACD7008DE511 /* FEOrthotropicCLE.cpp */; };\n\t\tD5322FFC2142ACD9008DE511 /* FESymmetryPlane.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E592142ACD7008DE511 /* FESymmetryPlane.h */; };\n\t\tD5322FFD2142ACD9008DE511 /* FEMergedConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E5A2142ACD7008DE511 /* FEMergedConstraint.h */; };\n\t\tD5322FFE2142ACD9008DE511 /* FEFiberPowLinear.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E5B2142ACD7008DE511 /* FEFiberPowLinear.h */; };\n\t\tD5322FFF2142ACD9008DE511 /* FEVolumeConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E5C2142ACD7008DE511 /* FEVolumeConstraint.cpp */; };\n\t\tD53230002142ACD9008DE511 /* FEElasticShellDomainOld.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E5D2142ACD7008DE511 /* FEElasticShellDomainOld.h */; };\n\t\tD53230012142ACD9008DE511 /* FEEFDNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E5E2142ACD7008DE511 /* FEEFDNeoHookean.cpp */; };\n\t\tD53230022142ACD9008DE511 /* FERigidShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E5F2142ACD7008DE511 /* FERigidShellDomain.h */; };\n\t\tD53230032142ACD9008DE511 /* FEMuscleMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E602142ACD7008DE511 /* FEMuscleMaterial.h */; };\n\t\tD53230042142ACD9008DE511 /* FEFiberIntegrationGeodesic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E612142ACD7008DE511 /* FEFiberIntegrationGeodesic.h */; };\n\t\tD53230052142ACD9008DE511 /* FERigidShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E622142ACD7008DE511 /* FERigidShellDomain.cpp */; };\n\t\tD53230062142ACD9008DE511 /* FEFungOrthotropic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E632142ACD7008DE511 /* FEFungOrthotropic.cpp */; };\n\t\tD53230072142ACD9008DE511 /* FE2DTransIsoMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E642142ACD7008DE511 /* FE2DTransIsoMooneyRivlin.h */; };\n\t\tD53230082142ACD9008DE511 /* FEElasticFiberMaterialUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E652142ACD7008DE511 /* FEElasticFiberMaterialUC.cpp */; };\n\t\tD53230092142ACD9008DE511 /* FESphericalFiberDistribution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E662142ACD7008DE511 /* FESphericalFiberDistribution.cpp */; };\n\t\tD532300A2142ACD9008DE511 /* FECoupledTransIsoVerondaWestmann.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E672142ACD7008DE511 /* FECoupledTransIsoVerondaWestmann.h */; };\n\t\tD532300B2142ACD9008DE511 /* FEAugLagLinearConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E682142ACD7008DE511 /* FEAugLagLinearConstraint.h */; };\n\t\tD532300C2142ACD9008DE511 /* FEWrinkleOgdenMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E692142ACD7008DE511 /* FEWrinkleOgdenMaterial.h */; };\n\t\tD532300D2142ACD9008DE511 /* FEContactSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E6A2142ACD7008DE511 /* FEContactSurface.h */; };\n\t\tD532300E2142ACD9008DE511 /* FEPeriodicBoundary1O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E6B2142ACD7008DE511 /* FEPeriodicBoundary1O.h */; };\n\t\tD532300F2142ACD9008DE511 /* FEPeriodicLinearConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E6C2142ACD7008DE511 /* FEPeriodicLinearConstraint.h */; };\n\t\tD53230102142ACD9008DE511 /* FECellGrowth.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E6D2142ACD7008DE511 /* FECellGrowth.h */; };\n\t\tD53230112142ACD9008DE511 /* FEFiberIntegrationTrapezoidal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E6E2142ACD7008DE511 /* FEFiberIntegrationTrapezoidal.cpp */; };\n\t\tD53230122142ACD9008DE511 /* FERigidCable.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E6F2142ACD7008DE511 /* FERigidCable.h */; };\n\t\tD53230132142ACD9008DE511 /* FEPeriodicBoundary.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E702142ACD7008DE511 /* FEPeriodicBoundary.h */; };\n\t\tD53230142142ACD9008DE511 /* FEFiberIntegrationGaussKronrod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E712142ACD7008DE511 /* FEFiberIntegrationGaussKronrod.h */; };\n\t\tD53230152142ACD9008DE511 /* FESolidDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E722142ACD7008DE511 /* FESolidDomainFactory.cpp */; };\n\t\tD53230162142ACD9008DE511 /* FERigidRevoluteJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E732142ACD7008DE511 /* FERigidRevoluteJoint.h */; };\n\t\tD53230172142ACD9008DE511 /* FESlidingInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E742142ACD7008DE511 /* FESlidingInterface.cpp */; };\n\t\tD53230192142ACD9008DE511 /* FERVEModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E762142ACD7008DE511 /* FERVEModel.h */; };\n\t\tD532301A2142ACD9008DE511 /* FECoupledTransIsoMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E772142ACD7008DE511 /* FECoupledTransIsoMooneyRivlin.cpp */; };\n\t\tD532301B2142ACD9008DE511 /* FEUDGHexDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E782142ACD7008DE511 /* FEUDGHexDomain.h */; };\n\t\tD532301D2142ACD9008DE511 /* FEPointBodyForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E7A2142ACD7008DE511 /* FEPointBodyForce.cpp */; };\n\t\tD532301E2142ACD9008DE511 /* FECarterHayesOld.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E7B2142ACD7008DE511 /* FECarterHayesOld.h */; };\n\t\tD532301F2142ACD9008DE511 /* FEContinuousFiberDistribution.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E7C2142ACD7008DE511 /* FEContinuousFiberDistribution.h */; };\n\t\tD53230202142ACD9008DE511 /* FEViscousMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E7D2142ACD7008DE511 /* FEViscousMaterialPoint.h */; };\n\t\tD53230212142ACD9008DE511 /* FERemodelingElasticDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E7E2142ACD7008DE511 /* FERemodelingElasticDomain.cpp */; };\n\t\tD53230222142ACD9008DE511 /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E7F2142ACD7008DE511 /* stdafx.h */; };\n\t\tD53230232142ACD9008DE511 /* FEBioMechPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E802142ACD7008DE511 /* FEBioMechPlot.h */; };\n\t\tD53230252142ACD9008DE511 /* FEPeriodicLinearConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E822142ACD7008DE511 /* FEPeriodicLinearConstraint.cpp */; };\n\t\tD53230262142ACD9008DE511 /* FEMicroMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E832142ACD7008DE511 /* FEMicroMaterial.h */; };\n\t\tD53230272142ACD9008DE511 /* FEFiberIntegrationGaussKronrod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E842142ACD7008DE511 /* FEFiberIntegrationGaussKronrod.cpp */; };\n\t\tD53230282142ACD9008DE511 /* FEPressureLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E852142ACD7008DE511 /* FEPressureLoad.cpp */; };\n\t\tD53230292142ACD9008DE511 /* FEDonnanEquilibrium.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E862142ACD7008DE511 /* FEDonnanEquilibrium.h */; };\n\t\tD532302A2142ACD9008DE511 /* FETendonMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E872142ACD7008DE511 /* FETendonMaterial.cpp */; };\n\t\tD532302B2142ACD9008DE511 /* FEFiberExpLinear.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E882142ACD7008DE511 /* FEFiberExpLinear.cpp */; };\n\t\tD532302C2142ACD9008DE511 /* FECellGrowth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E892142ACD7008DE511 /* FECellGrowth.cpp */; };\n\t\tD532302D2142ACD9008DE511 /* FERigidSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E8A2142ACD7008DE511 /* FERigidSolidDomain.h */; };\n\t\tD532302E2142ACD9008DE511 /* FEMicroMaterial2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E8B2142ACD7008DE511 /* FEMicroMaterial2O.h */; };\n\t\tD532302F2142ACD9008DE511 /* FEDamageCDF.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E8C2142ACD7008DE511 /* FEDamageCDF.h */; };\n\t\tD53230302142ACD9008DE511 /* FEUT4Domain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E8D2142ACD7008DE511 /* FEUT4Domain.h */; };\n\t\tD53230312142ACD9008DE511 /* FEStVenantKirchhoff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E8E2142ACD7008DE511 /* FEStVenantKirchhoff.cpp */; };\n\t\tD53230322142ACD9008DE511 /* FETractionLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E8F2142ACD7008DE511 /* FETractionLoad.h */; };\n\t\tD53230332142ACD9008DE511 /* FERigidCylindricalJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E902142ACD7008DE511 /* FERigidCylindricalJoint.h */; };\n\t\tD53230342142ACD9008DE511 /* FESymmetryPlane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E912142ACD7008DE511 /* FESymmetryPlane.cpp */; };\n\t\tD53230352142ACD9008DE511 /* FEMortarSlidingContact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E922142ACD7008DE511 /* FEMortarSlidingContact.cpp */; };\n\t\tD53230362142ACD9008DE511 /* FEHolzapfelGasserOgden.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E932142ACD7008DE511 /* FEHolzapfelGasserOgden.h */; };\n\t\tD53230372142ACD9008DE511 /* FENeoHookeanTransIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E942142ACD7008DE511 /* FENeoHookeanTransIso.h */; };\n\t\tD53230382142ACD9008DE511 /* gausskronrod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E952142ACD7008DE511 /* gausskronrod.h */; };\n\t\tD53230392142ACD9008DE511 /* FEBondRelaxation.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E962142ACD7008DE511 /* FEBondRelaxation.h */; };\n\t\tD532303A2142ACD9008DE511 /* FEContinuousFiberDistributionUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E972142ACD7008DE511 /* FEContinuousFiberDistributionUC.h */; };\n\t\tD532303B2142ACD9008DE511 /* FERigidSpring.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E982142ACD7008DE511 /* FERigidSpring.cpp */; };\n\t\tD532303C2142ACD9008DE511 /* FEMortarContactSurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E992142ACD7008DE511 /* FEMortarContactSurface.cpp */; };\n\t\tD532303D2142ACD9008DE511 /* FERigidPlanarJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E9A2142ACD7008DE511 /* FERigidPlanarJoint.h */; };\n\t\tD532303E2142ACD9008DE511 /* FEDamageMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E9B2142ACD7008DE511 /* FEDamageMooneyRivlin.h */; };\n\t\tD532303F2142ACD9008DE511 /* FEFiberIntegrationScheme.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E9C2142ACD7008DE511 /* FEFiberIntegrationScheme.cpp */; };\n\t\tD53230402142ACD9008DE511 /* FEDamageNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E9D2142ACD7008DE511 /* FEDamageNeoHookean.h */; };\n\t\tD53230412142ACD9008DE511 /* FEActiveFiberContraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322E9E2142ACD7008DE511 /* FEActiveFiberContraction.cpp */; };\n\t\tD53230422142ACD9008DE511 /* FESolidSolver2.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322E9F2142ACD7008DE511 /* FESolidSolver2.h */; };\n\t\tD53230432142ACD9008DE511 /* FESlidingElasticInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EA02142ACD7008DE511 /* FESlidingElasticInterface.cpp */; };\n\t\tD53230442142ACD9008DE511 /* FEContactInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EA12142ACD7008DE511 /* FEContactInterface.h */; };\n\t\tD53230452142ACD9008DE511 /* FERigidJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EA22142ACD7008DE511 /* FERigidJoint.h */; };\n\t\tD53230462142ACD9008DE511 /* FEReactiveViscoelastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EA32142ACD7008DE511 /* FEReactiveViscoelastic.h */; };\n\t\tD53230472142ACD9008DE511 /* FEElasticMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EA42142ACD7008DE511 /* FEElasticMaterial.cpp */; };\n\t\tD53230482142ACD9008DE511 /* FEDamageTransIsoMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EA52142ACD7008DE511 /* FEDamageTransIsoMooneyRivlin.cpp */; };\n\t\tD53230492142ACD9008DE511 /* FEFiberExpPow.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EA62142ACD7008DE511 /* FEFiberExpPow.h */; };\n\t\tD532304A2142ACD9008DE511 /* FEDamageMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EA72142ACD7008DE511 /* FEDamageMaterialPoint.cpp */; };\n\t\tD532304B2142ACD9008DE511 /* FECoupledTransIsoVerondaWestmann.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EA82142ACD7008DE511 /* FECoupledTransIsoVerondaWestmann.cpp */; };\n\t\tD532304C2142ACD9008DE511 /* FERigidPlanarJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EA92142ACD7008DE511 /* FERigidPlanarJoint.cpp */; };\n\t\tD532304D2142ACD9008DE511 /* FEStVenantKirchhoff.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EAA2142ACD7008DE511 /* FEStVenantKirchhoff.h */; };\n\t\tD532304F2142ACD9008DE511 /* FEElasticMultigeneration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EAC2142ACD7008DE511 /* FEElasticMultigeneration.cpp */; };\n\t\tD53230502142ACD9008DE511 /* FEPRLig.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EAD2142ACD7008DE511 /* FEPRLig.h */; };\n\t\tD53230512142ACD9008DE511 /* FEBCPrescribedDeformation.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EAE2142ACD7008DE511 /* FEBCPrescribedDeformation.h */; };\n\t\tD53230522142ACD9008DE511 /* FEElasticDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EAF2142ACD7008DE511 /* FEElasticDomain.h */; };\n\t\tD53230532142ACD9008DE511 /* FEElasticFiberMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EB02142ACD7008DE511 /* FEElasticFiberMaterial.h */; };\n\t\tD53230542142ACD9008DE511 /* FERigidPrismaticJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EB12142ACD7008DE511 /* FERigidPrismaticJoint.cpp */; };\n\t\tD53230552142ACD9008DE511 /* FEIsotropicElastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EB22142ACD7008DE511 /* FEIsotropicElastic.cpp */; };\n\t\tD53230572142ACD9008DE511 /* FEPrescribedActiveContractionUniaxial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EB42142ACD7008DE511 /* FEPrescribedActiveContractionUniaxial.cpp */; };\n\t\tD53230592142ACD9008DE511 /* FE2DTransIsoMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EB62142ACD7008DE511 /* FE2DTransIsoMooneyRivlin.cpp */; };\n\t\tD532305A2142ACD9008DE511 /* FESpringMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EB72142ACD7008DE511 /* FESpringMaterial.cpp */; };\n\t\tD532305B2142ACD9008DE511 /* FEElasticMultiscaleDomain1O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EB82142ACD7008DE511 /* FEElasticMultiscaleDomain1O.cpp */; };\n\t\tD532305C2142ACD9008DE511 /* FEEFDVerondaWestmann.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EB92142ACD7008DE511 /* FEEFDVerondaWestmann.h */; };\n\t\tD532305D2142ACD9008DE511 /* FEFiberExpPowUncoupled.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EBA2142ACD7008DE511 /* FEFiberExpPowUncoupled.h */; };\n\t\tD532305E2142ACD9008DE511 /* FEVolumeConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EBB2142ACD7008DE511 /* FEVolumeConstraint.h */; };\n\t\tD532305F2142ACD9008DE511 /* FEViscoElasticMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EBC2142ACD7008DE511 /* FEViscoElasticMaterial.h */; };\n\t\tD53230602142ACD9008DE511 /* FECentrifugalBodyForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EBD2142ACD7008DE511 /* FECentrifugalBodyForce.cpp */; };\n\t\tD53230612142ACD9008DE511 /* FENeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EBE2142ACD7008DE511 /* FENeoHookean.cpp */; };\n\t\tD53230622142ACD9008DE511 /* FEFiberExpPowUncoupled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EBF2142ACD7008DE511 /* FEFiberExpPowUncoupled.cpp */; };\n\t\tD53230632142ACD9008DE511 /* FEActiveFiberContraction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC02142ACD7008DE511 /* FEActiveFiberContraction.h */; };\n\t\tD53230642142ACD9008DE511 /* FERigidConnector.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC12142ACD7008DE511 /* FERigidConnector.h */; };\n\t\tD53230652142ACD9008DE511 /* FE2OMicroConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EC22142ACD7008DE511 /* FE2OMicroConstraint.cpp */; };\n\t\tD53230662142ACD9008DE511 /* FERigidSlidingContact.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC32142ACD7008DE511 /* FERigidSlidingContact.h */; };\n\t\tD53230672142ACD9008DE511 /* FEFacet2FacetSliding.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC42142ACD7008DE511 /* FEFacet2FacetSliding.h */; };\n\t\tD53230682142ACD9008DE511 /* FERigidLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EC52142ACD7008DE511 /* FERigidLock.cpp */; };\n\t\tD53230692142ACD9008DE511 /* FEMembraneMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC62142ACD7008DE511 /* FEMembraneMaterial.h */; };\n\t\tD532306A2142ACD9008DE511 /* FECoupledVerondaWestmann.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC72142ACD7008DE511 /* FECoupledVerondaWestmann.h */; };\n\t\tD532306C2142ACD9008DE511 /* FETrussMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EC92142ACD7008DE511 /* FETrussMaterial.h */; };\n\t\tD532306D2142ACD9008DE511 /* FERigidSphericalJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ECA2142ACD7008DE511 /* FERigidSphericalJoint.cpp */; };\n\t\tD532306E2142ACD9008DE511 /* FERigidForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ECB2142ACD7008DE511 /* FERigidForce.cpp */; };\n\t\tD532306F2142ACD9008DE511 /* FERigidSphericalJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ECC2142ACD7008DE511 /* FERigidSphericalJoint.h */; };\n\t\tD53230702142ACD9008DE511 /* FEBodyForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ECD2142ACD7008DE511 /* FEBodyForce.h */; };\n\t\tD53230712142ACD9008DE511 /* FEPeriodicBoundary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ECE2142ACD7008DE511 /* FEPeriodicBoundary.cpp */; };\n\t\tD53230722142ACD9008DE511 /* FEVonMisesPlasticity.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ECF2142ACD7008DE511 /* FEVonMisesPlasticity.h */; };\n\t\tD53230732142ACD9008DE511 /* FEMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ED02142ACD7008DE511 /* FEMooneyRivlin.cpp */; };\n\t\tD53230742142ACD9008DE511 /* FEPrescribedActiveContractionIsotropicUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ED12142ACD7008DE511 /* FEPrescribedActiveContractionIsotropicUC.cpp */; };\n\t\tD53230752142ACD9008DE511 /* FE2DFiberNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ED22142ACD7008DE511 /* FE2DFiberNeoHookean.h */; };\n\t\tD53230762142ACD9008DE511 /* FERemodelingElasticDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ED32142ACD7008DE511 /* FERemodelingElasticDomain.h */; };\n\t\tD53230772142ACD9008DE511 /* FEFiberIntegrationGauss.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ED42142ACD7008DE511 /* FEFiberIntegrationGauss.h */; };\n\t\tD53230782142ACD9008DE511 /* FEDiscreteContact.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ED52142ACD7008DE511 /* FEDiscreteContact.h */; };\n\t\tD53230792142ACD9008DE511 /* FEIncompNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ED62142ACD7008DE511 /* FEIncompNeoHookean.cpp */; };\n\t\tD532307A2142ACD9008DE511 /* FEDiscreteContact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ED72142ACD7008DE511 /* FEDiscreteContact.cpp */; };\n\t\tD532307B2142ACD9008DE511 /* FEElasticMixture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322ED82142ACD7008DE511 /* FEElasticMixture.cpp */; };\n\t\tD532307C2142ACD9008DE511 /* FEOrthotropicCLE.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322ED92142ACD7008DE511 /* FEOrthotropicCLE.h */; };\n\t\tD532307D2142ACD9008DE511 /* FEElasticShellDomainOld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EDA2142ACD7008DE511 /* FEElasticShellDomainOld.cpp */; };\n\t\tD532307F2142ACD9008DE511 /* FEFacet2FacetSliding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EDC2142ACD7008DE511 /* FEFacet2FacetSliding.cpp */; };\n\t\tD53230802142ACD9008DE511 /* FEPrescribedActiveContractionTransIsoUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EDD2142ACD7008DE511 /* FEPrescribedActiveContractionTransIsoUC.h */; };\n\t\tD53230812142ACD9008DE511 /* FEMortarInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EDE2142ACD7008DE511 /* FEMortarInterface.h */; };\n\t\tD53230822142ACD9008DE511 /* FEFungOrthotropic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EDF2142ACD7008DE511 /* FEFungOrthotropic.h */; };\n\t\tD53230832142ACD9008DE511 /* FEElasticFiberMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EE02142ACD7008DE511 /* FEElasticFiberMaterial.cpp */; };\n\t\tD53230842142ACD9008DE511 /* FEUncoupledElasticMixture.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EE12142ACD7008DE511 /* FEUncoupledElasticMixture.h */; };\n\t\tD53230872142ACD9008DE511 /* FEElasticMixture.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EE42142ACD7008DE511 /* FEElasticMixture.h */; };\n\t\tD53230882142ACD9008DE511 /* FEDamageTransIsoMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EE52142ACD7008DE511 /* FEDamageTransIsoMooneyRivlin.h */; };\n\t\tD53230892142ACD9008DE511 /* FEOgdenUnconstrained.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EE62142ACD7008DE511 /* FEOgdenUnconstrained.h */; };\n\t\tD532308A2142ACD9008DE511 /* FEPrescribedNormalDisplacement.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EE72142ACD7008DE511 /* FEPrescribedNormalDisplacement.h */; };\n\t\tD532308B2142ACD9008DE511 /* FEEFDDonnanEquilibrium.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EE82142ACD7008DE511 /* FEEFDDonnanEquilibrium.cpp */; };\n\t\tD532308D2142ACD9008DE511 /* FESlidingInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EEA2142ACD7008DE511 /* FESlidingInterface.h */; };\n\t\tD532308E2142ACD9008DE511 /* FEPeriodicLinearConstraint2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EEB2142ACD7008DE511 /* FEPeriodicLinearConstraint2O.cpp */; };\n\t\tD532308F2142ACD9008DE511 /* FEElasticSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EEC2142ACD7008DE511 /* FEElasticSolidDomain.cpp */; };\n\t\tD53230902142ACD9008DE511 /* FEGentMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EED2142ACD7008DE511 /* FEGentMaterial.cpp */; };\n\t\tD53230912142ACD9008DE511 /* FESRIElasticSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EEE2142ACD7008DE511 /* FESRIElasticSolidDomain.h */; };\n\t\tD53230932142ACD9008DE511 /* FEUncoupledMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF02142ACD7008DE511 /* FEUncoupledMaterial.cpp */; };\n\t\tD53230942142ACD9008DE511 /* FEPrescribedNormalDisplacement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF12142ACD7008DE511 /* FEPrescribedNormalDisplacement.cpp */; };\n\t\tD53230952142ACD9008DE511 /* FERVEModel2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF22142ACD7008DE511 /* FERVEModel2O.cpp */; };\n\t\tD53230962142ACD9008DE511 /* FESlidingElasticInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EF32142ACD7008DE511 /* FESlidingElasticInterface.h */; };\n\t\tD53230972142ACD9008DE511 /* FEEFDVerondaWestmann.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF42142ACD7008DE511 /* FEEFDVerondaWestmann.cpp */; };\n\t\tD53230982142ACD9008DE511 /* FETiedContactSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EF52142ACD8008DE511 /* FETiedContactSurface.h */; };\n\t\tD53230992142ACD9008DE511 /* FERigidMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF62142ACD8008DE511 /* FERigidMaterial.cpp */; };\n\t\tD532309A2142ACD9008DE511 /* FENewtonianViscousSolidUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EF72142ACD8008DE511 /* FENewtonianViscousSolidUC.h */; };\n\t\tD532309B2142ACD9008DE511 /* FEPeriodicBoundary1O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF82142ACD8008DE511 /* FEPeriodicBoundary1O.cpp */; };\n\t\tD532309C2142ACD9008DE511 /* FEDamageCDF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EF92142ACD8008DE511 /* FEDamageCDF.cpp */; };\n\t\tD532309D2142ACD9008DE511 /* FERigidWallInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EFA2142ACD8008DE511 /* FERigidWallInterface.cpp */; };\n\t\tD532309F2142ACD9008DE511 /* FECoupledMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EFC2142ACD8008DE511 /* FECoupledMooneyRivlin.cpp */; };\n\t\tD53230A02142ACD9008DE511 /* FEOgdenMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322EFD2142ACD8008DE511 /* FEOgdenMaterial.cpp */; };\n\t\tD53230A22142ACD9008DE511 /* FESolidDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322EFF2142ACD8008DE511 /* FESolidDomainFactory.h */; };\n\t\tD53230A32142ACD9008DE511 /* FERigidRevoluteJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F002142ACD8008DE511 /* FERigidRevoluteJoint.cpp */; };\n\t\tD53230A42142ACD9008DE511 /* FERigidSpring.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F012142ACD8008DE511 /* FERigidSpring.h */; };\n\t\tD53230A52142ACD9008DE511 /* FEEFDNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F022142ACD8008DE511 /* FEEFDNeoHookean.h */; };\n\t\tD53230A62142ACD9008DE511 /* FESolidSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F032142ACD8008DE511 /* FESolidSolver.cpp */; };\n\t\tD53230A72142ACD9008DE511 /* FEOsmoticVirialExpansion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F042142ACD8008DE511 /* FEOsmoticVirialExpansion.h */; };\n\t\tD53230A82142ACD9008DE511 /* FEDamageMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F052142ACD8008DE511 /* FEDamageMaterial.cpp */; };\n\t\tD53230A92142ACD9008DE511 /* FEMortarInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F062142ACD8008DE511 /* FEMortarInterface.cpp */; };\n\t\tD53230AA2142ACD9008DE511 /* FEMicroMaterial2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F072142ACD8008DE511 /* FEMicroMaterial2O.cpp */; };\n\t\tD53230AB2142ACD9008DE511 /* FEHolmesMow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F082142ACD8008DE511 /* FEHolmesMow.cpp */; };\n\t\tD53230AD2142ACD9008DE511 /* FEBioMech.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F0A2142ACD8008DE511 /* FEBioMech.h */; };\n\t\tD53230AE2142ACD9008DE511 /* FEOrthoElastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F0B2142ACD8008DE511 /* FEOrthoElastic.h */; };\n\t\tD53230AF2142ACD9008DE511 /* FEUncoupledReactiveViscoelastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F0C2142ACD8008DE511 /* FEUncoupledReactiveViscoelastic.cpp */; };\n\t\tD53230B02142ACD9008DE511 /* FEStickyInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F0D2142ACD8008DE511 /* FEStickyInterface.h */; };\n\t\tD53230B12142ACD9008DE511 /* FE2OMicroConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F0E2142ACD8008DE511 /* FE2OMicroConstraint.h */; };\n\t\tD53230B22142ACD9008DE511 /* FEOgdenMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F0F2142ACD8008DE511 /* FEOgdenMaterial.h */; };\n\t\tD53230B32142ACD9008DE511 /* FECarterHayesOld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F102142ACD8008DE511 /* FECarterHayesOld.cpp */; };\n\t\tD53230B42142ACD9008DE511 /* FEMembraneMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F112142ACD8008DE511 /* FEMembraneMaterial.cpp */; };\n\t\tD53230B52142ACD9008DE511 /* FERigidConnector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F122142ACD8008DE511 /* FERigidConnector.cpp */; };\n\t\tD53230B62142ACD9008DE511 /* FEElasticTrussDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F132142ACD8008DE511 /* FEElasticTrussDomain.h */; };\n\t\tD53230B72142ACD9008DE511 /* FEFungOrthoCompressible.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F142142ACD8008DE511 /* FEFungOrthoCompressible.cpp */; };\n\t\tD53230B82142ACD9008DE511 /* FEReactiveViscoelastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F152142ACD8008DE511 /* FEReactiveViscoelastic.cpp */; };\n\t\tD53230B92142ACD9008DE511 /* FEElasticMultiscaleDomain2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F162142ACD8008DE511 /* FEElasticMultiscaleDomain2O.h */; };\n\t\tD53230BA2142ACD9008DE511 /* FEPrescribedActiveContractionTransIsoUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F172142ACD8008DE511 /* FEPrescribedActiveContractionTransIsoUC.cpp */; };\n\t\tD53230BB2142ACD9008DE511 /* FEFiberPowLinearUncoupled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F182142ACD8008DE511 /* FEFiberPowLinearUncoupled.cpp */; };\n\t\tD53230BC2142ACD9008DE511 /* FERigidSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F192142ACD8008DE511 /* FERigidSolver.cpp */; };\n\t\tD53230BD2142ACD9008DE511 /* FEHolzapfelGasserOgden.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F1A2142ACD8008DE511 /* FEHolzapfelGasserOgden.cpp */; };\n\t\tD53230BE2142ACD9008DE511 /* FEPrescribedActiveContractionUniaxial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F1B2142ACD8008DE511 /* FEPrescribedActiveContractionUniaxial.h */; };\n\t\tD53230BF2142ACD9008DE511 /* FERVEModel2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F1C2142ACD8008DE511 /* FERVEModel2O.h */; };\n\t\tD53230C02142ACD9008DE511 /* FE3FieldElasticShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F1D2142ACD8008DE511 /* FE3FieldElasticShellDomain.cpp */; };\n\t\tD53230C12142ACD9008DE511 /* FESphericalFiberDistribution.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F1E2142ACD8008DE511 /* FESphericalFiberDistribution.h */; };\n\t\tD53230C22142ACD9008DE511 /* FEContactSurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F1F2142ACD8008DE511 /* FEContactSurface.cpp */; };\n\t\tD53230C32142ACD9008DE511 /* FEFiberMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F202142ACD8008DE511 /* FEFiberMaterialPoint.h */; };\n\t\tD53230C42142ACD9008DE511 /* FEFiberDensityDistribution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F212142ACD8008DE511 /* FEFiberDensityDistribution.cpp */; };\n\t\tD53230C52142ACD9008DE511 /* FEBioMechData.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F222142ACD8008DE511 /* FEBioMechData.h */; };\n\t\tD53230C62142ACD9008DE511 /* FEPeriodicSurfaceConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F232142ACD8008DE511 /* FEPeriodicSurfaceConstraint.h */; };\n\t\tD53230C72142ACD9008DE511 /* FEMicroMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F242142ACD8008DE511 /* FEMicroMaterial.cpp */; };\n\t\tD53230C82142ACD9008DE511 /* FEMortarContactSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F252142ACD8008DE511 /* FEMortarContactSurface.h */; };\n\t\tD53230C92142ACD9008DE511 /* FEWrinkleOgdenMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F262142ACD8008DE511 /* FEWrinkleOgdenMaterial.cpp */; };\n\t\tD53230CA2142ACD9008DE511 /* FEElasticMultigeneration.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F272142ACD8008DE511 /* FEElasticMultigeneration.h */; };\n\t\tD53230CB2142ACD9008DE511 /* FE2DTransIsoVerondaWestmann.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F282142ACD8008DE511 /* FE2DTransIsoVerondaWestmann.cpp */; };\n\t\tD53230CD2142ACD9008DE511 /* FESSIShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F2A2142ACD8008DE511 /* FESSIShellDomain.h */; };\n\t\tD53230CF2142ACD9008DE511 /* FEOrthoElastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F2C2142ACD8008DE511 /* FEOrthoElastic.cpp */; };\n\t\tD53230D02142ACD9008DE511 /* FEBCPrescribedDeformation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F2D2142ACD8008DE511 /* FEBCPrescribedDeformation.cpp */; };\n\t\tD53230D12142ACD9008DE511 /* FENewtonianViscousSolid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F2E2142ACD8008DE511 /* FENewtonianViscousSolid.h */; };\n\t\tD53230D22142ACD9008DE511 /* FEMortarSlidingContact.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F2F2142ACD8008DE511 /* FEMortarSlidingContact.h */; };\n\t\tD53230D42142ACD9008DE511 /* FEEFDMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F312142ACD8008DE511 /* FEEFDMooneyRivlin.h */; };\n\t\tD53230D52142ACD9008DE511 /* FEFiberMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F322142ACD8008DE511 /* FEFiberMaterialPoint.cpp */; };\n\t\tD53230D62142ACD9008DE511 /* FEElasticShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F332142ACD8008DE511 /* FEElasticShellDomain.h */; };\n\t\tD53230D72142ACD9008DE511 /* FEBioMech.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F342142ACD8008DE511 /* FEBioMech.cpp */; };\n\t\tD53230D82142ACD9008DE511 /* FEUncoupledActiveContraction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F352142ACD8008DE511 /* FEUncoupledActiveContraction.h */; };\n\t\tD53230D92142ACD9008DE511 /* FEMuscleMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F362142ACD8008DE511 /* FEMuscleMaterial.cpp */; };\n\t\tD53230DA2142ACD9008DE511 /* FEPeriodicLinearConstraint2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F372142ACD8008DE511 /* FEPeriodicLinearConstraint2O.h */; };\n\t\tD53230DB2142ACD9008DE511 /* FEFiberEFDNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F382142ACD8008DE511 /* FEFiberEFDNeoHookean.h */; };\n\t\tD53230DC2142ACD9008DE511 /* FEVerondaWestmann.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F392142ACD8008DE511 /* FEVerondaWestmann.cpp */; };\n\t\tD53230DD2142ACD9008DE511 /* FEVerondaWestmann.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F3A2142ACD8008DE511 /* FEVerondaWestmann.h */; };\n\t\tD53230DE2142ACD9008DE511 /* FEDistanceConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F3B2142ACD8008DE511 /* FEDistanceConstraint.h */; };\n\t\tD53230DF2142ACD9008DE511 /* FEElasticDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F3C2142ACD8008DE511 /* FEElasticDomain.cpp */; };\n\t\tD53230E02142ACD9008DE511 /* FEEllipsoidalFiberDistribution.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F3D2142ACD8008DE511 /* FEEllipsoidalFiberDistribution.h */; };\n\t\tD53230E12142ACD9008DE511 /* FETransIsoMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F3E2142ACD8008DE511 /* FETransIsoMooneyRivlin.h */; };\n\t\tD53230E22142ACD9008DE511 /* triangle_sphere.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F3F2142ACD8008DE511 /* triangle_sphere.h */; };\n\t\tD53230E32142ACD9008DE511 /* FEPrescribedActiveContractionUniaxialUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F402142ACD8008DE511 /* FEPrescribedActiveContractionUniaxialUC.cpp */; };\n\t\tD53230E42142ACD9008DE511 /* FEElasticMaterial2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F412142ACD8008DE511 /* FEElasticMaterial2O.h */; };\n\t\tD53230E52142ACD9008DE511 /* FEFiberIntegrationScheme.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F422142ACD8008DE511 /* FEFiberIntegrationScheme.h */; };\n\t\tD53230E62142ACD9008DE511 /* FEHuiskesSupply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F432142ACD8008DE511 /* FEHuiskesSupply.cpp */; };\n\t\tD53230E72142ACD9008DE511 /* FEArrudaBoyce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F442142ACD8008DE511 /* FEArrudaBoyce.cpp */; };\n\t\tD53230E82142ACD9008DE511 /* FEFiberDensityDistribution.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F452142ACD8008DE511 /* FEFiberDensityDistribution.h */; };\n\t\tD53230E92142ACD9008DE511 /* FEHolmesMow.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F462142ACD8008DE511 /* FEHolmesMow.h */; };\n\t\tD53230EA2142ACD9008DE511 /* FEElasticMultiscaleDomain1O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F472142ACD8008DE511 /* FEElasticMultiscaleDomain1O.h */; };\n\t\tD53230EB2142ACD9008DE511 /* FEPeriodicBoundary2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F482142ACD8008DE511 /* FEPeriodicBoundary2O.cpp */; };\n\t\tD53230ED2142ACD9008DE511 /* FECubicCLE.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F4A2142ACD8008DE511 /* FECubicCLE.h */; };\n\t\tD53230EE2142ACD9008DE511 /* FEPressureLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F4B2142ACD8008DE511 /* FEPressureLoad.h */; };\n\t\tD53230EF2142ACD9008DE511 /* FEFacet2FacetTied.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F4C2142ACD8008DE511 /* FEFacet2FacetTied.h */; };\n\t\tD53230F12142ACD9008DE511 /* FEElasticMaterial2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F4E2142ACD8008DE511 /* FEElasticMaterial2O.cpp */; };\n\t\tD53230F32142ACD9008DE511 /* FEEFDUncoupled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F502142ACD8008DE511 /* FEEFDUncoupled.cpp */; };\n\t\tD53230F42142ACD9008DE511 /* FEGentMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F512142ACD8008DE511 /* FEGentMaterial.h */; };\n\t\tD53230F52142ACD9008DE511 /* FEDamageCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F522142ACD8008DE511 /* FEDamageCriterion.h */; };\n\t\tD53230F62142ACD9008DE511 /* FEContinuousFiberDistribution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F532142ACD8008DE511 /* FEContinuousFiberDistribution.cpp */; };\n\t\tD53230F82142ACD9008DE511 /* FEFiberEFDNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F552142ACD8008DE511 /* FEFiberEFDNeoHookean.cpp */; };\n\t\tD53230F92142ACD9008DE511 /* FETrussMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F562142ACD8008DE511 /* FETrussMaterial.cpp */; };\n\t\tD53230FA2142ACD9008DE511 /* FEBioMechData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F572142ACD8008DE511 /* FEBioMechData.cpp */; };\n\t\tD53230FC2142ACD9008DE511 /* FEDonnanEquilibrium.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F592142ACD8008DE511 /* FEDonnanEquilibrium.cpp */; };\n\t\tD53230FD2142ACD9008DE511 /* FEOgdenUnconstrained.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F5A2142ACD8008DE511 /* FEOgdenUnconstrained.cpp */; };\n\t\tD53230FE2142ACD9008DE511 /* FETendonMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F5B2142ACD8008DE511 /* FETendonMaterial.h */; };\n\t\tD53230FF2142ACD9008DE511 /* FEDamageMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F5C2142ACD8008DE511 /* FEDamageMaterialPoint.h */; };\n\t\tD53231002142ACD9008DE511 /* FERigidAngularDamper.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F5D2142ACD8008DE511 /* FERigidAngularDamper.h */; };\n\t\tD53231012142ACD9008DE511 /* FETiedElasticInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F5E2142ACD8008DE511 /* FETiedElasticInterface.cpp */; };\n\t\tD53231022142ACD9008DE511 /* FERemodelingElasticMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F5F2142ACD8008DE511 /* FERemodelingElasticMaterial.h */; };\n\t\tD53231032142ACD9008DE511 /* FEFiberIntegrationTrapezoidal.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F602142ACD8008DE511 /* FEFiberIntegrationTrapezoidal.h */; };\n\t\tD53231042142ACD9008DE511 /* FEFiberPowLinearUncoupled.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F612142ACD8008DE511 /* FEFiberPowLinearUncoupled.h */; };\n\t\tD53231052142ACD9008DE511 /* FE3FieldElasticSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F622142ACD8008DE511 /* FE3FieldElasticSolidDomain.cpp */; };\n\t\tD53231062142ACD9008DE511 /* FEUncoupledFiberExpLinear.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F632142ACD8008DE511 /* FEUncoupledFiberExpLinear.cpp */; };\n\t\tD53231072142ACD9008DE511 /* FEPRLig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F642142ACD8008DE511 /* FEPRLig.cpp */; };\n\t\tD53231082142ACD9008DE511 /* FEFiberPowLinear.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F652142ACD8008DE511 /* FEFiberPowLinear.cpp */; };\n\t\tD53231092142ACD9008DE511 /* FEArrudaBoyce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F662142ACD8008DE511 /* FEArrudaBoyce.h */; };\n\t\tD532310B2142ACD9008DE511 /* FEReactiveVEMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F682142ACD8008DE511 /* FEReactiveVEMaterialPoint.h */; };\n\t\tD532310C2142ACD9008DE511 /* FEStickyInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F692142ACD8008DE511 /* FEStickyInterface.cpp */; };\n\t\tD532310D2142ACD9008DE511 /* FEIsotropicElastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F6A2142ACD8008DE511 /* FEIsotropicElastic.h */; };\n\t\tD532310E2142ACD9008DE511 /* FEDamageNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F6B2142ACD8008DE511 /* FEDamageNeoHookean.cpp */; };\n\t\tD532310F2142ACD9008DE511 /* FEMRVonMisesFibers.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F6C2142ACD8008DE511 /* FEMRVonMisesFibers.h */; };\n\t\tD53231102142ACD9008DE511 /* FEPrescribedActiveContractionIsotropic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F6D2142ACD8008DE511 /* FEPrescribedActiveContractionIsotropic.cpp */; };\n\t\tD53231112142ACD9008DE511 /* FEElasticSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F6E2142ACD8008DE511 /* FEElasticSolidDomain.h */; };\n\t\tD53231122142ACD9008DE511 /* FERigidJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F6F2142ACD8008DE511 /* FERigidJoint.cpp */; };\n\t\tD53231132142ACD9008DE511 /* FEContinuousFiberDistributionUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F702142ACD8008DE511 /* FEContinuousFiberDistributionUC.cpp */; };\n\t\tD53231142142ACD9008DE511 /* gauss.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F712142ACD8008DE511 /* gauss.h */; };\n\t\tD53231152142ACD9008DE511 /* FEUncoupledViscoElasticMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F722142ACD8008DE511 /* FEUncoupledViscoElasticMaterial.cpp */; };\n\t\tD53231162142ACD9008DE511 /* FEElasticShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F732142ACD8008DE511 /* FEElasticShellDomain.cpp */; };\n\t\tD53231182142ACD9008DE511 /* FETransIsoVerondaWestmann.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F752142ACD8008DE511 /* FETransIsoVerondaWestmann.cpp */; };\n\t\tD53231192142ACD9008DE511 /* FEElasticMultiscaleDomain2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F762142ACD8008DE511 /* FEElasticMultiscaleDomain2O.cpp */; };\n\t\tD532311A2142ACD9008DE511 /* FEUncoupledViscoElasticMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F772142ACD8008DE511 /* FEUncoupledViscoElasticMaterial.h */; };\n\t\tD532311B2142ACD9008DE511 /* FEViscoElasticMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F782142ACD8008DE511 /* FEViscoElasticMaterial.cpp */; };\n\t\tD532311C2142ACD9008DE511 /* FEExplicitSolidSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F792142ACD8008DE511 /* FEExplicitSolidSolver.cpp */; };\n\t\tD532311D2142ACD9008DE511 /* FEEFDUncoupled.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F7A2142ACD8008DE511 /* FEEFDUncoupled.h */; };\n\t\tD532311E2142ACD9008DE511 /* FEFacet2FacetTied.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F7B2142ACD8008DE511 /* FEFacet2FacetTied.cpp */; };\n\t\tD532311F2142ACD9008DE511 /* FEReactiveVEMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F7C2142ACD8008DE511 /* FEReactiveVEMaterialPoint.cpp */; };\n\t\tD53231202142ACD9008DE511 /* FECentrifugalBodyForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F7D2142ACD8008DE511 /* FECentrifugalBodyForce.h */; };\n\t\tD53231212142ACD9008DE511 /* FESolidMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F7E2142ACD8008DE511 /* FESolidMaterial.cpp */; };\n\t\tD53231222142ACD9008DE511 /* FEFiberNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F7F2142ACD8008DE511 /* FEFiberNeoHookean.cpp */; };\n\t\tD53231232142ACD9008DE511 /* FECGSolidSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F802142ACD8008DE511 /* FECGSolidSolver.h */; };\n\t\tD53231242142ACD9008DE511 /* FETiedInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F812142ACD8008DE511 /* FETiedInterface.cpp */; };\n\t\tD53231252142ACD9008DE511 /* FERigidPrismaticJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F822142ACD8008DE511 /* FERigidPrismaticJoint.h */; };\n\t\tD53231262142ACD9008DE511 /* FEMindlinElastic2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F832142ACD8008DE511 /* FEMindlinElastic2O.h */; };\n\t\tD53231272142ACD9008DE511 /* FENeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F842142ACD8008DE511 /* FENeoHookean.h */; };\n\t\tD53231282142ACD9008DE511 /* FESolidSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F852142ACD8008DE511 /* FESolidSolver.h */; };\n\t\tD532312A2142ACD9008DE511 /* FERigidDamper.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F872142ACD8008DE511 /* FERigidDamper.h */; };\n\t\tD532312B2142ACD9008DE511 /* FERigidSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F882142ACD8008DE511 /* FERigidSolidDomain.cpp */; };\n\t\tD532312C2142ACD9008DE511 /* FETransIsoVerondaWestmann.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F892142ACD8008DE511 /* FETransIsoVerondaWestmann.h */; };\n\t\tD532312D2142ACD9008DE511 /* FEElasticSolidDomain2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F8A2142ACD8008DE511 /* FEElasticSolidDomain2O.cpp */; };\n\t\tD532312E2142ACD9008DE511 /* FEPrescribedActiveContractionIsotropic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F8B2142ACD8008DE511 /* FEPrescribedActiveContractionIsotropic.h */; };\n\t\tD53231302142ACD9008DE511 /* FEDamageCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F8D2142ACD8008DE511 /* FEDamageCriterion.cpp */; };\n\t\tD53231312142ACD9008DE511 /* FEUT4Domain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F8E2142ACD8008DE511 /* FEUT4Domain.cpp */; };\n\t\tD53231322142ACD9008DE511 /* FENeoHookeanTransIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F8F2142ACD8008DE511 /* FENeoHookeanTransIso.cpp */; };\n\t\tD53231332142ACD9008DE511 /* FEDamageMaterialUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F902142ACD8008DE511 /* FEDamageMaterialUC.h */; };\n\t\tD53231342142ACD9008DE511 /* FEDeformableSpringDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F912142ACD8008DE511 /* FEDeformableSpringDomain.h */; };\n\t\tD53231352142ACD9008DE511 /* FERigidMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F922142ACD8008DE511 /* FERigidMaterial.h */; };\n\t\tD53231362142ACD9008DE511 /* FEAugLagLinearConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F932142ACD8008DE511 /* FEAugLagLinearConstraint.cpp */; };\n\t\tD53231372142ACD9008DE511 /* FEUncoupledReactiveViscoelastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F942142ACD8008DE511 /* FEUncoupledReactiveViscoelastic.h */; };\n\t\tD53231382142ACD9008DE511 /* FEElasticSolidDomain2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F952142ACD8008DE511 /* FEElasticSolidDomain2O.h */; };\n\t\tD53231392142ACD9008DE511 /* FEElasticEASShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F962142ACD8008DE511 /* FEElasticEASShellDomain.h */; };\n\t\tD532313A2142ACD9008DE511 /* FERigidLock.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F972142ACD8008DE511 /* FERigidLock.h */; };\n\t\tD532313B2142ACD9008DE511 /* FEPrescribedActiveContractionTransIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F982142ACD8008DE511 /* FEPrescribedActiveContractionTransIso.h */; };\n\t\tD532313D2142ACD9008DE511 /* FEResidualVector.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322F9A2142ACD8008DE511 /* FEResidualVector.h */; };\n\t\tD532313E2142ACD9008DE511 /* FENewtonianViscousSolid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F9B2142ACD8008DE511 /* FENewtonianViscousSolid.cpp */; };\n\t\tD53231402142ACD9008DE511 /* FERigidContractileForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F9D2142ACD8008DE511 /* FERigidContractileForce.cpp */; };\n\t\tD53231412142ACD9008DE511 /* FEUDGHexDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F9E2142ACD8008DE511 /* FEUDGHexDomain.cpp */; };\n\t\tD53231422142ACD9008DE511 /* FEBioMechPlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322F9F2142ACD8008DE511 /* FEBioMechPlot.cpp */; };\n\t\tD53231432142ACD9008DE511 /* FEPointConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA02142ACD8008DE511 /* FEPointConstraint.cpp */; };\n\t\tD53231442142ACD9008DE511 /* FE3FieldElasticShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FA12142ACD8008DE511 /* FE3FieldElasticShellDomain.h */; };\n\t\tD53231452142ACD9008DE511 /* FEEFDDonnanEquilibrium.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FA22142ACD8008DE511 /* FEEFDDonnanEquilibrium.h */; };\n\t\tD53231462142ACD9008DE511 /* FECGSolidSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA32142ACD8008DE511 /* FECGSolidSolver.cpp */; };\n\t\tD53231472142ACD9008DE511 /* FEPeriodicSurfaceConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA42142ACD8008DE511 /* FEPeriodicSurfaceConstraint.cpp */; };\n\t\tD53231482142ACD9008DE511 /* FEUncoupledActiveContraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA52142ACD8008DE511 /* FEUncoupledActiveContraction.cpp */; };\n\t\tD53231492142ACD9008DE511 /* FERigidDamper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA62142ACD8008DE511 /* FERigidDamper.cpp */; };\n\t\tD532314A2142ACD9008DE511 /* FEMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FA72142ACD8008DE511 /* FEMooneyRivlin.h */; };\n\t\tD532314B2142ACD9008DE511 /* FERVEModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA82142ACD8008DE511 /* FERVEModel.cpp */; };\n\t\tD532314C2142ACD9008DE511 /* FERigidAngularDamper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FA92142ACD8008DE511 /* FERigidAngularDamper.cpp */; };\n\t\tD532314D2142ACD9008DE511 /* FEDamageMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FAA2142ACD8008DE511 /* FEDamageMooneyRivlin.cpp */; };\n\t\tD532314E2142ACD9008DE511 /* FEMRVonMisesFibers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FAB2142ACD8008DE511 /* FEMRVonMisesFibers.cpp */; };\n\t\tD532314F2142ACD9008DE511 /* FEUncoupledMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FAC2142ACD8008DE511 /* FEUncoupledMaterial.h */; };\n\t\tD53231502142ACD9008DE511 /* FEElasticANSShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FAD2142ACD8008DE511 /* FEElasticANSShellDomain.cpp */; };\n\t\tD53231512142ACD9008DE511 /* FEUncoupledElasticMixture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FAE2142ACD8008DE511 /* FEUncoupledElasticMixture.cpp */; };\n\t\tD53231522142ACD9008DE511 /* FETCNonlinearOrthotropic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FAF2142ACD8008DE511 /* FETCNonlinearOrthotropic.cpp */; };\n\t\tD53231532142ACD9008DE511 /* FEFiberIntegrationTriangle.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FB02142ACD8008DE511 /* FEFiberIntegrationTriangle.h */; };\n\t\tD53231542142ACD9008DE511 /* FERigidSlidingContact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FB12142ACD8008DE511 /* FERigidSlidingContact.cpp */; };\n\t\tD53231562142ACD9008DE511 /* FESolidMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FB32142ACD8008DE511 /* FESolidMaterial.h */; };\n\t\tD53231572142ACD9008DE511 /* FEIncompNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FB42142ACD8008DE511 /* FEIncompNeoHookean.h */; };\n\t\tD53231582142ACD9008DE511 /* FETractionLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FB52142ACD8008DE511 /* FETractionLoad.cpp */; };\n\t\tD53231592142ACD9008DE511 /* FEPerfectOsmometer.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FB62142ACD8008DE511 /* FEPerfectOsmometer.h */; };\n\t\tD532315A2142ACD9008DE511 /* FEPerfectOsmometer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FB72142ACD8008DE511 /* FEPerfectOsmometer.cpp */; };\n\t\tD532315B2142ACD9008DE511 /* FESolidSolver2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FB82142ACD8008DE511 /* FESolidSolver2.cpp */; };\n\t\tD532315D2142ACD9008DE511 /* FEMindlinElastic2O.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FBA2142ACD8008DE511 /* FEMindlinElastic2O.cpp */; };\n\t\tD532315E2142ACD9008DE511 /* FEPointConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FBB2142ACD8008DE511 /* FEPointConstraint.h */; };\n\t\tD532315F2142ACD9008DE511 /* FEDamageMaterialUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FBC2142ACD8008DE511 /* FEDamageMaterialUC.cpp */; };\n\t\tD53231602142ACD9008DE511 /* FERigidForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FBD2142ACD8008DE511 /* FERigidForce.h */; };\n\t\tD53231612142ACD9008DE511 /* FETransIsoMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FBE2142ACD8008DE511 /* FETransIsoMooneyRivlin.cpp */; };\n\t\tD53231622142ACD9008DE511 /* FECubicCLE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FBF2142ACD8008DE511 /* FECubicCLE.cpp */; };\n\t\tD53231632142ACD9008DE511 /* FENewtonianViscousSolidUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FC02142ACD8008DE511 /* FENewtonianViscousSolidUC.cpp */; };\n\t\tD53231642142ACD9008DE511 /* FEDamageMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FC12142ACD8008DE511 /* FEDamageMaterial.h */; };\n\t\tD53231652142ACD9008DE511 /* FERigidContractileForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FC22142ACD8008DE511 /* FERigidContractileForce.h */; };\n\t\tD53231662142ACD9008DE511 /* FETCNonlinearOrthotropic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FC32142ACD8008DE511 /* FETCNonlinearOrthotropic.h */; };\n\t\tD53231672142ACD9008DE511 /* FEOsmoticVirialExpansion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FC42142ACD8008DE511 /* FEOsmoticVirialExpansion.cpp */; };\n\t\tD53231682142ACD9008DE511 /* FEEFDMooneyRivlin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FC52142ACD8008DE511 /* FEEFDMooneyRivlin.cpp */; };\n\t\tD53231692142ACD9008DE511 /* FERigidSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FC62142ACD8008DE511 /* FERigidSolver.h */; };\n\t\tD532316A2142ACD9008DE511 /* FERigidCylindricalJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FC72142ACD8008DE511 /* FERigidCylindricalJoint.cpp */; };\n\t\tD532316B2142ACD9008DE511 /* FEHuiskesSupply.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FC82142ACD8008DE511 /* FEHuiskesSupply.h */; };\n\t\tD532316C2142ACD9008DE511 /* FE2DTransIsoVerondaWestmann.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FC92142ACD8008DE511 /* FE2DTransIsoVerondaWestmann.h */; };\n\t\tD532316D2142ACD9008DE511 /* FETiedContactSurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FCA2142ACD8008DE511 /* FETiedContactSurface.cpp */; };\n\t\tD532316F2142ACD9008DE511 /* FERemodelingElasticMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FCC2142ACD8008DE511 /* FERemodelingElasticMaterial.cpp */; };\n\t\tD53231702142ACD9008DE511 /* FEPrescribedActiveContractionUniaxialUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FCD2142ACD8008DE511 /* FEPrescribedActiveContractionUniaxialUC.h */; };\n\t\tD53231712142ACD9008DE511 /* FECoupledVerondaWestmann.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FCE2142ACD8008DE511 /* FECoupledVerondaWestmann.cpp */; };\n\t\tD53231722142ACD9008DE511 /* FEViscousMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FCF2142ACD8008DE511 /* FEViscousMaterialPoint.cpp */; };\n\t\tD53231732142ACD9008DE511 /* FECoupledTransIsoMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FD02142ACD8008DE511 /* FECoupledTransIsoMooneyRivlin.h */; };\n\t\tD53231742142ACD9008DE511 /* FEFiberNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FD12142ACD8008DE511 /* FEFiberNeoHookean.h */; };\n\t\tD53231762142ACD9008DE511 /* FE3FieldElasticSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FD32142ACD8008DE511 /* FE3FieldElasticSolidDomain.h */; };\n\t\tD53231772142ACD9008DE511 /* FEElasticTrussDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FD42142ACD8008DE511 /* FEElasticTrussDomain.cpp */; };\n\t\tD53231782142ACD9008DE511 /* FEFiberIntegrationGeodesic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FD52142ACD8008DE511 /* FEFiberIntegrationGeodesic.cpp */; };\n\t\tD53231792142ACD9008DE511 /* FEBodyForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FD62142ACD8008DE511 /* FEBodyForce.cpp */; };\n\t\tD532317A2142ACD9008DE511 /* FEPrescribedActiveContractionTransIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FD72142ACD8008DE511 /* FEPrescribedActiveContractionTransIso.cpp */; };\n\t\tD532317B2142ACD9008DE511 /* FEBondRelaxation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FD82142ACD8008DE511 /* FEBondRelaxation.cpp */; };\n\t\tD532317C2142ACD9008DE511 /* FEPointBodyForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FD92142ACD8008DE511 /* FEPointBodyForce.h */; };\n\t\tD532317D2142ACD9008DE511 /* FEElasticEASShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FDA2142ACD8008DE511 /* FEElasticEASShellDomain.cpp */; };\n\t\tD532317E2142ACD9008DE511 /* FEElasticFiberMaterialUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FDB2142ACD8008DE511 /* FEElasticFiberMaterialUC.h */; };\n\t\tD532317F2142ACD9008DE511 /* FERigidCable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FDC2142ACD8008DE511 /* FERigidCable.cpp */; };\n\t\tD53231802142ACD9008DE511 /* FEMergedConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FDD2142ACD8008DE511 /* FEMergedConstraint.cpp */; };\n\t\tD53231812142ACD9008DE511 /* FEPeriodicBoundary2O.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FDE2142ACD8008DE511 /* FEPeriodicBoundary2O.h */; };\n\t\tD53231822142ACD9008DE511 /* FEFiberIntegrationGauss.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FDF2142ACD8008DE511 /* FEFiberIntegrationGauss.cpp */; };\n\t\tD53231832142ACD9008DE511 /* FESpringMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FE02142ACD8008DE511 /* FESpringMaterial.h */; };\n\t\tD53231842142ACD9008DE511 /* FE2DFiberNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FE12142ACD8008DE511 /* FE2DFiberNeoHookean.cpp */; };\n\t\tD53231852142ACD9008DE511 /* FESSIShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FE22142ACD8008DE511 /* FESSIShellDomain.cpp */; };\n\t\tD53231862142ACD9008DE511 /* FECoupledMooneyRivlin.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322FE32142ACD8008DE511 /* FECoupledMooneyRivlin.h */; };\n\t\tD53231872142ACD9008DE511 /* FEContactInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FE42142ACD8008DE511 /* FEContactInterface.cpp */; };\n\t\tD53231882142ACD9008DE511 /* FEMortarTiedContact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322FE52142ACD8008DE511 /* FEMortarTiedContact.cpp */; };\n\t\tD532318C2142AD6E008DE511 /* libgsl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D532318B2142AD6E008DE511 /* libgsl.a */; };\n\t\tD54A66B32581AAFA0023C8D1 /* FEAzimuthConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D54A66B12581AAF90023C8D1 /* FEAzimuthConstraint.h */; };\n\t\tD54A66B42581AAFA0023C8D1 /* FEAzimuthConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54A66B22581AAF90023C8D1 /* FEAzimuthConstraint.cpp */; };\n\t\tD550834424F07EBE00E919D8 /* FEReactivePlasticDamageMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D550834024F07EBD00E919D8 /* FEReactivePlasticDamageMaterialPoint.cpp */; };\n\t\tD550834524F07EBE00E919D8 /* FEReactivePlasticDamage.h in Headers */ = {isa = PBXBuildFile; fileRef = D550834124F07EBD00E919D8 /* FEReactivePlasticDamage.h */; };\n\t\tD550834624F07EBE00E919D8 /* FEReactivePlasticDamageMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D550834224F07EBD00E919D8 /* FEReactivePlasticDamageMaterialPoint.h */; };\n\t\tD550834724F07EBE00E919D8 /* FEReactivePlasticDamage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D550834324F07EBE00E919D8 /* FEReactivePlasticDamage.cpp */; };\n\t\tD5613D30217B5FD0007CAB89 /* FEElasticMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D2A217B5FCF007CAB89 /* FEElasticMaterialPoint.h */; };\n\t\tD5613D31217B5FD0007CAB89 /* FEFiberExponentialPowerUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D2B217B5FCF007CAB89 /* FEFiberExponentialPowerUC.h */; };\n\t\tD5613D32217B5FD0007CAB89 /* FEFiberNHUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D2C217B5FD0007CAB89 /* FEFiberNHUC.h */; };\n\t\tD5613D33217B5FD0007CAB89 /* FEFiberExponentialPowerUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D2D217B5FD0007CAB89 /* FEFiberExponentialPowerUC.cpp */; };\n\t\tD5613D34217B5FD0007CAB89 /* FEElasticMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D2E217B5FD0007CAB89 /* FEElasticMaterialPoint.cpp */; };\n\t\tD5613D35217B5FD0007CAB89 /* FEFiberNHUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D2F217B5FD0007CAB89 /* FEFiberNHUC.cpp */; };\n\t\tD565CDC8215D28A500E08ED6 /* FERigidSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDBC215D28A300E08ED6 /* FERigidSystem.cpp */; };\n\t\tD565CDC9215D28A500E08ED6 /* RigidBC.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDBD215D28A300E08ED6 /* RigidBC.h */; };\n\t\tD565CDCA215D28A500E08ED6 /* FERigidBody.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDBE215D28A400E08ED6 /* FERigidBody.h */; };\n\t\tD565CDCB215D28A500E08ED6 /* FERigidSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDBF215D28A400E08ED6 /* FERigidSystem.h */; };\n\t\tD565CDCC215D28A500E08ED6 /* ObjectDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDC0215D28A400E08ED6 /* ObjectDataRecord.cpp */; };\n\t\tD565CDCD215D28A500E08ED6 /* RigidBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDC1215D28A400E08ED6 /* RigidBC.cpp */; };\n\t\tD565CDCE215D28A500E08ED6 /* FEMechModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDC2215D28A400E08ED6 /* FEMechModel.h */; };\n\t\tD565CDCF215D28A500E08ED6 /* FEMechModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDC3215D28A400E08ED6 /* FEMechModel.cpp */; };\n\t\tD565CDD0215D28A500E08ED6 /* FERigidBody.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDC4215D28A400E08ED6 /* FERigidBody.cpp */; };\n\t\tD565CDD1215D28A500E08ED6 /* FERigidSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDC5215D28A500E08ED6 /* FERigidSurface.h */; };\n\t\tD565CDD2215D28A500E08ED6 /* FERigidSurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDC6215D28A500E08ED6 /* FERigidSurface.cpp */; };\n\t\tD565CDD3215D28A500E08ED6 /* ObjectDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDC7215D28A500E08ED6 /* ObjectDataRecord.h */; };\n\t\tD56DB99C255C162D0072414C /* FETorsionalSpring.h in Headers */ = {isa = PBXBuildFile; fileRef = D56DB99A255C162D0072414C /* FETorsionalSpring.h */; };\n\t\tD56DB99D255C162D0072414C /* FETorsionalSpring.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56DB99B255C162D0072414C /* FETorsionalSpring.cpp */; };\n\t\tD5709D8B22832FD2007CAB0A /* FEInitialVelocity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D8322832FD1007CAB0A /* FEInitialVelocity.cpp */; };\n\t\tD5709D8C22832FD2007CAB0A /* FESolidLinearSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D8422832FD1007CAB0A /* FESolidLinearSystem.cpp */; };\n\t\tD5709D8D22832FD2007CAB0A /* FENodalForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D8522832FD1007CAB0A /* FENodalForce.h */; };\n\t\tD5709D8E22832FD2007CAB0A /* FEMaxDamageCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D8622832FD1007CAB0A /* FEMaxDamageCriterion.cpp */; };\n\t\tD5709D8F22832FD2007CAB0A /* FENodalForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D8722832FD1007CAB0A /* FENodalForce.cpp */; };\n\t\tD5709D9022832FD2007CAB0A /* FEInitialVelocity.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D8822832FD1007CAB0A /* FEInitialVelocity.h */; };\n\t\tD5709D9122832FD2007CAB0A /* FEMaxDamageCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D8922832FD1007CAB0A /* FEMaxDamageCriterion.h */; };\n\t\tD5709D9222832FD2007CAB0A /* FESolidLinearSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D8A22832FD1007CAB0A /* FESolidLinearSystem.h */; };\n\t\tD576A7D5256C366B00415BF7 /* FERigidFollowerForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D576A7D3256C366B00415BF7 /* FERigidFollowerForce.cpp */; };\n\t\tD576A7D6256C366B00415BF7 /* FERigidFollowerForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D576A7D4256C366B00415BF7 /* FERigidFollowerForce.h */; };\n\t\tD57DD3DF23F6EF2C001347CB /* FEGenericHyperelasticUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57DD3DB23F6EF2B001347CB /* FEGenericHyperelasticUC.cpp */; };\n\t\tD57DD3E023F6EF2C001347CB /* FEGenericHyperelastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57DD3DC23F6EF2B001347CB /* FEGenericHyperelastic.cpp */; };\n\t\tD57DD3E123F6EF2C001347CB /* FEGenericHyperelastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D57DD3DD23F6EF2B001347CB /* FEGenericHyperelastic.h */; };\n\t\tD57DD3E223F6EF2C001347CB /* FEGenericHyperelasticUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D57DD3DE23F6EF2B001347CB /* FEGenericHyperelasticUC.h */; };\n\t\tD57EE13B256F08310068733E /* FERigidFollowerMoment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D57EE139256F08310068733E /* FERigidFollowerMoment.cpp */; };\n\t\tD57EE13C256F08310068733E /* FERigidFollowerMoment.h in Headers */ = {isa = PBXBuildFile; fileRef = D57EE13A256F08310068733E /* FERigidFollowerMoment.h */; };\n\t\tD5885716260B985B00659AEF /* FESpringRuptureCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5885714260B985A00659AEF /* FESpringRuptureCriterion.cpp */; };\n\t\tD5885717260B985B00659AEF /* FESpringRuptureCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5885715260B985A00659AEF /* FESpringRuptureCriterion.h */; };\n\t\tD58FA376247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58FA374247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.cpp */; };\n\t\tD58FA377247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D58FA375247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.h */; };\n\t\tD5B805BC223BED2900198805 /* febiomech_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B805B9223BED2800198805 /* febiomech_api.h */; };\n\t\tD5B939A922D23D4700E495BA /* FEReactivePlasticity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B939A722D23D4700E495BA /* FEReactivePlasticity.cpp */; };\n\t\tD5B939AA22D23D4700E495BA /* FEReactivePlasticity.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B939A822D23D4700E495BA /* FEReactivePlasticity.h */; };\n\t\tD5B939AD22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B939AB22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.cpp */; };\n\t\tD5B939AE22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B939AC22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.h */; };\n\t\tD5BA5330240D541F001A5C0F /* FEBCRigidDeformation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BA532E240D541F001A5C0F /* FEBCRigidDeformation.cpp */; };\n\t\tD5BA5331240D541F001A5C0F /* FEBCRigidDeformation.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BA532F240D541F001A5C0F /* FEBCRigidDeformation.h */; };\n\t\tD5BC983D26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BC983B26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.cpp */; };\n\t\tD5BC983E26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BC983C26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.h */; };\n\t\tD5BC9F4D26D07B07003BBF6E /* FEMassDamping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BC9F4926D07B06003BBF6E /* FEMassDamping.cpp */; };\n\t\tD5BC9F4E26D07B07003BBF6E /* FEContactPotential.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BC9F4A26D07B06003BBF6E /* FEContactPotential.cpp */; };\n\t\tD5BC9F4F26D07B07003BBF6E /* FEMassDamping.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BC9F4B26D07B06003BBF6E /* FEMassDamping.h */; };\n\t\tD5BC9F5026D07B07003BBF6E /* FEContactPotential.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BC9F4C26D07B07003BBF6E /* FEContactPotential.h */; };\n\t\tD5BD65A22333E32000EB9FD1 /* FEPreStrainConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BD65962333E31F00EB9FD1 /* FEPreStrainConstraint.h */; };\n\t\tD5BD65A32333E32000EB9FD1 /* FEPreStrainUncoupledElastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BD65972333E31F00EB9FD1 /* FEPreStrainUncoupledElastic.h */; };\n\t\tD5BD65A42333E32000EB9FD1 /* FEPreStrainElastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BD65982333E32000EB9FD1 /* FEPreStrainElastic.cpp */; };\n\t\tD5BD65A52333E32000EB9FD1 /* FEInSituStretchGradient.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BD65992333E32000EB9FD1 /* FEInSituStretchGradient.h */; };\n\t\tD5BD65A62333E32000EB9FD1 /* FEConstPrestrain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BD659A2333E32000EB9FD1 /* FEConstPrestrain.h */; };\n\t\tD5BD65A72333E32000EB9FD1 /* FEInSituStretchGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BD659B2333E32000EB9FD1 /* FEInSituStretchGradient.cpp */; };\n\t\tD5BD65A82333E32000EB9FD1 /* FEPreStrainUncoupledElastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BD659C2333E32000EB9FD1 /* FEPreStrainUncoupledElastic.cpp */; };\n\t\tD5BD65A92333E32000EB9FD1 /* FEConstPrestrain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BD659D2333E32000EB9FD1 /* FEConstPrestrain.cpp */; };\n\t\tD5BD65AA2333E32000EB9FD1 /* FEInitialPreStrain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BD659E2333E32000EB9FD1 /* FEInitialPreStrain.cpp */; };\n\t\tD5BD65AB2333E32000EB9FD1 /* FEInitialPreStrain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BD659F2333E32000EB9FD1 /* FEInitialPreStrain.h */; };\n\t\tD5BD65AC2333E32000EB9FD1 /* FEPreStrainConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BD65A02333E32000EB9FD1 /* FEPreStrainConstraint.cpp */; };\n\t\tD5BD65AD2333E32000EB9FD1 /* FEPreStrainElastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BD65A12333E32000EB9FD1 /* FEPreStrainElastic.h */; };\n\t\tD5C071F52151A978003321F9 /* FEGenericBodyForce.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C071F32151A977003321F9 /* FEGenericBodyForce.cpp */; };\n\t\tD5C071F62151A978003321F9 /* FEGenericBodyForce.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C071F42151A978003321F9 /* FEGenericBodyForce.h */; };\n\t\tD5C123E42411845500657663 /* FEGenericTransIsoHyperelasticUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C123E22411845500657663 /* FEGenericTransIsoHyperelasticUC.cpp */; };\n\t\tD5C123E52411845500657663 /* FEGenericTransIsoHyperelasticUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C123E32411845500657663 /* FEGenericTransIsoHyperelasticUC.h */; };\n\t\tD5C13ED2240477A3005897E1 /* FEActiveFiberStressUC.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13ECC240477A3005897E1 /* FEActiveFiberStressUC.h */; };\n\t\tD5C13ED3240477A3005897E1 /* FEActiveFiberStress.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13ECD240477A3005897E1 /* FEActiveFiberStress.h */; };\n\t\tD5C13ED4240477A3005897E1 /* FEActiveFiberStress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13ECE240477A3005897E1 /* FEActiveFiberStress.cpp */; };\n\t\tD5C13ED5240477A3005897E1 /* FEActiveFiberStressUC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13ECF240477A3005897E1 /* FEActiveFiberStressUC.cpp */; };\n\t\tD5C13ED6240477A3005897E1 /* FEGenericRigidJoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C13ED0240477A3005897E1 /* FEGenericRigidJoint.cpp */; };\n\t\tD5C13ED7240477A3005897E1 /* FEGenericRigidJoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C13ED1240477A3005897E1 /* FEGenericRigidJoint.h */; };\n\t\tD5D56E80235523790078BCC4 /* FENodeToNodeConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D56E7E235523790078BCC4 /* FENodeToNodeConstraint.cpp */; };\n\t\tD5D56E81235523790078BCC4 /* FENodeToNodeConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D56E7F235523790078BCC4 /* FENodeToNodeConstraint.h */; };\n\t\tD5D741CE2543427A002B2825 /* FEContinuousElasticDamage.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D741CC2543427A002B2825 /* FEContinuousElasticDamage.h */; };\n\t\tD5D741CF2543427A002B2825 /* FEContinuousElasticDamage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D741CD2543427A002B2825 /* FEContinuousElasticDamage.cpp */; };\n\t\tD5D84F02240F21B600DF3D67 /* FEGenericTransIsoHyperelastic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D84F00240F21B500DF3D67 /* FEGenericTransIsoHyperelastic.cpp */; };\n\t\tD5D84F03240F21B600DF3D67 /* FEGenericTransIsoHyperelastic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D84F01240F21B500DF3D67 /* FEGenericTransIsoHyperelastic.h */; };\n\t\tD5D8C9DF26E2776D00C193C3 /* FEKamensky.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D8C9DB26E2776C00C193C3 /* FEKamensky.h */; };\n\t\tD5D8C9E026E2776D00C193C3 /* FEKamensky.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D8C9DC26E2776C00C193C3 /* FEKamensky.cpp */; };\n\t\tD5D8C9E126E2776D00C193C3 /* FEKamenskyUncoupled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D8C9DD26E2776C00C193C3 /* FEKamenskyUncoupled.cpp */; };\n\t\tD5D8C9E226E2776D00C193C3 /* FEKamenskyUncoupled.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D8C9DE26E2776D00C193C3 /* FEKamenskyUncoupled.h */; };\n\t\tD5D8C9E526E6AF8E00C193C3 /* FEPlasticFlowCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5D8C9E326E6AF8E00C193C3 /* FEPlasticFlowCurve.cpp */; };\n\t\tD5D8C9E626E6AF8E00C193C3 /* FEPlasticFlowCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D8C9E426E6AF8E00C193C3 /* FEPlasticFlowCurve.h */; };\n\t\tD5DA95A023D3540200D38BF2 /* FEDiscreteElementMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5DA959C23D3540200D38BF2 /* FEDiscreteElementMaterial.cpp */; };\n\t\tD5DA95A123D3540200D38BF2 /* FEDiscreteElementMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5DA959D23D3540200D38BF2 /* FEDiscreteElementMaterial.h */; };\n\t\tD5DAB6D022A954F1004C4213 /* FERVEDamageMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5DAB6CE22A954F1004C4213 /* FERVEDamageMaterial.cpp */; };\n\t\tD5DAB6D122A954F1004C4213 /* FERVEDamageMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5DAB6CF22A954F1004C4213 /* FERVEDamageMaterial.h */; };\n\t\tD5DEA26226F52F6900E26ED3 /* FEFiberNaturalNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5DEA26026F52F6900E26ED3 /* FEFiberNaturalNeoHookean.cpp */; };\n\t\tD5DEA26326F52F6900E26ED3 /* FEFiberNaturalNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5DEA26126F52F6900E26ED3 /* FEFiberNaturalNeoHookean.h */; };\n\t\tD5E1AA8E25E3F892003162AB /* FEDeformationMapGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E1AA8C25E3F892003162AB /* FEDeformationMapGenerator.h */; };\n\t\tD5E1AA8F25E3F892003162AB /* FEDeformationMapGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E1AA8D25E3F892003162AB /* FEDeformationMapGenerator.cpp */; };\n\t\tD5EBB7A12328231800EED2BE /* FECarreauYasudaViscousSolid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5EBB79F2328231800EED2BE /* FECarreauYasudaViscousSolid.cpp */; };\n\t\tD5EBB7A22328231800EED2BE /* FECarreauYasudaViscousSolid.h in Headers */ = {isa = PBXBuildFile; fileRef = D5EBB7A02328231800EED2BE /* FECarreauYasudaViscousSolid.h */; };\n\t\tD5F09074248A84FA00E4F940 /* FEDiscreteElasticMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F09070248A84FA00E4F940 /* FEDiscreteElasticMaterial.h */; };\n\t\tD5F09075248A84FA00E4F940 /* FEDiscreteElasticMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F09071248A84FA00E4F940 /* FEDiscreteElasticMaterial.cpp */; };\n\t\tD5F09076248A84FA00E4F940 /* FEDiscreteElasticDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F09072248A84FA00E4F940 /* FEDiscreteElasticDomain.cpp */; };\n\t\tD5F09077248A84FA00E4F940 /* FEDiscreteElasticDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F09073248A84FA00E4F940 /* FEDiscreteElasticDomain.h */; };\n\t\tD5F72FA021601E6300271806 /* FEFatigueMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F72F9E21601E6300271806 /* FEFatigueMaterial.cpp */; };\n\t\tD5F72FA121601E6300271806 /* FEFatigueMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F72F9F21601E6300271806 /* FEFatigueMaterial.h */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD511EF11253A74AE00893F69 /* FEBioMechModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMechModule.cpp; sourceTree = \"<group>\"; };\n\t\tD511EF12253A74AE00893F69 /* FEBioMechModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMechModule.h; sourceTree = \"<group>\"; };\n\t\tD5135C27232191C6008AFD7D /* FENaturalNeoHookean.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FENaturalNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5135C28232191C6008AFD7D /* FENaturalNeoHookean.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FENaturalNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD51E614A22443FDB0049F545 /* FEMaxStressCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMaxStressCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD51E614B22443FDB0049F545 /* FEMaxStressCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMaxStressCriterion.h; sourceTree = \"<group>\"; };\n\t\tD524F6B2258BEB6000A403CD /* FEHolzapfelUnconstrained.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEHolzapfelUnconstrained.cpp; sourceTree = \"<group>\"; };\n\t\tD524F6B3258BEB6000A403CD /* FEHolzapfelUnconstrained.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEHolzapfelUnconstrained.h; sourceTree = \"<group>\"; };\n\t\tD52D841921CE9A7600472620 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E352142ACC9008DE511 /* libFEBioMech.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioMech.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5322E442142ACD7008DE511 /* FEDeformableSpringDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDeformableSpringDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E452142ACD7008DE511 /* FEExplicitSolidSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEExplicitSolidSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322E462142ACD7008DE511 /* FEPrescribedActiveContractionIsotropicUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedActiveContractionIsotropicUC.h; sourceTree = \"<group>\"; };\n\t\tD5322E472142ACD7008DE511 /* FESRIElasticSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESRIElasticSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E482142ACD7008DE511 /* FEFungOrthoCompressible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFungOrthoCompressible.h; sourceTree = \"<group>\"; };\n\t\tD5322E492142ACD7008DE511 /* FEMortarTiedContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMortarTiedContact.h; sourceTree = \"<group>\"; };\n\t\tD5322E4A2142ACD7008DE511 /* FEFiberIntegrationTriangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberIntegrationTriangle.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E4B2142ACD7008DE511 /* FEFiberExpPow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberExpPow.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E4C2142ACD7008DE511 /* FEResidualVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEResidualVector.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E4D2142ACD7008DE511 /* FEFiberExpLinear.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberExpLinear.h; sourceTree = \"<group>\"; };\n\t\tD5322E4E2142ACD7008DE511 /* geodesic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = geodesic.h; sourceTree = \"<group>\"; };\n\t\tD5322E4F2142ACD7008DE511 /* FEUncoupledFiberExpLinear.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUncoupledFiberExpLinear.h; sourceTree = \"<group>\"; };\n\t\tD5322E502142ACD7008DE511 /* FEElasticANSShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticANSShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322E512142ACD7008DE511 /* FERigidWallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidWallInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322E522142ACD7008DE511 /* FETiedInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322E532142ACD7008DE511 /* FEEllipsoidalFiberDistribution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEllipsoidalFiberDistribution.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E542142ACD7008DE511 /* FEVonMisesPlasticity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEVonMisesPlasticity.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E552142ACD7008DE511 /* FEDistanceConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDistanceConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E562142ACD7008DE511 /* FETiedElasticInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedElasticInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322E572142ACD7008DE511 /* FEElasticMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322E582142ACD7008DE511 /* FEOrthotropicCLE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOrthotropicCLE.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E592142ACD7008DE511 /* FESymmetryPlane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESymmetryPlane.h; sourceTree = \"<group>\"; };\n\t\tD5322E5A2142ACD7008DE511 /* FEMergedConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMergedConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322E5B2142ACD7008DE511 /* FEFiberPowLinear.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberPowLinear.h; sourceTree = \"<group>\"; };\n\t\tD5322E5C2142ACD7008DE511 /* FEVolumeConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEVolumeConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E5D2142ACD7008DE511 /* FEElasticShellDomainOld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticShellDomainOld.h; sourceTree = \"<group>\"; };\n\t\tD5322E5E2142ACD7008DE511 /* FEEFDNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEFDNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E5F2142ACD7008DE511 /* FERigidShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322E602142ACD7008DE511 /* FEMuscleMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMuscleMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322E612142ACD7008DE511 /* FEFiberIntegrationGeodesic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberIntegrationGeodesic.h; sourceTree = \"<group>\"; };\n\t\tD5322E622142ACD7008DE511 /* FERigidShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E632142ACD7008DE511 /* FEFungOrthotropic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFungOrthotropic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E642142ACD7008DE511 /* FE2DTransIsoMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FE2DTransIsoMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322E652142ACD7008DE511 /* FEElasticFiberMaterialUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticFiberMaterialUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E662142ACD7008DE511 /* FESphericalFiberDistribution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESphericalFiberDistribution.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E672142ACD7008DE511 /* FECoupledTransIsoVerondaWestmann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoupledTransIsoVerondaWestmann.h; sourceTree = \"<group>\"; };\n\t\tD5322E682142ACD7008DE511 /* FEAugLagLinearConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEAugLagLinearConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322E692142ACD7008DE511 /* FEWrinkleOgdenMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEWrinkleOgdenMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322E6A2142ACD7008DE511 /* FEContactSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContactSurface.h; sourceTree = \"<group>\"; };\n\t\tD5322E6B2142ACD7008DE511 /* FEPeriodicBoundary1O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPeriodicBoundary1O.h; sourceTree = \"<group>\"; };\n\t\tD5322E6C2142ACD7008DE511 /* FEPeriodicLinearConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPeriodicLinearConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322E6D2142ACD7008DE511 /* FECellGrowth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECellGrowth.h; sourceTree = \"<group>\"; };\n\t\tD5322E6E2142ACD7008DE511 /* FEFiberIntegrationTrapezoidal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberIntegrationTrapezoidal.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E6F2142ACD7008DE511 /* FERigidCable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidCable.h; sourceTree = \"<group>\"; };\n\t\tD5322E702142ACD7008DE511 /* FEPeriodicBoundary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPeriodicBoundary.h; sourceTree = \"<group>\"; };\n\t\tD5322E712142ACD7008DE511 /* FEFiberIntegrationGaussKronrod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberIntegrationGaussKronrod.h; sourceTree = \"<group>\"; };\n\t\tD5322E722142ACD7008DE511 /* FESolidDomainFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E732142ACD7008DE511 /* FERigidRevoluteJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidRevoluteJoint.h; sourceTree = \"<group>\"; };\n\t\tD5322E742142ACD7008DE511 /* FESlidingInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E762142ACD7008DE511 /* FERVEModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERVEModel.h; sourceTree = \"<group>\"; };\n\t\tD5322E772142ACD7008DE511 /* FECoupledTransIsoMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoupledTransIsoMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E782142ACD7008DE511 /* FEUDGHexDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUDGHexDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322E7A2142ACD7008DE511 /* FEPointBodyForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPointBodyForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E7B2142ACD7008DE511 /* FECarterHayesOld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECarterHayesOld.h; sourceTree = \"<group>\"; };\n\t\tD5322E7C2142ACD7008DE511 /* FEContinuousFiberDistribution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContinuousFiberDistribution.h; sourceTree = \"<group>\"; };\n\t\tD5322E7D2142ACD7008DE511 /* FEViscousMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEViscousMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5322E7E2142ACD7008DE511 /* FERemodelingElasticDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERemodelingElasticDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E7F2142ACD7008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5322E802142ACD7008DE511 /* FEBioMechPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMechPlot.h; sourceTree = \"<group>\"; };\n\t\tD5322E822142ACD7008DE511 /* FEPeriodicLinearConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPeriodicLinearConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E832142ACD7008DE511 /* FEMicroMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMicroMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322E842142ACD7008DE511 /* FEFiberIntegrationGaussKronrod.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberIntegrationGaussKronrod.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E852142ACD7008DE511 /* FEPressureLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPressureLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E862142ACD7008DE511 /* FEDonnanEquilibrium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDonnanEquilibrium.h; sourceTree = \"<group>\"; };\n\t\tD5322E872142ACD7008DE511 /* FETendonMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETendonMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E882142ACD7008DE511 /* FEFiberExpLinear.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberExpLinear.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E892142ACD7008DE511 /* FECellGrowth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECellGrowth.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E8A2142ACD7008DE511 /* FERigidSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322E8B2142ACD7008DE511 /* FEMicroMaterial2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMicroMaterial2O.h; sourceTree = \"<group>\"; };\n\t\tD5322E8C2142ACD7008DE511 /* FEDamageCDF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageCDF.h; sourceTree = \"<group>\"; };\n\t\tD5322E8D2142ACD7008DE511 /* FEUT4Domain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUT4Domain.h; sourceTree = \"<group>\"; };\n\t\tD5322E8E2142ACD7008DE511 /* FEStVenantKirchhoff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEStVenantKirchhoff.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E8F2142ACD7008DE511 /* FETractionLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETractionLoad.h; sourceTree = \"<group>\"; };\n\t\tD5322E902142ACD7008DE511 /* FERigidCylindricalJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidCylindricalJoint.h; sourceTree = \"<group>\"; };\n\t\tD5322E912142ACD7008DE511 /* FESymmetryPlane.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESymmetryPlane.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E922142ACD7008DE511 /* FEMortarSlidingContact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMortarSlidingContact.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E932142ACD7008DE511 /* FEHolzapfelGasserOgden.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEHolzapfelGasserOgden.h; sourceTree = \"<group>\"; };\n\t\tD5322E942142ACD7008DE511 /* FENeoHookeanTransIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENeoHookeanTransIso.h; sourceTree = \"<group>\"; };\n\t\tD5322E952142ACD7008DE511 /* gausskronrod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gausskronrod.h; sourceTree = \"<group>\"; };\n\t\tD5322E962142ACD7008DE511 /* FEBondRelaxation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBondRelaxation.h; sourceTree = \"<group>\"; };\n\t\tD5322E972142ACD7008DE511 /* FEContinuousFiberDistributionUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContinuousFiberDistributionUC.h; sourceTree = \"<group>\"; };\n\t\tD5322E982142ACD7008DE511 /* FERigidSpring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSpring.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E992142ACD7008DE511 /* FEMortarContactSurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMortarContactSurface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E9A2142ACD7008DE511 /* FERigidPlanarJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidPlanarJoint.h; sourceTree = \"<group>\"; };\n\t\tD5322E9B2142ACD7008DE511 /* FEDamageMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322E9C2142ACD7008DE511 /* FEFiberIntegrationScheme.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberIntegrationScheme.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E9D2142ACD7008DE511 /* FEDamageNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322E9E2142ACD7008DE511 /* FEActiveFiberContraction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEActiveFiberContraction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322E9F2142ACD7008DE511 /* FESolidSolver2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidSolver2.h; sourceTree = \"<group>\"; };\n\t\tD5322EA02142ACD7008DE511 /* FESlidingElasticInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingElasticInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EA12142ACD7008DE511 /* FEContactInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContactInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322EA22142ACD7008DE511 /* FERigidJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidJoint.h; sourceTree = \"<group>\"; };\n\t\tD5322EA32142ACD7008DE511 /* FEReactiveViscoelastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactiveViscoelastic.h; sourceTree = \"<group>\"; };\n\t\tD5322EA42142ACD7008DE511 /* FEElasticMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EA52142ACD7008DE511 /* FEDamageTransIsoMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageTransIsoMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EA62142ACD7008DE511 /* FEFiberExpPow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberExpPow.h; sourceTree = \"<group>\"; };\n\t\tD5322EA72142ACD7008DE511 /* FEDamageMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EA82142ACD7008DE511 /* FECoupledTransIsoVerondaWestmann.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoupledTransIsoVerondaWestmann.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EA92142ACD7008DE511 /* FERigidPlanarJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidPlanarJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EAA2142ACD7008DE511 /* FEStVenantKirchhoff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEStVenantKirchhoff.h; sourceTree = \"<group>\"; };\n\t\tD5322EAC2142ACD7008DE511 /* FEElasticMultigeneration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMultigeneration.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EAD2142ACD7008DE511 /* FEPRLig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPRLig.h; sourceTree = \"<group>\"; };\n\t\tD5322EAE2142ACD7008DE511 /* FEBCPrescribedDeformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBCPrescribedDeformation.h; sourceTree = \"<group>\"; };\n\t\tD5322EAF2142ACD7008DE511 /* FEElasticDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322EB02142ACD7008DE511 /* FEElasticFiberMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticFiberMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322EB12142ACD7008DE511 /* FERigidPrismaticJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidPrismaticJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EB22142ACD7008DE511 /* FEIsotropicElastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEIsotropicElastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EB42142ACD7008DE511 /* FEPrescribedActiveContractionUniaxial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedActiveContractionUniaxial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EB62142ACD7008DE511 /* FE2DTransIsoMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FE2DTransIsoMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EB72142ACD7008DE511 /* FESpringMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESpringMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EB82142ACD7008DE511 /* FEElasticMultiscaleDomain1O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMultiscaleDomain1O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EB92142ACD7008DE511 /* FEEFDVerondaWestmann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEFDVerondaWestmann.h; sourceTree = \"<group>\"; };\n\t\tD5322EBA2142ACD7008DE511 /* FEFiberExpPowUncoupled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberExpPowUncoupled.h; sourceTree = \"<group>\"; };\n\t\tD5322EBB2142ACD7008DE511 /* FEVolumeConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEVolumeConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322EBC2142ACD7008DE511 /* FEViscoElasticMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEViscoElasticMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322EBD2142ACD7008DE511 /* FECentrifugalBodyForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECentrifugalBodyForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EBE2142ACD7008DE511 /* FENeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EBF2142ACD7008DE511 /* FEFiberExpPowUncoupled.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberExpPowUncoupled.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EC02142ACD7008DE511 /* FEActiveFiberContraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEActiveFiberContraction.h; sourceTree = \"<group>\"; };\n\t\tD5322EC12142ACD7008DE511 /* FERigidConnector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidConnector.h; sourceTree = \"<group>\"; };\n\t\tD5322EC22142ACD7008DE511 /* FE2OMicroConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FE2OMicroConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EC32142ACD7008DE511 /* FERigidSlidingContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSlidingContact.h; sourceTree = \"<group>\"; };\n\t\tD5322EC42142ACD7008DE511 /* FEFacet2FacetSliding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFacet2FacetSliding.h; sourceTree = \"<group>\"; };\n\t\tD5322EC52142ACD7008DE511 /* FERigidLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidLock.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EC62142ACD7008DE511 /* FEMembraneMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322EC72142ACD7008DE511 /* FECoupledVerondaWestmann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoupledVerondaWestmann.h; sourceTree = \"<group>\"; };\n\t\tD5322EC92142ACD7008DE511 /* FETrussMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETrussMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322ECA2142ACD7008DE511 /* FERigidSphericalJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSphericalJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ECB2142ACD7008DE511 /* FERigidForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ECC2142ACD7008DE511 /* FERigidSphericalJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSphericalJoint.h; sourceTree = \"<group>\"; };\n\t\tD5322ECD2142ACD7008DE511 /* FEBodyForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBodyForce.h; sourceTree = \"<group>\"; };\n\t\tD5322ECE2142ACD7008DE511 /* FEPeriodicBoundary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPeriodicBoundary.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ECF2142ACD7008DE511 /* FEVonMisesPlasticity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEVonMisesPlasticity.h; sourceTree = \"<group>\"; };\n\t\tD5322ED02142ACD7008DE511 /* FEMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ED12142ACD7008DE511 /* FEPrescribedActiveContractionIsotropicUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedActiveContractionIsotropicUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ED22142ACD7008DE511 /* FE2DFiberNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FE2DFiberNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322ED32142ACD7008DE511 /* FERemodelingElasticDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERemodelingElasticDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322ED42142ACD7008DE511 /* FEFiberIntegrationGauss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberIntegrationGauss.h; sourceTree = \"<group>\"; };\n\t\tD5322ED52142ACD7008DE511 /* FEDiscreteContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteContact.h; sourceTree = \"<group>\"; };\n\t\tD5322ED62142ACD7008DE511 /* FEIncompNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEIncompNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ED72142ACD7008DE511 /* FEDiscreteContact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteContact.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ED82142ACD7008DE511 /* FEElasticMixture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMixture.cpp; sourceTree = \"<group>\"; };\n\t\tD5322ED92142ACD7008DE511 /* FEOrthotropicCLE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOrthotropicCLE.h; sourceTree = \"<group>\"; };\n\t\tD5322EDA2142ACD7008DE511 /* FEElasticShellDomainOld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticShellDomainOld.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EDC2142ACD7008DE511 /* FEFacet2FacetSliding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFacet2FacetSliding.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EDD2142ACD7008DE511 /* FEPrescribedActiveContractionTransIsoUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedActiveContractionTransIsoUC.h; sourceTree = \"<group>\"; };\n\t\tD5322EDE2142ACD7008DE511 /* FEMortarInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMortarInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322EDF2142ACD7008DE511 /* FEFungOrthotropic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFungOrthotropic.h; sourceTree = \"<group>\"; };\n\t\tD5322EE02142ACD7008DE511 /* FEElasticFiberMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticFiberMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EE12142ACD7008DE511 /* FEUncoupledElasticMixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUncoupledElasticMixture.h; sourceTree = \"<group>\"; };\n\t\tD5322EE42142ACD7008DE511 /* FEElasticMixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMixture.h; sourceTree = \"<group>\"; };\n\t\tD5322EE52142ACD7008DE511 /* FEDamageTransIsoMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageTransIsoMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322EE62142ACD7008DE511 /* FEOgdenUnconstrained.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOgdenUnconstrained.h; sourceTree = \"<group>\"; };\n\t\tD5322EE72142ACD7008DE511 /* FEPrescribedNormalDisplacement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedNormalDisplacement.h; sourceTree = \"<group>\"; };\n\t\tD5322EE82142ACD7008DE511 /* FEEFDDonnanEquilibrium.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEFDDonnanEquilibrium.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EEA2142ACD7008DE511 /* FESlidingInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322EEB2142ACD7008DE511 /* FEPeriodicLinearConstraint2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPeriodicLinearConstraint2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EEC2142ACD7008DE511 /* FEElasticSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EED2142ACD7008DE511 /* FEGentMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGentMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EEE2142ACD7008DE511 /* FESRIElasticSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESRIElasticSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322EF02142ACD7008DE511 /* FEUncoupledMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUncoupledMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EF12142ACD7008DE511 /* FEPrescribedNormalDisplacement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedNormalDisplacement.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EF22142ACD7008DE511 /* FERVEModel2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERVEModel2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EF32142ACD7008DE511 /* FESlidingElasticInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingElasticInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322EF42142ACD7008DE511 /* FEEFDVerondaWestmann.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEFDVerondaWestmann.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EF52142ACD8008DE511 /* FETiedContactSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedContactSurface.h; sourceTree = \"<group>\"; };\n\t\tD5322EF62142ACD8008DE511 /* FERigidMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EF72142ACD8008DE511 /* FENewtonianViscousSolidUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENewtonianViscousSolidUC.h; sourceTree = \"<group>\"; };\n\t\tD5322EF82142ACD8008DE511 /* FEPeriodicBoundary1O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPeriodicBoundary1O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EF92142ACD8008DE511 /* FEDamageCDF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageCDF.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EFA2142ACD8008DE511 /* FERigidWallInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidWallInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EFC2142ACD8008DE511 /* FECoupledMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoupledMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EFD2142ACD8008DE511 /* FEOgdenMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOgdenMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322EFF2142ACD8008DE511 /* FESolidDomainFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD5322F002142ACD8008DE511 /* FERigidRevoluteJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidRevoluteJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F012142ACD8008DE511 /* FERigidSpring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSpring.h; sourceTree = \"<group>\"; };\n\t\tD5322F022142ACD8008DE511 /* FEEFDNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEFDNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322F032142ACD8008DE511 /* FESolidSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F042142ACD8008DE511 /* FEOsmoticVirialExpansion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOsmoticVirialExpansion.h; sourceTree = \"<group>\"; };\n\t\tD5322F052142ACD8008DE511 /* FEDamageMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F062142ACD8008DE511 /* FEMortarInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMortarInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F072142ACD8008DE511 /* FEMicroMaterial2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMicroMaterial2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F082142ACD8008DE511 /* FEHolmesMow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEHolmesMow.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F0A2142ACD8008DE511 /* FEBioMech.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMech.h; sourceTree = \"<group>\"; };\n\t\tD5322F0B2142ACD8008DE511 /* FEOrthoElastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOrthoElastic.h; sourceTree = \"<group>\"; };\n\t\tD5322F0C2142ACD8008DE511 /* FEUncoupledReactiveViscoelastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUncoupledReactiveViscoelastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F0D2142ACD8008DE511 /* FEStickyInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEStickyInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322F0E2142ACD8008DE511 /* FE2OMicroConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FE2OMicroConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322F0F2142ACD8008DE511 /* FEOgdenMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOgdenMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322F102142ACD8008DE511 /* FECarterHayesOld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECarterHayesOld.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F112142ACD8008DE511 /* FEMembraneMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F122142ACD8008DE511 /* FERigidConnector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidConnector.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F132142ACD8008DE511 /* FEElasticTrussDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticTrussDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322F142142ACD8008DE511 /* FEFungOrthoCompressible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFungOrthoCompressible.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F152142ACD8008DE511 /* FEReactiveViscoelastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactiveViscoelastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F162142ACD8008DE511 /* FEElasticMultiscaleDomain2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMultiscaleDomain2O.h; sourceTree = \"<group>\"; };\n\t\tD5322F172142ACD8008DE511 /* FEPrescribedActiveContractionTransIsoUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedActiveContractionTransIsoUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F182142ACD8008DE511 /* FEFiberPowLinearUncoupled.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberPowLinearUncoupled.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F192142ACD8008DE511 /* FERigidSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F1A2142ACD8008DE511 /* FEHolzapfelGasserOgden.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEHolzapfelGasserOgden.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F1B2142ACD8008DE511 /* FEPrescribedActiveContractionUniaxial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedActiveContractionUniaxial.h; sourceTree = \"<group>\"; };\n\t\tD5322F1C2142ACD8008DE511 /* FERVEModel2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERVEModel2O.h; sourceTree = \"<group>\"; };\n\t\tD5322F1D2142ACD8008DE511 /* FE3FieldElasticShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FE3FieldElasticShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F1E2142ACD8008DE511 /* FESphericalFiberDistribution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESphericalFiberDistribution.h; sourceTree = \"<group>\"; };\n\t\tD5322F1F2142ACD8008DE511 /* FEContactSurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContactSurface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F202142ACD8008DE511 /* FEFiberMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5322F212142ACD8008DE511 /* FEFiberDensityDistribution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberDensityDistribution.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F222142ACD8008DE511 /* FEBioMechData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMechData.h; sourceTree = \"<group>\"; };\n\t\tD5322F232142ACD8008DE511 /* FEPeriodicSurfaceConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPeriodicSurfaceConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322F242142ACD8008DE511 /* FEMicroMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMicroMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F252142ACD8008DE511 /* FEMortarContactSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMortarContactSurface.h; sourceTree = \"<group>\"; };\n\t\tD5322F262142ACD8008DE511 /* FEWrinkleOgdenMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEWrinkleOgdenMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F272142ACD8008DE511 /* FEElasticMultigeneration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMultigeneration.h; sourceTree = \"<group>\"; };\n\t\tD5322F282142ACD8008DE511 /* FE2DTransIsoVerondaWestmann.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FE2DTransIsoVerondaWestmann.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F2A2142ACD8008DE511 /* FESSIShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESSIShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322F2C2142ACD8008DE511 /* FEOrthoElastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOrthoElastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F2D2142ACD8008DE511 /* FEBCPrescribedDeformation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBCPrescribedDeformation.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F2E2142ACD8008DE511 /* FENewtonianViscousSolid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENewtonianViscousSolid.h; sourceTree = \"<group>\"; };\n\t\tD5322F2F2142ACD8008DE511 /* FEMortarSlidingContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMortarSlidingContact.h; sourceTree = \"<group>\"; };\n\t\tD5322F312142ACD8008DE511 /* FEEFDMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEFDMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322F322142ACD8008DE511 /* FEFiberMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F332142ACD8008DE511 /* FEElasticShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322F342142ACD8008DE511 /* FEBioMech.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMech.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F352142ACD8008DE511 /* FEUncoupledActiveContraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUncoupledActiveContraction.h; sourceTree = \"<group>\"; };\n\t\tD5322F362142ACD8008DE511 /* FEMuscleMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMuscleMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F372142ACD8008DE511 /* FEPeriodicLinearConstraint2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPeriodicLinearConstraint2O.h; sourceTree = \"<group>\"; };\n\t\tD5322F382142ACD8008DE511 /* FEFiberEFDNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberEFDNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322F392142ACD8008DE511 /* FEVerondaWestmann.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEVerondaWestmann.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F3A2142ACD8008DE511 /* FEVerondaWestmann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEVerondaWestmann.h; sourceTree = \"<group>\"; };\n\t\tD5322F3B2142ACD8008DE511 /* FEDistanceConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDistanceConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322F3C2142ACD8008DE511 /* FEElasticDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F3D2142ACD8008DE511 /* FEEllipsoidalFiberDistribution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEllipsoidalFiberDistribution.h; sourceTree = \"<group>\"; };\n\t\tD5322F3E2142ACD8008DE511 /* FETransIsoMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETransIsoMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322F3F2142ACD8008DE511 /* triangle_sphere.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = triangle_sphere.h; sourceTree = \"<group>\"; };\n\t\tD5322F402142ACD8008DE511 /* FEPrescribedActiveContractionUniaxialUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedActiveContractionUniaxialUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F412142ACD8008DE511 /* FEElasticMaterial2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMaterial2O.h; sourceTree = \"<group>\"; };\n\t\tD5322F422142ACD8008DE511 /* FEFiberIntegrationScheme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberIntegrationScheme.h; sourceTree = \"<group>\"; };\n\t\tD5322F432142ACD8008DE511 /* FEHuiskesSupply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEHuiskesSupply.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F442142ACD8008DE511 /* FEArrudaBoyce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEArrudaBoyce.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F452142ACD8008DE511 /* FEFiberDensityDistribution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberDensityDistribution.h; sourceTree = \"<group>\"; };\n\t\tD5322F462142ACD8008DE511 /* FEHolmesMow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEHolmesMow.h; sourceTree = \"<group>\"; };\n\t\tD5322F472142ACD8008DE511 /* FEElasticMultiscaleDomain1O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMultiscaleDomain1O.h; sourceTree = \"<group>\"; };\n\t\tD5322F482142ACD8008DE511 /* FEPeriodicBoundary2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPeriodicBoundary2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F4A2142ACD8008DE511 /* FECubicCLE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECubicCLE.h; sourceTree = \"<group>\"; };\n\t\tD5322F4B2142ACD8008DE511 /* FEPressureLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPressureLoad.h; sourceTree = \"<group>\"; };\n\t\tD5322F4C2142ACD8008DE511 /* FEFacet2FacetTied.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFacet2FacetTied.h; sourceTree = \"<group>\"; };\n\t\tD5322F4E2142ACD8008DE511 /* FEElasticMaterial2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMaterial2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F502142ACD8008DE511 /* FEEFDUncoupled.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEFDUncoupled.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F512142ACD8008DE511 /* FEGentMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGentMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322F522142ACD8008DE511 /* FEDamageCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageCriterion.h; sourceTree = \"<group>\"; };\n\t\tD5322F532142ACD8008DE511 /* FEContinuousFiberDistribution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContinuousFiberDistribution.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F552142ACD8008DE511 /* FEFiberEFDNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberEFDNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F562142ACD8008DE511 /* FETrussMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETrussMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F572142ACD8008DE511 /* FEBioMechData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMechData.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F592142ACD8008DE511 /* FEDonnanEquilibrium.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDonnanEquilibrium.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F5A2142ACD8008DE511 /* FEOgdenUnconstrained.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOgdenUnconstrained.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F5B2142ACD8008DE511 /* FETendonMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETendonMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322F5C2142ACD8008DE511 /* FEDamageMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5322F5D2142ACD8008DE511 /* FERigidAngularDamper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidAngularDamper.h; sourceTree = \"<group>\"; };\n\t\tD5322F5E2142ACD8008DE511 /* FETiedElasticInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedElasticInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F5F2142ACD8008DE511 /* FERemodelingElasticMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERemodelingElasticMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322F602142ACD8008DE511 /* FEFiberIntegrationTrapezoidal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberIntegrationTrapezoidal.h; sourceTree = \"<group>\"; };\n\t\tD5322F612142ACD8008DE511 /* FEFiberPowLinearUncoupled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberPowLinearUncoupled.h; sourceTree = \"<group>\"; };\n\t\tD5322F622142ACD8008DE511 /* FE3FieldElasticSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FE3FieldElasticSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F632142ACD8008DE511 /* FEUncoupledFiberExpLinear.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUncoupledFiberExpLinear.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F642142ACD8008DE511 /* FEPRLig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPRLig.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F652142ACD8008DE511 /* FEFiberPowLinear.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberPowLinear.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F662142ACD8008DE511 /* FEArrudaBoyce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEArrudaBoyce.h; sourceTree = \"<group>\"; };\n\t\tD5322F682142ACD8008DE511 /* FEReactiveVEMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactiveVEMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5322F692142ACD8008DE511 /* FEStickyInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEStickyInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F6A2142ACD8008DE511 /* FEIsotropicElastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEIsotropicElastic.h; sourceTree = \"<group>\"; };\n\t\tD5322F6B2142ACD8008DE511 /* FEDamageNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F6C2142ACD8008DE511 /* FEMRVonMisesFibers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMRVonMisesFibers.h; sourceTree = \"<group>\"; };\n\t\tD5322F6D2142ACD8008DE511 /* FEPrescribedActiveContractionIsotropic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedActiveContractionIsotropic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F6E2142ACD8008DE511 /* FEElasticSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322F6F2142ACD8008DE511 /* FERigidJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F702142ACD8008DE511 /* FEContinuousFiberDistributionUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContinuousFiberDistributionUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F712142ACD8008DE511 /* gauss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gauss.h; sourceTree = \"<group>\"; };\n\t\tD5322F722142ACD8008DE511 /* FEUncoupledViscoElasticMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUncoupledViscoElasticMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F732142ACD8008DE511 /* FEElasticShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F752142ACD8008DE511 /* FETransIsoVerondaWestmann.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETransIsoVerondaWestmann.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F762142ACD8008DE511 /* FEElasticMultiscaleDomain2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMultiscaleDomain2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F772142ACD8008DE511 /* FEUncoupledViscoElasticMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUncoupledViscoElasticMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322F782142ACD8008DE511 /* FEViscoElasticMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEViscoElasticMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F792142ACD8008DE511 /* FEExplicitSolidSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEExplicitSolidSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F7A2142ACD8008DE511 /* FEEFDUncoupled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEFDUncoupled.h; sourceTree = \"<group>\"; };\n\t\tD5322F7B2142ACD8008DE511 /* FEFacet2FacetTied.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFacet2FacetTied.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F7C2142ACD8008DE511 /* FEReactiveVEMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactiveVEMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F7D2142ACD8008DE511 /* FECentrifugalBodyForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECentrifugalBodyForce.h; sourceTree = \"<group>\"; };\n\t\tD5322F7E2142ACD8008DE511 /* FESolidMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F7F2142ACD8008DE511 /* FEFiberNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F802142ACD8008DE511 /* FECGSolidSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECGSolidSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322F812142ACD8008DE511 /* FETiedInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F822142ACD8008DE511 /* FERigidPrismaticJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidPrismaticJoint.h; sourceTree = \"<group>\"; };\n\t\tD5322F832142ACD8008DE511 /* FEMindlinElastic2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMindlinElastic2O.h; sourceTree = \"<group>\"; };\n\t\tD5322F842142ACD8008DE511 /* FENeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322F852142ACD8008DE511 /* FESolidSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322F872142ACD8008DE511 /* FERigidDamper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidDamper.h; sourceTree = \"<group>\"; };\n\t\tD5322F882142ACD8008DE511 /* FERigidSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F892142ACD8008DE511 /* FETransIsoVerondaWestmann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETransIsoVerondaWestmann.h; sourceTree = \"<group>\"; };\n\t\tD5322F8A2142ACD8008DE511 /* FEElasticSolidDomain2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticSolidDomain2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F8B2142ACD8008DE511 /* FEPrescribedActiveContractionIsotropic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedActiveContractionIsotropic.h; sourceTree = \"<group>\"; };\n\t\tD5322F8D2142ACD8008DE511 /* FEDamageCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F8E2142ACD8008DE511 /* FEUT4Domain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUT4Domain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F8F2142ACD8008DE511 /* FENeoHookeanTransIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENeoHookeanTransIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F902142ACD8008DE511 /* FEDamageMaterialUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageMaterialUC.h; sourceTree = \"<group>\"; };\n\t\tD5322F912142ACD8008DE511 /* FEDeformableSpringDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDeformableSpringDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322F922142ACD8008DE511 /* FERigidMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322F932142ACD8008DE511 /* FEAugLagLinearConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEAugLagLinearConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F942142ACD8008DE511 /* FEUncoupledReactiveViscoelastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUncoupledReactiveViscoelastic.h; sourceTree = \"<group>\"; };\n\t\tD5322F952142ACD8008DE511 /* FEElasticSolidDomain2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticSolidDomain2O.h; sourceTree = \"<group>\"; };\n\t\tD5322F962142ACD8008DE511 /* FEElasticEASShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticEASShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322F972142ACD8008DE511 /* FERigidLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidLock.h; sourceTree = \"<group>\"; };\n\t\tD5322F982142ACD8008DE511 /* FEPrescribedActiveContractionTransIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedActiveContractionTransIso.h; sourceTree = \"<group>\"; };\n\t\tD5322F9A2142ACD8008DE511 /* FEResidualVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEResidualVector.h; sourceTree = \"<group>\"; };\n\t\tD5322F9B2142ACD8008DE511 /* FENewtonianViscousSolid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENewtonianViscousSolid.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F9D2142ACD8008DE511 /* FERigidContractileForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidContractileForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F9E2142ACD8008DE511 /* FEUDGHexDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUDGHexDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322F9F2142ACD8008DE511 /* FEBioMechPlot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMechPlot.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA02142ACD8008DE511 /* FEPointConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPointConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA12142ACD8008DE511 /* FE3FieldElasticShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FE3FieldElasticShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322FA22142ACD8008DE511 /* FEEFDDonnanEquilibrium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEFDDonnanEquilibrium.h; sourceTree = \"<group>\"; };\n\t\tD5322FA32142ACD8008DE511 /* FECGSolidSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECGSolidSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA42142ACD8008DE511 /* FEPeriodicSurfaceConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPeriodicSurfaceConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA52142ACD8008DE511 /* FEUncoupledActiveContraction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUncoupledActiveContraction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA62142ACD8008DE511 /* FERigidDamper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidDamper.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA72142ACD8008DE511 /* FEMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322FA82142ACD8008DE511 /* FERVEModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERVEModel.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FA92142ACD8008DE511 /* FERigidAngularDamper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidAngularDamper.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FAA2142ACD8008DE511 /* FEDamageMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FAB2142ACD8008DE511 /* FEMRVonMisesFibers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMRVonMisesFibers.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FAC2142ACD8008DE511 /* FEUncoupledMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEUncoupledMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322FAD2142ACD8008DE511 /* FEElasticANSShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticANSShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FAE2142ACD8008DE511 /* FEUncoupledElasticMixture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEUncoupledElasticMixture.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FAF2142ACD8008DE511 /* FETCNonlinearOrthotropic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETCNonlinearOrthotropic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FB02142ACD8008DE511 /* FEFiberIntegrationTriangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberIntegrationTriangle.h; sourceTree = \"<group>\"; };\n\t\tD5322FB12142ACD8008DE511 /* FERigidSlidingContact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSlidingContact.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FB32142ACD8008DE511 /* FESolidMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322FB42142ACD8008DE511 /* FEIncompNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEIncompNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322FB52142ACD8008DE511 /* FETractionLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETractionLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FB62142ACD8008DE511 /* FEPerfectOsmometer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPerfectOsmometer.h; sourceTree = \"<group>\"; };\n\t\tD5322FB72142ACD8008DE511 /* FEPerfectOsmometer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPerfectOsmometer.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FB82142ACD8008DE511 /* FESolidSolver2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidSolver2.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FBA2142ACD8008DE511 /* FEMindlinElastic2O.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMindlinElastic2O.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FBB2142ACD8008DE511 /* FEPointConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPointConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5322FBC2142ACD8008DE511 /* FEDamageMaterialUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDamageMaterialUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FBD2142ACD8008DE511 /* FERigidForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidForce.h; sourceTree = \"<group>\"; };\n\t\tD5322FBE2142ACD8008DE511 /* FETransIsoMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETransIsoMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FBF2142ACD8008DE511 /* FECubicCLE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECubicCLE.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FC02142ACD8008DE511 /* FENewtonianViscousSolidUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENewtonianViscousSolidUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FC12142ACD8008DE511 /* FEDamageMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDamageMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322FC22142ACD8008DE511 /* FERigidContractileForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidContractileForce.h; sourceTree = \"<group>\"; };\n\t\tD5322FC32142ACD8008DE511 /* FETCNonlinearOrthotropic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETCNonlinearOrthotropic.h; sourceTree = \"<group>\"; };\n\t\tD5322FC42142ACD8008DE511 /* FEOsmoticVirialExpansion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOsmoticVirialExpansion.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FC52142ACD8008DE511 /* FEEFDMooneyRivlin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEFDMooneyRivlin.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FC62142ACD8008DE511 /* FERigidSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322FC72142ACD8008DE511 /* FERigidCylindricalJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidCylindricalJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FC82142ACD8008DE511 /* FEHuiskesSupply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEHuiskesSupply.h; sourceTree = \"<group>\"; };\n\t\tD5322FC92142ACD8008DE511 /* FE2DTransIsoVerondaWestmann.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FE2DTransIsoVerondaWestmann.h; sourceTree = \"<group>\"; };\n\t\tD5322FCA2142ACD8008DE511 /* FETiedContactSurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedContactSurface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FCC2142ACD8008DE511 /* FERemodelingElasticMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERemodelingElasticMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FCD2142ACD8008DE511 /* FEPrescribedActiveContractionUniaxialUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedActiveContractionUniaxialUC.h; sourceTree = \"<group>\"; };\n\t\tD5322FCE2142ACD8008DE511 /* FECoupledVerondaWestmann.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoupledVerondaWestmann.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FCF2142ACD8008DE511 /* FEViscousMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEViscousMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FD02142ACD8008DE511 /* FECoupledTransIsoMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoupledTransIsoMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322FD12142ACD8008DE511 /* FEFiberNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5322FD32142ACD8008DE511 /* FE3FieldElasticSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FE3FieldElasticSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322FD42142ACD8008DE511 /* FEElasticTrussDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticTrussDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FD52142ACD8008DE511 /* FEFiberIntegrationGeodesic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberIntegrationGeodesic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FD62142ACD8008DE511 /* FEBodyForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBodyForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FD72142ACD8008DE511 /* FEPrescribedActiveContractionTransIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedActiveContractionTransIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FD82142ACD8008DE511 /* FEBondRelaxation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBondRelaxation.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FD92142ACD8008DE511 /* FEPointBodyForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPointBodyForce.h; sourceTree = \"<group>\"; };\n\t\tD5322FDA2142ACD8008DE511 /* FEElasticEASShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticEASShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FDB2142ACD8008DE511 /* FEElasticFiberMaterialUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticFiberMaterialUC.h; sourceTree = \"<group>\"; };\n\t\tD5322FDC2142ACD8008DE511 /* FERigidCable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidCable.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FDD2142ACD8008DE511 /* FEMergedConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMergedConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FDE2142ACD8008DE511 /* FEPeriodicBoundary2O.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPeriodicBoundary2O.h; sourceTree = \"<group>\"; };\n\t\tD5322FDF2142ACD8008DE511 /* FEFiberIntegrationGauss.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberIntegrationGauss.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FE02142ACD8008DE511 /* FESpringMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESpringMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5322FE12142ACD8008DE511 /* FE2DFiberNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FE2DFiberNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FE22142ACD8008DE511 /* FESSIShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESSIShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FE32142ACD8008DE511 /* FECoupledMooneyRivlin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoupledMooneyRivlin.h; sourceTree = \"<group>\"; };\n\t\tD5322FE42142ACD8008DE511 /* FEContactInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContactInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322FE52142ACD8008DE511 /* FEMortarTiedContact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMortarTiedContact.cpp; sourceTree = \"<group>\"; };\n\t\tD532318B2142AD6E008DE511 /* libgsl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgsl.a; path = /usr/local/Cellar/gsl/2.6/lib/libgsl.a; sourceTree = \"<absolute>\"; };\n\t\tD54A66B12581AAF90023C8D1 /* FEAzimuthConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEAzimuthConstraint.h; sourceTree = \"<group>\"; };\n\t\tD54A66B22581AAF90023C8D1 /* FEAzimuthConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEAzimuthConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD550834024F07EBD00E919D8 /* FEReactivePlasticDamageMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactivePlasticDamageMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD550834124F07EBD00E919D8 /* FEReactivePlasticDamage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactivePlasticDamage.h; sourceTree = \"<group>\"; };\n\t\tD550834224F07EBD00E919D8 /* FEReactivePlasticDamageMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactivePlasticDamageMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD550834324F07EBE00E919D8 /* FEReactivePlasticDamage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactivePlasticDamage.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D2A217B5FCF007CAB89 /* FEElasticMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElasticMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5613D2B217B5FCF007CAB89 /* FEFiberExponentialPowerUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberExponentialPowerUC.h; sourceTree = \"<group>\"; };\n\t\tD5613D2C217B5FD0007CAB89 /* FEFiberNHUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberNHUC.h; sourceTree = \"<group>\"; };\n\t\tD5613D2D217B5FD0007CAB89 /* FEFiberExponentialPowerUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberExponentialPowerUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D2E217B5FD0007CAB89 /* FEElasticMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElasticMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D2F217B5FD0007CAB89 /* FEFiberNHUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberNHUC.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDBC215D28A300E08ED6 /* FERigidSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSystem.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDBD215D28A300E08ED6 /* RigidBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RigidBC.h; sourceTree = \"<group>\"; };\n\t\tD565CDBE215D28A400E08ED6 /* FERigidBody.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidBody.h; sourceTree = \"<group>\"; };\n\t\tD565CDBF215D28A400E08ED6 /* FERigidSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSystem.h; sourceTree = \"<group>\"; };\n\t\tD565CDC0215D28A400E08ED6 /* ObjectDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDC1215D28A400E08ED6 /* RigidBC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RigidBC.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDC2215D28A400E08ED6 /* FEMechModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMechModel.h; sourceTree = \"<group>\"; };\n\t\tD565CDC3215D28A400E08ED6 /* FEMechModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMechModel.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDC4215D28A400E08ED6 /* FERigidBody.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidBody.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDC5215D28A500E08ED6 /* FERigidSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERigidSurface.h; sourceTree = \"<group>\"; };\n\t\tD565CDC6215D28A500E08ED6 /* FERigidSurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidSurface.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDC7215D28A500E08ED6 /* ObjectDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD56DB99A255C162D0072414C /* FETorsionalSpring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETorsionalSpring.h; sourceTree = \"<group>\"; };\n\t\tD56DB99B255C162D0072414C /* FETorsionalSpring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETorsionalSpring.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D8322832FD1007CAB0A /* FEInitialVelocity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEInitialVelocity.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D8422832FD1007CAB0A /* FESolidLinearSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidLinearSystem.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D8522832FD1007CAB0A /* FENodalForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodalForce.h; sourceTree = \"<group>\"; };\n\t\tD5709D8622832FD1007CAB0A /* FEMaxDamageCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMaxDamageCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D8722832FD1007CAB0A /* FENodalForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodalForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D8822832FD1007CAB0A /* FEInitialVelocity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEInitialVelocity.h; sourceTree = \"<group>\"; };\n\t\tD5709D8922832FD1007CAB0A /* FEMaxDamageCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMaxDamageCriterion.h; sourceTree = \"<group>\"; };\n\t\tD5709D8A22832FD1007CAB0A /* FESolidLinearSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidLinearSystem.h; sourceTree = \"<group>\"; };\n\t\tD576A7D3256C366B00415BF7 /* FERigidFollowerForce.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidFollowerForce.cpp; sourceTree = \"<group>\"; };\n\t\tD576A7D4256C366B00415BF7 /* FERigidFollowerForce.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FERigidFollowerForce.h; sourceTree = \"<group>\"; };\n\t\tD57DD3DB23F6EF2B001347CB /* FEGenericHyperelasticUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGenericHyperelasticUC.cpp; sourceTree = \"<group>\"; };\n\t\tD57DD3DC23F6EF2B001347CB /* FEGenericHyperelastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGenericHyperelastic.cpp; sourceTree = \"<group>\"; };\n\t\tD57DD3DD23F6EF2B001347CB /* FEGenericHyperelastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGenericHyperelastic.h; sourceTree = \"<group>\"; };\n\t\tD57DD3DE23F6EF2B001347CB /* FEGenericHyperelasticUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGenericHyperelasticUC.h; sourceTree = \"<group>\"; };\n\t\tD57EE139256F08310068733E /* FERigidFollowerMoment.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FERigidFollowerMoment.cpp; sourceTree = \"<group>\"; };\n\t\tD57EE13A256F08310068733E /* FERigidFollowerMoment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FERigidFollowerMoment.h; sourceTree = \"<group>\"; };\n\t\tD5885714260B985A00659AEF /* FESpringRuptureCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESpringRuptureCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5885715260B985A00659AEF /* FESpringRuptureCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESpringRuptureCriterion.h; sourceTree = \"<group>\"; };\n\t\tD58FA374247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceAttractionBodyForce.cpp; sourceTree = \"<group>\"; };\n\t\tD58FA375247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FESurfaceAttractionBodyForce.h; sourceTree = \"<group>\"; };\n\t\tD5B805B9223BED2800198805 /* febiomech_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febiomech_api.h; sourceTree = \"<group>\"; };\n\t\tD5B939A722D23D4700E495BA /* FEReactivePlasticity.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactivePlasticity.cpp; sourceTree = \"<group>\"; };\n\t\tD5B939A822D23D4700E495BA /* FEReactivePlasticity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEReactivePlasticity.h; sourceTree = \"<group>\"; };\n\t\tD5B939AB22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactivePlasticityMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5B939AC22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEReactivePlasticityMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5BA532E240D541F001A5C0F /* FEBCRigidDeformation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBCRigidDeformation.cpp; sourceTree = \"<group>\"; };\n\t\tD5BA532F240D541F001A5C0F /* FEBCRigidDeformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBCRigidDeformation.h; sourceTree = \"<group>\"; };\n\t\tD5BC983B26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberKiousisUncoupled.cpp; sourceTree = \"<group>\"; };\n\t\tD5BC983C26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFiberKiousisUncoupled.h; sourceTree = \"<group>\"; };\n\t\tD5BC9F4926D07B06003BBF6E /* FEMassDamping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMassDamping.cpp; sourceTree = \"<group>\"; };\n\t\tD5BC9F4A26D07B06003BBF6E /* FEContactPotential.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContactPotential.cpp; sourceTree = \"<group>\"; };\n\t\tD5BC9F4B26D07B06003BBF6E /* FEMassDamping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMassDamping.h; sourceTree = \"<group>\"; };\n\t\tD5BC9F4C26D07B07003BBF6E /* FEContactPotential.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContactPotential.h; sourceTree = \"<group>\"; };\n\t\tD5BD65962333E31F00EB9FD1 /* FEPreStrainConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPreStrainConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5BD65972333E31F00EB9FD1 /* FEPreStrainUncoupledElastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPreStrainUncoupledElastic.h; sourceTree = \"<group>\"; };\n\t\tD5BD65982333E32000EB9FD1 /* FEPreStrainElastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPreStrainElastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5BD65992333E32000EB9FD1 /* FEInSituStretchGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEInSituStretchGradient.h; sourceTree = \"<group>\"; };\n\t\tD5BD659A2333E32000EB9FD1 /* FEConstPrestrain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConstPrestrain.h; sourceTree = \"<group>\"; };\n\t\tD5BD659B2333E32000EB9FD1 /* FEInSituStretchGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEInSituStretchGradient.cpp; sourceTree = \"<group>\"; };\n\t\tD5BD659C2333E32000EB9FD1 /* FEPreStrainUncoupledElastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPreStrainUncoupledElastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5BD659D2333E32000EB9FD1 /* FEConstPrestrain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEConstPrestrain.cpp; sourceTree = \"<group>\"; };\n\t\tD5BD659E2333E32000EB9FD1 /* FEInitialPreStrain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEInitialPreStrain.cpp; sourceTree = \"<group>\"; };\n\t\tD5BD659F2333E32000EB9FD1 /* FEInitialPreStrain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEInitialPreStrain.h; sourceTree = \"<group>\"; };\n\t\tD5BD65A02333E32000EB9FD1 /* FEPreStrainConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPreStrainConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5BD65A12333E32000EB9FD1 /* FEPreStrainElastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPreStrainElastic.h; sourceTree = \"<group>\"; };\n\t\tD5C071F32151A977003321F9 /* FEGenericBodyForce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGenericBodyForce.cpp; sourceTree = \"<group>\"; };\n\t\tD5C071F42151A978003321F9 /* FEGenericBodyForce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGenericBodyForce.h; sourceTree = \"<group>\"; };\n\t\tD5C123E22411845500657663 /* FEGenericTransIsoHyperelasticUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGenericTransIsoHyperelasticUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5C123E32411845500657663 /* FEGenericTransIsoHyperelasticUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGenericTransIsoHyperelasticUC.h; sourceTree = \"<group>\"; };\n\t\tD5C13ECC240477A3005897E1 /* FEActiveFiberStressUC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEActiveFiberStressUC.h; sourceTree = \"<group>\"; };\n\t\tD5C13ECD240477A3005897E1 /* FEActiveFiberStress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEActiveFiberStress.h; sourceTree = \"<group>\"; };\n\t\tD5C13ECE240477A3005897E1 /* FEActiveFiberStress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEActiveFiberStress.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13ECF240477A3005897E1 /* FEActiveFiberStressUC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEActiveFiberStressUC.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13ED0240477A3005897E1 /* FEGenericRigidJoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGenericRigidJoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5C13ED1240477A3005897E1 /* FEGenericRigidJoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGenericRigidJoint.h; sourceTree = \"<group>\"; };\n\t\tD5D56E7E235523790078BCC4 /* FENodeToNodeConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeToNodeConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5D56E7F235523790078BCC4 /* FENodeToNodeConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeToNodeConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5D741CC2543427A002B2825 /* FEContinuousElasticDamage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContinuousElasticDamage.h; sourceTree = \"<group>\"; };\n\t\tD5D741CD2543427A002B2825 /* FEContinuousElasticDamage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContinuousElasticDamage.cpp; sourceTree = \"<group>\"; };\n\t\tD5D84F00240F21B500DF3D67 /* FEGenericTransIsoHyperelastic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGenericTransIsoHyperelastic.cpp; sourceTree = \"<group>\"; };\n\t\tD5D84F01240F21B500DF3D67 /* FEGenericTransIsoHyperelastic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGenericTransIsoHyperelastic.h; sourceTree = \"<group>\"; };\n\t\tD5D8C9DB26E2776C00C193C3 /* FEKamensky.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEKamensky.h; sourceTree = \"<group>\"; };\n\t\tD5D8C9DC26E2776C00C193C3 /* FEKamensky.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEKamensky.cpp; sourceTree = \"<group>\"; };\n\t\tD5D8C9DD26E2776C00C193C3 /* FEKamenskyUncoupled.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEKamenskyUncoupled.cpp; sourceTree = \"<group>\"; };\n\t\tD5D8C9DE26E2776D00C193C3 /* FEKamenskyUncoupled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEKamenskyUncoupled.h; sourceTree = \"<group>\"; };\n\t\tD5D8C9E326E6AF8E00C193C3 /* FEPlasticFlowCurve.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEPlasticFlowCurve.cpp; sourceTree = \"<group>\"; };\n\t\tD5D8C9E426E6AF8E00C193C3 /* FEPlasticFlowCurve.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEPlasticFlowCurve.h; sourceTree = \"<group>\"; };\n\t\tD5DA959C23D3540200D38BF2 /* FEDiscreteElementMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteElementMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5DA959D23D3540200D38BF2 /* FEDiscreteElementMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteElementMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5DAB6CE22A954F1004C4213 /* FERVEDamageMaterial.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FERVEDamageMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5DAB6CF22A954F1004C4213 /* FERVEDamageMaterial.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FERVEDamageMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5DEA26026F52F6900E26ED3 /* FEFiberNaturalNeoHookean.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberNaturalNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5DEA26126F52F6900E26ED3 /* FEFiberNaturalNeoHookean.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFiberNaturalNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5E1AA8C25E3F892003162AB /* FEDeformationMapGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDeformationMapGenerator.h; sourceTree = \"<group>\"; };\n\t\tD5E1AA8D25E3F892003162AB /* FEDeformationMapGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDeformationMapGenerator.cpp; sourceTree = \"<group>\"; };\n\t\tD5EBB79F2328231800EED2BE /* FECarreauYasudaViscousSolid.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FECarreauYasudaViscousSolid.cpp; sourceTree = \"<group>\"; };\n\t\tD5EBB7A02328231800EED2BE /* FECarreauYasudaViscousSolid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FECarreauYasudaViscousSolid.h; sourceTree = \"<group>\"; };\n\t\tD5F09070248A84FA00E4F940 /* FEDiscreteElasticMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteElasticMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5F09071248A84FA00E4F940 /* FEDiscreteElasticMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteElasticMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5F09072248A84FA00E4F940 /* FEDiscreteElasticDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteElasticDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5F09073248A84FA00E4F940 /* FEDiscreteElasticDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteElasticDomain.h; sourceTree = \"<group>\"; };\n\t\tD5F72F9E21601E6300271806 /* FEFatigueMaterial.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEFatigueMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5F72F9F21601E6300271806 /* FEFatigueMaterial.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEFatigueMaterial.h; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5322E322142ACC9008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD532318C2142AD6E008DE511 /* libgsl.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5322E2C2142ACC9008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322E432142ACD7008DE511 /* FEBioMech */,\n\t\t\t\tD5322E362142ACC9008DE511 /* Products */,\n\t\t\t\tD532318A2142AD6D008DE511 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322E362142ACC9008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322E352142ACC9008DE511 /* libFEBioMech.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322E432142ACD7008DE511 /* FEBioMech */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322FE12142ACD8008DE511 /* FE2DFiberNeoHookean.cpp */,\n\t\t\t\tD5322ED22142ACD7008DE511 /* FE2DFiberNeoHookean.h */,\n\t\t\t\tD5322EB62142ACD7008DE511 /* FE2DTransIsoMooneyRivlin.cpp */,\n\t\t\t\tD5322E642142ACD7008DE511 /* FE2DTransIsoMooneyRivlin.h */,\n\t\t\t\tD5322F282142ACD8008DE511 /* FE2DTransIsoVerondaWestmann.cpp */,\n\t\t\t\tD5322FC92142ACD8008DE511 /* FE2DTransIsoVerondaWestmann.h */,\n\t\t\t\tD5322EC22142ACD7008DE511 /* FE2OMicroConstraint.cpp */,\n\t\t\t\tD5322F0E2142ACD8008DE511 /* FE2OMicroConstraint.h */,\n\t\t\t\tD5322F1D2142ACD8008DE511 /* FE3FieldElasticShellDomain.cpp */,\n\t\t\t\tD5322FA12142ACD8008DE511 /* FE3FieldElasticShellDomain.h */,\n\t\t\t\tD5322F622142ACD8008DE511 /* FE3FieldElasticSolidDomain.cpp */,\n\t\t\t\tD5322FD32142ACD8008DE511 /* FE3FieldElasticSolidDomain.h */,\n\t\t\t\tD5322E9E2142ACD7008DE511 /* FEActiveFiberContraction.cpp */,\n\t\t\t\tD5322EC02142ACD7008DE511 /* FEActiveFiberContraction.h */,\n\t\t\t\tD5C13ECE240477A3005897E1 /* FEActiveFiberStress.cpp */,\n\t\t\t\tD5C13ECD240477A3005897E1 /* FEActiveFiberStress.h */,\n\t\t\t\tD5C13ECF240477A3005897E1 /* FEActiveFiberStressUC.cpp */,\n\t\t\t\tD5C13ECC240477A3005897E1 /* FEActiveFiberStressUC.h */,\n\t\t\t\tD5322F442142ACD8008DE511 /* FEArrudaBoyce.cpp */,\n\t\t\t\tD5322F662142ACD8008DE511 /* FEArrudaBoyce.h */,\n\t\t\t\tD5322F932142ACD8008DE511 /* FEAugLagLinearConstraint.cpp */,\n\t\t\t\tD5322E682142ACD7008DE511 /* FEAugLagLinearConstraint.h */,\n\t\t\t\tD54A66B22581AAF90023C8D1 /* FEAzimuthConstraint.cpp */,\n\t\t\t\tD54A66B12581AAF90023C8D1 /* FEAzimuthConstraint.h */,\n\t\t\t\tD5322F2D2142ACD8008DE511 /* FEBCPrescribedDeformation.cpp */,\n\t\t\t\tD5322EAE2142ACD7008DE511 /* FEBCPrescribedDeformation.h */,\n\t\t\t\tD5BA532E240D541F001A5C0F /* FEBCRigidDeformation.cpp */,\n\t\t\t\tD5BA532F240D541F001A5C0F /* FEBCRigidDeformation.h */,\n\t\t\t\tD5B805B9223BED2800198805 /* febiomech_api.h */,\n\t\t\t\tD5322F342142ACD8008DE511 /* FEBioMech.cpp */,\n\t\t\t\tD5322F0A2142ACD8008DE511 /* FEBioMech.h */,\n\t\t\t\tD5322F572142ACD8008DE511 /* FEBioMechData.cpp */,\n\t\t\t\tD5322F222142ACD8008DE511 /* FEBioMechData.h */,\n\t\t\t\tD511EF11253A74AE00893F69 /* FEBioMechModule.cpp */,\n\t\t\t\tD511EF12253A74AE00893F69 /* FEBioMechModule.h */,\n\t\t\t\tD5322F9F2142ACD8008DE511 /* FEBioMechPlot.cpp */,\n\t\t\t\tD5322E802142ACD7008DE511 /* FEBioMechPlot.h */,\n\t\t\t\tD5322FD62142ACD8008DE511 /* FEBodyForce.cpp */,\n\t\t\t\tD5322ECD2142ACD7008DE511 /* FEBodyForce.h */,\n\t\t\t\tD5322FD82142ACD8008DE511 /* FEBondRelaxation.cpp */,\n\t\t\t\tD5322E962142ACD7008DE511 /* FEBondRelaxation.h */,\n\t\t\t\tD5EBB79F2328231800EED2BE /* FECarreauYasudaViscousSolid.cpp */,\n\t\t\t\tD5EBB7A02328231800EED2BE /* FECarreauYasudaViscousSolid.h */,\n\t\t\t\tD5322F102142ACD8008DE511 /* FECarterHayesOld.cpp */,\n\t\t\t\tD5322E7B2142ACD7008DE511 /* FECarterHayesOld.h */,\n\t\t\t\tD5322E892142ACD7008DE511 /* FECellGrowth.cpp */,\n\t\t\t\tD5322E6D2142ACD7008DE511 /* FECellGrowth.h */,\n\t\t\t\tD5322EBD2142ACD7008DE511 /* FECentrifugalBodyForce.cpp */,\n\t\t\t\tD5322F7D2142ACD8008DE511 /* FECentrifugalBodyForce.h */,\n\t\t\t\tD5322FA32142ACD8008DE511 /* FECGSolidSolver.cpp */,\n\t\t\t\tD5322F802142ACD8008DE511 /* FECGSolidSolver.h */,\n\t\t\t\tD5BD659D2333E32000EB9FD1 /* FEConstPrestrain.cpp */,\n\t\t\t\tD5BD659A2333E32000EB9FD1 /* FEConstPrestrain.h */,\n\t\t\t\tD5322FE42142ACD8008DE511 /* FEContactInterface.cpp */,\n\t\t\t\tD5322EA12142ACD7008DE511 /* FEContactInterface.h */,\n\t\t\t\tD5BC9F4A26D07B06003BBF6E /* FEContactPotential.cpp */,\n\t\t\t\tD5BC9F4C26D07B07003BBF6E /* FEContactPotential.h */,\n\t\t\t\tD5322F1F2142ACD8008DE511 /* FEContactSurface.cpp */,\n\t\t\t\tD5322E6A2142ACD7008DE511 /* FEContactSurface.h */,\n\t\t\t\tD5D741CD2543427A002B2825 /* FEContinuousElasticDamage.cpp */,\n\t\t\t\tD5D741CC2543427A002B2825 /* FEContinuousElasticDamage.h */,\n\t\t\t\tD5322F532142ACD8008DE511 /* FEContinuousFiberDistribution.cpp */,\n\t\t\t\tD5322E7C2142ACD7008DE511 /* FEContinuousFiberDistribution.h */,\n\t\t\t\tD5322F702142ACD8008DE511 /* FEContinuousFiberDistributionUC.cpp */,\n\t\t\t\tD5322E972142ACD7008DE511 /* FEContinuousFiberDistributionUC.h */,\n\t\t\t\tD5322EFC2142ACD8008DE511 /* FECoupledMooneyRivlin.cpp */,\n\t\t\t\tD5322FE32142ACD8008DE511 /* FECoupledMooneyRivlin.h */,\n\t\t\t\tD5322E772142ACD7008DE511 /* FECoupledTransIsoMooneyRivlin.cpp */,\n\t\t\t\tD5322FD02142ACD8008DE511 /* FECoupledTransIsoMooneyRivlin.h */,\n\t\t\t\tD5322EA82142ACD7008DE511 /* FECoupledTransIsoVerondaWestmann.cpp */,\n\t\t\t\tD5322E672142ACD7008DE511 /* FECoupledTransIsoVerondaWestmann.h */,\n\t\t\t\tD5322FCE2142ACD8008DE511 /* FECoupledVerondaWestmann.cpp */,\n\t\t\t\tD5322EC72142ACD7008DE511 /* FECoupledVerondaWestmann.h */,\n\t\t\t\tD5322FBF2142ACD8008DE511 /* FECubicCLE.cpp */,\n\t\t\t\tD5322F4A2142ACD8008DE511 /* FECubicCLE.h */,\n\t\t\t\tD5322EF92142ACD8008DE511 /* FEDamageCDF.cpp */,\n\t\t\t\tD5322E8C2142ACD7008DE511 /* FEDamageCDF.h */,\n\t\t\t\tD5322F8D2142ACD8008DE511 /* FEDamageCriterion.cpp */,\n\t\t\t\tD5322F522142ACD8008DE511 /* FEDamageCriterion.h */,\n\t\t\t\tD5322F052142ACD8008DE511 /* FEDamageMaterial.cpp */,\n\t\t\t\tD5322FC12142ACD8008DE511 /* FEDamageMaterial.h */,\n\t\t\t\tD5322EA72142ACD7008DE511 /* FEDamageMaterialPoint.cpp */,\n\t\t\t\tD5322F5C2142ACD8008DE511 /* FEDamageMaterialPoint.h */,\n\t\t\t\tD5322FBC2142ACD8008DE511 /* FEDamageMaterialUC.cpp */,\n\t\t\t\tD5322F902142ACD8008DE511 /* FEDamageMaterialUC.h */,\n\t\t\t\tD5322FAA2142ACD8008DE511 /* FEDamageMooneyRivlin.cpp */,\n\t\t\t\tD5322E9B2142ACD7008DE511 /* FEDamageMooneyRivlin.h */,\n\t\t\t\tD5322F6B2142ACD8008DE511 /* FEDamageNeoHookean.cpp */,\n\t\t\t\tD5322E9D2142ACD7008DE511 /* FEDamageNeoHookean.h */,\n\t\t\t\tD5322EA52142ACD7008DE511 /* FEDamageTransIsoMooneyRivlin.cpp */,\n\t\t\t\tD5322EE52142ACD7008DE511 /* FEDamageTransIsoMooneyRivlin.h */,\n\t\t\t\tD5322E442142ACD7008DE511 /* FEDeformableSpringDomain.cpp */,\n\t\t\t\tD5322F912142ACD8008DE511 /* FEDeformableSpringDomain.h */,\n\t\t\t\tD5E1AA8D25E3F892003162AB /* FEDeformationMapGenerator.cpp */,\n\t\t\t\tD5E1AA8C25E3F892003162AB /* FEDeformationMapGenerator.h */,\n\t\t\t\tD5322ED72142ACD7008DE511 /* FEDiscreteContact.cpp */,\n\t\t\t\tD5322ED52142ACD7008DE511 /* FEDiscreteContact.h */,\n\t\t\t\tD5F09072248A84FA00E4F940 /* FEDiscreteElasticDomain.cpp */,\n\t\t\t\tD5F09073248A84FA00E4F940 /* FEDiscreteElasticDomain.h */,\n\t\t\t\tD5F09071248A84FA00E4F940 /* FEDiscreteElasticMaterial.cpp */,\n\t\t\t\tD5F09070248A84FA00E4F940 /* FEDiscreteElasticMaterial.h */,\n\t\t\t\tD5DA959C23D3540200D38BF2 /* FEDiscreteElementMaterial.cpp */,\n\t\t\t\tD5DA959D23D3540200D38BF2 /* FEDiscreteElementMaterial.h */,\n\t\t\t\tD5322E552142ACD7008DE511 /* FEDistanceConstraint.cpp */,\n\t\t\t\tD5322F3B2142ACD8008DE511 /* FEDistanceConstraint.h */,\n\t\t\t\tD5322F592142ACD8008DE511 /* FEDonnanEquilibrium.cpp */,\n\t\t\t\tD5322E862142ACD7008DE511 /* FEDonnanEquilibrium.h */,\n\t\t\t\tD5322EE82142ACD7008DE511 /* FEEFDDonnanEquilibrium.cpp */,\n\t\t\t\tD5322FA22142ACD8008DE511 /* FEEFDDonnanEquilibrium.h */,\n\t\t\t\tD5322FC52142ACD8008DE511 /* FEEFDMooneyRivlin.cpp */,\n\t\t\t\tD5322F312142ACD8008DE511 /* FEEFDMooneyRivlin.h */,\n\t\t\t\tD5322E5E2142ACD7008DE511 /* FEEFDNeoHookean.cpp */,\n\t\t\t\tD5322F022142ACD8008DE511 /* FEEFDNeoHookean.h */,\n\t\t\t\tD5322F502142ACD8008DE511 /* FEEFDUncoupled.cpp */,\n\t\t\t\tD5322F7A2142ACD8008DE511 /* FEEFDUncoupled.h */,\n\t\t\t\tD5322EF42142ACD7008DE511 /* FEEFDVerondaWestmann.cpp */,\n\t\t\t\tD5322EB92142ACD7008DE511 /* FEEFDVerondaWestmann.h */,\n\t\t\t\tD5322FAD2142ACD8008DE511 /* FEElasticANSShellDomain.cpp */,\n\t\t\t\tD5322E502142ACD7008DE511 /* FEElasticANSShellDomain.h */,\n\t\t\t\tD5322F3C2142ACD8008DE511 /* FEElasticDomain.cpp */,\n\t\t\t\tD5322EAF2142ACD7008DE511 /* FEElasticDomain.h */,\n\t\t\t\tD5322FDA2142ACD8008DE511 /* FEElasticEASShellDomain.cpp */,\n\t\t\t\tD5322F962142ACD8008DE511 /* FEElasticEASShellDomain.h */,\n\t\t\t\tD5322EE02142ACD7008DE511 /* FEElasticFiberMaterial.cpp */,\n\t\t\t\tD5322EB02142ACD7008DE511 /* FEElasticFiberMaterial.h */,\n\t\t\t\tD5322E652142ACD7008DE511 /* FEElasticFiberMaterialUC.cpp */,\n\t\t\t\tD5322FDB2142ACD8008DE511 /* FEElasticFiberMaterialUC.h */,\n\t\t\t\tD5322EA42142ACD7008DE511 /* FEElasticMaterial.cpp */,\n\t\t\t\tD5322E572142ACD7008DE511 /* FEElasticMaterial.h */,\n\t\t\t\tD5322F4E2142ACD8008DE511 /* FEElasticMaterial2O.cpp */,\n\t\t\t\tD5322F412142ACD8008DE511 /* FEElasticMaterial2O.h */,\n\t\t\t\tD5613D2E217B5FD0007CAB89 /* FEElasticMaterialPoint.cpp */,\n\t\t\t\tD5613D2A217B5FCF007CAB89 /* FEElasticMaterialPoint.h */,\n\t\t\t\tD5322ED82142ACD7008DE511 /* FEElasticMixture.cpp */,\n\t\t\t\tD5322EE42142ACD7008DE511 /* FEElasticMixture.h */,\n\t\t\t\tD5322EAC2142ACD7008DE511 /* FEElasticMultigeneration.cpp */,\n\t\t\t\tD5322F272142ACD8008DE511 /* FEElasticMultigeneration.h */,\n\t\t\t\tD5322EB82142ACD7008DE511 /* FEElasticMultiscaleDomain1O.cpp */,\n\t\t\t\tD5322F472142ACD8008DE511 /* FEElasticMultiscaleDomain1O.h */,\n\t\t\t\tD5322F762142ACD8008DE511 /* FEElasticMultiscaleDomain2O.cpp */,\n\t\t\t\tD5322F162142ACD8008DE511 /* FEElasticMultiscaleDomain2O.h */,\n\t\t\t\tD5322F732142ACD8008DE511 /* FEElasticShellDomain.cpp */,\n\t\t\t\tD5322F332142ACD8008DE511 /* FEElasticShellDomain.h */,\n\t\t\t\tD5322EDA2142ACD7008DE511 /* FEElasticShellDomainOld.cpp */,\n\t\t\t\tD5322E5D2142ACD7008DE511 /* FEElasticShellDomainOld.h */,\n\t\t\t\tD5322EEC2142ACD7008DE511 /* FEElasticSolidDomain.cpp */,\n\t\t\t\tD5322F6E2142ACD8008DE511 /* FEElasticSolidDomain.h */,\n\t\t\t\tD5322F8A2142ACD8008DE511 /* FEElasticSolidDomain2O.cpp */,\n\t\t\t\tD5322F952142ACD8008DE511 /* FEElasticSolidDomain2O.h */,\n\t\t\t\tD5322FD42142ACD8008DE511 /* FEElasticTrussDomain.cpp */,\n\t\t\t\tD5322F132142ACD8008DE511 /* FEElasticTrussDomain.h */,\n\t\t\t\tD5322E532142ACD7008DE511 /* FEEllipsoidalFiberDistribution.cpp */,\n\t\t\t\tD5322F3D2142ACD8008DE511 /* FEEllipsoidalFiberDistribution.h */,\n\t\t\t\tD5322F792142ACD8008DE511 /* FEExplicitSolidSolver.cpp */,\n\t\t\t\tD5322E452142ACD7008DE511 /* FEExplicitSolidSolver.h */,\n\t\t\t\tD5322EDC2142ACD7008DE511 /* FEFacet2FacetSliding.cpp */,\n\t\t\t\tD5322EC42142ACD7008DE511 /* FEFacet2FacetSliding.h */,\n\t\t\t\tD5322F7B2142ACD8008DE511 /* FEFacet2FacetTied.cpp */,\n\t\t\t\tD5322F4C2142ACD8008DE511 /* FEFacet2FacetTied.h */,\n\t\t\t\tD5F72F9E21601E6300271806 /* FEFatigueMaterial.cpp */,\n\t\t\t\tD5F72F9F21601E6300271806 /* FEFatigueMaterial.h */,\n\t\t\t\tD5322F212142ACD8008DE511 /* FEFiberDensityDistribution.cpp */,\n\t\t\t\tD5322F452142ACD8008DE511 /* FEFiberDensityDistribution.h */,\n\t\t\t\tD5322F552142ACD8008DE511 /* FEFiberEFDNeoHookean.cpp */,\n\t\t\t\tD5322F382142ACD8008DE511 /* FEFiberEFDNeoHookean.h */,\n\t\t\t\tD5322E882142ACD7008DE511 /* FEFiberExpLinear.cpp */,\n\t\t\t\tD5322E4D2142ACD7008DE511 /* FEFiberExpLinear.h */,\n\t\t\t\tD5613D2D217B5FD0007CAB89 /* FEFiberExponentialPowerUC.cpp */,\n\t\t\t\tD5613D2B217B5FCF007CAB89 /* FEFiberExponentialPowerUC.h */,\n\t\t\t\tD5322E4B2142ACD7008DE511 /* FEFiberExpPow.cpp */,\n\t\t\t\tD5322EA62142ACD7008DE511 /* FEFiberExpPow.h */,\n\t\t\t\tD5322EBF2142ACD7008DE511 /* FEFiberExpPowUncoupled.cpp */,\n\t\t\t\tD5322EBA2142ACD7008DE511 /* FEFiberExpPowUncoupled.h */,\n\t\t\t\tD5322FDF2142ACD8008DE511 /* FEFiberIntegrationGauss.cpp */,\n\t\t\t\tD5322ED42142ACD7008DE511 /* FEFiberIntegrationGauss.h */,\n\t\t\t\tD5322E842142ACD7008DE511 /* FEFiberIntegrationGaussKronrod.cpp */,\n\t\t\t\tD5322E712142ACD7008DE511 /* FEFiberIntegrationGaussKronrod.h */,\n\t\t\t\tD5322FD52142ACD8008DE511 /* FEFiberIntegrationGeodesic.cpp */,\n\t\t\t\tD5322E612142ACD7008DE511 /* FEFiberIntegrationGeodesic.h */,\n\t\t\t\tD5322E9C2142ACD7008DE511 /* FEFiberIntegrationScheme.cpp */,\n\t\t\t\tD5322F422142ACD8008DE511 /* FEFiberIntegrationScheme.h */,\n\t\t\t\tD5322E6E2142ACD7008DE511 /* FEFiberIntegrationTrapezoidal.cpp */,\n\t\t\t\tD5322F602142ACD8008DE511 /* FEFiberIntegrationTrapezoidal.h */,\n\t\t\t\tD5322E4A2142ACD7008DE511 /* FEFiberIntegrationTriangle.cpp */,\n\t\t\t\tD5322FB02142ACD8008DE511 /* FEFiberIntegrationTriangle.h */,\n\t\t\t\tD5BC983B26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.cpp */,\n\t\t\t\tD5BC983C26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.h */,\n\t\t\t\tD5322F322142ACD8008DE511 /* FEFiberMaterialPoint.cpp */,\n\t\t\t\tD5322F202142ACD8008DE511 /* FEFiberMaterialPoint.h */,\n\t\t\t\tD5DEA26026F52F6900E26ED3 /* FEFiberNaturalNeoHookean.cpp */,\n\t\t\t\tD5DEA26126F52F6900E26ED3 /* FEFiberNaturalNeoHookean.h */,\n\t\t\t\tD5322F7F2142ACD8008DE511 /* FEFiberNeoHookean.cpp */,\n\t\t\t\tD5322FD12142ACD8008DE511 /* FEFiberNeoHookean.h */,\n\t\t\t\tD5613D2F217B5FD0007CAB89 /* FEFiberNHUC.cpp */,\n\t\t\t\tD5613D2C217B5FD0007CAB89 /* FEFiberNHUC.h */,\n\t\t\t\tD5322F652142ACD8008DE511 /* FEFiberPowLinear.cpp */,\n\t\t\t\tD5322E5B2142ACD7008DE511 /* FEFiberPowLinear.h */,\n\t\t\t\tD5322F182142ACD8008DE511 /* FEFiberPowLinearUncoupled.cpp */,\n\t\t\t\tD5322F612142ACD8008DE511 /* FEFiberPowLinearUncoupled.h */,\n\t\t\t\tD5322F142142ACD8008DE511 /* FEFungOrthoCompressible.cpp */,\n\t\t\t\tD5322E482142ACD7008DE511 /* FEFungOrthoCompressible.h */,\n\t\t\t\tD5322E632142ACD7008DE511 /* FEFungOrthotropic.cpp */,\n\t\t\t\tD5322EDF2142ACD7008DE511 /* FEFungOrthotropic.h */,\n\t\t\t\tD5C071F32151A977003321F9 /* FEGenericBodyForce.cpp */,\n\t\t\t\tD5C071F42151A978003321F9 /* FEGenericBodyForce.h */,\n\t\t\t\tD57DD3DC23F6EF2B001347CB /* FEGenericHyperelastic.cpp */,\n\t\t\t\tD57DD3DD23F6EF2B001347CB /* FEGenericHyperelastic.h */,\n\t\t\t\tD57DD3DB23F6EF2B001347CB /* FEGenericHyperelasticUC.cpp */,\n\t\t\t\tD57DD3DE23F6EF2B001347CB /* FEGenericHyperelasticUC.h */,\n\t\t\t\tD5C13ED0240477A3005897E1 /* FEGenericRigidJoint.cpp */,\n\t\t\t\tD5C13ED1240477A3005897E1 /* FEGenericRigidJoint.h */,\n\t\t\t\tD5D84F00240F21B500DF3D67 /* FEGenericTransIsoHyperelastic.cpp */,\n\t\t\t\tD5D84F01240F21B500DF3D67 /* FEGenericTransIsoHyperelastic.h */,\n\t\t\t\tD5C123E22411845500657663 /* FEGenericTransIsoHyperelasticUC.cpp */,\n\t\t\t\tD5C123E32411845500657663 /* FEGenericTransIsoHyperelasticUC.h */,\n\t\t\t\tD5322EED2142ACD7008DE511 /* FEGentMaterial.cpp */,\n\t\t\t\tD5322F512142ACD8008DE511 /* FEGentMaterial.h */,\n\t\t\t\tD5322F082142ACD8008DE511 /* FEHolmesMow.cpp */,\n\t\t\t\tD5322F462142ACD8008DE511 /* FEHolmesMow.h */,\n\t\t\t\tD5322F1A2142ACD8008DE511 /* FEHolzapfelGasserOgden.cpp */,\n\t\t\t\tD5322E932142ACD7008DE511 /* FEHolzapfelGasserOgden.h */,\n\t\t\t\tD524F6B2258BEB6000A403CD /* FEHolzapfelUnconstrained.cpp */,\n\t\t\t\tD524F6B3258BEB6000A403CD /* FEHolzapfelUnconstrained.h */,\n\t\t\t\tD5322F432142ACD8008DE511 /* FEHuiskesSupply.cpp */,\n\t\t\t\tD5322FC82142ACD8008DE511 /* FEHuiskesSupply.h */,\n\t\t\t\tD5322ED62142ACD7008DE511 /* FEIncompNeoHookean.cpp */,\n\t\t\t\tD5322FB42142ACD8008DE511 /* FEIncompNeoHookean.h */,\n\t\t\t\tD5BD659E2333E32000EB9FD1 /* FEInitialPreStrain.cpp */,\n\t\t\t\tD5BD659F2333E32000EB9FD1 /* FEInitialPreStrain.h */,\n\t\t\t\tD5709D8322832FD1007CAB0A /* FEInitialVelocity.cpp */,\n\t\t\t\tD5709D8822832FD1007CAB0A /* FEInitialVelocity.h */,\n\t\t\t\tD5BD659B2333E32000EB9FD1 /* FEInSituStretchGradient.cpp */,\n\t\t\t\tD5BD65992333E32000EB9FD1 /* FEInSituStretchGradient.h */,\n\t\t\t\tD5322EB22142ACD7008DE511 /* FEIsotropicElastic.cpp */,\n\t\t\t\tD5322F6A2142ACD8008DE511 /* FEIsotropicElastic.h */,\n\t\t\t\tD5D8C9DC26E2776C00C193C3 /* FEKamensky.cpp */,\n\t\t\t\tD5D8C9DB26E2776C00C193C3 /* FEKamensky.h */,\n\t\t\t\tD5D8C9DD26E2776C00C193C3 /* FEKamenskyUncoupled.cpp */,\n\t\t\t\tD5D8C9DE26E2776D00C193C3 /* FEKamenskyUncoupled.h */,\n\t\t\t\tD5BC9F4926D07B06003BBF6E /* FEMassDamping.cpp */,\n\t\t\t\tD5BC9F4B26D07B06003BBF6E /* FEMassDamping.h */,\n\t\t\t\tD5709D8622832FD1007CAB0A /* FEMaxDamageCriterion.cpp */,\n\t\t\t\tD5709D8922832FD1007CAB0A /* FEMaxDamageCriterion.h */,\n\t\t\t\tD51E614A22443FDB0049F545 /* FEMaxStressCriterion.cpp */,\n\t\t\t\tD51E614B22443FDB0049F545 /* FEMaxStressCriterion.h */,\n\t\t\t\tD565CDC3215D28A400E08ED6 /* FEMechModel.cpp */,\n\t\t\t\tD565CDC2215D28A400E08ED6 /* FEMechModel.h */,\n\t\t\t\tD5322F112142ACD8008DE511 /* FEMembraneMaterial.cpp */,\n\t\t\t\tD5322EC62142ACD7008DE511 /* FEMembraneMaterial.h */,\n\t\t\t\tD5322FDD2142ACD8008DE511 /* FEMergedConstraint.cpp */,\n\t\t\t\tD5322E5A2142ACD7008DE511 /* FEMergedConstraint.h */,\n\t\t\t\tD5322F242142ACD8008DE511 /* FEMicroMaterial.cpp */,\n\t\t\t\tD5322E832142ACD7008DE511 /* FEMicroMaterial.h */,\n\t\t\t\tD5322F072142ACD8008DE511 /* FEMicroMaterial2O.cpp */,\n\t\t\t\tD5322E8B2142ACD7008DE511 /* FEMicroMaterial2O.h */,\n\t\t\t\tD5322FBA2142ACD8008DE511 /* FEMindlinElastic2O.cpp */,\n\t\t\t\tD5322F832142ACD8008DE511 /* FEMindlinElastic2O.h */,\n\t\t\t\tD5322ED02142ACD7008DE511 /* FEMooneyRivlin.cpp */,\n\t\t\t\tD5322FA72142ACD8008DE511 /* FEMooneyRivlin.h */,\n\t\t\t\tD5322E992142ACD7008DE511 /* FEMortarContactSurface.cpp */,\n\t\t\t\tD5322F252142ACD8008DE511 /* FEMortarContactSurface.h */,\n\t\t\t\tD5322F062142ACD8008DE511 /* FEMortarInterface.cpp */,\n\t\t\t\tD5322EDE2142ACD7008DE511 /* FEMortarInterface.h */,\n\t\t\t\tD5322E922142ACD7008DE511 /* FEMortarSlidingContact.cpp */,\n\t\t\t\tD5322F2F2142ACD8008DE511 /* FEMortarSlidingContact.h */,\n\t\t\t\tD5322FE52142ACD8008DE511 /* FEMortarTiedContact.cpp */,\n\t\t\t\tD5322E492142ACD7008DE511 /* FEMortarTiedContact.h */,\n\t\t\t\tD5322FAB2142ACD8008DE511 /* FEMRVonMisesFibers.cpp */,\n\t\t\t\tD5322F6C2142ACD8008DE511 /* FEMRVonMisesFibers.h */,\n\t\t\t\tD5322F362142ACD8008DE511 /* FEMuscleMaterial.cpp */,\n\t\t\t\tD5322E602142ACD7008DE511 /* FEMuscleMaterial.h */,\n\t\t\t\tD5135C27232191C6008AFD7D /* FENaturalNeoHookean.cpp */,\n\t\t\t\tD5135C28232191C6008AFD7D /* FENaturalNeoHookean.h */,\n\t\t\t\tD5322EBE2142ACD7008DE511 /* FENeoHookean.cpp */,\n\t\t\t\tD5322F842142ACD8008DE511 /* FENeoHookean.h */,\n\t\t\t\tD5322F8F2142ACD8008DE511 /* FENeoHookeanTransIso.cpp */,\n\t\t\t\tD5322E942142ACD7008DE511 /* FENeoHookeanTransIso.h */,\n\t\t\t\tD5322F9B2142ACD8008DE511 /* FENewtonianViscousSolid.cpp */,\n\t\t\t\tD5322F2E2142ACD8008DE511 /* FENewtonianViscousSolid.h */,\n\t\t\t\tD5322FC02142ACD8008DE511 /* FENewtonianViscousSolidUC.cpp */,\n\t\t\t\tD5322EF72142ACD8008DE511 /* FENewtonianViscousSolidUC.h */,\n\t\t\t\tD5709D8722832FD1007CAB0A /* FENodalForce.cpp */,\n\t\t\t\tD5709D8522832FD1007CAB0A /* FENodalForce.h */,\n\t\t\t\tD5D56E7E235523790078BCC4 /* FENodeToNodeConstraint.cpp */,\n\t\t\t\tD5D56E7F235523790078BCC4 /* FENodeToNodeConstraint.h */,\n\t\t\t\tD5322EFD2142ACD8008DE511 /* FEOgdenMaterial.cpp */,\n\t\t\t\tD5322F0F2142ACD8008DE511 /* FEOgdenMaterial.h */,\n\t\t\t\tD5322F5A2142ACD8008DE511 /* FEOgdenUnconstrained.cpp */,\n\t\t\t\tD5322EE62142ACD7008DE511 /* FEOgdenUnconstrained.h */,\n\t\t\t\tD5322F2C2142ACD8008DE511 /* FEOrthoElastic.cpp */,\n\t\t\t\tD5322F0B2142ACD8008DE511 /* FEOrthoElastic.h */,\n\t\t\t\tD5322E582142ACD7008DE511 /* FEOrthotropicCLE.cpp */,\n\t\t\t\tD5322ED92142ACD7008DE511 /* FEOrthotropicCLE.h */,\n\t\t\t\tD5322FC42142ACD8008DE511 /* FEOsmoticVirialExpansion.cpp */,\n\t\t\t\tD5322F042142ACD8008DE511 /* FEOsmoticVirialExpansion.h */,\n\t\t\t\tD5322FB72142ACD8008DE511 /* FEPerfectOsmometer.cpp */,\n\t\t\t\tD5322FB62142ACD8008DE511 /* FEPerfectOsmometer.h */,\n\t\t\t\tD5322ECE2142ACD7008DE511 /* FEPeriodicBoundary.cpp */,\n\t\t\t\tD5322E702142ACD7008DE511 /* FEPeriodicBoundary.h */,\n\t\t\t\tD5322EF82142ACD8008DE511 /* FEPeriodicBoundary1O.cpp */,\n\t\t\t\tD5322E6B2142ACD7008DE511 /* FEPeriodicBoundary1O.h */,\n\t\t\t\tD5322F482142ACD8008DE511 /* FEPeriodicBoundary2O.cpp */,\n\t\t\t\tD5322FDE2142ACD8008DE511 /* FEPeriodicBoundary2O.h */,\n\t\t\t\tD5322E822142ACD7008DE511 /* FEPeriodicLinearConstraint.cpp */,\n\t\t\t\tD5322E6C2142ACD7008DE511 /* FEPeriodicLinearConstraint.h */,\n\t\t\t\tD5322EEB2142ACD7008DE511 /* FEPeriodicLinearConstraint2O.cpp */,\n\t\t\t\tD5322F372142ACD8008DE511 /* FEPeriodicLinearConstraint2O.h */,\n\t\t\t\tD5322FA42142ACD8008DE511 /* FEPeriodicSurfaceConstraint.cpp */,\n\t\t\t\tD5322F232142ACD8008DE511 /* FEPeriodicSurfaceConstraint.h */,\n\t\t\t\tD5D8C9E326E6AF8E00C193C3 /* FEPlasticFlowCurve.cpp */,\n\t\t\t\tD5D8C9E426E6AF8E00C193C3 /* FEPlasticFlowCurve.h */,\n\t\t\t\tD5322E7A2142ACD7008DE511 /* FEPointBodyForce.cpp */,\n\t\t\t\tD5322FD92142ACD8008DE511 /* FEPointBodyForce.h */,\n\t\t\t\tD5322FA02142ACD8008DE511 /* FEPointConstraint.cpp */,\n\t\t\t\tD5322FBB2142ACD8008DE511 /* FEPointConstraint.h */,\n\t\t\t\tD5322F6D2142ACD8008DE511 /* FEPrescribedActiveContractionIsotropic.cpp */,\n\t\t\t\tD5322F8B2142ACD8008DE511 /* FEPrescribedActiveContractionIsotropic.h */,\n\t\t\t\tD5322ED12142ACD7008DE511 /* FEPrescribedActiveContractionIsotropicUC.cpp */,\n\t\t\t\tD5322E462142ACD7008DE511 /* FEPrescribedActiveContractionIsotropicUC.h */,\n\t\t\t\tD5322FD72142ACD8008DE511 /* FEPrescribedActiveContractionTransIso.cpp */,\n\t\t\t\tD5322F982142ACD8008DE511 /* FEPrescribedActiveContractionTransIso.h */,\n\t\t\t\tD5322F172142ACD8008DE511 /* FEPrescribedActiveContractionTransIsoUC.cpp */,\n\t\t\t\tD5322EDD2142ACD7008DE511 /* FEPrescribedActiveContractionTransIsoUC.h */,\n\t\t\t\tD5322EB42142ACD7008DE511 /* FEPrescribedActiveContractionUniaxial.cpp */,\n\t\t\t\tD5322F1B2142ACD8008DE511 /* FEPrescribedActiveContractionUniaxial.h */,\n\t\t\t\tD5322F402142ACD8008DE511 /* FEPrescribedActiveContractionUniaxialUC.cpp */,\n\t\t\t\tD5322FCD2142ACD8008DE511 /* FEPrescribedActiveContractionUniaxialUC.h */,\n\t\t\t\tD5322EF12142ACD7008DE511 /* FEPrescribedNormalDisplacement.cpp */,\n\t\t\t\tD5322EE72142ACD7008DE511 /* FEPrescribedNormalDisplacement.h */,\n\t\t\t\tD5322E852142ACD7008DE511 /* FEPressureLoad.cpp */,\n\t\t\t\tD5322F4B2142ACD8008DE511 /* FEPressureLoad.h */,\n\t\t\t\tD5BD65A02333E32000EB9FD1 /* FEPreStrainConstraint.cpp */,\n\t\t\t\tD5BD65962333E31F00EB9FD1 /* FEPreStrainConstraint.h */,\n\t\t\t\tD5BD65982333E32000EB9FD1 /* FEPreStrainElastic.cpp */,\n\t\t\t\tD5BD65A12333E32000EB9FD1 /* FEPreStrainElastic.h */,\n\t\t\t\tD5BD659C2333E32000EB9FD1 /* FEPreStrainUncoupledElastic.cpp */,\n\t\t\t\tD5BD65972333E31F00EB9FD1 /* FEPreStrainUncoupledElastic.h */,\n\t\t\t\tD5322F642142ACD8008DE511 /* FEPRLig.cpp */,\n\t\t\t\tD5322EAD2142ACD7008DE511 /* FEPRLig.h */,\n\t\t\t\tD550834324F07EBE00E919D8 /* FEReactivePlasticDamage.cpp */,\n\t\t\t\tD550834124F07EBD00E919D8 /* FEReactivePlasticDamage.h */,\n\t\t\t\tD550834024F07EBD00E919D8 /* FEReactivePlasticDamageMaterialPoint.cpp */,\n\t\t\t\tD550834224F07EBD00E919D8 /* FEReactivePlasticDamageMaterialPoint.h */,\n\t\t\t\tD5B939A722D23D4700E495BA /* FEReactivePlasticity.cpp */,\n\t\t\t\tD5B939A822D23D4700E495BA /* FEReactivePlasticity.h */,\n\t\t\t\tD5B939AB22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.cpp */,\n\t\t\t\tD5B939AC22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.h */,\n\t\t\t\tD5322F7C2142ACD8008DE511 /* FEReactiveVEMaterialPoint.cpp */,\n\t\t\t\tD5322F682142ACD8008DE511 /* FEReactiveVEMaterialPoint.h */,\n\t\t\t\tD5322F152142ACD8008DE511 /* FEReactiveViscoelastic.cpp */,\n\t\t\t\tD5322EA32142ACD7008DE511 /* FEReactiveViscoelastic.h */,\n\t\t\t\tD5322E7E2142ACD7008DE511 /* FERemodelingElasticDomain.cpp */,\n\t\t\t\tD5322ED32142ACD7008DE511 /* FERemodelingElasticDomain.h */,\n\t\t\t\tD5322FCC2142ACD8008DE511 /* FERemodelingElasticMaterial.cpp */,\n\t\t\t\tD5322F5F2142ACD8008DE511 /* FERemodelingElasticMaterial.h */,\n\t\t\t\tD5322E4C2142ACD7008DE511 /* FEResidualVector.cpp */,\n\t\t\t\tD5322F9A2142ACD8008DE511 /* FEResidualVector.h */,\n\t\t\t\tD5322FA92142ACD8008DE511 /* FERigidAngularDamper.cpp */,\n\t\t\t\tD5322F5D2142ACD8008DE511 /* FERigidAngularDamper.h */,\n\t\t\t\tD565CDC4215D28A400E08ED6 /* FERigidBody.cpp */,\n\t\t\t\tD565CDBE215D28A400E08ED6 /* FERigidBody.h */,\n\t\t\t\tD5322FDC2142ACD8008DE511 /* FERigidCable.cpp */,\n\t\t\t\tD5322E6F2142ACD7008DE511 /* FERigidCable.h */,\n\t\t\t\tD5322F122142ACD8008DE511 /* FERigidConnector.cpp */,\n\t\t\t\tD5322EC12142ACD7008DE511 /* FERigidConnector.h */,\n\t\t\t\tD5322F9D2142ACD8008DE511 /* FERigidContractileForce.cpp */,\n\t\t\t\tD5322FC22142ACD8008DE511 /* FERigidContractileForce.h */,\n\t\t\t\tD5322FC72142ACD8008DE511 /* FERigidCylindricalJoint.cpp */,\n\t\t\t\tD5322E902142ACD7008DE511 /* FERigidCylindricalJoint.h */,\n\t\t\t\tD5322FA62142ACD8008DE511 /* FERigidDamper.cpp */,\n\t\t\t\tD5322F872142ACD8008DE511 /* FERigidDamper.h */,\n\t\t\t\tD576A7D3256C366B00415BF7 /* FERigidFollowerForce.cpp */,\n\t\t\t\tD576A7D4256C366B00415BF7 /* FERigidFollowerForce.h */,\n\t\t\t\tD57EE139256F08310068733E /* FERigidFollowerMoment.cpp */,\n\t\t\t\tD57EE13A256F08310068733E /* FERigidFollowerMoment.h */,\n\t\t\t\tD5322ECB2142ACD7008DE511 /* FERigidForce.cpp */,\n\t\t\t\tD5322FBD2142ACD8008DE511 /* FERigidForce.h */,\n\t\t\t\tD5322F6F2142ACD8008DE511 /* FERigidJoint.cpp */,\n\t\t\t\tD5322EA22142ACD7008DE511 /* FERigidJoint.h */,\n\t\t\t\tD5322EC52142ACD7008DE511 /* FERigidLock.cpp */,\n\t\t\t\tD5322F972142ACD8008DE511 /* FERigidLock.h */,\n\t\t\t\tD5322EF62142ACD8008DE511 /* FERigidMaterial.cpp */,\n\t\t\t\tD5322F922142ACD8008DE511 /* FERigidMaterial.h */,\n\t\t\t\tD5322EA92142ACD7008DE511 /* FERigidPlanarJoint.cpp */,\n\t\t\t\tD5322E9A2142ACD7008DE511 /* FERigidPlanarJoint.h */,\n\t\t\t\tD5322EB12142ACD7008DE511 /* FERigidPrismaticJoint.cpp */,\n\t\t\t\tD5322F822142ACD8008DE511 /* FERigidPrismaticJoint.h */,\n\t\t\t\tD5322F002142ACD8008DE511 /* FERigidRevoluteJoint.cpp */,\n\t\t\t\tD5322E732142ACD7008DE511 /* FERigidRevoluteJoint.h */,\n\t\t\t\tD5322E622142ACD7008DE511 /* FERigidShellDomain.cpp */,\n\t\t\t\tD5322E5F2142ACD7008DE511 /* FERigidShellDomain.h */,\n\t\t\t\tD5322FB12142ACD8008DE511 /* FERigidSlidingContact.cpp */,\n\t\t\t\tD5322EC32142ACD7008DE511 /* FERigidSlidingContact.h */,\n\t\t\t\tD5322F882142ACD8008DE511 /* FERigidSolidDomain.cpp */,\n\t\t\t\tD5322E8A2142ACD7008DE511 /* FERigidSolidDomain.h */,\n\t\t\t\tD5322F192142ACD8008DE511 /* FERigidSolver.cpp */,\n\t\t\t\tD5322FC62142ACD8008DE511 /* FERigidSolver.h */,\n\t\t\t\tD5322ECA2142ACD7008DE511 /* FERigidSphericalJoint.cpp */,\n\t\t\t\tD5322ECC2142ACD7008DE511 /* FERigidSphericalJoint.h */,\n\t\t\t\tD5322E982142ACD7008DE511 /* FERigidSpring.cpp */,\n\t\t\t\tD5322F012142ACD8008DE511 /* FERigidSpring.h */,\n\t\t\t\tD565CDC6215D28A500E08ED6 /* FERigidSurface.cpp */,\n\t\t\t\tD565CDC5215D28A500E08ED6 /* FERigidSurface.h */,\n\t\t\t\tD565CDBC215D28A300E08ED6 /* FERigidSystem.cpp */,\n\t\t\t\tD565CDBF215D28A400E08ED6 /* FERigidSystem.h */,\n\t\t\t\tD5322EFA2142ACD8008DE511 /* FERigidWallInterface.cpp */,\n\t\t\t\tD5322E512142ACD7008DE511 /* FERigidWallInterface.h */,\n\t\t\t\tD5DAB6CE22A954F1004C4213 /* FERVEDamageMaterial.cpp */,\n\t\t\t\tD5DAB6CF22A954F1004C4213 /* FERVEDamageMaterial.h */,\n\t\t\t\tD5322FA82142ACD8008DE511 /* FERVEModel.cpp */,\n\t\t\t\tD5322E762142ACD7008DE511 /* FERVEModel.h */,\n\t\t\t\tD5322EF22142ACD7008DE511 /* FERVEModel2O.cpp */,\n\t\t\t\tD5322F1C2142ACD8008DE511 /* FERVEModel2O.h */,\n\t\t\t\tD5322EA02142ACD7008DE511 /* FESlidingElasticInterface.cpp */,\n\t\t\t\tD5322EF32142ACD7008DE511 /* FESlidingElasticInterface.h */,\n\t\t\t\tD5322E742142ACD7008DE511 /* FESlidingInterface.cpp */,\n\t\t\t\tD5322EEA2142ACD7008DE511 /* FESlidingInterface.h */,\n\t\t\t\tD5322E722142ACD7008DE511 /* FESolidDomainFactory.cpp */,\n\t\t\t\tD5322EFF2142ACD8008DE511 /* FESolidDomainFactory.h */,\n\t\t\t\tD5709D8422832FD1007CAB0A /* FESolidLinearSystem.cpp */,\n\t\t\t\tD5709D8A22832FD1007CAB0A /* FESolidLinearSystem.h */,\n\t\t\t\tD5322F7E2142ACD8008DE511 /* FESolidMaterial.cpp */,\n\t\t\t\tD5322FB32142ACD8008DE511 /* FESolidMaterial.h */,\n\t\t\t\tD5322F032142ACD8008DE511 /* FESolidSolver.cpp */,\n\t\t\t\tD5322F852142ACD8008DE511 /* FESolidSolver.h */,\n\t\t\t\tD5322FB82142ACD8008DE511 /* FESolidSolver2.cpp */,\n\t\t\t\tD5322E9F2142ACD7008DE511 /* FESolidSolver2.h */,\n\t\t\t\tD5322E662142ACD7008DE511 /* FESphericalFiberDistribution.cpp */,\n\t\t\t\tD5322F1E2142ACD8008DE511 /* FESphericalFiberDistribution.h */,\n\t\t\t\tD5322EB72142ACD7008DE511 /* FESpringMaterial.cpp */,\n\t\t\t\tD5322FE02142ACD8008DE511 /* FESpringMaterial.h */,\n\t\t\t\tD5885714260B985A00659AEF /* FESpringRuptureCriterion.cpp */,\n\t\t\t\tD5885715260B985A00659AEF /* FESpringRuptureCriterion.h */,\n\t\t\t\tD5322E472142ACD7008DE511 /* FESRIElasticSolidDomain.cpp */,\n\t\t\t\tD5322EEE2142ACD7008DE511 /* FESRIElasticSolidDomain.h */,\n\t\t\t\tD5322FE22142ACD8008DE511 /* FESSIShellDomain.cpp */,\n\t\t\t\tD5322F2A2142ACD8008DE511 /* FESSIShellDomain.h */,\n\t\t\t\tD5322F692142ACD8008DE511 /* FEStickyInterface.cpp */,\n\t\t\t\tD5322F0D2142ACD8008DE511 /* FEStickyInterface.h */,\n\t\t\t\tD5322E8E2142ACD7008DE511 /* FEStVenantKirchhoff.cpp */,\n\t\t\t\tD5322EAA2142ACD7008DE511 /* FEStVenantKirchhoff.h */,\n\t\t\t\tD58FA374247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.cpp */,\n\t\t\t\tD58FA375247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.h */,\n\t\t\t\tD5322E912142ACD7008DE511 /* FESymmetryPlane.cpp */,\n\t\t\t\tD5322E592142ACD7008DE511 /* FESymmetryPlane.h */,\n\t\t\t\tD5322FAF2142ACD8008DE511 /* FETCNonlinearOrthotropic.cpp */,\n\t\t\t\tD5322FC32142ACD8008DE511 /* FETCNonlinearOrthotropic.h */,\n\t\t\t\tD5322E872142ACD7008DE511 /* FETendonMaterial.cpp */,\n\t\t\t\tD5322F5B2142ACD8008DE511 /* FETendonMaterial.h */,\n\t\t\t\tD5322FCA2142ACD8008DE511 /* FETiedContactSurface.cpp */,\n\t\t\t\tD5322EF52142ACD8008DE511 /* FETiedContactSurface.h */,\n\t\t\t\tD5322F5E2142ACD8008DE511 /* FETiedElasticInterface.cpp */,\n\t\t\t\tD5322E562142ACD7008DE511 /* FETiedElasticInterface.h */,\n\t\t\t\tD5322F812142ACD8008DE511 /* FETiedInterface.cpp */,\n\t\t\t\tD5322E522142ACD7008DE511 /* FETiedInterface.h */,\n\t\t\t\tD56DB99B255C162D0072414C /* FETorsionalSpring.cpp */,\n\t\t\t\tD56DB99A255C162D0072414C /* FETorsionalSpring.h */,\n\t\t\t\tD5322FB52142ACD8008DE511 /* FETractionLoad.cpp */,\n\t\t\t\tD5322E8F2142ACD7008DE511 /* FETractionLoad.h */,\n\t\t\t\tD5322FBE2142ACD8008DE511 /* FETransIsoMooneyRivlin.cpp */,\n\t\t\t\tD5322F3E2142ACD8008DE511 /* FETransIsoMooneyRivlin.h */,\n\t\t\t\tD5322F752142ACD8008DE511 /* FETransIsoVerondaWestmann.cpp */,\n\t\t\t\tD5322F892142ACD8008DE511 /* FETransIsoVerondaWestmann.h */,\n\t\t\t\tD5322F562142ACD8008DE511 /* FETrussMaterial.cpp */,\n\t\t\t\tD5322EC92142ACD7008DE511 /* FETrussMaterial.h */,\n\t\t\t\tD5322F9E2142ACD8008DE511 /* FEUDGHexDomain.cpp */,\n\t\t\t\tD5322E782142ACD7008DE511 /* FEUDGHexDomain.h */,\n\t\t\t\tD5322FA52142ACD8008DE511 /* FEUncoupledActiveContraction.cpp */,\n\t\t\t\tD5322F352142ACD8008DE511 /* FEUncoupledActiveContraction.h */,\n\t\t\t\tD5322FAE2142ACD8008DE511 /* FEUncoupledElasticMixture.cpp */,\n\t\t\t\tD5322EE12142ACD7008DE511 /* FEUncoupledElasticMixture.h */,\n\t\t\t\tD5322F632142ACD8008DE511 /* FEUncoupledFiberExpLinear.cpp */,\n\t\t\t\tD5322E4F2142ACD7008DE511 /* FEUncoupledFiberExpLinear.h */,\n\t\t\t\tD5322EF02142ACD7008DE511 /* FEUncoupledMaterial.cpp */,\n\t\t\t\tD5322FAC2142ACD8008DE511 /* FEUncoupledMaterial.h */,\n\t\t\t\tD5322F0C2142ACD8008DE511 /* FEUncoupledReactiveViscoelastic.cpp */,\n\t\t\t\tD5322F942142ACD8008DE511 /* FEUncoupledReactiveViscoelastic.h */,\n\t\t\t\tD5322F722142ACD8008DE511 /* FEUncoupledViscoElasticMaterial.cpp */,\n\t\t\t\tD5322F772142ACD8008DE511 /* FEUncoupledViscoElasticMaterial.h */,\n\t\t\t\tD5322F8E2142ACD8008DE511 /* FEUT4Domain.cpp */,\n\t\t\t\tD5322E8D2142ACD7008DE511 /* FEUT4Domain.h */,\n\t\t\t\tD5322F392142ACD8008DE511 /* FEVerondaWestmann.cpp */,\n\t\t\t\tD5322F3A2142ACD8008DE511 /* FEVerondaWestmann.h */,\n\t\t\t\tD5322F782142ACD8008DE511 /* FEViscoElasticMaterial.cpp */,\n\t\t\t\tD5322EBC2142ACD7008DE511 /* FEViscoElasticMaterial.h */,\n\t\t\t\tD5322FCF2142ACD8008DE511 /* FEViscousMaterialPoint.cpp */,\n\t\t\t\tD5322E7D2142ACD7008DE511 /* FEViscousMaterialPoint.h */,\n\t\t\t\tD5322E5C2142ACD7008DE511 /* FEVolumeConstraint.cpp */,\n\t\t\t\tD5322EBB2142ACD7008DE511 /* FEVolumeConstraint.h */,\n\t\t\t\tD5322E542142ACD7008DE511 /* FEVonMisesPlasticity.cpp */,\n\t\t\t\tD5322ECF2142ACD7008DE511 /* FEVonMisesPlasticity.h */,\n\t\t\t\tD5322F262142ACD8008DE511 /* FEWrinkleOgdenMaterial.cpp */,\n\t\t\t\tD5322E692142ACD7008DE511 /* FEWrinkleOgdenMaterial.h */,\n\t\t\t\tD5322F712142ACD8008DE511 /* gauss.h */,\n\t\t\t\tD5322E952142ACD7008DE511 /* gausskronrod.h */,\n\t\t\t\tD5322E4E2142ACD7008DE511 /* geodesic.h */,\n\t\t\t\tD565CDC0215D28A400E08ED6 /* ObjectDataRecord.cpp */,\n\t\t\t\tD565CDC7215D28A500E08ED6 /* ObjectDataRecord.h */,\n\t\t\t\tD565CDC1215D28A400E08ED6 /* RigidBC.cpp */,\n\t\t\t\tD565CDBD215D28A300E08ED6 /* RigidBC.h */,\n\t\t\t\tD52D841921CE9A7600472620 /* stdafx.cpp */,\n\t\t\t\tD5322E7F2142ACD7008DE511 /* stdafx.h */,\n\t\t\t\tD5322F3F2142ACD8008DE511 /* triangle_sphere.h */,\n\t\t\t);\n\t\t\tname = FEBioMech;\n\t\t\tpath = ../../FEBioMech;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD532318A2142AD6D008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD532318B2142AD6E008DE511 /* libgsl.a */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5322E332142ACC9008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD53230522142ACD9008DE511 /* FEElasticDomain.h in Headers */,\n\t\t\t\tD532300D2142ACD9008DE511 /* FEContactSurface.h in Headers */,\n\t\t\t\tD53230022142ACD9008DE511 /* FERigidShellDomain.h in Headers */,\n\t\t\t\tD5B805BC223BED2900198805 /* febiomech_api.h in Headers */,\n\t\t\t\tD5B939AE22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.h in Headers */,\n\t\t\t\tD532312C2142ACD9008DE511 /* FETransIsoVerondaWestmann.h in Headers */,\n\t\t\t\tD565CDCB215D28A500E08ED6 /* FERigidSystem.h in Headers */,\n\t\t\t\tD53231742142ACD9008DE511 /* FEFiberNeoHookean.h in Headers */,\n\t\t\t\tD532310F2142ACD9008DE511 /* FEMRVonMisesFibers.h in Headers */,\n\t\t\t\tD53230BF2142ACD9008DE511 /* FERVEModel2O.h in Headers */,\n\t\t\t\tD53231372142ACD9008DE511 /* FEUncoupledReactiveViscoelastic.h in Headers */,\n\t\t\t\tD532308A2142ACD9008DE511 /* FEPrescribedNormalDisplacement.h in Headers */,\n\t\t\t\tD5F72FA121601E6300271806 /* FEFatigueMaterial.h in Headers */,\n\t\t\t\tD53230E22142ACD9008DE511 /* triangle_sphere.h in Headers */,\n\t\t\t\tD53230A72142ACD9008DE511 /* FEOsmoticVirialExpansion.h in Headers */,\n\t\t\t\tD53230FF2142ACD9008DE511 /* FEDamageMaterialPoint.h in Headers */,\n\t\t\t\tD53230C62142ACD9008DE511 /* FEPeriodicSurfaceConstraint.h in Headers */,\n\t\t\t\tD532306C2142ACD9008DE511 /* FETrussMaterial.h in Headers */,\n\t\t\t\tD5BD65A62333E32000EB9FD1 /* FEConstPrestrain.h in Headers */,\n\t\t\t\tD524F6B5258BEB6000A403CD /* FEHolzapfelUnconstrained.h in Headers */,\n\t\t\t\tD53230D62142ACD9008DE511 /* FEElasticShellDomain.h in Headers */,\n\t\t\t\tD5C13ED3240477A3005897E1 /* FEActiveFiberStress.h in Headers */,\n\t\t\t\tD53231532142ACD9008DE511 /* FEFiberIntegrationTriangle.h in Headers */,\n\t\t\t\tD53230392142ACD9008DE511 /* FEBondRelaxation.h in Headers */,\n\t\t\t\tD5613D32217B5FD0007CAB89 /* FEFiberNHUC.h in Headers */,\n\t\t\t\tD5BD65A32333E32000EB9FD1 /* FEPreStrainUncoupledElastic.h in Headers */,\n\t\t\t\tD53231862142ACD9008DE511 /* FECoupledMooneyRivlin.h in Headers */,\n\t\t\t\tD5DA95A123D3540200D38BF2 /* FEDiscreteElementMaterial.h in Headers */,\n\t\t\t\tD532302F2142ACD9008DE511 /* FEDamageCDF.h in Headers */,\n\t\t\t\tD53230102142ACD9008DE511 /* FECellGrowth.h in Headers */,\n\t\t\t\tD565CDCE215D28A500E08ED6 /* FEMechModel.h in Headers */,\n\t\t\t\tD5C13ED2240477A3005897E1 /* FEActiveFiberStressUC.h in Headers */,\n\t\t\t\tD53230662142ACD9008DE511 /* FERigidSlidingContact.h in Headers */,\n\t\t\t\tD53231202142ACD9008DE511 /* FECentrifugalBodyForce.h in Headers */,\n\t\t\t\tD532300E2142ACD9008DE511 /* FEPeriodicBoundary1O.h in Headers */,\n\t\t\t\tD53230CA2142ACD9008DE511 /* FEElasticMultigeneration.h in Headers */,\n\t\t\t\tD532305E2142ACD9008DE511 /* FEVolumeConstraint.h in Headers */,\n\t\t\t\tD53230892142ACD9008DE511 /* FEOgdenUnconstrained.h in Headers */,\n\t\t\t\tD5BD65AB2333E32000EB9FD1 /* FEInitialPreStrain.h in Headers */,\n\t\t\t\tD53231812142ACD9008DE511 /* FEPeriodicBoundary2O.h in Headers */,\n\t\t\t\tD53231762142ACD9008DE511 /* FE3FieldElasticSolidDomain.h in Headers */,\n\t\t\t\tD53230B12142ACD9008DE511 /* FE2OMicroConstraint.h in Headers */,\n\t\t\t\tD5BA5331240D541F001A5C0F /* FEBCRigidDeformation.h in Headers */,\n\t\t\t\tD53231262142ACD9008DE511 /* FEMindlinElastic2O.h in Headers */,\n\t\t\t\tD532310D2142ACD9008DE511 /* FEIsotropicElastic.h in Headers */,\n\t\t\t\tD53230402142ACD9008DE511 /* FEDamageNeoHookean.h in Headers */,\n\t\t\t\tD5322FFA2142ACD9008DE511 /* FEElasticMaterial.h in Headers */,\n\t\t\t\tD5BC9F5026D07B07003BBF6E /* FEContactPotential.h in Headers */,\n\t\t\t\tD53231732142ACD9008DE511 /* FECoupledTransIsoMooneyRivlin.h in Headers */,\n\t\t\t\tD532304D2142ACD9008DE511 /* FEStVenantKirchhoff.h in Headers */,\n\t\t\t\tD5D741CE2543427A002B2825 /* FEContinuousElasticDamage.h in Headers */,\n\t\t\t\tD53230332142ACD9008DE511 /* FERigidCylindricalJoint.h in Headers */,\n\t\t\t\tD5322FFE2142ACD9008DE511 /* FEFiberPowLinear.h in Headers */,\n\t\t\t\tD53230D42142ACD9008DE511 /* FEEFDMooneyRivlin.h in Headers */,\n\t\t\t\tD53231022142ACD9008DE511 /* FERemodelingElasticMaterial.h in Headers */,\n\t\t\t\tD565CDCA215D28A500E08ED6 /* FERigidBody.h in Headers */,\n\t\t\t\tD5135C2A232191C6008AFD7D /* FENaturalNeoHookean.h in Headers */,\n\t\t\t\tD53230302142ACD9008DE511 /* FEUT4Domain.h in Headers */,\n\t\t\t\tD53231592142ACD9008DE511 /* FEPerfectOsmometer.h in Headers */,\n\t\t\t\tD532310B2142ACD9008DE511 /* FEReactiveVEMaterialPoint.h in Headers */,\n\t\t\t\tD5709D9222832FD2007CAB0A /* FESolidLinearSystem.h in Headers */,\n\t\t\t\tD53230532142ACD9008DE511 /* FEElasticFiberMaterial.h in Headers */,\n\t\t\t\tD51E614D22443FDB0049F545 /* FEMaxStressCriterion.h in Headers */,\n\t\t\t\tD53230492142ACD9008DE511 /* FEFiberExpPow.h in Headers */,\n\t\t\t\tD53231662142ACD9008DE511 /* FETCNonlinearOrthotropic.h in Headers */,\n\t\t\t\tD53230772142ACD9008DE511 /* FEFiberIntegrationGauss.h in Headers */,\n\t\t\t\tD576A7D6256C366B00415BF7 /* FERigidFollowerForce.h in Headers */,\n\t\t\t\tD53230D12142ACD9008DE511 /* FENewtonianViscousSolid.h in Headers */,\n\t\t\t\tD53230DE2142ACD9008DE511 /* FEDistanceConstraint.h in Headers */,\n\t\t\t\tD53230872142ACD9008DE511 /* FEElasticMixture.h in Headers */,\n\t\t\t\tD53231452142ACD9008DE511 /* FEEFDDonnanEquilibrium.h in Headers */,\n\t\t\t\tD53230202142ACD9008DE511 /* FEViscousMaterialPoint.h in Headers */,\n\t\t\t\tD5C13ED7240477A3005897E1 /* FEGenericRigidJoint.h in Headers */,\n\t\t\t\tD53231392142ACD9008DE511 /* FEElasticEASShellDomain.h in Headers */,\n\t\t\t\tD53230072142ACD9008DE511 /* FE2DTransIsoMooneyRivlin.h in Headers */,\n\t\t\t\tD53230D82142ACD9008DE511 /* FEUncoupledActiveContraction.h in Headers */,\n\t\t\t\tD565CDD3215D28A500E08ED6 /* ObjectDataRecord.h in Headers */,\n\t\t\t\tD5885717260B985B00659AEF /* FESpringRuptureCriterion.h in Headers */,\n\t\t\t\tD5322FEB2142ACD9008DE511 /* FEFungOrthoCompressible.h in Headers */,\n\t\t\t\tD5709D9122832FD2007CAB0A /* FEMaxDamageCriterion.h in Headers */,\n\t\t\t\tD5322FF52142ACD9008DE511 /* FETiedInterface.h in Headers */,\n\t\t\t\tD5D56E81235523790078BCC4 /* FENodeToNodeConstraint.h in Headers */,\n\t\t\t\tD53230DD2142ACD9008DE511 /* FEVerondaWestmann.h in Headers */,\n\t\t\t\tD53230372142ACD9008DE511 /* FENeoHookeanTransIso.h in Headers */,\n\t\t\t\tD532301B2142ACD9008DE511 /* FEUDGHexDomain.h in Headers */,\n\t\t\t\tD511EF14253A74AE00893F69 /* FEBioMechModule.h in Headers */,\n\t\t\t\tD565CDC9215D28A500E08ED6 /* RigidBC.h in Headers */,\n\t\t\t\tD53230E52142ACD9008DE511 /* FEFiberIntegrationScheme.h in Headers */,\n\t\t\t\tD53231232142ACD9008DE511 /* FECGSolidSolver.h in Headers */,\n\t\t\t\tD53230132142ACD9008DE511 /* FEPeriodicBoundary.h in Headers */,\n\t\t\t\tD532313A2142ACD9008DE511 /* FERigidLock.h in Headers */,\n\t\t\t\tD53230F42142ACD9008DE511 /* FEGentMaterial.h in Headers */,\n\t\t\t\tD5322FF32142ACD9008DE511 /* FEElasticANSShellDomain.h in Headers */,\n\t\t\t\tD5B939AA22D23D4700E495BA /* FEReactivePlasticity.h in Headers */,\n\t\t\t\tD53230E82142ACD9008DE511 /* FEFiberDensityDistribution.h in Headers */,\n\t\t\t\tD53230802142ACD9008DE511 /* FEPrescribedActiveContractionTransIsoUC.h in Headers */,\n\t\t\t\tD5C071F62151A978003321F9 /* FEGenericBodyForce.h in Headers */,\n\t\t\t\tD53230B92142ACD9008DE511 /* FEElasticMultiscaleDomain2O.h in Headers */,\n\t\t\t\tD5709D9022832FD2007CAB0A /* FEInitialVelocity.h in Headers */,\n\t\t\t\tD53231092142ACD9008DE511 /* FEArrudaBoyce.h in Headers */,\n\t\t\t\tD53230CD2142ACD9008DE511 /* FESSIShellDomain.h in Headers */,\n\t\t\t\tD53230AE2142ACD9008DE511 /* FEOrthoElastic.h in Headers */,\n\t\t\t\tD53230842142ACD9008DE511 /* FEUncoupledElasticMixture.h in Headers */,\n\t\t\t\tD53230192142ACD9008DE511 /* FERVEModel.h in Headers */,\n\t\t\t\tD53230692142ACD9008DE511 /* FEMembraneMaterial.h in Headers */,\n\t\t\t\tD5322FF12142ACD9008DE511 /* geodesic.h in Headers */,\n\t\t\t\tD5709D8D22832FD2007CAB0A /* FENodalForce.h in Headers */,\n\t\t\t\tD565CDD1215D28A500E08ED6 /* FERigidSurface.h in Headers */,\n\t\t\t\tD5D8C9E226E2776D00C193C3 /* FEKamenskyUncoupled.h in Headers */,\n\t\t\t\tD532309A2142ACD9008DE511 /* FENewtonianViscousSolidUC.h in Headers */,\n\t\t\t\tD5BC9F4F26D07B07003BBF6E /* FEMassDamping.h in Headers */,\n\t\t\t\tD53231442142ACD9008DE511 /* FE3FieldElasticShellDomain.h in Headers */,\n\t\t\t\tD53230142142ACD9008DE511 /* FEFiberIntegrationGaussKronrod.h in Headers */,\n\t\t\t\tD5322FEC2142ACD9008DE511 /* FEMortarTiedContact.h in Headers */,\n\t\t\t\tD5BD65AD2333E32000EB9FD1 /* FEPreStrainElastic.h in Headers */,\n\t\t\t\tD5E1AA8E25E3F892003162AB /* FEDeformationMapGenerator.h in Headers */,\n\t\t\t\tD53230042142ACD9008DE511 /* FEFiberIntegrationGeodesic.h in Headers */,\n\t\t\t\tD57DD3E223F6EF2C001347CB /* FEGenericHyperelasticUC.h in Headers */,\n\t\t\t\tD53230B02142ACD9008DE511 /* FEStickyInterface.h in Headers */,\n\t\t\t\tD532312E2142ACD9008DE511 /* FEPrescribedActiveContractionIsotropic.h in Headers */,\n\t\t\t\tD53231832142ACD9008DE511 /* FESpringMaterial.h in Headers */,\n\t\t\t\tD5322FF02142ACD9008DE511 /* FEFiberExpLinear.h in Headers */,\n\t\t\t\tD56DB99C255C162D0072414C /* FETorsionalSpring.h in Headers */,\n\t\t\t\tD53230782142ACD9008DE511 /* FEDiscreteContact.h in Headers */,\n\t\t\t\tD532314A2142ACD9008DE511 /* FEMooneyRivlin.h in Headers */,\n\t\t\t\tD53230262142ACD9008DE511 /* FEMicroMaterial.h in Headers */,\n\t\t\t\tD53230442142ACD9008DE511 /* FEContactInterface.h in Headers */,\n\t\t\t\tD532300A2142ACD9008DE511 /* FECoupledTransIsoVerondaWestmann.h in Headers */,\n\t\t\t\tD53230222142ACD9008DE511 /* stdafx.h in Headers */,\n\t\t\t\tD532300F2142ACD9008DE511 /* FEPeriodicLinearConstraint.h in Headers */,\n\t\t\t\tD53230A52142ACD9008DE511 /* FEEFDNeoHookean.h in Headers */,\n\t\t\t\tD53230462142ACD9008DE511 /* FEReactiveViscoelastic.h in Headers */,\n\t\t\t\tD5D8C9DF26E2776D00C193C3 /* FEKamensky.h in Headers */,\n\t\t\t\tD532305D2142ACD9008DE511 /* FEFiberExpPowUncoupled.h in Headers */,\n\t\t\t\tD53230452142ACD9008DE511 /* FERigidJoint.h in Headers */,\n\t\t\t\tD5322FF92142ACD9008DE511 /* FETiedElasticInterface.h in Headers */,\n\t\t\t\tD54A66B32581AAFA0023C8D1 /* FEAzimuthConstraint.h in Headers */,\n\t\t\t\tD53230E92142ACD9008DE511 /* FEHolmesMow.h in Headers */,\n\t\t\t\tD532316B2142ACD9008DE511 /* FEHuiskesSupply.h in Headers */,\n\t\t\t\tD53231572142ACD9008DE511 /* FEIncompNeoHookean.h in Headers */,\n\t\t\t\tD53230822142ACD9008DE511 /* FEFungOrthotropic.h in Headers */,\n\t\t\t\tD550834624F07EBE00E919D8 /* FEReactivePlasticDamageMaterialPoint.h in Headers */,\n\t\t\t\tD532307C2142ACD9008DE511 /* FEOrthotropicCLE.h in Headers */,\n\t\t\t\tD532311A2142ACD9008DE511 /* FEUncoupledViscoElasticMaterial.h in Headers */,\n\t\t\t\tD53230292142ACD9008DE511 /* FEDonnanEquilibrium.h in Headers */,\n\t\t\t\tD532302D2142ACD9008DE511 /* FERigidSolidDomain.h in Headers */,\n\t\t\t\tD53230D22142ACD9008DE511 /* FEMortarSlidingContact.h in Headers */,\n\t\t\t\tD53230EA2142ACD9008DE511 /* FEElasticMultiscaleDomain1O.h in Headers */,\n\t\t\t\tD53230C82142ACD9008DE511 /* FEMortarContactSurface.h in Headers */,\n\t\t\t\tD532302E2142ACD9008DE511 /* FEMicroMaterial2O.h in Headers */,\n\t\t\t\tD53230632142ACD9008DE511 /* FEActiveFiberContraction.h in Headers */,\n\t\t\t\tD53230B22142ACD9008DE511 /* FEOgdenMaterial.h in Headers */,\n\t\t\t\tD5322FFC2142ACD9008DE511 /* FESymmetryPlane.h in Headers */,\n\t\t\t\tD532314F2142ACD9008DE511 /* FEUncoupledMaterial.h in Headers */,\n\t\t\t\tD53230EF2142ACD9008DE511 /* FEFacet2FacetTied.h in Headers */,\n\t\t\t\tD5322FE92142ACD9008DE511 /* FEPrescribedActiveContractionIsotropicUC.h in Headers */,\n\t\t\t\tD53230C12142ACD9008DE511 /* FESphericalFiberDistribution.h in Headers */,\n\t\t\t\tD53231692142ACD9008DE511 /* FERigidSolver.h in Headers */,\n\t\t\t\tD53230962142ACD9008DE511 /* FESlidingElasticInterface.h in Headers */,\n\t\t\t\tD532303A2142ACD9008DE511 /* FEContinuousFiberDistributionUC.h in Headers */,\n\t\t\t\tD5613D30217B5FD0007CAB89 /* FEElasticMaterialPoint.h in Headers */,\n\t\t\t\tD53230FE2142ACD9008DE511 /* FETendonMaterial.h in Headers */,\n\t\t\t\tD53231042142ACD9008DE511 /* FEFiberPowLinearUncoupled.h in Headers */,\n\t\t\t\tD53231652142ACD9008DE511 /* FERigidContractileForce.h in Headers */,\n\t\t\t\tD53230882142ACD9008DE511 /* FEDamageTransIsoMooneyRivlin.h in Headers */,\n\t\t\t\tD53230762142ACD9008DE511 /* FERemodelingElasticDomain.h in Headers */,\n\t\t\t\tD5322FF42142ACD9008DE511 /* FERigidWallInterface.h in Headers */,\n\t\t\t\tD53230812142ACD9008DE511 /* FEMortarInterface.h in Headers */,\n\t\t\t\tD5D8C9E626E6AF8E00C193C3 /* FEPlasticFlowCurve.h in Headers */,\n\t\t\t\tD53230322142ACD9008DE511 /* FETractionLoad.h in Headers */,\n\t\t\t\tD53230DB2142ACD9008DE511 /* FEFiberEFDNeoHookean.h in Headers */,\n\t\t\t\tD532313D2142ACD9008DE511 /* FEResidualVector.h in Headers */,\n\t\t\t\tD53230502142ACD9008DE511 /* FEPRLig.h in Headers */,\n\t\t\t\tD53230EE2142ACD9008DE511 /* FEPressureLoad.h in Headers */,\n\t\t\t\tD53230982142ACD9008DE511 /* FETiedContactSurface.h in Headers */,\n\t\t\t\tD53230E42142ACD9008DE511 /* FEElasticMaterial2O.h in Headers */,\n\t\t\t\tD58FA377247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.h in Headers */,\n\t\t\t\tD53230912142ACD9008DE511 /* FESRIElasticSolidDomain.h in Headers */,\n\t\t\t\tD53230702142ACD9008DE511 /* FEBodyForce.h in Headers */,\n\t\t\t\tD53230A22142ACD9008DE511 /* FESolidDomainFactory.h in Headers */,\n\t\t\t\tD532306F2142ACD9008DE511 /* FERigidSphericalJoint.h in Headers */,\n\t\t\t\tD532300C2142ACD9008DE511 /* FEWrinkleOgdenMaterial.h in Headers */,\n\t\t\t\tD53230C32142ACD9008DE511 /* FEFiberMaterialPoint.h in Headers */,\n\t\t\t\tD532303E2142ACD9008DE511 /* FEDamageMooneyRivlin.h in Headers */,\n\t\t\t\tD53230C52142ACD9008DE511 /* FEBioMechData.h in Headers */,\n\t\t\t\tD53230672142ACD9008DE511 /* FEFacet2FacetSliding.h in Headers */,\n\t\t\t\tD5D84F03240F21B600DF3D67 /* FEGenericTransIsoHyperelastic.h in Headers */,\n\t\t\t\tD5BD65A52333E32000EB9FD1 /* FEInSituStretchGradient.h in Headers */,\n\t\t\t\tD53230422142ACD9008DE511 /* FESolidSolver2.h in Headers */,\n\t\t\t\tD5DEA26326F52F6900E26ED3 /* FEFiberNaturalNeoHookean.h in Headers */,\n\t\t\t\tD532313B2142ACD9008DE511 /* FEPrescribedActiveContractionTransIso.h in Headers */,\n\t\t\t\tD532303D2142ACD9008DE511 /* FERigidPlanarJoint.h in Headers */,\n\t\t\t\tD53231282142ACD9008DE511 /* FESolidSolver.h in Headers */,\n\t\t\t\tD532317C2142ACD9008DE511 /* FEPointBodyForce.h in Headers */,\n\t\t\t\tD53230B62142ACD9008DE511 /* FEElasticTrussDomain.h in Headers */,\n\t\t\t\tD532311D2142ACD9008DE511 /* FEEFDUncoupled.h in Headers */,\n\t\t\t\tD5322FE82142ACD9008DE511 /* FEExplicitSolidSolver.h in Headers */,\n\t\t\t\tD5EBB7A22328231800EED2BE /* FECarreauYasudaViscousSolid.h in Headers */,\n\t\t\t\tD53230E02142ACD9008DE511 /* FEEllipsoidalFiberDistribution.h in Headers */,\n\t\t\t\tD53231332142ACD9008DE511 /* FEDamageMaterialUC.h in Headers */,\n\t\t\t\tD550834524F07EBE00E919D8 /* FEReactivePlasticDamage.h in Headers */,\n\t\t\t\tD532301F2142ACD9008DE511 /* FEContinuousFiberDistribution.h in Headers */,\n\t\t\t\tD532316C2142ACD9008DE511 /* FE2DTransIsoVerondaWestmann.h in Headers */,\n\t\t\t\tD53231252142ACD9008DE511 /* FERigidPrismaticJoint.h in Headers */,\n\t\t\t\tD53231702142ACD9008DE511 /* FEPrescribedActiveContractionUniaxialUC.h in Headers */,\n\t\t\t\tD532305C2142ACD9008DE511 /* FEEFDVerondaWestmann.h in Headers */,\n\t\t\t\tD53230BE2142ACD9008DE511 /* FEPrescribedActiveContractionUniaxial.h in Headers */,\n\t\t\t\tD532317E2142ACD9008DE511 /* FEElasticFiberMaterialUC.h in Headers */,\n\t\t\t\tD53231562142ACD9008DE511 /* FESolidMaterial.h in Headers */,\n\t\t\t\tD53231112142ACD9008DE511 /* FEElasticSolidDomain.h in Headers */,\n\t\t\t\tD53231032142ACD9008DE511 /* FEFiberIntegrationTrapezoidal.h in Headers */,\n\t\t\t\tD5322FFD2142ACD9008DE511 /* FEMergedConstraint.h in Headers */,\n\t\t\t\tD53230A42142ACD9008DE511 /* FERigidSpring.h in Headers */,\n\t\t\t\tD5C123E52411845500657663 /* FEGenericTransIsoHyperelasticUC.h in Headers */,\n\t\t\t\tD5F09077248A84FA00E4F940 /* FEDiscreteElasticDomain.h in Headers */,\n\t\t\t\tD53230232142ACD9008DE511 /* FEBioMechPlot.h in Headers */,\n\t\t\t\tD532300B2142ACD9008DE511 /* FEAugLagLinearConstraint.h in Headers */,\n\t\t\t\tD53230E12142ACD9008DE511 /* FETransIsoMooneyRivlin.h in Headers */,\n\t\t\t\tD53230032142ACD9008DE511 /* FEMuscleMaterial.h in Headers */,\n\t\t\t\tD53231342142ACD9008DE511 /* FEDeformableSpringDomain.h in Headers */,\n\t\t\t\tD53230362142ACD9008DE511 /* FEHolzapfelGasserOgden.h in Headers */,\n\t\t\t\tD532306A2142ACD9008DE511 /* FECoupledVerondaWestmann.h in Headers */,\n\t\t\t\tD532312A2142ACD9008DE511 /* FERigidDamper.h in Headers */,\n\t\t\t\tD53231602142ACD9008DE511 /* FERigidForce.h in Headers */,\n\t\t\t\tD5BC983E26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.h in Headers */,\n\t\t\t\tD53230DA2142ACD9008DE511 /* FEPeriodicLinearConstraint2O.h in Headers */,\n\t\t\t\tD53230382142ACD9008DE511 /* gausskronrod.h in Headers */,\n\t\t\t\tD53230122142ACD9008DE511 /* FERigidCable.h in Headers */,\n\t\t\t\tD532301E2142ACD9008DE511 /* FECarterHayesOld.h in Headers */,\n\t\t\t\tD5613D31217B5FD0007CAB89 /* FEFiberExponentialPowerUC.h in Headers */,\n\t\t\t\tD532308D2142ACD9008DE511 /* FESlidingInterface.h in Headers */,\n\t\t\t\tD53230642142ACD9008DE511 /* FERigidConnector.h in Headers */,\n\t\t\t\tD5BD65A22333E32000EB9FD1 /* FEPreStrainConstraint.h in Headers */,\n\t\t\t\tD53230ED2142ACD9008DE511 /* FECubicCLE.h in Headers */,\n\t\t\t\tD57EE13C256F08310068733E /* FERigidFollowerMoment.h in Headers */,\n\t\t\t\tD53230512142ACD9008DE511 /* FEBCPrescribedDeformation.h in Headers */,\n\t\t\t\tD53231272142ACD9008DE511 /* FENeoHookean.h in Headers */,\n\t\t\t\tD53230F52142ACD9008DE511 /* FEDamageCriterion.h in Headers */,\n\t\t\t\tD5DAB6D122A954F1004C4213 /* FERVEDamageMaterial.h in Headers */,\n\t\t\t\tD53230AD2142ACD9008DE511 /* FEBioMech.h in Headers */,\n\t\t\t\tD53230002142ACD9008DE511 /* FEElasticShellDomainOld.h in Headers */,\n\t\t\t\tD532315E2142ACD9008DE511 /* FEPointConstraint.h in Headers */,\n\t\t\t\tD53231352142ACD9008DE511 /* FERigidMaterial.h in Headers */,\n\t\t\t\tD5F09074248A84FA00E4F940 /* FEDiscreteElasticMaterial.h in Headers */,\n\t\t\t\tD532305F2142ACD9008DE511 /* FEViscoElasticMaterial.h in Headers */,\n\t\t\t\tD53230722142ACD9008DE511 /* FEVonMisesPlasticity.h in Headers */,\n\t\t\t\tD53231142142ACD9008DE511 /* gauss.h in Headers */,\n\t\t\t\tD53231382142ACD9008DE511 /* FEElasticSolidDomain2O.h in Headers */,\n\t\t\t\tD53231002142ACD9008DE511 /* FERigidAngularDamper.h in Headers */,\n\t\t\t\tD53230752142ACD9008DE511 /* FE2DFiberNeoHookean.h in Headers */,\n\t\t\t\tD57DD3E123F6EF2C001347CB /* FEGenericHyperelastic.h in Headers */,\n\t\t\t\tD5322FF22142ACD9008DE511 /* FEUncoupledFiberExpLinear.h in Headers */,\n\t\t\t\tD53231642142ACD9008DE511 /* FEDamageMaterial.h in Headers */,\n\t\t\t\tD53230162142ACD9008DE511 /* FERigidRevoluteJoint.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5322E342142ACC9008DE511 /* FEBioMech */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5322E402142ACC9008DE511 /* Build configuration list for PBXNativeTarget \"FEBioMech\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5322E312142ACC9008DE511 /* Sources */,\n\t\t\t\tD5322E322142ACC9008DE511 /* Frameworks */,\n\t\t\t\tD5322E332142ACC9008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioMech;\n\t\t\tproductName = FEBioMech;\n\t\t\tproductReference = D5322E352142ACC9008DE511 /* libFEBioMech.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5322E2D2142ACC9008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5322E342142ACC9008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5322E302142ACC9008DE511 /* Build configuration list for PBXProject \"FEBioMech\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5322E2C2142ACC9008DE511;\n\t\t\tproductRefGroup = D5322E362142ACC9008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5322E342142ACC9008DE511 /* FEBioMech */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5322E312142ACC9008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5BD65A92333E32000EB9FD1 /* FEConstPrestrain.cpp in Sources */,\n\t\t\t\tD565CDD2215D28A500E08ED6 /* FERigidSurface.cpp in Sources */,\n\t\t\t\tD532315B2142ACD9008DE511 /* FESolidSolver2.cpp in Sources */,\n\t\t\t\tD5B939AD22D23E8100E495BA /* FEReactivePlasticityMaterialPoint.cpp in Sources */,\n\t\t\t\tD53230B42142ACD9008DE511 /* FEMembraneMaterial.cpp in Sources */,\n\t\t\t\tD53231482142ACD9008DE511 /* FEUncoupledActiveContraction.cpp in Sources */,\n\t\t\t\tD532317A2142ACD9008DE511 /* FEPrescribedActiveContractionTransIso.cpp in Sources */,\n\t\t\t\tD53230D52142ACD9008DE511 /* FEFiberMaterialPoint.cpp in Sources */,\n\t\t\t\tD53230942142ACD9008DE511 /* FEPrescribedNormalDisplacement.cpp in Sources */,\n\t\t\t\tD53230C42142ACD9008DE511 /* FEFiberDensityDistribution.cpp in Sources */,\n\t\t\t\tD5613D34217B5FD0007CAB89 /* FEElasticMaterialPoint.cpp in Sources */,\n\t\t\t\tD53231802142ACD9008DE511 /* FEMergedConstraint.cpp in Sources */,\n\t\t\t\tD53231192142ACD9008DE511 /* FEElasticMultiscaleDomain2O.cpp in Sources */,\n\t\t\t\tD5D8C9E026E2776D00C193C3 /* FEKamensky.cpp in Sources */,\n\t\t\t\tD532311E2142ACD9008DE511 /* FEFacet2FacetTied.cpp in Sources */,\n\t\t\t\tD53230832142ACD9008DE511 /* FEElasticFiberMaterial.cpp in Sources */,\n\t\t\t\tD53230542142ACD9008DE511 /* FERigidPrismaticJoint.cpp in Sources */,\n\t\t\t\tD53230EB2142ACD9008DE511 /* FEPeriodicBoundary2O.cpp in Sources */,\n\t\t\t\tD565CDC8215D28A500E08ED6 /* FERigidSystem.cpp in Sources */,\n\t\t\t\tD53230A92142ACD9008DE511 /* FEMortarInterface.cpp in Sources */,\n\t\t\t\tD53230412142ACD9008DE511 /* FEActiveFiberContraction.cpp in Sources */,\n\t\t\t\tD532307B2142ACD9008DE511 /* FEElasticMixture.cpp in Sources */,\n\t\t\t\tD53230742142ACD9008DE511 /* FEPrescribedActiveContractionIsotropicUC.cpp in Sources */,\n\t\t\t\tD53231322142ACD9008DE511 /* FENeoHookeanTransIso.cpp in Sources */,\n\t\t\t\tD53231612142ACD9008DE511 /* FETransIsoMooneyRivlin.cpp in Sources */,\n\t\t\t\tD53231182142ACD9008DE511 /* FETransIsoVerondaWestmann.cpp in Sources */,\n\t\t\t\tD5885716260B985B00659AEF /* FESpringRuptureCriterion.cpp in Sources */,\n\t\t\t\tD532306D2142ACD9008DE511 /* FERigidSphericalJoint.cpp in Sources */,\n\t\t\t\tD58FA376247B03FD00AD6B17 /* FESurfaceAttractionBodyForce.cpp in Sources */,\n\t\t\t\tD53231712142ACD9008DE511 /* FECoupledVerondaWestmann.cpp in Sources */,\n\t\t\t\tD53230FD2142ACD9008DE511 /* FEOgdenUnconstrained.cpp in Sources */,\n\t\t\t\tD532316F2142ACD9008DE511 /* FERemodelingElasticMaterial.cpp in Sources */,\n\t\t\t\tD57EE13B256F08310068733E /* FERigidFollowerMoment.cpp in Sources */,\n\t\t\t\tD53230682142ACD9008DE511 /* FERigidLock.cpp in Sources */,\n\t\t\t\tD532309B2142ACD9008DE511 /* FEPeriodicBoundary1O.cpp in Sources */,\n\t\t\t\tD53230652142ACD9008DE511 /* FE2OMicroConstraint.cpp in Sources */,\n\t\t\t\tD5BD65A42333E32000EB9FD1 /* FEPreStrainElastic.cpp in Sources */,\n\t\t\t\tD53230212142ACD9008DE511 /* FERemodelingElasticDomain.cpp in Sources */,\n\t\t\t\tD53230DF2142ACD9008DE511 /* FEElasticDomain.cpp in Sources */,\n\t\t\t\tD5D8C9E126E2776D00C193C3 /* FEKamenskyUncoupled.cpp in Sources */,\n\t\t\t\tD53230602142ACD9008DE511 /* FECentrifugalBodyForce.cpp in Sources */,\n\t\t\t\tD53230BC2142ACD9008DE511 /* FERigidSolver.cpp in Sources */,\n\t\t\t\tD532314D2142ACD9008DE511 /* FEDamageMooneyRivlin.cpp in Sources */,\n\t\t\t\tD53230352142ACD9008DE511 /* FEMortarSlidingContact.cpp in Sources */,\n\t\t\t\tD532317F2142ACD9008DE511 /* FERigidCable.cpp in Sources */,\n\t\t\t\tD532307D2142ACD9008DE511 /* FEElasticShellDomainOld.cpp in Sources */,\n\t\t\t\tD53231122142ACD9008DE511 /* FERigidJoint.cpp in Sources */,\n\t\t\t\tD53231422142ACD9008DE511 /* FEBioMechPlot.cpp in Sources */,\n\t\t\t\tD550834424F07EBE00E919D8 /* FEReactivePlasticDamageMaterialPoint.cpp in Sources */,\n\t\t\t\tD53230342142ACD9008DE511 /* FESymmetryPlane.cpp in Sources */,\n\t\t\t\tD5613D35217B5FD0007CAB89 /* FEFiberNHUC.cpp in Sources */,\n\t\t\t\tD532315A2142ACD9008DE511 /* FEPerfectOsmometer.cpp in Sources */,\n\t\t\t\tD53230F12142ACD9008DE511 /* FEElasticMaterial2O.cpp in Sources */,\n\t\t\t\tD53230252142ACD9008DE511 /* FEPeriodicLinearConstraint.cpp in Sources */,\n\t\t\t\tD53230A32142ACD9008DE511 /* FERigidRevoluteJoint.cpp in Sources */,\n\t\t\t\tD532313E2142ACD9008DE511 /* FENewtonianViscousSolid.cpp in Sources */,\n\t\t\t\tD5BA5330240D541F001A5C0F /* FEBCRigidDeformation.cpp in Sources */,\n\t\t\t\tD532312D2142ACD9008DE511 /* FEElasticSolidDomain2O.cpp in Sources */,\n\t\t\t\tD53230AA2142ACD9008DE511 /* FEMicroMaterial2O.cpp in Sources */,\n\t\t\t\tD53230472142ACD9008DE511 /* FEElasticMaterial.cpp in Sources */,\n\t\t\t\tD5322FEE2142ACD9008DE511 /* FEFiberExpPow.cpp in Sources */,\n\t\t\t\tD54A66B42581AAFA0023C8D1 /* FEAzimuthConstraint.cpp in Sources */,\n\t\t\t\tD532314B2142ACD9008DE511 /* FERVEModel.cpp in Sources */,\n\t\t\t\tD53230152142ACD9008DE511 /* FESolidDomainFactory.cpp in Sources */,\n\t\t\t\tD52D841C21CE9A7600472620 /* stdafx.cpp in Sources */,\n\t\t\t\tD53230BB2142ACD9008DE511 /* FEFiberPowLinearUncoupled.cpp in Sources */,\n\t\t\t\tD550834724F07EBE00E919D8 /* FEReactivePlasticDamage.cpp in Sources */,\n\t\t\t\tD53230272142ACD9008DE511 /* FEFiberIntegrationGaussKronrod.cpp in Sources */,\n\t\t\t\tD532308B2142ACD9008DE511 /* FEEFDDonnanEquilibrium.cpp in Sources */,\n\t\t\t\tD532307A2142ACD9008DE511 /* FEDiscreteContact.cpp in Sources */,\n\t\t\t\tD53230FA2142ACD9008DE511 /* FEBioMechData.cpp in Sources */,\n\t\t\t\tD53231492142ACD9008DE511 /* FERigidDamper.cpp in Sources */,\n\t\t\t\tD5322FE72142ACD9008DE511 /* FEDeformableSpringDomain.cpp in Sources */,\n\t\t\t\tD565CDCC215D28A500E08ED6 /* ObjectDataRecord.cpp in Sources */,\n\t\t\t\tD53230592142ACD9008DE511 /* FE2DTransIsoMooneyRivlin.cpp in Sources */,\n\t\t\t\tD53230AF2142ACD9008DE511 /* FEUncoupledReactiveViscoelastic.cpp in Sources */,\n\t\t\t\tD532307F2142ACD9008DE511 /* FEFacet2FacetSliding.cpp in Sources */,\n\t\t\t\tD532316A2142ACD9008DE511 /* FERigidCylindricalJoint.cpp in Sources */,\n\t\t\t\tD5D84F02240F21B600DF3D67 /* FEGenericTransIsoHyperelastic.cpp in Sources */,\n\t\t\t\tD5322FEA2142ACD9008DE511 /* FESRIElasticSolidDomain.cpp in Sources */,\n\t\t\t\tD53230552142ACD9008DE511 /* FEIsotropicElastic.cpp in Sources */,\n\t\t\t\tD5C123E42411845500657663 /* FEGenericTransIsoHyperelasticUC.cpp in Sources */,\n\t\t\t\tD532303F2142ACD9008DE511 /* FEFiberIntegrationScheme.cpp in Sources */,\n\t\t\t\tD53231632142ACD9008DE511 /* FENewtonianViscousSolidUC.cpp in Sources */,\n\t\t\t\tD53231542142ACD9008DE511 /* FERigidSlidingContact.cpp in Sources */,\n\t\t\t\tD5E1AA8F25E3F892003162AB /* FEDeformationMapGenerator.cpp in Sources */,\n\t\t\t\tD532312B2142ACD9008DE511 /* FERigidSolidDomain.cpp in Sources */,\n\t\t\t\tD53230C72142ACD9008DE511 /* FEMicroMaterial.cpp in Sources */,\n\t\t\t\tD53231312142ACD9008DE511 /* FEUT4Domain.cpp in Sources */,\n\t\t\t\tD532309C2142ACD9008DE511 /* FEDamageCDF.cpp in Sources */,\n\t\t\t\tD53231852142ACD9008DE511 /* FESSIShellDomain.cpp in Sources */,\n\t\t\t\tD53230E62142ACD9008DE511 /* FEHuiskesSupply.cpp in Sources */,\n\t\t\t\tD53231502142ACD9008DE511 /* FEElasticANSShellDomain.cpp in Sources */,\n\t\t\t\tD53230062142ACD9008DE511 /* FEFungOrthotropic.cpp in Sources */,\n\t\t\t\tD53230B72142ACD9008DE511 /* FEFungOrthoCompressible.cpp in Sources */,\n\t\t\t\tD53231682142ACD9008DE511 /* FEEFDMooneyRivlin.cpp in Sources */,\n\t\t\t\tD53230F62142ACD9008DE511 /* FEContinuousFiberDistribution.cpp in Sources */,\n\t\t\t\tD53231072142ACD9008DE511 /* FEPRLig.cpp in Sources */,\n\t\t\t\tD53230F82142ACD9008DE511 /* FEFiberEFDNeoHookean.cpp in Sources */,\n\t\t\t\tD532315F2142ACD9008DE511 /* FEDamageMaterialUC.cpp in Sources */,\n\t\t\t\tD5D56E80235523790078BCC4 /* FENodeToNodeConstraint.cpp in Sources */,\n\t\t\t\tD53231222142ACD9008DE511 /* FEFiberNeoHookean.cpp in Sources */,\n\t\t\t\tD532314E2142ACD9008DE511 /* FEMRVonMisesFibers.cpp in Sources */,\n\t\t\t\tD53230E72142ACD9008DE511 /* FEArrudaBoyce.cpp in Sources */,\n\t\t\t\tD5709D8F22832FD2007CAB0A /* FENodalForce.cpp in Sources */,\n\t\t\t\tD532303B2142ACD9008DE511 /* FERigidSpring.cpp in Sources */,\n\t\t\t\tD53230F32142ACD9008DE511 /* FEEFDUncoupled.cpp in Sources */,\n\t\t\t\tD532302B2142ACD9008DE511 /* FEFiberExpLinear.cpp in Sources */,\n\t\t\t\tD56DB99D255C162D0072414C /* FETorsionalSpring.cpp in Sources */,\n\t\t\t\tD53230482142ACD9008DE511 /* FEDamageTransIsoMooneyRivlin.cpp in Sources */,\n\t\t\t\tD532306E2142ACD9008DE511 /* FERigidForce.cpp in Sources */,\n\t\t\t\tD5BC9F4E26D07B07003BBF6E /* FEContactPotential.cpp in Sources */,\n\t\t\t\tD5F72FA021601E6300271806 /* FEFatigueMaterial.cpp in Sources */,\n\t\t\t\tD53230082142ACD9008DE511 /* FEElasticFiberMaterialUC.cpp in Sources */,\n\t\t\t\tD532308F2142ACD9008DE511 /* FEElasticSolidDomain.cpp in Sources */,\n\t\t\t\tD53231102142ACD9008DE511 /* FEPrescribedActiveContractionIsotropic.cpp in Sources */,\n\t\t\t\tD53230B52142ACD9008DE511 /* FERigidConnector.cpp in Sources */,\n\t\t\t\tD53231582142ACD9008DE511 /* FETractionLoad.cpp in Sources */,\n\t\t\t\tD532308E2142ACD9008DE511 /* FEPeriodicLinearConstraint2O.cpp in Sources */,\n\t\t\t\tD565CDCF215D28A500E08ED6 /* FEMechModel.cpp in Sources */,\n\t\t\t\tD53230992142ACD9008DE511 /* FERigidMaterial.cpp in Sources */,\n\t\t\t\tD53230D72142ACD9008DE511 /* FEBioMech.cpp in Sources */,\n\t\t\t\tD53230312142ACD9008DE511 /* FEStVenantKirchhoff.cpp in Sources */,\n\t\t\t\tD53230E32142ACD9008DE511 /* FEPrescribedActiveContractionUniaxialUC.cpp in Sources */,\n\t\t\t\tD53231462142ACD9008DE511 /* FECGSolidSolver.cpp in Sources */,\n\t\t\t\tD565CDD0215D28A500E08ED6 /* FERigidBody.cpp in Sources */,\n\t\t\t\tD53231522142ACD9008DE511 /* FETCNonlinearOrthotropic.cpp in Sources */,\n\t\t\t\tD53230A62142ACD9008DE511 /* FESolidSolver.cpp in Sources */,\n\t\t\t\tD53231162142ACD9008DE511 /* FEElasticShellDomain.cpp in Sources */,\n\t\t\t\tD5322FFF2142ACD9008DE511 /* FEVolumeConstraint.cpp in Sources */,\n\t\t\t\tD53230B32142ACD9008DE511 /* FECarterHayesOld.cpp in Sources */,\n\t\t\t\tD532302C2142ACD9008DE511 /* FECellGrowth.cpp in Sources */,\n\t\t\t\tD5322FEF2142ACD9008DE511 /* FEResidualVector.cpp in Sources */,\n\t\t\t\tD532304C2142ACD9008DE511 /* FERigidPlanarJoint.cpp in Sources */,\n\t\t\t\tD53230B82142ACD9008DE511 /* FEReactiveViscoelastic.cpp in Sources */,\n\t\t\t\tD5F09076248A84FA00E4F940 /* FEDiscreteElasticDomain.cpp in Sources */,\n\t\t\t\tD532304A2142ACD9008DE511 /* FEDamageMaterialPoint.cpp in Sources */,\n\t\t\t\tD5EBB7A12328231800EED2BE /* FECarreauYasudaViscousSolid.cpp in Sources */,\n\t\t\t\tD53231402142ACD9008DE511 /* FERigidContractileForce.cpp in Sources */,\n\t\t\t\tD53231412142ACD9008DE511 /* FEUDGHexDomain.cpp in Sources */,\n\t\t\t\tD53230902142ACD9008DE511 /* FEGentMaterial.cpp in Sources */,\n\t\t\t\tD5709D8E22832FD2007CAB0A /* FEMaxDamageCriterion.cpp in Sources */,\n\t\t\t\tD532311F2142ACD9008DE511 /* FEReactiveVEMaterialPoint.cpp in Sources */,\n\t\t\t\tD5D8C9E526E6AF8E00C193C3 /* FEPlasticFlowCurve.cpp in Sources */,\n\t\t\t\tD53230012142ACD9008DE511 /* FEEFDNeoHookean.cpp in Sources */,\n\t\t\t\tD53230972142ACD9008DE511 /* FEEFDVerondaWestmann.cpp in Sources */,\n\t\t\t\tD53230112142ACD9008DE511 /* FEFiberIntegrationTrapezoidal.cpp in Sources */,\n\t\t\t\tD532316D2142ACD9008DE511 /* FETiedContactSurface.cpp in Sources */,\n\t\t\t\tD5F09075248A84FA00E4F940 /* FEDiscreteElasticMaterial.cpp in Sources */,\n\t\t\t\tD532309F2142ACD9008DE511 /* FECoupledMooneyRivlin.cpp in Sources */,\n\t\t\t\tD532310C2142ACD9008DE511 /* FEStickyInterface.cpp in Sources */,\n\t\t\t\tD53231722142ACD9008DE511 /* FEViscousMaterialPoint.cpp in Sources */,\n\t\t\t\tD511EF13253A74AE00893F69 /* FEBioMechModule.cpp in Sources */,\n\t\t\t\tD53231082142ACD9008DE511 /* FEFiberPowLinear.cpp in Sources */,\n\t\t\t\tD565CDCD215D28A500E08ED6 /* RigidBC.cpp in Sources */,\n\t\t\t\tD53230A82142ACD9008DE511 /* FEDamageMaterial.cpp in Sources */,\n\t\t\t\tD5322FF82142ACD9008DE511 /* FEDistanceConstraint.cpp in Sources */,\n\t\t\t\tD53230612142ACD9008DE511 /* FENeoHookean.cpp in Sources */,\n\t\t\t\tD532311B2142ACD9008DE511 /* FEViscoElasticMaterial.cpp in Sources */,\n\t\t\t\tD5709D8B22832FD2007CAB0A /* FEInitialVelocity.cpp in Sources */,\n\t\t\t\tD53230792142ACD9008DE511 /* FEIncompNeoHookean.cpp in Sources */,\n\t\t\t\tD5BD65AA2333E32000EB9FD1 /* FEInitialPreStrain.cpp in Sources */,\n\t\t\t\tD53230CF2142ACD9008DE511 /* FEOrthoElastic.cpp in Sources */,\n\t\t\t\tD51E614C22443FDB0049F545 /* FEMaxStressCriterion.cpp in Sources */,\n\t\t\t\tD53230432142ACD9008DE511 /* FESlidingElasticInterface.cpp in Sources */,\n\t\t\t\tD5BC983D26C7FB6A00BF9C00 /* FEFiberKiousisUncoupled.cpp in Sources */,\n\t\t\t\tD5613D33217B5FD0007CAB89 /* FEFiberExponentialPowerUC.cpp in Sources */,\n\t\t\t\tD5C071F52151A978003321F9 /* FEGenericBodyForce.cpp in Sources */,\n\t\t\t\tD53230C02142ACD9008DE511 /* FE3FieldElasticShellDomain.cpp in Sources */,\n\t\t\t\tD532317B2142ACD9008DE511 /* FEBondRelaxation.cpp in Sources */,\n\t\t\t\tD5DA95A023D3540200D38BF2 /* FEDiscreteElementMaterial.cpp in Sources */,\n\t\t\t\tD53230932142ACD9008DE511 /* FEUncoupledMaterial.cpp in Sources */,\n\t\t\t\tD53230A02142ACD9008DE511 /* FEOgdenMaterial.cpp in Sources */,\n\t\t\t\tD5322FF62142ACD9008DE511 /* FEEllipsoidalFiberDistribution.cpp in Sources */,\n\t\t\t\tD532302A2142ACD9008DE511 /* FETendonMaterial.cpp in Sources */,\n\t\t\t\tD5C13ED5240477A3005897E1 /* FEActiveFiberStressUC.cpp in Sources */,\n\t\t\t\tD5C13ED6240477A3005897E1 /* FEGenericRigidJoint.cpp in Sources */,\n\t\t\t\tD53230732142ACD9008DE511 /* FEMooneyRivlin.cpp in Sources */,\n\t\t\t\tD53230712142ACD9008DE511 /* FEPeriodicBoundary.cpp in Sources */,\n\t\t\t\tD53231882142ACD9008DE511 /* FEMortarTiedContact.cpp in Sources */,\n\t\t\t\tD53231212142ACD9008DE511 /* FESolidMaterial.cpp in Sources */,\n\t\t\t\tD5D741CF2543427A002B2825 /* FEContinuousElasticDamage.cpp in Sources */,\n\t\t\t\tD53231842142ACD9008DE511 /* FE2DFiberNeoHookean.cpp in Sources */,\n\t\t\t\tD5DAB6D022A954F1004C4213 /* FERVEDamageMaterial.cpp in Sources */,\n\t\t\t\tD5BD65AC2333E32000EB9FD1 /* FEPreStrainConstraint.cpp in Sources */,\n\t\t\t\tD53231792142ACD9008DE511 /* FEBodyForce.cpp in Sources */,\n\t\t\t\tD524F6B4258BEB6000A403CD /* FEHolzapfelUnconstrained.cpp in Sources */,\n\t\t\t\tD53230282142ACD9008DE511 /* FEPressureLoad.cpp in Sources */,\n\t\t\t\tD532311C2142ACD9008DE511 /* FEExplicitSolidSolver.cpp in Sources */,\n\t\t\t\tD53231872142ACD9008DE511 /* FEContactInterface.cpp in Sources */,\n\t\t\t\tD5322FED2142ACD9008DE511 /* FEFiberIntegrationTriangle.cpp in Sources */,\n\t\t\t\tD5BD65A82333E32000EB9FD1 /* FEPreStrainUncoupledElastic.cpp in Sources */,\n\t\t\t\tD53231782142ACD9008DE511 /* FEFiberIntegrationGeodesic.cpp in Sources */,\n\t\t\t\tD53231052142ACD9008DE511 /* FE3FieldElasticSolidDomain.cpp in Sources */,\n\t\t\t\tD53231822142ACD9008DE511 /* FEFiberIntegrationGauss.cpp in Sources */,\n\t\t\t\tD57DD3E023F6EF2C001347CB /* FEGenericHyperelastic.cpp in Sources */,\n\t\t\t\tD53230C22142ACD9008DE511 /* FEContactSurface.cpp in Sources */,\n\t\t\t\tD53231672142ACD9008DE511 /* FEOsmoticVirialExpansion.cpp in Sources */,\n\t\t\t\tD532304F2142ACD9008DE511 /* FEElasticMultigeneration.cpp in Sources */,\n\t\t\t\tD53230052142ACD9008DE511 /* FERigidShellDomain.cpp in Sources */,\n\t\t\t\tD53231062142ACD9008DE511 /* FEUncoupledFiberExpLinear.cpp in Sources */,\n\t\t\t\tD53231472142ACD9008DE511 /* FEPeriodicSurfaceConstraint.cpp in Sources */,\n\t\t\t\tD532305B2142ACD9008DE511 /* FEElasticMultiscaleDomain1O.cpp in Sources */,\n\t\t\t\tD5322FF72142ACD9008DE511 /* FEVonMisesPlasticity.cpp in Sources */,\n\t\t\t\tD5B939A922D23D4700E495BA /* FEReactivePlasticity.cpp in Sources */,\n\t\t\t\tD53230C92142ACD9008DE511 /* FEWrinkleOgdenMaterial.cpp in Sources */,\n\t\t\t\tD532303C2142ACD9008DE511 /* FEMortarContactSurface.cpp in Sources */,\n\t\t\t\tD532304B2142ACD9008DE511 /* FECoupledTransIsoVerondaWestmann.cpp in Sources */,\n\t\t\t\tD532309D2142ACD9008DE511 /* FERigidWallInterface.cpp in Sources */,\n\t\t\t\tD53230AB2142ACD9008DE511 /* FEHolmesMow.cpp in Sources */,\n\t\t\t\tD5DEA26226F52F6900E26ED3 /* FEFiberNaturalNeoHookean.cpp in Sources */,\n\t\t\t\tD532317D2142ACD9008DE511 /* FEElasticEASShellDomain.cpp in Sources */,\n\t\t\t\tD53231512142ACD9008DE511 /* FEUncoupledElasticMixture.cpp in Sources */,\n\t\t\t\tD53230D02142ACD9008DE511 /* FEBCPrescribedDeformation.cpp in Sources */,\n\t\t\t\tD53231772142ACD9008DE511 /* FEElasticTrussDomain.cpp in Sources */,\n\t\t\t\tD53230172142ACD9008DE511 /* FESlidingInterface.cpp in Sources */,\n\t\t\t\tD53230BD2142ACD9008DE511 /* FEHolzapfelGasserOgden.cpp in Sources */,\n\t\t\t\tD53230BA2142ACD9008DE511 /* FEPrescribedActiveContractionTransIsoUC.cpp in Sources */,\n\t\t\t\tD53231242142ACD9008DE511 /* FETiedInterface.cpp in Sources */,\n\t\t\t\tD53230D92142ACD9008DE511 /* FEMuscleMaterial.cpp in Sources */,\n\t\t\t\tD532310E2142ACD9008DE511 /* FEDamageNeoHookean.cpp in Sources */,\n\t\t\t\tD532315D2142ACD9008DE511 /* FEMindlinElastic2O.cpp in Sources */,\n\t\t\t\tD53230CB2142ACD9008DE511 /* FE2DTransIsoVerondaWestmann.cpp in Sources */,\n\t\t\t\tD53230622142ACD9008DE511 /* FEFiberExpPowUncoupled.cpp in Sources */,\n\t\t\t\tD5135C29232191C6008AFD7D /* FENaturalNeoHookean.cpp in Sources */,\n\t\t\t\tD53231622142ACD9008DE511 /* FECubicCLE.cpp in Sources */,\n\t\t\t\tD5709D8C22832FD2007CAB0A /* FESolidLinearSystem.cpp in Sources */,\n\t\t\t\tD53230952142ACD9008DE511 /* FERVEModel2O.cpp in Sources */,\n\t\t\t\tD57DD3DF23F6EF2C001347CB /* FEGenericHyperelasticUC.cpp in Sources */,\n\t\t\t\tD53231152142ACD9008DE511 /* FEUncoupledViscoElasticMaterial.cpp in Sources */,\n\t\t\t\tD53230572142ACD9008DE511 /* FEPrescribedActiveContractionUniaxial.cpp in Sources */,\n\t\t\t\tD53230092142ACD9008DE511 /* FESphericalFiberDistribution.cpp in Sources */,\n\t\t\t\tD53230F92142ACD9008DE511 /* FETrussMaterial.cpp in Sources */,\n\t\t\t\tD532301A2142ACD9008DE511 /* FECoupledTransIsoMooneyRivlin.cpp in Sources */,\n\t\t\t\tD53231432142ACD9008DE511 /* FEPointConstraint.cpp in Sources */,\n\t\t\t\tD53231012142ACD9008DE511 /* FETiedElasticInterface.cpp in Sources */,\n\t\t\t\tD53230FC2142ACD9008DE511 /* FEDonnanEquilibrium.cpp in Sources */,\n\t\t\t\tD5C13ED4240477A3005897E1 /* FEActiveFiberStress.cpp in Sources */,\n\t\t\t\tD5BD65A72333E32000EB9FD1 /* FEInSituStretchGradient.cpp in Sources */,\n\t\t\t\tD532305A2142ACD9008DE511 /* FESpringMaterial.cpp in Sources */,\n\t\t\t\tD53231302142ACD9008DE511 /* FEDamageCriterion.cpp in Sources */,\n\t\t\t\tD532314C2142ACD9008DE511 /* FERigidAngularDamper.cpp in Sources */,\n\t\t\t\tD53231132142ACD9008DE511 /* FEContinuousFiberDistributionUC.cpp in Sources */,\n\t\t\t\tD53230DC2142ACD9008DE511 /* FEVerondaWestmann.cpp in Sources */,\n\t\t\t\tD53231362142ACD9008DE511 /* FEAugLagLinearConstraint.cpp in Sources */,\n\t\t\t\tD532301D2142ACD9008DE511 /* FEPointBodyForce.cpp in Sources */,\n\t\t\t\tD5BC9F4D26D07B07003BBF6E /* FEMassDamping.cpp in Sources */,\n\t\t\t\tD5322FFB2142ACD9008DE511 /* FEOrthotropicCLE.cpp in Sources */,\n\t\t\t\tD576A7D5256C366B00415BF7 /* FERigidFollowerForce.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5322E3E2142ACC9008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322E3F2142ACC9008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5322E412142ACC9008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/usr/local/Cellar/gsl/2.4/lib,\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322E422142ACC9008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/usr/local/Cellar/gsl/2.4/lib,\n\t\t\t\t);\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5322E302142ACC9008DE511 /* Build configuration list for PBXProject \"FEBioMech\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322E3E2142ACC9008DE511 /* Debug */,\n\t\t\t\tD5322E3F2142ACC9008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5322E402142ACC9008DE511 /* Build configuration list for PBXNativeTarget \"FEBioMech\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322E412142ACC9008DE511 /* Debug */,\n\t\t\t\tD5322E422142ACC9008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5322E2D2142ACC9008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioMech/FEBioMech.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioMech.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioMech/FEBioMech.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioMix/FEBioMix.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD507640E2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D507640C2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.cpp */; };\n\t\tD507640F2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.h in Headers */ = {isa = PBXBuildFile; fileRef = D507640D2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.h */; };\n\t\tD5322D852142ABA0008DE511 /* FEBiphasicDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CDE2142ABA0008DE511 /* FEBiphasicDomain.h */; };\n\t\tD5322D862142ABA0008DE511 /* FEMultiphasicSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CDF2142ABA0008DE511 /* FEMultiphasicSolver.h */; };\n\t\tD5322D872142ABA0008DE511 /* FEMultiphasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CE02142ABA0008DE511 /* FEMultiphasic.cpp */; };\n\t\tD5322D882142ABA0008DE511 /* FESupplyMichaelisMenten.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CE12142ABA0008DE511 /* FESupplyMichaelisMenten.h */; };\n\t\tD5322D892142ABA0008DE511 /* FEMultiphasicStandard.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CE22142ABA0008DE511 /* FEMultiphasicStandard.h */; };\n\t\tD5322D8A2142ABA0008DE511 /* FEMassActionReversibleEffective.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CE32142ABA0008DE511 /* FEMassActionReversibleEffective.h */; };\n\t\tD5322D8B2142ABA0008DE511 /* FEConcentrationIndependentReaction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CE42142ABA0008DE511 /* FEConcentrationIndependentReaction.h */; };\n\t\tD5322D8C2142ABA0008DE511 /* FESlidingInterfaceBiphasic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CE52142ABA0008DE511 /* FESlidingInterfaceBiphasic.h */; };\n\t\tD5322D8D2142ABA0008DE511 /* FEReactionRateHuiskes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CE62142ABA0008DE511 /* FEReactionRateHuiskes.cpp */; };\n\t\tD5322D8E2142ABA0008DE511 /* FEFiberExpPowSBM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CE72142ABA0008DE511 /* FEFiberExpPowSBM.cpp */; };\n\t\tD5322D8F2142ABA0008DE511 /* FEPermRefTransIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CE82142ABA0008DE511 /* FEPermRefTransIso.h */; };\n\t\tD5322D902142ABA0008DE511 /* FESFDSBM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CE92142ABA0008DE511 /* FESFDSBM.cpp */; };\n\t\tD5322D912142ABA0008DE511 /* FEActiveMomentumSupply.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CEA2142ABA0008DE511 /* FEActiveMomentumSupply.h */; };\n\t\tD5322D922142ABA0008DE511 /* FEMultiphasicMultigeneration.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CEB2142ABA0008DE511 /* FEMultiphasicMultigeneration.h */; };\n\t\tD5322D932142ABA0008DE511 /* FEFluidFlux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CEC2142ABA0008DE511 /* FEFluidFlux.cpp */; };\n\t\tD5322D942142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CED2142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.h */; };\n\t\tD5322D952142ABA0008DE511 /* FEMassActionForwardEffective.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CEE2142ABA0008DE511 /* FEMassActionForwardEffective.h */; };\n\t\tD5322D962142ABA0008DE511 /* FETiedMultiphasicInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CEF2142ABA0008DE511 /* FETiedMultiphasicInterface.h */; };\n\t\tD5322D972142ABA0008DE511 /* FESlidingInterface3.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CF02142ABA0008DE511 /* FESlidingInterface3.h */; };\n\t\tD5322D982142ABA0008DE511 /* FEMultiphasicSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CF12142ABA0008DE511 /* FEMultiphasicSolidDomain.cpp */; };\n\t\tD5322D992142ABA0008DE511 /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CF22142ABA0008DE511 /* stdafx.h */; };\n\t\tD5322D9A2142ABA0008DE511 /* FESolubConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CF32142ABA0008DE511 /* FESolubConst.cpp */; };\n\t\tD5322D9B2142ABA0008DE511 /* FEMembraneMassActionReversible.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CF42142ABA0008DE511 /* FEMembraneMassActionReversible.cpp */; };\n\t\tD5322D9C2142ABA0008DE511 /* FEPoroTraction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CF52142ABA0008DE511 /* FEPoroTraction.h */; };\n\t\tD5322D9D2142ABA0008DE511 /* FESolventSupply.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CF62142ABA0008DE511 /* FESolventSupply.h */; };\n\t\tD5322D9E2142ABA0008DE511 /* FETriphasicDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CF72142ABA0008DE511 /* FETriphasicDomain.cpp */; };\n\t\tD5322D9F2142ABA0008DE511 /* FEOsmCoefConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CF82142ABA0008DE511 /* FEOsmCoefConst.cpp */; };\n\t\tD5322DA02142ABA0008DE511 /* FESFDSBM.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CF92142ABA0008DE511 /* FESFDSBM.h */; };\n\t\tD5322DA12142ABA0008DE511 /* FEMultiphasicDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CFA2142ABA0008DE511 /* FEMultiphasicDomain.h */; };\n\t\tD5322DA22142ABA0008DE511 /* FEFluidFlux.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CFB2142ABA0008DE511 /* FEFluidFlux.h */; };\n\t\tD5322DA32142ABA0008DE511 /* FEBiphasicSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CFC2142ABA0008DE511 /* FEBiphasicSolver.h */; };\n\t\tD5322DA42142ABA0008DE511 /* FEMembraneMassActionForward.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CFD2142ABA0008DE511 /* FEMembraneMassActionForward.cpp */; };\n\t\tD5322DA52142ABA1008DE511 /* FEPermHolmesMow.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CFE2142ABA0008DE511 /* FEPermHolmesMow.h */; };\n\t\tD5322DA62142ABA1008DE511 /* FEMichaelisMenten.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CFF2142ABA0008DE511 /* FEMichaelisMenten.h */; };\n\t\tD5322DA72142ABA1008DE511 /* FEActiveConstantSupply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D002142ABA0008DE511 /* FEActiveConstantSupply.cpp */; };\n\t\tD5322DA82142ABA1008DE511 /* FEMassActionForwardEffective.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D012142ABA0008DE511 /* FEMassActionForwardEffective.cpp */; };\n\t\tD5322DA92142ABA1008DE511 /* FETriphasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D022142ABA0008DE511 /* FETriphasic.cpp */; };\n\t\tD5322DAA2142ABA1008DE511 /* FEReactionRateExpSED.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D032142ABA0008DE511 /* FEReactionRateExpSED.h */; };\n\t\tD5322DAB2142ABA1008DE511 /* FEDiffConstIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D042142ABA0008DE511 /* FEDiffConstIso.cpp */; };\n\t\tD5322DAC2142ABA1008DE511 /* FEHydraulicPermeability.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D052142ABA0008DE511 /* FEHydraulicPermeability.cpp */; };\n\t\tD5322DAD2142ABA1008DE511 /* FEMultiphasicSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D062142ABA0008DE511 /* FEMultiphasicSolidDomain.h */; };\n\t\tD5322DAE2142ABA1008DE511 /* FEBiphasicContactSurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D072142ABA0008DE511 /* FEBiphasicContactSurface.cpp */; };\n\t\tD5322DAF2142ABA1008DE511 /* FEPermRefOrtho.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D082142ABA0008DE511 /* FEPermRefOrtho.h */; };\n\t\tD5322DB02142ABA1008DE511 /* FESoluteInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D092142ABA0008DE511 /* FESoluteInterface.cpp */; };\n\t\tD5322DB12142ABA1008DE511 /* FEChemicalReaction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D0A2142ABA0008DE511 /* FEChemicalReaction.h */; };\n\t\tD5322DB22142ABA1008DE511 /* FESlidingInterfaceMP.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D0B2142ABA0008DE511 /* FESlidingInterfaceMP.h */; };\n\t\tD5322DB32142ABA1008DE511 /* FESolutesMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D0C2142ABA0008DE511 /* FESolutesMaterialPoint.h */; };\n\t\tD5322DB42142ABA1008DE511 /* FEMassActionReversible.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D0D2142ABA0008DE511 /* FEMassActionReversible.cpp */; };\n\t\tD5322DB52142ABA1008DE511 /* FEBiphasicSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D0E2142ABA0008DE511 /* FEBiphasicSolidDomain.h */; };\n\t\tD5322DB62142ABA1008DE511 /* FEMassActionReversibleEffective.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D0F2142ABA0008DE511 /* FEMassActionReversibleEffective.cpp */; };\n\t\tD5322DB82142ABA1008DE511 /* FETiedMultiphasicInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D112142ABA0008DE511 /* FETiedMultiphasicInterface.cpp */; };\n\t\tD5322DB92142ABA1008DE511 /* FETriphasicDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D122142ABA0008DE511 /* FETriphasicDomain.h */; };\n\t\tD5322DBA2142ABA1008DE511 /* FEMultiphasicSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D132142ABA0008DE511 /* FEMultiphasicSolver.cpp */; };\n\t\tD5322DBB2142ABA1008DE511 /* FEPressureStabilization.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D142142ABA0008DE511 /* FEPressureStabilization.h */; };\n\t\tD5322DBC2142ABA1008DE511 /* FEBiphasicSoluteDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D152142ABA0008DE511 /* FEBiphasicSoluteDomain.h */; };\n\t\tD5322DBD2142ABA1008DE511 /* FEDiffAlbroIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D162142ABA0008DE511 /* FEDiffAlbroIso.cpp */; };\n\t\tD5322DBE2142ABA1008DE511 /* FESoluteFlux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D172142ABA0008DE511 /* FESoluteFlux.cpp */; };\n\t\tD5322DBF2142ABA1008DE511 /* FEBiphasicSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D182142ABA0008DE511 /* FEBiphasicSolver.cpp */; };\n\t\tD5322DC02142ABA1008DE511 /* FEBiphasicSolute.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D192142ABA0008DE511 /* FEBiphasicSolute.h */; };\n\t\tD5322DC12142ABA1008DE511 /* FEOsmoticCoefficient.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D1A2142ABA0008DE511 /* FEOsmoticCoefficient.h */; };\n\t\tD5322DC22142ABA1008DE511 /* FEMembraneReaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D1B2142ABA0008DE511 /* FEMembraneReaction.cpp */; };\n\t\tD5322DC32142ABA1008DE511 /* FEMultiphasicDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D1C2142ABA0008DE511 /* FEMultiphasicDomain.cpp */; };\n\t\tD5322DC42142ABA1008DE511 /* FEMassActionReversible.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D1D2142ABA0008DE511 /* FEMassActionReversible.h */; };\n\t\tD5322DC52142ABA1008DE511 /* FEBiphasicDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D1E2142ABA0008DE511 /* FEBiphasicDomain.cpp */; };\n\t\tD5322DC62142ABA1008DE511 /* FEBiphasicShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D1F2142ABA0008DE511 /* FEBiphasicShellDomain.cpp */; };\n\t\tD5322DC72142ABA1008DE511 /* FEMixDomainFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D202142ABA0008DE511 /* FEMixDomainFactory.cpp */; };\n\t\tD5322DC82142ABA1008DE511 /* FEBiphasicShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D212142ABA0008DE511 /* FEBiphasicShellDomain.h */; };\n\t\tD5322DC92142ABA1008DE511 /* FEBiphasicSoluteSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D222142ABA0008DE511 /* FEBiphasicSoluteSolver.h */; };\n\t\tD5322DCA2142ABA1008DE511 /* FESolventSupply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D232142ABA0008DE511 /* FESolventSupply.cpp */; };\n\t\tD5322DCB2142ABA1008DE511 /* FEActiveConstantSupply.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D242142ABA0008DE511 /* FEActiveConstantSupply.h */; };\n\t\tD5322DCC2142ABA1008DE511 /* FEMassActionForward.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D252142ABA0008DE511 /* FEMassActionForward.cpp */; };\n\t\tD5322DCD2142ABA1008DE511 /* FESolubConst.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D262142ABA0008DE511 /* FESolubConst.h */; };\n\t\tD5322DCE2142ABA1008DE511 /* FEMultiphasicShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D272142ABA0008DE511 /* FEMultiphasicShellDomain.h */; };\n\t\tD5322DCF2142ABA1008DE511 /* FESolute.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D282142ABA0008DE511 /* FESolute.cpp */; };\n\t\tD5322DD02142ABA1008DE511 /* FECarterHayes.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D292142ABA0008DE511 /* FECarterHayes.h */; };\n\t\tD5322DD12142ABA1008DE511 /* FEBiphasicSoluteSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D2A2142ABA0008DE511 /* FEBiphasicSoluteSolidDomain.cpp */; };\n\t\tD5322DD22142ABA1008DE511 /* FEPermConstIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D2B2142ABA0008DE511 /* FEPermConstIso.h */; };\n\t\tD5322DD32142ABA1008DE511 /* FEMembraneReaction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D2C2142ABA0008DE511 /* FEMembraneReaction.h */; };\n\t\tD5322DD42142ABA1008DE511 /* FEMembraneMassActionReversible.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D2D2142ABA0008DE511 /* FEMembraneMassActionReversible.h */; };\n\t\tD5322DD52142ABA1008DE511 /* FEPermRefTransIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D2E2142ABA0008DE511 /* FEPermRefTransIso.cpp */; };\n\t\tD5322DD62142ABA1008DE511 /* FEPermHolmesMow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D2F2142ABA0008DE511 /* FEPermHolmesMow.cpp */; };\n\t\tD5322DD72142ABA1008DE511 /* FEMembraneMassActionForward.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D302142ABA0008DE511 /* FEMembraneMassActionForward.h */; };\n\t\tD5322DD82142ABA1008DE511 /* FEReactionRateNims.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D312142ABA0008DE511 /* FEReactionRateNims.h */; };\n\t\tD5322DD92142ABA1008DE511 /* FEBiphasicSolute.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D322142ABA0008DE511 /* FEBiphasicSolute.cpp */; };\n\t\tD5322DDB2142ABA1008DE511 /* FEBiphasicSoluteDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D342142ABA0008DE511 /* FEBiphasicSoluteDomain.cpp */; };\n\t\tD5322DDC2142ABA1008DE511 /* FEMembraneReactionRateIonChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D352142ABA0008DE511 /* FEMembraneReactionRateIonChannel.cpp */; };\n\t\tD5322DDD2142ABA1008DE511 /* FEDiffRefIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D362142ABA0008DE511 /* FEDiffRefIso.cpp */; };\n\t\tD5322DDE2142ABA1008DE511 /* FECarterHayes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D372142ABA0008DE511 /* FECarterHayes.cpp */; };\n\t\tD5322DDF2142ABA1008DE511 /* FESupplyBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D382142ABA0008DE511 /* FESupplyBinding.h */; };\n\t\tD5322DE02142ABA1008DE511 /* FETiedBiphasicInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D392142ABA0008DE511 /* FETiedBiphasicInterface.h */; };\n\t\tD5322DE12142ABA1008DE511 /* FEFiberPowLinearSBM.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D3A2142ABA0008DE511 /* FEFiberPowLinearSBM.h */; };\n\t\tD5322DE22142ABA1008DE511 /* FEMembraneReactionRateVoltageGated.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D3B2142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.cpp */; };\n\t\tD5322DE32142ABA1008DE511 /* FESoluteInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D3C2142ABA0008DE511 /* FESoluteInterface.h */; };\n\t\tD5322DE42142ABA1008DE511 /* FEOsmCoefManning.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D3D2142ABA0008DE511 /* FEOsmCoefManning.cpp */; };\n\t\tD5322DE52142ABA1008DE511 /* FESolubManning.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D3E2142ABA0008DE511 /* FESolubManning.h */; };\n\t\tD5322DE62142ABA1008DE511 /* FESoluteFlux.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D3F2142ABA0008DE511 /* FESoluteFlux.h */; };\n\t\tD5322DE72142ABA1008DE511 /* FEReactionRateHuiskes.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D402142ABA0008DE511 /* FEReactionRateHuiskes.h */; };\n\t\tD5322DE82142ABA1008DE511 /* FEOsmCoefManning.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D412142ABA0008DE511 /* FEOsmCoefManning.h */; };\n\t\tD5322DE92142ABA1008DE511 /* FEBioMix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D422142ABA0008DE511 /* FEBioMix.cpp */; };\n\t\tD5322DEA2142ABA1008DE511 /* FESupplyConst.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D432142ABA0008DE511 /* FESupplyConst.h */; };\n\t\tD5322DEB2142ABA1008DE511 /* FEReactionRateExpSED.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D442142ABA0008DE511 /* FEReactionRateExpSED.cpp */; };\n\t\tD5322DEC2142ABA1008DE511 /* FETiedBiphasicInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D452142ABA0008DE511 /* FETiedBiphasicInterface.cpp */; };\n\t\tD5322DED2142ABA1008DE511 /* FEBioMixData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D462142ABA0008DE511 /* FEBioMixData.cpp */; };\n\t\tD5322DEE2142ABA1008DE511 /* FEMembraneReactionRateConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D472142ABA0008DE511 /* FEMembraneReactionRateConst.cpp */; };\n\t\tD5322DEF2142ABA1008DE511 /* FESupplyConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D482142ABA0008DE511 /* FESupplyConst.cpp */; };\n\t\tD5322DF02142ABA1008DE511 /* FEMassActionForward.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D492142ABA0008DE511 /* FEMassActionForward.h */; };\n\t\tD5322DF12142ABA1008DE511 /* FEBiphasicSoluteShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D4A2142ABA0008DE511 /* FEBiphasicSoluteShellDomain.cpp */; };\n\t\tD5322DF22142ABA1008DE511 /* FEPermRefIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D4B2142ABA0008DE511 /* FEPermRefIso.h */; };\n\t\tD5322DF32142ABA1008DE511 /* FEOsmCoefConst.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D4C2142ABA0008DE511 /* FEOsmCoefConst.h */; };\n\t\tD5322DF42142ABA1008DE511 /* FEPoroTraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D4D2142ABA0008DE511 /* FEPoroTraction.cpp */; };\n\t\tD5322DF52142ABA1008DE511 /* FEPermRefIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D4E2142ABA0008DE511 /* FEPermRefIso.cpp */; };\n\t\tD5322DF62142ABA1008DE511 /* FEBiphasicSolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D4F2142ABA0008DE511 /* FEBiphasicSolidDomain.cpp */; };\n\t\tD5322DF72142ABA1008DE511 /* FEBiphasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D502142ABA0008DE511 /* FEBiphasic.cpp */; };\n\t\tD5322DF82142ABA1008DE511 /* FESolventSupplyStarling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D512142ABA0008DE511 /* FESolventSupplyStarling.cpp */; };\n\t\tD5322DF92142ABA1008DE511 /* FESolubManning.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D522142ABA0008DE511 /* FESolubManning.cpp */; };\n\t\tD5322DFA2142ABA1008DE511 /* FEBiphasicSoluteSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D532142ABA0008DE511 /* FEBiphasicSoluteSolver.cpp */; };\n\t\tD5322DFB2142ABA1008DE511 /* FEDiffConstIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D542142ABA0008DE511 /* FEDiffConstIso.h */; };\n\t\tD5322DFC2142ABA1008DE511 /* FEBioMixData.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D552142ABA0008DE511 /* FEBioMixData.h */; };\n\t\tD5322DFD2142ABA1008DE511 /* FEFiberPowLinearSBM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D562142ABA0008DE511 /* FEFiberPowLinearSBM.cpp */; };\n\t\tD5322DFE2142ABA1008DE511 /* FEDiffConstOrtho.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D572142ABA0008DE511 /* FEDiffConstOrtho.h */; };\n\t\tD5322DFF2142ABA1008DE511 /* FESlidingInterface2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D582142ABA0008DE511 /* FESlidingInterface2.cpp */; };\n\t\tD5322E002142ABA1008DE511 /* FESlidingInterfaceBiphasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D592142ABA0008DE511 /* FESlidingInterfaceBiphasic.cpp */; };\n\t\tD5322E012142ABA1008DE511 /* FEPermRefOrtho.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D5A2142ABA0008DE511 /* FEPermRefOrtho.cpp */; };\n\t\tD5322E022142ABA1008DE511 /* FEMembraneReactionRateIonChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D5B2142ABA0008DE511 /* FEMembraneReactionRateIonChannel.h */; };\n\t\tD5322E032142ABA1008DE511 /* FEReactionRateNims.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D5C2142ABA0008DE511 /* FEReactionRateNims.cpp */; };\n\t\tD5322E042142ABA1008DE511 /* FEMichaelisMenten.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D5D2142ABA0008DE511 /* FEMichaelisMenten.cpp */; };\n\t\tD5322E052142ABA1008DE511 /* FESolute.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D5E2142ABA0008DE511 /* FESolute.h */; };\n\t\tD5322E062142ABA1008DE511 /* FEBioMixPlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D5F2142ABA0008DE511 /* FEBioMixPlot.cpp */; };\n\t\tD5322E072142ABA1008DE511 /* FEFiberExpPowSBM.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D602142ABA0008DE511 /* FEFiberExpPowSBM.h */; };\n\t\tD5322E082142ABA1008DE511 /* FESlidingInterface3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D612142ABA0008DE511 /* FESlidingInterface3.cpp */; };\n\t\tD5322E092142ABA1008DE511 /* FEReactionRateConst.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D622142ABA0008DE511 /* FEReactionRateConst.h */; };\n\t\tD5322E0A2142ABA1008DE511 /* FEMultiphasicStandard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D632142ABA0008DE511 /* FEMultiphasicStandard.cpp */; };\n\t\tD5322E0B2142ABA1008DE511 /* FESupplySynthesisBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D642142ABA0008DE511 /* FESupplySynthesisBinding.h */; };\n\t\tD5322E0C2142ABA1008DE511 /* FEMultiphasicMultigeneration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D652142ABA0008DE511 /* FEMultiphasicMultigeneration.cpp */; };\n\t\tD5322E0D2142ABA1008DE511 /* FEBiphasicContactSurface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D662142ABA0008DE511 /* FEBiphasicContactSurface.h */; };\n\t\tD5322E0E2142ABA1008DE511 /* FEBioMix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D672142ABA0008DE511 /* FEBioMix.h */; };\n\t\tD5322E0F2142ABA1008DE511 /* FEBiphasicSoluteShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D682142ABA0008DE511 /* FEBiphasicSoluteShellDomain.h */; };\n\t\tD5322E102142ABA1008DE511 /* FESolventSupplyStarling.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D692142ABA0008DE511 /* FESolventSupplyStarling.h */; };\n\t\tD5322E112142ABA1008DE511 /* FEMixDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D6A2142ABA0008DE511 /* FEMixDomainFactory.h */; };\n\t\tD5322E122142ABA1008DE511 /* FEHydraulicPermeability.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D6B2142ABA0008DE511 /* FEHydraulicPermeability.h */; };\n\t\tD5322E132142ABA1008DE511 /* FETriphasic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D6C2142ABA0008DE511 /* FETriphasic.h */; };\n\t\tD5322E142142ABA1008DE511 /* FEDiffAlbroIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D6D2142ABA0008DE511 /* FEDiffAlbroIso.h */; };\n\t\tD5322E152142ABA1008DE511 /* FESolutesMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D6E2142ABA0008DE511 /* FESolutesMaterialPoint.cpp */; };\n\t\tD5322E162142ABA1008DE511 /* FEOsmoticCoefficient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D6F2142ABA0008DE511 /* FEOsmoticCoefficient.cpp */; };\n\t\tD5322E172142ABA1008DE511 /* FEActiveMomentumSupply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D702142ABA0008DE511 /* FEActiveMomentumSupply.cpp */; };\n\t\tD5322E182142ABA1008DE511 /* FEDiffConstOrtho.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D712142ABA0008DE511 /* FEDiffConstOrtho.cpp */; };\n\t\tD5322E192142ABA1008DE511 /* FESlidingInterface2.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D722142ABA0008DE511 /* FESlidingInterface2.h */; };\n\t\tD5322E1A2142ABA1008DE511 /* FEDiffRefIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D732142ABA0008DE511 /* FEDiffRefIso.h */; };\n\t\tD5322E1B2142ABA1008DE511 /* FEBiphasic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D742142ABA0008DE511 /* FEBiphasic.h */; };\n\t\tD5322E1C2142ABA1008DE511 /* FEReaction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D752142ABA0008DE511 /* FEReaction.h */; };\n\t\tD5322E1D2142ABA1008DE511 /* FEMembraneReactionRateConst.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D762142ABA0008DE511 /* FEMembraneReactionRateConst.h */; };\n\t\tD5322E1E2142ABA1008DE511 /* FESupplySynthesisBinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D772142ABA0008DE511 /* FESupplySynthesisBinding.cpp */; };\n\t\tD5322E1F2142ABA1008DE511 /* FEBioMixPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D782142ABA0008DE511 /* FEBioMixPlot.h */; };\n\t\tD5322E202142ABA1008DE511 /* FEMultiphasic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D792142ABA0008DE511 /* FEMultiphasic.h */; };\n\t\tD5322E212142ABA1008DE511 /* FEPressureStabilization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D7A2142ABA0008DE511 /* FEPressureStabilization.cpp */; };\n\t\tD5322E222142ABA1008DE511 /* FEConcentrationIndependentReaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D7B2142ABA0008DE511 /* FEConcentrationIndependentReaction.cpp */; };\n\t\tD5322E232142ABA1008DE511 /* FEBiphasicSoluteSolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322D7C2142ABA0008DE511 /* FEBiphasicSoluteSolidDomain.h */; };\n\t\tD5322E242142ABA1008DE511 /* FESupplyMichaelisMenten.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D7D2142ABA0008DE511 /* FESupplyMichaelisMenten.cpp */; };\n\t\tD5322E252142ABA1008DE511 /* FEPermConstIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D7E2142ABA0008DE511 /* FEPermConstIso.cpp */; };\n\t\tD5322E262142ABA1008DE511 /* FEMultiphasicShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D7F2142ABA0008DE511 /* FEMultiphasicShellDomain.cpp */; };\n\t\tD5322E272142ABA1008DE511 /* FESlidingInterfaceMP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D802142ABA0008DE511 /* FESlidingInterfaceMP.cpp */; };\n\t\tD5322E282142ABA1008DE511 /* FESupplyBinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D812142ABA0008DE511 /* FESupplyBinding.cpp */; };\n\t\tD5322E292142ABA1008DE511 /* FEReaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D822142ABA0008DE511 /* FEReaction.cpp */; };\n\t\tD5322E2A2142ABA1008DE511 /* FEReactionRateConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D832142ABA0008DE511 /* FEReactionRateConst.cpp */; };\n\t\tD5322E2B2142ABA1008DE511 /* FEChemicalReaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322D842142ABA0008DE511 /* FEChemicalReaction.cpp */; };\n\t\tD5743D3F247EDABF006B4069 /* FEPermExpIso.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5743D3D247EDABE006B4069 /* FEPermExpIso.cpp */; };\n\t\tD5743D40247EDABF006B4069 /* FEPermExpIso.h in Headers */ = {isa = PBXBuildFile; fileRef = D5743D3E247EDABE006B4069 /* FEPermExpIso.h */; };\n\t\tD5C5B79C239F0BA400D29EE0 /* FESBMPointSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C5B79A239F0BA400D29EE0 /* FESBMPointSource.h */; };\n\t\tD5C5B79D239F0BA400D29EE0 /* FESBMPointSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C5B79B239F0BA400D29EE0 /* FESBMPointSource.cpp */; };\n\t\tD5DA959823D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5DA959623D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.cpp */; };\n\t\tD5DA959923D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.h in Headers */ = {isa = PBXBuildFile; fileRef = D5DA959723D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.h */; };\n\t\tD5E85D9822021E4C00F5DF83 /* febiomix_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E85D9522021E4B00F5DF83 /* febiomix_api.h */; };\n\t\tD5E85D9922021E4C00F5DF83 /* FEPorousNeoHookean.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E85D9622021E4B00F5DF83 /* FEPorousNeoHookean.cpp */; };\n\t\tD5E85D9A22021E4C00F5DF83 /* FEPorousNeoHookean.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E85D9722021E4C00F5DF83 /* FEPorousNeoHookean.h */; };\n\t\tD5F50DC823E1205300B21A30 /* FESolutePointSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F50DC623E1205200B21A30 /* FESolutePointSource.h */; };\n\t\tD5F50DC923E1205300B21A30 /* FESolutePointSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F50DC723E1205300B21A30 /* FESolutePointSource.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD507640C2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FEMatchingOsmoticCoefficientBC.cpp; sourceTree = \"<group>\"; };\n\t\tD507640D2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FEMatchingOsmoticCoefficientBC.h; sourceTree = \"<group>\"; };\n\t\tD5322CCF2142AB8D008DE511 /* libFEBioMix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioMix.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5322CDE2142ABA0008DE511 /* FEBiphasicDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322CDF2142ABA0008DE511 /* FEMultiphasicSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322CE02142ABA0008DE511 /* FEMultiphasic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CE12142ABA0008DE511 /* FESupplyMichaelisMenten.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESupplyMichaelisMenten.h; sourceTree = \"<group>\"; };\n\t\tD5322CE22142ABA0008DE511 /* FEMultiphasicStandard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicStandard.h; sourceTree = \"<group>\"; };\n\t\tD5322CE32142ABA0008DE511 /* FEMassActionReversibleEffective.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMassActionReversibleEffective.h; sourceTree = \"<group>\"; };\n\t\tD5322CE42142ABA0008DE511 /* FEConcentrationIndependentReaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConcentrationIndependentReaction.h; sourceTree = \"<group>\"; };\n\t\tD5322CE52142ABA0008DE511 /* FESlidingInterfaceBiphasic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingInterfaceBiphasic.h; sourceTree = \"<group>\"; };\n\t\tD5322CE62142ABA0008DE511 /* FEReactionRateHuiskes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactionRateHuiskes.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CE72142ABA0008DE511 /* FEFiberExpPowSBM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberExpPowSBM.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CE82142ABA0008DE511 /* FEPermRefTransIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPermRefTransIso.h; sourceTree = \"<group>\"; };\n\t\tD5322CE92142ABA0008DE511 /* FESFDSBM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESFDSBM.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CEA2142ABA0008DE511 /* FEActiveMomentumSupply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEActiveMomentumSupply.h; sourceTree = \"<group>\"; };\n\t\tD5322CEB2142ABA0008DE511 /* FEMultiphasicMultigeneration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicMultigeneration.h; sourceTree = \"<group>\"; };\n\t\tD5322CEC2142ABA0008DE511 /* FEFluidFlux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFlux.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CED2142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneReactionRateVoltageGated.h; sourceTree = \"<group>\"; };\n\t\tD5322CEE2142ABA0008DE511 /* FEMassActionForwardEffective.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMassActionForwardEffective.h; sourceTree = \"<group>\"; };\n\t\tD5322CEF2142ABA0008DE511 /* FETiedMultiphasicInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedMultiphasicInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322CF02142ABA0008DE511 /* FESlidingInterface3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingInterface3.h; sourceTree = \"<group>\"; };\n\t\tD5322CF12142ABA0008DE511 /* FEMultiphasicSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CF22142ABA0008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5322CF32142ABA0008DE511 /* FESolubConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolubConst.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CF42142ABA0008DE511 /* FEMembraneMassActionReversible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneMassActionReversible.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CF52142ABA0008DE511 /* FEPoroTraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPoroTraction.h; sourceTree = \"<group>\"; };\n\t\tD5322CF62142ABA0008DE511 /* FESolventSupply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolventSupply.h; sourceTree = \"<group>\"; };\n\t\tD5322CF72142ABA0008DE511 /* FETriphasicDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETriphasicDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CF82142ABA0008DE511 /* FEOsmCoefConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOsmCoefConst.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CF92142ABA0008DE511 /* FESFDSBM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESFDSBM.h; sourceTree = \"<group>\"; };\n\t\tD5322CFA2142ABA0008DE511 /* FEMultiphasicDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322CFB2142ABA0008DE511 /* FEFluidFlux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFlux.h; sourceTree = \"<group>\"; };\n\t\tD5322CFC2142ABA0008DE511 /* FEBiphasicSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322CFD2142ABA0008DE511 /* FEMembraneMassActionForward.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneMassActionForward.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CFE2142ABA0008DE511 /* FEPermHolmesMow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPermHolmesMow.h; sourceTree = \"<group>\"; };\n\t\tD5322CFF2142ABA0008DE511 /* FEMichaelisMenten.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMichaelisMenten.h; sourceTree = \"<group>\"; };\n\t\tD5322D002142ABA0008DE511 /* FEActiveConstantSupply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEActiveConstantSupply.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D012142ABA0008DE511 /* FEMassActionForwardEffective.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMassActionForwardEffective.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D022142ABA0008DE511 /* FETriphasic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETriphasic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D032142ABA0008DE511 /* FEReactionRateExpSED.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactionRateExpSED.h; sourceTree = \"<group>\"; };\n\t\tD5322D042142ABA0008DE511 /* FEDiffConstIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiffConstIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D052142ABA0008DE511 /* FEHydraulicPermeability.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEHydraulicPermeability.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D062142ABA0008DE511 /* FEMultiphasicSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D072142ABA0008DE511 /* FEBiphasicContactSurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicContactSurface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D082142ABA0008DE511 /* FEPermRefOrtho.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPermRefOrtho.h; sourceTree = \"<group>\"; };\n\t\tD5322D092142ABA0008DE511 /* FESoluteInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESoluteInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D0A2142ABA0008DE511 /* FEChemicalReaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEChemicalReaction.h; sourceTree = \"<group>\"; };\n\t\tD5322D0B2142ABA0008DE511 /* FESlidingInterfaceMP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingInterfaceMP.h; sourceTree = \"<group>\"; };\n\t\tD5322D0C2142ABA0008DE511 /* FESolutesMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolutesMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5322D0D2142ABA0008DE511 /* FEMassActionReversible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMassActionReversible.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D0E2142ABA0008DE511 /* FEBiphasicSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D0F2142ABA0008DE511 /* FEMassActionReversibleEffective.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMassActionReversibleEffective.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D112142ABA0008DE511 /* FETiedMultiphasicInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedMultiphasicInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D122142ABA0008DE511 /* FETriphasicDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETriphasicDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D132142ABA0008DE511 /* FEMultiphasicSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D142142ABA0008DE511 /* FEPressureStabilization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPressureStabilization.h; sourceTree = \"<group>\"; };\n\t\tD5322D152142ABA0008DE511 /* FEBiphasicSoluteDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSoluteDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D162142ABA0008DE511 /* FEDiffAlbroIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiffAlbroIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D172142ABA0008DE511 /* FESoluteFlux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESoluteFlux.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D182142ABA0008DE511 /* FEBiphasicSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D192142ABA0008DE511 /* FEBiphasicSolute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSolute.h; sourceTree = \"<group>\"; };\n\t\tD5322D1A2142ABA0008DE511 /* FEOsmoticCoefficient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOsmoticCoefficient.h; sourceTree = \"<group>\"; };\n\t\tD5322D1B2142ABA0008DE511 /* FEMembraneReaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneReaction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D1C2142ABA0008DE511 /* FEMultiphasicDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D1D2142ABA0008DE511 /* FEMassActionReversible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMassActionReversible.h; sourceTree = \"<group>\"; };\n\t\tD5322D1E2142ABA0008DE511 /* FEBiphasicDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D1F2142ABA0008DE511 /* FEBiphasicShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D202142ABA0008DE511 /* FEMixDomainFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMixDomainFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D212142ABA0008DE511 /* FEBiphasicShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D222142ABA0008DE511 /* FEBiphasicSoluteSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSoluteSolver.h; sourceTree = \"<group>\"; };\n\t\tD5322D232142ABA0008DE511 /* FESolventSupply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolventSupply.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D242142ABA0008DE511 /* FEActiveConstantSupply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEActiveConstantSupply.h; sourceTree = \"<group>\"; };\n\t\tD5322D252142ABA0008DE511 /* FEMassActionForward.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMassActionForward.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D262142ABA0008DE511 /* FESolubConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolubConst.h; sourceTree = \"<group>\"; };\n\t\tD5322D272142ABA0008DE511 /* FEMultiphasicShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D282142ABA0008DE511 /* FESolute.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolute.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D292142ABA0008DE511 /* FECarterHayes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECarterHayes.h; sourceTree = \"<group>\"; };\n\t\tD5322D2A2142ABA0008DE511 /* FEBiphasicSoluteSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSoluteSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D2B2142ABA0008DE511 /* FEPermConstIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPermConstIso.h; sourceTree = \"<group>\"; };\n\t\tD5322D2C2142ABA0008DE511 /* FEMembraneReaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneReaction.h; sourceTree = \"<group>\"; };\n\t\tD5322D2D2142ABA0008DE511 /* FEMembraneMassActionReversible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneMassActionReversible.h; sourceTree = \"<group>\"; };\n\t\tD5322D2E2142ABA0008DE511 /* FEPermRefTransIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPermRefTransIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D2F2142ABA0008DE511 /* FEPermHolmesMow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPermHolmesMow.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D302142ABA0008DE511 /* FEMembraneMassActionForward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneMassActionForward.h; sourceTree = \"<group>\"; };\n\t\tD5322D312142ABA0008DE511 /* FEReactionRateNims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactionRateNims.h; sourceTree = \"<group>\"; };\n\t\tD5322D322142ABA0008DE511 /* FEBiphasicSolute.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSolute.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D342142ABA0008DE511 /* FEBiphasicSoluteDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSoluteDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D352142ABA0008DE511 /* FEMembraneReactionRateIonChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneReactionRateIonChannel.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D362142ABA0008DE511 /* FEDiffRefIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiffRefIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D372142ABA0008DE511 /* FECarterHayes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECarterHayes.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D382142ABA0008DE511 /* FESupplyBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESupplyBinding.h; sourceTree = \"<group>\"; };\n\t\tD5322D392142ABA0008DE511 /* FETiedBiphasicInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedBiphasicInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322D3A2142ABA0008DE511 /* FEFiberPowLinearSBM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberPowLinearSBM.h; sourceTree = \"<group>\"; };\n\t\tD5322D3B2142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneReactionRateVoltageGated.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D3C2142ABA0008DE511 /* FESoluteInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESoluteInterface.h; sourceTree = \"<group>\"; };\n\t\tD5322D3D2142ABA0008DE511 /* FEOsmCoefManning.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOsmCoefManning.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D3E2142ABA0008DE511 /* FESolubManning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolubManning.h; sourceTree = \"<group>\"; };\n\t\tD5322D3F2142ABA0008DE511 /* FESoluteFlux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESoluteFlux.h; sourceTree = \"<group>\"; };\n\t\tD5322D402142ABA0008DE511 /* FEReactionRateHuiskes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactionRateHuiskes.h; sourceTree = \"<group>\"; };\n\t\tD5322D412142ABA0008DE511 /* FEOsmCoefManning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOsmCoefManning.h; sourceTree = \"<group>\"; };\n\t\tD5322D422142ABA0008DE511 /* FEBioMix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMix.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D432142ABA0008DE511 /* FESupplyConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESupplyConst.h; sourceTree = \"<group>\"; };\n\t\tD5322D442142ABA0008DE511 /* FEReactionRateExpSED.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactionRateExpSED.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D452142ABA0008DE511 /* FETiedBiphasicInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedBiphasicInterface.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D462142ABA0008DE511 /* FEBioMixData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMixData.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D472142ABA0008DE511 /* FEMembraneReactionRateConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMembraneReactionRateConst.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D482142ABA0008DE511 /* FESupplyConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESupplyConst.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D492142ABA0008DE511 /* FEMassActionForward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMassActionForward.h; sourceTree = \"<group>\"; };\n\t\tD5322D4A2142ABA0008DE511 /* FEBiphasicSoluteShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSoluteShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D4B2142ABA0008DE511 /* FEPermRefIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPermRefIso.h; sourceTree = \"<group>\"; };\n\t\tD5322D4C2142ABA0008DE511 /* FEOsmCoefConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOsmCoefConst.h; sourceTree = \"<group>\"; };\n\t\tD5322D4D2142ABA0008DE511 /* FEPoroTraction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPoroTraction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D4E2142ABA0008DE511 /* FEPermRefIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPermRefIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D4F2142ABA0008DE511 /* FEBiphasicSolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D502142ABA0008DE511 /* FEBiphasic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D512142ABA0008DE511 /* FESolventSupplyStarling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolventSupplyStarling.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D522142ABA0008DE511 /* FESolubManning.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolubManning.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D532142ABA0008DE511 /* FEBiphasicSoluteSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicSoluteSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D542142ABA0008DE511 /* FEDiffConstIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiffConstIso.h; sourceTree = \"<group>\"; };\n\t\tD5322D552142ABA0008DE511 /* FEBioMixData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMixData.h; sourceTree = \"<group>\"; };\n\t\tD5322D562142ABA0008DE511 /* FEFiberPowLinearSBM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFiberPowLinearSBM.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D572142ABA0008DE511 /* FEDiffConstOrtho.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiffConstOrtho.h; sourceTree = \"<group>\"; };\n\t\tD5322D582142ABA0008DE511 /* FESlidingInterface2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingInterface2.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D592142ABA0008DE511 /* FESlidingInterfaceBiphasic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingInterfaceBiphasic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D5A2142ABA0008DE511 /* FEPermRefOrtho.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPermRefOrtho.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D5B2142ABA0008DE511 /* FEMembraneReactionRateIonChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneReactionRateIonChannel.h; sourceTree = \"<group>\"; };\n\t\tD5322D5C2142ABA0008DE511 /* FEReactionRateNims.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactionRateNims.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D5D2142ABA0008DE511 /* FEMichaelisMenten.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMichaelisMenten.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D5E2142ABA0008DE511 /* FESolute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolute.h; sourceTree = \"<group>\"; };\n\t\tD5322D5F2142ABA0008DE511 /* FEBioMixPlot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMixPlot.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D602142ABA0008DE511 /* FEFiberExpPowSBM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFiberExpPowSBM.h; sourceTree = \"<group>\"; };\n\t\tD5322D612142ABA0008DE511 /* FESlidingInterface3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingInterface3.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D622142ABA0008DE511 /* FEReactionRateConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReactionRateConst.h; sourceTree = \"<group>\"; };\n\t\tD5322D632142ABA0008DE511 /* FEMultiphasicStandard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicStandard.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D642142ABA0008DE511 /* FESupplySynthesisBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESupplySynthesisBinding.h; sourceTree = \"<group>\"; };\n\t\tD5322D652142ABA0008DE511 /* FEMultiphasicMultigeneration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicMultigeneration.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D662142ABA0008DE511 /* FEBiphasicContactSurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicContactSurface.h; sourceTree = \"<group>\"; };\n\t\tD5322D672142ABA0008DE511 /* FEBioMix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMix.h; sourceTree = \"<group>\"; };\n\t\tD5322D682142ABA0008DE511 /* FEBiphasicSoluteShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSoluteShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D692142ABA0008DE511 /* FESolventSupplyStarling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolventSupplyStarling.h; sourceTree = \"<group>\"; };\n\t\tD5322D6A2142ABA0008DE511 /* FEMixDomainFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMixDomainFactory.h; sourceTree = \"<group>\"; };\n\t\tD5322D6B2142ABA0008DE511 /* FEHydraulicPermeability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEHydraulicPermeability.h; sourceTree = \"<group>\"; };\n\t\tD5322D6C2142ABA0008DE511 /* FETriphasic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETriphasic.h; sourceTree = \"<group>\"; };\n\t\tD5322D6D2142ABA0008DE511 /* FEDiffAlbroIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiffAlbroIso.h; sourceTree = \"<group>\"; };\n\t\tD5322D6E2142ABA0008DE511 /* FESolutesMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolutesMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D6F2142ABA0008DE511 /* FEOsmoticCoefficient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOsmoticCoefficient.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D702142ABA0008DE511 /* FEActiveMomentumSupply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEActiveMomentumSupply.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D712142ABA0008DE511 /* FEDiffConstOrtho.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiffConstOrtho.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D722142ABA0008DE511 /* FESlidingInterface2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingInterface2.h; sourceTree = \"<group>\"; };\n\t\tD5322D732142ABA0008DE511 /* FEDiffRefIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiffRefIso.h; sourceTree = \"<group>\"; };\n\t\tD5322D742142ABA0008DE511 /* FEBiphasic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasic.h; sourceTree = \"<group>\"; };\n\t\tD5322D752142ABA0008DE511 /* FEReaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEReaction.h; sourceTree = \"<group>\"; };\n\t\tD5322D762142ABA0008DE511 /* FEMembraneReactionRateConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMembraneReactionRateConst.h; sourceTree = \"<group>\"; };\n\t\tD5322D772142ABA0008DE511 /* FESupplySynthesisBinding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESupplySynthesisBinding.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D782142ABA0008DE511 /* FEBioMixPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMixPlot.h; sourceTree = \"<group>\"; };\n\t\tD5322D792142ABA0008DE511 /* FEMultiphasic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasic.h; sourceTree = \"<group>\"; };\n\t\tD5322D7A2142ABA0008DE511 /* FEPressureStabilization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPressureStabilization.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D7B2142ABA0008DE511 /* FEConcentrationIndependentReaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEConcentrationIndependentReaction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D7C2142ABA0008DE511 /* FEBiphasicSoluteSolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicSoluteSolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5322D7D2142ABA0008DE511 /* FESupplyMichaelisMenten.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESupplyMichaelisMenten.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D7E2142ABA0008DE511 /* FEPermConstIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPermConstIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D7F2142ABA0008DE511 /* FEMultiphasicShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D802142ABA0008DE511 /* FESlidingInterfaceMP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingInterfaceMP.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D812142ABA0008DE511 /* FESupplyBinding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESupplyBinding.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D822142ABA0008DE511 /* FEReaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReaction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D832142ABA0008DE511 /* FEReactionRateConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEReactionRateConst.cpp; sourceTree = \"<group>\"; };\n\t\tD5322D842142ABA0008DE511 /* FEChemicalReaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEChemicalReaction.cpp; sourceTree = \"<group>\"; };\n\t\tD5743D3D247EDABE006B4069 /* FEPermExpIso.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPermExpIso.cpp; sourceTree = \"<group>\"; };\n\t\tD5743D3E247EDABE006B4069 /* FEPermExpIso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPermExpIso.h; sourceTree = \"<group>\"; };\n\t\tD5C5B79A239F0BA400D29EE0 /* FESBMPointSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESBMPointSource.h; sourceTree = \"<group>\"; };\n\t\tD5C5B79B239F0BA400D29EE0 /* FESBMPointSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESBMPointSource.cpp; sourceTree = \"<group>\"; };\n\t\tD5DA959623D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESlidingInterfaceBiphasicMixed.cpp; sourceTree = \"<group>\"; };\n\t\tD5DA959723D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESlidingInterfaceBiphasicMixed.h; sourceTree = \"<group>\"; };\n\t\tD5E85D9522021E4B00F5DF83 /* febiomix_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febiomix_api.h; sourceTree = \"<group>\"; };\n\t\tD5E85D9622021E4B00F5DF83 /* FEPorousNeoHookean.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPorousNeoHookean.cpp; sourceTree = \"<group>\"; };\n\t\tD5E85D9722021E4C00F5DF83 /* FEPorousNeoHookean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPorousNeoHookean.h; sourceTree = \"<group>\"; };\n\t\tD5F50DC623E1205200B21A30 /* FESolutePointSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolutePointSource.h; sourceTree = \"<group>\"; };\n\t\tD5F50DC723E1205300B21A30 /* FESolutePointSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolutePointSource.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5322CCC2142AB8D008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5322CC62142AB8D008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322CDD2142ABA0008DE511 /* FEBioMix */,\n\t\t\t\tD5322CD02142AB8D008DE511 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322CD02142AB8D008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322CCF2142AB8D008DE511 /* libFEBioMix.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322CDD2142ABA0008DE511 /* FEBioMix */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322D002142ABA0008DE511 /* FEActiveConstantSupply.cpp */,\n\t\t\t\tD5322D242142ABA0008DE511 /* FEActiveConstantSupply.h */,\n\t\t\t\tD5322D702142ABA0008DE511 /* FEActiveMomentumSupply.cpp */,\n\t\t\t\tD5322CEA2142ABA0008DE511 /* FEActiveMomentumSupply.h */,\n\t\t\t\tD5E85D9522021E4B00F5DF83 /* febiomix_api.h */,\n\t\t\t\tD5322D422142ABA0008DE511 /* FEBioMix.cpp */,\n\t\t\t\tD5322D672142ABA0008DE511 /* FEBioMix.h */,\n\t\t\t\tD5322D462142ABA0008DE511 /* FEBioMixData.cpp */,\n\t\t\t\tD5322D552142ABA0008DE511 /* FEBioMixData.h */,\n\t\t\t\tD5322D5F2142ABA0008DE511 /* FEBioMixPlot.cpp */,\n\t\t\t\tD5322D782142ABA0008DE511 /* FEBioMixPlot.h */,\n\t\t\t\tD5322D502142ABA0008DE511 /* FEBiphasic.cpp */,\n\t\t\t\tD5322D742142ABA0008DE511 /* FEBiphasic.h */,\n\t\t\t\tD5322D072142ABA0008DE511 /* FEBiphasicContactSurface.cpp */,\n\t\t\t\tD5322D662142ABA0008DE511 /* FEBiphasicContactSurface.h */,\n\t\t\t\tD5322D1E2142ABA0008DE511 /* FEBiphasicDomain.cpp */,\n\t\t\t\tD5322CDE2142ABA0008DE511 /* FEBiphasicDomain.h */,\n\t\t\t\tD5322D1F2142ABA0008DE511 /* FEBiphasicShellDomain.cpp */,\n\t\t\t\tD5322D212142ABA0008DE511 /* FEBiphasicShellDomain.h */,\n\t\t\t\tD5322D4F2142ABA0008DE511 /* FEBiphasicSolidDomain.cpp */,\n\t\t\t\tD5322D0E2142ABA0008DE511 /* FEBiphasicSolidDomain.h */,\n\t\t\t\tD5322D322142ABA0008DE511 /* FEBiphasicSolute.cpp */,\n\t\t\t\tD5322D192142ABA0008DE511 /* FEBiphasicSolute.h */,\n\t\t\t\tD5322D342142ABA0008DE511 /* FEBiphasicSoluteDomain.cpp */,\n\t\t\t\tD5322D152142ABA0008DE511 /* FEBiphasicSoluteDomain.h */,\n\t\t\t\tD5322D4A2142ABA0008DE511 /* FEBiphasicSoluteShellDomain.cpp */,\n\t\t\t\tD5322D682142ABA0008DE511 /* FEBiphasicSoluteShellDomain.h */,\n\t\t\t\tD5322D2A2142ABA0008DE511 /* FEBiphasicSoluteSolidDomain.cpp */,\n\t\t\t\tD5322D7C2142ABA0008DE511 /* FEBiphasicSoluteSolidDomain.h */,\n\t\t\t\tD5322D532142ABA0008DE511 /* FEBiphasicSoluteSolver.cpp */,\n\t\t\t\tD5322D222142ABA0008DE511 /* FEBiphasicSoluteSolver.h */,\n\t\t\t\tD5322D182142ABA0008DE511 /* FEBiphasicSolver.cpp */,\n\t\t\t\tD5322CFC2142ABA0008DE511 /* FEBiphasicSolver.h */,\n\t\t\t\tD5322D372142ABA0008DE511 /* FECarterHayes.cpp */,\n\t\t\t\tD5322D292142ABA0008DE511 /* FECarterHayes.h */,\n\t\t\t\tD5322D842142ABA0008DE511 /* FEChemicalReaction.cpp */,\n\t\t\t\tD5322D0A2142ABA0008DE511 /* FEChemicalReaction.h */,\n\t\t\t\tD5322D7B2142ABA0008DE511 /* FEConcentrationIndependentReaction.cpp */,\n\t\t\t\tD5322CE42142ABA0008DE511 /* FEConcentrationIndependentReaction.h */,\n\t\t\t\tD5322D162142ABA0008DE511 /* FEDiffAlbroIso.cpp */,\n\t\t\t\tD5322D6D2142ABA0008DE511 /* FEDiffAlbroIso.h */,\n\t\t\t\tD5322D042142ABA0008DE511 /* FEDiffConstIso.cpp */,\n\t\t\t\tD5322D542142ABA0008DE511 /* FEDiffConstIso.h */,\n\t\t\t\tD5322D712142ABA0008DE511 /* FEDiffConstOrtho.cpp */,\n\t\t\t\tD5322D572142ABA0008DE511 /* FEDiffConstOrtho.h */,\n\t\t\t\tD5322D362142ABA0008DE511 /* FEDiffRefIso.cpp */,\n\t\t\t\tD5322D732142ABA0008DE511 /* FEDiffRefIso.h */,\n\t\t\t\tD5322CE72142ABA0008DE511 /* FEFiberExpPowSBM.cpp */,\n\t\t\t\tD5322D602142ABA0008DE511 /* FEFiberExpPowSBM.h */,\n\t\t\t\tD5322D562142ABA0008DE511 /* FEFiberPowLinearSBM.cpp */,\n\t\t\t\tD5322D3A2142ABA0008DE511 /* FEFiberPowLinearSBM.h */,\n\t\t\t\tD5322CEC2142ABA0008DE511 /* FEFluidFlux.cpp */,\n\t\t\t\tD5322CFB2142ABA0008DE511 /* FEFluidFlux.h */,\n\t\t\t\tD5322D052142ABA0008DE511 /* FEHydraulicPermeability.cpp */,\n\t\t\t\tD5322D6B2142ABA0008DE511 /* FEHydraulicPermeability.h */,\n\t\t\t\tD5322D252142ABA0008DE511 /* FEMassActionForward.cpp */,\n\t\t\t\tD5322D492142ABA0008DE511 /* FEMassActionForward.h */,\n\t\t\t\tD5322D012142ABA0008DE511 /* FEMassActionForwardEffective.cpp */,\n\t\t\t\tD5322CEE2142ABA0008DE511 /* FEMassActionForwardEffective.h */,\n\t\t\t\tD5322D0D2142ABA0008DE511 /* FEMassActionReversible.cpp */,\n\t\t\t\tD5322D1D2142ABA0008DE511 /* FEMassActionReversible.h */,\n\t\t\t\tD5322D0F2142ABA0008DE511 /* FEMassActionReversibleEffective.cpp */,\n\t\t\t\tD5322CE32142ABA0008DE511 /* FEMassActionReversibleEffective.h */,\n\t\t\t\tD507640C2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.cpp */,\n\t\t\t\tD507640D2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.h */,\n\t\t\t\tD5322CFD2142ABA0008DE511 /* FEMembraneMassActionForward.cpp */,\n\t\t\t\tD5322D302142ABA0008DE511 /* FEMembraneMassActionForward.h */,\n\t\t\t\tD5322CF42142ABA0008DE511 /* FEMembraneMassActionReversible.cpp */,\n\t\t\t\tD5322D2D2142ABA0008DE511 /* FEMembraneMassActionReversible.h */,\n\t\t\t\tD5322D1B2142ABA0008DE511 /* FEMembraneReaction.cpp */,\n\t\t\t\tD5322D2C2142ABA0008DE511 /* FEMembraneReaction.h */,\n\t\t\t\tD5322D472142ABA0008DE511 /* FEMembraneReactionRateConst.cpp */,\n\t\t\t\tD5322D762142ABA0008DE511 /* FEMembraneReactionRateConst.h */,\n\t\t\t\tD5322D352142ABA0008DE511 /* FEMembraneReactionRateIonChannel.cpp */,\n\t\t\t\tD5322D5B2142ABA0008DE511 /* FEMembraneReactionRateIonChannel.h */,\n\t\t\t\tD5322D3B2142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.cpp */,\n\t\t\t\tD5322CED2142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.h */,\n\t\t\t\tD5322D5D2142ABA0008DE511 /* FEMichaelisMenten.cpp */,\n\t\t\t\tD5322CFF2142ABA0008DE511 /* FEMichaelisMenten.h */,\n\t\t\t\tD5322D202142ABA0008DE511 /* FEMixDomainFactory.cpp */,\n\t\t\t\tD5322D6A2142ABA0008DE511 /* FEMixDomainFactory.h */,\n\t\t\t\tD5322CE02142ABA0008DE511 /* FEMultiphasic.cpp */,\n\t\t\t\tD5322D792142ABA0008DE511 /* FEMultiphasic.h */,\n\t\t\t\tD5322D1C2142ABA0008DE511 /* FEMultiphasicDomain.cpp */,\n\t\t\t\tD5322CFA2142ABA0008DE511 /* FEMultiphasicDomain.h */,\n\t\t\t\tD5322D652142ABA0008DE511 /* FEMultiphasicMultigeneration.cpp */,\n\t\t\t\tD5322CEB2142ABA0008DE511 /* FEMultiphasicMultigeneration.h */,\n\t\t\t\tD5322D7F2142ABA0008DE511 /* FEMultiphasicShellDomain.cpp */,\n\t\t\t\tD5322D272142ABA0008DE511 /* FEMultiphasicShellDomain.h */,\n\t\t\t\tD5322CF12142ABA0008DE511 /* FEMultiphasicSolidDomain.cpp */,\n\t\t\t\tD5322D062142ABA0008DE511 /* FEMultiphasicSolidDomain.h */,\n\t\t\t\tD5322D132142ABA0008DE511 /* FEMultiphasicSolver.cpp */,\n\t\t\t\tD5322CDF2142ABA0008DE511 /* FEMultiphasicSolver.h */,\n\t\t\t\tD5322D632142ABA0008DE511 /* FEMultiphasicStandard.cpp */,\n\t\t\t\tD5322CE22142ABA0008DE511 /* FEMultiphasicStandard.h */,\n\t\t\t\tD5322CF82142ABA0008DE511 /* FEOsmCoefConst.cpp */,\n\t\t\t\tD5322D4C2142ABA0008DE511 /* FEOsmCoefConst.h */,\n\t\t\t\tD5322D3D2142ABA0008DE511 /* FEOsmCoefManning.cpp */,\n\t\t\t\tD5322D412142ABA0008DE511 /* FEOsmCoefManning.h */,\n\t\t\t\tD5322D6F2142ABA0008DE511 /* FEOsmoticCoefficient.cpp */,\n\t\t\t\tD5322D1A2142ABA0008DE511 /* FEOsmoticCoefficient.h */,\n\t\t\t\tD5322D7E2142ABA0008DE511 /* FEPermConstIso.cpp */,\n\t\t\t\tD5322D2B2142ABA0008DE511 /* FEPermConstIso.h */,\n\t\t\t\tD5743D3D247EDABE006B4069 /* FEPermExpIso.cpp */,\n\t\t\t\tD5743D3E247EDABE006B4069 /* FEPermExpIso.h */,\n\t\t\t\tD5322D2F2142ABA0008DE511 /* FEPermHolmesMow.cpp */,\n\t\t\t\tD5322CFE2142ABA0008DE511 /* FEPermHolmesMow.h */,\n\t\t\t\tD5322D4E2142ABA0008DE511 /* FEPermRefIso.cpp */,\n\t\t\t\tD5322D4B2142ABA0008DE511 /* FEPermRefIso.h */,\n\t\t\t\tD5322D5A2142ABA0008DE511 /* FEPermRefOrtho.cpp */,\n\t\t\t\tD5322D082142ABA0008DE511 /* FEPermRefOrtho.h */,\n\t\t\t\tD5322D2E2142ABA0008DE511 /* FEPermRefTransIso.cpp */,\n\t\t\t\tD5322CE82142ABA0008DE511 /* FEPermRefTransIso.h */,\n\t\t\t\tD5322D4D2142ABA0008DE511 /* FEPoroTraction.cpp */,\n\t\t\t\tD5322CF52142ABA0008DE511 /* FEPoroTraction.h */,\n\t\t\t\tD5E85D9622021E4B00F5DF83 /* FEPorousNeoHookean.cpp */,\n\t\t\t\tD5E85D9722021E4C00F5DF83 /* FEPorousNeoHookean.h */,\n\t\t\t\tD5322D7A2142ABA0008DE511 /* FEPressureStabilization.cpp */,\n\t\t\t\tD5322D142142ABA0008DE511 /* FEPressureStabilization.h */,\n\t\t\t\tD5322D822142ABA0008DE511 /* FEReaction.cpp */,\n\t\t\t\tD5322D752142ABA0008DE511 /* FEReaction.h */,\n\t\t\t\tD5322D832142ABA0008DE511 /* FEReactionRateConst.cpp */,\n\t\t\t\tD5322D622142ABA0008DE511 /* FEReactionRateConst.h */,\n\t\t\t\tD5322D442142ABA0008DE511 /* FEReactionRateExpSED.cpp */,\n\t\t\t\tD5322D032142ABA0008DE511 /* FEReactionRateExpSED.h */,\n\t\t\t\tD5322CE62142ABA0008DE511 /* FEReactionRateHuiskes.cpp */,\n\t\t\t\tD5322D402142ABA0008DE511 /* FEReactionRateHuiskes.h */,\n\t\t\t\tD5322D5C2142ABA0008DE511 /* FEReactionRateNims.cpp */,\n\t\t\t\tD5322D312142ABA0008DE511 /* FEReactionRateNims.h */,\n\t\t\t\tD5C5B79B239F0BA400D29EE0 /* FESBMPointSource.cpp */,\n\t\t\t\tD5C5B79A239F0BA400D29EE0 /* FESBMPointSource.h */,\n\t\t\t\tD5322CE92142ABA0008DE511 /* FESFDSBM.cpp */,\n\t\t\t\tD5322CF92142ABA0008DE511 /* FESFDSBM.h */,\n\t\t\t\tD5322D582142ABA0008DE511 /* FESlidingInterface2.cpp */,\n\t\t\t\tD5322D722142ABA0008DE511 /* FESlidingInterface2.h */,\n\t\t\t\tD5322D612142ABA0008DE511 /* FESlidingInterface3.cpp */,\n\t\t\t\tD5322CF02142ABA0008DE511 /* FESlidingInterface3.h */,\n\t\t\t\tD5322D592142ABA0008DE511 /* FESlidingInterfaceBiphasic.cpp */,\n\t\t\t\tD5322CE52142ABA0008DE511 /* FESlidingInterfaceBiphasic.h */,\n\t\t\t\tD5DA959623D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.cpp */,\n\t\t\t\tD5DA959723D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.h */,\n\t\t\t\tD5322D802142ABA0008DE511 /* FESlidingInterfaceMP.cpp */,\n\t\t\t\tD5322D0B2142ABA0008DE511 /* FESlidingInterfaceMP.h */,\n\t\t\t\tD5322CF32142ABA0008DE511 /* FESolubConst.cpp */,\n\t\t\t\tD5322D262142ABA0008DE511 /* FESolubConst.h */,\n\t\t\t\tD5322D522142ABA0008DE511 /* FESolubManning.cpp */,\n\t\t\t\tD5322D3E2142ABA0008DE511 /* FESolubManning.h */,\n\t\t\t\tD5322D282142ABA0008DE511 /* FESolute.cpp */,\n\t\t\t\tD5322D5E2142ABA0008DE511 /* FESolute.h */,\n\t\t\t\tD5322D172142ABA0008DE511 /* FESoluteFlux.cpp */,\n\t\t\t\tD5322D3F2142ABA0008DE511 /* FESoluteFlux.h */,\n\t\t\t\tD5322D092142ABA0008DE511 /* FESoluteInterface.cpp */,\n\t\t\t\tD5322D3C2142ABA0008DE511 /* FESoluteInterface.h */,\n\t\t\t\tD5F50DC723E1205300B21A30 /* FESolutePointSource.cpp */,\n\t\t\t\tD5F50DC623E1205200B21A30 /* FESolutePointSource.h */,\n\t\t\t\tD5322D6E2142ABA0008DE511 /* FESolutesMaterialPoint.cpp */,\n\t\t\t\tD5322D0C2142ABA0008DE511 /* FESolutesMaterialPoint.h */,\n\t\t\t\tD5322D232142ABA0008DE511 /* FESolventSupply.cpp */,\n\t\t\t\tD5322CF62142ABA0008DE511 /* FESolventSupply.h */,\n\t\t\t\tD5322D512142ABA0008DE511 /* FESolventSupplyStarling.cpp */,\n\t\t\t\tD5322D692142ABA0008DE511 /* FESolventSupplyStarling.h */,\n\t\t\t\tD5322D812142ABA0008DE511 /* FESupplyBinding.cpp */,\n\t\t\t\tD5322D382142ABA0008DE511 /* FESupplyBinding.h */,\n\t\t\t\tD5322D482142ABA0008DE511 /* FESupplyConst.cpp */,\n\t\t\t\tD5322D432142ABA0008DE511 /* FESupplyConst.h */,\n\t\t\t\tD5322D7D2142ABA0008DE511 /* FESupplyMichaelisMenten.cpp */,\n\t\t\t\tD5322CE12142ABA0008DE511 /* FESupplyMichaelisMenten.h */,\n\t\t\t\tD5322D772142ABA0008DE511 /* FESupplySynthesisBinding.cpp */,\n\t\t\t\tD5322D642142ABA0008DE511 /* FESupplySynthesisBinding.h */,\n\t\t\t\tD5322D452142ABA0008DE511 /* FETiedBiphasicInterface.cpp */,\n\t\t\t\tD5322D392142ABA0008DE511 /* FETiedBiphasicInterface.h */,\n\t\t\t\tD5322D112142ABA0008DE511 /* FETiedMultiphasicInterface.cpp */,\n\t\t\t\tD5322CEF2142ABA0008DE511 /* FETiedMultiphasicInterface.h */,\n\t\t\t\tD5322D022142ABA0008DE511 /* FETriphasic.cpp */,\n\t\t\t\tD5322D6C2142ABA0008DE511 /* FETriphasic.h */,\n\t\t\t\tD5322CF72142ABA0008DE511 /* FETriphasicDomain.cpp */,\n\t\t\t\tD5322D122142ABA0008DE511 /* FETriphasicDomain.h */,\n\t\t\t\tD5322CF22142ABA0008DE511 /* stdafx.h */,\n\t\t\t);\n\t\t\tname = FEBioMix;\n\t\t\tpath = ../../FEBioMix;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5322CCD2142AB8D008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322E092142ABA1008DE511 /* FEReactionRateConst.h in Headers */,\n\t\t\t\tD5322D8F2142ABA0008DE511 /* FEPermRefTransIso.h in Headers */,\n\t\t\t\tD5322D892142ABA0008DE511 /* FEMultiphasicStandard.h in Headers */,\n\t\t\t\tD5322D912142ABA0008DE511 /* FEActiveMomentumSupply.h in Headers */,\n\t\t\t\tD5322DE52142ABA1008DE511 /* FESolubManning.h in Headers */,\n\t\t\t\tD5322D922142ABA0008DE511 /* FEMultiphasicMultigeneration.h in Headers */,\n\t\t\t\tD5322DCB2142ABA1008DE511 /* FEActiveConstantSupply.h in Headers */,\n\t\t\t\tD5322E0E2142ABA1008DE511 /* FEBioMix.h in Headers */,\n\t\t\t\tD5322DCD2142ABA1008DE511 /* FESolubConst.h in Headers */,\n\t\t\t\tD5322E0D2142ABA1008DE511 /* FEBiphasicContactSurface.h in Headers */,\n\t\t\t\tD5322DCE2142ABA1008DE511 /* FEMultiphasicShellDomain.h in Headers */,\n\t\t\t\tD5322D962142ABA0008DE511 /* FETiedMultiphasicInterface.h in Headers */,\n\t\t\t\tD5322E122142ABA1008DE511 /* FEHydraulicPermeability.h in Headers */,\n\t\t\t\tD5322DD32142ABA1008DE511 /* FEMembraneReaction.h in Headers */,\n\t\t\t\tD5743D40247EDABF006B4069 /* FEPermExpIso.h in Headers */,\n\t\t\t\tD5322E1F2142ABA1008DE511 /* FEBioMixPlot.h in Headers */,\n\t\t\t\tD5322DB12142ABA1008DE511 /* FEChemicalReaction.h in Headers */,\n\t\t\t\tD5322D9C2142ABA0008DE511 /* FEPoroTraction.h in Headers */,\n\t\t\t\tD5322DDF2142ABA1008DE511 /* FESupplyBinding.h in Headers */,\n\t\t\t\tD5322D8B2142ABA0008DE511 /* FEConcentrationIndependentReaction.h in Headers */,\n\t\t\t\tD5322D972142ABA0008DE511 /* FESlidingInterface3.h in Headers */,\n\t\t\t\tD5322E192142ABA1008DE511 /* FESlidingInterface2.h in Headers */,\n\t\t\t\tD5322DE82142ABA1008DE511 /* FEOsmCoefManning.h in Headers */,\n\t\t\t\tD5322DB32142ABA1008DE511 /* FESolutesMaterialPoint.h in Headers */,\n\t\t\t\tD5322D8A2142ABA0008DE511 /* FEMassActionReversibleEffective.h in Headers */,\n\t\t\t\tD5322E232142ABA1008DE511 /* FEBiphasicSoluteSolidDomain.h in Headers */,\n\t\t\t\tD5322DAA2142ABA1008DE511 /* FEReactionRateExpSED.h in Headers */,\n\t\t\t\tD5E85D9A22021E4C00F5DF83 /* FEPorousNeoHookean.h in Headers */,\n\t\t\t\tD5322DE72142ABA1008DE511 /* FEReactionRateHuiskes.h in Headers */,\n\t\t\t\tD5322E1D2142ABA1008DE511 /* FEMembraneReactionRateConst.h in Headers */,\n\t\t\t\tD5322E052142ABA1008DE511 /* FESolute.h in Headers */,\n\t\t\t\tD5322DC02142ABA1008DE511 /* FEBiphasicSolute.h in Headers */,\n\t\t\t\tD5322DA22142ABA0008DE511 /* FEFluidFlux.h in Headers */,\n\t\t\t\tD5322DFB2142ABA1008DE511 /* FEDiffConstIso.h in Headers */,\n\t\t\t\tD5322DC82142ABA1008DE511 /* FEBiphasicShellDomain.h in Headers */,\n\t\t\t\tD5322DFC2142ABA1008DE511 /* FEBioMixData.h in Headers */,\n\t\t\t\tD5322E102142ABA1008DE511 /* FESolventSupplyStarling.h in Headers */,\n\t\t\t\tD5322DE62142ABA1008DE511 /* FESoluteFlux.h in Headers */,\n\t\t\t\tD5322DE32142ABA1008DE511 /* FESoluteInterface.h in Headers */,\n\t\t\t\tD5322D9D2142ABA0008DE511 /* FESolventSupply.h in Headers */,\n\t\t\t\tD5322DEA2142ABA1008DE511 /* FESupplyConst.h in Headers */,\n\t\t\t\tD5322DA32142ABA0008DE511 /* FEBiphasicSolver.h in Headers */,\n\t\t\t\tD5322DD42142ABA1008DE511 /* FEMembraneMassActionReversible.h in Headers */,\n\t\t\t\tD5322DA02142ABA0008DE511 /* FESFDSBM.h in Headers */,\n\t\t\t\tD5322DE12142ABA1008DE511 /* FEFiberPowLinearSBM.h in Headers */,\n\t\t\t\tD5322DD02142ABA1008DE511 /* FECarterHayes.h in Headers */,\n\t\t\t\tD5322DF02142ABA1008DE511 /* FEMassActionForward.h in Headers */,\n\t\t\t\tD5322E132142ABA1008DE511 /* FETriphasic.h in Headers */,\n\t\t\t\tD5322DBB2142ABA1008DE511 /* FEPressureStabilization.h in Headers */,\n\t\t\t\tD5322D862142ABA0008DE511 /* FEMultiphasicSolver.h in Headers */,\n\t\t\t\tD5322E1A2142ABA1008DE511 /* FEDiffRefIso.h in Headers */,\n\t\t\t\tD5322DA12142ABA0008DE511 /* FEMultiphasicDomain.h in Headers */,\n\t\t\t\tD5322DC42142ABA1008DE511 /* FEMassActionReversible.h in Headers */,\n\t\t\t\tD5322DD82142ABA1008DE511 /* FEReactionRateNims.h in Headers */,\n\t\t\t\tD5322E202142ABA1008DE511 /* FEMultiphasic.h in Headers */,\n\t\t\t\tD5322E1C2142ABA1008DE511 /* FEReaction.h in Headers */,\n\t\t\t\tD5322DD72142ABA1008DE511 /* FEMembraneMassActionForward.h in Headers */,\n\t\t\t\tD5322E112142ABA1008DE511 /* FEMixDomainFactory.h in Headers */,\n\t\t\t\tD5322DFE2142ABA1008DE511 /* FEDiffConstOrtho.h in Headers */,\n\t\t\t\tD5322E0B2142ABA1008DE511 /* FESupplySynthesisBinding.h in Headers */,\n\t\t\t\tD5322E072142ABA1008DE511 /* FEFiberExpPowSBM.h in Headers */,\n\t\t\t\tD5DA959923D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.h in Headers */,\n\t\t\t\tD5322DB92142ABA1008DE511 /* FETriphasicDomain.h in Headers */,\n\t\t\t\tD5322DAF2142ABA1008DE511 /* FEPermRefOrtho.h in Headers */,\n\t\t\t\tD5322D882142ABA0008DE511 /* FESupplyMichaelisMenten.h in Headers */,\n\t\t\t\tD5E85D9822021E4C00F5DF83 /* febiomix_api.h in Headers */,\n\t\t\t\tD5322D8C2142ABA0008DE511 /* FESlidingInterfaceBiphasic.h in Headers */,\n\t\t\t\tD507640F2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.h in Headers */,\n\t\t\t\tD5F50DC823E1205300B21A30 /* FESolutePointSource.h in Headers */,\n\t\t\t\tD5322D852142ABA0008DE511 /* FEBiphasicDomain.h in Headers */,\n\t\t\t\tD5322D992142ABA0008DE511 /* stdafx.h in Headers */,\n\t\t\t\tD5C5B79C239F0BA400D29EE0 /* FESBMPointSource.h in Headers */,\n\t\t\t\tD5322DA62142ABA1008DE511 /* FEMichaelisMenten.h in Headers */,\n\t\t\t\tD5322E022142ABA1008DE511 /* FEMembraneReactionRateIonChannel.h in Headers */,\n\t\t\t\tD5322DC92142ABA1008DE511 /* FEBiphasicSoluteSolver.h in Headers */,\n\t\t\t\tD5322DF32142ABA1008DE511 /* FEOsmCoefConst.h in Headers */,\n\t\t\t\tD5322DD22142ABA1008DE511 /* FEPermConstIso.h in Headers */,\n\t\t\t\tD5322DB52142ABA1008DE511 /* FEBiphasicSolidDomain.h in Headers */,\n\t\t\t\tD5322E142142ABA1008DE511 /* FEDiffAlbroIso.h in Headers */,\n\t\t\t\tD5322E1B2142ABA1008DE511 /* FEBiphasic.h in Headers */,\n\t\t\t\tD5322D942142ABA0008DE511 /* FEMembraneReactionRateVoltageGated.h in Headers */,\n\t\t\t\tD5322DAD2142ABA1008DE511 /* FEMultiphasicSolidDomain.h in Headers */,\n\t\t\t\tD5322DE02142ABA1008DE511 /* FETiedBiphasicInterface.h in Headers */,\n\t\t\t\tD5322DF22142ABA1008DE511 /* FEPermRefIso.h in Headers */,\n\t\t\t\tD5322DBC2142ABA1008DE511 /* FEBiphasicSoluteDomain.h in Headers */,\n\t\t\t\tD5322E0F2142ABA1008DE511 /* FEBiphasicSoluteShellDomain.h in Headers */,\n\t\t\t\tD5322DC12142ABA1008DE511 /* FEOsmoticCoefficient.h in Headers */,\n\t\t\t\tD5322D952142ABA0008DE511 /* FEMassActionForwardEffective.h in Headers */,\n\t\t\t\tD5322DA52142ABA1008DE511 /* FEPermHolmesMow.h in Headers */,\n\t\t\t\tD5322DB22142ABA1008DE511 /* FESlidingInterfaceMP.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5322CCE2142AB8D008DE511 /* FEBioMix */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5322CDA2142AB8D008DE511 /* Build configuration list for PBXNativeTarget \"FEBioMix\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5322CCB2142AB8D008DE511 /* Sources */,\n\t\t\t\tD5322CCC2142AB8D008DE511 /* Frameworks */,\n\t\t\t\tD5322CCD2142AB8D008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioMix;\n\t\t\tproductName = FEBioMix;\n\t\t\tproductReference = D5322CCF2142AB8D008DE511 /* libFEBioMix.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5322CC72142AB8D008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5322CCE2142AB8D008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5322CCA2142AB8D008DE511 /* Build configuration list for PBXProject \"FEBioMix\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5322CC62142AB8D008DE511;\n\t\t\tproductRefGroup = D5322CD02142AB8D008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5322CCE2142AB8D008DE511 /* FEBioMix */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5322CCB2142AB8D008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322DBF2142ABA1008DE511 /* FEBiphasicSolver.cpp in Sources */,\n\t\t\t\tD5322D9A2142ABA0008DE511 /* FESolubConst.cpp in Sources */,\n\t\t\t\tD5322DEE2142ABA1008DE511 /* FEMembraneReactionRateConst.cpp in Sources */,\n\t\t\t\tD5322DDE2142ABA1008DE511 /* FECarterHayes.cpp in Sources */,\n\t\t\t\tD5322DCF2142ABA1008DE511 /* FESolute.cpp in Sources */,\n\t\t\t\tD5322DA42142ABA0008DE511 /* FEMembraneMassActionForward.cpp in Sources */,\n\t\t\t\tD5322D8D2142ABA0008DE511 /* FEReactionRateHuiskes.cpp in Sources */,\n\t\t\t\tD5322E042142ABA1008DE511 /* FEMichaelisMenten.cpp in Sources */,\n\t\t\t\tD5322DB82142ABA1008DE511 /* FETiedMultiphasicInterface.cpp in Sources */,\n\t\t\t\tD5322E272142ABA1008DE511 /* FESlidingInterfaceMP.cpp in Sources */,\n\t\t\t\tD5322E082142ABA1008DE511 /* FESlidingInterface3.cpp in Sources */,\n\t\t\t\tD5322DAC2142ABA1008DE511 /* FEHydraulicPermeability.cpp in Sources */,\n\t\t\t\tD5C5B79D239F0BA400D29EE0 /* FESBMPointSource.cpp in Sources */,\n\t\t\t\tD5322DD12142ABA1008DE511 /* FEBiphasicSoluteSolidDomain.cpp in Sources */,\n\t\t\t\tD5E85D9922021E4C00F5DF83 /* FEPorousNeoHookean.cpp in Sources */,\n\t\t\t\tD5322E182142ABA1008DE511 /* FEDiffConstOrtho.cpp in Sources */,\n\t\t\t\tD5322E0C2142ABA1008DE511 /* FEMultiphasicMultigeneration.cpp in Sources */,\n\t\t\t\tD5322DBA2142ABA1008DE511 /* FEMultiphasicSolver.cpp in Sources */,\n\t\t\t\tD5322D9B2142ABA0008DE511 /* FEMembraneMassActionReversible.cpp in Sources */,\n\t\t\t\tD5322DFA2142ABA1008DE511 /* FEBiphasicSoluteSolver.cpp in Sources */,\n\t\t\t\tD5322DF72142ABA1008DE511 /* FEBiphasic.cpp in Sources */,\n\t\t\t\tD5322DEC2142ABA1008DE511 /* FETiedBiphasicInterface.cpp in Sources */,\n\t\t\t\tD5322E012142ABA1008DE511 /* FEPermRefOrtho.cpp in Sources */,\n\t\t\t\tD5322E172142ABA1008DE511 /* FEActiveMomentumSupply.cpp in Sources */,\n\t\t\t\tD5DA959823D353F500D38BF2 /* FESlidingInterfaceBiphasicMixed.cpp in Sources */,\n\t\t\t\tD5322DF62142ABA1008DE511 /* FEBiphasicSolidDomain.cpp in Sources */,\n\t\t\t\tD5322E292142ABA1008DE511 /* FEReaction.cpp in Sources */,\n\t\t\t\tD5322DCA2142ABA1008DE511 /* FESolventSupply.cpp in Sources */,\n\t\t\t\tD5322DB62142ABA1008DE511 /* FEMassActionReversibleEffective.cpp in Sources */,\n\t\t\t\tD5322DED2142ABA1008DE511 /* FEBioMixData.cpp in Sources */,\n\t\t\t\tD5322DE22142ABA1008DE511 /* FEMembraneReactionRateVoltageGated.cpp in Sources */,\n\t\t\t\tD5322DAE2142ABA1008DE511 /* FEBiphasicContactSurface.cpp in Sources */,\n\t\t\t\tD5322DD92142ABA1008DE511 /* FEBiphasicSolute.cpp in Sources */,\n\t\t\t\tD5322DC22142ABA1008DE511 /* FEMembraneReaction.cpp in Sources */,\n\t\t\t\tD5322DD62142ABA1008DE511 /* FEPermHolmesMow.cpp in Sources */,\n\t\t\t\tD5322DFF2142ABA1008DE511 /* FESlidingInterface2.cpp in Sources */,\n\t\t\t\tD5322DEF2142ABA1008DE511 /* FESupplyConst.cpp in Sources */,\n\t\t\t\tD5322DDB2142ABA1008DE511 /* FEBiphasicSoluteDomain.cpp in Sources */,\n\t\t\t\tD5322E002142ABA1008DE511 /* FESlidingInterfaceBiphasic.cpp in Sources */,\n\t\t\t\tD5322DF12142ABA1008DE511 /* FEBiphasicSoluteShellDomain.cpp in Sources */,\n\t\t\t\tD5322DA82142ABA1008DE511 /* FEMassActionForwardEffective.cpp in Sources */,\n\t\t\t\tD5322DC62142ABA1008DE511 /* FEBiphasicShellDomain.cpp in Sources */,\n\t\t\t\tD5322E262142ABA1008DE511 /* FEMultiphasicShellDomain.cpp in Sources */,\n\t\t\t\tD5322E162142ABA1008DE511 /* FEOsmoticCoefficient.cpp in Sources */,\n\t\t\t\tD5322D9E2142ABA0008DE511 /* FETriphasicDomain.cpp in Sources */,\n\t\t\t\tD5322DB02142ABA1008DE511 /* FESoluteInterface.cpp in Sources */,\n\t\t\t\tD5322E252142ABA1008DE511 /* FEPermConstIso.cpp in Sources */,\n\t\t\t\tD5322DD52142ABA1008DE511 /* FEPermRefTransIso.cpp in Sources */,\n\t\t\t\tD5322DA92142ABA1008DE511 /* FETriphasic.cpp in Sources */,\n\t\t\t\tD5322DAB2142ABA1008DE511 /* FEDiffConstIso.cpp in Sources */,\n\t\t\t\tD5322D872142ABA0008DE511 /* FEMultiphasic.cpp in Sources */,\n\t\t\t\tD5322DF82142ABA1008DE511 /* FESolventSupplyStarling.cpp in Sources */,\n\t\t\t\tD5322DE42142ABA1008DE511 /* FEOsmCoefManning.cpp in Sources */,\n\t\t\t\tD5322DFD2142ABA1008DE511 /* FEFiberPowLinearSBM.cpp in Sources */,\n\t\t\t\tD5322E062142ABA1008DE511 /* FEBioMixPlot.cpp in Sources */,\n\t\t\t\tD5F50DC923E1205300B21A30 /* FESolutePointSource.cpp in Sources */,\n\t\t\t\tD5322DBE2142ABA1008DE511 /* FESoluteFlux.cpp in Sources */,\n\t\t\t\tD5322E242142ABA1008DE511 /* FESupplyMichaelisMenten.cpp in Sources */,\n\t\t\t\tD5322E1E2142ABA1008DE511 /* FESupplySynthesisBinding.cpp in Sources */,\n\t\t\t\tD5322E282142ABA1008DE511 /* FESupplyBinding.cpp in Sources */,\n\t\t\t\tD5322D9F2142ABA0008DE511 /* FEOsmCoefConst.cpp in Sources */,\n\t\t\t\tD507640E2488113100D34EBB /* FEMatchingOsmoticCoefficientBC.cpp in Sources */,\n\t\t\t\tD5322DF92142ABA1008DE511 /* FESolubManning.cpp in Sources */,\n\t\t\t\tD5322DC72142ABA1008DE511 /* FEMixDomainFactory.cpp in Sources */,\n\t\t\t\tD5322E032142ABA1008DE511 /* FEReactionRateNims.cpp in Sources */,\n\t\t\t\tD5322DF52142ABA1008DE511 /* FEPermRefIso.cpp in Sources */,\n\t\t\t\tD5322D8E2142ABA0008DE511 /* FEFiberExpPowSBM.cpp in Sources */,\n\t\t\t\tD5743D3F247EDABF006B4069 /* FEPermExpIso.cpp in Sources */,\n\t\t\t\tD5322DBD2142ABA1008DE511 /* FEDiffAlbroIso.cpp in Sources */,\n\t\t\t\tD5322E0A2142ABA1008DE511 /* FEMultiphasicStandard.cpp in Sources */,\n\t\t\t\tD5322DDC2142ABA1008DE511 /* FEMembraneReactionRateIonChannel.cpp in Sources */,\n\t\t\t\tD5322DDD2142ABA1008DE511 /* FEDiffRefIso.cpp in Sources */,\n\t\t\t\tD5322E2B2142ABA1008DE511 /* FEChemicalReaction.cpp in Sources */,\n\t\t\t\tD5322E222142ABA1008DE511 /* FEConcentrationIndependentReaction.cpp in Sources */,\n\t\t\t\tD5322D902142ABA0008DE511 /* FESFDSBM.cpp in Sources */,\n\t\t\t\tD5322DA72142ABA1008DE511 /* FEActiveConstantSupply.cpp in Sources */,\n\t\t\t\tD5322DCC2142ABA1008DE511 /* FEMassActionForward.cpp in Sources */,\n\t\t\t\tD5322E2A2142ABA1008DE511 /* FEReactionRateConst.cpp in Sources */,\n\t\t\t\tD5322DC52142ABA1008DE511 /* FEBiphasicDomain.cpp in Sources */,\n\t\t\t\tD5322DEB2142ABA1008DE511 /* FEReactionRateExpSED.cpp in Sources */,\n\t\t\t\tD5322DE92142ABA1008DE511 /* FEBioMix.cpp in Sources */,\n\t\t\t\tD5322E212142ABA1008DE511 /* FEPressureStabilization.cpp in Sources */,\n\t\t\t\tD5322DB42142ABA1008DE511 /* FEMassActionReversible.cpp in Sources */,\n\t\t\t\tD5322DC32142ABA1008DE511 /* FEMultiphasicDomain.cpp in Sources */,\n\t\t\t\tD5322D982142ABA0008DE511 /* FEMultiphasicSolidDomain.cpp in Sources */,\n\t\t\t\tD5322E152142ABA1008DE511 /* FESolutesMaterialPoint.cpp in Sources */,\n\t\t\t\tD5322DF42142ABA1008DE511 /* FEPoroTraction.cpp in Sources */,\n\t\t\t\tD5322D932142ABA0008DE511 /* FEFluidFlux.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5322CD82142AB8D008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322CD92142AB8D008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5322CDB2142AB8D008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322CDC2142AB8D008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5322CCA2142AB8D008DE511 /* Build configuration list for PBXProject \"FEBioMix\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322CD82142AB8D008DE511 /* Debug */,\n\t\t\t\tD5322CD92142AB8D008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5322CDA2142AB8D008DE511 /* Build configuration list for PBXNativeTarget \"FEBioMix\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322CDB2142AB8D008DE511 /* Debug */,\n\t\t\t\tD5322CDC2142AB8D008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5322CC72142AB8D008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioMix/FEBioMix.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioMix.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioMix/FEBioMix.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioOpt/FEBioOpt.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD5322CAC2142AACE008DE511 /* FEConstrainedLMOptimizeMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C952142AACE008DE511 /* FEConstrainedLMOptimizeMethod.h */; };\n\t\tD5322CAD2142AACE008DE511 /* FEDataSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C962142AACE008DE511 /* FEDataSource.cpp */; };\n\t\tD5322CAE2142AACE008DE511 /* FELMOptimizeMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C972142AACE008DE511 /* FELMOptimizeMethod.h */; };\n\t\tD5322CAF2142AACE008DE511 /* FEObjectiveFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C982142AACE008DE511 /* FEObjectiveFunction.h */; };\n\t\tD5322CB02142AACE008DE511 /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C992142AACE008DE511 /* stdafx.h */; };\n\t\tD5322CB12142AACE008DE511 /* FEPowellOptimizeMethod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C9A2142AACE008DE511 /* FEPowellOptimizeMethod.cpp */; };\n\t\tD5322CB22142AACE008DE511 /* FEOptimize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C9B2142AACE008DE511 /* FEOptimize.cpp */; };\n\t\tD5322CB32142AACE008DE511 /* FEOptimizeMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C9C2142AACE008DE511 /* FEOptimizeMethod.h */; };\n\t\tD5322CB42142AACE008DE511 /* FEBioOpt.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C9D2142AACE008DE511 /* FEBioOpt.h */; };\n\t\tD5322CB52142AACE008DE511 /* FEBioOpt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C9E2142AACE008DE511 /* FEBioOpt.cpp */; };\n\t\tD5322CB62142AACE008DE511 /* FEOptimizeData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C9F2142AACE008DE511 /* FEOptimizeData.cpp */; };\n\t\tD5322CB72142AACE008DE511 /* FEOptimizeInput.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CA02142AACE008DE511 /* FEOptimizeInput.h */; };\n\t\tD5322CB82142AACE008DE511 /* FEOptimize.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CA12142AACE008DE511 /* FEOptimize.h */; };\n\t\tD5322CB92142AACE008DE511 /* FEDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CA22142AACE008DE511 /* FEDataSource.h */; };\n\t\tD5322CBA2142AACE008DE511 /* targetver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CA32142AACE008DE511 /* targetver.h */; };\n\t\tD5322CBB2142AACE008DE511 /* FEScanOptimizeMethod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CA42142AACE008DE511 /* FEScanOptimizeMethod.cpp */; };\n\t\tD5322CBC2142AACE008DE511 /* FEConstrainedLMOptimizeMethod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CA52142AACE008DE511 /* FEConstrainedLMOptimizeMethod.cpp */; };\n\t\tD5322CBD2142AACE008DE511 /* FEOptimizeData.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CA62142AACE008DE511 /* FEOptimizeData.h */; };\n\t\tD5322CBE2142AACE008DE511 /* FEObjectiveFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CA72142AACE008DE511 /* FEObjectiveFunction.cpp */; };\n\t\tD5322CBF2142AACE008DE511 /* FELMOptimizeMethod.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CA82142AACE008DE511 /* FELMOptimizeMethod.cpp */; };\n\t\tD5322CC02142AACE008DE511 /* FEOptimizeInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322CA92142AACE008DE511 /* FEOptimizeInput.cpp */; };\n\t\tD5322CC12142AACE008DE511 /* FEPowellOptimizeMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CAA2142AACE008DE511 /* FEPowellOptimizeMethod.h */; };\n\t\tD5322CC22142AACE008DE511 /* FEScanOptimizeMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322CAB2142AACE008DE511 /* FEScanOptimizeMethod.h */; };\n\t\tD5322CC52142AB40008DE511 /* liblevmar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D5322CC42142AB40008DE511 /* liblevmar.a */; };\n\t\tD5FF2668233A64DD00C621EB /* FEParameterSweep.h in Headers */ = {isa = PBXBuildFile; fileRef = D5FF2666233A64DD00C621EB /* FEParameterSweep.h */; };\n\t\tD5FF2669233A64DD00C621EB /* FEParameterSweep.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5FF2667233A64DD00C621EB /* FEParameterSweep.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD5322C862142AABF008DE511 /* libFEBioOpt.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioOpt.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5322C952142AACE008DE511 /* FEConstrainedLMOptimizeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConstrainedLMOptimizeMethod.h; sourceTree = \"<group>\"; };\n\t\tD5322C962142AACE008DE511 /* FEDataSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDataSource.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C972142AACE008DE511 /* FELMOptimizeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELMOptimizeMethod.h; sourceTree = \"<group>\"; };\n\t\tD5322C982142AACE008DE511 /* FEObjectiveFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEObjectiveFunction.h; sourceTree = \"<group>\"; };\n\t\tD5322C992142AACE008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5322C9A2142AACE008DE511 /* FEPowellOptimizeMethod.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPowellOptimizeMethod.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C9B2142AACE008DE511 /* FEOptimize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOptimize.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C9C2142AACE008DE511 /* FEOptimizeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOptimizeMethod.h; sourceTree = \"<group>\"; };\n\t\tD5322C9D2142AACE008DE511 /* FEBioOpt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioOpt.h; sourceTree = \"<group>\"; };\n\t\tD5322C9E2142AACE008DE511 /* FEBioOpt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioOpt.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C9F2142AACE008DE511 /* FEOptimizeData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOptimizeData.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CA02142AACE008DE511 /* FEOptimizeInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOptimizeInput.h; sourceTree = \"<group>\"; };\n\t\tD5322CA12142AACE008DE511 /* FEOptimize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOptimize.h; sourceTree = \"<group>\"; };\n\t\tD5322CA22142AACE008DE511 /* FEDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataSource.h; sourceTree = \"<group>\"; };\n\t\tD5322CA32142AACE008DE511 /* targetver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = targetver.h; sourceTree = \"<group>\"; };\n\t\tD5322CA42142AACE008DE511 /* FEScanOptimizeMethod.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEScanOptimizeMethod.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CA52142AACE008DE511 /* FEConstrainedLMOptimizeMethod.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEConstrainedLMOptimizeMethod.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CA62142AACE008DE511 /* FEOptimizeData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOptimizeData.h; sourceTree = \"<group>\"; };\n\t\tD5322CA72142AACE008DE511 /* FEObjectiveFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEObjectiveFunction.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CA82142AACE008DE511 /* FELMOptimizeMethod.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELMOptimizeMethod.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CA92142AACE008DE511 /* FEOptimizeInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOptimizeInput.cpp; sourceTree = \"<group>\"; };\n\t\tD5322CAA2142AACE008DE511 /* FEPowellOptimizeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPowellOptimizeMethod.h; sourceTree = \"<group>\"; };\n\t\tD5322CAB2142AACE008DE511 /* FEScanOptimizeMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEScanOptimizeMethod.h; sourceTree = \"<group>\"; };\n\t\tD5322CC42142AB40008DE511 /* liblevmar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblevmar.a; path = /usr/local/lib/liblevmar.a; sourceTree = \"<absolute>\"; };\n\t\tD5FF2666233A64DD00C621EB /* FEParameterSweep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEParameterSweep.h; sourceTree = \"<group>\"; };\n\t\tD5FF2667233A64DD00C621EB /* FEParameterSweep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEParameterSweep.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5322C832142AABF008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322CC52142AB40008DE511 /* liblevmar.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5322C7D2142AABF008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C942142AACE008DE511 /* FEBioOpt */,\n\t\t\t\tD5322C872142AABF008DE511 /* Products */,\n\t\t\t\tD5322CC32142AB40008DE511 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322C872142AABF008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C862142AABF008DE511 /* libFEBioOpt.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322C942142AACE008DE511 /* FEBioOpt */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C9E2142AACE008DE511 /* FEBioOpt.cpp */,\n\t\t\t\tD5322C9D2142AACE008DE511 /* FEBioOpt.h */,\n\t\t\t\tD5322CA52142AACE008DE511 /* FEConstrainedLMOptimizeMethod.cpp */,\n\t\t\t\tD5322C952142AACE008DE511 /* FEConstrainedLMOptimizeMethod.h */,\n\t\t\t\tD5322C962142AACE008DE511 /* FEDataSource.cpp */,\n\t\t\t\tD5322CA22142AACE008DE511 /* FEDataSource.h */,\n\t\t\t\tD5322CA82142AACE008DE511 /* FELMOptimizeMethod.cpp */,\n\t\t\t\tD5322C972142AACE008DE511 /* FELMOptimizeMethod.h */,\n\t\t\t\tD5322CA72142AACE008DE511 /* FEObjectiveFunction.cpp */,\n\t\t\t\tD5322C982142AACE008DE511 /* FEObjectiveFunction.h */,\n\t\t\t\tD5322C9B2142AACE008DE511 /* FEOptimize.cpp */,\n\t\t\t\tD5322CA12142AACE008DE511 /* FEOptimize.h */,\n\t\t\t\tD5322C9F2142AACE008DE511 /* FEOptimizeData.cpp */,\n\t\t\t\tD5322CA62142AACE008DE511 /* FEOptimizeData.h */,\n\t\t\t\tD5322CA92142AACE008DE511 /* FEOptimizeInput.cpp */,\n\t\t\t\tD5322CA02142AACE008DE511 /* FEOptimizeInput.h */,\n\t\t\t\tD5322C9C2142AACE008DE511 /* FEOptimizeMethod.h */,\n\t\t\t\tD5FF2667233A64DD00C621EB /* FEParameterSweep.cpp */,\n\t\t\t\tD5FF2666233A64DD00C621EB /* FEParameterSweep.h */,\n\t\t\t\tD5322C9A2142AACE008DE511 /* FEPowellOptimizeMethod.cpp */,\n\t\t\t\tD5322CAA2142AACE008DE511 /* FEPowellOptimizeMethod.h */,\n\t\t\t\tD5322CA42142AACE008DE511 /* FEScanOptimizeMethod.cpp */,\n\t\t\t\tD5322CAB2142AACE008DE511 /* FEScanOptimizeMethod.h */,\n\t\t\t\tD5322C992142AACE008DE511 /* stdafx.h */,\n\t\t\t\tD5322CA32142AACE008DE511 /* targetver.h */,\n\t\t\t);\n\t\t\tname = FEBioOpt;\n\t\t\tpath = ../../FEBioOpt;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322CC32142AB40008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322CC42142AB40008DE511 /* liblevmar.a */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5322C842142AABF008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5FF2668233A64DD00C621EB /* FEParameterSweep.h in Headers */,\n\t\t\t\tD5322CB02142AACE008DE511 /* stdafx.h in Headers */,\n\t\t\t\tD5322CBA2142AACE008DE511 /* targetver.h in Headers */,\n\t\t\t\tD5322CC22142AACE008DE511 /* FEScanOptimizeMethod.h in Headers */,\n\t\t\t\tD5322CB72142AACE008DE511 /* FEOptimizeInput.h in Headers */,\n\t\t\t\tD5322CAE2142AACE008DE511 /* FELMOptimizeMethod.h in Headers */,\n\t\t\t\tD5322CB42142AACE008DE511 /* FEBioOpt.h in Headers */,\n\t\t\t\tD5322CB92142AACE008DE511 /* FEDataSource.h in Headers */,\n\t\t\t\tD5322CC12142AACE008DE511 /* FEPowellOptimizeMethod.h in Headers */,\n\t\t\t\tD5322CB32142AACE008DE511 /* FEOptimizeMethod.h in Headers */,\n\t\t\t\tD5322CAF2142AACE008DE511 /* FEObjectiveFunction.h in Headers */,\n\t\t\t\tD5322CB82142AACE008DE511 /* FEOptimize.h in Headers */,\n\t\t\t\tD5322CBD2142AACE008DE511 /* FEOptimizeData.h in Headers */,\n\t\t\t\tD5322CAC2142AACE008DE511 /* FEConstrainedLMOptimizeMethod.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5322C852142AABF008DE511 /* FEBioOpt */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5322C912142AABF008DE511 /* Build configuration list for PBXNativeTarget \"FEBioOpt\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5322C822142AABF008DE511 /* Sources */,\n\t\t\t\tD5322C832142AABF008DE511 /* Frameworks */,\n\t\t\t\tD5322C842142AABF008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioOpt;\n\t\t\tproductName = FEBioOpt;\n\t\t\tproductReference = D5322C862142AABF008DE511 /* libFEBioOpt.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5322C7E2142AABF008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5322C852142AABF008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5322C812142AABF008DE511 /* Build configuration list for PBXProject \"FEBioOpt\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5322C7D2142AABF008DE511;\n\t\t\tproductRefGroup = D5322C872142AABF008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5322C852142AABF008DE511 /* FEBioOpt */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5322C822142AABF008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322CAD2142AACE008DE511 /* FEDataSource.cpp in Sources */,\n\t\t\t\tD5322CB52142AACE008DE511 /* FEBioOpt.cpp in Sources */,\n\t\t\t\tD5322CC02142AACE008DE511 /* FEOptimizeInput.cpp in Sources */,\n\t\t\t\tD5322CB12142AACE008DE511 /* FEPowellOptimizeMethod.cpp in Sources */,\n\t\t\t\tD5322CB22142AACE008DE511 /* FEOptimize.cpp in Sources */,\n\t\t\t\tD5322CBE2142AACE008DE511 /* FEObjectiveFunction.cpp in Sources */,\n\t\t\t\tD5322CB62142AACE008DE511 /* FEOptimizeData.cpp in Sources */,\n\t\t\t\tD5322CBF2142AACE008DE511 /* FELMOptimizeMethod.cpp in Sources */,\n\t\t\t\tD5FF2669233A64DD00C621EB /* FEParameterSweep.cpp in Sources */,\n\t\t\t\tD5322CBC2142AACE008DE511 /* FEConstrainedLMOptimizeMethod.cpp in Sources */,\n\t\t\t\tD5322CBB2142AACE008DE511 /* FEScanOptimizeMethod.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5322C8F2142AABF008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-DHAVE_LEVMAR\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322C902142AABF008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-DHAVE_LEVMAR\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5322C922142AABF008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322C932142AABF008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5322C812142AABF008DE511 /* Build configuration list for PBXProject \"FEBioOpt\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322C8F2142AABF008DE511 /* Debug */,\n\t\t\t\tD5322C902142AABF008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5322C912142AABF008DE511 /* Build configuration list for PBXNativeTarget \"FEBioOpt\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322C922142AABF008DE511 /* Debug */,\n\t\t\t\tD5322C932142AABF008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5322C7E2142AABF008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioOpt/FEBioOpt.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioOpt.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioOpt/FEBioOpt.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioPlot/FEBioPlot.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD5322C722142AA51008DE511 /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C672142AA51008DE511 /* stdafx.h */; };\n\t\tD5322C742142AA51008DE511 /* PltArchive.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C692142AA51008DE511 /* PltArchive.h */; };\n\t\tD5322C752142AA51008DE511 /* FEBioPlotFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C6A2142AA51008DE511 /* FEBioPlotFile.cpp */; };\n\t\tD5322C762142AA51008DE511 /* FEBioPlotFile.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C6B2142AA51008DE511 /* FEBioPlotFile.h */; };\n\t\tD5322C772142AA51008DE511 /* targetver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C6C2142AA51008DE511 /* targetver.h */; };\n\t\tD5322C792142AA51008DE511 /* PlotFile.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C6E2142AA51008DE511 /* PlotFile.h */; };\n\t\tD5322C7A2142AA51008DE511 /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C6F2142AA51008DE511 /* stdafx.cpp */; };\n\t\tD5322C7B2142AA51008DE511 /* PltArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C702142AA51008DE511 /* PltArchive.cpp */; };\n\t\tD5322C7C2142AA51008DE511 /* PlotFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C712142AA51008DE511 /* PlotFile.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD5322C582142AA21008DE511 /* libFEBioPlot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioPlot.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5322C672142AA51008DE511 /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5322C692142AA51008DE511 /* PltArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PltArchive.h; sourceTree = \"<group>\"; };\n\t\tD5322C6A2142AA51008DE511 /* FEBioPlotFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioPlotFile.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C6B2142AA51008DE511 /* FEBioPlotFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioPlotFile.h; sourceTree = \"<group>\"; };\n\t\tD5322C6C2142AA51008DE511 /* targetver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = targetver.h; sourceTree = \"<group>\"; };\n\t\tD5322C6E2142AA51008DE511 /* PlotFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlotFile.h; sourceTree = \"<group>\"; };\n\t\tD5322C6F2142AA51008DE511 /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C702142AA51008DE511 /* PltArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PltArchive.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C712142AA51008DE511 /* PlotFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlotFile.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5322C552142AA21008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5322C4F2142AA21008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C662142AA51008DE511 /* FEBioPlot */,\n\t\t\t\tD5322C592142AA21008DE511 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322C592142AA21008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C582142AA21008DE511 /* libFEBioPlot.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322C662142AA51008DE511 /* FEBioPlot */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C6A2142AA51008DE511 /* FEBioPlotFile.cpp */,\n\t\t\t\tD5322C6B2142AA51008DE511 /* FEBioPlotFile.h */,\n\t\t\t\tD5322C712142AA51008DE511 /* PlotFile.cpp */,\n\t\t\t\tD5322C6E2142AA51008DE511 /* PlotFile.h */,\n\t\t\t\tD5322C702142AA51008DE511 /* PltArchive.cpp */,\n\t\t\t\tD5322C692142AA51008DE511 /* PltArchive.h */,\n\t\t\t\tD5322C6F2142AA51008DE511 /* stdafx.cpp */,\n\t\t\t\tD5322C672142AA51008DE511 /* stdafx.h */,\n\t\t\t\tD5322C6C2142AA51008DE511 /* targetver.h */,\n\t\t\t);\n\t\t\tname = FEBioPlot;\n\t\t\tpath = ../../FEBioPlot;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5322C562142AA21008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322C742142AA51008DE511 /* PltArchive.h in Headers */,\n\t\t\t\tD5322C772142AA51008DE511 /* targetver.h in Headers */,\n\t\t\t\tD5322C762142AA51008DE511 /* FEBioPlotFile.h in Headers */,\n\t\t\t\tD5322C722142AA51008DE511 /* stdafx.h in Headers */,\n\t\t\t\tD5322C792142AA51008DE511 /* PlotFile.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5322C572142AA21008DE511 /* FEBioPlot */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5322C632142AA21008DE511 /* Build configuration list for PBXNativeTarget \"FEBioPlot\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5322C542142AA21008DE511 /* Sources */,\n\t\t\t\tD5322C552142AA21008DE511 /* Frameworks */,\n\t\t\t\tD5322C562142AA21008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioPlot;\n\t\t\tproductName = FEBioPlot;\n\t\t\tproductReference = D5322C582142AA21008DE511 /* libFEBioPlot.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5322C502142AA21008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5322C572142AA21008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5322C532142AA21008DE511 /* Build configuration list for PBXProject \"FEBioPlot\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5322C4F2142AA21008DE511;\n\t\t\tproductRefGroup = D5322C592142AA21008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5322C572142AA21008DE511 /* FEBioPlot */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5322C542142AA21008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322C7C2142AA51008DE511 /* PlotFile.cpp in Sources */,\n\t\t\t\tD5322C7A2142AA51008DE511 /* stdafx.cpp in Sources */,\n\t\t\t\tD5322C7B2142AA51008DE511 /* PltArchive.cpp in Sources */,\n\t\t\t\tD5322C752142AA51008DE511 /* FEBioPlotFile.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5322C612142AA21008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322C622142AA21008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5322C642142AA21008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322C652142AA21008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5322C532142AA21008DE511 /* Build configuration list for PBXProject \"FEBioPlot\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322C612142AA21008DE511 /* Debug */,\n\t\t\t\tD5322C622142AA21008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5322C632142AA21008DE511 /* Build configuration list for PBXNativeTarget \"FEBioPlot\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322C642142AA21008DE511 /* Debug */,\n\t\t\t\tD5322C652142AA21008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5322C502142AA21008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioPlot/FEBioPlot.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioPlot.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioPlot/FEBioPlot.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioTest/FEBioTest.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD5322C2F2142A96C008DE511 /* FEBioDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C0F2142A96B008DE511 /* FEBioDiagnostic.h */; };\n\t\tD5322C302142A96C008DE511 /* FEContactDiagnosticBiphasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C102142A96B008DE511 /* FEContactDiagnosticBiphasic.cpp */; };\n\t\tD5322C312142A96C008DE511 /* FEFluidTangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C112142A96B008DE511 /* FEFluidTangentDiagnostic.h */; };\n\t\tD5322C322142A96C008DE511 /* FETangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C122142A96B008DE511 /* FETangentDiagnostic.h */; };\n\t\tD5322C332142A96C008DE511 /* FEContactDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C132142A96B008DE511 /* FEContactDiagnostic.h */; };\n\t\tD5322C342142A96C008DE511 /* FEPrintMatrixDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C142142A96B008DE511 /* FEPrintMatrixDiagnostic.cpp */; };\n\t\tD5322C352142A96C008DE511 /* FEMultiphasicTangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C152142A96B008DE511 /* FEMultiphasicTangentDiagnostic.cpp */; };\n\t\tD5322C362142A96C008DE511 /* FEContactDiagnosticBiphasic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C162142A96B008DE511 /* FEContactDiagnosticBiphasic.h */; };\n\t\tD5322C372142A96C008DE511 /* FEBioDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C172142A96B008DE511 /* FEBioDiagnostic.cpp */; };\n\t\tD5322C382142A96C008DE511 /* FEMemoryDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C182142A96B008DE511 /* FEMemoryDiagnostic.cpp */; };\n\t\tD5322C392142A96C008DE511 /* FEMemoryDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C192142A96B008DE511 /* FEMemoryDiagnostic.h */; };\n\t\tD5322C3A2142A96C008DE511 /* FEMultiphasicTangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C1A2142A96B008DE511 /* FEMultiphasicTangentDiagnostic.h */; };\n\t\tD5322C3B2142A96C008DE511 /* FEPrintMatrixDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C1B2142A96B008DE511 /* FEPrintMatrixDiagnostic.h */; };\n\t\tD5322C3C2142A96C008DE511 /* FEPrintHBMatrixDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C1C2142A96B008DE511 /* FEPrintHBMatrixDiagnostic.h */; };\n\t\tD5322C3D2142A96C008DE511 /* FEBioTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C1D2142A96B008DE511 /* FEBioTest.cpp */; };\n\t\tD5322C3E2142A96C008DE511 /* FEEASShellTangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C1E2142A96B008DE511 /* FEEASShellTangentDiagnostic.cpp */; };\n\t\tD5322C3F2142A96C008DE511 /* FETiedBiphasicDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C1F2142A96B008DE511 /* FETiedBiphasicDiagnostic.h */; };\n\t\tD5322C402142A96C008DE511 /* FEEASShellTangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C202142A96B008DE511 /* FEEASShellTangentDiagnostic.h */; };\n\t\tD5322C412142A96C008DE511 /* FEDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C212142A96B008DE511 /* FEDiagnostic.h */; };\n\t\tD5322C422142A96C008DE511 /* FEPrintHBMatrixDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C222142A96B008DE511 /* FEPrintHBMatrixDiagnostic.cpp */; };\n\t\tD5322C432142A96C008DE511 /* FEBiphasicTangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C232142A96B008DE511 /* FEBiphasicTangentDiagnostic.cpp */; };\n\t\tD5322C442142A96C008DE511 /* FEFluidFSITangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C242142A96B008DE511 /* FEFluidFSITangentDiagnostic.h */; };\n\t\tD5322C452142A96C008DE511 /* FEFluidTangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C252142A96B008DE511 /* FEFluidTangentDiagnostic.cpp */; };\n\t\tD5322C462142A96C008DE511 /* FERestartDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C262142A96B008DE511 /* FERestartDiagnostic.cpp */; };\n\t\tD5322C472142A96C008DE511 /* FEBiphasicTangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C272142A96B008DE511 /* FEBiphasicTangentDiagnostic.h */; };\n\t\tD5322C482142A96C008DE511 /* FETangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C282142A96B008DE511 /* FETangentDiagnostic.cpp */; };\n\t\tD5322C492142A96C008DE511 /* FERestartDiagnostics.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C292142A96B008DE511 /* FERestartDiagnostics.h */; };\n\t\tD5322C4A2142A96C008DE511 /* FEDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C2A2142A96B008DE511 /* FEDiagnostic.cpp */; };\n\t\tD5322C4B2142A96C008DE511 /* FETiedBiphasicDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C2B2142A96B008DE511 /* FETiedBiphasicDiagnostic.cpp */; };\n\t\tD5322C4C2142A96C008DE511 /* FEBioTest.h in Headers */ = {isa = PBXBuildFile; fileRef = D5322C2C2142A96B008DE511 /* FEBioTest.h */; };\n\t\tD5322C4D2142A96C008DE511 /* FEContactDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C2D2142A96B008DE511 /* FEContactDiagnostic.cpp */; };\n\t\tD5322C4E2142A96C008DE511 /* FEFluidFSITangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5322C2E2142A96B008DE511 /* FEFluidFSITangentDiagnostic.cpp */; };\n\t\tD550835224F086F700E919D8 /* FEBioEigenSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D550835024F086F700E919D8 /* FEBioEigenSolver.cpp */; };\n\t\tD550835324F086F700E919D8 /* FEBioEigenSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D550835124F086F700E919D8 /* FEBioEigenSolver.h */; };\n\t\tD559C4D022D916CA00CDC2BD /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D559C4CD22D916CA00CDC2BD /* stdafx.h */; };\n\t\tD559C4D122D916CA00CDC2BD /* FEJFNKTangentDiagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = D559C4CE22D916CA00CDC2BD /* FEJFNKTangentDiagnostic.h */; };\n\t\tD559C4D222D916CA00CDC2BD /* FEJFNKTangentDiagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D559C4CF22D916CA00CDC2BD /* FEJFNKTangentDiagnostic.cpp */; };\n\t\tD56DB9A0255EEF510072414C /* FEResetTest.h in Headers */ = {isa = PBXBuildFile; fileRef = D56DB99E255EEF510072414C /* FEResetTest.h */; };\n\t\tD56DB9A1255EEF510072414C /* FEResetTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56DB99F255EEF510072414C /* FEResetTest.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD5322C002142A95D008DE511 /* libFEBioTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioTest.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5322C0F2142A96B008DE511 /* FEBioDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C102142A96B008DE511 /* FEContactDiagnosticBiphasic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContactDiagnosticBiphasic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C112142A96B008DE511 /* FEFluidTangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidTangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C122142A96B008DE511 /* FETangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C132142A96B008DE511 /* FEContactDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContactDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C142142A96B008DE511 /* FEPrintMatrixDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrintMatrixDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C152142A96B008DE511 /* FEMultiphasicTangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMultiphasicTangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C162142A96B008DE511 /* FEContactDiagnosticBiphasic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEContactDiagnosticBiphasic.h; sourceTree = \"<group>\"; };\n\t\tD5322C172142A96B008DE511 /* FEBioDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C182142A96B008DE511 /* FEMemoryDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMemoryDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C192142A96B008DE511 /* FEMemoryDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMemoryDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C1A2142A96B008DE511 /* FEMultiphasicTangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMultiphasicTangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C1B2142A96B008DE511 /* FEPrintMatrixDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrintMatrixDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C1C2142A96B008DE511 /* FEPrintHBMatrixDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrintHBMatrixDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C1D2142A96B008DE511 /* FEBioTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioTest.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C1E2142A96B008DE511 /* FEEASShellTangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEASShellTangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C1F2142A96B008DE511 /* FETiedBiphasicDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETiedBiphasicDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C202142A96B008DE511 /* FEEASShellTangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEASShellTangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C212142A96B008DE511 /* FEDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C222142A96B008DE511 /* FEPrintHBMatrixDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrintHBMatrixDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C232142A96B008DE511 /* FEBiphasicTangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBiphasicTangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C242142A96B008DE511 /* FEFluidFSITangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFluidFSITangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C252142A96B008DE511 /* FEFluidTangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidTangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C262142A96B008DE511 /* FERestartDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERestartDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C272142A96B008DE511 /* FEBiphasicTangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBiphasicTangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD5322C282142A96B008DE511 /* FETangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C292142A96B008DE511 /* FERestartDiagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERestartDiagnostics.h; sourceTree = \"<group>\"; };\n\t\tD5322C2A2142A96B008DE511 /* FEDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C2B2142A96B008DE511 /* FETiedBiphasicDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETiedBiphasicDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C2C2142A96B008DE511 /* FEBioTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioTest.h; sourceTree = \"<group>\"; };\n\t\tD5322C2D2142A96B008DE511 /* FEContactDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEContactDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD5322C2E2142A96B008DE511 /* FEFluidFSITangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFluidFSITangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD550835024F086F700E919D8 /* FEBioEigenSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioEigenSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD550835124F086F700E919D8 /* FEBioEigenSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioEigenSolver.h; sourceTree = \"<group>\"; };\n\t\tD559C4CD22D916CA00CDC2BD /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD559C4CE22D916CA00CDC2BD /* FEJFNKTangentDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEJFNKTangentDiagnostic.h; sourceTree = \"<group>\"; };\n\t\tD559C4CF22D916CA00CDC2BD /* FEJFNKTangentDiagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEJFNKTangentDiagnostic.cpp; sourceTree = \"<group>\"; };\n\t\tD56DB99E255EEF510072414C /* FEResetTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEResetTest.h; sourceTree = \"<group>\"; };\n\t\tD56DB99F255EEF510072414C /* FEResetTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEResetTest.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5322BFD2142A95D008DE511 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5322BF72142A95D008DE511 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C0E2142A96B008DE511 /* FEBioTest */,\n\t\t\t\tD5322C012142A95D008DE511 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322C012142A95D008DE511 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C002142A95D008DE511 /* libFEBioTest.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5322C0E2142A96B008DE511 /* FEBioTest */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5322C172142A96B008DE511 /* FEBioDiagnostic.cpp */,\n\t\t\t\tD5322C0F2142A96B008DE511 /* FEBioDiagnostic.h */,\n\t\t\t\tD550835024F086F700E919D8 /* FEBioEigenSolver.cpp */,\n\t\t\t\tD550835124F086F700E919D8 /* FEBioEigenSolver.h */,\n\t\t\t\tD5322C1D2142A96B008DE511 /* FEBioTest.cpp */,\n\t\t\t\tD5322C2C2142A96B008DE511 /* FEBioTest.h */,\n\t\t\t\tD5322C232142A96B008DE511 /* FEBiphasicTangentDiagnostic.cpp */,\n\t\t\t\tD5322C272142A96B008DE511 /* FEBiphasicTangentDiagnostic.h */,\n\t\t\t\tD5322C2D2142A96B008DE511 /* FEContactDiagnostic.cpp */,\n\t\t\t\tD5322C132142A96B008DE511 /* FEContactDiagnostic.h */,\n\t\t\t\tD5322C102142A96B008DE511 /* FEContactDiagnosticBiphasic.cpp */,\n\t\t\t\tD5322C162142A96B008DE511 /* FEContactDiagnosticBiphasic.h */,\n\t\t\t\tD5322C2A2142A96B008DE511 /* FEDiagnostic.cpp */,\n\t\t\t\tD5322C212142A96B008DE511 /* FEDiagnostic.h */,\n\t\t\t\tD5322C1E2142A96B008DE511 /* FEEASShellTangentDiagnostic.cpp */,\n\t\t\t\tD5322C202142A96B008DE511 /* FEEASShellTangentDiagnostic.h */,\n\t\t\t\tD5322C2E2142A96B008DE511 /* FEFluidFSITangentDiagnostic.cpp */,\n\t\t\t\tD5322C242142A96B008DE511 /* FEFluidFSITangentDiagnostic.h */,\n\t\t\t\tD5322C252142A96B008DE511 /* FEFluidTangentDiagnostic.cpp */,\n\t\t\t\tD5322C112142A96B008DE511 /* FEFluidTangentDiagnostic.h */,\n\t\t\t\tD559C4CF22D916CA00CDC2BD /* FEJFNKTangentDiagnostic.cpp */,\n\t\t\t\tD559C4CE22D916CA00CDC2BD /* FEJFNKTangentDiagnostic.h */,\n\t\t\t\tD5322C182142A96B008DE511 /* FEMemoryDiagnostic.cpp */,\n\t\t\t\tD5322C192142A96B008DE511 /* FEMemoryDiagnostic.h */,\n\t\t\t\tD5322C152142A96B008DE511 /* FEMultiphasicTangentDiagnostic.cpp */,\n\t\t\t\tD5322C1A2142A96B008DE511 /* FEMultiphasicTangentDiagnostic.h */,\n\t\t\t\tD5322C222142A96B008DE511 /* FEPrintHBMatrixDiagnostic.cpp */,\n\t\t\t\tD5322C1C2142A96B008DE511 /* FEPrintHBMatrixDiagnostic.h */,\n\t\t\t\tD5322C142142A96B008DE511 /* FEPrintMatrixDiagnostic.cpp */,\n\t\t\t\tD5322C1B2142A96B008DE511 /* FEPrintMatrixDiagnostic.h */,\n\t\t\t\tD56DB99F255EEF510072414C /* FEResetTest.cpp */,\n\t\t\t\tD56DB99E255EEF510072414C /* FEResetTest.h */,\n\t\t\t\tD5322C262142A96B008DE511 /* FERestartDiagnostic.cpp */,\n\t\t\t\tD5322C292142A96B008DE511 /* FERestartDiagnostics.h */,\n\t\t\t\tD5322C282142A96B008DE511 /* FETangentDiagnostic.cpp */,\n\t\t\t\tD5322C122142A96B008DE511 /* FETangentDiagnostic.h */,\n\t\t\t\tD5322C2B2142A96B008DE511 /* FETiedBiphasicDiagnostic.cpp */,\n\t\t\t\tD5322C1F2142A96B008DE511 /* FETiedBiphasicDiagnostic.h */,\n\t\t\t\tD559C4CD22D916CA00CDC2BD /* stdafx.h */,\n\t\t\t);\n\t\t\tname = FEBioTest;\n\t\t\tpath = ../../FEBioTest;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5322BFE2142A95D008DE511 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322C412142A96C008DE511 /* FEDiagnostic.h in Headers */,\n\t\t\t\tD5322C472142A96C008DE511 /* FEBiphasicTangentDiagnostic.h in Headers */,\n\t\t\t\tD5322C2F2142A96C008DE511 /* FEBioDiagnostic.h in Headers */,\n\t\t\t\tD5322C3B2142A96C008DE511 /* FEPrintMatrixDiagnostic.h in Headers */,\n\t\t\t\tD5322C322142A96C008DE511 /* FETangentDiagnostic.h in Headers */,\n\t\t\t\tD56DB9A0255EEF510072414C /* FEResetTest.h in Headers */,\n\t\t\t\tD559C4D122D916CA00CDC2BD /* FEJFNKTangentDiagnostic.h in Headers */,\n\t\t\t\tD550835324F086F700E919D8 /* FEBioEigenSolver.h in Headers */,\n\t\t\t\tD5322C442142A96C008DE511 /* FEFluidFSITangentDiagnostic.h in Headers */,\n\t\t\t\tD5322C4C2142A96C008DE511 /* FEBioTest.h in Headers */,\n\t\t\t\tD5322C3C2142A96C008DE511 /* FEPrintHBMatrixDiagnostic.h in Headers */,\n\t\t\t\tD5322C362142A96C008DE511 /* FEContactDiagnosticBiphasic.h in Headers */,\n\t\t\t\tD5322C492142A96C008DE511 /* FERestartDiagnostics.h in Headers */,\n\t\t\t\tD5322C312142A96C008DE511 /* FEFluidTangentDiagnostic.h in Headers */,\n\t\t\t\tD559C4D022D916CA00CDC2BD /* stdafx.h in Headers */,\n\t\t\t\tD5322C332142A96C008DE511 /* FEContactDiagnostic.h in Headers */,\n\t\t\t\tD5322C3F2142A96C008DE511 /* FETiedBiphasicDiagnostic.h in Headers */,\n\t\t\t\tD5322C402142A96C008DE511 /* FEEASShellTangentDiagnostic.h in Headers */,\n\t\t\t\tD5322C3A2142A96C008DE511 /* FEMultiphasicTangentDiagnostic.h in Headers */,\n\t\t\t\tD5322C392142A96C008DE511 /* FEMemoryDiagnostic.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5322BFF2142A95D008DE511 /* FEBioTest */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5322C0B2142A95D008DE511 /* Build configuration list for PBXNativeTarget \"FEBioTest\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5322BFC2142A95D008DE511 /* Sources */,\n\t\t\t\tD5322BFD2142A95D008DE511 /* Frameworks */,\n\t\t\t\tD5322BFE2142A95D008DE511 /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioTest;\n\t\t\tproductName = FEBioTest;\n\t\t\tproductReference = D5322C002142A95D008DE511 /* libFEBioTest.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5322BF82142A95D008DE511 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5322BFF2142A95D008DE511 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5322BFB2142A95D008DE511 /* Build configuration list for PBXProject \"FEBioTest\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5322BF72142A95D008DE511;\n\t\t\tproductRefGroup = D5322C012142A95D008DE511 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5322BFF2142A95D008DE511 /* FEBioTest */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5322BFC2142A95D008DE511 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5322C432142A96C008DE511 /* FEBiphasicTangentDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C462142A96C008DE511 /* FERestartDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C4D2142A96C008DE511 /* FEContactDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C4B2142A96C008DE511 /* FETiedBiphasicDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C342142A96C008DE511 /* FEPrintMatrixDiagnostic.cpp in Sources */,\n\t\t\t\tD559C4D222D916CA00CDC2BD /* FEJFNKTangentDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C4A2142A96C008DE511 /* FEDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C3E2142A96C008DE511 /* FEEASShellTangentDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C4E2142A96C008DE511 /* FEFluidFSITangentDiagnostic.cpp in Sources */,\n\t\t\t\tD550835224F086F700E919D8 /* FEBioEigenSolver.cpp in Sources */,\n\t\t\t\tD5322C422142A96C008DE511 /* FEPrintHBMatrixDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C352142A96C008DE511 /* FEMultiphasicTangentDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C372142A96C008DE511 /* FEBioDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C452142A96C008DE511 /* FEFluidTangentDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C3D2142A96C008DE511 /* FEBioTest.cpp in Sources */,\n\t\t\t\tD56DB9A1255EEF510072414C /* FEResetTest.cpp in Sources */,\n\t\t\t\tD5322C482142A96C008DE511 /* FETangentDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C382142A96C008DE511 /* FEMemoryDiagnostic.cpp in Sources */,\n\t\t\t\tD5322C302142A96C008DE511 /* FEContactDiagnosticBiphasic.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5322C092142A95D008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322C0A2142A95D008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5322C0C2142A95D008DE511 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5322C0D2142A95D008DE511 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5322BFB2142A95D008DE511 /* Build configuration list for PBXProject \"FEBioTest\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322C092142A95D008DE511 /* Debug */,\n\t\t\t\tD5322C0A2142A95D008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5322C0B2142A95D008DE511 /* Build configuration list for PBXNativeTarget \"FEBioTest\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5322C0C2142A95D008DE511 /* Debug */,\n\t\t\t\tD5322C0D2142A95D008DE511 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5322BF82142A95D008DE511 /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioTest/FEBioTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioTest.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioTest/FEBioTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FEBioXML/FEBioXML.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD5613D38217B601A007CAB89 /* FEBioControlSection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D36217B601A007CAB89 /* FEBioControlSection3.cpp */; };\n\t\tD5613D39217B601A007CAB89 /* FEBioLoadDataSection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D37217B601A007CAB89 /* FEBioLoadDataSection3.cpp */; };\n\t\tD565CDE7215D451B00E08ED6 /* FEBioLoadsSection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDE4215D451B00E08ED6 /* FEBioLoadsSection3.cpp */; };\n\t\tD565CDE8215D451B00E08ED6 /* FEBioMeshDataSection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDE5215D451B00E08ED6 /* FEBioMeshDataSection3.cpp */; };\n\t\tD565CDE9215D451B00E08ED6 /* FEBioGeometrySection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDE6215D451B00E08ED6 /* FEBioGeometrySection3.cpp */; };\n\t\tD5709D9A22833011007CAB0A /* FEBioControlSection3.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D9322833011007CAB0A /* FEBioControlSection3.h */; };\n\t\tD5709D9B22833011007CAB0A /* FEBioBoundarySection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D9422833011007CAB0A /* FEBioBoundarySection3.cpp */; };\n\t\tD5709D9C22833011007CAB0A /* FEBioStepSection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D9522833011007CAB0A /* FEBioStepSection3.cpp */; };\n\t\tD5709D9D22833011007CAB0A /* FEBioStepSection3.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D9622833011007CAB0A /* FEBioStepSection3.h */; };\n\t\tD5709D9E22833011007CAB0A /* FEBioInitialSection3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709D9722833011007CAB0A /* FEBioInitialSection3.cpp */; };\n\t\tD5709D9F22833011007CAB0A /* FEBioInitialSection3.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D9822833011007CAB0A /* FEBioInitialSection3.h */; };\n\t\tD5709DA022833011007CAB0A /* FEBioBoundarySection3.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709D9922833011007CAB0A /* FEBioBoundarySection3.h */; };\n\t\tD59248C322333F4800B30723 /* febioxml_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D59248C222333F4800B30723 /* febioxml_api.h */; };\n\t\tD5B805B7223BEC0200198805 /* FEBioMeshAdaptorSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B805B5223BEC0200198805 /* FEBioMeshAdaptorSection.h */; };\n\t\tD5B805B8223BEC0200198805 /* FEBioMeshAdaptorSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B805B6223BEC0200198805 /* FEBioMeshAdaptorSection.cpp */; };\n\t\tD5B9E65F213F6A600008B38A /* FEBioBoundarySection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E62A213F6A600008B38A /* FEBioBoundarySection.cpp */; };\n\t\tD5B9E660213F6A600008B38A /* FEBioLoadDataSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E62B213F6A600008B38A /* FEBioLoadDataSection.h */; };\n\t\tD5B9E661213F6A600008B38A /* FileImport.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E62C213F6A600008B38A /* FileImport.h */; };\n\t\tD5B9E662213F6A600008B38A /* FEBioDiscreteSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E62D213F6A600008B38A /* FEBioDiscreteSection.cpp */; };\n\t\tD5B9E663213F6A600008B38A /* FEBioLoadsSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E62E213F6A600008B38A /* FEBioLoadsSection.h */; };\n\t\tD5B9E664213F6A600008B38A /* FEBioContactSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E62F213F6A600008B38A /* FEBioContactSection.h */; };\n\t\tD5B9E665213F6A600008B38A /* FEBModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E630213F6A600008B38A /* FEBModel.h */; };\n\t\tD5B9E666213F6A600008B38A /* FEBioConstraintsSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E631213F6A600008B38A /* FEBioConstraintsSection.cpp */; };\n\t\tD5B9E667213F6A600008B38A /* FEBioRigidSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E632213F6A600008B38A /* FEBioRigidSection.cpp */; };\n\t\tD5B9E668213F6A600008B38A /* FEBioControlSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E633213F6A600008B38A /* FEBioControlSection.h */; };\n\t\tD5B9E669213F6A600008B38A /* FEBioIncludeSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E634213F6A600008B38A /* FEBioIncludeSection.cpp */; };\n\t\tD5B9E66A213F6A600008B38A /* FEBioModuleSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E635213F6A600008B38A /* FEBioModuleSection.h */; };\n\t\tD5B9E66B213F6A600008B38A /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E636213F6A600008B38A /* stdafx.h */; };\n\t\tD5B9E66C213F6A600008B38A /* FEBioRigidSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E637213F6A600008B38A /* FEBioRigidSection.h */; };\n\t\tD5B9E66D213F6A600008B38A /* FEBioLoadDataSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E638213F6A600008B38A /* FEBioLoadDataSection.cpp */; };\n\t\tD5B9E66E213F6A600008B38A /* FEBioMaterialSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E639213F6A600008B38A /* FEBioMaterialSection.h */; };\n\t\tD5B9E66F213F6A600008B38A /* FEBioMeshDataSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E63A213F6A600008B38A /* FEBioMeshDataSection.cpp */; };\n\t\tD5B9E670213F6A600008B38A /* FEBioGeometrySection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E63B213F6A600008B38A /* FEBioGeometrySection.h */; };\n\t\tD5B9E671213F6A600008B38A /* FEBioCodeSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E63C213F6A600008B38A /* FEBioCodeSection.h */; };\n\t\tD5B9E672213F6A600008B38A /* FEBioControlSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E63D213F6A600008B38A /* FEBioControlSection.cpp */; };\n\t\tD5B9E673213F6A600008B38A /* FEBioStepSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E63E213F6A600008B38A /* FEBioStepSection.h */; };\n\t\tD5B9E674213F6A600008B38A /* FEBioInitialSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E63F213F6A600008B38A /* FEBioInitialSection.cpp */; };\n\t\tD5B9E675213F6A600008B38A /* FEBioGlobalsSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E640213F6A600008B38A /* FEBioGlobalsSection.cpp */; };\n\t\tD5B9E676213F6A600008B38A /* FEModelBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E641213F6A600008B38A /* FEModelBuilder.h */; };\n\t\tD5B9E677213F6A600008B38A /* FEBioInitialSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E642213F6A600008B38A /* FEBioInitialSection.h */; };\n\t\tD5B9E678213F6A600008B38A /* FEBioMeshDataSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E643213F6A600008B38A /* FEBioMeshDataSection.h */; };\n\t\tD5B9E679213F6A600008B38A /* xmltool.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E644213F6A600008B38A /* xmltool.h */; };\n\t\tD5B9E67A213F6A600008B38A /* FEBioDiscreteSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E645213F6A600008B38A /* FEBioDiscreteSection.h */; };\n\t\tD5B9E67B213F6A600008B38A /* XMLReader.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E646213F6A600008B38A /* XMLReader.h */; };\n\t\tD5B9E67C213F6A600008B38A /* FEBioLoadsSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E647213F6A600008B38A /* FEBioLoadsSection.cpp */; };\n\t\tD5B9E67D213F6A600008B38A /* FEBioBoundarySection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E648213F6A600008B38A /* FEBioBoundarySection.h */; };\n\t\tD5B9E67E213F6A600008B38A /* XMLReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E649213F6A600008B38A /* XMLReader.cpp */; };\n\t\tD5B9E67F213F6A600008B38A /* FEBioGeometrySection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E64A213F6A600008B38A /* FEBioGeometrySection.cpp */; };\n\t\tD5B9E680213F6A600008B38A /* FEBioMaterialSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E64B213F6A600008B38A /* FEBioMaterialSection.cpp */; };\n\t\tD5B9E681213F6A600008B38A /* FERestartImport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E64C213F6A600008B38A /* FERestartImport.cpp */; };\n\t\tD5B9E682213F6A600008B38A /* FEBioModuleSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E64D213F6A600008B38A /* FEBioModuleSection.cpp */; };\n\t\tD5B9E683213F6A600008B38A /* FEBioGlobalsSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E64E213F6A600008B38A /* FEBioGlobalsSection.h */; };\n\t\tD5B9E684213F6A600008B38A /* FEBioConstraintsSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E64F213F6A600008B38A /* FEBioConstraintsSection.h */; };\n\t\tD5B9E685213F6A600008B38A /* FEBioOutputSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E650213F6A600008B38A /* FEBioOutputSection.cpp */; };\n\t\tD5B9E686213F6A600008B38A /* xmltool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E651213F6A600008B38A /* xmltool.cpp */; };\n\t\tD5B9E687213F6A600008B38A /* FEBioStepSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E652213F6A600008B38A /* FEBioStepSection.cpp */; };\n\t\tD5B9E688213F6A600008B38A /* FEBioImport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E653213F6A600008B38A /* FEBioImport.cpp */; };\n\t\tD5B9E689213F6A600008B38A /* FEBModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E654213F6A600008B38A /* FEBModel.cpp */; };\n\t\tD5B9E68A213F6A600008B38A /* FileImport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E655213F6A600008B38A /* FileImport.cpp */; };\n\t\tD5B9E68B213F6A600008B38A /* FEModelBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E656213F6A600008B38A /* FEModelBuilder.cpp */; };\n\t\tD5B9E68D213F6A600008B38A /* FEBioCodeSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E658213F6A600008B38A /* FEBioCodeSection.cpp */; };\n\t\tD5B9E68E213F6A600008B38A /* FEBioIncludeSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E659213F6A600008B38A /* FEBioIncludeSection.h */; };\n\t\tD5B9E68F213F6A600008B38A /* FEBioOutputSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E65A213F6A600008B38A /* FEBioOutputSection.h */; };\n\t\tD5B9E690213F6A600008B38A /* FEBioContactSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E65B213F6A600008B38A /* FEBioContactSection.cpp */; };\n\t\tD5B9E692213F6A600008B38A /* FERestartImport.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E65D213F6A600008B38A /* FERestartImport.h */; };\n\t\tD5B9E693213F6A600008B38A /* FEBioImport.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E65E213F6A600008B38A /* FEBioImport.h */; };\n\t\tD5C5881024D72F2900051B9D /* FEBioMeshSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C5880E24D72F2900051B9D /* FEBioMeshSection.cpp */; };\n\t\tD5C5881124D72F2900051B9D /* FEBioMeshSection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C5880F24D72F2900051B9D /* FEBioMeshSection.h */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD5613D36217B601A007CAB89 /* FEBioControlSection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioControlSection3.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D37217B601A007CAB89 /* FEBioLoadDataSection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioLoadDataSection3.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDE4215D451B00E08ED6 /* FEBioLoadsSection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioLoadsSection3.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDE5215D451B00E08ED6 /* FEBioMeshDataSection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMeshDataSection3.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDE6215D451B00E08ED6 /* FEBioGeometrySection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioGeometrySection3.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D9322833011007CAB0A /* FEBioControlSection3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioControlSection3.h; sourceTree = \"<group>\"; };\n\t\tD5709D9422833011007CAB0A /* FEBioBoundarySection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioBoundarySection3.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D9522833011007CAB0A /* FEBioStepSection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioStepSection3.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D9622833011007CAB0A /* FEBioStepSection3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioStepSection3.h; sourceTree = \"<group>\"; };\n\t\tD5709D9722833011007CAB0A /* FEBioInitialSection3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioInitialSection3.cpp; sourceTree = \"<group>\"; };\n\t\tD5709D9822833011007CAB0A /* FEBioInitialSection3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioInitialSection3.h; sourceTree = \"<group>\"; };\n\t\tD5709D9922833011007CAB0A /* FEBioBoundarySection3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioBoundarySection3.h; sourceTree = \"<group>\"; };\n\t\tD59248C222333F4800B30723 /* febioxml_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = febioxml_api.h; sourceTree = \"<group>\"; };\n\t\tD5B805B5223BEC0200198805 /* FEBioMeshAdaptorSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMeshAdaptorSection.h; sourceTree = \"<group>\"; };\n\t\tD5B805B6223BEC0200198805 /* FEBioMeshAdaptorSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMeshAdaptorSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E61B213F6A510008B38A /* libFEBioXML.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFEBioXML.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5B9E62A213F6A600008B38A /* FEBioBoundarySection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioBoundarySection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E62B213F6A600008B38A /* FEBioLoadDataSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioLoadDataSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E62C213F6A600008B38A /* FileImport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileImport.h; sourceTree = \"<group>\"; };\n\t\tD5B9E62D213F6A600008B38A /* FEBioDiscreteSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioDiscreteSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E62E213F6A600008B38A /* FEBioLoadsSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioLoadsSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E62F213F6A600008B38A /* FEBioContactSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioContactSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E630213F6A600008B38A /* FEBModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBModel.h; sourceTree = \"<group>\"; };\n\t\tD5B9E631213F6A600008B38A /* FEBioConstraintsSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioConstraintsSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E632213F6A600008B38A /* FEBioRigidSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioRigidSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E633213F6A600008B38A /* FEBioControlSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioControlSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E634213F6A600008B38A /* FEBioIncludeSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioIncludeSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E635213F6A600008B38A /* FEBioModuleSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioModuleSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E636213F6A600008B38A /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5B9E637213F6A600008B38A /* FEBioRigidSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioRigidSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E638213F6A600008B38A /* FEBioLoadDataSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioLoadDataSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E639213F6A600008B38A /* FEBioMaterialSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMaterialSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E63A213F6A600008B38A /* FEBioMeshDataSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMeshDataSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E63B213F6A600008B38A /* FEBioGeometrySection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioGeometrySection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E63C213F6A600008B38A /* FEBioCodeSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioCodeSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E63D213F6A600008B38A /* FEBioControlSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioControlSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E63E213F6A600008B38A /* FEBioStepSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioStepSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E63F213F6A600008B38A /* FEBioInitialSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioInitialSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E640213F6A600008B38A /* FEBioGlobalsSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioGlobalsSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E641213F6A600008B38A /* FEModelBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEModelBuilder.h; sourceTree = \"<group>\"; };\n\t\tD5B9E642213F6A600008B38A /* FEBioInitialSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioInitialSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E643213F6A600008B38A /* FEBioMeshDataSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMeshDataSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E644213F6A600008B38A /* xmltool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmltool.h; sourceTree = \"<group>\"; };\n\t\tD5B9E645213F6A600008B38A /* FEBioDiscreteSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioDiscreteSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E646213F6A600008B38A /* XMLReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLReader.h; sourceTree = \"<group>\"; };\n\t\tD5B9E647213F6A600008B38A /* FEBioLoadsSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioLoadsSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E648213F6A600008B38A /* FEBioBoundarySection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioBoundarySection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E649213F6A600008B38A /* XMLReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XMLReader.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E64A213F6A600008B38A /* FEBioGeometrySection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioGeometrySection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E64B213F6A600008B38A /* FEBioMaterialSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMaterialSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E64C213F6A600008B38A /* FERestartImport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FERestartImport.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E64D213F6A600008B38A /* FEBioModuleSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioModuleSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E64E213F6A600008B38A /* FEBioGlobalsSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioGlobalsSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E64F213F6A600008B38A /* FEBioConstraintsSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioConstraintsSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E650213F6A600008B38A /* FEBioOutputSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioOutputSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E651213F6A600008B38A /* xmltool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xmltool.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E652213F6A600008B38A /* FEBioStepSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioStepSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E653213F6A600008B38A /* FEBioImport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioImport.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E654213F6A600008B38A /* FEBModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBModel.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E655213F6A600008B38A /* FileImport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileImport.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E656213F6A600008B38A /* FEModelBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEModelBuilder.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E658213F6A600008B38A /* FEBioCodeSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioCodeSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E659213F6A600008B38A /* FEBioIncludeSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioIncludeSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E65A213F6A600008B38A /* FEBioOutputSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioOutputSection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E65B213F6A600008B38A /* FEBioContactSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioContactSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E65D213F6A600008B38A /* FERestartImport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FERestartImport.h; sourceTree = \"<group>\"; };\n\t\tD5B9E65E213F6A600008B38A /* FEBioImport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioImport.h; sourceTree = \"<group>\"; };\n\t\tD5C5880E24D72F2900051B9D /* FEBioMeshSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBioMeshSection.cpp; sourceTree = \"<group>\"; };\n\t\tD5C5880F24D72F2900051B9D /* FEBioMeshSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBioMeshSection.h; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5B9E618213F6A510008B38A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5B9E612213F6A510008B38A = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E629213F6A600008B38A /* FEBioXML */,\n\t\t\t\tD5B9E61C213F6A510008B38A /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5B9E61C213F6A510008B38A /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E61B213F6A510008B38A /* libFEBioXML.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5B9E629213F6A600008B38A /* FEBioXML */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E62A213F6A600008B38A /* FEBioBoundarySection.cpp */,\n\t\t\t\tD5B9E648213F6A600008B38A /* FEBioBoundarySection.h */,\n\t\t\t\tD5709D9422833011007CAB0A /* FEBioBoundarySection3.cpp */,\n\t\t\t\tD5709D9922833011007CAB0A /* FEBioBoundarySection3.h */,\n\t\t\t\tD5B9E658213F6A600008B38A /* FEBioCodeSection.cpp */,\n\t\t\t\tD5B9E63C213F6A600008B38A /* FEBioCodeSection.h */,\n\t\t\t\tD5B9E631213F6A600008B38A /* FEBioConstraintsSection.cpp */,\n\t\t\t\tD5B9E64F213F6A600008B38A /* FEBioConstraintsSection.h */,\n\t\t\t\tD5B9E65B213F6A600008B38A /* FEBioContactSection.cpp */,\n\t\t\t\tD5B9E62F213F6A600008B38A /* FEBioContactSection.h */,\n\t\t\t\tD5B9E63D213F6A600008B38A /* FEBioControlSection.cpp */,\n\t\t\t\tD5B9E633213F6A600008B38A /* FEBioControlSection.h */,\n\t\t\t\tD5613D36217B601A007CAB89 /* FEBioControlSection3.cpp */,\n\t\t\t\tD5709D9322833011007CAB0A /* FEBioControlSection3.h */,\n\t\t\t\tD5B9E62D213F6A600008B38A /* FEBioDiscreteSection.cpp */,\n\t\t\t\tD5B9E645213F6A600008B38A /* FEBioDiscreteSection.h */,\n\t\t\t\tD5B9E64A213F6A600008B38A /* FEBioGeometrySection.cpp */,\n\t\t\t\tD5B9E63B213F6A600008B38A /* FEBioGeometrySection.h */,\n\t\t\t\tD565CDE6215D451B00E08ED6 /* FEBioGeometrySection3.cpp */,\n\t\t\t\tD5B9E640213F6A600008B38A /* FEBioGlobalsSection.cpp */,\n\t\t\t\tD5B9E64E213F6A600008B38A /* FEBioGlobalsSection.h */,\n\t\t\t\tD5B9E653213F6A600008B38A /* FEBioImport.cpp */,\n\t\t\t\tD5B9E65E213F6A600008B38A /* FEBioImport.h */,\n\t\t\t\tD5B9E634213F6A600008B38A /* FEBioIncludeSection.cpp */,\n\t\t\t\tD5B9E659213F6A600008B38A /* FEBioIncludeSection.h */,\n\t\t\t\tD5B9E63F213F6A600008B38A /* FEBioInitialSection.cpp */,\n\t\t\t\tD5B9E642213F6A600008B38A /* FEBioInitialSection.h */,\n\t\t\t\tD5709D9722833011007CAB0A /* FEBioInitialSection3.cpp */,\n\t\t\t\tD5709D9822833011007CAB0A /* FEBioInitialSection3.h */,\n\t\t\t\tD5B9E638213F6A600008B38A /* FEBioLoadDataSection.cpp */,\n\t\t\t\tD5B9E62B213F6A600008B38A /* FEBioLoadDataSection.h */,\n\t\t\t\tD5613D37217B601A007CAB89 /* FEBioLoadDataSection3.cpp */,\n\t\t\t\tD5B9E647213F6A600008B38A /* FEBioLoadsSection.cpp */,\n\t\t\t\tD5B9E62E213F6A600008B38A /* FEBioLoadsSection.h */,\n\t\t\t\tD565CDE4215D451B00E08ED6 /* FEBioLoadsSection3.cpp */,\n\t\t\t\tD5B9E64B213F6A600008B38A /* FEBioMaterialSection.cpp */,\n\t\t\t\tD5B9E639213F6A600008B38A /* FEBioMaterialSection.h */,\n\t\t\t\tD5B805B6223BEC0200198805 /* FEBioMeshAdaptorSection.cpp */,\n\t\t\t\tD5B805B5223BEC0200198805 /* FEBioMeshAdaptorSection.h */,\n\t\t\t\tD5B9E63A213F6A600008B38A /* FEBioMeshDataSection.cpp */,\n\t\t\t\tD5B9E643213F6A600008B38A /* FEBioMeshDataSection.h */,\n\t\t\t\tD565CDE5215D451B00E08ED6 /* FEBioMeshDataSection3.cpp */,\n\t\t\t\tD5C5880E24D72F2900051B9D /* FEBioMeshSection.cpp */,\n\t\t\t\tD5C5880F24D72F2900051B9D /* FEBioMeshSection.h */,\n\t\t\t\tD5B9E64D213F6A600008B38A /* FEBioModuleSection.cpp */,\n\t\t\t\tD5B9E635213F6A600008B38A /* FEBioModuleSection.h */,\n\t\t\t\tD5B9E650213F6A600008B38A /* FEBioOutputSection.cpp */,\n\t\t\t\tD5B9E65A213F6A600008B38A /* FEBioOutputSection.h */,\n\t\t\t\tD5B9E632213F6A600008B38A /* FEBioRigidSection.cpp */,\n\t\t\t\tD5B9E637213F6A600008B38A /* FEBioRigidSection.h */,\n\t\t\t\tD5B9E652213F6A600008B38A /* FEBioStepSection.cpp */,\n\t\t\t\tD5B9E63E213F6A600008B38A /* FEBioStepSection.h */,\n\t\t\t\tD5709D9522833011007CAB0A /* FEBioStepSection3.cpp */,\n\t\t\t\tD5709D9622833011007CAB0A /* FEBioStepSection3.h */,\n\t\t\t\tD59248C222333F4800B30723 /* febioxml_api.h */,\n\t\t\t\tD5B9E654213F6A600008B38A /* FEBModel.cpp */,\n\t\t\t\tD5B9E630213F6A600008B38A /* FEBModel.h */,\n\t\t\t\tD5B9E656213F6A600008B38A /* FEModelBuilder.cpp */,\n\t\t\t\tD5B9E641213F6A600008B38A /* FEModelBuilder.h */,\n\t\t\t\tD5B9E64C213F6A600008B38A /* FERestartImport.cpp */,\n\t\t\t\tD5B9E65D213F6A600008B38A /* FERestartImport.h */,\n\t\t\t\tD5B9E655213F6A600008B38A /* FileImport.cpp */,\n\t\t\t\tD5B9E62C213F6A600008B38A /* FileImport.h */,\n\t\t\t\tD5B9E636213F6A600008B38A /* stdafx.h */,\n\t\t\t\tD5B9E649213F6A600008B38A /* XMLReader.cpp */,\n\t\t\t\tD5B9E646213F6A600008B38A /* XMLReader.h */,\n\t\t\t\tD5B9E651213F6A600008B38A /* xmltool.cpp */,\n\t\t\t\tD5B9E644213F6A600008B38A /* xmltool.h */,\n\t\t\t);\n\t\t\tname = FEBioXML;\n\t\t\tpath = ../../FEBioXML;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5B9E619213F6A510008B38A /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5B9E66C213F6A600008B38A /* FEBioRigidSection.h in Headers */,\n\t\t\t\tD5B9E673213F6A600008B38A /* FEBioStepSection.h in Headers */,\n\t\t\t\tD5B9E66B213F6A600008B38A /* stdafx.h in Headers */,\n\t\t\t\tD5709D9F22833011007CAB0A /* FEBioInitialSection3.h in Headers */,\n\t\t\t\tD59248C322333F4800B30723 /* febioxml_api.h in Headers */,\n\t\t\t\tD5B9E67A213F6A600008B38A /* FEBioDiscreteSection.h in Headers */,\n\t\t\t\tD5B9E692213F6A600008B38A /* FERestartImport.h in Headers */,\n\t\t\t\tD5709DA022833011007CAB0A /* FEBioBoundarySection3.h in Headers */,\n\t\t\t\tD5709D9D22833011007CAB0A /* FEBioStepSection3.h in Headers */,\n\t\t\t\tD5B805B7223BEC0200198805 /* FEBioMeshAdaptorSection.h in Headers */,\n\t\t\t\tD5B9E668213F6A600008B38A /* FEBioControlSection.h in Headers */,\n\t\t\t\tD5B9E693213F6A600008B38A /* FEBioImport.h in Headers */,\n\t\t\t\tD5B9E68F213F6A600008B38A /* FEBioOutputSection.h in Headers */,\n\t\t\t\tD5B9E66A213F6A600008B38A /* FEBioModuleSection.h in Headers */,\n\t\t\t\tD5B9E67D213F6A600008B38A /* FEBioBoundarySection.h in Headers */,\n\t\t\t\tD5B9E684213F6A600008B38A /* FEBioConstraintsSection.h in Headers */,\n\t\t\t\tD5B9E663213F6A600008B38A /* FEBioLoadsSection.h in Headers */,\n\t\t\t\tD5709D9A22833011007CAB0A /* FEBioControlSection3.h in Headers */,\n\t\t\t\tD5B9E671213F6A600008B38A /* FEBioCodeSection.h in Headers */,\n\t\t\t\tD5C5881124D72F2900051B9D /* FEBioMeshSection.h in Headers */,\n\t\t\t\tD5B9E664213F6A600008B38A /* FEBioContactSection.h in Headers */,\n\t\t\t\tD5B9E665213F6A600008B38A /* FEBModel.h in Headers */,\n\t\t\t\tD5B9E66E213F6A600008B38A /* FEBioMaterialSection.h in Headers */,\n\t\t\t\tD5B9E683213F6A600008B38A /* FEBioGlobalsSection.h in Headers */,\n\t\t\t\tD5B9E68E213F6A600008B38A /* FEBioIncludeSection.h in Headers */,\n\t\t\t\tD5B9E660213F6A600008B38A /* FEBioLoadDataSection.h in Headers */,\n\t\t\t\tD5B9E679213F6A600008B38A /* xmltool.h in Headers */,\n\t\t\t\tD5B9E676213F6A600008B38A /* FEModelBuilder.h in Headers */,\n\t\t\t\tD5B9E661213F6A600008B38A /* FileImport.h in Headers */,\n\t\t\t\tD5B9E678213F6A600008B38A /* FEBioMeshDataSection.h in Headers */,\n\t\t\t\tD5B9E67B213F6A600008B38A /* XMLReader.h in Headers */,\n\t\t\t\tD5B9E670213F6A600008B38A /* FEBioGeometrySection.h in Headers */,\n\t\t\t\tD5B9E677213F6A600008B38A /* FEBioInitialSection.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5B9E61A213F6A510008B38A /* FEBioXML */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5B9E626213F6A510008B38A /* Build configuration list for PBXNativeTarget \"FEBioXML\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5B9E617213F6A510008B38A /* Sources */,\n\t\t\t\tD5B9E618213F6A510008B38A /* Frameworks */,\n\t\t\t\tD5B9E619213F6A510008B38A /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FEBioXML;\n\t\t\tproductName = FEBioXML;\n\t\t\tproductReference = D5B9E61B213F6A510008B38A /* libFEBioXML.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5B9E613213F6A510008B38A /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5B9E61A213F6A510008B38A = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5B9E616213F6A510008B38A /* Build configuration list for PBXProject \"FEBioXML\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5B9E612213F6A510008B38A;\n\t\t\tproductRefGroup = D5B9E61C213F6A510008B38A /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5B9E61A213F6A510008B38A /* FEBioXML */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5B9E617213F6A510008B38A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5B9E66F213F6A600008B38A /* FEBioMeshDataSection.cpp in Sources */,\n\t\t\t\tD5B9E67C213F6A600008B38A /* FEBioLoadsSection.cpp in Sources */,\n\t\t\t\tD5B9E67F213F6A600008B38A /* FEBioGeometrySection.cpp in Sources */,\n\t\t\t\tD5B9E669213F6A600008B38A /* FEBioIncludeSection.cpp in Sources */,\n\t\t\t\tD5B9E666213F6A600008B38A /* FEBioConstraintsSection.cpp in Sources */,\n\t\t\t\tD5B9E672213F6A600008B38A /* FEBioControlSection.cpp in Sources */,\n\t\t\t\tD5B9E68A213F6A600008B38A /* FileImport.cpp in Sources */,\n\t\t\t\tD5709D9E22833011007CAB0A /* FEBioInitialSection3.cpp in Sources */,\n\t\t\t\tD5B9E689213F6A600008B38A /* FEBModel.cpp in Sources */,\n\t\t\t\tD5613D39217B601A007CAB89 /* FEBioLoadDataSection3.cpp in Sources */,\n\t\t\t\tD5B9E662213F6A600008B38A /* FEBioDiscreteSection.cpp in Sources */,\n\t\t\t\tD565CDE9215D451B00E08ED6 /* FEBioGeometrySection3.cpp in Sources */,\n\t\t\t\tD5C5881024D72F2900051B9D /* FEBioMeshSection.cpp in Sources */,\n\t\t\t\tD5B9E686213F6A600008B38A /* xmltool.cpp in Sources */,\n\t\t\t\tD5B9E682213F6A600008B38A /* FEBioModuleSection.cpp in Sources */,\n\t\t\t\tD5709D9C22833011007CAB0A /* FEBioStepSection3.cpp in Sources */,\n\t\t\t\tD5B9E65F213F6A600008B38A /* FEBioBoundarySection.cpp in Sources */,\n\t\t\t\tD5B9E680213F6A600008B38A /* FEBioMaterialSection.cpp in Sources */,\n\t\t\t\tD5B9E674213F6A600008B38A /* FEBioInitialSection.cpp in Sources */,\n\t\t\t\tD5B9E688213F6A600008B38A /* FEBioImport.cpp in Sources */,\n\t\t\t\tD5B9E681213F6A600008B38A /* FERestartImport.cpp in Sources */,\n\t\t\t\tD5B9E667213F6A600008B38A /* FEBioRigidSection.cpp in Sources */,\n\t\t\t\tD5B9E690213F6A600008B38A /* FEBioContactSection.cpp in Sources */,\n\t\t\t\tD5B9E685213F6A600008B38A /* FEBioOutputSection.cpp in Sources */,\n\t\t\t\tD5B9E675213F6A600008B38A /* FEBioGlobalsSection.cpp in Sources */,\n\t\t\t\tD5B9E67E213F6A600008B38A /* XMLReader.cpp in Sources */,\n\t\t\t\tD5709D9B22833011007CAB0A /* FEBioBoundarySection3.cpp in Sources */,\n\t\t\t\tD565CDE8215D451B00E08ED6 /* FEBioMeshDataSection3.cpp in Sources */,\n\t\t\t\tD5B9E687213F6A600008B38A /* FEBioStepSection.cpp in Sources */,\n\t\t\t\tD5B9E66D213F6A600008B38A /* FEBioLoadDataSection.cpp in Sources */,\n\t\t\t\tD565CDE7215D451B00E08ED6 /* FEBioLoadsSection3.cpp in Sources */,\n\t\t\t\tD5613D38217B601A007CAB89 /* FEBioControlSection3.cpp in Sources */,\n\t\t\t\tD5B9E68D213F6A600008B38A /* FEBioCodeSection.cpp in Sources */,\n\t\t\t\tD5B805B8223BEC0200198805 /* FEBioMeshAdaptorSection.cpp in Sources */,\n\t\t\t\tD5B9E68B213F6A600008B38A /* FEModelBuilder.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5B9E624213F6A510008B38A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_CXX_LIBRARY = \"compiler-default\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5B9E625213F6A510008B38A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_CXX_LIBRARY = \"compiler-default\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5B9E627213F6A510008B38A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5B9E628213F6A510008B38A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5B9E616213F6A510008B38A /* Build configuration list for PBXProject \"FEBioXML\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5B9E624213F6A510008B38A /* Debug */,\n\t\t\t\tD5B9E625213F6A510008B38A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5B9E626213F6A510008B38A /* Build configuration list for PBXNativeTarget \"FEBioXML\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5B9E627213F6A510008B38A /* Debug */,\n\t\t\t\tD5B9E628213F6A510008B38A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5B9E613213F6A510008B38A /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FEBioXML/FEBioXML.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FEBioXML.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FEBioXML/FEBioXML.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/FECore/FECore.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD5006A7821AAF98100031CB6 /* FEElementShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5006A7621AAF98000031CB6 /* FEElementShape.cpp */; };\n\t\tD5006A7921AAF98100031CB6 /* FEElementShape.h in Headers */ = {isa = PBXBuildFile; fileRef = D5006A7721AAF98100031CB6 /* FEElementShape.h */; };\n\t\tD5044A672603DC8500C81BC4 /* FESurfacePairConstraintNL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5044A652603DC8500C81BC4 /* FESurfacePairConstraintNL.cpp */; };\n\t\tD5044A682603DC8500C81BC4 /* FESurfacePairConstraintNL.h in Headers */ = {isa = PBXBuildFile; fileRef = D5044A662603DC8500C81BC4 /* FESurfacePairConstraintNL.h */; };\n\t\tD50D09F0261E2ED3005564C6 /* expint_Ei.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50D09EE261E2ED3005564C6 /* expint_Ei.cpp */; };\n\t\tD50D09F1261E2ED3005564C6 /* expint_Ei.h in Headers */ = {isa = PBXBuildFile; fileRef = D50D09EF261E2ED3005564C6 /* expint_Ei.h */; };\n\t\tD50D879426D930DC0034E121 /* FELogElementVolume.h in Headers */ = {isa = PBXBuildFile; fileRef = D50D879026D930DB0034E121 /* FELogElementVolume.h */; };\n\t\tD50D879526D930DC0034E121 /* FELogEnclosedVolume.h in Headers */ = {isa = PBXBuildFile; fileRef = D50D879126D930DB0034E121 /* FELogEnclosedVolume.h */; };\n\t\tD50D879626D930DC0034E121 /* FELogEnclosedVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50D879226D930DC0034E121 /* FELogEnclosedVolume.cpp */; };\n\t\tD50D879726D930DC0034E121 /* FELogElementVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50D879326D930DC0034E121 /* FELogElementVolume.cpp */; };\n\t\tD510616D217CDD1600CF1690 /* FEPropertyT.h in Headers */ = {isa = PBXBuildFile; fileRef = D510616C217CDD1600CF1690 /* FEPropertyT.h */; };\n\t\tD51EBF9F217E3AED006F73C4 /* FEPIDController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D51EBF9D217E3AED006F73C4 /* FEPIDController.cpp */; };\n\t\tD51EBFA0217E3AED006F73C4 /* FEPIDController.h in Headers */ = {isa = PBXBuildFile; fileRef = D51EBF9E217E3AED006F73C4 /* FEPIDController.h */; };\n\t\tD52D83FC21CE89A200472620 /* FEDomainParameter.h in Headers */ = {isa = PBXBuildFile; fileRef = D52D83F121CE89A100472620 /* FEDomainParameter.h */; };\n\t\tD52D83FD21CE89A200472620 /* FEValuator.h in Headers */ = {isa = PBXBuildFile; fileRef = D52D83F221CE89A100472620 /* FEValuator.h */; };\n\t\tD52D83FE21CE89A200472620 /* FEVec3dValuator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D83F321CE89A100472620 /* FEVec3dValuator.cpp */; };\n\t\tD52D83FF21CE89A200472620 /* FEVec3dValuator.h in Headers */ = {isa = PBXBuildFile; fileRef = D52D83F421CE89A100472620 /* FEVec3dValuator.h */; };\n\t\tD52D840021CE89A200472620 /* FEScalarValuator.h in Headers */ = {isa = PBXBuildFile; fileRef = D52D83F521CE89A100472620 /* FEScalarValuator.h */; };\n\t\tD52D840221CE89A200472620 /* FEScalarValuator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D83F721CE89A200472620 /* FEScalarValuator.cpp */; };\n\t\tD52D840321CE89A200472620 /* FEMat3dValuator.h in Headers */ = {isa = PBXBuildFile; fileRef = D52D83F821CE89A200472620 /* FEMat3dValuator.h */; };\n\t\tD52D840421CE89A200472620 /* FEMat3dValuator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D83F921CE89A200472620 /* FEMat3dValuator.cpp */; };\n\t\tD52D840521CE89A200472620 /* FEDomainParameter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D83FA21CE89A200472620 /* FEDomainParameter.cpp */; };\n\t\tD5400D44249922C200570A47 /* FEMat3dsValuator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5400D42249922C200570A47 /* FEMat3dsValuator.cpp */; };\n\t\tD5400D45249922C200570A47 /* FEMat3dsValuator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5400D43249922C200570A47 /* FEMat3dsValuator.h */; };\n\t\tD54626F4260E6FDA007E2D8E /* FaceDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54626F2260E6FDA007E2D8E /* FaceDataRecord.cpp */; };\n\t\tD54626F5260E6FDA007E2D8E /* FaceDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D54626F3260E6FDA007E2D8E /* FaceDataRecord.h */; };\n\t\tD54A66AF257BDCB90023C8D1 /* FSPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54A66AD257BDCB90023C8D1 /* FSPath.cpp */; };\n\t\tD54A66B0257BDCB90023C8D1 /* FSPath.h in Headers */ = {isa = PBXBuildFile; fileRef = D54A66AE257BDCB90023C8D1 /* FSPath.h */; };\n\t\tD54E21AC2149BB56008A9DD3 /* FEElementSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E219E2149BB54008A9DD3 /* FEElementSet.cpp */; };\n\t\tD54E21AD2149BB56008A9DD3 /* FENode.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E219F2149BB54008A9DD3 /* FENode.h */; };\n\t\tD54E21AE2149BB56008A9DD3 /* FEFacetSet.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21A02149BB54008A9DD3 /* FEFacetSet.h */; };\n\t\tD54E21AF2149BB56008A9DD3 /* FESurfacePair.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21A12149BB54008A9DD3 /* FESurfacePair.h */; };\n\t\tD54E21B02149BB56008A9DD3 /* FESegmentSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21A22149BB54008A9DD3 /* FESegmentSet.cpp */; };\n\t\tD54E21B12149BB56008A9DD3 /* FEFacetSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21A32149BB55008A9DD3 /* FEFacetSet.cpp */; };\n\t\tD54E21B22149BB56008A9DD3 /* FESurfacePair.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21A42149BB55008A9DD3 /* FESurfacePair.cpp */; };\n\t\tD54E21B32149BB56008A9DD3 /* FESegmentSet.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21A52149BB55008A9DD3 /* FESegmentSet.h */; };\n\t\tD54E21B42149BB56008A9DD3 /* FENode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21A62149BB55008A9DD3 /* FENode.cpp */; };\n\t\tD54E21B52149BB56008A9DD3 /* FEElementSet.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21A72149BB55008A9DD3 /* FEElementSet.h */; };\n\t\tD54E21B62149BB56008A9DD3 /* FEDiscreteSet.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21A82149BB55008A9DD3 /* FEDiscreteSet.h */; };\n\t\tD54E21B72149BB56008A9DD3 /* FENodeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21A92149BB55008A9DD3 /* FENodeSet.h */; };\n\t\tD54E21B82149BB56008A9DD3 /* FENodeSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21AA2149BB55008A9DD3 /* FENodeSet.cpp */; };\n\t\tD54E21B92149BB56008A9DD3 /* FEDiscreteSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21AB2149BB56008A9DD3 /* FEDiscreteSet.cpp */; };\n\t\tD54E21DC21517EEE008A9DD3 /* MFunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21BA21517EEA008A9DD3 /* MFunctions.cpp */; };\n\t\tD54E21DD21517EEE008A9DD3 /* FEModelParam.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21BB21517EEA008A9DD3 /* FEModelParam.h */; };\n\t\tD54E21DE21517EEE008A9DD3 /* FEPrescribedBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21BC21517EEA008A9DD3 /* FEPrescribedBC.cpp */; };\n\t\tD54E21DF21517EEE008A9DD3 /* FENodalLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21BD21517EEA008A9DD3 /* FENodalLoad.cpp */; };\n\t\tD54E21E021517EEE008A9DD3 /* MIntegral.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21BE21517EEA008A9DD3 /* MIntegral.cpp */; };\n\t\tD54E21E121517EEE008A9DD3 /* MEvaluate.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21BF21517EEA008A9DD3 /* MEvaluate.h */; };\n\t\tD54E21E221517EEE008A9DD3 /* MTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21C021517EEA008A9DD3 /* MTypes.h */; };\n\t\tD54E21E321517EEE008A9DD3 /* MDerive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21C121517EEB008A9DD3 /* MDerive.cpp */; };\n\t\tD54E21E421517EEE008A9DD3 /* MObjBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21C221517EEB008A9DD3 /* MObjBuilder.cpp */; };\n\t\tD54E21E521517EEE008A9DD3 /* MathObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21C321517EEB008A9DD3 /* MathObject.h */; };\n\t\tD54E21E621517EEE008A9DD3 /* FEFixedBC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21C421517EEB008A9DD3 /* FEFixedBC.cpp */; };\n\t\tD54E21E721517EEE008A9DD3 /* FEFixedBC.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21C521517EEB008A9DD3 /* FEFixedBC.h */; };\n\t\tD54E21E821517EEE008A9DD3 /* MathObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21C621517EEB008A9DD3 /* MathObject.cpp */; };\n\t\tD54E21E921517EEE008A9DD3 /* MMath.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21C721517EEB008A9DD3 /* MMath.h */; };\n\t\tD54E21EA21517EEE008A9DD3 /* MObj2String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21C821517EEC008A9DD3 /* MObj2String.cpp */; };\n\t\tD54E21EB21517EEE008A9DD3 /* FEPrescribedBC.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21C921517EEC008A9DD3 /* FEPrescribedBC.h */; };\n\t\tD54E21EC21517EEE008A9DD3 /* MItem.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21CA21517EEC008A9DD3 /* MItem.h */; };\n\t\tD54E21ED21517EEE008A9DD3 /* MObjBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21CB21517EEC008A9DD3 /* MObjBuilder.h */; };\n\t\tD54E21EE21517EEE008A9DD3 /* MObj2String.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21CC21517EEC008A9DD3 /* MObj2String.h */; };\n\t\tD54E21EF21517EEE008A9DD3 /* FEModelParam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21CD21517EEC008A9DD3 /* FEModelParam.cpp */; };\n\t\tD54E21F021517EEE008A9DD3 /* FENodalLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21CE21517EEC008A9DD3 /* FENodalLoad.h */; };\n\t\tD54E21F121517EEE008A9DD3 /* FEPrescribedDOF.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21CF21517EEC008A9DD3 /* FEPrescribedDOF.h */; };\n\t\tD54E21F221517EEE008A9DD3 /* MMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21D021517EED008A9DD3 /* MMatrix.h */; };\n\t\tD54E21F321517EEE008A9DD3 /* MExpand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D121517EED008A9DD3 /* MExpand.cpp */; };\n\t\tD54E21F421517EEE008A9DD3 /* MItem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D221517EED008A9DD3 /* MItem.cpp */; };\n\t\tD54E21F521517EEE008A9DD3 /* MMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D321517EED008A9DD3 /* MMatrix.cpp */; };\n\t\tD54E21F621517EEE008A9DD3 /* MCollect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D421517EED008A9DD3 /* MCollect.cpp */; };\n\t\tD54E21F721517EEE008A9DD3 /* MReplace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D521517EED008A9DD3 /* MReplace.cpp */; };\n\t\tD54E21F821517EEE008A9DD3 /* MTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D621517EED008A9DD3 /* MTypes.cpp */; };\n\t\tD54E21F921517EEE008A9DD3 /* FEPrescribedDOF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D721517EED008A9DD3 /* FEPrescribedDOF.cpp */; };\n\t\tD54E21FA21517EEE008A9DD3 /* MSimplify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D821517EEE008A9DD3 /* MSimplify.cpp */; };\n\t\tD54E21FB21517EEE008A9DD3 /* MSolve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21D921517EEE008A9DD3 /* MSolve.cpp */; };\n\t\tD54E21FC21517EEE008A9DD3 /* MFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = D54E21DA21517EEE008A9DD3 /* MFunctions.h */; };\n\t\tD54E21FD21517EEE008A9DD3 /* MEvaluate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D54E21DB21517EEE008A9DD3 /* MEvaluate.cpp */; };\n\t\tD550834E24F086E300E919D8 /* EigenSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D550834C24F086E300E919D8 /* EigenSolver.cpp */; };\n\t\tD550834F24F086E300E919D8 /* EigenSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D550834D24F086E300E919D8 /* EigenSolver.h */; };\n\t\tD559C4C922D9169E00CDC2BD /* FEMat3dSphericalAngleMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D559C4C522D9169D00CDC2BD /* FEMat3dSphericalAngleMap.h */; };\n\t\tD559C4CA22D9169E00CDC2BD /* FEParabolicMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D559C4C622D9169D00CDC2BD /* FEParabolicMap.cpp */; };\n\t\tD559C4CB22D9169E00CDC2BD /* FEMat3dSphericalAngleMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D559C4C722D9169E00CDC2BD /* FEMat3dSphericalAngleMap.cpp */; };\n\t\tD559C4CC22D9169E00CDC2BD /* FEParabolicMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D559C4C822D9169E00CDC2BD /* FEParabolicMap.h */; };\n\t\tD5613D50217B604F007CAB89 /* FEMeshPartition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D3A217B604D007CAB89 /* FEMeshPartition.cpp */; };\n\t\tD5613D51217B604F007CAB89 /* FEMeshPartition.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D3B217B604D007CAB89 /* FEMeshPartition.h */; };\n\t\tD5613D52217B604F007CAB89 /* FESPRProjection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D3C217B604E007CAB89 /* FESPRProjection.cpp */; };\n\t\tD5613D53217B604F007CAB89 /* FESurfaceToSurfaceMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D3D217B604E007CAB89 /* FESurfaceToSurfaceMap.h */; };\n\t\tD5613D55217B604F007CAB89 /* FEMaterialPointProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D3F217B604E007CAB89 /* FEMaterialPointProperty.h */; };\n\t\tD5613D56217B604F007CAB89 /* FELoadController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D40217B604E007CAB89 /* FELoadController.cpp */; };\n\t\tD5613D57217B604F007CAB89 /* FEPointFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D41217B604E007CAB89 /* FEPointFunction.h */; };\n\t\tD5613D58217B604F007CAB89 /* writeplot.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D42217B604E007CAB89 /* writeplot.h */; };\n\t\tD5613D59217B604F007CAB89 /* FEMathController.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D43217B604E007CAB89 /* FEMathController.h */; };\n\t\tD5613D5A217B604F007CAB89 /* FEMathController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D44217B604E007CAB89 /* FEMathController.cpp */; };\n\t\tD5613D5B217B604F007CAB89 /* fecore_type.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D45217B604E007CAB89 /* fecore_type.h */; };\n\t\tD5613D5C217B604F007CAB89 /* fecore_type.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D46217B604E007CAB89 /* fecore_type.cpp */; };\n\t\tD5613D5D217B604F007CAB89 /* FELoadController.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D47217B604E007CAB89 /* FELoadController.h */; };\n\t\tD5613D5E217B604F007CAB89 /* FEConstDataGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D48217B604E007CAB89 /* FEConstDataGenerator.h */; };\n\t\tD5613D5F217B604F007CAB89 /* FELoadCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D49217B604E007CAB89 /* FELoadCurve.cpp */; };\n\t\tD5613D60217B604F007CAB89 /* FEPointFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D4A217B604E007CAB89 /* FEPointFunction.cpp */; };\n\t\tD5613D61217B604F007CAB89 /* FESPRProjection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D4B217B604E007CAB89 /* FESPRProjection.h */; };\n\t\tD5613D62217B604F007CAB89 /* writeplot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D4C217B604F007CAB89 /* writeplot.cpp */; };\n\t\tD5613D64217B604F007CAB89 /* FESurfaceToSurfaceMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5613D4E217B604F007CAB89 /* FESurfaceToSurfaceMap.cpp */; };\n\t\tD5613D65217B604F007CAB89 /* FELoadCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = D5613D4F217B604F007CAB89 /* FELoadCurve.h */; };\n\t\tD565CDDC215D290C00E08ED6 /* FEDomainMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDD4215D290B00E08ED6 /* FEDomainMap.h */; };\n\t\tD565CDDD215D290C00E08ED6 /* FEDomainList.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDD5215D290B00E08ED6 /* FEDomainList.h */; };\n\t\tD565CDDE215D290C00E08ED6 /* FEItemList.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDD6215D290B00E08ED6 /* FEItemList.h */; };\n\t\tD565CDDF215D290C00E08ED6 /* FEDataMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDD7215D290B00E08ED6 /* FEDataMap.cpp */; };\n\t\tD565CDE0215D290C00E08ED6 /* FEDomainList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDD8215D290B00E08ED6 /* FEDomainList.cpp */; };\n\t\tD565CDE1215D290C00E08ED6 /* FEDomainMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDD9215D290B00E08ED6 /* FEDomainMap.cpp */; };\n\t\tD565CDE2215D290C00E08ED6 /* FEItemList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D565CDDA215D290B00E08ED6 /* FEItemList.cpp */; };\n\t\tD565CDE3215D290C00E08ED6 /* FEDataMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D565CDDB215D290B00E08ED6 /* FEDataMap.h */; };\n\t\tD56B208D23AD5F94000AE9C2 /* FESolidElementShape.h in Headers */ = {isa = PBXBuildFile; fileRef = D56B208323AD5F94000AE9C2 /* FESolidElementShape.h */; };\n\t\tD56B208E23AD5F94000AE9C2 /* FESurfaceElementShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56B208423AD5F94000AE9C2 /* FESurfaceElementShape.cpp */; };\n\t\tD56B208F23AD5F94000AE9C2 /* FESurfaceElementShape.h in Headers */ = {isa = PBXBuildFile; fileRef = D56B208523AD5F94000AE9C2 /* FESurfaceElementShape.h */; };\n\t\tD56B209023AD5F94000AE9C2 /* FESolidElementShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56B208623AD5F94000AE9C2 /* FESolidElementShape.cpp */; };\n\t\tD56B209123AD5F94000AE9C2 /* FEShellElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D56B208723AD5F94000AE9C2 /* FEShellElement.h */; };\n\t\tD56B209223AD5F94000AE9C2 /* FESolidElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D56B208823AD5F94000AE9C2 /* FESolidElement.h */; };\n\t\tD56B209323AD5F94000AE9C2 /* FESolidElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56B208923AD5F94000AE9C2 /* FESolidElement.cpp */; };\n\t\tD56B209423AD5F94000AE9C2 /* FEShellElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56B208A23AD5F94000AE9C2 /* FEShellElement.cpp */; };\n\t\tD56B209523AD5F94000AE9C2 /* FESurfaceElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D56B208B23AD5F94000AE9C2 /* FESurfaceElement.cpp */; };\n\t\tD56B209623AD5F94000AE9C2 /* FESurfaceElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D56B208C23AD5F94000AE9C2 /* FESurfaceElement.h */; };\n\t\tD5709DA722833034007CAB0A /* FENodeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709DA122833034007CAB0A /* FENodeList.cpp */; };\n\t\tD5709DA822833034007CAB0A /* FENodeList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709DA222833034007CAB0A /* FENodeList.h */; };\n\t\tD5709DA922833034007CAB0A /* FEDofList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5709DA322833034007CAB0A /* FEDofList.cpp */; };\n\t\tD5709DAB22833034007CAB0A /* FEDofList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5709DA522833034007CAB0A /* FEDofList.h */; };\n\t\tD58FA88224A1631400FC768B /* FEConstValueVec3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D58FA88024A1631400FC768B /* FEConstValueVec3.cpp */; };\n\t\tD58FA88324A1631400FC768B /* FEConstValueVec3.h in Headers */ = {isa = PBXBuildFile; fileRef = D58FA88124A1631400FC768B /* FEConstValueVec3.h */; };\n\t\tD5946E2124B524F90009C22F /* mathalg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5946E1F24B524F90009C22F /* mathalg.cpp */; };\n\t\tD5946E2224B524F90009C22F /* mathalg.h in Headers */ = {isa = PBXBuildFile; fileRef = D5946E2024B524F90009C22F /* mathalg.h */; };\n\t\tD5A37D2A2286167300867D77 /* FEOctreeSearch.h in Headers */ = {isa = PBXBuildFile; fileRef = D5A37D282286167300867D77 /* FEOctreeSearch.h */; };\n\t\tD5A37D2B2286167300867D77 /* FEOctreeSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5A37D292286167300867D77 /* FEOctreeSearch.cpp */; };\n\t\tD5A56F8323D4FBFE002F28CA /* tens4dmm.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5A56F8223D4FBFE002F28CA /* tens4dmm.hpp */; };\n\t\tD5AB623F220289E200AAF141 /* FEEdgeList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AB623D220289E200AAF141 /* FEEdgeList.h */; };\n\t\tD5AB6240220289E200AAF141 /* FEEdgeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AB623E220289E200AAF141 /* FEEdgeList.cpp */; };\n\t\tD5AE8CC62592ECCB003AC08B /* FEMeshAdaptorCriterion.h in Headers */ = {isa = PBXBuildFile; fileRef = D5AE8CC42592ECCB003AC08B /* FEMeshAdaptorCriterion.h */; };\n\t\tD5AE8CC72592ECCB003AC08B /* FEMeshAdaptorCriterion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5AE8CC52592ECCB003AC08B /* FEMeshAdaptorCriterion.cpp */; };\n\t\tD5B805B3223BE2DC00198805 /* FEMeshAdaptor.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B805B1223BE2DC00198805 /* FEMeshAdaptor.h */; };\n\t\tD5B805B4223BE2DC00198805 /* FEMeshAdaptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B805B2223BE2DC00198805 /* FEMeshAdaptor.cpp */; };\n\t\tD5B9E4FF213F67DE0008B38A /* FELinearConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3EC213F67DE0008B38A /* FELinearConstraint.h */; };\n\t\tD5B9E500213F67DE0008B38A /* MatrixOperator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3ED213F67DE0008B38A /* MatrixOperator.h */; };\n\t\tD5B9E501213F67DE0008B38A /* FEDiscreteMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3EE213F67DE0008B38A /* FEDiscreteMaterial.h */; };\n\t\tD5B9E502213F67DE0008B38A /* SparseMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3EF213F67DE0008B38A /* SparseMatrix.h */; };\n\t\tD5B9E503213F67DE0008B38A /* mortar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E3F0213F67DE0008B38A /* mortar.cpp */; };\n\t\tD5B9E504213F67DE0008B38A /* JFNKMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E3F1213F67DE0008B38A /* JFNKMatrix.cpp */; };\n\t\tD5B9E505213F67DE0008B38A /* FEBroydenStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3F2213F67DE0008B38A /* FEBroydenStrategy.h */; };\n\t\tD5B9E506213F67DE0008B38A /* NodeDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3F3213F67DE0008B38A /* NodeDataRecord.h */; };\n\t\tD5B9E507213F67DE0008B38A /* FECoreFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3F4213F67DE0008B38A /* FECoreFactory.h */; };\n\t\tD5B9E508213F67DE0008B38A /* FEGlobalData.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3F5213F67DE0008B38A /* FEGlobalData.h */; };\n\t\tD5B9E509213F67DE0008B38A /* FECube.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E3F6213F67DE0008B38A /* FECube.cpp */; };\n\t\tD5B9E50B213F67DE0008B38A /* FENodeDataMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E3F8213F67DE0008B38A /* FENodeDataMap.cpp */; };\n\t\tD5B9E50C213F67DE0008B38A /* tens6d.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E3F9213F67DE0008B38A /* tens6d.cpp */; };\n\t\tD5B9E50D213F67DE0008B38A /* FEFunction1D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E3FA213F67DE0008B38A /* FEFunction1D.cpp */; };\n\t\tD5B9E50E213F67DE0008B38A /* tens4dms.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3FB213F67DE0008B38A /* tens4dms.hpp */; };\n\t\tD5B9E50F213F67DE0008B38A /* FEClosestPointProjection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3FC213F67DE0008B38A /* FEClosestPointProjection.h */; };\n\t\tD5B9E510213F67DE0008B38A /* FEMaterial.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3FD213F67DE0008B38A /* FEMaterial.h */; };\n\t\tD5B9E511213F67DE0008B38A /* Archive.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3FE213F67DE0008B38A /* Archive.h */; };\n\t\tD5B9E512213F67DE0008B38A /* FELinearSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E3FF213F67DE0008B38A /* FELinearSystem.h */; };\n\t\tD5B9E513213F67DE0008B38A /* FECallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E400213F67DE0008B38A /* FECallback.cpp */; };\n\t\tD5B9E516213F67DE0008B38A /* tens4d.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E403213F67DE0008B38A /* tens4d.hpp */; };\n\t\tD5B9E517213F67DE0008B38A /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E404213F67DE0008B38A /* version.h */; };\n\t\tD5B9E518213F67DE0008B38A /* fecore_debug.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E405213F67DE0008B38A /* fecore_debug.h */; };\n\t\tD5B9E519213F67DE0008B38A /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E406213F67DE0008B38A /* stdafx.h */; };\n\t\tD5B9E51B213F67DE0008B38A /* tens5d.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E408213F67DE0008B38A /* tens5d.hpp */; };\n\t\tD5B9E51C213F67DE0008B38A /* tensor_base.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E409213F67DE0008B38A /* tensor_base.h */; };\n\t\tD5B9E51D213F67DE0008B38A /* Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E40A213F67DE0008B38A /* Image.cpp */; };\n\t\tD5B9E51E213F67DE0008B38A /* FEInitialCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E40B213F67DE0008B38A /* FEInitialCondition.h */; };\n\t\tD5B9E51F213F67DE0008B38A /* Callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E40C213F67DE0008B38A /* Callback.cpp */; };\n\t\tD5B9E520213F67DE0008B38A /* FEEdge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E40D213F67DE0008B38A /* FEEdge.cpp */; };\n\t\tD5B9E521213F67DE0008B38A /* mat3d.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E40E213F67DE0008B38A /* mat3d.hpp */; };\n\t\tD5B9E522213F67DE0008B38A /* FEParamValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E40F213F67DE0008B38A /* FEParamValidator.h */; };\n\t\tD5B9E523213F67DE0008B38A /* FEBoundingBox.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E410213F67DE0008B38A /* FEBoundingBox.h */; };\n\t\tD5B9E524213F67DE0008B38A /* FEModelLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E411213F67DE0008B38A /* FEModelLoad.cpp */; };\n\t\tD5B9E525213F67DE0008B38A /* svd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E412213F67DE0008B38A /* svd.cpp */; };\n\t\tD5B9E527213F67DE0008B38A /* FEElementTraits.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E414213F67DE0008B38A /* FEElementTraits.h */; };\n\t\tD5B9E528213F67DE0008B38A /* FEAnalysis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E415213F67DE0008B38A /* FEAnalysis.cpp */; };\n\t\tD5B9E529213F67DE0008B38A /* FEPlotData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E416213F67DE0008B38A /* FEPlotData.cpp */; };\n\t\tD5B9E52A213F67DE0008B38A /* FEDataGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E417213F67DE0008B38A /* FEDataGenerator.cpp */; };\n\t\tD5B9E52B213F67DE0008B38A /* FEDataMathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E418213F67DE0008B38A /* FEDataMathGenerator.h */; };\n\t\tD5B9E52E213F67DE0008B38A /* FEFunction1D.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E41B213F67DE0008B38A /* FEFunction1D.h */; };\n\t\tD5B9E52F213F67DE0008B38A /* LinearSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E41C213F67DE0008B38A /* LinearSolver.cpp */; };\n\t\tD5B9E530213F67DE0008B38A /* quatd.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E41D213F67DE0008B38A /* quatd.h */; };\n\t\tD5B9E531213F67DE0008B38A /* Integrate.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E41E213F67DE0008B38A /* Integrate.h */; };\n\t\tD5B9E532213F67DE0008B38A /* FEMaterialPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E41F213F67DE0008B38A /* FEMaterialPoint.cpp */; };\n\t\tD5B9E533213F67DE0008B38A /* FENLConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E420213F67DE0008B38A /* FENLConstraint.h */; };\n\t\tD5B9E534213F67DE0008B38A /* FEBodyLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E421213F67DE0008B38A /* FEBodyLoad.cpp */; };\n\t\tD5B9E536213F67DE0008B38A /* NodeDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E423213F67DE0008B38A /* NodeDataRecord.cpp */; };\n\t\tD5B9E539213F67DE0008B38A /* BFGSSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E426213F67DE0008B38A /* BFGSSolver.cpp */; };\n\t\tD5B9E53A213F67DE0008B38A /* Integrate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E427213F67DE0008B38A /* Integrate.cpp */; };\n\t\tD5B9E53B213F67DE0008B38A /* FEModelData.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E428213F67DE0008B38A /* FEModelData.h */; };\n\t\tD5B9E53C213F67DE0008B38A /* FEModelLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E429213F67DE0008B38A /* FEModelLoad.h */; };\n\t\tD5B9E53D213F67DE0008B38A /* FEBroydenStrategy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E42A213F67DE0008B38A /* FEBroydenStrategy.cpp */; };\n\t\tD5B9E53E213F67DE0008B38A /* eig3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E42B213F67DE0008B38A /* eig3.cpp */; };\n\t\tD5B9E540213F67DE0008B38A /* FELevelStructure.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E42D213F67DE0008B38A /* FELevelStructure.h */; };\n\t\tD5B9E541213F67DE0008B38A /* DataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E42E213F67DE0008B38A /* DataStore.h */; };\n\t\tD5B9E542213F67DE0008B38A /* FEParam.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E42F213F67DE0008B38A /* FEParam.h */; };\n\t\tD5B9E543213F67DE0008B38A /* ParamString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E430213F67DE0008B38A /* ParamString.cpp */; };\n\t\tD5B9E544213F67DE0008B38A /* tens4d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E431213F67DE0008B38A /* tens4d.h */; };\n\t\tD5B9E545213F67DE0008B38A /* FENormalProjection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E432213F67DE0008B38A /* FENormalProjection.cpp */; };\n\t\tD5B9E546213F67DE0008B38A /* FEParam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E433213F67DE0008B38A /* FEParam.cpp */; };\n\t\tD5B9E547213F67DE0008B38A /* FENodeReorder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E434213F67DE0008B38A /* FENodeReorder.cpp */; };\n\t\tD5B9E548213F67DE0008B38A /* FETrussDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E435213F67DE0008B38A /* FETrussDomain.h */; };\n\t\tD5B9E549213F67DE0008B38A /* FEParameterList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E436213F67DE0008B38A /* FEParameterList.cpp */; };\n\t\tD5B9E54A213F67DE0008B38A /* FEException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E437213F67DE0008B38A /* FEException.cpp */; };\n\t\tD5B9E54B213F67DE0008B38A /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E438213F67DE0008B38A /* Timer.cpp */; };\n\t\tD5B9E54C213F67DE0008B38A /* FETimeStepController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E439213F67DE0008B38A /* FETimeStepController.cpp */; };\n\t\tD5B9E54D213F67DE0008B38A /* DumpStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E43A213F67DE0008B38A /* DumpStream.h */; };\n\t\tD5B9E54E213F67DE0008B38A /* FENewtonStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E43B213F67DE0008B38A /* FENewtonStrategy.h */; };\n\t\tD5B9E54F213F67DE0008B38A /* FENewtonSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E43C213F67DE0008B38A /* FENewtonSolver.h */; };\n\t\tD5B9E550213F67DE0008B38A /* FEElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E43D213F67DE0008B38A /* FEElement.h */; };\n\t\tD5B9E551213F67DE0008B38A /* vec2d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E43E213F67DE0008B38A /* vec2d.h */; };\n\t\tD5B9E552213F67DE0008B38A /* FESurfaceLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E43F213F67DE0008B38A /* FESurfaceLoad.h */; };\n\t\tD5B9E553213F67DE0008B38A /* FEDataArray.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E440213F67DE0008B38A /* FEDataArray.h */; };\n\t\tD5B9E554213F67DE0008B38A /* FESolidDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E441213F67DE0008B38A /* FESolidDomain.cpp */; };\n\t\tD5B9E555213F67DE0008B38A /* FEEdgeLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E442213F67DE0008B38A /* FEEdgeLoad.h */; };\n\t\tD5B9E556213F67DE0008B38A /* tens5d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E443213F67DE0008B38A /* tens5d.h */; };\n\t\tD5B9E557213F67DE0008B38A /* mortar.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E444213F67DE0008B38A /* mortar.h */; };\n\t\tD5B9E558213F67DE0008B38A /* FENewtonStrategy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E445213F67DE0008B38A /* FENewtonStrategy.cpp */; };\n\t\tD5B9E559213F67DE0008B38A /* ParamString.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E446213F67DE0008B38A /* ParamString.h */; };\n\t\tD5B9E55A213F67DE0008B38A /* matrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E447213F67DE0008B38A /* matrix.h */; };\n\t\tD5B9E55B213F67DE0008B38A /* DumpMemStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E448213F67DE0008B38A /* DumpMemStream.cpp */; };\n\t\tD5B9E55C213F67DE0008B38A /* FEParamValidator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E449213F67DE0008B38A /* FEParamValidator.cpp */; };\n\t\tD5B9E55D213F67DE0008B38A /* FECoreBase.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E44A213F67DE0008B38A /* FECoreBase.h */; };\n\t\tD5B9E55F213F67DE0008B38A /* FEGlobalMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E44C213F67DE0008B38A /* FEGlobalMatrix.h */; };\n\t\tD5B9E560213F67DE0008B38A /* vec3d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E44D213F67DE0008B38A /* vec3d.h */; };\n\t\tD5B9E561213F67DE0008B38A /* qsort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E44E213F67DE0008B38A /* qsort.cpp */; };\n\t\tD5B9E562213F67DE0008B38A /* FECoreKernel.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E44F213F67DE0008B38A /* FECoreKernel.h */; };\n\t\tD5B9E563213F67DE0008B38A /* FEModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E450213F67DE0008B38A /* FEModel.h */; };\n\t\tD5B9E564213F67DE0008B38A /* FEElemElemList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E451213F67DE0008B38A /* FEElemElemList.cpp */; };\n\t\tD5B9E565213F67DE0008B38A /* tens3d.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E452213F67DE0008B38A /* tens3d.hpp */; };\n\t\tD5B9E566213F67DE0008B38A /* FEProperty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E453213F67DE0008B38A /* FEProperty.cpp */; };\n\t\tD5B9E567213F67DE0008B38A /* DumpFile.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E454213F67DE0008B38A /* DumpFile.h */; };\n\t\tD5B9E568213F67DE0008B38A /* FENNQuery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E455213F67DE0008B38A /* FENNQuery.cpp */; };\n\t\tD5B9E569213F67DE0008B38A /* FECoreTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E456213F67DE0008B38A /* FECoreTask.cpp */; };\n\t\tD5B9E56B213F67DE0008B38A /* FELinearConstraintManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E458213F67DE0008B38A /* FELinearConstraintManager.h */; };\n\t\tD5B9E56C213F67DE0008B38A /* FEModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E459213F67DE0008B38A /* FEModel.cpp */; };\n\t\tD5B9E56D213F67DE0008B38A /* SparseMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E45A213F67DE0008B38A /* SparseMatrix.cpp */; };\n\t\tD5B9E56F213F67DE0008B38A /* FEGlobalData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E45C213F67DE0008B38A /* FEGlobalData.cpp */; };\n\t\tD5B9E570213F67DE0008B38A /* FESurfacePairConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E45D213F67DE0008B38A /* FESurfacePairConstraint.cpp */; };\n\t\tD5B9E571213F67DE0008B38A /* eig3.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E45E213F67DE0008B38A /* eig3.h */; };\n\t\tD5B9E572213F67DE0008B38A /* ElementDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E45F213F67DE0008B38A /* ElementDataRecord.cpp */; };\n\t\tD5B9E573213F67DE0008B38A /* fecore_api.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E460213F67DE0008B38A /* fecore_api.h */; };\n\t\tD5B9E574213F67DE0008B38A /* FEDiscreteDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E461213F67DE0008B38A /* FEDiscreteDomain.cpp */; };\n\t\tD5B9E575213F67DE0008B38A /* tens6d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E462213F67DE0008B38A /* tens6d.h */; };\n\t\tD5B9E576213F67DE0008B38A /* FECorePlot.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E463213F67DE0008B38A /* FECorePlot.h */; };\n\t\tD5B9E577213F67DE0008B38A /* FENodeElemList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E464213F67DE0008B38A /* FENodeElemList.cpp */; };\n\t\tD5B9E578213F67DE0008B38A /* FEElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E465213F67DE0008B38A /* FEElement.cpp */; };\n\t\tD5B9E579213F67DE0008B38A /* Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E466213F67DE0008B38A /* Timer.h */; };\n\t\tD5B9E57A213F67DE0008B38A /* FECoreTask.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E467213F67DE0008B38A /* FECoreTask.h */; };\n\t\tD5B9E57B213F67DE0008B38A /* FEProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E468213F67DE0008B38A /* FEProperty.h */; };\n\t\tD5B9E57C213F67DE0008B38A /* FEDataArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E469213F67DE0008B38A /* FEDataArray.cpp */; };\n\t\tD5B9E57D213F67DE0008B38A /* FELinearConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E46A213F67DE0008B38A /* FELinearConstraint.cpp */; };\n\t\tD5B9E57E213F67DE0008B38A /* LinearSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E46B213F67DE0008B38A /* LinearSolver.h */; };\n\t\tD5B9E57F213F67DE0008B38A /* FESurface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E46C213F67DE0008B38A /* FESurface.cpp */; };\n\t\tD5B9E580213F67DE0008B38A /* MatrixProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E46D213F67DE0008B38A /* MatrixProfile.cpp */; };\n\t\tD5B9E581213F67DE0008B38A /* FESolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E46E213F67DE0008B38A /* FESolver.h */; };\n\t\tD5B9E582213F67DE0008B38A /* targetver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E46F213F67DE0008B38A /* targetver.h */; };\n\t\tD5B9E583213F67DE0008B38A /* FEShellDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E470213F67DE0008B38A /* FEShellDomain.cpp */; };\n\t\tD5B9E584213F67DE0008B38A /* vector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E471213F67DE0008B38A /* vector.cpp */; };\n\t\tD5B9E585213F67DE0008B38A /* DataStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E472213F67DE0008B38A /* DataStore.cpp */; };\n\t\tD5B9E586213F67DE0008B38A /* FESolidDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E473213F67DE0008B38A /* FESolidDomain.h */; };\n\t\tD5B9E587213F67DE0008B38A /* NLConstraintDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E474213F67DE0008B38A /* NLConstraintDataRecord.cpp */; };\n\t\tD5B9E588213F67DE0008B38A /* FEBodyLoad.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E475213F67DE0008B38A /* FEBodyLoad.h */; };\n\t\tD5B9E589213F67DE0008B38A /* FEModelComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E476213F67DE0008B38A /* FEModelComponent.h */; };\n\t\tD5B9E58A213F67DE0008B38A /* FEElementLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E477213F67DE0008B38A /* FEElementLibrary.h */; };\n\t\tD5B9E58B213F67DE0008B38A /* FEException.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E478213F67DE0008B38A /* FEException.h */; };\n\t\tD5B9E58C213F67DE0008B38A /* FELinearSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E479213F67DE0008B38A /* FELinearSolver.h */; };\n\t\tD5B9E58D213F67DE0008B38A /* FECore.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E47A213F67DE0008B38A /* FECore.h */; };\n\t\tD5B9E58E213F67DE0008B38A /* FEElementLibrary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E47B213F67DE0008B38A /* FEElementLibrary.cpp */; };\n\t\tD5B9E590213F67DE0008B38A /* FEDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E47D213F67DE0008B38A /* FEDomain.cpp */; };\n\t\tD5B9E591213F67DE0008B38A /* FENormalProjection.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E47E213F67DE0008B38A /* FENormalProjection.h */; };\n\t\tD5B9E592213F67DE0008B38A /* FEDataExport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E47F213F67DE0008B38A /* FEDataExport.cpp */; };\n\t\tD5B9E593213F67DE0008B38A /* FEModelData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E480213F67DE0008B38A /* FEModelData.cpp */; };\n\t\tD5B9E594213F67DE0008B38A /* FEMaterialPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E481213F67DE0008B38A /* FEMaterialPoint.h */; };\n\t\tD5B9E595213F67DE0008B38A /* MatrixProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E482213F67DE0008B38A /* MatrixProfile.h */; };\n\t\tD5B9E596213F67DE0008B38A /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E483213F67DE0008B38A /* log.h */; };\n\t\tD5B9E597213F67DE0008B38A /* FETimeInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E484213F67DE0008B38A /* FETimeInfo.cpp */; };\n\t\tD5B9E598213F67DE0008B38A /* tens3drs.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E485213F67DE0008B38A /* tens3drs.hpp */; };\n\t\tD5B9E599213F67DE0008B38A /* FESolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E486213F67DE0008B38A /* FESolver.cpp */; };\n\t\tD5B9E59A213F67DE0008B38A /* FEEdge.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E487213F67DE0008B38A /* FEEdge.h */; };\n\t\tD5B9E59B213F67DE0008B38A /* FEDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E488213F67DE0008B38A /* FEDomain.h */; };\n\t\tD5B9E59C213F67DE0008B38A /* FESurfaceLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E489213F67DE0008B38A /* FESurfaceLoad.cpp */; };\n\t\tD5B9E59E213F67DE0008B38A /* tens3d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E48B213F67DE0008B38A /* tens3d.h */; };\n\t\tD5B9E59F213F67DE0008B38A /* FECoreKernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E48C213F67DE0008B38A /* FECoreKernel.cpp */; };\n\t\tD5B9E5A0213F67DE0008B38A /* FESurfaceConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E48D213F67DE0008B38A /* FESurfaceConstraint.h */; };\n\t\tD5B9E5A1213F67DE0008B38A /* FEBox.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E48E213F67DE0008B38A /* FEBox.h */; };\n\t\tD5B9E5A2213F67DE0008B38A /* FEClosestPointProjection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E48F213F67DE0008B38A /* FEClosestPointProjection.cpp */; };\n\t\tD5B9E5A3213F67DE0008B38A /* FENodeNodeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E490213F67DE0008B38A /* FENodeNodeList.cpp */; };\n\t\tD5B9E5A4213F67DE0008B38A /* FEModelComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E491213F67DE0008B38A /* FEModelComponent.cpp */; };\n\t\tD5B9E5A5213F67DE0008B38A /* JFNKStrategy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E492213F67DE0008B38A /* JFNKStrategy.cpp */; };\n\t\tD5B9E5A6213F67DE0008B38A /* FESurfaceConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E493213F67DE0008B38A /* FESurfaceConstraint.cpp */; };\n\t\tD5B9E5A7213F67DE0008B38A /* tools.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E494213F67DE0008B38A /* tools.h */; };\n\t\tD5B9E5A8213F67DE0008B38A /* FEShellDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E495213F67DE0008B38A /* FEShellDomain.h */; };\n\t\tD5B9E5A9213F67DE0008B38A /* FENewtonSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E496213F67DE0008B38A /* FENewtonSolver.cpp */; };\n\t\tD5B9E5AA213F67DE0008B38A /* FELinearSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E497213F67DE0008B38A /* FELinearSystem.cpp */; };\n\t\tD5B9E5AB213F67DE0008B38A /* tens4ds.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E498213F67DE0008B38A /* tens4ds.hpp */; };\n\t\tD5B9E5AC213F67DE0008B38A /* FECoreBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E499213F67DE0008B38A /* FECoreBase.cpp */; };\n\t\tD5B9E5AD213F67DE0008B38A /* FEElementList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E49A213F67DE0008B38A /* FEElementList.cpp */; };\n\t\tD5B9E5AE213F67DE0008B38A /* DumpFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E49B213F67DE0008B38A /* DumpFile.cpp */; };\n\t\tD5B9E5AF213F67DE0008B38A /* quatd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E49C213F67DE0008B38A /* quatd.cpp */; };\n\t\tD5B9E5B0213F67DE0008B38A /* FECallBack.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E49D213F67DE0008B38A /* FECallBack.h */; };\n\t\tD5B9E5B1213F67DE0008B38A /* DumpStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E49E213F67DE0008B38A /* DumpStream.cpp */; };\n\t\tD5B9E5B2213F67DE0008B38A /* matrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E49F213F67DE0008B38A /* matrix.cpp */; };\n\t\tD5B9E5B3213F67DE0008B38A /* tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4A0213F67DE0008B38A /* tools.cpp */; };\n\t\tD5B9E5B4213F67DE0008B38A /* FEElemElemList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4A1213F67DE0008B38A /* FEElemElemList.h */; };\n\t\tD5B9E5B5213F67DE0008B38A /* tens3d.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4A2213F67DE0008B38A /* tens3d.cpp */; };\n\t\tD5B9E5B6213F67DE0008B38A /* FEAnalysis.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4A3213F67DE0008B38A /* FEAnalysis.h */; };\n\t\tD5B9E5B7213F67DE0008B38A /* sdk.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4A4213F67DE0008B38A /* sdk.h */; };\n\t\tD5B9E5B8213F67DE0008B38A /* FECoreFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4A5213F67DE0008B38A /* FECoreFactory.cpp */; };\n\t\tD5B9E5B9213F67DE0008B38A /* sys.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4A6213F67DE0008B38A /* sys.h */; };\n\t\tD5B9E5BA213F67DE0008B38A /* JFNKStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4A7213F67DE0008B38A /* JFNKStrategy.h */; };\n\t\tD5B9E5BB213F67DE0008B38A /* FEDomain2D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4A8213F67DE0008B38A /* FEDomain2D.cpp */; };\n\t\tD5B9E5BC213F67DE0008B38A /* tens6ds.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4A9213F67DE0008B38A /* tens6ds.hpp */; };\n\t\tD5B9E5BD213F67DE0008B38A /* FELineSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4AA213F67DE0008B38A /* FELineSearch.cpp */; };\n\t\tD5B9E5BE213F67DE0008B38A /* DataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4AB213F67DE0008B38A /* DataRecord.h */; };\n\t\tD5B9E5BF213F67DE0008B38A /* JFNKMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4AC213F67DE0008B38A /* JFNKMatrix.h */; };\n\t\tD5B9E5C0213F67DE0008B38A /* FETimeInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4AD213F67DE0008B38A /* FETimeInfo.h */; };\n\t\tD5B9E5C1213F67DE0008B38A /* FEDataStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4AE213F67DE0008B38A /* FEDataStream.h */; };\n\t\tD5B9E5C2213F67DE0008B38A /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4AF213F67DE0008B38A /* table.h */; };\n\t\tD5B9E5C3213F67DE0008B38A /* FENLConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4B0213F67DE0008B38A /* FENLConstraint.cpp */; };\n\t\tD5B9E5C4213F67DE0008B38A /* mat2d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B1213F67DE0008B38A /* mat2d.h */; };\n\t\tD5B9E5C5213F67DE0008B38A /* FELineSearch.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B2213F67DE0008B38A /* FELineSearch.h */; };\n\t\tD5B9E5C6213F67DE0008B38A /* FENNQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B3213F67DE0008B38A /* FENNQuery.h */; };\n\t\tD5B9E5C8213F67DE0008B38A /* FELinearConstraintManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4B5213F67DE0008B38A /* FELinearConstraintManager.cpp */; };\n\t\tD5B9E5C9213F67DE0008B38A /* FEDataGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B6213F67DE0008B38A /* FEDataGenerator.h */; };\n\t\tD5B9E5CA213F67DE0008B38A /* FEBoundaryCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B7213F67DE0008B38A /* FEBoundaryCondition.h */; };\n\t\tD5B9E5CB213F67DE0008B38A /* FEParameterList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B8213F67DE0008B38A /* FEParameterList.h */; };\n\t\tD5B9E5CC213F67DE0008B38A /* BFGSSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4B9213F67DE0008B38A /* BFGSSolver.h */; };\n\t\tD5B9E5CD213F67DE0008B38A /* DOFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4BA213F67DE0008B38A /* DOFS.cpp */; };\n\t\tD5B9E5CE213F67DE0008B38A /* FETrussDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4BB213F67DE0008B38A /* FETrussDomain.cpp */; };\n\t\tD5B9E5CF213F67DE0008B38A /* FESurfaceMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4BC213F67DE0008B38A /* FESurfaceMap.cpp */; };\n\t\tD5B9E5D0213F67DE0008B38A /* FEGlobalVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4BD213F67DE0008B38A /* FEGlobalVector.cpp */; };\n\t\tD5B9E5D1213F67DE0008B38A /* FENodeNodeList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4BE213F67DE0008B38A /* FENodeNodeList.h */; };\n\t\tD5B9E5D2213F67DE0008B38A /* DOFS.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4BF213F67DE0008B38A /* DOFS.h */; };\n\t\tD5B9E5D3213F67DE0008B38A /* FECorePlot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4C0213F67DE0008B38A /* FECorePlot.cpp */; };\n\t\tD5B9E5D5213F67DE0008B38A /* FESurfacePairConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4C2213F67DE0008B38A /* FESurfacePairConstraint.h */; };\n\t\tD5B9E5D7213F67DE0008B38A /* FEDiscreteDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4C4213F67DE0008B38A /* FEDiscreteDomain.h */; };\n\t\tD5B9E5D9213F67DE0008B38A /* FENodeElemList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4C6213F67DE0008B38A /* FENodeElemList.h */; };\n\t\tD5B9E5DA213F67DE0008B38A /* FESurfaceMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4C7213F67DE0008B38A /* FESurfaceMap.h */; };\n\t\tD5B9E5DB213F67DE0008B38A /* tens6d.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4C8213F67DE0008B38A /* tens6d.hpp */; };\n\t\tD5B9E5DC213F67DE0008B38A /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4C9213F67DE0008B38A /* stdafx.cpp */; };\n\t\tD5B9E5DD213F67DE0008B38A /* mat6d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4CA213F67DE0008B38A /* mat6d.h */; };\n\t\tD5B9E5DF213F67DE0008B38A /* FEGlobalVector.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4CC213F67DE0008B38A /* FEGlobalVector.h */; };\n\t\tD5B9E5E0213F67DE0008B38A /* FELevelStructure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4CD213F67DE0008B38A /* FELevelStructure.cpp */; };\n\t\tD5B9E5E1213F67DE0008B38A /* fecore_debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4CE213F67DE0008B38A /* fecore_debug.cpp */; };\n\t\tD5B9E5E2213F67DE0008B38A /* FEEdgeLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4CF213F67DE0008B38A /* FEEdgeLoad.cpp */; };\n\t\tD5B9E5E3213F67DE0008B38A /* FEMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4D0213F67DE0008B38A /* FEMesh.cpp */; };\n\t\tD5B9E5E5213F67DE0008B38A /* tens3ds.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4D2213F67DE0008B38A /* tens3ds.hpp */; };\n\t\tD5B9E5E6213F67DE0008B38A /* FETimeStepController.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4D3213F67DE0008B38A /* FETimeStepController.h */; };\n\t\tD5B9E5E7213F67DE0008B38A /* vector.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4D4213F67DE0008B38A /* vector.h */; };\n\t\tD5B9E5E8213F67DE0008B38A /* DataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4D5213F67DE0008B38A /* DataRecord.cpp */; };\n\t\tD5B9E5E9213F67DE0008B38A /* tens4d.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4D6213F67DE0008B38A /* tens4d.cpp */; };\n\t\tD5B9E5EA213F67DE0008B38A /* FENodeReorder.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4D7213F67DE0008B38A /* FENodeReorder.h */; };\n\t\tD5B9E5EB213F67DE0008B38A /* FEBoundaryCondition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4D8213F67DE0008B38A /* FEBoundaryCondition.cpp */; };\n\t\tD5B9E5ED213F67DE0008B38A /* tens5d.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4DA213F67DE0008B38A /* tens5d.cpp */; };\n\t\tD5B9E5EE213F67DE0008B38A /* FEElementTraits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4DB213F67DE0008B38A /* FEElementTraits.cpp */; };\n\t\tD5B9E5EF213F67DE0008B38A /* FETransform.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4DC213F67DE0008B38A /* FETransform.h */; };\n\t\tD5B9E5F0213F67DE0008B38A /* FEInitialCondition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4DD213F67DE0008B38A /* FEInitialCondition.cpp */; };\n\t\tD5B9E5F1213F67DE0008B38A /* FENodeDataMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4DE213F67DE0008B38A /* FENodeDataMap.h */; };\n\t\tD5B9E5F2213F67DE0008B38A /* FEElementList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4DF213F67DE0008B38A /* FEElementList.h */; };\n\t\tD5B9E5F3213F67DE0008B38A /* Callback.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4E0213F67DE0008B38A /* Callback.h */; };\n\t\tD5B9E5F4213F67DE0008B38A /* FEPlotData.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4E1213F67DE0008B38A /* FEPlotData.h */; };\n\t\tD5B9E5F5213F67DE0008B38A /* FEDataMathGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4E2213F67DE0008B38A /* FEDataMathGenerator.cpp */; };\n\t\tD5B9E5F6213F67DE0008B38A /* FELinearSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4E3213F67DE0008B38A /* FELinearSolver.cpp */; };\n\t\tD5B9E5F7213F67DE0008B38A /* mat3d.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4E4213F67DE0008B38A /* mat3d.cpp */; };\n\t\tD5B9E5F9213F67DE0008B38A /* FEBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4E6213F67DE0008B38A /* FEBox.cpp */; };\n\t\tD5B9E5FA213F67DE0008B38A /* tens3dls.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4E7213F67DE0008B38A /* tens3dls.hpp */; };\n\t\tD5B9E5FB213F67DE0008B38A /* FEGlobalMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4E8213F67DE0008B38A /* FEGlobalMatrix.cpp */; };\n\t\tD5B9E5FC213F67DE0008B38A /* FEMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4E9213F67DE0008B38A /* FEMaterial.cpp */; };\n\t\tD5B9E5FD213F67DE0008B38A /* Image.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4EA213F67DE0008B38A /* Image.h */; };\n\t\tD5B9E5FE213F67DE0008B38A /* DumpMemStream.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4EB213F67DE0008B38A /* DumpMemStream.h */; };\n\t\tD5B9E5FF213F67DE0008B38A /* FECube.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4EC213F67DE0008B38A /* FECube.h */; };\n\t\tD5B9E600213F67DE0008B38A /* FEOctree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4ED213F67DE0008B38A /* FEOctree.cpp */; };\n\t\tD5B9E601213F67DE0008B38A /* FECore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4EE213F67DE0008B38A /* FECore.cpp */; };\n\t\tD5B9E602213F67DE0008B38A /* FEDiscreteMaterial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4EF213F67DE0008B38A /* FEDiscreteMaterial.cpp */; };\n\t\tD5B9E603213F67DE0008B38A /* fecore_debug_t.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4F0213F67DE0008B38A /* fecore_debug_t.h */; };\n\t\tD5B9E604213F67DE0008B38A /* FEDataExport.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4F1213F67DE0008B38A /* FEDataExport.h */; };\n\t\tD5B9E605213F67DE0008B38A /* mat3d.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4F2213F67DE0008B38A /* mat3d.h */; };\n\t\tD5B9E606213F67DE0008B38A /* Archive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4F3213F67DE0008B38A /* Archive.cpp */; };\n\t\tD5B9E607213F67DE0008B38A /* FESurface.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4F4213F67DE0008B38A /* FESurface.h */; };\n\t\tD5B9E608213F67DE0008B38A /* NLConstraintDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4F5213F67DE0008B38A /* NLConstraintDataRecord.h */; };\n\t\tD5B9E609213F67DE0008B38A /* FEMesh.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4F6213F67DE0008B38A /* FEMesh.h */; };\n\t\tD5B9E60A213F67DE0008B38A /* FETransform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4F7213F67DE0008B38A /* FETransform.cpp */; };\n\t\tD5B9E60D213F67DE0008B38A /* colsol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5B9E4FA213F67DE0008B38A /* colsol.cpp */; };\n\t\tD5B9E60E213F67DE0008B38A /* FEDomain2D.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4FB213F67DE0008B38A /* FEDomain2D.h */; };\n\t\tD5B9E60F213F67DE0008B38A /* ElementDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4FC213F67DE0008B38A /* ElementDataRecord.h */; };\n\t\tD5B9E610213F67DE0008B38A /* tens5ds.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4FD213F67DE0008B38A /* tens5ds.hpp */; };\n\t\tD5B9E611213F67DE0008B38A /* FEOctree.h in Headers */ = {isa = PBXBuildFile; fileRef = D5B9E4FE213F67DE0008B38A /* FEOctree.h */; };\n\t\tD5BC9F5526D07B29003BBF6E /* DomainDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BC9F5126D07B29003BBF6E /* DomainDataRecord.h */; };\n\t\tD5BC9F5626D07B29003BBF6E /* DomainDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BC9F5226D07B29003BBF6E /* DomainDataRecord.cpp */; };\n\t\tD5BC9F5726D07B29003BBF6E /* SurfaceDataRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BC9F5326D07B29003BBF6E /* SurfaceDataRecord.h */; };\n\t\tD5BC9F5826D07B29003BBF6E /* SurfaceDataRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BC9F5426D07B29003BBF6E /* SurfaceDataRecord.cpp */; };\n\t\tD5BE6F812423B4FB009AE76F /* FENodeSetConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5BE6F7F2423B4FB009AE76F /* FENodeSetConstraint.cpp */; };\n\t\tD5BE6F822423B4FB009AE76F /* FENodeSetConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = D5BE6F802423B4FB009AE76F /* FENodeSetConstraint.h */; };\n\t\tD5D56E872355239E0078BCC4 /* ClassDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = D5D56E842355239E0078BCC4 /* ClassDescriptor.h */; };\n\t\tD5E82FD226206FCF00B23605 /* gamma.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E82FD026206FCF00B23605 /* gamma.cpp */; };\n\t\tD5E82FD326206FCF00B23605 /* gamma.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E82FD126206FCF00B23605 /* gamma.h */; };\n\t\tD5E82FD62620A6FD00B23605 /* BSpline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E82FD42620A6FD00B23605 /* BSpline.cpp */; };\n\t\tD5E82FD72620A6FD00B23605 /* BSpline.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E82FD52620A6FD00B23605 /* BSpline.h */; };\n\t\tD5E85DA322021E8C00F5DF83 /* FEFaceList.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E85D9B22021E8A00F5DF83 /* FEFaceList.h */; };\n\t\tD5E85DA522021E8C00F5DF83 /* FEMeshTopo.h in Headers */ = {isa = PBXBuildFile; fileRef = D5E85D9D22021E8B00F5DF83 /* FEMeshTopo.h */; };\n\t\tD5E85DA622021E8C00F5DF83 /* FEFaceList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E85D9E22021E8B00F5DF83 /* FEFaceList.cpp */; };\n\t\tD5E85DA722021E8C00F5DF83 /* FEMeshTopo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5E85D9F22021E8B00F5DF83 /* FEMeshTopo.cpp */; };\n\t\tD5F1947221908646000F738D /* fecore_enum.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1946121908644000F738D /* fecore_enum.h */; };\n\t\tD5F1947421908646000F738D /* CompactMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1946321908644000F738D /* CompactMatrix.h */; };\n\t\tD5F1947521908646000F738D /* DenseMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1946421908644000F738D /* DenseMatrix.cpp */; };\n\t\tD5F1947621908646000F738D /* SchurComplement.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1946521908644000F738D /* SchurComplement.h */; };\n\t\tD5F1947721908646000F738D /* CSRMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1946621908645000F738D /* CSRMatrix.cpp */; };\n\t\tD5F1947921908646000F738D /* DenseMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1946821908645000F738D /* DenseMatrix.h */; };\n\t\tD5F1947A21908646000F738D /* SchurComplement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1946921908645000F738D /* SchurComplement.cpp */; };\n\t\tD5F1947E21908646000F738D /* CSRMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1946D21908645000F738D /* CSRMatrix.h */; };\n\t\tD5F1947F21908646000F738D /* CompactMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1946E21908646000F738D /* CompactMatrix.cpp */; };\n\t\tD5F1948121908646000F738D /* Preconditioner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1947021908646000F738D /* Preconditioner.cpp */; };\n\t\tD5F1948221908646000F738D /* Preconditioner.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1947121908646000F738D /* Preconditioner.h */; };\n\t\tD5FAD69C26FF93420043FC69 /* FELogDomainVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5FAD69A26FF93420043FC69 /* FELogDomainVolume.cpp */; };\n\t\tD5FAD69D26FF93420043FC69 /* FELogDomainVolume.h in Headers */ = {isa = PBXBuildFile; fileRef = D5FAD69B26FF93420043FC69 /* FELogDomainVolume.h */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD5006A7621AAF98000031CB6 /* FEElementShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElementShape.cpp; sourceTree = \"<group>\"; };\n\t\tD5006A7721AAF98100031CB6 /* FEElementShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElementShape.h; sourceTree = \"<group>\"; };\n\t\tD5044A652603DC8500C81BC4 /* FESurfacePairConstraintNL.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfacePairConstraintNL.cpp; sourceTree = \"<group>\"; };\n\t\tD5044A662603DC8500C81BC4 /* FESurfacePairConstraintNL.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FESurfacePairConstraintNL.h; sourceTree = \"<group>\"; };\n\t\tD50D09EE261E2ED3005564C6 /* expint_Ei.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = expint_Ei.cpp; sourceTree = \"<group>\"; };\n\t\tD50D09EF261E2ED3005564C6 /* expint_Ei.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = expint_Ei.h; sourceTree = \"<group>\"; };\n\t\tD50D879026D930DB0034E121 /* FELogElementVolume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELogElementVolume.h; sourceTree = \"<group>\"; };\n\t\tD50D879126D930DB0034E121 /* FELogEnclosedVolume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELogEnclosedVolume.h; sourceTree = \"<group>\"; };\n\t\tD50D879226D930DC0034E121 /* FELogEnclosedVolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELogEnclosedVolume.cpp; sourceTree = \"<group>\"; };\n\t\tD50D879326D930DC0034E121 /* FELogElementVolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELogElementVolume.cpp; sourceTree = \"<group>\"; };\n\t\tD510616C217CDD1600CF1690 /* FEPropertyT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPropertyT.h; sourceTree = \"<group>\"; };\n\t\tD51EBF9D217E3AED006F73C4 /* FEPIDController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPIDController.cpp; sourceTree = \"<group>\"; };\n\t\tD51EBF9E217E3AED006F73C4 /* FEPIDController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPIDController.h; sourceTree = \"<group>\"; };\n\t\tD52D83F121CE89A100472620 /* FEDomainParameter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomainParameter.h; sourceTree = \"<group>\"; };\n\t\tD52D83F221CE89A100472620 /* FEValuator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEValuator.h; sourceTree = \"<group>\"; };\n\t\tD52D83F321CE89A100472620 /* FEVec3dValuator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEVec3dValuator.cpp; sourceTree = \"<group>\"; };\n\t\tD52D83F421CE89A100472620 /* FEVec3dValuator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEVec3dValuator.h; sourceTree = \"<group>\"; };\n\t\tD52D83F521CE89A100472620 /* FEScalarValuator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEScalarValuator.h; sourceTree = \"<group>\"; };\n\t\tD52D83F721CE89A200472620 /* FEScalarValuator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEScalarValuator.cpp; sourceTree = \"<group>\"; };\n\t\tD52D83F821CE89A200472620 /* FEMat3dValuator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMat3dValuator.h; sourceTree = \"<group>\"; };\n\t\tD52D83F921CE89A200472620 /* FEMat3dValuator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMat3dValuator.cpp; sourceTree = \"<group>\"; };\n\t\tD52D83FA21CE89A200472620 /* FEDomainParameter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomainParameter.cpp; sourceTree = \"<group>\"; };\n\t\tD5400D42249922C200570A47 /* FEMat3dsValuator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMat3dsValuator.cpp; sourceTree = \"<group>\"; };\n\t\tD5400D43249922C200570A47 /* FEMat3dsValuator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMat3dsValuator.h; sourceTree = \"<group>\"; };\n\t\tD54626F2260E6FDA007E2D8E /* FaceDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FaceDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD54626F3260E6FDA007E2D8E /* FaceDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FaceDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD54A66AD257BDCB90023C8D1 /* FSPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FSPath.cpp; sourceTree = \"<group>\"; };\n\t\tD54A66AE257BDCB90023C8D1 /* FSPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSPath.h; sourceTree = \"<group>\"; };\n\t\tD54E219E2149BB54008A9DD3 /* FEElementSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElementSet.cpp; sourceTree = \"<group>\"; };\n\t\tD54E219F2149BB54008A9DD3 /* FENode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENode.h; sourceTree = \"<group>\"; };\n\t\tD54E21A02149BB54008A9DD3 /* FEFacetSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFacetSet.h; sourceTree = \"<group>\"; };\n\t\tD54E21A12149BB54008A9DD3 /* FESurfacePair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfacePair.h; sourceTree = \"<group>\"; };\n\t\tD54E21A22149BB54008A9DD3 /* FESegmentSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESegmentSet.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21A32149BB55008A9DD3 /* FEFacetSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFacetSet.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21A42149BB55008A9DD3 /* FESurfacePair.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfacePair.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21A52149BB55008A9DD3 /* FESegmentSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESegmentSet.h; sourceTree = \"<group>\"; };\n\t\tD54E21A62149BB55008A9DD3 /* FENode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENode.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21A72149BB55008A9DD3 /* FEElementSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElementSet.h; sourceTree = \"<group>\"; };\n\t\tD54E21A82149BB55008A9DD3 /* FEDiscreteSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteSet.h; sourceTree = \"<group>\"; };\n\t\tD54E21A92149BB55008A9DD3 /* FENodeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeSet.h; sourceTree = \"<group>\"; };\n\t\tD54E21AA2149BB55008A9DD3 /* FENodeSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeSet.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21AB2149BB56008A9DD3 /* FEDiscreteSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteSet.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21BA21517EEA008A9DD3 /* MFunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MFunctions.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21BB21517EEA008A9DD3 /* FEModelParam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEModelParam.h; sourceTree = \"<group>\"; };\n\t\tD54E21BC21517EEA008A9DD3 /* FEPrescribedBC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedBC.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21BD21517EEA008A9DD3 /* FENodalLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodalLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21BE21517EEA008A9DD3 /* MIntegral.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MIntegral.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21BF21517EEA008A9DD3 /* MEvaluate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MEvaluate.h; sourceTree = \"<group>\"; };\n\t\tD54E21C021517EEA008A9DD3 /* MTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTypes.h; sourceTree = \"<group>\"; };\n\t\tD54E21C121517EEB008A9DD3 /* MDerive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MDerive.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21C221517EEB008A9DD3 /* MObjBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MObjBuilder.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21C321517EEB008A9DD3 /* MathObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathObject.h; sourceTree = \"<group>\"; };\n\t\tD54E21C421517EEB008A9DD3 /* FEFixedBC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFixedBC.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21C521517EEB008A9DD3 /* FEFixedBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFixedBC.h; sourceTree = \"<group>\"; };\n\t\tD54E21C621517EEB008A9DD3 /* MathObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathObject.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21C721517EEB008A9DD3 /* MMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMath.h; sourceTree = \"<group>\"; };\n\t\tD54E21C821517EEC008A9DD3 /* MObj2String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MObj2String.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21C921517EEC008A9DD3 /* FEPrescribedBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedBC.h; sourceTree = \"<group>\"; };\n\t\tD54E21CA21517EEC008A9DD3 /* MItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MItem.h; sourceTree = \"<group>\"; };\n\t\tD54E21CB21517EEC008A9DD3 /* MObjBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MObjBuilder.h; sourceTree = \"<group>\"; };\n\t\tD54E21CC21517EEC008A9DD3 /* MObj2String.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MObj2String.h; sourceTree = \"<group>\"; };\n\t\tD54E21CD21517EEC008A9DD3 /* FEModelParam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEModelParam.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21CE21517EEC008A9DD3 /* FENodalLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodalLoad.h; sourceTree = \"<group>\"; };\n\t\tD54E21CF21517EEC008A9DD3 /* FEPrescribedDOF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPrescribedDOF.h; sourceTree = \"<group>\"; };\n\t\tD54E21D021517EED008A9DD3 /* MMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMatrix.h; sourceTree = \"<group>\"; };\n\t\tD54E21D121517EED008A9DD3 /* MExpand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MExpand.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D221517EED008A9DD3 /* MItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MItem.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D321517EED008A9DD3 /* MMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D421517EED008A9DD3 /* MCollect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCollect.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D521517EED008A9DD3 /* MReplace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MReplace.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D621517EED008A9DD3 /* MTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MTypes.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D721517EED008A9DD3 /* FEPrescribedDOF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPrescribedDOF.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D821517EEE008A9DD3 /* MSimplify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MSimplify.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21D921517EEE008A9DD3 /* MSolve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MSolve.cpp; sourceTree = \"<group>\"; };\n\t\tD54E21DA21517EEE008A9DD3 /* MFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFunctions.h; sourceTree = \"<group>\"; };\n\t\tD54E21DB21517EEE008A9DD3 /* MEvaluate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MEvaluate.cpp; sourceTree = \"<group>\"; };\n\t\tD550834C24F086E300E919D8 /* EigenSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EigenSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD550834D24F086E300E919D8 /* EigenSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EigenSolver.h; sourceTree = \"<group>\"; };\n\t\tD559C4C522D9169D00CDC2BD /* FEMat3dSphericalAngleMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMat3dSphericalAngleMap.h; sourceTree = \"<group>\"; };\n\t\tD559C4C622D9169D00CDC2BD /* FEParabolicMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEParabolicMap.cpp; sourceTree = \"<group>\"; };\n\t\tD559C4C722D9169E00CDC2BD /* FEMat3dSphericalAngleMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMat3dSphericalAngleMap.cpp; sourceTree = \"<group>\"; };\n\t\tD559C4C822D9169E00CDC2BD /* FEParabolicMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEParabolicMap.h; sourceTree = \"<group>\"; };\n\t\tD5613D3A217B604D007CAB89 /* FEMeshPartition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMeshPartition.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D3B217B604D007CAB89 /* FEMeshPartition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMeshPartition.h; sourceTree = \"<group>\"; };\n\t\tD5613D3C217B604E007CAB89 /* FESPRProjection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESPRProjection.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D3D217B604E007CAB89 /* FESurfaceToSurfaceMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfaceToSurfaceMap.h; sourceTree = \"<group>\"; };\n\t\tD5613D3F217B604E007CAB89 /* FEMaterialPointProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMaterialPointProperty.h; sourceTree = \"<group>\"; };\n\t\tD5613D40217B604E007CAB89 /* FELoadController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELoadController.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D41217B604E007CAB89 /* FEPointFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPointFunction.h; sourceTree = \"<group>\"; };\n\t\tD5613D42217B604E007CAB89 /* writeplot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = writeplot.h; sourceTree = \"<group>\"; };\n\t\tD5613D43217B604E007CAB89 /* FEMathController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMathController.h; sourceTree = \"<group>\"; };\n\t\tD5613D44217B604E007CAB89 /* FEMathController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMathController.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D45217B604E007CAB89 /* fecore_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fecore_type.h; sourceTree = \"<group>\"; };\n\t\tD5613D46217B604E007CAB89 /* fecore_type.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fecore_type.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D47217B604E007CAB89 /* FELoadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELoadController.h; sourceTree = \"<group>\"; };\n\t\tD5613D48217B604E007CAB89 /* FEConstDataGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConstDataGenerator.h; sourceTree = \"<group>\"; };\n\t\tD5613D49217B604E007CAB89 /* FELoadCurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELoadCurve.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D4A217B604E007CAB89 /* FEPointFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPointFunction.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D4B217B604E007CAB89 /* FESPRProjection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESPRProjection.h; sourceTree = \"<group>\"; };\n\t\tD5613D4C217B604F007CAB89 /* writeplot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = writeplot.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D4E217B604F007CAB89 /* FESurfaceToSurfaceMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceToSurfaceMap.cpp; sourceTree = \"<group>\"; };\n\t\tD5613D4F217B604F007CAB89 /* FELoadCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELoadCurve.h; sourceTree = \"<group>\"; };\n\t\tD565CDD4215D290B00E08ED6 /* FEDomainMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomainMap.h; sourceTree = \"<group>\"; };\n\t\tD565CDD5215D290B00E08ED6 /* FEDomainList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomainList.h; sourceTree = \"<group>\"; };\n\t\tD565CDD6215D290B00E08ED6 /* FEItemList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEItemList.h; sourceTree = \"<group>\"; };\n\t\tD565CDD7215D290B00E08ED6 /* FEDataMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDataMap.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDD8215D290B00E08ED6 /* FEDomainList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomainList.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDD9215D290B00E08ED6 /* FEDomainMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomainMap.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDDA215D290B00E08ED6 /* FEItemList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEItemList.cpp; sourceTree = \"<group>\"; };\n\t\tD565CDDB215D290B00E08ED6 /* FEDataMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataMap.h; sourceTree = \"<group>\"; };\n\t\tD56B208323AD5F94000AE9C2 /* FESolidElementShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidElementShape.h; sourceTree = \"<group>\"; };\n\t\tD56B208423AD5F94000AE9C2 /* FESurfaceElementShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceElementShape.cpp; sourceTree = \"<group>\"; };\n\t\tD56B208523AD5F94000AE9C2 /* FESurfaceElementShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfaceElementShape.h; sourceTree = \"<group>\"; };\n\t\tD56B208623AD5F94000AE9C2 /* FESolidElementShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidElementShape.cpp; sourceTree = \"<group>\"; };\n\t\tD56B208723AD5F94000AE9C2 /* FEShellElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEShellElement.h; sourceTree = \"<group>\"; };\n\t\tD56B208823AD5F94000AE9C2 /* FESolidElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidElement.h; sourceTree = \"<group>\"; };\n\t\tD56B208923AD5F94000AE9C2 /* FESolidElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidElement.cpp; sourceTree = \"<group>\"; };\n\t\tD56B208A23AD5F94000AE9C2 /* FEShellElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEShellElement.cpp; sourceTree = \"<group>\"; };\n\t\tD56B208B23AD5F94000AE9C2 /* FESurfaceElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceElement.cpp; sourceTree = \"<group>\"; };\n\t\tD56B208C23AD5F94000AE9C2 /* FESurfaceElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfaceElement.h; sourceTree = \"<group>\"; };\n\t\tD5709DA122833034007CAB0A /* FENodeList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeList.cpp; sourceTree = \"<group>\"; };\n\t\tD5709DA222833034007CAB0A /* FENodeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeList.h; sourceTree = \"<group>\"; };\n\t\tD5709DA322833034007CAB0A /* FEDofList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDofList.cpp; sourceTree = \"<group>\"; };\n\t\tD5709DA522833034007CAB0A /* FEDofList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDofList.h; sourceTree = \"<group>\"; };\n\t\tD58FA88024A1631400FC768B /* FEConstValueVec3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEConstValueVec3.cpp; sourceTree = \"<group>\"; };\n\t\tD58FA88124A1631400FC768B /* FEConstValueVec3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEConstValueVec3.h; sourceTree = \"<group>\"; };\n\t\tD5946E1F24B524F90009C22F /* mathalg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mathalg.cpp; sourceTree = \"<group>\"; };\n\t\tD5946E2024B524F90009C22F /* mathalg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mathalg.h; sourceTree = \"<group>\"; };\n\t\tD5A37D282286167300867D77 /* FEOctreeSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOctreeSearch.h; sourceTree = \"<group>\"; };\n\t\tD5A37D292286167300867D77 /* FEOctreeSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOctreeSearch.cpp; sourceTree = \"<group>\"; };\n\t\tD5A56F8223D4FBFE002F28CA /* tens4dmm.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tens4dmm.hpp; sourceTree = \"<group>\"; };\n\t\tD5AB623D220289E200AAF141 /* FEEdgeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEdgeList.h; sourceTree = \"<group>\"; };\n\t\tD5AB623E220289E200AAF141 /* FEEdgeList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEdgeList.cpp; sourceTree = \"<group>\"; };\n\t\tD5AE8CC42592ECCB003AC08B /* FEMeshAdaptorCriterion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMeshAdaptorCriterion.h; sourceTree = \"<group>\"; };\n\t\tD5AE8CC52592ECCB003AC08B /* FEMeshAdaptorCriterion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMeshAdaptorCriterion.cpp; sourceTree = \"<group>\"; };\n\t\tD5B805B1223BE2DC00198805 /* FEMeshAdaptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMeshAdaptor.h; sourceTree = \"<group>\"; };\n\t\tD5B805B2223BE2DC00198805 /* FEMeshAdaptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMeshAdaptor.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3DD213F67CE0008B38A /* libFECore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFECore.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5B9E3EC213F67DE0008B38A /* FELinearConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELinearConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3ED213F67DE0008B38A /* MatrixOperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatrixOperator.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3EE213F67DE0008B38A /* FEDiscreteMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3EF213F67DE0008B38A /* SparseMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SparseMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3F0213F67DE0008B38A /* mortar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mortar.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3F1213F67DE0008B38A /* JFNKMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JFNKMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3F2213F67DE0008B38A /* FEBroydenStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBroydenStrategy.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3F3213F67DE0008B38A /* NodeDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NodeDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3F4213F67DE0008B38A /* FECoreFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoreFactory.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3F5213F67DE0008B38A /* FEGlobalData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGlobalData.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3F6213F67DE0008B38A /* FECube.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECube.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3F8213F67DE0008B38A /* FENodeDataMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeDataMap.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3F9213F67DE0008B38A /* tens6d.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tens6d.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3FA213F67DE0008B38A /* FEFunction1D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFunction1D.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3FB213F67DE0008B38A /* tens4dms.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens4dms.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E3FC213F67DE0008B38A /* FEClosestPointProjection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEClosestPointProjection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3FD213F67DE0008B38A /* FEMaterial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMaterial.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3FE213F67DE0008B38A /* Archive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Archive.h; sourceTree = \"<group>\"; };\n\t\tD5B9E3FF213F67DE0008B38A /* FELinearSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELinearSystem.h; sourceTree = \"<group>\"; };\n\t\tD5B9E400213F67DE0008B38A /* FECallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECallback.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E403213F67DE0008B38A /* tens4d.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens4d.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E404213F67DE0008B38A /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = \"<group>\"; };\n\t\tD5B9E405213F67DE0008B38A /* fecore_debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fecore_debug.h; sourceTree = \"<group>\"; };\n\t\tD5B9E406213F67DE0008B38A /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5B9E408213F67DE0008B38A /* tens5d.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens5d.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E409213F67DE0008B38A /* tensor_base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tensor_base.h; sourceTree = \"<group>\"; };\n\t\tD5B9E40A213F67DE0008B38A /* Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E40B213F67DE0008B38A /* FEInitialCondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEInitialCondition.h; sourceTree = \"<group>\"; };\n\t\tD5B9E40C213F67DE0008B38A /* Callback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Callback.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E40D213F67DE0008B38A /* FEEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEdge.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E40E213F67DE0008B38A /* mat3d.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mat3d.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E40F213F67DE0008B38A /* FEParamValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEParamValidator.h; sourceTree = \"<group>\"; };\n\t\tD5B9E410213F67DE0008B38A /* FEBoundingBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBoundingBox.h; sourceTree = \"<group>\"; };\n\t\tD5B9E411213F67DE0008B38A /* FEModelLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEModelLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E412213F67DE0008B38A /* svd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = svd.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E414213F67DE0008B38A /* FEElementTraits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElementTraits.h; sourceTree = \"<group>\"; };\n\t\tD5B9E415213F67DE0008B38A /* FEAnalysis.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEAnalysis.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E416213F67DE0008B38A /* FEPlotData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEPlotData.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E417213F67DE0008B38A /* FEDataGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDataGenerator.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E418213F67DE0008B38A /* FEDataMathGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataMathGenerator.h; sourceTree = \"<group>\"; };\n\t\tD5B9E41B213F67DE0008B38A /* FEFunction1D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFunction1D.h; sourceTree = \"<group>\"; };\n\t\tD5B9E41C213F67DE0008B38A /* LinearSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LinearSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E41D213F67DE0008B38A /* quatd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = quatd.h; sourceTree = \"<group>\"; };\n\t\tD5B9E41E213F67DE0008B38A /* Integrate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Integrate.h; sourceTree = \"<group>\"; };\n\t\tD5B9E41F213F67DE0008B38A /* FEMaterialPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMaterialPoint.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E420213F67DE0008B38A /* FENLConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENLConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5B9E421213F67DE0008B38A /* FEBodyLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBodyLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E423213F67DE0008B38A /* NodeDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NodeDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E426213F67DE0008B38A /* BFGSSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BFGSSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E427213F67DE0008B38A /* Integrate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Integrate.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E428213F67DE0008B38A /* FEModelData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEModelData.h; sourceTree = \"<group>\"; };\n\t\tD5B9E429213F67DE0008B38A /* FEModelLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEModelLoad.h; sourceTree = \"<group>\"; };\n\t\tD5B9E42A213F67DE0008B38A /* FEBroydenStrategy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBroydenStrategy.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E42B213F67DE0008B38A /* eig3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = eig3.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E42D213F67DE0008B38A /* FELevelStructure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELevelStructure.h; sourceTree = \"<group>\"; };\n\t\tD5B9E42E213F67DE0008B38A /* DataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataStore.h; sourceTree = \"<group>\"; };\n\t\tD5B9E42F213F67DE0008B38A /* FEParam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEParam.h; sourceTree = \"<group>\"; };\n\t\tD5B9E430213F67DE0008B38A /* ParamString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParamString.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E431213F67DE0008B38A /* tens4d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tens4d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E432213F67DE0008B38A /* FENormalProjection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENormalProjection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E433213F67DE0008B38A /* FEParam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEParam.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E434213F67DE0008B38A /* FENodeReorder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeReorder.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E435213F67DE0008B38A /* FETrussDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETrussDomain.h; sourceTree = \"<group>\"; };\n\t\tD5B9E436213F67DE0008B38A /* FEParameterList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEParameterList.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E437213F67DE0008B38A /* FEException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEException.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E438213F67DE0008B38A /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E439213F67DE0008B38A /* FETimeStepController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETimeStepController.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E43A213F67DE0008B38A /* DumpStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpStream.h; sourceTree = \"<group>\"; };\n\t\tD5B9E43B213F67DE0008B38A /* FENewtonStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENewtonStrategy.h; sourceTree = \"<group>\"; };\n\t\tD5B9E43C213F67DE0008B38A /* FENewtonSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENewtonSolver.h; sourceTree = \"<group>\"; };\n\t\tD5B9E43D213F67DE0008B38A /* FEElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElement.h; sourceTree = \"<group>\"; };\n\t\tD5B9E43E213F67DE0008B38A /* vec2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec2d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E43F213F67DE0008B38A /* FESurfaceLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfaceLoad.h; sourceTree = \"<group>\"; };\n\t\tD5B9E440213F67DE0008B38A /* FEDataArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataArray.h; sourceTree = \"<group>\"; };\n\t\tD5B9E441213F67DE0008B38A /* FESolidDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolidDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E442213F67DE0008B38A /* FEEdgeLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEdgeLoad.h; sourceTree = \"<group>\"; };\n\t\tD5B9E443213F67DE0008B38A /* tens5d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tens5d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E444213F67DE0008B38A /* mortar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mortar.h; sourceTree = \"<group>\"; };\n\t\tD5B9E445213F67DE0008B38A /* FENewtonStrategy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENewtonStrategy.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E446213F67DE0008B38A /* ParamString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParamString.h; sourceTree = \"<group>\"; };\n\t\tD5B9E447213F67DE0008B38A /* matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix.h; sourceTree = \"<group>\"; };\n\t\tD5B9E448213F67DE0008B38A /* DumpMemStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DumpMemStream.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E449213F67DE0008B38A /* FEParamValidator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEParamValidator.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E44A213F67DE0008B38A /* FECoreBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoreBase.h; sourceTree = \"<group>\"; };\n\t\tD5B9E44C213F67DE0008B38A /* FEGlobalMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGlobalMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5B9E44D213F67DE0008B38A /* vec3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec3d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E44E213F67DE0008B38A /* qsort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = qsort.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E44F213F67DE0008B38A /* FECoreKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoreKernel.h; sourceTree = \"<group>\"; };\n\t\tD5B9E450213F67DE0008B38A /* FEModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEModel.h; sourceTree = \"<group>\"; };\n\t\tD5B9E451213F67DE0008B38A /* FEElemElemList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElemElemList.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E452213F67DE0008B38A /* tens3d.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens3d.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E453213F67DE0008B38A /* FEProperty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEProperty.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E454213F67DE0008B38A /* DumpFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpFile.h; sourceTree = \"<group>\"; };\n\t\tD5B9E455213F67DE0008B38A /* FENNQuery.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENNQuery.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E456213F67DE0008B38A /* FECoreTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoreTask.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E458213F67DE0008B38A /* FELinearConstraintManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELinearConstraintManager.h; sourceTree = \"<group>\"; };\n\t\tD5B9E459213F67DE0008B38A /* FEModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEModel.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E45A213F67DE0008B38A /* SparseMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SparseMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E45C213F67DE0008B38A /* FEGlobalData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGlobalData.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E45D213F67DE0008B38A /* FESurfacePairConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfacePairConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E45E213F67DE0008B38A /* eig3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eig3.h; sourceTree = \"<group>\"; };\n\t\tD5B9E45F213F67DE0008B38A /* ElementDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ElementDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E460213F67DE0008B38A /* fecore_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fecore_api.h; sourceTree = \"<group>\"; };\n\t\tD5B9E461213F67DE0008B38A /* FEDiscreteDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E462213F67DE0008B38A /* tens6d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tens6d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E463213F67DE0008B38A /* FECorePlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECorePlot.h; sourceTree = \"<group>\"; };\n\t\tD5B9E464213F67DE0008B38A /* FENodeElemList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeElemList.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E465213F67DE0008B38A /* FEElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElement.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E466213F67DE0008B38A /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = \"<group>\"; };\n\t\tD5B9E467213F67DE0008B38A /* FECoreTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECoreTask.h; sourceTree = \"<group>\"; };\n\t\tD5B9E468213F67DE0008B38A /* FEProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEProperty.h; sourceTree = \"<group>\"; };\n\t\tD5B9E469213F67DE0008B38A /* FEDataArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDataArray.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E46A213F67DE0008B38A /* FELinearConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELinearConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E46B213F67DE0008B38A /* LinearSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinearSolver.h; sourceTree = \"<group>\"; };\n\t\tD5B9E46C213F67DE0008B38A /* FESurface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurface.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E46D213F67DE0008B38A /* MatrixProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MatrixProfile.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E46E213F67DE0008B38A /* FESolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolver.h; sourceTree = \"<group>\"; };\n\t\tD5B9E46F213F67DE0008B38A /* targetver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = targetver.h; sourceTree = \"<group>\"; };\n\t\tD5B9E470213F67DE0008B38A /* FEShellDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEShellDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E471213F67DE0008B38A /* vector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vector.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E472213F67DE0008B38A /* DataStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataStore.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E473213F67DE0008B38A /* FESolidDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESolidDomain.h; sourceTree = \"<group>\"; };\n\t\tD5B9E474213F67DE0008B38A /* NLConstraintDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NLConstraintDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E475213F67DE0008B38A /* FEBodyLoad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBodyLoad.h; sourceTree = \"<group>\"; };\n\t\tD5B9E476213F67DE0008B38A /* FEModelComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEModelComponent.h; sourceTree = \"<group>\"; };\n\t\tD5B9E477213F67DE0008B38A /* FEElementLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElementLibrary.h; sourceTree = \"<group>\"; };\n\t\tD5B9E478213F67DE0008B38A /* FEException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEException.h; sourceTree = \"<group>\"; };\n\t\tD5B9E479213F67DE0008B38A /* FELinearSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELinearSolver.h; sourceTree = \"<group>\"; };\n\t\tD5B9E47A213F67DE0008B38A /* FECore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECore.h; sourceTree = \"<group>\"; };\n\t\tD5B9E47B213F67DE0008B38A /* FEElementLibrary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElementLibrary.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E47D213F67DE0008B38A /* FEDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E47E213F67DE0008B38A /* FENormalProjection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENormalProjection.h; sourceTree = \"<group>\"; };\n\t\tD5B9E47F213F67DE0008B38A /* FEDataExport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDataExport.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E480213F67DE0008B38A /* FEModelData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEModelData.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E481213F67DE0008B38A /* FEMaterialPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMaterialPoint.h; sourceTree = \"<group>\"; };\n\t\tD5B9E482213F67DE0008B38A /* MatrixProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatrixProfile.h; sourceTree = \"<group>\"; };\n\t\tD5B9E483213F67DE0008B38A /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = \"<group>\"; };\n\t\tD5B9E484213F67DE0008B38A /* FETimeInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETimeInfo.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E485213F67DE0008B38A /* tens3drs.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens3drs.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E486213F67DE0008B38A /* FESolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E487213F67DE0008B38A /* FEEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEEdge.h; sourceTree = \"<group>\"; };\n\t\tD5B9E488213F67DE0008B38A /* FEDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomain.h; sourceTree = \"<group>\"; };\n\t\tD5B9E489213F67DE0008B38A /* FESurfaceLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E48B213F67DE0008B38A /* tens3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tens3d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E48C213F67DE0008B38A /* FECoreKernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoreKernel.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E48D213F67DE0008B38A /* FESurfaceConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfaceConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5B9E48E213F67DE0008B38A /* FEBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBox.h; sourceTree = \"<group>\"; };\n\t\tD5B9E48F213F67DE0008B38A /* FEClosestPointProjection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEClosestPointProjection.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E490213F67DE0008B38A /* FENodeNodeList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeNodeList.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E491213F67DE0008B38A /* FEModelComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEModelComponent.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E492213F67DE0008B38A /* JFNKStrategy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JFNKStrategy.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E493213F67DE0008B38A /* FESurfaceConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E494213F67DE0008B38A /* tools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tools.h; sourceTree = \"<group>\"; };\n\t\tD5B9E495213F67DE0008B38A /* FEShellDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEShellDomain.h; sourceTree = \"<group>\"; };\n\t\tD5B9E496213F67DE0008B38A /* FENewtonSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENewtonSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E497213F67DE0008B38A /* FELinearSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELinearSystem.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E498213F67DE0008B38A /* tens4ds.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens4ds.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E499213F67DE0008B38A /* FECoreBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoreBase.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E49A213F67DE0008B38A /* FEElementList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElementList.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E49B213F67DE0008B38A /* DumpFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DumpFile.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E49C213F67DE0008B38A /* quatd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = quatd.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E49D213F67DE0008B38A /* FECallBack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECallBack.h; sourceTree = \"<group>\"; };\n\t\tD5B9E49E213F67DE0008B38A /* DumpStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DumpStream.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E49F213F67DE0008B38A /* matrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = matrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4A0213F67DE0008B38A /* tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tools.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4A1213F67DE0008B38A /* FEElemElemList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElemElemList.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4A2213F67DE0008B38A /* tens3d.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tens3d.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4A3213F67DE0008B38A /* FEAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEAnalysis.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4A4213F67DE0008B38A /* sdk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdk.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4A5213F67DE0008B38A /* FECoreFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECoreFactory.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4A6213F67DE0008B38A /* sys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sys.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4A7213F67DE0008B38A /* JFNKStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFNKStrategy.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4A8213F67DE0008B38A /* FEDomain2D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDomain2D.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4A9213F67DE0008B38A /* tens6ds.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens6ds.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4AA213F67DE0008B38A /* FELineSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELineSearch.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4AB213F67DE0008B38A /* DataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataRecord.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4AC213F67DE0008B38A /* JFNKMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFNKMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4AD213F67DE0008B38A /* FETimeInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETimeInfo.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4AE213F67DE0008B38A /* FEDataStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataStream.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4AF213F67DE0008B38A /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B0213F67DE0008B38A /* FENLConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FENLConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4B1213F67DE0008B38A /* mat2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mat2d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B2213F67DE0008B38A /* FELineSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELineSearch.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B3213F67DE0008B38A /* FENNQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENNQuery.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B5213F67DE0008B38A /* FELinearConstraintManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELinearConstraintManager.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4B6213F67DE0008B38A /* FEDataGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataGenerator.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B7213F67DE0008B38A /* FEBoundaryCondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEBoundaryCondition.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B8213F67DE0008B38A /* FEParameterList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEParameterList.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4B9213F67DE0008B38A /* BFGSSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFGSSolver.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4BA213F67DE0008B38A /* DOFS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOFS.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4BB213F67DE0008B38A /* FETrussDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETrussDomain.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4BC213F67DE0008B38A /* FESurfaceMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FESurfaceMap.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4BD213F67DE0008B38A /* FEGlobalVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGlobalVector.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4BE213F67DE0008B38A /* FENodeNodeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeNodeList.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4BF213F67DE0008B38A /* DOFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOFS.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4C0213F67DE0008B38A /* FECorePlot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECorePlot.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4C2213F67DE0008B38A /* FESurfacePairConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfacePairConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4C4213F67DE0008B38A /* FEDiscreteDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDiscreteDomain.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4C6213F67DE0008B38A /* FENodeElemList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeElemList.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4C7213F67DE0008B38A /* FESurfaceMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurfaceMap.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4C8213F67DE0008B38A /* tens6d.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens6d.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4C9213F67DE0008B38A /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4CA213F67DE0008B38A /* mat6d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mat6d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4CC213F67DE0008B38A /* FEGlobalVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEGlobalVector.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4CD213F67DE0008B38A /* FELevelStructure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELevelStructure.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4CE213F67DE0008B38A /* fecore_debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fecore_debug.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4CF213F67DE0008B38A /* FEEdgeLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEEdgeLoad.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4D0213F67DE0008B38A /* FEMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMesh.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4D2213F67DE0008B38A /* tens3ds.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens3ds.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4D3213F67DE0008B38A /* FETimeStepController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETimeStepController.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4D4213F67DE0008B38A /* vector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4D5213F67DE0008B38A /* DataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4D6213F67DE0008B38A /* tens4d.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tens4d.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4D7213F67DE0008B38A /* FENodeReorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeReorder.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4D8213F67DE0008B38A /* FEBoundaryCondition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBoundaryCondition.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4DA213F67DE0008B38A /* tens5d.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tens5d.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4DB213F67DE0008B38A /* FEElementTraits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEElementTraits.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4DC213F67DE0008B38A /* FETransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FETransform.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4DD213F67DE0008B38A /* FEInitialCondition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEInitialCondition.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4DE213F67DE0008B38A /* FENodeDataMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FENodeDataMap.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4DF213F67DE0008B38A /* FEElementList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEElementList.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4E0213F67DE0008B38A /* Callback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Callback.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4E1213F67DE0008B38A /* FEPlotData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEPlotData.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4E2213F67DE0008B38A /* FEDataMathGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDataMathGenerator.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4E3213F67DE0008B38A /* FELinearSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELinearSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4E4213F67DE0008B38A /* mat3d.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mat3d.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4E6213F67DE0008B38A /* FEBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEBox.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4E7213F67DE0008B38A /* tens3dls.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens3dls.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4E8213F67DE0008B38A /* FEGlobalMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEGlobalMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4E9213F67DE0008B38A /* FEMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4EA213F67DE0008B38A /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4EB213F67DE0008B38A /* DumpMemStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpMemStream.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4EC213F67DE0008B38A /* FECube.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FECube.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4ED213F67DE0008B38A /* FEOctree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEOctree.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4EE213F67DE0008B38A /* FECore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FECore.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4EF213F67DE0008B38A /* FEDiscreteMaterial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEDiscreteMaterial.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4F0213F67DE0008B38A /* fecore_debug_t.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fecore_debug_t.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4F1213F67DE0008B38A /* FEDataExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDataExport.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4F2213F67DE0008B38A /* mat3d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mat3d.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4F3213F67DE0008B38A /* Archive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Archive.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4F4213F67DE0008B38A /* FESurface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FESurface.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4F5213F67DE0008B38A /* NLConstraintDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NLConstraintDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4F6213F67DE0008B38A /* FEMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMesh.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4F7213F67DE0008B38A /* FETransform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FETransform.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4FA213F67DE0008B38A /* colsol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = colsol.cpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4FB213F67DE0008B38A /* FEDomain2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEDomain2D.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4FC213F67DE0008B38A /* ElementDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD5B9E4FD213F67DE0008B38A /* tens5ds.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tens5ds.hpp; sourceTree = \"<group>\"; };\n\t\tD5B9E4FE213F67DE0008B38A /* FEOctree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEOctree.h; sourceTree = \"<group>\"; };\n\t\tD5BC9F5126D07B29003BBF6E /* DomainDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DomainDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD5BC9F5226D07B29003BBF6E /* DomainDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DomainDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD5BC9F5326D07B29003BBF6E /* SurfaceDataRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SurfaceDataRecord.h; sourceTree = \"<group>\"; };\n\t\tD5BC9F5426D07B29003BBF6E /* SurfaceDataRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SurfaceDataRecord.cpp; sourceTree = \"<group>\"; };\n\t\tD5BE6F7F2423B4FB009AE76F /* FENodeSetConstraint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FENodeSetConstraint.cpp; sourceTree = \"<group>\"; };\n\t\tD5BE6F802423B4FB009AE76F /* FENodeSetConstraint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FENodeSetConstraint.h; sourceTree = \"<group>\"; };\n\t\tD5D56E842355239E0078BCC4 /* ClassDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClassDescriptor.h; sourceTree = \"<group>\"; };\n\t\tD5E82FD026206FCF00B23605 /* gamma.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = gamma.cpp; sourceTree = \"<group>\"; };\n\t\tD5E82FD126206FCF00B23605 /* gamma.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gamma.h; sourceTree = \"<group>\"; };\n\t\tD5E82FD42620A6FD00B23605 /* BSpline.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BSpline.cpp; sourceTree = \"<group>\"; };\n\t\tD5E82FD52620A6FD00B23605 /* BSpline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSpline.h; sourceTree = \"<group>\"; };\n\t\tD5E85D9B22021E8A00F5DF83 /* FEFaceList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEFaceList.h; sourceTree = \"<group>\"; };\n\t\tD5E85D9D22021E8B00F5DF83 /* FEMeshTopo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEMeshTopo.h; sourceTree = \"<group>\"; };\n\t\tD5E85D9E22021E8B00F5DF83 /* FEFaceList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEFaceList.cpp; sourceTree = \"<group>\"; };\n\t\tD5E85D9F22021E8B00F5DF83 /* FEMeshTopo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEMeshTopo.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1946121908644000F738D /* fecore_enum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fecore_enum.h; sourceTree = \"<group>\"; };\n\t\tD5F1946321908644000F738D /* CompactMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompactMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5F1946421908644000F738D /* DenseMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DenseMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1946521908644000F738D /* SchurComplement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchurComplement.h; sourceTree = \"<group>\"; };\n\t\tD5F1946621908645000F738D /* CSRMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSRMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1946821908645000F738D /* DenseMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DenseMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5F1946921908645000F738D /* SchurComplement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SchurComplement.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1946D21908645000F738D /* CSRMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSRMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5F1946E21908646000F738D /* CompactMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompactMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1947021908646000F738D /* Preconditioner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Preconditioner.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1947121908646000F738D /* Preconditioner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Preconditioner.h; sourceTree = \"<group>\"; };\n\t\tD5FAD69A26FF93420043FC69 /* FELogDomainVolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FELogDomainVolume.cpp; sourceTree = \"<group>\"; };\n\t\tD5FAD69B26FF93420043FC69 /* FELogDomainVolume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FELogDomainVolume.h; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5B9E3DA213F67CE0008B38A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5B9E3D4213F67CE0008B38A = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E3EB213F67DE0008B38A /* FECore */,\n\t\t\t\tD5B9E3DE213F67CE0008B38A /* Products */,\n\t\t\t\tD5E1B59C22B82A0A00E7939E /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5B9E3DE213F67CE0008B38A /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E3DD213F67CE0008B38A /* libFECore.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5B9E3EB213F67DE0008B38A /* FECore */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5B9E4F3213F67DE0008B38A /* Archive.cpp */,\n\t\t\t\tD5B9E3FE213F67DE0008B38A /* Archive.h */,\n\t\t\t\tD5B9E426213F67DE0008B38A /* BFGSSolver.cpp */,\n\t\t\t\tD5B9E4B9213F67DE0008B38A /* BFGSSolver.h */,\n\t\t\t\tD5E82FD42620A6FD00B23605 /* BSpline.cpp */,\n\t\t\t\tD5E82FD52620A6FD00B23605 /* BSpline.h */,\n\t\t\t\tD5B9E40C213F67DE0008B38A /* Callback.cpp */,\n\t\t\t\tD5B9E4E0213F67DE0008B38A /* Callback.h */,\n\t\t\t\tD5D56E842355239E0078BCC4 /* ClassDescriptor.h */,\n\t\t\t\tD5B9E4FA213F67DE0008B38A /* colsol.cpp */,\n\t\t\t\tD5F1946E21908646000F738D /* CompactMatrix.cpp */,\n\t\t\t\tD5F1946321908644000F738D /* CompactMatrix.h */,\n\t\t\t\tD5F1946621908645000F738D /* CSRMatrix.cpp */,\n\t\t\t\tD5F1946D21908645000F738D /* CSRMatrix.h */,\n\t\t\t\tD5B9E4D5213F67DE0008B38A /* DataRecord.cpp */,\n\t\t\t\tD5B9E4AB213F67DE0008B38A /* DataRecord.h */,\n\t\t\t\tD5B9E472213F67DE0008B38A /* DataStore.cpp */,\n\t\t\t\tD5B9E42E213F67DE0008B38A /* DataStore.h */,\n\t\t\t\tD5F1946421908644000F738D /* DenseMatrix.cpp */,\n\t\t\t\tD5F1946821908645000F738D /* DenseMatrix.h */,\n\t\t\t\tD5B9E4BA213F67DE0008B38A /* DOFS.cpp */,\n\t\t\t\tD5B9E4BF213F67DE0008B38A /* DOFS.h */,\n\t\t\t\tD5BC9F5226D07B29003BBF6E /* DomainDataRecord.cpp */,\n\t\t\t\tD5BC9F5126D07B29003BBF6E /* DomainDataRecord.h */,\n\t\t\t\tD5B9E49B213F67DE0008B38A /* DumpFile.cpp */,\n\t\t\t\tD5B9E454213F67DE0008B38A /* DumpFile.h */,\n\t\t\t\tD5B9E448213F67DE0008B38A /* DumpMemStream.cpp */,\n\t\t\t\tD5B9E4EB213F67DE0008B38A /* DumpMemStream.h */,\n\t\t\t\tD5B9E49E213F67DE0008B38A /* DumpStream.cpp */,\n\t\t\t\tD5B9E43A213F67DE0008B38A /* DumpStream.h */,\n\t\t\t\tD5B9E42B213F67DE0008B38A /* eig3.cpp */,\n\t\t\t\tD5B9E45E213F67DE0008B38A /* eig3.h */,\n\t\t\t\tD550834C24F086E300E919D8 /* EigenSolver.cpp */,\n\t\t\t\tD550834D24F086E300E919D8 /* EigenSolver.h */,\n\t\t\t\tD5B9E45F213F67DE0008B38A /* ElementDataRecord.cpp */,\n\t\t\t\tD5B9E4FC213F67DE0008B38A /* ElementDataRecord.h */,\n\t\t\t\tD50D09EE261E2ED3005564C6 /* expint_Ei.cpp */,\n\t\t\t\tD50D09EF261E2ED3005564C6 /* expint_Ei.h */,\n\t\t\t\tD54626F2260E6FDA007E2D8E /* FaceDataRecord.cpp */,\n\t\t\t\tD54626F3260E6FDA007E2D8E /* FaceDataRecord.h */,\n\t\t\t\tD5B9E415213F67DE0008B38A /* FEAnalysis.cpp */,\n\t\t\t\tD5B9E4A3213F67DE0008B38A /* FEAnalysis.h */,\n\t\t\t\tD5B9E421213F67DE0008B38A /* FEBodyLoad.cpp */,\n\t\t\t\tD5B9E475213F67DE0008B38A /* FEBodyLoad.h */,\n\t\t\t\tD5B9E4D8213F67DE0008B38A /* FEBoundaryCondition.cpp */,\n\t\t\t\tD5B9E4B7213F67DE0008B38A /* FEBoundaryCondition.h */,\n\t\t\t\tD5B9E410213F67DE0008B38A /* FEBoundingBox.h */,\n\t\t\t\tD5B9E4E6213F67DE0008B38A /* FEBox.cpp */,\n\t\t\t\tD5B9E48E213F67DE0008B38A /* FEBox.h */,\n\t\t\t\tD5B9E42A213F67DE0008B38A /* FEBroydenStrategy.cpp */,\n\t\t\t\tD5B9E3F2213F67DE0008B38A /* FEBroydenStrategy.h */,\n\t\t\t\tD5B9E400213F67DE0008B38A /* FECallback.cpp */,\n\t\t\t\tD5B9E49D213F67DE0008B38A /* FECallBack.h */,\n\t\t\t\tD5B9E48F213F67DE0008B38A /* FEClosestPointProjection.cpp */,\n\t\t\t\tD5B9E3FC213F67DE0008B38A /* FEClosestPointProjection.h */,\n\t\t\t\tD5613D48217B604E007CAB89 /* FEConstDataGenerator.h */,\n\t\t\t\tD58FA88024A1631400FC768B /* FEConstValueVec3.cpp */,\n\t\t\t\tD58FA88124A1631400FC768B /* FEConstValueVec3.h */,\n\t\t\t\tD5B9E460213F67DE0008B38A /* fecore_api.h */,\n\t\t\t\tD5B9E4F0213F67DE0008B38A /* fecore_debug_t.h */,\n\t\t\t\tD5B9E4CE213F67DE0008B38A /* fecore_debug.cpp */,\n\t\t\t\tD5B9E405213F67DE0008B38A /* fecore_debug.h */,\n\t\t\t\tD5F1946121908644000F738D /* fecore_enum.h */,\n\t\t\t\tD5613D46217B604E007CAB89 /* fecore_type.cpp */,\n\t\t\t\tD5613D45217B604E007CAB89 /* fecore_type.h */,\n\t\t\t\tD5B9E4EE213F67DE0008B38A /* FECore.cpp */,\n\t\t\t\tD5B9E47A213F67DE0008B38A /* FECore.h */,\n\t\t\t\tD5B9E499213F67DE0008B38A /* FECoreBase.cpp */,\n\t\t\t\tD5B9E44A213F67DE0008B38A /* FECoreBase.h */,\n\t\t\t\tD5B9E4A5213F67DE0008B38A /* FECoreFactory.cpp */,\n\t\t\t\tD5B9E3F4213F67DE0008B38A /* FECoreFactory.h */,\n\t\t\t\tD5B9E48C213F67DE0008B38A /* FECoreKernel.cpp */,\n\t\t\t\tD5B9E44F213F67DE0008B38A /* FECoreKernel.h */,\n\t\t\t\tD5B9E4C0213F67DE0008B38A /* FECorePlot.cpp */,\n\t\t\t\tD5B9E463213F67DE0008B38A /* FECorePlot.h */,\n\t\t\t\tD5B9E456213F67DE0008B38A /* FECoreTask.cpp */,\n\t\t\t\tD5B9E467213F67DE0008B38A /* FECoreTask.h */,\n\t\t\t\tD5B9E3F6213F67DE0008B38A /* FECube.cpp */,\n\t\t\t\tD5B9E4EC213F67DE0008B38A /* FECube.h */,\n\t\t\t\tD5B9E469213F67DE0008B38A /* FEDataArray.cpp */,\n\t\t\t\tD5B9E440213F67DE0008B38A /* FEDataArray.h */,\n\t\t\t\tD5B9E47F213F67DE0008B38A /* FEDataExport.cpp */,\n\t\t\t\tD5B9E4F1213F67DE0008B38A /* FEDataExport.h */,\n\t\t\t\tD5B9E417213F67DE0008B38A /* FEDataGenerator.cpp */,\n\t\t\t\tD5B9E4B6213F67DE0008B38A /* FEDataGenerator.h */,\n\t\t\t\tD565CDD7215D290B00E08ED6 /* FEDataMap.cpp */,\n\t\t\t\tD565CDDB215D290B00E08ED6 /* FEDataMap.h */,\n\t\t\t\tD5B9E4E2213F67DE0008B38A /* FEDataMathGenerator.cpp */,\n\t\t\t\tD5B9E418213F67DE0008B38A /* FEDataMathGenerator.h */,\n\t\t\t\tD5B9E4AE213F67DE0008B38A /* FEDataStream.h */,\n\t\t\t\tD5B9E461213F67DE0008B38A /* FEDiscreteDomain.cpp */,\n\t\t\t\tD5B9E4C4213F67DE0008B38A /* FEDiscreteDomain.h */,\n\t\t\t\tD5B9E4EF213F67DE0008B38A /* FEDiscreteMaterial.cpp */,\n\t\t\t\tD5B9E3EE213F67DE0008B38A /* FEDiscreteMaterial.h */,\n\t\t\t\tD54E21AB2149BB56008A9DD3 /* FEDiscreteSet.cpp */,\n\t\t\t\tD54E21A82149BB55008A9DD3 /* FEDiscreteSet.h */,\n\t\t\t\tD5709DA322833034007CAB0A /* FEDofList.cpp */,\n\t\t\t\tD5709DA522833034007CAB0A /* FEDofList.h */,\n\t\t\t\tD5B9E47D213F67DE0008B38A /* FEDomain.cpp */,\n\t\t\t\tD5B9E488213F67DE0008B38A /* FEDomain.h */,\n\t\t\t\tD5B9E4A8213F67DE0008B38A /* FEDomain2D.cpp */,\n\t\t\t\tD5B9E4FB213F67DE0008B38A /* FEDomain2D.h */,\n\t\t\t\tD565CDD8215D290B00E08ED6 /* FEDomainList.cpp */,\n\t\t\t\tD565CDD5215D290B00E08ED6 /* FEDomainList.h */,\n\t\t\t\tD565CDD9215D290B00E08ED6 /* FEDomainMap.cpp */,\n\t\t\t\tD565CDD4215D290B00E08ED6 /* FEDomainMap.h */,\n\t\t\t\tD52D83FA21CE89A200472620 /* FEDomainParameter.cpp */,\n\t\t\t\tD52D83F121CE89A100472620 /* FEDomainParameter.h */,\n\t\t\t\tD5B9E40D213F67DE0008B38A /* FEEdge.cpp */,\n\t\t\t\tD5B9E487213F67DE0008B38A /* FEEdge.h */,\n\t\t\t\tD5AB623E220289E200AAF141 /* FEEdgeList.cpp */,\n\t\t\t\tD5AB623D220289E200AAF141 /* FEEdgeList.h */,\n\t\t\t\tD5B9E4CF213F67DE0008B38A /* FEEdgeLoad.cpp */,\n\t\t\t\tD5B9E442213F67DE0008B38A /* FEEdgeLoad.h */,\n\t\t\t\tD5B9E451213F67DE0008B38A /* FEElemElemList.cpp */,\n\t\t\t\tD5B9E4A1213F67DE0008B38A /* FEElemElemList.h */,\n\t\t\t\tD5B9E465213F67DE0008B38A /* FEElement.cpp */,\n\t\t\t\tD5B9E43D213F67DE0008B38A /* FEElement.h */,\n\t\t\t\tD5B9E47B213F67DE0008B38A /* FEElementLibrary.cpp */,\n\t\t\t\tD5B9E477213F67DE0008B38A /* FEElementLibrary.h */,\n\t\t\t\tD5B9E49A213F67DE0008B38A /* FEElementList.cpp */,\n\t\t\t\tD5B9E4DF213F67DE0008B38A /* FEElementList.h */,\n\t\t\t\tD54E219E2149BB54008A9DD3 /* FEElementSet.cpp */,\n\t\t\t\tD54E21A72149BB55008A9DD3 /* FEElementSet.h */,\n\t\t\t\tD5006A7621AAF98000031CB6 /* FEElementShape.cpp */,\n\t\t\t\tD5006A7721AAF98100031CB6 /* FEElementShape.h */,\n\t\t\t\tD5B9E4DB213F67DE0008B38A /* FEElementTraits.cpp */,\n\t\t\t\tD5B9E414213F67DE0008B38A /* FEElementTraits.h */,\n\t\t\t\tD5B9E437213F67DE0008B38A /* FEException.cpp */,\n\t\t\t\tD5B9E478213F67DE0008B38A /* FEException.h */,\n\t\t\t\tD5E85D9E22021E8B00F5DF83 /* FEFaceList.cpp */,\n\t\t\t\tD5E85D9B22021E8A00F5DF83 /* FEFaceList.h */,\n\t\t\t\tD54E21A32149BB55008A9DD3 /* FEFacetSet.cpp */,\n\t\t\t\tD54E21A02149BB54008A9DD3 /* FEFacetSet.h */,\n\t\t\t\tD54E21C421517EEB008A9DD3 /* FEFixedBC.cpp */,\n\t\t\t\tD54E21C521517EEB008A9DD3 /* FEFixedBC.h */,\n\t\t\t\tD5B9E3FA213F67DE0008B38A /* FEFunction1D.cpp */,\n\t\t\t\tD5B9E41B213F67DE0008B38A /* FEFunction1D.h */,\n\t\t\t\tD5B9E45C213F67DE0008B38A /* FEGlobalData.cpp */,\n\t\t\t\tD5B9E3F5213F67DE0008B38A /* FEGlobalData.h */,\n\t\t\t\tD5B9E4E8213F67DE0008B38A /* FEGlobalMatrix.cpp */,\n\t\t\t\tD5B9E44C213F67DE0008B38A /* FEGlobalMatrix.h */,\n\t\t\t\tD5B9E4BD213F67DE0008B38A /* FEGlobalVector.cpp */,\n\t\t\t\tD5B9E4CC213F67DE0008B38A /* FEGlobalVector.h */,\n\t\t\t\tD5B9E4DD213F67DE0008B38A /* FEInitialCondition.cpp */,\n\t\t\t\tD5B9E40B213F67DE0008B38A /* FEInitialCondition.h */,\n\t\t\t\tD565CDDA215D290B00E08ED6 /* FEItemList.cpp */,\n\t\t\t\tD565CDD6215D290B00E08ED6 /* FEItemList.h */,\n\t\t\t\tD5B9E4CD213F67DE0008B38A /* FELevelStructure.cpp */,\n\t\t\t\tD5B9E42D213F67DE0008B38A /* FELevelStructure.h */,\n\t\t\t\tD5B9E46A213F67DE0008B38A /* FELinearConstraint.cpp */,\n\t\t\t\tD5B9E3EC213F67DE0008B38A /* FELinearConstraint.h */,\n\t\t\t\tD5B9E4B5213F67DE0008B38A /* FELinearConstraintManager.cpp */,\n\t\t\t\tD5B9E458213F67DE0008B38A /* FELinearConstraintManager.h */,\n\t\t\t\tD5B9E4E3213F67DE0008B38A /* FELinearSolver.cpp */,\n\t\t\t\tD5B9E479213F67DE0008B38A /* FELinearSolver.h */,\n\t\t\t\tD5B9E497213F67DE0008B38A /* FELinearSystem.cpp */,\n\t\t\t\tD5B9E3FF213F67DE0008B38A /* FELinearSystem.h */,\n\t\t\t\tD5B9E4AA213F67DE0008B38A /* FELineSearch.cpp */,\n\t\t\t\tD5B9E4B2213F67DE0008B38A /* FELineSearch.h */,\n\t\t\t\tD5613D40217B604E007CAB89 /* FELoadController.cpp */,\n\t\t\t\tD5613D47217B604E007CAB89 /* FELoadController.h */,\n\t\t\t\tD5613D49217B604E007CAB89 /* FELoadCurve.cpp */,\n\t\t\t\tD5613D4F217B604F007CAB89 /* FELoadCurve.h */,\n\t\t\t\tD5FAD69A26FF93420043FC69 /* FELogDomainVolume.cpp */,\n\t\t\t\tD5FAD69B26FF93420043FC69 /* FELogDomainVolume.h */,\n\t\t\t\tD50D879326D930DC0034E121 /* FELogElementVolume.cpp */,\n\t\t\t\tD50D879026D930DB0034E121 /* FELogElementVolume.h */,\n\t\t\t\tD50D879226D930DC0034E121 /* FELogEnclosedVolume.cpp */,\n\t\t\t\tD50D879126D930DB0034E121 /* FELogEnclosedVolume.h */,\n\t\t\t\tD559C4C722D9169E00CDC2BD /* FEMat3dSphericalAngleMap.cpp */,\n\t\t\t\tD559C4C522D9169D00CDC2BD /* FEMat3dSphericalAngleMap.h */,\n\t\t\t\tD5400D42249922C200570A47 /* FEMat3dsValuator.cpp */,\n\t\t\t\tD5400D43249922C200570A47 /* FEMat3dsValuator.h */,\n\t\t\t\tD52D83F921CE89A200472620 /* FEMat3dValuator.cpp */,\n\t\t\t\tD52D83F821CE89A200472620 /* FEMat3dValuator.h */,\n\t\t\t\tD5B9E4E9213F67DE0008B38A /* FEMaterial.cpp */,\n\t\t\t\tD5B9E3FD213F67DE0008B38A /* FEMaterial.h */,\n\t\t\t\tD5B9E41F213F67DE0008B38A /* FEMaterialPoint.cpp */,\n\t\t\t\tD5B9E481213F67DE0008B38A /* FEMaterialPoint.h */,\n\t\t\t\tD5613D3F217B604E007CAB89 /* FEMaterialPointProperty.h */,\n\t\t\t\tD5613D44217B604E007CAB89 /* FEMathController.cpp */,\n\t\t\t\tD5613D43217B604E007CAB89 /* FEMathController.h */,\n\t\t\t\tD5B9E4D0213F67DE0008B38A /* FEMesh.cpp */,\n\t\t\t\tD5B9E4F6213F67DE0008B38A /* FEMesh.h */,\n\t\t\t\tD5B805B2223BE2DC00198805 /* FEMeshAdaptor.cpp */,\n\t\t\t\tD5B805B1223BE2DC00198805 /* FEMeshAdaptor.h */,\n\t\t\t\tD5AE8CC52592ECCB003AC08B /* FEMeshAdaptorCriterion.cpp */,\n\t\t\t\tD5AE8CC42592ECCB003AC08B /* FEMeshAdaptorCriterion.h */,\n\t\t\t\tD5613D3A217B604D007CAB89 /* FEMeshPartition.cpp */,\n\t\t\t\tD5613D3B217B604D007CAB89 /* FEMeshPartition.h */,\n\t\t\t\tD5E85D9F22021E8B00F5DF83 /* FEMeshTopo.cpp */,\n\t\t\t\tD5E85D9D22021E8B00F5DF83 /* FEMeshTopo.h */,\n\t\t\t\tD5B9E459213F67DE0008B38A /* FEModel.cpp */,\n\t\t\t\tD5B9E450213F67DE0008B38A /* FEModel.h */,\n\t\t\t\tD5B9E491213F67DE0008B38A /* FEModelComponent.cpp */,\n\t\t\t\tD5B9E476213F67DE0008B38A /* FEModelComponent.h */,\n\t\t\t\tD5B9E480213F67DE0008B38A /* FEModelData.cpp */,\n\t\t\t\tD5B9E428213F67DE0008B38A /* FEModelData.h */,\n\t\t\t\tD5B9E411213F67DE0008B38A /* FEModelLoad.cpp */,\n\t\t\t\tD5B9E429213F67DE0008B38A /* FEModelLoad.h */,\n\t\t\t\tD54E21CD21517EEC008A9DD3 /* FEModelParam.cpp */,\n\t\t\t\tD54E21BB21517EEA008A9DD3 /* FEModelParam.h */,\n\t\t\t\tD5B9E496213F67DE0008B38A /* FENewtonSolver.cpp */,\n\t\t\t\tD5B9E43C213F67DE0008B38A /* FENewtonSolver.h */,\n\t\t\t\tD5B9E445213F67DE0008B38A /* FENewtonStrategy.cpp */,\n\t\t\t\tD5B9E43B213F67DE0008B38A /* FENewtonStrategy.h */,\n\t\t\t\tD5B9E4B0213F67DE0008B38A /* FENLConstraint.cpp */,\n\t\t\t\tD5B9E420213F67DE0008B38A /* FENLConstraint.h */,\n\t\t\t\tD5B9E455213F67DE0008B38A /* FENNQuery.cpp */,\n\t\t\t\tD5B9E4B3213F67DE0008B38A /* FENNQuery.h */,\n\t\t\t\tD54E21BD21517EEA008A9DD3 /* FENodalLoad.cpp */,\n\t\t\t\tD54E21CE21517EEC008A9DD3 /* FENodalLoad.h */,\n\t\t\t\tD54E21A62149BB55008A9DD3 /* FENode.cpp */,\n\t\t\t\tD54E219F2149BB54008A9DD3 /* FENode.h */,\n\t\t\t\tD5B9E3F8213F67DE0008B38A /* FENodeDataMap.cpp */,\n\t\t\t\tD5B9E4DE213F67DE0008B38A /* FENodeDataMap.h */,\n\t\t\t\tD5B9E464213F67DE0008B38A /* FENodeElemList.cpp */,\n\t\t\t\tD5B9E4C6213F67DE0008B38A /* FENodeElemList.h */,\n\t\t\t\tD5709DA122833034007CAB0A /* FENodeList.cpp */,\n\t\t\t\tD5709DA222833034007CAB0A /* FENodeList.h */,\n\t\t\t\tD5B9E490213F67DE0008B38A /* FENodeNodeList.cpp */,\n\t\t\t\tD5B9E4BE213F67DE0008B38A /* FENodeNodeList.h */,\n\t\t\t\tD5B9E434213F67DE0008B38A /* FENodeReorder.cpp */,\n\t\t\t\tD5B9E4D7213F67DE0008B38A /* FENodeReorder.h */,\n\t\t\t\tD54E21AA2149BB55008A9DD3 /* FENodeSet.cpp */,\n\t\t\t\tD54E21A92149BB55008A9DD3 /* FENodeSet.h */,\n\t\t\t\tD5BE6F7F2423B4FB009AE76F /* FENodeSetConstraint.cpp */,\n\t\t\t\tD5BE6F802423B4FB009AE76F /* FENodeSetConstraint.h */,\n\t\t\t\tD5B9E432213F67DE0008B38A /* FENormalProjection.cpp */,\n\t\t\t\tD5B9E47E213F67DE0008B38A /* FENormalProjection.h */,\n\t\t\t\tD5B9E4ED213F67DE0008B38A /* FEOctree.cpp */,\n\t\t\t\tD5B9E4FE213F67DE0008B38A /* FEOctree.h */,\n\t\t\t\tD5A37D292286167300867D77 /* FEOctreeSearch.cpp */,\n\t\t\t\tD5A37D282286167300867D77 /* FEOctreeSearch.h */,\n\t\t\t\tD559C4C622D9169D00CDC2BD /* FEParabolicMap.cpp */,\n\t\t\t\tD559C4C822D9169E00CDC2BD /* FEParabolicMap.h */,\n\t\t\t\tD5B9E433213F67DE0008B38A /* FEParam.cpp */,\n\t\t\t\tD5B9E42F213F67DE0008B38A /* FEParam.h */,\n\t\t\t\tD5B9E436213F67DE0008B38A /* FEParameterList.cpp */,\n\t\t\t\tD5B9E4B8213F67DE0008B38A /* FEParameterList.h */,\n\t\t\t\tD5B9E449213F67DE0008B38A /* FEParamValidator.cpp */,\n\t\t\t\tD5B9E40F213F67DE0008B38A /* FEParamValidator.h */,\n\t\t\t\tD51EBF9D217E3AED006F73C4 /* FEPIDController.cpp */,\n\t\t\t\tD51EBF9E217E3AED006F73C4 /* FEPIDController.h */,\n\t\t\t\tD5B9E416213F67DE0008B38A /* FEPlotData.cpp */,\n\t\t\t\tD5B9E4E1213F67DE0008B38A /* FEPlotData.h */,\n\t\t\t\tD5613D4A217B604E007CAB89 /* FEPointFunction.cpp */,\n\t\t\t\tD5613D41217B604E007CAB89 /* FEPointFunction.h */,\n\t\t\t\tD54E21BC21517EEA008A9DD3 /* FEPrescribedBC.cpp */,\n\t\t\t\tD54E21C921517EEC008A9DD3 /* FEPrescribedBC.h */,\n\t\t\t\tD54E21D721517EED008A9DD3 /* FEPrescribedDOF.cpp */,\n\t\t\t\tD54E21CF21517EEC008A9DD3 /* FEPrescribedDOF.h */,\n\t\t\t\tD5B9E453213F67DE0008B38A /* FEProperty.cpp */,\n\t\t\t\tD5B9E468213F67DE0008B38A /* FEProperty.h */,\n\t\t\t\tD510616C217CDD1600CF1690 /* FEPropertyT.h */,\n\t\t\t\tD52D83F721CE89A200472620 /* FEScalarValuator.cpp */,\n\t\t\t\tD52D83F521CE89A100472620 /* FEScalarValuator.h */,\n\t\t\t\tD54E21A22149BB54008A9DD3 /* FESegmentSet.cpp */,\n\t\t\t\tD54E21A52149BB55008A9DD3 /* FESegmentSet.h */,\n\t\t\t\tD5B9E470213F67DE0008B38A /* FEShellDomain.cpp */,\n\t\t\t\tD5B9E495213F67DE0008B38A /* FEShellDomain.h */,\n\t\t\t\tD56B208A23AD5F94000AE9C2 /* FEShellElement.cpp */,\n\t\t\t\tD56B208723AD5F94000AE9C2 /* FEShellElement.h */,\n\t\t\t\tD5B9E441213F67DE0008B38A /* FESolidDomain.cpp */,\n\t\t\t\tD5B9E473213F67DE0008B38A /* FESolidDomain.h */,\n\t\t\t\tD56B208923AD5F94000AE9C2 /* FESolidElement.cpp */,\n\t\t\t\tD56B208823AD5F94000AE9C2 /* FESolidElement.h */,\n\t\t\t\tD56B208623AD5F94000AE9C2 /* FESolidElementShape.cpp */,\n\t\t\t\tD56B208323AD5F94000AE9C2 /* FESolidElementShape.h */,\n\t\t\t\tD5B9E486213F67DE0008B38A /* FESolver.cpp */,\n\t\t\t\tD5B9E46E213F67DE0008B38A /* FESolver.h */,\n\t\t\t\tD5613D3C217B604E007CAB89 /* FESPRProjection.cpp */,\n\t\t\t\tD5613D4B217B604E007CAB89 /* FESPRProjection.h */,\n\t\t\t\tD5B9E46C213F67DE0008B38A /* FESurface.cpp */,\n\t\t\t\tD5B9E4F4213F67DE0008B38A /* FESurface.h */,\n\t\t\t\tD5B9E493213F67DE0008B38A /* FESurfaceConstraint.cpp */,\n\t\t\t\tD5B9E48D213F67DE0008B38A /* FESurfaceConstraint.h */,\n\t\t\t\tD56B208B23AD5F94000AE9C2 /* FESurfaceElement.cpp */,\n\t\t\t\tD56B208C23AD5F94000AE9C2 /* FESurfaceElement.h */,\n\t\t\t\tD56B208423AD5F94000AE9C2 /* FESurfaceElementShape.cpp */,\n\t\t\t\tD56B208523AD5F94000AE9C2 /* FESurfaceElementShape.h */,\n\t\t\t\tD5B9E489213F67DE0008B38A /* FESurfaceLoad.cpp */,\n\t\t\t\tD5B9E43F213F67DE0008B38A /* FESurfaceLoad.h */,\n\t\t\t\tD5B9E4BC213F67DE0008B38A /* FESurfaceMap.cpp */,\n\t\t\t\tD5B9E4C7213F67DE0008B38A /* FESurfaceMap.h */,\n\t\t\t\tD54E21A42149BB55008A9DD3 /* FESurfacePair.cpp */,\n\t\t\t\tD54E21A12149BB54008A9DD3 /* FESurfacePair.h */,\n\t\t\t\tD5B9E45D213F67DE0008B38A /* FESurfacePairConstraint.cpp */,\n\t\t\t\tD5B9E4C2213F67DE0008B38A /* FESurfacePairConstraint.h */,\n\t\t\t\tD5044A652603DC8500C81BC4 /* FESurfacePairConstraintNL.cpp */,\n\t\t\t\tD5044A662603DC8500C81BC4 /* FESurfacePairConstraintNL.h */,\n\t\t\t\tD5613D4E217B604F007CAB89 /* FESurfaceToSurfaceMap.cpp */,\n\t\t\t\tD5613D3D217B604E007CAB89 /* FESurfaceToSurfaceMap.h */,\n\t\t\t\tD5B9E484213F67DE0008B38A /* FETimeInfo.cpp */,\n\t\t\t\tD5B9E4AD213F67DE0008B38A /* FETimeInfo.h */,\n\t\t\t\tD5B9E439213F67DE0008B38A /* FETimeStepController.cpp */,\n\t\t\t\tD5B9E4D3213F67DE0008B38A /* FETimeStepController.h */,\n\t\t\t\tD5B9E4F7213F67DE0008B38A /* FETransform.cpp */,\n\t\t\t\tD5B9E4DC213F67DE0008B38A /* FETransform.h */,\n\t\t\t\tD5B9E4BB213F67DE0008B38A /* FETrussDomain.cpp */,\n\t\t\t\tD5B9E435213F67DE0008B38A /* FETrussDomain.h */,\n\t\t\t\tD52D83F221CE89A100472620 /* FEValuator.h */,\n\t\t\t\tD52D83F321CE89A100472620 /* FEVec3dValuator.cpp */,\n\t\t\t\tD52D83F421CE89A100472620 /* FEVec3dValuator.h */,\n\t\t\t\tD54A66AD257BDCB90023C8D1 /* FSPath.cpp */,\n\t\t\t\tD54A66AE257BDCB90023C8D1 /* FSPath.h */,\n\t\t\t\tD5E82FD026206FCF00B23605 /* gamma.cpp */,\n\t\t\t\tD5E82FD126206FCF00B23605 /* gamma.h */,\n\t\t\t\tD5B9E40A213F67DE0008B38A /* Image.cpp */,\n\t\t\t\tD5B9E4EA213F67DE0008B38A /* Image.h */,\n\t\t\t\tD5B9E427213F67DE0008B38A /* Integrate.cpp */,\n\t\t\t\tD5B9E41E213F67DE0008B38A /* Integrate.h */,\n\t\t\t\tD5B9E3F1213F67DE0008B38A /* JFNKMatrix.cpp */,\n\t\t\t\tD5B9E4AC213F67DE0008B38A /* JFNKMatrix.h */,\n\t\t\t\tD5B9E492213F67DE0008B38A /* JFNKStrategy.cpp */,\n\t\t\t\tD5B9E4A7213F67DE0008B38A /* JFNKStrategy.h */,\n\t\t\t\tD5B9E41C213F67DE0008B38A /* LinearSolver.cpp */,\n\t\t\t\tD5B9E46B213F67DE0008B38A /* LinearSolver.h */,\n\t\t\t\tD5B9E483213F67DE0008B38A /* log.h */,\n\t\t\t\tD5B9E4B1213F67DE0008B38A /* mat2d.h */,\n\t\t\t\tD5B9E4E4213F67DE0008B38A /* mat3d.cpp */,\n\t\t\t\tD5B9E4F2213F67DE0008B38A /* mat3d.h */,\n\t\t\t\tD5B9E40E213F67DE0008B38A /* mat3d.hpp */,\n\t\t\t\tD5B9E4CA213F67DE0008B38A /* mat6d.h */,\n\t\t\t\tD5946E1F24B524F90009C22F /* mathalg.cpp */,\n\t\t\t\tD5946E2024B524F90009C22F /* mathalg.h */,\n\t\t\t\tD54E21C621517EEB008A9DD3 /* MathObject.cpp */,\n\t\t\t\tD54E21C321517EEB008A9DD3 /* MathObject.h */,\n\t\t\t\tD5B9E49F213F67DE0008B38A /* matrix.cpp */,\n\t\t\t\tD5B9E447213F67DE0008B38A /* matrix.h */,\n\t\t\t\tD5B9E3ED213F67DE0008B38A /* MatrixOperator.h */,\n\t\t\t\tD5B9E46D213F67DE0008B38A /* MatrixProfile.cpp */,\n\t\t\t\tD5B9E482213F67DE0008B38A /* MatrixProfile.h */,\n\t\t\t\tD54E21D421517EED008A9DD3 /* MCollect.cpp */,\n\t\t\t\tD54E21C121517EEB008A9DD3 /* MDerive.cpp */,\n\t\t\t\tD54E21DB21517EEE008A9DD3 /* MEvaluate.cpp */,\n\t\t\t\tD54E21BF21517EEA008A9DD3 /* MEvaluate.h */,\n\t\t\t\tD54E21D121517EED008A9DD3 /* MExpand.cpp */,\n\t\t\t\tD54E21BA21517EEA008A9DD3 /* MFunctions.cpp */,\n\t\t\t\tD54E21DA21517EEE008A9DD3 /* MFunctions.h */,\n\t\t\t\tD54E21BE21517EEA008A9DD3 /* MIntegral.cpp */,\n\t\t\t\tD54E21D221517EED008A9DD3 /* MItem.cpp */,\n\t\t\t\tD54E21CA21517EEC008A9DD3 /* MItem.h */,\n\t\t\t\tD54E21C721517EEB008A9DD3 /* MMath.h */,\n\t\t\t\tD54E21D321517EED008A9DD3 /* MMatrix.cpp */,\n\t\t\t\tD54E21D021517EED008A9DD3 /* MMatrix.h */,\n\t\t\t\tD54E21C821517EEC008A9DD3 /* MObj2String.cpp */,\n\t\t\t\tD54E21CC21517EEC008A9DD3 /* MObj2String.h */,\n\t\t\t\tD54E21C221517EEB008A9DD3 /* MObjBuilder.cpp */,\n\t\t\t\tD54E21CB21517EEC008A9DD3 /* MObjBuilder.h */,\n\t\t\t\tD5B9E3F0213F67DE0008B38A /* mortar.cpp */,\n\t\t\t\tD5B9E444213F67DE0008B38A /* mortar.h */,\n\t\t\t\tD54E21D521517EED008A9DD3 /* MReplace.cpp */,\n\t\t\t\tD54E21D821517EEE008A9DD3 /* MSimplify.cpp */,\n\t\t\t\tD54E21D921517EEE008A9DD3 /* MSolve.cpp */,\n\t\t\t\tD54E21D621517EED008A9DD3 /* MTypes.cpp */,\n\t\t\t\tD54E21C021517EEA008A9DD3 /* MTypes.h */,\n\t\t\t\tD5B9E474213F67DE0008B38A /* NLConstraintDataRecord.cpp */,\n\t\t\t\tD5B9E4F5213F67DE0008B38A /* NLConstraintDataRecord.h */,\n\t\t\t\tD5B9E423213F67DE0008B38A /* NodeDataRecord.cpp */,\n\t\t\t\tD5B9E3F3213F67DE0008B38A /* NodeDataRecord.h */,\n\t\t\t\tD5B9E430213F67DE0008B38A /* ParamString.cpp */,\n\t\t\t\tD5B9E446213F67DE0008B38A /* ParamString.h */,\n\t\t\t\tD5F1947021908646000F738D /* Preconditioner.cpp */,\n\t\t\t\tD5F1947121908646000F738D /* Preconditioner.h */,\n\t\t\t\tD5B9E44E213F67DE0008B38A /* qsort.cpp */,\n\t\t\t\tD5B9E49C213F67DE0008B38A /* quatd.cpp */,\n\t\t\t\tD5B9E41D213F67DE0008B38A /* quatd.h */,\n\t\t\t\tD5F1946921908645000F738D /* SchurComplement.cpp */,\n\t\t\t\tD5F1946521908644000F738D /* SchurComplement.h */,\n\t\t\t\tD5B9E4A4213F67DE0008B38A /* sdk.h */,\n\t\t\t\tD5B9E45A213F67DE0008B38A /* SparseMatrix.cpp */,\n\t\t\t\tD5B9E3EF213F67DE0008B38A /* SparseMatrix.h */,\n\t\t\t\tD5B9E4C9213F67DE0008B38A /* stdafx.cpp */,\n\t\t\t\tD5B9E406213F67DE0008B38A /* stdafx.h */,\n\t\t\t\tD5BC9F5426D07B29003BBF6E /* SurfaceDataRecord.cpp */,\n\t\t\t\tD5BC9F5326D07B29003BBF6E /* SurfaceDataRecord.h */,\n\t\t\t\tD5B9E412213F67DE0008B38A /* svd.cpp */,\n\t\t\t\tD5B9E4A6213F67DE0008B38A /* sys.h */,\n\t\t\t\tD5B9E4AF213F67DE0008B38A /* table.h */,\n\t\t\t\tD5B9E46F213F67DE0008B38A /* targetver.h */,\n\t\t\t\tD5B9E4A2213F67DE0008B38A /* tens3d.cpp */,\n\t\t\t\tD5B9E48B213F67DE0008B38A /* tens3d.h */,\n\t\t\t\tD5B9E452213F67DE0008B38A /* tens3d.hpp */,\n\t\t\t\tD5B9E4E7213F67DE0008B38A /* tens3dls.hpp */,\n\t\t\t\tD5B9E485213F67DE0008B38A /* tens3drs.hpp */,\n\t\t\t\tD5B9E4D2213F67DE0008B38A /* tens3ds.hpp */,\n\t\t\t\tD5B9E4D6213F67DE0008B38A /* tens4d.cpp */,\n\t\t\t\tD5B9E431213F67DE0008B38A /* tens4d.h */,\n\t\t\t\tD5B9E403213F67DE0008B38A /* tens4d.hpp */,\n\t\t\t\tD5A56F8223D4FBFE002F28CA /* tens4dmm.hpp */,\n\t\t\t\tD5B9E3FB213F67DE0008B38A /* tens4dms.hpp */,\n\t\t\t\tD5B9E498213F67DE0008B38A /* tens4ds.hpp */,\n\t\t\t\tD5B9E4DA213F67DE0008B38A /* tens5d.cpp */,\n\t\t\t\tD5B9E443213F67DE0008B38A /* tens5d.h */,\n\t\t\t\tD5B9E408213F67DE0008B38A /* tens5d.hpp */,\n\t\t\t\tD5B9E4FD213F67DE0008B38A /* tens5ds.hpp */,\n\t\t\t\tD5B9E3F9213F67DE0008B38A /* tens6d.cpp */,\n\t\t\t\tD5B9E462213F67DE0008B38A /* tens6d.h */,\n\t\t\t\tD5B9E4C8213F67DE0008B38A /* tens6d.hpp */,\n\t\t\t\tD5B9E4A9213F67DE0008B38A /* tens6ds.hpp */,\n\t\t\t\tD5B9E409213F67DE0008B38A /* tensor_base.h */,\n\t\t\t\tD5B9E438213F67DE0008B38A /* Timer.cpp */,\n\t\t\t\tD5B9E466213F67DE0008B38A /* Timer.h */,\n\t\t\t\tD5B9E4A0213F67DE0008B38A /* tools.cpp */,\n\t\t\t\tD5B9E494213F67DE0008B38A /* tools.h */,\n\t\t\t\tD5B9E43E213F67DE0008B38A /* vec2d.h */,\n\t\t\t\tD5B9E44D213F67DE0008B38A /* vec3d.h */,\n\t\t\t\tD5B9E471213F67DE0008B38A /* vector.cpp */,\n\t\t\t\tD5B9E4D4213F67DE0008B38A /* vector.h */,\n\t\t\t\tD5B9E404213F67DE0008B38A /* version.h */,\n\t\t\t\tD5613D4C217B604F007CAB89 /* writeplot.cpp */,\n\t\t\t\tD5613D42217B604E007CAB89 /* writeplot.h */,\n\t\t\t);\n\t\t\tname = FECore;\n\t\t\tpath = ../../FECore;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5E1B59C22B82A0A00E7939E /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5B9E3DB213F67CE0008B38A /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5613D58217B604F007CAB89 /* writeplot.h in Headers */,\n\t\t\t\tD5B9E5B7213F67DE0008B38A /* sdk.h in Headers */,\n\t\t\t\tD5613D59217B604F007CAB89 /* FEMathController.h in Headers */,\n\t\t\t\tD5B9E5DA213F67DE0008B38A /* FESurfaceMap.h in Headers */,\n\t\t\t\tD5B9E552213F67DE0008B38A /* FESurfaceLoad.h in Headers */,\n\t\t\t\tD5B9E557213F67DE0008B38A /* mortar.h in Headers */,\n\t\t\t\tD54E21DD21517EEE008A9DD3 /* FEModelParam.h in Headers */,\n\t\t\t\tD52D840321CE89A200472620 /* FEMat3dValuator.h in Headers */,\n\t\t\t\tD5B9E5CB213F67DE0008B38A /* FEParameterList.h in Headers */,\n\t\t\t\tD5B9E5E6213F67DE0008B38A /* FETimeStepController.h in Headers */,\n\t\t\t\tD5B9E575213F67DE0008B38A /* tens6d.h in Headers */,\n\t\t\t\tD5B9E59B213F67DE0008B38A /* FEDomain.h in Headers */,\n\t\t\t\tD5B9E521213F67DE0008B38A /* mat3d.hpp in Headers */,\n\t\t\t\tD5B9E5D5213F67DE0008B38A /* FESurfacePairConstraint.h in Headers */,\n\t\t\t\tD54E21AE2149BB56008A9DD3 /* FEFacetSet.h in Headers */,\n\t\t\t\tD5B9E5B4213F67DE0008B38A /* FEElemElemList.h in Headers */,\n\t\t\t\tD5613D5B217B604F007CAB89 /* fecore_type.h in Headers */,\n\t\t\t\tD54E21B32149BB56008A9DD3 /* FESegmentSet.h in Headers */,\n\t\t\t\tD52D83FD21CE89A200472620 /* FEValuator.h in Headers */,\n\t\t\t\tD5B9E591213F67DE0008B38A /* FENormalProjection.h in Headers */,\n\t\t\t\tD5613D55217B604F007CAB89 /* FEMaterialPointProperty.h in Headers */,\n\t\t\t\tD5B9E571213F67DE0008B38A /* eig3.h in Headers */,\n\t\t\t\tD54E21E221517EEE008A9DD3 /* MTypes.h in Headers */,\n\t\t\t\tD54E21EB21517EEE008A9DD3 /* FEPrescribedBC.h in Headers */,\n\t\t\t\tD5AE8CC62592ECCB003AC08B /* FEMeshAdaptorCriterion.h in Headers */,\n\t\t\t\tD5B9E57A213F67DE0008B38A /* FECoreTask.h in Headers */,\n\t\t\t\tD565CDDE215D290C00E08ED6 /* FEItemList.h in Headers */,\n\t\t\t\tD5B9E5F2213F67DE0008B38A /* FEElementList.h in Headers */,\n\t\t\t\tD5B9E5FD213F67DE0008B38A /* Image.h in Headers */,\n\t\t\t\tD5B9E603213F67DE0008B38A /* fecore_debug_t.h in Headers */,\n\t\t\t\tD52D83FF21CE89A200472620 /* FEVec3dValuator.h in Headers */,\n\t\t\t\tD5B9E5D7213F67DE0008B38A /* FEDiscreteDomain.h in Headers */,\n\t\t\t\tD5B9E54D213F67DE0008B38A /* DumpStream.h in Headers */,\n\t\t\t\tD5B9E519213F67DE0008B38A /* stdafx.h in Headers */,\n\t\t\t\tD5A37D2A2286167300867D77 /* FEOctreeSearch.h in Headers */,\n\t\t\t\tD510616D217CDD1600CF1690 /* FEPropertyT.h in Headers */,\n\t\t\t\tD5BC9F5726D07B29003BBF6E /* SurfaceDataRecord.h in Headers */,\n\t\t\t\tD54E21E921517EEE008A9DD3 /* MMath.h in Headers */,\n\t\t\t\tD54E21AF2149BB56008A9DD3 /* FESurfacePair.h in Headers */,\n\t\t\t\tD5B9E5CA213F67DE0008B38A /* FEBoundaryCondition.h in Headers */,\n\t\t\t\tD5B9E5A8213F67DE0008B38A /* FEShellDomain.h in Headers */,\n\t\t\t\tD5F1947921908646000F738D /* DenseMatrix.h in Headers */,\n\t\t\t\tD52D83FC21CE89A200472620 /* FEDomainParameter.h in Headers */,\n\t\t\t\tD5A56F8323D4FBFE002F28CA /* tens4dmm.hpp in Headers */,\n\t\t\t\tD5B9E595213F67DE0008B38A /* MatrixProfile.h in Headers */,\n\t\t\t\tD5B9E540213F67DE0008B38A /* FELevelStructure.h in Headers */,\n\t\t\t\tD5B9E4FF213F67DE0008B38A /* FELinearConstraint.h in Headers */,\n\t\t\t\tD5F1947421908646000F738D /* CompactMatrix.h in Headers */,\n\t\t\t\tD5613D5D217B604F007CAB89 /* FELoadController.h in Headers */,\n\t\t\t\tD5B9E5FA213F67DE0008B38A /* tens3dls.hpp in Headers */,\n\t\t\t\tD5FAD69D26FF93420043FC69 /* FELogDomainVolume.h in Headers */,\n\t\t\t\tD5B9E512213F67DE0008B38A /* FELinearSystem.h in Headers */,\n\t\t\t\tD5613D5E217B604F007CAB89 /* FEConstDataGenerator.h in Headers */,\n\t\t\t\tD5B9E506213F67DE0008B38A /* NodeDataRecord.h in Headers */,\n\t\t\t\tD54A66B0257BDCB90023C8D1 /* FSPath.h in Headers */,\n\t\t\t\tD5B9E516213F67DE0008B38A /* tens4d.hpp in Headers */,\n\t\t\t\tD5B9E511213F67DE0008B38A /* Archive.h in Headers */,\n\t\t\t\tD5E85DA322021E8C00F5DF83 /* FEFaceList.h in Headers */,\n\t\t\t\tD5B9E5F3213F67DE0008B38A /* Callback.h in Headers */,\n\t\t\t\tD5B9E55D213F67DE0008B38A /* FECoreBase.h in Headers */,\n\t\t\t\tD5B9E5AB213F67DE0008B38A /* tens4ds.hpp in Headers */,\n\t\t\t\tD5B9E58D213F67DE0008B38A /* FECore.h in Headers */,\n\t\t\t\tD5B9E576213F67DE0008B38A /* FECorePlot.h in Headers */,\n\t\t\t\tD5B9E53B213F67DE0008B38A /* FEModelData.h in Headers */,\n\t\t\t\tD54E21EC21517EEE008A9DD3 /* MItem.h in Headers */,\n\t\t\t\tD5B9E5CC213F67DE0008B38A /* BFGSSolver.h in Headers */,\n\t\t\t\tD5044A682603DC8500C81BC4 /* FESurfacePairConstraintNL.h in Headers */,\n\t\t\t\tD5B9E523213F67DE0008B38A /* FEBoundingBox.h in Headers */,\n\t\t\t\tD54E21F021517EEE008A9DD3 /* FENodalLoad.h in Headers */,\n\t\t\t\tD559C4CC22D9169E00CDC2BD /* FEParabolicMap.h in Headers */,\n\t\t\t\tD5B9E5F4213F67DE0008B38A /* FEPlotData.h in Headers */,\n\t\t\t\tD5B9E582213F67DE0008B38A /* targetver.h in Headers */,\n\t\t\t\tD54626F5260E6FDA007E2D8E /* FaceDataRecord.h in Headers */,\n\t\t\t\tD5B9E53C213F67DE0008B38A /* FEModelLoad.h in Headers */,\n\t\t\t\tD5B805B3223BE2DC00198805 /* FEMeshAdaptor.h in Headers */,\n\t\t\t\tD5B9E5A0213F67DE0008B38A /* FESurfaceConstraint.h in Headers */,\n\t\t\t\tD5B9E550213F67DE0008B38A /* FEElement.h in Headers */,\n\t\t\t\tD5B9E60E213F67DE0008B38A /* FEDomain2D.h in Headers */,\n\t\t\t\tD5B9E51B213F67DE0008B38A /* tens5d.hpp in Headers */,\n\t\t\t\tD5B9E54F213F67DE0008B38A /* FENewtonSolver.h in Headers */,\n\t\t\t\tD550834F24F086E300E919D8 /* EigenSolver.h in Headers */,\n\t\t\t\tD5B9E5DB213F67DE0008B38A /* tens6d.hpp in Headers */,\n\t\t\t\tD5B9E5B6213F67DE0008B38A /* FEAnalysis.h in Headers */,\n\t\t\t\tD5B9E5EF213F67DE0008B38A /* FETransform.h in Headers */,\n\t\t\t\tD5B9E5D9213F67DE0008B38A /* FENodeElemList.h in Headers */,\n\t\t\t\tD5B9E57B213F67DE0008B38A /* FEProperty.h in Headers */,\n\t\t\t\tD5B9E51E213F67DE0008B38A /* FEInitialCondition.h in Headers */,\n\t\t\t\tD5B9E52B213F67DE0008B38A /* FEDataMathGenerator.h in Headers */,\n\t\t\t\tD50D09F1261E2ED3005564C6 /* expint_Ei.h in Headers */,\n\t\t\t\tD54E21E721517EEE008A9DD3 /* FEFixedBC.h in Headers */,\n\t\t\t\tD5B9E541213F67DE0008B38A /* DataStore.h in Headers */,\n\t\t\t\tD56B209223AD5F94000AE9C2 /* FESolidElement.h in Headers */,\n\t\t\t\tD5B9E5BA213F67DE0008B38A /* JFNKStrategy.h in Headers */,\n\t\t\t\tD5B9E5D1213F67DE0008B38A /* FENodeNodeList.h in Headers */,\n\t\t\t\tD54E21F221517EEE008A9DD3 /* MMatrix.h in Headers */,\n\t\t\t\tD5F1947221908646000F738D /* fecore_enum.h in Headers */,\n\t\t\t\tD50D879426D930DC0034E121 /* FELogElementVolume.h in Headers */,\n\t\t\t\tD5B9E527213F67DE0008B38A /* FEElementTraits.h in Headers */,\n\t\t\t\tD5F1948221908646000F738D /* Preconditioner.h in Headers */,\n\t\t\t\tD5613D57217B604F007CAB89 /* FEPointFunction.h in Headers */,\n\t\t\t\tD5946E2224B524F90009C22F /* mathalg.h in Headers */,\n\t\t\t\tD52D840021CE89A200472620 /* FEScalarValuator.h in Headers */,\n\t\t\t\tD5709DAB22833034007CAB0A /* FEDofList.h in Headers */,\n\t\t\t\tD5B9E5E5213F67DE0008B38A /* tens3ds.hpp in Headers */,\n\t\t\t\tD5B9E556213F67DE0008B38A /* tens5d.h in Headers */,\n\t\t\t\tD5D56E872355239E0078BCC4 /* ClassDescriptor.h in Headers */,\n\t\t\t\tD5B9E517213F67DE0008B38A /* version.h in Headers */,\n\t\t\t\tD5B9E5C6213F67DE0008B38A /* FENNQuery.h in Headers */,\n\t\t\t\tD559C4C922D9169E00CDC2BD /* FEMat3dSphericalAngleMap.h in Headers */,\n\t\t\t\tD5B9E5C0213F67DE0008B38A /* FETimeInfo.h in Headers */,\n\t\t\t\tD5E85DA522021E8C00F5DF83 /* FEMeshTopo.h in Headers */,\n\t\t\t\tD5B9E605213F67DE0008B38A /* mat3d.h in Headers */,\n\t\t\t\tD54E21E521517EEE008A9DD3 /* MathObject.h in Headers */,\n\t\t\t\tD5B9E5EA213F67DE0008B38A /* FENodeReorder.h in Headers */,\n\t\t\t\tD5B9E518213F67DE0008B38A /* fecore_debug.h in Headers */,\n\t\t\t\tD5B9E59E213F67DE0008B38A /* tens3d.h in Headers */,\n\t\t\t\tD565CDE3215D290C00E08ED6 /* FEDataMap.h in Headers */,\n\t\t\t\tD5B9E5C9213F67DE0008B38A /* FEDataGenerator.h in Headers */,\n\t\t\t\tD5B9E559213F67DE0008B38A /* ParamString.h in Headers */,\n\t\t\t\tD5B9E5BE213F67DE0008B38A /* DataRecord.h in Headers */,\n\t\t\t\tD54E21B72149BB56008A9DD3 /* FENodeSet.h in Headers */,\n\t\t\t\tD5B9E522213F67DE0008B38A /* FEParamValidator.h in Headers */,\n\t\t\t\tD5B9E60F213F67DE0008B38A /* ElementDataRecord.h in Headers */,\n\t\t\t\tD5B9E54E213F67DE0008B38A /* FENewtonStrategy.h in Headers */,\n\t\t\t\tD5B9E5DF213F67DE0008B38A /* FEGlobalVector.h in Headers */,\n\t\t\t\tD5B9E610213F67DE0008B38A /* tens5ds.hpp in Headers */,\n\t\t\t\tD5B9E533213F67DE0008B38A /* FENLConstraint.h in Headers */,\n\t\t\t\tD5B9E542213F67DE0008B38A /* FEParam.h in Headers */,\n\t\t\t\tD5B9E5F1213F67DE0008B38A /* FENodeDataMap.h in Headers */,\n\t\t\t\tD5B9E57E213F67DE0008B38A /* LinearSolver.h in Headers */,\n\t\t\t\tD5613D65217B604F007CAB89 /* FELoadCurve.h in Headers */,\n\t\t\t\tD50D879526D930DC0034E121 /* FELogEnclosedVolume.h in Headers */,\n\t\t\t\tD5B9E5BF213F67DE0008B38A /* JFNKMatrix.h in Headers */,\n\t\t\t\tD5B9E50E213F67DE0008B38A /* tens4dms.hpp in Headers */,\n\t\t\t\tD5B9E5D2213F67DE0008B38A /* DOFS.h in Headers */,\n\t\t\t\tD5B9E611213F67DE0008B38A /* FEOctree.h in Headers */,\n\t\t\t\tD5B9E55A213F67DE0008B38A /* matrix.h in Headers */,\n\t\t\t\tD5B9E5E7213F67DE0008B38A /* vector.h in Headers */,\n\t\t\t\tD5B9E607213F67DE0008B38A /* FESurface.h in Headers */,\n\t\t\t\tD5B9E604213F67DE0008B38A /* FEDataExport.h in Headers */,\n\t\t\t\tD56B209623AD5F94000AE9C2 /* FESurfaceElement.h in Headers */,\n\t\t\t\tD54E21B52149BB56008A9DD3 /* FEElementSet.h in Headers */,\n\t\t\t\tD5B9E573213F67DE0008B38A /* fecore_api.h in Headers */,\n\t\t\t\tD5B9E530213F67DE0008B38A /* quatd.h in Headers */,\n\t\t\t\tD5B9E5C5213F67DE0008B38A /* FELineSearch.h in Headers */,\n\t\t\t\tD5B9E589213F67DE0008B38A /* FEModelComponent.h in Headers */,\n\t\t\t\tD5B9E52E213F67DE0008B38A /* FEFunction1D.h in Headers */,\n\t\t\t\tD5400D45249922C200570A47 /* FEMat3dsValuator.h in Headers */,\n\t\t\t\tD5B9E5B0213F67DE0008B38A /* FECallBack.h in Headers */,\n\t\t\t\tD5709DA822833034007CAB0A /* FENodeList.h in Headers */,\n\t\t\t\tD5B9E5DD213F67DE0008B38A /* mat6d.h in Headers */,\n\t\t\t\tD5B9E563213F67DE0008B38A /* FEModel.h in Headers */,\n\t\t\t\tD5F1947621908646000F738D /* SchurComplement.h in Headers */,\n\t\t\t\tD54E21FC21517EEE008A9DD3 /* MFunctions.h in Headers */,\n\t\t\t\tD5B9E560213F67DE0008B38A /* vec3d.h in Headers */,\n\t\t\t\tD56B208D23AD5F94000AE9C2 /* FESolidElementShape.h in Headers */,\n\t\t\t\tD5BE6F822423B4FB009AE76F /* FENodeSetConstraint.h in Headers */,\n\t\t\t\tD5B9E50F213F67DE0008B38A /* FEClosestPointProjection.h in Headers */,\n\t\t\t\tD56B209123AD5F94000AE9C2 /* FEShellElement.h in Headers */,\n\t\t\t\tD5E82FD72620A6FD00B23605 /* BSpline.h in Headers */,\n\t\t\t\tD5AB623F220289E200AAF141 /* FEEdgeList.h in Headers */,\n\t\t\t\tD5B9E5FF213F67DE0008B38A /* FECube.h in Headers */,\n\t\t\t\tD565CDDD215D290C00E08ED6 /* FEDomainList.h in Headers */,\n\t\t\t\tD5B9E5A1213F67DE0008B38A /* FEBox.h in Headers */,\n\t\t\t\tD5B9E567213F67DE0008B38A /* DumpFile.h in Headers */,\n\t\t\t\tD5B9E5A7213F67DE0008B38A /* tools.h in Headers */,\n\t\t\t\tD5B9E555213F67DE0008B38A /* FEEdgeLoad.h in Headers */,\n\t\t\t\tD5B9E51C213F67DE0008B38A /* tensor_base.h in Headers */,\n\t\t\t\tD5613D53217B604F007CAB89 /* FESurfaceToSurfaceMap.h in Headers */,\n\t\t\t\tD54E21AD2149BB56008A9DD3 /* FENode.h in Headers */,\n\t\t\t\tD54E21ED21517EEE008A9DD3 /* MObjBuilder.h in Headers */,\n\t\t\t\tD5B9E508213F67DE0008B38A /* FEGlobalData.h in Headers */,\n\t\t\t\tD5B9E562213F67DE0008B38A /* FECoreKernel.h in Headers */,\n\t\t\t\tD5B9E58A213F67DE0008B38A /* FEElementLibrary.h in Headers */,\n\t\t\t\tD5B9E609213F67DE0008B38A /* FEMesh.h in Headers */,\n\t\t\t\tD58FA88324A1631400FC768B /* FEConstValueVec3.h in Headers */,\n\t\t\t\tD5B9E58B213F67DE0008B38A /* FEException.h in Headers */,\n\t\t\t\tD51EBFA0217E3AED006F73C4 /* FEPIDController.h in Headers */,\n\t\t\t\tD5B9E544213F67DE0008B38A /* tens4d.h in Headers */,\n\t\t\t\tD5B9E501213F67DE0008B38A /* FEDiscreteMaterial.h in Headers */,\n\t\t\t\tD5B9E553213F67DE0008B38A /* FEDataArray.h in Headers */,\n\t\t\t\tD5B9E531213F67DE0008B38A /* Integrate.h in Headers */,\n\t\t\t\tD5B9E500213F67DE0008B38A /* MatrixOperator.h in Headers */,\n\t\t\t\tD5613D51217B604F007CAB89 /* FEMeshPartition.h in Headers */,\n\t\t\t\tD5B9E59A213F67DE0008B38A /* FEEdge.h in Headers */,\n\t\t\t\tD54E21B62149BB56008A9DD3 /* FEDiscreteSet.h in Headers */,\n\t\t\t\tD5B9E5B9213F67DE0008B38A /* sys.h in Headers */,\n\t\t\t\tD5B9E510213F67DE0008B38A /* FEMaterial.h in Headers */,\n\t\t\t\tD5B9E565213F67DE0008B38A /* tens3d.hpp in Headers */,\n\t\t\t\tD5B9E594213F67DE0008B38A /* FEMaterialPoint.h in Headers */,\n\t\t\t\tD5B9E505213F67DE0008B38A /* FEBroydenStrategy.h in Headers */,\n\t\t\t\tD54E21EE21517EEE008A9DD3 /* MObj2String.h in Headers */,\n\t\t\t\tD5B9E5FE213F67DE0008B38A /* DumpMemStream.h in Headers */,\n\t\t\t\tD5B9E507213F67DE0008B38A /* FECoreFactory.h in Headers */,\n\t\t\t\tD5B9E58C213F67DE0008B38A /* FELinearSolver.h in Headers */,\n\t\t\t\tD5B9E579213F67DE0008B38A /* Timer.h in Headers */,\n\t\t\t\tD5B9E548213F67DE0008B38A /* FETrussDomain.h in Headers */,\n\t\t\t\tD5B9E5BC213F67DE0008B38A /* tens6ds.hpp in Headers */,\n\t\t\t\tD54E21F121517EEE008A9DD3 /* FEPrescribedDOF.h in Headers */,\n\t\t\t\tD5B9E596213F67DE0008B38A /* log.h in Headers */,\n\t\t\t\tD5B9E598213F67DE0008B38A /* tens3drs.hpp in Headers */,\n\t\t\t\tD5006A7921AAF98100031CB6 /* FEElementShape.h in Headers */,\n\t\t\t\tD5B9E502213F67DE0008B38A /* SparseMatrix.h in Headers */,\n\t\t\t\tD5B9E608213F67DE0008B38A /* NLConstraintDataRecord.h in Headers */,\n\t\t\t\tD5B9E586213F67DE0008B38A /* FESolidDomain.h in Headers */,\n\t\t\t\tD5BC9F5526D07B29003BBF6E /* DomainDataRecord.h in Headers */,\n\t\t\t\tD5B9E5C2213F67DE0008B38A /* table.h in Headers */,\n\t\t\t\tD5B9E551213F67DE0008B38A /* vec2d.h in Headers */,\n\t\t\t\tD5B9E588213F67DE0008B38A /* FEBodyLoad.h in Headers */,\n\t\t\t\tD5613D61217B604F007CAB89 /* FESPRProjection.h in Headers */,\n\t\t\t\tD5B9E55F213F67DE0008B38A /* FEGlobalMatrix.h in Headers */,\n\t\t\t\tD56B208F23AD5F94000AE9C2 /* FESurfaceElementShape.h in Headers */,\n\t\t\t\tD5B9E581213F67DE0008B38A /* FESolver.h in Headers */,\n\t\t\t\tD5F1947E21908646000F738D /* CSRMatrix.h in Headers */,\n\t\t\t\tD54E21E121517EEE008A9DD3 /* MEvaluate.h in Headers */,\n\t\t\t\tD5B9E5C4213F67DE0008B38A /* mat2d.h in Headers */,\n\t\t\t\tD5E82FD326206FCF00B23605 /* gamma.h in Headers */,\n\t\t\t\tD565CDDC215D290C00E08ED6 /* FEDomainMap.h in Headers */,\n\t\t\t\tD5B9E56B213F67DE0008B38A /* FELinearConstraintManager.h in Headers */,\n\t\t\t\tD5B9E5C1213F67DE0008B38A /* FEDataStream.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5B9E3DC213F67CE0008B38A /* FECore */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5B9E3E8213F67CE0008B38A /* Build configuration list for PBXNativeTarget \"FECore\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5B9E3D9213F67CE0008B38A /* Sources */,\n\t\t\t\tD5B9E3DA213F67CE0008B38A /* Frameworks */,\n\t\t\t\tD5B9E3DB213F67CE0008B38A /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = FECore;\n\t\t\tproductName = FECore;\n\t\t\tproductReference = D5B9E3DD213F67CE0008B38A /* libFECore.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5B9E3D5213F67CE0008B38A /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5B9E3DC213F67CE0008B38A = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5B9E3D8213F67CE0008B38A /* Build configuration list for PBXProject \"FECore\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5B9E3D4213F67CE0008B38A;\n\t\t\tproductRefGroup = D5B9E3DE213F67CE0008B38A /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5B9E3DC213F67CE0008B38A /* FECore */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5B9E3D9213F67CE0008B38A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD54E21E621517EEE008A9DD3 /* FEFixedBC.cpp in Sources */,\n\t\t\t\tD5B9E5A4213F67DE0008B38A /* FEModelComponent.cpp in Sources */,\n\t\t\t\tD5B9E57F213F67DE0008B38A /* FESurface.cpp in Sources */,\n\t\t\t\tD559C4CB22D9169E00CDC2BD /* FEMat3dSphericalAngleMap.cpp in Sources */,\n\t\t\t\tD5E85DA722021E8C00F5DF83 /* FEMeshTopo.cpp in Sources */,\n\t\t\t\tD5B9E5AE213F67DE0008B38A /* DumpFile.cpp in Sources */,\n\t\t\t\tD5B9E5A5213F67DE0008B38A /* JFNKStrategy.cpp in Sources */,\n\t\t\t\tD5B9E584213F67DE0008B38A /* vector.cpp in Sources */,\n\t\t\t\tD5BC9F5626D07B29003BBF6E /* DomainDataRecord.cpp in Sources */,\n\t\t\t\tD5B9E601213F67DE0008B38A /* FECore.cpp in Sources */,\n\t\t\t\tD54E21B82149BB56008A9DD3 /* FENodeSet.cpp in Sources */,\n\t\t\t\tD51EBF9F217E3AED006F73C4 /* FEPIDController.cpp in Sources */,\n\t\t\t\tD54E21F521517EEE008A9DD3 /* MMatrix.cpp in Sources */,\n\t\t\t\tD5B9E520213F67DE0008B38A /* FEEdge.cpp in Sources */,\n\t\t\t\tD5B9E545213F67DE0008B38A /* FENormalProjection.cpp in Sources */,\n\t\t\t\tD54E21AC2149BB56008A9DD3 /* FEElementSet.cpp in Sources */,\n\t\t\t\tD52D83FE21CE89A200472620 /* FEVec3dValuator.cpp in Sources */,\n\t\t\t\tD5B9E5FC213F67DE0008B38A /* FEMaterial.cpp in Sources */,\n\t\t\t\tD5B9E5CE213F67DE0008B38A /* FETrussDomain.cpp in Sources */,\n\t\t\t\tD5B9E56D213F67DE0008B38A /* SparseMatrix.cpp in Sources */,\n\t\t\t\tD58FA88224A1631400FC768B /* FEConstValueVec3.cpp in Sources */,\n\t\t\t\tD54E21E821517EEE008A9DD3 /* MathObject.cpp in Sources */,\n\t\t\t\tD5B9E53D213F67DE0008B38A /* FEBroydenStrategy.cpp in Sources */,\n\t\t\t\tD52D840421CE89A200472620 /* FEMat3dValuator.cpp in Sources */,\n\t\t\t\tD5B9E561213F67DE0008B38A /* qsort.cpp in Sources */,\n\t\t\t\tD5B9E59F213F67DE0008B38A /* FECoreKernel.cpp in Sources */,\n\t\t\t\tD54E21B92149BB56008A9DD3 /* FEDiscreteSet.cpp in Sources */,\n\t\t\t\tD56B209023AD5F94000AE9C2 /* FESolidElementShape.cpp in Sources */,\n\t\t\t\tD54E21B22149BB56008A9DD3 /* FESurfacePair.cpp in Sources */,\n\t\t\t\tD5B9E569213F67DE0008B38A /* FECoreTask.cpp in Sources */,\n\t\t\t\tD5B9E5B3213F67DE0008B38A /* tools.cpp in Sources */,\n\t\t\t\tD5AB6240220289E200AAF141 /* FEEdgeList.cpp in Sources */,\n\t\t\t\tD5B9E5B1213F67DE0008B38A /* DumpStream.cpp in Sources */,\n\t\t\t\tD5B9E5F9213F67DE0008B38A /* FEBox.cpp in Sources */,\n\t\t\t\tD5B9E52F213F67DE0008B38A /* LinearSolver.cpp in Sources */,\n\t\t\t\tD5BC9F5826D07B29003BBF6E /* SurfaceDataRecord.cpp in Sources */,\n\t\t\t\tD5F1948121908646000F738D /* Preconditioner.cpp in Sources */,\n\t\t\t\tD5B9E5D3213F67DE0008B38A /* FECorePlot.cpp in Sources */,\n\t\t\t\tD54E21B12149BB56008A9DD3 /* FEFacetSet.cpp in Sources */,\n\t\t\t\tD5946E2124B524F90009C22F /* mathalg.cpp in Sources */,\n\t\t\t\tD5B9E5A2213F67DE0008B38A /* FEClosestPointProjection.cpp in Sources */,\n\t\t\t\tD5B9E509213F67DE0008B38A /* FECube.cpp in Sources */,\n\t\t\t\tD5B9E592213F67DE0008B38A /* FEDataExport.cpp in Sources */,\n\t\t\t\tD5B9E597213F67DE0008B38A /* FETimeInfo.cpp in Sources */,\n\t\t\t\tD5B9E51F213F67DE0008B38A /* Callback.cpp in Sources */,\n\t\t\t\tD5E82FD62620A6FD00B23605 /* BSpline.cpp in Sources */,\n\t\t\t\tD5B9E554213F67DE0008B38A /* FESolidDomain.cpp in Sources */,\n\t\t\t\tD5B9E5E2213F67DE0008B38A /* FEEdgeLoad.cpp in Sources */,\n\t\t\t\tD5B9E583213F67DE0008B38A /* FEShellDomain.cpp in Sources */,\n\t\t\t\tD5B9E599213F67DE0008B38A /* FESolver.cpp in Sources */,\n\t\t\t\tD5613D52217B604F007CAB89 /* FESPRProjection.cpp in Sources */,\n\t\t\t\tD54E21B42149BB56008A9DD3 /* FENode.cpp in Sources */,\n\t\t\t\tD5BE6F812423B4FB009AE76F /* FENodeSetConstraint.cpp in Sources */,\n\t\t\t\tD5B9E5B8213F67DE0008B38A /* FECoreFactory.cpp in Sources */,\n\t\t\t\tD5B9E54A213F67DE0008B38A /* FEException.cpp in Sources */,\n\t\t\t\tD5B9E5F5213F67DE0008B38A /* FEDataMathGenerator.cpp in Sources */,\n\t\t\t\tD5B9E524213F67DE0008B38A /* FEModelLoad.cpp in Sources */,\n\t\t\t\tD50D879726D930DC0034E121 /* FELogElementVolume.cpp in Sources */,\n\t\t\t\tD5B9E529213F67DE0008B38A /* FEPlotData.cpp in Sources */,\n\t\t\t\tD5B9E60A213F67DE0008B38A /* FETransform.cpp in Sources */,\n\t\t\t\tD54E21DE21517EEE008A9DD3 /* FEPrescribedBC.cpp in Sources */,\n\t\t\t\tD5B9E5A6213F67DE0008B38A /* FESurfaceConstraint.cpp in Sources */,\n\t\t\t\tD5613D62217B604F007CAB89 /* writeplot.cpp in Sources */,\n\t\t\t\tD5B9E5AA213F67DE0008B38A /* FELinearSystem.cpp in Sources */,\n\t\t\t\tD5B9E5BD213F67DE0008B38A /* FELineSearch.cpp in Sources */,\n\t\t\t\tD5B9E5F7213F67DE0008B38A /* mat3d.cpp in Sources */,\n\t\t\t\tD54E21FD21517EEE008A9DD3 /* MEvaluate.cpp in Sources */,\n\t\t\t\tD5B9E590213F67DE0008B38A /* FEDomain.cpp in Sources */,\n\t\t\t\tD5B9E5E9213F67DE0008B38A /* tens4d.cpp in Sources */,\n\t\t\t\tD5B9E5EE213F67DE0008B38A /* FEElementTraits.cpp in Sources */,\n\t\t\t\tD5B9E5A9213F67DE0008B38A /* FENewtonSolver.cpp in Sources */,\n\t\t\t\tD56B209523AD5F94000AE9C2 /* FESurfaceElement.cpp in Sources */,\n\t\t\t\tD5B9E539213F67DE0008B38A /* BFGSSolver.cpp in Sources */,\n\t\t\t\tD5613D5F217B604F007CAB89 /* FELoadCurve.cpp in Sources */,\n\t\t\t\tD54E21E421517EEE008A9DD3 /* MObjBuilder.cpp in Sources */,\n\t\t\t\tD54E21E021517EEE008A9DD3 /* MIntegral.cpp in Sources */,\n\t\t\t\tD54E21F921517EEE008A9DD3 /* FEPrescribedDOF.cpp in Sources */,\n\t\t\t\tD5B9E53A213F67DE0008B38A /* Integrate.cpp in Sources */,\n\t\t\t\tD5B9E5ED213F67DE0008B38A /* tens5d.cpp in Sources */,\n\t\t\t\tD5613D64217B604F007CAB89 /* FESurfaceToSurfaceMap.cpp in Sources */,\n\t\t\t\tD5B9E5F0213F67DE0008B38A /* FEInitialCondition.cpp in Sources */,\n\t\t\t\tD5B9E54B213F67DE0008B38A /* Timer.cpp in Sources */,\n\t\t\t\tD5B9E50D213F67DE0008B38A /* FEFunction1D.cpp in Sources */,\n\t\t\t\tD5B9E566213F67DE0008B38A /* FEProperty.cpp in Sources */,\n\t\t\t\tD5B9E5E0213F67DE0008B38A /* FELevelStructure.cpp in Sources */,\n\t\t\t\tD5F1947721908646000F738D /* CSRMatrix.cpp in Sources */,\n\t\t\t\tD5613D5C217B604F007CAB89 /* fecore_type.cpp in Sources */,\n\t\t\t\tD54E21EF21517EEE008A9DD3 /* FEModelParam.cpp in Sources */,\n\t\t\t\tD559C4CA22D9169E00CDC2BD /* FEParabolicMap.cpp in Sources */,\n\t\t\t\tD5AE8CC72592ECCB003AC08B /* FEMeshAdaptorCriterion.cpp in Sources */,\n\t\t\t\tD5709DA922833034007CAB0A /* FEDofList.cpp in Sources */,\n\t\t\t\tD50D879626D930DC0034E121 /* FELogEnclosedVolume.cpp in Sources */,\n\t\t\t\tD5B9E5C3213F67DE0008B38A /* FENLConstraint.cpp in Sources */,\n\t\t\t\tD54E21E321517EEE008A9DD3 /* MDerive.cpp in Sources */,\n\t\t\t\tD5E82FD226206FCF00B23605 /* gamma.cpp in Sources */,\n\t\t\t\tD54E21B02149BB56008A9DD3 /* FESegmentSet.cpp in Sources */,\n\t\t\t\tD565CDE2215D290C00E08ED6 /* FEItemList.cpp in Sources */,\n\t\t\t\tD5B9E55B213F67DE0008B38A /* DumpMemStream.cpp in Sources */,\n\t\t\t\tD5B9E5FB213F67DE0008B38A /* FEGlobalMatrix.cpp in Sources */,\n\t\t\t\tD5B9E5B5213F67DE0008B38A /* tens3d.cpp in Sources */,\n\t\t\t\tD5B9E54C213F67DE0008B38A /* FETimeStepController.cpp in Sources */,\n\t\t\t\tD54E21FB21517EEE008A9DD3 /* MSolve.cpp in Sources */,\n\t\t\t\tD5E85DA622021E8C00F5DF83 /* FEFaceList.cpp in Sources */,\n\t\t\t\tD5B9E532213F67DE0008B38A /* FEMaterialPoint.cpp in Sources */,\n\t\t\t\tD5613D5A217B604F007CAB89 /* FEMathController.cpp in Sources */,\n\t\t\t\tD5613D60217B604F007CAB89 /* FEPointFunction.cpp in Sources */,\n\t\t\t\tD5B9E5E1213F67DE0008B38A /* fecore_debug.cpp in Sources */,\n\t\t\t\tD5B9E503213F67DE0008B38A /* mortar.cpp in Sources */,\n\t\t\t\tD5B9E5D0213F67DE0008B38A /* FEGlobalVector.cpp in Sources */,\n\t\t\t\tD5B9E572213F67DE0008B38A /* ElementDataRecord.cpp in Sources */,\n\t\t\t\tD54E21FA21517EEE008A9DD3 /* MSimplify.cpp in Sources */,\n\t\t\t\tD52D840521CE89A200472620 /* FEDomainParameter.cpp in Sources */,\n\t\t\t\tD5F1947A21908646000F738D /* SchurComplement.cpp in Sources */,\n\t\t\t\tD5B9E525213F67DE0008B38A /* svd.cpp in Sources */,\n\t\t\t\tD5400D44249922C200570A47 /* FEMat3dsValuator.cpp in Sources */,\n\t\t\t\tD56B209423AD5F94000AE9C2 /* FEShellElement.cpp in Sources */,\n\t\t\t\tD5B9E570213F67DE0008B38A /* FESurfacePairConstraint.cpp in Sources */,\n\t\t\t\tD5B9E57D213F67DE0008B38A /* FELinearConstraint.cpp in Sources */,\n\t\t\t\tD5B9E558213F67DE0008B38A /* FENewtonStrategy.cpp in Sources */,\n\t\t\t\tD54626F4260E6FDA007E2D8E /* FaceDataRecord.cpp in Sources */,\n\t\t\t\tD5B9E549213F67DE0008B38A /* FEParameterList.cpp in Sources */,\n\t\t\t\tD5B9E504213F67DE0008B38A /* JFNKMatrix.cpp in Sources */,\n\t\t\t\tD5B9E543213F67DE0008B38A /* ParamString.cpp in Sources */,\n\t\t\t\tD5B9E5CF213F67DE0008B38A /* FESurfaceMap.cpp in Sources */,\n\t\t\t\tD5B9E5A3213F67DE0008B38A /* FENodeNodeList.cpp in Sources */,\n\t\t\t\tD54E21F821517EEE008A9DD3 /* MTypes.cpp in Sources */,\n\t\t\t\tD5B9E602213F67DE0008B38A /* FEDiscreteMaterial.cpp in Sources */,\n\t\t\t\tD5B9E600213F67DE0008B38A /* FEOctree.cpp in Sources */,\n\t\t\t\tD5B9E606213F67DE0008B38A /* Archive.cpp in Sources */,\n\t\t\t\tD5B9E57C213F67DE0008B38A /* FEDataArray.cpp in Sources */,\n\t\t\t\tD56B209323AD5F94000AE9C2 /* FESolidElement.cpp in Sources */,\n\t\t\t\tD5B9E580213F67DE0008B38A /* MatrixProfile.cpp in Sources */,\n\t\t\t\tD54E21DF21517EEE008A9DD3 /* FENodalLoad.cpp in Sources */,\n\t\t\t\tD5B9E5AC213F67DE0008B38A /* FECoreBase.cpp in Sources */,\n\t\t\t\tD5F1947F21908646000F738D /* CompactMatrix.cpp in Sources */,\n\t\t\t\tD5B9E59C213F67DE0008B38A /* FESurfaceLoad.cpp in Sources */,\n\t\t\t\tD565CDE0215D290C00E08ED6 /* FEDomainList.cpp in Sources */,\n\t\t\t\tD5FAD69C26FF93420043FC69 /* FELogDomainVolume.cpp in Sources */,\n\t\t\t\tD565CDE1215D290C00E08ED6 /* FEDomainMap.cpp in Sources */,\n\t\t\t\tD5613D56217B604F007CAB89 /* FELoadController.cpp in Sources */,\n\t\t\t\tD5B9E56C213F67DE0008B38A /* FEModel.cpp in Sources */,\n\t\t\t\tD5B9E52A213F67DE0008B38A /* FEDataGenerator.cpp in Sources */,\n\t\t\t\tD5B9E5AF213F67DE0008B38A /* quatd.cpp in Sources */,\n\t\t\t\tD5B9E5BB213F67DE0008B38A /* FEDomain2D.cpp in Sources */,\n\t\t\t\tD5B9E578213F67DE0008B38A /* FEElement.cpp in Sources */,\n\t\t\t\tD54E21F421517EEE008A9DD3 /* MItem.cpp in Sources */,\n\t\t\t\tD5B9E5C8213F67DE0008B38A /* FELinearConstraintManager.cpp in Sources */,\n\t\t\t\tD5B9E50C213F67DE0008B38A /* tens6d.cpp in Sources */,\n\t\t\t\tD5B9E564213F67DE0008B38A /* FEElemElemList.cpp in Sources */,\n\t\t\t\tD5A37D2B2286167300867D77 /* FEOctreeSearch.cpp in Sources */,\n\t\t\t\tD56B208E23AD5F94000AE9C2 /* FESurfaceElementShape.cpp in Sources */,\n\t\t\t\tD5613D50217B604F007CAB89 /* FEMeshPartition.cpp in Sources */,\n\t\t\t\tD5B9E58E213F67DE0008B38A /* FEElementLibrary.cpp in Sources */,\n\t\t\t\tD5B9E546213F67DE0008B38A /* FEParam.cpp in Sources */,\n\t\t\t\tD5B805B4223BE2DC00198805 /* FEMeshAdaptor.cpp in Sources */,\n\t\t\t\tD5B9E60D213F67DE0008B38A /* colsol.cpp in Sources */,\n\t\t\t\tD5006A7821AAF98100031CB6 /* FEElementShape.cpp in Sources */,\n\t\t\t\tD5B9E574213F67DE0008B38A /* FEDiscreteDomain.cpp in Sources */,\n\t\t\t\tD5B9E587213F67DE0008B38A /* NLConstraintDataRecord.cpp in Sources */,\n\t\t\t\tD54E21DC21517EEE008A9DD3 /* MFunctions.cpp in Sources */,\n\t\t\t\tD5B9E568213F67DE0008B38A /* FENNQuery.cpp in Sources */,\n\t\t\t\tD5B9E53E213F67DE0008B38A /* eig3.cpp in Sources */,\n\t\t\t\tD5B9E5AD213F67DE0008B38A /* FEElementList.cpp in Sources */,\n\t\t\t\tD5B9E536213F67DE0008B38A /* NodeDataRecord.cpp in Sources */,\n\t\t\t\tD5709DA722833034007CAB0A /* FENodeList.cpp in Sources */,\n\t\t\t\tD5B9E577213F67DE0008B38A /* FENodeElemList.cpp in Sources */,\n\t\t\t\tD5B9E51D213F67DE0008B38A /* Image.cpp in Sources */,\n\t\t\t\tD5B9E5EB213F67DE0008B38A /* FEBoundaryCondition.cpp in Sources */,\n\t\t\t\tD5B9E55C213F67DE0008B38A /* FEParamValidator.cpp in Sources */,\n\t\t\t\tD5044A672603DC8500C81BC4 /* FESurfacePairConstraintNL.cpp in Sources */,\n\t\t\t\tD550834E24F086E300E919D8 /* EigenSolver.cpp in Sources */,\n\t\t\t\tD5B9E534213F67DE0008B38A /* FEBodyLoad.cpp in Sources */,\n\t\t\t\tD54E21EA21517EEE008A9DD3 /* MObj2String.cpp in Sources */,\n\t\t\t\tD5B9E513213F67DE0008B38A /* FECallback.cpp in Sources */,\n\t\t\t\tD5B9E5DC213F67DE0008B38A /* stdafx.cpp in Sources */,\n\t\t\t\tD565CDDF215D290C00E08ED6 /* FEDataMap.cpp in Sources */,\n\t\t\t\tD5B9E528213F67DE0008B38A /* FEAnalysis.cpp in Sources */,\n\t\t\t\tD5B9E50B213F67DE0008B38A /* FENodeDataMap.cpp in Sources */,\n\t\t\t\tD5B9E585213F67DE0008B38A /* DataStore.cpp in Sources */,\n\t\t\t\tD5B9E5B2213F67DE0008B38A /* matrix.cpp in Sources */,\n\t\t\t\tD5F1947521908646000F738D /* DenseMatrix.cpp in Sources */,\n\t\t\t\tD54E21F321517EEE008A9DD3 /* MExpand.cpp in Sources */,\n\t\t\t\tD54E21F721517EEE008A9DD3 /* MReplace.cpp in Sources */,\n\t\t\t\tD5B9E547213F67DE0008B38A /* FENodeReorder.cpp in Sources */,\n\t\t\t\tD5B9E5F6213F67DE0008B38A /* FELinearSolver.cpp in Sources */,\n\t\t\t\tD5B9E5CD213F67DE0008B38A /* DOFS.cpp in Sources */,\n\t\t\t\tD5B9E5E8213F67DE0008B38A /* DataRecord.cpp in Sources */,\n\t\t\t\tD54E21F621517EEE008A9DD3 /* MCollect.cpp in Sources */,\n\t\t\t\tD52D840221CE89A200472620 /* FEScalarValuator.cpp in Sources */,\n\t\t\t\tD5B9E56F213F67DE0008B38A /* FEGlobalData.cpp in Sources */,\n\t\t\t\tD50D09F0261E2ED3005564C6 /* expint_Ei.cpp in Sources */,\n\t\t\t\tD54A66AF257BDCB90023C8D1 /* FSPath.cpp in Sources */,\n\t\t\t\tD5B9E593213F67DE0008B38A /* FEModelData.cpp in Sources */,\n\t\t\t\tD5B9E5E3213F67DE0008B38A /* FEMesh.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5B9E3E6213F67CE0008B38A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5B9E3E7213F67CE0008B38A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tLIBRARY_SEARCH_PATHS = /usr/local/lib;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5B9E3E9213F67CE0008B38A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5B9E3EA213F67CE0008B38A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5B9E3D8213F67CE0008B38A /* Build configuration list for PBXProject \"FECore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5B9E3E6213F67CE0008B38A /* Debug */,\n\t\t\t\tD5B9E3E7213F67CE0008B38A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5B9E3E8213F67CE0008B38A /* Build configuration list for PBXNativeTarget \"FECore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5B9E3E9213F67CE0008B38A /* Debug */,\n\t\t\t\tD5B9E3EA213F67CE0008B38A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5B9E3D5213F67CE0008B38A /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/FECore/FECore.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:FECore.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Xcode/FECore/FECore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Xcode/NumCore/NumCore.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tD50D45D0247C6B1C0085C759 /* StrategySolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50D45CE247C6B1C0085C759 /* StrategySolver.cpp */; };\n\t\tD50D45D1247C6B1C0085C759 /* StrategySolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D50D45CF247C6B1C0085C759 /* StrategySolver.h */; };\n\t\tD50EC3862217AD75006F6A57 /* CompactUnSymmMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50EC3802217AD74006F6A57 /* CompactUnSymmMatrix.cpp */; };\n\t\tD50EC3872217AD75006F6A57 /* BlockMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50EC3812217AD74006F6A57 /* BlockMatrix.cpp */; };\n\t\tD50EC3882217AD75006F6A57 /* CompactSymmMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D50EC3822217AD75006F6A57 /* CompactSymmMatrix.h */; };\n\t\tD50EC3892217AD75006F6A57 /* BlockMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D50EC3832217AD75006F6A57 /* BlockMatrix.h */; };\n\t\tD50EC38A2217AD75006F6A57 /* CompactUnSymmMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D50EC3842217AD75006F6A57 /* CompactUnSymmMatrix.h */; };\n\t\tD50EC38B2217AD75006F6A57 /* CompactSymmMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D50EC3852217AD75006F6A57 /* CompactSymmMatrix.cpp */; };\n\t\tD52D840B21CE89BC00472620 /* MatrixTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D52D840621CE89BC00472620 /* MatrixTools.cpp */; };\n\t\tD52D840E21CE89BC00472620 /* MatrixTools.h in Headers */ = {isa = PBXBuildFile; fileRef = D52D840921CE89BC00472620 /* MatrixTools.h */; };\n\t\tD550834A24F086CE00E919D8 /* FEASTEigenSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D550834824F086CE00E919D8 /* FEASTEigenSolver.cpp */; };\n\t\tD550834B24F086CE00E919D8 /* FEASTEigenSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D550834924F086CE00E919D8 /* FEASTEigenSolver.h */; };\n\t\tD5C5B798239F0B8900D29EE0 /* Hypre_PCG_AMG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5C5B796239F0B8900D29EE0 /* Hypre_PCG_AMG.cpp */; };\n\t\tD5C5B799239F0B8900D29EE0 /* Hypre_PCG_AMG.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C5B797239F0B8900D29EE0 /* Hypre_PCG_AMG.h */; };\n\t\tD5F1945A21908513000F738D /* ILUT_Preconditioner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1945221908513000F738D /* ILUT_Preconditioner.cpp */; };\n\t\tD5F1945B21908513000F738D /* ILUT_Preconditioner.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1945321908513000F738D /* ILUT_Preconditioner.h */; };\n\t\tD5F1945C21908513000F738D /* ILU0_Preconditioner.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1945421908513000F738D /* ILU0_Preconditioner.h */; };\n\t\tD5F1945D21908513000F738D /* IncompleteCholesky.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F1945521908513000F738D /* IncompleteCholesky.h */; };\n\t\tD5F1945E21908513000F738D /* ILU0_Preconditioner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1945621908513000F738D /* ILU0_Preconditioner.cpp */; };\n\t\tD5F1945F21908513000F738D /* IncompleteCholesky.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F1945721908513000F738D /* IncompleteCholesky.cpp */; };\n\t\tD5F6DCA0213F63B7001E96CB /* SkylineMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC62213F63B7001E96CB /* SkylineMatrix.h */; };\n\t\tD5F6DCA3213F63B7001E96CB /* HypreGMRESsolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC65213F63B7001E96CB /* HypreGMRESsolver.cpp */; };\n\t\tD5F6DCA4213F63B7001E96CB /* SchurSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC66213F63B7001E96CB /* SchurSolver.h */; };\n\t\tD5F6DCA7213F63B7001E96CB /* stdafx.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC69213F63B7001E96CB /* stdafx.h */; };\n\t\tD5F6DCA9213F63B7001E96CB /* LUSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC6B213F63B7001E96CB /* LUSolver.cpp */; };\n\t\tD5F6DCAA213F63B7001E96CB /* SkylineSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC6C213F63B7001E96CB /* SkylineSolver.h */; };\n\t\tD5F6DCAD213F63B7001E96CB /* SchurSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC6F213F63B7001E96CB /* SchurSolver.cpp */; };\n\t\tD5F6DCAF213F63B7001E96CB /* BlockSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC71213F63B7001E96CB /* BlockSolver.cpp */; };\n\t\tD5F6DCB2213F63B7001E96CB /* RCICGSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC74213F63B7001E96CB /* RCICGSolver.cpp */; };\n\t\tD5F6DCB3213F63B7001E96CB /* PardisoSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC75213F63B7001E96CB /* PardisoSolver.h */; };\n\t\tD5F6DCB5213F63B7001E96CB /* LUSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC77213F63B7001E96CB /* LUSolver.h */; };\n\t\tD5F6DCB8213F63B7001E96CB /* BIPNSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC7A213F63B7001E96CB /* BIPNSolver.cpp */; };\n\t\tD5F6DCBB213F63B7001E96CB /* RCICGSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC7D213F63B7001E96CB /* RCICGSolver.h */; };\n\t\tD5F6DCBD213F63B7001E96CB /* FGMRESSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC7F213F63B7001E96CB /* FGMRESSolver.cpp */; };\n\t\tD5F6DCBF213F63B7001E96CB /* targetver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC81213F63B7001E96CB /* targetver.h */; };\n\t\tD5F6DCC3213F63B7001E96CB /* FGMRESSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC85213F63B7001E96CB /* FGMRESSolver.h */; };\n\t\tD5F6DCC7213F63B7001E96CB /* BlockSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC89213F63B7001E96CB /* BlockSolver.h */; };\n\t\tD5F6DCC8213F63B7001E96CB /* PardisoSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC8A213F63B7001E96CB /* PardisoSolver.cpp */; };\n\t\tD5F6DCCA213F63B7001E96CB /* SkylineMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC8C213F63B7001E96CB /* SkylineMatrix.cpp */; };\n\t\tD5F6DCCC213F63B7001E96CB /* NumCore.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC8E213F63B7001E96CB /* NumCore.h */; };\n\t\tD5F6DCD0213F63B7001E96CB /* stdafx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC92213F63B7001E96CB /* stdafx.cpp */; };\n\t\tD5F6DCD6213F63B7001E96CB /* BIPNSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC98213F63B7001E96CB /* BIPNSolver.h */; };\n\t\tD5F6DCD9213F63B7001E96CB /* NumCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC9B213F63B7001E96CB /* NumCore.cpp */; };\n\t\tD5F6DCDA213F63B7001E96CB /* SkylineSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5F6DC9C213F63B7001E96CB /* SkylineSolver.cpp */; };\n\t\tD5F6DCDC213F63B7001E96CB /* HypreGMRESsolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5F6DC9E213F63B7001E96CB /* HypreGMRESsolver.h */; };\n\t\tD5FA08982238205C0074FD50 /* BoomerAMGSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5FA08962238205B0074FD50 /* BoomerAMGSolver.cpp */; };\n\t\tD5FA08992238205C0074FD50 /* BoomerAMGSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5FA08972238205C0074FD50 /* BoomerAMGSolver.h */; };\n\t\tD5FF266C233A652300C621EB /* BiCGStabSolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D5FF266A233A652200C621EB /* BiCGStabSolver.h */; };\n\t\tD5FF266D233A652300C621EB /* BiCGStabSolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D5FF266B233A652300C621EB /* BiCGStabSolver.cpp */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\tD50D45CE247C6B1C0085C759 /* StrategySolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StrategySolver.cpp; sourceTree = \"<group>\"; };\n\t\tD50D45CF247C6B1C0085C759 /* StrategySolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StrategySolver.h; sourceTree = \"<group>\"; };\n\t\tD50EC3802217AD74006F6A57 /* CompactUnSymmMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompactUnSymmMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD50EC3812217AD74006F6A57 /* BlockMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BlockMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD50EC3822217AD75006F6A57 /* CompactSymmMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompactSymmMatrix.h; sourceTree = \"<group>\"; };\n\t\tD50EC3832217AD75006F6A57 /* BlockMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockMatrix.h; sourceTree = \"<group>\"; };\n\t\tD50EC3842217AD75006F6A57 /* CompactUnSymmMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompactUnSymmMatrix.h; sourceTree = \"<group>\"; };\n\t\tD50EC3852217AD75006F6A57 /* CompactSymmMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompactSymmMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD52D840621CE89BC00472620 /* MatrixTools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MatrixTools.cpp; sourceTree = \"<group>\"; };\n\t\tD52D840921CE89BC00472620 /* MatrixTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatrixTools.h; sourceTree = \"<group>\"; };\n\t\tD550834824F086CE00E919D8 /* FEASTEigenSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FEASTEigenSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD550834924F086CE00E919D8 /* FEASTEigenSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FEASTEigenSolver.h; sourceTree = \"<group>\"; };\n\t\tD5C5B796239F0B8900D29EE0 /* Hypre_PCG_AMG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Hypre_PCG_AMG.cpp; sourceTree = \"<group>\"; };\n\t\tD5C5B797239F0B8900D29EE0 /* Hypre_PCG_AMG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hypre_PCG_AMG.h; sourceTree = \"<group>\"; };\n\t\tD5F1945221908513000F738D /* ILUT_Preconditioner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ILUT_Preconditioner.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1945321908513000F738D /* ILUT_Preconditioner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ILUT_Preconditioner.h; sourceTree = \"<group>\"; };\n\t\tD5F1945421908513000F738D /* ILU0_Preconditioner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ILU0_Preconditioner.h; sourceTree = \"<group>\"; };\n\t\tD5F1945521908513000F738D /* IncompleteCholesky.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IncompleteCholesky.h; sourceTree = \"<group>\"; };\n\t\tD5F1945621908513000F738D /* ILU0_Preconditioner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ILU0_Preconditioner.cpp; sourceTree = \"<group>\"; };\n\t\tD5F1945721908513000F738D /* IncompleteCholesky.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IncompleteCholesky.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC52213F639F001E96CB /* libNumCore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libNumCore.a; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tD5F6DC62213F63B7001E96CB /* SkylineMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkylineMatrix.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC65213F63B7001E96CB /* HypreGMRESsolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HypreGMRESsolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC66213F63B7001E96CB /* SchurSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SchurSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC69213F63B7001E96CB /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC6B213F63B7001E96CB /* LUSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LUSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC6C213F63B7001E96CB /* SkylineSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkylineSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC6F213F63B7001E96CB /* SchurSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SchurSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC71213F63B7001E96CB /* BlockSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BlockSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC74213F63B7001E96CB /* RCICGSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RCICGSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC75213F63B7001E96CB /* PardisoSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PardisoSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC77213F63B7001E96CB /* LUSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LUSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC7A213F63B7001E96CB /* BIPNSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BIPNSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC7D213F63B7001E96CB /* RCICGSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCICGSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC7F213F63B7001E96CB /* FGMRESSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FGMRESSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC81213F63B7001E96CB /* targetver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = targetver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC85213F63B7001E96CB /* FGMRESSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FGMRESSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC89213F63B7001E96CB /* BlockSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC8A213F63B7001E96CB /* PardisoSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PardisoSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC8C213F63B7001E96CB /* SkylineMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkylineMatrix.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC8E213F63B7001E96CB /* NumCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NumCore.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC92213F63B7001E96CB /* stdafx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stdafx.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC98213F63B7001E96CB /* BIPNSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BIPNSolver.h; sourceTree = \"<group>\"; };\n\t\tD5F6DC9B213F63B7001E96CB /* NumCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NumCore.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC9C213F63B7001E96CB /* SkylineSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkylineSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5F6DC9E213F63B7001E96CB /* HypreGMRESsolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HypreGMRESsolver.h; sourceTree = \"<group>\"; };\n\t\tD5FA08962238205B0074FD50 /* BoomerAMGSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BoomerAMGSolver.cpp; sourceTree = \"<group>\"; };\n\t\tD5FA08972238205C0074FD50 /* BoomerAMGSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BoomerAMGSolver.h; sourceTree = \"<group>\"; };\n\t\tD5FF266A233A652200C621EB /* BiCGStabSolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BiCGStabSolver.h; sourceTree = \"<group>\"; };\n\t\tD5FF266B233A652300C621EB /* BiCGStabSolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BiCGStabSolver.cpp; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tD5F6DC4F213F639F001E96CB /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tD5F6DC49213F639F001E96CB = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5F6DC60213F63B7001E96CB /* NumCore */,\n\t\t\t\tD5F6DC53213F639F001E96CB /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5F6DC53213F639F001E96CB /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5F6DC52213F639F001E96CB /* libNumCore.a */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD5F6DC60213F63B7001E96CB /* NumCore */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD5FF266B233A652300C621EB /* BiCGStabSolver.cpp */,\n\t\t\t\tD5FF266A233A652200C621EB /* BiCGStabSolver.h */,\n\t\t\t\tD5F6DC7A213F63B7001E96CB /* BIPNSolver.cpp */,\n\t\t\t\tD5F6DC98213F63B7001E96CB /* BIPNSolver.h */,\n\t\t\t\tD50EC3812217AD74006F6A57 /* BlockMatrix.cpp */,\n\t\t\t\tD50EC3832217AD75006F6A57 /* BlockMatrix.h */,\n\t\t\t\tD5F6DC71213F63B7001E96CB /* BlockSolver.cpp */,\n\t\t\t\tD5F6DC89213F63B7001E96CB /* BlockSolver.h */,\n\t\t\t\tD5FA08962238205B0074FD50 /* BoomerAMGSolver.cpp */,\n\t\t\t\tD5FA08972238205C0074FD50 /* BoomerAMGSolver.h */,\n\t\t\t\tD50EC3852217AD75006F6A57 /* CompactSymmMatrix.cpp */,\n\t\t\t\tD50EC3822217AD75006F6A57 /* CompactSymmMatrix.h */,\n\t\t\t\tD50EC3802217AD74006F6A57 /* CompactUnSymmMatrix.cpp */,\n\t\t\t\tD50EC3842217AD75006F6A57 /* CompactUnSymmMatrix.h */,\n\t\t\t\tD550834824F086CE00E919D8 /* FEASTEigenSolver.cpp */,\n\t\t\t\tD550834924F086CE00E919D8 /* FEASTEigenSolver.h */,\n\t\t\t\tD5F6DC7F213F63B7001E96CB /* FGMRESSolver.cpp */,\n\t\t\t\tD5F6DC85213F63B7001E96CB /* FGMRESSolver.h */,\n\t\t\t\tD5C5B796239F0B8900D29EE0 /* Hypre_PCG_AMG.cpp */,\n\t\t\t\tD5C5B797239F0B8900D29EE0 /* Hypre_PCG_AMG.h */,\n\t\t\t\tD5F6DC65213F63B7001E96CB /* HypreGMRESsolver.cpp */,\n\t\t\t\tD5F6DC9E213F63B7001E96CB /* HypreGMRESsolver.h */,\n\t\t\t\tD5F1945621908513000F738D /* ILU0_Preconditioner.cpp */,\n\t\t\t\tD5F1945421908513000F738D /* ILU0_Preconditioner.h */,\n\t\t\t\tD5F1945221908513000F738D /* ILUT_Preconditioner.cpp */,\n\t\t\t\tD5F1945321908513000F738D /* ILUT_Preconditioner.h */,\n\t\t\t\tD5F1945721908513000F738D /* IncompleteCholesky.cpp */,\n\t\t\t\tD5F1945521908513000F738D /* IncompleteCholesky.h */,\n\t\t\t\tD5F6DC6B213F63B7001E96CB /* LUSolver.cpp */,\n\t\t\t\tD5F6DC77213F63B7001E96CB /* LUSolver.h */,\n\t\t\t\tD52D840621CE89BC00472620 /* MatrixTools.cpp */,\n\t\t\t\tD52D840921CE89BC00472620 /* MatrixTools.h */,\n\t\t\t\tD5F6DC9B213F63B7001E96CB /* NumCore.cpp */,\n\t\t\t\tD5F6DC8E213F63B7001E96CB /* NumCore.h */,\n\t\t\t\tD5F6DC8A213F63B7001E96CB /* PardisoSolver.cpp */,\n\t\t\t\tD5F6DC75213F63B7001E96CB /* PardisoSolver.h */,\n\t\t\t\tD5F6DC74213F63B7001E96CB /* RCICGSolver.cpp */,\n\t\t\t\tD5F6DC7D213F63B7001E96CB /* RCICGSolver.h */,\n\t\t\t\tD5F6DC6F213F63B7001E96CB /* SchurSolver.cpp */,\n\t\t\t\tD5F6DC66213F63B7001E96CB /* SchurSolver.h */,\n\t\t\t\tD5F6DC8C213F63B7001E96CB /* SkylineMatrix.cpp */,\n\t\t\t\tD5F6DC62213F63B7001E96CB /* SkylineMatrix.h */,\n\t\t\t\tD5F6DC9C213F63B7001E96CB /* SkylineSolver.cpp */,\n\t\t\t\tD5F6DC6C213F63B7001E96CB /* SkylineSolver.h */,\n\t\t\t\tD5F6DC92213F63B7001E96CB /* stdafx.cpp */,\n\t\t\t\tD5F6DC69213F63B7001E96CB /* stdafx.h */,\n\t\t\t\tD50D45CE247C6B1C0085C759 /* StrategySolver.cpp */,\n\t\t\t\tD50D45CF247C6B1C0085C759 /* StrategySolver.h */,\n\t\t\t\tD5F6DC81213F63B7001E96CB /* targetver.h */,\n\t\t\t);\n\t\t\tname = NumCore;\n\t\t\tpath = ../../NumCore;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tD5F6DC50213F639F001E96CB /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5FF266C233A652300C621EB /* BiCGStabSolver.h in Headers */,\n\t\t\t\tD5F6DCA7213F63B7001E96CB /* stdafx.h in Headers */,\n\t\t\t\tD5F6DCBF213F63B7001E96CB /* targetver.h in Headers */,\n\t\t\t\tD50EC3892217AD75006F6A57 /* BlockMatrix.h in Headers */,\n\t\t\t\tD5F6DCC7213F63B7001E96CB /* BlockSolver.h in Headers */,\n\t\t\t\tD5F6DCB3213F63B7001E96CB /* PardisoSolver.h in Headers */,\n\t\t\t\tD550834B24F086CE00E919D8 /* FEASTEigenSolver.h in Headers */,\n\t\t\t\tD5F6DCA4213F63B7001E96CB /* SchurSolver.h in Headers */,\n\t\t\t\tD5F6DCD6213F63B7001E96CB /* BIPNSolver.h in Headers */,\n\t\t\t\tD5F6DCB5213F63B7001E96CB /* LUSolver.h in Headers */,\n\t\t\t\tD5F1945D21908513000F738D /* IncompleteCholesky.h in Headers */,\n\t\t\t\tD50EC3882217AD75006F6A57 /* CompactSymmMatrix.h in Headers */,\n\t\t\t\tD5C5B799239F0B8900D29EE0 /* Hypre_PCG_AMG.h in Headers */,\n\t\t\t\tD5F6DCC3213F63B7001E96CB /* FGMRESSolver.h in Headers */,\n\t\t\t\tD5F1945B21908513000F738D /* ILUT_Preconditioner.h in Headers */,\n\t\t\t\tD5F6DCAA213F63B7001E96CB /* SkylineSolver.h in Headers */,\n\t\t\t\tD5F6DCCC213F63B7001E96CB /* NumCore.h in Headers */,\n\t\t\t\tD52D840E21CE89BC00472620 /* MatrixTools.h in Headers */,\n\t\t\t\tD5F6DCA0213F63B7001E96CB /* SkylineMatrix.h in Headers */,\n\t\t\t\tD5F6DCBB213F63B7001E96CB /* RCICGSolver.h in Headers */,\n\t\t\t\tD50EC38A2217AD75006F6A57 /* CompactUnSymmMatrix.h in Headers */,\n\t\t\t\tD50D45D1247C6B1C0085C759 /* StrategySolver.h in Headers */,\n\t\t\t\tD5F1945C21908513000F738D /* ILU0_Preconditioner.h in Headers */,\n\t\t\t\tD5FA08992238205C0074FD50 /* BoomerAMGSolver.h in Headers */,\n\t\t\t\tD5F6DCDC213F63B7001E96CB /* HypreGMRESsolver.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tD5F6DC51213F639F001E96CB /* NumCore */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = D5F6DC5D213F63A0001E96CB /* Build configuration list for PBXNativeTarget \"NumCore\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tD5F6DC4E213F639F001E96CB /* Sources */,\n\t\t\t\tD5F6DC4F213F639F001E96CB /* Frameworks */,\n\t\t\t\tD5F6DC50213F639F001E96CB /* Headers */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = NumCore;\n\t\t\tproductName = NumCore;\n\t\t\tproductReference = D5F6DC52213F639F001E96CB /* libNumCore.a */;\n\t\t\tproductType = \"com.apple.product-type.library.static\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tD5F6DC4A213F639F001E96CB /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0940;\n\t\t\t\tORGANIZATIONNAME = febio.org;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tD5F6DC51213F639F001E96CB = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = D5F6DC4D213F639F001E96CB /* Build configuration list for PBXProject \"NumCore\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = D5F6DC49213F639F001E96CB;\n\t\t\tproductRefGroup = D5F6DC53213F639F001E96CB /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tD5F6DC51213F639F001E96CB /* NumCore */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tD5F6DC4E213F639F001E96CB /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tD5FF266D233A652300C621EB /* BiCGStabSolver.cpp in Sources */,\n\t\t\t\tD5F6DCA3213F63B7001E96CB /* HypreGMRESsolver.cpp in Sources */,\n\t\t\t\tD52D840B21CE89BC00472620 /* MatrixTools.cpp in Sources */,\n\t\t\t\tD5C5B798239F0B8900D29EE0 /* Hypre_PCG_AMG.cpp in Sources */,\n\t\t\t\tD50EC38B2217AD75006F6A57 /* CompactSymmMatrix.cpp in Sources */,\n\t\t\t\tD5F6DCAD213F63B7001E96CB /* SchurSolver.cpp in Sources */,\n\t\t\t\tD5F6DCC8213F63B7001E96CB /* PardisoSolver.cpp in Sources */,\n\t\t\t\tD5F6DCB2213F63B7001E96CB /* RCICGSolver.cpp in Sources */,\n\t\t\t\tD5F1945F21908513000F738D /* IncompleteCholesky.cpp in Sources */,\n\t\t\t\tD5F6DCA9213F63B7001E96CB /* LUSolver.cpp in Sources */,\n\t\t\t\tD5F1945E21908513000F738D /* ILU0_Preconditioner.cpp in Sources */,\n\t\t\t\tD550834A24F086CE00E919D8 /* FEASTEigenSolver.cpp in Sources */,\n\t\t\t\tD50EC3872217AD75006F6A57 /* BlockMatrix.cpp in Sources */,\n\t\t\t\tD5F6DCD0213F63B7001E96CB /* stdafx.cpp in Sources */,\n\t\t\t\tD5F6DCB8213F63B7001E96CB /* BIPNSolver.cpp in Sources */,\n\t\t\t\tD5F6DCDA213F63B7001E96CB /* SkylineSolver.cpp in Sources */,\n\t\t\t\tD50EC3862217AD75006F6A57 /* CompactUnSymmMatrix.cpp in Sources */,\n\t\t\t\tD5FA08982238205C0074FD50 /* BoomerAMGSolver.cpp in Sources */,\n\t\t\t\tD5F6DCAF213F63B7001E96CB /* BlockSolver.cpp in Sources */,\n\t\t\t\tD50D45D0247C6B1C0085C759 /* StrategySolver.cpp in Sources */,\n\t\t\t\tD5F1945A21908513000F738D /* ILUT_Preconditioner.cpp in Sources */,\n\t\t\t\tD5F6DCBD213F63B7001E96CB /* FGMRESSolver.cpp in Sources */,\n\t\t\t\tD5F6DCCA213F63B7001E96CB /* SkylineMatrix.cpp in Sources */,\n\t\t\t\tD5F6DCD9213F63B7001E96CB /* NumCore.cpp in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\tD5F6DC5B213F63A0001E96CB /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t/opt/intel/mkl/include,\n\t\t\t\t\t\"/usr/local/hypre-master/src/hypre/include\",\n\t\t\t\t\t/usr/local/include,\n\t\t\t\t\t\"/usr/local/opt/open-mpi/include\",\n\t\t\t\t\t/usr/local/opt/libomp/include,\n\t\t\t\t);\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_CXX_LIBRARY = \"compiler-default\";\n\t\t\t\tICC_C_LANG_DIALECT = \"compiler-default\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-DPARDISO\",\n\t\t\t\t\t\"-DMKL_ISS\",\n\t\t\t\t\t\"-DHYPRE\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5F6DC5C213F63A0001E96CB /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = fast;\n\t\t\t\tGCC_VERSION = \"\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t/opt/intel/mkl/include,\n\t\t\t\t\t\"/usr/local/hypre-master/src/hypre/include\",\n\t\t\t\t\t/usr/local/include,\n\t\t\t\t\t\"/usr/local/opt/open-mpi/include\",\n\t\t\t\t\t/usr/local/opt/libomp/include,\n\t\t\t\t);\n\t\t\t\tICC_CXX_LANG_DIALECT = \"c++11\";\n\t\t\t\tICC_CXX_LIBRARY = \"compiler-default\";\n\t\t\t\tICC_C_LANG_DIALECT = \"compiler-default\";\n\t\t\t\tICC_LANG_OPENMP = parallel;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_CPLUSPLUSFLAGS = (\n\t\t\t\t\t\"$(OTHER_CFLAGS)\",\n\t\t\t\t\t\"-DPARDISO\",\n\t\t\t\t\t\"-DMKL_ISS\",\n\t\t\t\t\t\"-DHYPRE\",\n\t\t\t\t\t\"-Xpreprocessor\",\n\t\t\t\t\t\"-fopenmp\",\n\t\t\t\t);\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tUSER_HEADER_SEARCH_PATHS = \"$(SRCROOT)/../..\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tD5F6DC5E213F63A0001E96CB /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tD5F6DC5F213F63A0001E96CB /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tEXECUTABLE_PREFIX = lib;\n\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS = YES;\n\t\t\t\tGCC_ENABLE_CPP_RTTI = YES;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tD5F6DC4D213F639F001E96CB /* Build configuration list for PBXProject \"NumCore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5F6DC5B213F63A0001E96CB /* Debug */,\n\t\t\t\tD5F6DC5C213F63A0001E96CB /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tD5F6DC5D213F63A0001E96CB /* Build configuration list for PBXNativeTarget \"NumCore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tD5F6DC5E213F63A0001E96CB /* Debug */,\n\t\t\t\tD5F6DC5F213F63A0001E96CB /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = D5F6DC4A213F639F001E96CB /* Project object */;\n}\n"
  },
  {
    "path": "Xcode/NumCore/NumCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:NumCore.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "build/.gitignore",
    "content": "# Ignore everything in this directory\n*\n# Except this file\n!.gitignore\n"
  },
  {
    "path": "ci/Linux/build.sh",
    "content": "#! /bin/bash\n# Uncomment next line if not global on target machine\nset -e\n\nsource \"/opt/intel/oneapi/setvars.sh\" --force\ncmake . -B cmbuild -LA \\\n\t-DCMAKE_BUILD_TYPE=Release \\\n\t-DSET_DEVCOMMIT=ON \\\n\t-DUSE_FFTW=ON \\\n\t-DUSE_HYPRE=ON \\\n\t-DUSE_LEVMAR=ON \\\n\t-DUSE_MKL=ON \\\n\t-DUSE_MMG=ON \\\n\t-DUSE_STATIC_STDLIBS=ON \\\n\t-DUSE_ZLIB=ON \\\n\t-DUSE_NLOPT=ON \npushd cmbuild\nmake -j $(nproc)\npopd\n"
  },
  {
    "path": "ci/Linux/create-sdk.sh",
    "content": "#! /bin/bash\nset -e\nTARGET_DIR=\"${TARGET_DIR:-febio4-sdk}\"\nFEBIO_REPO=\"${FEBIO_REPO:-.}\"\nmkdir -p ${TARGET_DIR}/{include,lib}\n\n# Copy in FEBioConfig.cmake\nmkdir -p ${TARGET_DIR}/lib/cmake/FEBio\ncp $FEBIO_REPO/FEBioConfig.cmake ${TARGET_DIR}/lib/cmake/FEBio\n\nsdkDirs=(\n    FECore\n    FEBioMech\n    FEBioMix\n    FEBioFluid\n    FEBioRVE\n    FEBioPlot\n    FEBioXML\n    FEBioLib\n    FEAMR\n    FEBioOpt\n    FEImgLib\n)\n\nsdkLibs=(\n    libfecore.so\n    libfebiomech.so\n    libfebiomix.so\n    libfebiofluid.so\n    libfebiorve.so\n    libfebioplot.so\n    libfebioxml.so\n    libfebiolib.so\n    libfeamr.so\n    libfebioopt.so\n    libfeimglib.so\n)\n\nfor item in ${sdkDirs[@]}; do\n    mkdir ${TARGET_DIR}/include/$item\n    cp $FEBIO_REPO/$item/*.h ${TARGET_DIR}/include/$item\n    \n    # don't exit if there aren't .hpp files\n    if ls \"$FEBIO_REPO/$item\"/*.hpp >/dev/null 2>&1; then\n        cp \"$FEBIO_REPO/$item\"/*.hpp \"${TARGET_DIR}/include/$item\"\n    fi \ndone\n\nfor item in ${sdkLibs[@]}; do\n    cp $FEBIO_REPO/cmbuild/lib/$item ${TARGET_DIR}/lib\ndone\n\n"
  },
  {
    "path": "ci/Linux/scp-to-repo.sh",
    "content": "#! /bin/bash\n\nREMOTE_PATH=\"/serverRoot/update2/FEBioStudio2Dev/Linux/stage\"\nif [ $# == 1 ] && [ \"$1\" != \"develop\" ]; then\n    REMOTE_PATH=\"/serverRoot/update2/FEBioStudio2Dev/branches/$1/Linux/stage\"\nfi\n\n\nset -e\nscp cmbuild/bin/* febio-web:$REMOTE_PATH/bin\nscp cmbuild/lib/* febio-web:$REMOTE_PATH/lib\nssh febio-web \"chmod +x $REMOTE_PATH/bin/febio4\"\n\n# package and upload sdk\npushd sdk\nzip -r sdk.zip include\nzip -r sdk.zip lib\nscp sdk.zip febio-web:$REMOTE_PATH/\npopd"
  },
  {
    "path": "ci/Linux/test.sh",
    "content": "#! /bin/bash\n# Uncomment next line if not global on target machine\nset -e\n\nFEBIO_XML=$(realpath ./ci/febio.xml)\nFEBIO_DIR=$(realpath ./cmbuild/bin)\nFEBIO_BIN=\"${FEBIO_DIR}/febio4\"\nchmod +x $FEBIO_BIN\n\n# If PLUGIN_DIRS is not empty, the plugins were built and we need to copy them \n# and set the plugin folder in the febio xml. \nif [[ -n \"$PLUGIN_DIRS\" ]]; then\n    PLUGIN_DIR=$(realpath ./plugins)\n\n    # Copy the plugins from their subdirectories directly to\n    # the root of the plugin dir\n    cp $PLUGIN_DIR/*/*.so $PLUGIN_DIR \n\n    # Set the plugin dir in the FEBio XML\n    sed -i 's@PLUGINS_FOLDER@'$PLUGIN_DIR'@' $FEBIO_XML\n\n# Otherwise, remove the import_folder tag from the febio xml or FEBio will fail to start\nelse\n    sed -i 's@<import_folder>PLUGINS_FOLDER</import_folder>@@' $FEBIO_XML\nfi\n\n# Copy febio xml into febio dir\ncp $FEBIO_XML $FEBIO_DIR\n\n./TestSuite/code/tools.py -r $FEBIO_BIN -n -p $PLUGIN_DIRS\n"
  },
  {
    "path": "ci/Windows/build.bat",
    "content": "call \"%ONEAPI_ROOT%\\setvars.bat\"\n\nset CONFIG_TYPE=\"Release\"\n\nIF \"%~1\"==\"-d\" set CONFIG_TYPE=\"Debug\"\n\ncmake . -LA -B cmbuild ^\n  -DSET_DEVCOMMIT=ON ^\n  -DUSE_FFTW=ON ^\n  -DUSE_HYPRE=ON ^\n  -DUSE_LEVMAR=ON ^\n  -DUSE_MKL=ON ^\n  -DUSE_MMG=ON ^\n  -DUSE_ZLIB=ON ^\n  -DUSE_NLOPT=ON ^\n  -DFFTW_LIB=\"C:\\usr\\local\\febio\\vcpkg_installed\\x64-windows\\lib\\fftw3.lib\" ^\n  -DHYPRE_LIB=\"C:\\usr\\local\\lib\\HYPRE.lib\" ^\n  -DMMG_LIB=\"C:\\usr\\local\\lib\\mmg3d.lib\" ^\n  -DMMGS_LIB=\"C:\\usr\\local\\lib\\mmgs.lib\" ^\n  -DLEVMAR_INC=\"C:\\usr\\local\\include\\levmar\" ^\n  -DLEVMAR_LIB=\"C:\\usr\\local\\lib\\levmar.lib\" ^\n  -DNLOPT_INC=\"C:\\usr\\local\\include\" ^\n  -DNLOPT_LIB=\"C:\\usr\\local\\lib\\nlopt.lib\"\n  \n\ncd \"cmbuild\"\nmsbuild /p:configuration=Release /maxCpuCount:%NUMBER_OF_PROCESSORS% ALL_BUILD.vcxproj\n@REM msbuild /p:configuration=Debug /maxCpuCount:%NUMBER_OF_PROCESSORS% ALL_BUILD.vcxproj\ncd ..\n\nexit /b %errorlevel%\n"
  },
  {
    "path": "ci/Windows/create-sdk.sh",
    "content": "#! /bin/bash\nset -e\nTARGET_DIR=\"${TARGET_DIR:-febio4-sdk}\"\nFEBIO_REPO=\"${FEBIO_REPO:-.}\"\nmkdir -p ${TARGET_DIR}/{include,lib,bin}\nmkdir ${TARGET_DIR}/lib/{Release,Debug}\nmkdir ${TARGET_DIR}/bin/{Release,Debug}\n\n# Copy in FEBioConfig.cmake\nmkdir -p ${TARGET_DIR}/lib/cmake/FEBio\ncp $FEBIO_REPO/FEBioConfig.cmake ${TARGET_DIR}/lib/cmake/FEBio\n\nsdkDirs=(\n    FECore\n    FEBioMech\n    FEBioMix\n    FEBioFluid\n    FEBioRVE\n    FEBioPlot\n    FEBioXML\n    FEBioLib\n    FEAMR\n    FEBioOpt\n    FEImgLib\n)\n\nfor item in ${sdkDirs[@]}; do\n    mkdir ${TARGET_DIR}/include/$item\n    cp $FEBIO_REPO/$item/*.h ${TARGET_DIR}/include/$item\n    \n    # don't exit if there aren't .hpp files\n    if ls \"$FEBIO_REPO/$item\"/*.hpp >/dev/null 2>&1; then\n        cp \"$FEBIO_REPO/$item\"/*.hpp \"${TARGET_DIR}/include/$item\"\n    fi \n\n    cp $FEBIO_REPO/cmbuild/lib/Release/$item.lib ${TARGET_DIR}/lib/Release\n    cp $FEBIO_REPO/cmbuild/bin/Release/$item.dll ${TARGET_DIR}/bin/Release\n    # cp $FEBIO_REPO/cmbuild/lib/Debug/$item.lib ${TARGET_DIR}/lib/Debug\ndone\n\n# cp $FEBIO_REPO/cmbuild/bin/Debug/febio4.exe ${TARGET_DIR}/bin/Debug\n# cp $FEBIO_REPO/cmbuild/bin/Debug/*.dll ${TARGET_DIR}/bin/Debug\n\n"
  },
  {
    "path": "ci/Windows/scp-to-repo.sh",
    "content": "#! /bin/bash\n\nREMOTE_PATH=\"/serverRoot/update2/FEBioStudio2Dev/Windows/stage\"\nif [ $# == 1 ] && [ \"$1\" != \"develop\" ]; then\n    REMOTE_PATH=\"/serverRoot/update2/FEBioStudio2Dev/branches/$1/Windows/stage\"\nfi\n\nscp cmbuild/bin/Release/* repo:$REMOTE_PATH/bin\n\n# package and upload sdk\npushd sdk\n/c/WINDOWS/system32/tar -acf sdk.zip include lib bin\nscp sdk.zip repo:$REMOTE_PATH/\npopd"
  },
  {
    "path": "ci/Windows/test.sh",
    "content": "#! /usr/bin/bash\nset -e\n\nFEBIO_XML=$(realpath ./ci/febio.xml)\nFEBIO_DIR=$(realpath ./cmbuild/bin/Release)\nFEBIO_LIB=$(realpath ./cmbuild/bin/Release)\nFEBIO_BIN=\"${FEBIO_DIR}/febio4.exe\"\n\n# If PLUGIN_DIRS is not empty, the plugins were built and we need to copy them \n# and set the plugin folder in the febio xml. \nif [[ -n \"$PLUGIN_DIRS\" ]]; then\n    PLUGIN_DIR=$(realpath ./plugins)\n\n    # Convert plugin dir to Windows style\n    PLUGIN_DIR_WIN=$(cygpath -w \"$PLUGIN_DIR\" | sed 's|\\\\|/|g')\n\n    # Copy the plugins from their subdirectories directly to\n    # the root of the plugin dir\n    cp $PLUGIN_DIR/*/*.dll $PLUGIN_DIR\n\n    # Set the plugin dir in the FEBio XML\n    sed -i \"s@PLUGINS_FOLDER@${PLUGIN_DIR_WIN}@g\" \"$FEBIO_XML\"\n\n# Otherwise, remove the import_folder tag from the febio xml or FEBio will fail to start\nelse\n    sed -i 's@<import_folder>PLUGINS_FOLDER</import_folder>@@' $FEBIO_XML\nfi\n\n# Copy febio xml into febio dir\ncp $FEBIO_XML $FEBIO_DIR\n\n# Copy iomp lib into febio dir\nONEAPI=$(cygpath -u \"$ONEAPI_ROOT\") #Convert $ONEAPI_ROOT to posix path\nIOMP_LIB=\"${ONEAPI}compiler/latest/windows/redist/intel64_win/compiler/libiomp5md.dll\"\ncp -a \"$IOMP_LIB\" \"$FEBIO_LIB\"\n\n# Copy fftw lib into febio dir\nFFTW_LIB=\"/c/usr/local/febio/vcpkg_installed/x64-windows/bin/fftw3.dll\"\ncp -a \"$FFTW_LIB\" \"$FEBIO_LIB\"\n\nZLIB=\"/c/usr/local/febio/vcpkg_installed/x64-windows/bin/zlib1.dll\"\ncp -a \"$ZLIB\" \"$FEBIO_LIB\"\n\n# Run the test suite\npython ./TestSuite/code/tools.py -r $FEBIO_BIN -n -p $PLUGIN_DIRS\n"
  },
  {
    "path": "ci/febio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<febio_config version=\"3.0\">\n    <default_linear_solver type=\"pardiso\"></default_linear_solver>\n    <import_folder>PLUGINS_FOLDER</import_folder>\n</febio_config>\n"
  },
  {
    "path": "ci/gather-plugins.py",
    "content": "import os, shutil, json\n\nos.mkdir(\"plugins\")\n\nfor dirName in os.listdir(\"pluginRepos\"):\n    dirPath = os.path.join(\"pluginRepos\", dirName)\n    \n    if os.path.isdir(dirPath):\n        newDir = os.path.join(\"plugins\", dirName)\n        os.mkdir(newDir)\n\n        for root, dirs, files in os.walk(dirPath, followlinks=True):\n            for name in files:\n                if name.endswith(\".dll\") or name.endswith(\".dylib\") or name.endswith(\".so\"):\n                    filename = name.split(\"/\")[-1].split(\"\\\\\")[-1]\n\n                    shutil.copy2(os.path.join(root,name), os.path.join(newDir, filename))\n\ndef getVersion(path):\n    if not path:\n        return None\n\n    major = minor = patch = None\n    with open(path, \"r\") as f:\n        for line in f:\n            if \"#define VERSION\" in line:\n                major = line.split()[-1].strip()\n            elif \"#define SUBVERSION\" in line:\n                minor = line.split()[-1].strip()\n            elif \"#define SUBSUBVERSION\" in line:\n                patch = line.split()[-1].strip()\n\n    if major and minor and patch:\n        return f\"{major}.{minor}.{patch}\"\n    \n    return None\n\nversionInfo = {}\n\n# FEBio version\nversionInfo[\"febio\"] = getVersion(\"febio4-sdk/include/FEBioLib/version.h\")\n\n# Find version for each plugin\nfor dirName in os.listdir(\"pluginRepos\"):\n    dirPath = os.path.join(\"pluginRepos\", dirName)\n    \n    if os.path.isdir(dirPath):\n        versionHeader = None\n        for root, dirs, files in os.walk(dirPath):\n            for name in files:\n                if name.endswith(\"version.h\"):\n                    versionHeader = os.path.join(root, name)\n                    break\n                \n            if versionHeader:\n                break\n        \n        if versionHeader:\n            versionInfo[dirName] = getVersion(versionHeader)\n        else:\n            print(f\"Unable to find version header for {dirPath}\")\n\nwith open(\"plugins/versions.json\", \"w\") as f:\n    json.dump(versionInfo, f)"
  },
  {
    "path": "ci/macOS/build.sh",
    "content": "#! /bin/bash\nset -e\n\nsource \"/opt/intel/oneapi/setvars.sh\" --force\n. $(dirname $0)/cmake.sh\npushd cmbuild\nmake -j $(sysctl -n hw.ncpu)\npopd\n"
  },
  {
    "path": "ci/macOS/cmake.sh",
    "content": "set -e\n\ncmake . -B cmbuild -L \\\n\t-DCMAKE_BUILD_TYPE=Release \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \\\n\t-DSET_DEVCOMMIT=ON \\\n\t-DUSE_FFTW=ON \\\n\t-DUSE_HYPRE=ON \\\n\t-DUSE_LEVMAR=ON \\\n\t-DUSE_MKL=ON \\\n\t-DUSE_MMG=ON \\\n\t-DUSE_ZLIB=ON \\\n\t-DUSE_NLOPT=ON \\\n    -DOMP_INC=/Users/gitRunner/local/x86_64/homebrew/opt/libomp/include\n"
  },
  {
    "path": "ci/macOS/create-sdk.sh",
    "content": "#! /bin/bash\nset -e\nTARGET_DIR=\"${TARGET_DIR:-febio4-sdk}\"\nFEBIO_REPO=\"${FEBIO_REPO:-.}\"\nmkdir -p ${TARGET_DIR}/{include,lib}\n\n# Copy in FEBioConfig.cmake\nmkdir -p ${TARGET_DIR}/lib/cmake/FEBio\ncp $FEBIO_REPO/FEBioConfig.cmake ${TARGET_DIR}/lib/cmake/FEBio\n\nsdkDirs=(\n    FECore\n    FEBioMech\n    FEBioMix\n    FEBioFluid\n    FEBioRVE\n    FEBioPlot\n    FEBioXML\n    FEBioLib\n    FEAMR\n    FEBioOpt\n    FEImgLib\n)\n\nsdkLibs=(\n    libfecore.dylib\n    libfebiomech.dylib\n    libfebiomix.dylib\n    libfebiofluid.dylib\n    libfebiorve.dylib\n    libfebioplot.dylib\n    libfebioxml.dylib\n    libfebiolib.dylib\n    libfeamr.dylib\n    libfebioopt.dylib\n    libfeimglib.dylib\n)\n\nfor item in ${sdkDirs[@]}; do\n    mkdir ${TARGET_DIR}/include/$item\n    cp $FEBIO_REPO/$item/*.h ${TARGET_DIR}/include/$item\n    \n    # don't exit if there aren't .hpp files\n    if ls \"$FEBIO_REPO/$item\"/*.hpp >/dev/null 2>&1; then\n        cp \"$FEBIO_REPO/$item\"/*.hpp \"${TARGET_DIR}/include/$item\"\n    fi \ndone\n\nfor item in ${sdkLibs[@]}; do\n    cp $FEBIO_REPO/cmbuild/lib/$item ${TARGET_DIR}/lib\ndone\n"
  },
  {
    "path": "ci/macOS/scp-to-repo.sh",
    "content": "#! /bin/bash\n\nREMOTE_PATH=\"/serverRoot/update2/FEBioStudio2Dev/macOS/stage\"\nif [ $# == 1 ] && [ \"$1\" != \"develop\" ]; then\n    REMOTE_PATH=\"/serverRoot/update2/FEBioStudio2Dev/branches/$1/macOS/stage\"\nfi\n\nscp cmbuild/bin/* repo:$REMOTE_PATH/FEBioStudio.app/Contents/MacOS\nscp cmbuild/lib/* repo:$REMOTE_PATH/FEBioStudio.app/Contents/Frameworks\nssh repo \"chmod +x $REMOTE_PATH/FEBioStudio.app/Contents/MacOS/febio4\"\n\n# package and upload sdk\npushd sdk\nzip -r sdk.zip include\nzip -r sdk.zip lib\nscp sdk.zip repo:$REMOTE_PATH/\npopd"
  },
  {
    "path": "ci/macOS/test.sh",
    "content": "#! /bin/bash\n# Uncomment next line if not global on target machine\nset -e\n\nFEBIO_XML=$(realpath ./ci/febio.xml)\nFEBIO_DIR=$(realpath ./cmbuild/bin)\nFEBIO_BIN=\"${FEBIO_DIR}/febio4\"\nchmod +x $FEBIO_BIN\n\n# If PLUGIN_DIRS is not empty, the plugins were built and we need to copy them \n# and set the plugin folder in the febio xml. \nif [[ -n \"$PLUGIN_DIRS\" ]]; then\n    PLUGIN_DIR=$(realpath ./plugins)\n\n    # Copy the plugins from their subdirectories directly to\n    # the root of the plugin dir\n    cp $PLUGIN_DIR/*/*.dylib $PLUGIN_DIR \n\n    # Set the plugin dir in the FEBio XML\n    sed -i '' 's@PLUGINS_FOLDER@'$PLUGIN_DIR'@' $FEBIO_XML\n\n# Otherwise, remove the import_folder tag from the febio xml or FEBio will fail to start\nelse\n    sed -i '' 's@<import_folder>PLUGINS_FOLDER</import_folder>@@' $FEBIO_XML\nfi\n\n# Copy febio xml into febio dir\ncp $FEBIO_XML $FEBIO_DIR\n\n# Run the test suite\n./TestSuite/code/tools.py -r $FEBIO_BIN -n -p $PLUGIN_DIRS\n\n"
  },
  {
    "path": "ci/repo-plugins.py",
    "content": "import os, json\n\nHOMEPATH = \"/root\"\n\ntry:\n    with open(\"plugins/versions.json\", \"r\") as file:\n        versionInfo = json.load(file)\n\n    febioVersion = versionInfo[\"febio\"]\n    del versionInfo[\"febio\"]\n\n    platform = os.environ[\"OS\"]\n    if platform == \"Windows\":\n        osFlag = \"-w\"\n    elif platform == \"macOS\":\n        osFlag = \"-m\"\n    else:\n        osFlag = \"-l\"\n\n    for name in versionInfo:\n        os.system(f\"scp plugins/{name}/* repo:{HOMEPATH}/pluginRepo/files/{name}/develop/stage/\")\n        os.system(f'ssh repo \"python3 {HOMEPATH}/modelServer/plugins.py -d {name} {versionInfo[name]} {febioVersion} {osFlag}\"')\n\nexcept FileNotFoundError:\n    print(\"Error: 'plugins/versions.json not found. \")\nexcept json.JSONDecodeError:\n    print(\"Error: Invalid JSON format in 'plugins/versions.json'.\")"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3\"\nservices:\n  febiobase:\n    platform: \"linux/amd64\"\n    build:\n      context: ./infrastructure\n      dockerfile: DockerfileBase\n    image: febiosoftware/febiobase:ubuntu-22.04\n    working_dir: /FEBio\n    volumes:\n      - ./:/FEBio\n    command: bash\n\n  febio: &default\n    platform: \"linux/amd64\"\n    build:\n      context: ./infrastructure\n      dockerfile: Dockerfile\n    image: febiosoftware/febio:ubuntu-22.04\n    working_dir: /FEBio\n    volumes:\n      - ./:/FEBio\n    command: bash\n\n  febio-dev:\n    <<: *default\n    container_name: febio-dev\n    command: bash\n\n  febio-build:\n    <<: *default\n    container_name: febio-build\n    command: ci/linux/build.sh\n\n  febio-tests:\n    <<: *default\n    container_name: febio-tests\n    command: ci/linux/test.sh\n\n  febio-runtime:\n    build:\n      context: .\n      dockerfile: infrastructure/DockerfileRuntime\n    image: ${DOCKER_REPO}febio-runtime:ubuntu-22.04\n    working_dir: /FEBio\n    volumes:\n      - ./TestSuite/:/FEBio/TestSuite\n"
  },
  {
    "path": "febcode/ast.cpp",
    "content": "#include \"ast.h\"\n#include <iostream>\n#include <iomanip>\n#include <sstream>\n\nusing namespace febcode;\n\nExprPtr febcode::clone(const Expression* expr)\n{\n\tExprPtr cpy;\n\n\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\tcpy = std::make_unique<LiteralExpr>(literal->value);\n\t}\n\telse if (auto variable = dynamic_cast<const VariableExpr*>(expr)) {\n\t\tcpy = std::make_unique<VariableExpr>(variable->name);\n\t}\n\telse if (auto unary = dynamic_cast<const UnaryExpr*>(expr)) {\n\t\tcpy = std::make_unique<UnaryExpr>(unary->op, clone(unary->right.get()));\n\t}\n\telse if (auto binary = dynamic_cast<const BinaryExpr*>(expr)) {\n\t\tcpy = std::make_unique<BinaryExpr>(clone(binary->left.get()), binary->op, clone(binary->right.get()));\n\t}\n\telse if (auto call = dynamic_cast<const CallExpr*>(expr))\n\t{\n\t\tstd::vector<ExprPtr> copyArgs;\n\t\tfor (auto& arg : call->arguments)\n\t\t{\n\t\t\tcopyArgs.emplace_back(clone(arg.get()));\n\t\t}\n\t\tcpy = std::make_unique<CallExpr>(call->name, std::move(copyArgs));\n\t}\n\telse if (auto call = dynamic_cast<const InitExpr*>(expr))\n\t{\n\t\tstd::vector<ExprPtr> copyArgs;\n\t\tfor (auto& arg : call->elements)\n\t\t{\n\t\t\tcopyArgs.emplace_back(clone(arg.get()));\n\t\t}\n\t\tcpy = std::make_unique<InitExpr>(std::move(copyArgs));\n\t}\n\telse if (auto constructor = dynamic_cast<const ConstructorExpr*>(expr))\n\t{\n\t\tstd::vector<ExprPtr> copyArgs;\n\t\tfor (auto& arg : constructor->args)\n\t\t{\n\t\t\tcopyArgs.emplace_back(clone(arg.get()));\n\t\t}\n\t\tcpy = std::make_unique<ConstructorExpr>(constructor->valType, std::move(copyArgs));\n\t}\n\telse if (auto assign = dynamic_cast<const AssignExpr*>(expr))\n\t{\n\t\tcpy = std::make_unique<AssignExpr>(clone(assign->target.get()), clone(assign->value.get()));\n\t}\n\telse if (auto member = dynamic_cast<const MemberExpr*>(expr))\n\t{\n\t\tcpy = std::make_unique<MemberExpr>(clone(member->object.get()), member->property);\n\t}\n\telse if (auto index = dynamic_cast<const IndexExpr*>(expr))\n\t{\n\t\tcpy = std::make_unique<IndexExpr>(clone(index->object.get()), clone(index->index.get()));\n\t}\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Unsupported expression type for copying\");\n\t}\n\n\tif (cpy)\n\t\tcpy->valType = expr->valType; // Copy the value type as well\n\n\treturn cpy;\n}\n\nbool febcode::isEqual(const ExprPtr& l, const ExprPtr& r)\n{\n\treturn isEqual(l.get(), r.get());\n}\n\nbool febcode::isEqual(const Expression* l, const Expression* r)\n{\n\tif (!l && !r) return true;\n\tif (!l || !r) return false;\n\n\tif (auto litL = dynamic_cast<const LiteralExpr*>(l))\n\t{\n\t\tif (auto litR = dynamic_cast<const LiteralExpr*>(r))\n\t\t{\n\t\t\treturn litL->value == litR->value;\n\t\t}\n\t}\n\telse if (auto varL = dynamic_cast<const VariableExpr*>(l))\n\t{\n\t\tif (auto varR = dynamic_cast<const VariableExpr*>(r))\n\t\t{\n\t\t\treturn varL->name == varR->name;\n\t\t}\n\t}\n\telse if (auto binL = dynamic_cast<const BinaryExpr*>(l))\n\t{\n\t\tif (auto binR = dynamic_cast<const BinaryExpr*>(r))\n\t\t{\n\t\t\treturn binL->op == binR->op &&\n\t\t\t\tisEqual(binL->left, binR->left) &&\n\t\t\t\tisEqual(binL->right, binR->right);\n\t\t}\n\t}\n\telse if (auto unL = dynamic_cast<const UnaryExpr*>(l))\n\t{\n\t\tif (auto unR = dynamic_cast<const UnaryExpr*>(r))\n\t\t{\n\t\t\treturn unL->op == unR->op &&\n\t\t\t\tisEqual(unL->right, unR->right);\n\t\t}\n\t}\n\telse if (auto memberL = dynamic_cast<const MemberExpr*>(l))\n\t{\n\t\tif (auto memberR = dynamic_cast<const MemberExpr*>(r))\n\t\t{\n\t\t\treturn isEqual(memberL->object, memberR->object) &&\n\t\t\t\tmemberL->property == memberR->property;\n\t\t}\n\t}\n\telse if (auto indexL = dynamic_cast<const IndexExpr*>(l))\n\t{\n\t\tif (auto indexR = dynamic_cast<const IndexExpr*>(r))\n\t\t{\n\t\t\treturn isEqual(indexL->object, indexR->object) &&\n\t\t\t\tisEqual(indexL->index, indexR->index);\n\t\t}\n\t}\n\telse if (auto callL = dynamic_cast<const CallExpr*>(l))\n\t{\n\t\tif (auto callR = dynamic_cast<const CallExpr*>(r))\n\t\t{\n\t\t\treturn (callL->name == callR->name) && isEqual(callL->arguments, callR->arguments);\n\t\t}\n\t}\n\telse if (auto ctorL = dynamic_cast<const ConstructorExpr*>(l))\n\t{\n\t\tif (auto ctorR = dynamic_cast<const ConstructorExpr*>(r))\n\t\t{\n\t\t\tif (ctorL->valType != ctorR->valType) return false;\n\t\t\tassert(ctorL->args.size() == ctorR->args.size());\n\t\t\tif (ctorL->args.size() != ctorR->args.size()) return false;\n\t\t\tfor (int i = 0; i < ctorL->args.size(); ++i)\n\t\t\t{\n\t\t\t\tif (!isEqual(ctorL->args[i], ctorR->args[i]))\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nbool febcode::isEqual(const std::vector<ExprPtr>& l, const std::vector<ExprPtr>& r)\n{\n\tif (l.size() != r.size()) return false;\n\tfor (int i = 0; i < l.size(); ++i)\n\t{\n\t\tif (!isEqual(l[i], r[i])) return false;\n\t}\n\treturn true;\n}\n\nstd::ostream& operator << (std::ostream& o, const febcode::Value& v)\n{\n\tif      (isVoid  (v)) return o << \"null\";\n\telse if (isInt   (v)) return o << getInt(v);\n\telse if (isDouble(v)) return o << getDouble(v);\n\telse if (isBool  (v)) return o << (getBool(v) ? \"true\" : \"false\");\n\telse if (isArray(v))\n\t{\n\t\tconst febcode::ArrayValue& arr = getArray(v);\n\t\to << \"[\";\n\t\tfor (size_t i = 0; i < arr.size(); ++i)\n\t\t{\n\t\t\to << arr.elements[i];\n\t\t\tif (i != arr.size() - 1) o << \", \";\n\t\t}\n\t\treturn o << \"]\";\n\t}\n\telse if (isStruct(v))\n\t{\n\t\tconst febcode::StructValue& s = getStruct(v);\n\t\to << \"{\";\n\t\tfor (size_t i = 0; i < s.fields.size(); ++i)\n\t\t{\n\t\t\to << s.fields[i];\n\t\t\tif (i != s.fields.size() - 1) o << \", \";\n\t\t}\n\t\treturn o << \"}\";\n\t}\n\telse if (isVec2(v))\n\t{\n\t\tconst febcode::vec2& vec = getVec2(v);\n\t\treturn o << \"vec2(\" << vec.x << \", \" << vec.y << \")\";\n\t}\n\telse if (isVec3(v))\n\t{\n\t\tconst febcode::vec3& vec = getVec3(v);\n\t\treturn o << \"vec3(\" << vec.x << \", \" << vec.y << \", \" << vec.z << \")\";\n\t}\n\telse if (isMat2(v))\n\t{\n\t\tconst febcode::mat2& mat = getMat2(v);\n\t\treturn o << \"mat2(\" << mat.m[0][0] << \", \" << mat.m[0][1] << \", \" << mat.m[1][0] << \", \" << mat.m[1][1] << \")\";\n\t}\n\telse if (isMat3(v))\n\t{\n\t\tconst febcode::mat3& mat = getMat3(v);\n\t\treturn o << \"mat3(\" << mat.m[0][0] << \", \" << mat.m[0][1] << \", \" << mat.m[0][2] << \", \" << mat.m[1][0] << \", \" << mat.m[1][1] << \", \" << mat.m[1][2] << \", \" << mat.m[2][0] << \", \" << mat.m[2][1] << \", \" << mat.m[2][2] << \")\";\n\t}\n\telse\n\t\treturn o << \"<unknown value>\";\n}\n\nstd::string to_nice_string(double d)\n{\n\tstd::ostringstream ss;\n\tss << d;\n\treturn ss.str();\n}\n\nstd::string ValueToString(const febcode::Value& v)\n{\n\tstd::string s;\n\tif      (isVoid  (v)) s = \"null\";\n\telse if (isBool  (v)) s = getBool(v) ? \"true\" : \"false\";\n\telse if (isInt   (v)) s = std::to_string(getInt (v));\n\telse if (isDouble(v)) s = to_nice_string(getDouble(v));\n\telse if (isVec2(v))\n\t{\n\t\ts = \"vec2(\";\n\t\tvec2 q = getVec2(v);\n\t\tif (isZero(q))\n\t\t\ts += to_nice_string(0.0);\n\t\telse\n\t\t\ts += to_nice_string(q.x) + \", \" + to_nice_string(q.y);\n\t\ts += \")\";\n\t}\n\telse if (isVec3(v))\n\t{\n\t\ts = \"vec3(\";\n\t\tvec3 q = getVec3(v);\n\t\tif (isZero(q))\n\t\t{\n\t\t\ts += to_nice_string(0.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts += to_nice_string(q.x) + \", \" + to_nice_string(q.y) + \", \" + to_nice_string(q.z);\n\t\t}\n\t\ts += \")\";\n\t}\n\telse if (isMat2(v))\n\t{\n\t\ts = \"mat2(\";\n\n\t\tconst mat2& m = v.mat2Value;\n\t\tif (isZero(m))\n\t\t{\n\t\t\ts += to_nice_string(0.0);\n\t\t}\n\t\telse if (isIdentity(m))\n\t\t{\n\t\t\ts += to_nice_string(1.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (int i = 0; i < 2; ++i)\n\t\t\t\tfor (int j = 0; j < 2; ++j)\n\t\t\t\t{\n\t\t\t\t\ts += to_nice_string(m.m[i][j]);\n\t\t\t\t\tif ((i != 1) || (j != 1)) s += \",\";\n\t\t\t\t}\n\t\t}\n\t\ts += \")\";\n\t}\n\telse if (isMat3(v))\n\t{\n\t\ts = \"mat3(\";\n\n\t\tconst mat3& m = v.mat3Value;\n\t\tif (isZero(m))\n\t\t{\n\t\t\ts += to_nice_string(0.0);\n\t\t}\n\t\telse if (isIdentity(m))\n\t\t{\n\t\t\ts += to_nice_string(1.0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (int i = 0; i < 3; ++i)\n\t\t\t\tfor (int j = 0; j < 3; ++j)\n\t\t\t\t{\n\t\t\t\t\ts += to_nice_string(m.m[i][j]);\n\t\t\t\t\tif ((i != 2) || (j != 2)) s += \",\";\n\t\t\t\t}\n\t\t}\n\t\ts += \")\";\n\t}\n\telse if (isArray (v)) { auto& p = getArrayPtr (v); s = \"[array:\"  + std::to_string(p.use_count()) + \"]\"; }\n\telse if (isStruct(v)) { \n\t\tconst febcode::StructValue& o = febcode::getStruct(v);\n\t\tconst std::string& name = o.type->name;\n\t\tauto& p = getStructPtr(v);\n\t\ts = \"[\" + name;\n\t\ts = \"{\";\n\t\tfor (size_t i = 0; i < o.fields.size(); ++i)\n\t\t{\n\t\t\ts += ValueToString(o.fields[i]);\n\t\t\tif (i != o.fields.size() - 1) s += \", \";\n\t\t}\n\t\ts += \"}\";\n\t\ts += \":\" + std::to_string(p.use_count()) + \"]\";\n\t}\n\telse s = \"unknown\";\n\treturn s;\n}\n\nstd::string ValueTypeToString(const febcode::Value& v)\n{\n\tif      (isVoid  (v)) return \"void\";\n\telse if (isBool  (v)) return \"bool\";\n\telse if (isInt   (v)) return \"int\";\n\telse if (isDouble(v)) return \"double\";\n\telse if (isArray (v)) return \"array\";\n\telse if (isStruct(v)) return \"struct\";\n\telse if (isVec2  (v)) return \"vec2\";\n\telse if (isVec3  (v)) return \"vec3\";\n\telse if (isRef    (v)) return \"ref\";\n\telse return \"<unknown type>\";\n}\n\nstd::string opToString(BinaryOp op)\n{\n\tswitch (op)\n\t{\n\tcase BinaryOp::Plus:         return \"+\";\n\tcase BinaryOp::Minus:        return \"-\";\n\tcase BinaryOp::Multiply:     return \"*\";\n\tcase BinaryOp::Divide:       return \"/\";\n\tcase BinaryOp::Exponent:     return \"**\";\n\tcase BinaryOp::Greater:      return \">\";\n\tcase BinaryOp::GreaterEqual: return \">=\";\n\tcase BinaryOp::Less:         return \"<\";\n\tcase BinaryOp::LessEqual:    return \"<=\";\n\tcase BinaryOp::EqualEqual:   return \"==\";\n\tcase BinaryOp::NotEqual:     return \"!=\";\n\tcase BinaryOp::AndAnd:       return \"&&\";\n\tcase BinaryOp::OrOr:         return \"||\";\n\tdefault:\n\t\treturn \"unknown_op\";\n\t}\n}\n"
  },
  {
    "path": "febcode/ast.h",
    "content": "#pragma once\n#include <string>\n#include <vector>\n#include <algorithm>\n#include <cstring>\n#include <cmath>\n#include \"types.h\"\n\nnamespace febcode {\n\n\tenum class BinaryOp {\n\t\tPlus, Minus, Multiply, Divide, Exponent,\n\t\tEqualEqual, NotEqual,\n\t\tGreater, GreaterEqual,\n\t\tLess, LessEqual, \n\t\tAndAnd, OrOr\n\t};\n\n\tenum class UnaryOp {\n\t\tNegate, // \"-\"\n\t\tNot\t // \"!\"\n\t};\n\n\t// Forward declarations\n\tstruct Expression;\n\tstruct Statement;\n\n\tenum class ExpressionType {\n\t\tLiteral,\n\t\tVariable,\n\t\tBinary,\n\t\tUnary,\n\t\tCall,\n\t\tMember,\n\t\tInitializer,\n\t\tIndex,\n\t\tAssignment,\n\t\tConstructor\n\t};\n\n\t// --- Expressions ---\n\n\t// Base classes\n\tstruct Expression {\n\t\tExpression(ExpressionType type) : exprType(type) {}\n\t\tvirtual ~Expression() = default;\n\t\tExpressionType exprType;\n\t\tType valType = nullptr; // Value type of expression. Determined during resolution\n\t};\n\n\tusing ExprPtr = std::unique_ptr<Expression>;\n\n\tstruct LiteralExpr : Expression {\n\t\tValue value;\n\t\tLiteralExpr(const Value& v) : Expression(ExpressionType::Literal), value(v) {}\n\t};\n\n\tstruct VariableExpr : Expression {\n\t\tstd::string name;\n\t\tVariableExpr(const std::string& n) : Expression(ExpressionType::Variable), name(n) {}\n\t};\n\n\tclass AssignExpr : public Expression\n\t{\n\tpublic:\n\t\tExprPtr target;\n\t\tExprPtr value;\n\n\tpublic:\n\t\tAssignExpr(ExprPtr target, ExprPtr value)\n\t\t\t: Expression(ExpressionType::Assignment), target(std::move(target)), value(std::move(value)) {\n\t\t}\n\t};\n\n\tstruct BinaryExpr : Expression {\n\t\tBinaryOp op;\n\t\tExprPtr left;\n\t\tExprPtr right;\n\n\t\tBinaryExpr(ExprPtr l, BinaryOp o, ExprPtr r)\n\t\t\t: Expression(ExpressionType::Binary), left(std::move(l)), op(o), right(std::move(r)) {\n\t\t}\n\t};\n\n\tstruct UnaryExpr : Expression {\n\t\tUnaryOp op;\n\t\tExprPtr right;\n\n\t\tUnaryExpr(UnaryOp o, ExprPtr r)\n\t\t\t: Expression(ExpressionType::Unary), op(o), right(std::move(r)) {}\n\t};\n\n\tstruct CallExpr : Expression {\n\t\tstd::string name;\n\t\tstd::vector<ExprPtr> arguments;\n\n\t\tCallExpr(const std::string& name, std::vector<ExprPtr> arguments)\n\t\t\t: Expression(ExpressionType::Call), name(name),\n\t\t\targuments(std::move(arguments)) {}\n\t};\n\n\tstruct MemberExpr : Expression\n\t{\n\t\tExprPtr object;\n\t\tstd::string property;\n\n\t\tMemberExpr(ExprPtr object, std::string property)\n\t\t\t: Expression(ExpressionType::Member), object(std::move(object)), property(std::move(property)) {}\n\t};\n\n\tstruct InitExpr : Expression {\n\t\tstd::vector<ExprPtr> elements;\n\t\tInitExpr(std::vector<ExprPtr> elements)\n\t\t\t: Expression(ExpressionType::Initializer), elements(std::move(elements)) {}\n\t};\n\n\tstruct IndexExpr : Expression {\n\t\tExprPtr object;\n\t\tExprPtr index;\n\t\tIndexExpr(ExprPtr object, ExprPtr index)\n\t\t\t: Expression(ExpressionType::Index), object(std::move(object)), index(std::move(index)) {}\n\t};\n\n\tstruct ConstructorExpr : Expression {\n\t\tstd::vector<ExprPtr> args;\n\t\tConstructorExpr(Type type, std::vector<ExprPtr> arguments) :\n\t\t\tExpression(ExpressionType::Constructor), args(std::move(arguments)) {\n\t\t\tvalType = type;\n\t\t}\n\t};\n\n\t// --- Statements ---\n\n\tstruct Statement {\n\t\tvirtual ~Statement() = default;\n\t};\n\tusing StmtPtr = std::unique_ptr<Statement>;\n\n\tstruct ExpressionStmt : Statement {\n\t\tExprPtr expr;\n\t\tExpressionStmt(ExprPtr e) : expr(std::move(e)) {}\n\t};\n\n\tstruct Var {\n\t\tstd::string name;\n\t\tstd::vector<size_t> arraySizes; // empty if not an array\n\t\tExprPtr initializer; // can be null\n\t};\n\n\tstruct VarDeclStmt : Statement {\n\t\tType type = nullptr;\n\t\tbool input = false; // declare input variables (treated as const, non-differentiable)\n\t\tstd::vector<Var> vars;\n\t\tVarDeclStmt(Type type, const std::string& name, ExprPtr initializer, bool input = false) : type(type), input(input)\n\t\t{\n\t\t\tvars.push_back({ name, std::vector<size_t>(), std::move(initializer)});\n\t\t}\n\t\tVarDeclStmt(Type type, Var& var, bool input = false)\n\t\t\t: type(type), input(input) { \n\t\t\tvars.emplace_back(std::move(var));\n\t\t}\n\t\tVarDeclStmt(Type type, std::vector<Var>& vars, bool input = false)\n\t\t\t: type(type), input(input), vars(std::move(vars)) {}\n\t};\n\n\tstruct ReturnStmt : Statement {\n\t\tExprPtr value; // can be null\n\t\tReturnStmt(ExprPtr v) : value(std::move(v)) {}\n\t};\n\n\tstruct BlockStmt : Statement {\n\t\tstd::vector<StmtPtr> statements;\n\n\t\tvoid addStatement(StmtPtr stmt) {\n\t\t\tstatements.push_back(std::move(stmt));\n\t\t}\n\t};\n\n\tstruct IfStmt : Statement {\n\t\tExprPtr condition;\n\t\tStmtPtr thenBranch;\n\t\tStmtPtr elseBranch; // optional\n\n\t\tIfStmt() {}\n\n\t\tIfStmt(ExprPtr cond,\n\t\t\tStmtPtr thenStmt,\n\t\t\tStmtPtr elseStmt = nullptr)\n\t\t\t: condition(std::move(cond)),\n\t\t\tthenBranch(std::move(thenStmt)),\n\t\t\telseBranch(std::move(elseStmt)) {\n\t\t}\n\t};\n\n\tstruct WhileStmt : Statement {\n\t\tExprPtr condition;\n\t\tStmtPtr body;\n\n\t\tWhileStmt(ExprPtr cond,\n\t\t\tstd::unique_ptr<Statement> bodyStmt)\n\t\t\t: condition(std::move(cond)),\n\t\t\tbody(std::move(bodyStmt)) {\n\t\t}\n\t};\n\n\tstruct ForStmt : Statement {\n\t\tStmtPtr initializer; // can be null\n\t\tExprPtr condition;\n\t\tExprPtr increment;\n\t\tStmtPtr body;\n\n\t\tForStmt(std::unique_ptr<Statement> init,\n\t\t\tExprPtr cond,\n\t\t\tExprPtr incr,\n\t\t\tstd::unique_ptr<Statement> bodyStmt)\n\t\t\t: initializer(std::move(init)),\n\t\t\tcondition(std::move(cond)),\n\t\t\tincrement(std::move(incr)),\n\t\t\tbody(std::move(bodyStmt)) {\n\t\t}\n\t};\n\n\tstruct FunctionStmt : Statement {\n\t\tstd::string name;\n\t\tType returnType = nullptr;\n\t\tstd::vector<std::pair<Type,std::string>> params;\n\t\tStmtPtr body;\n\n\t\tFunctionStmt(std::string name, Type returnType,\n\t\t\tstd::vector<std::pair<Type,std::string>> parameters,\n\t\t\tstd::unique_ptr<Statement> bdy)\n\t\t\t: name(std::move(name)),\n\t\t\treturnType(returnType),\n\t\t\tparams(std::move(parameters)),\n\t\t\tbody(std::move(bdy)) {\n\t\t}\n\t};\n\n\tstruct StructStmt : Statement {\n\t\tstd::string name;\n\t\tType type;\n\t\tstd::vector<std::pair<Type, std::string>> fields;\n\t\tStructStmt(std::string name, Type type,\n\t\t\tstd::vector<std::pair<Type, std::string>> fields)\n\t\t\t: name(std::move(name)),\n\t\t\ttype(type),\n\t\t\tfields(std::move(fields)) {\n\t\t}\n\t};\n\n\tstruct AST {\n\t\tBlockStmt root;\n\n\t\tAST() {}\n\n\t\tvoid clear() { root.statements.clear(); }\n\n\t\tbool empty() const { return root.statements.empty(); }\n\n\t\tsize_t size() const { return root.statements.size(); }\n\n\t\tStatement* operator[](size_t index) const {\n\t\t\tif (index >= root.statements.size()) {\n\t\t\t\treturn nullptr;\n\t\t\t}\n\t\t\treturn root.statements[index].get();\n\t\t}\n\n\t\tvoid addStatement(std::unique_ptr<Statement> stmt) {\n\t\t\troot.statements.push_back(std::move(stmt));\n\t\t}\n\t};\n\n\t// some helper variables for statements\n\tinline bool isVarDecl (const StmtPtr& stmt) { return dynamic_cast<const VarDeclStmt*   >(stmt.get()) != nullptr; }\n\tinline bool isExprStmt(const StmtPtr& stmt) { return dynamic_cast<const ExpressionStmt*>(stmt.get()) != nullptr; }\n\tinline bool isReturn  (const StmtPtr& stmt) { return dynamic_cast<const ReturnStmt*    >(stmt.get()) != nullptr; }\n\n\t// use this to make a deep copy of an expression when constructing new expressions from existing ones\n\tExprPtr clone(const Expression* expr);\n\n\t// expression checks\n\tinline bool isLiteral    (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Literal    ); }\n\tinline bool isVariable   (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Variable   ); }\n\tinline bool isBinary     (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Binary     ); }\n\tinline bool isUnary      (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Unary      ); }\n\tinline bool isCall       (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Call       ); }\n\tinline bool isMember     (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Member     ); }\n\tinline bool isInitializer(const ExprPtr& expr) { return (expr->exprType == ExpressionType::Initializer); }\n\tinline bool isIndex      (const ExprPtr& expr) { return (expr->exprType == ExpressionType::Index      ); }\n\n\tinline bool isInt(const Expression* expr, int& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isInt(literal->value)) {\n\t\t\t\tvalue = literal->value.i;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isDouble(const Expression* expr, double& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isDouble(literal->value)) {\n\t\t\t\tvalue = literal->value.d;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isScalar(const Expression* expr, double& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isInt(literal->value)) {\n\t\t\t\tvalue = static_cast<double>(literal->value.i);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (isDouble(literal->value)) {\n\t\t\t\tvalue = literal->value.d;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isVec2(const Expression* expr, vec2& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isVec2(literal->value)) {\n\t\t\t\tvalue = literal->value.vec2Value;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isVec3(const Expression* expr, vec3& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isVec3(literal->value)) {\n\t\t\t\tvalue = literal->value.vec3Value;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isMat2(const Expression* expr, mat2& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isMat2(literal->value)) {\n\t\t\t\tvalue = literal->value.mat2Value;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isMat3(const Expression* expr, mat3& value) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr)) {\n\t\t\tif (isMat3(literal->value)) {\n\t\t\t\tvalue = literal->value.mat3Value;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isAdd(const Expression* expr, Expression*& left, Expression*& right) {\n\t\tif (expr->exprType == ExpressionType::Binary) {\n\t\t\tauto binary = dynamic_cast<const BinaryExpr*>(expr);\n\t\t\tif (binary->op == BinaryOp::Plus) {\n\t\t\t\tleft  = binary->left.get();\n\t\t\t\tright = binary->right.get();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isSub(const Expression* expr, Expression*& left, Expression*& right) {\n\t\tif (expr->exprType == ExpressionType::Binary) {\n\t\t\tauto binary = dynamic_cast<const BinaryExpr*>(expr);\n\t\t\tif (binary->op == BinaryOp::Minus) {\n\t\t\t\tleft  = binary->left.get();\n\t\t\t\tright = binary->right.get();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isMul(const Expression* expr, Expression*& left, Expression*& right) {\n\t\tif (expr->exprType == ExpressionType::Binary) {\n\t\t\tauto binary = dynamic_cast<const BinaryExpr*>(expr);\n\t\t\tif (binary->op == BinaryOp::Multiply) {\n\t\t\t\tleft = binary->left.get();\n\t\t\t\tright = binary->right.get();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isDiv(const Expression* expr, Expression*& left, Expression*& right) {\n\t\tif (expr->exprType == ExpressionType::Binary) {\n\t\t\tauto binary = dynamic_cast<const BinaryExpr*>(expr);\n\t\t\tif (binary->op == BinaryOp::Divide) {\n\t\t\t\tleft = binary->left.get();\n\t\t\t\tright = binary->right.get();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isExp(const Expression* expr, Expression*& left, Expression*& right) {\n\t\tif (expr->exprType == ExpressionType::Binary) {\n\t\t\tauto binary = dynamic_cast<const BinaryExpr*>(expr);\n\t\t\tif (binary->op == BinaryOp::Exponent) {\n\t\t\t\tleft = binary->left.get();\n\t\t\t\tright = binary->right.get();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isNegate(const Expression* expr, Expression*& operand) {\n\t\tif (expr->exprType != ExpressionType::Unary) return false;\n\t\tauto unary = dynamic_cast<const UnaryExpr*>(expr);\n\t\tif (unary->op == UnaryOp::Negate) {\n\t\t\toperand = unary->right.get();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isCall1(const Expression* expr, const std::string& fncName, const Expression*& arg) {\n\t\tif (expr->exprType != ExpressionType::Call) return false;\n\t\tauto call = dynamic_cast<const CallExpr*>(expr);\n\t\tif (call->arguments.size() != 1) return false;\n\t\tif (fncName != call->name) return false;\n\t\targ = call->arguments[0].get();\n\t\treturn true;\n\t}\n\n\tinline bool isCall2(const Expression* expr, const std::string& fncName, const Expression*& arg1, const Expression*& arg2) {\n\t\tif (expr->exprType != ExpressionType::Call) return false;\n\t\tauto call = dynamic_cast<const CallExpr*>(expr);\n\t\tif (call->arguments.size() != 2) return false;\n\t\tif (fncName != call->name) return false;\n\t\targ1 = call->arguments[0].get();\n\t\targ2 = call->arguments[1].get();\n\t\treturn true;\n\t}\n\n\tinline bool isScalar(const Expression* expr) {\n\t\treturn isScalarType(expr->valType);\n\t}\n\n\tinline bool isScalar(const ExprPtr& expr) {\n\t\treturn isScalar(expr.get());\n\t}\n\n\tinline bool isZero(const Expression* expr) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr))\n\t\t\treturn isZero(literal->value);\n\t\tif (auto init = dynamic_cast<const InitExpr*>(expr))\n\t\t\treturn std::all_of(init->elements.begin(), init->elements.end(), [](const ExprPtr& arg) { return isZero(arg.get()); });\n\t\tif (auto ctor = dynamic_cast<const ConstructorExpr*>(expr))\n\t\t\treturn std::all_of(ctor->args.begin(), ctor->args.end(), [](const ExprPtr& arg) { return isZero(arg.get()); });\n\t\treturn false;\n\t}\n\n\tinline bool isZero(const ExprPtr& expr) {\n\t\treturn isZero(expr.get());\n\t}\n\n\tinline bool isOne(const Expression* expr) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr))\n\t\t\treturn isOne(literal->value);\n\t\treturn false;\n\t}\n\n\tinline bool isOne(const ExprPtr& expr) {\n\t\treturn isOne(expr.get());\n\t}\n\n\tinline bool isIdentity(const Expression* expr) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr))\n\t\t{\n\t\t\tconst Value& v = literal->value;\n\t\t\tif (isMat2(v) && v.mat2Value == mat2(1.0)) return true;\n\t\t\tif (isMat3(v) && v.mat3Value == mat3(1.0)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isVector(const Expression* expr) {\n\t\treturn (isVec2Type(expr->valType) || isVec3Type(expr->valType));\n\t}\n\n\tinline bool isMatrix(const Expression* expr) {\n\t\treturn (isMat2Type(expr->valType) || isMat3Type(expr->valType));\n\t}\n\n\tinline bool isSymmetric(const Expression* expr) {\n\t\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr))\n\t\t{\n\t\t\tconst Value& v = literal->value;\n\t\t\tif (isMat2(v) && isSymmetric(v.mat2Value)) return true;\n\t\t\tif (isMat3(v) && isSymmetric(v.mat3Value)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline bool isNegation(const ExprPtr& expr) {\n\t\tif (expr->exprType != ExpressionType::Unary) return false;\n\t\tauto unary = dynamic_cast<UnaryExpr*>(expr.get());\n\t\treturn (unary->op == UnaryOp::Negate);\n\t}\n\n\tbool isEqual(const Expression* l, const Expression* r);\n\tbool isEqual(const ExprPtr& l, const ExprPtr& r);\n\tbool isEqual(const std::vector<ExprPtr>& l, const std::vector<ExprPtr>& r);\n\n} // namespace febcode\n\nstd::ostream& operator << (std::ostream& o, const febcode::Value& v);\n\nstd::string ValueToString(const febcode::Value& v);\n\nstd::string ValueTypeToString(const febcode::Value& v);\n\nstd::string opToString(febcode::BinaryOp op);\n"
  },
  {
    "path": "febcode/compiler.cpp",
    "content": "#include \"compiler.h\"\n#include \"scanner.h\"\n#include \"parser.h\"\n#include \"resolver.h\"\n\nusing namespace febcode;\n\nvoid febcode::CompileSource(Program& prg, const std::string& source)\n{\n\t// 1. Tokenize\n\tScanner scanner(source);\n\tstd::vector<Token> tokens = scanner.scanTokens();\n\n\t// 2. Parse -> AST\n\tParser parser(prg);\n\tparser.parse(tokens);\n\n\t// Optional safety check\n\tif (prg.ast->empty())\n\t\tthrow std::runtime_error(\"Parser produced no statements.\");\n\n\t// 3. semantic analysis\n\tResolver resolve(prg);\n\tresolve.resolve();\n\n\t// 4. Compile -> bytecode\n\tCompiler compiler(prg);\n\tcompiler.compile();\n}\n\n//\n// ================= IMPLEMENTATION =================\n//\n\nCompiler::Compiler(Program& prg) : prg(prg)\n{\n}\n\n//\n// ===== Scope =====\n//\n\nvoid Compiler::beginScope()\n{\n\tm_scopeDepth++;\n}\n\nvoid Compiler::endScope()\n{\n\twhile (!m_locals.empty() && m_locals.back().depth == m_scopeDepth)\n\t{\n\t\tLocal& local = m_locals.back();\n\n\t\tswitch (local.type->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\t\tcase TypeKind::Vec2  : emit(OpCode::POP_VEC2  ); break;\n\t\tcase TypeKind::Vec3  : emit(OpCode::POP_VEC3  ); break;\n\t\tcase TypeKind::Mat2  : emit(OpCode::POP_MAT2  ); break;\n\t\tcase TypeKind::Mat3  : emit(OpCode::POP_MAT3  ); break;\n\t\tcase TypeKind::Array : \n\t\t\tif (local.type->size() > 255)\n\t\t\t\tthrow std::runtime_error(\"Array size exceeds maximum of 255.\");\n\t\t\temit(OpCode::POP_ARRAY, (int)local.type->size());\n\t\t\temitUint8((uint8_t)local.type->size());\n\t\t\tbreak;\n\t\tcase TypeKind::Struct:\n\t\t\temit(OpCode::POP_STRUCT, (int)local.type->size());\n\t\t\temitUint8((uint8_t)local.type->typeIndex);\n\t\tbreak;\t\t\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unknown type kind in endScope.\");\n\t\t\tbreak;\n\t\t}\n\n\t\tm_locals.pop_back();\n\t\tlocalStackSize -= local.type->size();\n\t}\n\tm_scopeDepth--;\n}\n\nint Compiler::resolveLocal(const std::string& name)\n{\n\tfor (int i = (int)m_locals.size() - 1; i >= 0; --i)\n\t\tif (m_locals[i].name == name)\n\t\t\treturn i;\n\treturn -1;\n}\n\nint Compiler::resolveGlobal(const std::string& name)\n{\n\tauto it = prg.globalIndices.find(name);\n\tif (it != prg.globalIndices.end())\n\t\treturn (int)it->second;\n\n\treturn -1;\n}\n\nType Compiler::resolveVariableType(const std::string& name)\n{\n\tint localIndex = resolveLocal(name);\n\tif (localIndex != -1)\n\t\treturn m_locals[localIndex].type;\n\tint globalIndex = resolveGlobal(name);\n\tif (globalIndex != -1)\n\t\treturn prg.globals[globalIndex].type;\n\tthrow std::runtime_error(\"Undefined variable: \" + name);\n}\n\n//\n// ===== Bytecode Helpers =====\n//\n\nvoid Compiler::emit(OpCode op, int arg)\n{\n\tprg.code.push_back((uint8_t)op);\n\n\tstackDepth += stackEffect(op, arg); assert(stackDepth >= 0);\n\tif (stackDepth > maxStackDepth)\n\t\tmaxStackDepth = stackDepth;\n}\n\nvoid Compiler::emitUint8(uint8_t v)\n{\n\tprg.code.push_back(v);\n}\n\nvoid Compiler::emitUint16(uint16_t v)\n{\n\tprg.code.push_back((v >> 8) & 0xff);\n\tprg.code.push_back(v & 0xff);\n}\n\nint Compiler::stackEffect(OpCode op, int arg)\n{\n\tswitch (op)\n\t{\n\tcase OpCode::PUSH_VOID: \n\tcase OpCode::PUSH_BOOL: \n\tcase OpCode::PUSH_INT: \n\tcase OpCode::PUSH_DOUBLE: \n\t\treturn +1;\n\n\tcase OpCode::PUSH_VEC2: return +2;\n\tcase OpCode::PUSH_VEC3: return +3;\n\tcase OpCode::PUSH_MAT2: return +4;\n\tcase OpCode::PUSH_MAT3: return +9;\n\n\tcase OpCode::GET_GLOBAL_BOOL  : return +1;\n\tcase OpCode::GET_GLOBAL_INT   : return +1;\n\tcase OpCode::GET_GLOBAL_DOUBLE: return +1;\n\tcase OpCode::GET_GLOBAL_VEC2  : return +2;\n\tcase OpCode::GET_GLOBAL_VEC3  : return +3;\n\tcase OpCode::GET_GLOBAL_MAT2  : return +4;\n\tcase OpCode::GET_GLOBAL_MAT3  : return +9;\n\tcase OpCode::GET_GLOBAL_ARRAY : return +arg;\n\tcase OpCode::GET_GLOBAL_STRUCT: return +arg;\n\n\tcase OpCode::SET_GLOBAL_BOOL: \n\tcase OpCode::SET_GLOBAL_INT: \n\tcase OpCode::SET_GLOBAL_DOUBLE: \n\tcase OpCode::SET_GLOBAL_VEC2: \n\tcase OpCode::SET_GLOBAL_VEC3: \n\tcase OpCode::SET_GLOBAL_MAT2: \n\tcase OpCode::SET_GLOBAL_MAT3: \n\tcase OpCode::SET_GLOBAL_ARRAY: \n\tcase OpCode::SET_GLOBAL_STRUCT: \n\t\treturn 0;\n\n\tcase OpCode::GET_GLOBAL_REF       : return +1;\n\n\tcase OpCode::GET_LOCAL_BOOL  : return +1;\n\tcase OpCode::GET_LOCAL_INT   : return +1;\n\tcase OpCode::GET_LOCAL_DOUBLE: return +1;\n\tcase OpCode::GET_LOCAL_VEC2  : return +2;\n\tcase OpCode::GET_LOCAL_VEC3  : return +3;\n\tcase OpCode::GET_LOCAL_MAT2  : return +4;\n\tcase OpCode::GET_LOCAL_MAT3  : return +9;\n\tcase OpCode::GET_LOCAL_ARRAY : return +arg;\n\tcase OpCode::GET_LOCAL_STRUCT: return +arg;\n\n\tcase OpCode::GET_LOCAL_REF       : return +1;\n\n\tcase OpCode::GET_PROPERTY_BOOL  : return +1;\n\tcase OpCode::GET_PROPERTY_INT   : return +1;\n\tcase OpCode::GET_PROPERTY_DOUBLE: return +1;\n\tcase OpCode::GET_PROPERTY_VEC2  : return +2;\n\tcase OpCode::GET_PROPERTY_VEC3  : return +3;\n\tcase OpCode::GET_PROPERTY_MAT2  : return +4;\n\tcase OpCode::GET_PROPERTY_MAT3  : return +9;\n\tcase OpCode::GET_PROPERTY_ARRAY : return +1;\n\tcase OpCode::GET_PROPERTY_STRUCT: return +1;\n\n\tcase OpCode::GET_MEMBER_REF: return 0;\n\n\tcase OpCode::GET_INDEX_BOOL  : return +arg;\n\tcase OpCode::GET_INDEX_INT   : return +arg;\n\tcase OpCode::GET_INDEX_DOUBLE: return +arg;\n\tcase OpCode::GET_INDEX_VEC2  : return +arg;\n\tcase OpCode::GET_INDEX_VEC3  : return +arg;\n\tcase OpCode::GET_INDEX_MAT2  : return +arg;\n\tcase OpCode::GET_INDEX_MAT3  : return +arg;\n\tcase OpCode::GET_INDEX_ARRAY : return +arg;\n\tcase OpCode::GET_INDEX_STRUCT: return +arg;\n\n\tcase OpCode::GET_GLOBAL_INDEX_DOUBLE: return +1;\n\n\tcase OpCode::GET_INDEX_REF:\n\tcase OpCode::GET_INDEX_REF_BOOL:\n\tcase OpCode::GET_INDEX_REF_INT:\n\tcase OpCode::GET_INDEX_REF_DOUBLE:\n\tcase OpCode::GET_INDEX_REF_VEC2:\n\tcase OpCode::GET_INDEX_REF_VEC3:\n\tcase OpCode::GET_INDEX_REF_MAT2:\n\tcase OpCode::GET_INDEX_REF_MAT3:\n\t\treturn 0;\n\n\tcase OpCode::GET_VEC2_X: return -1;\n\tcase OpCode::GET_VEC2_Y: return -1;\n\tcase OpCode::GET_VEC2_X_REF: return 0;\n\tcase OpCode::GET_VEC2_Y_REF: return 0;\n\tcase OpCode::GET_VEC2_SWIZZLE: return +1; // in case the swizzle returns a vec3\n\tcase OpCode::GET_VEC2_INDEX: return -2; // pops the index and vec2, and pushes the component\n\n\tcase OpCode::GET_VEC3_X: return 0;\n\tcase OpCode::GET_VEC3_Y: return 0;\n\tcase OpCode::GET_VEC3_Z: return 0;\n\tcase OpCode::GET_VEC3_X_REF: return 0;\n\tcase OpCode::GET_VEC3_Y_REF: return 0;\n\tcase OpCode::GET_VEC3_Z_REF: return 0;\n\tcase OpCode::GET_VEC3_SWIZZLE: return +1;\n\tcase OpCode::GET_VEC3_INDEX: return -3; // pops the index and vec3, and pushes the component\n\n\tcase OpCode::NEG_INT: return 0;\n\tcase OpCode::ADD_INT: return -1;\n\tcase OpCode::SUB_INT: return -1;\n\tcase OpCode::MUL_INT: return -1;\n\tcase OpCode::DIV_INT: return -1;\n\tcase OpCode::EXP_INT: return -1;\n\tcase OpCode::GT_INT: return -1;\n\tcase OpCode::LT_INT: return -1;\n\tcase OpCode::GE_INT: return -1;\n\tcase OpCode::LE_INT: return -1;\n\tcase OpCode::NEG_DOUBLE: return 0;\n\tcase OpCode::SQR_DOUBLE: return 0;\n\tcase OpCode::SQRT_DOUBLE: return 0;\n\tcase OpCode::ADD_DOUBLE: return -1;\n\tcase OpCode::SUB_DOUBLE: return -1;\n\tcase OpCode::MUL_DOUBLE: return -1;\n\tcase OpCode::DIV_DOUBLE: return -1;\n\tcase OpCode::EXP_DOUBLE: return -1;\n\tcase OpCode::GT_DOUBLE: return -1;\n\tcase OpCode::LT_DOUBLE: return -1;\n\tcase OpCode::GE_DOUBLE: return -1;\n\tcase OpCode::LE_DOUBLE: return -1;\n\n\tcase OpCode::CREATE_VEC2_1ARG: return +1; // pops the double, pushes the vec2\n\tcase OpCode::NEG_VEC2: return 0;\n\tcase OpCode::ADD_VEC2: return -2;\n\tcase OpCode::SUB_VEC2: return -2;\n\tcase OpCode::DOT_VEC2: return -3;\n\tcase OpCode::MUL_VEC2_DOUBLE: return -1;\n\tcase OpCode::MUL_DOUBLE_VEC2: return -1;\n\tcase OpCode::DIV_VEC2_DOUBLE: return -1;\n\n\tcase OpCode::CREATE_VEC3_1ARG: return +2; // pops the double, pushes the vec3\n\tcase OpCode::NEG_VEC3: return 0;\n\tcase OpCode::ADD_VEC3: return -3;\n\tcase OpCode::SUB_VEC3: return -3;\n\tcase OpCode::DOT_VEC3: return -5;\n\tcase OpCode::MUL_VEC3_DOUBLE: return -1;\n\tcase OpCode::MUL_DOUBLE_VEC3: return -1;\n\tcase OpCode::DIV_VEC3_DOUBLE: return -1;\n\n\tcase OpCode::NEG_MAT2: return 0;\n\tcase OpCode::ADD_MAT2: return -4;\n\tcase OpCode::SUB_MAT2: return -4;\n\tcase OpCode::MUL_MAT2: return -4;\n\tcase OpCode::MUL_MAT2_DOUBLE: return -1;\n\tcase OpCode::MUL_DOUBLE_MAT2: return -1;\n\tcase OpCode::DIV_MAT2_DOUBLE: return -1;\n\tcase OpCode::MUL_MAT2_VEC2 : return -4;\n\tcase OpCode::GET_MAT2_INDEX: return -3;\n\tcase OpCode::CREATE_MAT2_DIAG: return +3;\n\n\tcase OpCode::NEG_MAT3: return 0;\n\tcase OpCode::ADD_MAT3: return -9;\n\tcase OpCode::SUB_MAT3: return -9;\n\tcase OpCode::MUL_MAT3: return -9;\n\tcase OpCode::MUL_MAT3_DOUBLE: return -1;\n\tcase OpCode::DIV_MAT3_DOUBLE: return -1;\n\tcase OpCode::MUL_DOUBLE_MAT3: return -1;\n\tcase OpCode::MUL_MAT3_VEC3  : return -9;\n\tcase OpCode::GET_MAT3_INDEX : return -7;\n\tcase OpCode::ADD_GLOBAL_MAT3: return +9;\n\tcase OpCode::SUB_GLOBAL_MAT3: return +9;\n\tcase OpCode::MUL_GLOBAL_MAT3: return +9;\n\tcase OpCode::CREATE_MAT3_DIAG: return +8;\n\tcase OpCode::CREATE_MAT3_VEC3: return +0;\n\n\tcase OpCode::NOT: return 0;\n\n\tcase OpCode::EQUAL_BOOL: \n\tcase OpCode::EQUAL_INT: \n\tcase OpCode::EQUAL_DOUBLE: \n\t\treturn -1;\n\n\tcase OpCode::NEQ_BOOL: \n\tcase OpCode::NEQ_INT: \n\tcase OpCode::NEQ_DOUBLE: \n\t\treturn -1;\n\n\tcase OpCode::JUMP: return 0;\n\tcase OpCode::JUMP_IF_FALSE: return +1; // technically +0, but each jump adds two pops (one for each branch)\n\tcase OpCode::JUMP_IF_TRUE: return +1; // technically +0, but each jump adds two pops (one for each branch)\n\tcase OpCode::LOOP: return 0;\n\n\tcase OpCode::STORE_BOOL:\n\tcase OpCode::STORE_INT:\n\tcase OpCode::STORE_DOUBLE:\n\tcase OpCode::STORE_VEC2:\n\tcase OpCode::STORE_VEC3:\n\tcase OpCode::STORE_MAT2:\n\tcase OpCode::STORE_MAT3:\n\tcase OpCode::STORE_ARRAY:\n\tcase OpCode::STORE_STRUCT:\n\t\treturn -1;\n\n\tcase OpCode::POP_VOID  : return -1;\n\tcase OpCode::POP_BOOL  : return -1;\n\tcase OpCode::POP_INT   : return -1;\n\tcase OpCode::POP_DOUBLE: return -1;\n\tcase OpCode::POP_VEC2  : return -2;\n\tcase OpCode::POP_VEC3  : return -3;\n\tcase OpCode::POP_MAT2  : return -4;\n\tcase OpCode::POP_MAT3  : return -9;\n\tcase OpCode::POP_ARRAY : return -arg;\n\tcase OpCode::POP_STRUCT: return -arg;\n\n\tcase OpCode::CALL: return arg; // this is the net stack effect calculated when compiling.\n\n\tcase OpCode::RETURN_VOID:\n\tcase OpCode::RETURN_BOOL: \n\tcase OpCode::RETURN_INT: \n\tcase OpCode::RETURN_DOUBLE: \n\tcase OpCode::RETURN_VEC2: \n\tcase OpCode::RETURN_VEC3: \n\tcase OpCode::RETURN_MAT2: \n\tcase OpCode::RETURN_MAT3: \n\tcase OpCode::RETURN_ARRAY: \n\tcase OpCode::RETURN_STRUCT: \n\t\treturn 0;\n\tdefault:\n\t\tassert(false);\n\t\treturn 0;\n\t}\n}\n\nuint8_t Compiler::addConstant(const Value& v)\n{\n\tprg.constants.push_back(v);\n\treturn (uint8_t)(prg.constants.size() - 1);\n}\n\nint Compiler::emitJump(OpCode op)\n{\n\temit(op);\n\temitUint16(0xffff);\n\treturn (int)prg.code.size() - 2;\n}\n\nvoid Compiler::patchJump(int offset)\n{\n\tint jump = (int)prg.code.size() - offset - 2;\n\tprg.code[offset] = (jump >> 8) & 0xff;\n\tprg.code[offset + 1] = jump & 0xff;\n}\n\nvoid Compiler::emitLoop(int loopStart)\n{\n\temit(OpCode::LOOP);\n\tint offset = (int)prg.code.size() - loopStart + 2;\n\temitUint16(offset);\n}\n\n//\n// ===== Program =====\n//\n\nvoid Compiler::compile()\n{\n\tprg.functions[0].entry = 0;\n\thasReturn = false;\n\n\texpectedReturnType = prg.returnType;\n\n\tfor (auto& stmt : prg.ast->root.statements)\n\t\tcompileStatement(stmt.get());\n\n\t// only add return if no return was encountered.\n\tif (!hasReturn)\n\t\temit(OpCode::RETURN_VOID);\n\n\tprg.maxStackSize = maxStackDepth;\n}\n\n//\n// ===== Statements =====\n//\n\nvoid Compiler::compileStatement(Statement* stmt)\n{\n\tif      (auto b = dynamic_cast<ExpressionStmt*>(stmt)) compileExprStmt(b);\n\telse if (auto b = dynamic_cast<BlockStmt*     >(stmt)) compileBlock(b);\n\telse if (auto v = dynamic_cast<VarDeclStmt*   >(stmt)) compileVarDecl(v);\n\telse if (auto f = dynamic_cast<FunctionStmt*  >(stmt)) compileFunction(f);\n\telse if (auto i = dynamic_cast<IfStmt*        >(stmt)) compileIf(i);\n\telse if (auto w = dynamic_cast<WhileStmt*     >(stmt)) compileWhile(w);\n\telse if (auto l = dynamic_cast<ForStmt*       >(stmt)) compileFor(l);\n\telse if (auto r = dynamic_cast<ReturnStmt*    >(stmt)) compileReturn(r);\n\telse if (auto s = dynamic_cast<StructStmt*    >(stmt)) compileStruct(s);\n\telse\n\t\tthrow std::runtime_error(\"Unsupported statement type\");\n}\n\nvoid Compiler::compileExprStmt(ExpressionStmt* stmt)\n{\n\tType type = compileExpression(stmt->expr.get());\n\tpop(type);\n}\n\nvoid Compiler::pop(Type type)\n{\n\tswitch (type->kind)\n\t{\n\tcase TypeKind::Void  : emit(OpCode::POP_VOID  ); break;\n\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\tcase TypeKind::Vec2  : emit(OpCode::POP_VEC2  ); break;\n\tcase TypeKind::Vec3  : emit(OpCode::POP_VEC3  ); break;\n\tcase TypeKind::Mat2  : emit(OpCode::POP_MAT2  ); break;\n\tcase TypeKind::Mat3  : emit(OpCode::POP_MAT3  ); break;\n\tcase TypeKind::Array : \n\t\tif (type->size() > 255)\n\t\t\tthrow std::runtime_error(\"Array size exceeds maximum of 255.\");\n\t\temit(OpCode::POP_ARRAY, (int)type->size());\n\t\temitUint8((uint8_t)type->size());\n\t\tbreak;\n\tcase TypeKind::Struct: \n\t\temit(OpCode::POP_STRUCT, (int)type->size());\n\t\temitUint8((uint8_t)type->typeIndex);\n\t\tbreak;\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported expression type in expression statement\");\n\t}\n}\n\nvoid Compiler::compileBlock(BlockStmt* stmt)\n{\n\tbeginScope();\n\tfor (auto& s : stmt->statements)\n\t\tcompileStatement(s.get());\n\tendScope();\n}\n\nType Compiler::compileInitializer(InitExpr* init)\n{\n\tif (init->elements.empty())\n\t\tthrow std::runtime_error(\"Initializer cannot be empty.\");\n\n\t// deduce the array type from the first element\n\tType elemType = compileExpression(init->elements[0].get());\n\tType arrayType = prg.types.getArrayType(elemType, { init->elements.size() });\n\n\t// compile the rest of the elements and check that they match the deduced type\n\tfor (size_t i = 1; i < init->elements.size(); ++i)\n\t{\n\t\tType type = coerce(compileExpression(init->elements[i].get()), elemType);\n\t\tif (type != elemType)\n\t\t\tthrow std::runtime_error(\"Initializer element type mismatch.\");\n\t}\n\n\treturn arrayType;\n}\n\nType Compiler::compileConstructor(ConstructorExpr* construct)\n{\n\t// check the number of arguments for the constructor\n\tint nargs = (int)construct->args.size();\n\tswitch (construct->valType->kind)\n\t{\n\tcase TypeKind::Vec2:\n\t\tif ((nargs != 1) && (nargs != 2))\n\t\t\tthrow std::runtime_error(\"Vec2 constructor must have 1 or 2 arguments.\");\n\t\tbreak;\n\tcase TypeKind::Vec3:\n\t\tif ((nargs != 1) && (nargs != 3))\n\t\t\tthrow std::runtime_error(\"Vec3 constructor must have 1 or 3 arguments.\");\n\t\tbreak;\n\tcase TypeKind::Mat2:\n\t\tif (nargs != 1 && nargs != 4)\n\t\t\tthrow std::runtime_error(\"Mat2 constructor must have either 1 or 4 arguments.\");\n\t\tbreak;\n\tcase TypeKind::Mat3:\n\t\tif (nargs != 1 && nargs != 3 && nargs != 9)\n\t\t\tthrow std::runtime_error(\"Mat3 constructor must have either 1, 3, or 9 arguments.\");\n\t\tbreak;\n\tcase TypeKind::Struct:\n\t\tif (nargs != construct->valType->fields.size())\n\t\t\tthrow std::runtime_error(\"Struct constructor must have exactly \" + std::to_string(construct->valType->fields.size()) + \" arguments.\");\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\t// process each argument and check their types\n\tstd::vector<Type> argTypes;\n\tfor (int i = 0; i < construct->args.size(); ++i)\n\t{\n\t\tType argType = compileExpression(construct->args[i].get());\n\t\targTypes.push_back(argType);\n\n\t\t// check the argument type against the expected type for the constructor\n\t\tswitch (construct->valType->kind)\n\t\t{\n\t\tcase TypeKind::Vec2:\n\t\tcase TypeKind::Vec3:\n\t\tcase TypeKind::Mat2:\n\t\t\targType = coerce(argType, prg.types.Double());\n\t\t\tif (argType != prg.types.Double())\n\t\t\t\tthrow std::runtime_error(\"Vec2 constructor arguments must be of type double.\");\n\t\t\tbreak;\n\t\tcase TypeKind::Mat3:\n\t\t\tif (!isScalarType(argType) && argType != prg.types.Vec3())\n\t\t\t\tthrow std::runtime_error(\"Mat3 constructor arguments must be of type int, double or vec3.\");\n\t\t\tbreak;\n\t\tcase TypeKind::Struct:\n\t\t\targType = coerce(argType, construct->valType->fields[i].first);\n\t\t\tif (argType != construct->valType->fields[i].first)\n\t\t\t\tthrow std::runtime_error(\"Struct constructor argument type mismatch.\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported constructor type\");\n\t\t}\n\t}\n\n\t// emit special codes for matrix constructors\n\tif (nargs == 1)\n\t{\n\t\tif (construct->valType->kind == TypeKind::Vec2)\n\t\t\temit(CREATE_VEC2_1ARG);\n\t\telse if (construct->valType->kind == TypeKind::Vec3)\n\t\t\temit(CREATE_VEC3_1ARG);\n\t\telse if (construct->valType->kind == TypeKind::Mat2)\n\t\t\temit(CREATE_MAT2_DIAG);\n\t\telse if (construct->valType->kind == TypeKind::Mat3)\n\t\t\temit(CREATE_MAT3_DIAG);\n\t}\n\n\tif (nargs == 3)\n\t{\n\t\tif (construct->valType->kind == TypeKind::Mat3)\n\t\t\temit(CREATE_MAT3_VEC3);\n\t}\n\n\treturn construct->valType;\n}\n\nType Compiler::expressionType(Expression* expr)\n{\n\tif (auto l = dynamic_cast<LiteralExpr*>(expr))\n\t{\n\t\treturn prg.types.getBuiltinType(l->value);\n\t}\n\tif (auto v = dynamic_cast<VariableExpr*>(expr))\n\t{\n\t\treturn resolveVariableType(v->name);\n\t}\n\tif (auto u = dynamic_cast<UnaryExpr*>(expr))\n\t{\n\t\treturn expressionType(u->right.get());\n\t}\n\tif (auto b = dynamic_cast<BinaryExpr*>(expr))\n\t{\n\t\tType leftType = expressionType(b->left.get());\n\t\tType rightType = expressionType(b->right.get());\n\n\t\t// this is only supported for multiplications!\n\t\tif (b->op == BinaryOp::Multiply)\n\t\t{\n\t\t\t// scalar * scalar --> scalar\n\t\t\tif (isScalarType(leftType) && isScalarType(rightType))\n\t\t\t\treturn commonType(leftType, rightType);\n\n\t\t\t// scalar * type --> type\n\t\t\tif (isScalarType(leftType) && isVec2Type(rightType))\n\t\t\t\treturn rightType;\n\t\t\tif (isScalarType(leftType) && isVec3Type(rightType))\n\t\t\t\treturn rightType;\n\t\t\tif (isScalarType(leftType) && isMat2Type(rightType))\n\t\t\t\treturn rightType;\n\t\t\tif (isScalarType(leftType) && isMat3Type(rightType))\n\t\t\t\treturn rightType;\n\n\t\t\t// vec2 * type\n\t\t\tif (isVec2Type(leftType) && isScalarType(rightType))\n\t\t\t\treturn leftType;\n\t\t\tif (isVec2Type(leftType) && isVec2Type(rightType)) // dot product\n\t\t\t\treturn prg.types.Double();\n\n\t\t\t// vec3 * type\n\t\t\tif (isVec3Type(leftType) && isScalarType(rightType))\n\t\t\t\treturn leftType;\n\t\t\tif (isVec3Type(leftType) && isVec3Type(rightType)) // dot product\n\t\t\t\treturn prg.types.Double();\n\n\t\t\t// mat2 * type\n\t\t\tif (isMat2Type(leftType) && isScalarType(rightType))\n\t\t\t\treturn leftType;\n\t\t\tif (isMat2Type(leftType) && isVec2Type(rightType))\n\t\t\t\treturn rightType;\n\t\t\tif (isMat2Type(leftType) && isMat2Type(rightType))\n\t\t\t\treturn leftType;\n\n\t\t\t// mat3 * type\n\t\t\tif (isMat3Type(leftType) && isScalarType(rightType))\n\t\t\t\treturn leftType;\n\t\t\tif (isMat3Type(leftType) && isVec3Type(rightType))\n\t\t\t\treturn rightType;\n\t\t\tif (isMat3Type(leftType) && isMat3Type(rightType))\n\t\t\t\treturn leftType;\n\t\t}\n\t}\n\tif (auto c = dynamic_cast<CallExpr*>(expr))\n\t{\n\t\tstd::vector<Type> args;\n\t\tfor (auto& arg : c->arguments)\n\t\t\targs.push_back(expressionType(arg.get()));\n\n\t\tint index = prg.resolveFunction(c->name, args);\n\t\tif (index >=0 )\n\t\t{\n\t\t\tFunctionInfo& func = prg.functions[index];\n\t\t\treturn func.returnType;\n\t\t}\n\t}\n\tif (auto i = dynamic_cast<ConstructorExpr*>(expr))\n\t{\n\t\treturn i->valType;\n\t}\n\tthrow std::runtime_error(\"Unsupported expression type in expressionType\");\n}\n\nvoid Compiler::compileVarDecl(VarDeclStmt* decl)\n{\n\tType baseType = decl->type;\n\tfor (auto& var : decl->vars)\n\t{\n\t\tType type = baseType;\n\t\tif (var.arraySizes.size() > 0)\n\t\t{\n\t\t\ttype = prg.types.getArrayType(baseType, var.arraySizes);\n\t\t}\n\n\t\tif (!decl->input && var.initializer)\n\t\t{\n\t\t\tType initType = coerce(compileExpression(var.initializer.get()), type);\n\t\t}\n\n\t\tif (m_scopeDepth == 0)\n\t\t{\n\t\t\tif (decl->input)\n\t\t\t\tprg.addInput(var.name, type);\n\t\t\telse\n\t\t\t{\n\t\t\t\tprg.addGlobal(var.name, type);\n\n\t\t\t\tif (var.initializer)\n\t\t\t\t{\n\t\t\t\t\tauto it = prg.globalIndices.find(var.name);\n\n\t\t\t\t\tProgram::Global& global = prg.globals[it->second];\n\n\t\t\t\t\tswitch (type->kind)\n\t\t\t\t\t{\n\t\t\t\t\tcase TypeKind::Bool  : emit(OpCode::SET_GLOBAL_BOOL  ); break;\n\t\t\t\t\tcase TypeKind::Int   : emit(OpCode::SET_GLOBAL_INT   ); break;\n\t\t\t\t\tcase TypeKind::Double: emit(OpCode::SET_GLOBAL_DOUBLE); break;\n\t\t\t\t\tcase TypeKind::Vec2  : emit(OpCode::SET_GLOBAL_VEC2  ); break;\n\t\t\t\t\tcase TypeKind::Vec3  : emit(OpCode::SET_GLOBAL_VEC3  ); break;\n\t\t\t\t\tcase TypeKind::Mat2  : emit(OpCode::SET_GLOBAL_MAT2  ); break;\n\t\t\t\t\tcase TypeKind::Mat3  : emit(OpCode::SET_GLOBAL_MAT3  ); break;\n\t\t\t\t\tcase TypeKind::Array :\n\t\t\t\t\t\temit(OpCode::SET_GLOBAL_ARRAY);\n\t\t\t\t\t\temitUint8((uint8_t)type->size());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TypeKind::Struct:\n\t\t\t\t\t\temit(OpCode::SET_GLOBAL_STRUCT);\n\t\t\t\t\t\temitUint8((uint8_t)type->size());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow std::runtime_error(\"Unsupported global variable type\");\n\t\t\t\t\t}\n\n\t\t\t\t\temitUint8((uint8_t)global.slot);\n\n\t\t\t\t\tswitch (type->kind)\n\t\t\t\t\t{\n\t\t\t\t\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\t\t\t\t\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\t\t\t\t\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\t\t\t\t\tcase TypeKind::Vec2  : emit(OpCode::POP_VEC2  ); break;\n\t\t\t\t\tcase TypeKind::Vec3  : emit(OpCode::POP_VEC3  ); break;\n\t\t\t\t\tcase TypeKind::Mat2  : emit(OpCode::POP_MAT2  ); break;\n\t\t\t\t\tcase TypeKind::Mat3  : emit(OpCode::POP_MAT3  ); break;\n\t\t\t\t\tcase TypeKind::Array :\n\t\t\t\t\t\tif (type->size() > 255)\n\t\t\t\t\t\t\tthrow std::runtime_error(\"Array size exceeds maximum of 255.\");\n\t\t\t\t\t\temit(OpCode::POP_ARRAY, type->size());\n\t\t\t\t\t\temitUint8((uint8_t)type->size());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TypeKind::Struct:\n\t\t\t\t\t\temit(OpCode::POP_STRUCT, type->size());\n\t\t\t\t\t\temitUint8((uint8_t)type->typeIndex);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow std::runtime_error(\"Unsupported global variable type\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// make sure the variable isn't already declared in this scope\n\t\t\tfor (int i = (int)m_locals.size() - 1; i >= 0; --i)\n\t\t\t{\n\t\t\t\tif (m_locals[i].depth < m_scopeDepth)\n\t\t\t\t\tbreak;\n\t\t\t\tif (m_locals[i].name == var.name)\n\t\t\t\t\tthrow std::runtime_error(\"Variable '\" + var.name + \"' is already declared in this scope.\");\n\t\t\t}\n\n\t\t\tm_locals.push_back({ var.name, type, m_scopeDepth, localStackSize });\n\n\t\t\tlocalStackSize += (int)type->size();\n\t\t}\n\t}\n}\n\n//\n// ===== If / While =====\n//\n\nvoid Compiler::compileIf(IfStmt* stmt)\n{\n\tType type = compileExpression(stmt->condition.get());\n\tif (!isScalarType(type) && !isBoolType(type))\n\t\tthrow std::runtime_error(\"Condition expression must be of type bool.\");\n\n\tint thenJump = emitJump(OpCode::JUMP_IF_FALSE);\n\n\tswitch (type->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported condition type in while statement\");\n\t}\n\n\tcompileStatement(stmt->thenBranch.get());\n\n\tint elseJump = emitJump(OpCode::JUMP);\n\n\tpatchJump(thenJump);\n\n\tswitch (type->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported condition type in while statement\");\n\t}\n\n\tif (stmt->elseBranch)\n\t\tcompileStatement(stmt->elseBranch.get());\n\n\tpatchJump(elseJump);\n}\n\nvoid Compiler::compileWhile(WhileStmt* stmt)\n{\n\tint loopStart = (int)prg.code.size();\n\n\tType type = compileExpression(stmt->condition.get());\n\tif (!isLogicalType(type))\n\t\tthrow std::runtime_error(\"Condition expression must be of a numeric type or bool.\");\n\n\tint exitJump = emitJump(OpCode::JUMP_IF_FALSE);\n\temit(OpCode::POP_BOOL);\n\tcompileStatement(stmt->body.get());\n\n\temitLoop(loopStart);\n\n\tpatchJump(exitJump);\n\temit(OpCode::POP_BOOL);\n}\n\nvoid Compiler::compileFor(ForStmt* stmt)\n{\n\tif (stmt->initializer) compileStatement(stmt->initializer.get());\n\n\tint loopStart = (int)prg.code.size();\n\tType type = compileExpression(stmt->condition.get());\n\tif (!isScalarType(type) && type != prg.types.Bool())\n\t\tthrow std::runtime_error(\"Condition expression must be of a numeric type or bool.\");\n\n\tint exitJump = emitJump(OpCode::JUMP_IF_FALSE);\n\temit(OpCode::POP_BOOL);\n\n\tcompileStatement(stmt->body.get());\n\tType incrType = compileExpression(stmt->increment.get());\n\tpop(incrType);\n\n\temitLoop(loopStart);\n\n\tpatchJump(exitJump);\n\temit(OpCode::POP_BOOL);\n}\n\nvoid Compiler::compileReturn(ReturnStmt* stmt)\n{\n\tif (stmt->value)\n\t{\n\t\tType returnType = expectedReturnType;\n\t\treturnType = compileExpression(stmt->value.get());\n\n\t\tif (expectedReturnType)\n\t\t{\n\t\t\tType coerceType = coerce(returnType, expectedReturnType);\n\t\t\tif (coerceType)\n\t\t\t\treturnType = coerceType;\n\n\t\t\tif (returnType != expectedReturnType)\n\t\t\t\tthrow std::runtime_error(\"Return type mismatch. Expected \" + TypeToString(expectedReturnType) + \" but got \" + TypeToString(returnType));\n\t\t}\n\n\t\tswitch (returnType->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::RETURN_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::RETURN_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::RETURN_DOUBLE); break;\n\t\tcase TypeKind::Vec2  : emit(OpCode::RETURN_VEC2  ); break;\n\t\tcase TypeKind::Vec3  : emit(OpCode::RETURN_VEC3  ); break;\n\t\tcase TypeKind::Mat2  : emit(OpCode::RETURN_MAT2  ); break;\n\t\tcase TypeKind::Mat3  : emit(OpCode::RETURN_MAT3  ); break;\n\t\tcase TypeKind::Array : \n\t\t\temit(OpCode::RETURN_ARRAY ); \n\t\t\temitUint8(returnType->typeIndex);\n\t\t\tbreak;\n\t\tcase TypeKind::Struct: \n\t\t\temit(OpCode::RETURN_STRUCT); \n\t\t\temitUint8(returnType->typeIndex);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported return type\");\n\t\t};\n\t}\n\telse\n\t{\n\t\tif (expectedReturnType != nullptr && expectedReturnType != prg.types.Void())\n\t\t\tthrow std::runtime_error(\"Missing return value in function with non-void return type.\");\n\n\t\t// return without value -> push monostate\n\t\temit(OpCode::PUSH_VOID);\n\t\temit(OpCode::RETURN_VOID);\n\t}\n\n\t// Mark that a return statement was encountered. \n\t// This is used to determine whether we need to emit an implicit return at the end of a function/program.\n\thasReturn = true;\n}\n\nvoid Compiler::compileStruct(StructStmt* stmt)\n{\n\t// nothing to do here since struct definitions are handled during parsing and type registration.\n}\n\n//\n// ===== Functions =====\n//\n\nvoid Compiler::compileFunction(FunctionStmt* fn)\n{\n\t// Emit jump over function body\n\tint jumpOver = emitJump(OpCode::JUMP);\n\n\tstd::vector<Type> argTypes;\n\tfor (auto& p : fn->params)\n\t\targTypes.push_back(p.first);\n\n\tint fnIndex = prg.resolveFunction(fn->name, argTypes);\n\tif (fnIndex < 0)\n\t\tthrow std::runtime_error(\"Compiler error: function not found after resolution.\");\n\n\tFunctionInfo& info = prg.functions[fnIndex];\n\tinfo.entry = prg.code.size();\n\tcurrentFunction = fnIndex;\n\n\tType currentReturnType = expectedReturnType;\n\texpectedReturnType = fn->returnType;\n\tbool hasReturnBefore = hasReturn;\n\thasReturn = false;\n\n\tbeginScope();\n\n\tfor (auto& p : fn->params)\n\t{\n\t\tm_locals.push_back({ p.second, p.first, m_scopeDepth, localStackSize });\n\t\tlocalStackSize += (int)p.first->size();\n\t\tinfo.argSize += (int)p.first->size();\n\t\tstackDepth += (int)p.first->size();\n\t}\n\n\tsize_t currentStackSize = stackDepth;\n\n\tBlockStmt* body = dynamic_cast<BlockStmt*>(fn->body.get());\n\tfor (auto& stmt : body->statements)\n\t\tcompileStatement(stmt.get());\n\n\tif (!hasReturn)\n\t{\n\t\tif (fn->returnType != nullptr && fn->returnType != prg.types.Void())\n\t\t\tthrow std::runtime_error(\"Missing return statement in function with non-void return type.\");\n\n\t\temit(OpCode::RETURN_VOID);\n\t}\n\n\t// We don't call endScope() here since the function body must end with a return, this will never pop the function's locals. \n\t// Instead, just clear locals and reset the scope depth.\n\twhile (!m_locals.empty() && m_locals.back().depth == m_scopeDepth)\n\t{\n\t\tLocal& local = m_locals.back();\n\t\tswitch (local.type->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\t\tcase TypeKind::Vec2  : emit(OpCode::POP_VEC2  ); break;\n\t\tcase TypeKind::Vec3  : emit(OpCode::POP_VEC3  ); break;\n\t\tcase TypeKind::Mat2  : emit(OpCode::POP_MAT2  ); break;\n\t\tcase TypeKind::Mat3  : emit(OpCode::POP_MAT3  ); break;\n\t\tcase TypeKind::Array : \n\t\t\tif (local.type->size() > 255)\n\t\t\t\tthrow std::runtime_error(\"Array size exceeds maximum of 255.\");\n\t\t\temit(OpCode::POP_ARRAY, local.type->size()); \n\t\t\temitUint8(local.type->size());\n\t\t\tbreak;\n\t\tcase TypeKind::Struct:\n\t\t\temit(OpCode::POP_STRUCT, local.type->size());\n\t\t\temitUint8(local.type->typeIndex);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported local variable type\");\n\t\t\tbreak;\n\t\t}\n\t\tm_locals.pop_back();\n\t\tlocalStackSize -= (int)local.type->size();\n\t}\n\tm_scopeDepth--;\n\n\t// Patch jump so execution skips function body\n\tpatchJump(jumpOver);\n\n\texpectedReturnType = currentReturnType;\n\thasReturn = hasReturnBefore;\n\n\tinfo.maxStackSize = maxStackDepth - currentStackSize;\n\n\tcurrentFunction = -1;\n}\n\n//\n// ===== Expressions =====\n//\n\nType Compiler::compileExpression(Expression* expr)\n{\n\tType type;\n\tif      (auto b = dynamic_cast<BinaryExpr*     >(expr)) type = compileBinary(b);\n\telse if (auto u = dynamic_cast<UnaryExpr*      >(expr)) type = compileUnary(u);\n\telse if (auto l = dynamic_cast<LiteralExpr*    >(expr)) type = compileLiteral(l);\n\telse if (auto v = dynamic_cast<VariableExpr*   >(expr)) type = compileVariable(v);\n\telse if (auto a = dynamic_cast<AssignExpr*     >(expr)) type = compileAssign(a);\n\telse if (auto c = dynamic_cast<CallExpr*       >(expr)) type = compileCall(c);\n\telse if (auto m = dynamic_cast<MemberExpr*     >(expr)) type = compileMember(m);\n\telse if (auto s = dynamic_cast<IndexExpr*      >(expr)) type = compileIndex(s);\n\telse if (auto i = dynamic_cast<InitExpr*       >(expr)) type = compileInitializer(i);\n\telse if (auto c = dynamic_cast<ConstructorExpr*>(expr)) type = compileConstructor(c);\n\telse\n\t\tthrow std::runtime_error(\"Unsupported expression type\");\n\n\treturn type;\n}\n\nType Compiler::compileLiteral(LiteralExpr* expr)\n{\n\tType type = prg.types.getBuiltinType(expr->value);\n\tswitch (type->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::PUSH_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::PUSH_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::PUSH_DOUBLE); break;\n\tcase TypeKind::Vec2  : emit(OpCode::PUSH_VEC2  ); break;\n\tcase TypeKind::Vec3  : emit(OpCode::PUSH_VEC3  ); break;\n\tcase TypeKind::Mat2  : emit(OpCode::PUSH_MAT2  ); break;\n\tcase TypeKind::Mat3  : emit(OpCode::PUSH_MAT3  ); break;\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported literal type\");\n\t}\n\n\tuint8_t idx = addConstant(expr->value);\n\temitUint8(idx);\n\treturn type;\n}\n\nType Compiler::compileVariable(VariableExpr* expr)\n{\n\tint local = resolveLocal(expr->name);\n\tif (local != -1)\n\t{\n\t\tLocal& localInfo = m_locals[local];\n\t\tType type = localInfo.type;\n\n\t\tswitch (type->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::GET_LOCAL_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::GET_LOCAL_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::GET_LOCAL_DOUBLE); break;\n\t\tcase TypeKind::Vec2  : emit(OpCode::GET_LOCAL_VEC2  ); break;\n\t\tcase TypeKind::Vec3  : emit(OpCode::GET_LOCAL_VEC3  ); break;\n\t\tcase TypeKind::Mat2  : emit(OpCode::GET_LOCAL_MAT2  ); break;\n\t\tcase TypeKind::Mat3  : emit(OpCode::GET_LOCAL_MAT3  ); break;\n\t\tcase TypeKind::Array:\n\t\t{\n\t\t\temit(OpCode::GET_LOCAL_ARRAY, type->size()); \n\t\t\temitUint8(type->size());\n\t\t\tbreak;\n\t\t}\n\t\tcase TypeKind::Struct:\n\t\t{\n\t\t\temit(OpCode::GET_LOCAL_STRUCT, type->size());\n\t\t\temitUint8(type->size());\n\t\t\tbreak;\n\t\t}\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported local variable type\");\n\t\t}\n\n\t\temitUint8(localInfo.slot);\n\n\t\treturn type;\n\t}\n\n\tint globalIndex = resolveGlobal(expr->name);\n\tif (globalIndex < 0)\n\t\tthrow std::runtime_error(\"Undefined variable: \" + expr->name);\n\n\tProgram::Global& glob = prg.globals[globalIndex];\n\n\tglob.refcount++;\n\n\tswitch (glob.type->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::GET_GLOBAL_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::GET_GLOBAL_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::GET_GLOBAL_DOUBLE); break;\n\tcase TypeKind::Vec2  : emit(OpCode::GET_GLOBAL_VEC2  ); break;\n\tcase TypeKind::Vec3  : emit(OpCode::GET_GLOBAL_VEC3  ); break;\n\tcase TypeKind::Mat2  : emit(OpCode::GET_GLOBAL_MAT2  ); break;\n\tcase TypeKind::Mat3  : emit(OpCode::GET_GLOBAL_MAT3  ); break;\n\tcase TypeKind::Array:\n\t{\n\t\temit(OpCode::GET_GLOBAL_ARRAY, glob.type->size()); \n\t\temitUint8(glob.type->size());\n\t\tbreak;\n\t}\n\tcase TypeKind::Struct:\n\t{\n\t\temit(OpCode::GET_GLOBAL_STRUCT, glob.type->size());\n\t\temitUint8(glob.type->size());\n\t\tbreak;\n\t}\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported global variable type\");\n\t}\n\n\temitUint8(glob.slot);\n\treturn glob.type;\n}\n\nType Compiler::compileAssign(AssignExpr* expr)\n{\n\tType l_type = compileLValue(expr->target.get());\n\tType r_type = coerce(compileExpression(expr->value.get()), l_type);\n\n\tswitch (l_type->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::STORE_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::STORE_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::STORE_DOUBLE); break;\n\tcase TypeKind::Vec2  : emit(OpCode::STORE_VEC2  ); break;\n\tcase TypeKind::Vec3  : emit(OpCode::STORE_VEC3  ); break;\n\tcase TypeKind::Mat2  : emit(OpCode::STORE_MAT2  ); break;\n\tcase TypeKind::Mat3  : emit(OpCode::STORE_MAT3  ); break;\n\tcase TypeKind::Array:\n\t{\n\t\temit(OpCode::STORE_ARRAY);\n\t\temitUint8(l_type->size());\n\t\tbreak;\n\t}\n\tcase TypeKind::Struct:\n\t{\n\t\temit(OpCode::STORE_STRUCT);\n\t\temitUint8(l_type->size());\n\t\tbreak;\n\t}\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported type in assignment\");\n\t\tbreak;\n\t}\n\n\treturn l_type;\n}\n\nType Compiler::compileLValue(Expression* expr)\n{\n\tif (auto var = dynamic_cast<VariableExpr*>(expr))\n\t{\n\t\treturn compileVariableRef(var);\n\t}\n\n\tif (auto member = dynamic_cast<MemberExpr*>(expr))\n\t{\n\t\treturn compileMemberRef(member);\n\t}\n\n\tif (auto index = dynamic_cast<IndexExpr*>(expr))\n\t{\n\t\treturn compileIndexRef(index);\n\t}\n\n\tthrow std::runtime_error(\"Invalid assignment target.\");\n}\n\nType Compiler::compileVariableRef(VariableExpr* expr)\n{\n\tType returnType = nullptr;\n\n\tint index = resolveLocal(expr->name);\n\tif (index != -1)\n\t{\n\t\treturnType = m_locals[index].type;\n\n\t\temit(OpCode::GET_LOCAL_REF);\n\t\temitUint8((uint8_t)m_locals[index].slot);\n\t}\n\telse\n\t{\n\t\tindex = resolveGlobal(expr->name);\n\t\tif (index < 0)\n\t\t\tthrow std::runtime_error(\"Undefined variable: \" + expr->name);\n\n\t\tif (prg.globals[index].immutable)\n\t\t\tthrow std::runtime_error(\"Cannot assign to immutable global variable: \" + expr->name);\n\n\t\treturnType = prg.globals[index].type;\n\n\t\temit(OpCode::GET_GLOBAL_REF);\n\t\temitUint8((uint8_t)prg.globals[index].slot);\n\t}\n\n\treturn returnType;\n}\n\nType Compiler::compileMemberRef(MemberExpr* expr)\n{\n\tType objectType = compileLValue(expr->object.get());\n\tif (isStructType(objectType))\n\t{\n\t\tint memberIndex  = resolveMember(objectType, expr->property);\n\t\tint memberOffset = resolveMemberOffset(objectType, expr->property);\n\n\t\temit(OpCode::GET_MEMBER_REF);\n\t\temitUint8((uint8_t)memberOffset);\n\n\t\treturn memberType(objectType, memberIndex);\n\t}\n\telse if (isVec2Type(objectType))\n\t{\n\t\tif (expr->property == \"x\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC2_X_REF);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse if (expr->property == \"y\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC2_Y_REF);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse\n\t\t\tthrow std::runtime_error(\"Vec2 has no member named '\" + expr->property + \"'.\");\n\t}\n\telse if (isVec3Type(objectType))\n\t{\n\t\tif (expr->property == \"x\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC3_X_REF);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse if (expr->property == \"y\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC3_Y_REF);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse if (expr->property == \"z\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC3_Z_REF);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse\n\t\t\tthrow std::runtime_error(\"Vec3 has no member named '\" + expr->property + \"'.\");\n\t}\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Left-hand side of member access must be a struct or vector.\");\n\t}\n}\n\nint Compiler::resolveMember(Type type, const std::string& member)\n{\n\tassert(type->kind == TypeKind::Struct);\n\tfor (size_t i = 0; i < type->fields.size(); ++i)\n\t{\n\t\tif (type->fields[i].second == member)\n\t\t\treturn (int)i;\n\t}\n\tthrow std::runtime_error(\"Struct '\" + TypeToString(type) + \"' has no member named '\" + member + \"'.\");\n}\n\nint Compiler::resolveMemberOffset(Type type, const std::string& member)\n{\n\tassert(type->kind == TypeKind::Struct);\n\tint offset = 0;\n\tfor (size_t i = 0; i < type->fields.size(); ++i)\n\t{\n\t\tif (type->fields[i].second == member)\n\t\t\treturn offset;\n\t\toffset += (int)type->fields[i].first->size();\n\t}\n\tthrow std::runtime_error(\"Struct '\" + TypeToString(type) + \"' has no member named '\" + member + \"'.\");\n}\n\nType Compiler::memberType(Type type, int memberIndex)\n{\n\tassert(type->kind == TypeKind::Struct);\n\tif (memberIndex < 0 || memberIndex >= (int)type->fields.size())\n\t\tthrow std::runtime_error(\"Invalid member index for struct '\" + TypeToString(type) + \"'.\");\n\treturn type->fields[memberIndex].first;\n}\n\nType Compiler::compileIndexRef(IndexExpr* expr)\n{\n\tType objectType = compileLValue(expr->object.get());\n\n\tif (objectType->kind != TypeKind::Array)\n\t\tthrow std::runtime_error(\"Left-hand side of index access must be an array.\");\n\n\tcompileExpression(expr->index.get());\n\n\tswitch (objectType->elementType->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::GET_INDEX_REF_BOOL  ); break;\n\tcase TypeKind::Int   : emit(OpCode::GET_INDEX_REF_INT   ); break;\n\tcase TypeKind::Double: emit(OpCode::GET_INDEX_REF_DOUBLE); break;\n\tcase TypeKind::Vec2  : emit(OpCode::GET_INDEX_REF_VEC2  ); break;\n\tcase TypeKind::Vec3  : emit(OpCode::GET_INDEX_REF_VEC3  ); break;\n\tcase TypeKind::Mat2  : emit(OpCode::GET_INDEX_REF_MAT2  ); break;\n\tcase TypeKind::Mat3  : emit(OpCode::GET_INDEX_REF_MAT3  ); break;\n\tcase TypeKind::Array : \n\tcase TypeKind::Struct: \n\t\temit(OpCode::GET_INDEX_REF);\n\t\temitUint8(objectType->elementType->size());\n\t\tbreak;\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported array element type in index access\");\n\t}\n\n\treturn objectType->elementType;\n}\n\n//\n// ===== Binary (includes &&, ||) =====\n//\n\nType Compiler::compileBinary(BinaryExpr* expr)\n{\n\tBinaryOp op = expr->op;\n\n\t// Short-circuit AND\n\tif (op == BinaryOp::AndAnd)\n\t{\n\t\tType type = compileExpression(expr->left.get());\n\n\t\tif (!isScalarType(type) && !isBoolType(type))\n\t\t\tthrow std::runtime_error(\"Cannot convert left operand of '&&' to boolean.\");\n\n\t\tint endJump = emitJump(OpCode::JUMP_IF_FALSE);\n\n\t\tswitch (type->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported condition type in '&&' operator\");\n\t\t}\n\n\t\ttype = compileExpression(expr->right.get());\n\t\tif (!isScalarType(type) && !isBoolType(type))\n\t\t\tthrow std::runtime_error(\"Cannot convert right operand of '&&' to boolean.\");\n\n\t\tpatchJump(endJump);\n\t\treturn prg.types.Bool();\n\t}\n\n\t// Short-circuit OR\n\tif (op == BinaryOp::OrOr)\n\t{\n\t\tType type = compileExpression(expr->left.get());\n\n\t\tif (!isScalarType(type) && !isBoolType(type))\n\t\t\tthrow std::runtime_error(\"Cannot convert left operand of '||' to boolean.\");\n\n\t\tint endJump = emitJump(OpCode::JUMP_IF_TRUE);\n\t\tswitch (type->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::POP_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::POP_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::POP_DOUBLE); break;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported condition type in || operator\");\n\t\t}\n\n\t\ttype = compileExpression(expr->right.get());\n\t\tif (!isScalarType(type) && !isBoolType(type))\n\t\t\tthrow std::runtime_error(\"Cannot convert right operand of '||' to boolean.\");\n\n\t\tpatchJump(endJump);\n\t\treturn prg.types.Bool();\n\t}\n\n\t// Handle special cases first\n\t// see if both expressions are global variables\n\tif (isVariable(expr->left) && isVariable(expr->right))\n\t{\n\t\tint globA = resolveGlobal(dynamic_cast<VariableExpr*>(expr->left.get())->name);\n\t\tint globB = resolveGlobal(dynamic_cast<VariableExpr*>(expr->right.get())->name);\n\n\t\tif ((globA >= 0) && (globB >= 0))\n\t\t{\n\t\t\tProgram::Global& a = prg.globals[globA];\n\t\t\tProgram::Global& b = prg.globals[globB];\n\n\t\t\tif (isMat3Type(a.type) && isMat3Type(b.type))\n\t\t\t{\n\t\t\t\tswitch (op)\n\t\t\t\t{\n\t\t\t\tcase BinaryOp::Plus    : emit(OpCode::ADD_GLOBAL_MAT3); break;\n\t\t\t\tcase BinaryOp::Minus   : emit(OpCode::SUB_GLOBAL_MAT3); break;\n\t\t\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_GLOBAL_MAT3); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported binary op for mat3 type.\");\n\t\t\t\t}\n\n\t\t\t\temitUint8((uint8_t)a.slot);\n\t\t\t\temitUint8((uint8_t)b.slot);\n\t\t\t\treturn a.type;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tType type_l = compileExpression(expr->left.get());\n\n\tif (isDoubleType(type_l) && isLiteral(expr->right) && (expr->op == BinaryOp::Exponent))\n\t{\n\t\tValue e = dynamic_cast<LiteralExpr*>(expr->right.get())->value;\n\t\tif (isDouble(e))\n\t\t{\n\t\t\t// If the exponent is a literal, we can optimize by using a fast exponentiation algorithm.\n\t\t\tdouble exponentValue = e.d;\n\t\t\tif (exponentValue == 0.5)\n\t\t\t{\n\t\t\t\temit(OpCode::SQRT_DOUBLE);\n\t\t\t\treturn type_l;\n\t\t\t}\n\t\t\telse if (exponentValue == 2.0)\n\t\t\t{\n\t\t\t\temit(OpCode::SQR_DOUBLE);\n\t\t\t\treturn type_l;\n\t\t\t}\n\t\t}\n\t\telse if (isInt(e))\n\t\t{\n\t\t\tint exponentValue = e.i;\n\t\t\tif (exponentValue == 2)\n\t\t\t{\n\t\t\t\temit(OpCode::SQR_DOUBLE);\n\t\t\t\treturn type_l;\n\t\t\t}\n\t\t}\n\t}\n\n\tType type_r = compileExpression(expr->right.get());\n\tType t = commonType(type_l, type_r);\n\tif (t)\n\t{\n\t\ttype_l = t;\n\t\ttype_r = t;\n\t}\n\n\tif (isBoolType(type_l) && isBoolType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::EqualEqual: emit(OpCode::EQUAL_BOOL); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::NotEqual  : emit(OpCode::NEQ_BOOL  ); type_l = prg.types.Bool(); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for bool type.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\tif (isIntType(type_l) && isIntType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Plus    : emit(OpCode::ADD_INT); break;\n\t\tcase BinaryOp::Minus   : emit(OpCode::SUB_INT); break;\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_INT); break;\n\t\tcase BinaryOp::Divide  : emit(OpCode::DIV_INT); break;\n\t\tcase BinaryOp::Exponent: emit(OpCode::EXP_INT); break;\n\t\tcase BinaryOp::Greater     : emit(OpCode::GT_INT   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::Less        : emit(OpCode::LT_INT   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::GreaterEqual: emit(OpCode::GE_INT   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::LessEqual   : emit(OpCode::LE_INT   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::EqualEqual  : emit(OpCode::EQUAL_INT); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::NotEqual    : emit(OpCode::NEQ_INT  ); type_l = prg.types.Bool(); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for int type.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\tif (isDoubleType(type_l) && isDoubleType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Plus    : emit(OpCode::ADD_DOUBLE); break;\n\t\tcase BinaryOp::Minus   : emit(OpCode::SUB_DOUBLE); break;\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_DOUBLE); break;\n\t\tcase BinaryOp::Divide  : emit(OpCode::DIV_DOUBLE); break;\n\t\tcase BinaryOp::Exponent: emit(OpCode::EXP_DOUBLE); break;\n\t\tcase BinaryOp::Greater     : emit(OpCode::GT_DOUBLE   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::Less        : emit(OpCode::LT_DOUBLE   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::GreaterEqual: emit(OpCode::GE_DOUBLE   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::LessEqual   : emit(OpCode::LE_DOUBLE   ); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::EqualEqual  : emit(OpCode::EQUAL_DOUBLE); type_l = prg.types.Bool(); break;\n\t\tcase BinaryOp::NotEqual    : emit(OpCode::NEQ_DOUBLE  ); type_l = prg.types.Bool(); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for double type.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\t// vec2 operations\n\tif (isVec2Type(type_l) && isVec2Type(type_r))\n\t{\n\t\tType returnType = type_l; // default return type is vec2, but some ops like dot product will return double\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Plus    : emit(OpCode::ADD_VEC2); break;\n\t\tcase BinaryOp::Minus   : emit(OpCode::SUB_VEC2); break;\n\t\tcase BinaryOp::Multiply: emit(OpCode::DOT_VEC2); returnType = prg.types.Double(); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for vec2 type.\");\n\t\t}\n\t\treturn returnType;\n\t}\n\n\t// vec2 op scalar\n\tif (isVec2Type(type_l) && isScalarType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_VEC2_DOUBLE); break;\n\t\tcase BinaryOp::Divide : emit(OpCode::DIV_VEC2_DOUBLE); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for vec2 and double types.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\t// scalar * vec2\n\tif (isScalarType(type_l) && isVec2Type(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_DOUBLE_VEC2); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for double and vec2 types.\");\n\t\t}\n\t\treturn type_r;\n\t}\n\n\t// vec3 operations\n\tif (isVec3Type(type_l) && isVec3Type(type_r))\n\t{\n\t\tType returnType = type_l; // default return type is vec3, but some ops like dot product will return double\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Plus : emit(OpCode::ADD_VEC3); break;\n\t\tcase BinaryOp::Minus: emit(OpCode::SUB_VEC3); break;\n\t\tcase BinaryOp::Multiply: emit(OpCode::DOT_VEC3); returnType = prg.types.Double(); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for vec3 type.\");\n\t\t}\n\t\treturn returnType;\n\t}\n\n\t// vec3 op scalar\n\tif (isVec3Type(type_l) && isScalarType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_VEC3_DOUBLE); break;\n\t\tcase BinaryOp::Divide  : emit(OpCode::DIV_VEC3_DOUBLE); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for vec3 and double types.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\t// scalar * vec3\n\tif (isScalarType(type_l) && isVec3Type(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_DOUBLE_VEC3); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for double and vec3 types.\");\n\t\t}\n\t\treturn type_r;\n\t}\n\n\t// mat2 operations\n\tif (isMat2Type(type_l) && isMat2Type(type_r))\n\t{\n\t\tType returnType = type_l;\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Plus    : emit(OpCode::ADD_MAT2); break;\n\t\tcase BinaryOp::Minus   : emit(OpCode::SUB_MAT2); break;\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_MAT2); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for mat2 type.\");\n\t\t}\n\t\treturn returnType;\n\t}\n\n\t// mat2 * vec2\n\tif (isMat2Type(type_l) && isVec2Type(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_MAT2_VEC2); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for mat2 and vec2 types.\");\n\t\t}\n\t\treturn type_r;\n\t}\n\n\t// mat2 op scalar\n\tif (isMat2Type(type_l) && isScalarType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_MAT2_DOUBLE); break;\n\t\tcase BinaryOp::Divide  : emit(OpCode::DIV_MAT2_DOUBLE); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for mat2 and double types.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\t// scalar * mat2\n\tif (isScalarType(type_l) && isMat2Type(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_DOUBLE_MAT2); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for double and mat2 types.\");\n\t\t}\n\t\treturn type_r;\n\t}\n\n\t// mat3 operations\n\tif (isMat3Type(type_l) && isMat3Type(type_r))\n\t{\n\t\tType returnType = type_l;\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Plus    : emit(OpCode::ADD_MAT3); break;\n\t\tcase BinaryOp::Minus   : emit(OpCode::SUB_MAT3); break;\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_MAT3); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for mat3 type.\");\n\t\t}\n\t\treturn returnType;\n\t}\n\n\t// mat3 * scalar\n\tif (isMat3Type(type_l) && isScalarType(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_MAT3_DOUBLE); break;\n\t\tcase BinaryOp::Divide  : emit(OpCode::DIV_MAT3_DOUBLE); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for mat3 and double types.\");\n\t\t}\n\t\treturn type_l;\n\t}\n\n\t// scalar * mat3\n\tif (isScalarType(type_l) && isMat3Type(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_DOUBLE_MAT3); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for double and mat3 types.\");\n\t\t}\n\t\treturn type_r;\n\t}\n\n\t// mat3 * vec3\n\tif (isMat3Type(type_l) && isVec3Type(type_r))\n\t{\n\t\tswitch (op)\n\t\t{\n\t\tcase BinaryOp::Multiply: emit(OpCode::MUL_MAT3_VEC3); break;\n\t\tdefault: throw std::runtime_error(\"Unsupported binary op for mat3 and vec3 types.\");\n\t\t}\n\t\treturn type_r;\n\t}\n\n\tthrow std::runtime_error(\"Compiler error: Unsupported binary operator '\" + opToString(op) + \"' with operand types '\" + TypeToString(type_l) + \"' and '\" + TypeToString(type_r) + \"'.\");\n\n\treturn type_l;\n}\n\nType Compiler::compileUnary(UnaryExpr* expr)\n{\n\tType type = compileExpression(expr->right.get());\n\tswitch (expr->op)\n\t{\n\tcase UnaryOp::Negate:\n\t{\n\t\tif      (isIntType   (type)) emit(OpCode::NEG_INT);\n\t\telse if (isDoubleType(type)) emit(OpCode::NEG_DOUBLE);\n\t\telse if (isVec2Type  (type)) emit(OpCode::NEG_VEC2);\n\t\telse if (isVec3Type  (type)) emit(OpCode::NEG_VEC3);\n\t\telse if (isMat2Type  (type)) emit(OpCode::NEG_MAT2);\n\t\telse if (isMat3Type  (type)) emit(OpCode::NEG_MAT3);\n\t\telse\n\t\t\tthrow std::runtime_error(\"Invalid operand type for unary '-'.\");\n\t\tbreak;\n\t}\n\tcase UnaryOp::Not:\n\t{\n\t\tif (isBoolType(type))\n\t\t{\n\t\t\temit(OpCode::NOT);\n\t\t\ttype = prg.types.Bool();\n\t\t}\n\t\telse\n\t\t\tthrow std::runtime_error(\"Invalid operand type for unary '!'.\");\n\t\tbreak;\n\t}\n\tdefault: throw std::runtime_error(\"Unsupported unary op\");\n\t}\n\treturn type;\n}\n\nbool Compiler::isNativeFunction(const std::string& name)\n{\n\tfor (int i = 0; i < (int)prg.functions.size(); ++i)\n\t{\n\t\tif (prg.functions[i].name == name)\n\t\t{\n\t\t\treturn prg.functions[i].isNative;\n\t\t}\n\t}\n\treturn false;\n}\n\nstd::vector<Type> Compiler::compileFncArgs(std::vector<std::unique_ptr<Expression>>& args)\n{\n\tstd::vector<Type> argTypes;\n\tfor (auto& arg : args)\n\t{\n\t\tType type = compileExpression(arg.get());\n\t\targTypes.push_back(type);\n\t}\n\treturn argTypes;\n}\n\nType Compiler::compileCall(CallExpr* call)\n{\n\t// don't copy args for native functions\n\tstd::vector<Type> argTypes = compileFncArgs(call->arguments);\n\tint fnIndex = prg.resolveFunction(call->name, argTypes);\n\tif (fnIndex == -1)\n\t\tthrow std::runtime_error(\"Undefined function: \" + call->name);\n\n\t// We don't allow recursive calls.\n\tif (currentFunction == fnIndex)\n\t\tthrow std::runtime_error(\"Recursive calls are not allowed: \" + call->name);\n\n\tconst FunctionInfo& fi = prg.functions[fnIndex];\n\n\t// check arg count\n\tif (fi.args.size() != (int)call->arguments.size())\n\t\tthrow std::runtime_error(\"Argument count mismatch in call to function: \" + call->name);\n\n\t// check arg types\n\tfor (int i = 0; i < argTypes.size(); ++i)\n\t{\n\t\tif (argTypes[i] != fi.args[i])\n\t\t\tthrow std::runtime_error(\"Argument type mismatch in call to function: \" + call->name);\n\t}\n\n\tstackDepth += (int)fi.maxStackSize;\n\n\t// calculate stack size needed for arguments\n\tint argStackSize = 0;\n\tfor (Type argType : argTypes)\n\t{\n\t\targStackSize += (int)argType->size();\n\t}\n\n\t// stack size needed for return value\n\tint returnStackSize = (int)prg.functions[fnIndex].returnType->size();\n\n\temit(OpCode::CALL, returnStackSize - argStackSize);\n\temitUint8(fnIndex);\n\temitUint8((uint8_t)call->arguments.size());\n\n\treturn fi.returnType;\n}\n\nType Compiler::compileMember(MemberExpr* expr)\n{\n\tType type = compileExpression(expr->object.get());\n\n\tif (isStructType(type))\n\t{\n\t\tauto it = std::find_if(type->fields.begin(), type->fields.end(),\n\t\t\t[&](const auto& field) { return field.second == expr->property; });\n\t\tif (it == type->fields.end())\n\t\t\tthrow std::runtime_error(\"Unknown property: \" + expr->property);\n\n\t\tstd::size_t index = it - type->fields.begin();\n\n\t\tswitch (it->first->kind)\n\t\t{\n\t\tcase TypeKind::Bool  : emit(OpCode::GET_PROPERTY_BOOL  ); break;\n\t\tcase TypeKind::Int   : emit(OpCode::GET_PROPERTY_INT   ); break;\n\t\tcase TypeKind::Double: emit(OpCode::GET_PROPERTY_DOUBLE); break;\n\t\tcase TypeKind::Vec2  : emit(OpCode::GET_PROPERTY_VEC2  ); break;\n\t\tcase TypeKind::Vec3  : emit(OpCode::GET_PROPERTY_VEC3  ); break;\n\t\tcase TypeKind::Mat2  : emit(OpCode::GET_PROPERTY_MAT2  ); break;\n\t\tcase TypeKind::Mat3  : emit(OpCode::GET_PROPERTY_MAT3  ); break;\n\t\tcase TypeKind::Array : emit(OpCode::GET_PROPERTY_ARRAY ); break;\n\t\tcase TypeKind::Struct: emit(OpCode::GET_PROPERTY_STRUCT); break;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported struct member type\");\n\t\t}\n\n\t\temitUint8((uint8_t)type->typeIndex);\n\t\temitUint8((uint8_t)index);\n\n\t\treturn it->first;\n\t}\n\telse if (isVec2Type(type))\n\t{\n\t\tif (expr->property == \"x\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC2_X);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse if (expr->property == \"y\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC2_Y);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstd::string swizzle = expr->property;\n\t\t\tif (((swizzle.size() == 2) || (swizzle.size() == 3)) &&\n\t\t\t\tswizzle.find_first_not_of(\"xy\") == std::string::npos)\n\t\t\t{\n\t\t\t\tuint8_t size = (uint8_t)swizzle.size();\n\t\t\t\tuint8_t mask = 0;\n\t\t\t\tfor (char c : swizzle)\n\t\t\t\t{\n\t\t\t\t\tswitch (c)\n\t\t\t\t\t{\n\t\t\t\t\tcase 'x': mask = (mask << 2) | 0b00; break;\n\t\t\t\t\tcase 'y': mask = (mask << 2) | 0b01; break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\temit(OpCode::GET_VEC2_SWIZZLE);\n\t\t\t\temitUint8(mask);\n\t\t\t\temitUint8(size);\n\t\t\t\treturn (size == 2 ? prg.types.Vec2() : prg.types.Vec3());\n\t\t\t}\n\t\t\telse\n\t\t\t\tthrow std::runtime_error(\"Unknown property: \" + expr->property);\n\t\t}\n\t}\n\telse if (isVec3Type(type))\n\t{\n\t\tif (expr->property == \"x\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC3_X);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse if (expr->property == \"y\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC3_Y);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse if (expr->property == \"z\")\n\t\t{\n\t\t\temit(OpCode::GET_VEC3_Z);\n\t\t\treturn prg.types.Double();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstd::string swizzle = expr->property;\n\t\t\tif (((swizzle.size() == 2) || (swizzle.size() == 3)) &&\n\t\t\t\tswizzle.find_first_not_of(\"xyz\") == std::string::npos)\n\t\t\t{\n\t\t\t\tuint8_t size = (uint8_t)swizzle.size();\n\t\t\t\tuint8_t mask = 0;\n\t\t\t\tfor (char c : swizzle)\n\t\t\t\t{\n\t\t\t\t\tswitch (c)\n\t\t\t\t\t{\n\t\t\t\t\tcase 'x': mask = (mask << 2) | 0b00; break;\n\t\t\t\t\tcase 'y': mask = (mask << 2) | 0b01; break;\n\t\t\t\t\tcase 'z': mask = (mask << 2) | 0b10; break;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\temit(OpCode::GET_VEC3_SWIZZLE);\n\t\t\t\temitUint8(mask);\n\t\t\t\temitUint8(size);\n\t\t\t\treturn (size == 2 ? prg.types.Vec2() : prg.types.Vec3());\n\t\t\t}\n\t\t\telse\n\t\t\t\tthrow std::runtime_error(\"Unknown property: \" + expr->property);\n\t\t}\n\t}\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Cannot access property of non-struct type.\");\n\t}\n}\n\nType Compiler::compileIndex(IndexExpr* expr)\n{\n\t// handle special cases first\n\tif (isVariable(expr->object))\n\t{\n\t\tint globIndex = resolveGlobal(dynamic_cast<VariableExpr*>(expr->object.get())->name);\n\t\tif (globIndex >= 0)\n\t\t{\n\t\t\tType globType = prg.globals[globIndex].type;\n\t\t\tif (isArrayType(globType) && isDoubleType(globType->elementType))\n\t\t\t{\n\t\t\t\tType indxType = compileExpression(expr->index.get());\n\t\t\t\tif (indxType->kind != TypeKind::Int)\n\t\t\t\t\tthrow std::runtime_error(\"array index must be an integer.\");\n\t\t\t\temit(OpCode::GET_GLOBAL_INDEX_DOUBLE);\n\t\t\t\temitUint8((uint8_t)prg.globals[globIndex].slot);\n\t\t\t\treturn prg.types.Double();\n\t\t\t}\n\t\t}\n\t}\n\n\tType exprType = compileExpression(expr->object.get());\n\tType indxType = compileExpression(expr->index.get());\n\n\t// vec2 indexing\n\tif (exprType->kind == TypeKind::Vec2)\n\t{\n\t\tif (indxType->kind != TypeKind::Int)\n\t\t\tthrow std::runtime_error(\"vec2 index must be an integer.\");\n\n\t\temit(OpCode::GET_VEC2_INDEX);\n\t\treturn prg.types.Double();\n\t}\n\n\t// vec3 indexing\n\tif (exprType->kind == TypeKind::Vec3)\n\t{\n\t\tif (indxType->kind != TypeKind::Int)\n\t\t\tthrow std::runtime_error(\"vec3 index must be an integer.\");\n\n\t\temit(OpCode::GET_VEC3_INDEX);\n\t\treturn prg.types.Double();\n\t}\n\n\t// mat2 indexing\n\tif (exprType->kind == TypeKind::Mat2)\n\t{\n\t\tif (indxType->kind != TypeKind::Int)\n\t\t\tthrow std::runtime_error(\"mat2 index must be an integer.\");\n\n\t\temit(OpCode::GET_MAT2_INDEX);\n\t\treturn prg.types.Vec2();\n\t}\n\n\t// mat3 indexing\n\tif (exprType->kind == TypeKind::Mat3)\n\t{\n\t\tif (indxType->kind != TypeKind::Int)\n\t\t\tthrow std::runtime_error(\"mat3 index must be an integer.\");\n\n\t\temit(OpCode::GET_MAT3_INDEX);\n\t\treturn prg.types.Vec3();\n\t}\n\n\tif (exprType->kind != TypeKind::Array)\n\t\tthrow std::runtime_error(\"Cannot index non-array type.\");\n\tif (indxType->kind != TypeKind::Int)\n\t\tthrow std::runtime_error(\"Array index must be a number.\");\n\n\tint stackEffect = -(int)exprType->size() + exprType->elementType->size();\n\n\tswitch (exprType->elementType->kind)\n\t{\n\tcase TypeKind::Bool  : emit(OpCode::GET_INDEX_BOOL  , stackEffect); break;\n\tcase TypeKind::Int   : emit(OpCode::GET_INDEX_INT   , stackEffect); break;\n\tcase TypeKind::Double: emit(OpCode::GET_INDEX_DOUBLE, stackEffect); break;\n\tcase TypeKind::Vec2  : emit(OpCode::GET_INDEX_VEC2  , stackEffect); break;\n\tcase TypeKind::Vec3  : emit(OpCode::GET_INDEX_VEC3  , stackEffect); break;\n\tcase TypeKind::Mat2  : emit(OpCode::GET_INDEX_MAT2  , stackEffect); break;\n\tcase TypeKind::Mat3  : emit(OpCode::GET_INDEX_MAT3  , stackEffect); break;\n\tcase TypeKind::Array : emit(OpCode::GET_INDEX_ARRAY , stackEffect); break;\n\tcase TypeKind::Struct: emit(OpCode::GET_INDEX_STRUCT, stackEffect); break;\n\t\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported array element type\");\n\t}\n\n\temitUint8(exprType->typeIndex);\n\n\treturn exprType->elementType;\n}\n\n\nconst char* febcode::OpCodeToString(febcode::OpCode op)\n{\n\tswitch (op)\n\t{\n\tcase OpCode::PUSH_VOID  : \n\tcase OpCode::PUSH_BOOL  :\n\tcase OpCode::PUSH_INT   :\n\tcase OpCode::PUSH_DOUBLE:\n\tcase OpCode::PUSH_VEC2  :\n\tcase OpCode::PUSH_VEC3  :\n\tcase OpCode::PUSH_MAT2  :\n\tcase OpCode::PUSH_MAT3  :\n\t\treturn \"MOV \";\n\n\tcase OpCode::GET_GLOBAL_BOOL  :\n\tcase OpCode::GET_GLOBAL_INT   :\n\tcase OpCode::GET_GLOBAL_DOUBLE:\n\tcase OpCode::GET_GLOBAL_VEC2  :\n\tcase OpCode::GET_GLOBAL_VEC3  : \n\tcase OpCode::GET_GLOBAL_MAT2  : \n\tcase OpCode::GET_GLOBAL_MAT3  : \n\tcase OpCode::GET_GLOBAL_ARRAY : \n\tcase OpCode::GET_GLOBAL_STRUCT: return \"GTG \";\n\n\tcase OpCode::SET_GLOBAL_BOOL  :\n\tcase OpCode::SET_GLOBAL_INT   :\n\tcase OpCode::SET_GLOBAL_DOUBLE:\n\tcase OpCode::SET_GLOBAL_VEC2  :\n\tcase OpCode::SET_GLOBAL_VEC3  :\n\tcase OpCode::SET_GLOBAL_MAT2  :\n\tcase OpCode::SET_GLOBAL_MAT3  :\n\tcase OpCode::SET_GLOBAL_ARRAY :\n\tcase OpCode::SET_GLOBAL_STRUCT: return \"STG \";\n\n\tcase OpCode::STORE_BOOL  :\n\tcase OpCode::STORE_INT   :\n\tcase OpCode::STORE_DOUBLE:\n\tcase OpCode::STORE_VEC2  :\n\tcase OpCode::STORE_VEC3  :\n\tcase OpCode::STORE_MAT2:\n\tcase OpCode::STORE_MAT3:\n\tcase OpCode::STORE_ARRAY :\n\tcase OpCode::STORE_STRUCT:\n\t\treturn \"STOR\";\n\n\tcase OpCode::GET_GLOBAL_REF: \n\t\treturn \"GREF\";\n\n\tcase OpCode::GET_LOCAL_REF       :\n\t\treturn \"LREF\";\n\n\tcase OpCode::GET_INDEX_REF       : \n\tcase OpCode::GET_INDEX_REF_BOOL  : \n\tcase OpCode::GET_INDEX_REF_INT   : \n\tcase OpCode::GET_INDEX_REF_DOUBLE: \n\tcase OpCode::GET_INDEX_REF_VEC2  : \n\tcase OpCode::GET_INDEX_REF_VEC3  : \n\tcase OpCode::GET_INDEX_REF_MAT2  :\n\tcase OpCode::GET_INDEX_REF_MAT3  :\n\t\treturn \"IREF\";\n\n\tcase OpCode::GET_MEMBER_REF: return \"MREF\";\n\n\tcase OpCode::GET_LOCAL_BOOL  :\n\tcase OpCode::GET_LOCAL_INT   :\n\tcase OpCode::GET_LOCAL_DOUBLE:\n\tcase OpCode::GET_LOCAL_VEC2  :\n\tcase OpCode::GET_LOCAL_VEC3  :\n\tcase OpCode::GET_LOCAL_MAT2  :\n\tcase OpCode::GET_LOCAL_MAT3  :\n\tcase OpCode::GET_LOCAL_ARRAY :\n\tcase OpCode::GET_LOCAL_STRUCT: return \"GETL\";\n\n\tcase OpCode::ADD_INT       : return \"ADDI\";\n\tcase OpCode::ADD_DOUBLE    : return \"ADDF\";\n\tcase OpCode::SUB_INT       : return \"SUBI\";\n\tcase OpCode::SUB_DOUBLE    : return \"SUBF\";\n\tcase OpCode::MUL_INT       : return \"MULI\";\n\tcase OpCode::MUL_DOUBLE    : return \"MULF\";\n\tcase OpCode::DIV_INT       : return \"DIVI\";\n\tcase OpCode::DIV_DOUBLE    : return \"DIVF\";\n\tcase OpCode::EXP_INT       : return \"EXPI\";\n\tcase OpCode::EXP_DOUBLE    : return \"EXPF\";\n\tcase OpCode::SQR_DOUBLE    : return \"SQR \";\n\tcase OpCode::SQRT_DOUBLE   : return \"SQRT\";\n\n\tcase OpCode::EQUAL_BOOL  :\n\tcase OpCode::EQUAL_INT   :\n\tcase OpCode::EQUAL_DOUBLE: return \"EQ  \";\n\n\tcase OpCode::NEQ_BOOL  :\n\tcase OpCode::NEQ_INT   :\n\tcase OpCode::NEQ_DOUBLE: return \"NEQ \";\n\n\tcase OpCode::GT_INT        : return \"GTI \";\n\tcase OpCode::GT_DOUBLE     : return \"GTF \";\n\tcase OpCode::LT_INT        : return \"LTI \";\n\tcase OpCode::LT_DOUBLE     : return \"LTF \";\n\tcase OpCode::GE_INT        : return \"GEI \";\n\tcase OpCode::GE_DOUBLE     : return \"GEF \";\n\tcase OpCode::LE_INT        : return \"LEI \";\n\tcase OpCode::LE_DOUBLE     : return \"LEF \";\n\tcase OpCode::NEG_INT       : return \"NEGI\";\n\tcase OpCode::NEG_DOUBLE    : return \"NEGF\";\n\n\tcase OpCode::CREATE_VEC2_1ARG: return \"V2_1\";\n\tcase OpCode::GET_VEC2_X    : return \"GV2X\";\n\tcase OpCode::GET_VEC2_Y    : return \"GV2Y\";\n\tcase OpCode::GET_VEC2_SWIZZLE: return \"G2SW\";\n\tcase OpCode::GET_VEC2_INDEX: return \"G2I \";\n\tcase OpCode::ADD_VEC2      : return \"ADD2\";\n\tcase OpCode::SUB_VEC2      : return \"SUB2\";\n\tcase OpCode::DOT_VEC2      : return \"DOT2\";\n\tcase OpCode::MUL_VEC2_DOUBLE: return \"ML2F\";\n\tcase OpCode::MUL_DOUBLE_VEC2: return \"MLF2\";\n\tcase OpCode::DIV_VEC2_DOUBLE: return \"DV2F\";\n\tcase OpCode::NEG_VEC2      : return \"NEG2\";\n\n\tcase OpCode::CREATE_VEC3_1ARG: return \"V3_1\";\n\tcase OpCode::GET_VEC3_X    : return \"GV3X\";\n\tcase OpCode::GET_VEC3_Y    : return \"GV3Y\";\n\tcase OpCode::GET_VEC3_Z    : return \"GV3Z\";\n\tcase OpCode::GET_VEC3_SWIZZLE: return \"G3SW\";\n\tcase OpCode::GET_VEC3_INDEX: return \"G3I \";\n\tcase OpCode::ADD_VEC3      : return \"ADD3\";\n\tcase OpCode::SUB_VEC3      : return \"SUB3\";\n\tcase OpCode::DOT_VEC3      : return \"DOT3\";\n\tcase OpCode::MUL_VEC3_DOUBLE: return \"ML3F\";\n\tcase OpCode::MUL_DOUBLE_VEC3: return \"MLF3\";\n\tcase OpCode::DIV_VEC3_DOUBLE: return \"DV3F\";\n\tcase OpCode::NEG_VEC3      : return \"NEG3\";\n\n\tcase OpCode::ADD_MAT2       : return \"ADM2\";\n\tcase OpCode::SUB_MAT2       : return \"SBM2\";\n\tcase OpCode::MUL_MAT2       : return \"MLM2\";\n\tcase OpCode::MUL_MAT2_DOUBLE: return \"MM2F\";\n\tcase OpCode::MUL_DOUBLE_MAT2: return \"FM2 \";\n\tcase OpCode::MUL_MAT2_VEC2  : return \"M2V2\";\n\tcase OpCode::DIV_MAT2_DOUBLE: return \"DM2F\";\n\tcase OpCode::NEG_MAT2       : return \"NGM2\";\n\tcase OpCode::GET_MAT2_INDEX : return \"GM2I\";\n\tcase OpCode::CREATE_MAT2_DIAG: return \"MAT2\";\n\n\tcase OpCode::ADD_MAT3       : return \"ADM3\";\n\tcase OpCode::SUB_MAT3       : return \"SBM3\";\n\tcase OpCode::MUL_MAT3       : return \"MLM3\";\n\tcase OpCode::MUL_MAT3_DOUBLE: return \"MM3F\";\n\tcase OpCode::DIV_MAT3_DOUBLE: return \"DM3F\";\n\tcase OpCode::MUL_DOUBLE_MAT3: return \"FM3 \";\n\tcase OpCode::MUL_MAT3_VEC3  : return \"M3V3\";\n\tcase OpCode::NEG_MAT3       : return \"NGM3\";\n\tcase OpCode::GET_MAT3_INDEX : return \"GM3I\";\n\tcase OpCode::ADD_GLOBAL_MAT3: return \"ADM3\";\n\tcase OpCode::SUB_GLOBAL_MAT3: return \"SBM3\";\n\tcase OpCode::MUL_GLOBAL_MAT3: return \"MLM3\";\n\tcase OpCode::CREATE_MAT3_DIAG: return \"MAT3\";\n\tcase OpCode::CREATE_MAT3_VEC3: return \"C3V3\";\n\n\tcase OpCode::NOT           : return \"NOT \";\n\n\tcase OpCode::GET_PROPERTY_BOOL:\n\tcase OpCode::GET_PROPERTY_INT:\n\tcase OpCode::GET_PROPERTY_DOUBLE:\n\tcase OpCode::GET_PROPERTY_VEC2:\n\tcase OpCode::GET_PROPERTY_VEC3:\n\tcase OpCode::GET_PROPERTY_MAT2:\n\tcase OpCode::GET_PROPERTY_MAT3:\n\tcase OpCode::GET_PROPERTY_ARRAY:\n\tcase OpCode::GET_PROPERTY_STRUCT:\n\t\treturn \"GETP\";\n\n\tcase OpCode::GET_INDEX_BOOL  :\n\tcase OpCode::GET_INDEX_INT   :\n\tcase OpCode::GET_INDEX_DOUBLE:\n\tcase OpCode::GET_INDEX_VEC2  :\n\tcase OpCode::GET_INDEX_VEC3  :\n\tcase OpCode::GET_INDEX_ARRAY :\n\tcase OpCode::GET_INDEX_STRUCT:\n\t\treturn \"GETI\";\n\n\tcase OpCode::GET_GLOBAL_INDEX_DOUBLE:\n\t\treturn \"GETI\";\n\n\tcase OpCode::JUMP          : return \"JMP \";\n\tcase OpCode::JUMP_IF_FALSE : return \"JMPF\";\n\tcase OpCode::JUMP_IF_TRUE  : return \"JMPT\";\n\tcase OpCode::LOOP          : return \"LOOP\";\n\tcase OpCode::CALL          : return \"CALL\";\n\n\tcase OpCode::POP_VOID  : return \"POPV\";\n\tcase OpCode::POP_BOOL  : return \"POPB\";\n\tcase OpCode::POP_INT   : return \"POPI\";\n\tcase OpCode::POP_DOUBLE: return \"POPD\";\n\tcase OpCode::POP_VEC2  : return \"POP2\";\n\tcase OpCode::POP_VEC3  : return \"POP3\";\n\tcase OpCode::POP_MAT2  : return \"PPM2\";\n\tcase OpCode::POP_MAT3  : return \"PPM3\";\n\tcase OpCode::POP_ARRAY : return \"POPA\";\n\tcase OpCode::POP_STRUCT: return \"POPS\";\t\n\n\tcase OpCode::GET_VEC2_X_REF: return \"RV2X\";\n\tcase OpCode::GET_VEC2_Y_REF: return \"RV2Y\";\n\tcase OpCode::GET_VEC3_X_REF: return \"RV3X\";\n\tcase OpCode::GET_VEC3_Y_REF: return \"RV3Y\";\n\tcase OpCode::GET_VEC3_Z_REF: return \"RV3Z\";\n\n\tcase OpCode::RETURN_VOID: \n\tcase OpCode::RETURN_BOOL: \n\tcase OpCode::RETURN_INT: \n\tcase OpCode::RETURN_DOUBLE: \n\tcase OpCode::RETURN_VEC2: \n\tcase OpCode::RETURN_VEC3: \n\tcase OpCode::RETURN_MAT2:\n\tcase OpCode::RETURN_MAT3:\n\tcase OpCode::RETURN_ARRAY: \n\tcase OpCode::RETURN_STRUCT: \n\t\treturn \"RET \";\n\t}\n\treturn \"(UNKNOWN)\";\n}\n"
  },
  {
    "path": "febcode/compiler.h",
    "content": "#pragma once\n#include <vector>\n#include <string>\n#include <cstdint>\n#include \"program.h\"\n\nnamespace febcode\n{\n\tenum OpCode : uint8_t\n\t{\n\t\tPUSH_VOID, // just push a dummy value, used for return without value\n\t\tPUSH_BOOL,\n\t\tPUSH_INT,\n\t\tPUSH_DOUBLE,\n\t\tPUSH_VEC2,\n\t\tPUSH_VEC3,\n\t\tPUSH_MAT2,\n\t\tPUSH_MAT3,\n\n\t\tGET_GLOBAL_BOOL,\n\t\tGET_GLOBAL_INT,\n\t\tGET_GLOBAL_DOUBLE,\n\t\tGET_GLOBAL_VEC2,\n\t\tGET_GLOBAL_VEC3,\n\t\tGET_GLOBAL_MAT2,\n\t\tGET_GLOBAL_MAT3,\n\t\tGET_GLOBAL_ARRAY,\n\t\tGET_GLOBAL_STRUCT,\n\n\t\tSET_GLOBAL_BOOL,\n\t\tSET_GLOBAL_INT,\n\t\tSET_GLOBAL_DOUBLE,\n\t\tSET_GLOBAL_VEC2,\n\t\tSET_GLOBAL_VEC3,\n\t\tSET_GLOBAL_MAT2,\n\t\tSET_GLOBAL_MAT3,\n\t\tSET_GLOBAL_ARRAY,\n\t\tSET_GLOBAL_STRUCT,\n\n\t\tGET_LOCAL_BOOL,\n\t\tGET_LOCAL_INT,\n\t\tGET_LOCAL_DOUBLE,\n\t\tGET_LOCAL_VEC2,\n\t\tGET_LOCAL_VEC3,\n\t\tGET_LOCAL_MAT2,\n\t\tGET_LOCAL_MAT3,\n\t\tGET_LOCAL_ARRAY,\n\t\tGET_LOCAL_STRUCT,\n\n\t\tGET_GLOBAL_REF,\n\n\t\tGET_LOCAL_REF,\n\n\t\t// struct codes\n\t\tGET_PROPERTY_BOOL,\n\t\tGET_PROPERTY_INT,\n\t\tGET_PROPERTY_DOUBLE,\n\t\tGET_PROPERTY_VEC2,\n\t\tGET_PROPERTY_VEC3,\n\t\tGET_PROPERTY_MAT2,\n\t\tGET_PROPERTY_MAT3,\n\t\tGET_PROPERTY_ARRAY,\n\t\tGET_PROPERTY_STRUCT,\n\n\t\tGET_MEMBER_REF,\n\n\t\tGET_INDEX_BOOL,\n\t\tGET_INDEX_INT,\n\t\tGET_INDEX_DOUBLE,\n\t\tGET_INDEX_VEC2,\n\t\tGET_INDEX_VEC3,\n\t\tGET_INDEX_MAT2,\n\t\tGET_INDEX_MAT3,\n\t\tGET_INDEX_ARRAY,\n\t\tGET_INDEX_STRUCT,\n\n\t\tGET_GLOBAL_INDEX_DOUBLE,\n\n\t\tGET_INDEX_REF,\n\t\tGET_INDEX_REF_BOOL,\n\t\tGET_INDEX_REF_INT,\n\t\tGET_INDEX_REF_DOUBLE,\n\t\tGET_INDEX_REF_VEC2,\n\t\tGET_INDEX_REF_VEC3,\n\t\tGET_INDEX_REF_MAT2,\n\t\tGET_INDEX_REF_MAT3,\n\n\t\t// vec2 codes\n\t\tGET_VEC2_X,\n\t\tGET_VEC2_Y,\n\t\tGET_VEC2_X_REF,\n\t\tGET_VEC2_Y_REF,\n\t\tGET_VEC2_SWIZZLE,\n\t\tGET_VEC2_INDEX,\n\n\t\t// vec3 codes\n\t\tGET_VEC3_X,\n\t\tGET_VEC3_Y,\n\t\tGET_VEC3_Z,\n\t\tGET_VEC3_X_REF,\n\t\tGET_VEC3_Y_REF,\n\t\tGET_VEC3_Z_REF,\n\t\tGET_VEC3_SWIZZLE,\n\t\tGET_VEC3_INDEX,\n\n\t\t// int operators\n\t\tNEG_INT,\n\t\tADD_INT,\n\t\tSUB_INT,\n\t\tMUL_INT,\n\t\tDIV_INT,\n\t\tEXP_INT,\n\t\tGT_INT,\n\t\tLT_INT,\n\t\tGE_INT,\n\t\tLE_INT,\n\n\t\t// double operators\n\t\tNEG_DOUBLE,\n\t\tADD_DOUBLE,\n\t\tSUB_DOUBLE,\n\t\tMUL_DOUBLE,\n\t\tDIV_DOUBLE,\n\t\tEXP_DOUBLE,\n\t\tSQR_DOUBLE,\n\t\tSQRT_DOUBLE,\n\t\tGT_DOUBLE,\n\t\tLT_DOUBLE,\n\t\tGE_DOUBLE,\n\t\tLE_DOUBLE,\n\n\t\t// vec2 operators\n\t\tCREATE_VEC2_1ARG, // create a vec2 from a single double (replicated to both components)\n\t\tNEG_VEC2,\n\t\tADD_VEC2,\n\t\tSUB_VEC2,\n\t\tDOT_VEC2,\n\t\tMUL_VEC2_DOUBLE,\n\t\tMUL_DOUBLE_VEC2,\n\t\tDIV_VEC2_DOUBLE,\n\n\t\t// vec3 operators\n\t\tCREATE_VEC3_1ARG, // create a vec3 from a single double (replicated to all components)\n\t\tNEG_VEC3,\n\t\tADD_VEC3,\n\t\tSUB_VEC3,\n\t\tDOT_VEC3,\n\t\tMUL_VEC3_DOUBLE,\n\t\tMUL_DOUBLE_VEC3,\n\t\tDIV_VEC3_DOUBLE,\n\n\t\t// mat2 operators\n\t\tNEG_MAT2,\n\t\tADD_MAT2,\n\t\tSUB_MAT2,\n\t\tMUL_MAT2,\n\t\tMUL_MAT2_DOUBLE,\n\t\tMUL_DOUBLE_MAT2,\n\t\tMUL_MAT2_VEC2,\n\t\tDIV_MAT2_DOUBLE,\n\t\tGET_MAT2_INDEX,\n\t\tCREATE_MAT2_DIAG,\n\n\t\t// mat3 operators\n\t\tNEG_MAT3,\n\t\tADD_MAT3,\n\t\tSUB_MAT3,\n\t\tMUL_MAT3,\n\t\tMUL_MAT3_DOUBLE,\n\t\tMUL_DOUBLE_MAT3,\n\t\tMUL_MAT3_VEC3,\n\t\tDIV_MAT3_DOUBLE,\n\t\tGET_MAT3_INDEX,\n\t\tCREATE_MAT3_DIAG,\n\t\tCREATE_MAT3_VEC3, // create a mat3 from 3 row-vectors\n\n\t\tADD_GLOBAL_MAT3,\n\t\tSUB_GLOBAL_MAT3,\n\t\tMUL_GLOBAL_MAT3,\n\n\t\t// logical operators\n\t\tNOT,\n\n\t\t// comparison operators\n\t\tEQUAL_BOOL,\n\t\tEQUAL_INT,\n\t\tEQUAL_DOUBLE,\n\n\t\tNEQ_BOOL,\n\t\tNEQ_INT,\n\t\tNEQ_DOUBLE,\n\n\t\t// control flow\n\t\tJUMP,\n\t\tJUMP_IF_FALSE,\n\t\tJUMP_IF_TRUE,\n\t\tLOOP,\n\n\t\t// store value in variable (local or global)\n\t\tSTORE_BOOL,\n\t\tSTORE_INT,\n\t\tSTORE_DOUBLE,\n\t\tSTORE_VEC2,\n\t\tSTORE_VEC3,\n\t\tSTORE_MAT2,\n\t\tSTORE_MAT3,\n\t\tSTORE_ARRAY,\n\t\tSTORE_STRUCT,\n\n\t\tPOP_VOID,\n\t\tPOP_BOOL,\n\t\tPOP_INT,\n\t\tPOP_DOUBLE,\n\t\tPOP_VEC2,\n\t\tPOP_VEC3,\n\t\tPOP_MAT2,\n\t\tPOP_MAT3,\n\t\tPOP_ARRAY,\n\t\tPOP_STRUCT,\n\n\t\tCALL,\t\t// call function\n\n\t\t// returns (make sure these are the last opcodes, since they have special handling in the VM)\n\t\tRETURN_VOID,\n\t\tRETURN_BOOL,\n\t\tRETURN_INT,\n\t\tRETURN_DOUBLE,\n\t\tRETURN_VEC2,\n\t\tRETURN_VEC3,\n\t\tRETURN_MAT2,\n\t\tRETURN_MAT3,\n\t\tRETURN_ARRAY,\n\t\tRETURN_STRUCT,\n\n\t\tLAST_OPCODE, // not really an opcode, just a marker for the end of the enum. Also, make sure this is less than 255!\n\t};\n\n\t//\n\t// ================= COMPILER =================\n\t//\n\n\tclass Compiler\n\t{\n\tpublic:\n\t\tCompiler(Program& program);\n\n\t\tvoid compile();\n\n\tprivate:\n\n\t\t// ===== Scope =====\n\n\t\tstruct Local\n\t\t{\n\t\t\tstd::string name;\n\t\t\tType type;\n\t\t\tint depth;\n\t\t\tint slot;\n\t\t};\n\n\t\tvoid beginScope();\n\t\tvoid endScope();\n\t\tint resolveLocal(const std::string& name);\n\t\tint resolveGlobal(const std::string& name);\n\n\t\tbool isNativeFunction(const std::string& name);\n\t\tType resolveVariableType(const std::string& name);\n\n\t\t// ===== Compile =====\n\n\t\tvoid compileStatement(Statement* stmt);\n\t\tvoid compileExprStmt (ExpressionStmt* stmt);\n\t\tvoid compileBlock    (BlockStmt* stmt);\n\t\tvoid compileVarDecl  (VarDeclStmt* decl);\n\t\tvoid compileFunction (FunctionStmt* fn);\n\t\tvoid compileIf       (IfStmt* stmt);\n\t\tvoid compileWhile    (WhileStmt* stmt);\n\t\tvoid compileFor      (ForStmt* stmt);\n\t\tvoid compileReturn   (ReturnStmt* stmt);\n\t\tvoid compileStruct   (StructStmt* stmt);\n\n\t\tType compileExpression   (Expression* expr);\n\t\tType compileBinary       (BinaryExpr* expr);\n\t\tType compileUnary        (UnaryExpr* expr);\n\t\tType compileLiteral      (LiteralExpr* expr);\n\t\tType compileVariable     (VariableExpr* expr);\n\t\tType compileAssign       (AssignExpr* expr);\n\t\tType compileCall         (CallExpr* expr);\n\t\tType compileMember       (MemberExpr* expr);\n\t\tType compileIndex        (IndexExpr* expr);\n\t\tType compileInitializer  (InitExpr* expr);\n\t\tType compileConstructor  (ConstructorExpr* expr);\n\n\t\tType expressionType(Expression* expr);\n\n\t\tType compileLValue(Expression* expr);\n\t\tType compileVariableRef(VariableExpr* expr);\n\t\tType compileMemberRef(MemberExpr* expr);\n\t\tType compileIndexRef(IndexExpr* expr);\n\n\t\tint resolveMember(Type type, const std::string& member);\n\t\tint resolveMemberOffset(Type type, const std::string& member);\n\t\tType memberType(Type type, int memberIndex);\n\n\t\tstd::vector<Type> compileFncArgs(std::vector<std::unique_ptr<Expression>>& args);\n\n\t\tvoid pop(Type type);\n\n\t\t// ===== Bytecode =====\n\n\t\tvoid emit(OpCode op, int arg = 0);\n\t\tvoid emitUint8(uint8_t v);\n\t\tvoid emitUint16(uint16_t v);\n\n\t\tint stackEffect(OpCode op, int arg);\n\n\t\tuint8_t addConstant(const Value& v);\n\n\t\tint emitJump(OpCode op);\n\t\tvoid patchJump(int offset);\n\t\tvoid emitLoop(int loopStart);\n\n\tprivate:\n\t\tProgram& prg;\n\n\t\tbool hasReturn = false; // see if the program or function has an explicit return statement. If not, we will add a default return at the end.\n\t\tType expectedReturnType = nullptr;\n\n\t\tstd::vector<Local> m_locals;\n\t\tint m_scopeDepth = 0;\n\t\tint localStackSize = 0;\n\n\t\tint stackDepth = 0;\n\t\tint maxStackDepth = 0;\n\n\t\tint currentFunction = -1;\n\t};\n\n\tvoid CompileSource(Program& prg, const std::string& source);\n\n\tconst char* OpCodeToString(febcode::OpCode ip);\n} // namespace febcode\n"
  },
  {
    "path": "febcode/differentiator.cpp",
    "content": "#include \"differentiator.h\"\n#include \"simplifier.h\"\n#include <math.h>\n\nusing namespace febcode;\n\nType Differentiator::getDerivativeType(Type varType, TypeKind derivType)\n{\n\tif (derivType == TypeKind::Double)\n\t{\n\t\treturn varType;\n\t}\n\telse if (derivType == TypeKind::Vec2)\n\t{\n\t\tif (varType->kind == TypeKind::Double) return prg.types.Vec2();\n\t\tif (varType->kind == TypeKind::Vec2) return prg.types.Mat2();\n\t}\n\telse if (derivType == TypeKind::Vec3)\n\t{\n\t\tif (varType->kind == TypeKind::Double) return prg.types.Vec3();\n\t\tif (varType->kind == TypeKind::Vec3  ) return prg.types.Mat3();\n\t}\n\tthrow std::runtime_error(\"Can't determine type of derivative.\");\n}\n\nvoid Differentiator::differentiate(const std::string& var)\n{\n\t// get the variable's type\n\tType varType = prg.globalType(var);\n\tif (varType == nullptr)\n\t\tthrow std::runtime_error(\"Variable not found in program: \" + var);\n\n\t// update the program's return type\n\tif (prg.returnType)\n\t{\n\t\tType derivType = getDerivativeType(prg.returnType, varType->kind);\n\t\tprg.returnType = derivType;\n\t}\n\n\tDerivVar dvar{ var, varType };\n\n\t// differentiate the program's AST\n\tauto diffAST = differentiate(*prg.ast, dvar);\n\tprg.ast.reset(diffAST.release()); // replace original AST with derivative AST\n}\n\nstd::unique_ptr<AST> Differentiator::differentiate(const AST& ast, const DerivVar& var)\n{\n\tauto derivativeAst = std::make_unique<AST>();\n\n\t// first, see if the source AST actually depends on the variable we're differentiating with respect to. \n\t// If not, we can just return an empty AST\n\tdependencyFound = false;\n\tfor (const auto& stmt : ast.root.statements)\n\t{\n\t\tif (febcode::dependsOn(stmt.get(), var.name))\n\t\t{\n\t\t\tdependencyFound = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!dependencyFound)\n\t{\n\t\t// the derivative of a constant is zero, so we can just return an AST with a single statement that returns zero.\n\t\tExprPtr returnValue;\n\t\tif (prg.returnType)\n\t\t{\n\t\t\treturnValue = Zero(prg.returnType);\n\t\t}\n\t\tderivativeAst->addStatement(std::make_unique<ReturnStmt>(std::move(returnValue)));\n\t\treturn derivativeAst;\n\t}\n\n\t// add all injected globals to vartypes\n\tfor (const auto& global : prg.globalIndices)\n\t{\n\t\tstd::string name = global.first;\n\t\tType type = prg.globals[global.second].type;\n\t\tvarTypes[name] = type;\n\t}\n\n\tfor (const auto& stmt : ast.root.statements)\n\t{\n\t\tdifferentiateStmt(derivativeAst->root, stmt.get(), var);\n\t}\n\treturn derivativeAst;\n}\n\nvoid Differentiator::differentiateStmt(BlockStmt& ast, Statement* stmt, const DerivVar& var)\n{\n\tif      (auto exprStmt   = dynamic_cast<ExpressionStmt*>(stmt)) diffExpressionStmt(ast, exprStmt  , var);\n\telse if (auto returnStmt = dynamic_cast<ReturnStmt*    >(stmt)) diffReturnStmt    (ast, returnStmt, var); \n\telse if (auto structStmt = dynamic_cast<StructStmt*    >(stmt)) diffStructStmt    (ast, structStmt, var);\n\telse if (auto varStmt    = dynamic_cast<VarDeclStmt*   >(stmt)) diffVarDeclStmt   (ast, varStmt   , var);\n\telse if (auto ifStmt     = dynamic_cast<IfStmt*        >(stmt)) diffIfStmt        (ast, ifStmt    , var);\n\telse if (auto blockStmt  = dynamic_cast<BlockStmt*     >(stmt)) diffBlockStmt     (ast, blockStmt , var);\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Unsupported statement type for differentiation\");\n\t}\n}\n\nvoid Differentiator::diffExpressionStmt(BlockStmt& ast, ExpressionStmt* stmt, const DerivVar& var)\n{\n\tauto derivativeExpr = differentiate(stmt->expr.get(), var);\n\tast.addStatement(std::make_unique<ExpressionStmt>(std::move(derivativeExpr)));\n}\n\nvoid Differentiator::diffReturnStmt(BlockStmt& ast, ReturnStmt* stmt, const DerivVar& var)\n{\n\tauto derivativeExpr = differentiate(stmt->value.get(), var);\n\tast.addStatement(std::make_unique<ReturnStmt>(std::move(derivativeExpr)));\n}\n\nvoid Differentiator::diffStructStmt(BlockStmt& ast, StructStmt* stmt, const DerivVar& var)\n{\n\t// copy the original struct declaration to the derivative AST\n\tast.addStatement(std::make_unique<StructStmt>(stmt->name, stmt->type, stmt->fields));\n}\n\nvoid Differentiator::diffVarDeclStmt(BlockStmt& ast, VarDeclStmt* stmt, const DerivVar& var)\n{\n\tif (stmt->input)\n\t{\n\t\t// input variables are treated as constants with respect to differentiation, so we can just copy the variable declaration to the derivative AST without creating a derivative variable for it.\n\t\tast.addStatement(std::make_unique<VarDeclStmt>(stmt->type, stmt->vars, stmt->input));\n\t\treturn;\n\t}\n\n\tstd::vector<Var> copyVars; // copy of the original variables for the derivative AST\n\tstd::vector<Var> newVars; // new variables for the derivatives in the derivative AST\n\n\tType baseType = stmt->type;\n\n\t// determine the type of the derivative variable based on the type of the original variable and the derivative variable.\n\tType derivType = getDerivativeType(baseType, var.type->kind);\n\n\tfor (auto& var_i : stmt->vars)\n\t{\n\t\t// handle array types by getting the base type and then reconstructing the array type\n\t\tType type = baseType;\n\t\tif (var_i.arraySizes.size() > 0)\n\t\t{\n\t\t\ttype = prg.types.getArrayType(baseType, var_i.arraySizes);\n\t\t}\n\n\t\t// copy the original variable declaration to the derivative AST\n\t\tcopyVars.push_back({ var_i.name, var_i.arraySizes, clone(var_i.initializer.get())});\n\n\t\t// store the type of this variable in the map for later use when creating derivative variables for it.\n\t\tvarTypes[var_i.name] = type;\n\n\t\t// No need to differentiate non-numeric types, since they don't contribute to the derivative. \n\t\t// We can just copy them to the derivative AST without creating a derivative variable for them.\n\t\tif (type->kind == TypeKind::Bool || type->kind == TypeKind::Int) continue;\n\n\t\t// create a new variable for the derivative of this variable\n\t\tstd::string derivName = \"__d\" + var_i.name + \"_d\" + var.name;\n\n\t\tExprPtr init = nullptr;\n\t\tif (var_i.initializer)\n\t\t{\n\t\t\t// lets differentiate the initializer.\n\t\t\tinit = differentiate(var_i.initializer.get(), var);\n\n\t\t\t// if it's zero, then this variable didn't contribute to the derivative, so we can skip creating a derivative variable for it.\n\t\t\tif (isZero(init))\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// If the variable was not initialized, it could be assigned later. \n\t\t\t// To be safe, we should create a derivative variable for it and initialize it to zero.\n\t\t\tswitch (derivType->kind)\n\t\t\t{\n\t\t\tcase TypeKind::Double: init = Literal(0.0); break;\n\t\t\tcase TypeKind::Vec2  : init = Literal(vec2()); break;\n\t\t\tcase TypeKind::Vec3  : init = Literal(vec3()); break;\n\t\t\tcase TypeKind::Mat2  : init = Literal(mat2()); break;\n\t\t\tcase TypeKind::Mat3  : init = Literal(mat3()); break;\n\t\t\tcase TypeKind::Array : init = Initializer(type); break;\n\t\t\tcase TypeKind::Struct: init = Constructor(type); break;\n\t\t\tdefault:\n\t\t\t\tthrow std::runtime_error(\"Don't know how to differentiate variable.\");\n\t\t\t}\n\t\t}\n\n\t\t// add the new derivative variable to the map and the list of new variables for the derivative AST\n\t\tstd::vector<size_t> arraySizes;\n\t\tif (type->kind == TypeKind::Array)\n\t\t{\n\t\t\tarraySizes.push_back(type->arraySize);\n\t\t}\n\t\tderiveVars[var_i.name] = derivName;\n\t\tvarTypes[derivName] = derivType;\n\t\tnewVars.push_back({ derivName, arraySizes, std::move(init)});\n\t}\n\n\t// create new variable declaration statements for the derivatives and add it to the derivative AST\n\tast.addStatement(std::make_unique<VarDeclStmt>(stmt->type, copyVars));\n\tif (!newVars.empty()) ast.addStatement(std::make_unique<VarDeclStmt>(derivType, newVars));\n}\n\nvoid Differentiator::diffIfStmt(BlockStmt& ast, IfStmt* stmt, const DerivVar& var)\n{\n\t// copy the condition\n\tauto newIf = std::make_unique<IfStmt>();\n\tnewIf->condition = std::move(clone(stmt->condition.get()));\n\n\t// differentiate the then branch\n\tstd::unique_ptr<BlockStmt> thenStmt = std::make_unique<BlockStmt>();\n\tdifferentiateStmt(*thenStmt, stmt->thenBranch.get(), var);\n\tnewIf->thenBranch = std::move(thenStmt);\n\n\t// differentiate the else branch if it exists\n\tif (stmt->elseBranch)\n\t{\n\t\tstd::unique_ptr<BlockStmt> elseStmt = std::make_unique<BlockStmt>();\n\t\tdifferentiateStmt(*elseStmt, stmt->elseBranch.get(), var);\n\t\tnewIf->elseBranch = std::move(elseStmt);\n\t}\n\n\t// create the new if statement with the differentiated branches\n\tast.addStatement(std::move(newIf));\n}\n\nvoid Differentiator::diffBlockStmt(BlockStmt& ast, BlockStmt* stmt, const DerivVar& var)\n{\n\tfor (const auto& s : stmt->statements)\n\t{\n\t\tdifferentiateStmt(ast, s.get(), var);\n\t}\n}\n\nExprPtr Differentiator::differentiate(const Expression* expr, const DerivVar& var)\n{\n\tif      (auto literal  = dynamic_cast<const LiteralExpr*    >(expr)) return simplify(diffLiteral    (literal , var));\n\telse if (auto variable = dynamic_cast<const VariableExpr*   >(expr)) return simplify(diffVariable   (variable, var));\n\telse if (auto unary    = dynamic_cast<const UnaryExpr*      >(expr)) return simplify(diffUnary      (unary   , var));\n\telse if (auto binary   = dynamic_cast<const BinaryExpr*     >(expr)) return simplify(diffBinary     (binary  , var));\n\telse if (auto call     = dynamic_cast<const CallExpr*       >(expr)) return simplify(diffCall       (call    , var));\n\telse if (auto init     = dynamic_cast<const InitExpr*       >(expr)) return simplify(diffInit       (init    , var));\n\telse if (auto ctor     = dynamic_cast<const ConstructorExpr*>(expr)) return simplify(diffConstructor(ctor    , var));\n\telse if (auto assign   = dynamic_cast<const AssignExpr*     >(expr)) return simplify(diffAssign     (assign  , var));\n\telse if (auto index    = dynamic_cast<const IndexExpr*      >(expr)) return simplify(diffIndex      (index   , var));\n\telse if (auto member   = dynamic_cast<const MemberExpr*     >(expr)) return simplify(diffMember     (member  , var));\n\telse\n\t\tthrow std::runtime_error(\"Unsupported expression type for differentiation\");\n\n\treturn nullptr;\n}\n\nExprPtr Differentiator::diffLiteral(const LiteralExpr* literal, const DerivVar& var)\n{\n\t// scalar derivation\n\tif (var.type->kind == TypeKind::Double)\n\t{\n\t\t// The derivative of a constant is zero\n\t\tswitch (literal->value.index)\n\t\t{\n\t\tcase ValueIndex::INT: return Literal(0);\n\t\tcase ValueIndex::DOUBLE: return Literal(0.0);\n\t\tcase ValueIndex::VEC2: return Literal(vec2());\n\t\tcase ValueIndex::VEC3: return Literal(vec3());\n\t\tcase ValueIndex::MAT2: return Literal(mat2());\n\t\tcase ValueIndex::MAT3: return Literal(mat3());\n\t\t}\n\t}\n\n\t// 2d gradient\n\tif (var.type->kind == TypeKind::Vec2)\n\t{\n\t\tswitch (literal->value.index)\n\t\t{\n\t\tcase ValueIndex::INT:\n\t\tcase ValueIndex::DOUBLE: return Literal(vec2());\n\t\tcase ValueIndex::VEC2  : return Literal(mat2());\n\t\t}\n\t}\n\n\t// 3d gradient\n\tif (var.type->kind == TypeKind::Vec3)\n\t{\n\t\tswitch (literal->value.index)\n\t\t{\n\t\tcase ValueIndex::INT:\n\t\tcase ValueIndex::DOUBLE: return Literal(vec3());\n\t\tcase ValueIndex::VEC3  : return Literal(mat3());\n\t\t}\n\t}\n\n\tthrow std::runtime_error(\"Don't know how to differentiate this literal type.\");\n}\n\nExprPtr Differentiator::diffVariable(const VariableExpr* variable, const DerivVar& var)\n{\n\tType derivType = getDerivativeType(variable->valType, var.type->kind);\n\n\t// The derivative of a variable with respect to itself is 1\n\tif (variable->name == var.name)\n\t{\n\t\tswitch (derivType->kind)\n\t\t{\n\t\tcase TypeKind::Double: return Literal(1.0);\n\t\tcase TypeKind::Mat2  : return Literal(mat2(1.0));\n\t\tcase TypeKind::Mat3  : return Literal(mat3(1.0));\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Don't know how to make literal of derivative type.\");\n\t\t}\n\t}\n\n\t// see if we have a derivative for this variable\n\tauto it = deriveVars.find(variable->name);\n\tif (it != deriveVars.end())\n\t{\n\t\treturn Variable(it->second, derivType);\n\t}\n\telse\n\t{\n\t\t// we may get here if a derivative was not created for this variable \n\t\t// (e.g. if it's a non-numeric type or has a literal initializer),\n\t\t// in which case we treat it as a constant and return zero\n\t\tswitch (derivType->kind)\n\t\t{\n\t\tcase TypeKind::Double: return Literal(0.0);\n\t\tcase TypeKind::Vec2: return Literal(vec2());\n\t\tcase TypeKind::Vec3: return Literal(vec3());\n\t\tcase TypeKind::Mat2: return Literal(mat2());\n\t\tcase TypeKind::Mat3: return Literal(mat3());\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Don't know how to make literal of derivative type.\");\n\t\t}\n\t}\n}\n\nExprPtr Differentiator::diffUnary(const UnaryExpr* unary, const DerivVar& var)\n{\n\tauto right = simplify(unary->right);\n\n\t// For unary negation, the derivative is the negation of the derivative of the operand\n\t// d(-f) = -df\n\tif (unary->op == UnaryOp::Negate) {\n\t\tauto operandDerivative = differentiate(right.get(), var);\n\t\treturn Negate(operandDerivative);\n\t}\n\n\tthrow std::runtime_error(\"Unsupported unary operator for differentiation\");\n}\n\nstd::unique_ptr<Expression> Differentiator::diffBinary(const BinaryExpr* binary, const DerivVar& var)\n{\n\tauto left  = simplify(binary->left);\n\tauto right = simplify(binary->right);\n\n\tauto dleft = differentiate(left.get(), var);\n\tauto dright = differentiate(right.get(), var);\n\n\t// plus and minus are easy, so let's handle them first.\n\tif (binary->op == BinaryOp::Plus)\n\t\treturn Add(dleft, dright);\n\tif (binary->op == BinaryOp::Minus)\n\t\treturn Sub(dleft, dright);\n\n\t// The other operators are more complex. \n\t// First, figure out the types of all the expressions involved, so we can determine how to apply the differentiation rules.\n\tType ltype  = left->valType;\n\tType rtype  = right->valType;\n\tType dltype = dleft->valType;\n\tType drtype = dright->valType;\n\n\t// scalar differentation\n\tif (var.type->kind == TypeKind::Double)\n\t{\n\t\tif (isScalarType(ltype) && isScalarType(rtype))\n\t\t{\n\t\t\tswitch (binary->op)\n\t\t\t{\n\t\t\tcase BinaryOp::Multiply: return Add(Mul(dleft, right), Mul(left, dright)); // d( f * g ) = df * g + f * dg\n\t\t\tcase BinaryOp::Divide: return Div(Sub(Mul(dleft, right), Mul(left, dright)), Mul(right, right)); // d( f / g ) = (df * g - f * dg) / (g * g)\n\t\t\tcase BinaryOp::Exponent:\n\t\t\t{\n\t\t\t\t// some special cases first\n\t\t\t\tif (auto lit = dynamic_cast<LiteralExpr*>(right.get()))\n\t\t\t\t{\n\t\t\t\t\tconst Value& e = lit->value;\n\t\t\t\t\tif (isIntNumber(e))\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble p = (double)toIntNumber(e);\n\t\t\t\t\t\tif (p == 1) return clone(dleft.get());\n\t\t\t\t\t\telse if (p != 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn Mul(Mul(Literal(p), Pow(left, Literal(p - 1.0))), dleft); // d(x^p) = p * x^(p-1)*dx\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (binary->op)\n\t\t\t{\n\t\t\t// The rule d( f * g ) = df * g + f * dg applies to all types, since it doesn't matter what the types of f and g are, as long as we can multiply them together.\n\t\t\tcase BinaryOp::Multiply: return Add(Mul(dleft, right), Mul(left, dright));\n\t\t\t};\n\t\t}\n\t}\n\telse if (var.type->kind == TypeKind::Vec2) // gradient w.r.t. a vec2 variable\n\t{\n\n\t}\n\telse if (var.type->kind == TypeKind::Vec3) // gradient w.r.t. a vec3 variable\n\t{\n\t\tif (binary->op == BinaryOp::Multiply)\n\t\t{\n\t\t\tif (isScalarType(ltype) && isScalarType(rtype))\n\t\t\t{\n\t\t\t\t// the derivatives of scalars with respect to a vec3 variable should be vec3s)\n\t\t\t\tassert(isVec3Type(dltype) && isVec3Type(drtype));\n\t\t\t\t// grad( f * g ) = grad(f) * g + f * grad(g), where f and g are scalars\n\t\t\t\treturn Add(Mul(dleft, right), Mul(left, dright));\n\t\t\t}\n\t\t\tif (isScalarType(ltype) && (rtype->kind == TypeKind::Vec3))\n\t\t\t{\n\t\t\t\t// grad( f * v ) = v * grad(f) + f * grad(v), where f is a scalar and v is a vector\n\t\t\t\treturn Add(OuterProduct(right, dleft), Mul(left, dright));\n\t\t\t}\n\t\t\telse if ((ltype->kind == TypeKind::Vec3) && isScalarType(rtype))\n\t\t\t{\n\t\t\t\t// grad( v * f ) = grad(v) * f + v * grad(f), where v is a vector and f is a scalar\n\t\t\t\treturn Add(Mul(dleft, right), OuterProduct(left, dright));\n\t\t\t}\n\t\t\telse if ((ltype->kind == TypeKind::Vec3) && (rtype->kind == TypeKind::Vec3))\n\t\t\t{\n\t\t\t\t// grad( v1 . v2 ) = transpose(grad(v1)) . v2 + transpose(grad(v2)) . v1, where v1 and v2 are vectors\n\t\t\t\treturn Add(Mul(Transpose(dleft), right), Mul(Transpose(dright), left));\n\t\t\t}\n\t\t}\n\t\tif (binary->op == BinaryOp::Divide)\n\t\t{\n\t\t\tif (isScalarType(ltype) && isScalarType(rtype))\n\t\t\t{\n\t\t\t\t// the derivatives of scalars with respect to a vec3 variable should be vec3s)\n\t\t\t\tassert(isVec3Type(dltype) && isVec3Type(drtype));\n\t\t\t\t// grad( f / g ) = (grad(f) * g - f * grad(g)) / (g * g), where f and g are scalars\n\t\t\t\treturn Div(Sub(Mul(dleft, right), Mul(left, dright)), Mul(right, right));\n\t\t\t}\n\t\t\telse if ((ltype->kind == TypeKind::Vec3) && isScalarType(rtype))\n\t\t\t{\n\t\t\t\t// grad( v / f ) = (grad(v) * f - v & grad(f)) / (f * f), where v is a vector and f is a scalar\n\t\t\t\treturn Div(Sub(Mul(dleft, right), OuterProduct(left, dright)), Mul(right, right));\n\t\t\t}\n\t\t}\n\t\tif (binary->op == BinaryOp::Exponent)\n\t\t{\n\t\t\tif (isScalarType(ltype) && isLiteral(right))\n\t\t\t{\n\t\t\t\tLiteralExpr* exponent = dynamic_cast<LiteralExpr*>(right.get());\n\t\t\t\tif (isIntNumber(exponent->value))\n\t\t\t\t{\n\t\t\t\t\tint p = toIntNumber(exponent->value);\n\t\t\t\t\tif (p == 1) return clone(dleft.get());\n\t\t\t\t\telse if (p != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t// grad(x^p) = p * x^(p-1)*grad(x), where x is a vector and p is a scalar literal\n\t\t\t\t\t\treturn Mul(Mul(Literal(p), Pow(left, Literal(p - 1.0))), dleft);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow std::runtime_error(\"AD error: Unsupported binary operator '\" + opToString(binary->op) + \"' with operand types '\" + TypeToString(ltype) + \"' and '\" + TypeToString(rtype) + \"'.\");\n}\n\nExprPtr Differentiator::diffCall(const CallExpr* call, const DerivVar& var)\n{\n\tstd::vector<Type> argTypes;\n\tfor (const auto& arg : call->arguments)\n\t{\n\t\targTypes.push_back(arg->valType);\n\t}\n\n\t// find the function with the matching name and argument types in the program's function definitions\n\tint fnIndex = prg.resolveFunction(call->name, argTypes);\n\tif (fnIndex == -1)\n\t\tthrow std::runtime_error(\"Function \\\"\" + call->name + \"\\\" not found for differentiation.\");\n\n\tType returnType = call->valType;\n\tType derivType = getDerivativeType(returnType, var.type->kind);\n\n\tconst std::string& fnc = call->name;\n\tauto& args = call->arguments;\n\tif (args.size() == 1)\n\t{\n\t\t// differentiate argument\n\t\tauto diffArg = differentiate(args[0].get(), var);\n\n\t\tif      (fnc == \"sin\") return Mul(Call(\"cos\", args), diffArg);\n\t\telse if (fnc == \"cos\") return Negate(Mul(Call(\"sin\", args), diffArg));\n\t\telse if (fnc == \"exp\") return Mul(Call(\"exp\", args), diffArg);\n\t\telse if (fnc == \"sqrt\") return Mul(Div(Literal(0.5), Call(\"sqrt\", args)), diffArg);\n\t\telse if (fnc == \"length\" && isVec3Type(args[0]->valType))\n\t\t{\n\t\t\tif (derivType->kind == TypeKind::Double)\n\t\t\t{\n\t\t\t\tassert(diffArg->valType->kind == TypeKind::Vec3);\n\t\t\t\t// d(length(v)) = (v . d(v)) / length(v)\n\t\t\t\treturn Div(Mul(args[0], diffArg), Call(\"length\", args));\n\t\t\t}\n\t\t\telse if (derivType->kind == TypeKind::Vec3)\n\t\t\t{\n\t\t\t\tassert(diffArg->valType->kind == TypeKind::Mat3);\n\t\t\t\t// grad(length(v)) = (transpose(grad(v)) . v) / length(v)\n\t\t\t\treturn Div(Mul(Transpose(diffArg), args[0]), Call(\"length\", args));\n\t\t\t}\n\t\t}\n\t\telse if (fnc == \"normalize\" && isVec3Type(args[0]->valType))\n\t\t{\n\t\t\t// rewrite normalize(v) as v / length(v), then derivate that\n\t\t\tExprPtr normalize = Div(args[0], Call(\"length\", args));\n\t\t\treturn differentiate(normalize.get(), var);\n\t\t}\n\t}\n\n\tthrow std::runtime_error(\"Don't know how to differentiate function \\\"\" + fnc + \"\\\".\");\n}\n\nExprPtr Differentiator::diffInit(const InitExpr* init, const DerivVar& var)\n{\n\tstd::vector<ExprPtr> diffElements;\n\tfor (const auto& elem : init->elements)\n\t{\n\t\tdiffElements.push_back(differentiate(elem.get(), var));\n\t}\n\treturn std::make_unique<InitExpr>(std::move(diffElements));\n}\n\nExprPtr Differentiator::diffConstructor(const ConstructorExpr* ctor, const DerivVar& var)\n{\n\t// determine the type of the derivative variable based on the type of the original variable and the derivative variable.\n\tType derivType = getDerivativeType(ctor->valType, var.type->kind);\n\n\tif (var.type->kind == TypeKind::Double)\n\t{\n\t\tstd::vector<ExprPtr> diffArgs;\n\t\tfor (const auto& arg : ctor->args)\n\t\t{\n\t\t\tdiffArgs.push_back(differentiate(arg.get(), var));\n\t\t}\n\t\treturn std::make_unique<ConstructorExpr>(ctor->valType, std::move(diffArgs));\n\t}\n\telse if (var.type->kind == TypeKind::Vec3)\n\t{\n\t\tif (ctor->args.size() == 3)\n\t\t{\n\t\t\tstd::vector<ExprPtr> diffArgs;\n\t\t\tfor (const auto& arg : ctor->args)\n\t\t\t{\n\t\t\t\tExprPtr darg = differentiate(arg.get(), var);\n\t\t\t\tif (darg->valType != prg.types.Vec3())\n\t\t\t\t{\n\t\t\t\t\tthrow std::runtime_error(\"Don't know how to differentiate this constructor for vec3 variable.\");\n\t\t\t\t}\n\t\t\t\tdiffArgs.push_back(std::move(darg));\n\t\t\t}\n\n\t\t\t// put all the components into a mat3 constructor\n\t\t\t// grad(vec3(x, y, z)) = mat3( grad(x),\n\t\t\t//                             grad(y),\n\t\t\t//                             grad(z) )\n\t\t\treturn std::make_unique<ConstructorExpr>(prg.types.Mat3(), std::move(diffArgs));\n\t\t}\n\t\telse\n\t\t\tthrow std::runtime_error(\"Don't know how to differentiate this constructor for vec3 variable.\");\n\t}\n\n\tthrow std::runtime_error(\"Don't know how to differentiate constructor.\");\n}\n\nstd::unique_ptr<Expression> Differentiator::diffAssign(const AssignExpr* assign, const DerivVar& var)\n{\n\t// For an assignment expression, we can use the rule: d( y = expr ) --> dy = d(expr)\n\tauto du = differentiate(assign->target.get(), var);\n\tauto dv = differentiate(assign->value.get(), var);\n\n\t// if the left is zero, that meants that it did not depend on the variable.\n\t// In that case, we just copy the original expression, since it doesn't contribute to the derivative.\n\tif (isZero(du))\n\t\treturn clone(assign);\n\n\treturn Assign(du, dv);\n}\n\nstd::unique_ptr<Expression> Differentiator::diffMember(const MemberExpr* member, const DerivVar& var)\n{\n\tif (var.type->kind == TypeKind::Double)\n\t{\n\t\t// For a member access expression, we can use the rule: d( obj.field ) --> dobj.field\n\t\tauto dobj = differentiate(member->object.get(), var);\n\t\treturn Member(dobj, member->property);\n\t}\n\telse if (var.type->kind == TypeKind::Vec2)\n\t{\n\t\tif (isVariable(member->object))\n\t\t{\n\t\t\tauto var = dynamic_cast<const VariableExpr*>(member->object.get());\n\t\t\tif (var->name == var->name)\n\t\t\t{\n\t\t\t\tif      (member->property == \"x\") return Literal(vec2(1, 0));\n\t\t\t\telse if (member->property == \"y\") return Literal(vec2(0, 1));\n\t\t\t\telse\n\t\t\t\t\tthrow std::runtime_error(\"Don't know how to differentiate this member access for vec2 variable.\");\n\t\t\t}\n\t\t}\n\t}\n\telse if (var.type->kind == TypeKind::Vec3)\n\t{\n\t\tif (isVariable(member->object))\n\t\t{\n\t\t\tauto var = dynamic_cast<const VariableExpr*>(member->object.get());\n\t\t\tif (var->name == var->name)\n\t\t\t{\n\t\t\t\tif      (member->property == \"x\") return Literal(vec3(1, 0, 0));\n\t\t\t\telse if (member->property == \"y\") return Literal(vec3(0, 1, 0));\n\t\t\t\telse if (member->property == \"z\") return Literal(vec3(0, 0, 1));\n\t\t\t\telse\n\t\t\t\t\tthrow std::runtime_error(\"Don't know how to differentiate this member access for vec3 variable.\");\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow std::runtime_error(\"Don't know how to differentiate member access for this variable type.\");\n}\n\nstd::unique_ptr<Expression> Differentiator::diffIndex(const IndexExpr* index, const DerivVar& var)\n{\n\tif (var.type->kind == TypeKind::Double)\n\t{\n\t\t// For an index access expression, we can use the rule: d( obj[i] ) --> d(obj)[i]\n\t\tauto dobj = differentiate(index->object.get(), var);\n\t\treturn Index(dobj, index->index);\n\t}\n\n\tthrow std::runtime_error(\"Don't know how to differentiate index access for this variable type.\");\n}\n\nbool febcode::dependsOn(const Expression* expr, const std::string& var)\n{\n\tif (auto literal = dynamic_cast<const LiteralExpr*>(expr))\n\t{\n\t\treturn false;\n\t}\n\telse if (auto variable = dynamic_cast<const VariableExpr*>(expr))\n\t{\n\t\treturn variable->name == var;\n\t}\n\telse if (auto binary = dynamic_cast<const BinaryExpr*>(expr))\n\t{\n\t\treturn dependsOn(binary->left.get(), var) || dependsOn(binary->right.get(), var);\n\t}\n\telse if (auto unary = dynamic_cast<const UnaryExpr*>(expr))\n\t{\n\t\treturn dependsOn(unary->right.get(), var);\n\t}\n\telse if (auto call = dynamic_cast<const CallExpr*>(expr))\n\t{\n\t\tfor (const auto& arg : call->arguments)\n\t\t{\n\t\t\tif (dependsOn(arg.get(), var))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse if (auto member = dynamic_cast<const MemberExpr*>(expr))\n\t{\n\t\treturn dependsOn(member->object.get(), var);\n\t}\n\telse if (auto index = dynamic_cast<const IndexExpr*>(expr))\n\t{\n\t\treturn dependsOn(index->object.get(), var) || dependsOn(index->index.get(), var);\n\t}\n\telse if (auto assign = dynamic_cast<const AssignExpr*>(expr))\n\t{\n\t\treturn dependsOn(assign->target.get(), var) || dependsOn(assign->value.get(), var);\n\t}\n\telse if (auto init = dynamic_cast<const InitExpr*>(expr))\n\t{\n\t\tfor (const auto& elem : init->elements)\n\t\t{\n\t\t\tif (dependsOn(elem.get(), var))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse if (auto constructor = dynamic_cast<const ConstructorExpr*>(expr))\n\t{\n\t\tfor (const auto& elem : constructor->args)\n\t\t{\n\t\t\tif (dependsOn(elem.get(), var))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse\n\t\tthrow std::runtime_error(\"Unsupported expression type for dependency analysis\");\n}\n\nbool febcode::dependsOn(const Statement* stmt, const std::string& varName)\n{\n\tif (auto exprStmt = dynamic_cast<const ExpressionStmt*>(stmt))\n\t{\n\t\treturn ::dependsOn(exprStmt->expr.get(), varName);\n\t}\n\telse if (auto returnStmt = dynamic_cast<const ReturnStmt*>(stmt))\n\t{\n\t\treturn ::dependsOn(returnStmt->value.get(), varName);\n\t}\n\telse if (auto structStmt = dynamic_cast<const StructStmt*>(stmt))\n\t{\n\t\treturn false;\n\t}\n\telse if (auto varDeclStmt = dynamic_cast<const VarDeclStmt*>(stmt))\n\t{\n\t\tif (!varDeclStmt->input)\n\t\t{\n\t\t\tfor (const auto& var : varDeclStmt->vars)\n\t\t\t{\n\t\t\t\tif (::dependsOn(var.initializer.get(), varName))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\telse if (auto ifStmt = dynamic_cast<const IfStmt*>(stmt))\n\t{\n\t\tif (::dependsOn(ifStmt->condition.get(), varName)) return true;\n\t\tif (dependsOn(ifStmt->thenBranch.get(), varName)) return true;\n\t\tif (ifStmt->elseBranch && dependsOn(ifStmt->elseBranch.get(), varName)) return true;\n\t\treturn false;\n\t}\n\telse if (auto block = dynamic_cast<const BlockStmt*>(stmt))\n\t{\n\t\tfor (const StmtPtr& s : block->statements)\n\t\t{\n\t\t\tif (dependsOn(s.get(), varName))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse\n\t\tthrow std::runtime_error(\"Unsupported statement type for dependency analysis\");\n}\n"
  },
  {
    "path": "febcode/differentiator.h",
    "content": "#pragma once\n#include <memory>\n#include <unordered_map>\n#include <string>\n#include \"ast.h\"\n#include \"modifier.h\"\n#include \"simplifier.h\"\n\nnamespace febcode {\n\tclass Differentiator : public Modifier {\n\n\t\tstruct DerivVar\n\t\t{\n\t\t\tstd::string name; // name of the derivative variable\n\t\t\tType type; // type of the derivative variable\n\t\t};\n\n\tpublic:\n\t\tDifferentiator(Program& prg) : Modifier(prg), simplifier(prg) {}\n\n\t\tvoid differentiate(const std::string& var);\n\n\t\tbool DependencyFound() const { return dependencyFound; }\n\n\t\tvoid SetSimplify(bool value) { doSimplify = value; }\n\n\tprivate:\n\n\t\t// differentiate an AST to produce a new AST representing the derivative\n\t\tstd::unique_ptr<AST> differentiate(const AST& ast, const DerivVar& var);\n\n\t\tvoid differentiateStmt(BlockStmt& ast, Statement* stmt, const DerivVar& var);\n\n\t\tvoid diffExpressionStmt(BlockStmt& ast, ExpressionStmt* stmt, const DerivVar& var);\n\t\tvoid diffReturnStmt    (BlockStmt& ast, ReturnStmt*     stmt, const DerivVar& var);\n\t\tvoid diffStructStmt    (BlockStmt& ast, StructStmt*     stmt, const DerivVar& var);\n\t\tvoid diffVarDeclStmt   (BlockStmt& ast, VarDeclStmt*    stmt, const DerivVar& var);\n\t\tvoid diffIfStmt        (BlockStmt& ast, IfStmt*         stmt, const DerivVar& var);\n\t\tvoid diffBlockStmt     (BlockStmt& ast, BlockStmt*      stmt, const DerivVar& var);\n\n\tprivate:\n\t\t// Differentiate an expression with respect to a variable\n\t\tstd::unique_ptr<Expression> differentiate(const Expression* expr, const DerivVar& var);\n\n\t\tstd::unique_ptr<Expression> diffLiteral    (const LiteralExpr*     literal , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffVariable   (const VariableExpr*    variable, const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffUnary      (const UnaryExpr*       unary   , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffBinary     (const BinaryExpr*      binary  , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffCall       (const CallExpr*        call    , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffInit       (const InitExpr*        init    , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffConstructor(const ConstructorExpr* ctor    , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffAssign     (const AssignExpr*      assign  , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffIndex      (const IndexExpr*       index   , const DerivVar& var);\n\t\tstd::unique_ptr<Expression> diffMember     (const MemberExpr*      member  , const DerivVar& var);\n\n\t\tType getDerivativeType(Type varType, TypeKind derivType);\n\n\t\tExprPtr simplify(const Expression* expr) \n\t\t{ \n\t\t\tif (doSimplify)\n\t\t\t\treturn simplifier.simplify(expr); \n\t\t\telse\n\t\t\t\treturn clone(expr);\n\t\t}\n\t\tExprPtr simplify(const ExprPtr& expr) { return simplify(expr.get()); }\n\n\tprivate:\n\t\tbool dependencyFound = false; // flag to indicate if we found a dependency on the variable we're differentiating with respect to\n\t\tstd::unordered_map<std::string, std::string> deriveVars; // map of derivative variables\n\t\tstd::unordered_map<std::string, Type> varTypes; // map of variable types for variables in the original program, used to determine the type of the derivative variables.\n\n\t\tbool doSimplify = true;\n\t\tSimplifier simplifier;\n\t};\n\n\tbool dependsOn(const Statement* stmt, const std::string& varName);\n\tbool dependsOn(const Expression* expr, const std::string& varName);\n} // namespace febcode\n"
  },
  {
    "path": "febcode/modifier.cpp",
    "content": "#include \"modifier.h\"\nusing namespace febcode;\n\nExprPtr Modifier::Call(const std::string& name, const std::vector<ExprPtr>& args) \n{\n\tstd::vector<ExprPtr> copyArgs;\n\tstd::vector<Type> argTypes;\n\tfor (const auto& arg : args)\n\t{\n\t\tcopyArgs.emplace_back(clone(arg.get()));\n\t\targTypes.push_back(arg->valType);\n\t}\n\n\tint index = prg.resolveFunction(name, argTypes);\n\tif (index < 0)\n\t{\n\t\tthrow std::runtime_error(\"Undefined function: \" + name);\n\t}\n\n\tType returnType = prg.functions[index].returnType;\n\n\tExprPtr c = std::make_unique<CallExpr>(name, std::move(copyArgs));\n\tc->valType = returnType;\n\treturn c;\n}\n\nExprPtr Modifier::Index(const ExprPtr& object, const ExprPtr& index)\n{\n\tif (object->valType && object->valType->kind == TypeKind::Array)\n\t{\n\t\tType elementType = object->valType->elementType;\n\t\tExprPtr i = std::make_unique<IndexExpr>(clone(object.get()), clone(index.get()));\n\t\ti->valType = elementType;\n\t\treturn i;\n\t}\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Cannot index non-array type\");\n\t}\n}\n\nExprPtr Modifier::Member(const ExprPtr& object, const std::string& property)\n{\n\tif (object->valType && object->valType->kind == TypeKind::Struct)\n\t{\n\t\tconst auto& fields = object->valType->fields;\n\t\tauto it = std::find_if(fields.begin(), fields.end(), [&](const auto& field) { return field.second == property; });\n\t\tif (it != fields.end())\n\t\t{\n\t\t\tType fieldType = it->first;\n\t\t\tExprPtr m = std::make_unique<MemberExpr>(clone(object.get()), property);\n\t\t\tm->valType = fieldType;\n\t\t\treturn m;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow std::runtime_error(\"Struct type does not have member: \" + property);\n\t\t}\n\t}\n\telse if (object->valType && object->valType->kind == TypeKind::Vec2)\n\t{\n\t\tif (property == \"x\" || property == \"y\")\n\t\t{\n\t\t\tType fieldType = prg.types.Double();\n\t\t\tExprPtr m = std::make_unique<MemberExpr>(clone(object.get()), property);\n\t\t\tm->valType = fieldType;\n\t\t\treturn m;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow std::runtime_error(\"Vec2 type does not have member: \" + property);\n\t\t}\n\t}\n\telse if (object->valType && object->valType->kind == TypeKind::Vec3)\n\t{\n\t\tif (property == \"x\" || property == \"y\" || property == \"z\")\n\t\t{\n\t\t\tType fieldType = prg.types.Double();\n\t\t\tExprPtr m = std::make_unique<MemberExpr>(clone(object.get()), property);\n\t\t\tm->valType = fieldType;\n\t\t\treturn m;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthrow std::runtime_error(\"Vec3 type does not have member: \" + property);\n\t\t}\n\t}\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Cannot access member of non-struct type\");\n\t}\n}\n\nExprPtr Modifier::Initializer(Type type)\n{\n\tif ((type == nullptr) || (type->kind != TypeKind::Array) || (type->elementType == nullptr))\n\t{\n\t\tthrow std::runtime_error(\"Invalid type in Initializer\");\n\t}\n\n\tType elementType = type->elementType;\n\tsize_t arraySize = type->arraySize;\n\n\tstd::vector<ExprPtr> elements;\n\tfor (int i = 0; i < arraySize; ++i)\n\t{\n\t\telements.push_back(Zero(elementType));\n\t}\n\n\tExprPtr init = std::make_unique<InitExpr>(std::move(elements));\n\tinit->valType = type;\n\treturn init;\n}\n\nExprPtr Modifier::Constructor(Type type)\n{\n\tif ((type == nullptr) || (type->kind != TypeKind::Struct))\n\t{\n\t\tthrow std::runtime_error(\"Invalid type in Constructor\");\n\t}\n\n\tstd::vector<ExprPtr> init;\n\tfor (const auto& field : type->fields)\n\t{\n\t\tinit.push_back(Zero(field.first));\n\t}\n\tExprPtr i = std::make_unique<ConstructorExpr>(type, std::move(init));\n\treturn i;\n}\n\nExprPtr Modifier::Zero(Type type)\n{\n\tswitch (type->kind)\n\t{\n\tcase TypeKind::Bool  : return Literal(false);\n\tcase TypeKind::Int   : return Literal(0);\n\tcase TypeKind::Double: return Literal(0.0);\n\tcase TypeKind::Vec2  : return Literal(vec2());\n\tcase TypeKind::Vec3  : return Literal(vec3());\n\tcase TypeKind::Mat2  : return Literal(mat2());\n\tcase TypeKind::Mat3  : return Literal(mat3());\n\tcase TypeKind::Array : return Initializer(type);\n\tcase TypeKind::Struct: return Constructor(type);\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported type for Zero\");\n\t}\n}\n\nExprPtr Modifier::Unary(UnaryOp op, const Expression* arg)\n{\n\tExprPtr u = std::make_unique<UnaryExpr>(op, clone(arg));\n\tu->valType = arg->valType;\n\treturn u;\n}\n\nExprPtr Modifier::Binary(BinaryOp op, const Expression* left, const Expression* right)\n{\n\t// get the signature for this operator and operand types (this throws if the operator is not defined for these types)\n\tBinaryOpSignature sig = prg.resolveBinaryOp(op, left->valType, right->valType);\n\tExprPtr b = std::make_unique<BinaryExpr>(std::move(clone(left)), op, std::move(clone(right)));\n\tb->valType = sig.resultType;\n\treturn b;\n}\n"
  },
  {
    "path": "febcode/modifier.h",
    "content": "#pragma once\n#include \"program.h\"\n#include <vector>\n#include <memory>\n\nnamespace febcode\n{\n\t// base class for AST modifiers. This is used for both differentiation and simplification, since they both modify the AST in some way. \n\t// It provides a common interface for applying modifications to an AST, and it can be extended to implement specific modifications like differentiation or simplification.\n\tclass Modifier\n\t{\n\tpublic:\n\t\tModifier(Program& prg) : prg(prg) {}\n\n\tprotected:\n\t\t// helper functions for creating (typed) expressions more easily\n\t\tExprPtr Literal(bool   b) { ExprPtr e = std::make_unique<LiteralExpr>(Value(b)); e->valType = prg.types.Bool  (); return e; }\n\t\tExprPtr Literal(int    n) { ExprPtr e = std::make_unique<LiteralExpr>(Value(n)); e->valType = prg.types.Int   (); return e; }\n\t\tExprPtr Literal(double a) { ExprPtr e = std::make_unique<LiteralExpr>(Value(a)); e->valType = prg.types.Double(); return e; }\n\t\tExprPtr Literal(vec2   a) { ExprPtr e = std::make_unique<LiteralExpr>(Value(a)); e->valType = prg.types.Vec2  (); return e; }\n\t\tExprPtr Literal(vec3   a) { ExprPtr e = std::make_unique<LiteralExpr>(Value(a)); e->valType = prg.types.Vec3  (); return e; }\n\t\tExprPtr Literal(mat2   a) { ExprPtr e = std::make_unique<LiteralExpr>(Value(a)); e->valType = prg.types.Mat2  (); return e; }\n\t\tExprPtr Literal(mat3   a) { ExprPtr e = std::make_unique<LiteralExpr>(Value(a)); e->valType = prg.types.Mat3  (); return e; }\n\n\t\tExprPtr Variable(const std::string& name, Type type) { ExprPtr v = std::make_unique<VariableExpr>(name); v->valType = type; return v; }\n\n\t\tExprPtr Assign(const ExprPtr& target, const ExprPtr& value) { ExprPtr a = std::make_unique<AssignExpr>(clone(target.get()), clone(value.get())); a->valType = target->valType; return a; }\n\n\t\tExprPtr Call(const std::string& name, const std::vector<ExprPtr>& args);\n\n\t\tExprPtr Index(const ExprPtr& object, const ExprPtr& index);\n\n\t\tExprPtr Member(const ExprPtr& object, const std::string& property);\n\n\t\tExprPtr Negate(const Expression* arg) { ExprPtr n = std::make_unique<UnaryExpr>(UnaryOp::Negate, clone(arg)); n->valType = arg->valType; return n; }\n\t\tExprPtr Negate(const ExprPtr& arg) { return Negate(arg.get()); }\n\n\t\t// create zero initializer for array\n\t\tExprPtr Initializer(Type type);\n\n\t\t// create zero constructor for struct\n\t\tExprPtr Constructor(Type type);\n\n\t\t// make a literal expression of type with zero values.\n\t\tExprPtr Zero(Type type);\n\n\t\t// create unary expression with the given operator and operand.\n\t\tExprPtr Unary(UnaryOp op, const Expression* arg);\n\t\tExprPtr Unary(UnaryOp op, const ExprPtr& arg) { return Unary(op, arg.get()); }\n\n\t\t// create a binary expression with the given operator and operands.\n\t\tExprPtr Binary(BinaryOp op, const Expression* left, const Expression* right);\n\t\tExprPtr Binary(BinaryOp op, const ExprPtr& left, const ExprPtr& right)\n\t\t{\n\t\t\treturn Binary(op, left.get(), right.get());\n\t\t}\n\n\t\tExprPtr Mul(const Expression* left, const Expression* right)\n\t\t{\n\t\t\treturn Binary(BinaryOp::Multiply, left, right);\n\t\t}\n\n\t\tfebcode::ExprPtr OuterProduct(const febcode::ExprPtr& left, const febcode::ExprPtr& right)\n\t\t{\n\t\t\tstd::vector<febcode::ExprPtr> args(2);\n\t\t\targs[0] = clone(left.get());\n\t\t\targs[1] = clone(right.get());\n\t\t\treturn Call(\"outer\", std::move(args));\n\t\t}\n\n\t\tfebcode::ExprPtr Transpose(const febcode::ExprPtr& arg)\n\t\t{\n\t\t\tstd::vector<febcode::ExprPtr> args(1);\n\t\t\targs[0] = clone(arg.get());\n\t\t\treturn Call(\"transpose\", std::move(args));\n\t\t}\n\n\t\tfebcode::ExprPtr Inverse(const febcode::ExprPtr& arg)\n\t\t{\n\t\t\tstd::vector<febcode::ExprPtr> args(1);\n\t\t\targs[0] = clone(arg.get());\n\t\t\treturn Call(\"inverse\", std::move(args));\n\t\t}\n\n\t\tfebcode::ExprPtr Pow(const febcode::ExprPtr& left, const febcode::ExprPtr& right)\n\t\t{ \n\t\t\treturn Binary(febcode::BinaryOp::Exponent, left, right); \n\t\t}\n\n\t\tfebcode::ExprPtr Add(const febcode::ExprPtr& left, const febcode::ExprPtr& right) { return Binary(febcode::BinaryOp::Plus    , left, right); }\n\t\tfebcode::ExprPtr Sub(const febcode::ExprPtr& left, const febcode::ExprPtr& right) { return Binary(febcode::BinaryOp::Minus   , left, right); }\n\t\tfebcode::ExprPtr Mul(const febcode::ExprPtr& left, const febcode::ExprPtr& right) { return Binary(febcode::BinaryOp::Multiply, left, right); }\n\t\tfebcode::ExprPtr Div(const febcode::ExprPtr& left, const febcode::ExprPtr& right) { return Binary(febcode::BinaryOp::Divide  , left, right); }\n\n\tprotected:\n\t\tProgram& prg;\n\t};\n}\n"
  },
  {
    "path": "febcode/module.h",
    "content": "#pragma once\n#include \"program.h\"\n\nnamespace febcode \n{\n\tclass Module\n\t{\n\tpublic:\n\t\tvirtual void Register(Program& prg) = 0;\n\t\tvirtual ~Module() = default;\n\t};\n}\n"
  },
  {
    "path": "febcode/module_mat2.cpp",
    "content": "#include \"module_mat2.h\"\nusing namespace febcode;\n\nValue febcode::TransposeMat2(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst mat2& m = args.getMat2();\n\treturn mat2(\n\t\tm.m[0][0], m.m[1][0],\n\t\tm.m[0][1], m.m[1][1]\n\t);\n}\n\nValue febcode::InvertMat2(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst mat2& m = args.getMat2();\n\tdouble a = m.m[0][0];\n\tdouble b = m.m[0][1];\n\tdouble c = m.m[1][0];\n\tdouble d = m.m[1][1];\n\tdouble det = a * d - b * c;\n\tdouble invDet = 1.0 / det;\n\treturn mat2(\n\t\td * invDet, -b * invDet,\n\t\t-c * invDet, a * invDet\n\t);\n}\n"
  },
  {
    "path": "febcode/module_mat2.h",
    "content": "#pragma once\n#include \"module.h\"\n\nnamespace febcode\n{\n\tValue TransposeMat2(FuncArgs args);\n\n\tValue InvertMat2(FuncArgs args);\n\n\tclass Mat2Module : public Module\n\t{\n\tpublic:\n\t\tvoid Register(Program& prg) override\n\t\t{\n\t\t\tType bool_t = prg.types.Bool();\n\t\t\tType double_t = prg.types.Double();\n\t\t\tType vec2_t = prg.types.Vec2();\n\t\t\tType mat2_t = prg.types.Mat2();\n\n\t\t\t// binary operators                            LHS     RHS    Result\n\t\t\tprg.binaryOps[BinaryOp::Plus    ].push_back({ mat2_t, mat2_t, mat2_t });\n\t\t\tprg.binaryOps[BinaryOp::Minus   ].push_back({ mat2_t, mat2_t, mat2_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ mat2_t, mat2_t, mat2_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ mat2_t, vec2_t, vec2_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ mat2_t, double_t, mat2_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ double_t, mat2_t, mat2_t });\n\t\t\tprg.binaryOps[BinaryOp::Divide  ].push_back({ mat2_t, double_t, mat2_t });\n\n\t\t\tprg.binaryOps[BinaryOp::EqualEqual].push_back({ mat2_t, mat2_t, bool_t });\n\t\t\tprg.binaryOps[BinaryOp::NotEqual  ].push_back({ mat2_t, mat2_t, bool_t });\n\n\t\t\tprg.registerNative(\"transpose\", mat2_t, { mat2_t }, TransposeMat2);\n\t\t\tprg.registerNative(\"inverse\", mat2_t, { mat2_t }, InvertMat2);\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "febcode/module_mat3.cpp",
    "content": "#include \"module_mat3.h\"\nusing namespace febcode;\n\nValue febcode::TransposeMat3(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst mat3& m = args.getMat3();\n\treturn mat3(\n\t\tm.m[0][0], m.m[1][0], m.m[2][0],\n\t\tm.m[0][1], m.m[1][1], m.m[2][1],\n\t\tm.m[0][2], m.m[1][2], m.m[2][2]\n\t);\n}\n\nValue febcode::InvertMat3(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst mat3& m = args.getMat3();\n\tconst double (*a)[3] = m.m; // for easier access to elements\n\n\tdouble det = a[0][0] * (a[1][1] * a[2][2] - a[1][2] * a[2][1]) -\n\t\t\t\t a[0][1] * (a[1][0] * a[2][2] - a[1][2] * a[2][0]) +\n\t\t\t\t a[0][2] * (a[1][0] * a[2][1] - a[1][1] * a[2][0]);\n\n\tmat3 mInv;\n\tdouble (*b)[3] = mInv.m; // for easier access to elements\n\tb[0][0] = (a[1][1] * a[2][2] - a[1][2] * a[2][1]) / det;\n\tb[0][1] = (a[0][2] * a[2][1] - a[0][1] * a[2][2]) / det;\n\tb[0][2] = (a[0][1] * a[1][2] - a[0][2] * a[1][1]) / det;\n\n\tb[1][0] = (a[1][2] * a[2][0] - a[1][0] * a[2][2]) / det;\n\tb[1][1] = (a[0][0] * a[2][2] - a[0][2] * a[2][0]) / det;\n\tb[1][2] = (a[0][2] * a[1][0] - a[0][0] * a[1][2]) / det;\n\n\tb[2][0] = (a[1][0] * a[2][1] - a[1][1] * a[2][0]) / det;\n\tb[2][1] = (a[0][1] * a[2][0] - a[0][0] * a[2][1]) / det;\n\tb[2][2] = (a[0][0] * a[1][1] - a[0][1] * a[1][0]) / det;\n\n\treturn mInv;\n}\n\nValue febcode::OuterVec3(FuncArgs args)\n{\n\tassert(args.count == 2);\n\tconst vec3& a = args.getVec3();\n\tconst vec3& b = args.getVec3();\n\treturn mat3(\n\t\ta.x * b.x, a.x * b.y, a.x * b.z,\n\t\ta.y * b.x, a.y * b.y, a.y * b.z,\n\t\ta.z * b.x, a.z * b.y, a.z * b.z\n\t);\n}\n"
  },
  {
    "path": "febcode/module_mat3.h",
    "content": "#pragma once\n#include \"module.h\"\n\nnamespace febcode\n{\n\tValue TransposeMat3(FuncArgs args);\n\n\tValue InvertMat3(FuncArgs args);\n\n\t// outer product = outer(vec3, vec3) -> mat3\n\tValue OuterVec3(FuncArgs args);\n\n\tclass Mat3Module : public Module\n\t{\n\tpublic:\n\t\tvoid Register(Program& prg) override\n\t\t{\n\t\t\tType bool_t = prg.types.Bool();\n\t\t\tType double_t = prg.types.Double();\n\t\t\tType vec3_t = prg.types.Vec3();\n\t\t\tType mat3_t = prg.types.Mat3();\n\n\t\t\t// binary operators                            LHS     RHS    Result\n\t\t\tprg.binaryOps[BinaryOp::Plus    ].push_back({ mat3_t, mat3_t, mat3_t });\n\t\t\tprg.binaryOps[BinaryOp::Minus   ].push_back({ mat3_t, mat3_t, mat3_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ mat3_t, mat3_t, mat3_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ mat3_t, vec3_t, vec3_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ mat3_t, double_t, mat3_t });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ double_t, mat3_t, mat3_t });\n\t\t\tprg.binaryOps[BinaryOp::Divide  ].push_back({ mat3_t, double_t, mat3_t });\n\n\t\t\tprg.binaryOps[BinaryOp::EqualEqual].push_back({ mat3_t, mat3_t, bool_t });\n\t\t\tprg.binaryOps[BinaryOp::NotEqual  ].push_back({ mat3_t, mat3_t, bool_t });\n\n\t\t\tprg.registerNative(\"transpose\", mat3_t, { mat3_t }, TransposeMat3);\n\t\t\tprg.registerNative(\"inverse\", mat3_t, { mat3_t }, InvertMat3);\n\t\t\tprg.registerNative(\"outer\", mat3_t, { vec3_t, vec3_t }, OuterVec3);\n\t\t}\n\t};\n}\n\n"
  },
  {
    "path": "febcode/module_math.h",
    "content": "#pragma once\n#include \"module.h\"\n\nnamespace febcode {\n\n\tclass MathModule : public Module\n\t{\n\tpublic:\n\t\tvoid Register(Program& prg) override\n\t\t{\n\t\t\t// Math functions\n\t\t\tprg.registerNative(\"abs\"  , std::abs);\n\t\t\tprg.registerNative(\"acos\" , std::acos);\n\t\t\tprg.registerNative(\"acosh\", std::acosh);\n\t\t\tprg.registerNative(\"asin\" , std::asin);\n\t\t\tprg.registerNative(\"asinh\", std::asinh);\n\t\t\tprg.registerNative(\"atan\" , std::atan);\n\t\t\tprg.registerNative(\"atanh\", std::atanh);\n\t\t\tprg.registerNative(\"cos\"  , std::cos);\n\t\t\tprg.registerNative(\"cosh\" , std::cosh);\n\t\t\tprg.registerNative(\"exp\"  , std::exp);\n\t\t\tprg.registerNative(\"log\"  , std::log);\n\t\t\tprg.registerNative(\"log10\", std::log10);\n\t\t\tprg.registerNative(\"sin\"  , std::sin);\n\t\t\tprg.registerNative(\"sinh\" , std::sinh);\n\t\t\tprg.registerNative(\"sqrt\" , std::sqrt);\n\t\t\tprg.registerNative(\"tan\"  , std::tan);\n\t\t\tprg.registerNative(\"tanh\" , std::tanh);\n\t\t}\n\t};\n}"
  },
  {
    "path": "febcode/module_vec2.cpp",
    "content": "#include \"module_vec2.h\"\n#include <assert.h>\n\nusing namespace febcode;\n\nValue febcode::DotVec2(FuncArgs args)\n{\n\tassert(args.count == 2);\n\tvec2 a = args.getVec2();\n\tvec2 b = args.getVec2();\n\treturn a*b;\n}\n\nValue febcode::LengthVec2(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst vec2& a = args.getVec2();\n\treturn std::sqrt(a.x*a.x + a.y*a.y);\n}\n\nValue febcode::NormalizeVec2(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst vec2& a = args.getVec2();\n\n\tdouble x = a.x;\n\tdouble y = a.y;\n\n\tdouble D = std::sqrt(x * x + y * y);\n\tif (D != 0) {\n\t\tx /= D;\n\t\ty /= D;\n\t}\n\n\treturn vec2(x, y);\n}\n\nValue febcode::OuterVec2(FuncArgs args)\n{\n\tassert(args.count == 2);\n\tconst vec2& a = args.getVec2();\n\tconst vec2& b = args.getVec2();\n\treturn mat2(\n\t\ta.x * b.x, a.x * b.y,\n\t\ta.y * b.x, a.y * b.y\n\t);\n}\n"
  },
  {
    "path": "febcode/module_vec2.h",
    "content": "#pragma once\n#include \"module.h\"\n\nnamespace febcode\n{\n\t// double = dot(vec2)\n\tValue DotVec2(FuncArgs args);\n\n\t// double = length(vec2)\n\tValue LengthVec2(FuncArgs args);\n\n\t// normalized = Normalize(vec2)\n\tValue NormalizeVec2(FuncArgs);\n\n\t// outer product = outer(vec2, vec2) -> mat2\n\tValue OuterVec2(FuncArgs args);\n\n\tclass Vec2Module : public Module\n\t{\n\tpublic:\n\t\tvoid Register(Program& prg) override\n\t\t{\n\t\t\tType bool_t = prg.types.Bool();\n\t\t\tType doubleType = prg.types.Double();\n\t\t\tType vec2Type = prg.types.Vec2();\n\t\t\tType mat2Type = prg.types.Mat2();\n\n\t\t\t// binary operators                            LHS       RHS       Result\n\t\t\tprg.binaryOps[BinaryOp::Plus    ].push_back({ vec2Type, vec2Type, vec2Type });\n\t\t\tprg.binaryOps[BinaryOp::Minus   ].push_back({ vec2Type, vec2Type, vec2Type });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ vec2Type, doubleType, vec2Type });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ doubleType, vec2Type, vec2Type });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ vec2Type, vec2Type, doubleType }); // dot product\n\t\t\tprg.binaryOps[BinaryOp::Divide  ].push_back({ vec2Type, doubleType, vec2Type });\n\n\t\t\tprg.binaryOps[BinaryOp::EqualEqual].push_back({ vec2Type, vec2Type, bool_t });\n\t\t\tprg.binaryOps[BinaryOp::NotEqual  ].push_back({ vec2Type, vec2Type, bool_t });\n\n\t\t\tprg.registerNative(\"dot\"      , doubleType, { vec2Type, vec2Type }, DotVec2);\n\t\t\tprg.registerNative(\"length\"   , doubleType, { vec2Type }, LengthVec2);\n\t\t\tprg.registerNative(\"normalize\", vec2Type, { vec2Type }, NormalizeVec2);\n\t\t\tprg.registerNative(\"outer\"    , mat2Type, { vec2Type, vec2Type }, OuterVec2);\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "febcode/module_vec3.cpp",
    "content": "#include \"module_vec3.h\"\n\nusing namespace febcode;\n\nValue febcode::DotVec3(FuncArgs args)\n{\n\tassert(args.count == 2);\n\tconst vec3& a = args.getVec3();\n\tconst vec3& b = args.getVec3();\n\treturn a*b;\n}\n\nValue febcode::CrossVec3(FuncArgs args)\n{\n\tassert(args.count == 2);\n\tconst vec3& a = args.getVec3();\n\tconst vec3& b = args.getVec3();\n\treturn a.cross(b);\n}\n\nValue febcode::NormalizeVec3(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst vec3& a = args.getVec3();\n\tdouble x = a.x;\n\tdouble y = a.y;\n\tdouble z = a.z;\n\n\tdouble D = std::sqrt(x * x + y * y + z * z);\n\tif (D != 0) {\n\t\tx /= D;\n\t\ty /= D;\n\t\tz /= D;\n\t}\n\n\treturn vec3(x,y,z);\n}\n\nValue febcode::LengthVec3(FuncArgs args)\n{\n\tassert(args.count == 1);\n\tconst vec3& a = args.getVec3();\n\treturn std::sqrt(a.x*a.x + a.y*a.y + a.z*a.z);\n}\n"
  },
  {
    "path": "febcode/module_vec3.h",
    "content": "#pragma once\n#include \"module.h\"\n\nnamespace febcode\n{\n\t// vec3 = dot(vec3, vec3)\n\tValue DotVec3(FuncArgs args);\n\n\t// vec3 = cross(vec3, vec3)\n\tValue CrossVec3(FuncArgs args);\n\n\t// normalized = Normalize(vec3)\n\tValue NormalizeVec3(FuncArgs args);\n\n\t// double length (vec3)\n\tValue LengthVec3(FuncArgs args);\n\n\tclass Vec3Module : public Module\n\t{\n\tpublic:\n\t\tvoid Register(Program& prg) override\n\t\t{\n\t\t\tType bool_t = prg.types.Bool();\n\t\t\tType doubleType = prg.types.Double();\n\t\t\tType vec3Type = prg.types.Vec3();\n\t\t\tType mat3Type = prg.types.Mat3();\n\n\t\t\t// binary operators                            LHS       RHS       Result\n\t\t\tprg.binaryOps[BinaryOp::Plus    ].push_back({ vec3Type, vec3Type, vec3Type });\n\t\t\tprg.binaryOps[BinaryOp::Minus   ].push_back({ vec3Type, vec3Type, vec3Type });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ vec3Type, doubleType, vec3Type });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ doubleType, vec3Type, vec3Type });\n\t\t\tprg.binaryOps[BinaryOp::Multiply].push_back({ vec3Type, vec3Type, doubleType }); // dot product\n\t\t\tprg.binaryOps[BinaryOp::Divide  ].push_back({ vec3Type, doubleType, vec3Type });\n\n\t\t\tprg.binaryOps[BinaryOp::EqualEqual].push_back({ vec3Type, vec3Type, bool_t });\n\t\t\tprg.binaryOps[BinaryOp::NotEqual  ].push_back({ vec3Type, vec3Type, bool_t });\n\n\t\t\t// Register native functions\n\t\t\tprg.registerNative(\"dot\"      , doubleType, { vec3Type, vec3Type }, DotVec3);\n\t\t\tprg.registerNative(\"cross\"    , vec3Type, { vec3Type, vec3Type }, CrossVec3);\n\t\t\tprg.registerNative(\"normalize\", vec3Type, { vec3Type }, NormalizeVec3);\n\t\t\tprg.registerNative(\"length\"   , doubleType, { vec3Type }, LengthVec3);\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "febcode/optimizer.cpp",
    "content": "#include \"optimizer.h\"\nusing namespace febcode;\n\nOptimizer::Optimizer(Program& program) : Modifier(program) {}\n\nvoid Optimizer::optimize()\n{\n\tAST& ast = *prg.ast;\n\tfor (int i = (int)ast.root.statements.size() - 1; i >= 0; --i)\n\t{\n\t\tStatement* stmt = ast.root.statements[i].get();\n\n\t\tif (shouldRemove(stmt))\n\t\t{\n\t\t\tast.root.statements.erase(ast.root.statements.begin() + i);\n\t\t}\n\t}\n}\n\nbool Optimizer::shouldRemove(Statement* stmt)\n{\n\tif (auto retStmt  = dynamic_cast<ReturnStmt*    >(stmt)) return shouldRemoveReturn   (retStmt);\n\tif (auto varDecl  = dynamic_cast<VarDeclStmt*   >(stmt)) return shouldRemoveVarDecl  (varDecl);\n\tif (auto exprStmt = dynamic_cast<ExpressionStmt*>(stmt)) return shouldRemoveExprStmt (exprStmt);\n\tif (auto blckStmt = dynamic_cast<BlockStmt     *>(stmt)) return shouldRemoveBlockStmt(blckStmt);\n\tif (auto ifStmt   = dynamic_cast<IfStmt        *>(stmt)) return shouldRemoveIfStmt   (ifStmt  );\n\n\tthrow std::runtime_error(\"Unsupported statement type in optimizer\");\n}\n\nbool Optimizer::shouldRemoveReturn(ReturnStmt* stmt)\n{\n\tupdateLiveness(stmt->value.get());\n\t// don't remove return statements.\n\treturn false;\n}\n\nbool Optimizer::shouldRemoveVarDecl(VarDeclStmt* stmt)\n{\n\t// remove unused variables.\n\tfor (int i = (int)stmt->vars.size() - 1; i >= 0; i--)\n\t{\n\t\tauto& var = stmt->vars[i];\n\t\tif (live.find(var.name) == live.end())\n\t\t{\n\t\t\tif (assignedLater.find(var.name) != assignedLater.end())\n\t\t\t{\n\t\t\t\t// the variable is assigned later, so we can't remove the declaration, \n\t\t\t\t// but we can remove the initializer if it exists.\n\t\t\t\tassignedLater.erase(var.name);\n\t\t\t\tvar.initializer.reset();\n\t\t\t}\n\t\t\telse\n\t\t\t\tstmt->vars.erase(stmt->vars.begin() + i);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlive.erase(var.name);\n\t\t\tupdateLiveness(var.initializer.get());\n\t\t}\n\t}\n\treturn stmt->vars.empty();\n}\n\nbool Optimizer::shouldRemoveExprStmt(ExpressionStmt* stmt)\n{\n\tExprPtr& expr = stmt->expr;\n\n\tif (expr->exprType == ExpressionType::Assignment)\n\t{\n\t\tconst auto* assignExpr = static_cast<const AssignExpr*>(expr.get());\n\n\t\tif (assignExpr->target->exprType == ExpressionType::Variable)\n\t\t{\n\t\t\tconst auto* varExpr = static_cast<const VariableExpr*>(assignExpr->target.get());\n\t\t\tif (live.find(varExpr->name) == live.end())\n\t\t\t{\n\t\t\t\t// the variable is not live, so we can remove the assignment, but\n\t\t\t\t// only if the value being assigned doesn't have side effects, otherwise we need to keep the assignment for the side effects.\n\t\t\t\tif (hasSideEffects(assignExpr->value.get()))\n\t\t\t\t{\n\t\t\t\t\tassignedLater.insert(varExpr->name);\n\t\t\t\t\tupdateLiveness(assignExpr->value.get());\n\t\t\t\t\treturn false; // keep the assignment for the side effects, even though the variable is not live.\n\t\t\t\t}\n\t\t\t\tupdateLiveness(assignExpr->value.get());\n\t\t\t\treturn !hasSideEffects(assignExpr->value.get()); // remove this statement if it doesn't have side effects.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tassignedLater.insert(varExpr->name);\n\t\t\t\tlive.erase(varExpr->name);\n\t\t\t\tupdateLiveness(assignExpr->value.get());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif (assignExpr->target->exprType == ExpressionType::Member)\n\t\t{\n\t\t\t// for member assignments, we need to check if the object is live, since the assignment has side effects on the object.\n\t\t\tconst auto* memberExpr = static_cast<const MemberExpr*>(assignExpr->target.get());\n\t\t\tupdateLiveness(memberExpr->object.get());\n\t\t\tupdateLiveness(assignExpr->value.get());\n\t\t\treturn false; // don't remove member assignments, since they have side effects on the object.\n\t\t}\n\t}\n\n\tupdateLiveness(expr.get());\n\treturn !hasSideEffects(expr.get());\n}\n\nbool Optimizer::shouldRemoveBlockStmt(BlockStmt* blckStmt)\n{\n\tfor (int i = (int)blckStmt->statements.size() - 1; i >= 0; --i)\n\t{\n\t\tStatement* stmt = blckStmt->statements[i].get();\n\n\t\tif (shouldRemove(stmt))\n\t\t{\n\t\t\tblckStmt->statements.erase(blckStmt->statements.begin() + i);\n\t\t}\n\t}\n\treturn blckStmt->statements.empty();\n}\n\nbool Optimizer::shouldRemoveIfStmt(IfStmt* stmt)\n{\n\tauto liveBefore = live; // save live variables before processing branches\n\n\tbool removeThen = shouldRemove(stmt->thenBranch.get());\n\tauto liveThen = live;\n\n\tbool removeElse = true;\n\tif (stmt->elseBranch)\n\t{\n\t\tlive = liveBefore; // reset live variables before processing else branch\n\t\tbool removeElse = shouldRemove(stmt->elseBranch.get());\n\t}\n\n\tif (removeThen && removeElse && !hasSideEffects(stmt->condition.get()))\n\t{\n\t\tlive = liveBefore; // restore live variables before processing branches\n\t\treturn true; // remove the entire if statement\n\t}\n\telse\n\t{\n\t\t// merge live variables from both branches\n\t\tfor (const auto& var : liveThen)\n\t\t\tlive.insert(var);\n\n\t\tupdateLiveness(stmt->condition.get());\n\n\t\t// remove else branch if not needed\n\t\tif (removeElse)\n\t\t\tstmt->elseBranch.reset();\n\n\t\treturn false;\n\t}\n}\n\nvoid Optimizer::updateLiveness(Expression* expr) \n{\n\tif (!expr) return;\n\tswitch (expr->exprType)\n\t{\n\tcase ExpressionType::Literal:\n\t\tbreak;\n\tcase ExpressionType::Variable:\n\t{\n\t\tconst auto* varExpr = static_cast<const VariableExpr*>(expr);\n\t\tlive.insert(varExpr->name);\n\t\tbreak;\n\t}\n\tcase ExpressionType::Member:\n\t{\n\t\tconst auto* memberExpr = static_cast<const MemberExpr*>(expr);\n\t\tupdateLiveness(memberExpr->object.get());\n\t\tbreak;\n\t}\n\tcase ExpressionType::Index:\n\t{\n\t\tconst auto* indexExpr = static_cast<const IndexExpr*>(expr);\n\t\tupdateLiveness(indexExpr->object.get());\n\t\tupdateLiveness(indexExpr->index.get());\n\t\tbreak;\n\t}\n\tcase ExpressionType::Binary:\n\t{\n\t\tconst auto* binaryExpr = static_cast<const BinaryExpr*>(expr);\n\t\tupdateLiveness(binaryExpr->left.get());\n\t\tupdateLiveness(binaryExpr->right.get());\n\t\tbreak;\n\t}\n\tcase ExpressionType::Unary:\n\t{\n\t\tconst auto* unaryExpr = static_cast<const UnaryExpr*>(expr);\n\t\tupdateLiveness(unaryExpr->right.get());\n\t\tbreak;\n\t}\n\tcase ExpressionType::Call:\n\t{\n\t\tconst auto* callExpr = static_cast<const CallExpr*>(expr);\n\t\tfor (const auto& arg : callExpr->arguments)\n\t\t\tupdateLiveness(arg.get());\n\t\tbreak;\n\t}\n\tcase ExpressionType::Constructor:\n\t{\n\t\tconst auto* ctorExpr = static_cast<const ConstructorExpr*>(expr);\n\t\tfor (const auto& arg : ctorExpr->args)\n\t\t\tupdateLiveness(arg.get());\n\t\tbreak;\n\t}\n\tcase ExpressionType::Initializer:\n\t{\n\t\tconst auto* initExpr = static_cast<const InitExpr*>(expr);\n\t\tfor (const auto& element : initExpr->elements)\n\t\t\tupdateLiveness(element.get());\n\t\tbreak;\n\t}\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported expression type in optimizer\");\n\t\tbreak; // For now, we won't handle these expression types\n\t}\n}\n\n\nbool Optimizer::hasSideEffects(Expression* expr)\n{\n\tif (!expr) return false;\n\tswitch (expr->exprType)\n\t{\n\tcase ExpressionType::Literal:\n\tcase ExpressionType::Variable:\n\tcase ExpressionType::Member:\n\t\treturn false;\n\tcase ExpressionType::Index:\n\t{\n\t\tconst auto* indexExpr = static_cast<const IndexExpr*>(expr);\n\t\treturn hasSideEffects(indexExpr->object.get()) || hasSideEffects(indexExpr->index.get());\n\t}\n\tcase ExpressionType::Binary:\n\t{\n\t\tconst auto* binaryExpr = static_cast<const BinaryExpr*>(expr);\n\t\treturn hasSideEffects(binaryExpr->left.get()) || hasSideEffects(binaryExpr->right.get());\n\t}\n\tcase ExpressionType::Unary:\n\t{\n\t\tconst auto* unaryExpr = static_cast<const UnaryExpr*>(expr);\n\t\treturn hasSideEffects(unaryExpr->right.get());\n\t}\n\tcase ExpressionType::Initializer:\n\t{\n\t\tconst auto* initExpr = static_cast<const InitExpr*>(expr);\n\t\tfor (const auto& element : initExpr->elements)\n\t\t{\n\t\t\tif (hasSideEffects(element.get()))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tcase ExpressionType::Constructor:\n\t{\n\t\tconst auto* ctorExpr = static_cast<const ConstructorExpr*>(expr);\n\t\tfor (const auto& arg : ctorExpr->args)\n\t\t{\n\t\t\tif (hasSideEffects(arg.get()))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tcase ExpressionType::Assignment:\n\t\treturn true; // assignments have side effects\n\tcase ExpressionType::Call:\n\t\treturn true; // assume all function calls have side effects for simplicity\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported expression type in optimizer\");\n\t}\n}\n"
  },
  {
    "path": "febcode/optimizer.h",
    "content": "#pragma once\n#include <unordered_set>\n#include \"ast.h\"\n#include \"modifier.h\"\n\nnamespace febcode {\n\tclass Optimizer : public Modifier {\n\tpublic:\n\t\tOptimizer(Program& program);\n\n\t\tvoid optimize();\n\n\tprivate:\n\t\tbool shouldRemove(Statement* stmt);\n\t\tvoid updateLiveness(Expression* expr);\n\t\tbool hasSideEffects(Expression* expr);\n\n\tprivate:\n\t\tbool shouldRemoveReturn   (ReturnStmt*     stmt);\n\t\tbool shouldRemoveVarDecl  (VarDeclStmt*    stmt);\n\t\tbool shouldRemoveExprStmt (ExpressionStmt* stmt);\n\t\tbool shouldRemoveBlockStmt(BlockStmt*      stmt);\n\t\tbool shouldRemoveIfStmt   (IfStmt*         stmt);\n\n\tprivate:\n\t\tstd::unordered_set<std::string> live; // variables that are currently live (used in the future)\n\t\tstd::unordered_set<std::string> assignedLater; // variables that are re-assigned later\n\t};\n}\n"
  },
  {
    "path": "febcode/parser.cpp",
    "content": "#include \"parser.h\"\n#include \"resolver.h\"\n#include <iostream>\nusing namespace febcode;\n\nstd::unique_ptr<febcode::Statement> Parser::parseDeclaration() {\n\tif (match(TokenType::Input)) \n\t{\n\t\tif (!isType()) {\n\t\t\tthrow std::runtime_error(\"Expected type after 'in'.\");\n\t\t}\n\n\t\tType type = prg.types.getType(lexeme(previous()));\n\t\tif (type == nullptr)\n\t\t\tthrow std::runtime_error(\"Unknown type name\");\n\n\t\tif (!match(TokenType::Identifier)) {\n\t\t\tthrow std::runtime_error(\"Expected identifier after type.\");\n\t\t}\n\n\t\tstd::string name = lexeme(previous());\n\n\t\tType varType = type;\n\n\t\tstd::vector<size_t> arraySizes;\n\t\twhile (match(TokenType::LeftBrack))\n\t\t{\n\t\t\tif (!match(TokenType::Integer))\n\t\t\t\tthrow std::runtime_error(\"Expected array size after '['.\");\n\n\t\t\tint size = std::stoul(lexeme(previous()));\n\t\t\tif (size == 0)\n\t\t\t\tthrow std::runtime_error(\"Array size must be greater than zero.\");\n\n\t\t\tif (!match(TokenType::RightBrack))\n\t\t\t\tthrow std::runtime_error(\"Expected ']' after array size.\");\n\n\t\t\tarraySizes.push_back(size);\n\t\t}\n\n\t\tfor (int i = (int)arraySizes.size() - 1; i >= 0; --i)\n\t\t{\n\t\t\tvarType = prg.types.getArrayType(varType, arraySizes[i]);\n\t\t}\n\n\t\tif (!match(TokenType::Semicolon)) {\n\t\t\tthrow std::runtime_error(\"Expected ';' after input declaration.\");\n\t\t}\n\n\t\tVar var{ name, {}, nullptr };\n\t\treturn std::make_unique<VarDeclStmt>(varType, var, true);\n\t}\n\telse if (isType())\n\t{\n\t\tType type = prg.types.getType(lexeme(previous()));\n\t\tif (type == nullptr)\n\t\t\tthrow std::runtime_error(\"Unknown type name\");\n\n\t\tif (match(TokenType::Identifier)) {\n\t\t\tstd::string name = lexeme(previous());\n\n\t\t\tif (match(TokenType::LeftParen)) {\n\n\t\t\t\tif (checkType() || check(TokenType::RightParen))\n\t\t\t\t{\n\t\t\t\t\t// function declaration\n\t\t\t\t\treturn parseFunctionDeclaration(type, name);\n\t\t\t\t}\n\n\t\t\t\trewind();\n\t\t\t}\n\n\t\t\treturn parseVarDeclaration(type, name);\n\t\t}\n\t\telse\n\t\t\tthrow std::runtime_error(\"Expected identifier after type.\");\n\t}\n\tif (match(TokenType::Struct)) return parseStructDeclaration();\n\treturn parseStatement();\n}\n\nstd::unique_ptr<febcode::Statement> Parser::parseBlockStatement() {\n\tauto block = std::make_unique<BlockStmt>();\n\n\twhile (!check(TokenType::RightBrace) && !isAtEnd()) {\n\t\tauto decl = parseDeclaration();\n\t\tif (decl) {\n\t\t\tblock->statements.push_back(std::move(decl));\n\t\t}\n\t}\n\n\tif (!match(TokenType::RightBrace)) {\n\t\tthrow std::runtime_error(\"Expected '}' after block.\");\n\t}\n\n\treturn block;\n}\n\nstd::unique_ptr<Statement> Parser::parseStatement() {\n\tif (match(TokenType::Return   )) return parseReturnStatement();\n\tif (match(TokenType::LeftBrace)) return parseBlockStatement();\n\tif (match(TokenType::If       )) return parseIfStatement();\n\tif (match(TokenType::While    )) return parseWhileStatement();\n\tif (match(TokenType::For      )) return parseForStatement();\n\n\treturn parseExpressionStatement();\n}\n\nstd::unique_ptr<Statement> Parser::parseExpressionStatement() {\n\tauto expr = parseExpression();\n\tif (!match(TokenType::Semicolon)) {\n\t\tthrow std::runtime_error(\"Expected ';' after expression.\");\n\t}\n\treturn std::make_unique<ExpressionStmt>(std::move(expr));\n}\n\nstd::unique_ptr<Statement> Parser::parseVarDeclaration(Type type, const std::string& name) \n{\n\tif (type == prg.types.Void())\n\t\tthrow std::runtime_error(\"Variables cannot be of type void.\");\n\n\tstd::vector<Var> vars;\n\tstd::string varName = name;\n\twhile (true)\n\t{\n\t\tType varType = type;\n\n\t\tstd::vector<size_t> arraySizes;\n\t\twhile (match(TokenType::LeftBrack))\n\t\t{\n\t\t\tif (!match(TokenType::Integer))\n\t\t\t\tthrow std::runtime_error(\"Expected array size after '['.\");\n\n\t\t\tint size = std::stoul(lexeme(previous()));\n\t\t\tif (size == 0)\n\t\t\t\tthrow std::runtime_error(\"Array size must be greater than zero.\");\n\n\t\t\tif (!match(TokenType::RightBrack))\n\t\t\t\tthrow std::runtime_error(\"Expected ']' after array size.\");\n\n\t\t\tarraySizes.push_back(size);\n\t\t}\n\n\t\tfor (int i = (int)arraySizes.size() -1; i >= 0; --i)\n\t\t{\n\t\t\tvarType = prg.types.getArrayType(varType, arraySizes[i]);\n\t\t}\n\n\t\tstd::unique_ptr<Expression> initializer;\n\t\tif (match(TokenType::Equal)) {\n\t\t\tinitializer = parseExpression();\n\t\t}\n\t\telse if (check(TokenType::LeftParen))\n\t\t{\n\t\t\tinitializer = parseConstructor(type);\n\t\t}\n\n\t\t// make sure the name doesn't start with an underscore, which is reserved for internal use\n\t\tif (!varName.empty() && varName[0] == '_')\n\t\t\tthrow std::runtime_error(\"Variable names cannot start with an underscore.\");\n\n\t\tvars.push_back({varName, arraySizes, std::move(initializer)});\n\n\t\tif (!match(TokenType::Comma)) break;\n\n\t\tif (!match(TokenType::Identifier))\n\t\t\tthrow std::runtime_error(\"Identifier expected after comma.\");\n\n\t\tvarName = std::string(lexeme(previous()));\n\t}\n\n\tif (!match(TokenType::Semicolon)) {\n\t\tthrow std::runtime_error(\"Expected ';' after variable declaration.\");\n\t}\n\n\treturn std::make_unique<VarDeclStmt>(type, vars);\n}\n\nstd::unique_ptr<Statement> Parser::parseStructDeclaration() {\n\tif (!match(TokenType::Identifier)) {\n\t\tthrow std::runtime_error(\"Expected struct name.\");\n\t}\n\tstd::string name(lexeme(previous()));\n\tif (!match(TokenType::LeftBrace)) {\n\t\tthrow std::runtime_error(\"Expected '{' after struct name.\");\n\t}\n\tstd::vector<std::pair<Type, std::string>> fields;\n\twhile (!check(TokenType::RightBrace) && !isAtEnd()) \n\t{\n\t\tif (!isType()) {\n\t\t\tthrow std::runtime_error(\"Expected field type in struct.\");\n\t\t}\n\n\t\tType type = prg.types.getType(lexeme(previous()));\n\t\tif (type == nullptr)\n\t\t\tthrow std::runtime_error(\"Unknown type name\");\n\n\t\tif (!match(TokenType::Identifier)) {\n\t\t\tthrow std::runtime_error(\"Expected field name in struct.\");\n\t\t}\n\t\tstd::string fieldName(lexeme(previous()));\n\n\t\tif (match(TokenType::LeftBrack))\n\t\t{\n\t\t\tif (!match(TokenType::Integer))\n\t\t\t\tthrow std::runtime_error(\"Expected array size after '[' in struct field.\");\n\t\t\tsize_t arraySize = std::stoul(lexeme(previous()));\n\t\t\tif (arraySize == 0)\n\t\t\t\tthrow std::runtime_error(\"Array size must be greater than zero.\");\n\t\t\tif (!match(TokenType::RightBrack))\n\t\t\t\tthrow std::runtime_error(\"Expected ']' after array size in struct field.\");\n\n\t\t\ttype = prg.types.getArrayType(type, arraySize);\n\t\t}\n\n\t\tif (!match(TokenType::Semicolon)) {\n\t\t\tthrow std::runtime_error(\"Expected ';' after struct field declaration.\");\n\t\t}\n\n\t\tfields.push_back({ type, fieldName });\n\t}\n\tif (!match(TokenType::RightBrace)) {\n\t\tthrow std::runtime_error(\"Expected '}' after struct body.\");\n\t}\n\n\tif (!match(TokenType::Semicolon)) {\n\t\tthrow std::runtime_error(\"Expected ';' after struct declaration.\");\n\t}\n\n\tType type = prg.types.defineStructType(name, fields);\n\n\treturn std::make_unique<StructStmt>(name, type, std::move(fields));\n}\n\nstd::unique_ptr<Statement> Parser::parseFunctionDeclaration(Type type, const std::string& name) \n{\n\tstd::vector<std::pair<Type, std::string>> parameters;\n\n\tif (!check(TokenType::RightParen)) {\n\t\tdo {\n\t\t\tif (!isType()) {\n\t\t\t\tthrow std::runtime_error(\"Expect type.\");\n\t\t\t}\n\n\t\t\tType paramType = prg.types.getType(lexeme(previous()));\n\t\t\tif (paramType == nullptr)\n\t\t\t\tthrow std::runtime_error(\"Unknown type name\");\n\n\t\t\tif (!match(TokenType::Identifier)) {\n\t\t\t\tthrow std::runtime_error(\"Expected parameter name.\");\n\t\t\t}\n\n\t\t\tstd::string param = lexeme(previous());\n\n\t\t\tif (match(TokenType::LeftBrack))\n\t\t\t{\n\t\t\t\tif (!match(TokenType::Integer))\n\t\t\t\t\tthrow std::runtime_error(\"Expected array size after '[' in parameter.\");\n\t\t\t\tsize_t arraySize = std::stoul(lexeme(previous()));\n\t\t\t\tif (arraySize == 0)\n\t\t\t\t\tthrow std::runtime_error(\"Array size must be greater than zero.\");\n\t\t\t\tif (!match(TokenType::RightBrack))\n\t\t\t\t\tthrow std::runtime_error(\"Expected ']' after array size in parameter.\");\n\t\t\t\tparamType = prg.types.getArrayType(paramType, arraySize);\n\t\t\t}\n\n\t\t\tparameters.push_back({ paramType, param });\n\t\t} while (match(TokenType::Comma));\n\t}\n\n\tif (!match(TokenType::RightParen)) {\n\t\tthrow std::runtime_error(\"Expected ')' after parameters.\");\n\t}\n\n\tif (!match(TokenType::LeftBrace)) {\n\t\tthrow std::runtime_error(\"Expected '{' before function body.\");\n\t}\n\n\tauto body = parseBlockStatement();\n\n\treturn std::make_unique<FunctionStmt>(\n\t\tname,\n\t\ttype,\n\t\tstd::move(parameters),\n\t\tstd::move(body)\n\t);\n}\n\nstd::unique_ptr<Statement> Parser::parseReturnStatement() {\n\tstd::unique_ptr<Expression> value = nullptr;\n\tif (!check(TokenType::Semicolon)) {\n\t\tvalue = parseExpression();\n\t}\n\n\tif (!match(TokenType::Semicolon)) {\n\t\tthrow std::runtime_error(\"Expected ';' after return value.\");\n\t}\n\n\treturn std::make_unique<febcode::ReturnStmt>(std::move(value));\n}\n\nstd::unique_ptr<febcode::Statement> Parser::parseIfStatement() {\n\tif (!match(TokenType::LeftParen)) {\n\t\tthrow std::runtime_error(\"Expected '(' after 'if'.\");\n\t}\n\n\tauto condition = parseExpression();\n\n\tif (!match(TokenType::RightParen)) {\n\t\tthrow std::runtime_error(\"Expected ')' after if condition.\");\n\t}\n\n\tauto thenBranch = parseStatement(); // could be block or single statement\n\n\tstd::unique_ptr<Statement> elseBranch = nullptr;\n\tif (match(TokenType::Else)) {\n\t\telseBranch = parseStatement();\n\t}\n\n\treturn std::make_unique<IfStmt>(std::move(condition),\n\t\tstd::move(thenBranch),\n\t\tstd::move(elseBranch));\n}\n\nstd::unique_ptr<febcode::Statement> Parser::parseWhileStatement() {\n\tif (!match(TokenType::LeftParen)) {\n\t\tthrow std::runtime_error(\"Expected '(' after 'while'.\");\n\t}\n\n\tauto condition = parseExpression();\n\n\tif (!match(TokenType::RightParen)) {\n\t\tthrow std::runtime_error(\"Expected ')' after while condition.\");\n\t}\n\n\tauto body = parseStatement(); // can be block or single statement\n\n\treturn std::make_unique<WhileStmt>(\n\t\tstd::move(condition),\n\t\tstd::move(body));\n}\n\nstd::unique_ptr<febcode::Statement> Parser::parseForStatement() {\n\tif (!match(TokenType::LeftParen)) {\n\t\tthrow std::runtime_error(\"Expected '(' after 'for'.\");\n\t}\n\n\tauto init = parseDeclaration(); // initializer (can be var decl, expression stmt, or empty)\n\tif (!init && !isVarDecl(init) && !isExprStmt(init)) {\n\t\tthrow std::runtime_error(\"Expected variable declaration, expression statement, or ';' in for loop initializer.\");\n\t}\n\n\tauto condition = parseExpression();\n\n\tif (!match(TokenType::Semicolon)) {\n\t\tthrow std::runtime_error(\"Expected ';' after for initializer.\");\n\t}\n\n\tauto increment = parseExpression();\n\n\tif (!match(TokenType::RightParen)) {\n\t\tthrow std::runtime_error(\"Expected ')' after for increment.\");\n\t}\n\n\tauto body = parseStatement(); // can be block or single statement\n\n\treturn std::make_unique<ForStmt>(\n\t\tstd::move(init),\n\t\tstd::move(condition),\n\t\tstd::move(increment),\n\t\tstd::move(body));\n}\n\nstd::unique_ptr<Expression> Parser::parseAssignment() {\n\tauto expr = parseOr(); // parse the left-hand side first\n\n\t// Check if this is an assignment\n\tif (match(TokenType::Equal)) {\n\t\tconst Token& equalsToken = previous();\n\n\t\tauto value = parseAssignment();\n\n\t\treturn std::make_unique<AssignExpr>(\n\t\t\tstd::move(expr),\n\t\t\tstd::move(value)\n\t\t);\n\t}\n\n\treturn expr; // just an equality/expression if no '='\n}\n\nstd::unique_ptr<Expression> Parser::parseOr()\n{\n\tauto expr = parseAnd();\n\n\twhile (match(TokenType::OrOr))\n\t{\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseAnd();\n\t\texpr = std::make_unique<BinaryExpr>(\n\t\t\tstd::move(expr),\n\t\t\top,\n\t\t\tstd::move(right));\n\t}\n\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseAnd()\n{\n\tauto expr = parseEquality();\n\n\twhile (match(TokenType::AndAnd))\n\t{\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseEquality();\n\t\texpr = std::make_unique<BinaryExpr>(\n\t\t\tstd::move(expr),\n\t\t\top,\n\t\t\tstd::move(right));\n\t}\n\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseEquality() {\n\tauto expr = parseComparison();\n\n\twhile (match(TokenType::EqualEqual) || match(TokenType::NotEqual)) {\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseComparison();\n\t\texpr = std::make_unique<BinaryExpr>(std::move(expr), op, std::move(right));\n\t}\n\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseComparison() {\n\tauto expr = parseTerm();\n\n\twhile (match(TokenType::Greater) || match(TokenType::GreaterEqual) ||\n\t\tmatch(TokenType::Less) || match(TokenType::LessEqual)) {\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseTerm();\n\t\texpr = std::make_unique<BinaryExpr>(std::move(expr), op, std::move(right));\n\t}\n\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseTerm() {\n\tauto expr = parseFactor();\n\n\twhile (match(TokenType::Plus) || match(TokenType::Minus)) {\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseFactor();\n\t\texpr = std::make_unique<BinaryExpr>(std::move(expr), op, std::move(right));\n\t}\n\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseFactor() {\n\tauto expr = parseUnary();\n\n\twhile (match(TokenType::Star) || match(TokenType::Slash)) {\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseUnary();\n\t\texpr = std::make_unique<BinaryExpr>(std::move(expr), op, std::move(right));\n\t}\n\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseUnary() {\n\tif (match(TokenType::Minus) || match(TokenType::Not)) {\n\t\tUnaryOp op = tokenToUnaryOp(previous());\n\t\tauto right = parseUnary();\n\n\t\t// absorbe negative sign for scalars.\n\t\tif (op == UnaryOp::Negate) {\n\n\t\t\tint n;\n\t\t\tif (isInt(right.get(), n)) return std::make_unique<LiteralExpr>(-n);\n\t\t\tdouble d;\n\t\t\tif (isDouble(right.get(), d)) return std::make_unique<LiteralExpr>(-d);\n\t\t}\n\n\t\treturn std::make_unique<UnaryExpr>(op, std::move(right));\n\t}\n\n\treturn parseExponent();\n}\n\nstd::unique_ptr<Expression> Parser::parseExponent() {\n\tauto left = parseCall();\n\n\tif (match(TokenType::Exponent)) {\n\t\tBinaryOp op = tokenToBinaryOp(previous());\n\t\tauto right = parseUnary();\n\t\treturn std::make_unique<BinaryExpr>(\n\t\t\tstd::move(left), op, std::move(right));\n\t}\n\n\treturn left;\n}\n\nstd::unique_ptr<Expression> Parser::parseCall() {\n\n\tif (isType())\n\t{\n\t\tstd::string typeName = lexeme(previous());\n\t\tType type = prg.types.getType(typeName);\n\t\tif (type == nullptr)\n\t\t\tthrow std::runtime_error(\"Unknown type name in constructor expression.\");\n\n\t\treturn parseConstructor(type);\n\t}\n\n\tauto expr = parsePrimary();\n\n\twhile (true) {\n\t\tif (match(TokenType::LeftParen)) {\n\t\t\texpr = finishCall(std::move(expr));\n\t\t}\n\t\telse if (match(TokenType::Dot))\n\t\t{\n\t\t\tif (!match(TokenType::Identifier)) {\n\t\t\t\tthrow std::runtime_error(\"Expected property name after '.'.\");\n\t\t\t}\n\n\t\t\texpr = std::make_unique<MemberExpr>(\n\t\t\t\tstd::move(expr),\n\t\t\t\tstd::string(previous().start, previous().length)\n\t\t\t);\n\t\t}\n\t\telse if (match(TokenType::LeftBrack)) {\n\t\t\tauto index = parseExpression();\n\t\t\tif (!match(TokenType::RightBrack))\n\t\t\t\tthrow std::runtime_error(\"Expect ']' after index.\");\n\n\t\t\texpr = std::make_unique<IndexExpr>(\n\t\t\t\tstd::move(expr),\n\t\t\t\tstd::move(index));\n\t\t}\n\t\telse {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn expr;\n}\n\nstd::unique_ptr<Expression> Parser::parseConstructor(Type type)\n{\n\tstd::vector<std::unique_ptr<Expression>> arguments;\n\n\tif (!match(TokenType::LeftParen))\n\t\tthrow std::runtime_error(\"Expected '(' after type name in constructor expression.\");\n\n\tif (!check(TokenType::RightParen)) {\n\t\tdo {\n\t\t\targuments.push_back(parseExpression());\n\t\t} while (match(TokenType::Comma));\n\t}\n\n\tif (!match(TokenType::RightParen))\n\t\tthrow std::runtime_error(\"Expected ')' after arguments.\");\n\n\treturn std::make_unique<ConstructorExpr>(type, std::move(arguments));\n}\n\nstd::unique_ptr<Expression> Parser::finishCall(std::unique_ptr<Expression> callee) \n{\n\tauto* varExpr = dynamic_cast<VariableExpr*>(callee.get());\n\tif (!varExpr) {\n\t\tthrow std::runtime_error(\"Can only call functions by name.\");\n\t}\n\n\tstd::vector<std::unique_ptr<Expression>> arguments;\n\n\tif (!check(TokenType::RightParen)) {\n\t\tdo {\n\t\t\targuments.push_back(parseExpression());\n\t\t} while (match(TokenType::Comma));\n\t}\n\n\tif (!match(TokenType::RightParen))\n\t\tthrow std::runtime_error(\"Expected ')' after arguments.\");\n\n\treturn std::make_unique<CallExpr>(\n\t\tvarExpr->name,\n\t\tstd::move(arguments)\n\t);\n}\n\nstd::unique_ptr<Expression> Parser::parsePrimary() \n{\n\tif (match(TokenType::Integer)) {\n\t\tint value = std::stoi(std::string(previous().start, previous().length));\n\t\treturn std::make_unique<LiteralExpr>(value);\n\t}\n\n\tif (match(TokenType::Double)) {\n\t\tdouble value = std::stod(std::string(previous().start, previous().length));\n\t\treturn std::make_unique<LiteralExpr>(value);\n\t}\n\n\tif (match(TokenType::True))\n\t\treturn std::make_unique<LiteralExpr>(true);\n\t\n\tif (match(TokenType::False))\n\t\treturn std::make_unique<LiteralExpr>(false);\n\n\tif (match(TokenType::Identifier)) {\n\t\tstd::string name(previous().start, previous().length);\n\n\t\t// check some special built-in constants like \"PI\"\n\t\tif (name == \"PI\") {\n\t\t\treturn std::make_unique<LiteralExpr>(3.14159265358979323846);\n\t\t}\n\n\t\t// otherwise, it's a variable\n\t\treturn std::make_unique<VariableExpr>(\n\t\t\tstd::string(previous().start, previous().length));\n\t}\n\n\tif (match(TokenType::LeftParen)) {\n\t\tauto expr = parseExpression();\n\t\tif (!match(TokenType::RightParen)) {\n\t\t\tthrow std::runtime_error(\"Expected ')' after expression.\");\n\t\t}\n\t\treturn expr;\n\t}\n\n\tif (match(TokenType::LeftBrace)) \n\t{\n\t\tstd::vector<std::unique_ptr<Expression>> elements;\n\n\t\tif (!check(TokenType::RightBrace)) {\n\t\t\tdo {\n\t\t\t\telements.push_back(parseExpression());\n\t\t\t} while (match(TokenType::Comma));\n\t\t}\n\n\t\tif (!match(TokenType::RightBrace))\n\t\t\tthrow std::runtime_error(\"Expected '}' after initializer list.\");\n\n\t\treturn std::make_unique<InitExpr>(std::move(elements));\n\t}\n\n\tthrow std::runtime_error(\"Expected expression.\");\n}\n\n//---------------------------------------------------------------------\nstatic int l = 0;\nstatic void printTabs()\n{\n\tfor (int i = 0; i < l; ++i) std::cout << \"    \";\n}\n\nstatic void printTabs(std::ostream& os)\n{\n\tfor (int i = 0; i < l; ++i) os << \"    \";\n}\n\nstd::ostream& operator << (std::ostream& o, const UnaryOp& op)\n{\n\tswitch (op)\n\t{\n\tcase UnaryOp::Negate: return o << \"-\";\n\tcase UnaryOp::Not: return o << \"!\";\n\tdefault: return o << \"<unknown op>\";\n\t}\n}\n\nstd::ostream& operator << (std::ostream& o, const std::vector<std::string>& v)\n{\n\to << \"[\";\n\tfor (size_t i = 0; i < v.size(); ++i)\n\t{\n\t\to << \"\\\"\" << v[i] << \"\\\"\";\n\t\tif (i != v.size() - 1) o << \", \";\n\t}\n\to << \"]\";\n\treturn o;\n}\n\nstd::ostream& operator << (std::ostream& o, const BinaryOp& op)\n{\n\tswitch (op)\n\t{\n\tcase BinaryOp::Plus        : return o << \"+\";\n\tcase BinaryOp::Minus       : return o << \"-\";\n\tcase BinaryOp::Multiply    : return o << \"*\";\n\tcase BinaryOp::Divide      : return o << \"/\";\n\tcase BinaryOp::Exponent    : return o << \"**\";\n\tcase BinaryOp::EqualEqual  : return o << \"==\";\n\tcase BinaryOp::NotEqual    : return o << \"!=\";\n\tcase BinaryOp::Greater     : return o << \">\";\n\tcase BinaryOp::GreaterEqual: return o << \">=\";\n\tcase BinaryOp::Less        : return o << \"<\";\n\tcase BinaryOp::LessEqual   : return o << \"<=\";\n\tcase BinaryOp::AndAnd      : return o << \"&&\";\n\tcase BinaryOp::OrOr        : return o << \"||\";\n\tdefault: return o << \"<unknown op>\";\n\t}\n}\n\nstatic void printExpr(const Expression* e);\n\nstatic void printLiteralExpr(const LiteralExpr* e)\n{\n\tstd::cout << ValueToString(e->value);\n}\n\nstatic void printVariableExpr(const VariableExpr* e)\n{\n\tstd::cout << e->name;\n}\n\nstatic void printMemberExpr(const MemberExpr* e)\n{\n\tstd::cout << \"MemberExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"object: \"; printExpr(e->object.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"property: \" << e->property << \"\\n\";\n\tl--; printTabs(); std::cout << \"}\";\n}\n\nstatic void printAssignmentExpr(const AssignExpr* e)\n{\n\tstd::cout << \"AssignExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"target: \"; printExpr(e->target.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"value: \"; printExpr(e->value.get()); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printUnaryExpr(const UnaryExpr* e)\n{\n\tstd::cout << \"UnaryExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"op: \" << e->op << \",\\n\";\n\tprintTabs(); std::cout << \"right: \"; printExpr(e->right.get()); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printBinaryExpr(const BinaryExpr* e)\n{\n\tstd::cout << \"BinaryExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"op: \" << e->op << \",\\n\";\n\tprintTabs(); std::cout << \"left: \"; printExpr(e->left.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"right: \"; printExpr(e->right.get()); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printExprList(const std::vector<std::unique_ptr<Expression>>& a)\n{\n\tstd::cout << \"[\\n\"; l++;\n\tfor (size_t i = 0; i < a.size(); ++i)\n\t{\n\t\tprintTabs(); printExpr(a[i].get());\n\t\tif (i != a.size() - 1) std::cout << \",\\n\";\n\t\telse std::cout << \"\\n\";\n\t}\n\tl--; printTabs(); std::cout << \"]\";\n}\n\nstatic void printCallExpr(const CallExpr* e)\n{\n\tstd::cout << \"CallExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"callee: \" << e->name << \",\\n\";\n\tprintTabs(); std::cout << \"args: \"; printExprList(e->arguments); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printInitializerExpr(const InitExpr* e)\n{\n\tstd::cout << \"InitializerExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"elements: \"; printExprList(e->elements); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printIndexExpr(const IndexExpr* e)\n{\n\tstd::cout << \"IndexExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"object: \"; printExpr(e->object.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"index: \"; printExpr(e->index.get()); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printConstructorExpr(const ConstructorExpr* e)\n{\n\tstd::cout << \"ConstructorExpr {\\n\"; l++;\n\tprintTabs(); std::cout << \"type: \" << TypeToString(e->valType) << \",\\n\";\n\tprintTabs(); std::cout << \"args: \"; printExprList(e->args); std::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printExpr(const Expression* e)\n{\n\tif      (auto l = dynamic_cast<const LiteralExpr*      >(e)) printLiteralExpr      (l);\n\telse if (auto v = dynamic_cast<const VariableExpr*     >(e)) printVariableExpr     (v);\n\telse if (auto m = dynamic_cast<const MemberExpr*       >(e)) printMemberExpr       (m);\n\telse if (auto a = dynamic_cast<const AssignExpr*       >(e)) printAssignmentExpr   (a);\n\telse if (auto u = dynamic_cast<const UnaryExpr*        >(e)) printUnaryExpr        (u);\n\telse if (auto b = dynamic_cast<const BinaryExpr*       >(e)) printBinaryExpr       (b);\n\telse if (auto c = dynamic_cast<const CallExpr*         >(e)) printCallExpr         (c);\n\telse if (auto c = dynamic_cast<const InitExpr*         >(e)) printInitializerExpr  (c);\n\telse if (auto c = dynamic_cast<const IndexExpr*        >(e)) printIndexExpr        (c);\n\telse if (auto c = dynamic_cast<const ConstructorExpr*  >(e)) printConstructorExpr  (c);\n\telse if (e == nullptr)\n\t\tstd::cout << \"null\";\n\telse\n\t\tstd::cout << \"(Unknown Expression)\";\n}\n\nstatic void printExpressionStmt(const ExpressionStmt* s)\n{\n\tstd::cout << \"ExpressionStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"expr: \"; printExpr(s->expr.get());\n\tstd::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printStatement(const Statement* stmt);\n\nstatic std::ostream& operator << (std::ostream& o, TypeKind type)\n{\n\tswitch (type)\n\t{\n\tcase TypeKind::Void  : return o << \"void\";\n\tcase TypeKind::Bool  : return o << \"bool\";\n\tcase TypeKind::Int   : return o << \"int\";\n\tcase TypeKind::Double: return o << \"double\";\n\tcase TypeKind::Vec2  : return o << \"vec2\";\n\tcase TypeKind::Vec3  : return o << \"vec3\";\n\tcase TypeKind::Mat2  : return o << \"mat2\";\n\tcase TypeKind::Mat3  : return o << \"mat3\";\n\tdefault:\n\t\treturn o << \"<unknown type>\";\n\t}\n}\n\nstatic void printVarDeclStmt(const VarDeclStmt* s)\n{\n\tstd::cout << \"VarDeclStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"type: \" << TypeToString(s->type) << \",\\n\";\n\tprintTabs(); std::cout << \"vars: [\\n\"; l++;\n\tfor (const auto& var : s->vars) {\n\t\tprintTabs(); std::cout << \"{ name: \" << var.name;\n\t\tif (!var.arraySizes.empty())\n\t\t{\n\t\t\tstd::cout << \", size: [\";\n\t\t\tfor (size_t i = 0; i < var.arraySizes.size(); ++i)\n\t\t\t{\n\t\t\t\tstd::cout << var.arraySizes[i];\n\t\t\t\tif (i != var.arraySizes.size() - 1) std::cout << \"][\";\n\t\t\t}\n\t\t\tstd::cout << \"]\";\n\t\t}\n\t\tstd::cout << \", initializer: \";\n\t\tprintExpr(var.initializer.get());\n\t\tstd::cout << \" },\\n\";\n\t}\n\tl--; printTabs(); std::cout << \"],\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printReturnStmt(const ReturnStmt* s)\n{\n\tstd::cout << \"ReturnStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"value: \"; printExpr(s->value.get());\n\tstd::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printBlockStmt(const BlockStmt* s)\n{\n\tstd::cout << \"BlockStmt: [\\n\"; l++;\n\tsize_t n = s->statements.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tauto& stmt = s->statements[i];\n\t\tprintStatement(stmt.get());\n\t\tif (i != n - 1) std::cout << \",\\n\";\n\t}\tstd::cout << \"\\n\";\n\tl--;\n\tprintTabs(); std::cout << \"]\";\n}\n\nstatic void printIfStmt(const IfStmt* s)\n{\n\tstd::cout << \"IfStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"condition: \"; printExpr(s->condition.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"thenBranch: {\\n\"; l++; printStatement(s->thenBranch.get());\n\tstd::cout << \"\\n\";\n\tl--; printTabs(); std::cout << \"}\\n\";\n\tif (s->elseBranch)\n\t{\n\t\tprintTabs(); std::cout << \"elseBranch: {\\n\"; l++;\n\t\tprintStatement(s->elseBranch.get());\n\t\tstd::cout << \"\\n\";\n\t\tl--; printTabs(); std::cout << \"}\\n\";\n\t}\n\tl--;\n\tprintTabs(); std::cout << \"}\";\n}\n\nstatic void printWhileStmt(const WhileStmt* s)\n{\n\tstd::cout << \"WhileStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"condition: \"; printExpr(s->condition.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"body: {\\n\"; l++; printStatement(s->body.get());\n\tstd::cout << \"\\n\";\n\tl--; printTabs(); std::cout << \"}\\n\";\n}\n\nstatic void printForStmt(const ForStmt* s)\n{\n\tstd::cout << \"ForStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"initializer: \"; printStatement(s->initializer.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"condition: \"; printExpr(s->condition.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"increment: \"; printExpr(s->increment.get()); std::cout << \",\\n\";\n\tprintTabs(); std::cout << \"body: {\\n\"; l++; printStatement(s->body.get());\n\tstd::cout << \"\\n\";\n\tl--; printTabs(); std::cout << \"}\\n\";\n\tl--; printTabs(); std::cout << \"}\";\n}\n\nstatic void printFunctionStmt(const FunctionStmt* s)\n{\n\tstd::cout << \"FunctionStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"type: \" << TypeToString(s->returnType) << \",\\n\";\n\tprintTabs(); std::cout << \"name: \" << s->name << \",\\n\";\n\tprintTabs(); std::cout << \"params: [\\n\"; l++;\n\tfor (const auto& param : s->params)\n\t{\n\t\tprintTabs(); std::cout << \"{ type: \" << TypeToString(param.first) << \", name: \" << param.second << \" },\\n\";\n\t}\n\tl--; printTabs(); std::cout << \"],\\n\";\n\tprintTabs(); std::cout << \"body: {\\n\"; l++; printStatement(s->body.get());\n\tstd::cout << \"\\n\";\n\tl--; printTabs(); std::cout << \"}\\n\";\n\tl--; printTabs(); std::cout << \"}\";\n}\n\nstatic void printStructStmt(const StructStmt* s)\n{\n\tstd::cout << \"StructStmt: {\\n\"; l++;\n\tprintTabs(); std::cout << \"name: \" << s->name << \",\\n\";\n\tprintTabs(); std::cout << \"fields: [\\n\"; l++;\n\tfor (const auto& field : s->fields)\n\t{\n\t\tprintTabs(); std::cout << \"{ name: \" << field.second << \", type: \" << TypeToString(field.first) << \" },\\n\";\n\t}\n\tl--; printTabs(); std::cout << \"]\\n\";\n\tl--; printTabs(); std::cout << \"}\";\n}\n\nstatic void printStatement(const Statement* stmt)\n{\n\tprintTabs();\n\tif      (auto e = dynamic_cast<const ExpressionStmt*>(stmt)) printExpressionStmt(e);\n\telse if (auto v = dynamic_cast<const VarDeclStmt*   >(stmt)) printVarDeclStmt   (v);\n\telse if (auto r = dynamic_cast<const ReturnStmt*    >(stmt)) printReturnStmt    (r);\n\telse if (auto b = dynamic_cast<const BlockStmt*     >(stmt)) printBlockStmt     (b);\n\telse if (auto i = dynamic_cast<const IfStmt*        >(stmt)) printIfStmt        (i);\n\telse if (auto w = dynamic_cast<const WhileStmt*     >(stmt)) printWhileStmt     (w);\n\telse if (auto l = dynamic_cast<const ForStmt*       >(stmt)) printForStmt       (l);\n\telse if (auto f = dynamic_cast<const FunctionStmt*  >(stmt)) printFunctionStmt  (f);\n\telse if (auto s = dynamic_cast<const StructStmt*    >(stmt)) printStructStmt    (s);\n\telse\n\t\tstd::cout << \"(Unknown Statement)\";\n}\n\nvoid febcode::printAST(const AST& ast)\n{\n\tl = 0;\n\tsize_t n = ast.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tauto stmt = ast[i];\n\t\tprintStatement(stmt);\n\t\tif (i != n - 1) std::cout << \",\\n\";\n\t}\n\n\tstd::cout << std::endl;\n}\n\nvoid febcode::ParseSource(Program& prg, const std::string& source)\n{\n\tScanner scanner(source);\n\tstd::vector<Token> tokens = scanner.scanTokens();\n\n\tParser parser(prg);\n\tparser.parse(tokens);\n\n\tResolver resolver(prg);\n\tresolver.resolve();\n}\n\n//---------------------------------------------------------------------\n// pretty print functions for debugging purposes\n\nstatic void prettyPrintLiteralExpr(std::ostream& os, const LiteralExpr& expr)\n{\n\tos << ValueToString(expr.value);\n}\n\nstatic void prettyPrintVariableExpr(std::ostream& os, const VariableExpr& expr)\n{\n\tos << expr.name;\n}\n\nstatic void prettyPrintMemberExpr(std::ostream& os, const MemberExpr& expr)\n{\n\tprettyPrintExpression(os, *expr.object);\n\tos << \".\" << expr.property;\n}\n\nstatic void prettyPrintAssignmentExpr(std::ostream& os, const AssignExpr& expr)\n{\n\tprettyPrintExpression(os, *expr.target);\n\tos << \" = \";\n\tprettyPrintExpression(os, *expr.value);\n}\n\nstatic void prettyPrintUnaryExpr(std::ostream& os, const UnaryExpr& expr)\n{\n\tos << expr.op;\n\tos << \"(\";\n\tprettyPrintExpression(os, *expr.right);\n\tos << \")\";\n}\n\nstatic void prettyPrintBinaryExpr(std::ostream& os, const BinaryExpr& expr)\n{\n\tbool leftBinary  = dynamic_cast<const BinaryExpr*>(expr.left.get()) != nullptr;\n\tbool rightBinary = dynamic_cast<const BinaryExpr*>(expr.right.get()) != nullptr;\n\n\tif (leftBinary) os << \"(\";\n\tprettyPrintExpression(os, *expr.left);\n\tif (leftBinary) os << \")\";\n\tos << expr.op;\n\tif (rightBinary) os << \"(\";\n\tprettyPrintExpression(os, *expr.right);\n\tif (rightBinary) os << \")\";\n}\n\nstatic void prettyPrintCallExpr(std::ostream& os, const CallExpr& expr)\n{\n\tos << expr.name;\n\tos << \"(\";\n\tsize_t n = expr.arguments.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tprettyPrintExpression(os, *expr.arguments[i]);\n\t\tif (i != n - 1) os << \", \";\n\t}\n\tos << \")\";\n}\n\nstatic void prettyPrintInitializerExpr(std::ostream& os, const InitExpr& expr)\n{\n\tos << \"{ \";\n\tsize_t n = expr.elements.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tprettyPrintExpression(os, *expr.elements[i]);\n\t\tif (i != n - 1) os << \", \";\n\t}\n\tos << \" }\";\n}\n\nstatic void prettyPrintIndexExpr(std::ostream& os, const IndexExpr& expr)\n{\n\tprettyPrintExpression(os, *expr.object);\n\tos << \"[\";\n\tprettyPrintExpression(os, *expr.index);\n\tos << \"]\";\n}\n\nstatic void prettyPrintConstructorExpr(std::ostream& os, const ConstructorExpr& expr)\n{\n\tos << TypeToString(expr.valType);\n\tos << \"(\";\n\tsize_t n = expr.args.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tprettyPrintExpression(os, *expr.args[i]);\n\t\tif (i != n - 1) os << \", \";\n\t}\n\tos << \")\";\n}\n\nvoid febcode::prettyPrintExpression(std::ostream& os, const Expression& expr)\n{\n\tif      (auto l = dynamic_cast<const LiteralExpr*      >(&expr)) prettyPrintLiteralExpr      (os, *l);\n\telse if (auto v = dynamic_cast<const VariableExpr*     >(&expr)) prettyPrintVariableExpr     (os, *v);\n\telse if (auto m = dynamic_cast<const MemberExpr*       >(&expr)) prettyPrintMemberExpr       (os, *m);\n\telse if (auto a = dynamic_cast<const AssignExpr*       >(&expr)) prettyPrintAssignmentExpr   (os, *a);\n\telse if (auto u = dynamic_cast<const UnaryExpr*        >(&expr)) prettyPrintUnaryExpr        (os, *u);\n\telse if (auto b = dynamic_cast<const BinaryExpr*       >(&expr)) prettyPrintBinaryExpr       (os, *b);\n\telse if (auto c = dynamic_cast<const CallExpr*         >(&expr)) prettyPrintCallExpr         (os, *c);\n\telse if (auto c = dynamic_cast<const InitExpr*         >(&expr)) prettyPrintInitializerExpr  (os, *c);\n\telse if (auto c = dynamic_cast<const IndexExpr*        >(&expr)) prettyPrintIndexExpr        (os, *c);\n\telse if (auto c = dynamic_cast<const ConstructorExpr*  >(&expr)) prettyPrintConstructorExpr  (os, *c);\n\telse\n\t\tos << \"(Unknown Expression)\";\n}\n\nstatic void prettyPrintStatement(std::ostream& os, const Statement& stmt);\n\nstatic void prettyPrintExpressionStmt(std::ostream& os, const ExpressionStmt& stmt)\n{\n\tprettyPrintExpression(os, *stmt.expr);\n\tos << \";\";\n}\n\nstatic void prettyPrintVarDeclStmt(std::ostream& os, const VarDeclStmt& stmt)\n{\n\tsize_t n = stmt.vars.size();\n\tif (stmt.input)\n\t\tos << \"in \";\n\tos << TypeToString(stmt.type) << \" \";\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tconst auto& var = stmt.vars[i];\n\t\tos << var.name;\n\t\tif (!var.arraySizes.empty())\n\t\t{\n\t\t\tfor (size_t j = 0; j < var.arraySizes.size(); ++j)\n\t\t\t{\n\t\t\t\tos << \"[\";\n\t\t\t\tos << var.arraySizes[j];\n\t\t\t\tos << \"]\";\n\t\t\t}\n\t\t}\n\t\tif (var.initializer)\n\t\t{\n\t\t\tos << \" = \";\n\t\t\tprettyPrintExpression(os, *var.initializer);\n\t\t}\n\t\tif (i != n - 1) os << \", \";\n\t}\n\tos << \";\";\n}\n\nstatic void prettyPrintReturnStmt(std::ostream& os, const ReturnStmt& stmt)\n{\n\tos << \"return\";\n\tif (stmt.value)\n\t{\n\t\tos << \" \";\n\t\tprettyPrintExpression(os, *stmt.value);\n\t}\n\tos << \";\";\n}\n\nstatic void prettyPrintBlockStmt(std::ostream& os, const BlockStmt& stmt)\n{\n\tos << \"{\\n\"; l++;\n\tsize_t n = stmt.statements.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tauto& s = stmt.statements[i];\n\t\tprettyPrintStatement(os, *s);\n\t\tif (i != n - 1) os << \"\\n\";\n\t}\n\tos << \"\\n\"; l--;\n\tprintTabs(os); os << \"}\";\n}\n\nstatic void prettyPrintIfStmt(std::ostream& os, const IfStmt& stmt)\n{\n\tos << \"if (\";\n\tprettyPrintExpression(os, *stmt.condition);\n\tos << \")\\n\";\n\tprintTabs(os); prettyPrintStatement(os, *stmt.thenBranch);\n\tif (stmt.elseBranch)\n\t{\n\t\tos << \"\\n\";\n\t\tprintTabs(os); os << \"else\\n\";\n\t\tprintTabs(os); prettyPrintStatement(os, *stmt.elseBranch);\n\t}\n}\n\nstatic void prettyPrintWhileStmt(std::ostream& os, const WhileStmt& stmt)\n{\n\tos << \"while (\";\n\tprettyPrintExpression(os, *stmt.condition);\n\tos << \")\\n\";\n\tprintTabs(os); prettyPrintStatement(os, *stmt.body);\n}\n\nstatic void prettyPrintFunctionStmt(std::ostream& os, const FunctionStmt& stmt)\n{\n\tos << TypeToString(stmt.returnType) << \" \" << stmt.name << \"(\";\n\tsize_t n = stmt.params.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tconst auto& param = stmt.params[i];\n\t\tos << TypeToString(param.first) << \" \" << param.second;\n\t\tif (i != n - 1) std::cout << \", \";\n\t}\n\tos << \")\\n\";\n\tprintTabs(os); prettyPrintStatement(os, *stmt.body);\n}\n\nstatic void prettyPrintStructStmt(std::ostream& os, const StructStmt& stmt)\n{\n\tos << \"struct \" << stmt.name << \" {\\n\";\n\tfor (auto& field : stmt.fields)\n\t{\n\t\tos << \"    \" << TypeToString(field.first) << \" \" << field.second << \";\\n\";\n\t}\n\tos << \"};\";\n}\n\nstatic void prettyPrintStatement(std::ostream& os, const febcode::Statement& stmt)\n{\n\tprintTabs(os);\n\tif      (auto e = dynamic_cast<const ExpressionStmt*>(&stmt)) prettyPrintExpressionStmt(os, *e);\n\telse if (auto v = dynamic_cast<const VarDeclStmt*   >(&stmt)) prettyPrintVarDeclStmt   (os, *v);\n\telse if (auto r = dynamic_cast<const ReturnStmt*    >(&stmt)) prettyPrintReturnStmt    (os, *r);\n\telse if (auto b = dynamic_cast<const BlockStmt*     >(&stmt)) prettyPrintBlockStmt     (os, *b);\n\telse if (auto i = dynamic_cast<const IfStmt*        >(&stmt)) prettyPrintIfStmt        (os, *i);\n\telse if (auto w = dynamic_cast<const WhileStmt*     >(&stmt)) prettyPrintWhileStmt     (os, *w);\n\telse if (auto f = dynamic_cast<const FunctionStmt*  >(&stmt)) prettyPrintFunctionStmt  (os, *f);\n\telse if (auto s = dynamic_cast<const StructStmt*    >(&stmt)) prettyPrintStructStmt    (os, *s);\n\telse\n\t\tstd::cout << \"(Unknown Statement)\";\n}\n\nvoid febcode::prettyPrintAST(std::ostream& os, const AST& ast)\n{\n\tl = 0;\n\tsize_t n = ast.size();\n\tfor (size_t i = 0; i < n; ++i)\n\t{\n\t\tauto& stmt = *ast[i];\n\t\tprettyPrintStatement(os, stmt);\n\t\tif (i != n - 1) os << \"\\n\";\n\t}\n}\n\nvoid febcode::prettyPrintAST(const AST& ast)\n{\n\tprettyPrintAST(std::cout, ast);\n}\n"
  },
  {
    "path": "febcode/parser.h",
    "content": "#pragma once\n#include \"scanner.h\"\n#include \"ast.h\"\n#include \"program.h\"\n#include <stdexcept>\n#include <ostream>\n#include <cstring>\n\nnamespace febcode {\n\n\tclass Parser {\n\tpublic:\n\t\tParser(Program& program) : prg(program), current(0), m_ast(nullptr) {}\n\n\t\tvoid parse(const std::vector<Token>& tokens) {\n\t\t\tm_ast = prg.ast.get();\n\t\t\tthis->tokens = tokens;\n\t\t\tcurrent = 0;\n\t\t\twhile (!isAtEnd()) {\n\t\t\t\tm_ast->root.statements.push_back(parseDeclaration());\n\t\t\t}\n\t\t\tm_ast = nullptr;\n\t\t}\n\n\tprivate:\n\t\tProgram& prg;\n\t\tstd::vector<Token> tokens;\n\t\tsize_t current;\n\n\t\tAST* m_ast;\n\n\t\tbool isAtEnd() const { return peek().type == TokenType::EndOfFile; }\n\t\tconst Token& peek() const { return tokens[current]; }\n\t\tconst Token& previous() const { return tokens[current - 1]; }\n\n\t\tstd::string lexeme(const Token& t) const {\n\t\t\treturn std::string(t.start, t.length);\n\t\t}\n\n\t\tconst Token& advance() {\n\t\t\tif (!isAtEnd()) current++;\n\t\t\treturn previous();\n\t\t}\n\n\t\tvoid rewind() {\n\t\t\tif (current > 0) current--;\n\t\t}\n\n\t\tbool check(TokenType type) const {\n\t\t\tif (isAtEnd()) return false;\n\t\t\treturn peek().type == type;\n\t\t}\n\n\t\tbool match(TokenType type) {\n\t\t\tif (check(type)) {\n\t\t\t\tadvance();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tbool isType()\n\t\t{\n\t\t\tif (check(TokenType::Type)) return match(TokenType::Type);\n\t\t\tif (check(TokenType::Identifier)) {\n\t\t\t\t// Check if it's a user-defined struct type\n\t\t\t\tstd::string name = lexeme(peek());\n\t\t\t\tType type = prg.types.getStructType(name);\n\t\t\t\tif (type != nullptr) {\n\t\t\t\t\tadvance(); // consume the identifier\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tbool checkType()\n\t\t{\n\t\t\tif (check(TokenType::Type)) return true;\n\t\t\tif (check(TokenType::Identifier)) {\n\t\t\t\t// Check if it's a user-defined struct type\n\t\t\t\tstd::string name = lexeme(peek());\n\t\t\t\tType type = prg.types.getStructType(name);\n\t\t\t\tif (type != nullptr) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// --- Parsing functions (placeholders) ---\n\t\tstd::unique_ptr<Statement> parseDeclaration();\n\t\tstd::unique_ptr<Statement> parseStatement();\n\t\tstd::unique_ptr<Statement> parseVarDeclaration(Type type, const std::string& name);\n\t\tstd::unique_ptr<Statement> parseFunctionDeclaration(Type type, const std::string& name);\n\t\tstd::unique_ptr<Statement> parseStructDeclaration();\n\t\tstd::unique_ptr<Statement> parseReturnStatement();\n\t\tstd::unique_ptr<Statement> parseIfStatement();\n\t\tstd::unique_ptr<Statement> parseWhileStatement();\n\t\tstd::unique_ptr<Statement> parseForStatement();\n\t\tstd::unique_ptr<Statement> parseBlockStatement();\n\t\tstd::unique_ptr<Statement> parseExpressionStatement();\n\n\t\tstd::unique_ptr<Expression> parseExpression() { return parseAssignment(); }\n\t\tstd::unique_ptr<Expression> parseAssignment();\n\t\tstd::unique_ptr<Expression> parseOr();\n\t\tstd::unique_ptr<Expression> parseAnd();\n\t\tstd::unique_ptr<Expression> parseEquality();\n\t\tstd::unique_ptr<Expression> parseComparison();\n\t\tstd::unique_ptr<Expression> parseTerm();\n\t\tstd::unique_ptr<Expression> parseFactor();\n\t\tstd::unique_ptr<Expression> parseExponent();\n\t\tstd::unique_ptr<Expression> parseUnary();\n\t\tstd::unique_ptr<Expression> parseCall();\n\t\tstd::unique_ptr<Expression> parseConstructor(Type type);\n\t\tstd::unique_ptr<Expression> parsePrimary();\n\n\t\tstd::unique_ptr<Expression> finishCall(std::unique_ptr<Expression> callee);\n\n\t\t// --- Helper functions to map tokens to enums ---\n\t\tBinaryOp tokenToBinaryOp(const Token& t) {\n\t\t\tswitch (t.type) {\n\t\t\tcase TokenType::Plus: return BinaryOp::Plus;\n\t\t\tcase TokenType::Minus: return BinaryOp::Minus;\n\t\t\tcase TokenType::Star: return BinaryOp::Multiply;\n\t\t\tcase TokenType::Slash: return BinaryOp::Divide;\n\t\t\tcase TokenType::Exponent: return BinaryOp::Exponent;\n\t\t\tcase TokenType::EqualEqual: return BinaryOp::EqualEqual;\n\t\t\tcase TokenType::NotEqual: return BinaryOp::NotEqual;\n\t\t\tcase TokenType::Greater: return BinaryOp::Greater;\n\t\t\tcase TokenType::GreaterEqual: return BinaryOp::GreaterEqual;\n\t\t\tcase TokenType::Less: return BinaryOp::Less;\n\t\t\tcase TokenType::LessEqual: return BinaryOp::LessEqual;\n\t\t\tcase TokenType::AndAnd: return BinaryOp::AndAnd;\n\t\t\tcase TokenType::OrOr: return BinaryOp::OrOr;\n\t\t\tdefault: throw std::runtime_error(\"Invalid binary operator token\");\n\t\t\t}\n\t\t}\n\n\t\tUnaryOp tokenToUnaryOp(const Token& t) {\n\t\t\tswitch (t.type) {\n\t\t\tcase TokenType::Minus: return UnaryOp::Negate;\n\t\t\tcase TokenType::Not: return UnaryOp::Not;\n\t\t\tdefault: throw std::runtime_error(\"Invalid unary operator token\");\n\t\t\t}\n\t\t}\n\t};\n\n\tvoid printAST(const AST& ast);\n\n\tvoid prettyPrintAST(const AST& ast);\n\tvoid prettyPrintAST(std::ostream& os, const AST& ast);\n\tvoid prettyPrintExpression(std::ostream& os, const Expression& expr);\n\n\tvoid ParseSource(Program& prg, const std::string& source);\n\n} // namespace febcode\n\n"
  },
  {
    "path": "febcode/program.cpp",
    "content": "#include \"program.h\"\n#include \"module_math.h\"\n#include \"module_vec2.h\"\n#include \"module_vec3.h\"\n#include \"module_mat2.h\"\n#include \"module_mat3.h\"\n\nusing namespace febcode;\n\nProgram::Program()\n{\n\t// create (empty) AST\n\tast = std::make_unique<AST>();\n\n\t// Add \"main\" function (must be the first function, so it gets index 0)\n\tfunctions.push_back(FunctionInfo{\n\t\t\"main\",\n\t\tnullptr,\n\t\tstd::vector<Type>{},\n\t\t0,\n\t\t0,\n\t\tfalse\n\t\t});\n\n\t// register built-in binary operators for basic types\n\tbinaryOps[BinaryOp::Plus] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Int   () },\n\t\t{ types.Double(), types.Double(), types.Double() },\n\t\t{ types.Double(), types.Int   (), types.Double() },\n\t\t{ types.Int   (), types.Double(), types.Double() },\n\t};\n\tbinaryOps[BinaryOp::Minus] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Int   () },\n\t\t{ types.Double(), types.Double(), types.Double() },\n\t\t{ types.Double(), types.Int   (), types.Double() },\n\t\t{ types.Int   (), types.Double(), types.Double() },\n\t};\n\tbinaryOps[BinaryOp::Multiply] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Int   () },\n\t\t{ types.Double(), types.Double(), types.Double() },\n\t\t{ types.Double(), types.Int   (), types.Double() },\n\t\t{ types.Int   (), types.Double(), types.Double() },\n\t};\n\tbinaryOps[BinaryOp::Divide] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Int   () },\n\t\t{ types.Double(), types.Double(), types.Double() },\n\t\t{ types.Double(), types.Int   (), types.Double() },\n\t\t{ types.Int   (), types.Double(), types.Double() },\n\t};\n\tbinaryOps[BinaryOp::Exponent] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Int   () },\n\t\t{ types.Double(), types.Double(), types.Double() },\n\t\t{ types.Double(), types.Int   (), types.Double() },\n\t\t{ types.Int   (), types.Double(), types.Double() },\n\t};\n\tbinaryOps[BinaryOp::Greater] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Bool() },\n\t\t{ types.Double(), types.Double(), types.Bool() },\n\t\t{ types.Double(), types.Int   (), types.Bool() },\n\t\t{ types.Int   (), types.Double(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::GreaterEqual] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Bool() },\n\t\t{ types.Double(), types.Double(), types.Bool() },\n\t\t{ types.Double(), types.Int   (), types.Bool() },\n\t\t{ types.Int   (), types.Double(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::Less] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Bool() },\n\t\t{ types.Double(), types.Double(), types.Bool() },\n\t\t{ types.Double(), types.Int   (), types.Bool() },\n\t\t{ types.Int   (), types.Double(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::LessEqual] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Bool() },\n\t\t{ types.Double(), types.Double(), types.Bool() },\n\t\t{ types.Double(), types.Int   (), types.Bool() },\n\t\t{ types.Int   (), types.Double(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::EqualEqual] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Bool() },\n\t\t{ types.Double(), types.Double(), types.Bool() },\n\t\t{ types.Double(), types.Int   (), types.Bool() },\n\t\t{ types.Int   (), types.Double(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::NotEqual] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Int   (), types.Int   (), types.Bool() },\n\t\t{ types.Double(), types.Double(), types.Bool() },\n\t\t{ types.Double(), types.Int   (), types.Bool() },\n\t\t{ types.Int   (), types.Double(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::AndAnd] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Bool(), types.Bool(), types.Bool() },\n\t};\n\tbinaryOps[BinaryOp::OrOr] = {\n\t\t//   LHS           RHS             Result\n\t\t{ types.Bool(), types.Bool(), types.Bool() },\n\t};\n\n\t// register modules\n\tMathModule mathModule;\n\tmathModule.Register(*this);\n\n\tVec2Module vec2Module;\n\tvec2Module.Register(*this);\n\n\tVec3Module vec3Module;\n\tvec3Module.Register(*this);\n\n\tMat2Module mat2Module;\n\tmat2Module.Register(*this);\n\n\tMat3Module mat3Module;\n\tmat3Module.Register(*this);\n}\n\nint Program::addGlobal(const std::string& name, Type type)\n{\n\t// make sure the global variable name is unique\n\tauto it = globalIndices.find(name);\n\tif (it != globalIndices.end())\n\t\tthrow std::runtime_error(\"Global variable '\" + name + \"' is already defined.\");\n\n\tint slot = (int)globals.size();\n\tglobals.push_back({ type, (int)globalStackSize, false, 0 });\n\tglobalIndices[name] = slot;\n\n\tglobalStackSize += type->size(); // reserve stack slots for this global variable\n\n\treturn slot;\n}\n\nint Program::injectGlobal(const std::string& name, Type type)\n{\n\t// make sure the global variable name is unique\n\tauto it = globalIndices.find(name);\n\tif (it != globalIndices.end())\n\t\tthrow std::runtime_error(\"Global variable '\" + name + \"' is already defined.\");\n\n\tint slot = (int)globals.size();\n\tglobals.push_back({ type, (int)globalStackSize, true, 0 });\n\tglobalIndices[name] = slot;\n\n\tglobalStackSize += type->size(); // reserve stack slots for this global variable\n\n\treturn slot;\n}\n\nint Program::addInput(const std::string& name, Type type)\n{\n\tint slot = injectGlobal(name, type);\n\tinputIndices[name] = inputs.size();\n\tinputs.push_back({ type, name, slot });\n\treturn slot;\n}\n\nType Program::RegisterStruct(const std::string& name, const std::vector<std::pair<Type, std::string>>& fields)\n{\n\treturn types.defineStructType(name, fields);\n}\n\nType Program::RegisterStruct(const std::string& name, const std::vector<std::pair<TypeKind, std::string>>& fields)\n{\n\treturn types.defineStructType(name, fields);\n}\n\n\nvoid Program::registerNative(const std::string& name, Type returnType, std::vector<Type> argTypes, NativeFnc fn)\n{\n\t// calculate required stack size for arguments\n\tint argSize = 0;\n\tfor (const auto& argType : argTypes)\n\t{\n\t\targSize += (int)argType->size();\n\t}\n\n\tsize_t slot = functions.size();\n\tfunctions.push_back(FunctionInfo{\n\t\tname,\n\t\treturnType,\n\t\targTypes,\n\t\tslot,\n\t\targSize,\n\t\ttrue,\n\t\tfn\n\t\t});\n}\n\nvoid Program::registerNative(const std::string& name, double (*f)(double))\n{\n\tregisterNative(\n\t\tname,\n\t\ttypes.Double(),\n\t\t{ types.Double() },\n\t\t[f](FuncArgs args) -> Value {\n\t\t\tdouble arg = args.getDouble();\n\t\t\treturn f(arg);\n\t\t});\n}\n\nType Program::globalType(const std::string& name) const\n{\n\tauto it = globalIndices.find(name);\n\tif (it == globalIndices.end())\n\t\tthrow std::runtime_error(\"Global variable '\" + name + \"' is not defined.\");\n\tsize_t slot = it->second;\n\treturn globals[slot].type;\n}\n\nint Program::resolveFunction(const std::string& name, std::vector<Type> args)\n{\n\tfor (int i = 0; i < (int)functions.size(); ++i)\n\t{\n\t\tif ((functions[i].name == name) && (functions[i].args == args))\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nBinaryOpSignature Program::resolveBinaryOp(BinaryOp op, Type left, Type right)\n{\n\tauto it = binaryOps.find(op);\n\tif (it != binaryOps.end())\n\t{\n\t\tfor (const auto& sig : it->second)\n\t\t{\n\t\t\tif ((sig.leftType == left) && (sig.rightType == right))\n\t\t\t{\n\t\t\t\treturn sig;\n\t\t\t}\n\t\t\tif (sig.leftType == left)\n\t\t\t{\n\t\t\t\t// try implicit conversion of right operand\n\t\t\t\tType convertedRight = coerce(right, sig.rightType);\n\t\t\t\tif (convertedRight != nullptr)\n\t\t\t\t\treturn sig;\n\t\t\t}\n\t\t\tif (sig.rightType == right)\n\t\t\t{\n\t\t\t\t// try implicit conversion of left operand\n\t\t\t\tType convertedLeft = coerce(left, sig.leftType);\n\t\t\t\tif (convertedLeft != nullptr)\n\t\t\t\t\treturn sig;\n\t\t\t}\n\t\t}\n\t}\n\tthrow std::runtime_error(\"No matching binary operator found for operator '\" + opToString(op) + \"' with operand types '\" + TypeToString(left) + \"' and '\" + TypeToString(right) + \"'.\");\n}\n"
  },
  {
    "path": "febcode/program.h",
    "content": "#pragma once\n#include \"types.h\"\n#include \"ast.h\"\n\nnamespace febcode {\n\n\tstruct FunctionInfo\n\t{\n\t\tstd::string name;\n\t\tType returnType = nullptr;\n\t\tstd::vector<Type> args;\n\t\tsize_t entry = 0;\n\t\tint argSize = 0; // stack size needed for arguments.\n\t\tbool isNative = false;\n\t\tNativeFnc fnc;\n\n\t\tsize_t maxStackSize = 0;\n\t};\n\n\tclass Program\n\t{\n\tpublic:\n\t\tstruct Global\n\t\t{\n\t\t\tType type = nullptr;\n\t\t\tint slot = -1;\t// start index in stack\n\t\t\tbool immutable = false;\n\t\t\tint refcount = 0;\n\t\t};\n\n\t\tstruct Input\n\t\t{\n\t\t\tType type = nullptr;\n\t\t\tstd::string name;\n\t\t\tint slot = -1;\t// index in global's list\n\t\t};\n\n\tpublic:\n\t\tProgram();\n\n\t\t// adds a global variable (from compiled code)\n\t\tint addGlobal(const std::string& name, Type type);\n\n\t\t// adds a global variable (from native code)\n\t\tint injectGlobal(const std::string& name, Type type);\n\n\t\tint addInput(const std::string& name, Type type);\n\n\t\tType RegisterStruct(const std::string& name, const std::vector<std::pair<Type, std::string>>& fields);\n\t\tType RegisterStruct(const std::string& name, const std::vector<std::pair<TypeKind, std::string>>& fields);\n\n\t\tvoid registerNative(const std::string& name, Type returnType, std::vector<Type> argTypes, NativeFnc f);\n\n\t\tvoid registerNative(const std::string& name, double (*f)(double));\n\n\t\tType globalType(const std::string& name) const;\n\n\tpublic:\n\t\tint resolveFunction(const std::string& name, std::vector<Type> args);\n\n\t\tBinaryOpSignature resolveBinaryOp(BinaryOp op, Type left, Type right);\n\n\tpublic:\n\t\tstd::unique_ptr<AST> ast;\n\t\tTypeRegistry types;\n\n\t\tType returnType = nullptr; // expected return type of the program\n\n\t\tstd::vector<uint8_t> code;\n\t\tstd::vector<Value> constants;\n\t\tstd::vector<FunctionInfo> functions;\n\n\t\tsize_t globalStackSize = 0; // next available slot index for global variables\n\t\tstd::vector<Global> globals;\n\t\tstd::unordered_map<std::string, size_t> globalIndices; // maps global variable names to their slot index\n\n\t\tstd::vector<Input> inputs;\n\t\tstd::unordered_map<std::string, size_t> inputIndices; // maps global variable names to their slot index\n\n\t\tsize_t maxStackSize = 0;\n\n\t\t// binary operator signatures for operator overloading\n\t\tstd::unordered_map<BinaryOp, std::vector<BinaryOpSignature>> binaryOps;\n\t};\n}\n"
  },
  {
    "path": "febcode/resolver.cpp",
    "content": "#include \"resolver.h\"\n\nusing namespace febcode;\n\nResolver::Resolver(Program& prg) : prg(prg)\n{\n\n}\n\nResolver::~Resolver()\n{\n\n}\n\nvoid Resolver::resolve()\n{\n\t// add all global variables to the variables map so they can be resolved in expressions\n\tpushScope();\n\tfor (const auto& var : prg.globalIndices)\n\t{\n\t\tauto& global = prg.globals[var.second];\n\t\tdeclare(var.first, global.type);\n\t}\n\n\t// resolve all statements in the program\n\tresolveBlockStmt(&prg.ast->root);\n}\n\nvoid Resolver::resolveStatement(Statement* stmt)\n{\n\tif (stmt == nullptr)\n\t\tthrow std::runtime_error(\"Null statement cannot be resolved.\");\n\n\tif      (auto e = dynamic_cast<ExpressionStmt*>(stmt)) resolveExprStmt    (e);\n\telse if (auto v = dynamic_cast<VarDeclStmt*   >(stmt)) resolveVarDecl     (v);\n\telse if (auto i = dynamic_cast<IfStmt*        >(stmt)) resolveIfStmt      (i);\n\telse if (auto w = dynamic_cast<WhileStmt*     >(stmt)) resolveWhileStmt   (w);\n\telse if (auto f = dynamic_cast<ForStmt*       >(stmt)) resolveForStmt     (f);\n\telse if (auto b = dynamic_cast<BlockStmt*     >(stmt)) resolveBlockStmt   (b);\n\telse if (auto r = dynamic_cast<ReturnStmt*    >(stmt)) resolveReturnStmt  (r);\n\telse if (auto c = dynamic_cast<FunctionStmt*  >(stmt)) resolveFunctionStmt(c);\n\telse if (auto s = dynamic_cast<StructStmt*    >(stmt)) resolveStructStmt  (s);\n\telse \n\t\tthrow std::runtime_error(\"cannot resolve statement\");\n}\n\nvoid Resolver::resolveBlockStmt(BlockStmt* stmt)\n{\n\tpushScope();\n\tfor (auto& s : stmt->statements)\n\t{\n\t\tresolveStatement(s.get());\n\t}\n\tpopScope();\n}\n\nvoid Resolver::resolveExprStmt(ExpressionStmt* stmt)\n{\n\tresolveExpression(stmt->expr.get());\n}\n\nvoid Resolver::resolveVarDecl(VarDeclStmt* stmt)\n{\n\tfor (auto& var : stmt->vars)\n\t{\n\t\tType varType = nullptr;\n\t\tif (var.arraySizes.size() > 0)\n\t\t\tvarType = prg.types.getArrayType(stmt->type, var.arraySizes);\n\t\telse\n\t\t\tvarType = stmt->type;\n\n\t\t// resolve initializer first!\n\t\tif (var.initializer)\n\t\t{\n\t\t\tresolveExpression(var.initializer.get());\n\n\t\t\t// make sure initializer type can be converted to variable type\n\t\t\tif (commonType(var.initializer->valType, varType) == nullptr)\n\t\t\t\tthrow std::runtime_error(\"Type mismatch in variable initializer for variable '\" + var.name + \"'. Expected type: \" + TypeToString(varType) + \", but got: \" + TypeToString(var.initializer->valType));\n\t\t}\n\n\t\t// now we can declare the variable in the current scope\n\t\tdeclare(var.name, varType);\n\t}\n}\n\nvoid Resolver::resolveIfStmt(IfStmt* stmt)\n{\n\tresolveExpression(stmt->condition.get());\n\tif (!isLogicalType(stmt->condition->valType))\n\t\tthrow std::runtime_error(\"Condition expression in if statement must be of type bool. Got: \" + TypeToString(stmt->condition->valType));\n\tresolveStatement(stmt->thenBranch.get());\n\tif (stmt->elseBranch)\n\t\tresolveStatement(stmt->elseBranch.get());\n}\n\nvoid Resolver::resolveWhileStmt(WhileStmt* stmt)\n{\n\tresolveExpression(stmt->condition.get());\n\tif (!isLogicalType(stmt->condition->valType))\n\t\tthrow std::runtime_error(\"Condition expression in while statement must be of type bool. Got: \" + TypeToString(stmt->condition->valType));\n\tresolveStatement(stmt->body.get());\n}\n\nvoid Resolver::resolveForStmt(ForStmt* stmt)\n{\n\tif (stmt->initializer)\n\t\tresolveStatement(stmt->initializer.get());\n\tresolveExpression(stmt->condition.get());\n\tif (stmt->condition->valType != prg.types.Bool())\n\t\tthrow std::runtime_error(\"Condition expression in for statement must be of type bool. Got: \" + TypeToString(stmt->condition->valType));\n\tresolveExpression(stmt->increment.get());\n\tresolveStatement(stmt->body.get());\n}\n\nvoid Resolver::resolveReturnStmt(ReturnStmt* stmt)\n{\n\tif (stmt->value)\n\t{\n\t\tresolveExpression(stmt->value.get());\n\t\tif (prg.returnType && (stmt->value->valType != prg.returnType))\n\t\t\tthrow std::runtime_error(\"Type mismatch in return statement. Expected type: \" + TypeToString(prg.returnType) + \", but got: \" + TypeToString(stmt->value->valType));\n\t}\n\telse\n\t{\n\t\tif (prg.returnType && (prg.returnType != prg.types.Void()))\n\t\t\tthrow std::runtime_error(\"Missing return value in function with non-void return type.\");\n\t}\n}\n\nvoid Resolver::resolveFunctionStmt(FunctionStmt* fn)\n{\n\tFunctionInfo info;\n\tinfo.name = fn->name;\n\tinfo.entry = prg.code.size();\n\tinfo.returnType = fn->returnType;\n\n\tpushScope();\n\t// add function parameters to variables map\n\tfor (const auto& param : fn->params)\n\t{\n\t\tinfo.args.push_back(param.first);\n\t\tdeclare(param.second, param.first);\n\t}\n\tresolveStatement(fn->body.get());\n\tpopScope();\n\n\t// make sure the function is not defined yet\n\tint index = prg.resolveFunction(fn->name, info.args);\n\tif (index != -1)\n\t\tthrow std::runtime_error(\"Function '\" + fn->name + \"' with the same parameter types is already defined.\");\n\n\t// add it to the program's function list\n\tprg.functions.push_back(info);\n}\n\nvoid Resolver::resolveStructStmt(StructStmt* stmt)\n{\n\t// nothing to do here since struct definitions are handled during parsing and type registration.\n}\n\nvoid Resolver::resolveExpression(Expression* expr)\n{\n\tif (expr == nullptr)\n\t\tthrow std::runtime_error(\"Null expression cannot be resolved.\");\n\n\tif      (auto l = dynamic_cast<LiteralExpr*    >(expr)) resolveLiteral    (l);\n\telse if (auto v = dynamic_cast<VariableExpr*   >(expr)) resolveVariable   (v);\n\telse if (auto a = dynamic_cast<AssignExpr*     >(expr)) resolveAssignment (a);\n\telse if (auto u = dynamic_cast<UnaryExpr*      >(expr)) resolveUnary      (u);\n\telse if (auto b = dynamic_cast<BinaryExpr*     >(expr)) resolveBinary     (b);\n\telse if (auto m = dynamic_cast<MemberExpr*     >(expr)) resolveMember     (m);\n\telse if (auto i = dynamic_cast<IndexExpr*      >(expr)) resolveIndex      (i);\n\telse if (auto i = dynamic_cast<InitExpr*       >(expr)) resolveInitializer(i);\n\telse if (auto c = dynamic_cast<CallExpr*       >(expr)) resolveCall       (c);\n\telse if (auto c = dynamic_cast<ConstructorExpr*>(expr)) resolveConstructor(c);\n\telse\n\t\tthrow std::runtime_error(\"cannot resolve expression\");\n}\n\nvoid Resolver::resolveLiteral(LiteralExpr* expr)\n{\n\texpr->valType = prg.types.getBuiltinType(expr->value);\n}\n\nvoid Resolver::resolveVariable(VariableExpr* expr)\n{\n\tType type = lookup(expr->name);\n\tif (type == nullptr)\n\t\tthrow std::runtime_error(\"Undefined variable: \" + expr->name);\n\texpr->valType = type;\n}\n\nvoid Resolver::resolveAssignment(AssignExpr* expr)\n{\n\tresolveExpression(expr->target.get());\n\tresolveExpression(expr->value.get());\n\tif (expr->target->valType == nullptr)\n\t\tthrow std::runtime_error(\"Invalid assignment target.\");\n\tif (expr->target->valType != expr->value->valType)\n\t\tthrow std::runtime_error(\"Type mismatch in assignment. Expected type: \" + TypeToString(expr->target->valType) + \", but got: \" + TypeToString(expr->value->valType));\n\texpr->valType = expr->target->valType;\n}\n\nvoid Resolver::resolveUnary(UnaryExpr* expr)\n{\n\tresolveExpression(expr->right.get());\n\texpr->valType = expr->right->valType;\n\n\tswitch (expr->op)\n\t{\n\tcase UnaryOp::Negate:\n\t\tif (!isNumericType(expr->valType))\n\t\t\tthrow std::runtime_error(\"Unary '-' operator can only be applied to numeric types. Got: \" + TypeToString(expr->valType));\n\t\tbreak;\n\tcase UnaryOp::Not:\n\t\tif (expr->valType != prg.types.Bool())\n\t\t\tthrow std::runtime_error(\"Unary '!' operator can only be applied to bool type. Got: \" + TypeToString(expr->valType));\n\t\tbreak;\n\tdefault:\n\t\tthrow std::runtime_error(\"Unsupported unary operator.\");\n\t}\n}\n\nvoid Resolver::resolveBinary(BinaryExpr* expr)\n{\n\tresolveExpression(expr->left.get());\n\tresolveExpression(expr->right.get());\n\n\tBinaryOpSignature sig = prg.resolveBinaryOp(expr->op, expr->left->valType, expr->right->valType);\n\texpr->valType = sig.resultType;\n}\n\nvoid Resolver::resolveMember(MemberExpr* expr)\n{\n\tresolveExpression(expr->object.get());\n\tType objType = expr->object->valType;\n\tif (objType == nullptr)\n\t\tthrow std::runtime_error(\"Invalid member access on unresolved type.\");\n\n\tif (objType->kind == TypeKind::Vec2)\n\t{\n\t\tint l = (int)expr->property.length();\n\t\t// make sure it's a valid swizzle (only 'x', 'y' allowed, and length must be 1, 2, 3)\n\t\tfor (char c : expr->property)\n\t\t{\n\t\t\tif ((c != 'x') && (c != 'y'))\n\t\t\t\tthrow std::runtime_error(\"Invalid character '\" + std::string(1, c) + \"' in swizzle. Only 'x' and 'y' are allowed.\");\n\t\t}\n\n\t\tif      (l == 1) expr->valType = prg.types.Double();\n\t\telse if (l == 2) expr->valType = prg.types.Vec2();\n\t\telse if (l == 3) expr->valType = prg.types.Vec3();\n\t\telse\n\t\t\tthrow std::runtime_error(\"Invalid property '\" + expr->property + \"' for type vec2.\");\n\n\t\treturn;\n\t}\n\telse if (objType->kind == TypeKind::Vec3)\n\t{\n\t\tint l = (int)expr->property.length();\n\t\t// make sure it's a valid swizzle (only 'x', 'y', 'z' allowed, and length must be 1, 2, or 3)\n\t\tfor (char c : expr->property)\n\t\t{\n\t\t\tif ((c != 'x') && (c != 'y') && (c != 'z'))\n\t\t\t\tthrow std::runtime_error(\"Invalid character '\" + std::string(1, c) + \"' in swizzle. Only 'x', 'y', and 'z' are allowed.\");\n\t\t}\n\n\t\tif      (l == 1) expr->valType = prg.types.Double();\n\t\telse if (l == 2) expr->valType = prg.types.Vec2();\n\t\telse if (l == 3) expr->valType = prg.types.Vec3();\n\t\telse\n\t\t\tthrow std::runtime_error(\"Invalid property '\" + expr->property + \"' for type vec3.\");\n\n\t\treturn;\n\t}\n\telse if (objType->kind != TypeKind::Struct)\n\t\tthrow std::runtime_error(\"Member access on non-struct type.\");\n\n\tconst auto& fields = objType->fields;\n\tfor (const auto& field : fields)\n\t{\n\t\tif (field.second == expr->property)\n\t\t{\n\t\t\texpr->valType = field.first;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tthrow std::runtime_error(\"Field '\" + expr->property + \"' not found in struct.\");\n}\n\nvoid Resolver::resolveIndex(IndexExpr* expr)\n{\n\tresolveExpression(expr->object.get());\n\tresolveExpression(expr->index.get());\n\n\tif (expr->index->valType != prg.types.Int())\n\t\tthrow std::runtime_error(\"Index must be of type int. Got: \" + TypeToString(expr->index->valType));\n\n\tType arrayType = expr->object->valType;\n\tif (arrayType == nullptr)\n\t\tthrow std::runtime_error(\"Invalid indexing on unresolved type.\");\n\n\tif (arrayType->kind == TypeKind::Array)\n\t{\n\t\texpr->valType = arrayType->elementType;\n\t}\n\telse if (arrayType->kind == TypeKind::Mat2)\n\t{\n\t\texpr->valType = prg.types.Vec2();\n\t}\n\telse if (arrayType->kind == TypeKind::Mat3)\n\t{\n\t\texpr->valType = prg.types.Vec3();\n\t}\n\telse if (arrayType->kind == TypeKind::Vec2)\n\t{\n\t\texpr->valType = prg.types.Double();\n\t}\n\telse if (arrayType->kind == TypeKind::Vec3)\n\t{\n\t\texpr->valType = prg.types.Double();\n\t}\n\telse\n\t{\n\t\tthrow std::runtime_error(\"Indexing can only be applied to array, vector, or matrix types. Got: \" + TypeToString(arrayType));\n\t}\n}\n\nvoid Resolver::resolveInitializer(InitExpr* expr)\n{\n\tType elemType = nullptr;\n\tfor (auto& element : expr->elements)\n\t{\n\t\tresolveExpression(element.get());\n\t\tif (element->valType == nullptr)\n\t\t\tthrow std::runtime_error(\"Invalid initializer element with unresolved type.\");\n\n\t\tif (elemType == nullptr)\n\t\t\t{\n\t\t\telemType = element->valType;\n\t\t}\n\t\telse if (element->valType != elemType)\n\t\t{\n\t\t\tthrow std::runtime_error(\"All elements in an initializer must have the same type. Got: \" + TypeToString(elemType) + \" and \" + TypeToString(element->valType));\n\t\t}\n\t}\n\n\texpr->valType = prg.types.getArrayType(elemType, expr->elements.size());\n}\n\nvoid Resolver::resolveConstructor(ConstructorExpr* expr)\n{\n\tif (expr->valType == nullptr)\n\t\tthrow std::runtime_error(\"Invalid constructor with unresolved type.\");\n\n\tfor (auto& arg : expr->args)\n\t{\n\t\tresolveExpression(arg.get());\n\t\tif (arg->valType == nullptr)\n\t\t\tthrow std::runtime_error(\"Invalid constructor argument with unresolved type.\");\n\t}\n}\n\nvoid Resolver::resolveCall(CallExpr* expr)\n{\n\tstd::vector<Type> argTypes;\n\tfor (size_t i = 0; i < expr->arguments.size(); ++i)\n\t{\n\t\tresolveExpression(expr->arguments[i].get());\n\t\tif (expr->arguments[i]->valType == nullptr)\n\t\t\tthrow std::runtime_error(\"Invalid function call argument with unresolved type.\");\n\t\targTypes.push_back(expr->arguments[i]->valType);\n\t}\n\n\tint index = prg.resolveFunction(expr->name, argTypes);\n\tif (index < 0)\n\t\tthrow std::runtime_error(\"Undefined function: \" + expr->name + \" (or can't match arguments)\");\n\n\tFunctionInfo& func = prg.functions[index];\n\tif (func.returnType == nullptr)\n\t\tthrow std::runtime_error(\"Invalid function with unresolved return type.\");\n\n\texpr->valType = func.returnType;\n}\n"
  },
  {
    "path": "febcode/resolver.h",
    "content": "#pragma once\n#include \"ast.h\"\n#include \"program.h\"\n#include <unordered_map>\n#include <string>\n\nnamespace febcode {\n\n\t// This class is resonsible for type inference and other semantic analysis. \n\t// It should be called after the AST is generated, and it will traverse the AST to resolve types and check for semantic errors. It will also generate intermediate code for the AST, which will be used later for code generation.\n\tclass Resolver\n\t{\n\tpublic:\n\t\tResolver(Program& prg);\n\t\t~Resolver();\n\t\tvoid resolve();\n\n\t\tusing Scope = std::unordered_map<std::string, Type>;\n\n\tprivate:\n\t\tvoid resolveStatement   (Statement*      stmt);\n\t\tvoid resolveExprStmt    (ExpressionStmt* stmt);\n\t\tvoid resolveVarDecl     (VarDeclStmt*    stmt);\n\t\tvoid resolveIfStmt      (IfStmt*         stmt);\n\t\tvoid resolveWhileStmt   (WhileStmt*      stmt);\n\t\tvoid resolveForStmt     (ForStmt*        stmt);\n\t\tvoid resolveBlockStmt   (BlockStmt*      stmt);\n\t\tvoid resolveReturnStmt  (ReturnStmt*     stmt);\n\t\tvoid resolveFunctionStmt(FunctionStmt*   stmt);\n\t\tvoid resolveStructStmt  (StructStmt*     stmt);\n\n\t\tvoid resolveExpression (Expression*      expr);\n\t\tvoid resolveLiteral    (LiteralExpr*     expr);\n\t\tvoid resolveVariable   (VariableExpr*    expr);\n\t\tvoid resolveAssignment (AssignExpr*      expr);\n\t\tvoid resolveUnary      (UnaryExpr*       expr);\n\t\tvoid resolveBinary     (BinaryExpr*      expr);\n\t\tvoid resolveMember     (MemberExpr*      expr);\n\t\tvoid resolveIndex      (IndexExpr*       expr);\n\t\tvoid resolveInitializer(InitExpr*        expr);\n\t\tvoid resolveConstructor(ConstructorExpr* expr);\n\t\tvoid resolveCall       (CallExpr*        expr);\n\n\tprivate:\n\t\tvoid pushScope() {\n\t\t\tscopeStack.emplace_back();\n\t\t}\n\n\t\tvoid popScope() {\n\t\t\tscopeStack.pop_back();\n\t\t}\n\n\t\tvoid declare(const std::string& name, Type type) {\n\t\t\tScope& current = scopeStack.back();\n\t\t\tcurrent[name] = { type };\n\t\t}\n\n\t\tType lookup(const std::string& name) {\n\t\t\tfor (int i = (int)scopeStack.size() - 1; i >= 0; --i) {\n\t\t\t\tauto it = scopeStack[i].find(name);\n\t\t\t\tif (it != scopeStack[i].end())\n\t\t\t\t\treturn it->second;\n\t\t\t}\n\t\t\treturn nullptr;\n\t\t}\n\n\tprivate:\n\t\tProgram& prg;\n\n\t\tstd::vector<Scope> scopeStack;\n\t};\n}\n"
  },
  {
    "path": "febcode/scanner.h",
    "content": "#pragma once\n#include <string>\n#include <vector>\n#include <cstring>\nnamespace febcode {\n\n\tenum class TokenType {\n\t\t// Single-character tokens\n\t\tLeftParen, RightParen,\n\t\tLeftBrace, RightBrace,\n\t\tLeftBrack, RightBrack,\n\t\tPlus, Minus, Star, Slash, Exponent,\n\t\tLess, Greater,\n\t\tEqual, Not,\n\t\tSemicolon,\n\t\tColon,\n\t\tComma,\n\t\tDot,\n\n\t\t// Multi-character tokens\n\t\tNotEqual, EqualEqual,\n\t\tGreaterEqual, LessEqual,\n\t\tAndAnd, OrOr,\n\n\t\t// Literals\n\t\tIdentifier,\n\t\tInteger,\n\t\tDouble,\n\t\tTrue,\n\t\tFalse,\n\n\t\t// Keywords\n\t\tReturn,\n\t\tIf,\n\t\tElse,\n\t\tWhile,\n\t\tFor,\n\t\tStruct,\n\t\tInput,\n\n\t\t// for type definitions\n\t\tType,\n\n\t\t// Special\n\t\tEndOfFile,\n\t\tError,\n\t};\n\n\tstruct Token {\n\t\tTokenType type;\n\t\tconst char* start;\n\t\tint length;\n\t\tint line;\n\t};\n\n\tclass Scanner {\n\tpublic:\n\t\tScanner(const std::string& source)\n\t\t\t: source(source)\n\t\t{\n\t\t\tstart = source.c_str();\n\t\t\tcurrent = start;\n\t\t\tline = 1;\n\t\t}\n\n\t\tstd::vector<Token> scanTokens() {\n\t\t\tstd::vector<Token> tokens;\n\t\t\twhile (true) {\n\t\t\t\tToken token = nextToken();\n\t\t\t\ttokens.push_back(token);\n\t\t\t\tif (token.type == TokenType::EndOfFile) break;\n\t\t\t}\n\t\t\treturn tokens;\n\t\t}\n\n\t\tToken nextToken() {\n\t\t\tskipWhitespace();\n\t\t\tstart = current;\n\n\t\t\tif (isAtEnd()) return makeToken(TokenType::EndOfFile);\n\n\t\t\tchar c = advance();\n\n\t\t\t// multi-character tokens\n\t\t\tswitch (c)\n\t\t\t{\n\t\t\tcase '!':\n\t\t\t\tif (peek() == '=') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::NotEqual);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '=':\n\t\t\t\tif (peek() == '=') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::EqualEqual);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '<':\n\t\t\t\tif (peek() == '=') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::LessEqual);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '>':\n\t\t\t\tif (peek() == '=') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::GreaterEqual);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '&':\n\t\t\t\tif (peek() == '&') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::AndAnd);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '|':\n\t\t\t\tif (peek() == '|') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::OrOr);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '*':\n\t\t\t\tif (peek() == '*') {\n\t\t\t\t\tadvance();\n\t\t\t\t\treturn makeToken(TokenType::Exponent);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '/':\n\t\t\t\tif (peek() == '/') {\n\t\t\t\t\t// Comment until end of line\n\t\t\t\t\twhile (peek() != '\\n' && !isAtEnd()) advance();\n\t\t\t\t\treturn nextToken(); // skip comment token\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Single-character tokens\n\t\t\tswitch (c) {\n\t\t\tcase '(': return makeToken(TokenType::LeftParen);\n\t\t\tcase ')': return makeToken(TokenType::RightParen);\n\t\t\tcase '{': return makeToken(TokenType::LeftBrace);\n\t\t\tcase '}': return makeToken(TokenType::RightBrace);\n\t\t\tcase '[': return makeToken(TokenType::LeftBrack);\n\t\t\tcase ']': return makeToken(TokenType::RightBrack);\n\t\t\tcase '+': return makeToken(TokenType::Plus);\n\t\t\tcase '-': return makeToken(TokenType::Minus);\n\t\t\tcase '*': return makeToken(TokenType::Star);\n\t\t\tcase '/': return makeToken(TokenType::Slash);\n\t\t\tcase '=': return makeToken(TokenType::Equal);\n\t\t\tcase ';': return makeToken(TokenType::Semicolon);\n\t\t\tcase ':': return makeToken(TokenType::Colon);\n\t\t\tcase ',': return makeToken(TokenType::Comma);\n\t\t\tcase '.': return makeToken(TokenType::Dot);\n\t\t\tcase '<': return makeToken(TokenType::Less);\n\t\t\tcase '>': return makeToken(TokenType::Greater);\n\t\t\tcase '!': return makeToken(TokenType::Not);\n\t\t\t}\n\n\t\t\tif (std::isdigit(c)) return number();\n\t\t\tif (isAlpha(c)) return identifier();\n\n\t\t\treturn errorToken(\"Unexpected character.\");\n\t\t}\n\n\tprivate:\n\t\tbool isAtEnd() const {\n\t\t\treturn *current == '\\0';\n\t\t}\n\n\t\tchar advance() {\n\t\t\treturn *current++;\n\t\t}\n\n\t\tvoid skipWhitespace() {\n\t\t\twhile (true) {\n\t\t\t\tchar c = *current;\n\t\t\t\tswitch (c) {\n\t\t\t\tcase ' ':\n\t\t\t\tcase '\\r':\n\t\t\t\tcase '\\t':\n\t\t\t\t\tcurrent++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\n':\n\t\t\t\t\tline++;\n\t\t\t\t\tcurrent++;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tToken makeToken(TokenType type) const {\n\t\t\treturn Token{ type, start, int(current - start), line };\n\t\t}\n\n\t\tToken errorToken(const char* message) const {\n\t\t\treturn Token{ TokenType::Error, message, int(std::strlen(message)), line };\n\t\t}\n\n\t\tToken number() {\n\t\t\twhile (std::isdigit(peek())) advance();\n\n\t\t\t// fractional part\n\t\t\tif (peek() == '.') {\n\t\t\t\tadvance(); // consume '.'\n\t\t\t\twhile (std::isdigit(peek())) advance();\n\t\t\t\tif ((peek() == 'e') || (peek() == 'E'))\n\t\t\t\t{\n\t\t\t\t\tadvance(); // consume 'e' or 'E'\n\t\t\t\t\tif ((peek() == '+') || (peek() == '-')) advance();\n\t\t\t\t\twhile (std::isdigit(peek())) advance();\n\t\t\t\t}\n\t\t\t\treturn makeToken(TokenType::Double);\n\t\t\t}\n\t\t\treturn makeToken(TokenType::Integer);\n\t\t}\n\n\t\tToken identifier() {\n\t\t\twhile (isAlphaNumeric(peek())) advance();\n\n\t\t\tint length = int(current - start);\n\n\t\t\t// Keyword checks\n\t\t\tif (length == 2 && std::memcmp(start, \"if\", 2) == 0)\n\t\t\t\treturn makeToken(TokenType::If);\n\n\t\t\tif (length == 4 && std::memcmp(start, \"else\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Else);\n\n\t\t\tif (length == 6 && std::memcmp(start, \"return\", 6) == 0)\n\t\t\t\treturn makeToken(TokenType::Return);\n\n\t\t\tif (length == 5 && std::memcmp(start, \"while\", 5) == 0)\n\t\t\t\treturn makeToken(TokenType::While);\n\n\t\t\tif (length == 3 && std::memcmp(start, \"for\", 3) == 0)\n\t\t\t\treturn makeToken(TokenType::For);\n\n\t\t\tif (length == 6 && std::memcmp(start, \"struct\", 6) == 0)\n\t\t\t\treturn makeToken(TokenType::Struct);\n\n\t\t\tif (length == 4 && std::memcmp(start, \"true\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::True);\n\n\t\t\tif (length == 5 && std::memcmp(start, \"false\", 5) == 0)\n\t\t\t\treturn makeToken(TokenType::False);\n\n\t\t\tif (length == 2 && std::memcmp(start, \"in\", 2) == 0)\n\t\t\t\treturn makeToken(TokenType::Input);\n\n\t\t\t// type checks\n\t\t\tif (length == 4 && std::memcmp(start, \"void\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 4 && std::memcmp(start, \"bool\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 3 && std::memcmp(start, \"int\", 3) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 6 && std::memcmp(start, \"double\", 6) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 4 && std::memcmp(start, \"vec2\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 4 && std::memcmp(start, \"vec3\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 4 && std::memcmp(start, \"mat2\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\t\t\tif (length == 4 && std::memcmp(start, \"mat3\", 4) == 0)\n\t\t\t\treturn makeToken(TokenType::Type);\n\n\t\t\t// Fallback: regular identifier\n\t\t\treturn makeToken(TokenType::Identifier);\n\t\t}\n\n\t\tchar peek() const {\n\t\t\treturn *current;\n\t\t}\n\n\t\tchar peekNext() const {\n\t\t\tif (isAtEnd()) return '\\0';\n\t\t\treturn current[1];\n\t\t}\n\n\t\tstatic bool isAlpha(char c) {\n\t\t\treturn std::isalpha(static_cast<unsigned char>(c)) || c == '_';\n\t\t}\n\n\t\tstatic bool isAlphaNumeric(char c) {\n\t\t\treturn isAlpha(c) || std::isdigit(static_cast<unsigned char>(c));\n\t\t}\n\n\tprivate:\n\t\tconst std::string& source;\n\t\tconst char* start;\n\t\tconst char* current;\n\t\tint line;\n\t};\n}\n"
  },
  {
    "path": "febcode/simplifier.cpp",
    "content": "#include \"simplifier.h\"\nusing namespace febcode;\n\nSimplifier::Simplifier(Program& prg) : Modifier(prg) \n{\n\t// --- constant folding rules ---\n\t// integer constant folding\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *b;\n\t\tint l, r;\n\t\tif (isAdd(e, a, b) && isInt(a, l) && isInt(b, r)) return Literal(l + r);\n\t\tif (isSub(e, a, b) && isInt(a, l) && isInt(b, r)) return Literal(l - r);\n\t\tif (isMul(e, a, b) && isInt(a, l) && isInt(b, r)) return Literal(l * r);\n\t\tif (isDiv(e, a, b) && isInt(a, l) && isInt(b, r)) return Literal(l / r);\n\t\tif (isExp(e, a, b) && isInt(a, l) && isInt(b, r)) return Literal(static_cast<int>(std::pow(l, r)));\n\t\tif (isNegate(e, a) && isInt(a, l)) return Literal(-l);\n\n\t\treturn nullptr;\n\t});\n\n\t// scalar constant folding\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tdouble l, r;\n\t\tif (isAdd(e, a, b) && isScalar(a, l) && isScalar(b, r)) return Literal(l + r);\n\t\tif (isSub(e, a, b) && isScalar(a, l) && isScalar(b, r)) return Literal(l - r);\n\t\tif (isMul(e, a, b) && isScalar(a, l) && isScalar(b, r)) return Literal(l * r);\n\t\tif (isExp(e, a, b) && isScalar(a, l) && isScalar(b, r)) return Literal(std::pow(l, r));\n\t\tif (isDiv(e, a, b) && isScalar(a, l) && isScalar(b, r))\n\t\t{\n\t\t\tif (r == 0.0)\n\t\t\t\tthrow std::runtime_error(\"Division by zero in constant folding\");\n\t\t\treturn Literal(l / r);\n\t\t}\n\t\tif (isNegate(e, a) && isDouble(a, l)) return Literal(-l);\n\t\treturn nullptr;\n\t});\n\n\t// vec2 constant folding for binary operations\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\t\n\t\tvec2 l, r;\n\t\tif (isAdd(e, a, b) && isVec2(a, l) && isVec2(b, r)) return Literal(l + r);\n\t\tif (isSub(e, a, b) && isVec2(a, l) && isVec2(b, r)) return Literal(l - r);\n\t\tif (isMul(e, a, b) && isVec2(a, l) && isVec2(b, r)) return Literal(l * r);\n\n\t\tdouble scalar;\n\t\tif (isMul(e, a, b) && isVec2(a, l) && isScalar(b, scalar)) return Literal(l * scalar);\n\t\tif (isDiv(e, a, b) && isVec2(a, l) && isScalar(b, scalar)) return Literal(l / scalar);\n\n\t\tif (isMul(e, a, b) && isScalar(a, scalar) && isVec2(b, r)) return Literal(r * scalar);\n\n\t\treturn nullptr;\n\t});\n\n\t// vec3 constant folding for binary operations\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\n\t\tvec3 l, r;\n\t\tif (isAdd(e, a, b) && isVec3(a, l) && isVec3(b, r)) return Literal(l + r);\n\t\tif (isSub(e, a, b) && isVec3(a, l) && isVec3(b, r)) return Literal(l - r);\n\t\tif (isMul(e, a, b) && isVec3(a, l) && isVec3(b, r)) return Literal(l * r);\n\n\t\tdouble scalar;\n\t\tif (isMul(e, a, b) && isVec3(a, l) && isScalar(b, scalar)) return Literal(l * scalar);\n\t\tif (isDiv(e, a, b) && isVec3(a, l) && isScalar(b, scalar)) return Literal(l / scalar);\n\n\t\tif (isMul(e, a, b) && isScalar(a, scalar) && isVec3(b, r)) return Literal(r * scalar);\n\n\t\treturn nullptr;\n\t});\n\n\t// mat2 constant folding for binary operations\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\n\t\tmat2 l, r;\n\t\tif (isAdd(e, a, b) && isMat2(a, l) && isMat2(b, r)) return Literal(l + r);\n\t\tif (isSub(e, a, b) && isMat2(a, l) && isMat2(b, r)) return Literal(l - r);\n\t\tif (isMul(e, a, b) && isMat2(a, l) && isMat2(b, r)) return Literal(l * r);\n\n\t\tdouble scalar;\n\t\tif (isMul(e, a, b) && isMat2(a, l) && isScalar(b, scalar)) return Literal(l * scalar);\n\t\tif (isDiv(e, a, b) && isMat2(a, l) && isScalar(b, scalar)) return Literal(l / scalar);\n\t\tif (isMul(e, a, b) && isScalar(a, scalar) && isMat2(b, r)) return Literal(r * scalar);\n\n\t\tvec2 v;\n\t\tif (isMul(e, a, b) && isMat2(a, l) && isVec2(b, v)) return Literal(l * v);\n\n\t\treturn nullptr;\n\t});\n\n\t// mat3 constant folding for binary operations\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\n\t\tmat3 l, r;\n\t\tif (isAdd(e, a, b) && isMat3(a, l) && isMat3(b, r)) return Literal(l + r);\n\t\tif (isSub(e, a, b) && isMat3(a, l) && isMat3(b, r)) return Literal(l - r);\n\t\tif (isMul(e, a, b) && isMat3(a, l) && isMat3(b, r)) return Literal(l * r);\n\n\t\tdouble scalar;\n\t\tif (isMul(e, a, b) && isMat3(a, l) && isScalar(b, scalar)) return Literal(l * scalar);\n\t\tif (isDiv(e, a, b) && isMat3(a, l) && isScalar(b, scalar)) return Literal(l / scalar);\n\t\tif (isMul(e, a, b) && isScalar(a, scalar) && isMat3(b, r)) return Literal(r * scalar);\n\n\t\tvec3 v;\n\t\tif (isMul(e, a, b) && isMat3(a, l) && isVec3(b, v)) return Literal(l * v);\n\n\t\treturn nullptr;\n\t});\n\n\t// function evaluation for built-in functions with constant arguments\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tif (auto call = dynamic_cast<const CallExpr*>(e))\n\t\t{\n\t\t\tstd::vector<double> scalarArgs;\n\t\t\tfor (const auto& arg : call->arguments)\n\t\t\t{\n\t\t\t\tdouble value;\n\t\t\t\tif (!isScalar(arg.get(), value)) return nullptr; // not all arguments are scalar constants\n\t\t\t\tscalarArgs.push_back(value);\n\t\t\t}\n\t\t\tconst std::string& fname = call->name;\n\t\t\tif (scalarArgs.size() == 1)\n\t\t\t{\n\t\t\t\t// Make sure this list matches the built-in functions defined in the math module!\n\t\t\t\tif (fname == \"abs\"  ) return Literal(std::abs  (scalarArgs[0]));\n\t\t\t\tif (fname == \"acos\" ) return Literal(std::acos (scalarArgs[0]));\n\t\t\t\tif (fname == \"acosh\") return Literal(std::acosh(scalarArgs[0]));\n\t\t\t\tif (fname == \"asin\" ) return Literal(std::asin (scalarArgs[0]));\n\t\t\t\tif (fname == \"asinh\") return Literal(std::asinh(scalarArgs[0]));\n\t\t\t\tif (fname == \"atan\" ) return Literal(std::atan (scalarArgs[0]));\n\t\t\t\tif (fname == \"atanh\") return Literal(std::atanh(scalarArgs[0]));\n\t\t\t\tif (fname == \"cos\"  ) return Literal(std::cos  (scalarArgs[0]));\n\t\t\t\tif (fname == \"cosh\" ) return Literal(std::cosh (scalarArgs[0]));\n\t\t\t\tif (fname == \"exp\"  ) return Literal(std::exp  (scalarArgs[0]));\n\t\t\t\tif (fname == \"log\"  ) return Literal(std::log  (scalarArgs[0]));\n\t\t\t\tif (fname == \"log10\") return Literal(std::log10(scalarArgs[0]));\n\t\t\t\tif (fname == \"sin\"  ) return Literal(std::sin  (scalarArgs[0]));\n\t\t\t\tif (fname == \"sinh\" ) return Literal(std::sinh (scalarArgs[0]));\n\t\t\t\tif (fname == \"sqrt\" ) return Literal(std::sqrt (scalarArgs[0]));\n\t\t\t\tif (fname == \"tan\"  ) return Literal(std::tan  (scalarArgs[0]));\n\t\t\t\tif (fname == \"tanh\" ) return Literal(std::tanh (scalarArgs[0]));\n\t\t\t}\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// --- algebraic simplification rules ---\n\t\n\t// -0 = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a;\n\t\tif (isNegate(e, a) && isZero(a)) return Zero(e->valType); // -0 = 0\n\t\treturn nullptr;\n\t});\n\n\t// --x = x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *b;\n\t\tif (isNegate(e, a) && isNegate(a, b)) return simplify(b); // --x = x\n\t\treturn nullptr;\n\t});\n\t\n\t// x +/- 0 = x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *b;\n\t\tif (isAdd(e, a, b))\n\t\t{\n\t\t\tif (isZero(a)) return simplify(b); // 0 + x = x\n\t\t\tif (isZero(b)) return simplify(a); // x + 0 = x\n\t\t}\n\t\tif (isSub(e, a, b))\n\t\t{\n\t\t\tif (isZero(a)) return simplify(Negate(b)); // 0 - x = -x\n\t\t\tif (isZero(b)) return simplify(a); // x - 0 = x\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// a + (-b) = a - b, a - (-b) = a + b\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *n, *b;\n\t\tif (isAdd(e, a, n) && isNegate(n, b))\n\t\t{\n\t\t\t return simplify(Binary(BinaryOp::Minus, simplify(a), simplify(b))); // a + (-b) = a - b\n\t\t}\n\t\tif (isSub(e, a, n) && isNegate(n, b))\n\t\t{\n\t\t\treturn simplify(Binary(BinaryOp::Plus, simplify(a), simplify(b))); // a - (-b) = a + b\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// x * 0 = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *b;\n\t\tif (isMul(e, a, b))\n\t\t{\n\t\t\tif (isZero(a)) return Zero(e->valType); // 0 * x = 0\n\t\t\tif (isZero(b)) return Zero(e->valType); // x * 0 = 0\n\t\t}\n\t\treturn nullptr;\n\t\t});\n\n\t// x * 1 = x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *b;\n\t\tif (isMul(e, a, b))\n\t\t{\n\t\t\tif (isOne(a)) return simplify(b); // 1 * x = x\n\t\t\tif (isOne(b)) return simplify(a); // x * 1 = x\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// x / 1 = x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tif (isDiv(e, a, b))\n\t\t{\n\t\t\tif (isOne(b)) return simplify(a); // x / 1 = x\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// 0 / x = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tif (isDiv(e, a, b))\n\t\t{\n\t\t\tif (isZero(a) && !isZero(b)) return Zero(e->valType); // 0 / x = 0\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// x / 0 --> throws\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tif (isDiv(e, a, b))\n\t\t{\n\t\t\tif (!isZero(a) && isZero(b)) throw std::runtime_error(\"Division by zero in algebraic simplification\");\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// x ^ 1 = x, x ^ 0 = 1\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tif (isExp(e, a, b))\n\t\t{\n\t\t\tif (isOne(b)) return simplify(a); // x ^ 1 = x\n\n\t\t\tdouble l, r;\n\t\t\tif (isScalar(a, l) && isScalar(b, r))\n\t\t\t{\n\t\t\t\tif ((l != 0.0) && (r == 0.0)) return Literal(1.0); // x ^ 0 = 1\n\t\t\t\tif ((l == 1.0) && (r != 0.0)) return Literal(1.0); // 1 ^ x = 1\n\t\t\t} \n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// I * x = x, x * I = x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tif (isMul(e, a, b))\n\t\t{\n\t\t\tif (isIdentity(a) && isVector(b)) return simplify(b); // I * x = x\n\t\t\tif (isIdentity(a) && isMatrix(b)) return simplify(b); // I * x = x\n\t\t\tif (isIdentity(b) && isMatrix(a)) return simplify(a); // x * I = x\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// a op a\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression* a, * b;\n\t\tif (isAdd(e, a, b) && isEqual(a, b)) return simplify(Binary(BinaryOp::Multiply, Literal(2), simplify(a))); // a + a = 2 * a\n\t\tif (isSub(e, a, b) && isEqual(a, b)) return Zero(e->valType); // a - a = 0\n\t\tif (isMul(e, a, b) && isEqual(a, b) && isScalar(a)) return simplify(Binary(BinaryOp::Exponent, simplify(a), Literal(2))); // a * a = a ^ 2\n\t\tif (isDiv(e, a, b) && isEqual(a, b) && isScalar(a)) return Literal(1.0); // a / a = 1\n\t\treturn nullptr;\n\t});\n\n\t// --- rules for built-in functions ---\n\t// dot(0,x) = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tconst Expression* a, * b;\n\t\tif (isCall2(e, \"dot\", a, b) && (isZero(a) || isZero(b))) return Literal(0.0);\n\t\treturn nullptr;\n\t});\n\n\t// length(0) = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tconst Expression* arg;\n\t\tif (isCall1(e, \"length\", arg) && isZero(arg)) return Literal(0.0);\n\t\treturn nullptr;\n\t});\n\n\t// outer(0,x) = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tconst Expression* a, * b;\n\t\tif (isCall2(e, \"outer\", a, b) && (isZero(a) || isZero(b))) return Zero(e->valType);\n\t\treturn nullptr;\n\t});\n\n\t// cross(0,x) = 0\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tconst Expression* a, * b;\n\t\tif (isCall2(e, \"cross\", a, b) && (isZero(a) || isZero(b))) return Zero(e->valType);\n\t\treturn nullptr;\n\t});\n\n\t// transpose(transpose(x)) = x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tconst Expression* arg;\n\t\tif (isCall1(e, \"transpose\", arg) && isCall1(arg, \"transpose\", arg))\n\t\t{\n\t\t\treturn simplify(arg);\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// transpose(symmetric) = symmetric\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tconst Expression* arg;\n\t\tif (isCall1(e, \"transpose\", arg) && isSymmetric(arg))\n\t\t{\n\t\t\treturn simplify(arg);\n\t\t}\n\t\treturn nullptr;\n\t});\n\n\t// --- rules for moving towards canonical forms ---\n\t// scalar * (scalar * x) = (scalar * scalar) * x\n\trules.push_back([this](const Expression* e) -> ExprPtr {\n\t\tExpression *a, *m, *b, *c;\n\t\tdouble l, r;\n\t\tif (isMul(e, a, m) && isScalar(a, l) && isMul(m, b, c) && isScalar(b, r))\n\t\t{\n\t\t\treturn simplify(Mul(Literal(l * r).get(), c));\n\t\t}\n\t\tif (isMul(e, a, m) && isScalar(a, l) && isMul(m, b, c) && isScalar(c, r))\n\t\t{\n\t\t\treturn simplify(Mul(Literal(l * r).get(), b));\n\t\t}\n\t\treturn nullptr;\n\t});\n}\n\nExprPtr Simplifier::applyRules(const Expression* expr)\n{\n\tfor (const auto& rule : rules)\n\t{\n\t\tif (auto result = rule(expr))\n\t\t\treturn result;\n\t}\n\treturn nullptr;\n}\n\nExprPtr Simplifier::simplify(const Expression* expr)\n{\n\tif (auto ctor   = dynamic_cast<const ConstructorExpr*>(expr)) return simplifyConstructor(ctor  );\n\tif (auto unary  = dynamic_cast<const UnaryExpr*      >(expr)) return simplifyUnary      (unary );\n\tif (auto init   = dynamic_cast<const InitExpr*       >(expr)) return simplifyInitializer(init  );\n\tif (auto call   = dynamic_cast<const CallExpr*       >(expr)) return simplifyCall       (call  );\n\tif (auto binary = dynamic_cast<const BinaryExpr*     >(expr)) return simplifyBinary     (binary);\n\tif (auto assign = dynamic_cast<const AssignExpr*     >(expr)) return simplifyAssign     (assign);\n\treturn clone(expr);\n}\n\nExprPtr Simplifier::simplifyConstructor(const ConstructorExpr* ctor)\n{\n\t// simplify all arguments first\n\tstd::vector<ExprPtr> simplifiedArgs;\n\tfor (const auto& arg : ctor->args)\n\t{\n\t\tsimplifiedArgs.push_back(simplify(arg.get()));\n\t}\n\n\t// if all args are numbers, we'll make this a literal too\n\tif (std::all_of(simplifiedArgs.begin(), simplifiedArgs.end(), [](const ExprPtr& arg) {\n\t\tauto lit = dynamic_cast<const LiteralExpr*>(arg.get());\n\t\treturn lit && (isDouble(lit->value) || isInt(lit->value));\n\t\t}))\n\t{\n\t\tstd::vector<double> v;\n\t\tfor (const auto& arg : simplifiedArgs)\n\t\t{\n\t\t\tauto lit = dynamic_cast<const LiteralExpr*>(arg.get());\n\t\t\tif (isInt(lit->value))\n\t\t\t\tv.push_back(lit->value.i);\n\t\t\telse\n\t\t\t\tv.push_back(lit->value.d);\n\t\t}\n\n\t\tTypeKind type = ctor->valType->kind;\n\t\tif ((type == TypeKind::Vec2) && (v.size() == 1)) return Literal(vec2(v[0], v[0]));\n\t\tif ((type == TypeKind::Vec2) && (v.size() == 2)) return Literal(vec2(v[0], v[1]));\n\t\tif ((type == TypeKind::Vec3) && (v.size() == 1)) return Literal(vec3(v[0], v[0], v[0]));\n\t\tif ((type == TypeKind::Vec3) && (v.size() == 3)) return Literal(vec3(v[0], v[1], v[2]));\n\t\tif ((type == TypeKind::Mat2) && (v.size() == 1)) return Literal(mat2(v[0], 0.0, 0.0, v[0]));\n\t\tif ((type == TypeKind::Mat2) && (v.size() == 4)) return Literal(mat2(v[0], v[1], v[2], v[3]));\n\t\tif ((type == TypeKind::Mat3) && (v.size() == 1)) return Literal(mat3(v[0], 0.0, 0.0, 0.0, v[0], 0.0, 0.0, 0.0, v[0]));\n\t\tif ((type == TypeKind::Mat3) && (v.size() == 9)) return Literal(mat3(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]));\n\t}\n\n\treturn std::make_unique<ConstructorExpr>(ctor->valType, std::move(simplifiedArgs));\n}\n\nExprPtr Simplifier::simplifyUnary(const UnaryExpr* unary)\n{\n\tauto simplifiedRight = simplify(unary->right.get());\n\tExprPtr tmp = Unary(unary->op, simplifiedRight);\n\tif (auto result = applyRules(tmp.get()))\n\t{\n\t\treturn result;\n\t}\n\treturn std::move(tmp);\n}\n\nExprPtr Simplifier::simplifyInitializer(const InitExpr* init)\n{\n\tstd::vector<ExprPtr> simplifiedElements;\n\tfor (const auto& elem : init->elements)\n\t{\n\t\tsimplifiedElements.push_back(simplify(elem.get()));\n\t}\n\treturn std::make_unique<InitExpr>(std::move(simplifiedElements));\n}\n\nExprPtr Simplifier::simplifyCall(const CallExpr* call)\n{\n\tstd::vector<ExprPtr> simplifiedArgs;\n\tfor (const auto& arg : call->arguments)\n\t{\n\t\tsimplifiedArgs.push_back(simplify(arg.get()));\n\t}\n\n\tExprPtr tmp = Call(call->name, simplifiedArgs);\n\tif (auto result = applyRules(tmp.get()))\n\t{\n\t\treturn result;\n\t}\n\n\treturn std::move(tmp);\n}\n\nExprPtr Simplifier::simplifyBinary(const BinaryExpr* binary)\n{\n\tauto l = simplify(binary->left.get());\n\tauto r = simplify(binary->right.get());\n\n\tExprPtr tmp = Binary(binary->op, l, r);\n\tif (auto result = applyRules(tmp.get()))\n\t{\n\t\treturn result;\n\t}\n\n\treturn std::move(tmp);\n}\n\nExprPtr Simplifier::simplifyAssign(const AssignExpr* assign)\n{\n\tauto l = simplify(assign->target.get());\n\tauto r = simplify(assign->value.get());\n\n\tExprPtr tmp = Assign(l, r);\n\tif (auto result = applyRules(tmp.get()))\n\t{\n\t\treturn result;\n\t}\n\n\treturn std::move(tmp);\n}"
  },
  {
    "path": "febcode/simplifier.h",
    "content": "#pragma once\n#include \"ast.h\"\n#include \"modifier.h\"\n#include <functional>\n#include <vector>\n\nnamespace febcode {\n\n\tclass Simplifier : public Modifier {\n\tpublic:\n\t\tSimplifier(Program& prg);\n\n\t\tExprPtr simplify(const Expression* expr);\n\t\tinline ExprPtr simplify(const std::unique_ptr<Expression>& expr) { return simplify(expr.get()); }\n\n\tprivate:\n\t\tExprPtr simplifyConstructor(const ConstructorExpr* expr);\n\t\tExprPtr simplifyUnary      (const UnaryExpr* expr);\n\t\tExprPtr simplifyInitializer(const InitExpr* expr);\n\t\tExprPtr simplifyCall       (const CallExpr* expr);\n\t\tExprPtr simplifyBinary     (const BinaryExpr* expr);\n\t\tExprPtr simplifyAssign     (const AssignExpr* expr);\n\n\t\tExprPtr applyRules(const Expression* expr);\n\n\t\tusing RuleFn = std::function<ExprPtr (const Expression* e)>;\n\t\tstd::vector<RuleFn> rules;\n\t};\n}\n"
  },
  {
    "path": "febcode/types.cpp",
    "content": "#include \"types.h\"\nusing namespace febcode;\n\n\nArrayValuePtr febcode::copyArray(const ArrayValue& src)\n{\n\tauto arr = std::make_shared<ArrayValue>();\n\tarr->type = src.type;\n\tfor (int i = 0; i < src.size(); ++i)\n\t{\n\t\tif (isStruct(src.elements[i]))\n\t\t{\n\t\t\tarr->elements.emplace_back(copyStruct(getStruct(src.elements[i])));\n\t\t}\n\t\telse if (isArray(src.elements[i]))\n\t\t{\n\t\t\tarr->elements.emplace_back(copyArray(getArray(src.elements[i])));\n\t\t}\n\t\telse\n\t\t\tarr->elements.emplace_back(src.elements[i]);\n\t}\n\treturn arr;\n}\n\nStructValuePtr febcode::copyStruct(const StructValue& src)\n{\n\tauto obj = std::make_shared<StructValue>();\n\tobj->type = src.type;\n\tfor (size_t i = 0; i < src.fields.size(); ++i)\n\t{\n\t\tif (isStruct(src.fields[i]))\n\t\t{\n\t\t\tobj->fields.emplace_back(copyStruct(getStruct(src.fields[i])));\n\t\t}\n\t\telse if (isArray(src.fields[i]))\n\t\t{\n\t\t\tobj->fields.emplace_back(copyArray(getArray(src.fields[i])));\n\t\t}\n\t\telse\n\t\t\tobj->fields.emplace_back(src.fields[i]);\n\t}\n\n\treturn obj;\n}\n\nTypeRegistry::TypeRegistry()\n{\n\tm_void   = { TypeKind::Void  };\n\tm_bool   = { TypeKind::Bool  };\n\tm_int    = { TypeKind::Int   };\n\tm_double = { TypeKind::Double};\n\tm_vec2   = { TypeKind::Vec2  };\n\tm_vec3   = { TypeKind::Vec3  };\n\tm_mat2   = { TypeKind::Mat2  };\n\tm_mat3   = { TypeKind::Mat3  };\n}\n\nvoid TypeRegistry::clear()\n{\n//\tm_arrayTypes.clear();\n}\n\nType TypeRegistry::Void() const { return &m_void; }\nType TypeRegistry::Bool() const { return &m_bool; }\nType TypeRegistry::Int() const { return &m_int; }\nType TypeRegistry::Double() const { return &m_double; }\nType TypeRegistry::Vec2() const { return &m_vec2; }\nType TypeRegistry::Vec3() const { return &m_vec3; }\nType TypeRegistry::Mat2() const { return &m_mat2; }\nType TypeRegistry::Mat3() const { return &m_mat3; }\n\nType TypeRegistry::getTypeFromKind(TypeKind kind) const\n{\n\tswitch (kind)\n\t{\n\tcase TypeKind::Void  : return Void();\n\tcase TypeKind::Bool  : return Bool();\n\tcase TypeKind::Int   : return Int();\n\tcase TypeKind::Double: return Double();\n\tcase TypeKind::Vec2  : return Vec2();\n\tcase TypeKind::Vec3  : return Vec3();\n\tcase TypeKind::Mat2  : return Mat2();\n\tcase TypeKind::Mat3  : return Mat3();\n\tdefault:\n\t\treturn nullptr;\n\t}\n}\n\nType TypeRegistry::getArrayType(TypeKind type, size_t size)\n{\n\tType elementType = getTypeFromKind(type);\n\tif (!elementType)\n\t\tthrow std::runtime_error(\"Invalid array element type.\");\n\treturn getArrayType(elementType, size);\n}\n\nType TypeRegistry::getArrayType(Type element, size_t size)\n{\n\tif (size == 0)\n\t\tthrow std::runtime_error(\"Array size must be greater than zero.\");\n\n\t// Check if already exists\n\tfor (auto& t : m_arrayTypes)\n\t{\n\t\tif ((t->elementType == element) && (t->arraySize == size))\n\t\t\treturn t.get();\n\t}\n\n\t// Create new one\n\tauto newType = std::make_unique<TypeStruct>();\n\tnewType->kind = TypeKind::Array;\n\tnewType->typeIndex = (int)m_arrayTypes.size();\n\tnewType->elementType = element;\n\tnewType->arraySize = size;\n\n\tm_arrayTypes.push_back(std::move(newType));\n\treturn m_arrayTypes.back().get();\n}\n\nType TypeRegistry::getArrayType(Type element, const std::vector<size_t>& sizes) const\n{\n\tType type = element;\n\tfor (auto it = sizes.rbegin(); it != sizes.rend(); ++it)\n\t{\n\t\ttype = getArrayType(type, *it);\n\t}\n\treturn type;\n}\n\nType TypeRegistry::getArrayType(int index) const\n{\n\tif (index < 0 || index >= (int)m_arrayTypes.size())\n\t\tthrow std::runtime_error(\"Invalid array type index: \" + std::to_string(index));\n\treturn m_arrayTypes[index].get();\n}\n\nType TypeRegistry::getArrayType(Type element, size_t size) const\n{\n\tif (size == 0)\n\t\tthrow std::runtime_error(\"Array size must be greater than zero.\");\n\n\tfor (auto& t : m_arrayTypes)\n\t{\n\t\tif ((t->elementType == element) && (t->arraySize == size))\n\t\t\treturn t.get();\n\t}\n\n\treturn nullptr;\n}\n\nType TypeRegistry::getBuiltinType(const Value& v) const\n{\n\tif (isVoid  (v)) return Void();\n\tif (isBool  (v)) return Bool();\n\tif (isInt   (v)) return Int();\n\tif (isDouble(v)) return Double();\n\tif (isVec2  (v)) return Vec2();\n\tif (isVec3  (v)) return Vec3();\n\tif (isMat2  (v)) return Mat2();\n\tif (isMat3  (v)) return Mat3();\n\tif (isArray (v)) { const ArrayValue&  a = getArray (v); return a.type; }\n\tif (isStruct(v)) { const StructValue& s = getStruct(v); return s.type; }\n\n\tthrow std::runtime_error(\"Unknown value type\");\n}\n\nType TypeRegistry::getType(const std::string& name) const\n{\n\tif (name == \"void\"  ) return Void();\n\tif (name == \"bool\"  ) return Bool();\n\tif (name == \"int\"   ) return Int();\n\tif (name == \"double\") return Double();\n\tif (name == \"vec2\"  ) return Vec2();\n\tif (name == \"vec3\"  ) return Vec3();\n\tif (name == \"mat2\"  ) return Mat2();\n\tif (name == \"mat3\"  ) return Mat3();\n\n\tType s = getStructType(name);\n\tif (s) return s;\n\n\treturn nullptr;\n}\n\nType TypeRegistry::getStructType(const std::string& name) const\n{\n\tauto it = std::find_if(m_structTypes.begin(), m_structTypes.end(),\n\t\t[&name](const std::unique_ptr<TypeStruct>& t) { return t->name == name; });\n\tif (it == m_structTypes.end()) return nullptr;\n\treturn it->get();\n}\n\nint TypeRegistry::getStructTypeIndex(const std::string& name) const\n{\n\tauto it = std::find_if(m_structTypes.begin(), m_structTypes.end(),\n\t\t[&name](const std::unique_ptr<TypeStruct>& t) { return t->name == name; });\n\tif (it == m_structTypes.end()) return -1;\n\treturn (*it)->typeIndex;\n}\n\nType TypeRegistry::getStructType(int index) const\n{\n\tif (index < 0 || index >= (int)m_structTypes.size())\n\t\tthrow std::runtime_error(\"Invalid struct type index: \" + std::to_string(index));\n\treturn m_structTypes[index].get();\n}\n\nType TypeRegistry::defineStructType(\n\tconst std::string& name,\n\tconst std::vector<std::pair<Type, std::string>>& fields)\n{\n\tType s = getStructType(name);\n\tif (s)\n\t\tthrow std::runtime_error(\"Struct already defined: \" + name);\n\n\tauto t = std::make_unique<TypeStruct>();\n\tt->kind = TypeKind::Struct;\n\tt->typeIndex = (int)m_structTypes.size();\n\tt->name = name;\n\tt->fields = fields;\n\n\tType result = t.get();\n\tm_structTypes.push_back(std::move(t));\n\n\treturn result;\n}\n\nType TypeRegistry::defineStructType(const std::string& name, const std::vector<std::pair<TypeKind, std::string>>& fields)\n{\n\tstd::vector<std::pair<Type, std::string>> resolvedFields;\n\tfor (const auto& f : fields)\n\t{\n\t\tType t = nullptr;\n\t\tswitch (f.first)\n\t\t{\n\t\tcase TypeKind::Bool  : t = Bool(); break;\n\t\tcase TypeKind::Int   : t = Int(); break;\n\t\tcase TypeKind::Double: t = Double(); break;\n\t\tcase TypeKind::Vec2  : t = Vec2(); break;\n\t\tcase TypeKind::Vec3  : t = Vec3(); break;\n\t\tcase TypeKind::Mat2  : t = Mat2(); break;\n\t\tcase TypeKind::Mat3  : t = Mat3(); break;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported field type in RegisterStruct: \" + std::to_string((int)f.first));\n\t\t}\n\t\tresolvedFields.push_back({ t, f.second });\n\t}\n\treturn defineStructType(name, resolvedFields);\n}\n\nstd::string febcode::TypeToString(Type type)\n{\n\tif (type == nullptr) return \"<null>\";\n\n\tswitch (type->kind)\n\t{\n\tcase TypeKind::Void  : return \"void\";\n\tcase TypeKind::Bool  : return \"bool\";\n\tcase TypeKind::Int   : return \"int\";\n\tcase TypeKind::Double: return \"double\";\n\tcase TypeKind::Vec2  : return \"vec2\";\n\tcase TypeKind::Vec3  : return \"vec3\";\n\tcase TypeKind::Mat2  : return \"mat2\";\n\tcase TypeKind::Mat3  : return \"mat3\";\n\tcase TypeKind::Struct: return type->name;\n\tcase TypeKind::Array:\n\t{\n\t\tstd::string elemStr = TypeToString(type->elementType);\n\t\treturn \"(\" + elemStr + \")[\" + std::to_string(type->arraySize) + \"]\";\n\t}\n\tdefault:\n\t\treturn \"<unknown type>\";\n\t}\n}\n\nType febcode::coerce(Type from, Type to)\n{\n\tif (from == to)\n\t\treturn from;\n\t// allow coercion from numeric types to double, but not the other way around\n\tif (isScalarType(from) && to->kind == TypeKind::Double)\n\t\treturn to;\n\tif (isLogicalType(from) && to->kind == TypeKind::Bool)\n\t\treturn to;\n\t\n\treturn nullptr;\n}\n"
  },
  {
    "path": "febcode/types.h",
    "content": "#pragma once\n#include <memory>\n#include <string>\n#include <vector>\n#include <stdexcept>\n#include <functional>\n#include <cstring>\n#include <cmath>\n#include <assert.h>\n\nnamespace febcode\n{\n\tenum class TypeKind : uint8_t {\n\t\tVoid,\n\t\tBool,\n\t\tInt,\n\t\tDouble,\n\t\tVec2,\n\t\tVec3,\n\t\tMat2,\n\t\tMat3,\n\t\tArray,\n\t\tStruct,\n\t};\n\n\tstruct Value;\n\n\tstruct vec2\n\t{\n\t\tdouble x, y;\n\n\t\tvec2() : x(0), y(0) {}\n\t\tvec2(double x, double y) : x(x), y(y) {}\n\n\t\tvec2 operator+(const vec2& other) const { return vec2(x + other.x, y + other.y); }\n\t\tvec2 operator-(const vec2& other) const { return vec2(x - other.x, y - other.y); }\n\t\tvec2 operator*(double scalar) const { return vec2(x * scalar, y * scalar); }\n\t\tvec2 operator/(double scalar) const { return vec2(x / scalar, y / scalar); }\n\n\t\t// dot product\n\t\tdouble operator*(const vec2& other) const { return x * other.x + y * other.y; }\n\n\t\tbool operator==(const vec2& other) const { return (x == other.x) && (y == other.y); }\n\t\tbool operator!=(const vec2& other) const { return !(*this == other); }\n\t};\n\n\tstruct vec3\n\t{\n\t\tdouble x, y, z;\n\n\t\tvec3() : x(0), y(0), z(0) {}\n\t\tvec3(double x, double y, double z) : x(x), y(y), z(z) {}\n\n\t\tvec3 operator+(const vec3& other) const { return vec3(x + other.x, y + other.y, z + other.z); }\n\t\tvec3 operator-(const vec3& other) const { return vec3(x - other.x, y - other.y, z - other.z); }\n\t\tvec3 operator*(double scalar) const { return vec3(x * scalar, y * scalar, z * scalar); }\n\t\tvec3 operator/(double scalar) const { return vec3(x / scalar, y / scalar, z / scalar); }\n\n\t\t// dot product\n\t\tdouble operator*(const vec3& other) const { return x * other.x + y * other.y + z * other.z; }\n\n\t\tbool operator==(const vec3& other) const { return (x == other.x) && (y == other.y) && (z == other.z); }\n\t\tbool operator!=(const vec3& other) const { return !(*this == other); }\n\n\t\tvec3 cross(const vec3& other) const {\n\t\t\treturn vec3(\n\t\t\t\ty * other.z - z * other.y,\n\t\t\t\tz * other.x - x * other.z,\n\t\t\t\tx * other.y - y * other.x\n\t\t\t);\n\t\t}\n\t};\n\n\tstruct mat2 {\n\t\tdouble m[2][2];\n\t\tmat2() {\n\t\t\tm[0][0] = m[0][1] = m[1][0] = m[1][1] = 0.0;\n\t\t}\n\t\tmat2(double d)\n\t\t{\n\t\t\tm[0][0] = m[1][1] = d;\n\t\t\tm[0][1] = m[1][0] = 0.0;\n\t\t}\n\t\tmat2(double a00, double a01, double a10, double a11) {\n\t\t\tm[0][0] = a00; m[0][1] = a01;\n\t\t\tm[1][0] = a10; m[1][1] = a11;\n\t\t}\n\t\tmat2 operator + (const mat2& other) const {\n\t\t\treturn mat2(\n\t\t\t\tm[0][0] + other.m[0][0], m[0][1] + other.m[0][1],\n\t\t\t\tm[1][0] + other.m[1][0], m[1][1] + other.m[1][1]\n\t\t\t);\n\t\t}\n\t\tmat2 operator-(const mat2& other) const {\n\t\t\treturn mat2(\n\t\t\t\tm[0][0] - other.m[0][0], m[0][1] - other.m[0][1],\n\t\t\t\tm[1][0] - other.m[1][0], m[1][1] - other.m[1][1]\n\t\t\t);\n\t\t}\n\t\tmat2 operator*(double scalar) const {\n\t\t\treturn mat2(\n\t\t\t\tm[0][0] * scalar, m[0][1] * scalar,\n\t\t\t\tm[1][0] * scalar, m[1][1] * scalar\n\t\t\t);\n\t\t}\n\t\tmat2 operator/(double scalar) const {\n\t\t\treturn mat2(\n\t\t\t\tm[0][0] / scalar, m[0][1] / scalar,\n\t\t\t\tm[1][0] / scalar, m[1][1] / scalar\n\t\t\t);\n\t\t}\n\t\tvec2 operator*(const vec2& other) const {\n\t\t\treturn vec2(\n\t\t\t\tm[0][0] * other.x + m[0][1] * other.y,\n\t\t\t\tm[1][0] * other.x + m[1][1] * other.y\n\t\t\t);\n\t\t}\n\t\tmat2 operator*(const mat2& other) const {\n\t\t\treturn mat2(\n\t\t\t\tm[0][0] * other.m[0][0] + m[0][1] * other.m[1][0],\n\t\t\t\tm[0][0] * other.m[0][1] + m[0][1] * other.m[1][1],\n\t\t\t\tm[1][0] * other.m[0][0] + m[1][1] * other.m[1][0],\n\t\t\t\tm[1][0] * other.m[0][1] + m[1][1] * other.m[1][1]\n\t\t\t);\n\t\t}\n\t\tbool operator==(const mat2& other) const {\n\t\t\treturn (m[0][0] == other.m[0][0]) && (m[0][1] == other.m[0][1]) &&\n\t\t\t\t   (m[1][0] == other.m[1][0]) && (m[1][1] == other.m[1][1]);\n\t\t}\n\t\tbool operator!=(const mat2& other) const {\n\t\t\treturn !(*this == other);\n\t\t}\n\t};\n\n\tstruct mat3 {\n\t\tdouble m[3][3];\n\t\tmat3() {\n\t\t\tm[0][0] = m[0][1] = m[0][2] = 0.0;\n\t\t\tm[1][0] = m[1][1] = m[1][2] = 0.0;\n\t\t\tm[2][0] = m[2][1] = m[2][2] = 0.0;\n\t\t}\n\t\tmat3(double d) {\n\t\t\tm[0][0] = m[1][1] = m[2][2] = d;\n\n\t\t\tm[0][1] = m[1][0] = 0.0;\n\t\t\tm[0][2] = m[2][0] = 0.0;\n\t\t\tm[1][2] = m[2][1] = 0.0;\n\t\t}\n\t\tmat3(double a00, double a01, double a02,\n\t\t\t double a10, double a11, double a12,\n\t\t\t double a20, double a21, double a22) {\n\t\t\tm[0][0] = a00; m[0][1] = a01; m[0][2] = a02;\n\t\t\tm[1][0] = a10; m[1][1] = a11; m[1][2] = a12;\n\t\t\tm[2][0] = a20; m[2][1] = a21; m[2][2] = a22;\n\t\t}\n\n\t\tmat3 operator + (const mat3& other) const {\n\t\t\treturn mat3(\n\t\t\t\tm[0][0] + other.m[0][0], m[0][1] + other.m[0][1], m[0][2] + other.m[0][2],\n\t\t\t\tm[1][0] + other.m[1][0], m[1][1] + other.m[1][1], m[1][2] + other.m[1][2],\n\t\t\t\tm[2][0] + other.m[2][0], m[2][1] + other.m[2][1], m[2][2] + other.m[2][2]\n\t\t\t);\n\t\t}\n\n\t\tmat3 operator - (const mat3& other) const {\n\t\t\treturn mat3(\n\t\t\t\tm[0][0] - other.m[0][0], m[0][1] - other.m[0][1], m[0][2] - other.m[0][2],\n\t\t\t\tm[1][0] - other.m[1][0], m[1][1] - other.m[1][1], m[1][2] - other.m[1][2],\n\t\t\t\tm[2][0] - other.m[2][0], m[2][1] - other.m[2][1], m[2][2] - other.m[2][2]\n\t\t\t);\n\t\t}\n\n\t\tmat3 operator*(double scalar) const {\n\t\t\treturn mat3(\n\t\t\t\tm[0][0] * scalar, m[0][1] * scalar, m[0][2] * scalar,\n\t\t\t\tm[1][0] * scalar, m[1][1] * scalar, m[1][2] * scalar,\n\t\t\t\tm[2][0] * scalar, m[2][1] * scalar, m[2][2] * scalar\n\t\t\t);\n\t\t}\n\n\t\tmat3 operator/(double scalar) const {\n\t\t\treturn mat3(\n\t\t\t\tm[0][0] / scalar, m[0][1] / scalar, m[0][2] / scalar,\n\t\t\t\tm[1][0] / scalar, m[1][1] / scalar, m[1][2] / scalar,\n\t\t\t\tm[2][0] / scalar, m[2][1] / scalar, m[2][2] / scalar\n\t\t\t);\n\t\t}\n\n\t\tvec3 operator*(const vec3& other) const {\n\t\t\treturn vec3(\n\t\t\t\tm[0][0] * other.x + m[0][1] * other.y + m[0][2] * other.z,\n\t\t\t\tm[1][0] * other.x + m[1][1] * other.y + m[1][2] * other.z,\n\t\t\t\tm[2][0] * other.x + m[2][1] * other.y + m[2][2] * other.z\n\t\t\t);\n\t\t}\n\n\t\tmat3 operator*(const mat3& other) const {\n\t\t\treturn mat3(\n\t\t\t\tm[0][0] * other.m[0][0] + m[0][1] * other.m[1][0] + m[0][2] * other.m[2][0],\n\t\t\t\tm[0][0] * other.m[0][1] + m[0][1] * other.m[1][1] + m[0][2] * other.m[2][1],\n\t\t\t\tm[0][0] * other.m[0][2] + m[0][1] * other.m[1][2] + m[0][2] * other.m[2][2],\n\n\t\t\t\tm[1][0] * other.m[0][0] + m[1][1] * other.m[1][0] + m[1][2] * other.m[2][0],\n\t\t\t\tm[1][0] * other.m[0][1] + m[1][1] * other.m[1][1] + m[1][2] * other.m[2][1],\n\t\t\t\tm[1][0] * other.m[0][2] + m[1][1] * other.m[1][2] + m[1][2] * other.m[2][2],\n\n\t\t\t\tm[2][0] * other.m[0][0] + m[2][1] * other.m[1][0] + m[2][2] * other.m[2][0],\n\t\t\t\tm[2][0] * other.m[0][1] + m[2][1] * other.m[1][1] + m[2][2] * other.m[2][1],\n\t\t\t\tm[2][0] * other.m[0][2] + m[2][1] * other.m[1][2] + m[2][2] * other.m[2][2]\n\t\t\t);\n\t\t}\n\n\t\tbool operator==(const mat3& other) const {\n\t\t\treturn (m[0][0] == other.m[0][0]) && (m[0][1] == other.m[0][1]) && (m[0][2] == other.m[0][2]) &&\n\t\t\t\t   (m[1][0] == other.m[1][0]) && (m[1][1] == other.m[1][1]) && (m[1][2] == other.m[1][2]) &&\n\t\t\t\t   (m[2][0] == other.m[2][0]) && (m[2][1] == other.m[2][1]) && (m[2][2] == other.m[2][2]);\n\t\t}\n\t\tbool operator!=(const mat3& other) const {\n\t\t\treturn !(*this == other);\n\t\t}\n\t};\n\n\tstruct TypeStruct;\n\tusing Type = const TypeStruct*;\n\n\tusing StructField = std::pair<Type, std::string>; // type and name of a struct field\n\n\tstruct TypeStruct {\n\t\tTypeKind kind = TypeKind::Void;\n\n\t\t// index in type registry (for arrays and structs)\n\t\tint typeIndex = -1;\n\n\t\t// for arrays\n\t\tType elementType = nullptr;\n\t\tsize_t arraySize = 0;\n\n\t\t// For struct types\n\t\tstd::string name;\n\t\tstd::vector<StructField> fields;\n\n\t\tsize_t size() const {\n\t\t\tswitch (kind)\n\t\t\t{\n\t\t\tcase TypeKind::Void:   return 1;\n\t\t\tcase TypeKind::Bool:   return 1;\n\t\t\tcase TypeKind::Int:    return 1;\n\t\t\tcase TypeKind::Double: return 1;\n\t\t\tcase TypeKind::Vec2:   return 2;\n\t\t\tcase TypeKind::Vec3:   return 3;\n\t\t\tcase TypeKind::Mat2:   return 4;\n\t\t\tcase TypeKind::Mat3:   return 9;\n\t\t\tcase TypeKind::Array:  return elementType->size()* arraySize;\n\t\t\tcase TypeKind::Struct:\n\t\t\t\t{\n\t\t\t\t\tsize_t totalSize = 0;\n\t\t\t\t\tfor (const auto& field : fields)\n\t\t\t\t\t\ttotalSize += field.first->size();\n\t\t\t\t\treturn totalSize;\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow std::runtime_error(\"Unknown type kind.\");\n\t\t\t}\n\t\t}\n\t};\n\n\tstruct ArrayValue;\n\tstruct StructValue;\n\n\tusing ArrayValuePtr = std::shared_ptr<ArrayValue>;\n\tusing StructValuePtr = std::shared_ptr<StructValue>;\n\n\t// make sure this matches the order of the Value variant below (and of TypeKind above)\n\tenum ValueIndex {\n\t\tVOID = 0,\n\t\tBOOL,\n\t\tINT,\n\t\tDOUBLE,\n\t\tVEC2,\n\t\tVEC3,\n\t\tMAT2,\n\t\tMAT3,\n\t\tARRAY,\n\t\tSTRUCT,\n\t\tREF\n\t};\n\n\tstruct Void {};\n\n\tstruct Value\n\t{\n\t\tValueIndex index = ValueIndex::VOID;\n\n\t\tValue() {}\n\t\tValue(bool   a) : index(ValueIndex::BOOL), b(a) {}\n\t\tValue(int    a) : index(ValueIndex::INT), i(a) {}\n\t\tValue(double a) : index(ValueIndex::DOUBLE), d(a) {}\n\t\tValue(const vec2& a) : index(ValueIndex::VEC2), vec2Value(a) {}\n\t\tValue(const vec3& a) : index(ValueIndex::VEC3), vec3Value(a) {}\n\t\tValue(const mat2& a) : index(ValueIndex::MAT2), mat2Value(a) {}\n\t\tValue(const mat3& a) : index(ValueIndex::MAT3), mat3Value(a) {}\n\t\tValue(const ArrayValuePtr& a) : index(ValueIndex::ARRAY), arrayValue(a) {}\n\t\tValue(const StructValuePtr& a) : index(ValueIndex::STRUCT), structValue(a) {}\n\n\t\tValue(const Value& v)\n\t\t{\n\t\t\tcopyFrom(v);\n\t\t}\n\n\t\tvoid operator = (const Value& other)\n\t\t{\n\t\t\t// for simple types we use the fast path\n\t\t\tif ((index < ValueIndex::ARRAY) && (other.index < ValueIndex::ARRAY))\n\t\t\t{\n\t\t\t\tindex = other.index;\n\t\t\t\tswitch (index)\n\t\t\t\t{\n\t\t\t\tcase ValueIndex::VOID  : break;\t// no data to copy\n\t\t\t\tcase ValueIndex::BOOL  : b = other.b; break;\n\t\t\t\tcase ValueIndex::INT   : i = other.i; break;\n\t\t\t\tcase ValueIndex::DOUBLE: d = other.d; break;\n\t\t\t\tcase ValueIndex::VEC2  : vec2Value = other.vec2Value; break;\n\t\t\t\tcase ValueIndex::VEC3  : vec3Value = other.vec3Value; break;\n\t\t\t\tcase ValueIndex::MAT2  : mat2Value = other.mat2Value; break;\n\t\t\t\tcase ValueIndex::MAT3  : mat3Value = other.mat3Value; break;\n\t\t\t\tdefault: \n\t\t\t\t\tassert(false);\n\t\t\t\t\tbreak; // should not happen\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (this != &other)\n\t\t\t{\n\t\t\t\tdestroy();\n\t\t\t\tcopyFrom(other);\n\t\t\t}\n\t\t}\n\n\t\t~Value()\n\t\t{\n\t\t\tif (index >= ValueIndex::ARRAY)\n\t\t\t\tdestroy();\n\t\t}\n\n\t\tbool operator == (const Value& other) const\n\t\t{\n\t\t\tif (index != other.index)\n\t\t\t\treturn false;\n\t\t\tswitch (index)\n\t\t\t{\n\t\t\tcase ValueIndex::VOID  : return true; // all void values are equal\n\t\t\tcase ValueIndex::BOOL  : return b == other.b;\n\t\t\tcase ValueIndex::INT   : return i == other.i;\n\t\t\tcase ValueIndex::DOUBLE: return d == other.d;\n\t\t\tcase ValueIndex::VEC2  : return vec2Value == other.vec2Value;\n\t\t\tcase ValueIndex::VEC3  : return vec3Value == other.vec3Value;\n\t\t\tcase ValueIndex::MAT2  : return mat2Value == other.mat2Value;\n\t\t\tcase ValueIndex::MAT3  : return mat3Value == other.mat3Value;\n\t\t\tcase ValueIndex::ARRAY : return arrayValue == other.arrayValue;\n\t\t\tcase ValueIndex::STRUCT: return structValue == other.structValue;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tbool operator != (const Value& other) const\n\t\t{\n\t\t\treturn !(*this == other);\n\t\t}\n\n\t\tunion {\n\t\t\tVoid v;\n\t\t\tbool b;\n\t\t\tint i;\n\t\t\tdouble d;\n\t\t\tvec2 vec2Value;\n\t\t\tvec3 vec3Value;\n\t\t\tmat2 mat2Value;\n\t\t\tmat3 mat3Value;\n\t\t\tArrayValuePtr arrayValue;\n\t\t\tStructValuePtr structValue;\n\t\t};\n\n\tprivate:\n\t\tvoid copyFrom(const Value& v) \n\t\t{\n\t\t\tindex = v.index;\n\t\t\tswitch (index)\n\t\t\t{\n\t\t\tcase ValueIndex::VOID: break;\t// no data to copy\n\t\t\tcase ValueIndex::BOOL: b = v.b; break;\n\t\t\tcase ValueIndex::INT: i = v.i; break;\n\t\t\tcase ValueIndex::DOUBLE: d = v.d; break;\n\t\t\tcase ValueIndex::VEC2: vec2Value = v.vec2Value; break;\n\t\t\tcase ValueIndex::VEC3: vec3Value = v.vec3Value; break;\n\t\t\tcase ValueIndex::MAT2: mat2Value = v.mat2Value; break;\n\t\t\tcase ValueIndex::MAT3: mat3Value = v.mat3Value; break;\n\t\t\tcase ValueIndex::ARRAY: new (&arrayValue) ArrayValuePtr(v.arrayValue); break;\n\t\t\tcase ValueIndex::STRUCT: new (&structValue) StructValuePtr(v.structValue); break;\n\t\t\t}\n\t\t}\n\n\t\tvoid destroy()\n\t\t{\n\t\t\tswitch (index)\n\t\t\t{\n\t\t\t\tcase ValueIndex::ARRAY:\n\t\t\t\t\tarrayValue.~shared_ptr();\n\t\t\t\t\tbreak;\n\t\t\t\tcase ValueIndex::STRUCT:\n\t\t\t\t\tstructValue.~shared_ptr();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak; // no special handling needed for other types\n\t\t\t}\n\t\t\tindex = ValueIndex::VOID; // reset to void after destruction\n\t\t}\n\t};\n\n\tstruct ArrayValue {\n\t\tType type;\n\t\tstd::vector<Value> elements;\n\t\tsize_t size() const { return elements.size(); }\n\t};\n\n\tstruct StructValue {\n\t\tType type;\n\t\tstd::vector<Value> fields;\n\t};\n\n\tinline ArrayValuePtr createArray(Type elementType, size_t size)\n\t{\n\t\tauto arr = std::make_shared<ArrayValue>();\n\t\tarr->type = elementType;\n\t\tarr->elements.resize(size);\n\t\treturn arr;\n\t}\n\n\tinline StructValuePtr createStruct(Type type)\n\t{\n\t\tauto obj = std::make_shared<StructValue>();\n\t\tobj->type = type;\n\t\tobj->fields.resize(type->fields.size());\n\t\treturn obj;\n\t}\n\n\tArrayValuePtr copyArray(const ArrayValue& src);\n\n\tStructValuePtr copyStruct(const StructValue& src);\n\n\tinline bool isVoid  (const Value& v) { return v.index == ValueIndex::VOID;}\n\tinline bool isBool  (const Value& v) { return v.index == ValueIndex::BOOL;}\n\tinline bool isInt   (const Value& v) { return v.index == ValueIndex::INT; }\n\tinline bool isDouble(const Value& v) { return v.index == ValueIndex::DOUBLE; }\n\tinline bool isVec2  (const Value& v) { return v.index == ValueIndex::VEC2; }\n\tinline bool isVec3  (const Value& v) { return v.index == ValueIndex::VEC3; }\n\tinline bool isMat2  (const Value& v) { return v.index == ValueIndex::MAT2; }\n\tinline bool isMat3  (const Value& v) { return v.index == ValueIndex::MAT3; }\n\tinline bool isArray (const Value& v) { return v.index == ValueIndex::ARRAY; }\n\tinline bool isStruct(const Value& v) { return v.index == ValueIndex::STRUCT; }\n\tinline bool isRef   (const Value& v) { return v.index == ValueIndex::REF; }\n\n\tinline bool isVoidType  (Type type) { return type->kind == TypeKind::Void; }\n\tinline bool isBoolType  (Type type) { return type->kind == TypeKind::Bool; }\n\tinline bool isIntType   (Type type) { return type->kind == TypeKind::Int; }\n\tinline bool isDoubleType(Type type) { return type->kind == TypeKind::Double; }\n\tinline bool isVec2Type  (Type type) { return type->kind == TypeKind::Vec2; }\n\tinline bool isVec3Type  (Type type) { return type->kind == TypeKind::Vec3; }\n\tinline bool isMat2Type  (Type type) { return type->kind == TypeKind::Mat2; }\n\tinline bool isMat3Type  (Type type) { return type->kind == TypeKind::Mat3; }\n\tinline bool isArrayType (Type type) { return type->kind == TypeKind::Array; }\n\tinline bool isStructType(Type type) { return type->kind == TypeKind::Struct; }\n\n\tinline bool isLogicalType(Type type) { return isBoolType(type) || isIntType(type) || isDoubleType(type); }\n\n\tinline bool isScalarType(Type type) { return isIntType(type) || isDoubleType(type); }\n\n\tinline bool isNumericType(Type type) { return isIntType(type) || isDoubleType(type) || isVec2Type(type) || isVec3Type(type) || isMat2Type(type) || isMat3Type(type); }\n\n\tinline bool   getBool(const Value& v) { assert(v.index == ValueIndex::BOOL); return v.b; }\n\tinline int    getInt   (const Value& v) { assert(v.index == ValueIndex::INT); return v.i; }\n\tinline double getDouble(const Value& v) { assert(v.index == ValueIndex::DOUBLE); return v.d; }\n\tinline const vec2& getVec2(const Value& v) { assert(v.index == ValueIndex::VEC2); return v.vec2Value; }\n\tinline vec2& getVec2(Value& v) { assert(v.index == ValueIndex::VEC2); return v.vec2Value; }\n\tinline const vec3& getVec3(const Value& v) { assert(v.index == ValueIndex::VEC3); return v.vec3Value; }\n\tinline vec3& getVec3(Value& v) { assert(v.index == ValueIndex::VEC3); return v.vec3Value; }\n\tinline const mat2& getMat2(const Value& v) { assert(v.index == ValueIndex::MAT2); return v.mat2Value; }\n\tinline mat2& getMat2(Value& v) { assert(v.index == ValueIndex::MAT2); return v.mat2Value; }\n\tinline const mat3& getMat3(const Value& v) { assert(v.index == ValueIndex::MAT3); return v.mat3Value; }\n\tinline mat3& getMat3(Value& v) { assert(v.index == ValueIndex::MAT3); return v.mat3Value; }\n\tinline const ArrayValue&  getArray (const Value& v) { assert(v.index == ValueIndex::ARRAY); return *v.arrayValue; }\n\tinline const StructValue& getStruct(const Value& v) { assert(v.index == ValueIndex::STRUCT); return *v.structValue; }\n\n\tinline const ArrayValuePtr& getArrayPtr(const Value& v) { assert(v.index == ValueIndex::ARRAY); return v.arrayValue; }\n\tinline const StructValuePtr& getStructPtr(const Value& v) { assert(v.index == ValueIndex::STRUCT); return v.structValue; }\n\n\tinline ArrayValue&  getArray (Value& v) { assert(v.index == ValueIndex::ARRAY); return *v.arrayValue; }\n\tinline StructValue& getStruct(Value& v) { assert(v.index == ValueIndex::STRUCT); return *v.structValue; }\n\n\tType coerce(Type from, Type to);\n\n\tinline Type commonType(Type l, Type r)\n\t{\n\t\tif (l == r) return l;\n\t\tif (isDoubleType(l) && isScalarType(r)) return l;\n\t\tif (isDoubleType(r) && isScalarType(l)) return r;\n\t\treturn nullptr;\n\t}\n\n\tinline bool isZero(const Value& v)\n\t{\n\t\tif (isInt(v) && (getInt(v) == 0)) return true;\n\t\tif (isDouble(v) && (getDouble(v) == 0.0)) return true;\n\t\tif (isVec2(v) && (getVec2(v) == vec2(0, 0))) return true;\n\t\tif (isVec3(v) && (getVec3(v) == vec3(0, 0, 0))) return true;\n\t\tif (isMat2(v) && (getMat2(v) == mat2(0.0))) return true;\n\t\tif (isMat3(v) && (getMat3(v) == mat3(0.0))) return true;\n\t\treturn false;\n\t}\n\n\tinline bool isOne(const Value& v)\n\t{\n\t\tif (isInt(v) && (getInt(v) == 1)) return true;\n\t\tif (isDouble(v) && (getDouble(v) == 1.0)) return true;\n\t\treturn false;\n\t}\n\n\tinline bool isIdentity(const mat2& m)\n\t{\n\t\treturn (m == mat2(1.0));\n\t}\n\n\tinline bool isIdentity(const mat3& m)\n\t{\n\t\treturn (m == mat3(1.0));\n\t}\n\n\tinline bool isSymmetric(const mat2& m)\n\t{\n\t\treturn (m.m[0][1] == m.m[1][0]);\n\t}\n\n\tinline bool isSymmetric(const mat3& m)\n\t{\n\t\treturn (m.m[0][1] == m.m[1][0]) && (m.m[0][2] == m.m[2][0]) && (m.m[1][2] == m.m[2][1]);\n\t}\n\n\tinline bool isIntNumber(const Value& v)\n\t{\n\t\tif (isInt(v)) return true;\n\t\tif (isDouble(v))\n\t\t{\n\t\t\tdouble d = getDouble(v);\n\t\t\treturn (d == std::floor(d));\n\t\t}\n\t\treturn false;\n\t}\n\n\tinline int toIntNumber(const Value& v)\n\t{\n\t\tassert(isIntNumber(v));\n\t\tif (isInt(v)) return getInt(v);\n\t\tif (isDouble(v)) return (int)getDouble(v);\n\t\treturn 0;\n\t}\n\n\tclass TypeRegistry {\n\tpublic:\n\t\tTypeRegistry();\n\t\tvoid clear();\n\n\t\tType Void() const;\n\t\tType Int() const;\n\t\tType Bool() const;\n\t\tType Double() const;\n\t\tType Vec2() const;\n\t\tType Vec3() const;\n\t\tType Mat2() const;\n\t\tType Mat3() const;\n\t\tType getArrayType(TypeKind type, size_t size);\n\t\tType getArrayType(Type element, size_t size);\n\t\tType getArrayType(Type element, size_t size) const;\n\t\tType getArrayType(Type element, const std::vector<size_t>& size) const;\n\t\tType getArrayType(int index) const;\n\n\t\tType getTypeFromKind(TypeKind kind) const;\n\n\t\tType getBuiltinType(const Value& v) const;\n\n\t\tType getType(const std::string& name) const;\n\n\t\tType getStructType(const std::string& name) const;\n\t\tint getStructTypeIndex(const std::string& name) const;\n\t\tType getStructType(int index) const;\n\n\t\tType defineStructType(const std::string& name, const std::vector<std::pair<Type, std::string>>& fields);\n\t\tType defineStructType(const std::string& name, const std::vector<std::pair<TypeKind, std::string>>& fields);\n\n\tprivate:\n\t\t// Primitive canonical types\n\t\tTypeStruct m_void;\n\t\tTypeStruct m_bool;\n\t\tTypeStruct m_int;\n\t\tTypeStruct m_double;\n\t\tTypeStruct m_vec2;\n\t\tTypeStruct m_vec3;\n\t\tTypeStruct m_mat2;\n\t\tTypeStruct m_mat3;\n\n\t\t// Interned array types\n\t\tstd::vector<std::unique_ptr<TypeStruct>> m_arrayTypes;\n\n\t\t// User-defined struct types\n\t\tstd::vector<std::unique_ptr<TypeStruct>> m_structTypes;\n\t};\n\n\tinline TypeKind ValueType(const Value& v)\n\t{\n\t\treturn (TypeKind)v.index;\n\t}\n\n\tstd::string TypeToString(Type type);\n\n\tstruct BinaryOpSignature\n\t{\n\t\tType leftType;\n\t\tType rightType;\n\n\t\tType resultType;\n\t};\n}\n\nnamespace febcode\n{\n\tstruct FuncArgs {\n\t\tdouble* stack = nullptr;\n\t\tsize_t count = 0; // number of arguments passed to the function\n\t\tsize_t index = 0;\n\n\t\tbool   getBool()   { return (bool  )(stack[index++]); }\n\t\tint    getInt()    { return (int   )(stack[index++]); }\n\t\tdouble getDouble() { return stack[index++]; }\n\t\tvec2 getVec2() {\n\t\t\tdouble x = stack[index++];\n\t\t\tdouble y = stack[index++];\n\t\t\treturn vec2(x, y);\n\t\t}\n\t\tvec3 getVec3() {\n\t\t\tdouble x = stack[index++];\n\t\t\tdouble y = stack[index++];\n\t\t\tdouble z = stack[index++];\n\t\t\treturn vec3(x, y, z);\n\t\t}\n\t\tmat2 getMat2() {\n\t\t\tdouble a00 = stack[index++];\n\t\t\tdouble a01 = stack[index++];\n\t\t\tdouble a10 = stack[index++];\n\t\t\tdouble a11 = stack[index++];\n\t\t\treturn mat2(a00, a01, a10, a11);\n\t\t}\n\t\tmat3 getMat3() {\n\t\t\tdouble a00 = stack[index++]; double a01 = stack[index++]; double a02 = stack[index++];\n\t\t\tdouble a10 = stack[index++]; double a11 = stack[index++]; double a12 = stack[index++];\n\t\t\tdouble a20 = stack[index++]; double a21 = stack[index++]; double a22 = stack[index++];\n\t\t\treturn mat3(a00, a01, a02,\n\t\t\t\t\t\ta10, a11, a12,\n\t\t\t\t\t\ta20, a21, a22);\n\t\t}\n\t};\n\n\tusing NativeFnc = std::function<Value(FuncArgs args)>;\n}\n"
  },
  {
    "path": "febcode/vm.cpp",
    "content": "#include \"vm.h\"\n#include <iostream>\n#include <iomanip>\n\nusing namespace febcode;\n\n// helper function for integer exponentiation (used in EXP_INT)\nstatic int ipow(int base, int exp)\n{\n\tint result = 1;\n\twhile (exp > 0)\n\t{\n\t\tif (exp & 1)\n\t\t\tresult *= base;\n\t\tbase *= base;\n\t\texp >>= 1;\n\t}\n\treturn result;\n}\n\nvoid printStack(const std::vector<double>& stack, int numGlobals, int stackSize, double* ref)\n{\n\tstd::cout << \"Stack: [\";\n\tfor (size_t i = 0; i < numGlobals; i++)\n\t{\n\t\tif (ref == &stack[i])\n\t\t\tstd::cout << \"*\";\n\n\t\tstd::cout << ValueToString(stack[i]);\n\t\tif (i < numGlobals - 1)\n\t\t\tstd::cout << \",\";\n\t}\n\tstd::cout << \"|\";\n\tfor (size_t i = numGlobals; i < stackSize; i++)\n\t{\n\t\tstd::cout << ValueToString(stack[i]);\n\t\tif (i < stack.size() - 1)\n\t\t\tstd::cout << \",\";\n\t}\n\tfor (size_t i= stackSize; i < stack.size(); i++)\n\t{\n\t\tstd::cout << \"_\";\n\t\tif (i < stack.size() - 1)\n\t\t\tstd::cout << \",\";\n\t}\n\tstd::cout << \"]\" << std::endl;\n}\n\nValue VM::execute()\n{\n\tif (frameCount == 0)\n\t\tthrow std::runtime_error(\"No function to execute\");\n\n\tconst size_t instructions = m_program->code.size();\n\tref.ptr = nullptr;\n\tconst uint8_t* lastIP = m_program->code.data() + instructions;\n\n\twhile (ip < lastIP)\n\t{\n\t\tOpCode instruction = (OpCode)readByte();\n\n#ifndef NDEBUG\n\t\tif (m_debug)\n\t\t{\n\t\t\tstd::cout << \"IP: \" << std::setw(4) << (ip - &m_program->code[0]) - 1;\n\t\t\tstd::cout << \" | Executing: \" << OpCodeToString(instruction) << \" | \";\n\t\t}\n#endif\n\n\t\tswitch (instruction)\n\t\t{\n\t\tcase OpCode::PUSH_VOID:\n\t\t{\n\t\t\tpushVoid();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_BOOL:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushBool(m_program->constants[idx].b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_INT:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushInt(m_program->constants[idx].i);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_DOUBLE:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushDouble(m_program->constants[idx].d);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_VEC2:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushVec2(m_program->constants[idx].vec2Value);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_VEC3:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushVec3(m_program->constants[idx].vec3Value);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_MAT2:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushMat2(m_program->constants[idx].mat2Value);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::PUSH_MAT3:\n\t\t{\n\t\t\tuint8_t idx = readByte();\n\t\t\tpushMat3(m_program->constants[idx].mat3Value);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_BOOL:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushBool(getBoolAt(slot));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_INT:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushInt(getIntAt(slot));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_DOUBLE:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushDouble(getDoubleAt(slot));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_VEC2:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushDouble(m_stack[slot]);\n\t\t\tpushDouble(m_stack[slot + 1]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_VEC3:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushDouble(m_stack[slot  ]);\n\t\t\tpushDouble(m_stack[slot+1]);\n\t\t\tpushDouble(m_stack[slot+2]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_MAT2:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushDouble(m_stack[slot    ]);\n\t\t\tpushDouble(m_stack[slot + 1]);\n\t\t\tpushDouble(m_stack[slot + 2]);\n\t\t\tpushDouble(m_stack[slot + 3]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_MAT3:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushFrom(slot, 9);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_ARRAY:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tuint8_t slot = readByte();\n\t\t\tcopy(stackTop, slot, size);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_STRUCT:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tuint8_t slot = readByte();\n\t\t\tcopy(stackTop, slot, size);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_REF:\n\t\t{\n\t\t\tassert(ref.ptr == nullptr); // make sure we don't overwrite an existing reference\n\t\t\tuint8_t slot = readByte();\n\t\t\tref.ptr = &m_stack[slot];\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_REF:\n\t\t{\n\t\t\tassert(ref.ptr == nullptr); // make sure we don't overwrite an existing reference\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tref.ptr = &m_stack[frame.base + slot];\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_MEMBER_REF:\n\t\t{\n\t\t\tuint8_t offset = readByte();\n\t\t\tdouble* slot = ref.ptr;\n\t\t\tref.ptr = slot + offset;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_REF:\n\t\t{\n\t\t\tuint8_t elemSize = readByte();\n\t\t\tint index = popInt();\n\n\t\t\tdouble* slot = ref.ptr;\n\t\t\tref.ptr = slot + elemSize*index;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_REF_BOOL:\n\t\tcase OpCode::GET_INDEX_REF_INT:\n\t\tcase OpCode::GET_INDEX_REF_DOUBLE:\n\t\t{\n\t\t\tint index = popInt();\n\n\t\t\tdouble* slot = ref.ptr;\n\t\t\tref.ptr = slot + index;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_REF_VEC2:\n\t\t{\n\t\t\tint index = popInt();\n\n\t\t\tdouble* slot = ref.ptr;\n\t\t\tref.ptr = slot + index * 2;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_REF_VEC3:\n\t\t{\n\t\t\tint index = popInt();\n\n\t\t\tdouble* slot = ref.ptr;\n\t\t\tref.ptr = slot + index * 3;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC2_X_REF:\n\t\t{\n\t\t\tdouble* v = ref.ptr;\n\t\t\tref.ptr = v;\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_VEC2_Y_REF:\n\t\t{\n\t\t\tdouble* v = ref.ptr;\n\t\t\tref.ptr = v + 1;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC3_X_REF:\n\t\t{\n\t\t\tdouble* v = ref.ptr;\n\t\t\tref.ptr = v;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC3_Y_REF:\n\t\t{\n\t\t\tdouble* v = ref.ptr;\n\t\t\tref.ptr = v + 1;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC3_Z_REF:\n\t\t{\n\t\t\tdouble* v = ref.ptr;\n\t\t\tref.ptr = v + 2;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_BOOL:\n\t\t{\n\t\t\tbool b = peekBool();\n\t\t\t*ref.ptr = b;\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_INT :\n\t\t{\n\t\t\tint n = peekInt();\n\t\t\t*ref.ptr = n;\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_DOUBLE:\n\t\t{\n\t\t\t*ref.ptr = peek();\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_VEC2:\n\t\t{\n\t\t\tvec2 v = peekVec2();\n\t\t\tdouble* xPtr = ref.ptr;\n\t\t\txPtr[0] = v.x;\n\t\t\txPtr[1] = v.y;\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_VEC3:\n\t\t{\n\t\t\tvec3 v = peekVec3();\n\t\t\tdouble* xPtr = ref.ptr;\n\t\t\txPtr[0] = v.x;\n\t\t\txPtr[1] = v.y;\n\t\t\txPtr[2] = v.z;\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_MAT2:\n\t\t{\n\t\t\tmat2 v = peekMat2();\n\t\t\tdouble* xPtr = ref.ptr;\n\t\t\txPtr[0] = v.m[0][0];\n\t\t\txPtr[1] = v.m[0][1];\n\t\t\txPtr[2] = v.m[1][0];\n\t\t\txPtr[3] = v.m[1][1];\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_MAT3:\n\t\t{\n\t\t\tdouble* xPtr = ref.ptr;\n\t\t\tmemcpy(xPtr, &m_stack[stackTop - 9], 9 * sizeof(double)); // copy all 9 elements at once\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_ARRAY:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tmemcpy(ref.ptr, &m_stack[stackTop - size], size * sizeof(double));\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::STORE_STRUCT:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tmemcpy(ref.ptr, &m_stack[stackTop - size], size * sizeof(double));\n\t\t\tref.ptr = nullptr;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_BOOL:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tsetBoolAt(slot, peekBool());\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_INT:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tsetIntAt(slot, peekInt());\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_DOUBLE:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tm_stack[slot] = peek();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_VEC2:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tsetVec2At(slot, peekVec2());\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_VEC3:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tsetVec3At(slot, peekVec3());\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_MAT2:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tsetMat2At(slot);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_MAT3:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tsetMat3At(slot);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_ARRAY:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tuint8_t slot = readByte();\n\t\t\tcopy(slot, (int)(stackTop - size), (int)size);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SET_GLOBAL_STRUCT:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tuint8_t slot = readByte();\n\n\t\t\tcopy(slot, (int)(stackTop - size), (int)size);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_BOOL:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tpushBool((bool)m_stack[frame.base + slot]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_INT:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tpushInt((int)m_stack[frame.base + slot]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_DOUBLE:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tpushDouble(m_stack[frame.base + slot]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_VEC2:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tpushVec2(getVec2At((int)frame.base + (int)slot));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_VEC3:\n\t\t{\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tpushVec3(getVec3At((int)frame.base + (int)slot));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_ARRAY:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tcopy(stackTop, frame.base + slot, size);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_LOCAL_STRUCT:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tuint8_t slot = readByte();\n\t\t\tCallFrame& frame = currentFrame();\n\t\t\tcopy(stackTop, frame.base + slot, size);\n\t\t\tbreak;\n\t\t}\n\n\t\t// Integer operators\n\t\tcase OpCode::NEG_INT:\n\t\t{\n\t\t\tint a = popInt();\n\t\t\tpushInt(-a);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::ADD_INT:\n\t\tcase OpCode::SUB_INT:\n\t\tcase OpCode::MUL_INT:\n\t\tcase OpCode::DIV_INT:\n\t\tcase OpCode::EXP_INT:\n\t\tcase OpCode::GT_INT:\n\t\tcase OpCode::LT_INT:\n\t\tcase OpCode::GE_INT:\n\t\tcase OpCode::LE_INT:\n\t\t{\n\t\t\tint b = popInt();\n\t\t\tint a = popInt();\n\n\t\t\tswitch (instruction)\n\t\t\t{\n\t\t\tcase OpCode::ADD_INT: pushInt(a + b); break;\n\t\t\tcase OpCode::SUB_INT: pushInt(a - b); break;\n\t\t\tcase OpCode::MUL_INT: pushInt(a * b); break;\n\t\t\tcase OpCode::DIV_INT:\n\t\t\t\tif (b == 0)\n\t\t\t\t\tthrow std::runtime_error(\"division by zero.\");\n\t\t\t\tpushInt(a / b);\n\t\t\t\tbreak;\n\t\t\tcase OpCode::EXP_INT:\n\t\t\t\tif (b < 0)\n\t\t\t\t\tthrow std::runtime_error(\"Negative exponent not supported for integers.\");\n\n\t\t\t\tpushInt(ipow(a, b));\n\t\t\t\tbreak;\n\t\t\tcase OpCode::GT_INT: pushBool(a > b); break;\n\t\t\tcase OpCode::LT_INT: pushBool(a < b); break;\n\t\t\tcase OpCode::GE_INT: pushBool(a >= b); break;\n\t\t\tcase OpCode::LE_INT: pushBool(a <= b); break;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\t// Double unary operators\n\t\tcase OpCode::NEG_DOUBLE:\n\t\tcase OpCode::SQR_DOUBLE:\n\t\tcase OpCode::SQRT_DOUBLE:\n\t\t{\n\t\t\tdouble a = popDouble();\n\n\t\t\tswitch (instruction)\n\t\t\t{\n\t\t\tcase OpCode::NEG_DOUBLE: pushDouble(-a); break;\n\t\t\tcase OpCode::SQR_DOUBLE: pushDouble(a * a); break;\n\t\t\tcase OpCode::SQRT_DOUBLE: pushDouble(std::sqrt(a)); break;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// double binary operators\n\t\tcase OpCode::ADD_DOUBLE:\n\t\tcase OpCode::SUB_DOUBLE:\n\t\tcase OpCode::MUL_DOUBLE:\n\t\tcase OpCode::DIV_DOUBLE:\n\t\tcase OpCode::EXP_DOUBLE:\n\t\tcase OpCode::GT_DOUBLE:\n\t\tcase OpCode::LT_DOUBLE:\n\t\tcase OpCode::GE_DOUBLE:\n\t\tcase OpCode::LE_DOUBLE:\n\t\t{\n\t\t\tdouble b = popDouble();\n\t\t\tdouble a = popDouble();\n\n\t\t\tswitch (instruction)\n\t\t\t{\n\t\t\tcase OpCode::ADD_DOUBLE: pushDouble(a + b); break;\n\t\t\tcase OpCode::SUB_DOUBLE: pushDouble(a - b); break;\n\t\t\tcase OpCode::MUL_DOUBLE: pushDouble(a * b); break;\n\t\t\tcase OpCode::EXP_DOUBLE: pushDouble(std::pow(a, b)); break;\n\t\t\tcase OpCode::DIV_DOUBLE: \n\t\t\t\tif (b == 0.0)\n\t\t\t\t\tthrow std::runtime_error(\"division by zero.\");\n\t\t\t\tpushDouble(a / b); \n\t\t\t\tbreak;\n\t\t\tcase OpCode::GT_DOUBLE: pushBool(a > b); break;\n\t\t\tcase OpCode::LT_DOUBLE: pushBool(a < b); break;\n\t\t\tcase OpCode::GE_DOUBLE: pushBool(a >= b); break;\n\t\t\tcase OpCode::LE_DOUBLE: pushBool(a <= b); break;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::ADD_VEC2:\n\t\tcase OpCode::SUB_VEC2:\n\t\tcase OpCode::DOT_VEC2:\n\t\t{\n\t\t\tvec2& vec2_1 = popVec2();\n\t\t\tvec2& vec2_0 = popVec2();\n\n\t\t\tswitch (instruction)\n\t\t\t{\n\t\t\tcase OpCode::ADD_VEC2: pushVec2(vec2_0 + vec2_1); break;\n\t\t\tcase OpCode::SUB_VEC2: pushVec2(vec2_0 - vec2_1); break;\n\t\t\tcase OpCode::DOT_VEC2: pushDouble(vec2_0 * vec2_1); break;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_VEC2_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tdouble* v = peekPtr(2);\n\t\t\tv[0] *= scalar;\n\t\t\tv[1] *= scalar;\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_DOUBLE_VEC2:\n\t\t{\n\t\t\tdouble* v = popPtr(2);\n\t\t\tdouble scalar = popDouble();\n\t\t\tpushDouble(v[0] * scalar);\n\t\t\tpushDouble(v[1] * scalar);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::DIV_VEC2_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tdouble* a = peekPtr(2);\n\t\t\ta[0] /= scalar;\n\t\t\ta[1] /= scalar;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC2_X:\n\t\t{\n\t\t\tdouble* v = popPtr(2);\n\t\t\tpushDouble(v[0]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_VEC2_Y:\n\t\t{\n\t\t\tdouble* v = popPtr(2);\n\t\t\tpushDouble(v[1]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::NEG_VEC2:\n\t\t{\n\t\t\tdouble* v = peekPtr(2);\n\t\t\tv[0] = -v[0];\n\t\t\tv[1] = -v[1];\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC2_SWIZZLE:\n\t\t{\n\t\t\tuint8_t mask = readByte();\n\t\t\tuint8_t size = readByte();\n\t\t\tdouble* v = popPtr(2);\n\t\t\tdouble c[4] = { v[0], v[1], 0.0, 0.0};\n\n\t\t\tif (size == 2)\n\t\t\t{\n\t\t\t\tvec2 result;\n\t\t\t\tresult.y = c[mask & 0b11];\n\t\t\t\tresult.x = c[(mask >> 2) & 0b11];\n\t\t\t\tpushVec2(result);\n\t\t\t}\n\t\t\telse if (size == 3)\n\t\t\t{\n\t\t\t\tvec3 result;\n\t\t\t\tresult.z = c[mask & 0b11];\n\t\t\t\tresult.y = c[(mask >> 2) & 0b11];\n\t\t\t\tresult.x = c[(mask >> 4) & 0b11];\n\t\t\t\tpushVec3(result);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC2_INDEX:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tdouble* v = popPtr(2);\n#ifndef NDEBUG\n\t\t\tif (index < 0 || index > 1)\n\t\t\t\tthrow std::runtime_error(\"vec2 index out of bounds.\");\n#endif\n\t\t\tpushDouble(v[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::CREATE_VEC2_1ARG:\n\t\t{\n\t\t\tdouble a = popDouble();\n\t\t\tpushDouble(a);\n\t\t\tpushDouble(a);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::ADD_VEC3:\n\t\t{\n\t\t\tdouble* b = popPtr(3);\n\t\t\tdouble* a = peekPtr(3);\n\t\t\ta[0] += b[0];\n\t\t\ta[1] += b[1];\n\t\t\ta[2] += b[2];\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::SUB_VEC3:\n\t\t{\n\t\t\tdouble* b = popPtr(3);\n\t\t\tdouble* a = peekPtr(3);\n\t\t\ta[0] -= b[0];\n\t\t\ta[1] -= b[1];\n\t\t\ta[2] -= b[2];\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::DOT_VEC3:\n\t\t{\n\t\t\tdouble* b = popPtr(3);\n\t\t\tdouble* a = popPtr(3);\n\t\t\tpushDouble(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_VEC3_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tdouble* a = peekPtr(3);\n\t\t\ta[0] *= scalar;\n\t\t\ta[1] *= scalar;\n\t\t\ta[2] *= scalar;\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_DOUBLE_VEC3:\n\t\t{\n\t\t\tdouble* a = popPtr(3);\n\t\t\tdouble scalar = popDouble();\n\t\t\tpushDouble(a[0] * scalar);\n\t\t\tpushDouble(a[1] * scalar);\n\t\t\tpushDouble(a[2] * scalar);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::DIV_VEC3_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tdouble* a = peekPtr(3);\n\t\t\ta[0] /= scalar;\n\t\t\ta[1] /= scalar;\n\t\t\ta[2] /= scalar;\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_VEC3_X:\n\t\t{\n\t\t\tdouble* a = popPtr(3);\n\t\t\tpushDouble(a[0]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_VEC3_Y:\n\t\t{\n\t\t\tdouble* a = popPtr(3);\n\t\t\tpushDouble(a[1]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_VEC3_Z:\n\t\t{\n\t\t\tdouble* a = popPtr(3);\n\t\t\tpushDouble(a[2]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_VEC3_SWIZZLE:\n\t\t{\n\t\t\tuint8_t mask = readByte();\n\t\t\tuint8_t size = readByte();\n\t\t\tdouble* a = popPtr(3);\n\t\t\tdouble c[4] = { a[0], a[1], a[2], 0.0 };\n\n\t\t\tif (size == 2)\n\t\t\t{\n\t\t\t\tvec2 result;\n\t\t\t\tresult.y = c[mask & 0b11];\n\t\t\t\tresult.x = c[(mask >> 2) & 0b11];\n\t\t\t\tpushVec2(result);\n\t\t\t}\n\t\t\telse if (size == 3)\n\t\t\t{\n\t\t\t\tvec3 result;\n\t\t\t\tresult.z = c[mask & 0b11];\n\t\t\t\tresult.y = c[(mask >> 2) & 0b11];\n\t\t\t\tresult.x = c[(mask >> 4) & 0b11];\n\t\t\t\tpushVec3(result);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::CREATE_VEC3_1ARG:\n\t\t{\n\t\t\tdouble a = popDouble();\n\t\t\tpushDouble(a);\n\t\t\tpushDouble(a);\n\t\t\tpushDouble(a);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::NEG_VEC3:\n\t\t{\n\t\t\tdouble* a = peekPtr(3);\n\t\t\ta[0] = -a[0];\n\t\t\ta[1] = -a[1];\n\t\t\ta[2] = -a[2];\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_VEC3_INDEX:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tdouble* a = popPtr(3);\n#ifndef NDEBUG\n\t\t\tif (index < 0 || index > 2)\n\t\t\t\tthrow std::runtime_error(\"vec3 index out of bounds.\");\n#endif\n\t\t\tpushDouble(a[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\t// ----- Mat2 operators ------\n\t\tcase OpCode::ADD_MAT2:\n\t\t{\n\t\t\tmat2& b = popMat2();\n\t\t\tmat2& a = popMat2();\n\t\t\tpushMat2(a + b);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::SUB_MAT2:\n\t\t{\n\t\t\tmat2& b = popMat2();\n\t\t\tmat2& a = popMat2();\n\t\t\tpushMat2(a - b);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_MAT2:\n\t\t{\n\t\t\tmat2& b = popMat2();\n\t\t\tmat2& a = popMat2();\n\t\t\tpushMat2(a * b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::MUL_DOUBLE_MAT2:\n\t\t{\n\t\t\tmat2& a = popMat2();\n\t\t\tdouble scalar = popDouble();\n\t\t\tpushMat2(a * scalar);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_MAT2_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tmat2& a = popMat2();\n\t\t\tpushMat2(a * scalar);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::DIV_MAT2_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tmat2& a = popMat2();\n\t\t\tpushMat2(a / scalar);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::MUL_MAT2_VEC2:\n\t\t{\n\t\t\tvec2& v = popVec2();\n\t\t\tmat2& A = popMat2();\n\t\t\tpushVec2(A * v);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_MAT2_INDEX:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tmat2& A = popMat2();\n#ifndef NDEBUG\n\t\t\tif (index < 0 || index > 1)\n\t\t\t\tthrow std::runtime_error(\"mat2 index out of bounds.\");\n#endif\n\t\t\tpushVec2(*((vec2*)(&A.m[index][0])));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::CREATE_MAT2_DIAG:\n\t\t{\n\t\t\tdouble a = popDouble();\n\t\t\tpushDouble(a);\n\t\t\tpushDouble(0.0);\n\t\t\tpushDouble(0.0);\n\t\t\tpushDouble(a);\n\t\t\tbreak;\n\t\t}\n\n\t\t// ----- Mat3 operators ------\n\t\tcase OpCode::NEG_MAT3:\n\t\t{\n\t\t\tmat3& A = peekMat3();\n\t\t\tA.m[0][0] = -A.m[0][0]; A.m[0][1] = -A.m[0][1]; A.m[0][2] = -A.m[0][2];\n\t\t\tA.m[1][0] = -A.m[1][0]; A.m[1][1] = -A.m[1][1]; A.m[1][2] = -A.m[1][2];\n\t\t\tA.m[2][0] = -A.m[2][0]; A.m[2][1] = -A.m[2][1]; A.m[2][2] = -A.m[2][2];\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::ADD_MAT3:\n\t\t{\n\t\t\tmat3& B = popMat3();\n\t\t\tmat3& A = popMat3();\n\t\t\tpushMat3(A + B);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::SUB_MAT3:\n\t\t{\n\t\t\tmat3& B = popMat3();\n\t\t\tmat3& A = popMat3();\n\t\t\tpushMat3(A - B);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_MAT3:\n\t\t{\n\t\t\tmat3& B = popMat3();\n\t\t\tmat3& A = popMat3();\n\t\t\tpushMat3(A * B);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_DOUBLE_MAT3:\n\t\t{\n\t\t\tmat3& A = popMat3();\n\t\t\tdouble scalar = popDouble();\n\t\t\tpushMat3(A * scalar);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::MUL_MAT3_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tmat3& A = popMat3();\n\t\t\tpushMat3(A * scalar);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::DIV_MAT3_DOUBLE:\n\t\t{\n\t\t\tdouble scalar = popDouble();\n\t\t\tmat3& A = popMat3();\n\t\t\tpushMat3(A / scalar);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::MUL_MAT3_VEC3:\n\t\t{\n\t\t\tvec3& v = popVec3();\n\t\t\tmat3& A = popMat3();\n\t\t\tpushVec3(A * v);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_MAT3_INDEX:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tmat3& A = popMat3();\n#ifndef NDEBUG\n\t\t\tif (index < 0 || index > 2)\n\t\t\t\tthrow std::runtime_error(\"mat3 index out of bounds.\");\n#endif\n\t\t\tpushVec3(*((vec3*)(&A.m[index][0])));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::CREATE_MAT3_DIAG:\n\t\t{\n\t\t\tdouble a = popDouble();\n\t\t\tpushDouble(a);\n\t\t\tpushDouble(0.0);\n\t\t\tpushDouble(0.0);\n\n\t\t\tpushDouble(0.0);\n\t\t\tpushDouble(a);\n\t\t\tpushDouble(0.0);\n\n\t\t\tpushDouble(0.0);\n\t\t\tpushDouble(0.0);\n\t\t\tpushDouble(a);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::CREATE_MAT3_VEC3:\n\t\t\t// don't need to do anything here\n\t\t\tbreak;\n\n\t\tcase OpCode::ADD_GLOBAL_MAT3:\n\t\t{\n\t\t\tuint8_t slotA = readByte();\n\t\t\tuint8_t slotB = readByte();\n\t\t\tmat3& A = getMat3At(slotA);\n\t\t\tmat3& B = getMat3At(slotB);\n\t\t\tpushMat3(A + B);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::SUB_GLOBAL_MAT3:\n\t\t{\n\t\t\tuint8_t slotA = readByte();\n\t\t\tuint8_t slotB = readByte();\n\t\t\tmat3& A = getMat3At(slotA);\n\t\t\tmat3& B = getMat3At(slotB);\n\t\t\tpushMat3(A - B);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::MUL_GLOBAL_MAT3:\n\t\t{\n\t\t\tuint8_t slotA = readByte();\n\t\t\tuint8_t slotB = readByte();\n\t\t\tmat3& A = getMat3At(slotA);\n\t\t\tmat3& B = getMat3At(slotB);\n\t\t\tpushMat3(A * B);\n\t\t\tbreak;\n\t\t}\n\n\t\t// ----- Logical operators -----\n\t\tcase OpCode::NOT:\n\t\t{\n\t\t\tbool b = popBool();\n\t\t\tpushBool(!b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::EQUAL_BOOL:\n\t\t{\n\t\t\tbool b = popBool();\n\t\t\tbool a = popBool();\n\t\t\tpushBool(a == b);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::EQUAL_INT:\n\t\t{\n\t\t\tint b = popInt();\n\t\t\tint a = popInt();\n\t\t\tpushBool(a == b);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::EQUAL_DOUBLE:\n\t\t{\n\t\t\tdouble b = popDouble();\n\t\t\tdouble a = popDouble();\n\t\t\tpushBool(a == b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::NEQ_BOOL:\n\t\t{\n\t\t\tbool b = popBool();\n\t\t\tbool a = popBool();\n\t\t\tpushBool(a != b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::NEQ_INT:\n\t\t{\n\t\t\tint b = popInt();\n\t\t\tint a = popInt();\n\t\t\tpushBool(a != b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::NEQ_DOUBLE:\n\t\t{\n\t\t\tdouble b = popDouble();\n\t\t\tdouble a = popDouble();\n\t\t\tpushBool(a != b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_BOOL:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushBool(obj.fields[slot].b);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_INT:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushInt(obj.fields[slot].i);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_DOUBLE:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushDouble(obj.fields[slot].d);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_VEC2:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushVec2(obj.fields[slot].vec2Value);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_VEC3:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushVec3(obj.fields[slot].vec3Value);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_ARRAY:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushArray(*obj.fields[slot].arrayValue);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_PROPERTY_STRUCT:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tStructValue obj = popStruct(m_program->types.getStructType(typeIndex));\n\t\t\tuint8_t slot = readByte();\n\t\t\tpushStruct(*obj.fields[slot].structValue);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_BOOL:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Bool);\n\n\t\t\tdouble* arr = popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->arraySize)\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushBool(arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_INT:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Int);\n\n\t\t\tdouble* arr = popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->arraySize)\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushInt((int)arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_DOUBLE:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Double);\n\n\t\t\tdouble* arr = popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->arraySize)\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushDouble(arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_VEC2:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Vec2);\n\n\t\t\tvec2* arr = (vec2*) popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->arraySize)\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushVec2(arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_VEC3:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Vec3);\n\n\t\t\tvec3* arr = (vec3*)popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->size())\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushVec3(arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_MAT2:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Mat2);\n\n\t\t\tmat2* arr = (mat2*)popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->size())\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushMat2(arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_MAT3:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Mat3);\n\n\t\t\tmat3* arr = (mat3*)popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->size())\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpushMat3(arr[index]);\n\t\t\tbreak;\n\t\t}\n\t\tcase OpCode::GET_INDEX_ARRAY:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Array);\n\n\t\t\tdouble* arr = popPtr(type->size());\n\n\t\t\tsize_t elemSize = type->elementType->size();\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->arraySize)\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tpush(arr + index * elemSize, elemSize); break;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_INDEX_STRUCT:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t typeIndex = readByte();\n\n\t\t\tType type = m_program->types.getArrayType((int)typeIndex);\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tassert(type->elementType->kind == TypeKind::Struct);\n\n\t\t\tdouble* arr = popPtr(type->size());\n\n#ifndef  NDEBUG\n\t\t\tif (index >= type->arraySize)\n\t\t\t\tthrow std::runtime_error(\"Array index out of bounds.\");\n#endif // ! NDEBUG\n\n\t\t\tsize_t structSize = type->elementType->size();\n\n\t\t\tpush(arr + index * structSize, structSize);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::GET_GLOBAL_INDEX_DOUBLE:\n\t\t{\n\t\t\tint index = popInt();\n\t\t\tuint8_t slot = readByte();\n\t\t\tdouble* arr = getPtrAt(slot);\n\t\t\tpushDouble(arr[index]);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::JUMP:\n\t\t{\n\t\t\tuint16_t offset = readUint16();\n\t\t\tip += offset;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::JUMP_IF_FALSE:\n\t\t{\n\t\t\tuint16_t offset = readUint16();\n\t\t\tif (peek() == 0.0)\n\t\t\t\tip += offset;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::JUMP_IF_TRUE:\n\t\t{\n\t\t\tuint16_t offset = readUint16();\n\t\t\tif (peek() != 0.0)\n\t\t\t\tip += offset;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::LOOP:\n\t\t{\n\t\t\tuint16_t offset = readUint16();\n\t\t\tip -= offset;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_VOID:\n\t\t{\n\t\t\tpopVoid();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_BOOL:\n\t\t{\n\t\t\tpopBool();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_INT:\n\t\t{\n\t\t\tpopInt();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_DOUBLE:\n\t\t{\n\t\t\tpopDouble();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_VEC2:\n\t\t{\n\t\t\tpop(2);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_VEC3:\n\t\t{\n\t\t\tpop(3);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_MAT2:\n\t\t{\n\t\t\tpop(4);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_MAT3:\n\t\t{\n\t\t\tpop(9);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_ARRAY:\n\t\t{\n\t\t\tuint8_t size = readByte();\n\t\t\tpop(size);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::POP_STRUCT:\n\t\t{\n\t\t\tuint8_t typeIndex = readByte();\n\t\t\tType type = m_program->types.getStructType(typeIndex);\n\t\t\tpop(type->size());\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::CALL:\n\t\t{\n\t\t\tuint8_t fnIndex = readByte();\n\t\t\tuint8_t args = readByte();\n\t\t\tcallFunction(fnIndex, args);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OpCode::RETURN_VOID:\n\t\tcase OpCode::RETURN_BOOL:\n\t\tcase OpCode::RETURN_INT:\n\t\tcase OpCode::RETURN_DOUBLE:\n\t\tcase OpCode::RETURN_VEC2:\n\t\tcase OpCode::RETURN_VEC3:\n\t\tcase OpCode::RETURN_MAT2:\n\t\tcase OpCode::RETURN_MAT3:\n\t\tcase OpCode::RETURN_ARRAY:\n\t\tcase OpCode::RETURN_STRUCT:\n\t\t{\n\t\t\tTypeKind returnType = (TypeKind)((uint8_t)instruction - (uint8_t)OpCode::RETURN_VOID);\n\n\t\t\tValue result;\n\t\t\tif (stackTop > globalStackSize)\n\t\t\t{\n\t\t\t\tswitch (returnType)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Void  : pop(); break;\n\t\t\t\tcase TypeKind::Bool  : result = popBool  (); break;\n\t\t\t\tcase TypeKind::Int   : result = popInt   (); break;\n\t\t\t\tcase TypeKind::Double: result = popDouble(); break;\n\t\t\t\tcase TypeKind::Vec2  : result = popVec2  (); break;\n\t\t\t\tcase TypeKind::Vec3  : result = popVec3  (); break;\n\t\t\t\tcase TypeKind::Mat2  : result = popMat2  (); break;\n\t\t\t\tcase TypeKind::Mat3  : result = popMat3  (); break;\n\t\t\t\tcase TypeKind::Array:\n\t\t\t\t{\n\t\t\t\t\tuint8_t typeIndex = readByte();\n\t\t\t\t\tresult = std::make_shared<ArrayValue>(popArray(m_program->types.getArrayType(typeIndex)));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase TypeKind::Struct:\n\t\t\t\t{\n\t\t\t\t\tuint8_t typeIndex = readByte();\n\t\t\t\t\tresult = std::make_shared<StructValue>(popStruct(m_program->types.getStructType(typeIndex)));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tresult = pop();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tCallFrame& frame = m_frames[--frameCount];\n\t\t\tstackTop = frame.base;\n\t\t\tip = frame.ip;\n\n\t\t\tif (frameCount == 0)\n\t\t\t\treturn result;\n\n\t\t\tswitch (returnType)\n\t\t\t{\n\t\t\tcase TypeKind::Void  : pushVoid  (); break;\n\t\t\tcase TypeKind::Bool  : pushBool  (result.b); break;\n\t\t\tcase TypeKind::Int   : pushInt   (result.i); break;\n\t\t\tcase TypeKind::Double: pushDouble(result.d); break;\n\t\t\tcase TypeKind::Vec2  : pushVec2  (result.vec2Value); break;\n\t\t\tcase TypeKind::Vec3  : pushVec3  (result.vec3Value); break;\n\t\t\tcase TypeKind::Array : pushArray (*result.arrayValue); break;\n\t\t\tcase TypeKind::Struct: pushStruct(*result.structValue); break;\n\t\t\tdefault:\n\t\t\t\tthrow std::runtime_error(\"Unsupported return type\");\n\t\t\t}\n\n#ifndef NDEBUG\n\t\t\tif (m_debug)\n\t\t\t{\n\t\t\t\tprintStack(m_stack, (int)globalStackSize, (int)stackTop, ref.ptr);\n\t\t\t}\n#endif\n\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unknown opcode: \" + std::to_string((int)instruction));\n\t\t}\n\n#ifndef NDEBUG\n\t\tif (m_debug && (instruction < OpCode::RETURN_VOID))\n\t\t{\n\t\t\tprintStack(m_stack, (int)globalStackSize, (int)stackTop, ref.ptr);\n\t\t}\n#endif\n\t}\n\n\tthrow std::runtime_error(\"Unexpected end of code.\");\n}\n\nvoid VM::callFunction(int fnIndex, int argCount)\n{\n\tconst FunctionInfo& fn = m_program->functions[fnIndex];\n\n\tif (argCount != fn.args.size())\n\t\tthrow std::runtime_error(\"Arity mismatch in call to \" + fn.name);\n\n\tif (fn.isNative)\n\t{\n\t\tFuncArgs args;\n\t\targs.count = argCount;\n\t\targs.stack = &m_stack[stackTop - fn.argSize];\n\n\t\tValue result = fn.fnc(args);\n\t\tstackTop -= fn.argSize;\n\n\t\tswitch (fn.returnType->kind)\n\t\t{\n\t\tcase TypeKind::Void  : pushVoid  (); break;\n\t\tcase TypeKind::Bool  : pushBool  (result.b); break;\n\t\tcase TypeKind::Int   : pushInt   (result.i); break;\n\t\tcase TypeKind::Double: pushDouble(result.d); break;\n\t\tcase TypeKind::Vec2  : pushVec2  (result.vec2Value); break;\n\t\tcase TypeKind::Vec3  : pushVec3  (result.vec3Value); break;\n\t\tcase TypeKind::Mat2  : pushMat2  (result.mat2Value); break;\n\t\tcase TypeKind::Mat3  : pushMat3  (result.mat3Value); break;\n\t\tdefault:\n\t\t\tthrow std::runtime_error(\"Unsupported return type from native function: \" + std::to_string((int)fn.returnType->kind));\n\t\t}\n\t\treturn;\n\t}\n\n\tif (frameCount >= MAX_CALL_DEPTH)\n\t\tthrow std::runtime_error(\"Stack overflow: too many nested function calls.\");\n\n\tCallFrame& frame = m_frames[frameCount++];\n\tframe.functionIndex = fnIndex;\n\tframe.base = stackTop - fn.argSize;\n\tframe.ip = ip;\n\tip = &(m_program->code[fn.entry]);\n}\n\nValue febcode::runScript(const std::string& script)\n{\n\tProgram prg;\n\n\tCompileSource(prg, script);\n\tVM vm(prg);\n\treturn vm.run();\n}\n"
  },
  {
    "path": "febcode/vm.h",
    "content": "#pragma once\n#include <vector>\n#include <string>\n#include <functional>\n#include <stdexcept>\n#include <cstring>\n#include \"compiler.h\"\n#include <assert.h>\n\nnamespace febcode\n{\n\tclass VM\n\t{\n\t\tstruct Ref {\n\t\t\tdouble* ptr;\n\t\t\tRef() : ptr(nullptr) {}\n\t\t};\n\n\tpublic:\n\t\tenum { MAX_CALL_DEPTH = 8 };\n\n\tpublic:\n\t\tVM() : m_program(nullptr) {}\n\n\t\tvoid setProgram(const Program& program)\n\t\t{\n\t\t\tm_program = &program;\n\t\t\tglobalStackSize = m_program->globalStackSize;\n\t\t\tm_stack.resize(globalStackSize + program.maxStackSize);\n\t\t\tstackTop = globalStackSize; // stack starts after global region\n\t\t}\n\n\t\tVM(const Program& program)\n\t\t{\n\t\t\tsetProgram(program);\n\t\t}\n\n\t\tValue run()\n\t\t{\n\t\t\tif (m_program == nullptr) return Value();\n\t\t\tcallFunction(0, 0);              // call \"main\"\n\t\t\treturn execute();\n\t\t}\n\n\t\tvoid setDebugMode(bool b) { m_debug = b; }\n\n\t\tbool stackEmpty() const { return stackTop == globalStackSize; }\n\t\tsize_t stackSize() const { return stackTop - globalStackSize; }\n\n\t\tValue getGlobal(size_t n)\n\t\t{\n\t\t\tif (n >= globalStackSize)\n\t\t\t\tthrow std::runtime_error(\"Invalid global index: \" + std::to_string(n));\n\n\t\t\tconst Program::Global& glob = m_program->globals[n];\n\t\t\tint slot = glob.slot;\n\t\t\tswitch (glob.type->kind)\n\t\t\t{\n\t\t\tcase TypeKind::Bool  : return getBoolAt  (slot);\n\t\t\tcase TypeKind::Int   : return getIntAt   (slot);\n\t\t\tcase TypeKind::Double: return getDoubleAt(slot);\n\t\t\tcase TypeKind::Vec2  : return getVec2At  (slot);\n\t\t\tcase TypeKind::Vec3  : return getVec3At  (slot);\n\t\t\tdefault:\n\t\t\t\treturn m_stack[slot];\n\t\t\t};\n\t\t}\n\n\t\tValue getGlobal(const std::string& name)\n\t\t{\n\t\t\tauto it = m_program->globalIndices.find(name);\n#ifndef NDEBUG\n\t\t\tif (it == m_program->globalIndices.end())\n\t\t\t\tthrow std::runtime_error(\"Undefined global variable: \" + name);\n#endif\n\t\t\treturn getGlobal(it->second);\n\t\t}\n\n\t\tvoid setGlobal(int i, double v)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif ((i<0) || (i >= globalStackSize))\n\t\t\t\tthrow std::runtime_error(\"Invalid global index: \" + std::to_string(i));\n#endif\n\t\t\tconst Program::Global& glob = m_program->globals[i];\n\t\t\tint slot = glob.slot;\n\t\t\tsetDoubleAt(slot, v);\n\t\t}\n\n\t\tvoid setGlobal(int i, vec2 v)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif ((i < 0) || (i >= globalStackSize))\n\t\t\t\tthrow std::runtime_error(\"Invalid global index: \" + std::to_string(i));\n#endif\n\t\t\tconst Program::Global& glob = m_program->globals[i];\n\t\t\tint slot = glob.slot;\n\t\t\tsetVec2At(slot, v);\n\t\t}\n\n\t\tvoid setGlobal(int i, vec3 v)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif ((i<0) || (i >= globalStackSize))\n\t\t\t\tthrow std::runtime_error(\"Invalid global index: \" + std::to_string(i));\n#endif\n\t\t\tconst Program::Global& glob = m_program->globals[i];\n\t\t\tint slot = glob.slot;\n\t\t\tsetVec3At(slot, v);\n\t\t}\n\n\t\tvoid setGlobal(int i, const Value& value)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif ((i < 0) || (i >= globalStackSize))\n\t\t\t\tthrow std::runtime_error(\"Invalid global index: \" + std::to_string(i));\n#endif\n\t\t\tconst Program::Global& glob = m_program->globals[i];\n\t\t\tint slot = glob.slot;\n\n\t\t\tswitch (glob.type->kind)\n\t\t\t{\n\t\t\tcase TypeKind::Bool: setBoolAt(slot, value.b); break;\n\t\t\tcase TypeKind::Int: setIntAt(slot, value.i); break;\n\t\t\tcase TypeKind::Double: setDoubleAt(slot, value.d); break;\n\t\t\tcase TypeKind::Vec2: setVec2At(slot, value.vec2Value); break;\n\t\t\tcase TypeKind::Vec3: setVec3At(slot, value.vec3Value); break;\n\t\t\tdefault:\n\t\t\t\tthrow std::runtime_error(\"Unsupported global variable type for setGlobal.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tvoid setInput(const std::string& name, const Value& value)\n\t\t{\n\t\t\tconst auto it = m_program->inputIndices.find(name);\n\n#ifndef NDEBUG\n\t\t\tif (it == m_program->inputIndices.end())\n\t\t\t\tthrow std::runtime_error(\"Input variable '\" + name + \"' is not defined.\");\n#endif\n\n\t\t\tsize_t index = it->second;\n\n#ifndef NDEBUG\n\t\t\tif (m_program->inputs[index].type != m_program->types.getBuiltinType(value))\n\t\t\t\tthrow std::runtime_error(\"Type mismatch when setting input variable '\" + name + \"'.\");\n#endif\n\n\t\t\tint globalSlot = m_program->inputs[index].slot;\n\t\t\tsetGlobal(globalSlot, value);\n\t\t}\n\n\tprivate:\n\t\tstruct CallFrame\n\t\t{\n\t\t\tint functionIndex;\n\t\t\tconst uint8_t* ip;\n\t\t\tsize_t base;\n\t\t};\n\n\t\t// ===== Execution Loop =====\n\n\t\tValue execute();\n\t\n\t\t// ===== Call Handling =====\n\n\t\tvoid callFunction(int fnIndex, int argCount);\n\n\t\t// ===== Helpers =====\n\n\t\tCallFrame& currentFrame()\n\t\t{\n\t\t\treturn m_frames[frameCount - 1];\n\t\t}\n\n\t\tuint8_t readByte()\n\t\t{\n\t\t\treturn *ip++;\n\t\t}\n\n\t\tuint16_t readUint16()\n\t\t{\n\t\t\tuint16_t high = readByte();\n\t\t\tuint16_t low = readByte();\n\t\t\treturn (high << 8) | low;\n\t\t}\n\n\t\tvoid push(const double& v)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop >= m_stack.size())\n\t\t\t\tthrow std::runtime_error(\"Stack overflow.\");\n#endif\n\t\t\tm_stack[stackTop++] = v;\n\t\t}\n\n\t\tvoid push(double* d, size_t n)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop + n > m_stack.size())\n\t\t\t\tthrow std::runtime_error(\"Stack overflow.\");\n#endif\n\t\t\tmemcpy(&m_stack[stackTop], d, n * sizeof(double));\n\t\t\tstackTop += n;\n\t\t}\n\n\t\tvoid pushVoid()\n\t\t{\n\t\t\tpush(0.0);\n\t\t}\n\n\t\tvoid pushBool(bool b)\n\t\t{\n\t\t\tpush((double)b);\n\t\t}\n\n\t\tvoid pushInt(int n)\n\t\t{\n\t\t\tpush((double)n);\n\t\t}\n\n\t\tvoid pushDouble(double d)\n\t\t{\n\t\t\tpush(d);\n\t\t}\n\n\t\tvoid pushVec2(const vec2& v)\n\t\t{\n\t\t\tpush(v.x);\n\t\t\tpush(v.y);\n\t\t}\n\n\t\tvoid pushVec3(const vec3& v)\n\t\t{\n\t\t\tpush(v.x);\n\t\t\tpush(v.y);\n\t\t\tpush(v.z);\n\t\t}\n\n\t\tvoid pushMat2(const mat2& m)\n\t\t{\n\t\t\tpush(m.m[0][0]);\n\t\t\tpush(m.m[0][1]);\n\t\t\tpush(m.m[1][0]);\n\t\t\tpush(m.m[1][1]);\n\t\t}\n\n\t\tvoid pushMat3(const mat3& m)\n\t\t{\n\t\t\tmemcpy(&m_stack[stackTop], &(m.m[0][0]), 9 * sizeof(double));\n\t\t\tstackTop += 9;\n\t\t}\n\n\t\tvoid pushFrom(int slot, size_t size)\n\t\t{\n\t\t\tmemcpy(&m_stack[stackTop], &m_stack[slot], size * sizeof(double));\n\t\t\tstackTop += size;\n\t\t}\n\n\t\tvoid pushArray(const ArrayValue& arr)\n\t\t{\n\t\t\tfor (int i = 0; i < arr.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Value& v = arr.elements[i];\n\t\t\t\tswitch (arr.type->elementType->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : pushBool  (v.b); break;\n\t\t\t\tcase TypeKind::Int   : pushInt   (v.i); break;\n\t\t\t\tcase TypeKind::Double: pushDouble(v.d); break;\n\t\t\t\tcase TypeKind::Vec2  : pushVec2  (v.vec2Value); break;\n\t\t\t\tcase TypeKind::Vec3  : pushVec3  (v.vec3Value); break;\n\t\t\t\tcase TypeKind::Array : pushArray (*v.arrayValue); break;\n\t\t\t\tcase TypeKind::Struct: pushStruct(*v.structValue); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported array element type for push.\");\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tvoid pushStruct(const StructValue& obj)\n\t\t{\n\t\t\tfor (int i=0; i<obj.fields.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Value& field = obj.fields[i];\n\t\t\t\tswitch (obj.type->fields[i].first->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : pushBool  (field.b); break;\n\t\t\t\tcase TypeKind::Int   : pushInt   (field.i); break;\n\t\t\t\tcase TypeKind::Double: pushDouble(field.d); break;\n\t\t\t\tcase TypeKind::Vec2  : pushVec2  (field.vec2Value); break;\n\t\t\t\tcase TypeKind::Vec3  : pushVec3  (field.vec3Value); break;\n\t\t\t\tcase TypeKind::Array : pushArray (*field.arrayValue); break;\n\t\t\t\tcase TypeKind::Struct: pushStruct(*field.structValue); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported field type in pushStruct.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst double& pop()\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop <= globalStackSize)\n\t\t\t\tthrow std::runtime_error(\"Stack underflow.\");\n#endif\n\t\t\treturn m_stack[--stackTop];\n\t\t}\n\n\t\tvoid pop(size_t n)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop < globalStackSize + n)\n\t\t\t\tthrow std::runtime_error(\"Stack underflow.\");\n#endif\n\t\t\tstackTop -= n;\n\t\t}\n\n\t\tvoid popVoid()\n\t\t{\n\t\t\tpop();\n\t\t}\n\n\t\tbool popBool()\n\t\t{\n\t\t\treturn (pop() != 0.0);\n\t\t}\n\n\t\tint popInt()\n\t\t{\n\t\t\treturn (int)pop();\n\t\t}\n\n\t\tdouble popDouble()\n\t\t{\n\t\t\treturn pop();\n\t\t}\n\n\t\tvec2& popVec2()\n\t\t{\n\t\t\tstackTop -= 2;\n\t\t\treturn *(vec2*)(&m_stack[stackTop]);\n\t\t}\n\n\t\tvec3& popVec3()\n\t\t{\n\t\t\tstackTop -= 3;\n\t\t\treturn *(vec3*)(&m_stack[stackTop]);\n\t\t}\n\n\t\tmat2& popMat2()\n\t\t{\n\t\t\tstackTop -= 4;\n\t\t\treturn *(mat2*)(&m_stack[stackTop]);\n\t\t}\n\n\t\tmat3& popMat3()\n\t\t{\n\t\t\tstackTop -= 9;\n\t\t\treturn *(mat3*)(&m_stack[stackTop]);\n\t\t}\n\n\t\tdouble* popPtr(size_t n)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop < globalStackSize + n)\n\t\t\t\tthrow std::runtime_error(\"Stack underflow.\");\n#endif\n\t\t\tstackTop -= n;\n\t\t\treturn &m_stack[stackTop];\n\t\t}\n\n\t\tmat3& getMat3At(int slot)\n\t\t{\n\t\t\treturn *(mat3*)(&m_stack[slot]);\n\t\t}\n\n\t\tArrayValue popArray(Type type)\n\t\t{\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tArrayValue arr;\n\t\t\tarr.type = type;\n\t\t\tarr.elements.resize(type->arraySize);\n\t\t\tfor (int i = (int)arr.size() - 1; i >= 0; --i)\n\t\t\t{\n\t\t\t\tswitch (type->elementType->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : arr.elements[i] = popBool(); break;\n\t\t\t\tcase TypeKind::Int   : arr.elements[i] = popInt(); break;\n\t\t\t\tcase TypeKind::Double: arr.elements[i] = popDouble(); break;\n\t\t\t\tcase TypeKind::Vec2  : arr.elements[i] = popVec2(); break;\n\t\t\t\tcase TypeKind::Vec3  : arr.elements[i] = popVec3(); break;\n\t\t\t\tcase TypeKind::Array : arr.elements[i] = std::make_shared<ArrayValue >(popArray(type->elementType)); break;\n\t\t\t\tcase TypeKind::Struct: arr.elements[i] = std::make_shared<StructValue>(popStruct(type->elementType)); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported array element type for pop.\");\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn arr;\n\t\t}\n\n\t\tStructValue popStruct(Type type)\n\t\t{\n\t\t\tassert(type->kind == TypeKind::Struct);\n\t\t\tStructValue obj;\n\t\t\tobj.type = type;\n\t\t\tobj.fields.resize(type->fields.size());\n\t\t\tfor (int i = (int)type->fields.size() - 1; i >= 0; --i)\n\t\t\t{\n\t\t\t\tValue& field = obj.fields[i];\n\t\t\t\tswitch (type->fields[i].first->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : field = popBool(); break;\n\t\t\t\tcase TypeKind::Int   : field = popInt(); break;\n\t\t\t\tcase TypeKind::Double: field = popDouble(); break;\n\t\t\t\tcase TypeKind::Vec2  : field = popVec2(); break;\n\t\t\t\tcase TypeKind::Vec3  : field = popVec3(); break;\n\t\t\t\tcase TypeKind::Array : field = std::make_shared<ArrayValue >(popArray (type->fields[i].first)); break;\n\t\t\t\tcase TypeKind::Struct: field = std::make_shared<StructValue>(popStruct(type->fields[i].first)); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported array element type for pop.\");\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn obj;\n\t\t}\n\n\t\tconst double& peek()\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop <= globalStackSize)\n\t\t\t\tthrow std::runtime_error(\"Stack underflow.\");\n#endif\n\t\t\treturn m_stack[stackTop-1];\n\t\t}\n\n\t\tdouble* peekPtr(size_t n)\n\t\t{\n#ifndef NDEBUG\n\t\t\tif (stackTop < globalStackSize + n)\n\t\t\t\tthrow std::runtime_error(\"Stack underflow.\");\n#endif\n\t\t\treturn &m_stack[stackTop - n];\n\t\t}\n\n\t\tbool peekBool()\n\t\t{\n\t\t\treturn (peek() != 0.0);\n\t\t}\n\n\t\tint peekInt()\n\t\t{\n\t\t\treturn (int)peek();\n\t\t}\n\n\t\tvec2& peekVec2()\n\t\t{\n\t\t\treturn *(vec2*)&m_stack[stackTop - 2];\n\t\t}\n\n\t\tvec3& peekVec3()\n\t\t{\n\t\t\treturn *(vec3*)&m_stack[stackTop - 3];\n\t\t}\n\n\t\tmat2& peekMat2()\n\t\t{\n\t\t\treturn *(mat2*)&m_stack[stackTop - 4];\n\t\t}\n\n\t\tmat3& peekMat3()\n\t\t{\n\t\t\treturn *(mat3*)&m_stack[stackTop - 9];\n\t\t}\n\n\t\tArrayValue peekArray(Type type)\n\t\t{\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tint c = (int)(stackTop - type->size());\n\t\t\tArrayValue arr = getArrayAt(c, type);\n\t\t\treturn arr;\n\t\t}\n\n\t\tStructValue peekStruct(Type type)\n\t\t{\n\t\t\tassert(type->kind == TypeKind::Struct);\n\t\t\tint c = (int)(stackTop - type->size());\n\t\t\tStructValue obj = getStructAt(c, type);\n\t\t\treturn obj;\n\t\t}\n\n\t\tvoid setBoolAt(int slot, bool b)\n\t\t{\n\t\t\tm_stack[slot] = b;\n\t\t}\n\n\t\tvoid setIntAt(int slot, int n)\n\t\t{\n\t\t\tm_stack[slot] = n;\n\t\t}\n\n\t\tvoid setDoubleAt(int slot, double d)\n\t\t{\n\t\t\tm_stack[slot] = d;\n\t\t}\n\n\t\tvoid setVec2At(int slot, const vec2& v)\n\t\t{\n\t\t\tm_stack[slot  ] = v.x;\n\t\t\tm_stack[slot+1] = v.y;\n\t\t}\n\n\t\tvoid setVec3At(int slot, const vec3& v)\n\t\t{\n\t\t\tm_stack[slot    ] = v.x;\n\t\t\tm_stack[slot + 1] = v.y;\n\t\t\tm_stack[slot + 2] = v.z;\n\t\t}\n\n\t\tvoid setMat2At(int slot)\n\t\t{\n\t\t\tdouble* s = &m_stack[stackTop - 4]; // last 4 slots\n\t\t\tm_stack[slot  ] = s[0];\n\t\t\tm_stack[slot+1] = s[1];\n\t\t\tm_stack[slot+2] = s[2];\n\t\t\tm_stack[slot+3] = s[3];\n\t\t}\n\n\t\tvoid setMat3At(int slot)\n\t\t{\n\t\t\tdouble* s = &m_stack[stackTop - 9]; // last 9 slots\n\t\t\tmemcpy(&m_stack[slot], s, 9 * sizeof(double));\n\t\t}\n\n\t\tvoid setArrayAt(int slot, const ArrayValue& arr)\n\t\t{\n\t\t\tfor (int i = 0; i < arr.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Value& v = arr.elements[i];\n\t\t\t\tswitch (arr.type->elementType->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : setBoolAt  (slot, v.b); break;\n\t\t\t\tcase TypeKind::Int   : setIntAt   (slot, v.i); break;\n\t\t\t\tcase TypeKind::Double: setDoubleAt(slot, v.d); break;\n\t\t\t\tcase TypeKind::Vec2  : setVec2At  (slot, v.vec2Value); break;\n\t\t\t\tcase TypeKind::Vec3  : setVec3At  (slot, v.vec3Value); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported array element type for setArrayAt.\");\n\t\t\t\t};\n\t\t\t\tslot += (int)arr.type->elementType->size();\n\t\t\t}\n\t\t}\n\n\t\tvoid setStructAt(int slot, const StructValue& obj)\n\t\t{\n\t\t\tfor (int i = 0; i < obj.fields.size(); ++i)\n\t\t\t{\n\t\t\t\tconst Value& v = obj.fields[i];\n\t\t\t\tswitch (obj.type->fields[i].first->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : setBoolAt  (slot, v.b); break;\n\t\t\t\tcase TypeKind::Int   : setIntAt   (slot, v.i); break;\n\t\t\t\tcase TypeKind::Double: setDoubleAt(slot, v.d); break;\n\t\t\t\tcase TypeKind::Vec2  : setVec2At  (slot, v.vec2Value); break;\n\t\t\t\tcase TypeKind::Vec3  : setVec3At  (slot, v.vec3Value); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported array element type for setArrayAt.\");\n\t\t\t\t};\n\t\t\t\tslot += (int)obj.type->fields[i].first->size();\n\t\t\t}\n\t\t}\n\n\t\tdouble* getPtrAt(int slot)\n\t\t{\n\t\t\treturn &m_stack[slot];\n\t\t}\n\n\t\tbool getBoolAt(int slot)\n\t\t{\n\t\t\treturn (m_stack[slot] != 0.0);\n\t\t}\n\n\t\tint getIntAt(int slot)\n\t\t{\n\t\t\treturn (int)m_stack[slot];\n\t\t}\n\n\t\tdouble getDoubleAt(int slot)\n\t\t{\n\t\t\treturn m_stack[slot];\n\t\t}\n\n\t\tvec2 getVec2At(int slot)\n\t\t{\n\t\t\treturn vec2(m_stack[slot], m_stack[slot + 1]);\n\t\t}\n\n\t\tvec3 getVec3At(int slot)\n\t\t{\n\t\t\treturn vec3(m_stack[slot], m_stack[slot + 1], m_stack[slot + 2]);\n\t\t}\n\n\t\tArrayValue getArrayAt(int slot, Type type)\n\t\t{\n\t\t\tassert(type->kind == TypeKind::Array);\n\t\t\tArrayValue arr;\n\t\t\tarr.type = type;\n\t\t\tarr.elements.resize(type->arraySize);\n\n\t\t\tint c = slot;\n\t\t\tfor (int i = 0; i < arr.size(); ++i)\n\t\t\t{\n\t\t\t\tValue& elem = arr.elements[i];\n\t\t\t\tswitch (type->elementType->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : elem = getBoolAt  (c); break;\n\t\t\t\tcase TypeKind::Int   : elem = getIntAt   (c); break;\n\t\t\t\tcase TypeKind::Double: elem = getDoubleAt(c); break;\n\t\t\t\tcase TypeKind::Vec2  : elem = getVec2At  (c); break;\n\t\t\t\tcase TypeKind::Vec3  : elem = getVec3At  (c); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported type in getArrayAt\");\n\t\t\t\t}\n\t\t\t\tc += (int)type->elementType->size();\n\t\t\t}\n\t\t\treturn arr;\n\t\t}\n\n\t\tStructValue getStructAt(int slot, Type type)\n\t\t{\n\t\t\tassert(type->kind == TypeKind::Struct);\n\t\t\tStructValue obj;\n\t\t\tobj.type = type;\n\t\t\tobj.fields.resize(type->fields.size());\n\t\t\tint c = slot;\n\t\t\tfor (int i = 0; i < type->fields.size(); ++i)\n\t\t\t{\n\t\t\t\tValue& field = obj.fields[i];\n\t\t\t\tswitch (type->fields[i].first->kind)\n\t\t\t\t{\n\t\t\t\tcase TypeKind::Bool  : field = getBoolAt  (c); break;\n\t\t\t\tcase TypeKind::Int   : field = getIntAt   (c); break;\n\t\t\t\tcase TypeKind::Double: field = getDoubleAt(c); break;\n\t\t\t\tcase TypeKind::Vec2  : field = getVec2At  (c); break;\n\t\t\t\tcase TypeKind::Vec3  : field = getVec3At  (c); break;\n\t\t\t\tcase TypeKind::Array : field = std::make_shared<ArrayValue >(getArrayAt (c, type->fields[i].first)); break;\n\t\t\t\tcase TypeKind::Struct: field = std::make_shared<StructValue>(getStructAt(c, type->fields[i].first)); break;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow std::runtime_error(\"Unsupported type in getStrucAt\");\n\t\t\t\t}\n\t\t\t\tc += (int)type->elementType->size();\n\t\t\t}\n\t\t\treturn obj;\n\t\t}\n\n\t\tvoid copy(size_t dest, size_t src, size_t size)\n\t\t{\n\t\t\tfor (size_t i = 0; i < size; ++i)\n\t\t\t\tm_stack[dest + i] = m_stack[src + i];\n\n\t\t\tif (dest + size > stackTop)\n\t\t\t\tstackTop = dest + size;\n\t\t}\n\n\t\tstd::string toString(const Value& v)\n\t\t{\n\t\t\tswitch (v.index)\n\t\t\t{\n\t\t\tcase ValueIndex::VOID  : return \"void\";\n\t\t\tcase ValueIndex::BOOL  : return getBool(v) ? \"true\" : \"false\";\n\t\t\tcase ValueIndex::INT   : return std::to_string(getInt(v));\n\t\t\tcase ValueIndex::DOUBLE: return std::to_string(getDouble(v));\n\t\t\t}\n\t\t\treturn \"\";\n\t\t}\n\n\tprivate:\n\t\tconst Program* m_program;\n\t\tsize_t globalStackSize = 0;\n\n\t\tstd::vector<double> m_stack;\n\t\tsize_t stackTop = 0;\n\t\tRef ref;\n\n\t\tCallFrame m_frames[MAX_CALL_DEPTH];\n\t\tsize_t frameCount = 0;\n\t\tconst uint8_t*  ip = nullptr; // current instruction pointer\n\n\t\tbool m_debug = false;\n\t};\n\n\tValue runScript(const std::string& script);\n}\n"
  },
  {
    "path": "infrastructure/Dockerfile",
    "content": "FROM febiosoftware/febiobase:ubuntu-22.04\nSHELL [\"/bin/bash\", \"-c\"]\nRUN pwd\n# Setup dependency installers\nARG IMAGE_BUILD_PATH=/tmp/linux\nARG DEPENDENCIES_PATH=${IMAGE_BUILD_PATH}/dependencies\nRUN mkdir -p ${IMAGE_BUILD_PATH}\n\nCOPY ./common/linux ${IMAGE_BUILD_PATH}\nARG IMAGE_BUILD_PATH=/tmp/linux\nARG BUILD_PATH=/tmp/src\nRUN mkdir -p ${BUILD_PATH}\nARG DEPENDENCIES_PATH=${IMAGE_BUILD_PATH}/dependencies\nWORKDIR ${DEPENDENCIES_PATH}\nRUN ${IMAGE_BUILD_PATH}/dependencies/install.sh\nRUN ${DEPENDENCIES_PATH}/hypre.sh\nRUN ${DEPENDENCIES_PATH}/levmar.sh\nRUN ${DEPENDENCIES_PATH}/mmg.sh\nRUN ${DEPENDENCIES_PATH}/tetgen.sh\nRUN ${DEPENDENCIES_PATH}/itk.sh\nRUN ${DEPENDENCIES_PATH}/sitk.sh\nRUN ${DEPENDENCIES_PATH}/occt.sh\nRUN ${DEPENDENCIES_PATH}/netgen.sh\nWORKDIR /FEBio\nCMD [\"sleep\", \"infinity\"]\n"
  },
  {
    "path": "infrastructure/DockerfileBase",
    "content": "FROM ubuntu:22.04\nSHELL [\"/bin/bash\", \"-c\"]\nRUN pwd\n# Setup dependency installers\nARG IMAGE_BUILD_PATH=/tmp/linux\nARG DEPENDENCIES_PATH=${IMAGE_BUILD_PATH}/dependencies\nRUN mkdir -p ${IMAGE_BUILD_PATH}\n\nCOPY ./common/linux ${IMAGE_BUILD_PATH}\nWORKDIR ${IMAGE_BUILD_PATH}\nRUN ./apt.sh\nRUN ./cmake.sh\nRUN ./git.sh\nRUN ./ffmpeg.sh\nRUN ./qt.sh\nRUN ./openapi.sh\nRUN ./aws.sh\n\nWORKDIR /FEBio\nCMD [\"bash\"]\n"
  },
  {
    "path": "infrastructure/DockerfileRuntime",
    "content": "FROM ubuntu:22.04\nSHELL [\"/bin/bash\", \"-c\"]\nRUN pwd\n\nRUN apt-get update\nRUN DEBIAN_FRONTEND=noninteractive apt-get install curl gpg sudo -y\nRUN curl -L https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null\nRUN echo \"deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main\" | sudo tee /etc/apt/sources.list.d/oneAPI.list\nRUN apt-get update && apt-get install intel-oneapi-runtime-libs -y\n\nCOPY cmbuild/bin/* /usr/local/bin/\nCOPY cmbuild/lib/* /usr/local/lib/\nENV PATH \"$PATH:/usr/local/bin:/usr/local/lib\"\nRUN chmod +x /usr/local/bin/febio4\n"
  },
  {
    "path": "infrastructure/ami-ubuntu.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    amazon = {\n      version = \">= 0.0.2\"\n      source  = \"github.com/hashicorp/amazon\"\n    }\n  }\n}\n\nlocals {\n  buildtime = formatdate(\"YYYYMMDDhhmm\", timestamp())\n}\n\ndata \"amazon-ami\" \"ubuntu\" {\n  filters = {\n    name             = \"ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server*\"\n    root-device-type = \"ebs\"\n  }\n\n  most_recent = true\n  owners      = [\"099720109477\"]\n  region      = \"us-east-1\"\n}\n\nvariable \"skip_create_ami\" {\n  type    = bool\n  default = false\n}\n\nsource \"amazon-ebs\" \"ubuntu\" {\n  ami_name      = \"packer-provisioned-ubuntu-22.04-intel-oneapi-${local.buildtime}\"\n  instance_type = \"c5a.4xlarge\"\n  source_ami    = data.amazon-ami.ubuntu.id\n  ssh_username  = \"ubuntu\"\n\n  skip_create_ami = var.skip_create_ami\n\n  iam_instance_profile = \"s3-read-access\"\n\n  aws_polling {\n    delay_seconds = 60\n    max_attempts  = 90\n  }\n\n  launch_block_device_mappings {\n    device_name           = \"/dev/sda1\"\n    volume_size           = 50\n    volume_type           = \"gp2\"\n    delete_on_termination = true\n  }\n}\nvariable \"image_build_path\" {\n  type    = string\n  default = \"/tmp\"\n}\n\nvariable \"dependencies_folder\" {\n  type    = string\n  default = \"/tmp/linux/dependencies/\"\n}\n\nbuild {\n  name = \"febio\"\n  sources = [\n    \"source.amazon-ebs.ubuntu\"\n  ]\n\n  provisioner \"file\" {\n    source      = \"./common/linux\"\n    destination = var.image_build_path\n  }\n\n  provisioner \"shell\" {\n    remote_folder = \"${var.image_build_path}/linux\"\n    script        = \"./common/linux/apt.sh\"\n  }\n\n  # awscli\n  provisioner \"shell\" {\n    script = \"./common/linux/aws.sh\"\n  }\n\n  provisioner \"shell\" {\n    remote_folder = \"${var.image_build_path}/linux\"\n    script        = \"./common/linux/install-builder.sh\"\n  }\n\n  provisioner \"shell\" {\n    script = \"./common/linux/qt.sh\"\n  }\n\n  provisioner \"shell\" {\n    script = \"./common/linux/openapi.sh\"\n  }\n\n  # Latest version of cmake (v3.23.2)\n  provisioner \"shell\" {\n    script = \"./common/linux/cmake.sh\"\n  }\n\n  # Latest version of git (v2.38.1)\n  provisioner \"shell\" {\n    script = \"./common/linux/git.sh\"\n  }\n\n  # awscli\n  provisioner \"shell\" {\n    script = \"./common/linux/aws.sh\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\n      \"DEPENDENCIES_PATH=${var.dependencies_folder}\"\n    ]\n    script = \"./common/linux/dependencies/install.sh\"\n  }\n}\n"
  },
  {
    "path": "infrastructure/ami-windows-studio.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    amazon = {\n      version = \">= 0.0.2\"\n      source  = \"github.com/hashicorp/amazon\"\n    }\n  }\n}\n\nlocals {\n  buildtime          = formatdate(\"YYYYMMDDhhmm\", timestamp())\n  installation_path  = var.installation_path\n  src_path           = \"${local.installation_path}\\\\src\"\n  vcpkg_package_path = \"${local.installation_path}\\\\febio\"\n  qt_path = \"${local.installation_path}\\\\Qt\"\n  environment = {\n    \"INSTALLATION_PATH\"  = local.installation_path\n    \"SOURCE_PATH\"        = local.src_path\n    \"VCPKG_PACKAGE_PATH\" = local.vcpkg_package_path\n    \"QT_INSTALL_DIR\" = local.qt_path\n  }\n}\n\nvariable \"installation_path\" {\n  default = \"c:\\\\usr\\\\local\"\n}\n\ndata \"amazon-ami\" \"windows\" {\n  filters = {\n    name             = \"packer-provisioned-windows-2019-intel-oneapi-*\"\n    root-device-type = \"ebs\"\n  }\n\n  most_recent = true\n  owners      = [\"353328028284\"]\n  region      = \"us-east-1\"\n}\n\ndata \"amazon-parameterstore\" \"winrm_password\" {\n  name            = \"/packer/winrm_password\"\n  with_decryption = true\n}\n\ndata \"amazon-parameterstore\" \"winrm_username\" {\n  name            = \"/packer/winrm_username\"\n  with_decryption = true\n}\n\nvariable \"skip_create_ami\" {\n  type    = bool\n  default = false\n}\n\nsource \"amazon-ebs\" \"windows\" {\n  ami_name      = \"packer-provisioned-windows-2019-febio-studio-${local.buildtime}\"\n  instance_type = \"c7i.8xlarge\"\n  source_ami    = data.amazon-ami.windows.id\n\n  communicator = \"winrm\"\n\n  skip_create_ami = var.skip_create_ami\n\n  iam_instance_profile = \"s3-read-access\"\n\n  aws_polling {\n    delay_seconds = 60\n    max_attempts  = 90\n  }\n\n\n  user_data_file = \"./common/windows/user-data.txt\"\n  winrm_password = data.amazon-parameterstore.winrm_password.value\n  winrm_username = data.amazon-parameterstore.winrm_username.value\n\n  launch_block_device_mappings {\n    device_name           = \"/dev/sda1\"\n    volume_size           = 150\n    volume_type           = \"gp3\"\n    delete_on_termination = true\n  }\n}\n\nbuild {\n  name = \"febiostudio\"\n  sources = [\n    \"source.amazon-ebs.windows\"\n  ]\n\n  # Conditionally maximize partition\n  provisioner \"powershell\" {\n    script = \"./common/windows/resize-partition.ps1\"\n    env    = local.environment\n  }\n\n  # paths\n  provisioner \"powershell\" {\n    script = \"./common/windows/pathprep.ps1\"\n    env    = local.environment\n  }\n\n  provisioner \"powershell\" {\n    script = \"./common/windows/jq.ps1\"\n    env    = local.environment\n  }\n\n  # cmake\n  provisioner \"powershell\" {\n    script = \"./common/windows/cmake.ps1\"\n    env    = local.environment\n  }\n\n#   provisioner \"powershell\" {\n#     script = \"./common/windows/msmpi.ps1\"\n#     env    = local.environment\n#   }\n\n  provisioner \"powershell\" {\n    script = \"./common/windows/aws.ps1\"\n    env    = local.environment\n  }\n\n  # vcpkg\n  provisioner \"powershell\" {\n    script = \"./common/windows/vcpkg-installer.ps1\"\n    env    = local.environment\n  }\n\n  #vcpkg packages\n  provisioner \"file\" {\n    source      = \"./common/windows/vcpkg.json\"\n    destination = \"${local.vcpkg_package_path}\\\\\"\n  }\n\n  provisioner \"powershell\" {\n    script = \"./common/windows/vcpkg-package-install.ps1\"\n    env    = local.environment\n  }\n\n  # Lua 5.3\n  provisioner \"powershell\" {\n    script = \"./common/windows/lua.ps1\"\n    env    = local.environment\n  }\n\n  # itk\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/itk.bat\"\n    env    = local.environment\n  }\n\n  # sitk\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/sitk.bat\"\n    env    = local.environment\n  }\n\n  # qt\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/qt.bat\"\n    env    = local.environment\n  }\n\n  provisioner \"powershell\" {\n    script = \"./common/windows/install-builder.ps1\"\n    env    = local.environment\n  }\n\n  provisioner \"powershell\" {\n    script = \"./common/windows/ffmpeg.ps1\"\n    env    = local.environment\n  }\n\n  # LEVMAR\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/levmar.bat\"\n    env    = local.environment\n  }\n\n  # NLOPT\n   provisioner \"windows-shell\" {\n    script = \"./common/windows/nlopt.bat\"\n    env    = local.environment\n  }\n\n  # HYPRE\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/hypre.bat\"\n    env    = local.environment\n  }\n\n  # mmg\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/mmg.bat\"\n    env    = local.environment\n  }\n\n  # tetgen\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/tetgen.bat\"\n    env    = local.environment\n  }\n\n  # occt\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/occt.bat\"\n    env    = local.environment\n  }\n\n  # netgen\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/netgen.bat\"\n    env    = local.environment\n  }\n\n  # libzip\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/libzip.bat\"\n    env    = local.environment\n  }\n\n   # python\n  provisioner \"windows-shell\" {\n    script = \"./common/windows/python.bat\"\n    env    = local.environment\n  }\n\n  # sysprep for next launch\n  provisioner \"powershell\" {\n    inline = [\n      \"C:\\\\ProgramData\\\\Amazon\\\\EC2-Windows\\\\Launch\\\\Scripts\\\\InitializeInstance.ps1 -Schedule\",\n    ]\n  }\n}\n"
  },
  {
    "path": "infrastructure/ami-windows.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    amazon = {\n      version = \">= 0.0.2\"\n      source  = \"github.com/hashicorp/amazon\"\n    }\n  }\n}\n\nlocals {\n  buildtime              = formatdate(\"YYYYMMDDhhmm\", timestamp())\n  intel_basekit          = \"w_BaseKit_p_2022.2.0.252_offline.exe\"\n  intel_basekit_uri      = \"https://registrationcenter-download.intel.com/akdlm/IRC_NAS/18674/${local.intel_basekit}\"\n  intel_install_windows  = \"https://raw.githubusercontent.com/oneapi-src/oneapi-ci/master/scripts/install_windows.bat\"\n  vs_2022_buildtools_bin = \"vs_buildtools.exe\"\n  vs_2022_buildtools_uri = \"https://aka.ms/vs/17/release/${local.vs_2022_buildtools_bin}\"\n  installation_path      = var.installation_path\n}\n\nvariable \"installation_path\" {\n  default = \"c:\\\\usr\\\\local\"\n}\n\ndata \"amazon-parameterstore\" \"winrm_password\" {\n  name            = \"/packer/winrm_password\"\n  with_decryption = true\n}\n\ndata \"amazon-parameterstore\" \"winrm_username\" {\n  name            = \"/packer/winrm_username\"\n  with_decryption = true\n}\n\n\ndata \"amazon-ami\" \"windows\" {\n  filters = {\n    name             = \"Windows_Server-2019-English-Full-Base-*\"\n    root-device-type = \"ebs\"\n  }\n\n  most_recent = true\n  owners      = [\"801119661308\"]\n  region      = \"us-east-1\"\n}\n\nvariable \"skip_create_ami\" {\n  type    = bool\n  default = false\n}\n\nsource \"amazon-ebs\" \"windows\" {\n  ami_name      = \"packer-provisioned-windows-2019-intel-oneapi-${local.buildtime}\"\n  instance_type = \"c7i.8xlarge\"\n  source_ami    = data.amazon-ami.windows.id\n  communicator  = \"winrm\"\n\n  skip_create_ami = var.skip_create_ami\n\n  aws_polling {\n    delay_seconds = 60\n    max_attempts  = 90\n  }\n\n  user_data_file = \"./common/windows/user-data.txt\"\n  winrm_password = data.amazon-parameterstore.winrm_password.value\n  winrm_username = data.amazon-parameterstore.winrm_username.value\n\n  launch_block_device_mappings {\n    device_name           = \"/dev/sda1\"\n    volume_size           = 100\n    volume_type           = \"gp3\"\n    delete_on_termination = true\n  }\n}\n\nbuild {\n  name = \"febio\"\n  sources = [\n    \"source.amazon-ebs.windows\"\n  ]\n\n  # Install Choco\n  provisioner \"powershell\" {\n    script = \"./common/windows/choco.ps1\"\n  }\n\n  # VS Build tools\n  provisioner \"windows-shell\" {\n    inline = [\n      \"curl -L -O ${local.vs_2022_buildtools_uri}\",\n      \"start /wait ${local.vs_2022_buildtools_bin} --add Microsoft.VisualStudio.Workload.VCTools --includeOptional --includeRecommended --quiet --nocache --wait\",\n      \"del ${local.vs_2022_buildtools_bin}\",\n    ]\n  }\n\n  # Intel basekit\n  provisioner \"windows-shell\" {\n    inline = [\n      \"curl -L -O ${local.intel_install_windows}\",\n      \"install_windows.bat ${local.intel_basekit_uri}\"\n    ]\n  }\n}\n"
  },
  {
    "path": "infrastructure/common/linux/apt.sh",
    "content": "#!/bin/bash\nset -e\nexport DEBIAN_FRONTEND=noninteractive\nSUDO=\"\"\nif command -v sudo &> /dev/null\nthen\n\tSUDO=$(which sudo)\nfi\n\n$SUDO apt-get update\n$SUDO apt-get install linux-headers-generic -y\n$SUDO apt-get install software-properties-common wget gpg sudo -y\n$SUDO apt-get update --fix-missing\n\n\nwget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null\necho \"deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main\" | sudo tee /etc/apt/sources.list.d/oneAPI.list\n$SUDO apt-get update\n\nDEBIAN_FRONTEND=noninteractive $SUDO apt-get install lua5.3 -y\n\nxargs $SUDO apt-get install </tmp/linux/packages.txt -y\n"
  },
  {
    "path": "infrastructure/common/linux/aws.sh",
    "content": "#!/bin/bash\n# set -e\nexport DEBIAN_FRONTEND=noninteractive\npushd /tmp\nsudo curl \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -o \"awscliv2.zip\"\nsudo unzip awscliv2.zip\nsudo ./aws/install\naws --version\npopd\n"
  },
  {
    "path": "infrastructure/common/linux/cmake.sh",
    "content": "#!/bin/bash\nset -e\nexport DEBIAN_FRONTEND=noninteractive\n\nsudo apt update\nsudo apt install libssl3 cmake cmake-curses-gui -y\ncmake --version\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/common-functions.sh",
    "content": "#! /bin/bash\n\ndownload_source() {\n\tlocal source=$1\n\n\tcurl -L -O \"$source\"\n}\n\nextract_source() {\n\tlocal archive=$1\n\tunzip -o \"$archive\"\n}\n\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/ffmpeg.sh",
    "content": "#!/bin/bash\nset -e\n\nREPO=\"https://github.com/FFmpeg/FFmpeg.git\"\nBRANCH=\"n6.1\"\n\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\n  pushd $BRANCH\n\n  ./configure \\\n    --disable-everything \\\n    --disable-programs \\\n    --disable-doc \\\n    --disable-static \\\n    --disable-debug \\\n    --enable-shared \\\n    --enable-encoder=mpeg1video \\\n    --enable-muxer=mpeg1video \\\n    --prefix=\"/usr/local\"\n\n  sudo make -j $(nproc)\n  sudo make install\n  popd\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$REPO\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/hypre.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nHYPRE_SOURCE=\"https://github.com/hypre-space/hypre/archive/refs/tags/v2.23.0.zip\"\nHYPRE_ARCHIVE=$(basename $HYPRE_SOURCE)\nHYPRE_PATH=\"hypre-2.23.0\"\n\nbuild_and_install() {\n\tlocal source=$1\n\tpushd \"$source\" || exit 1\n\tpushd src || exit 1\n\tcmake .  -LA -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DHYPRE_HAVE_MPI=Off -DHYPRE_WITH_MPI=Off \\\n\t\t-DCMAKE_POSITION_INDEPENDENT_CODE=On\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tdownload_source \"$HYPRE_SOURCE\"\n\textract_source \"$HYPRE_ARCHIVE\"\n\tbuild_and_install \"$HYPRE_PATH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/install.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\nexport BUILD_PATH=/tmp/src\nmkdir -p $BUILD_PATH\n\nSETVARS=\"${SETVARS:-/opt/intel/oneapi/setvars.sh}\"\n. $SETVARS\n\nmain() {\n\tlocal dir=$1\n\tpushd $dir\n\n\tlocal installers=(hypre levmar mmg tetgen itk sitk occt netgen libzip ffmpeg python nlopt)\n\tfor installer in ${installers[@]}; do\n\t\t./${installer}.sh\n\tdone\n\n\tpopd\n}\n\nmain $DEPENDENCIES_PATH\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/itk.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nITK=\"https://github.com/InsightSoftwareConsortium/ITK.git\"\nBRANCH=\"v5.2.1\"\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake . -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DBUILD_EXAMPLES:BOOL=OFF \\\n\t\t-DBUILD_SHARED_LIBS:BOOL=OFF \\\n\t\t-DBUILD_TESTING:BOOL=OFF \\\n\t\t-DITK_WRAP_PYTHON:BOOL=OFF \\\n\t\t-DITK_DOXYGEN_HTML:BOOL=OFF \\\n\t\t-DCMAKE_BUILD_TYPE=Release\n\n\tpushd cmbuild\n\tmake -j \"$(nproc --ignore 2)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$ITK\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/levmar.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\nREPO=https://github.com/jturney/levmar.git\nDIR=levmar\n\nbuild_and_install() {\n\tlocal repo=$1\n\tlocal dir=$2\n\tgit clone $repo $dir\n\tpushd $dir\n\tcmake  . -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DCMAKE_POSITION_INDEPENDENT_CODE=ON \\\n\t\t-DBUILD_DEMO:BOOLEAN=false\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd $BUILD_PATH\n\tbuild_and_install $REPO $DIR\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/libzip.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nSOURCE=\"https://github.com/nih-at/libzip.git\"\nBRANCH=\"v1.10.1\"\n\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake .  -LA -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DBUILD_DOC=OFF \\\n\t\t-DBUILD_EXAMPLES=OFF \\\n\t\t-DBUILD_OSSFUZZ=OFF \\\n\t\t-DBUILD_REGRESS=OFF \\\n\t\t-DBUILD_TOOLS=OFF \\\n\t\t-DBUILD_SHARED_LIBS=ON \\\n\t\t-DENABLE_BZIP2=OFF \\\n\t\t-DENABLE_COMMONCRYPTO=OFF \\\n\t\t-DENABLE_FDOPEN=OFF \\\n\t\t-DENABLE_GNUTILS=OFF \\\n\t\t-DENABLE_LZMA=OFF \\\n\t\t-DENABLE_MBEDTLS=OFF \\\n\t\t-DENABLE_OPENSSL=OFF \\\n\t\t-DENABLE_WINDOWS_CRYPTO=OFF \\\n\t\t-DLIBZIP_DO_INSTALL=ON \\\n\t\t-DSHARED_LIB_VERSIONNING=ON\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$SOURCE\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/mmg.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nMMG=\"https://github.com/MmgTools/mmg.git\"\nBRANCH=\"v5.7.3\"\n\nbuild_and_install() {\n    local source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake . -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DCMAKE_POSITION_INDEPENDENT_CODE=ON\n\tpushd cmbuild || exit 1\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$MMG\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/netgen.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\nNETGEN=\"https://github.com/NGSolve/netgen.git\"\nBRANCH=\"v6.2.2501\"\n\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tgit submodule update --init --recursive\n\tcmake .  -LA -B cmbuild \\\n\t\t-DCMAKE_BUILD_TYPE=Release \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DUSE_CCACHE:BOOL=OFF \\\n\t\t-DUSE_CGNS:BOOL=OFF \\\n\t\t-DUSE_CSG:BOOL=ON \\\n\t\t-DUSE_GEOM2D:BOOL=ON \\\n\t\t-DUSE_GUI:BOOL=OFF \\\n\t\t-DUSE_INTERFACE:BOOL=ON \\\n\t\t-DUSE_INTERNAL_TCL:BOOL=OFF \\\n\t\t-DUSE_JPEG:BOOL=OFF \\\n\t\t-DUSE_MPEG:BOOL=OFF \\\n\t\t-DUSE_MPI:BOOL=OFF \\\n\t\t-DUSE_MPI4PY:BOOL=OFF \\\n\t\t-DUSE_NATIVE_ARCH:BOOL=OFF \\\n\t\t-DUSE_NUMA:BOOL=OFF \\\n\t\t-DUSE_OCC:BOOL=ON \\\n\t\t-DUSE_PYTHON:BOOL=OFF \\\n\t\t-DUSE_STLGEOM:BOOL=ON \\\n\t\t-DUSE_SUPERBUILD:BOOL=OFF \\\n\t\t-DENABLE_CPP_CORE_GUIDELINES_CHECK:BOOL=OFF \\\n\t\t-DENABLE_UNIT_TESTS:BOOL=OFF\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$NETGEN\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/nlopt.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\nNLOPT=\"https://github.com/stevengj/nlopt.git\"\nBRANCH=\"v2.10.1\"\n\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake .  -LA -B cmbuild \\\n\t\t-DBUILD_SHARED_LIBS=OFF \\\n\t\t-DCMAKE_BUILD_TYPE=Release \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DNLOPT_GUILE=OFF \\\n\t\t-DNLOPT_JAVA=OFF \\\n\t\t-DNLOPT_MATLAB=OFF \\\n\t\t-DNLOPT_OCTAVE=OFF \\\n\t\t-DNLOPT_PYTHON=OFF \\\n\t\t-DNLOPT_PYTHON_SABI=OFF \\\n\t\t-DNLOPT_SWIG=OFF \\\n\t\t-DNLOPT_TESTS=OFF \n\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$NLOPT\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/occt.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nOCCT=\"https://github.com/Open-Cascade-SAS/OCCT.git\"\nBRANCH=\"V7_7_2\"\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake . -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DBUILD_DOC_Overview:BOOL=OFF \\\n\t\t-DBUILD_ENABLE_FPE_SIGNAL_HANDLER:BOOL=OFF \\\n\t\t-DBUILD_Inspector:BOOL=OFF \\\n\t\t-DBUILD_LIBRARY_TYPE:STRING=Shared \\\n\t\t-DBUILD_MODULE_ApplicationFramework:BOOL=OFF \\\n\t\t-DBUILD_MODULE_DETools:BOOL=OFF \\\n\t\t-DBUILD_MODULE_DataExchange:BOOL=ON \\\n\t\t-DBUILD_MODULE_Draw:BOOL=OFF \\\n\t\t-DBUILD_MODULE_FoundationClasses:BOOL=TRUE \\\n\t\t-DBUILD_MODULE_ModelingAlgorithms:BOOL=TRUE \\\n\t\t-DBUILD_MODULE_ModelingData:BOOL=OFF \\\n\t\t-DBUILD_MODULE_Visualization:BOOL=OFF \\\n\t\t-DBUILD_OPT_PROFILE:STRING=Default \\\n\t\t-DBUILD_RELEASE_DISABLE_EXCEPTIONS:BOOL=OFF \\\n\t\t-DBUILD_RESOURCES:BOOL=OFF \\\n\t\t-DBUILD_SAMPLES_QT:BOOL=OFF \\\n\t\t-DBUILD_USE_PCH:BOOL=OFF \\\n\t\t-DUSE_DRACO:BOOL=OFF \\\n\t\t-DUSE_FFMPEG:BOOL=OFF \\\n\t\t-DUSE_FREEIMAGE:BOOL=OFF \\\n\t\t-DUSE_FREETYPE:BOOL=OFF \\\n\t\t-DUSE_GLES2:BOOL=OFF \\\n\t\t-DUSE_OPENGL:BOOL=OFF \\\n\t\t-DUSE_OPENVR:BOOL=OFF \\\n\t\t-DUSE_RAPIDJSON:BOOL=OFF \\\n\t\t-DUSE_TBB:BOOL=OFF \\\n\t\t-DUSE_TK:BOOL=OFF \\\n\t\t-DUSE_VTK:BOOL=OFF \\\n\t\t-DUSE_XLIB:BOOL=OFF\n\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$OCCT\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/python.sh",
    "content": "curl https://pyenv.run | bash\n\nexport PYENV_ROOT=\"$HOME/.pyenv\"\nexport PATH=\"$PYENV_ROOT/bin:$PATH\"\n\neval \"$(pyenv init -)\"\n\npyenv install 3.13.1\npyenv global 3.13.1\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/quazip.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nQUAZIP=\"https://github.com/stachenov/quazip.git\"\nBRANCH=\"v1.4\"\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake .  -LA -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\"\n\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$QUAZIP\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/sitk.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nSITK=\"https://github.com/SimpleITK/SimpleITK.git\"\nBRANCH=\"v2.1.1.2\"\nbuild_and_install() {\n\tlocal source=$1\n\tlocal branch=$2\n\n\tgit clone --depth 1 --branch \"$branch\" \"$source\" \"$branch\"\n\tpushd $branch || exit 1\n\tcmake .  -LA -B cmbuild \\\n\t\t-DCMAKE_INSTALL_PREFIX=\"/usr/local\" \\\n\t\t-DWRAP_DEFAULT:BOOL=OFF \\\n\t\t-DBUILD_EXAMPLES:BOOL=OFF \\\n\t\t-DBUILD_TESTING:BOOL=OFF \\\n\t\t-DBUILD_SHARED_LIBS:BOOL=OFF \\\n\t\t-DCMAKE_BUILD_TYPE=Release\n\n\tpushd cmbuild\n\tmake -j \"$(nproc --ignore 2)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$SITK\" \"$BRANCH\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/dependencies/tetgen.sh",
    "content": "#! /bin/bash\nset -o errexit\nset -o verbose\n\n# shellcheck disable=1091\n. ./common-functions.sh\n\nTETGEN_GIT=\"https://github.com/ufz/tetgen.git\"\nTETGEN_FILENAME=$(basename $TETGEN_GIT)\nTETGEN_SOURCE_DIR=\"${TETGEN_FILENAME%.*}\"\n\nbuild_and_install() {\n\tlocal git_remote=$1\n\tlocal src_dir=$2\n\n\tgit clone $git_remote $src_dir\n\tpushd \"${src_dir}\" || exit 1\n\tcmake .  -LA -B cmbuild -DCMAKE_INSTALL_PREFIX=\"/usr/local\" -DCMAKE_BUILD_TYPE=\"Release\" -DCMAKE_CXX_FLAGS=\"-fPIC\"\n\tpushd cmbuild\n\tmake -j \"$(nproc)\"\n\tsudo make install\n\tpopd || exit 1\n\tpopd || exit 1\n}\n\nmain() {\n\tpushd \"$BUILD_PATH\" || exit 1\n\tbuild_and_install \"$TETGEN_GIT\" \"$TETGEN_SOURCE_DIR\"\n\tpopd || exit 1\n}\n\nmain\n"
  },
  {
    "path": "infrastructure/common/linux/git.sh",
    "content": "#!/bin/bash\nset -e\nexport DEBIAN_FRONTEND=noninteractive\n\nif command -v git &> /dev/null\nthen\n\tgit --version\nfi\nsudo add-apt-repository ppa:git-core/ppa -y\nsudo apt-get update\nsudo apt-get install git -y\ngit --version\n"
  },
  {
    "path": "infrastructure/common/linux/install-builder.sh",
    "content": "#!/bin/bash\nset -ex\nexport DEBIAN_FRONTEND=noninteractive\npushd /tmp\nINSTALLER=\"installbuilder-enterprise-23.11.0-linux-x64-installer.run\"\naws s3 cp \"s3://febiosoftware/linux/installbuilder/${INSTALLER}\" .\nchmod +x ./$INSTALLER\n\nsudo ./$INSTALLER \\\n\t--mode unattended \\\n\t--installer-language en\n\nsudo ln -s /opt/installbuilder-23.11.0/bin/builder /usr/local/bin/\nbuilder --version\n"
  },
  {
    "path": "infrastructure/common/linux/openapi.sh",
    "content": "#!/bin/bash\nset -e\nexport DEBIAN_FRONTEND=noninteractive\nSUDO=\"\"\nif command -v sudo &> /dev/null\nthen\n\tSUDO=$(which sudo)\nfi\n\nINSTALLER=\"intel-oneapi-base-toolkit-2025.0.1.46_offline.sh\"\naws s3 cp \"s3://febiosoftware/linux/oneapi/${INSTALLER}\" .\nchmod +x ./$INSTALLER\n$SUDO ./intel-oneapi-base-toolkit-2025.0.1.46_offline.sh -a --cli --silent --eula accept --action install --components intel.oneapi.lin.mkl.devel\nrm ./$INSTALLER"
  },
  {
    "path": "infrastructure/common/linux/packages.txt",
    "content": "apt-utils\ncurl\nbuild-essential\ndos2unix\nf2c\nlibf2c2-dev\nlibomp-dev\nlibomp5\nliblapack-dev\nliblapack3\nlibopenmpi-dev\nlibopenmpi3\npkg-config\nunzip\nzlib1g-dev\nlibgtk-3-dev\nlibglew-dev\nmesa-common-dev\nfreeglut3-dev\nlibssh-dev\nlibsqlite3-dev\ntcl-dev\ntk-dev\nlibx11-dev\nlibxcb-cursor0\nlibxmu-dev\nlibinsighttoolkit5-dev\nswig\nlibxorg-gtest-dev\njq\nyasm\npatchelf\nzip\npython3-pip\nlibfftw3-dev\nlibvulkan-dev\nvulkan-tools\nbuild-essential\nlibssl-dev\nzlib1g-dev\nlibbz2-dev\nlibreadline-dev\nlibffi-dev\nliblzma-dev\nuuid-dev"
  },
  {
    "path": "infrastructure/common/linux/qt.sh",
    "content": "#!/bin/bash\nset -e\nexport DEBIAN_FRONTEND=noninteractive\nSUDO=\"\"\nif command -v sudo &> /dev/null\nthen\n\tSUDO=$(which sudo)\nfi\n\n$SUDO pip install aqtinstall -v\n$SUDO aqt install-qt --outputdir /opt/Qt linux desktop 6.9.3 -m all\n"
  },
  {
    "path": "infrastructure/common/macos/ffmpeg.sh",
    "content": "#!/bin/zsh\nset -e\n\nREPO=\"https://github.com/FFmpeg/FFmpeg.git\"\nBRANCH=\"n6.1\"\n\npushd $SOURCE_PATH\nrm -rf \"${SOURCE_PATH}/${BRANCH}\"\n\nexport MACOSX_DEPLOYMENT_TARGET=10.15\nexport MACOSX_DEPLOYMENT_ARCHITECTURES=x86_64\n\ngit clone --depth 1 --branch $BRANCH $REPO $BRANCH\n\npushd $BRANCH\n\narch -x86_64 ./configure \\\n  --disable-everything \\\n  --disable-programs \\\n  --disable-doc \\\n  --disable-static \\\n  --disable-debug \\\n  --enable-shared \\\n  --enable-encoder=mpeg1video \\\n  --enable-muxer=mpeg1video \\\n  --prefix=\"$INSTALLATION_PATH\"\n\narch -x86_64 make -j $(sysctl -n hw.ncpu)\narch -x86_64 make install\n\npopd\npopd\nrm -rf $BRANCH\n"
  },
  {
    "path": "infrastructure/common/macos/homebrew-packages.sh",
    "content": "#!/bin/zsh\nset -ex\n\npackages=(jq awscli eigen glew libssh libssh2 yasm zstd pcre2 harfbuzz freetype pkg-config jpeg-turbo)\n\narch -x86_64 $HOMEBREW_BIN  install \"${packages[@]}\"\n"
  },
  {
    "path": "infrastructure/common/macos/homebrew-x86.sh",
    "content": "#!/bin/bash\nset -ex\n\nif [ -f \"${HOMEBREW_BIN}\" ]; then\n\techo \"Homebrew already installed at: ${HOMEBREW_PREFIX}\"\n\texit\nfi\n\ngit clone https://github.com/Homebrew/brew.git $HOMEBREW_PREFIX\n\ncat << EOF >> /Users/$SSH_USER/.zprofile\nexport HOMEBREW_PREFIX=\"${HOMEBREW_PREFIX}\"\nexport PATH=\"\\$PATH:${HOMEBREW_PREFIX}:${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/Cellar:${HOMEBREW_PREFIX}/opt\"\nEOF\n"
  },
  {
    "path": "infrastructure/common/macos/hypre.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone https://github.com/hypre-space/hypre.git\npushd hypre/src\ncmake .  -L -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n\t-DHYPRE_HAVE_MPI=Off \\\n\t-DHYPRE_WITH_MPI=Off \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf hypre\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/installation_prep.sh",
    "content": "#!/bin/zsh\nset -ex\n\nif [ -d \"${INSTALLATION_PATH}\" ]; then\n  echo \"Paths already configured\"\n  exit\nfi\n\nmkdir -p \"${SOURCE_PATH}\"\nmkdir -p \"${INSTALLATION_PATH}\"\nchown -R \"${SSH_USER}\" \"${INSTALLATION_PATH}\"\n\ncat << EOF >> /Users/$SSH_USER/.zprofile\nexport PATH=\"\\$PATH:${INSTALLATION_PATH}\"\nEOF\n"
  },
  {
    "path": "infrastructure/common/macos/itk.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone --depth 1 --branch \"v5.2.1\" \"https://github.com/InsightSoftwareConsortium/ITK.git\"\npushd ITK\ncmake .  -L -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n  -DBUILD_EXAMPLES:BOOL=OFF \\\n  -DBUILD_SHARED_LIBS:BOOL=OFF \\\n  -DBUILD_TESTING:BOOL=OFF \\\n  -DITK_USE_SYSTEM_EIGEN:BOOL=ON \\\n  -DITK_WRAP_PYTHON:BOOL=OFF \\\n  -DITK_DOXYGEN_HTML:BOOL=OFF \\\n  -DCMAKE_BUILD_TYPE=Release \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf ITK\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/levmar.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone https://github.com/jturney/levmar.git\npushd levmar\ncmake . -LA -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n\t-DCMAKE_POSITION_INDEPENDENT_CODE=ON \\\n\t-DBUILD_DEMO:BOOLEAN=false \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf levmar\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/libzip.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\nrepo=\"https://github.com/nih-at/libzip.git\"\nbranch=\"v1.10.1\"\n\ngit clone --depth 1 --branch \"$branch\" \"$repo\" \"$branch\"\npushd $branch\ncmake .  -LA -B cmbuild \\\n  -DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n  -DBUILD_DOC=OFF \\\n  -DBUILD_EXAMPLES=OFF \\\n  -DBUILD_OSSFUZZ=OFF \\\n  -DBUILD_REGRESS=OFF \\\n  -DBUILD_TOOLS=OFF \\\n  -DBUILD_SHARED_LIBS=ON \\\n  -DENABLE_BZIP2=OFF \\\n  -DENABLE_COMMONCRYPTO=OFF \\\n  -DENABLE_FDOPEN=OFF \\\n  -DENABLE_GNUTILS=OFF \\\n  -DENABLE_LZMA=OFF \\\n  -DENABLE_MBEDTLS=OFF \\\n  -DENABLE_OPENSSL=OFF \\\n  -DENABLE_WINDOWS_CRYPTO=OFF \\\n  -DLIBZIP_DO_INSTALL=ON \\\n  -DSHARED_LIB_VERSIONNING=ON \\\n  -DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n  -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf $branch\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/lua.sh",
    "content": "#!/bin/zsh\nset -ex\n\narch -x86_64 $HOMEBREW_BIN install lua@5.3\n\ncat << EOF >> /Users/$SSH_USER/.zprofile\nexport PATH=\"${HOMEBREW_PREFIX}/opt/lua@5.3/bin:\\$PATH\"\nEOF\n"
  },
  {
    "path": "infrastructure/common/macos/mmg.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone https://github.com/MmgTools/mmg.git\npushd mmg\ngit checkout v5.7.3\ncmake .  -L -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"/Users/gitRunner/local/x86_64\" \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf mmg\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/netgen.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone --depth 1 --branch \"v6.2.2501\" \"https://github.com/NGSolve/netgen.git\"\npushd netgen\n cmake .  -L -B cmbuild \\\n  -DCMAKE_BUILD_TYPE=Release \\\n  -DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n  -DUSE_CCACHE:BOOL=OFF \\\n  -DUSE_CGNS:BOOL=OFF \\\n  -DUSE_CSG:BOOL=ON \\\n  -DUSE_GEOM2D:BOOL=ON \\\n  -DUSE_GUI:BOOL=OFF \\\n  -DUSE_INTERFACE:BOOL=ON \\\n  -DUSE_INTERNAL_TCL:BOOL=OFF \\\n  -DUSE_JPEG:BOOL=OFF \\\n  -DUSE_MPEG:BOOL=OFF \\\n  -DUSE_MPI:BOOL=OFF \\\n  -DUSE_MPI4PY:BOOL=OFF \\\n  -DUSE_NATIVE_ARCH:BOOL=OFF \\\n  -DUSE_NUMA:BOOL=OFF \\\n  -DUSE_OCC:BOOL=ON \\\n  -DUSE_PYTHON:BOOL=OFF \\\n  -DUSE_STLGEOM:BOOL=ON \\\n  -DUSE_SUPERBUILD:BOOL=OFF \\\n  -DENABLE_CPP_CORE_GUIDELINES_CHECK:BOOL=OFF \\\n  -DENABLE_UNIT_TESTS:BOOL=OFF \\\n  -DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n  -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake -j $(sysctl -n hw.ncpu)\nmake install\npopd\npopd\nrm -rf netgen\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/occt.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone --depth 1 --branch \"V7_7_2\" \"https://github.com/Open-Cascade-SAS/OCCT.git\"\npushd OCCT\n cmake .  -L -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n  -DBUILD_DOC_Overview:BOOL=OFF \\\n  -DBUILD_ENABLE_FPE_SIGNAL_HANDLER:BOOL=OFF \\\n  -DBUILD_Inspector:BOOL=OFF \\\n  -DBUILD_LIBRARY_TYPE:STRING=Shared \\\n  -DBUILD_MODULE_ApplicationFramework:BOOL=OFF \\\n  -DBUILD_MODULE_DETools:BOOL=OFF \\\n  -DBUILD_MODULE_DataExchange:BOOL=ON \\\n  -DBUILD_MODULE_Draw:BOOL=OFF \\\n  -DBUILD_MODULE_FoundationClasses:BOOL=TRUE \\\n  -DBUILD_MODULE_ModelingAlgorithms:BOOL=TRUE \\\n  -DBUILD_MODULE_ModelingData:BOOL=OFF \\\n  -DBUILD_MODULE_Visualization:BOOL=OFF \\\n  -DBUILD_OPT_PROFILE:STRING=Default \\\n  -DBUILD_RELEASE_DISABLE_EXCEPTIONS:BOOL=OFF \\\n  -DBUILD_RESOURCES:BOOL=OFF \\\n  -DBUILD_SAMPLES_QT:BOOL=OFF \\\n  -DBUILD_USE_PCH:BOOL=OFF \\\n  -DUSE_DRACO:BOOL=OFF \\\n  -DUSE_FFMPEG:BOOL=OFF \\\n  -DUSE_FREEIMAGE:BOOL=OFF \\\n  -DUSE_FREETYPE:BOOL=OFF \\\n  -DUSE_GLES2:BOOL=OFF \\\n  -DUSE_OPENGL:BOOL=OFF \\\n  -DUSE_OPENVR:BOOL=OFF \\\n  -DUSE_RAPIDJSON:BOOL=OFF \\\n  -DUSE_TBB:BOOL=OFF \\\n  -DUSE_TK:BOOL=OFF \\\n  -DUSE_VTK:BOOL=OFF \\\n  -DUSE_XLIB:BOOL=OFF \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf OCCT\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/openapi.sh",
    "content": "#!/bin/bash\nset -e\n\nif [ -f /opt/intel/oneapi/setvars.sh ]; then\n\techo \"Oneapi was previously installed\"\n\texit 0\nfi\n\nTARGET_DIR=\"/tmp/intel\"\nONEAPI_FILE=\"m_BaseKit_p_2023.2.0.49398_offline\"\nONEAPI_DMG=\"$ONEAPI_FILE.dmg\"\nONEAPI_URI=\"https://registrationcenter-download.intel.com/akdlm/IRC_NAS/cd013e6c-49c4-488b-8b86-25df6693a9b7/$ONEAPI_DMG\"\ncurl --create-dirs -O --output-dir \"$TARGET_DIR\" \"$ONEAPI_URI\"\nhdiutil attach \"$TARGET_DIR/$ONEAPI_DMG\"\npushd /Volumes/$ONEAPI_FILE/bootstrapper.app/Contents/MacOS/\nchmod +x ./install.sh\narch -x86_64 ./install.sh --silent --eula accept\npopd\nhdiutil detach \"/Volumes/$ONEAPI_FILE\"\n"
  },
  {
    "path": "infrastructure/common/macos/qt.sh",
    "content": "#!/bin/zsh\nset -e\n\nREPO=\"https://github.com/qt/qtbase.git\"\nBRANCH=\"v6.6.1\"\n\npushd $SOURCE_PATH\nrm -rf \"${SOURCE_PATH}/${BRANCH}\"\n\ngit clone --depth 1 --branch $BRANCH $REPO $BRANCH\n\npushd $BRANCH\ncmake . -G Ninja -L -B cmbuild \\\n  -DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n  -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \\\n  -DCMAKE_INSTALL_PREFIX=\"${INSTALLATION_PATH}/Qt\" \\\n  -DCMAKE_PREFIX_PATH=\"${INSTALLATION_PATH}/homebrew\" \\\n  -DFEATURE_developer_build=ON \\\n  -DFEATURE_private_tests=OFF \\\n  -DCMAKE_BUILD_TYPE=Release \\\n  -DQT_BUILD_TESTS=OFF \\\n  -DQT_BUILD_TESTS_BY_DEFAULT=OFF \\\n  -DQT_BUILD_EXAMPLES_BY_DEFAULT=OFF \\\n  -DQT_INSTALL_EXAMPLE_SOURCE_BY_DEFAULT=OFF \\\n\npushd cmbuild\ncmake --build . --parallel\ncmake --install .\npopd\npopd\n\nrm -rf \"${BRANCH}\"\n\ncat << EOF >> /Users/$SSH_USER/.zprofile\nexport PATH=\"${INSTALLATION_PATH}/Qt/bin:\\$PATH\"\nEOF\n\n"
  },
  {
    "path": "infrastructure/common/macos/rosetta.sh",
    "content": "#!/bin/bash\nset -e\n\nif arch -x86_64 /usr/bin/true 2> /dev/null; then\n\techo \"Rosetta previously installed\"\nelse\n\t/usr/sbin/softwareupdate --install-rosetta --agree-to-license\nfi\n"
  },
  {
    "path": "infrastructure/common/macos/sitk.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone --depth 1 --branch \"v2.1.1.2\" \"https://github.com/SimpleITK/SimpleITK.git\"\npushd SimpleITK\ncmake .  -L -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n  -DWRAP_DEFAULT:BOOL=OFF \\\n  -DBUILD_EXAMPLES:BOOL=OFF \\\n  -DBUILD_TESTING:BOOL=OFF \\\n  -DBUILD_SHARED_LIBS:BOOL=OFF \\\n  -DCMAKE_BUILD_TYPE=Release \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake install -j $(sysctl -n hw.ncpu)\npopd\npopd\nrm -rf SimpleITK\npopd\n"
  },
  {
    "path": "infrastructure/common/macos/tetgen.sh",
    "content": "#!/bin/zsh\npushd $SOURCE_PATH\n\ngit clone \"https://github.com/ufz/tetgen.git\"\npushd tetgen\ncmake .  -L -B cmbuild \\\n\t-DCMAKE_INSTALL_PREFIX=\"$INSTALLATION_PATH\" \\\n\t-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" \\\n\t-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15\n\npushd cmbuild\nmake -j $(sysctl -n hw.ncpu)\nmake install\n\npopd\npopd\nrm -rf tetgen\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/aws.ps1",
    "content": "choco install awscli -y\n"
  },
  {
    "path": "infrastructure/common/windows/choco.ps1",
    "content": "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))\n\nchoco install git -y\n# Set Path for git-bash and perl\n$gitPath = \"C:\\Program Files\\Git\\bin\"\n$gnu32Path = \"C:\\Program Files\\Git\\usr\\bin\"\n$machinePath = [Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n$machinePath = $machinePath -Split ';'\n$machinePath + $machinePath + $gitPath + $gnu32Path\n$machinePath = $machinePath -Join ';'\n[Environment]::SetEnvironmentVariable(\"Path\", $machinePath, \"MACHINE\")\n"
  },
  {
    "path": "infrastructure/common/windows/cmake.ps1",
    "content": "choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' --apply-install-arguments-to-dependencies -y"
  },
  {
    "path": "infrastructure/common/windows/ffmpeg.ps1",
    "content": "choco install ffmpeg-shared -y --version 6.1.1\n\n# Conditionally set path for ffmpeg\n$machinepath=[Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n$ffmpegPath = \"C:\\Program Files\\FFMpeg\"\n$paths = $machinepath -split ';'\n\nif ($ffmpegPath -notin $paths) {\n  $paths = $paths + $ffmpegPath\n  $paths = $paths -join ';'\n  [Environment]::SetEnvironmentVariable(\"Path\", $paths, \"MACHINE\")\n}\n"
  },
  {
    "path": "infrastructure/common/windows/hypre.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\ngit clone https://github.com/hypre-space/hypre.git\npushd hypre\\src\ngit checkout v2.20.0\ncmake .  -L -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DHYPRE_HAVE_MPI=Off ^\n  -DHYPRE_WITH_MPI=Off ^\n  -DCMAKE_POLICY_VERSION_MINIMUM=3.5\n\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q hypre\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/install-builder.ps1",
    "content": "aws s3 cp s3://febiosoftware/windows/installbuilder/installbuilder-enterprise-23.11.0-windows-x64-installer.exe .\n$installBuilderPath = \"C:\\Program Files\\InstallBuilder Enterprise 23.11.0\"\nStart-Process .\\installbuilder-enterprise-23.11.0-windows-x64-installer.exe `\n  -ArgumentList '--mode unattended', '--installer-language en', \"--prefix \"\"${installBuilderPath}\"\"\"`\n  -NoNewWindow `\n  -Wait\n\n$builderPath = \"${installBuilderPath}/bin\"\n\n# Conditionally add builderpath to environment\n$machinepath=[Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n$paths = $machinepath -split ';'\n\nif ($builderPath -notin $paths) {\n  $paths = $paths + $builderPath\n  $paths = $paths -join ';'\n  [Environment]::SetEnvironmentVariable(\"INSTALL_BUILDER_ROOT\", $installBuilderPath, \"MACHINE\")\n  [Environment]::SetEnvironmentVariable(\"Path\", $paths, \"MACHINE\")\n}\n"
  },
  {
    "path": "infrastructure/common/windows/itk.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\nset SOURCE=\"https://github.com/InsightSoftwareConsortium/ITK.git\"\nset BRANCH=\"v5.2.1\"\ngit clone --depth 1 --branch \"%BRANCH%\" \"%SOURCE%\" \"%BRANCH%\"\npushd %BRANCH%\ncmake .  -LA -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DBUILD_EXAMPLES:BOOL=OFF ^\n  -DBUILD_SHARED_LIBS:BOOL=OFF ^\n  -DBUILD_TESTING:BOOL=OFF ^\n  -DITK_USE_SYSTEM_EIGEN:BOOL=ON ^\n  -DITK_WRAP_PYTHON:BOOL=OFF ^\n  -DITK_DOXYGEN_HTML:BOOL=OFF ^\n  -DCMAKE_BUILD_TYPE=Release ^\n  -DCMAKE_POLICY_VERSION_MINIMUM=3.5\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q %BRANCH%\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/jq.ps1",
    "content": "choco install jq -y\n"
  },
  {
    "path": "infrastructure/common/windows/levmar.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\ngit clone https://github.com/jturney/levmar.git\npushd levmar\ncmake . -LA -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DBUILD_DEMO:BOOLEAN=false ^\n  -DCMAKE_POLICY_VERSION_MINIMUM=3.5\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q levmar\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/libzip.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\ngit clone --depth 1 --branch v1.10.1 https://github.com/nih-at/libzip.git\npushd libzip\ncmake . -LA -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DBUILD_DOC=OFF ^\n  -DBUILD_EXAMPLES=OFF ^\n  -DBUILD_OSSFUZZ=OFF ^\n  -DBUILD_REGRESS=OFF ^\n  -DBUILD_TOOLS=OFF ^\n  -DBUILD_SHARED_LIBS=ON ^\n  -DENABLE_BZIP2=OFF ^\n  -DENABLE_COMMONCRYPTO=OFF ^\n  -DENABLE_FDOPEN=OFF ^\n  -DENABLE_GNUTILS=OFF ^\n  -DENABLE_LZMA=OFF ^\n  -DENABLE_MBEDTLS=OFF ^\n  -DENABLE_OPENSSL=OFF ^\n  -DENABLE_WINDOWS_CRYPTO=OFF ^\n  -DLIBZIP_DO_INSTALL=ON ^\n  -DSHARED_LIB_VERSIONNING=ON\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q libzip\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/lua.ps1",
    "content": "choco install lua53 -y --version 5.3.5\n$luaPath = \"C:\\ProgramData\\chocolatey\\lib\\lua53\\tools\"\nNew-Item -Path \"${luaPath}\\lua.exe\" -ItemType SymbolicLink -Value \"${luaPath}\\lua53.exe\"\n"
  },
  {
    "path": "infrastructure/common/windows/mmg.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\ngit clone https://github.com/MmgTools/mmg.git\npushd mmg\ngit checkout v5.7.3\ncmake . -LA -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DCMAKE_POLICY_VERSION_MINIMUM=3.5\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q mmg\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/msmpi.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\n$rooturl = \"https://github.com/microsoft/Microsoft-MPI/releases/download\"\n$version = \"10.1.1\"\n$baseurl = \"$rooturl/v$version\"\n\n$tempdir    = $Env:SOURCE_PATH\n$msmpisdk   = Join-Path $tempdir msmpisdk.msi\n$msmpisetup = Join-Path $tempdir msmpisetup.exe\n\nfunction Download-File($url, $filename) {\n  foreach ($i in 1..5) {\n    try {\n      Write-Host \"Downloading ${url}\"\n      Invoke-WebRequest $url -OutFile $filename\n      return\n    }\n    catch {\n      $message = $_\n      Write-Warning \"${message}\"\n      Write-Host \"Download failed, retrying ...\"\n      Start-Sleep -Seconds $i\n    }\n  }\n  throw \"Failed to download from ${url}\"\n  return $null\n}\n\nWrite-Host \"Downloading Microsoft MPI $version\"\nDownload-File \"$baseurl/msmpisdk.msi\"   $msmpisdk\nDownload-File \"$baseurl/msmpisetup.exe\" $msmpisetup\n\nWrite-Host \"Installing Microsoft MPI $version\"\nStart-Process msiexec.exe -ArgumentList \"/quiet /passive /qn /i $msmpisdk\" -Wait\nStart-Process $msmpisetup -ArgumentList \"-unattend\" -Wait\n\nif ($Env:GITHUB_ENV) {\n  Write-Host 'Adding environment variables to $GITHUB_ENV'\n  $envlist = @(\"MSMPI_BIN\", \"MSMPI_INC\", \"MSMPI_LIB32\", \"MSMPI_LIB64\")\n  foreach ($name in $envlist) {\n    $value = [Environment]::GetEnvironmentVariable($name, \"Machine\")\n    Write-Host \"$name=$value\"\n    Add-Content $Env:GITHUB_ENV \"$name=$value\"\n  }\n}\n\nif ($Env:GITHUB_PATH) {\n  Write-Host 'Adding $MSMPI_BIN to $GITHUB_PATH'\n  $MSMPI_BIN = [Environment]::GetEnvironmentVariable(\"MSMPI_BIN\", \"Machine\")\n  Add-Content $Env:GITHUB_PATH $MSMPI_BIN\n}  \n"
  },
  {
    "path": "infrastructure/common/windows/netgen.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\nset SOURCE=\"https://github.com/NGSolve/netgen.git\"\nset BRANCH=\"v6.2.2501\"\n\ngit clone --depth 1 --branch \"%BRANCH%\" \"%SOURCE%\" \"%BRANCH%\"\npushd %BRANCH%\ngit submodule update --init --recursive\ncmake .  -LA -B cmbuild ^\n  -DCMAKE_BUILD_TYPE=Release ^\n  -DCMAKE_INSTALL_PREFIX=\"c:/usr/local\" ^\n  -DUSE_CCACHE:BOOL=OFF ^\n  -DUSE_CGNS:BOOL=OFF ^\n  -DUSE_CSG:BOOL=ON ^\n  -DUSE_GEOM2D:BOOL=ON ^\n  -DUSE_GUI:BOOL=OFF ^\n  -DUSE_INTERFACE:BOOL=ON ^\n  -DUSE_INTERNAL_TCL:BOOL=OFF ^\n  -DUSE_JPEG:BOOL=OFF ^\n  -DUSE_MPEG:BOOL=OFF ^\n  -DUSE_MPI:BOOL=OFF ^\n  -DUSE_MPI4PY:BOOL=OFF ^\n  -DUSE_NATIVE_ARCH:BOOL=OFF ^\n  -DUSE_NUMA:BOOL=OFF ^\n  -DUSE_OCC:BOOL=ON ^\n  -DUSE_PYTHON:BOOL=OFF ^\n  -DUSE_STLGEOM:BOOL=ON ^\n  -DUSE_SUPERBUILD:BOOL=OFF ^\n  -DENABLE_CPP_CORE_GUIDELINES_CHECK:BOOL=OFF ^\n  -DENABLE_UNIT_TESTS:BOOL=OFF\n\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q %BRANCH%\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/nlopt.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\nset SOURCE=\"https://github.com/stevengj/nlopt.git\"\nset BRANCH=\"v2.10.1\"\n\ngit clone --depth 1 --branch \"%BRANCH%\" \"%SOURCE%\" \"%BRANCH%\"\npushd %BRANCH%\ncmake . -LA -B cmbuild ^\n  -DCMAKE_BUILD_TYPE=Release ^\n  -DCMAKE_INSTALL_PREFIX=\"c:/usr/local\" ^\n  -DBUILD_SHARED_LIBS=OFF ^\t\n  -DNLOPT_CXX=OFF ^\n\t-DNLOPT_GUILE=OFF ^\n\t-DNLOPT_JAVA=OFF ^\n\t-DNLOPT_MATLAB=OFF ^\n\t-DNLOPT_OCTAVE=OFF ^\n\t-DNLOPT_PYTHON=OFF ^\n\t-DNLOPT_PYTHON_SABI=OFF ^\n\t-DNLOPT_SWIG=OFF ^\n\t-DNLOPT_TESTS=OFF \n\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q %BRANCH%\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/occt.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\"\npushd %SOURCE_PATH%\n\nset SOURCE=\"https://github.com/Open-Cascade-SAS/OCCT.git\"\nset BRANCH=\"V7_7_2\"\n\ngit clone --depth 1 --branch \"%BRANCH%\" \"%SOURCE%\" \"%BRANCH%\"\npushd %BRANCH%\ncmake . -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DBUILD_DOC_Overview:BOOL=OFF ^\n  -DBUILD_ENABLE_FPE_SIGNAL_HANDLER:BOOL=OFF ^\n  -DBUILD_Inspector:BOOL=OFF ^\n  -DBUILD_LIBRARY_TYPE:STRING=Shared ^\n  -DBUILD_MODULE_ApplicationFramework:BOOL=OFF ^\n  -DBUILD_MODULE_DETools:BOOL=OFF ^\n  -DBUILD_MODULE_DataExchange:BOOL=ON ^\n  -DBUILD_MODULE_Draw:BOOL=OFF ^\n  -DBUILD_MODULE_FoundationClasses:BOOL=TRUE ^\n  -DBUILD_MODULE_ModelingAlgorithms:BOOL=TRUE ^\n  -DBUILD_MODULE_ModelingData:BOOL=OFF ^\n  -DBUILD_MODULE_Visualization:BOOL=OFF ^\n  -DBUILD_OPT_PROFILE:STRING=Default ^\n  -DBUILD_RELEASE_DISABLE_EXCEPTIONS:BOOL=OFF ^\n  -DBUILD_RESOURCES:BOOL=OFF ^\n  -DBUILD_SAMPLES_QT:BOOL=OFF ^\n  -DBUILD_USE_PCH:BOOL=OFF ^\n  -DUSE_DRACO:BOOL=OFF ^\n  -DUSE_FFMPEG:BOOL=OFF ^\n  -DUSE_FREEIMAGE:BOOL=OFF ^\n  -DUSE_FREETYPE:BOOL=OFF ^\n  -DUSE_GLES2:BOOL=OFF ^\n  -DUSE_OPENGL:BOOL=OFF ^\n  -DUSE_OPENVR:BOOL=OFF ^\n  -DUSE_RAPIDJSON:BOOL=OFF ^\n  -DUSE_TBB:BOOL=OFF ^\n  -DUSE_TK:BOOL=OFF ^\n  -DUSE_VTK:BOOL=OFF ^\n  -DUSE_XLIB:BOOL=OFF ^\n  -DCMAKE_POLICY_VERSION_MINIMUM=3.5\n\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q %BRANCH%\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/pathprep.ps1",
    "content": "$installPath = $Env:INSTALLATION_PATH\n$srcPath = $Env:SOURCE_PATH\n$vcpkgPackagePath = $Env:VCPKG_PACKAGE_PATH\nmd $installPath\nmd $srcPath\nmd $vcpkgPackagePath\n\n$gitPath = \"C:\\Program Files\\Git\\bin\"\n$gnu32Path = \"C:\\Program Files\\Git\\usr\\bin\"\n\n$systempath=[Environment]::GetEnvironmentVariable(\"Path\", \"Machine\")\n$systempath = $systempath -split ';'\n$systempath= $systempath + $installPath + $gitPath + $gnu32Path\n$systempath = $systempath  -join ';'\n[Environment]::SetEnvironmentVariable(\"Path\", $systempath, \"MACHINE\")\n\n[Environment]::GetEnvironmentVariable(\"Path\", \"Machine\") -Split ';'\n"
  },
  {
    "path": "infrastructure/common/windows/python.bat",
    "content": "git clone https://github.com/pybind/pybind11.git %USERPROFILE%\\pybind11\n\ncurl.exe -O https://www.python.org/ftp/python/3.13.1/python-3.13.1-amd64.exe\n.\\python-3.13.1-amd64.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_launcher=0 Include_doc=0 Include_tcltk=0 Include_dev=1"
  },
  {
    "path": "infrastructure/common/windows/qt.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\n\nset AQT_DISABLE_UPDATES=1\n\ncall pip install aqtinstall -v\ncall aqt list-qt windows desktop\ncall aqt install-qt --outputdir \"%QT_INSTALL_DIR%\" windows desktop 6.9.3 win64_msvc2022_64 -m all\n"
  },
  {
    "path": "infrastructure/common/windows/resize-partition.ps1",
    "content": "Get-Partition\n\"rescan\" | diskpart\n$currentSize = $(Get-Partition -DriveLetter C).Size\n$maxSize = $(Get-PartitionSupportedSize -DriveLetter C).SizeMax\nif($currentSize -lt $maxSize) {\n    \"Resizing Partition\"\n    Resize-Partition -DriveLetter C -Size $maxSize\n    Get-Partition\n}\n\n# TODO: Move this to choco.ps1\n$machinePath = [Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n$machinePath = $machinePath -Split ';'\n$gnu32 = \"C:\\Program Files\\Git\\usr\\bin\"\nif($machinePath -notcontains $gnu32) {\n  $machinePath = $machinePath + $gnu32\n  $machinePath = $machinePath -join ';'\n  [Environment]::SetEnvironmentVariable(\"Path\", $machinePath, \"MACHINE\")\n}\n"
  },
  {
    "path": "infrastructure/common/windows/sitk.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\nset SOURCE=\"https://github.com/SimpleITK/SimpleITK.git\"\nset BRANCH=\"v2.1.1.2\"\ngit clone --depth 1 --branch \"%BRANCH%\" \"%SOURCE%\" \"%BRANCH%\"\npushd %BRANCH%\ncmake .  -LA -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH% ^\n  -DWRAP_DEFAULT:BOOL=OFF ^\n  -DBUILD_EXAMPLES:BOOL=OFF ^\n  -DBUILD_TESTING:BOOL=OFF ^\n  -DBUILD_SHARED_LIBS:BOOL=OFF ^\n  -DCMAKE_BUILD_TYPE=Release ^\n  -DUSE_CCACHE=OFF ^\n  -DWRAP_CSHARP=OFF ^\n  -DWRAP_JAVA=OFF ^\n  -DWRAP_LUA=OFF ^\n  -DWRAP_PYTHON=OFF ^\n  -DWRAP_R=OFF ^\n  -DWRAP_RUBY=OFF ^\n  -DWRAP_TCL=OFF\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nREM RD /S /Q %BRANCH%\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/tetgen.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\npushd %SOURCE_PATH%\ngit clone \"https://github.com/ufz/tetgen.git\"\npushd tetgen\ncmake .  -L -B cmbuild ^\n  -DCMAKE_INSTALL_PREFIX=%INSTALLATION_PATH%\n\npushd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\npopd\npopd\nRD /S /Q %TETGEN_SRC%\npopd\n"
  },
  {
    "path": "infrastructure/common/windows/user-data.txt",
    "content": "<powershell>\n# Set administrator password\nnet user Administrator SuperS3cr3t!!!!\nwmic useraccount where \"name='Administrator'\" set PasswordExpires=FALSE\n\n# First, make sure WinRM can't be connected to\nnetsh advfirewall firewall delete rule name=\"Windows Remote Management (HTTP-In)\" 2>$Null\nnetsh advfirewall firewall set rule name=\"Windows Remote Management (HTTP-In)\" new enable=yes action=block\n\n# Delete any existing WinRM listeners\nwinrm delete winrm/config/listener?Address=*+Transport=HTTP  2>$Null\nwinrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null\n\n# Disable group policies which block basic authentication and unencrypted login\n\nSet-ItemProperty -Path HKLM:\\Software\\Policies\\Microsoft\\Windows\\WinRM\\Client -Name AllowBasic -Value 1\nSet-ItemProperty -Path HKLM:\\Software\\Policies\\Microsoft\\Windows\\WinRM\\Client -Name AllowUnencryptedTraffic -Value 1\nSet-ItemProperty -Path HKLM:\\Software\\Policies\\Microsoft\\Windows\\WinRM\\Service -Name AllowBasic -Value 1\nSet-ItemProperty -Path HKLM:\\Software\\Policies\\Microsoft\\Windows\\WinRM\\Service -Name AllowUnencryptedTraffic -Value 1\n\n\n# Create a new WinRM listener and configure\nwinrm create winrm/config/listener?Address=*+Transport=HTTP\nwinrm set winrm/config/winrs '@{MaxMemoryPerShellMB=\"0\"}'\nwinrm set winrm/config '@{MaxTimeoutms=\"7200000\"}'\nwinrm set winrm/config/service '@{AllowUnencrypted=\"true\"}'\nwinrm set winrm/config/service '@{MaxConcurrentOperationsPerUser=\"12000\"}'\nwinrm set winrm/config/service/auth '@{Basic=\"true\"}'\nwinrm set winrm/config/client/auth '@{Basic=\"true\"}'\n\n# Configure UAC to allow privilege elevation in remote shells\n$Key = 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System'\n$Setting = 'LocalAccountTokenFilterPolicy'\nSet-ItemProperty -Path $Key -Name $Setting -Value 1 -Force\n\n# Disable Windows Defender Firewall\nSet-NetFirewallProfile -Profile Domain,Public,Private -Enabled False\nGet-NetFirewallProfile | Select Name, Enabled\n\n# Configure and restart the WinRM Service; Enable the required firewall exception\nStop-Service -Name WinRM\nSet-Service -Name WinRM -StartupType Automatic\nnetsh advfirewall firewall set rule name=\"Windows Remote Management (HTTP-In)\" new action=allow localip=any remoteip=any\nStart-Service -Name WinRM\n</powershell>\n"
  },
  {
    "path": "infrastructure/common/windows/vcpkg-installer.ps1",
    "content": "$vcpkgInstallPath=\"c:\\vcpkg\"\ngit clone https://github.com/MicroSoft/vcpkg.git $vcpkgInstallPath\n& \"$vcpkgInstallPath\\bootstrap-vcpkg.bat\"\n\n# Add vcpkg_root to environment\n[Environment]::SetEnvironmentVariable(\"VCPKG_ROOT\", $vcpkgInstallPath, \"MACHINE\")\n$machinepath=[Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n\n# Append Release build only to x64-windows triplet set(VCPKG_BUILD_TYPE release)\n$tripletPath = \"${vcpkgInstallPath}\\triplets\\x64-windows.cmake\"\nGet-Content $tripletPath\nAdd-Content -Path $tripletPath \"set(VCPKG_BUILD_TYPE release)\"\nGet-Content $tripletPath\n\n# Add vcpkg to machine path\n$paths = $machinepath -split ';'\n$paths = $paths + $vcpkgInstallPath\n$value = $paths -join ';'\n[Environment]::SetEnvironmentVariable(\"Path\", $value, \"MACHINE\")\n"
  },
  {
    "path": "infrastructure/common/windows/vcpkg-package-install.ps1",
    "content": "$vcpkgPackagePath = $Env:VCPKG_PACKAGE_PATH\ncd $vcpkgPackagePath\nvcpkg install --triplet=x64-windows\n$machinepath=[Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n$paths = $machinepath -split ';'\n$paths = $paths + \"${vcpkgPackagePath}\\vcpkg_installed\\x64-windows\"\n$value = $paths -join ';'\n[Environment]::SetEnvironmentVariable(\"Path\", $value, \"MACHINE\")\n"
  },
  {
    "path": "infrastructure/common/windows/vcpkg.json",
    "content": "{\n  \"builtin-baseline\": \"f415cba6374936225962ef0fcd262ba09fdb84ce\",\n  \"dependencies\": [\n    \"eigen3\",\n    \"libssh\",\n    \"opengl\",\n    \"openssl\",\n    \"pthread\",\n    \"zstd\",\n    \"zlib\",\n    \"sqlite3\",\n    \"fftw3\",\n    \"vulkan\"\n  ]\n}\n"
  },
  {
    "path": "infrastructure/common/windows/vcpkg.ps1",
    "content": "$vcpkgInstallPath=\"C:\\usr\\local\\vcpkg\"\nmd $vcpkgInstallPath\ngit clone https://github.com/MicroSoft/vcpkg.git $vcpkgInstallPath\n& \"$vcpkgInstallPath\\bootstrap-vcpkg.bat\"\n[Environment]::SetEnvironmentVariable(\"VCPKG_ROOT\", $vcpkgInstallPath, \"MACHINE\")\n$machinepath=[Environment]::GetEnvironmentVariable(\"Path\", \"MACHINE\")\n\n$paths = $machinepath -split ';'\n$paths = $paths + $vcpkgInstallPath\n$value = $paths -join ';'\n[Environment]::SetEnvironmentVariable(\"Path\", $value, \"MACHINE\")\n"
  },
  {
    "path": "infrastructure/common/windows/zlib.bat",
    "content": "call \"%ONEAPI_ROOT%setvars.bat\" --force\ncd %TEMP%\nset ZLIB=zlib-1.2.12.tar.gz\nset ZLIB_HOST=http://zlib.net\nset ZLIB_SRC=zlib-1.2.12\nset ZLIB_URI=%ZLIB_HOST%/%ZLIB%\ncurl -O -L %ZLIB_URI%\nbash --login -c \"tar xzf %ZLIB%\"\ncd %ZLIB_SRC%\ncmake .  -LA -B cmbuild -DCMAKE_INSTALL_PREFIX=\"c:\\local\"\ncd cmbuild\nmsbuild /P:Configuration=Release /m:%NUMBER_OF_PROCESSORS% INSTALL.vcxproj\ncd %TEMP%\nDEL %ZLIB%\nRD /S /Q %ZLIB_SRC%\n"
  },
  {
    "path": "infrastructure/macos.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    amazon = {\n      version = \">= 0.0.2\"\n      source  = \"github.com/hashicorp/amazon\"\n    }\n  }\n}\n\nlocals {\n  buildtime         = formatdate(\"YYYYMMDDhhmm\", timestamp())\n  sudo              = \"echo 'packer' | sudo -S /bin/zsh -l -c '{{ .Vars }} {{ .Path }}'\"\n  zsh_command       = \"echo 'packer' | /bin/zsh -c -l '{{ .Vars }} {{ .Path }}'\"\n  installation_path = var.installation_path\n  homebrew_prefix   = \"${local.installation_path}/homebrew\"\n  homebrew_bin      = \"${local.homebrew_prefix}/bin/brew\"\n  src_path          = \"${local.installation_path}/src\"\n  ssh_user          = var.ssh_user\n  environment = {\n    \"HOMEBREW_PREFIX\"   = local.homebrew_prefix\n    \"HOMEBREW_BIN\"      = local.homebrew_bin\n    \"SSH_USER\"          = local.ssh_user\n    \"INSTALLATION_PATH\" = local.installation_path\n    \"SOURCE_PATH\"       = local.src_path\n  }\n}\n\nsource \"null\" \"macos\" {\n  ssh_pty              = true\n  ssh_host             = var.ssh_host\n  ssh_username         = var.ssh_user\n  ssh_private_key_file = var.ssh_private_key\n  ssh_port             = var.ssh_port\n  communicator         = \"ssh\"\n}\n\nvariable \"installation_path\" {\n  default = \"/usr/local/x86_64\"\n}\n\nvariable \"ssh_host\" {\n  type    = string\n  default = \"midnight.local\"\n}\n\nvariable \"ssh_user\" {\n  type = string\n}\n\nvariable \"ssh_port\" {\n  type    = string\n  default = \"22\"\n}\n\nvariable \"ssh_private_key\" {\n  type = string\n}\n\nbuild {\n  name = \"febio\"\n  sources = [\n    \"source.null.macos\"\n  ]\n\n  provisioner \"shell\" {\n    inline = [\n      \"sudo xcode-select --install || echo \\\"xcode tools  are already installed\\\"\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = local.sudo\n    script          = \"./common/macos/rosetta.sh\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = local.sudo\n    script          = \"./common/macos/openapi.sh\"\n  }\n\n  provisioner \"shell\" {\n    # execute_command = local.zsh_command\n    script = \"./common/macos/installation_prep.sh\"\n    env    = local.environment\n  }\n\n  provisioner \"shell\" {\n    script = \"./common/macos/homebrew-x86.sh\"\n    env    = local.environment\n  }\n\n  provisioner \"shell\" {\n    script = \"./common/macos/homebrew-packages.sh\"\n    env    = local.environment\n  }\n\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script = \"./common/macos/ffmpeg.sh\"\n    env    = local.environment\n  }\n\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/qt.sh\"\n    env             = local.environment\n  }\n\n  provisioner \"shell\" {\n    script = \"./common/macos/lua.sh\"\n    env    = local.environment\n  }\n\n  # LEVMAR\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/levmar.sh\"\n    env             = local.environment\n  }\n\n  # HYPRE\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/hypre.sh\"\n    env             = local.environment\n  }\n\n  # mmg\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/mmg.sh\"\n    env             = local.environment\n  }\n\n  # tetgen\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/tetgen.sh\"\n    env             = local.environment\n  }\n\n  # itk\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/itk.sh\"\n    env             = local.environment\n  }\n\n  # sitk\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/sitk.sh\"\n    env             = local.environment\n  }\n\n  # occt\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/occt.sh\"\n    env             = local.environment\n  }\n\n  # netgen\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/netgen.sh\"\n    env             = local.environment\n  }\n\n  # libzip\n  provisioner \"shell\" {\n    execute_command = local.zsh_command\n    script          = \"./common/macos/libzip.sh\"\n    env             = local.environment\n  }\n}\n"
  }
]